diff --git a/mysql-test/main/ps.result b/mysql-test/main/ps.result index a72b6660288..2bb817c8286 100644 --- a/mysql-test/main/ps.result +++ b/mysql-test/main/ps.result @@ -4663,7 +4663,7 @@ EXECUTE IMMEDIATE 'SELECT ? FROM DUAL' USING (SELECT 1); ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'SELECT 1)' at line 1 CREATE FUNCTION f1() RETURNS VARCHAR(10) RETURN 'test'; EXECUTE IMMEDIATE 'SELECT ? FROM DUAL' USING f1(); -ERROR 42000: EXECUTE..USING does not support subqueries or stored functions +ERROR 42000: EXECUTE IMMEDIATE does not support subqueries or stored functions DROP FUNCTION f1; # # DDL diff --git a/mysql-test/suite/compat/oracle/r/ps.result b/mysql-test/suite/compat/oracle/r/ps.result index 158d15e9f90..73aa04b972c 100644 --- a/mysql-test/suite/compat/oracle/r/ps.result +++ b/mysql-test/suite/compat/oracle/r/ps.result @@ -163,7 +163,7 @@ RETURN 'test'; END; $$ EXECUTE IMMEDIATE 'SELECT ? FROM DUAL' USING f1(); -ERROR 42000: EXECUTE..USING does not support subqueries or stored functions +ERROR 42000: EXECUTE IMMEDIATE does not support subqueries or stored functions DROP FUNCTION f1; # # Testing simple expressions diff --git a/sql/protocol.cc b/sql/protocol.cc index 1d5f77fb995..aec222a2410 100644 --- a/sql/protocol.cc +++ b/sql/protocol.cc @@ -1338,11 +1338,10 @@ bool Protocol_text::store_time(MYSQL_TIME *tm, int decimals) bool Protocol_text::send_out_parameters(List *sp_params) { - DBUG_ASSERT(sp_params->elements == - thd->lex->prepared_stmt_params.elements); + DBUG_ASSERT(sp_params->elements == thd->lex->prepared_stmt.param_count()); List_iterator_fast item_param_it(*sp_params); - List_iterator_fast param_it(thd->lex->prepared_stmt_params); + List_iterator_fast param_it(thd->lex->prepared_stmt.params()); while (true) { diff --git a/sql/sql_class.h b/sql/sql_class.h index 4e846d3f169..bac5807677c 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1214,7 +1214,7 @@ public: int insert(THD *thd, Statement *statement); - Statement *find_by_name(LEX_CSTRING *name) + Statement *find_by_name(const LEX_CSTRING *name) { Statement *stmt; stmt= (Statement*)my_hash_search(&names_hash, (uchar*)name->str, diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 53b40aabbc8..76e0e13b873 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -705,7 +705,7 @@ void LEX::start(THD *thd_arg) with_persistent_for_clause= FALSE; column_list= NULL; index_list= NULL; - prepared_stmt_params.empty(); + prepared_stmt.lex_start(); auxiliary_table_list.empty(); unit.next= unit.master= unit.link_next= unit.return_to= 0; unit.prev= unit.link_prev= 0; @@ -10216,3 +10216,50 @@ bool LEX::stmt_uninstall_plugin_by_soname(const DDL_options_st &opt, ident= soname; return false; } + + +bool LEX::stmt_prepare_validate(const char *stmt_type) +{ + if (unlikely(table_or_sp_used())) + { + my_error(ER_SUBQUERIES_NOT_SUPPORTED, MYF(0), stmt_type); + return true; + } + return check_main_unit_semantics(); +} + + +bool LEX::stmt_prepare(const Lex_ident_sys_st &ident, Item *code) +{ + sql_command= SQLCOM_PREPARE; + if (stmt_prepare_validate("PREPARE..FROM")) + return true; + prepared_stmt.set(ident, code, NULL); + return false; +} + + +bool LEX::stmt_execute_immediate(Item *code, List *params) +{ + sql_command= SQLCOM_EXECUTE_IMMEDIATE; + if (stmt_prepare_validate("EXECUTE IMMEDIATE")) + return true; + static const Lex_ident_sys immediate(STRING_WITH_LEN("IMMEDIATE")); + prepared_stmt.set(immediate, code, params); + return false; +} + + +bool LEX::stmt_execute(const Lex_ident_sys_st &ident, List *params) +{ + sql_command= SQLCOM_EXECUTE; + prepared_stmt.set(ident, NULL, params); + return stmt_prepare_validate("EXECUTE..USING"); +} + + +void LEX::stmt_deallocate_prepare(const Lex_ident_sys_st &ident) +{ + sql_command= SQLCOM_DEALLOCATE_PREPARE; + prepared_stmt.set(ident, NULL, NULL); +} diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 35b13b37b26..a5212adf7a4 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -169,6 +169,16 @@ public: { ((LEX_CSTRING &) *this)= null_clex_str; } + Lex_ident_sys(const char *name, size_t length) + { + LEX_CSTRING tmp= {name, length}; + set_valid_utf8(&tmp); + } + Lex_ident_sys & operator=(const Lex_ident_sys_st &name) + { + Lex_ident_sys_st::operator=(name); + return *this; + } }; @@ -2990,6 +3000,56 @@ struct Account_options: public USER_RESOURCES class Query_arena_memroot; /* The state of the lex parsing. This is saved in the THD struct */ + +class Lex_prepared_stmt +{ + Lex_ident_sys m_name; // Statement name (in all queries) + Item *m_code; // PREPARE or EXECUTE IMMEDIATE source expression + List m_params; // List of parameters for EXECUTE [IMMEDIATE] +public: + + Lex_prepared_stmt() + :m_code(NULL) + { } + const Lex_ident_sys &name() const + { + return m_name; + } + uint param_count() const + { + return m_params.elements; + } + List ¶ms() + { + return m_params; + } + void set(const Lex_ident_sys_st &ident, Item *code, List *params) + { + DBUG_ASSERT(m_params.elements == 0); + m_name= ident; + m_code= code; + if (params) + m_params= *params; + } + bool params_fix_fields(THD *thd) + { + // Fix Items in the EXECUTE..USING list + List_iterator_fast param_it(m_params); + while (Item *param= param_it++) + { + if (param->fix_fields_if_needed_for_scalar(thd, 0)) + return true; + } + return false; + } + bool get_dynamic_sql_string(THD *thd, LEX_CSTRING *dst, String *buffer); + void lex_start() + { + m_params.empty(); + } +}; + + struct LEX: public Query_tables_list { SELECT_LEX_UNIT unit; /* most upper unit */ @@ -3254,12 +3314,7 @@ public: creating or last of tables referenced by foreign keys). */ TABLE_LIST *create_last_non_select_table; - /* Prepared statements SQL syntax:*/ - LEX_CSTRING prepared_stmt_name; /* Statement name (in all queries) */ - /* PREPARE or EXECUTE IMMEDIATE source expression */ - Item *prepared_stmt_code; - /* Names of user variables holding parameters (in EXECUTE) */ - List prepared_stmt_params; + Lex_prepared_stmt prepared_stmt; sp_head *sphead; sp_name *spname; bool sp_lex_in_use; // Keep track on lex usage in SPs for error handling @@ -3647,18 +3702,6 @@ public: bool sp_proc_stmt_statement_finalize_buf(THD *, const LEX_CSTRING &qbuf); bool sp_proc_stmt_statement_finalize(THD *, bool no_lookahead); - bool get_dynamic_sql_string(LEX_CSTRING *dst, String *buffer); - bool prepared_stmt_params_fix_fields(THD *thd) - { - // Fix Items in the EXECUTE..USING list - List_iterator_fast param_it(prepared_stmt_params); - while (Item *param= param_it++) - { - if (param->fix_fields_if_needed_for_scalar(thd, 0)) - return true; - } - return false; - } sp_variable *sp_param_init(LEX_CSTRING *name); bool sp_param_fill_definition(sp_variable *spvar); @@ -4403,6 +4446,11 @@ public: const Lex_ident_sys_st &name); bool stmt_uninstall_plugin_by_soname(const DDL_options_st &opt, const LEX_CSTRING &soname); + bool stmt_prepare_validate(const char *stmt_type); + bool stmt_prepare(const Lex_ident_sys_st &ident, Item *code); + bool stmt_execute(const Lex_ident_sys_st &ident, List *params); + bool stmt_execute_immediate(Item *code, List *params); + void stmt_deallocate_prepare(const Lex_ident_sys_st &ident); }; diff --git a/sql/sql_list.h b/sql/sql_list.h index 27827b42be5..60ec8ab4177 100644 --- a/sql/sql_list.h +++ b/sql/sql_list.h @@ -518,6 +518,12 @@ public: empty(); } T *elem(uint n) { return (T*) base_list::elem(n); } + // Create a new list with one element + static List *make(MEM_ROOT *mem_root, T *first) + { + List *res= new (mem_root) List; + return res == NULL || res->push_back(first, mem_root) ? NULL : res; + } }; diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 0cb9be44636..cbabdd4221b 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -190,7 +190,7 @@ public: void setup_set_params(); virtual Query_arena::Type type() const; virtual void cleanup_stmt(); - bool set_name(LEX_CSTRING *name); + bool set_name(const LEX_CSTRING *name); inline void close_cursor() { delete cursor; cursor= 0; } inline bool is_in_use() { return flags & (uint) IS_IN_USE; } inline bool is_sql_prepare() const { return flags & (uint) IS_SQL_PREPARE; } @@ -2664,7 +2664,7 @@ end: } /** - Get an SQL statement from an item in lex->prepared_stmt_code. + Get an SQL statement from an item in m_code. This function can return pointers to very different memory classes: - a static string "NULL", if the item returned NULL @@ -2689,13 +2689,15 @@ end: @retval true on error (out of memory) */ -bool LEX::get_dynamic_sql_string(LEX_CSTRING *dst, String *buffer) +bool Lex_prepared_stmt::get_dynamic_sql_string(THD *thd, + LEX_CSTRING *dst, + String *buffer) { - if (prepared_stmt_code->fix_fields_if_needed_for_scalar(thd, NULL)) + if (m_code->fix_fields_if_needed_for_scalar(thd, NULL)) return true; - const String *str= prepared_stmt_code->val_str(buffer); - if (prepared_stmt_code->null_value) + const String *str= m_code->val_str(buffer); + if (m_code->null_value) { /* Prepare source was NULL, so we need to set "str" to @@ -2776,7 +2778,7 @@ bool LEX::get_dynamic_sql_string(LEX_CSTRING *dst, String *buffer) void mysql_sql_stmt_prepare(THD *thd) { LEX *lex= thd->lex; - LEX_CSTRING *name= &lex->prepared_stmt_name; + const LEX_CSTRING *name= &lex->prepared_stmt.name(); Prepared_statement *stmt; LEX_CSTRING query; DBUG_ENTER("mysql_sql_stmt_prepare"); @@ -2801,7 +2803,7 @@ void mysql_sql_stmt_prepare(THD *thd) See comments in get_dynamic_sql_string(). */ StringBuffer<256> buffer; - if (lex->get_dynamic_sql_string(&query, &buffer) || + if (lex->prepared_stmt.get_dynamic_sql_string(thd, &query, &buffer) || ! (stmt= new Prepared_statement(thd))) { DBUG_VOID_RETURN; /* out of memory */ @@ -2864,7 +2866,7 @@ void mysql_sql_stmt_execute_immediate(THD *thd) LEX_CSTRING query; DBUG_ENTER("mysql_sql_stmt_execute_immediate"); - if (lex->prepared_stmt_params_fix_fields(thd)) + if (lex->prepared_stmt.params_fix_fields(thd)) DBUG_VOID_RETURN; /* @@ -2876,7 +2878,7 @@ void mysql_sql_stmt_execute_immediate(THD *thd) See comments in get_dynamic_sql_string(). */ StringBuffer<256> buffer; - if (lex->get_dynamic_sql_string(&query, &buffer) || + if (lex->prepared_stmt.get_dynamic_sql_string(thd, &query, &buffer) || !(stmt= new Prepared_statement(thd))) DBUG_VOID_RETURN; // out of memory @@ -3265,7 +3267,7 @@ void mysql_sql_stmt_execute(THD *thd) { LEX *lex= thd->lex; Prepared_statement *stmt; - LEX_CSTRING *name= &lex->prepared_stmt_name; + const LEX_CSTRING *name= &lex->prepared_stmt.name(); /* Query text for binary, general or slow log, if any of them is open */ String expanded_query; DBUG_ENTER("mysql_sql_stmt_execute"); @@ -3278,7 +3280,7 @@ void mysql_sql_stmt_execute(THD *thd) DBUG_VOID_RETURN; } - if (stmt->param_count != lex->prepared_stmt_params.elements) + if (stmt->param_count != lex->prepared_stmt.param_count()) { my_error(ER_WRONG_ARGUMENTS, MYF(0), "EXECUTE"); DBUG_VOID_RETURN; @@ -3286,7 +3288,7 @@ void mysql_sql_stmt_execute(THD *thd) DBUG_PRINT("info",("stmt: %p", stmt)); - if (lex->prepared_stmt_params_fix_fields(thd)) + if (lex->prepared_stmt.params_fix_fields(thd)) DBUG_VOID_RETURN; /* @@ -3506,7 +3508,7 @@ void mysqld_stmt_close(THD *thd, char *packet) void mysql_sql_stmt_close(THD *thd) { Prepared_statement* stmt; - LEX_CSTRING *name= &thd->lex->prepared_stmt_name; + const LEX_CSTRING *name= &thd->lex->prepared_stmt.name(); DBUG_PRINT("info", ("DEALLOCATE PREPARE: %.*s\n", (int) name->length, name->str)); @@ -3871,7 +3873,7 @@ void Prepared_statement::cleanup_stmt() } -bool Prepared_statement::set_name(LEX_CSTRING *name_arg) +bool Prepared_statement::set_name(const LEX_CSTRING *name_arg) { name.length= name_arg->length; name.str= (char*) memdup_root(mem_root, name_arg->str, name_arg->length); @@ -4120,7 +4122,7 @@ Prepared_statement::set_parameters(String *expanded_query, if (is_sql_ps) { /* SQL prepared statement */ - res= set_params_from_actual_params(this, thd->lex->prepared_stmt_params, + res= set_params_from_actual_params(this, thd->lex->prepared_stmt.params(), expanded_query); } else if (param_count) @@ -4849,7 +4851,7 @@ bool Prepared_statement::execute_immediate(const char *query, uint query_len) if (unlikely(prepare(query, query_len))) DBUG_RETURN(true); - if (param_count != thd->lex->prepared_stmt_params.elements) + if (param_count != thd->lex->prepared_stmt.param_count()) { my_error(ER_WRONG_ARGUMENTS, MYF(0), "EXECUTE"); deallocate_immediate(); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 811d464dba9..a59f0a025fb 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1856,7 +1856,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); %type literal insert_ident order_ident temporal_literal - simple_ident expr sum_expr in_sum_expr + simple_ident expr prepare_src 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 @@ -1897,6 +1897,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); expr_list opt_udf_expr_list udf_expr_list when_list when_list_opt_else ident_list ident_list_arg opt_expr_list decode_when_list_oracle + execute_using + execute_params %type sp_cursor_stmt_lex @@ -2054,7 +2056,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); select_var_list select_var_list_init help opt_extended_describe shutdown opt_format_json - prepare prepare_src execute deallocate + prepare execute deallocate statement 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 @@ -2306,9 +2308,7 @@ statement: deallocate: deallocate_or_drop PREPARE_SYM ident { - LEX *lex= thd->lex; - lex->sql_command= SQLCOM_DEALLOCATE_PREPARE; - lex->prepared_stmt_name= $3; + Lex->stmt_deallocate_prepare($3); } ; @@ -2320,14 +2320,8 @@ deallocate_or_drop: prepare: PREPARE_SYM ident FROM prepare_src { - LEX *lex= thd->lex; - if (unlikely(lex->table_or_sp_used())) - my_yyabort_error((ER_SUBQUERIES_NOT_SUPPORTED, MYF(0), - "PREPARE..FROM")); - if (Lex->check_main_unit_semantics()) + if (Lex->stmt_prepare($2, $4)) MYSQL_YYABORT; - lex->sql_command= SQLCOM_PREPARE; - lex->prepared_stmt_name= $2; } ; @@ -2335,63 +2329,48 @@ prepare_src: { Lex->expr_allows_subselect= false; } expr { - Lex->prepared_stmt_code= $2; Lex->expr_allows_subselect= true; + $$= $2; } ; execute: - EXECUTE_SYM ident + EXECUTE_SYM ident execute_using { - LEX *lex= thd->lex; - lex->sql_command= SQLCOM_EXECUTE; - lex->prepared_stmt_name= $2; - } - execute_using - { - if (Lex->check_main_unit_semantics()) + if (Lex->stmt_execute($2, $3)) MYSQL_YYABORT; } - | EXECUTE_SYM IMMEDIATE_SYM prepare_src + | EXECUTE_SYM IMMEDIATE_SYM prepare_src execute_using { - if (unlikely(Lex->table_or_sp_used())) - my_yyabort_error((ER_SUBQUERIES_NOT_SUPPORTED, MYF(0), - "EXECUTE IMMEDIATE")); - Lex->sql_command= SQLCOM_EXECUTE_IMMEDIATE; - } - execute_using - { - if (Lex->check_main_unit_semantics()) + if (Lex->stmt_execute_immediate($3, $4)) MYSQL_YYABORT; } ; execute_using: - /* nothing */ + /* nothing */ { $$= NULL; } | USING { Lex->expr_allows_subselect= false; } - execute_var_list + execute_params { - if (unlikely(Lex->table_or_sp_used())) - my_yyabort_error((ER_SUBQUERIES_NOT_SUPPORTED, MYF(0), - "EXECUTE..USING")); + $$= $3; Lex->expr_allows_subselect= true; } ; -execute_var_list: - execute_var_list ',' execute_var_ident - | execute_var_ident - ; - -execute_var_ident: +execute_params: expr_or_default { - if (unlikely(Lex->prepared_stmt_params.push_back($1, - thd->mem_root))) + if (unlikely(!($$= List::make(thd->mem_root, $1)))) + MYSQL_YYABORT; + } + | execute_params ',' expr_or_default + { + if (($$= $1)->push_back($3, thd->mem_root)) MYSQL_YYABORT; } ; + /* help */ help: @@ -11833,9 +11812,7 @@ opt_expr_list: expr_list: expr { - $$= new (thd->mem_root) List; - if (unlikely($$ == NULL) || - unlikely($$->push_back($1, thd->mem_root))) + if (unlikely(!($$= List::make(thd->mem_root, $1)))) MYSQL_YYABORT; } | expr_list ',' expr diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy index 333e1f76343..45726722c0d 100644 --- a/sql/sql_yacc_ora.yy +++ b/sql/sql_yacc_ora.yy @@ -1357,7 +1357,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); %type literal insert_ident order_ident temporal_literal - simple_ident expr sum_expr in_sum_expr + simple_ident expr prepare_src 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 @@ -1398,6 +1398,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); expr_list opt_udf_expr_list udf_expr_list when_list when_list_opt_else ident_list ident_list_arg opt_expr_list decode_when_list_oracle + execute_using + execute_params %type sp_cursor_stmt_lex @@ -1557,7 +1559,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); select_var_list select_var_list_init help opt_extended_describe shutdown opt_format_json - prepare prepare_src execute deallocate + prepare execute deallocate statement 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 @@ -1827,9 +1829,7 @@ statement: deallocate: deallocate_or_drop PREPARE_SYM ident { - LEX *lex= thd->lex; - lex->sql_command= SQLCOM_DEALLOCATE_PREPARE; - lex->prepared_stmt_name= $3; + Lex->stmt_deallocate_prepare($3); } ; @@ -1841,12 +1841,8 @@ deallocate_or_drop: prepare: PREPARE_SYM ident FROM prepare_src { - LEX *lex= thd->lex; - if (unlikely(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; + if (Lex->stmt_prepare($2, $4)) + MYSQL_YYABORT; } ; @@ -1854,57 +1850,48 @@ prepare_src: { Lex->expr_allows_subselect= false; } expr { - Lex->prepared_stmt_code= $2; Lex->expr_allows_subselect= true; + $$= $2; } ; execute: - EXECUTE_SYM ident + EXECUTE_SYM ident execute_using { - LEX *lex= thd->lex; - lex->sql_command= SQLCOM_EXECUTE; - lex->prepared_stmt_name= $2; + if (Lex->stmt_execute($2, $3)) + MYSQL_YYABORT; } - execute_using - {} - | EXECUTE_SYM IMMEDIATE_SYM prepare_src + | EXECUTE_SYM IMMEDIATE_SYM prepare_src execute_using { - if (unlikely(Lex->table_or_sp_used())) - my_yyabort_error((ER_SUBQUERIES_NOT_SUPPORTED, MYF(0), - "EXECUTE IMMEDIATE")); - Lex->sql_command= SQLCOM_EXECUTE_IMMEDIATE; + if (Lex->stmt_execute_immediate($3, $4)) + MYSQL_YYABORT; } - execute_using - {} ; execute_using: - /* nothing */ + /* nothing */ { $$= NULL; } | USING { Lex->expr_allows_subselect= false; } - execute_var_list + execute_params { - if (unlikely(Lex->table_or_sp_used())) - my_yyabort_error((ER_SUBQUERIES_NOT_SUPPORTED, MYF(0), - "EXECUTE..USING")); + $$= $3; Lex->expr_allows_subselect= true; } ; -execute_var_list: - execute_var_list ',' execute_var_ident - | execute_var_ident - ; - -execute_var_ident: +execute_params: expr_or_default { - if (unlikely(Lex->prepared_stmt_params.push_back($1, - thd->mem_root))) + if (unlikely(!($$= List::make(thd->mem_root, $1)))) + MYSQL_YYABORT; + } + | execute_params ',' expr_or_default + { + if (($$= $1)->push_back($3, thd->mem_root)) MYSQL_YYABORT; } ; + /* help */ help: @@ -11919,9 +11906,7 @@ opt_expr_list: expr_list: expr { - $$= new (thd->mem_root) List; - if (unlikely($$ == NULL) || - unlikely($$->push_back($1, thd->mem_root))) + if (unlikely(!($$= List::make(thd->mem_root, $1)))) MYSQL_YYABORT; } | expr_list ',' expr