diff options
author | halfspawn <j.brauge@qualiac.com> | 2017-09-27 16:49:40 +0200 |
---|---|---|
committer | halfspawn <j.brauge@qualiac.com> | 2017-09-27 16:49:40 +0200 |
commit | f44d5de689cb62fc96676571f72df050a1760b20 (patch) | |
tree | 503363893180b8e67b0ce33c8ce7f1b8e1cc0c88 | |
parent | d387bc89ede0adf00d5940ee89004a412aebdf46 (diff) | |
download | mariadb-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.result | 86 | ||||
-rw-r--r-- | mysql-test/suite/compat/oracle/t/sp-param.test | 82 | ||||
-rw-r--r-- | sql/sp_head.cc | 19 | ||||
-rw-r--r-- | sql/sp_head.h | 2 | ||||
-rw-r--r-- | sql/sp_rcontext.cc | 25 | ||||
-rw-r--r-- | sql/sp_rcontext.h | 6 | ||||
-rw-r--r-- | sql/sql_type.cc | 23 | ||||
-rw-r--r-- | sql/sql_type.h | 6 | ||||
-rw-r--r-- | sql/sql_yacc_ora.yy | 23 |
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); } |