Commit 47b7ca62 authored by Alexander Barkov's avatar Alexander Barkov

MDEV-18796 Synchronize PS grammar between sql_yacc.yy and sql_yacc_ora.yy

parent 3b47587f
......@@ -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
......
......@@ -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
......
......@@ -1338,11 +1338,10 @@ bool Protocol_text::store_time(MYSQL_TIME *tm, int decimals)
bool Protocol_text::send_out_parameters(List<Item_param> *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> item_param_it(*sp_params);
List_iterator_fast<Item> param_it(thd->lex->prepared_stmt_params);
List_iterator_fast<Item> param_it(thd->lex->prepared_stmt.params());
while (true)
{
......
......@@ -1214,7 +1214,7 @@ class Statement_map
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,
......
......@@ -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<Item> *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<Item> *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);
}
......@@ -169,6 +169,16 @@ class Lex_ident_sys: public Lex_ident_sys_st
{
((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<Item> 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<Item> &params()
{
return m_params;
}
void set(const Lex_ident_sys_st &ident, Item *code, List<Item> *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<Item> 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 @@ struct LEX: public Query_tables_list
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<Item> 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 @@ struct LEX: public Query_tables_list
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<Item> 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 @@ struct LEX: public Query_tables_list
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<Item> *params);
bool stmt_execute_immediate(Item *code, List<Item> *params);
void stmt_deallocate_prepare(const Lex_ident_sys_st &ident);
};
......
......@@ -518,6 +518,12 @@ template <class T> class List :public base_list
empty();
}
T *elem(uint n) { return (T*) base_list::elem(n); }
// Create a new list with one element
static List<T> *make(MEM_ROOT *mem_root, T *first)
{
List<T> *res= new (mem_root) List<T>;
return res == NULL || res->push_back(first, mem_root) ? NULL : res;
}
};
......
......@@ -190,7 +190,7 @@ class Prepared_statement: public Statement
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 @@ void mysqld_stmt_prepare(THD *thd, const char *packet, uint packet_length)
}
/**
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 @@ void mysqld_stmt_prepare(THD *thd, const char *packet, uint packet_length)
@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();
......
......@@ -1856,7 +1856,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%type <item>
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>
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
{
LEX *lex= thd->lex;
lex->sql_command= SQLCOM_EXECUTE;
lex->prepared_stmt_name= $2;
}
execute_using
EXECUTE_SYM ident 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<Item>::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<Item>;
if (unlikely($$ == NULL) ||
unlikely($$->push_back($1, thd->mem_root)))
if (unlikely(!($$= List<Item>::make(thd->mem_root, $1))))
MYSQL_YYABORT;
}
| expr_list ',' expr
......
......@@ -1357,7 +1357,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%type <item>
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>
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<Item>::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<Item>;
if (unlikely($$ == NULL) ||
unlikely($$->push_back($1, thd->mem_root)))
if (unlikely(!($$= List<Item>::make(thd->mem_root, $1))))
MYSQL_YYABORT;
}
| expr_list ',' expr
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment