morningman opened a new pull request, #66713:
URL: https://github.com/apache/doris/pull/66713

   ### What problem does this PR solve?
   
   Issue Number: related to #65871
   
   Related PR: follow-up to #66116
   
   Problem Summary:
   
   #66116 fixed `SHOW TABLETS ... ORDER BY ... LIMIT n` so that the sort sees 
the whole tablet set instead of an arbitrary prefix of the scan. That part is 
correct and is kept as is. But the fix routed both the ORDER BY branch and the 
branch without ORDER BY through one `SortAndLimit` call, and that helper needs 
a comparator even when no ordering was asked for. Three problems follow from 
that, plus one that was never covered by a test.
   
   **1. Without ORDER BY, an arbitrary subset was sorted and presented as if it 
were a global top-N.**
   
   When no ORDER BY is given, the scan stops as soon as enough rows are 
gathered, so the collected rows are an arbitrary subset of the table. Which 
partition is walked first comes from `ConcurrentHashMap` iteration order 
(`OlapTable#getPartitions` returns `idToPartition.values()`), so it is neither 
id order nor creation order, and it shifts when partitions are added or 
dropped. Sorting that subset by `(TabletId, ReplicaId)` produced a clean 
ascending column that looks like the globally smallest tablet ids but is not.
   
   The trap is that it *is* correct on a table with a single partition and no 
rollup: one index is materialized in full before the size check, so the 
collected set is the whole table. The discrepancy only shows up on partitioned 
tables, which is exactly where it will not be noticed during testing.
   
   ```sql
   -- p1 = 10001..10003, p2 = 10004..10006, p3 = 10007..10009
   SHOW TABLETS FROM t LIMIT 3;
   -- returns e.g. 10007, 10008, 10009 -- neatly ascending, but not the 3 
smallest,
   -- and a later ADD PARTITION can change which three come back
   ```
   
   This PR returns those rows in scan order and bounds only their number. 
Sorting an arbitrary subset cannot be made meaningful, so it is better not to 
imply an order that is not there. Without a LIMIT every row is collected 
anyway, so that case keeps the `(TabletId, ReplicaId)` ordering the command has 
always returned.
   
   **2. `LIMIT 0` returned the whole table.**
   
   `LogicalPlanBuilder#visitShowTabletsFromTable` used `0` both for "the 
statement has no LIMIT clause" and for an explicit `LIMIT 0`, so the command 
could not tell them apart and fell back to "no limit at all".
   
   ```sql
   SHOW TABLETS FROM t LIMIT 0;      -- returned every tablet, MySQL semantics 
say no row
   SHOW TABLETS FROM t LIMIT 5, 0;   -- returned everything past the offset
   ```
   
   The parser now passes `-1` for a missing LIMIT clause, so `limit == 0` 
bounds the result to nothing and the scan is skipped entirely.
   
   **3. `limit + offset` overflowed.**
   
   Both operands come from `Long.parseLong`, and the sum wrapped into a 
negative size that later reached `List#subList`:
   
   ```sql
   SHOW TABLETS FROM t LIMIT 9223372036854775807, 9223372036854775807;
   -- IndexOutOfBoundsException
   ```
   
   Each operand is now clamped to `Integer.MAX_VALUE` before they are added, 
which restores a guard that #66116 had removed. This is the case @morrySnow 
raised on #66116 with `Utils#addOverflows`.
   
   **4. The layer where these bugs keep landing had no test.**
   
   Every `sizeLimit` bug so far has been in the mapping from LIMIT/OFFSET onto 
the number of rows to keep. `SortAndLimitTest` only covers the utility, and the 
command test only covers `validate()`, so that layer sat between two test 
suites with none of its own. It is extracted into `computeSizeLimit()` and 
covered directly.
   
   **Additionally**: sorting and formatting move out of `olapTable.readLock()`. 
`TabletsProcDir#fetchComparableResult` builds rows out of copied longs and 
strings and keeps no reference to catalog objects, so the lock is only needed 
for the scan. This matters because an explicit ORDER BY now has to collect the 
whole tablet set: on a table with hundreds of thousands of tablets times 
replicas, `SHOW TABLETS ... ORDER BY LocalDataSize DESC LIMIT 10` materializes 
every row, and holding the table read lock across the sort and the string 
conversion blocks schema change and partition DDL on that table for the whole 
time.
   
   ### Resulting semantics
   
   | statement | result |
   |---|---|
   | `SHOW TABLETS FROM t` | every row, ordered by (TabletId, ReplicaId) |
   | `... ORDER BY k` | every row, ordered by k |
   | `... ORDER BY k LIMIT n` | global top-n by k |
   | `... ORDER BY k LIMIT m, n` | global rank m..m+n-1 by k |
   | `... LIMIT n` | n arbitrary rows, no ordering promised |
   | `... LIMIT m, n` | n arbitrary rows, no ordering promised |
   | `... LIMIT 0` / `... LIMIT m, 0` | no row |
   
   ### Release note
   
   Fix `SHOW TABLETS ... LIMIT 0`, which returned the whole table instead of an 
empty result. Without an explicit `ORDER BY`, `SHOW TABLETS ... LIMIT n` no 
longer sorts its result: the rows it returns are an arbitrary subset of the 
table, and sorting them suggested a global order that was never there. The 
unbounded `SHOW TABLETS FROM tbl` keeps returning every row ordered by 
(TabletId, ReplicaId).
   
   ### Check List (For Author)
   
   - Test
       - [x] Regression test
       - [x] Unit Test
       - [ ] Manual test (add detailed scripts or steps below)
       - [ ] No need to test or manual test. Explain why:
           - [ ] This is a refactor/code format and no logic has been changed.
           - [ ] Previous test can cover this change.
           - [ ] No code files have been changed.
           - [ ] Other reason
   
   - Behavior changed:
       - [ ] No.
       - [x] Yes. Three user visible changes, all listed in the release note 
and in the table above: `LIMIT 0` and `LIMIT m, 0` now return no row instead of 
the whole table; `LIMIT n` without `ORDER BY` no longer sorts its result; 
`LIMIT` with a huge `OFFSET` no longer fails with an internal error. `SHOW 
TABLETS` without a LIMIT is unchanged, and `ORDER BY` keeps the behavior #66116 
established.
   
   - Does this need documentation?
       - [ ] No.
       - [x] Yes. The docs do not currently describe an ordering guarantee for 
`SHOW TABLETS` without `ORDER BY`, so nothing there becomes wrong, but the 
`LIMIT 0` correction is worth a note. Will open a doris-website PR if reviewers 
agree on the semantics above.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to