Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions mysql-test/main/subselect_cache.result
Original file line number Diff line number Diff line change
Expand Up @@ -3933,3 +3933,40 @@ drop table t1,t2,t3,t4;
SET optimizer_switch=@save_optimizer_switch;
# restore default
set @@optimizer_switch= default;
#
# MDEV-38801 Item_sum & Item_cache implement only shallow copy
#
CREATE TABLE t1 (c YEAR KEY);
INSERT INTO t1 VALUES (2000),(2001);
INSERT INTO t1 VALUES ((c IN (SELECT * FROM (SELECT * FROM t1 GROUP BY c) AS d
NATURAL JOIN (SELECT * FROM t1) AS e)));
DROP TABLE t1;
CREATE TABLE t1 (pk INT PRIMARY KEY, d DATE, q INT, c CHAR(8));
INSERT INTO t1 SELECT seq, DATE'1998-01-01' + INTERVAL (seq%700) DAY, seq%50,
CONCAT('n', seq%9) FROM seq_1_to_40000;
ANALYZE TABLE t1 PERSISTENT FOR ALL;
Table Op Msg_type Msg_text
test.t1 analyze status Engine-independent statistics collected
test.t1 analyze status OK
# Item_cache_date
EXPLAIN SELECT q FROM t1 WHERE d <= DATE'1998-12-01' - INTERVAL '63' DAY;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 40000 Using where
# Item_cache_int
EXPLAIN SELECT q FROM t1 WHERE q <= 3 + 4;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 40000 Using where
# Item_cache_decimal
EXPLAIN SELECT q FROM t1 WHERE q <= 7.5 * 2;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 40000 Using where
# Item_cache_double
EXPLAIN SELECT q FROM t1 WHERE q <= SQRT(2) * 10;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 40000 Using where
# Item_cache_str
EXPLAIN SELECT q FROM t1 WHERE c <= CONCAT('n', '4');
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 40000 Using where
DROP TABLE t1;
# end of 10.11 tests
33 changes: 33 additions & 0 deletions mysql-test/main/subselect_cache.test
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Tests will be skipped for the view protocol because the view protocol creates
# an additional util connection and other statistics data
-- source include/no_view_protocol.inc
-- source include/have_sequence.inc

--disable_warnings
drop table if exists t0,t1,t2,t3,t4,t5,t6,t7,t8,t9;
Expand Down Expand Up @@ -1743,3 +1744,35 @@ SET optimizer_switch=@save_optimizer_switch;

--echo # restore default
set @@optimizer_switch= default;


--echo #
--echo # MDEV-38801 Item_sum & Item_cache implement only shallow copy
--echo #


CREATE TABLE t1 (c YEAR KEY);
INSERT INTO t1 VALUES (2000),(2001);
INSERT INTO t1 VALUES ((c IN (SELECT * FROM (SELECT * FROM t1 GROUP BY c) AS d
NATURAL JOIN (SELECT * FROM t1) AS e)));
DROP TABLE t1;

CREATE TABLE t1 (pk INT PRIMARY KEY, d DATE, q INT, c CHAR(8));
INSERT INTO t1 SELECT seq, DATE'1998-01-01' + INTERVAL (seq%700) DAY, seq%50,
CONCAT('n', seq%9) FROM seq_1_to_40000;
ANALYZE TABLE t1 PERSISTENT FOR ALL;

--echo # Item_cache_date
EXPLAIN SELECT q FROM t1 WHERE d <= DATE'1998-12-01' - INTERVAL '63' DAY;
--echo # Item_cache_int
EXPLAIN SELECT q FROM t1 WHERE q <= 3 + 4;
--echo # Item_cache_decimal
EXPLAIN SELECT q FROM t1 WHERE q <= 7.5 * 2;
--echo # Item_cache_double
EXPLAIN SELECT q FROM t1 WHERE q <= SQRT(2) * 10;
--echo # Item_cache_str
EXPLAIN SELECT q FROM t1 WHERE c <= CONCAT('n', '4');

DROP TABLE t1;

--echo # end of 10.11 tests
113 changes: 113 additions & 0 deletions sql/item.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10739,6 +10739,72 @@ void Item_cache::store(Item *item)
value_cached= FALSE;
}


#ifndef DBUG_OFF
/*
@brief
Whether 'clone' reaches any item object that 'src' reaches too.

@description
A copy is only a deep copy iff it shares no nodes at all with the item
it came from. Some classes implement deep_copy() as a shallow copy while
still holding child items -- Item_outer_ref and Item_copy_string -- so their
copy keeps pointing at the original's children.

Walking with find_item_processor asks whether a tree reaches one given
object, so collecting the copy's nodes and asking that of the original
covers both, and covers the classes not yet met rather than the two above.
*/
bool item_clone_shares_nodes(Item *src, Item *clone)
{
List<Item> clone_nodes;
if (clone->walk(&Item::collect_all_items_processor, true, &clone_nodes))
return true; // could not collect them

List_iterator_fast<Item> it(clone_nodes);
Item *node;
while ((node= it++))
if (src->walk(&Item::find_item_processor, true, (void*) node))
return true;
return false;
}
#endif


/**
@brief
Build a clone of an Item_cache.

@details
'example' is an ordinary pointer, so the copy constructor alone would give a
new cache still reading the expression the original caches, and every node
below the cache would then belong to both trees. Clone the expression as
well, the way Item_func_or_sum::deep_copy() clones its arguments, and keep
the invariant setup() establishes between 'example' and 'cached_field'.

An expression that cannot be cloned -- a subquery, for one -- makes the
cache unclonable too, rather than half copied.

@return clone of the item
@retval 0 on a failure, or if 'example' cannot be cloned
*/

Item* Item_cache::deep_copy(THD *thd) const
{
Item *example_clone= NULL;
if (example && !(example_clone= example->deep_copy_with_checks(thd)))
return NULL;
Item_cache *copy= static_cast<Item_cache *>(shallow_copy_with_checks(thd));
if (unlikely(!copy))
return NULL;
copy->example= example_clone;
if (cached_field && example_clone &&
example_clone->type() == Item::FIELD_ITEM)
copy->cached_field= ((Item_field *) example_clone)->field;
DBUG_ASSERT(!item_clone_shares_nodes((Item*)this, copy));
return copy;
}

void Item_cache::print(String *str, enum_query_type query_type)
{
if (example && // There is a cached item
Expand Down Expand Up @@ -11391,6 +11457,46 @@ void Item_cache_row::set_null()
};


/**
@brief
Build a clone of an Item_cache_row.

@details
A row cache holds a cache per column in values[], and those are not reached
through 'example', so Item_cache::deep_copy() leaves them shared. Clone the
array too, on a fresh allocation: the copy must not write through the
original's.

@return clone of the item
@retval 0 on a failure, or if any element cannot be cloned
*/

Item* Item_cache_row::deep_copy(THD *thd) const
{
Item_cache_row *copy=
static_cast<Item_cache_row *>(Item_cache::deep_copy(thd));
if (unlikely(!copy) || !values)
return copy;

Item_cache **values_clone= (Item_cache**)thd->calloc(
item_count*sizeof(Item_cache*));
if (unlikely(!values_clone))
return NULL;
for (uint i= 0; i < item_count; i++)
{
if (!values[i])
continue;
Item *el_clone= values[i]->deep_copy_with_checks(thd);
if (unlikely(!el_clone))
return NULL;
values_clone[i]= static_cast<Item_cache *>(el_clone);
}
copy->values= values_clone;
DBUG_ASSERT(!item_clone_shares_nodes((Item*)this, copy));
return copy;
}


double Item_type_holder::val_real()
{
DBUG_ASSERT(0); // should never be called
Expand Down Expand Up @@ -11722,3 +11828,10 @@ bool ignored_list_includes_table(ignored_tables_list_t list, TABLE_LIST *tbl)
}
return false;
}


bool Item::collect_all_items_processor(void *arg)
{
List<Item> *items= (List<Item> *) arg;
return items->push_back(this); // stops the walk if it cannot record
}
36 changes: 14 additions & 22 deletions sql/item.h
Original file line number Diff line number Diff line change
Expand Up @@ -2292,6 +2292,7 @@ class Item :public Value_source,
virtual bool check_inner_refs_processor(void *arg) { return 0; }
virtual bool find_item_in_field_list_processor(void *arg) { return 0; }
virtual bool find_item_processor(void *arg);
bool collect_all_items_processor(void *arg);
virtual bool change_context_processor(void *arg) { return 0; }
virtual bool reset_query_id_processor(void *arg) { return 0; }
virtual bool is_expensive_processor(void *arg) { return 0; }
Expand Down Expand Up @@ -7827,6 +7828,14 @@ class Item_cache: public Item_fixed_hybrid,
{ return convert_to_basic_const_item(thd); }
Item *in_subq_field_transformer_for_having(THD *thd, uchar *) override
{ return convert_to_basic_const_item(thd); }

protected:
/*
A shallow copy would leave the copy's 'example' pointing at the original's
expression, so the two would share every node below the cache. Defined here
once for the whole family: it dispatches to each class's shallow_copy().
*/
Item *deep_copy(THD *thd) const override;
};


Expand All @@ -7850,8 +7859,6 @@ class Item_cache_int: public Item_cache
protected:
Item *shallow_copy(THD *thd) const override
{ return get_item_copy<Item_cache_int>(thd, this); }
Item *deep_copy(THD *thd) const override
{ return shallow_copy_with_checks(thd); }
};


Expand Down Expand Up @@ -7886,8 +7893,9 @@ class Item_cache_year: public Item_cache_int
{
return type_handler_year.Item_get_date_with_warn(thd, this, to, mode);
}
Item *deep_copy(THD *thd) const override
{ return shallow_copy_with_checks(thd); }
protected:
Item *shallow_copy(THD *thd) const override
{ return get_item_copy<Item_cache_year>(thd, this); }
};


Expand Down Expand Up @@ -8039,8 +8047,6 @@ class Item_cache_timestamp: public Item_cache
protected:
Item *shallow_copy(THD *thd) const override
{ return get_item_copy<Item_cache_timestamp>(thd, this); }
Item *deep_copy(THD *thd) const override
{ return shallow_copy_with_checks(thd); }
public:
bool cache_value() override;
String* val_str(String *to) override
Expand Down Expand Up @@ -8103,8 +8109,6 @@ class Item_cache_double: public Item_cache_real
protected:
Item *shallow_copy(THD *thd) const override
{ return get_item_copy<Item_cache_double>(thd, this); }
Item *deep_copy(THD *thd) const override
{ return shallow_copy_with_checks(thd); }
};


Expand All @@ -8118,8 +8122,6 @@ class Item_cache_float: public Item_cache_real
protected:
Item *shallow_copy(THD *thd) const override
{ return get_item_copy<Item_cache_float>(thd, this); }
Item *deep_copy(THD *thd) const override
{ return shallow_copy_with_checks(thd); }
};


Expand All @@ -8144,8 +8146,6 @@ class Item_cache_decimal: public Item_cache
protected:
Item *shallow_copy(THD *thd) const override
{ return get_item_copy<Item_cache_decimal>(thd, this); }
Item *deep_copy(THD *thd) const override
{ return shallow_copy_with_checks(thd); }
};


Expand Down Expand Up @@ -8177,8 +8177,6 @@ class Item_cache_str: public Item_cache
protected:
Item *shallow_copy(THD *thd) const override
{ return get_item_copy<Item_cache_str>(thd, this); }
Item *deep_copy(THD *thd) const override
{ return shallow_copy_with_checks(thd); }
};


Expand All @@ -8205,10 +8203,6 @@ class Item_cache_str_for_nullif: public Item_cache_str
protected:
Item *shallow_copy(THD *thd) const override
{ return get_item_copy<Item_cache_str_for_nullif>(thd, this); }
Item *deep_copy(THD *thd) const override
{
return shallow_copy_with_checks(thd);
}
};


Expand Down Expand Up @@ -8287,10 +8281,8 @@ class Item_cache_row: public Item_cache
protected:
Item *shallow_copy(THD *thd) const override
{ return get_item_copy<Item_cache_row>(thd, this); }
Item *deep_copy(THD *thd) const override
{
return shallow_copy_with_checks(thd);
}
/* The row's element caches are held in values[], not in 'example' alone. */
Item *deep_copy(THD *thd) const override;
};


Expand Down
Loading
Loading