mirror of
https://github.com/MariaDB/server.git
synced 2026-10-23 18:49:29 +03:00
PQ: Code tidy ups and fixups of tidyups.
parallel_query_aggregate.test: fix statistics impersonalize language in can_parallel_scan_jointab_access() remove dead code (trace_parallel_scan_options) add an assert to ensure we do not use Item_sum_avg without two packed fields minor comment fixups remove enum element NESTED_LOOP_DECLINED and use a local bool instead fix silly code in pwt_row_layout::build_aggregates (throwing away a pointer then searching for it again later); 'one' starts as nullptr, because GCC cannot see that whole_table is unchanged between the two blocks and warns -Wmaybe-uninitialized in an ASAN build remove pwt_batch* worker->manager streaming functions tidy up Parallel_scan_partitioner::Scan_ctx::create_chunks to use create_chunks_unsplit The 'one= nullptr' initialisation was added with Claude Code (Opus 5.5), which traced the -Wmaybe-uninitialized warning to this change and folded the fix in here.
This commit is contained in:
@@ -16,6 +16,7 @@
|
||||
--source include/not_embedded.inc
|
||||
--source include/have_sequence.inc
|
||||
--source include/have_debug.inc
|
||||
--source include/innodb_stable_estimates.inc
|
||||
|
||||
--disable_service_connection
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@ Parallel_queries_executed 0
|
||||
SELECT JSON_EXTRACT(trace, '$**.parallel_scan_declined_because') AS why
|
||||
FROM information_schema.optimizer_trace;
|
||||
why
|
||||
["an index supplies the ORDER BY order, so we can't split rows", "no table this plan reads can be divided"]
|
||||
["an index supplies the ORDER BY order, rows aren't splittable", "no table this plan reads can be divided"]
|
||||
SET SESSION optimizer_trace= @save_trace;
|
||||
SET SESSION parallel_worker_threads=default;
|
||||
DROP TABLE t1;
|
||||
|
||||
@@ -26,7 +26,7 @@ why
|
||||
["grouped: a select list item reads a column outside an aggregate that GROUP BY does not name, so its value is whichever row the terminal happened to hold", "the plan needs a temporary table and the workers cannot pre-aggregate into it"]
|
||||
# an index supplying the ORDER BY
|
||||
why
|
||||
["an index supplies the ORDER BY order, so we can't split rows", "no table this plan reads can be divided"]
|
||||
["an index supplies the ORDER BY order, rows aren't splittable", "no table this plan reads can be divided"]
|
||||
# a sort this thread cannot do over its container
|
||||
why
|
||||
["the manager cannot sort what the workers produce (see pwt_manager_sort_order)", "no table this plan reads can be divided"]
|
||||
|
||||
@@ -196,48 +196,6 @@ bool jointab_can_be_parallel_scanned(JOIN_TAB *tab, bool trace)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
@brief
|
||||
Trace our parallel scan table option.
|
||||
*/
|
||||
|
||||
void trace_parallel_scan_options(JOIN *join)
|
||||
{
|
||||
if (unlikely(join->thd->trace_started()))
|
||||
{
|
||||
JOIN_TAB *scan_tab= first_linear_tab(join, WITH_BUSH_ROOTS,
|
||||
WITHOUT_CONST_TABLES);
|
||||
JOIN_TAB *sorted= NULL;
|
||||
for (uint t= join->const_tables; t < join->table_count; t++)
|
||||
if (join->join_tab[t].filesort)
|
||||
{
|
||||
sorted= join->join_tab + t;
|
||||
break;
|
||||
}
|
||||
|
||||
const bool divisible= jointab_can_be_parallel_scanned(scan_tab, false) &&
|
||||
!sorted;
|
||||
Json_writer_object trace_pscan(join->thd);
|
||||
if (divisible)
|
||||
trace_pscan.add("chosen_for_parallel_scan", scan_tab->table->alias.c_ptr());
|
||||
else
|
||||
{
|
||||
trace_pscan.add("parallel_scan_abandoned",
|
||||
scan_tab ? scan_tab->table->alias.c_ptr() : "");
|
||||
/* Named so it can be grepped for: "cause" is a common trace key. */
|
||||
trace_pscan.add("parallel_scan_abandoned_because",
|
||||
sorted ?
|
||||
"the rows would have to reach the join sorted" :
|
||||
join->ordered_index_usage == join->ordered_index_group_by ?
|
||||
"an index now supplies the GROUP BY order" :
|
||||
join->ordered_index_usage == join->ordered_index_order_by ?
|
||||
"an index now supplies the ORDER BY order" :
|
||||
"the access path can no longer be divided in chunks");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
@brief
|
||||
The whole condition this table has to be filtered by, whatever the plan did
|
||||
@@ -2105,6 +2063,31 @@ bool pwt_manager::setup_worker_preagg(THD *thd, pwt_worker *worker)
|
||||
DBUG_ASSERT(worker->exec.result.table->s->fields ==
|
||||
layout.recv.table->s->fields);
|
||||
|
||||
#ifndef DBUG_OFF
|
||||
/*
|
||||
An AVG partial is only mergeable if the container holds its count beside
|
||||
its sum. Item_sum_avg::create_tmp_field() packs the two into one binary
|
||||
string field -- but only when the table has a group, which is the whole
|
||||
reason the whole-table shape is given a constant group. Nothing else
|
||||
enforces that, so check it here: the unpacked form is a plain double or
|
||||
decimal field, and the manager would read it as a sum with no count.
|
||||
*/
|
||||
for (uint i= 0; i < layout.n_sums; i++)
|
||||
{
|
||||
Item_sum *s= worker->exec.sums[i];
|
||||
if (s->sum_func() != Item_sum::AVG_FUNC)
|
||||
continue;
|
||||
Item_sum_avg *avg= (Item_sum_avg *) s;
|
||||
Field *f= worker->exec.result.table->field[layout.n_ship_base + i];
|
||||
DBUG_ASSERT(f->type() == MYSQL_TYPE_STRING &&
|
||||
f->charset() == &my_charset_bin &&
|
||||
f->pack_length() ==
|
||||
sizeof(longlong) +
|
||||
(avg->result_type() == DECIMAL_RESULT ? avg->dec_bin_size
|
||||
: sizeof(double)));
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
Aggregation JOIN_TAB send to end_update().
|
||||
It reads the table, the param (for the key buffer, the Copy_field pairs and
|
||||
@@ -2230,11 +2213,14 @@ static enum_nested_loop_state pwt_end_send(JOIN *join, JOIN_TAB *join_tab,
|
||||
if (pwt_self->manager->row_layout().grouped)
|
||||
{
|
||||
THD *thd= pwt_self->thd;
|
||||
// record local memory used so that if a HEAP->Aria conversion happens...
|
||||
/*
|
||||
record local memory used so that if a we allocate another block, or
|
||||
HEAP->Aria conversion happens, then owed gets transferred to the manager
|
||||
*/
|
||||
const int64 before= thd->status_var.local_memory_used;
|
||||
const enum_nested_loop_state rc= pwt_self->exec.aggr_tab->aggr->put_record();
|
||||
const int64 owed= thd->status_var.local_memory_used - before;
|
||||
if (unlikely(owed)) // we can account for it here
|
||||
if (owed)
|
||||
{
|
||||
thd->status_var.local_memory_used= before;
|
||||
pwt_self->sink->account_spilled_memory(owed);
|
||||
@@ -2935,8 +2921,8 @@ bool can_parallel_scan_jointab_access(JOIN_TAB *join_tab, bool trace)
|
||||
if (join_tab->join->ordered_index_usage != JOIN::ordered_index_void)
|
||||
return pwt_decline(join_tab->join, trace,
|
||||
join_tab->join->ordered_index_usage == JOIN::ordered_index_group_by
|
||||
? "an index supplies the GROUP BY order, so we can't split rows"
|
||||
: "an index supplies the ORDER BY order, so we can't split rows");
|
||||
? "an index supplies the GROUP BY order, rows aren't splittable"
|
||||
: "an index supplies the ORDER BY order, rows aren't splittable");
|
||||
if (!((join_tab->type == JT_ALL || join_tab->type == JT_RANGE) &&
|
||||
join_tab->read_first_record == join_init_read_record))
|
||||
return pwt_decline(join_tab->join, trace,
|
||||
@@ -3180,13 +3166,15 @@ void parallel_join_check(JOIN *join)
|
||||
@brief
|
||||
|
||||
Called from do_select() in sql_select.cc, run either the worker side
|
||||
join, the scan only in the workers, or return NESTED_LOOP_DECLINED
|
||||
join, the scan only in the workers, or set declined
|
||||
*/
|
||||
|
||||
enum_nested_loop_state do_select_parallel(JOIN *join)
|
||||
enum_nested_loop_state do_select_parallel(JOIN *join, bool *declined)
|
||||
{
|
||||
*declined= true;
|
||||
|
||||
if (!join->worker_side_parallel)
|
||||
return NESTED_LOOP_DECLINED;
|
||||
return NESTED_LOOP_ERROR;
|
||||
|
||||
/*
|
||||
PROTOTYPE, behind debug_dbug='+d,pwt_scan_only'. The workers are a
|
||||
@@ -3197,12 +3185,15 @@ enum_nested_loop_state do_select_parallel(JOIN *join)
|
||||
*/
|
||||
if (pwt_scan_only_enabled())
|
||||
{
|
||||
int rc;
|
||||
JOIN_TAB *scan_tab= first_linear_tab(join, WITH_BUSH_ROOTS,
|
||||
WITHOUT_CONST_TABLES);
|
||||
join->worker_side_parallel= false;
|
||||
if (run_scan_only_workers(join, scan_tab) > 0)
|
||||
return NESTED_LOOP_ERROR;
|
||||
return NESTED_LOOP_DECLINED;
|
||||
rc= run_scan_only_workers(join, scan_tab);
|
||||
if (rc == 0)
|
||||
return NESTED_LOOP_OK;
|
||||
*declined= false;
|
||||
return NESTED_LOOP_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -3215,11 +3206,14 @@ enum_nested_loop_state do_select_parallel(JOIN *join)
|
||||
*/
|
||||
int wr= run_worker_side_join(join, scan_tab);
|
||||
if (wr >= 0)
|
||||
{
|
||||
*declined= false;
|
||||
return wr ? NESTED_LOOP_ERROR : NESTED_LOOP_OK;
|
||||
}
|
||||
else
|
||||
join->worker_side_parallel= false; // declined, run serially
|
||||
}
|
||||
|
||||
return NESTED_LOOP_DECLINED;
|
||||
return NESTED_LOOP_ERROR;
|
||||
}
|
||||
|
||||
|
||||
@@ -394,6 +394,7 @@ bool pwt_row_layout::build(THD *thd, JOIN *join_arg, TABLE **tables,
|
||||
|
||||
bool pwt_row_layout::build_aggregates(THD *thd, ORDER *plan_group)
|
||||
{
|
||||
Item *one= nullptr;
|
||||
n_sums= 0;
|
||||
for (Item_sum **s= join->sum_funcs; *s; s++)
|
||||
n_sums++;
|
||||
@@ -438,7 +439,7 @@ bool pwt_row_layout::build_aggregates(THD *thd, ORDER *plan_group)
|
||||
if (whole_table)
|
||||
{
|
||||
DBUG_ASSERT(!n_group);
|
||||
Item *one= new (thd->mem_root) Item_int(thd, (longlong) 1, 1);
|
||||
one= new (thd->mem_root) Item_int(thd, (longlong) 1, 1);
|
||||
if (!one || result_defn.push_back(one, thd->mem_root))
|
||||
{
|
||||
my_error(ER_OUTOFMEMORY, MYF(0), (int) sizeof(Item_int));
|
||||
@@ -454,13 +455,8 @@ bool pwt_row_layout::build_aggregates(THD *thd, ORDER *plan_group)
|
||||
}
|
||||
if (whole_table)
|
||||
{
|
||||
List_iterator_fast<Item> di(result_defn);
|
||||
Item *defn_item= nullptr;
|
||||
for (uint j= 0; j < result_defn.elements; j++)
|
||||
defn_item= di++; // the constant, pushed last
|
||||
|
||||
bzero((char *) &group_defn[0], sizeof(ORDER));
|
||||
group_defn[0].item_ptr= defn_item;
|
||||
group_defn[0].item_ptr= one;
|
||||
group_defn[0].item= &group_defn[0].item_ptr;
|
||||
group_defn[0].direction= ORDER::ORDER_ASC;
|
||||
group_defn[0].next= nullptr;
|
||||
@@ -555,7 +551,7 @@ bool pwt_row_layout::make_container_from(THD *thd, List<Item> &defn,
|
||||
ORDER *group)
|
||||
{
|
||||
/*
|
||||
out needs it's own TMP_TABLE_PARAM because
|
||||
out needs its own TMP_TABLE_PARAM because
|
||||
1) create_tmp_table() overwrites param->func_count with the number
|
||||
of items it actually has to copy, so a second table built from the same
|
||||
param allocates fewer fields than the layout needs.
|
||||
@@ -1159,252 +1155,3 @@ pwt_row_source *pwt_create_transport(THD *thd, pwt_manager *mgr,
|
||||
}
|
||||
return src;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/*
|
||||
pwt_batch* classes implement streaming from workers to the manager
|
||||
they may be useful in the future, so left here.
|
||||
*/
|
||||
|
||||
bool pwt_batch_sink::init(pwt_manager *mgr, pwt_batch_source *peer_arg,
|
||||
uint reclength_arg)
|
||||
{
|
||||
manager= mgr;
|
||||
peer= peer_arg;
|
||||
reclength= reclength_arg;
|
||||
count= 0;
|
||||
full= false;
|
||||
rows= (uchar*) my_malloc(key_memory_pwt_batch_rows,
|
||||
(size_t) PWT_ROW_GANULARITY * reclength, MYF(MY_WME));
|
||||
return rows == nullptr;
|
||||
}
|
||||
|
||||
|
||||
void pwt_batch_sink::cleanup()
|
||||
{
|
||||
my_free(rows);
|
||||
rows= nullptr;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
@brief
|
||||
Hand this worker's filled buffer to the manager.
|
||||
|
||||
@description
|
||||
Marks the buffer ready and blocks until the manager has drained it (clears
|
||||
'full') or asks the producers to stop. On return the buffer is the worker's
|
||||
again: either to refill, or to abandon.
|
||||
|
||||
This is the only place a worker waits for the manager, and it is what the
|
||||
temporary-table transport exists to remove.
|
||||
|
||||
@return
|
||||
true the consumer asked us to stop
|
||||
false the buffer was drained; refill it
|
||||
*/
|
||||
|
||||
bool pwt_batch_sink::handoff()
|
||||
{
|
||||
DBUG_ENTER("pwt_batch_sink::handoff");
|
||||
mysql_mutex_lock(&manager->LOCK_data);
|
||||
if (manager->workers_must_stop)
|
||||
{
|
||||
mysql_mutex_unlock(&manager->LOCK_data);
|
||||
DBUG_RETURN(true);
|
||||
}
|
||||
full= true;
|
||||
mysql_cond_signal(&manager->COND_data_avail); // wake the consumer
|
||||
while (full && !manager->workers_must_stop)
|
||||
{
|
||||
mysql_cond_wait(&peer->COND_data_space, &manager->LOCK_data);
|
||||
DBUG_PRINT("info", ("worker wakes"));
|
||||
}
|
||||
bool stopped= manager->workers_must_stop;
|
||||
mysql_mutex_unlock(&manager->LOCK_data);
|
||||
DBUG_RETURN(stopped);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
@brief
|
||||
Take one finished row: copy its record image into the buffer, handing the
|
||||
buffer over when it fills.
|
||||
*/
|
||||
|
||||
int pwt_batch_sink::emit_row(const uchar *rec)
|
||||
{
|
||||
memcpy(rows + (size_t) count * reclength, rec, reclength);
|
||||
if (++count == PWT_ROW_GANULARITY)
|
||||
{
|
||||
if (handoff()) // manager asked us to stop
|
||||
return PWT_EMIT_STOP;
|
||||
count= 0; // buffer drained; refill
|
||||
}
|
||||
return PWT_EMIT_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Hand over the final partial buffer. A stop arriving now is not interesting:
|
||||
this producer has finished anyway.
|
||||
*/
|
||||
|
||||
bool pwt_batch_sink::flush()
|
||||
{
|
||||
if (count)
|
||||
handoff();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
pwt_batch_source -- the consuming end of the batch transport
|
||||
*****************************************************************************/
|
||||
|
||||
bool pwt_batch_source::init(THD *thd, pwt_manager *mgr, uint n_workers,
|
||||
uint reclength_arg)
|
||||
{
|
||||
manager= mgr;
|
||||
n_sinks= n_workers;
|
||||
reclength= reclength_arg;
|
||||
cur= nullptr;
|
||||
cur_cursor= 0;
|
||||
if (!(sinks= thd->alloc<pwt_batch_sink*>(n_workers)))
|
||||
return true;
|
||||
for (uint i= 0; i < n_workers; i++)
|
||||
sinks[i]= nullptr;
|
||||
mysql_cond_init(key_COND_pwt_data_space, &COND_data_space, nullptr);
|
||||
inited= true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
pwt_row_sink *pwt_batch_source::make_sink(THD *thd, uint worker_nr,
|
||||
pwt_row_container *container)
|
||||
{
|
||||
DBUG_ASSERT(worker_nr < n_sinks);
|
||||
/*
|
||||
Not used: this transport copies the record out of the container rather than
|
||||
keeping it, so it needs only the size, which it has from the layout.
|
||||
*/
|
||||
(void) container;
|
||||
pwt_batch_sink *s= new (thd->mem_root) pwt_batch_sink;
|
||||
if (!s || s->init(manager, this, reclength))
|
||||
{
|
||||
my_error(ER_OUTOFMEMORY, MYF(0),
|
||||
(int) (PWT_ROW_GANULARITY * reclength));
|
||||
return nullptr;
|
||||
}
|
||||
sinks[worker_nr]= s;
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Release every producer blocked waiting for its buffer back, so it sees the
|
||||
manager's stop request. The caller sets that request; this only wakes them.
|
||||
*/
|
||||
|
||||
void pwt_batch_source::wake_producers()
|
||||
{
|
||||
mysql_mutex_assert_owner(&manager->LOCK_data);
|
||||
mysql_cond_broadcast(&COND_data_space);
|
||||
}
|
||||
|
||||
|
||||
void pwt_batch_source::cleanup()
|
||||
{
|
||||
if (inited)
|
||||
{
|
||||
mysql_cond_destroy(&COND_data_space);
|
||||
inited= false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
@brief
|
||||
Copy the next result row's record image into dst.
|
||||
|
||||
@description
|
||||
Drains one worker's buffer at a time (cur), advancing cur_cursor through
|
||||
its rows; when the buffer is exhausted it releases that worker to refill
|
||||
and picks the next ready one. Blocks when no buffer is momentarily ready.
|
||||
|
||||
This is also the manager's only wait, so it is where the team's own state
|
||||
is noticed: a worker killed is propagated to the manager's THD, a worker
|
||||
error aborts, and "no buffer ready and nobody still running" is end of
|
||||
data.
|
||||
|
||||
@return
|
||||
0 = row copied into dst, -1 = end of data, 1 = error.
|
||||
*/
|
||||
|
||||
int pwt_batch_source::next_row(uchar *dst)
|
||||
{
|
||||
DBUG_ENTER("pwt_batch_source::next_row");
|
||||
THD *thd= manager->thd;
|
||||
struct timespec wait;
|
||||
wait.tv_nsec= 0;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (cur) // draining a worker's buffer
|
||||
{
|
||||
if (cur_cursor < cur->count)
|
||||
{
|
||||
memcpy(dst, cur->rows + (size_t) cur_cursor * reclength, reclength);
|
||||
cur_cursor++;
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
// buffer drained; release the worker so it can refill
|
||||
mysql_mutex_lock(&manager->LOCK_data);
|
||||
pwt_batch_sink *drained= cur;
|
||||
cur= nullptr;
|
||||
drained->full= false; // buffer is the worker's again
|
||||
mysql_cond_broadcast(&COND_data_space); // wake it to refill
|
||||
mysql_mutex_unlock(&manager->LOCK_data);
|
||||
// fall through and look for the next ready worker
|
||||
}
|
||||
|
||||
// find the next worker whose buffer is filled and ready
|
||||
pwt_batch_sink *next= nullptr;
|
||||
PSI_stage_info old_stage;
|
||||
mysql_mutex_lock(&manager->LOCK_data);
|
||||
for (;;)
|
||||
{
|
||||
for (uint i= 0; i < n_sinks; i++)
|
||||
if (sinks[i] && sinks[i]->full)
|
||||
{
|
||||
next= sinks[i];
|
||||
break;
|
||||
}
|
||||
if (next)
|
||||
break;
|
||||
|
||||
int res;
|
||||
if ((res= manager->locked__process_manager_wakeup()) ||
|
||||
(res=(thd->killed != NOT_KILLED)))
|
||||
{
|
||||
mysql_mutex_unlock(&manager->LOCK_data);
|
||||
DBUG_RETURN(res);
|
||||
}
|
||||
// wait for a batch, a finishing worker, or a 1s tick to re-check killed.
|
||||
// ENTER_COND/EXIT_COND publish the "Reading data from parallel workers"
|
||||
// stage and register the cond so a KILL of the manager wakes it.
|
||||
wait.tv_sec= time(0) + 1;
|
||||
thd->ENTER_COND(&manager->COND_data_avail, &manager->LOCK_data,
|
||||
&stage_reading_data_from_parallel_worker, &old_stage);
|
||||
mysql_cond_timedwait(&manager->COND_data_avail, &manager->LOCK_data,
|
||||
&wait);
|
||||
thd->EXIT_COND(&old_stage); // unlocks LOCK_data
|
||||
mysql_mutex_lock(&manager->LOCK_data); // re-lock for the next pass
|
||||
}
|
||||
cur= next;
|
||||
cur_cursor= 0; // start of next's buffer
|
||||
mysql_mutex_unlock(&manager->LOCK_data);
|
||||
// loop back and drain cur
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -532,8 +532,7 @@ extern bool can_parallel_scan_jointab_access(JOIN_TAB *join_tab,
|
||||
bool trace= false);
|
||||
extern bool pwt_decline(JOIN *join, bool trace, const char *why);
|
||||
extern bool pwt_scan_only_enabled();
|
||||
extern void trace_parallel_scan_options(JOIN *join);
|
||||
extern void parallel_join_check(JOIN *join);
|
||||
|
||||
extern enum_nested_loop_state do_select_parallel(JOIN *join);
|
||||
extern enum_nested_loop_state do_select_parallel(JOIN *join, bool *declined);
|
||||
#endif
|
||||
|
||||
@@ -3546,9 +3546,6 @@ int JOIN::optimize_stage2()
|
||||
}
|
||||
}
|
||||
|
||||
if (worker_side_parallel)
|
||||
trace_parallel_scan_options(this);
|
||||
|
||||
if (having)
|
||||
having_is_correlated= MY_TEST(having->used_tables() & OUTER_REF_TABLE_BIT);
|
||||
tmp_having= having;
|
||||
@@ -24292,6 +24289,7 @@ do_select(JOIN *join, Procedure *procedure)
|
||||
}
|
||||
else
|
||||
{
|
||||
bool declined= false;
|
||||
DBUG_EXECUTE_IF("show_explain_probe_do_select",
|
||||
if (dbug_user_var_equals_int(join->thd,
|
||||
"show_explain_probe_select_id",
|
||||
@@ -24313,7 +24311,8 @@ do_select(JOIN *join, Procedure *procedure)
|
||||
JOIN_TAB *join_tab= join->join_tab +
|
||||
(join->tables_list ? join->const_tables : 0);
|
||||
|
||||
if ((error= do_select_parallel(join)) == NESTED_LOOP_DECLINED)
|
||||
error= do_select_parallel(join, &declined);
|
||||
if (declined)
|
||||
{
|
||||
if (join->outer_ref_cond && !join->outer_ref_cond->val_bool())
|
||||
error= NESTED_LOOP_NO_MORE_ROWS;
|
||||
|
||||
@@ -444,8 +444,7 @@ enum enum_nested_loop_state
|
||||
{
|
||||
NESTED_LOOP_KILLED= -2, NESTED_LOOP_ERROR= -1,
|
||||
NESTED_LOOP_OK= 0, NESTED_LOOP_NO_MORE_ROWS= 1,
|
||||
NESTED_LOOP_QUERY_LIMIT= 3, NESTED_LOOP_CURSOR_LIMIT= 4,
|
||||
NESTED_LOOP_DECLINED= 5
|
||||
NESTED_LOOP_QUERY_LIMIT= 3, NESTED_LOOP_CURSOR_LIMIT= 4
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -808,14 +808,15 @@ dberr_t Parallel_scan_partitioner::Scan_ctx::create_chunk(const Bounds &bounds,
|
||||
return (err);
|
||||
}
|
||||
|
||||
dberr_t Parallel_scan_partitioner::Scan_ctx::create_chunks_unsplit()
|
||||
dberr_t Parallel_scan_partitioner::Scan_ctx::create_chunks_unsplit(bool resplit,
|
||||
size_t split_point)
|
||||
{
|
||||
size_t i{};
|
||||
|
||||
for (auto bounds : m_bounds_list) {
|
||||
const bool last = (i + 1 == m_bounds_list.size());
|
||||
|
||||
if (dberr_t err= create_chunk(bounds, false,
|
||||
if (dberr_t err= create_chunk(bounds, resplit && (i >= split_point),
|
||||
last && m_config.m_scan_range.m_end_inclusive))
|
||||
return err;
|
||||
|
||||
@@ -901,27 +902,7 @@ dberr_t Parallel_scan_partitioner::Scan_ctx::create_chunks()
|
||||
}
|
||||
}
|
||||
|
||||
size_t i{};
|
||||
|
||||
for (auto bounds : m_bounds_list) {
|
||||
/* Only the last chunk ends at the caller's upper bound, so only it can
|
||||
be inclusive. */
|
||||
const bool last = (i + 1 == m_bounds_list.size());
|
||||
|
||||
auto err = create_chunk(bounds, i >= split_point,
|
||||
last && m_config.m_scan_range.m_end_inclusive);
|
||||
|
||||
if (err != DB_SUCCESS) {
|
||||
return (err);
|
||||
}
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
m_bounds_list.clear();
|
||||
m_bounds_list.shrink_to_fit();
|
||||
|
||||
return DB_SUCCESS;
|
||||
return create_chunks_unsplit(true, split_point);
|
||||
}
|
||||
|
||||
dberr_t Parallel_scan_partitioner::add_scan(
|
||||
@@ -1016,7 +997,7 @@ dberr_t Parallel_scan_partitioner::create_chunks()
|
||||
|
||||
scan_ctx->coalesce_bounds(std::max(share, size_t{1}));
|
||||
|
||||
if (dberr_t err = scan_ctx->create_chunks_unsplit())
|
||||
if (dberr_t err = scan_ctx->create_chunks_unsplit(false, 0))
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
@@ -475,7 +475,8 @@ class Parallel_scan_partitioner::Scan_ctx {
|
||||
the division was decided knowing every range's size, so there is nothing
|
||||
left for a worker to correct.
|
||||
@return DB_SUCCESS or error code. */
|
||||
[[nodiscard]] dberr_t create_chunks_unsplit();
|
||||
[[nodiscard]] dberr_t create_chunks_unsplit(bool resplit,
|
||||
size_t split_point);
|
||||
|
||||
/** Walk one level further down, to the children of the sub-trees this
|
||||
range's boundaries name, and keep what that finds in place of them.
|
||||
@@ -484,11 +485,12 @@ class Parallel_scan_partitioner::Scan_ctx {
|
||||
wants -- coalesce_bounds() cuts them back down. What the walk is for is the
|
||||
count: the pages a range covers is how much work it holds, and the only
|
||||
measure of that the tree offers without descending into it.
|
||||
NOTE: MAX_PARALLEL_SCAN_RANGES = 128 see sql/sql_parallel_execution.cc
|
||||
@return DB_SUCCESS or error code. */
|
||||
[[nodiscard]] dberr_t partition_deeper();
|
||||
|
||||
/** Merge this range's boundaries into runs, leaving it with at most
|
||||
'target' chunks. Adjacent chunks are contiguous intervals sharing an
|
||||
2x'target'-1 chunks. Adjacent chunks are contiguous intervals sharing an
|
||||
endpoint, so a run of them merges into the first one's start and the last
|
||||
one's end.
|
||||
@param[in] target the most chunks this range should produce. */
|
||||
|
||||
Reference in New Issue
Block a user