mirror of
https://github.com/MariaDB/server.git
synced 2025-07-04 01:23:45 +03:00
MDEV-16039 Crash when selecting virtual columns generated using functions with DAYNAME()
* Allocate items on thd->mem_root while refixing vcol exprs * Make vcol tree changes register and roll them back after the statement is executed. Explanation: Due to collation implementation specifics an Item tree could change while fixing. The tricky thing here is to make it on a proper arena. It's usually not a problem when a field is deterministic, however, makes a pain vice-versa, during allocation allocating. A non-deterministic field should be refixed on each statement, since it depends on the environment state. Changing the tree will be temporary and therefore it should be reverted after the statement execution.
This commit is contained in:
@ -603,3 +603,69 @@ test gcol_t1 sidea NEVER NULL
|
||||
test gcol_t1 sideb NEVER NULL
|
||||
test gcol_t1 sidec VIRTUAL GENERATED ALWAYS sqrt(`sidea` * `sidea` + `sideb` * `sideb`)
|
||||
DROP TABLE gcol_t1;
|
||||
#
|
||||
# MDEV-16039 Crash when selecting virtual columns
|
||||
# generated using functions with DAYNAME()
|
||||
#
|
||||
CREATE TABLE t1 (
|
||||
suppliersenttoday INT NOT NULL,
|
||||
suppliercaptoday CHAR(10) AS (CONCAT('',DAYNAME('2020-02-05')))
|
||||
) COLLATE utf8_bin;
|
||||
INSERT INTO t1 (suppliersenttoday) VALUES (0);
|
||||
INSERT INTO t1 (suppliersenttoday) VALUES (0);
|
||||
SELECT * FROM t1;
|
||||
suppliersenttoday suppliercaptoday
|
||||
0 Wednesday
|
||||
0 Wednesday
|
||||
PREPARE STMT FROM 'INSERT INTO t1 (suppliersenttoday) VALUES (1)';
|
||||
CREATE OR REPLACE TABLE t1 (
|
||||
suppliersenttoday INT NOT NULL,
|
||||
suppliercaptoday CHAR(10) AS (CONCAT('',DAYNAME('2020-02-05')))
|
||||
) COLLATE utf8_bin;
|
||||
EXECUTE STMT;
|
||||
EXECUTE STMT;
|
||||
SELECT * FROM t1;
|
||||
suppliersenttoday suppliercaptoday
|
||||
1 Wednesday
|
||||
1 Wednesday
|
||||
DROP TABLE t1;
|
||||
# (duplicate) MDEV-20380 Server crash during update
|
||||
CREATE TABLE gafld (
|
||||
nuigafld INTEGER NOT NULL,
|
||||
ucrgafld VARCHAR(30) COLLATE UTF8_BIN NOT NULL
|
||||
DEFAULT SUBSTRING_INDEX(USER(),'@',1)
|
||||
);
|
||||
EXPLAIN UPDATE gafld SET nuigafld = 0 WHERE nuigafld = 10;
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE gafld ALL NULL NULL NULL NULL 1 Using where
|
||||
EXPLAIN UPDATE gafld SET nuigafld = 0 WHERE nuigafld = 10;
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE gafld ALL NULL NULL NULL NULL 1 Using where
|
||||
DROP TABLE gafld;
|
||||
# (duplicate) MDEV-17653 replace into generated columns is unstable
|
||||
# Some columns are snipped from the MDEV test
|
||||
CREATE TABLE t (
|
||||
c0 TIMESTAMP NOT NULL DEFAULT current_timestamp()
|
||||
ON UPDATE current_timestamp(),
|
||||
c1 DECIMAL(27,25) GENERATED ALWAYS AS (DAYOFMONTH('2020-02-05')),
|
||||
c4 TIME NOT NULL,
|
||||
c8 SMALLINT(6) GENERATED ALWAYS AS
|
||||
(CONCAT_WS(CONVERT(C1 USING CP932),
|
||||
'900') <> (c4 = 1)),
|
||||
PRIMARY KEY (c4)
|
||||
) DEFAULT CHARSET=latin1;
|
||||
REPLACE INTO t SET c0 = '2018-06-03 10:31:43', c4 = '02:58:55';
|
||||
REPLACE INTO t SET c0 = '2018-06-03 10:31:44', c4 = '02:58:55';
|
||||
REPLACE INTO t SET c0 = '2018-06-03 10:31:45', c4 = '02:58:55';
|
||||
DROP TABLE t;
|
||||
# (duplicate) MDEV-17986 crash when I insert on a table
|
||||
CREATE OR REPLACE TABLE t2 (
|
||||
number BIGINT(20) NOT NULL,
|
||||
lrn BIGINT(20) NOT NULL DEFAULT 0,
|
||||
source VARCHAR(15) NOT NULL
|
||||
DEFAULT (REVERSE(SUBSTRING_INDEX(REVERSE(user()), '@', 1))),
|
||||
PRIMARY KEY (number)
|
||||
);
|
||||
REPLACE t2(number) VALUES('1');
|
||||
REPLACE t2(number) VALUES('1');
|
||||
DROP TABLE t2;
|
||||
|
@ -564,3 +564,73 @@ SELECT table_schema,table_name,column_name,extra,is_generated,generation_express
|
||||
FROM information_schema.columns WHERE table_name='gcol_t1';
|
||||
|
||||
DROP TABLE gcol_t1;
|
||||
|
||||
--echo #
|
||||
--echo # MDEV-16039 Crash when selecting virtual columns
|
||||
--echo # generated using functions with DAYNAME()
|
||||
--echo #
|
||||
|
||||
CREATE TABLE t1 (
|
||||
suppliersenttoday INT NOT NULL,
|
||||
suppliercaptoday CHAR(10) AS (CONCAT('',DAYNAME('2020-02-05')))
|
||||
) COLLATE utf8_bin;
|
||||
|
||||
INSERT INTO t1 (suppliersenttoday) VALUES (0);
|
||||
INSERT INTO t1 (suppliersenttoday) VALUES (0);
|
||||
SELECT * FROM t1;
|
||||
|
||||
PREPARE STMT FROM 'INSERT INTO t1 (suppliersenttoday) VALUES (1)';
|
||||
|
||||
CREATE OR REPLACE TABLE t1 (
|
||||
suppliersenttoday INT NOT NULL,
|
||||
suppliercaptoday CHAR(10) AS (CONCAT('',DAYNAME('2020-02-05')))
|
||||
) COLLATE utf8_bin;
|
||||
|
||||
EXECUTE STMT;
|
||||
EXECUTE STMT;
|
||||
SELECT * FROM t1;
|
||||
|
||||
DROP TABLE t1;
|
||||
|
||||
--echo # (duplicate) MDEV-20380 Server crash during update
|
||||
CREATE TABLE gafld (
|
||||
nuigafld INTEGER NOT NULL,
|
||||
ucrgafld VARCHAR(30) COLLATE UTF8_BIN NOT NULL
|
||||
DEFAULT SUBSTRING_INDEX(USER(),'@',1)
|
||||
);
|
||||
EXPLAIN UPDATE gafld SET nuigafld = 0 WHERE nuigafld = 10;
|
||||
EXPLAIN UPDATE gafld SET nuigafld = 0 WHERE nuigafld = 10;
|
||||
DROP TABLE gafld;
|
||||
|
||||
--echo # (duplicate) MDEV-17653 replace into generated columns is unstable
|
||||
--echo # Some columns are snipped from the MDEV test
|
||||
CREATE TABLE t (
|
||||
c0 TIMESTAMP NOT NULL DEFAULT current_timestamp()
|
||||
ON UPDATE current_timestamp(),
|
||||
c1 DECIMAL(27,25) GENERATED ALWAYS AS (DAYOFMONTH('2020-02-05')),
|
||||
c4 TIME NOT NULL,
|
||||
c8 SMALLINT(6) GENERATED ALWAYS AS
|
||||
(CONCAT_WS(CONVERT(C1 USING CP932),
|
||||
'900') <> (c4 = 1)),
|
||||
PRIMARY KEY (c4)
|
||||
) DEFAULT CHARSET=latin1;
|
||||
|
||||
REPLACE INTO t SET c0 = '2018-06-03 10:31:43', c4 = '02:58:55';
|
||||
REPLACE INTO t SET c0 = '2018-06-03 10:31:44', c4 = '02:58:55';
|
||||
REPLACE INTO t SET c0 = '2018-06-03 10:31:45', c4 = '02:58:55';
|
||||
|
||||
DROP TABLE t;
|
||||
|
||||
--echo # (duplicate) MDEV-17986 crash when I insert on a table
|
||||
CREATE OR REPLACE TABLE t2 (
|
||||
number BIGINT(20) NOT NULL,
|
||||
lrn BIGINT(20) NOT NULL DEFAULT 0,
|
||||
source VARCHAR(15) NOT NULL
|
||||
DEFAULT (REVERSE(SUBSTRING_INDEX(REVERSE(user()), '@', 1))),
|
||||
PRIMARY KEY (number)
|
||||
);
|
||||
|
||||
REPLACE t2(number) VALUES('1');
|
||||
REPLACE t2(number) VALUES('1');
|
||||
|
||||
DROP TABLE t2;
|
||||
|
27
sql/item.cc
27
sql/item.cc
@ -2325,14 +2325,7 @@ bool Item_func_or_sum::agg_item_set_converter(const DTCollation &coll,
|
||||
bool res= FALSE;
|
||||
uint i;
|
||||
|
||||
/*
|
||||
In case we're in statement prepare, create conversion item
|
||||
in its memory: it will be reused on each execute.
|
||||
*/
|
||||
Query_arena backup;
|
||||
Query_arena *arena= thd->stmt_arena->is_stmt_prepare() ?
|
||||
thd->activate_stmt_arena_if_needed(&backup) :
|
||||
NULL;
|
||||
DBUG_ASSERT(!thd->stmt_arena->is_stmt_prepare());
|
||||
|
||||
for (i= 0, arg= args; i < nargs; i++, arg+= item_sep)
|
||||
{
|
||||
@ -2354,20 +2347,8 @@ bool Item_func_or_sum::agg_item_set_converter(const DTCollation &coll,
|
||||
res= TRUE;
|
||||
break; // we cannot return here, we need to restore "arena".
|
||||
}
|
||||
/*
|
||||
If in statement prepare, then we create a converter for two
|
||||
constant items, do it once and then reuse it.
|
||||
If we're in execution of a prepared statement, arena is NULL,
|
||||
and the conv was created in runtime memory. This can be
|
||||
the case only if the argument is a parameter marker ('?'),
|
||||
because for all true constants the charset converter has already
|
||||
been created in prepare. In this case register the change for
|
||||
rollback.
|
||||
*/
|
||||
if (thd->stmt_arena->is_stmt_prepare())
|
||||
*arg= conv;
|
||||
else
|
||||
thd->change_item_tree(arg, conv);
|
||||
|
||||
thd->change_item_tree(arg, conv);
|
||||
|
||||
if (conv->fix_fields(thd, arg))
|
||||
{
|
||||
@ -2375,8 +2356,6 @@ bool Item_func_or_sum::agg_item_set_converter(const DTCollation &coll,
|
||||
break; // we cannot return here, we need to restore "arena".
|
||||
}
|
||||
}
|
||||
if (arena)
|
||||
thd->restore_active_arena(arena, &backup);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -4961,6 +4961,24 @@ static void mark_real_tables_as_free_for_reuse(TABLE_LIST *table_list)
|
||||
}
|
||||
}
|
||||
|
||||
int TABLE::fix_vcol_exprs(THD *thd)
|
||||
{
|
||||
for (Field **vf= vfield; vf && *vf; vf++)
|
||||
if (fix_session_vcol_expr(thd, (*vf)->vcol_info))
|
||||
return 1;
|
||||
|
||||
for (Field **df= default_field; df && *df; df++)
|
||||
if ((*df)->default_value &&
|
||||
fix_session_vcol_expr(thd, (*df)->default_value))
|
||||
return 1;
|
||||
|
||||
for (Virtual_column_info **cc= check_constraints; cc && *cc; cc++)
|
||||
if (fix_session_vcol_expr(thd, (*cc)))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static bool fix_all_session_vcol_exprs(THD *thd, TABLE_LIST *tables)
|
||||
{
|
||||
@ -4968,36 +4986,27 @@ static bool fix_all_session_vcol_exprs(THD *thd, TABLE_LIST *tables)
|
||||
TABLE_LIST *first_not_own= thd->lex->first_not_own_table();
|
||||
DBUG_ENTER("fix_session_vcol_expr");
|
||||
|
||||
for (TABLE_LIST *table= tables; table && table != first_not_own;
|
||||
int error= 0;
|
||||
for (TABLE_LIST *table= tables; table && table != first_not_own && !error;
|
||||
table= table->next_global)
|
||||
{
|
||||
TABLE *t= table->table;
|
||||
if (!table->placeholder() && t->s->vcols_need_refixing &&
|
||||
table->lock_type >= TL_WRITE_ALLOW_WRITE)
|
||||
{
|
||||
Query_arena *stmt_backup= thd->stmt_arena;
|
||||
if (thd->stmt_arena->is_conventional())
|
||||
thd->stmt_arena= t->expr_arena;
|
||||
if (table->security_ctx)
|
||||
thd->security_ctx= table->security_ctx;
|
||||
|
||||
for (Field **vf= t->vfield; vf && *vf; vf++)
|
||||
if (fix_session_vcol_expr(thd, (*vf)->vcol_info))
|
||||
goto err;
|
||||
|
||||
for (Field **df= t->default_field; df && *df; df++)
|
||||
if ((*df)->default_value &&
|
||||
fix_session_vcol_expr(thd, (*df)->default_value))
|
||||
goto err;
|
||||
|
||||
for (Virtual_column_info **cc= t->check_constraints; cc && *cc; cc++)
|
||||
if (fix_session_vcol_expr(thd, (*cc)))
|
||||
goto err;
|
||||
error= t->fix_vcol_exprs(thd);
|
||||
|
||||
thd->security_ctx= save_security_ctx;
|
||||
thd->stmt_arena= stmt_backup;
|
||||
}
|
||||
}
|
||||
DBUG_RETURN(0);
|
||||
err:
|
||||
thd->security_ctx= save_security_ctx;
|
||||
DBUG_RETURN(1);
|
||||
DBUG_RETURN(error);
|
||||
}
|
||||
|
||||
|
||||
|
@ -3482,7 +3482,7 @@ void select_dumpvar::cleanup()
|
||||
Query_arena::Type Query_arena::type() const
|
||||
{
|
||||
DBUG_ASSERT(0); /* Should never be called */
|
||||
return STATEMENT;
|
||||
return Type::STATEMENT;
|
||||
}
|
||||
|
||||
|
||||
@ -3535,7 +3535,7 @@ Statement::Statement(LEX *lex_arg, MEM_ROOT *mem_root_arg,
|
||||
|
||||
Query_arena::Type Statement::type() const
|
||||
{
|
||||
return STATEMENT;
|
||||
return Type::STATEMENT;
|
||||
}
|
||||
|
||||
|
||||
|
@ -951,9 +951,9 @@ public:
|
||||
enum_state state;
|
||||
|
||||
/* We build without RTTI, so dynamic_cast can't be used. */
|
||||
enum Type
|
||||
enum class Type
|
||||
{
|
||||
STATEMENT, PREPARED_STATEMENT, STORED_PROCEDURE
|
||||
STATEMENT, PREPARED_STATEMENT, STORED_PROCEDURE, TABLE
|
||||
};
|
||||
|
||||
Query_arena(MEM_ROOT *mem_root_arg, enum enum_state state_arg) :
|
||||
@ -3654,13 +3654,20 @@ public:
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
bool is_item_tree_change_register_required()
|
||||
{
|
||||
return !stmt_arena->is_conventional()
|
||||
|| stmt_arena->type() == Query_arena::Type::TABLE;
|
||||
}
|
||||
|
||||
void change_item_tree(Item **place, Item *new_value)
|
||||
{
|
||||
DBUG_ENTER("THD::change_item_tree");
|
||||
DBUG_PRINT("enter", ("Register: %p (%p) <- %p",
|
||||
*place, place, new_value));
|
||||
/* TODO: check for OOM condition here */
|
||||
if (!stmt_arena->is_conventional())
|
||||
if (is_item_tree_change_register_required())
|
||||
nocheck_register_item_tree_change(place, *place, mem_root);
|
||||
*place= new_value;
|
||||
DBUG_VOID_RETURN;
|
||||
|
@ -7751,8 +7751,8 @@ void mysql_parse(THD *thd, char *rawbuf, uint length,
|
||||
sp_cache_enforce_limit(thd->sp_proc_cache, stored_program_cache_size);
|
||||
sp_cache_enforce_limit(thd->sp_func_cache, stored_program_cache_size);
|
||||
thd->end_statement();
|
||||
thd->Item_change_list::rollback_item_tree_changes();
|
||||
thd->cleanup_after_query();
|
||||
DBUG_ASSERT(thd->Item_change_list::is_empty());
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -338,7 +338,7 @@ find_prepared_statement(THD *thd, ulong id)
|
||||
thd->last_stmt :
|
||||
thd->stmt_map.find(id));
|
||||
|
||||
if (stmt == 0 || stmt->type() != Query_arena::PREPARED_STATEMENT)
|
||||
if (stmt == 0 || stmt->type() != Query_arena::Type::PREPARED_STATEMENT)
|
||||
return NULL;
|
||||
|
||||
return (Prepared_statement *) stmt;
|
||||
@ -3893,7 +3893,7 @@ Prepared_statement::~Prepared_statement()
|
||||
|
||||
Query_arena::Type Prepared_statement::type() const
|
||||
{
|
||||
return PREPARED_STATEMENT;
|
||||
return Type::PREPARED_STATEMENT;
|
||||
}
|
||||
|
||||
|
||||
|
16
sql/table.cc
16
sql/table.cc
@ -47,6 +47,17 @@
|
||||
#define MYSQL57_GENERATED_FIELD 128
|
||||
#define MYSQL57_GCOL_HEADER_SIZE 4
|
||||
|
||||
class Table_arena: public Query_arena
|
||||
{
|
||||
public:
|
||||
Table_arena(MEM_ROOT *mem_root, enum enum_state state_arg) :
|
||||
Query_arena(mem_root, state_arg){}
|
||||
virtual Type type() const
|
||||
{
|
||||
return Type::TABLE;
|
||||
}
|
||||
};
|
||||
|
||||
static Virtual_column_info * unpack_vcol_info_from_frm(THD *, MEM_ROOT *,
|
||||
TABLE *, String *, Virtual_column_info **, bool *);
|
||||
static bool check_vcol_forward_refs(Field *, Virtual_column_info *);
|
||||
@ -1020,8 +1031,9 @@ bool parse_vcol_defs(THD *thd, MEM_ROOT *mem_root, TABLE *table,
|
||||
We need to use CONVENTIONAL_EXECUTION here to ensure that
|
||||
any new items created by fix_fields() are not reverted.
|
||||
*/
|
||||
table->expr_arena= new (alloc_root(mem_root, sizeof(Query_arena)))
|
||||
Query_arena(mem_root, Query_arena::STMT_CONVENTIONAL_EXECUTION);
|
||||
table->expr_arena= new (alloc_root(mem_root, sizeof(Table_arena)))
|
||||
Table_arena(mem_root,
|
||||
Query_arena::STMT_CONVENTIONAL_EXECUTION);
|
||||
if (!table->expr_arena)
|
||||
DBUG_RETURN(1);
|
||||
|
||||
|
@ -1559,6 +1559,7 @@ public:
|
||||
TABLE *tmp_table,
|
||||
TMP_TABLE_PARAM *tmp_table_param,
|
||||
bool with_cleanup);
|
||||
int fix_vcol_exprs(THD *thd);
|
||||
};
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user