summaryrefslogtreecommitdiff
path: root/sql/sql_yacc.yy
diff options
context:
space:
mode:
Diffstat (limited to 'sql/sql_yacc.yy')
-rw-r--r--sql/sql_yacc.yy472
1 files changed, 326 insertions, 146 deletions
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index e2263e21e8f..7b7f766bbd1 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -730,6 +730,116 @@ static bool add_create_index (LEX *lex, Key::Keytype type,
return FALSE;
}
+
+/**
+ Create a separate LEX for each assignment if in SP.
+
+ If we are in SP we want have own LEX for each assignment.
+ This is mostly because it is hard for several sp_instr_set
+ and sp_instr_set_trigger instructions share one LEX.
+ (Well, it is theoretically possible but adds some extra
+ overhead on preparation for execution stage and IMO less
+ robust).
+
+ QQ: May be we should simply prohibit group assignments in SP?
+
+ @see sp_create_assignment_instr
+
+ @param thd Thread context
+ @param no_lookahead True if the parser has no lookahead
+*/
+
+static void sp_create_assignment_lex(THD *thd, bool no_lookahead)
+{
+ LEX *lex= thd->lex;
+
+ if (lex->sphead)
+ {
+ Lex_input_stream *lip= &thd->m_parser_state->m_lip;
+ LEX *old_lex= lex;
+ lex->sphead->reset_lex(thd);
+ lex= thd->lex;
+
+ /* Set new LEX as if we at start of set rule. */
+ lex->sql_command= SQLCOM_SET_OPTION;
+ mysql_init_select(lex);
+ lex->var_list.empty();
+ lex->one_shot_set= 0;
+ lex->autocommit= 0;
+ /* get_ptr() is only correct with no lookahead. */
+ DBUG_ASSERT(no_lookahead);
+ lex->sphead->m_tmp_query= lip->get_ptr();
+ /* Inherit from outer lex. */
+ lex->option_type= old_lex->option_type;
+ }
+}
+
+
+/**
+ Create a SP instruction for a SET assignment.
+
+ @see sp_create_assignment_lex
+
+ @param thd Thread context
+ @param no_lookahead True if the parser has no lookahead
+
+ @return false if success, true otherwise.
+*/
+
+static bool sp_create_assignment_instr(THD *thd, bool no_lookahead)
+{
+ LEX *lex= thd->lex;
+
+ if (lex->sphead)
+ {
+ sp_head *sp= lex->sphead;
+
+ if (!lex->var_list.is_empty())
+ {
+ /*
+ We have assignment to user or system variable or
+ option setting, so we should construct sp_instr_stmt
+ for it.
+ */
+ LEX_STRING qbuff;
+ sp_instr_stmt *i;
+ Lex_input_stream *lip= &thd->m_parser_state->m_lip;
+
+ if (!(i= new sp_instr_stmt(sp->instructions(), lex->spcont,
+ lex)))
+ return true;
+
+ /*
+ Extract the query statement from the tokenizer. The
+ end is either lip->ptr, if there was no lookahead,
+ lip->tok_end otherwise.
+ */
+ if (no_lookahead)
+ qbuff.length= lip->get_ptr() - sp->m_tmp_query;
+ else
+ qbuff.length= lip->get_tok_end() - sp->m_tmp_query;
+
+ if (!(qbuff.str= (char*) alloc_root(thd->mem_root,
+ qbuff.length + 5)))
+ return true;
+
+ strmake(strmake(qbuff.str, "SET ", 4), sp->m_tmp_query,
+ qbuff.length);
+ qbuff.length+= 4;
+ i->m_query= qbuff;
+ if (sp->add_instr(i))
+ return true;
+ }
+ enum_var_type inner_option_type= lex->option_type;
+ if (lex->sphead->restore_lex(thd))
+ return true;
+ /* Copy option_type to outer lex in case it has changed. */
+ thd->lex->option_type= inner_option_type;
+ }
+ return false;
+}
+
+
%}
%union {
int num;
@@ -788,10 +898,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%pure_parser /* We have threads */
/*
- Currently there are 174 shift/reduce conflicts.
+ Currently there are 171 shift/reduce conflicts.
We should not introduce new conflicts any more.
*/
-%expect 174
+%expect 171
/*
Comments for TOKENS.
@@ -1172,6 +1282,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token ON /* SQL-2003-R */
%token ONE_SHOT_SYM
%token ONE_SYM
+%token ONLY_SYM /* SQL-2003-R */
%token ONLINE_SYM
%token OPEN_SYM /* SQL-2003-R */
%token OPTIMIZE
@@ -1475,14 +1586,21 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
table_option opt_if_not_exists opt_no_write_to_binlog
opt_temporary all_or_any opt_distinct
opt_ignore_leaves fulltext_options spatial_type union_option
- start_transaction_opts field_def
- union_opt select_derived_init option_type2
+ field_def
+ union_opt select_derived_init transaction_access_mode_types
opt_natural_language_mode opt_query_expansion
opt_ev_status opt_ev_on_completion ev_on_completion opt_ev_comment
ev_alter_on_schedule_completion opt_ev_rename_to opt_ev_sql_stmt
optional_flush_tables_arguments opt_dyncol_type dyncol_type
opt_time_precision kill_type kill_option int_num
+/*
+ Bit field of MYSQL_START_TRANS_OPT_* flags.
+*/
+%type <num> opt_start_transaction_option_list
+%type <num> start_transaction_option_list
+%type <num> start_transaction_option
+
%type <m_yes_no_unk>
opt_chain opt_release
@@ -1610,7 +1728,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
ref_list opt_match_clause opt_on_update_delete use
opt_delete_options opt_delete_option varchar nchar nvarchar
opt_outer table_list table_name table_alias_ref_list table_alias_ref
- opt_option opt_place
+ opt_place
opt_attribute opt_attribute_list attribute column_list column_list_id
opt_column_list grant_privileges grant_ident grant_list grant_option
object_privilege object_privilege_list user_list rename_list
@@ -7100,20 +7218,56 @@ slave:
;
start:
- START_SYM TRANSACTION_SYM start_transaction_opts
+ START_SYM TRANSACTION_SYM opt_start_transaction_option_list
{
LEX *lex= Lex;
lex->sql_command= SQLCOM_BEGIN;
+ /* READ ONLY and READ WRITE are mutually exclusive. */
+ if (($3 & MYSQL_START_TRANS_OPT_READ_WRITE) &&
+ ($3 & MYSQL_START_TRANS_OPT_READ_ONLY))
+ {
+ my_parse_error(ER(ER_SYNTAX_ERROR));
+ MYSQL_YYABORT;
+ }
lex->start_transaction_opt= $3;
}
;
-start_transaction_opts:
- /*empty*/ { $$ = 0; }
- | WITH CONSISTENT_SYM SNAPSHOT_SYM
+opt_start_transaction_option_list:
+ /* empty */
+ {
+ $$= 0;
+ }
+ | start_transaction_option_list
+ {
+ $$= $1;
+ }
+ ;
+
+start_transaction_option_list:
+ start_transaction_option
+ {
+ $$= $1;
+ }
+ | start_transaction_option_list ',' start_transaction_option
+ {
+ $$= $1 | $3;
+ }
+ ;
+
+start_transaction_option:
+ WITH CONSISTENT_SYM SNAPSHOT_SYM
{
$$= MYSQL_START_TRANS_OPT_WITH_CONS_SNAPSHOT;
}
+ | READ_SYM ONLY_SYM
+ {
+ $$= MYSQL_START_TRANS_OPT_READ_ONLY;
+ }
+ | READ_SYM WRITE_SYM
+ {
+ $$= MYSQL_START_TRANS_OPT_READ_WRITE;
+ }
;
slave_thread_opts:
@@ -13148,6 +13302,7 @@ keyword_sp:
| ONE_SHOT_SYM {}
| ONE_SYM {}
| ONLINE_SYM {}
+ | ONLY_SYM {}
| PACK_KEYS_SYM {}
| PAGE_SYM {}
| PARTIAL {}
@@ -13271,8 +13426,15 @@ keyword_sp:
/* Option functions */
+/*
+ SQLCOM_SET_OPTION statement.
+
+ Note that to avoid shift/reduce conflicts, we have separate rules for the
+ first option listed in the statement.
+*/
+
set:
- SET opt_option
+ SET
{
LEX *lex=Lex;
lex->sql_command= SQLCOM_SET_OPTION;
@@ -13281,115 +13443,96 @@ set:
lex->var_list.empty();
lex->one_shot_set= 0;
lex->autocommit= 0;
+ sp_create_assignment_lex(YYTHD, yychar == YYEMPTY);
}
- option_value_list
+ start_option_value_list
{}
;
-opt_option:
- /* empty */ {}
- | OPTION {}
- ;
-option_value_list:
- option_type_value
- | option_value_list ',' option_type_value
- ;
-
-option_type_value:
+// Start of option value list
+start_option_value_list:
+ option_value_no_option_type
{
- THD *thd= YYTHD;
- LEX *lex= thd->lex;
- Lex_input_stream *lip= YYLIP;
-
- if (lex->sphead)
- {
- /*
- If we are in SP we want have own LEX for each assignment.
- This is mostly because it is hard for several sp_instr_set
- and sp_instr_set_trigger instructions share one LEX.
- (Well, it is theoretically possible but adds some extra
- overhead on preparation for execution stage and IMO less
- robust).
-
- QQ: May be we should simply prohibit group assignments in SP?
- */
- lex->sphead->reset_lex(thd);
- lex= thd->lex;
-
- /* Set new LEX as if we at start of set rule. */
- lex->sql_command= SQLCOM_SET_OPTION;
- mysql_init_select(lex);
- lex->option_type=OPT_SESSION;
- lex->var_list.empty();
- lex->one_shot_set= 0;
- lex->autocommit= 0;
- lex->sphead->m_tmp_query= lip->get_tok_start();
- }
+ if (sp_create_assignment_instr(YYTHD, yychar == YYEMPTY))
+ MYSQL_YYABORT;
}
- ext_option_value
+ option_value_list_continued
+ | TRANSACTION_SYM
{
- THD *thd= YYTHD;
- LEX *lex= thd->lex;
- Lex_input_stream *lip= YYLIP;
-
- if (lex->sphead)
- {
- sp_head *sp= lex->sphead;
+ Lex->option_type= OPT_DEFAULT;
+ }
+ transaction_characteristics
+ {
+ if (sp_create_assignment_instr(YYTHD, yychar == YYEMPTY))
+ MYSQL_YYABORT;
+ }
+ | option_type
+ {
+ Lex->option_type= $1;
+ }
+ start_option_value_list_following_option_type
+ ;
- if (!lex->var_list.is_empty())
- {
- /*
- We have assignment to user or system variable or
- option setting, so we should construct sp_instr_stmt
- for it.
- */
- LEX_STRING qbuff;
- sp_instr_stmt *i;
- if (!(i= new sp_instr_stmt(sp->instructions(), lex->spcont,
- lex)))
- MYSQL_YYABORT;
+// Start of option value list, option_type was given
+start_option_value_list_following_option_type:
+ option_value_following_option_type
+ {
+ if (sp_create_assignment_instr(YYTHD, yychar == YYEMPTY))
+ MYSQL_YYABORT;
+ }
+ option_value_list_continued
+ | TRANSACTION_SYM transaction_characteristics
+ {
+ if (sp_create_assignment_instr(YYTHD, yychar == YYEMPTY))
+ MYSQL_YYABORT;
+ }
+ ;
- /*
- Extract the query statement from the tokenizer. The
- end is either lip->ptr, if there was no lookahead,
- lip->tok_end otherwise.
- */
- if (yychar == YYEMPTY)
- qbuff.length= lip->get_ptr() - sp->m_tmp_query;
- else
- qbuff.length= lip->get_tok_end() - sp->m_tmp_query;
+// Remainder of the option value list after first option value.
+option_value_list_continued:
+ /* empty */
+ | ',' option_value_list
+ ;
- if (!(qbuff.str= (char*) alloc_root(thd->mem_root,
- qbuff.length + 5)))
- MYSQL_YYABORT;
+// Repeating list of option values after first option value.
+option_value_list:
+ {
+ sp_create_assignment_lex(YYTHD, yychar == YYEMPTY);
+ }
+ option_value
+ {
+ if (sp_create_assignment_instr(YYTHD, yychar == YYEMPTY))
+ MYSQL_YYABORT;
+ }
+ | option_value_list ','
+ {
+ sp_create_assignment_lex(YYTHD, yychar == YYEMPTY);
+ }
+ option_value
+ {
+ if (sp_create_assignment_instr(YYTHD, yychar == YYEMPTY))
+ MYSQL_YYABORT;
+ }
+ ;
- strmake(strmake(qbuff.str, "SET ", 4), sp->m_tmp_query,
- qbuff.length);
- qbuff.length+= 4;
- i->m_query= qbuff;
- if (sp->add_instr(i))
- MYSQL_YYABORT;
- }
- if (lex->sphead->restore_lex(thd))
- MYSQL_YYABORT;
- }
+// Wrapper around option values following the first option value in the stmt.
+option_value:
+ option_type
+ {
+ Lex->option_type= $1;
}
+ option_value_following_option_type
+ | option_value_no_option_type
;
option_type:
- option_type2 {}
- | GLOBAL_SYM { $$=OPT_GLOBAL; }
+ GLOBAL_SYM { $$=OPT_GLOBAL; }
| LOCAL_SYM { $$=OPT_SESSION; }
| SESSION_SYM { $$=OPT_SESSION; }
;
-option_type2:
- /* empty */ { $$= OPT_DEFAULT; }
- | ONE_SHOT_SYM { Lex->one_shot_set= 1; $$= OPT_SESSION; }
- ;
-
opt_var_type:
/* empty */ { $$=OPT_SESSION; }
| GLOBAL_SYM { $$=OPT_GLOBAL; }
@@ -13404,74 +13547,62 @@ opt_var_ident_type:
| SESSION_SYM '.' { $$=OPT_SESSION; }
;
-ext_option_value:
- sys_option_value
- | option_type2 option_value
+// Option values with preceeding option_type.
+option_value_following_option_type:
+ internal_variable_name equal set_expr_or_default
+ {
+ THD *thd= YYTHD;
+ LEX *lex= Lex;
+
+ 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 preceeding local variable is illegal.
+ */
+ my_parse_error(ER(ER_SYNTAX_ERROR));
+ MYSQL_YYABORT;
+ }
+ }
;
-sys_option_value:
- option_type internal_variable_name equal set_expr_or_default
+// Option values without preceeding option_type.
+option_value_no_option_type:
+ internal_variable_name equal set_expr_or_default
{
THD *thd= YYTHD;
LEX *lex= Lex;
- LEX_STRING *name= &$2.base_name;
+ LEX_STRING *name= &$1.base_name;
- if ($2.var == trg_new_row_fake_var)
+ if ($1.var == trg_new_row_fake_var)
{
/* We are in trigger and assigning value to field of new row */
- if ($1)
- {
- my_parse_error(ER(ER_SYNTAX_ERROR));
- MYSQL_YYABORT;
- }
- if (set_trigger_new_row(YYTHD, name, $4))
+ if (set_trigger_new_row(YYTHD, name, $3))
MYSQL_YYABORT;
}
- else if ($2.var)
+ else if ($1.var)
{
- if ($1)
- lex->option_type= $1;
-
/* It is a system variable. */
- if (set_system_variable(thd, &$2, lex->option_type, $4))
+ if (set_system_variable(thd, &$1, lex->option_type, $3))
MYSQL_YYABORT;
}
else
{
sp_pcontext *spc= lex->spcont;
- sp_variable_t *spv= spc->find_variable(name);
-
- if ($1)
- {
- my_parse_error(ER(ER_SYNTAX_ERROR));
- MYSQL_YYABORT;
- }
+ sp_variable *spv= spc->find_variable(name, false);
/* It is a local variable. */
- if (set_local_variable(thd, spv, $4))
+ if (set_local_variable(thd, spv, $3))
MYSQL_YYABORT;
}
}
- | option_type TRANSACTION_SYM ISOLATION LEVEL_SYM isolation_types
- {
- THD *thd= YYTHD;
- LEX *lex=Lex;
- lex->option_type= $1;
- Item *item= new (thd->mem_root) Item_int((int32) $5);
- if (item == NULL)
- MYSQL_YYABORT;
- set_var *var= new set_var(lex->option_type,
- find_sys_var(thd, "tx_isolation"),
- &null_lex_str,
- item);
- if (var == NULL)
- MYSQL_YYABORT;
- lex->var_list.push_back(var);
- }
- ;
-
-option_value:
- '@' ident_or_text equal expr
+ | '@' ident_or_text equal expr
{
Item_func_set_user_var *item;
item= new (YYTHD->mem_root) Item_func_set_user_var($2, $4);
@@ -13517,7 +13648,7 @@ option_value:
names.str= (char *)"names";
names.length= 5;
- if (spc && spc->find_variable(&names))
+ if (spc && spc->find_variable(&names, false))
my_error(ER_SP_BAD_VAR_SHADOW, MYF(0), names.str);
else
my_parse_error(ER(ER_SYNTAX_ERROR));
@@ -13553,7 +13684,7 @@ option_value:
pw.str= (char *)"password";
pw.length= 8;
- if (spc && spc->find_variable(&pw))
+ if (spc && spc->find_variable(&pw, false))
{
my_error(ER_SP_BAD_VAR_SHADOW, MYF(0), pw.str);
MYSQL_YYABORT;
@@ -13562,6 +13693,7 @@ option_value:
MYSQL_YYABORT;
user->host=null_lex_str;
user->user.str=thd->security_ctx->user;
+ user->user.length= strlen(thd->security_ctx->user);
set_var_password *var= new set_var_password(user, $3);
if (var == NULL)
MYSQL_YYABORT;
@@ -13666,6 +13798,54 @@ internal_variable_name:
}
;
+transaction_characteristics:
+ transaction_access_mode
+ | isolation_level
+ | transaction_access_mode ',' isolation_level
+ | isolation_level ',' transaction_access_mode
+ ;
+
+transaction_access_mode:
+ transaction_access_mode_types
+ {
+ THD *thd= YYTHD;
+ LEX *lex=Lex;
+ Item *item= new (thd->mem_root) Item_int((int32) $1);
+ if (item == NULL)
+ MYSQL_YYABORT;
+ set_var *var= new set_var(lex->option_type,
+ find_sys_var(thd, "tx_read_only"),
+ &null_lex_str,
+ item);
+ if (var == NULL)
+ MYSQL_YYABORT;
+ lex->var_list.push_back(var);
+ }
+ ;
+
+isolation_level:
+ ISOLATION LEVEL_SYM isolation_types
+ {
+ THD *thd= YYTHD;
+ LEX *lex=Lex;
+ Item *item= new (thd->mem_root) Item_int((int32) $3);
+ if (item == NULL)
+ MYSQL_YYABORT;
+ set_var *var= new set_var(lex->option_type,
+ find_sys_var(thd, "tx_isolation"),
+ &null_lex_str,
+ item);
+ if (var == NULL)
+ MYSQL_YYABORT;
+ lex->var_list.push_back(var);
+ }
+ ;
+
+transaction_access_mode_types:
+ READ_SYM ONLY_SYM { $$= true; }
+ | READ_SYM WRITE_SYM { $$= false; }
+ ;
+
isolation_types:
READ_SYM UNCOMMITTED_SYM { $$= ISO_READ_UNCOMMITTED; }
| READ_SYM COMMITTED_SYM { $$= ISO_READ_COMMITTED; }