1
by brian
clean slate |
1 |
/**********************************************************************
|
2 |
Data dictionary system
|
|
3 |
||
4 |
(c) 1996 Innobase Oy
|
|
5 |
||
6 |
Created 1/8/1996 Heikki Tuuri
|
|
7 |
***********************************************************************/
|
|
8 |
||
9 |
#include "dict0dict.h" |
|
10 |
||
11 |
#ifdef UNIV_NONINL
|
|
12 |
#include "dict0dict.ic" |
|
13 |
#endif
|
|
14 |
||
15 |
#include "buf0buf.h" |
|
16 |
#include "data0type.h" |
|
17 |
#include "mach0data.h" |
|
18 |
#include "dict0boot.h" |
|
19 |
#include "dict0mem.h" |
|
20 |
#include "dict0crea.h" |
|
21 |
#include "trx0undo.h" |
|
22 |
#include "btr0btr.h" |
|
23 |
#include "btr0cur.h" |
|
24 |
#include "btr0sea.h" |
|
25 |
#include "pars0pars.h" |
|
26 |
#include "pars0sym.h" |
|
27 |
#include "que0que.h" |
|
28 |
#include "rem0cmp.h" |
|
29 |
#ifndef UNIV_HOTBACKUP
|
|
30 |
# include "m_ctype.h" /* my_isspace() */ |
|
31 |
#endif /* !UNIV_HOTBACKUP */ |
|
32 |
||
33 |
#include <ctype.h> |
|
34 |
||
35 |
dict_sys_t* dict_sys = NULL; /* the dictionary system */ |
|
36 |
||
37 |
rw_lock_t dict_operation_lock; /* table create, drop, etc. reserve |
|
38 |
this in X-mode; implicit or backround
|
|
39 |
operations purge, rollback, foreign
|
|
40 |
key checks reserve this in S-mode; we
|
|
41 |
cannot trust that MySQL protects
|
|
42 |
implicit or background operations
|
|
43 |
a table drop since MySQL does not
|
|
44 |
know of them; therefore we need this;
|
|
45 |
NOTE: a transaction which reserves
|
|
46 |
this must keep book on the mode in
|
|
47 |
trx->dict_operation_lock_mode */
|
|
48 |
||
49 |
#define DICT_HEAP_SIZE 100 /* initial memory heap size when |
|
50 |
creating a table or index object */
|
|
51 |
#define DICT_POOL_PER_TABLE_HASH 512 /* buffer pool max size per table |
|
52 |
hash table fixed size in bytes */
|
|
53 |
#define DICT_POOL_PER_VARYING 4 /* buffer pool max size per data |
|
54 |
dictionary varying size in bytes */
|
|
55 |
||
56 |
/* Identifies generated InnoDB foreign key names */
|
|
57 |
static char dict_ibfk[] = "_ibfk_"; |
|
58 |
||
59 |
#ifndef UNIV_HOTBACKUP
|
|
60 |
/**********************************************************************
|
|
61 |
Converts an identifier to a table name.
|
|
62 |
||
63 |
NOTE: the prototype of this function is copied from ha_innodb.cc! If you change
|
|
64 |
this function, you MUST change also the prototype here! */
|
|
65 |
extern
|
|
66 |
void
|
|
67 |
innobase_convert_from_table_id( |
|
68 |
/*===========================*/
|
|
69 |
char* to, /* out: converted identifier */ |
|
70 |
const char* from, /* in: identifier to convert */ |
|
71 |
ulint len); /* in: length of 'to', in bytes; |
|
72 |
should be at least 5 * strlen(to) + 1 */
|
|
73 |
/**********************************************************************
|
|
74 |
Converts an identifier to UTF-8.
|
|
75 |
||
76 |
NOTE: the prototype of this function is copied from ha_innodb.cc! If you change
|
|
77 |
this function, you MUST change also the prototype here! */
|
|
78 |
extern
|
|
79 |
void
|
|
80 |
innobase_convert_from_id( |
|
81 |
/*=====================*/
|
|
82 |
char* to, /* out: converted identifier */ |
|
83 |
const char* from, /* in: identifier to convert */ |
|
84 |
ulint len); /* in: length of 'to', in bytes; |
|
85 |
should be at least 3 * strlen(to) + 1 */
|
|
86 |
/**********************************************************************
|
|
87 |
Compares NUL-terminated UTF-8 strings case insensitively.
|
|
88 |
||
89 |
NOTE: the prototype of this function is copied from ha_innodb.cc! If you change
|
|
90 |
this function, you MUST change also the prototype here! */
|
|
91 |
extern
|
|
92 |
int
|
|
93 |
innobase_strcasecmp( |
|
94 |
/*================*/
|
|
95 |
/* out: 0 if a=b, <0 if a<b, >1 if a>b */
|
|
96 |
const char* a, /* in: first string to compare */ |
|
97 |
const char* b); /* in: second string to compare */ |
|
98 |
||
99 |
/**********************************************************************
|
|
100 |
Makes all characters in a NUL-terminated UTF-8 string lower case.
|
|
101 |
||
102 |
NOTE: the prototype of this function is copied from ha_innodb.cc! If you change
|
|
103 |
this function, you MUST change also the prototype here! */
|
|
104 |
extern
|
|
105 |
void
|
|
106 |
innobase_casedn_str( |
|
107 |
/*================*/
|
|
108 |
char* a); /* in/out: string to put in lower case */ |
|
109 |
||
110 |
/**************************************************************************
|
|
111 |
Determines the connection character set.
|
|
112 |
||
113 |
NOTE: the prototype of this function is copied from ha_innodb.cc! If you change
|
|
114 |
this function, you MUST change also the prototype here! */
|
|
115 |
struct charset_info_st* |
|
116 |
innobase_get_charset( |
|
117 |
/*=================*/
|
|
118 |
/* out: connection character set */
|
|
119 |
void* mysql_thd); /* in: MySQL thread handle */ |
|
120 |
#endif /* !UNIV_HOTBACKUP */ |
|
121 |
||
122 |
/**************************************************************************
|
|
123 |
Removes an index from the dictionary cache. */
|
|
124 |
static
|
|
125 |
void
|
|
126 |
dict_index_remove_from_cache( |
|
127 |
/*=========================*/
|
|
128 |
dict_table_t* table, /* in: table */ |
|
129 |
dict_index_t* index); /* in, own: index */ |
|
130 |
/***********************************************************************
|
|
131 |
Copies fields contained in index2 to index1. */
|
|
132 |
static
|
|
133 |
void
|
|
134 |
dict_index_copy( |
|
135 |
/*============*/
|
|
136 |
dict_index_t* index1, /* in: index to copy to */ |
|
137 |
dict_index_t* index2, /* in: index to copy from */ |
|
138 |
dict_table_t* table, /* in: table */ |
|
139 |
ulint start, /* in: first position to copy */ |
|
140 |
ulint end); /* in: last position to copy */ |
|
141 |
/***********************************************************************
|
|
142 |
Tries to find column names for the index and sets the col field of the
|
|
143 |
index. */
|
|
144 |
static
|
|
145 |
void
|
|
146 |
dict_index_find_cols( |
|
147 |
/*=================*/
|
|
148 |
dict_table_t* table, /* in: table */ |
|
149 |
dict_index_t* index); /* in: index */ |
|
150 |
/***********************************************************************
|
|
151 |
Builds the internal dictionary cache representation for a clustered
|
|
152 |
index, containing also system fields not defined by the user. */
|
|
153 |
static
|
|
154 |
dict_index_t* |
|
155 |
dict_index_build_internal_clust( |
|
156 |
/*============================*/
|
|
157 |
/* out, own: the internal representation
|
|
158 |
of the clustered index */
|
|
159 |
dict_table_t* table, /* in: table */ |
|
160 |
dict_index_t* index); /* in: user representation of a clustered |
|
161 |
index */
|
|
162 |
/***********************************************************************
|
|
163 |
Builds the internal dictionary cache representation for a non-clustered
|
|
164 |
index, containing also system fields not defined by the user. */
|
|
165 |
static
|
|
166 |
dict_index_t* |
|
167 |
dict_index_build_internal_non_clust( |
|
168 |
/*================================*/
|
|
169 |
/* out, own: the internal representation
|
|
170 |
of the non-clustered index */
|
|
171 |
dict_table_t* table, /* in: table */ |
|
172 |
dict_index_t* index); /* in: user representation of a non-clustered |
|
173 |
index */
|
|
174 |
/**************************************************************************
|
|
175 |
Removes a foreign constraint struct from the dictionary cache. */
|
|
176 |
static
|
|
177 |
void
|
|
178 |
dict_foreign_remove_from_cache( |
|
179 |
/*===========================*/
|
|
180 |
dict_foreign_t* foreign); /* in, own: foreign constraint */ |
|
181 |
/**************************************************************************
|
|
182 |
Prints a column data. */
|
|
183 |
static
|
|
184 |
void
|
|
185 |
dict_col_print_low( |
|
186 |
/*===============*/
|
|
187 |
const dict_table_t* table, /* in: table */ |
|
188 |
const dict_col_t* col); /* in: column */ |
|
189 |
/**************************************************************************
|
|
190 |
Prints an index data. */
|
|
191 |
static
|
|
192 |
void
|
|
193 |
dict_index_print_low( |
|
194 |
/*=================*/
|
|
195 |
dict_index_t* index); /* in: index */ |
|
196 |
/**************************************************************************
|
|
197 |
Prints a field data. */
|
|
198 |
static
|
|
199 |
void
|
|
200 |
dict_field_print_low( |
|
201 |
/*=================*/
|
|
202 |
dict_field_t* field); /* in: field */ |
|
203 |
/*************************************************************************
|
|
204 |
Frees a foreign key struct. */
|
|
205 |
static
|
|
206 |
void
|
|
207 |
dict_foreign_free( |
|
208 |
/*==============*/
|
|
209 |
dict_foreign_t* foreign); /* in, own: foreign key struct */ |
|
210 |
||
211 |
/* Stream for storing detailed information about the latest foreign key
|
|
212 |
and unique key errors */
|
|
213 |
FILE* dict_foreign_err_file = NULL; |
|
214 |
mutex_t dict_foreign_err_mutex; /* mutex protecting the foreign |
|
215 |
and unique error buffers */
|
|
216 |
||
217 |
#ifndef UNIV_HOTBACKUP
|
|
218 |
/**********************************************************************
|
|
219 |
Makes all characters in a NUL-terminated UTF-8 string lower case. */
|
|
220 |
||
221 |
void
|
|
222 |
dict_casedn_str( |
|
223 |
/*============*/
|
|
224 |
char* a) /* in/out: string to put in lower case */ |
|
225 |
{
|
|
226 |
innobase_casedn_str(a); |
|
227 |
}
|
|
228 |
#endif /* !UNIV_HOTBACKUP */ |
|
229 |
||
230 |
/************************************************************************
|
|
231 |
Checks if the database name in two table names is the same. */
|
|
232 |
||
233 |
ibool
|
|
234 |
dict_tables_have_same_db( |
|
235 |
/*=====================*/
|
|
236 |
/* out: TRUE if same db name */
|
|
237 |
const char* name1, /* in: table name in the form |
|
238 |
dbname '/' tablename */
|
|
239 |
const char* name2) /* in: table name in the form |
|
240 |
dbname '/' tablename */
|
|
241 |
{
|
|
242 |
for (; *name1 == *name2; name1++, name2++) { |
|
243 |
if (*name1 == '/') { |
|
244 |
return(TRUE); |
|
245 |
}
|
|
246 |
ut_a(*name1); /* the names must contain '/' */ |
|
247 |
}
|
|
248 |
return(FALSE); |
|
249 |
}
|
|
250 |
||
251 |
/************************************************************************
|
|
252 |
Return the end of table name where we have removed dbname and '/'. */
|
|
253 |
||
254 |
const char* |
|
255 |
dict_remove_db_name( |
|
256 |
/*================*/
|
|
257 |
/* out: table name */
|
|
258 |
const char* name) /* in: table name in the form |
|
259 |
dbname '/' tablename */
|
|
260 |
{
|
|
261 |
const char* s = strchr(name, '/'); |
|
262 |
ut_a(s); |
|
263 |
||
264 |
return(s + 1); |
|
265 |
}
|
|
266 |
||
267 |
/************************************************************************
|
|
268 |
Get the database name length in a table name. */
|
|
269 |
||
270 |
ulint
|
|
271 |
dict_get_db_name_len( |
|
272 |
/*=================*/
|
|
273 |
/* out: database name length */
|
|
274 |
const char* name) /* in: table name in the form |
|
275 |
dbname '/' tablename */
|
|
276 |
{
|
|
277 |
const char* s; |
|
278 |
s = strchr(name, '/'); |
|
279 |
ut_a(s); |
|
280 |
return(s - name); |
|
281 |
}
|
|
282 |
||
283 |
/************************************************************************
|
|
284 |
Reserves the dictionary system mutex for MySQL. */
|
|
285 |
||
286 |
void
|
|
287 |
dict_mutex_enter_for_mysql(void) |
|
288 |
/*============================*/
|
|
289 |
{
|
|
290 |
mutex_enter(&(dict_sys->mutex)); |
|
291 |
}
|
|
292 |
||
293 |
/************************************************************************
|
|
294 |
Releases the dictionary system mutex for MySQL. */
|
|
295 |
||
296 |
void
|
|
297 |
dict_mutex_exit_for_mysql(void) |
|
298 |
/*===========================*/
|
|
299 |
{
|
|
300 |
mutex_exit(&(dict_sys->mutex)); |
|
301 |
}
|
|
302 |
||
303 |
/************************************************************************
|
|
304 |
Decrements the count of open MySQL handles to a table. */
|
|
305 |
||
306 |
void
|
|
307 |
dict_table_decrement_handle_count( |
|
308 |
/*==============================*/
|
|
309 |
dict_table_t* table) /* in: table */ |
|
310 |
{
|
|
311 |
mutex_enter(&(dict_sys->mutex)); |
|
312 |
||
313 |
ut_a(table->n_mysql_handles_opened > 0); |
|
314 |
||
315 |
table->n_mysql_handles_opened--; |
|
316 |
||
317 |
mutex_exit(&(dict_sys->mutex)); |
|
318 |
}
|
|
319 |
||
320 |
/*************************************************************************
|
|
321 |
Gets the column data type. */
|
|
322 |
||
323 |
void
|
|
324 |
dict_col_copy_type_noninline( |
|
325 |
/*=========================*/
|
|
326 |
const dict_col_t* col, /* in: column */ |
|
327 |
dtype_t* type) /* out: data type */ |
|
328 |
{
|
|
329 |
dict_col_copy_type(col, type); |
|
330 |
}
|
|
331 |
||
332 |
/************************************************************************
|
|
333 |
Gets the nth column of a table. */
|
|
334 |
||
335 |
const dict_col_t* |
|
336 |
dict_table_get_nth_col_noninline( |
|
337 |
/*=============================*/
|
|
338 |
/* out: pointer to column object */
|
|
339 |
const dict_table_t* table, /* in: table */ |
|
340 |
ulint pos) /* in: position of column */ |
|
341 |
{
|
|
342 |
return(dict_table_get_nth_col(table, pos)); |
|
343 |
}
|
|
344 |
||
345 |
/************************************************************************
|
|
346 |
Gets the first index on the table (the clustered index). */
|
|
347 |
||
348 |
dict_index_t* |
|
349 |
dict_table_get_first_index_noninline( |
|
350 |
/*=================================*/
|
|
351 |
/* out: index, NULL if none exists */
|
|
352 |
dict_table_t* table) /* in: table */ |
|
353 |
{
|
|
354 |
return(dict_table_get_first_index(table)); |
|
355 |
}
|
|
356 |
||
357 |
/************************************************************************
|
|
358 |
Gets the next index on the table. */
|
|
359 |
||
360 |
dict_index_t* |
|
361 |
dict_table_get_next_index_noninline( |
|
362 |
/*================================*/
|
|
363 |
/* out: index, NULL if none left */
|
|
364 |
dict_index_t* index) /* in: index */ |
|
365 |
{
|
|
366 |
return(dict_table_get_next_index(index)); |
|
367 |
}
|
|
368 |
||
369 |
/**************************************************************************
|
|
370 |
Returns an index object. */
|
|
371 |
||
372 |
dict_index_t* |
|
373 |
dict_table_get_index_noninline( |
|
374 |
/*===========================*/
|
|
375 |
/* out: index, NULL if does not exist */
|
|
376 |
dict_table_t* table, /* in: table */ |
|
377 |
const char* name) /* in: index name */ |
|
378 |
{
|
|
379 |
return(dict_table_get_index(table, name)); |
|
380 |
}
|
|
381 |
||
382 |
/**************************************************************************
|
|
383 |
Returns a column's name. */
|
|
384 |
||
385 |
const char* |
|
386 |
dict_table_get_col_name( |
|
387 |
/*====================*/
|
|
388 |
/* out: column name. NOTE: not
|
|
389 |
guaranteed to stay valid if table is
|
|
390 |
modified in any way (columns added,
|
|
391 |
etc.). */
|
|
392 |
const dict_table_t* table, /* in: table */ |
|
393 |
ulint col_nr) /* in: column number */ |
|
394 |
{
|
|
395 |
ulint i; |
|
396 |
const char* s; |
|
397 |
||
398 |
ut_ad(table); |
|
399 |
ut_ad(col_nr < table->n_def); |
|
400 |
ut_ad(table->magic_n == DICT_TABLE_MAGIC_N); |
|
401 |
||
402 |
s = table->col_names; |
|
403 |
if (s) { |
|
404 |
for (i = 0; i < col_nr; i++) { |
|
405 |
s += strlen(s) + 1; |
|
406 |
}
|
|
407 |
}
|
|
408 |
||
409 |
return(s); |
|
410 |
}
|
|
411 |
||
412 |
||
413 |
/************************************************************************
|
|
414 |
Acquire the autoinc lock.*/
|
|
415 |
||
416 |
void
|
|
417 |
dict_table_autoinc_lock( |
|
418 |
/*====================*/
|
|
419 |
dict_table_t* table) |
|
420 |
{
|
|
421 |
mutex_enter(&table->autoinc_mutex); |
|
422 |
}
|
|
423 |
||
424 |
/************************************************************************
|
|
425 |
Initializes the autoinc counter. It is not an error to initialize an already
|
|
426 |
initialized counter. */
|
|
427 |
||
428 |
void
|
|
429 |
dict_table_autoinc_initialize( |
|
430 |
/*==========================*/
|
|
431 |
dict_table_t* table, /* in: table */ |
|
432 |
ib_longlong value) /* in: next value to assign to a row */ |
|
433 |
{
|
|
434 |
ut_ad(mutex_own(&table->autoinc_mutex)); |
|
435 |
||
436 |
table->autoinc_inited = TRUE; |
|
437 |
table->autoinc = value; |
|
438 |
}
|
|
439 |
||
440 |
/************************************************************************
|
|
441 |
Reads the next autoinc value (== autoinc counter value), 0 if not yet
|
|
442 |
initialized. */
|
|
443 |
||
444 |
ib_longlong
|
|
445 |
dict_table_autoinc_read( |
|
446 |
/*====================*/
|
|
447 |
/* out: value for a new row, or 0 */
|
|
448 |
dict_table_t* table) /* in: table */ |
|
449 |
{
|
|
450 |
ib_longlong value; |
|
451 |
||
452 |
ut_ad(mutex_own(&table->autoinc_mutex)); |
|
453 |
||
454 |
if (!table->autoinc_inited) { |
|
455 |
||
456 |
value = 0; |
|
457 |
} else { |
|
458 |
value = table->autoinc; |
|
459 |
}
|
|
460 |
||
461 |
return(value); |
|
462 |
}
|
|
463 |
||
464 |
/************************************************************************
|
|
465 |
Updates the autoinc counter if the value supplied is greater than the
|
|
466 |
current value. If not inited, does nothing. */
|
|
467 |
||
468 |
void
|
|
469 |
dict_table_autoinc_update( |
|
470 |
/*======================*/
|
|
471 |
||
472 |
dict_table_t* table, /* in: table */ |
|
473 |
ib_longlong value) /* in: value which was assigned to a row */ |
|
474 |
{
|
|
475 |
if (table->autoinc_inited && value > table->autoinc) { |
|
476 |
||
477 |
table->autoinc = value; |
|
478 |
}
|
|
479 |
}
|
|
480 |
||
481 |
/************************************************************************
|
|
482 |
Release the autoinc lock.*/
|
|
483 |
||
484 |
void
|
|
485 |
dict_table_autoinc_unlock( |
|
486 |
/*======================*/
|
|
487 |
dict_table_t* table) /* in: release autoinc lock for this table */ |
|
488 |
{
|
|
489 |
mutex_exit(&table->autoinc_mutex); |
|
490 |
}
|
|
491 |
||
492 |
/************************************************************************
|
|
493 |
Looks for column n in an index. */
|
|
494 |
||
495 |
ulint
|
|
496 |
dict_index_get_nth_col_pos( |
|
497 |
/*=======================*/
|
|
498 |
/* out: position in internal representation
|
|
499 |
of the index; if not contained, returns
|
|
500 |
ULINT_UNDEFINED */
|
|
501 |
dict_index_t* index, /* in: index */ |
|
502 |
ulint n) /* in: column number */ |
|
503 |
{
|
|
504 |
const dict_field_t* field; |
|
505 |
const dict_col_t* col; |
|
506 |
ulint pos; |
|
507 |
ulint n_fields; |
|
508 |
||
509 |
ut_ad(index); |
|
510 |
ut_ad(index->magic_n == DICT_INDEX_MAGIC_N); |
|
511 |
||
512 |
col = dict_table_get_nth_col(index->table, n); |
|
513 |
||
514 |
if (index->type & DICT_CLUSTERED) { |
|
515 |
||
516 |
return(dict_col_get_clust_pos(col, index)); |
|
517 |
}
|
|
518 |
||
519 |
n_fields = dict_index_get_n_fields(index); |
|
520 |
||
521 |
for (pos = 0; pos < n_fields; pos++) { |
|
522 |
field = dict_index_get_nth_field(index, pos); |
|
523 |
||
524 |
if (col == field->col && field->prefix_len == 0) { |
|
525 |
||
526 |
return(pos); |
|
527 |
}
|
|
528 |
}
|
|
529 |
||
530 |
return(ULINT_UNDEFINED); |
|
531 |
}
|
|
532 |
||
533 |
/************************************************************************
|
|
534 |
Returns TRUE if the index contains a column or a prefix of that column. */
|
|
535 |
||
536 |
ibool
|
|
537 |
dict_index_contains_col_or_prefix( |
|
538 |
/*==============================*/
|
|
539 |
/* out: TRUE if contains the column or its
|
|
540 |
prefix */
|
|
541 |
dict_index_t* index, /* in: index */ |
|
542 |
ulint n) /* in: column number */ |
|
543 |
{
|
|
544 |
const dict_field_t* field; |
|
545 |
const dict_col_t* col; |
|
546 |
ulint pos; |
|
547 |
ulint n_fields; |
|
548 |
||
549 |
ut_ad(index); |
|
550 |
ut_ad(index->magic_n == DICT_INDEX_MAGIC_N); |
|
551 |
||
552 |
if (index->type & DICT_CLUSTERED) { |
|
553 |
||
554 |
return(TRUE); |
|
555 |
}
|
|
556 |
||
557 |
col = dict_table_get_nth_col(index->table, n); |
|
558 |
||
559 |
n_fields = dict_index_get_n_fields(index); |
|
560 |
||
561 |
for (pos = 0; pos < n_fields; pos++) { |
|
562 |
field = dict_index_get_nth_field(index, pos); |
|
563 |
||
564 |
if (col == field->col) { |
|
565 |
||
566 |
return(TRUE); |
|
567 |
}
|
|
568 |
}
|
|
569 |
||
570 |
return(FALSE); |
|
571 |
}
|
|
572 |
||
573 |
/************************************************************************
|
|
574 |
Looks for a matching field in an index. The column has to be the same. The
|
|
575 |
column in index must be complete, or must contain a prefix longer than the
|
|
576 |
column in index2. That is, we must be able to construct the prefix in index2
|
|
577 |
from the prefix in index. */
|
|
578 |
||
579 |
ulint
|
|
580 |
dict_index_get_nth_field_pos( |
|
581 |
/*=========================*/
|
|
582 |
/* out: position in internal representation
|
|
583 |
of the index; if not contained, returns
|
|
584 |
ULINT_UNDEFINED */
|
|
585 |
dict_index_t* index, /* in: index from which to search */ |
|
586 |
dict_index_t* index2, /* in: index */ |
|
587 |
ulint n) /* in: field number in index2 */ |
|
588 |
{
|
|
589 |
dict_field_t* field; |
|
590 |
dict_field_t* field2; |
|
591 |
ulint n_fields; |
|
592 |
ulint pos; |
|
593 |
||
594 |
ut_ad(index); |
|
595 |
ut_ad(index->magic_n == DICT_INDEX_MAGIC_N); |
|
596 |
||
597 |
field2 = dict_index_get_nth_field(index2, n); |
|
598 |
||
599 |
n_fields = dict_index_get_n_fields(index); |
|
600 |
||
601 |
for (pos = 0; pos < n_fields; pos++) { |
|
602 |
field = dict_index_get_nth_field(index, pos); |
|
603 |
||
604 |
if (field->col == field2->col |
|
605 |
&& (field->prefix_len == 0 |
|
606 |
|| (field->prefix_len >= field2->prefix_len |
|
607 |
&& field2->prefix_len != 0))) { |
|
608 |
||
609 |
return(pos); |
|
610 |
}
|
|
611 |
}
|
|
612 |
||
613 |
return(ULINT_UNDEFINED); |
|
614 |
}
|
|
615 |
||
616 |
/**************************************************************************
|
|
617 |
Returns a table object based on table id. */
|
|
618 |
||
619 |
dict_table_t* |
|
620 |
dict_table_get_on_id( |
|
621 |
/*=================*/
|
|
622 |
/* out: table, NULL if does not exist */
|
|
623 |
dulint table_id, /* in: table id */ |
|
624 |
trx_t* trx) /* in: transaction handle */ |
|
625 |
{
|
|
626 |
dict_table_t* table; |
|
627 |
||
628 |
if (ut_dulint_cmp(table_id, DICT_FIELDS_ID) <= 0 |
|
629 |
|| trx->dict_operation_lock_mode == RW_X_LATCH) { |
|
630 |
/* It is a system table which will always exist in the table
|
|
631 |
cache: we avoid acquiring the dictionary mutex, because
|
|
632 |
if we are doing a rollback to handle an error in TABLE
|
|
633 |
CREATE, for example, we already have the mutex! */
|
|
634 |
||
635 |
ut_ad(mutex_own(&(dict_sys->mutex)) |
|
636 |
|| trx->dict_operation_lock_mode == RW_X_LATCH); |
|
637 |
||
638 |
return(dict_table_get_on_id_low(table_id)); |
|
639 |
}
|
|
640 |
||
641 |
mutex_enter(&(dict_sys->mutex)); |
|
642 |
||
643 |
table = dict_table_get_on_id_low(table_id); |
|
644 |
||
645 |
mutex_exit(&(dict_sys->mutex)); |
|
646 |
||
647 |
return(table); |
|
648 |
}
|
|
649 |
||
650 |
/************************************************************************
|
|
651 |
Looks for column n position in the clustered index. */
|
|
652 |
||
653 |
ulint
|
|
654 |
dict_table_get_nth_col_pos( |
|
655 |
/*=======================*/
|
|
656 |
/* out: position in internal representation
|
|
657 |
of the clustered index */
|
|
658 |
dict_table_t* table, /* in: table */ |
|
659 |
ulint n) /* in: column number */ |
|
660 |
{
|
|
661 |
return(dict_index_get_nth_col_pos(dict_table_get_first_index(table), |
|
662 |
n)); |
|
663 |
}
|
|
664 |
||
665 |
/************************************************************************
|
|
666 |
Check whether the table uses the compact page format. */
|
|
667 |
||
668 |
ibool
|
|
669 |
dict_table_is_comp_noninline( |
|
670 |
/*=========================*/
|
|
671 |
/* out: TRUE if table uses the
|
|
672 |
compact page format */
|
|
673 |
const dict_table_t* table) /* in: table */ |
|
674 |
{
|
|
675 |
return(dict_table_is_comp(table)); |
|
676 |
}
|
|
677 |
||
678 |
/************************************************************************
|
|
679 |
Checks if a column is in the ordering columns of the clustered index of a
|
|
680 |
table. Column prefixes are treated like whole columns. */
|
|
681 |
||
682 |
ibool
|
|
683 |
dict_table_col_in_clustered_key( |
|
684 |
/*============================*/
|
|
685 |
/* out: TRUE if the column, or its prefix, is
|
|
686 |
in the clustered key */
|
|
687 |
dict_table_t* table, /* in: table */ |
|
688 |
ulint n) /* in: column number */ |
|
689 |
{
|
|
690 |
dict_index_t* index; |
|
691 |
const dict_field_t* field; |
|
692 |
const dict_col_t* col; |
|
693 |
ulint pos; |
|
694 |
ulint n_fields; |
|
695 |
||
696 |
ut_ad(table); |
|
697 |
||
698 |
col = dict_table_get_nth_col(table, n); |
|
699 |
||
700 |
index = dict_table_get_first_index(table); |
|
701 |
||
702 |
n_fields = dict_index_get_n_unique(index); |
|
703 |
||
704 |
for (pos = 0; pos < n_fields; pos++) { |
|
705 |
field = dict_index_get_nth_field(index, pos); |
|
706 |
||
707 |
if (col == field->col) { |
|
708 |
||
709 |
return(TRUE); |
|
710 |
}
|
|
711 |
}
|
|
712 |
||
713 |
return(FALSE); |
|
714 |
}
|
|
715 |
||
716 |
/**************************************************************************
|
|
717 |
Inits the data dictionary module. */
|
|
718 |
||
719 |
void
|
|
720 |
dict_init(void) |
|
721 |
/*===========*/
|
|
722 |
{
|
|
723 |
dict_sys = mem_alloc(sizeof(dict_sys_t)); |
|
724 |
||
725 |
mutex_create(&dict_sys->mutex, SYNC_DICT); |
|
726 |
||
727 |
dict_sys->table_hash = hash_create(buf_pool_get_max_size() |
|
728 |
/ (DICT_POOL_PER_TABLE_HASH |
|
729 |
* UNIV_WORD_SIZE)); |
|
730 |
dict_sys->table_id_hash = hash_create(buf_pool_get_max_size() |
|
731 |
/ (DICT_POOL_PER_TABLE_HASH |
|
732 |
* UNIV_WORD_SIZE)); |
|
733 |
dict_sys->size = 0; |
|
734 |
||
735 |
UT_LIST_INIT(dict_sys->table_LRU); |
|
736 |
||
737 |
rw_lock_create(&dict_operation_lock, SYNC_DICT_OPERATION); |
|
738 |
||
739 |
dict_foreign_err_file = os_file_create_tmpfile(); |
|
740 |
ut_a(dict_foreign_err_file); |
|
741 |
||
742 |
mutex_create(&dict_foreign_err_mutex, SYNC_ANY_LATCH); |
|
743 |
}
|
|
744 |
||
745 |
/**************************************************************************
|
|
746 |
Returns a table object and optionally increment its MySQL open handle count.
|
|
747 |
NOTE! This is a high-level function to be used mainly from outside the
|
|
748 |
'dict' directory. Inside this directory dict_table_get_low is usually the
|
|
749 |
appropriate function. */
|
|
750 |
||
751 |
dict_table_t* |
|
752 |
dict_table_get( |
|
753 |
/*===========*/
|
|
754 |
/* out: table, NULL if
|
|
755 |
does not exist */
|
|
756 |
const char* table_name, /* in: table name */ |
|
757 |
ibool inc_mysql_count) |
|
758 |
/* in: whether to increment the open
|
|
759 |
handle count on the table */
|
|
760 |
{
|
|
761 |
dict_table_t* table; |
|
762 |
||
763 |
mutex_enter(&(dict_sys->mutex)); |
|
764 |
||
765 |
table = dict_table_get_low(table_name); |
|
766 |
||
767 |
if (inc_mysql_count && table) { |
|
768 |
table->n_mysql_handles_opened++; |
|
769 |
}
|
|
770 |
||
771 |
mutex_exit(&(dict_sys->mutex)); |
|
772 |
||
773 |
if (table != NULL) { |
|
774 |
if (!table->stat_initialized) { |
|
775 |
/* If table->ibd_file_missing == TRUE, this will
|
|
776 |
print an error message and return without doing
|
|
777 |
anything. */
|
|
778 |
dict_update_statistics(table); |
|
779 |
}
|
|
780 |
}
|
|
781 |
||
782 |
return(table); |
|
783 |
}
|
|
784 |
||
785 |
/**************************************************************************
|
|
786 |
Adds system columns to a table object. */
|
|
787 |
||
788 |
void
|
|
789 |
dict_table_add_system_columns( |
|
790 |
/*==========================*/
|
|
791 |
dict_table_t* table, /* in/out: table */ |
|
792 |
mem_heap_t* heap) /* in: temporary heap */ |
|
793 |
{
|
|
794 |
ut_ad(table); |
|
795 |
ut_ad(table->n_def == table->n_cols - DATA_N_SYS_COLS); |
|
796 |
ut_ad(table->magic_n == DICT_TABLE_MAGIC_N); |
|
797 |
ut_ad(!table->cached); |
|
798 |
||
799 |
/* NOTE: the system columns MUST be added in the following order
|
|
800 |
(so that they can be indexed by the numerical value of DATA_ROW_ID,
|
|
801 |
etc.) and as the last columns of the table memory object.
|
|
802 |
The clustered index will not always physically contain all
|
|
803 |
system columns. */
|
|
804 |
||
805 |
dict_mem_table_add_col(table, heap, "DB_ROW_ID", DATA_SYS, |
|
806 |
DATA_ROW_ID | DATA_NOT_NULL, |
|
807 |
DATA_ROW_ID_LEN); |
|
808 |
#if DATA_ROW_ID != 0
|
|
809 |
#error "DATA_ROW_ID != 0"
|
|
810 |
#endif
|
|
811 |
dict_mem_table_add_col(table, heap, "DB_TRX_ID", DATA_SYS, |
|
812 |
DATA_TRX_ID | DATA_NOT_NULL, |
|
813 |
DATA_TRX_ID_LEN); |
|
814 |
#if DATA_TRX_ID != 1
|
|
815 |
#error "DATA_TRX_ID != 1"
|
|
816 |
#endif
|
|
817 |
dict_mem_table_add_col(table, heap, "DB_ROLL_PTR", DATA_SYS, |
|
818 |
DATA_ROLL_PTR | DATA_NOT_NULL, |
|
819 |
DATA_ROLL_PTR_LEN); |
|
820 |
#if DATA_ROLL_PTR != 2
|
|
821 |
#error "DATA_ROLL_PTR != 2"
|
|
822 |
#endif
|
|
823 |
||
824 |
/* This check reminds that if a new system column is added to
|
|
825 |
the program, it should be dealt with here */
|
|
826 |
#if DATA_N_SYS_COLS != 3
|
|
827 |
#error "DATA_N_SYS_COLS != 3"
|
|
828 |
#endif
|
|
829 |
}
|
|
830 |
||
831 |
/**************************************************************************
|
|
832 |
Adds a table object to the dictionary cache. */
|
|
833 |
||
834 |
void
|
|
835 |
dict_table_add_to_cache( |
|
836 |
/*====================*/
|
|
837 |
dict_table_t* table, /* in: table */ |
|
838 |
mem_heap_t* heap) /* in: temporary heap */ |
|
839 |
{
|
|
840 |
ulint fold; |
|
841 |
ulint id_fold; |
|
842 |
ulint i; |
|
843 |
ulint row_len; |
|
844 |
||
845 |
/* The lower limit for what we consider a "big" row */
|
|
846 |
#define BIG_ROW_SIZE 1024
|
|
847 |
||
848 |
ut_ad(mutex_own(&(dict_sys->mutex))); |
|
849 |
||
850 |
dict_table_add_system_columns(table, heap); |
|
851 |
||
852 |
table->cached = TRUE; |
|
853 |
||
854 |
fold = ut_fold_string(table->name); |
|
855 |
id_fold = ut_fold_dulint(table->id); |
|
856 |
||
857 |
row_len = 0; |
|
858 |
for (i = 0; i < table->n_def; i++) { |
|
859 |
ulint col_len = dict_col_get_max_size( |
|
860 |
dict_table_get_nth_col(table, i)); |
|
861 |
||
862 |
row_len += col_len; |
|
863 |
||
864 |
/* If we have a single unbounded field, or several gigantic
|
|
865 |
fields, mark the maximum row size as BIG_ROW_SIZE. */
|
|
866 |
if (row_len >= BIG_ROW_SIZE || col_len >= BIG_ROW_SIZE) { |
|
867 |
row_len = BIG_ROW_SIZE; |
|
868 |
||
869 |
break; |
|
870 |
}
|
|
871 |
}
|
|
872 |
||
873 |
table->big_rows = row_len >= BIG_ROW_SIZE; |
|
874 |
||
875 |
/* Look for a table with the same name: error if such exists */
|
|
876 |
{
|
|
877 |
dict_table_t* table2; |
|
878 |
HASH_SEARCH(name_hash, dict_sys->table_hash, fold, table2, |
|
879 |
(ut_strcmp(table2->name, table->name) == 0)); |
|
880 |
ut_a(table2 == NULL); |
|
881 |
}
|
|
882 |
||
883 |
/* Look for a table with the same id: error if such exists */
|
|
884 |
{
|
|
885 |
dict_table_t* table2; |
|
886 |
HASH_SEARCH(id_hash, dict_sys->table_id_hash, id_fold, table2, |
|
887 |
(ut_dulint_cmp(table2->id, table->id) == 0)); |
|
888 |
ut_a(table2 == NULL); |
|
889 |
}
|
|
890 |
||
891 |
/* Add table to hash table of tables */
|
|
892 |
HASH_INSERT(dict_table_t, name_hash, dict_sys->table_hash, fold, |
|
893 |
table); |
|
894 |
||
895 |
/* Add table to hash table of tables based on table id */
|
|
896 |
HASH_INSERT(dict_table_t, id_hash, dict_sys->table_id_hash, id_fold, |
|
897 |
table); |
|
898 |
/* Add table to LRU list of tables */
|
|
899 |
UT_LIST_ADD_FIRST(table_LRU, dict_sys->table_LRU, table); |
|
900 |
||
901 |
dict_sys->size += mem_heap_get_size(table->heap); |
|
902 |
}
|
|
903 |
||
904 |
/**************************************************************************
|
|
905 |
Looks for an index with the given id. NOTE that we do not reserve
|
|
906 |
the dictionary mutex: this function is for emergency purposes like
|
|
907 |
printing info of a corrupt database page! */
|
|
908 |
||
909 |
dict_index_t* |
|
910 |
dict_index_find_on_id_low( |
|
911 |
/*======================*/
|
|
912 |
/* out: index or NULL if not found from cache */
|
|
913 |
dulint id) /* in: index id */ |
|
914 |
{
|
|
915 |
dict_table_t* table; |
|
916 |
dict_index_t* index; |
|
917 |
||
918 |
table = UT_LIST_GET_FIRST(dict_sys->table_LRU); |
|
919 |
||
920 |
while (table) { |
|
921 |
index = dict_table_get_first_index(table); |
|
922 |
||
923 |
while (index) { |
|
924 |
if (0 == ut_dulint_cmp(id, index->id)) { |
|
925 |
/* Found */
|
|
926 |
||
927 |
return(index); |
|
928 |
}
|
|
929 |
||
930 |
index = dict_table_get_next_index(index); |
|
931 |
}
|
|
932 |
||
933 |
table = UT_LIST_GET_NEXT(table_LRU, table); |
|
934 |
}
|
|
935 |
||
936 |
return(NULL); |
|
937 |
}
|
|
938 |
||
939 |
/**************************************************************************
|
|
940 |
Renames a table object. */
|
|
941 |
||
942 |
ibool
|
|
943 |
dict_table_rename_in_cache( |
|
944 |
/*=======================*/
|
|
945 |
/* out: TRUE if success */
|
|
946 |
dict_table_t* table, /* in: table */ |
|
947 |
const char* new_name, /* in: new name */ |
|
948 |
ibool rename_also_foreigns)/* in: in ALTER TABLE we want |
|
949 |
to preserve the original table name
|
|
950 |
in constraints which reference it */
|
|
951 |
{
|
|
952 |
dict_foreign_t* foreign; |
|
953 |
dict_index_t* index; |
|
954 |
ulint fold; |
|
955 |
ulint old_size; |
|
956 |
char* old_name; |
|
957 |
ibool success; |
|
958 |
||
959 |
ut_ad(table); |
|
960 |
ut_ad(mutex_own(&(dict_sys->mutex))); |
|
961 |
||
962 |
old_size = mem_heap_get_size(table->heap); |
|
963 |
||
964 |
fold = ut_fold_string(new_name); |
|
965 |
||
966 |
/* Look for a table with the same name: error if such exists */
|
|
967 |
{
|
|
968 |
dict_table_t* table2; |
|
969 |
HASH_SEARCH(name_hash, dict_sys->table_hash, fold, table2, |
|
970 |
(ut_strcmp(table2->name, new_name) == 0)); |
|
971 |
if (table2) { |
|
972 |
fprintf(stderr, |
|
973 |
"InnoDB: Error: dictionary cache"
|
|
974 |
" already contains a table of name %s\n", |
|
975 |
new_name); |
|
976 |
return(FALSE); |
|
977 |
}
|
|
978 |
}
|
|
979 |
||
980 |
/* If the table is stored in a single-table tablespace, rename the
|
|
981 |
.ibd file */
|
|
982 |
||
983 |
if (table->space != 0) { |
|
984 |
if (table->dir_path_of_temp_table != NULL) { |
|
985 |
fprintf(stderr, |
|
986 |
"InnoDB: Error: trying to rename a table"
|
|
987 |
" %s (%s) created with CREATE\n" |
|
988 |
"InnoDB: TEMPORARY TABLE\n", |
|
989 |
table->name, table->dir_path_of_temp_table); |
|
990 |
success = FALSE; |
|
991 |
} else { |
|
992 |
success = fil_rename_tablespace( |
|
993 |
table->name, table->space, new_name); |
|
994 |
}
|
|
995 |
||
996 |
if (!success) { |
|
997 |
||
998 |
return(FALSE); |
|
999 |
}
|
|
1000 |
}
|
|
1001 |
||
1002 |
/* Remove table from the hash tables of tables */
|
|
1003 |
HASH_DELETE(dict_table_t, name_hash, dict_sys->table_hash, |
|
1004 |
ut_fold_string(table->name), table); |
|
1005 |
old_name = mem_heap_strdup(table->heap, table->name); |
|
1006 |
table->name = mem_heap_strdup(table->heap, new_name); |
|
1007 |
||
1008 |
/* Add table to hash table of tables */
|
|
1009 |
HASH_INSERT(dict_table_t, name_hash, dict_sys->table_hash, fold, |
|
1010 |
table); |
|
1011 |
dict_sys->size += (mem_heap_get_size(table->heap) - old_size); |
|
1012 |
||
1013 |
/* Update the table_name field in indexes */
|
|
1014 |
index = dict_table_get_first_index(table); |
|
1015 |
||
1016 |
while (index != NULL) { |
|
1017 |
index->table_name = table->name; |
|
1018 |
||
1019 |
index = dict_table_get_next_index(index); |
|
1020 |
}
|
|
1021 |
||
1022 |
if (!rename_also_foreigns) { |
|
1023 |
/* In ALTER TABLE we think of the rename table operation
|
|
1024 |
in the direction table -> temporary table (#sql...)
|
|
1025 |
as dropping the table with the old name and creating
|
|
1026 |
a new with the new name. Thus we kind of drop the
|
|
1027 |
constraints from the dictionary cache here. The foreign key
|
|
1028 |
constraints will be inherited to the new table from the
|
|
1029 |
system tables through a call of dict_load_foreigns. */
|
|
1030 |
||
1031 |
/* Remove the foreign constraints from the cache */
|
|
1032 |
foreign = UT_LIST_GET_LAST(table->foreign_list); |
|
1033 |
||
1034 |
while (foreign != NULL) { |
|
1035 |
dict_foreign_remove_from_cache(foreign); |
|
1036 |
foreign = UT_LIST_GET_LAST(table->foreign_list); |
|
1037 |
}
|
|
1038 |
||
1039 |
/* Reset table field in referencing constraints */
|
|
1040 |
||
1041 |
foreign = UT_LIST_GET_FIRST(table->referenced_list); |
|
1042 |
||
1043 |
while (foreign != NULL) { |
|
1044 |
foreign->referenced_table = NULL; |
|
1045 |
foreign->referenced_index = NULL; |
|
1046 |
||
1047 |
foreign = UT_LIST_GET_NEXT(referenced_list, foreign); |
|
1048 |
}
|
|
1049 |
||
1050 |
/* Make the list of referencing constraints empty */
|
|
1051 |
||
1052 |
UT_LIST_INIT(table->referenced_list); |
|
1053 |
||
1054 |
return(TRUE); |
|
1055 |
}
|
|
1056 |
||
1057 |
/* Update the table name fields in foreign constraints, and update also
|
|
1058 |
the constraint id of new format >= 4.0.18 constraints. Note that at
|
|
1059 |
this point we have already changed table->name to the new name. */
|
|
1060 |
||
1061 |
foreign = UT_LIST_GET_FIRST(table->foreign_list); |
|
1062 |
||
1063 |
while (foreign != NULL) { |
|
1064 |
if (ut_strlen(foreign->foreign_table_name) |
|
1065 |
< ut_strlen(table->name)) { |
|
1066 |
/* Allocate a longer name buffer;
|
|
1067 |
TODO: store buf len to save memory */
|
|
1068 |
||
1069 |
foreign->foreign_table_name |
|
1070 |
= mem_heap_alloc(foreign->heap, |
|
1071 |
ut_strlen(table->name) + 1); |
|
1072 |
}
|
|
1073 |
||
1074 |
strcpy(foreign->foreign_table_name, table->name); |
|
1075 |
||
1076 |
if (strchr(foreign->id, '/')) { |
|
1077 |
ulint db_len; |
|
1078 |
char* old_id; |
|
1079 |
||
1080 |
/* This is a >= 4.0.18 format id */
|
|
1081 |
||
1082 |
old_id = mem_strdup(foreign->id); |
|
1083 |
||
1084 |
if (ut_strlen(foreign->id) > ut_strlen(old_name) |
|
1085 |
+ ((sizeof dict_ibfk) - 1) |
|
1086 |
&& !memcmp(foreign->id, old_name, |
|
1087 |
ut_strlen(old_name)) |
|
1088 |
&& !memcmp(foreign->id + ut_strlen(old_name), |
|
1089 |
dict_ibfk, (sizeof dict_ibfk) - 1)) { |
|
1090 |
||
1091 |
/* This is a generated >= 4.0.18 format id */
|
|
1092 |
||
1093 |
if (strlen(table->name) > strlen(old_name)) { |
|
1094 |
foreign->id = mem_heap_alloc( |
|
1095 |
foreign->heap, |
|
1096 |
strlen(table->name) |
|
1097 |
+ strlen(old_id) + 1); |
|
1098 |
}
|
|
1099 |
||
1100 |
/* Replace the prefix 'databasename/tablename'
|
|
1101 |
with the new names */
|
|
1102 |
strcpy(foreign->id, table->name); |
|
1103 |
strcat(foreign->id, |
|
1104 |
old_id + ut_strlen(old_name)); |
|
1105 |
} else { |
|
1106 |
/* This is a >= 4.0.18 format id where the user
|
|
1107 |
gave the id name */
|
|
1108 |
db_len = dict_get_db_name_len(table->name) + 1; |
|
1109 |
||
1110 |
if (dict_get_db_name_len(table->name) |
|
1111 |
> dict_get_db_name_len(foreign->id)) { |
|
1112 |
||
1113 |
foreign->id = mem_heap_alloc( |
|
1114 |
foreign->heap, |
|
1115 |
db_len + strlen(old_id) + 1); |
|
1116 |
}
|
|
1117 |
||
1118 |
/* Replace the database prefix in id with the
|
|
1119 |
one from table->name */
|
|
1120 |
||
1121 |
ut_memcpy(foreign->id, table->name, db_len); |
|
1122 |
||
1123 |
strcpy(foreign->id + db_len, |
|
1124 |
dict_remove_db_name(old_id)); |
|
1125 |
}
|
|
1126 |
||
1127 |
mem_free(old_id); |
|
1128 |
}
|
|
1129 |
||
1130 |
foreign = UT_LIST_GET_NEXT(foreign_list, foreign); |
|
1131 |
}
|
|
1132 |
||
1133 |
foreign = UT_LIST_GET_FIRST(table->referenced_list); |
|
1134 |
||
1135 |
while (foreign != NULL) { |
|
1136 |
if (ut_strlen(foreign->referenced_table_name) |
|
1137 |
< ut_strlen(table->name)) { |
|
1138 |
/* Allocate a longer name buffer;
|
|
1139 |
TODO: store buf len to save memory */
|
|
1140 |
||
1141 |
foreign->referenced_table_name = mem_heap_alloc( |
|
1142 |
foreign->heap, strlen(table->name) + 1); |
|
1143 |
}
|
|
1144 |
||
1145 |
strcpy(foreign->referenced_table_name, table->name); |
|
1146 |
||
1147 |
foreign = UT_LIST_GET_NEXT(referenced_list, foreign); |
|
1148 |
}
|
|
1149 |
||
1150 |
return(TRUE); |
|
1151 |
}
|
|
1152 |
||
1153 |
/**************************************************************************
|
|
1154 |
Change the id of a table object in the dictionary cache. This is used in
|
|
1155 |
DISCARD TABLESPACE. */
|
|
1156 |
||
1157 |
void
|
|
1158 |
dict_table_change_id_in_cache( |
|
1159 |
/*==========================*/
|
|
1160 |
dict_table_t* table, /* in: table object already in cache */ |
|
1161 |
dulint new_id) /* in: new id to set */ |
|
1162 |
{
|
|
1163 |
ut_ad(table); |
|
1164 |
ut_ad(mutex_own(&(dict_sys->mutex))); |
|
1165 |
ut_ad(table->magic_n == DICT_TABLE_MAGIC_N); |
|
1166 |
||
1167 |
/* Remove the table from the hash table of id's */
|
|
1168 |
||
1169 |
HASH_DELETE(dict_table_t, id_hash, dict_sys->table_id_hash, |
|
1170 |
ut_fold_dulint(table->id), table); |
|
1171 |
table->id = new_id; |
|
1172 |
||
1173 |
/* Add the table back to the hash table */
|
|
1174 |
HASH_INSERT(dict_table_t, id_hash, dict_sys->table_id_hash, |
|
1175 |
ut_fold_dulint(table->id), table); |
|
1176 |
}
|
|
1177 |
||
1178 |
/**************************************************************************
|
|
1179 |
Removes a table object from the dictionary cache. */
|
|
1180 |
||
1181 |
void
|
|
1182 |
dict_table_remove_from_cache( |
|
1183 |
/*=========================*/
|
|
1184 |
dict_table_t* table) /* in, own: table */ |
|
1185 |
{
|
|
1186 |
dict_foreign_t* foreign; |
|
1187 |
dict_index_t* index; |
|
1188 |
ulint size; |
|
1189 |
||
1190 |
ut_ad(table); |
|
1191 |
ut_ad(mutex_own(&(dict_sys->mutex))); |
|
1192 |
ut_ad(table->magic_n == DICT_TABLE_MAGIC_N); |
|
1193 |
||
1194 |
#if 0
|
|
1195 |
fputs("Removing table ", stderr);
|
|
1196 |
ut_print_name(stderr, table->name, ULINT_UNDEFINED);
|
|
1197 |
fputs(" from dictionary cache\n", stderr);
|
|
1198 |
#endif
|
|
1199 |
||
1200 |
/* Remove the foreign constraints from the cache */
|
|
1201 |
foreign = UT_LIST_GET_LAST(table->foreign_list); |
|
1202 |
||
1203 |
while (foreign != NULL) { |
|
1204 |
dict_foreign_remove_from_cache(foreign); |
|
1205 |
foreign = UT_LIST_GET_LAST(table->foreign_list); |
|
1206 |
}
|
|
1207 |
||
1208 |
/* Reset table field in referencing constraints */
|
|
1209 |
||
1210 |
foreign = UT_LIST_GET_FIRST(table->referenced_list); |
|
1211 |
||
1212 |
while (foreign != NULL) { |
|
1213 |
foreign->referenced_table = NULL; |
|
1214 |
foreign->referenced_index = NULL; |
|
1215 |
||
1216 |
foreign = UT_LIST_GET_NEXT(referenced_list, foreign); |
|
1217 |
}
|
|
1218 |
||
1219 |
/* Remove the indexes from the cache */
|
|
1220 |
index = UT_LIST_GET_LAST(table->indexes); |
|
1221 |
||
1222 |
while (index != NULL) { |
|
1223 |
dict_index_remove_from_cache(table, index); |
|
1224 |
index = UT_LIST_GET_LAST(table->indexes); |
|
1225 |
}
|
|
1226 |
||
1227 |
/* Remove table from the hash tables of tables */
|
|
1228 |
HASH_DELETE(dict_table_t, name_hash, dict_sys->table_hash, |
|
1229 |
ut_fold_string(table->name), table); |
|
1230 |
HASH_DELETE(dict_table_t, id_hash, dict_sys->table_id_hash, |
|
1231 |
ut_fold_dulint(table->id), table); |
|
1232 |
||
1233 |
/* Remove table from LRU list of tables */
|
|
1234 |
UT_LIST_REMOVE(table_LRU, dict_sys->table_LRU, table); |
|
1235 |
||
1236 |
size = mem_heap_get_size(table->heap); |
|
1237 |
||
1238 |
ut_ad(dict_sys->size >= size); |
|
1239 |
||
1240 |
dict_sys->size -= size; |
|
1241 |
||
1242 |
dict_mem_table_free(table); |
|
1243 |
}
|
|
1244 |
||
1245 |
/*************************************************************************
|
|
1246 |
Gets the column position in the clustered index. */
|
|
1247 |
||
1248 |
ulint
|
|
1249 |
dict_col_get_clust_pos_noninline( |
|
1250 |
/*=============================*/
|
|
1251 |
const dict_col_t* col, /* in: table column */ |
|
1252 |
const dict_index_t* clust_index) /* in: clustered index */ |
|
1253 |
{
|
|
1254 |
return(dict_col_get_clust_pos(col, clust_index)); |
|
1255 |
}
|
|
1256 |
||
1257 |
/********************************************************************
|
|
1258 |
If the given column name is reserved for InnoDB system columns, return
|
|
1259 |
TRUE. */
|
|
1260 |
||
1261 |
ibool
|
|
1262 |
dict_col_name_is_reserved( |
|
1263 |
/*======================*/
|
|
1264 |
/* out: TRUE if name is reserved */
|
|
1265 |
const char* name) /* in: column name */ |
|
1266 |
{
|
|
1267 |
/* This check reminds that if a new system column is added to
|
|
1268 |
the program, it should be dealt with here. */
|
|
1269 |
#if DATA_N_SYS_COLS != 3
|
|
1270 |
#error "DATA_N_SYS_COLS != 3"
|
|
1271 |
#endif
|
|
1272 |
||
1273 |
static const char* reserved_names[] = { |
|
1274 |
"DB_ROW_ID", "DB_TRX_ID", "DB_ROLL_PTR" |
|
1275 |
};
|
|
1276 |
||
1277 |
ulint i; |
|
1278 |
||
1279 |
for (i = 0; i < UT_ARR_SIZE(reserved_names); i++) { |
|
1280 |
if (strcmp(name, reserved_names[i]) == 0) { |
|
1281 |
||
1282 |
return(TRUE); |
|
1283 |
}
|
|
1284 |
}
|
|
1285 |
||
1286 |
return(FALSE); |
|
1287 |
}
|
|
1288 |
||
1289 |
/**************************************************************************
|
|
1290 |
Adds an index to the dictionary cache. */
|
|
1291 |
||
1292 |
void
|
|
1293 |
dict_index_add_to_cache( |
|
1294 |
/*====================*/
|
|
1295 |
dict_table_t* table, /* in: table on which the index is */ |
|
1296 |
dict_index_t* index, /* in, own: index; NOTE! The index memory |
|
1297 |
object is freed in this function! */
|
|
1298 |
ulint page_no)/* in: root page number of the index */ |
|
1299 |
{
|
|
1300 |
dict_index_t* new_index; |
|
1301 |
ulint n_ord; |
|
1302 |
ulint i; |
|
1303 |
||
1304 |
ut_ad(index); |
|
1305 |
ut_ad(mutex_own(&(dict_sys->mutex))); |
|
1306 |
ut_ad(index->n_def == index->n_fields); |
|
1307 |
ut_ad(index->magic_n == DICT_INDEX_MAGIC_N); |
|
1308 |
||
1309 |
ut_ad(mem_heap_validate(index->heap)); |
|
1310 |
||
1311 |
#ifdef UNIV_DEBUG
|
|
1312 |
{
|
|
1313 |
dict_index_t* index2; |
|
1314 |
index2 = UT_LIST_GET_FIRST(table->indexes); |
|
1315 |
||
1316 |
while (index2 != NULL) { |
|
1317 |
ut_ad(ut_strcmp(index->name, index2->name) != 0); |
|
1318 |
||
1319 |
index2 = UT_LIST_GET_NEXT(indexes, index2); |
|
1320 |
}
|
|
1321 |
}
|
|
1322 |
#endif /* UNIV_DEBUG */ |
|
1323 |
||
1324 |
ut_a(!(index->type & DICT_CLUSTERED) |
|
1325 |
|| UT_LIST_GET_LEN(table->indexes) == 0); |
|
1326 |
||
1327 |
dict_index_find_cols(table, index); |
|
1328 |
||
1329 |
/* Build the cache internal representation of the index,
|
|
1330 |
containing also the added system fields */
|
|
1331 |
||
1332 |
if (index->type & DICT_CLUSTERED) { |
|
1333 |
new_index = dict_index_build_internal_clust(table, index); |
|
1334 |
} else { |
|
1335 |
new_index = dict_index_build_internal_non_clust(table, index); |
|
1336 |
}
|
|
1337 |
||
1338 |
new_index->search_info = btr_search_info_create(new_index->heap); |
|
1339 |
||
1340 |
/* Set the n_fields value in new_index to the actual defined
|
|
1341 |
number of fields in the cache internal representation */
|
|
1342 |
||
1343 |
new_index->n_fields = new_index->n_def; |
|
1344 |
||
1345 |
/* Add the new index as the last index for the table */
|
|
1346 |
||
1347 |
UT_LIST_ADD_LAST(indexes, table->indexes, new_index); |
|
1348 |
new_index->table = table; |
|
1349 |
new_index->table_name = table->name; |
|
1350 |
||
1351 |
/* Increment the ord_part counts in columns which are ordering */
|
|
1352 |
||
1353 |
if (UNIV_UNLIKELY(index->type & DICT_UNIVERSAL)) { |
|
1354 |
n_ord = new_index->n_fields; |
|
1355 |
} else { |
|
1356 |
n_ord = dict_index_get_n_unique(new_index); |
|
1357 |
}
|
|
1358 |
||
1359 |
for (i = 0; i < n_ord; i++) { |
|
1360 |
||
1361 |
dict_index_get_nth_field(new_index, i)->col->ord_part = 1; |
|
1362 |
}
|
|
1363 |
||
1364 |
new_index->page = (unsigned int) page_no; |
|
1365 |
rw_lock_create(&new_index->lock, SYNC_INDEX_TREE); |
|
1366 |
||
1367 |
if (!UNIV_UNLIKELY(new_index->type & DICT_UNIVERSAL)) { |
|
1368 |
||
1369 |
new_index->stat_n_diff_key_vals = mem_heap_alloc( |
|
1370 |
new_index->heap, |
|
1371 |
(1 + dict_index_get_n_unique(new_index)) |
|
1372 |
* sizeof(ib_longlong)); |
|
1373 |
/* Give some sensible values to stat_n_... in case we do
|
|
1374 |
not calculate statistics quickly enough */
|
|
1375 |
||
1376 |
for (i = 0; i <= dict_index_get_n_unique(new_index); i++) { |
|
1377 |
||
1378 |
new_index->stat_n_diff_key_vals[i] = 100; |
|
1379 |
}
|
|
1380 |
}
|
|
1381 |
||
1382 |
dict_sys->size += mem_heap_get_size(new_index->heap); |
|
1383 |
||
1384 |
dict_mem_index_free(index); |
|
1385 |
}
|
|
1386 |
||
1387 |
/**************************************************************************
|
|
1388 |
Removes an index from the dictionary cache. */
|
|
1389 |
static
|
|
1390 |
void
|
|
1391 |
dict_index_remove_from_cache( |
|
1392 |
/*=========================*/
|
|
1393 |
dict_table_t* table, /* in: table */ |
|
1394 |
dict_index_t* index) /* in, own: index */ |
|
1395 |
{
|
|
1396 |
ulint size; |
|
1397 |
||
1398 |
ut_ad(table && index); |
|
1399 |
ut_ad(table->magic_n == DICT_TABLE_MAGIC_N); |
|
1400 |
ut_ad(index->magic_n == DICT_INDEX_MAGIC_N); |
|
1401 |
ut_ad(mutex_own(&(dict_sys->mutex))); |
|
1402 |
||
1403 |
rw_lock_free(&index->lock); |
|
1404 |
||
1405 |
/* Remove the index from the list of indexes of the table */
|
|
1406 |
UT_LIST_REMOVE(indexes, table->indexes, index); |
|
1407 |
||
1408 |
size = mem_heap_get_size(index->heap); |
|
1409 |
||
1410 |
ut_ad(dict_sys->size >= size); |
|
1411 |
||
1412 |
dict_sys->size -= size; |
|
1413 |
||
1414 |
dict_mem_index_free(index); |
|
1415 |
}
|
|
1416 |
||
1417 |
/***********************************************************************
|
|
1418 |
Tries to find column names for the index and sets the col field of the
|
|
1419 |
index. */
|
|
1420 |
static
|
|
1421 |
void
|
|
1422 |
dict_index_find_cols( |
|
1423 |
/*=================*/
|
|
1424 |
dict_table_t* table, /* in: table */ |
|
1425 |
dict_index_t* index) /* in: index */ |
|
1426 |
{
|
|
1427 |
ulint i; |
|
1428 |
||
1429 |
ut_ad(table && index); |
|
1430 |
ut_ad(table->magic_n == DICT_TABLE_MAGIC_N); |
|
1431 |
ut_ad(mutex_own(&(dict_sys->mutex))); |
|
1432 |
||
1433 |
for (i = 0; i < index->n_fields; i++) { |
|
1434 |
ulint j; |
|
1435 |
dict_field_t* field = dict_index_get_nth_field(index, i); |
|
1436 |
||
1437 |
for (j = 0; j < table->n_cols; j++) { |
|
1438 |
if (!strcmp(dict_table_get_col_name(table, j), |
|
1439 |
field->name)) { |
|
1440 |
field->col = (dict_col_t*) |
|
1441 |
dict_table_get_nth_col(table, j); |
|
1442 |
||
1443 |
goto found; |
|
1444 |
}
|
|
1445 |
}
|
|
1446 |
||
1447 |
/* It is an error not to find a matching column. */
|
|
1448 |
ut_error; |
|
1449 |
||
1450 |
found: |
|
1451 |
;
|
|
1452 |
}
|
|
1453 |
}
|
|
1454 |
||
1455 |
/***********************************************************************
|
|
1456 |
Adds a column to index. */
|
|
1457 |
||
1458 |
void
|
|
1459 |
dict_index_add_col( |
|
1460 |
/*===============*/
|
|
1461 |
dict_index_t* index, /* in: index */ |
|
1462 |
dict_table_t* table, /* in: table */ |
|
1463 |
dict_col_t* col, /* in: column */ |
|
1464 |
ulint prefix_len) /* in: column prefix length */ |
|
1465 |
{
|
|
1466 |
dict_field_t* field; |
|
1467 |
const char* col_name; |
|
1468 |
||
1469 |
col_name = dict_table_get_col_name(table, dict_col_get_no(col)); |
|
1470 |
||
1471 |
dict_mem_index_add_field(index, col_name, prefix_len); |
|
1472 |
||
1473 |
field = dict_index_get_nth_field(index, index->n_def - 1); |
|
1474 |
||
1475 |
field->col = col; |
|
1476 |
field->fixed_len = (unsigned int) dict_col_get_fixed_size(col); |
|
1477 |
||
1478 |
if (prefix_len && field->fixed_len > prefix_len) { |
|
1479 |
field->fixed_len = (unsigned int) prefix_len; |
|
1480 |
}
|
|
1481 |
||
1482 |
/* Long fixed-length fields that need external storage are treated as
|
|
1483 |
variable-length fields, so that the extern flag can be embedded in
|
|
1484 |
the length word. */
|
|
1485 |
||
1486 |
if (field->fixed_len > DICT_MAX_INDEX_COL_LEN) { |
|
1487 |
field->fixed_len = 0; |
|
1488 |
}
|
|
1489 |
#if DICT_MAX_INDEX_COL_LEN != 768
|
|
1490 |
/* The comparison limit above must be constant. If it were
|
|
1491 |
changed, the disk format of some fixed-length columns would
|
|
1492 |
change, which would be a disaster. */
|
|
1493 |
# error "DICT_MAX_INDEX_COL_LEN != 768"
|
|
1494 |
#endif
|
|
1495 |
||
1496 |
if (!(col->prtype & DATA_NOT_NULL)) { |
|
1497 |
index->n_nullable++; |
|
1498 |
}
|
|
1499 |
}
|
|
1500 |
||
1501 |
/***********************************************************************
|
|
1502 |
Copies fields contained in index2 to index1. */
|
|
1503 |
static
|
|
1504 |
void
|
|
1505 |
dict_index_copy( |
|
1506 |
/*============*/
|
|
1507 |
dict_index_t* index1, /* in: index to copy to */ |
|
1508 |
dict_index_t* index2, /* in: index to copy from */ |
|
1509 |
dict_table_t* table, /* in: table */ |
|
1510 |
ulint start, /* in: first position to copy */ |
|
1511 |
ulint end) /* in: last position to copy */ |
|
1512 |
{
|
|
1513 |
dict_field_t* field; |
|
1514 |
ulint i; |
|
1515 |
||
1516 |
/* Copy fields contained in index2 */
|
|
1517 |
||
1518 |
for (i = start; i < end; i++) { |
|
1519 |
||
1520 |
field = dict_index_get_nth_field(index2, i); |
|
1521 |
dict_index_add_col(index1, table, field->col, |
|
1522 |
field->prefix_len); |
|
1523 |
}
|
|
1524 |
}
|
|
1525 |
||
1526 |
/***********************************************************************
|
|
1527 |
Copies types of fields contained in index to tuple. */
|
|
1528 |
||
1529 |
void
|
|
1530 |
dict_index_copy_types( |
|
1531 |
/*==================*/
|
|
1532 |
dtuple_t* tuple, /* in: data tuple */ |
|
1533 |
dict_index_t* index, /* in: index */ |
|
1534 |
ulint n_fields) /* in: number of field types to copy */ |
|
1535 |
{
|
|
1536 |
ulint i; |
|
1537 |
||
1538 |
if (UNIV_UNLIKELY(index->type & DICT_UNIVERSAL)) { |
|
1539 |
dtuple_set_types_binary(tuple, n_fields); |
|
1540 |
||
1541 |
return; |
|
1542 |
}
|
|
1543 |
||
1544 |
for (i = 0; i < n_fields; i++) { |
|
1545 |
dict_field_t* ifield; |
|
1546 |
dtype_t* dfield_type; |
|
1547 |
||
1548 |
ifield = dict_index_get_nth_field(index, i); |
|
1549 |
dfield_type = dfield_get_type(dtuple_get_nth_field(tuple, i)); |
|
1550 |
dict_col_copy_type(dict_field_get_col(ifield), dfield_type); |
|
1551 |
}
|
|
1552 |
}
|
|
1553 |
||
1554 |
/***********************************************************************
|
|
1555 |
Copies types of columns contained in table to tuple. */
|
|
1556 |
||
1557 |
void
|
|
1558 |
dict_table_copy_types( |
|
1559 |
/*==================*/
|
|
1560 |
dtuple_t* tuple, /* in: data tuple */ |
|
1561 |
dict_table_t* table) /* in: index */ |
|
1562 |
{
|
|
1563 |
dtype_t* dfield_type; |
|
1564 |
ulint i; |
|
1565 |
||
1566 |
for (i = 0; i < dtuple_get_n_fields(tuple); i++) { |
|
1567 |
||
1568 |
dfield_type = dfield_get_type(dtuple_get_nth_field(tuple, i)); |
|
1569 |
dict_col_copy_type(dict_table_get_nth_col(table, i), |
|
1570 |
dfield_type); |
|
1571 |
}
|
|
1572 |
}
|
|
1573 |
||
1574 |
/***********************************************************************
|
|
1575 |
Builds the internal dictionary cache representation for a clustered
|
|
1576 |
index, containing also system fields not defined by the user. */
|
|
1577 |
static
|
|
1578 |
dict_index_t* |
|
1579 |
dict_index_build_internal_clust( |
|
1580 |
/*============================*/
|
|
1581 |
/* out, own: the internal representation
|
|
1582 |
of the clustered index */
|
|
1583 |
dict_table_t* table, /* in: table */ |
|
1584 |
dict_index_t* index) /* in: user representation of a clustered |
|
1585 |
index */
|
|
1586 |
{
|
|
1587 |
dict_index_t* new_index; |
|
1588 |
dict_field_t* field; |
|
1589 |
ulint fixed_size; |
|
1590 |
ulint trx_id_pos; |
|
1591 |
ulint i; |
|
1592 |
ibool* indexed; |
|
1593 |
||
1594 |
ut_ad(table && index); |
|
1595 |
ut_ad(index->type & DICT_CLUSTERED); |
|
1596 |
ut_ad(mutex_own(&(dict_sys->mutex))); |
|
1597 |
ut_ad(table->magic_n == DICT_TABLE_MAGIC_N); |
|
1598 |
||
1599 |
/* Create a new index object with certainly enough fields */
|
|
1600 |
new_index = dict_mem_index_create(table->name, |
|
1601 |
index->name, table->space, |
|
1602 |
index->type, |
|
1603 |
index->n_fields + table->n_cols); |
|
1604 |
||
1605 |
/* Copy other relevant data from the old index struct to the new
|
|
1606 |
struct: it inherits the values */
|
|
1607 |
||
1608 |
new_index->n_user_defined_cols = index->n_fields; |
|
1609 |
||
1610 |
new_index->id = index->id; |
|
1611 |
||
1612 |
/* Copy the fields of index */
|
|
1613 |
dict_index_copy(new_index, index, table, 0, index->n_fields); |
|
1614 |
||
1615 |
if (UNIV_UNLIKELY(index->type & DICT_UNIVERSAL)) { |
|
1616 |
/* No fixed number of fields determines an entry uniquely */
|
|
1617 |
||
1618 |
new_index->n_uniq = REC_MAX_N_FIELDS; |
|
1619 |
||
1620 |
} else if (index->type & DICT_UNIQUE) { |
|
1621 |
/* Only the fields defined so far are needed to identify
|
|
1622 |
the index entry uniquely */
|
|
1623 |
||
1624 |
new_index->n_uniq = new_index->n_def; |
|
1625 |
} else { |
|
1626 |
/* Also the row id is needed to identify the entry */
|
|
1627 |
new_index->n_uniq = 1 + new_index->n_def; |
|
1628 |
}
|
|
1629 |
||
1630 |
new_index->trx_id_offset = 0; |
|
1631 |
||
1632 |
if (!(index->type & DICT_IBUF)) { |
|
1633 |
/* Add system columns, trx id first */
|
|
1634 |
||
1635 |
trx_id_pos = new_index->n_def; |
|
1636 |
||
1637 |
#if DATA_ROW_ID != 0
|
|
1638 |
# error "DATA_ROW_ID != 0"
|
|
1639 |
#endif
|
|
1640 |
#if DATA_TRX_ID != 1
|
|
1641 |
# error "DATA_TRX_ID != 1"
|
|
1642 |
#endif
|
|
1643 |
#if DATA_ROLL_PTR != 2
|
|
1644 |
# error "DATA_ROLL_PTR != 2"
|
|
1645 |
#endif
|
|
1646 |
||
1647 |
if (!(index->type & DICT_UNIQUE)) { |
|
1648 |
dict_index_add_col(new_index, table, (dict_col_t*) |
|
1649 |
dict_table_get_sys_col( |
|
1650 |
table, DATA_ROW_ID), |
|
1651 |
0); |
|
1652 |
trx_id_pos++; |
|
1653 |
}
|
|
1654 |
||
1655 |
dict_index_add_col(new_index, table, (dict_col_t*) |
|
1656 |
dict_table_get_sys_col(table, DATA_TRX_ID), |
|
1657 |
0); |
|
1658 |
||
1659 |
dict_index_add_col(new_index, table, (dict_col_t*) |
|
1660 |
dict_table_get_sys_col(table, |
|
1661 |
DATA_ROLL_PTR), |
|
1662 |
0); |
|
1663 |
||
1664 |
for (i = 0; i < trx_id_pos; i++) { |
|
1665 |
||
1666 |
fixed_size = dict_col_get_fixed_size( |
|
1667 |
dict_index_get_nth_col(new_index, i)); |
|
1668 |
||
1669 |
if (fixed_size == 0) { |
|
1670 |
new_index->trx_id_offset = 0; |
|
1671 |
||
1672 |
break; |
|
1673 |
}
|
|
1674 |
||
1675 |
if (dict_index_get_nth_field(new_index, i)->prefix_len |
|
1676 |
> 0) { |
|
1677 |
new_index->trx_id_offset = 0; |
|
1678 |
||
1679 |
break; |
|
1680 |
}
|
|
1681 |
||
1682 |
new_index->trx_id_offset += (unsigned int) fixed_size; |
|
1683 |
}
|
|
1684 |
||
1685 |
}
|
|
1686 |
||
1687 |
/* Remember the table columns already contained in new_index */
|
|
1688 |
indexed = mem_alloc(table->n_cols * sizeof *indexed); |
|
1689 |
memset(indexed, 0, table->n_cols * sizeof *indexed); |
|
1690 |
||
1691 |
/* Mark with 0 the table columns already contained in new_index */
|
|
1692 |
for (i = 0; i < new_index->n_def; i++) { |
|
1693 |
||
1694 |
field = dict_index_get_nth_field(new_index, i); |
|
1695 |
||
1696 |
/* If there is only a prefix of the column in the index
|
|
1697 |
field, do not mark the column as contained in the index */
|
|
1698 |
||
1699 |
if (field->prefix_len == 0) { |
|
1700 |
||
1701 |
indexed[field->col->ind] = TRUE; |
|
1702 |
}
|
|
1703 |
}
|
|
1704 |
||
1705 |
/* Add to new_index non-system columns of table not yet included
|
|
1706 |
there */
|
|
1707 |
for (i = 0; i + DATA_N_SYS_COLS < (ulint) table->n_cols; i++) { |
|
1708 |
||
1709 |
dict_col_t* col = (dict_col_t*) |
|
1710 |
dict_table_get_nth_col(table, i); |
|
1711 |
ut_ad(col->mtype != DATA_SYS); |
|
1712 |
||
1713 |
if (!indexed[col->ind]) { |
|
1714 |
dict_index_add_col(new_index, table, col, 0); |
|
1715 |
}
|
|
1716 |
}
|
|
1717 |
||
1718 |
mem_free(indexed); |
|
1719 |
||
1720 |
ut_ad((index->type & DICT_IBUF) |
|
1721 |
|| (UT_LIST_GET_LEN(table->indexes) == 0)); |
|
1722 |
||
1723 |
new_index->cached = TRUE; |
|
1724 |
||
1725 |
return(new_index); |
|
1726 |
}
|
|
1727 |
||
1728 |
/***********************************************************************
|
|
1729 |
Builds the internal dictionary cache representation for a non-clustered
|
|
1730 |
index, containing also system fields not defined by the user. */
|
|
1731 |
static
|
|
1732 |
dict_index_t* |
|
1733 |
dict_index_build_internal_non_clust( |
|
1734 |
/*================================*/
|
|
1735 |
/* out, own: the internal representation
|
|
1736 |
of the non-clustered index */
|
|
1737 |
dict_table_t* table, /* in: table */ |
|
1738 |
dict_index_t* index) /* in: user representation of a non-clustered |
|
1739 |
index */
|
|
1740 |
{
|
|
1741 |
dict_field_t* field; |
|
1742 |
dict_index_t* new_index; |
|
1743 |
dict_index_t* clust_index; |
|
1744 |
ulint i; |
|
1745 |
ibool* indexed; |
|
1746 |
||
1747 |
ut_ad(table && index); |
|
1748 |
ut_ad(0 == (index->type & DICT_CLUSTERED)); |
|
1749 |
ut_ad(mutex_own(&(dict_sys->mutex))); |
|
1750 |
ut_ad(table->magic_n == DICT_TABLE_MAGIC_N); |
|
1751 |
||
1752 |
/* The clustered index should be the first in the list of indexes */
|
|
1753 |
clust_index = UT_LIST_GET_FIRST(table->indexes); |
|
1754 |
||
1755 |
ut_ad(clust_index); |
|
1756 |
ut_ad(clust_index->type & DICT_CLUSTERED); |
|
1757 |
ut_ad(!(clust_index->type & DICT_UNIVERSAL)); |
|
1758 |
||
1759 |
/* Create a new index */
|
|
1760 |
new_index = dict_mem_index_create( |
|
1761 |
table->name, index->name, index->space, index->type, |
|
1762 |
index->n_fields + 1 + clust_index->n_uniq); |
|
1763 |
||
1764 |
/* Copy other relevant data from the old index
|
|
1765 |
struct to the new struct: it inherits the values */
|
|
1766 |
||
1767 |
new_index->n_user_defined_cols = index->n_fields; |
|
1768 |
||
1769 |
new_index->id = index->id; |
|
1770 |
||
1771 |
/* Copy fields from index to new_index */
|
|
1772 |
dict_index_copy(new_index, index, table, 0, index->n_fields); |
|
1773 |
||
1774 |
/* Remember the table columns already contained in new_index */
|
|
1775 |
indexed = mem_alloc(table->n_cols * sizeof *indexed); |
|
1776 |
memset(indexed, 0, table->n_cols * sizeof *indexed); |
|
1777 |
||
1778 |
/* Mark with 0 table columns already contained in new_index */
|
|
1779 |
for (i = 0; i < new_index->n_def; i++) { |
|
1780 |
||
1781 |
field = dict_index_get_nth_field(new_index, i); |
|
1782 |
||
1783 |
/* If there is only a prefix of the column in the index
|
|
1784 |
field, do not mark the column as contained in the index */
|
|
1785 |
||
1786 |
if (field->prefix_len == 0) { |
|
1787 |
||
1788 |
indexed[field->col->ind] = TRUE; |
|
1789 |
}
|
|
1790 |
}
|
|
1791 |
||
1792 |
/* Add to new_index the columns necessary to determine the clustered
|
|
1793 |
index entry uniquely */
|
|
1794 |
||
1795 |
for (i = 0; i < clust_index->n_uniq; i++) { |
|
1796 |
||
1797 |
field = dict_index_get_nth_field(clust_index, i); |
|
1798 |
||
1799 |
if (!indexed[field->col->ind]) { |
|
1800 |
dict_index_add_col(new_index, table, field->col, |
|
1801 |
field->prefix_len); |
|
1802 |
}
|
|
1803 |
}
|
|
1804 |
||
1805 |
mem_free(indexed); |
|
1806 |
||
1807 |
if ((index->type) & DICT_UNIQUE) { |
|
1808 |
new_index->n_uniq = index->n_fields; |
|
1809 |
} else { |
|
1810 |
new_index->n_uniq = new_index->n_def; |
|
1811 |
}
|
|
1812 |
||
1813 |
/* Set the n_fields value in new_index to the actual defined
|
|
1814 |
number of fields */
|
|
1815 |
||
1816 |
new_index->n_fields = new_index->n_def; |
|
1817 |
||
1818 |
new_index->cached = TRUE; |
|
1819 |
||
1820 |
return(new_index); |
|
1821 |
}
|
|
1822 |
||
1823 |
/*====================== FOREIGN KEY PROCESSING ========================*/
|
|
1824 |
||
1825 |
/*************************************************************************
|
|
1826 |
Checks if a table is referenced by foreign keys. */
|
|
1827 |
||
1828 |
ibool
|
|
1829 |
dict_table_referenced_by_foreign_key( |
|
1830 |
/*=================================*/
|
|
1831 |
/* out: TRUE if table is referenced by a
|
|
1832 |
foreign key */
|
|
1833 |
dict_table_t* table) /* in: InnoDB table */ |
|
1834 |
{
|
|
1835 |
if (UT_LIST_GET_LEN(table->referenced_list) > 0) { |
|
1836 |
||
1837 |
return(TRUE); |
|
1838 |
}
|
|
1839 |
||
1840 |
return(FALSE); |
|
1841 |
}
|
|
1842 |
||
1843 |
/*************************************************************************
|
|
1844 |
Frees a foreign key struct. */
|
|
1845 |
static
|
|
1846 |
void
|
|
1847 |
dict_foreign_free( |
|
1848 |
/*==============*/
|
|
1849 |
dict_foreign_t* foreign) /* in, own: foreign key struct */ |
|
1850 |
{
|
|
1851 |
mem_heap_free(foreign->heap); |
|
1852 |
}
|
|
1853 |
||
1854 |
/**************************************************************************
|
|
1855 |
Removes a foreign constraint struct from the dictionary cache. */
|
|
1856 |
static
|
|
1857 |
void
|
|
1858 |
dict_foreign_remove_from_cache( |
|
1859 |
/*===========================*/
|
|
1860 |
dict_foreign_t* foreign) /* in, own: foreign constraint */ |
|
1861 |
{
|
|
1862 |
ut_ad(mutex_own(&(dict_sys->mutex))); |
|
1863 |
ut_a(foreign); |
|
1864 |
||
1865 |
if (foreign->referenced_table) { |
|
1866 |
UT_LIST_REMOVE(referenced_list, |
|
1867 |
foreign->referenced_table->referenced_list, |
|
1868 |
foreign); |
|
1869 |
}
|
|
1870 |
||
1871 |
if (foreign->foreign_table) { |
|
1872 |
UT_LIST_REMOVE(foreign_list, |
|
1873 |
foreign->foreign_table->foreign_list, |
|
1874 |
foreign); |
|
1875 |
}
|
|
1876 |
||
1877 |
dict_foreign_free(foreign); |
|
1878 |
}
|
|
1879 |
||
1880 |
/**************************************************************************
|
|
1881 |
Looks for the foreign constraint from the foreign and referenced lists
|
|
1882 |
of a table. */
|
|
1883 |
static
|
|
1884 |
dict_foreign_t* |
|
1885 |
dict_foreign_find( |
|
1886 |
/*==============*/
|
|
1887 |
/* out: foreign constraint */
|
|
1888 |
dict_table_t* table, /* in: table object */ |
|
1889 |
const char* id) /* in: foreign constraint id */ |
|
1890 |
{
|
|
1891 |
dict_foreign_t* foreign; |
|
1892 |
||
1893 |
ut_ad(mutex_own(&(dict_sys->mutex))); |
|
1894 |
||
1895 |
foreign = UT_LIST_GET_FIRST(table->foreign_list); |
|
1896 |
||
1897 |
while (foreign) { |
|
1898 |
if (ut_strcmp(id, foreign->id) == 0) { |
|
1899 |
||
1900 |
return(foreign); |
|
1901 |
}
|
|
1902 |
||
1903 |
foreign = UT_LIST_GET_NEXT(foreign_list, foreign); |
|
1904 |
}
|
|
1905 |
||
1906 |
foreign = UT_LIST_GET_FIRST(table->referenced_list); |
|
1907 |
||
1908 |
while (foreign) { |
|
1909 |
if (ut_strcmp(id, foreign->id) == 0) { |
|
1910 |
||
1911 |
return(foreign); |
|
1912 |
}
|
|
1913 |
||
1914 |
foreign = UT_LIST_GET_NEXT(referenced_list, foreign); |
|
1915 |
}
|
|
1916 |
||
1917 |
return(NULL); |
|
1918 |
}
|
|
1919 |
||
1920 |
#ifndef UNIV_HOTBACKUP
|
|
1921 |
/*************************************************************************
|
|
1922 |
Tries to find an index whose first fields are the columns in the array,
|
|
1923 |
in the same order. */
|
|
1924 |
static
|
|
1925 |
dict_index_t* |
|
1926 |
dict_foreign_find_index( |
|
1927 |
/*====================*/
|
|
1928 |
/* out: matching index, NULL if not found */
|
|
1929 |
dict_table_t* table, /* in: table */ |
|
1930 |
const char** columns,/* in: array of column names */ |
|
1931 |
ulint n_cols, /* in: number of columns */ |
|
1932 |
dict_index_t* types_idx, /* in: NULL or an index to whose types the |
|
1933 |
column types must match */
|
|
1934 |
ibool check_charsets, |
|
1935 |
/* in: whether to check charsets.
|
|
1936 |
only has an effect if types_idx != NULL */
|
|
1937 |
ulint check_null) |
|
1938 |
/* in: nonzero if none of the columns must
|
|
1939 |
be declared NOT NULL */
|
|
1940 |
{
|
|
1941 |
dict_index_t* index; |
|
1942 |
dict_field_t* field; |
|
1943 |
const char* col_name; |
|
1944 |
ulint i; |
|
1945 |
||
1946 |
index = dict_table_get_first_index(table); |
|
1947 |
||
1948 |
while (index != NULL) { |
|
1949 |
if (dict_index_get_n_fields(index) >= n_cols) { |
|
1950 |
||
1951 |
for (i = 0; i < n_cols; i++) { |
|
1952 |
field = dict_index_get_nth_field(index, i); |
|
1953 |
||
1954 |
col_name = dict_table_get_col_name( |
|
1955 |
table, dict_col_get_no(field->col)); |
|
1956 |
||
1957 |
if (field->prefix_len != 0) { |
|
1958 |
/* We do not accept column prefix
|
|
1959 |
indexes here */
|
|
1960 |
||
1961 |
break; |
|
1962 |
}
|
|
1963 |
||
1964 |
if (0 != innobase_strcasecmp(columns[i], |
|
1965 |
col_name)) { |
|
1966 |
break; |
|
1967 |
}
|
|
1968 |
||
1969 |
if (check_null |
|
1970 |
&& (field->col->prtype & DATA_NOT_NULL)) { |
|
1971 |
||
1972 |
return(NULL); |
|
1973 |
}
|
|
1974 |
||
1975 |
if (types_idx && !cmp_cols_are_equal( |
|
1976 |
dict_index_get_nth_col(index, i), |
|
1977 |
dict_index_get_nth_col(types_idx, |
|
1978 |
i), |
|
1979 |
check_charsets)) { |
|
1980 |
||
1981 |
break; |
|
1982 |
}
|
|
1983 |
}
|
|
1984 |
||
1985 |
if (i == n_cols) { |
|
1986 |
/* We found a matching index */
|
|
1987 |
||
1988 |
return(index); |
|
1989 |
}
|
|
1990 |
}
|
|
1991 |
||
1992 |
index = dict_table_get_next_index(index); |
|
1993 |
}
|
|
1994 |
||
1995 |
return(NULL); |
|
1996 |
}
|
|
1997 |
||
1998 |
/**************************************************************************
|
|
1999 |
Report an error in a foreign key definition. */
|
|
2000 |
static
|
|
2001 |
void
|
|
2002 |
dict_foreign_error_report_low( |
|
2003 |
/*==========================*/
|
|
2004 |
FILE* file, /* in: output stream */ |
|
2005 |
const char* name) /* in: table name */ |
|
2006 |
{
|
|
2007 |
rewind(file); |
|
2008 |
ut_print_timestamp(file); |
|
2009 |
fprintf(file, " Error in foreign key constraint of table %s:\n", |
|
2010 |
name); |
|
2011 |
}
|
|
2012 |
||
2013 |
/**************************************************************************
|
|
2014 |
Report an error in a foreign key definition. */
|
|
2015 |
static
|
|
2016 |
void
|
|
2017 |
dict_foreign_error_report( |
|
2018 |
/*======================*/
|
|
2019 |
FILE* file, /* in: output stream */ |
|
2020 |
dict_foreign_t* fk, /* in: foreign key constraint */ |
|
2021 |
const char* msg) /* in: the error message */ |
|
2022 |
{
|
|
2023 |
mutex_enter(&dict_foreign_err_mutex); |
|
2024 |
dict_foreign_error_report_low(file, fk->foreign_table_name); |
|
2025 |
fputs(msg, file); |
|
2026 |
fputs(" Constraint:\n", file); |
|
2027 |
dict_print_info_on_foreign_key_in_create_format(file, NULL, fk, TRUE); |
|
2028 |
putc('\n', file); |
|
2029 |
if (fk->foreign_index) { |
|
2030 |
fputs("The index in the foreign key in table is ", file); |
|
2031 |
ut_print_name(file, NULL, FALSE, fk->foreign_index->name); |
|
2032 |
fputs("\n" |
|
2033 |
"See http://dev.mysql.com/doc/refman/5.1/en/"
|
|
2034 |
"innodb-foreign-key-constraints.html\n" |
|
2035 |
"for correct foreign key definition.\n", |
|
2036 |
file); |
|
2037 |
}
|
|
2038 |
mutex_exit(&dict_foreign_err_mutex); |
|
2039 |
}
|
|
2040 |
||
2041 |
/**************************************************************************
|
|
2042 |
Adds a foreign key constraint object to the dictionary cache. May free
|
|
2043 |
the object if there already is an object with the same identifier in.
|
|
2044 |
At least one of the foreign table and the referenced table must already
|
|
2045 |
be in the dictionary cache! */
|
|
2046 |
||
2047 |
ulint
|
|
2048 |
dict_foreign_add_to_cache( |
|
2049 |
/*======================*/
|
|
2050 |
/* out: DB_SUCCESS or error code */
|
|
2051 |
dict_foreign_t* foreign, /* in, own: foreign key constraint */ |
|
2052 |
ibool check_charsets) /* in: TRUE=check charset |
|
2053 |
compatibility */
|
|
2054 |
{
|
|
2055 |
dict_table_t* for_table; |
|
2056 |
dict_table_t* ref_table; |
|
2057 |
dict_foreign_t* for_in_cache = NULL; |
|
2058 |
dict_index_t* index; |
|
2059 |
ibool added_to_referenced_list= FALSE; |
|
2060 |
FILE* ef = dict_foreign_err_file; |
|
2061 |
||
2062 |
ut_ad(mutex_own(&(dict_sys->mutex))); |
|
2063 |
||
2064 |
for_table = dict_table_check_if_in_cache_low( |
|
2065 |
foreign->foreign_table_name); |
|
2066 |
||
2067 |
ref_table = dict_table_check_if_in_cache_low( |
|
2068 |
foreign->referenced_table_name); |
|
2069 |
ut_a(for_table || ref_table); |
|
2070 |
||
2071 |
if (for_table) { |
|
2072 |
for_in_cache = dict_foreign_find(for_table, foreign->id); |
|
2073 |
}
|
|
2074 |
||
2075 |
if (!for_in_cache && ref_table) { |
|
2076 |
for_in_cache = dict_foreign_find(ref_table, foreign->id); |
|
2077 |
}
|
|
2078 |
||
2079 |
if (for_in_cache) { |
|
2080 |
/* Free the foreign object */
|
|
2081 |
mem_heap_free(foreign->heap); |
|
2082 |
} else { |
|
2083 |
for_in_cache = foreign; |
|
2084 |
}
|
|
2085 |
||
2086 |
if (for_in_cache->referenced_table == NULL && ref_table) { |
|
2087 |
index = dict_foreign_find_index( |
|
2088 |
ref_table, |
|
2089 |
(const char**) for_in_cache->referenced_col_names, |
|
2090 |
for_in_cache->n_fields, for_in_cache->foreign_index, |
|
2091 |
check_charsets, FALSE); |
|
2092 |
||
2093 |
if (index == NULL) { |
|
2094 |
dict_foreign_error_report( |
|
2095 |
ef, for_in_cache, |
|
2096 |
"there is no index in referenced table"
|
|
2097 |
" which would contain\n" |
|
2098 |
"the columns as the first columns,"
|
|
2099 |
" or the data types in the\n" |
|
2100 |
"referenced table do not match"
|
|
2101 |
" the ones in table."); |
|
2102 |
||
2103 |
if (for_in_cache == foreign) { |
|
2104 |
mem_heap_free(foreign->heap); |
|
2105 |
}
|
|
2106 |
||
2107 |
return(DB_CANNOT_ADD_CONSTRAINT); |
|
2108 |
}
|
|
2109 |
||
2110 |
for_in_cache->referenced_table = ref_table; |
|
2111 |
for_in_cache->referenced_index = index; |
|
2112 |
UT_LIST_ADD_LAST(referenced_list, |
|
2113 |
ref_table->referenced_list, |
|
2114 |
for_in_cache); |
|
2115 |
added_to_referenced_list = TRUE; |
|
2116 |
}
|
|
2117 |
||
2118 |
if (for_in_cache->foreign_table == NULL && for_table) { |
|
2119 |
index = dict_foreign_find_index( |
|
2120 |
for_table, |
|
2121 |
(const char**) for_in_cache->foreign_col_names, |
|
2122 |
for_in_cache->n_fields, |
|
2123 |
for_in_cache->referenced_index, check_charsets, |
|
2124 |
for_in_cache->type |
|
2125 |
& (DICT_FOREIGN_ON_DELETE_SET_NULL |
|
2126 |
| DICT_FOREIGN_ON_UPDATE_SET_NULL)); |
|
2127 |
||
2128 |
if (index == NULL) { |
|
2129 |
dict_foreign_error_report( |
|
2130 |
ef, for_in_cache, |
|
2131 |
"there is no index in the table"
|
|
2132 |
" which would contain\n" |
|
2133 |
"the columns as the first columns,"
|
|
2134 |
" or the data types in the\n" |
|
2135 |
"table do not match"
|
|
2136 |
" the ones in the referenced table\n" |
|
2137 |
"or one of the ON ... SET NULL columns"
|
|
2138 |
" is declared NOT NULL."); |
|
2139 |
||
2140 |
if (for_in_cache == foreign) { |
|
2141 |
if (added_to_referenced_list) { |
|
2142 |
UT_LIST_REMOVE( |
|
2143 |
referenced_list, |
|
2144 |
ref_table->referenced_list, |
|
2145 |
for_in_cache); |
|
2146 |
}
|
|
2147 |
||
2148 |
mem_heap_free(foreign->heap); |
|
2149 |
}
|
|
2150 |
||
2151 |
return(DB_CANNOT_ADD_CONSTRAINT); |
|
2152 |
}
|
|
2153 |
||
2154 |
for_in_cache->foreign_table = for_table; |
|
2155 |
for_in_cache->foreign_index = index; |
|
2156 |
UT_LIST_ADD_LAST(foreign_list, |
|
2157 |
for_table->foreign_list, |
|
2158 |
for_in_cache); |
|
2159 |
}
|
|
2160 |
||
2161 |
return(DB_SUCCESS); |
|
2162 |
}
|
|
2163 |
||
2164 |
/*************************************************************************
|
|
2165 |
Scans from pointer onwards. Stops if is at the start of a copy of
|
|
2166 |
'string' where characters are compared without case sensitivity, and
|
|
2167 |
only outside `` or "" quotes. Stops also at '\0'. */
|
|
2168 |
||
2169 |
const char* |
|
2170 |
dict_scan_to( |
|
2171 |
/*=========*/
|
|
2172 |
/* out: scanned up to this */
|
|
2173 |
const char* ptr, /* in: scan from */ |
|
2174 |
const char* string) /* in: look for this */ |
|
2175 |
{
|
|
2176 |
char quote = '\0'; |
|
2177 |
||
2178 |
for (; *ptr; ptr++) { |
|
2179 |
if (*ptr == quote) { |
|
2180 |
/* Closing quote character: do not look for
|
|
2181 |
starting quote or the keyword. */
|
|
2182 |
quote = '\0'; |
|
2183 |
} else if (quote) { |
|
2184 |
/* Within quotes: do nothing. */
|
|
2185 |
} else if (*ptr == '`' || *ptr == '"') { |
|
2186 |
/* Starting quote: remember the quote character. */
|
|
2187 |
quote = *ptr; |
|
2188 |
} else { |
|
2189 |
/* Outside quotes: look for the keyword. */
|
|
2190 |
ulint i; |
|
2191 |
for (i = 0; string[i]; i++) { |
|
2192 |
if (toupper((int)(unsigned char)(ptr[i])) |
|
2193 |
!= toupper((int)(unsigned char) |
|
2194 |
(string[i]))) { |
|
2195 |
goto nomatch; |
|
2196 |
}
|
|
2197 |
}
|
|
2198 |
break; |
|
2199 |
nomatch: |
|
2200 |
;
|
|
2201 |
}
|
|
2202 |
}
|
|
2203 |
||
2204 |
return(ptr); |
|
2205 |
}
|
|
2206 |
||
2207 |
/*************************************************************************
|
|
2208 |
Accepts a specified string. Comparisons are case-insensitive. */
|
|
2209 |
static
|
|
2210 |
const char* |
|
2211 |
dict_accept( |
|
2212 |
/*========*/
|
|
2213 |
/* out: if string was accepted, the pointer
|
|
2214 |
is moved after that, else ptr is returned */
|
|
2215 |
struct charset_info_st* cs,/* in: the character set of ptr */ |
|
2216 |
const char* ptr, /* in: scan from this */ |
|
2217 |
const char* string, /* in: accept only this string as the next |
|
2218 |
non-whitespace string */
|
|
2219 |
ibool* success)/* out: TRUE if accepted */ |
|
2220 |
{
|
|
2221 |
const char* old_ptr = ptr; |
|
2222 |
const char* old_ptr2; |
|
2223 |
||
2224 |
*success = FALSE; |
|
2225 |
||
2226 |
while (my_isspace(cs, *ptr)) { |
|
2227 |
ptr++; |
|
2228 |
}
|
|
2229 |
||
2230 |
old_ptr2 = ptr; |
|
2231 |
||
2232 |
ptr = dict_scan_to(ptr, string); |
|
2233 |
||
2234 |
if (*ptr == '\0' || old_ptr2 != ptr) { |
|
2235 |
return(old_ptr); |
|
2236 |
}
|
|
2237 |
||
2238 |
*success = TRUE; |
|
2239 |
||
2240 |
return(ptr + ut_strlen(string)); |
|
2241 |
}
|
|
2242 |
||
2243 |
/*************************************************************************
|
|
2244 |
Scans an id. For the lexical definition of an 'id', see the code below.
|
|
2245 |
Strips backquotes or double quotes from around the id. */
|
|
2246 |
static
|
|
2247 |
const char* |
|
2248 |
dict_scan_id( |
|
2249 |
/*=========*/
|
|
2250 |
/* out: scanned to */
|
|
2251 |
struct charset_info_st* cs,/* in: the character set of ptr */ |
|
2252 |
const char* ptr, /* in: scanned to */ |
|
2253 |
mem_heap_t* heap, /* in: heap where to allocate the id |
|
2254 |
(NULL=id will not be allocated, but it
|
|
2255 |
will point to string near ptr) */
|
|
2256 |
const char** id, /* out,own: the id; NULL if no id was |
|
2257 |
scannable */
|
|
2258 |
ibool table_id,/* in: TRUE=convert the allocated id |
|
2259 |
as a table name; FALSE=convert to UTF-8 */
|
|
2260 |
ibool accept_also_dot) |
|
2261 |
/* in: TRUE if also a dot can appear in a
|
|
2262 |
non-quoted id; in a quoted id it can appear
|
|
2263 |
always */
|
|
2264 |
{
|
|
2265 |
char quote = '\0'; |
|
2266 |
ulint len = 0; |
|
2267 |
const char* s; |
|
2268 |
char* str; |
|
2269 |
char* dst; |
|
2270 |
||
2271 |
*id = NULL; |
|
2272 |
||
2273 |
while (my_isspace(cs, *ptr)) { |
|
2274 |
ptr++; |
|
2275 |
}
|
|
2276 |
||
2277 |
if (*ptr == '\0') { |
|
2278 |
||
2279 |
return(ptr); |
|
2280 |
}
|
|
2281 |
||
2282 |
if (*ptr == '`' || *ptr == '"') { |
|
2283 |
quote = *ptr++; |
|
2284 |
}
|
|
2285 |
||
2286 |
s = ptr; |
|
2287 |
||
2288 |
if (quote) { |
|
2289 |
for (;;) { |
|
2290 |
if (!*ptr) { |
|
2291 |
/* Syntax error */
|
|
2292 |
return(ptr); |
|
2293 |
}
|
|
2294 |
if (*ptr == quote) { |
|
2295 |
ptr++; |
|
2296 |
if (*ptr != quote) { |
|
2297 |
break; |
|
2298 |
}
|
|
2299 |
}
|
|
2300 |
ptr++; |
|
2301 |
len++; |
|
2302 |
}
|
|
2303 |
} else { |
|
2304 |
while (!my_isspace(cs, *ptr) && *ptr != '(' && *ptr != ')' |
|
2305 |
&& (accept_also_dot || *ptr != '.') |
|
2306 |
&& *ptr != ',' && *ptr != '\0') { |
|
2307 |
||
2308 |
ptr++; |
|
2309 |
}
|
|
2310 |
||
2311 |
len = ptr - s; |
|
2312 |
}
|
|
2313 |
||
2314 |
if (UNIV_UNLIKELY(!heap)) { |
|
2315 |
/* no heap given: id will point to source string */
|
|
2316 |
*id = s; |
|
2317 |
return(ptr); |
|
2318 |
}
|
|
2319 |
||
2320 |
if (quote) { |
|
2321 |
char* d; |
|
2322 |
str = d = mem_heap_alloc(heap, len + 1); |
|
2323 |
while (len--) { |
|
2324 |
if ((*d++ = *s++) == quote) { |
|
2325 |
s++; |
|
2326 |
}
|
|
2327 |
}
|
|
2328 |
*d++ = 0; |
|
2329 |
len = d - str; |
|
2330 |
ut_ad(*s == quote); |
|
2331 |
ut_ad(s + 1 == ptr); |
|
2332 |
} else { |
|
2333 |
str = mem_heap_strdupl(heap, s, len); |
|
2334 |
}
|
|
2335 |
||
2336 |
if (!table_id) { |
|
2337 |
convert_id: |
|
2338 |
/* Convert the identifier from connection character set
|
|
2339 |
to UTF-8. */
|
|
2340 |
len = 3 * len + 1; |
|
2341 |
*id = dst = mem_heap_alloc(heap, len); |
|
2342 |
||
2343 |
innobase_convert_from_id(dst, str, len); |
|
2344 |
} else if (!strncmp(str, srv_mysql50_table_name_prefix, |
|
2345 |
sizeof srv_mysql50_table_name_prefix)) { |
|
2346 |
/* This is a pre-5.1 table name
|
|
2347 |
containing chars other than [A-Za-z0-9].
|
|
2348 |
Discard the prefix and use raw UTF-8 encoding. */
|
|
2349 |
str += sizeof srv_mysql50_table_name_prefix; |
|
2350 |
len -= sizeof srv_mysql50_table_name_prefix; |
|
2351 |
goto convert_id; |
|
2352 |
} else { |
|
2353 |
/* Encode using filename-safe characters. */
|
|
2354 |
len = 5 * len + 1; |
|
2355 |
*id = dst = mem_heap_alloc(heap, len); |
|
2356 |
||
2357 |
innobase_convert_from_table_id(dst, str, len); |
|
2358 |
}
|
|
2359 |
||
2360 |
return(ptr); |
|
2361 |
}
|
|
2362 |
||
2363 |
/*************************************************************************
|
|
2364 |
Tries to scan a column name. */
|
|
2365 |
static
|
|
2366 |
const char* |
|
2367 |
dict_scan_col( |
|
2368 |
/*==========*/
|
|
2369 |
/* out: scanned to */
|
|
2370 |
struct charset_info_st* cs, /* in: the character set of ptr */ |
|
2371 |
const char* ptr, /* in: scanned to */ |
|
2372 |
ibool* success,/* out: TRUE if success */ |
|
2373 |
dict_table_t* table, /* in: table in which the column is */ |
|
2374 |
const dict_col_t** column, /* out: pointer to column if success */ |
|
2375 |
mem_heap_t* heap, /* in: heap where to allocate */ |
|
2376 |
const char** name) /* out,own: the column name; |
|
2377 |
NULL if no name was scannable */
|
|
2378 |
{
|
|
2379 |
ulint i; |
|
2380 |
||
2381 |
*success = FALSE; |
|
2382 |
||
2383 |
ptr = dict_scan_id(cs, ptr, heap, name, FALSE, TRUE); |
|
2384 |
||
2385 |
if (*name == NULL) { |
|
2386 |
||
2387 |
return(ptr); /* Syntax error */ |
|
2388 |
}
|
|
2389 |
||
2390 |
if (table == NULL) { |
|
2391 |
*success = TRUE; |
|
2392 |
*column = NULL; |
|
2393 |
} else { |
|
2394 |
for (i = 0; i < dict_table_get_n_cols(table); i++) { |
|
2395 |
||
2396 |
const char* col_name = dict_table_get_col_name( |
|
2397 |
table, i); |
|
2398 |
||
2399 |
if (0 == innobase_strcasecmp(col_name, *name)) { |
|
2400 |
/* Found */
|
|
2401 |
||
2402 |
*success = TRUE; |
|
2403 |
*column = dict_table_get_nth_col(table, i); |
|
2404 |
strcpy((char*) *name, col_name); |
|
2405 |
||
2406 |
break; |
|
2407 |
}
|
|
2408 |
}
|
|
2409 |
}
|
|
2410 |
||
2411 |
return(ptr); |
|
2412 |
}
|
|
2413 |
||
2414 |
/*************************************************************************
|
|
2415 |
Scans a table name from an SQL string. */
|
|
2416 |
static
|
|
2417 |
const char* |
|
2418 |
dict_scan_table_name( |
|
2419 |
/*=================*/
|
|
2420 |
/* out: scanned to */
|
|
2421 |
struct charset_info_st* cs,/* in: the character set of ptr */ |
|
2422 |
const char* ptr, /* in: scanned to */ |
|
2423 |
dict_table_t** table, /* out: table object or NULL */ |
|
2424 |
const char* name, /* in: foreign key table name */ |
|
2425 |
ibool* success,/* out: TRUE if ok name found */ |
|
2426 |
mem_heap_t* heap, /* in: heap where to allocate the id */ |
|
2427 |
const char** ref_name)/* out,own: the table name; |
|
2428 |
NULL if no name was scannable */
|
|
2429 |
{
|
|
2430 |
const char* database_name = NULL; |
|
2431 |
ulint database_name_len = 0; |
|
2432 |
const char* table_name = NULL; |
|
2433 |
ulint table_name_len; |
|
2434 |
const char* scan_name; |
|
2435 |
char* ref; |
|
2436 |
||
2437 |
*success = FALSE; |
|
2438 |
*table = NULL; |
|
2439 |
||
2440 |
ptr = dict_scan_id(cs, ptr, heap, &scan_name, TRUE, FALSE); |
|
2441 |
||
2442 |
if (scan_name == NULL) { |
|
2443 |
||
2444 |
return(ptr); /* Syntax error */ |
|
2445 |
}
|
|
2446 |
||
2447 |
if (*ptr == '.') { |
|
2448 |
/* We scanned the database name; scan also the table name */
|
|
2449 |
||
2450 |
ptr++; |
|
2451 |
||
2452 |
database_name = scan_name; |
|
2453 |
database_name_len = strlen(database_name); |
|
2454 |
||
2455 |
ptr = dict_scan_id(cs, ptr, heap, &table_name, TRUE, FALSE); |
|
2456 |
||
2457 |
if (table_name == NULL) { |
|
2458 |
||
2459 |
return(ptr); /* Syntax error */ |
|
2460 |
}
|
|
2461 |
} else { |
|
2462 |
/* To be able to read table dumps made with InnoDB-4.0.17 or
|
|
2463 |
earlier, we must allow the dot separator between the database
|
|
2464 |
name and the table name also to appear within a quoted
|
|
2465 |
identifier! InnoDB used to print a constraint as:
|
|
2466 |
... REFERENCES `databasename.tablename` ...
|
|
2467 |
starting from 4.0.18 it is
|
|
2468 |
... REFERENCES `databasename`.`tablename` ... */
|
|
2469 |
const char* s; |
|
2470 |
||
2471 |
for (s = scan_name; *s; s++) { |
|
2472 |
if (*s == '.') { |
|
2473 |
database_name = scan_name; |
|
2474 |
database_name_len = s - scan_name; |
|
2475 |
scan_name = ++s; |
|
2476 |
break;/* to do: multiple dots? */ |
|
2477 |
}
|
|
2478 |
}
|
|
2479 |
||
2480 |
table_name = scan_name; |
|
2481 |
}
|
|
2482 |
||
2483 |
if (database_name == NULL) { |
|
2484 |
/* Use the database name of the foreign key table */
|
|
2485 |
||
2486 |
database_name = name; |
|
2487 |
database_name_len = dict_get_db_name_len(name); |
|
2488 |
}
|
|
2489 |
||
2490 |
table_name_len = strlen(table_name); |
|
2491 |
||
2492 |
/* Copy database_name, '/', table_name, '\0' */
|
|
2493 |
ref = mem_heap_alloc(heap, database_name_len + table_name_len + 2); |
|
2494 |
memcpy(ref, database_name, database_name_len); |
|
2495 |
ref[database_name_len] = '/'; |
|
2496 |
memcpy(ref + database_name_len + 1, table_name, table_name_len + 1); |
|
2497 |
#ifndef __WIN__
|
|
2498 |
if (srv_lower_case_table_names) { |
|
2499 |
#endif /* !__WIN__ */ |
|
2500 |
/* The table name is always put to lower case on Windows. */
|
|
2501 |
innobase_casedn_str(ref); |
|
2502 |
#ifndef __WIN__
|
|
2503 |
}
|
|
2504 |
#endif /* !__WIN__ */ |
|
2505 |
||
2506 |
*success = TRUE; |
|
2507 |
*ref_name = ref; |
|
2508 |
*table = dict_table_get_low(ref); |
|
2509 |
||
2510 |
return(ptr); |
|
2511 |
}
|
|
2512 |
||
2513 |
/*************************************************************************
|
|
2514 |
Skips one id. The id is allowed to contain also '.'. */
|
|
2515 |
static
|
|
2516 |
const char* |
|
2517 |
dict_skip_word( |
|
2518 |
/*===========*/
|
|
2519 |
/* out: scanned to */
|
|
2520 |
struct charset_info_st* cs,/* in: the character set of ptr */ |
|
2521 |
const char* ptr, /* in: scanned to */ |
|
2522 |
ibool* success)/* out: TRUE if success, FALSE if just spaces |
|
2523 |
left in string or a syntax error */
|
|
2524 |
{
|
|
2525 |
const char* start; |
|
2526 |
||
2527 |
*success = FALSE; |
|
2528 |
||
2529 |
ptr = dict_scan_id(cs, ptr, NULL, &start, FALSE, TRUE); |
|
2530 |
||
2531 |
if (start) { |
|
2532 |
*success = TRUE; |
|
2533 |
}
|
|
2534 |
||
2535 |
return(ptr); |
|
2536 |
}
|
|
2537 |
||
2538 |
/*************************************************************************
|
|
2539 |
Removes MySQL comments from an SQL string. A comment is either
|
|
2540 |
(a) '#' to the end of the line,
|
|
2541 |
(b) '--<space>' to the end of the line, or
|
|
2542 |
(c) '<slash><asterisk>' till the next '<asterisk><slash>' (like the familiar
|
|
2543 |
C comment syntax). */
|
|
2544 |
static
|
|
2545 |
char* |
|
2546 |
dict_strip_comments( |
|
2547 |
/*================*/
|
|
2548 |
/* out, own: SQL string stripped from
|
|
2549 |
comments; the caller must free this
|
|
2550 |
with mem_free()! */
|
|
2551 |
const char* sql_string) /* in: SQL string */ |
|
2552 |
{
|
|
2553 |
char* str; |
|
2554 |
const char* sptr; |
|
2555 |
char* ptr; |
|
2556 |
/* unclosed quote character (0 if none) */
|
|
2557 |
char quote = 0; |
|
2558 |
||
2559 |
str = mem_alloc(strlen(sql_string) + 1); |
|
2560 |
||
2561 |
sptr = sql_string; |
|
2562 |
ptr = str; |
|
2563 |
||
2564 |
for (;;) { |
|
2565 |
scan_more: |
|
2566 |
if (*sptr == '\0') { |
|
2567 |
*ptr = '\0'; |
|
2568 |
||
2569 |
ut_a(ptr <= str + strlen(sql_string)); |
|
2570 |
||
2571 |
return(str); |
|
2572 |
}
|
|
2573 |
||
2574 |
if (*sptr == quote) { |
|
2575 |
/* Closing quote character: do not look for
|
|
2576 |
starting quote or comments. */
|
|
2577 |
quote = 0; |
|
2578 |
} else if (quote) { |
|
2579 |
/* Within quotes: do not look for
|
|
2580 |
starting quotes or comments. */
|
|
2581 |
} else if (*sptr == '"' || *sptr == '`') { |
|
2582 |
/* Starting quote: remember the quote character. */
|
|
2583 |
quote = *sptr; |
|
2584 |
} else if (*sptr == '#' |
|
2585 |
|| (sptr[0] == '-' && sptr[1] == '-' |
|
2586 |
&& sptr[2] == ' ')) { |
|
2587 |
for (;;) { |
|
2588 |
/* In Unix a newline is 0x0A while in Windows
|
|
2589 |
it is 0x0D followed by 0x0A */
|
|
2590 |
||
2591 |
if (*sptr == (char)0x0A |
|
2592 |
|| *sptr == (char)0x0D |
|
2593 |
|| *sptr == '\0') { |
|
2594 |
||
2595 |
goto scan_more; |
|
2596 |
}
|
|
2597 |
||
2598 |
sptr++; |
|
2599 |
}
|
|
2600 |
} else if (!quote && *sptr == '/' && *(sptr + 1) == '*') { |
|
2601 |
for (;;) { |
|
2602 |
if (*sptr == '*' && *(sptr + 1) == '/') { |
|
2603 |
||
2604 |
sptr += 2; |
|
2605 |
||
2606 |
goto scan_more; |
|
2607 |
}
|
|
2608 |
||
2609 |
if (*sptr == '\0') { |
|
2610 |
||
2611 |
goto scan_more; |
|
2612 |
}
|
|
2613 |
||
2614 |
sptr++; |
|
2615 |
}
|
|
2616 |
}
|
|
2617 |
||
2618 |
*ptr = *sptr; |
|
2619 |
||
2620 |
ptr++; |
|
2621 |
sptr++; |
|
2622 |
}
|
|
2623 |
}
|
|
2624 |
||
2625 |
/*************************************************************************
|
|
2626 |
Finds the highest <number> for foreign key constraints of the table. Looks
|
|
2627 |
only at the >= 4.0.18-format id's, which are of the form
|
|
2628 |
databasename/tablename_ibfk_<number>. */
|
|
2629 |
static
|
|
2630 |
ulint
|
|
2631 |
dict_table_get_highest_foreign_id( |
|
2632 |
/*==============================*/
|
|
2633 |
/* out: highest number, 0 if table has no new
|
|
2634 |
format foreign key constraints */
|
|
2635 |
dict_table_t* table) /* in: table in the dictionary memory cache */ |
|
2636 |
{
|
|
2637 |
dict_foreign_t* foreign; |
|
2638 |
char* endp; |
|
2639 |
ulint biggest_id = 0; |
|
2640 |
ulint id; |
|
2641 |
ulint len; |
|
2642 |
||
2643 |
ut_a(table); |
|
2644 |
||
2645 |
len = ut_strlen(table->name); |
|
2646 |
foreign = UT_LIST_GET_FIRST(table->foreign_list); |
|
2647 |
||
2648 |
while (foreign) { |
|
2649 |
if (ut_strlen(foreign->id) > ((sizeof dict_ibfk) - 1) + len |
|
2650 |
&& 0 == ut_memcmp(foreign->id, table->name, len) |
|
2651 |
&& 0 == ut_memcmp(foreign->id + len, |
|
2652 |
dict_ibfk, (sizeof dict_ibfk) - 1) |
|
2653 |
&& foreign->id[len + ((sizeof dict_ibfk) - 1)] != '0') { |
|
2654 |
/* It is of the >= 4.0.18 format */
|
|
2655 |
||
2656 |
id = strtoul(foreign->id + len |
|
2657 |
+ ((sizeof dict_ibfk) - 1), |
|
2658 |
&endp, 10); |
|
2659 |
if (*endp == '\0') { |
|
2660 |
ut_a(id != biggest_id); |
|
2661 |
||
2662 |
if (id > biggest_id) { |
|
2663 |
biggest_id = id; |
|
2664 |
}
|
|
2665 |
}
|
|
2666 |
}
|
|
2667 |
||
2668 |
foreign = UT_LIST_GET_NEXT(foreign_list, foreign); |
|
2669 |
}
|
|
2670 |
||
2671 |
return(biggest_id); |
|
2672 |
}
|
|
2673 |
||
2674 |
/*************************************************************************
|
|
2675 |
Reports a simple foreign key create clause syntax error. */
|
|
2676 |
static
|
|
2677 |
void
|
|
2678 |
dict_foreign_report_syntax_err( |
|
2679 |
/*===========================*/
|
|
2680 |
const char* name, /* in: table name */ |
|
2681 |
const char* start_of_latest_foreign, |
|
2682 |
/* in: start of the foreign key clause
|
|
2683 |
in the SQL string */
|
|
2684 |
const char* ptr) /* in: place of the syntax error */ |
|
2685 |
{
|
|
2686 |
FILE* ef = dict_foreign_err_file; |
|
2687 |
||
2688 |
mutex_enter(&dict_foreign_err_mutex); |
|
2689 |
dict_foreign_error_report_low(ef, name); |
|
2690 |
fprintf(ef, "%s:\nSyntax error close to:\n%s\n", |
|
2691 |
start_of_latest_foreign, ptr); |
|
2692 |
mutex_exit(&dict_foreign_err_mutex); |
|
2693 |
}
|
|
2694 |
||
2695 |
/*************************************************************************
|
|
2696 |
Scans a table create SQL string and adds to the data dictionary the foreign
|
|
2697 |
key constraints declared in the string. This function should be called after
|
|
2698 |
the indexes for a table have been created. Each foreign key constraint must
|
|
2699 |
be accompanied with indexes in both participating tables. The indexes are
|
|
2700 |
allowed to contain more fields than mentioned in the constraint. */
|
|
2701 |
static
|
|
2702 |
ulint
|
|
2703 |
dict_create_foreign_constraints_low( |
|
2704 |
/*================================*/
|
|
2705 |
/* out: error code or DB_SUCCESS */
|
|
2706 |
trx_t* trx, /* in: transaction */ |
|
2707 |
mem_heap_t* heap, /* in: memory heap */ |
|
2708 |
struct charset_info_st* cs,/* in: the character set of sql_string */ |
|
2709 |
const char* sql_string, |
|
2710 |
/* in: CREATE TABLE or ALTER TABLE statement
|
|
2711 |
where foreign keys are declared like:
|
|
2712 |
FOREIGN KEY (a, b) REFERENCES table2(c, d),
|
|
2713 |
table2 can be written also with the database
|
|
2714 |
name before it: test.table2; the default
|
|
2715 |
database is the database of parameter name */
|
|
2716 |
const char* name, /* in: table full name in the normalized form |
|
2717 |
database_name/table_name */
|
|
2718 |
ibool reject_fks) |
|
2719 |
/* in: if TRUE, fail with error code
|
|
2720 |
DB_CANNOT_ADD_CONSTRAINT if any foreign
|
|
2721 |
keys are found. */
|
|
2722 |
{
|
|
2723 |
dict_table_t* table; |
|
2724 |
dict_table_t* referenced_table; |
|
2725 |
dict_table_t* table_to_alter; |
|
2726 |
ulint highest_id_so_far = 0; |
|
2727 |
dict_index_t* index; |
|
2728 |
dict_foreign_t* foreign; |
|
2729 |
const char* ptr = sql_string; |
|
2730 |
const char* start_of_latest_foreign = sql_string; |
|
2731 |
FILE* ef = dict_foreign_err_file; |
|
2732 |
const char* constraint_name; |
|
2733 |
ibool success; |
|
2734 |
ulint error; |
|
2735 |
const char* ptr1; |
|
2736 |
const char* ptr2; |
|
2737 |
ulint i; |
|
2738 |
ulint j; |
|
2739 |
ibool is_on_delete; |
|
2740 |
ulint n_on_deletes; |
|
2741 |
ulint n_on_updates; |
|
2742 |
const dict_col_t*columns[500]; |
|
2743 |
const char* column_names[500]; |
|
2744 |
const char* referenced_table_name; |
|
2745 |
||
2746 |
ut_ad(mutex_own(&(dict_sys->mutex))); |
|
2747 |
||
2748 |
table = dict_table_get_low(name); |
|
2749 |
||
2750 |
if (table == NULL) { |
|
2751 |
mutex_enter(&dict_foreign_err_mutex); |
|
2752 |
dict_foreign_error_report_low(ef, name); |
|
2753 |
fprintf(ef, |
|
2754 |
"Cannot find the table in the internal"
|
|
2755 |
" data dictionary of InnoDB.\n" |
|
2756 |
"Create table statement:\n%s\n", sql_string); |
|
2757 |
mutex_exit(&dict_foreign_err_mutex); |
|
2758 |
||
2759 |
return(DB_ERROR); |
|
2760 |
}
|
|
2761 |
||
2762 |
/* First check if we are actually doing an ALTER TABLE, and in that
|
|
2763 |
case look for the table being altered */
|
|
2764 |
||
2765 |
ptr = dict_accept(cs, ptr, "ALTER", &success); |
|
2766 |
||
2767 |
if (!success) { |
|
2768 |
||
2769 |
goto loop; |
|
2770 |
}
|
|
2771 |
||
2772 |
ptr = dict_accept(cs, ptr, "TABLE", &success); |
|
2773 |
||
2774 |
if (!success) { |
|
2775 |
||
2776 |
goto loop; |
|
2777 |
}
|
|
2778 |
||
2779 |
/* We are doing an ALTER TABLE: scan the table name we are altering */
|
|
2780 |
||
2781 |
ptr = dict_scan_table_name(cs, ptr, &table_to_alter, name, |
|
2782 |
&success, heap, &referenced_table_name); |
|
2783 |
if (!success) { |
|
2784 |
fprintf(stderr, |
|
2785 |
"InnoDB: Error: could not find"
|
|
2786 |
" the table being ALTERED in:\n%s\n", |
|
2787 |
sql_string); |
|
2788 |
||
2789 |
return(DB_ERROR); |
|
2790 |
}
|
|
2791 |
||
2792 |
/* Starting from 4.0.18 and 4.1.2, we generate foreign key id's in the
|
|
2793 |
format databasename/tablename_ibfk_<number>, where <number> is local
|
|
2794 |
to the table; look for the highest <number> for table_to_alter, so
|
|
2795 |
that we can assign to new constraints higher numbers. */
|
|
2796 |
||
2797 |
/* If we are altering a temporary table, the table name after ALTER
|
|
2798 |
TABLE does not correspond to the internal table name, and
|
|
2799 |
table_to_alter is NULL. TODO: should we fix this somehow? */
|
|
2800 |
||
2801 |
if (table_to_alter == NULL) { |
|
2802 |
highest_id_so_far = 0; |
|
2803 |
} else { |
|
2804 |
highest_id_so_far = dict_table_get_highest_foreign_id( |
|
2805 |
table_to_alter); |
|
2806 |
}
|
|
2807 |
||
2808 |
/* Scan for foreign key declarations in a loop */
|
|
2809 |
loop: |
|
2810 |
/* Scan either to "CONSTRAINT" or "FOREIGN", whichever is closer */
|
|
2811 |
||
2812 |
ptr1 = dict_scan_to(ptr, "CONSTRAINT"); |
|
2813 |
ptr2 = dict_scan_to(ptr, "FOREIGN"); |
|
2814 |
||
2815 |
constraint_name = NULL; |
|
2816 |
||
2817 |
if (ptr1 < ptr2) { |
|
2818 |
/* The user may have specified a constraint name. Pick it so
|
|
2819 |
that we can store 'databasename/constraintname' as the id of
|
|
2820 |
of the constraint to system tables. */
|
|
2821 |
ptr = ptr1; |
|
2822 |
||
2823 |
ptr = dict_accept(cs, ptr, "CONSTRAINT", &success); |
|
2824 |
||
2825 |
ut_a(success); |
|
2826 |
||
2827 |
if (!my_isspace(cs, *ptr) && *ptr != '"' && *ptr != '`') { |
|
2828 |
goto loop; |
|
2829 |
}
|
|
2830 |
||
2831 |
while (my_isspace(cs, *ptr)) { |
|
2832 |
ptr++; |
|
2833 |
}
|
|
2834 |
||
2835 |
/* read constraint name unless got "CONSTRAINT FOREIGN" */
|
|
2836 |
if (ptr != ptr2) { |
|
2837 |
ptr = dict_scan_id(cs, ptr, heap, |
|
2838 |
&constraint_name, FALSE, FALSE); |
|
2839 |
}
|
|
2840 |
} else { |
|
2841 |
ptr = ptr2; |
|
2842 |
}
|
|
2843 |
||
2844 |
if (*ptr == '\0') { |
|
2845 |
/* The proper way to reject foreign keys for temporary
|
|
2846 |
tables would be to split the lexing and syntactical
|
|
2847 |
analysis of foreign key clauses from the actual adding
|
|
2848 |
of them, so that ha_innodb.cc could first parse the SQL
|
|
2849 |
command, determine if there are any foreign keys, and
|
|
2850 |
if so, immediately reject the command if the table is a
|
|
2851 |
temporary one. For now, this kludge will work. */
|
|
2852 |
if (reject_fks && (UT_LIST_GET_LEN(table->foreign_list) > 0)) { |
|
2853 |
||
2854 |
return(DB_CANNOT_ADD_CONSTRAINT); |
|
2855 |
}
|
|
2856 |
||
2857 |
/**********************************************************/
|
|
2858 |
/* The following call adds the foreign key constraints
|
|
2859 |
to the data dictionary system tables on disk */
|
|
2860 |
||
2861 |
error = dict_create_add_foreigns_to_dictionary( |
|
2862 |
highest_id_so_far, table, trx); |
|
2863 |
return(error); |
|
2864 |
}
|
|
2865 |
||
2866 |
start_of_latest_foreign = ptr; |
|
2867 |
||
2868 |
ptr = dict_accept(cs, ptr, "FOREIGN", &success); |
|
2869 |
||
2870 |
if (!success) { |
|
2871 |
goto loop; |
|
2872 |
}
|
|
2873 |
||
2874 |
if (!my_isspace(cs, *ptr)) { |
|
2875 |
goto loop; |
|
2876 |
}
|
|
2877 |
||
2878 |
ptr = dict_accept(cs, ptr, "KEY", &success); |
|
2879 |
||
2880 |
if (!success) { |
|
2881 |
goto loop; |
|
2882 |
}
|
|
2883 |
||
2884 |
ptr = dict_accept(cs, ptr, "(", &success); |
|
2885 |
||
2886 |
if (!success) { |
|
2887 |
/* MySQL allows also an index id before the '('; we
|
|
2888 |
skip it */
|
|
2889 |
ptr = dict_skip_word(cs, ptr, &success); |
|
2890 |
||
2891 |
if (!success) { |
|
2892 |
dict_foreign_report_syntax_err( |
|
2893 |
name, start_of_latest_foreign, ptr); |
|
2894 |
||
2895 |
return(DB_CANNOT_ADD_CONSTRAINT); |
|
2896 |
}
|
|
2897 |
||
2898 |
ptr = dict_accept(cs, ptr, "(", &success); |
|
2899 |
||
2900 |
if (!success) { |
|
2901 |
/* We do not flag a syntax error here because in an
|
|
2902 |
ALTER TABLE we may also have DROP FOREIGN KEY abc */
|
|
2903 |
||
2904 |
goto loop; |
|
2905 |
}
|
|
2906 |
}
|
|
2907 |
||
2908 |
i = 0; |
|
2909 |
||
2910 |
/* Scan the columns in the first list */
|
|
2911 |
col_loop1: |
|
2912 |
ut_a(i < (sizeof column_names) / sizeof *column_names); |
|
2913 |
ptr = dict_scan_col(cs, ptr, &success, table, columns + i, |
|
2914 |
heap, column_names + i); |
|
2915 |
if (!success) { |
|
2916 |
mutex_enter(&dict_foreign_err_mutex); |
|
2917 |
dict_foreign_error_report_low(ef, name); |
|
2918 |
fprintf(ef, "%s:\nCannot resolve column name close to:\n%s\n", |
|
2919 |
start_of_latest_foreign, ptr); |
|
2920 |
mutex_exit(&dict_foreign_err_mutex); |
|
2921 |
||
2922 |
return(DB_CANNOT_ADD_CONSTRAINT); |
|
2923 |
}
|
|
2924 |
||
2925 |
i++; |
|
2926 |
||
2927 |
ptr = dict_accept(cs, ptr, ",", &success); |
|
2928 |
||
2929 |
if (success) { |
|
2930 |
goto col_loop1; |
|
2931 |
}
|
|
2932 |
||
2933 |
ptr = dict_accept(cs, ptr, ")", &success); |
|
2934 |
||
2935 |
if (!success) { |
|
2936 |
dict_foreign_report_syntax_err( |
|
2937 |
name, start_of_latest_foreign, ptr); |
|
2938 |
return(DB_CANNOT_ADD_CONSTRAINT); |
|
2939 |
}
|
|
2940 |
||
2941 |
/* Try to find an index which contains the columns
|
|
2942 |
as the first fields and in the right order */
|
|
2943 |
||
2944 |
index = dict_foreign_find_index(table, column_names, i, |
|
2945 |
NULL, TRUE, FALSE); |
|
2946 |
||
2947 |
if (!index) { |
|
2948 |
mutex_enter(&dict_foreign_err_mutex); |
|
2949 |
dict_foreign_error_report_low(ef, name); |
|
2950 |
fputs("There is no index in table ", ef); |
|
2951 |
ut_print_name(ef, NULL, TRUE, name); |
|
2952 |
fprintf(ef, " where the columns appear\n" |
|
2953 |
"as the first columns. Constraint:\n%s\n" |
|
2954 |
"See http://dev.mysql.com/doc/refman/5.1/en/"
|
|
2955 |
"innodb-foreign-key-constraints.html\n" |
|
2956 |
"for correct foreign key definition.\n", |
|
2957 |
start_of_latest_foreign); |
|
2958 |
mutex_exit(&dict_foreign_err_mutex); |
|
2959 |
||
2960 |
return(DB_CANNOT_ADD_CONSTRAINT); |
|
2961 |
}
|
|
2962 |
ptr = dict_accept(cs, ptr, "REFERENCES", &success); |
|
2963 |
||
2964 |
if (!success || !my_isspace(cs, *ptr)) { |
|
2965 |
dict_foreign_report_syntax_err( |
|
2966 |
name, start_of_latest_foreign, ptr); |
|
2967 |
return(DB_CANNOT_ADD_CONSTRAINT); |
|
2968 |
}
|
|
2969 |
||
2970 |
/* Let us create a constraint struct */
|
|
2971 |
||
2972 |
foreign = dict_mem_foreign_create(); |
|
2973 |
||
2974 |
if (constraint_name) { |
|
2975 |
ulint db_len; |
|
2976 |
||
2977 |
/* Catenate 'databasename/' to the constraint name specified
|
|
2978 |
by the user: we conceive the constraint as belonging to the
|
|
2979 |
same MySQL 'database' as the table itself. We store the name
|
|
2980 |
to foreign->id. */
|
|
2981 |
||
2982 |
db_len = dict_get_db_name_len(table->name); |
|
2983 |
||
2984 |
foreign->id = mem_heap_alloc( |
|
2985 |
foreign->heap, db_len + strlen(constraint_name) + 2); |
|
2986 |
||
2987 |
ut_memcpy(foreign->id, table->name, db_len); |
|
2988 |
foreign->id[db_len] = '/'; |
|
2989 |
strcpy(foreign->id + db_len + 1, constraint_name); |
|
2990 |
}
|
|
2991 |
||
2992 |
foreign->foreign_table = table; |
|
2993 |
foreign->foreign_table_name = mem_heap_strdup(foreign->heap, |
|
2994 |
table->name); |
|
2995 |
foreign->foreign_index = index; |
|
2996 |
foreign->n_fields = (unsigned int) i; |
|
2997 |
foreign->foreign_col_names = mem_heap_alloc(foreign->heap, |
|
2998 |
i * sizeof(void*)); |
|
2999 |
for (i = 0; i < foreign->n_fields; i++) { |
|
3000 |
foreign->foreign_col_names[i] = mem_heap_strdup( |
|
3001 |
foreign->heap, |
|
3002 |
dict_table_get_col_name(table, |
|
3003 |
dict_col_get_no(columns[i]))); |
|
3004 |
}
|
|
3005 |
||
3006 |
ptr = dict_scan_table_name(cs, ptr, &referenced_table, name, |
|
3007 |
&success, heap, &referenced_table_name); |
|
3008 |
||
3009 |
/* Note that referenced_table can be NULL if the user has suppressed
|
|
3010 |
checking of foreign key constraints! */
|
|
3011 |
||
3012 |
if (!success || (!referenced_table && trx->check_foreigns)) { |
|
3013 |
dict_foreign_free(foreign); |
|
3014 |
||
3015 |
mutex_enter(&dict_foreign_err_mutex); |
|
3016 |
dict_foreign_error_report_low(ef, name); |
|
3017 |
fprintf(ef, "%s:\nCannot resolve table name close to:\n" |
|
3018 |
"%s\n", |
|
3019 |
start_of_latest_foreign, ptr); |
|
3020 |
mutex_exit(&dict_foreign_err_mutex); |
|
3021 |
||
3022 |
return(DB_CANNOT_ADD_CONSTRAINT); |
|
3023 |
}
|
|
3024 |
||
3025 |
ptr = dict_accept(cs, ptr, "(", &success); |
|
3026 |
||
3027 |
if (!success) { |
|
3028 |
dict_foreign_free(foreign); |
|
3029 |
dict_foreign_report_syntax_err(name, start_of_latest_foreign, |
|
3030 |
ptr); |
|
3031 |
return(DB_CANNOT_ADD_CONSTRAINT); |
|
3032 |
}
|
|
3033 |
||
3034 |
/* Scan the columns in the second list */
|
|
3035 |
i = 0; |
|
3036 |
||
3037 |
col_loop2: |
|
3038 |
ptr = dict_scan_col(cs, ptr, &success, referenced_table, columns + i, |
|
3039 |
heap, column_names + i); |
|
3040 |
i++; |
|
3041 |
||
3042 |
if (!success) { |
|
3043 |
dict_foreign_free(foreign); |
|
3044 |
||
3045 |
mutex_enter(&dict_foreign_err_mutex); |
|
3046 |
dict_foreign_error_report_low(ef, name); |
|
3047 |
fprintf(ef, "%s:\nCannot resolve column name close to:\n" |
|
3048 |
"%s\n", |
|
3049 |
start_of_latest_foreign, ptr); |
|
3050 |
mutex_exit(&dict_foreign_err_mutex); |
|
3051 |
||
3052 |
return(DB_CANNOT_ADD_CONSTRAINT); |
|
3053 |
}
|
|
3054 |
||
3055 |
ptr = dict_accept(cs, ptr, ",", &success); |
|
3056 |
||
3057 |
if (success) { |
|
3058 |
goto col_loop2; |
|
3059 |
}
|
|
3060 |
||
3061 |
ptr = dict_accept(cs, ptr, ")", &success); |
|
3062 |
||
3063 |
if (!success || foreign->n_fields != i) { |
|
3064 |
dict_foreign_free(foreign); |
|
3065 |
||
3066 |
dict_foreign_report_syntax_err(name, start_of_latest_foreign, |
|
3067 |
ptr); |
|
3068 |
return(DB_CANNOT_ADD_CONSTRAINT); |
|
3069 |
}
|
|
3070 |
||
3071 |
n_on_deletes = 0; |
|
3072 |
n_on_updates = 0; |
|
3073 |
||
3074 |
scan_on_conditions: |
|
3075 |
/* Loop here as long as we can find ON ... conditions */
|
|
3076 |
||
3077 |
ptr = dict_accept(cs, ptr, "ON", &success); |
|
3078 |
||
3079 |
if (!success) { |
|
3080 |
||
3081 |
goto try_find_index; |
|
3082 |
}
|
|
3083 |
||
3084 |
ptr = dict_accept(cs, ptr, "DELETE", &success); |
|
3085 |
||
3086 |
if (!success) { |
|
3087 |
ptr = dict_accept(cs, ptr, "UPDATE", &success); |
|
3088 |
||
3089 |
if (!success) { |
|
3090 |
dict_foreign_free(foreign); |
|
3091 |
||
3092 |
dict_foreign_report_syntax_err( |
|
3093 |
name, start_of_latest_foreign, ptr); |
|
3094 |
return(DB_CANNOT_ADD_CONSTRAINT); |
|
3095 |
}
|
|
3096 |
||
3097 |
is_on_delete = FALSE; |
|
3098 |
n_on_updates++; |
|
3099 |
} else { |
|
3100 |
is_on_delete = TRUE; |
|
3101 |
n_on_deletes++; |
|
3102 |
}
|
|
3103 |
||
3104 |
ptr = dict_accept(cs, ptr, "RESTRICT", &success); |
|
3105 |
||
3106 |
if (success) { |
|
3107 |
goto scan_on_conditions; |
|
3108 |
}
|
|
3109 |
||
3110 |
ptr = dict_accept(cs, ptr, "CASCADE", &success); |
|
3111 |
||
3112 |
if (success) { |
|
3113 |
if (is_on_delete) { |
|
3114 |
foreign->type |= DICT_FOREIGN_ON_DELETE_CASCADE; |
|
3115 |
} else { |
|
3116 |
foreign->type |= DICT_FOREIGN_ON_UPDATE_CASCADE; |
|
3117 |
}
|
|
3118 |
||
3119 |
goto scan_on_conditions; |
|
3120 |
}
|
|
3121 |
||
3122 |
ptr = dict_accept(cs, ptr, "NO", &success); |
|
3123 |
||
3124 |
if (success) { |
|
3125 |
ptr = dict_accept(cs, ptr, "ACTION", &success); |
|
3126 |
||
3127 |
if (!success) { |
|
3128 |
dict_foreign_free(foreign); |
|
3129 |
dict_foreign_report_syntax_err( |
|
3130 |
name, start_of_latest_foreign, ptr); |
|
3131 |
||
3132 |
return(DB_CANNOT_ADD_CONSTRAINT); |
|
3133 |
}
|
|
3134 |
||
3135 |
if (is_on_delete) { |
|
3136 |
foreign->type |= DICT_FOREIGN_ON_DELETE_NO_ACTION; |
|
3137 |
} else { |
|
3138 |
foreign->type |= DICT_FOREIGN_ON_UPDATE_NO_ACTION; |
|
3139 |
}
|
|
3140 |
||
3141 |
goto scan_on_conditions; |
|
3142 |
}
|
|
3143 |
||
3144 |
ptr = dict_accept(cs, ptr, "SET", &success); |
|
3145 |
||
3146 |
if (!success) { |
|
3147 |
dict_foreign_free(foreign); |
|
3148 |
dict_foreign_report_syntax_err(name, start_of_latest_foreign, |
|
3149 |
ptr); |
|
3150 |
return(DB_CANNOT_ADD_CONSTRAINT); |
|
3151 |
}
|
|
3152 |
||
3153 |
ptr = dict_accept(cs, ptr, "NULL", &success); |
|
3154 |
||
3155 |
if (!success) { |
|
3156 |
dict_foreign_free(foreign); |
|
3157 |
dict_foreign_report_syntax_err(name, start_of_latest_foreign, |
|
3158 |
ptr); |
|
3159 |
return(DB_CANNOT_ADD_CONSTRAINT); |
|
3160 |
}
|
|
3161 |
||
3162 |
for (j = 0; j < foreign->n_fields; j++) { |
|
3163 |
if ((dict_index_get_nth_col(foreign->foreign_index, j)->prtype) |
|
3164 |
& DATA_NOT_NULL) { |
|
3165 |
||
3166 |
/* It is not sensible to define SET NULL
|
|
3167 |
if the column is not allowed to be NULL! */
|
|
3168 |
||
3169 |
dict_foreign_free(foreign); |
|
3170 |
||
3171 |
mutex_enter(&dict_foreign_err_mutex); |
|
3172 |
dict_foreign_error_report_low(ef, name); |
|
3173 |
fprintf(ef, "%s:\n" |
|
3174 |
"You have defined a SET NULL condition"
|
|
3175 |
" though some of the\n" |
|
3176 |
"columns are defined as NOT NULL.\n", |
|
3177 |
start_of_latest_foreign); |
|
3178 |
mutex_exit(&dict_foreign_err_mutex); |
|
3179 |
||
3180 |
return(DB_CANNOT_ADD_CONSTRAINT); |
|
3181 |
}
|
|
3182 |
}
|
|
3183 |
||
3184 |
if (is_on_delete) { |
|
3185 |
foreign->type |= DICT_FOREIGN_ON_DELETE_SET_NULL; |
|
3186 |
} else { |
|
3187 |
foreign->type |= DICT_FOREIGN_ON_UPDATE_SET_NULL; |
|
3188 |
}
|
|
3189 |
||
3190 |
goto scan_on_conditions; |
|
3191 |
||
3192 |
try_find_index: |
|
3193 |
if (n_on_deletes > 1 || n_on_updates > 1) { |
|
3194 |
/* It is an error to define more than 1 action */
|
|
3195 |
||
3196 |
dict_foreign_free(foreign); |
|
3197 |
||
3198 |
mutex_enter(&dict_foreign_err_mutex); |
|
3199 |
dict_foreign_error_report_low(ef, name); |
|
3200 |
fprintf(ef, "%s:\n" |
|
3201 |
"You have twice an ON DELETE clause"
|
|
3202 |
" or twice an ON UPDATE clause.\n", |
|
3203 |
start_of_latest_foreign); |
|
3204 |
mutex_exit(&dict_foreign_err_mutex); |
|
3205 |
||
3206 |
return(DB_CANNOT_ADD_CONSTRAINT); |
|
3207 |
}
|
|
3208 |
||
3209 |
/* Try to find an index which contains the columns as the first fields
|
|
3210 |
and in the right order, and the types are the same as in
|
|
3211 |
foreign->foreign_index */
|
|
3212 |
||
3213 |
if (referenced_table) { |
|
3214 |
index = dict_foreign_find_index(referenced_table, |
|
3215 |
column_names, i, |
|
3216 |
foreign->foreign_index, |
|
3217 |
TRUE, FALSE); |
|
3218 |
if (!index) { |
|
3219 |
dict_foreign_free(foreign); |
|
3220 |
mutex_enter(&dict_foreign_err_mutex); |
|
3221 |
dict_foreign_error_report_low(ef, name); |
|
3222 |
fprintf(ef, "%s:\n" |
|
3223 |
"Cannot find an index in the"
|
|
3224 |
" referenced table where the\n" |
|
3225 |
"referenced columns appear as the"
|
|
3226 |
" first columns, or column types\n" |
|
3227 |
"in the table and the referenced table"
|
|
3228 |
" do not match for constraint.\n" |
|
3229 |
"Note that the internal storage type of"
|
|
3230 |
" ENUM and SET changed in\n" |
|
3231 |
"tables created with >= InnoDB-4.1.12,"
|
|
3232 |
" and such columns in old tables\n" |
|
3233 |
"cannot be referenced by such columns"
|
|
3234 |
" in new tables.\n" |
|
3235 |
"See http://dev.mysql.com/doc/refman/5.1/en/"
|
|
3236 |
"innodb-foreign-key-constraints.html\n" |
|
3237 |
"for correct foreign key definition.\n", |
|
3238 |
start_of_latest_foreign); |
|
3239 |
mutex_exit(&dict_foreign_err_mutex); |
|
3240 |
||
3241 |
return(DB_CANNOT_ADD_CONSTRAINT); |
|
3242 |
}
|
|
3243 |
} else { |
|
3244 |
ut_a(trx->check_foreigns == FALSE); |
|
3245 |
index = NULL; |
|
3246 |
}
|
|
3247 |
||
3248 |
foreign->referenced_index = index; |
|
3249 |
foreign->referenced_table = referenced_table; |
|
3250 |
||
3251 |
foreign->referenced_table_name |
|
3252 |
= mem_heap_strdup(foreign->heap, referenced_table_name); |
|
3253 |
||
3254 |
foreign->referenced_col_names = mem_heap_alloc(foreign->heap, |
|
3255 |
i * sizeof(void*)); |
|
3256 |
for (i = 0; i < foreign->n_fields; i++) { |
|
3257 |
foreign->referenced_col_names[i] |
|
3258 |
= mem_heap_strdup(foreign->heap, column_names[i]); |
|
3259 |
}
|
|
3260 |
||
3261 |
/* We found an ok constraint definition: add to the lists */
|
|
3262 |
||
3263 |
UT_LIST_ADD_LAST(foreign_list, table->foreign_list, foreign); |
|
3264 |
||
3265 |
if (referenced_table) { |
|
3266 |
UT_LIST_ADD_LAST(referenced_list, |
|
3267 |
referenced_table->referenced_list, |
|
3268 |
foreign); |
|
3269 |
}
|
|
3270 |
||
3271 |
goto loop; |
|
3272 |
}
|
|
3273 |
||
3274 |
/**************************************************************************
|
|
3275 |
Determines whether a string starts with the specified keyword. */
|
|
3276 |
||
3277 |
ibool
|
|
3278 |
dict_str_starts_with_keyword( |
|
3279 |
/*=========================*/
|
|
3280 |
/* out: TRUE if str starts
|
|
3281 |
with keyword */
|
|
3282 |
void* mysql_thd, /* in: MySQL thread handle */ |
|
3283 |
const char* str, /* in: string to scan for keyword */ |
|
3284 |
const char* keyword) /* in: keyword to look for */ |
|
3285 |
{
|
|
3286 |
struct charset_info_st* cs = innobase_get_charset(mysql_thd); |
|
3287 |
ibool success; |
|
3288 |
||
3289 |
dict_accept(cs, str, keyword, &success); |
|
3290 |
return(success); |
|
3291 |
}
|
|
3292 |
||
3293 |
/*************************************************************************
|
|
3294 |
Scans a table create SQL string and adds to the data dictionary the foreign
|
|
3295 |
key constraints declared in the string. This function should be called after
|
|
3296 |
the indexes for a table have been created. Each foreign key constraint must
|
|
3297 |
be accompanied with indexes in both participating tables. The indexes are
|
|
3298 |
allowed to contain more fields than mentioned in the constraint. */
|
|
3299 |
||
3300 |
ulint
|
|
3301 |
dict_create_foreign_constraints( |
|
3302 |
/*============================*/
|
|
3303 |
/* out: error code or DB_SUCCESS */
|
|
3304 |
trx_t* trx, /* in: transaction */ |
|
3305 |
const char* sql_string, /* in: table create statement where |
|
3306 |
foreign keys are declared like:
|
|
3307 |
FOREIGN KEY (a, b) REFERENCES
|
|
3308 |
table2(c, d), table2 can be written
|
|
3309 |
also with the database
|
|
3310 |
name before it: test.table2; the
|
|
3311 |
default database id the database of
|
|
3312 |
parameter name */
|
|
3313 |
const char* name, /* in: table full name in the |
|
3314 |
normalized form
|
|
3315 |
database_name/table_name */
|
|
3316 |
ibool reject_fks) /* in: if TRUE, fail with error |
|
3317 |
code DB_CANNOT_ADD_CONSTRAINT if
|
|
3318 |
any foreign keys are found. */
|
|
3319 |
{
|
|
3320 |
char* str; |
|
3321 |
ulint err; |
|
3322 |
mem_heap_t* heap; |
|
3323 |
||
3324 |
ut_a(trx); |
|
3325 |
ut_a(trx->mysql_thd); |
|
3326 |
||
3327 |
str = dict_strip_comments(sql_string); |
|
3328 |
heap = mem_heap_create(10000); |
|
3329 |
||
3330 |
err = dict_create_foreign_constraints_low( |
|
3331 |
trx, heap, innobase_get_charset(trx->mysql_thd), str, name, |
|
3332 |
reject_fks); |
|
3333 |
||
3334 |
mem_heap_free(heap); |
|
3335 |
mem_free(str); |
|
3336 |
||
3337 |
return(err); |
|
3338 |
}
|
|
3339 |
||
3340 |
/**************************************************************************
|
|
3341 |
Parses the CONSTRAINT id's to be dropped in an ALTER TABLE statement. */
|
|
3342 |
||
3343 |
ulint
|
|
3344 |
dict_foreign_parse_drop_constraints( |
|
3345 |
/*================================*/
|
|
3346 |
/* out: DB_SUCCESS or
|
|
3347 |
DB_CANNOT_DROP_CONSTRAINT if
|
|
3348 |
syntax error or the constraint
|
|
3349 |
id does not match */
|
|
3350 |
mem_heap_t* heap, /* in: heap from which we can |
|
3351 |
allocate memory */
|
|
3352 |
trx_t* trx, /* in: transaction */ |
|
3353 |
dict_table_t* table, /* in: table */ |
|
3354 |
ulint* n, /* out: number of constraints |
|
3355 |
to drop */
|
|
3356 |
const char*** constraints_to_drop) /* out: id's of the |
|
3357 |
constraints to drop */
|
|
3358 |
{
|
|
3359 |
dict_foreign_t* foreign; |
|
3360 |
ibool success; |
|
3361 |
char* str; |
|
3362 |
const char* ptr; |
|
3363 |
const char* id; |
|
3364 |
FILE* ef = dict_foreign_err_file; |
|
3365 |
struct charset_info_st* cs; |
|
3366 |
||
3367 |
ut_a(trx); |
|
3368 |
ut_a(trx->mysql_thd); |
|
3369 |
||
3370 |
cs = innobase_get_charset(trx->mysql_thd); |
|
3371 |
||
3372 |
*n = 0; |
|
3373 |
||
3374 |
*constraints_to_drop = mem_heap_alloc(heap, 1000 * sizeof(char*)); |
|
3375 |
||
3376 |
str = dict_strip_comments(*(trx->mysql_query_str)); |
|
3377 |
ptr = str; |
|
3378 |
||
3379 |
ut_ad(mutex_own(&(dict_sys->mutex))); |
|
3380 |
loop: |
|
3381 |
ptr = dict_scan_to(ptr, "DROP"); |
|
3382 |
||
3383 |
if (*ptr == '\0') { |
|
3384 |
mem_free(str); |
|
3385 |
||
3386 |
return(DB_SUCCESS); |
|
3387 |
}
|
|
3388 |
||
3389 |
ptr = dict_accept(cs, ptr, "DROP", &success); |
|
3390 |
||
3391 |
if (!my_isspace(cs, *ptr)) { |
|
3392 |
||
3393 |
goto loop; |
|
3394 |
}
|
|
3395 |
||
3396 |
ptr = dict_accept(cs, ptr, "FOREIGN", &success); |
|
3397 |
||
3398 |
if (!success || !my_isspace(cs, *ptr)) { |
|
3399 |
||
3400 |
goto loop; |
|
3401 |
}
|
|
3402 |
||
3403 |
ptr = dict_accept(cs, ptr, "KEY", &success); |
|
3404 |
||
3405 |
if (!success) { |
|
3406 |
||
3407 |
goto syntax_error; |
|
3408 |
}
|
|
3409 |
||
3410 |
ptr = dict_scan_id(cs, ptr, heap, &id, FALSE, TRUE); |
|
3411 |
||
3412 |
if (id == NULL) { |
|
3413 |
||
3414 |
goto syntax_error; |
|
3415 |
}
|
|
3416 |
||
3417 |
ut_a(*n < 1000); |
|
3418 |
(*constraints_to_drop)[*n] = id; |
|
3419 |
(*n)++; |
|
3420 |
||
3421 |
/* Look for the given constraint id */
|
|
3422 |
||
3423 |
foreign = UT_LIST_GET_FIRST(table->foreign_list); |
|
3424 |
||
3425 |
while (foreign != NULL) { |
|
3426 |
if (0 == strcmp(foreign->id, id) |
|
3427 |
|| (strchr(foreign->id, '/') |
|
3428 |
&& 0 == strcmp(id, |
|
3429 |
dict_remove_db_name(foreign->id)))) { |
|
3430 |
/* Found */
|
|
3431 |
break; |
|
3432 |
}
|
|
3433 |
||
3434 |
foreign = UT_LIST_GET_NEXT(foreign_list, foreign); |
|
3435 |
}
|
|
3436 |
||
3437 |
if (foreign == NULL) { |
|
3438 |
mutex_enter(&dict_foreign_err_mutex); |
|
3439 |
rewind(ef); |
|
3440 |
ut_print_timestamp(ef); |
|
3441 |
fputs(" Error in dropping of a foreign key constraint" |
|
3442 |
" of table ", ef); |
|
3443 |
ut_print_name(ef, NULL, TRUE, table->name); |
|
3444 |
fputs(",\n" |
|
3445 |
"in SQL command\n", ef); |
|
3446 |
fputs(str, ef); |
|
3447 |
fputs("\nCannot find a constraint with the given id ", ef); |
|
3448 |
ut_print_name(ef, NULL, FALSE, id); |
|
3449 |
fputs(".\n", ef); |
|
3450 |
mutex_exit(&dict_foreign_err_mutex); |
|
3451 |
||
3452 |
mem_free(str); |
|
3453 |
||
3454 |
return(DB_CANNOT_DROP_CONSTRAINT); |
|
3455 |
}
|
|
3456 |
||
3457 |
goto loop; |
|
3458 |
||
3459 |
syntax_error: |
|
3460 |
mutex_enter(&dict_foreign_err_mutex); |
|
3461 |
rewind(ef); |
|
3462 |
ut_print_timestamp(ef); |
|
3463 |
fputs(" Syntax error in dropping of a" |
|
3464 |
" foreign key constraint of table ", ef); |
|
3465 |
ut_print_name(ef, NULL, TRUE, table->name); |
|
3466 |
fprintf(ef, ",\n" |
|
3467 |
"close to:\n%s\n in SQL command\n%s\n", ptr, str); |
|
3468 |
mutex_exit(&dict_foreign_err_mutex); |
|
3469 |
||
3470 |
mem_free(str); |
|
3471 |
||
3472 |
return(DB_CANNOT_DROP_CONSTRAINT); |
|
3473 |
}
|
|
3474 |
#endif /* UNIV_HOTBACKUP */ |
|
3475 |
||
3476 |
/*==================== END OF FOREIGN KEY PROCESSING ====================*/
|
|
3477 |
||
3478 |
#ifdef UNIV_DEBUG
|
|
3479 |
/**************************************************************************
|
|
3480 |
Returns an index object if it is found in the dictionary cache. */
|
|
3481 |
||
3482 |
dict_index_t* |
|
3483 |
dict_index_get_if_in_cache( |
|
3484 |
/*=======================*/
|
|
3485 |
/* out: index, NULL if not found */
|
|
3486 |
dulint index_id) /* in: index id */ |
|
3487 |
{
|
|
3488 |
dict_index_t* index; |
|
3489 |
||
3490 |
if (dict_sys == NULL) { |
|
3491 |
return(NULL); |
|
3492 |
}
|
|
3493 |
||
3494 |
mutex_enter(&(dict_sys->mutex)); |
|
3495 |
||
3496 |
index = dict_index_find_on_id_low(index_id); |
|
3497 |
||
3498 |
mutex_exit(&(dict_sys->mutex)); |
|
3499 |
||
3500 |
return(index); |
|
3501 |
}
|
|
3502 |
#endif /* UNIV_DEBUG */ |
|
3503 |
||
3504 |
#ifdef UNIV_DEBUG
|
|
3505 |
/**************************************************************************
|
|
3506 |
Checks that a tuple has n_fields_cmp value in a sensible range, so that
|
|
3507 |
no comparison can occur with the page number field in a node pointer. */
|
|
3508 |
||
3509 |
ibool
|
|
3510 |
dict_index_check_search_tuple( |
|
3511 |
/*==========================*/
|
|
3512 |
/* out: TRUE if ok */
|
|
3513 |
dict_index_t* index, /* in: index tree */ |
|
3514 |
dtuple_t* tuple) /* in: tuple used in a search */ |
|
3515 |
{
|
|
3516 |
ut_a(index); |
|
3517 |
ut_a(dtuple_get_n_fields_cmp(tuple) |
|
3518 |
<= dict_index_get_n_unique_in_tree(index)); |
|
3519 |
return(TRUE); |
|
3520 |
}
|
|
3521 |
#endif /* UNIV_DEBUG */ |
|
3522 |
||
3523 |
/**************************************************************************
|
|
3524 |
Builds a node pointer out of a physical record and a page number. */
|
|
3525 |
||
3526 |
dtuple_t* |
|
3527 |
dict_index_build_node_ptr( |
|
3528 |
/*======================*/
|
|
3529 |
/* out, own: node pointer */
|
|
3530 |
dict_index_t* index, /* in: index tree */ |
|
3531 |
rec_t* rec, /* in: record for which to build node |
|
3532 |
pointer */
|
|
3533 |
ulint page_no,/* in: page number to put in node pointer */ |
|
3534 |
mem_heap_t* heap, /* in: memory heap where pointer created */ |
|
3535 |
ulint level) /* in: level of rec in tree: 0 means leaf |
|
3536 |
level */
|
|
3537 |
{
|
|
3538 |
dtuple_t* tuple; |
|
3539 |
dfield_t* field; |
|
3540 |
byte* buf; |
|
3541 |
ulint n_unique; |
|
3542 |
||
3543 |
if (UNIV_UNLIKELY(index->type & DICT_UNIVERSAL)) { |
|
3544 |
/* In a universal index tree, we take the whole record as
|
|
3545 |
the node pointer if the record is on the leaf level,
|
|
3546 |
on non-leaf levels we remove the last field, which
|
|
3547 |
contains the page number of the child page */
|
|
3548 |
||
3549 |
ut_a(!dict_table_is_comp(index->table)); |
|
3550 |
n_unique = rec_get_n_fields_old(rec); |
|
3551 |
||
3552 |
if (level > 0) { |
|
3553 |
ut_a(n_unique > 1); |
|
3554 |
n_unique--; |
|
3555 |
}
|
|
3556 |
} else { |
|
3557 |
n_unique = dict_index_get_n_unique_in_tree(index); |
|
3558 |
}
|
|
3559 |
||
3560 |
tuple = dtuple_create(heap, n_unique + 1); |
|
3561 |
||
3562 |
/* When searching in the tree for the node pointer, we must not do
|
|
3563 |
comparison on the last field, the page number field, as on upper
|
|
3564 |
levels in the tree there may be identical node pointers with a
|
|
3565 |
different page number; therefore, we set the n_fields_cmp to one
|
|
3566 |
less: */
|
|
3567 |
||
3568 |
dtuple_set_n_fields_cmp(tuple, n_unique); |
|
3569 |
||
3570 |
dict_index_copy_types(tuple, index, n_unique); |
|
3571 |
||
3572 |
buf = mem_heap_alloc(heap, 4); |
|
3573 |
||
3574 |
mach_write_to_4(buf, page_no); |
|
3575 |
||
3576 |
field = dtuple_get_nth_field(tuple, n_unique); |
|
3577 |
dfield_set_data(field, buf, 4); |
|
3578 |
||
3579 |
dtype_set(dfield_get_type(field), DATA_SYS_CHILD, DATA_NOT_NULL, 4); |
|
3580 |
||
3581 |
rec_copy_prefix_to_dtuple(tuple, rec, index, n_unique, heap); |
|
3582 |
dtuple_set_info_bits(tuple, dtuple_get_info_bits(tuple) |
|
3583 |
| REC_STATUS_NODE_PTR); |
|
3584 |
||
3585 |
ut_ad(dtuple_check_typed(tuple)); |
|
3586 |
||
3587 |
return(tuple); |
|
3588 |
}
|
|
3589 |
||
3590 |
/**************************************************************************
|
|
3591 |
Copies an initial segment of a physical record, long enough to specify an
|
|
3592 |
index entry uniquely. */
|
|
3593 |
||
3594 |
rec_t* |
|
3595 |
dict_index_copy_rec_order_prefix( |
|
3596 |
/*=============================*/
|
|
3597 |
/* out: pointer to the prefix record */
|
|
3598 |
dict_index_t* index, /* in: index tree */ |
|
3599 |
rec_t* rec, /* in: record for which to copy prefix */ |
|
3600 |
ulint* n_fields,/* out: number of fields copied */ |
|
3601 |
byte** buf, /* in/out: memory buffer for the copied prefix, |
|
3602 |
or NULL */
|
|
3603 |
ulint* buf_size)/* in/out: buffer size */ |
|
3604 |
{
|
|
3605 |
ulint n; |
|
3606 |
||
3607 |
UNIV_PREFETCH_R(rec); |
|
3608 |
||
3609 |
if (UNIV_UNLIKELY(index->type & DICT_UNIVERSAL)) { |
|
3610 |
ut_a(!dict_table_is_comp(index->table)); |
|
3611 |
n = rec_get_n_fields_old(rec); |
|
3612 |
} else { |
|
3613 |
n = dict_index_get_n_unique_in_tree(index); |
|
3614 |
}
|
|
3615 |
||
3616 |
*n_fields = n; |
|
3617 |
return(rec_copy_prefix_to_buf(rec, index, n, buf, buf_size)); |
|
3618 |
}
|
|
3619 |
||
3620 |
/**************************************************************************
|
|
3621 |
Builds a typed data tuple out of a physical record. */
|
|
3622 |
||
3623 |
dtuple_t* |
|
3624 |
dict_index_build_data_tuple( |
|
3625 |
/*========================*/
|
|
3626 |
/* out, own: data tuple */
|
|
3627 |
dict_index_t* index, /* in: index tree */ |
|
3628 |
rec_t* rec, /* in: record for which to build data tuple */ |
|
3629 |
ulint n_fields,/* in: number of data fields */ |
|
3630 |
mem_heap_t* heap) /* in: memory heap where tuple created */ |
|
3631 |
{
|
|
3632 |
dtuple_t* tuple; |
|
3633 |
||
3634 |
ut_ad(dict_table_is_comp(index->table) |
|
3635 |
|| n_fields <= rec_get_n_fields_old(rec)); |
|
3636 |
||
3637 |
tuple = dtuple_create(heap, n_fields); |
|
3638 |
||
3639 |
dict_index_copy_types(tuple, index, n_fields); |
|
3640 |
||
3641 |
rec_copy_prefix_to_dtuple(tuple, rec, index, n_fields, heap); |
|
3642 |
||
3643 |
ut_ad(dtuple_check_typed(tuple)); |
|
3644 |
||
3645 |
return(tuple); |
|
3646 |
}
|
|
3647 |
||
3648 |
/*************************************************************************
|
|
3649 |
Calculates the minimum record length in an index. */
|
|
3650 |
||
3651 |
ulint
|
|
3652 |
dict_index_calc_min_rec_len( |
|
3653 |
/*========================*/
|
|
3654 |
dict_index_t* index) /* in: index */ |
|
3655 |
{
|
|
3656 |
ulint sum = 0; |
|
3657 |
ulint i; |
|
3658 |
||
3659 |
if (dict_table_is_comp(index->table)) { |
|
3660 |
ulint nullable = 0; |
|
3661 |
sum = REC_N_NEW_EXTRA_BYTES; |
|
3662 |
for (i = 0; i < dict_index_get_n_fields(index); i++) { |
|
3663 |
const dict_col_t* col |
|
3664 |
= dict_index_get_nth_col(index, i); |
|
3665 |
ulint size = dict_col_get_fixed_size(col); |
|
3666 |
sum += size; |
|
3667 |
if (!size) { |
|
3668 |
size = col->len; |
|
3669 |
sum += size < 128 ? 1 : 2; |
|
3670 |
}
|
|
3671 |
if (!(col->prtype & DATA_NOT_NULL)) { |
|
3672 |
nullable++; |
|
3673 |
}
|
|
3674 |
}
|
|
3675 |
||
3676 |
/* round the NULL flags up to full bytes */
|
|
3677 |
sum += UT_BITS_IN_BYTES(nullable); |
|
3678 |
||
3679 |
return(sum); |
|
3680 |
}
|
|
3681 |
||
3682 |
for (i = 0; i < dict_index_get_n_fields(index); i++) { |
|
3683 |
sum += dict_col_get_fixed_size( |
|
3684 |
dict_index_get_nth_col(index, i)); |
|
3685 |
}
|
|
3686 |
||
3687 |
if (sum > 127) { |
|
3688 |
sum += 2 * dict_index_get_n_fields(index); |
|
3689 |
} else { |
|
3690 |
sum += dict_index_get_n_fields(index); |
|
3691 |
}
|
|
3692 |
||
3693 |
sum += REC_N_OLD_EXTRA_BYTES; |
|
3694 |
||
3695 |
return(sum); |
|
3696 |
}
|
|
3697 |
||
3698 |
/*************************************************************************
|
|
3699 |
Calculates new estimates for table and index statistics. The statistics
|
|
3700 |
are used in query optimization. */
|
|
3701 |
||
3702 |
void
|
|
3703 |
dict_update_statistics_low( |
|
3704 |
/*=======================*/
|
|
3705 |
dict_table_t* table, /* in: table */ |
|
3706 |
ibool has_dict_mutex __attribute__((unused))) |
|
3707 |
/* in: TRUE if the caller has the
|
|
3708 |
dictionary mutex */
|
|
3709 |
{
|
|
3710 |
dict_index_t* index; |
|
3711 |
ulint size; |
|
3712 |
ulint sum_of_index_sizes = 0; |
|
3713 |
||
3714 |
if (table->ibd_file_missing) { |
|
3715 |
ut_print_timestamp(stderr); |
|
3716 |
fprintf(stderr, |
|
3717 |
" InnoDB: cannot calculate statistics for table %s\n" |
|
3718 |
"InnoDB: because the .ibd file is missing. For help,"
|
|
3719 |
" please refer to\n" |
|
3720 |
"InnoDB: http://dev.mysql.com/doc/refman/5.1/en/"
|
|
3721 |
"innodb-troubleshooting.html\n", |
|
3722 |
table->name); |
|
3723 |
||
3724 |
return; |
|
3725 |
}
|
|
3726 |
||
3727 |
/* If we have set a high innodb_force_recovery level, do not calculate
|
|
3728 |
statistics, as a badly corrupted index can cause a crash in it. */
|
|
3729 |
||
3730 |
if (srv_force_recovery >= SRV_FORCE_NO_IBUF_MERGE) { |
|
3731 |
||
3732 |
return; |
|
3733 |
}
|
|
3734 |
||
3735 |
/* Find out the sizes of the indexes and how many different values
|
|
3736 |
for the key they approximately have */
|
|
3737 |
||
3738 |
index = dict_table_get_first_index(table); |
|
3739 |
||
3740 |
if (index == NULL) { |
|
3741 |
/* Table definition is corrupt */
|
|
3742 |
||
3743 |
return; |
|
3744 |
}
|
|
3745 |
||
3746 |
while (index) { |
|
3747 |
size = btr_get_size(index, BTR_TOTAL_SIZE); |
|
3748 |
||
3749 |
index->stat_index_size = size; |
|
3750 |
||
3751 |
sum_of_index_sizes += size; |
|
3752 |
||
3753 |
size = btr_get_size(index, BTR_N_LEAF_PAGES); |
|
3754 |
||
3755 |
if (size == 0) { |
|
3756 |
/* The root node of the tree is a leaf */
|
|
3757 |
size = 1; |
|
3758 |
}
|
|
3759 |
||
3760 |
index->stat_n_leaf_pages = size; |
|
3761 |
||
3762 |
btr_estimate_number_of_different_key_vals(index); |
|
3763 |
||
3764 |
index = dict_table_get_next_index(index); |
|
3765 |
}
|
|
3766 |
||
3767 |
index = dict_table_get_first_index(table); |
|
3768 |
||
3769 |
table->stat_n_rows = index->stat_n_diff_key_vals[ |
|
3770 |
dict_index_get_n_unique(index)]; |
|
3771 |
||
3772 |
table->stat_clustered_index_size = index->stat_index_size; |
|
3773 |
||
3774 |
table->stat_sum_of_other_index_sizes = sum_of_index_sizes |
|
3775 |
- index->stat_index_size; |
|
3776 |
||
3777 |
table->stat_initialized = TRUE; |
|
3778 |
||
3779 |
table->stat_modified_counter = 0; |
|
3780 |
}
|
|
3781 |
||
3782 |
/*************************************************************************
|
|
3783 |
Calculates new estimates for table and index statistics. The statistics
|
|
3784 |
are used in query optimization. */
|
|
3785 |
||
3786 |
void
|
|
3787 |
dict_update_statistics( |
|
3788 |
/*===================*/
|
|
3789 |
dict_table_t* table) /* in: table */ |
|
3790 |
{
|
|
3791 |
dict_update_statistics_low(table, FALSE); |
|
3792 |
}
|
|
3793 |
||
3794 |
/**************************************************************************
|
|
3795 |
A noninlined version of dict_table_get_low. */
|
|
3796 |
||
3797 |
dict_table_t* |
|
3798 |
dict_table_get_low_noninlined( |
|
3799 |
/*==========================*/
|
|
3800 |
/* out: table, NULL if not found */
|
|
3801 |
const char* table_name) /* in: table name */ |
|
3802 |
{
|
|
3803 |
return(dict_table_get_low(table_name)); |
|
3804 |
}
|
|
3805 |
||
3806 |
/**************************************************************************
|
|
3807 |
Prints info of a foreign key constraint. */
|
|
3808 |
static
|
|
3809 |
void
|
|
3810 |
dict_foreign_print_low( |
|
3811 |
/*===================*/
|
|
3812 |
dict_foreign_t* foreign) /* in: foreign key constraint */ |
|
3813 |
{
|
|
3814 |
ulint i; |
|
3815 |
||
3816 |
ut_ad(mutex_own(&(dict_sys->mutex))); |
|
3817 |
||
3818 |
fprintf(stderr, " FOREIGN KEY CONSTRAINT %s: %s (", |
|
3819 |
foreign->id, foreign->foreign_table_name); |
|
3820 |
||
3821 |
for (i = 0; i < foreign->n_fields; i++) { |
|
3822 |
fprintf(stderr, " %s", foreign->foreign_col_names[i]); |
|
3823 |
}
|
|
3824 |
||
3825 |
fprintf(stderr, " )\n" |
|
3826 |
" REFERENCES %s (", |
|
3827 |
foreign->referenced_table_name); |
|
3828 |
||
3829 |
for (i = 0; i < foreign->n_fields; i++) { |
|
3830 |
fprintf(stderr, " %s", foreign->referenced_col_names[i]); |
|
3831 |
}
|
|
3832 |
||
3833 |
fputs(" )\n", stderr); |
|
3834 |
}
|
|
3835 |
||
3836 |
/**************************************************************************
|
|
3837 |
Prints a table data. */
|
|
3838 |
||
3839 |
void
|
|
3840 |
dict_table_print( |
|
3841 |
/*=============*/
|
|
3842 |
dict_table_t* table) /* in: table */ |
|
3843 |
{
|
|
3844 |
mutex_enter(&(dict_sys->mutex)); |
|
3845 |
dict_table_print_low(table); |
|
3846 |
mutex_exit(&(dict_sys->mutex)); |
|
3847 |
}
|
|
3848 |
||
3849 |
/**************************************************************************
|
|
3850 |
Prints a table data when we know the table name. */
|
|
3851 |
||
3852 |
void
|
|
3853 |
dict_table_print_by_name( |
|
3854 |
/*=====================*/
|
|
3855 |
const char* name) |
|
3856 |
{
|
|
3857 |
dict_table_t* table; |
|
3858 |
||
3859 |
mutex_enter(&(dict_sys->mutex)); |
|
3860 |
||
3861 |
table = dict_table_get_low(name); |
|
3862 |
||
3863 |
ut_a(table); |
|
3864 |
||
3865 |
dict_table_print_low(table); |
|
3866 |
mutex_exit(&(dict_sys->mutex)); |
|
3867 |
}
|
|
3868 |
||
3869 |
/**************************************************************************
|
|
3870 |
Prints a table data. */
|
|
3871 |
||
3872 |
void
|
|
3873 |
dict_table_print_low( |
|
3874 |
/*=================*/
|
|
3875 |
dict_table_t* table) /* in: table */ |
|
3876 |
{
|
|
3877 |
dict_index_t* index; |
|
3878 |
dict_foreign_t* foreign; |
|
3879 |
ulint i; |
|
3880 |
||
3881 |
ut_ad(mutex_own(&(dict_sys->mutex))); |
|
3882 |
||
3883 |
dict_update_statistics_low(table, TRUE); |
|
3884 |
||
3885 |
fprintf(stderr, |
|
3886 |
"--------------------------------------\n" |
|
3887 |
"TABLE: name %s, id %lu %lu, columns %lu, indexes %lu,"
|
|
3888 |
" appr.rows %lu\n" |
|
3889 |
" COLUMNS: ", |
|
3890 |
table->name, |
|
3891 |
(ulong) ut_dulint_get_high(table->id), |
|
3892 |
(ulong) ut_dulint_get_low(table->id), |
|
3893 |
(ulong) table->n_cols, |
|
3894 |
(ulong) UT_LIST_GET_LEN(table->indexes), |
|
3895 |
(ulong) table->stat_n_rows); |
|
3896 |
||
3897 |
for (i = 0; i + 1 < (ulint) table->n_cols; i++) { |
|
3898 |
dict_col_print_low(table, dict_table_get_nth_col(table, i)); |
|
3899 |
fputs("; ", stderr); |
|
3900 |
}
|
|
3901 |
||
3902 |
putc('\n', stderr); |
|
3903 |
||
3904 |
index = UT_LIST_GET_FIRST(table->indexes); |
|
3905 |
||
3906 |
while (index != NULL) { |
|
3907 |
dict_index_print_low(index); |
|
3908 |
index = UT_LIST_GET_NEXT(indexes, index); |
|
3909 |
}
|
|
3910 |
||
3911 |
foreign = UT_LIST_GET_FIRST(table->foreign_list); |
|
3912 |
||
3913 |
while (foreign != NULL) { |
|
3914 |
dict_foreign_print_low(foreign); |
|
3915 |
foreign = UT_LIST_GET_NEXT(foreign_list, foreign); |
|
3916 |
}
|
|
3917 |
||
3918 |
foreign = UT_LIST_GET_FIRST(table->referenced_list); |
|
3919 |
||
3920 |
while (foreign != NULL) { |
|
3921 |
dict_foreign_print_low(foreign); |
|
3922 |
foreign = UT_LIST_GET_NEXT(referenced_list, foreign); |
|
3923 |
}
|
|
3924 |
}
|
|
3925 |
||
3926 |
/**************************************************************************
|
|
3927 |
Prints a column data. */
|
|
3928 |
static
|
|
3929 |
void
|
|
3930 |
dict_col_print_low( |
|
3931 |
/*===============*/
|
|
3932 |
const dict_table_t* table, /* in: table */ |
|
3933 |
const dict_col_t* col) /* in: column */ |
|
3934 |
{
|
|
3935 |
dtype_t type; |
|
3936 |
||
3937 |
ut_ad(mutex_own(&(dict_sys->mutex))); |
|
3938 |
||
3939 |
dict_col_copy_type(col, &type); |
|
3940 |
fprintf(stderr, "%s: ", dict_table_get_col_name(table, |
|
3941 |
dict_col_get_no(col))); |
|
3942 |
||
3943 |
dtype_print(&type); |
|
3944 |
}
|
|
3945 |
||
3946 |
/**************************************************************************
|
|
3947 |
Prints an index data. */
|
|
3948 |
static
|
|
3949 |
void
|
|
3950 |
dict_index_print_low( |
|
3951 |
/*=================*/
|
|
3952 |
dict_index_t* index) /* in: index */ |
|
3953 |
{
|
|
3954 |
ib_longlong n_vals; |
|
3955 |
ulint i; |
|
3956 |
||
3957 |
ut_ad(mutex_own(&(dict_sys->mutex))); |
|
3958 |
||
3959 |
if (index->n_user_defined_cols > 0) { |
|
3960 |
n_vals = index->stat_n_diff_key_vals[ |
|
3961 |
index->n_user_defined_cols]; |
|
3962 |
} else { |
|
3963 |
n_vals = index->stat_n_diff_key_vals[1]; |
|
3964 |
}
|
|
3965 |
||
3966 |
fprintf(stderr, |
|
3967 |
" INDEX: name %s, id %lu %lu, fields %lu/%lu,"
|
|
3968 |
" uniq %lu, type %lu\n" |
|
3969 |
" root page %lu, appr.key vals %lu,"
|
|
3970 |
" leaf pages %lu, size pages %lu\n" |
|
3971 |
" FIELDS: ", |
|
3972 |
index->name, |
|
3973 |
(ulong) ut_dulint_get_high(index->id), |
|
3974 |
(ulong) ut_dulint_get_low(index->id), |
|
3975 |
(ulong) index->n_user_defined_cols, |
|
3976 |
(ulong) index->n_fields, |
|
3977 |
(ulong) index->n_uniq, |
|
3978 |
(ulong) index->type, |
|
3979 |
(ulong) index->page, |
|
3980 |
(ulong) n_vals, |
|
3981 |
(ulong) index->stat_n_leaf_pages, |
|
3982 |
(ulong) index->stat_index_size); |
|
3983 |
||
3984 |
for (i = 0; i < index->n_fields; i++) { |
|
3985 |
dict_field_print_low(dict_index_get_nth_field(index, i)); |
|
3986 |
}
|
|
3987 |
||
3988 |
putc('\n', stderr); |
|
3989 |
||
3990 |
#ifdef UNIV_BTR_PRINT
|
|
3991 |
btr_print_size(index); |
|
3992 |
||
3993 |
btr_print_index(index, 7); |
|
3994 |
#endif /* UNIV_BTR_PRINT */ |
|
3995 |
}
|
|
3996 |
||
3997 |
/**************************************************************************
|
|
3998 |
Prints a field data. */
|
|
3999 |
static
|
|
4000 |
void
|
|
4001 |
dict_field_print_low( |
|
4002 |
/*=================*/
|
|
4003 |
dict_field_t* field) /* in: field */ |
|
4004 |
{
|
|
4005 |
ut_ad(mutex_own(&(dict_sys->mutex))); |
|
4006 |
||
4007 |
fprintf(stderr, " %s", field->name); |
|
4008 |
||
4009 |
if (field->prefix_len != 0) { |
|
4010 |
fprintf(stderr, "(%lu)", (ulong) field->prefix_len); |
|
4011 |
}
|
|
4012 |
}
|
|
4013 |
||
4014 |
/**************************************************************************
|
|
4015 |
Outputs info on a foreign key of a table in a format suitable for
|
|
4016 |
CREATE TABLE. */
|
|
4017 |
||
4018 |
void
|
|
4019 |
dict_print_info_on_foreign_key_in_create_format( |
|
4020 |
/*============================================*/
|
|
4021 |
FILE* file, /* in: file where to print */ |
|
4022 |
trx_t* trx, /* in: transaction */ |
|
4023 |
dict_foreign_t* foreign, /* in: foreign key constraint */ |
|
4024 |
ibool add_newline) /* in: whether to add a newline */ |
|
4025 |
{
|
|
4026 |
const char* stripped_id; |
|
4027 |
ulint i; |
|
4028 |
||
4029 |
if (strchr(foreign->id, '/')) { |
|
4030 |
/* Strip the preceding database name from the constraint id */
|
|
4031 |
stripped_id = foreign->id + 1 |
|
4032 |
+ dict_get_db_name_len(foreign->id); |
|
4033 |
} else { |
|
4034 |
stripped_id = foreign->id; |
|
4035 |
}
|
|
4036 |
||
4037 |
putc(',', file); |
|
4038 |
||
4039 |
if (add_newline) { |
|
4040 |
/* SHOW CREATE TABLE wants constraints each printed nicely
|
|
4041 |
on its own line, while error messages want no newlines
|
|
4042 |
inserted. */
|
|
4043 |
fputs("\n ", file); |
|
4044 |
}
|
|
4045 |
||
4046 |
fputs(" CONSTRAINT ", file); |
|
4047 |
ut_print_name(file, trx, FALSE, stripped_id); |
|
4048 |
fputs(" FOREIGN KEY (", file); |
|
4049 |
||
4050 |
for (i = 0;;) { |
|
4051 |
ut_print_name(file, trx, FALSE, foreign->foreign_col_names[i]); |
|
4052 |
if (++i < foreign->n_fields) { |
|
4053 |
fputs(", ", file); |
|
4054 |
} else { |
|
4055 |
break; |
|
4056 |
}
|
|
4057 |
}
|
|
4058 |
||
4059 |
fputs(") REFERENCES ", file); |
|
4060 |
||
4061 |
if (dict_tables_have_same_db(foreign->foreign_table_name, |
|
4062 |
foreign->referenced_table_name)) { |
|
4063 |
/* Do not print the database name of the referenced table */
|
|
4064 |
ut_print_name(file, trx, TRUE, |
|
4065 |
dict_remove_db_name( |
|
4066 |
foreign->referenced_table_name)); |
|
4067 |
} else { |
|
4068 |
ut_print_name(file, trx, TRUE, |
|
4069 |
foreign->referenced_table_name); |
|
4070 |
}
|
|
4071 |
||
4072 |
putc(' ', file); |
|
4073 |
putc('(', file); |
|
4074 |
||
4075 |
for (i = 0;;) { |
|
4076 |
ut_print_name(file, trx, FALSE, |
|
4077 |
foreign->referenced_col_names[i]); |
|
4078 |
if (++i < foreign->n_fields) { |
|
4079 |
fputs(", ", file); |
|
4080 |
} else { |
|
4081 |
break; |
|
4082 |
}
|
|
4083 |
}
|
|
4084 |
||
4085 |
putc(')', file); |
|
4086 |
||
4087 |
if (foreign->type & DICT_FOREIGN_ON_DELETE_CASCADE) { |
|
4088 |
fputs(" ON DELETE CASCADE", file); |
|
4089 |
}
|
|
4090 |
||
4091 |
if (foreign->type & DICT_FOREIGN_ON_DELETE_SET_NULL) { |
|
4092 |
fputs(" ON DELETE SET NULL", file); |
|
4093 |
}
|
|
4094 |
||
4095 |
if (foreign->type & DICT_FOREIGN_ON_DELETE_NO_ACTION) { |
|
4096 |
fputs(" ON DELETE NO ACTION", file); |
|
4097 |
}
|
|
4098 |
||
4099 |
if (foreign->type & DICT_FOREIGN_ON_UPDATE_CASCADE) { |
|
4100 |
fputs(" ON UPDATE CASCADE", file); |
|
4101 |
}
|
|
4102 |
||
4103 |
if (foreign->type & DICT_FOREIGN_ON_UPDATE_SET_NULL) { |
|
4104 |
fputs(" ON UPDATE SET NULL", file); |
|
4105 |
}
|
|
4106 |
||
4107 |
if (foreign->type & DICT_FOREIGN_ON_UPDATE_NO_ACTION) { |
|
4108 |
fputs(" ON UPDATE NO ACTION", file); |
|
4109 |
}
|
|
4110 |
}
|
|
4111 |
||
4112 |
/**************************************************************************
|
|
4113 |
Outputs info on foreign keys of a table. */
|
|
4114 |
||
4115 |
void
|
|
4116 |
dict_print_info_on_foreign_keys( |
|
4117 |
/*============================*/
|
|
4118 |
ibool create_table_format, /* in: if TRUE then print in |
|
4119 |
a format suitable to be inserted into
|
|
4120 |
a CREATE TABLE, otherwise in the format
|
|
4121 |
of SHOW TABLE STATUS */
|
|
4122 |
FILE* file, /* in: file where to print */ |
|
4123 |
trx_t* trx, /* in: transaction */ |
|
4124 |
dict_table_t* table) /* in: table */ |
|
4125 |
{
|
|
4126 |
dict_foreign_t* foreign; |
|
4127 |
||
4128 |
mutex_enter(&(dict_sys->mutex)); |
|
4129 |
||
4130 |
foreign = UT_LIST_GET_FIRST(table->foreign_list); |
|
4131 |
||
4132 |
if (foreign == NULL) { |
|
4133 |
mutex_exit(&(dict_sys->mutex)); |
|
4134 |
||
4135 |
return; |
|
4136 |
}
|
|
4137 |
||
4138 |
while (foreign != NULL) { |
|
4139 |
if (create_table_format) { |
|
4140 |
dict_print_info_on_foreign_key_in_create_format( |
|
4141 |
file, trx, foreign, TRUE); |
|
4142 |
} else { |
|
4143 |
ulint i; |
|
4144 |
fputs("; (", file); |
|
4145 |
||
4146 |
for (i = 0; i < foreign->n_fields; i++) { |
|
4147 |
if (i) { |
|
4148 |
putc(' ', file); |
|
4149 |
}
|
|
4150 |
||
4151 |
ut_print_name(file, trx, FALSE, |
|
4152 |
foreign->foreign_col_names[i]); |
|
4153 |
}
|
|
4154 |
||
4155 |
fputs(") REFER ", file); |
|
4156 |
ut_print_name(file, trx, TRUE, |
|
4157 |
foreign->referenced_table_name); |
|
4158 |
putc('(', file); |
|
4159 |
||
4160 |
for (i = 0; i < foreign->n_fields; i++) { |
|
4161 |
if (i) { |
|
4162 |
putc(' ', file); |
|
4163 |
}
|
|
4164 |
ut_print_name( |
|
4165 |
file, trx, FALSE, |
|
4166 |
foreign->referenced_col_names[i]); |
|
4167 |
}
|
|
4168 |
||
4169 |
putc(')', file); |
|
4170 |
||
4171 |
if (foreign->type == DICT_FOREIGN_ON_DELETE_CASCADE) { |
|
4172 |
fputs(" ON DELETE CASCADE", file); |
|
4173 |
}
|
|
4174 |
||
4175 |
if (foreign->type == DICT_FOREIGN_ON_DELETE_SET_NULL) { |
|
4176 |
fputs(" ON DELETE SET NULL", file); |
|
4177 |
}
|
|
4178 |
||
4179 |
if (foreign->type & DICT_FOREIGN_ON_DELETE_NO_ACTION) { |
|
4180 |
fputs(" ON DELETE NO ACTION", file); |
|
4181 |
}
|
|
4182 |
||
4183 |
if (foreign->type & DICT_FOREIGN_ON_UPDATE_CASCADE) { |
|
4184 |
fputs(" ON UPDATE CASCADE", file); |
|
4185 |
}
|
|
4186 |
||
4187 |
if (foreign->type & DICT_FOREIGN_ON_UPDATE_SET_NULL) { |
|
4188 |
fputs(" ON UPDATE SET NULL", file); |
|
4189 |
}
|
|
4190 |
||
4191 |
if (foreign->type & DICT_FOREIGN_ON_UPDATE_NO_ACTION) { |
|
4192 |
fputs(" ON UPDATE NO ACTION", file); |
|
4193 |
}
|
|
4194 |
}
|
|
4195 |
||
4196 |
foreign = UT_LIST_GET_NEXT(foreign_list, foreign); |
|
4197 |
}
|
|
4198 |
||
4199 |
mutex_exit(&(dict_sys->mutex)); |
|
4200 |
}
|
|
4201 |
||
4202 |
/************************************************************************
|
|
4203 |
Displays the names of the index and the table. */
|
|
4204 |
void
|
|
4205 |
dict_index_name_print( |
|
4206 |
/*==================*/
|
|
4207 |
FILE* file, /* in: output stream */ |
|
4208 |
trx_t* trx, /* in: transaction */ |
|
4209 |
const dict_index_t* index) /* in: index to print */ |
|
4210 |
{
|
|
4211 |
fputs("index ", file); |
|
4212 |
ut_print_name(file, trx, FALSE, index->name); |
|
4213 |
fputs(" of table ", file); |
|
4214 |
ut_print_name(file, trx, TRUE, index->table_name); |
|
4215 |
}
|