summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorOleksandr Byelkin <sanja@mariadb.com>2014-10-22 19:47:05 +0200
committerOleksandr Byelkin <sanja@mariadb.com>2014-10-22 19:58:50 +0200
commitaf93dc48cffdc9a071e3ec6e1fe51f1d7d06686b (patch)
tree938d7fa8cbefa67161d50172ff962a842904d71c /sql
parentc3db4459561bc491582bef6bea7b83e9fe464a10 (diff)
downloadmariadb-git-bb-set-statement.tar.gz
MDEV-5231: Per query variables from Percona Server (rewritten)bb-set-statement
Diffstat (limited to 'sql')
-rw-r--r--sql/item.h11
-rw-r--r--sql/item_func.cc2
-rw-r--r--sql/lex.h1
-rw-r--r--sql/set_var.cc13
-rw-r--r--sql/set_var.h7
-rw-r--r--sql/share/errmsg-utf8.txt2
-rw-r--r--sql/sql_class.cc1
-rw-r--r--sql/sql_class.h4
-rw-r--r--sql/sql_lex.cc13
-rw-r--r--sql/sql_lex.h3
-rw-r--r--sql/sql_parse.cc103
-rw-r--r--sql/sql_plugin.h8
-rw-r--r--sql/sql_prepare.cc8
-rw-r--r--sql/sql_yacc.yy74
14 files changed, 218 insertions, 32 deletions
diff --git a/sql/item.h b/sql/item.h
index ff0c786ab94..e006622dca2 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -2819,6 +2819,16 @@ protected:
fixed= 1;
}
public:
+ Item_string(CHARSET_INFO *csi, const char *str_arg, uint length_arg)
+ : m_cs_specified(FALSE)
+ {
+ collation.set(csi, DERIVATION_COERCIBLE);
+ set_name(NULL, 0, system_charset_info);
+ decimals= NOT_FIXED_DEC;
+ fixed= 1;
+ str_value.copy(str_arg, length_arg, csi);
+ max_length= str_value.numchars() * csi->mbmaxlen;
+ }
// Constructors with the item name set from its value
Item_string(const char *str, uint length, CHARSET_INFO *cs,
Derivation dv, uint repertoire)
@@ -2954,6 +2964,7 @@ public:
}
return MYSQL_TYPE_STRING; // Not a temporal literal
}
+
};
diff --git a/sql/item_func.cc b/sql/item_func.cc
index 4ec0466bda8..25c0197cb9b 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -5547,7 +5547,7 @@ get_var_with_binlog(THD *thd, enum_sql_command sql_command,
tmp_var_list.push_back(new set_var_user(new Item_func_set_user_var(name,
new Item_null())));
/* Create the variable */
- if (sql_set_variables(thd, &tmp_var_list))
+ if (sql_set_variables(thd, &tmp_var_list, false))
{
thd->lex= sav_lex;
goto err;
diff --git a/sql/lex.h b/sql/lex.h
index 5ca188f99a0..51b69f00990 100644
--- a/sql/lex.h
+++ b/sql/lex.h
@@ -567,6 +567,7 @@ static SYMBOL symbols[] = {
{ "START", SYM(START_SYM)},
{ "STARTING", SYM(STARTING)},
{ "STARTS", SYM(STARTS_SYM)},
+ { "STATEMENT", SYM(STATEMENT_SYM)},
{ "STATS_AUTO_RECALC",SYM(STATS_AUTO_RECALC_SYM)},
{ "STATS_PERSISTENT", SYM(STATS_PERSISTENT_SYM)},
{ "STATS_SAMPLE_PAGES",SYM(STATS_SAMPLE_PAGES_SYM)},
diff --git a/sql/set_var.cc b/sql/set_var.cc
index bae65118112..1dad3603799 100644
--- a/sql/set_var.cc
+++ b/sql/set_var.cc
@@ -147,7 +147,7 @@ sys_var::sys_var(sys_var_chain *chain, const char *name_arg,
flags(flags_arg), show_val_type(show_val_type_arg),
guard(lock), offset(off), on_check(on_check_func), on_update(on_update_func),
deprecation_substitute(substitute),
- is_os_charset(FALSE)
+ is_os_charset(FALSE), default_val(FALSE)
{
/*
There is a limitation in handle_options() related to short options:
@@ -675,9 +675,10 @@ sys_var *intern_find_sys_var(const char *str, uint length)
-1 ERROR, message not sent
*/
-int sql_set_variables(THD *thd, List<set_var_base> *var_list)
+int sql_set_variables(THD *thd, List<set_var_base> *var_list, bool free)
{
- int error;
+ int error= 0;
+ bool was_error= thd->is_error();
List_iterator_fast<set_var_base> it(*var_list);
DBUG_ENTER("sql_set_variables");
@@ -687,7 +688,7 @@ int sql_set_variables(THD *thd, List<set_var_base> *var_list)
if ((error= var->check(thd)))
goto err;
}
- if (!(error= MY_TEST(thd->is_error())))
+ if (was_error || !(error= MY_TEST(thd->is_error())))
{
it.rewind();
while ((var= it++))
@@ -695,7 +696,8 @@ int sql_set_variables(THD *thd, List<set_var_base> *var_list)
}
err:
- free_underlaid_joins(thd, &thd->lex->select_lex);
+ if (free)
+ free_underlaid_joins(thd, &thd->lex->select_lex);
DBUG_RETURN(error);
}
@@ -788,6 +790,7 @@ int set_var::light_check(THD *thd)
*/
int set_var::update(THD *thd)
{
+ var->set_is_default(value == 0);
return value ? var->update(thd, this) : var->set_default(thd, this);
}
diff --git a/sql/set_var.h b/sql/set_var.h
index fe2a0d8e953..e4a4385f056 100644
--- a/sql/set_var.h
+++ b/sql/set_var.h
@@ -87,6 +87,7 @@ protected:
on_update_function on_update;
const char *const deprecation_substitute;
bool is_os_charset; ///< true if the value is in character_set_filesystem
+ bool default_val;
public:
sys_var(sys_var_chain *chain, const char *name_arg, const char *comment,
@@ -191,6 +192,8 @@ public:
return insert_dynamic(array, (uchar*)&option);
}
void do_deprecated_warning(THD *thd);
+ bool is_default() { return default_val; }
+ void set_is_default(bool def) { default_val= def; }
virtual uchar *default_value_ptr(THD *thd)
{ return (uchar*)&option.def_value; }
@@ -249,6 +252,7 @@ public:
virtual int check(THD *thd)=0; /* To check privileges etc. */
virtual int update(THD *thd)=0; /* To set the value */
virtual int light_check(THD *thd) { return check(thd); } /* for PS */
+ virtual bool is_system() { return FALSE; }
};
@@ -290,6 +294,7 @@ public:
else
value=value_arg;
}
+ virtual bool is_system() { return 1; }
int check(THD *thd);
int update(THD *thd);
int light_check(THD *thd);
@@ -388,7 +393,7 @@ SHOW_VAR* enumerate_sys_vars(THD *thd, bool sorted, enum enum_var_type type);
int fill_sysvars(THD *thd, TABLE_LIST *tables, COND *cond);
sys_var *find_sys_var(THD *thd, const char *str, uint length=0);
-int sql_set_variables(THD *thd, List<set_var_base> *var_list);
+int sql_set_variables(THD *thd, List<set_var_base> *var_list, bool free);
#define SYSVAR_AUTOSIZE(VAR,VAL) \
do { \
diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt
index 199a822d022..bb428079433 100644
--- a/sql/share/errmsg-utf8.txt
+++ b/sql/share/errmsg-utf8.txt
@@ -7111,3 +7111,5 @@ ER_TABLE_DEFINITION_TOO_BIG
eng "The definition for table %`s is too big"
ER_STATEMENT_TIMEOUT 70100
eng "Query execution was interrupted (max_statement_time exceeded)"
+ER_SUBQUERIES_NOT_SUPPORTED 42000
+ eng "%s does not support subqueries or stored functions."
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 34fa0308f7d..90c550d6a30 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -3441,6 +3441,7 @@ void Query_arena::free_items()
{
next= free_list->next;
DBUG_ASSERT(free_list != next);
+ DBUG_PRINT("info", ("free item: 0x%lx", (ulong) free_list));
free_list->delete_self();
}
/* Postcondition: free_list is 0 */
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 4198402ae9b..7a34960e390 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -3826,6 +3826,10 @@ public:
thr_timer_end(&query_timer);
#endif
}
+ void restore_set_statement_var()
+ {
+ main_lex.restore_set_statement_var();
+ }
};
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 7eb38b1dd7a..6432e1937be 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -542,6 +542,8 @@ void lex_start(THD *thd)
lex->reset_slave_info.all= false;
lex->limit_rows_examined= 0;
lex->limit_rows_examined_cnt= ULONGLONG_MAX;
+ lex->var_list.empty();
+ lex->stmt_var_list.empty();
DBUG_VOID_RETURN;
}
@@ -4233,6 +4235,17 @@ int LEX::print_explain(select_result_sink *output, uint8 explain_flags,
return res;
}
+void LEX::restore_set_statement_var()
+{
+ DBUG_ENTER("LEX::restore_set_statement_var");
+ if (!old_var_list.is_empty())
+ {
+ DBUG_PRINT("info", ("vars: %d", old_var_list.elements));
+ sql_set_variables(thd, &old_var_list, false);
+ old_var_list.empty();
+ }
+ DBUG_VOID_RETURN;
+}
/*
Save explain structures of a UNION. The only variable member is whether the
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index b18cff131ca..6c631e20c87 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -2401,6 +2401,8 @@ struct LEX: public Query_tables_list
List<Item> *insert_list,field_list,value_list,update_list;
List<List_item> many_values;
List<set_var_base> var_list;
+ List<set_var_base> stmt_var_list; //SET_STATEMENT values
+ List<set_var_base> old_var_list; // SET STATEMENT old values
List<Item_func_set_user_var> set_var_list; // in-query assignment list
List<Item_param> param_list;
List<LEX_STRING> view_list; // view list (list of field names in view)
@@ -2775,6 +2777,7 @@ struct LEX: public Query_tables_list
int print_explain(select_result_sink *output, uint8 explain_flags,
bool is_analyze, bool *printed_anything);
+ void restore_set_statement_var();
};
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index b905d8b0bd6..20bc0431d4d 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -2635,6 +2635,98 @@ mysql_execute_command(THD *thd)
thd->get_binlog_format(&orig_binlog_format,
&orig_current_stmt_binlog_format);
+ if (!lex->stmt_var_list.is_empty())
+ {
+ DBUG_PRINT("info", ("SET STATEMENT %d vars", lex->stmt_var_list.elements));
+
+ lex->old_var_list.empty();
+ List_iterator_fast<set_var_base> it(lex->stmt_var_list);
+ set_var_base *var;
+ while ((var=it++))
+ {
+ DBUG_ASSERT(var->is_system());
+ set_var *o= NULL, *v= (set_var*)var;
+ if (v->var->is_default())
+ o= new set_var(v->type, v->var, &v->base, NULL);
+ else
+ {
+ switch (v->var->option.var_type)
+ {
+ case GET_BOOL:
+ case GET_INT:
+ case GET_LONG:
+ case GET_LL:
+ {
+ bool null_value;
+ longlong val= v->var->val_int(&null_value, thd, v->type, &v->base);
+ o= new set_var(v->type, v->var, &v->base,
+ (null_value ?
+ (Item *)new Item_null() :
+ (Item *)new Item_int(val)));
+ }
+ break;
+ case GET_UINT:
+ case GET_ULONG:
+ case GET_ULL:
+ {
+ bool null_value;
+ ulonglong val= v->var->val_int(&null_value, thd, v->type, &v->base);
+ o= new set_var(v->type, v->var, &v->base,
+ (null_value ?
+ (Item *)new Item_null() :
+ (Item *)new Item_uint(val)));
+ }
+ break;
+ case GET_DOUBLE:
+ {
+ bool null_value;
+ double val= v->var->val_real(&null_value, thd, v->type, &v->base);
+ o= new set_var(v->type, v->var, &v->base,
+ (null_value ?
+ (Item *)new Item_null() :
+ (Item *)new Item_float(val, 1)));
+ }
+ break;
+ default:
+ case GET_NO_ARG:
+ case GET_DISABLED:
+ DBUG_ASSERT(0);
+ case 0:
+ case GET_FLAGSET:
+ case GET_ASK_ADDR:
+ case GET_TYPE_MASK:
+ case GET_ENUM:
+ case GET_SET:
+ case GET_STR:
+ case GET_STR_ALLOC:
+ {
+ char buff[STRING_BUFFER_USUAL_SIZE];
+ String tmp(buff, sizeof(buff), v->var->charset(thd)),*val;
+ val= v->var->val_str(&tmp, thd, v->type, &v->base);
+ if (val)
+ {
+ Item_string *str= new Item_string(v->var->charset(thd),
+ val->ptr(), val->length());
+ o= new set_var(v->type, v->var, &v->base, str);
+ }
+ else
+ o= new set_var(v->type, v->var, &v->base, new Item_null());
+ }
+ break;
+ }
+ }
+ DBUG_ASSERT(o);
+ lex->old_var_list.push_back(o);
+ }
+ if (thd->is_error() ||
+ (res= sql_set_variables(thd, &lex->stmt_var_list, false)))
+ {
+ if (!thd->is_error())
+ my_error(ER_WRONG_ARGUMENTS, MYF(0), "SET");
+ goto error;
+ }
+ }
+
/*
Force statement logging for DDL commands to allow us to update
privilege, system or statistic tables directly without the updates
@@ -4100,7 +4192,7 @@ end_with_restore_list:
if ((check_table_access(thd, SELECT_ACL, all_tables, FALSE, UINT_MAX, FALSE)
|| open_and_lock_tables(thd, all_tables, TRUE, 0)))
goto error;
- if (!(res= sql_set_variables(thd, lex_var_list)))
+ if (!(res= sql_set_variables(thd, lex_var_list, true)))
{
my_ok(thd);
}
@@ -4344,8 +4436,7 @@ end_with_restore_list:
DBUG_ASSERT(lex->event_parse_data);
if (lex->table_or_sp_used())
{
- my_error(ER_NOT_SUPPORTED_YET, MYF(0), "Usage of subqueries or stored "
- "function calls as part of this statement");
+ my_error(ER_SUBQUERIES_NOT_SUPPORTED, MYF(0), "CREATE/ALTER EVENT");
break;
}
@@ -4665,8 +4756,7 @@ end_with_restore_list:
{
if (lex->table_or_sp_used())
{
- my_error(ER_NOT_SUPPORTED_YET, MYF(0), "Usage of subqueries or stored "
- "function calls as part of this statement");
+ my_error(ER_SUBQUERIES_NOT_SUPPORTED, MYF(0), "KILL");
break;
}
@@ -5059,6 +5149,7 @@ create_sp_error:
case SQLCOM_COMPOUND:
DBUG_ASSERT(all_tables == 0);
DBUG_ASSERT(thd->in_sub_stmt == 0);
+ lex->sphead->m_sql_mode= thd->variables.sql_mode;
if (do_execute_sp(thd, lex->sphead))
goto error;
break;
@@ -5482,6 +5573,8 @@ finish:
DBUG_ASSERT(!thd->in_active_multi_stmt_transaction() ||
thd->in_multi_stmt_transaction_mode());
+
+ lex->restore_set_statement_var();
lex->unit.cleanup();
if (! thd->in_sub_stmt)
diff --git a/sql/sql_plugin.h b/sql/sql_plugin.h
index 6b310865bba..59970b61574 100644
--- a/sql/sql_plugin.h
+++ b/sql/sql_plugin.h
@@ -189,4 +189,12 @@ extern bool plugin_foreach_with_mask(THD *thd, plugin_foreach_func *func,
int type, uint state_mask, void *arg);
extern bool plugin_dl_foreach(THD *thd, const LEX_STRING *dl,
plugin_foreach_func *func, void *arg);
+
+/**
+ Create deep copy of system_variables instance.
+*/
+extern
+struct system_variables *
+copy_system_variables(const struct system_variables *src);
+extern void free_system_variables(struct system_variables *v);
#endif
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index b1765cdda04..5c67a4c45a7 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -3299,7 +3299,7 @@ void Prepared_statement::cleanup_stmt()
{
DBUG_ENTER("Prepared_statement::cleanup_stmt");
DBUG_PRINT("enter",("stmt: 0x%lx", (long) this));
-
+ thd->restore_set_statement_var();
cleanup_items(free_list);
thd->cleanup_after_query();
thd->rollback_item_tree_changes();
@@ -3619,7 +3619,9 @@ Prepared_statement::execute_loop(String *expanded_query,
Reprepare_observer reprepare_observer;
bool error;
int reprepare_attempt= 0;
-
+#ifndef DBUG_OFF
+ Item *free_list_state= thd->free_list;
+#endif
thd->select_number= select_number_after_prepare;
/* Check if we got an error when sending long data */
if (state == Query_arena::STMT_ERROR)
@@ -3646,7 +3648,7 @@ reexecute:
allocated items when cleaning up after validation of the prepared
statement.
*/
- DBUG_ASSERT(thd->free_list == NULL);
+ DBUG_ASSERT(thd->free_list == free_list_state);
/*
Install the metadata observer. If some metadata version is
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 09f7678fd83..5ebf4d29a80 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -256,7 +256,6 @@ static bool maybe_start_compound_statement(THD *thd)
Lex->sp_chistics.suid= SP_IS_NOT_SUID;
Lex->sphead->set_body_start(thd, YYLIP->get_cpp_ptr());
- Lex->sphead->m_sql_mode= thd->variables.sql_mode;
}
return 0;
}
@@ -788,8 +787,10 @@ static void sp_create_assignment_lex(THD *thd, bool no_lookahead)
lex->var_list.empty();
lex->autocommit= 0;
/* get_ptr() is only correct with no lookahead. */
- DBUG_ASSERT(no_lookahead);
- lex->sphead->m_tmp_query= lip->get_ptr();
+ if (no_lookahead)
+ lex->sphead->m_tmp_query= lip->get_ptr();
+ else
+ lex->sphead->m_tmp_query= lip->get_tok_end();
/* Inherit from outer lex. */
lex->option_type= old_lex->option_type;
}
@@ -932,10 +933,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%parse-param { THD *thd }
%lex-param { THD *thd }
/*
- Currently there are 163 shift/reduce conflicts.
+ Currently there are 164 shift/reduce conflicts.
We should not introduce new conflicts any more.
*/
-%expect 163
+%expect 164
/*
Comments for TOKENS.
@@ -1481,6 +1482,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token STARTING
%token STARTS_SYM
%token START_SYM /* SQL-2003-R */
+%token STATEMENT_SYM
%token STATS_AUTO_RECALC_SYM
%token STATS_PERSISTENT_SYM
%token STATS_SAMPLE_PAGES_SYM
@@ -14317,6 +14319,7 @@ keyword_sp:
| SQL_NO_CACHE_SYM {}
| SQL_THREAD {}
| STARTS_SYM {}
+ | STATEMENT_SYM {}
| STATUS_SYM {}
| STORAGE_SYM {}
| STRING_SYM {}
@@ -14391,8 +14394,36 @@ set:
}
start_option_value_list
{}
+ | SET STATEMENT_SYM
+ {
+ LEX *lex= Lex;
+ mysql_init_select(lex);
+ lex->sql_command= SQLCOM_SET_OPTION;
+ lex->autocommit= 0;
+ }
+ set_stmt_option_value_following_option_type_list
+ {
+ LEX *lex= Lex;
+ if (lex->table_or_sp_used())
+ {
+ my_error(ER_SUBQUERIES_NOT_SUPPORTED, MYF(0), "SET STATEMENT");
+ MYSQL_YYABORT;
+ }
+ lex->stmt_var_list= lex->var_list;
+ lex->var_list.empty();
+ }
+ FOR_SYM verb_clause
+ {}
;
+set_stmt_option_value_following_option_type_list:
+ /*
+ Only system variables can be used here. If this condition is changed
+ please check careful code under lex->option_type == OPT_STATEMENT
+ condition on wrong type casts.
+ */
+ option_value_following_option_type
+ | set_stmt_option_value_following_option_type_list ',' option_value_following_option_type
// Start of option value list
start_option_value_list:
@@ -14497,20 +14528,29 @@ option_value_following_option_type:
{
LEX *lex= Lex;
- if ($1.var && $1.var != trg_new_row_fake_var)
+ /*
+ Ignore SET STATEMENT variables list on slaves because system
+ variables are not replicated except certain variables set the
+ values of whose are written to binlog event header and nothing
+ additional is required to set them.
+ */
+ if (!thd->slave_thread)
{
- /* It is a system variable. */
- if (set_system_variable(thd, &$1, lex->option_type, $3))
+ if ($1.var && $1.var != trg_new_row_fake_var)
+ {
+ /* It is a system variable. */
+ if (set_system_variable(thd, &$1, lex->option_type, $3))
+ MYSQL_YYABORT;
+ }
+ else
+ {
+ /*
+ Not in trigger assigning value to new row,
+ and option_type preceding local variable is illegal.
+ */
+ my_parse_error(ER(ER_SYNTAX_ERROR));
MYSQL_YYABORT;
- }
- else
- {
- /*
- Not in trigger assigning value to new row,
- and option_type preceding local variable is illegal.
- */
- my_parse_error(ER(ER_SYNTAX_ERROR));
- MYSQL_YYABORT;
+ }
}
}
;