mirror of
https://github.com/MariaDB/server.git
synced 2026-10-23 18:49:29 +03:00
SELECT COUNT(*), SUM(b) FROM t ran serially. The workers could pre-aggregate a GROUP BY, but aggregation over the whole table was refused, because the shape the grouped path merges through is not there: with no group the server materialises nothing, and aggregates in end_send_group() straight off the last join tab -- no aggregation table, no group key, nothing for a partial to be merged into. It turns out neither end needs new machinery, and this commit is mostly the finding of that. The manager already merges these. end_send_group() reaches Item_sum::add(), and add() honours a value left by direct_add() for every aggregate the gate accepts -- COUNT adds a count instead of incrementing, AVG takes a partial sum together with the number of rows behind it. So the drain primes the aggregates and hands the row to the plan's own terminal here exactly as it does through end_update() for a GROUP BY, and the drain needed no case of its own. The workers accumulate into the grouped machinery with a single group in it. The layout gives this shape a group of one constant column, and from there it is the grouped path: a worker keeps one partial in a keyed container, merges into it through end_update(), and ships one row. Beyond costing a column per shipped row -- of which there is one per worker -- that is also what keeps Item_sum_avg::create_tmp_field() packing the count beside the sum, which it does only for a container that has a group. An average built any other way would have arrived without its count. So what is actually new is the gate. The aggregate tests -- the five that can be merged, no DISTINCT, one argument, an argument a worker can evaluate -- are the same for both shapes and move out to pwt_aggregates_can_be_merged(). What is left is the shape question, and which shape to ask about is the query's own grouping rather than the plan's: a query that names a GROUP BY stays the grouped question whatever the optimizer then did with it, so a plan with no group key to merge through is a refusal of that query and not an invitation to read it as aggregating over everything, which would answer something else. Only a query that never grouped reaches the second question. Refused there: a select-list item reading a column outside an aggregate. Its value is whichever row the terminal happened to hold, and a worker holds a different one. An empty result needed nothing. No rows means no groups means no partials shipped, and end_send_group() still sends its one row at end of records: COUNT(*) is 0, SUM is NULL, one row and not one per worker. The new test checks each of those rather than assuming them. main.parallel_query_aggregate_whole is that test: serial against parallel for COUNT, COUNT(col), SUM, MIN, MAX and AVG over integer, decimal and double columns; the empty case; that exactly one row is sent; and that a DISTINCT aggregate, STD, GROUP_CONCAT and a bare column outside an aggregate are still refused and run serially. main.parallel_query_aggregate gains the fall-back case, where the container turns out to have no group index: the constant group reaches the same fall-back, ships plain rows, and the manager aggregates them a row at a time. Known and accepted: adding doubles is not associative, so SUM and AVG over DOUBLE or FLOAT can differ from the serial answer, and between runs, in the last significant bit -- which worker summed which chunk is scheduling. Decimal and integer aggregates are exact and unaffected. The test rounds well clear of the last bit rather than leaving floats uncovered. Under a forced-worker sweep of the main suite (--parallel-worker-threads=4) three more tests differ than before, all of them EXPLAIN text where a query of this shape now says ALL_parallel or range_parallel: main.costs, main.func_group_innodb and main.innodb_full_scan. No value changes in any of them. This commit was prepared with Claude Code (Opus 5), which established that Item_sum::add() already honours a directly added partial -- the fact the whole change rests on -- and found that a container without a group would have dropped an average's count.
25 KiB
25 KiB