1
0
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:
ManoharKB
2021-09-25 22:20:22 +05:30
committed by Alexander Barkov
parent 5595ed9d9f
commit 4572dc23f7
6 changed files with 5250 additions and 91 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -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"

View File

@@ -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.

View File

@@ -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.

View File

@@ -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;
}
;