← Back to team overview

maria-developers team mailing list archive

Review: [MDEV-7062] parser refactoring: don't store field properties in LEX

 

Hi Sergei,


> Sergei Golubchik edited comment on MDEV-7062 at 11/10/14 11:40 AM:
> -------------------------------------------------------------------
>
> http://lists.askmonty.org/pipermail/commits/2014-November/006908.html
>
> {noformat}
> From: serg@xxxxxxxxxxx
> To: commits@xxxxxxxxxxx
> Subject: [Commits] 2c904b9: parser cleanup: don't store field properties
>           in LEX, use Create_field directly
> Date: Mon, 10 Nov 2014 10:26:48 +0100
> {noformat}
>


Please find my comments below:



[Commits] 2c904b9: parser cleanup: don't store field properties in LEX, use Create_field directly
serg at mariadb.org serg at mariadb.org
Mon Nov 10 11:26:48 EET 2014

    Previous message: [Commits] Rev 4344: MDEV-6179: dynamic columns functions/cast()/convert() doesn't play nice with CREATE/ALTER TABLE in lp:~maria-captains/maria/5.5
    Next message: [Commits] Rev 4345: MDEV-6862 "#error <my_config.h>" and third-party libraries in lp:~maria-captains/maria/5.5
    Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]

revision-id: 2c904b937b5ef7fbec60ebb9cb514a501c5bd8e4
parent(s): f59588b9112965849986c45fcd150d9abed74063
committer: Sergei Golubchik
branch nick: maria
timestamp: 2014-11-09 22:33:17 +0100
message:

parser cleanup: don't store field properties in LEX, use Create_field directly

length/dec/charset are still in LEX, because they're also used
for CAST and dynamic columns.

also
1. fix "MDEV-7041 COLLATION(CAST('a' AS CHAR BINARY)) returns a wrong result"
2. allow BINARY modifier in stored function RETURN clause
3. allow "COLLATION without CHARSET" in SP/SF (parameters, RETURN, DECLARE)
4. print correct variable name in error messages for stored routine parameters

A very nice clean up.

I only found it a little bit not easy to track that
all Lex and Create_field attributes get initialized and copied to each other properly.

I suggest to go a little bit further and get rid from Lex->dec, Lex->length and Lex->charset at all, and write the attributes directly
to the destination as soon as they get parsed. See a proposed addon
patch attached.



diff --git a/mysql-test/r/cast.result b/mysql-test/r/cast.result
index c81af13..8ad4568 100644
--- a/mysql-test/r/cast.result
+++ b/mysql-test/r/cast.result
@@ -796,3 +796,32 @@ DATE("foo")
 NULL
 Warnings:
 Warning	1292	Incorrect datetime value: 'foo'
+create table t1 (a int, b char(5) as (cast("a" as char(10) binary) + a) );
+show create table t1;
+Table	Create Table
+t1	CREATE TABLE `t1` (
+  `a` int(11) DEFAULT NULL,
+  `b` char(5) AS (cast("a" as char(10) binary) + a) VIRTUAL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+drop table t1;
+select collation(cast("a" as char(10) binary));
+collation(cast("a" as char(10) binary))
+latin1_bin
+select collation(cast("a" as char(10) charset utf8 binary));
+collation(cast("a" as char(10) charset utf8 binary))
+utf8_bin
+select collation(cast("a" as char(10) ascii binary));
+collation(cast("a" as char(10) ascii binary))
+latin1_bin
+select collation(cast("a" as char(10) unicode binary));
+collation(cast("a" as char(10) unicode binary))
> +ucs2_bin

UNICODE is an alias for UCS2, which is not always compiled.
Please move ucs2 related tests to ctype_ucs.test.

<skip>


diff --git a/mysql-test/t/cast.test b/mysql-test/t/cast.test
index b6c37ca..aa43ee6 100644
--- a/mysql-test/t/cast.test
+++ b/mysql-test/t/cast.test
@@ -456,3 +456,21 @@ SELECT CAST(TIME('10:20:30') AS DATE) + INTERVAL 1 DAY;
 SET SQL_MODE=ALLOW_INVALID_DATES;
 SELECT DATE("foo");

+#
+# CAST and field definition using same fields in LEX
+#
+create table t1 (a int, b char(5) as (cast("a" as char(10) binary) + a) );
+show create table t1;
+drop table t1;
+
+#
+# CAST (... BINARY)
+#
+select collation(cast("a" as char(10) binary));
+select collation(cast("a" as char(10) charset utf8 binary));
+select collation(cast("a" as char(10) ascii binary));
+select collation(cast("a" as char(10) unicode binary));
+select collation(cast("a" as char(10) binary charset utf8));
+select collation(cast("a" as char(10) binary ascii));
+select collation(cast("a" as char(10) binary unicode));

More UCS2.



You removed this code in field.cc:

-  if (fld_length != NULL)
-  {
-    errno= 0;
-    length= strtoul(fld_length, NULL, 10);
-    if ((errno != 0) || (length > MAX_FIELD_BLOBLENGTH))
-    {
-      my_error(ER_TOO_BIG_DISPLAYWIDTH, MYF(0), fld_name, MAX_FIELD_BLOBLENGTH);
-      DBUG_RETURN(TRUE);
-    }
-
-    if (length == 0)
-      fld_length= NULL; /* purecov: inspected */
-  }

and added this in sql_yacc.yy:

+  if (length)
+  {
+    int err;
+    last_field->length= (ulong)my_strtoll10(length, NULL, &err);
+    if (err)
+      last_field->length= ~0UL; // safety
+  }
+  else
+    last_field->length= 0;


I have a feeling that on a 32bit platforms it will stop sending errors
on attempt to create a blob larger that 4Gb.
last_field->length is ulong, which is 32bit is a 32 bit platform,
and it gets assigned to a 64 bit value.

diff --git a/sql/field.cc b/sql/field.cc
index a7d9106..801e191 100644
--- a/sql/field.cc
+++ b/sql/field.cc
@@ -9169,6 +9169,24 @@ static inline bool is_item_func(Item* x)
 }
 
 
+bool Create_type_attributes_st::check(const char *field_name) const
+{
+  if (length > MAX_FIELD_BLOBLENGTH)
+  {
+    my_error(ER_TOO_BIG_DISPLAYWIDTH, MYF(0), field_name, MAX_FIELD_BLOBLENGTH);
+    return true;
+  }
+
+  if (decimals >= NOT_FIXED_DEC)
+  {
+    my_error(ER_TOO_BIG_SCALE, MYF(0), decimals, field_name,
+             static_cast<ulong>(NOT_FIXED_DEC - 1));
+    return true;
+  }
+  return false;
+}
+
+
 bool Create_field::check(THD *thd)
 {
   const uint conditional_type_modifiers= AUTO_INCREMENT_FLAG;
@@ -9182,19 +9200,8 @@ bool Create_field::check(THD *thd)
     sql_type= (enum enum_field_types)MYSQL_TYPE_VIRTUAL;
   }
 
-  if (length > MAX_FIELD_BLOBLENGTH)
-  {
-    my_error(ER_TOO_BIG_DISPLAYWIDTH, MYF(0), field_name, MAX_FIELD_BLOBLENGTH);
-    DBUG_RETURN(1);
-  }
-
-  if (decimals >= NOT_FIXED_DEC)
-  {
-    my_error(ER_TOO_BIG_SCALE, MYF(0), decimals, field_name,
-             static_cast<ulong>(NOT_FIXED_DEC - 1));
-    DBUG_RETURN(TRUE);
-  }
-
+  if (Create_type_attributes_st::check(field_name))
+    DBUG_RETURN(true);
   if (def)
   {
     /*
@@ -9802,18 +9809,16 @@ Field *make_field(TABLE_SHARE *share, uchar *ptr, uint32 field_length,
 /** Create a field suitable for create of table. */
 
 Create_field::Create_field(Field *old_field,Field *orig_field)
+ :Create_type_attributes(old_field)
 {
   field=      old_field;
   field_name=change=old_field->field_name;
-  length=     old_field->field_length;
   flags=      old_field->flags;
   unireg_check=old_field->unireg_check;
   pack_length=old_field->pack_length();
   key_length= old_field->key_length();
   sql_type=   old_field->real_type();
-  charset=    old_field->charset();		// May be NULL ptr
   comment=    old_field->comment;
-  decimals=   old_field->decimals();
   vcol_info=  old_field->vcol_info;
   create_if_not_exists= FALSE;
   stored_in_db= old_field->stored_in_db;
diff --git a/sql/field.h b/sql/field.h
index fdc00ed..55998e2 100644
--- a/sql/field.h
+++ b/sql/field.h
@@ -2833,11 +2833,25 @@ class Field_bit_as_char: public Field_bit {
 };
 
 
+class Create_type_attributes: public Sql_alloc,
+                              public Create_type_attributes_st
+{
+public:
+  Create_type_attributes() { init(); }
+  Create_type_attributes(const char *len, const char *dec, CHARSET_INFO *cs)
+  { set(len, dec, cs); }
+  Create_type_attributes(const Field *field)
+  { set(field->field_length, field->decimals(), field->charset()); }
+  Create_type_attributes(const Create_type_attributes_st &attr)
+  { set(attr.length, attr.decimals, attr.charset); }
+};
+
+
 /*
   Create field class for CREATE TABLE
 */
 
-class Create_field :public Sql_alloc
+class Create_field :public Create_type_attributes
 {
 public:
   const char *field_name;
@@ -2847,22 +2861,16 @@ class Create_field :public Sql_alloc
   Item *def, *on_update;                // Default value
   enum	enum_field_types sql_type;
   /*
-    At various stages in execution this can be length of field in bytes or
-    max number of characters. 
-  */
-  ulong length;
-  /*
     The value of `length' as set by parser: is the number of characters
     for most of the types, or of bytes for BLOBs or numeric types.
   */
   uint32 char_length;
-  uint  decimals, flags, pack_length, key_length;
+  uint  flags, pack_length, key_length;
   Field::utype unireg_check;
   TYPELIB *interval;			// Which interval to use
   TYPELIB *save_interval;               // Temporary copy for the above
                                         // Used only for UCS2 intervals
   List<String> interval_list;
-  CHARSET_INFO *charset;
   Field::geometry_type geom_type;
   Field *field;				// For alter table
   engine_option_value *option_list;
diff --git a/sql/item.h b/sql/item.h
index ff0c786..7e8039f 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -593,16 +593,22 @@ class Copy_query_with_rewrite
   { return copy_up_to(src_len); }
 };
 
-struct st_dyncall_create_def
+class DYNCALL_CREATE_DEF: public Create_type_attributes
 {
+public:
+  DYNCALL_CREATE_DEF(const char *len, const char *dec, CHARSET_INFO *cs)
+   :Create_type_attributes(len, dec, cs)
+  { }
+  DYNCALL_CREATE_DEF(const Create_type_attributes_st &attr)
+   :Create_type_attributes(attr)
+  { }
+  DYNCALL_CREATE_DEF()
+   :Create_type_attributes()
+  { }
   Item  *key, *value;
-  CHARSET_INFO *cs;
-  uint len, frac;
   DYNAMIC_COLUMN_TYPE type;
 };
 
-typedef struct st_dyncall_create_def DYNCALL_CREATE_DEF;
-
 
 typedef bool (Item::*Item_processor) (uchar *arg);
 /*
diff --git a/sql/item_create.cc b/sql/item_create.cc
index fa8249c..6175bd6 100644
--- a/sql/item_create.cc
+++ b/sql/item_create.cc
@@ -6017,22 +6017,10 @@ void item_create_cleanup()
 
 Item *
 create_func_cast(THD *thd, Item *a, Cast_target cast_type,
-                 const char *c_len, const char *c_dec,
-                 CHARSET_INFO *cs)
+                 const Create_type_attributes &attr)
 {
   Item *UNINIT_VAR(res);
-  ulonglong length= 0, decimals= 0;
-  int error;
   
-  /*
-    We don't have to check for error here as sql_yacc.yy has guaranteed
-    that the values are in range of ulonglong
-  */
-  if (c_len)
-    length= (ulonglong) my_strtoll10(c_len, NULL, &error);
-  if (c_dec)
-    decimals= (ulonglong) my_strtoll10(c_dec, NULL, &error);
-
   switch (cast_type) {
   case ITEM_CAST_BINARY:
     res= new (thd->mem_root) Item_func_binary(a);
@@ -6047,28 +6035,28 @@ void item_create_cleanup()
     res= new (thd->mem_root) Item_date_typecast(a);
     break;
   case ITEM_CAST_TIME:
-    if (decimals > MAX_DATETIME_PRECISION)
+    if (attr.decimals > MAX_DATETIME_PRECISION)
     {
-      wrong_precision_error(ER_TOO_BIG_PRECISION, a, decimals,
+      wrong_precision_error(ER_TOO_BIG_PRECISION, a, attr.decimals,
                             MAX_DATETIME_PRECISION);
       return 0;
     }
-    res= new (thd->mem_root) Item_time_typecast(a, (uint) decimals);
+    res= new (thd->mem_root) Item_time_typecast(a, (uint) attr.decimals);
     break;
   case ITEM_CAST_DATETIME:
-    if (decimals > MAX_DATETIME_PRECISION)
+    if (attr.decimals > MAX_DATETIME_PRECISION)
     {
-      wrong_precision_error(ER_TOO_BIG_PRECISION, a, decimals,
+      wrong_precision_error(ER_TOO_BIG_PRECISION, a, attr.decimals,
                             MAX_DATETIME_PRECISION);
       return 0;
     }
-    res= new (thd->mem_root) Item_datetime_typecast(a, (uint) decimals);
+    res= new (thd->mem_root) Item_datetime_typecast(a, (uint) attr.decimals);
     break;
   case ITEM_CAST_DECIMAL:
   {
     ulong len;
     uint dec;
-    if (get_length_and_scale(length, decimals, &len, &dec,
+    if (get_length_and_scale(attr.length, attr.decimals, &len, &dec,
                              DECIMAL_MAX_PRECISION, DECIMAL_MAX_SCALE,
                              a))
       return NULL;
@@ -6079,8 +6067,10 @@ void item_create_cleanup()
   {
     ulong len;
     uint dec;
+    ulonglong length= attr.length;
+    ulonglong decimals= attr.decimals;
 
-    if (!c_len)
+    if (!attr.length && !attr.decimals)
     {
       length=   DBL_DIG+7;
       decimals= NOT_FIXED_DEC;
@@ -6096,10 +6086,11 @@ void item_create_cleanup()
   case ITEM_CAST_CHAR:
   {
     int len= -1;
-    CHARSET_INFO *real_cs= (cs ? cs : thd->variables.collation_connection);
-    if (c_len)
+    CHARSET_INFO *real_cs= attr.charset ? attr.charset :
+                                          thd->variables.collation_connection;
+    if (attr.length_was_set_explicitly())
     {
-      if (length > MAX_FIELD_BLOBLENGTH)
+      if (attr.length > MAX_FIELD_BLOBLENGTH)
       {
         char buff[1024];
         String buf(buff, sizeof(buff), system_charset_info);
@@ -6107,7 +6098,7 @@ void item_create_cleanup()
                  MAX_FIELD_BLOBLENGTH);
         return NULL;
       }
-      len= (int) length;
+      len= (int) attr.length;
     }
     res= new (thd->mem_root) Item_char_typecast(a, len, real_cs);
     break;
@@ -6281,12 +6272,11 @@ Item *create_func_dyncol_delete(THD *thd, Item *str, List<Item> &nums)
 
 Item *create_func_dyncol_get(THD *thd,  Item *str, Item *num,
                              Cast_target cast_type,
-                             const char *c_len, const char *c_dec,
-                             CHARSET_INFO *cs)
+                             const Create_type_attributes &attr)
 {
   Item *res;
 
   if (!(res= new (thd->mem_root) Item_dyncol_get(str, num)))
     return res;                                 // Return NULL
-  return create_func_cast(thd, res, cast_type, c_len, c_dec, cs);
+  return create_func_cast(thd, res, cast_type, attr);
 }
diff --git a/sql/item_create.h b/sql/item_create.h
index 05fe48f..efee18e 100644
--- a/sql/item_create.h
+++ b/sql/item_create.h
@@ -165,8 +165,7 @@ class Create_udf_func : public Create_func
 */
 Item *
 create_func_cast(THD *thd, Item *a, Cast_target cast_type,
-                 const char *len, const char *dec,
-                 CHARSET_INFO *cs);
+                 const Create_type_attributes &attr);
 
 Item *create_temporal_literal(THD *thd,
                               const char *str, uint length,
@@ -192,8 +191,7 @@ Item *create_func_dyncol_add(THD *thd, Item *str,
 Item *create_func_dyncol_delete(THD *thd, Item *str, List<Item> &nums);
 Item *create_func_dyncol_get(THD *thd, Item *num, Item *str,
                              Cast_target cast_type,
-                             const char *c_len, const char *c_dec,
-                             CHARSET_INFO *cs);
+                             const Create_type_attributes &attr);
 Item *create_func_dyncol_json(THD *thd, Item *str);
 #endif
 
diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc
index 9a3c758..72e3c99 100644
--- a/sql/item_strfunc.cc
+++ b/sql/item_strfunc.cc
@@ -4562,10 +4562,10 @@ void Item_func_dyncol_create::print_arguments(String *str,
     case DYN_COL_DYNCOL:
     case DYN_COL_STRING:
       str->append(STRING_WITH_LEN(" AS char"));
-      if (defs[i].cs)
+      if (defs[i].charset)
       {
         str->append(STRING_WITH_LEN(" charset "));
-        str->append(defs[i].cs->csname);
+        str->append(defs[i].charset->csname);
         str->append(' ');
       }
       break;
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 7eb38b1..62a5510 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -491,7 +491,6 @@ void lex_start(THD *thd)
   lex->parsing_options.reset();
   lex->empty_field_list_on_rset= 0;
   lex->select_lex.select_number= 1;
-  lex->length=0;
   lex->part_info= 0;
   lex->select_lex.in_sum_expr=0;
   lex->select_lex.ftfunc_list_alloc.empty();
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 409358d..fcbda9c 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -2355,8 +2355,34 @@ struct LEX: public Query_tables_list
   Explain_query *explain;
 
   // type information
-  char *length,*dec;
-  CHARSET_INFO *charset;
+  Create_type_attributes *last_attr;
+  CHARSET_INFO *charset() const { return last_attr->charset;  }
+  ulong length() const          { return last_attr->length;   }
+  uint dec() const              { return last_attr->decimals; }
+  bool length_was_set_explicitly() const
+  { return last_attr->length_was_set_explicitly(); }
+
+  void init_last_attributes(CHARSET_INFO *cs= NULL) { last_attr->init(cs); }
+  void set_length(ulong length) { last_attr->set_length(length); }
+  CHARSET_INFO *set_charset(CHARSET_INFO *cs)
+  { return last_attr->charset= cs; }
+  void set_explicit_length(const char *length)
+  { last_attr->set_explicit_length(length); }
+  void set_length_and_dec(const char *length, const char *decimals)
+  {
+    last_attr->set_length(length);
+    last_attr->set_decimals(decimals);
+  }
+  void set_length_and_dec(ulong length, uint decimals)
+  {
+    last_attr->set_length(length);
+    last_attr->set_decimals(decimals);
+  }
+  void set_charset_and_dec(CHARSET_INFO *cs, const char *decimals)
+  {
+    last_attr->set_charset(cs);
+    last_attr->set_decimals(decimals);
+  }
 
   LEX_STRING name;
   char *help_arg;
@@ -2782,8 +2808,7 @@ struct LEX: public Query_tables_list
 
   int print_explain(select_result_sink *output, uint8 explain_flags,
                     bool is_analyze, bool *printed_anything);
-
-  void init_last_field(Create_field *field, const char *name, CHARSET_INFO *cs);
+  void init_last_field(Create_field *field, const char *name);
   void set_last_field_type(enum enum_field_types type);
   bool set_bincmp(CHARSET_INFO *cs, bool bin);
 };
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 7b36d39..ff75b63 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -871,9 +871,10 @@ static void add_key_to_list(LEX *lex, LEX_STRING *field_name,
   lex->col_list.empty();
 }
 
-void LEX::init_last_field(Create_field *field, const char *name, CHARSET_INFO *cs)
+void LEX::init_last_field(Create_field *field, const char *name)
 {
   last_field= field;
+  last_attr= field;
 
   field->field_name= name;
   field->flags= 0;
@@ -885,30 +886,12 @@ void LEX::init_last_field(Create_field *field, const char *name, CHARSET_INFO *c
   field->comment= null_lex_str;
   field->vcol_info= 0;
   field->interval_list.empty();
-
-  /* reset LEX fields that are used in Create_field::set_and_check() */
-  length= 0;
-  dec= 0;
-  charset= cs;
 }
 
 void LEX::set_last_field_type(enum enum_field_types type)
 {
   last_field->sql_type= type;
   last_field->create_if_not_exists= check_exists;
-  last_field->charset= charset;
-
-  if (length)
-  {
-    int err;
-    last_field->length= (ulong)my_strtoll10(length, NULL, &err);
-    if (err)
-      last_field->length= ~0UL; // safety
-  }
-  else
-    last_field->length= 0;
-
-  last_field->decimals= dec ? (uint)atoi(dec) : 0;
 }
 
 bool LEX::set_bincmp(CHARSET_INFO *cs, bool bin)
@@ -919,16 +902,16 @@ bool LEX::set_bincmp(CHARSET_INFO *cs, bool bin)
      field charset is determined in get_sql_field_charset() much later.
      so we only set a flag here.
   */
-  if (!charset)
+  if (!charset())
   {
-    charset= cs;
+    set_charset(cs);
     last_field->flags|= bin ? BINCMP_FLAG : 0;
     return false;
   }
 
-  charset= bin ? find_bin_collation(cs ? cs : charset)
-               :                    cs ? cs : charset;
-  return charset == NULL;
+  set_charset(bin ? find_bin_collation(cs ? cs : charset())
+                  :                    cs ? cs : charset());
+  return charset() == NULL;
 }
 
 #define bincmp_collation(X,Y)           \
@@ -1836,7 +1819,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
 
 %type <ha_rkey_mode> handler_rkey_mode
 
-%type <cast_type> cast_type
+%type <cast_type> cast_type create_new_cast_type
 
 %type <symbol> keyword keyword_sp
 
@@ -2904,9 +2887,8 @@ sp_param_name_and_type:
             }
 
             sp_variable *spvar= spc->add_variable(thd, $1);
-
-            lex->init_last_field(&spvar->field_def, $1.str,
-                                 thd->variables.collation_database);
+            lex->init_last_field(&spvar->field_def, $1.str);
+            lex->init_last_attributes(thd->variables.collation_database);
             $<spvar>$= spvar;
           }
           type_with_opt_collate
@@ -3002,8 +2984,8 @@ sp_decl:
 
             lex->sphead->reset_lex(thd);
             pctx->declare_var_boundary($2);
-            thd->lex->init_last_field(&spvar->field_def, spvar->name.str,
-                                      thd->variables.collation_database);
+            thd->lex->init_last_field(&spvar->field_def, spvar->name.str);
+            thd->lex->init_last_attributes(thd->variables.collation_database);
           }
           type_with_opt_collate
           sp_opt_default
@@ -6071,7 +6053,7 @@ field_spec:
             if (!f)
               MYSQL_YYABORT;
 
-            lex->init_last_field(f, $1.str, NULL);
+            lex->init_last_field(f, $1.str);
           }
           field_type  { Lex->set_last_field_type($3); }
           field_def
@@ -6183,24 +6165,22 @@ field_type:
         | FLOAT_SYM float_options field_options
           {
             $$=MYSQL_TYPE_FLOAT;
-            if (Lex->length && !Lex->dec)
+            if (Lex->length_was_set_explicitly() && !Lex->dec())
             {
-              int err;
-              ulonglong tmp_length= my_strtoll10(Lex->length, NULL, &err);
-              if (err || tmp_length > PRECISION_FOR_DOUBLE)
+              if (Lex->length() > PRECISION_FOR_DOUBLE)
               {
                 my_error(ER_WRONG_FIELD_SPEC, MYF(0),
                          Lex->last_field->field_name);
                 MYSQL_YYABORT;
               }
-              else if (tmp_length > PRECISION_FOR_FLOAT)
+              else if (Lex->length() > PRECISION_FOR_FLOAT)
                 $$= MYSQL_TYPE_DOUBLE;
-              Lex->length= 0;
+              Lex->set_length(0);
             }
           }
         | BIT_SYM
           {
-            Lex->length= (char*) "1";
+            Lex->set_length(1);
             $$=MYSQL_TYPE_BIT;
           }
         | BIT_SYM field_length
@@ -6209,12 +6189,12 @@ field_type:
           }
         | BOOL_SYM
           {
-            Lex->length= (char*) "1";
+            Lex->set_length(1);
             $$=MYSQL_TYPE_TINY;
           }
         | BOOLEAN_SYM
           {
-            Lex->length= (char*) "1";
+            Lex->set_length(1);
             $$=MYSQL_TYPE_TINY;
           }
         | char field_length opt_binary
@@ -6223,7 +6203,7 @@ field_type:
           }
         | char opt_binary
           {
-            Lex->length= (char*) "1";
+            Lex->set_length(1);
             $$=MYSQL_TYPE_STRING;
           }
         | nchar field_length opt_bin_mod
@@ -6233,19 +6213,19 @@ field_type:
           }
         | nchar opt_bin_mod
           {
-            Lex->length= (char*) "1";
+            Lex->set_length(1);
             $$=MYSQL_TYPE_STRING;
             bincmp_collation(national_charset_info, $2);
           }
         | BINARY field_length
           {
-            Lex->charset=&my_charset_bin;
+            Lex->set_charset(&my_charset_bin);
             $$=MYSQL_TYPE_STRING;
           }
         | BINARY
           {
-            Lex->length= (char*) "1";
-            Lex->charset=&my_charset_bin;
+            Lex->set_length(1);
+            Lex->set_charset(&my_charset_bin);
             $$=MYSQL_TYPE_STRING;
           }
         | varchar field_length opt_binary
@@ -6259,19 +6239,17 @@ field_type:
           }
         | VARBINARY field_length
           {
-            Lex->charset=&my_charset_bin;
+            Lex->set_charset(&my_charset_bin);
             $$= MYSQL_TYPE_VARCHAR;
           }
         | YEAR_SYM opt_field_length field_options
           {
-            if (Lex->length)
+            if (Lex->length_was_set_explicitly())
             {
-              errno= 0;
-              ulong length= strtoul(Lex->length, NULL, 10);
-              if (errno == 0 && length <= MAX_FIELD_BLOBLENGTH && length != 4)
+              if (Lex->length() <= MAX_FIELD_BLOBLENGTH && Lex->length() != 4)
               {
                 char buff[sizeof("YEAR()") + MY_INT64_NUM_DECIMAL_DIGITS + 1];
-                my_snprintf(buff, sizeof(buff), "YEAR(%lu)", length);
+                my_snprintf(buff, sizeof(buff), "YEAR(%lu)", Lex->length());
                 push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
                                     ER_WARN_DEPRECATED_SYNTAX,
                                     ER(ER_WARN_DEPRECATED_SYNTAX),
@@ -6305,18 +6283,18 @@ field_type:
                 MYSQL_TYPE_DATETIME2 : MYSQL_TYPE_DATETIME; }
         | TINYBLOB
           {
-            Lex->charset=&my_charset_bin;
+            Lex->set_charset(&my_charset_bin);
             $$=MYSQL_TYPE_TINY_BLOB;
           }
         | BLOB_SYM opt_field_length
           {
-            Lex->charset=&my_charset_bin;
+            Lex->set_charset(&my_charset_bin);
             $$=MYSQL_TYPE_BLOB;
           }
         | spatial_type
           {
 #ifdef HAVE_SPATIAL
-            Lex->charset=&my_charset_bin;
+            Lex->set_charset(&my_charset_bin);
             Lex->last_field->geom_type= $1;
             $$=MYSQL_TYPE_GEOMETRY;
 #else
@@ -6327,17 +6305,17 @@ field_type:
           }
         | MEDIUMBLOB
           {
-            Lex->charset=&my_charset_bin;
+            Lex->set_charset(&my_charset_bin);
             $$=MYSQL_TYPE_MEDIUM_BLOB;
           }
         | LONGBLOB
           {
-            Lex->charset=&my_charset_bin;
+            Lex->set_charset(&my_charset_bin);
             $$=MYSQL_TYPE_LONG_BLOB;
           }
         | LONG_SYM VARBINARY
           {
-            Lex->charset=&my_charset_bin;
+            Lex->set_charset(&my_charset_bin);
             $$=MYSQL_TYPE_MEDIUM_BLOB;
           }
         | LONG_SYM varchar opt_binary
@@ -6375,8 +6353,7 @@ spatial_type:
         | GEOMETRYCOLLECTION  { $$= Field::GEOM_GEOMETRYCOLLECTION; }
         | POINT_SYM
           {
-            Lex->length= const_cast<char*>(STRINGIFY_ARG
-                                           (MAX_LEN_GEOM_POINT_FIELD));
+            Lex->set_length(MAX_LEN_GEOM_POINT_FIELD);
             $$= Field::GEOM_POINT;
           }
         | MULTIPOINT          { $$= Field::GEOM_MULTIPOINT; }
@@ -6430,9 +6407,9 @@ real_type:
 
 float_options:
           /* empty */
-          { Lex->dec=Lex->length= (char*)0; }
+          { }
         | field_length
-          { Lex->dec= (char*)0; }
+          { }
         | precision
           {}
         ;
@@ -6440,9 +6417,7 @@ float_options:
 precision:
           '(' NUM ',' NUM ')'
           {
-            LEX *lex=Lex;
-            lex->length=$2.str;
-            lex->dec=$4.str;
+            Lex->set_length_and_dec($2.str, $4.str);
           }
         ;
 
@@ -6463,13 +6438,13 @@ field_option:
         ;
 
 field_length:
-          '(' LONG_NUM ')'      { Lex->length= $2.str; }
-        | '(' ULONGLONG_NUM ')' { Lex->length= $2.str; }
-        | '(' DECIMAL_NUM ')'   { Lex->length= $2.str; }
-        | '(' NUM ')'           { Lex->length= $2.str; };
+          '(' LONG_NUM ')'      { Lex->set_explicit_length($2.str); }
+        | '(' ULONGLONG_NUM ')' { Lex->set_explicit_length($2.str); }
+        | '(' DECIMAL_NUM ')'   { Lex->set_explicit_length($2.str); }
+        | '(' NUM ')'           { Lex->set_explicit_length($2.str); };
 
 opt_field_length:
-          /* empty */  { Lex->length=(char*) 0; /* use default length */ }
+          /* empty */  { /* use default length */ }
         | field_length { }
         ;
 
@@ -6527,10 +6502,10 @@ attribute:
         | COMMENT_SYM TEXT_STRING_sys { Lex->last_field->comment= $2; }
         | COLLATE_SYM collation_name
           {
-            if (Lex->charset && !my_charset_same(Lex->charset,$2))
+            if (Lex->charset() && !my_charset_same(Lex->charset(), $2))
             {
               my_error(ER_COLLATION_CHARSET_MISMATCH, MYF(0),
-                       $2->name,Lex->charset->csname);
+                       $2->name, Lex->charset()->csname);
               MYSQL_YYABORT;
             }
             else
@@ -6571,7 +6546,7 @@ type_with_opt_collate:
 
           if ($2)
           {
-            if (!(Lex->charset= merge_charset_and_collation(Lex->charset, $2)))
+            if (!(Lex->set_charset(merge_charset_and_collation(Lex->charset(), $2))))
               MYSQL_YYABORT;
           }
           Lex->set_last_field_type($1);
@@ -9006,117 +8981,54 @@ all_or_any:
         ;
 
 opt_dyncol_type:
-          /* empty */ 
-          {
-            LEX *lex= Lex;
-	    $$= DYN_COL_NULL; /* automatic type */
-            lex->charset= NULL;
-            lex->length= lex->dec= 0;
-	  }
+          /* empty */    { $$= DYN_COL_NULL; } /* automatic type */
         | AS dyncol_type { $$= $2; }
         ;
 
 dyncol_type:
-          INT_SYM
-          {
-            LEX *lex= Lex;
-            $$= DYN_COL_INT;
-            lex->charset= NULL;
-            lex->length= lex->dec= 0;
-          }
-        | UNSIGNED INT_SYM 
-          {
-            LEX *lex= Lex;
-            $$= DYN_COL_UINT;
-            lex->charset= NULL;
-            lex->length= lex->dec= 0;
-          }
-        | DOUBLE_SYM
-          {
-            LEX *lex= Lex;
-            $$= DYN_COL_DOUBLE;
-            lex->charset= NULL;
-            lex->length= lex->dec= 0;
-          }
-        | REAL
-          {
-            LEX *lex= Lex;
-            $$= DYN_COL_DOUBLE;
-            lex->charset= NULL;
-            lex->length= lex->dec= 0;
-          }
-        | FLOAT_SYM
-          {
-            LEX *lex= Lex;
-            $$= DYN_COL_DOUBLE;
-            lex->charset= NULL;
-            lex->length= lex->dec= 0;
-          }
-        | DECIMAL_SYM float_options
-          {
-            $$= DYN_COL_DECIMAL;
-            Lex->charset= NULL;
-          }
+          INT_SYM                   { $$= DYN_COL_INT;     }
+        | UNSIGNED INT_SYM          { $$= DYN_COL_UINT;    }
+        | DOUBLE_SYM                { $$= DYN_COL_DOUBLE;  }
+        | REAL                      { $$= DYN_COL_DOUBLE;  }
+        | FLOAT_SYM                 { $$= DYN_COL_DOUBLE;  }
+        | DECIMAL_SYM float_options { $$= DYN_COL_DECIMAL; }
+        | DATE_SYM                  { $$= DYN_COL_DATE;    }
         | char
-          { Lex->charset= thd->variables.collation_connection; }
+          { Lex->set_charset(thd->variables.collation_connection); }
           opt_binary
           {
-            LEX *lex= Lex;
             $$= DYN_COL_STRING;
-            lex->length= lex->dec= 0;
           }
         | nchar
           {
-            LEX *lex= Lex;
             $$= DYN_COL_STRING;
-            lex->charset= national_charset_info;
-            lex->length= lex->dec= 0;
-          }
-        | DATE_SYM
-          {
-            LEX *lex= Lex;
-            $$= DYN_COL_DATE;
-            lex->charset= NULL;
-            lex->length= lex->dec= 0;
+            Lex->set_charset(national_charset_info);
           }
         | TIME_SYM opt_field_length
           {
-            LEX *lex= Lex;
             $$= DYN_COL_TIME;
-            lex->charset= NULL;
-            lex->dec= lex->length;
-            lex->length= 0;
+            Lex->set_length_and_dec(0, Lex->length()); // Move length to dec
           }
         | DATETIME opt_field_length
           {
-            LEX *lex= Lex;
             $$= DYN_COL_DATETIME;
-            lex->charset= NULL;
-            lex->dec= lex->length;
-            lex->length= 0;
+            Lex->set_length_and_dec(0, Lex->length()); // Move length to dec
           }
         ;
 
 dyncall_create_element:
-   expr ',' expr opt_dyncol_type
+   expr ',' expr
    {
-     LEX *lex= Lex;
-     $$= (DYNCALL_CREATE_DEF *)
-       alloc_root(thd->mem_root, sizeof(DYNCALL_CREATE_DEF));
-     if ($$ == NULL)
+     if (!(Lex->last_attr= new DYNCALL_CREATE_DEF()))
        MYSQL_YYABORT;
+   }
+   opt_dyncol_type
+   {
+     LEX *lex= Lex;
+     $$= (DYNCALL_CREATE_DEF*) lex->last_attr;
      $$->key= $1;
      $$->value= $3;
-     $$->type= (DYNAMIC_COLUMN_TYPE)$4;
-     $$->cs= lex->charset;
-     if (lex->length)
-       $$->len= strtoul(lex->length, NULL, 10);
-     else
-       $$->len= 0;
-     if (lex->dec)
-       $$->frac= strtoul(lex->dec, NULL, 10);
-     else
-       $$->len= 0;
+     $$->type= (DYNAMIC_COLUMN_TYPE)$5;
    }
 
 dyncall_create_list:
@@ -9246,16 +9158,15 @@ simple_expr:
           }
         | BINARY simple_expr %prec NEG
           {
-            $$= create_func_cast(thd, $2, ITEM_CAST_CHAR, NULL, NULL,
-                                 &my_charset_bin);
+            $$= create_func_cast(thd, $2, ITEM_CAST_CHAR,
+                          Create_type_attributes(NULL, NULL, &my_charset_bin));
             if ($$ == NULL)
               MYSQL_YYABORT;
           }
-        | CAST_SYM '(' expr AS cast_type ')'
+        | CAST_SYM '(' expr AS create_new_cast_type ')'
           {
             LEX *lex= Lex;
-            $$= create_func_cast(thd, $3, $5, lex->length, lex->dec,
-                                 lex->charset);
+            $$= create_func_cast(thd, $3, $5, lex->last_attr[0]);
             if ($$ == NULL)
               MYSQL_YYABORT;
           }
@@ -9265,10 +9176,9 @@ simple_expr:
             if ($$ == NULL)
               MYSQL_YYABORT;
           }
-        | CONVERT_SYM '(' expr ',' cast_type ')'
+        | CONVERT_SYM '(' expr ',' create_new_cast_type ')'
           {
-            $$= create_func_cast(thd, $3, $5, Lex->length, Lex->dec,
-                                 Lex->charset);
+            $$= create_func_cast(thd, $3, $5, Lex->last_attr[0]);
             if ($$ == NULL)
               MYSQL_YYABORT;
           }
@@ -9700,12 +9610,10 @@ function_call_nonkeyword:
               MYSQL_YYABORT;
           }
         |
-          COLUMN_GET_SYM '(' expr ',' expr AS cast_type ')'
+          COLUMN_GET_SYM '(' expr ',' expr AS create_new_cast_type ')'
           {
             LEX *lex= Lex;
-            $$= create_func_dyncol_get(thd, $3, $5, $7,
-                                        lex->length, lex->dec,
-                                        lex->charset);
+            $$= create_func_dyncol_get(thd, $3, $5, $7, lex->last_attr[0]);
             if ($$ == NULL)
               MYSQL_YYABORT;
           }
@@ -10374,45 +10282,39 @@ in_sum_expr:
           }
         ;
 
+create_new_cast_type:
+        /* Empty*/ { Lex->last_attr= new Create_type_attributes(); }
+        cast_type  { $$= $2; }
+        ;
+
 cast_type:
           BINARY opt_field_length
-          { $$=ITEM_CAST_CHAR; Lex->charset= &my_charset_bin; Lex->dec= 0; }
+          { $$=ITEM_CAST_CHAR; Lex->set_charset(&my_charset_bin); }
         | CHAR_SYM opt_field_length
-          { Lex->charset= thd->variables.collation_connection; }
-          opt_binary
-          { $$=ITEM_CAST_CHAR; Lex->dec= 0; }
+          { Lex->set_charset(thd->variables.collation_connection); }
+          opt_binary                { $$=ITEM_CAST_CHAR; }
         | NCHAR_SYM opt_field_length
-          { $$=ITEM_CAST_CHAR; Lex->charset= national_charset_info; Lex->dec=0; }
-        | INT_SYM
-          { $$=ITEM_CAST_SIGNED_INT; Lex->charset= NULL; Lex->dec=Lex->length= (char*)0; }
-        | SIGNED_SYM
-          { $$=ITEM_CAST_SIGNED_INT; Lex->charset= NULL; Lex->dec=Lex->length= (char*)0; }
-        | SIGNED_SYM INT_SYM
-          { $$=ITEM_CAST_SIGNED_INT; Lex->charset= NULL; Lex->dec=Lex->length= (char*)0; }
-        | UNSIGNED
-          { $$=ITEM_CAST_UNSIGNED_INT; Lex->charset= NULL; Lex->dec=Lex->length= (char*)0; }
-        | UNSIGNED INT_SYM
-          { $$=ITEM_CAST_UNSIGNED_INT; Lex->charset= NULL; Lex->dec=Lex->length= (char*)0; }
-        | DATE_SYM
-          { $$=ITEM_CAST_DATE; Lex->charset= NULL; Lex->dec=Lex->length= (char*)0; }
+          { $$=ITEM_CAST_CHAR; Lex->set_charset_and_dec(national_charset_info, 0); }
+        | INT_SYM                   { $$=ITEM_CAST_SIGNED_INT; }
+        | SIGNED_SYM                { $$=ITEM_CAST_SIGNED_INT; }
+        | SIGNED_SYM INT_SYM        { $$=ITEM_CAST_SIGNED_INT; }
+        | UNSIGNED                  { $$=ITEM_CAST_UNSIGNED_INT; }
+        | UNSIGNED INT_SYM          { $$=ITEM_CAST_UNSIGNED_INT; }
+        | DATE_SYM                  { $$=ITEM_CAST_DATE; }
         | TIME_SYM opt_field_length
           {
             $$=ITEM_CAST_TIME;
             LEX *lex= Lex;
-            lex->charset= NULL; lex->dec= lex->length; lex->length= (char*)0;
+            lex->set_length_and_dec(0, lex->length());
            }
         | DATETIME opt_field_length
           {
             $$=ITEM_CAST_DATETIME;
             LEX *lex= Lex;
-            lex->charset= NULL; lex->dec= lex->length; lex->length= (char*)0;
+            lex->set_length_and_dec(0, lex->length()); // Swap dec and length
            }
-        | DECIMAL_SYM float_options
-          { $$=ITEM_CAST_DECIMAL; Lex->charset= NULL; }
-        | DOUBLE_SYM
-          { Lex->charset= NULL; Lex->length= Lex->dec= 0;}
-          opt_precision
-          { $$=ITEM_CAST_DOUBLE; }
+        | DECIMAL_SYM float_options { $$=ITEM_CAST_DECIMAL; }
+        | DOUBLE_SYM opt_precision  { $$=ITEM_CAST_DOUBLE; }
 
 opt_expr_list:
           /* empty */ { $$= NULL; }
@@ -16184,8 +16086,8 @@ sf_tail:
           RETURNS_SYM /* $9 */
           { /* $10 */
             LEX *lex= Lex;
-            lex->init_last_field(&lex->sphead->m_return_field_def, NULL,
-                                 thd->variables.collation_database);
+            lex->init_last_field(&lex->sphead->m_return_field_def, NULL);
+            lex->init_last_attributes(thd->variables.collation_database);
           }
           type_with_opt_collate /* $11 */
           { /* $12 */
diff --git a/sql/structs.h b/sql/structs.h
index 99561c5..20af8e0 100644
--- a/sql/structs.h
+++ b/sql/structs.h
@@ -471,4 +471,72 @@ class Discrete_intervals_list {
   Discrete_interval* get_current() const { return current; };
 };
 
+
+struct Create_type_attributes_st
+{
+  bool m_length_was_set_explicitly;
+public:
+  /*
+    When in Create_field:
+    At various stages in execution this can be length of field in bytes or
+    max number of characters. 
+  */
+  ulong length;
+  uint decimals;
+  CHARSET_INFO *charset;
+
+  bool length_was_set_explicitly() const
+  { return m_length_was_set_explicitly; }
+
+  void init(CHARSET_INFO *cs= NULL)
+  {
+    charset= cs;
+    length= 0;
+    decimals= 0;
+    m_length_was_set_explicitly= false;
+  }
+  bool check(const char *field_name) const;
+  void set_charset(CHARSET_INFO *cs) { charset= cs; }
+  void set_decimals(uint dec)        { decimals= dec; }
+  void set_decimals(const char *dec) { decimals= dec ? (uint) atoi(dec) : 0; }
+  void set_length(const char *length_arg)
+  {
+    if (length_arg)
+    {
+      int err;
+      length= (ulong)my_strtoll10(length_arg, NULL, &err);
+      if (err)
+        length= ~0UL; // safety
+    }
+    else
+      length= 0;
+    m_length_was_set_explicitly= false;
+  }
+  void set(const char *length_arg, const char *dec_arg, CHARSET_INFO *cs)
+  {
+    charset= cs;
+    set_length(length_arg);
+    set_decimals(dec_arg);
+    m_length_was_set_explicitly= length_arg != NULL;
+  }
+  void set(ulong length_arg, uint dec_arg, CHARSET_INFO *cs)
+  {
+    charset= cs;
+    set_length(length_arg);
+    set_decimals(dec_arg);
+    m_length_was_set_explicitly= false;
+  }
+  void set_explicit_length(const char *length)
+  {    
+    set_length(length);
+    m_length_was_set_explicitly= true;
+  }
+  void set_length(ulong length_arg)
+  {
+    length= length_arg;
+    m_length_was_set_explicitly= false;
+  }
+};
+
+
 #endif /* STRUCTS_INCLUDED */

Follow ups