mirror of
https://github.com/MariaDB/server.git
synced 2025-01-29 02:05:57 +01:00
MDEV-13919 sql_mode=ORACLE: Derive length of VARCHAR SP parameters with no length from actual parameters
This commit is contained in:
parent
d387bc89ed
commit
f44d5de689
9 changed files with 251 additions and 21 deletions
|
@ -130,3 +130,89 @@ t1 CREATE TABLE "t1" (
|
|||
)
|
||||
DROP TABLE t1;
|
||||
DROP FUNCTION f1;
|
||||
|
||||
MDEV-13919 sql_mode=ORACLE: Derive length of VARCHAR SP parameters with no length from actual parameters
|
||||
|
||||
set sql_mode= 'oracle,strict_trans_tables';
|
||||
CREATE OR REPLACE PROCEDURE p1(pinout INOUT varchar, pin IN varchar)
|
||||
AS
|
||||
BEGIN
|
||||
pinout:=pin;
|
||||
END;
|
||||
/
|
||||
call p1(@w,'0123456789')
|
||||
/
|
||||
declare w varchar(10);
|
||||
begin
|
||||
call p1(w,'0123456789');
|
||||
end;
|
||||
/
|
||||
declare w varchar(5);
|
||||
begin
|
||||
call p1(w,'0123456789');
|
||||
end;
|
||||
/
|
||||
ERROR 22001: Data too long for column 'pinout' at row 1
|
||||
declare w varchar(20);
|
||||
begin
|
||||
w:='aaa';
|
||||
call p1(w,'0123456789');
|
||||
end;
|
||||
/
|
||||
declare w varchar(8);
|
||||
begin
|
||||
w:='aaa';
|
||||
call p1(w,'0123456789');
|
||||
end;
|
||||
/
|
||||
ERROR 22001: Data too long for column 'pinout' at row 1
|
||||
declare str varchar(6000);
|
||||
pout varchar(6000);
|
||||
begin
|
||||
str:=lpad('x',6000,'y');
|
||||
call p1(pout,str);
|
||||
select length(pout);
|
||||
end;
|
||||
/
|
||||
length(pout)
|
||||
6000
|
||||
declare str varchar(6000);
|
||||
pout varchar(4000);
|
||||
begin
|
||||
str:=lpad('x',6000,'y');
|
||||
call p1(pout,str);
|
||||
select length(pout);
|
||||
end;
|
||||
/
|
||||
ERROR 22001: Data too long for column 'pinout' at row 1
|
||||
declare str varchar(40000);
|
||||
pout varchar(60000);
|
||||
begin
|
||||
str:=lpad('x',40000,'y');
|
||||
call p1(pout,str);
|
||||
select length(pout);
|
||||
end;
|
||||
/
|
||||
length(pout)
|
||||
40000
|
||||
declare str text(80000);
|
||||
pout text(80000);
|
||||
begin
|
||||
str:=lpad('x',80000,'y');
|
||||
call p1(pout,str);
|
||||
select length(pout);
|
||||
end;
|
||||
/
|
||||
ERROR 22001: Data too long for column 'pin' at row 1
|
||||
declare str text(80000);
|
||||
pout text(80000);
|
||||
begin
|
||||
str:=lpad('x',60000,'y');
|
||||
call p1(pout,str);
|
||||
select length(pout);
|
||||
end;
|
||||
/
|
||||
length(pout)
|
||||
60000
|
||||
drop procedure p1
|
||||
/
|
||||
|
|
|
@ -35,3 +35,85 @@ SET sql_mode=ORACLE;
|
|||
--let type = RAW
|
||||
--let length = 4000
|
||||
--source sp-param.inc
|
||||
|
||||
--echo
|
||||
--echo MDEV-13919 sql_mode=ORACLE: Derive length of VARCHAR SP parameters with no length from actual parameters
|
||||
--echo
|
||||
set sql_mode= 'oracle,strict_trans_tables';
|
||||
delimiter /;
|
||||
CREATE OR REPLACE PROCEDURE p1(pinout INOUT varchar, pin IN varchar)
|
||||
AS
|
||||
BEGIN
|
||||
pinout:=pin;
|
||||
END;
|
||||
/
|
||||
call p1(@w,'0123456789')
|
||||
/
|
||||
declare w varchar(10);
|
||||
begin
|
||||
call p1(w,'0123456789');
|
||||
end;
|
||||
/
|
||||
--error ER_DATA_TOO_LONG
|
||||
declare w varchar(5);
|
||||
begin
|
||||
call p1(w,'0123456789');
|
||||
end;
|
||||
/
|
||||
declare w varchar(20);
|
||||
begin
|
||||
w:='aaa';
|
||||
call p1(w,'0123456789');
|
||||
end;
|
||||
/
|
||||
--error ER_DATA_TOO_LONG
|
||||
declare w varchar(8);
|
||||
begin
|
||||
w:='aaa';
|
||||
call p1(w,'0123456789');
|
||||
end;
|
||||
/
|
||||
declare str varchar(6000);
|
||||
pout varchar(6000);
|
||||
begin
|
||||
str:=lpad('x',6000,'y');
|
||||
call p1(pout,str);
|
||||
select length(pout);
|
||||
end;
|
||||
/
|
||||
--error ER_DATA_TOO_LONG
|
||||
declare str varchar(6000);
|
||||
pout varchar(4000);
|
||||
begin
|
||||
str:=lpad('x',6000,'y');
|
||||
call p1(pout,str);
|
||||
select length(pout);
|
||||
end;
|
||||
/
|
||||
declare str varchar(40000);
|
||||
pout varchar(60000);
|
||||
begin
|
||||
str:=lpad('x',40000,'y');
|
||||
call p1(pout,str);
|
||||
select length(pout);
|
||||
end;
|
||||
/
|
||||
--error ER_DATA_TOO_LONG
|
||||
declare str text(80000);
|
||||
pout text(80000);
|
||||
begin
|
||||
str:=lpad('x',80000,'y');
|
||||
call p1(pout,str);
|
||||
select length(pout);
|
||||
end;
|
||||
/
|
||||
declare str text(80000);
|
||||
pout text(80000);
|
||||
begin
|
||||
str:=lpad('x',60000,'y');
|
||||
call p1(pout,str);
|
||||
select length(pout);
|
||||
end;
|
||||
/
|
||||
drop procedure p1
|
||||
/
|
||||
|
|
|
@ -1439,7 +1439,8 @@ bool sp_head::check_execute_access(THD *thd) const
|
|||
@retval NULL - error (access denided or EOM)
|
||||
@retval !NULL - success (the invoker has rights to all %TYPE tables)
|
||||
*/
|
||||
sp_rcontext *sp_head::rcontext_create(THD *thd, Field *ret_value)
|
||||
sp_rcontext *sp_head::rcontext_create(THD *thd, Field *ret_value,
|
||||
List<Item> *args)
|
||||
{
|
||||
bool has_column_type_refs= m_flags & HAS_COLUMN_TYPE_REFS;
|
||||
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
||||
|
@ -1449,7 +1450,7 @@ sp_rcontext *sp_head::rcontext_create(THD *thd, Field *ret_value)
|
|||
return NULL;
|
||||
#endif
|
||||
sp_rcontext *res= sp_rcontext::create(thd, m_pcont, ret_value,
|
||||
has_column_type_refs);
|
||||
has_column_type_refs, args);
|
||||
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
||||
if (has_column_type_refs)
|
||||
m_security_ctx.restore_security_context(thd, save_security_ctx);
|
||||
|
@ -1556,7 +1557,7 @@ sp_head::execute_trigger(THD *thd,
|
|||
thd->set_n_backup_active_arena(&call_arena, &backup_arena);
|
||||
|
||||
if (!(nctx= sp_rcontext::create(thd, m_pcont, NULL,
|
||||
m_flags & HAS_COLUMN_TYPE_REFS)))
|
||||
m_flags & HAS_COLUMN_TYPE_REFS, NULL)))
|
||||
{
|
||||
err_status= TRUE;
|
||||
goto err_with_cleanup;
|
||||
|
@ -1637,6 +1638,7 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount,
|
|||
MEM_ROOT call_mem_root;
|
||||
Query_arena call_arena(&call_mem_root, Query_arena::STMT_INITIALIZED_FOR_SP);
|
||||
Query_arena backup_arena;
|
||||
List<Item> largs;
|
||||
DBUG_ENTER("sp_head::execute_function");
|
||||
DBUG_PRINT("info", ("function %s", m_name.str));
|
||||
|
||||
|
@ -1671,7 +1673,12 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount,
|
|||
init_sql_alloc(&call_mem_root, MEM_ROOT_BLOCK_SIZE, 0, MYF(0));
|
||||
thd->set_n_backup_active_arena(&call_arena, &backup_arena);
|
||||
|
||||
if (!(nctx= rcontext_create(thd, return_value_fld)))
|
||||
for (uint i= 0 ; i < argcount ; i++)
|
||||
{
|
||||
largs.push_back(argp[i]);
|
||||
}
|
||||
|
||||
if (!(nctx= rcontext_create(thd, return_value_fld, &largs)))
|
||||
{
|
||||
thd->restore_active_arena(&call_arena, &backup_arena);
|
||||
err_status= TRUE;
|
||||
|
@ -1886,7 +1893,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
|
|||
if (! octx)
|
||||
{
|
||||
/* Create a temporary old context. */
|
||||
if (!(octx= rcontext_create(thd, NULL)))
|
||||
if (!(octx= rcontext_create(thd, NULL, args)))
|
||||
{
|
||||
DBUG_PRINT("error", ("Could not create octx"));
|
||||
DBUG_RETURN(TRUE);
|
||||
|
@ -1901,7 +1908,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
|
|||
thd->spcont->callers_arena= thd;
|
||||
}
|
||||
|
||||
if (!(nctx= rcontext_create(thd, NULL)))
|
||||
if (!(nctx= rcontext_create(thd, NULL, args)))
|
||||
{
|
||||
delete nctx; /* Delete nctx if it was init() that failed. */
|
||||
thd->spcont= save_spcont;
|
||||
|
|
|
@ -215,7 +215,7 @@ public:
|
|||
m_sp_cache_version= version_arg;
|
||||
}
|
||||
|
||||
sp_rcontext *rcontext_create(THD *thd, Field *retval);
|
||||
sp_rcontext *rcontext_create(THD *thd, Field *retval, List<Item> *args);
|
||||
|
||||
private:
|
||||
/**
|
||||
|
|
|
@ -63,7 +63,8 @@ sp_rcontext::~sp_rcontext()
|
|||
sp_rcontext *sp_rcontext::create(THD *thd,
|
||||
const sp_pcontext *root_parsing_ctx,
|
||||
Field *return_value_fld,
|
||||
bool resolve_type_refs)
|
||||
bool resolve_type_refs,
|
||||
List<Item> *args)
|
||||
{
|
||||
sp_rcontext *ctx= new (thd->mem_root) sp_rcontext(root_parsing_ctx,
|
||||
return_value_fld,
|
||||
|
@ -75,6 +76,10 @@ sp_rcontext *sp_rcontext::create(THD *thd,
|
|||
List<Spvar_definition> field_def_lst;
|
||||
ctx->m_root_parsing_ctx->retrieve_field_definitions(&field_def_lst);
|
||||
|
||||
if (args &&
|
||||
ctx->adjust_formal_params_to_actual_params(thd, field_def_lst, args))
|
||||
return NULL;
|
||||
|
||||
if (ctx->alloc_arrays(thd) ||
|
||||
(resolve_type_refs && ctx->resolve_type_refs(thd, field_def_lst)) ||
|
||||
ctx->init_var_table(thd, field_def_lst) ||
|
||||
|
@ -88,6 +93,24 @@ sp_rcontext *sp_rcontext::create(THD *thd,
|
|||
}
|
||||
|
||||
|
||||
bool sp_rcontext::adjust_formal_params_to_actual_params(THD *thd,
|
||||
List<Spvar_definition> &field_def_lst,
|
||||
List<Item> *args)
|
||||
{
|
||||
List_iterator<Spvar_definition> it(field_def_lst);
|
||||
List_iterator<Item> it_args(*args);
|
||||
DBUG_ASSERT(field_def_lst.elements >= args->elements );
|
||||
Spvar_definition *def;
|
||||
Item *arg;
|
||||
while ((def= it++) && (arg= it_args++))
|
||||
{
|
||||
if (def->type_handler()->adjust_spparam_type(def, arg))
|
||||
true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool sp_rcontext::alloc_arrays(THD *thd)
|
||||
{
|
||||
{
|
||||
|
|
|
@ -71,7 +71,8 @@ public:
|
|||
static sp_rcontext *create(THD *thd,
|
||||
const sp_pcontext *root_parsing_ctx,
|
||||
Field *return_value_fld,
|
||||
bool resolve_type_refs);
|
||||
bool resolve_type_refs,
|
||||
List<Item> *args);
|
||||
|
||||
~sp_rcontext();
|
||||
|
||||
|
@ -344,6 +345,9 @@ private:
|
|||
Qualified_column_ident *ref);
|
||||
bool resolve_table_rowtype_ref(THD *thd, Row_definition_list &defs,
|
||||
Table_ident *ref);
|
||||
bool adjust_formal_params_to_actual_params(THD *thd,
|
||||
List<Spvar_definition> &field_def_lst,
|
||||
List<Item> *args);
|
||||
|
||||
/// Create and initialize an Item-adapter (Item_field) for each SP-var field.
|
||||
///
|
||||
|
|
|
@ -2384,6 +2384,29 @@ Field *Type_handler_set::make_table_field(const LEX_CSTRING *name,
|
|||
attr.collation);
|
||||
}
|
||||
|
||||
/*
|
||||
If length is not specified for a varchar parameter, set length to the
|
||||
maximum length of the actual argument. Goals are:
|
||||
- avoid to allocate too much unused memory for m_var_table
|
||||
- allow length check inside the callee rather than during copy of
|
||||
returned values in output variables.
|
||||
- allow varchar parameter size greater than 4000
|
||||
Default length has been stored in "decimal" member during parse.
|
||||
*/
|
||||
bool Type_handler_varchar::adjust_spparam_type(Spvar_definition *def,
|
||||
Item *from) const
|
||||
{
|
||||
if (def->decimals)
|
||||
{
|
||||
uint def_max_char_length= MAX_FIELD_VARCHARLENGTH / def->charset->mbmaxlen;
|
||||
uint arg_max_length= from->max_char_length();
|
||||
set_if_smaller(arg_max_length, def_max_char_length);
|
||||
def->length= arg_max_length > 0 ? arg_max_length : def->decimals;
|
||||
def->create_length_to_internal_length_string();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
uint32 Type_handler_decimal_result::max_display_length(const Item *item) const
|
||||
|
|
|
@ -65,6 +65,7 @@ class in_vector;
|
|||
class Type_handler_hybrid_field_type;
|
||||
class Sort_param;
|
||||
class Arg_comparator;
|
||||
class Spvar_definition;
|
||||
struct st_value;
|
||||
class Protocol;
|
||||
class handler;
|
||||
|
@ -688,6 +689,10 @@ public:
|
|||
type_handler_adjusted_to_max_octet_length(uint max_octet_length,
|
||||
CHARSET_INFO *cs) const
|
||||
{ return this; }
|
||||
virtual bool adjust_spparam_type(Spvar_definition *def, Item *from) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
virtual ~Type_handler() {}
|
||||
/**
|
||||
Determines MariaDB traditional data types that always present
|
||||
|
@ -2523,6 +2528,7 @@ public:
|
|||
const Record_addr &addr,
|
||||
const Type_all_attributes &attr,
|
||||
TABLE *table) const;
|
||||
bool adjust_spparam_type(Spvar_definition *def, Item *from) const;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -1054,8 +1054,6 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
|
|||
|
||||
%type <const_simple_string>
|
||||
field_length opt_field_length opt_field_length_default_1
|
||||
opt_field_length_default_sp_param_varchar
|
||||
opt_field_length_default_sp_param_char
|
||||
|
||||
%type <string>
|
||||
text_string hex_or_bin_String opt_gconcat_separator
|
||||
|
@ -1219,6 +1217,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
|
|||
%type <Lex_cast_type> cast_type cast_type_numeric cast_type_temporal
|
||||
|
||||
%type <Lex_length_and_dec> precision opt_precision float_options
|
||||
opt_field_length_default_sp_param_varchar
|
||||
opt_field_length_default_sp_param_char
|
||||
|
||||
%type <symbol> keyword keyword_sp
|
||||
keyword_directly_assignable
|
||||
|
@ -6549,9 +6549,11 @@ opt_field_length_default_1:
|
|||
|
||||
|
||||
/*
|
||||
In sql_mode=ORACLE, a VARCHAR with no length is used
|
||||
in SP parameters and return values and it's translated to VARCHAR(4000),
|
||||
where 4000 is the maximum possible size for VARCHAR.
|
||||
In sql_mode=ORACLE, real size of VARCHAR and CHAR with no length
|
||||
in SP parameters is fixed at runtime with the length of real args.
|
||||
Let's translate VARCHAR to VARCHAR(4000) for return value.
|
||||
|
||||
Since Oracle 9, maximum size for VARCHAR in PL/SQL is 32767.
|
||||
|
||||
In MariaDB the limit for VARCHAR is 65535 bytes.
|
||||
We could translate VARCHAR with no length to VARCHAR(65535), but
|
||||
|
@ -6562,17 +6564,14 @@ opt_field_length_default_1:
|
|||
the maximum possible length in characters in case of mbmaxlen=4
|
||||
(e.g. utf32, utf16, utf8mb4). However, we'll have character sets with
|
||||
mbmaxlen=5 soon (e.g. gb18030).
|
||||
|
||||
Let's translate VARCHAR to VARCHAR(4000), which covert all possible Oracle
|
||||
values.
|
||||
*/
|
||||
opt_field_length_default_sp_param_varchar:
|
||||
/* empty */ { $$= (char*) "4000"; }
|
||||
| field_length { $$= $1; }
|
||||
/* empty */ { $$.set("4000", "4000"); }
|
||||
| field_length { $$.set($1, NULL); }
|
||||
|
||||
opt_field_length_default_sp_param_char:
|
||||
/* empty */ { $$= (char*) "2000"; }
|
||||
| field_length { $$= $1; }
|
||||
/* empty */ { $$.set("2000", "2000"); }
|
||||
| field_length { $$.set($1, NULL); }
|
||||
|
||||
opt_precision:
|
||||
/* empty */ { $$.set(0, 0); }
|
||||
|
|
Loading…
Add table
Reference in a new issue