diff options
author | Alexander Barkov <bar@mariadb.org> | 2016-09-17 07:56:56 +0400 |
---|---|---|
committer | Alexander Barkov <bar@mariadb.org> | 2017-04-05 15:01:59 +0400 |
commit | 9f6aca198c9d580c310630dcf3f545647bb49368 (patch) | |
tree | 65f6798d345124400eda0ff797583ea7b131483e | |
parent | e34acc838b6f8ce4c84c28d6f65c463f831a9ef9 (diff) | |
download | mariadb-git-9f6aca198c9d580c310630dcf3f545647bb49368.tar.gz |
Adding an alternative grammar file sql_yacc_ora.yy for sql_mode=ORACLE
- Adding a new grammar file sql_yacc_ora.yy, which is currently
almost a full copy of sql_yacc.yy.
Note, it's now assumed that sql_yacc.yy and sql_yacc_ora.yy
use the same set of %token directives and exactly the same
%union directive.
These declarations should eventually be moved into a shared
included file, to make sure that sql_yacc.h and sql_yacc_ora.h
are compatible.
- Removing the "-p MYSQL" flag from cmake/bison.cmake, using
the %name-prefix directive inside sql_yacc.yy and sql_yacc_ora.yy instead
- Adding other CMake related changes to build sql_yacc_ora.o
form sql_yacc_ora.yy
- Adding NUMBER(M,N) as a synonym to DECIMAL(M,N) as the first
Oracle compatibility syntax understood in sql_mode=ORACLE.
- Adding prototypes to functions add_virtual_expression()
and handle_sql2003_note184_exception(), so they can be used
in both sql_yacc.yy and sql_yacc_ora.yy.
- Adding a new test suite compat/oracle, with the first test "type_number".
Use this:
./mtr compat/oracle.type_number # to run a single test
./mtr --suite=compat/oracle # to run the entire new suite
- Adding compat/oracle into the list of default suites,
so BuildBot can run it automatically on pushes.
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | cmake/bison.cmake | 2 | ||||
-rw-r--r-- | libmysqld/CMakeLists.txt | 2 | ||||
-rwxr-xr-x | mysql-test/mysql-test-run.pl | 1 | ||||
-rw-r--r-- | mysql-test/suite/compat/oracle/r/type_number.result | 8 | ||||
-rw-r--r-- | mysql-test/suite/compat/oracle/t/type_number.test | 4 | ||||
-rw-r--r-- | sql/CMakeLists.txt | 9 | ||||
-rw-r--r-- | sql/sql_lex.cc | 5 | ||||
-rw-r--r-- | sql/sql_lex.h | 4 | ||||
-rw-r--r-- | sql/sql_parse.cc | 6 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 1 | ||||
-rw-r--r-- | sql/sql_yacc_ora.yy | 16411 |
12 files changed, 16453 insertions, 2 deletions
diff --git a/.gitignore b/.gitignore index 469aa0d894b..4efe3a24e24 100644 --- a/.gitignore +++ b/.gitignore @@ -161,6 +161,8 @@ sql/mysqld sql/sql_builtin.cc sql/sql_yacc.cc sql/sql_yacc.h +sql/sql_yacc_ora.cc +sql/sql_yacc_ora.h storage/heap/hp_test1 storage/heap/hp_test2 storage/maria/aria_chk diff --git a/cmake/bison.cmake b/cmake/bison.cmake index d5c725fbbde..9651fe70cbb 100644 --- a/cmake/bison.cmake +++ b/cmake/bison.cmake @@ -50,7 +50,7 @@ MACRO (RUN_BISON input_yy output_cc output_h) ADD_CUSTOM_COMMAND( OUTPUT ${output_cc} ${output_h} - COMMAND ${BISON_EXECUTABLE} -y -p MYSQL + COMMAND ${BISON_EXECUTABLE} -y --output=${output_cc} --defines=${output_h} ${input_yy} diff --git a/libmysqld/CMakeLists.txt b/libmysqld/CMakeLists.txt index 5516cd7013a..07e7d433dde 100644 --- a/libmysqld/CMakeLists.txt +++ b/libmysqld/CMakeLists.txt @@ -30,6 +30,8 @@ ${SSL_INTERNAL_INCLUDE_DIRS} SET(GEN_SOURCES ${CMAKE_BINARY_DIR}/sql/sql_yacc.h ${CMAKE_BINARY_DIR}/sql/sql_yacc.cc +${CMAKE_BINARY_DIR}/sql/sql_yacc_ora.h +${CMAKE_BINARY_DIR}/sql/sql_yacc_ora.cc ${CMAKE_BINARY_DIR}/sql/lex_hash.h ) diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 296a646d9ba..5d81ee3265b 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -171,6 +171,7 @@ my @DEFAULT_SUITES= qw( binlog- binlog_encryption- csv- + compat/oracle- encryption- federated- funcs_1- diff --git a/mysql-test/suite/compat/oracle/r/type_number.result b/mysql-test/suite/compat/oracle/r/type_number.result new file mode 100644 index 00000000000..feac402b801 --- /dev/null +++ b/mysql-test/suite/compat/oracle/r/type_number.result @@ -0,0 +1,8 @@ +SET sql_mode=ORACLE; +CREATE TABLE t1 (a NUMBER(10,2)); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE "t1" ( + "a" decimal(10,2) DEFAULT NULL +) +DROP TABLE t1; diff --git a/mysql-test/suite/compat/oracle/t/type_number.test b/mysql-test/suite/compat/oracle/t/type_number.test new file mode 100644 index 00000000000..24b905c3bea --- /dev/null +++ b/mysql-test/suite/compat/oracle/t/type_number.test @@ -0,0 +1,4 @@ +SET sql_mode=ORACLE; +CREATE TABLE t1 (a NUMBER(10,2)); +SHOW CREATE TABLE t1; +DROP TABLE t1; diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index fc41a31c3c4..a82aeb8f32e 100644 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -48,6 +48,8 @@ ${WSREP_INCLUDES} SET(GEN_SOURCES ${CMAKE_CURRENT_BINARY_DIR}/sql_yacc.h ${CMAKE_CURRENT_BINARY_DIR}/sql_yacc.cc +${CMAKE_CURRENT_BINARY_DIR}/sql_yacc_ora.h +${CMAKE_CURRENT_BINARY_DIR}/sql_yacc_ora.cc ${CMAKE_CURRENT_BINARY_DIR}/lex_hash.h ) SET(GEN_DIGEST_SOURCES @@ -279,6 +281,12 @@ RUN_BISON( ${CMAKE_CURRENT_BINARY_DIR}/sql_yacc.h ) +RUN_BISON( + ${CMAKE_CURRENT_SOURCE_DIR}/sql_yacc_ora.yy + ${CMAKE_CURRENT_BINARY_DIR}/sql_yacc_ora.cc + ${CMAKE_CURRENT_BINARY_DIR}/sql_yacc_ora.h +) + # Gen_lex_hash IF(NOT CMAKE_CROSSCOMPILING) ADD_EXECUTABLE(gen_lex_hash gen_lex_hash.cc) @@ -329,6 +337,7 @@ CONFIGURE_FILE( ADD_CUSTOM_TARGET(dist COMMAND ${CMAKE_COMMAND} -P ${CMAKE_BINARY_DIR}/make_dist.cmake DEPENDS ${CMAKE_BINARY_DIR}/sql/sql_yacc.cc ${CMAKE_BINARY_DIR}/sql/sql_yacc.h + DEPENDS ${CMAKE_BINARY_DIR}/sql/sql_yacc_ora.cc ${CMAKE_BINARY_DIR}/sql/sql_yacc_ora.h WORKING_DIRECTORY ${CMAKE_BINARY_DIR} ) diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index a3b87d59df3..335652d2bd7 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -1336,6 +1336,11 @@ int MYSQLlex(YYSTYPE *yylval, THD *thd) return token; } +int ORAlex(YYSTYPE *yylval, THD *thd) +{ + return MYSQLlex(yylval, thd); +} + static int lex_one_token(YYSTYPE *yylval, THD *thd) { reg1 uchar UNINIT_VAR(c); diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 136f4b4fbd1..f3ec34b6d4c 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -3367,6 +3367,7 @@ extern void lex_end_stage2(LEX *lex); void end_lex_with_single_table(THD *thd, TABLE *table, LEX *old_lex); int init_lex_with_single_table(THD *thd, TABLE *table, LEX *lex); extern int MYSQLlex(union YYSTYPE *yylval, THD *thd); +extern int ORAlex(union YYSTYPE *yylval, THD *thd); extern void trim_whitespace(CHARSET_INFO *cs, LEX_STRING *str, uint *prefix_removed); @@ -3382,5 +3383,8 @@ extern bool is_native_function_with_warn(THD *thd, const LEX_STRING *name); void my_missing_function_error(const LEX_STRING &token, const char *name); bool is_keyword(const char *name, uint len); +Virtual_column_info *add_virtual_expression(THD *thd, Item *expr); +Item* handle_sql2003_note184_exception(THD *thd, Item* left, bool equal, + Item *expr); #endif /* MYSQL_SERVER */ #endif /* SQL_LEX_INCLUDED */ diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 4cfa89a4bc6..efcde7950eb 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -9849,6 +9849,7 @@ bool check_host_name(LEX_STRING *str) extern int MYSQLparse(THD *thd); // from sql_yacc.cc +extern int ORAparse(THD *thd); // from sql_yacc_ora.cc /** @@ -9908,7 +9909,10 @@ bool parse_sql(THD *thd, Parser_state *parser_state, /* Parse the query. */ - bool mysql_parse_status= MYSQLparse(thd) != 0; + bool mysql_parse_status= + ((thd->variables.sql_mode & MODE_ORACLE) ? + ORAparse(thd) : + MYSQLparse(thd)) != 0; /* Check that if MYSQLparse() failed either thd->is_error() is set, or an diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 1396700de06..3532288e519 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -993,6 +993,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %} %pure-parser /* We have threads */ +%name-prefix "MYSQL" %parse-param { THD *thd } %lex-param { THD *thd } /* diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy new file mode 100644 index 00000000000..228086d4478 --- /dev/null +++ b/sql/sql_yacc_ora.yy @@ -0,0 +1,16411 @@ +/* + Copyright (c) 2000, 2015, Oracle and/or its affiliates. + Copyright (c) 2010, 2016, MariaDB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +/* sql_yacc.yy */ + +/** + @defgroup Parser Parser + @{ +*/ + +%{ +#define YYLIP (& thd->m_parser_state->m_lip) +#define YYPS (& thd->m_parser_state->m_yacc) +#define YYCSCL (thd->variables.character_set_client) + +#define MYSQL_YACC +#define YYINITDEPTH 100 +#define YYMAXDEPTH 3200 /* Because of 64K stack */ +#define Lex (thd->lex) + +#define Select Lex->current_select +#include <my_global.h> +#include "sql_priv.h" +#include "sql_parse.h" /* comp_*_creator */ +#include "sql_table.h" /* primary_key_name */ +#include "sql_partition.h" /* mem_alloc_error, partition_info, HASH_PARTITION */ +#include "sql_acl.h" /* *_ACL */ +#include "sql_class.h" /* Key_part_spec, enum_filetype, Diag_condition_item_name */ +#include "slave.h" +#include "lex_symbol.h" +#include "item_create.h" +#include "sp_head.h" +#include "sp_rcontext.h" +#include "sp.h" +#include "sql_show.h" +#include "sql_alter.h" // Sql_cmd_alter_table* +#include "sql_truncate.h" // Sql_cmd_truncate_table +#include "sql_admin.h" // Sql_cmd_analyze/Check..._table +#include "sql_partition_admin.h" // Sql_cmd_alter_table_*_part. +#include "sql_handler.h" // Sql_cmd_handler_* +#include "sql_signal.h" +#include "sql_get_diagnostics.h" // Sql_cmd_get_diagnostics +#include "sql_cte.h" +#include "sql_window.h" +#include "item_windowfunc.h" +#include "event_parse_data.h" +#include "create_options.h" +#include <myisam.h> +#include <myisammrg.h> +#include "keycaches.h" +#include "set_var.h" +#include "rpl_mi.h" +#include "lex_token.h" +#include "sql_lex.h" + +/* this is to get the bison compilation windows warnings out */ +#ifdef _MSC_VER +/* warning C4065: switch statement contains 'default' but no 'case' labels */ +#pragma warning (disable : 4065) +#endif + +int yylex(void *yylval, void *yythd); + +#define yyoverflow(A,B,C,D,E,F) \ + { \ + ulong val= *(F); \ + if (my_yyoverflow((B), (D), &val)) \ + { \ + yyerror(thd, (char*) (A)); \ + return 2; \ + } \ + else \ + { \ + *(F)= (YYSIZE_T)val; \ + } \ + } + +#define MYSQL_YYABORT \ + do \ + { \ + LEX::cleanup_lex_after_parse_error(thd); \ + YYABORT; \ + } while (0) + +#define MYSQL_YYABORT_UNLESS(A) \ + if (!(A)) \ + { \ + my_parse_error(thd, ER_SYNTAX_ERROR); \ + MYSQL_YYABORT; \ + } + +#define my_yyabort_error(A) \ + do { my_error A; MYSQL_YYABORT; } while(0) + +#ifndef DBUG_OFF +#define YYDEBUG 1 +#else +#define YYDEBUG 0 +#endif + +/** + @brief Push an error message into MySQL error stack with line + and position information. + + This function provides semantic action implementers with a way + to push the famous "You have a syntax error near..." error + message into the error stack, which is normally produced only if + a parse error is discovered internally by the Bison generated + parser. +*/ + +static void my_parse_error_intern(THD *thd, const char *err_text, + const char *yytext) +{ + Lex_input_stream *lip= &thd->m_parser_state->m_lip; + if (!yytext) + { + if (!(yytext= lip->get_tok_start())) + yytext= ""; + } + /* Push an error into the error stack */ + ErrConvString err(yytext, strlen(yytext), + thd->variables.character_set_client); + my_error(ER_PARSE_ERROR, MYF(0), err_text, err.ptr(), lip->yylineno); +} + + +static void my_parse_error(THD *thd, uint err_number, const char *yytext=0) +{ + return my_parse_error_intern(thd, ER_THD(thd, err_number), yytext); +} + +/** + @brief Bison callback to report a syntax/OOM error + + This function is invoked by the bison-generated parser + when a syntax error, a parse error or an out-of-memory + condition occurs. This function is not invoked when the + parser is requested to abort by semantic action code + by means of YYABORT or YYACCEPT macros. This is why these + macros should not be used (use MYSQL_YYABORT/MYSQL_YYACCEPT + instead). + + The parser will abort immediately after invoking this callback. + + This function is not for use in semantic actions and is internal to + the parser, as it performs some pre-return cleanup. + In semantic actions, please use my_parse_error or my_error to + push an error into the error stack and MYSQL_YYABORT + to abort from the parser. +*/ + +void ORAerror(THD *thd, const char *s) +{ + /* + Restore the original LEX if it was replaced when parsing + a stored procedure. We must ensure that a parsing error + does not leave any side effects in the THD. + */ + LEX::cleanup_lex_after_parse_error(thd); + + /* "parse error" changed into "syntax error" between bison 1.75 and 1.875 */ + if (strcmp(s,"parse error") == 0 || strcmp(s,"syntax error") == 0) + s= ER_THD(thd, ER_SYNTAX_ERROR); + my_parse_error_intern(thd, s, 0); +} + + + + +static sp_head *make_sp_head(THD *thd, sp_name *name, + enum stored_procedure_type type) +{ + LEX *lex= thd->lex; + sp_head *sp; + + /* Order is important here: new - reset - init */ + if ((sp= new sp_head())) + { + sp->reset_thd_mem_root(thd); + sp->init(lex); + sp->m_type= type; + if (name) + sp->init_sp_name(thd, name); + sp->m_chistics= &lex->sp_chistics; + lex->sphead= sp; + } + bzero(&lex->sp_chistics, sizeof(lex->sp_chistics)); + return sp; +} + +static bool maybe_start_compound_statement(THD *thd) +{ + if (!thd->lex->sphead) + { + if (!make_sp_head(thd, NULL, TYPE_ENUM_PROCEDURE)) + return 1; + + Lex->sp_chistics.suid= SP_IS_NOT_SUID; + Lex->sphead->set_body_start(thd, YYLIP->get_cpp_ptr()); + } + return 0; +} + +static bool push_sp_label(THD *thd, LEX_STRING label) +{ + sp_pcontext *ctx= thd->lex->spcont; + sp_label *lab= ctx->find_label(label); + + if (lab) + { + my_error(ER_SP_LABEL_REDEFINE, MYF(0), label.str); + return 1; + } + else + { + lab= thd->lex->spcont->push_label(thd, label, + thd->lex->sphead->instructions()); + lab->type= sp_label::ITERATION; + } + return 0; +} + +static bool push_sp_empty_label(THD *thd) +{ + if (maybe_start_compound_statement(thd)) + return 1; + /* Unlabeled controls get an empty label. */ + thd->lex->spcont->push_label(thd, empty_lex_str, + thd->lex->sphead->instructions()); + return 0; +} + + +static 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_lex_str; + + return thd->is_error(); +} + + +/** + 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->autocommit= 0; + /* get_ptr() is only correct with no lookahead. */ + 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; + } +} + + +/** + 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 (thd->mem_root) + 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; +} + + +#define bincmp_collation(X,Y) \ + do \ + { \ + if (Lex->set_bincmp(X,Y)) \ + MYSQL_YYABORT; \ + } while(0) + +%} +%union { + int num; + ulong ulong_num; + ulonglong ulonglong_number; + longlong longlong_number; + + /* structs */ + LEX_STRING lex_str; + LEX_SYMBOL symbol; + Lex_string_with_metadata_st lex_string_with_metadata; + struct sys_var_with_base variable; + struct { int vars, conds, hndlrs, curs; } spblock; + Lex_length_and_dec_st Lex_length_and_dec; + Lex_cast_type_st Lex_cast_type; + Lex_field_type_st Lex_field_type; + Lex_dyncol_type_st Lex_dyncol_type; + + /* pointers */ + Create_field *create_field; + CHARSET_INFO *charset; + Condition_information_item *cond_info_item; + DYNCALL_CREATE_DEF *dyncol_def; + Diagnostics_information *diag_info; + Item *item; + Item_num *item_num; + Item_param *item_param; + Key_part_spec *key_part; + LEX *lex; + LEX_STRING *lex_str_ptr; + LEX_USER *lex_user; + List<Condition_information_item> *cond_info_list; + List<DYNCALL_CREATE_DEF> *dyncol_def_list; + List<Item> *item_list; + List<Statement_information_item> *stmt_info_list; + List<String> *string_list; + List<LEX_STRING> *lex_str_list; + Statement_information_item *stmt_info_item; + String *string; + TABLE_LIST *table_list; + Table_ident *table; + char *simple_string; + const char *const_simple_string; + chooser_compare_func_creator boolfunc2creator; + class my_var *myvar; + class sp_condition_value *spcondvalue; + class sp_head *sphead; + class sp_label *splabel; + class sp_name *spname; + class sp_variable *spvar; + class With_clause *with_clause; + class Virtual_column_info *virtual_column; + + handlerton *db_type; + st_select_lex *select_lex; + struct p_elem_val *p_elem_value; + class Window_frame *window_frame; + class Window_frame_bound *window_frame_bound; + udf_func *udf; + st_trg_execution_order trg_execution_order; + + /* enums */ + enum sub_select_type unit_type; + enum Condition_information_item::Name cond_info_item_name; + enum enum_diag_condition_item_name diag_condition_item_name; + enum Diagnostics_information::Which_area diag_area; + enum Field::geometry_type geom_type; + enum enum_fk_option m_fk_option; + enum Item_udftype udf_type; + enum Key::Keytype key_type; + enum Statement_information_item::Name stmt_info_item_name; + enum enum_field_types field_type; + enum enum_filetype filetype; + enum enum_tx_isolation tx_isolation; + enum enum_var_type var_type; + enum enum_yes_no_unknown m_yes_no_unk; + enum ha_choice choice; + enum ha_key_alg key_alg; + enum ha_rkey_function ha_rkey_mode; + enum index_hint_type index_hint; + enum interval_type interval, interval_time_st; + enum row_type row_type; + enum sp_variable::enum_mode spvar_mode; + enum thr_lock_type lock_type; + enum enum_mysql_timestamp_type date_time_type; + enum Window_frame_bound::Bound_precedence_type bound_precedence_type; + enum Window_frame::Frame_units frame_units; + enum Window_frame::Frame_exclusion frame_exclusion; + enum trigger_order_type trigger_action_order_type; + DDL_options_st object_ddl_options; +} + +%{ +bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); +%} + +%pure-parser /* We have threads */ +%name-prefix "ORA" +%parse-param { THD *thd } +%lex-param { THD *thd } +/* + Currently there are 102 shift/reduce conflicts. + We should not introduce new conflicts any more. +*/ +%expect 102 + +/* + Comments for TOKENS. + For each token, please include in the same line a comment that contains + the following tags: + SQL-2011-N : Non Reserved keywird as per SQL-2011 + SQL-2003-R : Reserved keyword as per SQL-2003 + SQL-2003-N : Non Reserved keyword as per SQL-2003 + SQL-1999-R : Reserved keyword as per SQL-1999 + SQL-1999-N : Non Reserved keyword as per SQL-1999 + MYSQL : MySQL extention (unspecified) + MYSQL-FUNC : MySQL extention, function + INTERNAL : Not a real token, lex optimization + OPERATOR : SQL operator + FUTURE-USE : Reserved for future use + + This makes the code grep-able, and helps maintenance. +*/ + +%token ABORT_SYM /* INTERNAL (used in lex) */ +%token ACCESSIBLE_SYM +%token ACTION /* SQL-2003-N */ +%token ADD /* SQL-2003-R */ +%token ADMIN_SYM /* SQL-2003-N */ +%token ADDDATE_SYM /* MYSQL-FUNC */ +%token AFTER_SYM /* SQL-2003-N */ +%token AGAINST +%token AGGREGATE_SYM +%token ALGORITHM_SYM +%token ALL /* SQL-2003-R */ +%token ALTER /* SQL-2003-R */ +%token ALWAYS_SYM +%token ANALYZE_SYM +%token AND_AND_SYM /* OPERATOR */ +%token AND_SYM /* SQL-2003-R */ +%token ANY_SYM /* SQL-2003-R */ +%token AS /* SQL-2003-R */ +%token ASC /* SQL-2003-N */ +%token ASCII_SYM /* MYSQL-FUNC */ +%token ASENSITIVE_SYM /* FUTURE-USE */ +%token AT_SYM /* SQL-2003-R */ +%token ATOMIC_SYM /* SQL-2003-R */ +%token AUTHORS_SYM +%token AUTOEXTEND_SIZE_SYM +%token AUTO_INC +%token AUTO_SYM +%token AVG_ROW_LENGTH +%token AVG_SYM /* SQL-2003-N */ +%token BACKUP_SYM +%token BEFORE_SYM /* SQL-2003-N */ +%token BEGIN_SYM /* SQL-2003-R */ +%token BETWEEN_SYM /* SQL-2003-R */ +%token BIGINT /* SQL-2003-R */ +%token BINARY /* SQL-2003-R */ +%token BINLOG_SYM +%token BIN_NUM +%token BIT_AND /* MYSQL-FUNC */ +%token BIT_OR /* MYSQL-FUNC */ +%token BIT_SYM /* MYSQL-FUNC */ +%token BIT_XOR /* MYSQL-FUNC */ +%token BLOB_SYM /* SQL-2003-R */ +%token BLOCK_SYM +%token BOOLEAN_SYM /* SQL-2003-R */ +%token BOOL_SYM +%token BOTH /* SQL-2003-R */ +%token BTREE_SYM +%token BY /* SQL-2003-R */ +%token BYTE_SYM +%token CACHE_SYM +%token CALL_SYM /* SQL-2003-R */ +%token CASCADE /* SQL-2003-N */ +%token CASCADED /* SQL-2003-R */ +%token CASE_SYM /* SQL-2003-R */ +%token CAST_SYM /* SQL-2003-R */ +%token CATALOG_NAME_SYM /* SQL-2003-N */ +%token CHAIN_SYM /* SQL-2003-N */ +%token CHANGE +%token CHANGED +%token CHARSET +%token CHAR_SYM /* SQL-2003-R */ +%token CHECKPOINT_SYM +%token CHECKSUM_SYM +%token CHECK_SYM /* SQL-2003-R */ +%token CIPHER_SYM +%token CLASS_ORIGIN_SYM /* SQL-2003-N */ +%token CLIENT_SYM +%token CLOSE_SYM /* SQL-2003-R */ +%token COALESCE /* SQL-2003-N */ +%token CODE_SYM +%token COLLATE_SYM /* SQL-2003-R */ +%token COLLATION_SYM /* SQL-2003-N */ +%token COLUMNS +%token COLUMN_ADD_SYM +%token COLUMN_CHECK_SYM +%token COLUMN_CREATE_SYM +%token COLUMN_DELETE_SYM +%token COLUMN_GET_SYM +%token COLUMN_SYM /* SQL-2003-R */ +%token COLUMN_NAME_SYM /* SQL-2003-N */ +%token COMMENT_SYM +%token COMMITTED_SYM /* SQL-2003-N */ +%token COMMIT_SYM /* SQL-2003-R */ +%token COMPACT_SYM +%token COMPLETION_SYM +%token COMPRESSED_SYM +%token CONCURRENT +%token CONDITION_SYM /* SQL-2003-R, SQL-2008-R */ +%token CONNECTION_SYM +%token CONSISTENT_SYM +%token CONSTRAINT /* SQL-2003-R */ +%token CONSTRAINT_CATALOG_SYM /* SQL-2003-N */ +%token CONSTRAINT_NAME_SYM /* SQL-2003-N */ +%token CONSTRAINT_SCHEMA_SYM /* SQL-2003-N */ +%token CONTAINS_SYM /* SQL-2003-N */ +%token CONTEXT_SYM +%token CONTINUE_SYM /* SQL-2003-R */ +%token CONTRIBUTORS_SYM +%token CONVERT_SYM /* SQL-2003-N */ +%token COUNT_SYM /* SQL-2003-N */ +%token CPU_SYM +%token CREATE /* SQL-2003-R */ +%token CROSS /* SQL-2003-R */ +%token CUBE_SYM /* SQL-2003-R */ +%token CUME_DIST_SYM +%token CURDATE /* MYSQL-FUNC */ +%token CURRENT_SYM /* SQL-2003-R */ +%token CURRENT_USER /* SQL-2003-R */ +%token CURRENT_ROLE /* SQL-2003-R */ +%token CURRENT_POS_SYM +%token CURSOR_SYM /* SQL-2003-R */ +%token CURSOR_NAME_SYM /* SQL-2003-N */ +%token CURTIME /* MYSQL-FUNC */ +%token DATABASE +%token DATABASES +%token DATAFILE_SYM +%token DATA_SYM /* SQL-2003-N */ +%token DATETIME +%token DATE_ADD_INTERVAL /* MYSQL-FUNC */ +%token DATE_SUB_INTERVAL /* MYSQL-FUNC */ +%token DATE_SYM /* SQL-2003-R */ +%token DAY_HOUR_SYM +%token DAY_MICROSECOND_SYM +%token DAY_MINUTE_SYM +%token DAY_SECOND_SYM +%token DAY_SYM /* SQL-2003-R */ +%token DEALLOCATE_SYM /* SQL-2003-R */ +%token DECIMAL_NUM +%token DECIMAL_SYM /* SQL-2003-R */ +%token DECLARE_SYM /* SQL-2003-R */ +%token DEFAULT /* SQL-2003-R */ +%token DEFINER_SYM +%token DELAYED_SYM +%token DELAY_KEY_WRITE_SYM +%token DELETE_SYM /* SQL-2003-R */ +%token DENSE_RANK_SYM +%token DESC /* SQL-2003-N */ +%token DESCRIBE /* SQL-2003-R */ +%token DES_KEY_FILE +%token DETERMINISTIC_SYM /* SQL-2003-R */ +%token DIAGNOSTICS_SYM /* SQL-2003-N */ +%token DIRECTORY_SYM +%token DISABLE_SYM +%token DISCARD +%token DISK_SYM +%token DISTINCT /* SQL-2003-R */ +%token DIV_SYM +%token DOUBLE_SYM /* SQL-2003-R */ +%token DO_DOMAIN_IDS_SYM +%token DO_SYM +%token DROP /* SQL-2003-R */ +%token DUAL_SYM +%token DUMPFILE +%token DUPLICATE_SYM +%token DYNAMIC_SYM /* SQL-2003-R */ +%token EACH_SYM /* SQL-2003-R */ +%token ELSE /* SQL-2003-R */ +%token ELSEIF_SYM +%token ENABLE_SYM +%token ENCLOSED +%token END /* SQL-2003-R */ +%token ENDS_SYM +%token END_OF_INPUT /* INTERNAL */ +%token ENGINES_SYM +%token ENGINE_SYM +%token ENUM +%token EQUAL_SYM /* OPERATOR */ +%token ERROR_SYM +%token ERRORS +%token ESCAPED +%token ESCAPE_SYM /* SQL-2003-R */ +%token EVENTS_SYM +%token EVENT_SYM +%token EVERY_SYM /* SQL-2003-N */ +%token EXCHANGE_SYM +%token EXAMINED_SYM +%token EXCEPT_SYM /* SQL-2003-R */ +%token EXCLUDE_SYM /* SQL-2011-N */ +%token EXECUTE_SYM /* SQL-2003-R */ +%token EXISTS /* SQL-2003-R */ +%token EXIT_SYM +%token EXPANSION_SYM +%token EXPORT_SYM +%token EXTENDED_SYM +%token EXTENT_SIZE_SYM +%token EXTRACT_SYM /* SQL-2003-N */ +%token FALSE_SYM /* SQL-2003-R */ +%token FAST_SYM +%token FAULTS_SYM +%token FETCH_SYM /* SQL-2003-R */ +%token FILE_SYM +%token FIRST_VALUE_SYM /* SQL-2011 */ +%token FIRST_SYM /* SQL-2003-N */ +%token FIXED_SYM +%token FLOAT_NUM +%token FLOAT_SYM /* SQL-2003-R */ +%token FLUSH_SYM +%token FOLLOWS_SYM /* MYSQL trigger*/ +%token FOLLOWING_SYM /* SQL-2011-N */ +%token FORCE_SYM +%token FOREIGN /* SQL-2003-R */ +%token FOR_SYM /* SQL-2003-R */ +%token FORMAT_SYM +%token FOUND_SYM /* SQL-2003-R */ +%token FROM +%token FULL /* SQL-2003-R */ +%token FULLTEXT_SYM +%token FUNCTION_SYM /* SQL-2003-R */ +%token GE +%token GENERAL +%token GENERATED_SYM +%token GEOMETRYCOLLECTION +%token GEOMETRY_SYM +%token GET_FORMAT /* MYSQL-FUNC */ +%token GET_SYM /* SQL-2003-R */ +%token GLOBAL_SYM /* SQL-2003-R */ +%token GRANT /* SQL-2003-R */ +%token GRANTS +%token GROUP_SYM /* SQL-2003-R */ +%token GROUP_CONCAT_SYM +%token LAG_SYM /* SQL-2011 */ +%token LEAD_SYM /* SQL-2011 */ +%token HANDLER_SYM +%token HARD_SYM +%token HASH_SYM +%token HAVING /* SQL-2003-R */ +%token HELP_SYM +%token HEX_NUM +%token HEX_STRING +%token HIGH_PRIORITY +%token HOST_SYM +%token HOSTS_SYM +%token HOUR_MICROSECOND_SYM +%token HOUR_MINUTE_SYM +%token HOUR_SECOND_SYM +%token HOUR_SYM /* SQL-2003-R */ +%token ID_SYM /* MYSQL */ +%token IDENT +%token IDENTIFIED_SYM +%token IDENT_QUOTED +%token IF_SYM +%token IGNORE_DOMAIN_IDS_SYM +%token IGNORE_SYM +%token IGNORE_SERVER_IDS_SYM +%token IMMEDIATE_SYM /* SQL-2003-R */ +%token IMPORT +%token INDEXES +%token INDEX_SYM +%token INFILE +%token INITIAL_SIZE_SYM +%token INNER_SYM /* SQL-2003-R */ +%token INOUT_SYM /* SQL-2003-R */ +%token INSENSITIVE_SYM /* SQL-2003-R */ +%token INSERT /* SQL-2003-R */ +%token INSERT_METHOD +%token INSTALL_SYM +%token INTERSECT_SYM /* SQL-2003-R */ +%token INTERVAL_SYM /* SQL-2003-R */ +%token INTO /* SQL-2003-R */ +%token INT_SYM /* SQL-2003-R */ +%token INVOKER_SYM +%token IN_SYM /* SQL-2003-R */ +%token IO_SYM +%token IPC_SYM +%token IS /* SQL-2003-R */ +%token ISOLATION /* SQL-2003-R */ +%token ISSUER_SYM +%token ITERATE_SYM +%token JOIN_SYM /* SQL-2003-R */ +%token JSON_SYM +%token KEYS +%token KEY_BLOCK_SIZE +%token KEY_SYM /* SQL-2003-N */ +%token KILL_SYM +%token LANGUAGE_SYM /* SQL-2003-R */ +%token LAST_SYM /* SQL-2003-N */ +%token LAST_VALUE +%token LE /* OPERATOR */ +%token LEADING /* SQL-2003-R */ +%token LEAVES +%token LEAVE_SYM +%token LEFT /* SQL-2003-R */ +%token LESS_SYM +%token LEVEL_SYM +%token LEX_HOSTNAME +%token LIKE /* SQL-2003-R */ +%token LIMIT +%token LINEAR_SYM +%token LINES +%token LINESTRING +%token LIST_SYM +%token LOAD +%token LOCAL_SYM /* SQL-2003-R */ +%token LOCATOR_SYM /* SQL-2003-N */ +%token LOCKS_SYM +%token LOCK_SYM +%token LOGFILE_SYM +%token LOGS_SYM +%token LONGBLOB +%token LONGTEXT +%token LONG_NUM +%token LONG_SYM +%token LOOP_SYM +%token LOW_PRIORITY +%token MASTER_CONNECT_RETRY_SYM +%token MASTER_DELAY_SYM +%token MASTER_GTID_POS_SYM +%token MASTER_HOST_SYM +%token MASTER_LOG_FILE_SYM +%token MASTER_LOG_POS_SYM +%token MASTER_PASSWORD_SYM +%token MASTER_PORT_SYM +%token MASTER_SERVER_ID_SYM +%token MASTER_SSL_CAPATH_SYM +%token MASTER_SSL_CA_SYM +%token MASTER_SSL_CERT_SYM +%token MASTER_SSL_CIPHER_SYM +%token MASTER_SSL_CRL_SYM +%token MASTER_SSL_CRLPATH_SYM +%token MASTER_SSL_KEY_SYM +%token MASTER_SSL_SYM +%token MASTER_SSL_VERIFY_SERVER_CERT_SYM +%token MASTER_SYM +%token MASTER_USER_SYM +%token MASTER_USE_GTID_SYM +%token MASTER_HEARTBEAT_PERIOD_SYM +%token MATCH /* SQL-2003-R */ +%token MAX_CONNECTIONS_PER_HOUR +%token MAX_QUERIES_PER_HOUR +%token MAX_ROWS +%token MAX_SIZE_SYM +%token MAX_SYM /* SQL-2003-N */ +%token MAX_UPDATES_PER_HOUR +%token MAX_STATEMENT_TIME_SYM +%token MAX_USER_CONNECTIONS_SYM +%token MAX_VALUE_SYM /* SQL-2003-N */ +%token MEDIUMBLOB +%token MEDIUMINT +%token MEDIUMTEXT +%token MEDIUM_SYM +%token MEMORY_SYM +%token MERGE_SYM /* SQL-2003-R */ +%token MESSAGE_TEXT_SYM /* SQL-2003-N */ +%token MICROSECOND_SYM /* MYSQL-FUNC */ +%token MIGRATE_SYM +%token MINUTE_MICROSECOND_SYM +%token MINUTE_SECOND_SYM +%token MINUTE_SYM /* SQL-2003-R */ +%token MIN_ROWS +%token MIN_SYM /* SQL-2003-N */ +%token MODE_SYM +%token MODIFIES_SYM /* SQL-2003-R */ +%token MODIFY_SYM +%token MOD_SYM /* SQL-2003-N */ +%token MONTH_SYM /* SQL-2003-R */ +%token MULTILINESTRING +%token MULTIPOINT +%token MULTIPOLYGON +%token MUTEX_SYM +%token MYSQL_SYM +%token MYSQL_ERRNO_SYM +%token NAMES_SYM /* SQL-2003-N */ +%token NAME_SYM /* SQL-2003-N */ +%token NATIONAL_SYM /* SQL-2003-R */ +%token NATURAL /* SQL-2003-R */ +%token NCHAR_STRING +%token NCHAR_SYM /* SQL-2003-R */ +%token NE /* OPERATOR */ +%token NEG +%token NEW_SYM /* SQL-2003-R */ +%token NEXT_SYM /* SQL-2003-N */ +%token NODEGROUP_SYM +%token NONE_SYM /* SQL-2003-R */ +%token NOT2_SYM +%token NOT_SYM /* SQL-2003-R */ +%token NOW_SYM +%token NO_SYM /* SQL-2003-R */ +%token NO_WAIT_SYM +%token NO_WRITE_TO_BINLOG +%token NTILE_SYM +%token NULL_SYM /* SQL-2003-R */ +%token NUM +%token NUMBER_SYM /* SQL-2003-N */ +%token NUMERIC_SYM /* SQL-2003-R */ +%token NTH_VALUE_SYM /* SQL-2011 */ +%token NVARCHAR_SYM +%token OFFSET_SYM +%token OLD_PASSWORD_SYM +%token ON /* SQL-2003-R */ +%token ONE_SYM +%token ONLY_SYM /* SQL-2003-R */ +%token ONLINE_SYM +%token OPEN_SYM /* SQL-2003-R */ +%token OPTIMIZE +%token OPTIONS_SYM +%token OPTION /* SQL-2003-N */ +%token OPTIONALLY +%token OR2_SYM +%token ORDER_SYM /* SQL-2003-R */ +%token OR_OR_SYM /* OPERATOR */ +%token OR_SYM /* SQL-2003-R */ +%token OTHERS_SYM /* SQL-2011-N */ +%token OUTER +%token OUTFILE +%token OUT_SYM /* SQL-2003-R */ +%token OVER_SYM +%token OWNER_SYM +%token PACK_KEYS_SYM +%token PAGE_SYM +%token PAGE_CHECKSUM_SYM +%token PARAM_MARKER +%token PARSER_SYM +%token PARSE_VCOL_EXPR_SYM +%token PARTIAL /* SQL-2003-N */ +%token PARTITION_SYM /* SQL-2003-R */ +%token PARTITIONS_SYM +%token PARTITIONING_SYM +%token PASSWORD_SYM +%token PERCENT_RANK_SYM +%token PERSISTENT_SYM +%token PHASE_SYM +%token PLUGINS_SYM +%token PLUGIN_SYM +%token POINT_SYM +%token POLYGON +%token PORT_SYM +%token POSITION_SYM /* SQL-2003-N */ +%token PRECEDES_SYM /* MYSQL */ +%token PRECEDING_SYM /* SQL-2011-N */ +%token PRECISION /* SQL-2003-R */ +%token PREPARE_SYM /* SQL-2003-R */ +%token PRESERVE_SYM +%token PREV_SYM +%token PRIMARY_SYM /* SQL-2003-R */ +%token PRIVILEGES /* SQL-2003-N */ +%token PROCEDURE_SYM /* SQL-2003-R */ +%token PROCESS +%token PROCESSLIST_SYM +%token PROFILE_SYM +%token PROFILES_SYM +%token PROXY_SYM +%token PURGE +%token QUARTER_SYM +%token QUERY_SYM +%token QUICK +%token RANGE_SYM /* SQL-2003-R */ +%token RANK_SYM +%token READS_SYM /* SQL-2003-R */ +%token READ_ONLY_SYM +%token READ_SYM /* SQL-2003-N */ +%token READ_WRITE_SYM +%token REAL /* SQL-2003-R */ +%token REBUILD_SYM +%token RECOVER_SYM +%token RECURSIVE_SYM +%token REDOFILE_SYM +%token REDO_BUFFER_SIZE_SYM +%token REDUNDANT_SYM +%token REFERENCES /* SQL-2003-R */ +%token REGEXP +%token RELAY +%token RELAYLOG_SYM +%token RELAY_LOG_FILE_SYM +%token RELAY_LOG_POS_SYM +%token RELAY_THREAD +%token RELEASE_SYM /* SQL-2003-R */ +%token RELOAD +%token REMOVE_SYM +%token RENAME +%token REORGANIZE_SYM +%token REPAIR +%token REPEATABLE_SYM /* SQL-2003-N */ +%token REPEAT_SYM /* MYSQL-FUNC */ +%token REPLACE /* MYSQL-FUNC */ +%token REPLICATION +%token REQUIRE_SYM +%token RESET_SYM +%token RESIGNAL_SYM /* SQL-2003-R */ +%token RESOURCES +%token RESTORE_SYM +%token RESTRICT +%token RESUME_SYM +%token RETURNED_SQLSTATE_SYM /* SQL-2003-N */ +%token RETURNING_SYM +%token RETURNS_SYM /* SQL-2003-R */ +%token RETURN_SYM /* SQL-2003-R */ +%token REVERSE_SYM +%token REVOKE /* SQL-2003-R */ +%token RIGHT /* SQL-2003-R */ +%token ROLE_SYM +%token ROLLBACK_SYM /* SQL-2003-R */ +%token ROLLUP_SYM /* SQL-2003-R */ +%token ROUTINE_SYM /* SQL-2003-N */ +%token ROW_SYM /* SQL-2003-R */ +%token ROWS_SYM /* SQL-2003-R */ +%token ROW_COUNT_SYM /* SQL-2003-N */ +%token ROW_FORMAT_SYM +%token ROW_NUMBER_SYM +%token RTREE_SYM +%token SAVEPOINT_SYM /* SQL-2003-R */ +%token SCHEDULE_SYM +%token SCHEMA_NAME_SYM /* SQL-2003-N */ +%token SECOND_MICROSECOND_SYM +%token SECOND_SYM /* SQL-2003-R */ +%token SECURITY_SYM /* SQL-2003-N */ +%token SELECT_SYM /* SQL-2003-R */ +%token SENSITIVE_SYM /* FUTURE-USE */ +%token SEPARATOR_SYM +%token SERIALIZABLE_SYM /* SQL-2003-N */ +%token SERIAL_SYM +%token SESSION_SYM /* SQL-2003-N */ +%token SERVER_SYM +%token SERVER_OPTIONS +%token SET /* SQL-2003-R */ +%token SET_VAR +%token SHARE_SYM +%token SHIFT_LEFT /* OPERATOR */ +%token SHIFT_RIGHT /* OPERATOR */ +%token SHOW +%token SHUTDOWN +%token SIGNAL_SYM /* SQL-2003-R */ +%token SIGNED_SYM +%token SIMPLE_SYM /* SQL-2003-N */ +%token SLAVE +%token SLAVES +%token SLAVE_POS_SYM +%token SLOW +%token SMALLINT /* SQL-2003-R */ +%token SNAPSHOT_SYM +%token SOCKET_SYM +%token SOFT_SYM +%token SONAME_SYM +%token SOUNDS_SYM +%token SOURCE_SYM +%token SPATIAL_SYM +%token SPECIFIC_SYM /* SQL-2003-R */ +%token SQLEXCEPTION_SYM /* SQL-2003-R */ +%token SQLSTATE_SYM /* SQL-2003-R */ +%token SQLWARNING_SYM /* SQL-2003-R */ +%token SQL_BIG_RESULT +%token SQL_BUFFER_RESULT +%token SQL_CACHE_SYM +%token SQL_CALC_FOUND_ROWS +%token SQL_NO_CACHE_SYM +%token SQL_SMALL_RESULT +%token SQL_SYM /* SQL-2003-R */ +%token SQL_THREAD +%token REF_SYSTEM_ID_SYM +%token SSL_SYM +%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 +%token STATUS_SYM +%token STDDEV_SAMP_SYM /* SQL-2003-N */ +%token STD_SYM +%token STOP_SYM +%token STORAGE_SYM +%token STORED_SYM +%token STRAIGHT_JOIN +%token STRING_SYM +%token SUBCLASS_ORIGIN_SYM /* SQL-2003-N */ +%token SUBDATE_SYM +%token SUBJECT_SYM +%token SUBPARTITIONS_SYM +%token SUBPARTITION_SYM +%token SUBSTRING /* SQL-2003-N */ +%token SUM_SYM /* SQL-2003-N */ +%token SUPER_SYM +%token SUSPEND_SYM +%token SWAPS_SYM +%token SWITCHES_SYM +%token SYSDATE +%token TABLES +%token TABLESPACE +%token TABLE_REF_PRIORITY +%token TABLE_SYM /* SQL-2003-R */ +%token TABLE_CHECKSUM_SYM +%token TABLE_NAME_SYM /* SQL-2003-N */ +%token TEMPORARY /* SQL-2003-N */ +%token TEMPTABLE_SYM +%token TERMINATED +%token TEXT_STRING +%token TEXT_SYM +%token THAN_SYM +%token THEN_SYM /* SQL-2003-R */ +%token TIES_SYM /* SQL-2011-N */ +%token TIMESTAMP /* SQL-2003-R */ +%token TIMESTAMP_ADD +%token TIMESTAMP_DIFF +%token TIME_SYM /* SQL-2003-R */ +%token TINYBLOB +%token TINYINT +%token TINYTEXT +%token TO_SYM /* SQL-2003-R */ +%token TRAILING /* SQL-2003-R */ +%token TRANSACTION_SYM +%token TRANSACTIONAL_SYM +%token TRIGGERS_SYM +%token TRIGGER_SYM /* SQL-2003-R */ +%token TRIM /* SQL-2003-N */ +%token TRUE_SYM /* SQL-2003-R */ +%token TRUNCATE_SYM +%token TYPES_SYM +%token TYPE_SYM /* SQL-2003-N */ +%token UDF_RETURNS_SYM +%token ULONGLONG_NUM +%token UNBOUNDED_SYM /* SQL-2011-N */ +%token UNCOMMITTED_SYM /* SQL-2003-N */ +%token UNDEFINED_SYM +%token UNDERSCORE_CHARSET +%token UNDOFILE_SYM +%token UNDO_BUFFER_SIZE_SYM +%token UNDO_SYM /* FUTURE-USE */ +%token UNICODE_SYM +%token UNINSTALL_SYM +%token UNION_SYM /* SQL-2003-R */ +%token UNIQUE_SYM +%token UNKNOWN_SYM /* SQL-2003-R */ +%token UNLOCK_SYM +%token UNSIGNED +%token UNTIL_SYM +%token UPDATE_SYM /* SQL-2003-R */ +%token UPGRADE_SYM +%token USAGE /* SQL-2003-N */ +%token USER_SYM /* SQL-2003-R */ +%token USE_FRM +%token USE_SYM +%token USING /* SQL-2003-R */ +%token UTC_DATE_SYM +%token UTC_TIMESTAMP_SYM +%token UTC_TIME_SYM +%token VALUES /* SQL-2003-R */ +%token VALUE_SYM /* SQL-2003-R */ +%token VARBINARY +%token VARCHAR /* SQL-2003-R */ +%token VARIABLES +%token VARIANCE_SYM +%token VARYING /* SQL-2003-R */ +%token VAR_SAMP_SYM +%token VIA_SYM +%token VIEW_SYM /* SQL-2003-N */ +%token VIRTUAL_SYM +%token WAIT_SYM +%token WARNINGS +%token WEEK_SYM +%token WEIGHT_STRING_SYM +%token WHEN_SYM /* SQL-2003-R */ +%token WHERE /* SQL-2003-R */ +%token WINDOW_SYM +%token WHILE_SYM +%token WITH /* SQL-2003-R */ +%token WITH_CUBE_SYM /* INTERNAL */ +%token WITH_ROLLUP_SYM /* INTERNAL */ +%token WORK_SYM /* SQL-2003-N */ +%token WRAPPER_SYM +%token WRITE_SYM /* SQL-2003-N */ +%token X509_SYM +%token XA_SYM +%token XML_SYM +%token XOR +%token YEAR_MONTH_SYM +%token YEAR_SYM /* SQL-2003-R */ +%token ZEROFILL + +%token IMPOSSIBLE_ACTION /* To avoid warning for yyerrlab1 */ + +%left JOIN_SYM INNER_SYM STRAIGHT_JOIN CROSS LEFT RIGHT +/* A dummy token to force the priority of table_ref production in a join. */ +%left TABLE_REF_PRIORITY +%left SET_VAR +%left OR_OR_SYM OR_SYM OR2_SYM +%left XOR +%left AND_SYM AND_AND_SYM +%left BETWEEN_SYM CASE_SYM WHEN_SYM THEN_SYM ELSE +%left '=' EQUAL_SYM GE '>' LE '<' NE IS LIKE REGEXP IN_SYM +%left '|' +%left '&' +%left SHIFT_LEFT SHIFT_RIGHT +%left '-' '+' +%left '*' '/' '%' DIV_SYM MOD_SYM +%left '^' +%left NEG '~' +%right NOT_SYM NOT2_SYM +%right BINARY COLLATE_SYM +%left INTERVAL_SYM + +%type <lex_str> + IDENT IDENT_QUOTED DECIMAL_NUM FLOAT_NUM NUM LONG_NUM + HEX_NUM HEX_STRING + LEX_HOSTNAME ULONGLONG_NUM field_ident select_alias ident ident_or_text + IDENT_sys TEXT_STRING_sys TEXT_STRING_literal + opt_component key_cache_name + sp_opt_label BIN_NUM label_ident TEXT_STRING_filesystem ident_or_empty + opt_constraint constraint opt_ident + +%type <lex_string_with_metadata> + TEXT_STRING + NCHAR_STRING + +%type <lex_str_ptr> + opt_table_alias + +%type <table> + table_ident table_ident_nodb references xid + table_ident_opt_wild create_like + +%type <simple_string> + remember_name remember_end opt_db remember_tok_start + wild_and_where + field_length opt_field_length opt_field_length_default_1 + +%type <const_simple_string> + opt_place + +%type <string> + text_string hex_or_bin_String opt_gconcat_separator + +%type <field_type> int_type real_type + +%type <Lex_field_type> type_with_opt_collate field_type + +%type <Lex_dyncol_type> opt_dyncol_type dyncol_type + numeric_dyncol_type temporal_dyncol_type string_dyncol_type + +%type <create_field> field_spec column_def + +%type <geom_type> spatial_type + +%type <num> + order_dir lock_option + udf_type opt_local opt_no_write_to_binlog + opt_temporary all_or_any opt_distinct + opt_ignore_leaves fulltext_options union_option + opt_not + 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_time_precision kill_type kill_option int_num + opt_default_time_precision + case_stmt_body opt_bin_mod + opt_if_exists_table_element opt_if_not_exists_table_element + opt_recursive + +%type <object_ddl_options> + create_or_replace + opt_if_not_exists + opt_if_exists + +/* + 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 + +%type <m_fk_option> + delete_option + +%type <ulong_num> + ulong_num real_ulong_num merge_insert_types + ws_nweights + ws_level_flag_desc ws_level_flag_reverse ws_level_flags + opt_ws_levels ws_level_list ws_level_list_item ws_level_number + ws_level_range ws_level_list_or_range + +%type <ulonglong_number> + ulonglong_num real_ulonglong_num size_number + +%type <choice> choice + +%type <lock_type> + replace_lock_option opt_low_priority insert_lock_option load_data_lock + +%type <item> + literal text_literal insert_ident order_ident temporal_literal + simple_ident expr opt_expr opt_else sum_expr in_sum_expr + variable variable_aux bool_pri + predicate bit_expr parenthesized_expr + table_wild simple_expr column_default_non_parenthesized_expr udf_expr + expr_or_default set_expr_or_default + geometry_function signed_literal expr_or_literal + opt_escape + sp_opt_default + simple_ident_nospvar simple_ident_q + field_or_var limit_option + part_func_expr + window_func_expr + window_func + simple_window_func + function_call_keyword + function_call_nonkeyword + function_call_generic + function_call_conflict kill_expr + signal_allowed_expr + simple_target_specification + condition_number + +%type <item_param> param_marker + +%type <item_num> + NUM_literal + +%type <item_list> + expr_list opt_udf_expr_list udf_expr_list when_list + ident_list ident_list_arg opt_expr_list + +%type <var_type> + option_type opt_var_type opt_var_ident_type + +%type <key_type> + opt_unique constraint_key_type fulltext spatial + +%type <key_alg> + btree_or_rtree opt_key_algorithm_clause opt_USING_key_algorithm + +%type <string_list> + using_list opt_use_partition use_partition + +%type <key_part> + key_part + +%type <table_list> + join_table_list join_table + table_factor table_ref esc_table_ref + table_primary_ident table_primary_derived + select_derived derived_table_list + select_derived_union + derived_query_specification +%type <date_time_type> date_time_type; +%type <interval> interval + +%type <interval_time_st> interval_time_stamp + +%type <db_type> storage_engines known_storage_engines + +%type <row_type> row_types + +%type <tx_isolation> isolation_types + +%type <ha_rkey_mode> handler_rkey_mode + +%type <Lex_cast_type> cast_type cast_type_numeric cast_type_temporal + +%type <Lex_length_and_dec> precision opt_precision float_options + +%type <symbol> keyword keyword_sp + +%type <lex_user> user grant_user grant_role user_or_role current_role + admin_option_for_role user_maybe_role + +%type <charset> + opt_collate + charset_name + charset_or_alias + charset_name_or_default + old_or_new_charset_name + old_or_new_charset_name_or_default + collation_name + collation_name_or_default + opt_load_data_charset + UNDERSCORE_CHARSET + +%type <variable> internal_variable_name + +%type <select_lex> subselect + get_select_lex get_select_lex_derived + query_specification + query_term_union_not_ready + query_term_union_ready + query_expression_body + select_paren_derived + +%type <boolfunc2creator> comp_op + +%type <dyncol_def> dyncall_create_element + +%type <dyncol_def_list> dyncall_create_list + +%type <myvar> select_outvar + +%type <virtual_column> opt_check_constraint check_constraint virtual_column_func + column_default_expr +%type <unit_type> unit_type_decl + +%type <NONE> + analyze_stmt_command + query verb_clause create change select do drop insert replace insert2 + insert_values update delete truncate rename compound_statement + show describe load alter optimize keycache preload flush + reset purge begin commit rollback savepoint release + slave master_def master_defs master_file_def slave_until_opts + repair analyze opt_with_admin opt_with_admin_option + analyze_table_list analyze_table_elem_spec + opt_persistent_stat_clause persistent_stat_spec + persistent_column_stat_spec persistent_index_stat_spec + table_column_list table_index_list table_index_name + check start checksum + field_list field_list_item kill key_def constraint_def + keycache_list keycache_list_or_parts assign_to_keycache + assign_to_keycache_parts + preload_list preload_list_or_parts preload_keys preload_keys_parts + select_item_list select_item values_list no_braces + opt_limit_clause delete_limit_clause fields opt_values values + procedure_list procedure_list2 procedure_item + field_def handler opt_generated_always + opt_ignore opt_column opt_restrict + grant revoke set lock unlock string_list field_options field_option + field_opt_list opt_binary table_lock_list table_lock + 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_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 user_and_role_list + rename_list table_or_tables + clear_privileges flush_options flush_option + opt_flush_lock flush_lock flush_options_list + equal optional_braces + opt_mi_check_type opt_to mi_check_types + table_to_table_list table_to_table opt_table_list opt_as + handler_rkey_function handler_read_or_scan + single_multi table_wild_list table_wild_one opt_wild + union_clause union_list + subselect_start opt_and charset + subselect_end select_var_list select_var_list_init help + opt_extended_describe shutdown + opt_format_json + prepare prepare_src execute deallocate + statement sp_suid + sp_c_chistics sp_a_chistics sp_chistic sp_c_chistic xa + opt_field_or_var_spec fields_or_vars opt_load_data_set_spec + view_algorithm view_or_trigger_or_sp_or_event + definer_tail no_definer_tail + view_suid view_tail view_list_opt view_list view_select + view_check_option trigger_tail sp_tail sf_tail event_tail + udf_tail udf_tail2 + install uninstall partition_entry binlog_base64_event + normal_key_options normal_key_opts all_key_opt + spatial_key_options fulltext_key_options normal_key_opt + fulltext_key_opt spatial_key_opt fulltext_key_opts spatial_key_opts + keep_gcc_happy + key_using_alg + part_column_list + server_def server_options_list server_option + definer_opt no_definer definer get_diagnostics + parse_vcol_expr vcol_opt_specifier vcol_opt_attribute + vcol_opt_attribute_list vcol_attribute + opt_serial_attribute opt_serial_attribute_list serial_attribute + explainable_command +END_OF_INPUT + +%type <NONE> call sp_proc_stmts sp_proc_stmts1 sp_proc_stmt +%type <NONE> sp_proc_stmt_statement sp_proc_stmt_return + sp_proc_stmt_in_returns_clause +%type <NONE> sp_proc_stmt_compound_ok +%type <NONE> sp_proc_stmt_if +%type <NONE> sp_labeled_control sp_unlabeled_control +%type <NONE> sp_labeled_block sp_unlabeled_block sp_unlabeled_block_not_atomic +%type <NONE> sp_proc_stmt_leave +%type <NONE> sp_proc_stmt_iterate +%type <NONE> sp_proc_stmt_open sp_proc_stmt_fetch sp_proc_stmt_close +%type <NONE> case_stmt_specification +%type <NONE> loop_body while_body repeat_body + +%type <num> sp_decl_idents sp_handler_type sp_hcond_list +%type <spcondvalue> sp_cond sp_hcond sqlstate signal_value opt_signal_value +%type <spblock> sp_decls sp_decl +%type <lex> sp_cursor_stmt +%type <spname> sp_name +%type <splabel> sp_block_content +%type <spvar> sp_param_name_and_type +%type <spvar_mode> sp_opt_inout +%type <index_hint> index_hint_type +%type <num> index_hint_clause normal_join inner_join +%type <filetype> data_or_xml + +%type <NONE> signal_stmt resignal_stmt +%type <diag_condition_item_name> signal_condition_information_item_name + +%type <trg_execution_order> trigger_follows_precedes_clause; +%type <trigger_action_order_type> trigger_action_order; + +%type <diag_area> which_area; +%type <diag_info> diagnostics_information; +%type <stmt_info_item> statement_information_item; +%type <stmt_info_item_name> statement_information_item_name; +%type <stmt_info_list> statement_information; +%type <cond_info_item> condition_information_item; +%type <cond_info_item_name> condition_information_item_name; +%type <cond_info_list> condition_information; + +%type <NONE> opt_window_clause window_def_list window_def window_spec +%type <lex_str_ptr> window_name +%type <NONE> opt_window_ref opt_window_frame_clause +%type <frame_units> window_frame_units; +%type <NONE> window_frame_extent; +%type <frame_exclusion> opt_window_frame_exclusion; +%type <window_frame_bound> window_frame_start window_frame_bound; + + +%type <NONE> + '-' '+' '*' '/' '%' '(' ')' + ',' '!' '{' '}' '&' '|' AND_SYM OR_SYM OR_OR_SYM BETWEEN_SYM CASE_SYM + THEN_SYM WHEN_SYM DIV_SYM MOD_SYM OR2_SYM AND_AND_SYM DELETE_SYM + ROLE_SYM + +%type <with_clause> opt_with_clause with_clause + +%type <lex_str_ptr> query_name + +%type <lex_str_list> opt_with_column_list + +%% + + +/* + Indentation of grammar rules: + +rule: <-- starts at col 1 + rule1a rule1b rule1c <-- starts at col 11 + { <-- starts at col 11 + code <-- starts at col 13, indentation is 2 spaces + } + | rule2a rule2b + { + code + } + ; <-- on a line by itself, starts at col 9 + + Also, please do not use any <TAB>, but spaces. + Having a uniform indentation in this file helps + code reviews, patches, merges, and make maintenance easier. + Tip: grep [[:cntrl:]] sql_yacc.yy + Thanks. +*/ + +query: + END_OF_INPUT + { + if (!thd->bootstrap && + (!(thd->lex->select_lex.options & OPTION_FOUND_COMMENT))) + my_yyabort_error((ER_EMPTY_QUERY, MYF(0))); + + thd->lex->sql_command= SQLCOM_EMPTY_QUERY; + YYLIP->found_semicolon= NULL; + } + | verb_clause + { + Lex_input_stream *lip = YYLIP; + + if ((thd->client_capabilities & CLIENT_MULTI_QUERIES) && + lip->multi_statements && + ! lip->eof()) + { + /* + We found a well formed query, and multi queries are allowed: + - force the parser to stop after the ';' + - mark the start of the next query for the next invocation + of the parser. + */ + lip->next_state= MY_LEX_END; + lip->found_semicolon= lip->get_ptr(); + } + else + { + /* Single query, terminated. */ + lip->found_semicolon= NULL; + } + } + ';' + opt_end_of_input + | verb_clause END_OF_INPUT + { + /* Single query, not terminated. */ + YYLIP->found_semicolon= NULL; + } + ; + +opt_end_of_input: + /* empty */ + | END_OF_INPUT + ; + +verb_clause: + statement + | begin + | compound_statement + ; + +/* Verb clauses, except begin and compound_statement */ +statement: + alter + | analyze + | analyze_stmt_command + | binlog_base64_event + | call + | change + | check + | checksum + | commit + | create + | deallocate + | delete + | describe + | do + | drop + | execute + | flush + | get_diagnostics + | grant + | handler + | help + | insert + | install + | keep_gcc_happy + | keycache + | kill + | load + | lock + | optimize + | parse_vcol_expr + | partition_entry + | preload + | prepare + | purge + | release + | rename + | repair + | replace + | reset + | resignal_stmt + | revoke + | rollback + | savepoint + | select + | set + | signal_stmt + | show + | shutdown + | slave + | start + | truncate + | uninstall + | unlock + | update + | use + | xa + ; + +deallocate: + deallocate_or_drop PREPARE_SYM ident + { + LEX *lex= thd->lex; + lex->sql_command= SQLCOM_DEALLOCATE_PREPARE; + lex->prepared_stmt_name= $3; + } + ; + +deallocate_or_drop: + DEALLOCATE_SYM + | DROP + ; + +prepare: + PREPARE_SYM ident FROM prepare_src + { + LEX *lex= thd->lex; + if (lex->table_or_sp_used()) + my_yyabort_error((ER_SUBQUERIES_NOT_SUPPORTED, MYF(0), + "PREPARE..FROM")); + lex->sql_command= SQLCOM_PREPARE; + lex->prepared_stmt_name= $2; + } + ; + +prepare_src: + { Lex->expr_allows_subselect= false; } + expr + { + Lex->prepared_stmt_code= $2; + Lex->expr_allows_subselect= true; + } + ; + +execute: + EXECUTE_SYM ident + { + LEX *lex= thd->lex; + lex->sql_command= SQLCOM_EXECUTE; + lex->prepared_stmt_name= $2; + } + execute_using + {} + | EXECUTE_SYM IMMEDIATE_SYM prepare_src + { + if (Lex->table_or_sp_used()) + my_yyabort_error((ER_SUBQUERIES_NOT_SUPPORTED, MYF(0), + "EXECUTE IMMEDIATE")); + Lex->sql_command= SQLCOM_EXECUTE_IMMEDIATE; + } + execute_using + {} + ; + +execute_using: + /* nothing */ + | USING { Lex->expr_allows_subselect= false; } + execute_var_list + { + if (Lex->table_or_sp_used()) + my_yyabort_error((ER_SUBQUERIES_NOT_SUPPORTED, MYF(0), + "EXECUTE..USING")); + Lex->expr_allows_subselect= true; + } + ; + +execute_var_list: + execute_var_list ',' execute_var_ident + | execute_var_ident + ; + +execute_var_ident: + expr_or_default + { + if (Lex->prepared_stmt_params.push_back($1, thd->mem_root)) + MYSQL_YYABORT; + } + ; + +/* help */ + +help: + HELP_SYM + { + if (Lex->sphead) + my_yyabort_error((ER_SP_BADSTATEMENT, MYF(0), "HELP")); + } + ident_or_text + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_HELP; + lex->help_arg= $3.str; + } + ; + +/* change master */ + +change: + CHANGE MASTER_SYM optional_connection_name TO_SYM + { + Lex->sql_command = SQLCOM_CHANGE_MASTER; + } + master_defs + {} + ; + +master_defs: + master_def + | master_defs ',' master_def + ; + +master_def: + MASTER_HOST_SYM '=' TEXT_STRING_sys + { + Lex->mi.host = $3.str; + } + | MASTER_USER_SYM '=' TEXT_STRING_sys + { + Lex->mi.user = $3.str; + } + | MASTER_PASSWORD_SYM '=' TEXT_STRING_sys + { + Lex->mi.password = $3.str; + } + | MASTER_PORT_SYM '=' ulong_num + { + Lex->mi.port = $3; + } + | MASTER_CONNECT_RETRY_SYM '=' ulong_num + { + Lex->mi.connect_retry = $3; + } + | MASTER_DELAY_SYM '=' ulong_num + { + if ($3 > MASTER_DELAY_MAX) + { + my_error(ER_MASTER_DELAY_VALUE_OUT_OF_RANGE, MYF(0), + $3, MASTER_DELAY_MAX); + } + else + Lex->mi.sql_delay = $3; + } + | MASTER_SSL_SYM '=' ulong_num + { + Lex->mi.ssl= $3 ? + LEX_MASTER_INFO::LEX_MI_ENABLE : LEX_MASTER_INFO::LEX_MI_DISABLE; + } + | MASTER_SSL_CA_SYM '=' TEXT_STRING_sys + { + Lex->mi.ssl_ca= $3.str; + } + | MASTER_SSL_CAPATH_SYM '=' TEXT_STRING_sys + { + Lex->mi.ssl_capath= $3.str; + } + | MASTER_SSL_CERT_SYM '=' TEXT_STRING_sys + { + Lex->mi.ssl_cert= $3.str; + } + | MASTER_SSL_CIPHER_SYM '=' TEXT_STRING_sys + { + Lex->mi.ssl_cipher= $3.str; + } + | MASTER_SSL_KEY_SYM '=' TEXT_STRING_sys + { + Lex->mi.ssl_key= $3.str; + } + | MASTER_SSL_VERIFY_SERVER_CERT_SYM '=' ulong_num + { + Lex->mi.ssl_verify_server_cert= $3 ? + LEX_MASTER_INFO::LEX_MI_ENABLE : LEX_MASTER_INFO::LEX_MI_DISABLE; + } + | MASTER_SSL_CRL_SYM '=' TEXT_STRING_sys + { + Lex->mi.ssl_crl= $3.str; + } + | MASTER_SSL_CRLPATH_SYM '=' TEXT_STRING_sys + { + Lex->mi.ssl_crlpath= $3.str; + } + + | MASTER_HEARTBEAT_PERIOD_SYM '=' NUM_literal + { + Lex->mi.heartbeat_period= (float) $3->val_real(); + if (Lex->mi.heartbeat_period > SLAVE_MAX_HEARTBEAT_PERIOD || + Lex->mi.heartbeat_period < 0.0) + my_yyabort_error((ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE, MYF(0), + SLAVE_MAX_HEARTBEAT_PERIOD)); + + if (Lex->mi.heartbeat_period > slave_net_timeout) + { + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE_MAX, + ER_THD(thd, ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE_MAX)); + } + if (Lex->mi.heartbeat_period < 0.001) + { + if (Lex->mi.heartbeat_period != 0.0) + { + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE_MIN, + ER_THD(thd, ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE_MIN)); + Lex->mi.heartbeat_period= 0.0; + } + Lex->mi.heartbeat_opt= LEX_MASTER_INFO::LEX_MI_DISABLE; + } + Lex->mi.heartbeat_opt= LEX_MASTER_INFO::LEX_MI_ENABLE; + } + | IGNORE_SERVER_IDS_SYM '=' '(' ignore_server_id_list ')' + { + Lex->mi.repl_ignore_server_ids_opt= LEX_MASTER_INFO::LEX_MI_ENABLE; + } + | DO_DOMAIN_IDS_SYM '=' '(' do_domain_id_list ')' + { + Lex->mi.repl_do_domain_ids_opt= LEX_MASTER_INFO::LEX_MI_ENABLE; + } + | IGNORE_DOMAIN_IDS_SYM '=' '(' ignore_domain_id_list ')' + { + Lex->mi.repl_ignore_domain_ids_opt= LEX_MASTER_INFO::LEX_MI_ENABLE; + } + | + master_file_def + ; + +ignore_server_id_list: + /* Empty */ + | ignore_server_id + | ignore_server_id_list ',' ignore_server_id + ; + +ignore_server_id: + ulong_num + { + insert_dynamic(&Lex->mi.repl_ignore_server_ids, (uchar*) &($1)); + } + ; + +do_domain_id_list: + /* Empty */ + | do_domain_id + | do_domain_id_list ',' do_domain_id + ; + +do_domain_id: + ulong_num + { + insert_dynamic(&Lex->mi.repl_do_domain_ids, (uchar*) &($1)); + } + ; + +ignore_domain_id_list: + /* Empty */ + | ignore_domain_id + | ignore_domain_id_list ',' ignore_domain_id + ; + +ignore_domain_id: + ulong_num + { + insert_dynamic(&Lex->mi.repl_ignore_domain_ids, (uchar*) &($1)); + } + ; + +master_file_def: + MASTER_LOG_FILE_SYM '=' TEXT_STRING_sys + { + Lex->mi.log_file_name = $3.str; + } + | MASTER_LOG_POS_SYM '=' ulonglong_num + { + /* + If the user specified a value < BIN_LOG_HEADER_SIZE, adjust it + instead of causing subsequent errors. + We need to do it in this file, because only there we know that + MASTER_LOG_POS has been explicitly specified. On the contrary + in change_master() (sql_repl.cc) we cannot distinguish between 0 + (MASTER_LOG_POS explicitly specified as 0) and 0 (unspecified), + whereas we want to distinguish (specified 0 means "read the binlog + from 0" (4 in fact), unspecified means "don't change the position + (keep the preceding value)"). + */ + Lex->mi.pos= MY_MAX(BIN_LOG_HEADER_SIZE, $3); + } + | RELAY_LOG_FILE_SYM '=' TEXT_STRING_sys + { + Lex->mi.relay_log_name = $3.str; + } + | RELAY_LOG_POS_SYM '=' ulong_num + { + Lex->mi.relay_log_pos = $3; + /* Adjust if < BIN_LOG_HEADER_SIZE (same comment as Lex->mi.pos) */ + Lex->mi.relay_log_pos= MY_MAX(BIN_LOG_HEADER_SIZE, Lex->mi.relay_log_pos); + } + | MASTER_USE_GTID_SYM '=' CURRENT_POS_SYM + { + if (Lex->mi.use_gtid_opt != LEX_MASTER_INFO::LEX_GTID_UNCHANGED) + my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "MASTER_use_gtid")); + Lex->mi.use_gtid_opt= LEX_MASTER_INFO::LEX_GTID_CURRENT_POS; + } + | MASTER_USE_GTID_SYM '=' SLAVE_POS_SYM + { + if (Lex->mi.use_gtid_opt != LEX_MASTER_INFO::LEX_GTID_UNCHANGED) + my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "MASTER_use_gtid")); + Lex->mi.use_gtid_opt= LEX_MASTER_INFO::LEX_GTID_SLAVE_POS; + } + | MASTER_USE_GTID_SYM '=' NO_SYM + { + if (Lex->mi.use_gtid_opt != LEX_MASTER_INFO::LEX_GTID_UNCHANGED) + my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "MASTER_use_gtid")); + Lex->mi.use_gtid_opt= LEX_MASTER_INFO::LEX_GTID_NO; + } + ; + +optional_connection_name: + /* empty */ + { + LEX *lex= thd->lex; + lex->mi.connection_name= null_lex_str; + } + | connection_name + ; + +connection_name: + TEXT_STRING_sys + { + Lex->mi.connection_name= $1; +#ifdef HAVE_REPLICATION + if (check_master_connection_name(&$1)) + my_yyabort_error((ER_WRONG_ARGUMENTS, MYF(0), "MASTER_CONNECTION_NAME")); +#endif + } + ; + +/* create a table */ + +create: + create_or_replace opt_temporary TABLE_SYM opt_if_not_exists table_ident + { + LEX *lex= thd->lex; + lex->create_info.init(); + if (lex->set_command_with_check(SQLCOM_CREATE_TABLE, $2, $1 | $4)) + MYSQL_YYABORT; + if (!lex->select_lex.add_table_to_list(thd, $5, NULL, + TL_OPTION_UPDATING, + TL_WRITE, MDL_EXCLUSIVE)) + MYSQL_YYABORT; + lex->alter_info.reset(); + /* + For CREATE TABLE we should not open the table even if it exists. + If the table exists, we should either not create it or replace it + */ + lex->query_tables->open_strategy= TABLE_LIST::OPEN_STUB; + lex->create_info.default_table_charset= NULL; + lex->name= null_lex_str; + lex->create_last_non_select_table= lex->last_table(); + } + create_body + { + LEX *lex= thd->lex; + lex->current_select= &lex->select_lex; + if ((lex->create_info.used_fields & HA_CREATE_USED_ENGINE) && + !lex->create_info.db_type) + { + lex->create_info.use_default_db_type(thd); + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_WARN_USING_OTHER_HANDLER, + ER_THD(thd, ER_WARN_USING_OTHER_HANDLER), + hton_name(lex->create_info.db_type)->str, + $5->table.str); + } + create_table_set_open_action_and_adjust_tables(lex); + } + | create_or_replace opt_unique INDEX_SYM opt_if_not_exists ident + opt_key_algorithm_clause + ON table_ident + { + if (Lex->add_create_index_prepare($8)) + MYSQL_YYABORT; + if (Lex->add_create_index($2, $5, $6, $1 | $4)) + MYSQL_YYABORT; + } + '(' key_list ')' normal_key_options + opt_index_lock_algorithm { } + | create_or_replace fulltext INDEX_SYM opt_if_not_exists ident + ON table_ident + { + if (Lex->add_create_index_prepare($7)) + MYSQL_YYABORT; + if (Lex->add_create_index($2, $5, HA_KEY_ALG_UNDEF, $1 | $4)) + MYSQL_YYABORT; + } + '(' key_list ')' fulltext_key_options + opt_index_lock_algorithm { } + | create_or_replace spatial INDEX_SYM opt_if_not_exists ident + ON table_ident + { + if (Lex->add_create_index_prepare($7)) + MYSQL_YYABORT; + if (Lex->add_create_index($2, $5, HA_KEY_ALG_UNDEF, $1 | $4)) + MYSQL_YYABORT; + } + '(' key_list ')' spatial_key_options + opt_index_lock_algorithm { } + | create_or_replace DATABASE opt_if_not_exists ident + { + Lex->create_info.default_table_charset= NULL; + Lex->create_info.used_fields= 0; + } + opt_create_database_options + { + LEX *lex=Lex; + if (lex->set_command_with_check(SQLCOM_CREATE_DB, 0, $1 | $3)) + MYSQL_YYABORT; + lex->name= $4; + } + | create_or_replace + { + Lex->create_info.set($1); + Lex->create_view_mode= ($1.or_replace() ? VIEW_CREATE_OR_REPLACE : + VIEW_CREATE_NEW); + Lex->create_view_algorithm= DTYPE_ALGORITHM_UNDEFINED; + Lex->create_view_suid= TRUE; + } + view_or_trigger_or_sp_or_event { } + | create_or_replace USER_SYM opt_if_not_exists clear_privileges grant_list + opt_require_clause opt_resource_options + { + if (Lex->set_command_with_check(SQLCOM_CREATE_USER, $1 | $3)) + MYSQL_YYABORT; + } + | create_or_replace ROLE_SYM opt_if_not_exists + clear_privileges role_list opt_with_admin + { + if (Lex->set_command_with_check(SQLCOM_CREATE_ROLE, $1 | $3)) + MYSQL_YYABORT; + } + | CREATE LOGFILE_SYM GROUP_SYM logfile_group_info + { + Lex->alter_tablespace_info->ts_cmd_type= CREATE_LOGFILE_GROUP; + } + | CREATE TABLESPACE tablespace_info + { + Lex->alter_tablespace_info->ts_cmd_type= CREATE_TABLESPACE; + } + | create_or_replace { Lex->set_command(SQLCOM_CREATE_SERVER, $1); } + server_def + { } + ; + +server_def: + SERVER_SYM opt_if_not_exists ident_or_text + { + if (Lex->add_create_options_with_check($2)) + MYSQL_YYABORT; + Lex->server_options.reset($3); + } + FOREIGN DATA_SYM WRAPPER_SYM ident_or_text + OPTIONS_SYM '(' server_options_list ')' + { Lex->server_options.scheme= $8; } + ; + +server_options_list: + server_option + | server_options_list ',' server_option + ; + +server_option: + USER_SYM TEXT_STRING_sys + { + MYSQL_YYABORT_UNLESS(Lex->server_options.username.str == 0); + Lex->server_options.username= $2; + } + | HOST_SYM TEXT_STRING_sys + { + MYSQL_YYABORT_UNLESS(Lex->server_options.host.str == 0); + Lex->server_options.host= $2; + my_casedn_str(system_charset_info, Lex->server_options.host.str); + } + | DATABASE TEXT_STRING_sys + { + MYSQL_YYABORT_UNLESS(Lex->server_options.db.str == 0); + Lex->server_options.db= $2; + } + | OWNER_SYM TEXT_STRING_sys + { + MYSQL_YYABORT_UNLESS(Lex->server_options.owner.str == 0); + Lex->server_options.owner= $2; + } + | PASSWORD_SYM TEXT_STRING_sys + { + MYSQL_YYABORT_UNLESS(Lex->server_options.password.str == 0); + Lex->server_options.password= $2; + } + | SOCKET_SYM TEXT_STRING_sys + { + MYSQL_YYABORT_UNLESS(Lex->server_options.socket.str == 0); + Lex->server_options.socket= $2; + } + | PORT_SYM ulong_num + { + Lex->server_options.port= $2; + } + ; + +event_tail: + remember_name EVENT_SYM opt_if_not_exists sp_name + { + LEX *lex=Lex; + + lex->stmt_definition_begin= $1; + if (lex->add_create_options_with_check($3)) + MYSQL_YYABORT; + if (!(lex->event_parse_data= Event_parse_data::new_instance(thd))) + MYSQL_YYABORT; + lex->event_parse_data->identifier= $4; + lex->event_parse_data->on_completion= + Event_parse_data::ON_COMPLETION_DROP; + + lex->sql_command= SQLCOM_CREATE_EVENT; + /* We need that for disallowing subqueries */ + } + ON SCHEDULE_SYM ev_schedule_time + opt_ev_on_completion + opt_ev_status + opt_ev_comment + DO_SYM ev_sql_stmt + { + /* + sql_command is set here because some rules in ev_sql_stmt + can overwrite it + */ + Lex->sql_command= SQLCOM_CREATE_EVENT; + } + ; + +ev_schedule_time: + EVERY_SYM expr interval + { + Lex->event_parse_data->item_expression= $2; + Lex->event_parse_data->interval= $3; + } + ev_starts + ev_ends + | AT_SYM expr + { + Lex->event_parse_data->item_execute_at= $2; + } + ; + +opt_ev_status: + /* empty */ { $$= 0; } + | ENABLE_SYM + { + Lex->event_parse_data->status= Event_parse_data::ENABLED; + Lex->event_parse_data->status_changed= true; + $$= 1; + } + | DISABLE_SYM ON SLAVE + { + Lex->event_parse_data->status= Event_parse_data::SLAVESIDE_DISABLED; + Lex->event_parse_data->status_changed= true; + $$= 1; + } + | DISABLE_SYM + { + Lex->event_parse_data->status= Event_parse_data::DISABLED; + Lex->event_parse_data->status_changed= true; + $$= 1; + } + ; + +ev_starts: + /* empty */ + { + Item *item= new (thd->mem_root) Item_func_now_local(thd, 0); + if (item == NULL) + MYSQL_YYABORT; + Lex->event_parse_data->item_starts= item; + } + | STARTS_SYM expr + { + Lex->event_parse_data->item_starts= $2; + } + ; + +ev_ends: + /* empty */ + | ENDS_SYM expr + { + Lex->event_parse_data->item_ends= $2; + } + ; + +opt_ev_on_completion: + /* empty */ { $$= 0; } + | ev_on_completion + ; + +ev_on_completion: + ON COMPLETION_SYM opt_not PRESERVE_SYM + { + Lex->event_parse_data->on_completion= $3 + ? Event_parse_data::ON_COMPLETION_DROP + : Event_parse_data::ON_COMPLETION_PRESERVE; + $$= 1; + } + ; + +opt_ev_comment: + /* empty */ { $$= 0; } + | COMMENT_SYM TEXT_STRING_sys + { + Lex->comment= Lex->event_parse_data->comment= $2; + $$= 1; + } + ; + +ev_sql_stmt: + { + LEX *lex= thd->lex; + Lex_input_stream *lip= YYLIP; + + /* + This stops the following : + - CREATE EVENT ... DO CREATE EVENT ...; + - ALTER EVENT ... DO CREATE EVENT ...; + - CREATE EVENT ... DO ALTER EVENT DO ....; + - CREATE PROCEDURE ... BEGIN CREATE EVENT ... END| + This allows: + - CREATE EVENT ... DO DROP EVENT yyy; + - CREATE EVENT ... DO ALTER EVENT yyy; + (the nested ALTER EVENT can have anything but DO clause) + - ALTER EVENT ... DO ALTER EVENT yyy; + (the nested ALTER EVENT can have anything but DO clause) + - ALTER EVENT ... DO DROP EVENT yyy; + - CREATE PROCEDURE ... BEGIN ALTER EVENT ... END| + (the nested ALTER EVENT can have anything but DO clause) + - CREATE PROCEDURE ... BEGIN DROP EVENT ... END| + */ + if (lex->sphead) + my_yyabort_error((ER_EVENT_RECURSION_FORBIDDEN, MYF(0))); + + if (!make_sp_head(thd, lex->event_parse_data->identifier, TYPE_ENUM_PROCEDURE)) + MYSQL_YYABORT; + + lex->sp_chistics.suid= SP_IS_SUID; //always the definer! + lex->sphead->set_body_start(thd, lip->get_cpp_ptr()); + } + sp_proc_stmt + { + LEX *lex= thd->lex; + + /* return back to the original memory root ASAP */ + lex->sphead->set_stmt_end(thd); + lex->sphead->restore_thd_mem_root(thd); + + lex->event_parse_data->body_changed= TRUE; + } + ; + +clear_privileges: + /* Nothing */ + { + LEX *lex=Lex; + lex->users_list.empty(); + lex->columns.empty(); + lex->grant= lex->grant_tot_col= 0; + lex->all_privileges= 0; + lex->select_lex.db= 0; + lex->ssl_type= SSL_TYPE_NOT_SPECIFIED; + lex->ssl_cipher= lex->x509_subject= lex->x509_issuer= 0; + bzero((char *)&(lex->mqh),sizeof(lex->mqh)); + } + ; + +sp_name: + ident '.' ident + { + if (!$1.str || check_db_name(&$1)) + my_yyabort_error((ER_WRONG_DB_NAME, MYF(0), $1.str)); + if (check_routine_name(&$3)) + MYSQL_YYABORT; + $$= new (thd->mem_root) sp_name($1, $3, true); + if ($$ == NULL) + MYSQL_YYABORT; + } + | ident + { + LEX *lex= thd->lex; + LEX_STRING db; + if (check_routine_name(&$1)) + { + MYSQL_YYABORT; + } + if (lex->copy_db_to(&db.str, &db.length)) + MYSQL_YYABORT; + $$= new (thd->mem_root) sp_name(db, $1, false); + if ($$ == NULL) + MYSQL_YYABORT; + } + ; + +sp_a_chistics: + /* Empty */ {} + | sp_a_chistics sp_chistic {} + ; + +sp_c_chistics: + /* Empty */ {} + | sp_c_chistics sp_c_chistic {} + ; + +/* Characteristics for both create and alter */ +sp_chistic: + COMMENT_SYM TEXT_STRING_sys + { Lex->sp_chistics.comment= $2; } + | LANGUAGE_SYM SQL_SYM + { /* Just parse it, we only have one language for now. */ } + | NO_SYM SQL_SYM + { Lex->sp_chistics.daccess= SP_NO_SQL; } + | CONTAINS_SYM SQL_SYM + { Lex->sp_chistics.daccess= SP_CONTAINS_SQL; } + | READS_SYM SQL_SYM DATA_SYM + { Lex->sp_chistics.daccess= SP_READS_SQL_DATA; } + | MODIFIES_SYM SQL_SYM DATA_SYM + { Lex->sp_chistics.daccess= SP_MODIFIES_SQL_DATA; } + | sp_suid + {} + ; + +/* Create characteristics */ +sp_c_chistic: + sp_chistic { } + | opt_not DETERMINISTIC_SYM { Lex->sp_chistics.detistic= ! $1; } + ; + +sp_suid: + SQL_SYM SECURITY_SYM DEFINER_SYM + { + Lex->sp_chistics.suid= SP_IS_SUID; + } + | SQL_SYM SECURITY_SYM INVOKER_SYM + { + Lex->sp_chistics.suid= SP_IS_NOT_SUID; + } + ; + +call: + CALL_SYM sp_name + { + LEX *lex = Lex; + + lex->sql_command= SQLCOM_CALL; + lex->spname= $2; + lex->value_list.empty(); + sp_add_used_routine(lex, thd, $2, TYPE_ENUM_PROCEDURE); + } + opt_sp_cparam_list {} + ; + +/* CALL parameters */ +opt_sp_cparam_list: + /* Empty */ + | '(' opt_sp_cparams ')' + ; + +opt_sp_cparams: + /* Empty */ + | sp_cparams + ; + +sp_cparams: + sp_cparams ',' expr + { + Lex->value_list.push_back($3, thd->mem_root); + } + | expr + { + Lex->value_list.push_back($1, thd->mem_root); + } + ; + +/* Stored FUNCTION parameter declaration list */ +sp_fdparam_list: + /* Empty */ + | sp_fdparams + ; + +sp_fdparams: + sp_fdparams ',' sp_param_name_and_type + | sp_param_name_and_type + ; + +sp_param_name_and_type: + ident + { + LEX *lex= Lex; + sp_pcontext *spc= lex->spcont; + + if (spc->find_variable($1, TRUE)) + my_yyabort_error((ER_SP_DUP_PARAM, MYF(0), $1.str)); + + sp_variable *spvar= spc->add_variable(thd, $1); + + lex->init_last_field(&spvar->field_def, $1.str, + thd->variables.collation_database); + $<spvar>$= spvar; + } + type_with_opt_collate + { + LEX *lex= Lex; + sp_variable *spvar= $<spvar>2; + + if (lex->sphead->fill_field_definition(thd, lex->last_field)) + { + MYSQL_YYABORT; + } + spvar->field_def.field_name= spvar->name.str; + spvar->field_def.pack_flag |= FIELDFLAG_MAYBE_NULL; + + $$= spvar; + } + ; + +/* Stored PROCEDURE parameter declaration list */ +sp_pdparam_list: + /* Empty */ + | sp_pdparams + ; + +sp_pdparams: + sp_pdparams ',' sp_pdparam + | sp_pdparam + ; + +sp_pdparam: + sp_opt_inout sp_param_name_and_type { $2->mode=$1; } + ; + +sp_opt_inout: + /* Empty */ { $$= sp_variable::MODE_IN; } + | IN_SYM { $$= sp_variable::MODE_IN; } + | OUT_SYM { $$= sp_variable::MODE_OUT; } + | INOUT_SYM { $$= sp_variable::MODE_INOUT; } + ; + +sp_proc_stmts: + /* Empty */ {} + | sp_proc_stmts sp_proc_stmt ';' + ; + +sp_proc_stmts1: + sp_proc_stmt ';' {} + | sp_proc_stmts1 sp_proc_stmt ';' + ; + +sp_decls: + /* Empty */ + { + $$.vars= $$.conds= $$.hndlrs= $$.curs= 0; + } + | sp_decls sp_decl ';' + { + /* We check for declarations out of (standard) order this way + because letting the grammar rules reflect it caused tricky + shift/reduce conflicts with the wrong result. (And we get + better error handling this way.) */ + if (($2.vars || $2.conds) && ($1.curs || $1.hndlrs)) + my_yyabort_error((ER_SP_VARCOND_AFTER_CURSHNDLR, MYF(0))); + if ($2.curs && $1.hndlrs) + my_yyabort_error((ER_SP_CURSOR_AFTER_HANDLER, MYF(0))); + $$.vars= $1.vars + $2.vars; + $$.conds= $1.conds + $2.conds; + $$.hndlrs= $1.hndlrs + $2.hndlrs; + $$.curs= $1.curs + $2.curs; + } + ; + +sp_decl: + DECLARE_SYM sp_decl_idents + { + LEX *lex= Lex; + sp_pcontext *pctx= lex->spcont; + + // get the last variable: + uint num_vars= pctx->context_var_count(); + uint var_idx= pctx->var_context2runtime(num_vars - 1); + sp_variable *spvar= pctx->find_variable(var_idx); + + lex->sphead->reset_lex(thd); + pctx->declare_var_boundary($2); + thd->lex->init_last_field(&spvar->field_def, spvar->name.str, + thd->variables.collation_database); + } + type_with_opt_collate + sp_opt_default + { + LEX *lex= Lex; + sp_pcontext *pctx= lex->spcont; + uint num_vars= pctx->context_var_count(); + Item *dflt_value_item= $5; + + if (!dflt_value_item) + { + dflt_value_item= new (thd->mem_root) Item_null(thd); + if (dflt_value_item == NULL) + MYSQL_YYABORT; + /* QQ Set to the var_type with null_value? */ + } + + for (uint i = num_vars-$2 ; i < num_vars ; i++) + { + uint var_idx= pctx->var_context2runtime(i); + sp_variable *spvar= pctx->find_variable(var_idx); + bool last= i == num_vars - 1; + + if (!spvar) + MYSQL_YYABORT; + + if (!last) + spvar->field_def= *lex->last_field; + + spvar->default_value= dflt_value_item; + spvar->field_def.field_name= spvar->name.str; + + if (lex->sphead->fill_field_definition(thd, &spvar->field_def)) + { + MYSQL_YYABORT; + } + + spvar->field_def.pack_flag |= FIELDFLAG_MAYBE_NULL; + + /* The last instruction is responsible for freeing LEX. */ + + sp_instr_set *is= new (lex->thd->mem_root) + sp_instr_set(lex->sphead->instructions(), + pctx, var_idx, dflt_value_item, + $4.field_type(), lex, last); + if (is == NULL || lex->sphead->add_instr(is)) + MYSQL_YYABORT; + } + + pctx->declare_var_boundary(0); + if (lex->sphead->restore_lex(thd)) + MYSQL_YYABORT; + $$.vars= $2; + $$.conds= $$.hndlrs= $$.curs= 0; + } + | DECLARE_SYM ident CONDITION_SYM FOR_SYM sp_cond + { + LEX *lex= Lex; + sp_pcontext *spc= lex->spcont; + + if (spc->find_condition($2, TRUE)) + my_yyabort_error((ER_SP_DUP_COND, MYF(0), $2.str)); + if(spc->add_condition(thd, $2, $5)) + MYSQL_YYABORT; + $$.vars= $$.hndlrs= $$.curs= 0; + $$.conds= 1; + } + | DECLARE_SYM sp_handler_type HANDLER_SYM FOR_SYM + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + + sp_handler *h= lex->spcont->add_handler(thd, + (sp_handler::enum_type) $2); + + lex->spcont= lex->spcont->push_context(thd, + sp_pcontext::HANDLER_SCOPE); + + sp_pcontext *ctx= lex->spcont; + sp_instr_hpush_jump *i= + new (thd->mem_root) sp_instr_hpush_jump(sp->instructions(), + ctx, h); + + if (i == NULL || sp->add_instr(i)) + MYSQL_YYABORT; + + /* For continue handlers, mark end of handler scope. */ + if ($2 == sp_handler::CONTINUE && + sp->push_backpatch(thd, i, ctx->last_label())) + MYSQL_YYABORT; + + if (sp->push_backpatch(thd, i, ctx->push_label(thd, empty_lex_str, 0))) + MYSQL_YYABORT; + } + sp_hcond_list sp_proc_stmt + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + sp_pcontext *ctx= lex->spcont; + sp_label *hlab= lex->spcont->pop_label(); /* After this hdlr */ + sp_instr_hreturn *i; + + if ($2 == sp_handler::CONTINUE) + { + i= new (thd->mem_root) + sp_instr_hreturn(sp->instructions(), ctx); + if (i == NULL || + sp->add_instr(i)) + MYSQL_YYABORT; + } + else + { /* EXIT or UNDO handler, just jump to the end of the block */ + i= new (thd->mem_root) + sp_instr_hreturn(sp->instructions(), ctx); + if (i == NULL || + sp->add_instr(i) || + sp->push_backpatch(thd, i, lex->spcont->last_label())) /* Block end */ + MYSQL_YYABORT; + } + lex->sphead->backpatch(hlab); + + lex->spcont= ctx->pop_context(); + + $$.vars= $$.conds= $$.curs= 0; + $$.hndlrs= 1; + } + | DECLARE_SYM ident CURSOR_SYM FOR_SYM sp_cursor_stmt + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + sp_pcontext *ctx= lex->spcont; + uint offp; + sp_instr_cpush *i; + + if (ctx->find_cursor($2, &offp, TRUE)) + my_yyabort_error((ER_SP_DUP_CURS, MYF(0), $2.str)); + + i= new (thd->mem_root) + sp_instr_cpush(sp->instructions(), ctx, $5, + ctx->current_cursor_count()); + if (i == NULL || sp->add_instr(i) || ctx->add_cursor($2)) + MYSQL_YYABORT; + $$.vars= $$.conds= $$.hndlrs= 0; + $$.curs= 1; + } + ; + +sp_cursor_stmt: + { + Lex->sphead->reset_lex(thd); + } + select + { + LEX *lex= Lex; + + DBUG_ASSERT(lex->sql_command == SQLCOM_SELECT); + + if (lex->result) + my_yyabort_error((ER_SP_BAD_CURSOR_SELECT, MYF(0))); + lex->sp_lex_in_use= TRUE; + $$= lex; + if (lex->sphead->restore_lex(thd)) + MYSQL_YYABORT; + } + ; + +sp_handler_type: + EXIT_SYM { $$= sp_handler::EXIT; } + | CONTINUE_SYM { $$= sp_handler::CONTINUE; } + /*| UNDO_SYM { QQ No yet } */ + ; + +sp_hcond_list: + sp_hcond_element + { $$= 1; } + | sp_hcond_list ',' sp_hcond_element + { $$+= 1; } + ; + +sp_hcond_element: + sp_hcond + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + sp_pcontext *ctx= lex->spcont->parent_context(); + + if (ctx->check_duplicate_handler($1)) + my_yyabort_error((ER_SP_DUP_HANDLER, MYF(0))); + + sp_instr_hpush_jump *i= (sp_instr_hpush_jump *)sp->last_instruction(); + i->add_condition($1); + } + ; + +sp_cond: + ulong_num + { /* mysql errno */ + if ($1 == 0) + my_yyabort_error((ER_WRONG_VALUE, MYF(0), "CONDITION", "0")); + $$= new (thd->mem_root) sp_condition_value($1); + if ($$ == NULL) + MYSQL_YYABORT; + } + | sqlstate + ; + +sqlstate: + SQLSTATE_SYM opt_value TEXT_STRING_literal + { /* SQLSTATE */ + + /* + An error is triggered: + - if the specified string is not a valid SQLSTATE, + - or if it represents the completion condition -- it is not + allowed to SIGNAL, or declare a handler for the completion + condition. + */ + if (!is_sqlstate_valid(&$3) || is_sqlstate_completion($3.str)) + my_yyabort_error((ER_SP_BAD_SQLSTATE, MYF(0), $3.str)); + $$= new (thd->mem_root) sp_condition_value($3.str); + if ($$ == NULL) + MYSQL_YYABORT; + } + ; + +opt_value: + /* Empty */ {} + | VALUE_SYM {} + ; + +sp_hcond: + sp_cond + { + $$= $1; + } + | ident /* CONDITION name */ + { + $$= Lex->spcont->find_condition($1, false); + if ($$ == NULL) + my_yyabort_error((ER_SP_COND_MISMATCH, MYF(0), $1.str)); + } + | SQLWARNING_SYM /* SQLSTATEs 01??? */ + { + $$= new (thd->mem_root) sp_condition_value(sp_condition_value::WARNING); + if ($$ == NULL) + MYSQL_YYABORT; + } + | not FOUND_SYM /* SQLSTATEs 02??? */ + { + $$= new (thd->mem_root) sp_condition_value(sp_condition_value::NOT_FOUND); + if ($$ == NULL) + MYSQL_YYABORT; + } + | SQLEXCEPTION_SYM /* All other SQLSTATEs */ + { + $$= new (thd->mem_root) sp_condition_value(sp_condition_value::EXCEPTION); + if ($$ == NULL) + MYSQL_YYABORT; + } + ; + +signal_stmt: + SIGNAL_SYM signal_value opt_set_signal_information + { + LEX *lex= thd->lex; + Yacc_state *state= & thd->m_parser_state->m_yacc; + + lex->sql_command= SQLCOM_SIGNAL; + lex->m_sql_cmd= + new (thd->mem_root) Sql_cmd_signal($2, state->m_set_signal_info); + if (lex->m_sql_cmd == NULL) + MYSQL_YYABORT; + } + ; + +signal_value: + ident + { + LEX *lex= Lex; + sp_condition_value *cond; + + /* SIGNAL foo cannot be used outside of stored programs */ + if (lex->spcont == NULL) + my_yyabort_error((ER_SP_COND_MISMATCH, MYF(0), $1.str)); + cond= lex->spcont->find_condition($1, false); + if (cond == NULL) + my_yyabort_error((ER_SP_COND_MISMATCH, MYF(0), $1.str)); + if (cond->type != sp_condition_value::SQLSTATE) + my_yyabort_error((ER_SIGNAL_BAD_CONDITION_TYPE, MYF(0))); + $$= cond; + } + | sqlstate + { $$= $1; } + ; + +opt_signal_value: + /* empty */ + { $$= NULL; } + | signal_value + { $$= $1; } + ; + +opt_set_signal_information: + /* empty */ + { + thd->m_parser_state->m_yacc.m_set_signal_info.clear(); + } + | SET signal_information_item_list + ; + +signal_information_item_list: + signal_condition_information_item_name '=' signal_allowed_expr + { + Set_signal_information *info; + info= &thd->m_parser_state->m_yacc.m_set_signal_info; + int index= (int) $1; + info->clear(); + info->m_item[index]= $3; + } + | signal_information_item_list ',' + signal_condition_information_item_name '=' signal_allowed_expr + { + Set_signal_information *info; + info= &thd->m_parser_state->m_yacc.m_set_signal_info; + int index= (int) $3; + if (info->m_item[index] != NULL) + my_yyabort_error((ER_DUP_SIGNAL_SET, MYF(0), + Diag_condition_item_names[index].str)); + info->m_item[index]= $5; + } + ; + +/* + Only a limited subset of <expr> are allowed in SIGNAL/RESIGNAL. +*/ +signal_allowed_expr: + literal + { $$= $1; } + | variable + { + if ($1->type() == Item::FUNC_ITEM) + { + Item_func *item= (Item_func*) $1; + if (item->functype() == Item_func::SUSERVAR_FUNC) + { + /* + Don't allow the following syntax: + SIGNAL/RESIGNAL ... + SET <signal condition item name> = @foo := expr + */ + my_parse_error(thd, ER_SYNTAX_ERROR); + MYSQL_YYABORT; + } + } + $$= $1; + } + | simple_ident + { $$= $1; } + ; + +/* conditions that can be set in signal / resignal */ +signal_condition_information_item_name: + CLASS_ORIGIN_SYM + { $$= DIAG_CLASS_ORIGIN; } + | SUBCLASS_ORIGIN_SYM + { $$= DIAG_SUBCLASS_ORIGIN; } + | CONSTRAINT_CATALOG_SYM + { $$= DIAG_CONSTRAINT_CATALOG; } + | CONSTRAINT_SCHEMA_SYM + { $$= DIAG_CONSTRAINT_SCHEMA; } + | CONSTRAINT_NAME_SYM + { $$= DIAG_CONSTRAINT_NAME; } + | CATALOG_NAME_SYM + { $$= DIAG_CATALOG_NAME; } + | SCHEMA_NAME_SYM + { $$= DIAG_SCHEMA_NAME; } + | TABLE_NAME_SYM + { $$= DIAG_TABLE_NAME; } + | COLUMN_NAME_SYM + { $$= DIAG_COLUMN_NAME; } + | CURSOR_NAME_SYM + { $$= DIAG_CURSOR_NAME; } + | MESSAGE_TEXT_SYM + { $$= DIAG_MESSAGE_TEXT; } + | MYSQL_ERRNO_SYM + { $$= DIAG_MYSQL_ERRNO; } + ; + +resignal_stmt: + RESIGNAL_SYM opt_signal_value opt_set_signal_information + { + LEX *lex= thd->lex; + Yacc_state *state= & thd->m_parser_state->m_yacc; + + lex->sql_command= SQLCOM_RESIGNAL; + lex->m_sql_cmd= + new (thd->mem_root) Sql_cmd_resignal($2, + state->m_set_signal_info); + if (lex->m_sql_cmd == NULL) + MYSQL_YYABORT; + } + ; + +get_diagnostics: + GET_SYM which_area DIAGNOSTICS_SYM diagnostics_information + { + Diagnostics_information *info= $4; + + info->set_which_da($2); + + Lex->sql_command= SQLCOM_GET_DIAGNOSTICS; + Lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_get_diagnostics(info); + + if (Lex->m_sql_cmd == NULL) + MYSQL_YYABORT; + } + ; + +which_area: + /* If <which area> is not specified, then CURRENT is implicit. */ + { $$= Diagnostics_information::CURRENT_AREA; } + | CURRENT_SYM + { $$= Diagnostics_information::CURRENT_AREA; } + ; + +diagnostics_information: + statement_information + { + $$= new (thd->mem_root) Statement_information($1); + if ($$ == NULL) + MYSQL_YYABORT; + } + | CONDITION_SYM condition_number condition_information + { + $$= new (thd->mem_root) Condition_information($2, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + ; + +statement_information: + statement_information_item + { + $$= new (thd->mem_root) List<Statement_information_item>; + if ($$ == NULL || $$->push_back($1, thd->mem_root)) + MYSQL_YYABORT; + } + | statement_information ',' statement_information_item + { + if ($1->push_back($3, thd->mem_root)) + MYSQL_YYABORT; + $$= $1; + } + ; + +statement_information_item: + simple_target_specification '=' statement_information_item_name + { + $$= new (thd->mem_root) Statement_information_item($3, $1); + if ($$ == NULL) + MYSQL_YYABORT; + } + +simple_target_specification: + ident + { + Lex_input_stream *lip= &thd->m_parser_state->m_lip; + $$= thd->lex->create_item_for_sp_var($1, NULL, + lip->get_tok_start(), + lip->get_ptr()); + if ($$ == NULL) + MYSQL_YYABORT; + } + | '@' ident_or_text + { + $$= new (thd->mem_root) Item_func_get_user_var(thd, $2); + if ($$ == NULL) + MYSQL_YYABORT; + } + ; + +statement_information_item_name: + NUMBER_SYM + { $$= Statement_information_item::NUMBER; } + | ROW_COUNT_SYM + { $$= Statement_information_item::ROW_COUNT; } + ; + +/* + Only a limited subset of <expr> are allowed in GET DIAGNOSTICS + <condition number>, same subset as for SIGNAL/RESIGNAL. +*/ +condition_number: + signal_allowed_expr + { $$= $1; } + ; + +condition_information: + condition_information_item + { + $$= new (thd->mem_root) List<Condition_information_item>; + if ($$ == NULL || $$->push_back($1, thd->mem_root)) + MYSQL_YYABORT; + } + | condition_information ',' condition_information_item + { + if ($1->push_back($3, thd->mem_root)) + MYSQL_YYABORT; + $$= $1; + } + ; + +condition_information_item: + simple_target_specification '=' condition_information_item_name + { + $$= new (thd->mem_root) Condition_information_item($3, $1); + if ($$ == NULL) + MYSQL_YYABORT; + } + +condition_information_item_name: + CLASS_ORIGIN_SYM + { $$= Condition_information_item::CLASS_ORIGIN; } + | SUBCLASS_ORIGIN_SYM + { $$= Condition_information_item::SUBCLASS_ORIGIN; } + | CONSTRAINT_CATALOG_SYM + { $$= Condition_information_item::CONSTRAINT_CATALOG; } + | CONSTRAINT_SCHEMA_SYM + { $$= Condition_information_item::CONSTRAINT_SCHEMA; } + | CONSTRAINT_NAME_SYM + { $$= Condition_information_item::CONSTRAINT_NAME; } + | CATALOG_NAME_SYM + { $$= Condition_information_item::CATALOG_NAME; } + | SCHEMA_NAME_SYM + { $$= Condition_information_item::SCHEMA_NAME; } + | TABLE_NAME_SYM + { $$= Condition_information_item::TABLE_NAME; } + | COLUMN_NAME_SYM + { $$= Condition_information_item::COLUMN_NAME; } + | CURSOR_NAME_SYM + { $$= Condition_information_item::CURSOR_NAME; } + | MESSAGE_TEXT_SYM + { $$= Condition_information_item::MESSAGE_TEXT; } + | MYSQL_ERRNO_SYM + { $$= Condition_information_item::MYSQL_ERRNO; } + | RETURNED_SQLSTATE_SYM + { $$= Condition_information_item::RETURNED_SQLSTATE; } + ; + +sp_decl_idents: + ident + { + /* NOTE: field definition is filled in sp_decl section. */ + + LEX *lex= Lex; + sp_pcontext *spc= lex->spcont; + + if (spc->find_variable($1, TRUE)) + my_yyabort_error((ER_SP_DUP_VAR, MYF(0), $1.str)); + spc->add_variable(thd, $1); + $$= 1; + } + | sp_decl_idents ',' ident + { + /* NOTE: field definition is filled in sp_decl section. */ + + LEX *lex= Lex; + sp_pcontext *spc= lex->spcont; + + if (spc->find_variable($3, TRUE)) + my_yyabort_error((ER_SP_DUP_VAR, MYF(0), $3.str)); + spc->add_variable(thd, $3); + $$= $1 + 1; + } + ; + +sp_opt_default: + /* Empty */ { $$ = NULL; } + | DEFAULT expr { $$ = $2; } + ; + +/* + ps_proc_stmt_in_returns_clause is a statement that is allowed + in the RETURNS clause of a stored function definition directly, + without the BEGIN..END block. + It should not include any syntax structures starting with '(', to avoid + shift/reduce conflicts with the rule "field_type" and its sub-rules + that scan an optional length, like CHAR(1) or YEAR(4). + See MDEV-9166. +*/ +sp_proc_stmt_in_returns_clause: + sp_proc_stmt_return + | sp_labeled_block + | sp_unlabeled_block + | sp_labeled_control + | sp_proc_stmt_compound_ok + ; + +sp_proc_stmt: + sp_proc_stmt_in_returns_clause + | sp_proc_stmt_statement + | sp_proc_stmt_leave + | sp_proc_stmt_iterate + | sp_proc_stmt_open + | sp_proc_stmt_fetch + | sp_proc_stmt_close + ; + +sp_proc_stmt_compound_ok: + sp_proc_stmt_if + | case_stmt_specification + | sp_unlabeled_block_not_atomic + | sp_unlabeled_control + ; + +sp_proc_stmt_if: + IF_SYM + { + if (maybe_start_compound_statement(thd)) + MYSQL_YYABORT; + Lex->sphead->new_cont_backpatch(NULL); + } + sp_if END IF_SYM + { Lex->sphead->do_cont_backpatch(); } + ; + +sp_proc_stmt_statement: + { + LEX *lex= thd->lex; + Lex_input_stream *lip= YYLIP; + + lex->sphead->reset_lex(thd); + lex->sphead->m_tmp_query= lip->get_tok_start(); + } + statement + { + LEX *lex= thd->lex; + Lex_input_stream *lip= YYLIP; + sp_head *sp= lex->sphead; + + sp->m_flags|= sp_get_flags_for_command(lex); + /* "USE db" doesn't work in a procedure */ + if (lex->sql_command == SQLCOM_CHANGE_DB) + my_yyabort_error((ER_SP_BADSTATEMENT, MYF(0), "USE")); + /* + Don't add an instruction for SET statements, since all + instructions for them were already added during processing + of "set" rule. + */ + DBUG_ASSERT(lex->sql_command != SQLCOM_SET_OPTION || + lex->var_list.is_empty()); + if (lex->sql_command != SQLCOM_SET_OPTION) + { + sp_instr_stmt *i=new (thd->mem_root) + sp_instr_stmt(sp->instructions(), lex->spcont, lex); + if (i == NULL) + MYSQL_YYABORT; + + /* + Extract the query statement from the tokenizer. The + end is either lex->ptr, if there was no lookahead, + lex->tok_end otherwise. + */ + if (yychar == YYEMPTY) + i->m_query.length= lip->get_ptr() - sp->m_tmp_query; + else + i->m_query.length= lip->get_tok_start() - sp->m_tmp_query;; + if (!(i->m_query.str= strmake_root(thd->mem_root, + sp->m_tmp_query, + i->m_query.length)) || + sp->add_instr(i)) + MYSQL_YYABORT; + } + if (sp->restore_lex(thd)) + MYSQL_YYABORT; + } + ; + +sp_proc_stmt_return: + RETURN_SYM + { Lex->sphead->reset_lex(thd); } + expr + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + + if (sp->m_type != TYPE_ENUM_FUNCTION) + my_yyabort_error((ER_SP_BADRETURN, MYF(0))); + + sp_instr_freturn *i; + + i= new (thd->mem_root) + sp_instr_freturn(sp->instructions(), lex->spcont, $3, + sp->m_return_field_def.sql_type, lex); + if (i == NULL || sp->add_instr(i)) + MYSQL_YYABORT; + sp->m_flags|= sp_head::HAS_RETURN; + + if (sp->restore_lex(thd)) + MYSQL_YYABORT; + } + ; + +sp_proc_stmt_leave: + LEAVE_SYM label_ident + { + LEX *lex= Lex; + sp_head *sp = lex->sphead; + sp_pcontext *ctx= lex->spcont; + sp_label *lab= ctx->find_label($2); + + if (! lab) + my_yyabort_error((ER_SP_LILABEL_MISMATCH, MYF(0), "LEAVE", $2.str)); + + sp_instr_jump *i; + uint ip= sp->instructions(); + uint n; + /* + When jumping to a BEGIN-END block end, the target jump + points to the block hpop/cpop cleanup instructions, + so we should exclude the block context here. + When jumping to something else (i.e., SP_LAB_ITER), + there are no hpop/cpop at the jump destination, + so we should include the block context here for cleanup. + */ + bool exclusive= (lab->type == sp_label::BEGIN); + + n= ctx->diff_handlers(lab->ctx, exclusive); + if (n) + { + sp_instr_hpop *hpop= new (thd->mem_root) + sp_instr_hpop(ip++, ctx, n); + if (hpop == NULL) + MYSQL_YYABORT; + sp->add_instr(hpop); + } + n= ctx->diff_cursors(lab->ctx, exclusive); + if (n) + { + sp_instr_cpop *cpop= new (thd->mem_root) + sp_instr_cpop(ip++, ctx, n); + if (cpop == NULL) + MYSQL_YYABORT; + sp->add_instr(cpop); + } + i= new (thd->mem_root) sp_instr_jump(ip, ctx); + if (i == NULL) + MYSQL_YYABORT; + sp->push_backpatch(thd, i, lab); /* Jumping forward */ + sp->add_instr(i); + } + ; + +sp_proc_stmt_iterate: + ITERATE_SYM label_ident + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + sp_pcontext *ctx= lex->spcont; + sp_label *lab= ctx->find_label($2); + + if (! lab || lab->type != sp_label::ITERATION) + my_yyabort_error((ER_SP_LILABEL_MISMATCH, MYF(0), "ITERATE", $2.str)); + + sp_instr_jump *i; + uint ip= sp->instructions(); + uint n; + + n= ctx->diff_handlers(lab->ctx, FALSE); /* Inclusive the dest. */ + if (n) + { + sp_instr_hpop *hpop= new (thd->mem_root) + sp_instr_hpop(ip++, ctx, n); + if (hpop == NULL || + sp->add_instr(hpop)) + MYSQL_YYABORT; + } + n= ctx->diff_cursors(lab->ctx, FALSE); /* Inclusive the dest. */ + if (n) + { + sp_instr_cpop *cpop= new (thd->mem_root) + sp_instr_cpop(ip++, ctx, n); + if (cpop == NULL || + sp->add_instr(cpop)) + MYSQL_YYABORT; + } + i= new (thd->mem_root) + sp_instr_jump(ip, ctx, lab->ip); /* Jump back */ + if (i == NULL || + sp->add_instr(i)) + MYSQL_YYABORT; + } + ; + +sp_proc_stmt_open: + OPEN_SYM ident + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + uint offset; + sp_instr_copen *i; + + if (! lex->spcont->find_cursor($2, &offset, false)) + my_yyabort_error((ER_SP_CURSOR_MISMATCH, MYF(0), $2.str)); + i= new (thd->mem_root) + sp_instr_copen(sp->instructions(), lex->spcont, offset); + if (i == NULL || + sp->add_instr(i)) + MYSQL_YYABORT; + } + ; + +sp_proc_stmt_fetch: + FETCH_SYM sp_opt_fetch_noise ident INTO + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + uint offset; + sp_instr_cfetch *i; + + if (! lex->spcont->find_cursor($3, &offset, false)) + my_yyabort_error((ER_SP_CURSOR_MISMATCH, MYF(0), $3.str)); + i= new (thd->mem_root) + sp_instr_cfetch(sp->instructions(), lex->spcont, offset); + if (i == NULL || + sp->add_instr(i)) + MYSQL_YYABORT; + } + sp_fetch_list + {} + ; + +sp_proc_stmt_close: + CLOSE_SYM ident + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + uint offset; + sp_instr_cclose *i; + + if (! lex->spcont->find_cursor($2, &offset, false)) + my_yyabort_error((ER_SP_CURSOR_MISMATCH, MYF(0), $2.str)); + i= new (thd->mem_root) + sp_instr_cclose(sp->instructions(), lex->spcont, offset); + if (i == NULL || + sp->add_instr(i)) + MYSQL_YYABORT; + } + ; + +sp_opt_fetch_noise: + /* Empty */ + | NEXT_SYM FROM + | FROM + ; + +sp_fetch_list: + ident + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + sp_pcontext *spc= lex->spcont; + sp_variable *spv; + + if (!spc || !(spv = spc->find_variable($1, false))) + my_yyabort_error((ER_SP_UNDECLARED_VAR, MYF(0), $1.str)); + + /* An SP local variable */ + sp_instr_cfetch *i= (sp_instr_cfetch *)sp->last_instruction(); + i->add_to_varlist(spv); + } + | sp_fetch_list ',' ident + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + sp_pcontext *spc= lex->spcont; + sp_variable *spv; + + if (!spc || !(spv = spc->find_variable($3, false))) + my_yyabort_error((ER_SP_UNDECLARED_VAR, MYF(0), $3.str)); + + /* An SP local variable */ + sp_instr_cfetch *i= (sp_instr_cfetch *)sp->last_instruction(); + i->add_to_varlist(spv); + } + ; + +sp_if: + { Lex->sphead->reset_lex(thd); } + expr THEN_SYM + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + sp_pcontext *ctx= lex->spcont; + uint ip= sp->instructions(); + sp_instr_jump_if_not *i= new (thd->mem_root) + sp_instr_jump_if_not(ip, ctx, $2, lex); + if (i == NULL || + sp->push_backpatch(thd, i, ctx->push_label(thd, empty_lex_str, 0)) || + sp->add_cont_backpatch(i) || + sp->add_instr(i)) + MYSQL_YYABORT; + if (sp->restore_lex(thd)) + MYSQL_YYABORT; + } + sp_proc_stmts1 + { + sp_head *sp= Lex->sphead; + sp_pcontext *ctx= Lex->spcont; + uint ip= sp->instructions(); + sp_instr_jump *i= new (thd->mem_root) sp_instr_jump(ip, ctx); + if (i == NULL || + sp->add_instr(i)) + MYSQL_YYABORT; + sp->backpatch(ctx->pop_label()); + sp->push_backpatch(thd, i, ctx->push_label(thd, empty_lex_str, 0)); + } + sp_elseifs + { + LEX *lex= Lex; + + lex->sphead->backpatch(lex->spcont->pop_label()); + } + ; + +sp_elseifs: + /* Empty */ + | ELSEIF_SYM sp_if + | ELSE sp_proc_stmts1 + ; + +case_stmt_specification: + CASE_SYM + { + if (maybe_start_compound_statement(thd)) + MYSQL_YYABORT; + + /** + An example of the CASE statement in use is + <pre> + CREATE PROCEDURE proc_19194_simple(i int) + BEGIN + DECLARE str CHAR(10); + + CASE i + WHEN 1 THEN SET str="1"; + WHEN 2 THEN SET str="2"; + WHEN 3 THEN SET str="3"; + ELSE SET str="unknown"; + END CASE; + + SELECT str; + END + </pre> + The actions are used to generate the following code: + <pre> + SHOW PROCEDURE CODE proc_19194_simple; + Pos Instruction + 0 set str@1 NULL + 1 set_case_expr (12) 0 i@0 + 2 jump_if_not 5(12) (case_expr@0 = 1) + 3 set str@1 _latin1'1' + 4 jump 12 + 5 jump_if_not 8(12) (case_expr@0 = 2) + 6 set str@1 _latin1'2' + 7 jump 12 + 8 jump_if_not 11(12) (case_expr@0 = 3) + 9 set str@1 _latin1'3' + 10 jump 12 + 11 set str@1 _latin1'unknown' + 12 stmt 0 "SELECT str" + </pre> + */ + + Lex->sphead->new_cont_backpatch(NULL); + + /* + BACKPATCH: Creating target label for the jump to after END CASE + (instruction 12 in the example) + */ + Lex->spcont->push_label(thd, empty_lex_str, Lex->sphead->instructions()); + } + case_stmt_body + else_clause_opt + END + CASE_SYM + { + /* + BACKPATCH: Resolving forward jump from + "case_stmt_action_then" to after END CASE + (jump from instruction 4 to 12, 7 to 12 ... in the example) + */ + Lex->sphead->backpatch(Lex->spcont->pop_label()); + + if ($3) + Lex->spcont->pop_case_expr_id(); + + Lex->sphead->do_cont_backpatch(); + } + ; + +case_stmt_body: + { Lex->sphead->reset_lex(thd); /* For expr $2 */ } + expr + { + if (Lex->case_stmt_action_expr($2)) + MYSQL_YYABORT; + + if (Lex->sphead->restore_lex(thd)) + MYSQL_YYABORT; + } + simple_when_clause_list + { $$= 1; } + | searched_when_clause_list + { $$= 0; } + ; + +simple_when_clause_list: + simple_when_clause + | simple_when_clause_list simple_when_clause + ; + +searched_when_clause_list: + searched_when_clause + | searched_when_clause_list searched_when_clause + ; + +simple_when_clause: + WHEN_SYM + { + Lex->sphead->reset_lex(thd); /* For expr $3 */ + } + expr + { + /* Simple case: <caseval> = <whenval> */ + + LEX *lex= Lex; + if (lex->case_stmt_action_when($3, true)) + MYSQL_YYABORT; + /* For expr $3 */ + if (lex->sphead->restore_lex(thd)) + MYSQL_YYABORT; + } + THEN_SYM + sp_proc_stmts1 + { + if (Lex->case_stmt_action_then()) + MYSQL_YYABORT; + } + ; + +searched_when_clause: + WHEN_SYM + { + Lex->sphead->reset_lex(thd); /* For expr $3 */ + } + expr + { + LEX *lex= Lex; + if (lex->case_stmt_action_when($3, false)) + MYSQL_YYABORT; + /* For expr $3 */ + if (lex->sphead->restore_lex(thd)) + MYSQL_YYABORT; + } + THEN_SYM + sp_proc_stmts1 + { + if (Lex->case_stmt_action_then()) + MYSQL_YYABORT; + } + ; + +else_clause_opt: + /* empty */ + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + uint ip= sp->instructions(); + sp_instr_error *i= new (thd->mem_root) + sp_instr_error(ip, lex->spcont, ER_SP_CASE_NOT_FOUND); + if (i == NULL || + sp->add_instr(i)) + MYSQL_YYABORT; + } + | ELSE sp_proc_stmts1 + ; + +sp_opt_label: + /* Empty */ { $$= null_lex_str; } + | label_ident { $$= $1; } + ; + +sp_labeled_block: + label_ident ':' BEGIN_SYM + { + LEX *lex= Lex; + sp_pcontext *ctx= lex->spcont; + sp_label *lab= ctx->find_label($1); + + if (lab) + my_yyabort_error((ER_SP_LABEL_REDEFINE, MYF(0), $1.str)); + lex->name= $1; + } + sp_block_content sp_opt_label + { + if ($6.str) + { + if (my_strcasecmp(system_charset_info, $6.str, $5->name.str) != 0) + my_yyabort_error((ER_SP_LABEL_MISMATCH, MYF(0), $6.str)); + } + } + ; + +sp_unlabeled_block: + BEGIN_SYM + { + Lex->name= empty_lex_str; // Unlabeled blocks get an empty label + } + sp_block_content + { } + ; + +sp_unlabeled_block_not_atomic: + BEGIN_SYM not ATOMIC_SYM /* TODO: BEGIN ATOMIC (not -> opt_not) */ + { + if (maybe_start_compound_statement(thd)) + MYSQL_YYABORT; + Lex->name= empty_lex_str; // Unlabeled blocks get an empty label + } + sp_block_content + { } + ; + +sp_block_content: + { + LEX *lex= Lex; + sp_label *lab= lex->spcont->push_label(thd, lex->name, + lex->sphead->instructions()); + lab->type= sp_label::BEGIN; + lex->spcont= lex->spcont->push_context(thd, + sp_pcontext::REGULAR_SCOPE); + } + sp_decls + sp_proc_stmts + END + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + sp_pcontext *ctx= lex->spcont; + sp_instr *i; + + sp->backpatch(ctx->last_label()); /* We always have a label */ + if ($2.hndlrs) + { + i= new (thd->mem_root) + sp_instr_hpop(sp->instructions(), ctx, $2.hndlrs); + if (i == NULL || + sp->add_instr(i)) + MYSQL_YYABORT; + } + if ($2.curs) + { + i= new (thd->mem_root) + sp_instr_cpop(sp->instructions(), ctx, $2.curs); + if (i == NULL || + sp->add_instr(i)) + MYSQL_YYABORT; + } + lex->spcont= ctx->pop_context(); + $$ = lex->spcont->pop_label(); + } + ; + +loop_body: + sp_proc_stmts1 END LOOP_SYM + { + LEX *lex= Lex; + uint ip= lex->sphead->instructions(); + sp_label *lab= lex->spcont->last_label(); /* Jumping back */ + sp_instr_jump *i= new (thd->mem_root) + sp_instr_jump(ip, lex->spcont, lab->ip); + if (i == NULL || + lex->sphead->add_instr(i)) + MYSQL_YYABORT; + } + ; + +while_body: + expr DO_SYM + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + uint ip= sp->instructions(); + sp_instr_jump_if_not *i= new (thd->mem_root) + sp_instr_jump_if_not(ip, lex->spcont, $1, lex); + if (i == NULL || + /* Jumping forward */ + sp->push_backpatch(thd, i, lex->spcont->last_label()) || + sp->new_cont_backpatch(i) || + sp->add_instr(i)) + MYSQL_YYABORT; + if (sp->restore_lex(thd)) + MYSQL_YYABORT; + } + sp_proc_stmts1 END WHILE_SYM + { + LEX *lex= Lex; + uint ip= lex->sphead->instructions(); + sp_label *lab= lex->spcont->last_label(); /* Jumping back */ + sp_instr_jump *i= new (thd->mem_root) + sp_instr_jump(ip, lex->spcont, lab->ip); + if (i == NULL || + lex->sphead->add_instr(i)) + MYSQL_YYABORT; + lex->sphead->do_cont_backpatch(); + } + ; + +repeat_body: + sp_proc_stmts1 UNTIL_SYM + { Lex->sphead->reset_lex(thd); } + expr END REPEAT_SYM + { + LEX *lex= Lex; + uint ip= lex->sphead->instructions(); + sp_label *lab= lex->spcont->last_label(); /* Jumping back */ + sp_instr_jump_if_not *i= new (thd->mem_root) + sp_instr_jump_if_not(ip, lex->spcont, $4, lab->ip, lex); + if (i == NULL || + lex->sphead->add_instr(i)) + MYSQL_YYABORT; + if (lex->sphead->restore_lex(thd)) + MYSQL_YYABORT; + /* We can shortcut the cont_backpatch here */ + i->m_cont_dest= ip+1; + } + ; + +pop_sp_label: + sp_opt_label + { + sp_label *lab; + Lex->sphead->backpatch(lab= Lex->spcont->pop_label()); + if ($1.str) + { + if (my_strcasecmp(system_charset_info, $1.str, + lab->name.str) != 0) + my_yyabort_error((ER_SP_LABEL_MISMATCH, MYF(0), $1.str)); + } + } + ; + +pop_sp_empty_label: + { + sp_label *lab; + Lex->sphead->backpatch(lab= Lex->spcont->pop_label()); + DBUG_ASSERT(lab->name.length == 0); + } + ; + +sp_labeled_control: + label_ident ':' LOOP_SYM + { + if (push_sp_label(thd, $1)) + MYSQL_YYABORT; + } + loop_body pop_sp_label + { } + | label_ident ':' WHILE_SYM + { + if (push_sp_label(thd, $1)) + MYSQL_YYABORT; + Lex->sphead->reset_lex(thd); + } + while_body pop_sp_label + { } + | label_ident ':' REPEAT_SYM + { + if (push_sp_label(thd, $1)) + MYSQL_YYABORT; + } + repeat_body pop_sp_label + { } + ; + +sp_unlabeled_control: + LOOP_SYM + { + if (push_sp_empty_label(thd)) + MYSQL_YYABORT; + } + loop_body + pop_sp_empty_label + { } + | WHILE_SYM + { + if (push_sp_empty_label(thd)) + MYSQL_YYABORT; + Lex->sphead->reset_lex(thd); + } + while_body + pop_sp_empty_label + { } + | REPEAT_SYM + { + if (push_sp_empty_label(thd)) + MYSQL_YYABORT; + } + repeat_body + pop_sp_empty_label + { } + ; + +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; } + ; +/* + This part of the parser contains common code for all TABLESPACE + commands. + CREATE TABLESPACE name ... + ALTER TABLESPACE name CHANGE DATAFILE ... + ALTER TABLESPACE name ADD DATAFILE ... + ALTER TABLESPACE name access_mode + CREATE LOGFILE GROUP_SYM name ... + ALTER LOGFILE GROUP_SYM name ADD UNDOFILE .. + ALTER LOGFILE GROUP_SYM name ADD REDOFILE .. + DROP TABLESPACE name + DROP LOGFILE GROUP_SYM name +*/ +change_tablespace_access: + tablespace_name + ts_access_mode + ; + +change_tablespace_info: + tablespace_name + CHANGE ts_datafile + change_ts_option_list + ; + +tablespace_info: + tablespace_name + ADD ts_datafile + opt_logfile_group_name + tablespace_option_list + ; + +opt_logfile_group_name: + /* empty */ {} + | USE_SYM LOGFILE_SYM GROUP_SYM ident + { + LEX *lex= Lex; + lex->alter_tablespace_info->logfile_group_name= $4.str; + } + ; + +alter_tablespace_info: + tablespace_name + ADD ts_datafile + alter_tablespace_option_list + { + Lex->alter_tablespace_info->ts_alter_tablespace_type= ALTER_TABLESPACE_ADD_FILE; + } + | tablespace_name + DROP ts_datafile + alter_tablespace_option_list + { + Lex->alter_tablespace_info->ts_alter_tablespace_type= ALTER_TABLESPACE_DROP_FILE; + } + ; + +logfile_group_info: + logfile_group_name + add_log_file + logfile_group_option_list + ; + +alter_logfile_group_info: + logfile_group_name + add_log_file + alter_logfile_group_option_list + ; + +add_log_file: + ADD lg_undofile + | ADD lg_redofile + ; + +change_ts_option_list: + /* empty */ {} + change_ts_options + ; + +change_ts_options: + change_ts_option + | change_ts_options change_ts_option + | change_ts_options ',' change_ts_option + ; + +change_ts_option: + opt_ts_initial_size + | opt_ts_autoextend_size + | opt_ts_max_size + ; + +tablespace_option_list: + tablespace_options + ; + +tablespace_options: + tablespace_option + | tablespace_options tablespace_option + | tablespace_options ',' tablespace_option + ; + +tablespace_option: + opt_ts_initial_size + | opt_ts_autoextend_size + | opt_ts_max_size + | opt_ts_extent_size + | opt_ts_nodegroup + | opt_ts_engine + | ts_wait + | opt_ts_comment + ; + +alter_tablespace_option_list: + alter_tablespace_options + ; + +alter_tablespace_options: + alter_tablespace_option + | alter_tablespace_options alter_tablespace_option + | alter_tablespace_options ',' alter_tablespace_option + ; + +alter_tablespace_option: + opt_ts_initial_size + | opt_ts_autoextend_size + | opt_ts_max_size + | opt_ts_engine + | ts_wait + ; + +logfile_group_option_list: + logfile_group_options + ; + +logfile_group_options: + logfile_group_option + | logfile_group_options logfile_group_option + | logfile_group_options ',' logfile_group_option + ; + +logfile_group_option: + opt_ts_initial_size + | opt_ts_undo_buffer_size + | opt_ts_redo_buffer_size + | opt_ts_nodegroup + | opt_ts_engine + | ts_wait + | opt_ts_comment + ; + +alter_logfile_group_option_list: + alter_logfile_group_options + ; + +alter_logfile_group_options: + alter_logfile_group_option + | alter_logfile_group_options alter_logfile_group_option + | alter_logfile_group_options ',' alter_logfile_group_option + ; + +alter_logfile_group_option: + opt_ts_initial_size + | opt_ts_engine + | ts_wait + ; + + +ts_datafile: + DATAFILE_SYM TEXT_STRING_sys + { + LEX *lex= Lex; + lex->alter_tablespace_info->data_file_name= $2.str; + } + ; + +lg_undofile: + UNDOFILE_SYM TEXT_STRING_sys + { + LEX *lex= Lex; + lex->alter_tablespace_info->undo_file_name= $2.str; + } + ; + +lg_redofile: + REDOFILE_SYM TEXT_STRING_sys + { + LEX *lex= Lex; + lex->alter_tablespace_info->redo_file_name= $2.str; + } + ; + +tablespace_name: + ident + { + LEX *lex= Lex; + lex->alter_tablespace_info= (new (thd->mem_root) + st_alter_tablespace()); + if (lex->alter_tablespace_info == NULL) + MYSQL_YYABORT; + lex->alter_tablespace_info->tablespace_name= $1.str; + lex->sql_command= SQLCOM_ALTER_TABLESPACE; + } + ; + +logfile_group_name: + ident + { + LEX *lex= Lex; + lex->alter_tablespace_info= (new (thd->mem_root) + st_alter_tablespace()); + if (lex->alter_tablespace_info == NULL) + MYSQL_YYABORT; + lex->alter_tablespace_info->logfile_group_name= $1.str; + lex->sql_command= SQLCOM_ALTER_TABLESPACE; + } + ; + +ts_access_mode: + READ_ONLY_SYM + { + LEX *lex= Lex; + lex->alter_tablespace_info->ts_access_mode= TS_READ_ONLY; + } + | READ_WRITE_SYM + { + LEX *lex= Lex; + lex->alter_tablespace_info->ts_access_mode= TS_READ_WRITE; + } + | NOT_SYM ACCESSIBLE_SYM + { + LEX *lex= Lex; + lex->alter_tablespace_info->ts_access_mode= TS_NOT_ACCESSIBLE; + } + ; + +opt_ts_initial_size: + INITIAL_SIZE_SYM opt_equal size_number + { + LEX *lex= Lex; + lex->alter_tablespace_info->initial_size= $3; + } + ; + +opt_ts_autoextend_size: + AUTOEXTEND_SIZE_SYM opt_equal size_number + { + LEX *lex= Lex; + lex->alter_tablespace_info->autoextend_size= $3; + } + ; + +opt_ts_max_size: + MAX_SIZE_SYM opt_equal size_number + { + LEX *lex= Lex; + lex->alter_tablespace_info->max_size= $3; + } + ; + +opt_ts_extent_size: + EXTENT_SIZE_SYM opt_equal size_number + { + LEX *lex= Lex; + lex->alter_tablespace_info->extent_size= $3; + } + ; + +opt_ts_undo_buffer_size: + UNDO_BUFFER_SIZE_SYM opt_equal size_number + { + LEX *lex= Lex; + lex->alter_tablespace_info->undo_buffer_size= $3; + } + ; + +opt_ts_redo_buffer_size: + REDO_BUFFER_SIZE_SYM opt_equal size_number + { + LEX *lex= Lex; + lex->alter_tablespace_info->redo_buffer_size= $3; + } + ; + +opt_ts_nodegroup: + NODEGROUP_SYM opt_equal real_ulong_num + { + LEX *lex= Lex; + if (lex->alter_tablespace_info->nodegroup_id != UNDEF_NODEGROUP) + my_yyabort_error((ER_FILEGROUP_OPTION_ONLY_ONCE,MYF(0),"NODEGROUP")); + lex->alter_tablespace_info->nodegroup_id= $3; + } + ; + +opt_ts_comment: + COMMENT_SYM opt_equal TEXT_STRING_sys + { + LEX *lex= Lex; + if (lex->alter_tablespace_info->ts_comment != NULL) + my_yyabort_error((ER_FILEGROUP_OPTION_ONLY_ONCE,MYF(0),"COMMENT")); + lex->alter_tablespace_info->ts_comment= $3.str; + } + ; + +opt_ts_engine: + opt_storage ENGINE_SYM opt_equal storage_engines + { + LEX *lex= Lex; + if (lex->alter_tablespace_info->storage_engine != NULL) + my_yyabort_error((ER_FILEGROUP_OPTION_ONLY_ONCE, MYF(0), + "STORAGE ENGINE")); + lex->alter_tablespace_info->storage_engine= $4; + } + ; + +opt_ts_wait: + /* empty */ + | ts_wait + ; + +ts_wait: + WAIT_SYM + { + LEX *lex= Lex; + lex->alter_tablespace_info->wait_until_completed= TRUE; + } + | NO_WAIT_SYM + { + LEX *lex= Lex; + if (!(lex->alter_tablespace_info->wait_until_completed)) + my_yyabort_error((ER_FILEGROUP_OPTION_ONLY_ONCE,MYF(0),"NO_WAIT")); + lex->alter_tablespace_info->wait_until_completed= FALSE; + } + ; + +size_number: + real_ulonglong_num { $$= $1;} + | IDENT_sys + { + ulonglong number; + uint text_shift_number= 0; + longlong prefix_number; + char *start_ptr= $1.str; + uint str_len= $1.length; + char *end_ptr= start_ptr + str_len; + int error; + prefix_number= my_strtoll10(start_ptr, &end_ptr, &error); + if ((start_ptr + str_len - 1) == end_ptr) + { + switch (end_ptr[0]) + { + case 'g': + case 'G': + text_shift_number+=10; + case 'm': + case 'M': + text_shift_number+=10; + case 'k': + case 'K': + text_shift_number+=10; + break; + default: + my_yyabort_error((ER_WRONG_SIZE_NUMBER, MYF(0))); + } + if (prefix_number >> 31) + my_yyabort_error((ER_SIZE_OVERFLOW_ERROR, MYF(0))); + number= prefix_number << text_shift_number; + } + else + my_yyabort_error((ER_WRONG_SIZE_NUMBER, MYF(0))); + $$= number; + } + ; + +/* + End tablespace part +*/ + +create_body: + '(' create_field_list ')' + { Lex->create_info.option_list= NULL; } + opt_create_table_options opt_create_partitioning opt_create_select {} + | opt_create_table_options opt_create_partitioning opt_create_select {} + /* + the following rule is redundant, but there's a shift/reduce + conflict that prevents the rule above from parsing a syntax like + CREATE TABLE t1 (SELECT 1); + */ + | '(' create_select_query_specification ')' + | '(' create_select_query_specification ')' + { Select->set_braces(1);} union_list {} + | '(' create_select_query_specification ')' + { Select->set_braces(1);} union_order_or_limit {} + | create_like + { + + Lex->create_info.add(DDL_options_st::OPT_LIKE); + TABLE_LIST *src_table= Lex->select_lex.add_table_to_list(thd, + $1, NULL, 0, TL_READ, MDL_SHARED_READ); + if (! src_table) + MYSQL_YYABORT; + /* CREATE TABLE ... LIKE is not allowed for views. */ + src_table->required_type= FRMTYPE_TABLE; + } + ; + +create_like: + LIKE table_ident { $$= $2; } + | '(' LIKE table_ident ')' { $$= $3; } + ; + +opt_create_select: + /* empty */ {} + | opt_duplicate opt_as create_select_query_expression + ; + +create_select_query_expression: + opt_with_clause SELECT_SYM create_select_part2 opt_table_expression + create_select_part4 + { + Select->set_braces(0); + Select->set_with_clause($1); + } + union_clause + | opt_with_clause SELECT_SYM create_select_part2 + create_select_part3_union_not_ready create_select_part4 + { + Select->set_with_clause($1); + } + | '(' create_select_query_specification ')' + | '(' create_select_query_specification ')' + { Select->set_braces(1);} union_list {} + | '(' create_select_query_specification ')' + { Select->set_braces(1);} union_order_or_limit {} + ; + +opt_create_partitioning: + opt_partitioning + { + /* + Remove all tables used in PARTITION clause from the global table + list. Partitioning with subqueries is not allowed anyway. + */ + TABLE_LIST *last_non_sel_table= Lex->create_last_non_select_table; + last_non_sel_table->next_global= 0; + Lex->query_tables_last= &last_non_sel_table->next_global; + } + ; + +/* + This part of the parser is about handling of the partition information. + + It's first version was written by Mikael Ronstrm with lots of answers to + questions provided by Antony Curtis. + + The partition grammar can be called from three places. + 1) CREATE TABLE ... PARTITION .. + 2) ALTER TABLE table_name PARTITION ... + 3) PARTITION ... + + The first place is called when a new table is created from a MySQL client. + The second place is called when a table is altered with the ALTER TABLE + command from a MySQL client. + The third place is called when opening an frm file and finding partition + info in the .frm file. It is necessary to avoid allowing PARTITION to be + an allowed entry point for SQL client queries. This is arranged by setting + some state variables before arriving here. + + To be able to handle errors we will only set error code in this code + and handle the error condition in the function calling the parser. This + is necessary to ensure we can also handle errors when calling the parser + from the openfrm function. +*/ +opt_partitioning: + /* empty */ {} + | partitioning + ; + +partitioning: + PARTITION_SYM have_partitioning + { + LEX *lex= Lex; + lex->part_info= new (thd->mem_root) partition_info(); + if (!lex->part_info) + { + mem_alloc_error(sizeof(partition_info)); + MYSQL_YYABORT; + } + if (lex->sql_command == SQLCOM_ALTER_TABLE) + { + lex->alter_info.flags|= Alter_info::ALTER_PARTITION; + } + } + partition + ; + +have_partitioning: + /* empty */ + { +#ifdef WITH_PARTITION_STORAGE_ENGINE + LEX_STRING partition_name={C_STRING_WITH_LEN("partition")}; + if (!plugin_is_ready(&partition_name, MYSQL_STORAGE_ENGINE_PLUGIN)) + my_yyabort_error((ER_OPTION_PREVENTS_STATEMENT, MYF(0), + "--skip-partition")); +#else + my_yyabort_error((ER_FEATURE_DISABLED, MYF(0), "partitioning", + "--with-plugin-partition")); +#endif + } + ; + +partition_entry: + PARTITION_SYM + { + LEX *lex= Lex; + if (!lex->part_info) + { + my_parse_error(thd, ER_PARTITION_ENTRY_ERROR); + MYSQL_YYABORT; + } + /* + We enter here when opening the frm file to translate + partition info string into part_info data structure. + */ + } + partition {} + ; + +partition: + BY + { Lex->safe_to_cache_query= 1; } + part_type_def opt_num_parts opt_sub_part part_defs + ; + +part_type_def: + opt_linear KEY_SYM opt_key_algo '(' part_field_list ')' + { + partition_info *part_info= Lex->part_info; + part_info->list_of_part_fields= TRUE; + part_info->column_list= FALSE; + part_info->part_type= HASH_PARTITION; + } + | opt_linear HASH_SYM + { Lex->part_info->part_type= HASH_PARTITION; } + part_func {} + | RANGE_SYM part_func + { Lex->part_info->part_type= RANGE_PARTITION; } + | RANGE_SYM part_column_list + { Lex->part_info->part_type= RANGE_PARTITION; } + | LIST_SYM part_func + { Lex->part_info->part_type= LIST_PARTITION; } + | LIST_SYM part_column_list + { Lex->part_info->part_type= LIST_PARTITION; } + ; + +opt_linear: + /* empty */ {} + | LINEAR_SYM + { Lex->part_info->linear_hash_ind= TRUE;} + ; + +opt_key_algo: + /* empty */ + { Lex->part_info->key_algorithm= partition_info::KEY_ALGORITHM_NONE;} + | ALGORITHM_SYM '=' real_ulong_num + { + switch ($3) { + case 1: + Lex->part_info->key_algorithm= partition_info::KEY_ALGORITHM_51; + break; + case 2: + Lex->part_info->key_algorithm= partition_info::KEY_ALGORITHM_55; + break; + default: + my_parse_error(thd, ER_SYNTAX_ERROR); + MYSQL_YYABORT; + } + } + ; + +part_field_list: + /* empty */ {} + | part_field_item_list {} + ; + +part_field_item_list: + part_field_item {} + | part_field_item_list ',' part_field_item {} + ; + +part_field_item: + ident + { + partition_info *part_info= Lex->part_info; + part_info->num_columns++; + if (part_info->part_field_list.push_back($1.str, thd->mem_root)) + { + mem_alloc_error(1); + MYSQL_YYABORT; + } + if (part_info->num_columns > MAX_REF_PARTS) + my_yyabort_error((ER_TOO_MANY_PARTITION_FUNC_FIELDS_ERROR, MYF(0), + "list of partition fields")); + } + ; + +part_column_list: + COLUMNS '(' part_field_list ')' + { + partition_info *part_info= Lex->part_info; + part_info->column_list= TRUE; + part_info->list_of_part_fields= TRUE; + } + ; + + +part_func: + '(' remember_name part_func_expr remember_end ')' + { + partition_info *part_info= Lex->part_info; + if (part_info->set_part_expr(thd, $2 + 1, $3, $4, FALSE)) + { MYSQL_YYABORT; } + part_info->num_columns= 1; + part_info->column_list= FALSE; + } + ; + +sub_part_func: + '(' remember_name part_func_expr remember_end ')' + { + if (Lex->part_info->set_part_expr(thd, $2 + 1, $3, $4, TRUE)) + { MYSQL_YYABORT; } + } + ; + + +opt_num_parts: + /* empty */ {} + | PARTITIONS_SYM real_ulong_num + { + uint num_parts= $2; + partition_info *part_info= Lex->part_info; + if (num_parts == 0) + my_yyabort_error((ER_NO_PARTS_ERROR, MYF(0), "partitions")); + + part_info->num_parts= num_parts; + part_info->use_default_num_partitions= FALSE; + } + ; + +opt_sub_part: + /* empty */ {} + | SUBPARTITION_SYM BY opt_linear HASH_SYM sub_part_func + { Lex->part_info->subpart_type= HASH_PARTITION; } + opt_num_subparts {} + | SUBPARTITION_SYM BY opt_linear KEY_SYM opt_key_algo + '(' sub_part_field_list ')' + { + partition_info *part_info= Lex->part_info; + part_info->subpart_type= HASH_PARTITION; + part_info->list_of_subpart_fields= TRUE; + } + opt_num_subparts {} + ; + +sub_part_field_list: + sub_part_field_item {} + | sub_part_field_list ',' sub_part_field_item {} + ; + +sub_part_field_item: + ident + { + partition_info *part_info= Lex->part_info; + if (part_info->subpart_field_list.push_back($1.str, thd->mem_root)) + { + mem_alloc_error(1); + MYSQL_YYABORT; + } + if (part_info->subpart_field_list.elements > MAX_REF_PARTS) + my_yyabort_error((ER_TOO_MANY_PARTITION_FUNC_FIELDS_ERROR, MYF(0), + "list of subpartition fields")); + } + ; + +part_func_expr: + bit_expr + { + if (!Lex->safe_to_cache_query) + { + my_parse_error(thd, ER_WRONG_EXPR_IN_PARTITION_FUNC_ERROR); + MYSQL_YYABORT; + } + $$=$1; + } + ; + +opt_num_subparts: + /* empty */ {} + | SUBPARTITIONS_SYM real_ulong_num + { + uint num_parts= $2; + LEX *lex= Lex; + if (num_parts == 0) + my_yyabort_error((ER_NO_PARTS_ERROR, MYF(0), "subpartitions")); + lex->part_info->num_subparts= num_parts; + lex->part_info->use_default_num_subpartitions= FALSE; + } + ; + +part_defs: + /* empty */ + { + partition_info *part_info= Lex->part_info; + if (part_info->part_type == RANGE_PARTITION) + my_yyabort_error((ER_PARTITIONS_MUST_BE_DEFINED_ERROR, MYF(0), + "RANGE")); + if (part_info->part_type == LIST_PARTITION) + my_yyabort_error((ER_PARTITIONS_MUST_BE_DEFINED_ERROR, MYF(0), + "LIST")); + } + | '(' part_def_list ')' + { + partition_info *part_info= Lex->part_info; + uint count_curr_parts= part_info->partitions.elements; + if (part_info->num_parts != 0) + { + if (part_info->num_parts != + count_curr_parts) + { + my_parse_error(thd, ER_PARTITION_WRONG_NO_PART_ERROR); + MYSQL_YYABORT; + } + } + else if (count_curr_parts > 0) + { + part_info->num_parts= count_curr_parts; + } + part_info->count_curr_subparts= 0; + } + ; + +part_def_list: + part_definition {} + | part_def_list ',' part_definition {} + ; + +part_definition: + PARTITION_SYM + { + partition_info *part_info= Lex->part_info; + partition_element *p_elem= new (thd->mem_root) partition_element(); + + if (!p_elem || + part_info->partitions.push_back(p_elem, thd->mem_root)) + { + mem_alloc_error(sizeof(partition_element)); + MYSQL_YYABORT; + } + p_elem->part_state= PART_NORMAL; + part_info->curr_part_elem= p_elem; + part_info->current_partition= p_elem; + part_info->use_default_partitions= FALSE; + part_info->use_default_num_partitions= FALSE; + } + part_name + opt_part_values + opt_part_options + opt_sub_partition + {} + ; + +part_name: + ident + { + partition_info *part_info= Lex->part_info; + partition_element *p_elem= part_info->curr_part_elem; + if (check_ident_length(&$1)) + MYSQL_YYABORT; + p_elem->partition_name= $1.str; + } + ; + +opt_part_values: + /* empty */ + { + LEX *lex= Lex; + partition_info *part_info= lex->part_info; + if (! lex->is_partition_management()) + { + if (part_info->part_type == RANGE_PARTITION) + my_yyabort_error((ER_PARTITION_REQUIRES_VALUES_ERROR, MYF(0), + "RANGE", "LESS THAN")); + if (part_info->part_type == LIST_PARTITION) + my_yyabort_error((ER_PARTITION_REQUIRES_VALUES_ERROR, MYF(0), + "LIST", "IN")); + } + else + part_info->part_type= HASH_PARTITION; + } + | VALUES LESS_SYM THAN_SYM + { + LEX *lex= Lex; + partition_info *part_info= lex->part_info; + if (! lex->is_partition_management()) + { + if (part_info->part_type != RANGE_PARTITION) + my_yyabort_error((ER_PARTITION_WRONG_VALUES_ERROR, MYF(0), + "RANGE", "LESS THAN")); + } + else + part_info->part_type= RANGE_PARTITION; + } + part_func_max {} + | VALUES IN_SYM + { + LEX *lex= Lex; + partition_info *part_info= lex->part_info; + if (! lex->is_partition_management()) + { + if (part_info->part_type != LIST_PARTITION) + my_yyabort_error((ER_PARTITION_WRONG_VALUES_ERROR, MYF(0), + "LIST", "IN")); + } + else + part_info->part_type= LIST_PARTITION; + } + part_values_in {} + | DEFAULT + { + LEX *lex= Lex; + partition_info *part_info= lex->part_info; + if (! lex->is_partition_management()) + { + if (part_info->part_type != LIST_PARTITION) + my_yyabort_error((ER_PARTITION_WRONG_VALUES_ERROR, MYF(0), + "LIST", "DEFAULT")); + } + else + part_info->part_type= LIST_PARTITION; + if (part_info->init_column_part(thd)) + { + MYSQL_YYABORT; + } + if (part_info->add_max_value(thd)) + { + MYSQL_YYABORT; + } + } + ; + +part_func_max: + MAX_VALUE_SYM + { + partition_info *part_info= Lex->part_info; + + if (part_info->num_columns && + part_info->num_columns != 1U) + { + part_info->print_debug("Kilroy II", NULL); + my_parse_error(thd, ER_PARTITION_COLUMN_LIST_ERROR); + MYSQL_YYABORT; + } + else + part_info->num_columns= 1U; + if (part_info->init_column_part(thd)) + { + MYSQL_YYABORT; + } + if (part_info->add_max_value(thd)) + { + MYSQL_YYABORT; + } + } + | part_value_item {} + ; + +part_values_in: + part_value_item + { + LEX *lex= Lex; + partition_info *part_info= lex->part_info; + part_info->print_debug("part_values_in: part_value_item", NULL); + + if (part_info->num_columns != 1U) + { + if (!lex->is_partition_management() || + part_info->num_columns == 0 || + part_info->num_columns > MAX_REF_PARTS) + { + part_info->print_debug("Kilroy III", NULL); + my_parse_error(thd, ER_PARTITION_COLUMN_LIST_ERROR); + MYSQL_YYABORT; + } + /* + Reorganize the current large array into a list of small + arrays with one entry in each array. This can happen + in the first partition of an ALTER TABLE statement where + we ADD or REORGANIZE partitions. Also can only happen + for LIST partitions. + */ + if (part_info->reorganize_into_single_field_col_val(thd)) + { + MYSQL_YYABORT; + } + } + } + | '(' part_value_list ')' + { + partition_info *part_info= Lex->part_info; + if (part_info->num_columns < 2U) + { + my_parse_error(thd, ER_ROW_SINGLE_PARTITION_FIELD_ERROR); + MYSQL_YYABORT; + } + } + ; + +part_value_list: + part_value_item {} + | part_value_list ',' part_value_item {} + ; + +part_value_item: + '(' + { + partition_info *part_info= Lex->part_info; + part_info->print_debug("( part_value_item", NULL); + /* Initialisation code needed for each list of value expressions */ + if (!(part_info->part_type == LIST_PARTITION && + part_info->num_columns == 1U) && + part_info->init_column_part(thd)) + { + MYSQL_YYABORT; + } + } + part_value_item_list {} + ')' + { + partition_info *part_info= Lex->part_info; + part_info->print_debug(") part_value_item", NULL); + if (part_info->num_columns == 0) + part_info->num_columns= part_info->curr_list_object; + if (part_info->num_columns != part_info->curr_list_object) + { + /* + All value items lists must be of equal length, in some cases + which is covered by the above if-statement we don't know yet + how many columns is in the partition so the assignment above + ensures that we only report errors when we know we have an + error. + */ + part_info->print_debug("Kilroy I", NULL); + my_parse_error(thd, ER_PARTITION_COLUMN_LIST_ERROR); + MYSQL_YYABORT; + } + part_info->curr_list_object= 0; + } + ; + +part_value_item_list: + part_value_expr_item {} + | part_value_item_list ',' part_value_expr_item {} + ; + +part_value_expr_item: + MAX_VALUE_SYM + { + partition_info *part_info= Lex->part_info; + if (part_info->part_type == LIST_PARTITION) + { + my_parse_error(thd, ER_MAXVALUE_IN_VALUES_IN); + MYSQL_YYABORT; + } + if (part_info->add_max_value(thd)) + { + MYSQL_YYABORT; + } + } + | bit_expr + { + LEX *lex= Lex; + partition_info *part_info= lex->part_info; + Item *part_expr= $1; + + if (!lex->safe_to_cache_query) + { + my_parse_error(thd, ER_WRONG_EXPR_IN_PARTITION_FUNC_ERROR); + MYSQL_YYABORT; + } + if (part_info->add_column_list_value(thd, part_expr)) + { + MYSQL_YYABORT; + } + } + ; + + +opt_sub_partition: + /* empty */ + { + partition_info *part_info= Lex->part_info; + if (part_info->num_subparts != 0 && + !part_info->use_default_subpartitions) + { + /* + We come here when we have defined subpartitions on the first + partition but not on all the subsequent partitions. + */ + my_parse_error(thd, ER_PARTITION_WRONG_NO_SUBPART_ERROR); + MYSQL_YYABORT; + } + } + | '(' sub_part_list ')' + { + partition_info *part_info= Lex->part_info; + if (part_info->num_subparts != 0) + { + if (part_info->num_subparts != + part_info->count_curr_subparts) + { + my_parse_error(thd, ER_PARTITION_WRONG_NO_SUBPART_ERROR); + MYSQL_YYABORT; + } + } + else if (part_info->count_curr_subparts > 0) + { + if (part_info->partitions.elements > 1) + { + my_parse_error(thd, ER_PARTITION_WRONG_NO_SUBPART_ERROR); + MYSQL_YYABORT; + } + part_info->num_subparts= part_info->count_curr_subparts; + } + part_info->count_curr_subparts= 0; + } + ; + +sub_part_list: + sub_part_definition {} + | sub_part_list ',' sub_part_definition {} + ; + +sub_part_definition: + SUBPARTITION_SYM + { + partition_info *part_info= Lex->part_info; + partition_element *curr_part= part_info->current_partition; + partition_element *sub_p_elem= new (thd->mem_root) + partition_element(curr_part); + if (part_info->use_default_subpartitions && + part_info->partitions.elements >= 2) + { + /* + create table t1 (a int) + partition by list (a) subpartition by hash (a) + (partition p0 values in (1), + partition p1 values in (2) subpartition sp11); + causes use to arrive since we are on the second + partition, but still use_default_subpartitions + is set. When we come here we're processing at least + the second partition (the current partition processed + have already been put into the partitions list. + */ + my_parse_error(thd, ER_PARTITION_WRONG_NO_SUBPART_ERROR); + MYSQL_YYABORT; + } + if (!sub_p_elem || + curr_part->subpartitions.push_back(sub_p_elem, thd->mem_root)) + { + mem_alloc_error(sizeof(partition_element)); + MYSQL_YYABORT; + } + part_info->curr_part_elem= sub_p_elem; + part_info->use_default_subpartitions= FALSE; + part_info->use_default_num_subpartitions= FALSE; + part_info->count_curr_subparts++; + } + sub_name opt_part_options {} + ; + +sub_name: + ident_or_text + { + if (check_ident_length(&$1)) + MYSQL_YYABORT; + Lex->part_info->curr_part_elem->partition_name= $1.str; + } + ; + +opt_part_options: + /* empty */ {} + | opt_part_option_list {} + ; + +opt_part_option_list: + opt_part_option_list opt_part_option {} + | opt_part_option {} + ; + +opt_part_option: + TABLESPACE opt_equal ident_or_text + { Lex->part_info->curr_part_elem->tablespace_name= $3.str; } + | opt_storage ENGINE_SYM opt_equal storage_engines + { + partition_info *part_info= Lex->part_info; + part_info->curr_part_elem->engine_type= $4; + part_info->default_engine_type= $4; + } + | CONNECTION_SYM opt_equal TEXT_STRING_sys + { + LEX *lex= Lex; + lex->part_info->curr_part_elem->connect_string.str= $3.str; + lex->part_info->curr_part_elem->connect_string.length= $3.length; + } + | NODEGROUP_SYM opt_equal real_ulong_num + { Lex->part_info->curr_part_elem->nodegroup_id= (uint16) $3; } + | MAX_ROWS opt_equal real_ulonglong_num + { Lex->part_info->curr_part_elem->part_max_rows= (ha_rows) $3; } + | MIN_ROWS opt_equal real_ulonglong_num + { Lex->part_info->curr_part_elem->part_min_rows= (ha_rows) $3; } + | DATA_SYM DIRECTORY_SYM opt_equal TEXT_STRING_sys + { Lex->part_info->curr_part_elem->data_file_name= $4.str; } + | INDEX_SYM DIRECTORY_SYM opt_equal TEXT_STRING_sys + { Lex->part_info->curr_part_elem->index_file_name= $4.str; } + | COMMENT_SYM opt_equal TEXT_STRING_sys + { Lex->part_info->curr_part_elem->part_comment= $3.str; } + ; + +/* + End of partition parser part +*/ + +create_select_query_specification: + opt_with_clause SELECT_SYM create_select_part2 create_select_part3 + create_select_part4 + { + Select->set_with_clause($1); + } + ; + +create_select_part2: + { + LEX *lex=Lex; + if (lex->sql_command == SQLCOM_INSERT) + lex->sql_command= SQLCOM_INSERT_SELECT; + else if (lex->sql_command == SQLCOM_REPLACE) + lex->sql_command= SQLCOM_REPLACE_SELECT; + /* + The following work only with the local list, the global list + is created correctly in this case + */ + lex->current_select->table_list.save_and_clear(&lex->save_list); + mysql_init_select(lex); + lex->current_select->parsing_place= SELECT_LIST; + } + select_options select_item_list + { + Select->parsing_place= NO_MATTER; + } + ; + +create_select_part3: + opt_table_expression + | create_select_part3_union_not_ready + ; + +create_select_part3_union_not_ready: + table_expression order_or_limit + | order_or_limit + ; + +create_select_part4: + opt_select_lock_type + { + /* + The following work only with the local list, the global list + is created correctly in this case + */ + Lex->current_select->table_list.push_front(&Lex->save_list); + } + ; + +opt_as: + /* empty */ {} + | AS {} + ; + +opt_create_database_options: + /* empty */ {} + | create_database_options {} + ; + +create_database_options: + create_database_option {} + | create_database_options create_database_option {} + ; + +create_database_option: + default_collation {} + | default_charset {} + ; + +opt_if_not_exists_table_element: + /* empty */ + { + Lex->check_exists= FALSE; + } + | IF_SYM not EXISTS + { + Lex->check_exists= TRUE; + } + ; + +opt_if_not_exists: + /* empty */ + { + $$.init(); + } + | IF_SYM not EXISTS + { + $$.set(DDL_options_st::OPT_IF_NOT_EXISTS); + } + ; + +create_or_replace: + CREATE /* empty */ + { + $$.init(); + } + | CREATE OR_SYM REPLACE + { + $$.set(DDL_options_st::OPT_OR_REPLACE); + } + ; + +opt_create_table_options: + /* empty */ + | create_table_options + ; + +create_table_options_space_separated: + create_table_option + | create_table_option create_table_options_space_separated + ; + +create_table_options: + create_table_option + | create_table_option create_table_options + | create_table_option ',' create_table_options + ; + +create_table_option: + ENGINE_SYM opt_equal storage_engines + { + Lex->create_info.db_type= $3; + Lex->create_info.used_fields|= HA_CREATE_USED_ENGINE; + } + | MAX_ROWS opt_equal ulonglong_num + { + Lex->create_info.max_rows= $3; + Lex->create_info.used_fields|= HA_CREATE_USED_MAX_ROWS; + } + | MIN_ROWS opt_equal ulonglong_num + { + Lex->create_info.min_rows= $3; + Lex->create_info.used_fields|= HA_CREATE_USED_MIN_ROWS; + } + | AVG_ROW_LENGTH opt_equal ulong_num + { + Lex->create_info.avg_row_length=$3; + Lex->create_info.used_fields|= HA_CREATE_USED_AVG_ROW_LENGTH; + } + | PASSWORD_SYM opt_equal TEXT_STRING_sys + { + Lex->create_info.password=$3.str; + Lex->create_info.used_fields|= HA_CREATE_USED_PASSWORD; + } + | COMMENT_SYM opt_equal TEXT_STRING_sys + { + Lex->create_info.comment=$3; + Lex->create_info.used_fields|= HA_CREATE_USED_COMMENT; + } + | AUTO_INC opt_equal ulonglong_num + { + Lex->create_info.auto_increment_value=$3; + Lex->create_info.used_fields|= HA_CREATE_USED_AUTO; + } + | PACK_KEYS_SYM opt_equal ulong_num + { + switch($3) { + case 0: + Lex->create_info.table_options|= HA_OPTION_NO_PACK_KEYS; + break; + case 1: + Lex->create_info.table_options|= HA_OPTION_PACK_KEYS; + break; + default: + my_parse_error(thd, ER_SYNTAX_ERROR); + MYSQL_YYABORT; + } + Lex->create_info.used_fields|= HA_CREATE_USED_PACK_KEYS; + } + | PACK_KEYS_SYM opt_equal DEFAULT + { + Lex->create_info.table_options&= + ~(HA_OPTION_PACK_KEYS | HA_OPTION_NO_PACK_KEYS); + Lex->create_info.used_fields|= HA_CREATE_USED_PACK_KEYS; + } + | STATS_AUTO_RECALC_SYM opt_equal ulong_num + { + switch($3) { + case 0: + Lex->create_info.stats_auto_recalc= HA_STATS_AUTO_RECALC_OFF; + break; + case 1: + Lex->create_info.stats_auto_recalc= HA_STATS_AUTO_RECALC_ON; + break; + default: + my_parse_error(thd, ER_SYNTAX_ERROR); + MYSQL_YYABORT; + } + Lex->create_info.used_fields|= HA_CREATE_USED_STATS_AUTO_RECALC; + } + | STATS_AUTO_RECALC_SYM opt_equal DEFAULT + { + Lex->create_info.stats_auto_recalc= HA_STATS_AUTO_RECALC_DEFAULT; + Lex->create_info.used_fields|= HA_CREATE_USED_STATS_AUTO_RECALC; + } + | STATS_PERSISTENT_SYM opt_equal ulong_num + { + switch($3) { + case 0: + Lex->create_info.table_options|= HA_OPTION_NO_STATS_PERSISTENT; + break; + case 1: + Lex->create_info.table_options|= HA_OPTION_STATS_PERSISTENT; + break; + default: + my_parse_error(thd, ER_SYNTAX_ERROR); + MYSQL_YYABORT; + } + Lex->create_info.used_fields|= HA_CREATE_USED_STATS_PERSISTENT; + } + | STATS_PERSISTENT_SYM opt_equal DEFAULT + { + Lex->create_info.table_options&= + ~(HA_OPTION_STATS_PERSISTENT | HA_OPTION_NO_STATS_PERSISTENT); + Lex->create_info.used_fields|= HA_CREATE_USED_STATS_PERSISTENT; + } + | STATS_SAMPLE_PAGES_SYM opt_equal ulong_num + { + /* From user point of view STATS_SAMPLE_PAGES can be specified as + STATS_SAMPLE_PAGES=N (where 0<N<=65535, it does not make sense to + scan 0 pages) or STATS_SAMPLE_PAGES=default. Internally we record + =default as 0. See create_frm() in sql/table.cc, we use only two + bytes for stats_sample_pages and this is why we do not allow + larger values. 65535 pages, 16kb each means to sample 1GB, which + is impractical. If at some point this needs to be extended, then + we can store the higher bits from stats_sample_pages in .frm too. */ + if ($3 == 0 || $3 > 0xffff) + { + my_parse_error(thd, ER_SYNTAX_ERROR); + MYSQL_YYABORT; + } + Lex->create_info.stats_sample_pages=$3; + Lex->create_info.used_fields|= HA_CREATE_USED_STATS_SAMPLE_PAGES; + } + | STATS_SAMPLE_PAGES_SYM opt_equal DEFAULT + { + Lex->create_info.stats_sample_pages=0; + Lex->create_info.used_fields|= HA_CREATE_USED_STATS_SAMPLE_PAGES; + } + | CHECKSUM_SYM opt_equal ulong_num + { + Lex->create_info.table_options|= $3 ? HA_OPTION_CHECKSUM : HA_OPTION_NO_CHECKSUM; + Lex->create_info.used_fields|= HA_CREATE_USED_CHECKSUM; + } + | TABLE_CHECKSUM_SYM opt_equal ulong_num + { + Lex->create_info.table_options|= $3 ? HA_OPTION_CHECKSUM : HA_OPTION_NO_CHECKSUM; + Lex->create_info.used_fields|= HA_CREATE_USED_CHECKSUM; + } + | PAGE_CHECKSUM_SYM opt_equal choice + { + Lex->create_info.used_fields|= HA_CREATE_USED_PAGE_CHECKSUM; + Lex->create_info.page_checksum= $3; + } + | DELAY_KEY_WRITE_SYM opt_equal ulong_num + { + Lex->create_info.table_options|= $3 ? HA_OPTION_DELAY_KEY_WRITE : HA_OPTION_NO_DELAY_KEY_WRITE; + Lex->create_info.used_fields|= HA_CREATE_USED_DELAY_KEY_WRITE; + } + | ROW_FORMAT_SYM opt_equal row_types + { + Lex->create_info.row_type= $3; + Lex->create_info.used_fields|= HA_CREATE_USED_ROW_FORMAT; + } + | UNION_SYM opt_equal + { + Lex->select_lex.table_list.save_and_clear(&Lex->save_list); + } + '(' opt_table_list ')' + { + /* + Move the union list to the merge_list and exclude its tables + from the global list. + */ + LEX *lex=Lex; + lex->create_info.merge_list= lex->select_lex.table_list; + lex->select_lex.table_list= lex->save_list; + /* + When excluding union list from the global list we assume that + elements of the former immediately follow elements which represent + table being created/altered and parent tables. + */ + TABLE_LIST *last_non_sel_table= lex->create_last_non_select_table; + DBUG_ASSERT(last_non_sel_table->next_global == + lex->create_info.merge_list.first); + last_non_sel_table->next_global= 0; + Lex->query_tables_last= &last_non_sel_table->next_global; + + lex->create_info.used_fields|= HA_CREATE_USED_UNION; + } + | default_charset + | default_collation + | INSERT_METHOD opt_equal merge_insert_types + { + Lex->create_info.merge_insert_method= $3; + Lex->create_info.used_fields|= HA_CREATE_USED_INSERT_METHOD; + } + | DATA_SYM DIRECTORY_SYM opt_equal TEXT_STRING_sys + { + Lex->create_info.data_file_name= $4.str; + Lex->create_info.used_fields|= HA_CREATE_USED_DATADIR; + } + | INDEX_SYM DIRECTORY_SYM opt_equal TEXT_STRING_sys + { + Lex->create_info.index_file_name= $4.str; + Lex->create_info.used_fields|= HA_CREATE_USED_INDEXDIR; + } + | TABLESPACE ident + {Lex->create_info.tablespace= $2.str;} + | STORAGE_SYM DISK_SYM + {Lex->create_info.storage_media= HA_SM_DISK;} + | STORAGE_SYM MEMORY_SYM + {Lex->create_info.storage_media= HA_SM_MEMORY;} + | CONNECTION_SYM opt_equal TEXT_STRING_sys + { + Lex->create_info.connect_string.str= $3.str; + Lex->create_info.connect_string.length= $3.length; + Lex->create_info.used_fields|= HA_CREATE_USED_CONNECTION; + } + | KEY_BLOCK_SIZE opt_equal ulong_num + { + Lex->create_info.used_fields|= HA_CREATE_USED_KEY_BLOCK_SIZE; + Lex->create_info.key_block_size= $3; + } + | TRANSACTIONAL_SYM opt_equal choice + { + Lex->create_info.used_fields|= HA_CREATE_USED_TRANSACTIONAL; + Lex->create_info.transactional= $3; + } + | IDENT_sys equal TEXT_STRING_sys + { + if ($3.length > ENGINE_OPTION_MAX_LENGTH) + my_yyabort_error((ER_VALUE_TOO_LONG, MYF(0), $1.str)); + new (thd->mem_root) + engine_option_value($1, $3, true, &Lex->create_info.option_list, + &Lex->option_list_last); + } + | IDENT_sys equal ident + { + if ($3.length > ENGINE_OPTION_MAX_LENGTH) + my_yyabort_error((ER_VALUE_TOO_LONG, MYF(0), $1.str)); + new (thd->mem_root) + engine_option_value($1, $3, false, &Lex->create_info.option_list, + &Lex->option_list_last); + } + | IDENT_sys equal real_ulonglong_num + { + new (thd->mem_root) + engine_option_value($1, $3, &Lex->create_info.option_list, + &Lex->option_list_last, thd->mem_root); + } + | IDENT_sys equal DEFAULT + { + new (thd->mem_root) + engine_option_value($1, &Lex->create_info.option_list, + &Lex->option_list_last); + } + ; + +default_charset: + opt_default charset opt_equal charset_name_or_default + { + if (Lex->create_info.add_table_option_default_charset($4)) + MYSQL_YYABORT; + } + ; + +default_collation: + opt_default COLLATE_SYM opt_equal collation_name_or_default + { + HA_CREATE_INFO *cinfo= &Lex->create_info; + if ((cinfo->used_fields & HA_CREATE_USED_DEFAULT_CHARSET) && + cinfo->default_table_charset && $4 && + !($4= merge_charset_and_collation(cinfo->default_table_charset, + $4))) + { + MYSQL_YYABORT; + } + + Lex->create_info.default_table_charset= $4; + Lex->create_info.used_fields|= HA_CREATE_USED_DEFAULT_CHARSET; + } + ; + +storage_engines: + ident_or_text + { + plugin_ref plugin= ha_resolve_by_name(thd, &$1, + thd->lex->create_info.tmp_table()); + + if (plugin) + $$= plugin_hton(plugin); + else + { + if (thd->variables.sql_mode & MODE_NO_ENGINE_SUBSTITUTION) + my_yyabort_error((ER_UNKNOWN_STORAGE_ENGINE, MYF(0), $1.str)); + $$= 0; + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_UNKNOWN_STORAGE_ENGINE, + ER_THD(thd, ER_UNKNOWN_STORAGE_ENGINE), + $1.str); + } + } + ; + +known_storage_engines: + ident_or_text + { + plugin_ref plugin; + if ((plugin= ha_resolve_by_name(thd, &$1, false))) + $$= plugin_hton(plugin); + else + my_yyabort_error((ER_UNKNOWN_STORAGE_ENGINE, MYF(0), $1.str)); + } + ; + +row_types: + DEFAULT { $$= ROW_TYPE_DEFAULT; } + | FIXED_SYM { $$= ROW_TYPE_FIXED; } + | DYNAMIC_SYM { $$= ROW_TYPE_DYNAMIC; } + | COMPRESSED_SYM { $$= ROW_TYPE_COMPRESSED; } + | REDUNDANT_SYM { $$= ROW_TYPE_REDUNDANT; } + | COMPACT_SYM { $$= ROW_TYPE_COMPACT; } + | PAGE_SYM { $$= ROW_TYPE_PAGE; } + ; + +merge_insert_types: + NO_SYM { $$= MERGE_INSERT_DISABLED; } + | FIRST_SYM { $$= MERGE_INSERT_TO_FIRST; } + | LAST_SYM { $$= MERGE_INSERT_TO_LAST; } + ; + +udf_type: + STRING_SYM {$$ = (int) STRING_RESULT; } + | REAL {$$ = (int) REAL_RESULT; } + | DECIMAL_SYM {$$ = (int) DECIMAL_RESULT; } + | INT_SYM {$$ = (int) INT_RESULT; } + ; + + +create_field_list: + field_list + { + Lex->create_last_non_select_table= Lex->last_table(); + } + ; + +field_list: + field_list_item + | field_list ',' field_list_item + ; + +field_list_item: + column_def { } + | key_def + | constraint_def + ; + +column_def: + field_spec + { $$= $1; } + | field_spec references + { $$= $1; } + ; + +key_def: + key_or_index opt_if_not_exists opt_ident opt_USING_key_algorithm + { + Lex->option_list= NULL; + if (Lex->add_key(Key::MULTIPLE, $3, $4, $2)) + MYSQL_YYABORT; + } + '(' key_list ')' normal_key_options { } + | key_or_index opt_if_not_exists ident TYPE_SYM btree_or_rtree + { + Lex->option_list= NULL; + if (Lex->add_key(Key::MULTIPLE, $3, $5, $2)) + MYSQL_YYABORT; + } + '(' key_list ')' normal_key_options { } + | fulltext opt_key_or_index opt_if_not_exists opt_ident + { + Lex->option_list= NULL; + if (Lex->add_key($1, $4, HA_KEY_ALG_UNDEF, $3)) + MYSQL_YYABORT; + } + '(' key_list ')' fulltext_key_options { } + | spatial opt_key_or_index opt_if_not_exists opt_ident + { + Lex->option_list= NULL; + if (Lex->add_key($1, $4, HA_KEY_ALG_UNDEF, $3)) + MYSQL_YYABORT; + } + '(' key_list ')' spatial_key_options { } + | opt_constraint constraint_key_type + opt_if_not_exists opt_ident + opt_USING_key_algorithm + { + Lex->option_list= NULL; + if (Lex->add_key($2, $4.str ? $4 : $1, $5, $3)) + MYSQL_YYABORT; + } + '(' key_list ')' normal_key_options { } + | opt_constraint constraint_key_type opt_if_not_exists ident + TYPE_SYM btree_or_rtree + { + Lex->option_list= NULL; + if (Lex->add_key($2, $4.str ? $4 : $1, $6, $3)) + MYSQL_YYABORT; + } + '(' key_list ')' normal_key_options { } + | opt_constraint FOREIGN KEY_SYM opt_if_not_exists opt_ident + { + if (Lex->check_add_key($4) || + !(Lex->last_key= (new (thd->mem_root) + Key(Key::MULTIPLE, $1.str ? $1 : $5, + HA_KEY_ALG_UNDEF, true, $4)))) + MYSQL_YYABORT; + Lex->option_list= NULL; + } + '(' key_list ')' references + { + LEX *lex=Lex; + Key *key= (new (thd->mem_root) + Foreign_key($5.str ? $5 : $1, + lex->last_key->columns, + $10->db, + $10->table, + lex->ref_list, + lex->fk_delete_opt, + lex->fk_update_opt, + lex->fk_match_option, + $4)); + if (key == NULL) + MYSQL_YYABORT; + /* + handle_if_exists_options() expectes the two keys in this order: + the Foreign_key, followed by its auto-generated Key. + */ + lex->alter_info.key_list.push_back(key, thd->mem_root); + lex->alter_info.key_list.push_back(Lex->last_key, thd->mem_root); + lex->option_list= NULL; + + /* Only used for ALTER TABLE. Ignored otherwise. */ + lex->alter_info.flags|= Alter_info::ADD_FOREIGN_KEY; + } + ; + +constraint_def: + opt_constraint check_constraint + { + Lex->add_constraint(&$1, $2, FALSE); + } + ; + +opt_check_constraint: + /* empty */ { $$= (Virtual_column_info*) 0; } + | check_constraint { $$= $1;} + ; + +check_constraint: + CHECK_SYM '(' expr ')' + { + Virtual_column_info *v= + add_virtual_expression(thd, $3); + if (!v) + { + MYSQL_YYABORT; + } + $$= v; + } + ; + +opt_constraint: + /* empty */ { $$= null_lex_str; } + | constraint { $$= $1; } + ; + +constraint: + CONSTRAINT opt_ident { $$=$2; } + ; + +field_spec: + field_ident + { + LEX *lex=Lex; + Create_field *f= new (thd->mem_root) Create_field(); + + if (check_string_char_length(&$1, 0, NAME_CHAR_LEN, + system_charset_info, 1)) + my_yyabort_error((ER_TOO_LONG_IDENT, MYF(0), $1.str)); + + if (!f) + MYSQL_YYABORT; + + lex->init_last_field(f, $1.str, NULL); + $<create_field>$= f; + } + field_type_or_serial opt_check_constraint + { + LEX *lex=Lex; + $$= $<create_field>2; + + $$->check_constraint= $4; + + if ($$->check(thd)) + MYSQL_YYABORT; + + lex->alter_info.create_list.push_back($$, thd->mem_root); + + $$->create_if_not_exists= Lex->check_exists; + if ($$->flags & PRI_KEY_FLAG) + lex->add_key_to_list(&$1, Key::PRIMARY, lex->check_exists); + else if ($$->flags & UNIQUE_KEY_FLAG) + lex->add_key_to_list(&$1, Key::UNIQUE, lex->check_exists); + } + ; + +field_type_or_serial: + field_type { Lex->last_field->set_attributes($1, Lex->charset); } + field_def + | SERIAL_SYM + { + Lex->last_field->sql_type= MYSQL_TYPE_LONGLONG; + Lex->last_field->flags|= AUTO_INCREMENT_FLAG | NOT_NULL_FLAG + | UNSIGNED_FLAG | UNIQUE_KEY_FLAG; + } + opt_serial_attribute + ; + +opt_serial_attribute: + /* empty */ {} + | opt_serial_attribute_list {} + ; + +opt_serial_attribute_list: + opt_serial_attribute_list serial_attribute {} + | serial_attribute + ; + + +field_def: + opt_attribute + | opt_generated_always AS virtual_column_func + { + Lex->last_field->vcol_info= $3; + Lex->last_field->flags&= ~NOT_NULL_FLAG; // undo automatic NOT NULL for timestamps + } + vcol_opt_specifier vcol_opt_attribute + ; + +opt_generated_always: + /* empty */ {} + | GENERATED_SYM ALWAYS_SYM {} + ; + +vcol_opt_specifier: + /* empty */ + { + Lex->last_field->vcol_info->set_stored_in_db_flag(FALSE); + } + | VIRTUAL_SYM + { + Lex->last_field->vcol_info->set_stored_in_db_flag(FALSE); + } + | PERSISTENT_SYM + { + Lex->last_field->vcol_info->set_stored_in_db_flag(TRUE); + } + | STORED_SYM + { + Lex->last_field->vcol_info->set_stored_in_db_flag(TRUE); + } + ; + +vcol_opt_attribute: + /* empty */ {} + | vcol_opt_attribute_list {} + ; + +vcol_opt_attribute_list: + vcol_opt_attribute_list vcol_attribute {} + | vcol_attribute + ; + +vcol_attribute: + UNIQUE_SYM + { + LEX *lex=Lex; + lex->last_field->flags|= UNIQUE_KEY_FLAG; + lex->alter_info.flags|= Alter_info::ALTER_ADD_INDEX; + } + | UNIQUE_SYM KEY_SYM + { + LEX *lex=Lex; + lex->last_field->flags|= UNIQUE_KEY_FLAG; + lex->alter_info.flags|= Alter_info::ALTER_ADD_INDEX; + } + | COMMENT_SYM TEXT_STRING_sys { Lex->last_field->comment= $2; } + ; + +parse_vcol_expr: + PARSE_VCOL_EXPR_SYM + { + /* + "PARSE_VCOL_EXPR" can only be used by the SQL server + when reading a '*.frm' file. + Prevent the end user from invoking this command. + */ + MYSQL_YYABORT_UNLESS(Lex->parse_vcol_expr); + } + expr + { + Virtual_column_info *v= add_virtual_expression(thd, $3); + if (!v) + MYSQL_YYABORT; + Lex->last_field->vcol_info= v; + } + ; + +parenthesized_expr: + subselect + { + $$= new (thd->mem_root) Item_singlerow_subselect(thd, $1); + if ($$ == NULL) + MYSQL_YYABORT; + } + | expr + | expr ',' expr_list + { + $3->push_front($1, thd->mem_root); + $$= new (thd->mem_root) Item_row(thd, *$3); + if ($$ == NULL) + MYSQL_YYABORT; + } + ; + +virtual_column_func: + '(' parenthesized_expr ')' + { + Virtual_column_info *v= + add_virtual_expression(thd, $2); + if (!v) + { + MYSQL_YYABORT; + } + $$= v; + } + ; + +expr_or_literal: column_default_non_parenthesized_expr | signed_literal ; + +column_default_expr: + virtual_column_func + | expr_or_literal + { + if (!($$= add_virtual_expression(thd, $1))) + MYSQL_YYABORT; + } + ; + +field_type: + int_type opt_field_length field_options { $$.set($1, $2); } + | real_type opt_precision field_options { $$.set($1, $2); } + | FLOAT_SYM float_options field_options + { + $$.set(MYSQL_TYPE_FLOAT, $2); + if ($2.length() && !$2.dec()) + { + int err; + ulonglong tmp_length= my_strtoll10($2.length(), NULL, &err); + if (err || tmp_length > PRECISION_FOR_DOUBLE) + my_yyabort_error((ER_WRONG_FIELD_SPEC, MYF(0), + Lex->last_field->field_name)); + if (tmp_length > PRECISION_FOR_FLOAT) + $$.set(MYSQL_TYPE_DOUBLE); + else + $$.set(MYSQL_TYPE_FLOAT); + } + } + | BIT_SYM opt_field_length_default_1 + { + $$.set(MYSQL_TYPE_BIT, $2); + } + | BOOL_SYM + { + $$.set(MYSQL_TYPE_TINY, "1"); + } + | BOOLEAN_SYM + { + $$.set(MYSQL_TYPE_TINY, "1"); + } + | char opt_field_length_default_1 opt_binary + { + $$.set(MYSQL_TYPE_STRING, $2); + } + | nchar opt_field_length_default_1 opt_bin_mod + { + $$.set(MYSQL_TYPE_STRING, $2); + bincmp_collation(national_charset_info, $3); + } + | BINARY opt_field_length_default_1 + { + Lex->charset=&my_charset_bin; + $$.set(MYSQL_TYPE_STRING, $2); + } + | varchar field_length opt_binary + { + $$.set(MYSQL_TYPE_VARCHAR, $2); + } + | nvarchar field_length opt_bin_mod + { + $$.set(MYSQL_TYPE_VARCHAR, $2); + bincmp_collation(national_charset_info, $3); + } + | VARBINARY field_length + { + Lex->charset=&my_charset_bin; + $$.set(MYSQL_TYPE_VARCHAR, $2); + } + | YEAR_SYM opt_field_length field_options + { + if ($2) + { + errno= 0; + ulong length= strtoul($2, NULL, 10); + if (errno == 0 && length <= MAX_FIELD_BLOBLENGTH && length != 4) + { + char buff[sizeof("YEAR()") + MY_INT64_NUM_DECIMAL_DIGITS + 1]; + my_snprintf(buff, sizeof(buff), "YEAR(%lu)", length); + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, + ER_WARN_DEPRECATED_SYNTAX, + ER_THD(thd, ER_WARN_DEPRECATED_SYNTAX), + buff, "YEAR(4)"); + } + } + $$.set(MYSQL_TYPE_YEAR, $2); + } + | DATE_SYM + { $$.set(MYSQL_TYPE_DATE); } + | TIME_SYM opt_field_length + { $$.set(opt_mysql56_temporal_format ? + MYSQL_TYPE_TIME2 : MYSQL_TYPE_TIME, $2); } + | TIMESTAMP opt_field_length + { + if (thd->variables.sql_mode & MODE_MAXDB) + $$.set(opt_mysql56_temporal_format ? + MYSQL_TYPE_DATETIME2 : MYSQL_TYPE_DATETIME, $2); + else + { + /* + Unlike other types TIMESTAMP fields are NOT NULL by default. + Unless --explicit-defaults-for-timestamp is given. + */ + if (!opt_explicit_defaults_for_timestamp) + Lex->last_field->flags|= NOT_NULL_FLAG; + $$.set(opt_mysql56_temporal_format ? MYSQL_TYPE_TIMESTAMP2 + : MYSQL_TYPE_TIMESTAMP, $2); + } + } + | DATETIME opt_field_length + { $$.set(opt_mysql56_temporal_format ? + MYSQL_TYPE_DATETIME2 : MYSQL_TYPE_DATETIME, $2); } + | TINYBLOB + { + Lex->charset=&my_charset_bin; + $$.set(MYSQL_TYPE_TINY_BLOB); + } + | BLOB_SYM opt_field_length + { + Lex->charset=&my_charset_bin; + $$.set(MYSQL_TYPE_BLOB, $2); + } + | spatial_type float_options srid_option + { +#ifdef HAVE_SPATIAL + Lex->charset=&my_charset_bin; + Lex->last_field->geom_type= $1; + $$.set(MYSQL_TYPE_GEOMETRY, $2); +#else + my_yyabort_error((ER_FEATURE_DISABLED, MYF(0), sym_group_geom.name, + sym_group_geom.needed_define)); +#endif + } + | MEDIUMBLOB + { + Lex->charset=&my_charset_bin; + $$.set(MYSQL_TYPE_MEDIUM_BLOB); + } + | LONGBLOB + { + Lex->charset=&my_charset_bin; + $$.set(MYSQL_TYPE_LONG_BLOB); + } + | LONG_SYM VARBINARY + { + Lex->charset=&my_charset_bin; + $$.set(MYSQL_TYPE_MEDIUM_BLOB); + } + | LONG_SYM varchar opt_binary + { $$.set(MYSQL_TYPE_MEDIUM_BLOB); } + | TINYTEXT opt_binary + { $$.set(MYSQL_TYPE_TINY_BLOB); } + | TEXT_SYM opt_field_length opt_binary + { $$.set(MYSQL_TYPE_BLOB, $2); } + | MEDIUMTEXT opt_binary + { $$.set(MYSQL_TYPE_MEDIUM_BLOB); } + | LONGTEXT opt_binary + { $$.set(MYSQL_TYPE_LONG_BLOB); } + | DECIMAL_SYM float_options field_options + { $$.set(MYSQL_TYPE_NEWDECIMAL, $2);} + | NUMBER_SYM float_options field_options + { $$.set(MYSQL_TYPE_NEWDECIMAL, $2);} + | NUMERIC_SYM float_options field_options + { $$.set(MYSQL_TYPE_NEWDECIMAL, $2);} + | FIXED_SYM float_options field_options + { $$.set(MYSQL_TYPE_NEWDECIMAL, $2);} + | ENUM '(' string_list ')' opt_binary + { $$.set(MYSQL_TYPE_ENUM); } + | SET '(' string_list ')' opt_binary + { $$.set(MYSQL_TYPE_SET); } + | LONG_SYM opt_binary + { $$.set(MYSQL_TYPE_MEDIUM_BLOB); } + ; + +spatial_type: + GEOMETRY_SYM { $$= Field::GEOM_GEOMETRY; } + | GEOMETRYCOLLECTION { $$= Field::GEOM_GEOMETRYCOLLECTION; } + | POINT_SYM { $$= Field::GEOM_POINT; } + | MULTIPOINT { $$= Field::GEOM_MULTIPOINT; } + | LINESTRING { $$= Field::GEOM_LINESTRING; } + | MULTILINESTRING { $$= Field::GEOM_MULTILINESTRING; } + | POLYGON { $$= Field::GEOM_POLYGON; } + | MULTIPOLYGON { $$= Field::GEOM_MULTIPOLYGON; } + ; + +char: + CHAR_SYM {} + ; + +nchar: + NCHAR_SYM {} + | NATIONAL_SYM CHAR_SYM {} + ; + +varchar: + char VARYING {} + | VARCHAR {} + ; + +nvarchar: + NATIONAL_SYM VARCHAR {} + | NVARCHAR_SYM {} + | NCHAR_SYM VARCHAR {} + | NATIONAL_SYM CHAR_SYM VARYING {} + | NCHAR_SYM VARYING {} + ; + +int_type: + INT_SYM { $$=MYSQL_TYPE_LONG; } + | TINYINT { $$=MYSQL_TYPE_TINY; } + | SMALLINT { $$=MYSQL_TYPE_SHORT; } + | MEDIUMINT { $$=MYSQL_TYPE_INT24; } + | BIGINT { $$=MYSQL_TYPE_LONGLONG; } + ; + +real_type: + REAL + { + $$= thd->variables.sql_mode & MODE_REAL_AS_FLOAT ? + MYSQL_TYPE_FLOAT : MYSQL_TYPE_DOUBLE; + } + | DOUBLE_SYM + { $$=MYSQL_TYPE_DOUBLE; } + | DOUBLE_SYM PRECISION + { $$=MYSQL_TYPE_DOUBLE; } + ; + +srid_option: + /* empty */ + { Lex->last_field->srid= 0; } + | + REF_SYSTEM_ID_SYM '=' NUM + { + Lex->last_field->srid=atoi($3.str); + } + ; + +float_options: + /* empty */ { $$.set(0, 0); } + | field_length { $$.set($1, 0); } + | precision { $$= $1; } + ; + +precision: + '(' NUM ',' NUM ')' { $$.set($2.str, $4.str); } + ; + +field_options: + /* empty */ {} + | field_opt_list {} + ; + +field_opt_list: + field_opt_list field_option {} + | field_option {} + ; + +field_option: + SIGNED_SYM {} + | UNSIGNED { Lex->last_field->flags|= UNSIGNED_FLAG;} + | ZEROFILL { Lex->last_field->flags|= UNSIGNED_FLAG | ZEROFILL_FLAG; } + ; + +field_length: + '(' LONG_NUM ')' { $$= $2.str; } + | '(' ULONGLONG_NUM ')' { $$= $2.str; } + | '(' DECIMAL_NUM ')' { $$= $2.str; } + | '(' NUM ')' { $$= $2.str; }; + +opt_field_length: + /* empty */ { $$= (char*) 0; /* use default length */ } + | field_length { $$= $1; } + +opt_field_length_default_1: + /* empty */ { $$= (char*) "1"; } + | field_length { $$= $1; } + +opt_precision: + /* empty */ { $$.set(0, 0); } + | precision { $$= $1; } + ; + +opt_attribute: + /* empty */ {} + | opt_attribute_list {} + ; + +opt_attribute_list: + opt_attribute_list attribute {} + | attribute + ; + +attribute: + NULL_SYM { Lex->last_field->flags&= ~ NOT_NULL_FLAG; } + | DEFAULT column_default_expr { Lex->last_field->default_value= $2; } + | ON UPDATE_SYM NOW_SYM opt_default_time_precision + { + Item *item= new (thd->mem_root) Item_func_now_local(thd, $4); + if (item == NULL) + MYSQL_YYABORT; + Lex->last_field->on_update= item; + } + | AUTO_INC { Lex->last_field->flags|= AUTO_INCREMENT_FLAG | NOT_NULL_FLAG; } + | SERIAL_SYM DEFAULT VALUE_SYM + { + LEX *lex=Lex; + lex->last_field->flags|= AUTO_INCREMENT_FLAG | NOT_NULL_FLAG | UNIQUE_KEY_FLAG; + lex->alter_info.flags|= Alter_info::ALTER_ADD_INDEX; + } + | COLLATE_SYM collation_name + { + if (Lex->charset && !my_charset_same(Lex->charset,$2)) + my_yyabort_error((ER_COLLATION_CHARSET_MISMATCH, MYF(0), + $2->name,Lex->charset->csname)); + Lex->last_field->charset= $2; + } + | serial_attribute + ; + +serial_attribute: + not NULL_SYM { Lex->last_field->flags|= NOT_NULL_FLAG; } + | opt_primary KEY_SYM + { + LEX *lex=Lex; + lex->last_field->flags|= PRI_KEY_FLAG | NOT_NULL_FLAG; + lex->alter_info.flags|= Alter_info::ALTER_ADD_INDEX; + } + | UNIQUE_SYM + { + LEX *lex=Lex; + lex->last_field->flags|= UNIQUE_KEY_FLAG; + lex->alter_info.flags|= Alter_info::ALTER_ADD_INDEX; + } + | UNIQUE_SYM KEY_SYM + { + LEX *lex=Lex; + lex->last_field->flags|= UNIQUE_KEY_FLAG; + lex->alter_info.flags|= Alter_info::ALTER_ADD_INDEX; + } + | COMMENT_SYM TEXT_STRING_sys { Lex->last_field->comment= $2; } + | IDENT_sys equal TEXT_STRING_sys + { + if ($3.length > ENGINE_OPTION_MAX_LENGTH) + my_yyabort_error((ER_VALUE_TOO_LONG, MYF(0), $1.str)); + new (thd->mem_root) + engine_option_value($1, $3, true, &Lex->last_field->option_list, + &Lex->option_list_last); + } + | IDENT_sys equal ident + { + if ($3.length > ENGINE_OPTION_MAX_LENGTH) + my_yyabort_error((ER_VALUE_TOO_LONG, MYF(0), $1.str)); + new (thd->mem_root) + engine_option_value($1, $3, false, &Lex->last_field->option_list, + &Lex->option_list_last); + } + | IDENT_sys equal real_ulonglong_num + { + new (thd->mem_root) + engine_option_value($1, $3, &Lex->last_field->option_list, + &Lex->option_list_last, thd->mem_root); + } + | IDENT_sys equal DEFAULT + { + new (thd->mem_root) + engine_option_value($1, &Lex->last_field->option_list, &Lex->option_list_last); + } + ; + + +type_with_opt_collate: + field_type opt_collate + { + $$= $1; + + if ($2) + { + if (!(Lex->charset= merge_charset_and_collation(Lex->charset, $2))) + MYSQL_YYABORT; + } + Lex->last_field->set_attributes($1, Lex->charset); + } + ; + +charset: + CHAR_SYM SET {} + | CHARSET {} + ; + +charset_name: + ident_or_text + { + if (!($$=get_charset_by_csname($1.str,MY_CS_PRIMARY,MYF(0)))) + my_yyabort_error((ER_UNKNOWN_CHARACTER_SET, MYF(0), $1.str)); + } + | BINARY { $$= &my_charset_bin; } + ; + +charset_name_or_default: + charset_name { $$=$1; } + | DEFAULT { $$=NULL; } + ; + +opt_load_data_charset: + /* Empty */ { $$= NULL; } + | charset charset_name_or_default { $$= $2; } + ; + +old_or_new_charset_name: + ident_or_text + { + if (!($$=get_charset_by_csname($1.str,MY_CS_PRIMARY,MYF(0))) && + !($$=get_old_charset_by_name($1.str))) + my_yyabort_error((ER_UNKNOWN_CHARACTER_SET, MYF(0), $1.str)); + } + | BINARY { $$= &my_charset_bin; } + ; + +old_or_new_charset_name_or_default: + old_or_new_charset_name { $$=$1; } + | DEFAULT { $$=NULL; } + ; + +collation_name: + ident_or_text + { + if (!($$= mysqld_collation_get_by_name($1.str))) + MYSQL_YYABORT; + } + ; + +opt_collate: + /* empty */ { $$=NULL; } + | COLLATE_SYM collation_name_or_default { $$=$2; } + ; + +collation_name_or_default: + collation_name { $$=$1; } + | DEFAULT { $$=NULL; } + ; + +opt_default: + /* empty */ {} + | DEFAULT {} + ; + +charset_or_alias: + charset charset_name { $$= $2; } + | ASCII_SYM { $$= &my_charset_latin1; } + | UNICODE_SYM + { + if (!($$= get_charset_by_csname("ucs2", MY_CS_PRIMARY,MYF(0)))) + my_yyabort_error((ER_UNKNOWN_CHARACTER_SET, MYF(0), "ucs2")); + } + ; + +opt_binary: + /* empty */ { bincmp_collation(NULL, false); } + | BYTE_SYM { bincmp_collation(&my_charset_bin, false); } + | charset_or_alias opt_bin_mod { bincmp_collation($1, $2); } + | BINARY { bincmp_collation(NULL, true); } + | BINARY charset_or_alias { bincmp_collation($2, true); } + ; + +opt_bin_mod: + /* empty */ { $$= false; } + | BINARY { $$= true; } + ; + +ws_nweights: + '(' real_ulong_num + { + if ($2 == 0) + { + my_parse_error(thd, ER_SYNTAX_ERROR); + MYSQL_YYABORT; + } + } + ')' + { $$= $2; } + ; + +ws_level_flag_desc: + ASC { $$= 0; } + | DESC { $$= 1 << MY_STRXFRM_DESC_SHIFT; } + ; + +ws_level_flag_reverse: + REVERSE_SYM { $$= 1 << MY_STRXFRM_REVERSE_SHIFT; } ; + +ws_level_flags: + /* empty */ { $$= 0; } + | ws_level_flag_desc { $$= $1; } + | ws_level_flag_desc ws_level_flag_reverse { $$= $1 | $2; } + | ws_level_flag_reverse { $$= $1 ; } + ; + +ws_level_number: + real_ulong_num + { + $$= $1 < 1 ? 1 : ($1 > MY_STRXFRM_NLEVELS ? MY_STRXFRM_NLEVELS : $1); + $$--; + } + ; + +ws_level_list_item: + ws_level_number ws_level_flags + { + $$= (1 | $2) << $1; + } + ; + +ws_level_list: + ws_level_list_item { $$= $1; } + | ws_level_list ',' ws_level_list_item { $$|= $3; } + ; + +ws_level_range: + ws_level_number '-' ws_level_number + { + uint start= $1; + uint end= $3; + for ($$= 0; start <= end; start++) + $$|= (1 << start); + } + ; + +ws_level_list_or_range: + ws_level_list { $$= $1; } + | ws_level_range { $$= $1; } + ; + +opt_ws_levels: + /* empty*/ { $$= 0; } + | LEVEL_SYM ws_level_list_or_range { $$= $2; } + ; + +opt_primary: + /* empty */ + | PRIMARY_SYM + ; + +references: + REFERENCES + table_ident + opt_ref_list + opt_match_clause + opt_on_update_delete + { + $$=$2; + } + ; + +opt_ref_list: + /* empty */ + { Lex->ref_list.empty(); } + | '(' ref_list ')' + ; + +ref_list: + ref_list ',' ident + { + Key_part_spec *key= new (thd->mem_root) Key_part_spec($3, 0); + if (key == NULL) + MYSQL_YYABORT; + Lex->ref_list.push_back(key, thd->mem_root); + } + | ident + { + Key_part_spec *key= new (thd->mem_root) Key_part_spec($1, 0); + if (key == NULL) + MYSQL_YYABORT; + LEX *lex= Lex; + lex->ref_list.empty(); + lex->ref_list.push_back(key, thd->mem_root); + } + ; + +opt_match_clause: + /* empty */ + { Lex->fk_match_option= Foreign_key::FK_MATCH_UNDEF; } + | MATCH FULL + { Lex->fk_match_option= Foreign_key::FK_MATCH_FULL; } + | MATCH PARTIAL + { Lex->fk_match_option= Foreign_key::FK_MATCH_PARTIAL; } + | MATCH SIMPLE_SYM + { Lex->fk_match_option= Foreign_key::FK_MATCH_SIMPLE; } + ; + +opt_on_update_delete: + /* empty */ + { + LEX *lex= Lex; + lex->fk_update_opt= FK_OPTION_UNDEF; + lex->fk_delete_opt= FK_OPTION_UNDEF; + } + | ON UPDATE_SYM delete_option + { + LEX *lex= Lex; + lex->fk_update_opt= $3; + lex->fk_delete_opt= FK_OPTION_UNDEF; + } + | ON DELETE_SYM delete_option + { + LEX *lex= Lex; + lex->fk_update_opt= FK_OPTION_UNDEF; + lex->fk_delete_opt= $3; + } + | ON UPDATE_SYM delete_option + ON DELETE_SYM delete_option + { + LEX *lex= Lex; + lex->fk_update_opt= $3; + lex->fk_delete_opt= $6; + } + | ON DELETE_SYM delete_option + ON UPDATE_SYM delete_option + { + LEX *lex= Lex; + lex->fk_update_opt= $6; + lex->fk_delete_opt= $3; + } + ; + +delete_option: + RESTRICT { $$= FK_OPTION_RESTRICT; } + | CASCADE { $$= FK_OPTION_CASCADE; } + | SET NULL_SYM { $$= FK_OPTION_SET_NULL; } + | NO_SYM ACTION { $$= FK_OPTION_NO_ACTION; } + | SET DEFAULT { $$= FK_OPTION_SET_DEFAULT; } + ; + +constraint_key_type: + PRIMARY_SYM KEY_SYM { $$= Key::PRIMARY; } + | UNIQUE_SYM opt_key_or_index { $$= Key::UNIQUE; } + ; + +key_or_index: + KEY_SYM {} + | INDEX_SYM {} + ; + +opt_key_or_index: + /* empty */ {} + | key_or_index + ; + +keys_or_index: + KEYS {} + | INDEX_SYM {} + | INDEXES {} + ; + +opt_unique: + /* empty */ { $$= Key::MULTIPLE; } + | UNIQUE_SYM { $$= Key::UNIQUE; } + ; + +fulltext: + FULLTEXT_SYM { $$= Key::FULLTEXT;} + ; + +spatial: + SPATIAL_SYM + { +#ifdef HAVE_SPATIAL + $$= Key::SPATIAL; +#else + my_yyabort_error((ER_FEATURE_DISABLED, MYF(0), sym_group_geom.name, + sym_group_geom.needed_define)); +#endif + } + ; + +normal_key_options: + /* empty */ {} + | normal_key_opts { Lex->last_key->option_list= Lex->option_list; } + ; + +fulltext_key_options: + /* empty */ {} + | fulltext_key_opts { Lex->last_key->option_list= Lex->option_list; } + ; + +spatial_key_options: + /* empty */ {} + | spatial_key_opts { Lex->last_key->option_list= Lex->option_list; } + ; + +normal_key_opts: + normal_key_opt + | normal_key_opts normal_key_opt + ; + +spatial_key_opts: + spatial_key_opt + | spatial_key_opts spatial_key_opt + ; + +fulltext_key_opts: + fulltext_key_opt + | fulltext_key_opts fulltext_key_opt + ; + +opt_USING_key_algorithm: + /* Empty*/ { $$= HA_KEY_ALG_UNDEF; } + | USING btree_or_rtree { $$= $2; } + +/* TYPE is a valid identifier, so it's handled differently than USING */ +opt_key_algorithm_clause: + /* Empty*/ { $$= HA_KEY_ALG_UNDEF; } + | USING btree_or_rtree { $$= $2; } + | TYPE_SYM btree_or_rtree { $$= $2; } + +key_using_alg: + USING btree_or_rtree + { Lex->last_key->key_create_info.algorithm= $2; } + | TYPE_SYM btree_or_rtree + { Lex->last_key->key_create_info.algorithm= $2; } + ; + +all_key_opt: + KEY_BLOCK_SIZE opt_equal ulong_num + { Lex->last_key->key_create_info.block_size= $3; } + | COMMENT_SYM TEXT_STRING_sys + { Lex->last_key->key_create_info.comment= $2; } + | IDENT_sys equal TEXT_STRING_sys + { + if ($3.length > ENGINE_OPTION_MAX_LENGTH) + my_yyabort_error((ER_VALUE_TOO_LONG, MYF(0), $1.str)); + new (thd->mem_root) + engine_option_value($1, $3, true, &Lex->option_list, + &Lex->option_list_last); + } + | IDENT_sys equal ident + { + if ($3.length > ENGINE_OPTION_MAX_LENGTH) + my_yyabort_error((ER_VALUE_TOO_LONG, MYF(0), $1.str)); + new (thd->mem_root) + engine_option_value($1, $3, false, &Lex->option_list, + &Lex->option_list_last); + } + | IDENT_sys equal real_ulonglong_num + { + new (thd->mem_root) + engine_option_value($1, $3, &Lex->option_list, + &Lex->option_list_last, thd->mem_root); + } + | IDENT_sys equal DEFAULT + { + new (thd->mem_root) + engine_option_value($1, &Lex->option_list, &Lex->option_list_last); + } + ; + +normal_key_opt: + all_key_opt + | key_using_alg + ; + +spatial_key_opt: + all_key_opt + ; + +fulltext_key_opt: + all_key_opt + | WITH PARSER_SYM IDENT_sys + { + if (plugin_is_ready(&$3, MYSQL_FTPARSER_PLUGIN)) + Lex->last_key->key_create_info.parser_name= $3; + else + my_yyabort_error((ER_FUNCTION_NOT_DEFINED, MYF(0), $3.str)); + } + ; + +btree_or_rtree: + BTREE_SYM { $$= HA_KEY_ALG_BTREE; } + | RTREE_SYM { $$= HA_KEY_ALG_RTREE; } + | HASH_SYM { $$= HA_KEY_ALG_HASH; } + ; + +key_list: + key_list ',' key_part order_dir + { + Lex->last_key->columns.push_back($3, thd->mem_root); + } + | key_part order_dir + { + Lex->last_key->columns.push_back($1, thd->mem_root); + } + ; + +key_part: + ident + { + $$= new (thd->mem_root) Key_part_spec($1, 0); + if ($$ == NULL) + MYSQL_YYABORT; + } + | ident '(' NUM ')' + { + int key_part_len= atoi($3.str); + if (!key_part_len) + my_yyabort_error((ER_KEY_PART_0, MYF(0), $1.str)); + $$= new (thd->mem_root) Key_part_spec($1, (uint) key_part_len); + if ($$ == NULL) + MYSQL_YYABORT; + } + ; + +opt_ident: + /* empty */ { $$= null_lex_str; } + | field_ident { $$= $1; } + ; + +opt_component: + /* empty */ { $$= null_lex_str; } + | '.' ident { $$= $2; } + ; + +string_list: + text_string + { Lex->last_field->interval_list.push_back($1, thd->mem_root); } + | string_list ',' text_string + { Lex->last_field->interval_list.push_back($3, thd->mem_root); }; + +/* +** Alter table +*/ + +alter: + ALTER + { + Lex->name= null_lex_str; + Lex->only_view= FALSE; + Lex->sql_command= SQLCOM_ALTER_TABLE; + Lex->duplicates= DUP_ERROR; + Lex->select_lex.init_order(); + Lex->create_info.init(); + Lex->create_info.row_type= ROW_TYPE_NOT_USED; + Lex->alter_info.reset(); + Lex->no_write_to_binlog= 0; + Lex->create_info.storage_media= HA_SM_DEFAULT; + DBUG_ASSERT(!Lex->m_sql_cmd); + } + alter_options TABLE_SYM table_ident + { + if (!Lex->select_lex.add_table_to_list(thd, $5, NULL, + TL_OPTION_UPDATING, + TL_READ_NO_INSERT, + MDL_SHARED_UPGRADABLE)) + MYSQL_YYABORT; + Lex->select_lex.db= (Lex->select_lex.table_list.first)->db; + Lex->create_last_non_select_table= Lex->last_table(); + } + alter_commands + { + if (!Lex->m_sql_cmd) + { + /* Create a generic ALTER TABLE statment. */ + Lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_alter_table(); + if (Lex->m_sql_cmd == NULL) + MYSQL_YYABORT; + } + } + | ALTER DATABASE ident_or_empty + { + Lex->create_info.default_table_charset= NULL; + Lex->create_info.used_fields= 0; + } + create_database_options + { + LEX *lex=Lex; + lex->sql_command=SQLCOM_ALTER_DB; + lex->name= $3; + if (lex->name.str == NULL && + lex->copy_db_to(&lex->name.str, &lex->name.length)) + MYSQL_YYABORT; + } + | ALTER DATABASE ident UPGRADE_SYM DATA_SYM DIRECTORY_SYM NAME_SYM + { + LEX *lex= Lex; + if (lex->sphead) + my_yyabort_error((ER_SP_NO_DROP_SP, MYF(0), "DATABASE")); + lex->sql_command= SQLCOM_ALTER_DB_UPGRADE; + lex->name= $3; + } + | ALTER PROCEDURE_SYM sp_name + { + LEX *lex= Lex; + + if (lex->sphead) + my_yyabort_error((ER_SP_NO_DROP_SP, MYF(0), "PROCEDURE")); + bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics)); + } + sp_a_chistics + { + LEX *lex=Lex; + + lex->sql_command= SQLCOM_ALTER_PROCEDURE; + lex->spname= $3; + } + | ALTER FUNCTION_SYM sp_name + { + LEX *lex= Lex; + + if (lex->sphead) + my_yyabort_error((ER_SP_NO_DROP_SP, MYF(0), "FUNCTION")); + bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics)); + } + sp_a_chistics + { + LEX *lex=Lex; + + lex->sql_command= SQLCOM_ALTER_FUNCTION; + lex->spname= $3; + } + | ALTER view_algorithm definer_opt + { + LEX *lex= Lex; + + if (lex->sphead) + my_yyabort_error((ER_SP_BADSTATEMENT, MYF(0), "ALTER VIEW")); + lex->create_view_mode= VIEW_ALTER; + } + view_tail + {} + | ALTER definer_opt + /* + We have two separate rules for ALTER VIEW rather that + optional view_algorithm above, to resolve the ambiguity + with the ALTER EVENT below. + */ + { + LEX *lex= Lex; + + if (lex->sphead) + my_yyabort_error((ER_SP_BADSTATEMENT, MYF(0), "ALTER VIEW")); + lex->create_view_algorithm= VIEW_ALGORITHM_INHERIT; + lex->create_view_mode= VIEW_ALTER; + } + view_tail + {} + | ALTER definer_opt remember_name EVENT_SYM sp_name + { + /* + It is safe to use Lex->spname because + ALTER EVENT xxx RENATE TO yyy DO ALTER EVENT RENAME TO + is not allowed. Lex->spname is used in the case of RENAME TO + If it had to be supported spname had to be added to + Event_parse_data. + */ + + if (!(Lex->event_parse_data= Event_parse_data::new_instance(thd))) + MYSQL_YYABORT; + Lex->event_parse_data->identifier= $5; + + Lex->sql_command= SQLCOM_ALTER_EVENT; + Lex->stmt_definition_begin= $3; + } + ev_alter_on_schedule_completion + opt_ev_rename_to + opt_ev_status + opt_ev_comment + opt_ev_sql_stmt + { + if (!($7 || $8 || $9 || $10 || $11)) + { + my_parse_error(thd, ER_SYNTAX_ERROR); + MYSQL_YYABORT; + } + /* + sql_command is set here because some rules in ev_sql_stmt + can overwrite it + */ + Lex->sql_command= SQLCOM_ALTER_EVENT; + Lex->stmt_definition_end= (char*)YYLIP->get_cpp_ptr(); + } + | ALTER TABLESPACE alter_tablespace_info + { + LEX *lex= Lex; + lex->alter_tablespace_info->ts_cmd_type= ALTER_TABLESPACE; + } + | ALTER LOGFILE_SYM GROUP_SYM alter_logfile_group_info + { + LEX *lex= Lex; + lex->alter_tablespace_info->ts_cmd_type= ALTER_LOGFILE_GROUP; + } + | ALTER TABLESPACE change_tablespace_info + { + LEX *lex= Lex; + lex->alter_tablespace_info->ts_cmd_type= CHANGE_FILE_TABLESPACE; + } + | ALTER TABLESPACE change_tablespace_access + { + LEX *lex= Lex; + lex->alter_tablespace_info->ts_cmd_type= ALTER_ACCESS_MODE_TABLESPACE; + } + | ALTER SERVER_SYM ident_or_text + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_ALTER_SERVER; + lex->server_options.reset($3); + } OPTIONS_SYM '(' server_options_list ')' { } + /* ALTER USER foo is allowed for MySQL compatibility. */ + | ALTER opt_if_exists USER_SYM clear_privileges grant_list + opt_require_clause opt_resource_options + { + Lex->create_info.set($2); + Lex->sql_command= SQLCOM_ALTER_USER; + } + ; + +ev_alter_on_schedule_completion: + /* empty */ { $$= 0;} + | ON SCHEDULE_SYM ev_schedule_time { $$= 1; } + | ev_on_completion { $$= 1; } + | ON SCHEDULE_SYM ev_schedule_time ev_on_completion { $$= 1; } + ; + +opt_ev_rename_to: + /* empty */ { $$= 0;} + | RENAME TO_SYM sp_name + { + /* + Use lex's spname to hold the new name. + The original name is in the Event_parse_data object + */ + Lex->spname= $3; + $$= 1; + } + ; + +opt_ev_sql_stmt: + /* empty*/ { $$= 0;} + | DO_SYM ev_sql_stmt { $$= 1; } + ; + +ident_or_empty: + /* empty */ { $$= null_lex_str; } + | ident { $$= $1; } + ; + +alter_commands: + /* empty */ + | DISCARD TABLESPACE + { + Lex->m_sql_cmd= new (thd->mem_root) + Sql_cmd_discard_import_tablespace( + Sql_cmd_discard_import_tablespace::DISCARD_TABLESPACE); + if (Lex->m_sql_cmd == NULL) + MYSQL_YYABORT; + } + | IMPORT TABLESPACE + { + Lex->m_sql_cmd= new (thd->mem_root) + Sql_cmd_discard_import_tablespace( + Sql_cmd_discard_import_tablespace::IMPORT_TABLESPACE); + if (Lex->m_sql_cmd == NULL) + MYSQL_YYABORT; + } + | alter_list + opt_partitioning + | alter_list + remove_partitioning + | remove_partitioning + | partitioning +/* + This part was added for release 5.1 by Mikael Ronstrm. + From here we insert a number of commands to manage the partitions of a + partitioned table such as adding partitions, dropping partitions, + reorganising partitions in various manners. In future releases the list + will be longer. +*/ + | add_partition_rule + | DROP PARTITION_SYM opt_if_exists alt_part_name_list + { + Lex->alter_info.flags|= Alter_info::ALTER_DROP_PARTITION; + DBUG_ASSERT(!Lex->if_exists()); + Lex->create_info.add($3); + } + | REBUILD_SYM PARTITION_SYM opt_no_write_to_binlog + all_or_alt_part_name_list + { + LEX *lex= Lex; + lex->alter_info.flags|= Alter_info::ALTER_REBUILD_PARTITION; + lex->no_write_to_binlog= $3; + } + | OPTIMIZE PARTITION_SYM opt_no_write_to_binlog + all_or_alt_part_name_list + { + LEX *lex= thd->lex; + lex->no_write_to_binlog= $3; + lex->check_opt.init(); + DBUG_ASSERT(!lex->m_sql_cmd); + lex->m_sql_cmd= new (thd->mem_root) + Sql_cmd_alter_table_optimize_partition(); + if (lex->m_sql_cmd == NULL) + MYSQL_YYABORT; + } + opt_no_write_to_binlog + | ANALYZE_SYM PARTITION_SYM opt_no_write_to_binlog + all_or_alt_part_name_list + { + LEX *lex= thd->lex; + lex->no_write_to_binlog= $3; + lex->check_opt.init(); + DBUG_ASSERT(!lex->m_sql_cmd); + lex->m_sql_cmd= new (thd->mem_root) + Sql_cmd_alter_table_analyze_partition(); + if (lex->m_sql_cmd == NULL) + MYSQL_YYABORT; + } + | CHECK_SYM PARTITION_SYM all_or_alt_part_name_list + { + LEX *lex= thd->lex; + lex->check_opt.init(); + DBUG_ASSERT(!lex->m_sql_cmd); + lex->m_sql_cmd= new (thd->mem_root) + Sql_cmd_alter_table_check_partition(); + if (lex->m_sql_cmd == NULL) + MYSQL_YYABORT; + } + opt_mi_check_type + | REPAIR PARTITION_SYM opt_no_write_to_binlog + all_or_alt_part_name_list + { + LEX *lex= thd->lex; + lex->no_write_to_binlog= $3; + lex->check_opt.init(); + DBUG_ASSERT(!lex->m_sql_cmd); + lex->m_sql_cmd= new (thd->mem_root) + Sql_cmd_alter_table_repair_partition(); + if (lex->m_sql_cmd == NULL) + MYSQL_YYABORT; + } + opt_mi_repair_type + | COALESCE PARTITION_SYM opt_no_write_to_binlog real_ulong_num + { + LEX *lex= Lex; + lex->alter_info.flags|= Alter_info::ALTER_COALESCE_PARTITION; + lex->no_write_to_binlog= $3; + lex->alter_info.num_parts= $4; + } + | TRUNCATE_SYM PARTITION_SYM all_or_alt_part_name_list + { + LEX *lex= thd->lex; + lex->check_opt.init(); + DBUG_ASSERT(!lex->m_sql_cmd); + lex->m_sql_cmd= new (thd->mem_root) + Sql_cmd_alter_table_truncate_partition(); + if (lex->m_sql_cmd == NULL) + MYSQL_YYABORT; + } + | reorg_partition_rule + | EXCHANGE_SYM PARTITION_SYM alt_part_name_item + WITH TABLE_SYM table_ident have_partitioning + { + LEX *lex= thd->lex; + size_t dummy; + lex->select_lex.db=$6->db.str; + if (lex->select_lex.db == NULL && + lex->copy_db_to(&lex->select_lex.db, &dummy)) + { + MYSQL_YYABORT; + } + lex->name= $6->table; + lex->alter_info.flags|= Alter_info::ALTER_EXCHANGE_PARTITION; + if (!lex->select_lex.add_table_to_list(thd, $6, NULL, + TL_OPTION_UPDATING, + TL_READ_NO_INSERT, + MDL_SHARED_NO_WRITE)) + MYSQL_YYABORT; + DBUG_ASSERT(!lex->m_sql_cmd); + lex->m_sql_cmd= new (thd->mem_root) + Sql_cmd_alter_table_exchange_partition(); + if (lex->m_sql_cmd == NULL) + MYSQL_YYABORT; + } + ; + +remove_partitioning: + REMOVE_SYM PARTITIONING_SYM + { + Lex->alter_info.flags|= Alter_info::ALTER_REMOVE_PARTITIONING; + } + ; + +all_or_alt_part_name_list: + ALL + { + Lex->alter_info.flags|= Alter_info::ALTER_ALL_PARTITION; + } + | alt_part_name_list + ; + +add_partition_rule: + ADD PARTITION_SYM opt_if_not_exists + opt_no_write_to_binlog + { + LEX *lex= Lex; + lex->part_info= new (thd->mem_root) partition_info(); + if (!lex->part_info) + { + mem_alloc_error(sizeof(partition_info)); + MYSQL_YYABORT; + } + lex->alter_info.flags|= Alter_info::ALTER_ADD_PARTITION; + DBUG_ASSERT(!Lex->create_info.if_not_exists()); + lex->create_info.set($3); + lex->no_write_to_binlog= $4; + } + add_part_extra + {} + ; + +add_part_extra: + /* empty */ + | '(' part_def_list ')' + { + LEX *lex= Lex; + lex->part_info->num_parts= lex->part_info->partitions.elements; + } + | PARTITIONS_SYM real_ulong_num + { + Lex->part_info->num_parts= $2; + } + ; + +reorg_partition_rule: + REORGANIZE_SYM PARTITION_SYM opt_no_write_to_binlog + { + LEX *lex= Lex; + lex->part_info= new (thd->mem_root) partition_info(); + if (!lex->part_info) + { + mem_alloc_error(sizeof(partition_info)); + MYSQL_YYABORT; + } + lex->no_write_to_binlog= $3; + } + reorg_parts_rule + ; + +reorg_parts_rule: + /* empty */ + { + Lex->alter_info.flags|= Alter_info::ALTER_TABLE_REORG; + } + | alt_part_name_list + { + Lex->alter_info.flags|= Alter_info::ALTER_REORGANIZE_PARTITION; + } + INTO '(' part_def_list ')' + { + partition_info *part_info= Lex->part_info; + part_info->num_parts= part_info->partitions.elements; + } + ; + +alt_part_name_list: + alt_part_name_item {} + | alt_part_name_list ',' alt_part_name_item {} + ; + +alt_part_name_item: + ident + { + if (Lex->alter_info.partition_names.push_back($1.str, + thd->mem_root)) + { + mem_alloc_error(1); + MYSQL_YYABORT; + } + } + ; + +/* + End of management of partition commands +*/ + +alter_list: + alter_list_item + | alter_list ',' alter_list_item + ; + +add_column: + ADD opt_column opt_if_not_exists_table_element + { + LEX *lex=Lex; + lex->alter_info.flags|= Alter_info::ALTER_ADD_COLUMN; + } + ; + +alter_list_item: + add_column column_def opt_place + { + Lex->create_last_non_select_table= Lex->last_table(); + $2->after= $3; + } + | ADD key_def + { + Lex->create_last_non_select_table= Lex->last_table(); + Lex->alter_info.flags|= Alter_info::ALTER_ADD_INDEX; + } + | add_column '(' create_field_list ')' + { + Lex->alter_info.flags|= Alter_info::ALTER_ADD_COLUMN | + Alter_info::ALTER_ADD_INDEX; + } + | ADD constraint_def + { + Lex->alter_info.flags|= Alter_info::ALTER_ADD_CHECK_CONSTRAINT; + } + | ADD CONSTRAINT IF_SYM not EXISTS field_ident check_constraint + { + Lex->alter_info.flags|= Alter_info::ALTER_ADD_CHECK_CONSTRAINT; + Lex->add_constraint(&$6, $7, TRUE); + } + | CHANGE opt_column opt_if_exists_table_element field_ident + field_spec opt_place + { + Lex->alter_info.flags|= Alter_info::ALTER_CHANGE_COLUMN; + Lex->create_last_non_select_table= Lex->last_table(); + $5->change= $4.str; + $5->after= $6; + } + | MODIFY_SYM opt_column opt_if_exists_table_element + field_spec opt_place + { + Lex->alter_info.flags|= Alter_info::ALTER_CHANGE_COLUMN; + Lex->create_last_non_select_table= Lex->last_table(); + $4->change= $4->field_name; + $4->after= $5; + } + | DROP opt_column opt_if_exists_table_element field_ident opt_restrict + { + LEX *lex=Lex; + Alter_drop *ad= (new (thd->mem_root) + Alter_drop(Alter_drop::COLUMN, $4.str, $3)); + if (ad == NULL) + MYSQL_YYABORT; + lex->alter_info.drop_list.push_back(ad, thd->mem_root); + lex->alter_info.flags|= Alter_info::ALTER_DROP_COLUMN; + } + | DROP CONSTRAINT opt_if_exists_table_element field_ident + { + LEX *lex=Lex; + Alter_drop *ad= (new (thd->mem_root) + Alter_drop(Alter_drop::CHECK_CONSTRAINT, + $4.str, $3)); + if (ad == NULL) + MYSQL_YYABORT; + lex->alter_info.drop_list.push_back(ad, thd->mem_root); + lex->alter_info.flags|= Alter_info::ALTER_DROP_CHECK_CONSTRAINT; + } + | DROP FOREIGN KEY_SYM opt_if_exists_table_element field_ident + { + LEX *lex=Lex; + Alter_drop *ad= (new (thd->mem_root) + Alter_drop(Alter_drop::FOREIGN_KEY, $5.str, $4)); + if (ad == NULL) + MYSQL_YYABORT; + lex->alter_info.drop_list.push_back(ad, thd->mem_root); + lex->alter_info.flags|= Alter_info::DROP_FOREIGN_KEY; + } + | DROP PRIMARY_SYM KEY_SYM + { + LEX *lex=Lex; + Alter_drop *ad= (new (thd->mem_root) + Alter_drop(Alter_drop::KEY, primary_key_name, + FALSE)); + if (ad == NULL) + MYSQL_YYABORT; + lex->alter_info.drop_list.push_back(ad, thd->mem_root); + lex->alter_info.flags|= Alter_info::ALTER_DROP_INDEX; + } + | DROP key_or_index opt_if_exists_table_element field_ident + { + LEX *lex=Lex; + Alter_drop *ad= (new (thd->mem_root) + Alter_drop(Alter_drop::KEY, $4.str, $3)); + if (ad == NULL) + MYSQL_YYABORT; + lex->alter_info.drop_list.push_back(ad, thd->mem_root); + lex->alter_info.flags|= Alter_info::ALTER_DROP_INDEX; + } + | DISABLE_SYM KEYS + { + LEX *lex=Lex; + lex->alter_info.keys_onoff= Alter_info::DISABLE; + lex->alter_info.flags|= Alter_info::ALTER_KEYS_ONOFF; + } + | ENABLE_SYM KEYS + { + LEX *lex=Lex; + lex->alter_info.keys_onoff= Alter_info::ENABLE; + lex->alter_info.flags|= Alter_info::ALTER_KEYS_ONOFF; + } + | ALTER opt_column field_ident SET DEFAULT column_default_expr + { + LEX *lex=Lex; + Alter_column *ac= new (thd->mem_root) Alter_column($3.str,$6); + if (ac == NULL) + MYSQL_YYABORT; + lex->alter_info.alter_list.push_back(ac, thd->mem_root); + lex->alter_info.flags|= Alter_info::ALTER_CHANGE_COLUMN_DEFAULT; + } + | ALTER opt_column field_ident DROP DEFAULT + { + LEX *lex=Lex; + Alter_column *ac= (new (thd->mem_root) + Alter_column($3.str, (Virtual_column_info*) 0)); + if (ac == NULL) + MYSQL_YYABORT; + lex->alter_info.alter_list.push_back(ac, thd->mem_root); + lex->alter_info.flags|= Alter_info::ALTER_CHANGE_COLUMN_DEFAULT; + } + | RENAME opt_to table_ident + { + LEX *lex=Lex; + size_t dummy; + lex->select_lex.db=$3->db.str; + if (lex->select_lex.db == NULL && + lex->copy_db_to(&lex->select_lex.db, &dummy)) + { + MYSQL_YYABORT; + } + if (check_table_name($3->table.str,$3->table.length, FALSE) || + ($3->db.str && check_db_name(&$3->db))) + my_yyabort_error((ER_WRONG_TABLE_NAME, MYF(0), $3->table.str)); + lex->name= $3->table; + lex->alter_info.flags|= Alter_info::ALTER_RENAME; + } + | CONVERT_SYM TO_SYM charset charset_name_or_default opt_collate + { + if (!$4) + { + $4= thd->variables.collation_database; + } + $5= $5 ? $5 : $4; + if (!my_charset_same($4,$5)) + my_yyabort_error((ER_COLLATION_CHARSET_MISMATCH, MYF(0), + $5->name, $4->csname)); + if (Lex->create_info.add_alter_list_item_convert_to_charset($5)) + MYSQL_YYABORT; + Lex->alter_info.flags|= Alter_info::ALTER_OPTIONS; + } + | create_table_options_space_separated + { + LEX *lex=Lex; + lex->alter_info.flags|= Alter_info::ALTER_OPTIONS; + if ((lex->create_info.used_fields & HA_CREATE_USED_ENGINE) && + !lex->create_info.db_type) + { + lex->create_info.used_fields&= ~HA_CREATE_USED_ENGINE; + } + } + | FORCE_SYM + { + Lex->alter_info.flags|= Alter_info::ALTER_RECREATE; + } + | alter_order_clause + { + LEX *lex=Lex; + lex->alter_info.flags|= Alter_info::ALTER_ORDER; + } + | alter_algorithm_option + | alter_lock_option + ; + +opt_index_lock_algorithm: + /* empty */ + | alter_lock_option + | alter_algorithm_option + | alter_lock_option alter_algorithm_option + | alter_algorithm_option alter_lock_option + +alter_algorithm_option: + ALGORITHM_SYM opt_equal DEFAULT + { + Lex->alter_info.requested_algorithm= + Alter_info::ALTER_TABLE_ALGORITHM_DEFAULT; + } + | ALGORITHM_SYM opt_equal ident + { + if (Lex->alter_info.set_requested_algorithm(&$3)) + my_yyabort_error((ER_UNKNOWN_ALTER_ALGORITHM, MYF(0), $3.str)); + } + ; + +alter_lock_option: + LOCK_SYM opt_equal DEFAULT + { + Lex->alter_info.requested_lock= + Alter_info::ALTER_TABLE_LOCK_DEFAULT; + } + | LOCK_SYM opt_equal ident + { + if (Lex->alter_info.set_requested_lock(&$3)) + my_yyabort_error((ER_UNKNOWN_ALTER_LOCK, MYF(0), $3.str)); + } + ; + +opt_column: + /* empty */ {} + | COLUMN_SYM {} + ; + +opt_ignore: + /* empty */ { Lex->ignore= 0;} + | IGNORE_SYM { Lex->ignore= 1;} + ; + +alter_options: + { Lex->ignore= 0;} alter_options_part2 + ; + +alter_options_part2: + /* empty */ + | alter_option_list + ; + +alter_option_list: + alter_option_list alter_option + | alter_option + ; + +alter_option: + IGNORE_SYM { Lex->ignore= 1;} + | ONLINE_SYM + { + Lex->alter_info.requested_lock= + Alter_info::ALTER_TABLE_LOCK_NONE; + } + + +opt_restrict: + /* empty */ { Lex->drop_mode= DROP_DEFAULT; } + | RESTRICT { Lex->drop_mode= DROP_RESTRICT; } + | CASCADE { Lex->drop_mode= DROP_CASCADE; } + ; + +opt_place: + /* empty */ { $$= NULL; } + | AFTER_SYM ident + { + $$= $2.str; + Lex->alter_info.flags |= Alter_info::ALTER_COLUMN_ORDER; + } + | FIRST_SYM + { + $$= first_keyword; + Lex->alter_info.flags |= Alter_info::ALTER_COLUMN_ORDER; + } + ; + +opt_to: + /* empty */ {} + | TO_SYM {} + | '=' {} + | AS {} + ; + +slave: + START_SYM SLAVE optional_connection_name slave_thread_opts + { + LEX *lex=Lex; + lex->sql_command = SQLCOM_SLAVE_START; + lex->type = 0; + /* If you change this code don't forget to update SLAVE START too */ + } + slave_until + {} + | START_SYM ALL SLAVES slave_thread_opts + { + LEX *lex=Lex; + lex->sql_command = SQLCOM_SLAVE_ALL_START; + lex->type = 0; + /* If you change this code don't forget to update STOP SLAVE too */ + } + {} + | STOP_SYM SLAVE optional_connection_name slave_thread_opts + { + LEX *lex=Lex; + lex->sql_command = SQLCOM_SLAVE_STOP; + lex->type = 0; + /* If you change this code don't forget to update SLAVE STOP too */ + } + | STOP_SYM ALL SLAVES slave_thread_opts + { + LEX *lex=Lex; + lex->sql_command = SQLCOM_SLAVE_ALL_STOP; + lex->type = 0; + /* If you change this code don't forget to update SLAVE STOP too */ + } + ; + +start: + 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(thd, ER_SYNTAX_ERROR); + MYSQL_YYABORT; + } + lex->start_transaction_opt= $3; + } + ; + +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: + { Lex->slave_thd_opt= 0; } + slave_thread_opt_list + {} + ; + +slave_thread_opt_list: + slave_thread_opt + | slave_thread_opt_list ',' slave_thread_opt + ; + +slave_thread_opt: + /*empty*/ {} + | SQL_THREAD { Lex->slave_thd_opt|=SLAVE_SQL; } + | RELAY_THREAD { Lex->slave_thd_opt|=SLAVE_IO; } + ; + +slave_until: + /*empty*/ {} + | UNTIL_SYM slave_until_opts + { + LEX *lex=Lex; + if (((lex->mi.log_file_name || lex->mi.pos) && + (lex->mi.relay_log_name || lex->mi.relay_log_pos)) || + !((lex->mi.log_file_name && lex->mi.pos) || + (lex->mi.relay_log_name && lex->mi.relay_log_pos))) + my_yyabort_error((ER_BAD_SLAVE_UNTIL_COND, MYF(0))); + } + | UNTIL_SYM MASTER_GTID_POS_SYM '=' TEXT_STRING_sys + { + Lex->mi.gtid_pos_str = $4; + } + ; + +slave_until_opts: + master_file_def + | slave_until_opts ',' master_file_def + ; + +checksum: + CHECKSUM_SYM table_or_tables + { + LEX *lex=Lex; + lex->sql_command = SQLCOM_CHECKSUM; + /* Will be overridden during execution. */ + YYPS->m_lock_type= TL_UNLOCK; + } + table_list opt_checksum_type + {} + ; + +opt_checksum_type: + /* nothing */ { Lex->check_opt.flags= 0; } + | QUICK { Lex->check_opt.flags= T_QUICK; } + | EXTENDED_SYM { Lex->check_opt.flags= T_EXTEND; } + ; + +repair_table_or_view: + table_or_tables table_list opt_mi_repair_type + | VIEW_SYM { Lex->only_view= TRUE; } table_list opt_view_repair_type + ; + +repair: + REPAIR opt_no_write_to_binlog + { + LEX *lex=Lex; + lex->sql_command = SQLCOM_REPAIR; + lex->no_write_to_binlog= $2; + lex->check_opt.init(); + lex->alter_info.reset(); + /* Will be overridden during execution. */ + YYPS->m_lock_type= TL_UNLOCK; + } + repair_table_or_view + { + LEX* lex= thd->lex; + DBUG_ASSERT(!lex->m_sql_cmd); + lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_repair_table(); + if (lex->m_sql_cmd == NULL) + MYSQL_YYABORT; + } + ; + +opt_mi_repair_type: + /* empty */ { Lex->check_opt.flags = T_MEDIUM; } + | mi_repair_types {} + ; + +mi_repair_types: + mi_repair_type {} + | mi_repair_type mi_repair_types {} + ; + +mi_repair_type: + QUICK { Lex->check_opt.flags|= T_QUICK; } + | EXTENDED_SYM { Lex->check_opt.flags|= T_EXTEND; } + | USE_FRM { Lex->check_opt.sql_flags|= TT_USEFRM; } + ; + +opt_view_repair_type: + /* empty */ { } + | FROM MYSQL_SYM { Lex->check_opt.sql_flags|= TT_FROM_MYSQL; } + ; + +analyze: + ANALYZE_SYM opt_no_write_to_binlog table_or_tables + { + LEX *lex=Lex; + lex->sql_command = SQLCOM_ANALYZE; + lex->no_write_to_binlog= $2; + lex->check_opt.init(); + lex->alter_info.reset(); + /* Will be overridden during execution. */ + YYPS->m_lock_type= TL_UNLOCK; + } + analyze_table_list + { + LEX* lex= thd->lex; + DBUG_ASSERT(!lex->m_sql_cmd); + lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_analyze_table(); + if (lex->m_sql_cmd == NULL) + MYSQL_YYABORT; + } + ; + +analyze_table_list: + analyze_table_elem_spec + | analyze_table_list ',' analyze_table_elem_spec + ; + +analyze_table_elem_spec: + table_name opt_persistent_stat_clause + ; + +opt_persistent_stat_clause: + /* empty */ + {} + | PERSISTENT_SYM FOR_SYM persistent_stat_spec + { + thd->lex->with_persistent_for_clause= TRUE; + } + ; + +persistent_stat_spec: + ALL + {} + | COLUMNS persistent_column_stat_spec INDEXES persistent_index_stat_spec + {} + +persistent_column_stat_spec: + ALL {} + | '(' + { + LEX* lex= thd->lex; + lex->column_list= new (thd->mem_root) List<LEX_STRING>; + if (lex->column_list == NULL) + MYSQL_YYABORT; + } + table_column_list + ')' + ; + +persistent_index_stat_spec: + ALL {} + | '(' + { + LEX* lex= thd->lex; + lex->index_list= new (thd->mem_root) List<LEX_STRING>; + if (lex->index_list == NULL) + MYSQL_YYABORT; + } + table_index_list + ')' + ; + +table_column_list: + /* empty */ + {} + | ident + { + Lex->column_list->push_back((LEX_STRING*) + thd->memdup(&$1, sizeof(LEX_STRING)), thd->mem_root); + } + | table_column_list ',' ident + { + Lex->column_list->push_back((LEX_STRING*) + thd->memdup(&$3, sizeof(LEX_STRING)), thd->mem_root); + } + ; + +table_index_list: + /* empty */ + {} + | table_index_name + | table_index_list ',' table_index_name + ; + +table_index_name: + ident + { + Lex->index_list->push_back((LEX_STRING*) + thd->memdup(&$1, sizeof(LEX_STRING)), + thd->mem_root); + } + | + PRIMARY_SYM + { + LEX_STRING str= {(char*) "PRIMARY", 7}; + Lex->index_list->push_back((LEX_STRING*) + thd->memdup(&str, sizeof(LEX_STRING)), + thd->mem_root); + } + ; + +binlog_base64_event: + BINLOG_SYM TEXT_STRING_sys + { + Lex->sql_command = SQLCOM_BINLOG_BASE64_EVENT; + Lex->comment= $2; + } + ; + +check_view_or_table: + table_or_tables table_list opt_mi_check_type + | VIEW_SYM { Lex->only_view= TRUE; } table_list opt_view_check_type + ; + +check: CHECK_SYM + { + LEX *lex=Lex; + + lex->sql_command = SQLCOM_CHECK; + lex->check_opt.init(); + lex->alter_info.reset(); + /* Will be overridden during execution. */ + YYPS->m_lock_type= TL_UNLOCK; + } + check_view_or_table + { + LEX* lex= thd->lex; + if (lex->sphead) + my_yyabort_error((ER_SP_BADSTATEMENT, MYF(0), "CHECK")); + DBUG_ASSERT(!lex->m_sql_cmd); + lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_check_table(); + if (lex->m_sql_cmd == NULL) + MYSQL_YYABORT; + } + ; + +opt_mi_check_type: + /* empty */ { Lex->check_opt.flags = T_MEDIUM; } + | mi_check_types {} + ; + +mi_check_types: + mi_check_type {} + | mi_check_type mi_check_types {} + ; + +mi_check_type: + QUICK { Lex->check_opt.flags|= T_QUICK; } + | FAST_SYM { Lex->check_opt.flags|= T_FAST; } + | MEDIUM_SYM { Lex->check_opt.flags|= T_MEDIUM; } + | EXTENDED_SYM { Lex->check_opt.flags|= T_EXTEND; } + | CHANGED { Lex->check_opt.flags|= T_CHECK_ONLY_CHANGED; } + | FOR_SYM UPGRADE_SYM { Lex->check_opt.sql_flags|= TT_FOR_UPGRADE; } + ; + +opt_view_check_type: + /* empty */ { } + | FOR_SYM UPGRADE_SYM { Lex->check_opt.sql_flags|= TT_FOR_UPGRADE; } + ; + +optimize: + OPTIMIZE opt_no_write_to_binlog table_or_tables + { + LEX *lex=Lex; + lex->sql_command = SQLCOM_OPTIMIZE; + lex->no_write_to_binlog= $2; + lex->check_opt.init(); + lex->alter_info.reset(); + /* Will be overridden during execution. */ + YYPS->m_lock_type= TL_UNLOCK; + } + table_list + { + LEX* lex= thd->lex; + DBUG_ASSERT(!lex->m_sql_cmd); + lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_optimize_table(); + if (lex->m_sql_cmd == NULL) + MYSQL_YYABORT; + } + ; + +opt_no_write_to_binlog: + /* empty */ { $$= 0; } + | NO_WRITE_TO_BINLOG { $$= 1; } + | LOCAL_SYM { $$= 1; } + ; + +rename: + RENAME table_or_tables + { + Lex->sql_command= SQLCOM_RENAME_TABLE; + } + table_to_table_list + {} + | RENAME USER_SYM clear_privileges rename_list + { + Lex->sql_command = SQLCOM_RENAME_USER; + } + ; + +rename_list: + user TO_SYM user + { + if (Lex->users_list.push_back($1, thd->mem_root) || + Lex->users_list.push_back($3, thd->mem_root)) + MYSQL_YYABORT; + } + | rename_list ',' user TO_SYM user + { + if (Lex->users_list.push_back($3, thd->mem_root) || + Lex->users_list.push_back($5, thd->mem_root)) + MYSQL_YYABORT; + } + ; + +table_to_table_list: + table_to_table + | table_to_table_list ',' table_to_table + ; + +table_to_table: + table_ident TO_SYM table_ident + { + LEX *lex=Lex; + SELECT_LEX *sl= lex->current_select; + if (!sl->add_table_to_list(thd, $1,NULL,TL_OPTION_UPDATING, + TL_IGNORE, MDL_EXCLUSIVE) || + !sl->add_table_to_list(thd, $3,NULL,TL_OPTION_UPDATING, + TL_IGNORE, MDL_EXCLUSIVE)) + MYSQL_YYABORT; + } + ; + +keycache: + CACHE_SYM INDEX_SYM + { + Lex->alter_info.reset(); + } + keycache_list_or_parts IN_SYM key_cache_name + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_ASSIGN_TO_KEYCACHE; + lex->ident= $6; + } + ; + +keycache_list_or_parts: + keycache_list + | assign_to_keycache_parts + ; + +keycache_list: + assign_to_keycache + | keycache_list ',' assign_to_keycache + ; + +assign_to_keycache: + table_ident cache_keys_spec + { + if (!Select->add_table_to_list(thd, $1, NULL, 0, TL_READ, + MDL_SHARED_READ, + Select->pop_index_hints())) + MYSQL_YYABORT; + } + ; + +assign_to_keycache_parts: + table_ident adm_partition cache_keys_spec + { + if (!Select->add_table_to_list(thd, $1, NULL, 0, TL_READ, + MDL_SHARED_READ, + Select->pop_index_hints())) + MYSQL_YYABORT; + } + ; + +key_cache_name: + ident { $$= $1; } + | DEFAULT { $$ = default_key_cache_base; } + ; + +preload: + LOAD INDEX_SYM INTO CACHE_SYM + { + LEX *lex=Lex; + lex->sql_command=SQLCOM_PRELOAD_KEYS; + lex->alter_info.reset(); + } + preload_list_or_parts + {} + ; + +preload_list_or_parts: + preload_keys_parts + | preload_list + ; + +preload_list: + preload_keys + | preload_list ',' preload_keys + ; + +preload_keys: + table_ident cache_keys_spec opt_ignore_leaves + { + if (!Select->add_table_to_list(thd, $1, NULL, $3, TL_READ, + MDL_SHARED_READ, + Select->pop_index_hints())) + MYSQL_YYABORT; + } + ; + +preload_keys_parts: + table_ident adm_partition cache_keys_spec opt_ignore_leaves + { + if (!Select->add_table_to_list(thd, $1, NULL, $4, TL_READ, + MDL_SHARED_READ, + Select->pop_index_hints())) + MYSQL_YYABORT; + } + ; + +adm_partition: + PARTITION_SYM have_partitioning + { + Lex->alter_info.flags|= Alter_info::ALTER_ADMIN_PARTITION; + } + '(' all_or_alt_part_name_list ')' + ; + +cache_keys_spec: + { + Lex->select_lex.alloc_index_hints(thd); + Select->set_index_hint_type(INDEX_HINT_USE, + INDEX_HINT_MASK_ALL); + } + cache_key_list_or_empty + ; + +cache_key_list_or_empty: + /* empty */ { } + | key_or_index '(' opt_key_usage_list ')' + ; + +opt_ignore_leaves: + /* empty */ + { $$= 0; } + | IGNORE_SYM LEAVES { $$= TL_OPTION_IGNORE_LEAVES; } + ; + +/* + Select : retrieve data from table +*/ + + +select: + opt_with_clause select_init + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SELECT; + lex->current_select->set_with_clause($1); + } + ; + +select_init: + SELECT_SYM select_options_and_item_list select_init3 + | '(' select_paren ')' + | '(' select_paren ')' union_list + | '(' select_paren ')' union_order_or_limit + ; + +union_list_part2: + SELECT_SYM select_options_and_item_list select_init3_union_query_term + | '(' select_paren_union_query_term ')' + | '(' select_paren_union_query_term ')' union_list + | '(' select_paren_union_query_term ')' union_order_or_limit + ; + +select_paren: + { + /* + In order to correctly parse UNION's global ORDER BY we need to + set braces before parsing the clause. + */ + Lex->current_select->set_braces(true); + } + SELECT_SYM select_options_and_item_list select_part3 + opt_select_lock_type + { + DBUG_ASSERT(Lex->current_select->braces); + } + | '(' select_paren ')' + ; + +select_paren_union_query_term: + { + /* + In order to correctly parse UNION's global ORDER BY we need to + set braces before parsing the clause. + */ + Lex->current_select->set_braces(true); + } + SELECT_SYM select_options_and_item_list select_part3_union_query_term + opt_select_lock_type + { + DBUG_ASSERT(Lex->current_select->braces); + } + | '(' select_paren_union_query_term ')' + ; + +select_paren_view: + { + /* + In order to correctly parse UNION's global ORDER BY we need to + set braces before parsing the clause. + */ + Lex->current_select->set_braces(true); + } + SELECT_SYM select_options_and_item_list select_part3_view + opt_select_lock_type + { + DBUG_ASSERT(Lex->current_select->braces); + } + | '(' select_paren_view ')' + ; + +/* The equivalent of select_paren for nested queries. */ +select_paren_derived: + { + Lex->current_select->set_braces(true); + } + SELECT_SYM select_part2_derived + opt_table_expression + opt_order_clause + opt_limit_clause + opt_select_lock_type + { + DBUG_ASSERT(Lex->current_select->braces); + $$= Lex->current_select->master_unit()->first_select(); + } + | '(' select_paren_derived ')' { $$= $2; } + ; + +select_init3: + opt_table_expression + opt_select_lock_type + { + /* Parentheses carry no meaning here */ + Lex->current_select->set_braces(false); + } + union_clause + | select_part3_union_not_ready + opt_select_lock_type + { + /* Parentheses carry no meaning here */ + Lex->current_select->set_braces(false); + } + ; + + +select_init3_union_query_term: + opt_table_expression + opt_select_lock_type + { + /* Parentheses carry no meaning here */ + Lex->current_select->set_braces(false); + } + union_clause + | select_part3_union_not_ready_noproc + opt_select_lock_type + { + /* Parentheses carry no meaning here */ + Lex->current_select->set_braces(false); + } + ; + + +select_init3_view: + opt_table_expression opt_select_lock_type + { + Lex->current_select->set_braces(false); + } + | opt_table_expression opt_select_lock_type + { + Lex->current_select->set_braces(false); + } + union_list_view + | order_or_limit opt_select_lock_type + { + Lex->current_select->set_braces(false); + } + | table_expression order_or_limit opt_select_lock_type + { + Lex->current_select->set_braces(false); + } + ; + +/* + The SELECT parts after select_item_list that cannot be followed by UNION. +*/ + +select_part3: + opt_table_expression + | select_part3_union_not_ready + ; + +select_part3_union_query_term: + opt_table_expression + | select_part3_union_not_ready_noproc + ; + +select_part3_view: + opt_table_expression + | order_or_limit + | table_expression order_or_limit + ; + +select_part3_union_not_ready: + select_part3_union_not_ready_noproc + | table_expression procedure_clause + | table_expression order_or_limit procedure_clause + ; + +select_part3_union_not_ready_noproc: + order_or_limit + | into opt_table_expression opt_order_clause opt_limit_clause + | table_expression into + | table_expression order_or_limit + | table_expression order_or_limit into + ; + +select_options_and_item_list: + { + LEX *lex= Lex; + SELECT_LEX *sel= lex->current_select; + if (sel->linkage != UNION_TYPE) + mysql_init_select(lex); + lex->current_select->parsing_place= SELECT_LIST; + } + select_options select_item_list + { + Select->parsing_place= NO_MATTER; + } + ; + + +/** + <table expression>, as in the SQL standard. +*/ +table_expression: + from_clause + opt_where_clause + opt_group_clause + opt_having_clause + opt_window_clause + ; + +opt_table_expression: + /* Empty */ + | table_expression + ; + +from_clause: + FROM table_reference_list + ; + +table_reference_list: + join_table_list + { + Select->context.table_list= + Select->context.first_name_resolution_table= + Select->table_list.first; + } + | DUAL_SYM + /* oracle compatibility: oracle always requires FROM clause, + and DUAL is system table without fields. + Is "SELECT 1 FROM DUAL" any better than "SELECT 1" ? + Hmmm :) */ + ; + +select_options: + /* empty*/ + | select_option_list + { + if (Select->options & SELECT_DISTINCT && Select->options & SELECT_ALL) + my_yyabort_error((ER_WRONG_USAGE, MYF(0), "ALL", "DISTINCT")); + } + ; + +select_option_list: + select_option_list select_option + | select_option + ; + +select_option: + query_expression_option + | SQL_NO_CACHE_SYM + { + /* + Allow this flag only on the first top-level SELECT statement, if + SQL_CACHE wasn't specified, and only once per query. + */ + if (Lex->current_select != &Lex->select_lex) + my_yyabort_error((ER_CANT_USE_OPTION_HERE, MYF(0), "SQL_NO_CACHE")); + if (Lex->select_lex.sql_cache == SELECT_LEX::SQL_CACHE) + my_yyabort_error((ER_WRONG_USAGE, MYF(0), "SQL_CACHE", "SQL_NO_CACHE")); + if (Lex->select_lex.sql_cache == SELECT_LEX::SQL_NO_CACHE) + my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "SQL_NO_CACHE")); + + Lex->safe_to_cache_query=0; + Lex->select_lex.options&= ~OPTION_TO_QUERY_CACHE; + Lex->select_lex.sql_cache= SELECT_LEX::SQL_NO_CACHE; + } + | SQL_CACHE_SYM + { + /* + Allow this flag only on the first top-level SELECT statement, if + SQL_NO_CACHE wasn't specified, and only once per query. + */ + if (Lex->current_select != &Lex->select_lex) + my_yyabort_error((ER_CANT_USE_OPTION_HERE, MYF(0), "SQL_CACHE")); + if (Lex->select_lex.sql_cache == SELECT_LEX::SQL_NO_CACHE) + my_yyabort_error((ER_WRONG_USAGE, MYF(0), "SQL_NO_CACHE", "SQL_CACHE")); + if (Lex->select_lex.sql_cache == SELECT_LEX::SQL_CACHE) + my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "SQL_CACHE")); + + Lex->safe_to_cache_query=1; + Lex->select_lex.options|= OPTION_TO_QUERY_CACHE; + Lex->select_lex.sql_cache= SELECT_LEX::SQL_CACHE; + } + ; + +opt_select_lock_type: + /* empty */ + | FOR_SYM UPDATE_SYM + { + LEX *lex=Lex; + lex->current_select->lock_type= TL_WRITE; + lex->current_select->set_lock_for_tables(TL_WRITE); + lex->safe_to_cache_query=0; + } + | LOCK_SYM IN_SYM SHARE_SYM MODE_SYM + { + LEX *lex=Lex; + lex->current_select->lock_type= TL_READ_WITH_SHARED_LOCKS; + lex->current_select-> + set_lock_for_tables(TL_READ_WITH_SHARED_LOCKS); + lex->safe_to_cache_query=0; + } + ; + +select_item_list: + select_item_list ',' select_item + | select_item + | '*' + { + Item *item= new (thd->mem_root) + Item_field(thd, &thd->lex->current_select->context, + NULL, NULL, "*"); + if (item == NULL) + MYSQL_YYABORT; + if (add_item_to_list(thd, item)) + MYSQL_YYABORT; + (thd->lex->current_select->with_wild)++; + } + ; + +select_item: + remember_name table_wild remember_end + { + if (add_item_to_list(thd, $2)) + MYSQL_YYABORT; + } + | remember_name expr remember_end select_alias + { + DBUG_ASSERT($1 < $3); + + if (add_item_to_list(thd, $2)) + MYSQL_YYABORT; + if ($4.str) + { + if (Lex->sql_command == SQLCOM_CREATE_VIEW && + check_column_name($4.str)) + my_yyabort_error((ER_WRONG_COLUMN_NAME, MYF(0), $4.str)); + $2->is_autogenerated_name= FALSE; + $2->set_name(thd, $4.str, $4.length, system_charset_info); + } + else if (!$2->name) + { + $2->set_name(thd, $1, (uint) ($3 - $1), thd->charset()); + } + } + ; + +remember_tok_start: + { + $$= (char*) YYLIP->get_tok_start(); + } + ; + +remember_name: + { + $$= (char*) YYLIP->get_cpp_tok_start(); + } + ; + +remember_end: + { + $$= (char*) YYLIP->get_cpp_tok_end(); + } + ; + +select_alias: + /* empty */ { $$=null_lex_str;} + | AS ident { $$=$2; } + | AS TEXT_STRING_sys { $$=$2; } + | ident { $$=$1; } + | TEXT_STRING_sys { $$=$1; } + ; + +opt_default_time_precision: + /* empty */ { $$= NOT_FIXED_DEC; } + | '(' ')' { $$= NOT_FIXED_DEC; } + | '(' real_ulong_num ')' { $$= $2; }; + ; + +opt_time_precision: + /* empty */ { $$= 0; } + | '(' ')' { $$= 0; } + | '(' real_ulong_num ')' { $$= $2; }; + ; + +optional_braces: + /* empty */ {} + | '(' ')' {} + ; + +/* all possible expressions */ +expr: + expr or expr %prec OR_SYM + { + /* + Design notes: + Do not use a manually maintained stack like thd->lex->xxx_list, + but use the internal bison stack ($$, $1 and $3) instead. + Using the bison stack is: + - more robust to changes in the grammar, + - guaranteed to be in sync with the parser state, + - better for performances (no memory allocation). + */ + Item_cond_or *item1; + Item_cond_or *item3; + if (is_cond_or($1)) + { + item1= (Item_cond_or*) $1; + if (is_cond_or($3)) + { + item3= (Item_cond_or*) $3; + /* + (X1 OR X2) OR (Y1 OR Y2) ==> OR (X1, X2, Y1, Y2) + */ + item3->add_at_head(item1->argument_list()); + $$ = $3; + } + else + { + /* + (X1 OR X2) OR Y ==> OR (X1, X2, Y) + */ + item1->add($3, thd->mem_root); + $$ = $1; + } + } + else if (is_cond_or($3)) + { + item3= (Item_cond_or*) $3; + /* + X OR (Y1 OR Y2) ==> OR (X, Y1, Y2) + */ + item3->add_at_head($1, thd->mem_root); + $$ = $3; + } + else + { + /* X OR Y */ + $$= new (thd->mem_root) Item_cond_or(thd, $1, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + } + | expr XOR expr %prec XOR + { + /* XOR is a proprietary extension */ + $$= new (thd->mem_root) Item_func_xor(thd, $1, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | expr and expr %prec AND_SYM + { + /* See comments in rule expr: expr or expr */ + Item_cond_and *item1; + Item_cond_and *item3; + if (is_cond_and($1)) + { + item1= (Item_cond_and*) $1; + if (is_cond_and($3)) + { + item3= (Item_cond_and*) $3; + /* + (X1 AND X2) AND (Y1 AND Y2) ==> AND (X1, X2, Y1, Y2) + */ + item3->add_at_head(item1->argument_list()); + $$ = $3; + } + else + { + /* + (X1 AND X2) AND Y ==> AND (X1, X2, Y) + */ + item1->add($3, thd->mem_root); + $$ = $1; + } + } + else if (is_cond_and($3)) + { + item3= (Item_cond_and*) $3; + /* + X AND (Y1 AND Y2) ==> AND (X, Y1, Y2) + */ + item3->add_at_head($1, thd->mem_root); + $$ = $3; + } + else + { + /* X AND Y */ + $$= new (thd->mem_root) Item_cond_and(thd, $1, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + } + | NOT_SYM expr %prec NOT_SYM + { + $$= negate_expression(thd, $2); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bool_pri IS TRUE_SYM %prec IS + { + $$= new (thd->mem_root) Item_func_istrue(thd, $1); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bool_pri IS not TRUE_SYM %prec IS + { + $$= new (thd->mem_root) Item_func_isnottrue(thd, $1); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bool_pri IS FALSE_SYM %prec IS + { + $$= new (thd->mem_root) Item_func_isfalse(thd, $1); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bool_pri IS not FALSE_SYM %prec IS + { + $$= new (thd->mem_root) Item_func_isnotfalse(thd, $1); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bool_pri IS UNKNOWN_SYM %prec IS + { + $$= new (thd->mem_root) Item_func_isnull(thd, $1); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bool_pri IS not UNKNOWN_SYM %prec IS + { + $$= new (thd->mem_root) Item_func_isnotnull(thd, $1); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bool_pri + ; + +bool_pri: + bool_pri IS NULL_SYM %prec IS + { + $$= new (thd->mem_root) Item_func_isnull(thd, $1); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bool_pri IS not NULL_SYM %prec IS + { + $$= new (thd->mem_root) Item_func_isnotnull(thd, $1); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bool_pri EQUAL_SYM predicate %prec EQUAL_SYM + { + $$= new (thd->mem_root) Item_func_equal(thd, $1, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bool_pri comp_op predicate %prec '=' + { + $$= (*$2)(0)->create(thd, $1, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bool_pri comp_op all_or_any '(' subselect ')' %prec '=' + { + $$= all_any_subquery_creator(thd, $1, $2, $3, $5); + if ($$ == NULL) + MYSQL_YYABORT; + } + | predicate + ; + +predicate: + bit_expr IN_SYM '(' subselect ')' + { + $$= new (thd->mem_root) Item_in_subselect(thd, $1, $4); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bit_expr not IN_SYM '(' subselect ')' + { + Item *item= new (thd->mem_root) Item_in_subselect(thd, $1, $5); + if (item == NULL) + MYSQL_YYABORT; + $$= negate_expression(thd, item); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bit_expr IN_SYM '(' expr ')' + { + $$= handle_sql2003_note184_exception(thd, $1, true, $4); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bit_expr IN_SYM '(' expr ',' expr_list ')' + { + $6->push_front($4, thd->mem_root); + $6->push_front($1, thd->mem_root); + $$= new (thd->mem_root) Item_func_in(thd, *$6); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bit_expr not IN_SYM '(' expr ')' + { + $$= handle_sql2003_note184_exception(thd, $1, false, $5); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bit_expr not IN_SYM '(' expr ',' expr_list ')' + { + $7->push_front($5, thd->mem_root); + $7->push_front($1, thd->mem_root); + Item_func_in *item= new (thd->mem_root) Item_func_in(thd, *$7); + if (item == NULL) + MYSQL_YYABORT; + $$= item->neg_transformer(thd); + } + | bit_expr BETWEEN_SYM bit_expr AND_SYM predicate + { + $$= new (thd->mem_root) Item_func_between(thd, $1, $3, $5); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bit_expr not BETWEEN_SYM bit_expr AND_SYM predicate + { + Item_func_between *item; + item= new (thd->mem_root) Item_func_between(thd, $1, $4, $6); + if (item == NULL) + MYSQL_YYABORT; + $$= item->neg_transformer(thd); + } + | bit_expr SOUNDS_SYM LIKE bit_expr + { + Item *item1= new (thd->mem_root) Item_func_soundex(thd, $1); + Item *item4= new (thd->mem_root) Item_func_soundex(thd, $4); + if ((item1 == NULL) || (item4 == NULL)) + MYSQL_YYABORT; + $$= new (thd->mem_root) Item_func_eq(thd, item1, item4); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bit_expr LIKE simple_expr opt_escape + { + $$= new (thd->mem_root) Item_func_like(thd, $1, $3, $4, + Lex->escape_used); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bit_expr not LIKE simple_expr opt_escape + { + Item *item= new (thd->mem_root) Item_func_like(thd, $1, $4, $5, + Lex->escape_used); + if (item == NULL) + MYSQL_YYABORT; + $$= item->neg_transformer(thd); + } + | bit_expr REGEXP bit_expr + { + $$= new (thd->mem_root) Item_func_regex(thd, $1, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bit_expr not REGEXP bit_expr + { + Item *item= new (thd->mem_root) Item_func_regex(thd, $1, $4); + if (item == NULL) + MYSQL_YYABORT; + $$= negate_expression(thd, item); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bit_expr + ; + +bit_expr: + bit_expr '|' bit_expr %prec '|' + { + $$= new (thd->mem_root) Item_func_bit_or(thd, $1, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bit_expr '&' bit_expr %prec '&' + { + $$= new (thd->mem_root) Item_func_bit_and(thd, $1, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bit_expr SHIFT_LEFT bit_expr %prec SHIFT_LEFT + { + $$= new (thd->mem_root) Item_func_shift_left(thd, $1, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bit_expr SHIFT_RIGHT bit_expr %prec SHIFT_RIGHT + { + $$= new (thd->mem_root) Item_func_shift_right(thd, $1, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bit_expr '+' bit_expr %prec '+' + { + $$= new (thd->mem_root) Item_func_plus(thd, $1, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bit_expr '-' bit_expr %prec '-' + { + $$= new (thd->mem_root) Item_func_minus(thd, $1, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bit_expr '+' INTERVAL_SYM expr interval %prec '+' + { + $$= new (thd->mem_root) Item_date_add_interval(thd, $1, $4, $5, 0); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bit_expr '-' INTERVAL_SYM expr interval %prec '-' + { + $$= new (thd->mem_root) Item_date_add_interval(thd, $1, $4, $5, 1); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bit_expr '*' bit_expr %prec '*' + { + $$= new (thd->mem_root) Item_func_mul(thd, $1, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bit_expr '/' bit_expr %prec '/' + { + $$= new (thd->mem_root) Item_func_div(thd, $1, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bit_expr '%' bit_expr %prec '%' + { + $$= new (thd->mem_root) Item_func_mod(thd, $1, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bit_expr DIV_SYM bit_expr %prec DIV_SYM + { + $$= new (thd->mem_root) Item_func_int_div(thd, $1, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bit_expr MOD_SYM bit_expr %prec MOD_SYM + { + $$= new (thd->mem_root) Item_func_mod(thd, $1, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bit_expr '^' bit_expr + { + $$= new (thd->mem_root) Item_func_bit_xor(thd, $1, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | simple_expr + ; + +or: + OR_SYM + | OR2_SYM + ; + +and: + AND_SYM + | AND_AND_SYM + ; + +not: + NOT_SYM + | NOT2_SYM + ; + +not2: + '!' + | NOT2_SYM + ; + +comp_op: + '=' { $$ = &comp_eq_creator; } + | GE { $$ = &comp_ge_creator; } + | '>' { $$ = &comp_gt_creator; } + | LE { $$ = &comp_le_creator; } + | '<' { $$ = &comp_lt_creator; } + | NE { $$ = &comp_ne_creator; } + ; + +all_or_any: + ALL { $$ = 1; } + | ANY_SYM { $$ = 0; } + ; + +opt_dyncol_type: + /* empty */ + { + $$.set(DYN_COL_NULL); /* automatic type */ + Lex->charset= NULL; + } + | AS dyncol_type { $$= $2; } + ; + +dyncol_type: + numeric_dyncol_type { $$= $1; Lex->charset= NULL; } + | temporal_dyncol_type { $$= $1; Lex->charset= NULL; } + | string_dyncol_type { $$= $1; } + ; + +numeric_dyncol_type: + INT_SYM { $$.set(DYN_COL_INT); } + | UNSIGNED INT_SYM { $$.set(DYN_COL_UINT); } + | DOUBLE_SYM { $$.set(DYN_COL_DOUBLE); } + | REAL { $$.set(DYN_COL_DOUBLE); } + | FLOAT_SYM { $$.set(DYN_COL_DOUBLE); } + | DECIMAL_SYM float_options { $$.set(DYN_COL_DECIMAL, $2); } + ; + +temporal_dyncol_type: + DATE_SYM { $$.set(DYN_COL_DATE); } + | TIME_SYM opt_field_length { $$.set(DYN_COL_TIME, 0, $2); } + | DATETIME opt_field_length { $$.set(DYN_COL_DATETIME, 0, $2); } + ; + +string_dyncol_type: + char + { Lex->charset= thd->variables.collation_connection; } + opt_binary + { + $$.set(DYN_COL_STRING); + } + | nchar + { + $$.set(DYN_COL_STRING); + Lex->charset= national_charset_info; + } + ; + +dyncall_create_element: + expr ',' expr opt_dyncol_type + { + LEX *lex= Lex; + $$= (DYNCALL_CREATE_DEF *) + alloc_root(thd->mem_root, sizeof(DYNCALL_CREATE_DEF)); + if ($$ == NULL) + MYSQL_YYABORT; + $$->key= $1; + $$->value= $3; + $$->type= (DYNAMIC_COLUMN_TYPE)$4.dyncol_type(); + $$->cs= lex->charset; + if ($4.length()) + $$->len= strtoul($4.length(), NULL, 10); + else + $$->len= 0; + if ($4.dec()) + $$->frac= strtoul($4.dec(), NULL, 10); + else + $$->len= 0; + } + +dyncall_create_list: + dyncall_create_element + { + $$= new (thd->mem_root) List<DYNCALL_CREATE_DEF>; + if ($$ == NULL) + MYSQL_YYABORT; + $$->push_back($1, thd->mem_root); + } + | dyncall_create_list ',' dyncall_create_element + { + $1->push_back($3, thd->mem_root); + $$= $1; + } + ; + +/* + Expressions that the parser allows in a column DEFAULT clause + without parentheses. These expressions cannot end with a COLLATE clause. + + If we allowed any "expr" in DEFAULT clause, there would be a confusion + in queries like this: + CREATE TABLE t1 (a TEXT DEFAULT 'a' COLLATE latin1_bin); + It would be not clear what COLLATE stands for: + - the collation of the column `a`, or + - the collation of the string literal 'a' + + This restriction allows to parse the above query unambiguiusly: + COLLATE belongs to the column rather than the literal. + If one needs COLLATE to belong to the literal, parentheses must be used: + CREATE TABLE t1 (a TEXT DEFAULT ('a' COLLATE latin1_bin)); + Note: the COLLATE clause is rather meaningless here, but the query + is syntactically correct. + + Note, some of the expressions are not actually allowed in DEFAULT, + e.g. sum_expr, window_func_expr, ROW(...), VALUES(). + We could move them to simple_expr, but that would make + these two queries return a different error messages: + CREATE TABLE t1 (a INT DEFAULT AVG(1)); + CREATE TABLE t1 (a INT DEFAULT (AVG(1))); + The first query would return "syntax error". + Currenly both return: + Function or expression 'avg(' is not allowed for 'DEFAULT' ... +*/ +column_default_non_parenthesized_expr: + simple_ident + | function_call_keyword + | function_call_nonkeyword + | function_call_generic + | function_call_conflict + | literal + | param_marker { $$= $1; } + | variable + | sum_expr + | window_func_expr + | ROW_SYM '(' expr ',' expr_list ')' + { + $5->push_front($3, thd->mem_root); + $$= new (thd->mem_root) Item_row(thd, *$5); + if ($$ == NULL) + MYSQL_YYABORT; + } + | EXISTS '(' subselect ')' + { + $$= new (thd->mem_root) Item_exists_subselect(thd, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | '{' ident expr '}' + { + $$= NULL; + /* + If "expr" is reasonably short pure ASCII string literal, + try to parse known ODBC style date, time or timestamp literals, + e.g: + SELECT {d'2001-01-01'}; + SELECT {t'10:20:30'}; + SELECT {ts'2001-01-01 10:20:30'}; + */ + if ($3->type() == Item::STRING_ITEM) + { + Item_string *item= (Item_string *) $3; + enum_field_types type= item->odbc_temporal_literal_type(&$2); + if (type != MYSQL_TYPE_STRING) + { + $$= create_temporal_literal(thd, item->val_str(NULL), + type, false); + } + } + if ($$ == NULL) + $$= $3; + } + | MATCH ident_list_arg AGAINST '(' bit_expr fulltext_options ')' + { + $2->push_front($5, thd->mem_root); + Item_func_match *i1= new (thd->mem_root) Item_func_match(thd, *$2, + $6); + if (i1 == NULL) + MYSQL_YYABORT; + Select->add_ftfunc_to_list(thd, i1); + $$= i1; + } + | CAST_SYM '(' expr AS cast_type ')' + { + LEX *lex= Lex; + $$= create_func_cast(thd, $3, $5.type(), $5.length(), $5.dec(), + lex->charset); + if ($$ == NULL) + MYSQL_YYABORT; + } + | CASE_SYM opt_expr when_list opt_else END + { + $$= new (thd->mem_root) Item_func_case(thd, *$3, $2, $4); + if ($$ == NULL) + MYSQL_YYABORT; + } + | CONVERT_SYM '(' expr ',' cast_type ')' + { + $$= create_func_cast(thd, $3, $5.type(), $5.length(), $5.dec(), + Lex->charset); + if ($$ == NULL) + MYSQL_YYABORT; + } + | CONVERT_SYM '(' expr USING charset_name ')' + { + $$= new (thd->mem_root) Item_func_conv_charset(thd, $3, $5); + if ($$ == NULL) + MYSQL_YYABORT; + } + | DEFAULT '(' simple_ident ')' + { + Item_splocal *il= $3->get_item_splocal(); + if (il) + my_yyabort_error((ER_WRONG_COLUMN_NAME, MYF(0), il->my_name()->str)); + $$= new (thd->mem_root) Item_default_value(thd, Lex->current_context(), + $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | VALUES '(' simple_ident_nospvar ')' + { + $$= new (thd->mem_root) Item_insert_value(thd, Lex->current_context(), + $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + ; + +simple_expr: + column_default_non_parenthesized_expr + | simple_expr COLLATE_SYM ident_or_text %prec NEG + { + Item *i1= new (thd->mem_root) Item_string(thd, $3.str, + $3.length, + thd->charset()); + if (i1 == NULL) + MYSQL_YYABORT; + $$= new (thd->mem_root) Item_func_set_collation(thd, $1, i1); + if ($$ == NULL) + MYSQL_YYABORT; + } + | '(' parenthesized_expr ')' { $$= $2; } + | BINARY simple_expr %prec NEG + { + $$= create_func_cast(thd, $2, ITEM_CAST_CHAR, NULL, NULL, + &my_charset_bin); + if ($$ == NULL) + MYSQL_YYABORT; + } + | simple_expr OR_OR_SYM simple_expr + { + $$= new (thd->mem_root) Item_func_concat(thd, $1, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | '+' simple_expr %prec NEG + { + $$= $2; + } + | '-' simple_expr %prec NEG + { + $$= $2->neg(thd); + if ($$ == NULL) + MYSQL_YYABORT; + } + | '~' simple_expr %prec NEG + { + $$= new (thd->mem_root) Item_func_bit_neg(thd, $2); + if ($$ == NULL) + MYSQL_YYABORT; + } + | not2 simple_expr %prec NEG + { + $$= negate_expression(thd, $2); + if ($$ == NULL) + MYSQL_YYABORT; + } + | INTERVAL_SYM expr interval '+' expr %prec INTERVAL_SYM + /* we cannot put interval before - */ + { + $$= new (thd->mem_root) Item_date_add_interval(thd, $5, $2, $3, 0); + if ($$ == NULL) + MYSQL_YYABORT; + } + ; + +/* + Function call syntax using official SQL 2003 keywords. + Because the function name is an official token, + a dedicated grammar rule is needed in the parser. + There is no potential for conflicts +*/ +function_call_keyword: + CHAR_SYM '(' expr_list ')' + { + $$= new (thd->mem_root) Item_func_char(thd, *$3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | CHAR_SYM '(' expr_list USING charset_name ')' + { + $$= new (thd->mem_root) Item_func_char(thd, *$3, $5); + if ($$ == NULL) + MYSQL_YYABORT; + } + | CURRENT_USER optional_braces + { + $$= new (thd->mem_root) Item_func_current_user(thd, + Lex->current_context()); + if ($$ == NULL) + MYSQL_YYABORT; + Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION); + Lex->safe_to_cache_query= 0; + } + | CURRENT_ROLE optional_braces + { + $$= new (thd->mem_root) Item_func_current_role(thd, + Lex->current_context()); + if ($$ == NULL) + MYSQL_YYABORT; + Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION); + Lex->safe_to_cache_query= 0; + } + | DATE_SYM '(' expr ')' + { + $$= new (thd->mem_root) Item_date_typecast(thd, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | DAY_SYM '(' expr ')' + { + $$= new (thd->mem_root) Item_func_dayofmonth(thd, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | HOUR_SYM '(' expr ')' + { + $$= new (thd->mem_root) Item_func_hour(thd, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | INSERT '(' expr ',' expr ',' expr ',' expr ')' + { + $$= new (thd->mem_root) Item_func_insert(thd, $3, $5, $7, $9); + if ($$ == NULL) + MYSQL_YYABORT; + } + | INTERVAL_SYM '(' expr ',' expr ')' %prec INTERVAL_SYM + { + List<Item> *list= new (thd->mem_root) List<Item>; + if (list == NULL) + MYSQL_YYABORT; + list->push_front($5, thd->mem_root); + list->push_front($3, thd->mem_root); + Item_row *item= new (thd->mem_root) Item_row(thd, *list); + if (item == NULL) + MYSQL_YYABORT; + $$= new (thd->mem_root) Item_func_interval(thd, item); + if ($$ == NULL) + MYSQL_YYABORT; + } + | INTERVAL_SYM '(' expr ',' expr ',' expr_list ')' %prec INTERVAL_SYM + { + $7->push_front($5, thd->mem_root); + $7->push_front($3, thd->mem_root); + Item_row *item= new (thd->mem_root) Item_row(thd, *$7); + if (item == NULL) + MYSQL_YYABORT; + $$= new (thd->mem_root) Item_func_interval(thd, item); + if ($$ == NULL) + MYSQL_YYABORT; + } + | LEFT '(' expr ',' expr ')' + { + $$= new (thd->mem_root) Item_func_left(thd, $3, $5); + if ($$ == NULL) + MYSQL_YYABORT; + } + | MINUTE_SYM '(' expr ')' + { + $$= new (thd->mem_root) Item_func_minute(thd, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | MONTH_SYM '(' expr ')' + { + $$= new (thd->mem_root) Item_func_month(thd, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | RIGHT '(' expr ',' expr ')' + { + $$= new (thd->mem_root) Item_func_right(thd, $3, $5); + if ($$ == NULL) + MYSQL_YYABORT; + } + | SECOND_SYM '(' expr ')' + { + $$= new (thd->mem_root) Item_func_second(thd, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | TIME_SYM '(' expr ')' + { + $$= new (thd->mem_root) Item_time_typecast(thd, $3, + AUTO_SEC_PART_DIGITS); + if ($$ == NULL) + MYSQL_YYABORT; + } + | TIMESTAMP '(' expr ')' + { + $$= new (thd->mem_root) Item_datetime_typecast(thd, $3, + AUTO_SEC_PART_DIGITS); + if ($$ == NULL) + MYSQL_YYABORT; + } + | TIMESTAMP '(' expr ',' expr ')' + { + $$= new (thd->mem_root) Item_func_add_time(thd, $3, $5, 1, 0); + if ($$ == NULL) + MYSQL_YYABORT; + } + | TRIM '(' expr ')' + { + $$= new (thd->mem_root) Item_func_trim(thd, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | TRIM '(' LEADING expr FROM expr ')' + { + $$= new (thd->mem_root) Item_func_ltrim(thd, $6, $4); + if ($$ == NULL) + MYSQL_YYABORT; + } + | TRIM '(' TRAILING expr FROM expr ')' + { + $$= new (thd->mem_root) Item_func_rtrim(thd, $6, $4); + if ($$ == NULL) + MYSQL_YYABORT; + } + | TRIM '(' BOTH expr FROM expr ')' + { + $$= new (thd->mem_root) Item_func_trim(thd, $6, $4); + if ($$ == NULL) + MYSQL_YYABORT; + } + | TRIM '(' LEADING FROM expr ')' + { + $$= new (thd->mem_root) Item_func_ltrim(thd, $5); + if ($$ == NULL) + MYSQL_YYABORT; + } + | TRIM '(' TRAILING FROM expr ')' + { + $$= new (thd->mem_root) Item_func_rtrim(thd, $5); + if ($$ == NULL) + MYSQL_YYABORT; + } + | TRIM '(' BOTH FROM expr ')' + { + $$= new (thd->mem_root) Item_func_trim(thd, $5); + if ($$ == NULL) + MYSQL_YYABORT; + } + | TRIM '(' expr FROM expr ')' + { + $$= new (thd->mem_root) Item_func_trim(thd, $5, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | USER_SYM '(' ')' + { + $$= new (thd->mem_root) Item_func_user(thd); + if ($$ == NULL) + MYSQL_YYABORT; + Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION); + Lex->safe_to_cache_query=0; + } + | YEAR_SYM '(' expr ')' + { + $$= new (thd->mem_root) Item_func_year(thd, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + ; + +/* + Function calls using non reserved keywords, with special syntaxic forms. + Dedicated grammar rules are needed because of the syntax, + but also have the potential to cause incompatibilities with other + parts of the language. + MAINTAINER: + The only reasons a function should be added here are: + - for compatibility reasons with another SQL syntax (CURDATE), + - for typing reasons (GET_FORMAT) + Any other 'Syntaxic sugar' enhancements should be *STRONGLY* + discouraged. +*/ +function_call_nonkeyword: + ADDDATE_SYM '(' expr ',' expr ')' + { + $$= new (thd->mem_root) Item_date_add_interval(thd, $3, $5, + INTERVAL_DAY, 0); + if ($$ == NULL) + MYSQL_YYABORT; + } + | ADDDATE_SYM '(' expr ',' INTERVAL_SYM expr interval ')' + { + $$= new (thd->mem_root) Item_date_add_interval(thd, $3, $6, $7, 0); + if ($$ == NULL) + MYSQL_YYABORT; + } + | CURDATE optional_braces + { + $$= new (thd->mem_root) Item_func_curdate_local(thd); + if ($$ == NULL) + MYSQL_YYABORT; + Lex->safe_to_cache_query=0; + } + | CURTIME opt_time_precision + { + $$= new (thd->mem_root) Item_func_curtime_local(thd, $2); + if ($$ == NULL) + MYSQL_YYABORT; + Lex->safe_to_cache_query=0; + } + | DATE_ADD_INTERVAL '(' expr ',' INTERVAL_SYM expr interval ')' + %prec INTERVAL_SYM + { + $$= new (thd->mem_root) Item_date_add_interval(thd, $3, $6, $7, 0); + if ($$ == NULL) + MYSQL_YYABORT; + } + | DATE_SUB_INTERVAL '(' expr ',' INTERVAL_SYM expr interval ')' + %prec INTERVAL_SYM + { + $$= new (thd->mem_root) Item_date_add_interval(thd, $3, $6, $7, 1); + if ($$ == NULL) + MYSQL_YYABORT; + } + | EXTRACT_SYM '(' interval FROM expr ')' + { + $$=new (thd->mem_root) Item_extract(thd, $3, $5); + if ($$ == NULL) + MYSQL_YYABORT; + } + | GET_FORMAT '(' date_time_type ',' expr ')' + { + $$= new (thd->mem_root) Item_func_get_format(thd, $3, $5); + if ($$ == NULL) + MYSQL_YYABORT; + } + | NOW_SYM opt_time_precision + { + $$= new (thd->mem_root) Item_func_now_local(thd, $2); + if ($$ == NULL) + MYSQL_YYABORT; + Lex->safe_to_cache_query=0; + } + | POSITION_SYM '(' bit_expr IN_SYM expr ')' + { + $$= new (thd->mem_root) Item_func_locate(thd, $5, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | SUBDATE_SYM '(' expr ',' expr ')' + { + $$= new (thd->mem_root) Item_date_add_interval(thd, $3, $5, + INTERVAL_DAY, 1); + if ($$ == NULL) + MYSQL_YYABORT; + } + | SUBDATE_SYM '(' expr ',' INTERVAL_SYM expr interval ')' + { + $$= new (thd->mem_root) Item_date_add_interval(thd, $3, $6, $7, 1); + if ($$ == NULL) + MYSQL_YYABORT; + } + | SUBSTRING '(' expr ',' expr ',' expr ')' + { + $$= new (thd->mem_root) Item_func_substr(thd, $3, $5, $7); + if ($$ == NULL) + MYSQL_YYABORT; + } + | SUBSTRING '(' expr ',' expr ')' + { + $$= new (thd->mem_root) Item_func_substr(thd, $3, $5); + if ($$ == NULL) + MYSQL_YYABORT; + } + | SUBSTRING '(' expr FROM expr FOR_SYM expr ')' + { + $$= new (thd->mem_root) Item_func_substr(thd, $3, $5, $7); + if ($$ == NULL) + MYSQL_YYABORT; + } + | SUBSTRING '(' expr FROM expr ')' + { + $$= new (thd->mem_root) Item_func_substr(thd, $3, $5); + if ($$ == NULL) + MYSQL_YYABORT; + } + | SYSDATE opt_time_precision + { + /* + Unlike other time-related functions, SYSDATE() is + replication-unsafe because it is not affected by the + TIMESTAMP variable. It is unsafe even if + sysdate_is_now=1, because the slave may have + sysdate_is_now=0. + */ + Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION); + if (global_system_variables.sysdate_is_now == 0) + $$= new (thd->mem_root) Item_func_sysdate_local(thd, $2); + else + $$= new (thd->mem_root) Item_func_now_local(thd, $2); + if ($$ == NULL) + MYSQL_YYABORT; + Lex->safe_to_cache_query=0; + } + | TIMESTAMP_ADD '(' interval_time_stamp ',' expr ',' expr ')' + { + $$= new (thd->mem_root) Item_date_add_interval(thd, $7, $5, $3, 0); + if ($$ == NULL) + MYSQL_YYABORT; + } + | TIMESTAMP_DIFF '(' interval_time_stamp ',' expr ',' expr ')' + { + $$= new (thd->mem_root) Item_func_timestamp_diff(thd, $5, $7, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | UTC_DATE_SYM optional_braces + { + $$= new (thd->mem_root) Item_func_curdate_utc(thd); + if ($$ == NULL) + MYSQL_YYABORT; + Lex->safe_to_cache_query=0; + } + | UTC_TIME_SYM opt_time_precision + { + $$= new (thd->mem_root) Item_func_curtime_utc(thd, $2); + if ($$ == NULL) + MYSQL_YYABORT; + Lex->safe_to_cache_query=0; + } + | UTC_TIMESTAMP_SYM opt_time_precision + { + $$= new (thd->mem_root) Item_func_now_utc(thd, $2); + if ($$ == NULL) + MYSQL_YYABORT; + Lex->safe_to_cache_query=0; + } + | + COLUMN_ADD_SYM '(' expr ',' dyncall_create_list ')' + { + $$= create_func_dyncol_add(thd, $3, *$5); + if ($$ == NULL) + MYSQL_YYABORT; + } + | + COLUMN_DELETE_SYM '(' expr ',' expr_list ')' + { + $$= create_func_dyncol_delete(thd, $3, *$5); + if ($$ == NULL) + MYSQL_YYABORT; + } + | + COLUMN_CHECK_SYM '(' expr ')' + { + $$= new (thd->mem_root) Item_func_dyncol_check(thd, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | + COLUMN_CREATE_SYM '(' dyncall_create_list ')' + { + $$= create_func_dyncol_create(thd, *$3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | + COLUMN_GET_SYM '(' expr ',' expr AS cast_type ')' + { + LEX *lex= Lex; + $$= create_func_dyncol_get(thd, $3, $5, $7.type(), + $7.length(), $7.dec(), + lex->charset); + if ($$ == NULL) + MYSQL_YYABORT; + } + ; + +/* + Functions calls using a non reserved keyword, and using a regular syntax. + Because the non reserved keyword is used in another part of the grammar, + a dedicated rule is needed here. +*/ +function_call_conflict: + ASCII_SYM '(' expr ')' + { + $$= new (thd->mem_root) Item_func_ascii(thd, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | CHARSET '(' expr ')' + { + $$= new (thd->mem_root) Item_func_charset(thd, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | COALESCE '(' expr_list ')' + { + $$= new (thd->mem_root) Item_func_coalesce(thd, *$3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | COLLATION_SYM '(' expr ')' + { + $$= new (thd->mem_root) Item_func_collation(thd, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | DATABASE '(' ')' + { + $$= new (thd->mem_root) Item_func_database(thd); + if ($$ == NULL) + MYSQL_YYABORT; + Lex->safe_to_cache_query=0; + } + | IF_SYM '(' expr ',' expr ',' expr ')' + { + $$= new (thd->mem_root) Item_func_if(thd, $3, $5, $7); + if ($$ == NULL) + MYSQL_YYABORT; + } + | FORMAT_SYM '(' expr ',' expr ')' + { + $$= new (thd->mem_root) Item_func_format(thd, $3, $5); + if ($$ == NULL) + MYSQL_YYABORT; + } + | FORMAT_SYM '(' expr ',' expr ',' expr ')' + { + $$= new (thd->mem_root) Item_func_format(thd, $3, $5, $7); + if ($$ == NULL) + MYSQL_YYABORT; + } + /* LAST_VALUE here conflicts with the definition for window functions. + We have these 2 separate rules to remove the shift/reduce conflict. + */ + | LAST_VALUE '(' expr ')' + { + List<Item> *list= new (thd->mem_root) List<Item>; + if (list == NULL) + MYSQL_YYABORT; + list->push_back($3, thd->mem_root); + + $$= new (thd->mem_root) Item_func_last_value(thd, *list); + if ($$ == NULL) + MYSQL_YYABORT; + } + | LAST_VALUE '(' expr_list ',' expr ')' + { + $3->push_back($5, thd->mem_root); + $$= new (thd->mem_root) Item_func_last_value(thd, *$3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | MICROSECOND_SYM '(' expr ')' + { + $$= new (thd->mem_root) Item_func_microsecond(thd, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | MOD_SYM '(' expr ',' expr ')' + { + $$= new (thd->mem_root) Item_func_mod(thd, $3, $5); + if ($$ == NULL) + MYSQL_YYABORT; + } + | OLD_PASSWORD_SYM '(' expr ')' + { + $$= new (thd->mem_root) + Item_func_password(thd, $3, Item_func_password::OLD); + if ($$ == NULL) + MYSQL_YYABORT; + } + | PASSWORD_SYM '(' expr ')' + { + Item* i1; + i1= new (thd->mem_root) Item_func_password(thd, $3); + if (i1 == NULL) + MYSQL_YYABORT; + $$= i1; + } + | QUARTER_SYM '(' expr ')' + { + $$= new (thd->mem_root) Item_func_quarter(thd, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | REPEAT_SYM '(' expr ',' expr ')' + { + $$= new (thd->mem_root) Item_func_repeat(thd, $3, $5); + if ($$ == NULL) + MYSQL_YYABORT; + } + | REPLACE '(' expr ',' expr ',' expr ')' + { + $$= new (thd->mem_root) Item_func_replace(thd, $3, $5, $7); + if ($$ == NULL) + MYSQL_YYABORT; + } + | REVERSE_SYM '(' expr ')' + { + $$= new (thd->mem_root) Item_func_reverse(thd, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | ROW_COUNT_SYM '(' ')' + { + $$= new (thd->mem_root) Item_func_row_count(thd); + if ($$ == NULL) + MYSQL_YYABORT; + Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION); + Lex->safe_to_cache_query= 0; + } + | TRUNCATE_SYM '(' expr ',' expr ')' + { + $$= new (thd->mem_root) Item_func_round(thd, $3, $5, 1); + if ($$ == NULL) + MYSQL_YYABORT; + } + | WEEK_SYM '(' expr ')' + { + $$= new (thd->mem_root) Item_func_week(thd, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | WEEK_SYM '(' expr ',' expr ')' + { + $$= new (thd->mem_root) Item_func_week(thd, $3, $5); + if ($$ == NULL) + MYSQL_YYABORT; + } + | WEIGHT_STRING_SYM '(' expr opt_ws_levels ')' + { + $$= new (thd->mem_root) Item_func_weight_string(thd, $3, 0, 0, $4); + if ($$ == NULL) + MYSQL_YYABORT; + } + | WEIGHT_STRING_SYM '(' expr AS CHAR_SYM ws_nweights opt_ws_levels ')' + { + $$= new (thd->mem_root) + Item_func_weight_string(thd, $3, 0, $6, + $7 | MY_STRXFRM_PAD_WITH_SPACE); + if ($$ == NULL) + MYSQL_YYABORT; + } + | WEIGHT_STRING_SYM '(' expr AS BINARY ws_nweights ')' + { + Item *item= new (thd->mem_root) Item_char_typecast(thd, $3, $6, + &my_charset_bin); + if (item == NULL) + MYSQL_YYABORT; + $$= new (thd->mem_root) + Item_func_weight_string(thd, item, 0, $6, + MY_STRXFRM_PAD_WITH_SPACE); + if ($$ == NULL) + MYSQL_YYABORT; + } + | WEIGHT_STRING_SYM '(' expr ',' ulong_num ',' ulong_num ',' ulong_num ')' + { + $$= new (thd->mem_root) Item_func_weight_string(thd, $3, $5, $7, + $9); + if ($$ == NULL) + MYSQL_YYABORT; + } + | geometry_function + { +#ifdef HAVE_SPATIAL + $$= $1; + /* $1 may be NULL, GEOM_NEW not tested for out of memory */ + if ($$ == NULL) + MYSQL_YYABORT; +#else + my_yyabort_error((ER_FEATURE_DISABLED, MYF(0), sym_group_geom.name, + sym_group_geom.needed_define)); +#endif + } + ; + +geometry_function: + CONTAINS_SYM '(' expr ',' expr ')' + { + $$= GEOM_NEW(thd, + Item_func_spatial_precise_rel(thd, $3, $5, + Item_func::SP_CONTAINS_FUNC)); + } + | GEOMETRYCOLLECTION '(' expr_list ')' + { + $$= GEOM_NEW(thd, + Item_func_spatial_collection(thd, *$3, + Geometry::wkb_geometrycollection, + Geometry::wkb_point)); + } + | LINESTRING '(' expr_list ')' + { + $$= GEOM_NEW(thd, + Item_func_spatial_collection(thd, *$3, + Geometry::wkb_linestring, + Geometry::wkb_point)); + } + | MULTILINESTRING '(' expr_list ')' + { + $$= GEOM_NEW(thd, + Item_func_spatial_collection(thd, *$3, + Geometry::wkb_multilinestring, + Geometry::wkb_linestring)); + } + | MULTIPOINT '(' expr_list ')' + { + $$= GEOM_NEW(thd, + Item_func_spatial_collection(thd, *$3, + Geometry::wkb_multipoint, + Geometry::wkb_point)); + } + | MULTIPOLYGON '(' expr_list ')' + { + $$= GEOM_NEW(thd, + Item_func_spatial_collection(thd, *$3, + Geometry::wkb_multipolygon, + Geometry::wkb_polygon)); + } + | POINT_SYM '(' expr ',' expr ')' + { + $$= GEOM_NEW(thd, Item_func_point(thd, $3, $5)); + } + | POLYGON '(' expr_list ')' + { + $$= GEOM_NEW(thd, + Item_func_spatial_collection(thd, *$3, + Geometry::wkb_polygon, + Geometry::wkb_linestring)); + } + ; + +/* + Regular function calls. + The function name is *not* a token, and therefore is guaranteed to not + introduce side effects to the language in general. + MAINTAINER: + All the new functions implemented for new features should fit into + this category. The place to implement the function itself is + in sql/item_create.cc +*/ +function_call_generic: + IDENT_sys '(' + { +#ifdef HAVE_DLOPEN + udf_func *udf= 0; + LEX *lex= Lex; + if (using_udf_functions && + (udf= find_udf($1.str, $1.length)) && + udf->type == UDFTYPE_AGGREGATE) + { + if (lex->current_select->inc_in_sum_expr()) + { + my_parse_error(thd, ER_SYNTAX_ERROR); + MYSQL_YYABORT; + } + } + /* Temporary placing the result of find_udf in $3 */ + $<udf>$= udf; +#endif + } + opt_udf_expr_list ')' + { + Create_func *builder; + Item *item= NULL; + + if (check_routine_name(&$1)) + { + MYSQL_YYABORT; + } + + /* + Implementation note: + names are resolved with the following order: + - MySQL native functions, + - User Defined Functions, + - Stored Functions (assuming the current <use> database) + + This will be revised with WL#2128 (SQL PATH) + */ + builder= find_native_function_builder(thd, $1); + if (builder) + { + item= builder->create_func(thd, $1, $4); + } + else + { +#ifdef HAVE_DLOPEN + /* Retrieving the result of find_udf */ + udf_func *udf= $<udf>3; + + if (udf) + { + if (udf->type == UDFTYPE_AGGREGATE) + { + Select->in_sum_expr--; + } + + item= Create_udf_func::s_singleton.create(thd, udf, $4); + } + else +#endif + { + builder= find_qualified_function_builder(thd); + DBUG_ASSERT(builder); + item= builder->create_func(thd, $1, $4); + } + } + + if (! ($$= item)) + { + MYSQL_YYABORT; + } + } + | ident '.' ident '(' opt_expr_list ')' + { + Create_qfunc *builder; + Item *item= NULL; + + /* + The following in practice calls: + <code>Create_sp_func::create()</code> + and builds a stored function. + + However, it's important to maintain the interface between the + parser and the implementation in item_create.cc clean, + since this will change with WL#2128 (SQL PATH): + - INFORMATION_SCHEMA.version() is the SQL 99 syntax for the native + function version(), + - MySQL.version() is the SQL 2003 syntax for the native function + version() (a vendor can specify any schema). + */ + + if (!$1.str || check_db_name(&$1)) + my_yyabort_error((ER_WRONG_DB_NAME, MYF(0), $1.str)); + if (check_routine_name(&$3)) + { + MYSQL_YYABORT; + } + + builder= find_qualified_function_builder(thd); + DBUG_ASSERT(builder); + item= builder->create_with_db(thd, $1, $3, true, $5); + + if (! ($$= item)) + { + MYSQL_YYABORT; + } + } + ; + +fulltext_options: + opt_natural_language_mode opt_query_expansion + { $$= $1 | $2; } + | IN_SYM BOOLEAN_SYM MODE_SYM + { $$= FT_BOOL; } + ; + +opt_natural_language_mode: + /* nothing */ { $$= FT_NL; } + | IN_SYM NATURAL LANGUAGE_SYM MODE_SYM { $$= FT_NL; } + ; + +opt_query_expansion: + /* nothing */ { $$= 0; } + | WITH QUERY_SYM EXPANSION_SYM { $$= FT_EXPAND; } + ; + +opt_udf_expr_list: + /* empty */ { $$= NULL; } + | udf_expr_list { $$= $1; } + ; + +udf_expr_list: + udf_expr + { + $$= new (thd->mem_root) List<Item>; + if ($$ == NULL) + MYSQL_YYABORT; + $$->push_back($1, thd->mem_root); + } + | udf_expr_list ',' udf_expr + { + $1->push_back($3, thd->mem_root); + $$= $1; + } + ; + +udf_expr: + remember_name expr remember_end select_alias + { + /* + Use Item::name as a storage for the attribute value of user + defined function argument. It is safe to use Item::name + because the syntax will not allow having an explicit name here. + See WL#1017 re. udf attributes. + */ + if ($4.str) + { + $2->is_autogenerated_name= FALSE; + $2->set_name(thd, $4.str, $4.length, system_charset_info); + } + /* + A field has to have its proper name in order for name + resolution to work, something we are only guaranteed if we + parse it out. If we hijack the input stream with + remember_name we may get quoted or escaped names. + */ + else if ($2->type() != Item::FIELD_ITEM && + $2->type() != Item::REF_ITEM /* For HAVING */ ) + $2->set_name(thd, $1, (uint) ($3 - $1), thd->charset()); + $$= $2; + } + ; + +sum_expr: + AVG_SYM '(' in_sum_expr ')' + { + $$= new (thd->mem_root) Item_sum_avg(thd, $3, FALSE); + if ($$ == NULL) + MYSQL_YYABORT; + } + | AVG_SYM '(' DISTINCT in_sum_expr ')' + { + $$= new (thd->mem_root) Item_sum_avg(thd, $4, TRUE); + if ($$ == NULL) + MYSQL_YYABORT; + } + | BIT_AND '(' in_sum_expr ')' + { + $$= new (thd->mem_root) Item_sum_and(thd, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | BIT_OR '(' in_sum_expr ')' + { + $$= new (thd->mem_root) Item_sum_or(thd, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | BIT_XOR '(' in_sum_expr ')' + { + $$= new (thd->mem_root) Item_sum_xor(thd, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | COUNT_SYM '(' opt_all '*' ')' + { + Item *item= new (thd->mem_root) Item_int(thd, (int32) 0L, 1); + if (item == NULL) + MYSQL_YYABORT; + $$= new (thd->mem_root) Item_sum_count(thd, item); + if ($$ == NULL) + MYSQL_YYABORT; + } + | COUNT_SYM '(' in_sum_expr ')' + { + $$= new (thd->mem_root) Item_sum_count(thd, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | COUNT_SYM '(' DISTINCT + { Select->in_sum_expr++; } + expr_list + { Select->in_sum_expr--; } + ')' + { + $$= new (thd->mem_root) Item_sum_count(thd, *$5); + if ($$ == NULL) + MYSQL_YYABORT; + } + | MIN_SYM '(' in_sum_expr ')' + { + $$= new (thd->mem_root) Item_sum_min(thd, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + /* + According to ANSI SQL, DISTINCT is allowed and has + no sense inside MIN and MAX grouping functions; so MIN|MAX(DISTINCT ...) + is processed like an ordinary MIN | MAX() + */ + | MIN_SYM '(' DISTINCT in_sum_expr ')' + { + $$= new (thd->mem_root) Item_sum_min(thd, $4); + if ($$ == NULL) + MYSQL_YYABORT; + } + | MAX_SYM '(' in_sum_expr ')' + { + $$= new (thd->mem_root) Item_sum_max(thd, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | MAX_SYM '(' DISTINCT in_sum_expr ')' + { + $$= new (thd->mem_root) Item_sum_max(thd, $4); + if ($$ == NULL) + MYSQL_YYABORT; + } + | STD_SYM '(' in_sum_expr ')' + { + $$= new (thd->mem_root) Item_sum_std(thd, $3, 0); + if ($$ == NULL) + MYSQL_YYABORT; + } + | VARIANCE_SYM '(' in_sum_expr ')' + { + $$= new (thd->mem_root) Item_sum_variance(thd, $3, 0); + if ($$ == NULL) + MYSQL_YYABORT; + } + | STDDEV_SAMP_SYM '(' in_sum_expr ')' + { + $$= new (thd->mem_root) Item_sum_std(thd, $3, 1); + if ($$ == NULL) + MYSQL_YYABORT; + } + | VAR_SAMP_SYM '(' in_sum_expr ')' + { + $$= new (thd->mem_root) Item_sum_variance(thd, $3, 1); + if ($$ == NULL) + MYSQL_YYABORT; + } + | SUM_SYM '(' in_sum_expr ')' + { + $$= new (thd->mem_root) Item_sum_sum(thd, $3, FALSE); + if ($$ == NULL) + MYSQL_YYABORT; + } + | SUM_SYM '(' DISTINCT in_sum_expr ')' + { + $$= new (thd->mem_root) Item_sum_sum(thd, $4, TRUE); + if ($$ == NULL) + MYSQL_YYABORT; + } + | GROUP_CONCAT_SYM '(' opt_distinct + { Select->in_sum_expr++; } + expr_list opt_gorder_clause + opt_gconcat_separator + ')' + { + SELECT_LEX *sel= Select; + sel->in_sum_expr--; + $$= new (thd->mem_root) + Item_func_group_concat(thd, Lex->current_context(), $3, $5, + sel->gorder_list, $7); + if ($$ == NULL) + MYSQL_YYABORT; + $5->empty(); + sel->gorder_list.empty(); + } + ; + +window_func_expr: + window_func OVER_SYM window_name + { + $$= new (thd->mem_root) Item_window_func(thd, (Item_sum *) $1, $3); + if ($$ == NULL) + MYSQL_YYABORT; + if (Select->add_window_func((Item_window_func *) $$)) + MYSQL_YYABORT; + } + | + window_func OVER_SYM window_spec + { + LEX *lex= Lex; + if (Select->add_window_spec(thd, lex->win_ref, + Select->group_list, + Select->order_list, + lex->win_frame)) + MYSQL_YYABORT; + $$= new (thd->mem_root) Item_window_func(thd, (Item_sum *) $1, + thd->lex->win_spec); + if ($$ == NULL) + MYSQL_YYABORT; + if (Select->add_window_func((Item_window_func *) $$)) + MYSQL_YYABORT; + } + ; + +window_func: + simple_window_func + | + sum_expr + { + ((Item_sum *) $1)->mark_as_window_func_sum_expr(); + } + ; + +simple_window_func: + ROW_NUMBER_SYM '(' ')' + { + $$= new (thd->mem_root) Item_sum_row_number(thd); + if ($$ == NULL) + MYSQL_YYABORT; + } + | + RANK_SYM '(' ')' + { + $$= new (thd->mem_root) Item_sum_rank(thd); + if ($$ == NULL) + MYSQL_YYABORT; + } + | + DENSE_RANK_SYM '(' ')' + { + $$= new (thd->mem_root) Item_sum_dense_rank(thd); + if ($$ == NULL) + MYSQL_YYABORT; + } + | + PERCENT_RANK_SYM '(' ')' + { + $$= new (thd->mem_root) Item_sum_percent_rank(thd); + if ($$ == NULL) + MYSQL_YYABORT; + } + | + CUME_DIST_SYM '(' ')' + { + $$= new (thd->mem_root) Item_sum_cume_dist(thd); + if ($$ == NULL) + MYSQL_YYABORT; + } + | + NTILE_SYM '(' expr ')' + { + $$= new (thd->mem_root) Item_sum_ntile(thd, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | + FIRST_VALUE_SYM '(' expr ')' + { + $$= new (thd->mem_root) Item_sum_first_value(thd, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | + LAST_VALUE '(' expr ')' + { + $$= new (thd->mem_root) Item_sum_last_value(thd, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | + NTH_VALUE_SYM '(' expr ',' expr ')' + { + $$= new (thd->mem_root) Item_sum_nth_value(thd, $3, $5); + if ($$ == NULL) + MYSQL_YYABORT; + } + | + LEAD_SYM '(' expr ')' + { + /* No second argument defaults to 1. */ + Item* item_offset= new (thd->mem_root) Item_uint(thd, 1); + if (item_offset == NULL) + MYSQL_YYABORT; + $$= new (thd->mem_root) Item_sum_lead(thd, $3, item_offset); + if ($$ == NULL) + MYSQL_YYABORT; + } + | + LEAD_SYM '(' expr ',' expr ')' + { + $$= new (thd->mem_root) Item_sum_lead(thd, $3, $5); + if ($$ == NULL) + MYSQL_YYABORT; + } + | + LAG_SYM '(' expr ')' + { + /* No second argument defaults to 1. */ + Item* item_offset= new (thd->mem_root) Item_uint(thd, 1); + if (item_offset == NULL) + MYSQL_YYABORT; + $$= new (thd->mem_root) Item_sum_lag(thd, $3, item_offset); + if ($$ == NULL) + MYSQL_YYABORT; + } + | + LAG_SYM '(' expr ',' expr ')' + { + $$= new (thd->mem_root) Item_sum_lag(thd, $3, $5); + if ($$ == NULL) + MYSQL_YYABORT; + } + ; + +window_name: + ident + { + $$= (LEX_STRING *) thd->memdup(&$1, sizeof(LEX_STRING)); + if ($$ == NULL) + MYSQL_YYABORT; + } + ; + +variable: + '@' + { + if (! Lex->parsing_options.allows_variable) + my_yyabort_error((ER_VIEW_SELECT_VARIABLE, MYF(0))); + } + variable_aux + { + $$= $3; + } + ; + +variable_aux: + ident_or_text SET_VAR expr + { + Item_func_set_user_var *item; + $$= item= new (thd->mem_root) Item_func_set_user_var(thd, $1, $3); + if ($$ == NULL) + MYSQL_YYABORT; + LEX *lex= Lex; + lex->uncacheable(UNCACHEABLE_SIDEEFFECT); + lex->set_var_list.push_back(item, thd->mem_root); + } + | ident_or_text + { + $$= new (thd->mem_root) Item_func_get_user_var(thd, $1); + if ($$ == NULL) + MYSQL_YYABORT; + LEX *lex= Lex; + lex->uncacheable(UNCACHEABLE_SIDEEFFECT); + } + | '@' opt_var_ident_type ident_or_text opt_component + { + /* disallow "SELECT @@global.global.variable" */ + if ($3.str && $4.str && check_reserved_words(&$3)) + { + my_parse_error(thd, ER_SYNTAX_ERROR); + MYSQL_YYABORT; + } + if (!($$= get_system_var(thd, $2, $3, $4))) + MYSQL_YYABORT; + if (!((Item_func_get_system_var*) $$)->is_written_to_binlog()) + Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_VARIABLE); + } + ; + +opt_distinct: + /* empty */ { $$ = 0; } + | DISTINCT { $$ = 1; } + ; + +opt_gconcat_separator: + /* empty */ + { + $$= new (thd->mem_root) String(",", 1, &my_charset_latin1); + if ($$ == NULL) + MYSQL_YYABORT; + } + | SEPARATOR_SYM text_string { $$ = $2; } + ; + +opt_gorder_clause: + /* empty */ + | ORDER_SYM BY gorder_list; + ; + +gorder_list: + gorder_list ',' order_ident order_dir + { if (add_gorder_to_list(thd, $3,(bool) $4)) MYSQL_YYABORT; } + | order_ident order_dir + { if (add_gorder_to_list(thd, $1,(bool) $2)) MYSQL_YYABORT; } + ; + +in_sum_expr: + opt_all + { + LEX *lex= Lex; + if (lex->current_select->inc_in_sum_expr()) + { + my_parse_error(thd, ER_SYNTAX_ERROR); + MYSQL_YYABORT; + } + } + expr + { + Select->in_sum_expr--; + $$= $3; + } + ; + +cast_type: + BINARY opt_field_length + { $$.set(ITEM_CAST_CHAR, $2); Lex->charset= &my_charset_bin; } + | CHAR_SYM opt_field_length + { Lex->charset= thd->variables.collation_connection; } + opt_binary + { $$.set(ITEM_CAST_CHAR, $2); } + | NCHAR_SYM opt_field_length + { + Lex->charset= national_charset_info; + $$.set(ITEM_CAST_CHAR, $2, 0); + } + | cast_type_numeric { $$= $1; Lex->charset= NULL; } + | cast_type_temporal { $$= $1; Lex->charset= NULL; } + ; + +cast_type_numeric: + INT_SYM { $$.set(ITEM_CAST_SIGNED_INT); } + | SIGNED_SYM { $$.set(ITEM_CAST_SIGNED_INT); } + | SIGNED_SYM INT_SYM { $$.set(ITEM_CAST_SIGNED_INT); } + | UNSIGNED { $$.set(ITEM_CAST_UNSIGNED_INT); } + | UNSIGNED INT_SYM { $$.set(ITEM_CAST_UNSIGNED_INT); } + | DECIMAL_SYM float_options { $$.set(ITEM_CAST_DECIMAL, $2); } + | DOUBLE_SYM opt_precision { $$.set(ITEM_CAST_DOUBLE, $2); } + ; + +cast_type_temporal: + DATE_SYM { $$.set(ITEM_CAST_DATE); } + | TIME_SYM opt_field_length { $$.set(ITEM_CAST_TIME, 0, $2); } + | DATETIME opt_field_length { $$.set(ITEM_CAST_DATETIME, 0, $2); } + ; + +opt_expr_list: + /* empty */ { $$= NULL; } + | expr_list { $$= $1;} + ; + +expr_list: + expr + { + $$= new (thd->mem_root) List<Item>; + if ($$ == NULL) + MYSQL_YYABORT; + $$->push_back($1, thd->mem_root); + } + | expr_list ',' expr + { + $1->push_back($3, thd->mem_root); + $$= $1; + } + ; + +ident_list_arg: + ident_list { $$= $1; } + | '(' ident_list ')' { $$= $2; } + ; + +ident_list: + simple_ident + { + $$= new (thd->mem_root) List<Item>; + if ($$ == NULL) + MYSQL_YYABORT; + $$->push_back($1, thd->mem_root); + } + | ident_list ',' simple_ident + { + $1->push_back($3, thd->mem_root); + $$= $1; + } + ; + +opt_expr: + /* empty */ { $$= NULL; } + | expr { $$= $1; } + ; + +opt_else: + /* empty */ { $$= NULL; } + | ELSE expr { $$= $2; } + ; + +when_list: + WHEN_SYM expr THEN_SYM expr + { + $$= new (thd->mem_root) List<Item>; + if ($$ == NULL) + MYSQL_YYABORT; + $$->push_back($2, thd->mem_root); + $$->push_back($4, thd->mem_root); + } + | when_list WHEN_SYM expr THEN_SYM expr + { + $1->push_back($3, thd->mem_root); + $1->push_back($5, thd->mem_root); + $$= $1; + } + ; + +/* Equivalent to <table reference> in the SQL:2003 standard. */ +/* Warning - may return NULL in case of incomplete SELECT */ +table_ref: + table_factor { $$= $1; } + | join_table + { + LEX *lex= Lex; + if (!($$= lex->current_select->nest_last_join(thd))) + { + my_parse_error(thd, ER_SYNTAX_ERROR); + MYSQL_YYABORT; + } + } + ; + +join_table_list: + derived_table_list { MYSQL_YYABORT_UNLESS($$=$1); } + ; + +/* + The ODBC escape syntax for Outer Join is: '{' OJ join_table '}' + The parser does not define OJ as a token, any ident is accepted + instead in $2 (ident). Also, all productions from table_ref can + be escaped, not only join_table. Both syntax extensions are safe + and are ignored. +*/ +esc_table_ref: + table_ref { $$=$1; } + | '{' ident table_ref '}' { $$=$3; } + ; + +/* Equivalent to <table reference list> in the SQL:2003 standard. */ +/* Warning - may return NULL in case of incomplete SELECT */ +derived_table_list: + esc_table_ref { $$=$1; } + | derived_table_list ',' esc_table_ref + { + MYSQL_YYABORT_UNLESS($1 && ($$=$3)); + } + ; + +/* + Notice that JOIN is a left-associative operation, and it must be parsed + as such, that is, the parser must process first the left join operand + then the right one. Such order of processing ensures that the parser + produces correct join trees which is essential for semantic analysis + and subsequent optimization phases. +*/ +join_table: + /* INNER JOIN variants */ + /* + Use %prec to evaluate production 'table_ref' before 'normal_join' + so that [INNER | CROSS] JOIN is properly nested as other + left-associative joins. + */ + table_ref normal_join table_ref %prec TABLE_REF_PRIORITY + { MYSQL_YYABORT_UNLESS($1 && ($$=$3)); $3->straight=$2; } + | table_ref normal_join table_ref + ON + { + MYSQL_YYABORT_UNLESS($1 && $3); + /* Change the current name resolution context to a local context. */ + if (push_new_name_resolution_context(thd, $1, $3)) + MYSQL_YYABORT; + Select->parsing_place= IN_ON; + } + expr + { + $3->straight=$2; + add_join_on(thd, $3, $6); + Lex->pop_context(); + Select->parsing_place= NO_MATTER; + } + | table_ref normal_join table_ref + USING + { + MYSQL_YYABORT_UNLESS($1 && $3); + } + '(' using_list ')' + { + $3->straight=$2; + add_join_natural($1,$3,$7,Select); + $$=$3; + } + | table_ref NATURAL inner_join table_factor + { + MYSQL_YYABORT_UNLESS($1 && ($$=$4)); + $4->straight=$3; + add_join_natural($1,$4,NULL,Select); + } + + /* LEFT JOIN variants */ + | table_ref LEFT opt_outer JOIN_SYM table_ref + ON + { + MYSQL_YYABORT_UNLESS($1 && $5); + /* Change the current name resolution context to a local context. */ + if (push_new_name_resolution_context(thd, $1, $5)) + MYSQL_YYABORT; + Select->parsing_place= IN_ON; + } + expr + { + add_join_on(thd, $5, $8); + Lex->pop_context(); + $5->outer_join|=JOIN_TYPE_LEFT; + $$=$5; + Select->parsing_place= NO_MATTER; + } + | table_ref LEFT opt_outer JOIN_SYM table_factor + { + MYSQL_YYABORT_UNLESS($1 && $5); + } + USING '(' using_list ')' + { + add_join_natural($1,$5,$9,Select); + $5->outer_join|=JOIN_TYPE_LEFT; + $$=$5; + } + | table_ref NATURAL LEFT opt_outer JOIN_SYM table_factor + { + MYSQL_YYABORT_UNLESS($1 && $6); + add_join_natural($1,$6,NULL,Select); + $6->outer_join|=JOIN_TYPE_LEFT; + $$=$6; + } + + /* RIGHT JOIN variants */ + | table_ref RIGHT opt_outer JOIN_SYM table_ref + ON + { + MYSQL_YYABORT_UNLESS($1 && $5); + /* Change the current name resolution context to a local context. */ + if (push_new_name_resolution_context(thd, $1, $5)) + MYSQL_YYABORT; + Select->parsing_place= IN_ON; + } + expr + { + LEX *lex= Lex; + if (!($$= lex->current_select->convert_right_join())) + MYSQL_YYABORT; + add_join_on(thd, $$, $8); + Lex->pop_context(); + Select->parsing_place= NO_MATTER; + } + | table_ref RIGHT opt_outer JOIN_SYM table_factor + { + MYSQL_YYABORT_UNLESS($1 && $5); + } + USING '(' using_list ')' + { + LEX *lex= Lex; + if (!($$= lex->current_select->convert_right_join())) + MYSQL_YYABORT; + add_join_natural($$,$5,$9,Select); + } + | table_ref NATURAL RIGHT opt_outer JOIN_SYM table_factor + { + MYSQL_YYABORT_UNLESS($1 && $6); + add_join_natural($6,$1,NULL,Select); + LEX *lex= Lex; + if (!($$= lex->current_select->convert_right_join())) + MYSQL_YYABORT; + } + ; + + +inner_join: /* $$ set if using STRAIGHT_JOIN, false otherwise */ + JOIN_SYM { $$ = 0; } + | INNER_SYM JOIN_SYM { $$ = 0; } + | STRAIGHT_JOIN { $$ = 1; } + ; + +normal_join: + inner_join { $$ = $1; } + | CROSS JOIN_SYM { $$ = 0; } + ; + +/* + table PARTITION (list of partitions), reusing using_list instead of creating + a new rule for partition_list. +*/ +opt_use_partition: + /* empty */ { $$= 0;} + | use_partition + ; + +use_partition: + PARTITION_SYM '(' using_list ')' have_partitioning + { + $$= $3; + } + ; + +/* + This is a flattening of the rules <table factor> and <table primary> + in the SQL:2003 standard, since we don't have <sample clause> + + I.e. + <table factor> ::= <table primary> [ <sample clause> ] +*/ +/* Warning - may return NULL in case of incomplete SELECT */ +table_factor: + table_primary_ident + | table_primary_derived + ; + +table_primary_ident: + { + SELECT_LEX *sel= Select; + sel->table_join_options= 0; + } + table_ident opt_use_partition opt_table_alias opt_key_definition + { + if (!($$= Select->add_table_to_list(thd, $2, $4, + Select->get_table_join_options(), + YYPS->m_lock_type, + YYPS->m_mdl_type, + Select->pop_index_hints(), + $3))) + MYSQL_YYABORT; + Select->add_joined_table($$); + } + ; + + + +/* + Represents a flattening of the following rules from the SQL:2003 + standard. This sub-rule corresponds to the sub-rule + <table primary> ::= ... | <derived table> [ AS ] <correlation name> + + <derived table> ::= <table subquery> + <table subquery> ::= <subquery> + <subquery> ::= <left paren> <query expression> <right paren> + <query expression> ::= [ <with clause> ] <query expression body> + + For the time being we use the non-standard rule + select_derived_union which is a compromise between the standard + and our parser. Possibly this rule could be replaced by our + query_expression_body. +*/ + +table_primary_derived: + '(' get_select_lex select_derived_union ')' opt_table_alias + { + /* Use $2 instead of Lex->current_select as derived table will + alter value of Lex->current_select. */ + if (!($3 || $5) && $2->embedding && + !$2->embedding->nested_join->join_list.elements) + { + /* we have a derived table ($3 == NULL) but no alias, + Since we are nested in further parentheses so we + can pass NULL to the outer level parentheses + Permits parsing of "((((select ...))) as xyz)" */ + $$= 0; + } + else if (!$3) + { + /* Handle case of derived table, alias may be NULL if there + are no outer parentheses, add_table_to_list() will throw + error in this case */ + LEX *lex=Lex; + lex->check_automatic_up(UNSPECIFIED_TYPE); + SELECT_LEX *sel= lex->current_select; + SELECT_LEX_UNIT *unit= sel->master_unit(); + lex->current_select= sel= unit->outer_select(); + Table_ident *ti= new (thd->mem_root) Table_ident(unit); + if (ti == NULL) + MYSQL_YYABORT; + if (!($$= sel->add_table_to_list(thd, + ti, $5, 0, + TL_READ, MDL_SHARED_READ))) + + MYSQL_YYABORT; + sel->add_joined_table($$); + lex->pop_context(); + lex->nest_level--; + } + else if ($5 != NULL) + { + /* + Tables with or without joins within parentheses cannot + have aliases, and we ruled out derived tables above. + */ + my_parse_error(thd, ER_SYNTAX_ERROR); + MYSQL_YYABORT; + } + else + { + /* nested join: FROM (t1 JOIN t2 ...), + nest_level is the same as in the outer query */ + $$= $3; + } + /* + Fields in derived table can be used in upper select in + case of merge. We do not add HAVING fields because we do + not merge such derived. We do not add union because + also do not merge them + */ + if ($$ && $$->derived && + !$$->derived->first_select()->next_select()) + $$->select_lex->add_where_field($$->derived->first_select()); + } + /* Represents derived table with WITH clause */ + | '(' get_select_lex subselect_start + with_clause query_expression_body + subselect_end ')' opt_table_alias + { + LEX *lex=Lex; + SELECT_LEX *sel= $2; + SELECT_LEX_UNIT *unit= $5->master_unit(); + Table_ident *ti= new (thd->mem_root) Table_ident(unit); + if (ti == NULL) + MYSQL_YYABORT; + $5->set_with_clause($4); + lex->current_select= sel; + if (!($$= sel->add_table_to_list(lex->thd, + ti, $8, 0, + TL_READ, MDL_SHARED_READ))) + MYSQL_YYABORT; + sel->add_joined_table($$); + } + ; + +/* + This rule accepts just about anything. The reason is that we have + empty-producing rules in the beginning of rules, in this case + subselect_start. This forces bison to take a decision which rules to + reduce by long before it has seen any tokens. This approach ties us + to a very limited class of parseable languages, and unfortunately + SQL is not one of them. The chosen 'solution' was this rule, which + produces just about anything, even complete bogus statements, for + instance ( table UNION SELECT 1 ). + Fortunately, we know that the semantic value returned by + select_derived is NULL if it contained a derived table, and a pointer to + the base table's TABLE_LIST if it was a base table. So in the rule + regarding union's, we throw a parse error manually and pretend it + was bison that did it. + + Also worth noting is that this rule concerns query expressions in + the from clause only. Top level select statements and other types of + subqueries have their own union rules. +*/ +select_derived_union: + select_derived + | select_derived union_order_or_limit + { + if ($1) + { + my_parse_error(thd, ER_SYNTAX_ERROR); + MYSQL_YYABORT; + } + } + | select_derived union_head_non_top + { + if ($1) + { + my_parse_error(thd, ER_SYNTAX_ERROR); + MYSQL_YYABORT; + } + } + union_list_derived_part2 + | derived_query_specification opt_select_lock_type + | derived_query_specification order_or_limit opt_select_lock_type + | derived_query_specification opt_select_lock_type union_list_derived + ; + +union_list_derived_part2: + query_term_union_not_ready { Lex->pop_context(); } + | query_term_union_ready { Lex->pop_context(); } + | query_term_union_ready { Lex->pop_context(); } union_list_derived + ; + +union_list_derived: + union_head_non_top union_list_derived_part2 + ; + + +/* The equivalent of select_init2 for nested queries. */ +select_init2_derived: + select_part2_derived + { + Select->set_braces(0); + } + ; + +/* The equivalent of select_part2 for nested queries. */ +select_part2_derived: + { + LEX *lex= Lex; + SELECT_LEX *sel= lex->current_select; + if (sel->linkage != UNION_TYPE) + mysql_init_select(lex); + lex->current_select->parsing_place= SELECT_LIST; + } + opt_query_expression_options select_item_list + { + Select->parsing_place= NO_MATTER; + } + ; + +/* handle contents of parentheses in join expression */ +select_derived: + get_select_lex_derived derived_table_list + { + LEX *lex= Lex; + /* for normal joins, $2 != NULL and end_nested_join() != NULL, + for derived tables, both must equal NULL */ + + if (!($$= $1->end_nested_join(lex->thd)) && $2) + MYSQL_YYABORT; + if (!$2 && $$) + { + my_parse_error(thd, ER_SYNTAX_ERROR); + MYSQL_YYABORT; + } + } + ; + +/* + Similar to query_specification, but for derived tables. + Example: the inner parenthesized SELECT in this query: + SELECT * FROM (SELECT * FROM t1); +*/ +derived_query_specification: + SELECT_SYM select_derived_init select_derived2 + { + if ($2) + Select->set_braces(1); + $$= NULL; + } + ; + +select_derived2: + { + LEX *lex= Lex; + lex->derived_tables|= DERIVED_SUBQUERY; + if (!lex->expr_allows_subselect || + lex->sql_command == (int)SQLCOM_PURGE) + { + my_parse_error(thd, ER_SYNTAX_ERROR); + MYSQL_YYABORT; + } + if (lex->current_select->linkage == GLOBAL_OPTIONS_TYPE || + mysql_new_select(lex, 1, NULL)) + MYSQL_YYABORT; + mysql_init_select(lex); + lex->current_select->linkage= DERIVED_TABLE_TYPE; + lex->current_select->parsing_place= SELECT_LIST; + } + select_options select_item_list + { + Select->parsing_place= NO_MATTER; + } + opt_table_expression + ; + +get_select_lex: + /* Empty */ { $$= Select; } + ; + +get_select_lex_derived: + get_select_lex + { + LEX *lex= Lex; + if ($1->init_nested_join(lex->thd)) + MYSQL_YYABORT; + } + ; + +select_derived_init: + { + LEX *lex= Lex; + + TABLE_LIST *embedding= lex->current_select->embedding; + $$= embedding && + !embedding->nested_join->join_list.elements; + /* return true if we are deeply nested */ + } + ; + +opt_outer: + /* empty */ {} + | OUTER {} + ; + +index_hint_clause: + /* empty */ + { + $$= thd->variables.old_mode ? INDEX_HINT_MASK_JOIN : INDEX_HINT_MASK_ALL; + } + | FOR_SYM JOIN_SYM { $$= INDEX_HINT_MASK_JOIN; } + | FOR_SYM ORDER_SYM BY { $$= INDEX_HINT_MASK_ORDER; } + | FOR_SYM GROUP_SYM BY { $$= INDEX_HINT_MASK_GROUP; } + ; + +index_hint_type: + FORCE_SYM { $$= INDEX_HINT_FORCE; } + | IGNORE_SYM { $$= INDEX_HINT_IGNORE; } + ; + +index_hint_definition: + index_hint_type key_or_index index_hint_clause + { + Select->set_index_hint_type($1, $3); + } + '(' key_usage_list ')' + | USE_SYM key_or_index index_hint_clause + { + Select->set_index_hint_type(INDEX_HINT_USE, $3); + } + '(' opt_key_usage_list ')' + ; + +index_hints_list: + index_hint_definition + | index_hints_list index_hint_definition + ; + +opt_index_hints_list: + /* empty */ + | { Select->alloc_index_hints(thd); } index_hints_list + ; + +opt_key_definition: + { Select->clear_index_hints(); } + opt_index_hints_list + ; + +opt_key_usage_list: + /* empty */ { Select->add_index_hint(thd, NULL, 0); } + | key_usage_list {} + ; + +key_usage_element: + ident + { Select->add_index_hint(thd, $1.str, $1.length); } + | PRIMARY_SYM + { Select->add_index_hint(thd, (char *)"PRIMARY", 7); } + ; + +key_usage_list: + key_usage_element + | key_usage_list ',' key_usage_element + ; + +using_list: + ident + { + if (!($$= new (thd->mem_root) List<String>)) + MYSQL_YYABORT; + String *s= new (thd->mem_root) String((const char *) $1.str, + $1.length, + system_charset_info); + if (s == NULL) + MYSQL_YYABORT; + $$->push_back(s, thd->mem_root); + } + | using_list ',' ident + { + String *s= new (thd->mem_root) String((const char *) $3.str, + $3.length, + system_charset_info); + if (s == NULL) + MYSQL_YYABORT; + $1->push_back(s, thd->mem_root); + $$= $1; + } + ; + +interval: + interval_time_stamp {} + | DAY_HOUR_SYM { $$=INTERVAL_DAY_HOUR; } + | DAY_MICROSECOND_SYM { $$=INTERVAL_DAY_MICROSECOND; } + | DAY_MINUTE_SYM { $$=INTERVAL_DAY_MINUTE; } + | DAY_SECOND_SYM { $$=INTERVAL_DAY_SECOND; } + | HOUR_MICROSECOND_SYM { $$=INTERVAL_HOUR_MICROSECOND; } + | HOUR_MINUTE_SYM { $$=INTERVAL_HOUR_MINUTE; } + | HOUR_SECOND_SYM { $$=INTERVAL_HOUR_SECOND; } + | MINUTE_MICROSECOND_SYM { $$=INTERVAL_MINUTE_MICROSECOND; } + | MINUTE_SECOND_SYM { $$=INTERVAL_MINUTE_SECOND; } + | SECOND_MICROSECOND_SYM { $$=INTERVAL_SECOND_MICROSECOND; } + | YEAR_MONTH_SYM { $$=INTERVAL_YEAR_MONTH; } + ; + +interval_time_stamp: + DAY_SYM { $$=INTERVAL_DAY; } + | WEEK_SYM { $$=INTERVAL_WEEK; } + | HOUR_SYM { $$=INTERVAL_HOUR; } + | MINUTE_SYM { $$=INTERVAL_MINUTE; } + | MONTH_SYM { $$=INTERVAL_MONTH; } + | QUARTER_SYM { $$=INTERVAL_QUARTER; } + | SECOND_SYM { $$=INTERVAL_SECOND; } + | MICROSECOND_SYM { $$=INTERVAL_MICROSECOND; } + | YEAR_SYM { $$=INTERVAL_YEAR; } + ; + +date_time_type: + DATE_SYM {$$=MYSQL_TIMESTAMP_DATE;} + | TIME_SYM {$$=MYSQL_TIMESTAMP_TIME;} + | DATETIME {$$=MYSQL_TIMESTAMP_DATETIME;} + | TIMESTAMP {$$=MYSQL_TIMESTAMP_DATETIME;} + ; + +table_alias: + /* empty */ + | AS + | '=' + ; + +opt_table_alias: + /* empty */ { $$=0; } + | table_alias ident + { + $$= (LEX_STRING*) thd->memdup(&$2,sizeof(LEX_STRING)); + if ($$ == NULL) + MYSQL_YYABORT; + } + ; + +opt_all: + /* empty */ + | ALL + ; + +opt_where_clause: + /* empty */ { Select->where= 0; } + | WHERE + { + Select->parsing_place= IN_WHERE; + } + expr + { + SELECT_LEX *select= Select; + select->where= normalize_cond(thd, $3); + select->parsing_place= NO_MATTER; + if ($3) + $3->top_level_item(); + } + ; + +opt_having_clause: + /* empty */ + | HAVING + { + Select->parsing_place= IN_HAVING; + } + expr + { + SELECT_LEX *sel= Select; + sel->having= normalize_cond(thd, $3); + sel->parsing_place= NO_MATTER; + if ($3) + $3->top_level_item(); + } + ; + +opt_escape: + ESCAPE_SYM simple_expr + { + Lex->escape_used= TRUE; + $$= $2; + } + | /* empty */ + { + Lex->escape_used= FALSE; + $$= ((thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES) ? + new (thd->mem_root) Item_string_ascii(thd, "", 0) : + new (thd->mem_root) Item_string_ascii(thd, "\\", 1)); + if ($$ == NULL) + MYSQL_YYABORT; + } + ; + +/* + group by statement in select +*/ + +opt_group_clause: + /* empty */ + | GROUP_SYM BY group_list olap_opt + ; + +group_list: + group_list ',' order_ident order_dir + { if (add_group_to_list(thd, $3,(bool) $4)) MYSQL_YYABORT; } + | order_ident order_dir + { if (add_group_to_list(thd, $1,(bool) $2)) MYSQL_YYABORT; } + ; + +olap_opt: + /* empty */ {} + | WITH_CUBE_SYM + { + /* + 'WITH CUBE' is reserved in the MySQL syntax, but not implemented, + and cause LALR(2) conflicts. + This syntax is not standard. + MySQL syntax: GROUP BY col1, col2, col3 WITH CUBE + SQL-2003: GROUP BY ... CUBE(col1, col2, col3) + */ + LEX *lex=Lex; + if (lex->current_select->linkage == GLOBAL_OPTIONS_TYPE) + my_yyabort_error((ER_WRONG_USAGE, MYF(0), "WITH CUBE", + "global union parameters")); + lex->current_select->olap= CUBE_TYPE; + + my_yyabort_error((ER_NOT_SUPPORTED_YET, MYF(0), "CUBE")); + } + | WITH_ROLLUP_SYM + { + /* + 'WITH ROLLUP' is needed for backward compatibility, + and cause LALR(2) conflicts. + This syntax is not standard. + MySQL syntax: GROUP BY col1, col2, col3 WITH ROLLUP + SQL-2003: GROUP BY ... ROLLUP(col1, col2, col3) + */ + LEX *lex= Lex; + if (lex->current_select->linkage == GLOBAL_OPTIONS_TYPE) + my_yyabort_error((ER_WRONG_USAGE, MYF(0), "WITH ROLLUP", + "global union parameters")); + lex->current_select->olap= ROLLUP_TYPE; + } + ; + +/* + optional window clause in select +*/ + +opt_window_clause: + /* empty */ + {} + | WINDOW_SYM + window_def_list + {} + ; + +window_def_list: + window_def_list ',' window_def + | window_def + ; + +window_def: + window_name AS window_spec + { + LEX *lex= Lex; + if (Select->add_window_def(thd, $1, lex->win_ref, + Select->group_list, + Select->order_list, + lex->win_frame )) + MYSQL_YYABORT; + } + ; + +window_spec: + '(' + { Select->prepare_add_window_spec(thd); } + opt_window_ref opt_window_partition_clause + opt_window_order_clause opt_window_frame_clause + ')' + ; + +opt_window_ref: + /* empty */ {} + | ident + { + thd->lex->win_ref= (LEX_STRING *) thd->memdup(&$1, sizeof(LEX_STRING)); + if (thd->lex->win_ref == NULL) + MYSQL_YYABORT; + } + +opt_window_partition_clause: + /* empty */ { } + | PARTITION_SYM BY group_list + ; + +opt_window_order_clause: + /* empty */ { } + | ORDER_SYM BY order_list + ; + +opt_window_frame_clause: + /* empty */ {} + | window_frame_units window_frame_extent opt_window_frame_exclusion + { + LEX *lex= Lex; + lex->win_frame= + new (thd->mem_root) Window_frame($1, + lex->frame_top_bound, + lex->frame_bottom_bound, + $3); + if (lex->win_frame == NULL) + MYSQL_YYABORT; + } + ; + +window_frame_units: + ROWS_SYM { $$= Window_frame::UNITS_ROWS; } + | RANGE_SYM { $$= Window_frame::UNITS_RANGE; } + ; + +window_frame_extent: + window_frame_start + { + LEX *lex= Lex; + lex->frame_top_bound= $1; + lex->frame_bottom_bound= + new (thd->mem_root) + Window_frame_bound(Window_frame_bound::CURRENT, NULL); + if (lex->frame_bottom_bound == NULL) + MYSQL_YYABORT; + } + | BETWEEN_SYM window_frame_bound AND_SYM window_frame_bound + { + LEX *lex= Lex; + lex->frame_top_bound= $2; + lex->frame_bottom_bound= $4; + } + ; + +window_frame_start: + UNBOUNDED_SYM PRECEDING_SYM + { + $$= new (thd->mem_root) + Window_frame_bound(Window_frame_bound::PRECEDING, NULL); + if ($$ == NULL) + MYSQL_YYABORT; + } + | CURRENT_SYM ROW_SYM + { + $$= new (thd->mem_root) + Window_frame_bound(Window_frame_bound::CURRENT, NULL); + if ($$ == NULL) + MYSQL_YYABORT; + } + | literal PRECEDING_SYM + { + $$= new (thd->mem_root) + Window_frame_bound(Window_frame_bound::PRECEDING, $1); + if ($$ == NULL) + MYSQL_YYABORT; + } + ; + +window_frame_bound: + window_frame_start { $$= $1; } + | UNBOUNDED_SYM FOLLOWING_SYM + { + $$= new (thd->mem_root) + Window_frame_bound(Window_frame_bound::FOLLOWING, NULL); + if ($$ == NULL) + MYSQL_YYABORT; + } + | literal FOLLOWING_SYM + { + $$= new (thd->mem_root) + Window_frame_bound(Window_frame_bound::FOLLOWING, $1); + if ($$ == NULL) + MYSQL_YYABORT; + } + ; + +opt_window_frame_exclusion: + /* empty */ { $$= Window_frame::EXCL_NONE; } + | EXCLUDE_SYM CURRENT_SYM ROW_SYM + { $$= Window_frame::EXCL_CURRENT_ROW; } + | EXCLUDE_SYM GROUP_SYM + { $$= Window_frame::EXCL_GROUP; } + | EXCLUDE_SYM TIES_SYM + { $$= Window_frame::EXCL_TIES; } + | EXCLUDE_SYM NO_SYM OTHERS_SYM + { $$= Window_frame::EXCL_NONE; } + ; + +/* + Order by statement in ALTER TABLE +*/ + +alter_order_clause: + ORDER_SYM BY alter_order_list + ; + +alter_order_list: + alter_order_list ',' alter_order_item + | alter_order_item + ; + +alter_order_item: + simple_ident_nospvar order_dir + { + bool ascending= ($2 == 1) ? true : false; + if (add_order_to_list(thd, $1, ascending)) + MYSQL_YYABORT; + } + ; + +/* + Order by statement in select +*/ + +opt_order_clause: + /* empty */ + | order_clause + ; + +order_clause: + ORDER_SYM BY + { + LEX *lex=Lex; + SELECT_LEX *sel= lex->current_select; + SELECT_LEX_UNIT *unit= sel-> master_unit(); + if (sel->linkage != GLOBAL_OPTIONS_TYPE && + sel->olap != UNSPECIFIED_OLAP_TYPE && + (sel->linkage != UNION_TYPE || sel->braces)) + { + my_error(ER_WRONG_USAGE, MYF(0), + "CUBE/ROLLUP", "ORDER BY"); + MYSQL_YYABORT; + } + if (lex->sql_command != SQLCOM_ALTER_TABLE && + !unit->fake_select_lex) + { + /* + A query of the of the form (SELECT ...) ORDER BY order_list is + executed in the same way as the query + SELECT ... ORDER BY order_list + unless the SELECT construct contains ORDER BY or LIMIT clauses. + Otherwise we create a fake SELECT_LEX if it has not been created + yet. + */ + SELECT_LEX *first_sl= unit->first_select(); + if (!unit->is_unit_op() && + (first_sl->order_list.elements || + first_sl->select_limit) && + unit->add_fake_select_lex(thd)) + MYSQL_YYABORT; + } + if (sel->master_unit()->is_unit_op() && !sel->braces) + { + /* + At this point we don't know yet whether this is the last + select in union or not, but we move ORDER BY to + fake_select_lex anyway. If there would be one more select + in union mysql_new_select will correctly throw error. + */ + DBUG_ASSERT(sel->master_unit()->fake_select_lex); + lex->current_select= sel->master_unit()->fake_select_lex; + } + } + order_list + { + + } + ; + +order_list: + order_list ',' order_ident order_dir + { if (add_order_to_list(thd, $3,(bool) $4)) MYSQL_YYABORT; } + | order_ident order_dir + { if (add_order_to_list(thd, $1,(bool) $2)) MYSQL_YYABORT; } + ; + +order_dir: + /* empty */ { $$ = 1; } + | ASC { $$ =1; } + | DESC { $$ =0; } + ; + +opt_limit_clause: + /* empty */ {} + | limit_clause {} + ; + +limit_clause_init: + LIMIT + { + SELECT_LEX *sel= Select; + if (sel->master_unit()->is_unit_op() && !sel->braces) + { + /* Move LIMIT that belongs to UNION to fake_select_lex */ + Lex->current_select= sel->master_unit()->fake_select_lex; + DBUG_ASSERT(Select); + } + } + ; + +limit_clause: + limit_clause_init limit_options + { + SELECT_LEX *sel= Select; + if (!sel->select_limit->basic_const_item() || + sel->select_limit->val_int() > 0) + Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_LIMIT); + } + | limit_clause_init limit_options + ROWS_SYM EXAMINED_SYM limit_rows_option + { + Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_LIMIT); + } + | limit_clause_init ROWS_SYM EXAMINED_SYM limit_rows_option + { + Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_LIMIT); + } + ; + +limit_options: + limit_option + { + SELECT_LEX *sel= Select; + sel->select_limit= $1; + sel->offset_limit= 0; + sel->explicit_limit= 1; + } + | limit_option ',' limit_option + { + SELECT_LEX *sel= Select; + sel->select_limit= $3; + sel->offset_limit= $1; + sel->explicit_limit= 1; + } + | limit_option OFFSET_SYM limit_option + { + SELECT_LEX *sel= Select; + sel->select_limit= $1; + sel->offset_limit= $3; + sel->explicit_limit= 1; + } + ; + +limit_option: + ident + { + Item_splocal *splocal; + LEX *lex= thd->lex; + Lex_input_stream *lip= & thd->m_parser_state->m_lip; + sp_variable *spv; + sp_pcontext *spc = lex->spcont; + if (spc && (spv = spc->find_variable($1, false))) + { + splocal= new (thd->mem_root) + Item_splocal(thd, $1, spv->offset, spv->sql_type(), + lip->get_tok_start() - lex->sphead->m_tmp_query, + lip->get_ptr() - lip->get_tok_start()); + if (splocal == NULL) + MYSQL_YYABORT; +#ifndef DBUG_OFF + splocal->m_sp= lex->sphead; +#endif + lex->safe_to_cache_query=0; + } + else + my_yyabort_error((ER_SP_UNDECLARED_VAR, MYF(0), $1.str)); + if (splocal->type() != Item::INT_ITEM) + my_yyabort_error((ER_WRONG_SPVAR_TYPE_IN_LIMIT, MYF(0))); + splocal->limit_clause_param= TRUE; + $$= splocal; + } + | param_marker + { + $1->limit_clause_param= TRUE; + } + | ULONGLONG_NUM + { + $$= new (thd->mem_root) Item_uint(thd, $1.str, $1.length); + if ($$ == NULL) + MYSQL_YYABORT; + } + | LONG_NUM + { + $$= new (thd->mem_root) Item_uint(thd, $1.str, $1.length); + if ($$ == NULL) + MYSQL_YYABORT; + } + | NUM + { + $$= new (thd->mem_root) Item_uint(thd, $1.str, $1.length); + if ($$ == NULL) + MYSQL_YYABORT; + } + ; + +limit_rows_option: + limit_option + { + LEX *lex=Lex; + lex->limit_rows_examined= $1; + } + +delete_limit_clause: + /* empty */ + { + LEX *lex=Lex; + lex->current_select->select_limit= 0; + } + | LIMIT limit_option + { + SELECT_LEX *sel= Select; + sel->select_limit= $2; + Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_LIMIT); + sel->explicit_limit= 1; + } + | LIMIT ROWS_SYM EXAMINED_SYM { my_parse_error(thd, ER_SYNTAX_ERROR); MYSQL_YYABORT; } + | LIMIT limit_option ROWS_SYM EXAMINED_SYM { my_parse_error(thd, ER_SYNTAX_ERROR); MYSQL_YYABORT; } + ; + +int_num: + NUM { int error; $$= (int) my_strtoll10($1.str, (char**) 0, &error); } + | '-' NUM { int error; $$= -(int) my_strtoll10($2.str, (char**) 0, &error); } + | '-' LONG_NUM { int error; $$= -(int) my_strtoll10($2.str, (char**) 0, &error); } + ; + +ulong_num: + NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); } + | HEX_NUM { $$= (ulong) strtol($1.str, (char**) 0, 16); } + | LONG_NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); } + | ULONGLONG_NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); } + | DECIMAL_NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); } + | FLOAT_NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); } + ; + +real_ulong_num: + NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); } + | HEX_NUM { $$= (ulong) strtol($1.str, (char**) 0, 16); } + | LONG_NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); } + | ULONGLONG_NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); } + | dec_num_error { MYSQL_YYABORT; } + ; + +ulonglong_num: + NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); } + | ULONGLONG_NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); } + | LONG_NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); } + | DECIMAL_NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); } + | FLOAT_NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); } + ; + +real_ulonglong_num: + NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); } + | ULONGLONG_NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); } + | HEX_NUM { $$= strtoull($1.str, (char**) 0, 16); } + | LONG_NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); } + | dec_num_error { MYSQL_YYABORT; } + ; + +dec_num_error: + dec_num + { my_parse_error(thd, ER_ONLY_INTEGERS_ALLOWED); } + ; + +dec_num: + DECIMAL_NUM + | FLOAT_NUM + ; + +choice: + ulong_num { $$= $1 != 0 ? HA_CHOICE_YES : HA_CHOICE_NO; } + | DEFAULT { $$= HA_CHOICE_UNDEF; } + ; + +procedure_clause: + PROCEDURE_SYM ident /* Procedure name */ + { + LEX *lex=Lex; + + DBUG_ASSERT(&lex->select_lex == lex->current_select); + + lex->proc_list.elements=0; + lex->proc_list.first=0; + lex->proc_list.next= &lex->proc_list.first; + Item_field *item= new (thd->mem_root) + Item_field(thd, &lex->current_select->context, + NULL, NULL, $2.str); + if (item == NULL) + MYSQL_YYABORT; + if (add_proc_to_list(thd, item)) + MYSQL_YYABORT; + Lex->uncacheable(UNCACHEABLE_SIDEEFFECT); + + /* + PROCEDURE CLAUSE cannot handle subquery as one of its parameter, + so set expr_allows_subselect as false to disallow any subqueries + further. Reset expr_allows_subselect back to true once the + parameters are reduced. + */ + Lex->expr_allows_subselect= false; + } + '(' procedure_list ')' + { + /* Subqueries are allowed from now.*/ + Lex->expr_allows_subselect= true; + } + ; + +procedure_list: + /* empty */ {} + | procedure_list2 {} + ; + +procedure_list2: + procedure_list2 ',' procedure_item + | procedure_item + ; + +procedure_item: + remember_name expr remember_end + { + if (add_proc_to_list(thd, $2)) + MYSQL_YYABORT; + if (!$2->name) + $2->set_name(thd, $1, (uint) ($3 - $1), thd->charset()); + } + ; + +select_var_list_init: + { + LEX *lex=Lex; + if (!lex->describe && + (!(lex->result= new (thd->mem_root) select_dumpvar(thd)))) + MYSQL_YYABORT; + } + select_var_list + {} + ; + +select_var_list: + select_var_list ',' select_var_ident + | select_var_ident {} + ; + +select_var_ident: select_outvar + { + if (Lex->result) + { + if ($1 == NULL) + MYSQL_YYABORT; + ((select_dumpvar *)Lex->result)->var_list.push_back($1, thd->mem_root); + } + else + { + /* + The parser won't create select_result instance only + if it's an EXPLAIN. + */ + DBUG_ASSERT(Lex->describe); + } + } + ; + +select_outvar: + '@' ident_or_text + { + $$ = Lex->result ? new (thd->mem_root) my_var_user($2) : NULL; + } + | ident_or_text + { + sp_variable *t; + + if (!Lex->spcont || !(t= Lex->spcont->find_variable($1, false))) + my_yyabort_error((ER_SP_UNDECLARED_VAR, MYF(0), $1.str)); + $$ = Lex->result ? (new (thd->mem_root) + my_var_sp($1, t->offset, t->sql_type(), + Lex->sphead)) : + NULL; + } + ; + +into: + INTO into_destination + ; + +into_destination: + OUTFILE TEXT_STRING_filesystem + { + LEX *lex= Lex; + lex->uncacheable(UNCACHEABLE_SIDEEFFECT); + if (!(lex->exchange= + new (thd->mem_root) sql_exchange($2.str, 0)) || + !(lex->result= + new (thd->mem_root) select_export(thd, lex->exchange))) + MYSQL_YYABORT; + } + opt_load_data_charset + { Lex->exchange->cs= $4; } + opt_field_term opt_line_term + | DUMPFILE TEXT_STRING_filesystem + { + LEX *lex=Lex; + if (!lex->describe) + { + lex->uncacheable(UNCACHEABLE_SIDEEFFECT); + if (!(lex->exchange= new (thd->mem_root) sql_exchange($2.str,1))) + MYSQL_YYABORT; + if (!(lex->result= + new (thd->mem_root) select_dump(thd, lex->exchange))) + MYSQL_YYABORT; + } + } + | select_var_list_init + { + Lex->uncacheable(UNCACHEABLE_SIDEEFFECT); + } + ; + +/* + DO statement +*/ + +do: + DO_SYM + { + LEX *lex=Lex; + lex->sql_command = SQLCOM_DO; + mysql_init_select(lex); + } + expr_list + { + Lex->insert_list= $3; + } + ; + +/* + Drop : delete tables or index or user +*/ + +drop: + DROP opt_temporary table_or_tables opt_if_exists + { + LEX *lex=Lex; + lex->set_command(SQLCOM_DROP_TABLE, $2, $4); + YYPS->m_lock_type= TL_UNLOCK; + YYPS->m_mdl_type= MDL_EXCLUSIVE; + } + table_list opt_restrict + {} + | DROP INDEX_SYM opt_if_exists_table_element ident ON table_ident {} + { + LEX *lex=Lex; + Alter_drop *ad= (new (thd->mem_root) + Alter_drop(Alter_drop::KEY, $4.str, $3)); + if (ad == NULL) + MYSQL_YYABORT; + lex->sql_command= SQLCOM_DROP_INDEX; + lex->alter_info.reset(); + lex->alter_info.flags= Alter_info::ALTER_DROP_INDEX; + lex->alter_info.drop_list.push_back(ad, thd->mem_root); + if (!lex->current_select->add_table_to_list(thd, $6, NULL, + TL_OPTION_UPDATING, + TL_READ_NO_INSERT, + MDL_SHARED_UPGRADABLE)) + MYSQL_YYABORT; + } + | DROP DATABASE opt_if_exists ident + { + LEX *lex=Lex; + lex->set_command(SQLCOM_DROP_DB, $3); + lex->name= $4; + } + | DROP FUNCTION_SYM opt_if_exists ident '.' ident + { + LEX *lex= thd->lex; + sp_name *spname; + if ($4.str && check_db_name(&$4)) + my_yyabort_error((ER_WRONG_DB_NAME, MYF(0), $4.str)); + if (lex->sphead) + my_yyabort_error((ER_SP_NO_DROP_SP, MYF(0), "FUNCTION")); + lex->set_command(SQLCOM_DROP_FUNCTION, $3); + spname= new (thd->mem_root) sp_name($4, $6, true); + if (spname == NULL) + MYSQL_YYABORT; + lex->spname= spname; + } + | DROP FUNCTION_SYM opt_if_exists ident + { + LEX *lex= thd->lex; + LEX_STRING db= {0, 0}; + sp_name *spname; + if (lex->sphead) + my_yyabort_error((ER_SP_NO_DROP_SP, MYF(0), "FUNCTION")); + if (thd->db && lex->copy_db_to(&db.str, &db.length)) + MYSQL_YYABORT; + lex->set_command(SQLCOM_DROP_FUNCTION, $3); + spname= new (thd->mem_root) sp_name(db, $4, false); + if (spname == NULL) + MYSQL_YYABORT; + lex->spname= spname; + } + | DROP PROCEDURE_SYM opt_if_exists sp_name + { + LEX *lex=Lex; + if (lex->sphead) + my_yyabort_error((ER_SP_NO_DROP_SP, MYF(0), "PROCEDURE")); + lex->set_command(SQLCOM_DROP_PROCEDURE, $3); + lex->spname= $4; + } + | DROP USER_SYM opt_if_exists clear_privileges user_list + { + Lex->set_command(SQLCOM_DROP_USER, $3); + } + | DROP ROLE_SYM opt_if_exists clear_privileges role_list + { + Lex->set_command(SQLCOM_DROP_ROLE, $3); + } + | DROP VIEW_SYM opt_if_exists + { + LEX *lex= Lex; + lex->set_command(SQLCOM_DROP_VIEW, $3); + YYPS->m_lock_type= TL_UNLOCK; + YYPS->m_mdl_type= MDL_EXCLUSIVE; + } + table_list opt_restrict + {} + | DROP EVENT_SYM opt_if_exists sp_name + { + Lex->spname= $4; + Lex->set_command(SQLCOM_DROP_EVENT, $3); + } + | DROP TRIGGER_SYM opt_if_exists sp_name + { + LEX *lex= Lex; + lex->set_command(SQLCOM_DROP_TRIGGER, $3); + lex->spname= $4; + } + | DROP TABLESPACE tablespace_name opt_ts_engine opt_ts_wait + { + LEX *lex= Lex; + lex->alter_tablespace_info->ts_cmd_type= DROP_TABLESPACE; + } + | DROP LOGFILE_SYM GROUP_SYM logfile_group_name opt_ts_engine opt_ts_wait + { + LEX *lex= Lex; + lex->alter_tablespace_info->ts_cmd_type= DROP_LOGFILE_GROUP; + } + | DROP SERVER_SYM opt_if_exists ident_or_text + { + Lex->set_command(SQLCOM_DROP_SERVER, $3); + Lex->server_options.reset($4); + } + ; + +table_list: + table_name + | table_list ',' table_name + ; + +table_name: + table_ident + { + if (!Select->add_table_to_list(thd, $1, NULL, + TL_OPTION_UPDATING, + YYPS->m_lock_type, + YYPS->m_mdl_type)) + MYSQL_YYABORT; + } + ; + +table_name_with_opt_use_partition: + table_ident opt_use_partition + { + if (!Select->add_table_to_list(thd, $1, NULL, + TL_OPTION_UPDATING, + YYPS->m_lock_type, + YYPS->m_mdl_type, + NULL, + $2)) + MYSQL_YYABORT; + } + ; + +table_alias_ref_list: + table_alias_ref + | table_alias_ref_list ',' table_alias_ref + ; + +table_alias_ref: + table_ident_opt_wild + { + if (!Select->add_table_to_list(thd, $1, NULL, + TL_OPTION_UPDATING | TL_OPTION_ALIAS, + YYPS->m_lock_type, + YYPS->m_mdl_type)) + MYSQL_YYABORT; + } + ; + +opt_if_exists_table_element: + /* empty */ + { + Lex->check_exists= FALSE; + $$= 0; + } + | IF_SYM EXISTS + { + Lex->check_exists= TRUE; + $$= 1; + } + ; + +opt_if_exists: + /* empty */ + { + $$.set(DDL_options_st::OPT_NONE); + } + | IF_SYM EXISTS + { + $$.set(DDL_options_st::OPT_IF_EXISTS); + } + ; + +opt_temporary: + /* empty */ { $$= 0; } + | TEMPORARY { $$= HA_LEX_CREATE_TMP_TABLE; } + ; +/* +** Insert : add new data to table +*/ + +insert: + INSERT + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_INSERT; + lex->duplicates= DUP_ERROR; + mysql_init_select(lex); + } + insert_lock_option + opt_ignore insert2 + { + Select->set_lock_for_tables($3); + Lex->current_select= &Lex->select_lex; + } + insert_field_spec opt_insert_update + {} + ; + +replace: + REPLACE + { + LEX *lex=Lex; + lex->sql_command = SQLCOM_REPLACE; + lex->duplicates= DUP_REPLACE; + mysql_init_select(lex); + } + replace_lock_option insert2 + { + Select->set_lock_for_tables($3); + Lex->current_select= &Lex->select_lex; + } + insert_field_spec + {} + ; + +insert_lock_option: + /* empty */ + { + /* + If it is SP we do not allow insert optimisation when result of + insert visible only after the table unlocking but everyone can + read table. + */ + $$= (Lex->sphead ? TL_WRITE_DEFAULT : TL_WRITE_CONCURRENT_INSERT); + } + | LOW_PRIORITY { $$= TL_WRITE_LOW_PRIORITY; } + | DELAYED_SYM + { + Lex->keyword_delayed_begin_offset= (uint)(YYLIP->get_tok_start() - + thd->query()); + Lex->keyword_delayed_end_offset= Lex->keyword_delayed_begin_offset + + YYLIP->yyLength() + 1; + $$= TL_WRITE_DELAYED; + } + | HIGH_PRIORITY { $$= TL_WRITE; } + ; + +replace_lock_option: + opt_low_priority { $$= $1; } + | DELAYED_SYM + { + Lex->keyword_delayed_begin_offset= (uint)(YYLIP->get_tok_start() - + thd->query()); + Lex->keyword_delayed_end_offset= Lex->keyword_delayed_begin_offset + + YYLIP->yyLength() + 1; + $$= TL_WRITE_DELAYED; + } + ; + +insert2: + INTO insert_table {} + | insert_table {} + ; + +insert_table: + table_name_with_opt_use_partition + { + LEX *lex=Lex; + lex->field_list.empty(); + lex->many_values.empty(); + lex->insert_list=0; + }; + +insert_field_spec: + insert_values {} + | '(' ')' insert_values {} + | '(' fields ')' insert_values {} + | SET + { + LEX *lex=Lex; + if (!(lex->insert_list= new (thd->mem_root) List_item) || + lex->many_values.push_back(lex->insert_list, thd->mem_root)) + MYSQL_YYABORT; + } + ident_eq_list + ; + +fields: + fields ',' insert_ident + { Lex->field_list.push_back($3, thd->mem_root); } + | insert_ident { Lex->field_list.push_back($1, thd->mem_root); } + ; + +insert_values: + VALUES values_list {} + | VALUE_SYM values_list {} + | create_select_query_expression {} + ; + +values_list: + values_list ',' no_braces + | no_braces + ; + +ident_eq_list: + ident_eq_list ',' ident_eq_value + | ident_eq_value + ; + +ident_eq_value: + simple_ident_nospvar equal expr_or_default + { + LEX *lex=Lex; + if (lex->field_list.push_back($1, thd->mem_root) || + lex->insert_list->push_back($3, thd->mem_root)) + MYSQL_YYABORT; + } + ; + +equal: + '=' {} + | SET_VAR {} + ; + +opt_equal: + /* empty */ {} + | equal {} + ; + +no_braces: + '(' + { + if (!(Lex->insert_list= new (thd->mem_root) List_item)) + MYSQL_YYABORT; + } + opt_values ')' + { + LEX *lex=Lex; + if (lex->many_values.push_back(lex->insert_list, thd->mem_root)) + MYSQL_YYABORT; + } + ; + +opt_values: + /* empty */ {} + | values + ; + +values: + values ',' expr_or_default + { + if (Lex->insert_list->push_back($3, thd->mem_root)) + MYSQL_YYABORT; + } + | expr_or_default + { + if (Lex->insert_list->push_back($1, thd->mem_root)) + MYSQL_YYABORT; + } + ; + +expr_or_default: + expr { $$= $1;} + | DEFAULT + { + $$= new (thd->mem_root) Item_default_value(thd, Lex->current_context()); + if ($$ == NULL) + MYSQL_YYABORT; + } + | IGNORE_SYM + { + $$= new (thd->mem_root) Item_ignore_value(thd, Lex->current_context()); + if ($$ == NULL) + MYSQL_YYABORT; + } + ; + +opt_insert_update: + /* empty */ + | ON DUPLICATE_SYM { Lex->duplicates= DUP_UPDATE; } + KEY_SYM UPDATE_SYM insert_update_list + ; + +/* Update rows in a table */ + +update: + UPDATE_SYM + { + LEX *lex= Lex; + mysql_init_select(lex); + lex->sql_command= SQLCOM_UPDATE; + lex->duplicates= DUP_ERROR; + } + opt_low_priority opt_ignore join_table_list + SET update_list + { + LEX *lex= Lex; + if (lex->select_lex.table_list.elements > 1) + lex->sql_command= SQLCOM_UPDATE_MULTI; + else if (lex->select_lex.get_table_list()->derived) + { + /* it is single table update and it is update of derived table */ + my_error(ER_NON_UPDATABLE_TABLE, MYF(0), + lex->select_lex.get_table_list()->alias, "UPDATE"); + MYSQL_YYABORT; + } + /* + In case of multi-update setting write lock for all tables may + be too pessimistic. We will decrease lock level if possible in + mysql_multi_update(). + */ + Select->set_lock_for_tables($3); + } + opt_where_clause opt_order_clause delete_limit_clause {} + ; + +update_list: + update_list ',' update_elem + | update_elem + ; + +update_elem: + simple_ident_nospvar equal expr_or_default + { + if (add_item_to_list(thd, $1) || add_value_to_list(thd, $3)) + MYSQL_YYABORT; + } + ; + +insert_update_list: + insert_update_list ',' insert_update_elem + | insert_update_elem + ; + +insert_update_elem: + simple_ident_nospvar equal expr_or_default + { + LEX *lex= Lex; + if (lex->update_list.push_back($1, thd->mem_root) || + lex->value_list.push_back($3, thd->mem_root)) + MYSQL_YYABORT; + } + ; + +opt_low_priority: + /* empty */ { $$= TL_WRITE_DEFAULT; } + | LOW_PRIORITY { $$= TL_WRITE_LOW_PRIORITY; } + ; + +/* Delete rows from a table */ + +delete: + DELETE_SYM + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_DELETE; + mysql_init_select(lex); + YYPS->m_lock_type= TL_WRITE_DEFAULT; + YYPS->m_mdl_type= MDL_SHARED_WRITE; + + lex->ignore= 0; + lex->select_lex.init_order(); + } + opt_delete_options single_multi + ; + +single_multi: + FROM table_ident opt_use_partition + { + if (!Select->add_table_to_list(thd, $2, NULL, TL_OPTION_UPDATING, + YYPS->m_lock_type, + YYPS->m_mdl_type, + NULL, + $3)) + MYSQL_YYABORT; + YYPS->m_lock_type= TL_READ_DEFAULT; + YYPS->m_mdl_type= MDL_SHARED_READ; + } + opt_where_clause opt_order_clause + delete_limit_clause {} + opt_select_expressions {} + | table_wild_list + { + mysql_init_multi_delete(Lex); + YYPS->m_lock_type= TL_READ_DEFAULT; + YYPS->m_mdl_type= MDL_SHARED_READ; + } + FROM join_table_list opt_where_clause + { + if (multi_delete_set_locks_and_link_aux_tables(Lex)) + MYSQL_YYABORT; + } + | FROM table_alias_ref_list + { + mysql_init_multi_delete(Lex); + YYPS->m_lock_type= TL_READ_DEFAULT; + YYPS->m_mdl_type= MDL_SHARED_READ; + } + USING join_table_list opt_where_clause + { + if (multi_delete_set_locks_and_link_aux_tables(Lex)) + MYSQL_YYABORT; + } + ; + +opt_select_expressions: + /* empty */ + | RETURNING_SYM select_item_list + ; + +table_wild_list: + table_wild_one + | table_wild_list ',' table_wild_one + ; + +table_wild_one: + ident opt_wild + { + Table_ident *ti= new (thd->mem_root) Table_ident($1); + if (ti == NULL) + MYSQL_YYABORT; + if (!Select->add_table_to_list(thd, + ti, + NULL, + TL_OPTION_UPDATING | TL_OPTION_ALIAS, + YYPS->m_lock_type, + YYPS->m_mdl_type)) + MYSQL_YYABORT; + } + | ident '.' ident opt_wild + { + Table_ident *ti= new (thd->mem_root) Table_ident(thd, $1, $3, 0); + if (ti == NULL) + MYSQL_YYABORT; + if (!Select->add_table_to_list(thd, + ti, + NULL, + TL_OPTION_UPDATING | TL_OPTION_ALIAS, + YYPS->m_lock_type, + YYPS->m_mdl_type)) + MYSQL_YYABORT; + } + ; + +opt_wild: + /* empty */ {} + | '.' '*' {} + ; + +opt_delete_options: + /* empty */ {} + | opt_delete_option opt_delete_options {} + ; + +opt_delete_option: + QUICK { Select->options|= OPTION_QUICK; } + | LOW_PRIORITY { YYPS->m_lock_type= TL_WRITE_LOW_PRIORITY; } + | IGNORE_SYM { Lex->ignore= 1; } + ; + +truncate: + TRUNCATE_SYM opt_table_sym + { + LEX* lex= Lex; + lex->sql_command= SQLCOM_TRUNCATE; + lex->alter_info.reset(); + lex->select_lex.options= 0; + lex->select_lex.sql_cache= SELECT_LEX::SQL_CACHE_UNSPECIFIED; + lex->select_lex.init_order(); + YYPS->m_lock_type= TL_WRITE; + YYPS->m_mdl_type= MDL_EXCLUSIVE; + } + table_name + { + LEX* lex= thd->lex; + DBUG_ASSERT(!lex->m_sql_cmd); + lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_truncate_table(); + if (lex->m_sql_cmd == NULL) + MYSQL_YYABORT; + } + ; + +opt_table_sym: + /* empty */ + | TABLE_SYM + ; + +opt_profile_defs: + /* empty */ + | profile_defs; + +profile_defs: + profile_def + | profile_defs ',' profile_def; + +profile_def: + CPU_SYM + { + Lex->profile_options|= PROFILE_CPU; + } + | MEMORY_SYM + { + Lex->profile_options|= PROFILE_MEMORY; + } + | BLOCK_SYM IO_SYM + { + Lex->profile_options|= PROFILE_BLOCK_IO; + } + | CONTEXT_SYM SWITCHES_SYM + { + Lex->profile_options|= PROFILE_CONTEXT; + } + | PAGE_SYM FAULTS_SYM + { + Lex->profile_options|= PROFILE_PAGE_FAULTS; + } + | IPC_SYM + { + Lex->profile_options|= PROFILE_IPC; + } + | SWAPS_SYM + { + Lex->profile_options|= PROFILE_SWAPS; + } + | SOURCE_SYM + { + Lex->profile_options|= PROFILE_SOURCE; + } + | ALL + { + Lex->profile_options|= PROFILE_ALL; + } + ; + +opt_profile_args: + /* empty */ + { + Lex->profile_query_id= 0; + } + | FOR_SYM QUERY_SYM NUM + { + Lex->profile_query_id= atoi($3.str); + } + ; + +/* Show things */ + +show: + SHOW + { + LEX *lex=Lex; + lex->wild=0; + lex->ident=null_lex_str; + mysql_init_select(lex); + lex->current_select->parsing_place= SELECT_LIST; + lex->create_info.init(); + } + show_param + { + Select->parsing_place= NO_MATTER; + } + ; + +show_param: + DATABASES wild_and_where + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_DATABASES; + if (prepare_schema_table(thd, lex, 0, SCH_SCHEMATA)) + MYSQL_YYABORT; + } + | opt_full TABLES opt_db wild_and_where + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_TABLES; + lex->select_lex.db= $3; + if (prepare_schema_table(thd, lex, 0, SCH_TABLE_NAMES)) + MYSQL_YYABORT; + } + | opt_full TRIGGERS_SYM opt_db wild_and_where + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_TRIGGERS; + lex->select_lex.db= $3; + if (prepare_schema_table(thd, lex, 0, SCH_TRIGGERS)) + MYSQL_YYABORT; + } + | EVENTS_SYM opt_db wild_and_where + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_EVENTS; + lex->select_lex.db= $2; + if (prepare_schema_table(thd, lex, 0, SCH_EVENTS)) + MYSQL_YYABORT; + } + | TABLE_SYM STATUS_SYM opt_db wild_and_where + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_TABLE_STATUS; + lex->select_lex.db= $3; + if (prepare_schema_table(thd, lex, 0, SCH_TABLES)) + MYSQL_YYABORT; + } + | OPEN_SYM TABLES opt_db wild_and_where + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_OPEN_TABLES; + lex->select_lex.db= $3; + if (prepare_schema_table(thd, lex, 0, SCH_OPEN_TABLES)) + MYSQL_YYABORT; + } + | PLUGINS_SYM + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_PLUGINS; + if (prepare_schema_table(thd, lex, 0, SCH_PLUGINS)) + MYSQL_YYABORT; + } + | PLUGINS_SYM SONAME_SYM TEXT_STRING_sys + { + Lex->ident= $3; + Lex->sql_command= SQLCOM_SHOW_PLUGINS; + if (prepare_schema_table(thd, Lex, 0, SCH_ALL_PLUGINS)) + MYSQL_YYABORT; + } + | PLUGINS_SYM SONAME_SYM wild_and_where + { + Lex->sql_command= SQLCOM_SHOW_PLUGINS; + if (prepare_schema_table(thd, Lex, 0, SCH_ALL_PLUGINS)) + MYSQL_YYABORT; + } + | ENGINE_SYM known_storage_engines show_engine_param + { Lex->create_info.db_type= $2; } + | ENGINE_SYM ALL show_engine_param + { Lex->create_info.db_type= NULL; } + | opt_full COLUMNS from_or_in table_ident opt_db wild_and_where + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_FIELDS; + if ($5) + $4->change_db($5); + if (prepare_schema_table(thd, lex, $4, SCH_COLUMNS)) + MYSQL_YYABORT; + } + | master_or_binary LOGS_SYM + { + Lex->sql_command = SQLCOM_SHOW_BINLOGS; + } + | SLAVE HOSTS_SYM + { + Lex->sql_command = SQLCOM_SHOW_SLAVE_HOSTS; + } + | BINLOG_SYM EVENTS_SYM binlog_in binlog_from + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_BINLOG_EVENTS; + } + opt_limit_clause + | RELAYLOG_SYM optional_connection_name EVENTS_SYM binlog_in binlog_from + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_RELAYLOG_EVENTS; + } opt_limit_clause + | keys_or_index from_or_in table_ident opt_db opt_where_clause + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_KEYS; + if ($4) + $3->change_db($4); + if (prepare_schema_table(thd, lex, $3, SCH_STATISTICS)) + MYSQL_YYABORT; + } + | opt_storage ENGINES_SYM + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_SHOW_STORAGE_ENGINES; + if (prepare_schema_table(thd, lex, 0, SCH_ENGINES)) + MYSQL_YYABORT; + } + | AUTHORS_SYM + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_SHOW_AUTHORS; + } + | CONTRIBUTORS_SYM + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_SHOW_CONTRIBUTORS; + } + | PRIVILEGES + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_SHOW_PRIVILEGES; + } + | COUNT_SYM '(' '*' ')' WARNINGS + { (void) create_select_for_variable("warning_count"); } + | COUNT_SYM '(' '*' ')' ERRORS + { (void) create_select_for_variable("error_count"); } + | WARNINGS opt_limit_clause + { Lex->sql_command = SQLCOM_SHOW_WARNS;} + | ERRORS opt_limit_clause + { Lex->sql_command = SQLCOM_SHOW_ERRORS;} + | PROFILES_SYM + { Lex->sql_command = SQLCOM_SHOW_PROFILES; } + | PROFILE_SYM opt_profile_defs opt_profile_args opt_limit_clause + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_PROFILE; + if (prepare_schema_table(thd, lex, NULL, SCH_PROFILES) != 0) + MYSQL_YYABORT; + } + | opt_var_type STATUS_SYM wild_and_where + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_STATUS; + lex->option_type= $1; + if (prepare_schema_table(thd, lex, 0, SCH_SESSION_STATUS)) + MYSQL_YYABORT; + } + | opt_full PROCESSLIST_SYM + { Lex->sql_command= SQLCOM_SHOW_PROCESSLIST;} + | opt_var_type VARIABLES wild_and_where + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_VARIABLES; + lex->option_type= $1; + if (prepare_schema_table(thd, lex, 0, SCH_SESSION_VARIABLES)) + MYSQL_YYABORT; + } + | charset wild_and_where + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_CHARSETS; + if (prepare_schema_table(thd, lex, 0, SCH_CHARSETS)) + MYSQL_YYABORT; + } + | COLLATION_SYM wild_and_where + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_COLLATIONS; + if (prepare_schema_table(thd, lex, 0, SCH_COLLATIONS)) + MYSQL_YYABORT; + } + | GRANTS + { + Lex->sql_command= SQLCOM_SHOW_GRANTS; + if (!(Lex->grant_user= (LEX_USER*)thd->alloc(sizeof(LEX_USER)))) + MYSQL_YYABORT; + Lex->grant_user->user= current_user_and_current_role; + } + | GRANTS FOR_SYM user_or_role clear_privileges + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_SHOW_GRANTS; + lex->grant_user=$3; + } + | CREATE DATABASE opt_if_not_exists ident + { + Lex->set_command(SQLCOM_SHOW_CREATE_DB, $3); + Lex->name= $4; + } + | CREATE TABLE_SYM table_ident + { + LEX *lex= Lex; + lex->sql_command = SQLCOM_SHOW_CREATE; + if (!lex->select_lex.add_table_to_list(thd, $3, NULL,0)) + MYSQL_YYABORT; + lex->create_info.storage_media= HA_SM_DEFAULT; + } + | CREATE VIEW_SYM table_ident + { + LEX *lex= Lex; + lex->sql_command = SQLCOM_SHOW_CREATE; + if (!lex->select_lex.add_table_to_list(thd, $3, NULL, 0)) + MYSQL_YYABORT; + lex->only_view= 1; + } + | MASTER_SYM STATUS_SYM + { + Lex->sql_command = SQLCOM_SHOW_MASTER_STAT; + } + | ALL SLAVES STATUS_SYM + { + Lex->sql_command = SQLCOM_SHOW_SLAVE_STAT; + Lex->verbose= 1; + } + | SLAVE STATUS_SYM + { + LEX *lex= thd->lex; + lex->mi.connection_name= null_lex_str; + lex->sql_command = SQLCOM_SHOW_SLAVE_STAT; + lex->verbose= 0; + } + | SLAVE connection_name STATUS_SYM + { + Lex->sql_command = SQLCOM_SHOW_SLAVE_STAT; + Lex->verbose= 0; + } + | CREATE PROCEDURE_SYM sp_name + { + LEX *lex= Lex; + + lex->sql_command = SQLCOM_SHOW_CREATE_PROC; + lex->spname= $3; + } + | CREATE FUNCTION_SYM sp_name + { + LEX *lex= Lex; + + lex->sql_command = SQLCOM_SHOW_CREATE_FUNC; + lex->spname= $3; + } + | CREATE TRIGGER_SYM sp_name + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_CREATE_TRIGGER; + lex->spname= $3; + } + | CREATE USER_SYM + { + Lex->sql_command= SQLCOM_SHOW_CREATE_USER; + if (!(Lex->grant_user= (LEX_USER*)thd->alloc(sizeof(LEX_USER)))) + MYSQL_YYABORT; + Lex->grant_user->user= current_user; + } + | CREATE USER_SYM user + { + Lex->sql_command= SQLCOM_SHOW_CREATE_USER; + Lex->grant_user= $3; + } + | PROCEDURE_SYM STATUS_SYM wild_and_where + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_STATUS_PROC; + if (prepare_schema_table(thd, lex, 0, SCH_PROCEDURES)) + MYSQL_YYABORT; + } + | FUNCTION_SYM STATUS_SYM wild_and_where + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_STATUS_FUNC; + if (prepare_schema_table(thd, lex, 0, SCH_PROCEDURES)) + MYSQL_YYABORT; + } + | PROCEDURE_SYM CODE_SYM sp_name + { + Lex->sql_command= SQLCOM_SHOW_PROC_CODE; + Lex->spname= $3; + } + | FUNCTION_SYM CODE_SYM sp_name + { + Lex->sql_command= SQLCOM_SHOW_FUNC_CODE; + Lex->spname= $3; + } + | CREATE EVENT_SYM sp_name + { + Lex->spname= $3; + Lex->sql_command = SQLCOM_SHOW_CREATE_EVENT; + } + | describe_command FOR_SYM expr + { + Lex->sql_command= SQLCOM_SHOW_EXPLAIN; + if (prepare_schema_table(thd, Lex, 0, SCH_EXPLAIN)) + MYSQL_YYABORT; + add_value_to_list(thd, $3); + } + | IDENT_sys remember_tok_start wild_and_where + { + LEX *lex= Lex; + bool in_plugin; + lex->sql_command= SQLCOM_SHOW_GENERIC; + ST_SCHEMA_TABLE *table= find_schema_table(thd, $1.str, &in_plugin); + if (!table || !table->old_format || !in_plugin) + { + my_parse_error(thd, ER_SYNTAX_ERROR, $2); + MYSQL_YYABORT; + } + if (lex->wild && table->idx_field1 < 0) + { + my_parse_error(thd, ER_SYNTAX_ERROR, $3); + MYSQL_YYABORT; + } + if (make_schema_select(thd, Lex->current_select, table)) + MYSQL_YYABORT; + } + ; + +show_engine_param: + STATUS_SYM + { Lex->sql_command= SQLCOM_SHOW_ENGINE_STATUS; } + | MUTEX_SYM + { Lex->sql_command= SQLCOM_SHOW_ENGINE_MUTEX; } + | LOGS_SYM + { Lex->sql_command= SQLCOM_SHOW_ENGINE_LOGS; } + ; + +master_or_binary: + MASTER_SYM + | BINARY + ; + +opt_storage: + /* empty */ + | STORAGE_SYM + ; + +opt_db: + /* empty */ { $$= 0; } + | from_or_in ident { $$= $2.str; } + ; + +opt_full: + /* empty */ { Lex->verbose=0; } + | FULL { Lex->verbose=1; } + ; + +from_or_in: + FROM + | IN_SYM + ; + +binlog_in: + /* empty */ { Lex->mi.log_file_name = 0; } + | IN_SYM TEXT_STRING_sys { Lex->mi.log_file_name = $2.str; } + ; + +binlog_from: + /* empty */ { Lex->mi.pos = 4; /* skip magic number */ } + | FROM ulonglong_num { Lex->mi.pos = $2; } + ; + +wild_and_where: + /* empty */ { $$= 0; } + | LIKE remember_tok_start TEXT_STRING_sys + { + Lex->wild= new (thd->mem_root) String($3.str, $3.length, + system_charset_info); + if (Lex->wild == NULL) + MYSQL_YYABORT; + $$= $2; + } + | WHERE remember_tok_start expr + { + Select->where= normalize_cond(thd, $3); + if ($3) + $3->top_level_item(); + $$= $2; + } + ; + +/* A Oracle compatible synonym for show */ +describe: + describe_command table_ident + { + LEX *lex= Lex; + mysql_init_select(lex); + lex->current_select->parsing_place= SELECT_LIST; + lex->sql_command= SQLCOM_SHOW_FIELDS; + lex->select_lex.db= 0; + lex->verbose= 0; + if (prepare_schema_table(thd, lex, $2, SCH_COLUMNS)) + MYSQL_YYABORT; + } + opt_describe_column + { + Select->parsing_place= NO_MATTER; + } + | describe_command opt_extended_describe + { Lex->describe|= DESCRIBE_NORMAL; } + explainable_command + { + LEX *lex=Lex; + lex->select_lex.options|= SELECT_DESCRIBE; + } + ; + +explainable_command: + select + | insert + | replace + | update + | delete + ; + +describe_command: + DESC + | DESCRIBE + ; + +analyze_stmt_command: + ANALYZE_SYM opt_format_json explainable_command + { + Lex->analyze_stmt= true; + } + ; + +opt_extended_describe: + EXTENDED_SYM { Lex->describe|= DESCRIBE_EXTENDED; } + | PARTITIONS_SYM { Lex->describe|= DESCRIBE_PARTITIONS; } + | opt_format_json {} + ; + +opt_format_json: + /* empty */ {} + | FORMAT_SYM '=' ident_or_text + { + if (!my_strcasecmp(system_charset_info, $3.str, "JSON")) + Lex->explain_json= true; + else if (!my_strcasecmp(system_charset_info, $3.str, "TRADITIONAL")) + DBUG_ASSERT(Lex->explain_json==false); + else + my_yyabort_error((ER_UNKNOWN_EXPLAIN_FORMAT, MYF(0), $3.str)); + } + ; + +opt_describe_column: + /* empty */ {} + | text_string { Lex->wild= $1; } + | ident + { + Lex->wild= new (thd->mem_root) String((const char*) $1.str, + $1.length, + system_charset_info); + if (Lex->wild == NULL) + MYSQL_YYABORT; + } + ; + + +/* flush things */ + +flush: + FLUSH_SYM opt_no_write_to_binlog + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_FLUSH; + lex->type= 0; + lex->no_write_to_binlog= $2; + } + flush_options + {} + ; + +flush_options: + table_or_tables + { + Lex->type|= REFRESH_TABLES; + /* + Set type of metadata and table locks for + FLUSH TABLES table_list [WITH READ LOCK]. + */ + YYPS->m_lock_type= TL_READ_NO_INSERT; + YYPS->m_mdl_type= MDL_SHARED_HIGH_PRIO; + } + opt_table_list opt_flush_lock + {} + | flush_options_list + ; + +opt_flush_lock: + /* empty */ {} + | flush_lock + { + TABLE_LIST *tables= Lex->query_tables; + for (; tables; tables= tables->next_global) + { + tables->mdl_request.set_type(MDL_SHARED_NO_WRITE); + tables->required_type= FRMTYPE_TABLE; /* Don't try to flush views. */ + tables->open_type= OT_BASE_ONLY; /* Ignore temporary tables. */ + } + } + ; + +flush_lock: + WITH READ_SYM LOCK_SYM optional_flush_tables_arguments + { Lex->type|= REFRESH_READ_LOCK | $4; } + | FOR_SYM + { + if (Lex->query_tables == NULL) // Table list can't be empty + { + my_parse_error(thd, ER_NO_TABLES_USED); + MYSQL_YYABORT; + } + Lex->type|= REFRESH_FOR_EXPORT; + } EXPORT_SYM {} + ; + +flush_options_list: + flush_options_list ',' flush_option + | flush_option + {} + ; + +flush_option: + ERROR_SYM LOGS_SYM + { Lex->type|= REFRESH_ERROR_LOG; } + | ENGINE_SYM LOGS_SYM + { Lex->type|= REFRESH_ENGINE_LOG; } + | GENERAL LOGS_SYM + { Lex->type|= REFRESH_GENERAL_LOG; } + | SLOW LOGS_SYM + { Lex->type|= REFRESH_SLOW_LOG; } + | BINARY LOGS_SYM + { Lex->type|= REFRESH_BINARY_LOG; } + | RELAY LOGS_SYM optional_connection_name + { + LEX *lex= Lex; + if (lex->type & REFRESH_RELAY_LOG) + my_yyabort_error((ER_WRONG_USAGE, MYF(0), "FLUSH", "RELAY LOGS")); + lex->type|= REFRESH_RELAY_LOG; + lex->relay_log_connection_name= lex->mi.connection_name; + } + | QUERY_SYM CACHE_SYM + { Lex->type|= REFRESH_QUERY_CACHE_FREE; } + | HOSTS_SYM + { Lex->type|= REFRESH_HOSTS; } + | PRIVILEGES + { Lex->type|= REFRESH_GRANT; } + | LOGS_SYM + { + Lex->type|= REFRESH_LOG; + Lex->relay_log_connection_name= empty_lex_str; + } + | STATUS_SYM + { Lex->type|= REFRESH_STATUS; } + | SLAVE optional_connection_name + { + LEX *lex= Lex; + if (lex->type & REFRESH_SLAVE) + my_yyabort_error((ER_WRONG_USAGE, MYF(0), "FLUSH","SLAVE")); + lex->type|= REFRESH_SLAVE; + lex->reset_slave_info.all= false; + } + | MASTER_SYM + { Lex->type|= REFRESH_MASTER; } + | DES_KEY_FILE + { Lex->type|= REFRESH_DES_KEY_FILE; } + | RESOURCES + { Lex->type|= REFRESH_USER_RESOURCES; } + | IDENT_sys remember_tok_start + { + Lex->type|= REFRESH_GENERIC; + ST_SCHEMA_TABLE *table= find_schema_table(thd, $1.str); + if (!table || !table->reset_table) + { + my_parse_error(thd, ER_SYNTAX_ERROR, $2); + MYSQL_YYABORT; + } + Lex->view_list.push_back((LEX_STRING*) + thd->memdup(&$1, sizeof(LEX_STRING)), + thd->mem_root); + } + ; + +opt_table_list: + /* empty */ {} + | table_list {} + ; + +optional_flush_tables_arguments: + /* empty */ {$$= 0;} + | AND_SYM DISABLE_SYM CHECKPOINT_SYM {$$= REFRESH_CHECKPOINT; } + +reset: + RESET_SYM + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_RESET; lex->type=0; + } + reset_options + {} + ; + +reset_options: + reset_options ',' reset_option + | reset_option + ; + +reset_option: + SLAVE { Lex->type|= REFRESH_SLAVE; } + optional_connection_name + slave_reset_options { } + | MASTER_SYM + { + Lex->type|= REFRESH_MASTER; + Lex->next_binlog_file_number= 0; + } + master_reset_options + | QUERY_SYM CACHE_SYM { Lex->type|= REFRESH_QUERY_CACHE;} + ; + +slave_reset_options: + /* empty */ { Lex->reset_slave_info.all= false; } + | ALL { Lex->reset_slave_info.all= true; } + ; + +master_reset_options: + /* empty */ {} + | TO_SYM ulong_num + { + Lex->next_binlog_file_number = $2; + } + ; + +purge: + PURGE + { + LEX *lex=Lex; + lex->type=0; + lex->sql_command = SQLCOM_PURGE; + } + purge_options + {} + ; + +purge_options: + master_or_binary LOGS_SYM purge_option + ; + +purge_option: + TO_SYM TEXT_STRING_sys + { + Lex->to_log = $2.str; + } + | BEFORE_SYM expr + { + LEX *lex= Lex; + lex->value_list.empty(); + lex->value_list.push_front($2, thd->mem_root); + lex->sql_command= SQLCOM_PURGE_BEFORE; + } + ; + +/* kill threads */ + +kill: + KILL_SYM + { + LEX *lex=Lex; + lex->value_list.empty(); + lex->users_list.empty(); + lex->sql_command= SQLCOM_KILL; + lex->kill_type= KILL_TYPE_ID; + } + kill_type kill_option kill_expr + { + Lex->kill_signal= (killed_state) ($3 | $4); + } + ; + +kill_type: + /* Empty */ { $$= (int) KILL_HARD_BIT; } + | HARD_SYM { $$= (int) KILL_HARD_BIT; } + | SOFT_SYM { $$= 0; } + +kill_option: + /* empty */ { $$= (int) KILL_CONNECTION; } + | CONNECTION_SYM { $$= (int) KILL_CONNECTION; } + | QUERY_SYM { $$= (int) KILL_QUERY; } + | QUERY_SYM ID_SYM + { + $$= (int) KILL_QUERY; + Lex->kill_type= KILL_TYPE_QUERY; + } + ; + +kill_expr: + expr + { + Lex->value_list.push_front($$, thd->mem_root); + } + | USER_SYM user + { + Lex->users_list.push_back($2, thd->mem_root); + Lex->kill_type= KILL_TYPE_USER; + } + ; + + +shutdown: + SHUTDOWN { Lex->sql_command= SQLCOM_SHUTDOWN; } + ; + +/* change database */ + +use: + USE_SYM ident + { + LEX *lex=Lex; + lex->sql_command=SQLCOM_CHANGE_DB; + lex->select_lex.db= $2.str; + } + ; + +/* import, export of files */ + +load: + LOAD data_or_xml + { + LEX *lex= thd->lex; + + if (lex->sphead) + { + my_error(ER_SP_BADSTATEMENT, MYF(0), + $2 == FILETYPE_CSV ? "LOAD DATA" : "LOAD XML"); + MYSQL_YYABORT; + } + } + load_data_lock opt_local INFILE TEXT_STRING_filesystem + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_LOAD; + lex->local_file= $5; + lex->duplicates= DUP_ERROR; + lex->ignore= 0; + if (!(lex->exchange= new (thd->mem_root) sql_exchange($7.str, 0, $2))) + MYSQL_YYABORT; + } + opt_duplicate INTO TABLE_SYM table_ident opt_use_partition + { + LEX *lex=Lex; + if (!Select->add_table_to_list(thd, $12, NULL, TL_OPTION_UPDATING, + $4, MDL_SHARED_WRITE, NULL, $13)) + MYSQL_YYABORT; + lex->field_list.empty(); + lex->update_list.empty(); + lex->value_list.empty(); + } + opt_load_data_charset + { Lex->exchange->cs= $15; } + opt_xml_rows_identified_by + opt_field_term opt_line_term opt_ignore_lines opt_field_or_var_spec + opt_load_data_set_spec + {} + ; + +data_or_xml: + DATA_SYM { $$= FILETYPE_CSV; } + | XML_SYM { $$= FILETYPE_XML; } + ; + +opt_local: + /* empty */ { $$=0;} + | LOCAL_SYM { $$=1;} + ; + +load_data_lock: + /* empty */ { $$= TL_WRITE_DEFAULT; } + | CONCURRENT + { + /* + Ignore this option in SP to avoid problem with query cache and + triggers with non default priority locks + */ + $$= (Lex->sphead ? TL_WRITE_DEFAULT : TL_WRITE_CONCURRENT_INSERT); + } + | LOW_PRIORITY { $$= TL_WRITE_LOW_PRIORITY; } + ; + +opt_duplicate: + /* empty */ { Lex->duplicates=DUP_ERROR; } + | REPLACE { Lex->duplicates=DUP_REPLACE; } + | IGNORE_SYM { Lex->ignore= 1; } + ; + +opt_field_term: + /* empty */ + | COLUMNS field_term_list + ; + +field_term_list: + field_term_list field_term + | field_term + ; + +field_term: + TERMINATED BY text_string + { + DBUG_ASSERT(Lex->exchange != 0); + Lex->exchange->field_term= $3; + } + | OPTIONALLY ENCLOSED BY text_string + { + LEX *lex= Lex; + DBUG_ASSERT(lex->exchange != 0); + lex->exchange->enclosed= $4; + lex->exchange->opt_enclosed= 1; + } + | ENCLOSED BY text_string + { + DBUG_ASSERT(Lex->exchange != 0); + Lex->exchange->enclosed= $3; + } + | ESCAPED BY text_string + { + DBUG_ASSERT(Lex->exchange != 0); + Lex->exchange->escaped= $3; + } + ; + +opt_line_term: + /* empty */ + | LINES line_term_list + ; + +line_term_list: + line_term_list line_term + | line_term + ; + +line_term: + TERMINATED BY text_string + { + DBUG_ASSERT(Lex->exchange != 0); + Lex->exchange->line_term= $3; + } + | STARTING BY text_string + { + DBUG_ASSERT(Lex->exchange != 0); + Lex->exchange->line_start= $3; + } + ; + +opt_xml_rows_identified_by: + /* empty */ { } + | ROWS_SYM IDENTIFIED_SYM BY text_string + { Lex->exchange->line_term = $4; }; + +opt_ignore_lines: + /* empty */ + | IGNORE_SYM NUM lines_or_rows + { + DBUG_ASSERT(Lex->exchange != 0); + Lex->exchange->skip_lines= atol($2.str); + } + ; + +lines_or_rows: + LINES { } + | ROWS_SYM { } + ; + +opt_field_or_var_spec: + /* empty */ {} + | '(' fields_or_vars ')' {} + | '(' ')' {} + ; + +fields_or_vars: + fields_or_vars ',' field_or_var + { Lex->field_list.push_back($3, thd->mem_root); } + | field_or_var + { Lex->field_list.push_back($1, thd->mem_root); } + ; + +field_or_var: + simple_ident_nospvar {$$= $1;} + | '@' ident_or_text + { + $$= new (thd->mem_root) Item_user_var_as_out_param(thd, $2); + if ($$ == NULL) + MYSQL_YYABORT; + } + ; + +opt_load_data_set_spec: + /* empty */ {} + | SET load_data_set_list {} + ; + +load_data_set_list: + load_data_set_list ',' load_data_set_elem + | load_data_set_elem + ; + +load_data_set_elem: + simple_ident_nospvar equal remember_name expr_or_default remember_end + { + LEX *lex= Lex; + if (lex->update_list.push_back($1, thd->mem_root) || + lex->value_list.push_back($4, thd->mem_root)) + MYSQL_YYABORT; + $4->set_name_no_truncate(thd, $3, (uint) ($5 - $3), thd->charset()); + } + ; + +/* Common definitions */ + +text_literal: + TEXT_STRING + { + LEX_STRING tmp; + CHARSET_INFO *cs_con= thd->variables.collation_connection; + CHARSET_INFO *cs_cli= thd->variables.character_set_client; + uint repertoire= $1.repertoire(cs_cli); + if (thd->charset_is_collation_connection || + (repertoire == MY_REPERTOIRE_ASCII && + my_charset_is_ascii_based(cs_con))) + tmp= $1; + else + { + if (thd->convert_string(&tmp, cs_con, $1.str, $1.length, cs_cli)) + MYSQL_YYABORT; + } + $$= new (thd->mem_root) Item_string(thd, tmp.str, tmp.length, + cs_con, + DERIVATION_COERCIBLE, + repertoire); + if ($$ == NULL) + MYSQL_YYABORT; + } + | NCHAR_STRING + { + DBUG_ASSERT(my_charset_is_ascii_based(national_charset_info)); + $$= new (thd->mem_root) Item_string(thd, $1.str, $1.length, + national_charset_info, + DERIVATION_COERCIBLE, + $1.repertoire()); + if ($$ == NULL) + MYSQL_YYABORT; + } + | UNDERSCORE_CHARSET TEXT_STRING + { + $$= new (thd->mem_root) Item_string_with_introducer(thd, $2.str, + $2.length, $1); + if ($$ == NULL) + MYSQL_YYABORT; + } + | text_literal TEXT_STRING_literal + { + Item_string* item= (Item_string*) $1; + item->append($2.str, $2.length); + if (!(item->collation.repertoire & MY_REPERTOIRE_EXTENDED)) + { + /* + If the string has been pure ASCII so far, + check the new part. + */ + CHARSET_INFO *cs= thd->variables.collation_connection; + item->collation.repertoire|= my_string_repertoire(cs, + $2.str, + $2.length); + } + } + ; + +text_string: + TEXT_STRING_literal + { + $$= new (thd->mem_root) String($1.str, + $1.length, + thd->variables.collation_connection); + if ($$ == NULL) + MYSQL_YYABORT; + } + | hex_or_bin_String { $$= $1; } + ; + + +hex_or_bin_String: + HEX_NUM + { + Item *tmp= new (thd->mem_root) Item_hex_hybrid(thd, $1.str, + $1.length); + if (tmp == NULL) + MYSQL_YYABORT; + /* + it is OK only emulate fix_fields, because we need only + value of constant + */ + tmp->quick_fix_field(); + $$= tmp->val_str((String*) 0); + } + | HEX_STRING + { + Item *tmp= new (thd->mem_root) Item_hex_string(thd, $1.str, + $1.length); + if (tmp == NULL) + MYSQL_YYABORT; + tmp->quick_fix_field(); + $$= tmp->val_str((String*) 0); + } + | BIN_NUM + { + Item *tmp= new (thd->mem_root) Item_bin_string(thd, $1.str, + $1.length); + if (tmp == NULL) + MYSQL_YYABORT; + /* + it is OK only emulate fix_fields, because we need only + value of constant + */ + tmp->quick_fix_field(); + $$= tmp->val_str((String*) 0); + } + ; + +param_marker: + PARAM_MARKER + { + LEX *lex= thd->lex; + Lex_input_stream *lip= YYLIP; + Item_param *item; + if (! lex->parsing_options.allows_variable) + my_yyabort_error((ER_VIEW_SELECT_VARIABLE, MYF(0))); + const char *query_start= lex->sphead ? lex->sphead->m_tmp_query + : thd->query(); + item= new (thd->mem_root) Item_param(thd, lip->get_tok_start() - + query_start); + if (!($$= item) || lex->param_list.push_back(item, thd->mem_root)) + my_yyabort_error((ER_OUT_OF_RESOURCES, MYF(0))); + } + ; + +signed_literal: + '+' NUM_literal { $$ = $2; } + | '-' NUM_literal + { + $2->max_length++; + $$= $2->neg(thd); + } + ; + +literal: + text_literal { $$ = $1; } + | NUM_literal { $$ = $1; } + | temporal_literal { $$= $1; } + | NULL_SYM + { + /* + For the digest computation, in this context only, + NULL is considered a literal, hence reduced to '?' + REDUCE: + TOK_GENERIC_VALUE := NULL_SYM + */ + YYLIP->reduce_digest_token(TOK_GENERIC_VALUE, NULL_SYM); + $$= new (thd->mem_root) Item_null(thd); + if ($$ == NULL) + MYSQL_YYABORT; + YYLIP->next_state= MY_LEX_OPERATOR_OR_IDENT; + } + | FALSE_SYM + { + $$= new (thd->mem_root) Item_bool(thd, (char*) "FALSE",0); + if ($$ == NULL) + MYSQL_YYABORT; + } + | TRUE_SYM + { + $$= new (thd->mem_root) Item_bool(thd, (char*) "TRUE",1); + if ($$ == NULL) + MYSQL_YYABORT; + } + | HEX_NUM + { + $$= new (thd->mem_root) Item_hex_hybrid(thd, $1.str, $1.length); + if ($$ == NULL) + MYSQL_YYABORT; + } + | HEX_STRING + { + $$= new (thd->mem_root) Item_hex_string(thd, $1.str, $1.length); + if ($$ == NULL) + MYSQL_YYABORT; + } + | BIN_NUM + { + $$= new (thd->mem_root) Item_bin_string(thd, $1.str, $1.length); + if ($$ == NULL) + MYSQL_YYABORT; + } + | UNDERSCORE_CHARSET hex_or_bin_String + { + Item_string_with_introducer *item_str; + /* + Pass NULL as name. Name will be set in the "select_item" rule and + will include the introducer and the original hex/bin notation. + */ + item_str= new (thd->mem_root) + Item_string_with_introducer(thd, NULL, $2->ptr(), $2->length(), + $1); + if (!item_str || !item_str->check_well_formed_result(true)) + MYSQL_YYABORT; + + $$= item_str; + } + ; + +NUM_literal: + NUM + { + int error; + $$= new (thd->mem_root) + Item_int(thd, $1.str, + (longlong) my_strtoll10($1.str, NULL, &error), + $1.length); + if ($$ == NULL) + MYSQL_YYABORT; + } + | LONG_NUM + { + int error; + $$= new (thd->mem_root) + Item_int(thd, $1.str, + (longlong) my_strtoll10($1.str, NULL, &error), + $1.length); + if ($$ == NULL) + MYSQL_YYABORT; + } + | ULONGLONG_NUM + { + $$= new (thd->mem_root) Item_uint(thd, $1.str, $1.length); + if ($$ == NULL) + MYSQL_YYABORT; + } + | DECIMAL_NUM + { + $$= new (thd->mem_root) Item_decimal(thd, $1.str, $1.length, + thd->charset()); + if (($$ == NULL) || (thd->is_error())) + { + MYSQL_YYABORT; + } + } + | FLOAT_NUM + { + $$= new (thd->mem_root) Item_float(thd, $1.str, $1.length); + if (($$ == NULL) || (thd->is_error())) + { + MYSQL_YYABORT; + } + } + ; + + +temporal_literal: + DATE_SYM TEXT_STRING + { + if (!($$= create_temporal_literal(thd, $2.str, $2.length, YYCSCL, + MYSQL_TYPE_DATE, true))) + MYSQL_YYABORT; + } + | TIME_SYM TEXT_STRING + { + if (!($$= create_temporal_literal(thd, $2.str, $2.length, YYCSCL, + MYSQL_TYPE_TIME, true))) + MYSQL_YYABORT; + } + | TIMESTAMP TEXT_STRING + { + if (!($$= create_temporal_literal(thd, $2.str, $2.length, YYCSCL, + MYSQL_TYPE_DATETIME, true))) + MYSQL_YYABORT; + } + ; + + +opt_with_clause: + /*empty */ { $$= 0; } + | with_clause + { + $$= $1; + } + ; + + +with_clause: + WITH opt_recursive + { + With_clause *with_clause= + new With_clause($2, Lex->curr_with_clause); + if (with_clause == NULL) + MYSQL_YYABORT; + Lex->derived_tables|= DERIVED_WITH; + Lex->curr_with_clause= with_clause; + with_clause->add_to_list(Lex->with_clauses_list_last_next); + } + with_list + { + $$= Lex->curr_with_clause; + Lex->curr_with_clause= Lex->curr_with_clause->pop(); + } + ; + + +opt_recursive: + /*empty*/ { $$= 0; } + | RECURSIVE_SYM { $$= 1; } + ; + + +with_list: + with_list_element + | with_list ',' with_list_element + ; + + +with_list_element: + query_name + opt_with_column_list + { + $2= new List<LEX_STRING> (Lex->with_column_list); + if ($2 == NULL) + MYSQL_YYABORT; + Lex->with_column_list.empty(); + } + AS '(' remember_name subselect remember_end ')' + { + With_element *elem= new With_element($1, *$2, $7->master_unit()); + if (elem == NULL || Lex->curr_with_clause->add_with_element(elem)) + MYSQL_YYABORT; + if (elem->set_unparsed_spec(thd, $6+1, $8)) + MYSQL_YYABORT; + } + ; + + +opt_with_column_list: + /* empty */ + { $$= NULL; } + | '(' with_column_list ')' + { $$= NULL; } + ; + + +with_column_list: + ident + { + Lex->with_column_list.push_back((LEX_STRING*) + thd->memdup(&$1, sizeof(LEX_STRING))); + } + | with_column_list ',' ident + { + Lex->with_column_list.push_back((LEX_STRING*) + thd->memdup(&$3, sizeof(LEX_STRING))); + } + ; + + +query_name: + ident + { + $$= (LEX_STRING *) thd->memdup(&$1, sizeof(LEX_STRING)); + if ($$ == NULL) + MYSQL_YYABORT; + } + ; + + + +/********************************************************************** +** Creating different items. +**********************************************************************/ + +insert_ident: + simple_ident_nospvar { $$=$1; } + | table_wild { $$=$1; } + ; + +table_wild: + ident '.' '*' + { + SELECT_LEX *sel= Select; + $$= new (thd->mem_root) Item_field(thd, Lex->current_context(), + NullS, $1.str, "*"); + if ($$ == NULL) + MYSQL_YYABORT; + sel->with_wild++; + } + | ident '.' ident '.' '*' + { + SELECT_LEX *sel= Select; + const char* schema= thd->client_capabilities & CLIENT_NO_SCHEMA ? + NullS : $1.str; + $$= new (thd->mem_root) Item_field(thd, Lex->current_context(), + schema, + $3.str,"*"); + if ($$ == NULL) + MYSQL_YYABORT; + sel->with_wild++; + } + ; + +order_ident: + expr { $$=$1; } + ; + +simple_ident: + ident + { + LEX *lex= thd->lex; + Lex_input_stream *lip= YYLIP; + sp_variable *spv; + sp_pcontext *spc = lex->spcont; + if (spc && (spv = spc->find_variable($1, false))) + { + /* We're compiling a stored procedure and found a variable */ + if (! lex->parsing_options.allows_variable) + my_yyabort_error((ER_VIEW_SELECT_VARIABLE, MYF(0))); + + Item_splocal *splocal; + splocal= new (thd->mem_root) + Item_splocal(thd, $1, spv->offset, spv->sql_type(), + lip->get_tok_start_prev() - lex->sphead->m_tmp_query, + lip->get_tok_end() - lip->get_tok_start_prev()); + if (splocal == NULL) + MYSQL_YYABORT; +#ifndef DBUG_OFF + splocal->m_sp= lex->sphead; +#endif + $$= splocal; + lex->safe_to_cache_query=0; + } + else + { + SELECT_LEX *sel=Select; + if ((sel->parsing_place != IN_HAVING) || + (sel->get_in_sum_expr() > 0)) + { + $$= new (thd->mem_root) Item_field(thd, Lex->current_context(), + NullS, NullS, $1.str); + } + else + { + $$= new (thd->mem_root) Item_ref(thd, Lex->current_context(), + NullS, NullS, $1.str); + } + if ($$ == NULL) + MYSQL_YYABORT; + } + } + | simple_ident_q { $$= $1; } + ; + +simple_ident_nospvar: + ident + { + SELECT_LEX *sel=Select; + if ((sel->parsing_place != IN_HAVING) || + (sel->get_in_sum_expr() > 0)) + { + $$= new (thd->mem_root) Item_field(thd, Lex->current_context(), + NullS, NullS, $1.str); + } + else + { + $$= new (thd->mem_root) Item_ref(thd, Lex->current_context(), + NullS, NullS, $1.str); + } + if ($$ == NULL) + MYSQL_YYABORT; + } + | simple_ident_q { $$= $1; } + ; + +simple_ident_q: + ident '.' ident + { + LEX *lex= thd->lex; + + /* + 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"))) + { + Item_trigger_field *trg_fld; + bool new_row= ($1.str[0]=='N' || $1.str[0]=='n'); + + if (lex->trg_chistics.event == TRG_EVENT_INSERT && + !new_row) + my_yyabort_error((ER_TRG_NO_SUCH_ROW_IN_TRG, MYF(0), "OLD", "on INSERT")); + + if (lex->trg_chistics.event == TRG_EVENT_DELETE && + new_row) + my_yyabort_error((ER_TRG_NO_SUCH_ROW_IN_TRG, MYF(0), "NEW", "on DELETE")); + + DBUG_ASSERT(!new_row || + (lex->trg_chistics.event == TRG_EVENT_INSERT || + lex->trg_chistics.event == TRG_EVENT_UPDATE)); + const bool tmp_read_only= + !(new_row && lex->trg_chistics.action_time == TRG_ACTION_BEFORE); + trg_fld= new (thd->mem_root) + Item_trigger_field(thd, Lex->current_context(), + new_row ? + Item_trigger_field::NEW_ROW: + Item_trigger_field::OLD_ROW, + $3.str, + SELECT_ACL, + tmp_read_only); + if (trg_fld == NULL) + MYSQL_YYABORT; + + /* + Let us add this item to list of all Item_trigger_field objects + in trigger. + */ + lex->trg_table_fields.link_in_list(trg_fld, + &trg_fld->next_trg_field); + + $$= trg_fld; + } + else + { + SELECT_LEX *sel= lex->current_select; + if (sel->no_table_names_allowed) + { + my_error(ER_TABLENAME_NOT_ALLOWED_HERE, + MYF(0), $1.str, thd->where); + } + if ((sel->parsing_place != IN_HAVING) || + (sel->get_in_sum_expr() > 0)) + { + $$= new (thd->mem_root) Item_field(thd, Lex->current_context(), + NullS, $1.str, $3.str); + } + else + { + $$= new (thd->mem_root) Item_ref(thd, Lex->current_context(), + NullS, $1.str, $3.str); + } + if ($$ == NULL) + MYSQL_YYABORT; + } + } + | '.' ident '.' ident + { + LEX *lex= thd->lex; + SELECT_LEX *sel= lex->current_select; + if (sel->no_table_names_allowed) + { + my_error(ER_TABLENAME_NOT_ALLOWED_HERE, + MYF(0), $2.str, thd->where); + } + if ((sel->parsing_place != IN_HAVING) || + (sel->get_in_sum_expr() > 0)) + { + $$= new (thd->mem_root) Item_field(thd, Lex->current_context(), + NullS, $2.str, $4.str); + + } + else + { + $$= new (thd->mem_root) Item_ref(thd, Lex->current_context(), + NullS, $2.str, $4.str); + } + if ($$ == NULL) + MYSQL_YYABORT; + } + | ident '.' ident '.' ident + { + LEX *lex= thd->lex; + SELECT_LEX *sel= lex->current_select; + const char* schema= (thd->client_capabilities & CLIENT_NO_SCHEMA ? + NullS : $1.str); + if (sel->no_table_names_allowed) + { + my_error(ER_TABLENAME_NOT_ALLOWED_HERE, + MYF(0), $3.str, thd->where); + } + if ((sel->parsing_place != IN_HAVING) || + (sel->get_in_sum_expr() > 0)) + { + $$= new (thd->mem_root) Item_field(thd, Lex->current_context(), + schema, + $3.str, $5.str); + } + else + { + $$= new (thd->mem_root) Item_ref(thd, Lex->current_context(), + schema, + $3.str, $5.str); + } + if ($$ == NULL) + MYSQL_YYABORT; + } + ; + +field_ident: + ident { $$=$1;} + | ident '.' ident '.' ident + { + TABLE_LIST *table= Select->table_list.first; + if (my_strcasecmp(table_alias_charset, $1.str, table->db)) + my_yyabort_error((ER_WRONG_DB_NAME, MYF(0), $1.str)); + if (my_strcasecmp(table_alias_charset, $3.str, + table->table_name)) + my_yyabort_error((ER_WRONG_TABLE_NAME, MYF(0), $3.str)); + $$=$5; + } + | ident '.' ident + { + TABLE_LIST *table= Select->table_list.first; + if (my_strcasecmp(table_alias_charset, $1.str, table->alias)) + my_yyabort_error((ER_WRONG_TABLE_NAME, MYF(0), $1.str)); + $$=$3; + } + | '.' ident { $$=$2;} /* For Delphi */ + ; + +table_ident: + ident + { + $$= new (thd->mem_root) Table_ident($1); + if ($$ == NULL) + MYSQL_YYABORT; + } + | ident '.' ident + { + $$= new (thd->mem_root) Table_ident(thd, $1, $3, 0); + if ($$ == NULL) + MYSQL_YYABORT; + } + | '.' ident + { + /* For Delphi */ + $$= new (thd->mem_root) Table_ident($2); + if ($$ == NULL) + MYSQL_YYABORT; + } + ; + +table_ident_opt_wild: + ident opt_wild + { + $$= new (thd->mem_root) Table_ident($1); + if ($$ == NULL) + MYSQL_YYABORT; + } + | ident '.' ident opt_wild + { + $$= new (thd->mem_root) Table_ident(thd, $1, $3, 0); + if ($$ == NULL) + MYSQL_YYABORT; + } + ; + +table_ident_nodb: + ident + { + LEX_STRING db={(char*) any_db,3}; + $$= new (thd->mem_root) Table_ident(thd, db, $1, 0); + if ($$ == NULL) + MYSQL_YYABORT; + } + ; + +IDENT_sys: + IDENT { $$= $1; } + | IDENT_QUOTED + { + if (thd->charset_is_system_charset) + { + CHARSET_INFO *cs= system_charset_info; + uint wlen= Well_formed_prefix(cs, $1.str, $1.length).length(); + if (wlen < $1.length) + { + ErrConvString err($1.str, $1.length, &my_charset_bin); + my_error(ER_INVALID_CHARACTER_STRING, MYF(0), + cs->csname, err.ptr()); + MYSQL_YYABORT; + } + $$= $1; + } + else + { + if (thd->convert_with_error(system_charset_info, &$$, + thd->charset(), $1.str, $1.length)) + MYSQL_YYABORT; + } + } + ; + +TEXT_STRING_sys: + TEXT_STRING + { + if (thd->charset_is_system_charset) + $$= $1; + else + { + if (thd->convert_string(&$$, system_charset_info, + $1.str, $1.length, thd->charset())) + MYSQL_YYABORT; + } + } + ; + +TEXT_STRING_literal: + TEXT_STRING + { + if (thd->charset_is_collation_connection) + $$= $1; + else + { + if (thd->convert_string(&$$, thd->variables.collation_connection, + $1.str, $1.length, thd->charset())) + MYSQL_YYABORT; + } + } + ; + +TEXT_STRING_filesystem: + TEXT_STRING + { + if (thd->charset_is_character_set_filesystem) + $$= $1; + else + { + if (thd->convert_string(&$$, + thd->variables.character_set_filesystem, + $1.str, $1.length, thd->charset())) + MYSQL_YYABORT; + } + } + ; + +ident: + IDENT_sys { $$=$1; } + | keyword + { + $$.str= thd->strmake($1.str, $1.length); + if ($$.str == NULL) + MYSQL_YYABORT; + $$.length= $1.length; + } + ; + +label_ident: + IDENT_sys { $$=$1; } + | keyword_sp + { + $$.str= thd->strmake($1.str, $1.length); + if ($$.str == NULL) + MYSQL_YYABORT; + $$.length= $1.length; + } + ; + +ident_or_text: + ident { $$=$1;} + | TEXT_STRING_sys { $$=$1;} + | LEX_HOSTNAME { $$=$1;} + ; + +user_maybe_role: + ident_or_text + { + if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user)))) + MYSQL_YYABORT; + $$->user = $1; + $$->host= null_lex_str; // User or Role, see get_current_user() + $$->reset_auth(); + + if (check_string_char_length(&$$->user, ER_USERNAME, + username_char_length, + system_charset_info, 0)) + MYSQL_YYABORT; + } + | ident_or_text '@' ident_or_text + { + if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user)))) + MYSQL_YYABORT; + $$->user = $1; $$->host=$3; + $$->reset_auth(); + + if (check_string_char_length(&$$->user, ER_USERNAME, + username_char_length, + system_charset_info, 0) || + check_host_name(&$$->host)) + MYSQL_YYABORT; + if ($$->host.str[0]) + { + /* + Convert hostname part of username to lowercase. + It's OK to use in-place lowercase as long as + the character set is utf8. + */ + my_casedn_str(system_charset_info, $$->host.str); + } + else + { + /* + fix historical undocumented convention that empty host is the + same as '%' + */ + $$->host= host_not_specified; + } + } + | CURRENT_USER optional_braces + { + if (!($$=(LEX_USER*)thd->calloc(sizeof(LEX_USER)))) + MYSQL_YYABORT; + $$->user= current_user; + $$->plugin= empty_lex_str; + $$->auth= empty_lex_str; + } + ; + +user_or_role: user_maybe_role | current_role; + +user: user_maybe_role + { + if ($1->user.str != current_user.str && $1->host.str == 0) + $1->host= host_not_specified; + $$= $1; + } + ; + +/* Keyword that we allow for identifiers (except SP labels) */ +keyword: + keyword_sp {} + | ASCII_SYM {} + | BACKUP_SYM {} + | BEGIN_SYM {} + | BINLOG_SYM {} + | BYTE_SYM {} + | CACHE_SYM {} + | CHARSET {} + | CHECKSUM_SYM {} + | CHECKPOINT_SYM {} + | CLOSE_SYM {} + | COLUMN_ADD_SYM {} + | COLUMN_CHECK_SYM {} + | COLUMN_CREATE_SYM {} + | COLUMN_DELETE_SYM {} + | COLUMN_GET_SYM {} + | COMMENT_SYM {} + | COMMIT_SYM {} + | CONTAINS_SYM {} + | DEALLOCATE_SYM {} + | DO_SYM {} + | END {} + | EXAMINED_SYM {} + | EXCLUDE_SYM {} + | EXECUTE_SYM {} + | FLUSH_SYM {} + | FOLLOWS_SYM {} + | FOLLOWING_SYM {} + | FORMAT_SYM {} + | GET_SYM {} + | HANDLER_SYM {} + | HELP_SYM {} + | HOST_SYM {} + | INSTALL_SYM {} + | LANGUAGE_SYM {} + | NO_SYM {} + | OPEN_SYM {} + | OPTION {} + | OPTIONS_SYM {} + | OTHERS_SYM {} + | OWNER_SYM {} + | PARSER_SYM {} + | PORT_SYM {} + | PRECEDES_SYM {} + | PRECEDING_SYM {} + | PREPARE_SYM {} + | REMOVE_SYM {} + | REPAIR {} + | RESET_SYM {} + | RESTORE_SYM {} + | ROLLBACK_SYM {} + | SAVEPOINT_SYM {} + | SECURITY_SYM {} + | SERVER_SYM {} + | SHUTDOWN {} + | SIGNED_SYM {} + | SOCKET_SYM {} + | SLAVE {} + | SLAVES {} + | SONAME_SYM {} + | START_SYM {} + | STOP_SYM {} + | STORED_SYM {} + | TIES_SYM {} + | TRUNCATE_SYM {} + | UNICODE_SYM {} + | UNINSTALL_SYM {} + | UNBOUNDED_SYM {} + | WRAPPER_SYM {} + | XA_SYM {} + | UPGRADE_SYM {} + ; + +/* + * Keywords that we allow for labels in SPs. + * Anything that's the beginning of a statement or characteristics + * must be in keyword above, otherwise we get (harmful) shift/reduce + * conflicts. + */ +keyword_sp: + ACTION {} + | ADDDATE_SYM {} + | ADMIN_SYM {} + | AFTER_SYM {} + | AGAINST {} + | AGGREGATE_SYM {} + | ALGORITHM_SYM {} + | ALWAYS_SYM {} + | ANY_SYM {} + | AT_SYM {} + | ATOMIC_SYM {} + | AUTHORS_SYM {} + | AUTO_INC {} + | AUTOEXTEND_SIZE_SYM {} + | AUTO_SYM {} + | AVG_ROW_LENGTH {} + | AVG_SYM {} + | BIT_SYM {} + | BLOCK_SYM {} + | BOOL_SYM {} + | BOOLEAN_SYM {} + | BTREE_SYM {} + | CASCADED {} + | CATALOG_NAME_SYM {} + | CHAIN_SYM {} + | CHANGED {} + | CIPHER_SYM {} + | CLIENT_SYM {} + | CLASS_ORIGIN_SYM {} + | COALESCE {} + | CODE_SYM {} + | COLLATION_SYM {} + | COLUMN_NAME_SYM {} + | COLUMNS {} + | COMMITTED_SYM {} + | COMPACT_SYM {} + | COMPLETION_SYM {} + | COMPRESSED_SYM {} + | CONCURRENT {} + | CONNECTION_SYM {} + | CONSISTENT_SYM {} + | CONSTRAINT_CATALOG_SYM {} + | CONSTRAINT_SCHEMA_SYM {} + | CONSTRAINT_NAME_SYM {} + | CONTEXT_SYM {} + | CONTRIBUTORS_SYM {} + | CURRENT_POS_SYM {} + | CPU_SYM {} + | CUBE_SYM {} + /* + Although a reserved keyword in SQL:2003 (and :2008), + not reserved in MySQL per WL#2111 specification. + */ + | CURRENT_SYM {} + | CURSOR_NAME_SYM {} + | DATA_SYM {} + | DATAFILE_SYM {} + | DATETIME {} + | DATE_SYM {} + | DAY_SYM {} + | DEFINER_SYM {} + | DELAY_KEY_WRITE_SYM {} + | DES_KEY_FILE {} + | DIAGNOSTICS_SYM {} + | DIRECTORY_SYM {} + | DISABLE_SYM {} + | DISCARD {} + | DISK_SYM {} + | DUMPFILE {} + | DUPLICATE_SYM {} + | DYNAMIC_SYM {} + | ENDS_SYM {} + | ENUM {} + | ENGINE_SYM {} + | ENGINES_SYM {} + | ERROR_SYM {} + | ERRORS {} + | ESCAPE_SYM {} + | EVENT_SYM {} + | EVENTS_SYM {} + | EVERY_SYM {} + | EXCHANGE_SYM {} + | EXPANSION_SYM {} + | EXPORT_SYM {} + | EXTENDED_SYM {} + | EXTENT_SIZE_SYM {} + | FAULTS_SYM {} + | FAST_SYM {} + | FOUND_SYM {} + | ENABLE_SYM {} + | FULL {} + | FILE_SYM {} + | FIRST_SYM {} + | FIXED_SYM {} + | GENERAL {} + | GENERATED_SYM {} + | GEOMETRY_SYM {} + | GEOMETRYCOLLECTION {} + | GET_FORMAT {} + | GRANTS {} + | GLOBAL_SYM {} + | HASH_SYM {} + | HARD_SYM {} + | HOSTS_SYM {} + | HOUR_SYM {} + | ID_SYM {} + | IDENTIFIED_SYM {} + | IGNORE_SERVER_IDS_SYM {} + | IMMEDIATE_SYM {} /* SQL-2003-R */ + | INVOKER_SYM {} + | IMPORT {} + | INDEXES {} + | INITIAL_SIZE_SYM {} + | IO_SYM {} + | IPC_SYM {} + | ISOLATION {} + | ISSUER_SYM {} + | JSON_SYM {} + | INSERT_METHOD {} + | KEY_BLOCK_SIZE {} + | LAST_VALUE {} + | LAST_SYM {} + | LEAVES {} + | LESS_SYM {} + | LEVEL_SYM {} + | LINESTRING {} + | LIST_SYM {} + | LOCAL_SYM {} + | LOCKS_SYM {} + | LOGFILE_SYM {} + | LOGS_SYM {} + | MAX_ROWS {} + | MASTER_SYM {} + | MASTER_HEARTBEAT_PERIOD_SYM {} + | MASTER_GTID_POS_SYM {} + | MASTER_HOST_SYM {} + | MASTER_PORT_SYM {} + | MASTER_LOG_FILE_SYM {} + | MASTER_LOG_POS_SYM {} + | MASTER_USER_SYM {} + | MASTER_USE_GTID_SYM {} + | MASTER_PASSWORD_SYM {} + | MASTER_SERVER_ID_SYM {} + | MASTER_CONNECT_RETRY_SYM {} + | MASTER_DELAY_SYM {} + | MASTER_SSL_SYM {} + | MASTER_SSL_CA_SYM {} + | MASTER_SSL_CAPATH_SYM {} + | MASTER_SSL_CERT_SYM {} + | MASTER_SSL_CIPHER_SYM {} + | MASTER_SSL_CRL_SYM {} + | MASTER_SSL_CRLPATH_SYM {} + | MASTER_SSL_KEY_SYM {} + | MAX_CONNECTIONS_PER_HOUR {} + | MAX_QUERIES_PER_HOUR {} + | MAX_SIZE_SYM {} + | MAX_STATEMENT_TIME_SYM {} + | MAX_UPDATES_PER_HOUR {} + | MAX_USER_CONNECTIONS_SYM {} + | MEDIUM_SYM {} + | MEMORY_SYM {} + | MERGE_SYM {} + | MESSAGE_TEXT_SYM {} + | MICROSECOND_SYM {} + | MIGRATE_SYM {} + | MINUTE_SYM {} + | MIN_ROWS {} + | MODIFY_SYM {} + | MODE_SYM {} + | MONTH_SYM {} + | MULTILINESTRING {} + | MULTIPOINT {} + | MULTIPOLYGON {} + | MUTEX_SYM {} + | MYSQL_SYM {} + | MYSQL_ERRNO_SYM {} + | NAME_SYM {} + | NAMES_SYM {} + | NATIONAL_SYM {} + | NCHAR_SYM {} + | NEXT_SYM {} + | NEW_SYM {} + | NO_WAIT_SYM {} + | NODEGROUP_SYM {} + | NONE_SYM {} + | NUMBER_SYM {} + | NVARCHAR_SYM {} + | OFFSET_SYM {} + | OLD_PASSWORD_SYM {} + | ONE_SYM {} + | ONLINE_SYM {} + | ONLY_SYM {} + | PACK_KEYS_SYM {} + | PAGE_SYM {} + | PARTIAL {} + | PARTITIONING_SYM {} + | PARTITIONS_SYM {} + | PASSWORD_SYM {} + | PERSISTENT_SYM {} + | PHASE_SYM {} + | PLUGIN_SYM {} + | PLUGINS_SYM {} + | POINT_SYM {} + | POLYGON {} + | PRESERVE_SYM {} + | PREV_SYM {} + | PRIVILEGES {} + | PROCESS {} + | PROCESSLIST_SYM {} + | PROFILE_SYM {} + | PROFILES_SYM {} + | PROXY_SYM {} + | QUARTER_SYM {} + | QUERY_SYM {} + | QUICK {} + | READ_ONLY_SYM {} + | REBUILD_SYM {} + | RECOVER_SYM {} + | REDO_BUFFER_SIZE_SYM {} + | REDOFILE_SYM {} + | REDUNDANT_SYM {} + | RELAY {} + | RELAYLOG_SYM {} + | RELAY_LOG_FILE_SYM {} + | RELAY_LOG_POS_SYM {} + | RELAY_THREAD {} + | RELOAD {} + | REORGANIZE_SYM {} + | REPEATABLE_SYM {} + | REPLICATION {} + | RESOURCES {} + | RESUME_SYM {} + | RETURNED_SQLSTATE_SYM {} + | RETURNS_SYM {} + | REVERSE_SYM {} + | ROLE_SYM {} + | ROLLUP_SYM {} + | ROUTINE_SYM {} + | ROW_COUNT_SYM {} + | ROW_FORMAT_SYM {} + | ROW_SYM {} + | RTREE_SYM {} + | SCHEDULE_SYM {} + | SCHEMA_NAME_SYM {} + | SECOND_SYM {} + | SERIAL_SYM {} + | SERIALIZABLE_SYM {} + | SESSION_SYM {} + | SIMPLE_SYM {} + | SHARE_SYM {} + | SLAVE_POS_SYM {} + | SLOW {} + | SNAPSHOT_SYM {} + | SOFT_SYM {} + | SOUNDS_SYM {} + | SOURCE_SYM {} + | SQL_CACHE_SYM {} + | SQL_BUFFER_RESULT {} + | SQL_NO_CACHE_SYM {} + | SQL_THREAD {} + | STARTS_SYM {} + | STATEMENT_SYM {} + | STATUS_SYM {} + | STORAGE_SYM {} + | STRING_SYM {} + | SUBCLASS_ORIGIN_SYM {} + | SUBDATE_SYM {} + | SUBJECT_SYM {} + | SUBPARTITION_SYM {} + | SUBPARTITIONS_SYM {} + | SUPER_SYM {} + | SUSPEND_SYM {} + | SWAPS_SYM {} + | SWITCHES_SYM {} + | TABLE_NAME_SYM {} + | TABLES {} + | TABLE_CHECKSUM_SYM {} + | TABLESPACE {} + | TEMPORARY {} + | TEMPTABLE_SYM {} + | TEXT_SYM {} + | THAN_SYM {} + | TRANSACTION_SYM {} + | TRANSACTIONAL_SYM {} + | TRIGGERS_SYM {} + | TIMESTAMP {} + | TIMESTAMP_ADD {} + | TIMESTAMP_DIFF {} + | TIME_SYM {} + | TYPES_SYM {} + | TYPE_SYM {} + | UDF_RETURNS_SYM {} + | FUNCTION_SYM {} + | UNCOMMITTED_SYM {} + | UNDEFINED_SYM {} + | UNDO_BUFFER_SIZE_SYM {} + | UNDOFILE_SYM {} + | UNKNOWN_SYM {} + | UNTIL_SYM {} + | USER_SYM {} + | USE_FRM {} + | VARIABLES {} + | VIEW_SYM {} + | VIRTUAL_SYM {} + | VALUE_SYM {} + | WARNINGS {} + | WAIT_SYM {} + | WEEK_SYM {} + | WEIGHT_STRING_SYM {} + | WORK_SYM {} + | X509_SYM {} + | XML_SYM {} + | YEAR_SYM {} + | VIA_SYM {} + ; + +/* + 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 + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_SET_OPTION; + mysql_init_select(lex); + lex->option_type=OPT_SESSION; + lex->var_list.empty(); + lex->autocommit= 0; + sp_create_assignment_lex(thd, yychar == YYEMPTY); + } + start_option_value_list + {} + | SET STATEMENT_SYM + { + LEX *lex= Lex; + mysql_init_select(lex); + lex->option_type= OPT_SESSION; + 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_yyabort_error((ER_SUBQUERIES_NOT_SUPPORTED, MYF(0), "SET STATEMENT")); + 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: + option_value_no_option_type + { + if (sp_create_assignment_instr(thd, yychar == YYEMPTY)) + MYSQL_YYABORT; + } + option_value_list_continued + | TRANSACTION_SYM + { + Lex->option_type= OPT_DEFAULT; + } + transaction_characteristics + { + if (sp_create_assignment_instr(thd, yychar == YYEMPTY)) + MYSQL_YYABORT; + } + | option_type + { + Lex->option_type= $1; + } + start_option_value_list_following_option_type + ; + + +// 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(thd, yychar == YYEMPTY)) + MYSQL_YYABORT; + } + option_value_list_continued + | TRANSACTION_SYM transaction_characteristics + { + if (sp_create_assignment_instr(thd, yychar == YYEMPTY)) + MYSQL_YYABORT; + } + ; + +// Remainder of the option value list after first option value. +option_value_list_continued: + /* empty */ + | ',' option_value_list + ; + +// Repeating list of option values after first option value. +option_value_list: + { + sp_create_assignment_lex(thd, yychar == YYEMPTY); + } + option_value + { + if (sp_create_assignment_instr(thd, yychar == YYEMPTY)) + MYSQL_YYABORT; + } + | option_value_list ',' + { + sp_create_assignment_lex(thd, yychar == YYEMPTY); + } + option_value + { + if (sp_create_assignment_instr(thd, yychar == YYEMPTY)) + 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: + GLOBAL_SYM { $$=OPT_GLOBAL; } + | LOCAL_SYM { $$=OPT_SESSION; } + | SESSION_SYM { $$=OPT_SESSION; } + ; + +opt_var_type: + /* empty */ { $$=OPT_SESSION; } + | GLOBAL_SYM { $$=OPT_GLOBAL; } + | LOCAL_SYM { $$=OPT_SESSION; } + | SESSION_SYM { $$=OPT_SESSION; } + ; + +opt_var_ident_type: + /* empty */ { $$=OPT_DEFAULT; } + | GLOBAL_SYM '.' { $$=OPT_GLOBAL; } + | LOCAL_SYM '.' { $$=OPT_SESSION; } + | SESSION_SYM '.' { $$=OPT_SESSION; } + ; + +// Option values with preceding option_type. +option_value_following_option_type: + internal_variable_name equal set_expr_or_default + { + LEX *lex= Lex; + + if ($1.var && $1.var != trg_new_row_fake_var) + { + /* It is a system variable. */ + if (lex->set_system_variable(&$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(thd, ER_SYNTAX_ERROR); + MYSQL_YYABORT; + } + } + ; + +// Option values without preceding option_type. +option_value_no_option_type: + internal_variable_name equal set_expr_or_default + { + LEX *lex= Lex; + + if ($1.var == trg_new_row_fake_var) + { + /* We are in trigger and assigning value to field of new row */ + if (lex->set_trigger_new_row(&$1.base_name, $3)) + MYSQL_YYABORT; + } + else if ($1.var) + { + /* It is a system variable. */ + if (lex->set_system_variable(&$1, lex->option_type, $3)) + MYSQL_YYABORT; + } + else + { + sp_pcontext *spc= lex->spcont; + sp_variable *spv= spc->find_variable($1.base_name, false); + + /* It is a local variable. */ + if (lex->set_local_variable(spv, $3)) + MYSQL_YYABORT; + } + } + | '@' ident_or_text equal expr + { + Item_func_set_user_var *item; + item= new (thd->mem_root) Item_func_set_user_var(thd, $2, $4); + if (item == NULL) + MYSQL_YYABORT; + set_var_user *var= new (thd->mem_root) set_var_user(item); + if (var == NULL) + MYSQL_YYABORT; + Lex->var_list.push_back(var, thd->mem_root); + } + | '@' '@' opt_var_ident_type internal_variable_name equal set_expr_or_default + { + struct sys_var_with_base tmp= $4; + /* Lookup if necessary: must be a system variable. */ + if (tmp.var == NULL) + { + if (find_sys_var_null_base(thd, &tmp)) + MYSQL_YYABORT; + } + if (Lex->set_system_variable(&tmp, $3, $6)) + MYSQL_YYABORT; + } + | charset old_or_new_charset_name_or_default + { + LEX *lex= thd->lex; + CHARSET_INFO *cs2; + cs2= $2 ? $2: global_system_variables.character_set_client; + set_var_collation_client *var; + var= (new (thd->mem_root) + set_var_collation_client(cs2, + thd->variables.collation_database, + cs2)); + if (var == NULL) + MYSQL_YYABORT; + lex->var_list.push_back(var, thd->mem_root); + } + | NAMES_SYM equal expr + { + LEX *lex= Lex; + sp_pcontext *spc= lex->spcont; + LEX_STRING names; + + names.str= (char *)"names"; + names.length= 5; + if (spc && spc->find_variable(names, false)) + my_error(ER_SP_BAD_VAR_SHADOW, MYF(0), names.str); + else + my_parse_error(thd, ER_SYNTAX_ERROR); + + MYSQL_YYABORT; + } + | NAMES_SYM charset_name_or_default opt_collate + { + LEX *lex= Lex; + CHARSET_INFO *cs2; + CHARSET_INFO *cs3; + cs2= $2 ? $2 : global_system_variables.character_set_client; + cs3= $3 ? $3 : cs2; + if (!my_charset_same(cs2, cs3)) + { + my_error(ER_COLLATION_CHARSET_MISMATCH, MYF(0), + cs3->name, cs2->csname); + MYSQL_YYABORT; + } + set_var_collation_client *var; + var= new (thd->mem_root) set_var_collation_client(cs3, cs3, cs3); + if (var == NULL) + MYSQL_YYABORT; + lex->var_list.push_back(var, thd->mem_root); + } + | DEFAULT ROLE_SYM grant_role + { + LEX *lex = Lex; + LEX_USER *user; + if (!(user=(LEX_USER *) thd->calloc(sizeof(LEX_USER)))) + MYSQL_YYABORT; + user->user= current_user; + set_var_default_role *var= (new (thd->mem_root) + set_var_default_role(user, + $3->user)); + if (var == NULL) + MYSQL_YYABORT; + lex->var_list.push_back(var, thd->mem_root); + thd->lex->autocommit= TRUE; + if (lex->sphead) + lex->sphead->m_flags|= sp_head::HAS_SET_AUTOCOMMIT_STMT; + } + | DEFAULT ROLE_SYM grant_role FOR_SYM user + { + LEX *lex = Lex; + set_var_default_role *var= (new (thd->mem_root) + set_var_default_role($5, $3->user)); + if (var == NULL) + MYSQL_YYABORT; + lex->var_list.push_back(var, thd->mem_root); + thd->lex->autocommit= TRUE; + if (lex->sphead) + lex->sphead->m_flags|= sp_head::HAS_SET_AUTOCOMMIT_STMT; + } + | ROLE_SYM ident_or_text + { + LEX *lex = Lex; + set_var_role *var= new (thd->mem_root) set_var_role($2); + if (var == NULL) + MYSQL_YYABORT; + lex->var_list.push_back(var, thd->mem_root); + } + | PASSWORD_SYM opt_for_user text_or_password + { + LEX *lex = Lex; + set_var_password *var= (new (thd->mem_root) + set_var_password(lex->definer)); + if (var == NULL) + MYSQL_YYABORT; + lex->var_list.push_back(var, thd->mem_root); + lex->autocommit= TRUE; + if (lex->sphead) + lex->sphead->m_flags|= sp_head::HAS_SET_AUTOCOMMIT_STMT; + } + ; + + +internal_variable_name: + ident + { + sp_pcontext *spc= thd->lex->spcont; + sp_variable *spv; + + /* Best effort lookup for system variable. */ + if (!spc || !(spv = spc->find_variable($1, false))) + { + struct sys_var_with_base tmp= {NULL, $1}; + + /* Not an SP local variable */ + if (find_sys_var_null_base(thd, &tmp)) + MYSQL_YYABORT; + + $$= tmp; + } + else + { + /* + Possibly an SP local variable (or a shadowed sysvar). + Will depend on the context of the SET statement. + */ + $$.var= NULL; + $$.base_name= $1; + } + } + | ident '.' ident + { + LEX *lex= Lex; + if (check_reserved_words(&$1)) + { + my_parse_error(thd, ER_SYNTAX_ERROR); + MYSQL_YYABORT; + } + 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') + my_yyabort_error((ER_TRG_CANT_CHANGE_ROW, MYF(0), "OLD", "")); + if (lex->trg_chistics.event == TRG_EVENT_DELETE) + { + my_error(ER_TRG_NO_SUCH_ROW_IN_TRG, MYF(0), + "NEW", "on DELETE"); + MYSQL_YYABORT; + } + if (lex->trg_chistics.action_time == TRG_ACTION_AFTER) + my_yyabort_error((ER_TRG_CANT_CHANGE_ROW, MYF(0), "NEW", "after ")); + /* 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(thd, $3.str, $3.length); + if (!tmp) + MYSQL_YYABORT; + if (!tmp->is_struct()) + my_error(ER_VARIABLE_IS_NOT_STRUCT, MYF(0), $3.str); + $$.var= tmp; + $$.base_name= $1; + } + } + | DEFAULT '.' ident + { + sys_var *tmp=find_sys_var(thd, $3.str, $3.length); + if (!tmp) + MYSQL_YYABORT; + if (!tmp->is_struct()) + my_error(ER_VARIABLE_IS_NOT_STRUCT, MYF(0), $3.str); + $$.var= tmp; + $$.base_name.str= (char*) "default"; + $$.base_name.length= 7; + } + ; + +transaction_characteristics: + transaction_access_mode + | isolation_level + | transaction_access_mode ',' isolation_level + | isolation_level ',' transaction_access_mode + ; + +transaction_access_mode: + transaction_access_mode_types + { + LEX *lex=Lex; + Item *item= new (thd->mem_root) Item_int(thd, (int32) $1); + if (item == NULL) + MYSQL_YYABORT; + set_var *var= (new (thd->mem_root) + set_var(thd, 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, thd->mem_root); + } + ; + +isolation_level: + ISOLATION LEVEL_SYM isolation_types + { + LEX *lex=Lex; + Item *item= new (thd->mem_root) Item_int(thd, (int32) $3); + if (item == NULL) + MYSQL_YYABORT; + set_var *var= (new (thd->mem_root) + set_var(thd, lex->option_type, + find_sys_var(thd, "tx_isolation"), + &null_lex_str, + item)); + if (var == NULL) + MYSQL_YYABORT; + lex->var_list.push_back(var, thd->mem_root); + } + ; + +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; } + | REPEATABLE_SYM READ_SYM { $$= ISO_REPEATABLE_READ; } + | SERIALIZABLE_SYM { $$= ISO_SERIALIZABLE; } + ; + +opt_for_user: + equal + { + LEX *lex= thd->lex; + sp_pcontext *spc= lex->spcont; + LEX_STRING pw= { C_STRING_WITH_LEN("password") }; + + if (spc && spc->find_variable(pw, false)) + my_yyabort_error((ER_SP_BAD_VAR_SHADOW, MYF(0), pw.str)); + if (!(lex->definer= (LEX_USER*) thd->calloc(sizeof(LEX_USER)))) + MYSQL_YYABORT; + lex->definer->user= current_user; + lex->definer->plugin= empty_lex_str; + lex->definer->auth= empty_lex_str; + } + | FOR_SYM user equal { Lex->definer= $2; } + ; + +text_or_password: + TEXT_STRING { Lex->definer->pwhash= $1;} + | PASSWORD_SYM '(' TEXT_STRING ')' { Lex->definer->pwtext= $3; } + | OLD_PASSWORD_SYM '(' TEXT_STRING ')' + { + Lex->definer->pwtext= $3; + Lex->definer->pwhash.str= Item_func_password::alloc(thd, + $3.str, $3.length, Item_func_password::OLD); + Lex->definer->pwhash.length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323; + } + ; + +set_expr_or_default: + expr { $$=$1; } + | DEFAULT { $$=0; } + | ON + { + $$=new (thd->mem_root) Item_string_sys(thd, "ON", 2); + if ($$ == NULL) + MYSQL_YYABORT; + } + | ALL + { + $$=new (thd->mem_root) Item_string_sys(thd, "ALL", 3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | BINARY + { + $$=new (thd->mem_root) Item_string_sys(thd, "binary", 6); + if ($$ == NULL) + MYSQL_YYABORT; + } + ; + +/* Lock function */ + +lock: + LOCK_SYM table_or_tables + { + LEX *lex= Lex; + + if (lex->sphead) + my_yyabort_error((ER_SP_BADSTATEMENT, MYF(0), "LOCK")); + lex->sql_command= SQLCOM_LOCK_TABLES; + } + table_lock_list + {} + ; + +table_or_tables: + TABLE_SYM { } + | TABLES { } + ; + +table_lock_list: + table_lock + | table_lock_list ',' table_lock + ; + +table_lock: + table_ident opt_table_alias lock_option + { + thr_lock_type lock_type= (thr_lock_type) $3; + bool lock_for_write= (lock_type >= TL_WRITE_ALLOW_WRITE); + if (!Select->add_table_to_list(thd, $1, $2, 0, lock_type, + (lock_for_write ? + lock_type == TL_WRITE_CONCURRENT_INSERT ? + MDL_SHARED_WRITE : + MDL_SHARED_NO_READ_WRITE : + MDL_SHARED_READ))) + MYSQL_YYABORT; + } + ; + +lock_option: + READ_SYM { $$= TL_READ_NO_INSERT; } + | WRITE_SYM { $$= TL_WRITE_DEFAULT; } + | WRITE_SYM CONCURRENT + { + $$= (Lex->sphead ? TL_WRITE_DEFAULT : TL_WRITE_CONCURRENT_INSERT); + } + + | LOW_PRIORITY WRITE_SYM { $$= TL_WRITE_LOW_PRIORITY; } + | READ_SYM LOCAL_SYM { $$= TL_READ; } + ; + +unlock: + UNLOCK_SYM + { + LEX *lex= Lex; + + if (lex->sphead) + my_yyabort_error((ER_SP_BADSTATEMENT, MYF(0), "UNLOCK")); + lex->sql_command= SQLCOM_UNLOCK_TABLES; + } + table_or_tables + {} + ; + +/* +** Handler: direct access to ISAM functions +*/ + +handler: + HANDLER_SYM table_ident OPEN_SYM opt_table_alias + { + LEX *lex= Lex; + if (lex->sphead) + my_yyabort_error((ER_SP_BADSTATEMENT, MYF(0), "HANDLER")); + lex->sql_command = SQLCOM_HA_OPEN; + if (!lex->current_select->add_table_to_list(thd, $2, $4, 0)) + MYSQL_YYABORT; + } + | HANDLER_SYM table_ident_nodb CLOSE_SYM + { + LEX *lex= Lex; + if (lex->sphead) + my_yyabort_error((ER_SP_BADSTATEMENT, MYF(0), "HANDLER")); + lex->sql_command = SQLCOM_HA_CLOSE; + if (!lex->current_select->add_table_to_list(thd, $2, 0, 0)) + MYSQL_YYABORT; + } + | HANDLER_SYM table_ident_nodb READ_SYM + { + LEX *lex=Lex; + if (lex->sphead) + my_yyabort_error((ER_SP_BADSTATEMENT, MYF(0), "HANDLER")); + lex->expr_allows_subselect= FALSE; + lex->sql_command = SQLCOM_HA_READ; + lex->ha_rkey_mode= HA_READ_KEY_EXACT; /* Avoid purify warnings */ + Item *one= new (thd->mem_root) Item_int(thd, (int32) 1); + if (one == NULL) + MYSQL_YYABORT; + lex->current_select->select_limit= one; + lex->current_select->offset_limit= 0; + lex->limit_rows_examined= 0; + if (!lex->current_select->add_table_to_list(thd, $2, 0, 0)) + MYSQL_YYABORT; + } + handler_read_or_scan opt_where_clause opt_limit_clause + { + Lex->expr_allows_subselect= TRUE; + /* Stored functions are not supported for HANDLER READ. */ + if (Lex->uses_stored_routines()) + { + my_error(ER_NOT_SUPPORTED_YET, MYF(0), + "stored functions in HANDLER ... READ"); + MYSQL_YYABORT; + } + } + ; + +handler_read_or_scan: + handler_scan_function { Lex->ident= null_lex_str; } + | ident handler_rkey_function { Lex->ident= $1; } + ; + +handler_scan_function: + FIRST_SYM { Lex->ha_read_mode = RFIRST; } + | NEXT_SYM { Lex->ha_read_mode = RNEXT; } + ; + +handler_rkey_function: + FIRST_SYM { Lex->ha_read_mode = RFIRST; } + | NEXT_SYM { Lex->ha_read_mode = RNEXT; } + | PREV_SYM { Lex->ha_read_mode = RPREV; } + | LAST_SYM { Lex->ha_read_mode = RLAST; } + | handler_rkey_mode + { + LEX *lex=Lex; + lex->ha_read_mode = RKEY; + lex->ha_rkey_mode=$1; + if (!(lex->insert_list= new (thd->mem_root) List_item)) + MYSQL_YYABORT; + } + '(' values ')' + {} + ; + +handler_rkey_mode: + '=' { $$=HA_READ_KEY_EXACT; } + | GE { $$=HA_READ_KEY_OR_NEXT; } + | LE { $$=HA_READ_KEY_OR_PREV; } + | '>' { $$=HA_READ_AFTER_KEY; } + | '<' { $$=HA_READ_BEFORE_KEY; } + ; + +/* GRANT / REVOKE */ + +revoke: + REVOKE clear_privileges revoke_command + {} + ; + +revoke_command: + grant_privileges ON opt_table grant_ident FROM user_and_role_list + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_REVOKE; + lex->type= 0; + } + | grant_privileges ON FUNCTION_SYM grant_ident FROM user_and_role_list + { + LEX *lex= Lex; + if (lex->columns.elements) + { + my_parse_error(thd, ER_SYNTAX_ERROR); + MYSQL_YYABORT; + } + lex->sql_command= SQLCOM_REVOKE; + lex->type= TYPE_ENUM_FUNCTION; + } + | grant_privileges ON PROCEDURE_SYM grant_ident FROM user_and_role_list + { + LEX *lex= Lex; + if (lex->columns.elements) + { + my_parse_error(thd, ER_SYNTAX_ERROR); + MYSQL_YYABORT; + } + lex->sql_command= SQLCOM_REVOKE; + lex->type= TYPE_ENUM_PROCEDURE; + } + | ALL opt_privileges ',' GRANT OPTION FROM user_and_role_list + { + Lex->sql_command = SQLCOM_REVOKE_ALL; + } + | PROXY_SYM ON user FROM user_list + { + LEX *lex= Lex; + lex->users_list.push_front ($3); + lex->sql_command= SQLCOM_REVOKE; + lex->type= TYPE_ENUM_PROXY; + } + | admin_option_for_role FROM user_and_role_list + { + Lex->sql_command= SQLCOM_REVOKE_ROLE; + if (Lex->users_list.push_front($1, thd->mem_root)) + MYSQL_YYABORT; + } + ; + +admin_option_for_role: + ADMIN_SYM OPTION FOR_SYM grant_role + { Lex->with_admin_option= true; $$= $4; } + | grant_role + { Lex->with_admin_option= false; $$= $1; } + ; + +grant: + GRANT clear_privileges grant_command + {} + ; + +grant_command: + grant_privileges ON opt_table grant_ident TO_SYM grant_list + opt_require_clause opt_grant_options + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_GRANT; + lex->type= 0; + } + | grant_privileges ON FUNCTION_SYM grant_ident TO_SYM grant_list + opt_require_clause opt_grant_options + { + LEX *lex= Lex; + if (lex->columns.elements) + { + my_parse_error(thd, ER_SYNTAX_ERROR); + MYSQL_YYABORT; + } + lex->sql_command= SQLCOM_GRANT; + lex->type= TYPE_ENUM_FUNCTION; + } + | grant_privileges ON PROCEDURE_SYM grant_ident TO_SYM grant_list + opt_require_clause opt_grant_options + { + LEX *lex= Lex; + if (lex->columns.elements) + { + my_parse_error(thd, ER_SYNTAX_ERROR); + MYSQL_YYABORT; + } + lex->sql_command= SQLCOM_GRANT; + lex->type= TYPE_ENUM_PROCEDURE; + } + | PROXY_SYM ON user TO_SYM grant_list opt_grant_option + { + LEX *lex= Lex; + lex->users_list.push_front ($3); + lex->sql_command= SQLCOM_GRANT; + lex->type= TYPE_ENUM_PROXY; + } + | grant_role TO_SYM grant_list opt_with_admin_option + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_GRANT_ROLE; + /* The first role is the one that is granted */ + if (Lex->users_list.push_front($1, thd->mem_root)) + MYSQL_YYABORT; + } + + ; + +opt_with_admin: + /* nothing */ { Lex->definer = 0; } + | WITH ADMIN_SYM user_or_role { Lex->definer = $3; } + +opt_with_admin_option: + /* nothing */ { Lex->with_admin_option= false; } + | WITH ADMIN_SYM OPTION { Lex->with_admin_option= true; } + +role_list: + grant_role + { + if (Lex->users_list.push_back($1, thd->mem_root)) + MYSQL_YYABORT; + } + | role_list ',' grant_role + { + if (Lex->users_list.push_back($3, thd->mem_root)) + MYSQL_YYABORT; + } + ; + +current_role: + CURRENT_ROLE optional_braces + { + if (!($$=(LEX_USER*) thd->calloc(sizeof(LEX_USER)))) + MYSQL_YYABORT; + $$->user= current_role; + $$->reset_auth(); + } + ; + +grant_role: + ident_or_text + { + CHARSET_INFO *cs= system_charset_info; + /* trim end spaces (as they'll be lost in mysql.user anyway) */ + $1.length= cs->cset->lengthsp(cs, $1.str, $1.length); + $1.str[$1.length] = '\0'; + if ($1.length == 0) + my_yyabort_error((ER_INVALID_ROLE, MYF(0), "")); + if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user)))) + MYSQL_YYABORT; + $$->user = $1; + $$->host= empty_lex_str; + $$->reset_auth(); + + if (check_string_char_length(&$$->user, ER_USERNAME, + username_char_length, + cs, 0)) + MYSQL_YYABORT; + } + | current_role + ; + +opt_table: + /* Empty */ + | TABLE_SYM + ; + +grant_privileges: + object_privilege_list {} + | ALL opt_privileges + { + Lex->all_privileges= 1; + Lex->grant= GLOBAL_ACLS; + } + ; + +opt_privileges: + /* empty */ + | PRIVILEGES + ; + +object_privilege_list: + object_privilege + | object_privilege_list ',' object_privilege + ; + +object_privilege: + SELECT_SYM + { Lex->which_columns = SELECT_ACL;} + opt_column_list {} + | INSERT + { Lex->which_columns = INSERT_ACL;} + opt_column_list {} + | UPDATE_SYM + { Lex->which_columns = UPDATE_ACL; } + opt_column_list {} + | REFERENCES + { Lex->which_columns = REFERENCES_ACL;} + opt_column_list {} + | DELETE_SYM { Lex->grant |= DELETE_ACL;} + | USAGE {} + | INDEX_SYM { Lex->grant |= INDEX_ACL;} + | ALTER { Lex->grant |= ALTER_ACL;} + | CREATE { Lex->grant |= CREATE_ACL;} + | DROP { Lex->grant |= DROP_ACL;} + | EXECUTE_SYM { Lex->grant |= EXECUTE_ACL;} + | RELOAD { Lex->grant |= RELOAD_ACL;} + | SHUTDOWN { Lex->grant |= SHUTDOWN_ACL;} + | PROCESS { Lex->grant |= PROCESS_ACL;} + | FILE_SYM { Lex->grant |= FILE_ACL;} + | GRANT OPTION { Lex->grant |= GRANT_ACL;} + | SHOW DATABASES { Lex->grant |= SHOW_DB_ACL;} + | SUPER_SYM { Lex->grant |= SUPER_ACL;} + | CREATE TEMPORARY TABLES { Lex->grant |= CREATE_TMP_ACL;} + | LOCK_SYM TABLES { Lex->grant |= LOCK_TABLES_ACL; } + | REPLICATION SLAVE { Lex->grant |= REPL_SLAVE_ACL; } + | REPLICATION CLIENT_SYM { Lex->grant |= REPL_CLIENT_ACL; } + | CREATE VIEW_SYM { Lex->grant |= CREATE_VIEW_ACL; } + | SHOW VIEW_SYM { Lex->grant |= SHOW_VIEW_ACL; } + | CREATE ROUTINE_SYM { Lex->grant |= CREATE_PROC_ACL; } + | ALTER ROUTINE_SYM { Lex->grant |= ALTER_PROC_ACL; } + | CREATE USER_SYM { Lex->grant |= CREATE_USER_ACL; } + | EVENT_SYM { Lex->grant |= EVENT_ACL;} + | TRIGGER_SYM { Lex->grant |= TRIGGER_ACL; } + | CREATE TABLESPACE { Lex->grant |= CREATE_TABLESPACE_ACL; } + ; + +opt_and: + /* empty */ {} + | AND_SYM {} + ; + +require_list: + require_list_element opt_and require_list + | require_list_element + ; + +require_list_element: + SUBJECT_SYM TEXT_STRING + { + LEX *lex=Lex; + if (lex->x509_subject) + my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "SUBJECT")); + lex->x509_subject=$2.str; + } + | ISSUER_SYM TEXT_STRING + { + LEX *lex=Lex; + if (lex->x509_issuer) + my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "ISSUER")); + lex->x509_issuer=$2.str; + } + | CIPHER_SYM TEXT_STRING + { + LEX *lex=Lex; + if (lex->ssl_cipher) + my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "CIPHER")); + lex->ssl_cipher=$2.str; + } + ; + +grant_ident: + '*' + { + LEX *lex= Lex; + size_t dummy; + if (lex->copy_db_to(&lex->current_select->db, &dummy)) + MYSQL_YYABORT; + if (lex->grant == GLOBAL_ACLS) + lex->grant = DB_ACLS & ~GRANT_ACL; + else if (lex->columns.elements) + my_yyabort_error((ER_ILLEGAL_GRANT_FOR_TABLE, MYF(0))); + } + | ident '.' '*' + { + LEX *lex= Lex; + lex->current_select->db = $1.str; + if (lex->grant == GLOBAL_ACLS) + lex->grant = DB_ACLS & ~GRANT_ACL; + else if (lex->columns.elements) + my_yyabort_error((ER_ILLEGAL_GRANT_FOR_TABLE, MYF(0))); + } + | '*' '.' '*' + { + LEX *lex= Lex; + lex->current_select->db = NULL; + if (lex->grant == GLOBAL_ACLS) + lex->grant= GLOBAL_ACLS & ~GRANT_ACL; + else if (lex->columns.elements) + my_yyabort_error((ER_ILLEGAL_GRANT_FOR_TABLE, MYF(0))); + } + | table_ident + { + LEX *lex=Lex; + if (!lex->current_select->add_table_to_list(thd, $1,NULL, + TL_OPTION_UPDATING)) + MYSQL_YYABORT; + if (lex->grant == GLOBAL_ACLS) + lex->grant = TABLE_ACLS & ~GRANT_ACL; + } + ; + +user_list: + user + { + if (Lex->users_list.push_back($1, thd->mem_root)) + MYSQL_YYABORT; + } + | user_list ',' user + { + if (Lex->users_list.push_back($3, thd->mem_root)) + MYSQL_YYABORT; + } + ; + +grant_list: + grant_user + { + if (Lex->users_list.push_back($1, thd->mem_root)) + MYSQL_YYABORT; + } + | grant_list ',' grant_user + { + if (Lex->users_list.push_back($3, thd->mem_root)) + MYSQL_YYABORT; + } + ; + +user_and_role_list: + user_or_role + { + if (Lex->users_list.push_back($1, thd->mem_root)) + MYSQL_YYABORT; + } + | user_and_role_list ',' user_or_role + { + if (Lex->users_list.push_back($3, thd->mem_root)) + MYSQL_YYABORT; + } + ; + +via_or_with: VIA_SYM | WITH ; +using_or_as: USING | AS ; + +grant_user: + user IDENTIFIED_SYM BY TEXT_STRING + { + $$= $1; + $1->pwtext= $4; + if (Lex->sql_command == SQLCOM_REVOKE) + MYSQL_YYABORT; + } + | user IDENTIFIED_SYM BY PASSWORD_SYM TEXT_STRING + { + $$= $1; + $1->pwhash= $5; + } + | user IDENTIFIED_SYM via_or_with ident_or_text + { + $$= $1; + $1->plugin= $4; + $1->auth= empty_lex_str; + } + | user IDENTIFIED_SYM via_or_with ident_or_text using_or_as TEXT_STRING_sys + { + $$= $1; + $1->plugin= $4; + $1->auth= $6; + } + | user_or_role + { $$= $1; } + ; + +opt_column_list: + /* empty */ + { + LEX *lex=Lex; + lex->grant |= lex->which_columns; + } + | '(' column_list ')' + ; + +column_list: + column_list ',' column_list_id + | column_list_id + ; + +column_list_id: + ident + { + String *new_str= new (thd->mem_root) String((const char*) $1.str,$1.length,system_charset_info); + if (new_str == NULL) + MYSQL_YYABORT; + List_iterator <LEX_COLUMN> iter(Lex->columns); + class LEX_COLUMN *point; + LEX *lex=Lex; + while ((point=iter++)) + { + if (!my_strcasecmp(system_charset_info, + point->column.c_ptr(), new_str->c_ptr())) + break; + } + lex->grant_tot_col|= lex->which_columns; + if (point) + point->rights |= lex->which_columns; + else + { + LEX_COLUMN *col= (new (thd->mem_root) + LEX_COLUMN(*new_str,lex->which_columns)); + if (col == NULL) + MYSQL_YYABORT; + lex->columns.push_back(col, thd->mem_root); + } + } + ; + +opt_require_clause: + /* empty */ + | REQUIRE_SYM require_list + { + Lex->ssl_type=SSL_TYPE_SPECIFIED; + } + | REQUIRE_SYM SSL_SYM + { + Lex->ssl_type=SSL_TYPE_ANY; + } + | REQUIRE_SYM X509_SYM + { + Lex->ssl_type=SSL_TYPE_X509; + } + | REQUIRE_SYM NONE_SYM + { + Lex->ssl_type=SSL_TYPE_NONE; + } + ; + +resource_option: + MAX_QUERIES_PER_HOUR ulong_num + { + LEX *lex=Lex; + lex->mqh.questions=$2; + lex->mqh.specified_limits|= USER_RESOURCES::QUERIES_PER_HOUR; + } + | MAX_UPDATES_PER_HOUR ulong_num + { + LEX *lex=Lex; + lex->mqh.updates=$2; + lex->mqh.specified_limits|= USER_RESOURCES::UPDATES_PER_HOUR; + } + | MAX_CONNECTIONS_PER_HOUR ulong_num + { + LEX *lex=Lex; + lex->mqh.conn_per_hour= $2; + lex->mqh.specified_limits|= USER_RESOURCES::CONNECTIONS_PER_HOUR; + } + | MAX_USER_CONNECTIONS_SYM int_num + { + LEX *lex=Lex; + lex->mqh.user_conn= $2; + lex->mqh.specified_limits|= USER_RESOURCES::USER_CONNECTIONS; + } + | MAX_STATEMENT_TIME_SYM NUM_literal + { + LEX *lex=Lex; + lex->mqh.max_statement_time= $2->val_real(); + lex->mqh.specified_limits|= USER_RESOURCES::MAX_STATEMENT_TIME; + } + ; + +resource_option_list: + resource_option_list resource_option {} + | resource_option {} + ; + +opt_resource_options: + /* empty */ {} + | WITH resource_option_list + ; + + +opt_grant_options: + /* empty */ {} + | WITH grant_option_list {} + ; + +opt_grant_option: + /* empty */ {} + | WITH GRANT OPTION { Lex->grant |= GRANT_ACL;} + ; + +grant_option_list: + grant_option_list grant_option {} + | grant_option {} + ; + +grant_option: + GRANT OPTION { Lex->grant |= GRANT_ACL;} + | resource_option {} + ; + +begin: + BEGIN_SYM + { + LEX *lex=Lex; + lex->sql_command = SQLCOM_BEGIN; + lex->start_transaction_opt= 0; + } + opt_work {} + ; + +compound_statement: + sp_proc_stmt_compound_ok + { + Lex->sql_command= SQLCOM_COMPOUND; + Lex->sphead->set_stmt_end(thd); + Lex->sphead->restore_thd_mem_root(thd); + } + ; + +opt_not: + /* nothing */ { $$= 0; } + | not { $$= 1; } + ; + +opt_work: + /* empty */ {} + | WORK_SYM {} + ; + +opt_chain: + /* empty */ + { $$= TVL_UNKNOWN; } + | AND_SYM NO_SYM CHAIN_SYM { $$= TVL_NO; } + | AND_SYM CHAIN_SYM { $$= TVL_YES; } + ; + +opt_release: + /* empty */ + { $$= TVL_UNKNOWN; } + | RELEASE_SYM { $$= TVL_YES; } + | NO_SYM RELEASE_SYM { $$= TVL_NO; } +; + +opt_savepoint: + /* empty */ {} + | SAVEPOINT_SYM {} + ; + +commit: + COMMIT_SYM opt_work opt_chain opt_release + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_COMMIT; + /* Don't allow AND CHAIN RELEASE. */ + MYSQL_YYABORT_UNLESS($3 != TVL_YES || $4 != TVL_YES); + lex->tx_chain= $3; + lex->tx_release= $4; + } + ; + +rollback: + ROLLBACK_SYM opt_work opt_chain opt_release + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_ROLLBACK; + /* Don't allow AND CHAIN RELEASE. */ + MYSQL_YYABORT_UNLESS($3 != TVL_YES || $4 != TVL_YES); + lex->tx_chain= $3; + lex->tx_release= $4; + } + | ROLLBACK_SYM opt_work + TO_SYM opt_savepoint ident + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_ROLLBACK_TO_SAVEPOINT; + lex->ident= $5; + } + ; + +savepoint: + SAVEPOINT_SYM ident + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_SAVEPOINT; + lex->ident= $2; + } + ; + +release: + RELEASE_SYM SAVEPOINT_SYM ident + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_RELEASE_SAVEPOINT; + lex->ident= $3; + } + ; + +/* + UNIONS : glue selects together +*/ + +unit_type_decl: + UNION_SYM + { $$= UNION_TYPE; } + | INTERSECT_SYM + { $$= INTERSECT_TYPE; } + | EXCEPT_SYM + { $$= EXCEPT_TYPE; } + + +union_clause: + /* empty */ {} + | union_list + ; + +union_list: + unit_type_decl union_option + { + if (Lex->add_select_to_union_list((bool)$2, $1, TRUE)) + MYSQL_YYABORT; + } + union_list_part2 + { + /* + Remove from the name resolution context stack the context of the + last select in the union. + */ + Lex->pop_context(); + } + ; + +union_list_view: + unit_type_decl union_option + { + if (Lex->add_select_to_union_list((bool)$2, $1, TRUE)) + MYSQL_YYABORT; + } + query_expression_body_view + { + Lex->pop_context(); + } + ; + +union_order_or_limit: + { + LEX *lex= thd->lex; + DBUG_ASSERT(lex->current_select->linkage != GLOBAL_OPTIONS_TYPE); + SELECT_LEX *sel= lex->current_select; + SELECT_LEX_UNIT *unit= sel->master_unit(); + SELECT_LEX *fake= unit->fake_select_lex; + if (fake) + { + fake->no_table_names_allowed= 1; + lex->current_select= fake; + } + thd->where= "global ORDER clause"; + } + order_or_limit + { + thd->lex->current_select->no_table_names_allowed= 0; + thd->where= ""; + } + ; + +order_or_limit: + order_clause opt_limit_clause + | limit_clause + ; + +/* + Start a UNION, for non-top level query expressions. +*/ +union_head_non_top: + unit_type_decl union_option + { + if (Lex->add_select_to_union_list((bool)$2, $1, FALSE)) + MYSQL_YYABORT; + } + ; + +union_option: + /* empty */ { $$=1; } + | DISTINCT { $$=1; } + | ALL { $$=0; } + ; + +/* + Corresponds to the SQL Standard + <query specification> ::= + SELECT [ <set quantifier> ] <select list> <table expression> + + Notes: + - We allow more options in addition to <set quantifier> + - <table expression> is optional in MariaDB +*/ +query_specification: + SELECT_SYM select_init2_derived opt_table_expression + { + $$= Lex->current_select->master_unit()->first_select(); + } + ; + +query_term_union_not_ready: + query_specification order_or_limit opt_select_lock_type { $$= $1; } + | '(' select_paren_derived ')' union_order_or_limit { $$= $2; } + ; + +query_term_union_ready: + query_specification opt_select_lock_type { $$= $1; } + | '(' select_paren_derived ')' { $$= $2; } + ; + +query_expression_body: + query_term_union_not_ready { $$= $1; } + | query_term_union_ready { $$= $1; } + | query_term_union_ready union_list_derived { $$= $1; } + ; + +/* Corresponds to <query expression> in the SQL:2003 standard. */ +subselect: + subselect_start opt_with_clause query_expression_body subselect_end + { + $3->set_with_clause($2); + $$= $3; + } + ; + +subselect_start: + { + LEX *lex=Lex; + if (!lex->expr_allows_subselect || + lex->sql_command == (int)SQLCOM_PURGE) + { + my_parse_error(thd, ER_SYNTAX_ERROR); + MYSQL_YYABORT; + } + /* + we are making a "derived table" for the parenthesis + as we need to have a lex level to fit the union + after the parenthesis, e.g. + (SELECT .. ) UNION ... becomes + SELECT * FROM ((SELECT ...) UNION ...) + */ + if (mysql_new_select(Lex, 1, NULL)) + MYSQL_YYABORT; + } + ; + +subselect_end: + { + LEX *lex=Lex; + + lex->check_automatic_up(UNSPECIFIED_TYPE); + lex->pop_context(); + SELECT_LEX *child= lex->current_select; + lex->current_select = lex->current_select->return_after_parsing(); + lex->nest_level--; + lex->current_select->n_child_sum_items += child->n_sum_items; + /* + A subselect can add fields to an outer select. Reserve space for + them. + */ + lex->current_select->select_n_where_fields+= + child->select_n_where_fields; + + /* + Aggregate functions in having clause may add fields to an outer + select. Count them also. + */ + lex->current_select->select_n_having_items+= + child->select_n_having_items; + } + ; + +opt_query_expression_options: + /* empty */ + | query_expression_option_list + ; + +query_expression_option_list: + query_expression_option_list query_expression_option + | query_expression_option + ; + +query_expression_option: + STRAIGHT_JOIN { Select->options|= SELECT_STRAIGHT_JOIN; } + | HIGH_PRIORITY + { + if (check_simple_select()) + MYSQL_YYABORT; + YYPS->m_lock_type= TL_READ_HIGH_PRIORITY; + YYPS->m_mdl_type= MDL_SHARED_READ; + Select->options|= SELECT_HIGH_PRIORITY; + } + | DISTINCT { Select->options|= SELECT_DISTINCT; } + | SQL_SMALL_RESULT { Select->options|= SELECT_SMALL_RESULT; } + | SQL_BIG_RESULT { Select->options|= SELECT_BIG_RESULT; } + | SQL_BUFFER_RESULT + { + if (check_simple_select()) + MYSQL_YYABORT; + Select->options|= OPTION_BUFFER_RESULT; + } + | SQL_CALC_FOUND_ROWS + { + if (check_simple_select()) + MYSQL_YYABORT; + Select->options|= OPTION_FOUND_ROWS; + } + | ALL { Select->options|= SELECT_ALL; } + ; + +/************************************************************************** + + CREATE VIEW | TRIGGER | PROCEDURE statements. + +**************************************************************************/ + +view_or_trigger_or_sp_or_event: + definer definer_tail + {} + | no_definer no_definer_tail + {} + | view_algorithm definer_opt view_tail + {} + ; + +definer_tail: + view_tail + | trigger_tail + | sp_tail + | sf_tail + | event_tail + ; + +no_definer_tail: + view_tail + | trigger_tail + | sp_tail + | sf_tail + | udf_tail + | event_tail + ; + +/************************************************************************** + + DEFINER clause support. + +**************************************************************************/ + +definer_opt: + no_definer + | definer + ; + +no_definer: + /* empty */ + { + /* + We have to distinguish missing DEFINER-clause from case when + CURRENT_USER specified as definer explicitly in order to properly + handle CREATE TRIGGER statements which come to replication thread + from older master servers (i.e. to create non-suid trigger in this + case). + */ + thd->lex->definer= 0; + } + ; + +definer: + DEFINER_SYM '=' user_or_role + { + Lex->definer= $3; + Lex->ssl_type= SSL_TYPE_NOT_SPECIFIED; + Lex->ssl_cipher= Lex->x509_subject= Lex->x509_issuer= 0; + bzero(&(Lex->mqh), sizeof(Lex->mqh)); + } + ; + +/************************************************************************** + + CREATE VIEW statement parts. + +**************************************************************************/ + +view_algorithm: + ALGORITHM_SYM '=' UNDEFINED_SYM + { Lex->create_view_algorithm= DTYPE_ALGORITHM_UNDEFINED; } + | ALGORITHM_SYM '=' MERGE_SYM + { Lex->create_view_algorithm= VIEW_ALGORITHM_MERGE; } + | ALGORITHM_SYM '=' TEMPTABLE_SYM + { Lex->create_view_algorithm= VIEW_ALGORITHM_TMPTABLE; } + ; + +view_suid: + /* empty */ + { Lex->create_view_suid= VIEW_SUID_DEFAULT; } + | SQL_SYM SECURITY_SYM DEFINER_SYM + { Lex->create_view_suid= VIEW_SUID_DEFINER; } + | SQL_SYM SECURITY_SYM INVOKER_SYM + { Lex->create_view_suid= VIEW_SUID_INVOKER; } + ; + +view_tail: + view_suid VIEW_SYM opt_if_not_exists table_ident + { + LEX *lex= thd->lex; + if (lex->add_create_options_with_check($3)) + MYSQL_YYABORT; + lex->sql_command= SQLCOM_CREATE_VIEW; + /* first table in list is target VIEW name */ + if (!lex->select_lex.add_table_to_list(thd, $4, NULL, + TL_OPTION_UPDATING, + TL_IGNORE, + MDL_EXCLUSIVE)) + MYSQL_YYABORT; + lex->query_tables->open_strategy= TABLE_LIST::OPEN_STUB; + } + view_list_opt AS view_select + ; + +view_list_opt: + /* empty */ + {} + | '(' view_list ')' + ; + +view_list: + ident + { + Lex->view_list.push_back((LEX_STRING*) + thd->memdup(&$1, sizeof(LEX_STRING)), + thd->mem_root); + } + | view_list ',' ident + { + Lex->view_list.push_back((LEX_STRING*) + thd->memdup(&$3, sizeof(LEX_STRING)), + thd->mem_root); + } + ; + +view_select: + { + LEX *lex= Lex; + lex->parsing_options.allows_variable= FALSE; + lex->create_view_select.str= (char *) YYLIP->get_cpp_ptr(); + } + opt_with_clause query_expression_body_view view_check_option + { + LEX *lex= Lex; + uint len= YYLIP->get_cpp_ptr() - lex->create_view_select.str; + uint not_used; + void *create_view_select= thd->memdup(lex->create_view_select.str, len); + lex->create_view_select.length= len; + lex->create_view_select.str= (char *) create_view_select; + trim_whitespace(thd->charset(), &lex->create_view_select, + ¬_used); + lex->parsing_options.allows_variable= TRUE; + lex->current_select->set_with_clause($2); + } + ; + +/* + SQL Standard <query expression body> for VIEWs. + Does not include INTO and PROCEDURE clauses. +*/ +query_expression_body_view: + SELECT_SYM select_options_and_item_list select_init3_view + | '(' select_paren_view ')' + | '(' select_paren_view ')' union_order_or_limit + | '(' select_paren_view ')' union_list_view + ; + +view_check_option: + /* empty */ + { Lex->create_view_check= VIEW_CHECK_NONE; } + | WITH CHECK_SYM OPTION + { Lex->create_view_check= VIEW_CHECK_CASCADED; } + | WITH CASCADED CHECK_SYM OPTION + { Lex->create_view_check= VIEW_CHECK_CASCADED; } + | WITH LOCAL_SYM CHECK_SYM OPTION + { Lex->create_view_check= VIEW_CHECK_LOCAL; } + ; + +/************************************************************************** + + CREATE TRIGGER statement parts. + +**************************************************************************/ + +trigger_action_order: + FOLLOWS_SYM + { $$= TRG_ORDER_FOLLOWS; } + | PRECEDES_SYM + { $$= TRG_ORDER_PRECEDES; } + ; + +trigger_follows_precedes_clause: + /* empty */ + { + $$.ordering_clause= TRG_ORDER_NONE; + $$.anchor_trigger_name.str= NULL; + $$.anchor_trigger_name.length= 0; + } + | + trigger_action_order ident_or_text + { + $$.ordering_clause= $1; + $$.anchor_trigger_name= $2; + } + ; + +trigger_tail: + TRIGGER_SYM + remember_name + opt_if_not_exists + { + if (Lex->add_create_options_with_check($3)) + MYSQL_YYABORT; + } + sp_name + trg_action_time + trg_event + ON + remember_name /* $9 */ + { /* $10 */ + Lex->raw_trg_on_table_name_begin= YYLIP->get_tok_start(); + } + table_ident /* $11 */ + FOR_SYM + remember_name /* $13 */ + { /* $14 */ + Lex->raw_trg_on_table_name_end= YYLIP->get_tok_start(); + } + EACH_SYM + ROW_SYM + { + Lex->trg_chistics.ordering_clause_begin= YYLIP->get_cpp_ptr(); + } + trigger_follows_precedes_clause /* $18 */ + { /* $19 */ + LEX *lex= thd->lex; + Lex_input_stream *lip= YYLIP; + + if (lex->sphead) + my_yyabort_error((ER_SP_NO_RECURSIVE_CREATE, MYF(0), "TRIGGER")); + + lex->stmt_definition_begin= $2; + lex->ident.str= $9; + lex->ident.length= $13 - $9; + lex->spname= $5; + (*static_cast<st_trg_execution_order*>(&lex->trg_chistics))= ($18); + lex->trg_chistics.ordering_clause_end= lip->get_cpp_ptr(); + + if (!make_sp_head(thd, $5, TYPE_ENUM_TRIGGER)) + MYSQL_YYABORT; + + lex->sphead->set_body_start(thd, lip->get_cpp_tok_start()); + } + sp_proc_stmt /* $20 */ + { /* $21 */ + LEX *lex= Lex; + sp_head *sp= lex->sphead; + + lex->sql_command= SQLCOM_CREATE_TRIGGER; + sp->set_stmt_end(thd); + sp->restore_thd_mem_root(thd); + + if (sp->is_not_allowed_in_function("trigger")) + MYSQL_YYABORT; + + /* + We have to do it after parsing trigger body, because some of + sp_proc_stmt alternatives are not saving/restoring LEX, so + lex->query_tables can be wiped out. + */ + if (!lex->select_lex.add_table_to_list(thd, $11, + (LEX_STRING*) 0, + TL_OPTION_UPDATING, + TL_READ_NO_INSERT, + MDL_SHARED_NO_WRITE)) + MYSQL_YYABORT; + } + ; + +/************************************************************************** + + CREATE FUNCTION | PROCEDURE statements parts. + +**************************************************************************/ + +udf_tail: + AGGREGATE_SYM udf_tail2 { thd->lex->udf.type= UDFTYPE_AGGREGATE; } + | udf_tail2 { thd->lex->udf.type= UDFTYPE_FUNCTION; } + ; + +udf_tail2: + FUNCTION_SYM opt_if_not_exists ident + RETURNS_SYM udf_type SONAME_SYM TEXT_STRING_sys + { + LEX *lex= thd->lex; + if (lex->add_create_options_with_check($2)) + MYSQL_YYABORT; + if (is_native_function(thd, & $3)) + my_yyabort_error((ER_NATIVE_FCT_NAME_COLLISION, MYF(0), $3.str)); + lex->sql_command= SQLCOM_CREATE_FUNCTION; + lex->udf.name= $3; + lex->udf.returns= (Item_result) $5; + lex->udf.dl= $7.str; + } + ; + +sf_tail: + FUNCTION_SYM /* $1 */ + opt_if_not_exists /* $2 */ + sp_name /* $3 */ + '(' /* $4 */ + { /* $5 */ + LEX *lex= Lex; + Lex_input_stream *lip= YYLIP; + const char* tmp_param_begin; + + if (lex->add_create_options_with_check($2)) + MYSQL_YYABORT; + lex->spname= $3; + + if (lex->sphead) + my_yyabort_error((ER_SP_NO_RECURSIVE_CREATE, MYF(0), "FUNCTION")); + + if (!make_sp_head(thd, $3, TYPE_ENUM_FUNCTION)) + MYSQL_YYABORT; + + tmp_param_begin= lip->get_cpp_tok_start(); + tmp_param_begin++; + lex->sphead->m_param_begin= tmp_param_begin; + } + sp_fdparam_list /* $6 */ + ')' /* $7 */ + { /* $8 */ + Lex->sphead->m_param_end= YYLIP->get_cpp_tok_start(); + } + RETURNS_SYM /* $9 */ + { /* $10 */ + LEX *lex= Lex; + lex->init_last_field(&lex->sphead->m_return_field_def, NULL, + thd->variables.collation_database); + } + type_with_opt_collate /* $11 */ + { /* $12 */ + if (Lex->sphead->fill_field_definition(thd, Lex->last_field)) + MYSQL_YYABORT; + } + sp_c_chistics /* $13 */ + { /* $14 */ + LEX *lex= thd->lex; + Lex_input_stream *lip= YYLIP; + + lex->sphead->set_body_start(thd, lip->get_cpp_tok_start()); + } + sp_proc_stmt_in_returns_clause /* $15 */ + { + LEX *lex= thd->lex; + sp_head *sp= lex->sphead; + + if (sp->is_not_allowed_in_function("function")) + MYSQL_YYABORT; + + lex->sql_command= SQLCOM_CREATE_SPFUNCTION; + sp->set_stmt_end(thd); + if (!(sp->m_flags & sp_head::HAS_RETURN)) + my_yyabort_error((ER_SP_NORETURN, MYF(0), + ErrConvDQName(sp).ptr())); + (void) is_native_function_with_warn(thd, &sp->m_name); + sp->restore_thd_mem_root(thd); + } + ; + +sp_tail: + PROCEDURE_SYM opt_if_not_exists sp_name + { + if (Lex->add_create_options_with_check($2)) + MYSQL_YYABORT; + + if (Lex->sphead) + my_yyabort_error((ER_SP_NO_RECURSIVE_CREATE, MYF(0), "PROCEDURE")); + + if (!make_sp_head(thd, $3, TYPE_ENUM_PROCEDURE)) + MYSQL_YYABORT; + Lex->spname= $3; + } + '(' + { + const char* tmp_param_begin; + + tmp_param_begin= YYLIP->get_cpp_tok_start(); + tmp_param_begin++; + Lex->sphead->m_param_begin= tmp_param_begin; + } + sp_pdparam_list + ')' + { + Lex->sphead->m_param_end= YYLIP->get_cpp_tok_start(); + } + sp_c_chistics + { + Lex->sphead->set_body_start(thd, YYLIP->get_cpp_tok_start()); + } + sp_proc_stmt + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + + sp->set_stmt_end(thd); + lex->sql_command= SQLCOM_CREATE_PROCEDURE; + sp->restore_thd_mem_root(thd); + } + ; + +/*************************************************************************/ + +xa: + XA_SYM begin_or_start xid opt_join_or_resume + { + Lex->sql_command = SQLCOM_XA_START; + } + | XA_SYM END xid opt_suspend + { + Lex->sql_command = SQLCOM_XA_END; + } + | XA_SYM PREPARE_SYM xid + { + Lex->sql_command = SQLCOM_XA_PREPARE; + } + | XA_SYM COMMIT_SYM xid opt_one_phase + { + Lex->sql_command = SQLCOM_XA_COMMIT; + } + | XA_SYM ROLLBACK_SYM xid + { + Lex->sql_command = SQLCOM_XA_ROLLBACK; + } + | XA_SYM RECOVER_SYM + { + Lex->sql_command = SQLCOM_XA_RECOVER; + } + ; + +xid: + text_string + { + MYSQL_YYABORT_UNLESS($1->length() <= MAXGTRIDSIZE); + if (!(Lex->xid=(XID *)thd->alloc(sizeof(XID)))) + MYSQL_YYABORT; + Lex->xid->set(1L, $1->ptr(), $1->length(), 0, 0); + } + | text_string ',' text_string + { + MYSQL_YYABORT_UNLESS($1->length() <= MAXGTRIDSIZE && $3->length() <= MAXBQUALSIZE); + if (!(Lex->xid=(XID *)thd->alloc(sizeof(XID)))) + MYSQL_YYABORT; + Lex->xid->set(1L, $1->ptr(), $1->length(), $3->ptr(), $3->length()); + } + | text_string ',' text_string ',' ulong_num + { + MYSQL_YYABORT_UNLESS($1->length() <= MAXGTRIDSIZE && $3->length() <= MAXBQUALSIZE); + if (!(Lex->xid=(XID *)thd->alloc(sizeof(XID)))) + MYSQL_YYABORT; + Lex->xid->set($5, $1->ptr(), $1->length(), $3->ptr(), $3->length()); + } + ; + +begin_or_start: + BEGIN_SYM {} + | START_SYM {} + ; + +opt_join_or_resume: + /* nothing */ { Lex->xa_opt=XA_NONE; } + | JOIN_SYM { Lex->xa_opt=XA_JOIN; } + | RESUME_SYM { Lex->xa_opt=XA_RESUME; } + ; + +opt_one_phase: + /* nothing */ { Lex->xa_opt=XA_NONE; } + | ONE_SYM PHASE_SYM { Lex->xa_opt=XA_ONE_PHASE; } + ; + +opt_suspend: + /* nothing */ + { Lex->xa_opt=XA_NONE; } + | SUSPEND_SYM + { Lex->xa_opt=XA_SUSPEND; } + opt_migrate + ; + +opt_migrate: + /* nothing */ {} + | FOR_SYM MIGRATE_SYM { Lex->xa_opt=XA_FOR_MIGRATE; } + ; + +install: + INSTALL_SYM PLUGIN_SYM ident SONAME_SYM TEXT_STRING_sys + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_INSTALL_PLUGIN; + lex->comment= $3; + lex->ident= $5; + } + | INSTALL_SYM SONAME_SYM TEXT_STRING_sys + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_INSTALL_PLUGIN; + lex->comment= null_lex_str; + lex->ident= $3; + } + ; + +uninstall: + UNINSTALL_SYM PLUGIN_SYM ident + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_UNINSTALL_PLUGIN; + lex->comment= $3; + } + | UNINSTALL_SYM SONAME_SYM TEXT_STRING_sys + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_UNINSTALL_PLUGIN; + lex->comment= null_lex_str; + lex->ident= $3; + } + ; + +/* Avoid compiler warning from sql_yacc.cc where yyerrlab1 is not used */ +keep_gcc_happy: + IMPOSSIBLE_ACTION + { + YYERROR; + } + +/** + @} (end of group Parser) +*/ |