[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


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

---
 mysql-test/r/cast.result                     |  29 ++
 mysql-test/r/sp-error.result                 |   2 +-
 mysql-test/r/sp-ucs2.result                  |   6 +-
 mysql-test/r/sp.result                       |  24 +-
 mysql-test/suite/funcs_1/r/storedproc.result |  27 +-
 mysql-test/suite/funcs_1/t/storedproc.test   |   1 -
 mysql-test/t/cast.test                       |  18 +
 mysql-test/t/sp-error.test                   |   7 +-
 mysql-test/t/sp-ucs2.test                    |   6 +-
 mysql-test/t/sp.test                         |  33 +-
 sql/field.cc                                 | 278 ++++++---------
 sql/field.h                                  |  19 +-
 sql/sp_head.cc                               |  19 +-
 sql/sp_pcontext.cc                           |   7 +-
 sql/sp_pcontext.h                            |  14 +-
 sql/sql_lex.h                                |  27 +-
 sql/sql_parse.cc                             | 122 +------
 sql/sql_parse.h                              |  11 +-
 sql/sql_plugin.cc                            |   4 +-
 sql/sql_table.cc                             |  12 +-
 sql/sql_yacc.yy                              | 514 +++++++++++++--------------
 sql/table.cc                                 |   6 +-
 storage/perfschema/pfs_digest.cc             |   3 +-
 23 files changed, 546 insertions(+), 643 deletions(-)

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
+select collation(cast("a" as char(10) binary charset utf8));
+collation(cast("a" as char(10) binary charset utf8))
+utf8_bin
+select collation(cast("a" as char(10) binary ascii));
+collation(cast("a" as char(10) binary ascii))
+latin1_bin
+select collation(cast("a" as char(10) binary unicode));
+collation(cast("a" as char(10) binary unicode))
+ucs2_bin
diff --git a/mysql-test/r/sp-error.result b/mysql-test/r/sp-error.result
index 71fab8c..7569673 100644
--- a/mysql-test/r/sp-error.result
+++ b/mysql-test/r/sp-error.result
@@ -1411,7 +1411,7 @@ end|
 ERROR 0A000: Not allowed to return a result set from a function
 drop function if exists bug20701;
 create function bug20701() returns varchar(25) binary return "test";
-ERROR 42000: This version of MariaDB doesn't yet support 'return value collation'
+drop function bug20701;
 create function bug20701() returns varchar(25) return "test";
 drop function bug20701;
 create procedure proc_26503_error_1()
diff --git a/mysql-test/r/sp-ucs2.result b/mysql-test/r/sp-ucs2.result
index 605fe4e..c7a3ed6 100644
--- a/mysql-test/r/sp-ucs2.result
+++ b/mysql-test/r/sp-ucs2.result
@@ -106,20 +106,20 @@ RETURNS VARCHAR(64) CHARACTER SET ucs2
 BEGIN
 RETURN 'str';
 END|
-ERROR 42000: This version of MariaDB doesn't yet support 'COLLATE with no CHARACTER SET in SP parameters, RETURNS, DECLARE'
+ERROR 42000: COLLATION 'ucs2_unicode_ci' is not valid for CHARACTER SET 'latin1'
 CREATE FUNCTION f(f1 VARCHAR(64) CHARACTER SET ucs2)
 RETURNS VARCHAR(64) COLLATE ucs2_unicode_ci
 BEGIN
 RETURN 'str';
 END|
-ERROR 42000: This version of MariaDB doesn't yet support 'COLLATE with no CHARACTER SET in SP parameters, RETURNS, DECLARE'
+ERROR 42000: COLLATION 'ucs2_unicode_ci' is not valid for CHARACTER SET 'latin1'
 CREATE FUNCTION f(f1 VARCHAR(64) CHARACTER SET ucs2)
 RETURNS VARCHAR(64) CHARACTER SET ucs2
 BEGIN
 DECLARE f2 VARCHAR(64) COLLATE ucs2_unicode_ci;
 RETURN 'str';
 END|
-ERROR 42000: This version of MariaDB doesn't yet support 'COLLATE with no CHARACTER SET in SP parameters, RETURNS, DECLARE'
+ERROR 42000: COLLATION 'ucs2_unicode_ci' is not valid for CHARACTER SET 'latin1'
 SET NAMES utf8;
 DROP FUNCTION IF EXISTS bug48766;
 CREATE FUNCTION bug48766 ()
diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result
index 56d8ea6..4fa43e8 100644
--- a/mysql-test/r/sp.result
+++ b/mysql-test/r/sp.result
@@ -3548,7 +3548,10 @@ begin
 set f1= concat( 'hello', f1 );
 return f1;
 end|
-ERROR 42000: This version of MariaDB doesn't yet support 'return value collation'
+select collation(bug9048("foo"))|
+collation(bug9048("foo"))
+latin1_bin
+drop function bug9048|
 drop procedure if exists bug12849_1|
 create procedure bug12849_1(inout x char) select x into x|
 set @var='a'|
@@ -7858,3 +7861,22 @@ v1
 DROP PROCEDURE p1;
 DROP TABLE t1;
 # End of 5.5 test
+CREATE FUNCTION f(f1 VARCHAR(64) COLLATE latin1_german2_ci)
+RETURNS VARCHAR(64)
+BEGIN
+RETURN 'str';
+END|
+DROP FUNCTION f|
+CREATE FUNCTION f(f1 VARCHAR(64))
+RETURNS VARCHAR(64) COLLATE latin1_german2_ci
+BEGIN
+RETURN 'str';
+END|
+DROP FUNCTION f|
+CREATE FUNCTION f(f1 VARCHAR(64))
+RETURNS VARCHAR(64)
+BEGIN
+DECLARE f2 VARCHAR(64) COLLATE latin1_german2_ci;
+RETURN 'str';
+END|
+DROP FUNCTION f|
diff --git a/mysql-test/suite/funcs_1/r/storedproc.result b/mysql-test/suite/funcs_1/r/storedproc.result
index 4ec9bfb..f316bdb 100644
--- a/mysql-test/suite/funcs_1/r/storedproc.result
+++ b/mysql-test/suite/funcs_1/r/storedproc.result
@@ -142,7 +142,7 @@ BEGIN
 SET @v1 = f1;
 SELECT @v1;
 END//
-ERROR 42000: Too big precision 256 specified for ''. Maximum is 65.
+ERROR 42000: Too big precision 256 specified for 'f1'. Maximum is 65.
 DROP PROCEDURE IF EXISTS sp1//
 Warnings:
 Note	1305	PROCEDURE db_storedproc.sp1 does not exist
@@ -152,7 +152,7 @@ BEGIN
 SET @v1 = f1;
 SELECT @v1;
 END//
-ERROR 42000: Too big precision 66 specified for ''. Maximum is 65.
+ERROR 42000: Too big precision 66 specified for 'f1'. Maximum is 65.
 DROP PROCEDURE IF EXISTS sp1//
 Warnings:
 Note	1305	PROCEDURE db_storedproc.sp1 does not exist
@@ -1407,12 +1407,12 @@ BEGIN
 SELECT f1;
 END//
 Warnings:
-Note	1291	Column '' has duplicated value 'value1' in ENUM
+Note	1291	Column 'f1' has duplicated value 'value1' in ENUM
 CALL sp1( "value1" );
 f1
 value1
 Warnings:
-Note	1291	Column '' has duplicated value 'value1' in ENUM
+Note	1291	Column 'f1' has duplicated value 'value1' in ENUM
 SHOW PROCEDURE STATUS WHERE db = 'db_storedproc';
 Db	Name	Type	Definer	Modified	Created	Security_type	Comment	character_set_client	collation_connection	Database Collation
 db_storedproc	sp1	PROCEDURE	root at localhost	<modified>	<created>	INVOKER	this is simple	latin1	latin1_swedish_ci	latin1_swedish_ci
@@ -1423,12 +1423,12 @@ BEGIN
 SELECT f1;
 END//
 Warnings:
-Note	1291	Column '' has duplicated value 'value1' in SET
+Note	1291	Column 'f1' has duplicated value 'value1' in SET
 CALL sp1( "value1, value1" );
 f1
 value1
 Warnings:
-Note	1291	Column '' has duplicated value 'value1' in SET
+Note	1291	Column 'f1' has duplicated value 'value1' in SET
 Warning	1265	Data truncated for column 'f1' at row 1
 SHOW PROCEDURE STATUS WHERE db = 'db_storedproc';
 Db	Name	Type	Definer	Modified	Created	Security_type	Comment	character_set_client	collation_connection	Database Collation
@@ -1440,12 +1440,12 @@ BEGIN
 SELECT f1;
 END//
 Warnings:
-Note	1291	Column '' has duplicated value 'value1' in ENUM
+Note	1291	Column 'f1' has duplicated value 'value1' in ENUM
 CALL sp1( "value1" );
 f1
 value1
 Warnings:
-Note	1291	Column '' has duplicated value 'value1' in ENUM
+Note	1291	Column 'f1' has duplicated value 'value1' in ENUM
 SHOW PROCEDURE STATUS WHERE db = 'db_storedproc';
 Db	Name	Type	Definer	Modified	Created	Security_type	Comment	character_set_client	collation_connection	Database Collation
 db_storedproc	sp1	PROCEDURE	root at localhost	<modified>	<created>	INVOKER	this is simple	latin1	latin1_swedish_ci	latin1_swedish_ci
@@ -1548,7 +1548,7 @@ BEGIN
 SET f1 = 1000000 + f1;
 RETURN f1;
 END//
-ERROR 42000: Too big scale 31 specified for ''. Maximum is 30.
+ERROR 42000: Too big scale 31 specified for 'f1'. Maximum is 30.
 SELECT fn1( 1.3326e+8 );
 ERROR 42000: FUNCTION db_storedproc.fn1 does not exist
 CREATE FUNCTION fn1( f1 DECIMAL(63, 30) ) RETURNS DECIMAL(63, 30)
@@ -1570,7 +1570,7 @@ BEGIN
 RETURN f1;
 END//
 Warnings:
-Note	1291	Column '' has duplicated value 'value1' in ENUM
+Note	1291	Column 'f1' has duplicated value 'value1' in ENUM
 SELECT fn1( "value1" );
 fn1( "value1" )
 1.000000000000000000000000000000
@@ -1584,7 +1584,7 @@ BEGIN
 RETURN f1;
 END//
 Warnings:
-Note	1291	Column '' has duplicated value 'value1' in SET
+Note	1291	Column 'f1' has duplicated value 'value1' in SET
 SELECT fn1( "value1, value1" );
 fn1( "value1, value1" )
 1.000000000000000000000000000000
@@ -3119,10 +3119,7 @@ return f1;
 DROP FUNCTION IF EXISTS fn1;
 CREATE FUNCTION fn1(f1 char binary ) returns char binary
 return f1;
-ERROR 42000: This version of MariaDB doesn't yet support 'return value collation'
 DROP FUNCTION IF EXISTS fn1;
-Warnings:
-Note	1305	FUNCTION db_storedproc.fn1 does not exist
 CREATE FUNCTION fn1(f1 char ascii ) returns char ascii
 return f1;
 DROP FUNCTION IF EXISTS fn1;
@@ -5836,7 +5833,7 @@ fetch cur1 into e;
 SELECT x, y, z, a, b, c, d, e;
 close cur1;
 END//
-ERROR 42000: Too big scale 255 specified for ''. Maximum is 30.
+ERROR 42000: Too big scale 255 specified for 'b'. Maximum is 30.
 CALL sp6();
 ERROR 42000: PROCEDURE db_storedproc.sp6 does not exist
 DROP PROCEDURE IF EXISTS sp6;
diff --git a/mysql-test/suite/funcs_1/t/storedproc.test b/mysql-test/suite/funcs_1/t/storedproc.test
index 69406a5..ff41795 100644
--- a/mysql-test/suite/funcs_1/t/storedproc.test
+++ b/mysql-test/suite/funcs_1/t/storedproc.test
@@ -2155,7 +2155,6 @@ CREATE FUNCTION fn1(f1 char ) returns char
     return f1;
 
 DROP FUNCTION IF EXISTS fn1;
---error ER_NOT_SUPPORTED_YET
 CREATE FUNCTION fn1(f1 char binary ) returns char binary
     return f1;
 
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));
+
diff --git a/mysql-test/t/sp-error.test b/mysql-test/t/sp-error.test
index 711e639..a16ef12 100644
--- a/mysql-test/t/sp-error.test
+++ b/mysql-test/t/sp-error.test
@@ -2051,13 +2051,8 @@ delimiter ;|
 --disable_warnings
 drop function if exists bug20701;
 --enable_warnings
-#
-# This was disabled in 5.1.12. See bug #20701
-# When collation support in SP is implemented, then this test should
-# be removed.
-#
---error ER_NOT_SUPPORTED_YET
 create function bug20701() returns varchar(25) binary return "test";
+drop function bug20701;
 create function bug20701() returns varchar(25) return "test";
 drop function bug20701;
 
diff --git a/mysql-test/t/sp-ucs2.test b/mysql-test/t/sp-ucs2.test
index f6f70c3..3532169 100644
--- a/mysql-test/t/sp-ucs2.test
+++ b/mysql-test/t/sp-ucs2.test
@@ -114,7 +114,7 @@ DROP FUNCTION f1|
 #
 # COLLATE with no CHARACTER SET in IN param
 #
---error ER_NOT_SUPPORTED_YET
+--error ER_COLLATION_CHARSET_MISMATCH
 CREATE FUNCTION f(f1 VARCHAR(64) COLLATE ucs2_unicode_ci)
   RETURNS VARCHAR(64) CHARACTER SET ucs2
 BEGIN
@@ -125,7 +125,7 @@ END|
 #
 # COLLATE with no CHARACTER SET in RETURNS
 #
---error ER_NOT_SUPPORTED_YET
+--error ER_COLLATION_CHARSET_MISMATCH
 CREATE FUNCTION f(f1 VARCHAR(64) CHARACTER SET ucs2)
   RETURNS VARCHAR(64) COLLATE ucs2_unicode_ci
 BEGIN
@@ -136,7 +136,7 @@ END|
 #
 # COLLATE with no CHARACTER SET in DECLARE
 #
---error ER_NOT_SUPPORTED_YET
+--error ER_COLLATION_CHARSET_MISMATCH
 CREATE FUNCTION f(f1 VARCHAR(64) CHARACTER SET ucs2)
   RETURNS VARCHAR(64) CHARACTER SET ucs2
 BEGIN
diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test
index 5179bb1..3f635ac 100644
--- a/mysql-test/t/sp.test
+++ b/mysql-test/t/sp.test
@@ -4355,17 +4355,14 @@ begin
   return f1;
 end|
 drop function bug9048|
-#
-# This was disabled in 5.1.12. See bug #20701
-# When collation support in SP is implemented, then this test should
-# be removed.
-#
---error ER_NOT_SUPPORTED_YET
+
 create function bug9048(f1 char binary) returns char binary
 begin
   set f1= concat( 'hello', f1 );
   return f1;
 end|
+select collation(bug9048("foo"))|
+drop function bug9048|
 
 # Bug #12849 Stored Procedure: Crash on procedure call with CHAR type
 # 'INOUT' parameter
@@ -9303,3 +9300,27 @@ DROP PROCEDURE p1;
 DROP TABLE t1;
 
 --echo # End of 5.5 test
+
+DELIMITER |;
+CREATE FUNCTION f(f1 VARCHAR(64) COLLATE latin1_german2_ci)
+  RETURNS VARCHAR(64)
+BEGIN
+  RETURN 'str';
+END|
+DROP FUNCTION f|
+
+CREATE FUNCTION f(f1 VARCHAR(64))
+  RETURNS VARCHAR(64) COLLATE latin1_german2_ci
+BEGIN
+  RETURN 'str';
+END|
+DROP FUNCTION f|
+
+CREATE FUNCTION f(f1 VARCHAR(64))
+  RETURNS VARCHAR(64)
+BEGIN
+  DECLARE f2 VARCHAR(64) COLLATE latin1_german2_ci;
+  RETURN 'str';
+END|
+DROP FUNCTION f|
+DELIMITER ;|
diff --git a/sql/field.cc b/sql/field.cc
index 3d5ba98..a7d9106 100644
--- a/sql/field.cc
+++ b/sql/field.cc
@@ -9163,96 +9163,100 @@ void Create_field::init_for_tmp_table(enum_field_types sql_type_arg,
 }
 
 
-/**
-  Initialize field definition for create.
-
-  @param thd                   Thread handle
-  @param fld_name              Field name
-  @param fld_type              Field type
-  @param fld_length            Field length
-  @param fld_decimals          Decimal (if any)
-  @param fld_type_modifier     Additional type information
-  @param fld_default_value     Field default value (if any)
-  @param fld_on_update_value   The value of ON UPDATE clause
-  @param fld_comment           Field comment
-  @param fld_change            Field change
-  @param fld_interval_list     Interval list (if any)
-  @param fld_charset           Field charset
-  @param fld_geom_type         Field geometry type (if any)
-  @param fld_vcol_info         Virtual column data
+static inline bool is_item_func(Item* x)
+{
+  return x != NULL && x->type() == Item::FUNC_ITEM;
+}
 
-  @retval
-    FALSE on success
-  @retval
-    TRUE  on error
-*/
 
-bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type,
-                        char *fld_length, char *fld_decimals,
-                        uint fld_type_modifier, Item *fld_default_value,
-                        Item *fld_on_update_value, LEX_STRING *fld_comment,
-                        char *fld_change, List<String> *fld_interval_list,
-                        CHARSET_INFO *fld_charset, uint fld_geom_type,
-			Virtual_column_info *fld_vcol_info,
-                        engine_option_value *create_opt, bool check_exists)
+bool Create_field::check(THD *thd)
 {
+  const uint conditional_type_modifiers= AUTO_INCREMENT_FLAG;
   uint sign_len, allowed_type_modifier= 0;
   ulong max_field_charlength= MAX_FIELD_CHARLENGTH;
-  const bool on_update_is_function=
-    (fld_on_update_value != NULL &&
-     fld_on_update_value->type() == Item::FUNC_ITEM);
+  DBUG_ENTER("Create_field::check");
+
+  if (vcol_info)
+  {
+    vcol_info->set_field_type(sql_type);
+    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);
+  }
 
-  DBUG_ENTER("Create_field::init()");
+  if (def)
+  {
+    /*
+      Default value should be literal => basic constants =>
+      no need fix_fields()
 
-  field= 0;
-  field_name= fld_name;
-  flags= fld_type_modifier;
-  option_list= create_opt;
+      We allow only one function as part of default value -
+      NOW() as default for TIMESTAMP and DATETIME type.
+    */
+    if (def->type() == Item::FUNC_ITEM &&
+        (static_cast<Item_func*>(def)->functype() != Item_func::NOW_FUNC ||
+         (mysql_type_to_time_type(sql_type) != MYSQL_TIMESTAMP_DATETIME) ||
+         def->decimals < length))
+    {
+      my_error(ER_INVALID_DEFAULT, MYF(0), field_name);
+      DBUG_RETURN(1);
+    }
+    else if (def->type() == Item::NULL_ITEM)
+    {
+      def= 0;
+      if ((flags & (NOT_NULL_FLAG | AUTO_INCREMENT_FLAG)) == NOT_NULL_FLAG)
+      {
+	my_error(ER_INVALID_DEFAULT, MYF(0), field_name);
+	DBUG_RETURN(1);
+      }
+    }
+    else if (flags & AUTO_INCREMENT_FLAG)
+    {
+      my_error(ER_INVALID_DEFAULT, MYF(0), field_name);
+      DBUG_RETURN(1);
+    }
+  }
 
-  if (fld_default_value != NULL && fld_default_value->type() == Item::FUNC_ITEM)
+  if (is_item_func(def))
   {
     /* There is a function default for insertions. */
     def= NULL;
-    unireg_check= (on_update_is_function ?
+    unireg_check= (is_item_func(on_update) ?
                    Field::TIMESTAMP_DNUN_FIELD : // for insertions and for updates.
                    Field::TIMESTAMP_DN_FIELD);   // only for insertions.
   }
   else
   {
     /* No function default for insertions. Either NULL or a constant. */
-    def= fld_default_value;
-    if (on_update_is_function)
+    if (is_item_func(on_update))
       unireg_check= Field::TIMESTAMP_UN_FIELD; // function default for updates
     else
-      unireg_check= ((fld_type_modifier & AUTO_INCREMENT_FLAG) != 0 ?
+      unireg_check= ((flags & AUTO_INCREMENT_FLAG) ?
                      Field::NEXT_NUMBER : // Automatic increment.
                      Field::NONE);
   }
 
-  decimals= fld_decimals ? (uint)atoi(fld_decimals) : 0;
-  if (decimals >= NOT_FIXED_DEC)
+  if (on_update &&
+      (mysql_type_to_time_type(sql_type) != MYSQL_TIMESTAMP_DATETIME ||
+       on_update->decimals < length))
   {
-    my_error(ER_TOO_BIG_SCALE, MYF(0), decimals, fld_name,
-             static_cast<ulong>(NOT_FIXED_DEC - 1));
-    DBUG_RETURN(TRUE);
+    my_error(ER_INVALID_ON_UPDATE, MYF(0), field_name);
+    DBUG_RETURN(1);
   }
 
-  sql_type= fld_type;
-  length= 0;
-  change= fld_change;
-  interval= 0;
-  pack_length= key_length= 0;
-  charset= fld_charset;
-  geom_type= (Field::geometry_type) fld_geom_type;
-  interval_list.empty();
-
-  comment= *fld_comment;
-  vcol_info= fld_vcol_info;
-  create_if_not_exists= check_exists;
-  stored_in_db= TRUE;
-
   /* Initialize data for a computed field */
-  if ((uchar)fld_type == (uchar)MYSQL_TYPE_VIRTUAL)
+  if (sql_type == MYSQL_TYPE_VIRTUAL)
   {
     DBUG_ASSERT(vcol_info && vcol_info->expr_item);
     stored_in_db= vcol_info->is_stored();
@@ -9272,56 +9276,42 @@ bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type,
       Field::vcol_info. It is is always NULL for a column that is not
       computed.
     */
-    sql_type= fld_type= vcol_info->get_real_type();
+    sql_type= vcol_info->get_real_type();
   }
 
   /*
     Set NO_DEFAULT_VALUE_FLAG if this field doesn't have a default value and
     it is NOT NULL, not an AUTO_INCREMENT field and not a TIMESTAMP.
   */
-  if (!fld_default_value && !(fld_type_modifier & AUTO_INCREMENT_FLAG) &&
-      (fld_type_modifier & NOT_NULL_FLAG) && !is_timestamp_type(fld_type))
+  if (!def && unireg_check == Field::NONE &&
+      (flags & NOT_NULL_FLAG) && !is_timestamp_type(sql_type))
     flags|= NO_DEFAULT_VALUE_FLAG;
 
-  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 */
-  }
+  sign_len= flags & UNSIGNED_FLAG ? 0 : 1;
 
-  sign_len= fld_type_modifier & UNSIGNED_FLAG ? 0 : 1;
-
-  switch (fld_type) {
+  switch (sql_type) {
   case MYSQL_TYPE_TINY:
-    if (!fld_length)
+    if (!length)
       length= MAX_TINYINT_WIDTH+sign_len;
     allowed_type_modifier= AUTO_INCREMENT_FLAG;
     break;
   case MYSQL_TYPE_SHORT:
-    if (!fld_length)
+    if (!length)
       length= MAX_SMALLINT_WIDTH+sign_len;
     allowed_type_modifier= AUTO_INCREMENT_FLAG;
     break;
   case MYSQL_TYPE_INT24:
-    if (!fld_length)
+    if (!length)
       length= MAX_MEDIUMINT_WIDTH+sign_len;
     allowed_type_modifier= AUTO_INCREMENT_FLAG;
     break;
   case MYSQL_TYPE_LONG:
-    if (!fld_length)
+    if (!length)
       length= MAX_INT_WIDTH+sign_len;
     allowed_type_modifier= AUTO_INCREMENT_FLAG;
     break;
   case MYSQL_TYPE_LONGLONG:
-    if (!fld_length)
+    if (!length)
       length= MAX_BIGINT_WIDTH;
     allowed_type_modifier= AUTO_INCREMENT_FLAG;
     break;
@@ -9332,17 +9322,16 @@ bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type,
     if (length > DECIMAL_MAX_PRECISION)
     {
       my_error(ER_TOO_BIG_PRECISION, MYF(0), static_cast<int>(length),
-               fld_name, static_cast<ulong>(DECIMAL_MAX_PRECISION));
+               field_name, static_cast<ulong>(DECIMAL_MAX_PRECISION));
       DBUG_RETURN(TRUE);
     }
     if (length < decimals)
     {
-      my_error(ER_M_BIGGER_THAN_D, MYF(0), fld_name);
+      my_error(ER_M_BIGGER_THAN_D, MYF(0), field_name);
       DBUG_RETURN(TRUE);
     }
     length=
-      my_decimal_precision_to_length(length, decimals,
-                                     fld_type_modifier & UNSIGNED_FLAG);
+      my_decimal_precision_to_length(length, decimals, flags & UNSIGNED_FLAG);
     pack_length=
       my_decimal_get_binary_size(length, decimals);
     break;
@@ -9360,11 +9349,11 @@ bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type,
   case MYSQL_TYPE_LONG_BLOB:
   case MYSQL_TYPE_MEDIUM_BLOB:
   case MYSQL_TYPE_GEOMETRY:
-    if (fld_default_value)
+    if (def)
     {
       /* Allow empty as default value. */
       String str,*res;
-      res= fld_default_value->val_str(&str);
+      res= def->val_str(&str);
       /*
         A default other than '' is always an error, and any non-NULL
         specified default is an error in strict mode.
@@ -9372,7 +9361,7 @@ bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type,
       if (res->length() || thd->is_strict_mode())
       {
         my_error(ER_BLOB_CANT_HAVE_DEFAULT, MYF(0),
-                 fld_name); /* purecov: inspected */
+                 field_name); /* purecov: inspected */
         DBUG_RETURN(TRUE);
       }
       else
@@ -9383,39 +9372,21 @@ bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type,
         push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
                             ER_BLOB_CANT_HAVE_DEFAULT,
                             ER(ER_BLOB_CANT_HAVE_DEFAULT),
-                            fld_name);
+                            field_name);
       }
       def= 0;
     }
     flags|= BLOB_FLAG;
     break;
   case MYSQL_TYPE_YEAR:
-    if (!fld_length || length != 2)
+    if (!length || length != 2)
       length= 4; /* Default length */
     flags|= ZEROFILL_FLAG | UNSIGNED_FLAG;
     break;
   case MYSQL_TYPE_FLOAT:
     /* change FLOAT(precision) to FLOAT or DOUBLE */
     allowed_type_modifier= AUTO_INCREMENT_FLAG;
-    if (fld_length && !fld_decimals)
-    {
-      uint tmp_length= length;
-      if (tmp_length > PRECISION_FOR_DOUBLE)
-      {
-        my_error(ER_WRONG_FIELD_SPEC, MYF(0), fld_name);
-        DBUG_RETURN(TRUE);
-      }
-      else if (tmp_length > PRECISION_FOR_FLOAT)
-      {
-        sql_type= MYSQL_TYPE_DOUBLE;
-        length= MAX_DOUBLE_STR_LENGTH; 
-      }
-      else
-        length= MAX_FLOAT_STR_LENGTH; 
-      decimals= NOT_FIXED_DEC;
-      break;
-    }
-    if (!fld_length && !fld_decimals)
+    if (!length && !decimals)
     {
       length=  MAX_FLOAT_STR_LENGTH;
       decimals= NOT_FIXED_DEC;
@@ -9423,13 +9394,13 @@ bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type,
     if (length < decimals &&
         decimals != NOT_FIXED_DEC)
     {
-      my_error(ER_M_BIGGER_THAN_D, MYF(0), fld_name);
+      my_error(ER_M_BIGGER_THAN_D, MYF(0), field_name);
       DBUG_RETURN(TRUE);
     }
     break;
   case MYSQL_TYPE_DOUBLE:
     allowed_type_modifier= AUTO_INCREMENT_FLAG;
-    if (!fld_length && !fld_decimals)
+    if (!length && !decimals)
     {
       length= DBL_DIG+7;
       decimals= NOT_FIXED_DEC;
@@ -9437,7 +9408,7 @@ bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type,
     if (length < decimals &&
         decimals != NOT_FIXED_DEC)
     {
-      my_error(ER_M_BIGGER_THAN_D, MYF(0), fld_name);
+      my_error(ER_M_BIGGER_THAN_D, MYF(0), field_name);
       DBUG_RETURN(TRUE);
     }
     break;
@@ -9445,7 +9416,7 @@ bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type,
   case MYSQL_TYPE_TIMESTAMP2:
     if (length > MAX_DATETIME_PRECISION)
     {
-      my_error(ER_TOO_BIG_PRECISION, MYF(0), length, fld_name,
+      my_error(ER_TOO_BIG_PRECISION, MYF(0), length, field_name,
                MAX_DATETIME_PRECISION);
       DBUG_RETURN(TRUE);
     }
@@ -9463,7 +9434,7 @@ bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type,
   case MYSQL_TYPE_TIME2:
     if (length > MAX_DATETIME_PRECISION)
     {
-      my_error(ER_TOO_BIG_PRECISION, MYF(0), length, fld_name,
+      my_error(ER_TOO_BIG_PRECISION, MYF(0), length, field_name,
                MAX_DATETIME_PRECISION);
       DBUG_RETURN(TRUE);
     }
@@ -9473,50 +9444,29 @@ bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type,
   case MYSQL_TYPE_DATETIME2:
     if (length > MAX_DATETIME_PRECISION)
     {
-      my_error(ER_TOO_BIG_PRECISION, MYF(0), length, fld_name,
+      my_error(ER_TOO_BIG_PRECISION, MYF(0), length, field_name,
                MAX_DATETIME_PRECISION);
       DBUG_RETURN(TRUE);
     }
     length+= MAX_DATETIME_WIDTH + (length ? 1 : 0);
     break;
   case MYSQL_TYPE_SET:
-    {
-      pack_length= get_set_pack_length(fld_interval_list->elements);
-
-      List_iterator<String> it(*fld_interval_list);
-      String *tmp;
-      while ((tmp= it++))
-        interval_list.push_back(tmp);
-      /*
-        Set fake length to 1 to pass the below conditions.
-        Real length will be set in mysql_prepare_table()
-        when we know the character set of the column
-      */
-      length= 1;
-      break;
-    }
+    pack_length= get_set_pack_length(interval_list.elements);
+    break;
   case MYSQL_TYPE_ENUM:
-    {
-      /* Should be safe. */
-      pack_length= get_enum_pack_length(fld_interval_list->elements);
-
-      List_iterator<String> it(*fld_interval_list);
-      String *tmp;
-      while ((tmp= it++))
-        interval_list.push_back(tmp);
-      length= 1; /* See comment for MYSQL_TYPE_SET above. */
-      break;
-   }
+    /* Should be safe. */
+    pack_length= get_enum_pack_length(interval_list.elements);
+    break;
   case MYSQL_TYPE_VAR_STRING:
     DBUG_ASSERT(0);  /* Impossible. */
     break;
   case MYSQL_TYPE_BIT:
     {
-      if (!fld_length)
+      if (!length)
         length= 1;
       if (length > MAX_BIT_FIELD_LENGTH)
       {
-        my_error(ER_TOO_BIG_DISPLAYWIDTH, MYF(0), fld_name,
+        my_error(ER_TOO_BIG_DISPLAYWIDTH, MYF(0), field_name,
                  static_cast<ulong>(MAX_BIT_FIELD_LENGTH));
         DBUG_RETURN(TRUE);
       }
@@ -9525,37 +9475,35 @@ bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type,
     }
   case MYSQL_TYPE_DECIMAL:
     DBUG_ASSERT(0); /* Was obsolete */
- }
+  }
   /* Remember the value of length */
   char_length= length;
 
   if (!(flags & BLOB_FLAG) &&
-      ((length > max_field_charlength && fld_type != MYSQL_TYPE_SET &&
-        fld_type != MYSQL_TYPE_ENUM &&
-        (fld_type != MYSQL_TYPE_VARCHAR || fld_default_value)) ||
-       ((length == 0) &&
-        fld_type != MYSQL_TYPE_STRING &&
-        fld_type != MYSQL_TYPE_VARCHAR && fld_type != MYSQL_TYPE_GEOMETRY)))
-  {
-    my_error((fld_type == MYSQL_TYPE_VAR_STRING ||
-              fld_type == MYSQL_TYPE_VARCHAR ||
-              fld_type == MYSQL_TYPE_STRING) ?  ER_TOO_BIG_FIELDLENGTH :
+      ((length > max_field_charlength &&
+        (sql_type != MYSQL_TYPE_VARCHAR || def)) ||
+       (length == 0 &&
+        sql_type != MYSQL_TYPE_ENUM && sql_type != MYSQL_TYPE_SET &&
+        sql_type != MYSQL_TYPE_STRING && sql_type != MYSQL_TYPE_VARCHAR &&
+        sql_type != MYSQL_TYPE_GEOMETRY)))
+  {
+    my_error((sql_type == MYSQL_TYPE_VAR_STRING ||
+              sql_type == MYSQL_TYPE_VARCHAR ||
+              sql_type == MYSQL_TYPE_STRING) ?  ER_TOO_BIG_FIELDLENGTH :
                                                 ER_TOO_BIG_DISPLAYWIDTH,
               MYF(0),
-              fld_name, max_field_charlength); /* purecov: inspected */
+              field_name, max_field_charlength); /* purecov: inspected */
     DBUG_RETURN(TRUE);
   }
-  fld_type_modifier&= AUTO_INCREMENT_FLAG;
-  if ((~allowed_type_modifier) & fld_type_modifier)
+  if ((~allowed_type_modifier) & flags & conditional_type_modifiers)
   {
-    my_error(ER_WRONG_FIELD_SPEC, MYF(0), fld_name);
+    my_error(ER_WRONG_FIELD_SPEC, MYF(0), field_name);
     DBUG_RETURN(TRUE);
   }
 
   DBUG_RETURN(FALSE); /* success */
 }
 
-
 enum_field_types get_blob_type_from_length(ulong length)
 {
   enum_field_types type;
diff --git a/sql/field.h b/sql/field.h
index eecdd89..fdc00ed 100644
--- a/sql/field.h
+++ b/sql/field.h
@@ -2844,7 +2844,7 @@ class Create_field :public Sql_alloc
   const char *change;			// If done with alter table
   const char *after;			// Put column after this one
   LEX_STRING comment;			// Comment for field
-  Item	*def;				// Default value
+  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
@@ -2886,9 +2886,13 @@ class Create_field :public Sql_alloc
   */
   bool stored_in_db;
 
-  Create_field() :after(0), option_list(NULL), option_struct(NULL),
-                  create_if_not_exists(FALSE)
-  {}
+  Create_field() :after(0), pack_length(0), key_length(0), interval(0),
+                  field(0), option_list(NULL), option_struct(NULL),
+                  create_if_not_exists(false), stored_in_db(true)
+  {
+    interval_list.empty();
+  }
+
   Create_field(Field *field, Field *orig_field);
   /* Used to make a clone of this object for ALTER/CREATE TABLE */
   Create_field *clone(MEM_ROOT *mem_root) const;
@@ -2900,12 +2904,7 @@ class Create_field :public Sql_alloc
                           bool maybe_null, bool is_unsigned,
                           uint pack_length = ~0U);
 
-  bool init(THD *thd, char *field_name, enum_field_types type, char *length,
-            char *decimals, uint type_modifier, Item *default_value,
-            Item *on_update_value, LEX_STRING *comment, char *change,
-            List<String> *interval_list, CHARSET_INFO *cs,
-            uint uint_geom_type, Virtual_column_info *vcol_info,
-            engine_option_value *option_list, bool check_exists);
+  bool check(THD *thd);
 
   bool field_flags_are_binary()
   {
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index 296135c..4069a73 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -2239,16 +2239,6 @@ sp_head::reset_lex(THD *thd)
   sublex->trg_table_fields.empty();
   sublex->sp_lex_in_use= FALSE;
 
-  /* Reset type info. */
-
-  sublex->charset= NULL;
-  sublex->length= NULL;
-  sublex->dec= NULL;
-  sublex->interval_list.empty();
-  sublex->type= 0;
-  sublex->uint_geom_type= 0;
-  sublex->vcol_info= 0;
-
   /* Reset part of parser state which needs this. */
   thd->m_parser_state->m_yacc.reset_before_substatement();
 
@@ -2374,16 +2364,9 @@ sp_head::fill_field_definition(THD *thd, LEX *lex,
                                enum enum_field_types field_type,
                                Create_field *field_def)
 {
-  LEX_STRING cmt = { 0, 0 };
   uint unused1= 0;
 
-  if (field_def->init(thd, (char*) "", field_type, lex->length, lex->dec,
-                      lex->type, (Item*) 0, (Item*) 0, &cmt, 0,
-                      &lex->interval_list,
-                      lex->charset ? lex->charset :
-                                     thd->variables.collation_database,
-                      lex->uint_geom_type,
-		      lex->vcol_info, NULL, FALSE))
+  if (field_def->check(thd))
     return TRUE;
 
   if (field_def->interval_list.elements)
diff --git a/sql/sp_pcontext.cc b/sql/sp_pcontext.cc
index 7c44e67..7a51cff 100644
--- a/sql/sp_pcontext.cc
+++ b/sql/sp_pcontext.cc
@@ -182,13 +182,10 @@ sp_variable *sp_pcontext::find_variable(uint offset) const
 }
 
 
-sp_variable *sp_pcontext::add_variable(THD *thd,
-                                       LEX_STRING name,
-                                       enum enum_field_types type,
-                                       sp_variable::enum_mode mode)
+sp_variable *sp_pcontext::add_variable(THD *thd, LEX_STRING name)
 {
   sp_variable *p=
-    new (thd->mem_root) sp_variable(name, type,mode, current_var_count());
+    new (thd->mem_root) sp_variable(name, current_var_count());
 
   if (!p)
     return NULL;
diff --git a/sql/sp_pcontext.h b/sql/sp_pcontext.h
index 4d86231..efe9531 100644
--- a/sql/sp_pcontext.h
+++ b/sql/sp_pcontext.h
@@ -63,12 +63,11 @@ class sp_variable : public Sql_alloc
   Create_field field_def;
 
 public:
-  sp_variable(LEX_STRING _name, enum_field_types _type, enum_mode _mode,
-              uint _offset)
+  sp_variable(LEX_STRING _name, uint _offset)
    :Sql_alloc(),
     name(_name),
-    type(_type),
-    mode(_mode),
+    type(MYSQL_TYPE_NULL),
+    mode(MODE_IN),
     offset(_offset),
     default_value(NULL)
   { }
@@ -340,14 +339,9 @@ class sp_pcontext : public Sql_alloc
   ///
   /// @param thd  Thread context.
   /// @param name Name of the SP-variable.
-  /// @param type Type of the SP-variable.
-  /// @param mode Mode of the SP-variable.
   ///
   /// @return instance of newly added SP-variable.
-  sp_variable *add_variable(THD *thd,
-                            LEX_STRING name,
-                            enum enum_field_types type,
-                            sp_variable::enum_mode mode);
+  sp_variable *add_variable(THD *thd, LEX_STRING name);
 
   /// Retrieve full type information about SP-variables in this parsing
   /// context and its children.
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index b18cff1..409358d 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -116,6 +116,7 @@ struct sys_var_with_base
 #if MYSQL_LEX
 #include "item_func.h"            /* Cast_target used in sql_yacc.h */
 #include "sql_get_diagnostics.h"  /* Types used in sql_yacc.h */
+#include "sp_pcontext.h"
 #include "sql_yacc.h"
 #define LEX_YYSTYPE YYSTYPE *
 #else
@@ -2353,7 +2354,10 @@ struct LEX: public Query_tables_list
   /* Query Plan Footprint of a currently running select  */
   Explain_query *explain;
 
-  char *length,*dec,*change;
+  // type information
+  char *length,*dec;
+  CHARSET_INFO *charset;
+
   LEX_STRING name;
   char *help_arg;
   char *backup_dir;				/* For RESTORE/BACKUP */
@@ -2362,18 +2366,15 @@ struct LEX: public Query_tables_list
   String *wild; /* Wildcard in SHOW {something} LIKE 'wild'*/ 
   sql_exchange *exchange;
   select_result *result;
-  Item *default_value, *on_update_value;
   LEX_STRING comment, ident;
   LEX_USER *grant_user;
   XID *xid;
   THD *thd;
-  Virtual_column_info *vcol_info;
 
   /* maintain a list of used plugins for this LEX */
   DYNAMIC_ARRAY plugins;
   plugin_ref plugins_static_buffer[INITIAL_LEX_PLUGIN_LIST_SIZE];
 
-  CHARSET_INFO *charset;
   bool text_string_is_7bit;
 
   /** SELECT of CREATE VIEW statement */
@@ -2395,7 +2396,6 @@ struct LEX: public Query_tables_list
 
   List<Key_part_spec> col_list;
   List<Key_part_spec> ref_list;
-  List<String>	      interval_list;
   List<LEX_USER>      users_list;
   List<LEX_COLUMN>    columns;
   List<Item>	      *insert_list,field_list,value_list,update_list;
@@ -2483,7 +2483,6 @@ struct LEX: public Query_tables_list
 
   uint profile_query_id;
   uint profile_options;
-  uint uint_geom_type;
   uint grant, grant_tot_col, which_columns;
   enum Foreign_key::fk_match_opt fk_match_option;
   enum Foreign_key::fk_option fk_update_opt;
@@ -2593,9 +2592,17 @@ struct LEX: public Query_tables_list
   };
 
   /**
-    Collects create options for Field and KEY
+    Collects create options for KEY
   */
-  engine_option_value *option_list, *option_list_last;
+  engine_option_value *option_list;
+
+  /**
+    Helper pointer to the end of the list when parsing options for
+      LEX::create_info.option_list (for table)
+      LEX::last_field->option_list (for fields)
+      LEX::option_list             (for indexes)
+  */
+  engine_option_value *option_list_last;
 
   /**
     During name resolution search only in the table list given by 
@@ -2775,6 +2782,10 @@ 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 set_last_field_type(enum enum_field_types type);
+  bool set_bincmp(CHARSET_INFO *cs, bool bin);
 };
 
 
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index b905d8b..cdbe173 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -7022,113 +7022,6 @@ bool mysql_test_parse_for_slave(THD *thd, char *rawbuf, uint length)
 #endif
 
 
-
-/**
-  Store field definition for create.
-
-  @return
-    Return 0 if ok
-*/
-
-bool add_field_to_list(THD *thd, LEX_STRING *field_name, enum_field_types type,
-		       char *length, char *decimals,
-		       uint type_modifier,
-		       Item *default_value, Item *on_update_value,
-                       LEX_STRING *comment,
-		       char *change,
-                       List<String> *interval_list, CHARSET_INFO *cs,
-		       uint uint_geom_type,
-		       Virtual_column_info *vcol_info,
-                       engine_option_value *create_options)
-{
-  register Create_field *new_field;
-  LEX  *lex= thd->lex;
-  uint8 datetime_precision= length ? atoi(length) : 0;
-  DBUG_ENTER("add_field_to_list");
-
-  if (check_string_char_length(field_name, "", NAME_CHAR_LEN,
-                               system_charset_info, 1))
-  {
-    my_error(ER_TOO_LONG_IDENT, MYF(0), field_name->str); /* purecov: inspected */
-    DBUG_RETURN(1);				/* purecov: inspected */
-  }
-  if (type_modifier & PRI_KEY_FLAG)
-  {
-    Key *key;
-    lex->col_list.push_back(new Key_part_spec(*field_name, 0));
-    key= new Key(Key::PRIMARY, null_lex_str,
-                      &default_key_create_info,
-                      0, lex->col_list, NULL, lex->check_exists);
-    lex->alter_info.key_list.push_back(key);
-    lex->col_list.empty();
-  }
-  if (type_modifier & (UNIQUE_FLAG | UNIQUE_KEY_FLAG))
-  {
-    Key *key;
-    lex->col_list.push_back(new Key_part_spec(*field_name, 0));
-    key= new Key(Key::UNIQUE, null_lex_str,
-                 &default_key_create_info, 0,
-                 lex->col_list, NULL, lex->check_exists);
-    lex->alter_info.key_list.push_back(key);
-    lex->col_list.empty();
-  }
-
-  if (default_value)
-  {
-    /* 
-      Default value should be literal => basic constants =>
-      no need fix_fields()
-      
-      We allow only one function as part of default value - 
-      NOW() as default for TIMESTAMP and DATETIME type.
-    */
-    if (default_value->type() == Item::FUNC_ITEM && 
-        (static_cast<Item_func*>(default_value)->functype() !=
-         Item_func::NOW_FUNC ||
-         (mysql_type_to_time_type(type) != MYSQL_TIMESTAMP_DATETIME) ||
-         default_value->decimals < datetime_precision))
-    {
-      my_error(ER_INVALID_DEFAULT, MYF(0), field_name->str);
-      DBUG_RETURN(1);
-    }
-    else if (default_value->type() == Item::NULL_ITEM)
-    {
-      default_value= 0;
-      if ((type_modifier & (NOT_NULL_FLAG | AUTO_INCREMENT_FLAG)) ==
-	  NOT_NULL_FLAG)
-      {
-	my_error(ER_INVALID_DEFAULT, MYF(0), field_name->str);
-	DBUG_RETURN(1);
-      }
-    }
-    else if (type_modifier & AUTO_INCREMENT_FLAG)
-    {
-      my_error(ER_INVALID_DEFAULT, MYF(0), field_name->str);
-      DBUG_RETURN(1);
-    }
-  }
-
-  if (on_update_value &&
-      (mysql_type_to_time_type(type) != MYSQL_TIMESTAMP_DATETIME ||
-       on_update_value->decimals < datetime_precision))
-  {
-    my_error(ER_INVALID_ON_UPDATE, MYF(0), field_name->str);
-    DBUG_RETURN(1);
-  }
-
-  if (!(new_field= new Create_field()) ||
-      new_field->init(thd, field_name->str, type, length, decimals, type_modifier,
-                      default_value, on_update_value, comment, change,
-                      interval_list, cs, uint_geom_type, vcol_info,
-                      create_options, lex->check_exists))
-    DBUG_RETURN(1);
-
-  lex->alter_info.create_list.push_back(new_field);
-  lex->last_field=new_field;
-  DBUG_RETURN(0);
-}
-
-
 /** Store position for column in ALTER TABLE .. ADD column. */
 
 void store_position_for_column(const char *name)
@@ -9060,3 +8953,18 @@ merge_charset_and_collation(CHARSET_INFO *cs, CHARSET_INFO *cl)
   }
   return cs;
 }
+
+/** find a collation with binary comparison rules
+*/
+CHARSET_INFO *find_bin_collation(CHARSET_INFO *cs)
+{
+  const char *csname= cs->csname;
+  cs= get_charset_by_csname(csname, MY_CS_BINSORT, MYF(0));
+  if (!cs)
+  {
+    char tmp[65];
+    strxnmov(tmp, sizeof(tmp)-1, csname, "_bin", NULL);
+    my_error(ER_UNKNOWN_COLLATION, MYF(0), tmp);
+  }
+  return cs;
+}
diff --git a/sql/sql_parse.h b/sql/sql_parse.h
index da024a5..0620e27 100644
--- a/sql/sql_parse.h
+++ b/sql/sql_parse.h
@@ -73,6 +73,7 @@ bool check_string_char_length(LEX_STRING *str, const char *err_msg,
                               uint max_char_length, CHARSET_INFO *cs,
                               bool no_error);
 CHARSET_INFO* merge_charset_and_collation(CHARSET_INFO *cs, CHARSET_INFO *cl);
+CHARSET_INFO *find_bin_collation(CHARSET_INFO *cs);
 bool check_host_name(LEX_STRING *str);
 bool check_identifier_name(LEX_STRING *str, uint max_char_length,
                            uint err_code, const char *param_for_err_msg);
@@ -104,16 +105,6 @@ bool append_file_to_dir(THD *thd, const char **filename_ptr,
                         const char *table_name);
 void execute_init_command(THD *thd, LEX_STRING *init_command,
                           mysql_rwlock_t *var_lock);
-bool add_field_to_list(THD *thd, LEX_STRING *field_name, enum enum_field_types type,
-		       char *length, char *decimal,
-		       uint type_modifier,
-		       Item *default_value, Item *on_update_value,
-		       LEX_STRING *comment,
-		       char *change, List<String> *interval_list,
-		       CHARSET_INFO *cs,
-		       uint uint_geom_type,
-                       Virtual_column_info *vcol_info,
-                       engine_option_value *create_options);
 bool add_to_list(THD *thd, SQL_I_List<ORDER> &list, Item *group, bool asc);
 void add_join_on(TABLE_LIST *b,Item *expr);
 void add_join_natural(TABLE_LIST *a,TABLE_LIST *b,List<String> *using_fields,
diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc
index 05e905b..2fa44bf 100644
--- a/sql/sql_plugin.cc
+++ b/sql/sql_plugin.cc
@@ -3068,7 +3068,9 @@ void plugin_thdvar_init(THD *thd)
     intern_plugin_unlock(NULL, old_table_plugin);
     intern_plugin_unlock(NULL, old_tmp_table_plugin);
     mysql_mutex_unlock(&LOCK_plugin);
-  } else {
+  }
+  else
+  {
     thd->variables.table_plugin= NULL;
     thd->variables.tmp_table_plugin= NULL;
   }
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index b991215..9a2c604 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -3251,18 +3251,10 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
     */
     sql_field->length= sql_field->char_length;
     /* Set field charset. */
-    save_cs= sql_field->charset= get_sql_field_charset(sql_field,
-                                                       create_info);
+    save_cs= sql_field->charset= get_sql_field_charset(sql_field, create_info);
     if ((sql_field->flags & BINCMP_FLAG) &&
-	!(sql_field->charset= get_charset_by_csname(sql_field->charset->csname,
-						    MY_CS_BINSORT,MYF(0))))
-    {
-      char tmp[65];
-      strmake(strmake(tmp, save_cs->csname, sizeof(tmp)-4),
-              STRING_WITH_LEN("_bin"));
-      my_error(ER_UNKNOWN_COLLATION, MYF(0), tmp);
+	!(sql_field->charset= find_bin_collation(sql_field->charset)))
       DBUG_RETURN(TRUE);
-    }
 
     /*
       Convert the default value from client character
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 632bdd2..fe23eed 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -45,7 +45,6 @@
 #include "lex_symbol.h"
 #include "item_create.h"
 #include "sp_head.h"
-#include "sp_pcontext.h"
 #include "sp_rcontext.h"
 #include "sp.h"
 #include "sql_show.h"
@@ -732,7 +731,6 @@ static bool add_create_index_prepare (LEX *lex, Table_ident *table)
   lex->alter_info.reset();
   lex->alter_info.flags= Alter_info::ALTER_ADD_INDEX;
   lex->col_list.empty();
-  lex->change= NullS;
   lex->option_list= NULL;
   return FALSE;
 }
@@ -861,6 +859,85 @@ static bool sp_create_assignment_instr(THD *thd, bool no_lookahead)
 }
 
 
+static void add_key_to_list(LEX *lex, LEX_STRING *field_name,
+                            enum Key::Keytype type)
+{
+  Key *key;
+  lex->col_list.push_back(new Key_part_spec(*field_name, 0));
+  key= new Key(type, null_lex_str,
+               &default_key_create_info, 0,
+               lex->col_list, NULL, lex->check_exists);
+  lex->alter_info.key_list.push_back(key);
+  lex->col_list.empty();
+}
+
+void LEX::init_last_field(Create_field *field, const char *name, CHARSET_INFO *cs)
+{
+  last_field= field;
+
+  field->field_name= name;
+  field->flags= 0;
+  field->def= 0;
+  field->on_update= 0;
+  field->sql_type= MYSQL_TYPE_NULL;
+  field->change= 0;
+  field->geom_type= Field::GEOM_GEOMETRY;
+  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)
+{
+  /*
+     if charset is NULL - we're parsing a field declaration.
+     we cannot call find_bin_collation for a field here, because actual
+     field charset is determined in get_sql_field_charset() much later.
+     so we only set a flag here.
+  */
+  if (!charset)
+  {
+    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;
+}
+
+#define bincmp_collation(X,Y)           \
+  do                                    \
+  {                                     \
+     if (Lex->set_bincmp(X,Y))          \
+       MYSQL_YYABORT;                   \
+  } while(0)
+
 %}
 %union {
   int  num;
@@ -886,6 +963,8 @@ static bool sp_create_assignment_instr(THD *thd, bool no_lookahead)
   enum enum_var_type var_type;
   Key::Keytype key_type;
   enum ha_key_alg key_alg;
+  enum enum_field_types field_type;
+  enum Field::geometry_type geom_type;
   handlerton *db_type;
   enum row_type row_type;
   enum ha_rkey_function ha_rkey_mode;
@@ -905,12 +984,14 @@ static bool sp_create_assignment_instr(THD *thd, bool no_lookahead)
   class sp_label *splabel;
   LEX *lex;
   class my_var *myvar;
-  sp_head *sphead;
+  class sp_head *sphead;
+  class sp_variable *spvar;
   struct p_elem_val *p_elem_value;
   enum index_hint_type index_hint;
   enum enum_filetype filetype;
   enum Foreign_key::fk_option m_fk_option;
   enum enum_yes_no_unknown m_yes_no_unk;
+  enum sp_variable::enum_mode spvar_mode;
   Diag_condition_item_name diag_condition_item_name;
   Diagnostics_information::Which_area diag_area;
   Diagnostics_information *diag_info;
@@ -1637,13 +1718,17 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
 %type <string>
         text_string hex_or_bin_String opt_gconcat_separator
 
+%type <field_type> type_with_opt_collate int_type real_type field_type
+
+%type <geom_type> spatial_type
+
 %type <num>
-        type type_with_opt_collate int_type real_type order_dir lock_option
+        order_dir lock_option
         udf_type opt_if_exists opt_local opt_table_options table_options
         table_option opt_if_not_exists create_or_replace opt_no_write_to_binlog
         opt_temporary all_or_any opt_distinct
-        opt_ignore_leaves fulltext_options spatial_type union_option
-        field_def opt_not opt_union_order_or_limit
+        opt_ignore_leaves fulltext_options union_option
+        opt_not opt_union_order_or_limit
         union_opt select_derived_init transaction_access_mode_types
         opt_natural_language_mode opt_query_expansion
         opt_ev_status opt_ev_on_completion ev_on_completion opt_ev_comment
@@ -1651,7 +1736,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
         optional_flush_tables_arguments opt_dyncol_type dyncol_type
         opt_time_precision kill_type kill_option int_num
         opt_default_time_precision
-        case_stmt_body
+        case_stmt_body opt_bin_mod
 
 /*
   Bit field of MYSQL_START_TRANS_OPT_* flags.
@@ -1755,6 +1840,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
 %type <charset>
         opt_collate
         charset_name
+        charset_or_alias
         charset_name_or_default
         old_or_new_charset_name
         old_or_new_charset_name_or_default
@@ -1797,10 +1883,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
         select_item_list select_item values_list no_braces
         opt_limit_clause delete_limit_clause fields opt_values values
         procedure_list procedure_list2 procedure_item
-        handler
+        field_def handler opt_generated_always
         opt_precision opt_ignore opt_column opt_restrict
         grant revoke set lock unlock string_list field_options field_option
-        field_opt_list opt_binary ascii unicode table_lock_list table_lock
+        field_opt_list opt_binary table_lock_list table_lock
         ref_list opt_match_clause opt_on_update_delete use
         opt_delete_options opt_delete_option varchar nchar nvarchar
         opt_outer table_list table_name table_alias_ref_list table_alias_ref
@@ -1854,12 +1940,14 @@ END_OF_INPUT
 %type <NONE> sp_proc_stmt_open sp_proc_stmt_fetch sp_proc_stmt_close
 %type <NONE> case_stmt_specification
 
-%type <num>  sp_decl_idents sp_opt_inout sp_handler_type sp_hcond_list
+%type <num>  sp_decl_idents sp_handler_type sp_hcond_list
 %type <spcondvalue> sp_cond sp_hcond sqlstate signal_value opt_signal_value
 %type <spblock> sp_decls sp_decl
 %type <lex> sp_cursor_stmt
 %type <spname> sp_name
 %type <splabel> sp_block_content
+%type <spvar> sp_param_name_and_type
+%type <spvar_mode> sp_opt_inout
 %type <index_hint> index_hint_type
 %type <num> index_hint_clause normal_join inner_join
 %type <filetype> data_or_xml
@@ -2338,7 +2426,6 @@ create:
               MYSQL_YYABORT;
             lex->alter_info.reset();
             lex->col_list.empty();
-            lex->change=NullS;
             bzero((char*) &lex->create_info,sizeof(lex->create_info));
             /*
               For CREATE TABLE we should not open the table even if it exists.
@@ -2794,33 +2881,12 @@ sp_fdparam_list:
         ;
 
 sp_fdparams:
-          sp_fdparams ',' sp_fdparam
-        | sp_fdparam
-        ;
-
-sp_init_param:
-          /* Empty */
-          {
-            LEX *lex= Lex;
-
-            lex->length= 0;
-            lex->dec= 0;
-            lex->type= 0;
-
-            lex->default_value= 0;
-            lex->on_update_value= 0;
-
-            lex->comment= null_lex_str;
-            lex->charset= NULL;
-
-            lex->interval_list.empty();
-            lex->uint_geom_type= 0;
-            lex->vcol_info= 0;
-          }
+          sp_fdparams ',' sp_param_name_and_type
+        | sp_param_name_and_type
         ;
 
-sp_fdparam:
-          ident sp_init_param type_with_opt_collate
+sp_param_name_and_type:
+          ident
           {
             LEX *lex= Lex;
             sp_pcontext *spc= lex->spcont;
@@ -2831,19 +2897,27 @@ sp_fdparam:
               MYSQL_YYABORT;
             }
 
-            sp_variable *spvar= spc->add_variable(thd,
-                                                  $1,
-                                                  (enum enum_field_types) $3,
-                                                  sp_variable::MODE_IN);
+            sp_variable *spvar= spc->add_variable(thd, $1);
 
-            if (lex->sphead->fill_field_definition(thd, lex,
-                                                   (enum enum_field_types) $3,
-                                                   &spvar->field_def))
+            lex->init_last_field(&spvar->field_def, $1.str,
+                                 thd->variables.collation_database);
+            $<spvar>$= spvar;
+          }
+          type_with_opt_collate
+          {
+            LEX *lex= Lex;
+            sp_variable *spvar= $<spvar>2;
+
+            spvar->type= $3;
+            if (lex->sphead->fill_field_definition(thd, lex, $3,
+                                                   lex->last_field))
             {
               MYSQL_YYABORT;
             }
             spvar->field_def.field_name= spvar->name.str;
             spvar->field_def.pack_flag |= FIELDFLAG_MAYBE_NULL;
+
+            $$= spvar;
           }
         ;
 
@@ -2859,30 +2933,7 @@ sp_pdparams:
         ;
 
 sp_pdparam:
-          sp_opt_inout sp_init_param ident type_with_opt_collate
-          {
-            LEX *lex= Lex;
-            sp_pcontext *spc= lex->spcont;
-
-            if (spc->find_variable($3, TRUE))
-            {
-              my_error(ER_SP_DUP_PARAM, MYF(0), $3.str);
-              MYSQL_YYABORT;
-            }
-            sp_variable *spvar= spc->add_variable(thd,
-                                                  $3,
-                                                  (enum enum_field_types) $4,
-                                                  (sp_variable::enum_mode) $1);
-
-            if (lex->sphead->fill_field_definition(thd, lex,
-                                                   (enum enum_field_types) $4,
-                                                   &spvar->field_def))
-            {
-              MYSQL_YYABORT;
-            }
-            spvar->field_def.field_name= spvar->name.str;
-            spvar->field_def.pack_flag |= FIELDFLAG_MAYBE_NULL;
-          }
+          sp_opt_inout sp_param_name_and_type { $2->mode=$1; }
         ;
 
 sp_opt_inout:
@@ -2936,9 +2987,17 @@ sp_decl:
           DECLARE_SYM sp_decl_idents
           {
             LEX *lex= Lex;
+            sp_pcontext *pctx= lex->spcont;
+
+            // get the last variable:
+            uint num_vars= pctx->context_var_count();
+            uint var_idx= pctx->var_context2runtime(num_vars - 1);
+            sp_variable *spvar= pctx->find_variable(var_idx);
 
             lex->sphead->reset_lex(thd);
-            lex->spcont->declare_var_boundary($2);
+            pctx->declare_var_boundary($2);
+            thd->lex->init_last_field(&spvar->field_def, spvar->name.str,
+                                      thd->variables.collation_database);
           }
           type_with_opt_collate
           sp_opt_default
@@ -2946,7 +3005,7 @@ sp_decl:
             LEX *lex= Lex;
             sp_pcontext *pctx= lex->spcont;
             uint num_vars= pctx->context_var_count();
-            enum enum_field_types var_type= (enum enum_field_types) $4;
+            enum enum_field_types var_type= $4;
             Item *dflt_value_item= $5;
             
             if (!dflt_value_item)
@@ -2961,12 +3020,17 @@ sp_decl:
             {
               uint var_idx= pctx->var_context2runtime(i);
               sp_variable *spvar= pctx->find_variable(var_idx);
+              bool last= i == num_vars - 1;
             
               if (!spvar)
                 MYSQL_YYABORT;
             
+              if (!last)
+                spvar->field_def= *lex->last_field;
+
               spvar->type= var_type;
               spvar->default_value= dflt_value_item;
+              spvar->field_def.field_name= spvar->name.str;
             
               if (lex->sphead->fill_field_definition(thd, lex, var_type,
                                                      &spvar->field_def))
@@ -2974,7 +3038,6 @@ sp_decl:
                 MYSQL_YYABORT;
               }
             
-              spvar->field_def.field_name= spvar->name.str;
               spvar->field_def.pack_flag |= FIELDFLAG_MAYBE_NULL;
             
               /* The last instruction is responsible for freeing LEX. */
@@ -2985,7 +3048,7 @@ sp_decl:
                                                  dflt_value_item,
                                                  var_type,
                                                  lex,
-                                                 (i == num_vars - 1));
+                                                 last);
               if (is == NULL ||
                   lex->sphead->add_instr(is))
                 MYSQL_YYABORT;
@@ -3539,10 +3602,7 @@ sp_decl_idents:
               my_error(ER_SP_DUP_VAR, MYF(0), $1.str);
               MYSQL_YYABORT;
             }
-            spc->add_variable(thd,
-                              $1,
-                              MYSQL_TYPE_DECIMAL,
-                              sp_variable::MODE_IN);
+            spc->add_variable(thd, $1);
             $$= 1;
           }
         | sp_decl_idents ',' ident
@@ -3557,10 +3617,7 @@ sp_decl_idents:
               my_error(ER_SP_DUP_VAR, MYF(0), $3.str);
               MYSQL_YYABORT;
             }
-            spc->add_variable(thd,
-                              $3,
-                              MYSQL_TYPE_DECIMAL,
-                              sp_variable::MODE_IN);
+            spc->add_variable(thd, $3);
             $$= $1 + 1;
           }
         ;
@@ -5996,56 +6053,62 @@ field_spec:
           field_ident
           {
             LEX *lex=Lex;
-            lex->length=lex->dec=0;
-            lex->type=0;
-            lex->default_value= lex->on_update_value= 0;
-            lex->comment=null_lex_str;
-            lex->charset=NULL;
-	    lex->vcol_info= 0;
-            lex->option_list= NULL;
+            Create_field *f= new Create_field();
+
+            if (check_string_char_length(&$1, "", NAME_CHAR_LEN,
+                                         system_charset_info, 1))
+            {
+              my_error(ER_TOO_LONG_IDENT, MYF(0), $1.str);
+              MYSQL_YYABORT;
+            }
+
+            if (!f)
+              MYSQL_YYABORT;
+
+            lex->init_last_field(f, $1.str, NULL);
           }
+          field_type  { Lex->set_last_field_type($3); }
           field_def
           {
             LEX *lex=Lex;
-            if (add_field_to_list(lex->thd, &$1, (enum enum_field_types) $3,
-                                  lex->length,lex->dec,lex->type,
-                                  lex->default_value, lex->on_update_value, 
-                                  &lex->comment,
-                                  lex->change,&lex->interval_list,lex->charset,
-                                  lex->uint_geom_type,
-                                  lex->vcol_info, lex->option_list))
+            Create_field *f= lex->last_field;
+
+            if (f->check(thd))
               MYSQL_YYABORT;
+
+            lex->alter_info.create_list.push_back(f);
+
+            if (f->flags & PRI_KEY_FLAG)
+              add_key_to_list(lex, &$1, Key::PRIMARY);
+            else if (f->flags & (UNIQUE_FLAG | UNIQUE_KEY_FLAG))
+              add_key_to_list(lex, &$1, Key::UNIQUE);
           }
         ;
 
 field_def:
-          type opt_attribute {}
-        | type opt_generated_always AS '(' virtual_column_func ')'
-          vcol_opt_specifier
-          vcol_opt_attribute
-          {
-            $$= (enum enum_field_types)MYSQL_TYPE_VIRTUAL;
-            Lex->vcol_info->set_field_type((enum enum_field_types) $1);
-          }
+          opt_attribute
+        | opt_generated_always AS
+          '(' virtual_column_func ')'
+          vcol_opt_specifier vcol_opt_attribute
         ;
 
 opt_generated_always:
-          /* empty */
+          /* empty */ {}
         | GENERATED_SYM ALWAYS_SYM {}
         ;
 
 vcol_opt_specifier:
           /* empty */
           {
-            Lex->vcol_info->set_stored_in_db_flag(FALSE);
+            Lex->last_field->vcol_info->set_stored_in_db_flag(FALSE);
           }
         | VIRTUAL_SYM
           {
-            Lex->vcol_info->set_stored_in_db_flag(FALSE);
+            Lex->last_field->vcol_info->set_stored_in_db_flag(FALSE);
           }
         | PERSISTENT_SYM
           {
-            Lex->vcol_info->set_stored_in_db_flag(TRUE);
+            Lex->last_field->vcol_info->set_stored_in_db_flag(TRUE);
           }
         ;
 
@@ -6063,16 +6126,16 @@ vcol_attribute:
           UNIQUE_SYM
           {
             LEX *lex=Lex;
-            lex->type|= UNIQUE_FLAG;
+            lex->last_field->flags|= UNIQUE_FLAG;
             lex->alter_info.flags|= Alter_info::ALTER_ADD_INDEX;
           }
         | UNIQUE_SYM KEY_SYM
           {
             LEX *lex=Lex;
-            lex->type|= UNIQUE_KEY_FLAG;
+            lex->last_field->flags|= UNIQUE_FLAG;
             lex->alter_info.flags|= Alter_info::ALTER_ADD_INDEX;
           }
-        | COMMENT_SYM TEXT_STRING_sys { Lex->comment= $2; }
+        | COMMENT_SYM TEXT_STRING_sys { Lex->last_field->comment= $2; }
         ;
 
 parse_vcol_expr:
@@ -6094,23 +6157,41 @@ parse_vcol_expr:
 virtual_column_func:
           remember_name expr remember_end
           {
-            Lex->vcol_info= new Virtual_column_info();
-            if (!Lex->vcol_info)
+            Virtual_column_info *v= new Virtual_column_info();
+            if (!v)
             {
               mem_alloc_error(sizeof(Virtual_column_info));
               MYSQL_YYABORT;
             }
             uint expr_len= (uint)($3 - $1) - 1;
-            Lex->vcol_info->expr_str.str= (char* ) thd->memdup($1 + 1, expr_len);
-            Lex->vcol_info->expr_str.length= expr_len;
-            Lex->vcol_info->expr_item= $2;
+            v->expr_str.str= (char* ) thd->memdup($1 + 1, expr_len);
+            v->expr_str.length= expr_len;
+            v->expr_item= $2;
+            Lex->last_field->vcol_info= v;
           }
         ;
 
-type:
+field_type:
           int_type opt_field_length field_options { $$=$1; }
         | real_type opt_precision field_options { $$=$1; }
-        | FLOAT_SYM float_options field_options { $$=MYSQL_TYPE_FLOAT; }
+        | FLOAT_SYM float_options field_options
+          {
+            $$=MYSQL_TYPE_FLOAT;
+            if (Lex->length && !Lex->dec)
+            {
+              int err;
+              ulonglong tmp_length= my_strtoll10(Lex->length, NULL, &err);
+              if (err || tmp_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)
+                $$= MYSQL_TYPE_DOUBLE;
+              Lex->length= 0;
+            }
+          }
         | BIT_SYM
           {
             Lex->length= (char*) "1";
@@ -6142,13 +6223,13 @@ type:
         | nchar field_length opt_bin_mod
           {
             $$=MYSQL_TYPE_STRING;
-            Lex->charset=national_charset_info;
+            bincmp_collation(national_charset_info, $3);
           }
         | nchar opt_bin_mod
           {
             Lex->length= (char*) "1";
             $$=MYSQL_TYPE_STRING;
-            Lex->charset=national_charset_info;
+            bincmp_collation(national_charset_info, $2);
           }
         | BINARY field_length
           {
@@ -6168,7 +6249,7 @@ type:
         | nvarchar field_length opt_bin_mod
           {
             $$= MYSQL_TYPE_VARCHAR;
-            Lex->charset=national_charset_info;
+            bincmp_collation(national_charset_info, $3);
           }
         | VARBINARY field_length
           {
@@ -6208,9 +6289,9 @@ type:
               /* 
                 Unlike other types TIMESTAMP fields are NOT NULL by default.
               */
-              Lex->type|= NOT_NULL_FLAG;
-              $$= opt_mysql56_temporal_format ?
-                  MYSQL_TYPE_TIMESTAMP2 : MYSQL_TYPE_TIMESTAMP;
+              Lex->last_field->flags|= NOT_NULL_FLAG;
+              $$= opt_mysql56_temporal_format ? MYSQL_TYPE_TIMESTAMP2
+                                              : MYSQL_TYPE_TIMESTAMP;
             }
           }
         | DATETIME opt_field_length
@@ -6230,7 +6311,7 @@ type:
           {
 #ifdef HAVE_SPATIAL
             Lex->charset=&my_charset_bin;
-            Lex->uint_geom_type= (uint)$1;
+            Lex->last_field->geom_type= $1;
             $$=MYSQL_TYPE_GEOMETRY;
 #else
             my_error(ER_FEATURE_DISABLED, MYF(0),
@@ -6269,20 +6350,16 @@ type:
           { $$=MYSQL_TYPE_NEWDECIMAL;}
         | FIXED_SYM float_options field_options
           { $$=MYSQL_TYPE_NEWDECIMAL;}
-        | ENUM
-          {Lex->interval_list.empty();}
-          '(' string_list ')' opt_binary
+        | ENUM '(' string_list ')' opt_binary
           { $$=MYSQL_TYPE_ENUM; }
-        | SET
-          { Lex->interval_list.empty();}
-          '(' string_list ')' opt_binary
+        | SET '(' string_list ')' opt_binary
           { $$=MYSQL_TYPE_SET; }
         | LONG_SYM opt_binary
           { $$=MYSQL_TYPE_MEDIUM_BLOB; }
         | SERIAL_SYM
           {
             $$=MYSQL_TYPE_LONGLONG;
-            Lex->type|= (AUTO_INCREMENT_FLAG | NOT_NULL_FLAG | UNSIGNED_FLAG |
+            Lex->last_field->flags|= (AUTO_INCREMENT_FLAG | NOT_NULL_FLAG | UNSIGNED_FLAG |
               UNIQUE_FLAG);
           }
         ;
@@ -6375,8 +6452,8 @@ field_opt_list:
 
 field_option:
           SIGNED_SYM {}
-        | UNSIGNED { Lex->type|= UNSIGNED_FLAG;}
-        | ZEROFILL { Lex->type|= UNSIGNED_FLAG | ZEROFILL_FLAG; }
+        | UNSIGNED { Lex->last_field->flags|= UNSIGNED_FLAG;}
+        | ZEROFILL { Lex->last_field->flags|= UNSIGNED_FLAG | ZEROFILL_FLAG; }
         ;
 
 field_length:
@@ -6406,42 +6483,42 @@ opt_attribute_list:
         ;
 
 attribute:
-          NULL_SYM { Lex->type&= ~ NOT_NULL_FLAG; }
-        | not NULL_SYM { Lex->type|= NOT_NULL_FLAG; }
-        | DEFAULT now_or_signed_literal { Lex->default_value=$2; }
+          NULL_SYM { Lex->last_field->flags&= ~ NOT_NULL_FLAG; }
+        | not NULL_SYM { Lex->last_field->flags|= NOT_NULL_FLAG; }
+        | DEFAULT now_or_signed_literal { Lex->last_field->def= $2; }
         | ON UPDATE_SYM NOW_SYM opt_default_time_precision
           {
             Item *item= new (thd->mem_root) Item_func_now_local($4);
             if (item == NULL)
               MYSQL_YYABORT;
-            Lex->on_update_value= item;
+            Lex->last_field->on_update= item;
           }
-        | AUTO_INC { Lex->type|= AUTO_INCREMENT_FLAG | NOT_NULL_FLAG; }
+        | AUTO_INC { Lex->last_field->flags|= AUTO_INCREMENT_FLAG | NOT_NULL_FLAG; }
         | SERIAL_SYM DEFAULT VALUE_SYM
           { 
             LEX *lex=Lex;
-            lex->type|= AUTO_INCREMENT_FLAG | NOT_NULL_FLAG | UNIQUE_FLAG;
+            lex->last_field->flags|= AUTO_INCREMENT_FLAG | NOT_NULL_FLAG | UNIQUE_FLAG;
             lex->alter_info.flags|= Alter_info::ALTER_ADD_INDEX;
           }
         | opt_primary KEY_SYM
           {
             LEX *lex=Lex;
-            lex->type|= PRI_KEY_FLAG | NOT_NULL_FLAG;
+            lex->last_field->flags|= PRI_KEY_FLAG | NOT_NULL_FLAG;
             lex->alter_info.flags|= Alter_info::ALTER_ADD_INDEX;
           }
         | UNIQUE_SYM
           {
             LEX *lex=Lex;
-            lex->type|= UNIQUE_FLAG; 
+            lex->last_field->flags|= UNIQUE_FLAG;
             lex->alter_info.flags|= Alter_info::ALTER_ADD_INDEX;
           }
         | UNIQUE_SYM KEY_SYM
           {
             LEX *lex=Lex;
-            lex->type|= UNIQUE_KEY_FLAG; 
+            lex->last_field->flags|= UNIQUE_KEY_FLAG;
             lex->alter_info.flags|= Alter_info::ALTER_ADD_INDEX; 
           }
-        | COMMENT_SYM TEXT_STRING_sys { Lex->comment= $2; }
+        | COMMENT_SYM TEXT_STRING_sys { Lex->last_field->comment= $2; }
         | COLLATE_SYM collation_name
           {
             if (Lex->charset && !my_charset_same(Lex->charset,$2))
@@ -6452,52 +6529,46 @@ attribute:
             }
             else
             {
-              Lex->charset=$2;
+              Lex->last_field->charset= $2;
             }
           }
         | IDENT_sys equal TEXT_STRING_sys
           {
             new (thd->mem_root)
-              engine_option_value($1, $3, true, &Lex->option_list,
+              engine_option_value($1, $3, true, &Lex->last_field->option_list,
                                   &Lex->option_list_last);
           }
         | IDENT_sys equal ident
           {
             new (thd->mem_root)
-              engine_option_value($1, $3, false, &Lex->option_list,
+              engine_option_value($1, $3, false, &Lex->last_field->option_list,
                                   &Lex->option_list_last);
           }
         | IDENT_sys equal real_ulonglong_num
           {
             new (thd->mem_root)
-              engine_option_value($1, $3, &Lex->option_list,
+              engine_option_value($1, $3, &Lex->last_field->option_list,
                                   &Lex->option_list_last, thd->mem_root);
           }
         | IDENT_sys equal DEFAULT
           {
             new (thd->mem_root)
-              engine_option_value($1, &Lex->option_list, &Lex->option_list_last);
+              engine_option_value($1, &Lex->last_field->option_list, &Lex->option_list_last);
           }
         ;
 
 
 type_with_opt_collate:
-        type opt_collate
+        field_type opt_collate
         {
           $$= $1;
 
-          if (Lex->charset) /* Lex->charset is scanned in "type" */
+          if ($2)
           {
             if (!(Lex->charset= merge_charset_and_collation(Lex->charset, $2)))
               MYSQL_YYABORT;
           }
-          else if ($2)
-          {
-            my_error(ER_NOT_SUPPORTED_YET, MYF(0),
-                     "COLLATE with no CHARACTER SET "
-                     "in SP parameters, RETURNS, DECLARE");
-            MYSQL_YYABORT;
-          }
+          Lex->set_last_field_type($1);
         }
         ;
 
@@ -6581,62 +6652,30 @@ opt_default:
         | DEFAULT {}
         ;
 
-
-ascii:
-          ASCII_SYM { Lex->charset= &my_charset_latin1; }
-        | BINARY ASCII_SYM
+charset_or_alias:
+          charset charset_name { $$= $2; }
+        | ASCII_SYM { $$= &my_charset_latin1; }
+        | UNICODE_SYM
           {
-            Lex->charset= &my_charset_latin1_bin;
-          }
-        | ASCII_SYM BINARY
-          {
-            Lex->charset= &my_charset_latin1_bin;
-          }
-        ;
-
-unicode:
-          UNICODE_SYM
-          {
-            if (!(Lex->charset=get_charset_by_csname("ucs2",
-                                                     MY_CS_PRIMARY,MYF(0))))
+            if (!($$= get_charset_by_csname("ucs2", MY_CS_PRIMARY,MYF(0))))
             {
               my_error(ER_UNKNOWN_CHARACTER_SET, MYF(0), "ucs2");
               MYSQL_YYABORT;
             }
           }
-        | UNICODE_SYM BINARY
-          {
-            if (!(Lex->charset= mysqld_collation_get_by_name("ucs2_bin")))
-              MYSQL_YYABORT;
-          }
-        | BINARY UNICODE_SYM
-          {
-            if (!(Lex->charset= mysqld_collation_get_by_name("ucs2_bin")))
-              MYSQL_YYABORT;
-          }
         ;
 
 opt_binary:
-          /* empty */ { Lex->charset=NULL; }
-        | ascii
-        | unicode
-        | BYTE_SYM { Lex->charset=&my_charset_bin; }
-        | charset charset_name opt_bin_mod { Lex->charset=$2; }
-        | BINARY
-          {
-            Lex->charset= NULL;
-            Lex->type|= BINCMP_FLAG;
-          }
-        | BINARY charset charset_name
-          {
-            Lex->charset= $3;
-            Lex->type|= BINCMP_FLAG;
-          }
+          /* empty */             { bincmp_collation(NULL, false); }
+        | BYTE_SYM                { bincmp_collation(&my_charset_bin, false); }
+        | charset_or_alias opt_bin_mod { bincmp_collation($1, $2); }
+        | BINARY                  { bincmp_collation(NULL, true); }
+        | BINARY charset_or_alias { bincmp_collation($2, true); }
         ;
 
 opt_bin_mod:
-          /* empty */ { }
-        | BINARY { Lex->type|= BINCMP_FLAG; }
+          /* empty */   { $$= false; }
+        | BINARY        { $$= true; }
         ;
 
 ws_nweights:
@@ -7007,8 +7046,8 @@ opt_component:
         ;
 
 string_list:
-          text_string { Lex->interval_list.push_back($1); }
-        | string_list ',' text_string { Lex->interval_list.push_back($3); };
+          text_string { Lex->last_field->interval_list.push_back($1); }
+        | string_list ',' text_string { Lex->last_field->interval_list.push_back($3); };
 
 /*
 ** Alter table
@@ -7479,7 +7518,6 @@ add_column:
           ADD opt_column opt_if_not_exists
           {
             LEX *lex=Lex;
-            lex->change=0;
             lex->alter_info.flags|= Alter_info::ALTER_ADD_COLUMN;
           }
         ;
@@ -7499,44 +7537,17 @@ alter_list_item:
             Lex->alter_info.flags|= Alter_info::ALTER_ADD_COLUMN |
                                     Alter_info::ALTER_ADD_INDEX;
           }
-        | CHANGE opt_column opt_if_exists field_ident
-          {
-            LEX *lex=Lex;
-            lex->change= $4.str;
-            lex->alter_info.flags|= Alter_info::ALTER_CHANGE_COLUMN;
-            lex->option_list= NULL;
-          }
-          field_spec opt_place
+        | CHANGE opt_column opt_if_exists field_ident field_spec opt_place
           {
+            Lex->alter_info.flags|= Alter_info::ALTER_CHANGE_COLUMN;
             Lex->create_last_non_select_table= Lex->last_table();
+            Lex->last_field->change= $4.str;
           }
-        | MODIFY_SYM opt_column opt_if_exists field_ident
-          {
-            LEX *lex=Lex;
-            lex->length=lex->dec=0; lex->type=0;
-            lex->default_value= lex->on_update_value= 0;
-            lex->comment=null_lex_str;
-            lex->charset= NULL;
-            lex->alter_info.flags|= Alter_info::ALTER_CHANGE_COLUMN;
-	    lex->vcol_info= 0;
-            lex->option_list= NULL;
-          }
-          field_def
-          {
-            LEX *lex=Lex;
-            if (add_field_to_list(lex->thd,&$4,
-                                  (enum enum_field_types) $6,
-                                  lex->length,lex->dec,lex->type,
-                                  lex->default_value, lex->on_update_value,
-                                  &lex->comment,
-                                  $4.str, &lex->interval_list, lex->charset,
-                                  lex->uint_geom_type,
-                                  lex->vcol_info, lex->option_list))
-              MYSQL_YYABORT;
-          }
-          opt_place
+        | MODIFY_SYM opt_column opt_if_exists field_spec opt_place
           {
+            Lex->alter_info.flags|= Alter_info::ALTER_CHANGE_COLUMN;
             Lex->create_last_non_select_table= Lex->last_table();
+            Lex->last_field->change= Lex->last_field->field_name;
           }
         | DROP opt_column opt_if_exists field_ident opt_restrict
           {
@@ -10358,7 +10369,9 @@ in_sum_expr:
 cast_type:
           BINARY opt_field_length
           { $$=ITEM_CAST_CHAR; Lex->charset= &my_charset_bin; Lex->dec= 0; }
-        | CHAR_SYM opt_field_length opt_binary
+        | CHAR_SYM opt_field_length
+          { Lex->charset= thd->variables.collation_connection; }
+          opt_binary
           { $$=ITEM_CAST_CHAR; Lex->dec= 0; }
         | NCHAR_SYM opt_field_length
           { $$=ITEM_CAST_CHAR; Lex->charset= national_charset_info; Lex->dec=0; }
@@ -13190,8 +13203,7 @@ opt_ignore_lines:
         ;
 
 lines_or_rows:
-        LINES { }
-
+          LINES { }
         | ROWS_SYM { }
         ;
 
@@ -16164,31 +16176,13 @@ sf_tail:
           RETURNS_SYM /* $9 */
           { /* $10 */
             LEX *lex= Lex;
-            lex->charset= NULL;
-            lex->length= lex->dec= NULL;
-            lex->interval_list.empty();
-            lex->type= 0;
-            lex->vcol_info= 0;
+            lex->init_last_field(&lex->sphead->m_return_field_def, NULL,
+                                 thd->variables.collation_database);
           }
           type_with_opt_collate /* $11 */
           { /* $12 */
-            LEX *lex= Lex;
-            sp_head *sp= lex->sphead;
-            /*
-              This was disabled in 5.1.12. See bug #20701
-              When collation support in SP is implemented, then this test
-              should be removed.
-            */
-            if (($11 == MYSQL_TYPE_STRING || $11 == MYSQL_TYPE_VARCHAR)
-                && (lex->type & BINCMP_FLAG))
-            {
-              my_error(ER_NOT_SUPPORTED_YET, MYF(0), "return value collation");
-              MYSQL_YYABORT;
-            }
-
-            if (sp->fill_field_definition(thd, lex,
-                                          (enum enum_field_types) $11,
-                                          &sp->m_return_field_def))
+            if (Lex->sphead->fill_field_definition(thd, Lex, $11,
+                                                   Lex->last_field))
               MYSQL_YYABORT;
           }
           sp_c_chistics /* $13 */
diff --git a/sql/table.cc b/sql/table.cc
index 6ac4544..d163282 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -2395,6 +2395,7 @@ bool unpack_vcol_info_from_frm(THD *thd,
   Query_arena *backup_stmt_arena_ptr;
   Query_arena backup_arena;
   Query_arena *vcol_arena= 0;
+  Create_field vcol_storage; // placeholder for vcol_info
   Parser_state parser_state;
   LEX *old_lex= thd->lex;
   LEX lex;
@@ -2458,7 +2459,8 @@ bool unpack_vcol_info_from_frm(THD *thd,
   if (init_lex_with_single_table(thd, table, &lex))
     goto err;
 
-  thd->lex->parse_vcol_expr= TRUE;
+  lex.parse_vcol_expr= TRUE;
+  lex.last_field= &vcol_storage;
 
   /* 
     Step 3: Use the parser to build an Item object from vcol_expr_str.
@@ -2468,7 +2470,7 @@ bool unpack_vcol_info_from_frm(THD *thd,
     goto err;
   }
   /* From now on use vcol_info generated by the parser. */
-  field->vcol_info= thd->lex->vcol_info;
+  field->vcol_info= vcol_storage.vcol_info;
 
   /* Validate the Item tree. */
   if (fix_vcol_expr(thd, table, field))
diff --git a/storage/perfschema/pfs_digest.cc b/storage/perfschema/pfs_digest.cc
index bafc22e..1fbf72c 100644
--- a/storage/perfschema/pfs_digest.cc
+++ b/storage/perfschema/pfs_digest.cc
@@ -22,6 +22,8 @@
   This code needs extra visibility in the lexer structures
 */
 
+#define MYSQL_LEX 1
+
 #include "my_global.h"
 #include "my_sys.h"
 #include "pfs_instr.h"
@@ -35,7 +37,6 @@
 #include <string.h>
 
 /* Generated code */
-#include "../sql/sql_yacc.h"
 #include "../storage/perfschema/pfs_lex_token.h"
 
 /* Name pollution from sql/sql_lex.h */


More information about the commits mailing list