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#148 | dlenev | 18 Feb |