1
0
mirror of https://github.com/MariaDB/server.git synced 2025-07-30 16:24:05 +03:00

MDEV-13919 sql_mode=ORACLE: Derive length of VARCHAR SP parameters with no length from actual parameters

This commit is contained in:
halfspawn
2017-09-27 16:49:40 +02:00
parent d387bc89ed
commit f44d5de689
9 changed files with 251 additions and 21 deletions

View File

@ -130,3 +130,89 @@ t1 CREATE TABLE "t1" (
) )
DROP TABLE t1; DROP TABLE t1;
DROP FUNCTION f1; 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
/

View File

@ -35,3 +35,85 @@ SET sql_mode=ORACLE;
--let type = RAW --let type = RAW
--let length = 4000 --let length = 4000
--source sp-param.inc --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
/

View File

@ -1439,7 +1439,8 @@ bool sp_head::check_execute_access(THD *thd) const
@retval NULL - error (access denided or EOM) @retval NULL - error (access denided or EOM)
@retval !NULL - success (the invoker has rights to all %TYPE tables) @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; bool has_column_type_refs= m_flags & HAS_COLUMN_TYPE_REFS;
#ifndef NO_EMBEDDED_ACCESS_CHECKS #ifndef NO_EMBEDDED_ACCESS_CHECKS
@ -1449,7 +1450,7 @@ sp_rcontext *sp_head::rcontext_create(THD *thd, Field *ret_value)
return NULL; return NULL;
#endif #endif
sp_rcontext *res= sp_rcontext::create(thd, m_pcont, ret_value, 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 #ifndef NO_EMBEDDED_ACCESS_CHECKS
if (has_column_type_refs) if (has_column_type_refs)
m_security_ctx.restore_security_context(thd, save_security_ctx); 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); thd->set_n_backup_active_arena(&call_arena, &backup_arena);
if (!(nctx= sp_rcontext::create(thd, m_pcont, NULL, 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; err_status= TRUE;
goto err_with_cleanup; goto err_with_cleanup;
@ -1637,6 +1638,7 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount,
MEM_ROOT call_mem_root; MEM_ROOT call_mem_root;
Query_arena call_arena(&call_mem_root, Query_arena::STMT_INITIALIZED_FOR_SP); Query_arena call_arena(&call_mem_root, Query_arena::STMT_INITIALIZED_FOR_SP);
Query_arena backup_arena; Query_arena backup_arena;
List<Item> largs;
DBUG_ENTER("sp_head::execute_function"); DBUG_ENTER("sp_head::execute_function");
DBUG_PRINT("info", ("function %s", m_name.str)); 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)); init_sql_alloc(&call_mem_root, MEM_ROOT_BLOCK_SIZE, 0, MYF(0));
thd->set_n_backup_active_arena(&call_arena, &backup_arena); 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); thd->restore_active_arena(&call_arena, &backup_arena);
err_status= TRUE; err_status= TRUE;
@ -1886,7 +1893,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
if (! octx) if (! octx)
{ {
/* Create a temporary old context. */ /* 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_PRINT("error", ("Could not create octx"));
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
@ -1901,7 +1908,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
thd->spcont->callers_arena= thd; 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. */ delete nctx; /* Delete nctx if it was init() that failed. */
thd->spcont= save_spcont; thd->spcont= save_spcont;

View File

@ -215,7 +215,7 @@ public:
m_sp_cache_version= version_arg; 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: private:
/** /**

View File

@ -63,7 +63,8 @@ sp_rcontext::~sp_rcontext()
sp_rcontext *sp_rcontext::create(THD *thd, sp_rcontext *sp_rcontext::create(THD *thd,
const sp_pcontext *root_parsing_ctx, const sp_pcontext *root_parsing_ctx,
Field *return_value_fld, 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, sp_rcontext *ctx= new (thd->mem_root) sp_rcontext(root_parsing_ctx,
return_value_fld, return_value_fld,
@ -75,6 +76,10 @@ sp_rcontext *sp_rcontext::create(THD *thd,
List<Spvar_definition> field_def_lst; List<Spvar_definition> field_def_lst;
ctx->m_root_parsing_ctx->retrieve_field_definitions(&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) || if (ctx->alloc_arrays(thd) ||
(resolve_type_refs && ctx->resolve_type_refs(thd, field_def_lst)) || (resolve_type_refs && ctx->resolve_type_refs(thd, field_def_lst)) ||
ctx->init_var_table(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) bool sp_rcontext::alloc_arrays(THD *thd)
{ {
{ {

View File

@ -71,7 +71,8 @@ public:
static sp_rcontext *create(THD *thd, static sp_rcontext *create(THD *thd,
const sp_pcontext *root_parsing_ctx, const sp_pcontext *root_parsing_ctx,
Field *return_value_fld, Field *return_value_fld,
bool resolve_type_refs); bool resolve_type_refs,
List<Item> *args);
~sp_rcontext(); ~sp_rcontext();
@ -344,6 +345,9 @@ private:
Qualified_column_ident *ref); Qualified_column_ident *ref);
bool resolve_table_rowtype_ref(THD *thd, Row_definition_list &defs, bool resolve_table_rowtype_ref(THD *thd, Row_definition_list &defs,
Table_ident *ref); 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. /// Create and initialize an Item-adapter (Item_field) for each SP-var field.
/// ///

View File

@ -2384,6 +2384,29 @@ Field *Type_handler_set::make_table_field(const LEX_CSTRING *name,
attr.collation); 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 uint32 Type_handler_decimal_result::max_display_length(const Item *item) const

View File

@ -65,6 +65,7 @@ class in_vector;
class Type_handler_hybrid_field_type; class Type_handler_hybrid_field_type;
class Sort_param; class Sort_param;
class Arg_comparator; class Arg_comparator;
class Spvar_definition;
struct st_value; struct st_value;
class Protocol; class Protocol;
class handler; class handler;
@ -688,6 +689,10 @@ public:
type_handler_adjusted_to_max_octet_length(uint max_octet_length, type_handler_adjusted_to_max_octet_length(uint max_octet_length,
CHARSET_INFO *cs) const CHARSET_INFO *cs) const
{ return this; } { return this; }
virtual bool adjust_spparam_type(Spvar_definition *def, Item *from) const
{
return false;
}
virtual ~Type_handler() {} virtual ~Type_handler() {}
/** /**
Determines MariaDB traditional data types that always present Determines MariaDB traditional data types that always present
@ -2523,6 +2528,7 @@ public:
const Record_addr &addr, const Record_addr &addr,
const Type_all_attributes &attr, const Type_all_attributes &attr,
TABLE *table) const; TABLE *table) const;
bool adjust_spparam_type(Spvar_definition *def, Item *from) const;
}; };

View File

@ -1054,8 +1054,6 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%type <const_simple_string> %type <const_simple_string>
field_length opt_field_length opt_field_length_default_1 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> %type <string>
text_string hex_or_bin_String opt_gconcat_separator 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_cast_type> cast_type cast_type_numeric cast_type_temporal
%type <Lex_length_and_dec> precision opt_precision float_options %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 %type <symbol> keyword keyword_sp
keyword_directly_assignable keyword_directly_assignable
@ -6549,9 +6549,11 @@ opt_field_length_default_1:
/* /*
In sql_mode=ORACLE, a VARCHAR with no length is used In sql_mode=ORACLE, real size of VARCHAR and CHAR with no length
in SP parameters and return values and it's translated to VARCHAR(4000), in SP parameters is fixed at runtime with the length of real args.
where 4000 is the maximum possible size for VARCHAR. 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. In MariaDB the limit for VARCHAR is 65535 bytes.
We could translate VARCHAR with no length to VARCHAR(65535), but 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 the maximum possible length in characters in case of mbmaxlen=4
(e.g. utf32, utf16, utf8mb4). However, we'll have character sets with (e.g. utf32, utf16, utf8mb4). However, we'll have character sets with
mbmaxlen=5 soon (e.g. gb18030). 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: opt_field_length_default_sp_param_varchar:
/* empty */ { $$= (char*) "4000"; } /* empty */ { $$.set("4000", "4000"); }
| field_length { $$= $1; } | field_length { $$.set($1, NULL); }
opt_field_length_default_sp_param_char: opt_field_length_default_sp_param_char:
/* empty */ { $$= (char*) "2000"; } /* empty */ { $$.set("2000", "2000"); }
| field_length { $$= $1; } | field_length { $$.set($1, NULL); }
opt_precision: opt_precision:
/* empty */ { $$.set(0, 0); } /* empty */ { $$.set(0, 0); }