Repository navigation
MDEV-38970: Streaming window functions step 1 - #5267
OmarGamal10 wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces support for streaming window functions, allowing certain functions like row number, rank, and dense rank to be computed on the fly without materializing into a temporary table. The code review feedback highlights several critical issues, including potential null pointer dereferences (crashes) due to missing checks on partition_list and order_list, a logic bug in compare_order_lists when handling trailing constants, and an ignored return value in cursor setup. Additionally, the reviewer suggests caching the THD pointer to avoid expensive thread-local lookups in the performance-critical per-row execution path, and resolving an inconsistency in how default versus explicit frames are handled.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
43cd799 to
d344567
Compare
|
in tests to avoid query text duplication. |
I thought about this actually (running queries side by side) but thought I would need an optimizer switch just for that, I will try |
|
Streaming such queries can provide correct results only when the rows arriving at the streaming callback are already grouped which is only true for a single-table loose index scan ( This concern also applies not only to multi-table plans, but also to single-table plans that group via If we still want to cover |
Yes, you're right I focused on cases where group by should stream and missed this. I handled this and added some tests. I looked into what makes a group by use end_send other than using a loose index scan. The other case that happens is when GROUP BY is rewritten into an ORDER BY when a unique non-null index exists on the group key. But for now this is skipped explicitly whenever grouping exists, I am experimenting with it to make sure I don't miss something. |
726e67b to
9cd7a44
Compare
b5f2f2d to
692f3ee
Compare
f4b3365 to
6c1811d
Compare
|
@OmarGamal10 , please also apply this cleanup.patch |
|
Next cleanup patch: |
755995a to
ce65396
Compare
|
@OmarGamal10 , everything looks good now. One more detail to add: a proper commit message, and you can remove the "Draft" flag from the PR. |
ce65396 to
e59b8c8
Compare
Instead of materializing rows into an intermediate temporary table and
sorting there, execute window functions on the fly when the streaming
criteria are met.
What has been added:
- Window_funcs_sort_streaming object, attached to JOIN_TAB via
window_funcs_streaming_step:
* setup(): Sets up cursor managers and partition trackers
(Group_bound_tracker), sets phase to computation, and configures
the simple aggregator for aggregate functions.
* process_row(): Iterates over window functions for the current
row in TABLE::record[0]. Checks if a new partition boundary was
crossed to clear and notify cursors; otherwise notifies cursors
of the next row.
* cleanup(): Cleans up trackers and cursor managers.
- have_streaming_window_funcs(): Checks if the query's window functions
can be streamed in a single pass:
* Verifies all window functions share compatible ordering
(find_longest_compatible_order()).
* Checks that functions are streamable: ranking functions
(ROW_NUMBER, RANK, DENSE_RANK) and running aggregates (SUM, COUNT,
AVG, MIN, MAX).
* Rejects DISTINCT aggregates (streaming uses simple aggregator and
cannot deduplicate).
* Validates frame specifications:
- Ranking functions (frame prohibited).
- Explicit ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW.
- Default RANGE frames whose ordering covers a UNIQUE NOT NULL key
(guaranteeing no peers, so RANGE behaves like ROWS).
* Verifies order compatibility with the main query ORDER BY
and GROUP BY.
* If the window order is longer than the main query ORDER BY,
ensures extra order items belong to the first table, are
deterministic, and do not reference aggregate functions (since
base rows cannot be sorted by aggregates prior to grouping).
- Single sort order replacement: When the window order is longer than
the query ORDER BY (streaming_wf_order_is_longer), replaces
this->order with win_func_longest_order so filesort on the first
table satisfies both in one go.
- set_window_order_uniqueness() / order_contains_unique_index():
For single-table queries, checks if the window ordering covers a
UNIQUE NOT NULL index. When true, no peers can exist, allowing
default RANGE to behave like ROWS and stream.
- test_if_need_tmp_table(): Skips creating a temporary table if
window functions can stream, falling back to materialization only
if a temp table is required for other reasons.
- GROUP BY with unique index: Allow the existing optimization that
removes redundant GROUP BY to proceed when window functions exist.
- Loose index scan support: When a loose index scan is used, rows
arrive already grouped, allowing window functions to stream over them.
HAVING is evaluated before process_row() to filter rows prior to
computing window functions.
- end_compute_win_func(): Attached to the last real table before
end_send. Runs process_row() on the live row and passes it directly
to end_send() without materializing into a temporary table.
- is_streamable(): Virtual method added to Item_sum, overridden for
supported functions.
- Tests (win_streaming.test / win_streaming.result):
* Single-table queries, joins, and derived tables.
* Loose index scan interactions and unique index detection.
* Running aggregates and LIMIT early-exit optimization.
* Comparative validation against SQL_BUFFER_RESULT to ensure
streaming matches materialized output.
* Negative test cases verifying proper fallback to temporary tables.
e59b8c8 to
3e96905
Compare
This PR implements the streaming window functions path as part of my GSoC project (MDEV-38970). Instead of materializing rows into a temporary table and sorting there, this executes window functions on the fly when the streaming criteria are met.
What's been added
Window_funcs_sort_streamingobject, attached toJOIN_TABviawindow_funcs_streaming_step. Contains:setup(): Sets up cursor managers and partition trackers (Group_bound_tracker), sets phase to computation, and configures the simple aggregator for aggregate functions.process_row(): Iterates over window functions for the current row inTABLE::record[0]. Checks if a new partition boundary was crossed to clear and notify cursors; otherwise, notifies cursors of the next row.cleanup(): Cleans up trackers and cursor managers.have_streaming_window_funcs(): Checks if the query's window functions can be streamed in a single pass:find_longest_compatible_order()).ROW_NUMBER,RANK,DENSE_RANK) and running aggregates (SUM,COUNT,AVG,MIN,MAX).DISTINCTaggregates and aggregates insidePARTITION BYorORDER BY.ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW.RANGEframes whose ordering covers a unique key.ORDER BYandGROUP BY.ORDER BY, ensures the extra columns belong to the first table, are deterministic, and are not expensive.Single sort order replacement: When the window order is longer than the query
ORDER BY(streaming_wf_order_is_longer), replacesthis->orderwithwin_func_longest_orderso filesort on the first table satisfies both in one go.set_window_order_uniqueness()/order_contains_unique_index(): For single-table queries, checks if the window ordering covers aUNIQUE NOT NULLindex. When true, no peers can exist, allowing defaultRANGEto behave likeROWSand stream.test_if_need_tmp_table(): Skips creating a temporary table if window functions can stream, falling back to materialization only if a temp table is required for other reasons (e.g., const tables only, orGROUP BYwithout a loose index scan).Group by with a unique index can be optimized away when window functions exist, an existing optimization that was skipped for window functions
Loose index scan support: When a loose index scan is used, rows come out already grouped, allowing window functions to stream over them.
HAVINGis evaluated beforeprocess_row()to filter rows out prior to computing window functions.end_compute_win_func(): Attached to the last real table beforeend_send. Runsprocess_row()on the live row and passes it directly toend_send()without materializing into a temporary table.is_streamable(): Virtual method added toItem_sum, overridden for supported functions.Tests (
win_streaming.test/win_streaming.result):LIMITearly-exit optimization.SQL_BUFFER_RESULTto ensure streaming matches materialized output.