summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhalfspawn <j.brauge@qualiac.com>2017-09-27 16:49:40 +0200
committerhalfspawn <j.brauge@qualiac.com>2017-09-27 16:49:40 +0200
commitf44d5de689cb62fc96676571f72df050a1760b20 (patch)
tree503363893180b8e67b0ce33c8ce7f1b8e1cc0c88
parentd387bc89ede0adf00d5940ee89004a412aebdf46 (diff)
downloadmariadb-git-f44d5de689cb62fc96676571f72df050a1760b20.tar.gz
MDEV-13919 sql_mode=ORACLE: Derive length of VARCHAR SP parameters with no length from actual parameters
-rw-r--r--mysql-test/suite/compat/oracle/r/sp-param.result86
-rw-r--r--mysql-test/suite/compat/oracle/t/sp-param.test82
-rw-r--r--sql/sp_head.cc19
-rw-r--r--sql/sp_head.h2
-rw-r--r--sql/sp_rcontext.cc25
-rw-r--r--sql/sp_rcontext.h6
-rw-r--r--sql/sql_type.cc23
-rw-r--r--sql/sql_type.h6
-rw-r--r--sql/sql_yacc_ora.yy23
9 files changed, 251 insertions, 21 deletions
diff --git a/mysql-test/suite/compat/oracle/r/sp-param.result b/mysql-test/suite/compat/oracle/r/sp-param.result
index 4f4585c4570..15e1516efdb 100644
--- a/mysql-test/suite/compat/oracle/r/sp-param.result
+++ b/mysql-test/suite/compat/oracle/r/sp-param.result
@@ -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
+/
diff --git a/mysql-test/suite/compat/oracle/t/sp-param.test b/mysql-test/suite/compat/oracle/t/sp-param.test
index 23203317f50..0a6d7dec0fd 100644
--- a/mysql-test/suite/compat/oracle/t/sp-param.test
+++ b/mysql-test/suite/compat/oracle/t/sp-param.test
@@ -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
+/
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index faf208f549f..72ce5d65a5d 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -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;
diff --git a/sql/sp_head.h b/sql/sp_head.h
index 207d41f74fd..2f4f21a76f4 100644
--- a/sql/sp_head.h
+++ b/sql/sp_head.h
@@ -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:
/**
diff --git a/sql/sp_rcontext.cc b/sql/sp_rcontext.cc
index a684265ee66..3413a76e8c7 100644
--- a/sql/sp_rcontext.cc
+++ b/sql/sp_rcontext.cc
@@ -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)
{
{
diff --git a/sql/sp_rcontext.h b/sql/sp_rcontext.h
index 98464518787..39635d47029 100644
--- a/sql/sp_rcontext.h
+++ b/sql/sp_rcontext.h
@@ -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.
///
diff --git a/sql/sql_type.cc b/sql/sql_type.cc
index d9b3d53465e..339d24ebeb6 100644
--- a/sql/sql_type.cc
+++ b/sql/sql_type.cc
@@ -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
diff --git a/sql/sql_type.h b/sql/sql_type.h
index 528ba005850..57737662c48 100644
--- a/sql/sql_type.h
+++ b/sql/sql_type.h
@@ -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;
};
diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy
index 053c2625da1..bc3302c49d3 100644
--- a/sql/sql_yacc_ora.yy
+++ b/sql/sql_yacc_ora.yy
@@ -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); }