rangareddy opened a new pull request, #19974:
URL: https://github.com/apache/hudi/pull/19974

   ### Describe the issue this Pull Request addresses
   
   Closes #19862.
   
   Every `show_*` procedure that accepts both `limit` and `filter` truncated 
its rows to `limit` first and evaluated `filter` on the truncated list, so 
`limit => 10, filter => ...` returned the matching subset of the first 10 rows 
rather than the first 10 matching rows. With a selective filter the result is 
empty whenever the matches sit past the cut-off, and since every one of these 
procedures applies a default `limit` (10, 20 or 100), a plain `filter => ...` 
call with no explicit limit was affected too.
   
   Introduced alongside the generic filter option in #13736 / #13790.
   
   ### Summary and Changelog
   
   `BaseProcedure` gains `applyFilterAndLimit(results, filter, schema, limit)`, 
which filters and then truncates, plus a `hasFilter` helper. The ten affected 
procedures now call it. They are not all the same shape, and the difference is 
the substance of the change:
   
   **Truncating an already-materialised list.** `ShowTablePropertiesProcedure`, 
`ShowFileSystemViewProcedure`, `ShowHoodieLogFileMetadataProcedure` and 
`ShowMetadataTableFilesProcedure` just dropped the early `stream().limit(...)`; 
the rows were already in memory, so nothing about the work done changes.
   
   **Bounding at the source.** `ShowBootstrapMappingProcedure` and 
`ShowFsPathDetailProcedure` used `df.orderBy(...).limit(n).collect()`, and 
`ShowInvalidParquetProcedure` used `rdd.take(n)`. The bound now moves after the 
filter. To keep the unfiltered path exactly as it was, the early bound is only 
skipped when a filter is actually present.
   
   **Bounding real work.** In `ShowCleansProcedure`, `ShowTimelineProcedure` 
and `ShowHoodieLogFileRecordsProcedure`, `limit` is not display truncation: it 
caps how many clean instants get their metadata read and how many records are 
pulled out of log files. Filtering first without care would make those 
unbounded, turning a correctness fix into a performance regression. These use 
`scanLimit = if (hasFilter(filter)) Int.MaxValue else limit`, so a call with no 
filter does exactly the work it did before, and only the filtered path reads 
further. `limit` is never used to size an allocation on those paths, so 
`Int.MaxValue` is only ever a `take` bound or a loop comparison.
   
   `ShowTimelineProcedure` needs one extra condition. `getTimelineEntries` 
deliberately ignores `limit` when both `startTime` and `endTime` are given 
("Apply limit only if time range is not fully specified"), so reapplying the 
bound unconditionally would have truncated a fully specified range to the 
default of 20. The reapplied bound honours that case.
   
   The six other procedures that call `applyFilter` 
(`ShowBootstrapPartitionsProcedure`, `ShowColumnStatsOverlapProcedure`, 
`ShowFileStatusProcedure`, `ShowMetadataTableColumnStatsProcedure`, 
`ShowMetadataTablePartitionsProcedure`, `ShowMetadataTableStatsProcedure`) take 
no `limit` at all and are untouched, so the list of ten in the issue is 
complete.
   
   ### Verification
   
   Two regression tests, both confirmed to fail without the corresponding fix.
   
   `TestShowCleansProcedures`: three cleans are created, then the **oldest** is 
requested with `limit => 1`. Cleans come back newest first, so the match sits 
past a limit of 1.
   
   ```
   Test show_cleans applies the filter before the limit *** FAILED ***
     Array() had length 0 instead of expected length 1
     limit must bound the rows the filter matched, not the rows the filter was 
shown;
     got 0 rows for clean_time = 20260916090635697
   ```
   
   `TestShowTimelineTableProcedure` Test Case 13, which runs across all four 
existing variants (V1/V2 x COW/MOR): a fully specified time range with `limit 
=> 1` must not be truncated. Reintroducing the unconditional bound fails it 
everywhere:
   
   ```
   Test show_timeline with various parameters - V2 MOR *** FAILED ***
     had length 1 instead of expected length 39
     Test 13: a fully specified range must not be truncated by limit, expected 
39 got 1
   Test show_timeline with various parameters - V2 COW *** FAILED ***
     had length 1 instead of expected length 34
   ```
   
   Full procedure package on Spark 3.5 / Scala 2.12:
   
   ```
   mvn test -Punit-tests -Dspark3.5 -Dscala-2.12 -pl 
hudi-spark-datasource/hudi-spark \
     -DwildcardSuites=org.apache.spark.sql.hudi.procedure
     -> Suites: completed 50, aborted 0
     -> Tests: succeeded 263, failed 0, canceled 0, ignored 2, pending 0
   
   mvn scalastyle:check checkstyle:check -pl hudi-spark-datasource/hudi-spark
     -> Found 0 errors
   ```
   
   ### Impact
   
   `limit => n, filter => ...` now returns the first n matching rows instead of 
the matches among the first n rows. Calls that pass no filter are unaffected, 
including the work they do: the early bound is only lifted when a filter is 
present.
   
   One behaviour change worth calling out: with a filter, 
`show_logfile_records` reads all log records rather than stopping at `limit`, 
and `show_cleans` reads the metadata of every clean rather than the newest 
`limit`. That is required for the filter to see the rows it is supposed to 
match. These are CLI and debugging procedures, and the unfiltered path, which 
is the common one, is unchanged.
   
   ### Risk Level
   
   low: the change is confined to how `limit` and `filter` compose inside these 
procedures. No API, config, or table format change, and no engine or table-type 
specific behaviour is involved.
   
   ### Documentation Update
   
   none: this restores the documented meaning of `limit` combined with `filter` 
rather than changing it.
   
   ### Contributor's checklist
   
   - [x] Read through [contributor's 
guide](https://hudi.apache.org/contribute/how-to-contribute)
   - [x] Enough context is provided in the sections above
   - [x] Adequate tests were added if applicable
   - [x] CI passes on my PR
   


-- 
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]

Reply via email to