← Back to team overview

maria-developers team mailing list archive

Added create options for table fields and keys (MWL#43).

 

Hi!

Here is version for maria 5.2

test create_options_example.test is quite strange, test on having
EXAMPLE engine fails (the same for plugin.test and plugin_load.test, but
if remove the "have" test and be sure that EXAMPLE is built in it will
work (I will look at it later).


=== modified file 'include/my_base.h'
--- include/my_base.h	2009-09-07 20:50:10 +0000
+++ include/my_base.h	2009-12-01 20:45:53 +0000
@@ -314,6 +314,8 @@
 #define HA_OPTION_RELIES_ON_SQL_LAYER   512
 #define HA_OPTION_NULL_FIELDS		1024
 #define HA_OPTION_PAGE_CHECKSUM		2048
+/* .frm has extra create options in linked-list format */
+#define HA_OPTION_TEXT_CREATE_OPTIONS   (1 << 14)
 #define HA_OPTION_TEMP_COMPRESS_RECORD  (1L << 15)      /* set by isamchk */
 #define HA_OPTION_READ_ONLY_DATA        (1L << 16)      /* Set by isamchk */
 #define HA_OPTION_NO_CHECKSUM           (1L << 17)

=== modified file 'libmysqld/CMakeLists.txt'
--- libmysqld/CMakeLists.txt	2009-10-03 19:24:13 +0000
+++ libmysqld/CMakeLists.txt	2009-12-01 20:45:53 +0000
@@ -136,7 +136,8 @@
            ../sql/strfunc.cc ../sql/table.cc ../sql/thr_malloc.cc
            ../sql/time.cc ../sql/tztime.cc ../sql/uniques.cc ../sql/unireg.cc
            ../sql/partition_info.cc ../sql/sql_connect.cc 
-           ../sql/scheduler.cc ../sql/event_parse_data.cc
+           ../sql/scheduler.cc ../sql/event_parse_data.cc 
+	   ../sql/sql_create_options.cc
            ${GEN_SOURCES}
            ${LIB_SOURCES})
 

=== modified file 'libmysqld/Makefile.am'
--- libmysqld/Makefile.am	2009-10-30 18:50:56 +0000
+++ libmysqld/Makefile.am	2009-12-01 20:45:53 +0000
@@ -74,7 +74,7 @@
 	sp_head.cc sp_pcontext.cc sp.cc sp_cache.cc sp_rcontext.cc \
 	parse_file.cc sql_view.cc sql_trigger.cc my_decimal.cc \
 	rpl_filter.cc sql_partition.cc sql_builtin.cc sql_plugin.cc \
-	sql_tablespace.cc \
+	sql_tablespace.cc sql_create_options.cc \
 	rpl_injector.cc my_user.c partition_info.cc \
 	sql_servers.cc event_parse_data.cc opt_table_elimination.cc
 

=== added file 'mysql-test/r/create_options.result'
--- mysql-test/r/create_options.result	1970-01-01 00:00:00 +0000
+++ mysql-test/r/create_options.result	2009-12-01 21:47:04 +0000
@@ -0,0 +1,143 @@
+drop table if exists t1;
+create table t1 (a int fkey1=v1, key akey (a) kkey1=v1) tkey1=1v1 tkey1=1v2 TKEY1=DEFAULT
+tkey2=2v1 tkey3=3v1;
+Warnings:
+Warning	1648	Unused option 'tkey2' (value '2v1')
+Warning	1648	Unused option 'tkey3' (value '3v1')
+Warning	1649	Unused option 'fkey1' (value 'v1') of field 'a'
+Warning	1650	Unused option 'kkey1' (value 'v1') of key 'akey'
+alter table t1 change a a int `fkey1`='v1';
+alter table t1 change a a int fkey1=v2;
+Warnings:
+Warning	1648	Unused option 'tkey2' (value '2v1')
+Warning	1648	Unused option 'tkey3' (value '3v1')
+Warning	1649	Unused option 'fkey1' (value 'v2') of field 'a'
+Warning	1650	Unused option 'kkey1' (value 'v1') of key 'akey'
+alter table t1 add column b int;
+Warnings:
+Warning	1648	Unused option 'tkey3' (value '3v1')
+Warning	1648	Unused option 'tkey2' (value '2v1')
+Warning	1649	Unused option 'fkey1' (value 'v2') of field 'a'
+Warning	1650	Unused option 'kkey1' (value 'v1') of key 'akey'
+show create table t1;
+Table	Create Table
+t1	CREATE TABLE `t1` (
+  `a` int(11) DEFAULT NULL `fkey1`='v2',
+  `b` int(11) DEFAULT NULL,
+  KEY `akey` (`a`) `kkey1`='v1'
+) ENGINE=MyISAM DEFAULT CHARSET=latin1 `tkey3`='3v1' `tkey2`='2v1'
+alter table t1 add key bkey (b) kkey2=v1;
+Warnings:
+Warning	1648	Unused option 'tkey2' (value '2v1')
+Warning	1648	Unused option 'tkey3' (value '3v1')
+Warning	1649	Unused option 'fkey1' (value 'v2') of field 'a'
+Warning	1650	Unused option 'kkey1' (value 'v1') of key 'akey'
+Warning	1650	Unused option 'kkey2' (value 'v1') of key 'bkey'
+show create table t1;
+Table	Create Table
+t1	CREATE TABLE `t1` (
+  `a` int(11) DEFAULT NULL `fkey1`='v2',
+  `b` int(11) DEFAULT NULL,
+  KEY `akey` (`a`) `kkey1`='v1',
+  KEY `bkey` (`b`) `kkey2`='v1'
+) ENGINE=MyISAM DEFAULT CHARSET=latin1 `tkey2`='2v1' `tkey3`='3v1'
+alter table t1 add column c int fkey1=v1 fkey2=v2;
+Warnings:
+Warning	1648	Unused option 'tkey3' (value '3v1')
+Warning	1648	Unused option 'tkey2' (value '2v1')
+Warning	1649	Unused option 'fkey1' (value 'v2') of field 'a'
+Warning	1649	Unused option 'fkey2' (value 'v2') of field 'c'
+Warning	1649	Unused option 'fkey1' (value 'v1') of field 'c'
+Warning	1650	Unused option 'kkey1' (value 'v1') of key 'akey'
+Warning	1650	Unused option 'kkey2' (value 'v1') of key 'bkey'
+show create table t1;
+Table	Create Table
+t1	CREATE TABLE `t1` (
+  `a` int(11) DEFAULT NULL `fkey1`='v2',
+  `b` int(11) DEFAULT NULL,
+  `c` int(11) DEFAULT NULL `fkey2`='v2' `fkey1`='v1',
+  KEY `akey` (`a`) `kkey1`='v1',
+  KEY `bkey` (`b`) `kkey2`='v1'
+) ENGINE=MyISAM DEFAULT CHARSET=latin1 `tkey3`='3v1' `tkey2`='2v1'
+alter table t1 add key ckey (c) kkey3=v1 kkey4=v1;
+Warnings:
+Warning	1648	Unused option 'tkey2' (value '2v1')
+Warning	1648	Unused option 'tkey3' (value '3v1')
+Warning	1649	Unused option 'fkey1' (value 'v2') of field 'a'
+Warning	1649	Unused option 'fkey1' (value 'v1') of field 'c'
+Warning	1649	Unused option 'fkey2' (value 'v2') of field 'c'
+Warning	1650	Unused option 'kkey1' (value 'v1') of key 'akey'
+Warning	1650	Unused option 'kkey2' (value 'v1') of key 'bkey'
+Warning	1650	Unused option 'kkey4' (value 'v1') of key 'ckey'
+Warning	1650	Unused option 'kkey3' (value 'v1') of key 'ckey'
+show create table t1;
+Table	Create Table
+t1	CREATE TABLE `t1` (
+  `a` int(11) DEFAULT NULL `fkey1`='v2',
+  `b` int(11) DEFAULT NULL,
+  `c` int(11) DEFAULT NULL `fkey1`='v1' `fkey2`='v2',
+  KEY `akey` (`a`) `kkey1`='v1',
+  KEY `bkey` (`b`) `kkey2`='v1',
+  KEY `ckey` (`c`) `kkey4`='v1' `kkey3`='v1'
+) ENGINE=MyISAM DEFAULT CHARSET=latin1 `tkey2`='2v1' `tkey3`='3v1'
+alter table t1 drop b;
+Warnings:
+Warning	1648	Unused option 'tkey3' (value '3v1')
+Warning	1648	Unused option 'tkey2' (value '2v1')
+Warning	1649	Unused option 'fkey1' (value 'v2') of field 'a'
+Warning	1649	Unused option 'fkey2' (value 'v2') of field 'c'
+Warning	1649	Unused option 'fkey1' (value 'v1') of field 'c'
+Warning	1650	Unused option 'kkey1' (value 'v1') of key 'akey'
+Warning	1650	Unused option 'kkey3' (value 'v1') of key 'ckey'
+Warning	1650	Unused option 'kkey4' (value 'v1') of key 'ckey'
+show create table t1;
+Table	Create Table
+t1	CREATE TABLE `t1` (
+  `a` int(11) DEFAULT NULL `fkey1`='v2',
+  `c` int(11) DEFAULT NULL `fkey2`='v2' `fkey1`='v1',
+  KEY `akey` (`a`) `kkey1`='v1',
+  KEY `ckey` (`c`) `kkey3`='v1' `kkey4`='v1'
+) ENGINE=MyISAM DEFAULT CHARSET=latin1 `tkey3`='3v1' `tkey2`='2v1'
+alter table t1 add column b int fkey2=v1;
+Warnings:
+Warning	1648	Unused option 'tkey2' (value '2v1')
+Warning	1648	Unused option 'tkey3' (value '3v1')
+Warning	1649	Unused option 'fkey1' (value 'v2') of field 'a'
+Warning	1649	Unused option 'fkey1' (value 'v1') of field 'c'
+Warning	1649	Unused option 'fkey2' (value 'v2') of field 'c'
+Warning	1649	Unused option 'fkey2' (value 'v1') of field 'b'
+Warning	1650	Unused option 'kkey1' (value 'v1') of key 'akey'
+Warning	1650	Unused option 'kkey4' (value 'v1') of key 'ckey'
+Warning	1650	Unused option 'kkey3' (value 'v1') of key 'ckey'
+show create table t1;
+Table	Create Table
+t1	CREATE TABLE `t1` (
+  `a` int(11) DEFAULT NULL `fkey1`='v2',
+  `c` int(11) DEFAULT NULL `fkey1`='v1' `fkey2`='v2',
+  `b` int(11) DEFAULT NULL `fkey2`='v1',
+  KEY `akey` (`a`) `kkey1`='v1',
+  KEY `ckey` (`c`) `kkey4`='v1' `kkey3`='v1'
+) ENGINE=MyISAM DEFAULT CHARSET=latin1 `tkey2`='2v1' `tkey3`='3v1'
+alter table t1 add key bkey (b) kkey2=v2;
+Warnings:
+Warning	1648	Unused option 'tkey3' (value '3v1')
+Warning	1648	Unused option 'tkey2' (value '2v1')
+Warning	1649	Unused option 'fkey1' (value 'v2') of field 'a'
+Warning	1649	Unused option 'fkey2' (value 'v2') of field 'c'
+Warning	1649	Unused option 'fkey1' (value 'v1') of field 'c'
+Warning	1649	Unused option 'fkey2' (value 'v1') of field 'b'
+Warning	1650	Unused option 'kkey1' (value 'v1') of key 'akey'
+Warning	1650	Unused option 'kkey3' (value 'v1') of key 'ckey'
+Warning	1650	Unused option 'kkey4' (value 'v1') of key 'ckey'
+Warning	1650	Unused option 'kkey2' (value 'v2') of key 'bkey'
+show create table t1;
+Table	Create Table
+t1	CREATE TABLE `t1` (
+  `a` int(11) DEFAULT NULL `fkey1`='v2',
+  `c` int(11) DEFAULT NULL `fkey2`='v2' `fkey1`='v1',
+  `b` int(11) DEFAULT NULL `fkey2`='v1',
+  KEY `akey` (`a`) `kkey1`='v1',
+  KEY `ckey` (`c`) `kkey3`='v1' `kkey4`='v1',
+  KEY `bkey` (`b`) `kkey2`='v2'
+) ENGINE=MyISAM DEFAULT CHARSET=latin1 `tkey3`='3v1' `tkey2`='2v1'
+drop table t1;

=== added file 'mysql-test/r/create_options_example.result'
--- mysql-test/r/create_options_example.result	1970-01-01 00:00:00 +0000
+++ mysql-test/r/create_options_example.result	2009-12-01 21:48:28 +0000
@@ -0,0 +1,16 @@
+drop table if exists t1;
+create table t1 (a int ttt=xxx E=1, key akey (a) kkk=xxx ) E=1 ttt=xxx ttt=yyy TTT=DEFAULT mmm=CCC zzz=MMM;
+Warnings:
+Warning	1648	Unused option 'E' (value '1')
+Warning	1648	Unused option 'mmm' (value 'CCC')
+Warning	1648	Unused option 'zzz' (value 'MMM')
+Warning	1649	Unused option 'E' (value '1') of field 'a'
+Warning	1649	Unused option 'ttt' (value 'xxx') of field 'a'
+Warning	1650	Unused option 'kkk' (value 'xxx') of key 'akey'
+drop table t1;
+create table t1 (a int ttt=xxx E=1) ENGINE=EXAMPLE E=1 ttt=xxx ttt=yyy TTT=DEFAULT mmm=CCC zzz=MMM;
+Warnings:
+Warning	1648	Unused option 'mmm' (value 'CCC')
+Warning	1648	Unused option 'zzz' (value 'MMM')
+Warning	1649	Unused option 'ttt' (value 'xxx') of field 'a'
+drop table t1;

=== added file 'mysql-test/t/create_options.test'
--- mysql-test/t/create_options.test	1970-01-01 00:00:00 +0000
+++ mysql-test/t/create_options.test	2009-12-01 20:45:53 +0000
@@ -0,0 +1,24 @@
+--disable_warnings
+drop table if exists t1;
+--enable_warnings
+
+create table t1 (a int fkey1=v1, key akey (a) kkey1=v1) tkey1=1v1 tkey1=1v2 TKEY1=DEFAULT
+tkey2=2v1 tkey3=3v1;
+# the same value so no warnings (no changes)
+alter table t1 change a a int `fkey1`='v1';
+alter table t1 change a a int fkey1=v2;
+alter table t1 add column b int;
+show create table t1;
+alter table t1 add key bkey (b) kkey2=v1;
+show create table t1;
+alter table t1 add column c int fkey1=v1 fkey2=v2;
+show create table t1;
+alter table t1 add key ckey (c) kkey3=v1 kkey4=v1;
+show create table t1;
+alter table t1 drop b;
+show create table t1;
+alter table t1 add column b int fkey2=v1;
+show create table t1;
+alter table t1 add key bkey (b) kkey2=v2;
+show create table t1;
+drop table t1;

=== added file 'mysql-test/t/create_options_example-master.opt'
--- mysql-test/t/create_options_example-master.opt	1970-01-01 00:00:00 +0000
+++ mysql-test/t/create_options_example-master.opt	2009-12-01 20:45:53 +0000
@@ -0,0 +1,2 @@
+$EXAMPLE_PLUGIN_OPT
+$EXAMPLE_PLUGIN_LOAD

=== added file 'mysql-test/t/create_options_example.test'
--- mysql-test/t/create_options_example.test	1970-01-01 00:00:00 +0000
+++ mysql-test/t/create_options_example.test	2009-12-01 21:49:11 +0000
@@ -0,0 +1,16 @@
+--source include/have_example_plugin.inc
+
+--disable_warnings
+drop table if exists t1;
+--enable_warnings
+
+#All vaues with warnings
+create table t1 (a int ttt=xxx E=1, key akey (a) kkk=xxx ) E=1 ttt=xxx ttt=yyy TTT=DEFAULT mmm=CCC zzz=MMM;
+
+drop table t1;
+
+# E=1 accepted by engine
+create table t1 (a int ttt=xxx E=1) ENGINE=EXAMPLE E=1 ttt=xxx ttt=yyy TTT=DEFAULT mmm=CCC zzz=MMM;
+
+drop table t1;
+

=== modified file 'sql/CMakeLists.txt'
--- sql/CMakeLists.txt	2009-09-15 10:46:35 +0000
+++ sql/CMakeLists.txt	2009-12-01 20:45:53 +0000
@@ -76,6 +76,7 @@
                rpl_rli.cc rpl_mi.cc sql_servers.cc
                sql_connect.cc scheduler.cc 
                sql_profile.cc event_parse_data.cc opt_table_elimination.cc
+	       sql_create_options.cc
                ${PROJECT_SOURCE_DIR}/sql/sql_yacc.cc
                ${PROJECT_SOURCE_DIR}/sql/sql_yacc.h
                ${PROJECT_SOURCE_DIR}/include/mysqld_error.h

=== modified file 'sql/Makefile.am'
--- sql/Makefile.am	2009-09-15 10:46:35 +0000
+++ sql/Makefile.am	2009-12-01 20:45:53 +0000
@@ -77,7 +77,7 @@
 			sql_plugin.h authors.h event_parse_data.h \
 			event_data_objects.h event_scheduler.h \
 			sql_partition.h partition_info.h partition_element.h \
-			contributors.h sql_servers.h
+			contributors.h sql_servers.h sql_create_options.h
 
 mysqld_SOURCES =	sql_lex.cc sql_handler.cc sql_partition.cc \
 			item.cc item_sum.cc item_buff.cc item_func.cc \
@@ -122,7 +122,7 @@
 			sql_plugin.cc sql_binlog.cc \
 			sql_builtin.cc sql_tablespace.cc partition_info.cc \
 			sql_servers.cc event_parse_data.cc \
-                        opt_table_elimination.cc
+                        opt_table_elimination.cc sql_create_options.cc
 
 nodist_mysqld_SOURCES =	mini_client_errors.c pack.c client.c my_time.c my_user.c 
 

=== modified file 'sql/field.cc'
--- sql/field.cc	2009-11-10 02:32:39 +0000
+++ sql/field.cc	2009-12-01 20:50:00 +0000
@@ -1308,7 +1308,7 @@
 	     utype unireg_check_arg, const char *field_name_arg)
   :ptr(ptr_arg), null_ptr(null_ptr_arg),
    table(0), orig_table(0), table_name(0),
-   field_name(field_name_arg),
+   field_name(field_name_arg), create_options(NULL),
    key_start(0), part_of_key(0), part_of_key_not_clustered(0),
    part_of_sortkey(0), unireg_check(unireg_check_arg),
    field_length(length_arg), null_bit(null_bit_arg), 
@@ -9577,7 +9577,8 @@
                         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)
+			Virtual_column_info *fld_vcol_info,
+                        CREATE_OPTION *create_opt)
 {
   uint sign_len, allowed_type_modifier= 0;
   ulong max_field_charlength= MAX_FIELD_CHARLENGTH;
@@ -9588,6 +9589,7 @@
   field_name= fld_name;
   def= fld_default_value;
   flags= fld_type_modifier;
+  create_options= create_opt;
   unireg_check= (fld_type_modifier & AUTO_INCREMENT_FLAG ?
                  Field::NEXT_NUMBER : Field::NONE);
   decimals= fld_decimals ? (uint)atoi(fld_decimals) : 0;
@@ -10217,6 +10219,7 @@
   decimals=   old_field->decimals();
   vcol_info=  old_field->vcol_info;
   stored_in_db= old_field->stored_in_db;
+  create_options= old_field->create_options;
 
   /* Fix if the original table had 4 byte pointer blobs */
   if (flags & BLOB_FLAG)
@@ -10291,6 +10294,21 @@
 
 
 /**
+  Makes a clone of this object for ALTER/CREATE TABLE
+
+  @param mem_root        MEM_ROOT where to clone the field
+*/
+
+Create_field *Create_field::clone(MEM_ROOT *mem_root) const
+{
+  Create_field *res= new (mem_root) Create_field(*this);
+  if (res)
+    res->create_options= create_options_clone(mem_root, res->create_options);
+  return res;
+}
+
+
+/**
   maximum possible display length for blob.
 
   @return

=== modified file 'sql/field.h'
--- sql/field.h	2009-11-10 02:32:39 +0000
+++ sql/field.h	2009-12-01 21:31:21 +0000
@@ -137,6 +137,8 @@
   struct st_table *table;		// Pointer for table
   struct st_table *orig_table;		// Pointer to original table
   const char	**table_name, *field_name;
+  /** reference to the list of options or NULL */
+  CREATE_OPTION *create_options;
   LEX_STRING	comment;
   /* Field is part of the following keys */
   key_map	key_start, part_of_key, part_of_key_not_clustered;
@@ -2137,6 +2139,7 @@
   CHARSET_INFO *charset;
   Field::geometry_type geom_type;
   Field *field;				// For alter table
+  CREATE_OPTION *create_options;
 
   uint8 row,col,sc_length,interval_id;	// For rea_create_table
   uint	offset,pack_flag;
@@ -2154,11 +2157,10 @@
   */
   bool stored_in_db;
 
-  Create_field() :after(0) {}
+  Create_field() :after(0), create_options(NULL) {}
   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
-    { return new (mem_root) Create_field(*this); }
+  Create_field *clone(MEM_ROOT *mem_root) const;
   void create_length_to_internal_length(void);
 
   /* Init for a tmp table field. To be extended if need be. */
@@ -2170,8 +2172,8 @@
             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);
+            uint uint_geom_type, Virtual_column_info *vcol_info,
+            CREATE_OPTION *create_opt);
 };
 
 

=== modified file 'sql/handler.cc'
--- sql/handler.cc	2009-10-19 17:14:48 +0000
+++ sql/handler.cc	2009-12-01 20:45:53 +0000
@@ -3699,12 +3699,18 @@
                             TRUE))
     goto err;
 
+  /* Use options which we just have read */
+  create_info->create_table_options= table.s->create_table_options;
+
   if (update_create_info)
     update_create_info_from_table(create_info, &table);
 
   name= get_canonical_filename(table.file, share.path.str, name_buff);
 
   error= table.file->ha_create(name, &table, create_info);
+
+  create_options_check_unused(thd, create_info->create_table_options);
+
   VOID(closefrm(&table, 0));
   if (error)
   {

=== modified file 'sql/handler.h'
--- sql/handler.h	2009-11-12 04:31:28 +0000
+++ sql/handler.h	2009-12-01 20:45:53 +0000
@@ -915,6 +915,8 @@
   LEX_STRING connect_string;
   const char *password, *tablespace;
   LEX_STRING comment;
+  TABLE_OPTIONS create_table_options_orig;
+  TABLE_OPTIONS *create_table_options;
   const char *data_file_name, *index_file_name;
   const char *alias;
   ulonglong max_rows,min_rows;

=== modified file 'sql/mysql_priv.h'
--- sql/mysql_priv.h	2009-11-12 04:31:28 +0000
+++ sql/mysql_priv.h	2009-12-01 20:54:22 +0000
@@ -44,6 +44,7 @@
 #include "sql_plugin.h"
 #include "scheduler.h"
 #include "log_slow.h"
+#include "sql_create_options.h"
 
 class Parser_state;
 
@@ -1484,7 +1485,8 @@
 		       char *change, List<String> *interval_list,
 		       CHARSET_INFO *cs,
 		       uint uint_geom_type,
-                       Virtual_column_info *vcol_info);
+                       Virtual_column_info *vcol_info,
+                       CREATE_OPTION *create_options);
 Create_field * new_create_field(THD *thd, char *field_name, enum_field_types type,
 				char *length, char *decimals,
 				uint type_modifier, 
@@ -2596,6 +2598,7 @@
                     CHARSET_INFO *dflt_cl,
                     CHARSET_INFO **cl);
 
+
 #endif /* MYSQL_SERVER */
 extern "C" int test_if_data_home_dir(const char *dir);
 

=== modified file 'sql/share/errmsg.txt'
--- sql/share/errmsg.txt	2009-11-10 02:32:39 +0000
+++ sql/share/errmsg.txt	2009-12-01 20:55:42 +0000
@@ -6232,4 +6232,13 @@
         eng "'%s' is not yet supported for computed columns."
 
 ER_CONST_EXPR_IN_VCOL
-         eng "Constant expression in computed column function is not allowed."
+        eng "Constant expression in computed column function is not allowed."
+
+WARN_UNUSED_TABLE_OPTION
+        eng "Unused option '%-.64s' (value '%-.64s')"
+
+WARN_UNUSED_FIELD_OPTION
+        eng "Unused option '%-.64s' (value '%-.64s') of field '%-.64s'"
+
+WARN_UNUSED_KEY_OPTION
+        eng "Unused option '%-.64s' (value '%-.64s') of key '%-.64s'"

=== modified file 'sql/sp_head.cc'
--- sql/sp_head.cc	2009-10-16 22:57:48 +0000
+++ sql/sp_head.cc	2009-12-01 20:56:53 +0000
@@ -2200,7 +2200,7 @@
                       lex->charset ? lex->charset :
                                      thd->variables.collation_database,
                       lex->uint_geom_type,
-		      lex->vcol_info))
+		      lex->vcol_info, lex->create_opt))
     return TRUE;
 
   if (field_def->interval_list.elements)

=== modified file 'sql/sql_class.cc'
--- sql/sql_class.cc	2009-11-12 04:31:28 +0000
+++ sql/sql_class.cc	2009-12-01 20:45:53 +0000
@@ -108,6 +108,7 @@
   generated(rhs.generated)
 {
   list_copy_and_replace_each_value(columns, mem_root);
+  create_options= create_options_clone(mem_root, rhs.create_options);
 }
 
 /**
@@ -741,6 +742,7 @@
 
 void THD::push_internal_handler(Internal_error_handler *handler)
 {
+  DBUG_ENTER("THD::push_internal_handler");
   if (m_internal_handler)
   {
     handler->m_prev_internal_handler= m_internal_handler;
@@ -750,6 +752,7 @@
   {
     m_internal_handler= handler;
   }
+  DBUG_VOID_RETURN;
 }
 
 
@@ -773,8 +776,10 @@
 
 void THD::pop_internal_handler()
 {
+  DBUG_ENTER("THD::pop_internal_handler");
   DBUG_ASSERT(m_internal_handler != NULL);
   m_internal_handler= m_internal_handler->m_prev_internal_handler;
+  DBUG_VOID_RETURN;
 }
 
 extern "C"

=== modified file 'sql/sql_class.h'
--- sql/sql_class.h	2009-11-12 04:31:28 +0000
+++ sql/sql_class.h	2009-12-01 20:45:53 +0000
@@ -202,13 +202,14 @@
   KEY_CREATE_INFO key_create_info;
   List<Key_part_spec> columns;
   const char *name;
+  CREATE_OPTION *create_options;
   bool generated;
 
   Key(enum Keytype type_par, const char *name_arg,
       KEY_CREATE_INFO *key_info_arg,
-      bool generated_arg, List<Key_part_spec> &cols)
+      bool generated_arg, List<Key_part_spec> &cols, CREATE_OPTION *create_opt)
     :type(type_par), key_create_info(*key_info_arg), columns(cols),
-    name(name_arg), generated(generated_arg)
+    name(name_arg), create_options(create_opt), generated(generated_arg)
   {}
   Key(const Key &rhs, MEM_ROOT *mem_root);
   virtual ~Key() {}
@@ -237,7 +238,7 @@
   Foreign_key(const char *name_arg, List<Key_part_spec> &cols,
 	      Table_ident *table,   List<Key_part_spec> &ref_cols,
 	      uint delete_opt_arg, uint update_opt_arg, uint match_opt_arg)
-    :Key(FOREIGN_KEY, name_arg, &default_key_create_info, 0, cols),
+    :Key(FOREIGN_KEY, name_arg, &default_key_create_info, 0, cols, NULL),
     ref_table(table), ref_columns(ref_cols),
     delete_opt(delete_opt_arg), update_opt(update_opt_arg),
     match_opt(match_opt_arg)

=== added file 'sql/sql_create_options.cc'
--- sql/sql_create_options.cc	1970-01-01 00:00:00 +0000
+++ sql/sql_create_options.cc	2009-12-01 21:40:38 +0000
@@ -0,0 +1,601 @@
+
+#include "mysql_priv.h"
+
+static CREATE_OPTION last_option;
+
+/* Additional length of index for CREATE_OPTION_XXX types */
+static uint create_options_len[3]= {0, 2, 2};
+
+/**
+  Adds new option to this list
+
+  @param options         pointer to the list
+  @param root            memroot to allocate option
+  @param str_key         key
+  @param str_val         value
+  @param changed         pointer to variable to report changed data or NULL
+
+  @retval TRUE  error
+  @retval FALSE OK
+*/
+
+my_bool create_option_add(CREATE_OPTION **options, MEM_ROOT *root,
+                          const LEX_STRING *str_key,
+                          const LEX_STRING *str_val,
+                          my_bool *changed)
+{
+  CREATE_OPTION *opt, **i;
+  char *key, *val;
+  DBUG_ENTER("create_option_add");
+  DBUG_PRINT("enter", ("key: '%s'  value: '%s'",
+                       str_key->str, str_val->str));
+  if (changed)
+    *changed= FALSE;
+
+  /* try to find the option first */
+  for (i= options;
+       *i && my_strcasecmp(system_charset_info, str_key->str, (*i)->key.str);
+       i= &((*i)->next)) ;
+  if (str_val->str)
+  {
+    /* add / replace */
+    if (*i)
+    {
+      /* replace */
+      opt= *i;
+      if (changed && !(*changed) &&
+          (opt->val.length != str_val->length ||
+           memcmp(opt->val.str, str_val->str, str_val->length)))
+      {
+        *changed= TRUE;
+      }
+    }
+    else
+    {
+      /* add */
+      if (!(opt= (CREATE_OPTION *)alloc_root(root, sizeof(CREATE_OPTION))))
+        DBUG_RETURN(TRUE);
+      opt->next= *options;
+      *options= opt;
+      if (changed)
+        *changed= TRUE;
+    }
+    if (!changed || *changed)
+    {
+      if (!multi_alloc_root(root, &key, str_key->length + 1,
+                            &val, str_val->length + 1, NULL))
+        DBUG_RETURN(TRUE);
+      opt->key.str= (char *)memcpy(key, str_key->str,
+                                   (opt->key.length= str_key->length));
+      key[str_key->length]= '\0';
+      opt->val.str= (char *)memcpy(val, str_val->str,
+                                   (opt->val.length= str_val->length));
+      val[str_val->length]= '\0';
+      opt->used= FALSE;
+      opt->owner= NULL;
+    }
+  }
+  else
+  {
+    /* remove */
+    if (*i)
+    {
+      *i= (*i)->next;
+      if (changed)
+        *changed= TRUE;
+    }
+  }
+  DBUG_RETURN(FALSE);
+}
+
+/**
+  Creates empty fields/keys array for table create options structure
+
+  @param root            memroot where to allocate memory for this structure
+  @param n               number of fields/keys
+
+  @return pointer to arrayor NULL in case of error.
+*/
+
+CREATE_OPTION **create_create_options_array(MEM_ROOT *root, uint n)
+{
+  DBUG_ENTER("create_create_options_array");
+  DBUG_PRINT("enter", ("Number: %u", n));
+
+  CREATE_OPTION **res=
+    (CREATE_OPTION **) alloc_root(root,
+                                  sizeof(CREATE_OPTION *) * (n + 1));
+  if (!res)
+    DBUG_RETURN(NULL);
+  bzero(res, sizeof(CREATE_OPTION *) * n);
+  res[n]= &last_option;
+  DBUG_RETURN(res);
+}
+
+
+/**
+  Creates empty table create options structure
+
+  @param root            memroot where to allocate memory for this structure
+  @param fields          number of fields
+  @param keys            number of keys
+
+  @return pointer to prepared structure or NULL in case of error.
+*/
+
+TABLE_OPTIONS *create_create_options(MEM_ROOT *root, uint fields, uint keys)
+{
+  CREATE_OPTION **opts;
+  TABLE_OPTIONS *res;
+  DBUG_ENTER("create_create_options");
+  DBUG_PRINT("enter", ("fields: %u  keys: %u", fields, keys));
+
+  if (!multi_alloc_root(root, &res, sizeof(TABLE_OPTIONS),
+                        &opts, sizeof(CREATE_OPTION *) * (fields + keys + 2),
+                        NULL))
+    DBUG_RETURN(NULL);
+
+  bzero((uchar *)opts, sizeof(CREATE_OPTION *) * (fields + keys + 2));
+  res->table_opt= NULL;
+  res->field_opt= opts;
+  res->field_opt[fields]= &last_option;
+  res->key_opt= opts + fields + 1;
+  res->key_opt[keys]= &last_option;
+  DBUG_RETURN(res);
+}
+
+
+/**
+  Reads options from this buffer
+
+  @param buffer          the buffer to read from
+  @param mem_root        memroot for allocating
+  @param opt             parametes to write to
+
+  @retval TRUE  Error
+  @retval FALSE OK
+*/
+
+my_bool create_options_read(const uchar *buff, uint length, MEM_ROOT *root,
+                            TABLE_OPTIONS *opt)
+{
+  const uchar *buff_end= buff + length;
+  DBUG_ENTER("create_options_read");
+  while (buff < buff_end)
+  {
+    CREATE_OPTION *option=
+      (CREATE_OPTION *) alloc_root(root, sizeof(CREATE_OPTION));
+    CREATE_OPTION_TYPES type;
+    uint index= 0;
+    if (!option)
+      DBUG_RETURN(TRUE);
+    DBUG_ASSERT(buff + 4 <= buff_end);
+    option->val.length= uint2korr(buff);
+    option->key.length= buff[2];
+    type= (CREATE_OPTION_TYPES)buff[3];
+    switch (type) {
+    case CREATE_OPTION_FIELD:
+      index= uint2korr(buff + 4);
+      buff+= 6;
+      option->next= opt->field_opt[index];
+      opt->field_opt[index]= option;
+      break;
+    case CREATE_OPTION_KEY:
+      index= uint2korr(buff + 4);
+      buff+= 6;
+      option->next= opt->key_opt[index];
+      opt->key_opt[index]= option;
+      break;
+    case CREATE_OPTION_TABLE:
+      /* table */
+      buff+= 4;
+      option->next= opt->table_opt;
+      opt->table_opt= option;
+      break;
+    default:
+      DBUG_ASSERT(0);
+    }
+    if (!(option->key.str= strmake_root(root, (const char*)buff,
+                                        option->key.length)))
+      DBUG_RETURN(TRUE);
+    buff+= option->key.length;
+    if (!(option->val.str= strmake_root(root, (const char*)buff,
+                                        option->val.length)))
+      DBUG_RETURN(TRUE);
+    buff+= option->val.length;
+    option->used= FALSE;
+    option->owner= NULL;
+    DBUG_PRINT("info", ("type: %u index: %u  key: '%s'  value: '%s'",
+			(uint) type, (uint) index,
+                        option->key.str, option->val.str));
+  }
+  DBUG_RETURN(FALSE);
+}
+
+/**
+  Calculates length of saved image of the option lists
+
+  @param opt             list of options
+  @param extra_length    type of the record
+
+  @return length
+*/
+
+static ulong create_options_list_length(CREATE_OPTION *opt, int extra_length)
+{
+  ulong res= 0;
+  DBUG_ENTER("create_options_list_length");
+  for (; opt != NULL; opt= opt->next)
+  {
+    DBUG_PRINT("info", ("key: '%s'  value: '%s'",
+			(opt->key.str ? opt->key.str : "<NULL>"),
+			(opt->val.str ? opt->val.str : "<NULL>")));
+    DBUG_ASSERT(opt->key.length);
+    /*
+      length of disk for every record:
+      2 bytes - value length
+      1 byte  - key length
+      1 byte  - record type
+      0/2 bytes - none/key number/field number
+    */
+    res+= 2 + 1 + 1 + extra_length + opt->key.length + opt->val.length;
+  }
+  DBUG_RETURN(res);
+}
+
+/**
+  Calculates length of saved image of the all options of the table
+
+  @param opt             table of options
+
+  @return length
+*/
+
+ulong create_options_length(TABLE_OPTIONS *opt)
+{
+  CREATE_OPTION **i;
+  ulong res;
+  DBUG_ENTER("create_options_length");
+
+  res=
+    (opt->table_opt ?
+     create_options_list_length(opt->table_opt,
+                                create_options_len[CREATE_OPTION_TABLE]):
+     0);
+  if (opt->field_opt)
+  {
+    for (i= opt->field_opt; *i != &last_option; i++)
+      res+=
+        create_options_list_length(*i,
+                                   create_options_len[CREATE_OPTION_FIELD]);
+  }
+  if (opt->key_opt)
+  {
+    for (i= opt->key_opt; *i != &last_option; i++)
+      res+=
+        create_options_list_length(*i,
+                                   create_options_len[CREATE_OPTION_KEY]);
+  }
+  DBUG_RETURN(res);
+}
+
+
+/**
+  Writes list of options of given type and index to the given file
+
+  @param buff            buffer for storing options
+  @param opt             list of options
+  @param record_type     type of options (table, field, key)
+  @param index           index of option (used by fields and keys)
+
+  @return pointer on next byte after written ones
+*/
+
+static uchar *create_options_list_store(uchar *buff, CREATE_OPTION *opt,
+                                         CREATE_OPTION_TYPES record_type,
+                                         uint index)
+{
+  DBUG_ENTER("create_options_list_store");
+
+  for (; opt != NULL; opt= opt->next)
+  {
+    DBUG_PRINT("info", ("type: %u index: %u  key: '%s'  value: '%s'",
+			(uint) record_type, (uint) index,
+                        opt->key.str, opt->val.str));
+    int2store(buff, opt->val.length);
+    buff[2]= opt->key.length;
+    buff[3]= record_type;
+    buff+= 4;
+    switch (record_type) {
+    case CREATE_OPTION_FIELD:
+    case CREATE_OPTION_KEY:
+      DBUG_ASSERT(index <= 0xffff);
+      int2store(buff, index);
+      buff+= 2;
+      break;
+    case CREATE_OPTION_TABLE:
+      break;
+    default:
+      DBUG_ASSERT(0); /* impossible */
+    }
+    memcpy(buff, opt->key.str, opt->key.length);
+    buff+= opt->key.length;
+    memcpy(buff, opt->val.str, opt->val.length);
+    buff+= opt->val.length;
+  }
+  DBUG_RETURN(buff);
+}
+
+
+/**
+  Writes options of the table to the given buffer
+
+  @param buff            buffer for storing options
+  @param opt             table options structure
+*/
+
+void create_options_store(uchar *buff, TABLE_OPTIONS *opt)
+{
+  CREATE_OPTION **i;
+  uint index;
+  DBUG_ENTER("create_options_store");
+  if (opt->table_opt)
+    buff= create_options_list_store(buff, opt->table_opt,
+                                    CREATE_OPTION_TABLE, 0 /*not used here*/);
+  if (opt->field_opt)
+  {
+    for (i= opt->field_opt, index= 0; *i != &last_option; i++, index++)
+      buff= create_options_list_store(buff, *i, CREATE_OPTION_FIELD, index);
+  }
+  if (opt->key_opt)
+  {
+    for (i= opt->key_opt, index= 0; *i != &last_option; i++, index++)
+      buff= create_options_list_store(buff, *i, CREATE_OPTION_KEY, index);
+  }
+  DBUG_VOID_RETURN;
+}
+
+
+/**
+  Issue warnings about unused options
+
+  @param thd             thread handler
+  @param options         table options structure
+*/
+
+void create_options_check_unused(THD *thd,
+                                 TABLE_OPTIONS *options)
+{
+  CREATE_OPTION *opt;
+  CREATE_OPTION **i;
+  uint index;
+  DBUG_ENTER("create_options_check_unused");
+
+  if (!options)
+    DBUG_VOID_RETURN;
+
+  for (opt= options->table_opt; opt != NULL; opt= opt->next)
+  {
+    if (!opt->used)
+    {
+      push_warning_printf(thd,
+			  MYSQL_ERROR::WARN_LEVEL_WARN,
+			  WARN_UNUSED_TABLE_OPTION,
+			  ER(WARN_UNUSED_TABLE_OPTION),
+			  (const char *) opt->key.str,
+			  (const char *) opt->val.str);
+
+    }
+  }
+  if (options->field_opt)
+  {
+    for (i= options->field_opt, index= 0; *i != &last_option; i++, index++)
+    {
+      for (opt= i[0]; opt != NULL; opt= opt->next)
+      {
+        if (!opt->used)
+        {
+          Field *field= (Field *) opt->owner;
+          push_warning_printf(thd,
+                              MYSQL_ERROR::WARN_LEVEL_WARN,
+                              WARN_UNUSED_FIELD_OPTION,
+                              ER(WARN_UNUSED_FIELD_OPTION),
+                              (const char *) opt->key.str,
+                              (const char *) opt->val.str,
+                              field->field_name);
+
+        }
+      }
+    }
+  }
+  if (options->key_opt)
+  {
+    for (i= options->key_opt, index= 0; *i != &last_option; i++, index++)
+    {
+      for (opt= i[0]; opt != NULL; opt= opt->next)
+      {
+        if (!opt->used)
+        {
+          KEY *key= (KEY *) opt->owner;
+          push_warning_printf(thd,
+                              MYSQL_ERROR::WARN_LEVEL_WARN,
+                              WARN_UNUSED_KEY_OPTION,
+                              ER(WARN_UNUSED_KEY_OPTION),
+                              (const char *) opt->key.str,
+                              (const char *) opt->val.str,
+                              key->name);
+
+        }
+      }
+    }
+  }
+
+  DBUG_VOID_RETURN;
+}
+
+
+/**
+  Adds references from Fields and KEYs objects to options
+
+  @param share           TABLE_SHARE object.
+*/
+
+void create_options_binding(TABLE_SHARE *share)
+{
+  TABLE_OPTIONS *options= share->create_table_options;
+  CREATE_OPTION **i, *opt;
+  Field **fld;
+  KEY *keyinfo;
+  DBUG_ENTER("create_options_binding");
+
+  if (!options)
+    DBUG_VOID_RETURN;
+
+  if (options->field_opt)
+  {
+    for (i= options->field_opt, fld= share->field;
+         *i != &last_option;
+         i++, fld++)
+    {
+      (*fld)->create_options= *i;
+      for (opt= i[0]; opt != NULL; opt= opt->next)
+      {
+        opt->owner= (void *)(*fld);
+        DBUG_PRINT("info", ("key: '%s'  value: '%s'  field: '%s'",
+                            opt->key.str, opt->val.str, (*fld)->field_name));
+
+      }
+    }
+  }
+  if (options->key_opt)
+  {
+    for (i= options->key_opt, keyinfo= share->key_info;
+         *i != &last_option;
+         i++, keyinfo++)
+    {
+      keyinfo->create_options= *i;
+      for (opt= i[0]; opt != NULL; opt= opt->next)
+      {
+        opt->owner= (void *)keyinfo;
+        DBUG_PRINT("info", ("key: '%s'  value: '%s'  key: '%s'",
+                            opt->key.str, opt->val.str, keyinfo->name));
+      }
+    }
+  }
+  DBUG_VOID_RETURN;
+}
+
+
+/**
+  clones options
+
+  @param root            mem_root where to clone the options
+  @param opt             options list to clone
+
+  @return cloned list
+*/
+
+CREATE_OPTION* create_options_clone(MEM_ROOT *root, CREATE_OPTION *opt)
+{
+  CREATE_OPTION *res= NULL, *clone;
+  char *key, *val;
+  DBUG_ENTER("create_options_clone");
+
+  for (; opt != NULL; opt= opt->next)
+  {
+    if (!multi_alloc_root(root, &clone, sizeof(CREATE_OPTION),
+                          &key, opt->key.length + 1,
+                          &val, opt->val.length + 1, NULL))
+      DBUG_RETURN(NULL);
+    clone->key.str= (char *)memcpy(key, opt->key.str,
+                                   (clone->key.length= opt->key.length) + 1);
+    clone->val.str= (char *)memcpy(val, opt->val.str,
+                                   (clone->val.length= opt->val.length) + 1);
+    clone->used= opt->used;
+    clone->owner= opt->owner;
+    clone->next= res;
+    res= clone;
+  }
+  DBUG_RETURN(res);
+}
+
+
+/**
+  Merges source and changes lists checking for real changes
+
+  @param source          source list to merge
+  @param changes         changes in the list
+  @param root            memroot to allocate option
+  @param changed         pointer to variable to report changed data or NULL
+
+  @return merged list
+*/
+
+CREATE_OPTION *create_table_list_merge(CREATE_OPTION *source,
+                                       CREATE_OPTION *changes,
+                                       MEM_ROOT *root,
+                                       my_bool *changed)
+{
+  my_bool chng= FALSE;
+  DBUG_ENTER("create_table_list_merge");
+
+  for(; changes; changes= changes->next)
+  {
+    if (create_option_add(&source, root, &changes->key, &changes->val,
+                          (chng ? NULL : &chng)))
+      DBUG_RETURN(NULL);
+  }
+
+  if (changed)
+    *changed= chng;
+
+  DBUG_RETURN(source);
+}
+
+
+/**
+  Compare 2 option lists
+
+  @param opt1            first options list
+  @param opt2            second options list
+
+  @retval TRUE  lists are equal
+  @retval FALSE lists are different
+*/
+
+my_bool is_equal_create_options(CREATE_OPTION *opt1, CREATE_OPTION *opt2)
+{
+  uint n1= 0, n2= 0;
+  CREATE_OPTION *i, *j;
+  DBUG_ENTER("is_equal_create_options");
+
+  /* check length first */
+  for (i= opt1; i; i= i->next) n1++;
+  for (i= opt2; i; i= i->next) n2++;
+
+  if (n1 != n2)
+  {
+    DBUG_PRINT("info", ("length mismatch (%u != %u)", n1, n2));
+    DBUG_RETURN(FALSE);
+  }
+  for (i= opt1; i; i= i->next)
+  {
+    for (j= opt2;
+         j && my_strcasecmp(system_charset_info,
+                            i->key.str, j->key.str);
+         j= j->next);
+    if (!j)
+    {
+      DBUG_PRINT("info", ("Key '%s' not found in second list", i->key.str));
+      DBUG_RETURN(FALSE);
+    }
+    if (i->val.length != j->val.length ||
+        memcmp(i->val.str, j->val.str, i->val.length))
+    {
+      DBUG_PRINT("info", ("Values of key '%s' mismatch ('%s' != '%s')",
+                          i->key.str, i->val.str, j->val.str));
+      DBUG_RETURN(FALSE);
+    }
+  }
+  DBUG_RETURN(TRUE);
+}

=== added file 'sql/sql_create_options.h'
--- sql/sql_create_options.h	1970-01-01 00:00:00 +0000
+++ sql/sql_create_options.h	2009-12-01 21:25:03 +0000
@@ -0,0 +1,61 @@
+
+#ifndef _SQL_CREATE_OPTIONS_H
+#define _SQL_CREATE_OPTIONS_H
+
+C_MODE_START
+
+/* types of cretate options records on disk, also it is length of extra data */
+typedef enum enum_create_options_type {
+  CREATE_OPTION_TABLE= 0,
+  CREATE_OPTION_KEY= 1,
+  CREATE_OPTION_FIELD= 2
+} CREATE_OPTION_TYPES;
+
+typedef struct st_create_option {
+  /* pointer to the next option or NULL */
+  struct st_create_option *next;
+  /* pointer to Field or KEY or NULL */
+  void *owner;
+  /* key and value of the option (\0 terminated)*/
+  LEX_STRING key, val;
+  /* used to issue warnings about unused options */
+  my_bool used;
+} CREATE_OPTION;
+
+typedef struct st_table_options {
+  CREATE_OPTION *table_opt;  /* table options list */
+  CREATE_OPTION **field_opt; /* fields options array */
+  CREATE_OPTION **key_opt;   /* keys options array */
+} TABLE_OPTIONS;
+
+CREATE_OPTION **create_create_options_array(MEM_ROOT *root, uint n);
+TABLE_OPTIONS *create_create_options(MEM_ROOT *root, uint fields, uint keys);
+
+my_bool create_options_read(const uchar *buff, uint length, MEM_ROOT *root,
+                            TABLE_OPTIONS *opt);
+
+my_bool create_option_add(CREATE_OPTION **options, MEM_ROOT *root,
+                          const LEX_STRING *k, const LEX_STRING *v,
+                          my_bool *chanes);
+
+ulong create_options_length(TABLE_OPTIONS *opt);
+
+void create_options_store(uchar *buff, TABLE_OPTIONS *opt);
+
+void create_options_check_unused(THD *thd,
+                                 TABLE_OPTIONS *options);
+
+struct st_table_share;
+void create_options_binding(struct st_table_share *share);
+
+CREATE_OPTION* create_options_clone(MEM_ROOT *root, CREATE_OPTION *opt);
+
+CREATE_OPTION *create_table_list_merge(CREATE_OPTION *source,
+                                       CREATE_OPTION *changes,
+                                       MEM_ROOT *root,
+                                       my_bool *changed);
+my_bool is_equal_create_options(CREATE_OPTION *opt1, CREATE_OPTION *opt2);
+
+C_MODE_END
+
+#endif

=== modified file 'sql/sql_lex.h'
--- sql/sql_lex.h	2009-11-12 04:31:28 +0000
+++ sql/sql_lex.h	2009-12-01 20:45:53 +0000
@@ -869,6 +869,7 @@
 #define ALTER_ALL_PARTITION      (1L << 21)
 #define ALTER_REMOVE_PARTITIONING (1L << 22)
 #define ALTER_FOREIGN_KEY        (1L << 23)
+#define ALTER_CREATE_OPT         (1L << 24)
 
 enum enum_alter_table_change_level
 {
@@ -1752,7 +1753,12 @@
   */
   const char *fname_start;
   const char *fname_end;
-  
+
+  /**
+    Collects create options for Field and KEY
+  */
+  CREATE_OPTION *create_opt;
+
   /**
     During name resolution search only in the table list given by 
     Name_resolution_context::first_name_resolution_table and

=== modified file 'sql/sql_parse.cc'
--- sql/sql_parse.cc	2009-11-12 04:31:28 +0000
+++ sql/sql_parse.cc	2009-12-01 20:58:59 +0000
@@ -6103,7 +6103,8 @@
 		       char *change,
                        List<String> *interval_list, CHARSET_INFO *cs,
 		       uint uint_geom_type,
-		       Virtual_column_info *vcol_info)
+		       Virtual_column_info *vcol_info,
+                       CREATE_OPTION *create_options)
 {
   register Create_field *new_field;
   LEX  *lex= thd->lex;
@@ -6121,7 +6122,7 @@
     lex->col_list.push_back(new Key_part_spec(field_name->str, 0));
     key= new Key(Key::PRIMARY, NullS,
                       &default_key_create_info,
-                      0, lex->col_list);
+                      0, lex->col_list, NULL);
     lex->alter_info.key_list.push_back(key);
     lex->col_list.empty();
   }
@@ -6131,7 +6132,7 @@
     lex->col_list.push_back(new Key_part_spec(field_name->str, 0));
     key= new Key(Key::UNIQUE, NullS,
                  &default_key_create_info, 0,
-                 lex->col_list);
+                 lex->col_list, NULL);
     lex->alter_info.key_list.push_back(key);
     lex->col_list.empty();
   }
@@ -6189,7 +6190,8 @@
   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))
+                      interval_list, cs, uint_geom_type, vcol_info,
+                      create_options))
     DBUG_RETURN(1);
 
   lex->alter_info.create_list.push_back(new_field);

=== modified file 'sql/sql_show.cc'
--- sql/sql_show.cc	2009-11-12 04:31:28 +0000
+++ sql/sql_show.cc	2009-12-01 20:45:53 +0000
@@ -1067,6 +1067,28 @@
   return has_default;
 }
 
+
+/**
+  Appends list of options to string
+
+  @param thd             thread handler
+  @param packet          string to append
+  @param opt             list of options
+*/
+
+static void append_create_options(THD *thd, String *packet, CREATE_OPTION *opt)
+{
+  bool first= TRUE;
+  for(; opt; opt= opt->next)
+  {
+    packet->append(' ');
+    append_identifier(thd, packet, opt->key.str, opt->key.length);
+    packet->append('=');
+    append_unescaped(packet, opt->val.str, opt->val.length);
+    first= FALSE;
+  }
+}
+
 /*
   Build a CREATE TABLE statement for a table.
 
@@ -1249,6 +1271,8 @@
       packet->append(STRING_WITH_LEN(" COMMENT "));
       append_unescaped(packet, field->comment.str, field->comment.length);
     }
+    if (field->create_options)
+      append_create_options(thd, packet, field->create_options);
   }
 
   key_info= table->key_info;
@@ -1320,6 +1344,8 @@
       append_identifier(thd, packet, parser_name->str, parser_name->length);
       packet->append(STRING_WITH_LEN(" */ "));
     }
+    if (key_info->create_options)
+      append_create_options(thd, packet, key_info->create_options);
   }
 
   /*
@@ -1479,6 +1505,9 @@
       packet->append(STRING_WITH_LEN(" CONNECTION="));
       append_unescaped(packet, share->connect_string.str, share->connect_string.length);
     }
+    if (share->create_table_options && share->create_table_options->table_opt)
+      append_create_options(thd, packet,
+                            share->create_table_options->table_opt);
     append_directory(thd, packet, "DATA",  create_info.data_file_name);
     append_directory(thd, packet, "INDEX", create_info.index_file_name);
   }

=== modified file 'sql/sql_table.cc'
--- sql/sql_table.cc	2009-11-12 04:31:28 +0000
+++ sql/sql_table.cc	2009-12-01 20:45:54 +0000
@@ -3034,6 +3034,7 @@
     key_info->key_part=key_part_info;
     key_info->usable_key_parts= key_number;
     key_info->algorithm= key->key_create_info.algorithm;
+    key_info->create_options= key->create_options;
 
     if (key->type == Key::FULLTEXT)
     {
@@ -5713,7 +5714,8 @@
       create_info->used_fields & HA_CREATE_USED_TRANSACTIONAL ||
       create_info->used_fields & HA_CREATE_USED_PACK_KEYS ||
       create_info->used_fields & HA_CREATE_USED_MAX_ROWS ||
-      (alter_info->flags & (ALTER_RECREATE | ALTER_FOREIGN_KEY)) ||
+      (alter_info->flags & (ALTER_RECREATE | ALTER_FOREIGN_KEY |
+                            ALTER_CREATE_OPT)) ||
       order_num ||
       !table->s->mysql_version ||
       (table->s->frm_version < FRM_VER_TRUE_VARCHAR && varchar))
@@ -5766,6 +5768,15 @@
       DBUG_RETURN(0);
     }
 
+    if (!is_equal_create_options(tmp_new_field->create_options,
+                                 field->create_options))
+    {
+      DBUG_PRINT("info", ("Options difference in field '%s'",
+                          new_field->field_name));
+      *need_copy_table= ALTER_TABLE_DATA_CHANGED;
+      DBUG_RETURN(0);
+    }
+
     /* Don't pack rows in old tables if the user has requested this. */
       if (create_info->row_type == ROW_TYPE_DYNAMIC ||
           (tmp_new_field->flags & BLOB_FLAG) ||
@@ -5850,7 +5861,9 @@
     if ((table_key->algorithm != new_key->algorithm) ||
 	((table_key->flags & HA_KEYFLAG_MASK) !=
          (new_key->flags & HA_KEYFLAG_MASK)) ||
-        (table_key->key_parts != new_key->key_parts))
+        (table_key->key_parts != new_key->key_parts) ||
+        !is_equal_create_options(table_key->create_options,
+                                 new_key->create_options))
       goto index_changed;
 
     /*
@@ -6081,7 +6094,7 @@
     char *tablespace= static_cast<char *>(thd->alloc(FN_LEN + 1));
     /*
        Regular alter table of disk stored table (no tablespace/storage change)
-       Copy tablespace name
+       C&opy tablespace name
     */
     if (tablespace &&
         (table->file->get_tablespace_name(thd, tablespace, FN_LEN)))
@@ -6089,6 +6102,30 @@
   }
   restore_record(table, s->default_values);     // Empty record for DEFAULT
 
+  if (create_info->create_table_options_orig.table_opt)
+  {
+    my_bool changed= FALSE;
+    create_info->create_table_options_orig.table_opt=
+      create_table_list_merge((table->s->create_table_options?
+                               table->s->create_table_options->table_opt:
+                               NULL),
+                              create_info->create_table_options_orig.table_opt,
+                              thd->mem_root,
+                              &changed);
+    if (changed)
+      alter_info->change_level= ALTER_TABLE_DATA_CHANGED;
+    else
+    {
+      alter_info->flags&= ~ALTER_CREATE_OPT;
+      DBUG_PRINT("info", ("Table options was not changed"));
+    }
+  }
+  else
+   create_info->create_table_options_orig.table_opt=
+     (table->s->create_table_options?
+      table->s->create_table_options->table_opt:
+      NULL);
+
   /*
     First collect all fields from table which isn't in drop_list
   */
@@ -6341,7 +6378,7 @@
       key= new Key(key_type, key_name,
                    &key_create_info,
                    test(key_info->flags & HA_GENERATED_KEY),
-                   key_parts);
+                   key_parts, key_info->create_options);
       new_key_list.push_back(key);
     }
   }

=== modified file 'sql/sql_yacc.yy'
--- sql/sql_yacc.yy	2009-11-12 04:31:28 +0000
+++ sql/sql_yacc.yy	2009-12-01 21:02:27 +0000
@@ -1144,6 +1144,7 @@
         IDENT_sys TEXT_STRING_sys TEXT_STRING_literal
         NCHAR_STRING opt_component key_cache_name
         sp_opt_label BIN_NUM label_ident TEXT_STRING_filesystem ident_or_empty
+        plugin_option_value
 
 %type <lex_str_ptr>
         opt_table_alias
@@ -1722,6 +1723,7 @@
             lex->alter_info.flags= ALTER_ADD_INDEX;
             lex->col_list.empty();
             lex->change=NullS;
+            lex->create_opt= NULL;
           }
           '(' key_list ')' key_options
           {
@@ -1733,7 +1735,7 @@
               MYSQL_YYABORT;
             }
             key= new Key($2, $4.str, &lex->key_create_info, 0,
-                         lex->col_list);
+                         lex->col_list, lex->create_opt);
             if (key == NULL)
               MYSQL_YYABORT;
             lex->alter_info.key_list.push_back(key);
@@ -2174,6 +2176,7 @@
 
             lex->interval_list.empty();
             lex->uint_geom_type= 0;
+            lex->create_opt= NULL;
           }
         ;
 
@@ -4570,6 +4573,16 @@
 	    Lex->create_info.used_fields|= HA_CREATE_USED_TRANSACTIONAL;
             Lex->create_info.transactional= $3;
           }
+        | IDENT_sys equal plugin_option_value
+          {
+            LEX *lex= Lex;
+            create_option_add(&(lex->
+                                create_info.
+                                create_table_options_orig.table_opt),
+                              YYTHD->mem_root, &$1, &$3,
+                              NULL);
+            lex->alter_info.flags|= ALTER_CREATE_OPT;
+          }
         ;
 
 default_charset:
@@ -4691,7 +4704,11 @@
         ;
 
 key_def:
-          key_type opt_ident key_alg '(' key_list ')' key_options
+          key_type opt_ident key_alg '(' key_list ')'
+          {
+            Lex->create_opt= NULL;
+          }
+          key_options
           {
             LEX *lex=Lex;
             if ($1 != Key::FULLTEXT && lex->key_create_info.parser_name.str)
@@ -4700,19 +4717,23 @@
               MYSQL_YYABORT;
             }
             Key *key= new Key($1, $2, &lex->key_create_info, 0,
-                              lex->col_list);
+                              lex->col_list, lex->create_opt);
             if (key == NULL)
               MYSQL_YYABORT;
             lex->alter_info.key_list.push_back(key);
             lex->col_list.empty(); /* Alloced by sql_alloc */
           }
         | opt_constraint constraint_key_type opt_ident key_alg
-          '(' key_list ')' key_options
+          '(' key_list ')'
+          {
+            Lex->create_opt= NULL;
+          }
+          key_options
           {
             LEX *lex=Lex;
             const char *key_name= $3 ? $3 : $1;
             Key *key= new Key($2, key_name, &lex->key_create_info, 0,
-                              lex->col_list);
+                              lex->col_list, lex->create_opt);
             if (key == NULL)
               MYSQL_YYABORT;
             lex->alter_info.key_list.push_back(key);
@@ -4734,7 +4755,7 @@
             lex->alter_info.key_list.push_back(key);
             key= new Key(Key::MULTIPLE, key_name,
                          &default_key_create_info, 1,
-                         lex->col_list);
+                         lex->col_list, NULL);
             if (key == NULL)
               MYSQL_YYABORT;
             lex->alter_info.key_list.push_back(key);
@@ -4780,6 +4801,7 @@
             lex->comment=null_lex_str;
             lex->charset=NULL;
 	    lex->vcol_info= 0;
+            lex->create_opt= NULL;
           }
           field_def
           {
@@ -4790,7 +4812,7 @@
                                   &lex->comment,
                                   lex->change,&lex->interval_list,lex->charset,
                                   lex->uint_geom_type,
-                                  lex->vcol_info))
+                                  lex->vcol_info, lex->create_opt))
               MYSQL_YYABORT;
           }
         ;
@@ -5210,6 +5232,13 @@
               Lex->charset=$2;
             }
           }
+        | IDENT_sys equal plugin_option_value
+          {
+            create_option_add(&(Lex->create_opt),
+                              YYTHD->mem_root, &$1, &$3,
+                              NULL);
+          }
+
         ;
 
 now_or_signed_literal:
@@ -5495,6 +5524,12 @@
               MYSQL_YYABORT;
             }
           }
+        | IDENT_sys equal plugin_option_value
+          {
+            create_option_add(&(Lex->create_opt),
+                              YYTHD->mem_root, &$1, &$3,
+                              NULL);
+          }
         ;
 
 btree_or_rtree:
@@ -5961,6 +5996,7 @@
             LEX *lex=Lex;
             lex->change= $3.str;
             lex->alter_info.flags|= ALTER_CHANGE_COLUMN;
+            lex->create_opt= NULL;
           }
           field_spec opt_place
         | MODIFY_SYM opt_column field_ident
@@ -5972,6 +6008,7 @@
             lex->charset= NULL;
             lex->alter_info.flags|= ALTER_CHANGE_COLUMN;
 	    lex->vcol_info= 0;
+            lex->create_opt= NULL;
           }
           field_def
           {
@@ -5983,7 +6020,7 @@
                                   &lex->comment,
                                   $3.str, &lex->interval_list, lex->charset,
                                   lex->uint_geom_type,
-                                  lex->vcol_info))
+                                  lex->vcol_info, lex->create_opt))
               MYSQL_YYABORT;
           }
           opt_place
@@ -13494,6 +13531,7 @@
             lex->interval_list.empty();
             lex->type= 0;
             lex->vcol_info= 0;
+            lex->create_opt= NULL;
           }
           type /* $11 */
           { /* $12 */
@@ -13744,6 +13782,32 @@
           }
         ;
 
+/**************************************************************************
+
+ Create options
+
+**************************************************************************/
+
+plugin_option_value:
+  DEFAULT
+    {
+      $$.str= NULL; /* We are going to remove the option */
+      $$.length= 0;
+    }
+  | NULL_SYM
+    {
+      $$.str= NULL; /* We are going to remove the option */
+      $$.length= 0;
+    }
+  | IDENT_sys { $$ = $1; }
+  | TEXT_STRING_sys { $$ = $1; }
+  | DECIMAL_NUM { $$ = $1; }
+  | FLOAT_NUM { $$ = $1; }
+  | NUM { $$ = $1; }
+  | LONG_NUM { $$ = $1; }
+  | HEX_NUM { $$ = $1; }
+
+
 /**
   @} (end of group Parser)
 */

=== modified file 'sql/structs.h'
--- sql/structs.h	2009-10-19 17:14:48 +0000
+++ sql/structs.h	2009-12-01 20:45:54 +0000
@@ -101,6 +101,8 @@
     int  bdb_return_if_eq;
   } handler;
   struct st_table *table;
+  /** reference to the list of options or NULL */
+  CREATE_OPTION *create_options;
 } KEY;
 
 

=== modified file 'sql/table.cc'
--- sql/table.cc	2009-11-12 04:31:28 +0000
+++ sql/table.cc	2009-12-01 21:38:32 +0000
@@ -670,12 +670,13 @@
   uint db_create_options, keys, key_parts, n_length;
   uint key_info_length, com_length, null_bit_pos;
   uint vcol_screen_length;
-  uint extra_rec_buf_length;
+  uint extra_rec_buf_length, options_len;
   uint i,j;
   bool use_hash;
   char *keynames, *names, *comment_pos, *vcol_screen_pos;
   uchar *record;
-  uchar *disk_buff, *strpos, *null_flags, *null_pos;
+  uchar *disk_buff, *strpos, *null_flags, *null_pos, *options;
+  uchar *buff= 0;
   ulong pos, record_offset, *rec_per_key, rec_buff_length;
   handler *handler_file= 0;
   KEY	*keyinfo;
@@ -791,7 +792,8 @@
 
   for (i=0 ; i < keys ; i++, keyinfo++)
   {
-    keyinfo->table= 0;                           // Updated in open_frm
+    keyinfo->table= 0;                   // Updated in open_frm
+    keyinfo->create_options= NULL;       // Updated in create_options_bindings
     if (new_frm_ver >= 3)
     {
       keyinfo->flags=	   (uint) uint2korr(strpos) ^ HA_NOSAME;
@@ -861,15 +863,14 @@
   if ((n_length= uint4korr(head+55)))
   {
     /* Read extra data segment */
-    uchar *buff, *next_chunk, *buff_end;
+    uchar *next_chunk, *buff_end;
     DBUG_PRINT("info", ("extra segment size is %u bytes", n_length));
     if (!(next_chunk= buff= (uchar*) my_malloc(n_length, MYF(MY_WME))))
       goto err;
     if (my_pread(file, buff, n_length, record_offset + share->reclength,
                  MYF(MY_NABP)))
     {
-      my_free(buff, MYF(0));
-      goto err;
+      goto free_and_err;
     }
     share->connect_string.length= uint2korr(buff);
     if (!(share->connect_string.str= strmake_root(&share->mem_root,
@@ -877,8 +878,7 @@
                                                   share->connect_string.
                                                   length)))
     {
-      my_free(buff, MYF(0));
-      goto err;
+      goto free_and_err;
     }
     next_chunk+= share->connect_string.length + 2;
     buff_end= buff + n_length;
@@ -898,8 +898,7 @@
                 plugin_data(tmp_plugin, handlerton *)))
         {
           /* bad file, legacy_db_type did not match the name */
-          my_free(buff, MYF(0));
-          goto err;
+          goto free_and_err;
         }
         /*
           tmp_plugin is locked with a local lock.
@@ -928,8 +927,7 @@
           error= 8;
           my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0),
                    "--skip-partition");
-          my_free(buff, MYF(0));
-          goto err;
+          goto free_and_err;
         }
         plugin_unlock(NULL, share->db_plugin);
         share->db_plugin= ha_lock_engine(NULL, partition_hton);
@@ -943,8 +941,7 @@
         /* purecov: begin inspected */
         error= 8;
         my_error(ER_UNKNOWN_STORAGE_ENGINE, MYF(0), name.str);
-        my_free(buff, MYF(0));
-        goto err;
+        goto free_and_err;
         /* purecov: end */
       }
       next_chunk+= str_db_type_length + 2;
@@ -960,16 +957,14 @@
               memdup_root(&share->mem_root, next_chunk + 4,
                           partition_info_len + 1)))
         {
-          my_free(buff, MYF(0));
-          goto err;
+          goto free_and_err;
         }
       }
 #else
       if (partition_info_len)
       {
         DBUG_PRINT("info", ("WITH_PARTITION_STORAGE_ENGINE is not defined"));
-        my_free(buff, MYF(0));
-        goto err;
+        goto free_and_err;
       }
 #endif
       next_chunk+= 5 + partition_info_len;
@@ -995,6 +990,17 @@
 #endif
       next_chunk++;
     }
+    if (share->db_create_options & HA_OPTION_TEXT_CREATE_OPTIONS)
+    {
+      /*
+        store options position, but skip till the time we will
+        know number of fields
+      */
+      options_len= uint4korr(next_chunk);
+      options= next_chunk + 4;
+      next_chunk+= options_len;
+      options_len-= 4;
+    }
     keyinfo= share->key_info;
     for (i= 0; i < keys; i++, keyinfo++)
     {
@@ -1005,8 +1011,7 @@
         {
           DBUG_PRINT("error",
                      ("fulltext key uses parser that is not defined in .frm"));
-          my_free(buff, MYF(0));
-          goto err;
+          goto free_and_err;
         }
         parser_name.str= (char*) next_chunk;
         parser_name.length= strlen((char*) next_chunk);
@@ -1016,12 +1021,10 @@
         if (! keyinfo->parser)
         {
           my_error(ER_PLUGIN_IS_NOT_LOADED, MYF(0), parser_name.str);
-          my_free(buff, MYF(0));
-          goto err;
+          goto free_and_err;
         }
       }
     }
-    my_free(buff, MYF(0));
   }
   share->key_block_size= uint2korr(head+62);
 
@@ -1031,21 +1034,21 @@
   share->rec_buff_length= rec_buff_length;
   if (!(record= (uchar *) alloc_root(&share->mem_root,
                                      rec_buff_length)))
-    goto err;                                   /* purecov: inspected */
+    goto free_and_err;                          /* purecov: inspected */
   share->default_values= record;
   if (my_pread(file, record, (size_t) share->reclength,
                record_offset, MYF(MY_NABP)))
-    goto err;                                   /* purecov: inspected */
+    goto free_and_err;                          /* purecov: inspected */
 
   VOID(my_seek(file,pos,MY_SEEK_SET,MYF(0)));
   if (my_read(file, head,288,MYF(MY_NABP)))
-    goto err;
+    goto free_and_err;
 #ifdef HAVE_CRYPTED_FRM
   if (crypted)
   {
     crypted->decode((char*) head+256,288-256);
     if (sint2korr(head+284) != 0)		// Should be 0
-      goto err;                                 // Wrong password
+      goto free_and_err;                        // Wrong password
   }
 #endif
 
@@ -1065,6 +1068,20 @@
                                    share->comment.length);
 
   DBUG_PRINT("info",("i_count: %d  i_parts: %d  index: %d  n_length: %d  int_length: %d  com_length: %d  vcol_screen_length: %d", interval_count,interval_parts, share->keys,n_length,int_length, com_length, vcol_screen_length));
+
+  if (share->db_create_options & HA_OPTION_TEXT_CREATE_OPTIONS)
+  {
+    if (!(share->create_table_options=
+          create_create_options(&share->mem_root, share->fields, keys)) ||
+        create_options_read(options, options_len, &share->mem_root,
+                            share->create_table_options))
+    {
+      goto free_and_err;
+    }
+  }
+  my_free(buff, MYF(MY_ALLOW_ZERO_PTR));
+
+
   if (!(field_ptr = (Field **)
 	alloc_root(&share->mem_root,
 		   (uint) ((share->fields+1)*sizeof(Field*)+
@@ -1670,6 +1687,8 @@
   bitmap_init(&share->all_set, bitmaps, share->fields, FALSE);
   bitmap_set_all(&share->all_set);
 
+  create_options_binding(share);
+
   delete handler_file;
 #ifndef DBUG_OFF
   if (use_hash)
@@ -1677,6 +1696,8 @@
 #endif
   DBUG_RETURN (0);
 
+ free_and_err:
+  my_free(buff, MYF(MY_ALLOW_ZERO_PTR));
  err:
   share->error= error;
   share->open_errno= my_errno;
@@ -2868,6 +2889,7 @@
   ulong length;
   uchar fill[IO_SIZE];
   int create_flags= O_RDWR | O_TRUNC;
+  DBUG_ENTER("create_frm");
 
   if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
     create_flags|= O_EXCL | O_NOFOLLOW;
@@ -2949,7 +2971,7 @@
       {
 	VOID(my_close(file,MYF(0)));
 	VOID(my_delete(name,MYF(0)));
-	return(-1);
+	DBUG_RETURN(-1);
       }
     }
   }
@@ -2960,7 +2982,7 @@
     else
       my_error(ER_CANT_CREATE_TABLE,MYF(0),table,my_errno);
   }
-  return (file);
+  DBUG_RETURN(file);
 } /* create_frm */
 
 

=== modified file 'sql/table.h'
--- sql/table.h	2009-11-12 04:31:28 +0000
+++ sql/table.h	2009-12-01 20:45:54 +0000
@@ -310,6 +310,7 @@
 #ifdef NOT_YET
   struct st_table *open_tables;		/* link to open tables */
 #endif
+  TABLE_OPTIONS *create_table_options;  /* text options for table */
 
   /* The following is copied to each TABLE on OPEN */
   Field **field;

=== modified file 'sql/unireg.cc'
--- sql/unireg.cc	2009-10-16 22:57:48 +0000
+++ sql/unireg.cc	2009-12-01 20:45:54 +0000
@@ -75,6 +75,94 @@
   return is_handled;
 }
 
+
+/**
+  Collects information about fields text create options into one array
+
+  @param thd                  Thread handler
+  @param create_fields        List of fields
+  @param create_table_options Table create options information
+                              structure to store result in it
+
+  @retval FALSE OK
+  @retval TRUE  Error
+*/
+
+static my_bool
+collect_fields_create_options(THD *thd, List<Create_field> &create_fields,
+                              TABLE_OPTIONS *create_table_options)
+{
+  List_iterator<Create_field> it(create_fields);
+  Create_field *field;
+  uint index= 0;
+  uint fields= create_fields.elements;
+  bool is_allocated= create_table_options->field_opt;
+  DBUG_ENTER("collect_fields_create_options");
+
+  while ((field= it++))
+  {
+    if (field->create_options)
+    {
+      if (!is_allocated)
+      {
+        if (!(create_table_options->field_opt=
+              create_create_options_array(thd->mem_root,
+                                          fields)))
+        {
+          DBUG_RETURN(TRUE);
+        }
+        is_allocated= TRUE;
+      }
+      create_table_options->field_opt[index]= field->create_options;
+    }
+    index++;
+  }
+  DBUG_RETURN(FALSE);
+}
+
+/**
+  Collects information about keys text create options into one array
+
+  @param thd                  Thread handler
+  @param keys                 Number of keys
+  @param key_info             Array of key descriptors
+  @param create_table_options Table create options information
+                              structure to store result in it
+
+  @retval FALSE OK
+  @retval TRUE  Error
+*/
+
+static my_bool
+collect_keys_create_options(THD *thd, uint keys, KEY *key_info,
+                            TABLE_OPTIONS *create_table_options)
+{
+  uint index;
+  bool is_allocated= create_table_options->key_opt;
+  DBUG_ENTER("collect_keys_create_options");
+
+  for(index= 0; index < keys; index++, key_info++)
+  {
+    if (key_info->create_options)
+    {
+      if (!is_allocated)
+      {
+        if (!(create_table_options->key_opt=
+              create_create_options_array(thd->mem_root,
+                                          keys)))
+        {
+          DBUG_RETURN(TRUE);
+        }
+        is_allocated= TRUE;
+      }
+      create_table_options->key_opt[index]= key_info->create_options;
+      key_info->create_options= create_table_options->key_opt[index];
+    }
+  }
+  DBUG_RETURN(FALSE);
+}
+
+
 /*
   Create a frm (table definition) file
 
@@ -107,6 +195,7 @@
   ulong key_buff_length;
   File file;
   ulong filepos, data_offset;
+  uint options_len= 0;
   uchar fileinfo[64],forminfo[288],*keybuff;
   TYPELIB formnames;
   uchar *screen_buff;
@@ -124,6 +213,7 @@
     DBUG_RETURN(1);
   DBUG_ASSERT(db_file != NULL);
 
+  create_info->create_table_options= &create_info->create_table_options_orig;
  /* If fixed row records, we need one bit to check for deleted rows */
   if (!(create_info->table_options & HA_OPTION_PACK_RECORD))
     create_info->null_bits++;
@@ -183,6 +273,25 @@
       create_info->extra_size+= key_info[i].parser_name->length + 1;
   }
 
+  if (collect_fields_create_options(thd, create_fields,
+                                    create_info->create_table_options) ||
+      collect_keys_create_options(thd, keys, key_info,
+                                  create_info->create_table_options))
+  {
+    my_free(screen_buff, MYF(0));
+    DBUG_RETURN(1);
+  }
+  if (create_info->create_table_options &&
+      (create_info->create_table_options->table_opt ||
+       create_info->create_table_options->field_opt ||
+       create_info->create_table_options->key_opt))
+  {
+    create_info->table_options|= HA_OPTION_TEXT_CREATE_OPTIONS;
+    create_info->extra_size+=
+      (options_len= 4 +
+       create_options_length(create_info->create_table_options));
+  }
+
   if ((file=create_frm(thd, file_name, db, table, reclength, fileinfo,
 		       create_info, keys)) < 0)
   {
@@ -294,6 +403,23 @@
     if (my_write(file, (uchar*) buff, 6, MYF_RW))
       goto err;
   }
+
+  if (options_len)
+  {
+    uchar *optbuff= (uchar *)my_malloc(options_len, MYF(0));
+    DBUG_PRINT("info", ("Create options length: %u", options_len));
+    if (!optbuff)
+      goto err;
+    int4store(optbuff, options_len);
+    create_options_store(optbuff + 4, create_info->create_table_options);
+    if (my_write(file, optbuff, options_len, MYF_RW))
+    {
+      my_free(optbuff, MYF(0));
+      goto err;
+    }
+    my_free(optbuff, MYF(0));
+  }
+
   for (i= 0; i < keys; i++)
   {
     if (key_info[i].parser_name)

=== modified file 'storage/example/ha_example.cc'
--- storage/example/ha_example.cc	2008-02-24 13:12:17 +0000
+++ storage/example/ha_example.cc	2009-12-01 20:45:54 +0000
@@ -836,11 +836,39 @@
 int ha_example::create(const char *name, TABLE *table_arg,
                        HA_CREATE_INFO *create_info)
 {
+  CREATE_OPTION *opt;
   DBUG_ENTER("ha_example::create");
   /*
     This is not implemented but we want someone to be able to see that it
     works.
   */
+  /* Example of checking parameters for table*/
+  for(opt= create_info->create_table_options->table_opt; opt; opt= opt->next)
+  {
+    /* check for legal options and its legal values */
+    if (opt->key.length == 1 &&
+        (opt->key.str[0] == 'e' || opt->key.str[0] == 'E') &&
+        opt->val.length == 1 &&
+        opt->val.str[0] == '1')
+      opt->used= 1; /* suppose that we used the only legal parameter */
+  }
+  /* Example of checking parameters for fields*/
+  for (Field **field= table_arg->s->field; *field; field++)
+  {
+    if ((*field)->create_options)
+    {
+      for(opt= (*field)->create_options; opt; opt= opt->next)
+      {
+        /* check for legal options and its legal values */
+        if (opt->key.length == 1 &&
+            (opt->key.str[0] == 'e' || opt->key.str[0] == 'E') &&
+            opt->val.length == 1 &&
+            opt->val.str[0] == '1')
+          opt->used= 1; /* suppose that we used the only legal parameter */
+      }
+    }
+  }
+
   DBUG_RETURN(0);
 }
 

=== modified file 'storage/pbxt/src/discover_xt.cc'
--- storage/pbxt/src/discover_xt.cc	2009-10-16 22:57:48 +0000
+++ storage/pbxt/src/discover_xt.cc	2009-12-01 21:33:24 +0000
@@ -1341,7 +1341,7 @@
 #endif
 		       NULL /*default_value*/, NULL /*on_update_value*/, &comment, NULL /*change*/, 
 		       NULL /*interval_list*/, info->field_charset, 0 /*uint_geom_type*/, 
-		       NULL /*vcol_info*/)) 
+                       NULL /*vcol_info*/, NULL /* create options */))
 #endif
 			goto error;
 


Follow ups

References