summaryrefslogtreecommitdiff
path: root/sql/sql_yacc.yy
diff options
context:
space:
mode:
authorunknown <dlenev@mysql.com>2004-09-08 13:29:21 +0400
committerunknown <dlenev@mysql.com>2004-09-08 13:29:21 +0400
commitcab6f8df6ddb35fbdef771eb56217bb0920be25b (patch)
treea103ef36c1230a085165269c481e5e135b27550d /sql/sql_yacc.yy
parent9243aa9ebe800724497316a8eb1bbab1d1065db6 (diff)
parent52ac4935e7e00218bf7abde7b841a2e5111cc819 (diff)
downloadmariadb-git-cab6f8df6ddb35fbdef771eb56217bb0920be25b.tar.gz
Merge of changes from the main tree to tree for WL#1218 "Triggers".
sql/item.cc: Auto merged sql/item.h: Auto merged sql/item_func.cc: Auto merged sql/lex.h: Auto merged sql/parse_file.cc: Auto merged sql/sp_head.h: Auto merged sql/sql_base.cc: Auto merged sql/sql_delete.cc: Auto merged sql/sql_insert.cc: Auto merged sql/sql_lex.cc: Auto merged sql/sql_lex.h: Auto merged sql/sql_parse.cc: Auto merged sql/sql_table.cc: Auto merged sql/sql_update.cc: Auto merged sql/sql_yacc.yy: Auto merged sql/table.h: Auto merged include/mysqld_error.h: Manual merge. sql/mysql_priv.h: Manual merge. sql/share/czech/errmsg.txt: Manual merge. sql/share/danish/errmsg.txt: Manual merge. sql/share/dutch/errmsg.txt: Manual merge. sql/share/english/errmsg.txt: Manual merge. sql/share/estonian/errmsg.txt: Manual merge. sql/share/french/errmsg.txt: Manual merge. sql/share/german/errmsg.txt: Manual merge. sql/share/greek/errmsg.txt: Manual merge. sql/share/hungarian/errmsg.txt: Manual merge. sql/share/italian/errmsg.txt: Manual merge. sql/share/japanese/errmsg.txt: Manual merge. sql/share/korean/errmsg.txt: Manual merge. sql/share/norwegian-ny/errmsg.txt: Manual merge. sql/share/norwegian/errmsg.txt: Manual merge. sql/share/polish/errmsg.txt: Manual merge. sql/share/portuguese/errmsg.txt: Manual merge. sql/share/romanian/errmsg.txt: Manual merge. sql/share/russian/errmsg.txt: Manual merge. sql/share/serbian/errmsg.txt: Manual merge. sql/share/slovak/errmsg.txt: Manual merge. sql/share/spanish/errmsg.txt: Manual merge. sql/share/swedish/errmsg.txt: Manual merge. sql/share/ukrainian/errmsg.txt: Manual merge. sql/sp_head.cc: Manual merge.
Diffstat (limited to 'sql/sql_yacc.yy')
-rw-r--r--sql/sql_yacc.yy284
1 files changed, 254 insertions, 30 deletions
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index ba3a36f2c34..41432cee673 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -242,6 +242,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token DISTINCT
%token DUPLICATE_SYM
%token DYNAMIC_SYM
+%token EACH_SYM
%token ENABLE_SYM
%token ENCLOSED
%token ESCAPED
@@ -411,6 +412,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token TO_SYM
%token TRAILING
%token TRANSACTION_SYM
+%token TRIGGER_SYM
%token TRUE_SYM
%token TYPE_SYM
%token TYPES_SYM
@@ -1204,6 +1206,57 @@ create:
}
opt_view_list AS select_init check_option
{}
+ | CREATE TRIGGER_SYM ident trg_action_time trg_event
+ ON table_ident FOR_SYM EACH_SYM ROW_SYM
+ {
+ LEX *lex= Lex;
+ sp_head *sp;
+
+ lex->name_and_length= $3;
+
+ /* QQ: Could we loosen lock type in certain cases ? */
+ if (!lex->select_lex.add_table_to_list(YYTHD, $7,
+ (LEX_STRING*) 0,
+ TL_OPTION_UPDATING,
+ TL_WRITE))
+ YYABORT;
+
+ if (lex->sphead)
+ {
+ net_printf(YYTHD, ER_SP_NO_RECURSIVE_CREATE, "TRIGGER");
+ YYABORT;
+ }
+
+ sp= new sp_head();
+ sp->reset_thd_mem_root(YYTHD);
+ sp->init(lex);
+
+ sp->m_type= TYPE_ENUM_TRIGGER;
+ lex->sphead= sp;
+ /*
+ We have to turn of CLIENT_MULTI_QUERIES while parsing a
+ stored procedure, otherwise yylex will chop it into pieces
+ at each ';'.
+ */
+ sp->m_old_cmq= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES;
+ YYTHD->client_capabilities &= ~CLIENT_MULTI_QUERIES;
+
+ bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics));
+ lex->sphead->m_chistics= &lex->sp_chistics;
+ lex->sphead->m_body_begin= lex->tok_start;
+ }
+ sp_proc_stmt
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+
+ lex->sql_command= SQLCOM_CREATE_TRIGGER;
+ sp->init_strings(YYTHD, lex, NULL);
+ /* Restore flag if it was cleared above */
+ if (sp->m_old_cmq)
+ YYTHD->client_capabilities |= CLIENT_MULTI_QUERIES;
+ sp->restore_thd_mem_root(YYTHD);
+ }
;
sp_name:
@@ -1738,14 +1791,14 @@ sp_proc_stmt:
if (lex->sql_command != SQLCOM_SET_OPTION ||
! lex->var_list.is_empty())
{
- /* Currently we can't handle queries inside a FUNCTION,
- ** because of the way table locking works.
- ** This is unfortunate, and limits the usefulness of functions
- ** a great deal, but it's nothing we can do about this at the
- ** moment.
- */
- if (lex->sphead->m_type == TYPE_ENUM_FUNCTION &&
- lex->sql_command != SQLCOM_SET_OPTION)
+ /*
+ Currently we can't handle queries inside a FUNCTION or
+ TRIGGER, because of the way table locking works. This is
+ unfortunate, and limits the usefulness of functions and
+ especially triggers a tremendously, but it's nothing we
+ can do about this at the moment.
+ */
+ if (lex->sphead->m_type != TYPE_ENUM_PROCEDURE)
{
send_error(YYTHD, ER_SP_BADSTATEMENT);
YYABORT;
@@ -2278,6 +2331,22 @@ sp_unlabeled_control:
}
;
+trg_action_time:
+ BEFORE_SYM
+ { Lex->trg_chistics.action_time= TRG_ACTION_BEFORE; }
+ | AFTER_SYM
+ { Lex->trg_chistics.action_time= TRG_ACTION_AFTER; }
+ ;
+
+trg_event:
+ INSERT
+ { Lex->trg_chistics.event= TRG_EVENT_INSERT; }
+ | UPDATE_SYM
+ { Lex->trg_chistics.event= TRG_EVENT_UPDATE; }
+ | DELETE_SYM
+ { Lex->trg_chistics.event= TRG_EVENT_DELETE; }
+ ;
+
create2:
'(' create2a {}
| opt_create_table_options create3 {}
@@ -5347,7 +5416,21 @@ drop:
lex->sql_command= SQLCOM_DROP_VIEW;
lex->drop_if_exists= $3;
}
- ;
+ | DROP TRIGGER_SYM ident '.' ident
+ {
+ LEX *lex= Lex;
+
+ lex->sql_command= SQLCOM_DROP_TRIGGER;
+ /* QQ: Could we loosen lock type in certain cases ? */
+ if (!lex->select_lex.add_table_to_list(YYTHD,
+ new Table_ident($3),
+ (LEX_STRING*) 0,
+ TL_OPTION_UPDATING,
+ TL_WRITE))
+ YYABORT;
+ lex->name_and_length= $5;
+ }
+ ;
table_list:
table_name
@@ -6338,18 +6421,70 @@ simple_ident_q:
{
THD *thd= YYTHD;
LEX *lex= thd->lex;
- SELECT_LEX *sel= lex->current_select;
- if (sel->no_table_names_allowed)
- {
- my_printf_error(ER_TABLENAME_NOT_ALLOWED_HERE,
- ER(ER_TABLENAME_NOT_ALLOWED_HERE),
- MYF(0), $1.str, thd->where);
- }
- $$= (sel->parsing_place != SELECT_LEX_NODE::IN_HAVING ||
- sel->get_in_sum_expr() > 0) ?
- (Item*) new Item_field(NullS,$1.str,$3.str) :
- (Item*) new Item_ref(0,0,NullS,$1.str,$3.str);
- }
+
+ /*
+ FIXME This will work ok in simple_ident_nospvar case because
+ we can't meet simple_ident_nospvar in trigger now. But it
+ should be changed in future.
+ */
+ if (lex->sphead && lex->sphead->m_type == TYPE_ENUM_TRIGGER &&
+ (!my_strcasecmp(system_charset_info, $1.str, "NEW") ||
+ !my_strcasecmp(system_charset_info, $1.str, "OLD")))
+ {
+ bool new_row= ($1.str[0]=='N' || $1.str[0]=='n');
+
+ if (lex->trg_chistics.event == TRG_EVENT_INSERT &&
+ !new_row)
+ {
+ net_printf(YYTHD, ER_TRG_NO_SUCH_ROW_IN_TRG, "OLD",
+ "on INSERT");
+ YYABORT;
+ }
+
+ if (lex->trg_chistics.event == TRG_EVENT_DELETE &&
+ new_row)
+ {
+ net_printf(YYTHD, ER_TRG_NO_SUCH_ROW_IN_TRG, "NEW",
+ "on DELETE");
+ YYABORT;
+ }
+
+ Item_trigger_field *trg_fld=
+ new Item_trigger_field(new_row ? Item_trigger_field::NEW_ROW :
+ Item_trigger_field::OLD_ROW,
+ $3.str);
+
+ if (lex->trg_table &&
+ trg_fld->setup_field(thd, lex->trg_table,
+ lex->trg_chistics.event))
+ {
+ /*
+ FIXME. Far from perfect solution. See comment for
+ "SET NEW.field_name:=..." for more info.
+ */
+ net_printf(YYTHD, ER_BAD_FIELD_ERROR, $3.str,
+ new_row ? "NEW": "OLD");
+ YYABORT;
+ }
+
+ $$= (Item *)trg_fld;
+ }
+ else
+ {
+ SELECT_LEX *sel= lex->current_select;
+
+ if (sel->no_table_names_allowed)
+ {
+ my_printf_error(ER_TABLENAME_NOT_ALLOWED_HERE,
+ ER(ER_TABLENAME_NOT_ALLOWED_HERE),
+ MYF(0), $1.str, thd->where);
+ }
+ $$= (sel->parsing_place != SELECT_LEX_NODE::IN_HAVING ||
+ sel->get_in_sum_expr() > 0) ?
+ (Item*) new Item_field(NullS,$1.str,$3.str) :
+ (Item*) new Item_ref(0,0,NullS,$1.str,$3.str);
+ }
+ }
| '.' ident '.' ident
{
THD *thd= YYTHD;
@@ -6779,13 +6914,74 @@ opt_var_ident_type:
option_value:
'@' ident_or_text equal expr
{
- Lex->var_list.push_back(new set_var_user(new Item_func_set_user_var($2,$4)));
+ LEX *lex= Lex;
+
+ if (lex->sphead && lex->sphead->m_type != TYPE_ENUM_PROCEDURE)
+ {
+ /*
+ We have to use special instruction in functions and triggers
+ because sp_instr_stmt will close all tables and thus ruin
+ execution of statement invoking function or trigger.
+ */
+ sp_instr_set_user_var *i=
+ new sp_instr_set_user_var(lex->sphead->instructions(),
+ $2, $4);
+ lex->sphead->add_instr(i);
+ }
+ else
+ lex->var_list.push_back(new set_var_user(new Item_func_set_user_var($2,$4)));
+
}
| internal_variable_name equal set_expr_or_default
{
LEX *lex=Lex;
- if ($1.var)
+ if ($1.var == &trg_new_row_fake_var)
+ {
+ /* We are in trigger and assigning value to field of new row */
+ Item *it;
+ sp_instr_set_trigger_field *i;
+ if ($3 && $3->type() == Item::SUBSELECT_ITEM)
+ { /*
+ QQ For now, just disallow subselects as values
+ Unfortunately this doesn't helps in case when we have
+ subselect deeper in expression.
+ */
+ send_error(YYTHD, ER_SP_SUBSELECT_NYI);
+ YYABORT;
+ }
+ if ($3)
+ it= $3;
+ else
+ {
+ /* QQ: Shouldn't this be field's default value ? */
+ it= new Item_null();
+ }
+ i= new sp_instr_set_trigger_field(lex->sphead->instructions(),
+ $1.base_name, it);
+ if (lex->trg_table && i->setup_field(YYTHD, lex->trg_table,
+ lex->trg_chistics.event))
+ {
+ /*
+ FIXME. Now we are catching this kind of errors only
+ during opening tables. But this doesn't save us from most
+ common user error - misspelling field name, because we
+ will bark too late in this case... Moreover it is easy to
+ make table unusable with such kind of error...
+
+ So in future we either have to parse trigger definition
+ second time during create trigger or gather all trigger
+ fields in one list and perform setup_field() for them as
+ separate stage.
+
+ Error message also should be improved.
+ */
+ net_printf(YYTHD, ER_BAD_FIELD_ERROR, $1.base_name, "NEW");
+ YYABORT;
+ }
+ lex->sphead->add_instr(i);
+ }
+ else if ($1.var)
{ /* System variable */
lex->var_list.push_back(new set_var(lex->option_type, $1.var,
&$1.base_name, $3));
@@ -6891,18 +7087,46 @@ internal_variable_name:
}
| ident '.' ident
{
+ LEX *lex= Lex;
if (check_reserved_words(&$1))
{
yyerror(ER(ER_SYNTAX_ERROR));
YYABORT;
}
- sys_var *tmp=find_sys_var($3.str, $3.length);
- if (!tmp)
- YYABORT;
- if (!tmp->is_struct())
- net_printf(YYTHD, ER_VARIABLE_IS_NOT_STRUCT, $3.str);
- $$.var= tmp;
- $$.base_name= $1;
+ if (lex->sphead && lex->sphead->m_type == TYPE_ENUM_TRIGGER &&
+ (!my_strcasecmp(system_charset_info, $1.str, "NEW") ||
+ !my_strcasecmp(system_charset_info, $1.str, "OLD")))
+ {
+ if ($1.str[0]=='O' || $1.str[0]=='o')
+ {
+ net_printf(YYTHD, ER_TRG_CANT_CHANGE_ROW, "OLD", "");
+ YYABORT;
+ }
+ if (lex->trg_chistics.event == TRG_EVENT_DELETE)
+ {
+ net_printf(YYTHD, ER_TRG_NO_SUCH_ROW_IN_TRG, "NEW",
+ "on DELETE");
+ YYABORT;
+ }
+ if (lex->trg_chistics.action_time == TRG_ACTION_AFTER)
+ {
+ net_printf(YYTHD, ER_TRG_CANT_CHANGE_ROW, "NEW", "after ");
+ YYABORT;
+ }
+ /* This special combination will denote field of NEW row */
+ $$.var= &trg_new_row_fake_var;
+ $$.base_name= $3;
+ }
+ else
+ {
+ sys_var *tmp=find_sys_var($3.str, $3.length);
+ if (!tmp)
+ YYABORT;
+ if (!tmp->is_struct())
+ net_printf(YYTHD, ER_VARIABLE_IS_NOT_STRUCT, $3.str);
+ $$.var= tmp;
+ $$.base_name= $1;
+ }
}
| DEFAULT '.' ident
{