← Back to team overview

maria-developers team mailing list archive

Re: MDEV-10596

 

Hi Jerome,

Thank you very much for your contribution!

On 09/12/2017 04:42 PM, jerome brauge wrote:
> Hi Alexander,
> In sql_mode=oracle, when a stored procedure varchar parameter is defined without size, you have chosen to translate it by VARCHAR(4000) (or CHAR(2000) for CHAR parameter).
> Oracle does not work like this. Size is inherited from the size of argument at runtime.
<cut>

Your patch looks very good.
I have minor improvement suggestions, to make the code more future-proof.

Please find my comments below:


<cut>
> diff --git a/mysql-test/suite/compat/oracle/t/sp-param.test b/mysql-test/suite/compat/oracle/t/sp-param.test
> index 2320331..be929cb 100644
> --- a/mysql-test/suite/compat/oracle/t/sp-param.test
> +++ b/mysql-test/suite/compat/oracle/t/sp-param.test
<cut>
> +--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;
> +/
> +drop procedure p1
> +/
> \ No newline at end of file

Please add a new line.


<cut>

> diff --git a/sql/sp_pcontext.cc b/sql/sp_pcontext.cc
> index d98f800..6531046 100644
> --- a/sql/sp_pcontext.cc
> +++ b/sql/sp_pcontext.cc
> @@ -95,7 +95,8 @@ sp_pcontext::sp_pcontext()
>    : Sql_alloc(),
>    m_max_var_index(0), m_max_cursor_index(0),
>    m_parent(NULL), m_pboundary(0),
> -  m_scope(REGULAR_SCOPE)
> +  m_scope(REGULAR_SCOPE),
> +  m_dyn_varchar_param(false)
>  {
>    init(0, 0, 0);
>  }
> diff --git a/sql/sp_pcontext.h b/sql/sp_pcontext.h
> index 215ebbe..776bd10 100644
> --- a/sql/sp_pcontext.h
> +++ b/sql/sp_pcontext.h
> @@ -692,6 +692,12 @@ class sp_pcontext : public Sql_alloc
>      return m_for_loop;
>    }
>  
> +  void set_dyn_char_param()
> +  { m_dyn_varchar_param= true; }
> +
> +  bool use_dyn_char_param() const
> +  { return m_dyn_varchar_param; }
> +
>  private:
>    /// Constructor for a tree node.
>    /// @param prev the parent parsing context
> @@ -786,6 +792,8 @@ class sp_pcontext : public Sql_alloc
>  
>    /// FOR LOOP characteristics
>    Lex_for_loop m_for_loop;
> +
> +  bool m_dyn_varchar_param;
>  }; // class sp_pcontext : public Sql_alloc
>  
>  
> diff --git a/sql/sp_rcontext.cc b/sql/sp_rcontext.cc
> index 8c598e8..3e449cd 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,34 @@ 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 (ctx->m_root_parsing_ctx->use_dyn_char_param() && 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;
> +    uint arg_max_length;
> +    /*
> +      If length is not specified for varchar arguments, set length to the
> +      maximum length of the real argument. Goals are:
> +      -- avoid to allocate too much inused 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.
> +    */
> +    while ((def= it++) && (arg= it_args++))
> +    {
> +      if (def->type_handler() == &type_handler_varchar && def->decimals)
> +      {
> +        arg_max_length= arg->max_char_length();
> +        def->length= arg_max_length > 0 ? arg_max_length : def->decimals;
> +        def->create_length_to_internal_length_string();
> +      }
> +    }
> +  }

I'd like to make the code more symmetric across data types.
It's very likely that we'll want some more
formal-param-to-actual-param-adjustment in the future.


Can you please move this code into a new virtual method in Type_handler?

Something like this:

virtual bool adjust_spparam_type(Spvar_definition *def, Item *from) const
{
  return false;
}

and override it for VARCHAR:

/*
   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 *param) const
{
  if (def->decimals)
  {
     arg_max_length= param->max_char_length();
     def->length= arg_max_length > 0 ? arg_max_length : def->decimals;
     def->create_length_to_internal_length_string();
  }
  return false;
}



Also, move the loop code into a new methods in sp_rcontext,
e.g. like this:


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;
  uint arg_max_length;
  while ((def= it++) && (arg= it_args++))
  {
    if (def->type_handler()->adjust_spparam_type(def, arg))
      true;
  }
  return false;
}


and call it from sp_rcontext::create() as follows:

  if (adjust_formal_params_to_actual_params(thd, args))
    return NULL.

No needs to test for ctx->m_root_parsing_ctx->use_dyn_char_param().
Just do it always unconditionally.

Please also remove:

sp_rcontext::set_dyn_char_param()
sp_rcontext::use_dyn_char_param()
sp_rcontext::m_dyn_varchar_param;

Btw, why did you add it?
Is it to safe on populating List<Item> ?

If so, then we can just pass arguments as follows;

sp_rcontext::create(THD *thd,
                    const sp_pcontext *root_parsing_ctx,
                    Field *return_value_fld,
                    bool resolve_type_refs)
                    bool resolve_type_refs,
                    Item **args, uint arg_count);

and:


bool sp_rcontext::adjust_formal_params_to_actual_params(THD *thd,
                                         List<Spvar_definition>
&field_def_lst,
                                         Item **args,
                                         uint arg_count);


<cut>

> diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy
> index 97a55df..8e8a016 100644
> --- a/sql/sql_yacc_ora.yy
> +++ b/sql/sql_yacc_ora.yy
<cut>
> @@ -6565,17 +6567,20 @@ 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");
> +                         Lex->spcont->set_dyn_char_param();
> +                       }
> +        | field_length { $$.set($1, NULL); }

Please follow our coding style for *.yy.
A multi-line code block corresponding to the "empty" rule should
go under the comment "/* empty */", like this:



opt_field_length_default_sp_param_varchar:
          /* empty */
          {
            $$.set("4000", "4000");
            Lex->spcont->set_dyn_char_param();
          }
        | field_length { $$.set($1, NULL); }


But if we don't need set_dyn_char_param(),
the above will be simplified to:

opt_field_length_default_sp_param_varchar:
          /* empty */   { $$.set("4000", "4000"); }
        | field_length  { $$.set($1, NULL); }

It's ok to put single-line code blocks to the right.


>  
>  opt_field_length_default_sp_param_char:
> -          /* empty */  { $$= (char*) "2000"; }
> -        | field_length { $$= $1; }
> +          /* empty */  {
> +                         $$.set("2000", "2000");
> +                         Lex->spcont->set_dyn_char_param();
> +                       }
> +        | field_length { $$.set($1, NULL); }

Same here.

>  
>  opt_precision:
>            /* empty */    { $$.set(0, 0); }

Thanks!


Follow ups

References