Commit f44d5de6 authored by halfspawn's avatar halfspawn

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

MDEV-13919 sql_mode=ORACLE: Derive length of VARCHAR SP parameters with no length from actual parameters
parent d387bc89
......@@ -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 @@ class sp_head :private Query_arena,
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 @@ class sp_rcontext : public Sql_alloc
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 @@ class sp_rcontext : public Sql_alloc
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 @@ class Type_handler
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 @@ class Type_handler_varchar: public Type_handler_longstr
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); }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment