MDEV-30469 (refactoring) Support ORDER BY and LIMIT for multi-table DELETE...

This patch includes a few changes to make the code easier to maintain:
  - Renamed SQL_I_List::link_in_list to SQL_I_List::insert.  link_in_list was
  ambiguous as it could refer to a link or it could refer to a node
  - Remove field_name local variable in multi_update::initialize_tables because
  it is not used when creating the temporary tables
  - multi_update changes:
    - Move temp table callocs to init, a more natural location for them, and moved
    tables_to_update to const member variable so we don't recompute it.
    - Filter out jtbm tables and tables not in the update map, pushing those that
    will be updated into an update_targets container.  This simplifies checks and
    loops in initialize_tables.
This commit is contained in:
Dave Gosselin 2024-12-03 09:48:17 -05:00
parent dfdbec1636
commit 02dc8615f2
12 changed files with 108 additions and 100 deletions

View file

@ -7160,7 +7160,7 @@ bool subselect_single_column_match_engine::partial_match()
void Item_subselect::register_as_with_rec_ref(With_element *with_elem) void Item_subselect::register_as_with_rec_ref(With_element *with_elem)
{ {
with_elem->sq_with_rec_ref.link_in_list(this, &this->next_with_rec_ref); with_elem->sq_with_rec_ref.insert(this, &this->next_with_rec_ref);
with_recursive_reference= true; with_recursive_reference= true;
} }

View file

@ -2415,7 +2415,7 @@ bool sp_add_used_routine(Query_tables_list *prelocking_ctx, Query_arena *arena,
MDL_REQUEST_INIT_BY_KEY(&rn->mdl_request, key, MDL_SHARED, MDL_TRANSACTION); MDL_REQUEST_INIT_BY_KEY(&rn->mdl_request, key, MDL_SHARED, MDL_TRANSACTION);
if (my_hash_insert(&prelocking_ctx->sroutines, (uchar *)rn)) if (my_hash_insert(&prelocking_ctx->sroutines, (uchar *)rn))
return FALSE; return FALSE;
prelocking_ctx->sroutines_list.link_in_list(rn, &rn->next); prelocking_ctx->sroutines_list.insert(rn, &rn->next);
rn->belong_to_view= belong_to_view; rn->belong_to_view= belong_to_view;
rn->m_handler= handler; rn->m_handler= handler;
rn->m_sp_cache_version= 0; rn->m_sp_cache_version= 0;

View file

@ -3166,7 +3166,7 @@ int sp_head::add_instr(sp_instr *instr)
if (instr_trig_fld_list) if (instr_trig_fld_list)
{ {
m_cur_instr_trig_field_items.save_and_clear(instr_trig_fld_list); m_cur_instr_trig_field_items.save_and_clear(instr_trig_fld_list);
m_trg_table_fields.link_in_list( m_trg_table_fields.insert(
instr_trig_fld_list, instr_trig_fld_list,
&instr_trig_fld_list->first->next_trig_field_list); &instr_trig_fld_list->first->next_trig_field_list);
} }

View file

@ -1386,7 +1386,7 @@ bool sp_instr_set_trigger_field::on_after_expr_parsing(THD *thd)
if (!val || !trigger_field) if (!val || !trigger_field)
return true; return true;
thd->spcont->m_sp->m_cur_instr_trig_field_items.link_in_list( thd->spcont->m_sp->m_cur_instr_trig_field_items.insert(
trigger_field, &trigger_field->next_trg_field); trigger_field, &trigger_field->next_trg_field);
value= val; value= val;

View file

@ -7601,7 +7601,8 @@ class multi_update :public select_result_interceptor
{ {
TABLE_LIST *all_tables; /* query/update command tables */ TABLE_LIST *all_tables; /* query/update command tables */
List<TABLE_LIST> *leaves; /* list of leaves of join table tree */ List<TABLE_LIST> *leaves; /* list of leaves of join table tree */
List<TABLE_LIST> updated_leaves; /* list of of updated leaves */ List<TABLE_LIST> updated_leaves; /* a superset of tables which will be updated */
List<TABLE_LIST> update_targets; /* the tables that will be UPDATE'd */
TABLE_LIST *update_tables; TABLE_LIST *update_tables;
TABLE **tmp_tables, *main_table, *table_to_update; TABLE **tmp_tables, *main_table, *table_to_update;
TMP_TABLE_PARAM *tmp_table_param; TMP_TABLE_PARAM *tmp_table_param;
@ -7633,6 +7634,7 @@ class multi_update :public select_result_interceptor
ha_rows updated_sys_ver; ha_rows updated_sys_ver;
bool has_vers_fields; bool has_vers_fields;
const table_map tables_to_update;
public: public:
multi_update(THD *thd_arg, TABLE_LIST *ut, List<TABLE_LIST> *leaves_list, multi_update(THD *thd_arg, TABLE_LIST *ut, List<TABLE_LIST> *leaves_list,

View file

@ -51,7 +51,7 @@ bool With_clause::add_with_element(With_element *elem)
elem->owner= this; elem->owner= this;
elem->number= with_list.elements; elem->number= with_list.elements;
elem->spec->with_element= elem; elem->spec->with_element= elem;
with_list.link_in_list(elem, &elem->next); with_list.insert(elem, &elem->next);
return false; return false;
} }

View file

@ -1112,7 +1112,7 @@ bool mysql_derived_create(THD *thd, LEX *lex, TABLE_LIST *derived)
void TABLE_LIST::register_as_derived_with_rec_ref(With_element *rec_elem) void TABLE_LIST::register_as_derived_with_rec_ref(With_element *rec_elem)
{ {
rec_elem->derived_with_rec_ref.link_in_list(this, &this->next_with_rec_ref); rec_elem->derived_with_rec_ref.insert(this, &this->next_with_rec_ref);
is_derived_with_recursive_reference= true; is_derived_with_recursive_reference= true;
get_unit()->uncacheable|= UNCACHEABLE_DEPENDENT; get_unit()->uncacheable|= UNCACHEABLE_DEPENDENT;
} }

View file

@ -242,7 +242,7 @@ bool LEX::set_trigger_new_row(const LEX_CSTRING *name, Item *val,
Let us add this item to list of all Item_trigger_field Let us add this item to list of all Item_trigger_field
objects in trigger. objects in trigger.
*/ */
sphead->m_cur_instr_trig_field_items.link_in_list(trg_fld, sphead->m_cur_instr_trig_field_items.insert(trg_fld,
&trg_fld->next_trg_field); &trg_fld->next_trg_field);
return sphead->add_instr(sp_fld); return sphead->add_instr(sp_fld);
@ -8263,7 +8263,7 @@ Item *LEX::create_and_link_Item_trigger_field(THD *thd,
in trigger. in trigger.
*/ */
if (likely(trg_fld)) if (likely(trg_fld))
sphead->m_cur_instr_trig_field_items.link_in_list(trg_fld, sphead->m_cur_instr_trig_field_items.insert(trg_fld,
&trg_fld->next_trg_field); &trg_fld->next_trg_field);
return trg_fld; return trg_fld;

View file

@ -61,7 +61,7 @@ public:
next= &first; next= &first;
} }
inline void link_in_list(T *element, T **next_ptr) inline void insert(T *element, T **next_ptr)
{ {
elements++; elements++;
(*next)= element; (*next)= element;

View file

@ -2065,7 +2065,7 @@ dispatch_command_return dispatch_command(enum enum_server_command command, THD *
*/ */
table_list.select_lex= thd->lex->first_select_lex(); table_list.select_lex= thd->lex->first_select_lex();
thd->lex-> thd->lex->
first_select_lex()->table_list.link_in_list(&table_list, first_select_lex()->table_list.insert(&table_list,
&table_list.next_local); &table_list.next_local);
thd->lex->add_to_query_tables(&table_list); thd->lex->add_to_query_tables(&table_list);
@ -8003,7 +8003,7 @@ add_proc_to_list(THD* thd, Item *item)
item_ptr = (Item**) (order+1); item_ptr = (Item**) (order+1);
*item_ptr= item; *item_ptr= item;
order->item=item_ptr; order->item=item_ptr;
thd->lex->proc_list.link_in_list(order, &order->next); thd->lex->proc_list.insert(order, &order->next);
return 0; return 0;
} }
@ -8024,7 +8024,7 @@ bool add_to_list(THD *thd, SQL_I_List<ORDER> &list, Item *item,bool asc)
order->used=0; order->used=0;
order->counter_used= 0; order->counter_used= 0;
order->fast_field_copier_setup= 0; order->fast_field_copier_setup= 0;
list.link_in_list(order, &order->next); list.insert(order, &order->next);
DBUG_RETURN(0); DBUG_RETURN(0);
} }
@ -8202,7 +8202,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
and SELECT. and SELECT.
*/ */
if (likely(!ptr->sequence)) if (likely(!ptr->sequence))
table_list.link_in_list(ptr, &ptr->next_local); table_list.insert(ptr, &ptr->next_local);
ptr->next_name_resolution_table= NULL; ptr->next_name_resolution_table= NULL;
#ifdef WITH_PARTITION_STORAGE_ENGINE #ifdef WITH_PARTITION_STORAGE_ENGINE
ptr->partition_names= partition_names; ptr->partition_names= partition_names;

View file

@ -1056,7 +1056,7 @@ st_select_lex_unit::init_prepare_fake_select_lex(THD *thd_arg,
bool first_execution) bool first_execution)
{ {
thd_arg->lex->current_select= fake_select_lex; thd_arg->lex->current_select= fake_select_lex;
fake_select_lex->table_list.link_in_list(&result_table_list, fake_select_lex->table_list.insert(&result_table_list,
&result_table_list.next_local); &result_table_list.next_local);
fake_select_lex->context.table_list= fake_select_lex->context.table_list=
fake_select_lex->context.first_name_resolution_table= fake_select_lex->context.first_name_resolution_table=

View file

@ -1782,25 +1782,47 @@ bool Multiupdate_prelocking_strategy::handle_end(THD *thd)
} }
multi_update::multi_update(THD *thd_arg, TABLE_LIST *table_list, multi_update::multi_update(THD *thd_arg,
TABLE_LIST *table_list,
List<TABLE_LIST> *leaves_list, List<TABLE_LIST> *leaves_list,
List<Item> *field_list, List<Item> *value_list, List<Item> *field_list,
enum enum_duplicates handle_duplicates_arg, List<Item> *value_list,
bool ignore_arg): enum enum_duplicates handle_duplicates_arg,
select_result_interceptor(thd_arg), bool ignore_arg)
all_tables(table_list), leaves(leaves_list), update_tables(0), : select_result_interceptor(thd_arg),
tmp_tables(0), updated(0), found(0), fields(field_list), all_tables(table_list),
values(value_list), table_count(0), copy_field(0), leaves(leaves_list),
handle_duplicates(handle_duplicates_arg), do_update(1), trans_safe(1), update_tables(0),
transactional_tables(0), ignore(ignore_arg), error_handled(0), prepared(0), tmp_tables(0),
updated_sys_ver(0) updated(0),
found(0),
fields(field_list),
values(value_list),
table_count(0),
copy_field(0),
handle_duplicates(handle_duplicates_arg),
do_update(1),
trans_safe(1),
transactional_tables(0),
ignore(ignore_arg),
error_handled(0),
prepared(0),
updated_sys_ver(0),
tables_to_update(get_table_map(fields))
{ {
// Defer error reporting to multi_update::init whne tables_to_update is zero
// because we don't have exceptions and we can't return values from a constructor.
} }
bool multi_update::init(THD *thd) bool multi_update::init(THD *thd)
{ {
table_map tables_to_update= get_table_map(fields); if (!tables_to_update)
{
my_message(ER_NO_TABLES_USED, ER_THD(thd, ER_NO_TABLES_USED), MYF(0));
return true;
}
List_iterator_fast<TABLE_LIST> li(*leaves); List_iterator_fast<TABLE_LIST> li(*leaves);
TABLE_LIST *tbl; TABLE_LIST *tbl;
while ((tbl =li++)) while ((tbl =li++))
@ -1812,6 +1834,24 @@ bool multi_update::init(THD *thd)
if (updated_leaves.push_back(tbl, thd->mem_root)) if (updated_leaves.push_back(tbl, thd->mem_root))
return true; return true;
} }
List_iterator<TABLE_LIST> updated_leaves_iter(updated_leaves);
TABLE_LIST *table_ref;
while ((table_ref= updated_leaves_iter++))
{
/* TODO: add support of view of join support */
if (table_ref->is_jtbm())
continue;
TABLE *table= table_ref->table;
if (tables_to_update & table->map)
update_targets.push_back(table_ref);
}
table_count= update_targets.elements;
tmp_tables = thd->calloc<TABLE*>(table_count);
tmp_table_param = thd->calloc<TMP_TABLE_PARAM>(table_count);
fields_for_table= thd->alloc<List_item*>(table_count);
values_for_table= thd->alloc<List_item*>(table_count);
return false; return false;
} }
@ -1838,14 +1878,13 @@ int multi_update::prepare(List<Item> &not_used_values,
{ {
TABLE_LIST *table_ref; TABLE_LIST *table_ref;
SQL_I_List<TABLE_LIST> update; SQL_I_List<TABLE_LIST> update_list;
table_map tables_to_update;
Item_field *item; Item_field *item;
List_iterator_fast<Item> field_it(*fields); List_iterator_fast<Item> field_it(*fields);
List_iterator_fast<Item> value_it(*values); List_iterator_fast<Item> value_it(*values);
uint i, max_fields; uint i, max_fields;
uint leaf_table_count= 0; uint leaf_table_count= 0;
List_iterator<TABLE_LIST> ti(updated_leaves); List_iterator<TABLE_LIST> update_targets_iter(update_targets);
DBUG_ENTER("multi_update::prepare"); DBUG_ENTER("multi_update::prepare");
if (prepared) if (prepared)
@ -1856,98 +1895,69 @@ int multi_update::prepare(List<Item> &not_used_values,
thd->cuted_fields=0L; thd->cuted_fields=0L;
THD_STAGE_INFO(thd, stage_updating_main_table); THD_STAGE_INFO(thd, stage_updating_main_table);
tables_to_update= get_table_map(fields);
if (!tables_to_update)
{
my_message(ER_NO_TABLES_USED, ER_THD(thd, ER_NO_TABLES_USED), MYF(0));
DBUG_RETURN(1);
}
/* /*
We gather the set of columns read during evaluation of SET expression in We gather the set of columns read during evaluation of SET expression in
TABLE::tmp_set by pointing TABLE::read_set to it and then restore it after TABLE::tmp_set by pointing TABLE::read_set to it and then restore it after
setup_fields(). setup_fields().
*/ */
while ((table_ref= ti++)) while ((table_ref= update_targets_iter++))
{ {
if (table_ref->is_jtbm())
continue;
TABLE *table= table_ref->table; TABLE *table= table_ref->table;
if (tables_to_update & table->map) DBUG_ASSERT(table->read_set == &table->def_read_set);
{ table->read_set= &table->tmp_set;
DBUG_ASSERT(table->read_set == &table->def_read_set); bitmap_clear_all(table->read_set);
table->read_set= &table->tmp_set;
bitmap_clear_all(table->read_set);
}
} }
/* /*
We have to check values after setup_tables to get covering_keys right in We have to check values after setup_tables to get covering_keys right in
reference tables reference tables
*/ */
int error= setup_fields(thd, Ref_ptr_array(), int error= setup_fields(thd, Ref_ptr_array(),
*values, MARK_COLUMNS_READ, 0, NULL, 0) || *values, MARK_COLUMNS_READ, 0, NULL, 0) ||
TABLE::check_assignability_explicit_fields(*fields, *values, TABLE::check_assignability_explicit_fields(*fields, *values,
ignore); ignore);
ti.rewind(); /*
while ((table_ref= ti++)) Restore TABLE::tmp_set as we promised just before setup_tables.
*/
update_targets_iter.rewind();
while ((table_ref= update_targets_iter++))
{ {
if (table_ref->is_jtbm())
continue;
TABLE *table= table_ref->table; TABLE *table= table_ref->table;
if (tables_to_update & table->map) table->read_set= &table->def_read_set;
{ bitmap_union(table->read_set, &table->tmp_set);
table->read_set= &table->def_read_set; if (!(thd->lex->context_analysis_only & CONTEXT_ANALYSIS_ONLY_PREPARE))
bitmap_union(table->read_set, &table->tmp_set); table->file->prepare_for_modify(true, true);
if (!(thd->lex->context_analysis_only & CONTEXT_ANALYSIS_ONLY_PREPARE))
table->file->prepare_for_modify(true, true);
}
} }
if (unlikely(error)) if (unlikely(error))
DBUG_RETURN(1); DBUG_RETURN(1);
/* /*
Save tables being updated in update_tables Save tables that we will update into update_list.
update_table->shared is position for table table_ref->shared links this table to its corresponding temporary table
Don't use key read on tables that are updated for collecting row ids.
Don't use key read on tables that are updated.
*/ */
update_list.empty();
update.empty(); update_targets_iter.rewind();
ti.rewind(); for (uint index= 0; (table_ref= update_targets_iter++);)
while ((table_ref= ti++))
{ {
/* TODO: add support of view of join support */
if (table_ref->is_jtbm())
continue;
TABLE *table=table_ref->table; TABLE *table=table_ref->table;
leaf_table_count++; leaf_table_count++;
if (tables_to_update & table->map) TABLE_LIST *tl= (TABLE_LIST*) thd->memdup(table_ref,
{ sizeof(*tl));
TABLE_LIST *tl= (TABLE_LIST*) thd->memdup(table_ref, if (!tl)
sizeof(*tl)); DBUG_RETURN(1);
if (!tl) update_list.insert(tl, &tl->next_local);
DBUG_RETURN(1); table_ref->shared= tl->shared= index++;
update.link_in_list(tl, &tl->next_local); table->no_keyread=1;
table_ref->shared= tl->shared= table_count++; table->covering_keys.clear_all();
table->no_keyread=1; table->prepare_triggers_for_update_stmt_or_event();
table->covering_keys.clear_all(); table->reset_default_fields();
table->prepare_triggers_for_update_stmt_or_event();
table->reset_default_fields();
}
} }
table_count= update.elements; update_tables= update_list.first;
update_tables= update.first;
tmp_tables = thd->calloc<TABLE*>(table_count);
tmp_table_param = thd->calloc<TMP_TABLE_PARAM>(table_count);
fields_for_table= thd->alloc<List_item*>(table_count);
values_for_table= thd->alloc<List_item*>(table_count);
if (unlikely(thd->is_fatal_error)) if (unlikely(thd->is_fatal_error))
DBUG_RETURN(1); DBUG_RETURN(1);
for (i=0 ; i < table_count ; i++) for (i=0 ; i < table_count ; i++)
@ -2129,7 +2139,7 @@ multi_update::initialize_tables(JOIN *join)
for (table_ref= update_tables; table_ref; table_ref= table_ref->next_local) for (table_ref= update_tables; table_ref; table_ref= table_ref->next_local)
{ {
TABLE *table=table_ref->table; TABLE *table=table_ref->table;
uint cnt= table_ref->shared; uint index= table_ref->shared;
List<Item> temp_fields; List<Item> temp_fields;
ORDER group; ORDER group;
TMP_TABLE_PARAM *tmp_param; TMP_TABLE_PARAM *tmp_param;
@ -2191,8 +2201,6 @@ loop_end:
} }
} }
tmp_param= tmp_table_param+cnt;
/* /*
Create a temporary table to store all fields that are changed for this Create a temporary table to store all fields that are changed for this
table. The first field in the temporary table is a pointer to the table. The first field in the temporary table is a pointer to the
@ -2205,9 +2213,6 @@ loop_end:
TABLE *tbl= table; TABLE *tbl= table;
do do
{ {
LEX_CSTRING field_name;
field_name.str= tbl->alias.c_ptr();
field_name.length= strlen(field_name.str);
/* /*
Signal each table (including tables referenced by WITH CHECK OPTION Signal each table (including tables referenced by WITH CHECK OPTION
clause) for which we will store row position in the temporary table clause) for which we will store row position in the temporary table
@ -2225,13 +2230,14 @@ loop_end:
DBUG_RETURN(1); DBUG_RETURN(1);
} while ((tbl= tbl_it++)); } while ((tbl= tbl_it++));
temp_fields.append(fields_for_table[cnt]); temp_fields.append(fields_for_table[index]);
/* Make an unique key over the first field to avoid duplicated updates */ /* Make an unique key over the first field to avoid duplicated updates */
bzero((char*) &group, sizeof(group)); bzero((char*) &group, sizeof(group));
group.direction= ORDER::ORDER_ASC; group.direction= ORDER::ORDER_ASC;
group.item= (Item**) temp_fields.head_ref(); group.item= (Item**) temp_fields.head_ref();
tmp_param= &tmp_table_param[index];
tmp_param->init(); tmp_param->init();
tmp_param->tmp_name="update"; tmp_param->tmp_name="update";
tmp_param->field_count= temp_fields.elements; tmp_param->field_count= temp_fields.elements;
@ -2240,13 +2246,13 @@ loop_end:
/* small table, ignore @@big_tables */ /* small table, ignore @@big_tables */
my_bool save_big_tables= thd->variables.big_tables; my_bool save_big_tables= thd->variables.big_tables;
thd->variables.big_tables= FALSE; thd->variables.big_tables= FALSE;
tmp_tables[cnt]=create_tmp_table(thd, tmp_param, temp_fields, tmp_tables[index]=create_tmp_table(thd, tmp_param, temp_fields,
(ORDER*) &group, 0, 0, (ORDER*) &group, 0, 0,
TMP_TABLE_ALL_COLUMNS, HA_POS_ERROR, &empty_clex_str); TMP_TABLE_ALL_COLUMNS, HA_POS_ERROR, &empty_clex_str);
thd->variables.big_tables= save_big_tables; thd->variables.big_tables= save_big_tables;
if (!tmp_tables[cnt]) if (!tmp_tables[index])
DBUG_RETURN(1); DBUG_RETURN(1);
tmp_tables[cnt]->file->extra(HA_EXTRA_WRITE_CACHE); tmp_tables[index]->file->extra(HA_EXTRA_WRITE_CACHE);
} }
join->tmp_table_keep_current_rowid= TRUE; join->tmp_table_keep_current_rowid= TRUE;
DBUG_RETURN(0); DBUG_RETURN(0);