List:Commits« Previous MessageNext Message »
From:dlenev Date:February 18 2008 10:29am
Subject:bk commit into 6.0 tree (dlenev:1.2796) WL#148
View as plain text  
Below is the list of changes that have just been committed into a local
6.0 repository of dlenev.  When dlenev does a push these changes
will be propagated to the main repository and, within 24 hours after the
push, to the public repository.
For information on how to access the public repository
see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html

ChangeSet@stripped, 2008-02-18 13:29:39+03:00, dlenev@stripped +20 -0
  Draft patch for the 2nd milestone of WL#148 "Foreign keys" storing
  and restoring information about foreign keys in the .FRM files and
  properly displaying it in SHOW CREATE TABLE output and I_S tables.
  
  Couple of important things to be noted:
    - This patch does not implement storing of foreign key info in the
      .FRMs of parent tables since it is much more convinient to do
      when proper meta-data locking and implementation of DDL statements
      are in place.
    - Instead of text format for storing foreign keys described in LLD,
      this patch uses binary format which is easily extensible. This was
      a proposal from several developers who felt that polluting and
      calling parser just to be able restore foreign key descriptions
      is a bad idea. And using the same text format/parser which is used
      for views and triggers requires significant changes in its code
      and does not give any real benefits (.FRM as the whole is still
      binary).   
  
  As usual questions for reviewers are marked by QQ.

  BitKeeper/etc/ignore@stripped, 2008-02-18 13:29:33+03:00, dlenev@stripped +1 -0
    Added libmysqld/sql_fk.cc to the ignore list

  BitKeeper/etc/ignore@stripped, 2008-02-18 13:29:32+03:00, dlenev@stripped +1 -0
    Added libmysqld/sql_fk.cc to the ignore list

  libmysqld/Makefile.am@stripped, 2008-02-18 13:29:29+03:00, dlenev@stripped +1 -1
    Added sql_fk.cc and sql_fk.h to the lists of files needed for building
    of server.

  mysql-test/r/foreign_key_new.result@stripped, 2008-02-18 13:29:32+03:00, dlenev@stripped +235 -0
    Added basic test-coverage for the 2nd milestone of WL#148 "Foreign keys"
    (Storing/restoring foreign key information in/from .FRMs and showing them
     in SHOW CREATE TABLE and I_S tables).

  mysql-test/r/foreign_key_new.result@stripped, 2008-02-18 13:29:32+03:00, dlenev@stripped +0 -0

  mysql-test/t/foreign_key_new-master.opt@stripped, 2008-02-18 13:29:32+03:00, dlenev@stripped +1 -0
    Added basic test-coverage for the 2nd milestone of WL#148 "Foreign keys"
    (Storing/restoring foreign key information in/from .FRMs and showing them
     in SHOW CREATE TABLE and I_S tables).

  mysql-test/t/foreign_key_new-master.opt@stripped, 2008-02-18 13:29:32+03:00, dlenev@stripped +0 -0

  mysql-test/t/foreign_key_new.test@stripped, 2008-02-18 13:29:32+03:00, dlenev@stripped +124 -0
    Added basic test-coverage for the 2nd milestone of WL#148 "Foreign keys"
    (Storing/restoring foreign key information in/from .FRMs and showing them
     in SHOW CREATE TABLE and I_S tables).

  mysql-test/t/foreign_key_new.test@stripped, 2008-02-18 13:29:32+03:00, dlenev@stripped +0 -0

  sql/Makefile.am@stripped, 2008-02-18 13:29:29+03:00, dlenev@stripped +2 -2
    Added sql_fk.cc to the list of files needed for building of libmysqld.

  sql/mysql_priv.h@stripped, 2008-02-18 13:29:29+03:00, dlenev@stripped +7 -1
    Added "fk-all-engines" option in order to be able enable/disable
    new foreign key functionality.
    mysql_create_frm() and rea_create_table() now accept information
    about foreign keys for table to be created.
    Added declaration of fix_type_pointers() routine to be able to
    use it in sql_fk.cc.

  sql/mysqld.cc@stripped, 2008-02-18 13:29:30+03:00, dlenev@stripped +5 -0
    Added "fk-all-engines" option in order to be able enable/disable
    new foreign key functionality.

  sql/share/errmsg.txt@stripped, 2008-02-18 13:29:31+03:00, dlenev@stripped +4 -0
    Added error code and messages for two new foreign key related errors.

  sql/sql_class.cc@stripped, 2008-02-18 13:29:30+03:00, dlenev@stripped +5 -2
    Changed type of Key_part_spec::field_name and Key::name to LEX_STRING in
    order to avoid extra strlen() calls in code responsible for saving/restoring
    info about foreign keys.
    Added Foreign_key::name_generated to distinguish foreign keys with
    automatically generated names.

  sql/sql_class.h@stripped, 2008-02-18 13:29:30+03:00, dlenev@stripped +41 -9
    Changed type of Key_part_spec::field_name and Key::name to LEX_STRING in
    order to avoid extra strlen() calls in code responsible for saving/restoring
    info about foreign keys.
    Foreign_key::ref_table is now pointer to TABLE_LIST object instead of
    Table_ident. We now do Table_ident -> TABLE_LIST conversion and associated
    checks in the parser as in the most other cases.
    Added Foreign_key::name_generated to distinguish foreign keys with
    automatically generated names.

  sql/sql_fk.cc@stripped, 2008-02-18 13:29:32+03:00, dlenev@stripped +492 -0
    Added functions for saving/restoring information about foreign keys
    to/from the section in .FRM file.
    Added routines for adding information about foreign keys to
    SHOW CREATE TABLE output.
    Added auxiliary arrays with names of referential actions and match
    options to be used during filling of I_S table and in SHOW CREATE
    TABLE.

  sql/sql_fk.cc@stripped, 2008-02-18 13:29:32+03:00, dlenev@stripped +0 -0

  sql/sql_fk.h@stripped, 2008-02-18 13:29:32+03:00, dlenev@stripped +33 -0
    Added functions for saving/restoring information about foreign keys
    to/from the section in .FRM file.
    Added routines for adding information about foreign keys to
    SHOW CREATE TABLE output.
    Added auxiliary arrays with names of referential actions and match
    options to be used during filling of I_S table.

  sql/sql_fk.h@stripped, 2008-02-18 13:29:32+03:00, dlenev@stripped +0 -0

  sql/sql_parse.cc@stripped, 2008-02-18 13:29:30+03:00, dlenev@stripped +6 -4
    Key_part_spec::field_name and Key::name are now LEX_STRING. Adjusted
    code accordingly.

  sql/sql_show.cc@stripped, 2008-02-18 13:29:30+03:00, dlenev@stripped +186 -80
    Added code for showing information about new foreign keys in SHOW CREATE TABLE
    and appropriate I_S tables.
    
    QQ: Can we somehow avoid code duplication in code related to showing info
        in I_S tables? May be by adjusting FOREIGN_KEY_INFO to be usable for
        new foreign keys?

  sql/sql_table.cc@stripped, 2008-02-18 13:29:31+03:00, dlenev@stripped +82 -32
    mysql_prepare_create_table():
      Provide default values for missing clauses in foreign key specification.
      Do some error checks presence of which simplifies code responsible for
      saving/restoring information about foreign keys to/from .FRM.
    mysql_create_table():
      Generate names for foreign keys for which names were not specified
      explicitly. This has to be done relatively early in the table creation
      process due to meta-data locking requirements.
    mysql_create_table_no_lock()/mysql_create_like_schema_frm():
      Pass information about foreign keys to be created to mysql_create_frm().
    Adjusted code to accomodate change of type to LEX_STRING for
    Key_part_spec::field_name and Key::name.

  sql/sql_yacc.yy@stripped, 2008-02-18 13:29:31+03:00, dlenev@stripped +32 -24
    Now Key::name and Key_part_spec::field_name are LEX_STRINGs. Adjusted
    grammar to be able properly initialize them. This saves some strlen()
    calls later.
    Do Table_ident -> TABLE_LIST conversion and associated checks for the
    parent table of foreign key in the parser as in the most other cases/
    statements.

  sql/table.cc@stripped, 2008-02-18 13:29:31+03:00, dlenev@stripped +55 -35
    alloc_table_share()/init_tmp_table_share():
      Added code for initializing of TABLE_SHARE::fkeys list.
    open_binary_frm():
      Added calls to load information about foreign keys from
      .FRM to TABLE_SHARE.
      Adjusted code according to changes in fix_type_pointers().
    fix_type_pointers():
      Now this function also calculates lengths of typenames
      and stores them in TYPELIB::type_lengths array.

  sql/table.h@stripped, 2008-02-18 13:29:31+03:00, dlenev@stripped +3 -0
    Added TABLE_SHARE::fkeys list to store information about foreign
    keys associated with table which is represented by this share.

  sql/unireg.cc@stripped, 2008-02-18 13:29:31+03:00, dlenev@stripped +23 -3
    Now mysql_create_frm()/rea_create_table() accept information about foreign
    keys for the table to be created and call appropriate functions to save
    it into the .FRM file.

diff -Nrup a/BitKeeper/etc/ignore b/BitKeeper/etc/ignore
--- a/BitKeeper/etc/ignore	2007-12-21 13:24:29 +03:00
+++ b/BitKeeper/etc/ignore	2008-02-18 13:29:33 +03:00
@@ -3026,3 +3026,5 @@ ylwrap
 zlib/*.ds?
 zlib/*.vcproj
 libmysqld/des_key_file.cc
+libmysqld/sql_fk.cc
+libmysqld/sql_fk.cc
diff -Nrup a/libmysqld/Makefile.am b/libmysqld/Makefile.am
--- a/libmysqld/Makefile.am	2007-12-11 23:02:19 +03:00
+++ b/libmysqld/Makefile.am	2008-02-18 13:29:29 +03:00
@@ -79,7 +79,7 @@ sqlsources = derror.cc field.cc field_co
 	rpl_filter.cc sql_partition.cc sql_builtin.cc sql_plugin.cc \
 	sql_tablespace.cc \
 	rpl_injector.cc my_user.c partition_info.cc \
-	sql_servers.cc
+	sql_servers.cc sql_fk.cc
 
 libmysqld_int_a_SOURCES= $(libmysqld_sources)
 nodist_libmysqld_int_a_SOURCES= $(libmysqlsources) $(sqlsources)
diff -Nrup a/mysql-test/r/foreign_key_new.result b/mysql-test/r/foreign_key_new.result
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/mysql-test/r/foreign_key_new.result	2008-02-18 13:29:32 +03:00
@@ -0,0 +1,235 @@
+drop tables if exists t1, t2;
+drop database if exists mysqltest;
+set @@rand_seed1=10000000,@@rand_seed2=1000000;
+create table t2 (pk int primary key);
+create table t1 (fk1 int references t2 (pk),
+fk2 int constraint fk2 references t2 (pk),
+fk3 int references t2 (pk) match full,
+fk4 int references t2 (pk) on update restrict on delete cascade,
+fk5 int references t2 (pk) on delete restrict on update cascade,
+fk6 int references t2 (pk) on delete set null on update no action,
+fk7 int references t2 (pk) on delete no action on update set null,
+fk8 int references t2 (pk) match full on update set default,
+fk9 int references t2 (pk) match simple on delete set default,
+fk10 int, fk11 int, fk12 int, fk13 int, fk14 int,
+fk15 int, fk16 int, fk17 int, fk18 int,
+constraint fk10 foreign key (fk10) references t2 (pk),
+foreign key (fk11) references t2 (pk) match full,
+foreign key (fk12) references t2 (pk) on update restrict on delete cascade,
+foreign key (fk13) references t2 (pk) on delete restrict on update cascade,
+foreign key (fk14) references t2 (pk) on delete set null on update no action,
+foreign key (fk15) references t2 (pk) on delete no action on update set null,
+foreign key (fk16) references t2 (pk) match full on update set default,
+foreign key (fk17) references t2 (pk) match simple on delete set default,
+foreign key (fk18) references t2 (pk));
+show create table t1;
+Table	Create Table
+t1	CREATE TABLE `t1` (
+  `fk1` int(11) DEFAULT NULL CONSTRAINT `fk_t1_1_288` REFERENCES `t2` (`pk`),
+  `fk2` int(11) DEFAULT NULL CONSTRAINT `fk2` REFERENCES `t2` (`pk`),
+  `fk3` int(11) DEFAULT NULL CONSTRAINT `fk_t1_1_1164` REFERENCES `t2` (`pk`) MATCH FULL,
+  `fk4` int(11) DEFAULT NULL CONSTRAINT `fk_t1_1_4954` REFERENCES `t2` (`pk`) ON DELETE CASCADE ON UPDATE RESTRICT,
+  `fk5` int(11) DEFAULT NULL CONSTRAINT `fk_t1_1_1280` REFERENCES `t2` (`pk`) ON DELETE RESTRICT ON UPDATE CASCADE,
+  `fk6` int(11) DEFAULT NULL CONSTRAINT `fk_t1_1_1539` REFERENCES `t2` (`pk`) ON DELETE SET NULL,
+  `fk7` int(11) DEFAULT NULL CONSTRAINT `fk_t1_1_3856` REFERENCES `t2` (`pk`) ON UPDATE SET NULL,
+  `fk8` int(11) DEFAULT NULL CONSTRAINT `fk_t1_1_4663` REFERENCES `t2` (`pk`) MATCH FULL ON UPDATE SET DEFAULT,
+  `fk9` int(11) DEFAULT NULL CONSTRAINT `fk_t1_1_1746` REFERENCES `t2` (`pk`) ON DELETE SET DEFAULT,
+  `fk10` int(11) DEFAULT NULL,
+  `fk11` int(11) DEFAULT NULL,
+  `fk12` int(11) DEFAULT NULL,
+  `fk13` int(11) DEFAULT NULL,
+  `fk14` int(11) DEFAULT NULL,
+  `fk15` int(11) DEFAULT NULL,
+  `fk16` int(11) DEFAULT NULL,
+  `fk17` int(11) DEFAULT NULL,
+  `fk18` int(11) DEFAULT NULL,
+  KEY `fk10` (`fk10`),
+  KEY `fk_t1_1_4743` (`fk11`),
+  KEY `fk_t1_1_8476` (`fk12`),
+  KEY `fk_t1_1_8152` (`fk13`),
+  KEY `fk_t1_1_5334` (`fk14`),
+  KEY `fk_t1_1_2214` (`fk15`),
+  KEY `fk_t1_1_5070` (`fk16`),
+  KEY `fk_t1_1_8706` (`fk17`),
+  KEY `fk_t1_1_8320` (`fk18`),
+  CONSTRAINT `fk10` FOREIGN KEY (`fk10`) REFERENCES `t2` (`pk`),
+  CONSTRAINT `fk_t1_1_4743` FOREIGN KEY (`fk11`) REFERENCES `t2` (`pk`) MATCH FULL,
+  CONSTRAINT `fk_t1_1_8476` FOREIGN KEY (`fk12`) REFERENCES `t2` (`pk`) ON DELETE CASCADE ON UPDATE RESTRICT,
+  CONSTRAINT `fk_t1_1_8152` FOREIGN KEY (`fk13`) REFERENCES `t2` (`pk`) ON DELETE RESTRICT ON UPDATE CASCADE,
+  CONSTRAINT `fk_t1_1_5334` FOREIGN KEY (`fk14`) REFERENCES `t2` (`pk`) ON DELETE SET NULL,
+  CONSTRAINT `fk_t1_1_2214` FOREIGN KEY (`fk15`) REFERENCES `t2` (`pk`) ON UPDATE SET NULL,
+  CONSTRAINT `fk_t1_1_5070` FOREIGN KEY (`fk16`) REFERENCES `t2` (`pk`) MATCH FULL ON UPDATE SET DEFAULT,
+  CONSTRAINT `fk_t1_1_8706` FOREIGN KEY (`fk17`) REFERENCES `t2` (`pk`) ON DELETE SET DEFAULT,
+  CONSTRAINT `fk_t1_1_8320` FOREIGN KEY (`fk18`) REFERENCES `t2` (`pk`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+select * from information_schema.referential_constraints
+where constraint_schema = 'test' and table_name = 't1';
+CONSTRAINT_CATALOG	CONSTRAINT_SCHEMA	CONSTRAINT_NAME	UNIQUE_CONSTRAINT_CATALOG	UNIQUE_CONSTRAINT_SCHEMA	UNIQUE_CONSTRAINT_NAME	MATCH_OPTION	UPDATE_RULE	DELETE_RULE	TABLE_NAME	REFERENCED_TABLE_NAME
+NULL	test	fk_t1_1_288	NULL	test		NONE	NO ACTION	NO ACTION	t1	t2
+NULL	test	fk2	NULL	test		NONE	NO ACTION	NO ACTION	t1	t2
+NULL	test	fk_t1_1_1164	NULL	test		FULL	NO ACTION	NO ACTION	t1	t2
+NULL	test	fk_t1_1_4954	NULL	test		NONE	RESTRICT	CASCADE	t1	t2
+NULL	test	fk_t1_1_1280	NULL	test		NONE	CASCADE	RESTRICT	t1	t2
+NULL	test	fk_t1_1_1539	NULL	test		NONE	NO ACTION	SET NULL	t1	t2
+NULL	test	fk_t1_1_3856	NULL	test		NONE	SET NULL	NO ACTION	t1	t2
+NULL	test	fk_t1_1_4663	NULL	test		FULL	SET DEFAULT	NO ACTION	t1	t2
+NULL	test	fk_t1_1_1746	NULL	test		NONE	NO ACTION	SET DEFAULT	t1	t2
+NULL	test	fk10	NULL	test		NONE	NO ACTION	NO ACTION	t1	t2
+NULL	test	fk_t1_1_4743	NULL	test		FULL	NO ACTION	NO ACTION	t1	t2
+NULL	test	fk_t1_1_8476	NULL	test		NONE	RESTRICT	CASCADE	t1	t2
+NULL	test	fk_t1_1_8152	NULL	test		NONE	CASCADE	RESTRICT	t1	t2
+NULL	test	fk_t1_1_5334	NULL	test		NONE	NO ACTION	SET NULL	t1	t2
+NULL	test	fk_t1_1_2214	NULL	test		NONE	SET NULL	NO ACTION	t1	t2
+NULL	test	fk_t1_1_5070	NULL	test		FULL	SET DEFAULT	NO ACTION	t1	t2
+NULL	test	fk_t1_1_8706	NULL	test		NONE	NO ACTION	SET DEFAULT	t1	t2
+NULL	test	fk_t1_1_8320	NULL	test		NONE	NO ACTION	NO ACTION	t1	t2
+select * from information_schema.table_constraints
+where table_schema = 'test' and table_name = 't1';
+CONSTRAINT_CATALOG	CONSTRAINT_SCHEMA	CONSTRAINT_NAME	TABLE_SCHEMA	TABLE_NAME	CONSTRAINT_TYPE
+NULL	test	fk_t1_1_288	test	t1	FOREIGN KEY
+NULL	test	fk2	test	t1	FOREIGN KEY
+NULL	test	fk_t1_1_1164	test	t1	FOREIGN KEY
+NULL	test	fk_t1_1_4954	test	t1	FOREIGN KEY
+NULL	test	fk_t1_1_1280	test	t1	FOREIGN KEY
+NULL	test	fk_t1_1_1539	test	t1	FOREIGN KEY
+NULL	test	fk_t1_1_3856	test	t1	FOREIGN KEY
+NULL	test	fk_t1_1_4663	test	t1	FOREIGN KEY
+NULL	test	fk_t1_1_1746	test	t1	FOREIGN KEY
+NULL	test	fk10	test	t1	FOREIGN KEY
+NULL	test	fk_t1_1_4743	test	t1	FOREIGN KEY
+NULL	test	fk_t1_1_8476	test	t1	FOREIGN KEY
+NULL	test	fk_t1_1_8152	test	t1	FOREIGN KEY
+NULL	test	fk_t1_1_5334	test	t1	FOREIGN KEY
+NULL	test	fk_t1_1_2214	test	t1	FOREIGN KEY
+NULL	test	fk_t1_1_5070	test	t1	FOREIGN KEY
+NULL	test	fk_t1_1_8706	test	t1	FOREIGN KEY
+NULL	test	fk_t1_1_8320	test	t1	FOREIGN KEY
+select * from information_schema.key_column_usage
+where table_schema = 'test' and table_name = 't1';
+CONSTRAINT_CATALOG	CONSTRAINT_SCHEMA	CONSTRAINT_NAME	TABLE_CATALOG	TABLE_SCHEMA	TABLE_NAME	COLUMN_NAME	ORDINAL_POSITION	POSITION_IN_UNIQUE_CONSTRAINT	REFERENCED_TABLE_SCHEMA	REFERENCED_TABLE_NAME	REFERENCED_COLUMN_NAME
+NULL	test	fk_t1_1_288	NULL	test	t1	fk1	1	1	test	t2	pk
+NULL	test	fk2	NULL	test	t1	fk2	1	1	test	t2	pk
+NULL	test	fk_t1_1_1164	NULL	test	t1	fk3	1	1	test	t2	pk
+NULL	test	fk_t1_1_4954	NULL	test	t1	fk4	1	1	test	t2	pk
+NULL	test	fk_t1_1_1280	NULL	test	t1	fk5	1	1	test	t2	pk
+NULL	test	fk_t1_1_1539	NULL	test	t1	fk6	1	1	test	t2	pk
+NULL	test	fk_t1_1_3856	NULL	test	t1	fk7	1	1	test	t2	pk
+NULL	test	fk_t1_1_4663	NULL	test	t1	fk8	1	1	test	t2	pk
+NULL	test	fk_t1_1_1746	NULL	test	t1	fk9	1	1	test	t2	pk
+NULL	test	fk10	NULL	test	t1	fk10	1	1	test	t2	pk
+NULL	test	fk_t1_1_4743	NULL	test	t1	fk11	1	1	test	t2	pk
+NULL	test	fk_t1_1_8476	NULL	test	t1	fk12	1	1	test	t2	pk
+NULL	test	fk_t1_1_8152	NULL	test	t1	fk13	1	1	test	t2	pk
+NULL	test	fk_t1_1_5334	NULL	test	t1	fk14	1	1	test	t2	pk
+NULL	test	fk_t1_1_2214	NULL	test	t1	fk15	1	1	test	t2	pk
+NULL	test	fk_t1_1_5070	NULL	test	t1	fk16	1	1	test	t2	pk
+NULL	test	fk_t1_1_8706	NULL	test	t1	fk17	1	1	test	t2	pk
+NULL	test	fk_t1_1_8320	NULL	test	t1	fk18	1	1	test	t2	pk
+drop tables t1, t2;
+create table t2 (a int, b int, primary key (a, b));
+create table t1 (fk1 int, fk2 int, fk3 int, fk4 int,
+foreign key (fk1, fk2) references t2 (a, b),
+foreign key (fk4, fk3) references t2 (a, b)
+match full on update restrict on delete restrict);
+show create table t1;
+Table	Create Table
+t1	CREATE TABLE `t1` (
+  `fk1` int(11) DEFAULT NULL,
+  `fk2` int(11) DEFAULT NULL,
+  `fk3` int(11) DEFAULT NULL,
+  `fk4` int(11) DEFAULT NULL,
+  KEY `fk_t1_1_5483` (`fk1`,`fk2`),
+  KEY `fk_t1_1_2454` (`fk4`,`fk3`),
+  CONSTRAINT `fk_t1_1_5483` FOREIGN KEY (`fk1`, `fk2`) REFERENCES `t2` (`a`, `b`),
+  CONSTRAINT `fk_t1_1_2454` FOREIGN KEY (`fk4`, `fk3`) REFERENCES `t2` (`a`, `b`) MATCH FULL ON DELETE RESTRICT ON UPDATE RESTRICT
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+select * from information_schema.referential_constraints
+where constraint_schema = 'test' and table_name = 't1';
+CONSTRAINT_CATALOG	CONSTRAINT_SCHEMA	CONSTRAINT_NAME	UNIQUE_CONSTRAINT_CATALOG	UNIQUE_CONSTRAINT_SCHEMA	UNIQUE_CONSTRAINT_NAME	MATCH_OPTION	UPDATE_RULE	DELETE_RULE	TABLE_NAME	REFERENCED_TABLE_NAME
+NULL	test	fk_t1_1_5483	NULL	test		NONE	NO ACTION	NO ACTION	t1	t2
+NULL	test	fk_t1_1_2454	NULL	test		FULL	RESTRICT	RESTRICT	t1	t2
+select * from information_schema.table_constraints
+where table_schema = 'test' and table_name = 't1';
+CONSTRAINT_CATALOG	CONSTRAINT_SCHEMA	CONSTRAINT_NAME	TABLE_SCHEMA	TABLE_NAME	CONSTRAINT_TYPE
+NULL	test	fk_t1_1_5483	test	t1	FOREIGN KEY
+NULL	test	fk_t1_1_2454	test	t1	FOREIGN KEY
+select * from information_schema.key_column_usage
+where table_schema = 'test' and table_name = 't1';
+CONSTRAINT_CATALOG	CONSTRAINT_SCHEMA	CONSTRAINT_NAME	TABLE_CATALOG	TABLE_SCHEMA	TABLE_NAME	COLUMN_NAME	ORDINAL_POSITION	POSITION_IN_UNIQUE_CONSTRAINT	REFERENCED_TABLE_SCHEMA	REFERENCED_TABLE_NAME	REFERENCED_COLUMN_NAME
+NULL	test	fk_t1_1_5483	NULL	test	t1	fk1	1	1	test	t2	a
+NULL	test	fk_t1_1_5483	NULL	test	t1	fk2	2	2	test	t2	b
+NULL	test	fk_t1_1_2454	NULL	test	t1	fk4	1	1	test	t2	a
+NULL	test	fk_t1_1_2454	NULL	test	t1	fk3	2	2	test	t2	b
+drop tables t1, t2;
+create database mysqltest;
+use mysqltest;
+create table t2 (pk int primary key);
+create table test.t1 (fk1 int references t2 (pk));
+show create table test.t1;
+Table	Create Table
+t1	CREATE TABLE `t1` (
+  `fk1` int(11) DEFAULT NULL CONSTRAINT `fk_t1_1_5823` REFERENCES `mysqltest`.`t2` (`pk`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+select * from information_schema.referential_constraints
+where constraint_schema = 'test' and table_name = 't1';
+CONSTRAINT_CATALOG	CONSTRAINT_SCHEMA	CONSTRAINT_NAME	UNIQUE_CONSTRAINT_CATALOG	UNIQUE_CONSTRAINT_SCHEMA	UNIQUE_CONSTRAINT_NAME	MATCH_OPTION	UPDATE_RULE	DELETE_RULE	TABLE_NAME	REFERENCED_TABLE_NAME
+NULL	test	fk_t1_1_5823	NULL	mysqltest		NONE	NO ACTION	NO ACTION	t1	t2
+select * from information_schema.table_constraints
+where table_schema = 'test' and table_name = 't1';
+CONSTRAINT_CATALOG	CONSTRAINT_SCHEMA	CONSTRAINT_NAME	TABLE_SCHEMA	TABLE_NAME	CONSTRAINT_TYPE
+NULL	test	fk_t1_1_5823	test	t1	FOREIGN KEY
+select * from information_schema.key_column_usage
+where table_schema = 'test' and table_name = 't1';
+CONSTRAINT_CATALOG	CONSTRAINT_SCHEMA	CONSTRAINT_NAME	TABLE_CATALOG	TABLE_SCHEMA	TABLE_NAME	COLUMN_NAME	ORDINAL_POSITION	POSITION_IN_UNIQUE_CONSTRAINT	REFERENCED_TABLE_SCHEMA	REFERENCED_TABLE_NAME	REFERENCED_COLUMN_NAME
+NULL	test	fk_t1_1_5823	NULL	test	t1	fk1	1	1	mysqltest	t2	pk
+drop table test.t1, t2;
+create table test.t2 (pk int primary key);
+create table t1 (fk1 int references test.t2 (pk));
+show create table t1;
+Table	Create Table
+t1	CREATE TABLE `t1` (
+  `fk1` int(11) DEFAULT NULL CONSTRAINT `fk_t1_1_1753` REFERENCES `test`.`t2` (`pk`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+select * from information_schema.referential_constraints
+where constraint_schema = 'mysqltest' and table_name = 't1';
+CONSTRAINT_CATALOG	CONSTRAINT_SCHEMA	CONSTRAINT_NAME	UNIQUE_CONSTRAINT_CATALOG	UNIQUE_CONSTRAINT_SCHEMA	UNIQUE_CONSTRAINT_NAME	MATCH_OPTION	UPDATE_RULE	DELETE_RULE	TABLE_NAME	REFERENCED_TABLE_NAME
+NULL	mysqltest	fk_t1_1_1753	NULL	test		NONE	NO ACTION	NO ACTION	t1	t2
+select * from information_schema.table_constraints
+where table_schema = 'mysqltest' and table_name = 't1';
+CONSTRAINT_CATALOG	CONSTRAINT_SCHEMA	CONSTRAINT_NAME	TABLE_SCHEMA	TABLE_NAME	CONSTRAINT_TYPE
+NULL	mysqltest	fk_t1_1_1753	mysqltest	t1	FOREIGN KEY
+select * from information_schema.key_column_usage
+where table_schema = 'mysqltest' and table_name = 't1';
+CONSTRAINT_CATALOG	CONSTRAINT_SCHEMA	CONSTRAINT_NAME	TABLE_CATALOG	TABLE_SCHEMA	TABLE_NAME	COLUMN_NAME	ORDINAL_POSITION	POSITION_IN_UNIQUE_CONSTRAINT	REFERENCED_TABLE_SCHEMA	REFERENCED_TABLE_NAME	REFERENCED_COLUMN_NAME
+NULL	mysqltest	fk_t1_1_1753	NULL	mysqltest	t1	fk1	1	1	test	t2	pk
+drop table t1, test.t2;
+create table test.t2 (pk int primary key);
+create table test.t1 (fk1 int references test.t2 (pk));
+show create table test.t1;
+Table	Create Table
+t1	CREATE TABLE `t1` (
+  `fk1` int(11) DEFAULT NULL CONSTRAINT `fk_t1_1_1297` REFERENCES `t2` (`pk`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+select * from information_schema.referential_constraints
+where constraint_schema = 'test' and table_name = 't1';
+CONSTRAINT_CATALOG	CONSTRAINT_SCHEMA	CONSTRAINT_NAME	UNIQUE_CONSTRAINT_CATALOG	UNIQUE_CONSTRAINT_SCHEMA	UNIQUE_CONSTRAINT_NAME	MATCH_OPTION	UPDATE_RULE	DELETE_RULE	TABLE_NAME	REFERENCED_TABLE_NAME
+NULL	test	fk_t1_1_1297	NULL	test		NONE	NO ACTION	NO ACTION	t1	t2
+select * from information_schema.table_constraints
+where table_schema = 'test' and table_name = 't1';
+CONSTRAINT_CATALOG	CONSTRAINT_SCHEMA	CONSTRAINT_NAME	TABLE_SCHEMA	TABLE_NAME	CONSTRAINT_TYPE
+NULL	test	fk_t1_1_1297	test	t1	FOREIGN KEY
+select * from information_schema.key_column_usage
+where table_schema = 'test' and table_name = 't1';
+CONSTRAINT_CATALOG	CONSTRAINT_SCHEMA	CONSTRAINT_NAME	TABLE_CATALOG	TABLE_SCHEMA	TABLE_NAME	COLUMN_NAME	ORDINAL_POSITION	POSITION_IN_UNIQUE_CONSTRAINT	REFERENCED_TABLE_SCHEMA	REFERENCED_TABLE_NAME	REFERENCED_COLUMN_NAME
+NULL	test	fk_t1_1_1297	NULL	test	t1	fk1	1	1	test	t2	pk
+use test;
+drop table t1, t2;
+drop database mysqltest;
+create table t2 (a int, b int, primary key (a,b));
+create table t1 (fk1 int, fk2 int, foreign key (fk1, fk2) references t2);
+ERROR HY000: Foreign key error: Constraint 'foreign key without name': '(parent column list)' is mandatory in MySQL
+create table t1 (fk1 int, fk2 int, foreign key (fk1, fk2) references t2 (a, b) match partial);
+ERROR HY000: Foreign key error: Constraint 'foreign key without name': MATCH PARTIAL not supported
+create table t1 (fk1 int references t2 (a, b));
+ERROR 42000: Incorrect foreign key definition for 'foreign key without name': Key reference and table reference don't match
+drop table t2;
diff -Nrup a/mysql-test/t/foreign_key_new-master.opt b/mysql-test/t/foreign_key_new-master.opt
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/mysql-test/t/foreign_key_new-master.opt	2008-02-18 13:29:32 +03:00
@@ -0,0 +1 @@
+--foreign-key-all-engines=1
diff -Nrup a/mysql-test/t/foreign_key_new.test b/mysql-test/t/foreign_key_new.test
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/mysql-test/t/foreign_key_new.test	2008-02-18 13:29:32 +03:00
@@ -0,0 +1,124 @@
+#
+# Basic test-coverage for the 2nd milestone of WL#148 "Foreign keys"
+# Let us ensure that we can create various kinds of foreign keys and
+# that information about them is properly saved in .FRM files and
+# shown in I_S tables and by SHOW CREATE TABLE statements.
+#
+
+--disable_warnings
+drop tables if exists t1, t2;
+drop database if exists mysqltest;
+--enable_warnings
+
+# Initialize random number generator to know values to get
+# repeatable constraint names.
+set @@rand_seed1=10000000,@@rand_seed2=1000000; 
+
+# Let us test that we properly save and restore information from
+# various clauses of foreign key definition.
+create table t2 (pk int primary key);
+create table t1 (fk1 int references t2 (pk),
+                 fk2 int constraint fk2 references t2 (pk),
+                 fk3 int references t2 (pk) match full,
+                 fk4 int references t2 (pk) on update restrict on delete cascade,
+                 fk5 int references t2 (pk) on delete restrict on update cascade,
+                 fk6 int references t2 (pk) on delete set null on update no action,
+                 fk7 int references t2 (pk) on delete no action on update set null,
+                 fk8 int references t2 (pk) match full on update set default,
+                 fk9 int references t2 (pk) match simple on delete set default,
+                 fk10 int, fk11 int, fk12 int, fk13 int, fk14 int,
+                 fk15 int, fk16 int, fk17 int, fk18 int,
+                 constraint fk10 foreign key (fk10) references t2 (pk),
+                 foreign key (fk11) references t2 (pk) match full,
+                 foreign key (fk12) references t2 (pk) on update restrict on delete cascade,
+                 foreign key (fk13) references t2 (pk) on delete restrict on update cascade,
+                 foreign key (fk14) references t2 (pk) on delete set null on update no action,
+                 foreign key (fk15) references t2 (pk) on delete no action on update set null,
+                 foreign key (fk16) references t2 (pk) match full on update set default,
+                 foreign key (fk17) references t2 (pk) match simple on delete set default,
+                 foreign key (fk18) references t2 (pk));
+
+show create table t1;
+select * from information_schema.referential_constraints
+  where constraint_schema = 'test' and table_name = 't1';
+select * from information_schema.table_constraints
+  where table_schema = 'test' and table_name = 't1';
+select * from information_schema.key_column_usage
+  where table_schema = 'test' and table_name = 't1';
+
+drop tables t1, t2;
+
+# Now same test for multi-column foreign keys
+create table t2 (a int, b int, primary key (a, b));
+create table t1 (fk1 int, fk2 int, fk3 int, fk4 int,
+                 foreign key (fk1, fk2) references t2 (a, b),
+                 foreign key (fk4, fk3) references t2 (a, b)
+                 match full on update restrict on delete restrict);
+
+show create table t1;
+select * from information_schema.referential_constraints
+  where constraint_schema = 'test' and table_name = 't1';
+select * from information_schema.table_constraints
+  where table_schema = 'test' and table_name = 't1';
+select * from information_schema.key_column_usage
+  where table_schema = 'test' and table_name = 't1';
+
+drop tables t1, t2;
+
+
+# Let us test behavior for the case when child and parent
+# databases differ.
+#
+# QQ: Behavior in this section is somewhat questionable...
+create database mysqltest;
+use mysqltest;
+
+create table t2 (pk int primary key);
+create table test.t1 (fk1 int references t2 (pk));
+show create table test.t1;
+select * from information_schema.referential_constraints
+  where constraint_schema = 'test' and table_name = 't1';
+select * from information_schema.table_constraints
+  where table_schema = 'test' and table_name = 't1';
+select * from information_schema.key_column_usage
+  where table_schema = 'test' and table_name = 't1';
+drop table test.t1, t2;
+
+create table test.t2 (pk int primary key);
+create table t1 (fk1 int references test.t2 (pk));
+show create table t1;
+select * from information_schema.referential_constraints
+  where constraint_schema = 'mysqltest' and table_name = 't1';
+select * from information_schema.table_constraints
+  where table_schema = 'mysqltest' and table_name = 't1';
+select * from information_schema.key_column_usage
+  where table_schema = 'mysqltest' and table_name = 't1';
+drop table t1, test.t2;
+
+create table test.t2 (pk int primary key);
+create table test.t1 (fk1 int references test.t2 (pk));
+show create table test.t1;
+select * from information_schema.referential_constraints
+  where constraint_schema = 'test' and table_name = 't1';
+select * from information_schema.table_constraints
+  where table_schema = 'test' and table_name = 't1';
+select * from information_schema.key_column_usage
+  where table_schema = 'test' and table_name = 't1';
+use test;
+drop table t1, t2;
+
+drop database mysqltest;
+
+
+# Tests for a couple of checks in CREATE STATEMENT that were implemented
+# on this stage or were already existing at this point and which simplify
+# implementation of saving/restoring info in .FRM.
+create table t2 (a int, b int, primary key (a,b));
+--error ER_FK_PARENT_COLUMN_MANDATORY
+create table t1 (fk1 int, fk2 int, foreign key (fk1, fk2) references t2);
+--error ER_FK_MATCH_PARTIAL
+create table t1 (fk1 int, fk2 int, foreign key (fk1, fk2) references t2 (a, b) match partial);
+# Error code for this situation might change on later milestones
+--error ER_WRONG_FK_DEF
+create table t1 (fk1 int references t2 (a, b));
+drop table t2;
diff -Nrup a/sql/Makefile.am b/sql/Makefile.am
--- a/sql/Makefile.am	2007-12-11 23:09:33 +03:00
+++ b/sql/Makefile.am	2008-02-18 13:29:29 +03:00
@@ -85,7 +85,7 @@ noinst_HEADERS =	item.h item_func.h item
 			event_data_objects.h event_scheduler.h \
 			sql_partition.h partition_info.h partition_element.h \
 			probes.h \
-			contributors.h sql_servers.h
+			contributors.h sql_servers.h sql_fk.h
 
 mysqld_SOURCES =	sql_lex.cc sql_handler.cc sql_partition.cc \
 			item.cc item_sum.cc item_buff.cc item_func.cc \
@@ -128,7 +128,7 @@ mysqld_SOURCES =	sql_lex.cc sql_handler.
                         event_queue.cc event_db_repository.cc events.cc \
 			sql_plugin.cc sql_binlog.cc \
 			sql_builtin.cc sql_tablespace.cc partition_info.cc \
-			sql_servers.cc sha2.cc
+			sql_servers.cc sha2.cc sql_fk.cc
 
 if HAVE_DTRACE
   mysqld_SOURCES += probes.d
diff -Nrup a/sql/mysql_priv.h b/sql/mysql_priv.h
--- a/sql/mysql_priv.h	2008-01-30 18:28:51 +03:00
+++ b/sql/mysql_priv.h	2008-02-18 13:29:29 +03:00
@@ -1958,6 +1958,7 @@ extern uint opt_large_page_size;
 #ifdef MYSQL_SERVER
 extern char *opt_logname, *opt_slow_logname;
 extern const char *log_output_str;
+extern my_bool opt_fk_all_engines;
 
 extern MYSQL_BIN_LOG mysql_bin_log;
 extern LOGGER logger;
@@ -2094,12 +2095,15 @@ bool mysql_create_frm(THD *thd, const ch
                       const char *db, const char *table,
 		      HA_CREATE_INFO *create_info,
 		      List<Create_field> &create_field,
-		      uint key_count,KEY *key_info,handler *db_type);
+		      uint key_count,KEY *key_info,
+                      List<Key> &fkey_list,
+                      handler *db_type);
 int rea_create_table(THD *thd, const char *path,
                      const char *db, const char *table_name,
                      HA_CREATE_INFO *create_info,
   		     List<Create_field> &create_field,
                      uint key_count,KEY *key_info,
+                     List<Key> &fkey_list,
                      handler *file);
 int format_number(uint inputflag,uint max_length,char * pos,uint length,
 		  char * *errpos);
@@ -2186,6 +2190,8 @@ uint calc_week(MYSQL_TIME *l_time, uint 
 void find_date(char *pos,uint *vek,uint flag);
 TYPELIB *convert_strings_to_array_type(char * *typelibs, char * *end);
 TYPELIB *typelib(MEM_ROOT *mem_root, List<String> &strings);
+void fix_type_pointers(const char ***array, unsigned int **lengths,
+                       TYPELIB *point_to_type, uint types, char **names);
 ulong get_form_pos(File file, uchar *head, TYPELIB *save_names);
 ulong make_new_entry(File file,uchar *fileinfo,TYPELIB *formnames,
 		     const char *newname);
diff -Nrup a/sql/mysqld.cc b/sql/mysqld.cc
--- a/sql/mysqld.cc	2008-01-02 19:18:56 +03:00
+++ b/sql/mysqld.cc	2008-02-18 13:29:30 +03:00
@@ -533,6 +533,7 @@ ulong slow_launch_threads = 0, sync_binl
 ulong expire_logs_days = 0;
 ulong rpl_recovery_rank=0;
 const char *log_output_str= "FILE";
+my_bool opt_fk_all_engines= 0;
 
 const double log_10[] = {
   1e000, 1e001, 1e002, 1e003, 1e004, 1e005, 1e006, 1e007, 1e008, 1e009,
@@ -5378,6 +5379,7 @@ enum options_mysqld
 #if HAVE_POOL_OF_THREADS == 1
   OPT_POOL_OF_THREADS,
 #endif
+  OPT_FK_ALL_ENGINES,
   OPT_OLD_MODE
 };
 
@@ -5569,6 +5571,9 @@ struct my_option my_long_options[] =
    0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
   {"flush", OPT_FLUSH, "Flush tables to disk between SQL commands.", 0, 0, 0,
    GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+  {"foreign-key-all-engines", OPT_FK_ALL_ENGINES, "Enable new foreign key feature.",
+    (uchar **)&opt_fk_all_engines, (uchar **)&opt_fk_all_engines, 0, GET_BOOL, NO_ARG,
+    0, 0, 0, 0, 0, 0},
   /* We must always support the next option to make scripts like mysqltest
      easier to do */
   {"gdb", OPT_DEBUGGING,
diff -Nrup a/sql/share/errmsg.txt b/sql/share/errmsg.txt
--- a/sql/share/errmsg.txt	2007-11-26 18:44:34 +03:00
+++ b/sql/share/errmsg.txt	2008-02-18 13:29:31 +03:00
@@ -6222,3 +6222,7 @@ ER_CANT_CREATE_SROUTINE
   eng "Cannot create stored routine `%-.64s`. Check warnings"
 ER_TABLESPACE_EXIST
   eng "Tablespace '%-.192s' already exists"
+ER_FK_MATCH_PARTIAL
+  eng "Foreign key error: Constraint '%-.192s': MATCH PARTIAL not supported"
+ER_FK_PARENT_COLUMN_MANDATORY
+  eng "Foreign key error: Constraint '%-.192s': '(parent column list)' is mandatory in MySQL"
diff -Nrup a/sql/sql_class.cc b/sql/sql_class.cc
--- a/sql/sql_class.cc	2008-02-04 16:42:40 +03:00
+++ b/sql/sql_class.cc	2008-02-18 13:29:30 +03:00
@@ -88,7 +88,9 @@ extern "C" void free_user_var(user_var_e
 
 bool Key_part_spec::operator==(const Key_part_spec& other) const
 {
-  return length == other.length && !strcmp(field_name, other.field_name);
+  return length == other.length &&
+         field_name.length == other.field_name.length &&
+         !strcmp(field_name.str, other.field_name.str);
 }
 
 /**
@@ -123,7 +125,8 @@ Foreign_key::Foreign_key(const Foreign_k
   update_opt(rhs.update_opt),
   match_opt(rhs.match_opt),
   fkey_name_used(rhs.fkey_name_used),
-  table_con(rhs.table_con)
+  table_con(rhs.table_con),
+  name_generated(rhs.name_generated)
 {
   list_copy_and_replace_each_value(ref_columns, mem_root);
 }
diff -Nrup a/sql/sql_class.h b/sql/sql_class.h
--- a/sql/sql_class.h	2008-02-04 16:42:40 +03:00
+++ b/sql/sql_class.h	2008-02-18 13:29:30 +03:00
@@ -97,9 +97,14 @@ typedef struct st_copy_info {
 
 class Key_part_spec :public Sql_alloc {
 public:
-  const char *field_name;
+  LEX_STRING field_name;
   uint length;
-  Key_part_spec(const char *name,uint len=0) :field_name(name), length(len) {}
+  Key_part_spec(const LEX_STRING &name, uint len)
+    : field_name(name), length(len)
+  {}
+  Key_part_spec(const char *name, const size_t name_len, uint len)
+    : length(len)
+  { field_name.str= (char *)name; field_name.length= name_len; }
   bool operator==(const Key_part_spec& other) const;
   /**
     Construct a copy of this Key_part_spec. field_name is copied
@@ -152,15 +157,24 @@ public:
   enum Keytype type;
   KEY_CREATE_INFO key_create_info;
   List<Key_part_spec> columns;
-  const char *name;
+  LEX_STRING name;
   bool generated;
 
-  Key(enum Keytype type_par, const char *name_arg,
+  Key(enum Keytype type_par, const LEX_STRING &name_arg,
       KEY_CREATE_INFO *key_info_arg,
       bool generated_arg, List<Key_part_spec> &cols)
     :type(type_par), key_create_info(*key_info_arg), columns(cols),
     name(name_arg), generated(generated_arg)
   {}
+  Key(enum Keytype type_par, const char *name_arg, size_t name_len_arg,
+      KEY_CREATE_INFO *key_info_arg, bool generated_arg,
+      List<Key_part_spec> &cols)
+    :type(type_par), key_create_info(*key_info_arg), columns(cols),
+    generated(generated_arg)
+  {
+    name.str= (char *)name_arg;
+    name.length= name_len_arg;
+  }
   Key(const Key &rhs, MEM_ROOT *mem_root);
   virtual ~Key() {}
   /* Equality comparison of keys (ignoring name) */
@@ -173,7 +187,9 @@ public:
     { return new (mem_root) Key(*this, mem_root); }
 };
 
-class Table_ident;
+/*
+  QQ: Does it makes sense to separate Foreign_key from the Key class?
+*/
 
 class Foreign_key: public Key {
 public:
@@ -182,7 +198,7 @@ public:
   enum fk_option { FK_OPTION_UNDEF, FK_OPTION_RESTRICT, FK_OPTION_CASCADE,
 		   FK_OPTION_SET_NULL, FK_OPTION_NO_ACTION, FK_OPTION_DEFAULT};
 
-  Table_ident *ref_table;
+  TABLE_LIST *ref_table;
   List<Key_part_spec> ref_columns;
   uint delete_opt, update_opt, match_opt;
   /*
@@ -195,15 +211,31 @@ public:
     FALSE - if it was created as column constraint.
   */
   bool table_con;
-  Foreign_key(const char *name_arg, bool fkey_name_used_arg,
+  /**
+     Tells if constraint name was generated rather than specified explicitly.
+  */
+  bool name_generated;
+  Foreign_key(const LEX_STRING &name_arg, bool fkey_name_used_arg,
               bool table_con_arg, List<Key_part_spec> &cols,
-	      Table_ident *table,   List<Key_part_spec> &ref_cols,
+	      TABLE_LIST *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),
     ref_table(table), ref_columns(ref_cols),
     delete_opt(delete_opt_arg), update_opt(update_opt_arg),
     match_opt(match_opt_arg), fkey_name_used(fkey_name_used_arg),
-    table_con(table_con_arg)
+    table_con(table_con_arg), name_generated(FALSE)
+  {}
+  Foreign_key(const char *name_arg, size_t name_len_arg,
+              bool fkey_name_used_arg, bool table_con_arg,
+              List<Key_part_spec> &cols, TABLE_LIST *table,
+              List<Key_part_spec> &ref_cols, uint delete_opt_arg,
+              uint update_opt_arg, uint match_opt_arg)
+    :Key(FOREIGN_KEY, name_arg, name_len_arg, &default_key_create_info,
+         0, cols),
+    ref_table(table), ref_columns(ref_cols),
+    delete_opt(delete_opt_arg), update_opt(update_opt_arg),
+    match_opt(match_opt_arg), fkey_name_used(fkey_name_used_arg),
+    table_con(table_con_arg), name_generated(FALSE)
   {}
   Foreign_key(const Foreign_key &rhs, MEM_ROOT *mem_root);
   /**
diff -Nrup a/sql/sql_fk.cc b/sql/sql_fk.cc
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/sql/sql_fk.cc	2008-02-18 13:29:32 +03:00
@@ -0,0 +1,492 @@
+/* Copyright (C) 2008 MySQL AB
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; version 2 of the License.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA */
+
+#include "mysql_priv.h"
+#include "sql_fk.h"
+
+
+/**
+   Calculate total length of .FRM section describing foreign keys.
+
+   @param fkey_list List of foreign keys to be save in .FRM
+*/
+
+uint fk_get_frm_section_length(List<Key> &fkey_list)
+{
+  List_iterator<Key> key_iterator(fkey_list);
+  Key *key;
+  /*
+    Reserve 4 bytes for total length of the section.
+    QQ: May be 2 bytes is enough?
+  */
+  uint length= 4;
+
+  while ((key= key_iterator++))
+  {
+    if (key->type == Key::FOREIGN_KEY)
+    {
+      Foreign_key *fkey= (Foreign_key*) key;
+      List_iterator<Key_part_spec> col_it1(fkey->columns);
+      List_iterator<Key_part_spec> col_it2(fkey->ref_columns);
+      const Key_part_spec *col;
+
+      /* Space for total length of description for this FK. */
+      length+= 4;
+      /* Space for flags. */
+      length+= 1;
+      /* Space for MATCH, ON UPDATE, ON DELETE clauses */
+      length+= 1+1+1;
+      /* Space for number of columns in FK. */
+      length+= 2;
+
+      /* Space for length of names and columns section. */
+      length+= 4;
+      /* Space for names and columns section separator. */
+      length++;
+      length+= fkey->name.length + 1;
+      length+= fkey->ref_table->db_length + 1;
+      length+= fkey->ref_table->table_name_length + 1;
+      while ((col= col_it1++))
+        length+= col->field_name.length + 1;
+      while ((col= col_it2++))
+        length+= col->field_name.length + 1;
+    }
+  }
+  return length;
+}
+
+
+static const char FK_FRM_FLAG_TABLE_CONSTRAINT= 1;
+
+/**
+   Save foreign keys description to the .FRM file.
+
+   @param thd              Thread context.
+   @param file             Descriptor of open .FRM file.
+   @param fkey_list        List of foreign keys.
+   @param predicted_length Length of the description which was predicted
+                           by fk_get_frm_section_length() function.
+
+   @return TRUE in case of error, FALSE otherwise.
+*/
+
+bool fk_save_to_frm_section(THD *thd, File file, List<Key> &fkey_list,
+                            uint predicted_length)
+{
+  List_iterator<Key> key_iterator(fkey_list);
+  char *buff= (char *)thd->alloc(predicted_length);
+  char *cur_pos= buff;
+  Key *key;
+
+  /* Store length of the whole section describing FKs. */
+  int4store(cur_pos, predicted_length - 4);
+  cur_pos+= 4;
+
+  while ((key= key_iterator++))
+  {
+    if (key->type == Key::FOREIGN_KEY)
+    {
+      Foreign_key *fkey= (Foreign_key*) key;
+      List_iterator<Key_part_spec> col_it1(fkey->columns);
+      List_iterator<Key_part_spec> col_it2(fkey->ref_columns);
+      const Key_part_spec *col;
+      char *fk_len_pos;
+
+      DBUG_ASSERT(cur_pos < buff + predicted_length);
+
+      /* Here we will store length of FK description a bit later. */
+      fk_len_pos= cur_pos;
+      cur_pos+= 4;
+
+      *(cur_pos++)= fkey->table_con ? FK_FRM_FLAG_TABLE_CONSTRAINT : 0;
+      *(cur_pos++)= fkey->match_opt;
+      *(cur_pos++)= fkey->update_opt;
+      *(cur_pos++)= fkey->delete_opt;
+      int2store(cur_pos, fkey->columns.elements);
+      cur_pos+= 2;
+
+      /* Again we will store length of names & columns section later. */
+      cur_pos+= 4;
+
+      /* Names & columns section starts with separator character. */
+      *(cur_pos++)= NAMES_SEP_CHAR;
+      cur_pos= strmov(cur_pos, fkey->name.str);
+      *(cur_pos++)= NAMES_SEP_CHAR;
+      /*
+        QQ: Should we store database of parent if it is the same as child's
+            database ?
+            Also how "lowercase_table_names" option should affect this?
+      */
+      cur_pos= strmov(cur_pos, fkey->ref_table->db);
+      *(cur_pos++)= NAMES_SEP_CHAR;
+      cur_pos= strmov(cur_pos, fkey->ref_table->table_name);
+      *(cur_pos++)= NAMES_SEP_CHAR;
+      while ((col= col_it1++))
+      {
+        cur_pos= strmov(cur_pos, col->field_name.str);
+        *(cur_pos++)= NAMES_SEP_CHAR;
+      }
+      while ((col= col_it2++))
+      {
+        cur_pos= strmov(cur_pos, col->field_name.str);
+        *(cur_pos++)= NAMES_SEP_CHAR;
+      }
+      /* Names & columns part ends. Write its length. */
+      int4store(fk_len_pos + 4 + 6, cur_pos - fk_len_pos - 12);
+
+      /* This is space reserved for future extensions. */
+
+      /*
+        This is the end of the whole description for this FK
+        so we can store its length.
+      */
+      int4store(fk_len_pos, cur_pos - fk_len_pos - 4);
+    }
+  }
+  return my_write(file, (const uchar*)buff, predicted_length, MYF(MY_NABP));
+}
+
+
+/**
+   Restore list of foreign keys for the table from its description in the .FRM.
+
+   @param thd      [in]     Thread context.
+   @param share    [in]     Share of the table FKs for which should be restored.
+   @param buff_p   [in/out] On entering routine should point to the beginning of
+                            FKs section in the buffer, on leaving it will point
+                            to the position right after this section.
+   @param buff_end [in]     Pointer to the end of buffer containing .FRM data.
+
+   @note This function assumes that share's MEM_ROOT is current active MEM_ROOT.
+
+   @return TRUE in case of error, FALSE otherwise.
+*/
+
+bool fk_restore_from_frm_section(THD *thd, TABLE_SHARE *share,
+                                 const char **buff_p, const char *buff_end)
+{
+  uint section_length;
+  const char *cur_pos;
+
+  if (*buff_p + 4 >= buff_end)
+    return FALSE;
+
+  section_length= uint4korr(*buff_p);
+  (*buff_p)+= 4;
+
+  /*
+    QQ: Should we do checks more for corruption ?
+        E.g. that new_buff_end <= buff_end ?
+  */
+  buff_end= *buff_p + section_length;
+  cur_pos= *buff_p;
+
+  while (cur_pos < buff_end)
+  {
+    TYPELIB dummy_type;
+    uint columns, fkey_descr_length, names_cols_len;
+    char *names_cols_buff;
+    const char **names_cols_arr, **names_cols_ptr;
+    unsigned int *names_cols_lengths, *names_cols_lengths_ptr;
+    const char *fkey_descr_start;
+    TABLE_LIST *table;
+    List<Key_part_spec> cols, ref_cols;
+    uint i;
+
+    /* Get length of description for one particular FK. */
+    fkey_descr_length= uint4korr(cur_pos);
+    cur_pos+= 4;
+    fkey_descr_start= cur_pos;
+
+    /*
+      Skip space where flags and info about MATCH and ON UPDATE/DELETE
+      are stored.
+    */
+    cur_pos+= 4;
+
+    columns= uint2korr(cur_pos);
+    cur_pos+= 2;
+
+    /*
+      Load names of constraint, parent table and columns into the
+      strings allocated on share's memory root.
+    */
+    names_cols_len= uint4korr(cur_pos);
+    cur_pos+= 4;
+    names_cols_buff= strmake_root(&share->mem_root, cur_pos, names_cols_len);
+    if (!(names_cols_arr= (const char **)alloc_root(&share->mem_root,
+                                                    sizeof(char*) *
+                                                    (3 + columns * 2 + 1) +
+                                                    sizeof(unsigned int) *
+                                                    (3 + columns * 2 + 1))))
+      return TRUE;
+    names_cols_ptr= names_cols_arr;
+    names_cols_lengths= (unsigned int *)(names_cols_arr + 3 + columns * 2 + 1);
+    names_cols_lengths_ptr= names_cols_lengths;
+    fix_type_pointers(&names_cols_ptr, &names_cols_lengths_ptr,
+                      &dummy_type, 1, &names_cols_buff);
+
+    DBUG_ASSERT(dummy_type.count == columns*2 + 3);
+
+    /*
+      Note that here we have share's MEM_ROOT as an active MEM_ROOT
+      so list elements are allocated on the memory with proper life
+      time.
+    */
+    for (i= 0; i< columns; i++)
+      cols.push_back(new (&share->mem_root) Key_part_spec(names_cols_arr[3+i],
+                                              names_cols_lengths[3+i], 0));
+    for (i= 0; i< columns; i++)
+      ref_cols.push_back(new (&share->mem_root) Key_part_spec(
+                                                  names_cols_arr[3+columns+i],
+                                                  names_cols_lengths[3+columns+i],
+                                                  0));
+
+    if (!(table= (TABLE_LIST *)alloc_root(&share->mem_root,
+                                          sizeof(TABLE_LIST))))
+      return TRUE;
+    bzero(table, sizeof(TABLE_LIST));
+
+    table->db= (char *)names_cols_arr[1];
+    table->db_length= names_cols_lengths[1];
+    table->table_name= (char *)names_cols_arr[2];
+    table->table_name_length= names_cols_lengths[2];
+
+    share->fkeys.push_back(new (&share->mem_root) Foreign_key(
+                                                    names_cols_arr[0],
+                                                    names_cols_lengths[0],
+                                                    FALSE,
+                                                    (fkey_descr_start[0] &
+                                                     FK_FRM_FLAG_TABLE_CONSTRAINT),
+                                                    cols,
+                                                    table, ref_cols,
+                                                    fkey_descr_start[3],
+                                                    fkey_descr_start[2],
+                                                    fkey_descr_start[1]));
+
+    /* Check for OOM .*/
+    if (thd->is_fatal_error)
+      return TRUE;
+
+    cur_pos= fkey_descr_start + fkey_descr_length;
+  }
+  *buff_p= cur_pos;
+  return FALSE;
+}
+
+
+/**
+   Names of referential actions in order corresponding to
+   Foreign_key::fk_option enum.
+*/
+
+const LEX_STRING fk_ref_action_names[]=
+  { {NULL, 0}, C_STRING_WITH_LEN("RESTRICT"), C_STRING_WITH_LEN("CASCADE"),
+    C_STRING_WITH_LEN("SET NULL"), C_STRING_WITH_LEN("NO ACTION"),
+    C_STRING_WITH_LEN("SET DEFAULT")};
+
+/**
+   Names of match options in order corresponding to
+   Foreign_key::fk_match_opt enum.
+
+   @note Contains NONE for MATCH SIMPLE as it is value which is show
+         for this match option in I_S table.
+*/
+
+const LEX_STRING fk_match_type_names[]=
+  { {NULL, 0}, C_STRING_WITH_LEN("FULL"), C_STRING_WITH_LEN("PARTIAL"),
+    C_STRING_WITH_LEN("NONE")};
+
+
+/**
+   Get CREATE TABLE clause for the foreign key.
+*/
+
+static void fk_get_create_statement_clause(THD *thd, String *str,
+                                           const char *db,
+                                           Foreign_key *fkey)
+{
+  List_iterator<Key_part_spec> col_it1(fkey->columns);
+  List_iterator<Key_part_spec> col_it2(fkey->ref_columns);
+  Key_part_spec *col;
+  bool first;
+
+  if (fkey->table_con)
+    str->append(STRING_WITH_LEN(",\n "));
+
+  str->append(STRING_WITH_LEN(" CONSTRAINT "));
+  append_identifier(thd, str, fkey->name.str, fkey->name.length);
+
+  if (fkey->table_con)
+  {
+    str->append(STRING_WITH_LEN(" FOREIGN KEY ("));
+    first= TRUE;
+    while ((col= col_it1++))
+    {
+      if (!first)
+        str->append(STRING_WITH_LEN(", "));
+      else
+        first= FALSE;
+      append_identifier(thd, str, col->field_name.str, col->field_name.length);
+    }
+    str->append(')');
+  }
+
+  str->append(STRING_WITH_LEN(" REFERENCES "));
+  /*
+    QQ: Here we surpress printing of parent's database if it the
+        same as child's database. Is this a correct approach?
+        Should we use THD::db instead ?
+    QQ: Also not sure about collation.
+  */
+  if (strcmp(db, fkey->ref_table->db))
+  {
+    append_identifier(thd, str, fkey->ref_table->db, fkey->ref_table->db_length);
+    str->append('.');
+  }
+  append_identifier(thd, str, fkey->ref_table->table_name,
+                    fkey->ref_table->table_name_length);
+  str->append(STRING_WITH_LEN(" ("));
+  first= TRUE;
+  while ((col= col_it2++))
+  {
+    if (!first)
+      str->append(STRING_WITH_LEN(", "));
+    else
+      first= FALSE;
+    append_identifier(thd, str, col->field_name.str, col->field_name.length);
+  }
+  str->append(')');
+
+  if (fkey->match_opt != Foreign_key::FK_MATCH_SIMPLE)
+  {
+    str->append(STRING_WITH_LEN(" MATCH "));
+    str->append(fk_match_type_names[fkey->match_opt].str,
+                fk_match_type_names[fkey->match_opt].length);
+  }
+
+  if (fkey->delete_opt != Foreign_key::FK_OPTION_NO_ACTION)
+  {
+    str->append(STRING_WITH_LEN(" ON DELETE "));
+    str->append(fk_ref_action_names[fkey->delete_opt].str,
+                fk_ref_action_names[fkey->delete_opt].length);
+  }
+
+  if (fkey->update_opt != Foreign_key::FK_OPTION_NO_ACTION)
+  {
+    str->append(STRING_WITH_LEN(" ON UPDATE "));
+    str->append(fk_ref_action_names[fkey->update_opt].str,
+                fk_ref_action_names[fkey->update_opt].length);
+  }
+}
+
+
+/**
+   Get CREATE TABLE clause describing column foreign key for one
+   of table columns.
+
+   @param thd          Thread context.
+   @param str [in/out] String to which clause should be appended.
+   @param table        TABLE object for the table in question.
+   @param field_name   Name of the column.
+*/
+
+void fk_get_column_constraint_for_create(THD *thd, String *str, TABLE *table,
+                                         const char *field_name)
+{
+  List_iterator<Foreign_key> fkey_it(table->s->fkeys);
+  Foreign_key *fkey;
+  while ((fkey= fkey_it++))
+  {
+    if (!fkey->table_con &&
+        !my_strcasecmp(system_charset_info, field_name,
+                       fkey->columns.head()->field_name.str))
+    {
+      fk_get_create_statement_clause(thd, str, table->s->db.str, fkey);
+      break;
+    }
+  }
+}
+
+
+/**
+   Get CREATE TABLE clauses describing all foreign keys for the table
+   which were created as table constraints.
+
+   @param thd          Thread context.
+   @param str [in/out] String to which clauses should be appended.
+   @param table        TABLE object for the table in question.
+*/
+
+void fk_get_table_constraints_for_create(THD *thd, String *str, TABLE *table)
+{
+  List_iterator<Foreign_key> fkey_it(table->s->fkeys);
+  Foreign_key *fkey;
+  while ((fkey= fkey_it++))
+    if (fkey->table_con)
+      fk_get_create_statement_clause(thd, str, table->s->db.str, fkey);
+}
+
+
+/**
+   Generate names for foreign key constraints without explicit names.
+
+   @param thd        Thread context.
+   @param table_name Name of the table to be created/altered.
+   @param fkey_list  List of foreign keys.
+*/
+
+void fk_generate_constraint_names(THD *thd, const char *table_name,
+                                  List<Key> &fkey_list)
+{
+  List_iterator<Key> key_iterator(fkey_list);
+  Key *key;
+  uint table_name_length= strlen(table_name);
+
+  while ((key= key_iterator++))
+  {
+    if (key->type == Key::FOREIGN_KEY)
+    {
+      Foreign_key *fkey= (Foreign_key*) key;
+      ulong randval;
+
+      if (!fkey->name.str || fkey->name_generated)
+      {
+        fkey->name.str= (char *)alloc_root(thd->mem_root,
+                                           3 + table_name_length + 1 + 10 +
+                                           1 + 5 + 1);
+        /*
+          Save rand_seed's in order to be able to replicate properly.
+
+          QQ: Does it makes sense to move some of this code to THD method?
+        */
+        if (!thd->rand_used)
+        {
+          thd->rand_used= 1;
+          thd->rand_saved_seed1= thd->rand.seed1;
+          thd->rand_saved_seed2= thd->rand.seed2;
+        }
+        randval= (ulong)(my_rnd(&thd->rand)*10000);
+        fkey->name.length= my_snprintf((char *)fkey->name.str,
+                                       3 + table_name_length + 1 + 10 + 1 + 5 + 1,
+                                       "fk_%s_%lu_%lu", table_name,
+                                       thd->variables.pseudo_thread_id,
+                                       randval);
+        thd->thread_specific_used= TRUE;
+        fkey->name_generated= TRUE;
+      }
+    }
+  }
+}
diff -Nrup a/sql/sql_fk.h b/sql/sql_fk.h
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/sql/sql_fk.h	2008-02-18 13:29:32 +03:00
@@ -0,0 +1,33 @@
+/* Copyright (C) 2008 MySQL AB
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; version 2 of the License.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA */
+
+#ifndef SQL_FK_H
+#define SQL_FK_H
+
+uint fk_get_frm_section_length(List<Key> &fkey_list);
+bool fk_save_to_frm_section(THD *thd, File file, List<Key> &fkey_list,
+                            uint predicted_length);
+bool fk_restore_from_frm_section(THD *thd, TABLE_SHARE *share,
+                                 const char **buff_p, const char *buff_end);
+
+extern const LEX_STRING fk_ref_action_names[];
+extern const LEX_STRING fk_match_type_names[];
+void fk_get_table_constraints_for_create(THD *thd, String *str, TABLE *table);
+void fk_get_column_constraint_for_create(THD *thd, String *str, TABLE *table,
+                                         const char *field_name);
+
+void fk_generate_constraint_names(THD *thd, const char *table_name,
+                                  List<Key> &fkey_list);
+#endif
diff -Nrup a/sql/sql_parse.cc b/sql/sql_parse.cc
--- a/sql/sql_parse.cc	2008-01-31 01:01:45 +03:00
+++ b/sql/sql_parse.cc	2008-02-18 13:29:30 +03:00
@@ -5539,8 +5539,9 @@ bool add_field_to_list(THD *thd, LEX_STR
   if (type_modifier & PRI_KEY_FLAG)
   {
     Key *key;
-    lex->col_list.push_back(new Key_part_spec(field_name->str, 0));
-    key= new Key(Key::PRIMARY, NullS,
+    LEX_STRING null_lex_str= {NULL, 0};
+    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);
     lex->alter_info.key_list.push_back(key);
@@ -5549,8 +5550,9 @@ bool add_field_to_list(THD *thd, LEX_STR
   if (type_modifier & (UNIQUE_FLAG | UNIQUE_KEY_FLAG))
   {
     Key *key;
-    lex->col_list.push_back(new Key_part_spec(field_name->str, 0));
-    key= new Key(Key::UNIQUE, NullS,
+    LEX_STRING null_lex_str= {NULL, 0};
+    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);
     lex->alter_info.key_list.push_back(key);
diff -Nrup a/sql/sql_show.cc b/sql/sql_show.cc
--- a/sql/sql_show.cc	2008-01-30 23:05:17 +03:00
+++ b/sql/sql_show.cc	2008-02-18 13:29:30 +03:00
@@ -28,6 +28,7 @@
 #include "contributors.h"
 #include "events.h"
 #include "event_data_objects.h"
+#include "sql_fk.h"
 #include <my_dir.h>
 
 #define STR_OR_NIL(S) ((S) ? (S) : "<nil>")
@@ -1185,6 +1186,9 @@ int store_create_info(THD *thd, TABLE_LI
         !(thd->variables.sql_mode & MODE_NO_FIELD_OPTIONS))
       packet->append(STRING_WITH_LEN(" AUTO_INCREMENT"));
 
+    if (opt_fk_all_engines)
+      fk_get_column_constraint_for_create(thd, packet, table, field->field_name);
+
     if (field->comment.length)
     {
       packet->append(STRING_WITH_LEN(" COMMENT "));
@@ -1259,15 +1263,19 @@ int store_create_info(THD *thd, TABLE_LI
     }
   }
 
-  /*
-    Get possible foreign key definitions stored in InnoDB and append them
-    to the CREATE TABLE statement
-  */
-
-  if ((for_str= file->get_foreign_key_create_info()))
+  if (opt_fk_all_engines)
+    fk_get_table_constraints_for_create(thd, packet, table);
+  else
   {
-    packet->append(for_str, strlen(for_str));
-    file->free_foreign_key_create_info(for_str);
+    /*
+      Get possible foreign key definitions stored in InnoDB and append them
+      to the CREATE TABLE statement
+    */
+    if ((for_str= file->get_foreign_key_create_info()))
+    {
+      packet->append(for_str, strlen(for_str));
+      file->free_foreign_key_create_info(for_str);
+    }
   }
 
   packet->append(STRING_WITH_LEN("\n)"));
@@ -4695,6 +4703,14 @@ static int get_schema_constraints_record
     TABLE *show_table= tables->table;
     KEY *key_info=show_table->key_info;
     uint primary_key= show_table->s->primary_key;
+    /*
+      QQ: Do we need this call only because we are going to retrieve
+          FK info? May be it makes sense to move it to place where we
+          do this?
+          Also if we don't need this call with new-FK implementation
+          we can avoid doing full-blown open and open .FRMs only
+          instead.
+    */
     show_table->file->info(HA_STATUS_VARIABLE | 
                            HA_STATUS_NO_LOCK |
                            HA_STATUS_TIME);
@@ -4719,16 +4735,32 @@ static int get_schema_constraints_record
       }
     }
 
-    show_table->file->get_foreign_key_list(thd, &f_key_list);
-    FOREIGN_KEY_INFO *f_key_info;
-    List_iterator_fast<FOREIGN_KEY_INFO> it(f_key_list);
-    while ((f_key_info=it++))
-    {
-      if (store_constraints(thd, table, db_name, table_name, 
-                            f_key_info->forein_id->str,
-                            strlen(f_key_info->forein_id->str),
-                            "FOREIGN KEY", 11))
-        DBUG_RETURN(1);
+    if (opt_fk_all_engines)
+    {
+      List_iterator<Foreign_key> fkey_it(show_table->s->fkeys);
+      Foreign_key *fkey;
+      while ((fkey= fkey_it++))
+      {
+        if (store_constraints(thd, table, db_name, table_name,
+                              fkey->name.str, fkey->name.length,
+                              "FOREIGN KEY", 11))
+          DBUG_RETURN(1);
+      }
+    }
+    else
+    {
+      show_table->file->get_foreign_key_list(thd, &f_key_list);
+      FOREIGN_KEY_INFO *f_key_info;
+      List_iterator_fast<FOREIGN_KEY_INFO> it(f_key_list);
+      while ((f_key_info=it++))
+      {
+        /* QQ: Why strlen()? */
+        if (store_constraints(thd, table, db_name, table_name, 
+                              f_key_info->forein_id->str,
+                              strlen(f_key_info->forein_id->str),
+                              "FOREIGN KEY", 11))
+          DBUG_RETURN(1);
+      }
     }
   }
   DBUG_RETURN(res);
@@ -4879,6 +4911,7 @@ static int get_schema_key_column_usage_r
     TABLE *show_table= tables->table;
     KEY *key_info=show_table->key_info;
     uint primary_key= show_table->s->primary_key;
+    // QQ: Same questions here ?
     show_table->file->info(HA_STATUS_VARIABLE | 
                            HA_STATUS_NO_LOCK |
                            HA_STATUS_TIME);
@@ -4906,41 +4939,83 @@ static int get_schema_key_column_usage_r
       }
     }
 
-    show_table->file->get_foreign_key_list(thd, &f_key_list);
-    FOREIGN_KEY_INFO *f_key_info;
-    List_iterator_fast<FOREIGN_KEY_INFO> fkey_it(f_key_list);
-    while ((f_key_info= fkey_it++))
-    {
-      LEX_STRING *f_info;
-      LEX_STRING *r_info;
-      List_iterator_fast<LEX_STRING> it(f_key_info->foreign_fields),
-        it1(f_key_info->referenced_fields);
-      uint f_idx= 0;
-      while ((f_info= it++))
-      {
-        r_info= it1++;
-        f_idx++;
-        restore_record(table, s->default_values);
-        store_key_column_usage(table, db_name, table_name,
-                               f_key_info->forein_id->str,
-                               f_key_info->forein_id->length,
-                               f_info->str, f_info->length,
-                               (longlong) f_idx);
-        table->field[8]->store((longlong) f_idx, TRUE);
-        table->field[8]->set_notnull();
-        table->field[9]->store(f_key_info->referenced_db->str,
-                               f_key_info->referenced_db->length,
-                               system_charset_info);
-        table->field[9]->set_notnull();
-        table->field[10]->store(f_key_info->referenced_table->str,
-                                f_key_info->referenced_table->length, 
-                                system_charset_info);
-        table->field[10]->set_notnull();
-        table->field[11]->store(r_info->str, r_info->length,
-                                system_charset_info);
-        table->field[11]->set_notnull();
-        if (schema_table_store_record(thd, table))
-          DBUG_RETURN(1);
+    if (opt_fk_all_engines)
+    {
+      List_iterator<Foreign_key> fkey_it(show_table->s->fkeys);
+      Foreign_key *fkey;
+      while ((fkey= fkey_it++))
+      {
+        List_iterator<Key_part_spec> it(fkey->columns);
+        List_iterator<Key_part_spec> it1(fkey->ref_columns);
+        Key_part_spec *f_col, *r_col;
+        uint f_idx= 0;
+
+        while ((f_col= it++))
+        {
+          r_col= it1++;
+          f_idx++;
+          restore_record(table, s->default_values);
+          store_key_column_usage(table, db_name, table_name,
+                                 fkey->name.str, fkey->name.length,
+                                 f_col->field_name.str, f_col->field_name.length,
+                                 (longlong) f_idx);
+          table->field[8]->store((longlong) f_idx, TRUE);
+          table->field[8]->set_notnull();
+          table->field[9]->store(fkey->ref_table->db,
+                                 fkey->ref_table->db_length,
+                                 system_charset_info);
+          table->field[9]->set_notnull();
+          table->field[10]->store(fkey->ref_table->table_name,
+                                  fkey->ref_table->table_name_length,
+                                  system_charset_info);
+          table->field[10]->set_notnull();
+          table->field[11]->store(r_col->field_name.str,
+                                  r_col->field_name.length,
+                                  system_charset_info);
+          table->field[11]->set_notnull();
+          if (schema_table_store_record(thd, table))
+            DBUG_RETURN(1);
+        }
+      }
+    }
+    else
+    {
+      show_table->file->get_foreign_key_list(thd, &f_key_list);
+      FOREIGN_KEY_INFO *f_key_info;
+      List_iterator_fast<FOREIGN_KEY_INFO> fkey_it(f_key_list);
+      while ((f_key_info= fkey_it++))
+      {
+        LEX_STRING *f_info;
+        LEX_STRING *r_info;
+        List_iterator_fast<LEX_STRING> it(f_key_info->foreign_fields),
+                                       it1(f_key_info->referenced_fields);
+        uint f_idx= 0;
+        while ((f_info= it++))
+        {
+          r_info= it1++;
+          f_idx++;
+          restore_record(table, s->default_values);
+          store_key_column_usage(table, db_name, table_name,
+                                 f_key_info->forein_id->str,
+                                 f_key_info->forein_id->length,
+                                 f_info->str, f_info->length,
+                                 (longlong) f_idx);
+          table->field[8]->store((longlong) f_idx, TRUE);
+          table->field[8]->set_notnull();
+          table->field[9]->store(f_key_info->referenced_db->str,
+                                 f_key_info->referenced_db->length,
+                                 system_charset_info);
+          table->field[9]->set_notnull();
+          table->field[10]->store(f_key_info->referenced_table->str,
+                                  f_key_info->referenced_table->length,
+                                  system_charset_info);
+          table->field[10]->set_notnull();
+          table->field[11]->store(r_info->str, r_info->length,
+                                  system_charset_info);
+          table->field[11]->set_notnull();
+          if (schema_table_store_record(thd, table))
+            DBUG_RETURN(1);
+        }
       }
     }
   }
@@ -5607,35 +5682,66 @@ get_referential_constraints_record(THD *
   }
   if (!tables->view)
   {
-    List<FOREIGN_KEY_INFO> f_key_list;
     TABLE *show_table= tables->table;
-    show_table->file->info(HA_STATUS_VARIABLE | 
-                           HA_STATUS_NO_LOCK |
-                           HA_STATUS_TIME);
-
-    show_table->file->get_foreign_key_list(thd, &f_key_list);
-    FOREIGN_KEY_INFO *f_key_info;
-    List_iterator_fast<FOREIGN_KEY_INFO> it(f_key_list);
-    while ((f_key_info= it++))
+    if (opt_fk_all_engines)
     {
-      restore_record(table, s->default_values);
-      table->field[1]->store(db_name->str, db_name->length, cs);
-      table->field[9]->store(table_name->str, table_name->length, cs);
-      table->field[2]->store(f_key_info->forein_id->str,
-                             f_key_info->forein_id->length, cs);
-      table->field[4]->store(f_key_info->referenced_db->str, 
-                             f_key_info->referenced_db->length, cs);
-      table->field[10]->store(f_key_info->referenced_table->str, 
-                             f_key_info->referenced_table->length, cs);
-      table->field[5]->store(f_key_info->referenced_key_name->str, 
-                             f_key_info->referenced_key_name->length, cs);
-      table->field[6]->store(STRING_WITH_LEN("NONE"), cs);
-      table->field[7]->store(f_key_info->update_method->str, 
-                             f_key_info->update_method->length, cs);
-      table->field[8]->store(f_key_info->delete_method->str, 
-                             f_key_info->delete_method->length, cs);
-      if (schema_table_store_record(thd, table))
-        DBUG_RETURN(1);
+      List_iterator<Foreign_key> fkey_it(show_table->s->fkeys);
+      Foreign_key *fkey;
+      while ((fkey= fkey_it++))
+      {
+        restore_record(table, s->default_values);
+        table->field[1]->store(db_name->str, db_name->length, cs);
+        table->field[2]->store(fkey->name.str, fkey->name.length, cs);
+        table->field[4]->store(fkey->ref_table->db,
+                               fkey->ref_table->db_length, cs);
+#ifdef WILL_BE_FILLED_ONCE_WE_WILL_REACH_LATER_MILESTONES_OF_WL148
+        table->field[5]->store(referenced_key_name.str,
+                               referenced_key_name.length, cs);
+#endif
+        table->field[6]->store(fk_match_type_names[fkey->match_opt].str,
+                               fk_match_type_names[fkey->match_opt].length, cs);
+        table->field[7]->store(fk_ref_action_names[fkey->update_opt].str,
+                               fk_ref_action_names[fkey->update_opt].length, cs);
+        table->field[8]->store(fk_ref_action_names[fkey->delete_opt].str,
+                               fk_ref_action_names[fkey->delete_opt].length, cs);
+        table->field[9]->store(table_name->str, table_name->length, cs);
+        table->field[10]->store(fkey->ref_table->table_name,
+                                fkey->ref_table->table_name_length, cs);
+        if (schema_table_store_record(thd, table))
+          DBUG_RETURN(1);
+      }
+    }
+    else
+    {
+      List<FOREIGN_KEY_INFO> f_key_list;
+
+      show_table->file->info(HA_STATUS_VARIABLE |
+                             HA_STATUS_NO_LOCK |
+                             HA_STATUS_TIME);
+      show_table->file->get_foreign_key_list(thd, &f_key_list);
+      FOREIGN_KEY_INFO *f_key_info;
+      List_iterator_fast<FOREIGN_KEY_INFO> it(f_key_list);
+      while ((f_key_info= it++))
+      {
+        restore_record(table, s->default_values);
+        table->field[1]->store(db_name->str, db_name->length, cs);
+        table->field[9]->store(table_name->str, table_name->length, cs);
+        table->field[2]->store(f_key_info->forein_id->str,
+                               f_key_info->forein_id->length, cs);
+        table->field[4]->store(f_key_info->referenced_db->str,
+                               f_key_info->referenced_db->length, cs);
+        table->field[10]->store(f_key_info->referenced_table->str,
+                                f_key_info->referenced_table->length, cs);
+        table->field[5]->store(f_key_info->referenced_key_name->str,
+                               f_key_info->referenced_key_name->length, cs);
+        table->field[6]->store(STRING_WITH_LEN("NONE"), cs);
+        table->field[7]->store(f_key_info->update_method->str,
+                               f_key_info->update_method->length, cs);
+        table->field[8]->store(f_key_info->delete_method->str,
+                               f_key_info->delete_method->length, cs);
+        if (schema_table_store_record(thd, table))
+          DBUG_RETURN(1);
+      }
     }
   }
   DBUG_RETURN(0);
diff -Nrup a/sql/sql_table.cc b/sql/sql_table.cc
--- a/sql/sql_table.cc	2008-02-04 16:42:40 +03:00
+++ b/sql/sql_table.cc	2008-02-18 13:29:31 +03:00
@@ -22,6 +22,7 @@
 #include "sp_head.h"
 #include "sql_trigger.h"
 #include "sql_show.h"
+#include "sql_fk.h"
 
 #ifdef __WIN__
 #include <io.h>
@@ -1298,7 +1299,8 @@ bool mysql_write_frm(ALTER_PARTITION_PAR
     if ((mysql_create_frm(lpt->thd, shadow_frm_name, lpt->db,
                           lpt->table_name, lpt->create_info,
                           lpt->alter_info->create_list, lpt->key_count,
-                          lpt->key_info_buffer, lpt->table->file)) ||
+                          lpt->key_info_buffer, lpt->alter_info->key_list,
+                          lpt->table->file)) ||
         lpt->table->file->ha_create_handler_files(shadow_path, NULL,
                                                   CHF_CREATE_FLAG,
                                                   lpt->create_info))
@@ -2527,18 +2529,51 @@ mysql_prepare_create_table(THD *thd, HA_
 
   while ((key=key_iterator++))
   {
-    DBUG_PRINT("info", ("key name: '%s'  type: %d", key->name ? key->name :
+    DBUG_PRINT("info", ("key name: '%s'  type: %d", key->name.str ? key->name.str :
                         "(none)" , key->type));
     LEX_STRING key_name_str;
     if (key->type == Key::FOREIGN_KEY)
     {
       fk_key_count++;
       Foreign_key *fk_key= (Foreign_key*) key;
+
+      /*
+        If needed provide default values for MATCH and
+        ON UPDATE/DELETE clauses.
+      */
+      if (fk_key->match_opt == Foreign_key::FK_MATCH_UNDEF)
+        fk_key->match_opt= Foreign_key::FK_MATCH_SIMPLE;
+      if (fk_key->update_opt == Foreign_key::FK_OPTION_UNDEF)
+        fk_key->update_opt= Foreign_key::FK_OPTION_NO_ACTION;
+      if (fk_key->delete_opt == Foreign_key::FK_OPTION_UNDEF)
+        fk_key->delete_opt= Foreign_key::FK_OPTION_NO_ACTION;
+
+      if (opt_fk_all_engines &&
+          fk_key->match_opt == Foreign_key::FK_MATCH_PARTIAL)
+      {
+        my_error(ER_FK_MATCH_PARTIAL, MYF(0),
+                 (fk_key->name.str && !fk_key->name_generated ?
+                                      fk_key->name.str :
+                                      "foreign key without name"));
+	DBUG_RETURN(TRUE);
+      }
+
+      if (opt_fk_all_engines && !fk_key->ref_columns.elements)
+      {
+        my_error(ER_FK_PARENT_COLUMN_MANDATORY, MYF(0),
+                 (fk_key->name.str && !fk_key->name_generated ?
+                                      fk_key->name.str :
+                                      "foreign key without name"));
+	DBUG_RETURN(TRUE);
+      }
+
       if (fk_key->ref_columns.elements &&
 	  fk_key->ref_columns.elements != fk_key->columns.elements)
       {
         my_error(ER_WRONG_FK_DEF, MYF(0),
-                 (fk_key->name ?  fk_key->name : "foreign key without name"),
+                 (fk_key->name.str && !fk_key->name_generated ?
+                                      fk_key->name.str :
+                                      "foreign key without name"),
                  ER(ER_KEY_REF_DO_NOT_MATCH_TABLE_REF));
 	DBUG_RETURN(TRUE);
       }
@@ -2553,6 +2588,9 @@ mysql_prepare_create_table(THD *thd, HA_
                      fk_key->columns);
         alter_info->key_list.push_back(key);
       }
+
+      /* At this point we should know names of constraints already. */
+      DBUG_ASSERT(!opt_fk_all_engines || fk_key->name.str);
       continue;
     }
     (*key_count)++;
@@ -2562,8 +2600,7 @@ mysql_prepare_create_table(THD *thd, HA_
       my_error(ER_TOO_MANY_KEY_PARTS,MYF(0),tmp);
       DBUG_RETURN(TRUE);
     }
-    key_name_str.str= (char*) key->name;
-    key_name_str.length= key->name ? strlen(key->name) : 0;
+    key_name_str= key->name;
     if (check_identifier_name(&key_name_str, ER_TOO_LONG_IDENT))
       DBUG_RETURN(TRUE);
     key_iterator2.rewind ();
@@ -2577,7 +2614,7 @@ mysql_prepare_create_table(THD *thd, HA_
           Then we do not need the generated shorter key.
         */
         if ((key2->type != Key::FOREIGN_KEY &&
-             key2->name != ignore_key &&
+             key2->name.str != ignore_key &&
              !foreign_key_prefix(key, key2)))
         {
           /* TODO: issue warning message */
@@ -2585,10 +2622,10 @@ mysql_prepare_create_table(THD *thd, HA_
           if (!key2->generated ||
               (key->generated && key->columns.elements <
                key2->columns.elements))
-            key->name= ignore_key;
+            key->name.str= ignore_key;
           else
           {
-            key2->name= ignore_key;
+            key2->name.str= ignore_key;
             key_parts-= key2->columns.elements;
             (*key_count)--;
           }
@@ -2596,14 +2633,14 @@ mysql_prepare_create_table(THD *thd, HA_
         }
       }
     }
-    if (key->name != ignore_key)
+    if (key->name.str != ignore_key)
       key_parts+=key->columns.elements;
     else
       (*key_count)--;
-    if (key->name && !tmp_table && (key->type != Key::PRIMARY) &&
-	!my_strcasecmp(system_charset_info,key->name,primary_key_name))
+    if (key->name.str && !tmp_table && (key->type != Key::PRIMARY) &&
+	!my_strcasecmp(system_charset_info,key->name.str, primary_key_name))
     {
-      my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key->name);
+      my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key->name.str);
       DBUG_RETURN(TRUE);
     }
   }
@@ -2626,12 +2663,12 @@ mysql_prepare_create_table(THD *thd, HA_
     uint key_length=0;
     Key_part_spec *column;
 
-    if (key->name == ignore_key)
+    if (key->name.str == ignore_key)
     {
       /* ignore redundant keys */
       do
 	key=key_iterator++;
-      while (key && key->name == ignore_key);
+      while (key && key->name.str == ignore_key);
       if (!key)
 	break;
     }
@@ -2774,22 +2811,22 @@ mysql_prepare_create_table(THD *thd, HA_
       field=0;
       while ((sql_field=it++) &&
 	     my_strcasecmp(system_charset_info,
-			   column->field_name,
+			   column->field_name.str,
 			   sql_field->field_name))
 	field++;
       if (!sql_field)
       {
-	my_error(ER_KEY_COLUMN_DOES_NOT_EXITS, MYF(0), column->field_name);
+	my_error(ER_KEY_COLUMN_DOES_NOT_EXITS, MYF(0), column->field_name.str);
 	DBUG_RETURN(TRUE);
       }
       while ((dup_column= cols2++) != column)
       {
         if (!my_strcasecmp(system_charset_info,
-	     	           column->field_name, dup_column->field_name))
+	     	           column->field_name.str, dup_column->field_name.str))
 	{
 	  my_printf_error(ER_DUP_FIELDNAME,
 			  ER(ER_DUP_FIELDNAME),MYF(0),
-			  column->field_name);
+			  column->field_name.str);
 	  DBUG_RETURN(TRUE);
 	}
       }
@@ -2803,7 +2840,7 @@ mysql_prepare_create_table(THD *thd, HA_
 	    sql_field->charset->mbminlen > 1 || // ucs2 doesn't work yet
 	    (ft_key_charset && sql_field->charset != ft_key_charset))
 	{
-	    my_error(ER_BAD_FT_COLUMN, MYF(0), column->field_name);
+	    my_error(ER_BAD_FT_COLUMN, MYF(0), column->field_name.str);
 	    DBUG_RETURN(-1);
 	}
 	ft_key_charset=sql_field->charset;
@@ -2831,7 +2868,7 @@ mysql_prepare_create_table(THD *thd, HA_
 	{
 	  if (!(file->ha_table_flags() & HA_CAN_INDEX_BLOBS))
 	  {
-	    my_error(ER_BLOB_USED_AS_KEY, MYF(0), column->field_name);
+	    my_error(ER_BLOB_USED_AS_KEY, MYF(0), column->field_name.str);
 	    DBUG_RETURN(TRUE);
 	  }
           if (f_is_geom(sql_field->pack_flag) && sql_field->geom_type ==
@@ -2839,7 +2876,7 @@ mysql_prepare_create_table(THD *thd, HA_
             column->length= 25;
 	  if (!column->length)
 	  {
-	    my_error(ER_BLOB_KEY_WITHOUT_LENGTH, MYF(0), column->field_name);
+	    my_error(ER_BLOB_KEY_WITHOUT_LENGTH, MYF(0), column->field_name.str);
 	    DBUG_RETURN(TRUE);
 	  }
 	}
@@ -2870,7 +2907,7 @@ mysql_prepare_create_table(THD *thd, HA_
             key_info->flags|= HA_NULL_PART_KEY;
             if (!(file->ha_table_flags() & HA_NULL_IN_KEY))
             {
-              my_error(ER_NULL_COLUMN_IN_INDEX, MYF(0), column->field_name);
+              my_error(ER_NULL_COLUMN_IN_INDEX, MYF(0), column->field_name.str);
               DBUG_RETURN(TRUE);
             }
             if (key->type == Key::SPATIAL)
@@ -2935,7 +2972,7 @@ mysql_prepare_create_table(THD *thd, HA_
       }
       else if (length == 0)
       {
-	my_error(ER_WRONG_KEY_COLUMN, MYF(0), column->field_name);
+	my_error(ER_WRONG_KEY_COLUMN, MYF(0), column->field_name.str);
 	  DBUG_RETURN(TRUE);
       }
       if (length > file->max_key_part_length() && key->type != Key::FULLTEXT)
@@ -2993,7 +3030,7 @@ mysql_prepare_create_table(THD *thd, HA_
 	  key_name=primary_key_name;
 	  primary_key=1;
 	}
-	else if (!(key_name = key->name))
+	else if (!(key_name= key->name.str))
 	  key_name=make_unique_key_name(sql_field->field_name,
 					*key_info_buffer, key_info);
 	if (check_if_keyname_exists(key_name, *key_info_buffer, key_info))
@@ -3521,7 +3558,8 @@ bool mysql_create_table_no_lock(THD *thd
   path[path_length - reg_ext_length]= '\0'; // Remove .frm extension
   if (rea_create_table(thd, path, db, table_name,
                        create_info, alter_info->create_list,
-                       key_count, key_info_buffer, file))
+                       key_count, key_info_buffer,
+                       alter_info->key_list, file))
     goto unlock_and_end;
 
   if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
@@ -3623,6 +3661,9 @@ bool mysql_create_table(THD *thd, const 
     }
   }
 
+  if (opt_fk_all_engines)
+    fk_generate_constraint_names(thd, table_name, alter_info->key_list);
+
   result= mysql_create_table_no_lock(thd, db, table_name, create_info,
                                      alter_info,
                                      internal_tmp_table,
@@ -4615,7 +4656,7 @@ bool mysql_create_like_schema_frm(THD* t
   if (mysql_create_frm(thd, dst_path, NullS, NullS,
                        &local_create_info, alter_info.create_list,
                        keys, schema_table->table->s->key_info,
-                       schema_table->table->file))
+                       alter_info.key_list, schema_table->table->file))
     DBUG_RETURN(1);
   DBUG_RETURN(0);
 }
@@ -5034,7 +5075,7 @@ bool
 compare_tables(THD *thd,
                TABLE *table,
                Alter_info *alter_info,
-                           HA_CREATE_INFO *create_info,
+               HA_CREATE_INFO *create_info,
                uint order_num,
                HA_ALTER_FLAGS *alter_flags,
                HA_ALTER_INFO *ha_alter_info,
@@ -6048,12 +6089,18 @@ mysql_prepare_alter_table(THD *thd, TABL
 	  key_part_length= 0;			// Use whole field
       }
       key_part_length /= key_part->field->charset()->mbmaxlen;
+      /*
+        QQ: strlen() is nasty... But Create_field::field_name is sometimes
+            initialized from Field::field_name and so on...
+      */
       key_parts.push_back(new Key_part_spec(cfield->field_name,
+                                            strlen(cfield->field_name),
 					    key_part_length));
     }
     if (key_parts.elements)
     {
       KEY_CREATE_INFO key_create_info;
+      LEX_STRING key_name_str;
       Key *key;
       enum Key::Keytype key_type;
       bzero((char*) &key_create_info, sizeof(key_create_info));
@@ -6080,9 +6127,12 @@ mysql_prepare_alter_table(THD *thd, TABL
       else
         key_type= Key::MULTIPLE;
 
-      key= new Key(key_type, key_name,
-                                 &key_create_info,
-                                 test(key_info->flags & HA_GENERATED_KEY),
+      key_name_str.str= key_name;
+      key_name_str.length= strlen(key_name);
+
+      key= new Key(key_type, key_name_str,
+                   &key_create_info,
+                   test(key_info->flags & HA_GENERATED_KEY),
                    key_parts);
       new_key_list.push_back(key);
     }
@@ -6093,8 +6143,8 @@ mysql_prepare_alter_table(THD *thd, TABL
     {
       if (key->type != Key::FOREIGN_KEY)
         new_key_list.push_back(key);
-      if (key->name &&
-	  !my_strcasecmp(system_charset_info,key->name,primary_key_name))
+      if (key->name.str &&
+	  !my_strcasecmp(system_charset_info, key->name.str, primary_key_name))
       {
 	my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key->name);
         goto err;
diff -Nrup a/sql/sql_yacc.yy b/sql/sql_yacc.yy
--- a/sql/sql_yacc.yy	2008-02-04 16:42:40 +03:00
+++ b/sql/sql_yacc.yy	2008-02-18 13:29:31 +03:00
@@ -1124,7 +1124,7 @@ bool my_yyoverflow(short **a, YYSTYPE **
         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
-        field_spec
+        field_spec opt_constraint constraint opt_ident
 
 %type <lex_str_ptr>
         opt_table_alias
@@ -1133,8 +1133,7 @@ bool my_yyoverflow(short **a, YYSTYPE **
         table_ident table_ident_nodb references xid
 
 %type <simple_string>
-        remember_name remember_end opt_ident opt_db text_or_password
-        opt_constraint constraint
+        remember_name remember_end opt_db text_or_password
 
 %type <string>
         text_string opt_gconcat_separator
@@ -1687,7 +1686,7 @@ create:
               my_parse_error(ER(ER_SYNTAX_ERROR));
               MYSQL_YYABORT;
             }
-            key= new Key($3, $5.str, &lex->key_create_info, 0,
+            key= new Key($3, $5, &lex->key_create_info, 0,
                          lex->col_list);
             lex->alter_info.key_list.push_back(key);
             lex->col_list.empty();
@@ -4554,8 +4553,13 @@ column_def:
           {
             LEX *lex= Lex;
             Key *key;
-            lex->col_list.push_back(new Key_part_spec($1.str));
-            key= new Foreign_key($2, FALSE, FALSE, lex->col_list, $3,
+            TABLE_LIST *table;
+            lex->col_list.push_back(new Key_part_spec($1, 0));
+            if (!(table= lex->select_lex.add_table_to_list(YYTHD, $3, NULL,
+                                                           TL_OPTION_UPDATING,
+                                                           TL_IGNORE)))
+              YYABORT;
+            key= new Foreign_key($2, FALSE, FALSE, lex->col_list, table,
                                  lex->ref_list, lex->fk_delete_opt,
                                  lex->fk_update_opt, lex->fk_match_option);
             lex->alter_info.key_list.push_back(key);
@@ -4583,8 +4587,7 @@ key_def:
           '(' key_list ')' key_options
           {
             LEX *lex=Lex;
-            const char *key_name= $3 ? $3 : $1;
-            Key *key= new Key($2, key_name, &lex->key_create_info, 0,
+            Key *key= new Key($2, $3.str ? $3 : $1, &lex->key_create_info, 0,
                               lex->col_list);
             lex->alter_info.key_list.push_back(key);
             lex->col_list.empty(); /* Alloced by sql_alloc */
@@ -4592,13 +4595,16 @@ key_def:
         | opt_constraint FOREIGN KEY_SYM opt_ident '(' key_list ')' references
           {
             LEX *lex=Lex;
-            Key *key= new Foreign_key($1 ? $1 : $4, (!$1) && $4, TRUE,
-                                      lex->col_list,
-                                      $8,
-                                      lex->ref_list,
-                                      lex->fk_delete_opt,
-                                      lex->fk_update_opt,
-                                      lex->fk_match_option);
+            TABLE_LIST *table;
+            Key *key;
+            if (!(table= lex->select_lex.add_table_to_list(YYTHD, $8, NULL,
+                                                           TL_OPTION_UPDATING,
+                                                           TL_IGNORE)))
+              YYABORT;
+            key= new Foreign_key($1.str ? $1 : $4, (!$1.str) && $4.str, TRUE,
+                                 lex->col_list, table, lex->ref_list,
+                                 lex->fk_delete_opt, lex->fk_update_opt,
+                                 lex->fk_match_option);
             lex->alter_info.key_list.push_back(key);
             lex->col_list.empty(); /* Alloced by sql_alloc */
             /* Only used for ALTER TABLE. Ignored otherwise. */
@@ -4624,7 +4630,7 @@ check_constraint:
         ;
 
 opt_constraint:
-          /* empty */ { $$=(char*) 0; }
+          /* empty */ { $$= null_lex_str; }
         | constraint { $$= $1; }
         ;
 
@@ -5213,8 +5219,8 @@ opt_ref_list:
         ;
 
 ref_list:
-          ref_list ',' ident { Lex->ref_list.push_back(new Key_part_spec($3.str)); }
-        | ident { Lex->ref_list.push_back(new Key_part_spec($1.str)); }
+          ref_list ',' ident { Lex->ref_list.push_back(new Key_part_spec($3, 0)); }
+        | ident { Lex->ref_list.push_back(new Key_part_spec($1, 0)); }
         ;
 
 opt_on_delete:
@@ -5357,7 +5363,7 @@ key_list:
         ;
 
 key_part:
-          ident { $$=new Key_part_spec($1.str); }
+          ident { $$=new Key_part_spec($1, 0); }
         | ident '(' NUM ')'
           {
             int key_part_len= atoi($3.str);
@@ -5365,13 +5371,13 @@ key_part:
             {
               my_error(ER_KEY_PART_0, MYF(0), $1.str);
             }
-            $$=new Key_part_spec($1.str,(uint) key_part_len);
+            $$=new Key_part_spec($1, (uint) key_part_len);
           }
         ;
 
 opt_ident:
-          /* empty */ { $$=(char*) 0; /* Default length */ }
-        | field_ident { $$=$1.str; }
+          /* empty */ { $$= null_lex_str; }
+        | field_ident { $$= $1; }
         ;
 
 opt_component:
@@ -5872,14 +5878,16 @@ alter_list_item:
         | DROP constraint
           {
             LEX *lex= Lex;
-            Alter_drop *alter_drop= new Alter_drop(Alter_drop::FOREIGN_KEY, $2);
+            Alter_drop *alter_drop= new Alter_drop(Alter_drop::FOREIGN_KEY,
+                                                   $2.str);
             lex->alter_info.drop_list.push_back(alter_drop);
             lex->alter_info.flags|= ALTER_FOREIGN_KEY;
           }
         | DROP FOREIGN KEY_SYM opt_ident
           {
             LEX *lex= Lex;
-            Alter_drop *alter_drop= new Alter_drop(Alter_drop::FOREIGN_KEY, $4);
+            Alter_drop *alter_drop= new Alter_drop(Alter_drop::FOREIGN_KEY,
+                                                   $4.str);
             lex->alter_info.drop_list.push_back(alter_drop);
             lex->alter_info.flags|= ALTER_FOREIGN_KEY;
           }
diff -Nrup a/sql/table.cc b/sql/table.cc
--- a/sql/table.cc	2007-12-19 03:46:32 +03:00
+++ b/sql/table.cc	2008-02-18 13:29:31 +03:00
@@ -20,6 +20,7 @@
 #include "sql_trigger.h"
 #include <m_ctype.h>
 #include "my_md5.h"
+#include "sql_fk.h"
 
 /* INFORMATION_SCHEMA name */
 LEX_STRING INFORMATION_SCHEMA_NAME= {C_STRING_WITH_LEN("information_schema")};
@@ -39,8 +40,6 @@ void open_table_error(TABLE_SHARE *share
                       myf errortype, int errarg);
 static int open_binary_frm(THD *thd, TABLE_SHARE *share,
                            uchar *head, File file);
-static void fix_type_pointers(const char ***array, TYPELIB *point_to_type,
-			      uint types, char **names);
 static uint find_field(Field **fields, uchar *record, uint start, uint length);
 
 inline bool is_system_table_name(const char *name, uint length);
@@ -321,6 +320,8 @@ TABLE_SHARE *alloc_table_share(TABLE_LIS
     memcpy((char*) &share->mem_root, (char*) &mem_root, sizeof(mem_root));
     pthread_mutex_init(&share->mutex, MY_MUTEX_INIT_FAST);
     pthread_cond_init(&share->cond, NULL);
+
+    share->fkeys.empty();
   }
   DBUG_RETURN(share);
 }
@@ -384,6 +385,8 @@ void init_tmp_table_share(THD *thd, TABL
   */
   share->table_map_id= (ulong) thd->query_id;
 
+  share->fkeys.empty();
+
   DBUG_VOID_RETURN;
 }
 
@@ -667,6 +670,7 @@ static int open_binary_frm(THD *thd, TAB
   SQL_CRYPT *crypted=0;
   Field  **field_ptr, *reg_field;
   const char **interval_array;
+  unsigned int *interval_lengths;
   enum legacy_db_type legacy_db_type;
   my_bitmap_map *bitmaps;
   uchar *buff= 0;
@@ -1065,6 +1069,13 @@ static int open_binary_frm(THD *thd, TAB
         next_chunk+= format_section_len;
       }
     }
+
+    DBUG_ASSERT (next_chunk <= buff_end);
+
+    if (fk_restore_from_frm_section(thd, share, (const char**)&next_chunk,
+                                    (const char*)buff_end))
+      goto err;
+
     DBUG_ASSERT (next_chunk <= buff_end);
     if (next_chunk > buff_end)
     {
@@ -1119,6 +1130,8 @@ static int open_binary_frm(THD *thd, TAB
 			   interval_count*sizeof(TYPELIB)+
 			   (share->fields+interval_parts+
 			    keys+3)*sizeof(char *)+
+			   (share->fields+interval_parts+
+			    keys+3)*sizeof(unsigned int)+
 			   (n_length+int_length+com_length)))))
     goto err;                                   /* purecov: inspected */
 
@@ -1139,7 +1152,9 @@ static int open_binary_frm(THD *thd, TAB
 
   share->intervals= (TYPELIB*) (field_ptr+share->fields+1);
   interval_array= (const char **) (share->intervals+interval_count);
-  names= (char*) (interval_array+share->fields+interval_parts+keys+3);
+  interval_lengths= (unsigned int *) (interval_array+share->fields+
+                                      interval_parts+keys+3);
+  names= (char*) (interval_lengths+share->fields+interval_parts+keys+3);
   if (!interval_count)
     share->intervals= 0;			// For better debugging
   memcpy((char*) names, strpos+(share->fields*field_pack_length),
@@ -1147,34 +1162,17 @@ static int open_binary_frm(THD *thd, TAB
   comment_pos= names+(n_length+int_length);
   memcpy(comment_pos, disk_buff+read_length-com_length, com_length);
 
-  fix_type_pointers(&interval_array, &share->fieldnames, 1, &names);
+  fix_type_pointers(&interval_array, &interval_lengths, &share->fieldnames,
+                    1, &names);
   if (share->fieldnames.count != share->fields)
     goto err;
-  fix_type_pointers(&interval_array, share->intervals, interval_count,
+  fix_type_pointers(&interval_array, &interval_lengths,
+                    share->intervals, interval_count,
 		    &names);
 
-  {
-    /* Set ENUM and SET lengths */
-    TYPELIB *interval;
-    for (interval= share->intervals;
-         interval < share->intervals + interval_count;
-         interval++)
-    {
-      uint count= (uint) (interval->count + 1) * sizeof(uint);
-      if (!(interval->type_lengths= (uint *) alloc_root(&share->mem_root,
-                                                        count)))
-        goto err;
-      for (count= 0; count < interval->count; count++)
-      {
-        char *val= (char*) interval->type_names[count];
-        interval->type_lengths[count]= strlen(val);
-      }
-      interval->type_lengths[count]= 0;
-    }
-  }
-
   if (keynames)
-    fix_type_pointers(&interval_array, &share->keynames, 1, &keynames);
+    fix_type_pointers(&interval_array, &interval_lengths,
+                      &share->keynames, 1, &keynames);
 
  /* Allocate handler */
   if (!(handler_file= get_new_handler(share, thd->mem_root,
@@ -2113,7 +2111,8 @@ ulong get_form_pos(File file, uchar *hea
   DBUG_ENTER("get_form_pos");
 
   names=uint2korr(head+8);
-  a_length=(names+2)*sizeof(char *);		/* Room for two extra */
+  /* Room for two extra */
+  a_length=(names+2)*(sizeof(char *)+sizeof(unsigned int));
 
   if (!save_names)
     a_length=0;
@@ -2145,8 +2144,10 @@ ulong get_form_pos(File file, uchar *hea
   else
   {
     char *str;
+    unsigned int *lengths;
+    lengths= (unsigned int *)(buf+ ((names+2)*sizeof(char *)));
     str=(char *) (buf+a_length);
-    fix_type_pointers((const char ***) &buf,save_names,1,&str);
+    fix_type_pointers((const char ***) &buf, &lengths, save_names, 1, &str);
   }
   DBUG_RETURN(ret_value);
 }
@@ -2319,14 +2320,30 @@ void open_table_error(TABLE_SHARE *share
 } /* open_table_error */
 
 
-	/*
-	** fix a str_type to a array type
-	** typeparts separated with some char. differents types are separated
-	** with a '\0'
-	*/
+/**
+   Load several types (TYPELIBs) from sequence of bytes.
+
+   @note Type elements are separated with some char (which goes first in type
+         representation). Different types are separated with a '\0'.
+
+   @param array         [in/out] On entering function points to first element
+                                 in array for storing type elements, on leaving
+                                 points to element after last used.
+   @param lengths       [in/out] On entering function points to first element
+                                 in array for storing lengths of type elements,
+                                 on leaving points to element after last used.
+   @param point_to_type [in/out] On entering function points to first element
+                                 array where types should be stored, on leaving
+                                 to element after last used element.
+   @param types         [in]     Number of types to be read
+   @param names         [in/out] On entering function points to the begging of
+                                 the buffer containing types descriptions, on
+                                 leaving position after these descriptions.
+*/
 
-static void
-fix_type_pointers(const char ***array, TYPELIB *point_to_type, uint types,
+void
+fix_type_pointers(const char ***array, unsigned int **lengths,
+                  TYPELIB *point_to_type, uint types,
 		  char **names)
 {
   char *type_name, *ptr;
@@ -2337,12 +2354,14 @@ fix_type_pointers(const char ***array, T
   {
     point_to_type->name=0;
     point_to_type->type_names= *array;
+    point_to_type->type_lengths= *lengths;
 
     if ((chr= *ptr))			/* Test if empty type */
     {
       while ((type_name=strchr(ptr+1,chr)) != NullS)
       {
 	*((*array)++) = ptr+1;
+        *((*lengths)++)= type_name-ptr-1;
 	*type_name= '\0';		/* End string */
 	ptr=type_name;
       }
@@ -2353,6 +2372,7 @@ fix_type_pointers(const char ***array, T
     point_to_type->count= (uint) (*array - point_to_type->type_names);
     point_to_type++;
     *((*array)++)= NullS;		/* End of type */
+    *((*lengths)++)= 0;
   }
   *names=ptr;				/* Update end */
   return;
diff -Nrup a/sql/table.h b/sql/table.h
--- a/sql/table.h	2007-12-19 03:46:32 +03:00
+++ b/sql/table.h	2008-02-18 13:29:31 +03:00
@@ -24,6 +24,7 @@ class st_select_lex;
 class partition_info;
 class COND_EQUAL;
 class Security_context;
+class Foreign_key;
 
 /*************************************************************************/
 
@@ -370,6 +371,8 @@ typedef struct st_table_share
   handlerton *default_part_db_type;
 #endif
 
+  /** List of foreign keys for this table. */
+  List<Foreign_key> fkeys;
 
   /*
     Set share's table cache key and update its db and table name appropriately.
diff -Nrup a/sql/unireg.cc b/sql/unireg.cc
--- a/sql/unireg.cc	2008-01-20 01:11:04 +03:00
+++ b/sql/unireg.cc	2008-02-18 13:29:31 +03:00
@@ -26,6 +26,7 @@
 #include "mysql_priv.h"
 #include <m_ctype.h>
 #include <assert.h>
+#include "sql_fk.h"
 
 #define FCOMP			17		/* Bytes for a packed field */
 
@@ -89,6 +90,7 @@ handle_error(uint sql_errno,
     create_fields	Fields to create
     keys		number of keys to create
     key_info		Keys to create
+    fkey_list           List of foreign keys to be created
     db_file		Handler to use. May be zero, in which case we use
 			create_info->db_type
   RETURN
@@ -101,10 +103,12 @@ bool mysql_create_frm(THD *thd, const ch
 		      HA_CREATE_INFO *create_info,
 		      List<Create_field> &create_fields,
 		      uint keys, KEY *key_info,
+                      List<Key> &fkey_list,
 		      handler *db_file)
 {
   LEX_STRING str_db_type;
-  uint reclength, info_length, screens, key_info_length, maxlength, tmp_len, i;
+  uint reclength, info_length, screens, key_info_length, maxlength, tmp_len, i,
+       fk_sec_length;
   ulong key_buff_length;
   File file;
   ulong filepos, data_offset;
@@ -240,6 +244,12 @@ bool mysql_create_frm(THD *thd, const ch
 #endif
   }
 
+  if (opt_fk_all_engines)
+  {
+    fk_sec_length= fk_get_frm_section_length(fkey_list);
+    create_info->extra_size+= fk_sec_length;
+  }
+
   if ((file=create_frm(thd, file_name, db, table, reclength, fileinfo,
 		       create_info, keys, key_info)) < 0)
   {
@@ -371,6 +381,13 @@ bool mysql_create_frm(THD *thd, const ch
       }
     }
   }
+
+  if (opt_fk_all_engines)
+  {
+    if (fk_save_to_frm_section(thd, file, fkey_list, fk_sec_length))
+      goto err;
+  }
+
   VOID(my_seek(file,filepos,MY_SEEK_SET,MYF(0)));
   if (my_write(file, forminfo, 288, MYF_RW) ||
       my_write(file, screen_buff, info_length, MYF_RW) ||
@@ -449,6 +466,7 @@ err3:
     create_info		create info parameters
     create_fields	Fields to create
     keys		number of keys to create
+    fkey_list           List of foreign keys to create
     key_info		Keys to create
     file		Handler to use
 
@@ -461,14 +479,16 @@ int rea_create_table(THD *thd, const cha
                      const char *db, const char *table_name,
                      HA_CREATE_INFO *create_info,
                      List<Create_field> &create_fields,
-                     uint keys, KEY *key_info, handler *file)
+                     uint keys, KEY *key_info,
+                     List<Key> &fkey_list,
+                     handler *file)
 {
   DBUG_ENTER("rea_create_table");
 
   char frm_name[FN_REFLEN];
   strxmov(frm_name, path, reg_ext, NullS);
   if (mysql_create_frm(thd, frm_name, db, table_name, create_info,
-                       create_fields, keys, key_info, file))
+                       create_fields, keys, key_info, fkey_list, file))
 
     DBUG_RETURN(1);
 
Thread
bk commit into 6.0 tree (dlenev:1.2796) WL#148dlenev18 Feb