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" eng "MariaDB tried to use the %s, but its provider plugin is not loaded"
ER_JSON_HISTOGRAM_PARSE_FAILED ER_JSON_HISTOGRAM_PARSE_FAILED
eng "Failed to parse histogram for table %s.%s: %s at offset %d." 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 */ /* Arguments must be fixed in Item_func_sp::fix_fields */
DBUG_ASSERT(argp[arg_no]->fixed()); 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; 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); my_error(ER_SP_NORETURNEND, MYF(0), m_name.str);
err_status= TRUE; 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 #ifndef NO_EMBEDDED_ACCESS_CHECKS
@@ -2324,52 +2338,11 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
if (!arg_item) if (!arg_item)
break; break;
sp_variable *spvar= m_pcont->find_variable(i); err_status= bind_input_param(thd, arg_item, i, nctx, FALSE);
if (err_status)
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; 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));
}
/* /*
Okay, got values for all arguments. Close tables that might be used by Okay, got values for all arguments. Close tables that might be used by
arguments evaluation. If arguments evaluation required prelocking mode, arguments evaluation. If arguments evaluation required prelocking mode,
@@ -2477,32 +2450,10 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
if (!arg_item) if (!arg_item)
break; break;
sp_variable *spvar= m_pcont->find_variable(i); err_status= bind_output_param(thd, arg_item, i, octx, nctx);
if (err_status)
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;
break; 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);
}
} }
#ifndef NO_EMBEDDED_ACCESS_CHECKS #ifndef NO_EMBEDDED_ACCESS_CHECKS
@@ -2532,6 +2483,112 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
DBUG_RETURN(err_status); 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. Reset lex during parsing, before we parse a sub statement.

View File

@@ -505,6 +505,18 @@ private:
sp_assignment_lex *param_lex, sp_assignment_lex *param_lex,
Item_args *parameters); 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: public:
/** /**
Generate a code for an "OPEN cursor" statement. 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 */ /* Stored PROCEDURE parameter declaration list */
sp_pdparam_list: sp_pdparam_list:
/* Empty */ /* 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_and_type_anchored:
sp_param_name TYPE_SYM OF_SYM ident '.' ident 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_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; 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; 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; 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; MYSQL_YYABORT;
} }
; ;