diff options
author | unknown <brian@zim.(none)> | 2005-04-26 19:07:13 -0700 |
---|---|---|
committer | unknown <brian@zim.(none)> | 2005-04-26 19:07:13 -0700 |
commit | a9da10f7a8e626aac0bd3a8d82b20c7b864c7061 (patch) | |
tree | 7028a9aade64c6143da00a4301627ce5d262c0f0 /storage/innobase/dict | |
parent | a2ed27af5291f778c1fc6e23cf4edc1bc36f0bed (diff) | |
parent | 25311ea4a5f83652959a0744d99a4eb51aa9d328 (diff) | |
download | mariadb-git-a9da10f7a8e626aac0bd3a8d82b20c7b864c7061.tar.gz |
Merge baker@bk-internal.mysql.com:/home/bk/mysql-5.1
into zim.(none):/home/brian/mysql/mysql-5.1
configure.in:
Auto merged
mysql-test/mysql-test-run.sh:
Auto merged
sql/Makefile.am:
Auto merged
sql/ha_innodb.cc:
Auto merged
sql/ha_myisam.cc:
Auto merged
sql/lock.cc:
Auto merged
storage/innobase/btr/btr0pcur.c:
Auto merged
storage/innobase/dict/dict0dict.c:
Auto merged
storage/innobase/dict/dict0load.c:
Auto merged
storage/innobase/fil/fil0fil.c:
Auto merged
storage/innobase/include/btr0pcur.h:
Auto merged
storage/innobase/include/btr0pcur.ic:
Auto merged
storage/innobase/include/dict0dict.h:
Auto merged
storage/innobase/include/dict0load.h:
Auto merged
storage/innobase/include/os0file.h:
Auto merged
storage/innobase/include/srv0srv.h:
Auto merged
storage/innobase/log/log0log.c:
Auto merged
storage/innobase/os/os0file.c:
Auto merged
storage/innobase/row/row0ins.c:
Auto merged
storage/innobase/row/row0mysql.c:
Auto merged
storage/innobase/row/row0sel.c:
Auto merged
storage/innobase/srv/srv0srv.c:
Auto merged
storage/innobase/srv/srv0start.c:
Auto merged
storage/myisam/mi_check.c:
Auto merged
storage/myisam/mi_create.c:
Auto merged
storage/myisam/mi_dynrec.c:
Auto merged
storage/myisam/mi_search.c:
Auto merged
storage/myisam/mi_write.c:
Auto merged
storage/myisam/myisamdef.h:
Auto merged
storage/myisam/myisampack.c:
Auto merged
storage/myisammrg/myrg_create.c:
Auto merged
storage/ndb/include/kernel/signaldata/BackupImpl.hpp:
Auto merged
storage/ndb/include/kernel/signaldata/BackupSignalData.hpp:
Auto merged
storage/ndb/include/kernel/signaldata/TuxMaint.hpp:
Auto merged
storage/ndb/include/mgmapi/mgmapi_config_parameters.h:
Auto merged
storage/ndb/include/ndbapi/NdbScanOperation.hpp:
Auto merged
storage/ndb/include/ndbapi/NdbTransaction.hpp:
Auto merged
storage/ndb/src/common/debugger/signaldata/BackupImpl.cpp:
Auto merged
storage/ndb/src/kernel/blocks/ERROR_codes.txt:
Auto merged
storage/ndb/src/kernel/blocks/backup/Backup.cpp:
Auto merged
storage/ndb/src/kernel/blocks/backup/Backup.hpp:
Auto merged
storage/ndb/src/kernel/blocks/backup/Backup.txt:
Auto merged
storage/ndb/src/kernel/blocks/backup/BackupInit.cpp:
Auto merged
storage/ndb/src/kernel/blocks/cmvmi/Cmvmi.cpp:
Auto merged
storage/ndb/src/kernel/blocks/dbacc/Dbacc.hpp:
Auto merged
storage/ndb/src/kernel/blocks/dbacc/DbaccMain.cpp:
Auto merged
storage/ndb/src/kernel/blocks/dbdict/Dbdict.cpp:
Auto merged
storage/ndb/src/kernel/main.cpp:
Auto merged
storage/ndb/src/kernel/blocks/dbdih/DbdihMain.cpp:
Auto merged
storage/ndb/src/kernel/blocks/dblqh/Dblqh.hpp:
Auto merged
storage/ndb/src/kernel/blocks/dblqh/DblqhMain.cpp:
Auto merged
storage/ndb/src/kernel/blocks/dbtc/DbtcMain.cpp:
Auto merged
storage/ndb/src/kernel/blocks/dbtup/Dbtup.hpp:
Auto merged
storage/ndb/src/kernel/blocks/dbtup/DbtupRoutines.cpp:
Auto merged
storage/ndb/src/kernel/blocks/dbtup/DbtupTrigger.cpp:
Auto merged
storage/ndb/src/kernel/blocks/dbtup/Notes.txt:
Auto merged
storage/ndb/src/kernel/blocks/dbtux/DbtuxNode.cpp:
Auto merged
storage/ndb/src/kernel/blocks/ndbcntr/NdbcntrMain.cpp:
Auto merged
storage/ndb/src/kernel/blocks/ndbfs/OpenFiles.hpp:
Auto merged
storage/ndb/src/mgmapi/mgmapi.cpp:
Auto merged
storage/ndb/src/mgmsrv/ConfigInfo.cpp:
Auto merged
storage/ndb/src/mgmsrv/MgmtSrvr.cpp:
Auto merged
storage/ndb/src/mgmsrv/MgmtSrvr.hpp:
Auto merged
storage/ndb/src/mgmsrv/MgmtSrvrGeneralSignalHandling.cpp:
Auto merged
storage/ndb/src/ndbapi/Ndb.cpp:
Auto merged
storage/ndb/src/ndbapi/NdbDictionaryImpl.cpp:
Auto merged
storage/ndb/src/ndbapi/NdbIndexOperation.cpp:
Auto merged
storage/ndb/src/ndbapi/NdbOperationDefine.cpp:
Auto merged
storage/ndb/src/ndbapi/NdbOperationSearch.cpp:
Auto merged
storage/ndb/src/ndbapi/NdbScanOperation.cpp:
Auto merged
storage/ndb/src/ndbapi/NdbTransaction.cpp:
Auto merged
storage/ndb/src/ndbapi/Ndbif.cpp:
Auto merged
storage/ndb/src/ndbapi/Ndblist.cpp:
Auto merged
storage/ndb/src/ndbapi/TransporterFacade.cpp:
Auto merged
storage/ndb/src/ndbapi/ndberror.c:
Auto merged
storage/ndb/test/ndbapi/Makefile.am:
Auto merged
storage/ndb/test/ndbapi/testBackup.cpp:
Auto merged
storage/ndb/test/ndbapi/testIndex.cpp:
Auto merged
storage/ndb/test/ndbapi/testNodeRestart.cpp:
Auto merged
storage/ndb/test/ndbapi/testOIBasic.cpp:
Auto merged
storage/ndb/test/ndbapi/testOperations.cpp:
Auto merged
storage/ndb/test/run-test/daily-basic-tests.txt:
Auto merged
storage/ndb/test/run-test/daily-devel-tests.txt:
Auto merged
storage/ndb/test/src/NdbBackup.cpp:
Auto merged
storage/ndb/tools/desc.cpp:
Auto merged
Diffstat (limited to 'storage/innobase/dict')
-rw-r--r-- | storage/innobase/dict/Makefile.am | 25 | ||||
-rw-r--r-- | storage/innobase/dict/dict0boot.c | 423 | ||||
-rw-r--r-- | storage/innobase/dict/dict0crea.c | 1468 | ||||
-rw-r--r-- | storage/innobase/dict/dict0dict.c | 4490 | ||||
-rw-r--r-- | storage/innobase/dict/dict0load.c | 1354 | ||||
-rw-r--r-- | storage/innobase/dict/dict0mem.c | 293 | ||||
-rw-r--r-- | storage/innobase/dict/makefilewin | 21 |
7 files changed, 8074 insertions, 0 deletions
diff --git a/storage/innobase/dict/Makefile.am b/storage/innobase/dict/Makefile.am new file mode 100644 index 00000000000..0034d2f8f1e --- /dev/null +++ b/storage/innobase/dict/Makefile.am @@ -0,0 +1,25 @@ +# Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +# & Innobase Oy +# +# 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; either version 2 of the License, or +# (at your option) any later version. +# +# 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 ../include/Makefile.i + +noinst_LIBRARIES = libdict.a + +libdict_a_SOURCES = dict0boot.c dict0crea.c dict0dict.c dict0load.c\ + dict0mem.c + +EXTRA_PROGRAMS = diff --git a/storage/innobase/dict/dict0boot.c b/storage/innobase/dict/dict0boot.c new file mode 100644 index 00000000000..0f6d55c9341 --- /dev/null +++ b/storage/innobase/dict/dict0boot.c @@ -0,0 +1,423 @@ +/****************************************************** +Data dictionary creation and booting + +(c) 1996 Innobase Oy + +Created 4/18/1996 Heikki Tuuri +*******************************************************/ + +#include "dict0boot.h" + +#ifdef UNIV_NONINL +#include "dict0boot.ic" +#endif + +#include "dict0crea.h" +#include "btr0btr.h" +#include "dict0load.h" +#include "dict0load.h" +#include "trx0trx.h" +#include "srv0srv.h" +#include "ibuf0ibuf.h" +#include "buf0flu.h" +#include "log0recv.h" +#include "os0file.h" + +/************************************************************************** +Gets a pointer to the dictionary header and x-latches its page. */ + +dict_hdr_t* +dict_hdr_get( +/*=========*/ + /* out: pointer to the dictionary header, + page x-latched */ + mtr_t* mtr) /* in: mtr */ +{ + dict_hdr_t* header; + + ut_ad(mtr); + + header = DICT_HDR + buf_page_get(DICT_HDR_SPACE, DICT_HDR_PAGE_NO, + RW_X_LATCH, mtr); +#ifdef UNIV_SYNC_DEBUG + buf_page_dbg_add_level(header, SYNC_DICT_HEADER); +#endif /* UNIV_SYNC_DEBUG */ + return(header); +} + +/************************************************************************** +Returns a new table, index, or tree id. */ + +dulint +dict_hdr_get_new_id( +/*================*/ + /* out: the new id */ + ulint type) /* in: DICT_HDR_ROW_ID, ... */ +{ + dict_hdr_t* dict_hdr; + dulint id; + mtr_t mtr; + + ut_ad((type == DICT_HDR_TABLE_ID) || (type == DICT_HDR_INDEX_ID) + || (type == DICT_HDR_MIX_ID)); + + mtr_start(&mtr); + + dict_hdr = dict_hdr_get(&mtr); + + id = mtr_read_dulint(dict_hdr + type, &mtr); + + /* Add some dummy code here because otherwise pgcc seems to + compile wrong */ + + if (0 == ut_dulint_cmp(id, ut_dulint_max)) { + /* TO DO: remove this code, or make it conditional */ + ut_dbg_null_ptr = 0; + } + + id = ut_dulint_add(id, 1); + + mlog_write_dulint(dict_hdr + type, id, &mtr); + + mtr_commit(&mtr); + + return(id); +} + +/************************************************************************** +Writes the current value of the row id counter to the dictionary header file +page. */ + +void +dict_hdr_flush_row_id(void) +/*=======================*/ +{ + dict_hdr_t* dict_hdr; + dulint id; + mtr_t mtr; + +#ifdef UNIV_SYNC_DEBUG + ut_ad(mutex_own(&(dict_sys->mutex))); +#endif /* UNIV_SYNC_DEBUG */ + + id = dict_sys->row_id; + + mtr_start(&mtr); + + dict_hdr = dict_hdr_get(&mtr); + + mlog_write_dulint(dict_hdr + DICT_HDR_ROW_ID, id, &mtr); + + mtr_commit(&mtr); +} + +/********************************************************************* +Creates the file page for the dictionary header. This function is +called only at the database creation. */ +static +ibool +dict_hdr_create( +/*============*/ + /* out: TRUE if succeed */ + mtr_t* mtr) /* in: mtr */ +{ + dict_hdr_t* dict_header; + ulint hdr_page_no; + ulint root_page_no; + page_t* page; + + ut_ad(mtr); + + /* Create the dictionary header file block in a new, allocated file + segment in the system tablespace */ + page = fseg_create(DICT_HDR_SPACE, 0, + DICT_HDR + DICT_HDR_FSEG_HEADER, mtr); + + hdr_page_no = buf_frame_get_page_no(page); + + ut_a(DICT_HDR_PAGE_NO == hdr_page_no); + + dict_header = dict_hdr_get(mtr); + + /* Start counting row, table, index, and tree ids from + DICT_HDR_FIRST_ID */ + mlog_write_dulint(dict_header + DICT_HDR_ROW_ID, + ut_dulint_create(0, DICT_HDR_FIRST_ID), mtr); + + mlog_write_dulint(dict_header + DICT_HDR_TABLE_ID, + ut_dulint_create(0, DICT_HDR_FIRST_ID), mtr); + + mlog_write_dulint(dict_header + DICT_HDR_INDEX_ID, + ut_dulint_create(0, DICT_HDR_FIRST_ID), mtr); + + mlog_write_dulint(dict_header + DICT_HDR_MIX_ID, + ut_dulint_create(0, DICT_HDR_FIRST_ID), mtr); + + /* Create the B-tree roots for the clustered indexes of the basic + system tables */ + + /*--------------------------*/ + root_page_no = btr_create(DICT_CLUSTERED | DICT_UNIQUE, + DICT_HDR_SPACE, DICT_TABLES_ID, FALSE, mtr); + if (root_page_no == FIL_NULL) { + + return(FALSE); + } + + mlog_write_ulint(dict_header + DICT_HDR_TABLES, root_page_no, + MLOG_4BYTES, mtr); + /*--------------------------*/ + root_page_no = btr_create(DICT_UNIQUE, DICT_HDR_SPACE, + DICT_TABLE_IDS_ID, FALSE, mtr); + if (root_page_no == FIL_NULL) { + + return(FALSE); + } + + mlog_write_ulint(dict_header + DICT_HDR_TABLE_IDS, root_page_no, + MLOG_4BYTES, mtr); + /*--------------------------*/ + root_page_no = btr_create(DICT_CLUSTERED | DICT_UNIQUE, + DICT_HDR_SPACE, DICT_COLUMNS_ID, FALSE, mtr); + if (root_page_no == FIL_NULL) { + + return(FALSE); + } + + mlog_write_ulint(dict_header + DICT_HDR_COLUMNS, root_page_no, + MLOG_4BYTES, mtr); + /*--------------------------*/ + root_page_no = btr_create(DICT_CLUSTERED | DICT_UNIQUE, + DICT_HDR_SPACE, DICT_INDEXES_ID, FALSE, mtr); + if (root_page_no == FIL_NULL) { + + return(FALSE); + } + + mlog_write_ulint(dict_header + DICT_HDR_INDEXES, root_page_no, + MLOG_4BYTES, mtr); + /*--------------------------*/ + root_page_no = btr_create(DICT_CLUSTERED | DICT_UNIQUE, + DICT_HDR_SPACE, DICT_FIELDS_ID, FALSE, mtr); + if (root_page_no == FIL_NULL) { + + return(FALSE); + } + + mlog_write_ulint(dict_header + DICT_HDR_FIELDS, root_page_no, + MLOG_4BYTES, mtr); + /*--------------------------*/ + + return(TRUE); +} + +/********************************************************************* +Initializes the data dictionary memory structures when the database is +started. This function is also called when the data dictionary is created. */ + +void +dict_boot(void) +/*===========*/ +{ + dict_table_t* table; + dict_index_t* index; + dict_hdr_t* dict_hdr; + mtr_t mtr; + ibool success; + + mtr_start(&mtr); + + /* Create the hash tables etc. */ + dict_init(); + + mutex_enter(&(dict_sys->mutex)); + + /* Get the dictionary header */ + dict_hdr = dict_hdr_get(&mtr); + + /* Because we only write new row ids to disk-based data structure + (dictionary header) when it is divisible by + DICT_HDR_ROW_ID_WRITE_MARGIN, in recovery we will not recover + the latest value of the row id counter. Therefore we advance + the counter at the database startup to avoid overlapping values. + Note that when a user after database startup first time asks for + a new row id, then because the counter is now divisible by + ..._MARGIN, it will immediately be updated to the disk-based + header. */ + + dict_sys->row_id = ut_dulint_add( + ut_dulint_align_up( + mtr_read_dulint(dict_hdr + DICT_HDR_ROW_ID, + &mtr), + DICT_HDR_ROW_ID_WRITE_MARGIN), + DICT_HDR_ROW_ID_WRITE_MARGIN); + + /* Insert into the dictionary cache the descriptions of the basic + system tables */ + /*-------------------------*/ + table = dict_mem_table_create("SYS_TABLES", DICT_HDR_SPACE, 8, FALSE); + + dict_mem_table_add_col(table, "NAME", DATA_BINARY, 0, 0, 0); + dict_mem_table_add_col(table, "ID", DATA_BINARY, 0, 0, 0); + dict_mem_table_add_col(table, "N_COLS", DATA_INT, 0, 4, 0); + dict_mem_table_add_col(table, "TYPE", DATA_INT, 0, 4, 0); + dict_mem_table_add_col(table, "MIX_ID", DATA_BINARY, 0, 0, 0); + dict_mem_table_add_col(table, "MIX_LEN", DATA_INT, 0, 4, 0); + dict_mem_table_add_col(table, "CLUSTER_NAME", DATA_BINARY, 0, 0, 0); + dict_mem_table_add_col(table, "SPACE", DATA_INT, 0, 4, 0); + + table->id = DICT_TABLES_ID; + + dict_table_add_to_cache(table); + dict_sys->sys_tables = table; + + index = dict_mem_index_create("SYS_TABLES", "CLUST_IND", + DICT_HDR_SPACE, DICT_UNIQUE | DICT_CLUSTERED, 1); + + dict_mem_index_add_field(index, "NAME", 0, 0); + + index->id = DICT_TABLES_ID; + + success = dict_index_add_to_cache(table, index, mtr_read_ulint( + dict_hdr + DICT_HDR_TABLES, MLOG_4BYTES, &mtr)); + ut_a(success); + /*-------------------------*/ + index = dict_mem_index_create("SYS_TABLES", "ID_IND", + DICT_HDR_SPACE, DICT_UNIQUE, 1); + dict_mem_index_add_field(index, "ID", 0, 0); + + index->id = DICT_TABLE_IDS_ID; + success = dict_index_add_to_cache(table, index, mtr_read_ulint( + dict_hdr + DICT_HDR_TABLE_IDS, MLOG_4BYTES, &mtr)); + ut_a(success); + /*-------------------------*/ + table = dict_mem_table_create("SYS_COLUMNS", DICT_HDR_SPACE, 7, FALSE); + + dict_mem_table_add_col(table, "TABLE_ID", DATA_BINARY,0,0,0); + dict_mem_table_add_col(table, "POS", DATA_INT, 0, 4, 0); + dict_mem_table_add_col(table, "NAME", DATA_BINARY, 0, 0, 0); + dict_mem_table_add_col(table, "MTYPE", DATA_INT, 0, 4, 0); + dict_mem_table_add_col(table, "PRTYPE", DATA_INT, 0, 4, 0); + dict_mem_table_add_col(table, "LEN", DATA_INT, 0, 4, 0); + dict_mem_table_add_col(table, "PREC", DATA_INT, 0, 4, 0); + + table->id = DICT_COLUMNS_ID; + + dict_table_add_to_cache(table); + dict_sys->sys_columns = table; + + index = dict_mem_index_create("SYS_COLUMNS", "CLUST_IND", + DICT_HDR_SPACE, DICT_UNIQUE | DICT_CLUSTERED, 2); + + dict_mem_index_add_field(index, "TABLE_ID", 0, 0); + dict_mem_index_add_field(index, "POS", 0, 0); + + index->id = DICT_COLUMNS_ID; + success = dict_index_add_to_cache(table, index, mtr_read_ulint( + dict_hdr + DICT_HDR_COLUMNS, MLOG_4BYTES, &mtr)); + ut_a(success); + /*-------------------------*/ + table = dict_mem_table_create("SYS_INDEXES", DICT_HDR_SPACE, 7, FALSE); + + dict_mem_table_add_col(table, "TABLE_ID", DATA_BINARY, 0,0,0); + dict_mem_table_add_col(table, "ID", DATA_BINARY, 0, 0, 0); + dict_mem_table_add_col(table, "NAME", DATA_BINARY, 0, 0, 0); + dict_mem_table_add_col(table, "N_FIELDS", DATA_INT, 0, 4, 0); + dict_mem_table_add_col(table, "TYPE", DATA_INT, 0, 4, 0); + dict_mem_table_add_col(table, "SPACE", DATA_INT, 0, 4, 0); + dict_mem_table_add_col(table, "PAGE_NO", DATA_INT, 0, 4, 0); + + /* The '+ 2' below comes from the 2 system fields */ +#if DICT_SYS_INDEXES_PAGE_NO_FIELD != 6 + 2 +#error "DICT_SYS_INDEXES_PAGE_NO_FIELD != 6 + 2" +#endif +#if DICT_SYS_INDEXES_SPACE_NO_FIELD != 5 + 2 +#error "DICT_SYS_INDEXES_SPACE_NO_FIELD != 5 + 2" +#endif +#if DICT_SYS_INDEXES_TYPE_FIELD != 4 + 2 +#error "DICT_SYS_INDEXES_TYPE_FIELD != 4 + 2" +#endif + + table->id = DICT_INDEXES_ID; + dict_table_add_to_cache(table); + dict_sys->sys_indexes = table; + + index = dict_mem_index_create("SYS_INDEXES", "CLUST_IND", + DICT_HDR_SPACE, DICT_UNIQUE | DICT_CLUSTERED, 2); + + dict_mem_index_add_field(index, "TABLE_ID", 0, 0); + dict_mem_index_add_field(index, "ID", 0, 0); + + index->id = DICT_INDEXES_ID; + success = dict_index_add_to_cache(table, index, mtr_read_ulint( + dict_hdr + DICT_HDR_INDEXES, MLOG_4BYTES, &mtr)); + ut_a(success); + /*-------------------------*/ + table = dict_mem_table_create("SYS_FIELDS", DICT_HDR_SPACE, 3, FALSE); + + dict_mem_table_add_col(table, "INDEX_ID", DATA_BINARY, 0,0,0); + dict_mem_table_add_col(table, "POS", DATA_INT, 0, 4, 0); + dict_mem_table_add_col(table, "COL_NAME", DATA_BINARY, 0,0,0); + + table->id = DICT_FIELDS_ID; + dict_table_add_to_cache(table); + dict_sys->sys_fields = table; + + index = dict_mem_index_create("SYS_FIELDS", "CLUST_IND", + DICT_HDR_SPACE, DICT_UNIQUE | DICT_CLUSTERED, 2); + + dict_mem_index_add_field(index, "INDEX_ID", 0, 0); + dict_mem_index_add_field(index, "POS", 0, 0); + + index->id = DICT_FIELDS_ID; + success = dict_index_add_to_cache(table, index, mtr_read_ulint( + dict_hdr + DICT_HDR_FIELDS, MLOG_4BYTES, &mtr)); + ut_a(success); + + mtr_commit(&mtr); + /*-------------------------*/ + + /* Initialize the insert buffer table and index for each tablespace */ + + ibuf_init_at_db_start(); + + /* Load definitions of other indexes on system tables */ + + dict_load_sys_table(dict_sys->sys_tables); + dict_load_sys_table(dict_sys->sys_columns); + dict_load_sys_table(dict_sys->sys_indexes); + dict_load_sys_table(dict_sys->sys_fields); + + mutex_exit(&(dict_sys->mutex)); +} + +/********************************************************************* +Inserts the basic system table data into themselves in the database +creation. */ +static +void +dict_insert_initial_data(void) +/*==========================*/ +{ + /* Does nothing yet */ +} + +/********************************************************************* +Creates and initializes the data dictionary at the database creation. */ + +void +dict_create(void) +/*=============*/ +{ + mtr_t mtr; + + mtr_start(&mtr); + + dict_hdr_create(&mtr); + + mtr_commit(&mtr); + + dict_boot(); + + dict_insert_initial_data(); +} diff --git a/storage/innobase/dict/dict0crea.c b/storage/innobase/dict/dict0crea.c new file mode 100644 index 00000000000..1f12386e413 --- /dev/null +++ b/storage/innobase/dict/dict0crea.c @@ -0,0 +1,1468 @@ +/****************************************************** +Database object creation + +(c) 1996 Innobase Oy + +Created 1/8/1996 Heikki Tuuri +*******************************************************/ + +#include "dict0crea.h" + +#ifdef UNIV_NONINL +#include "dict0crea.ic" +#endif + +#include "btr0pcur.h" +#include "btr0btr.h" +#include "page0page.h" +#include "mach0data.h" +#include "dict0boot.h" +#include "dict0dict.h" +#include "que0que.h" +#include "row0ins.h" +#include "row0mysql.h" +#include "pars0pars.h" +#include "trx0roll.h" +#include "usr0sess.h" + +/********************************************************************* +Based on a table object, this function builds the entry to be inserted +in the SYS_TABLES system table. */ +static +dtuple_t* +dict_create_sys_tables_tuple( +/*=========================*/ + /* out: the tuple which should be inserted */ + dict_table_t* table, /* in: table */ + mem_heap_t* heap) /* in: memory heap from which the memory for + the built tuple is allocated */ +{ + dict_table_t* sys_tables; + dtuple_t* entry; + dfield_t* dfield; + byte* ptr; + + ut_ad(table && heap); + + sys_tables = dict_sys->sys_tables; + + entry = dtuple_create(heap, 8 + DATA_N_SYS_COLS); + + /* 0: NAME -----------------------------*/ + dfield = dtuple_get_nth_field(entry, 0); + + dfield_set_data(dfield, table->name, ut_strlen(table->name)); + /* 3: ID -------------------------------*/ + dfield = dtuple_get_nth_field(entry, 1); + + ptr = mem_heap_alloc(heap, 8); + mach_write_to_8(ptr, table->id); + + dfield_set_data(dfield, ptr, 8); + /* 4: N_COLS ---------------------------*/ + dfield = dtuple_get_nth_field(entry, 2); + + ptr = mem_heap_alloc(heap, 4); + mach_write_to_4(ptr, table->n_def + | ((ulint) table->comp << 31)); + dfield_set_data(dfield, ptr, 4); + /* 5: TYPE -----------------------------*/ + dfield = dtuple_get_nth_field(entry, 3); + + ptr = mem_heap_alloc(heap, 4); + mach_write_to_4(ptr, table->type); + + dfield_set_data(dfield, ptr, 4); + /* 6: MIX_ID ---------------------------*/ + dfield = dtuple_get_nth_field(entry, 4); + + ptr = mem_heap_alloc(heap, 8); + mach_write_to_8(ptr, table->mix_id); + + dfield_set_data(dfield, ptr, 8); + /* 7: MIX_LEN --------------------------*/ + + dfield = dtuple_get_nth_field(entry, 5); + + ptr = mem_heap_alloc(heap, 4); + mach_write_to_4(ptr, table->mix_len); + + dfield_set_data(dfield, ptr, 4); + /* 8: CLUSTER_NAME ---------------------*/ + dfield = dtuple_get_nth_field(entry, 6); + + if (table->type == DICT_TABLE_CLUSTER_MEMBER) { + dfield_set_data(dfield, table->cluster_name, + ut_strlen(table->cluster_name)); + ut_error; /* Oracle-style clusters are not supported yet */ + } else { + dfield_set_data(dfield, NULL, UNIV_SQL_NULL); + } + /* 9: SPACE ----------------------------*/ + dfield = dtuple_get_nth_field(entry, 7); + + ptr = mem_heap_alloc(heap, 4); + mach_write_to_4(ptr, table->space); + + dfield_set_data(dfield, ptr, 4); + /*----------------------------------*/ + + dict_table_copy_types(entry, sys_tables); + + return(entry); +} + +/********************************************************************* +Based on a table object, this function builds the entry to be inserted +in the SYS_COLUMNS system table. */ +static +dtuple_t* +dict_create_sys_columns_tuple( +/*==========================*/ + /* out: the tuple which should be inserted */ + dict_table_t* table, /* in: table */ + ulint i, /* in: column number */ + mem_heap_t* heap) /* in: memory heap from which the memory for + the built tuple is allocated */ +{ + dict_table_t* sys_columns; + dtuple_t* entry; + dict_col_t* column; + dfield_t* dfield; + byte* ptr; + + ut_ad(table && heap); + + column = dict_table_get_nth_col(table, i); + + sys_columns = dict_sys->sys_columns; + + entry = dtuple_create(heap, 7 + DATA_N_SYS_COLS); + + /* 0: TABLE_ID -----------------------*/ + dfield = dtuple_get_nth_field(entry, 0); + + ptr = mem_heap_alloc(heap, 8); + mach_write_to_8(ptr, table->id); + + dfield_set_data(dfield, ptr, 8); + /* 1: POS ----------------------------*/ + dfield = dtuple_get_nth_field(entry, 1); + + ptr = mem_heap_alloc(heap, 4); + mach_write_to_4(ptr, i); + + dfield_set_data(dfield, ptr, 4); + /* 4: NAME ---------------------------*/ + dfield = dtuple_get_nth_field(entry, 2); + + dfield_set_data(dfield, column->name, ut_strlen(column->name)); + /* 5: MTYPE --------------------------*/ + dfield = dtuple_get_nth_field(entry, 3); + + ptr = mem_heap_alloc(heap, 4); + mach_write_to_4(ptr, (column->type).mtype); + + dfield_set_data(dfield, ptr, 4); + /* 6: PRTYPE -------------------------*/ + dfield = dtuple_get_nth_field(entry, 4); + + ptr = mem_heap_alloc(heap, 4); + mach_write_to_4(ptr, (column->type).prtype); + + dfield_set_data(dfield, ptr, 4); + /* 7: LEN ----------------------------*/ + dfield = dtuple_get_nth_field(entry, 5); + + ptr = mem_heap_alloc(heap, 4); + mach_write_to_4(ptr, (column->type).len); + + dfield_set_data(dfield, ptr, 4); + /* 8: PREC ---------------------------*/ + dfield = dtuple_get_nth_field(entry, 6); + + ptr = mem_heap_alloc(heap, 4); + mach_write_to_4(ptr, (column->type).prec); + + dfield_set_data(dfield, ptr, 4); + /*---------------------------------*/ + + dict_table_copy_types(entry, sys_columns); + + return(entry); +} + +/******************************************************************* +Builds a table definition to insert. */ +static +ulint +dict_build_table_def_step( +/*======================*/ + /* out: DB_SUCCESS or error code */ + que_thr_t* thr, /* in: query thread */ + tab_node_t* node) /* in: table create node */ +{ + dict_table_t* table; + dict_table_t* cluster_table; + dtuple_t* row; + ulint error; + const char* path_or_name; + ibool is_path; + mtr_t mtr; + ulint i; + ulint row_len; + +#ifdef UNIV_SYNC_DEBUG + ut_ad(mutex_own(&(dict_sys->mutex))); +#endif /* UNIV_SYNC_DEBUG */ + + table = node->table; + + table->id = dict_hdr_get_new_id(DICT_HDR_TABLE_ID); + + thr_get_trx(thr)->table_id = table->id; + + row_len = 0; + for (i = 0; i < table->n_def; i++) { + row_len += dtype_get_min_size(dict_col_get_type( + &table->cols[i])); + } + if (row_len > BTR_PAGE_MAX_REC_SIZE) { + return(DB_TOO_BIG_RECORD); + } + + if (table->type == DICT_TABLE_CLUSTER_MEMBER) { + + cluster_table = dict_table_get_low(table->cluster_name); + + if (cluster_table == NULL) { + + return(DB_CLUSTER_NOT_FOUND); + } + + /* Inherit space and mix len from the cluster */ + + table->space = cluster_table->space; + table->mix_len = cluster_table->mix_len; + + table->mix_id = dict_hdr_get_new_id(DICT_HDR_MIX_ID); + } + + if (srv_file_per_table) { + /* We create a new single-table tablespace for the table. + We initially let it be 4 pages: + - page 0 is the fsp header and an extent descriptor page, + - page 1 is an ibuf bitmap page, + - page 2 is the first inode page, + - page 3 will contain the root of the clustered index of the + table we create here. */ + + table->space = 0; /* reset to zero for the call below */ + + if (table->dir_path_of_temp_table) { + /* We place tables created with CREATE TEMPORARY + TABLE in the tmp dir of mysqld server */ + + path_or_name = table->dir_path_of_temp_table; + is_path = TRUE; + } else { + path_or_name = table->name; + is_path = FALSE; + } + + error = fil_create_new_single_table_tablespace( + &(table->space), path_or_name, is_path, + FIL_IBD_FILE_INITIAL_SIZE); + if (error != DB_SUCCESS) { + + return(error); + } + + mtr_start(&mtr); + + fsp_header_init(table->space, FIL_IBD_FILE_INITIAL_SIZE, &mtr); + + mtr_commit(&mtr); + } + + row = dict_create_sys_tables_tuple(table, node->heap); + + ins_node_set_new_row(node->tab_def, row); + + return(DB_SUCCESS); +} + +/******************************************************************* +Builds a column definition to insert. */ +static +ulint +dict_build_col_def_step( +/*====================*/ + /* out: DB_SUCCESS */ + tab_node_t* node) /* in: table create node */ +{ + dtuple_t* row; + + row = dict_create_sys_columns_tuple(node->table, node->col_no, + node->heap); + ins_node_set_new_row(node->col_def, row); + + return(DB_SUCCESS); +} + +/********************************************************************* +Based on an index object, this function builds the entry to be inserted +in the SYS_INDEXES system table. */ +static +dtuple_t* +dict_create_sys_indexes_tuple( +/*==========================*/ + /* out: the tuple which should be inserted */ + dict_index_t* index, /* in: index */ + mem_heap_t* heap) /* in: memory heap from which the memory for + the built tuple is allocated */ +{ + dict_table_t* sys_indexes; + dict_table_t* table; + dtuple_t* entry; + dfield_t* dfield; + byte* ptr; + +#ifdef UNIV_SYNC_DEBUG + ut_ad(mutex_own(&(dict_sys->mutex))); +#endif /* UNIV_SYNC_DEBUG */ + ut_ad(index && heap); + + sys_indexes = dict_sys->sys_indexes; + + table = dict_table_get_low(index->table_name); + + entry = dtuple_create(heap, 7 + DATA_N_SYS_COLS); + + /* 0: TABLE_ID -----------------------*/ + dfield = dtuple_get_nth_field(entry, 0); + + ptr = mem_heap_alloc(heap, 8); + mach_write_to_8(ptr, table->id); + + dfield_set_data(dfield, ptr, 8); + /* 1: ID ----------------------------*/ + dfield = dtuple_get_nth_field(entry, 1); + + ptr = mem_heap_alloc(heap, 8); + mach_write_to_8(ptr, index->id); + + dfield_set_data(dfield, ptr, 8); + /* 4: NAME --------------------------*/ + dfield = dtuple_get_nth_field(entry, 2); + + dfield_set_data(dfield, index->name, ut_strlen(index->name)); + /* 5: N_FIELDS ----------------------*/ + dfield = dtuple_get_nth_field(entry, 3); + + ptr = mem_heap_alloc(heap, 4); + mach_write_to_4(ptr, index->n_fields); + + dfield_set_data(dfield, ptr, 4); + /* 6: TYPE --------------------------*/ + dfield = dtuple_get_nth_field(entry, 4); + + ptr = mem_heap_alloc(heap, 4); + mach_write_to_4(ptr, index->type); + + dfield_set_data(dfield, ptr, 4); + /* 7: SPACE --------------------------*/ + +#if DICT_SYS_INDEXES_SPACE_NO_FIELD != 7 +#error "DICT_SYS_INDEXES_SPACE_NO_FIELD != 7" +#endif + + dfield = dtuple_get_nth_field(entry, 5); + + ptr = mem_heap_alloc(heap, 4); + mach_write_to_4(ptr, index->space); + + dfield_set_data(dfield, ptr, 4); + /* 8: PAGE_NO --------------------------*/ + +#if DICT_SYS_INDEXES_PAGE_NO_FIELD != 8 +#error "DICT_SYS_INDEXES_PAGE_NO_FIELD != 8" +#endif + + dfield = dtuple_get_nth_field(entry, 6); + + ptr = mem_heap_alloc(heap, 4); + mach_write_to_4(ptr, FIL_NULL); + + dfield_set_data(dfield, ptr, 4); + /*--------------------------------*/ + + dict_table_copy_types(entry, sys_indexes); + + return(entry); +} + +/********************************************************************* +Based on an index object, this function builds the entry to be inserted +in the SYS_FIELDS system table. */ +static +dtuple_t* +dict_create_sys_fields_tuple( +/*=========================*/ + /* out: the tuple which should be inserted */ + dict_index_t* index, /* in: index */ + ulint i, /* in: field number */ + mem_heap_t* heap) /* in: memory heap from which the memory for + the built tuple is allocated */ +{ + dict_table_t* sys_fields; + dtuple_t* entry; + dict_field_t* field; + dfield_t* dfield; + byte* ptr; + ibool index_contains_column_prefix_field = FALSE; + ulint j; + + ut_ad(index && heap); + + for (j = 0; j < index->n_fields; j++) { + if (dict_index_get_nth_field(index, j)->prefix_len > 0) { + index_contains_column_prefix_field = TRUE; + } + } + + field = dict_index_get_nth_field(index, i); + + sys_fields = dict_sys->sys_fields; + + entry = dtuple_create(heap, 3 + DATA_N_SYS_COLS); + + /* 0: INDEX_ID -----------------------*/ + dfield = dtuple_get_nth_field(entry, 0); + + ptr = mem_heap_alloc(heap, 8); + mach_write_to_8(ptr, index->id); + + dfield_set_data(dfield, ptr, 8); + /* 1: POS + PREFIX LENGTH ----------------------------*/ + + dfield = dtuple_get_nth_field(entry, 1); + + ptr = mem_heap_alloc(heap, 4); + + if (index_contains_column_prefix_field) { + /* If there are column prefix fields in the index, then + we store the number of the field to the 2 HIGH bytes + and the prefix length to the 2 low bytes, */ + + mach_write_to_4(ptr, (i << 16) + field->prefix_len); + } else { + /* Else we store the number of the field to the 2 LOW bytes. + This is to keep the storage format compatible with + InnoDB versions < 4.0.14. */ + + mach_write_to_4(ptr, i); + } + + dfield_set_data(dfield, ptr, 4); + /* 4: COL_NAME -------------------------*/ + dfield = dtuple_get_nth_field(entry, 2); + + dfield_set_data(dfield, field->name, + ut_strlen(field->name)); + /*---------------------------------*/ + + dict_table_copy_types(entry, sys_fields); + + return(entry); +} + +/********************************************************************* +Creates the tuple with which the index entry is searched for writing the index +tree root page number, if such a tree is created. */ +static +dtuple_t* +dict_create_search_tuple( +/*=====================*/ + /* out: the tuple for search */ + dtuple_t* tuple, /* in: the tuple inserted in the SYS_INDEXES + table */ + mem_heap_t* heap) /* in: memory heap from which the memory for + the built tuple is allocated */ +{ + dtuple_t* search_tuple; + dfield_t* field1; + dfield_t* field2; + + ut_ad(tuple && heap); + + search_tuple = dtuple_create(heap, 2); + + field1 = dtuple_get_nth_field(tuple, 0); + field2 = dtuple_get_nth_field(search_tuple, 0); + + dfield_copy(field2, field1); + + field1 = dtuple_get_nth_field(tuple, 1); + field2 = dtuple_get_nth_field(search_tuple, 1); + + dfield_copy(field2, field1); + + ut_ad(dtuple_validate(search_tuple)); + + return(search_tuple); +} + +/******************************************************************* +Builds an index definition row to insert. */ +static +ulint +dict_build_index_def_step( +/*======================*/ + /* out: DB_SUCCESS or error code */ + que_thr_t* thr, /* in: query thread */ + ind_node_t* node) /* in: index create node */ +{ + dict_table_t* table; + dict_index_t* index; + dtuple_t* row; + trx_t* trx; + +#ifdef UNIV_SYNC_DEBUG + ut_ad(mutex_own(&(dict_sys->mutex))); +#endif /* UNIV_SYNC_DEBUG */ + + trx = thr_get_trx(thr); + + index = node->index; + + table = dict_table_get_low(index->table_name); + + if (table == NULL) { + return(DB_TABLE_NOT_FOUND); + } + + trx->table_id = table->id; + + node->table = table; + + ut_ad((UT_LIST_GET_LEN(table->indexes) > 0) + || (index->type & DICT_CLUSTERED)); + + index->id = dict_hdr_get_new_id(DICT_HDR_INDEX_ID); + + /* Inherit the space id from the table; we store all indexes of a + table in the same tablespace */ + + index->space = table->space; + node->page_no = FIL_NULL; + row = dict_create_sys_indexes_tuple(index, node->heap); + node->ind_row = row; + + ins_node_set_new_row(node->ind_def, row); + + return(DB_SUCCESS); +} + +/******************************************************************* +Builds a field definition row to insert. */ +static +ulint +dict_build_field_def_step( +/*======================*/ + /* out: DB_SUCCESS */ + ind_node_t* node) /* in: index create node */ +{ + dict_index_t* index; + dtuple_t* row; + + index = node->index; + + row = dict_create_sys_fields_tuple(index, node->field_no, node->heap); + + ins_node_set_new_row(node->field_def, row); + + return(DB_SUCCESS); +} + +/******************************************************************* +Creates an index tree for the index if it is not a member of a cluster. */ +static +ulint +dict_create_index_tree_step( +/*========================*/ + /* out: DB_SUCCESS or DB_OUT_OF_FILE_SPACE */ + ind_node_t* node) /* in: index create node */ +{ + dict_index_t* index; + dict_table_t* sys_indexes; + dict_table_t* table; + dtuple_t* search_tuple; + btr_pcur_t pcur; + mtr_t mtr; + +#ifdef UNIV_SYNC_DEBUG + ut_ad(mutex_own(&(dict_sys->mutex))); +#endif /* UNIV_SYNC_DEBUG */ + + index = node->index; + table = node->table; + + sys_indexes = dict_sys->sys_indexes; + + if (index->type & DICT_CLUSTERED + && table->type == DICT_TABLE_CLUSTER_MEMBER) { + + /* Do not create a new index tree: entries are put to the + cluster tree */ + + return(DB_SUCCESS); + } + + /* Run a mini-transaction in which the index tree is allocated for + the index and its root address is written to the index entry in + sys_indexes */ + + mtr_start(&mtr); + + search_tuple = dict_create_search_tuple(node->ind_row, node->heap); + + btr_pcur_open(UT_LIST_GET_FIRST(sys_indexes->indexes), + search_tuple, PAGE_CUR_L, BTR_MODIFY_LEAF, + &pcur, &mtr); + + btr_pcur_move_to_next_user_rec(&pcur, &mtr); + + node->page_no = btr_create(index->type, index->space, index->id, + table->comp, &mtr); + /* printf("Created a new index tree in space %lu root page %lu\n", + index->space, index->page_no); */ + + page_rec_write_index_page_no(btr_pcur_get_rec(&pcur), + DICT_SYS_INDEXES_PAGE_NO_FIELD, + node->page_no, &mtr); + btr_pcur_close(&pcur); + mtr_commit(&mtr); + + if (node->page_no == FIL_NULL) { + + return(DB_OUT_OF_FILE_SPACE); + } + + return(DB_SUCCESS); +} + +/*********************************************************************** +Drops the index tree associated with a row in SYS_INDEXES table. */ + +void +dict_drop_index_tree( +/*=================*/ + rec_t* rec, /* in: record in the clustered index of SYS_INDEXES + table */ + mtr_t* mtr) /* in: mtr having the latch on the record page */ +{ + ulint root_page_no; + ulint space; + byte* ptr; + ulint len; + +#ifdef UNIV_SYNC_DEBUG + ut_ad(mutex_own(&(dict_sys->mutex))); +#endif /* UNIV_SYNC_DEBUG */ + + ut_a(!dict_sys->sys_indexes->comp); + ptr = rec_get_nth_field_old(rec, DICT_SYS_INDEXES_PAGE_NO_FIELD, &len); + + ut_ad(len == 4); + + root_page_no = mtr_read_ulint(ptr, MLOG_4BYTES, mtr); + + if (root_page_no == FIL_NULL) { + /* The tree has already been freed */ + + return; + } + + ptr = rec_get_nth_field_old(rec, + DICT_SYS_INDEXES_SPACE_NO_FIELD, &len); + + ut_ad(len == 4); + + space = mtr_read_ulint(ptr, MLOG_4BYTES, mtr); + + if (!fil_tablespace_exists_in_mem(space)) { + /* It is a single table tablespace and the .ibd file is + missing: do nothing */ + + return; + } + + /* We free all the pages but the root page first; this operation + may span several mini-transactions */ + + btr_free_but_not_root(space, root_page_no); + + /* Then we free the root page in the same mini-transaction where + we write FIL_NULL to the appropriate field in the SYS_INDEXES + record: this mini-transaction marks the B-tree totally freed */ + + /* printf("Dropping index tree in space %lu root page %lu\n", space, + root_page_no); */ + btr_free_root(space, root_page_no, mtr); + + page_rec_write_index_page_no(rec, + DICT_SYS_INDEXES_PAGE_NO_FIELD, FIL_NULL, mtr); +} + +/*********************************************************************** +Truncates the index tree associated with a row in SYS_INDEXES table. */ + +ulint +dict_truncate_index_tree( +/*=====================*/ + /* out: new root page number, or + FIL_NULL on failure */ + dict_table_t* table, /* in: the table the index belongs to */ + rec_t* rec, /* in: record in the clustered index of + SYS_INDEXES table */ + mtr_t* mtr) /* in: mtr having the latch + on the record page. The mtr may be + committed and restarted in this call. */ +{ + ulint root_page_no; + ulint space; + ulint type; + dulint index_id; + byte* ptr; + ulint len; + ibool comp; + dict_index_t* index; + +#ifdef UNIV_SYNC_DEBUG + ut_ad(mutex_own(&(dict_sys->mutex))); +#endif /* UNIV_SYNC_DEBUG */ + + ut_a(!dict_sys->sys_indexes->comp); + ptr = rec_get_nth_field_old(rec, DICT_SYS_INDEXES_PAGE_NO_FIELD, &len); + + ut_ad(len == 4); + + root_page_no = mtr_read_ulint(ptr, MLOG_4BYTES, mtr); + + if (root_page_no == FIL_NULL) { + /* The tree has been freed. */ + + ut_print_timestamp(stderr); + fprintf(stderr, " InnoDB: Trying to TRUNCATE" + " a missing index of table %s!\n", table->name); + return(FIL_NULL); + } + + ptr = rec_get_nth_field_old(rec, + DICT_SYS_INDEXES_SPACE_NO_FIELD, &len); + + ut_ad(len == 4); + + space = mtr_read_ulint(ptr, MLOG_4BYTES, mtr); + + if (!fil_tablespace_exists_in_mem(space)) { + /* It is a single table tablespace and the .ibd file is + missing: do nothing */ + + ut_print_timestamp(stderr); + fprintf(stderr, " InnoDB: Trying to TRUNCATE" + " a missing .ibd file of table %s!\n", table->name); + return(FIL_NULL); + } + + ptr = rec_get_nth_field_old(rec, + DICT_SYS_INDEXES_TYPE_FIELD, &len); + ut_ad(len == 4); + type = mach_read_from_4(ptr); + + ptr = rec_get_nth_field_old(rec, 1, &len); + ut_ad(len == 8); + index_id = mach_read_from_8(ptr); + + /* We free all the pages but the root page first; this operation + may span several mini-transactions */ + + btr_free_but_not_root(space, root_page_no); + + /* Then we free the root page in the same mini-transaction where + we create the b-tree and write its new root page number to the + appropriate field in the SYS_INDEXES record: this mini-transaction + marks the B-tree totally truncated */ + + comp = page_is_comp(btr_page_get( + space, root_page_no, RW_X_LATCH, mtr)); + + btr_free_root(space, root_page_no, mtr); + /* We will temporarily write FIL_NULL to the PAGE_NO field + in SYS_INDEXES, so that the database will not get into an + inconsistent state in case it crashes between the mtr_commit() + below and the following mtr_commit() call. */ + page_rec_write_index_page_no(rec, DICT_SYS_INDEXES_PAGE_NO_FIELD, + FIL_NULL, mtr); + + /* We will need to commit the mini-transaction in order to avoid + deadlocks in the btr_create() call, because otherwise we would + be freeing and allocating pages in the same mini-transaction. */ + mtr_commit(mtr); + /* mtr_commit() will invalidate rec. */ + rec = NULL; + mtr_start(mtr); + + /* Find the index corresponding to this SYS_INDEXES record. */ + for (index = UT_LIST_GET_FIRST(table->indexes); + index; + index = UT_LIST_GET_NEXT(indexes, index)) { + if (!ut_dulint_cmp(index->id, index_id)) { + break; + } + } + + root_page_no = btr_create(type, space, index_id, comp, mtr); + if (index) { + index->tree->page = root_page_no; + } else { + ut_print_timestamp(stderr); + fprintf(stderr, + " InnoDB: Index %lu %lu of table %s is missing\n" + "InnoDB: from the data dictionary during TRUNCATE!\n", + ut_dulint_get_high(index_id), + ut_dulint_get_low(index_id), + table->name); + } + + return(root_page_no); +} + +/************************************************************************* +Creates a table create graph. */ + +tab_node_t* +tab_create_graph_create( +/*====================*/ + /* out, own: table create node */ + dict_table_t* table, /* in: table to create, built as a memory data + structure */ + mem_heap_t* heap) /* in: heap where created */ +{ + tab_node_t* node; + + node = mem_heap_alloc(heap, sizeof(tab_node_t)); + + node->common.type = QUE_NODE_CREATE_TABLE; + + node->table = table; + + node->state = TABLE_BUILD_TABLE_DEF; + node->heap = mem_heap_create(256); + + node->tab_def = ins_node_create(INS_DIRECT, dict_sys->sys_tables, + heap); + node->tab_def->common.parent = node; + + node->col_def = ins_node_create(INS_DIRECT, dict_sys->sys_columns, + heap); + node->col_def->common.parent = node; + + node->commit_node = commit_node_create(heap); + node->commit_node->common.parent = node; + + return(node); +} + +/************************************************************************* +Creates an index create graph. */ + +ind_node_t* +ind_create_graph_create( +/*====================*/ + /* out, own: index create node */ + dict_index_t* index, /* in: index to create, built as a memory data + structure */ + mem_heap_t* heap) /* in: heap where created */ +{ + ind_node_t* node; + + node = mem_heap_alloc(heap, sizeof(ind_node_t)); + + node->common.type = QUE_NODE_CREATE_INDEX; + + node->index = index; + + node->state = INDEX_BUILD_INDEX_DEF; + node->page_no = FIL_NULL; + node->heap = mem_heap_create(256); + + node->ind_def = ins_node_create(INS_DIRECT, + dict_sys->sys_indexes, heap); + node->ind_def->common.parent = node; + + node->field_def = ins_node_create(INS_DIRECT, + dict_sys->sys_fields, heap); + node->field_def->common.parent = node; + + node->commit_node = commit_node_create(heap); + node->commit_node->common.parent = node; + + return(node); +} + +/*************************************************************** +Creates a table. This is a high-level function used in SQL execution graphs. */ + +que_thr_t* +dict_create_table_step( +/*===================*/ + /* out: query thread to run next or NULL */ + que_thr_t* thr) /* in: query thread */ +{ + tab_node_t* node; + ulint err = DB_ERROR; + trx_t* trx; + + ut_ad(thr); +#ifdef UNIV_SYNC_DEBUG + ut_ad(mutex_own(&(dict_sys->mutex))); +#endif /* UNIV_SYNC_DEBUG */ + + trx = thr_get_trx(thr); + + node = thr->run_node; + + ut_ad(que_node_get_type(node) == QUE_NODE_CREATE_TABLE); + + if (thr->prev_node == que_node_get_parent(node)) { + node->state = TABLE_BUILD_TABLE_DEF; + } + + if (node->state == TABLE_BUILD_TABLE_DEF) { + + /* DO THE CHECKS OF THE CONSISTENCY CONSTRAINTS HERE */ + + err = dict_build_table_def_step(thr, node); + + if (err != DB_SUCCESS) { + + goto function_exit; + } + + node->state = TABLE_BUILD_COL_DEF; + node->col_no = 0; + + thr->run_node = node->tab_def; + + return(thr); + } + + if (node->state == TABLE_BUILD_COL_DEF) { + + if (node->col_no < (node->table)->n_def) { + + err = dict_build_col_def_step(node); + + if (err != DB_SUCCESS) { + + goto function_exit; + } + + node->col_no++; + + thr->run_node = node->col_def; + + return(thr); + } else { + node->state = TABLE_COMMIT_WORK; + } + } + + if (node->state == TABLE_COMMIT_WORK) { + + /* Table was correctly defined: do NOT commit the transaction + (CREATE TABLE does NOT do an implicit commit of the current + transaction) */ + + node->state = TABLE_ADD_TO_CACHE; + + /* thr->run_node = node->commit_node; + + return(thr); */ + } + + if (node->state == TABLE_ADD_TO_CACHE) { + + dict_table_add_to_cache(node->table); + + err = DB_SUCCESS; + } + +function_exit: + trx->error_state = err; + + if (err == DB_SUCCESS) { + /* Ok: do nothing */ + + } else if (err == DB_LOCK_WAIT) { + + return(NULL); + } else { + /* SQL error detected */ + + return(NULL); + } + + thr->run_node = que_node_get_parent(node); + + return(thr); +} + +/*************************************************************** +Creates an index. This is a high-level function used in SQL execution +graphs. */ + +que_thr_t* +dict_create_index_step( +/*===================*/ + /* out: query thread to run next or NULL */ + que_thr_t* thr) /* in: query thread */ +{ + ind_node_t* node; + ibool success; + ulint err = DB_ERROR; + trx_t* trx; + + ut_ad(thr); +#ifdef UNIV_SYNC_DEBUG + ut_ad(mutex_own(&(dict_sys->mutex))); +#endif /* UNIV_SYNC_DEBUG */ + + trx = thr_get_trx(thr); + + node = thr->run_node; + + ut_ad(que_node_get_type(node) == QUE_NODE_CREATE_INDEX); + + if (thr->prev_node == que_node_get_parent(node)) { + node->state = INDEX_BUILD_INDEX_DEF; + } + + if (node->state == INDEX_BUILD_INDEX_DEF) { + /* DO THE CHECKS OF THE CONSISTENCY CONSTRAINTS HERE */ + err = dict_build_index_def_step(thr, node); + + if (err != DB_SUCCESS) { + + goto function_exit; + } + + node->state = INDEX_BUILD_FIELD_DEF; + node->field_no = 0; + + thr->run_node = node->ind_def; + + return(thr); + } + + if (node->state == INDEX_BUILD_FIELD_DEF) { + + if (node->field_no < (node->index)->n_fields) { + + err = dict_build_field_def_step(node); + + if (err != DB_SUCCESS) { + + goto function_exit; + } + + node->field_no++; + + thr->run_node = node->field_def; + + return(thr); + } else { + node->state = INDEX_CREATE_INDEX_TREE; + } + } + + if (node->state == INDEX_CREATE_INDEX_TREE) { + + err = dict_create_index_tree_step(node); + + if (err != DB_SUCCESS) { + + goto function_exit; + } + + node->state = INDEX_COMMIT_WORK; + } + + if (node->state == INDEX_COMMIT_WORK) { + + /* Index was correctly defined: do NOT commit the transaction + (CREATE INDEX does NOT currently do an implicit commit of + the current transaction) */ + + node->state = INDEX_ADD_TO_CACHE; + + /* thr->run_node = node->commit_node; + + return(thr); */ + } + + if (node->state == INDEX_ADD_TO_CACHE) { + + success = dict_index_add_to_cache(node->table, node->index, + node->page_no); + + ut_a(success); + + err = DB_SUCCESS; + } + +function_exit: + trx->error_state = err; + + if (err == DB_SUCCESS) { + /* Ok: do nothing */ + + } else if (err == DB_LOCK_WAIT) { + + return(NULL); + } else { + /* SQL error detected */ + + return(NULL); + } + + thr->run_node = que_node_get_parent(node); + + return(thr); +} + +/******************************************************************** +Creates the foreign key constraints system tables inside InnoDB +at database creation or database start if they are not found or are +not of the right form. */ + +ulint +dict_create_or_check_foreign_constraint_tables(void) +/*================================================*/ + /* out: DB_SUCCESS or error code */ +{ + dict_table_t* table1; + dict_table_t* table2; + que_thr_t* thr; + que_t* graph; + ulint error; + trx_t* trx; + const char* str; + + mutex_enter(&(dict_sys->mutex)); + + table1 = dict_table_get_low("SYS_FOREIGN"); + table2 = dict_table_get_low("SYS_FOREIGN_COLS"); + + if (table1 && table2 + && UT_LIST_GET_LEN(table1->indexes) == 3 + && UT_LIST_GET_LEN(table2->indexes) == 1) { + + /* Foreign constraint system tables have already been + created, and they are ok */ + + mutex_exit(&(dict_sys->mutex)); + + return(DB_SUCCESS); + } + + mutex_exit(&(dict_sys->mutex)); + + trx = trx_allocate_for_mysql(); + + trx->op_info = "creating foreign key sys tables"; + + row_mysql_lock_data_dictionary(trx); + + if (table1) { + fprintf(stderr, + "InnoDB: dropping incompletely created SYS_FOREIGN table\n"); + row_drop_table_for_mysql("SYS_FOREIGN", trx, TRUE); + } + + if (table2) { + fprintf(stderr, + "InnoDB: dropping incompletely created SYS_FOREIGN_COLS table\n"); + row_drop_table_for_mysql("SYS_FOREIGN_COLS", trx, TRUE); + } + + fprintf(stderr, + "InnoDB: Creating foreign key constraint system tables\n"); + + /* NOTE: in dict_load_foreigns we use the fact that + there are 2 secondary indexes on SYS_FOREIGN, and they + are defined just like below */ + + /* NOTE: when designing InnoDB's foreign key support in 2001, we made + an error and made the table names and the foreign key id of type + 'CHAR' (internally, really a VARCHAR). We should have made the type + VARBINARY, like in other InnoDB system tables, to get a clean + design. */ + + str = + "PROCEDURE CREATE_FOREIGN_SYS_TABLES_PROC () IS\n" + "BEGIN\n" + "CREATE TABLE\n" + "SYS_FOREIGN(ID CHAR, FOR_NAME CHAR, REF_NAME CHAR, N_COLS INT);\n" + "CREATE UNIQUE CLUSTERED INDEX ID_IND ON SYS_FOREIGN (ID);\n" + "CREATE INDEX FOR_IND ON SYS_FOREIGN (FOR_NAME);\n" + "CREATE INDEX REF_IND ON SYS_FOREIGN (REF_NAME);\n" + "CREATE TABLE\n" + "SYS_FOREIGN_COLS(ID CHAR, POS INT, FOR_COL_NAME CHAR, REF_COL_NAME CHAR);\n" + "CREATE UNIQUE CLUSTERED INDEX ID_IND ON SYS_FOREIGN_COLS (ID, POS);\n" + "COMMIT WORK;\n" + "END;\n"; + + graph = pars_sql(str); + + ut_a(graph); + + graph->trx = trx; + trx->graph = NULL; + + graph->fork_type = QUE_FORK_MYSQL_INTERFACE; + + ut_a(thr = que_fork_start_command(graph)); + + que_run_threads(thr); + + error = trx->error_state; + + if (error != DB_SUCCESS) { + fprintf(stderr, "InnoDB: error %lu in creation\n", + (ulong) error); + + ut_a(error == DB_OUT_OF_FILE_SPACE); + + fprintf(stderr, "InnoDB: creation failed\n"); + fprintf(stderr, "InnoDB: tablespace is full\n"); + fprintf(stderr, + "InnoDB: dropping incompletely created SYS_FOREIGN tables\n"); + + row_drop_table_for_mysql("SYS_FOREIGN", trx, TRUE); + row_drop_table_for_mysql("SYS_FOREIGN_COLS", trx, TRUE); + + error = DB_MUST_GET_MORE_FILE_SPACE; + } + + que_graph_free(graph); + + trx->op_info = ""; + + row_mysql_unlock_data_dictionary(trx); + + trx_free_for_mysql(trx); + + if (error == DB_SUCCESS) { + fprintf(stderr, + "InnoDB: Foreign key constraint system tables created\n"); + } + + return(error); +} + +/************************************************************************ +Adds foreign key definitions to data dictionary tables in the database. We +look at table->foreign_list, and also generate names to constraints that were +not named by the user. A generated constraint has a name of the format +databasename/tablename_ibfk_<number>, where the numbers start from 1, and are +given locally for this table, that is, the number is not global, as in the +old format constraints < 4.0.18 it used to be. */ + +ulint +dict_create_add_foreigns_to_dictionary( +/*===================================*/ + /* out: error code or DB_SUCCESS */ + ulint start_id,/* in: if we are actually doing ALTER TABLE + ADD CONSTRAINT, we want to generate constraint + numbers which are bigger than in the table so + far; we number the constraints from + start_id + 1 up; start_id should be set to 0 if + we are creating a new table, or if the table + so far has no constraints for which the name + was generated here */ + dict_table_t* table, /* in: table */ + trx_t* trx) /* in: transaction */ +{ + dict_foreign_t* foreign; + que_thr_t* thr; + que_t* graph; + ulint number = start_id + 1; + ulint len; + ulint error; + FILE* ef = dict_foreign_err_file; + ulint i; + char* sql; + char* sqlend; + /* This procedure builds an InnoDB stored procedure which will insert + the necessary rows into SYS_FOREIGN and SYS_FOREIGN_COLS. */ + static const char str1[] = "PROCEDURE ADD_FOREIGN_DEFS_PROC () IS\n" + "BEGIN\n" + "INSERT INTO SYS_FOREIGN VALUES("; + static const char str2[] = ");\n"; + static const char str3[] = + "INSERT INTO SYS_FOREIGN_COLS VALUES("; + static const char str4[] = + "COMMIT WORK;\n" + "END;\n"; + +#ifdef UNIV_SYNC_DEBUG + ut_ad(mutex_own(&(dict_sys->mutex))); +#endif /* UNIV_SYNC_DEBUG */ + + if (NULL == dict_table_get_low("SYS_FOREIGN")) { + fprintf(stderr, + "InnoDB: table SYS_FOREIGN not found from internal data dictionary\n"); + + return(DB_ERROR); + } + + foreign = UT_LIST_GET_FIRST(table->foreign_list); +loop: + if (foreign == NULL) { + + return(DB_SUCCESS); + } + + if (foreign->id == NULL) { + /* Generate a new constraint id */ + ulint namelen = strlen(table->name); + char* id = mem_heap_alloc(foreign->heap, namelen + 20); + /* no overflow if number < 1e13 */ + sprintf(id, "%s_ibfk_%lu", table->name, (ulong) number++); + foreign->id = id; + } + + len = (sizeof str1) + (sizeof str2) + (sizeof str4) - 3 + + 9/* ' and , chars */ + 10/* 32-bit integer */ + + ut_strlenq(foreign->id, '\'') * (foreign->n_fields + 1) + + ut_strlenq(table->name, '\'') + + ut_strlenq(foreign->referenced_table_name, '\''); + + for (i = 0; i < foreign->n_fields; i++) { + len += 9/* ' and , chars */ + 10/* 32-bit integer */ + + (sizeof str3) + (sizeof str2) - 2 + + ut_strlenq(foreign->foreign_col_names[i], '\'') + + ut_strlenq(foreign->referenced_col_names[i], '\''); + } + + sql = sqlend = mem_alloc(len + 1); + + /* INSERT INTO SYS_FOREIGN VALUES(...); */ + memcpy(sqlend, str1, (sizeof str1) - 1); + sqlend += (sizeof str1) - 1; + *sqlend++ = '\''; + sqlend = ut_strcpyq(sqlend, '\'', foreign->id); + *sqlend++ = '\'', *sqlend++ = ',', *sqlend++ = '\''; + sqlend = ut_strcpyq(sqlend, '\'', table->name); + *sqlend++ = '\'', *sqlend++ = ',', *sqlend++ = '\''; + sqlend = ut_strcpyq(sqlend, '\'', foreign->referenced_table_name); + *sqlend++ = '\'', *sqlend++ = ','; + sqlend += sprintf(sqlend, "%010lu", + foreign->n_fields + (foreign->type << 24)); + memcpy(sqlend, str2, (sizeof str2) - 1); + sqlend += (sizeof str2) - 1; + + for (i = 0; i < foreign->n_fields; i++) { + /* INSERT INTO SYS_FOREIGN_COLS VALUES(...); */ + memcpy(sqlend, str3, (sizeof str3) - 1); + sqlend += (sizeof str3) - 1; + *sqlend++ = '\''; + sqlend = ut_strcpyq(sqlend, '\'', foreign->id); + *sqlend++ = '\''; *sqlend++ = ','; + sqlend += sprintf(sqlend, "%010lu", (ulong) i); + *sqlend++ = ','; *sqlend++ = '\''; + sqlend = ut_strcpyq(sqlend, '\'', + foreign->foreign_col_names[i]); + *sqlend++ = '\''; *sqlend++ = ','; *sqlend++ = '\''; + sqlend = ut_strcpyq(sqlend, '\'', + foreign->referenced_col_names[i]); + *sqlend++ = '\''; + memcpy(sqlend, str2, (sizeof str2) - 1); + sqlend += (sizeof str2) - 1; + } + + memcpy(sqlend, str4, sizeof str4); + sqlend += sizeof str4; + + ut_a(sqlend == sql + len + 1); + + graph = pars_sql(sql); + + ut_a(graph); + + mem_free(sql); + + graph->trx = trx; + trx->graph = NULL; + + graph->fork_type = QUE_FORK_MYSQL_INTERFACE; + + ut_a(thr = que_fork_start_command(graph)); + + que_run_threads(thr); + + error = trx->error_state; + + que_graph_free(graph); + + if (error == DB_DUPLICATE_KEY) { + mutex_enter(&dict_foreign_err_mutex); + rewind(ef); + ut_print_timestamp(ef); + fputs(" Error in foreign key constraint creation for table ", + ef); + ut_print_name(ef, trx, table->name); + fputs(".\nA foreign key constraint of name ", ef); + ut_print_name(ef, trx, foreign->id); + fputs("\nalready exists." + " (Note that internally InnoDB adds 'databasename/'\n" + "in front of the user-defined constraint name).\n", + ef); + fputs("Note that InnoDB's FOREIGN KEY system tables store\n" + "constraint names as case-insensitive, with the\n" + "MySQL standard latin1_swedish_ci collation. If you\n" + "create tables or databases whose names differ only in\n" + "the character case, then collisions in constraint\n" + "names can occur. Workaround: name your constraints\n" + "explicitly with unique names.\n", + ef); + + mutex_exit(&dict_foreign_err_mutex); + + return(error); + } + + if (error != DB_SUCCESS) { + fprintf(stderr, + "InnoDB: Foreign key constraint creation failed:\n" + "InnoDB: internal error number %lu\n", (ulong) error); + + mutex_enter(&dict_foreign_err_mutex); + ut_print_timestamp(ef); + fputs(" Internal error in foreign key constraint creation" + " for table ", ef); + ut_print_name(ef, trx, table->name); + fputs(".\n" + "See the MySQL .err log in the datadir for more information.\n", ef); + mutex_exit(&dict_foreign_err_mutex); + + return(error); + } + + foreign = UT_LIST_GET_NEXT(foreign_list, foreign); + + goto loop; +} diff --git a/storage/innobase/dict/dict0dict.c b/storage/innobase/dict/dict0dict.c new file mode 100644 index 00000000000..8c9724da079 --- /dev/null +++ b/storage/innobase/dict/dict0dict.c @@ -0,0 +1,4490 @@ +/********************************************************************** +Data dictionary system + +(c) 1996 Innobase Oy + +Created 1/8/1996 Heikki Tuuri +***********************************************************************/ + +#include "dict0dict.h" + +#ifdef UNIV_NONINL +#include "dict0dict.ic" +#endif + +#include "buf0buf.h" +#include "data0type.h" +#include "mach0data.h" +#include "dict0boot.h" +#include "dict0mem.h" +#include "dict0crea.h" +#include "trx0undo.h" +#include "btr0btr.h" +#include "btr0cur.h" +#include "btr0sea.h" +#include "pars0pars.h" +#include "pars0sym.h" +#include "que0que.h" +#include "rem0cmp.h" + +dict_sys_t* dict_sys = NULL; /* the dictionary system */ + +rw_lock_t dict_operation_lock; /* table create, drop, etc. reserve + this in X-mode; implicit or backround + operations purge, rollback, foreign + key checks reserve this in S-mode; we + cannot trust that MySQL protects + implicit or background operations + a table drop since MySQL does not + know of them; therefore we need this; + NOTE: a transaction which reserves + this must keep book on the mode in + trx->dict_operation_lock_mode */ + +#define DICT_HEAP_SIZE 100 /* initial memory heap size when + creating a table or index object */ +#define DICT_POOL_PER_TABLE_HASH 512 /* buffer pool max size per table + hash table fixed size in bytes */ +#define DICT_POOL_PER_COL_HASH 128 /* buffer pool max size per column + hash table fixed size in bytes */ +#define DICT_POOL_PER_VARYING 4 /* buffer pool max size per data + dictionary varying size in bytes */ + +/* Identifies generated InnoDB foreign key names */ +static char dict_ibfk[] = "_ibfk_"; + +#ifndef UNIV_HOTBACKUP +/********************************************************************** +Compares NUL-terminated UTF-8 strings case insensitively. + +NOTE: the prototype of this function is copied from ha_innodb.cc! If you change +this function, you MUST change also the prototype here! */ +extern +int +innobase_strcasecmp( +/*================*/ + /* out: 0 if a=b, <0 if a<b, >1 if a>b */ + const char* a, /* in: first string to compare */ + const char* b); /* in: second string to compare */ + +/********************************************************************** +Makes all characters in a NUL-terminated UTF-8 string lower case. + +NOTE: the prototype of this function is copied from ha_innodb.cc! If you change +this function, you MUST change also the prototype here! */ +extern +void +innobase_casedn_str( +/*================*/ + char* a); /* in/out: string to put in lower case */ +#endif /* !UNIV_HOTBACKUP */ + +/************************************************************************** +Adds a column to the data dictionary hash table. */ +static +void +dict_col_add_to_cache( +/*==================*/ + dict_table_t* table, /* in: table */ + dict_col_t* col); /* in: column */ +/************************************************************************** +Repositions a column in the data dictionary hash table when the table name +changes. */ +static +void +dict_col_reposition_in_cache( +/*=========================*/ + dict_table_t* table, /* in: table */ + dict_col_t* col, /* in: column */ + const char* new_name); /* in: new table name */ +/************************************************************************** +Removes a column from the data dictionary hash table. */ +static +void +dict_col_remove_from_cache( +/*=======================*/ + dict_table_t* table, /* in: table */ + dict_col_t* col); /* in: column */ +/************************************************************************** +Removes an index from the dictionary cache. */ +static +void +dict_index_remove_from_cache( +/*=========================*/ + dict_table_t* table, /* in: table */ + dict_index_t* index); /* in, own: index */ +/*********************************************************************** +Copies fields contained in index2 to index1. */ +static +void +dict_index_copy( +/*============*/ + dict_index_t* index1, /* in: index to copy to */ + dict_index_t* index2, /* in: index to copy from */ + ulint start, /* in: first position to copy */ + ulint end); /* in: last position to copy */ +/*********************************************************************** +Tries to find column names for the index in the column hash table and +sets the col field of the index. */ +static +ibool +dict_index_find_cols( +/*=================*/ + /* out: TRUE if success */ + dict_table_t* table, /* in: table */ + dict_index_t* index); /* in: index */ +/*********************************************************************** +Builds the internal dictionary cache representation for a clustered +index, containing also system fields not defined by the user. */ +static +dict_index_t* +dict_index_build_internal_clust( +/*============================*/ + /* out, own: the internal representation + of the clustered index */ + dict_table_t* table, /* in: table */ + dict_index_t* index); /* in: user representation of a clustered + index */ +/*********************************************************************** +Builds the internal dictionary cache representation for a non-clustered +index, containing also system fields not defined by the user. */ +static +dict_index_t* +dict_index_build_internal_non_clust( +/*================================*/ + /* out, own: the internal representation + of the non-clustered index */ + dict_table_t* table, /* in: table */ + dict_index_t* index); /* in: user representation of a non-clustered + index */ +/************************************************************************** +Removes a foreign constraint struct from the dictionary cache. */ +static +void +dict_foreign_remove_from_cache( +/*===========================*/ + dict_foreign_t* foreign); /* in, own: foreign constraint */ +/************************************************************************** +Prints a column data. */ +static +void +dict_col_print_low( +/*===============*/ + dict_col_t* col); /* in: column */ +/************************************************************************** +Prints an index data. */ +static +void +dict_index_print_low( +/*=================*/ + dict_index_t* index); /* in: index */ +/************************************************************************** +Prints a field data. */ +static +void +dict_field_print_low( +/*=================*/ + dict_field_t* field); /* in: field */ +/************************************************************************* +Frees a foreign key struct. */ +static +void +dict_foreign_free( +/*==============*/ + dict_foreign_t* foreign); /* in, own: foreign key struct */ + +/* Stream for storing detailed information about the latest foreign key +and unique key errors */ +FILE* dict_foreign_err_file = NULL; +mutex_t dict_foreign_err_mutex; /* mutex protecting the foreign + and unique error buffers */ + + +/************************************************************************ +Checks if the database name in two table names is the same. */ + +ibool +dict_tables_have_same_db( +/*=====================*/ + /* out: TRUE if same db name */ + const char* name1, /* in: table name in the form + dbname '/' tablename */ + const char* name2) /* in: table name in the form + dbname '/' tablename */ +{ + for (; *name1 == *name2; name1++, name2++) { + if (*name1 == '/') { + return(TRUE); + } + ut_a(*name1); /* the names must contain '/' */ + } + return(FALSE); +} + +/************************************************************************ +Return the end of table name where we have removed dbname and '/'. */ +static +const char* +dict_remove_db_name( +/*================*/ + /* out: table name */ + const char* name) /* in: table name in the form + dbname '/' tablename */ +{ + const char* s; + s = strchr(name, '/'); + ut_a(s); + if (s) s++; + return(s); +} + +/************************************************************************ +Get the database name length in a table name. */ + +ulint +dict_get_db_name_len( +/*=================*/ + /* out: database name length */ + const char* name) /* in: table name in the form + dbname '/' tablename */ +{ + const char* s; + s = strchr(name, '/'); + ut_a(s); + return(s - name); +} + +/************************************************************************ +Reserves the dictionary system mutex for MySQL. */ + +void +dict_mutex_enter_for_mysql(void) +/*============================*/ +{ + mutex_enter(&(dict_sys->mutex)); +} + +/************************************************************************ +Releases the dictionary system mutex for MySQL. */ + +void +dict_mutex_exit_for_mysql(void) +/*===========================*/ +{ + mutex_exit(&(dict_sys->mutex)); +} + +/************************************************************************ +Decrements the count of open MySQL handles to a table. */ + +void +dict_table_decrement_handle_count( +/*==============================*/ + dict_table_t* table) /* in: table */ +{ + mutex_enter(&(dict_sys->mutex)); + + ut_a(table->n_mysql_handles_opened > 0); + + table->n_mysql_handles_opened--; + + mutex_exit(&(dict_sys->mutex)); +} + +/************************************************************************ +Gets the nth column of a table. */ + +dict_col_t* +dict_table_get_nth_col_noninline( +/*=============================*/ + /* out: pointer to column object */ + dict_table_t* table, /* in: table */ + ulint pos) /* in: position of column */ +{ + return(dict_table_get_nth_col(table, pos)); +} + +/************************************************************************ +Gets the first index on the table (the clustered index). */ + +dict_index_t* +dict_table_get_first_index_noninline( +/*=================================*/ + /* out: index, NULL if none exists */ + dict_table_t* table) /* in: table */ +{ + return(dict_table_get_first_index(table)); +} + +/************************************************************************ +Gets the next index on the table. */ + +dict_index_t* +dict_table_get_next_index_noninline( +/*================================*/ + /* out: index, NULL if none left */ + dict_index_t* index) /* in: index */ +{ + return(dict_table_get_next_index(index)); +} + +/************************************************************************** +Returns an index object. */ + +dict_index_t* +dict_table_get_index_noninline( +/*===========================*/ + /* out: index, NULL if does not exist */ + dict_table_t* table, /* in: table */ + const char* name) /* in: index name */ +{ + return(dict_table_get_index(table, name)); +} + +/************************************************************************ +Initializes the autoinc counter. It is not an error to initialize an already +initialized counter. */ + +void +dict_table_autoinc_initialize( +/*==========================*/ + dict_table_t* table, /* in: table */ + ib_longlong value) /* in: next value to assign to a row */ +{ + mutex_enter(&(table->autoinc_mutex)); + + table->autoinc_inited = TRUE; + table->autoinc = value; + + mutex_exit(&(table->autoinc_mutex)); +} + +/************************************************************************ +Gets the next autoinc value (== autoinc counter value), 0 if not yet +initialized. If initialized, increments the counter by 1. */ + +ib_longlong +dict_table_autoinc_get( +/*===================*/ + /* out: value for a new row, or 0 */ + dict_table_t* table) /* in: table */ +{ + ib_longlong value; + + mutex_enter(&(table->autoinc_mutex)); + + if (!table->autoinc_inited) { + + value = 0; + } else { + value = table->autoinc; + table->autoinc = table->autoinc + 1; + } + + mutex_exit(&(table->autoinc_mutex)); + + return(value); +} + +/************************************************************************ +Decrements the autoinc counter value by 1. */ + +void +dict_table_autoinc_decrement( +/*=========================*/ + dict_table_t* table) /* in: table */ +{ + mutex_enter(&(table->autoinc_mutex)); + + table->autoinc = table->autoinc - 1; + + mutex_exit(&(table->autoinc_mutex)); +} + +/************************************************************************ +Reads the next autoinc value (== autoinc counter value), 0 if not yet +initialized. */ + +ib_longlong +dict_table_autoinc_read( +/*====================*/ + /* out: value for a new row, or 0 */ + dict_table_t* table) /* in: table */ +{ + ib_longlong value; + + mutex_enter(&(table->autoinc_mutex)); + + if (!table->autoinc_inited) { + + value = 0; + } else { + value = table->autoinc; + } + + mutex_exit(&(table->autoinc_mutex)); + + return(value); +} + +/************************************************************************ +Peeks the autoinc counter value, 0 if not yet initialized. Does not +increment the counter. The read not protected by any mutex! */ + +ib_longlong +dict_table_autoinc_peek( +/*====================*/ + /* out: value of the counter */ + dict_table_t* table) /* in: table */ +{ + ib_longlong value; + + if (!table->autoinc_inited) { + + value = 0; + } else { + value = table->autoinc; + } + + return(value); +} + +/************************************************************************ +Updates the autoinc counter if the value supplied is equal or bigger than the +current value. If not inited, does nothing. */ + +void +dict_table_autoinc_update( +/*======================*/ + + dict_table_t* table, /* in: table */ + ib_longlong value) /* in: value which was assigned to a row */ +{ + mutex_enter(&(table->autoinc_mutex)); + + if (table->autoinc_inited) { + if (value >= table->autoinc) { + table->autoinc = value + 1; + } + } + + mutex_exit(&(table->autoinc_mutex)); +} + +/************************************************************************ +Looks for column n in an index. */ + +ulint +dict_index_get_nth_col_pos( +/*=======================*/ + /* out: position in internal representation + of the index; if not contained, returns + ULINT_UNDEFINED */ + dict_index_t* index, /* in: index */ + ulint n) /* in: column number */ +{ + dict_field_t* field; + dict_col_t* col; + ulint pos; + ulint n_fields; + + ut_ad(index); + ut_ad(index->magic_n == DICT_INDEX_MAGIC_N); + + col = dict_table_get_nth_col(index->table, n); + + if (index->type & DICT_CLUSTERED) { + + return(col->clust_pos); + } + + n_fields = dict_index_get_n_fields(index); + + for (pos = 0; pos < n_fields; pos++) { + field = dict_index_get_nth_field(index, pos); + + if (col == field->col && field->prefix_len == 0) { + + return(pos); + } + } + + return(ULINT_UNDEFINED); +} + +/************************************************************************ +Returns TRUE if the index contains a column or a prefix of that column. */ + +ibool +dict_index_contains_col_or_prefix( +/*==============================*/ + /* out: TRUE if contains the column or its + prefix */ + dict_index_t* index, /* in: index */ + ulint n) /* in: column number */ +{ + dict_field_t* field; + dict_col_t* col; + ulint pos; + ulint n_fields; + + ut_ad(index); + ut_ad(index->magic_n == DICT_INDEX_MAGIC_N); + + if (index->type & DICT_CLUSTERED) { + + return(TRUE); + } + + col = dict_table_get_nth_col(index->table, n); + + n_fields = dict_index_get_n_fields(index); + + for (pos = 0; pos < n_fields; pos++) { + field = dict_index_get_nth_field(index, pos); + + if (col == field->col) { + + return(TRUE); + } + } + + return(FALSE); +} + +/************************************************************************ +Looks for a matching field in an index. The column has to be the same. The +column in index must be complete, or must contain a prefix longer than the +column in index2. That is, we must be able to construct the prefix in index2 +from the prefix in index. */ + +ulint +dict_index_get_nth_field_pos( +/*=========================*/ + /* out: position in internal representation + of the index; if not contained, returns + ULINT_UNDEFINED */ + dict_index_t* index, /* in: index from which to search */ + dict_index_t* index2, /* in: index */ + ulint n) /* in: field number in index2 */ +{ + dict_field_t* field; + dict_field_t* field2; + ulint n_fields; + ulint pos; + + ut_ad(index); + ut_ad(index->magic_n == DICT_INDEX_MAGIC_N); + + field2 = dict_index_get_nth_field(index2, n); + + n_fields = dict_index_get_n_fields(index); + + for (pos = 0; pos < n_fields; pos++) { + field = dict_index_get_nth_field(index, pos); + + if (field->col == field2->col + && (field->prefix_len == 0 + || (field->prefix_len >= field2->prefix_len + && field2->prefix_len != 0))) { + + return(pos); + } + } + + return(ULINT_UNDEFINED); +} + +/************************************************************************** +Returns a table object, based on table id, and memoryfixes it. */ + +dict_table_t* +dict_table_get_on_id( +/*=================*/ + /* out: table, NULL if does not exist */ + dulint table_id, /* in: table id */ + trx_t* trx) /* in: transaction handle */ +{ + dict_table_t* table; + + if (ut_dulint_cmp(table_id, DICT_FIELDS_ID) <= 0 + || trx->dict_operation_lock_mode == RW_X_LATCH) { + /* It is a system table which will always exist in the table + cache: we avoid acquiring the dictionary mutex, because + if we are doing a rollback to handle an error in TABLE + CREATE, for example, we already have the mutex! */ + +#ifdef UNIV_SYNC_DEBUG + ut_ad(mutex_own(&(dict_sys->mutex))); +#endif /* UNIV_SYNC_DEBUG */ + + return(dict_table_get_on_id_low(table_id, trx)); + } + + mutex_enter(&(dict_sys->mutex)); + + table = dict_table_get_on_id_low(table_id, trx); + + mutex_exit(&(dict_sys->mutex)); + + return(table); +} + +/************************************************************************ +Looks for column n position in the clustered index. */ + +ulint +dict_table_get_nth_col_pos( +/*=======================*/ + /* out: position in internal representation + of the clustered index */ + dict_table_t* table, /* in: table */ + ulint n) /* in: column number */ +{ + return(dict_index_get_nth_col_pos(dict_table_get_first_index(table), + n)); +} + +/************************************************************************ +Checks if a column is in the ordering columns of the clustered index of a +table. Column prefixes are treated like whole columns. */ + +ibool +dict_table_col_in_clustered_key( +/*============================*/ + /* out: TRUE if the column, or its prefix, is + in the clustered key */ + dict_table_t* table, /* in: table */ + ulint n) /* in: column number */ +{ + dict_index_t* index; + dict_field_t* field; + dict_col_t* col; + ulint pos; + ulint n_fields; + + ut_ad(table); + + col = dict_table_get_nth_col(table, n); + + index = dict_table_get_first_index(table); + + n_fields = dict_index_get_n_unique(index); + + for (pos = 0; pos < n_fields; pos++) { + field = dict_index_get_nth_field(index, pos); + + if (col == field->col) { + + return(TRUE); + } + } + + return(FALSE); +} + +/************************************************************************** +Inits the data dictionary module. */ + +void +dict_init(void) +/*===========*/ +{ + dict_sys = mem_alloc(sizeof(dict_sys_t)); + + mutex_create(&(dict_sys->mutex)); + mutex_set_level(&(dict_sys->mutex), SYNC_DICT); + + dict_sys->table_hash = hash_create(buf_pool_get_max_size() / + (DICT_POOL_PER_TABLE_HASH * + UNIV_WORD_SIZE)); + dict_sys->table_id_hash = hash_create(buf_pool_get_max_size() / + (DICT_POOL_PER_TABLE_HASH * + UNIV_WORD_SIZE)); + dict_sys->col_hash = hash_create(buf_pool_get_max_size() / + (DICT_POOL_PER_COL_HASH * + UNIV_WORD_SIZE)); + dict_sys->size = 0; + + UT_LIST_INIT(dict_sys->table_LRU); + + rw_lock_create(&dict_operation_lock); + rw_lock_set_level(&dict_operation_lock, SYNC_DICT_OPERATION); + + dict_foreign_err_file = os_file_create_tmpfile(); + ut_a(dict_foreign_err_file); + mutex_create(&dict_foreign_err_mutex); + mutex_set_level(&dict_foreign_err_mutex, SYNC_ANY_LATCH); +} + +/************************************************************************** +Returns a table object and memoryfixes it. NOTE! This is a high-level +function to be used mainly from outside the 'dict' directory. Inside this +directory dict_table_get_low is usually the appropriate function. */ + +dict_table_t* +dict_table_get( +/*===========*/ + /* out: table, NULL if + does not exist */ + const char* table_name, /* in: table name */ + trx_t* trx) /* in: transaction handle or NULL */ +{ + dict_table_t* table; + + UT_NOT_USED(trx); + + mutex_enter(&(dict_sys->mutex)); + + table = dict_table_get_low(table_name); + + mutex_exit(&(dict_sys->mutex)); + + if (table != NULL) { + if (!table->stat_initialized) { + dict_update_statistics(table); + } + } + + return(table); +} + +/************************************************************************** +Returns a table object and increments MySQL open handle count on the table. */ + +dict_table_t* +dict_table_get_and_increment_handle_count( +/*======================================*/ + /* out: table, NULL if + does not exist */ + const char* table_name, /* in: table name */ + trx_t* trx) /* in: transaction handle or NULL */ +{ + dict_table_t* table; + + UT_NOT_USED(trx); + + mutex_enter(&(dict_sys->mutex)); + + table = dict_table_get_low(table_name); + + if (table != NULL) { + + table->n_mysql_handles_opened++; + } + + mutex_exit(&(dict_sys->mutex)); + + if (table != NULL) { + if (!table->stat_initialized && !table->ibd_file_missing) { + dict_update_statistics(table); + } + } + + return(table); +} + +/************************************************************************** +Adds a table object to the dictionary cache. */ + +void +dict_table_add_to_cache( +/*====================*/ + dict_table_t* table) /* in: table */ +{ + ulint fold; + ulint id_fold; + ulint i; + + ut_ad(table); +#ifdef UNIV_SYNC_DEBUG + ut_ad(mutex_own(&(dict_sys->mutex))); +#endif /* UNIV_SYNC_DEBUG */ + ut_ad(table->n_def == table->n_cols - DATA_N_SYS_COLS); + ut_ad(table->magic_n == DICT_TABLE_MAGIC_N); + ut_ad(table->cached == FALSE); + + fold = ut_fold_string(table->name); + id_fold = ut_fold_dulint(table->id); + + table->cached = TRUE; + + /* NOTE: the system columns MUST be added in the following order + (so that they can be indexed by the numerical value of DATA_ROW_ID, + etc.) and as the last columns of the table memory object. + The clustered index will not always physically contain all + system columns. */ + + dict_mem_table_add_col(table, "DB_ROW_ID", DATA_SYS, + DATA_ROW_ID | DATA_NOT_NULL, DATA_ROW_ID_LEN, 0); +#if DATA_ROW_ID != 0 +#error "DATA_ROW_ID != 0" +#endif + dict_mem_table_add_col(table, "DB_TRX_ID", DATA_SYS, + DATA_TRX_ID | DATA_NOT_NULL, DATA_TRX_ID_LEN, 0); +#if DATA_TRX_ID != 1 +#error "DATA_TRX_ID != 1" +#endif + dict_mem_table_add_col(table, "DB_ROLL_PTR", DATA_SYS, + DATA_ROLL_PTR | DATA_NOT_NULL, DATA_ROLL_PTR_LEN, 0); +#if DATA_ROLL_PTR != 2 +#error "DATA_ROLL_PTR != 2" +#endif + dict_mem_table_add_col(table, "DB_MIX_ID", DATA_SYS, + DATA_MIX_ID | DATA_NOT_NULL, DATA_MIX_ID_LEN, 0); +#if DATA_MIX_ID != 3 +#error "DATA_MIX_ID != 3" +#endif + + /* This check reminds that if a new system column is added to + the program, it should be dealt with here */ +#if DATA_N_SYS_COLS != 4 +#error "DATA_N_SYS_COLS != 4" +#endif + + /* Look for a table with the same name: error if such exists */ + { + dict_table_t* table2; + HASH_SEARCH(name_hash, dict_sys->table_hash, fold, table2, + (ut_strcmp(table2->name, table->name) == 0)); + ut_a(table2 == NULL); + } + + /* Look for a table with the same id: error if such exists */ + { + dict_table_t* table2; + HASH_SEARCH(id_hash, dict_sys->table_id_hash, id_fold, table2, + (ut_dulint_cmp(table2->id, table->id) == 0)); + ut_a(table2 == NULL); + } + + if (table->type == DICT_TABLE_CLUSTER_MEMBER) { + + table->mix_id_len = mach_dulint_get_compressed_size( + table->mix_id); + mach_dulint_write_compressed(table->mix_id_buf, table->mix_id); + } + + /* Add the columns to the column hash table */ + for (i = 0; i < table->n_cols; i++) { + dict_col_add_to_cache(table, dict_table_get_nth_col(table, i)); + } + + /* Add table to hash table of tables */ + HASH_INSERT(dict_table_t, name_hash, dict_sys->table_hash, fold, + table); + + /* Add table to hash table of tables based on table id */ + HASH_INSERT(dict_table_t, id_hash, dict_sys->table_id_hash, id_fold, + table); + /* Add table to LRU list of tables */ + UT_LIST_ADD_FIRST(table_LRU, dict_sys->table_LRU, table); + + /* If the dictionary cache grows too big, trim the table LRU list */ + + dict_sys->size += mem_heap_get_size(table->heap); + /* dict_table_LRU_trim(); */ +} + +/************************************************************************** +Looks for an index with the given id. NOTE that we do not reserve +the dictionary mutex: this function is for emergency purposes like +printing info of a corrupt database page! */ + +dict_index_t* +dict_index_find_on_id_low( +/*======================*/ + /* out: index or NULL if not found from cache */ + dulint id) /* in: index id */ +{ + dict_table_t* table; + dict_index_t* index; + + table = UT_LIST_GET_FIRST(dict_sys->table_LRU); + + while (table) { + index = dict_table_get_first_index(table); + + while (index) { + if (0 == ut_dulint_cmp(id, index->tree->id)) { + /* Found */ + + return(index); + } + + index = dict_table_get_next_index(index); + } + + table = UT_LIST_GET_NEXT(table_LRU, table); + } + + return(NULL); +} + +/************************************************************************** +Renames a table object. */ + +ibool +dict_table_rename_in_cache( +/*=======================*/ + /* out: TRUE if success */ + dict_table_t* table, /* in: table */ + const char* new_name, /* in: new name */ + ibool rename_also_foreigns)/* in: in ALTER TABLE we want + to preserve the original table name + in constraints which reference it */ +{ + dict_foreign_t* foreign; + dict_index_t* index; + ulint fold; + ulint old_size; + char* old_name; + ibool success; + ulint i; + + ut_ad(table); +#ifdef UNIV_SYNC_DEBUG + ut_ad(mutex_own(&(dict_sys->mutex))); +#endif /* UNIV_SYNC_DEBUG */ + + old_size = mem_heap_get_size(table->heap); + + fold = ut_fold_string(new_name); + + /* Look for a table with the same name: error if such exists */ + { + dict_table_t* table2; + HASH_SEARCH(name_hash, dict_sys->table_hash, fold, table2, + (ut_strcmp(table2->name, new_name) == 0)); + if (table2) { + fprintf(stderr, +"InnoDB: Error: dictionary cache already contains a table of name %s\n", + new_name); + return(FALSE); + } + } + + /* If the table is stored in a single-table tablespace, rename the + .ibd file */ + + if (table->space != 0) { + if (table->dir_path_of_temp_table != NULL) { + fprintf(stderr, +"InnoDB: Error: trying to rename a table %s (%s) created with CREATE\n" +"InnoDB: TEMPORARY TABLE\n", table->name, table->dir_path_of_temp_table); + success = FALSE; + } else { + success = fil_rename_tablespace(table->name, + table->space, new_name); + } + + if (!success) { + + return(FALSE); + } + } + + /* Reposition the columns in the column hash table; they are hashed + according to the pair (table name, column name) */ + + for (i = 0; i < table->n_cols; i++) { + dict_col_reposition_in_cache(table, + dict_table_get_nth_col(table, i), new_name); + } + + /* Remove table from the hash tables of tables */ + HASH_DELETE(dict_table_t, name_hash, dict_sys->table_hash, + ut_fold_string(table->name), table); + old_name = mem_heap_strdup(table->heap, table->name); + table->name = mem_heap_strdup(table->heap, new_name); + + /* Add table to hash table of tables */ + HASH_INSERT(dict_table_t, name_hash, dict_sys->table_hash, fold, + table); + dict_sys->size += (mem_heap_get_size(table->heap) - old_size); + + /* Update the table_name field in indexes */ + index = dict_table_get_first_index(table); + + while (index != NULL) { + index->table_name = table->name; + + index = dict_table_get_next_index(index); + } + + if (!rename_also_foreigns) { + /* In ALTER TABLE we think of the rename table operation + in the direction table -> temporary table (#sql...) + as dropping the table with the old name and creating + a new with the new name. Thus we kind of drop the + constraints from the dictionary cache here. The foreign key + constraints will be inherited to the new table from the + system tables through a call of dict_load_foreigns. */ + + /* Remove the foreign constraints from the cache */ + foreign = UT_LIST_GET_LAST(table->foreign_list); + + while (foreign != NULL) { + dict_foreign_remove_from_cache(foreign); + foreign = UT_LIST_GET_LAST(table->foreign_list); + } + + /* Reset table field in referencing constraints */ + + foreign = UT_LIST_GET_FIRST(table->referenced_list); + + while (foreign != NULL) { + foreign->referenced_table = NULL; + foreign->referenced_index = NULL; + + foreign = UT_LIST_GET_NEXT(referenced_list, foreign); + } + + /* Make the list of referencing constraints empty */ + + UT_LIST_INIT(table->referenced_list); + + return(TRUE); + } + + /* Update the table name fields in foreign constraints, and update also + the constraint id of new format >= 4.0.18 constraints. Note that at + this point we have already changed table->name to the new name. */ + + foreign = UT_LIST_GET_FIRST(table->foreign_list); + + while (foreign != NULL) { + if (ut_strlen(foreign->foreign_table_name) < + ut_strlen(table->name)) { + /* Allocate a longer name buffer; + TODO: store buf len to save memory */ + + foreign->foreign_table_name = mem_heap_alloc( + foreign->heap, + ut_strlen(table->name) + 1); + } + + strcpy(foreign->foreign_table_name, table->name); + + if (strchr(foreign->id, '/')) { + ulint db_len; + char* old_id; + + /* This is a >= 4.0.18 format id */ + + old_id = mem_strdup(foreign->id); + + if (ut_strlen(foreign->id) > ut_strlen(old_name) + + ((sizeof dict_ibfk) - 1) + && 0 == ut_memcmp(foreign->id, old_name, + ut_strlen(old_name)) + && 0 == ut_memcmp( + foreign->id + ut_strlen(old_name), + dict_ibfk, (sizeof dict_ibfk) - 1)) { + + /* This is a generated >= 4.0.18 format id */ + + if (ut_strlen(table->name) > ut_strlen(old_name)) { + foreign->id = mem_heap_alloc( + foreign->heap, + ut_strlen(table->name) + + ut_strlen(old_id) + 1); + } + + /* Replace the prefix 'databasename/tablename' + with the new names */ + strcpy(foreign->id, table->name); + strcat(foreign->id, + old_id + ut_strlen(old_name)); + } else { + /* This is a >= 4.0.18 format id where the user + gave the id name */ + db_len = dict_get_db_name_len(table->name) + 1; + + if (dict_get_db_name_len(table->name) + > dict_get_db_name_len(foreign->id)) { + + foreign->id = mem_heap_alloc( + foreign->heap, + db_len + ut_strlen(old_id) + 1); + } + + /* Replace the database prefix in id with the + one from table->name */ + + ut_memcpy(foreign->id, table->name, db_len); + + strcpy(foreign->id + db_len, + dict_remove_db_name(old_id)); + } + + mem_free(old_id); + } + + foreign = UT_LIST_GET_NEXT(foreign_list, foreign); + } + + foreign = UT_LIST_GET_FIRST(table->referenced_list); + + while (foreign != NULL) { + if (ut_strlen(foreign->referenced_table_name) < + ut_strlen(table->name)) { + /* Allocate a longer name buffer; + TODO: store buf len to save memory */ + + foreign->referenced_table_name = mem_heap_alloc( + foreign->heap, + ut_strlen(table->name) + 1); + } + + strcpy(foreign->referenced_table_name, table->name); + + foreign = UT_LIST_GET_NEXT(referenced_list, foreign); + } + + return(TRUE); +} + +/************************************************************************** +Change the id of a table object in the dictionary cache. This is used in +DISCARD TABLESPACE. */ + +void +dict_table_change_id_in_cache( +/*==========================*/ + dict_table_t* table, /* in: table object already in cache */ + dulint new_id) /* in: new id to set */ +{ + ut_ad(table); +#ifdef UNIV_SYNC_DEBUG + ut_ad(mutex_own(&(dict_sys->mutex))); +#endif /* UNIV_SYNC_DEBUG */ + ut_ad(table->magic_n == DICT_TABLE_MAGIC_N); + + /* Remove the table from the hash table of id's */ + + HASH_DELETE(dict_table_t, id_hash, dict_sys->table_id_hash, + ut_fold_dulint(table->id), table); + table->id = new_id; + + /* Add the table back to the hash table */ + HASH_INSERT(dict_table_t, id_hash, dict_sys->table_id_hash, + ut_fold_dulint(table->id), table); +} + +/************************************************************************** +Removes a table object from the dictionary cache. */ + +void +dict_table_remove_from_cache( +/*=========================*/ + dict_table_t* table) /* in, own: table */ +{ + dict_foreign_t* foreign; + dict_index_t* index; + ulint size; + ulint i; + + ut_ad(table); +#ifdef UNIV_SYNC_DEBUG + ut_ad(mutex_own(&(dict_sys->mutex))); +#endif /* UNIV_SYNC_DEBUG */ + ut_ad(table->magic_n == DICT_TABLE_MAGIC_N); + +#if 0 + fputs("Removing table ", stderr); + ut_print_name(stderr, table->name, ULINT_UNDEFINED); + fputs(" from dictionary cache\n", stderr); +#endif + + /* Remove the foreign constraints from the cache */ + foreign = UT_LIST_GET_LAST(table->foreign_list); + + while (foreign != NULL) { + dict_foreign_remove_from_cache(foreign); + foreign = UT_LIST_GET_LAST(table->foreign_list); + } + + /* Reset table field in referencing constraints */ + + foreign = UT_LIST_GET_FIRST(table->referenced_list); + + while (foreign != NULL) { + foreign->referenced_table = NULL; + foreign->referenced_index = NULL; + + foreign = UT_LIST_GET_NEXT(referenced_list, foreign); + } + + /* Remove the indexes from the cache */ + index = UT_LIST_GET_LAST(table->indexes); + + while (index != NULL) { + dict_index_remove_from_cache(table, index); + index = UT_LIST_GET_LAST(table->indexes); + } + + /* Remove the columns of the table from the cache */ + for (i = 0; i < table->n_cols; i++) { + dict_col_remove_from_cache(table, + dict_table_get_nth_col(table, i)); + } + + /* Remove table from the hash tables of tables */ + HASH_DELETE(dict_table_t, name_hash, dict_sys->table_hash, + ut_fold_string(table->name), table); + HASH_DELETE(dict_table_t, id_hash, dict_sys->table_id_hash, + ut_fold_dulint(table->id), table); + + /* Remove table from LRU list of tables */ + UT_LIST_REMOVE(table_LRU, dict_sys->table_LRU, table); + + mutex_free(&(table->autoinc_mutex)); + + size = mem_heap_get_size(table->heap); + + ut_ad(dict_sys->size >= size); + + dict_sys->size -= size; + + mem_heap_free(table->heap); +} + +/************************************************************************** +Frees tables from the end of table_LRU if the dictionary cache occupies +too much space. Currently not used! */ + +void +dict_table_LRU_trim(void) +/*=====================*/ +{ + dict_table_t* table; + dict_table_t* prev_table; + + ut_error; + +#ifdef UNIV_SYNC_DEBUG + ut_ad(mutex_own(&(dict_sys->mutex))); +#endif /* UNIV_SYNC_DEBUG */ + + table = UT_LIST_GET_LAST(dict_sys->table_LRU); + + while (table && (dict_sys->size > + buf_pool_get_max_size() / DICT_POOL_PER_VARYING)) { + + prev_table = UT_LIST_GET_PREV(table_LRU, table); + + if (table->mem_fix == 0) { + dict_table_remove_from_cache(table); + } + + table = prev_table; + } +} + +/************************************************************************** +Adds a column to the data dictionary hash table. */ +static +void +dict_col_add_to_cache( +/*==================*/ + dict_table_t* table, /* in: table */ + dict_col_t* col) /* in: column */ +{ + ulint fold; + + ut_ad(table && col); +#ifdef UNIV_SYNC_DEBUG + ut_ad(mutex_own(&(dict_sys->mutex))); +#endif /* UNIV_SYNC_DEBUG */ + ut_ad(table->magic_n == DICT_TABLE_MAGIC_N); + + fold = ut_fold_ulint_pair(ut_fold_string(table->name), + ut_fold_string(col->name)); + + /* Look for a column with same table name and column name: error */ + { + dict_col_t* col2; + HASH_SEARCH(hash, dict_sys->col_hash, fold, col2, + (ut_strcmp(col->name, col2->name) == 0) + && (ut_strcmp((col2->table)->name, table->name) + == 0)); + ut_a(col2 == NULL); + } + + HASH_INSERT(dict_col_t, hash, dict_sys->col_hash, fold, col); +} + +/************************************************************************** +Removes a column from the data dictionary hash table. */ +static +void +dict_col_remove_from_cache( +/*=======================*/ + dict_table_t* table, /* in: table */ + dict_col_t* col) /* in: column */ +{ + ulint fold; + + ut_ad(table && col); +#ifdef UNIV_SYNC_DEBUG + ut_ad(mutex_own(&(dict_sys->mutex))); +#endif /* UNIV_SYNC_DEBUG */ + ut_ad(table->magic_n == DICT_TABLE_MAGIC_N); + + fold = ut_fold_ulint_pair(ut_fold_string(table->name), + ut_fold_string(col->name)); + + HASH_DELETE(dict_col_t, hash, dict_sys->col_hash, fold, col); +} + +/************************************************************************** +Repositions a column in the data dictionary hash table when the table name +changes. */ +static +void +dict_col_reposition_in_cache( +/*=========================*/ + dict_table_t* table, /* in: table */ + dict_col_t* col, /* in: column */ + const char* new_name) /* in: new table name */ +{ + ulint fold; + + ut_ad(table && col); +#ifdef UNIV_SYNC_DEBUG + ut_ad(mutex_own(&(dict_sys->mutex))); +#endif /* UNIV_SYNC_DEBUG */ + ut_ad(table->magic_n == DICT_TABLE_MAGIC_N); + + fold = ut_fold_ulint_pair(ut_fold_string(table->name), + ut_fold_string(col->name)); + + HASH_DELETE(dict_col_t, hash, dict_sys->col_hash, fold, col); + + fold = ut_fold_ulint_pair(ut_fold_string(new_name), + ut_fold_string(col->name)); + + HASH_INSERT(dict_col_t, hash, dict_sys->col_hash, fold, col); +} + +/************************************************************************** +Adds an index to the dictionary cache. */ + +ibool +dict_index_add_to_cache( +/*====================*/ + /* out: TRUE if success */ + dict_table_t* table, /* in: table on which the index is */ + dict_index_t* index, /* in, own: index; NOTE! The index memory + object is freed in this function! */ + ulint page_no)/* in: root page number of the index */ +{ + dict_index_t* new_index; + dict_tree_t* tree; + dict_table_t* cluster; + dict_field_t* field; + ulint n_ord; + ibool success; + ulint i; + + ut_ad(index); +#ifdef UNIV_SYNC_DEBUG + ut_ad(mutex_own(&(dict_sys->mutex))); +#endif /* UNIV_SYNC_DEBUG */ + ut_ad(index->n_def == index->n_fields); + ut_ad(index->magic_n == DICT_INDEX_MAGIC_N); + + ut_ad(mem_heap_validate(index->heap)); + + { + dict_index_t* index2; + index2 = UT_LIST_GET_FIRST(table->indexes); + + while (index2 != NULL) { + ut_ad(ut_strcmp(index->name, index2->name) != 0); + + index2 = UT_LIST_GET_NEXT(indexes, index2); + } + + ut_a(UT_LIST_GET_LEN(table->indexes) == 0 + || (index->type & DICT_CLUSTERED) == 0); + } + + success = dict_index_find_cols(table, index); + + if (!success) { + dict_mem_index_free(index); + + return(FALSE); + } + + /* Build the cache internal representation of the index, + containing also the added system fields */ + + if (index->type & DICT_CLUSTERED) { + new_index = dict_index_build_internal_clust(table, index); + } else { + new_index = dict_index_build_internal_non_clust(table, index); + } + + new_index->search_info = btr_search_info_create(new_index->heap); + + /* Set the n_fields value in new_index to the actual defined + number of fields in the cache internal representation */ + + new_index->n_fields = new_index->n_def; + + /* Add the new index as the last index for the table */ + + UT_LIST_ADD_LAST(indexes, table->indexes, new_index); + new_index->table = table; + new_index->table_name = table->name; + + /* Increment the ord_part counts in columns which are ordering */ + + if (index->type & DICT_UNIVERSAL) { + n_ord = new_index->n_fields; + } else { + n_ord = dict_index_get_n_unique(new_index); + } + + for (i = 0; i < n_ord; i++) { + + field = dict_index_get_nth_field(new_index, i); + + dict_field_get_col(field)->ord_part++; + } + + if (table->type == DICT_TABLE_CLUSTER_MEMBER) { + /* The index tree is found from the cluster object */ + + cluster = dict_table_get_low(table->cluster_name); + + tree = dict_index_get_tree( + UT_LIST_GET_FIRST(cluster->indexes)); + new_index->tree = tree; + } else { + /* Create an index tree memory object for the index */ + tree = dict_tree_create(new_index, page_no); + ut_ad(tree); + + new_index->tree = tree; + } + + if (!(new_index->type & DICT_UNIVERSAL)) { + + new_index->stat_n_diff_key_vals = + mem_heap_alloc(new_index->heap, + (1 + dict_index_get_n_unique(new_index)) + * sizeof(ib_longlong)); + /* Give some sensible values to stat_n_... in case we do + not calculate statistics quickly enough */ + + for (i = 0; i <= dict_index_get_n_unique(new_index); i++) { + + new_index->stat_n_diff_key_vals[i] = 100; + } + } + + /* Add the index to the list of indexes stored in the tree */ + UT_LIST_ADD_LAST(tree_indexes, tree->tree_indexes, new_index); + + /* If the dictionary cache grows too big, trim the table LRU list */ + + dict_sys->size += mem_heap_get_size(new_index->heap); + /* dict_table_LRU_trim(); */ + + dict_mem_index_free(index); + + return(TRUE); +} + +/************************************************************************** +Removes an index from the dictionary cache. */ +static +void +dict_index_remove_from_cache( +/*=========================*/ + dict_table_t* table, /* in: table */ + dict_index_t* index) /* in, own: index */ +{ + dict_field_t* field; + ulint size; + ulint i; + + ut_ad(table && index); + ut_ad(table->magic_n == DICT_TABLE_MAGIC_N); + ut_ad(index->magic_n == DICT_INDEX_MAGIC_N); +#ifdef UNIV_SYNC_DEBUG + ut_ad(mutex_own(&(dict_sys->mutex))); +#endif /* UNIV_SYNC_DEBUG */ + + ut_ad(UT_LIST_GET_LEN((index->tree)->tree_indexes) == 1); + dict_tree_free(index->tree); + + /* Decrement the ord_part counts in columns which are ordering */ + for (i = 0; i < dict_index_get_n_unique(index); i++) { + + field = dict_index_get_nth_field(index, i); + + ut_ad(dict_field_get_col(field)->ord_part > 0); + (dict_field_get_col(field)->ord_part)--; + } + + /* Remove the index from the list of indexes of the table */ + UT_LIST_REMOVE(indexes, table->indexes, index); + + size = mem_heap_get_size(index->heap); + + ut_ad(dict_sys->size >= size); + + dict_sys->size -= size; + + mem_heap_free(index->heap); +} + +/*********************************************************************** +Tries to find column names for the index in the column hash table and +sets the col field of the index. */ +static +ibool +dict_index_find_cols( +/*=================*/ + /* out: TRUE if success */ + dict_table_t* table, /* in: table */ + dict_index_t* index) /* in: index */ +{ + dict_col_t* col; + dict_field_t* field; + ulint fold; + ulint i; + + ut_ad(table && index); + ut_ad(table->magic_n == DICT_TABLE_MAGIC_N); +#ifdef UNIV_SYNC_DEBUG + ut_ad(mutex_own(&(dict_sys->mutex))); +#endif /* UNIV_SYNC_DEBUG */ + + for (i = 0; i < index->n_fields; i++) { + field = dict_index_get_nth_field(index, i); + + fold = ut_fold_ulint_pair(ut_fold_string(table->name), + ut_fold_string(field->name)); + + HASH_SEARCH(hash, dict_sys->col_hash, fold, col, + (ut_strcmp(col->name, field->name) == 0) + && (ut_strcmp((col->table)->name, table->name) + == 0)); + if (col == NULL) { + + return(FALSE); + } else { + field->col = col; + } + } + + return(TRUE); +} + +/*********************************************************************** +Adds a column to index. */ + +void +dict_index_add_col( +/*===============*/ + dict_index_t* index, /* in: index */ + dict_col_t* col, /* in: column */ + ulint order, /* in: order criterion */ + ulint prefix_len) /* in: column prefix length */ +{ + dict_field_t* field; + + dict_mem_index_add_field(index, col->name, order, prefix_len); + + field = dict_index_get_nth_field(index, index->n_def - 1); + + field->col = col; + field->fixed_len = dtype_get_fixed_size(&col->type); + + if (prefix_len && field->fixed_len > prefix_len) { + field->fixed_len = prefix_len; + } + + /* Long fixed-length fields that need external storage are treated as + variable-length fields, so that the extern flag can be embedded in + the length word. */ + + if (field->fixed_len > DICT_MAX_COL_PREFIX_LEN) { + field->fixed_len = 0; + } + + if (!(dtype_get_prtype(&col->type) & DATA_NOT_NULL)) { + index->n_nullable++; + } + + if (index->n_def > 1) { + const dict_field_t* field2 = + dict_index_get_nth_field(index, index->n_def - 2); + field->fixed_offs = (!field2->fixed_len || + field2->fixed_offs == ULINT_UNDEFINED) + ? ULINT_UNDEFINED + : field2->fixed_len + field2->fixed_offs; + } else { + field->fixed_offs = 0; + } +} + +/*********************************************************************** +Copies fields contained in index2 to index1. */ +static +void +dict_index_copy( +/*============*/ + dict_index_t* index1, /* in: index to copy to */ + dict_index_t* index2, /* in: index to copy from */ + ulint start, /* in: first position to copy */ + ulint end) /* in: last position to copy */ +{ + dict_field_t* field; + ulint i; + + /* Copy fields contained in index2 */ + + for (i = start; i < end; i++) { + + field = dict_index_get_nth_field(index2, i); + dict_index_add_col(index1, field->col, field->order, + field->prefix_len); + } +} + +/*********************************************************************** +Copies types of fields contained in index to tuple. */ + +void +dict_index_copy_types( +/*==================*/ + dtuple_t* tuple, /* in: data tuple */ + dict_index_t* index, /* in: index */ + ulint n_fields) /* in: number of field types to copy */ +{ + dtype_t* dfield_type; + dtype_t* type; + ulint i; + + if (index->type & DICT_UNIVERSAL) { + dtuple_set_types_binary(tuple, n_fields); + + return; + } + + for (i = 0; i < n_fields; i++) { + dfield_type = dfield_get_type(dtuple_get_nth_field(tuple, i)); + type = dict_col_get_type(dict_field_get_col( + dict_index_get_nth_field(index, i))); + *dfield_type = *type; + } +} + +/*********************************************************************** +Copies types of columns contained in table to tuple. */ + +void +dict_table_copy_types( +/*==================*/ + dtuple_t* tuple, /* in: data tuple */ + dict_table_t* table) /* in: index */ +{ + dtype_t* dfield_type; + dtype_t* type; + ulint i; + + ut_ad(!(table->type & DICT_UNIVERSAL)); + + for (i = 0; i < dtuple_get_n_fields(tuple); i++) { + + dfield_type = dfield_get_type(dtuple_get_nth_field(tuple, i)); + type = dict_col_get_type(dict_table_get_nth_col(table, i)); + + *dfield_type = *type; + } +} + +/*********************************************************************** +Builds the internal dictionary cache representation for a clustered +index, containing also system fields not defined by the user. */ +static +dict_index_t* +dict_index_build_internal_clust( +/*============================*/ + /* out, own: the internal representation + of the clustered index */ + dict_table_t* table, /* in: table */ + dict_index_t* index) /* in: user representation of a clustered + index */ +{ + dict_index_t* new_index; + dict_field_t* field; + dict_col_t* col; + ulint fixed_size; + ulint trx_id_pos; + ulint i; + + ut_ad(table && index); + ut_ad(index->type & DICT_CLUSTERED); +#ifdef UNIV_SYNC_DEBUG + ut_ad(mutex_own(&(dict_sys->mutex))); +#endif /* UNIV_SYNC_DEBUG */ + ut_ad(table->magic_n == DICT_TABLE_MAGIC_N); + + /* Create a new index object with certainly enough fields */ + new_index = dict_mem_index_create(table->name, + index->name, + table->space, + index->type, + index->n_fields + table->n_cols); + + /* Copy other relevant data from the old index struct to the new + struct: it inherits the values */ + + new_index->n_user_defined_cols = index->n_fields; + + new_index->id = index->id; + + if (table->type != DICT_TABLE_ORDINARY) { + /* The index is mixed: copy common key prefix fields */ + + dict_index_copy(new_index, index, 0, table->mix_len); + + /* Add the mix id column */ + dict_index_add_col(new_index, + dict_table_get_sys_col(table, DATA_MIX_ID), 0, 0); + + /* Copy the rest of fields */ + dict_index_copy(new_index, index, table->mix_len, + index->n_fields); + } else { + /* Copy the fields of index */ + dict_index_copy(new_index, index, 0, index->n_fields); + } + + if (index->type & DICT_UNIVERSAL) { + /* No fixed number of fields determines an entry uniquely */ + + new_index->n_uniq = ULINT_MAX; + + } else if (index->type & DICT_UNIQUE) { + /* Only the fields defined so far are needed to identify + the index entry uniquely */ + + new_index->n_uniq = new_index->n_def; + } else { + /* Also the row id is needed to identify the entry */ + new_index->n_uniq = 1 + new_index->n_def; + } + + new_index->trx_id_offset = 0; + + if (!(index->type & DICT_IBUF)) { + /* Add system columns, trx id first */ + + trx_id_pos = new_index->n_def; + + ut_ad(DATA_ROW_ID == 0); + ut_ad(DATA_TRX_ID == 1); + ut_ad(DATA_ROLL_PTR == 2); + + if (!(index->type & DICT_UNIQUE)) { + dict_index_add_col(new_index, + dict_table_get_sys_col(table, DATA_ROW_ID), 0, 0); + trx_id_pos++; + } + + dict_index_add_col(new_index, + dict_table_get_sys_col(table, DATA_TRX_ID), 0, 0); + + dict_index_add_col(new_index, + dict_table_get_sys_col(table, DATA_ROLL_PTR), 0, 0); + + for (i = 0; i < trx_id_pos; i++) { + + fixed_size = dtype_get_fixed_size( + dict_index_get_nth_type(new_index, i)); + + if (fixed_size == 0) { + new_index->trx_id_offset = 0; + + break; + } + + if (dict_index_get_nth_field(new_index, i)->prefix_len + > 0) { + new_index->trx_id_offset = 0; + + break; + } + + new_index->trx_id_offset += fixed_size; + } + + } + + /* Set auxiliary variables in table columns as undefined */ + for (i = 0; i < table->n_cols; i++) { + + col = dict_table_get_nth_col(table, i); + col->aux = ULINT_UNDEFINED; + } + + /* Mark with 0 the table columns already contained in new_index */ + for (i = 0; i < new_index->n_def; i++) { + + field = dict_index_get_nth_field(new_index, i); + + /* If there is only a prefix of the column in the index + field, do not mark the column as contained in the index */ + + if (field->prefix_len == 0) { + + field->col->aux = 0; + } + } + + /* Add to new_index non-system columns of table not yet included + there */ + for (i = 0; i < table->n_cols - DATA_N_SYS_COLS; i++) { + + col = dict_table_get_nth_col(table, i); + ut_ad(col->type.mtype != DATA_SYS); + + if (col->aux == ULINT_UNDEFINED) { + dict_index_add_col(new_index, col, 0, 0); + } + } + + ut_ad((index->type & DICT_IBUF) + || (UT_LIST_GET_LEN(table->indexes) == 0)); + + /* Store to the column structs the position of the table columns + in the clustered index */ + + for (i = 0; i < new_index->n_def; i++) { + field = dict_index_get_nth_field(new_index, i); + + if (field->prefix_len == 0) { + + field->col->clust_pos = i; + } + } + + new_index->cached = TRUE; + + return(new_index); +} + +/*********************************************************************** +Builds the internal dictionary cache representation for a non-clustered +index, containing also system fields not defined by the user. */ +static +dict_index_t* +dict_index_build_internal_non_clust( +/*================================*/ + /* out, own: the internal representation + of the non-clustered index */ + dict_table_t* table, /* in: table */ + dict_index_t* index) /* in: user representation of a non-clustered + index */ +{ + dict_field_t* field; + dict_index_t* new_index; + dict_index_t* clust_index; + ulint i; + + ut_ad(table && index); + ut_ad(0 == (index->type & DICT_CLUSTERED)); +#ifdef UNIV_SYNC_DEBUG + ut_ad(mutex_own(&(dict_sys->mutex))); +#endif /* UNIV_SYNC_DEBUG */ + ut_ad(table->magic_n == DICT_TABLE_MAGIC_N); + + /* The clustered index should be the first in the list of indexes */ + clust_index = UT_LIST_GET_FIRST(table->indexes); + + ut_ad(clust_index); + ut_ad(clust_index->type & DICT_CLUSTERED); + ut_ad(!(clust_index->type & DICT_UNIVERSAL)); + + /* Create a new index */ + new_index = dict_mem_index_create(table->name, + index->name, + index->space, + index->type, + index->n_fields + + 1 + clust_index->n_uniq); + + /* Copy other relevant data from the old index + struct to the new struct: it inherits the values */ + + new_index->n_user_defined_cols = index->n_fields; + + new_index->id = index->id; + + /* Copy fields from index to new_index */ + dict_index_copy(new_index, index, 0, index->n_fields); + + /* Set the auxiliary variables in the clust_index unique columns + as undefined */ + for (i = 0; i < clust_index->n_uniq; i++) { + + field = dict_index_get_nth_field(clust_index, i); + field->col->aux = ULINT_UNDEFINED; + } + + /* Mark with 0 table columns already contained in new_index */ + for (i = 0; i < new_index->n_def; i++) { + + field = dict_index_get_nth_field(new_index, i); + + /* If there is only a prefix of the column in the index + field, do not mark the column as contained in the index */ + + if (field->prefix_len == 0) { + + field->col->aux = 0; + } + } + + /* Add to new_index the columns necessary to determine the clustered + index entry uniquely */ + + for (i = 0; i < clust_index->n_uniq; i++) { + + field = dict_index_get_nth_field(clust_index, i); + + if (field->col->aux == ULINT_UNDEFINED) { + dict_index_add_col(new_index, field->col, 0, + field->prefix_len); + } + } + + if ((index->type) & DICT_UNIQUE) { + new_index->n_uniq = index->n_fields; + } else { + new_index->n_uniq = new_index->n_def; + } + + /* Set the n_fields value in new_index to the actual defined + number of fields */ + + new_index->n_fields = new_index->n_def; + + new_index->cached = TRUE; + + return(new_index); +} + +/*====================== FOREIGN KEY PROCESSING ========================*/ + +/************************************************************************* +Checks if a table is referenced by foreign keys. */ + +ibool +dict_table_referenced_by_foreign_key( +/*=================================*/ + /* out: TRUE if table is referenced by a + foreign key */ + dict_table_t* table) /* in: InnoDB table */ +{ + if (UT_LIST_GET_LEN(table->referenced_list) > 0) { + + return(TRUE); + } + + return(FALSE); +} + +/************************************************************************* +Frees a foreign key struct. */ +static +void +dict_foreign_free( +/*==============*/ + dict_foreign_t* foreign) /* in, own: foreign key struct */ +{ + mem_heap_free(foreign->heap); +} + +/************************************************************************** +Removes a foreign constraint struct from the dictionary cache. */ +static +void +dict_foreign_remove_from_cache( +/*===========================*/ + dict_foreign_t* foreign) /* in, own: foreign constraint */ +{ +#ifdef UNIV_SYNC_DEBUG + ut_ad(mutex_own(&(dict_sys->mutex))); +#endif /* UNIV_SYNC_DEBUG */ + ut_a(foreign); + + if (foreign->referenced_table) { + UT_LIST_REMOVE(referenced_list, + foreign->referenced_table->referenced_list, foreign); + } + + if (foreign->foreign_table) { + UT_LIST_REMOVE(foreign_list, + foreign->foreign_table->foreign_list, foreign); + } + + dict_foreign_free(foreign); +} + +/************************************************************************** +Looks for the foreign constraint from the foreign and referenced lists +of a table. */ +static +dict_foreign_t* +dict_foreign_find( +/*==============*/ + /* out: foreign constraint */ + dict_table_t* table, /* in: table object */ + const char* id) /* in: foreign constraint id */ +{ + dict_foreign_t* foreign; + +#ifdef UNIV_SYNC_DEBUG + ut_ad(mutex_own(&(dict_sys->mutex))); +#endif /* UNIV_SYNC_DEBUG */ + + foreign = UT_LIST_GET_FIRST(table->foreign_list); + + while (foreign) { + if (ut_strcmp(id, foreign->id) == 0) { + + return(foreign); + } + + foreign = UT_LIST_GET_NEXT(foreign_list, foreign); + } + + foreign = UT_LIST_GET_FIRST(table->referenced_list); + + while (foreign) { + if (ut_strcmp(id, foreign->id) == 0) { + + return(foreign); + } + + foreign = UT_LIST_GET_NEXT(referenced_list, foreign); + } + + return(NULL); +} + +/************************************************************************* +Tries to find an index whose first fields are the columns in the array, +in the same order. */ +static +dict_index_t* +dict_foreign_find_index( +/*====================*/ + /* out: matching index, NULL if not found */ + dict_table_t* table, /* in: table */ + const char** columns,/* in: array of column names */ + ulint n_cols, /* in: number of columns */ + dict_index_t* types_idx)/* in: NULL or an index to whose types the + column types must match */ +{ +#ifndef UNIV_HOTBACKUP + dict_index_t* index; + const char* col_name; + ulint i; + + index = dict_table_get_first_index(table); + + while (index != NULL) { + if (dict_index_get_n_fields(index) >= n_cols) { + + for (i = 0; i < n_cols; i++) { + col_name = dict_index_get_nth_field(index, i) + ->col->name; + if (dict_index_get_nth_field(index, i) + ->prefix_len != 0) { + /* We do not accept column prefix + indexes here */ + + break; + } + + if (0 != innobase_strcasecmp(columns[i], + col_name)) { + break; + } + + if (types_idx && !cmp_types_are_equal( + dict_index_get_nth_type(index, i), + dict_index_get_nth_type(types_idx, i))) { + + break; + } + } + + if (i == n_cols) { + /* We found a matching index */ + + return(index); + } + } + + index = dict_table_get_next_index(index); + } + + return(NULL); +#else /* UNIV_HOTBACKUP */ + /* This function depends on MySQL code that is not included in + InnoDB Hot Backup builds. Besides, this function should never + be called in InnoDB Hot Backup. */ + ut_error; +#endif /* UNIV_HOTBACKUP */ +} + +/************************************************************************** +Report an error in a foreign key definition. */ +static +void +dict_foreign_error_report_low( +/*==========================*/ + FILE* file, /* in: output stream */ + const char* name) /* in: table name */ +{ + rewind(file); + ut_print_timestamp(file); + fprintf(file, " Error in foreign key constraint of table %s:\n", + name); +} + +/************************************************************************** +Report an error in a foreign key definition. */ +static +void +dict_foreign_error_report( +/*======================*/ + FILE* file, /* in: output stream */ + dict_foreign_t* fk, /* in: foreign key constraint */ + const char* msg) /* in: the error message */ +{ + mutex_enter(&dict_foreign_err_mutex); + dict_foreign_error_report_low(file, fk->foreign_table_name); + fputs(msg, file); + fputs(" Constraint:\n", file); + dict_print_info_on_foreign_key_in_create_format(file, NULL, fk); + if (fk->foreign_index) { + fputs("\nThe index in the foreign key in table is ", file); + ut_print_name(file, NULL, fk->foreign_index->name); + fputs( +"\nSee http://dev.mysql.com/doc/mysql/en/InnoDB_foreign_key_constraints.html\n" +"for correct foreign key definition.\n", + file); + } + mutex_exit(&dict_foreign_err_mutex); +} + +/************************************************************************** +Adds a foreign key constraint object to the dictionary cache. May free +the object if there already is an object with the same identifier in. +At least one of the foreign table and the referenced table must already +be in the dictionary cache! */ + +ulint +dict_foreign_add_to_cache( +/*======================*/ + /* out: DB_SUCCESS or error code */ + dict_foreign_t* foreign, /* in, own: foreign key constraint */ + ibool check_types) /* in: TRUE=check type compatibility */ +{ + dict_table_t* for_table; + dict_table_t* ref_table; + dict_foreign_t* for_in_cache = NULL; + dict_index_t* index; + ibool added_to_referenced_list= FALSE; + FILE* ef = dict_foreign_err_file; + +#ifdef UNIV_SYNC_DEBUG + ut_ad(mutex_own(&(dict_sys->mutex))); +#endif /* UNIV_SYNC_DEBUG */ + + for_table = dict_table_check_if_in_cache_low( + foreign->foreign_table_name); + + ref_table = dict_table_check_if_in_cache_low( + foreign->referenced_table_name); + ut_a(for_table || ref_table); + + if (for_table) { + for_in_cache = dict_foreign_find(for_table, foreign->id); + } + + if (!for_in_cache && ref_table) { + for_in_cache = dict_foreign_find(ref_table, foreign->id); + } + + if (for_in_cache) { + /* Free the foreign object */ + mem_heap_free(foreign->heap); + } else { + for_in_cache = foreign; + } + + if (for_in_cache->referenced_table == NULL && ref_table) { + dict_index_t* types_idx; + if (check_types) { + types_idx = for_in_cache->foreign_index; + } else { + types_idx = NULL; + } + index = dict_foreign_find_index(ref_table, + (const char**) for_in_cache->referenced_col_names, + for_in_cache->n_fields, + types_idx); + + if (index == NULL) { + dict_foreign_error_report(ef, for_in_cache, +"there is no index in referenced table which would contain\n" +"the columns as the first columns, or the data types in the\n" +"referenced table do not match to the ones in table."); + + if (for_in_cache == foreign) { + mem_heap_free(foreign->heap); + } + + return(DB_CANNOT_ADD_CONSTRAINT); + } + + for_in_cache->referenced_table = ref_table; + for_in_cache->referenced_index = index; + UT_LIST_ADD_LAST(referenced_list, + ref_table->referenced_list, + for_in_cache); + added_to_referenced_list = TRUE; + } + + if (for_in_cache->foreign_table == NULL && for_table) { + dict_index_t* types_idx; + if (check_types) { + types_idx = for_in_cache->referenced_index; + } else { + types_idx = NULL; + } + index = dict_foreign_find_index(for_table, + (const char**) for_in_cache->foreign_col_names, + for_in_cache->n_fields, + types_idx); + + if (index == NULL) { + dict_foreign_error_report(ef, for_in_cache, +"there is no index in the table which would contain\n" +"the columns as the first columns, or the data types in the\n" +"table do not match to the ones in the referenced table."); + + if (for_in_cache == foreign) { + if (added_to_referenced_list) { + UT_LIST_REMOVE(referenced_list, + ref_table->referenced_list, + for_in_cache); + } + + mem_heap_free(foreign->heap); + } + + return(DB_CANNOT_ADD_CONSTRAINT); + } + + for_in_cache->foreign_table = for_table; + for_in_cache->foreign_index = index; + UT_LIST_ADD_LAST(foreign_list, + for_table->foreign_list, + for_in_cache); + } + + return(DB_SUCCESS); +} + +/************************************************************************* +Scans from pointer onwards. Stops if is at the start of a copy of +'string' where characters are compared without case sensitivity, and +only outside `` or "" quotes. Stops also at '\0'. */ + +const char* +dict_scan_to( +/*=========*/ + /* out: scanned up to this */ + const char* ptr, /* in: scan from */ + const char* string) /* in: look for this */ +{ + char quote = '\0'; + + for (; *ptr; ptr++) { + if (*ptr == quote) { + /* Closing quote character: do not look for + starting quote or the keyword. */ + quote = '\0'; + } else if (quote) { + /* Within quotes: do nothing. */ + } else if (*ptr == '`' || *ptr == '"') { + /* Starting quote: remember the quote character. */ + quote = *ptr; + } else { + /* Outside quotes: look for the keyword. */ + ulint i; + for (i = 0; string[i]; i++) { + if (toupper((int)(unsigned char)(ptr[i])) + != toupper((int)(unsigned char) + (string[i]))) { + goto nomatch; + } + } + break; + nomatch: + ; + } + } + + return(ptr); +} + +/************************************************************************* +Accepts a specified string. Comparisons are case-insensitive. */ + +const char* +dict_accept( +/*========*/ + /* out: if string was accepted, the pointer + is moved after that, else ptr is returned */ + const char* ptr, /* in: scan from this */ + const char* string, /* in: accept only this string as the next + non-whitespace string */ + ibool* success)/* out: TRUE if accepted */ +{ + const char* old_ptr = ptr; + const char* old_ptr2; + + *success = FALSE; + + while (isspace(*ptr)) { + ptr++; + } + + old_ptr2 = ptr; + + ptr = dict_scan_to(ptr, string); + + if (*ptr == '\0' || old_ptr2 != ptr) { + return(old_ptr); + } + + *success = TRUE; + + return(ptr + ut_strlen(string)); +} + +/************************************************************************* +Scans an id. For the lexical definition of an 'id', see the code below. +Strips backquotes or double quotes from around the id. */ +static +const char* +dict_scan_id( +/*=========*/ + /* out: scanned to */ + const char* ptr, /* in: scanned to */ + mem_heap_t* heap, /* in: heap where to allocate the id + (NULL=id will not be allocated, but it + will point to string near ptr) */ + const char** id, /* out,own: the id; NULL if no id was + scannable */ + ibool accept_also_dot) + /* in: TRUE if also a dot can appear in a + non-quoted id; in a quoted id it can appear + always */ +{ + char quote = '\0'; + ulint len = 0; + const char* s; + char* d; + ulint id_len; + byte* b; + + *id = NULL; + + while (isspace(*ptr)) { + ptr++; + } + + if (*ptr == '\0') { + + return(ptr); + } + + if (*ptr == '`' || *ptr == '"') { + quote = *ptr++; + } + + s = ptr; + + if (quote) { + for (;;) { + if (!*ptr) { + /* Syntax error */ + return(ptr); + } + if (*ptr == quote) { + ptr++; + if (*ptr != quote) { + break; + } + } + ptr++; + len++; + } + } else { + while (!isspace(*ptr) && *ptr != '(' && *ptr != ')' + && (accept_also_dot || *ptr != '.') + && *ptr != ',' && *ptr != '\0') { + + ptr++; + } + + len = ptr - s; + } + + if (quote && heap) { + *id = d = mem_heap_alloc(heap, len + 1); + while (len--) { + if ((*d++ = *s++) == quote) { + s++; + } + } + *d++ = 0; + ut_a(*s == quote); + ut_a(s + 1 == ptr); + } else if (heap) { + *id = mem_heap_strdupl(heap, s, len); + } else { + /* no heap given: id will point to source string */ + *id = s; + } + + if (heap && !quote) { + /* EMS MySQL Manager sometimes adds characters 0xA0 (in + latin1, a 'non-breakable space') to the end of a table name. + But isspace(0xA0) is not true, which confuses our foreign key + parser. After the UTF-8 conversion in ha_innodb.cc, bytes 0xC2 + and 0xA0 are at the end of the string. + + TODO: we should lex the string using thd->charset_info, and + my_isspace(). Only after that, convert id names to UTF-8. */ + + b = (byte*)(*id); + id_len = strlen((char*) b); + + if (id_len >= 3 && b[id_len - 1] == 0xA0 + && b[id_len - 2] == 0xC2) { + + /* Strip the 2 last bytes */ + + b[id_len - 2] = '\0'; + } + } + + return(ptr); +} + +/************************************************************************* +Tries to scan a column name. */ +static +const char* +dict_scan_col( +/*==========*/ + /* out: scanned to */ + const char* ptr, /* in: scanned to */ + ibool* success,/* out: TRUE if success */ + dict_table_t* table, /* in: table in which the column is */ + dict_col_t** column, /* out: pointer to column if success */ + mem_heap_t* heap, /* in: heap where to allocate the name */ + const char** name) /* out,own: the column name; NULL if no name + was scannable */ +{ +#ifndef UNIV_HOTBACKUP + dict_col_t* col; + ulint i; + + *success = FALSE; + + ptr = dict_scan_id(ptr, heap, name, TRUE); + + if (*name == NULL) { + + return(ptr); /* Syntax error */ + } + + if (table == NULL) { + *success = TRUE; + *column = NULL; + } else { + for (i = 0; i < dict_table_get_n_cols(table); i++) { + + col = dict_table_get_nth_col(table, i); + + if (0 == innobase_strcasecmp(col->name, *name)) { + /* Found */ + + *success = TRUE; + *column = col; + strcpy((char*) *name, col->name); + + break; + } + } + } + + return(ptr); +#else /* UNIV_HOTBACKUP */ + /* This function depends on MySQL code that is not included in + InnoDB Hot Backup builds. Besides, this function should never + be called in InnoDB Hot Backup. */ + ut_error; +#endif /* UNIV_HOTBACKUP */ +} + +/************************************************************************* +Scans a table name from an SQL string. */ +static +const char* +dict_scan_table_name( +/*=================*/ + /* out: scanned to */ + const char* ptr, /* in: scanned to */ + dict_table_t** table, /* out: table object or NULL */ + const char* name, /* in: foreign key table name */ + ibool* success,/* out: TRUE if ok name found */ + mem_heap_t* heap, /* in: heap where to allocate the id */ + const char** ref_name)/* out,own: the table name; + NULL if no name was scannable */ +{ +#ifndef UNIV_HOTBACKUP + const char* database_name = NULL; + ulint database_name_len = 0; + const char* table_name = NULL; + ulint table_name_len; + const char* scan_name; + char* ref; + + *success = FALSE; + *table = NULL; + + ptr = dict_scan_id(ptr, heap, &scan_name, FALSE); + + if (scan_name == NULL) { + + return(ptr); /* Syntax error */ + } + + if (*ptr == '.') { + /* We scanned the database name; scan also the table name */ + + ptr++; + + database_name = scan_name; + database_name_len = strlen(database_name); + + ptr = dict_scan_id(ptr, heap, &table_name, FALSE); + + if (table_name == NULL) { + + return(ptr); /* Syntax error */ + } + } else { + /* To be able to read table dumps made with InnoDB-4.0.17 or + earlier, we must allow the dot separator between the database + name and the table name also to appear within a quoted + identifier! InnoDB used to print a constraint as: + ... REFERENCES `databasename.tablename` ... + starting from 4.0.18 it is + ... REFERENCES `databasename`.`tablename` ... */ + const char* s; + + for (s = scan_name; *s; s++) { + if (*s == '.') { + database_name = scan_name; + database_name_len = s - scan_name; + scan_name = ++s; + break;/* to do: multiple dots? */ + } + } + + table_name = scan_name; + } + + if (database_name == NULL) { + /* Use the database name of the foreign key table */ + + database_name = name; + database_name_len = dict_get_db_name_len(name); + } + + table_name_len = strlen(table_name); + + /* Copy database_name, '/', table_name, '\0' */ + ref = mem_heap_alloc(heap, database_name_len + table_name_len + 2); + memcpy(ref, database_name, database_name_len); + ref[database_name_len] = '/'; + memcpy(ref + database_name_len + 1, table_name, table_name_len + 1); +#ifndef __WIN__ + if (srv_lower_case_table_names) { +#endif /* !__WIN__ */ + /* The table name is always put to lower case on Windows. */ + innobase_casedn_str(ref); +#ifndef __WIN__ + } +#endif /* !__WIN__ */ + + *success = TRUE; + *ref_name = ref; + *table = dict_table_get_low(ref); + + return(ptr); +#else /* UNIV_HOTBACKUP */ + /* This function depends on MySQL code that is not included in + InnoDB Hot Backup builds. Besides, this function should never + be called in InnoDB Hot Backup. */ + ut_error; +#endif /* UNIV_HOTBACKUP */ +} + +/************************************************************************* +Skips one id. The id is allowed to contain also '.'. */ +static +const char* +dict_skip_word( +/*===========*/ + /* out: scanned to */ + const char* ptr, /* in: scanned to */ + ibool* success)/* out: TRUE if success, FALSE if just spaces + left in string or a syntax error */ +{ + const char* start; + + *success = FALSE; + + ptr = dict_scan_id(ptr, NULL, &start, TRUE); + + if (start) { + *success = TRUE; + } + + return(ptr); +} + +/************************************************************************* +Removes MySQL comments from an SQL string. A comment is either +(a) '#' to the end of the line, +(b) '--<space>' to the end of the line, or +(c) '<slash><asterisk>' till the next '<asterisk><slash>' (like the familiar +C comment syntax). */ +static +char* +dict_strip_comments( +/*================*/ + /* out, own: SQL string stripped from + comments; the caller must free this + with mem_free()! */ + const char* sql_string) /* in: SQL string */ +{ + char* str; + const char* sptr; + char* ptr; + /* unclosed quote character (0 if none) */ + char quote = 0; + + str = mem_alloc(strlen(sql_string) + 1); + + sptr = sql_string; + ptr = str; + + for (;;) { +scan_more: + if (*sptr == '\0') { + *ptr = '\0'; + + ut_a(ptr <= str + strlen(sql_string)); + + return(str); + } + + if (*sptr == quote) { + /* Closing quote character: do not look for + starting quote or comments. */ + quote = 0; + } else if (quote) { + /* Within quotes: do not look for + starting quotes or comments. */ + } else if (*sptr == '"' || *sptr == '`') { + /* Starting quote: remember the quote character. */ + quote = *sptr; + } else if (*sptr == '#' + || (sptr[0] == '-' && sptr[1] == '-' && + sptr[2] == ' ')) { + for (;;) { + /* In Unix a newline is 0x0A while in Windows + it is 0x0D followed by 0x0A */ + + if (*sptr == (char)0x0A + || *sptr == (char)0x0D + || *sptr == '\0') { + + goto scan_more; + } + + sptr++; + } + } else if (!quote && *sptr == '/' && *(sptr + 1) == '*') { + for (;;) { + if (*sptr == '*' && *(sptr + 1) == '/') { + + sptr += 2; + + goto scan_more; + } + + if (*sptr == '\0') { + + goto scan_more; + } + + sptr++; + } + } + + *ptr = *sptr; + + ptr++; + sptr++; + } +} + +/************************************************************************* +Finds the highest <number> for foreign key constraints of the table. Looks +only at the >= 4.0.18-format id's, which are of the form +databasename/tablename_ibfk_<number>. */ +static +ulint +dict_table_get_highest_foreign_id( +/*==============================*/ + /* out: highest number, 0 if table has no new + format foreign key constraints */ + dict_table_t* table) /* in: table in the dictionary memory cache */ +{ + dict_foreign_t* foreign; + char* endp; + ulint biggest_id = 0; + ulint id; + ulint len; + + ut_a(table); + + len = ut_strlen(table->name); + foreign = UT_LIST_GET_FIRST(table->foreign_list); + + while (foreign) { + if (ut_strlen(foreign->id) > ((sizeof dict_ibfk) - 1) + len + && 0 == ut_memcmp(foreign->id, table->name, len) + && 0 == ut_memcmp(foreign->id + len, + dict_ibfk, (sizeof dict_ibfk) - 1)) { + /* It is of the >= 4.0.18 format */ + + id = strtoul(foreign->id + len + ((sizeof dict_ibfk) - 1), + &endp, 10); + if (*endp == '\0') { + ut_a(id != biggest_id); + + if (id > biggest_id) { + biggest_id = id; + } + } + } + + foreign = UT_LIST_GET_NEXT(foreign_list, foreign); + } + + return(biggest_id); +} + +/************************************************************************* +Reports a simple foreign key create clause syntax error. */ +static +void +dict_foreign_report_syntax_err( +/*===========================*/ + const char* name, /* in: table name */ + const char* start_of_latest_foreign, + /* in: start of the foreign key clause + in the SQL string */ + const char* ptr) /* in: place of the syntax error */ +{ + FILE* ef = dict_foreign_err_file; + + mutex_enter(&dict_foreign_err_mutex); + dict_foreign_error_report_low(ef, name); + fprintf(ef, "%s:\nSyntax error close to:\n%s\n", + start_of_latest_foreign, ptr); + mutex_exit(&dict_foreign_err_mutex); +} + +/************************************************************************* +Scans a table create SQL string and adds to the data dictionary the foreign +key constraints declared in the string. This function should be called after +the indexes for a table have been created. Each foreign key constraint must +be accompanied with indexes in both participating tables. The indexes are +allowed to contain more fields than mentioned in the constraint. */ +static +ulint +dict_create_foreign_constraints_low( +/*================================*/ + /* out: error code or DB_SUCCESS */ + trx_t* trx, /* in: transaction */ + mem_heap_t* heap, /* in: memory heap */ + const char* sql_string, + /* in: CREATE TABLE or ALTER TABLE statement + where foreign keys are declared like: + FOREIGN KEY (a, b) REFERENCES table2(c, d), + table2 can be written also with the database + name before it: test.table2; the default + database is the database of parameter name */ + const char* name) /* in: table full name in the normalized form + database_name/table_name */ +{ + dict_table_t* table; + dict_table_t* referenced_table; + dict_table_t* table_to_alter; + ulint highest_id_so_far = 0; + dict_index_t* index; + dict_foreign_t* foreign; + const char* ptr = sql_string; + const char* start_of_latest_foreign = sql_string; + FILE* ef = dict_foreign_err_file; + const char* constraint_name; + ibool success; + ulint error; + const char* ptr1; + const char* ptr2; + ulint i; + ulint j; + ibool is_on_delete; + ulint n_on_deletes; + ulint n_on_updates; + dict_col_t* columns[500]; + const char* column_names[500]; + const char* referenced_table_name; + +#ifdef UNIV_SYNC_DEBUG + ut_ad(mutex_own(&(dict_sys->mutex))); +#endif /* UNIV_SYNC_DEBUG */ + + table = dict_table_get_low(name); + + if (table == NULL) { + mutex_enter(&dict_foreign_err_mutex); + dict_foreign_error_report_low(ef, name); + fprintf(ef, +"Cannot find the table in the internal data dictionary of InnoDB.\n" +"Create table statement:\n%s\n", sql_string); + mutex_exit(&dict_foreign_err_mutex); + + return(DB_ERROR); + } + + /* First check if we are actually doing an ALTER TABLE, and in that + case look for the table being altered */ + + ptr = dict_accept(ptr, "ALTER", &success); + + if (!success) { + + goto loop; + } + + ptr = dict_accept(ptr, "TABLE", &success); + + if (!success) { + + goto loop; + } + + /* We are doing an ALTER TABLE: scan the table name we are altering */ + + ptr = dict_scan_table_name(ptr, &table_to_alter, name, + &success, heap, &referenced_table_name); + if (!success) { + fprintf(stderr, +"InnoDB: Error: could not find the table being ALTERED in:\n%s\n", sql_string); + + return(DB_ERROR); + } + + /* Starting from 4.0.18 and 4.1.2, we generate foreign key id's in the + format databasename/tablename_ibfk_<number>, where <number> is local + to the table; look for the highest <number> for table_to_alter, so + that we can assign to new constraints higher numbers. */ + + /* If we are altering a temporary table, the table name after ALTER + TABLE does not correspond to the internal table name, and + table_to_alter is NULL. TODO: should we fix this somehow? */ + + if (table_to_alter == NULL) { + highest_id_so_far = 0; + } else { + highest_id_so_far = dict_table_get_highest_foreign_id( + table_to_alter); + } + + /* Scan for foreign key declarations in a loop */ +loop: + /* Scan either to "CONSTRAINT" or "FOREIGN", whichever is closer */ + + ptr1 = dict_scan_to(ptr, "CONSTRAINT"); + ptr2 = dict_scan_to(ptr, "FOREIGN"); + + constraint_name = NULL; + + if (ptr1 < ptr2) { + /* The user may have specified a constraint name. Pick it so + that we can store 'databasename/constraintname' as the id of + of the constraint to system tables. */ + ptr = ptr1; + + ptr = dict_accept(ptr, "CONSTRAINT", &success); + + ut_a(success); + + if (!isspace(*ptr) && *ptr != '"' && *ptr != '`') { + goto loop; + } + + while (isspace(*ptr)) { + ptr++; + } + + /* read constraint name unless got "CONSTRAINT FOREIGN" */ + if (ptr != ptr2) { + ptr = dict_scan_id(ptr, heap, &constraint_name, FALSE); + } + } else { + ptr = ptr2; + } + + if (*ptr == '\0') { + /**********************************************************/ + /* The following call adds the foreign key constraints + to the data dictionary system tables on disk */ + + error = dict_create_add_foreigns_to_dictionary( + highest_id_so_far, table, trx); + return(error); + } + + start_of_latest_foreign = ptr; + + ptr = dict_accept(ptr, "FOREIGN", &success); + + if (!success) { + goto loop; + } + + if (!isspace(*ptr)) { + goto loop; + } + + ptr = dict_accept(ptr, "KEY", &success); + + if (!success) { + goto loop; + } + + ptr = dict_accept(ptr, "(", &success); + + if (!success) { + /* MySQL allows also an index id before the '('; we + skip it */ + ptr = dict_skip_word(ptr, &success); + + if (!success) { + dict_foreign_report_syntax_err(name, + start_of_latest_foreign, ptr); + + return(DB_CANNOT_ADD_CONSTRAINT); + } + + ptr = dict_accept(ptr, "(", &success); + + if (!success) { + /* We do not flag a syntax error here because in an + ALTER TABLE we may also have DROP FOREIGN KEY abc */ + + goto loop; + } + } + + i = 0; + + /* Scan the columns in the first list */ +col_loop1: + ut_a(i < (sizeof column_names) / sizeof *column_names); + ptr = dict_scan_col(ptr, &success, table, columns + i, + heap, column_names + i); + if (!success) { + mutex_enter(&dict_foreign_err_mutex); + dict_foreign_error_report_low(ef, name); + fprintf(ef, "%s:\nCannot resolve column name close to:\n%s\n", + start_of_latest_foreign, ptr); + mutex_exit(&dict_foreign_err_mutex); + + return(DB_CANNOT_ADD_CONSTRAINT); + } + + i++; + + ptr = dict_accept(ptr, ",", &success); + + if (success) { + goto col_loop1; + } + + ptr = dict_accept(ptr, ")", &success); + + if (!success) { + dict_foreign_report_syntax_err(name, start_of_latest_foreign, + ptr); + return(DB_CANNOT_ADD_CONSTRAINT); + } + + /* Try to find an index which contains the columns + as the first fields and in the right order */ + + index = dict_foreign_find_index(table, column_names, i, NULL); + + if (!index) { + mutex_enter(&dict_foreign_err_mutex); + dict_foreign_error_report_low(ef, name); + fputs("There is no index in table ", ef); + ut_print_name(ef, NULL, name); + fprintf(ef, " where the columns appear\n" +"as the first columns. Constraint:\n%s\n" +"See http://dev.mysql.com/doc/mysql/en/InnoDB_foreign_key_constraints.html\n" +"for correct foreign key definition.\n", + start_of_latest_foreign); + mutex_exit(&dict_foreign_err_mutex); + + return(DB_CANNOT_ADD_CONSTRAINT); + } + ptr = dict_accept(ptr, "REFERENCES", &success); + + if (!success || !isspace(*ptr)) { + dict_foreign_report_syntax_err(name, start_of_latest_foreign, + ptr); + return(DB_CANNOT_ADD_CONSTRAINT); + } + + /* Let us create a constraint struct */ + + foreign = dict_mem_foreign_create(); + + if (constraint_name) { + ulint db_len; + + /* Catenate 'databasename/' to the constraint name specified + by the user: we conceive the constraint as belonging to the + same MySQL 'database' as the table itself. We store the name + to foreign->id. */ + + db_len = dict_get_db_name_len(table->name); + + foreign->id = mem_heap_alloc(foreign->heap, + db_len + strlen(constraint_name) + 2); + + ut_memcpy(foreign->id, table->name, db_len); + foreign->id[db_len] = '/'; + strcpy(foreign->id + db_len + 1, constraint_name); + } + + foreign->foreign_table = table; + foreign->foreign_table_name = mem_heap_strdup(foreign->heap, + table->name); + foreign->foreign_index = index; + foreign->n_fields = i; + foreign->foreign_col_names = mem_heap_alloc(foreign->heap, + i * sizeof(void*)); + for (i = 0; i < foreign->n_fields; i++) { + foreign->foreign_col_names[i] = + mem_heap_strdup(foreign->heap, columns[i]->name); + } + + ptr = dict_scan_table_name(ptr, &referenced_table, name, + &success, heap, &referenced_table_name); + + /* Note that referenced_table can be NULL if the user has suppressed + checking of foreign key constraints! */ + + if (!success || (!referenced_table && trx->check_foreigns)) { + dict_foreign_free(foreign); + + mutex_enter(&dict_foreign_err_mutex); + dict_foreign_error_report_low(ef, name); + fprintf(ef, "%s:\nCannot resolve table name close to:\n" + "%s\n", + start_of_latest_foreign, ptr); + mutex_exit(&dict_foreign_err_mutex); + + return(DB_CANNOT_ADD_CONSTRAINT); + } + + ptr = dict_accept(ptr, "(", &success); + + if (!success) { + dict_foreign_free(foreign); + dict_foreign_report_syntax_err(name, start_of_latest_foreign, + ptr); + return(DB_CANNOT_ADD_CONSTRAINT); + } + + /* Scan the columns in the second list */ + i = 0; + +col_loop2: + ptr = dict_scan_col(ptr, &success, referenced_table, columns + i, + heap, column_names + i); + i++; + + if (!success) { + dict_foreign_free(foreign); + + mutex_enter(&dict_foreign_err_mutex); + dict_foreign_error_report_low(ef, name); + fprintf(ef, "%s:\nCannot resolve column name close to:\n" + "%s\n", + start_of_latest_foreign, ptr); + mutex_exit(&dict_foreign_err_mutex); + + return(DB_CANNOT_ADD_CONSTRAINT); + } + + ptr = dict_accept(ptr, ",", &success); + + if (success) { + goto col_loop2; + } + + ptr = dict_accept(ptr, ")", &success); + + if (!success || foreign->n_fields != i) { + dict_foreign_free(foreign); + + dict_foreign_report_syntax_err(name, start_of_latest_foreign, + ptr); + return(DB_CANNOT_ADD_CONSTRAINT); + } + + n_on_deletes = 0; + n_on_updates = 0; + +scan_on_conditions: + /* Loop here as long as we can find ON ... conditions */ + + ptr = dict_accept(ptr, "ON", &success); + + if (!success) { + + goto try_find_index; + } + + ptr = dict_accept(ptr, "DELETE", &success); + + if (!success) { + ptr = dict_accept(ptr, "UPDATE", &success); + + if (!success) { + dict_foreign_free(foreign); + + dict_foreign_report_syntax_err(name, + start_of_latest_foreign, ptr); + return(DB_CANNOT_ADD_CONSTRAINT); + } + + is_on_delete = FALSE; + n_on_updates++; + } else { + is_on_delete = TRUE; + n_on_deletes++; + } + + ptr = dict_accept(ptr, "RESTRICT", &success); + + if (success) { + goto scan_on_conditions; + } + + ptr = dict_accept(ptr, "CASCADE", &success); + + if (success) { + if (is_on_delete) { + foreign->type |= DICT_FOREIGN_ON_DELETE_CASCADE; + } else { + foreign->type |= DICT_FOREIGN_ON_UPDATE_CASCADE; + } + + goto scan_on_conditions; + } + + ptr = dict_accept(ptr, "NO", &success); + + if (success) { + ptr = dict_accept(ptr, "ACTION", &success); + + if (!success) { + dict_foreign_free(foreign); + dict_foreign_report_syntax_err(name, + start_of_latest_foreign, ptr); + + return(DB_CANNOT_ADD_CONSTRAINT); + } + + if (is_on_delete) { + foreign->type |= DICT_FOREIGN_ON_DELETE_NO_ACTION; + } else { + foreign->type |= DICT_FOREIGN_ON_UPDATE_NO_ACTION; + } + + goto scan_on_conditions; + } + + ptr = dict_accept(ptr, "SET", &success); + + if (!success) { + dict_foreign_free(foreign); + dict_foreign_report_syntax_err(name, start_of_latest_foreign, + ptr); + return(DB_CANNOT_ADD_CONSTRAINT); + } + + ptr = dict_accept(ptr, "NULL", &success); + + if (!success) { + dict_foreign_free(foreign); + dict_foreign_report_syntax_err(name, start_of_latest_foreign, + ptr); + return(DB_CANNOT_ADD_CONSTRAINT); + } + + for (j = 0; j < foreign->n_fields; j++) { + if ((dict_index_get_nth_type( + foreign->foreign_index, j)->prtype) + & DATA_NOT_NULL) { + + /* It is not sensible to define SET NULL + if the column is not allowed to be NULL! */ + + dict_foreign_free(foreign); + + mutex_enter(&dict_foreign_err_mutex); + dict_foreign_error_report_low(ef, name); + fprintf(ef, "%s:\n" + "You have defined a SET NULL condition though some of the\n" + "columns are defined as NOT NULL.\n", start_of_latest_foreign); + mutex_exit(&dict_foreign_err_mutex); + + return(DB_CANNOT_ADD_CONSTRAINT); + } + } + + if (is_on_delete) { + foreign->type |= DICT_FOREIGN_ON_DELETE_SET_NULL; + } else { + foreign->type |= DICT_FOREIGN_ON_UPDATE_SET_NULL; + } + + goto scan_on_conditions; + +try_find_index: + if (n_on_deletes > 1 || n_on_updates > 1) { + /* It is an error to define more than 1 action */ + + dict_foreign_free(foreign); + + mutex_enter(&dict_foreign_err_mutex); + dict_foreign_error_report_low(ef, name); + fprintf(ef, "%s:\n" +"You have twice an ON DELETE clause or twice an ON UPDATE clause.\n", + start_of_latest_foreign); + mutex_exit(&dict_foreign_err_mutex); + + return(DB_CANNOT_ADD_CONSTRAINT); + } + + /* Try to find an index which contains the columns as the first fields + and in the right order, and the types are the same as in + foreign->foreign_index */ + + if (referenced_table) { + index = dict_foreign_find_index(referenced_table, + column_names, i, + foreign->foreign_index); + if (!index) { + dict_foreign_free(foreign); + mutex_enter(&dict_foreign_err_mutex); + dict_foreign_error_report_low(ef, name); + fprintf(ef, "%s:\n" +"Cannot find an index in the referenced table where the\n" +"referenced columns appear as the first columns, or column types\n" +"in the table and the referenced table do not match for constraint.\n" +"Note that the internal storage type of ENUM and SET changed in\n" +"tables created with >= InnoDB-4.1.12, and such columns in old tables\n" +"cannot be referenced by such columns in new tables.\n" +"See http://dev.mysql.com/doc/mysql/en/InnoDB_foreign_key_constraints.html\n" +"for correct foreign key definition.\n", + start_of_latest_foreign); + mutex_exit(&dict_foreign_err_mutex); + + return(DB_CANNOT_ADD_CONSTRAINT); + } + } else { + ut_a(trx->check_foreigns == FALSE); + index = NULL; + } + + foreign->referenced_index = index; + foreign->referenced_table = referenced_table; + + foreign->referenced_table_name = mem_heap_strdup(foreign->heap, + referenced_table_name); + + foreign->referenced_col_names = mem_heap_alloc(foreign->heap, + i * sizeof(void*)); + for (i = 0; i < foreign->n_fields; i++) { + foreign->referenced_col_names[i] + = mem_heap_strdup(foreign->heap, column_names[i]); + } + + /* We found an ok constraint definition: add to the lists */ + + UT_LIST_ADD_LAST(foreign_list, table->foreign_list, foreign); + + if (referenced_table) { + UT_LIST_ADD_LAST(referenced_list, + referenced_table->referenced_list, + foreign); + } + + goto loop; +} + +/************************************************************************* +Scans a table create SQL string and adds to the data dictionary the foreign +key constraints declared in the string. This function should be called after +the indexes for a table have been created. Each foreign key constraint must +be accompanied with indexes in both participating tables. The indexes are +allowed to contain more fields than mentioned in the constraint. */ + +ulint +dict_create_foreign_constraints( +/*============================*/ + /* out: error code or DB_SUCCESS */ + trx_t* trx, /* in: transaction */ + const char* sql_string, /* in: table create statement where + foreign keys are declared like: + FOREIGN KEY (a, b) REFERENCES + table2(c, d), table2 can be written + also with the database + name before it: test.table2; the + default database id the database of + parameter name */ + const char* name) /* in: table full name in the + normalized form + database_name/table_name */ +{ + char* str; + ulint err; + mem_heap_t* heap; + + str = dict_strip_comments(sql_string); + heap = mem_heap_create(10000); + + err = dict_create_foreign_constraints_low(trx, heap, str, name); + + mem_heap_free(heap); + mem_free(str); + + return(err); +} + +/************************************************************************** +Parses the CONSTRAINT id's to be dropped in an ALTER TABLE statement. */ + +ulint +dict_foreign_parse_drop_constraints( +/*================================*/ + /* out: DB_SUCCESS or + DB_CANNOT_DROP_CONSTRAINT if + syntax error or the constraint + id does not match */ + mem_heap_t* heap, /* in: heap from which we can + allocate memory */ + trx_t* trx, /* in: transaction */ + dict_table_t* table, /* in: table */ + ulint* n, /* out: number of constraints + to drop */ + const char*** constraints_to_drop) /* out: id's of the + constraints to drop */ +{ + dict_foreign_t* foreign; + ibool success; + char* str; + const char* ptr; + const char* id; + FILE* ef = dict_foreign_err_file; + + *n = 0; + + *constraints_to_drop = mem_heap_alloc(heap, 1000 * sizeof(char*)); + + str = dict_strip_comments(*(trx->mysql_query_str)); + ptr = str; + +#ifdef UNIV_SYNC_DEBUG + ut_ad(mutex_own(&(dict_sys->mutex))); +#endif /* UNIV_SYNC_DEBUG */ +loop: + ptr = dict_scan_to(ptr, "DROP"); + + if (*ptr == '\0') { + mem_free(str); + + return(DB_SUCCESS); + } + + ptr = dict_accept(ptr, "DROP", &success); + + if (!isspace(*ptr)) { + + goto loop; + } + + ptr = dict_accept(ptr, "FOREIGN", &success); + + if (!success) { + + goto loop; + } + + ptr = dict_accept(ptr, "KEY", &success); + + if (!success) { + + goto syntax_error; + } + + ptr = dict_scan_id(ptr, heap, &id, TRUE); + + if (id == NULL) { + + goto syntax_error; + } + + ut_a(*n < 1000); + (*constraints_to_drop)[*n] = id; + (*n)++; + + /* Look for the given constraint id */ + + foreign = UT_LIST_GET_FIRST(table->foreign_list); + + while (foreign != NULL) { + if (0 == strcmp(foreign->id, id) + || (strchr(foreign->id, '/') + && 0 == strcmp(id, + dict_remove_db_name(foreign->id)))) { + /* Found */ + break; + } + + foreign = UT_LIST_GET_NEXT(foreign_list, foreign); + } + + if (foreign == NULL) { + mutex_enter(&dict_foreign_err_mutex); + rewind(ef); + ut_print_timestamp(ef); + fputs( + " Error in dropping of a foreign key constraint of table ", ef); + ut_print_name(ef, NULL, table->name); + fputs(",\n" + "in SQL command\n", ef); + fputs(str, ef); + fputs("\nCannot find a constraint with the given id ", ef); + ut_print_name(ef, NULL, id); + fputs(".\n", ef); + mutex_exit(&dict_foreign_err_mutex); + + mem_free(str); + + return(DB_CANNOT_DROP_CONSTRAINT); + } + + goto loop; + +syntax_error: + mutex_enter(&dict_foreign_err_mutex); + rewind(ef); + ut_print_timestamp(ef); + fputs( + " Syntax error in dropping of a foreign key constraint of table ", ef); + ut_print_name(ef, NULL, table->name); + fprintf(ef, ",\n" + "close to:\n%s\n in SQL command\n%s\n", ptr, str); + mutex_exit(&dict_foreign_err_mutex); + + mem_free(str); + + return(DB_CANNOT_DROP_CONSTRAINT); +} + +/*==================== END OF FOREIGN KEY PROCESSING ====================*/ + +/************************************************************************** +Returns an index object if it is found in the dictionary cache. */ + +dict_index_t* +dict_index_get_if_in_cache( +/*=======================*/ + /* out: index, NULL if not found */ + dulint index_id) /* in: index id */ +{ + dict_table_t* table; + dict_index_t* index; + + if (dict_sys == NULL) { + return(NULL); + } + + mutex_enter(&(dict_sys->mutex)); + + table = UT_LIST_GET_FIRST(dict_sys->table_LRU); + + while (table) { + index = UT_LIST_GET_FIRST(table->indexes); + + while (index) { + if (0 == ut_dulint_cmp(index->id, index_id)) { + + goto found; + } + + index = UT_LIST_GET_NEXT(indexes, index); + } + + table = UT_LIST_GET_NEXT(table_LRU, table); + } + + index = NULL; +found: + mutex_exit(&(dict_sys->mutex)); + + return(index); +} + +/************************************************************************** +Creates an index tree struct. */ + +dict_tree_t* +dict_tree_create( +/*=============*/ + /* out, own: created tree */ + dict_index_t* index, /* in: the index for which to create: in the + case of a mixed tree, this should be the + index of the cluster object */ + ulint page_no)/* in: root page number of the index */ +{ + dict_tree_t* tree; + + tree = mem_alloc(sizeof(dict_tree_t)); + + /* Inherit info from the index */ + + tree->type = index->type; + tree->space = index->space; + tree->page = page_no; + + tree->id = index->id; + + UT_LIST_INIT(tree->tree_indexes); + + tree->magic_n = DICT_TREE_MAGIC_N; + + rw_lock_create(&(tree->lock)); + + rw_lock_set_level(&(tree->lock), SYNC_INDEX_TREE); + + return(tree); +} + +/************************************************************************** +Frees an index tree struct. */ + +void +dict_tree_free( +/*===========*/ + dict_tree_t* tree) /* in, own: index tree */ +{ + ut_a(tree); + ut_ad(tree->magic_n == DICT_TREE_MAGIC_N); + + rw_lock_free(&(tree->lock)); + mem_free(tree); +} + +/************************************************************************** +In an index tree, finds the index corresponding to a record in the tree. */ +UNIV_INLINE +dict_index_t* +dict_tree_find_index_low( +/*=====================*/ + /* out: index */ + dict_tree_t* tree, /* in: index tree */ + rec_t* rec) /* in: record for which to find correct + index */ +{ + dict_index_t* index; + dict_table_t* table; + dulint mix_id; + ulint len; + + index = UT_LIST_GET_FIRST(tree->tree_indexes); + ut_ad(index); + table = index->table; + + if ((index->type & DICT_CLUSTERED) + && (table->type != DICT_TABLE_ORDINARY)) { + + /* Get the mix id of the record */ + ut_a(!table->comp); + + mix_id = mach_dulint_read_compressed( + rec_get_nth_field_old(rec, table->mix_len, &len)); + + while (ut_dulint_cmp(table->mix_id, mix_id) != 0) { + + index = UT_LIST_GET_NEXT(tree_indexes, index); + table = index->table; + ut_ad(index); + } + } + + return(index); +} + +/************************************************************************** +In an index tree, finds the index corresponding to a record in the tree. */ + +dict_index_t* +dict_tree_find_index( +/*=================*/ + /* out: index */ + dict_tree_t* tree, /* in: index tree */ + rec_t* rec) /* in: record for which to find correct + index */ +{ + dict_index_t* index; + + index = dict_tree_find_index_low(tree, rec); + + return(index); +} + +/************************************************************************** +In an index tree, finds the index corresponding to a dtuple which is used +in a search to a tree. */ + +dict_index_t* +dict_tree_find_index_for_tuple( +/*===========================*/ + /* out: index; NULL if the tuple does not + contain the mix id field in a mixed tree */ + dict_tree_t* tree, /* in: index tree */ + dtuple_t* tuple) /* in: tuple for which to find index */ +{ + dict_index_t* index; + dict_table_t* table; + dulint mix_id; + + ut_ad(dtuple_check_typed(tuple)); + + if (UT_LIST_GET_LEN(tree->tree_indexes) == 1) { + + return(UT_LIST_GET_FIRST(tree->tree_indexes)); + } + + index = UT_LIST_GET_FIRST(tree->tree_indexes); + ut_ad(index); + table = index->table; + + if (dtuple_get_n_fields(tuple) <= table->mix_len) { + + return(NULL); + } + + /* Get the mix id of the record */ + + mix_id = mach_dulint_read_compressed( + dfield_get_data( + dtuple_get_nth_field(tuple, table->mix_len))); + + while (ut_dulint_cmp(table->mix_id, mix_id) != 0) { + + index = UT_LIST_GET_NEXT(tree_indexes, index); + table = index->table; + ut_ad(index); + } + + return(index); +} + +/*********************************************************************** +Checks if a table which is a mixed cluster member owns a record. */ + +ibool +dict_is_mixed_table_rec( +/*====================*/ + /* out: TRUE if the record belongs to this + table */ + dict_table_t* table, /* in: table in a mixed cluster */ + rec_t* rec) /* in: user record in the clustered index */ +{ + byte* mix_id_field; + ulint len; + + ut_ad(!table->comp); + + mix_id_field = rec_get_nth_field_old(rec, + table->mix_len, &len); + + return(len == table->mix_id_len + && !ut_memcmp(table->mix_id_buf, mix_id_field, len)); +} + +/************************************************************************** +Checks that a tuple has n_fields_cmp value in a sensible range, so that +no comparison can occur with the page number field in a node pointer. */ + +ibool +dict_tree_check_search_tuple( +/*=========================*/ + /* out: TRUE if ok */ + dict_tree_t* tree, /* in: index tree */ + dtuple_t* tuple) /* in: tuple used in a search */ +{ + dict_index_t* index; + + index = dict_tree_find_index_for_tuple(tree, tuple); + + if (index == NULL) { + + return(TRUE); + } + + ut_a(dtuple_get_n_fields_cmp(tuple) + <= dict_index_get_n_unique_in_tree(index)); + return(TRUE); +} + +/************************************************************************** +Builds a node pointer out of a physical record and a page number. */ + +dtuple_t* +dict_tree_build_node_ptr( +/*=====================*/ + /* out, own: node pointer */ + dict_tree_t* tree, /* in: index tree */ + rec_t* rec, /* in: record for which to build node + pointer */ + ulint page_no,/* in: page number to put in node pointer */ + mem_heap_t* heap, /* in: memory heap where pointer created */ + ulint level) /* in: level of rec in tree: 0 means leaf + level */ +{ + dtuple_t* tuple; + dict_index_t* ind; + dfield_t* field; + byte* buf; + ulint n_unique; + + ind = dict_tree_find_index_low(tree, rec); + + if (tree->type & DICT_UNIVERSAL) { + /* In a universal index tree, we take the whole record as + the node pointer if the reord is on the leaf level, + on non-leaf levels we remove the last field, which + contains the page number of the child page */ + + ut_a(!ind->table->comp); + n_unique = rec_get_n_fields_old(rec); + + if (level > 0) { + ut_a(n_unique > 1); + n_unique--; + } + } else { + n_unique = dict_index_get_n_unique_in_tree(ind); + } + + tuple = dtuple_create(heap, n_unique + 1); + + /* When searching in the tree for the node pointer, we must not do + comparison on the last field, the page number field, as on upper + levels in the tree there may be identical node pointers with a + different page number; therefore, we set the n_fields_cmp to one + less: */ + + dtuple_set_n_fields_cmp(tuple, n_unique); + + dict_index_copy_types(tuple, ind, n_unique); + + buf = mem_heap_alloc(heap, 4); + + mach_write_to_4(buf, page_no); + + field = dtuple_get_nth_field(tuple, n_unique); + dfield_set_data(field, buf, 4); + + dtype_set(dfield_get_type(field), DATA_SYS_CHILD, DATA_NOT_NULL, 4, 0); + + rec_copy_prefix_to_dtuple(tuple, rec, ind, n_unique, heap); + dtuple_set_info_bits(tuple, dtuple_get_info_bits(tuple) | + REC_STATUS_NODE_PTR); + + ut_ad(dtuple_check_typed(tuple)); + + return(tuple); +} + +/************************************************************************** +Copies an initial segment of a physical record, long enough to specify an +index entry uniquely. */ + +rec_t* +dict_tree_copy_rec_order_prefix( +/*============================*/ + /* out: pointer to the prefix record */ + dict_tree_t* tree, /* in: index tree */ + rec_t* rec, /* in: record for which to copy prefix */ + ulint* n_fields,/* out: number of fields copied */ + byte** buf, /* in/out: memory buffer for the copied prefix, + or NULL */ + ulint* buf_size)/* in/out: buffer size */ +{ + dict_index_t* index; + ulint n; + + index = dict_tree_find_index_low(tree, rec); + + if (tree->type & DICT_UNIVERSAL) { + ut_a(!index->table->comp); + n = rec_get_n_fields_old(rec); + } else { + n = dict_index_get_n_unique_in_tree(index); + } + + *n_fields = n; + return(rec_copy_prefix_to_buf(rec, index, n, buf, buf_size)); +} + +/************************************************************************** +Builds a typed data tuple out of a physical record. */ + +dtuple_t* +dict_tree_build_data_tuple( +/*=======================*/ + /* out, own: data tuple */ + dict_tree_t* tree, /* in: index tree */ + rec_t* rec, /* in: record for which to build data tuple */ + ulint n_fields,/* in: number of data fields */ + mem_heap_t* heap) /* in: memory heap where tuple created */ +{ + dtuple_t* tuple; + dict_index_t* ind; + + ind = dict_tree_find_index_low(tree, rec); + + ut_ad(ind->table->comp || n_fields <= rec_get_n_fields_old(rec)); + + tuple = dtuple_create(heap, n_fields); + + dict_index_copy_types(tuple, ind, n_fields); + + rec_copy_prefix_to_dtuple(tuple, rec, ind, n_fields, heap); + + ut_ad(dtuple_check_typed(tuple)); + + return(tuple); +} + +/************************************************************************* +Calculates the minimum record length in an index. */ + +ulint +dict_index_calc_min_rec_len( +/*========================*/ + dict_index_t* index) /* in: index */ +{ + ulint sum = 0; + ulint i; + + if (index->table->comp) { + ulint nullable = 0; + sum = REC_N_NEW_EXTRA_BYTES; + for (i = 0; i < dict_index_get_n_fields(index); i++) { + dtype_t*t = dict_index_get_nth_type(index, i); + ulint size = dtype_get_fixed_size(t); + sum += size; + if (!size) { + size = dtype_get_len(t); + sum += size < 128 ? 1 : 2; + } + if (!(dtype_get_prtype(t) & DATA_NOT_NULL)) + nullable++; + } + + /* round the NULL flags up to full bytes */ + sum += (nullable + 7) / 8; + + return(sum); + } + + for (i = 0; i < dict_index_get_n_fields(index); i++) { + sum += dtype_get_fixed_size(dict_index_get_nth_type(index, i)); + } + + if (sum > 127) { + sum += 2 * dict_index_get_n_fields(index); + } else { + sum += dict_index_get_n_fields(index); + } + + sum += REC_N_OLD_EXTRA_BYTES; + + return(sum); +} + +/************************************************************************* +Calculates new estimates for table and index statistics. The statistics +are used in query optimization. */ + +void +dict_update_statistics_low( +/*=======================*/ + dict_table_t* table, /* in: table */ + ibool has_dict_mutex __attribute__((unused))) + /* in: TRUE if the caller has the + dictionary mutex */ +{ + dict_index_t* index; + ulint size; + ulint sum_of_index_sizes = 0; + + if (table->ibd_file_missing) { + ut_print_timestamp(stderr); + fprintf(stderr, + " InnoDB: cannot calculate statistics for table %s\n" +"InnoDB: because the .ibd file is missing. For help, please refer to\n" +"InnoDB: " +"http://dev.mysql.com/doc/mysql/en/InnoDB_troubleshooting_datadict.html\n", + table->name); + + return; + } + + /* If we have set a high innodb_force_recovery level, do not calculate + statistics, as a badly corrupted index can cause a crash in it. */ + + if (srv_force_recovery >= SRV_FORCE_NO_IBUF_MERGE) { + + return; + } + + /* Find out the sizes of the indexes and how many different values + for the key they approximately have */ + + index = dict_table_get_first_index(table); + + if (index == NULL) { + /* Table definition is corrupt */ + + return; + } + + while (index) { + size = btr_get_size(index, BTR_TOTAL_SIZE); + + index->stat_index_size = size; + + sum_of_index_sizes += size; + + size = btr_get_size(index, BTR_N_LEAF_PAGES); + + if (size == 0) { + /* The root node of the tree is a leaf */ + size = 1; + } + + index->stat_n_leaf_pages = size; + + btr_estimate_number_of_different_key_vals(index); + + index = dict_table_get_next_index(index); + } + + index = dict_table_get_first_index(table); + + table->stat_n_rows = index->stat_n_diff_key_vals[ + dict_index_get_n_unique(index)]; + + table->stat_clustered_index_size = index->stat_index_size; + + table->stat_sum_of_other_index_sizes = sum_of_index_sizes + - index->stat_index_size; + + table->stat_initialized = TRUE; + + table->stat_modified_counter = 0; +} + +/************************************************************************* +Calculates new estimates for table and index statistics. The statistics +are used in query optimization. */ + +void +dict_update_statistics( +/*===================*/ + dict_table_t* table) /* in: table */ +{ + dict_update_statistics_low(table, FALSE); +} + +/************************************************************************** +Prints info of a foreign key constraint. */ +static +void +dict_foreign_print_low( +/*===================*/ + dict_foreign_t* foreign) /* in: foreign key constraint */ +{ + ulint i; + +#ifdef UNIV_SYNC_DEBUG + ut_ad(mutex_own(&(dict_sys->mutex))); +#endif /* UNIV_SYNC_DEBUG */ + + fprintf(stderr, " FOREIGN KEY CONSTRAINT %s: %s (", + foreign->id, foreign->foreign_table_name); + + for (i = 0; i < foreign->n_fields; i++) { + fprintf(stderr, " %s", foreign->foreign_col_names[i]); + } + + fprintf(stderr, " )\n" + " REFERENCES %s (", + foreign->referenced_table_name); + + for (i = 0; i < foreign->n_fields; i++) { + fprintf(stderr, " %s", foreign->referenced_col_names[i]); + } + + fputs(" )\n", stderr); +} + +/************************************************************************** +Prints a table data. */ + +void +dict_table_print( +/*=============*/ + dict_table_t* table) /* in: table */ +{ + mutex_enter(&(dict_sys->mutex)); + dict_table_print_low(table); + mutex_exit(&(dict_sys->mutex)); +} + +/************************************************************************** +Prints a table data when we know the table name. */ + +void +dict_table_print_by_name( +/*=====================*/ + const char* name) +{ + dict_table_t* table; + + mutex_enter(&(dict_sys->mutex)); + + table = dict_table_get_low(name); + + ut_a(table); + + dict_table_print_low(table); + mutex_exit(&(dict_sys->mutex)); +} + +/************************************************************************** +Prints a table data. */ + +void +dict_table_print_low( +/*=================*/ + dict_table_t* table) /* in: table */ +{ + dict_index_t* index; + dict_foreign_t* foreign; + ulint i; + +#ifdef UNIV_SYNC_DEBUG + ut_ad(mutex_own(&(dict_sys->mutex))); +#endif /* UNIV_SYNC_DEBUG */ + + dict_update_statistics_low(table, TRUE); + + fprintf(stderr, +"--------------------------------------\n" +"TABLE: name %s, id %lu %lu, columns %lu, indexes %lu, appr.rows %lu\n" +" COLUMNS: ", + table->name, + (ulong) ut_dulint_get_high(table->id), + (ulong) ut_dulint_get_low(table->id), + (ulong) table->n_cols, + (ulong) UT_LIST_GET_LEN(table->indexes), + (ulong) table->stat_n_rows); + + for (i = 0; i < table->n_cols - 1; i++) { + dict_col_print_low(dict_table_get_nth_col(table, i)); + fputs("; ", stderr); + } + + putc('\n', stderr); + + index = UT_LIST_GET_FIRST(table->indexes); + + while (index != NULL) { + dict_index_print_low(index); + index = UT_LIST_GET_NEXT(indexes, index); + } + + foreign = UT_LIST_GET_FIRST(table->foreign_list); + + while (foreign != NULL) { + dict_foreign_print_low(foreign); + foreign = UT_LIST_GET_NEXT(foreign_list, foreign); + } + + foreign = UT_LIST_GET_FIRST(table->referenced_list); + + while (foreign != NULL) { + dict_foreign_print_low(foreign); + foreign = UT_LIST_GET_NEXT(referenced_list, foreign); + } +} + +/************************************************************************** +Prints a column data. */ +static +void +dict_col_print_low( +/*===============*/ + dict_col_t* col) /* in: column */ +{ + dtype_t* type; + +#ifdef UNIV_SYNC_DEBUG + ut_ad(mutex_own(&(dict_sys->mutex))); +#endif /* UNIV_SYNC_DEBUG */ + + type = dict_col_get_type(col); + fprintf(stderr, "%s: ", col->name); + + dtype_print(type); +} + +/************************************************************************** +Prints an index data. */ +static +void +dict_index_print_low( +/*=================*/ + dict_index_t* index) /* in: index */ +{ + dict_tree_t* tree; + ib_longlong n_vals; + ulint i; + +#ifdef UNIV_SYNC_DEBUG + ut_ad(mutex_own(&(dict_sys->mutex))); +#endif /* UNIV_SYNC_DEBUG */ + + tree = index->tree; + + if (index->n_user_defined_cols > 0) { + n_vals = index->stat_n_diff_key_vals[ + index->n_user_defined_cols]; + } else { + n_vals = index->stat_n_diff_key_vals[1]; + } + + fprintf(stderr, + " INDEX: name %s, id %lu %lu, fields %lu/%lu, type %lu\n" + " root page %lu, appr.key vals %lu," + " leaf pages %lu, size pages %lu\n" + " FIELDS: ", + index->name, + (ulong) ut_dulint_get_high(tree->id), + (ulong) ut_dulint_get_low(tree->id), + (ulong) index->n_user_defined_cols, + (ulong) index->n_fields, (ulong) index->type, + (ulong) tree->page, + (ulong) n_vals, + (ulong) index->stat_n_leaf_pages, + (ulong) index->stat_index_size); + + for (i = 0; i < index->n_fields; i++) { + dict_field_print_low(dict_index_get_nth_field(index, i)); + } + + putc('\n', stderr); + +/* btr_print_size(tree); */ + +/* btr_print_tree(tree, 7); */ +} + +/************************************************************************** +Prints a field data. */ +static +void +dict_field_print_low( +/*=================*/ + dict_field_t* field) /* in: field */ +{ +#ifdef UNIV_SYNC_DEBUG + ut_ad(mutex_own(&(dict_sys->mutex))); +#endif /* UNIV_SYNC_DEBUG */ + fprintf(stderr, " %s", field->name); + + if (field->prefix_len != 0) { + fprintf(stderr, "(%lu)", (ulong) field->prefix_len); + } +} + +/************************************************************************** +Outputs info on a foreign key of a table in a format suitable for +CREATE TABLE. */ + +void +dict_print_info_on_foreign_key_in_create_format( +/*============================================*/ + FILE* file, /* in: file where to print */ + trx_t* trx, /* in: transaction */ + dict_foreign_t* foreign)/* in: foreign key constraint */ +{ + const char* stripped_id; + ulint i; + + if (strchr(foreign->id, '/')) { + /* Strip the preceding database name from the constraint id */ + stripped_id = foreign->id + 1 + + dict_get_db_name_len(foreign->id); + } else { + stripped_id = foreign->id; + } + + fputs(",\n CONSTRAINT ", file); + ut_print_name(file, trx, stripped_id); + fputs(" FOREIGN KEY (", file); + + for (i = 0;;) { + ut_print_name(file, trx, foreign->foreign_col_names[i]); + if (++i < foreign->n_fields) { + fputs(", ", file); + } else { + break; + } + } + + fputs(") REFERENCES ", file); + + if (dict_tables_have_same_db(foreign->foreign_table_name, + foreign->referenced_table_name)) { + /* Do not print the database name of the referenced table */ + ut_print_name(file, trx, dict_remove_db_name( + foreign->referenced_table_name)); + } else { + /* Look for the '/' in the table name */ + + i = 0; + while (foreign->referenced_table_name[i] != '/') { + i++; + } + + ut_print_namel(file, trx, foreign->referenced_table_name, i); + putc('.', file); + ut_print_name(file, trx, + foreign->referenced_table_name + i + 1); + } + + putc(' ', file); + putc('(', file); + + for (i = 0;;) { + ut_print_name(file, trx, foreign->referenced_col_names[i]); + if (++i < foreign->n_fields) { + fputs(", ", file); + } else { + break; + } + } + + putc(')', file); + + if (foreign->type & DICT_FOREIGN_ON_DELETE_CASCADE) { + fputs(" ON DELETE CASCADE", file); + } + + if (foreign->type & DICT_FOREIGN_ON_DELETE_SET_NULL) { + fputs(" ON DELETE SET NULL", file); + } + + if (foreign->type & DICT_FOREIGN_ON_DELETE_NO_ACTION) { + fputs(" ON DELETE NO ACTION", file); + } + + if (foreign->type & DICT_FOREIGN_ON_UPDATE_CASCADE) { + fputs(" ON UPDATE CASCADE", file); + } + + if (foreign->type & DICT_FOREIGN_ON_UPDATE_SET_NULL) { + fputs(" ON UPDATE SET NULL", file); + } + + if (foreign->type & DICT_FOREIGN_ON_UPDATE_NO_ACTION) { + fputs(" ON UPDATE NO ACTION", file); + } +} + +/************************************************************************** +Outputs info on foreign keys of a table. */ + +void +dict_print_info_on_foreign_keys( +/*============================*/ + ibool create_table_format, /* in: if TRUE then print in + a format suitable to be inserted into + a CREATE TABLE, otherwise in the format + of SHOW TABLE STATUS */ + FILE* file, /* in: file where to print */ + trx_t* trx, /* in: transaction */ + dict_table_t* table) /* in: table */ +{ + dict_foreign_t* foreign; + + mutex_enter(&(dict_sys->mutex)); + + foreign = UT_LIST_GET_FIRST(table->foreign_list); + + if (foreign == NULL) { + mutex_exit(&(dict_sys->mutex)); + + return; + } + + while (foreign != NULL) { + if (create_table_format) { + dict_print_info_on_foreign_key_in_create_format( + file, trx, foreign); + } else { + ulint i; + fputs("; (", file); + + for (i = 0; i < foreign->n_fields; i++) { + if (i) { + putc(' ', file); + } + + ut_print_name(file, trx, + foreign->foreign_col_names[i]); + } + + fputs(") REFER ", file); + ut_print_name(file, trx, + foreign->referenced_table_name); + putc('(', file); + + for (i = 0; i < foreign->n_fields; i++) { + if (i) { + putc(' ', file); + } + ut_print_name(file, trx, + foreign->referenced_col_names[i]); + } + + putc(')', file); + + if (foreign->type == DICT_FOREIGN_ON_DELETE_CASCADE) { + fputs(" ON DELETE CASCADE", file); + } + + if (foreign->type == DICT_FOREIGN_ON_DELETE_SET_NULL) { + fputs(" ON DELETE SET NULL", file); + } + + if (foreign->type & DICT_FOREIGN_ON_DELETE_NO_ACTION) { + fputs(" ON DELETE NO ACTION", file); + } + + if (foreign->type & DICT_FOREIGN_ON_UPDATE_CASCADE) { + fputs(" ON UPDATE CASCADE", file); + } + + if (foreign->type & DICT_FOREIGN_ON_UPDATE_SET_NULL) { + fputs(" ON UPDATE SET NULL", file); + } + + if (foreign->type & DICT_FOREIGN_ON_UPDATE_NO_ACTION) { + fputs(" ON UPDATE NO ACTION", file); + } + } + + foreign = UT_LIST_GET_NEXT(foreign_list, foreign); + } + + mutex_exit(&(dict_sys->mutex)); +} + +/************************************************************************ +Displays the names of the index and the table. */ +void +dict_index_name_print( +/*==================*/ + FILE* file, /* in: output stream */ + trx_t* trx, /* in: transaction */ + const dict_index_t* index) /* in: index to print */ +{ + fputs("index ", file); + ut_print_name(file, trx, index->name); + fputs(" of table ", file); + ut_print_name(file, trx, index->table_name); +} diff --git a/storage/innobase/dict/dict0load.c b/storage/innobase/dict/dict0load.c new file mode 100644 index 00000000000..9bafcf33553 --- /dev/null +++ b/storage/innobase/dict/dict0load.c @@ -0,0 +1,1354 @@ +/****************************************************** +Loads to the memory cache database object definitions +from dictionary tables + +(c) 1996 Innobase Oy + +Created 4/24/1996 Heikki Tuuri +*******************************************************/ + +#include "dict0load.h" +#ifndef UNIV_HOTBACKUP +#include "mysql_version.h" +#endif /* !UNIV_HOTBACKUP */ + +#ifdef UNIV_NONINL +#include "dict0load.ic" +#endif + +#include "btr0pcur.h" +#include "btr0btr.h" +#include "page0page.h" +#include "mach0data.h" +#include "dict0dict.h" +#include "dict0boot.h" +#include "rem0cmp.h" +#include "srv0start.h" +#include "srv0srv.h" + +/************************************************************************ +Finds the first table name in the given database. */ + +char* +dict_get_first_table_name_in_db( +/*============================*/ + /* out, own: table name, NULL if + does not exist; the caller must + free the memory in the string! */ + const char* name) /* in: database name which ends in '/' */ +{ + dict_table_t* sys_tables; + btr_pcur_t pcur; + dict_index_t* sys_index; + dtuple_t* tuple; + mem_heap_t* heap; + dfield_t* dfield; + rec_t* rec; + byte* field; + ulint len; + mtr_t mtr; + +#ifdef UNIV_SYNC_DEBUG + ut_ad(mutex_own(&(dict_sys->mutex))); +#endif /* UNIV_SYNC_DEBUG */ + + heap = mem_heap_create(1000); + + mtr_start(&mtr); + + sys_tables = dict_table_get_low("SYS_TABLES"); + sys_index = UT_LIST_GET_FIRST(sys_tables->indexes); + ut_a(!sys_tables->comp); + + tuple = dtuple_create(heap, 1); + dfield = dtuple_get_nth_field(tuple, 0); + + dfield_set_data(dfield, name, ut_strlen(name)); + dict_index_copy_types(tuple, sys_index, 1); + + btr_pcur_open_on_user_rec(sys_index, tuple, PAGE_CUR_GE, + BTR_SEARCH_LEAF, &pcur, &mtr); +loop: + rec = btr_pcur_get_rec(&pcur); + + if (!btr_pcur_is_on_user_rec(&pcur, &mtr)) { + /* Not found */ + + btr_pcur_close(&pcur); + mtr_commit(&mtr); + mem_heap_free(heap); + + return(NULL); + } + + field = rec_get_nth_field_old(rec, 0, &len); + + if (len < strlen(name) + || ut_memcmp(name, field, strlen(name)) != 0) { + /* Not found */ + + btr_pcur_close(&pcur); + mtr_commit(&mtr); + mem_heap_free(heap); + + return(NULL); + } + + if (!rec_get_deleted_flag(rec, sys_tables->comp)) { + + /* We found one */ + + char* table_name = mem_strdupl((char*) field, len); + + btr_pcur_close(&pcur); + mtr_commit(&mtr); + mem_heap_free(heap); + + return(table_name); + } + + btr_pcur_move_to_next_user_rec(&pcur, &mtr); + + goto loop; +} + +/************************************************************************ +Prints to the standard output information on all tables found in the data +dictionary system table. */ + +void +dict_print(void) +/*============*/ +{ + dict_table_t* sys_tables; + dict_index_t* sys_index; + dict_table_t* table; + btr_pcur_t pcur; + rec_t* rec; + byte* field; + ulint len; + mtr_t mtr; + + /* Enlarge the fatal semaphore wait timeout during the InnoDB table + monitor printout */ + + mutex_enter(&kernel_mutex); + srv_fatal_semaphore_wait_threshold += 7200; /* 2 hours */ + mutex_exit(&kernel_mutex); + + mutex_enter(&(dict_sys->mutex)); + + mtr_start(&mtr); + + sys_tables = dict_table_get_low("SYS_TABLES"); + sys_index = UT_LIST_GET_FIRST(sys_tables->indexes); + + btr_pcur_open_at_index_side(TRUE, sys_index, BTR_SEARCH_LEAF, &pcur, + TRUE, &mtr); +loop: + btr_pcur_move_to_next_user_rec(&pcur, &mtr); + + rec = btr_pcur_get_rec(&pcur); + + if (!btr_pcur_is_on_user_rec(&pcur, &mtr)) { + /* end of index */ + + btr_pcur_close(&pcur); + mtr_commit(&mtr); + + mutex_exit(&(dict_sys->mutex)); + + /* Restore the fatal semaphore wait timeout */ + + mutex_enter(&kernel_mutex); + srv_fatal_semaphore_wait_threshold -= 7200; /* 2 hours */ + mutex_exit(&kernel_mutex); + + return; + } + + field = rec_get_nth_field_old(rec, 0, &len); + + if (!rec_get_deleted_flag(rec, sys_tables->comp)) { + + /* We found one */ + + char* table_name = mem_strdupl((char*) field, len); + + btr_pcur_store_position(&pcur, &mtr); + + mtr_commit(&mtr); + + table = dict_table_get_low(table_name); + mem_free(table_name); + + if (table == NULL) { + fputs("InnoDB: Failed to load table ", stderr); + ut_print_namel(stderr, NULL, (char*) field, len); + putc('\n', stderr); + } else { + /* The table definition was corrupt if there + is no index */ + + if (dict_table_get_first_index(table)) { + dict_update_statistics_low(table, TRUE); + } + + dict_table_print_low(table); + } + + mtr_start(&mtr); + + btr_pcur_restore_position(BTR_SEARCH_LEAF, &pcur, &mtr); + } + + goto loop; +} + +/************************************************************************ +In a crash recovery we already have all the tablespace objects created. +This function compares the space id information in the InnoDB data dictionary +to what we already read with fil_load_single_table_tablespaces(). + +In a normal startup, we create the tablespace objects for every table in +InnoDB's data dictionary, if the corresponding .ibd file exists. +We also scan the biggest space id, and store it to fil_system. */ + +void +dict_check_tablespaces_and_store_max_id( +/*====================================*/ + ibool in_crash_recovery) /* in: are we doing a crash recovery */ +{ + dict_table_t* sys_tables; + dict_index_t* sys_index; + btr_pcur_t pcur; + rec_t* rec; + byte* field; + ulint len; + ulint space_id; + ulint max_space_id = 0; + mtr_t mtr; + + mutex_enter(&(dict_sys->mutex)); + + mtr_start(&mtr); + + sys_tables = dict_table_get_low("SYS_TABLES"); + sys_index = UT_LIST_GET_FIRST(sys_tables->indexes); + ut_a(!sys_tables->comp); + + btr_pcur_open_at_index_side(TRUE, sys_index, BTR_SEARCH_LEAF, &pcur, + TRUE, &mtr); +loop: + btr_pcur_move_to_next_user_rec(&pcur, &mtr); + + rec = btr_pcur_get_rec(&pcur); + + if (!btr_pcur_is_on_user_rec(&pcur, &mtr)) { + /* end of index */ + + btr_pcur_close(&pcur); + mtr_commit(&mtr); + + /* We must make the tablespace cache aware of the biggest + known space id */ + + /* printf("Biggest space id in data dictionary %lu\n", + max_space_id); */ + fil_set_max_space_id_if_bigger(max_space_id); + + mutex_exit(&(dict_sys->mutex)); + + return; + } + + field = rec_get_nth_field_old(rec, 0, &len); + + if (!rec_get_deleted_flag(rec, sys_tables->comp)) { + + /* We found one */ + + char* name = mem_strdupl((char*) field, len); + + field = rec_get_nth_field_old(rec, 9, &len); + ut_a(len == 4); + + space_id = mach_read_from_4(field); + + btr_pcur_store_position(&pcur, &mtr); + + mtr_commit(&mtr); + + if (space_id != 0 && in_crash_recovery) { + /* Check that the tablespace (the .ibd file) really + exists; print a warning to the .err log if not */ + + fil_space_for_table_exists_in_mem(space_id, name, + FALSE, TRUE, TRUE); + } + + if (space_id != 0 && !in_crash_recovery) { + /* It is a normal database startup: create the space + object and check that the .ibd file exists. */ + + fil_open_single_table_tablespace(FALSE, space_id, + name); + } + + mem_free(name); + + if (space_id > max_space_id) { + max_space_id = space_id; + } + + mtr_start(&mtr); + + btr_pcur_restore_position(BTR_SEARCH_LEAF, &pcur, &mtr); + } + + goto loop; +} + +/************************************************************************ +Loads definitions for table columns. */ +static +void +dict_load_columns( +/*==============*/ + dict_table_t* table, /* in: table */ + mem_heap_t* heap) /* in: memory heap for temporary storage */ +{ + dict_table_t* sys_columns; + dict_index_t* sys_index; + btr_pcur_t pcur; + dtuple_t* tuple; + dfield_t* dfield; + rec_t* rec; + byte* field; + ulint len; + byte* buf; + char* name; + ulint mtype; + ulint prtype; + ulint col_len; + ulint prec; + ulint i; + mtr_t mtr; + +#ifdef UNIV_SYNC_DEBUG + ut_ad(mutex_own(&(dict_sys->mutex))); +#endif /* UNIV_SYNC_DEBUG */ + + mtr_start(&mtr); + + sys_columns = dict_table_get_low("SYS_COLUMNS"); + sys_index = UT_LIST_GET_FIRST(sys_columns->indexes); + ut_a(!sys_columns->comp); + + tuple = dtuple_create(heap, 1); + dfield = dtuple_get_nth_field(tuple, 0); + + buf = mem_heap_alloc(heap, 8); + mach_write_to_8(buf, table->id); + + dfield_set_data(dfield, buf, 8); + dict_index_copy_types(tuple, sys_index, 1); + + btr_pcur_open_on_user_rec(sys_index, tuple, PAGE_CUR_GE, + BTR_SEARCH_LEAF, &pcur, &mtr); + for (i = 0; i < table->n_cols - DATA_N_SYS_COLS; i++) { + + rec = btr_pcur_get_rec(&pcur); + + ut_a(btr_pcur_is_on_user_rec(&pcur, &mtr)); + + ut_a(!rec_get_deleted_flag(rec, sys_columns->comp)); + + field = rec_get_nth_field_old(rec, 0, &len); + ut_ad(len == 8); + ut_a(ut_dulint_cmp(table->id, mach_read_from_8(field)) == 0); + + field = rec_get_nth_field_old(rec, 1, &len); + ut_ad(len == 4); + ut_a(i == mach_read_from_4(field)); + + ut_a(0 == ut_strcmp("NAME", + dict_field_get_col( + dict_index_get_nth_field(sys_index, 4))->name)); + + field = rec_get_nth_field_old(rec, 4, &len); + name = mem_heap_strdupl(heap, (char*) field, len); + + field = rec_get_nth_field_old(rec, 5, &len); + mtype = mach_read_from_4(field); + + field = rec_get_nth_field_old(rec, 6, &len); + prtype = mach_read_from_4(field); + + if (dtype_is_non_binary_string_type(mtype, prtype) + && dtype_get_charset_coll(prtype) == 0) { + /* This is a non-binary string type, and the table + was created with < 4.1.2. Use the default charset. */ + + prtype = dtype_form_prtype(prtype, + data_mysql_default_charset_coll); + } + + field = rec_get_nth_field_old(rec, 7, &len); + col_len = mach_read_from_4(field); + + ut_a(0 == ut_strcmp("PREC", + dict_field_get_col( + dict_index_get_nth_field(sys_index, 8))->name)); + + field = rec_get_nth_field_old(rec, 8, &len); + prec = mach_read_from_4(field); + + dict_mem_table_add_col(table, name, mtype, prtype, col_len, + prec); + btr_pcur_move_to_next_user_rec(&pcur, &mtr); + } + + btr_pcur_close(&pcur); + mtr_commit(&mtr); +} + +/************************************************************************ +Report that an index field or index for a table has been delete marked. */ +static +void +dict_load_report_deleted_index( +/*===========================*/ + const char* name, /* in: table name */ + ulint field) /* in: index field, or ULINT_UNDEFINED */ +{ + fprintf(stderr, "InnoDB: Error: data dictionary entry" + " for table %s is corrupt!\n", name); + if (field != ULINT_UNDEFINED) { + fprintf(stderr, + "InnoDB: Index field %lu is delete marked.\n", field); + } else { + fputs("InnoDB: An index is delete marked.\n", stderr); + } +} + +/************************************************************************ +Loads definitions for index fields. */ +static +void +dict_load_fields( +/*=============*/ + dict_table_t* table, /* in: table */ + dict_index_t* index, /* in: index whose fields to load */ + mem_heap_t* heap) /* in: memory heap for temporary storage */ +{ + dict_table_t* sys_fields; + dict_index_t* sys_index; + btr_pcur_t pcur; + dtuple_t* tuple; + dfield_t* dfield; + ulint pos_and_prefix_len; + ulint prefix_len; + rec_t* rec; + byte* field; + ulint len; + byte* buf; + ulint i; + mtr_t mtr; + +#ifdef UNIV_SYNC_DEBUG + ut_ad(mutex_own(&(dict_sys->mutex))); +#endif /* UNIV_SYNC_DEBUG */ + + UT_NOT_USED(table); + + mtr_start(&mtr); + + sys_fields = dict_table_get_low("SYS_FIELDS"); + sys_index = UT_LIST_GET_FIRST(sys_fields->indexes); + ut_a(!sys_fields->comp); + + tuple = dtuple_create(heap, 1); + dfield = dtuple_get_nth_field(tuple, 0); + + buf = mem_heap_alloc(heap, 8); + mach_write_to_8(buf, index->id); + + dfield_set_data(dfield, buf, 8); + dict_index_copy_types(tuple, sys_index, 1); + + btr_pcur_open_on_user_rec(sys_index, tuple, PAGE_CUR_GE, + BTR_SEARCH_LEAF, &pcur, &mtr); + for (i = 0; i < index->n_fields; i++) { + + rec = btr_pcur_get_rec(&pcur); + + ut_a(btr_pcur_is_on_user_rec(&pcur, &mtr)); + if (rec_get_deleted_flag(rec, sys_fields->comp)) { + dict_load_report_deleted_index(table->name, i); + } + + field = rec_get_nth_field_old(rec, 0, &len); + ut_ad(len == 8); + ut_a(ut_memcmp(buf, field, len) == 0); + + field = rec_get_nth_field_old(rec, 1, &len); + ut_a(len == 4); + + /* The next field stores the field position in the index + and a possible column prefix length if the index field + does not contain the whole column. The storage format is + like this: if there is at least one prefix field in the index, + then the HIGH 2 bytes contain the field number (== i) and the + low 2 bytes the prefix length for the field. Otherwise the + field number (== i) is contained in the 2 LOW bytes. */ + + pos_and_prefix_len = mach_read_from_4(field); + + ut_a((pos_and_prefix_len & 0xFFFFUL) == i + || (pos_and_prefix_len & 0xFFFF0000UL) == (i << 16)); + + if ((i == 0 && pos_and_prefix_len > 0) + || (pos_and_prefix_len & 0xFFFF0000UL) > 0) { + + prefix_len = pos_and_prefix_len & 0xFFFFUL; + } else { + prefix_len = 0; + } + + ut_a(0 == ut_strcmp("COL_NAME", + dict_field_get_col( + dict_index_get_nth_field(sys_index, 4))->name)); + + field = rec_get_nth_field_old(rec, 4, &len); + + dict_mem_index_add_field(index, + mem_heap_strdupl(heap, (char*) field, len), 0, prefix_len); + + btr_pcur_move_to_next_user_rec(&pcur, &mtr); + } + + btr_pcur_close(&pcur); + mtr_commit(&mtr); +} + +/************************************************************************ +Loads definitions for table indexes. Adds them to the data dictionary +cache. */ +static +ibool +dict_load_indexes( +/*==============*/ + /* out: TRUE if ok, FALSE if corruption + of dictionary table */ + dict_table_t* table, /* in: table */ + mem_heap_t* heap) /* in: memory heap for temporary storage */ +{ + dict_table_t* sys_indexes; + dict_index_t* sys_index; + dict_index_t* index; + btr_pcur_t pcur; + dtuple_t* tuple; + dfield_t* dfield; + rec_t* rec; + byte* field; + ulint len; + ulint name_len; + char* name_buf; + ulint type; + ulint space; + ulint page_no; + ulint n_fields; + byte* buf; + ibool is_sys_table; + dulint id; + mtr_t mtr; + +#ifdef UNIV_SYNC_DEBUG + ut_ad(mutex_own(&(dict_sys->mutex))); +#endif /* UNIV_SYNC_DEBUG */ + + if ((ut_dulint_get_high(table->id) == 0) + && (ut_dulint_get_low(table->id) < DICT_HDR_FIRST_ID)) { + is_sys_table = TRUE; + } else { + is_sys_table = FALSE; + } + + mtr_start(&mtr); + + sys_indexes = dict_table_get_low("SYS_INDEXES"); + sys_index = UT_LIST_GET_FIRST(sys_indexes->indexes); + ut_a(!sys_indexes->comp); + + tuple = dtuple_create(heap, 1); + dfield = dtuple_get_nth_field(tuple, 0); + + buf = mem_heap_alloc(heap, 8); + mach_write_to_8(buf, table->id); + + dfield_set_data(dfield, buf, 8); + dict_index_copy_types(tuple, sys_index, 1); + + btr_pcur_open_on_user_rec(sys_index, tuple, PAGE_CUR_GE, + BTR_SEARCH_LEAF, &pcur, &mtr); + for (;;) { + if (!btr_pcur_is_on_user_rec(&pcur, &mtr)) { + + break; + } + + rec = btr_pcur_get_rec(&pcur); + + field = rec_get_nth_field_old(rec, 0, &len); + ut_ad(len == 8); + + if (ut_memcmp(buf, field, len) != 0) { + break; + } + + if (rec_get_deleted_flag(rec, table->comp)) { + dict_load_report_deleted_index(table->name, + ULINT_UNDEFINED); + + btr_pcur_close(&pcur); + mtr_commit(&mtr); + + return(FALSE); + } + + field = rec_get_nth_field_old(rec, 1, &len); + ut_ad(len == 8); + id = mach_read_from_8(field); + + ut_a(0 == ut_strcmp("NAME", + dict_field_get_col( + dict_index_get_nth_field(sys_index, 4))->name)); + + field = rec_get_nth_field_old(rec, 4, &name_len); + name_buf = mem_heap_strdupl(heap, (char*) field, name_len); + + field = rec_get_nth_field_old(rec, 5, &len); + n_fields = mach_read_from_4(field); + + field = rec_get_nth_field_old(rec, 6, &len); + type = mach_read_from_4(field); + + field = rec_get_nth_field_old(rec, 7, &len); + space = mach_read_from_4(field); + + ut_a(0 == ut_strcmp("PAGE_NO", + dict_field_get_col( + dict_index_get_nth_field(sys_index, 8))->name)); + + field = rec_get_nth_field_old(rec, 8, &len); + page_no = mach_read_from_4(field); + + if (page_no == FIL_NULL) { + + fprintf(stderr, + "InnoDB: Error: trying to load index %s for table %s\n" + "InnoDB: but the index tree has been freed!\n", + name_buf, table->name); + + btr_pcur_close(&pcur); + mtr_commit(&mtr); + + return(FALSE); + } + + if ((type & DICT_CLUSTERED) == 0 + && NULL == dict_table_get_first_index(table)) { + + fprintf(stderr, + "InnoDB: Error: trying to load index %s for table %s\n" + "InnoDB: but the first index is not clustered!\n", + name_buf, table->name); + + btr_pcur_close(&pcur); + mtr_commit(&mtr); + + return(FALSE); + } + + if (is_sys_table + && ((type & DICT_CLUSTERED) + || ((table == dict_sys->sys_tables) + && (name_len == (sizeof "ID_IND") - 1) + && (0 == ut_memcmp(name_buf, "ID_IND", + name_len))))) { + + /* The index was created in memory already at booting + of the database server */ + } else { + index = dict_mem_index_create(table->name, name_buf, + space, type, n_fields); + index->id = id; + + dict_load_fields(table, index, heap); + dict_index_add_to_cache(table, index, page_no); + } + + btr_pcur_move_to_next_user_rec(&pcur, &mtr); + } + + btr_pcur_close(&pcur); + mtr_commit(&mtr); + + return(TRUE); +} + +/************************************************************************ +Loads a table definition and also all its index definitions, and also +the cluster definition if the table is a member in a cluster. Also loads +all foreign key constraints where the foreign key is in the table or where +a foreign key references columns in this table. Adds all these to the data +dictionary cache. */ + +dict_table_t* +dict_load_table( +/*============*/ + /* out: table, NULL if does not exist; + if the table is stored in an .ibd file, + but the file does not exist, + then we set the ibd_file_missing flag TRUE + in the table object we return */ + const char* name) /* in: table name in the + databasename/tablename format */ +{ + ibool ibd_file_missing = FALSE; + dict_table_t* table; + dict_table_t* sys_tables; + btr_pcur_t pcur; + dict_index_t* sys_index; + dtuple_t* tuple; + mem_heap_t* heap; + dfield_t* dfield; + rec_t* rec; + byte* field; + ulint len; + ulint space; + ulint n_cols; + ulint err; + mtr_t mtr; + +#ifdef UNIV_SYNC_DEBUG + ut_ad(mutex_own(&(dict_sys->mutex))); +#endif /* UNIV_SYNC_DEBUG */ + + heap = mem_heap_create(1000); + + mtr_start(&mtr); + + sys_tables = dict_table_get_low("SYS_TABLES"); + sys_index = UT_LIST_GET_FIRST(sys_tables->indexes); + ut_a(!sys_tables->comp); + + tuple = dtuple_create(heap, 1); + dfield = dtuple_get_nth_field(tuple, 0); + + dfield_set_data(dfield, name, ut_strlen(name)); + dict_index_copy_types(tuple, sys_index, 1); + + btr_pcur_open_on_user_rec(sys_index, tuple, PAGE_CUR_GE, + BTR_SEARCH_LEAF, &pcur, &mtr); + rec = btr_pcur_get_rec(&pcur); + + if (!btr_pcur_is_on_user_rec(&pcur, &mtr) + || rec_get_deleted_flag(rec, sys_tables->comp)) { + /* Not found */ + + btr_pcur_close(&pcur); + mtr_commit(&mtr); + mem_heap_free(heap); + + return(NULL); + } + + field = rec_get_nth_field_old(rec, 0, &len); + + /* Check if the table name in record is the searched one */ + if (len != ut_strlen(name) || ut_memcmp(name, field, len) != 0) { + btr_pcur_close(&pcur); + mtr_commit(&mtr); + mem_heap_free(heap); + + return(NULL); + } + + ut_a(0 == ut_strcmp("SPACE", + dict_field_get_col( + dict_index_get_nth_field(sys_index, 9))->name)); + + field = rec_get_nth_field_old(rec, 9, &len); + space = mach_read_from_4(field); + + /* Check if the tablespace exists and has the right name */ + if (space != 0) { + if (fil_space_for_table_exists_in_mem(space, name, FALSE, + FALSE, FALSE)) { + /* Ok; (if we did a crash recovery then the tablespace + can already be in the memory cache) */ + } else { + /* In >= 4.1.9, InnoDB scans the data dictionary also + at a normal mysqld startup. It is an error if the + space object does not exist in memory. */ + + ut_print_timestamp(stderr); + fprintf(stderr, +" InnoDB: error: space object of table %s,\n" +"InnoDB: space id %lu did not exist in memory. Retrying an open.\n", + name, (ulong)space); + /* Try to open the tablespace */ + if (!fil_open_single_table_tablespace(TRUE, + space, name)) { + /* We failed to find a sensible tablespace + file */ + + ibd_file_missing = TRUE; + } + } + } + + ut_a(0 == ut_strcmp("N_COLS", + dict_field_get_col( + dict_index_get_nth_field(sys_index, 4))->name)); + + field = rec_get_nth_field_old(rec, 4, &len); + n_cols = mach_read_from_4(field); + + /* The high-order bit of N_COLS is the "compact format" flag. */ + table = dict_mem_table_create(name, space, + n_cols & ~0x80000000UL, + !!(n_cols & 0x80000000UL)); + + table->ibd_file_missing = ibd_file_missing; + + ut_a(0 == ut_strcmp("ID", + dict_field_get_col( + dict_index_get_nth_field(sys_index, 3))->name)); + + field = rec_get_nth_field_old(rec, 3, &len); + table->id = mach_read_from_8(field); + + field = rec_get_nth_field_old(rec, 5, &len); + table->type = mach_read_from_4(field); + + if (table->type == DICT_TABLE_CLUSTER_MEMBER) { + ut_error; +#if 0 /* clustered tables have not been implemented yet */ + field = rec_get_nth_field_old(rec, 6, &len); + table->mix_id = mach_read_from_8(field); + + field = rec_get_nth_field_old(rec, 8, &len); + table->cluster_name = mem_heap_strdupl(heap, (char*) field, len); +#endif + } + + if ((table->type == DICT_TABLE_CLUSTER) + || (table->type == DICT_TABLE_CLUSTER_MEMBER)) { + + field = rec_get_nth_field_old(rec, 7, &len); + ut_a(len == 4); + table->mix_len = mach_read_from_4(field); + } + + btr_pcur_close(&pcur); + mtr_commit(&mtr); + + if (table->type == DICT_TABLE_CLUSTER_MEMBER) { + /* Load the cluster table definition if not yet in + memory cache */ + dict_table_get_low(table->cluster_name); + } + + dict_load_columns(table, heap); + + dict_table_add_to_cache(table); + + dict_load_indexes(table, heap); + + err = dict_load_foreigns(table->name, TRUE); +/* + if (err != DB_SUCCESS) { + + mutex_enter(&dict_foreign_err_mutex); + + ut_print_timestamp(stderr); + + fprintf(stderr, +" InnoDB: Error: could not make a foreign key definition to match\n" +"InnoDB: the foreign key table or the referenced table!\n" +"InnoDB: The data dictionary of InnoDB is corrupt. You may need to drop\n" +"InnoDB: and recreate the foreign key table or the referenced table.\n" +"InnoDB: Submit a detailed bug report to http://bugs.mysql.com\n" +"InnoDB: Latest foreign key error printout:\n%s\n", dict_foreign_err_buf); + + mutex_exit(&dict_foreign_err_mutex); + } +*/ + mem_heap_free(heap); + + return(table); +} + +/*************************************************************************** +Loads a table object based on the table id. */ + +dict_table_t* +dict_load_table_on_id( +/*==================*/ + /* out: table; NULL if table does not exist */ + dulint table_id) /* in: table id */ +{ + byte id_buf[8]; + btr_pcur_t pcur; + mem_heap_t* heap; + dtuple_t* tuple; + dfield_t* dfield; + dict_index_t* sys_table_ids; + dict_table_t* sys_tables; + rec_t* rec; + byte* field; + ulint len; + dict_table_t* table; + mtr_t mtr; + +#ifdef UNIV_SYNC_DEBUG + ut_ad(mutex_own(&(dict_sys->mutex))); +#endif /* UNIV_SYNC_DEBUG */ + + /* NOTE that the operation of this function is protected by + the dictionary mutex, and therefore no deadlocks can occur + with other dictionary operations. */ + + mtr_start(&mtr); + /*---------------------------------------------------*/ + /* Get the secondary index based on ID for table SYS_TABLES */ + sys_tables = dict_sys->sys_tables; + sys_table_ids = dict_table_get_next_index( + dict_table_get_first_index(sys_tables)); + ut_a(!sys_tables->comp); + heap = mem_heap_create(256); + + tuple = dtuple_create(heap, 1); + dfield = dtuple_get_nth_field(tuple, 0); + + /* Write the table id in byte format to id_buf */ + mach_write_to_8(id_buf, table_id); + + dfield_set_data(dfield, id_buf, 8); + dict_index_copy_types(tuple, sys_table_ids, 1); + + btr_pcur_open_on_user_rec(sys_table_ids, tuple, PAGE_CUR_GE, + BTR_SEARCH_LEAF, &pcur, &mtr); + rec = btr_pcur_get_rec(&pcur); + + if (!btr_pcur_is_on_user_rec(&pcur, &mtr) + || rec_get_deleted_flag(rec, sys_tables->comp)) { + /* Not found */ + + btr_pcur_close(&pcur); + mtr_commit(&mtr); + mem_heap_free(heap); + + return(NULL); + } + + /*---------------------------------------------------*/ + /* Now we have the record in the secondary index containing the + table ID and NAME */ + + rec = btr_pcur_get_rec(&pcur); + field = rec_get_nth_field_old(rec, 0, &len); + ut_ad(len == 8); + + /* Check if the table id in record is the one searched for */ + if (ut_dulint_cmp(table_id, mach_read_from_8(field)) != 0) { + + btr_pcur_close(&pcur); + mtr_commit(&mtr); + mem_heap_free(heap); + + return(NULL); + } + + /* Now we get the table name from the record */ + field = rec_get_nth_field_old(rec, 1, &len); + /* Load the table definition to memory */ + table = dict_load_table(mem_heap_strdupl(heap, (char*) field, len)); + + btr_pcur_close(&pcur); + mtr_commit(&mtr); + mem_heap_free(heap); + + return(table); +} + +/************************************************************************ +This function is called when the database is booted. Loads system table +index definitions except for the clustered index which is added to the +dictionary cache at booting before calling this function. */ + +void +dict_load_sys_table( +/*================*/ + dict_table_t* table) /* in: system table */ +{ + mem_heap_t* heap; + +#ifdef UNIV_SYNC_DEBUG + ut_ad(mutex_own(&(dict_sys->mutex))); +#endif /* UNIV_SYNC_DEBUG */ + + heap = mem_heap_create(1000); + + dict_load_indexes(table, heap); + + mem_heap_free(heap); +} + +/************************************************************************ +Loads foreign key constraint col names (also for the referenced table). */ +static +void +dict_load_foreign_cols( +/*===================*/ + const char* id, /* in: foreign constraint id as a null- + terminated string */ + dict_foreign_t* foreign)/* in: foreign constraint object */ +{ + dict_table_t* sys_foreign_cols; + dict_index_t* sys_index; + btr_pcur_t pcur; + dtuple_t* tuple; + dfield_t* dfield; + rec_t* rec; + byte* field; + ulint len; + ulint i; + mtr_t mtr; + +#ifdef UNIV_SYNC_DEBUG + ut_ad(mutex_own(&(dict_sys->mutex))); +#endif /* UNIV_SYNC_DEBUG */ + + foreign->foreign_col_names = mem_heap_alloc(foreign->heap, + foreign->n_fields * sizeof(void*)); + + foreign->referenced_col_names = mem_heap_alloc(foreign->heap, + foreign->n_fields * sizeof(void*)); + mtr_start(&mtr); + + sys_foreign_cols = dict_table_get_low("SYS_FOREIGN_COLS"); + sys_index = UT_LIST_GET_FIRST(sys_foreign_cols->indexes); + ut_a(!sys_foreign_cols->comp); + + tuple = dtuple_create(foreign->heap, 1); + dfield = dtuple_get_nth_field(tuple, 0); + + dfield_set_data(dfield, id, ut_strlen(id)); + dict_index_copy_types(tuple, sys_index, 1); + + btr_pcur_open_on_user_rec(sys_index, tuple, PAGE_CUR_GE, + BTR_SEARCH_LEAF, &pcur, &mtr); + for (i = 0; i < foreign->n_fields; i++) { + + rec = btr_pcur_get_rec(&pcur); + + ut_a(btr_pcur_is_on_user_rec(&pcur, &mtr)); + ut_a(!rec_get_deleted_flag(rec, sys_foreign_cols->comp)); + + field = rec_get_nth_field_old(rec, 0, &len); + ut_a(len == ut_strlen(id)); + ut_a(ut_memcmp(id, field, len) == 0); + + field = rec_get_nth_field_old(rec, 1, &len); + ut_a(len == 4); + ut_a(i == mach_read_from_4(field)); + + field = rec_get_nth_field_old(rec, 4, &len); + foreign->foreign_col_names[i] = + mem_heap_strdupl(foreign->heap, (char*) field, len); + + field = rec_get_nth_field_old(rec, 5, &len); + foreign->referenced_col_names[i] = + mem_heap_strdupl(foreign->heap, (char*) field, len); + + btr_pcur_move_to_next_user_rec(&pcur, &mtr); + } + + btr_pcur_close(&pcur); + mtr_commit(&mtr); +} + +/*************************************************************************** +Loads a foreign key constraint to the dictionary cache. */ +static +ulint +dict_load_foreign( +/*==============*/ + /* out: DB_SUCCESS or error code */ + const char* id, /* in: foreign constraint id as a + null-terminated string */ + ibool check_types)/* in: TRUE=check type compatibility */ +{ + dict_foreign_t* foreign; + dict_table_t* sys_foreign; + btr_pcur_t pcur; + dict_index_t* sys_index; + dtuple_t* tuple; + mem_heap_t* heap2; + dfield_t* dfield; + rec_t* rec; + byte* field; + ulint len; + mtr_t mtr; + +#ifdef UNIV_SYNC_DEBUG + ut_ad(mutex_own(&(dict_sys->mutex))); +#endif /* UNIV_SYNC_DEBUG */ + + heap2 = mem_heap_create(1000); + + mtr_start(&mtr); + + sys_foreign = dict_table_get_low("SYS_FOREIGN"); + sys_index = UT_LIST_GET_FIRST(sys_foreign->indexes); + ut_a(!sys_foreign->comp); + + tuple = dtuple_create(heap2, 1); + dfield = dtuple_get_nth_field(tuple, 0); + + dfield_set_data(dfield, id, ut_strlen(id)); + dict_index_copy_types(tuple, sys_index, 1); + + btr_pcur_open_on_user_rec(sys_index, tuple, PAGE_CUR_GE, + BTR_SEARCH_LEAF, &pcur, &mtr); + rec = btr_pcur_get_rec(&pcur); + + if (!btr_pcur_is_on_user_rec(&pcur, &mtr) + || rec_get_deleted_flag(rec, sys_foreign->comp)) { + /* Not found */ + + fprintf(stderr, + "InnoDB: Error A: cannot load foreign constraint %s\n", + id); + + btr_pcur_close(&pcur); + mtr_commit(&mtr); + mem_heap_free(heap2); + + return(DB_ERROR); + } + + field = rec_get_nth_field_old(rec, 0, &len); + + /* Check if the id in record is the searched one */ + if (len != ut_strlen(id) || ut_memcmp(id, field, len) != 0) { + + fprintf(stderr, + "InnoDB: Error B: cannot load foreign constraint %s\n", + id); + + btr_pcur_close(&pcur); + mtr_commit(&mtr); + mem_heap_free(heap2); + + return(DB_ERROR); + } + + /* Read the table names and the number of columns associated + with the constraint */ + + mem_heap_free(heap2); + + foreign = dict_mem_foreign_create(); + + foreign->n_fields = + mach_read_from_4(rec_get_nth_field_old(rec, 5, &len)); + + ut_a(len == 4); + + /* We store the type to the bits 24-31 of n_fields */ + + foreign->type = foreign->n_fields >> 24; + foreign->n_fields = foreign->n_fields & 0xFFFFFFUL; + + foreign->id = mem_heap_strdup(foreign->heap, id); + + field = rec_get_nth_field_old(rec, 3, &len); + foreign->foreign_table_name = + mem_heap_strdupl(foreign->heap, (char*) field, len); + + field = rec_get_nth_field_old(rec, 4, &len); + foreign->referenced_table_name = + mem_heap_strdupl(foreign->heap, (char*) field, len); + + btr_pcur_close(&pcur); + mtr_commit(&mtr); + + dict_load_foreign_cols(id, foreign); + + /* If the foreign table is not yet in the dictionary cache, we + have to load it so that we are able to make type comparisons + in the next function call. */ + + dict_table_get_low(foreign->foreign_table_name); + + /* Note that there may already be a foreign constraint object in + the dictionary cache for this constraint: then the following + call only sets the pointers in it to point to the appropriate table + and index objects and frees the newly created object foreign. + Adding to the cache should always succeed since we are not creating + a new foreign key constraint but loading one from the data + dictionary. */ + + return(dict_foreign_add_to_cache(foreign, check_types)); +} + +/*************************************************************************** +Loads foreign key constraints where the table is either the foreign key +holder or where the table is referenced by a foreign key. Adds these +constraints to the data dictionary. Note that we know that the dictionary +cache already contains all constraints where the other relevant table is +already in the dictionary cache. */ + +ulint +dict_load_foreigns( +/*===============*/ + /* out: DB_SUCCESS or error code */ + const char* table_name, /* in: table name */ + ibool check_types) /* in: TRUE=check type compatibility */ +{ + btr_pcur_t pcur; + mem_heap_t* heap; + dtuple_t* tuple; + dfield_t* dfield; + dict_index_t* sec_index; + dict_table_t* sys_foreign; + rec_t* rec; + byte* field; + ulint len; + char* id ; + ulint err; + mtr_t mtr; + +#ifdef UNIV_SYNC_DEBUG + ut_ad(mutex_own(&(dict_sys->mutex))); +#endif /* UNIV_SYNC_DEBUG */ + + sys_foreign = dict_table_get_low("SYS_FOREIGN"); + + if (sys_foreign == NULL) { + /* No foreign keys defined yet in this database */ + + fprintf(stderr, + "InnoDB: Error: no foreign key system tables in the database\n"); + + return(DB_ERROR); + } + + ut_a(!sys_foreign->comp); + mtr_start(&mtr); + + /* Get the secondary index based on FOR_NAME from table + SYS_FOREIGN */ + + sec_index = dict_table_get_next_index( + dict_table_get_first_index(sys_foreign)); +start_load: + heap = mem_heap_create(256); + + tuple = dtuple_create(heap, 1); + dfield = dtuple_get_nth_field(tuple, 0); + + dfield_set_data(dfield, table_name, ut_strlen(table_name)); + dict_index_copy_types(tuple, sec_index, 1); + + btr_pcur_open_on_user_rec(sec_index, tuple, PAGE_CUR_GE, + BTR_SEARCH_LEAF, &pcur, &mtr); +loop: + rec = btr_pcur_get_rec(&pcur); + + if (!btr_pcur_is_on_user_rec(&pcur, &mtr)) { + /* End of index */ + + goto load_next_index; + } + + /* Now we have the record in the secondary index containing a table + name and a foreign constraint ID */ + + rec = btr_pcur_get_rec(&pcur); + field = rec_get_nth_field_old(rec, 0, &len); + + /* Check if the table name in the record is the one searched for; the + following call does the comparison in the latin1_swedish_ci + charset-collation, in a case-insensitive way. */ + + if (0 != cmp_data_data(dfield_get_type(dfield), + dfield_get_data(dfield), dfield_get_len(dfield), + field, len)) { + + goto load_next_index; + } + + /* Since table names in SYS_FOREIGN are stored in a case-insensitive + order, we have to check that the table name matches also in a binary + string comparison. On Unix, MySQL allows table names that only differ + in character case. */ + + if (0 != ut_memcmp(field, table_name, len)) { + + goto next_rec; + } + + if (rec_get_deleted_flag(rec, sys_foreign->comp)) { + + goto next_rec; + } + + /* Now we get a foreign key constraint id */ + field = rec_get_nth_field_old(rec, 1, &len); + id = mem_heap_strdupl(heap, (char*) field, len); + + btr_pcur_store_position(&pcur, &mtr); + + mtr_commit(&mtr); + + /* Load the foreign constraint definition to the dictionary cache */ + + err = dict_load_foreign(id, check_types); + + if (err != DB_SUCCESS) { + btr_pcur_close(&pcur); + mem_heap_free(heap); + + return(err); + } + + mtr_start(&mtr); + + btr_pcur_restore_position(BTR_SEARCH_LEAF, &pcur, &mtr); +next_rec: + btr_pcur_move_to_next_user_rec(&pcur, &mtr); + + goto loop; + +load_next_index: + btr_pcur_close(&pcur); + mtr_commit(&mtr); + mem_heap_free(heap); + + sec_index = dict_table_get_next_index(sec_index); + + if (sec_index != NULL) { + + mtr_start(&mtr); + + goto start_load; + } + + return(DB_SUCCESS); +} diff --git a/storage/innobase/dict/dict0mem.c b/storage/innobase/dict/dict0mem.c new file mode 100644 index 00000000000..48b9f28d292 --- /dev/null +++ b/storage/innobase/dict/dict0mem.c @@ -0,0 +1,293 @@ +/********************************************************************** +Data dictionary memory object creation + +(c) 1996 Innobase Oy + +Created 1/8/1996 Heikki Tuuri +***********************************************************************/ + +#include "dict0mem.h" + +#ifdef UNIV_NONINL +#include "dict0mem.ic" +#endif + +#include "rem0rec.h" +#include "data0type.h" +#include "mach0data.h" +#include "dict0dict.h" +#include "que0que.h" +#include "pars0pars.h" +#include "lock0lock.h" + +#define DICT_HEAP_SIZE 100 /* initial memory heap size when + creating a table or index object */ + +/************************************************************************** +Creates a table memory object. */ + +dict_table_t* +dict_mem_table_create( +/*==================*/ + /* out, own: table object */ + const char* name, /* in: table name */ + ulint space, /* in: space where the clustered index of + the table is placed; this parameter is + ignored if the table is made a member of + a cluster */ + ulint n_cols, /* in: number of columns */ + ibool comp) /* in: TRUE=compact page format */ +{ + dict_table_t* table; + mem_heap_t* heap; + + ut_ad(name); + + heap = mem_heap_create(DICT_HEAP_SIZE); + + table = mem_heap_alloc(heap, sizeof(dict_table_t)); + + table->heap = heap; + + table->type = DICT_TABLE_ORDINARY; + table->name = mem_heap_strdup(heap, name); + table->dir_path_of_temp_table = NULL; + table->space = space; + table->ibd_file_missing = FALSE; + table->tablespace_discarded = FALSE; + table->comp = comp; + table->n_def = 0; + table->n_cols = n_cols + DATA_N_SYS_COLS; + table->mem_fix = 0; + + table->n_mysql_handles_opened = 0; + table->n_foreign_key_checks_running = 0; + + table->cached = FALSE; + + table->mix_id = ut_dulint_zero; + table->mix_len = 0; + + table->cols = mem_heap_alloc(heap, (n_cols + DATA_N_SYS_COLS) + * sizeof(dict_col_t)); + UT_LIST_INIT(table->indexes); + + table->auto_inc_lock = mem_heap_alloc(heap, lock_get_size()); + + table->query_cache_inv_trx_id = ut_dulint_zero; + + UT_LIST_INIT(table->locks); + UT_LIST_INIT(table->foreign_list); + UT_LIST_INIT(table->referenced_list); + + table->does_not_fit_in_memory = FALSE; + + table->stat_initialized = FALSE; + + table->stat_modified_counter = 0; + + mutex_create(&(table->autoinc_mutex)); + mutex_set_level(&(table->autoinc_mutex), SYNC_DICT_AUTOINC_MUTEX); + + table->autoinc_inited = FALSE; + + table->magic_n = DICT_TABLE_MAGIC_N; + + return(table); +} + +/************************************************************************** +Creates a cluster memory object. */ + +dict_table_t* +dict_mem_cluster_create( +/*====================*/ + /* out, own: cluster object */ + const char* name, /* in: cluster name */ + ulint space, /* in: space where the clustered indexes + of the member tables are placed */ + ulint n_cols, /* in: number of columns */ + ulint mix_len)/* in: length of the common key prefix in the + cluster */ +{ + dict_table_t* cluster; + + /* Clustered tables cannot work with the compact record format. */ + cluster = dict_mem_table_create(name, space, n_cols, FALSE); + + cluster->type = DICT_TABLE_CLUSTER; + cluster->mix_len = mix_len; + + return(cluster); +} + +/************************************************************************** +Declares a non-published table as a member in a cluster. */ + +void +dict_mem_table_make_cluster_member( +/*===============================*/ + dict_table_t* table, /* in: non-published table */ + const char* cluster_name) /* in: cluster name */ +{ + table->type = DICT_TABLE_CLUSTER_MEMBER; + table->cluster_name = cluster_name; +} + +/************************************************************************** +Adds a column definition to a table. */ + +void +dict_mem_table_add_col( +/*===================*/ + dict_table_t* table, /* in: table */ + const char* name, /* in: column name */ + ulint mtype, /* in: main datatype */ + ulint prtype, /* in: precise type */ + ulint len, /* in: length */ + ulint prec) /* in: precision */ +{ + dict_col_t* col; + dtype_t* type; + + ut_ad(table && name); + ut_ad(table->magic_n == DICT_TABLE_MAGIC_N); + + table->n_def++; + + col = dict_table_get_nth_col(table, table->n_def - 1); + + col->ind = table->n_def - 1; + col->name = mem_heap_strdup(table->heap, name); + col->table = table; + col->ord_part = 0; + + col->clust_pos = ULINT_UNDEFINED; + + type = dict_col_get_type(col); + + dtype_set(type, mtype, prtype, len, prec); +} + +/************************************************************************** +Creates an index memory object. */ + +dict_index_t* +dict_mem_index_create( +/*==================*/ + /* out, own: index object */ + const char* table_name, /* in: table name */ + const char* index_name, /* in: index name */ + ulint space, /* in: space where the index tree is + placed, ignored if the index is of + the clustered type */ + ulint type, /* in: DICT_UNIQUE, + DICT_CLUSTERED, ... ORed */ + ulint n_fields) /* in: number of fields */ +{ + dict_index_t* index; + mem_heap_t* heap; + + ut_ad(table_name && index_name); + + heap = mem_heap_create(DICT_HEAP_SIZE); + index = mem_heap_alloc(heap, sizeof(dict_index_t)); + + index->heap = heap; + + index->type = type; + index->space = space; + index->name = mem_heap_strdup(heap, index_name); + index->table_name = table_name; + index->table = NULL; + index->n_def = index->n_nullable = 0; + index->n_fields = n_fields; + index->fields = mem_heap_alloc(heap, 1 + n_fields + * sizeof(dict_field_t)); + /* The '1 +' above prevents allocation + of an empty mem block */ + index->stat_n_diff_key_vals = NULL; + + index->cached = FALSE; + index->magic_n = DICT_INDEX_MAGIC_N; + + return(index); +} + +/************************************************************************** +Creates and initializes a foreign constraint memory object. */ + +dict_foreign_t* +dict_mem_foreign_create(void) +/*=========================*/ + /* out, own: foreign constraint struct */ +{ + dict_foreign_t* foreign; + mem_heap_t* heap; + + heap = mem_heap_create(100); + + foreign = mem_heap_alloc(heap, sizeof(dict_foreign_t)); + + foreign->heap = heap; + + foreign->id = NULL; + + foreign->type = 0; + foreign->foreign_table_name = NULL; + foreign->foreign_table = NULL; + foreign->foreign_col_names = NULL; + + foreign->referenced_table_name = NULL; + foreign->referenced_table = NULL; + foreign->referenced_col_names = NULL; + + foreign->n_fields = 0; + + foreign->foreign_index = NULL; + foreign->referenced_index = NULL; + + return(foreign); +} + +/************************************************************************** +Adds a field definition to an index. NOTE: does not take a copy +of the column name if the field is a column. The memory occupied +by the column name may be released only after publishing the index. */ + +void +dict_mem_index_add_field( +/*=====================*/ + dict_index_t* index, /* in: index */ + const char* name, /* in: column name */ + ulint order, /* in: order criterion; 0 means an + ascending order */ + ulint prefix_len) /* in: 0 or the column prefix length + in a MySQL index like + INDEX (textcol(25)) */ +{ + dict_field_t* field; + + ut_ad(index && name); + ut_ad(index->magic_n == DICT_INDEX_MAGIC_N); + + index->n_def++; + + field = dict_index_get_nth_field(index, index->n_def - 1); + + field->name = name; + field->order = order; + + field->prefix_len = prefix_len; +} + +/************************************************************************** +Frees an index memory object. */ + +void +dict_mem_index_free( +/*================*/ + dict_index_t* index) /* in: index */ +{ + mem_heap_free(index->heap); +} diff --git a/storage/innobase/dict/makefilewin b/storage/innobase/dict/makefilewin new file mode 100644 index 00000000000..e828d06943c --- /dev/null +++ b/storage/innobase/dict/makefilewin @@ -0,0 +1,21 @@ +include ..\include\makefile.i + +dict.lib: dict0dict.obj dict0boot.obj dict0load.obj dict0mem.obj dict0crea.obj + lib -out:..\libs\dict.lib dict0dict.obj dict0boot.obj dict0load.obj dict0mem.obj dict0crea.obj + +dict0dict.obj: dict0dict.c + $(CCOM) $(CFL) -c dict0dict.c + +dict0boot.obj: dict0boot.c + $(CCOM) $(CFL) -c dict0boot.c + +dict0mem.obj: dict0mem.c + $(CCOM) $(CFL) -c dict0mem.c + +dict0crea.obj: dict0crea.c + $(CCOM) $(CFL) -c dict0crea.c + +dict0load.obj: dict0load.c + $(CCOM) $(CFL) -c dict0load.c + + |