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)
{
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;
}

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);
if (my_hash_insert(&prelocking_ctx->sroutines, (uchar *)rn))
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->m_handler= handler;
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)
{
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->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)
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);
value= val;

View file

@ -7601,7 +7601,8 @@ class multi_update :public select_result_interceptor
{
TABLE_LIST *all_tables; /* query/update command tables */
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 **tmp_tables, *main_table, *table_to_update;
TMP_TABLE_PARAM *tmp_table_param;
@ -7633,6 +7634,7 @@ class multi_update :public select_result_interceptor
ha_rows updated_sys_ver;
bool has_vers_fields;
const table_map tables_to_update;
public:
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->number= with_list.elements;
elem->spec->with_element= elem;
with_list.link_in_list(elem, &elem->next);
with_list.insert(elem, &elem->next);
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)
{
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;
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
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);
return sphead->add_instr(sp_fld);
@ -8263,7 +8263,7 @@ Item *LEX::create_and_link_Item_trigger_field(THD *thd,
in trigger.
*/
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);
return trg_fld;

View file

@ -61,7 +61,7 @@ public:
next= &first;
}
inline void link_in_list(T *element, T **next_ptr)
inline void insert(T *element, T **next_ptr)
{
elements++;
(*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();
thd->lex->
first_select_lex()->table_list.link_in_list(&table_list,
first_select_lex()->table_list.insert(&table_list,
&table_list.next_local);
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->item=item_ptr;
thd->lex->proc_list.link_in_list(order, &order->next);
thd->lex->proc_list.insert(order, &order->next);
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->counter_used= 0;
order->fast_field_copier_setup= 0;
list.link_in_list(order, &order->next);
list.insert(order, &order->next);
DBUG_RETURN(0);
}
@ -8202,7 +8202,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
and SELECT.
*/
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;
#ifdef WITH_PARTITION_STORAGE_ENGINE
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)
{
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);
fake_select_lex->context.table_list=
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<Item> *field_list, List<Item> *value_list,
enum enum_duplicates handle_duplicates_arg,
bool ignore_arg):
select_result_interceptor(thd_arg),
all_tables(table_list), leaves(leaves_list), update_tables(0),
tmp_tables(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)
List<Item> *field_list,
List<Item> *value_list,
enum enum_duplicates handle_duplicates_arg,
bool ignore_arg)
: select_result_interceptor(thd_arg),
all_tables(table_list),
leaves(leaves_list),
update_tables(0),
tmp_tables(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)
{
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);
TABLE_LIST *tbl;
while ((tbl =li++))
@ -1812,6 +1834,24 @@ bool multi_update::init(THD *thd)
if (updated_leaves.push_back(tbl, thd->mem_root))
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;
}
@ -1838,14 +1878,13 @@ int multi_update::prepare(List<Item> &not_used_values,
{
TABLE_LIST *table_ref;
SQL_I_List<TABLE_LIST> update;
table_map tables_to_update;
SQL_I_List<TABLE_LIST> update_list;
Item_field *item;
List_iterator_fast<Item> field_it(*fields);
List_iterator_fast<Item> value_it(*values);
uint i, max_fields;
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");
if (prepared)
@ -1856,98 +1895,69 @@ int multi_update::prepare(List<Item> &not_used_values,
thd->cuted_fields=0L;
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
TABLE::tmp_set by pointing TABLE::read_set to it and then restore it after
setup_fields().
*/
while ((table_ref= ti++))
while ((table_ref= update_targets_iter++))
{
if (table_ref->is_jtbm())
continue;
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;
bitmap_clear_all(table->read_set);
}
DBUG_ASSERT(table->read_set == &table->def_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
reference tables
*/
int error= setup_fields(thd, Ref_ptr_array(),
*values, MARK_COLUMNS_READ, 0, NULL, 0) ||
TABLE::check_assignability_explicit_fields(*fields, *values,
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;
if (tables_to_update & table->map)
{
table->read_set= &table->def_read_set;
bitmap_union(table->read_set, &table->tmp_set);
if (!(thd->lex->context_analysis_only & CONTEXT_ANALYSIS_ONLY_PREPARE))
table->file->prepare_for_modify(true, true);
}
table->read_set= &table->def_read_set;
bitmap_union(table->read_set, &table->tmp_set);
if (!(thd->lex->context_analysis_only & CONTEXT_ANALYSIS_ONLY_PREPARE))
table->file->prepare_for_modify(true, true);
}
if (unlikely(error))
DBUG_RETURN(1);
/*
Save tables being updated in update_tables
update_table->shared is position for table
Don't use key read on tables that are updated
Save tables that we will update into update_list.
table_ref->shared links this table to its corresponding temporary table
for collecting row ids.
Don't use key read on tables that are updated.
*/
update.empty();
ti.rewind();
while ((table_ref= ti++))
update_list.empty();
update_targets_iter.rewind();
for (uint index= 0; (table_ref= update_targets_iter++);)
{
/* TODO: add support of view of join support */
if (table_ref->is_jtbm())
continue;
TABLE *table=table_ref->table;
leaf_table_count++;
if (tables_to_update & table->map)
{
TABLE_LIST *tl= (TABLE_LIST*) thd->memdup(table_ref,
sizeof(*tl));
if (!tl)
DBUG_RETURN(1);
update.link_in_list(tl, &tl->next_local);
table_ref->shared= tl->shared= table_count++;
table->no_keyread=1;
table->covering_keys.clear_all();
table->prepare_triggers_for_update_stmt_or_event();
table->reset_default_fields();
}
TABLE_LIST *tl= (TABLE_LIST*) thd->memdup(table_ref,
sizeof(*tl));
if (!tl)
DBUG_RETURN(1);
update_list.insert(tl, &tl->next_local);
table_ref->shared= tl->shared= index++;
table->no_keyread=1;
table->covering_keys.clear_all();
table->prepare_triggers_for_update_stmt_or_event();
table->reset_default_fields();
}
table_count= update.elements;
update_tables= update.first;
update_tables= update_list.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))
DBUG_RETURN(1);
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)
{
TABLE *table=table_ref->table;
uint cnt= table_ref->shared;
uint index= table_ref->shared;
List<Item> temp_fields;
ORDER group;
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
table. The first field in the temporary table is a pointer to the
@ -2205,9 +2213,6 @@ loop_end:
TABLE *tbl= table;
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
clause) for which we will store row position in the temporary table
@ -2225,13 +2230,14 @@ loop_end:
DBUG_RETURN(1);
} 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 */
bzero((char*) &group, sizeof(group));
group.direction= ORDER::ORDER_ASC;
group.item= (Item**) temp_fields.head_ref();
tmp_param= &tmp_table_param[index];
tmp_param->init();
tmp_param->tmp_name="update";
tmp_param->field_count= temp_fields.elements;
@ -2240,13 +2246,13 @@ loop_end:
/* small table, ignore @@big_tables */
my_bool save_big_tables= thd->variables.big_tables;
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,
TMP_TABLE_ALL_COLUMNS, HA_POS_ERROR, &empty_clex_str);
thd->variables.big_tables= save_big_tables;
if (!tmp_tables[cnt])
if (!tmp_tables[index])
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;
DBUG_RETURN(0);