Commit 902ace09 authored by Alexander Barkov's avatar Alexander Barkov

MDEV-15620 Crash when using "SET @@NEW.a=expr" inside a trigger

The problem resided in this branch of the "option_value_no_option_type" rule:

| '@' '@' opt_var_ident_type internal_variable_name equal set_expr_or_default

Summary:

1. internal_variable_name initialized tmp.var to trg_new_row_fake_var (0x01).
2. The condition "if (tmp.var == NULL)" did not check
   the special case with trg_new_row_fake_var,
   so Lex->set_system_variable(&tmp, $3, $6) was
   called with tmp.var pointing to trg_new_row_fake_var,
   which created a sys_var instance pointing to 0x01 instead of
   a real system variable.
3. Later, at the trigger invocation time, this method was called:
   sys_var::do_deprecated_warning (this=0x1, thd=0x7ffe6c000a98)
   Notice, "this" is equal to trg_new_row_fake_var (0x01)

Solution:

The old implementation with separate rules
internal_variable_name (in sql_yacc.yy and sql_yacc_ora.yy) and
internal_variable_name_directly_assignable (in sql_yacc_ora.yy only)
was too complex and hard to follow.

Rewriting the code in a more straightforward way.

1. Changing LEX::set_system_variable()

from:

bool set_system_variable(struct sys_var_with_base *, enum_var_type, Item *);

to:

bool set_system_variable(enum_var_type, sys_var *, const LEX_CSTRING *, Item *);

2. Adding new methods in LEX, which operate with variable names:

bool set_trigger_field(const LEX_CSTRING *, const LEX_CSTRING *, Item *);
bool set_system_variable(enum_var_type var_type, const LEX_CSTRING *name,
                         Item *val);
bool set_system_variable(THD *thd, enum_var_type var_type,
                         const LEX_CSTRING *name1,
                         const LEX_CSTRING *name2,
                         Item *val);
bool set_default_system_variable(enum_var_type var_type,
                                 const LEX_CSTRING *name,
                                 Item *val);
bool set_variable(const LEX_CSTRING *name, Item *item);

3. Changing the grammar to call the new methods directly
   in option_value_no_option_type,
   Removing rules internal_variable_name and
   internal_variable_name_directly_assignable.

4. Removing "struct sys_var_with_base" and trg_new_row_fake_var.

Good side effect:

- The code in /sql reduced from 314 to 183 lines.
- MDEV-15615 Unexpected syntax error instead of "Unknown system variable" ...
  was also fixed automatically
parent ad647cc8
...@@ -1328,3 +1328,19 @@ CREATE TABLE raw (raw int); ...@@ -1328,3 +1328,19 @@ CREATE TABLE raw (raw int);
DROP TABLE raw; DROP TABLE raw;
CREATE TABLE varchar2 (varchar2 int); CREATE TABLE varchar2 (varchar2 int);
DROP TABLE varchar2; DROP TABLE varchar2;
#
# MDEV-15620 Crash when using "SET @@NEW.a=expr" inside a trigger
#
CREATE TABLE t1 (a INT);
CREATE TRIGGER tr1 BEFORE INSERT ON t1 FOR EACH ROW SET @@NEW.a=0;
ERROR HY000: Unknown structured system variable or ROW routine variable 'NEW'
DROP TABLE t1;
#
# MDEV-15615 Unexpected syntax error instead of "Unknown system variable" inside an SP
#
BEGIN NOT ATOMIC
DECLARE a INT;
SET GLOBAL a=10;
END;
$$
ERROR HY000: Unknown system variable 'a'
...@@ -881,7 +881,7 @@ ERROR 42000: You have an error in your SQL syntax; check the manual that corresp ...@@ -881,7 +881,7 @@ ERROR 42000: You have an error in your SQL syntax; check the manual that corresp
SET max_error_count=100 FOR INSERT INTO t1 VALUES (1,2); SET max_error_count=100 FOR INSERT INTO t1 VALUES (1,2);
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'FOR INSERT INTO t1 VALUES (1,2)' at line 1 ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'FOR INSERT INTO t1 VALUES (1,2)' at line 1
SET STATEMENT GLOBAL max_error_count=100 FOR INSERT INTO t1 VALUES (1,2); SET STATEMENT GLOBAL max_error_count=100 FOR INSERT INTO t1 VALUES (1,2);
ERROR HY000: Unknown system variable 'GLOBAL' ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'max_error_count=100 FOR INSERT INTO t1 VALUES (1,2)' at line 1
SET STATEMENT @@global.max_error_count=100 FOR INSERT INTO t1 VALUES (1,2); SET STATEMENT @@global.max_error_count=100 FOR INSERT INTO t1 VALUES (1,2);
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '@@global.max_error_count=100 FOR INSERT INTO t1 VALUES (1,2)' at line 1 ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '@@global.max_error_count=100 FOR INSERT INTO t1 VALUES (1,2)' at line 1
'' ''
......
...@@ -35,7 +35,7 @@ CALL p1('SELECT 1'); ...@@ -35,7 +35,7 @@ CALL p1('SELECT 1');
Error1: 0 normal, successful completition Error1: 0 normal, successful completition
CALL p1('xxx'); CALL p1('xxx');
'Error2: ' || SQLCODE || ' ' || SQLERRM 'Error2: ' || SQLCODE || ' ' || SQLERRM
Error2: 1193 Unknown system variable 'xxx' Error2: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '' at line 1
CALL p1('SELECT 1'); CALL p1('SELECT 1');
1 1
1 1
......
SET sql_mode=ORACLE;
#
# MDEV-15620 Crash when using "SET @@NEW.a=expr" inside a trigger
#
CREATE TABLE t1 (a INT);
CREATE TRIGGER tr1 BEFORE INSERT ON t1 FOR EACH ROW SET @@NEW.a=0;
ERROR HY000: Unknown structured system variable or ROW routine variable 'NEW'
DROP TABLE t1;
#
# MDEV-15615 Unexpected syntax error instead of "Unknown system variable" inside an SP
#
DECLARE
a INT;
BEGIN
SET GLOBAL a=10;
END;
$$
ERROR HY000: Unknown system variable 'a'
set sql_mode=ORACLE; set sql_mode=ORACLE;
:NEW.a := 1; :NEW.a := 1;
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'a := 1' at line 1 ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ':NEW.a := 1' at line 1
:OLD.a := 1; :OLD.a := 1;
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'a := 1' at line 1 ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ':OLD.a := 1' at line 1
:OLa.a := 1; :OLa.a := 1;
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'a := 1' at line 1 ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ':OLa.a := 1' at line 1
SELECT :NEW.a; SELECT :NEW.a;
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'a' at line 1 ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'a' at line 1
SELECT :OLD.a; SELECT :OLD.a;
......
SET sql_mode=ORACLE;
--echo #
--echo # MDEV-15620 Crash when using "SET @@NEW.a=expr" inside a trigger
--echo #
CREATE TABLE t1 (a INT);
--error ER_UNKNOWN_STRUCTURED_VARIABLE
CREATE TRIGGER tr1 BEFORE INSERT ON t1 FOR EACH ROW SET @@NEW.a=0;
DROP TABLE t1;
--echo #
--echo # MDEV-15615 Unexpected syntax error instead of "Unknown system variable" inside an SP
--echo #
DELIMITER $$;
--error ER_UNKNOWN_SYSTEM_VARIABLE
DECLARE
a INT;
BEGIN
SET GLOBAL a=10;
END;
$$
DELIMITER ;$$
...@@ -1349,3 +1349,26 @@ DROP TABLE raw; ...@@ -1349,3 +1349,26 @@ DROP TABLE raw;
CREATE TABLE varchar2 (varchar2 int); CREATE TABLE varchar2 (varchar2 int);
DROP TABLE varchar2; DROP TABLE varchar2;
--echo #
--echo # MDEV-15620 Crash when using "SET @@NEW.a=expr" inside a trigger
--echo #
CREATE TABLE t1 (a INT);
--error ER_UNKNOWN_STRUCTURED_VARIABLE
CREATE TRIGGER tr1 BEFORE INSERT ON t1 FOR EACH ROW SET @@NEW.a=0;
DROP TABLE t1;
--echo #
--echo # MDEV-15615 Unexpected syntax error instead of "Unknown system variable" inside an SP
--echo #
DELIMITER $$;
--error ER_UNKNOWN_SYSTEM_VARIABLE
BEGIN NOT ATOMIC
DECLARE a INT;
SET GLOBAL a=10;
END;
$$
DELIMITER ;$$
...@@ -828,7 +828,7 @@ SET STATEMENT max_error_count=100 INSERT t1 VALUES (1,2); ...@@ -828,7 +828,7 @@ SET STATEMENT max_error_count=100 INSERT t1 VALUES (1,2);
SET STATEMENT FOR INSERT INTO t1 VALUES (1,2); SET STATEMENT FOR INSERT INTO t1 VALUES (1,2);
--error ER_PARSE_ERROR --error ER_PARSE_ERROR
SET max_error_count=100 FOR INSERT INTO t1 VALUES (1,2); SET max_error_count=100 FOR INSERT INTO t1 VALUES (1,2);
--error ER_UNKNOWN_SYSTEM_VARIABLE --error ER_PARSE_ERROR
SET STATEMENT GLOBAL max_error_count=100 FOR INSERT INTO t1 VALUES (1,2); SET STATEMENT GLOBAL max_error_count=100 FOR INSERT INTO t1 VALUES (1,2);
--error ER_PARSE_ERROR --error ER_PARSE_ERROR
SET STATEMENT @@global.max_error_count=100 FOR INSERT INTO t1 VALUES (1,2); SET STATEMENT @@global.max_error_count=100 FOR INSERT INTO t1 VALUES (1,2);
......
...@@ -688,17 +688,6 @@ sys_var *intern_find_sys_var(const char *str, size_t length) ...@@ -688,17 +688,6 @@ sys_var *intern_find_sys_var(const char *str, size_t length)
} }
bool find_sys_var_null_base(THD *thd, struct sys_var_with_base *tmp)
{
tmp->var= find_sys_var(thd, tmp->base_name.str, tmp->base_name.length);
if (tmp->var != NULL)
tmp->base_name= null_clex_str;
return thd->is_error();
}
/** /**
Execute update of all variables. Execute update of all variables.
......
...@@ -393,7 +393,6 @@ SHOW_VAR* enumerate_sys_vars(THD *thd, bool sorted, enum enum_var_type type); ...@@ -393,7 +393,6 @@ SHOW_VAR* enumerate_sys_vars(THD *thd, bool sorted, enum enum_var_type type);
int fill_sysvars(THD *thd, TABLE_LIST *tables, COND *cond); int fill_sysvars(THD *thd, TABLE_LIST *tables, COND *cond);
sys_var *find_sys_var(THD *thd, const char *str, size_t length=0); sys_var *find_sys_var(THD *thd, const char *str, size_t length=0);
bool find_sys_var_null_base(THD *thd, struct sys_var_with_base *tmp);
int sql_set_variables(THD *thd, List<set_var_base> *var_list, bool free); int sql_set_variables(THD *thd, List<set_var_base> *var_list, bool free);
#define SYSVAR_AUTOSIZE(VAR,VAL) \ #define SYSVAR_AUTOSIZE(VAR,VAL) \
......
...@@ -41,12 +41,6 @@ void LEX::parse_error(uint err_number) ...@@ -41,12 +41,6 @@ void LEX::parse_error(uint err_number)
static int lex_one_token(YYSTYPE *yylval, THD *thd); static int lex_one_token(YYSTYPE *yylval, THD *thd);
/*
We are using pointer to this variable for distinguishing between assignment
to NEW row field (when parsing trigger definition) and structured variable.
*/
sys_var *trg_new_row_fake_var= (sys_var*) 0x01;
/** /**
LEX_STRING constant for null-string to be used in parser and other places. LEX_STRING constant for null-string to be used in parser and other places.
...@@ -5253,36 +5247,7 @@ LEX::find_variable(const LEX_CSTRING *name, ...@@ -5253,36 +5247,7 @@ LEX::find_variable(const LEX_CSTRING *name,
} }
bool LEX::init_internal_variable(struct sys_var_with_base *variable, bool LEX::is_trigger_new_or_old_reference(const LEX_CSTRING *name) const
const LEX_CSTRING *name)
{
sp_variable *spv;
const Sp_rcontext_handler *rh;
/* Best effort lookup for system variable. */
if (!(spv= find_variable(name, &rh)))
{
struct sys_var_with_base tmp= {NULL, *name};
/* Not an SP local variable */
if (find_sys_var_null_base(thd, &tmp))
return true;
*variable= tmp;
return false;
}
/*
Possibly an SP local variable (or a shadowed sysvar).
Will depend on the context of the SET statement.
*/
variable->var= NULL;
variable->base_name= *name;
return false;
}
bool LEX::is_trigger_new_or_old_reference(const LEX_CSTRING *name)
{ {
return sphead && sphead->m_handler->type() == TYPE_ENUM_TRIGGER && return sphead && sphead->m_handler->type() == TYPE_ENUM_TRIGGER &&
name->length == 3 && name->length == 3 &&
...@@ -5291,68 +5256,6 @@ bool LEX::is_trigger_new_or_old_reference(const LEX_CSTRING *name) ...@@ -5291,68 +5256,6 @@ bool LEX::is_trigger_new_or_old_reference(const LEX_CSTRING *name)
} }
bool LEX::init_internal_variable(struct sys_var_with_base *variable,
const LEX_CSTRING *dbname,
const LEX_CSTRING *name)
{
if (check_reserved_words(dbname))
{
my_error(ER_UNKNOWN_STRUCTURED_VARIABLE, MYF(0),
(int) dbname->length, dbname->str);
return true;
}
if (is_trigger_new_or_old_reference(dbname))
{
if (dbname->str[0]=='O' || dbname->str[0]=='o')
{
my_error(ER_TRG_CANT_CHANGE_ROW, MYF(0), "OLD", "");
return true;
}
if (trg_chistics.event == TRG_EVENT_DELETE)
{
my_error(ER_TRG_NO_SUCH_ROW_IN_TRG, MYF(0), "NEW", "on DELETE");
return true;
}
if (trg_chistics.action_time == TRG_ACTION_AFTER)
{
my_error(ER_TRG_CANT_CHANGE_ROW, MYF(0), "NEW", "after ");
return true;
}
/* This special combination will denote field of NEW row */
variable->var= trg_new_row_fake_var;
variable->base_name= *name;
return false;
}
sys_var *tmp= find_sys_var_ex(thd, name->str, name->length, true, false);
if (!tmp)
{
my_error(ER_UNKNOWN_STRUCTURED_VARIABLE, MYF(0),
(int) dbname->length, dbname->str);
return true;
}
if (!tmp->is_struct())
my_error(ER_VARIABLE_IS_NOT_STRUCT, MYF(0), name->str);
variable->var= tmp;
variable->base_name= *dbname;
return false;
}
bool LEX::init_default_internal_variable(struct sys_var_with_base *variable,
LEX_CSTRING name)
{
sys_var *tmp= find_sys_var(thd, name.str, name.length);
if (!tmp)
return true;
if (!tmp->is_struct())
my_error(ER_VARIABLE_IS_NOT_STRUCT, MYF(0), name.str);
variable->var= tmp;
variable->base_name.str= (char*) "default";
variable->base_name.length= 7;
return false;
}
void LEX::sp_variable_declarations_init(THD *thd, int nvars) void LEX::sp_variable_declarations_init(THD *thd, int nvars)
{ {
sp_variable *spvar= spcont->get_last_context_variable(); sp_variable *spvar= spcont->get_last_context_variable();
...@@ -6994,38 +6897,6 @@ bool LEX::set_user_variable(THD *thd, const LEX_CSTRING *name, Item *val) ...@@ -6994,38 +6897,6 @@ bool LEX::set_user_variable(THD *thd, const LEX_CSTRING *name, Item *val)
} }
/*
Perform assignment for a trigger, a system variable, or an SP variable.
"variable" be previously set by init_internal_variable(variable, name).
*/
bool LEX::set_variable(struct sys_var_with_base *variable, Item *item)
{
if (variable->var == trg_new_row_fake_var)
{
/* We are in trigger and assigning value to field of new row */
return set_trigger_new_row(&variable->base_name, item);
}
if (variable->var)
{
/* It is a system variable. */
return set_system_variable(variable, option_type, item);
}
/*
spcont and spv should not be NULL, as the variable
was previously checked by init_internal_variable().
*/
DBUG_ASSERT(spcont);
const Sp_rcontext_handler *rh;
sp_pcontext *ctx;
sp_variable *spv= find_variable(&variable->base_name, &ctx, &rh);
DBUG_ASSERT(spv);
DBUG_ASSERT(ctx);
DBUG_ASSERT(rh);
return sphead->set_local_variable(thd, ctx, rh, spv, item, this, true);
}
Item *LEX::create_item_ident_nosp(THD *thd, LEX_CSTRING *name) Item *LEX::create_item_ident_nosp(THD *thd, LEX_CSTRING *name)
{ {
if (current_select->parsing_place != IN_HAVING || if (current_select->parsing_place != IN_HAVING ||
...@@ -7084,6 +6955,17 @@ Item *LEX::create_item_ident_sp(THD *thd, LEX_CSTRING *name, ...@@ -7084,6 +6955,17 @@ Item *LEX::create_item_ident_sp(THD *thd, LEX_CSTRING *name,
} }
bool LEX::set_variable(const LEX_CSTRING *name, Item *item)
{
sp_pcontext *ctx;
const Sp_rcontext_handler *rh;
sp_variable *spv= find_variable(name, &ctx, &rh);
return spv ? sphead->set_local_variable(thd, ctx, rh, spv, item, this, true) :
set_system_variable(option_type, name, item);
}
/** /**
Generate instructions for: Generate instructions for:
SET x.y= expr; SET x.y= expr;
...@@ -7111,10 +6993,76 @@ bool LEX::set_variable(const LEX_CSTRING *name1, ...@@ -7111,10 +6993,76 @@ bool LEX::set_variable(const LEX_CSTRING *name1,
item, this); item, this);
} }
// A trigger field or a system variable if (is_trigger_new_or_old_reference(name1))
sys_var_with_base sysvar; return set_trigger_field(name1, name2, item);
return init_internal_variable(&sysvar, name1, name2) ||
set_variable(&sysvar, item); return set_system_variable(thd, option_type, name1, name2, item);
}
bool LEX::set_default_system_variable(enum_var_type var_type,
const LEX_CSTRING *name,
Item *val)
{
static LEX_CSTRING default_base_name= {STRING_WITH_LEN("default")};
sys_var *var= find_sys_var(thd, name->str, name->length);
if (!var)
return true;
if (!var->is_struct())
my_error(ER_VARIABLE_IS_NOT_STRUCT, MYF(0), name->str);
return set_system_variable(var_type, var, &default_base_name, val);
}
bool LEX::set_system_variable(enum_var_type var_type,
const LEX_CSTRING *name,
Item *val)
{
sys_var *var= find_sys_var(thd, name->str, name->length);
DBUG_ASSERT(thd->is_error() || var != NULL);
return var ? set_system_variable(var_type, var, &null_clex_str, val) : true;
}
bool LEX::set_system_variable(THD *thd, enum_var_type var_type,
const LEX_CSTRING *name1,
const LEX_CSTRING *name2,
Item *val)
{
sys_var *tmp;
if (check_reserved_words(name1) ||
!(tmp= find_sys_var_ex(thd, name2->str, name2->length, true, false)))
{
my_error(ER_UNKNOWN_STRUCTURED_VARIABLE, MYF(0),
(int) name1->length, name1->str);
return true;
}
if (!tmp->is_struct())
my_error(ER_VARIABLE_IS_NOT_STRUCT, MYF(0), name2->str);
return set_system_variable(var_type, tmp, name1, val);
}
bool LEX::set_trigger_field(const LEX_CSTRING *name1, const LEX_CSTRING *name2,
Item *val)
{
DBUG_ASSERT(is_trigger_new_or_old_reference(name1));
if (name1->str[0]=='O' || name1->str[0]=='o')
{
my_error(ER_TRG_CANT_CHANGE_ROW, MYF(0), "OLD", "");
return true;
}
if (trg_chistics.event == TRG_EVENT_DELETE)
{
my_error(ER_TRG_NO_SUCH_ROW_IN_TRG, MYF(0), "NEW", "on DELETE");
return true;
}
if (trg_chistics.action_time == TRG_ACTION_AFTER)
{
my_error(ER_TRG_CANT_CHANGE_ROW, MYF(0), "NEW", "after ");
return true;
}
return set_trigger_new_row(name2, val);
} }
......
...@@ -155,15 +155,6 @@ extern uint binlog_unsafe_map[256]; ...@@ -155,15 +155,6 @@ extern uint binlog_unsafe_map[256];
void binlog_unsafe_map_init(); void binlog_unsafe_map_init();
#endif #endif
/**
used by the parser to store internal variable name
*/
struct sys_var_with_base
{
sys_var *var;
LEX_CSTRING base_name;
};
struct LEX_TYPE struct LEX_TYPE
{ {
enum enum_field_types type; enum enum_field_types type;
...@@ -1338,8 +1329,6 @@ struct st_trg_chistics: public st_trg_execution_order ...@@ -1338,8 +1329,6 @@ struct st_trg_chistics: public st_trg_execution_order
}; };
extern sys_var *trg_new_row_fake_var;
enum xa_option_words {XA_NONE, XA_JOIN, XA_RESUME, XA_ONE_PHASE, enum xa_option_words {XA_NONE, XA_JOIN, XA_RESUME, XA_ONE_PHASE,
XA_SUSPEND, XA_FOR_MIGRATE}; XA_SUSPEND, XA_FOR_MIGRATE};
...@@ -3220,9 +3209,20 @@ struct LEX: public Query_tables_list ...@@ -3220,9 +3209,20 @@ struct LEX: public Query_tables_list
enum sub_select_type type, enum sub_select_type type,
bool is_top_level); bool is_top_level);
bool setup_select_in_parentheses(); bool setup_select_in_parentheses();
bool set_trigger_new_row(LEX_CSTRING *name, Item *val); bool set_trigger_new_row(const LEX_CSTRING *name, Item *val);
bool set_system_variable(struct sys_var_with_base *tmp, bool set_trigger_field(const LEX_CSTRING *name1, const LEX_CSTRING *name2,
enum enum_var_type var_type, Item *val); Item *val);
bool set_system_variable(enum_var_type var_type, sys_var *var,
const LEX_CSTRING *base_name, Item *val);
bool set_system_variable(enum_var_type var_type, const LEX_CSTRING *name,
Item *val);
bool set_system_variable(THD *thd, enum_var_type var_type,
const LEX_CSTRING *name1,
const LEX_CSTRING *name2,
Item *val);
bool set_default_system_variable(enum_var_type var_type,
const LEX_CSTRING *name,
Item *val);
bool set_user_variable(THD *thd, const LEX_CSTRING *name, Item *val); bool set_user_variable(THD *thd, const LEX_CSTRING *name, Item *val);
void set_stmt_init(); void set_stmt_init();
sp_name *make_sp_name(THD *thd, const LEX_CSTRING *name); sp_name *make_sp_name(THD *thd, const LEX_CSTRING *name);
...@@ -3265,14 +3265,7 @@ struct LEX: public Query_tables_list ...@@ -3265,14 +3265,7 @@ struct LEX: public Query_tables_list
sp_pcontext *not_used_ctx; sp_pcontext *not_used_ctx;
return find_variable(name, &not_used_ctx, rh); return find_variable(name, &not_used_ctx, rh);
} }
bool init_internal_variable(struct sys_var_with_base *variable, bool set_variable(const LEX_CSTRING *name, Item *item);
const LEX_CSTRING *name);
bool init_internal_variable(struct sys_var_with_base *variable,
const LEX_CSTRING *dbname,
const LEX_CSTRING *name);
bool init_default_internal_variable(struct sys_var_with_base *variable,
LEX_CSTRING name);
bool set_variable(struct sys_var_with_base *variable, Item *item);
bool set_variable(const LEX_CSTRING *name1, const LEX_CSTRING *name2, bool set_variable(const LEX_CSTRING *name1, const LEX_CSTRING *name2,
Item *item); Item *item);
void sp_variable_declarations_init(THD *thd, int nvars); void sp_variable_declarations_init(THD *thd, int nvars);
...@@ -3464,7 +3457,7 @@ struct LEX: public Query_tables_list ...@@ -3464,7 +3457,7 @@ struct LEX: public Query_tables_list
const LEX_CSTRING *var_name, const LEX_CSTRING *var_name,
const LEX_CSTRING *field_name); const LEX_CSTRING *field_name);
bool is_trigger_new_or_old_reference(const LEX_CSTRING *name); bool is_trigger_new_or_old_reference(const LEX_CSTRING *name) const;
Item *create_and_link_Item_trigger_field(THD *thd, const LEX_CSTRING *name, Item *create_and_link_Item_trigger_field(THD *thd, const LEX_CSTRING *name,
bool new_row); bool new_row);
......
...@@ -288,27 +288,28 @@ int LEX::case_stmt_action_then() ...@@ -288,27 +288,28 @@ int LEX::case_stmt_action_then()
*/ */
bool bool
LEX::set_system_variable(struct sys_var_with_base *tmp, LEX::set_system_variable(enum enum_var_type var_type,
enum enum_var_type var_type, Item *val) sys_var *sysvar, const LEX_CSTRING *base_name,
Item *val)
{ {
set_var *var; set_var *setvar;
/* No AUTOCOMMIT from a stored function or trigger. */ /* No AUTOCOMMIT from a stored function or trigger. */
if (spcont && tmp->var == Sys_autocommit_ptr) if (spcont && sysvar == Sys_autocommit_ptr)
sphead->m_flags|= sp_head::HAS_SET_AUTOCOMMIT_STMT; sphead->m_flags|= sp_head::HAS_SET_AUTOCOMMIT_STMT;
if (val && val->type() == Item::FIELD_ITEM && if (val && val->type() == Item::FIELD_ITEM &&
((Item_field*)val)->table_name) ((Item_field*)val)->table_name)
{ {
my_error(ER_WRONG_TYPE_FOR_VAR, MYF(0), tmp->var->name.str); my_error(ER_WRONG_TYPE_FOR_VAR, MYF(0), sysvar->name.str);
return TRUE; return TRUE;
} }
if (! (var= new (thd->mem_root) if (!(setvar= new (thd->mem_root) set_var(thd, var_type, sysvar,
set_var(thd, var_type, tmp->var, &tmp->base_name, val))) base_name, val)))
return TRUE; return TRUE;
return var_list.push_back(var, thd->mem_root); return var_list.push_back(setvar, thd->mem_root);
} }
...@@ -322,7 +323,7 @@ LEX::set_system_variable(struct sys_var_with_base *tmp, ...@@ -322,7 +323,7 @@ LEX::set_system_variable(struct sys_var_with_base *tmp,
@return TRUE if error, FALSE otherwise. @return TRUE if error, FALSE otherwise.
*/ */
bool LEX::set_trigger_new_row(LEX_CSTRING *name, Item *val) bool LEX::set_trigger_new_row(const LEX_CSTRING *name, Item *val)
{ {
Item_trigger_field *trg_fld; Item_trigger_field *trg_fld;
sp_instr_set_trigger_field *sp_fld; sp_instr_set_trigger_field *sp_fld;
...@@ -778,7 +779,6 @@ Virtual_column_info *add_virtual_expression(THD *thd, Item *expr) ...@@ -778,7 +779,6 @@ Virtual_column_info *add_virtual_expression(THD *thd, Item *expr)
LEX_CSTRING lex_str; LEX_CSTRING lex_str;
LEX_SYMBOL symbol; LEX_SYMBOL symbol;
Lex_string_with_metadata_st lex_string_with_metadata; Lex_string_with_metadata_st lex_string_with_metadata;
struct sys_var_with_base variable;
Lex_string_with_pos_st lex_string_with_pos; Lex_string_with_pos_st lex_string_with_pos;
Lex_spblock_st spblock; Lex_spblock_st spblock;
Lex_spblock_handlers_st spblock_handlers; Lex_spblock_handlers_st spblock_handlers;
...@@ -1869,8 +1869,6 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); ...@@ -1869,8 +1869,6 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
opt_load_data_charset opt_load_data_charset
UNDERSCORE_CHARSET UNDERSCORE_CHARSET
%type <variable> internal_variable_name
%type <select_lex> subselect %type <select_lex> subselect
get_select_lex get_select_lex_derived get_select_lex get_select_lex_derived
simple_table simple_table
...@@ -15960,25 +15958,20 @@ opt_var_ident_type: ...@@ -15960,25 +15958,20 @@ opt_var_ident_type:
/* Option values with preceding option_type. */ /* Option values with preceding option_type. */
option_value_following_option_type: option_value_following_option_type:
internal_variable_name equal set_expr_or_default ident equal set_expr_or_default
{ {
LEX *lex= Lex; if (Lex->set_system_variable(Lex->option_type, &$1, $3))
MYSQL_YYABORT;
if ($1.var && $1.var != trg_new_row_fake_var) }
{ | ident '.' ident equal set_expr_or_default
/* It is a system variable. */ {
if (lex->set_system_variable(&$1, lex->option_type, $3)) if (Lex->set_system_variable(thd, Lex->option_type, &$1, &$3, $5))
MYSQL_YYABORT; MYSQL_YYABORT;
} }
else | DEFAULT '.' ident equal set_expr_or_default
{ {
/* if (Lex->set_default_system_variable(Lex->option_type, &$3, $5))
Not in trigger assigning value to new row,
and option_type preceding local variable is illegal.
*/
thd->parse_error();
MYSQL_YYABORT; MYSQL_YYABORT;
}
} }
; ;
...@@ -15986,9 +15979,7 @@ option_value_following_option_type: ...@@ -15986,9 +15979,7 @@ option_value_following_option_type:
option_value_no_option_type: option_value_no_option_type:
ident equal set_expr_or_default ident equal set_expr_or_default
{ {
struct sys_var_with_base var; if (Lex->set_variable(&$1, $3))
if (Lex->init_internal_variable(&var, &$1) ||
Lex->set_variable(&var, $3))
MYSQL_YYABORT; MYSQL_YYABORT;
} }
| ident '.' ident equal set_expr_or_default | ident '.' ident equal set_expr_or_default
...@@ -15999,9 +15990,7 @@ option_value_no_option_type: ...@@ -15999,9 +15990,7 @@ option_value_no_option_type:
} }
| DEFAULT '.' ident equal set_expr_or_default | DEFAULT '.' ident equal set_expr_or_default
{ {
struct sys_var_with_base var; if (Lex->set_default_system_variable(Lex->option_type, &$3, $5))
if (Lex->init_default_internal_variable(&var, $3) ||
Lex->set_variable(&var, $5))
MYSQL_YYABORT; MYSQL_YYABORT;
} }
| '@' ident_or_text equal expr | '@' ident_or_text equal expr
...@@ -16009,16 +15998,19 @@ option_value_no_option_type: ...@@ -16009,16 +15998,19 @@ option_value_no_option_type:
if (Lex->set_user_variable(thd, &$2, $4)) if (Lex->set_user_variable(thd, &$2, $4))
MYSQL_YYABORT; MYSQL_YYABORT;
} }
| '@' '@' opt_var_ident_type internal_variable_name equal set_expr_or_default | '@' '@' opt_var_ident_type ident equal set_expr_or_default
{ {
struct sys_var_with_base tmp= $4; if (Lex->set_system_variable($3, &$4, $6))
/* Lookup if necessary: must be a system variable. */ MYSQL_YYABORT;
if (tmp.var == NULL) }
{ | '@' '@' opt_var_ident_type ident '.' ident equal set_expr_or_default
if (find_sys_var_null_base(thd, &tmp)) {
MYSQL_YYABORT; if (Lex->set_system_variable(thd, $3, &$4, &$6, $8))
} MYSQL_YYABORT;
if (Lex->set_system_variable(&tmp, $3, $6)) }
| '@' '@' opt_var_ident_type DEFAULT '.' ident equal set_expr_or_default
{
if (Lex->set_default_system_variable($3, &$6, $8))
MYSQL_YYABORT; MYSQL_YYABORT;
} }
| charset old_or_new_charset_name_or_default | charset old_or_new_charset_name_or_default
...@@ -16116,25 +16108,6 @@ option_value_no_option_type: ...@@ -16116,25 +16108,6 @@ option_value_no_option_type:
} }
; ;
internal_variable_name:
ident
{
if (Lex->init_internal_variable(&$$, &$1))
MYSQL_YYABORT;
}
| ident '.' ident
{
if (Lex->init_internal_variable(&$$, &$1, &$3))
MYSQL_YYABORT;
}
| DEFAULT '.' ident
{
if (Lex->init_default_internal_variable(&$$, $3))
MYSQL_YYABORT;
}
;
transaction_characteristics: transaction_characteristics:
transaction_access_mode transaction_access_mode
| isolation_level | isolation_level
......
...@@ -171,7 +171,6 @@ void ORAerror(THD *thd, const char *s) ...@@ -171,7 +171,6 @@ void ORAerror(THD *thd, const char *s)
LEX_CSTRING lex_str; LEX_CSTRING lex_str;
LEX_SYMBOL symbol; LEX_SYMBOL symbol;
Lex_string_with_metadata_st lex_string_with_metadata; Lex_string_with_metadata_st lex_string_with_metadata;
struct sys_var_with_base variable;
Lex_string_with_pos_st lex_string_with_pos; Lex_string_with_pos_st lex_string_with_pos;
Lex_spblock_st spblock; Lex_spblock_st spblock;
Lex_spblock_handlers_st spblock_handlers; Lex_spblock_handlers_st spblock_handlers;
...@@ -1271,9 +1270,6 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); ...@@ -1271,9 +1270,6 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
opt_load_data_charset opt_load_data_charset
UNDERSCORE_CHARSET UNDERSCORE_CHARSET
%type <variable> internal_variable_name
internal_variable_name_directly_assignable
%type <select_lex> subselect %type <select_lex> subselect
get_select_lex get_select_lex_derived get_select_lex get_select_lex_derived
query_specification query_specification
...@@ -14809,9 +14805,9 @@ simple_ident_q2: ...@@ -14809,9 +14805,9 @@ simple_ident_q2:
if (lex->is_trigger_new_or_old_reference(&$2)) if (lex->is_trigger_new_or_old_reference(&$2))
{ {
bool new_row= ($2.str[0]=='N' || $2.str[0]=='n'); bool new_row= ($2.str[0]=='N' || $2.str[0]=='n');
if (!($$= Lex->create_and_link_Item_trigger_field(thd, if (!($$= lex->create_and_link_Item_trigger_field(thd,
&$4, &$4,
new_row))) new_row)))
MYSQL_YYABORT; MYSQL_YYABORT;
} }
else else
...@@ -15659,7 +15655,7 @@ set: ...@@ -15659,7 +15655,7 @@ set:
; ;
set_assign: set_assign:
internal_variable_name_directly_assignable SET_VAR ident_directly_assignable SET_VAR
{ {
LEX *lex=Lex; LEX *lex=Lex;
lex->set_stmt_init(); lex->set_stmt_init();
...@@ -15687,6 +15683,25 @@ set_assign: ...@@ -15687,6 +15683,25 @@ set_assign:
lex->sphead->restore_lex(thd)) lex->sphead->restore_lex(thd))
MYSQL_YYABORT; MYSQL_YYABORT;
} }
| colon_with_pos ident '.' ident SET_VAR
{
LEX *lex= Lex;
if (!lex->is_trigger_new_or_old_reference(&$2))
{
thd->parse_error(ER_SYNTAX_ERROR, $1);
MYSQL_YYABORT;
}
lex->set_stmt_init();
lex->var_list.empty();
sp_create_assignment_lex(thd, yychar == YYEMPTY);
}
set_expr_or_default
{
LEX_CSTRING tmp= { $2.str, $2.length };
if (Lex->set_trigger_field(&tmp, &$4, $7) ||
sp_create_assignment_instr(thd, yychar == YYEMPTY))
MYSQL_YYABORT;
}
; ;
set_stmt_option_value_following_option_type_list: set_stmt_option_value_following_option_type_list:
...@@ -15798,25 +15813,20 @@ opt_var_ident_type: ...@@ -15798,25 +15813,20 @@ opt_var_ident_type:
/* Option values with preceding option_type. */ /* Option values with preceding option_type. */
option_value_following_option_type: option_value_following_option_type:
internal_variable_name equal set_expr_or_default ident equal set_expr_or_default
{ {
LEX *lex= Lex; if (Lex->set_system_variable(Lex->option_type, &$1, $3))
MYSQL_YYABORT;
if ($1.var && $1.var != trg_new_row_fake_var) }
{ | ident '.' ident equal set_expr_or_default
/* It is a system variable. */ {
if (lex->set_system_variable(&$1, lex->option_type, $3)) if (Lex->set_system_variable(thd, Lex->option_type, &$1, &$3, $5))
MYSQL_YYABORT; MYSQL_YYABORT;
} }
else | DEFAULT '.' ident equal set_expr_or_default
{ {
/* if (Lex->set_default_system_variable(Lex->option_type, &$3, $5))
Not in trigger assigning value to new row,
and option_type preceding local variable is illegal.
*/
thd->parse_error();
MYSQL_YYABORT; MYSQL_YYABORT;
}
} }
; ;
...@@ -15824,9 +15834,7 @@ option_value_following_option_type: ...@@ -15824,9 +15834,7 @@ option_value_following_option_type:
option_value_no_option_type: option_value_no_option_type:
ident equal set_expr_or_default ident equal set_expr_or_default
{ {
struct sys_var_with_base var; if (Lex->set_variable(&$1, $3))
if (Lex->init_internal_variable(&var, &$1) ||
Lex->set_variable(&var, $3))
MYSQL_YYABORT; MYSQL_YYABORT;
} }
| ident '.' ident equal set_expr_or_default | ident '.' ident equal set_expr_or_default
...@@ -15837,9 +15845,7 @@ option_value_no_option_type: ...@@ -15837,9 +15845,7 @@ option_value_no_option_type:
} }
| DEFAULT '.' ident equal set_expr_or_default | DEFAULT '.' ident equal set_expr_or_default
{ {
struct sys_var_with_base var; if (Lex->set_default_system_variable(Lex->option_type, &$3, $5))
if (Lex->init_default_internal_variable(&var, $3) ||
Lex->set_variable(&var, $5))
MYSQL_YYABORT; MYSQL_YYABORT;
} }
| '@' ident_or_text equal expr | '@' ident_or_text equal expr
...@@ -15847,16 +15853,19 @@ option_value_no_option_type: ...@@ -15847,16 +15853,19 @@ option_value_no_option_type:
if (Lex->set_user_variable(thd, &$2, $4)) if (Lex->set_user_variable(thd, &$2, $4))
MYSQL_YYABORT; MYSQL_YYABORT;
} }
| '@' '@' opt_var_ident_type internal_variable_name equal set_expr_or_default | '@' '@' opt_var_ident_type ident equal set_expr_or_default
{ {
struct sys_var_with_base tmp= $4; if (Lex->set_system_variable($3, &$4, $6))
/* Lookup if necessary: must be a system variable. */ MYSQL_YYABORT;
if (tmp.var == NULL) }
{ | '@' '@' opt_var_ident_type ident '.' ident equal set_expr_or_default
if (find_sys_var_null_base(thd, &tmp)) {
MYSQL_YYABORT; if (Lex->set_system_variable(thd, $3, &$4, &$6, $8))
} MYSQL_YYABORT;
if (Lex->set_system_variable(&tmp, $3, $6)) }
| '@' '@' opt_var_ident_type DEFAULT '.' ident equal set_expr_or_default
{
if (Lex->set_default_system_variable($3, &$6, $8))
MYSQL_YYABORT; MYSQL_YYABORT;
} }
| charset old_or_new_charset_name_or_default | charset old_or_new_charset_name_or_default
...@@ -15955,48 +15964,6 @@ option_value_no_option_type: ...@@ -15955,48 +15964,6 @@ option_value_no_option_type:
; ;
internal_variable_name:
ident
{
if (Lex->init_internal_variable(&$$, &$1))
MYSQL_YYABORT;
}
| ident '.' ident
{
if (Lex->init_internal_variable(&$$, &$1, &$3))
MYSQL_YYABORT;
}
| DEFAULT '.' ident
{
if (Lex->init_default_internal_variable(&$$, $3))
MYSQL_YYABORT;
}
;
internal_variable_name_directly_assignable:
ident_directly_assignable
{
if (Lex->init_internal_variable(&$$, &$1))
MYSQL_YYABORT;
}
| DEFAULT '.' ident
{
if (Lex->init_default_internal_variable(&$$, $3))
MYSQL_YYABORT;
}
| colon_with_pos ident_directly_assignable '.' ident
{
if (!Lex->is_trigger_new_or_old_reference(&$2))
{
thd->parse_error();
MYSQL_YYABORT;
}
if (Lex->init_internal_variable(&$$, &$2, &$4))
MYSQL_YYABORT;
}
;
transaction_characteristics: transaction_characteristics:
transaction_access_mode transaction_access_mode
| isolation_level | isolation_level
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment