mirror of
https://github.com/MariaDB/server.git
synced 2025-08-08 11:22:35 +03:00
MDEV-10654 add support IN, OUT, INOUT parameter qualifiers for stored functions
Problem: Currently stored function does not support IN/OUT/INOUT parameter qualifiers. This is needed for Oracle compatibility (sql_mode = ORACLE). Solution: Implemented parameter qualifier support to CREATE FUNCTION (reference: CREATE PROCEDURE) Implemented return by reference for OUT/INOUT parameters in execute_function() (reference: execute_procedure()) Files changed: sql/sql_yacc.yy: Added IN, OUT, INOUT parameter qualifiers for CREATE FUNCTION. sql/sp_head.cc: Added input and output parameter binding for IN/OUT/INOUT parameters in execute_function() so that OUT/INOUT can return by reference. sql/share/errmsg-utf8.txt: Added error message to restrict OUT/INOUT parameters while function being called from SQL query. mysql-test/suite/compat/oracle/t/sp-inout.test: Added test cases mysql-test/suite/compat/oracle/r/sp-inout.result: Added test results Reviewed-by: iqbal@hasprime.com
This commit is contained in:
committed by
Alexander Barkov
parent
5595ed9d9f
commit
4572dc23f7
2571
mysql-test/suite/compat/oracle/r/sp-inout.result
Normal file
2571
mysql-test/suite/compat/oracle/r/sp-inout.result
Normal file
File diff suppressed because it is too large
Load Diff
2497
mysql-test/suite/compat/oracle/t/sp-inout.test
Normal file
2497
mysql-test/suite/compat/oracle/t/sp-inout.test
Normal file
File diff suppressed because it is too large
Load Diff
@@ -8915,3 +8915,5 @@ ER_PROVIDER_NOT_LOADED
|
||||
eng "MariaDB tried to use the %s, but its provider plugin is not loaded"
|
||||
ER_JSON_HISTOGRAM_PARSE_FAILED
|
||||
eng "Failed to parse histogram for table %s.%s: %s at offset %d."
|
||||
ER_SF_OUT_INOUT_ARG_NOT_ALLOWED
|
||||
eng "OUT or INOUT argument %d for function %s is not allowed here"
|
||||
|
195
sql/sp_head.cc
195
sql/sp_head.cc
@@ -2079,7 +2079,8 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount,
|
||||
/* Arguments must be fixed in Item_func_sp::fix_fields */
|
||||
DBUG_ASSERT(argp[arg_no]->fixed());
|
||||
|
||||
if ((err_status= (*func_ctx)->set_parameter(thd, arg_no, &(argp[arg_no]))))
|
||||
err_status= bind_input_param(thd, argp[arg_no], arg_no, *func_ctx, TRUE);
|
||||
if (err_status)
|
||||
goto err_with_cleanup;
|
||||
}
|
||||
|
||||
@@ -2202,6 +2203,19 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount,
|
||||
my_error(ER_SP_NORETURNEND, MYF(0), m_name.str);
|
||||
err_status= TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
Copy back all OUT or INOUT values to the previous frame, or
|
||||
set global user variables
|
||||
*/
|
||||
for (arg_no= 0; arg_no < argcount; arg_no++)
|
||||
{
|
||||
err_status= bind_output_param(thd, argp[arg_no], arg_no, octx, *func_ctx);
|
||||
if (err_status)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
||||
@@ -2324,50 +2338,9 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
|
||||
if (!arg_item)
|
||||
break;
|
||||
|
||||
sp_variable *spvar= m_pcont->find_variable(i);
|
||||
|
||||
if (!spvar)
|
||||
continue;
|
||||
|
||||
if (spvar->mode != sp_variable::MODE_IN)
|
||||
{
|
||||
Settable_routine_parameter *srp=
|
||||
arg_item->get_settable_routine_parameter();
|
||||
|
||||
if (!srp)
|
||||
{
|
||||
my_error(ER_SP_NOT_VAR_ARG, MYF(0), i+1, ErrConvDQName(this).ptr());
|
||||
err_status= TRUE;
|
||||
break;
|
||||
}
|
||||
|
||||
srp->set_required_privilege(spvar->mode == sp_variable::MODE_INOUT);
|
||||
}
|
||||
|
||||
if (spvar->mode == sp_variable::MODE_OUT)
|
||||
{
|
||||
Item_null *null_item= new (thd->mem_root) Item_null(thd);
|
||||
Item *tmp_item= null_item;
|
||||
|
||||
if (!null_item ||
|
||||
nctx->set_parameter(thd, i, &tmp_item))
|
||||
{
|
||||
DBUG_PRINT("error", ("set variable failed"));
|
||||
err_status= TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (nctx->set_parameter(thd, i, it_args.ref()))
|
||||
{
|
||||
DBUG_PRINT("error", ("set variable 2 failed"));
|
||||
err_status= TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
TRANSACT_TRACKER(add_trx_state_from_thd(thd));
|
||||
err_status= bind_input_param(thd, arg_item, i, nctx, FALSE);
|
||||
if (err_status)
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2477,31 +2450,9 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
|
||||
if (!arg_item)
|
||||
break;
|
||||
|
||||
sp_variable *spvar= m_pcont->find_variable(i);
|
||||
|
||||
if (spvar->mode == sp_variable::MODE_IN)
|
||||
continue;
|
||||
|
||||
Settable_routine_parameter *srp=
|
||||
arg_item->get_settable_routine_parameter();
|
||||
|
||||
DBUG_ASSERT(srp);
|
||||
|
||||
if (srp->set_value(thd, octx, nctx->get_variable_addr(i)))
|
||||
{
|
||||
DBUG_PRINT("error", ("set value failed"));
|
||||
err_status= TRUE;
|
||||
err_status= bind_output_param(thd, arg_item, i, octx, nctx);
|
||||
if (err_status)
|
||||
break;
|
||||
}
|
||||
|
||||
Send_field *out_param_info= new (thd->mem_root) Send_field(thd, nctx->get_parameter(i));
|
||||
out_param_info->db_name= m_db;
|
||||
out_param_info->table_name= m_name;
|
||||
out_param_info->org_table_name= m_name;
|
||||
out_param_info->col_name= spvar->name;
|
||||
out_param_info->org_col_name= spvar->name;
|
||||
|
||||
srp->set_out_param_info(out_param_info);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2532,6 +2483,112 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
|
||||
DBUG_RETURN(err_status);
|
||||
}
|
||||
|
||||
bool
|
||||
sp_head::bind_input_param(THD *thd,
|
||||
Item *arg_item,
|
||||
uint arg_no,
|
||||
sp_rcontext *nctx,
|
||||
bool is_function)
|
||||
{
|
||||
DBUG_ENTER("sp_head::bind_input_param");
|
||||
|
||||
sp_variable *spvar= m_pcont->find_variable(arg_no);
|
||||
if (!spvar)
|
||||
DBUG_RETURN(FALSE);
|
||||
|
||||
if (spvar->mode != sp_variable::MODE_IN)
|
||||
{
|
||||
Settable_routine_parameter *srp=
|
||||
arg_item->get_settable_routine_parameter();
|
||||
|
||||
if (!srp)
|
||||
{
|
||||
my_error(ER_SP_NOT_VAR_ARG, MYF(0), arg_no+1, ErrConvDQName(this).ptr());
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
|
||||
if (is_function)
|
||||
{
|
||||
/*
|
||||
Check if the function is called from SELECT/INSERT/UPDATE/DELETE query
|
||||
and parameter is OUT or INOUT.
|
||||
If yes, it is an invalid call - throw error.
|
||||
*/
|
||||
if (thd->lex->sql_command == SQLCOM_SELECT ||
|
||||
thd->lex->sql_command == SQLCOM_INSERT ||
|
||||
thd->lex->sql_command == SQLCOM_INSERT_SELECT ||
|
||||
thd->lex->sql_command == SQLCOM_UPDATE ||
|
||||
thd->lex->sql_command == SQLCOM_DELETE)
|
||||
{
|
||||
my_error(ER_SF_OUT_INOUT_ARG_NOT_ALLOWED, MYF(0), arg_no+1, m_name.str);
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
srp->set_required_privilege(spvar->mode == sp_variable::MODE_INOUT);
|
||||
}
|
||||
|
||||
if (spvar->mode == sp_variable::MODE_OUT)
|
||||
{
|
||||
Item_null *null_item= new (thd->mem_root) Item_null(thd);
|
||||
Item *tmp_item= null_item;
|
||||
|
||||
if (!null_item ||
|
||||
nctx->set_parameter(thd, arg_no, &tmp_item))
|
||||
{
|
||||
DBUG_PRINT("error", ("set variable failed"));
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (nctx->set_parameter(thd, arg_no, &arg_item))
|
||||
{
|
||||
DBUG_PRINT("error", ("set variable 2 failed"));
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
TRANSACT_TRACKER(add_trx_state_from_thd(thd));
|
||||
|
||||
DBUG_RETURN(FALSE);
|
||||
}
|
||||
|
||||
bool
|
||||
sp_head::bind_output_param(THD *thd,
|
||||
Item *arg_item,
|
||||
uint arg_no,
|
||||
sp_rcontext *octx,
|
||||
sp_rcontext *nctx)
|
||||
{
|
||||
DBUG_ENTER("sp_head::bind_output_param");
|
||||
|
||||
sp_variable *spvar= m_pcont->find_variable(arg_no);
|
||||
if (spvar->mode == sp_variable::MODE_IN)
|
||||
DBUG_RETURN(FALSE);
|
||||
|
||||
Settable_routine_parameter *srp=
|
||||
arg_item->get_settable_routine_parameter();
|
||||
|
||||
DBUG_ASSERT(srp);
|
||||
|
||||
if (srp->set_value(thd, octx, nctx->get_variable_addr(arg_no)))
|
||||
{
|
||||
DBUG_PRINT("error", ("set value failed"));
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
|
||||
Send_field *out_param_info= new (thd->mem_root) Send_field(thd, nctx->get_parameter(arg_no));
|
||||
out_param_info->db_name= m_db;
|
||||
out_param_info->table_name= m_name;
|
||||
out_param_info->org_table_name= m_name;
|
||||
out_param_info->col_name= spvar->name;
|
||||
out_param_info->org_col_name= spvar->name;
|
||||
|
||||
srp->set_out_param_info(out_param_info);
|
||||
|
||||
DBUG_RETURN(FALSE);
|
||||
}
|
||||
|
||||
/**
|
||||
Reset lex during parsing, before we parse a sub statement.
|
||||
|
@@ -505,6 +505,18 @@ private:
|
||||
sp_assignment_lex *param_lex,
|
||||
Item_args *parameters);
|
||||
|
||||
bool bind_input_param(THD *thd,
|
||||
Item *arg_item,
|
||||
uint arg_no,
|
||||
sp_rcontext *nctx,
|
||||
bool is_function);
|
||||
|
||||
bool bind_output_param(THD *thd,
|
||||
Item *arg_item,
|
||||
uint arg_no,
|
||||
sp_rcontext *octx,
|
||||
sp_rcontext *nctx);
|
||||
|
||||
public:
|
||||
/**
|
||||
Generate a code for an "OPEN cursor" statement.
|
||||
|
@@ -3063,20 +3063,6 @@ sp_param_name:
|
||||
}
|
||||
;
|
||||
|
||||
sp_param_name_and_type:
|
||||
sp_param_name field_type
|
||||
{
|
||||
if (unlikely(Lex->sp_param_fill_definition($$= $1, $2)))
|
||||
MYSQL_YYABORT;
|
||||
}
|
||||
| sp_param_name ROW_SYM row_type_body
|
||||
{
|
||||
if (unlikely(Lex->sphead->spvar_fill_row(thd, $$= $1, $3)))
|
||||
MYSQL_YYABORT;
|
||||
}
|
||||
| sp_param_name_and_type_anchored
|
||||
;
|
||||
|
||||
/* Stored PROCEDURE parameter declaration list */
|
||||
sp_pdparam_list:
|
||||
/* Empty */
|
||||
@@ -18039,6 +18025,20 @@ sp_decl_variable_list_anchored:
|
||||
}
|
||||
;
|
||||
|
||||
sp_param_name_and_type:
|
||||
sp_param_name field_type
|
||||
{
|
||||
if (unlikely(Lex->sp_param_fill_definition($$= $1, $2)))
|
||||
MYSQL_YYABORT;
|
||||
}
|
||||
| sp_param_name ROW_SYM row_type_body
|
||||
{
|
||||
if (unlikely(Lex->sphead->spvar_fill_row(thd, $$= $1, $3)))
|
||||
MYSQL_YYABORT;
|
||||
}
|
||||
| sp_param_name_and_type_anchored
|
||||
;
|
||||
|
||||
sp_param_name_and_type_anchored:
|
||||
sp_param_name TYPE_SYM OF_SYM ident '.' ident
|
||||
{
|
||||
@@ -18913,25 +18913,45 @@ sp_decl_variable_list_anchored:
|
||||
}
|
||||
;
|
||||
|
||||
sp_param_name_and_type:
|
||||
sp_param_name sp_opt_inout field_type
|
||||
{
|
||||
$1->mode= $2;
|
||||
if (unlikely(Lex->sp_param_fill_definition($$= $1, $3)))
|
||||
MYSQL_YYABORT;
|
||||
}
|
||||
| sp_param_name sp_opt_inout ROW_SYM row_type_body
|
||||
{
|
||||
$1->mode= $2;
|
||||
if (unlikely(Lex->sphead->spvar_fill_row(thd, $$= $1, $4)))
|
||||
MYSQL_YYABORT;
|
||||
}
|
||||
| sp_param_name_and_type_anchored
|
||||
;
|
||||
|
||||
sp_param_name_and_type_anchored:
|
||||
sp_param_name sp_decl_ident '.' ident PERCENT_ORACLE_SYM TYPE_SYM
|
||||
sp_param_name sp_opt_inout sp_decl_ident '.' ident PERCENT_ORACLE_SYM TYPE_SYM
|
||||
{
|
||||
if (unlikely(Lex->sphead->spvar_fill_type_reference(thd, $$= $1, $2, $4)))
|
||||
$1->mode= $2;
|
||||
if (unlikely(Lex->sphead->spvar_fill_type_reference(thd, $$= $1, $3, $5)))
|
||||
MYSQL_YYABORT;
|
||||
}
|
||||
| sp_param_name sp_decl_ident '.' ident '.' ident PERCENT_ORACLE_SYM TYPE_SYM
|
||||
| sp_param_name sp_opt_inout sp_decl_ident '.' ident '.' ident PERCENT_ORACLE_SYM TYPE_SYM
|
||||
{
|
||||
if (unlikely(Lex->sphead->spvar_fill_type_reference(thd, $$= $1, $2, $4, $6)))
|
||||
$1->mode= $2;
|
||||
if (unlikely(Lex->sphead->spvar_fill_type_reference(thd, $$= $1, $3, $5, $7)))
|
||||
MYSQL_YYABORT;
|
||||
}
|
||||
| sp_param_name sp_decl_ident PERCENT_ORACLE_SYM ROWTYPE_ORACLE_SYM
|
||||
| sp_param_name sp_opt_inout sp_decl_ident PERCENT_ORACLE_SYM ROWTYPE_ORACLE_SYM
|
||||
{
|
||||
if (unlikely(Lex->sphead->spvar_fill_table_rowtype_reference(thd, $$= $1, $2)))
|
||||
$1->mode= $2;
|
||||
if (unlikely(Lex->sphead->spvar_fill_table_rowtype_reference(thd, $$= $1, $3)))
|
||||
MYSQL_YYABORT;
|
||||
}
|
||||
| sp_param_name sp_decl_ident '.' ident PERCENT_ORACLE_SYM ROWTYPE_ORACLE_SYM
|
||||
| sp_param_name sp_opt_inout sp_decl_ident '.' ident PERCENT_ORACLE_SYM ROWTYPE_ORACLE_SYM
|
||||
{
|
||||
if (unlikely(Lex->sphead->spvar_fill_table_rowtype_reference(thd, $$= $1, $2, $4)))
|
||||
$1->mode= $2;
|
||||
if (unlikely(Lex->sphead->spvar_fill_table_rowtype_reference(thd, $$= $1, $3, $5)))
|
||||
MYSQL_YYABORT;
|
||||
}
|
||||
;
|
||||
|
Reference in New Issue
Block a user