Skip to content

Commit 62f914c

Browse files
committed
Bug#34927110: Failure in Item_ref::real_item, count_field_types
The problem is a missing reference count increment for an Item that is added as part of subquery-to-derived transformation, which is part of a predicate that is later eliminated. Another issue was an Item_field object that was replaced in the select list of a derived table, and subsequently had its reference count decremented when checking for unused columns in derived tables. All instances of adding and replacing expressions in select list when transforming subqueries to use derived tables and joins have been changed so that reference counts are maintained properly. Change-Id: I1c6f528d82d55ea1f3fcd5ec902b71825cbe71d2
1 parent 89eaaff commit 62f914c

File tree

3 files changed

+49
-13
lines changed

3 files changed

+49
-13
lines changed

sql/item.h

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3401,8 +3401,20 @@ class Item : public Parse_tree_node {
34013401
Item_result cmp_context; ///< Comparison context
34023402
private:
34033403
/**
3404-
Number of references to this item from Item_ref objects. Used during
3405-
resolving to manage proper deletion of item sub-trees.
3404+
Number of references to this item. It is used for two purposes:
3405+
1. When eliminating redundant expressions, the reference count is used
3406+
to tell how many Item_ref objects that point to an item. When a
3407+
sub-tree of items is eliminated, it is traversed and any item that
3408+
is referenced from an Item_ref has its reference count decremented.
3409+
Only when the reference count reaches zero is the item actually deleted.
3410+
2. Keeping track of unused expressions selected from merged derived tables.
3411+
An item that is added to the select list of a query block has its
3412+
reference count set to 1. Any references from outer query blocks are
3413+
through Item_ref objects, thus they will cause the reference count
3414+
to be incremented. At end of resolving, the reference counts of all
3415+
items in select list of merged derived tables are decremented, thus
3416+
if the reference count becomes zero, the expression is known to
3417+
be unused and can be removed.
34063418
*/
34073419
uint m_ref_count{0};
34083420
const bool is_parser_item; ///< true if allocated directly by parser

sql/sql_lex.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2214,6 +2214,7 @@ class Query_block : public Query_term {
22142214
bool decorrelate_derived_scalar_subquery_post(
22152215
THD *thd, Table_ref *derived, Lifted_fields_map *lifted_where_fields,
22162216
bool added_card_check);
2217+
void replace_referenced_item(Item *const old_item, Item *const new_item);
22172218
void remap_tables(THD *thd);
22182219
bool resolve_subquery(THD *thd);
22192220
void mark_item_as_maybe_null_if_rollup_item(Item *item);

sql/sql_resolver.cc

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5839,6 +5839,7 @@ static bool replace_aggregate_in_list(Item::Aggregate_replacement &info,
58395839
new_item->update_used_tables();
58405840
if (new_item != select_expr) {
58415841
new_item->hidden = was_hidden;
5842+
new_item->increment_ref_count();
58425843
*lii = new_item;
58435844
for (size_t i = 0; i < list->size(); i++) {
58445845
if ((*ref_item_array)[i] == select_expr)
@@ -6464,6 +6465,8 @@ bool Query_block::transform_grouped_to_derived(THD *thd, bool *break_off) {
64646465
// now that replaces_field has inherited the upper context
64656466
pair.second->context = &new_derived->context;
64666467

6468+
replaces_field->increment_ref_count();
6469+
64676470
for (const auto &[expr, was_hidden] : contrib_exprs) {
64686471
Item_field *replacement = replaces_field;
64696472
// If this expression was hidden, we need to make a copy of the derived
@@ -6830,6 +6833,7 @@ bool Query_block::decorrelate_derived_scalar_subquery_pre(
68306833
lifted_fields->m_field_positions.push_back(fields.size() -
68316834
hidden_fields);
68326835
fields.push_back(inner_field);
6836+
inner_field->increment_ref_count();
68336837
// We have added to fields; master_query_expression->types must
68346838
// always be equal to it;
68356839
master_query_expression()->types.push_back(inner_field);
@@ -6883,11 +6887,12 @@ bool Query_block::decorrelate_derived_scalar_subquery_pre(
68836887
// added to group by above.
68846888
if (!selected_field_in_group_by &&
68856889
!fields[first_non_hidden]->has_aggregation()) {
6886-
Item *func_any =
6887-
new (thd->mem_root) Item_func_any_value(fields[first_non_hidden]);
6890+
Item *const old_field = fields[first_non_hidden];
6891+
Item *func_any = new (thd->mem_root) Item_func_any_value(old_field);
68886892
if (func_any == nullptr) return true;
68896893
if (func_any->fix_fields(thd, &func_any)) return true;
68906894
fields[first_non_hidden] = func_any;
6895+
replace_referenced_item(old_field, func_any);
68916896
}
68926897

68936898
if (!m_agg_func_used) {
@@ -6921,6 +6926,7 @@ bool Query_block::decorrelate_derived_scalar_subquery_pre(
69216926
base_ref_items[fields.size()] = cnt;
69226927
lifted_fields->m_field_positions.push_back(fields.size() - hidden_fields);
69236928
fields.push_back(cnt);
6929+
cnt->increment_ref_count();
69246930
m_agg_func_used = true;
69256931
// Add a new column to the derived table's query expression
69266932
derived->derived_query_expression()->types.push_back(cnt);
@@ -7002,6 +7008,31 @@ bool Query_block::decorrelate_derived_scalar_subquery_post(
70027008
return false;
70037009
}
70047010

7011+
/**
7012+
Replace item in select list and preserve its reference count.
7013+
7014+
@param old_item Item to be replaced.
7015+
@param new_item Item to replace the old item.
7016+
7017+
If old item is present in base_ref_items, make sure it is replaced there.
7018+
7019+
Also make sure that reference count for old item is preserved in new item.
7020+
*/
7021+
void Query_block::replace_referenced_item(Item *const old_item,
7022+
Item *const new_item) {
7023+
for (size_t i = 0; i < fields.size(); i++) {
7024+
if (base_ref_items[i] == old_item) {
7025+
base_ref_items[i] = new_item;
7026+
break;
7027+
}
7028+
}
7029+
// Keep the same number of references as for the old expression:
7030+
new_item->increment_ref_count();
7031+
while (old_item->decrement_ref_count() > 0) {
7032+
new_item->increment_ref_count();
7033+
}
7034+
}
7035+
70057036
/**
70067037
Converts a subquery to a derived table and inserts it into the FROM
70077038
clause of the owning query block
@@ -7691,15 +7722,7 @@ bool Query_block::transform_scalar_subqueries_to_join_with_derived(THD *thd) {
76917722
return true;
76927723
Item *unwrapped_select_expr = unwrap_rollup_group(select_expr);
76937724
if (unwrapped_select_expr != prev_value) {
7694-
// If we replace a subquery in the select field list, possibly
7695-
// hidden inside a rollup wrapper, replace corresponding item
7696-
// in base_ref_items
7697-
for (size_t i = 0; i < fields.size(); i++) {
7698-
if (base_ref_items[i] == prev_value)
7699-
base_ref_items[i] = unwrapped_select_expr;
7700-
}
7701-
// All items in select list must have a positive ref count.
7702-
unwrapped_select_expr->increment_ref_count();
7725+
replace_referenced_item(prev_value, unwrapped_select_expr);
77037726
}
77047727
if (fields.size() != old_size) {
77057728
// The (implicit) iterator over fields has been invalidated,

0 commit comments

Comments
 (0)