00001 /********************************************************************** 00002 Data dictionary system 00003 00004 (c) 1996 Innobase Oy 00005 00006 Created 1/8/1996 Heikki Tuuri 00007 ***********************************************************************/ 00008 00009 #include "dict0dict.h" 00010 00011 #ifdef UNIV_NONINL 00012 #include "dict0dict.ic" 00013 #endif 00014 00015 #include "buf0buf.h" 00016 #include "data0type.h" 00017 #include "mach0data.h" 00018 #include "dict0boot.h" 00019 #include "dict0mem.h" 00020 #include "dict0crea.h" 00021 #include "trx0undo.h" 00022 #include "btr0btr.h" 00023 #include "btr0cur.h" 00024 #include "btr0sea.h" 00025 #include "pars0pars.h" 00026 #include "pars0sym.h" 00027 #include "que0que.h" 00028 #include "rem0cmp.h" 00029 #ifndef UNIV_HOTBACKUP 00030 # include "m_ctype.h" /* my_isspace() */ 00031 #endif /* !UNIV_HOTBACKUP */ 00032 00033 dict_sys_t* dict_sys = NULL; /* the dictionary system */ 00034 00035 rw_lock_t dict_operation_lock; /* table create, drop, etc. reserve 00036 this in X-mode; implicit or backround 00037 operations purge, rollback, foreign 00038 key checks reserve this in S-mode; we 00039 cannot trust that MySQL protects 00040 implicit or background operations 00041 a table drop since MySQL does not 00042 know of them; therefore we need this; 00043 NOTE: a transaction which reserves 00044 this must keep book on the mode in 00045 trx->dict_operation_lock_mode */ 00046 00047 #define DICT_HEAP_SIZE 100 /* initial memory heap size when 00048 creating a table or index object */ 00049 #define DICT_POOL_PER_TABLE_HASH 512 /* buffer pool max size per table 00050 hash table fixed size in bytes */ 00051 #define DICT_POOL_PER_COL_HASH 128 /* buffer pool max size per column 00052 hash table fixed size in bytes */ 00053 #define DICT_POOL_PER_VARYING 4 /* buffer pool max size per data 00054 dictionary varying size in bytes */ 00055 00056 /* Identifies generated InnoDB foreign key names */ 00057 static char dict_ibfk[] = "_ibfk_"; 00058 00059 #ifndef UNIV_HOTBACKUP 00060 /********************************************************************** 00061 Converts an identifier to a table name. 00062 00063 NOTE: the prototype of this function is copied from ha_innodb.cc! If you change 00064 this function, you MUST change also the prototype here! */ 00065 extern 00066 void 00067 innobase_convert_from_table_id( 00068 /*===========================*/ 00069 char* to, /* out: converted identifier */ 00070 const char* from, /* in: identifier to convert */ 00071 ulint len); /* in: length of 'to', in bytes; 00072 should be at least 5 * strlen(to) + 1 */ 00073 /********************************************************************** 00074 Converts an identifier to UTF-8. 00075 00076 NOTE: the prototype of this function is copied from ha_innodb.cc! If you change 00077 this function, you MUST change also the prototype here! */ 00078 extern 00079 void 00080 innobase_convert_from_id( 00081 /*=====================*/ 00082 char* to, /* out: converted identifier */ 00083 const char* from, /* in: identifier to convert */ 00084 ulint len); /* in: length of 'to', in bytes; 00085 should be at least 3 * strlen(to) + 1 */ 00086 /********************************************************************** 00087 Removes the filename encoding of a table or database name. 00088 00089 NOTE: the prototype of this function is copied from ha_innodb.cc! If you change 00090 this function, you MUST change also the prototype here! */ 00091 extern 00092 void 00093 innobase_convert_from_filename( 00094 /*===========================*/ 00095 char* s); /* in: identifier; out: decoded identifier */ 00096 /********************************************************************** 00097 Compares NUL-terminated UTF-8 strings case insensitively. 00098 00099 NOTE: the prototype of this function is copied from ha_innodb.cc! If you change 00100 this function, you MUST change also the prototype here! */ 00101 extern 00102 int 00103 innobase_strcasecmp( 00104 /*================*/ 00105 /* out: 0 if a=b, <0 if a<b, >1 if a>b */ 00106 const char* a, /* in: first string to compare */ 00107 const char* b); /* in: second string to compare */ 00108 00109 /********************************************************************** 00110 Makes all characters in a NUL-terminated UTF-8 string lower case. 00111 00112 NOTE: the prototype of this function is copied from ha_innodb.cc! If you change 00113 this function, you MUST change also the prototype here! */ 00114 extern 00115 void 00116 innobase_casedn_str( 00117 /*================*/ 00118 char* a); /* in/out: string to put in lower case */ 00119 00120 /************************************************************************** 00121 Determines the connection character set. 00122 00123 NOTE: the prototype of this function is copied from ha_innodb.cc! If you change 00124 this function, you MUST change also the prototype here! */ 00125 struct charset_info_st* 00126 innobase_get_charset( 00127 /*=================*/ 00128 /* out: connection character set */ 00129 void* mysql_thd); /* in: MySQL thread handle */ 00130 #endif /* !UNIV_HOTBACKUP */ 00131 00132 /************************************************************************** 00133 Adds a column to the data dictionary hash table. */ 00134 static 00135 void 00136 dict_col_add_to_cache( 00137 /*==================*/ 00138 dict_table_t* table, /* in: table */ 00139 dict_col_t* col); /* in: column */ 00140 /************************************************************************** 00141 Repositions a column in the data dictionary hash table when the table name 00142 changes. */ 00143 static 00144 void 00145 dict_col_reposition_in_cache( 00146 /*=========================*/ 00147 dict_table_t* table, /* in: table */ 00148 dict_col_t* col, /* in: column */ 00149 const char* new_name); /* in: new table name */ 00150 /************************************************************************** 00151 Removes a column from the data dictionary hash table. */ 00152 static 00153 void 00154 dict_col_remove_from_cache( 00155 /*=======================*/ 00156 dict_table_t* table, /* in: table */ 00157 dict_col_t* col); /* in: column */ 00158 /************************************************************************** 00159 Removes an index from the dictionary cache. */ 00160 static 00161 void 00162 dict_index_remove_from_cache( 00163 /*=========================*/ 00164 dict_table_t* table, /* in: table */ 00165 dict_index_t* index); /* in, own: index */ 00166 /*********************************************************************** 00167 Copies fields contained in index2 to index1. */ 00168 static 00169 void 00170 dict_index_copy( 00171 /*============*/ 00172 dict_index_t* index1, /* in: index to copy to */ 00173 dict_index_t* index2, /* in: index to copy from */ 00174 ulint start, /* in: first position to copy */ 00175 ulint end); /* in: last position to copy */ 00176 /*********************************************************************** 00177 Tries to find column names for the index in the column hash table and 00178 sets the col field of the index. */ 00179 static 00180 ibool 00181 dict_index_find_cols( 00182 /*=================*/ 00183 /* out: TRUE if success */ 00184 dict_table_t* table, /* in: table */ 00185 dict_index_t* index); /* in: index */ 00186 /*********************************************************************** 00187 Builds the internal dictionary cache representation for a clustered 00188 index, containing also system fields not defined by the user. */ 00189 static 00190 dict_index_t* 00191 dict_index_build_internal_clust( 00192 /*============================*/ 00193 /* out, own: the internal representation 00194 of the clustered index */ 00195 dict_table_t* table, /* in: table */ 00196 dict_index_t* index); /* in: user representation of a clustered 00197 index */ 00198 /*********************************************************************** 00199 Builds the internal dictionary cache representation for a non-clustered 00200 index, containing also system fields not defined by the user. */ 00201 static 00202 dict_index_t* 00203 dict_index_build_internal_non_clust( 00204 /*================================*/ 00205 /* out, own: the internal representation 00206 of the non-clustered index */ 00207 dict_table_t* table, /* in: table */ 00208 dict_index_t* index); /* in: user representation of a non-clustered 00209 index */ 00210 /************************************************************************** 00211 Removes a foreign constraint struct from the dictionary cache. */ 00212 static 00213 void 00214 dict_foreign_remove_from_cache( 00215 /*===========================*/ 00216 dict_foreign_t* foreign); /* in, own: foreign constraint */ 00217 /************************************************************************** 00218 Prints a column data. */ 00219 static 00220 void 00221 dict_col_print_low( 00222 /*===============*/ 00223 dict_col_t* col); /* in: column */ 00224 /************************************************************************** 00225 Prints an index data. */ 00226 static 00227 void 00228 dict_index_print_low( 00229 /*=================*/ 00230 dict_index_t* index); /* in: index */ 00231 /************************************************************************** 00232 Prints a field data. */ 00233 static 00234 void 00235 dict_field_print_low( 00236 /*=================*/ 00237 dict_field_t* field); /* in: field */ 00238 /************************************************************************* 00239 Frees a foreign key struct. */ 00240 static 00241 void 00242 dict_foreign_free( 00243 /*==============*/ 00244 dict_foreign_t* foreign); /* in, own: foreign key struct */ 00245 00246 /* Stream for storing detailed information about the latest foreign key 00247 and unique key errors */ 00248 FILE* dict_foreign_err_file = NULL; 00249 mutex_t dict_foreign_err_mutex; /* mutex protecting the foreign 00250 and unique error buffers */ 00251 00252 #ifndef UNIV_HOTBACKUP 00253 /********************************************************************** 00254 Makes all characters in a NUL-terminated UTF-8 string lower case. */ 00255 00256 void 00257 dict_casedn_str( 00258 /*============*/ 00259 char* a) /* in/out: string to put in lower case */ 00260 { 00261 innobase_casedn_str(a); 00262 } 00263 #endif /* !UNIV_HOTBACKUP */ 00264 00265 /************************************************************************ 00266 Checks if the database name in two table names is the same. */ 00267 00268 ibool 00269 dict_tables_have_same_db( 00270 /*=====================*/ 00271 /* out: TRUE if same db name */ 00272 const char* name1, /* in: table name in the form 00273 dbname '/' tablename */ 00274 const char* name2) /* in: table name in the form 00275 dbname '/' tablename */ 00276 { 00277 for (; *name1 == *name2; name1++, name2++) { 00278 if (*name1 == '/') { 00279 return(TRUE); 00280 } 00281 ut_a(*name1); /* the names must contain '/' */ 00282 } 00283 return(FALSE); 00284 } 00285 00286 /************************************************************************ 00287 Return the end of table name where we have removed dbname and '/'. */ 00288 00289 const char* 00290 dict_remove_db_name( 00291 /*================*/ 00292 /* out: table name */ 00293 const char* name) /* in: table name in the form 00294 dbname '/' tablename */ 00295 { 00296 const char* s = strchr(name, '/'); 00297 ut_a(s); 00298 00299 return(s + 1); 00300 } 00301 00302 /************************************************************************ 00303 Get the database name length in a table name. */ 00304 00305 ulint 00306 dict_get_db_name_len( 00307 /*=================*/ 00308 /* out: database name length */ 00309 const char* name) /* in: table name in the form 00310 dbname '/' tablename */ 00311 { 00312 const char* s; 00313 s = strchr(name, '/'); 00314 ut_a(s); 00315 return(s - name); 00316 } 00317 00318 /************************************************************************ 00319 Reserves the dictionary system mutex for MySQL. */ 00320 00321 void 00322 dict_mutex_enter_for_mysql(void) 00323 /*============================*/ 00324 { 00325 mutex_enter(&(dict_sys->mutex)); 00326 } 00327 00328 /************************************************************************ 00329 Releases the dictionary system mutex for MySQL. */ 00330 00331 void 00332 dict_mutex_exit_for_mysql(void) 00333 /*===========================*/ 00334 { 00335 mutex_exit(&(dict_sys->mutex)); 00336 } 00337 00338 /************************************************************************ 00339 Decrements the count of open MySQL handles to a table. */ 00340 00341 void 00342 dict_table_decrement_handle_count( 00343 /*==============================*/ 00344 dict_table_t* table) /* in: table */ 00345 { 00346 mutex_enter(&(dict_sys->mutex)); 00347 00348 ut_a(table->n_mysql_handles_opened > 0); 00349 00350 table->n_mysql_handles_opened--; 00351 00352 mutex_exit(&(dict_sys->mutex)); 00353 } 00354 00355 /************************************************************************ 00356 Gets the nth column of a table. */ 00357 00358 dict_col_t* 00359 dict_table_get_nth_col_noninline( 00360 /*=============================*/ 00361 /* out: pointer to column object */ 00362 dict_table_t* table, /* in: table */ 00363 ulint pos) /* in: position of column */ 00364 { 00365 return(dict_table_get_nth_col(table, pos)); 00366 } 00367 00368 /************************************************************************ 00369 Gets the first index on the table (the clustered index). */ 00370 00371 dict_index_t* 00372 dict_table_get_first_index_noninline( 00373 /*=================================*/ 00374 /* out: index, NULL if none exists */ 00375 dict_table_t* table) /* in: table */ 00376 { 00377 return(dict_table_get_first_index(table)); 00378 } 00379 00380 /************************************************************************ 00381 Gets the next index on the table. */ 00382 00383 dict_index_t* 00384 dict_table_get_next_index_noninline( 00385 /*================================*/ 00386 /* out: index, NULL if none left */ 00387 dict_index_t* index) /* in: index */ 00388 { 00389 return(dict_table_get_next_index(index)); 00390 } 00391 00392 /************************************************************************** 00393 Returns an index object. */ 00394 00395 dict_index_t* 00396 dict_table_get_index_noninline( 00397 /*===========================*/ 00398 /* out: index, NULL if does not exist */ 00399 dict_table_t* table, /* in: table */ 00400 const char* name) /* in: index name */ 00401 { 00402 return(dict_table_get_index(table, name)); 00403 } 00404 00405 /************************************************************************ 00406 Initializes the autoinc counter. It is not an error to initialize an already 00407 initialized counter. */ 00408 00409 void 00410 dict_table_autoinc_initialize( 00411 /*==========================*/ 00412 dict_table_t* table, /* in: table */ 00413 ib_longlong value) /* in: next value to assign to a row */ 00414 { 00415 mutex_enter(&(table->autoinc_mutex)); 00416 00417 table->autoinc_inited = TRUE; 00418 table->autoinc = value; 00419 00420 mutex_exit(&(table->autoinc_mutex)); 00421 } 00422 00423 /************************************************************************ 00424 Gets the next autoinc value (== autoinc counter value), 0 if not yet 00425 initialized. If initialized, increments the counter by 1. */ 00426 00427 ib_longlong 00428 dict_table_autoinc_get( 00429 /*===================*/ 00430 /* out: value for a new row, or 0 */ 00431 dict_table_t* table) /* in: table */ 00432 { 00433 ib_longlong value; 00434 00435 mutex_enter(&(table->autoinc_mutex)); 00436 00437 if (!table->autoinc_inited) { 00438 00439 value = 0; 00440 } else { 00441 value = table->autoinc; 00442 table->autoinc = table->autoinc + 1; 00443 } 00444 00445 mutex_exit(&(table->autoinc_mutex)); 00446 00447 return(value); 00448 } 00449 00450 /************************************************************************ 00451 Decrements the autoinc counter value by 1. */ 00452 00453 void 00454 dict_table_autoinc_decrement( 00455 /*=========================*/ 00456 dict_table_t* table) /* in: table */ 00457 { 00458 mutex_enter(&(table->autoinc_mutex)); 00459 00460 table->autoinc = table->autoinc - 1; 00461 00462 mutex_exit(&(table->autoinc_mutex)); 00463 } 00464 00465 /************************************************************************ 00466 Reads the next autoinc value (== autoinc counter value), 0 if not yet 00467 initialized. */ 00468 00469 ib_longlong 00470 dict_table_autoinc_read( 00471 /*====================*/ 00472 /* out: value for a new row, or 0 */ 00473 dict_table_t* table) /* in: table */ 00474 { 00475 ib_longlong value; 00476 00477 mutex_enter(&(table->autoinc_mutex)); 00478 00479 if (!table->autoinc_inited) { 00480 00481 value = 0; 00482 } else { 00483 value = table->autoinc; 00484 } 00485 00486 mutex_exit(&(table->autoinc_mutex)); 00487 00488 return(value); 00489 } 00490 00491 /************************************************************************ 00492 Peeks the autoinc counter value, 0 if not yet initialized. Does not 00493 increment the counter. The read not protected by any mutex! */ 00494 00495 ib_longlong 00496 dict_table_autoinc_peek( 00497 /*====================*/ 00498 /* out: value of the counter */ 00499 dict_table_t* table) /* in: table */ 00500 { 00501 ib_longlong value; 00502 00503 if (!table->autoinc_inited) { 00504 00505 value = 0; 00506 } else { 00507 value = table->autoinc; 00508 } 00509 00510 return(value); 00511 } 00512 00513 /************************************************************************ 00514 Updates the autoinc counter if the value supplied is equal or bigger than the 00515 current value. If not inited, does nothing. */ 00516 00517 void 00518 dict_table_autoinc_update( 00519 /*======================*/ 00520 00521 dict_table_t* table, /* in: table */ 00522 ib_longlong value) /* in: value which was assigned to a row */ 00523 { 00524 mutex_enter(&(table->autoinc_mutex)); 00525 00526 if (table->autoinc_inited) { 00527 if (value >= table->autoinc) { 00528 table->autoinc = value + 1; 00529 } 00530 } 00531 00532 mutex_exit(&(table->autoinc_mutex)); 00533 } 00534 00535 /************************************************************************ 00536 Looks for column n in an index. */ 00537 00538 ulint 00539 dict_index_get_nth_col_pos( 00540 /*=======================*/ 00541 /* out: position in internal representation 00542 of the index; if not contained, returns 00543 ULINT_UNDEFINED */ 00544 dict_index_t* index, /* in: index */ 00545 ulint n) /* in: column number */ 00546 { 00547 dict_field_t* field; 00548 dict_col_t* col; 00549 ulint pos; 00550 ulint n_fields; 00551 00552 ut_ad(index); 00553 ut_ad(index->magic_n == DICT_INDEX_MAGIC_N); 00554 00555 col = dict_table_get_nth_col(index->table, n); 00556 00557 if (index->type & DICT_CLUSTERED) { 00558 00559 return(col->clust_pos); 00560 } 00561 00562 n_fields = dict_index_get_n_fields(index); 00563 00564 for (pos = 0; pos < n_fields; pos++) { 00565 field = dict_index_get_nth_field(index, pos); 00566 00567 if (col == field->col && field->prefix_len == 0) { 00568 00569 return(pos); 00570 } 00571 } 00572 00573 return(ULINT_UNDEFINED); 00574 } 00575 00576 /************************************************************************ 00577 Returns TRUE if the index contains a column or a prefix of that column. */ 00578 00579 ibool 00580 dict_index_contains_col_or_prefix( 00581 /*==============================*/ 00582 /* out: TRUE if contains the column or its 00583 prefix */ 00584 dict_index_t* index, /* in: index */ 00585 ulint n) /* in: column number */ 00586 { 00587 dict_field_t* field; 00588 dict_col_t* col; 00589 ulint pos; 00590 ulint n_fields; 00591 00592 ut_ad(index); 00593 ut_ad(index->magic_n == DICT_INDEX_MAGIC_N); 00594 00595 if (index->type & DICT_CLUSTERED) { 00596 00597 return(TRUE); 00598 } 00599 00600 col = dict_table_get_nth_col(index->table, n); 00601 00602 n_fields = dict_index_get_n_fields(index); 00603 00604 for (pos = 0; pos < n_fields; pos++) { 00605 field = dict_index_get_nth_field(index, pos); 00606 00607 if (col == field->col) { 00608 00609 return(TRUE); 00610 } 00611 } 00612 00613 return(FALSE); 00614 } 00615 00616 /************************************************************************ 00617 Looks for a matching field in an index. The column has to be the same. The 00618 column in index must be complete, or must contain a prefix longer than the 00619 column in index2. That is, we must be able to construct the prefix in index2 00620 from the prefix in index. */ 00621 00622 ulint 00623 dict_index_get_nth_field_pos( 00624 /*=========================*/ 00625 /* out: position in internal representation 00626 of the index; if not contained, returns 00627 ULINT_UNDEFINED */ 00628 dict_index_t* index, /* in: index from which to search */ 00629 dict_index_t* index2, /* in: index */ 00630 ulint n) /* in: field number in index2 */ 00631 { 00632 dict_field_t* field; 00633 dict_field_t* field2; 00634 ulint n_fields; 00635 ulint pos; 00636 00637 ut_ad(index); 00638 ut_ad(index->magic_n == DICT_INDEX_MAGIC_N); 00639 00640 field2 = dict_index_get_nth_field(index2, n); 00641 00642 n_fields = dict_index_get_n_fields(index); 00643 00644 for (pos = 0; pos < n_fields; pos++) { 00645 field = dict_index_get_nth_field(index, pos); 00646 00647 if (field->col == field2->col 00648 && (field->prefix_len == 0 00649 || (field->prefix_len >= field2->prefix_len 00650 && field2->prefix_len != 0))) { 00651 00652 return(pos); 00653 } 00654 } 00655 00656 return(ULINT_UNDEFINED); 00657 } 00658 00659 /************************************************************************** 00660 Returns a table object based on table id. */ 00661 00662 dict_table_t* 00663 dict_table_get_on_id( 00664 /*=================*/ 00665 /* out: table, NULL if does not exist */ 00666 dulint table_id, /* in: table id */ 00667 trx_t* trx) /* in: transaction handle */ 00668 { 00669 dict_table_t* table; 00670 00671 if (ut_dulint_cmp(table_id, DICT_FIELDS_ID) <= 0 00672 || trx->dict_operation_lock_mode == RW_X_LATCH) { 00673 /* It is a system table which will always exist in the table 00674 cache: we avoid acquiring the dictionary mutex, because 00675 if we are doing a rollback to handle an error in TABLE 00676 CREATE, for example, we already have the mutex! */ 00677 00678 #ifdef UNIV_SYNC_DEBUG 00679 ut_ad(mutex_own(&(dict_sys->mutex))); 00680 #endif /* UNIV_SYNC_DEBUG */ 00681 00682 return(dict_table_get_on_id_low(table_id)); 00683 } 00684 00685 mutex_enter(&(dict_sys->mutex)); 00686 00687 table = dict_table_get_on_id_low(table_id); 00688 00689 mutex_exit(&(dict_sys->mutex)); 00690 00691 return(table); 00692 } 00693 00694 /************************************************************************ 00695 Looks for column n position in the clustered index. */ 00696 00697 ulint 00698 dict_table_get_nth_col_pos( 00699 /*=======================*/ 00700 /* out: position in internal representation 00701 of the clustered index */ 00702 dict_table_t* table, /* in: table */ 00703 ulint n) /* in: column number */ 00704 { 00705 return(dict_index_get_nth_col_pos(dict_table_get_first_index(table), 00706 n)); 00707 } 00708 00709 /************************************************************************ 00710 Check whether the table uses the compact page format. */ 00711 00712 ibool 00713 dict_table_is_comp_noninline( 00714 /*=========================*/ 00715 /* out: TRUE if table uses the 00716 compact page format */ 00717 const dict_table_t* table) /* in: table */ 00718 { 00719 return(dict_table_is_comp(table)); 00720 } 00721 00722 /************************************************************************ 00723 Checks if a column is in the ordering columns of the clustered index of a 00724 table. Column prefixes are treated like whole columns. */ 00725 00726 ibool 00727 dict_table_col_in_clustered_key( 00728 /*============================*/ 00729 /* out: TRUE if the column, or its prefix, is 00730 in the clustered key */ 00731 dict_table_t* table, /* in: table */ 00732 ulint n) /* in: column number */ 00733 { 00734 dict_index_t* index; 00735 dict_field_t* field; 00736 dict_col_t* col; 00737 ulint pos; 00738 ulint n_fields; 00739 00740 ut_ad(table); 00741 00742 col = dict_table_get_nth_col(table, n); 00743 00744 index = dict_table_get_first_index(table); 00745 00746 n_fields = dict_index_get_n_unique(index); 00747 00748 for (pos = 0; pos < n_fields; pos++) { 00749 field = dict_index_get_nth_field(index, pos); 00750 00751 if (col == field->col) { 00752 00753 return(TRUE); 00754 } 00755 } 00756 00757 return(FALSE); 00758 } 00759 00760 /************************************************************************** 00761 Inits the data dictionary module. */ 00762 00763 void 00764 dict_init(void) 00765 /*===========*/ 00766 { 00767 dict_sys = mem_alloc(sizeof(dict_sys_t)); 00768 00769 mutex_create(&dict_sys->mutex, SYNC_DICT); 00770 00771 dict_sys->table_hash = hash_create(buf_pool_get_max_size() / 00772 (DICT_POOL_PER_TABLE_HASH * 00773 UNIV_WORD_SIZE)); 00774 dict_sys->table_id_hash = hash_create(buf_pool_get_max_size() / 00775 (DICT_POOL_PER_TABLE_HASH * 00776 UNIV_WORD_SIZE)); 00777 dict_sys->col_hash = hash_create(buf_pool_get_max_size() / 00778 (DICT_POOL_PER_COL_HASH * 00779 UNIV_WORD_SIZE)); 00780 dict_sys->size = 0; 00781 00782 UT_LIST_INIT(dict_sys->table_LRU); 00783 00784 rw_lock_create(&dict_operation_lock, SYNC_DICT_OPERATION); 00785 00786 dict_foreign_err_file = os_file_create_tmpfile(); 00787 ut_a(dict_foreign_err_file); 00788 00789 mutex_create(&dict_foreign_err_mutex, SYNC_ANY_LATCH); 00790 } 00791 00792 /************************************************************************** 00793 Returns a table object. NOTE! This is a high-level function to be used 00794 mainly from outside the 'dict' directory. Inside this directory 00795 dict_table_get_low is usually the appropriate function. */ 00796 00797 dict_table_t* 00798 dict_table_get( 00799 /*===========*/ 00800 /* out: table, NULL if 00801 does not exist */ 00802 const char* table_name) /* in: table name */ 00803 { 00804 dict_table_t* table; 00805 00806 mutex_enter(&(dict_sys->mutex)); 00807 00808 table = dict_table_get_low(table_name); 00809 00810 mutex_exit(&(dict_sys->mutex)); 00811 00812 if (table != NULL) { 00813 if (!table->stat_initialized) { 00814 dict_update_statistics(table); 00815 } 00816 } 00817 00818 return(table); 00819 } 00820 00821 /************************************************************************** 00822 Returns a table object and increments MySQL open handle count on the table. */ 00823 00824 dict_table_t* 00825 dict_table_get_and_increment_handle_count( 00826 /*======================================*/ 00827 /* out: table, NULL if 00828 does not exist */ 00829 const char* table_name) /* in: table name */ 00830 { 00831 dict_table_t* table; 00832 00833 mutex_enter(&(dict_sys->mutex)); 00834 00835 table = dict_table_get_low(table_name); 00836 00837 if (table != NULL) { 00838 00839 table->n_mysql_handles_opened++; 00840 } 00841 00842 mutex_exit(&(dict_sys->mutex)); 00843 00844 if (table != NULL) { 00845 if (!table->stat_initialized && !table->ibd_file_missing) { 00846 dict_update_statistics(table); 00847 } 00848 } 00849 00850 return(table); 00851 } 00852 00853 /************************************************************************** 00854 Adds a table object to the dictionary cache. */ 00855 00856 void 00857 dict_table_add_to_cache( 00858 /*====================*/ 00859 dict_table_t* table) /* in: table */ 00860 { 00861 ulint fold; 00862 ulint id_fold; 00863 ulint i; 00864 ulint row_len; 00865 00866 ut_ad(table); 00867 #ifdef UNIV_SYNC_DEBUG 00868 ut_ad(mutex_own(&(dict_sys->mutex))); 00869 #endif /* UNIV_SYNC_DEBUG */ 00870 ut_ad(table->n_def == table->n_cols - DATA_N_SYS_COLS); 00871 ut_ad(table->magic_n == DICT_TABLE_MAGIC_N); 00872 ut_ad(table->cached == FALSE); 00873 00874 fold = ut_fold_string(table->name); 00875 id_fold = ut_fold_dulint(table->id); 00876 00877 table->cached = TRUE; 00878 00879 /* NOTE: the system columns MUST be added in the following order 00880 (so that they can be indexed by the numerical value of DATA_ROW_ID, 00881 etc.) and as the last columns of the table memory object. 00882 The clustered index will not always physically contain all 00883 system columns. */ 00884 00885 dict_mem_table_add_col(table, "DB_ROW_ID", DATA_SYS, 00886 DATA_ROW_ID | DATA_NOT_NULL, DATA_ROW_ID_LEN, 0); 00887 #if DATA_ROW_ID != 0 00888 #error "DATA_ROW_ID != 0" 00889 #endif 00890 dict_mem_table_add_col(table, "DB_TRX_ID", DATA_SYS, 00891 DATA_TRX_ID | DATA_NOT_NULL, DATA_TRX_ID_LEN, 0); 00892 #if DATA_TRX_ID != 1 00893 #error "DATA_TRX_ID != 1" 00894 #endif 00895 dict_mem_table_add_col(table, "DB_ROLL_PTR", DATA_SYS, 00896 DATA_ROLL_PTR | DATA_NOT_NULL, DATA_ROLL_PTR_LEN, 0); 00897 #if DATA_ROLL_PTR != 2 00898 #error "DATA_ROLL_PTR != 2" 00899 #endif 00900 dict_mem_table_add_col(table, "DB_MIX_ID", DATA_SYS, 00901 DATA_MIX_ID | DATA_NOT_NULL, DATA_MIX_ID_LEN, 0); 00902 #if DATA_MIX_ID != 3 00903 #error "DATA_MIX_ID != 3" 00904 #endif 00905 00906 /* This check reminds that if a new system column is added to 00907 the program, it should be dealt with here */ 00908 #if DATA_N_SYS_COLS != 4 00909 #error "DATA_N_SYS_COLS != 4" 00910 #endif 00911 00912 row_len = 0; 00913 for (i = 0; i < table->n_def; i++) { 00914 ulint col_len = dtype_get_max_size( 00915 dict_col_get_type(dict_table_get_nth_col(table, i))); 00916 00917 /* If we have a single unbounded field, or several gigantic 00918 fields, mark the maximum row size as ULINT_MAX. */ 00919 if (ut_max(col_len, row_len) >= (ULINT_MAX / 2)) { 00920 row_len = ULINT_MAX; 00921 00922 break; 00923 } 00924 00925 row_len += col_len; 00926 } 00927 00928 table->max_row_size = row_len; 00929 00930 /* Look for a table with the same name: error if such exists */ 00931 { 00932 dict_table_t* table2; 00933 HASH_SEARCH(name_hash, dict_sys->table_hash, fold, table2, 00934 (ut_strcmp(table2->name, table->name) == 0)); 00935 ut_a(table2 == NULL); 00936 } 00937 00938 /* Look for a table with the same id: error if such exists */ 00939 { 00940 dict_table_t* table2; 00941 HASH_SEARCH(id_hash, dict_sys->table_id_hash, id_fold, table2, 00942 (ut_dulint_cmp(table2->id, table->id) == 0)); 00943 ut_a(table2 == NULL); 00944 } 00945 00946 /* Add the columns to the column hash table */ 00947 for (i = 0; i < table->n_cols; i++) { 00948 dict_col_add_to_cache(table, dict_table_get_nth_col(table, i)); 00949 } 00950 00951 /* Add table to hash table of tables */ 00952 HASH_INSERT(dict_table_t, name_hash, dict_sys->table_hash, fold, 00953 table); 00954 00955 /* Add table to hash table of tables based on table id */ 00956 HASH_INSERT(dict_table_t, id_hash, dict_sys->table_id_hash, id_fold, 00957 table); 00958 /* Add table to LRU list of tables */ 00959 UT_LIST_ADD_FIRST(table_LRU, dict_sys->table_LRU, table); 00960 00961 dict_sys->size += mem_heap_get_size(table->heap); 00962 } 00963 00964 /************************************************************************** 00965 Looks for an index with the given id. NOTE that we do not reserve 00966 the dictionary mutex: this function is for emergency purposes like 00967 printing info of a corrupt database page! */ 00968 00969 dict_index_t* 00970 dict_index_find_on_id_low( 00971 /*======================*/ 00972 /* out: index or NULL if not found from cache */ 00973 dulint id) /* in: index id */ 00974 { 00975 dict_table_t* table; 00976 dict_index_t* index; 00977 00978 table = UT_LIST_GET_FIRST(dict_sys->table_LRU); 00979 00980 while (table) { 00981 index = dict_table_get_first_index(table); 00982 00983 while (index) { 00984 if (0 == ut_dulint_cmp(id, index->tree->id)) { 00985 /* Found */ 00986 00987 return(index); 00988 } 00989 00990 index = dict_table_get_next_index(index); 00991 } 00992 00993 table = UT_LIST_GET_NEXT(table_LRU, table); 00994 } 00995 00996 return(NULL); 00997 } 00998 00999 /************************************************************************** 01000 Renames a table object. */ 01001 01002 ibool 01003 dict_table_rename_in_cache( 01004 /*=======================*/ 01005 /* out: TRUE if success */ 01006 dict_table_t* table, /* in: table */ 01007 const char* new_name, /* in: new name */ 01008 ibool rename_also_foreigns)/* in: in ALTER TABLE we want 01009 to preserve the original table name 01010 in constraints which reference it */ 01011 { 01012 dict_foreign_t* foreign; 01013 dict_index_t* index; 01014 ulint fold; 01015 ulint old_size; 01016 char* old_name; 01017 ibool success; 01018 ulint i; 01019 01020 ut_ad(table); 01021 #ifdef UNIV_SYNC_DEBUG 01022 ut_ad(mutex_own(&(dict_sys->mutex))); 01023 #endif /* UNIV_SYNC_DEBUG */ 01024 01025 old_size = mem_heap_get_size(table->heap); 01026 01027 fold = ut_fold_string(new_name); 01028 01029 /* Look for a table with the same name: error if such exists */ 01030 { 01031 dict_table_t* table2; 01032 HASH_SEARCH(name_hash, dict_sys->table_hash, fold, table2, 01033 (ut_strcmp(table2->name, new_name) == 0)); 01034 if (table2) { 01035 fprintf(stderr, 01036 "InnoDB: Error: dictionary cache already contains a table of name %s\n", 01037 new_name); 01038 return(FALSE); 01039 } 01040 } 01041 01042 /* If the table is stored in a single-table tablespace, rename the 01043 .ibd file */ 01044 01045 if (table->space != 0) { 01046 if (table->dir_path_of_temp_table != NULL) { 01047 fprintf(stderr, 01048 "InnoDB: Error: trying to rename a table %s (%s) created with CREATE\n" 01049 "InnoDB: TEMPORARY TABLE\n", table->name, table->dir_path_of_temp_table); 01050 success = FALSE; 01051 } else { 01052 success = fil_rename_tablespace(table->name, 01053 table->space, new_name); 01054 } 01055 01056 if (!success) { 01057 01058 return(FALSE); 01059 } 01060 } 01061 01062 /* Reposition the columns in the column hash table; they are hashed 01063 according to the pair (table name, column name) */ 01064 01065 for (i = 0; i < table->n_cols; i++) { 01066 dict_col_reposition_in_cache(table, 01067 dict_table_get_nth_col(table, i), new_name); 01068 } 01069 01070 /* Remove table from the hash tables of tables */ 01071 HASH_DELETE(dict_table_t, name_hash, dict_sys->table_hash, 01072 ut_fold_string(table->name), table); 01073 old_name = mem_heap_strdup(table->heap, table->name); 01074 table->name = mem_heap_strdup(table->heap, new_name); 01075 01076 /* Add table to hash table of tables */ 01077 HASH_INSERT(dict_table_t, name_hash, dict_sys->table_hash, fold, 01078 table); 01079 dict_sys->size += (mem_heap_get_size(table->heap) - old_size); 01080 01081 /* Update the table_name field in indexes */ 01082 index = dict_table_get_first_index(table); 01083 01084 while (index != NULL) { 01085 index->table_name = table->name; 01086 01087 index = dict_table_get_next_index(index); 01088 } 01089 01090 if (!rename_also_foreigns) { 01091 /* In ALTER TABLE we think of the rename table operation 01092 in the direction table -> temporary table (#sql...) 01093 as dropping the table with the old name and creating 01094 a new with the new name. Thus we kind of drop the 01095 constraints from the dictionary cache here. The foreign key 01096 constraints will be inherited to the new table from the 01097 system tables through a call of dict_load_foreigns. */ 01098 01099 /* Remove the foreign constraints from the cache */ 01100 foreign = UT_LIST_GET_LAST(table->foreign_list); 01101 01102 while (foreign != NULL) { 01103 dict_foreign_remove_from_cache(foreign); 01104 foreign = UT_LIST_GET_LAST(table->foreign_list); 01105 } 01106 01107 /* Reset table field in referencing constraints */ 01108 01109 foreign = UT_LIST_GET_FIRST(table->referenced_list); 01110 01111 while (foreign != NULL) { 01112 foreign->referenced_table = NULL; 01113 foreign->referenced_index = NULL; 01114 01115 foreign = UT_LIST_GET_NEXT(referenced_list, foreign); 01116 } 01117 01118 /* Make the list of referencing constraints empty */ 01119 01120 UT_LIST_INIT(table->referenced_list); 01121 01122 return(TRUE); 01123 } 01124 01125 /* Update the table name fields in foreign constraints, and update also 01126 the constraint id of new format >= 4.0.18 constraints. Note that at 01127 this point we have already changed table->name to the new name. */ 01128 01129 foreign = UT_LIST_GET_FIRST(table->foreign_list); 01130 01131 while (foreign != NULL) { 01132 if (ut_strlen(foreign->foreign_table_name) < 01133 ut_strlen(table->name)) { 01134 /* Allocate a longer name buffer; 01135 TODO: store buf len to save memory */ 01136 01137 foreign->foreign_table_name = mem_heap_alloc( 01138 foreign->heap, 01139 ut_strlen(table->name) + 1); 01140 } 01141 01142 strcpy(foreign->foreign_table_name, table->name); 01143 01144 if (strchr(foreign->id, '/')) { 01145 ulint db_len; 01146 char* old_id; 01147 01148 /* This is a >= 4.0.18 format id */ 01149 01150 old_id = mem_strdup(foreign->id); 01151 01152 if (ut_strlen(foreign->id) > ut_strlen(old_name) 01153 + ((sizeof dict_ibfk) - 1) 01154 && 0 == ut_memcmp(foreign->id, old_name, 01155 ut_strlen(old_name)) 01156 && 0 == ut_memcmp( 01157 foreign->id + ut_strlen(old_name), 01158 dict_ibfk, (sizeof dict_ibfk) - 1)) { 01159 01160 /* This is a generated >= 4.0.18 format id */ 01161 01162 if (ut_strlen(table->name) > ut_strlen(old_name)) { 01163 foreign->id = mem_heap_alloc( 01164 foreign->heap, 01165 ut_strlen(table->name) 01166 + ut_strlen(old_id) + 1); 01167 } 01168 01169 /* Replace the prefix 'databasename/tablename' 01170 with the new names */ 01171 strcpy(foreign->id, table->name); 01172 strcat(foreign->id, 01173 old_id + ut_strlen(old_name)); 01174 } else { 01175 /* This is a >= 4.0.18 format id where the user 01176 gave the id name */ 01177 db_len = dict_get_db_name_len(table->name) + 1; 01178 01179 if (dict_get_db_name_len(table->name) 01180 > dict_get_db_name_len(foreign->id)) { 01181 01182 foreign->id = mem_heap_alloc( 01183 foreign->heap, 01184 db_len + ut_strlen(old_id) + 1); 01185 } 01186 01187 /* Replace the database prefix in id with the 01188 one from table->name */ 01189 01190 ut_memcpy(foreign->id, table->name, db_len); 01191 01192 strcpy(foreign->id + db_len, 01193 dict_remove_db_name(old_id)); 01194 } 01195 01196 mem_free(old_id); 01197 } 01198 01199 foreign = UT_LIST_GET_NEXT(foreign_list, foreign); 01200 } 01201 01202 foreign = UT_LIST_GET_FIRST(table->referenced_list); 01203 01204 while (foreign != NULL) { 01205 if (ut_strlen(foreign->referenced_table_name) < 01206 ut_strlen(table->name)) { 01207 /* Allocate a longer name buffer; 01208 TODO: store buf len to save memory */ 01209 01210 foreign->referenced_table_name = mem_heap_alloc( 01211 foreign->heap, 01212 ut_strlen(table->name) + 1); 01213 } 01214 01215 strcpy(foreign->referenced_table_name, table->name); 01216 01217 foreign = UT_LIST_GET_NEXT(referenced_list, foreign); 01218 } 01219 01220 return(TRUE); 01221 } 01222 01223 /************************************************************************** 01224 Change the id of a table object in the dictionary cache. This is used in 01225 DISCARD TABLESPACE. */ 01226 01227 void 01228 dict_table_change_id_in_cache( 01229 /*==========================*/ 01230 dict_table_t* table, /* in: table object already in cache */ 01231 dulint new_id) /* in: new id to set */ 01232 { 01233 ut_ad(table); 01234 #ifdef UNIV_SYNC_DEBUG 01235 ut_ad(mutex_own(&(dict_sys->mutex))); 01236 #endif /* UNIV_SYNC_DEBUG */ 01237 ut_ad(table->magic_n == DICT_TABLE_MAGIC_N); 01238 01239 /* Remove the table from the hash table of id's */ 01240 01241 HASH_DELETE(dict_table_t, id_hash, dict_sys->table_id_hash, 01242 ut_fold_dulint(table->id), table); 01243 table->id = new_id; 01244 01245 /* Add the table back to the hash table */ 01246 HASH_INSERT(dict_table_t, id_hash, dict_sys->table_id_hash, 01247 ut_fold_dulint(table->id), table); 01248 } 01249 01250 /************************************************************************** 01251 Removes a table object from the dictionary cache. */ 01252 01253 void 01254 dict_table_remove_from_cache( 01255 /*=========================*/ 01256 dict_table_t* table) /* in, own: table */ 01257 { 01258 dict_foreign_t* foreign; 01259 dict_index_t* index; 01260 ulint size; 01261 ulint i; 01262 01263 ut_ad(table); 01264 #ifdef UNIV_SYNC_DEBUG 01265 ut_ad(mutex_own(&(dict_sys->mutex))); 01266 #endif /* UNIV_SYNC_DEBUG */ 01267 ut_ad(table->magic_n == DICT_TABLE_MAGIC_N); 01268 01269 #if 0 01270 fputs("Removing table ", stderr); 01271 ut_print_name(stderr, table->name, ULINT_UNDEFINED); 01272 fputs(" from dictionary cache\n", stderr); 01273 #endif 01274 01275 /* Remove the foreign constraints from the cache */ 01276 foreign = UT_LIST_GET_LAST(table->foreign_list); 01277 01278 while (foreign != NULL) { 01279 dict_foreign_remove_from_cache(foreign); 01280 foreign = UT_LIST_GET_LAST(table->foreign_list); 01281 } 01282 01283 /* Reset table field in referencing constraints */ 01284 01285 foreign = UT_LIST_GET_FIRST(table->referenced_list); 01286 01287 while (foreign != NULL) { 01288 foreign->referenced_table = NULL; 01289 foreign->referenced_index = NULL; 01290 01291 foreign = UT_LIST_GET_NEXT(referenced_list, foreign); 01292 } 01293 01294 /* Remove the indexes from the cache */ 01295 index = UT_LIST_GET_LAST(table->indexes); 01296 01297 while (index != NULL) { 01298 dict_index_remove_from_cache(table, index); 01299 index = UT_LIST_GET_LAST(table->indexes); 01300 } 01301 01302 /* Remove the columns of the table from the cache */ 01303 for (i = 0; i < table->n_cols; i++) { 01304 dict_col_remove_from_cache(table, 01305 dict_table_get_nth_col(table, i)); 01306 } 01307 01308 /* Remove table from the hash tables of tables */ 01309 HASH_DELETE(dict_table_t, name_hash, dict_sys->table_hash, 01310 ut_fold_string(table->name), table); 01311 HASH_DELETE(dict_table_t, id_hash, dict_sys->table_id_hash, 01312 ut_fold_dulint(table->id), table); 01313 01314 /* Remove table from LRU list of tables */ 01315 UT_LIST_REMOVE(table_LRU, dict_sys->table_LRU, table); 01316 01317 size = mem_heap_get_size(table->heap); 01318 01319 ut_ad(dict_sys->size >= size); 01320 01321 dict_sys->size -= size; 01322 01323 dict_mem_table_free(table); 01324 } 01325 01326 /************************************************************************** 01327 Adds a column to the data dictionary hash table. */ 01328 static 01329 void 01330 dict_col_add_to_cache( 01331 /*==================*/ 01332 dict_table_t* table, /* in: table */ 01333 dict_col_t* col) /* in: column */ 01334 { 01335 ulint fold; 01336 01337 ut_ad(table && col); 01338 #ifdef UNIV_SYNC_DEBUG 01339 ut_ad(mutex_own(&(dict_sys->mutex))); 01340 #endif /* UNIV_SYNC_DEBUG */ 01341 ut_ad(table->magic_n == DICT_TABLE_MAGIC_N); 01342 01343 fold = ut_fold_ulint_pair(ut_fold_string(table->name), 01344 ut_fold_string(col->name)); 01345 01346 /* Look for a column with same table name and column name: error */ 01347 { 01348 dict_col_t* col2; 01349 HASH_SEARCH(hash, dict_sys->col_hash, fold, col2, 01350 (ut_strcmp(col->name, col2->name) == 0) 01351 && (ut_strcmp((col2->table)->name, table->name) 01352 == 0)); 01353 ut_a(col2 == NULL); 01354 } 01355 01356 HASH_INSERT(dict_col_t, hash, dict_sys->col_hash, fold, col); 01357 } 01358 01359 /************************************************************************** 01360 Removes a column from the data dictionary hash table. */ 01361 static 01362 void 01363 dict_col_remove_from_cache( 01364 /*=======================*/ 01365 dict_table_t* table, /* in: table */ 01366 dict_col_t* col) /* in: column */ 01367 { 01368 ulint fold; 01369 01370 ut_ad(table && col); 01371 #ifdef UNIV_SYNC_DEBUG 01372 ut_ad(mutex_own(&(dict_sys->mutex))); 01373 #endif /* UNIV_SYNC_DEBUG */ 01374 ut_ad(table->magic_n == DICT_TABLE_MAGIC_N); 01375 01376 fold = ut_fold_ulint_pair(ut_fold_string(table->name), 01377 ut_fold_string(col->name)); 01378 01379 HASH_DELETE(dict_col_t, hash, dict_sys->col_hash, fold, col); 01380 } 01381 01382 /************************************************************************** 01383 Repositions a column in the data dictionary hash table when the table name 01384 changes. */ 01385 static 01386 void 01387 dict_col_reposition_in_cache( 01388 /*=========================*/ 01389 dict_table_t* table, /* in: table */ 01390 dict_col_t* col, /* in: column */ 01391 const char* new_name) /* in: new table name */ 01392 { 01393 ulint fold; 01394 01395 ut_ad(table && col); 01396 #ifdef UNIV_SYNC_DEBUG 01397 ut_ad(mutex_own(&(dict_sys->mutex))); 01398 #endif /* UNIV_SYNC_DEBUG */ 01399 ut_ad(table->magic_n == DICT_TABLE_MAGIC_N); 01400 01401 fold = ut_fold_ulint_pair(ut_fold_string(table->name), 01402 ut_fold_string(col->name)); 01403 01404 HASH_DELETE(dict_col_t, hash, dict_sys->col_hash, fold, col); 01405 01406 fold = ut_fold_ulint_pair(ut_fold_string(new_name), 01407 ut_fold_string(col->name)); 01408 01409 HASH_INSERT(dict_col_t, hash, dict_sys->col_hash, fold, col); 01410 } 01411 01412 /******************************************************************** 01413 If the given column name is reserved for InnoDB system columns, return 01414 TRUE. */ 01415 01416 ibool 01417 dict_col_name_is_reserved( 01418 /*======================*/ 01419 /* out: TRUE if name is reserved */ 01420 const char* name) /* in: column name */ 01421 { 01422 /* This check reminds that if a new system column is added to 01423 the program, it should be dealt with here. */ 01424 #if DATA_N_SYS_COLS != 4 01425 #error "DATA_N_SYS_COLS != 4" 01426 #endif 01427 01428 static const char* reserved_names[] = { 01429 "DB_ROW_ID", "DB_TRX_ID", "DB_ROLL_PTR", "DB_MIX_ID" 01430 }; 01431 01432 ulint i; 01433 01434 for (i = 0; i < UT_ARR_SIZE(reserved_names); i++) { 01435 if (strcmp(name, reserved_names[i]) == 0) { 01436 01437 return(TRUE); 01438 } 01439 } 01440 01441 return(FALSE); 01442 } 01443 01444 /************************************************************************** 01445 Adds an index to the dictionary cache. */ 01446 01447 ibool 01448 dict_index_add_to_cache( 01449 /*====================*/ 01450 /* out: TRUE if success */ 01451 dict_table_t* table, /* in: table on which the index is */ 01452 dict_index_t* index, /* in, own: index; NOTE! The index memory 01453 object is freed in this function! */ 01454 ulint page_no)/* in: root page number of the index */ 01455 { 01456 dict_index_t* new_index; 01457 dict_tree_t* tree; 01458 dict_field_t* field; 01459 ulint n_ord; 01460 ibool success; 01461 ulint i; 01462 01463 ut_ad(index); 01464 #ifdef UNIV_SYNC_DEBUG 01465 ut_ad(mutex_own(&(dict_sys->mutex))); 01466 #endif /* UNIV_SYNC_DEBUG */ 01467 ut_ad(index->n_def == index->n_fields); 01468 ut_ad(index->magic_n == DICT_INDEX_MAGIC_N); 01469 01470 ut_ad(mem_heap_validate(index->heap)); 01471 01472 #ifdef UNIV_DEBUG 01473 { 01474 dict_index_t* index2; 01475 index2 = UT_LIST_GET_FIRST(table->indexes); 01476 01477 while (index2 != NULL) { 01478 ut_ad(ut_strcmp(index->name, index2->name) != 0); 01479 01480 index2 = UT_LIST_GET_NEXT(indexes, index2); 01481 } 01482 } 01483 #endif /* UNIV_DEBUG */ 01484 01485 ut_a(!(index->type & DICT_CLUSTERED) 01486 || UT_LIST_GET_LEN(table->indexes) == 0); 01487 01488 success = dict_index_find_cols(table, index); 01489 01490 if (!success) { 01491 dict_mem_index_free(index); 01492 01493 return(FALSE); 01494 } 01495 01496 /* Build the cache internal representation of the index, 01497 containing also the added system fields */ 01498 01499 if (index->type & DICT_CLUSTERED) { 01500 new_index = dict_index_build_internal_clust(table, index); 01501 } else { 01502 new_index = dict_index_build_internal_non_clust(table, index); 01503 } 01504 01505 new_index->search_info = btr_search_info_create(new_index->heap); 01506 01507 /* Set the n_fields value in new_index to the actual defined 01508 number of fields in the cache internal representation */ 01509 01510 new_index->n_fields = new_index->n_def; 01511 01512 /* Add the new index as the last index for the table */ 01513 01514 UT_LIST_ADD_LAST(indexes, table->indexes, new_index); 01515 new_index->table = table; 01516 new_index->table_name = table->name; 01517 01518 /* Increment the ord_part counts in columns which are ordering */ 01519 01520 if (UNIV_UNLIKELY(index->type & DICT_UNIVERSAL)) { 01521 n_ord = new_index->n_fields; 01522 } else { 01523 n_ord = dict_index_get_n_unique(new_index); 01524 } 01525 01526 for (i = 0; i < n_ord; i++) { 01527 01528 field = dict_index_get_nth_field(new_index, i); 01529 01530 dict_field_get_col(field)->ord_part++; 01531 } 01532 01533 /* Create an index tree memory object for the index */ 01534 tree = dict_tree_create(new_index, page_no); 01535 ut_ad(tree); 01536 01537 new_index->tree = tree; 01538 01539 if (!UNIV_UNLIKELY(new_index->type & DICT_UNIVERSAL)) { 01540 01541 new_index->stat_n_diff_key_vals = 01542 mem_heap_alloc(new_index->heap, 01543 (1 + dict_index_get_n_unique(new_index)) 01544 * sizeof(ib_longlong)); 01545 /* Give some sensible values to stat_n_... in case we do 01546 not calculate statistics quickly enough */ 01547 01548 for (i = 0; i <= dict_index_get_n_unique(new_index); i++) { 01549 01550 new_index->stat_n_diff_key_vals[i] = 100; 01551 } 01552 } 01553 01554 /* Add the index to the list of indexes stored in the tree */ 01555 tree->tree_index = new_index; 01556 01557 dict_sys->size += mem_heap_get_size(new_index->heap); 01558 01559 dict_mem_index_free(index); 01560 01561 return(TRUE); 01562 } 01563 01564 /************************************************************************** 01565 Removes an index from the dictionary cache. */ 01566 static 01567 void 01568 dict_index_remove_from_cache( 01569 /*=========================*/ 01570 dict_table_t* table, /* in: table */ 01571 dict_index_t* index) /* in, own: index */ 01572 { 01573 dict_field_t* field; 01574 ulint size; 01575 ulint i; 01576 01577 ut_ad(table && index); 01578 ut_ad(table->magic_n == DICT_TABLE_MAGIC_N); 01579 ut_ad(index->magic_n == DICT_INDEX_MAGIC_N); 01580 #ifdef UNIV_SYNC_DEBUG 01581 ut_ad(mutex_own(&(dict_sys->mutex))); 01582 #endif /* UNIV_SYNC_DEBUG */ 01583 01584 ut_ad(index->tree->tree_index); 01585 dict_tree_free(index->tree); 01586 01587 /* Decrement the ord_part counts in columns which are ordering */ 01588 for (i = 0; i < dict_index_get_n_unique(index); i++) { 01589 01590 field = dict_index_get_nth_field(index, i); 01591 01592 ut_ad(dict_field_get_col(field)->ord_part > 0); 01593 (dict_field_get_col(field)->ord_part)--; 01594 } 01595 01596 /* Remove the index from the list of indexes of the table */ 01597 UT_LIST_REMOVE(indexes, table->indexes, index); 01598 01599 size = mem_heap_get_size(index->heap); 01600 01601 ut_ad(dict_sys->size >= size); 01602 01603 dict_sys->size -= size; 01604 01605 dict_mem_index_free(index); 01606 } 01607 01608 /*********************************************************************** 01609 Tries to find column names for the index in the column hash table and 01610 sets the col field of the index. */ 01611 static 01612 ibool 01613 dict_index_find_cols( 01614 /*=================*/ 01615 /* out: TRUE if success */ 01616 dict_table_t* table, /* in: table */ 01617 dict_index_t* index) /* in: index */ 01618 { 01619 dict_col_t* col; 01620 dict_field_t* field; 01621 ulint fold; 01622 ulint i; 01623 01624 ut_ad(table && index); 01625 ut_ad(table->magic_n == DICT_TABLE_MAGIC_N); 01626 #ifdef UNIV_SYNC_DEBUG 01627 ut_ad(mutex_own(&(dict_sys->mutex))); 01628 #endif /* UNIV_SYNC_DEBUG */ 01629 01630 for (i = 0; i < index->n_fields; i++) { 01631 field = dict_index_get_nth_field(index, i); 01632 01633 fold = ut_fold_ulint_pair(ut_fold_string(table->name), 01634 ut_fold_string(field->name)); 01635 01636 HASH_SEARCH(hash, dict_sys->col_hash, fold, col, 01637 (ut_strcmp(col->name, field->name) == 0) 01638 && (ut_strcmp((col->table)->name, table->name) 01639 == 0)); 01640 if (col == NULL) { 01641 01642 return(FALSE); 01643 } else { 01644 field->col = col; 01645 } 01646 } 01647 01648 return(TRUE); 01649 } 01650 01651 /*********************************************************************** 01652 Adds a column to index. */ 01653 01654 void 01655 dict_index_add_col( 01656 /*===============*/ 01657 dict_index_t* index, /* in: index */ 01658 dict_col_t* col, /* in: column */ 01659 ulint prefix_len) /* in: column prefix length */ 01660 { 01661 dict_field_t* field; 01662 01663 dict_mem_index_add_field(index, col->name, prefix_len); 01664 01665 field = dict_index_get_nth_field(index, index->n_def - 1); 01666 01667 field->col = col; 01668 field->fixed_len = dtype_get_fixed_size(&col->type); 01669 01670 if (prefix_len && field->fixed_len > prefix_len) { 01671 field->fixed_len = prefix_len; 01672 } 01673 01674 /* Long fixed-length fields that need external storage are treated as 01675 variable-length fields, so that the extern flag can be embedded in 01676 the length word. */ 01677 01678 if (field->fixed_len > DICT_MAX_INDEX_COL_LEN) { 01679 field->fixed_len = 0; 01680 } 01681 01682 if (!(dtype_get_prtype(&col->type) & DATA_NOT_NULL)) { 01683 index->n_nullable++; 01684 } 01685 } 01686 01687 /*********************************************************************** 01688 Copies fields contained in index2 to index1. */ 01689 static 01690 void 01691 dict_index_copy( 01692 /*============*/ 01693 dict_index_t* index1, /* in: index to copy to */ 01694 dict_index_t* index2, /* in: index to copy from */ 01695 ulint start, /* in: first position to copy */ 01696 ulint end) /* in: last position to copy */ 01697 { 01698 dict_field_t* field; 01699 ulint i; 01700 01701 /* Copy fields contained in index2 */ 01702 01703 for (i = start; i < end; i++) { 01704 01705 field = dict_index_get_nth_field(index2, i); 01706 dict_index_add_col(index1, field->col, field->prefix_len); 01707 } 01708 } 01709 01710 /*********************************************************************** 01711 Copies types of fields contained in index to tuple. */ 01712 01713 void 01714 dict_index_copy_types( 01715 /*==================*/ 01716 dtuple_t* tuple, /* in: data tuple */ 01717 dict_index_t* index, /* in: index */ 01718 ulint n_fields) /* in: number of field types to copy */ 01719 { 01720 dtype_t* dfield_type; 01721 dtype_t* type; 01722 ulint i; 01723 01724 if (UNIV_UNLIKELY(index->type & DICT_UNIVERSAL)) { 01725 dtuple_set_types_binary(tuple, n_fields); 01726 01727 return; 01728 } 01729 01730 for (i = 0; i < n_fields; i++) { 01731 dfield_type = dfield_get_type(dtuple_get_nth_field(tuple, i)); 01732 type = dict_col_get_type(dict_field_get_col( 01733 dict_index_get_nth_field(index, i))); 01734 *dfield_type = *type; 01735 } 01736 } 01737 01738 /*********************************************************************** 01739 Copies types of columns contained in table to tuple. */ 01740 01741 void 01742 dict_table_copy_types( 01743 /*==================*/ 01744 dtuple_t* tuple, /* in: data tuple */ 01745 dict_table_t* table) /* in: index */ 01746 { 01747 dtype_t* dfield_type; 01748 dtype_t* type; 01749 ulint i; 01750 01751 for (i = 0; i < dtuple_get_n_fields(tuple); i++) { 01752 01753 dfield_type = dfield_get_type(dtuple_get_nth_field(tuple, i)); 01754 type = dict_col_get_type(dict_table_get_nth_col(table, i)); 01755 01756 *dfield_type = *type; 01757 } 01758 } 01759 01760 /*********************************************************************** 01761 Builds the internal dictionary cache representation for a clustered 01762 index, containing also system fields not defined by the user. */ 01763 static 01764 dict_index_t* 01765 dict_index_build_internal_clust( 01766 /*============================*/ 01767 /* out, own: the internal representation 01768 of the clustered index */ 01769 dict_table_t* table, /* in: table */ 01770 dict_index_t* index) /* in: user representation of a clustered 01771 index */ 01772 { 01773 dict_index_t* new_index; 01774 dict_field_t* field; 01775 dict_col_t* col; 01776 ulint fixed_size; 01777 ulint trx_id_pos; 01778 ulint i; 01779 01780 ut_ad(table && index); 01781 ut_ad(index->type & DICT_CLUSTERED); 01782 #ifdef UNIV_SYNC_DEBUG 01783 ut_ad(mutex_own(&(dict_sys->mutex))); 01784 #endif /* UNIV_SYNC_DEBUG */ 01785 ut_ad(table->magic_n == DICT_TABLE_MAGIC_N); 01786 01787 /* Create a new index object with certainly enough fields */ 01788 new_index = dict_mem_index_create(table->name, 01789 index->name, table->space, index->type, 01790 index->n_fields + table->n_cols); 01791 01792 /* Copy other relevant data from the old index struct to the new 01793 struct: it inherits the values */ 01794 01795 new_index->n_user_defined_cols = index->n_fields; 01796 01797 new_index->id = index->id; 01798 01799 /* Copy the fields of index */ 01800 dict_index_copy(new_index, index, 0, index->n_fields); 01801 01802 if (UNIV_UNLIKELY(index->type & DICT_UNIVERSAL)) { 01803 /* No fixed number of fields determines an entry uniquely */ 01804 01805 new_index->n_uniq = ULINT_MAX; 01806 01807 } else if (index->type & DICT_UNIQUE) { 01808 /* Only the fields defined so far are needed to identify 01809 the index entry uniquely */ 01810 01811 new_index->n_uniq = new_index->n_def; 01812 } else { 01813 /* Also the row id is needed to identify the entry */ 01814 new_index->n_uniq = 1 + new_index->n_def; 01815 } 01816 01817 new_index->trx_id_offset = 0; 01818 01819 if (!(index->type & DICT_IBUF)) { 01820 /* Add system columns, trx id first */ 01821 01822 trx_id_pos = new_index->n_def; 01823 01824 #if DATA_ROW_ID != 0 01825 # error "DATA_ROW_ID != 0" 01826 #endif 01827 #if DATA_TRX_ID != 1 01828 # error "DATA_TRX_ID != 1" 01829 #endif 01830 #if DATA_ROLL_PTR != 2 01831 # error "DATA_ROLL_PTR != 2" 01832 #endif 01833 01834 if (!(index->type & DICT_UNIQUE)) { 01835 dict_index_add_col(new_index, 01836 dict_table_get_sys_col(table, DATA_ROW_ID), 0); 01837 trx_id_pos++; 01838 } 01839 01840 dict_index_add_col(new_index, 01841 dict_table_get_sys_col(table, DATA_TRX_ID), 0); 01842 01843 dict_index_add_col(new_index, 01844 dict_table_get_sys_col(table, DATA_ROLL_PTR), 0); 01845 01846 for (i = 0; i < trx_id_pos; i++) { 01847 01848 fixed_size = dtype_get_fixed_size( 01849 dict_index_get_nth_type(new_index, i)); 01850 01851 if (fixed_size == 0) { 01852 new_index->trx_id_offset = 0; 01853 01854 break; 01855 } 01856 01857 if (dict_index_get_nth_field(new_index, i)->prefix_len 01858 > 0) { 01859 new_index->trx_id_offset = 0; 01860 01861 break; 01862 } 01863 01864 new_index->trx_id_offset += fixed_size; 01865 } 01866 01867 } 01868 01869 /* Set auxiliary variables in table columns as undefined */ 01870 for (i = 0; i < table->n_cols; i++) { 01871 01872 col = dict_table_get_nth_col(table, i); 01873 col->aux = ULINT_UNDEFINED; 01874 } 01875 01876 /* Mark with 0 the table columns already contained in new_index */ 01877 for (i = 0; i < new_index->n_def; i++) { 01878 01879 field = dict_index_get_nth_field(new_index, i); 01880 01881 /* If there is only a prefix of the column in the index 01882 field, do not mark the column as contained in the index */ 01883 01884 if (field->prefix_len == 0) { 01885 01886 field->col->aux = 0; 01887 } 01888 } 01889 01890 /* Add to new_index non-system columns of table not yet included 01891 there */ 01892 for (i = 0; i < table->n_cols - DATA_N_SYS_COLS; i++) { 01893 01894 col = dict_table_get_nth_col(table, i); 01895 ut_ad(col->type.mtype != DATA_SYS); 01896 01897 if (col->aux == ULINT_UNDEFINED) { 01898 dict_index_add_col(new_index, col, 0); 01899 } 01900 } 01901 01902 ut_ad((index->type & DICT_IBUF) 01903 || (UT_LIST_GET_LEN(table->indexes) == 0)); 01904 01905 /* Store to the column structs the position of the table columns 01906 in the clustered index */ 01907 01908 for (i = 0; i < new_index->n_def; i++) { 01909 field = dict_index_get_nth_field(new_index, i); 01910 01911 if (field->prefix_len == 0) { 01912 01913 field->col->clust_pos = i; 01914 } 01915 } 01916 01917 new_index->cached = TRUE; 01918 01919 return(new_index); 01920 } 01921 01922 /*********************************************************************** 01923 Builds the internal dictionary cache representation for a non-clustered 01924 index, containing also system fields not defined by the user. */ 01925 static 01926 dict_index_t* 01927 dict_index_build_internal_non_clust( 01928 /*================================*/ 01929 /* out, own: the internal representation 01930 of the non-clustered index */ 01931 dict_table_t* table, /* in: table */ 01932 dict_index_t* index) /* in: user representation of a non-clustered 01933 index */ 01934 { 01935 dict_field_t* field; 01936 dict_index_t* new_index; 01937 dict_index_t* clust_index; 01938 ulint i; 01939 01940 ut_ad(table && index); 01941 ut_ad(0 == (index->type & DICT_CLUSTERED)); 01942 #ifdef UNIV_SYNC_DEBUG 01943 ut_ad(mutex_own(&(dict_sys->mutex))); 01944 #endif /* UNIV_SYNC_DEBUG */ 01945 ut_ad(table->magic_n == DICT_TABLE_MAGIC_N); 01946 01947 /* The clustered index should be the first in the list of indexes */ 01948 clust_index = UT_LIST_GET_FIRST(table->indexes); 01949 01950 ut_ad(clust_index); 01951 ut_ad(clust_index->type & DICT_CLUSTERED); 01952 ut_ad(!(clust_index->type & DICT_UNIVERSAL)); 01953 01954 /* Create a new index */ 01955 new_index = dict_mem_index_create(table->name, 01956 index->name, index->space, index->type, 01957 index->n_fields + 1 + clust_index->n_uniq); 01958 01959 /* Copy other relevant data from the old index 01960 struct to the new struct: it inherits the values */ 01961 01962 new_index->n_user_defined_cols = index->n_fields; 01963 01964 new_index->id = index->id; 01965 01966 /* Copy fields from index to new_index */ 01967 dict_index_copy(new_index, index, 0, index->n_fields); 01968 01969 /* Set the auxiliary variables in the clust_index unique columns 01970 as undefined */ 01971 for (i = 0; i < clust_index->n_uniq; i++) { 01972 01973 field = dict_index_get_nth_field(clust_index, i); 01974 field->col->aux = ULINT_UNDEFINED; 01975 } 01976 01977 /* Mark with 0 table columns already contained in new_index */ 01978 for (i = 0; i < new_index->n_def; i++) { 01979 01980 field = dict_index_get_nth_field(new_index, i); 01981 01982 /* If there is only a prefix of the column in the index 01983 field, do not mark the column as contained in the index */ 01984 01985 if (field->prefix_len == 0) { 01986 01987 field->col->aux = 0; 01988 } 01989 } 01990 01991 /* Add to new_index the columns necessary to determine the clustered 01992 index entry uniquely */ 01993 01994 for (i = 0; i < clust_index->n_uniq; i++) { 01995 01996 field = dict_index_get_nth_field(clust_index, i); 01997 01998 if (field->col->aux == ULINT_UNDEFINED) { 01999 dict_index_add_col(new_index, field->col, 02000 field->prefix_len); 02001 } 02002 } 02003 02004 if ((index->type) & DICT_UNIQUE) { 02005 new_index->n_uniq = index->n_fields; 02006 } else { 02007 new_index->n_uniq = new_index->n_def; 02008 } 02009 02010 /* Set the n_fields value in new_index to the actual defined 02011 number of fields */ 02012 02013 new_index->n_fields = new_index->n_def; 02014 02015 new_index->cached = TRUE; 02016 02017 return(new_index); 02018 } 02019 02020 /*====================== FOREIGN KEY PROCESSING ========================*/ 02021 02022 /************************************************************************* 02023 Checks if a table is referenced by foreign keys. */ 02024 02025 ibool 02026 dict_table_referenced_by_foreign_key( 02027 /*=================================*/ 02028 /* out: TRUE if table is referenced by a 02029 foreign key */ 02030 dict_table_t* table) /* in: InnoDB table */ 02031 { 02032 if (UT_LIST_GET_LEN(table->referenced_list) > 0) { 02033 02034 return(TRUE); 02035 } 02036 02037 return(FALSE); 02038 } 02039 02040 /************************************************************************* 02041 Frees a foreign key struct. */ 02042 static 02043 void 02044 dict_foreign_free( 02045 /*==============*/ 02046 dict_foreign_t* foreign) /* in, own: foreign key struct */ 02047 { 02048 mem_heap_free(foreign->heap); 02049 } 02050 02051 /************************************************************************** 02052 Removes a foreign constraint struct from the dictionary cache. */ 02053 static 02054 void 02055 dict_foreign_remove_from_cache( 02056 /*===========================*/ 02057 dict_foreign_t* foreign) /* in, own: foreign constraint */ 02058 { 02059 #ifdef UNIV_SYNC_DEBUG 02060 ut_ad(mutex_own(&(dict_sys->mutex))); 02061 #endif /* UNIV_SYNC_DEBUG */ 02062 ut_a(foreign); 02063 02064 if (foreign->referenced_table) { 02065 UT_LIST_REMOVE(referenced_list, 02066 foreign->referenced_table->referenced_list, foreign); 02067 } 02068 02069 if (foreign->foreign_table) { 02070 UT_LIST_REMOVE(foreign_list, 02071 foreign->foreign_table->foreign_list, foreign); 02072 } 02073 02074 dict_foreign_free(foreign); 02075 } 02076 02077 /************************************************************************** 02078 Looks for the foreign constraint from the foreign and referenced lists 02079 of a table. */ 02080 static 02081 dict_foreign_t* 02082 dict_foreign_find( 02083 /*==============*/ 02084 /* out: foreign constraint */ 02085 dict_table_t* table, /* in: table object */ 02086 const char* id) /* in: foreign constraint id */ 02087 { 02088 dict_foreign_t* foreign; 02089 02090 #ifdef UNIV_SYNC_DEBUG 02091 ut_ad(mutex_own(&(dict_sys->mutex))); 02092 #endif /* UNIV_SYNC_DEBUG */ 02093 02094 foreign = UT_LIST_GET_FIRST(table->foreign_list); 02095 02096 while (foreign) { 02097 if (ut_strcmp(id, foreign->id) == 0) { 02098 02099 return(foreign); 02100 } 02101 02102 foreign = UT_LIST_GET_NEXT(foreign_list, foreign); 02103 } 02104 02105 foreign = UT_LIST_GET_FIRST(table->referenced_list); 02106 02107 while (foreign) { 02108 if (ut_strcmp(id, foreign->id) == 0) { 02109 02110 return(foreign); 02111 } 02112 02113 foreign = UT_LIST_GET_NEXT(referenced_list, foreign); 02114 } 02115 02116 return(NULL); 02117 } 02118 02119 #ifndef UNIV_HOTBACKUP 02120 /************************************************************************* 02121 Tries to find an index whose first fields are the columns in the array, 02122 in the same order. */ 02123 static 02124 dict_index_t* 02125 dict_foreign_find_index( 02126 /*====================*/ 02127 /* out: matching index, NULL if not found */ 02128 dict_table_t* table, /* in: table */ 02129 const char** columns,/* in: array of column names */ 02130 ulint n_cols, /* in: number of columns */ 02131 dict_index_t* types_idx, /* in: NULL or an index to whose types the 02132 column types must match */ 02133 ibool check_charsets) /* in: whether to check charsets. 02134 only has an effect if types_idx != 02135 NULL. */ 02136 { 02137 dict_index_t* index; 02138 const char* col_name; 02139 ulint i; 02140 02141 index = dict_table_get_first_index(table); 02142 02143 while (index != NULL) { 02144 if (dict_index_get_n_fields(index) >= n_cols) { 02145 02146 for (i = 0; i < n_cols; i++) { 02147 col_name = dict_index_get_nth_field(index, i) 02148 ->col->name; 02149 if (dict_index_get_nth_field(index, i) 02150 ->prefix_len != 0) { 02151 /* We do not accept column prefix 02152 indexes here */ 02153 02154 break; 02155 } 02156 02157 if (0 != innobase_strcasecmp(columns[i], 02158 col_name)) { 02159 break; 02160 } 02161 02162 if (types_idx && !cmp_types_are_equal( 02163 dict_index_get_nth_type(index, i), 02164 dict_index_get_nth_type(types_idx, i), 02165 check_charsets)) { 02166 02167 break; 02168 } 02169 } 02170 02171 if (i == n_cols) { 02172 /* We found a matching index */ 02173 02174 return(index); 02175 } 02176 } 02177 02178 index = dict_table_get_next_index(index); 02179 } 02180 02181 return(NULL); 02182 } 02183 02184 /************************************************************************** 02185 Report an error in a foreign key definition. */ 02186 static 02187 void 02188 dict_foreign_error_report_low( 02189 /*==========================*/ 02190 FILE* file, /* in: output stream */ 02191 const char* name) /* in: table name */ 02192 { 02193 rewind(file); 02194 ut_print_timestamp(file); 02195 fprintf(file, " Error in foreign key constraint of table %s:\n", 02196 name); 02197 } 02198 02199 /************************************************************************** 02200 Report an error in a foreign key definition. */ 02201 static 02202 void 02203 dict_foreign_error_report( 02204 /*======================*/ 02205 FILE* file, /* in: output stream */ 02206 dict_foreign_t* fk, /* in: foreign key constraint */ 02207 const char* msg) /* in: the error message */ 02208 { 02209 mutex_enter(&dict_foreign_err_mutex); 02210 dict_foreign_error_report_low(file, fk->foreign_table_name); 02211 fputs(msg, file); 02212 fputs(" Constraint:\n", file); 02213 dict_print_info_on_foreign_key_in_create_format(file, NULL, fk, TRUE); 02214 putc('\n', file); 02215 if (fk->foreign_index) { 02216 fputs("The index in the foreign key in table is ", file); 02217 ut_print_name(file, NULL, FALSE, fk->foreign_index->name); 02218 fputs( 02219 "\nSee http://dev.mysql.com/doc/mysql/en/InnoDB_foreign_key_constraints.html\n" 02220 "for correct foreign key definition.\n", 02221 file); 02222 } 02223 mutex_exit(&dict_foreign_err_mutex); 02224 } 02225 02226 /************************************************************************** 02227 Adds a foreign key constraint object to the dictionary cache. May free 02228 the object if there already is an object with the same identifier in. 02229 At least one of the foreign table and the referenced table must already 02230 be in the dictionary cache! */ 02231 02232 ulint 02233 dict_foreign_add_to_cache( 02234 /*======================*/ 02235 /* out: DB_SUCCESS or error code */ 02236 dict_foreign_t* foreign, /* in, own: foreign key constraint */ 02237 ibool check_charsets) /* in: TRUE=check charset 02238 compatibility */ 02239 { 02240 dict_table_t* for_table; 02241 dict_table_t* ref_table; 02242 dict_foreign_t* for_in_cache = NULL; 02243 dict_index_t* index; 02244 ibool added_to_referenced_list= FALSE; 02245 FILE* ef = dict_foreign_err_file; 02246 02247 #ifdef UNIV_SYNC_DEBUG 02248 ut_ad(mutex_own(&(dict_sys->mutex))); 02249 #endif /* UNIV_SYNC_DEBUG */ 02250 02251 for_table = dict_table_check_if_in_cache_low( 02252 foreign->foreign_table_name); 02253 02254 ref_table = dict_table_check_if_in_cache_low( 02255 foreign->referenced_table_name); 02256 ut_a(for_table || ref_table); 02257 02258 if (for_table) { 02259 for_in_cache = dict_foreign_find(for_table, foreign->id); 02260 } 02261 02262 if (!for_in_cache && ref_table) { 02263 for_in_cache = dict_foreign_find(ref_table, foreign->id); 02264 } 02265 02266 if (for_in_cache) { 02267 /* Free the foreign object */ 02268 mem_heap_free(foreign->heap); 02269 } else { 02270 for_in_cache = foreign; 02271 } 02272 02273 if (for_in_cache->referenced_table == NULL && ref_table) { 02274 index = dict_foreign_find_index(ref_table, 02275 (const char**) for_in_cache->referenced_col_names, 02276 for_in_cache->n_fields, 02277 for_in_cache->foreign_index, check_charsets); 02278 02279 if (index == NULL) { 02280 dict_foreign_error_report(ef, for_in_cache, 02281 "there is no index in referenced table which would contain\n" 02282 "the columns as the first columns, or the data types in the\n" 02283 "referenced table do not match to the ones in table."); 02284 02285 if (for_in_cache == foreign) { 02286 mem_heap_free(foreign->heap); 02287 } 02288 02289 return(DB_CANNOT_ADD_CONSTRAINT); 02290 } 02291 02292 for_in_cache->referenced_table = ref_table; 02293 for_in_cache->referenced_index = index; 02294 UT_LIST_ADD_LAST(referenced_list, 02295 ref_table->referenced_list, 02296 for_in_cache); 02297 added_to_referenced_list = TRUE; 02298 } 02299 02300 if (for_in_cache->foreign_table == NULL && for_table) { 02301 index = dict_foreign_find_index(for_table, 02302 (const char**) for_in_cache->foreign_col_names, 02303 for_in_cache->n_fields, 02304 for_in_cache->referenced_index, check_charsets); 02305 02306 if (index == NULL) { 02307 dict_foreign_error_report(ef, for_in_cache, 02308 "there is no index in the table which would contain\n" 02309 "the columns as the first columns, or the data types in the\n" 02310 "table do not match to the ones in the referenced table."); 02311 02312 if (for_in_cache == foreign) { 02313 if (added_to_referenced_list) { 02314 UT_LIST_REMOVE(referenced_list, 02315 ref_table->referenced_list, 02316 for_in_cache); 02317 } 02318 02319 mem_heap_free(foreign->heap); 02320 } 02321 02322 return(DB_CANNOT_ADD_CONSTRAINT); 02323 } 02324 02325 for_in_cache->foreign_table = for_table; 02326 for_in_cache->foreign_index = index; 02327 UT_LIST_ADD_LAST(foreign_list, 02328 for_table->foreign_list, 02329 for_in_cache); 02330 } 02331 02332 return(DB_SUCCESS); 02333 } 02334 02335 /************************************************************************* 02336 Scans from pointer onwards. Stops if is at the start of a copy of 02337 'string' where characters are compared without case sensitivity, and 02338 only outside `` or "" quotes. Stops also at '\0'. */ 02339 02340 const char* 02341 dict_scan_to( 02342 /*=========*/ 02343 /* out: scanned up to this */ 02344 const char* ptr, /* in: scan from */ 02345 const char* string) /* in: look for this */ 02346 { 02347 char quote = '\0'; 02348 02349 for (; *ptr; ptr++) { 02350 if (*ptr == quote) { 02351 /* Closing quote character: do not look for 02352 starting quote or the keyword. */ 02353 quote = '\0'; 02354 } else if (quote) { 02355 /* Within quotes: do nothing. */ 02356 } else if (*ptr == '`' || *ptr == '"') { 02357 /* Starting quote: remember the quote character. */ 02358 quote = *ptr; 02359 } else { 02360 /* Outside quotes: look for the keyword. */ 02361 ulint i; 02362 for (i = 0; string[i]; i++) { 02363 if (toupper((int)(unsigned char)(ptr[i])) 02364 != toupper((int)(unsigned char) 02365 (string[i]))) { 02366 goto nomatch; 02367 } 02368 } 02369 break; 02370 nomatch: 02371 ; 02372 } 02373 } 02374 02375 return(ptr); 02376 } 02377 02378 /************************************************************************* 02379 Accepts a specified string. Comparisons are case-insensitive. */ 02380 static 02381 const char* 02382 dict_accept( 02383 /*========*/ 02384 /* out: if string was accepted, the pointer 02385 is moved after that, else ptr is returned */ 02386 struct charset_info_st* cs,/* in: the character set of ptr */ 02387 const char* ptr, /* in: scan from this */ 02388 const char* string, /* in: accept only this string as the next 02389 non-whitespace string */ 02390 ibool* success)/* out: TRUE if accepted */ 02391 { 02392 const char* old_ptr = ptr; 02393 const char* old_ptr2; 02394 02395 *success = FALSE; 02396 02397 while (my_isspace(cs, *ptr)) { 02398 ptr++; 02399 } 02400 02401 old_ptr2 = ptr; 02402 02403 ptr = dict_scan_to(ptr, string); 02404 02405 if (*ptr == '\0' || old_ptr2 != ptr) { 02406 return(old_ptr); 02407 } 02408 02409 *success = TRUE; 02410 02411 return(ptr + ut_strlen(string)); 02412 } 02413 02414 /************************************************************************* 02415 Scans an id. For the lexical definition of an 'id', see the code below. 02416 Strips backquotes or double quotes from around the id. */ 02417 static 02418 const char* 02419 dict_scan_id( 02420 /*=========*/ 02421 /* out: scanned to */ 02422 struct charset_info_st* cs,/* in: the character set of ptr */ 02423 const char* ptr, /* in: scanned to */ 02424 mem_heap_t* heap, /* in: heap where to allocate the id 02425 (NULL=id will not be allocated, but it 02426 will point to string near ptr) */ 02427 const char** id, /* out,own: the id; NULL if no id was 02428 scannable */ 02429 ibool table_id,/* in: TRUE=convert the allocated id 02430 as a table name; FALSE=convert to UTF-8 */ 02431 ibool accept_also_dot) 02432 /* in: TRUE if also a dot can appear in a 02433 non-quoted id; in a quoted id it can appear 02434 always */ 02435 { 02436 char quote = '\0'; 02437 ulint len = 0; 02438 const char* s; 02439 char* str; 02440 char* dst; 02441 02442 *id = NULL; 02443 02444 while (my_isspace(cs, *ptr)) { 02445 ptr++; 02446 } 02447 02448 if (*ptr == '\0') { 02449 02450 return(ptr); 02451 } 02452 02453 if (*ptr == '`' || *ptr == '"') { 02454 quote = *ptr++; 02455 } 02456 02457 s = ptr; 02458 02459 if (quote) { 02460 for (;;) { 02461 if (!*ptr) { 02462 /* Syntax error */ 02463 return(ptr); 02464 } 02465 if (*ptr == quote) { 02466 ptr++; 02467 if (*ptr != quote) { 02468 break; 02469 } 02470 } 02471 ptr++; 02472 len++; 02473 } 02474 } else { 02475 while (!my_isspace(cs, *ptr) && *ptr != '(' && *ptr != ')' 02476 && (accept_also_dot || *ptr != '.') 02477 && *ptr != ',' && *ptr != '\0') { 02478 02479 ptr++; 02480 } 02481 02482 len = ptr - s; 02483 } 02484 02485 if (UNIV_UNLIKELY(!heap)) { 02486 /* no heap given: id will point to source string */ 02487 *id = s; 02488 return(ptr); 02489 } 02490 02491 if (quote) { 02492 char* d; 02493 str = d = mem_heap_alloc(heap, len + 1); 02494 while (len--) { 02495 if ((*d++ = *s++) == quote) { 02496 s++; 02497 } 02498 } 02499 *d++ = 0; 02500 len = d - str; 02501 ut_ad(*s == quote); 02502 ut_ad(s + 1 == ptr); 02503 } else { 02504 str = mem_heap_strdupl(heap, s, len); 02505 } 02506 02507 if (!table_id) { 02508 convert_id: 02509 /* Convert the identifier from connection character set 02510 to UTF-8. */ 02511 len = 3 * len + 1; 02512 *id = dst = mem_heap_alloc(heap, len); 02513 02514 innobase_convert_from_id(dst, str, len); 02515 } else if (!strncmp(str, srv_mysql50_table_name_prefix, 02516 sizeof srv_mysql50_table_name_prefix)) { 02517 /* This is a pre-5.1 table name 02518 containing chars other than [A-Za-z0-9]. 02519 Discard the prefix and use raw UTF-8 encoding. */ 02520 str += sizeof srv_mysql50_table_name_prefix; 02521 len -= sizeof srv_mysql50_table_name_prefix; 02522 goto convert_id; 02523 } else { 02524 /* Encode using filename-safe characters. */ 02525 len = 5 * len + 1; 02526 *id = dst = mem_heap_alloc(heap, len); 02527 02528 innobase_convert_from_table_id(dst, str, len); 02529 } 02530 02531 return(ptr); 02532 } 02533 02534 /************************************************************************* 02535 Tries to scan a column name. */ 02536 static 02537 const char* 02538 dict_scan_col( 02539 /*==========*/ 02540 /* out: scanned to */ 02541 struct charset_info_st* cs,/* in: the character set of ptr */ 02542 const char* ptr, /* in: scanned to */ 02543 ibool* success,/* out: TRUE if success */ 02544 dict_table_t* table, /* in: table in which the column is */ 02545 dict_col_t** column, /* out: pointer to column if success */ 02546 mem_heap_t* heap, /* in: heap where to allocate the name */ 02547 const char** name) /* out,own: the column name; NULL if no name 02548 was scannable */ 02549 { 02550 dict_col_t* col; 02551 ulint i; 02552 02553 *success = FALSE; 02554 02555 ptr = dict_scan_id(cs, ptr, heap, name, FALSE, TRUE); 02556 02557 if (*name == NULL) { 02558 02559 return(ptr); /* Syntax error */ 02560 } 02561 02562 if (table == NULL) { 02563 *success = TRUE; 02564 *column = NULL; 02565 } else { 02566 for (i = 0; i < dict_table_get_n_cols(table); i++) { 02567 02568 col = dict_table_get_nth_col(table, i); 02569 02570 if (0 == innobase_strcasecmp(col->name, *name)) { 02571 /* Found */ 02572 02573 *success = TRUE; 02574 *column = col; 02575 strcpy((char*) *name, col->name); 02576 02577 break; 02578 } 02579 } 02580 } 02581 02582 return(ptr); 02583 } 02584 02585 /************************************************************************* 02586 Scans a table name from an SQL string. */ 02587 static 02588 const char* 02589 dict_scan_table_name( 02590 /*=================*/ 02591 /* out: scanned to */ 02592 struct charset_info_st* cs,/* in: the character set of ptr */ 02593 const char* ptr, /* in: scanned to */ 02594 dict_table_t** table, /* out: table object or NULL */ 02595 const char* name, /* in: foreign key table name */ 02596 ibool* success,/* out: TRUE if ok name found */ 02597 mem_heap_t* heap, /* in: heap where to allocate the id */ 02598 const char** ref_name)/* out,own: the table name; 02599 NULL if no name was scannable */ 02600 { 02601 const char* database_name = NULL; 02602 ulint database_name_len = 0; 02603 const char* table_name = NULL; 02604 ulint table_name_len; 02605 const char* scan_name; 02606 char* ref; 02607 02608 *success = FALSE; 02609 *table = NULL; 02610 02611 ptr = dict_scan_id(cs, ptr, heap, &scan_name, TRUE, FALSE); 02612 02613 if (scan_name == NULL) { 02614 02615 return(ptr); /* Syntax error */ 02616 } 02617 02618 if (*ptr == '.') { 02619 /* We scanned the database name; scan also the table name */ 02620 02621 ptr++; 02622 02623 database_name = scan_name; 02624 database_name_len = strlen(database_name); 02625 02626 ptr = dict_scan_id(cs, ptr, heap, &table_name, TRUE, FALSE); 02627 02628 if (table_name == NULL) { 02629 02630 return(ptr); /* Syntax error */ 02631 } 02632 } else { 02633 /* To be able to read table dumps made with InnoDB-4.0.17 or 02634 earlier, we must allow the dot separator between the database 02635 name and the table name also to appear within a quoted 02636 identifier! InnoDB used to print a constraint as: 02637 ... REFERENCES `databasename.tablename` ... 02638 starting from 4.0.18 it is 02639 ... REFERENCES `databasename`.`tablename` ... */ 02640 const char* s; 02641 02642 for (s = scan_name; *s; s++) { 02643 if (*s == '.') { 02644 database_name = scan_name; 02645 database_name_len = s - scan_name; 02646 scan_name = ++s; 02647 break;/* to do: multiple dots? */ 02648 } 02649 } 02650 02651 table_name = scan_name; 02652 } 02653 02654 if (database_name == NULL) { 02655 /* Use the database name of the foreign key table */ 02656 02657 database_name = name; 02658 database_name_len = dict_get_db_name_len(name); 02659 } 02660 02661 table_name_len = strlen(table_name); 02662 02663 /* Copy database_name, '/', table_name, '\0' */ 02664 ref = mem_heap_alloc(heap, database_name_len + table_name_len + 2); 02665 memcpy(ref, database_name, database_name_len); 02666 ref[database_name_len] = '/'; 02667 memcpy(ref + database_name_len + 1, table_name, table_name_len + 1); 02668 #ifndef __WIN__ 02669 if (srv_lower_case_table_names) { 02670 #endif /* !__WIN__ */ 02671 /* The table name is always put to lower case on Windows. */ 02672 innobase_casedn_str(ref); 02673 #ifndef __WIN__ 02674 } 02675 #endif /* !__WIN__ */ 02676 02677 *success = TRUE; 02678 *ref_name = ref; 02679 *table = dict_table_get_low(ref); 02680 02681 return(ptr); 02682 } 02683 02684 /************************************************************************* 02685 Skips one id. The id is allowed to contain also '.'. */ 02686 static 02687 const char* 02688 dict_skip_word( 02689 /*===========*/ 02690 /* out: scanned to */ 02691 struct charset_info_st* cs,/* in: the character set of ptr */ 02692 const char* ptr, /* in: scanned to */ 02693 ibool* success)/* out: TRUE if success, FALSE if just spaces 02694 left in string or a syntax error */ 02695 { 02696 const char* start; 02697 02698 *success = FALSE; 02699 02700 ptr = dict_scan_id(cs, ptr, NULL, &start, FALSE, TRUE); 02701 02702 if (start) { 02703 *success = TRUE; 02704 } 02705 02706 return(ptr); 02707 } 02708 02709 /************************************************************************* 02710 Removes MySQL comments from an SQL string. A comment is either 02711 (a) '#' to the end of the line, 02712 (b) '--<space>' to the end of the line, or 02713 (c) '<slash><asterisk>' till the next '<asterisk><slash>' (like the familiar 02714 C comment syntax). */ 02715 static 02716 char* 02717 dict_strip_comments( 02718 /*================*/ 02719 /* out, own: SQL string stripped from 02720 comments; the caller must free this 02721 with mem_free()! */ 02722 const char* sql_string) /* in: SQL string */ 02723 { 02724 char* str; 02725 const char* sptr; 02726 char* ptr; 02727 /* unclosed quote character (0 if none) */ 02728 char quote = 0; 02729 02730 str = mem_alloc(strlen(sql_string) + 1); 02731 02732 sptr = sql_string; 02733 ptr = str; 02734 02735 for (;;) { 02736 scan_more: 02737 if (*sptr == '\0') { 02738 *ptr = '\0'; 02739 02740 ut_a(ptr <= str + strlen(sql_string)); 02741 02742 return(str); 02743 } 02744 02745 if (*sptr == quote) { 02746 /* Closing quote character: do not look for 02747 starting quote or comments. */ 02748 quote = 0; 02749 } else if (quote) { 02750 /* Within quotes: do not look for 02751 starting quotes or comments. */ 02752 } else if (*sptr == '"' || *sptr == '`') { 02753 /* Starting quote: remember the quote character. */ 02754 quote = *sptr; 02755 } else if (*sptr == '#' 02756 || (sptr[0] == '-' && sptr[1] == '-' && 02757 sptr[2] == ' ')) { 02758 for (;;) { 02759 /* In Unix a newline is 0x0A while in Windows 02760 it is 0x0D followed by 0x0A */ 02761 02762 if (*sptr == (char)0x0A 02763 || *sptr == (char)0x0D 02764 || *sptr == '\0') { 02765 02766 goto scan_more; 02767 } 02768 02769 sptr++; 02770 } 02771 } else if (!quote && *sptr == '/' && *(sptr + 1) == '*') { 02772 for (;;) { 02773 if (*sptr == '*' && *(sptr + 1) == '/') { 02774 02775 sptr += 2; 02776 02777 goto scan_more; 02778 } 02779 02780 if (*sptr == '\0') { 02781 02782 goto scan_more; 02783 } 02784 02785 sptr++; 02786 } 02787 } 02788 02789 *ptr = *sptr; 02790 02791 ptr++; 02792 sptr++; 02793 } 02794 } 02795 02796 /************************************************************************* 02797 Finds the highest <number> for foreign key constraints of the table. Looks 02798 only at the >= 4.0.18-format id's, which are of the form 02799 databasename/tablename_ibfk_<number>. */ 02800 static 02801 ulint 02802 dict_table_get_highest_foreign_id( 02803 /*==============================*/ 02804 /* out: highest number, 0 if table has no new 02805 format foreign key constraints */ 02806 dict_table_t* table) /* in: table in the dictionary memory cache */ 02807 { 02808 dict_foreign_t* foreign; 02809 char* endp; 02810 ulint biggest_id = 0; 02811 ulint id; 02812 ulint len; 02813 02814 ut_a(table); 02815 02816 len = ut_strlen(table->name); 02817 foreign = UT_LIST_GET_FIRST(table->foreign_list); 02818 02819 while (foreign) { 02820 if (ut_strlen(foreign->id) > ((sizeof dict_ibfk) - 1) + len 02821 && 0 == ut_memcmp(foreign->id, table->name, len) 02822 && 0 == ut_memcmp(foreign->id + len, 02823 dict_ibfk, (sizeof dict_ibfk) - 1) 02824 && foreign->id[len + ((sizeof dict_ibfk) - 1)] != '0') { 02825 /* It is of the >= 4.0.18 format */ 02826 02827 id = strtoul(foreign->id + len + ((sizeof dict_ibfk) - 1), 02828 &endp, 10); 02829 if (*endp == '\0') { 02830 ut_a(id != biggest_id); 02831 02832 if (id > biggest_id) { 02833 biggest_id = id; 02834 } 02835 } 02836 } 02837 02838 foreign = UT_LIST_GET_NEXT(foreign_list, foreign); 02839 } 02840 02841 return(biggest_id); 02842 } 02843 02844 /************************************************************************* 02845 Reports a simple foreign key create clause syntax error. */ 02846 static 02847 void 02848 dict_foreign_report_syntax_err( 02849 /*===========================*/ 02850 const char* name, /* in: table name */ 02851 const char* start_of_latest_foreign, 02852 /* in: start of the foreign key clause 02853 in the SQL string */ 02854 const char* ptr) /* in: place of the syntax error */ 02855 { 02856 FILE* ef = dict_foreign_err_file; 02857 02858 mutex_enter(&dict_foreign_err_mutex); 02859 dict_foreign_error_report_low(ef, name); 02860 fprintf(ef, "%s:\nSyntax error close to:\n%s\n", 02861 start_of_latest_foreign, ptr); 02862 mutex_exit(&dict_foreign_err_mutex); 02863 } 02864 02865 /************************************************************************* 02866 Scans a table create SQL string and adds to the data dictionary the foreign 02867 key constraints declared in the string. This function should be called after 02868 the indexes for a table have been created. Each foreign key constraint must 02869 be accompanied with indexes in both participating tables. The indexes are 02870 allowed to contain more fields than mentioned in the constraint. */ 02871 static 02872 ulint 02873 dict_create_foreign_constraints_low( 02874 /*================================*/ 02875 /* out: error code or DB_SUCCESS */ 02876 trx_t* trx, /* in: transaction */ 02877 mem_heap_t* heap, /* in: memory heap */ 02878 struct charset_info_st* cs,/* in: the character set of sql_string */ 02879 const char* sql_string, 02880 /* in: CREATE TABLE or ALTER TABLE statement 02881 where foreign keys are declared like: 02882 FOREIGN KEY (a, b) REFERENCES table2(c, d), 02883 table2 can be written also with the database 02884 name before it: test.table2; the default 02885 database is the database of parameter name */ 02886 const char* name, /* in: table full name in the normalized form 02887 database_name/table_name */ 02888 ibool reject_fks) 02889 /* in: if TRUE, fail with error code 02890 DB_CANNOT_ADD_CONSTRAINT if any foreign 02891 keys are found. */ 02892 { 02893 dict_table_t* table; 02894 dict_table_t* referenced_table; 02895 dict_table_t* table_to_alter; 02896 ulint highest_id_so_far = 0; 02897 dict_index_t* index; 02898 dict_foreign_t* foreign; 02899 const char* ptr = sql_string; 02900 const char* start_of_latest_foreign = sql_string; 02901 FILE* ef = dict_foreign_err_file; 02902 const char* constraint_name; 02903 ibool success; 02904 ulint error; 02905 const char* ptr1; 02906 const char* ptr2; 02907 ulint i; 02908 ulint j; 02909 ibool is_on_delete; 02910 ulint n_on_deletes; 02911 ulint n_on_updates; 02912 dict_col_t* columns[500]; 02913 const char* column_names[500]; 02914 const char* referenced_table_name; 02915 02916 #ifdef UNIV_SYNC_DEBUG 02917 ut_ad(mutex_own(&(dict_sys->mutex))); 02918 #endif /* UNIV_SYNC_DEBUG */ 02919 02920 table = dict_table_get_low(name); 02921 02922 if (table == NULL) { 02923 mutex_enter(&dict_foreign_err_mutex); 02924 dict_foreign_error_report_low(ef, name); 02925 fprintf(ef, 02926 "Cannot find the table in the internal data dictionary of InnoDB.\n" 02927 "Create table statement:\n%s\n", sql_string); 02928 mutex_exit(&dict_foreign_err_mutex); 02929 02930 return(DB_ERROR); 02931 } 02932 02933 /* First check if we are actually doing an ALTER TABLE, and in that 02934 case look for the table being altered */ 02935 02936 ptr = dict_accept(cs, ptr, "ALTER", &success); 02937 02938 if (!success) { 02939 02940 goto loop; 02941 } 02942 02943 ptr = dict_accept(cs, ptr, "TABLE", &success); 02944 02945 if (!success) { 02946 02947 goto loop; 02948 } 02949 02950 /* We are doing an ALTER TABLE: scan the table name we are altering */ 02951 02952 ptr = dict_scan_table_name(cs, ptr, &table_to_alter, name, 02953 &success, heap, &referenced_table_name); 02954 if (!success) { 02955 fprintf(stderr, 02956 "InnoDB: Error: could not find the table being ALTERED in:\n%s\n", sql_string); 02957 02958 return(DB_ERROR); 02959 } 02960 02961 /* Starting from 4.0.18 and 4.1.2, we generate foreign key id's in the 02962 format databasename/tablename_ibfk_<number>, where <number> is local 02963 to the table; look for the highest <number> for table_to_alter, so 02964 that we can assign to new constraints higher numbers. */ 02965 02966 /* If we are altering a temporary table, the table name after ALTER 02967 TABLE does not correspond to the internal table name, and 02968 table_to_alter is NULL. TODO: should we fix this somehow? */ 02969 02970 if (table_to_alter == NULL) { 02971 highest_id_so_far = 0; 02972 } else { 02973 highest_id_so_far = dict_table_get_highest_foreign_id( 02974 table_to_alter); 02975 } 02976 02977 /* Scan for foreign key declarations in a loop */ 02978 loop: 02979 /* Scan either to "CONSTRAINT" or "FOREIGN", whichever is closer */ 02980 02981 ptr1 = dict_scan_to(ptr, "CONSTRAINT"); 02982 ptr2 = dict_scan_to(ptr, "FOREIGN"); 02983 02984 constraint_name = NULL; 02985 02986 if (ptr1 < ptr2) { 02987 /* The user may have specified a constraint name. Pick it so 02988 that we can store 'databasename/constraintname' as the id of 02989 of the constraint to system tables. */ 02990 ptr = ptr1; 02991 02992 ptr = dict_accept(cs, ptr, "CONSTRAINT", &success); 02993 02994 ut_a(success); 02995 02996 if (!my_isspace(cs, *ptr) && *ptr != '"' && *ptr != '`') { 02997 goto loop; 02998 } 02999 03000 while (my_isspace(cs, *ptr)) { 03001 ptr++; 03002 } 03003 03004 /* read constraint name unless got "CONSTRAINT FOREIGN" */ 03005 if (ptr != ptr2) { 03006 ptr = dict_scan_id(cs, ptr, heap, 03007 &constraint_name, FALSE, FALSE); 03008 } 03009 } else { 03010 ptr = ptr2; 03011 } 03012 03013 if (*ptr == '\0') { 03014 /* The proper way to reject foreign keys for temporary 03015 tables would be to split the lexing and syntactical 03016 analysis of foreign key clauses from the actual adding 03017 of them, so that ha_innodb.cc could first parse the SQL 03018 command, determine if there are any foreign keys, and 03019 if so, immediately reject the command if the table is a 03020 temporary one. For now, this kludge will work. */ 03021 if (reject_fks && (UT_LIST_GET_LEN(table->foreign_list) > 0)) { 03022 03023 return(DB_CANNOT_ADD_CONSTRAINT); 03024 } 03025 03026 /**********************************************************/ 03027 /* The following call adds the foreign key constraints 03028 to the data dictionary system tables on disk */ 03029 03030 error = dict_create_add_foreigns_to_dictionary( 03031 highest_id_so_far, table, trx); 03032 return(error); 03033 } 03034 03035 start_of_latest_foreign = ptr; 03036 03037 ptr = dict_accept(cs, ptr, "FOREIGN", &success); 03038 03039 if (!success) { 03040 goto loop; 03041 } 03042 03043 if (!my_isspace(cs, *ptr)) { 03044 goto loop; 03045 } 03046 03047 ptr = dict_accept(cs, ptr, "KEY", &success); 03048 03049 if (!success) { 03050 goto loop; 03051 } 03052 03053 ptr = dict_accept(cs, ptr, "(", &success); 03054 03055 if (!success) { 03056 /* MySQL allows also an index id before the '('; we 03057 skip it */ 03058 ptr = dict_skip_word(cs, ptr, &success); 03059 03060 if (!success) { 03061 dict_foreign_report_syntax_err(name, 03062 start_of_latest_foreign, ptr); 03063 03064 return(DB_CANNOT_ADD_CONSTRAINT); 03065 } 03066 03067 ptr = dict_accept(cs, ptr, "(", &success); 03068 03069 if (!success) { 03070 /* We do not flag a syntax error here because in an 03071 ALTER TABLE we may also have DROP FOREIGN KEY abc */ 03072 03073 goto loop; 03074 } 03075 } 03076 03077 i = 0; 03078 03079 /* Scan the columns in the first list */ 03080 col_loop1: 03081 ut_a(i < (sizeof column_names) / sizeof *column_names); 03082 ptr = dict_scan_col(cs, ptr, &success, table, columns + i, 03083 heap, column_names + i); 03084 if (!success) { 03085 mutex_enter(&dict_foreign_err_mutex); 03086 dict_foreign_error_report_low(ef, name); 03087 fprintf(ef, "%s:\nCannot resolve column name close to:\n%s\n", 03088 start_of_latest_foreign, ptr); 03089 mutex_exit(&dict_foreign_err_mutex); 03090 03091 return(DB_CANNOT_ADD_CONSTRAINT); 03092 } 03093 03094 i++; 03095 03096 ptr = dict_accept(cs, ptr, ",", &success); 03097 03098 if (success) { 03099 goto col_loop1; 03100 } 03101 03102 ptr = dict_accept(cs, ptr, ")", &success); 03103 03104 if (!success) { 03105 dict_foreign_report_syntax_err(name, start_of_latest_foreign, 03106 ptr); 03107 return(DB_CANNOT_ADD_CONSTRAINT); 03108 } 03109 03110 /* Try to find an index which contains the columns 03111 as the first fields and in the right order */ 03112 03113 index = dict_foreign_find_index(table, column_names, i, NULL, TRUE); 03114 03115 if (!index) { 03116 mutex_enter(&dict_foreign_err_mutex); 03117 dict_foreign_error_report_low(ef, name); 03118 fputs("There is no index in table ", ef); 03119 ut_print_name(ef, NULL, TRUE, name); 03120 fprintf(ef, " where the columns appear\n" 03121 "as the first columns. Constraint:\n%s\n" 03122 "See http://dev.mysql.com/doc/mysql/en/InnoDB_foreign_key_constraints.html\n" 03123 "for correct foreign key definition.\n", 03124 start_of_latest_foreign); 03125 mutex_exit(&dict_foreign_err_mutex); 03126 03127 return(DB_CANNOT_ADD_CONSTRAINT); 03128 } 03129 ptr = dict_accept(cs, ptr, "REFERENCES", &success); 03130 03131 if (!success || !my_isspace(cs, *ptr)) { 03132 dict_foreign_report_syntax_err(name, start_of_latest_foreign, 03133 ptr); 03134 return(DB_CANNOT_ADD_CONSTRAINT); 03135 } 03136 03137 /* Let us create a constraint struct */ 03138 03139 foreign = dict_mem_foreign_create(); 03140 03141 if (constraint_name) { 03142 ulint db_len; 03143 03144 /* Catenate 'databasename/' to the constraint name specified 03145 by the user: we conceive the constraint as belonging to the 03146 same MySQL 'database' as the table itself. We store the name 03147 to foreign->id. */ 03148 03149 db_len = dict_get_db_name_len(table->name); 03150 03151 foreign->id = mem_heap_alloc(foreign->heap, 03152 db_len + strlen(constraint_name) + 2); 03153 03154 ut_memcpy(foreign->id, table->name, db_len); 03155 foreign->id[db_len] = '/'; 03156 strcpy(foreign->id + db_len + 1, constraint_name); 03157 } 03158 03159 foreign->foreign_table = table; 03160 foreign->foreign_table_name = mem_heap_strdup(foreign->heap, 03161 table->name); 03162 foreign->foreign_index = index; 03163 foreign->n_fields = i; 03164 foreign->foreign_col_names = mem_heap_alloc(foreign->heap, 03165 i * sizeof(void*)); 03166 for (i = 0; i < foreign->n_fields; i++) { 03167 foreign->foreign_col_names[i] = 03168 mem_heap_strdup(foreign->heap, columns[i]->name); 03169 } 03170 03171 ptr = dict_scan_table_name(cs, ptr, &referenced_table, name, 03172 &success, heap, &referenced_table_name); 03173 03174 /* Note that referenced_table can be NULL if the user has suppressed 03175 checking of foreign key constraints! */ 03176 03177 if (!success || (!referenced_table && trx->check_foreigns)) { 03178 dict_foreign_free(foreign); 03179 03180 mutex_enter(&dict_foreign_err_mutex); 03181 dict_foreign_error_report_low(ef, name); 03182 fprintf(ef, "%s:\nCannot resolve table name close to:\n" 03183 "%s\n", 03184 start_of_latest_foreign, ptr); 03185 mutex_exit(&dict_foreign_err_mutex); 03186 03187 return(DB_CANNOT_ADD_CONSTRAINT); 03188 } 03189 03190 ptr = dict_accept(cs, ptr, "(", &success); 03191 03192 if (!success) { 03193 dict_foreign_free(foreign); 03194 dict_foreign_report_syntax_err(name, start_of_latest_foreign, 03195 ptr); 03196 return(DB_CANNOT_ADD_CONSTRAINT); 03197 } 03198 03199 /* Scan the columns in the second list */ 03200 i = 0; 03201 03202 col_loop2: 03203 ptr = dict_scan_col(cs, ptr, &success, referenced_table, columns + i, 03204 heap, column_names + i); 03205 i++; 03206 03207 if (!success) { 03208 dict_foreign_free(foreign); 03209 03210 mutex_enter(&dict_foreign_err_mutex); 03211 dict_foreign_error_report_low(ef, name); 03212 fprintf(ef, "%s:\nCannot resolve column name close to:\n" 03213 "%s\n", 03214 start_of_latest_foreign, ptr); 03215 mutex_exit(&dict_foreign_err_mutex); 03216 03217 return(DB_CANNOT_ADD_CONSTRAINT); 03218 } 03219 03220 ptr = dict_accept(cs, ptr, ",", &success); 03221 03222 if (success) { 03223 goto col_loop2; 03224 } 03225 03226 ptr = dict_accept(cs, ptr, ")", &success); 03227 03228 if (!success || foreign->n_fields != i) { 03229 dict_foreign_free(foreign); 03230 03231 dict_foreign_report_syntax_err(name, start_of_latest_foreign, 03232 ptr); 03233 return(DB_CANNOT_ADD_CONSTRAINT); 03234 } 03235 03236 n_on_deletes = 0; 03237 n_on_updates = 0; 03238 03239 scan_on_conditions: 03240 /* Loop here as long as we can find ON ... conditions */ 03241 03242 ptr = dict_accept(cs, ptr, "ON", &success); 03243 03244 if (!success) { 03245 03246 goto try_find_index; 03247 } 03248 03249 ptr = dict_accept(cs, ptr, "DELETE", &success); 03250 03251 if (!success) { 03252 ptr = dict_accept(cs, ptr, "UPDATE", &success); 03253 03254 if (!success) { 03255 dict_foreign_free(foreign); 03256 03257 dict_foreign_report_syntax_err(name, 03258 start_of_latest_foreign, ptr); 03259 return(DB_CANNOT_ADD_CONSTRAINT); 03260 } 03261 03262 is_on_delete = FALSE; 03263 n_on_updates++; 03264 } else { 03265 is_on_delete = TRUE; 03266 n_on_deletes++; 03267 } 03268 03269 ptr = dict_accept(cs, ptr, "RESTRICT", &success); 03270 03271 if (success) { 03272 goto scan_on_conditions; 03273 } 03274 03275 ptr = dict_accept(cs, ptr, "CASCADE", &success); 03276 03277 if (success) { 03278 if (is_on_delete) { 03279 foreign->type |= DICT_FOREIGN_ON_DELETE_CASCADE; 03280 } else { 03281 foreign->type |= DICT_FOREIGN_ON_UPDATE_CASCADE; 03282 } 03283 03284 goto scan_on_conditions; 03285 } 03286 03287 ptr = dict_accept(cs, ptr, "NO", &success); 03288 03289 if (success) { 03290 ptr = dict_accept(cs, ptr, "ACTION", &success); 03291 03292 if (!success) { 03293 dict_foreign_free(foreign); 03294 dict_foreign_report_syntax_err(name, 03295 start_of_latest_foreign, ptr); 03296 03297 return(DB_CANNOT_ADD_CONSTRAINT); 03298 } 03299 03300 if (is_on_delete) { 03301 foreign->type |= DICT_FOREIGN_ON_DELETE_NO_ACTION; 03302 } else { 03303 foreign->type |= DICT_FOREIGN_ON_UPDATE_NO_ACTION; 03304 } 03305 03306 goto scan_on_conditions; 03307 } 03308 03309 ptr = dict_accept(cs, ptr, "SET", &success); 03310 03311 if (!success) { 03312 dict_foreign_free(foreign); 03313 dict_foreign_report_syntax_err(name, start_of_latest_foreign, 03314 ptr); 03315 return(DB_CANNOT_ADD_CONSTRAINT); 03316 } 03317 03318 ptr = dict_accept(cs, ptr, "NULL", &success); 03319 03320 if (!success) { 03321 dict_foreign_free(foreign); 03322 dict_foreign_report_syntax_err(name, start_of_latest_foreign, 03323 ptr); 03324 return(DB_CANNOT_ADD_CONSTRAINT); 03325 } 03326 03327 for (j = 0; j < foreign->n_fields; j++) { 03328 if ((dict_index_get_nth_type( 03329 foreign->foreign_index, j)->prtype) 03330 & DATA_NOT_NULL) { 03331 03332 /* It is not sensible to define SET NULL 03333 if the column is not allowed to be NULL! */ 03334 03335 dict_foreign_free(foreign); 03336 03337 mutex_enter(&dict_foreign_err_mutex); 03338 dict_foreign_error_report_low(ef, name); 03339 fprintf(ef, "%s:\n" 03340 "You have defined a SET NULL condition though some of the\n" 03341 "columns are defined as NOT NULL.\n", start_of_latest_foreign); 03342 mutex_exit(&dict_foreign_err_mutex); 03343 03344 return(DB_CANNOT_ADD_CONSTRAINT); 03345 } 03346 } 03347 03348 if (is_on_delete) { 03349 foreign->type |= DICT_FOREIGN_ON_DELETE_SET_NULL; 03350 } else { 03351 foreign->type |= DICT_FOREIGN_ON_UPDATE_SET_NULL; 03352 } 03353 03354 goto scan_on_conditions; 03355 03356 try_find_index: 03357 if (n_on_deletes > 1 || n_on_updates > 1) { 03358 /* It is an error to define more than 1 action */ 03359 03360 dict_foreign_free(foreign); 03361 03362 mutex_enter(&dict_foreign_err_mutex); 03363 dict_foreign_error_report_low(ef, name); 03364 fprintf(ef, "%s:\n" 03365 "You have twice an ON DELETE clause or twice an ON UPDATE clause.\n", 03366 start_of_latest_foreign); 03367 mutex_exit(&dict_foreign_err_mutex); 03368 03369 return(DB_CANNOT_ADD_CONSTRAINT); 03370 } 03371 03372 /* Try to find an index which contains the columns as the first fields 03373 and in the right order, and the types are the same as in 03374 foreign->foreign_index */ 03375 03376 if (referenced_table) { 03377 index = dict_foreign_find_index(referenced_table, 03378 column_names, i, foreign->foreign_index, TRUE); 03379 if (!index) { 03380 dict_foreign_free(foreign); 03381 mutex_enter(&dict_foreign_err_mutex); 03382 dict_foreign_error_report_low(ef, name); 03383 fprintf(ef, "%s:\n" 03384 "Cannot find an index in the referenced table where the\n" 03385 "referenced columns appear as the first columns, or column types\n" 03386 "in the table and the referenced table do not match for constraint.\n" 03387 "Note that the internal storage type of ENUM and SET changed in\n" 03388 "tables created with >= InnoDB-4.1.12, and such columns in old tables\n" 03389 "cannot be referenced by such columns in new tables.\n" 03390 "See http://dev.mysql.com/doc/mysql/en/InnoDB_foreign_key_constraints.html\n" 03391 "for correct foreign key definition.\n", 03392 start_of_latest_foreign); 03393 mutex_exit(&dict_foreign_err_mutex); 03394 03395 return(DB_CANNOT_ADD_CONSTRAINT); 03396 } 03397 } else { 03398 ut_a(trx->check_foreigns == FALSE); 03399 index = NULL; 03400 } 03401 03402 foreign->referenced_index = index; 03403 foreign->referenced_table = referenced_table; 03404 03405 foreign->referenced_table_name = mem_heap_strdup(foreign->heap, 03406 referenced_table_name); 03407 03408 foreign->referenced_col_names = mem_heap_alloc(foreign->heap, 03409 i * sizeof(void*)); 03410 for (i = 0; i < foreign->n_fields; i++) { 03411 foreign->referenced_col_names[i] 03412 = mem_heap_strdup(foreign->heap, column_names[i]); 03413 } 03414 03415 /* We found an ok constraint definition: add to the lists */ 03416 03417 UT_LIST_ADD_LAST(foreign_list, table->foreign_list, foreign); 03418 03419 if (referenced_table) { 03420 UT_LIST_ADD_LAST(referenced_list, 03421 referenced_table->referenced_list, 03422 foreign); 03423 } 03424 03425 goto loop; 03426 } 03427 03428 /************************************************************************** 03429 Determines whether a string starts with the specified keyword. */ 03430 03431 ibool 03432 dict_str_starts_with_keyword( 03433 /*=========================*/ 03434 /* out: TRUE if str starts 03435 with keyword */ 03436 void* mysql_thd, /* in: MySQL thread handle */ 03437 const char* str, /* in: string to scan for keyword */ 03438 const char* keyword) /* in: keyword to look for */ 03439 { 03440 struct charset_info_st* cs = innobase_get_charset(mysql_thd); 03441 ibool success; 03442 03443 dict_accept(cs, str, keyword, &success); 03444 return(success); 03445 } 03446 03447 /************************************************************************* 03448 Scans a table create SQL string and adds to the data dictionary the foreign 03449 key constraints declared in the string. This function should be called after 03450 the indexes for a table have been created. Each foreign key constraint must 03451 be accompanied with indexes in both participating tables. The indexes are 03452 allowed to contain more fields than mentioned in the constraint. */ 03453 03454 ulint 03455 dict_create_foreign_constraints( 03456 /*============================*/ 03457 /* out: error code or DB_SUCCESS */ 03458 trx_t* trx, /* in: transaction */ 03459 const char* sql_string, /* in: table create statement where 03460 foreign keys are declared like: 03461 FOREIGN KEY (a, b) REFERENCES 03462 table2(c, d), table2 can be written 03463 also with the database 03464 name before it: test.table2; the 03465 default database id the database of 03466 parameter name */ 03467 const char* name, /* in: table full name in the 03468 normalized form 03469 database_name/table_name */ 03470 ibool reject_fks) /* in: if TRUE, fail with error 03471 code DB_CANNOT_ADD_CONSTRAINT if 03472 any foreign keys are found. */ 03473 { 03474 char* str; 03475 ulint err; 03476 mem_heap_t* heap; 03477 03478 ut_a(trx && trx->mysql_thd); 03479 03480 str = dict_strip_comments(sql_string); 03481 heap = mem_heap_create(10000); 03482 03483 err = dict_create_foreign_constraints_low(trx, heap, 03484 innobase_get_charset(trx->mysql_thd), 03485 str, name, reject_fks); 03486 03487 mem_heap_free(heap); 03488 mem_free(str); 03489 03490 return(err); 03491 } 03492 03493 /************************************************************************** 03494 Parses the CONSTRAINT id's to be dropped in an ALTER TABLE statement. */ 03495 03496 ulint 03497 dict_foreign_parse_drop_constraints( 03498 /*================================*/ 03499 /* out: DB_SUCCESS or 03500 DB_CANNOT_DROP_CONSTRAINT if 03501 syntax error or the constraint 03502 id does not match */ 03503 mem_heap_t* heap, /* in: heap from which we can 03504 allocate memory */ 03505 trx_t* trx, /* in: transaction */ 03506 dict_table_t* table, /* in: table */ 03507 ulint* n, /* out: number of constraints 03508 to drop */ 03509 const char*** constraints_to_drop) /* out: id's of the 03510 constraints to drop */ 03511 { 03512 dict_foreign_t* foreign; 03513 ibool success; 03514 char* str; 03515 const char* ptr; 03516 const char* id; 03517 FILE* ef = dict_foreign_err_file; 03518 struct charset_info_st* cs; 03519 03520 ut_a(trx && trx->mysql_thd); 03521 03522 cs = innobase_get_charset(trx->mysql_thd); 03523 03524 *n = 0; 03525 03526 *constraints_to_drop = mem_heap_alloc(heap, 1000 * sizeof(char*)); 03527 03528 str = dict_strip_comments(*(trx->mysql_query_str)); 03529 ptr = str; 03530 03531 #ifdef UNIV_SYNC_DEBUG 03532 ut_ad(mutex_own(&(dict_sys->mutex))); 03533 #endif /* UNIV_SYNC_DEBUG */ 03534 loop: 03535 ptr = dict_scan_to(ptr, "DROP"); 03536 03537 if (*ptr == '\0') { 03538 mem_free(str); 03539 03540 return(DB_SUCCESS); 03541 } 03542 03543 ptr = dict_accept(cs, ptr, "DROP", &success); 03544 03545 if (!my_isspace(cs, *ptr)) { 03546 03547 goto loop; 03548 } 03549 03550 ptr = dict_accept(cs, ptr, "FOREIGN", &success); 03551 03552 if (!success) { 03553 03554 goto loop; 03555 } 03556 03557 ptr = dict_accept(cs, ptr, "KEY", &success); 03558 03559 if (!success) { 03560 03561 goto syntax_error; 03562 } 03563 03564 ptr = dict_scan_id(cs, ptr, heap, &id, FALSE, TRUE); 03565 03566 if (id == NULL) { 03567 03568 goto syntax_error; 03569 } 03570 03571 ut_a(*n < 1000); 03572 (*constraints_to_drop)[*n] = id; 03573 (*n)++; 03574 03575 /* Look for the given constraint id */ 03576 03577 foreign = UT_LIST_GET_FIRST(table->foreign_list); 03578 03579 while (foreign != NULL) { 03580 if (0 == strcmp(foreign->id, id) 03581 || (strchr(foreign->id, '/') 03582 && 0 == strcmp(id, 03583 dict_remove_db_name(foreign->id)))) { 03584 /* Found */ 03585 break; 03586 } 03587 03588 foreign = UT_LIST_GET_NEXT(foreign_list, foreign); 03589 } 03590 03591 if (foreign == NULL) { 03592 mutex_enter(&dict_foreign_err_mutex); 03593 rewind(ef); 03594 ut_print_timestamp(ef); 03595 fputs( 03596 " Error in dropping of a foreign key constraint of table ", ef); 03597 ut_print_name(ef, NULL, TRUE, table->name); 03598 fputs(",\n" 03599 "in SQL command\n", ef); 03600 fputs(str, ef); 03601 fputs("\nCannot find a constraint with the given id ", ef); 03602 ut_print_name(ef, NULL, FALSE, id); 03603 fputs(".\n", ef); 03604 mutex_exit(&dict_foreign_err_mutex); 03605 03606 mem_free(str); 03607 03608 return(DB_CANNOT_DROP_CONSTRAINT); 03609 } 03610 03611 goto loop; 03612 03613 syntax_error: 03614 mutex_enter(&dict_foreign_err_mutex); 03615 rewind(ef); 03616 ut_print_timestamp(ef); 03617 fputs( 03618 " Syntax error in dropping of a foreign key constraint of table ", ef); 03619 ut_print_name(ef, NULL, TRUE, table->name); 03620 fprintf(ef, ",\n" 03621 "close to:\n%s\n in SQL command\n%s\n", ptr, str); 03622 mutex_exit(&dict_foreign_err_mutex); 03623 03624 mem_free(str); 03625 03626 return(DB_CANNOT_DROP_CONSTRAINT); 03627 } 03628 #endif /* UNIV_HOTBACKUP */ 03629 03630 /*==================== END OF FOREIGN KEY PROCESSING ====================*/ 03631 03632 /************************************************************************** 03633 Returns an index object if it is found in the dictionary cache. */ 03634 03635 dict_index_t* 03636 dict_index_get_if_in_cache( 03637 /*=======================*/ 03638 /* out: index, NULL if not found */ 03639 dulint index_id) /* in: index id */ 03640 { 03641 dict_table_t* table; 03642 dict_index_t* index; 03643 03644 if (dict_sys == NULL) { 03645 return(NULL); 03646 } 03647 03648 mutex_enter(&(dict_sys->mutex)); 03649 03650 table = UT_LIST_GET_FIRST(dict_sys->table_LRU); 03651 03652 while (table) { 03653 index = UT_LIST_GET_FIRST(table->indexes); 03654 03655 while (index) { 03656 if (0 == ut_dulint_cmp(index->id, index_id)) { 03657 03658 goto found; 03659 } 03660 03661 index = UT_LIST_GET_NEXT(indexes, index); 03662 } 03663 03664 table = UT_LIST_GET_NEXT(table_LRU, table); 03665 } 03666 03667 index = NULL; 03668 found: 03669 mutex_exit(&(dict_sys->mutex)); 03670 03671 return(index); 03672 } 03673 03674 /************************************************************************** 03675 Creates an index tree struct. */ 03676 03677 dict_tree_t* 03678 dict_tree_create( 03679 /*=============*/ 03680 /* out, own: created tree */ 03681 dict_index_t* index, /* in: the index for which to create: in the 03682 case of a mixed tree, this should be the 03683 index of the cluster object */ 03684 ulint page_no)/* in: root page number of the index */ 03685 { 03686 dict_tree_t* tree; 03687 03688 tree = mem_alloc(sizeof(dict_tree_t)); 03689 03690 /* Inherit info from the index */ 03691 03692 tree->type = index->type; 03693 tree->space = index->space; 03694 tree->page = page_no; 03695 03696 tree->id = index->id; 03697 03698 tree->tree_index = NULL; 03699 03700 tree->magic_n = DICT_TREE_MAGIC_N; 03701 03702 rw_lock_create(&tree->lock, SYNC_INDEX_TREE); 03703 03704 return(tree); 03705 } 03706 03707 /************************************************************************** 03708 Frees an index tree struct. */ 03709 03710 void 03711 dict_tree_free( 03712 /*===========*/ 03713 dict_tree_t* tree) /* in, own: index tree */ 03714 { 03715 ut_a(tree); 03716 ut_ad(tree->magic_n == DICT_TREE_MAGIC_N); 03717 03718 rw_lock_free(&(tree->lock)); 03719 mem_free(tree); 03720 } 03721 03722 #ifdef UNIV_DEBUG 03723 /************************************************************************** 03724 Checks that a tuple has n_fields_cmp value in a sensible range, so that 03725 no comparison can occur with the page number field in a node pointer. */ 03726 03727 ibool 03728 dict_tree_check_search_tuple( 03729 /*=========================*/ 03730 /* out: TRUE if ok */ 03731 dict_tree_t* tree, /* in: index tree */ 03732 dtuple_t* tuple) /* in: tuple used in a search */ 03733 { 03734 dict_index_t* index = tree->tree_index; 03735 03736 ut_a(index); 03737 ut_a(dtuple_get_n_fields_cmp(tuple) 03738 <= dict_index_get_n_unique_in_tree(index)); 03739 return(TRUE); 03740 } 03741 #endif /* UNIV_DEBUG */ 03742 03743 /************************************************************************** 03744 Builds a node pointer out of a physical record and a page number. */ 03745 03746 dtuple_t* 03747 dict_tree_build_node_ptr( 03748 /*=====================*/ 03749 /* out, own: node pointer */ 03750 dict_tree_t* tree, /* in: index tree */ 03751 rec_t* rec, /* in: record for which to build node 03752 pointer */ 03753 ulint page_no,/* in: page number to put in node pointer */ 03754 mem_heap_t* heap, /* in: memory heap where pointer created */ 03755 ulint level) /* in: level of rec in tree: 0 means leaf 03756 level */ 03757 { 03758 dtuple_t* tuple; 03759 dict_index_t* ind; 03760 dfield_t* field; 03761 byte* buf; 03762 ulint n_unique; 03763 03764 ind = tree->tree_index; 03765 03766 if (UNIV_UNLIKELY(tree->type & DICT_UNIVERSAL)) { 03767 /* In a universal index tree, we take the whole record as 03768 the node pointer if the reord is on the leaf level, 03769 on non-leaf levels we remove the last field, which 03770 contains the page number of the child page */ 03771 03772 ut_a(!dict_table_is_comp(ind->table)); 03773 n_unique = rec_get_n_fields_old(rec); 03774 03775 if (level > 0) { 03776 ut_a(n_unique > 1); 03777 n_unique--; 03778 } 03779 } else { 03780 n_unique = dict_index_get_n_unique_in_tree(ind); 03781 } 03782 03783 tuple = dtuple_create(heap, n_unique + 1); 03784 03785 /* When searching in the tree for the node pointer, we must not do 03786 comparison on the last field, the page number field, as on upper 03787 levels in the tree there may be identical node pointers with a 03788 different page number; therefore, we set the n_fields_cmp to one 03789 less: */ 03790 03791 dtuple_set_n_fields_cmp(tuple, n_unique); 03792 03793 dict_index_copy_types(tuple, ind, n_unique); 03794 03795 buf = mem_heap_alloc(heap, 4); 03796 03797 mach_write_to_4(buf, page_no); 03798 03799 field = dtuple_get_nth_field(tuple, n_unique); 03800 dfield_set_data(field, buf, 4); 03801 03802 dtype_set(dfield_get_type(field), DATA_SYS_CHILD, DATA_NOT_NULL, 4, 0); 03803 03804 rec_copy_prefix_to_dtuple(tuple, rec, ind, n_unique, heap); 03805 dtuple_set_info_bits(tuple, dtuple_get_info_bits(tuple) | 03806 REC_STATUS_NODE_PTR); 03807 03808 ut_ad(dtuple_check_typed(tuple)); 03809 03810 return(tuple); 03811 } 03812 03813 /************************************************************************** 03814 Copies an initial segment of a physical record, long enough to specify an 03815 index entry uniquely. */ 03816 03817 rec_t* 03818 dict_tree_copy_rec_order_prefix( 03819 /*============================*/ 03820 /* out: pointer to the prefix record */ 03821 dict_tree_t* tree, /* in: index tree */ 03822 rec_t* rec, /* in: record for which to copy prefix */ 03823 ulint* n_fields,/* out: number of fields copied */ 03824 byte** buf, /* in/out: memory buffer for the copied prefix, 03825 or NULL */ 03826 ulint* buf_size)/* in/out: buffer size */ 03827 { 03828 dict_index_t* index; 03829 ulint n; 03830 03831 UNIV_PREFETCH_R(rec); 03832 index = tree->tree_index; 03833 03834 if (UNIV_UNLIKELY(tree->type & DICT_UNIVERSAL)) { 03835 ut_a(!dict_table_is_comp(index->table)); 03836 n = rec_get_n_fields_old(rec); 03837 } else { 03838 n = dict_index_get_n_unique_in_tree(index); 03839 } 03840 03841 *n_fields = n; 03842 return(rec_copy_prefix_to_buf(rec, index, n, buf, buf_size)); 03843 } 03844 03845 /************************************************************************** 03846 Builds a typed data tuple out of a physical record. */ 03847 03848 dtuple_t* 03849 dict_tree_build_data_tuple( 03850 /*=======================*/ 03851 /* out, own: data tuple */ 03852 dict_tree_t* tree, /* in: index tree */ 03853 rec_t* rec, /* in: record for which to build data tuple */ 03854 ulint n_fields,/* in: number of data fields */ 03855 mem_heap_t* heap) /* in: memory heap where tuple created */ 03856 { 03857 dtuple_t* tuple; 03858 dict_index_t* ind; 03859 03860 ind = tree->tree_index; 03861 03862 ut_ad(dict_table_is_comp(ind->table) 03863 || n_fields <= rec_get_n_fields_old(rec)); 03864 03865 tuple = dtuple_create(heap, n_fields); 03866 03867 dict_index_copy_types(tuple, ind, n_fields); 03868 03869 rec_copy_prefix_to_dtuple(tuple, rec, ind, n_fields, heap); 03870 03871 ut_ad(dtuple_check_typed(tuple)); 03872 03873 return(tuple); 03874 } 03875 03876 /************************************************************************* 03877 Calculates the minimum record length in an index. */ 03878 03879 ulint 03880 dict_index_calc_min_rec_len( 03881 /*========================*/ 03882 dict_index_t* index) /* in: index */ 03883 { 03884 ulint sum = 0; 03885 ulint i; 03886 03887 if (dict_table_is_comp(index->table)) { 03888 ulint nullable = 0; 03889 sum = REC_N_NEW_EXTRA_BYTES; 03890 for (i = 0; i < dict_index_get_n_fields(index); i++) { 03891 dtype_t*t = dict_index_get_nth_type(index, i); 03892 ulint size = dtype_get_fixed_size(t); 03893 sum += size; 03894 if (!size) { 03895 size = dtype_get_len(t); 03896 sum += size < 128 ? 1 : 2; 03897 } 03898 if (!(dtype_get_prtype(t) & DATA_NOT_NULL)) 03899 nullable++; 03900 } 03901 03902 /* round the NULL flags up to full bytes */ 03903 sum += (nullable + 7) / 8; 03904 03905 return(sum); 03906 } 03907 03908 for (i = 0; i < dict_index_get_n_fields(index); i++) { 03909 sum += dtype_get_fixed_size(dict_index_get_nth_type(index, i)); 03910 } 03911 03912 if (sum > 127) { 03913 sum += 2 * dict_index_get_n_fields(index); 03914 } else { 03915 sum += dict_index_get_n_fields(index); 03916 } 03917 03918 sum += REC_N_OLD_EXTRA_BYTES; 03919 03920 return(sum); 03921 } 03922 03923 /************************************************************************* 03924 Calculates new estimates for table and index statistics. The statistics 03925 are used in query optimization. */ 03926 03927 void 03928 dict_update_statistics_low( 03929 /*=======================*/ 03930 dict_table_t* table, /* in: table */ 03931 ibool has_dict_mutex __attribute__((unused))) 03932 /* in: TRUE if the caller has the 03933 dictionary mutex */ 03934 { 03935 dict_index_t* index; 03936 ulint size; 03937 ulint sum_of_index_sizes = 0; 03938 03939 if (table->ibd_file_missing) { 03940 ut_print_timestamp(stderr); 03941 fprintf(stderr, 03942 " InnoDB: cannot calculate statistics for table %s\n" 03943 "InnoDB: because the .ibd file is missing. For help, please refer to\n" 03944 "InnoDB: " 03945 "http://dev.mysql.com/doc/mysql/en/InnoDB_troubleshooting_datadict.html\n", 03946 table->name); 03947 03948 return; 03949 } 03950 03951 /* If we have set a high innodb_force_recovery level, do not calculate 03952 statistics, as a badly corrupted index can cause a crash in it. */ 03953 03954 if (srv_force_recovery >= SRV_FORCE_NO_IBUF_MERGE) { 03955 03956 return; 03957 } 03958 03959 /* Find out the sizes of the indexes and how many different values 03960 for the key they approximately have */ 03961 03962 index = dict_table_get_first_index(table); 03963 03964 if (index == NULL) { 03965 /* Table definition is corrupt */ 03966 03967 return; 03968 } 03969 03970 while (index) { 03971 size = btr_get_size(index, BTR_TOTAL_SIZE); 03972 03973 index->stat_index_size = size; 03974 03975 sum_of_index_sizes += size; 03976 03977 size = btr_get_size(index, BTR_N_LEAF_PAGES); 03978 03979 if (size == 0) { 03980 /* The root node of the tree is a leaf */ 03981 size = 1; 03982 } 03983 03984 index->stat_n_leaf_pages = size; 03985 03986 btr_estimate_number_of_different_key_vals(index); 03987 03988 index = dict_table_get_next_index(index); 03989 } 03990 03991 index = dict_table_get_first_index(table); 03992 03993 table->stat_n_rows = index->stat_n_diff_key_vals[ 03994 dict_index_get_n_unique(index)]; 03995 03996 table->stat_clustered_index_size = index->stat_index_size; 03997 03998 table->stat_sum_of_other_index_sizes = sum_of_index_sizes 03999 - index->stat_index_size; 04000 04001 table->stat_initialized = TRUE; 04002 04003 table->stat_modified_counter = 0; 04004 } 04005 04006 /************************************************************************* 04007 Calculates new estimates for table and index statistics. The statistics 04008 are used in query optimization. */ 04009 04010 void 04011 dict_update_statistics( 04012 /*===================*/ 04013 dict_table_t* table) /* in: table */ 04014 { 04015 dict_update_statistics_low(table, FALSE); 04016 } 04017 04018 /************************************************************************** 04019 A noninlined version of dict_table_get_low. */ 04020 04021 dict_table_t* 04022 dict_table_get_low_noninlined( 04023 /*==========================*/ 04024 /* out: table, NULL if not found */ 04025 const char* table_name) /* in: table name */ 04026 { 04027 return(dict_table_get_low(table_name)); 04028 } 04029 04030 /************************************************************************** 04031 Prints info of a foreign key constraint. */ 04032 static 04033 void 04034 dict_foreign_print_low( 04035 /*===================*/ 04036 dict_foreign_t* foreign) /* in: foreign key constraint */ 04037 { 04038 ulint i; 04039 04040 #ifdef UNIV_SYNC_DEBUG 04041 ut_ad(mutex_own(&(dict_sys->mutex))); 04042 #endif /* UNIV_SYNC_DEBUG */ 04043 04044 fprintf(stderr, " FOREIGN KEY CONSTRAINT %s: %s (", 04045 foreign->id, foreign->foreign_table_name); 04046 04047 for (i = 0; i < foreign->n_fields; i++) { 04048 fprintf(stderr, " %s", foreign->foreign_col_names[i]); 04049 } 04050 04051 fprintf(stderr, " )\n" 04052 " REFERENCES %s (", 04053 foreign->referenced_table_name); 04054 04055 for (i = 0; i < foreign->n_fields; i++) { 04056 fprintf(stderr, " %s", foreign->referenced_col_names[i]); 04057 } 04058 04059 fputs(" )\n", stderr); 04060 } 04061 04062 /************************************************************************** 04063 Prints a table data. */ 04064 04065 void 04066 dict_table_print( 04067 /*=============*/ 04068 dict_table_t* table) /* in: table */ 04069 { 04070 mutex_enter(&(dict_sys->mutex)); 04071 dict_table_print_low(table); 04072 mutex_exit(&(dict_sys->mutex)); 04073 } 04074 04075 /************************************************************************** 04076 Prints a table data when we know the table name. */ 04077 04078 void 04079 dict_table_print_by_name( 04080 /*=====================*/ 04081 const char* name) 04082 { 04083 dict_table_t* table; 04084 04085 mutex_enter(&(dict_sys->mutex)); 04086 04087 table = dict_table_get_low(name); 04088 04089 ut_a(table); 04090 04091 dict_table_print_low(table); 04092 mutex_exit(&(dict_sys->mutex)); 04093 } 04094 04095 /************************************************************************** 04096 Prints a table data. */ 04097 04098 void 04099 dict_table_print_low( 04100 /*=================*/ 04101 dict_table_t* table) /* in: table */ 04102 { 04103 dict_index_t* index; 04104 dict_foreign_t* foreign; 04105 ulint i; 04106 04107 #ifdef UNIV_SYNC_DEBUG 04108 ut_ad(mutex_own(&(dict_sys->mutex))); 04109 #endif /* UNIV_SYNC_DEBUG */ 04110 04111 dict_update_statistics_low(table, TRUE); 04112 04113 fprintf(stderr, 04114 "--------------------------------------\n" 04115 "TABLE: name %s, id %lu %lu, columns %lu, indexes %lu, appr.rows %lu\n" 04116 " COLUMNS: ", 04117 table->name, 04118 (ulong) ut_dulint_get_high(table->id), 04119 (ulong) ut_dulint_get_low(table->id), 04120 (ulong) table->n_cols, 04121 (ulong) UT_LIST_GET_LEN(table->indexes), 04122 (ulong) table->stat_n_rows); 04123 04124 for (i = 0; i < table->n_cols - 1; i++) { 04125 dict_col_print_low(dict_table_get_nth_col(table, i)); 04126 fputs("; ", stderr); 04127 } 04128 04129 putc('\n', stderr); 04130 04131 index = UT_LIST_GET_FIRST(table->indexes); 04132 04133 while (index != NULL) { 04134 dict_index_print_low(index); 04135 index = UT_LIST_GET_NEXT(indexes, index); 04136 } 04137 04138 foreign = UT_LIST_GET_FIRST(table->foreign_list); 04139 04140 while (foreign != NULL) { 04141 dict_foreign_print_low(foreign); 04142 foreign = UT_LIST_GET_NEXT(foreign_list, foreign); 04143 } 04144 04145 foreign = UT_LIST_GET_FIRST(table->referenced_list); 04146 04147 while (foreign != NULL) { 04148 dict_foreign_print_low(foreign); 04149 foreign = UT_LIST_GET_NEXT(referenced_list, foreign); 04150 } 04151 } 04152 04153 /************************************************************************** 04154 Prints a column data. */ 04155 static 04156 void 04157 dict_col_print_low( 04158 /*===============*/ 04159 dict_col_t* col) /* in: column */ 04160 { 04161 dtype_t* type; 04162 04163 #ifdef UNIV_SYNC_DEBUG 04164 ut_ad(mutex_own(&(dict_sys->mutex))); 04165 #endif /* UNIV_SYNC_DEBUG */ 04166 04167 type = dict_col_get_type(col); 04168 fprintf(stderr, "%s: ", col->name); 04169 04170 dtype_print(type); 04171 } 04172 04173 /************************************************************************** 04174 Prints an index data. */ 04175 static 04176 void 04177 dict_index_print_low( 04178 /*=================*/ 04179 dict_index_t* index) /* in: index */ 04180 { 04181 dict_tree_t* tree; 04182 ib_longlong n_vals; 04183 ulint i; 04184 04185 #ifdef UNIV_SYNC_DEBUG 04186 ut_ad(mutex_own(&(dict_sys->mutex))); 04187 #endif /* UNIV_SYNC_DEBUG */ 04188 04189 tree = index->tree; 04190 04191 if (index->n_user_defined_cols > 0) { 04192 n_vals = index->stat_n_diff_key_vals[ 04193 index->n_user_defined_cols]; 04194 } else { 04195 n_vals = index->stat_n_diff_key_vals[1]; 04196 } 04197 04198 fprintf(stderr, 04199 " INDEX: name %s, id %lu %lu, fields %lu/%lu, uniq %lu, type %lu\n" 04200 " root page %lu, appr.key vals %lu," 04201 " leaf pages %lu, size pages %lu\n" 04202 " FIELDS: ", 04203 index->name, 04204 (ulong) ut_dulint_get_high(tree->id), 04205 (ulong) ut_dulint_get_low(tree->id), 04206 (ulong) index->n_user_defined_cols, 04207 (ulong) index->n_fields, 04208 (ulong) index->n_uniq, 04209 (ulong) index->type, 04210 (ulong) tree->page, 04211 (ulong) n_vals, 04212 (ulong) index->stat_n_leaf_pages, 04213 (ulong) index->stat_index_size); 04214 04215 for (i = 0; i < index->n_fields; i++) { 04216 dict_field_print_low(dict_index_get_nth_field(index, i)); 04217 } 04218 04219 putc('\n', stderr); 04220 04221 #ifdef UNIV_BTR_PRINT 04222 btr_print_size(tree); 04223 04224 btr_print_tree(tree, 7); 04225 #endif /* UNIV_BTR_PRINT */ 04226 } 04227 04228 /************************************************************************** 04229 Prints a field data. */ 04230 static 04231 void 04232 dict_field_print_low( 04233 /*=================*/ 04234 dict_field_t* field) /* in: field */ 04235 { 04236 #ifdef UNIV_SYNC_DEBUG 04237 ut_ad(mutex_own(&(dict_sys->mutex))); 04238 #endif /* UNIV_SYNC_DEBUG */ 04239 fprintf(stderr, " %s", field->name); 04240 04241 if (field->prefix_len != 0) { 04242 fprintf(stderr, "(%lu)", (ulong) field->prefix_len); 04243 } 04244 } 04245 04246 /************************************************************************** 04247 Outputs info on a foreign key of a table in a format suitable for 04248 CREATE TABLE. */ 04249 04250 void 04251 dict_print_info_on_foreign_key_in_create_format( 04252 /*============================================*/ 04253 FILE* file, /* in: file where to print */ 04254 trx_t* trx, /* in: transaction */ 04255 dict_foreign_t* foreign, /* in: foreign key constraint */ 04256 ibool add_newline) /* in: whether to add a newline */ 04257 { 04258 const char* stripped_id; 04259 ulint i; 04260 04261 if (strchr(foreign->id, '/')) { 04262 /* Strip the preceding database name from the constraint id */ 04263 stripped_id = foreign->id + 1 04264 + dict_get_db_name_len(foreign->id); 04265 } else { 04266 stripped_id = foreign->id; 04267 } 04268 04269 putc(',', file); 04270 04271 if (add_newline) { 04272 /* SHOW CREATE TABLE wants constraints each printed nicely 04273 on its own line, while error messages want no newlines 04274 inserted. */ 04275 fputs("\n ", file); 04276 } 04277 04278 fputs(" CONSTRAINT ", file); 04279 ut_print_name(file, trx, FALSE, stripped_id); 04280 fputs(" FOREIGN KEY (", file); 04281 04282 for (i = 0;;) { 04283 ut_print_name(file, trx, FALSE, foreign->foreign_col_names[i]); 04284 if (++i < foreign->n_fields) { 04285 fputs(", ", file); 04286 } else { 04287 break; 04288 } 04289 } 04290 04291 fputs(") REFERENCES ", file); 04292 04293 if (dict_tables_have_same_db(foreign->foreign_table_name, 04294 foreign->referenced_table_name)) { 04295 /* Do not print the database name of the referenced table */ 04296 ut_print_name(file, trx, TRUE, dict_remove_db_name( 04297 foreign->referenced_table_name)); 04298 } else { 04299 /* Look for the '/' in the table name */ 04300 04301 i = 0; 04302 while (foreign->referenced_table_name[i] != '/') { 04303 i++; 04304 } 04305 04306 ut_print_namel(file, trx, TRUE, 04307 foreign->referenced_table_name, i); 04308 putc('.', file); 04309 ut_print_name(file, trx, TRUE, 04310 foreign->referenced_table_name + i + 1); 04311 } 04312 04313 putc(' ', file); 04314 putc('(', file); 04315 04316 for (i = 0;;) { 04317 ut_print_name(file, trx, FALSE, 04318 foreign->referenced_col_names[i]); 04319 if (++i < foreign->n_fields) { 04320 fputs(", ", file); 04321 } else { 04322 break; 04323 } 04324 } 04325 04326 putc(')', file); 04327 04328 if (foreign->type & DICT_FOREIGN_ON_DELETE_CASCADE) { 04329 fputs(" ON DELETE CASCADE", file); 04330 } 04331 04332 if (foreign->type & DICT_FOREIGN_ON_DELETE_SET_NULL) { 04333 fputs(" ON DELETE SET NULL", file); 04334 } 04335 04336 if (foreign->type & DICT_FOREIGN_ON_DELETE_NO_ACTION) { 04337 fputs(" ON DELETE NO ACTION", file); 04338 } 04339 04340 if (foreign->type & DICT_FOREIGN_ON_UPDATE_CASCADE) { 04341 fputs(" ON UPDATE CASCADE", file); 04342 } 04343 04344 if (foreign->type & DICT_FOREIGN_ON_UPDATE_SET_NULL) { 04345 fputs(" ON UPDATE SET NULL", file); 04346 } 04347 04348 if (foreign->type & DICT_FOREIGN_ON_UPDATE_NO_ACTION) { 04349 fputs(" ON UPDATE NO ACTION", file); 04350 } 04351 } 04352 04353 /************************************************************************** 04354 Outputs info on foreign keys of a table. */ 04355 04356 void 04357 dict_print_info_on_foreign_keys( 04358 /*============================*/ 04359 ibool create_table_format, /* in: if TRUE then print in 04360 a format suitable to be inserted into 04361 a CREATE TABLE, otherwise in the format 04362 of SHOW TABLE STATUS */ 04363 FILE* file, /* in: file where to print */ 04364 trx_t* trx, /* in: transaction */ 04365 dict_table_t* table) /* in: table */ 04366 { 04367 dict_foreign_t* foreign; 04368 04369 mutex_enter(&(dict_sys->mutex)); 04370 04371 foreign = UT_LIST_GET_FIRST(table->foreign_list); 04372 04373 if (foreign == NULL) { 04374 mutex_exit(&(dict_sys->mutex)); 04375 04376 return; 04377 } 04378 04379 while (foreign != NULL) { 04380 if (create_table_format) { 04381 dict_print_info_on_foreign_key_in_create_format( 04382 file, trx, foreign, TRUE); 04383 } else { 04384 ulint i; 04385 fputs("; (", file); 04386 04387 for (i = 0; i < foreign->n_fields; i++) { 04388 if (i) { 04389 putc(' ', file); 04390 } 04391 04392 ut_print_name(file, trx, FALSE, 04393 foreign->foreign_col_names[i]); 04394 } 04395 04396 fputs(") REFER ", file); 04397 ut_print_name(file, trx, TRUE, 04398 foreign->referenced_table_name); 04399 putc('(', file); 04400 04401 for (i = 0; i < foreign->n_fields; i++) { 04402 if (i) { 04403 putc(' ', file); 04404 } 04405 ut_print_name(file, trx, FALSE, 04406 foreign->referenced_col_names[i]); 04407 } 04408 04409 putc(')', file); 04410 04411 if (foreign->type == DICT_FOREIGN_ON_DELETE_CASCADE) { 04412 fputs(" ON DELETE CASCADE", file); 04413 } 04414 04415 if (foreign->type == DICT_FOREIGN_ON_DELETE_SET_NULL) { 04416 fputs(" ON DELETE SET NULL", file); 04417 } 04418 04419 if (foreign->type & DICT_FOREIGN_ON_DELETE_NO_ACTION) { 04420 fputs(" ON DELETE NO ACTION", file); 04421 } 04422 04423 if (foreign->type & DICT_FOREIGN_ON_UPDATE_CASCADE) { 04424 fputs(" ON UPDATE CASCADE", file); 04425 } 04426 04427 if (foreign->type & DICT_FOREIGN_ON_UPDATE_SET_NULL) { 04428 fputs(" ON UPDATE SET NULL", file); 04429 } 04430 04431 if (foreign->type & DICT_FOREIGN_ON_UPDATE_NO_ACTION) { 04432 fputs(" ON UPDATE NO ACTION", file); 04433 } 04434 } 04435 04436 foreign = UT_LIST_GET_NEXT(foreign_list, foreign); 04437 } 04438 04439 mutex_exit(&(dict_sys->mutex)); 04440 } 04441 04442 /************************************************************************ 04443 Displays the names of the index and the table. */ 04444 void 04445 dict_index_name_print( 04446 /*==================*/ 04447 FILE* file, /* in: output stream */ 04448 trx_t* trx, /* in: transaction */ 04449 const dict_index_t* index) /* in: index to print */ 04450 { 04451 fputs("index ", file); 04452 ut_print_name(file, trx, FALSE, index->name); 04453 fputs(" of table ", file); 04454 ut_print_name(file, trx, TRUE, index->table_name); 04455 }
1.4.7

