1 | /* Copyright (c) 2000, 2015, Oracle and/or its affiliates. |
2 | Copyright (c) 2009, 2017, MariaDB |
3 | |
4 | This program is free software; you can redistribute it and/or modify |
5 | it under the terms of the GNU General Public License as published by |
6 | the Free Software Foundation; version 2 of the License. |
7 | |
8 | This program is distributed in the hope that it will be useful, |
9 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
11 | GNU General Public License for more details. |
12 | |
13 | You should have received a copy of the GNU General Public License |
14 | along with this program; if not, write to the Free Software |
15 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ |
16 | |
17 | |
18 | /* Function with list databases, tables or fields */ |
19 | |
20 | #include "sql_plugin.h" // SHOW_MY_BOOL |
21 | #include "sql_priv.h" |
22 | #include "unireg.h" |
23 | #include "sql_acl.h" // fill_schema_*_privileges |
24 | #include "sql_select.h" // For select_describe |
25 | #include "sql_base.h" // close_tables_for_reopen |
26 | #include "create_options.h" |
27 | #include "sql_show.h" |
28 | #include "sql_table.h" // filename_to_tablename, |
29 | // primary_key_name, |
30 | // build_table_filename |
31 | #include "sql_view.h" |
32 | #include "repl_failsafe.h" |
33 | #include "sql_parse.h" // check_access, check_table_access |
34 | #include "sql_partition.h" // partition_element |
35 | #include "sql_derived.h" // mysql_derived_prepare, |
36 | // mysql_handle_derived, |
37 | #include "sql_db.h" // check_db_dir_existence, load_db_opt_by_name |
38 | #include "sql_time.h" // interval_type_to_name |
39 | #include "tztime.h" // struct Time_zone |
40 | #include "sql_acl.h" // TABLE_ACLS, check_grant, DB_ACLS, acl_get, |
41 | // check_grant_db |
42 | #include "sp.h" |
43 | #include "sp_head.h" |
44 | #include "sp_pcontext.h" |
45 | #include "set_var.h" |
46 | #include "sql_trigger.h" |
47 | #include "sql_derived.h" |
48 | #include "sql_statistics.h" |
49 | #include "sql_connect.h" |
50 | #include "authors.h" |
51 | #include "contributors.h" |
52 | #include "sql_partition.h" |
53 | #ifdef HAVE_EVENT_SCHEDULER |
54 | #include "events.h" |
55 | #include "event_data_objects.h" |
56 | #endif |
57 | #include <my_dir.h> |
58 | #include "lock.h" // MYSQL_OPEN_IGNORE_FLUSH |
59 | #include "debug_sync.h" |
60 | #include "keycaches.h" |
61 | #include "ha_sequence.h" |
62 | #ifdef WITH_PARTITION_STORAGE_ENGINE |
63 | #include "ha_partition.h" |
64 | #endif |
65 | #include "transaction.h" |
66 | |
67 | enum enum_i_s_events_fields |
68 | { |
69 | ISE_EVENT_CATALOG= 0, |
70 | ISE_EVENT_SCHEMA, |
71 | ISE_EVENT_NAME, |
72 | ISE_DEFINER, |
73 | ISE_TIME_ZONE, |
74 | ISE_EVENT_BODY, |
75 | ISE_EVENT_DEFINITION, |
76 | ISE_EVENT_TYPE, |
77 | ISE_EXECUTE_AT, |
78 | ISE_INTERVAL_VALUE, |
79 | ISE_INTERVAL_FIELD, |
80 | ISE_SQL_MODE, |
81 | ISE_STARTS, |
82 | ISE_ENDS, |
83 | ISE_STATUS, |
84 | ISE_ON_COMPLETION, |
85 | ISE_CREATED, |
86 | ISE_LAST_ALTERED, |
87 | ISE_LAST_EXECUTED, |
88 | , |
89 | ISE_ORIGINATOR, |
90 | ISE_CLIENT_CS, |
91 | ISE_CONNECTION_CL, |
92 | ISE_DB_CL |
93 | }; |
94 | |
95 | #define USERNAME_WITH_HOST_CHAR_LENGTH (USERNAME_CHAR_LENGTH + HOSTNAME_LENGTH + 2) |
96 | |
97 | static const LEX_CSTRING trg_action_time_type_names[]= |
98 | { |
99 | { STRING_WITH_LEN("BEFORE" ) }, |
100 | { STRING_WITH_LEN("AFTER" ) } |
101 | }; |
102 | |
103 | static const LEX_CSTRING trg_event_type_names[]= |
104 | { |
105 | { STRING_WITH_LEN("INSERT" ) }, |
106 | { STRING_WITH_LEN("UPDATE" ) }, |
107 | { STRING_WITH_LEN("DELETE" ) } |
108 | }; |
109 | |
110 | #ifndef NO_EMBEDDED_ACCESS_CHECKS |
111 | static const char *grant_names[]={ |
112 | "select" ,"insert" ,"update" ,"delete" ,"create" ,"drop" ,"reload" ,"shutdown" , |
113 | "process" ,"file" ,"grant" ,"references" ,"index" ,"alter" }; |
114 | |
115 | static TYPELIB grant_types = { sizeof(grant_names)/sizeof(char **), |
116 | "grant_types" , |
117 | grant_names, NULL}; |
118 | #endif |
119 | |
120 | /* Match the values of enum ha_choice */ |
121 | static const char *ha_choice_values[] = {"" , "0" , "1" }; |
122 | |
123 | static void store_key_options(THD *, String *, TABLE *, KEY *); |
124 | |
125 | #ifdef WITH_PARTITION_STORAGE_ENGINE |
126 | static void get_cs_converted_string_value(THD *thd, |
127 | String *input_str, |
128 | String *output_str, |
129 | CHARSET_INFO *cs, |
130 | bool use_hex); |
131 | #endif |
132 | |
133 | static int show_create_view(THD *thd, TABLE_LIST *table, String *buff); |
134 | static int show_create_sequence(THD *thd, TABLE_LIST *table_list, |
135 | String *packet); |
136 | |
137 | static const LEX_CSTRING *view_algorithm(TABLE_LIST *table); |
138 | |
139 | bool get_lookup_field_values(THD *, COND *, TABLE_LIST *, LOOKUP_FIELD_VALUES *); |
140 | |
141 | /** |
142 | Try to lock a mutex, but give up after a short while to not cause deadlocks |
143 | |
144 | The loop is short, as the mutex we are trying to lock are mutex the should |
145 | never be locked a long time, just over a few instructions. |
146 | |
147 | @return 0 ok |
148 | @return 1 error |
149 | */ |
150 | |
151 | static bool trylock_short(mysql_mutex_t *mutex) |
152 | { |
153 | uint i; |
154 | for (i= 0 ; i < 100 ; i++) |
155 | { |
156 | if (!mysql_mutex_trylock(mutex)) |
157 | return 0; |
158 | LF_BACKOFF(); |
159 | } |
160 | return 1; |
161 | } |
162 | |
163 | |
164 | /*************************************************************************** |
165 | ** List all table types supported |
166 | ***************************************************************************/ |
167 | |
168 | |
169 | static bool is_show_command(THD *thd) |
170 | { |
171 | return sql_command_flags[thd->lex->sql_command] & CF_STATUS_COMMAND; |
172 | } |
173 | |
174 | static int make_version_string(char *buf, int buf_length, uint version) |
175 | { |
176 | return (int)my_snprintf(buf, buf_length, "%d.%d" , version>>8,version&0xff); |
177 | } |
178 | |
179 | |
180 | static const LEX_CSTRING maturity_name[]={ |
181 | { STRING_WITH_LEN("Unknown" ) }, |
182 | { STRING_WITH_LEN("Experimental" ) }, |
183 | { STRING_WITH_LEN("Alpha" ) }, |
184 | { STRING_WITH_LEN("Beta" ) }, |
185 | { STRING_WITH_LEN("Gamma" ) }, |
186 | { STRING_WITH_LEN("Stable" ) }}; |
187 | |
188 | |
189 | static my_bool show_plugins(THD *thd, plugin_ref plugin, |
190 | void *arg) |
191 | { |
192 | TABLE *table= (TABLE*) arg; |
193 | struct st_maria_plugin *plug= plugin_decl(plugin); |
194 | struct st_plugin_dl *plugin_dl= plugin_dlib(plugin); |
195 | CHARSET_INFO *cs= system_charset_info; |
196 | char version_buf[20]; |
197 | |
198 | restore_record(table, s->default_values); |
199 | |
200 | table->field[0]->store(plugin_name(plugin)->str, |
201 | plugin_name(plugin)->length, cs); |
202 | |
203 | table->field[1]->store(version_buf, |
204 | make_version_string(version_buf, sizeof(version_buf), plug->version), |
205 | cs); |
206 | |
207 | switch (plugin_state(plugin)) { |
208 | case PLUGIN_IS_DELETED: |
209 | table->field[2]->store(STRING_WITH_LEN("DELETED" ), cs); |
210 | break; |
211 | case PLUGIN_IS_UNINITIALIZED: |
212 | table->field[2]->store(STRING_WITH_LEN("INACTIVE" ), cs); |
213 | break; |
214 | case PLUGIN_IS_READY: |
215 | table->field[2]->store(STRING_WITH_LEN("ACTIVE" ), cs); |
216 | break; |
217 | case PLUGIN_IS_DISABLED: |
218 | table->field[2]->store(STRING_WITH_LEN("DISABLED" ), cs); |
219 | break; |
220 | case PLUGIN_IS_FREED: // filtered in fill_plugins, used in fill_all_plugins |
221 | table->field[2]->store(STRING_WITH_LEN("NOT INSTALLED" ), cs); |
222 | break; |
223 | default: |
224 | DBUG_ASSERT(0); |
225 | } |
226 | |
227 | table->field[3]->store(plugin_type_names[plug->type].str, |
228 | plugin_type_names[plug->type].length, |
229 | cs); |
230 | table->field[4]->store(version_buf, |
231 | make_version_string(version_buf, sizeof(version_buf), |
232 | *(uint *)plug->info), cs); |
233 | |
234 | if (plugin_dl) |
235 | { |
236 | table->field[5]->store(plugin_dl->dl.str, plugin_dl->dl.length, cs); |
237 | table->field[5]->set_notnull(); |
238 | table->field[6]->store(version_buf, |
239 | make_version_string(version_buf, sizeof(version_buf), |
240 | plugin_dl->mariaversion), |
241 | cs); |
242 | table->field[6]->set_notnull(); |
243 | } |
244 | else |
245 | { |
246 | table->field[5]->set_null(); |
247 | table->field[6]->set_null(); |
248 | } |
249 | |
250 | |
251 | if (plug->author) |
252 | { |
253 | table->field[7]->store(plug->author, strlen(plug->author), cs); |
254 | table->field[7]->set_notnull(); |
255 | } |
256 | else |
257 | table->field[7]->set_null(); |
258 | |
259 | if (plug->descr) |
260 | { |
261 | table->field[8]->store(plug->descr, strlen(plug->descr), cs); |
262 | table->field[8]->set_notnull(); |
263 | } |
264 | else |
265 | table->field[8]->set_null(); |
266 | |
267 | switch (plug->license) { |
268 | case PLUGIN_LICENSE_GPL: |
269 | table->field[9]->store(PLUGIN_LICENSE_GPL_STRING, |
270 | strlen(PLUGIN_LICENSE_GPL_STRING), cs); |
271 | break; |
272 | case PLUGIN_LICENSE_BSD: |
273 | table->field[9]->store(PLUGIN_LICENSE_BSD_STRING, |
274 | strlen(PLUGIN_LICENSE_BSD_STRING), cs); |
275 | break; |
276 | default: |
277 | table->field[9]->store(PLUGIN_LICENSE_PROPRIETARY_STRING, |
278 | strlen(PLUGIN_LICENSE_PROPRIETARY_STRING), cs); |
279 | break; |
280 | } |
281 | |
282 | table->field[10]->store( |
283 | global_plugin_typelib_names[plugin_load_option(plugin)], |
284 | strlen(global_plugin_typelib_names[plugin_load_option(plugin)]), |
285 | cs); |
286 | |
287 | if (plug->maturity <= MariaDB_PLUGIN_MATURITY_STABLE) |
288 | table->field[11]->store(maturity_name[plug->maturity].str, |
289 | maturity_name[plug->maturity].length, |
290 | cs); |
291 | else |
292 | table->field[11]->store("Unknown" , 7, cs); |
293 | |
294 | if (plug->version_info) |
295 | { |
296 | table->field[12]->store(plug->version_info, |
297 | strlen(plug->version_info), cs); |
298 | table->field[12]->set_notnull(); |
299 | } |
300 | else |
301 | table->field[12]->set_null(); |
302 | |
303 | return schema_table_store_record(thd, table); |
304 | } |
305 | |
306 | |
307 | int fill_plugins(THD *thd, TABLE_LIST *tables, COND *cond) |
308 | { |
309 | DBUG_ENTER("fill_plugins" ); |
310 | TABLE *table= tables->table; |
311 | |
312 | if (plugin_foreach_with_mask(thd, show_plugins, MYSQL_ANY_PLUGIN, |
313 | ~(PLUGIN_IS_FREED | PLUGIN_IS_DYING), table)) |
314 | DBUG_RETURN(1); |
315 | |
316 | DBUG_RETURN(0); |
317 | } |
318 | |
319 | |
320 | int fill_all_plugins(THD *thd, TABLE_LIST *tables, COND *cond) |
321 | { |
322 | DBUG_ENTER("fill_all_plugins" ); |
323 | TABLE *table= tables->table; |
324 | LOOKUP_FIELD_VALUES lookup; |
325 | |
326 | if (get_lookup_field_values(thd, cond, tables, &lookup)) |
327 | DBUG_RETURN(0); |
328 | |
329 | if (lookup.db_value.str && !lookup.db_value.str[0]) |
330 | DBUG_RETURN(0); // empty string never matches a valid SONAME |
331 | |
332 | MY_DIR *dirp= my_dir(opt_plugin_dir, MY_THREAD_SPECIFIC); |
333 | if (!dirp) |
334 | { |
335 | my_error(ER_CANT_READ_DIR, MYF(0), opt_plugin_dir, my_errno); |
336 | DBUG_RETURN(1); |
337 | } |
338 | |
339 | if (!lookup.db_value.str) |
340 | plugin_dl_foreach(thd, 0, show_plugins, table); |
341 | |
342 | const char *wstr= lookup.db_value.str, *wend= wstr + lookup.db_value.length; |
343 | for (uint i=0; i < (uint) dirp->number_of_files; i++) |
344 | { |
345 | FILEINFO *file= dirp->dir_entry+i; |
346 | LEX_CSTRING dl= { file->name, strlen(file->name) }; |
347 | const char *dlend= dl.str + dl.length; |
348 | const size_t so_ext_len= sizeof(SO_EXT) - 1; |
349 | |
350 | if (strcasecmp(dlend - so_ext_len, SO_EXT)) |
351 | continue; |
352 | |
353 | if (lookup.db_value.str) |
354 | { |
355 | if (lookup.wild_db_value) |
356 | { |
357 | if (my_wildcmp(files_charset_info, dl.str, dlend, wstr, wend, |
358 | wild_prefix, wild_one, wild_many)) |
359 | continue; |
360 | } |
361 | else |
362 | { |
363 | if (my_strnncoll(files_charset_info, |
364 | (uchar*)dl.str, dl.length, |
365 | (uchar*)lookup.db_value.str, lookup.db_value.length)) |
366 | continue; |
367 | } |
368 | } |
369 | |
370 | plugin_dl_foreach(thd, &dl, show_plugins, table); |
371 | thd->clear_error(); |
372 | } |
373 | |
374 | my_dirend(dirp); |
375 | DBUG_RETURN(0); |
376 | } |
377 | |
378 | |
379 | #ifdef HAVE_SPATIAL |
380 | static int fill_spatial_ref_sys(THD *thd, TABLE_LIST *tables, COND *cond) |
381 | { |
382 | DBUG_ENTER("fill_spatial_ref_sys" ); |
383 | TABLE *table= tables->table; |
384 | CHARSET_INFO *cs= system_charset_info; |
385 | int result= 1; |
386 | |
387 | restore_record(table, s->default_values); |
388 | |
389 | table->field[0]->store(-1, FALSE); /*SRID*/ |
390 | table->field[1]->store(STRING_WITH_LEN("Not defined" ), cs); /*AUTH_NAME*/ |
391 | table->field[2]->store(-1, FALSE); /*AUTH_SRID*/ |
392 | table->field[3]->store(STRING_WITH_LEN( |
393 | "LOCAL_CS[\"Spatial reference wasn't specified\"," |
394 | "LOCAL_DATUM[\"Unknown\",0]," "UNIT[\"m\",1.0]," "AXIS[\"x\",EAST]," |
395 | "AXIS[\"y\",NORTH]]" ), cs);/*SRTEXT*/ |
396 | if (schema_table_store_record(thd, table)) |
397 | goto exit; |
398 | |
399 | table->field[0]->store(0, TRUE); /*SRID*/ |
400 | table->field[1]->store(STRING_WITH_LEN("EPSG" ), cs); /*AUTH_NAME*/ |
401 | table->field[2]->store(404000, TRUE); /*AUTH_SRID*/ |
402 | table->field[3]->store(STRING_WITH_LEN( |
403 | "LOCAL_CS[\"Wildcard 2D cartesian plane in metric unit\"," |
404 | "LOCAL_DATUM[\"Unknown\",0]," "UNIT[\"m\",1.0]," |
405 | "AXIS[\"x\",EAST]," "AXIS[\"y\",NORTH]," |
406 | "AUTHORITY[\"EPSG\",\"404000\"]]" ), cs);/*SRTEXT*/ |
407 | if (schema_table_store_record(thd, table)) |
408 | goto exit; |
409 | |
410 | result= 0; |
411 | |
412 | exit: |
413 | DBUG_RETURN(result); |
414 | } |
415 | |
416 | |
417 | static int get_geometry_column_record(THD *thd, TABLE_LIST *tables, |
418 | TABLE *table, bool res, |
419 | const LEX_CSTRING *db_name, |
420 | const LEX_CSTRING *table_name) |
421 | { |
422 | CHARSET_INFO *cs= system_charset_info; |
423 | TABLE *show_table; |
424 | Field **ptr, *field; |
425 | DBUG_ENTER("get_geometry_column_record" ); |
426 | |
427 | if (res) |
428 | { |
429 | if (thd->lex->sql_command != SQLCOM_SHOW_FIELDS) |
430 | { |
431 | /* |
432 | I.e. we are in SELECT FROM INFORMATION_SCHEMA.COLUMS |
433 | rather than in SHOW COLUMNS |
434 | */ |
435 | push_warning(thd, Sql_condition::WARN_LEVEL_WARN, |
436 | thd->get_stmt_da()->sql_errno(), |
437 | thd->get_stmt_da()->message()); |
438 | thd->clear_error(); |
439 | res= 0; |
440 | } |
441 | DBUG_RETURN(res); |
442 | } |
443 | |
444 | if (tables->schema_table) |
445 | goto exit; |
446 | show_table= tables->table; |
447 | ptr= show_table->field; |
448 | show_table->use_all_columns(); // Required for default |
449 | restore_record(show_table, s->default_values); |
450 | |
451 | for (; (field= *ptr) ; ptr++) |
452 | if (field->type() == MYSQL_TYPE_GEOMETRY) |
453 | { |
454 | Field_geom *fg= (Field_geom *) field; |
455 | |
456 | DEBUG_SYNC(thd, "get_schema_column" ); |
457 | |
458 | /* Get default row, with all NULL fields set to NULL */ |
459 | restore_record(table, s->default_values); |
460 | |
461 | /*F_TABLE_CATALOG*/ |
462 | table->field[0]->store(STRING_WITH_LEN("def" ), cs); |
463 | /*F_TABLE_SCHEMA*/ |
464 | table->field[1]->store(db_name->str, db_name->length, cs); |
465 | /*F_TABLE_NAME*/ |
466 | table->field[2]->store(table_name->str, table_name->length, cs); |
467 | /*G_TABLE_CATALOG*/ |
468 | table->field[4]->store(STRING_WITH_LEN("def" ), cs); |
469 | /*G_TABLE_SCHEMA*/ |
470 | table->field[5]->store(db_name->str, db_name->length, cs); |
471 | /*G_TABLE_NAME*/ |
472 | table->field[6]->store(table_name->str, table_name->length, cs); |
473 | /*G_GEOMETRY_COLUMN*/ |
474 | table->field[7]->store(field->field_name.str, field->field_name.length, |
475 | cs); |
476 | /*STORAGE_TYPE*/ |
477 | table->field[8]->store(1LL, TRUE); /*Always 1 (binary implementation)*/ |
478 | /*GEOMETRY_TYPE*/ |
479 | table->field[9]->store((longlong) (fg->get_geometry_type()), TRUE); |
480 | /*COORD_DIMENSION*/ |
481 | table->field[10]->store(2LL, TRUE); |
482 | /*MAX_PPR*/ |
483 | table->field[11]->set_null(); |
484 | /*SRID*/ |
485 | table->field[12]->store((longlong) (fg->get_srid()), TRUE); |
486 | |
487 | if (schema_table_store_record(thd, table)) |
488 | DBUG_RETURN(1); |
489 | } |
490 | |
491 | exit: |
492 | DBUG_RETURN(0); |
493 | } |
494 | #endif /*HAVE_SPATIAL*/ |
495 | |
496 | |
497 | /*************************************************************************** |
498 | ** List all Authors. |
499 | ** If you can update it, you get to be in it :) |
500 | ***************************************************************************/ |
501 | |
502 | bool mysqld_show_authors(THD *thd) |
503 | { |
504 | List<Item> field_list; |
505 | Protocol *protocol= thd->protocol; |
506 | MEM_ROOT *mem_root= thd->mem_root; |
507 | DBUG_ENTER("mysqld_show_authors" ); |
508 | |
509 | field_list.push_back(new (mem_root) Item_empty_string(thd, "Name" , 40), |
510 | mem_root); |
511 | field_list.push_back(new (mem_root) Item_empty_string(thd, "Location" , 40), |
512 | mem_root); |
513 | field_list.push_back(new (mem_root) Item_empty_string(thd, "Comment" , 512), |
514 | mem_root); |
515 | |
516 | if (protocol->send_result_set_metadata(&field_list, |
517 | Protocol::SEND_NUM_ROWS | |
518 | Protocol::SEND_EOF)) |
519 | DBUG_RETURN(TRUE); |
520 | |
521 | show_table_authors_st *authors; |
522 | for (authors= show_table_authors; authors->name; authors++) |
523 | { |
524 | protocol->prepare_for_resend(); |
525 | protocol->store(authors->name, system_charset_info); |
526 | protocol->store(authors->location, system_charset_info); |
527 | protocol->store(authors->comment, system_charset_info); |
528 | if (protocol->write()) |
529 | DBUG_RETURN(TRUE); |
530 | } |
531 | my_eof(thd); |
532 | DBUG_RETURN(FALSE); |
533 | } |
534 | |
535 | |
536 | /*************************************************************************** |
537 | ** List all Contributors. |
538 | ** Please get permission before updating |
539 | ***************************************************************************/ |
540 | |
541 | bool mysqld_show_contributors(THD *thd) |
542 | { |
543 | List<Item> field_list; |
544 | Protocol *protocol= thd->protocol; |
545 | MEM_ROOT *mem_root= thd->mem_root; |
546 | DBUG_ENTER("mysqld_show_contributors" ); |
547 | |
548 | field_list.push_back(new (mem_root) Item_empty_string(thd, "Name" , 40), |
549 | mem_root); |
550 | field_list.push_back(new (mem_root) Item_empty_string(thd, "Location" , 40), |
551 | mem_root); |
552 | field_list.push_back(new (mem_root) Item_empty_string(thd, "Comment" , 512), |
553 | mem_root); |
554 | |
555 | if (protocol->send_result_set_metadata(&field_list, |
556 | Protocol::SEND_NUM_ROWS | |
557 | Protocol::SEND_EOF)) |
558 | DBUG_RETURN(TRUE); |
559 | |
560 | show_table_contributors_st *contributors; |
561 | for (contributors= show_table_contributors; contributors->name; contributors++) |
562 | { |
563 | protocol->prepare_for_resend(); |
564 | protocol->store(contributors->name, system_charset_info); |
565 | protocol->store(contributors->location, system_charset_info); |
566 | protocol->store(contributors->comment, system_charset_info); |
567 | if (protocol->write()) |
568 | DBUG_RETURN(TRUE); |
569 | } |
570 | my_eof(thd); |
571 | DBUG_RETURN(FALSE); |
572 | } |
573 | |
574 | |
575 | /*************************************************************************** |
576 | List all privileges supported |
577 | ***************************************************************************/ |
578 | |
579 | struct show_privileges_st { |
580 | const char *privilege; |
581 | const char *context; |
582 | const char *; |
583 | }; |
584 | |
585 | static struct show_privileges_st sys_privileges[]= |
586 | { |
587 | {"Alter" , "Tables" , "To alter the table" }, |
588 | {"Alter routine" , "Functions,Procedures" , "To alter or drop stored functions/procedures" }, |
589 | {"Create" , "Databases,Tables,Indexes" , "To create new databases and tables" }, |
590 | {"Create routine" ,"Databases" ,"To use CREATE FUNCTION/PROCEDURE" }, |
591 | {"Create temporary tables" ,"Databases" ,"To use CREATE TEMPORARY TABLE" }, |
592 | {"Create view" , "Tables" , "To create new views" }, |
593 | {"Create user" , "Server Admin" , "To create new users" }, |
594 | {"Delete" , "Tables" , "To delete existing rows" }, |
595 | {"Delete versioning rows" , "Tables" , "To delete versioning table historical rows" }, |
596 | {"Drop" , "Databases,Tables" , "To drop databases, tables, and views" }, |
597 | #ifdef HAVE_EVENT_SCHEDULER |
598 | {"Event" ,"Server Admin" ,"To create, alter, drop and execute events" }, |
599 | #endif |
600 | {"Execute" , "Functions,Procedures" , "To execute stored routines" }, |
601 | {"File" , "File access on server" , "To read and write files on the server" }, |
602 | {"Grant option" , "Databases,Tables,Functions,Procedures" , "To give to other users those privileges you possess" }, |
603 | {"Index" , "Tables" , "To create or drop indexes" }, |
604 | {"Insert" , "Tables" , "To insert data into tables" }, |
605 | {"Lock tables" ,"Databases" ,"To use LOCK TABLES (together with SELECT privilege)" }, |
606 | {"Process" , "Server Admin" , "To view the plain text of currently executing queries" }, |
607 | {"Proxy" , "Server Admin" , "To make proxy user possible" }, |
608 | {"References" , "Databases,Tables" , "To have references on tables" }, |
609 | {"Reload" , "Server Admin" , "To reload or refresh tables, logs and privileges" }, |
610 | {"Replication client" ,"Server Admin" ,"To ask where the slave or master servers are" }, |
611 | {"Replication slave" ,"Server Admin" ,"To read binary log events from the master" }, |
612 | {"Select" , "Tables" , "To retrieve rows from table" }, |
613 | {"Show databases" ,"Server Admin" ,"To see all databases with SHOW DATABASES" }, |
614 | {"Show view" ,"Tables" ,"To see views with SHOW CREATE VIEW" }, |
615 | {"Shutdown" ,"Server Admin" , "To shut down the server" }, |
616 | {"Super" ,"Server Admin" ,"To use KILL thread, SET GLOBAL, CHANGE MASTER, etc." }, |
617 | {"Trigger" ,"Tables" , "To use triggers" }, |
618 | {"Create tablespace" , "Server Admin" , "To create/alter/drop tablespaces" }, |
619 | {"Update" , "Tables" , "To update existing rows" }, |
620 | {"Usage" ,"Server Admin" ,"No privileges - allow connect only" }, |
621 | {NullS, NullS, NullS} |
622 | }; |
623 | |
624 | bool mysqld_show_privileges(THD *thd) |
625 | { |
626 | List<Item> field_list; |
627 | Protocol *protocol= thd->protocol; |
628 | MEM_ROOT *mem_root= thd->mem_root; |
629 | DBUG_ENTER("mysqld_show_privileges" ); |
630 | |
631 | field_list.push_back(new (mem_root) Item_empty_string(thd, "Privilege" , 10), |
632 | mem_root); |
633 | field_list.push_back(new (mem_root) Item_empty_string(thd, "Context" , 15), |
634 | mem_root); |
635 | field_list.push_back(new (mem_root) Item_empty_string(thd, "Comment" , |
636 | NAME_CHAR_LEN), |
637 | mem_root); |
638 | |
639 | if (protocol->send_result_set_metadata(&field_list, |
640 | Protocol::SEND_NUM_ROWS | |
641 | Protocol::SEND_EOF)) |
642 | DBUG_RETURN(TRUE); |
643 | |
644 | show_privileges_st *privilege= sys_privileges; |
645 | for (privilege= sys_privileges; privilege->privilege ; privilege++) |
646 | { |
647 | protocol->prepare_for_resend(); |
648 | protocol->store(privilege->privilege, system_charset_info); |
649 | protocol->store(privilege->context, system_charset_info); |
650 | protocol->store(privilege->comment, system_charset_info); |
651 | if (protocol->write()) |
652 | DBUG_RETURN(TRUE); |
653 | } |
654 | my_eof(thd); |
655 | DBUG_RETURN(FALSE); |
656 | } |
657 | |
658 | |
659 | /** Hash of LEX_STRINGs used to search for ignored db directories. */ |
660 | static HASH ignore_db_dirs_hash; |
661 | |
662 | /** |
663 | An array of LEX_STRING pointers to collect the options at |
664 | option parsing time. |
665 | */ |
666 | static DYNAMIC_ARRAY ignore_db_dirs_array; |
667 | |
668 | /** |
669 | A value for the read only system variable to show a list of |
670 | ignored directories. |
671 | */ |
672 | char *opt_ignore_db_dirs= NULL; |
673 | |
674 | /** |
675 | This flag is ON if: |
676 | - the list of ignored directories is not empty |
677 | |
678 | - and some of the ignored directory names |
679 | need no tablename-to-filename conversion. |
680 | Otherwise, if the name of the directory contains |
681 | unconditional characters like '+' or '.', they |
682 | never can match the database directory name. So the |
683 | db_name_is_in_ignore_db_dirs_list() can just return at once. |
684 | */ |
685 | static bool skip_ignored_dir_check= TRUE; |
686 | |
687 | /** |
688 | Sets up the data structures for collection of directories at option |
689 | processing time. |
690 | We need to collect the directories in an array first, because |
691 | we need the character sets initialized before setting up the hash. |
692 | |
693 | @return state |
694 | @retval TRUE failed |
695 | @retval FALSE success |
696 | */ |
697 | |
698 | bool |
699 | ignore_db_dirs_init() |
700 | { |
701 | return my_init_dynamic_array(&ignore_db_dirs_array, sizeof(LEX_CSTRING *), |
702 | 0, 0, MYF(0)); |
703 | } |
704 | |
705 | |
706 | /** |
707 | Retrieves the key (the string itself) from the LEX_STRING hash members. |
708 | |
709 | Needed by hash_init(). |
710 | |
711 | @param data the data element from the hash |
712 | @param out len_ret Placeholder to return the length of the key |
713 | @param unused |
714 | @return a pointer to the key |
715 | */ |
716 | |
717 | static uchar * |
718 | db_dirs_hash_get_key(const uchar *data, size_t *len_ret, |
719 | my_bool __attribute__((unused))) |
720 | { |
721 | LEX_CSTRING *e= (LEX_CSTRING *) data; |
722 | |
723 | *len_ret= e->length; |
724 | return (uchar *) e->str; |
725 | } |
726 | |
727 | |
728 | /** |
729 | Wrap a directory name into a LEX_STRING and push it to the array. |
730 | |
731 | Called at option processing time for each --ignore-db-dir option. |
732 | |
733 | @param path the name of the directory to push |
734 | @return state |
735 | @retval TRUE failed |
736 | @retval FALSE success |
737 | */ |
738 | |
739 | bool |
740 | push_ignored_db_dir(char *path) |
741 | { |
742 | LEX_CSTRING *new_elt; |
743 | char *new_elt_buffer; |
744 | size_t path_len= strlen(path); |
745 | |
746 | if (!path_len || path_len >= FN_REFLEN) |
747 | return true; |
748 | |
749 | // No need to normalize, it's only a directory name, not a path. |
750 | if (!my_multi_malloc(0, |
751 | &new_elt, sizeof(LEX_CSTRING), |
752 | &new_elt_buffer, path_len + 1, |
753 | NullS)) |
754 | return true; |
755 | new_elt->str= new_elt_buffer; |
756 | memcpy(new_elt_buffer, path, path_len); |
757 | new_elt_buffer[path_len]= 0; |
758 | new_elt->length= path_len; |
759 | return insert_dynamic(&ignore_db_dirs_array, (uchar*) &new_elt); |
760 | } |
761 | |
762 | |
763 | /** |
764 | Clean up the directory ignore options accumulated so far. |
765 | |
766 | Called at option processing time for each --ignore-db-dir option |
767 | with an empty argument. |
768 | */ |
769 | |
770 | void |
771 | ignore_db_dirs_reset() |
772 | { |
773 | LEX_CSTRING **elt; |
774 | while (NULL!= (elt= (LEX_CSTRING **) pop_dynamic(&ignore_db_dirs_array))) |
775 | if (elt && *elt) |
776 | my_free(*elt); |
777 | } |
778 | |
779 | |
780 | /** |
781 | Free the directory ignore option variables. |
782 | |
783 | Called at server shutdown. |
784 | */ |
785 | |
786 | void |
787 | ignore_db_dirs_free() |
788 | { |
789 | if (opt_ignore_db_dirs) |
790 | { |
791 | my_free(opt_ignore_db_dirs); |
792 | opt_ignore_db_dirs= NULL; |
793 | } |
794 | ignore_db_dirs_reset(); |
795 | delete_dynamic(&ignore_db_dirs_array); |
796 | my_hash_free(&ignore_db_dirs_hash); |
797 | } |
798 | |
799 | |
800 | /** |
801 | Initialize the ignore db directories hash and status variable from |
802 | the options collected in the array. |
803 | |
804 | Called when option processing is over and the server's in-memory |
805 | structures are fully initialized. |
806 | |
807 | @return state |
808 | @retval TRUE failed |
809 | @retval FALSE success |
810 | */ |
811 | |
812 | static void dispose_db_dir(void *ptr) |
813 | { |
814 | my_free(ptr); |
815 | } |
816 | |
817 | |
818 | /* |
819 | Append an element into @@ignore_db_dirs |
820 | |
821 | This is a function to be called after regular option processing has been |
822 | finalized. |
823 | */ |
824 | |
825 | void ignore_db_dirs_append(const char *dirname_arg) |
826 | { |
827 | char *new_entry_buf; |
828 | LEX_STRING *new_entry; |
829 | size_t len= strlen(dirname_arg); |
830 | |
831 | if (!my_multi_malloc(0, |
832 | &new_entry, sizeof(LEX_STRING), |
833 | &new_entry_buf, len + 1, |
834 | NullS)) |
835 | return; |
836 | |
837 | memcpy(new_entry_buf, dirname_arg, len+1); |
838 | new_entry->str = new_entry_buf; |
839 | new_entry->length= len; |
840 | |
841 | if (my_hash_insert(&ignore_db_dirs_hash, (uchar *)new_entry)) |
842 | { |
843 | // Either the name is already there or out-of-memory. |
844 | my_free(new_entry); |
845 | return; |
846 | } |
847 | |
848 | // Append the name to the option string. |
849 | size_t curlen= strlen(opt_ignore_db_dirs); |
850 | // Add one for comma and one for \0. |
851 | size_t newlen= curlen + len + 1 + 1; |
852 | char *new_db_dirs; |
853 | if (!(new_db_dirs= (char*)my_malloc(newlen ,MYF(0)))) |
854 | { |
855 | // This is not a critical condition |
856 | return; |
857 | } |
858 | |
859 | memcpy(new_db_dirs, opt_ignore_db_dirs, curlen); |
860 | if (curlen != 0) |
861 | new_db_dirs[curlen]=','; |
862 | memcpy(new_db_dirs + (curlen + ((curlen!=0)?1:0)), dirname_arg, len+1); |
863 | |
864 | if (opt_ignore_db_dirs) |
865 | my_free(opt_ignore_db_dirs); |
866 | opt_ignore_db_dirs= new_db_dirs; |
867 | } |
868 | |
869 | bool |
870 | ignore_db_dirs_process_additions() |
871 | { |
872 | ulong i; |
873 | size_t len; |
874 | char *ptr; |
875 | LEX_CSTRING *dir; |
876 | |
877 | skip_ignored_dir_check= TRUE; |
878 | |
879 | if (my_hash_init(&ignore_db_dirs_hash, |
880 | lower_case_table_names ? |
881 | character_set_filesystem : &my_charset_bin, |
882 | 0, 0, 0, db_dirs_hash_get_key, |
883 | dispose_db_dir, |
884 | HASH_UNIQUE)) |
885 | return true; |
886 | |
887 | /* len starts from 1 because of the terminating zero. */ |
888 | len= 1; |
889 | for (i= 0; i < ignore_db_dirs_array.elements; i++) |
890 | { |
891 | get_dynamic(&ignore_db_dirs_array, (uchar *) &dir, i); |
892 | len+= dir->length + 1; // +1 for the comma |
893 | if (skip_ignored_dir_check) |
894 | { |
895 | char buff[FN_REFLEN]; |
896 | (void) tablename_to_filename(dir->str, buff, sizeof(buff)); |
897 | skip_ignored_dir_check= strcmp(dir->str, buff) != 0; |
898 | } |
899 | } |
900 | |
901 | /* No delimiter for the last directory. */ |
902 | if (len > 1) |
903 | len--; |
904 | |
905 | /* +1 the terminating zero */ |
906 | ptr= opt_ignore_db_dirs= (char *) my_malloc(len + 1, MYF(0)); |
907 | if (!ptr) |
908 | return true; |
909 | |
910 | /* Make sure we have an empty string to start with. */ |
911 | *ptr= 0; |
912 | |
913 | for (i= 0; i < ignore_db_dirs_array.elements; i++) |
914 | { |
915 | get_dynamic(&ignore_db_dirs_array, (uchar *) &dir, i); |
916 | if (my_hash_insert(&ignore_db_dirs_hash, (uchar *)dir)) |
917 | { |
918 | /* ignore duplicates from the config file */ |
919 | if (my_hash_search(&ignore_db_dirs_hash, (uchar *)dir->str, dir->length)) |
920 | { |
921 | sql_print_warning("Duplicate ignore-db-dir directory name '%.*s' " |
922 | "found in the config file(s). Ignoring the duplicate." , |
923 | (int) dir->length, dir->str); |
924 | my_free(dir); |
925 | goto continue_loop; |
926 | } |
927 | |
928 | return true; |
929 | } |
930 | ptr= strnmov(ptr, dir->str, dir->length); |
931 | *(ptr++)= ','; |
932 | |
933 | continue_loop: |
934 | /* |
935 | Set the transferred array element to NULL to avoid double free |
936 | in case of error. |
937 | */ |
938 | dir= NULL; |
939 | set_dynamic(&ignore_db_dirs_array, (uchar *) &dir, i); |
940 | } |
941 | |
942 | if (ptr > opt_ignore_db_dirs) |
943 | { |
944 | ptr--; |
945 | DBUG_ASSERT(*ptr == ','); |
946 | } |
947 | |
948 | /* make sure the string is terminated */ |
949 | DBUG_ASSERT(ptr - opt_ignore_db_dirs <= (ptrdiff_t) len); |
950 | *ptr= 0; |
951 | |
952 | /* |
953 | It's OK to empty the array here as the allocated elements are |
954 | referenced through the hash now. |
955 | */ |
956 | reset_dynamic(&ignore_db_dirs_array); |
957 | |
958 | return false; |
959 | } |
960 | |
961 | |
962 | /** |
963 | Check if a directory name is in the hash of ignored directories. |
964 | |
965 | @return search result |
966 | @retval TRUE found |
967 | @retval FALSE not found |
968 | */ |
969 | |
970 | static inline bool |
971 | is_in_ignore_db_dirs_list(const char *directory) |
972 | { |
973 | return ignore_db_dirs_hash.records && |
974 | NULL != my_hash_search(&ignore_db_dirs_hash, (const uchar *) directory, |
975 | strlen(directory)); |
976 | } |
977 | |
978 | |
979 | /** |
980 | Check if a database name is in the hash of ignored directories. |
981 | |
982 | @return search result |
983 | @retval TRUE found |
984 | @retval FALSE not found |
985 | */ |
986 | |
987 | bool |
988 | db_name_is_in_ignore_db_dirs_list(const char *directory) |
989 | { |
990 | char buff[FN_REFLEN]; |
991 | uint buff_len; |
992 | |
993 | if (skip_ignored_dir_check) |
994 | return 0; |
995 | |
996 | buff_len= tablename_to_filename(directory, buff, sizeof(buff)); |
997 | |
998 | return my_hash_search(&ignore_db_dirs_hash, (uchar *) buff, buff_len)!=NULL; |
999 | } |
1000 | |
1001 | enum find_files_result { |
1002 | FIND_FILES_OK, |
1003 | FIND_FILES_OOM, |
1004 | FIND_FILES_DIR |
1005 | }; |
1006 | |
1007 | /* |
1008 | find_files() - find files in a given directory. |
1009 | |
1010 | SYNOPSIS |
1011 | find_files() |
1012 | thd thread handler |
1013 | files put found files in this list |
1014 | db database name to search tables in |
1015 | or NULL to search for databases |
1016 | path path to database |
1017 | wild filter for found files |
1018 | |
1019 | RETURN |
1020 | FIND_FILES_OK success |
1021 | FIND_FILES_OOM out of memory error |
1022 | FIND_FILES_DIR no such directory, or directory can't be read |
1023 | */ |
1024 | |
1025 | |
1026 | static find_files_result |
1027 | find_files(THD *thd, Dynamic_array<LEX_CSTRING*> *files, LEX_CSTRING *db, |
1028 | const char *path, const LEX_CSTRING *wild) |
1029 | { |
1030 | MY_DIR *dirp; |
1031 | Discovered_table_list tl(thd, files, wild); |
1032 | DBUG_ENTER("find_files" ); |
1033 | |
1034 | if (!(dirp = my_dir(path, MY_THREAD_SPECIFIC | (db ? 0 : MY_WANT_STAT)))) |
1035 | { |
1036 | if (my_errno == ENOENT) |
1037 | my_error(ER_BAD_DB_ERROR, MYF(ME_BELL | ME_WAITTANG), db->str); |
1038 | else |
1039 | my_error(ER_CANT_READ_DIR, MYF(ME_BELL | ME_WAITTANG), path, my_errno); |
1040 | DBUG_RETURN(FIND_FILES_DIR); |
1041 | } |
1042 | |
1043 | if (!db) /* Return databases */ |
1044 | { |
1045 | for (uint i=0; i < (uint) dirp->number_of_files; i++) |
1046 | { |
1047 | FILEINFO *file= dirp->dir_entry+i; |
1048 | #ifdef USE_SYMDIR |
1049 | char *ext; |
1050 | char buff[FN_REFLEN]; |
1051 | if (my_use_symdir && !strcmp(ext=fn_ext(file->name), ".sym" )) |
1052 | { |
1053 | /* Only show the sym file if it points to a directory */ |
1054 | char *end; |
1055 | *ext=0; /* Remove extension */ |
1056 | unpack_dirname(buff, file->name); |
1057 | end= strend(buff); |
1058 | if (end != buff && end[-1] == FN_LIBCHAR) |
1059 | end[-1]= 0; // Remove end FN_LIBCHAR |
1060 | if (!mysql_file_stat(key_file_misc, buff, file->mystat, MYF(0))) |
1061 | continue; |
1062 | } |
1063 | #endif |
1064 | if (!MY_S_ISDIR(file->mystat->st_mode)) |
1065 | continue; |
1066 | |
1067 | if (is_in_ignore_db_dirs_list(file->name)) |
1068 | continue; |
1069 | |
1070 | if (tl.add_file(file->name)) |
1071 | goto err; |
1072 | } |
1073 | } |
1074 | else |
1075 | { |
1076 | if (ha_discover_table_names(thd, db, dirp, &tl, false)) |
1077 | goto err; |
1078 | } |
1079 | if (is_show_command(thd)) |
1080 | tl.sort(); |
1081 | #ifndef DBUG_OFF |
1082 | else |
1083 | { |
1084 | /* |
1085 | sort_desc() is used to find easier unstable mtr tests that query |
1086 | INFORMATION_SCHEMA.{SCHEMATA|TABLES} without a proper ORDER BY. |
1087 | This can be removed in some release after 10.3 (e.g. in 10.4). |
1088 | */ |
1089 | tl.sort_desc(); |
1090 | } |
1091 | #endif |
1092 | |
1093 | DBUG_PRINT("info" ,("found: %zu files" , files->elements())); |
1094 | my_dirend(dirp); |
1095 | |
1096 | DBUG_RETURN(FIND_FILES_OK); |
1097 | |
1098 | err: |
1099 | my_dirend(dirp); |
1100 | DBUG_RETURN(FIND_FILES_OOM); |
1101 | } |
1102 | |
1103 | |
1104 | /** |
1105 | An Internal_error_handler that suppresses errors regarding views' |
1106 | underlying tables that occur during privilege checking within SHOW CREATE |
1107 | VIEW commands. This happens in the cases when |
1108 | |
1109 | - A view's underlying table (e.g. referenced in its SELECT list) does not |
1110 | exist. There should not be an error as no attempt was made to access it |
1111 | per se. |
1112 | |
1113 | - Access is denied for some table, column, function or stored procedure |
1114 | such as mentioned above. This error gets raised automatically, since we |
1115 | can't untangle its access checking from that of the view itself. |
1116 | */ |
1117 | class Show_create_error_handler : public Internal_error_handler { |
1118 | |
1119 | TABLE_LIST *m_top_view; |
1120 | bool m_handling; |
1121 | Security_context *m_sctx; |
1122 | |
1123 | char m_view_access_denied_message[MYSQL_ERRMSG_SIZE]; |
1124 | char *m_view_access_denied_message_ptr; |
1125 | |
1126 | public: |
1127 | |
1128 | /** |
1129 | Creates a new Show_create_error_handler for the particular security |
1130 | context and view. |
1131 | |
1132 | @thd Thread context, used for security context information if needed. |
1133 | @top_view The view. We do not verify at this point that top_view is in |
1134 | fact a view since, alas, these things do not stay constant. |
1135 | */ |
1136 | explicit Show_create_error_handler(THD *thd, TABLE_LIST *top_view) : |
1137 | m_top_view(top_view), m_handling(FALSE), |
1138 | m_view_access_denied_message_ptr(NULL) |
1139 | { |
1140 | |
1141 | m_sctx= MY_TEST(m_top_view->security_ctx) ? |
1142 | m_top_view->security_ctx : thd->security_ctx; |
1143 | } |
1144 | |
1145 | /** |
1146 | Lazy instantiation of 'view access denied' message. The purpose of the |
1147 | Show_create_error_handler is to hide details of underlying tables for |
1148 | which we have no privileges behind ER_VIEW_INVALID messages. But this |
1149 | obviously does not apply if we lack privileges on the view itself. |
1150 | Unfortunately the information about for which table privilege checking |
1151 | failed is not available at this point. The only way for us to check is by |
1152 | reconstructing the actual error message and see if it's the same. |
1153 | */ |
1154 | char* get_view_access_denied_message(THD *thd) |
1155 | { |
1156 | if (!m_view_access_denied_message_ptr) |
1157 | { |
1158 | m_view_access_denied_message_ptr= m_view_access_denied_message; |
1159 | my_snprintf(m_view_access_denied_message, MYSQL_ERRMSG_SIZE, |
1160 | ER_THD(thd, ER_TABLEACCESS_DENIED_ERROR), "SHOW VIEW" , |
1161 | m_sctx->priv_user, |
1162 | m_sctx->host_or_ip, m_top_view->get_table_name()); |
1163 | } |
1164 | return m_view_access_denied_message_ptr; |
1165 | } |
1166 | |
1167 | bool handle_condition(THD *thd, uint sql_errno, const char * /* sqlstate */, |
1168 | Sql_condition::enum_warning_level *level, |
1169 | const char *message, Sql_condition ** /* cond_hdl */) |
1170 | { |
1171 | /* |
1172 | The handler does not handle the errors raised by itself. |
1173 | At this point we know if top_view is really a view. |
1174 | */ |
1175 | if (m_handling || !m_top_view->view) |
1176 | return FALSE; |
1177 | |
1178 | m_handling= TRUE; |
1179 | |
1180 | bool is_handled; |
1181 | |
1182 | switch (sql_errno) |
1183 | { |
1184 | case ER_TABLEACCESS_DENIED_ERROR: |
1185 | if (!strcmp(get_view_access_denied_message(thd), message)) |
1186 | { |
1187 | /* Access to top view is not granted, don't interfere. */ |
1188 | is_handled= FALSE; |
1189 | break; |
1190 | } |
1191 | /* fall through */ |
1192 | case ER_COLUMNACCESS_DENIED_ERROR: |
1193 | case ER_VIEW_NO_EXPLAIN: /* Error was anonymized, ignore all the same. */ |
1194 | case ER_PROCACCESS_DENIED_ERROR: |
1195 | is_handled= TRUE; |
1196 | break; |
1197 | |
1198 | case ER_BAD_FIELD_ERROR: |
1199 | case ER_SP_DOES_NOT_EXIST: |
1200 | case ER_NO_SUCH_TABLE: |
1201 | case ER_NO_SUCH_TABLE_IN_ENGINE: |
1202 | /* Established behavior: warn if underlying tables, columns, or functions |
1203 | are missing. */ |
1204 | push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, |
1205 | ER_VIEW_INVALID, |
1206 | ER_THD(thd, ER_VIEW_INVALID), |
1207 | m_top_view->get_db_name(), |
1208 | m_top_view->get_table_name()); |
1209 | is_handled= TRUE; |
1210 | break; |
1211 | |
1212 | default: |
1213 | is_handled= FALSE; |
1214 | } |
1215 | |
1216 | m_handling= FALSE; |
1217 | return is_handled; |
1218 | } |
1219 | }; |
1220 | |
1221 | |
1222 | /* |
1223 | Return metadata for CREATE command for table or view |
1224 | |
1225 | @param thd Thread handler |
1226 | @param table_list Table / view |
1227 | @param field_list resulting list of fields |
1228 | @param buffer resulting CREATE statement |
1229 | |
1230 | @return |
1231 | @retval 0 OK |
1232 | @retval 1 Error |
1233 | |
1234 | */ |
1235 | |
1236 | bool |
1237 | mysqld_show_create_get_fields(THD *thd, TABLE_LIST *table_list, |
1238 | List<Item> *field_list, String *buffer) |
1239 | { |
1240 | bool error= TRUE; |
1241 | MEM_ROOT *mem_root= thd->mem_root; |
1242 | DBUG_ENTER("mysqld_show_create_get_fields" ); |
1243 | DBUG_PRINT("enter" ,("db: %s table: %s" ,table_list->db.str, |
1244 | table_list->table_name.str)); |
1245 | |
1246 | /* We want to preserve the tree for views. */ |
1247 | thd->lex->context_analysis_only|= CONTEXT_ANALYSIS_ONLY_VIEW; |
1248 | |
1249 | { |
1250 | /* |
1251 | Use open_tables() directly rather than |
1252 | open_normal_and_derived_tables(). This ensures that |
1253 | close_thread_tables() is not called if open tables fails and the |
1254 | error is ignored. This allows us to handle broken views nicely. |
1255 | */ |
1256 | uint counter; |
1257 | Show_create_error_handler view_error_suppressor(thd, table_list); |
1258 | thd->push_internal_handler(&view_error_suppressor); |
1259 | bool open_error= |
1260 | open_tables(thd, &table_list, &counter, |
1261 | MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL) || |
1262 | mysql_handle_derived(thd->lex, DT_INIT | DT_PREPARE); |
1263 | thd->pop_internal_handler(); |
1264 | if (unlikely(open_error && (thd->killed || thd->is_error()))) |
1265 | goto exit; |
1266 | } |
1267 | |
1268 | /* TODO: add environment variables show when it become possible */ |
1269 | if (thd->lex->table_type == TABLE_TYPE_VIEW && !table_list->view) |
1270 | { |
1271 | my_error(ER_WRONG_OBJECT, MYF(0), |
1272 | table_list->db.str, table_list->table_name.str, "VIEW" ); |
1273 | goto exit; |
1274 | } |
1275 | else if (thd->lex->table_type == TABLE_TYPE_SEQUENCE && |
1276 | table_list->table->s->table_type != TABLE_TYPE_SEQUENCE) |
1277 | { |
1278 | my_error(ER_NOT_SEQUENCE, MYF(0), |
1279 | table_list->db.str, table_list->table_name.str); |
1280 | goto exit; |
1281 | } |
1282 | |
1283 | buffer->length(0); |
1284 | |
1285 | if (table_list->view) |
1286 | buffer->set_charset(table_list->view_creation_ctx->get_client_cs()); |
1287 | |
1288 | if ((table_list->view ? |
1289 | show_create_view(thd, table_list, buffer) : |
1290 | thd->lex->table_type == TABLE_TYPE_SEQUENCE ? |
1291 | show_create_sequence(thd, table_list, buffer) : |
1292 | show_create_table(thd, table_list, buffer, NULL, WITHOUT_DB_NAME))) |
1293 | goto exit; |
1294 | |
1295 | if (table_list->view) |
1296 | { |
1297 | field_list->push_back(new (mem_root) |
1298 | Item_empty_string(thd, "View" , NAME_CHAR_LEN), |
1299 | mem_root); |
1300 | field_list->push_back(new (mem_root) |
1301 | Item_empty_string(thd, "Create View" , |
1302 | MY_MAX(buffer->length(),1024)), |
1303 | mem_root); |
1304 | field_list->push_back(new (mem_root) |
1305 | Item_empty_string(thd, "character_set_client" , |
1306 | MY_CS_NAME_SIZE), |
1307 | mem_root); |
1308 | field_list->push_back(new (mem_root) |
1309 | Item_empty_string(thd, "collation_connection" , |
1310 | MY_CS_NAME_SIZE), |
1311 | mem_root); |
1312 | } |
1313 | else |
1314 | { |
1315 | field_list->push_back(new (mem_root) |
1316 | Item_empty_string(thd, "Table" , NAME_CHAR_LEN), |
1317 | mem_root); |
1318 | // 1024 is for not to confuse old clients |
1319 | field_list->push_back(new (mem_root) |
1320 | Item_empty_string(thd, "Create Table" , |
1321 | MY_MAX(buffer->length(),1024)), |
1322 | mem_root); |
1323 | } |
1324 | error= FALSE; |
1325 | |
1326 | exit: |
1327 | DBUG_RETURN(error); |
1328 | } |
1329 | |
1330 | |
1331 | /* |
1332 | Return CREATE command for table or view |
1333 | |
1334 | @param thd Thread handler |
1335 | @param table_list Table / view |
1336 | |
1337 | @return |
1338 | @retval 0 OK |
1339 | @retval 1 Error |
1340 | |
1341 | @notes |
1342 | table_list->db and table_list->table_name are kept unchanged to |
1343 | not cause problems with SP. |
1344 | */ |
1345 | |
1346 | bool |
1347 | mysqld_show_create(THD *thd, TABLE_LIST *table_list) |
1348 | { |
1349 | Protocol *protocol= thd->protocol; |
1350 | char buff[2048]; |
1351 | String buffer(buff, sizeof(buff), system_charset_info); |
1352 | List<Item> field_list; |
1353 | bool error= TRUE; |
1354 | DBUG_ENTER("mysqld_show_create" ); |
1355 | DBUG_PRINT("enter" ,("db: %s table: %s" ,table_list->db.str, |
1356 | table_list->table_name.str)); |
1357 | |
1358 | /* |
1359 | Metadata locks taken during SHOW CREATE should be released when |
1360 | the statmement completes as it is an information statement. |
1361 | */ |
1362 | MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint(); |
1363 | |
1364 | TABLE_LIST archive; |
1365 | |
1366 | if (mysqld_show_create_get_fields(thd, table_list, &field_list, &buffer)) |
1367 | goto exit; |
1368 | |
1369 | if (protocol->send_result_set_metadata(&field_list, |
1370 | Protocol::SEND_NUM_ROWS | |
1371 | Protocol::SEND_EOF)) |
1372 | goto exit; |
1373 | |
1374 | protocol->prepare_for_resend(); |
1375 | if (table_list->view) |
1376 | protocol->store(table_list->view_name.str, system_charset_info); |
1377 | else |
1378 | { |
1379 | if (table_list->schema_table) |
1380 | protocol->store(table_list->schema_table->table_name, system_charset_info); |
1381 | else |
1382 | protocol->store(table_list->table->alias.c_ptr(), system_charset_info); |
1383 | } |
1384 | |
1385 | if (table_list->view) |
1386 | { |
1387 | protocol->store(buffer.ptr(), buffer.length(), |
1388 | table_list->view_creation_ctx->get_client_cs()); |
1389 | |
1390 | protocol->store(table_list->view_creation_ctx->get_client_cs()->csname, |
1391 | system_charset_info); |
1392 | |
1393 | protocol->store(table_list->view_creation_ctx->get_connection_cl()->name, |
1394 | system_charset_info); |
1395 | } |
1396 | else |
1397 | protocol->store(buffer.ptr(), buffer.length(), buffer.charset()); |
1398 | |
1399 | if (protocol->write()) |
1400 | goto exit; |
1401 | |
1402 | error= FALSE; |
1403 | my_eof(thd); |
1404 | |
1405 | exit: |
1406 | close_thread_tables(thd); |
1407 | /* Release any metadata locks taken during SHOW CREATE. */ |
1408 | thd->mdl_context.rollback_to_savepoint(mdl_savepoint); |
1409 | DBUG_RETURN(error); |
1410 | } |
1411 | |
1412 | |
1413 | void mysqld_show_create_db_get_fields(THD *thd, List<Item> *field_list) |
1414 | { |
1415 | MEM_ROOT *mem_root= thd->mem_root; |
1416 | field_list->push_back(new (mem_root) |
1417 | Item_empty_string(thd, "Database" , NAME_CHAR_LEN), |
1418 | mem_root); |
1419 | field_list->push_back(new (mem_root) |
1420 | Item_empty_string(thd, "Create Database" , 1024), |
1421 | mem_root); |
1422 | } |
1423 | |
1424 | |
1425 | bool mysqld_show_create_db(THD *thd, LEX_CSTRING *dbname, |
1426 | LEX_CSTRING *orig_dbname, |
1427 | const DDL_options_st &options) |
1428 | { |
1429 | char buff[2048]; |
1430 | String buffer(buff, sizeof(buff), system_charset_info); |
1431 | #ifndef NO_EMBEDDED_ACCESS_CHECKS |
1432 | Security_context *sctx= thd->security_ctx; |
1433 | uint db_access; |
1434 | #endif |
1435 | Schema_specification_st create; |
1436 | Protocol *protocol=thd->protocol; |
1437 | List<Item> field_list; |
1438 | DBUG_ENTER("mysql_show_create_db" ); |
1439 | |
1440 | #ifndef NO_EMBEDDED_ACCESS_CHECKS |
1441 | if (test_all_bits(sctx->master_access, DB_ACLS)) |
1442 | db_access=DB_ACLS; |
1443 | else |
1444 | { |
1445 | db_access= acl_get(sctx->host, sctx->ip, sctx->priv_user, dbname->str, 0) | |
1446 | sctx->master_access; |
1447 | if (sctx->priv_role[0]) |
1448 | db_access|= acl_get("" , "" , sctx->priv_role, dbname->str, 0); |
1449 | } |
1450 | |
1451 | if (!(db_access & DB_ACLS) && check_grant_db(thd,dbname->str)) |
1452 | { |
1453 | status_var_increment(thd->status_var.access_denied_errors); |
1454 | my_error(ER_DBACCESS_DENIED_ERROR, MYF(0), |
1455 | sctx->priv_user, sctx->host_or_ip, dbname->str); |
1456 | general_log_print(thd,COM_INIT_DB,ER_THD(thd, ER_DBACCESS_DENIED_ERROR), |
1457 | sctx->priv_user, sctx->host_or_ip, orig_dbname->str); |
1458 | DBUG_RETURN(TRUE); |
1459 | } |
1460 | #endif |
1461 | if (is_infoschema_db(dbname)) |
1462 | { |
1463 | *dbname= INFORMATION_SCHEMA_NAME; |
1464 | create.default_table_charset= system_charset_info; |
1465 | } |
1466 | else |
1467 | { |
1468 | if (check_db_dir_existence(dbname->str)) |
1469 | { |
1470 | my_error(ER_BAD_DB_ERROR, MYF(0), dbname->str); |
1471 | DBUG_RETURN(TRUE); |
1472 | } |
1473 | |
1474 | load_db_opt_by_name(thd, dbname->str, &create); |
1475 | } |
1476 | |
1477 | mysqld_show_create_db_get_fields(thd, &field_list); |
1478 | |
1479 | if (protocol->send_result_set_metadata(&field_list, |
1480 | Protocol::SEND_NUM_ROWS | |
1481 | Protocol::SEND_EOF)) |
1482 | DBUG_RETURN(TRUE); |
1483 | |
1484 | protocol->prepare_for_resend(); |
1485 | protocol->store(orig_dbname->str, orig_dbname->length, system_charset_info); |
1486 | buffer.length(0); |
1487 | buffer.append(STRING_WITH_LEN("CREATE DATABASE " )); |
1488 | if (options.if_not_exists()) |
1489 | buffer.append(STRING_WITH_LEN("/*!32312 IF NOT EXISTS*/ " )); |
1490 | append_identifier(thd, &buffer, dbname); |
1491 | |
1492 | if (create.default_table_charset) |
1493 | { |
1494 | buffer.append(STRING_WITH_LEN(" /*!40100" )); |
1495 | buffer.append(STRING_WITH_LEN(" DEFAULT CHARACTER SET " )); |
1496 | buffer.append(create.default_table_charset->csname); |
1497 | if (!(create.default_table_charset->state & MY_CS_PRIMARY)) |
1498 | { |
1499 | buffer.append(STRING_WITH_LEN(" COLLATE " )); |
1500 | buffer.append(create.default_table_charset->name); |
1501 | } |
1502 | buffer.append(STRING_WITH_LEN(" */" )); |
1503 | } |
1504 | protocol->store(buffer.ptr(), buffer.length(), buffer.charset()); |
1505 | |
1506 | if (protocol->write()) |
1507 | DBUG_RETURN(TRUE); |
1508 | my_eof(thd); |
1509 | DBUG_RETURN(FALSE); |
1510 | } |
1511 | |
1512 | |
1513 | |
1514 | /**************************************************************************** |
1515 | Return only fields for API mysql_list_fields |
1516 | Use "show table wildcard" in mysql instead of this |
1517 | ****************************************************************************/ |
1518 | |
1519 | void |
1520 | mysqld_list_fields(THD *thd, TABLE_LIST *table_list, const char *wild) |
1521 | { |
1522 | TABLE *table; |
1523 | MEM_ROOT *mem_root= thd->mem_root; |
1524 | DBUG_ENTER("mysqld_list_fields" ); |
1525 | DBUG_PRINT("enter" ,("table: %s" , table_list->table_name.str)); |
1526 | |
1527 | if (open_normal_and_derived_tables(thd, table_list, |
1528 | MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL, |
1529 | DT_INIT | DT_PREPARE | DT_CREATE)) |
1530 | DBUG_VOID_RETURN; |
1531 | table= table_list->table; |
1532 | |
1533 | List<Item> field_list; |
1534 | |
1535 | Field **ptr,*field; |
1536 | for (ptr=table->field ; (field= *ptr); ptr++) |
1537 | { |
1538 | if (!wild || !wild[0] || |
1539 | !wild_case_compare(system_charset_info, field->field_name.str,wild)) |
1540 | { |
1541 | if (table_list->view) |
1542 | field_list.push_back(new (mem_root) |
1543 | Item_ident_for_show(thd, field, |
1544 | table_list->view_db.str, |
1545 | table_list->view_name.str), |
1546 | mem_root); |
1547 | else |
1548 | field_list.push_back(new (mem_root) Item_field(thd, field), mem_root); |
1549 | } |
1550 | } |
1551 | restore_record(table, s->default_values); // Get empty record |
1552 | table->use_all_columns(); |
1553 | if (thd->protocol->send_result_set_metadata(&field_list, |
1554 | Protocol::SEND_DEFAULTS)) |
1555 | DBUG_VOID_RETURN; |
1556 | my_eof(thd); |
1557 | DBUG_VOID_RETURN; |
1558 | } |
1559 | |
1560 | /* |
1561 | Go through all character combinations and ensure that sql_lex.cc can |
1562 | parse it as an identifier. |
1563 | |
1564 | SYNOPSIS |
1565 | require_quotes() |
1566 | name attribute name |
1567 | name_length length of name |
1568 | |
1569 | RETURN |
1570 | # Pointer to conflicting character |
1571 | 0 No conflicting character |
1572 | */ |
1573 | |
1574 | static const char *require_quotes(const char *name, uint name_length) |
1575 | { |
1576 | bool pure_digit= TRUE; |
1577 | const char *end= name + name_length; |
1578 | |
1579 | for (; name < end ; name++) |
1580 | { |
1581 | uchar chr= (uchar) *name; |
1582 | int length= my_charlen(system_charset_info, name, end); |
1583 | if (length == 1 && !system_charset_info->ident_map[chr]) |
1584 | return name; |
1585 | if (length == 1 && (chr < '0' || chr > '9')) |
1586 | pure_digit= FALSE; |
1587 | } |
1588 | if (pure_digit) |
1589 | return name; |
1590 | return 0; |
1591 | } |
1592 | |
1593 | |
1594 | /** |
1595 | Convert and quote the given identifier if needed and append it to the |
1596 | target string. If the given identifier is empty, it will be quoted. |
1597 | @thd thread handler |
1598 | @packet target string |
1599 | @name the identifier to be appended |
1600 | @length length of the appending identifier |
1601 | |
1602 | @return |
1603 | 0 success |
1604 | 1 error |
1605 | */ |
1606 | |
1607 | bool |
1608 | append_identifier(THD *thd, String *packet, const char *name, size_t length) |
1609 | { |
1610 | const char *name_end; |
1611 | char quote_char; |
1612 | int q= get_quote_char_for_identifier(thd, name, length); |
1613 | |
1614 | if (q == EOF) |
1615 | return packet->append(name, length, packet->charset()); |
1616 | |
1617 | /* |
1618 | The identifier must be quoted as it includes a quote character or |
1619 | it's a keyword |
1620 | */ |
1621 | |
1622 | /* |
1623 | Special code for swe7. It encodes the letter "E WITH ACUTE" on |
1624 | the position 0x60, where backtick normally resides. |
1625 | In swe7 we cannot append 0x60 using system_charset_info, |
1626 | because it cannot be converted to swe7 and will be replaced to |
1627 | question mark '?'. Use &my_charset_bin to avoid this. |
1628 | It will prevent conversion and will append the backtick as is. |
1629 | */ |
1630 | CHARSET_INFO *quote_charset= q == 0x60 && |
1631 | (packet->charset()->state & MY_CS_NONASCII) && |
1632 | packet->charset()->mbmaxlen == 1 ? |
1633 | &my_charset_bin : system_charset_info; |
1634 | |
1635 | (void) packet->reserve(length*2 + 2); |
1636 | quote_char= (char) q; |
1637 | if (packet->append("e_char, 1, quote_charset)) |
1638 | return true; |
1639 | |
1640 | for (name_end= name+length ; name < name_end ; ) |
1641 | { |
1642 | uchar chr= (uchar) *name; |
1643 | int char_length= my_charlen(system_charset_info, name, name_end); |
1644 | /* |
1645 | charlen can return 0 and negative numbers on a wrong multibyte |
1646 | sequence. It is possible when upgrading from 4.0, |
1647 | and identifier contains some accented characters. |
1648 | The manual says it does not work. So we'll just |
1649 | change char_length to 1 not to hang in the endless loop. |
1650 | */ |
1651 | if (char_length <= 0) |
1652 | char_length= 1; |
1653 | if (char_length == 1 && chr == (uchar) quote_char && |
1654 | packet->append("e_char, 1, quote_charset)) |
1655 | return true; |
1656 | if (packet->append(name, char_length, system_charset_info)) |
1657 | return true; |
1658 | name+= char_length; |
1659 | } |
1660 | return packet->append("e_char, 1, quote_charset); |
1661 | } |
1662 | |
1663 | |
1664 | /* |
1665 | Get the quote character for displaying an identifier. |
1666 | |
1667 | SYNOPSIS |
1668 | get_quote_char_for_identifier() |
1669 | thd Thread handler |
1670 | name name to quote |
1671 | length length of name |
1672 | |
1673 | IMPLEMENTATION |
1674 | Force quoting in the following cases: |
1675 | - name is empty (for one, it is possible when we use this function for |
1676 | quoting user and host names for DEFINER clause); |
1677 | - name is a keyword; |
1678 | - name includes a special character; |
1679 | Otherwise identifier is quoted only if the option OPTION_QUOTE_SHOW_CREATE |
1680 | is set. |
1681 | |
1682 | RETURN |
1683 | EOF No quote character is needed |
1684 | # Quote character |
1685 | */ |
1686 | |
1687 | int get_quote_char_for_identifier(THD *thd, const char *name, size_t length) |
1688 | { |
1689 | if (length && |
1690 | !is_keyword(name,(uint)length) && |
1691 | !require_quotes(name, (uint)length) && |
1692 | !(thd->variables.option_bits & OPTION_QUOTE_SHOW_CREATE)) |
1693 | return EOF; |
1694 | if (thd->variables.sql_mode & MODE_ANSI_QUOTES) |
1695 | return '"'; |
1696 | return '`'; |
1697 | } |
1698 | |
1699 | |
1700 | /* Append directory name (if exists) to CREATE INFO */ |
1701 | |
1702 | static void append_directory(THD *thd, String *packet, const char *dir_type, |
1703 | const char *filename) |
1704 | { |
1705 | if (filename && !(thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE)) |
1706 | { |
1707 | size_t length= dirname_length(filename); |
1708 | packet->append(' '); |
1709 | packet->append(dir_type); |
1710 | packet->append(STRING_WITH_LEN(" DIRECTORY='" )); |
1711 | #ifdef __WIN__ |
1712 | /* Convert \ to / to be able to create table on unix */ |
1713 | char *winfilename= (char*) thd->memdup(filename, length); |
1714 | char *pos, *end; |
1715 | for (pos= winfilename, end= pos+length ; pos < end ; pos++) |
1716 | { |
1717 | if (*pos == '\\') |
1718 | *pos = '/'; |
1719 | } |
1720 | filename= winfilename; |
1721 | #endif |
1722 | packet->append(filename, length); |
1723 | packet->append('\''); |
1724 | } |
1725 | } |
1726 | |
1727 | |
1728 | #define LIST_PROCESS_HOST_LEN 64 |
1729 | |
1730 | |
1731 | /** |
1732 | Print "ON UPDATE" clause of a field into a string. |
1733 | |
1734 | @param timestamp_field Pointer to timestamp field of a table. |
1735 | @param field The field to generate ON UPDATE clause for. |
1736 | @bool lcase Whether to print in lower case. |
1737 | @return false on success, true on error. |
1738 | */ |
1739 | static bool print_on_update_clause(Field *field, String *val, bool lcase) |
1740 | { |
1741 | DBUG_ASSERT(val->charset()->mbminlen == 1); |
1742 | val->length(0); |
1743 | if (field->has_update_default_function()) |
1744 | { |
1745 | if (lcase) |
1746 | val->append(STRING_WITH_LEN("on update " )); |
1747 | else |
1748 | val->append(STRING_WITH_LEN("ON UPDATE " )); |
1749 | val->append(STRING_WITH_LEN("current_timestamp" )); |
1750 | if (field->decimals() > 0) |
1751 | val->append_parenthesized(field->decimals()); |
1752 | else |
1753 | val->append(STRING_WITH_LEN("()" )); |
1754 | return true; |
1755 | } |
1756 | return false; |
1757 | } |
1758 | |
1759 | |
1760 | static bool get_field_default_value(THD *thd, Field *field, String *def_value, |
1761 | bool quoted) |
1762 | { |
1763 | bool has_default; |
1764 | enum enum_field_types field_type= field->type(); |
1765 | |
1766 | has_default= (field->default_value || |
1767 | (!(field->flags & NO_DEFAULT_VALUE_FLAG) && |
1768 | !field->vers_sys_field() && |
1769 | field->unireg_check != Field::NEXT_NUMBER)); |
1770 | |
1771 | def_value->length(0); |
1772 | if (has_default) |
1773 | { |
1774 | StringBuffer<MAX_FIELD_WIDTH> str(field->charset()); |
1775 | if (field->default_value) |
1776 | { |
1777 | field->default_value->print(&str); |
1778 | if (field->default_value->expr->need_parentheses_in_default()) |
1779 | { |
1780 | def_value->set_charset(&my_charset_utf8mb4_general_ci); |
1781 | def_value->append('('); |
1782 | def_value->append(str); |
1783 | def_value->append(')'); |
1784 | } |
1785 | else |
1786 | def_value->append(str); |
1787 | } |
1788 | else if (!field->is_null()) |
1789 | { // Not null by default |
1790 | if (field_type == MYSQL_TYPE_BIT) |
1791 | { |
1792 | str.qs_append('b'); |
1793 | str.qs_append('\''); |
1794 | str.qs_append(field->val_int(), 2); |
1795 | str.qs_append('\''); |
1796 | quoted= 0; |
1797 | } |
1798 | else |
1799 | { |
1800 | field->val_str(&str); |
1801 | if (!field->str_needs_quotes()) |
1802 | quoted= 0; |
1803 | } |
1804 | if (str.length()) |
1805 | { |
1806 | StringBuffer<MAX_FIELD_WIDTH> def_val; |
1807 | uint dummy_errors; |
1808 | /* convert to system_charset_info == utf8 */ |
1809 | def_val.copy(str.ptr(), str.length(), field->charset(), |
1810 | system_charset_info, &dummy_errors); |
1811 | if (quoted) |
1812 | append_unescaped(def_value, def_val.ptr(), def_val.length()); |
1813 | else |
1814 | def_value->append(def_val); |
1815 | } |
1816 | else if (quoted) |
1817 | def_value->set(STRING_WITH_LEN("''" ), system_charset_info); |
1818 | } |
1819 | else if (field->maybe_null() && quoted) |
1820 | def_value->set(STRING_WITH_LEN("NULL" ), system_charset_info); // Null as default |
1821 | else |
1822 | return 0; |
1823 | |
1824 | } |
1825 | return has_default; |
1826 | } |
1827 | |
1828 | |
1829 | /** |
1830 | Appends list of options to string |
1831 | |
1832 | @param thd thread handler |
1833 | @param packet string to append |
1834 | @param opt list of options |
1835 | @param check_options only print known options |
1836 | @param rules list of known options |
1837 | */ |
1838 | |
1839 | static void append_create_options(THD *thd, String *packet, |
1840 | engine_option_value *opt, |
1841 | bool check_options, |
1842 | ha_create_table_option *rules) |
1843 | { |
1844 | bool = false; |
1845 | for(; opt; opt= opt->next) |
1846 | { |
1847 | if (check_options) |
1848 | { |
1849 | if (is_engine_option_known(opt, rules)) |
1850 | { |
1851 | if (in_comment) |
1852 | packet->append(STRING_WITH_LEN(" */" )); |
1853 | in_comment= false; |
1854 | } |
1855 | else |
1856 | { |
1857 | if (!in_comment) |
1858 | packet->append(STRING_WITH_LEN(" /*" )); |
1859 | in_comment= true; |
1860 | } |
1861 | } |
1862 | |
1863 | DBUG_ASSERT(opt->value.str); |
1864 | packet->append(' '); |
1865 | append_identifier(thd, packet, &opt->name); |
1866 | packet->append('='); |
1867 | if (opt->quoted_value) |
1868 | append_unescaped(packet, opt->value.str, opt->value.length); |
1869 | else |
1870 | packet->append(&opt->value); |
1871 | } |
1872 | if (in_comment) |
1873 | packet->append(STRING_WITH_LEN(" */" )); |
1874 | } |
1875 | |
1876 | /** |
1877 | Add table options to end of CREATE statement |
1878 | |
1879 | @param schema_table 1 if schema table |
1880 | @param sequence 1 if sequence. If sequence, we flush out options |
1881 | not relevant for sequences. |
1882 | */ |
1883 | |
1884 | static void add_table_options(THD *thd, TABLE *table, |
1885 | Table_specification_st *create_info_arg, |
1886 | bool schema_table, bool sequence, |
1887 | String *packet) |
1888 | { |
1889 | sql_mode_t sql_mode= thd->variables.sql_mode; |
1890 | TABLE_SHARE *share= table->s; |
1891 | handlerton *hton; |
1892 | HA_CREATE_INFO create_info; |
1893 | bool check_options= (!(sql_mode & MODE_IGNORE_BAD_TABLE_OPTIONS) && |
1894 | !create_info_arg); |
1895 | |
1896 | #ifdef WITH_PARTITION_STORAGE_ENGINE |
1897 | if (table->part_info) |
1898 | hton= table->part_info->default_engine_type; |
1899 | else |
1900 | #endif |
1901 | hton= table->file->ht; |
1902 | |
1903 | bzero((char*) &create_info, sizeof(create_info)); |
1904 | /* Allow update_create_info to update row type, page checksums and options */ |
1905 | create_info.row_type= share->row_type; |
1906 | create_info.page_checksum= share->page_checksum; |
1907 | create_info.options= share->db_create_options; |
1908 | table->file->update_create_info(&create_info); |
1909 | |
1910 | /* |
1911 | IF check_create_info |
1912 | THEN add ENGINE only if it was used when creating the table |
1913 | */ |
1914 | if (!create_info_arg || |
1915 | (create_info_arg->used_fields & HA_CREATE_USED_ENGINE)) |
1916 | { |
1917 | LEX_CSTRING *engine_name= table->file->engine_name(); |
1918 | |
1919 | if (sql_mode & (MODE_MYSQL323 | MODE_MYSQL40)) |
1920 | packet->append(STRING_WITH_LEN(" TYPE=" )); |
1921 | else |
1922 | packet->append(STRING_WITH_LEN(" ENGINE=" )); |
1923 | |
1924 | packet->append(engine_name->str, engine_name->length); |
1925 | } |
1926 | |
1927 | if (sequence) |
1928 | goto end_options; |
1929 | |
1930 | /* |
1931 | Add AUTO_INCREMENT=... if there is an AUTO_INCREMENT column, |
1932 | and NEXT_ID > 1 (the default). We must not print the clause |
1933 | for engines that do not support this as it would break the |
1934 | import of dumps, but as of this writing, the test for whether |
1935 | AUTO_INCREMENT columns are allowed and wether AUTO_INCREMENT=... |
1936 | is supported is identical, !(file->table_flags() & HA_NO_AUTO_INCREMENT)) |
1937 | Because of that, we do not explicitly test for the feature, |
1938 | but may extrapolate its existence from that of an AUTO_INCREMENT column. |
1939 | */ |
1940 | |
1941 | if (create_info.auto_increment_value > 1) |
1942 | { |
1943 | packet->append(STRING_WITH_LEN(" AUTO_INCREMENT=" )); |
1944 | packet->append_ulonglong(create_info.auto_increment_value); |
1945 | } |
1946 | |
1947 | if (share->table_charset && !(sql_mode & (MODE_MYSQL323 | MODE_MYSQL40)) && |
1948 | share->table_type != TABLE_TYPE_SEQUENCE) |
1949 | { |
1950 | /* |
1951 | IF check_create_info |
1952 | THEN add DEFAULT CHARSET only if it was used when creating the table |
1953 | */ |
1954 | if (!create_info_arg || |
1955 | (create_info_arg->used_fields & HA_CREATE_USED_DEFAULT_CHARSET)) |
1956 | { |
1957 | packet->append(STRING_WITH_LEN(" DEFAULT CHARSET=" )); |
1958 | packet->append(share->table_charset->csname); |
1959 | if (!(share->table_charset->state & MY_CS_PRIMARY)) |
1960 | { |
1961 | packet->append(STRING_WITH_LEN(" COLLATE=" )); |
1962 | packet->append(table->s->table_charset->name); |
1963 | } |
1964 | } |
1965 | } |
1966 | |
1967 | if (share->min_rows) |
1968 | { |
1969 | packet->append(STRING_WITH_LEN(" MIN_ROWS=" )); |
1970 | packet->append_ulonglong(share->min_rows); |
1971 | } |
1972 | |
1973 | if (share->max_rows && !schema_table && !sequence) |
1974 | { |
1975 | packet->append(STRING_WITH_LEN(" MAX_ROWS=" )); |
1976 | packet->append_ulonglong(share->max_rows); |
1977 | } |
1978 | |
1979 | if (share->avg_row_length) |
1980 | { |
1981 | packet->append(STRING_WITH_LEN(" AVG_ROW_LENGTH=" )); |
1982 | packet->append_ulonglong(share->avg_row_length); |
1983 | } |
1984 | |
1985 | if (create_info.options & HA_OPTION_PACK_KEYS) |
1986 | packet->append(STRING_WITH_LEN(" PACK_KEYS=1" )); |
1987 | if (create_info.options & HA_OPTION_NO_PACK_KEYS) |
1988 | packet->append(STRING_WITH_LEN(" PACK_KEYS=0" )); |
1989 | if (share->db_create_options & HA_OPTION_STATS_PERSISTENT) |
1990 | packet->append(STRING_WITH_LEN(" STATS_PERSISTENT=1" )); |
1991 | if (share->db_create_options & HA_OPTION_NO_STATS_PERSISTENT) |
1992 | packet->append(STRING_WITH_LEN(" STATS_PERSISTENT=0" )); |
1993 | if (share->stats_auto_recalc == HA_STATS_AUTO_RECALC_ON) |
1994 | packet->append(STRING_WITH_LEN(" STATS_AUTO_RECALC=1" )); |
1995 | else if (share->stats_auto_recalc == HA_STATS_AUTO_RECALC_OFF) |
1996 | packet->append(STRING_WITH_LEN(" STATS_AUTO_RECALC=0" )); |
1997 | if (share->stats_sample_pages != 0) |
1998 | { |
1999 | packet->append(STRING_WITH_LEN(" STATS_SAMPLE_PAGES=" )); |
2000 | packet->append_ulonglong(share->stats_sample_pages); |
2001 | } |
2002 | |
2003 | /* We use CHECKSUM, instead of TABLE_CHECKSUM, for backward compability */ |
2004 | if (create_info.options & HA_OPTION_CHECKSUM) |
2005 | packet->append(STRING_WITH_LEN(" CHECKSUM=1" )); |
2006 | if (create_info.page_checksum != HA_CHOICE_UNDEF) |
2007 | { |
2008 | packet->append(STRING_WITH_LEN(" PAGE_CHECKSUM=" )); |
2009 | packet->append(ha_choice_values[create_info.page_checksum], 1); |
2010 | } |
2011 | if (create_info.options & HA_OPTION_DELAY_KEY_WRITE) |
2012 | packet->append(STRING_WITH_LEN(" DELAY_KEY_WRITE=1" )); |
2013 | if (create_info.row_type != ROW_TYPE_DEFAULT) |
2014 | { |
2015 | packet->append(STRING_WITH_LEN(" ROW_FORMAT=" )); |
2016 | packet->append(ha_row_type[(uint) create_info.row_type]); |
2017 | } |
2018 | if (share->transactional != HA_CHOICE_UNDEF) |
2019 | { |
2020 | packet->append(STRING_WITH_LEN(" TRANSACTIONAL=" )); |
2021 | packet->append(ha_choice_values[(uint) share->transactional], 1); |
2022 | } |
2023 | if (share->table_type == TABLE_TYPE_SEQUENCE) |
2024 | packet->append(STRING_WITH_LEN(" SEQUENCE=1" )); |
2025 | if (table->s->key_block_size) |
2026 | { |
2027 | packet->append(STRING_WITH_LEN(" KEY_BLOCK_SIZE=" )); |
2028 | packet->append_ulonglong(table->s->key_block_size); |
2029 | } |
2030 | table->file->append_create_info(packet); |
2031 | |
2032 | end_options: |
2033 | if (share->comment.length) |
2034 | { |
2035 | packet->append(STRING_WITH_LEN(" COMMENT=" )); |
2036 | append_unescaped(packet, share->comment.str, share->comment.length); |
2037 | } |
2038 | if (share->connect_string.length) |
2039 | { |
2040 | packet->append(STRING_WITH_LEN(" CONNECTION=" )); |
2041 | append_unescaped(packet, share->connect_string.str, share->connect_string.length); |
2042 | } |
2043 | append_create_options(thd, packet, share->option_list, check_options, |
2044 | hton->table_options); |
2045 | append_directory(thd, packet, "DATA" , create_info.data_file_name); |
2046 | append_directory(thd, packet, "INDEX" , create_info.index_file_name); |
2047 | } |
2048 | |
2049 | /* |
2050 | Build a CREATE TABLE statement for a table. |
2051 | |
2052 | SYNOPSIS |
2053 | show_create_table() |
2054 | thd The thread |
2055 | table_list A list containing one table to write statement |
2056 | for. |
2057 | packet Pointer to a string where statement will be |
2058 | written. |
2059 | create_info_arg Pointer to create information that can be used |
2060 | to tailor the format of the statement. Can be |
2061 | NULL, in which case only SQL_MODE is considered |
2062 | when building the statement. |
2063 | with_db_name Add database name to table name |
2064 | |
2065 | NOTE |
2066 | Currently always return 0, but might return error code in the |
2067 | future. |
2068 | |
2069 | RETURN |
2070 | 0 OK |
2071 | */ |
2072 | |
2073 | int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, |
2074 | Table_specification_st *create_info_arg, |
2075 | enum_with_db_name with_db_name) |
2076 | { |
2077 | List<Item> field_list; |
2078 | char tmp[MAX_FIELD_WIDTH], *for_str, def_value_buf[MAX_FIELD_WIDTH]; |
2079 | LEX_CSTRING alias; |
2080 | String type; |
2081 | String def_value; |
2082 | Field **ptr,*field; |
2083 | uint primary_key; |
2084 | KEY *key_info; |
2085 | TABLE *table= table_list->table; |
2086 | TABLE_SHARE *share= table->s; |
2087 | sql_mode_t sql_mode= thd->variables.sql_mode; |
2088 | bool explicit_fields= false; |
2089 | bool foreign_db_mode= sql_mode & (MODE_POSTGRESQL | MODE_ORACLE | |
2090 | MODE_MSSQL | MODE_DB2 | |
2091 | MODE_MAXDB | MODE_ANSI); |
2092 | bool limited_mysql_mode= sql_mode & (MODE_NO_FIELD_OPTIONS | MODE_MYSQL323 | |
2093 | MODE_MYSQL40); |
2094 | bool show_table_options= !(sql_mode & MODE_NO_TABLE_OPTIONS) && |
2095 | !foreign_db_mode; |
2096 | bool check_options= !(sql_mode & MODE_IGNORE_BAD_TABLE_OPTIONS) && |
2097 | !create_info_arg; |
2098 | my_bitmap_map *old_map; |
2099 | handlerton *hton; |
2100 | int error= 0; |
2101 | DBUG_ENTER("show_create_table" ); |
2102 | DBUG_PRINT("enter" ,("table: %s" , table->s->table_name.str)); |
2103 | |
2104 | #ifdef WITH_PARTITION_STORAGE_ENGINE |
2105 | if (table->part_info) |
2106 | hton= table->part_info->default_engine_type; |
2107 | else |
2108 | #endif |
2109 | hton= table->file->ht; |
2110 | |
2111 | restore_record(table, s->default_values); // Get empty record |
2112 | |
2113 | packet->append(STRING_WITH_LEN("CREATE " )); |
2114 | if (create_info_arg && |
2115 | ((create_info_arg->or_replace() && |
2116 | !create_info_arg->or_replace_slave_generated()) || |
2117 | create_info_arg->table_was_deleted)) |
2118 | packet->append(STRING_WITH_LEN("OR REPLACE " )); |
2119 | if (share->tmp_table) |
2120 | packet->append(STRING_WITH_LEN("TEMPORARY " )); |
2121 | packet->append(STRING_WITH_LEN("TABLE " )); |
2122 | if (create_info_arg && create_info_arg->if_not_exists()) |
2123 | packet->append(STRING_WITH_LEN("IF NOT EXISTS " )); |
2124 | if (table_list->schema_table) |
2125 | { |
2126 | alias.str= table_list->schema_table->table_name; |
2127 | alias.length= strlen(alias.str); |
2128 | } |
2129 | else |
2130 | { |
2131 | if (lower_case_table_names == 2) |
2132 | { |
2133 | alias.str= table->alias.c_ptr(); |
2134 | alias.length= table->alias.length(); |
2135 | } |
2136 | else |
2137 | alias= share->table_name; |
2138 | } |
2139 | |
2140 | /* |
2141 | Print the database before the table name if told to do that. The |
2142 | database name is only printed in the event that it is different |
2143 | from the current database. The main reason for doing this is to |
2144 | avoid having to update gazillions of tests and result files, but |
2145 | it also saves a few bytes of the binary log. |
2146 | */ |
2147 | if (with_db_name == WITH_DB_NAME) |
2148 | { |
2149 | const LEX_CSTRING *const db= |
2150 | table_list->schema_table ? &INFORMATION_SCHEMA_NAME : &table->s->db; |
2151 | if (!thd->db.str || cmp(db, &thd->db)) |
2152 | { |
2153 | append_identifier(thd, packet, db); |
2154 | packet->append(STRING_WITH_LEN("." )); |
2155 | } |
2156 | } |
2157 | |
2158 | append_identifier(thd, packet, &alias); |
2159 | packet->append(STRING_WITH_LEN(" (\n" )); |
2160 | /* |
2161 | We need this to get default values from the table |
2162 | We have to restore the read_set if we are called from insert in case |
2163 | of row based replication. |
2164 | */ |
2165 | old_map= tmp_use_all_columns(table, table->read_set); |
2166 | |
2167 | bool not_the_first_field= false; |
2168 | for (ptr=table->field ; (field= *ptr); ptr++) |
2169 | { |
2170 | |
2171 | uint flags = field->flags; |
2172 | |
2173 | if (field->invisible > INVISIBLE_USER) |
2174 | continue; |
2175 | if (not_the_first_field) |
2176 | packet->append(STRING_WITH_LEN(",\n" )); |
2177 | |
2178 | not_the_first_field= true; |
2179 | packet->append(STRING_WITH_LEN(" " )); |
2180 | append_identifier(thd, packet, &field->field_name); |
2181 | packet->append(' '); |
2182 | |
2183 | type.set(tmp, sizeof(tmp), system_charset_info); |
2184 | field->sql_type(type); |
2185 | packet->append(type.ptr(), type.length(), system_charset_info); |
2186 | |
2187 | if (field->has_charset() && !(sql_mode & (MODE_MYSQL323 | MODE_MYSQL40))) |
2188 | { |
2189 | if (field->charset() != share->table_charset) |
2190 | { |
2191 | packet->append(STRING_WITH_LEN(" CHARACTER SET " )); |
2192 | packet->append(field->charset()->csname); |
2193 | } |
2194 | /* |
2195 | For string types dump collation name only if |
2196 | collation is not primary for the given charset |
2197 | */ |
2198 | if (!(field->charset()->state & MY_CS_PRIMARY) && !field->vcol_info) |
2199 | { |
2200 | packet->append(STRING_WITH_LEN(" COLLATE " )); |
2201 | packet->append(field->charset()->name); |
2202 | } |
2203 | } |
2204 | |
2205 | if (field->vcol_info) |
2206 | { |
2207 | StringBuffer<MAX_FIELD_WIDTH> str(&my_charset_utf8mb4_general_ci); |
2208 | field->vcol_info->print(&str); |
2209 | packet->append(STRING_WITH_LEN(" GENERATED ALWAYS AS (" )); |
2210 | packet->append(str); |
2211 | packet->append(STRING_WITH_LEN(")" )); |
2212 | if (field->vcol_info->stored_in_db) |
2213 | packet->append(STRING_WITH_LEN(" STORED" )); |
2214 | else |
2215 | packet->append(STRING_WITH_LEN(" VIRTUAL" )); |
2216 | } |
2217 | else |
2218 | { |
2219 | if (field->flags & VERS_SYS_START_FLAG) |
2220 | { |
2221 | packet->append(STRING_WITH_LEN(" GENERATED ALWAYS AS ROW START" )); |
2222 | } |
2223 | else if (field->flags & VERS_SYS_END_FLAG) |
2224 | { |
2225 | packet->append(STRING_WITH_LEN(" GENERATED ALWAYS AS ROW END" )); |
2226 | } |
2227 | else if (flags & NOT_NULL_FLAG) |
2228 | packet->append(STRING_WITH_LEN(" NOT NULL" )); |
2229 | else if (field->type() == MYSQL_TYPE_TIMESTAMP) |
2230 | { |
2231 | /* |
2232 | TIMESTAMP field require explicit NULL flag, because unlike |
2233 | all other fields they are treated as NOT NULL by default. |
2234 | */ |
2235 | packet->append(STRING_WITH_LEN(" NULL" )); |
2236 | } |
2237 | |
2238 | if (field->invisible == INVISIBLE_USER) |
2239 | { |
2240 | packet->append(STRING_WITH_LEN(" INVISIBLE" )); |
2241 | } |
2242 | def_value.set(def_value_buf, sizeof(def_value_buf), system_charset_info); |
2243 | if (get_field_default_value(thd, field, &def_value, 1)) |
2244 | { |
2245 | packet->append(STRING_WITH_LEN(" DEFAULT " )); |
2246 | packet->append(def_value.ptr(), def_value.length(), system_charset_info); |
2247 | } |
2248 | |
2249 | if (field->vers_update_unversioned()) |
2250 | { |
2251 | packet->append(STRING_WITH_LEN(" WITHOUT SYSTEM VERSIONING" )); |
2252 | } |
2253 | |
2254 | if (!limited_mysql_mode && |
2255 | print_on_update_clause(field, &def_value, false)) |
2256 | { |
2257 | packet->append(STRING_WITH_LEN(" " )); |
2258 | packet->append(def_value); |
2259 | } |
2260 | |
2261 | if (field->unireg_check == Field::NEXT_NUMBER && |
2262 | !(sql_mode & MODE_NO_FIELD_OPTIONS)) |
2263 | packet->append(STRING_WITH_LEN(" AUTO_INCREMENT" )); |
2264 | } |
2265 | if (field->check_constraint) |
2266 | { |
2267 | StringBuffer<MAX_FIELD_WIDTH> str(&my_charset_utf8mb4_general_ci); |
2268 | field->check_constraint->print(&str); |
2269 | packet->append(STRING_WITH_LEN(" CHECK (" )); |
2270 | packet->append(str); |
2271 | packet->append(STRING_WITH_LEN(")" )); |
2272 | } |
2273 | |
2274 | if (field->comment.length) |
2275 | { |
2276 | packet->append(STRING_WITH_LEN(" COMMENT " )); |
2277 | append_unescaped(packet, field->comment.str, field->comment.length); |
2278 | } |
2279 | append_create_options(thd, packet, field->option_list, check_options, |
2280 | hton->field_options); |
2281 | } |
2282 | |
2283 | key_info= table->key_info; |
2284 | primary_key= share->primary_key; |
2285 | |
2286 | for (uint i=0 ; i < share->keys ; i++,key_info++) |
2287 | { |
2288 | if (key_info->flags & HA_INVISIBLE_KEY) |
2289 | continue; |
2290 | KEY_PART_INFO *key_part= key_info->key_part; |
2291 | bool found_primary=0; |
2292 | packet->append(STRING_WITH_LEN(",\n " )); |
2293 | |
2294 | if (i == primary_key && !strcmp(key_info->name.str, primary_key_name)) |
2295 | { |
2296 | found_primary=1; |
2297 | /* |
2298 | No space at end, because a space will be added after where the |
2299 | identifier would go, but that is not added for primary key. |
2300 | */ |
2301 | packet->append(STRING_WITH_LEN("PRIMARY KEY" )); |
2302 | } |
2303 | else if (key_info->flags & HA_NOSAME) |
2304 | packet->append(STRING_WITH_LEN("UNIQUE KEY " )); |
2305 | else if (key_info->flags & HA_FULLTEXT) |
2306 | packet->append(STRING_WITH_LEN("FULLTEXT KEY " )); |
2307 | else if (key_info->flags & HA_SPATIAL) |
2308 | packet->append(STRING_WITH_LEN("SPATIAL KEY " )); |
2309 | else |
2310 | packet->append(STRING_WITH_LEN("KEY " )); |
2311 | |
2312 | if (!found_primary) |
2313 | append_identifier(thd, packet, &key_info->name); |
2314 | |
2315 | packet->append(STRING_WITH_LEN(" (" )); |
2316 | |
2317 | for (uint j=0 ; j < key_info->user_defined_key_parts ; j++,key_part++) |
2318 | { |
2319 | Field *field= key_part->field; |
2320 | if (field->invisible > INVISIBLE_USER) |
2321 | continue; |
2322 | |
2323 | if (j) |
2324 | packet->append(','); |
2325 | |
2326 | if (key_part->field) |
2327 | append_identifier(thd, packet, &key_part->field->field_name); |
2328 | if (key_part->field && |
2329 | (key_part->length != |
2330 | table->field[key_part->fieldnr-1]->key_length() && |
2331 | !(key_info->flags & (HA_FULLTEXT | HA_SPATIAL)))) |
2332 | { |
2333 | packet->append_parenthesized((long) key_part->length / |
2334 | key_part->field->charset()->mbmaxlen); |
2335 | } |
2336 | } |
2337 | packet->append(')'); |
2338 | store_key_options(thd, packet, table, key_info); |
2339 | if (key_info->parser) |
2340 | { |
2341 | LEX_CSTRING *parser_name= plugin_name(key_info->parser); |
2342 | packet->append(STRING_WITH_LEN(" /*!50100 WITH PARSER " )); |
2343 | append_identifier(thd, packet, parser_name); |
2344 | packet->append(STRING_WITH_LEN(" */ " )); |
2345 | } |
2346 | append_create_options(thd, packet, key_info->option_list, check_options, |
2347 | hton->index_options); |
2348 | } |
2349 | |
2350 | if (table->versioned()) |
2351 | { |
2352 | const Field *fs = table->vers_start_field(); |
2353 | const Field *fe = table->vers_end_field(); |
2354 | DBUG_ASSERT(fs); |
2355 | DBUG_ASSERT(fe); |
2356 | explicit_fields= fs->invisible < INVISIBLE_SYSTEM; |
2357 | DBUG_ASSERT(!explicit_fields || fe->invisible < INVISIBLE_SYSTEM); |
2358 | if (explicit_fields) |
2359 | { |
2360 | packet->append(STRING_WITH_LEN(",\n PERIOD FOR SYSTEM_TIME (" )); |
2361 | append_identifier(thd,packet,fs->field_name.str, fs->field_name.length); |
2362 | packet->append(STRING_WITH_LEN(", " )); |
2363 | append_identifier(thd,packet,fe->field_name.str, fe->field_name.length); |
2364 | packet->append(STRING_WITH_LEN(")" )); |
2365 | } |
2366 | else |
2367 | { |
2368 | DBUG_ASSERT(fs->invisible == INVISIBLE_SYSTEM); |
2369 | DBUG_ASSERT(fe->invisible == INVISIBLE_SYSTEM); |
2370 | } |
2371 | } |
2372 | |
2373 | /* |
2374 | Get possible foreign key definitions stored in InnoDB and append them |
2375 | to the CREATE TABLE statement |
2376 | */ |
2377 | |
2378 | if ((for_str= table->file->get_foreign_key_create_info())) |
2379 | { |
2380 | packet->append(for_str, strlen(for_str)); |
2381 | table->file->free_foreign_key_create_info(for_str); |
2382 | } |
2383 | |
2384 | /* Add table level check constraints */ |
2385 | if (share->table_check_constraints) |
2386 | { |
2387 | for (uint i= share->field_check_constraints; |
2388 | i < share->table_check_constraints ; i++) |
2389 | { |
2390 | StringBuffer<MAX_FIELD_WIDTH> str(&my_charset_utf8mb4_general_ci); |
2391 | Virtual_column_info *check= table->check_constraints[i]; |
2392 | check->print(&str); |
2393 | |
2394 | packet->append(STRING_WITH_LEN(",\n " )); |
2395 | if (check->name.str) |
2396 | { |
2397 | packet->append(STRING_WITH_LEN("CONSTRAINT " )); |
2398 | append_identifier(thd, packet, &check->name); |
2399 | } |
2400 | packet->append(STRING_WITH_LEN(" CHECK (" )); |
2401 | packet->append(str); |
2402 | packet->append(STRING_WITH_LEN(")" )); |
2403 | } |
2404 | } |
2405 | |
2406 | packet->append(STRING_WITH_LEN("\n)" )); |
2407 | if (show_table_options) |
2408 | add_table_options(thd, table, create_info_arg, |
2409 | table_list->schema_table != 0, 0, packet); |
2410 | |
2411 | if (table->versioned()) |
2412 | packet->append(STRING_WITH_LEN(" WITH SYSTEM VERSIONING" )); |
2413 | |
2414 | #ifdef WITH_PARTITION_STORAGE_ENGINE |
2415 | { |
2416 | if (table->part_info && |
2417 | !((table->s->db_type()->partition_flags() & HA_USE_AUTO_PARTITION) && |
2418 | table->part_info->is_auto_partitioned)) |
2419 | { |
2420 | /* |
2421 | Partition syntax for CREATE TABLE is at the end of the syntax. |
2422 | */ |
2423 | uint part_syntax_len; |
2424 | char *part_syntax; |
2425 | if ((part_syntax= generate_partition_syntax(thd, table->part_info, |
2426 | &part_syntax_len, |
2427 | show_table_options, |
2428 | NULL, NULL))) |
2429 | { |
2430 | packet->append('\n'); |
2431 | if (packet->append(part_syntax, part_syntax_len)) |
2432 | error= 1; |
2433 | } |
2434 | } |
2435 | } |
2436 | #endif |
2437 | tmp_restore_column_map(table->read_set, old_map); |
2438 | DBUG_RETURN(error); |
2439 | } |
2440 | |
2441 | |
2442 | static void store_key_options(THD *thd, String *packet, TABLE *table, |
2443 | KEY *key_info) |
2444 | { |
2445 | bool limited_mysql_mode= (thd->variables.sql_mode & |
2446 | (MODE_NO_FIELD_OPTIONS | MODE_MYSQL323 | |
2447 | MODE_MYSQL40)) != 0; |
2448 | bool foreign_db_mode= (thd->variables.sql_mode & (MODE_POSTGRESQL | |
2449 | MODE_ORACLE | |
2450 | MODE_MSSQL | |
2451 | MODE_DB2 | |
2452 | MODE_MAXDB | |
2453 | MODE_ANSI)) != 0; |
2454 | char *end, buff[32]; |
2455 | |
2456 | if (!(thd->variables.sql_mode & MODE_NO_KEY_OPTIONS) && |
2457 | !limited_mysql_mode && !foreign_db_mode) |
2458 | { |
2459 | |
2460 | if (key_info->algorithm == HA_KEY_ALG_BTREE) |
2461 | packet->append(STRING_WITH_LEN(" USING BTREE" )); |
2462 | |
2463 | if (key_info->algorithm == HA_KEY_ALG_HASH) |
2464 | packet->append(STRING_WITH_LEN(" USING HASH" )); |
2465 | |
2466 | /* send USING only in non-default case: non-spatial rtree */ |
2467 | if ((key_info->algorithm == HA_KEY_ALG_RTREE) && |
2468 | !(key_info->flags & HA_SPATIAL)) |
2469 | packet->append(STRING_WITH_LEN(" USING RTREE" )); |
2470 | |
2471 | if ((key_info->flags & HA_USES_BLOCK_SIZE) && |
2472 | table->s->key_block_size != key_info->block_size) |
2473 | { |
2474 | packet->append(STRING_WITH_LEN(" KEY_BLOCK_SIZE=" )); |
2475 | end= longlong10_to_str(key_info->block_size, buff, 10); |
2476 | packet->append(buff, (uint) (end - buff)); |
2477 | } |
2478 | DBUG_ASSERT(MY_TEST(key_info->flags & HA_USES_COMMENT) == |
2479 | (key_info->comment.length > 0)); |
2480 | if (key_info->flags & HA_USES_COMMENT) |
2481 | { |
2482 | packet->append(STRING_WITH_LEN(" COMMENT " )); |
2483 | append_unescaped(packet, key_info->comment.str, |
2484 | key_info->comment.length); |
2485 | } |
2486 | } |
2487 | } |
2488 | |
2489 | |
2490 | void view_store_options(THD *thd, TABLE_LIST *table, String *buff) |
2491 | { |
2492 | if (table->algorithm != VIEW_ALGORITHM_INHERIT) |
2493 | { |
2494 | buff->append(STRING_WITH_LEN("ALGORITHM=" )); |
2495 | buff->append(view_algorithm(table)); |
2496 | } |
2497 | buff->append(' '); |
2498 | append_definer(thd, buff, &table->definer.user, &table->definer.host); |
2499 | if (table->view_suid) |
2500 | buff->append(STRING_WITH_LEN("SQL SECURITY DEFINER " )); |
2501 | else |
2502 | buff->append(STRING_WITH_LEN("SQL SECURITY INVOKER " )); |
2503 | } |
2504 | |
2505 | |
2506 | /** |
2507 | Returns ALGORITHM clause of a view |
2508 | */ |
2509 | |
2510 | static const LEX_CSTRING *view_algorithm(TABLE_LIST *table) |
2511 | { |
2512 | static const LEX_CSTRING undefined= { STRING_WITH_LEN("UNDEFINED" ) }; |
2513 | static const LEX_CSTRING merge= { STRING_WITH_LEN("MERGE" ) }; |
2514 | static const LEX_CSTRING temptable= { STRING_WITH_LEN("TEMPTABLE" ) }; |
2515 | switch (table->algorithm) { |
2516 | case VIEW_ALGORITHM_TMPTABLE: |
2517 | return &temptable; |
2518 | case VIEW_ALGORITHM_MERGE: |
2519 | return &merge; |
2520 | default: |
2521 | DBUG_ASSERT(0); // never should happen |
2522 | case VIEW_ALGORITHM_UNDEFINED: |
2523 | return &undefined; |
2524 | } |
2525 | } |
2526 | |
2527 | |
2528 | static bool append_at_host(THD *thd, String *buffer, const LEX_CSTRING *host) |
2529 | { |
2530 | if (!host->str || !host->str[0]) |
2531 | return false; |
2532 | return |
2533 | buffer->append('@') || |
2534 | append_identifier(thd, buffer, host); |
2535 | } |
2536 | |
2537 | |
2538 | /* |
2539 | Append DEFINER clause to the given buffer. |
2540 | |
2541 | SYNOPSIS |
2542 | append_definer() |
2543 | thd [in] thread handle |
2544 | buffer [inout] buffer to hold DEFINER clause |
2545 | definer_user [in] user name part of definer |
2546 | definer_host [in] host name part of definer |
2547 | */ |
2548 | |
2549 | bool append_definer(THD *thd, String *buffer, const LEX_CSTRING *definer_user, |
2550 | const LEX_CSTRING *definer_host) |
2551 | { |
2552 | return |
2553 | buffer->append(STRING_WITH_LEN("DEFINER=" )) || |
2554 | append_identifier(thd, buffer, definer_user) || |
2555 | append_at_host(thd, buffer, definer_host) || |
2556 | buffer->append(' '); |
2557 | } |
2558 | |
2559 | |
2560 | static int show_create_view(THD *thd, TABLE_LIST *table, String *buff) |
2561 | { |
2562 | my_bool compact_view_name= TRUE; |
2563 | my_bool foreign_db_mode= (thd->variables.sql_mode & (MODE_POSTGRESQL | |
2564 | MODE_ORACLE | |
2565 | MODE_MSSQL | |
2566 | MODE_DB2 | |
2567 | MODE_MAXDB | |
2568 | MODE_ANSI)) != 0; |
2569 | |
2570 | if (!thd->db.str || cmp(&thd->db, &table->view_db)) |
2571 | /* |
2572 | print compact view name if the view belongs to the current database |
2573 | */ |
2574 | compact_view_name= table->compact_view_format= FALSE; |
2575 | else |
2576 | { |
2577 | /* |
2578 | Compact output format for view body can be used |
2579 | if this view only references table inside it's own db |
2580 | */ |
2581 | TABLE_LIST *tbl; |
2582 | table->compact_view_format= TRUE; |
2583 | for (tbl= thd->lex->query_tables; |
2584 | tbl; |
2585 | tbl= tbl->next_global) |
2586 | { |
2587 | if (cmp(&table->view_db, tbl->view ? &tbl->view_db : &tbl->db)) |
2588 | { |
2589 | table->compact_view_format= FALSE; |
2590 | break; |
2591 | } |
2592 | } |
2593 | } |
2594 | |
2595 | buff->append(STRING_WITH_LEN("CREATE " )); |
2596 | if (!foreign_db_mode) |
2597 | { |
2598 | view_store_options(thd, table, buff); |
2599 | } |
2600 | buff->append(STRING_WITH_LEN("VIEW " )); |
2601 | if (!compact_view_name) |
2602 | { |
2603 | append_identifier(thd, buff, &table->view_db); |
2604 | buff->append('.'); |
2605 | } |
2606 | append_identifier(thd, buff, &table->view_name); |
2607 | buff->append(STRING_WITH_LEN(" AS " )); |
2608 | |
2609 | /* |
2610 | We can't just use table->query, because our SQL_MODE may trigger |
2611 | a different syntax, like when ANSI_QUOTES is defined. |
2612 | */ |
2613 | table->view->unit.print(buff, enum_query_type(QT_ORDINARY | |
2614 | QT_ITEM_ORIGINAL_FUNC_NULLIF)); |
2615 | |
2616 | if (table->with_check != VIEW_CHECK_NONE) |
2617 | { |
2618 | if (table->with_check == VIEW_CHECK_LOCAL) |
2619 | buff->append(STRING_WITH_LEN(" WITH LOCAL CHECK OPTION" )); |
2620 | else |
2621 | buff->append(STRING_WITH_LEN(" WITH CASCADED CHECK OPTION" )); |
2622 | } |
2623 | return 0; |
2624 | } |
2625 | |
2626 | |
2627 | static int show_create_sequence(THD *thd, TABLE_LIST *table_list, |
2628 | String *packet) |
2629 | { |
2630 | TABLE *table= table_list->table; |
2631 | SEQUENCE *seq= table->s->sequence; |
2632 | LEX_CSTRING alias; |
2633 | sql_mode_t sql_mode= thd->variables.sql_mode; |
2634 | bool foreign_db_mode= sql_mode & (MODE_POSTGRESQL | MODE_ORACLE | |
2635 | MODE_MSSQL | MODE_DB2 | |
2636 | MODE_MAXDB | MODE_ANSI); |
2637 | bool show_table_options= !(sql_mode & MODE_NO_TABLE_OPTIONS) && |
2638 | !foreign_db_mode; |
2639 | |
2640 | if (lower_case_table_names == 2) |
2641 | { |
2642 | alias.str= table->alias.c_ptr(); |
2643 | alias.length= table->alias.length(); |
2644 | } |
2645 | else |
2646 | alias= table->s->table_name; |
2647 | |
2648 | packet->append(STRING_WITH_LEN("CREATE SEQUENCE " )); |
2649 | append_identifier(thd, packet, &alias); |
2650 | packet->append(STRING_WITH_LEN(" start with " )); |
2651 | packet->append_longlong(seq->start); |
2652 | packet->append(STRING_WITH_LEN(" minvalue " )); |
2653 | packet->append_longlong(seq->min_value); |
2654 | packet->append(STRING_WITH_LEN(" maxvalue " )); |
2655 | packet->append_longlong(seq->max_value); |
2656 | packet->append(STRING_WITH_LEN(" increment by " )); |
2657 | packet->append_longlong(seq->increment); |
2658 | if (seq->cache) |
2659 | { |
2660 | packet->append(STRING_WITH_LEN(" cache " )); |
2661 | packet->append_longlong(seq->cache); |
2662 | } |
2663 | else |
2664 | packet->append(STRING_WITH_LEN(" nocache" )); |
2665 | if (seq->cycle) |
2666 | packet->append(STRING_WITH_LEN(" cycle" )); |
2667 | else |
2668 | packet->append(STRING_WITH_LEN(" nocycle" )); |
2669 | |
2670 | if (show_table_options) |
2671 | add_table_options(thd, table, 0, 0, 1, packet); |
2672 | return 0; |
2673 | } |
2674 | |
2675 | |
2676 | /**************************************************************************** |
2677 | Return info about all processes |
2678 | returns for each thread: thread id, user, host, db, command, info |
2679 | ****************************************************************************/ |
2680 | |
2681 | class thread_info :public ilink { |
2682 | public: |
2683 | static void *operator new(size_t size, MEM_ROOT *mem_root) throw () |
2684 | { return alloc_root(mem_root, size); } |
2685 | static void operator delete(void *ptr __attribute__((unused)), |
2686 | size_t size __attribute__((unused))) |
2687 | { TRASH_FREE(ptr, size); } |
2688 | static void operator delete(void *, MEM_ROOT *){} |
2689 | |
2690 | my_thread_id thread_id; |
2691 | uint32 os_thread_id; |
2692 | ulonglong start_time; |
2693 | uint command; |
2694 | const char *user,*host,*db,*proc_info,*state_info; |
2695 | CSET_STRING query_string; |
2696 | double progress; |
2697 | }; |
2698 | |
2699 | static const char *thread_state_info(THD *tmp) |
2700 | { |
2701 | #ifndef EMBEDDED_LIBRARY |
2702 | if (tmp->net.reading_or_writing) |
2703 | { |
2704 | if (tmp->net.reading_or_writing == 2) |
2705 | return "Writing to net" ; |
2706 | if (tmp->get_command() == COM_SLEEP) |
2707 | return "" ; |
2708 | return "Reading from net" ; |
2709 | } |
2710 | #endif |
2711 | |
2712 | if (tmp->proc_info) |
2713 | return tmp->proc_info; |
2714 | |
2715 | /* Check if we are waiting on a condition */ |
2716 | if (!trylock_short(&tmp->LOCK_thd_kill)) |
2717 | { |
2718 | /* mysys_var is protected by above mutex */ |
2719 | bool cond= tmp->mysys_var && tmp->mysys_var->current_cond; |
2720 | mysql_mutex_unlock(&tmp->LOCK_thd_kill); |
2721 | if (cond) |
2722 | return "Waiting on cond" ; |
2723 | } |
2724 | return NULL; |
2725 | } |
2726 | |
2727 | |
2728 | void mysqld_list_processes(THD *thd,const char *user, bool verbose) |
2729 | { |
2730 | Item *field; |
2731 | List<Item> field_list; |
2732 | I_List<thread_info> thread_infos; |
2733 | ulong max_query_length= (verbose ? thd->variables.max_allowed_packet : |
2734 | PROCESS_LIST_WIDTH); |
2735 | Protocol *protocol= thd->protocol; |
2736 | MEM_ROOT *mem_root= thd->mem_root; |
2737 | DBUG_ENTER("mysqld_list_processes" ); |
2738 | |
2739 | field_list.push_back(new (mem_root) |
2740 | Item_int(thd, "Id" , 0, MY_INT32_NUM_DECIMAL_DIGITS), |
2741 | mem_root); |
2742 | field_list.push_back(new (mem_root) |
2743 | Item_empty_string(thd, "User" , |
2744 | USERNAME_CHAR_LENGTH), |
2745 | mem_root); |
2746 | field_list.push_back(new (mem_root) |
2747 | Item_empty_string(thd, "Host" , |
2748 | LIST_PROCESS_HOST_LEN), |
2749 | mem_root); |
2750 | field_list.push_back(field=new (mem_root) |
2751 | Item_empty_string(thd, "db" , NAME_CHAR_LEN), |
2752 | mem_root); |
2753 | field->maybe_null=1; |
2754 | field_list.push_back(new (mem_root) Item_empty_string(thd, "Command" , 16), |
2755 | mem_root); |
2756 | field_list.push_back(field= new (mem_root) |
2757 | Item_return_int(thd, "Time" , 7, MYSQL_TYPE_LONG), |
2758 | mem_root); |
2759 | field->unsigned_flag= 0; |
2760 | field_list.push_back(field=new (mem_root) |
2761 | Item_empty_string(thd, "State" , 30), |
2762 | mem_root); |
2763 | field->maybe_null=1; |
2764 | field_list.push_back(field=new (mem_root) |
2765 | Item_empty_string(thd, "Info" , max_query_length), |
2766 | mem_root); |
2767 | field->maybe_null=1; |
2768 | if (!thd->variables.old_mode && |
2769 | !(thd->variables.old_behavior & OLD_MODE_NO_PROGRESS_INFO)) |
2770 | { |
2771 | field_list.push_back(field= new (mem_root) |
2772 | Item_float(thd, "Progress" , 0.0, 3, 7), |
2773 | mem_root); |
2774 | field->maybe_null= 0; |
2775 | } |
2776 | if (protocol->send_result_set_metadata(&field_list, |
2777 | Protocol::SEND_NUM_ROWS | |
2778 | Protocol::SEND_EOF)) |
2779 | DBUG_VOID_RETURN; |
2780 | |
2781 | if (thd->killed) |
2782 | DBUG_VOID_RETURN; |
2783 | |
2784 | mysql_mutex_lock(&LOCK_thread_count); // For unlink from list |
2785 | I_List_iterator<THD> it(threads); |
2786 | THD *tmp; |
2787 | while ((tmp=it++)) |
2788 | { |
2789 | Security_context *tmp_sctx= tmp->security_ctx; |
2790 | bool got_thd_data; |
2791 | if ((tmp->vio_ok() || tmp->system_thread) && |
2792 | (!user || (!tmp->system_thread && |
2793 | tmp_sctx->user && !strcmp(tmp_sctx->user, user)))) |
2794 | { |
2795 | thread_info *thd_info= new (thd->mem_root) thread_info; |
2796 | |
2797 | thd_info->thread_id=tmp->thread_id; |
2798 | thd_info->os_thread_id=tmp->os_thread_id; |
2799 | thd_info->user= thd->strdup(tmp_sctx->user ? tmp_sctx->user : |
2800 | (tmp->system_thread ? |
2801 | "system user" : "unauthenticated user" )); |
2802 | if (tmp->peer_port && (tmp_sctx->host || tmp_sctx->ip) && |
2803 | thd->security_ctx->host_or_ip[0]) |
2804 | { |
2805 | if ((thd_info->host= (char*) thd->alloc(LIST_PROCESS_HOST_LEN+1))) |
2806 | my_snprintf((char *) thd_info->host, LIST_PROCESS_HOST_LEN, |
2807 | "%s:%u" , tmp_sctx->host_or_ip, tmp->peer_port); |
2808 | } |
2809 | else |
2810 | thd_info->host= thd->strdup(tmp_sctx->host_or_ip[0] ? |
2811 | tmp_sctx->host_or_ip : |
2812 | tmp_sctx->host ? tmp_sctx->host : "" ); |
2813 | thd_info->command=(int) tmp->get_command(); |
2814 | |
2815 | if ((got_thd_data= !trylock_short(&tmp->LOCK_thd_data))) |
2816 | { |
2817 | /* This is an approximation */ |
2818 | thd_info->proc_info= (char*) (tmp->killed >= KILL_QUERY ? |
2819 | "Killed" : 0); |
2820 | /* |
2821 | The following variables are only safe to access under a lock |
2822 | */ |
2823 | |
2824 | thd_info->db= 0; |
2825 | if (tmp->db.str) |
2826 | thd_info->db= thd->strmake(tmp->db.str, tmp->db.length); |
2827 | |
2828 | if (tmp->query()) |
2829 | { |
2830 | uint length= MY_MIN(max_query_length, tmp->query_length()); |
2831 | char *q= thd->strmake(tmp->query(),length); |
2832 | /* Safety: in case strmake failed, we set length to 0. */ |
2833 | thd_info->query_string= |
2834 | CSET_STRING(q, q ? length : 0, tmp->query_charset()); |
2835 | } |
2836 | |
2837 | /* |
2838 | Progress report. We need to do this under a lock to ensure that all |
2839 | is from the same stage. |
2840 | */ |
2841 | if (tmp->progress.max_counter) |
2842 | { |
2843 | uint max_stage= MY_MAX(tmp->progress.max_stage, 1); |
2844 | thd_info->progress= (((tmp->progress.stage / (double) max_stage) + |
2845 | ((tmp->progress.counter / |
2846 | (double) tmp->progress.max_counter) / |
2847 | (double) max_stage)) * |
2848 | 100.0); |
2849 | set_if_smaller(thd_info->progress, 100); |
2850 | } |
2851 | else |
2852 | thd_info->progress= 0.0; |
2853 | } |
2854 | else |
2855 | { |
2856 | thd_info->proc_info= "Busy" ; |
2857 | thd_info->progress= 0.0; |
2858 | thd_info->db= "" ; |
2859 | } |
2860 | |
2861 | thd_info->state_info= thread_state_info(tmp); |
2862 | thd_info->start_time= tmp->start_utime; |
2863 | ulonglong utime_after_query_snapshot= tmp->utime_after_query; |
2864 | if (thd_info->start_time < utime_after_query_snapshot) |
2865 | thd_info->start_time= utime_after_query_snapshot; // COM_SLEEP |
2866 | |
2867 | if (got_thd_data) |
2868 | mysql_mutex_unlock(&tmp->LOCK_thd_data); |
2869 | thread_infos.append(thd_info); |
2870 | } |
2871 | } |
2872 | mysql_mutex_unlock(&LOCK_thread_count); |
2873 | |
2874 | thread_info *thd_info; |
2875 | ulonglong now= microsecond_interval_timer(); |
2876 | char buff[20]; // For progress |
2877 | String store_buffer(buff, sizeof(buff), system_charset_info); |
2878 | |
2879 | while ((thd_info=thread_infos.get())) |
2880 | { |
2881 | protocol->prepare_for_resend(); |
2882 | protocol->store(thd_info->thread_id); |
2883 | protocol->store(thd_info->user, system_charset_info); |
2884 | protocol->store(thd_info->host, system_charset_info); |
2885 | protocol->store(thd_info->db, system_charset_info); |
2886 | if (thd_info->proc_info) |
2887 | protocol->store(thd_info->proc_info, system_charset_info); |
2888 | else |
2889 | protocol->store(command_name[thd_info->command].str, system_charset_info); |
2890 | if (thd_info->start_time && now > thd_info->start_time) |
2891 | protocol->store_long((now - thd_info->start_time) / HRTIME_RESOLUTION); |
2892 | else |
2893 | protocol->store_null(); |
2894 | protocol->store(thd_info->state_info, system_charset_info); |
2895 | protocol->store(thd_info->query_string.str(), |
2896 | thd_info->query_string.charset()); |
2897 | if (!thd->variables.old_mode && |
2898 | !(thd->variables.old_behavior & OLD_MODE_NO_PROGRESS_INFO)) |
2899 | protocol->store(thd_info->progress, 3, &store_buffer); |
2900 | if (protocol->write()) |
2901 | break; /* purecov: inspected */ |
2902 | } |
2903 | my_eof(thd); |
2904 | DBUG_VOID_RETURN; |
2905 | } |
2906 | |
2907 | |
2908 | /* |
2909 | Produce EXPLAIN data. |
2910 | |
2911 | This function is APC-scheduled to be run in the context of the thread that |
2912 | we're producing EXPLAIN for. |
2913 | */ |
2914 | |
2915 | void Show_explain_request::call_in_target_thread() |
2916 | { |
2917 | Query_arena backup_arena; |
2918 | bool printed_anything= FALSE; |
2919 | |
2920 | /* |
2921 | Change the arena because JOIN::print_explain and co. are going to allocate |
2922 | items. Let them allocate them on our arena. |
2923 | */ |
2924 | target_thd->set_n_backup_active_arena((Query_arena*)request_thd, |
2925 | &backup_arena); |
2926 | |
2927 | query_str.copy(target_thd->query(), |
2928 | target_thd->query_length(), |
2929 | target_thd->query_charset()); |
2930 | |
2931 | DBUG_ASSERT(current_thd == target_thd); |
2932 | set_current_thd(request_thd); |
2933 | if (target_thd->lex->print_explain(explain_buf, 0 /* explain flags*/, |
2934 | false /*TODO: analyze? */, &printed_anything)) |
2935 | { |
2936 | failed_to_produce= TRUE; |
2937 | } |
2938 | set_current_thd(target_thd); |
2939 | |
2940 | if (!printed_anything) |
2941 | failed_to_produce= TRUE; |
2942 | |
2943 | target_thd->restore_active_arena((Query_arena*)request_thd, &backup_arena); |
2944 | } |
2945 | |
2946 | |
2947 | int select_result_explain_buffer::send_data(List<Item> &items) |
2948 | { |
2949 | int res; |
2950 | THD *cur_thd= current_thd; |
2951 | DBUG_ENTER("select_result_explain_buffer::send_data" ); |
2952 | |
2953 | /* |
2954 | Switch to the receiveing thread, so that we correctly count memory used |
2955 | by it. This is needed as it's the receiving thread that will free the |
2956 | memory. |
2957 | */ |
2958 | set_current_thd(thd); |
2959 | fill_record(thd, dst_table, dst_table->field, items, TRUE, FALSE); |
2960 | res= dst_table->file->ha_write_tmp_row(dst_table->record[0]); |
2961 | set_current_thd(cur_thd); |
2962 | DBUG_RETURN(MY_TEST(res)); |
2963 | } |
2964 | |
2965 | bool select_result_text_buffer::send_result_set_metadata(List<Item> &fields, |
2966 | uint flag) |
2967 | { |
2968 | n_columns= fields.elements; |
2969 | return append_row(fields, true /*send item names */); |
2970 | } |
2971 | |
2972 | |
2973 | int select_result_text_buffer::send_data(List<Item> &items) |
2974 | { |
2975 | return append_row(items, false /*send item values */); |
2976 | } |
2977 | |
2978 | int select_result_text_buffer::append_row(List<Item> &items, bool send_names) |
2979 | { |
2980 | List_iterator<Item> it(items); |
2981 | Item *item; |
2982 | char **row; |
2983 | int column= 0; |
2984 | |
2985 | if (!(row= (char**) thd->alloc(sizeof(char*) * n_columns)) || |
2986 | rows.push_back(row, thd->mem_root)) |
2987 | return true; |
2988 | |
2989 | while ((item= it++)) |
2990 | { |
2991 | DBUG_ASSERT(column < n_columns); |
2992 | StringBuffer<32> buf; |
2993 | const char *data_ptr; |
2994 | char *ptr; |
2995 | size_t data_len; |
2996 | |
2997 | if (send_names) |
2998 | { |
2999 | DBUG_ASSERT(strlen(item->name.str) == item->name.length); |
3000 | data_ptr= item->name.str; |
3001 | data_len= item->name.length; |
3002 | } |
3003 | else |
3004 | { |
3005 | String *res; |
3006 | res= item->val_str(&buf); |
3007 | if (item->null_value) |
3008 | { |
3009 | data_ptr= "NULL" ; |
3010 | data_len=4; |
3011 | } |
3012 | else |
3013 | { |
3014 | data_ptr= res->c_ptr_safe(); |
3015 | data_len= res->length(); |
3016 | } |
3017 | } |
3018 | |
3019 | if (!(ptr= (char*) thd->memdup(data_ptr, data_len + 1))) |
3020 | return true; |
3021 | row[column]= ptr; |
3022 | |
3023 | column++; |
3024 | } |
3025 | return false; |
3026 | } |
3027 | |
3028 | |
3029 | void select_result_text_buffer::save_to(String *res) |
3030 | { |
3031 | List_iterator<char*> it(rows); |
3032 | char **row; |
3033 | res->append("#\n" ); |
3034 | while ((row= it++)) |
3035 | { |
3036 | res->append("# explain: " ); |
3037 | for (int i=0; i < n_columns; i++) |
3038 | { |
3039 | if (i) |
3040 | res->append('\t'); |
3041 | res->append(row[i]); |
3042 | } |
3043 | res->append("\n" ); |
3044 | } |
3045 | res->append("#\n" ); |
3046 | } |
3047 | |
3048 | |
3049 | /* |
3050 | Store the SHOW EXPLAIN output in the temporary table. |
3051 | */ |
3052 | |
3053 | int fill_show_explain(THD *thd, TABLE_LIST *table, COND *cond) |
3054 | { |
3055 | const char *calling_user; |
3056 | THD *tmp; |
3057 | my_thread_id thread_id; |
3058 | DBUG_ENTER("fill_show_explain" ); |
3059 | |
3060 | DBUG_ASSERT(cond==NULL); |
3061 | thread_id= thd->lex->value_list.head()->val_int(); |
3062 | calling_user= (thd->security_ctx->master_access & PROCESS_ACL) ? NullS : |
3063 | thd->security_ctx->priv_user; |
3064 | |
3065 | if ((tmp= find_thread_by_id(thread_id))) |
3066 | { |
3067 | Security_context *tmp_sctx= tmp->security_ctx; |
3068 | /* |
3069 | If calling_user==NULL, calling thread has SUPER or PROCESS |
3070 | privilege, and so can do SHOW EXPLAIN on any user. |
3071 | |
3072 | if calling_user!=NULL, he's only allowed to view SHOW EXPLAIN on |
3073 | his own threads. |
3074 | */ |
3075 | if (calling_user && (!tmp_sctx->user || strcmp(calling_user, |
3076 | tmp_sctx->user))) |
3077 | { |
3078 | my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "PROCESS" ); |
3079 | mysql_mutex_unlock(&tmp->LOCK_thd_kill); |
3080 | DBUG_RETURN(1); |
3081 | } |
3082 | |
3083 | if (tmp == thd) |
3084 | { |
3085 | mysql_mutex_unlock(&tmp->LOCK_thd_kill); |
3086 | my_error(ER_TARGET_NOT_EXPLAINABLE, MYF(0)); |
3087 | DBUG_RETURN(1); |
3088 | } |
3089 | |
3090 | bool bres; |
3091 | /* |
3092 | Ok we've found the thread of interest and it won't go away because |
3093 | we're holding its LOCK_thd_kill. Post it a SHOW EXPLAIN request. |
3094 | */ |
3095 | bool timed_out; |
3096 | int timeout_sec= 30; |
3097 | Show_explain_request explain_req; |
3098 | select_result_explain_buffer *explain_buf; |
3099 | |
3100 | explain_buf= new select_result_explain_buffer(thd, table->table); |
3101 | |
3102 | explain_req.explain_buf= explain_buf; |
3103 | explain_req.target_thd= tmp; |
3104 | explain_req.request_thd= thd; |
3105 | explain_req.failed_to_produce= FALSE; |
3106 | |
3107 | /* Ok, we have a lock on target->LOCK_thd_kill, can call: */ |
3108 | bres= tmp->apc_target.make_apc_call(thd, &explain_req, timeout_sec, &timed_out); |
3109 | |
3110 | if (bres || explain_req.failed_to_produce) |
3111 | { |
3112 | if (thd->killed) |
3113 | thd->send_kill_message(); |
3114 | else if (timed_out) |
3115 | my_error(ER_LOCK_WAIT_TIMEOUT, MYF(0)); |
3116 | else |
3117 | my_error(ER_TARGET_NOT_EXPLAINABLE, MYF(0)); |
3118 | |
3119 | bres= TRUE; |
3120 | } |
3121 | else |
3122 | { |
3123 | /* |
3124 | Push the query string as a warning. The query may be in a different |
3125 | charset than the charset that's used for error messages, so, convert it |
3126 | if needed. |
3127 | */ |
3128 | CHARSET_INFO *fromcs= explain_req.query_str.charset(); |
3129 | CHARSET_INFO *tocs= error_message_charset_info; |
3130 | char *warning_text; |
3131 | if (!my_charset_same(fromcs, tocs)) |
3132 | { |
3133 | uint conv_length= 1 + tocs->mbmaxlen * explain_req.query_str.length() / |
3134 | fromcs->mbminlen; |
3135 | uint dummy_errors; |
3136 | char *to, *p; |
3137 | if (!(to= (char*)thd->alloc(conv_length + 1))) |
3138 | DBUG_RETURN(1); |
3139 | p= to; |
3140 | p+= copy_and_convert(to, conv_length, tocs, |
3141 | explain_req.query_str.c_ptr(), |
3142 | explain_req.query_str.length(), fromcs, |
3143 | &dummy_errors); |
3144 | *p= 0; |
3145 | warning_text= to; |
3146 | } |
3147 | else |
3148 | warning_text= explain_req.query_str.c_ptr_safe(); |
3149 | |
3150 | push_warning(thd, Sql_condition::WARN_LEVEL_NOTE, |
3151 | ER_YES, warning_text); |
3152 | } |
3153 | DBUG_RETURN(bres); |
3154 | } |
3155 | else |
3156 | { |
3157 | my_error(ER_NO_SUCH_THREAD, MYF(0), (ulong) thread_id); |
3158 | DBUG_RETURN(1); |
3159 | } |
3160 | } |
3161 | |
3162 | |
3163 | int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond) |
3164 | { |
3165 | TABLE *table= tables->table; |
3166 | CHARSET_INFO *cs= system_charset_info; |
3167 | char *user; |
3168 | ulonglong unow= microsecond_interval_timer(); |
3169 | DBUG_ENTER("fill_schema_processlist" ); |
3170 | |
3171 | DEBUG_SYNC(thd,"fill_schema_processlist_after_unow" ); |
3172 | |
3173 | user= thd->security_ctx->master_access & PROCESS_ACL ? |
3174 | NullS : thd->security_ctx->priv_user; |
3175 | |
3176 | mysql_mutex_lock(&LOCK_thread_count); |
3177 | |
3178 | if (!thd->killed) |
3179 | { |
3180 | I_List_iterator<THD> it(threads); |
3181 | THD* tmp; |
3182 | |
3183 | while ((tmp= it++)) |
3184 | { |
3185 | Security_context *tmp_sctx= tmp->security_ctx; |
3186 | const char *val; |
3187 | ulonglong max_counter; |
3188 | bool got_thd_data; |
3189 | |
3190 | if ((!tmp->vio_ok() && !tmp->system_thread) || |
3191 | (user && (tmp->system_thread || !tmp_sctx->user || |
3192 | strcmp(tmp_sctx->user, user)))) |
3193 | continue; |
3194 | |
3195 | restore_record(table, s->default_values); |
3196 | /* ID */ |
3197 | table->field[0]->store((longlong) tmp->thread_id, TRUE); |
3198 | /* USER */ |
3199 | val= tmp_sctx->user ? tmp_sctx->user : |
3200 | (tmp->system_thread ? "system user" : "unauthenticated user" ); |
3201 | table->field[1]->store(val, strlen(val), cs); |
3202 | /* HOST */ |
3203 | if (tmp->peer_port && (tmp_sctx->host || tmp_sctx->ip) && |
3204 | thd->security_ctx->host_or_ip[0]) |
3205 | { |
3206 | char host[LIST_PROCESS_HOST_LEN + 1]; |
3207 | my_snprintf(host, LIST_PROCESS_HOST_LEN, "%s:%u" , |
3208 | tmp_sctx->host_or_ip, tmp->peer_port); |
3209 | table->field[2]->store(host, strlen(host), cs); |
3210 | } |
3211 | else |
3212 | table->field[2]->store(tmp_sctx->host_or_ip, |
3213 | strlen(tmp_sctx->host_or_ip), cs); |
3214 | |
3215 | if ((got_thd_data= !trylock_short(&tmp->LOCK_thd_data))) |
3216 | { |
3217 | /* DB */ |
3218 | if (tmp->db.str) |
3219 | { |
3220 | table->field[3]->store(tmp->db.str, tmp->db.length, cs); |
3221 | table->field[3]->set_notnull(); |
3222 | } |
3223 | } |
3224 | |
3225 | /* COMMAND */ |
3226 | if ((val= (char *) (!got_thd_data ? "Busy" : |
3227 | (tmp->killed >= KILL_QUERY ? |
3228 | "Killed" : 0)))) |
3229 | table->field[4]->store(val, strlen(val), cs); |
3230 | else |
3231 | table->field[4]->store(command_name[tmp->get_command()].str, |
3232 | command_name[tmp->get_command()].length, cs); |
3233 | |
3234 | /* MYSQL_TIME */ |
3235 | ulonglong utime= tmp->start_utime; |
3236 | ulonglong utime_after_query_snapshot= tmp->utime_after_query; |
3237 | if (utime < utime_after_query_snapshot) |
3238 | utime= utime_after_query_snapshot; // COM_SLEEP |
3239 | utime= utime && utime < unow ? unow - utime : 0; |
3240 | |
3241 | table->field[5]->store(utime / HRTIME_RESOLUTION, TRUE); |
3242 | |
3243 | if (got_thd_data) |
3244 | { |
3245 | if (tmp->query()) |
3246 | { |
3247 | table->field[7]->store(tmp->query(), |
3248 | MY_MIN(PROCESS_LIST_INFO_WIDTH, |
3249 | tmp->query_length()), cs); |
3250 | table->field[7]->set_notnull(); |
3251 | |
3252 | /* INFO_BINARY */ |
3253 | table->field[16]->store(tmp->query(), |
3254 | MY_MIN(PROCESS_LIST_INFO_WIDTH, |
3255 | tmp->query_length()), |
3256 | &my_charset_bin); |
3257 | table->field[16]->set_notnull(); |
3258 | } |
3259 | |
3260 | /* |
3261 | Progress report. We need to do this under a lock to ensure that all |
3262 | is from the same stage. |
3263 | */ |
3264 | if ((max_counter= tmp->progress.max_counter)) |
3265 | { |
3266 | table->field[9]->store((longlong) tmp->progress.stage + 1, 1); |
3267 | table->field[10]->store((longlong) tmp->progress.max_stage, 1); |
3268 | table->field[11]->store((double) tmp->progress.counter / |
3269 | (double) max_counter*100.0); |
3270 | } |
3271 | mysql_mutex_unlock(&tmp->LOCK_thd_data); |
3272 | } |
3273 | |
3274 | /* STATE */ |
3275 | if ((val= thread_state_info(tmp))) |
3276 | { |
3277 | table->field[6]->store(val, strlen(val), cs); |
3278 | table->field[6]->set_notnull(); |
3279 | } |
3280 | |
3281 | /* TIME_MS */ |
3282 | table->field[8]->store((double)(utime / (HRTIME_RESOLUTION / 1000.0))); |
3283 | |
3284 | /* |
3285 | This may become negative if we free a memory allocated by another |
3286 | thread in this thread. However it's better that we notice it eventually |
3287 | than hide it. |
3288 | */ |
3289 | table->field[12]->store((longlong) tmp->status_var.local_memory_used, |
3290 | FALSE); |
3291 | table->field[13]->store((longlong) tmp->status_var.max_local_memory_used, |
3292 | FALSE); |
3293 | table->field[14]->store((longlong) tmp->get_examined_row_count(), TRUE); |
3294 | |
3295 | /* QUERY_ID */ |
3296 | table->field[15]->store(tmp->query_id, TRUE); |
3297 | |
3298 | table->field[17]->store(tmp->os_thread_id); |
3299 | |
3300 | if (schema_table_store_record(thd, table)) |
3301 | { |
3302 | mysql_mutex_unlock(&LOCK_thread_count); |
3303 | DBUG_RETURN(1); |
3304 | } |
3305 | } |
3306 | } |
3307 | |
3308 | mysql_mutex_unlock(&LOCK_thread_count); |
3309 | DBUG_RETURN(0); |
3310 | } |
3311 | |
3312 | /***************************************************************************** |
3313 | Status functions |
3314 | *****************************************************************************/ |
3315 | |
3316 | static DYNAMIC_ARRAY all_status_vars; |
3317 | static bool status_vars_inited= 0; |
3318 | |
3319 | C_MODE_START |
3320 | static int show_var_cmp(const void *var1, const void *var2) |
3321 | { |
3322 | return strcasecmp(((SHOW_VAR*)var1)->name, ((SHOW_VAR*)var2)->name); |
3323 | } |
3324 | C_MODE_END |
3325 | |
3326 | /* |
3327 | deletes all the SHOW_UNDEF elements from the array and calls |
3328 | delete_dynamic() if it's completely empty. |
3329 | */ |
3330 | static void shrink_var_array(DYNAMIC_ARRAY *array) |
3331 | { |
3332 | uint a,b; |
3333 | SHOW_VAR *all= dynamic_element(array, 0, SHOW_VAR *); |
3334 | |
3335 | for (a= b= 0; b < array->elements; b++) |
3336 | if (all[b].type != SHOW_UNDEF) |
3337 | all[a++]= all[b]; |
3338 | if (a) |
3339 | { |
3340 | bzero(all+a, sizeof(SHOW_VAR)); // writing NULL-element to the end |
3341 | array->elements= a; |
3342 | } |
3343 | else // array is completely empty - delete it |
3344 | delete_dynamic(array); |
3345 | } |
3346 | |
3347 | /* |
3348 | Adds an array of SHOW_VAR entries to the output of SHOW STATUS |
3349 | |
3350 | SYNOPSIS |
3351 | add_status_vars(SHOW_VAR *list) |
3352 | list - an array of SHOW_VAR entries to add to all_status_vars |
3353 | the last entry must be {0,0,SHOW_UNDEF} |
3354 | |
3355 | NOTE |
3356 | The handling of all_status_vars[] is completely internal, it's allocated |
3357 | automatically when something is added to it, and deleted completely when |
3358 | the last entry is removed. |
3359 | |
3360 | As a special optimization, if add_status_vars() is called before |
3361 | init_status_vars(), it assumes "startup mode" - neither concurrent access |
3362 | to the array nor SHOW STATUS are possible (thus it skips locks and qsort) |
3363 | |
3364 | The last entry of the all_status_vars[] should always be {0,0,SHOW_UNDEF} |
3365 | */ |
3366 | int add_status_vars(SHOW_VAR *list) |
3367 | { |
3368 | int res= 0; |
3369 | if (status_vars_inited) |
3370 | mysql_mutex_lock(&LOCK_show_status); |
3371 | if (!all_status_vars.buffer && // array is not allocated yet - do it now |
3372 | my_init_dynamic_array(&all_status_vars, sizeof(SHOW_VAR), 250, 50, MYF(0))) |
3373 | { |
3374 | res= 1; |
3375 | goto err; |
3376 | } |
3377 | while (list->name) |
3378 | res|= insert_dynamic(&all_status_vars, (uchar*)list++); |
3379 | res|= insert_dynamic(&all_status_vars, (uchar*)list); // appending NULL-element |
3380 | all_status_vars.elements--; // but next insert_dynamic should overwite it |
3381 | if (status_vars_inited) |
3382 | sort_dynamic(&all_status_vars, show_var_cmp); |
3383 | err: |
3384 | if (status_vars_inited) |
3385 | mysql_mutex_unlock(&LOCK_show_status); |
3386 | return res; |
3387 | } |
3388 | |
3389 | /* |
3390 | Make all_status_vars[] usable for SHOW STATUS |
3391 | |
3392 | NOTE |
3393 | See add_status_vars(). Before init_status_vars() call, add_status_vars() |
3394 | works in a special fast "startup" mode. Thus init_status_vars() |
3395 | should be called as late as possible but before enabling multi-threading. |
3396 | */ |
3397 | void init_status_vars() |
3398 | { |
3399 | status_vars_inited=1; |
3400 | sort_dynamic(&all_status_vars, show_var_cmp); |
3401 | } |
3402 | |
3403 | void reset_status_vars() |
3404 | { |
3405 | SHOW_VAR *ptr= (SHOW_VAR*) all_status_vars.buffer; |
3406 | SHOW_VAR *last= ptr + all_status_vars.elements; |
3407 | for (; ptr < last; ptr++) |
3408 | { |
3409 | /* Note that SHOW_LONG_NOFLUSH variables are not reset */ |
3410 | if (ptr->type == SHOW_LONG) |
3411 | *(ulong*) ptr->value= 0; |
3412 | } |
3413 | } |
3414 | |
3415 | /* |
3416 | catch-all cleanup function, cleans up everything no matter what |
3417 | |
3418 | DESCRIPTION |
3419 | This function is not strictly required if all add_status_vars/ |
3420 | remove_status_vars are properly paired, but it's a safety measure that |
3421 | deletes everything from the all_status_vars[] even if some |
3422 | remove_status_vars were forgotten |
3423 | */ |
3424 | void free_status_vars() |
3425 | { |
3426 | delete_dynamic(&all_status_vars); |
3427 | } |
3428 | |
3429 | /* |
3430 | Removes an array of SHOW_VAR entries from the output of SHOW STATUS |
3431 | |
3432 | SYNOPSIS |
3433 | remove_status_vars(SHOW_VAR *list) |
3434 | list - an array of SHOW_VAR entries to remove to all_status_vars |
3435 | the last entry must be {0,0,SHOW_UNDEF} |
3436 | |
3437 | NOTE |
3438 | there's lots of room for optimizing this, especially in non-sorted mode, |
3439 | but nobody cares - it may be called only in case of failed plugin |
3440 | initialization in the mysqld startup. |
3441 | */ |
3442 | |
3443 | void remove_status_vars(SHOW_VAR *list) |
3444 | { |
3445 | if (status_vars_inited) |
3446 | { |
3447 | mysql_mutex_lock(&LOCK_show_status); |
3448 | SHOW_VAR *all= dynamic_element(&all_status_vars, 0, SHOW_VAR *); |
3449 | |
3450 | for (; list->name; list++) |
3451 | { |
3452 | int first= 0, last= ((int) all_status_vars.elements) - 1; |
3453 | for ( ; first <= last; ) |
3454 | { |
3455 | int res, middle= (first + last) / 2; |
3456 | if ((res= show_var_cmp(list, all + middle)) < 0) |
3457 | last= middle - 1; |
3458 | else if (res > 0) |
3459 | first= middle + 1; |
3460 | else |
3461 | { |
3462 | all[middle].type= SHOW_UNDEF; |
3463 | break; |
3464 | } |
3465 | } |
3466 | } |
3467 | shrink_var_array(&all_status_vars); |
3468 | mysql_mutex_unlock(&LOCK_show_status); |
3469 | } |
3470 | else |
3471 | { |
3472 | SHOW_VAR *all= dynamic_element(&all_status_vars, 0, SHOW_VAR *); |
3473 | uint i; |
3474 | for (; list->name; list++) |
3475 | { |
3476 | for (i= 0; i < all_status_vars.elements; i++) |
3477 | { |
3478 | if (show_var_cmp(list, all+i)) |
3479 | continue; |
3480 | all[i].type= SHOW_UNDEF; |
3481 | break; |
3482 | } |
3483 | } |
3484 | shrink_var_array(&all_status_vars); |
3485 | } |
3486 | } |
3487 | |
3488 | |
3489 | /** |
3490 | @brief Returns the value of a system or a status variable. |
3491 | |
3492 | @param thd [in] The handle of the current THD. |
3493 | @param variable [in] Details of the variable. |
3494 | @param value_type [in] Variable type. |
3495 | @param show_type [in] Variable show type. |
3496 | @param charset [out] Character set of the value. |
3497 | @param buff [in,out] Buffer to store the value. |
3498 | (Needs to have enough memory |
3499 | to hold the value of variable.) |
3500 | @param length [out] Length of the value. |
3501 | |
3502 | @return Pointer to the value buffer. |
3503 | */ |
3504 | |
3505 | const char* get_one_variable(THD *thd, |
3506 | const SHOW_VAR *variable, |
3507 | enum_var_type value_type, SHOW_TYPE show_type, |
3508 | system_status_var *status_var, |
3509 | const CHARSET_INFO **charset, char *buff, |
3510 | size_t *length) |
3511 | { |
3512 | void *value= variable->value; |
3513 | const char *pos= buff; |
3514 | const char *end= buff; |
3515 | |
3516 | |
3517 | if (show_type == SHOW_SYS) |
3518 | { |
3519 | sys_var *var= (sys_var *) value; |
3520 | show_type= var->show_type(); |
3521 | value= var->value_ptr(thd, value_type, &null_clex_str); |
3522 | *charset= var->charset(thd); |
3523 | } |
3524 | |
3525 | /* |
3526 | note that value may be == buff. All SHOW_xxx code below |
3527 | should still work in this case |
3528 | */ |
3529 | switch (show_type) { |
3530 | case SHOW_DOUBLE_STATUS: |
3531 | value= ((char *) status_var + (intptr) value); |
3532 | /* fall through */ |
3533 | case SHOW_DOUBLE: |
3534 | /* 6 is the default precision for '%f' in sprintf() */ |
3535 | end= buff + my_fcvt(*(double *) value, 6, buff, NULL); |
3536 | break; |
3537 | case SHOW_LONG_STATUS: |
3538 | value= ((char *) status_var + (intptr) value); |
3539 | /* fall through */ |
3540 | case SHOW_ULONG: |
3541 | case SHOW_LONG_NOFLUSH: // the difference lies in refresh_status() |
3542 | end= int10_to_str(*(long*) value, buff, 10); |
3543 | break; |
3544 | case SHOW_LONGLONG_STATUS: |
3545 | value= ((char *) status_var + (intptr) value); |
3546 | /* fall through */ |
3547 | case SHOW_ULONGLONG: |
3548 | end= longlong10_to_str(*(longlong*) value, buff, 10); |
3549 | break; |
3550 | case SHOW_HA_ROWS: |
3551 | end= longlong10_to_str((longlong) *(ha_rows*) value, buff, 10); |
3552 | break; |
3553 | case SHOW_BOOL: |
3554 | end= strmov(buff, *(bool*) value ? "ON" : "OFF" ); |
3555 | break; |
3556 | case SHOW_MY_BOOL: |
3557 | end= strmov(buff, *(my_bool*) value ? "ON" : "OFF" ); |
3558 | break; |
3559 | case SHOW_UINT32_STATUS: |
3560 | value= ((char *) status_var + (intptr) value); |
3561 | /* fall through */ |
3562 | case SHOW_UINT: |
3563 | end= int10_to_str((long) *(uint*) value, buff, 10); |
3564 | break; |
3565 | case SHOW_SINT: |
3566 | end= int10_to_str((long) *(int*) value, buff, -10); |
3567 | break; |
3568 | case SHOW_SLONG: |
3569 | end= int10_to_str(*(long*) value, buff, -10); |
3570 | break; |
3571 | case SHOW_SLONGLONG: |
3572 | end= longlong10_to_str(*(longlong*) value, buff, -10); |
3573 | break; |
3574 | case SHOW_HAVE: |
3575 | { |
3576 | SHOW_COMP_OPTION tmp= *(SHOW_COMP_OPTION*) value; |
3577 | pos= show_comp_option_name[(int) tmp]; |
3578 | end= strend(pos); |
3579 | break; |
3580 | } |
3581 | case SHOW_CHAR: |
3582 | { |
3583 | if (!(pos= (char*)value)) |
3584 | pos= "" ; |
3585 | end= strend(pos); |
3586 | break; |
3587 | } |
3588 | case SHOW_CHAR_PTR: |
3589 | { |
3590 | if (!(pos= *(char**) value)) |
3591 | pos= "" ; |
3592 | |
3593 | end= strend(pos); |
3594 | break; |
3595 | } |
3596 | case SHOW_LEX_STRING: |
3597 | { |
3598 | LEX_STRING *ls=(LEX_STRING*)value; |
3599 | if (!(pos= ls->str)) |
3600 | end= pos= "" ; |
3601 | else |
3602 | end= pos + ls->length; |
3603 | break; |
3604 | } |
3605 | case SHOW_UNDEF: |
3606 | break; // Return empty string |
3607 | case SHOW_SYS: // Cannot happen |
3608 | default: |
3609 | DBUG_ASSERT(0); |
3610 | break; |
3611 | } |
3612 | |
3613 | *length= (size_t) (end - pos); |
3614 | return pos; |
3615 | } |
3616 | |
3617 | |
3618 | static bool show_status_array(THD *thd, const char *wild, |
3619 | SHOW_VAR *variables, |
3620 | enum enum_var_type scope, |
3621 | struct system_status_var *status_var, |
3622 | const char *prefix, TABLE *table, |
3623 | bool ucase_names, |
3624 | COND *cond) |
3625 | { |
3626 | my_aligned_storage<SHOW_VAR_FUNC_BUFF_SIZE, MY_ALIGNOF(long)> buffer; |
3627 | char * const buff= buffer.data; |
3628 | char *prefix_end; |
3629 | char name_buffer[NAME_CHAR_LEN]; |
3630 | int len; |
3631 | SHOW_VAR tmp, *var; |
3632 | enum_check_fields save_count_cuted_fields= thd->count_cuted_fields; |
3633 | bool res= FALSE; |
3634 | CHARSET_INFO *charset= system_charset_info; |
3635 | DBUG_ENTER("show_status_array" ); |
3636 | |
3637 | thd->count_cuted_fields= CHECK_FIELD_WARN; |
3638 | |
3639 | prefix_end=strnmov(name_buffer, prefix, sizeof(name_buffer)-1); |
3640 | if (*prefix) |
3641 | *prefix_end++= '_'; |
3642 | len=(int)(name_buffer + sizeof(name_buffer) - prefix_end); |
3643 | |
3644 | #ifdef WITH_WSREP |
3645 | bool is_wsrep_var= FALSE; |
3646 | /* |
3647 | This is a workaround for lp:1306875 (PBX) to skip switching of wsrep |
3648 | status variable name's first letter to uppercase. This is an optimization |
3649 | for status variables defined under wsrep plugin. |
3650 | TODO: remove once lp:1306875 has been addressed. |
3651 | */ |
3652 | if (*prefix && !my_strcasecmp(system_charset_info, prefix, "wsrep" )) |
3653 | { |
3654 | is_wsrep_var= TRUE; |
3655 | } |
3656 | #endif /* WITH_WSREP */ |
3657 | |
3658 | for (; variables->name; variables++) |
3659 | { |
3660 | bool wild_checked= false; |
3661 | strnmov(prefix_end, variables->name, len); |
3662 | name_buffer[sizeof(name_buffer)-1]=0; /* Safety */ |
3663 | |
3664 | #ifdef WITH_WSREP |
3665 | /* |
3666 | If the prefix is NULL, that means we are looking into the status variables |
3667 | defined directly under mysqld.cc. Do not capitalize wsrep status variable |
3668 | names until lp:1306875 has been fixed. |
3669 | TODO: remove once lp:1306875 has been addressed. |
3670 | */ |
3671 | if (!(*prefix) && !strncasecmp(name_buffer, "wsrep" , strlen("wsrep" ))) |
3672 | { |
3673 | is_wsrep_var= TRUE; |
3674 | } |
3675 | #endif /* WITH_WSREP */ |
3676 | |
3677 | if (ucase_names) |
3678 | my_caseup_str(system_charset_info, name_buffer); |
3679 | else |
3680 | { |
3681 | my_casedn_str(system_charset_info, name_buffer); |
3682 | DBUG_ASSERT(name_buffer[0] >= 'a'); |
3683 | DBUG_ASSERT(name_buffer[0] <= 'z'); |
3684 | |
3685 | // WSREP_TODO: remove once lp:1306875 has been addressed. |
3686 | if (IF_WSREP(is_wsrep_var == FALSE, 1) && |
3687 | status_var) |
3688 | name_buffer[0]-= 'a' - 'A'; |
3689 | } |
3690 | |
3691 | |
3692 | restore_record(table, s->default_values); |
3693 | table->field[0]->store(name_buffer, strlen(name_buffer), |
3694 | system_charset_info); |
3695 | |
3696 | /* |
3697 | Compare name for types that can't return arrays. We do this to not |
3698 | calculate the value for function variables that we will not access |
3699 | */ |
3700 | if ((variables->type != SHOW_FUNC && variables->type != SHOW_ARRAY)) |
3701 | { |
3702 | if (wild && wild[0] && wild_case_compare(system_charset_info, |
3703 | name_buffer, wild)) |
3704 | continue; |
3705 | wild_checked= 1; // Avoid checking it again |
3706 | } |
3707 | |
3708 | /* |
3709 | if var->type is SHOW_FUNC or SHOW_SIMPLE_FUNC, call the function. |
3710 | Repeat as necessary, if new var is again one of the above |
3711 | */ |
3712 | for (var=variables; var->type == SHOW_FUNC || |
3713 | var->type == SHOW_SIMPLE_FUNC; var= &tmp) |
3714 | ((mysql_show_var_func)(var->value))(thd, &tmp, buff, |
3715 | status_var, scope); |
3716 | |
3717 | SHOW_TYPE show_type=var->type; |
3718 | if (show_type == SHOW_ARRAY) |
3719 | { |
3720 | show_status_array(thd, wild, (SHOW_VAR *) var->value, scope, |
3721 | status_var, name_buffer, table, ucase_names, cond); |
3722 | } |
3723 | else |
3724 | { |
3725 | if ((wild_checked || |
3726 | !(wild && wild[0] && wild_case_compare(system_charset_info, |
3727 | name_buffer, wild))) && |
3728 | (!cond || cond->val_int())) |
3729 | { |
3730 | const char *pos; // We assign a lot of const's |
3731 | size_t length; |
3732 | |
3733 | if (show_type == SHOW_SYS) |
3734 | mysql_mutex_lock(&LOCK_global_system_variables); |
3735 | pos= get_one_variable(thd, var, scope, show_type, status_var, |
3736 | &charset, buff, &length); |
3737 | |
3738 | table->field[1]->store(pos, (uint32) length, charset); |
3739 | thd->count_cuted_fields= CHECK_FIELD_IGNORE; |
3740 | table->field[1]->set_notnull(); |
3741 | if (show_type == SHOW_SYS) |
3742 | mysql_mutex_unlock(&LOCK_global_system_variables); |
3743 | |
3744 | |
3745 | if (schema_table_store_record(thd, table)) |
3746 | { |
3747 | res= TRUE; |
3748 | goto end; |
3749 | } |
3750 | thd->get_stmt_da()->inc_current_row_for_warning(); |
3751 | } |
3752 | } |
3753 | } |
3754 | end: |
3755 | thd->count_cuted_fields= save_count_cuted_fields; |
3756 | DBUG_RETURN(res); |
3757 | } |
3758 | |
3759 | /* |
3760 | collect status for all running threads |
3761 | Return number of threads used |
3762 | */ |
3763 | |
3764 | uint calc_sum_of_all_status(STATUS_VAR *to) |
3765 | { |
3766 | uint count= 0; |
3767 | DBUG_ENTER("calc_sum_of_all_status" ); |
3768 | |
3769 | /* Ensure that thread id not killed during loop */ |
3770 | mysql_mutex_lock(&LOCK_thread_count); // For unlink from list |
3771 | |
3772 | I_List_iterator<THD> it(threads); |
3773 | THD *tmp; |
3774 | |
3775 | /* Get global values as base */ |
3776 | *to= global_status_var; |
3777 | to->local_memory_used= 0; |
3778 | |
3779 | /* Add to this status from existing threads */ |
3780 | while ((tmp= it++)) |
3781 | { |
3782 | count++; |
3783 | if (!tmp->status_in_global) |
3784 | { |
3785 | add_to_status(to, &tmp->status_var); |
3786 | to->local_memory_used+= tmp->status_var.local_memory_used; |
3787 | } |
3788 | if (tmp->get_command() != COM_SLEEP) |
3789 | to->threads_running++; |
3790 | } |
3791 | |
3792 | mysql_mutex_unlock(&LOCK_thread_count); |
3793 | DBUG_RETURN(count); |
3794 | } |
3795 | |
3796 | |
3797 | /* This is only used internally, but we need it here as a forward reference */ |
3798 | extern ST_SCHEMA_TABLE schema_tables[]; |
3799 | |
3800 | /* |
3801 | Store record to I_S table, convert HEAP table |
3802 | to MyISAM if necessary |
3803 | |
3804 | SYNOPSIS |
3805 | schema_table_store_record() |
3806 | thd thread handler |
3807 | table Information schema table to be updated |
3808 | |
3809 | RETURN |
3810 | 0 success |
3811 | 1 error |
3812 | */ |
3813 | |
3814 | bool schema_table_store_record(THD *thd, TABLE *table) |
3815 | { |
3816 | int error; |
3817 | |
3818 | if (unlikely(thd->killed)) |
3819 | { |
3820 | thd->send_kill_message(); |
3821 | return 1; |
3822 | } |
3823 | |
3824 | if (unlikely((error= table->file->ha_write_tmp_row(table->record[0])))) |
3825 | { |
3826 | TMP_TABLE_PARAM *param= table->pos_in_table_list->schema_table_param; |
3827 | if (unlikely(create_internal_tmp_table_from_heap(thd, table, |
3828 | param->start_recinfo, |
3829 | ¶m->recinfo, error, 0, |
3830 | NULL))) |
3831 | |
3832 | return 1; |
3833 | } |
3834 | return 0; |
3835 | } |
3836 | |
3837 | |
3838 | static int make_table_list(THD *thd, SELECT_LEX *sel, |
3839 | LEX_CSTRING *db_name, LEX_CSTRING *table_name) |
3840 | { |
3841 | Table_ident *table_ident; |
3842 | table_ident= new Table_ident(thd, db_name, table_name, 1); |
3843 | if (!sel->add_table_to_list(thd, table_ident, 0, 0, TL_READ, MDL_SHARED_READ)) |
3844 | return 1; |
3845 | return 0; |
3846 | } |
3847 | |
3848 | |
3849 | /** |
3850 | @brief Get lookup value from the part of 'WHERE' condition |
3851 | |
3852 | @details This function gets lookup value from |
3853 | the part of 'WHERE' condition if it's possible and |
3854 | fill appropriate lookup_field_vals struct field |
3855 | with this value. |
3856 | |
3857 | @param[in] thd thread handler |
3858 | @param[in] item_func part of WHERE condition |
3859 | @param[in] table I_S table |
3860 | @param[in, out] lookup_field_vals Struct which holds lookup values |
3861 | |
3862 | @return |
3863 | 0 success |
3864 | 1 error, there can be no matching records for the condition |
3865 | */ |
3866 | |
3867 | bool get_lookup_value(THD *thd, Item_func *item_func, |
3868 | TABLE_LIST *table, |
3869 | LOOKUP_FIELD_VALUES *lookup_field_vals) |
3870 | { |
3871 | ST_SCHEMA_TABLE *schema_table= table->schema_table; |
3872 | ST_FIELD_INFO *field_info= schema_table->fields_info; |
3873 | const char *field_name1= schema_table->idx_field1 >= 0 ? |
3874 | field_info[schema_table->idx_field1].field_name : "" ; |
3875 | const char *field_name2= schema_table->idx_field2 >= 0 ? |
3876 | field_info[schema_table->idx_field2].field_name : "" ; |
3877 | |
3878 | if (item_func->functype() == Item_func::EQ_FUNC || |
3879 | item_func->functype() == Item_func::EQUAL_FUNC) |
3880 | { |
3881 | int idx_field, idx_val; |
3882 | char tmp[MAX_FIELD_WIDTH]; |
3883 | String *tmp_str, str_buff(tmp, sizeof(tmp), system_charset_info); |
3884 | Item_field *item_field; |
3885 | CHARSET_INFO *cs= system_charset_info; |
3886 | |
3887 | if (item_func->arguments()[0]->real_item()->type() == Item::FIELD_ITEM && |
3888 | item_func->arguments()[1]->const_item()) |
3889 | { |
3890 | idx_field= 0; |
3891 | idx_val= 1; |
3892 | } |
3893 | else if (item_func->arguments()[1]->real_item()->type() == Item::FIELD_ITEM && |
3894 | item_func->arguments()[0]->const_item()) |
3895 | { |
3896 | idx_field= 1; |
3897 | idx_val= 0; |
3898 | } |
3899 | else |
3900 | return 0; |
3901 | |
3902 | item_field= (Item_field*) item_func->arguments()[idx_field]->real_item(); |
3903 | if (table->table != item_field->field->table) |
3904 | return 0; |
3905 | tmp_str= item_func->arguments()[idx_val]->val_str(&str_buff); |
3906 | |
3907 | /* impossible value */ |
3908 | if (!tmp_str) |
3909 | return 1; |
3910 | |
3911 | /* Lookup value is database name */ |
3912 | if (!cs->coll->strnncollsp(cs, (uchar *) field_name1, strlen(field_name1), |
3913 | (uchar *) item_field->field_name.str, |
3914 | item_field->field_name.length)) |
3915 | { |
3916 | thd->make_lex_string(&lookup_field_vals->db_value, |
3917 | tmp_str->ptr(), tmp_str->length()); |
3918 | } |
3919 | /* Lookup value is table name */ |
3920 | else if (!cs->coll->strnncollsp(cs, (uchar *) field_name2, |
3921 | strlen(field_name2), |
3922 | (uchar *) item_field->field_name.str, |
3923 | item_field->field_name.length)) |
3924 | { |
3925 | thd->make_lex_string(&lookup_field_vals->table_value, |
3926 | tmp_str->ptr(), tmp_str->length()); |
3927 | } |
3928 | } |
3929 | return 0; |
3930 | } |
3931 | |
3932 | |
3933 | /** |
3934 | @brief Calculates lookup values from 'WHERE' condition |
3935 | |
3936 | @details This function calculates lookup value(database name, table name) |
3937 | from 'WHERE' condition if it's possible and |
3938 | fill lookup_field_vals struct fields with these values. |
3939 | |
3940 | @param[in] thd thread handler |
3941 | @param[in] cond WHERE condition |
3942 | @param[in] table I_S table |
3943 | @param[in, out] lookup_field_vals Struct which holds lookup values |
3944 | |
3945 | @return |
3946 | 0 success |
3947 | 1 error, there can be no matching records for the condition |
3948 | */ |
3949 | |
3950 | bool calc_lookup_values_from_cond(THD *thd, COND *cond, TABLE_LIST *table, |
3951 | LOOKUP_FIELD_VALUES *lookup_field_vals) |
3952 | { |
3953 | if (!cond) |
3954 | return 0; |
3955 | |
3956 | if (cond->type() == Item::COND_ITEM) |
3957 | { |
3958 | if (((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC) |
3959 | { |
3960 | List_iterator<Item> li(*((Item_cond*) cond)->argument_list()); |
3961 | Item *item; |
3962 | while ((item= li++)) |
3963 | { |
3964 | if (item->type() == Item::FUNC_ITEM) |
3965 | { |
3966 | if (get_lookup_value(thd, (Item_func*)item, table, lookup_field_vals)) |
3967 | return 1; |
3968 | } |
3969 | else |
3970 | { |
3971 | if (calc_lookup_values_from_cond(thd, item, table, lookup_field_vals)) |
3972 | return 1; |
3973 | } |
3974 | } |
3975 | } |
3976 | return 0; |
3977 | } |
3978 | else if (cond->type() == Item::FUNC_ITEM && |
3979 | get_lookup_value(thd, (Item_func*) cond, table, lookup_field_vals)) |
3980 | return 1; |
3981 | return 0; |
3982 | } |
3983 | |
3984 | |
3985 | bool uses_only_table_name_fields(Item *item, TABLE_LIST *table) |
3986 | { |
3987 | if (item->type() == Item::FUNC_ITEM) |
3988 | { |
3989 | Item_func *item_func= (Item_func*)item; |
3990 | for (uint i=0; i<item_func->argument_count(); i++) |
3991 | { |
3992 | if (!uses_only_table_name_fields(item_func->arguments()[i], table)) |
3993 | return 0; |
3994 | } |
3995 | } |
3996 | else if (item->type() == Item::ROW_ITEM) |
3997 | { |
3998 | Item_row *item_row= static_cast<Item_row*>(item); |
3999 | for (uint i= 0; i < item_row->cols(); i++) |
4000 | { |
4001 | if (!uses_only_table_name_fields(item_row->element_index(i), table)) |
4002 | return 0; |
4003 | } |
4004 | } |
4005 | else if (item->type() == Item::FIELD_ITEM) |
4006 | { |
4007 | Item_field *item_field= (Item_field*)item; |
4008 | CHARSET_INFO *cs= system_charset_info; |
4009 | ST_SCHEMA_TABLE *schema_table= table->schema_table; |
4010 | ST_FIELD_INFO *field_info= schema_table->fields_info; |
4011 | const char *field_name1= schema_table->idx_field1 >= 0 ? |
4012 | field_info[schema_table->idx_field1].field_name : "" ; |
4013 | const char *field_name2= schema_table->idx_field2 >= 0 ? |
4014 | field_info[schema_table->idx_field2].field_name : "" ; |
4015 | if (table->table != item_field->field->table || |
4016 | (cs->coll->strnncollsp(cs, (uchar *) field_name1, strlen(field_name1), |
4017 | (uchar *) item_field->field_name.str, |
4018 | item_field->field_name.length) && |
4019 | cs->coll->strnncollsp(cs, (uchar *) field_name2, strlen(field_name2), |
4020 | (uchar *) item_field->field_name.str, |
4021 | item_field->field_name.length))) |
4022 | return 0; |
4023 | } |
4024 | else if (item->type() == Item::EXPR_CACHE_ITEM) |
4025 | { |
4026 | Item_cache_wrapper *tmp= static_cast<Item_cache_wrapper*>(item); |
4027 | return uses_only_table_name_fields(tmp->get_orig_item(), table); |
4028 | } |
4029 | else if (item->type() == Item::REF_ITEM) |
4030 | return uses_only_table_name_fields(item->real_item(), table); |
4031 | |
4032 | if (item->real_type() == Item::SUBSELECT_ITEM && !item->const_item()) |
4033 | return 0; |
4034 | |
4035 | return 1; |
4036 | } |
4037 | |
4038 | |
4039 | COND *make_cond_for_info_schema(THD *thd, COND *cond, TABLE_LIST *table) |
4040 | { |
4041 | if (!cond) |
4042 | return (COND*) 0; |
4043 | if (cond->type() == Item::COND_ITEM) |
4044 | { |
4045 | if (((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC) |
4046 | { |
4047 | /* Create new top level AND item */ |
4048 | Item_cond_and *new_cond=new (thd->mem_root) Item_cond_and(thd); |
4049 | if (!new_cond) |
4050 | return (COND*) 0; |
4051 | List_iterator<Item> li(*((Item_cond*) cond)->argument_list()); |
4052 | Item *item; |
4053 | while ((item=li++)) |
4054 | { |
4055 | Item *fix= make_cond_for_info_schema(thd, item, table); |
4056 | if (fix) |
4057 | new_cond->argument_list()->push_back(fix, thd->mem_root); |
4058 | } |
4059 | switch (new_cond->argument_list()->elements) { |
4060 | case 0: |
4061 | return (COND*) 0; |
4062 | case 1: |
4063 | return new_cond->argument_list()->head(); |
4064 | default: |
4065 | new_cond->quick_fix_field(); |
4066 | return new_cond; |
4067 | } |
4068 | } |
4069 | else |
4070 | { // Or list |
4071 | Item_cond_or *new_cond= new (thd->mem_root) Item_cond_or(thd); |
4072 | if (!new_cond) |
4073 | return (COND*) 0; |
4074 | List_iterator<Item> li(*((Item_cond*) cond)->argument_list()); |
4075 | Item *item; |
4076 | while ((item=li++)) |
4077 | { |
4078 | Item *fix=make_cond_for_info_schema(thd, item, table); |
4079 | if (!fix) |
4080 | return (COND*) 0; |
4081 | new_cond->argument_list()->push_back(fix, thd->mem_root); |
4082 | } |
4083 | new_cond->quick_fix_field(); |
4084 | new_cond->top_level_item(); |
4085 | return new_cond; |
4086 | } |
4087 | } |
4088 | |
4089 | if (!uses_only_table_name_fields(cond, table)) |
4090 | return (COND*) 0; |
4091 | return cond; |
4092 | } |
4093 | |
4094 | |
4095 | /** |
4096 | @brief Calculate lookup values(database name, table name) |
4097 | |
4098 | @details This function calculates lookup values(database name, table name) |
4099 | from 'WHERE' condition or wild values (for 'SHOW' commands only) |
4100 | from LEX struct and fill lookup_field_vals struct field |
4101 | with these values. |
4102 | |
4103 | @param[in] thd thread handler |
4104 | @param[in] cond WHERE condition |
4105 | @param[in] tables I_S table |
4106 | @param[in, out] lookup_field_values Struct which holds lookup values |
4107 | |
4108 | @return |
4109 | 0 success |
4110 | 1 error, there can be no matching records for the condition |
4111 | */ |
4112 | |
4113 | bool get_lookup_field_values(THD *thd, COND *cond, TABLE_LIST *tables, |
4114 | LOOKUP_FIELD_VALUES *lookup_field_values) |
4115 | { |
4116 | LEX *lex= thd->lex; |
4117 | String *wild= lex->wild; |
4118 | bool rc= 0; |
4119 | |
4120 | bzero((char*) lookup_field_values, sizeof(LOOKUP_FIELD_VALUES)); |
4121 | switch (lex->sql_command) { |
4122 | case SQLCOM_SHOW_PLUGINS: |
4123 | if (lex->ident.str) |
4124 | { |
4125 | thd->make_lex_string(&lookup_field_values->db_value, |
4126 | lex->ident.str, lex->ident.length); |
4127 | break; |
4128 | } |
4129 | /* fall through */ |
4130 | case SQLCOM_SHOW_GENERIC: |
4131 | case SQLCOM_SHOW_DATABASES: |
4132 | if (wild) |
4133 | { |
4134 | thd->make_lex_string(&lookup_field_values->db_value, |
4135 | wild->ptr(), wild->length()); |
4136 | lookup_field_values->wild_db_value= 1; |
4137 | } |
4138 | break; |
4139 | case SQLCOM_SHOW_TABLES: |
4140 | case SQLCOM_SHOW_TABLE_STATUS: |
4141 | case SQLCOM_SHOW_TRIGGERS: |
4142 | case SQLCOM_SHOW_EVENTS: |
4143 | thd->make_lex_string(&lookup_field_values->db_value, |
4144 | lex->select_lex.db.str, lex->select_lex.db.length); |
4145 | if (wild) |
4146 | { |
4147 | thd->make_lex_string(&lookup_field_values->table_value, |
4148 | wild->ptr(), wild->length()); |
4149 | lookup_field_values->wild_table_value= 1; |
4150 | } |
4151 | break; |
4152 | default: |
4153 | /* |
4154 | The "default" is for queries over I_S. |
4155 | All previous cases handle SHOW commands. |
4156 | */ |
4157 | rc= calc_lookup_values_from_cond(thd, cond, tables, lookup_field_values); |
4158 | break; |
4159 | } |
4160 | |
4161 | if (lower_case_table_names && !rc) |
4162 | { |
4163 | /* |
4164 | We can safely do in-place upgrades here since all of the above cases |
4165 | are allocating a new memory buffer for these strings. |
4166 | */ |
4167 | if (lookup_field_values->db_value.str && lookup_field_values->db_value.str[0]) |
4168 | my_casedn_str(system_charset_info, |
4169 | (char*) lookup_field_values->db_value.str); |
4170 | if (lookup_field_values->table_value.str && |
4171 | lookup_field_values->table_value.str[0]) |
4172 | my_casedn_str(system_charset_info, |
4173 | (char*) lookup_field_values->table_value.str); |
4174 | } |
4175 | |
4176 | return rc; |
4177 | } |
4178 | |
4179 | |
4180 | enum enum_schema_tables get_schema_table_idx(ST_SCHEMA_TABLE *schema_table) |
4181 | { |
4182 | return (enum enum_schema_tables) (schema_table - &schema_tables[0]); |
4183 | } |
4184 | |
4185 | |
4186 | /* |
4187 | Create db names list. Information schema name always is first in list |
4188 | |
4189 | SYNOPSIS |
4190 | make_db_list() |
4191 | thd thread handler |
4192 | files list of db names |
4193 | wild wild string |
4194 | idx_field_vals idx_field_vals->db_name contains db name or |
4195 | wild string |
4196 | |
4197 | RETURN |
4198 | zero success |
4199 | non-zero error |
4200 | */ |
4201 | |
4202 | static int make_db_list(THD *thd, Dynamic_array<LEX_CSTRING*> *files, |
4203 | LOOKUP_FIELD_VALUES *lookup_field_vals) |
4204 | { |
4205 | if (lookup_field_vals->wild_db_value) |
4206 | { |
4207 | /* |
4208 | This part of code is only for SHOW DATABASES command. |
4209 | idx_field_vals->db_value can be 0 when we don't use |
4210 | LIKE clause (see also get_index_field_values() function) |
4211 | */ |
4212 | if (!lookup_field_vals->db_value.str || |
4213 | !wild_case_compare(system_charset_info, |
4214 | INFORMATION_SCHEMA_NAME.str, |
4215 | lookup_field_vals->db_value.str)) |
4216 | { |
4217 | if (files->append_val(&INFORMATION_SCHEMA_NAME)) |
4218 | return 1; |
4219 | } |
4220 | return find_files(thd, files, 0, mysql_data_home, |
4221 | &lookup_field_vals->db_value); |
4222 | } |
4223 | |
4224 | |
4225 | /* |
4226 | If we have db lookup value we just add it to list and |
4227 | exit from the function. |
4228 | We don't do this for database names longer than the maximum |
4229 | name length. |
4230 | */ |
4231 | if (lookup_field_vals->db_value.str) |
4232 | { |
4233 | if (lookup_field_vals->db_value.length > NAME_LEN) |
4234 | { |
4235 | /* |
4236 | Impossible value for a database name, |
4237 | found in a WHERE DATABASE_NAME = 'xxx' clause. |
4238 | */ |
4239 | return 0; |
4240 | } |
4241 | |
4242 | if (is_infoschema_db(&lookup_field_vals->db_value)) |
4243 | { |
4244 | if (files->append_val(&INFORMATION_SCHEMA_NAME)) |
4245 | return 1; |
4246 | return 0; |
4247 | } |
4248 | if (files->append_val(&lookup_field_vals->db_value)) |
4249 | return 1; |
4250 | return 0; |
4251 | } |
4252 | |
4253 | /* |
4254 | Create list of existing databases. It is used in case |
4255 | of select from information schema table |
4256 | */ |
4257 | if (files->append_val(&INFORMATION_SCHEMA_NAME)) |
4258 | return 1; |
4259 | return find_files(thd, files, 0, mysql_data_home, &null_clex_str); |
4260 | } |
4261 | |
4262 | |
4263 | struct st_add_schema_table |
4264 | { |
4265 | Dynamic_array<LEX_CSTRING*> *files; |
4266 | const char *wild; |
4267 | }; |
4268 | |
4269 | |
4270 | static my_bool add_schema_table(THD *thd, plugin_ref plugin, |
4271 | void* p_data) |
4272 | { |
4273 | LEX_CSTRING *file_name= 0; |
4274 | st_add_schema_table *data= (st_add_schema_table *)p_data; |
4275 | Dynamic_array<LEX_CSTRING*> *file_list= data->files; |
4276 | const char *wild= data->wild; |
4277 | ST_SCHEMA_TABLE *schema_table= plugin_data(plugin, ST_SCHEMA_TABLE *); |
4278 | DBUG_ENTER("add_schema_table" ); |
4279 | |
4280 | if (schema_table->hidden) |
4281 | DBUG_RETURN(0); |
4282 | if (wild) |
4283 | { |
4284 | if (lower_case_table_names) |
4285 | { |
4286 | if (wild_case_compare(files_charset_info, |
4287 | schema_table->table_name, |
4288 | wild)) |
4289 | DBUG_RETURN(0); |
4290 | } |
4291 | else if (wild_compare(schema_table->table_name, wild, 0)) |
4292 | DBUG_RETURN(0); |
4293 | } |
4294 | |
4295 | if ((file_name= thd->make_clex_string(schema_table->table_name, |
4296 | strlen(schema_table->table_name))) && |
4297 | !file_list->append(file_name)) |
4298 | DBUG_RETURN(0); |
4299 | DBUG_RETURN(1); |
4300 | } |
4301 | |
4302 | |
4303 | int schema_tables_add(THD *thd, Dynamic_array<LEX_CSTRING*> *files, |
4304 | const char *wild) |
4305 | { |
4306 | LEX_CSTRING *file_name; |
4307 | ST_SCHEMA_TABLE *tmp_schema_table= schema_tables; |
4308 | st_add_schema_table add_data; |
4309 | DBUG_ENTER("schema_tables_add" ); |
4310 | |
4311 | for (; tmp_schema_table->table_name; tmp_schema_table++) |
4312 | { |
4313 | if (tmp_schema_table->hidden) |
4314 | continue; |
4315 | if (wild) |
4316 | { |
4317 | if (lower_case_table_names) |
4318 | { |
4319 | if (wild_case_compare(files_charset_info, |
4320 | tmp_schema_table->table_name, |
4321 | wild)) |
4322 | continue; |
4323 | } |
4324 | else if (wild_compare(tmp_schema_table->table_name, wild, 0)) |
4325 | continue; |
4326 | } |
4327 | if ((file_name= |
4328 | thd->make_clex_string(tmp_schema_table->table_name, |
4329 | strlen(tmp_schema_table->table_name))) && |
4330 | !files->append(file_name)) |
4331 | continue; |
4332 | DBUG_RETURN(1); |
4333 | } |
4334 | |
4335 | add_data.files= files; |
4336 | add_data.wild= wild; |
4337 | if (plugin_foreach(thd, add_schema_table, |
4338 | MYSQL_INFORMATION_SCHEMA_PLUGIN, &add_data)) |
4339 | DBUG_RETURN(1); |
4340 | |
4341 | DBUG_RETURN(0); |
4342 | } |
4343 | |
4344 | |
4345 | /** |
4346 | @brief Create table names list |
4347 | |
4348 | @details The function creates the list of table names in |
4349 | database |
4350 | |
4351 | @param[in] thd thread handler |
4352 | @param[in] table_names List of table names in database |
4353 | @param[in] lex pointer to LEX struct |
4354 | @param[in] lookup_field_vals pointer to LOOKUP_FIELD_VALUE struct |
4355 | @param[in] db_name database name |
4356 | |
4357 | @return Operation status |
4358 | @retval 0 ok |
4359 | @retval 1 fatal error |
4360 | @retval 2 Not fatal error; Safe to ignore this file list |
4361 | */ |
4362 | |
4363 | static int |
4364 | make_table_name_list(THD *thd, Dynamic_array<LEX_CSTRING*> *table_names, |
4365 | LEX *lex, LOOKUP_FIELD_VALUES *lookup_field_vals, |
4366 | LEX_CSTRING *db_name) |
4367 | { |
4368 | char path[FN_REFLEN + 1]; |
4369 | build_table_filename(path, sizeof(path) - 1, db_name->str, "" , "" , 0); |
4370 | if (!lookup_field_vals->wild_table_value && |
4371 | lookup_field_vals->table_value.str) |
4372 | { |
4373 | if (lookup_field_vals->table_value.length > NAME_LEN) |
4374 | { |
4375 | /* |
4376 | Impossible value for a table name, |
4377 | found in a WHERE TABLE_NAME = 'xxx' clause. |
4378 | */ |
4379 | return 0; |
4380 | } |
4381 | if (db_name == &INFORMATION_SCHEMA_NAME) |
4382 | { |
4383 | LEX_CSTRING *name; |
4384 | ST_SCHEMA_TABLE *schema_table= |
4385 | find_schema_table(thd, &lookup_field_vals->table_value); |
4386 | if (schema_table && !schema_table->hidden) |
4387 | { |
4388 | if (!(name= thd->make_clex_string(schema_table->table_name, |
4389 | strlen(schema_table->table_name))) || |
4390 | table_names->append(name)) |
4391 | return 1; |
4392 | } |
4393 | } |
4394 | else |
4395 | { |
4396 | if (table_names->append_val(&lookup_field_vals->table_value)) |
4397 | return 1; |
4398 | } |
4399 | return 0; |
4400 | } |
4401 | |
4402 | /* |
4403 | This call will add all matching the wildcards (if specified) IS tables |
4404 | to the list |
4405 | */ |
4406 | if (db_name == &INFORMATION_SCHEMA_NAME) |
4407 | return (schema_tables_add(thd, table_names, |
4408 | lookup_field_vals->table_value.str)); |
4409 | |
4410 | find_files_result res= find_files(thd, table_names, db_name, path, |
4411 | &lookup_field_vals->table_value); |
4412 | if (res != FIND_FILES_OK) |
4413 | { |
4414 | /* |
4415 | Downgrade errors about problems with database directory to |
4416 | warnings if this is not a 'SHOW' command. Another thread |
4417 | may have dropped database, and we may still have a name |
4418 | for that directory. |
4419 | */ |
4420 | if (res == FIND_FILES_DIR) |
4421 | { |
4422 | if (is_show_command(thd)) |
4423 | return 1; |
4424 | thd->clear_error(); |
4425 | return 2; |
4426 | } |
4427 | return 1; |
4428 | } |
4429 | return 0; |
4430 | } |
4431 | |
4432 | |
4433 | static void get_table_engine_for_i_s(THD *thd, char *buf, TABLE_LIST *tl, |
4434 | LEX_CSTRING *db, LEX_CSTRING *table) |
4435 | { |
4436 | LEX_CSTRING engine_name= { buf, 0 }; |
4437 | |
4438 | if (thd->get_stmt_da()->sql_errno() == ER_UNKNOWN_STORAGE_ENGINE) |
4439 | { |
4440 | char path[FN_REFLEN]; |
4441 | build_table_filename(path, sizeof(path) - 1, |
4442 | db->str, table->str, reg_ext, 0); |
4443 | bool is_sequence; |
4444 | if (dd_frm_type(thd, path, &engine_name, &is_sequence) == TABLE_TYPE_NORMAL) |
4445 | tl->option= engine_name.str; |
4446 | } |
4447 | } |
4448 | |
4449 | |
4450 | /** |
4451 | Fill I_S table with data obtained by performing full-blown table open. |
4452 | |
4453 | @param thd Thread handler. |
4454 | @param is_show_fields_or_keys Indicates whether it is a legacy SHOW |
4455 | COLUMNS or SHOW KEYS statement. |
4456 | @param table TABLE object for I_S table to be filled. |
4457 | @param schema_table I_S table description structure. |
4458 | @param orig_db_name Database name. |
4459 | @param orig_table_name Table name. |
4460 | @param open_tables_state_backup Open_tables_state object which is used |
4461 | to save/restore original status of |
4462 | variables related to open tables state. |
4463 | @param can_deadlock Indicates that deadlocks are possible |
4464 | due to metadata locks, so to avoid |
4465 | them we should not wait in case if |
4466 | conflicting lock is present. |
4467 | |
4468 | @retval FALSE - Success. |
4469 | @retval TRUE - Failure. |
4470 | */ |
4471 | static bool |
4472 | fill_schema_table_by_open(THD *thd, MEM_ROOT *mem_root, |
4473 | bool is_show_fields_or_keys, |
4474 | TABLE *table, ST_SCHEMA_TABLE *schema_table, |
4475 | LEX_CSTRING *orig_db_name, |
4476 | LEX_CSTRING *orig_table_name, |
4477 | Open_tables_backup *open_tables_state_backup, |
4478 | bool can_deadlock) |
4479 | { |
4480 | Query_arena i_s_arena(mem_root, |
4481 | Query_arena::STMT_CONVENTIONAL_EXECUTION), |
4482 | backup_arena, *old_arena; |
4483 | LEX *old_lex= thd->lex, temp_lex, *lex; |
4484 | LEX_CSTRING db_name, table_name; |
4485 | TABLE_LIST *table_list; |
4486 | bool result= true; |
4487 | DBUG_ENTER("fill_schema_table_by_open" ); |
4488 | |
4489 | /* |
4490 | When a view is opened its structures are allocated on a permanent |
4491 | statement arena and linked into the LEX tree for the current statement |
4492 | (this happens even in cases when view is handled through TEMPTABLE |
4493 | algorithm). |
4494 | |
4495 | To prevent this process from unnecessary hogging of memory in the permanent |
4496 | arena of our I_S query and to avoid damaging its LEX we use temporary |
4497 | arena and LEX for table/view opening. |
4498 | |
4499 | Use temporary arena instead of statement permanent arena. Also make |
4500 | it active arena and save original one for successive restoring. |
4501 | */ |
4502 | old_arena= thd->stmt_arena; |
4503 | thd->stmt_arena= &i_s_arena; |
4504 | thd->set_n_backup_active_arena(&i_s_arena, &backup_arena); |
4505 | |
4506 | /* Prepare temporary LEX. */ |
4507 | thd->lex= lex= &temp_lex; |
4508 | lex_start(thd); |
4509 | lex->sql_command= old_lex->sql_command; |
4510 | |
4511 | /* Disable constant subquery evaluation as we won't be locking tables. */ |
4512 | lex->context_analysis_only= CONTEXT_ANALYSIS_ONLY_VIEW; |
4513 | |
4514 | /* |
4515 | Some of process_table() functions rely on wildcard being passed from |
4516 | old LEX (or at least being initialized). |
4517 | */ |
4518 | lex->wild= old_lex->wild; |
4519 | |
4520 | /* |
4521 | Since make_table_list() might change database and table name passed |
4522 | to it (if lower_case_table_names) we create copies of orig_db_name and |
4523 | orig_table_name here. These copies are used for make_table_list() |
4524 | while unaltered values are passed to process_table() functions. |
4525 | */ |
4526 | if (!thd->make_lex_string(&db_name, |
4527 | orig_db_name->str, orig_db_name->length) || |
4528 | !thd->make_lex_string(&table_name, |
4529 | orig_table_name->str, orig_table_name->length)) |
4530 | goto end; |
4531 | |
4532 | /* |
4533 | Create table list element for table to be open. Link it with the |
4534 | temporary LEX. The latter is required to correctly open views and |
4535 | produce table describing their structure. |
4536 | */ |
4537 | if (make_table_list(thd, &lex->select_lex, &db_name, &table_name)) |
4538 | goto end; |
4539 | |
4540 | table_list= lex->select_lex.table_list.first; |
4541 | |
4542 | if (is_show_fields_or_keys) |
4543 | { |
4544 | /* |
4545 | Restore thd->temporary_tables to be able to process |
4546 | temporary tables (only for 'show index' & 'show columns'). |
4547 | This should be changed when processing of temporary tables for |
4548 | I_S tables will be done. |
4549 | */ |
4550 | thd->temporary_tables= open_tables_state_backup->temporary_tables; |
4551 | } |
4552 | else |
4553 | { |
4554 | /* |
4555 | Apply optimization flags for table opening which are relevant for |
4556 | this I_S table. We can't do this for SHOW COLUMNS/KEYS because of |
4557 | backward compatibility. |
4558 | */ |
4559 | table_list->i_s_requested_object= schema_table->i_s_requested_object; |
4560 | } |
4561 | |
4562 | DBUG_ASSERT(thd->lex == lex); |
4563 | result= open_tables_only_view_structure(thd, table_list, can_deadlock); |
4564 | |
4565 | DEBUG_SYNC(thd, "after_open_table_ignore_flush" ); |
4566 | |
4567 | /* |
4568 | XXX: show_table_list has a flag i_is_requested, |
4569 | and when it's set, open_normal_and_derived_tables() |
4570 | can return an error without setting an error message |
4571 | in THD, which is a hack. This is why we have to |
4572 | check for res, then for thd->is_error() and only then |
4573 | for thd->main_da.sql_errno(). |
4574 | |
4575 | Again we don't do this for SHOW COLUMNS/KEYS because |
4576 | of backward compatibility. |
4577 | */ |
4578 | if (!is_show_fields_or_keys && result && |
4579 | (thd->get_stmt_da()->sql_errno() == ER_NO_SUCH_TABLE || |
4580 | thd->get_stmt_da()->sql_errno() == ER_WRONG_OBJECT || |
4581 | thd->get_stmt_da()->sql_errno() == ER_NOT_SEQUENCE)) |
4582 | { |
4583 | /* |
4584 | Hide error for a non-existing table. |
4585 | For example, this error can occur when we use a where condition |
4586 | with a db name and table, but the table does not exist or |
4587 | there is a view with the same name. |
4588 | */ |
4589 | result= false; |
4590 | thd->clear_error(); |
4591 | } |
4592 | else |
4593 | { |
4594 | char buf[NAME_CHAR_LEN + 1]; |
4595 | if (unlikely(thd->is_error())) |
4596 | get_table_engine_for_i_s(thd, buf, table_list, &db_name, &table_name); |
4597 | |
4598 | result= schema_table->process_table(thd, table_list, |
4599 | table, result, |
4600 | orig_db_name, |
4601 | orig_table_name); |
4602 | } |
4603 | |
4604 | |
4605 | end: |
4606 | lex->unit.cleanup(); |
4607 | |
4608 | /* Restore original LEX value, statement's arena and THD arena values. */ |
4609 | lex_end(thd->lex); |
4610 | |
4611 | // Free items, before restoring backup_arena below. |
4612 | DBUG_ASSERT(i_s_arena.free_list == NULL); |
4613 | thd->free_items(); |
4614 | |
4615 | /* |
4616 | For safety reset list of open temporary tables before closing |
4617 | all tables open within this Open_tables_state. |
4618 | */ |
4619 | thd->temporary_tables= NULL; |
4620 | |
4621 | close_thread_tables(thd); |
4622 | /* |
4623 | Release metadata lock we might have acquired. |
4624 | See comment in fill_schema_table_from_frm() for details. |
4625 | */ |
4626 | thd->mdl_context.rollback_to_savepoint(open_tables_state_backup->mdl_system_tables_svp); |
4627 | |
4628 | thd->lex= old_lex; |
4629 | |
4630 | thd->stmt_arena= old_arena; |
4631 | thd->restore_active_arena(&i_s_arena, &backup_arena); |
4632 | |
4633 | DBUG_RETURN(result); |
4634 | } |
4635 | |
4636 | |
4637 | /** |
4638 | @brief Fill I_S table for SHOW TABLE NAMES commands |
4639 | |
4640 | @param[in] thd thread handler |
4641 | @param[in] table TABLE struct for I_S table |
4642 | @param[in] db_name database name |
4643 | @param[in] table_name table name |
4644 | |
4645 | @return Operation status |
4646 | @retval 0 success |
4647 | @retval 1 error |
4648 | */ |
4649 | |
4650 | static int fill_schema_table_names(THD *thd, TABLE_LIST *tables, |
4651 | LEX_CSTRING *db_name, |
4652 | LEX_CSTRING *table_name) |
4653 | { |
4654 | TABLE *table= tables->table; |
4655 | if (db_name == &INFORMATION_SCHEMA_NAME) |
4656 | { |
4657 | table->field[3]->store(STRING_WITH_LEN("SYSTEM VIEW" ), |
4658 | system_charset_info); |
4659 | } |
4660 | else if (tables->table_open_method != SKIP_OPEN_TABLE) |
4661 | { |
4662 | CHARSET_INFO *cs= system_charset_info; |
4663 | handlerton *hton; |
4664 | bool is_sequence; |
4665 | if (ha_table_exists(thd, db_name, table_name, &hton, &is_sequence)) |
4666 | { |
4667 | if (hton == view_pseudo_hton) |
4668 | table->field[3]->store(STRING_WITH_LEN("VIEW" ), cs); |
4669 | else if (is_sequence) |
4670 | table->field[3]->store(STRING_WITH_LEN("SEQUENCE" ), cs); |
4671 | else |
4672 | table->field[3]->store(STRING_WITH_LEN("BASE TABLE" ), cs); |
4673 | } |
4674 | else |
4675 | table->field[3]->store(STRING_WITH_LEN("ERROR" ), cs); |
4676 | |
4677 | if (unlikely(thd->is_error() && |
4678 | thd->get_stmt_da()->sql_errno() == ER_NO_SUCH_TABLE)) |
4679 | { |
4680 | thd->clear_error(); |
4681 | return 0; |
4682 | } |
4683 | } |
4684 | if (unlikely(schema_table_store_record(thd, table))) |
4685 | return 1; |
4686 | return 0; |
4687 | } |
4688 | |
4689 | |
4690 | /** |
4691 | @brief Get open table method |
4692 | |
4693 | @details The function calculates the method which will be used |
4694 | for table opening: |
4695 | SKIP_OPEN_TABLE - do not open table |
4696 | OPEN_FRM_ONLY - open FRM file only |
4697 | OPEN_FULL_TABLE - open FRM, data, index files |
4698 | @param[in] tables I_S table table_list |
4699 | @param[in] schema_table I_S table struct |
4700 | @param[in] schema_table_idx I_S table index |
4701 | |
4702 | @return return a set of flags |
4703 | @retval SKIP_OPEN_TABLE | OPEN_FRM_ONLY | OPEN_FULL_TABLE |
4704 | */ |
4705 | |
4706 | uint get_table_open_method(TABLE_LIST *tables, |
4707 | ST_SCHEMA_TABLE *schema_table, |
4708 | enum enum_schema_tables schema_table_idx) |
4709 | { |
4710 | /* |
4711 | determine which method will be used for table opening |
4712 | */ |
4713 | if (schema_table->i_s_requested_object & OPTIMIZE_I_S_TABLE) |
4714 | { |
4715 | Field **ptr, *field; |
4716 | int table_open_method= 0, field_indx= 0; |
4717 | uint star_table_open_method= OPEN_FULL_TABLE; |
4718 | bool used_star= true; // true if '*' is used in select |
4719 | for (ptr=tables->table->field; (field= *ptr) ; ptr++) |
4720 | { |
4721 | star_table_open_method= |
4722 | MY_MIN(star_table_open_method, |
4723 | schema_table->fields_info[field_indx].open_method); |
4724 | if (bitmap_is_set(tables->table->read_set, field->field_index)) |
4725 | { |
4726 | used_star= false; |
4727 | table_open_method|= schema_table->fields_info[field_indx].open_method; |
4728 | } |
4729 | field_indx++; |
4730 | } |
4731 | if (used_star) |
4732 | return star_table_open_method; |
4733 | return table_open_method; |
4734 | } |
4735 | /* I_S tables which use get_all_tables but can not be optimized */ |
4736 | return (uint) OPEN_FULL_TABLE; |
4737 | } |
4738 | |
4739 | |
4740 | /** |
4741 | Try acquire high priority share metadata lock on a table (with |
4742 | optional wait for conflicting locks to go away). |
4743 | |
4744 | @param thd Thread context. |
4745 | @param mdl_request Pointer to memory to be used for MDL_request |
4746 | object for a lock request. |
4747 | @param table Table list element for the table |
4748 | @param can_deadlock Indicates that deadlocks are possible due to |
4749 | metadata locks, so to avoid them we should not |
4750 | wait in case if conflicting lock is present. |
4751 | |
4752 | @note This is an auxiliary function to be used in cases when we want to |
4753 | access table's description by looking up info in TABLE_SHARE without |
4754 | going through full-blown table open. |
4755 | @note This function assumes that there are no other metadata lock requests |
4756 | in the current metadata locking context. |
4757 | |
4758 | @retval FALSE No error, if lock was obtained TABLE_LIST::mdl_request::ticket |
4759 | is set to non-NULL value. |
4760 | @retval TRUE Some error occurred (probably thread was killed). |
4761 | */ |
4762 | |
4763 | static bool |
4764 | try_acquire_high_prio_shared_mdl_lock(THD *thd, TABLE_LIST *table, |
4765 | bool can_deadlock) |
4766 | { |
4767 | bool error; |
4768 | table->mdl_request.init(MDL_key::TABLE, table->db.str, table->table_name.str, |
4769 | MDL_SHARED_HIGH_PRIO, MDL_TRANSACTION); |
4770 | |
4771 | if (can_deadlock) |
4772 | { |
4773 | /* |
4774 | When .FRM is being open in order to get data for an I_S table, |
4775 | we might have some tables not only open but also locked. |
4776 | E.g. this happens when a SHOW or I_S statement is run |
4777 | under LOCK TABLES or inside a stored function. |
4778 | By waiting for the conflicting metadata lock to go away we |
4779 | might create a deadlock which won't entirely belong to the |
4780 | MDL subsystem and thus won't be detectable by this subsystem's |
4781 | deadlock detector. To avoid such situation, when there are |
4782 | other locked tables, we prefer not to wait on a conflicting |
4783 | lock. |
4784 | */ |
4785 | error= thd->mdl_context.try_acquire_lock(&table->mdl_request); |
4786 | } |
4787 | else |
4788 | error= thd->mdl_context.acquire_lock(&table->mdl_request, |
4789 | thd->variables.lock_wait_timeout); |
4790 | |
4791 | return error; |
4792 | } |
4793 | |
4794 | |
4795 | /** |
4796 | @brief Fill I_S table with data from FRM file only |
4797 | |
4798 | @param[in] thd thread handler |
4799 | @param[in] table TABLE struct for I_S table |
4800 | @param[in] schema_table I_S table struct |
4801 | @param[in] db_name database name |
4802 | @param[in] table_name table name |
4803 | @param[in] schema_table_idx I_S table index |
4804 | @param[in] open_tables_state_backup Open_tables_state object which is used |
4805 | to save/restore original state of metadata |
4806 | locks. |
4807 | @param[in] can_deadlock Indicates that deadlocks are possible |
4808 | due to metadata locks, so to avoid |
4809 | them we should not wait in case if |
4810 | conflicting lock is present. |
4811 | |
4812 | @return Operation status |
4813 | @retval 0 Table is processed and we can continue |
4814 | with new table |
4815 | @retval 1 It's view and we have to use |
4816 | open_tables function for this table |
4817 | */ |
4818 | |
4819 | static int fill_schema_table_from_frm(THD *thd, TABLE *table, |
4820 | ST_SCHEMA_TABLE *schema_table, |
4821 | LEX_CSTRING *db_name, |
4822 | LEX_CSTRING *table_name, |
4823 | Open_tables_backup *open_tables_state_backup, |
4824 | bool can_deadlock) |
4825 | { |
4826 | TABLE_SHARE *share; |
4827 | TABLE tbl; |
4828 | TABLE_LIST table_list; |
4829 | uint res= 0; |
4830 | char db_name_buff[NAME_LEN + 1], table_name_buff[NAME_LEN + 1]; |
4831 | |
4832 | bzero((char*) &table_list, sizeof(TABLE_LIST)); |
4833 | bzero((char*) &tbl, sizeof(TABLE)); |
4834 | |
4835 | DBUG_ASSERT(db_name->length <= NAME_LEN); |
4836 | DBUG_ASSERT(table_name->length <= NAME_LEN); |
4837 | |
4838 | if (lower_case_table_names) |
4839 | { |
4840 | /* |
4841 | In lower_case_table_names > 0 metadata locking and table definition |
4842 | cache subsystems require normalized (lowercased) database and table |
4843 | names as input. |
4844 | */ |
4845 | strmov(db_name_buff, db_name->str); |
4846 | strmov(table_name_buff, table_name->str); |
4847 | table_list.db.length= my_casedn_str(files_charset_info, db_name_buff); |
4848 | table_list.table_name.length= my_casedn_str(files_charset_info, table_name_buff); |
4849 | table_list.db.str= db_name_buff; |
4850 | table_list.table_name.str= table_name_buff; |
4851 | } |
4852 | else |
4853 | { |
4854 | table_list.table_name= *table_name; |
4855 | table_list.db= *db_name; |
4856 | } |
4857 | |
4858 | /* |
4859 | TODO: investigate if in this particular situation we can get by |
4860 | simply obtaining internal lock of the data-dictionary |
4861 | instead of obtaining full-blown metadata lock. |
4862 | */ |
4863 | if (try_acquire_high_prio_shared_mdl_lock(thd, &table_list, can_deadlock)) |
4864 | { |
4865 | /* |
4866 | Some error occurred (most probably we have been killed while |
4867 | waiting for conflicting locks to go away), let the caller to |
4868 | handle the situation. |
4869 | */ |
4870 | return 1; |
4871 | } |
4872 | |
4873 | if (! table_list.mdl_request.ticket) |
4874 | { |
4875 | /* |
4876 | We are in situation when we have encountered conflicting metadata |
4877 | lock and deadlocks can occur due to waiting for it to go away. |
4878 | So instead of waiting skip this table with an appropriate warning. |
4879 | */ |
4880 | DBUG_ASSERT(can_deadlock); |
4881 | |
4882 | push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, |
4883 | ER_WARN_I_S_SKIPPED_TABLE, |
4884 | ER_THD(thd, ER_WARN_I_S_SKIPPED_TABLE), |
4885 | table_list.db.str, table_list.table_name.str); |
4886 | return 0; |
4887 | } |
4888 | |
4889 | if (schema_table->i_s_requested_object & OPEN_TRIGGER_ONLY) |
4890 | { |
4891 | init_sql_alloc(&tbl.mem_root, "fill_schema_table_from_frm" , |
4892 | TABLE_ALLOC_BLOCK_SIZE, 0, MYF(0)); |
4893 | if (!Table_triggers_list::check_n_load(thd, db_name, |
4894 | table_name, &tbl, 1)) |
4895 | { |
4896 | table_list.table= &tbl; |
4897 | res= schema_table->process_table(thd, &table_list, table, |
4898 | res, db_name, table_name); |
4899 | delete tbl.triggers; |
4900 | } |
4901 | free_root(&tbl.mem_root, MYF(0)); |
4902 | goto end; |
4903 | } |
4904 | |
4905 | share= tdc_acquire_share(thd, &table_list, GTS_TABLE | GTS_VIEW); |
4906 | if (!share) |
4907 | { |
4908 | if (thd->get_stmt_da()->sql_errno() == ER_NO_SUCH_TABLE || |
4909 | thd->get_stmt_da()->sql_errno() == ER_WRONG_OBJECT || |
4910 | thd->get_stmt_da()->sql_errno() == ER_NOT_SEQUENCE) |
4911 | { |
4912 | res= 0; |
4913 | } |
4914 | else |
4915 | { |
4916 | char buf[NAME_CHAR_LEN + 1]; |
4917 | get_table_engine_for_i_s(thd, buf, &table_list, db_name, table_name); |
4918 | |
4919 | res= schema_table->process_table(thd, &table_list, table, |
4920 | true, db_name, table_name); |
4921 | } |
4922 | goto end; |
4923 | } |
4924 | |
4925 | if (share->is_view) |
4926 | { |
4927 | if (schema_table->i_s_requested_object & OPEN_TABLE_ONLY) |
4928 | { |
4929 | /* skip view processing */ |
4930 | res= 0; |
4931 | goto end_share; |
4932 | } |
4933 | else if (schema_table->i_s_requested_object & OPEN_VIEW_FULL) |
4934 | { |
4935 | /* |
4936 | tell get_all_tables() to fall back to |
4937 | open_normal_and_derived_tables() |
4938 | */ |
4939 | res= 1; |
4940 | goto end_share; |
4941 | } |
4942 | |
4943 | if (mysql_make_view(thd, share, &table_list, true)) |
4944 | goto end_share; |
4945 | table_list.view= (LEX*) share->is_view; |
4946 | res= schema_table->process_table(thd, &table_list, table, |
4947 | res, db_name, table_name); |
4948 | goto end_share; |
4949 | } |
4950 | |
4951 | if (!open_table_from_share(thd, share, table_name, 0, |
4952 | (EXTRA_RECORD | OPEN_FRM_FILE_ONLY), |
4953 | thd->open_options, &tbl, FALSE)) |
4954 | { |
4955 | tbl.s= share; |
4956 | table_list.table= &tbl; |
4957 | table_list.view= (LEX*) share->is_view; |
4958 | res= schema_table->process_table(thd, &table_list, table, |
4959 | res, db_name, table_name); |
4960 | closefrm(&tbl); |
4961 | } |
4962 | |
4963 | |
4964 | end_share: |
4965 | tdc_release_share(share); |
4966 | |
4967 | end: |
4968 | /* |
4969 | Release metadata lock we might have acquired. |
4970 | |
4971 | Without this step metadata locks acquired for each table processed |
4972 | will be accumulated. In situation when a lot of tables are processed |
4973 | by I_S query this will result in transaction with too many metadata |
4974 | locks. As result performance of acquisition of new lock will suffer. |
4975 | |
4976 | Of course, the fact that we don't hold metadata lock on tables which |
4977 | were processed till the end of I_S query makes execution less isolated |
4978 | from concurrent DDL. Consequently one might get 'dirty' results from |
4979 | such a query. But we have never promised serializability of I_S queries |
4980 | anyway. |
4981 | |
4982 | We don't have any tables open since we took backup, so rolling back to |
4983 | savepoint is safe. |
4984 | */ |
4985 | DBUG_ASSERT(thd->open_tables == NULL); |
4986 | thd->mdl_context.rollback_to_savepoint(open_tables_state_backup->mdl_system_tables_svp); |
4987 | thd->clear_error(); |
4988 | return res; |
4989 | } |
4990 | |
4991 | |
4992 | class Warnings_only_error_handler : public Internal_error_handler |
4993 | { |
4994 | public: |
4995 | bool handle_condition(THD *thd, |
4996 | uint sql_errno, |
4997 | const char* sqlstate, |
4998 | Sql_condition::enum_warning_level *level, |
4999 | const char* msg, |
5000 | Sql_condition ** cond_hdl) |
5001 | { |
5002 | if (sql_errno == ER_PARSE_ERROR || |
5003 | sql_errno == ER_TRG_NO_DEFINER || |
5004 | sql_errno == ER_TRG_NO_CREATION_CTX) |
5005 | return true; |
5006 | |
5007 | if (*level != Sql_condition::WARN_LEVEL_ERROR) |
5008 | return false; |
5009 | |
5010 | if (likely(!thd->get_stmt_da()->is_error())) |
5011 | thd->get_stmt_da()->set_error_status(sql_errno, msg, sqlstate, *cond_hdl); |
5012 | return true; // handled! |
5013 | } |
5014 | }; |
5015 | |
5016 | /** |
5017 | @brief Fill I_S tables whose data are retrieved |
5018 | from frm files and storage engine |
5019 | |
5020 | @details The information schema tables are internally represented as |
5021 | temporary tables that are filled at query execution time. |
5022 | Those I_S tables whose data are retrieved |
5023 | from frm files and storage engine are filled by the function |
5024 | get_all_tables(). |
5025 | |
5026 | @note This function assumes optimize_for_get_all_tables() has been |
5027 | run for the table and produced a "read plan" in |
5028 | tables->is_table_read_plan. |
5029 | |
5030 | @param[in] thd thread handler |
5031 | @param[in] tables I_S table |
5032 | @param[in] cond 'WHERE' condition |
5033 | |
5034 | @return Operation status |
5035 | @retval 0 success |
5036 | @retval 1 error |
5037 | */ |
5038 | |
5039 | int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) |
5040 | { |
5041 | LEX *lex= thd->lex; |
5042 | TABLE *table= tables->table; |
5043 | TABLE_LIST table_acl_check; |
5044 | SELECT_LEX *lsel= tables->schema_select_lex; |
5045 | ST_SCHEMA_TABLE *schema_table= tables->schema_table; |
5046 | IS_table_read_plan *plan= tables->is_table_read_plan; |
5047 | enum enum_schema_tables schema_table_idx; |
5048 | Dynamic_array<LEX_CSTRING*> db_names; |
5049 | Item *partial_cond= plan->partial_cond; |
5050 | int error= 1; |
5051 | Open_tables_backup open_tables_state_backup; |
5052 | #ifndef NO_EMBEDDED_ACCESS_CHECKS |
5053 | Security_context *sctx= thd->security_ctx; |
5054 | #endif |
5055 | uint table_open_method= tables->table_open_method; |
5056 | bool can_deadlock; |
5057 | MEM_ROOT tmp_mem_root; |
5058 | DBUG_ENTER("get_all_tables" ); |
5059 | |
5060 | bzero(&tmp_mem_root, sizeof(tmp_mem_root)); |
5061 | |
5062 | /* |
5063 | In cases when SELECT from I_S table being filled by this call is |
5064 | part of statement which also uses other tables or is being executed |
5065 | under LOCK TABLES or is part of transaction which also uses other |
5066 | tables waiting for metadata locks which happens below might result |
5067 | in deadlocks. |
5068 | To avoid them we don't wait if conflicting metadata lock is |
5069 | encountered and skip table with emitting an appropriate warning. |
5070 | */ |
5071 | can_deadlock= thd->mdl_context.has_locks(); |
5072 | |
5073 | /* |
5074 | We should not introduce deadlocks even if we already have some |
5075 | tables open and locked, since we won't lock tables which we will |
5076 | open and will ignore pending exclusive metadata locks for these |
5077 | tables by using high-priority requests for shared metadata locks. |
5078 | */ |
5079 | thd->reset_n_backup_open_tables_state(&open_tables_state_backup); |
5080 | |
5081 | schema_table_idx= get_schema_table_idx(schema_table); |
5082 | /* |
5083 | this branch processes SHOW FIELDS, SHOW INDEXES commands. |
5084 | see sql_parse.cc, prepare_schema_table() function where |
5085 | this values are initialized |
5086 | */ |
5087 | if (lsel && lsel->table_list.first) |
5088 | { |
5089 | error= fill_schema_table_by_open(thd, thd->mem_root, TRUE, |
5090 | table, schema_table, |
5091 | &lsel->table_list.first->db, |
5092 | &lsel->table_list.first->table_name, |
5093 | &open_tables_state_backup, |
5094 | can_deadlock); |
5095 | goto err; |
5096 | } |
5097 | |
5098 | if (plan->no_rows) |
5099 | { |
5100 | error= 0; |
5101 | goto err; |
5102 | } |
5103 | |
5104 | if (lex->describe) |
5105 | { |
5106 | /* EXPLAIN SELECT */ |
5107 | error= 0; |
5108 | goto err; |
5109 | } |
5110 | |
5111 | bzero((char*) &table_acl_check, sizeof(table_acl_check)); |
5112 | |
5113 | if (make_db_list(thd, &db_names, &plan->lookup_field_vals)) |
5114 | goto err; |
5115 | |
5116 | /* Use tmp_mem_root to allocate data for opened tables */ |
5117 | init_alloc_root(&tmp_mem_root, "get_all_tables" , SHOW_ALLOC_BLOCK_SIZE, |
5118 | SHOW_ALLOC_BLOCK_SIZE, MY_THREAD_SPECIFIC); |
5119 | |
5120 | for (size_t i=0; i < db_names.elements(); i++) |
5121 | { |
5122 | LEX_CSTRING *db_name= db_names.at(i); |
5123 | DBUG_ASSERT(db_name->length <= NAME_LEN); |
5124 | #ifndef NO_EMBEDDED_ACCESS_CHECKS |
5125 | if (!(check_access(thd, SELECT_ACL, db_name->str, |
5126 | &thd->col_access, NULL, 0, 1) || |
5127 | (!thd->col_access && check_grant_db(thd, db_name->str))) || |
5128 | sctx->master_access & (DB_ACLS | SHOW_DB_ACL) || |
5129 | acl_get(sctx->host, sctx->ip, sctx->priv_user, db_name->str, 0)) |
5130 | #endif |
5131 | { |
5132 | Dynamic_array<LEX_CSTRING*> table_names; |
5133 | int res= make_table_name_list(thd, &table_names, lex, |
5134 | &plan->lookup_field_vals, db_name); |
5135 | if (unlikely(res == 2)) /* Not fatal error, continue */ |
5136 | continue; |
5137 | if (unlikely(res)) |
5138 | goto err; |
5139 | |
5140 | for (size_t i=0; i < table_names.elements(); i++) |
5141 | { |
5142 | LEX_CSTRING *table_name= table_names.at(i); |
5143 | DBUG_ASSERT(table_name->length <= NAME_LEN); |
5144 | |
5145 | #ifndef NO_EMBEDDED_ACCESS_CHECKS |
5146 | if (!(thd->col_access & TABLE_ACLS)) |
5147 | { |
5148 | table_acl_check.db= *db_name; |
5149 | table_acl_check.table_name= *table_name; |
5150 | table_acl_check.grant.privilege= thd->col_access; |
5151 | if (check_grant(thd, TABLE_ACLS, &table_acl_check, TRUE, 1, TRUE)) |
5152 | continue; |
5153 | } |
5154 | #endif |
5155 | restore_record(table, s->default_values); |
5156 | table->field[schema_table->idx_field1]-> |
5157 | store(db_name->str, db_name->length, system_charset_info); |
5158 | table->field[schema_table->idx_field2]-> |
5159 | store(table_name->str, table_name->length, system_charset_info); |
5160 | |
5161 | if (!partial_cond || partial_cond->val_int()) |
5162 | { |
5163 | /* |
5164 | If table is I_S.tables and open_table_method is 0 (eg SKIP_OPEN) |
5165 | we can skip table opening and we don't have lookup value for |
5166 | table name or lookup value is wild string(table name list is |
5167 | already created by make_table_name_list() function). |
5168 | */ |
5169 | if (!table_open_method && schema_table_idx == SCH_TABLES && |
5170 | (!plan->lookup_field_vals.table_value.length || |
5171 | plan->lookup_field_vals.wild_table_value)) |
5172 | { |
5173 | table->field[0]->store(STRING_WITH_LEN("def" ), system_charset_info); |
5174 | if (schema_table_store_record(thd, table)) |
5175 | goto err; /* Out of space in temporary table */ |
5176 | continue; |
5177 | } |
5178 | |
5179 | /* SHOW TABLE NAMES command */ |
5180 | if (schema_table_idx == SCH_TABLE_NAMES) |
5181 | { |
5182 | if (fill_schema_table_names(thd, tables, db_name, table_name)) |
5183 | continue; |
5184 | } |
5185 | else if (schema_table_idx == SCH_TRIGGERS && |
5186 | db_name == &INFORMATION_SCHEMA_NAME) |
5187 | { |
5188 | continue; |
5189 | } |
5190 | else |
5191 | { |
5192 | if (!(table_open_method & ~OPEN_FRM_ONLY) && |
5193 | db_name != &INFORMATION_SCHEMA_NAME) |
5194 | { |
5195 | if (!fill_schema_table_from_frm(thd, table, schema_table, |
5196 | db_name, table_name, |
5197 | &open_tables_state_backup, |
5198 | can_deadlock)) |
5199 | continue; |
5200 | } |
5201 | |
5202 | DEBUG_SYNC(thd, "before_open_in_get_all_tables" ); |
5203 | if (fill_schema_table_by_open(thd, &tmp_mem_root, FALSE, |
5204 | table, schema_table, |
5205 | db_name, table_name, |
5206 | &open_tables_state_backup, |
5207 | can_deadlock)) |
5208 | goto err; |
5209 | free_root(&tmp_mem_root, MY_MARK_BLOCKS_FREE); |
5210 | } |
5211 | } |
5212 | } |
5213 | } |
5214 | } |
5215 | |
5216 | error= 0; |
5217 | err: |
5218 | thd->restore_backup_open_tables_state(&open_tables_state_backup); |
5219 | free_root(&tmp_mem_root, 0); |
5220 | |
5221 | DBUG_RETURN(error); |
5222 | } |
5223 | |
5224 | |
5225 | bool store_schema_shemata(THD* thd, TABLE *table, LEX_CSTRING *db_name, |
5226 | CHARSET_INFO *cs) |
5227 | { |
5228 | restore_record(table, s->default_values); |
5229 | table->field[0]->store(STRING_WITH_LEN("def" ), system_charset_info); |
5230 | table->field[1]->store(db_name->str, db_name->length, system_charset_info); |
5231 | table->field[2]->store(cs->csname, strlen(cs->csname), system_charset_info); |
5232 | table->field[3]->store(cs->name, strlen(cs->name), system_charset_info); |
5233 | return schema_table_store_record(thd, table); |
5234 | } |
5235 | |
5236 | |
5237 | int fill_schema_schemata(THD *thd, TABLE_LIST *tables, COND *cond) |
5238 | { |
5239 | /* |
5240 | TODO: fill_schema_shemata() is called when new client is connected. |
5241 | Returning error status in this case leads to client hangup. |
5242 | */ |
5243 | |
5244 | LOOKUP_FIELD_VALUES lookup_field_vals; |
5245 | Dynamic_array<LEX_CSTRING*> db_names; |
5246 | Schema_specification_st create; |
5247 | TABLE *table= tables->table; |
5248 | #ifndef NO_EMBEDDED_ACCESS_CHECKS |
5249 | Security_context *sctx= thd->security_ctx; |
5250 | #endif |
5251 | DBUG_ENTER("fill_schema_shemata" ); |
5252 | |
5253 | if (get_lookup_field_values(thd, cond, tables, &lookup_field_vals)) |
5254 | DBUG_RETURN(0); |
5255 | DBUG_PRINT("INDEX VALUES" ,("db_name: %s table_name: %s" , |
5256 | lookup_field_vals.db_value.str, |
5257 | lookup_field_vals.table_value.str)); |
5258 | if (make_db_list(thd, &db_names, &lookup_field_vals)) |
5259 | DBUG_RETURN(1); |
5260 | |
5261 | /* |
5262 | If we have lookup db value we should check that the database exists |
5263 | */ |
5264 | if(lookup_field_vals.db_value.str && !lookup_field_vals.wild_db_value && |
5265 | db_names.at(0) != &INFORMATION_SCHEMA_NAME) |
5266 | { |
5267 | char path[FN_REFLEN+16]; |
5268 | uint path_len; |
5269 | MY_STAT stat_info; |
5270 | if (!lookup_field_vals.db_value.str[0]) |
5271 | DBUG_RETURN(0); |
5272 | path_len= build_table_filename(path, sizeof(path) - 1, |
5273 | lookup_field_vals.db_value.str, "" , "" , 0); |
5274 | path[path_len-1]= 0; |
5275 | if (!mysql_file_stat(key_file_misc, path, &stat_info, MYF(0))) |
5276 | DBUG_RETURN(0); |
5277 | } |
5278 | |
5279 | for (size_t i=0; i < db_names.elements(); i++) |
5280 | { |
5281 | LEX_CSTRING *db_name= db_names.at(i); |
5282 | DBUG_ASSERT(db_name->length <= NAME_LEN); |
5283 | if (db_name == &INFORMATION_SCHEMA_NAME) |
5284 | { |
5285 | if (store_schema_shemata(thd, table, db_name, |
5286 | system_charset_info)) |
5287 | DBUG_RETURN(1); |
5288 | continue; |
5289 | } |
5290 | #ifndef NO_EMBEDDED_ACCESS_CHECKS |
5291 | if (sctx->master_access & (DB_ACLS | SHOW_DB_ACL) || |
5292 | acl_get(sctx->host, sctx->ip, sctx->priv_user, db_name->str, false) || |
5293 | (sctx->priv_role[0] ? |
5294 | acl_get("" , "" , sctx->priv_role, db_name->str, false) : 0) || |
5295 | !check_grant_db(thd, db_name->str)) |
5296 | #endif |
5297 | { |
5298 | load_db_opt_by_name(thd, db_name->str, &create); |
5299 | if (store_schema_shemata(thd, table, db_name, |
5300 | create.default_table_charset)) |
5301 | DBUG_RETURN(1); |
5302 | } |
5303 | } |
5304 | DBUG_RETURN(0); |
5305 | } |
5306 | |
5307 | |
5308 | static int get_schema_tables_record(THD *thd, TABLE_LIST *tables, |
5309 | TABLE *table, bool res, |
5310 | const LEX_CSTRING *db_name, |
5311 | const LEX_CSTRING *table_name) |
5312 | { |
5313 | const char *tmp_buff; |
5314 | MYSQL_TIME time; |
5315 | int info_error= 0; |
5316 | CHARSET_INFO *cs= system_charset_info; |
5317 | DBUG_ENTER("get_schema_tables_record" ); |
5318 | |
5319 | restore_record(table, s->default_values); |
5320 | table->field[0]->store(STRING_WITH_LEN("def" ), cs); |
5321 | table->field[1]->store(db_name->str, db_name->length, cs); |
5322 | table->field[2]->store(table_name->str, table_name->length, cs); |
5323 | |
5324 | if (res) |
5325 | { |
5326 | /* There was a table open error, so set the table type and return */ |
5327 | if (tables->view) |
5328 | table->field[3]->store(STRING_WITH_LEN("VIEW" ), cs); |
5329 | else if (tables->schema_table) |
5330 | table->field[3]->store(STRING_WITH_LEN("SYSTEM VIEW" ), cs); |
5331 | else |
5332 | table->field[3]->store(STRING_WITH_LEN("BASE TABLE" ), cs); |
5333 | |
5334 | if (tables->option) |
5335 | { |
5336 | table->field[4]->store(tables->option, strlen(tables->option), cs); |
5337 | table->field[4]->set_notnull(); |
5338 | } |
5339 | goto err; |
5340 | } |
5341 | |
5342 | if (tables->view) |
5343 | { |
5344 | table->field[3]->store(STRING_WITH_LEN("VIEW" ), cs); |
5345 | table->field[20]->store(STRING_WITH_LEN("VIEW" ), cs); |
5346 | } |
5347 | else |
5348 | { |
5349 | char option_buff[512]; |
5350 | String str(option_buff,sizeof(option_buff), system_charset_info); |
5351 | TABLE *show_table= tables->table; |
5352 | TABLE_SHARE *share= show_table->s; |
5353 | handler *file= show_table->db_stat ? show_table->file : 0; |
5354 | handlerton *tmp_db_type= share->db_type(); |
5355 | #ifdef WITH_PARTITION_STORAGE_ENGINE |
5356 | bool is_partitioned= FALSE; |
5357 | #endif |
5358 | |
5359 | if (share->tmp_table == SYSTEM_TMP_TABLE) |
5360 | table->field[3]->store(STRING_WITH_LEN("SYSTEM VIEW" ), cs); |
5361 | else if (share->table_type == TABLE_TYPE_SEQUENCE) |
5362 | table->field[3]->store(STRING_WITH_LEN("SEQUENCE" ), cs); |
5363 | else |
5364 | { |
5365 | DBUG_ASSERT(share->tmp_table == NO_TMP_TABLE); |
5366 | if (share->versioned) |
5367 | table->field[3]->store(STRING_WITH_LEN("SYSTEM VERSIONED" ), cs); |
5368 | else |
5369 | table->field[3]->store(STRING_WITH_LEN("BASE TABLE" ), cs); |
5370 | } |
5371 | |
5372 | for (uint i= 4; i < table->s->fields; i++) |
5373 | { |
5374 | if (i == 7 || (i > 12 && i < 17) || i == 18) |
5375 | continue; |
5376 | table->field[i]->set_notnull(); |
5377 | } |
5378 | |
5379 | /* Collect table info from the table share */ |
5380 | |
5381 | #ifdef WITH_PARTITION_STORAGE_ENGINE |
5382 | if (share->db_type() == partition_hton && |
5383 | share->partition_info_str_len) |
5384 | { |
5385 | tmp_db_type= plugin_hton(share->default_part_plugin); |
5386 | is_partitioned= TRUE; |
5387 | } |
5388 | #endif |
5389 | |
5390 | tmp_buff= (char *) ha_resolve_storage_engine_name(tmp_db_type); |
5391 | table->field[4]->store(tmp_buff, strlen(tmp_buff), cs); |
5392 | table->field[5]->store((longlong) share->frm_version, TRUE); |
5393 | |
5394 | str.length(0); |
5395 | |
5396 | if (share->min_rows) |
5397 | { |
5398 | str.qs_append(STRING_WITH_LEN(" min_rows=" )); |
5399 | str.qs_append(share->min_rows); |
5400 | } |
5401 | |
5402 | if (share->max_rows) |
5403 | { |
5404 | str.qs_append(STRING_WITH_LEN(" max_rows=" )); |
5405 | str.qs_append(share->max_rows); |
5406 | } |
5407 | |
5408 | if (share->avg_row_length) |
5409 | { |
5410 | str.qs_append(STRING_WITH_LEN(" avg_row_length=" )); |
5411 | str.qs_append(share->avg_row_length); |
5412 | } |
5413 | |
5414 | if (share->db_create_options & HA_OPTION_PACK_KEYS) |
5415 | str.qs_append(STRING_WITH_LEN(" pack_keys=1" )); |
5416 | |
5417 | if (share->db_create_options & HA_OPTION_NO_PACK_KEYS) |
5418 | str.qs_append(STRING_WITH_LEN(" pack_keys=0" )); |
5419 | |
5420 | if (share->db_create_options & HA_OPTION_STATS_PERSISTENT) |
5421 | str.qs_append(STRING_WITH_LEN(" stats_persistent=1" )); |
5422 | |
5423 | if (share->db_create_options & HA_OPTION_NO_STATS_PERSISTENT) |
5424 | str.qs_append(STRING_WITH_LEN(" stats_persistent=0" )); |
5425 | |
5426 | if (share->stats_auto_recalc == HA_STATS_AUTO_RECALC_ON) |
5427 | str.qs_append(STRING_WITH_LEN(" stats_auto_recalc=1" )); |
5428 | else if (share->stats_auto_recalc == HA_STATS_AUTO_RECALC_OFF) |
5429 | str.qs_append(STRING_WITH_LEN(" stats_auto_recalc=0" )); |
5430 | |
5431 | if (share->stats_sample_pages != 0) |
5432 | { |
5433 | str.qs_append(STRING_WITH_LEN(" stats_sample_pages=" )); |
5434 | str.qs_append(share->stats_sample_pages); |
5435 | } |
5436 | |
5437 | /* We use CHECKSUM, instead of TABLE_CHECKSUM, for backward compability */ |
5438 | if (share->db_create_options & HA_OPTION_CHECKSUM) |
5439 | str.qs_append(STRING_WITH_LEN(" checksum=1" )); |
5440 | |
5441 | if (share->page_checksum != HA_CHOICE_UNDEF) |
5442 | { |
5443 | str.qs_append(STRING_WITH_LEN(" page_checksum=" )); |
5444 | str.qs_append(ha_choice_values[(uint) share->page_checksum]); |
5445 | } |
5446 | |
5447 | if (share->db_create_options & HA_OPTION_DELAY_KEY_WRITE) |
5448 | str.qs_append(STRING_WITH_LEN(" delay_key_write=1" )); |
5449 | |
5450 | if (share->row_type != ROW_TYPE_DEFAULT) |
5451 | { |
5452 | str.qs_append(STRING_WITH_LEN(" row_format=" )); |
5453 | str.qs_append(ha_row_type[(uint) share->row_type]); |
5454 | } |
5455 | |
5456 | if (share->key_block_size) |
5457 | { |
5458 | str.qs_append(STRING_WITH_LEN(" key_block_size=" )); |
5459 | str.qs_append(share->key_block_size); |
5460 | } |
5461 | |
5462 | #ifdef WITH_PARTITION_STORAGE_ENGINE |
5463 | if (is_partitioned) |
5464 | str.qs_append(STRING_WITH_LEN(" partitioned" )); |
5465 | #endif |
5466 | |
5467 | if (share->transactional != HA_CHOICE_UNDEF) |
5468 | { |
5469 | str.qs_append(STRING_WITH_LEN(" transactional=" )); |
5470 | str.qs_append(ha_choice_values[(uint) share->transactional]); |
5471 | } |
5472 | append_create_options(thd, &str, share->option_list, false, 0); |
5473 | |
5474 | if (file) |
5475 | { |
5476 | HA_CREATE_INFO create_info; |
5477 | memset(&create_info, 0, sizeof(create_info)); |
5478 | file->update_create_info(&create_info); |
5479 | append_directory(thd, &str, "DATA" , create_info.data_file_name); |
5480 | append_directory(thd, &str, "INDEX" , create_info.index_file_name); |
5481 | } |
5482 | |
5483 | if (str.length()) |
5484 | table->field[19]->store(str.ptr()+1, str.length()-1, cs); |
5485 | |
5486 | tmp_buff= (share->table_charset ? |
5487 | share->table_charset->name : "default" ); |
5488 | |
5489 | table->field[17]->store(tmp_buff, strlen(tmp_buff), cs); |
5490 | |
5491 | if (share->comment.str) |
5492 | table->field[20]->store(share->comment.str, share->comment.length, cs); |
5493 | |
5494 | /* Collect table info from the storage engine */ |
5495 | |
5496 | if (file) |
5497 | { |
5498 | /* If info() fails, then there's nothing else to do */ |
5499 | if (unlikely((info_error= file->info(HA_STATUS_VARIABLE | |
5500 | HA_STATUS_TIME | |
5501 | HA_STATUS_VARIABLE_EXTRA | |
5502 | HA_STATUS_AUTO)) != 0)) |
5503 | { |
5504 | file->print_error(info_error, MYF(0)); |
5505 | goto err; |
5506 | } |
5507 | |
5508 | enum row_type row_type = file->get_row_type(); |
5509 | switch (row_type) { |
5510 | case ROW_TYPE_NOT_USED: |
5511 | case ROW_TYPE_DEFAULT: |
5512 | tmp_buff= ((share->db_options_in_use & |
5513 | HA_OPTION_COMPRESS_RECORD) ? "Compressed" : |
5514 | (share->db_options_in_use & HA_OPTION_PACK_RECORD) ? |
5515 | "Dynamic" : "Fixed" ); |
5516 | break; |
5517 | case ROW_TYPE_FIXED: |
5518 | tmp_buff= "Fixed" ; |
5519 | break; |
5520 | case ROW_TYPE_DYNAMIC: |
5521 | tmp_buff= "Dynamic" ; |
5522 | break; |
5523 | case ROW_TYPE_COMPRESSED: |
5524 | tmp_buff= "Compressed" ; |
5525 | break; |
5526 | case ROW_TYPE_REDUNDANT: |
5527 | tmp_buff= "Redundant" ; |
5528 | break; |
5529 | case ROW_TYPE_COMPACT: |
5530 | tmp_buff= "Compact" ; |
5531 | break; |
5532 | case ROW_TYPE_PAGE: |
5533 | tmp_buff= "Page" ; |
5534 | break; |
5535 | } |
5536 | |
5537 | table->field[6]->store(tmp_buff, strlen(tmp_buff), cs); |
5538 | |
5539 | if (!tables->schema_table) |
5540 | { |
5541 | table->field[7]->store((longlong) file->stats.records, TRUE); |
5542 | table->field[7]->set_notnull(); |
5543 | } |
5544 | table->field[8]->store((longlong) file->stats.mean_rec_length, TRUE); |
5545 | table->field[9]->store((longlong) file->stats.data_file_length, TRUE); |
5546 | if (file->stats.max_data_file_length) |
5547 | { |
5548 | table->field[10]->store((longlong) file->stats.max_data_file_length, |
5549 | TRUE); |
5550 | table->field[10]->set_notnull(); |
5551 | } |
5552 | table->field[11]->store((longlong) file->stats.index_file_length, TRUE); |
5553 | if (file->stats.max_index_file_length) |
5554 | { |
5555 | table->field[21]->store((longlong) file->stats.max_index_file_length, |
5556 | TRUE); |
5557 | table->field[21]->set_notnull(); |
5558 | } |
5559 | table->field[12]->store((longlong) file->stats.delete_length, TRUE); |
5560 | if (show_table->found_next_number_field) |
5561 | { |
5562 | table->field[13]->store((longlong) file->stats.auto_increment_value, |
5563 | TRUE); |
5564 | table->field[13]->set_notnull(); |
5565 | } |
5566 | if (file->stats.create_time) |
5567 | { |
5568 | thd->variables.time_zone->gmt_sec_to_TIME(&time, |
5569 | (my_time_t) file->stats.create_time); |
5570 | table->field[14]->store_time(&time); |
5571 | table->field[14]->set_notnull(); |
5572 | } |
5573 | if (file->stats.update_time) |
5574 | { |
5575 | thd->variables.time_zone->gmt_sec_to_TIME(&time, |
5576 | (my_time_t) file->stats.update_time); |
5577 | table->field[15]->store_time(&time); |
5578 | table->field[15]->set_notnull(); |
5579 | } |
5580 | if (file->stats.check_time) |
5581 | { |
5582 | thd->variables.time_zone->gmt_sec_to_TIME(&time, |
5583 | (my_time_t) file->stats.check_time); |
5584 | table->field[16]->store_time(&time); |
5585 | table->field[16]->set_notnull(); |
5586 | } |
5587 | if (file->ha_table_flags() & (HA_HAS_OLD_CHECKSUM | HA_HAS_NEW_CHECKSUM)) |
5588 | { |
5589 | table->field[18]->store((longlong) file->checksum(), TRUE); |
5590 | table->field[18]->set_notnull(); |
5591 | } |
5592 | } |
5593 | /* If table is a temporary table */ |
5594 | LEX_CSTRING tmp= { STRING_WITH_LEN("N" ) }; |
5595 | if (show_table->s->tmp_table != NO_TMP_TABLE) |
5596 | tmp.str= "Y" ; |
5597 | table->field[22]->store(tmp.str, tmp.length, cs); |
5598 | } |
5599 | |
5600 | err: |
5601 | if (unlikely(res || info_error)) |
5602 | { |
5603 | /* |
5604 | If an error was encountered, push a warning, set the TABLE COMMENT |
5605 | column with the error text, and clear the error so that the operation |
5606 | can continue. |
5607 | */ |
5608 | const char *error= thd->get_stmt_da()->message(); |
5609 | table->field[20]->store(error, strlen(error), cs); |
5610 | |
5611 | push_warning(thd, Sql_condition::WARN_LEVEL_WARN, |
5612 | thd->get_stmt_da()->sql_errno(), error); |
5613 | thd->clear_error(); |
5614 | } |
5615 | |
5616 | DBUG_RETURN(schema_table_store_record(thd, table)); |
5617 | } |
5618 | |
5619 | |
5620 | /** |
5621 | @brief Store field characteristics into appropriate I_S table columns |
5622 | |
5623 | @param[in] table I_S table |
5624 | @param[in] field processed field |
5625 | @param[in] cs I_S table charset |
5626 | @param[in] offset offset from beginning of table |
5627 | to DATE_TYPE column in I_S table |
5628 | |
5629 | @return void |
5630 | */ |
5631 | |
5632 | static void store_column_type(TABLE *table, Field *field, CHARSET_INFO *cs, |
5633 | uint offset) |
5634 | { |
5635 | const char *tmp_buff; |
5636 | char column_type_buff[MAX_FIELD_WIDTH]; |
5637 | String column_type(column_type_buff, sizeof(column_type_buff), cs); |
5638 | |
5639 | field->sql_type(column_type); |
5640 | /* DTD_IDENTIFIER column */ |
5641 | table->field[offset + 8]->store(column_type.ptr(), column_type.length(), cs); |
5642 | table->field[offset + 8]->set_notnull(); |
5643 | /* |
5644 | DATA_TYPE column: |
5645 | MySQL column type has the following format: |
5646 | base_type [(dimension)] [unsigned] [zerofill]. |
5647 | For DATA_TYPE column we extract only base type. |
5648 | */ |
5649 | tmp_buff= strchr(column_type.c_ptr_safe(), '('); |
5650 | if (!tmp_buff) |
5651 | /* |
5652 | if there is no dimention part then check the presence of |
5653 | [unsigned] [zerofill] attributes and cut them of if exist. |
5654 | */ |
5655 | tmp_buff= strchr(column_type.c_ptr_safe(), ' '); |
5656 | table->field[offset]->store(column_type.ptr(), |
5657 | (tmp_buff ? (uint)(tmp_buff - column_type.ptr()) : |
5658 | column_type.length()), cs); |
5659 | |
5660 | Information_schema_character_attributes cattr= |
5661 | field->information_schema_character_attributes(); |
5662 | if (cattr.has_char_length()) |
5663 | { |
5664 | /* CHARACTER_MAXIMUM_LENGTH column*/ |
5665 | table->field[offset + 1]->store((longlong) cattr.char_length(), true); |
5666 | table->field[offset + 1]->set_notnull(); |
5667 | } |
5668 | if (cattr.has_octet_length()) |
5669 | { |
5670 | /* CHARACTER_OCTET_LENGTH column */ |
5671 | table->field[offset + 2]->store((longlong) cattr.octet_length(), true); |
5672 | table->field[offset + 2]->set_notnull(); |
5673 | } |
5674 | |
5675 | /* |
5676 | Calculate field_length and decimals. |
5677 | They are set to -1 if they should not be set (we should return NULL) |
5678 | */ |
5679 | |
5680 | Information_schema_numeric_attributes num= |
5681 | field->information_schema_numeric_attributes(); |
5682 | |
5683 | switch (field->type()) { |
5684 | case MYSQL_TYPE_TIME: |
5685 | case MYSQL_TYPE_TIMESTAMP: |
5686 | case MYSQL_TYPE_DATETIME: |
5687 | /* DATETIME_PRECISION column */ |
5688 | table->field[offset + 5]->store((longlong) field->decimals(), TRUE); |
5689 | table->field[offset + 5]->set_notnull(); |
5690 | break; |
5691 | default: |
5692 | break; |
5693 | } |
5694 | |
5695 | /* NUMERIC_PRECISION column */ |
5696 | if (num.has_precision()) |
5697 | { |
5698 | table->field[offset + 3]->store((longlong) num.precision(), true); |
5699 | table->field[offset + 3]->set_notnull(); |
5700 | |
5701 | /* NUMERIC_SCALE column */ |
5702 | if (num.has_scale()) |
5703 | { |
5704 | table->field[offset + 4]->store((longlong) num.scale(), true); |
5705 | table->field[offset + 4]->set_notnull(); |
5706 | } |
5707 | } |
5708 | if (field->has_charset()) |
5709 | { |
5710 | /* CHARACTER_SET_NAME column*/ |
5711 | tmp_buff= field->charset()->csname; |
5712 | table->field[offset + 6]->store(tmp_buff, strlen(tmp_buff), cs); |
5713 | table->field[offset + 6]->set_notnull(); |
5714 | /* COLLATION_NAME column */ |
5715 | tmp_buff= field->charset()->name; |
5716 | table->field[offset + 7]->store(tmp_buff, strlen(tmp_buff), cs); |
5717 | table->field[offset + 7]->set_notnull(); |
5718 | } |
5719 | } |
5720 | |
5721 | |
5722 | /* |
5723 | Print DATA_TYPE independently from sql_mode. |
5724 | It's only a brief human-readable description, without attributes, |
5725 | so it should not be used by client programs to generate SQL scripts. |
5726 | */ |
5727 | static bool print_anchor_data_type(const Spvar_definition *def, |
5728 | String *data_type) |
5729 | { |
5730 | if (def->column_type_ref()) |
5731 | return data_type->append(STRING_WITH_LEN("TYPE OF" )); |
5732 | if (def->is_table_rowtype_ref()) |
5733 | return data_type->append(STRING_WITH_LEN("ROW TYPE OF" )); |
5734 | /* |
5735 | "ROW TYPE OF cursor" is not possible yet. |
5736 | May become possible when we add package-wide cursors. |
5737 | */ |
5738 | DBUG_ASSERT(0); |
5739 | return false; |
5740 | } |
5741 | |
5742 | |
5743 | /* |
5744 | DTD_IDENTIFIER is the full data type description with attributes. |
5745 | It can be used by client programs to generate SQL scripts. |
5746 | Let's print it according to the current sql_mode. |
5747 | It will make output in line with the value in mysql.proc.param_list, |
5748 | so both I_S.XXX.DTD_IDENTIFIER and mysql.proc.param_list use the same notation: |
5749 | default or Oracle, according to the sql_mode at the SP creation time. |
5750 | The caller must make sure to set thd->variables.sql_mode to the routine sql_mode. |
5751 | */ |
5752 | static bool print_anchor_dtd_identifier(THD *thd, const Spvar_definition *def, |
5753 | String *dtd_identifier) |
5754 | { |
5755 | if (def->column_type_ref()) |
5756 | return (thd->variables.sql_mode & MODE_ORACLE) ? |
5757 | def->column_type_ref()->append_to(thd, dtd_identifier) || |
5758 | dtd_identifier->append(STRING_WITH_LEN("%TYPE" )) : |
5759 | dtd_identifier->append(STRING_WITH_LEN("TYPE OF " )) || |
5760 | def->column_type_ref()->append_to(thd, dtd_identifier); |
5761 | if (def->is_table_rowtype_ref()) |
5762 | return (thd->variables.sql_mode & MODE_ORACLE) ? |
5763 | def->table_rowtype_ref()->append_to(thd, dtd_identifier) || |
5764 | dtd_identifier->append(STRING_WITH_LEN("%ROWTYPE" )) : |
5765 | dtd_identifier->append(STRING_WITH_LEN("ROW TYPE OF " )) || |
5766 | def->table_rowtype_ref()->append_to(thd, dtd_identifier); |
5767 | DBUG_ASSERT(0); // See comments in print_anchor_data_type() |
5768 | return false; |
5769 | } |
5770 | |
5771 | |
5772 | /* |
5773 | Set columns DATA_TYPE and DTD_IDENTIFIER from an SP variable definition |
5774 | */ |
5775 | static void store_variable_type(THD *thd, const sp_variable *spvar, |
5776 | TABLE *tmptbl, |
5777 | TABLE_SHARE *tmpshare, |
5778 | CHARSET_INFO *cs, |
5779 | TABLE *table, uint offset) |
5780 | { |
5781 | if (spvar->field_def.is_explicit_data_type()) |
5782 | { |
5783 | if (spvar->field_def.is_row()) |
5784 | { |
5785 | // Explicit ROW |
5786 | table->field[offset]->store(STRING_WITH_LEN("ROW" ), cs); |
5787 | table->field[offset]->set_notnull(); |
5788 | // Perhaps eventually we need to print all ROW elements in DTD_IDENTIFIER |
5789 | table->field[offset + 8]->store(STRING_WITH_LEN("ROW" ), cs); |
5790 | table->field[offset + 8]->set_notnull(); |
5791 | } |
5792 | else |
5793 | { |
5794 | // Explicit scalar data type |
5795 | Field *field= spvar->field_def.make_field(tmpshare, thd->mem_root, |
5796 | &spvar->name); |
5797 | field->table= tmptbl; |
5798 | tmptbl->in_use= thd; |
5799 | store_column_type(table, field, cs, offset); |
5800 | } |
5801 | } |
5802 | else |
5803 | { |
5804 | StringBuffer<128> data_type(cs), dtd_identifier(cs); |
5805 | |
5806 | if (print_anchor_data_type(&spvar->field_def, &data_type)) |
5807 | { |
5808 | table->field[offset]->store(STRING_WITH_LEN("ERROR" ), cs); // EOM? |
5809 | table->field[offset]->set_notnull(); |
5810 | } |
5811 | else |
5812 | { |
5813 | DBUG_ASSERT(data_type.length()); |
5814 | table->field[offset]->store(data_type.ptr(), data_type.length(), cs); |
5815 | table->field[offset]->set_notnull(); |
5816 | } |
5817 | |
5818 | if (print_anchor_dtd_identifier(thd, &spvar->field_def, &dtd_identifier)) |
5819 | { |
5820 | table->field[offset + 8]->store(STRING_WITH_LEN("ERROR" ), cs); // EOM? |
5821 | table->field[offset + 8]->set_notnull(); |
5822 | } |
5823 | else |
5824 | { |
5825 | DBUG_ASSERT(dtd_identifier.length()); |
5826 | table->field[offset + 8]->store(dtd_identifier.ptr(), |
5827 | dtd_identifier.length(), cs); |
5828 | table->field[offset + 8]->set_notnull(); |
5829 | } |
5830 | } |
5831 | } |
5832 | |
5833 | |
5834 | static int get_schema_column_record(THD *thd, TABLE_LIST *tables, |
5835 | TABLE *table, bool res, |
5836 | const LEX_CSTRING *db_name, |
5837 | const LEX_CSTRING *table_name) |
5838 | { |
5839 | LEX *lex= thd->lex; |
5840 | const char *wild= lex->wild ? lex->wild->ptr() : NullS; |
5841 | CHARSET_INFO *cs= system_charset_info; |
5842 | TABLE *show_table; |
5843 | Field **ptr, *field; |
5844 | int count; |
5845 | bool quoted_defaults= lex->sql_command != SQLCOM_SHOW_FIELDS; |
5846 | DBUG_ENTER("get_schema_column_record" ); |
5847 | |
5848 | if (res) |
5849 | { |
5850 | if (lex->sql_command != SQLCOM_SHOW_FIELDS) |
5851 | { |
5852 | /* |
5853 | I.e. we are in SELECT FROM INFORMATION_SCHEMA.COLUMS |
5854 | rather than in SHOW COLUMNS |
5855 | */ |
5856 | push_warning(thd, Sql_condition::WARN_LEVEL_WARN, |
5857 | thd->get_stmt_da()->sql_errno(), |
5858 | thd->get_stmt_da()->message()); |
5859 | thd->clear_error(); |
5860 | res= 0; |
5861 | } |
5862 | DBUG_RETURN(res); |
5863 | } |
5864 | |
5865 | show_table= tables->table; |
5866 | count= 0; |
5867 | ptr= show_table->field; |
5868 | show_table->use_all_columns(); // Required for default |
5869 | restore_record(show_table, s->default_values); |
5870 | |
5871 | for (; (field= *ptr) ; ptr++) |
5872 | { |
5873 | if(field->invisible > INVISIBLE_USER) |
5874 | continue; |
5875 | uchar *pos; |
5876 | char tmp[MAX_FIELD_WIDTH]; |
5877 | String type(tmp,sizeof(tmp), system_charset_info); |
5878 | |
5879 | DEBUG_SYNC(thd, "get_schema_column" ); |
5880 | |
5881 | if (wild && wild[0] && |
5882 | wild_case_compare(system_charset_info, field->field_name.str, wild)) |
5883 | continue; |
5884 | |
5885 | count++; |
5886 | /* Get default row, with all NULL fields set to NULL */ |
5887 | restore_record(table, s->default_values); |
5888 | |
5889 | #ifndef NO_EMBEDDED_ACCESS_CHECKS |
5890 | uint col_access; |
5891 | check_access(thd,SELECT_ACL, db_name->str, |
5892 | &tables->grant.privilege, 0, 0, MY_TEST(tables->schema_table)); |
5893 | col_access= get_column_grant(thd, &tables->grant, |
5894 | db_name->str, table_name->str, |
5895 | field->field_name.str) & COL_ACLS; |
5896 | if (!tables->schema_table && !col_access) |
5897 | continue; |
5898 | char *end= tmp; |
5899 | for (uint bitnr=0; col_access ; col_access>>=1,bitnr++) |
5900 | { |
5901 | if (col_access & 1) |
5902 | { |
5903 | *end++=','; |
5904 | end=strmov(end,grant_types.type_names[bitnr]); |
5905 | } |
5906 | } |
5907 | table->field[18]->store(tmp+1,end == tmp ? 0 : (uint) (end-tmp-1), cs); |
5908 | |
5909 | #endif |
5910 | table->field[0]->store(STRING_WITH_LEN("def" ), cs); |
5911 | table->field[1]->store(db_name->str, db_name->length, cs); |
5912 | table->field[2]->store(table_name->str, table_name->length, cs); |
5913 | table->field[3]->store(field->field_name.str, field->field_name.length, |
5914 | cs); |
5915 | table->field[4]->store((longlong) count, TRUE); |
5916 | |
5917 | if (get_field_default_value(thd, field, &type, quoted_defaults)) |
5918 | { |
5919 | table->field[5]->store(type.ptr(), type.length(), cs); |
5920 | table->field[5]->set_notnull(); |
5921 | } |
5922 | pos=(uchar*) ((field->flags & NOT_NULL_FLAG) ? "NO" : "YES" ); |
5923 | table->field[6]->store((const char*) pos, |
5924 | strlen((const char*) pos), cs); |
5925 | store_column_type(table, field, cs, 7); |
5926 | pos=(uchar*) ((field->flags & PRI_KEY_FLAG) ? "PRI" : |
5927 | (field->flags & UNIQUE_KEY_FLAG) ? "UNI" : |
5928 | (field->flags & MULTIPLE_KEY_FLAG) ? "MUL" :"" ); |
5929 | table->field[16]->store((const char*) pos, |
5930 | strlen((const char*) pos), cs); |
5931 | |
5932 | StringBuffer<256> buf; |
5933 | if (field->unireg_check == Field::NEXT_NUMBER) |
5934 | buf.set(STRING_WITH_LEN("auto_increment" ),cs); |
5935 | if (print_on_update_clause(field, &type, true)) |
5936 | buf.set(type.ptr(), type.length(),cs); |
5937 | if (field->vcol_info) |
5938 | { |
5939 | String gen_s(tmp,sizeof(tmp), system_charset_info); |
5940 | gen_s.length(0); |
5941 | field->vcol_info->print(&gen_s); |
5942 | table->field[21]->store(gen_s.ptr(), gen_s.length(), cs); |
5943 | table->field[21]->set_notnull(); |
5944 | table->field[20]->store(STRING_WITH_LEN("ALWAYS" ), cs); |
5945 | |
5946 | if (field->vcol_info->stored_in_db) |
5947 | buf.set(STRING_WITH_LEN("STORED GENERATED" ), cs); |
5948 | else |
5949 | buf.set(STRING_WITH_LEN("VIRTUAL GENERATED" ), cs); |
5950 | } |
5951 | else if (field->flags & VERS_SYSTEM_FIELD) |
5952 | { |
5953 | if (field->flags & VERS_SYS_START_FLAG) |
5954 | table->field[21]->store(STRING_WITH_LEN("ROW START" ), cs); |
5955 | else |
5956 | table->field[21]->store(STRING_WITH_LEN("ROW END" ), cs); |
5957 | table->field[21]->set_notnull(); |
5958 | table->field[20]->store(STRING_WITH_LEN("ALWAYS" ), cs); |
5959 | } |
5960 | else |
5961 | table->field[20]->store(STRING_WITH_LEN("NEVER" ), cs); |
5962 | /*Invisible can coexist with auto_increment and virtual */ |
5963 | if (field->invisible == INVISIBLE_USER) |
5964 | { |
5965 | if (buf.length()) |
5966 | buf.append(STRING_WITH_LEN(", " )); |
5967 | buf.append(STRING_WITH_LEN("INVISIBLE" ),cs); |
5968 | } |
5969 | if (field->vers_update_unversioned()) |
5970 | { |
5971 | if (buf.length()) |
5972 | buf.append(STRING_WITH_LEN(", " )); |
5973 | buf.append(STRING_WITH_LEN("WITHOUT SYSTEM VERSIONING" ), cs); |
5974 | } |
5975 | table->field[17]->store(buf.ptr(), buf.length(), cs); |
5976 | table->field[19]->store(field->comment.str, field->comment.length, cs); |
5977 | if (schema_table_store_record(thd, table)) |
5978 | DBUG_RETURN(1); |
5979 | } |
5980 | DBUG_RETURN(0); |
5981 | } |
5982 | |
5983 | |
5984 | int fill_schema_charsets(THD *thd, TABLE_LIST *tables, COND *cond) |
5985 | { |
5986 | CHARSET_INFO **cs; |
5987 | const char *wild= thd->lex->wild ? thd->lex->wild->ptr() : NullS; |
5988 | TABLE *table= tables->table; |
5989 | CHARSET_INFO *scs= system_charset_info; |
5990 | |
5991 | for (cs= all_charsets ; |
5992 | cs < all_charsets + array_elements(all_charsets) ; |
5993 | cs++) |
5994 | { |
5995 | CHARSET_INFO *tmp_cs= cs[0]; |
5996 | if (tmp_cs && (tmp_cs->state & MY_CS_PRIMARY) && |
5997 | (tmp_cs->state & MY_CS_AVAILABLE) && |
5998 | !(tmp_cs->state & MY_CS_HIDDEN) && |
5999 | !(wild && wild[0] && |
6000 | wild_case_compare(scs, tmp_cs->csname,wild))) |
6001 | { |
6002 | const char *; |
6003 | restore_record(table, s->default_values); |
6004 | table->field[0]->store(tmp_cs->csname, strlen(tmp_cs->csname), scs); |
6005 | table->field[1]->store(tmp_cs->name, strlen(tmp_cs->name), scs); |
6006 | comment= tmp_cs->comment ? tmp_cs->comment : "" ; |
6007 | table->field[2]->store(comment, strlen(comment), scs); |
6008 | table->field[3]->store((longlong) tmp_cs->mbmaxlen, TRUE); |
6009 | if (schema_table_store_record(thd, table)) |
6010 | return 1; |
6011 | } |
6012 | } |
6013 | return 0; |
6014 | } |
6015 | |
6016 | |
6017 | static my_bool iter_schema_engines(THD *thd, plugin_ref plugin, |
6018 | void *ptable) |
6019 | { |
6020 | TABLE *table= (TABLE *) ptable; |
6021 | handlerton *hton= plugin_hton(plugin); |
6022 | const char *wild= thd->lex->wild ? thd->lex->wild->ptr() : NullS; |
6023 | CHARSET_INFO *scs= system_charset_info; |
6024 | handlerton *default_type= ha_default_handlerton(thd); |
6025 | DBUG_ENTER("iter_schema_engines" ); |
6026 | |
6027 | |
6028 | /* Disabled plugins */ |
6029 | if (plugin_state(plugin) != PLUGIN_IS_READY) |
6030 | { |
6031 | |
6032 | struct st_maria_plugin *plug= plugin_decl(plugin); |
6033 | if (!(wild && wild[0] && |
6034 | wild_case_compare(scs, plug->name,wild))) |
6035 | { |
6036 | restore_record(table, s->default_values); |
6037 | table->field[0]->store(plug->name, strlen(plug->name), scs); |
6038 | table->field[1]->store(STRING_WITH_LEN("NO" ), scs); |
6039 | table->field[2]->store(plug->descr, strlen(plug->descr), scs); |
6040 | if (schema_table_store_record(thd, table)) |
6041 | DBUG_RETURN(1); |
6042 | } |
6043 | DBUG_RETURN(0); |
6044 | } |
6045 | |
6046 | if (!(hton->flags & HTON_HIDDEN)) |
6047 | { |
6048 | LEX_CSTRING *name= plugin_name(plugin); |
6049 | if (!(wild && wild[0] && |
6050 | wild_case_compare(scs, name->str,wild))) |
6051 | { |
6052 | LEX_CSTRING yesno[2]= {{ STRING_WITH_LEN("NO" ) }, |
6053 | { STRING_WITH_LEN("YES" ) }}; |
6054 | LEX_CSTRING *tmp; |
6055 | const char *option_name= show_comp_option_name[(int) hton->state]; |
6056 | restore_record(table, s->default_values); |
6057 | |
6058 | table->field[0]->store(name->str, name->length, scs); |
6059 | if (hton->state == SHOW_OPTION_YES && default_type == hton) |
6060 | option_name= "DEFAULT" ; |
6061 | table->field[1]->store(option_name, strlen(option_name), scs); |
6062 | table->field[2]->store(plugin_decl(plugin)->descr, |
6063 | strlen(plugin_decl(plugin)->descr), scs); |
6064 | tmp= &yesno[MY_TEST(hton->commit)]; |
6065 | table->field[3]->store(tmp->str, tmp->length, scs); |
6066 | table->field[3]->set_notnull(); |
6067 | tmp= &yesno[MY_TEST(hton->prepare)]; |
6068 | table->field[4]->store(tmp->str, tmp->length, scs); |
6069 | table->field[4]->set_notnull(); |
6070 | tmp= &yesno[MY_TEST(hton->savepoint_set)]; |
6071 | table->field[5]->store(tmp->str, tmp->length, scs); |
6072 | table->field[5]->set_notnull(); |
6073 | |
6074 | if (schema_table_store_record(thd, table)) |
6075 | DBUG_RETURN(1); |
6076 | } |
6077 | } |
6078 | DBUG_RETURN(0); |
6079 | } |
6080 | |
6081 | int fill_schema_engines(THD *thd, TABLE_LIST *tables, COND *cond) |
6082 | { |
6083 | DBUG_ENTER("fill_schema_engines" ); |
6084 | if (plugin_foreach_with_mask(thd, iter_schema_engines, |
6085 | MYSQL_STORAGE_ENGINE_PLUGIN, |
6086 | ~(PLUGIN_IS_FREED | PLUGIN_IS_DYING), |
6087 | tables->table)) |
6088 | DBUG_RETURN(1); |
6089 | DBUG_RETURN(0); |
6090 | } |
6091 | |
6092 | |
6093 | int fill_schema_collation(THD *thd, TABLE_LIST *tables, COND *cond) |
6094 | { |
6095 | CHARSET_INFO **cs; |
6096 | const char *wild= thd->lex->wild ? thd->lex->wild->ptr() : NullS; |
6097 | TABLE *table= tables->table; |
6098 | CHARSET_INFO *scs= system_charset_info; |
6099 | for (cs= all_charsets ; |
6100 | cs < all_charsets + array_elements(all_charsets) ; |
6101 | cs++ ) |
6102 | { |
6103 | CHARSET_INFO **cl; |
6104 | CHARSET_INFO *tmp_cs= cs[0]; |
6105 | if (!tmp_cs || !(tmp_cs->state & MY_CS_AVAILABLE) || |
6106 | (tmp_cs->state & MY_CS_HIDDEN) || |
6107 | !(tmp_cs->state & MY_CS_PRIMARY)) |
6108 | continue; |
6109 | for (cl= all_charsets; |
6110 | cl < all_charsets + array_elements(all_charsets) ; |
6111 | cl ++) |
6112 | { |
6113 | CHARSET_INFO *tmp_cl= cl[0]; |
6114 | if (!tmp_cl || !(tmp_cl->state & MY_CS_AVAILABLE) || |
6115 | !my_charset_same(tmp_cs, tmp_cl)) |
6116 | continue; |
6117 | if (!(wild && wild[0] && |
6118 | wild_case_compare(scs, tmp_cl->name,wild))) |
6119 | { |
6120 | const char *tmp_buff; |
6121 | restore_record(table, s->default_values); |
6122 | table->field[0]->store(tmp_cl->name, strlen(tmp_cl->name), scs); |
6123 | table->field[1]->store(tmp_cl->csname , strlen(tmp_cl->csname), scs); |
6124 | table->field[2]->store((longlong) tmp_cl->number, TRUE); |
6125 | tmp_buff= (tmp_cl->state & MY_CS_PRIMARY) ? "Yes" : "" ; |
6126 | table->field[3]->store(tmp_buff, strlen(tmp_buff), scs); |
6127 | tmp_buff= (tmp_cl->state & MY_CS_COMPILED)? "Yes" : "" ; |
6128 | table->field[4]->store(tmp_buff, strlen(tmp_buff), scs); |
6129 | table->field[5]->store((longlong) tmp_cl->strxfrm_multiply, TRUE); |
6130 | if (schema_table_store_record(thd, table)) |
6131 | return 1; |
6132 | } |
6133 | } |
6134 | } |
6135 | return 0; |
6136 | } |
6137 | |
6138 | |
6139 | int fill_schema_coll_charset_app(THD *thd, TABLE_LIST *tables, COND *cond) |
6140 | { |
6141 | CHARSET_INFO **cs; |
6142 | TABLE *table= tables->table; |
6143 | CHARSET_INFO *scs= system_charset_info; |
6144 | for (cs= all_charsets ; |
6145 | cs < all_charsets + array_elements(all_charsets) ; |
6146 | cs++ ) |
6147 | { |
6148 | CHARSET_INFO **cl; |
6149 | CHARSET_INFO *tmp_cs= cs[0]; |
6150 | if (!tmp_cs || !(tmp_cs->state & MY_CS_AVAILABLE) || |
6151 | !(tmp_cs->state & MY_CS_PRIMARY)) |
6152 | continue; |
6153 | for (cl= all_charsets; |
6154 | cl < all_charsets + array_elements(all_charsets) ; |
6155 | cl ++) |
6156 | { |
6157 | CHARSET_INFO *tmp_cl= cl[0]; |
6158 | if (!tmp_cl || !(tmp_cl->state & MY_CS_AVAILABLE) || |
6159 | (tmp_cl->state & MY_CS_HIDDEN) || |
6160 | !my_charset_same(tmp_cs,tmp_cl)) |
6161 | continue; |
6162 | restore_record(table, s->default_values); |
6163 | table->field[0]->store(tmp_cl->name, strlen(tmp_cl->name), scs); |
6164 | table->field[1]->store(tmp_cl->csname , strlen(tmp_cl->csname), scs); |
6165 | if (schema_table_store_record(thd, table)) |
6166 | return 1; |
6167 | } |
6168 | } |
6169 | return 0; |
6170 | } |
6171 | |
6172 | |
6173 | static inline void copy_field_as_string(Field *to_field, Field *from_field) |
6174 | { |
6175 | char buff[MAX_FIELD_WIDTH]; |
6176 | String tmp_str(buff, sizeof(buff), system_charset_info); |
6177 | from_field->val_str(&tmp_str); |
6178 | to_field->store(tmp_str.ptr(), tmp_str.length(), system_charset_info); |
6179 | } |
6180 | |
6181 | |
6182 | /** |
6183 | @brief Store record into I_S.PARAMETERS table |
6184 | |
6185 | @param[in] thd thread handler |
6186 | @param[in] table I_S table |
6187 | @param[in] proc_table 'mysql.proc' table |
6188 | @param[in] wild wild string, not used for now, |
6189 | will be useful |
6190 | if we add 'SHOW PARAMETERs' |
6191 | @param[in] full_access if 1 user has privileges on the routine |
6192 | @param[in] sp_user user in 'user@host' format |
6193 | |
6194 | @return Operation status |
6195 | @retval 0 ok |
6196 | @retval 1 error |
6197 | */ |
6198 | |
6199 | bool store_schema_params(THD *thd, TABLE *table, TABLE *proc_table, |
6200 | const char *wild, bool full_access, |
6201 | const char *sp_user) |
6202 | { |
6203 | TABLE_SHARE share; |
6204 | TABLE tbl; |
6205 | CHARSET_INFO *cs= system_charset_info; |
6206 | LEX_CSTRING definer, params, returns= empty_clex_str; |
6207 | LEX_CSTRING db, name; |
6208 | char path[FN_REFLEN]; |
6209 | sp_head *sp; |
6210 | const Sp_handler *sph; |
6211 | bool free_sp_head; |
6212 | bool error= 0; |
6213 | sql_mode_t sql_mode; |
6214 | DBUG_ENTER("store_schema_params" ); |
6215 | |
6216 | bzero((char*) &tbl, sizeof(TABLE)); |
6217 | (void) build_table_filename(path, sizeof(path), "" , "" , "" , 0); |
6218 | init_tmp_table_share(thd, &share, "" , 0, "" , path); |
6219 | |
6220 | proc_table->field[MYSQL_PROC_FIELD_DB]->val_str_nopad(thd->mem_root, &db); |
6221 | proc_table->field[MYSQL_PROC_FIELD_NAME]->val_str_nopad(thd->mem_root, &name); |
6222 | proc_table->field[MYSQL_PROC_FIELD_DEFINER]->val_str_nopad(thd->mem_root, &definer); |
6223 | sql_mode= (sql_mode_t) proc_table->field[MYSQL_PROC_FIELD_SQL_MODE]->val_int(); |
6224 | sph= Sp_handler::handler_mysql_proc((stored_procedure_type) |
6225 | proc_table->field[MYSQL_PROC_MYSQL_TYPE]-> |
6226 | val_int()); |
6227 | if (!sph) |
6228 | DBUG_RETURN(0); |
6229 | |
6230 | if (!full_access) |
6231 | full_access= !strcmp(sp_user, definer.str); |
6232 | if (!full_access && |
6233 | check_some_routine_access(thd, db.str, name.str, sph)) |
6234 | DBUG_RETURN(0); |
6235 | |
6236 | proc_table->field[MYSQL_PROC_FIELD_PARAM_LIST]->val_str_nopad(thd->mem_root, |
6237 | ¶ms); |
6238 | if (sph->type() == TYPE_ENUM_FUNCTION) |
6239 | proc_table->field[MYSQL_PROC_FIELD_RETURNS]->val_str_nopad(thd->mem_root, |
6240 | &returns); |
6241 | sp= sph->sp_load_for_information_schema(thd, proc_table, db, name, |
6242 | params, returns, sql_mode, |
6243 | &free_sp_head); |
6244 | if (sp) |
6245 | { |
6246 | Field *field; |
6247 | LEX_CSTRING tmp_string; |
6248 | Sql_mode_save sql_mode_backup(thd); |
6249 | thd->variables.sql_mode= sql_mode; |
6250 | |
6251 | if (sph->type() == TYPE_ENUM_FUNCTION) |
6252 | { |
6253 | restore_record(table, s->default_values); |
6254 | table->field[0]->store(STRING_WITH_LEN("def" ), cs); |
6255 | table->field[1]->store(db, cs); |
6256 | table->field[2]->store(name, cs); |
6257 | table->field[3]->store((longlong) 0, TRUE); |
6258 | proc_table->field[MYSQL_PROC_MYSQL_TYPE]->val_str_nopad(thd->mem_root, |
6259 | &tmp_string); |
6260 | table->field[15]->store(tmp_string, cs); |
6261 | field= sp->m_return_field_def.make_field(&share, thd->mem_root, |
6262 | &empty_clex_str); |
6263 | field->table= &tbl; |
6264 | tbl.in_use= thd; |
6265 | store_column_type(table, field, cs, 6); |
6266 | if (schema_table_store_record(thd, table)) |
6267 | { |
6268 | free_table_share(&share); |
6269 | if (free_sp_head) |
6270 | delete sp; |
6271 | DBUG_RETURN(1); |
6272 | } |
6273 | } |
6274 | |
6275 | sp_pcontext *spcont= sp->get_parse_context(); |
6276 | uint params= spcont->context_var_count(); |
6277 | for (uint i= 0 ; i < params ; i++) |
6278 | { |
6279 | const char *tmp_buff; |
6280 | sp_variable *spvar= spcont->find_variable(i); |
6281 | switch (spvar->mode) { |
6282 | case sp_variable::MODE_IN: |
6283 | tmp_buff= "IN" ; |
6284 | break; |
6285 | case sp_variable::MODE_OUT: |
6286 | tmp_buff= "OUT" ; |
6287 | break; |
6288 | case sp_variable::MODE_INOUT: |
6289 | tmp_buff= "INOUT" ; |
6290 | break; |
6291 | default: |
6292 | tmp_buff= "" ; |
6293 | break; |
6294 | } |
6295 | |
6296 | restore_record(table, s->default_values); |
6297 | table->field[0]->store(STRING_WITH_LEN("def" ), cs); |
6298 | table->field[1]->store(db, cs); |
6299 | table->field[2]->store(name, cs); |
6300 | table->field[3]->store((longlong) i + 1, TRUE); |
6301 | table->field[4]->store(tmp_buff, strlen(tmp_buff), cs); |
6302 | table->field[4]->set_notnull(); |
6303 | table->field[5]->store(spvar->name.str, spvar->name.length, cs); |
6304 | table->field[5]->set_notnull(); |
6305 | proc_table->field[MYSQL_PROC_MYSQL_TYPE]->val_str_nopad(thd->mem_root, |
6306 | &tmp_string); |
6307 | table->field[15]->store(tmp_string, cs); |
6308 | |
6309 | store_variable_type(thd, spvar, &tbl, &share, cs, table, 6); |
6310 | if (schema_table_store_record(thd, table)) |
6311 | { |
6312 | error= 1; |
6313 | break; |
6314 | } |
6315 | } |
6316 | if (free_sp_head) |
6317 | delete sp; |
6318 | } |
6319 | free_table_share(&share); |
6320 | DBUG_RETURN(error); |
6321 | } |
6322 | |
6323 | |
6324 | bool store_schema_proc(THD *thd, TABLE *table, TABLE *proc_table, |
6325 | const char *wild, bool full_access, const char *sp_user) |
6326 | { |
6327 | MYSQL_TIME time; |
6328 | LEX *lex= thd->lex; |
6329 | CHARSET_INFO *cs= system_charset_info; |
6330 | const Sp_handler *sph; |
6331 | LEX_CSTRING db, name, definer, returns= empty_clex_str; |
6332 | |
6333 | proc_table->field[MYSQL_PROC_FIELD_DB]->val_str_nopad(thd->mem_root, &db); |
6334 | proc_table->field[MYSQL_PROC_FIELD_NAME]->val_str_nopad(thd->mem_root, &name); |
6335 | proc_table->field[MYSQL_PROC_FIELD_DEFINER]->val_str_nopad(thd->mem_root, &definer); |
6336 | sph= Sp_handler::handler_mysql_proc((stored_procedure_type) |
6337 | proc_table->field[MYSQL_PROC_MYSQL_TYPE]-> |
6338 | val_int()); |
6339 | if (!sph) |
6340 | return 0; |
6341 | |
6342 | if (!full_access) |
6343 | full_access= !strcmp(sp_user, definer.str); |
6344 | if (!full_access && |
6345 | check_some_routine_access(thd, db.str, name.str, sph)) |
6346 | return 0; |
6347 | |
6348 | if (!is_show_command(thd) || |
6349 | sph == Sp_handler::handler(lex->sql_command)) |
6350 | { |
6351 | restore_record(table, s->default_values); |
6352 | if (!wild || !wild[0] || !wild_case_compare(system_charset_info, |
6353 | name.str, wild)) |
6354 | { |
6355 | int enum_idx= (int) proc_table->field[MYSQL_PROC_FIELD_ACCESS]->val_int(); |
6356 | table->field[3]->store(name, cs); |
6357 | |
6358 | copy_field_as_string(table->field[0], |
6359 | proc_table->field[MYSQL_PROC_FIELD_SPECIFIC_NAME]); |
6360 | table->field[1]->store(STRING_WITH_LEN("def" ), cs); |
6361 | table->field[2]->store(db, cs); |
6362 | copy_field_as_string(table->field[4], |
6363 | proc_table->field[MYSQL_PROC_MYSQL_TYPE]); |
6364 | |
6365 | if (sph->type() == TYPE_ENUM_FUNCTION) |
6366 | { |
6367 | sp_head *sp; |
6368 | bool free_sp_head; |
6369 | proc_table->field[MYSQL_PROC_FIELD_RETURNS]->val_str_nopad(thd->mem_root, |
6370 | &returns); |
6371 | sp= sph->sp_load_for_information_schema(thd, proc_table, |
6372 | db, name, |
6373 | empty_clex_str /*params*/, |
6374 | returns, |
6375 | (ulong) proc_table-> |
6376 | field[MYSQL_PROC_FIELD_SQL_MODE]-> |
6377 | val_int(), |
6378 | &free_sp_head); |
6379 | if (sp) |
6380 | { |
6381 | char path[FN_REFLEN]; |
6382 | TABLE_SHARE share; |
6383 | TABLE tbl; |
6384 | Field *field; |
6385 | |
6386 | bzero((char*) &tbl, sizeof(TABLE)); |
6387 | (void) build_table_filename(path, sizeof(path), "" , "" , "" , 0); |
6388 | init_tmp_table_share(thd, &share, "" , 0, "" , path); |
6389 | field= sp->m_return_field_def.make_field(&share, thd->mem_root, |
6390 | &empty_clex_str); |
6391 | field->table= &tbl; |
6392 | tbl.in_use= thd; |
6393 | store_column_type(table, field, cs, 5); |
6394 | free_table_share(&share); |
6395 | if (free_sp_head) |
6396 | delete sp; |
6397 | } |
6398 | } |
6399 | |
6400 | if (full_access) |
6401 | { |
6402 | copy_field_as_string(table->field[15], |
6403 | proc_table->field[MYSQL_PROC_FIELD_BODY_UTF8]); |
6404 | table->field[15]->set_notnull(); |
6405 | } |
6406 | table->field[14]->store(STRING_WITH_LEN("SQL" ), cs); |
6407 | table->field[18]->store(STRING_WITH_LEN("SQL" ), cs); |
6408 | copy_field_as_string(table->field[19], |
6409 | proc_table->field[MYSQL_PROC_FIELD_DETERMINISTIC]); |
6410 | table->field[20]->store(sp_data_access_name[enum_idx].str, |
6411 | sp_data_access_name[enum_idx].length , cs); |
6412 | copy_field_as_string(table->field[22], |
6413 | proc_table->field[MYSQL_PROC_FIELD_SECURITY_TYPE]); |
6414 | |
6415 | bzero((char *)&time, sizeof(time)); |
6416 | ((Field_timestamp *) proc_table->field[MYSQL_PROC_FIELD_CREATED])-> |
6417 | get_time(&time); |
6418 | table->field[23]->store_time(&time); |
6419 | bzero((char *)&time, sizeof(time)); |
6420 | ((Field_timestamp *) proc_table->field[MYSQL_PROC_FIELD_MODIFIED])-> |
6421 | get_time(&time); |
6422 | table->field[24]->store_time(&time); |
6423 | copy_field_as_string(table->field[25], |
6424 | proc_table->field[MYSQL_PROC_FIELD_SQL_MODE]); |
6425 | copy_field_as_string(table->field[26], |
6426 | proc_table->field[MYSQL_PROC_FIELD_COMMENT]); |
6427 | |
6428 | table->field[27]->store(definer, cs); |
6429 | copy_field_as_string(table->field[28], |
6430 | proc_table-> |
6431 | field[MYSQL_PROC_FIELD_CHARACTER_SET_CLIENT]); |
6432 | copy_field_as_string(table->field[29], |
6433 | proc_table-> |
6434 | field[MYSQL_PROC_FIELD_COLLATION_CONNECTION]); |
6435 | copy_field_as_string(table->field[30], |
6436 | proc_table->field[MYSQL_PROC_FIELD_DB_COLLATION]); |
6437 | |
6438 | return schema_table_store_record(thd, table); |
6439 | } |
6440 | } |
6441 | return 0; |
6442 | } |
6443 | |
6444 | |
6445 | int fill_schema_proc(THD *thd, TABLE_LIST *tables, COND *cond) |
6446 | { |
6447 | TABLE *proc_table; |
6448 | TABLE_LIST proc_tables; |
6449 | const char *wild= thd->lex->wild ? thd->lex->wild->ptr() : NullS; |
6450 | int res= 0; |
6451 | TABLE *table= tables->table; |
6452 | bool full_access; |
6453 | char definer[USER_HOST_BUFF_SIZE]; |
6454 | Open_tables_backup open_tables_state_backup; |
6455 | enum enum_schema_tables schema_table_idx= |
6456 | get_schema_table_idx(tables->schema_table); |
6457 | DBUG_ENTER("fill_schema_proc" ); |
6458 | |
6459 | strxmov(definer, thd->security_ctx->priv_user, "@" , |
6460 | thd->security_ctx->priv_host, NullS); |
6461 | /* We use this TABLE_LIST instance only for checking of privileges. */ |
6462 | bzero((char*) &proc_tables,sizeof(proc_tables)); |
6463 | proc_tables.db= MYSQL_SCHEMA_NAME; |
6464 | proc_tables.table_name= MYSQL_PROC_NAME; |
6465 | proc_tables.alias= MYSQL_PROC_NAME; |
6466 | proc_tables.lock_type= TL_READ; |
6467 | full_access= !check_table_access(thd, SELECT_ACL, &proc_tables, FALSE, |
6468 | 1, TRUE); |
6469 | if (!(proc_table= open_proc_table_for_read(thd, &open_tables_state_backup))) |
6470 | { |
6471 | DBUG_RETURN(1); |
6472 | } |
6473 | |
6474 | /* Disable padding temporarily so it doesn't break the query */ |
6475 | ulonglong sql_mode_was = thd->variables.sql_mode; |
6476 | thd->variables.sql_mode &= ~MODE_PAD_CHAR_TO_FULL_LENGTH; |
6477 | |
6478 | if (proc_table->file->ha_index_init(0, 1)) |
6479 | { |
6480 | res= 1; |
6481 | goto err; |
6482 | } |
6483 | |
6484 | if ((res= proc_table->file->ha_index_first(proc_table->record[0]))) |
6485 | { |
6486 | res= (res == HA_ERR_END_OF_FILE) ? 0 : 1; |
6487 | goto err; |
6488 | } |
6489 | |
6490 | if (schema_table_idx == SCH_PROCEDURES ? |
6491 | store_schema_proc(thd, table, proc_table, wild, full_access, definer) : |
6492 | store_schema_params(thd, table, proc_table, wild, full_access, definer)) |
6493 | { |
6494 | res= 1; |
6495 | goto err; |
6496 | } |
6497 | while (!proc_table->file->ha_index_next(proc_table->record[0])) |
6498 | { |
6499 | if (schema_table_idx == SCH_PROCEDURES ? |
6500 | store_schema_proc(thd, table, proc_table, wild, full_access, definer): |
6501 | store_schema_params(thd, table, proc_table, wild, full_access, definer)) |
6502 | { |
6503 | res= 1; |
6504 | goto err; |
6505 | } |
6506 | } |
6507 | |
6508 | err: |
6509 | if (proc_table->file->inited) |
6510 | (void) proc_table->file->ha_index_end(); |
6511 | |
6512 | close_system_tables(thd, &open_tables_state_backup); |
6513 | thd->variables.sql_mode = sql_mode_was; |
6514 | DBUG_RETURN(res); |
6515 | } |
6516 | |
6517 | |
6518 | static int get_schema_stat_record(THD *thd, TABLE_LIST *tables, |
6519 | TABLE *table, bool res, |
6520 | const LEX_CSTRING *db_name, |
6521 | const LEX_CSTRING *table_name) |
6522 | { |
6523 | CHARSET_INFO *cs= system_charset_info; |
6524 | DBUG_ENTER("get_schema_stat_record" ); |
6525 | if (res) |
6526 | { |
6527 | if (thd->lex->sql_command != SQLCOM_SHOW_KEYS) |
6528 | { |
6529 | /* |
6530 | I.e. we are in SELECT FROM INFORMATION_SCHEMA.STATISTICS |
6531 | rather than in SHOW KEYS |
6532 | */ |
6533 | if (unlikely(thd->is_error())) |
6534 | push_warning(thd, Sql_condition::WARN_LEVEL_WARN, |
6535 | thd->get_stmt_da()->sql_errno(), |
6536 | thd->get_stmt_da()->message()); |
6537 | thd->clear_error(); |
6538 | res= 0; |
6539 | } |
6540 | DBUG_RETURN(res); |
6541 | } |
6542 | else if (!tables->view) |
6543 | { |
6544 | TABLE *show_table= tables->table; |
6545 | KEY *key_info=show_table->s->key_info; |
6546 | if (show_table->file) |
6547 | { |
6548 | show_table->file->info(HA_STATUS_VARIABLE | |
6549 | HA_STATUS_NO_LOCK | |
6550 | HA_STATUS_TIME); |
6551 | set_statistics_for_table(thd, show_table); |
6552 | } |
6553 | for (uint i=0 ; i < show_table->s->keys ; i++,key_info++) |
6554 | { |
6555 | if ((key_info->flags & HA_INVISIBLE_KEY) && |
6556 | DBUG_EVALUATE_IF("test_invisible_index" , 0, 1)) |
6557 | continue; |
6558 | KEY_PART_INFO *key_part= key_info->key_part; |
6559 | LEX_CSTRING *str; |
6560 | LEX_CSTRING unknown= {STRING_WITH_LEN("?unknown field?" ) }; |
6561 | for (uint j=0 ; j < key_info->user_defined_key_parts ; j++,key_part++) |
6562 | { |
6563 | restore_record(table, s->default_values); |
6564 | table->field[0]->store(STRING_WITH_LEN("def" ), cs); |
6565 | table->field[1]->store(db_name->str, db_name->length, cs); |
6566 | table->field[2]->store(table_name->str, table_name->length, cs); |
6567 | table->field[3]->store((longlong) ((key_info->flags & |
6568 | HA_NOSAME) ? 0 : 1), TRUE); |
6569 | table->field[4]->store(db_name->str, db_name->length, cs); |
6570 | table->field[5]->store(key_info->name.str, key_info->name.length, cs); |
6571 | table->field[6]->store((longlong) (j+1), TRUE); |
6572 | str= (key_part->field ? &key_part->field->field_name : |
6573 | &unknown); |
6574 | table->field[7]->store(str->str, str->length, cs); |
6575 | if (show_table->file) |
6576 | { |
6577 | if (show_table->file->index_flags(i, j, 0) & HA_READ_ORDER) |
6578 | { |
6579 | table->field[8]->store(((key_part->key_part_flag & |
6580 | HA_REVERSE_SORT) ? |
6581 | "D" : "A" ), 1, cs); |
6582 | table->field[8]->set_notnull(); |
6583 | } |
6584 | KEY *key=show_table->key_info+i; |
6585 | if (key->rec_per_key[j]) |
6586 | { |
6587 | ha_rows records= (ha_rows) ((double) show_table->stat_records() / |
6588 | key->actual_rec_per_key(j)); |
6589 | table->field[9]->store((longlong) records, TRUE); |
6590 | table->field[9]->set_notnull(); |
6591 | } |
6592 | const char *tmp= show_table->file->index_type(i); |
6593 | table->field[13]->store(tmp, strlen(tmp), cs); |
6594 | } |
6595 | if (!(key_info->flags & HA_FULLTEXT) && |
6596 | (key_part->field && |
6597 | key_part->length != |
6598 | show_table->s->field[key_part->fieldnr-1]->key_length())) |
6599 | { |
6600 | table->field[10]->store((longlong) key_part->length / |
6601 | key_part->field->charset()->mbmaxlen, TRUE); |
6602 | table->field[10]->set_notnull(); |
6603 | } |
6604 | uint flags= key_part->field ? key_part->field->flags : 0; |
6605 | const char *pos=(char*) ((flags & NOT_NULL_FLAG) ? "" : "YES" ); |
6606 | table->field[12]->store(pos, strlen(pos), cs); |
6607 | if (!show_table->s->keys_in_use.is_set(i)) |
6608 | table->field[14]->store(STRING_WITH_LEN("disabled" ), cs); |
6609 | else |
6610 | table->field[14]->store("" , 0, cs); |
6611 | table->field[14]->set_notnull(); |
6612 | DBUG_ASSERT(MY_TEST(key_info->flags & HA_USES_COMMENT) == |
6613 | (key_info->comment.length > 0)); |
6614 | if (key_info->flags & HA_USES_COMMENT) |
6615 | table->field[15]->store(key_info->comment.str, |
6616 | key_info->comment.length, cs); |
6617 | if (schema_table_store_record(thd, table)) |
6618 | DBUG_RETURN(1); |
6619 | } |
6620 | } |
6621 | } |
6622 | DBUG_RETURN(res); |
6623 | } |
6624 | |
6625 | |
6626 | static int get_schema_views_record(THD *thd, TABLE_LIST *tables, |
6627 | TABLE *table, bool res, |
6628 | const LEX_CSTRING *db_name, |
6629 | const LEX_CSTRING *table_name) |
6630 | { |
6631 | CHARSET_INFO *cs= system_charset_info; |
6632 | char definer[USER_HOST_BUFF_SIZE]; |
6633 | uint definer_len; |
6634 | bool updatable_view; |
6635 | DBUG_ENTER("get_schema_views_record" ); |
6636 | |
6637 | if (tables->view) |
6638 | { |
6639 | Security_context *sctx= thd->security_ctx; |
6640 | if (!tables->allowed_show) |
6641 | { |
6642 | if (!my_strcasecmp(system_charset_info, tables->definer.user.str, |
6643 | sctx->priv_user) && |
6644 | !my_strcasecmp(system_charset_info, tables->definer.host.str, |
6645 | sctx->priv_host)) |
6646 | tables->allowed_show= TRUE; |
6647 | #ifndef NO_EMBEDDED_ACCESS_CHECKS |
6648 | else |
6649 | { |
6650 | if ((thd->col_access & (SHOW_VIEW_ACL|SELECT_ACL)) == |
6651 | (SHOW_VIEW_ACL|SELECT_ACL)) |
6652 | tables->allowed_show= TRUE; |
6653 | else |
6654 | { |
6655 | TABLE_LIST table_list; |
6656 | uint view_access; |
6657 | memset(&table_list, 0, sizeof(table_list)); |
6658 | table_list.db= tables->db; |
6659 | table_list.table_name= tables->table_name; |
6660 | table_list.grant.privilege= thd->col_access; |
6661 | view_access= get_table_grant(thd, &table_list); |
6662 | if ((view_access & (SHOW_VIEW_ACL|SELECT_ACL)) == |
6663 | (SHOW_VIEW_ACL|SELECT_ACL)) |
6664 | tables->allowed_show= TRUE; |
6665 | } |
6666 | } |
6667 | #endif |
6668 | } |
6669 | restore_record(table, s->default_values); |
6670 | table->field[0]->store(STRING_WITH_LEN("def" ), cs); |
6671 | table->field[1]->store(db_name->str, db_name->length, cs); |
6672 | table->field[2]->store(table_name->str, table_name->length, cs); |
6673 | |
6674 | if (tables->allowed_show) |
6675 | { |
6676 | table->field[3]->store(tables->view_body_utf8.str, |
6677 | tables->view_body_utf8.length, |
6678 | cs); |
6679 | } |
6680 | |
6681 | if (tables->with_check != VIEW_CHECK_NONE) |
6682 | { |
6683 | if (tables->with_check == VIEW_CHECK_LOCAL) |
6684 | table->field[4]->store(STRING_WITH_LEN("LOCAL" ), cs); |
6685 | else |
6686 | table->field[4]->store(STRING_WITH_LEN("CASCADED" ), cs); |
6687 | } |
6688 | else |
6689 | table->field[4]->store(STRING_WITH_LEN("NONE" ), cs); |
6690 | |
6691 | /* |
6692 | Only try to fill in the information about view updatability |
6693 | if it is requested as part of the top-level query (i.e. |
6694 | it's select * from i_s.views, as opposed to, say, select |
6695 | security_type from i_s.views). Do not try to access the |
6696 | underlying tables if there was an error when opening the |
6697 | view: all underlying tables are released back to the table |
6698 | definition cache on error inside open_normal_and_derived_tables(). |
6699 | If a field is not assigned explicitly, it defaults to NULL. |
6700 | */ |
6701 | if (res == FALSE && |
6702 | table->pos_in_table_list->table_open_method & OPEN_FULL_TABLE) |
6703 | { |
6704 | updatable_view= 0; |
6705 | if (tables->algorithm != VIEW_ALGORITHM_TMPTABLE) |
6706 | { |
6707 | /* |
6708 | We should use tables->view->select_lex.item_list here |
6709 | and can not use Field_iterator_view because the view |
6710 | always uses temporary algorithm during opening for I_S |
6711 | and TABLE_LIST fields 'field_translation' |
6712 | & 'field_translation_end' are uninitialized is this |
6713 | case. |
6714 | */ |
6715 | List<Item> *fields= &tables->view->select_lex.item_list; |
6716 | List_iterator<Item> it(*fields); |
6717 | Item *item; |
6718 | Item_field *field; |
6719 | /* |
6720 | check that at least one column in view is updatable |
6721 | */ |
6722 | while ((item= it++)) |
6723 | { |
6724 | if ((field= item->field_for_view_update()) && field->field && |
6725 | !field->field->table->pos_in_table_list->schema_table) |
6726 | { |
6727 | updatable_view= 1; |
6728 | break; |
6729 | } |
6730 | } |
6731 | if (updatable_view && !tables->view->can_be_merged()) |
6732 | updatable_view= 0; |
6733 | } |
6734 | if (updatable_view) |
6735 | table->field[5]->store(STRING_WITH_LEN("YES" ), cs); |
6736 | else |
6737 | table->field[5]->store(STRING_WITH_LEN("NO" ), cs); |
6738 | } |
6739 | |
6740 | definer_len= (uint)(strxmov(definer, tables->definer.user.str, "@" , |
6741 | tables->definer.host.str, NullS) - definer); |
6742 | table->field[6]->store(definer, definer_len, cs); |
6743 | if (tables->view_suid) |
6744 | table->field[7]->store(STRING_WITH_LEN("DEFINER" ), cs); |
6745 | else |
6746 | table->field[7]->store(STRING_WITH_LEN("INVOKER" ), cs); |
6747 | |
6748 | table->field[8]->store(tables->view_creation_ctx->get_client_cs()->csname, |
6749 | strlen(tables->view_creation_ctx-> |
6750 | get_client_cs()->csname), cs); |
6751 | |
6752 | table->field[9]->store(tables->view_creation_ctx-> |
6753 | get_connection_cl()->name, |
6754 | strlen(tables->view_creation_ctx-> |
6755 | get_connection_cl()->name), cs); |
6756 | |
6757 | table->field[10]->store(view_algorithm(tables), cs); |
6758 | |
6759 | if (schema_table_store_record(thd, table)) |
6760 | DBUG_RETURN(1); |
6761 | if (unlikely(res && thd->is_error())) |
6762 | push_warning(thd, Sql_condition::WARN_LEVEL_WARN, |
6763 | thd->get_stmt_da()->sql_errno(), |
6764 | thd->get_stmt_da()->message()); |
6765 | } |
6766 | if (res) |
6767 | thd->clear_error(); |
6768 | DBUG_RETURN(0); |
6769 | } |
6770 | |
6771 | |
6772 | static bool |
6773 | store_constraints(THD *thd, TABLE *table, const LEX_CSTRING *db_name, |
6774 | const LEX_CSTRING *table_name, const char *key_name, |
6775 | size_t key_len, const char *con_type, size_t con_len) |
6776 | { |
6777 | CHARSET_INFO *cs= system_charset_info; |
6778 | restore_record(table, s->default_values); |
6779 | table->field[0]->store(STRING_WITH_LEN("def" ), cs); |
6780 | table->field[1]->store(db_name->str, db_name->length, cs); |
6781 | table->field[2]->store(key_name, key_len, cs); |
6782 | table->field[3]->store(db_name->str, db_name->length, cs); |
6783 | table->field[4]->store(table_name->str, table_name->length, cs); |
6784 | table->field[5]->store(con_type, con_len, cs); |
6785 | return schema_table_store_record(thd, table); |
6786 | } |
6787 | |
6788 | |
6789 | static int get_schema_constraints_record(THD *thd, TABLE_LIST *tables, |
6790 | TABLE *table, bool res, |
6791 | const LEX_CSTRING *db_name, |
6792 | const LEX_CSTRING *table_name) |
6793 | { |
6794 | DBUG_ENTER("get_schema_constraints_record" ); |
6795 | if (res) |
6796 | { |
6797 | if (unlikely(thd->is_error())) |
6798 | push_warning(thd, Sql_condition::WARN_LEVEL_WARN, |
6799 | thd->get_stmt_da()->sql_errno(), |
6800 | thd->get_stmt_da()->message()); |
6801 | thd->clear_error(); |
6802 | DBUG_RETURN(0); |
6803 | } |
6804 | else if (!tables->view) |
6805 | { |
6806 | List<FOREIGN_KEY_INFO> f_key_list; |
6807 | TABLE *show_table= tables->table; |
6808 | KEY *key_info=show_table->key_info; |
6809 | uint primary_key= show_table->s->primary_key; |
6810 | show_table->file->info(HA_STATUS_VARIABLE | |
6811 | HA_STATUS_NO_LOCK | |
6812 | HA_STATUS_TIME); |
6813 | for (uint i=0 ; i < show_table->s->keys ; i++, key_info++) |
6814 | { |
6815 | if (i != primary_key && !(key_info->flags & HA_NOSAME)) |
6816 | continue; |
6817 | |
6818 | if (i == primary_key && !strcmp(key_info->name.str, primary_key_name)) |
6819 | { |
6820 | if (store_constraints(thd, table, db_name, table_name, |
6821 | key_info->name.str, key_info->name.length, |
6822 | STRING_WITH_LEN("PRIMARY KEY" ))) |
6823 | DBUG_RETURN(1); |
6824 | } |
6825 | else if (key_info->flags & HA_NOSAME) |
6826 | { |
6827 | if (store_constraints(thd, table, db_name, table_name, |
6828 | key_info->name.str, key_info->name.length, |
6829 | STRING_WITH_LEN("UNIQUE" ))) |
6830 | DBUG_RETURN(1); |
6831 | } |
6832 | } |
6833 | |
6834 | // Table check constraints |
6835 | for ( uint i = 0; i < show_table->s->table_check_constraints; i++ ) |
6836 | { |
6837 | Virtual_column_info *check = show_table->check_constraints[ i ]; |
6838 | |
6839 | if ( store_constraints( thd, table, db_name, table_name, check->name.str, |
6840 | check->name.length, |
6841 | STRING_WITH_LEN( "CHECK" ) ) ) |
6842 | { |
6843 | DBUG_RETURN( 1 ); |
6844 | } |
6845 | } |
6846 | |
6847 | show_table->file->get_foreign_key_list(thd, &f_key_list); |
6848 | FOREIGN_KEY_INFO *f_key_info; |
6849 | List_iterator_fast<FOREIGN_KEY_INFO> it(f_key_list); |
6850 | while ((f_key_info=it++)) |
6851 | { |
6852 | if (store_constraints(thd, table, db_name, table_name, |
6853 | f_key_info->foreign_id->str, |
6854 | strlen(f_key_info->foreign_id->str), |
6855 | "FOREIGN KEY" , 11)) |
6856 | DBUG_RETURN(1); |
6857 | } |
6858 | } |
6859 | DBUG_RETURN(res); |
6860 | } |
6861 | |
6862 | |
6863 | static bool store_trigger(THD *thd, Trigger *trigger, |
6864 | TABLE *table, const LEX_CSTRING *db_name, |
6865 | const LEX_CSTRING *table_name) |
6866 | { |
6867 | CHARSET_INFO *cs= system_charset_info; |
6868 | LEX_CSTRING sql_mode_rep; |
6869 | MYSQL_TIME timestamp; |
6870 | char definer_holder[USER_HOST_BUFF_SIZE]; |
6871 | LEX_STRING definer_buffer; |
6872 | LEX_CSTRING trigger_stmt, trigger_body; |
6873 | definer_buffer.str= definer_holder; |
6874 | |
6875 | trigger->get_trigger_info(&trigger_stmt, &trigger_body, &definer_buffer); |
6876 | |
6877 | restore_record(table, s->default_values); |
6878 | table->field[0]->store(STRING_WITH_LEN("def" ), cs); |
6879 | table->field[1]->store(db_name->str, db_name->length, cs); |
6880 | table->field[2]->store(trigger->name.str, trigger->name.length, cs); |
6881 | table->field[3]->store(trg_event_type_names[trigger->event].str, |
6882 | trg_event_type_names[trigger->event].length, cs); |
6883 | table->field[4]->store(STRING_WITH_LEN("def" ), cs); |
6884 | table->field[5]->store(db_name->str, db_name->length, cs); |
6885 | table->field[6]->store(table_name->str, table_name->length, cs); |
6886 | table->field[7]->store(trigger->action_order); |
6887 | table->field[9]->store(trigger_body.str, trigger_body.length, cs); |
6888 | table->field[10]->store(STRING_WITH_LEN("ROW" ), cs); |
6889 | table->field[11]->store(trg_action_time_type_names[trigger->action_time].str, |
6890 | trg_action_time_type_names[trigger->action_time].length, cs); |
6891 | table->field[14]->store(STRING_WITH_LEN("OLD" ), cs); |
6892 | table->field[15]->store(STRING_WITH_LEN("NEW" ), cs); |
6893 | |
6894 | if (trigger->create_time) |
6895 | { |
6896 | table->field[16]->set_notnull(); |
6897 | thd->variables.time_zone->gmt_sec_to_TIME(×tamp, |
6898 | (my_time_t)(trigger->create_time/100)); |
6899 | /* timestamp is with 6 digits */ |
6900 | timestamp.second_part= (trigger->create_time % 100) * 10000; |
6901 | ((Field_temporal_with_date*) table->field[16])->store_time_dec(×tamp, |
6902 | 2); |
6903 | } |
6904 | |
6905 | sql_mode_string_representation(thd, trigger->sql_mode, &sql_mode_rep); |
6906 | table->field[17]->store(sql_mode_rep.str, sql_mode_rep.length, cs); |
6907 | table->field[18]->store(definer_buffer.str, definer_buffer.length, cs); |
6908 | table->field[19]->store(trigger->client_cs_name.str, |
6909 | trigger->client_cs_name.length, cs); |
6910 | table->field[20]->store(trigger->connection_cl_name.str, |
6911 | trigger->connection_cl_name.length, cs); |
6912 | table->field[21]->store(trigger->db_cl_name.str, |
6913 | trigger->db_cl_name.length, cs); |
6914 | |
6915 | return schema_table_store_record(thd, table); |
6916 | } |
6917 | |
6918 | |
6919 | static int get_schema_triggers_record(THD *thd, TABLE_LIST *tables, |
6920 | TABLE *table, bool res, |
6921 | const LEX_CSTRING *db_name, |
6922 | const LEX_CSTRING *table_name) |
6923 | { |
6924 | DBUG_ENTER("get_schema_triggers_record" ); |
6925 | /* |
6926 | res can be non zero value when processed table is a view or |
6927 | error happened during opening of processed table. |
6928 | */ |
6929 | if (res) |
6930 | { |
6931 | if (unlikely(thd->is_error())) |
6932 | push_warning(thd, Sql_condition::WARN_LEVEL_WARN, |
6933 | thd->get_stmt_da()->sql_errno(), |
6934 | thd->get_stmt_da()->message()); |
6935 | thd->clear_error(); |
6936 | DBUG_RETURN(0); |
6937 | } |
6938 | if (!tables->view && tables->table->triggers) |
6939 | { |
6940 | Table_triggers_list *triggers= tables->table->triggers; |
6941 | int event, timing; |
6942 | |
6943 | if (check_table_access(thd, TRIGGER_ACL, tables, FALSE, 1, TRUE)) |
6944 | goto ret; |
6945 | |
6946 | for (event= 0; event < (int)TRG_EVENT_MAX; event++) |
6947 | { |
6948 | for (timing= 0; timing < (int)TRG_ACTION_MAX; timing++) |
6949 | { |
6950 | Trigger *trigger; |
6951 | for (trigger= triggers-> |
6952 | get_trigger((enum trg_event_type) event, |
6953 | (enum trg_action_time_type) timing) ; |
6954 | trigger; |
6955 | trigger= trigger->next) |
6956 | { |
6957 | if (store_trigger(thd, trigger, table, db_name, table_name)) |
6958 | DBUG_RETURN(1); |
6959 | } |
6960 | } |
6961 | } |
6962 | } |
6963 | ret: |
6964 | DBUG_RETURN(0); |
6965 | } |
6966 | |
6967 | |
6968 | static void |
6969 | store_key_column_usage(TABLE *table, const LEX_CSTRING *db_name, |
6970 | const LEX_CSTRING *table_name, const char *key_name, |
6971 | size_t key_len, const char *con_type, size_t con_len, |
6972 | longlong idx) |
6973 | { |
6974 | CHARSET_INFO *cs= system_charset_info; |
6975 | table->field[0]->store(STRING_WITH_LEN("def" ), cs); |
6976 | table->field[1]->store(db_name->str, db_name->length, cs); |
6977 | table->field[2]->store(key_name, key_len, cs); |
6978 | table->field[3]->store(STRING_WITH_LEN("def" ), cs); |
6979 | table->field[4]->store(db_name->str, db_name->length, cs); |
6980 | table->field[5]->store(table_name->str, table_name->length, cs); |
6981 | table->field[6]->store(con_type, con_len, cs); |
6982 | table->field[7]->store((longlong) idx, TRUE); |
6983 | } |
6984 | |
6985 | |
6986 | static int get_schema_key_column_usage_record(THD *thd, |
6987 | TABLE_LIST *tables, |
6988 | TABLE *table, bool res, |
6989 | const LEX_CSTRING *db_name, |
6990 | const LEX_CSTRING *table_name) |
6991 | { |
6992 | DBUG_ENTER("get_schema_key_column_usage_record" ); |
6993 | if (res) |
6994 | { |
6995 | if (unlikely(thd->is_error())) |
6996 | push_warning(thd, Sql_condition::WARN_LEVEL_WARN, |
6997 | thd->get_stmt_da()->sql_errno(), |
6998 | thd->get_stmt_da()->message()); |
6999 | thd->clear_error(); |
7000 | DBUG_RETURN(0); |
7001 | } |
7002 | else if (!tables->view) |
7003 | { |
7004 | List<FOREIGN_KEY_INFO> f_key_list; |
7005 | TABLE *show_table= tables->table; |
7006 | KEY *key_info=show_table->key_info; |
7007 | uint primary_key= show_table->s->primary_key; |
7008 | show_table->file->info(HA_STATUS_VARIABLE | |
7009 | HA_STATUS_NO_LOCK | |
7010 | HA_STATUS_TIME); |
7011 | for (uint i=0 ; i < show_table->s->keys ; i++, key_info++) |
7012 | { |
7013 | if (i != primary_key && !(key_info->flags & HA_NOSAME)) |
7014 | continue; |
7015 | uint f_idx= 0; |
7016 | KEY_PART_INFO *key_part= key_info->key_part; |
7017 | for (uint j=0 ; j < key_info->user_defined_key_parts ; j++,key_part++) |
7018 | { |
7019 | if (key_part->field) |
7020 | { |
7021 | f_idx++; |
7022 | restore_record(table, s->default_values); |
7023 | store_key_column_usage(table, db_name, table_name, |
7024 | key_info->name.str, key_info->name.length, |
7025 | key_part->field->field_name.str, |
7026 | key_part->field->field_name.length, |
7027 | (longlong) f_idx); |
7028 | if (schema_table_store_record(thd, table)) |
7029 | DBUG_RETURN(1); |
7030 | } |
7031 | } |
7032 | } |
7033 | |
7034 | show_table->file->get_foreign_key_list(thd, &f_key_list); |
7035 | FOREIGN_KEY_INFO *f_key_info; |
7036 | List_iterator_fast<FOREIGN_KEY_INFO> fkey_it(f_key_list); |
7037 | while ((f_key_info= fkey_it++)) |
7038 | { |
7039 | LEX_CSTRING *f_info; |
7040 | LEX_CSTRING *r_info; |
7041 | List_iterator_fast<LEX_CSTRING> it(f_key_info->foreign_fields), |
7042 | it1(f_key_info->referenced_fields); |
7043 | uint f_idx= 0; |
7044 | while ((f_info= it++)) |
7045 | { |
7046 | r_info= it1++; |
7047 | f_idx++; |
7048 | restore_record(table, s->default_values); |
7049 | store_key_column_usage(table, db_name, table_name, |
7050 | f_key_info->foreign_id->str, |
7051 | f_key_info->foreign_id->length, |
7052 | f_info->str, f_info->length, |
7053 | (longlong) f_idx); |
7054 | table->field[8]->store((longlong) f_idx, TRUE); |
7055 | table->field[8]->set_notnull(); |
7056 | table->field[9]->store(f_key_info->referenced_db->str, |
7057 | f_key_info->referenced_db->length, |
7058 | system_charset_info); |
7059 | table->field[9]->set_notnull(); |
7060 | table->field[10]->store(f_key_info->referenced_table->str, |
7061 | f_key_info->referenced_table->length, |
7062 | system_charset_info); |
7063 | table->field[10]->set_notnull(); |
7064 | table->field[11]->store(r_info->str, r_info->length, |
7065 | system_charset_info); |
7066 | table->field[11]->set_notnull(); |
7067 | if (schema_table_store_record(thd, table)) |
7068 | DBUG_RETURN(1); |
7069 | } |
7070 | } |
7071 | } |
7072 | DBUG_RETURN(res); |
7073 | } |
7074 | |
7075 | |
7076 | #ifdef WITH_PARTITION_STORAGE_ENGINE |
7077 | static void collect_partition_expr(THD *thd, List<const char> &field_list, |
7078 | String *str) |
7079 | { |
7080 | List_iterator<const char> part_it(field_list); |
7081 | ulong no_fields= field_list.elements; |
7082 | const char *field_str; |
7083 | str->length(0); |
7084 | while ((field_str= part_it++)) |
7085 | { |
7086 | append_identifier(thd, str, field_str, strlen(field_str)); |
7087 | if (--no_fields != 0) |
7088 | str->append("," ); |
7089 | } |
7090 | return; |
7091 | } |
7092 | |
7093 | |
7094 | /* |
7095 | Convert a string in a given character set to a string which can be |
7096 | used for FRM file storage in which case use_hex is TRUE and we store |
7097 | the character constants as hex strings in the character set encoding |
7098 | their field have. In the case of SHOW CREATE TABLE and the |
7099 | PARTITIONS information schema table we instead provide utf8 strings |
7100 | to the user and convert to the utf8 character set. |
7101 | |
7102 | SYNOPSIS |
7103 | get_cs_converted_part_value_from_string() |
7104 | item Item from which constant comes |
7105 | input_str String as provided by val_str after |
7106 | conversion to character set |
7107 | output_str Out value: The string created |
7108 | cs Character set string is encoded in |
7109 | NULL for INT_RESULT's here |
7110 | use_hex TRUE => hex string created |
7111 | FALSE => utf8 constant string created |
7112 | |
7113 | RETURN VALUES |
7114 | TRUE Error |
7115 | FALSE Ok |
7116 | */ |
7117 | |
7118 | int get_cs_converted_part_value_from_string(THD *thd, |
7119 | Item *item, |
7120 | String *input_str, |
7121 | String *output_str, |
7122 | CHARSET_INFO *cs, |
7123 | bool use_hex) |
7124 | { |
7125 | if (item->result_type() == INT_RESULT) |
7126 | { |
7127 | longlong value= item->val_int(); |
7128 | output_str->set(value, system_charset_info); |
7129 | return FALSE; |
7130 | } |
7131 | if (!input_str) |
7132 | { |
7133 | my_error(ER_PARTITION_FUNCTION_IS_NOT_ALLOWED, MYF(0)); |
7134 | return TRUE; |
7135 | } |
7136 | get_cs_converted_string_value(thd, |
7137 | input_str, |
7138 | output_str, |
7139 | cs, |
7140 | use_hex); |
7141 | return FALSE; |
7142 | } |
7143 | #endif |
7144 | |
7145 | |
7146 | static void store_schema_partitions_record(THD *thd, TABLE *schema_table, |
7147 | TABLE *showing_table, |
7148 | partition_element *part_elem, |
7149 | handler *file, uint part_id) |
7150 | { |
7151 | TABLE* table= schema_table; |
7152 | CHARSET_INFO *cs= system_charset_info; |
7153 | PARTITION_STATS stat_info; |
7154 | MYSQL_TIME time; |
7155 | file->get_dynamic_partition_info(&stat_info, part_id); |
7156 | table->field[0]->store(STRING_WITH_LEN("def" ), cs); |
7157 | table->field[12]->store((longlong) stat_info.records, TRUE); |
7158 | table->field[13]->store((longlong) stat_info.mean_rec_length, TRUE); |
7159 | table->field[14]->store((longlong) stat_info.data_file_length, TRUE); |
7160 | if (stat_info.max_data_file_length) |
7161 | { |
7162 | table->field[15]->store((longlong) stat_info.max_data_file_length, TRUE); |
7163 | table->field[15]->set_notnull(); |
7164 | } |
7165 | table->field[16]->store((longlong) stat_info.index_file_length, TRUE); |
7166 | table->field[17]->store((longlong) stat_info.delete_length, TRUE); |
7167 | if (stat_info.create_time) |
7168 | { |
7169 | thd->variables.time_zone->gmt_sec_to_TIME(&time, |
7170 | (my_time_t)stat_info.create_time); |
7171 | table->field[18]->store_time(&time); |
7172 | table->field[18]->set_notnull(); |
7173 | } |
7174 | if (stat_info.update_time) |
7175 | { |
7176 | thd->variables.time_zone->gmt_sec_to_TIME(&time, |
7177 | (my_time_t)stat_info.update_time); |
7178 | table->field[19]->store_time(&time); |
7179 | table->field[19]->set_notnull(); |
7180 | } |
7181 | if (stat_info.check_time) |
7182 | { |
7183 | thd->variables.time_zone->gmt_sec_to_TIME(&time, |
7184 | (my_time_t)stat_info.check_time); |
7185 | table->field[20]->store_time(&time); |
7186 | table->field[20]->set_notnull(); |
7187 | } |
7188 | if (file->ha_table_flags() & (HA_HAS_OLD_CHECKSUM | HA_HAS_NEW_CHECKSUM)) |
7189 | { |
7190 | table->field[21]->store((longlong) stat_info.check_sum, TRUE); |
7191 | table->field[21]->set_notnull(); |
7192 | } |
7193 | if (part_elem) |
7194 | { |
7195 | if (part_elem->part_comment) |
7196 | table->field[22]->store(part_elem->part_comment, |
7197 | strlen(part_elem->part_comment), cs); |
7198 | else |
7199 | table->field[22]->store(STRING_WITH_LEN("" ), cs); |
7200 | if (part_elem->nodegroup_id != UNDEF_NODEGROUP) |
7201 | table->field[23]->store((longlong) part_elem->nodegroup_id, TRUE); |
7202 | else |
7203 | table->field[23]->store(STRING_WITH_LEN("default" ), cs); |
7204 | |
7205 | table->field[24]->set_notnull(); |
7206 | if (part_elem->tablespace_name) |
7207 | table->field[24]->store(part_elem->tablespace_name, |
7208 | strlen(part_elem->tablespace_name), cs); |
7209 | else |
7210 | { |
7211 | char *ts= showing_table->s->tablespace; |
7212 | if(ts) |
7213 | table->field[24]->store(ts, strlen(ts), cs); |
7214 | else |
7215 | table->field[24]->set_null(); |
7216 | } |
7217 | } |
7218 | return; |
7219 | } |
7220 | |
7221 | #ifdef WITH_PARTITION_STORAGE_ENGINE |
7222 | static int get_partition_column_description(THD *thd, partition_info *part_info, |
7223 | part_elem_value *list_value, String &tmp_str) |
7224 | { |
7225 | uint num_elements= part_info->part_field_list.elements; |
7226 | uint i; |
7227 | DBUG_ENTER("get_partition_column_description" ); |
7228 | |
7229 | for (i= 0; i < num_elements; i++) |
7230 | { |
7231 | part_column_list_val *col_val= &list_value->col_val_array[i]; |
7232 | if (col_val->max_value) |
7233 | tmp_str.append(STRING_WITH_LEN("MAXVALUE" )); |
7234 | else if (col_val->null_value) |
7235 | tmp_str.append("NULL" ); |
7236 | else |
7237 | { |
7238 | char buffer[MAX_KEY_LENGTH]; |
7239 | String str(buffer, sizeof(buffer), &my_charset_bin); |
7240 | String val_conv; |
7241 | Item *item= col_val->item_expression; |
7242 | |
7243 | if (!(item= part_info->get_column_item(item, |
7244 | part_info->part_field_array[i]))) |
7245 | { |
7246 | DBUG_RETURN(1); |
7247 | } |
7248 | String *res= item->val_str(&str); |
7249 | if (get_cs_converted_part_value_from_string(thd, item, res, &val_conv, |
7250 | part_info->part_field_array[i]->charset(), |
7251 | FALSE)) |
7252 | { |
7253 | DBUG_RETURN(1); |
7254 | } |
7255 | tmp_str.append(val_conv); |
7256 | } |
7257 | if (i != num_elements - 1) |
7258 | tmp_str.append("," ); |
7259 | } |
7260 | DBUG_RETURN(0); |
7261 | } |
7262 | #endif /* WITH_PARTITION_STORAGE_ENGINE */ |
7263 | |
7264 | static int get_schema_partitions_record(THD *thd, TABLE_LIST *tables, |
7265 | TABLE *table, bool res, |
7266 | const LEX_CSTRING *db_name, |
7267 | const LEX_CSTRING *table_name) |
7268 | { |
7269 | CHARSET_INFO *cs= system_charset_info; |
7270 | char buff[61]; |
7271 | String tmp_res(buff, sizeof(buff), cs); |
7272 | String tmp_str; |
7273 | TABLE *show_table= tables->table; |
7274 | handler *file; |
7275 | #ifdef WITH_PARTITION_STORAGE_ENGINE |
7276 | partition_info *part_info; |
7277 | #endif |
7278 | DBUG_ENTER("get_schema_partitions_record" ); |
7279 | |
7280 | if (res) |
7281 | { |
7282 | if (unlikely(thd->is_error())) |
7283 | push_warning(thd, Sql_condition::WARN_LEVEL_WARN, |
7284 | thd->get_stmt_da()->sql_errno(), |
7285 | thd->get_stmt_da()->message()); |
7286 | thd->clear_error(); |
7287 | DBUG_RETURN(0); |
7288 | } |
7289 | file= show_table->file; |
7290 | #ifdef WITH_PARTITION_STORAGE_ENGINE |
7291 | part_info= show_table->part_info; |
7292 | if (part_info) |
7293 | { |
7294 | partition_element *part_elem; |
7295 | List_iterator<partition_element> part_it(part_info->partitions); |
7296 | uint part_pos= 0, part_id= 0; |
7297 | |
7298 | restore_record(table, s->default_values); |
7299 | table->field[0]->store(STRING_WITH_LEN("def" ), cs); |
7300 | table->field[1]->store(db_name->str, db_name->length, cs); |
7301 | table->field[2]->store(table_name->str, table_name->length, cs); |
7302 | |
7303 | |
7304 | /* Partition method*/ |
7305 | switch (part_info->part_type) { |
7306 | case RANGE_PARTITION: |
7307 | case LIST_PARTITION: |
7308 | tmp_res.length(0); |
7309 | if (part_info->part_type == RANGE_PARTITION) |
7310 | tmp_res.append(STRING_WITH_LEN("RANGE" )); |
7311 | else |
7312 | tmp_res.append(STRING_WITH_LEN("LIST" )); |
7313 | if (part_info->column_list) |
7314 | tmp_res.append(STRING_WITH_LEN(" COLUMNS" )); |
7315 | table->field[7]->store(tmp_res.ptr(), tmp_res.length(), cs); |
7316 | break; |
7317 | case HASH_PARTITION: |
7318 | tmp_res.length(0); |
7319 | if (part_info->linear_hash_ind) |
7320 | tmp_res.append(STRING_WITH_LEN("LINEAR " )); |
7321 | if (part_info->list_of_part_fields) |
7322 | tmp_res.append(STRING_WITH_LEN("KEY" )); |
7323 | else |
7324 | tmp_res.append(STRING_WITH_LEN("HASH" )); |
7325 | table->field[7]->store(tmp_res.ptr(), tmp_res.length(), cs); |
7326 | break; |
7327 | case VERSIONING_PARTITION: |
7328 | table->field[7]->store(STRING_WITH_LEN("SYSTEM_TIME" ), cs); |
7329 | break; |
7330 | default: |
7331 | DBUG_ASSERT(0); |
7332 | my_error(ER_OUT_OF_RESOURCES, MYF(ME_FATALERROR)); |
7333 | DBUG_RETURN(1); |
7334 | } |
7335 | table->field[7]->set_notnull(); |
7336 | |
7337 | /* Partition expression */ |
7338 | if (part_info->part_expr) |
7339 | { |
7340 | StringBuffer<STRING_BUFFER_USUAL_SIZE> str(cs); |
7341 | part_info->part_expr->print_for_table_def(&str); |
7342 | table->field[9]->store(str.ptr(), str.length(), str.charset()); |
7343 | } |
7344 | else if (part_info->list_of_part_fields) |
7345 | { |
7346 | collect_partition_expr(thd, part_info->part_field_list, &tmp_str); |
7347 | table->field[9]->store(tmp_str.ptr(), tmp_str.length(), cs); |
7348 | } |
7349 | table->field[9]->set_notnull(); |
7350 | |
7351 | if (part_info->is_sub_partitioned()) |
7352 | { |
7353 | /* Subpartition method */ |
7354 | tmp_res.length(0); |
7355 | if (part_info->linear_hash_ind) |
7356 | tmp_res.append(STRING_WITH_LEN("LINEAR " )); |
7357 | if (part_info->list_of_subpart_fields) |
7358 | tmp_res.append(STRING_WITH_LEN("KEY" )); |
7359 | else |
7360 | tmp_res.append(STRING_WITH_LEN("HASH" )); |
7361 | table->field[8]->store(tmp_res.ptr(), tmp_res.length(), cs); |
7362 | table->field[8]->set_notnull(); |
7363 | |
7364 | /* Subpartition expression */ |
7365 | if (part_info->subpart_expr) |
7366 | { |
7367 | StringBuffer<STRING_BUFFER_USUAL_SIZE> str(cs); |
7368 | part_info->subpart_expr->print_for_table_def(&str); |
7369 | table->field[10]->store(str.ptr(), str.length(), str.charset()); |
7370 | } |
7371 | else if (part_info->list_of_subpart_fields) |
7372 | { |
7373 | collect_partition_expr(thd, part_info->subpart_field_list, &tmp_str); |
7374 | table->field[10]->store(tmp_str.ptr(), tmp_str.length(), cs); |
7375 | } |
7376 | table->field[10]->set_notnull(); |
7377 | } |
7378 | |
7379 | while ((part_elem= part_it++)) |
7380 | { |
7381 | table->field[3]->store(part_elem->partition_name, |
7382 | strlen(part_elem->partition_name), cs); |
7383 | table->field[3]->set_notnull(); |
7384 | /* PARTITION_ORDINAL_POSITION */ |
7385 | table->field[5]->store((longlong) ++part_pos, TRUE); |
7386 | table->field[5]->set_notnull(); |
7387 | |
7388 | /* Partition description */ |
7389 | if (part_info->part_type == RANGE_PARTITION) |
7390 | { |
7391 | if (part_info->column_list) |
7392 | { |
7393 | List_iterator<part_elem_value> list_val_it(part_elem->list_val_list); |
7394 | part_elem_value *list_value= list_val_it++; |
7395 | tmp_str.length(0); |
7396 | if (get_partition_column_description(thd, part_info, list_value, |
7397 | tmp_str)) |
7398 | DBUG_RETURN(1); |
7399 | table->field[11]->store(tmp_str.ptr(), tmp_str.length(), cs); |
7400 | } |
7401 | else |
7402 | { |
7403 | if (part_elem->range_value != LONGLONG_MAX) |
7404 | table->field[11]->store((longlong) part_elem->range_value, FALSE); |
7405 | else |
7406 | table->field[11]->store(STRING_WITH_LEN("MAXVALUE" ), cs); |
7407 | } |
7408 | table->field[11]->set_notnull(); |
7409 | } |
7410 | else if (part_info->part_type == LIST_PARTITION) |
7411 | { |
7412 | List_iterator<part_elem_value> list_val_it(part_elem->list_val_list); |
7413 | part_elem_value *list_value; |
7414 | uint num_items= part_elem->list_val_list.elements; |
7415 | tmp_str.length(0); |
7416 | tmp_res.length(0); |
7417 | if (part_elem->has_null_value) |
7418 | { |
7419 | tmp_str.append(STRING_WITH_LEN("NULL" )); |
7420 | if (num_items > 0) |
7421 | tmp_str.append("," ); |
7422 | } |
7423 | while ((list_value= list_val_it++)) |
7424 | { |
7425 | if (part_info->column_list) |
7426 | { |
7427 | if (part_info->part_field_list.elements > 1U) |
7428 | tmp_str.append(STRING_WITH_LEN("(" )); |
7429 | if (get_partition_column_description(thd, part_info, list_value, |
7430 | tmp_str)) |
7431 | DBUG_RETURN(1); |
7432 | if (part_info->part_field_list.elements > 1U) |
7433 | tmp_str.append(")" ); |
7434 | } |
7435 | else |
7436 | { |
7437 | if (!list_value->unsigned_flag) |
7438 | tmp_res.set(list_value->value, cs); |
7439 | else |
7440 | tmp_res.set((ulonglong)list_value->value, cs); |
7441 | tmp_str.append(tmp_res); |
7442 | } |
7443 | if (--num_items != 0) |
7444 | tmp_str.append("," ); |
7445 | } |
7446 | table->field[11]->store(tmp_str.ptr(), tmp_str.length(), cs); |
7447 | table->field[11]->set_notnull(); |
7448 | } |
7449 | else if (part_info->part_type == VERSIONING_PARTITION) |
7450 | { |
7451 | if (part_elem == part_info->vers_info->now_part) |
7452 | { |
7453 | table->field[11]->store(STRING_WITH_LEN("CURRENT" ), cs); |
7454 | table->field[11]->set_notnull(); |
7455 | } |
7456 | else if (part_info->vers_info->interval.is_set()) |
7457 | { |
7458 | table->field[11]->store_timestamp((my_time_t)part_elem->range_value, 0); |
7459 | table->field[11]->set_notnull(); |
7460 | } |
7461 | } |
7462 | |
7463 | if (part_elem->subpartitions.elements) |
7464 | { |
7465 | List_iterator<partition_element> sub_it(part_elem->subpartitions); |
7466 | partition_element *subpart_elem; |
7467 | uint subpart_pos= 0; |
7468 | |
7469 | while ((subpart_elem= sub_it++)) |
7470 | { |
7471 | table->field[4]->store(subpart_elem->partition_name, |
7472 | strlen(subpart_elem->partition_name), cs); |
7473 | table->field[4]->set_notnull(); |
7474 | /* SUBPARTITION_ORDINAL_POSITION */ |
7475 | table->field[6]->store((longlong) ++subpart_pos, TRUE); |
7476 | table->field[6]->set_notnull(); |
7477 | |
7478 | store_schema_partitions_record(thd, table, show_table, subpart_elem, |
7479 | file, part_id); |
7480 | part_id++; |
7481 | if(schema_table_store_record(thd, table)) |
7482 | DBUG_RETURN(1); |
7483 | } |
7484 | } |
7485 | else |
7486 | { |
7487 | store_schema_partitions_record(thd, table, show_table, part_elem, |
7488 | file, part_id); |
7489 | part_id++; |
7490 | if(schema_table_store_record(thd, table)) |
7491 | DBUG_RETURN(1); |
7492 | } |
7493 | } |
7494 | DBUG_RETURN(0); |
7495 | } |
7496 | else |
7497 | #endif |
7498 | { |
7499 | store_schema_partitions_record(thd, table, show_table, 0, file, 0); |
7500 | if(schema_table_store_record(thd, table)) |
7501 | DBUG_RETURN(1); |
7502 | } |
7503 | DBUG_RETURN(0); |
7504 | } |
7505 | |
7506 | |
7507 | #ifdef HAVE_EVENT_SCHEDULER |
7508 | /* |
7509 | Loads an event from mysql.event and copies it's data to a row of |
7510 | I_S.EVENTS |
7511 | |
7512 | Synopsis |
7513 | copy_event_to_schema_table() |
7514 | thd Thread |
7515 | sch_table The schema table (information_schema.event) |
7516 | event_table The event table to use for loading (mysql.event). |
7517 | |
7518 | Returns |
7519 | 0 OK |
7520 | 1 Error |
7521 | */ |
7522 | |
7523 | int |
7524 | copy_event_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table) |
7525 | { |
7526 | const char *wild= thd->lex->wild ? thd->lex->wild->ptr() : NullS; |
7527 | CHARSET_INFO *scs= system_charset_info; |
7528 | MYSQL_TIME time; |
7529 | Event_timed et; |
7530 | DBUG_ENTER("copy_event_to_schema_table" ); |
7531 | |
7532 | restore_record(sch_table, s->default_values); |
7533 | |
7534 | if (et.load_from_row(thd, event_table)) |
7535 | { |
7536 | my_error(ER_CANNOT_LOAD_FROM_TABLE_V2, MYF(0), "mysql" , "event" ); |
7537 | DBUG_RETURN(1); |
7538 | } |
7539 | |
7540 | if (!(!wild || !wild[0] || !wild_case_compare(scs, et.name.str, wild))) |
7541 | DBUG_RETURN(0); |
7542 | |
7543 | /* |
7544 | Skip events in schemas one does not have access to. The check is |
7545 | optimized. It's guaranteed in case of SHOW EVENTS that the user |
7546 | has access. |
7547 | */ |
7548 | if (thd->lex->sql_command != SQLCOM_SHOW_EVENTS && |
7549 | check_access(thd, EVENT_ACL, et.dbname.str, NULL, NULL, 0, 1)) |
7550 | DBUG_RETURN(0); |
7551 | |
7552 | sch_table->field[ISE_EVENT_CATALOG]->store(STRING_WITH_LEN("def" ), scs); |
7553 | sch_table->field[ISE_EVENT_SCHEMA]-> |
7554 | store(et.dbname.str, et.dbname.length,scs); |
7555 | sch_table->field[ISE_EVENT_NAME]-> |
7556 | store(et.name.str, et.name.length, scs); |
7557 | sch_table->field[ISE_DEFINER]-> |
7558 | store(et.definer.str, et.definer.length, scs); |
7559 | const String *tz_name= et.time_zone->get_name(); |
7560 | sch_table->field[ISE_TIME_ZONE]-> |
7561 | store(tz_name->ptr(), tz_name->length(), scs); |
7562 | sch_table->field[ISE_EVENT_BODY]-> |
7563 | store(STRING_WITH_LEN("SQL" ), scs); |
7564 | sch_table->field[ISE_EVENT_DEFINITION]->store( |
7565 | et.body_utf8.str, et.body_utf8.length, scs); |
7566 | |
7567 | /* SQL_MODE */ |
7568 | { |
7569 | LEX_CSTRING sql_mode; |
7570 | sql_mode_string_representation(thd, et.sql_mode, &sql_mode); |
7571 | sch_table->field[ISE_SQL_MODE]-> |
7572 | store(sql_mode.str, sql_mode.length, scs); |
7573 | } |
7574 | |
7575 | int not_used=0; |
7576 | |
7577 | if (et.expression) |
7578 | { |
7579 | String show_str; |
7580 | /* type */ |
7581 | sch_table->field[ISE_EVENT_TYPE]->store(STRING_WITH_LEN("RECURRING" ), scs); |
7582 | |
7583 | if (Events::reconstruct_interval_expression(&show_str, et.interval, |
7584 | et.expression)) |
7585 | DBUG_RETURN(1); |
7586 | |
7587 | sch_table->field[ISE_INTERVAL_VALUE]->set_notnull(); |
7588 | sch_table->field[ISE_INTERVAL_VALUE]-> |
7589 | store(show_str.ptr(), show_str.length(), scs); |
7590 | |
7591 | LEX_CSTRING *ival= &interval_type_to_name[et.interval]; |
7592 | sch_table->field[ISE_INTERVAL_FIELD]->set_notnull(); |
7593 | sch_table->field[ISE_INTERVAL_FIELD]->store(ival->str, ival->length, scs); |
7594 | |
7595 | /* starts & ends . STARTS is always set - see sql_yacc.yy */ |
7596 | et.time_zone->gmt_sec_to_TIME(&time, et.starts); |
7597 | sch_table->field[ISE_STARTS]->set_notnull(); |
7598 | sch_table->field[ISE_STARTS]->store_time(&time); |
7599 | |
7600 | if (!et.ends_null) |
7601 | { |
7602 | et.time_zone->gmt_sec_to_TIME(&time, et.ends); |
7603 | sch_table->field[ISE_ENDS]->set_notnull(); |
7604 | sch_table->field[ISE_ENDS]->store_time(&time); |
7605 | } |
7606 | } |
7607 | else |
7608 | { |
7609 | /* type */ |
7610 | sch_table->field[ISE_EVENT_TYPE]->store(STRING_WITH_LEN("ONE TIME" ), scs); |
7611 | |
7612 | et.time_zone->gmt_sec_to_TIME(&time, et.execute_at); |
7613 | sch_table->field[ISE_EXECUTE_AT]->set_notnull(); |
7614 | sch_table->field[ISE_EXECUTE_AT]->store_time(&time); |
7615 | } |
7616 | |
7617 | /* status */ |
7618 | |
7619 | switch (et.status) |
7620 | { |
7621 | case Event_parse_data::ENABLED: |
7622 | sch_table->field[ISE_STATUS]->store(STRING_WITH_LEN("ENABLED" ), scs); |
7623 | break; |
7624 | case Event_parse_data::SLAVESIDE_DISABLED: |
7625 | sch_table->field[ISE_STATUS]->store(STRING_WITH_LEN("SLAVESIDE_DISABLED" ), |
7626 | scs); |
7627 | break; |
7628 | case Event_parse_data::DISABLED: |
7629 | sch_table->field[ISE_STATUS]->store(STRING_WITH_LEN("DISABLED" ), scs); |
7630 | break; |
7631 | default: |
7632 | DBUG_ASSERT(0); |
7633 | } |
7634 | sch_table->field[ISE_ORIGINATOR]->store(et.originator, TRUE); |
7635 | |
7636 | /* on_completion */ |
7637 | if (et.on_completion == Event_parse_data::ON_COMPLETION_DROP) |
7638 | sch_table->field[ISE_ON_COMPLETION]-> |
7639 | store(STRING_WITH_LEN("NOT PRESERVE" ), scs); |
7640 | else |
7641 | sch_table->field[ISE_ON_COMPLETION]-> |
7642 | store(STRING_WITH_LEN("PRESERVE" ), scs); |
7643 | |
7644 | number_to_datetime(et.created, 0, &time, 0, ¬_used); |
7645 | DBUG_ASSERT(not_used==0); |
7646 | sch_table->field[ISE_CREATED]->store_time(&time); |
7647 | |
7648 | number_to_datetime(et.modified, 0, &time, 0, ¬_used); |
7649 | DBUG_ASSERT(not_used==0); |
7650 | sch_table->field[ISE_LAST_ALTERED]->store_time(&time); |
7651 | |
7652 | if (et.last_executed) |
7653 | { |
7654 | et.time_zone->gmt_sec_to_TIME(&time, et.last_executed); |
7655 | sch_table->field[ISE_LAST_EXECUTED]->set_notnull(); |
7656 | sch_table->field[ISE_LAST_EXECUTED]->store_time(&time); |
7657 | } |
7658 | |
7659 | sch_table->field[ISE_EVENT_COMMENT]-> |
7660 | store(et.comment.str, et.comment.length, scs); |
7661 | |
7662 | sch_table->field[ISE_CLIENT_CS]->set_notnull(); |
7663 | sch_table->field[ISE_CLIENT_CS]->store( |
7664 | et.creation_ctx->get_client_cs()->csname, |
7665 | strlen(et.creation_ctx->get_client_cs()->csname), |
7666 | scs); |
7667 | |
7668 | sch_table->field[ISE_CONNECTION_CL]->set_notnull(); |
7669 | sch_table->field[ISE_CONNECTION_CL]->store( |
7670 | et.creation_ctx->get_connection_cl()->name, |
7671 | strlen(et.creation_ctx->get_connection_cl()->name), |
7672 | scs); |
7673 | |
7674 | sch_table->field[ISE_DB_CL]->set_notnull(); |
7675 | sch_table->field[ISE_DB_CL]->store( |
7676 | et.creation_ctx->get_db_cl()->name, |
7677 | strlen(et.creation_ctx->get_db_cl()->name), |
7678 | scs); |
7679 | |
7680 | if (schema_table_store_record(thd, sch_table)) |
7681 | DBUG_RETURN(1); |
7682 | |
7683 | DBUG_RETURN(0); |
7684 | } |
7685 | #endif |
7686 | |
7687 | int fill_open_tables(THD *thd, TABLE_LIST *tables, COND *cond) |
7688 | { |
7689 | DBUG_ENTER("fill_open_tables" ); |
7690 | const char *wild= thd->lex->wild ? thd->lex->wild->ptr() : NullS; |
7691 | TABLE *table= tables->table; |
7692 | CHARSET_INFO *cs= system_charset_info; |
7693 | OPEN_TABLE_LIST *open_list; |
7694 | if (unlikely(!(open_list= list_open_tables(thd, thd->lex->select_lex.db.str, |
7695 | wild))) && |
7696 | unlikely(thd->is_fatal_error)) |
7697 | DBUG_RETURN(1); |
7698 | |
7699 | for (; open_list ; open_list=open_list->next) |
7700 | { |
7701 | restore_record(table, s->default_values); |
7702 | table->field[0]->store(open_list->db, strlen(open_list->db), cs); |
7703 | table->field[1]->store(open_list->table, strlen(open_list->table), cs); |
7704 | table->field[2]->store((longlong) open_list->in_use, TRUE); |
7705 | table->field[3]->store((longlong) open_list->locked, TRUE); |
7706 | if (unlikely(schema_table_store_record(thd, table))) |
7707 | DBUG_RETURN(1); |
7708 | } |
7709 | DBUG_RETURN(0); |
7710 | } |
7711 | |
7712 | |
7713 | int fill_variables(THD *thd, TABLE_LIST *tables, COND *cond) |
7714 | { |
7715 | DBUG_ENTER("fill_variables" ); |
7716 | int res= 0; |
7717 | LEX *lex= thd->lex; |
7718 | const char *wild= lex->wild ? lex->wild->ptr() : NullS; |
7719 | enum enum_schema_tables schema_table_idx= |
7720 | get_schema_table_idx(tables->schema_table); |
7721 | enum enum_var_type scope= OPT_SESSION; |
7722 | bool upper_case_names= lex->sql_command != SQLCOM_SHOW_VARIABLES; |
7723 | bool sorted_vars= lex->sql_command == SQLCOM_SHOW_VARIABLES; |
7724 | |
7725 | if ((sorted_vars && lex->option_type == OPT_GLOBAL) || |
7726 | schema_table_idx == SCH_GLOBAL_VARIABLES) |
7727 | scope= OPT_GLOBAL; |
7728 | |
7729 | COND *partial_cond= make_cond_for_info_schema(thd, cond, tables); |
7730 | |
7731 | mysql_prlock_rdlock(&LOCK_system_variables_hash); |
7732 | |
7733 | /* |
7734 | Avoid recursive LOCK_system_variables_hash acquisition in |
7735 | intern_sys_var_ptr() by pre-syncing dynamic session variables. |
7736 | */ |
7737 | if (scope == OPT_SESSION && |
7738 | (!thd->variables.dynamic_variables_ptr || |
7739 | global_system_variables.dynamic_variables_head > |
7740 | thd->variables.dynamic_variables_head)) |
7741 | sync_dynamic_session_variables(thd, true); |
7742 | |
7743 | res= show_status_array(thd, wild, enumerate_sys_vars(thd, sorted_vars, scope), |
7744 | scope, NULL, "" , tables->table, |
7745 | upper_case_names, partial_cond); |
7746 | mysql_prlock_unlock(&LOCK_system_variables_hash); |
7747 | DBUG_RETURN(res); |
7748 | } |
7749 | |
7750 | |
7751 | int fill_status(THD *thd, TABLE_LIST *tables, COND *cond) |
7752 | { |
7753 | DBUG_ENTER("fill_status" ); |
7754 | LEX *lex= thd->lex; |
7755 | const char *wild= lex->wild ? lex->wild->ptr() : NullS; |
7756 | int res= 0; |
7757 | STATUS_VAR *tmp1, tmp; |
7758 | enum enum_schema_tables schema_table_idx= |
7759 | get_schema_table_idx(tables->schema_table); |
7760 | enum enum_var_type scope; |
7761 | bool upper_case_names= lex->sql_command != SQLCOM_SHOW_STATUS; |
7762 | |
7763 | if (lex->sql_command == SQLCOM_SHOW_STATUS) |
7764 | { |
7765 | scope= lex->option_type; |
7766 | if (scope == OPT_GLOBAL) |
7767 | tmp1= &tmp; |
7768 | else |
7769 | tmp1= thd->initial_status_var; |
7770 | } |
7771 | else if (schema_table_idx == SCH_GLOBAL_STATUS) |
7772 | { |
7773 | scope= OPT_GLOBAL; |
7774 | tmp1= &tmp; |
7775 | } |
7776 | else |
7777 | { |
7778 | scope= OPT_SESSION; |
7779 | tmp1= &thd->status_var; |
7780 | } |
7781 | |
7782 | COND *partial_cond= make_cond_for_info_schema(thd, cond, tables); |
7783 | // Evaluate and cache const subqueries now, before the mutex. |
7784 | if (partial_cond) |
7785 | partial_cond->val_int(); |
7786 | |
7787 | if (scope == OPT_GLOBAL) |
7788 | { |
7789 | /* We only hold LOCK_status for summary status vars */ |
7790 | mysql_mutex_lock(&LOCK_status); |
7791 | calc_sum_of_all_status(&tmp); |
7792 | mysql_mutex_unlock(&LOCK_status); |
7793 | } |
7794 | |
7795 | mysql_mutex_lock(&LOCK_show_status); |
7796 | res= show_status_array(thd, wild, |
7797 | (SHOW_VAR *)all_status_vars.buffer, |
7798 | scope, tmp1, "" , tables->table, |
7799 | upper_case_names, partial_cond); |
7800 | mysql_mutex_unlock(&LOCK_show_status); |
7801 | DBUG_RETURN(res); |
7802 | } |
7803 | |
7804 | |
7805 | /* |
7806 | Fill and store records into I_S.referential_constraints table |
7807 | |
7808 | SYNOPSIS |
7809 | get_referential_constraints_record() |
7810 | thd thread handle |
7811 | tables table list struct(processed table) |
7812 | table I_S table |
7813 | res 1 means the error during opening of the processed table |
7814 | 0 means processed table is opened without error |
7815 | base_name db name |
7816 | file_name table name |
7817 | |
7818 | RETURN |
7819 | 0 ok |
7820 | # error |
7821 | */ |
7822 | |
7823 | static int |
7824 | get_referential_constraints_record(THD *thd, TABLE_LIST *tables, |
7825 | TABLE *table, bool res, |
7826 | const LEX_CSTRING *db_name, |
7827 | const LEX_CSTRING *table_name) |
7828 | { |
7829 | CHARSET_INFO *cs= system_charset_info; |
7830 | LEX_CSTRING *s; |
7831 | DBUG_ENTER("get_referential_constraints_record" ); |
7832 | |
7833 | if (res) |
7834 | { |
7835 | if (unlikely(thd->is_error())) |
7836 | push_warning(thd, Sql_condition::WARN_LEVEL_WARN, |
7837 | thd->get_stmt_da()->sql_errno(), |
7838 | thd->get_stmt_da()->message()); |
7839 | thd->clear_error(); |
7840 | DBUG_RETURN(0); |
7841 | } |
7842 | if (!tables->view) |
7843 | { |
7844 | List<FOREIGN_KEY_INFO> f_key_list; |
7845 | TABLE *show_table= tables->table; |
7846 | show_table->file->info(HA_STATUS_VARIABLE | |
7847 | HA_STATUS_NO_LOCK | |
7848 | HA_STATUS_TIME); |
7849 | |
7850 | show_table->file->get_foreign_key_list(thd, &f_key_list); |
7851 | FOREIGN_KEY_INFO *f_key_info; |
7852 | List_iterator_fast<FOREIGN_KEY_INFO> it(f_key_list); |
7853 | while ((f_key_info= it++)) |
7854 | { |
7855 | restore_record(table, s->default_values); |
7856 | table->field[0]->store(STRING_WITH_LEN("def" ), cs); |
7857 | table->field[1]->store(db_name->str, db_name->length, cs); |
7858 | table->field[9]->store(table_name->str, table_name->length, cs); |
7859 | table->field[2]->store(f_key_info->foreign_id->str, |
7860 | f_key_info->foreign_id->length, cs); |
7861 | table->field[3]->store(STRING_WITH_LEN("def" ), cs); |
7862 | table->field[4]->store(f_key_info->referenced_db->str, |
7863 | f_key_info->referenced_db->length, cs); |
7864 | table->field[10]->store(f_key_info->referenced_table->str, |
7865 | f_key_info->referenced_table->length, cs); |
7866 | if (f_key_info->referenced_key_name) |
7867 | { |
7868 | table->field[5]->store(f_key_info->referenced_key_name->str, |
7869 | f_key_info->referenced_key_name->length, cs); |
7870 | table->field[5]->set_notnull(); |
7871 | } |
7872 | else |
7873 | table->field[5]->set_null(); |
7874 | table->field[6]->store(STRING_WITH_LEN("NONE" ), cs); |
7875 | s= fk_option_name(f_key_info->update_method); |
7876 | table->field[7]->store(s->str, s->length, cs); |
7877 | s= fk_option_name(f_key_info->delete_method); |
7878 | table->field[8]->store(s->str, s->length, cs); |
7879 | if (schema_table_store_record(thd, table)) |
7880 | DBUG_RETURN(1); |
7881 | } |
7882 | } |
7883 | DBUG_RETURN(0); |
7884 | } |
7885 | |
7886 | struct schema_table_ref |
7887 | { |
7888 | const char *table_name; |
7889 | ST_SCHEMA_TABLE *schema_table; |
7890 | }; |
7891 | |
7892 | /* |
7893 | Find schema_tables elment by name |
7894 | |
7895 | SYNOPSIS |
7896 | find_schema_table_in_plugin() |
7897 | thd thread handler |
7898 | plugin plugin |
7899 | table_name table name |
7900 | |
7901 | RETURN |
7902 | 0 table not found |
7903 | 1 found the schema table |
7904 | */ |
7905 | static my_bool find_schema_table_in_plugin(THD *thd, plugin_ref plugin, |
7906 | void* p_table) |
7907 | { |
7908 | schema_table_ref *p_schema_table= (schema_table_ref *)p_table; |
7909 | const char* table_name= p_schema_table->table_name; |
7910 | ST_SCHEMA_TABLE *schema_table= plugin_data(plugin, ST_SCHEMA_TABLE *); |
7911 | DBUG_ENTER("find_schema_table_in_plugin" ); |
7912 | |
7913 | if (!my_strcasecmp(system_charset_info, |
7914 | schema_table->table_name, |
7915 | table_name)) |
7916 | { |
7917 | my_plugin_lock(thd, plugin); |
7918 | p_schema_table->schema_table= schema_table; |
7919 | DBUG_RETURN(1); |
7920 | } |
7921 | |
7922 | DBUG_RETURN(0); |
7923 | } |
7924 | |
7925 | |
7926 | /* |
7927 | Find schema_tables element by name |
7928 | |
7929 | SYNOPSIS |
7930 | find_schema_table() |
7931 | thd thread handler |
7932 | table_name table name |
7933 | |
7934 | RETURN |
7935 | 0 table not found |
7936 | # pointer to 'schema_tables' element |
7937 | */ |
7938 | |
7939 | ST_SCHEMA_TABLE *find_schema_table(THD *thd, const LEX_CSTRING *table_name, |
7940 | bool *in_plugin) |
7941 | { |
7942 | schema_table_ref schema_table_a; |
7943 | ST_SCHEMA_TABLE *schema_table= schema_tables; |
7944 | DBUG_ENTER("find_schema_table" ); |
7945 | |
7946 | *in_plugin= false; |
7947 | for (; schema_table->table_name; schema_table++) |
7948 | { |
7949 | if (!my_strcasecmp(system_charset_info, |
7950 | schema_table->table_name, |
7951 | table_name->str)) |
7952 | DBUG_RETURN(schema_table); |
7953 | } |
7954 | |
7955 | *in_plugin= true; |
7956 | schema_table_a.table_name= table_name->str; |
7957 | if (plugin_foreach(thd, find_schema_table_in_plugin, |
7958 | MYSQL_INFORMATION_SCHEMA_PLUGIN, &schema_table_a)) |
7959 | DBUG_RETURN(schema_table_a.schema_table); |
7960 | |
7961 | DBUG_RETURN(NULL); |
7962 | } |
7963 | |
7964 | |
7965 | ST_SCHEMA_TABLE *get_schema_table(enum enum_schema_tables schema_table_idx) |
7966 | { |
7967 | return &schema_tables[schema_table_idx]; |
7968 | } |
7969 | |
7970 | static void |
7971 | mark_all_fields_used_in_query(THD *thd, |
7972 | ST_FIELD_INFO *schema_fields, |
7973 | MY_BITMAP *bitmap, |
7974 | Item *all_items) |
7975 | { |
7976 | Item *item; |
7977 | DBUG_ENTER("mark_all_fields_used_in_query" ); |
7978 | |
7979 | /* If not SELECT command, return all columns */ |
7980 | if (thd->lex->sql_command != SQLCOM_SELECT && |
7981 | thd->lex->sql_command != SQLCOM_SET_OPTION) |
7982 | { |
7983 | bitmap_set_all(bitmap); |
7984 | DBUG_VOID_RETURN; |
7985 | } |
7986 | |
7987 | for (item= all_items ; item ; item= item->next) |
7988 | { |
7989 | if (item->type() == Item::FIELD_ITEM) |
7990 | { |
7991 | ST_FIELD_INFO *fields= schema_fields; |
7992 | uint count; |
7993 | Item_field *item_field= (Item_field*) item; |
7994 | |
7995 | /* item_field can be '*' as this function is called before fix_fields */ |
7996 | if (item_field->field_name.str == star_clex_str.str) |
7997 | { |
7998 | bitmap_set_all(bitmap); |
7999 | break; |
8000 | } |
8001 | for (count=0; fields->field_name; fields++, count++) |
8002 | { |
8003 | if (!my_strcasecmp(system_charset_info, fields->field_name, |
8004 | item_field->field_name.str)) |
8005 | { |
8006 | bitmap_set_bit(bitmap, count); |
8007 | break; |
8008 | } |
8009 | } |
8010 | } |
8011 | } |
8012 | DBUG_VOID_RETURN; |
8013 | } |
8014 | |
8015 | /** |
8016 | Create information_schema table using schema_table data. |
8017 | |
8018 | @note |
8019 | For MYSQL_TYPE_DECIMAL fields only, the field_length member has encoded |
8020 | into it two numbers, based on modulus of base-10 numbers. In the ones |
8021 | position is the number of decimals. Tens position is unused. In the |
8022 | hundreds and thousands position is a two-digit decimal number representing |
8023 | length. Encode this value with (length*100)+decimals , where |
8024 | 0<decimals<10 and 0<=length<100 . |
8025 | |
8026 | @param |
8027 | thd thread handler |
8028 | |
8029 | @param table_list Used to pass I_S table information(fields info, tables |
8030 | parameters etc) and table name. |
8031 | |
8032 | @retval \# Pointer to created table |
8033 | @retval NULL Can't create table |
8034 | */ |
8035 | |
8036 | TABLE *create_schema_table(THD *thd, TABLE_LIST *table_list) |
8037 | { |
8038 | uint field_count; |
8039 | Item *item, *all_items; |
8040 | TABLE *table; |
8041 | List<Item> field_list; |
8042 | ST_SCHEMA_TABLE *schema_table= table_list->schema_table; |
8043 | ST_FIELD_INFO *fields_info= schema_table->fields_info; |
8044 | ST_FIELD_INFO *fields; |
8045 | CHARSET_INFO *cs= system_charset_info; |
8046 | MEM_ROOT *mem_root= thd->mem_root; |
8047 | MY_BITMAP bitmap; |
8048 | my_bitmap_map *buf; |
8049 | DBUG_ENTER("create_schema_table" ); |
8050 | |
8051 | for (field_count= 0, fields= fields_info; fields->field_name; fields++) |
8052 | field_count++; |
8053 | if (!(buf= (my_bitmap_map*) thd->alloc(bitmap_buffer_size(field_count)))) |
8054 | DBUG_RETURN(NULL); |
8055 | my_bitmap_init(&bitmap, buf, field_count, 0); |
8056 | |
8057 | if (!thd->stmt_arena->is_conventional() && |
8058 | thd->mem_root != thd->stmt_arena->mem_root) |
8059 | all_items= thd->stmt_arena->free_list; |
8060 | else |
8061 | all_items= thd->free_list; |
8062 | |
8063 | mark_all_fields_used_in_query(thd, fields_info, &bitmap, all_items); |
8064 | |
8065 | for (field_count=0; fields_info->field_name; fields_info++) |
8066 | { |
8067 | size_t field_name_length= strlen(fields_info->field_name); |
8068 | switch (fields_info->field_type) { |
8069 | case MYSQL_TYPE_TINY: |
8070 | case MYSQL_TYPE_LONG: |
8071 | case MYSQL_TYPE_SHORT: |
8072 | case MYSQL_TYPE_LONGLONG: |
8073 | case MYSQL_TYPE_INT24: |
8074 | if (!(item= new (mem_root) |
8075 | Item_return_int(thd, fields_info->field_name, |
8076 | fields_info->field_length, |
8077 | fields_info->field_type, |
8078 | fields_info->value))) |
8079 | { |
8080 | DBUG_RETURN(0); |
8081 | } |
8082 | item->unsigned_flag= (fields_info->field_flags & MY_I_S_UNSIGNED); |
8083 | break; |
8084 | case MYSQL_TYPE_DATE: |
8085 | if (!(item=new (mem_root) |
8086 | Item_return_date_time(thd, fields_info->field_name, |
8087 | (uint)field_name_length, |
8088 | fields_info->field_type))) |
8089 | DBUG_RETURN(0); |
8090 | break; |
8091 | case MYSQL_TYPE_TIME: |
8092 | if (!(item=new (mem_root) |
8093 | Item_return_date_time(thd, fields_info->field_name, |
8094 | (uint)field_name_length, |
8095 | fields_info->field_type))) |
8096 | DBUG_RETURN(0); |
8097 | break; |
8098 | case MYSQL_TYPE_TIMESTAMP: |
8099 | case MYSQL_TYPE_DATETIME: |
8100 | if (!(item=new (mem_root) |
8101 | Item_return_date_time(thd, fields_info->field_name, |
8102 | (uint)field_name_length, |
8103 | fields_info->field_type, |
8104 | fields_info->field_length))) |
8105 | DBUG_RETURN(0); |
8106 | item->decimals= fields_info->field_length; |
8107 | break; |
8108 | case MYSQL_TYPE_FLOAT: |
8109 | case MYSQL_TYPE_DOUBLE: |
8110 | if ((item= new (mem_root) |
8111 | Item_float(thd, fields_info->field_name, 0.0, |
8112 | NOT_FIXED_DEC, |
8113 | fields_info->field_length)) == NULL) |
8114 | DBUG_RETURN(NULL); |
8115 | break; |
8116 | case MYSQL_TYPE_DECIMAL: |
8117 | case MYSQL_TYPE_NEWDECIMAL: |
8118 | if (!(item= new (mem_root) |
8119 | Item_decimal(thd, (longlong) fields_info->value, false))) |
8120 | { |
8121 | DBUG_RETURN(0); |
8122 | } |
8123 | /* |
8124 | Create a type holder, as we want the type of the item to defined |
8125 | the type of the object, not the value |
8126 | */ |
8127 | if (!(item= new (mem_root) Item_type_holder(thd, item))) |
8128 | DBUG_RETURN(0); |
8129 | item->unsigned_flag= (fields_info->field_flags & MY_I_S_UNSIGNED); |
8130 | item->decimals= fields_info->field_length%10; |
8131 | item->max_length= (fields_info->field_length/100)%100; |
8132 | if (item->unsigned_flag == 0) |
8133 | item->max_length+= 1; |
8134 | if (item->decimals > 0) |
8135 | item->max_length+= 1; |
8136 | item->set_name(thd, fields_info->field_name, field_name_length, cs); |
8137 | break; |
8138 | case MYSQL_TYPE_TINY_BLOB: |
8139 | case MYSQL_TYPE_MEDIUM_BLOB: |
8140 | case MYSQL_TYPE_LONG_BLOB: |
8141 | case MYSQL_TYPE_BLOB: |
8142 | if (bitmap_is_set(&bitmap, field_count)) |
8143 | { |
8144 | if (!(item= new (mem_root) |
8145 | Item_blob(thd, fields_info->field_name, |
8146 | fields_info->field_length))) |
8147 | { |
8148 | DBUG_RETURN(0); |
8149 | } |
8150 | } |
8151 | else |
8152 | { |
8153 | if (!(item= new (mem_root) |
8154 | Item_empty_string(thd, "" , 0, cs))) |
8155 | { |
8156 | DBUG_RETURN(0); |
8157 | } |
8158 | item->set_name(thd, fields_info->field_name, |
8159 | field_name_length, cs); |
8160 | } |
8161 | break; |
8162 | default: |
8163 | { |
8164 | bool show_field; |
8165 | /* Don't let unimplemented types pass through. Could be a grave error. */ |
8166 | DBUG_ASSERT(fields_info->field_type == MYSQL_TYPE_STRING); |
8167 | |
8168 | show_field= bitmap_is_set(&bitmap, field_count); |
8169 | if (!(item= new (mem_root) |
8170 | Item_empty_string(thd, "" , |
8171 | show_field ? fields_info->field_length : 0, cs))) |
8172 | { |
8173 | DBUG_RETURN(0); |
8174 | } |
8175 | item->set_name(thd, fields_info->field_name, |
8176 | field_name_length, cs); |
8177 | break; |
8178 | } |
8179 | } |
8180 | field_list.push_back(item, thd->mem_root); |
8181 | item->maybe_null= (fields_info->field_flags & MY_I_S_MAYBE_NULL); |
8182 | field_count++; |
8183 | } |
8184 | TMP_TABLE_PARAM *tmp_table_param = |
8185 | (TMP_TABLE_PARAM*) (thd->alloc(sizeof(TMP_TABLE_PARAM))); |
8186 | tmp_table_param->init(); |
8187 | tmp_table_param->table_charset= cs; |
8188 | tmp_table_param->field_count= field_count; |
8189 | tmp_table_param->schema_table= 1; |
8190 | SELECT_LEX *select_lex= thd->lex->current_select; |
8191 | bool keep_row_order= is_show_command(thd); |
8192 | if (!(table= create_tmp_table(thd, tmp_table_param, |
8193 | field_list, (ORDER*) 0, 0, 0, |
8194 | (select_lex->options | thd->variables.option_bits | |
8195 | TMP_TABLE_ALL_COLUMNS), HA_POS_ERROR, |
8196 | &table_list->alias, false, keep_row_order))) |
8197 | DBUG_RETURN(0); |
8198 | my_bitmap_map* bitmaps= |
8199 | (my_bitmap_map*) thd->alloc(bitmap_buffer_size(field_count)); |
8200 | my_bitmap_init(&table->def_read_set, (my_bitmap_map*) bitmaps, field_count, |
8201 | FALSE); |
8202 | table->read_set= &table->def_read_set; |
8203 | bitmap_clear_all(table->read_set); |
8204 | table_list->schema_table_param= tmp_table_param; |
8205 | DBUG_RETURN(table); |
8206 | } |
8207 | |
8208 | |
8209 | /* |
8210 | For old SHOW compatibility. It is used when |
8211 | old SHOW doesn't have generated column names |
8212 | Make list of fields for SHOW |
8213 | |
8214 | SYNOPSIS |
8215 | make_old_format() |
8216 | thd thread handler |
8217 | schema_table pointer to 'schema_tables' element |
8218 | |
8219 | RETURN |
8220 | 1 error |
8221 | 0 success |
8222 | */ |
8223 | |
8224 | static int make_old_format(THD *thd, ST_SCHEMA_TABLE *schema_table) |
8225 | { |
8226 | ST_FIELD_INFO *field_info= schema_table->fields_info; |
8227 | Name_resolution_context *context= &thd->lex->select_lex.context; |
8228 | for (; field_info->field_name; field_info++) |
8229 | { |
8230 | if (field_info->old_name) |
8231 | { |
8232 | LEX_CSTRING field_name= {field_info->field_name, |
8233 | strlen(field_info->field_name)}; |
8234 | Item_field *field= new (thd->mem_root) |
8235 | Item_field(thd, context, NullS, NullS, &field_name); |
8236 | if (field) |
8237 | { |
8238 | field->set_name(thd, field_info->old_name, |
8239 | strlen(field_info->old_name), |
8240 | system_charset_info); |
8241 | if (add_item_to_list(thd, field)) |
8242 | return 1; |
8243 | } |
8244 | } |
8245 | } |
8246 | return 0; |
8247 | } |
8248 | |
8249 | |
8250 | int make_schemata_old_format(THD *thd, ST_SCHEMA_TABLE *schema_table) |
8251 | { |
8252 | char tmp[128]; |
8253 | LEX *lex= thd->lex; |
8254 | SELECT_LEX *sel= lex->current_select; |
8255 | Name_resolution_context *context= &sel->context; |
8256 | |
8257 | if (!sel->item_list.elements) |
8258 | { |
8259 | ST_FIELD_INFO *field_info= &schema_table->fields_info[1]; |
8260 | String buffer(tmp,sizeof(tmp), system_charset_info); |
8261 | LEX_CSTRING field_name= {field_info->field_name, |
8262 | strlen(field_info->field_name) }; |
8263 | |
8264 | Item_field *field= new (thd->mem_root) Item_field(thd, context, |
8265 | NullS, NullS, &field_name); |
8266 | if (!field || add_item_to_list(thd, field)) |
8267 | return 1; |
8268 | buffer.length(0); |
8269 | buffer.append(field_info->old_name); |
8270 | if (lex->wild && lex->wild->ptr()) |
8271 | { |
8272 | buffer.append(STRING_WITH_LEN(" (" )); |
8273 | buffer.append(lex->wild->ptr()); |
8274 | buffer.append(')'); |
8275 | } |
8276 | field->set_name(thd, buffer.ptr(), buffer.length(), system_charset_info); |
8277 | } |
8278 | return 0; |
8279 | } |
8280 | |
8281 | |
8282 | int make_table_names_old_format(THD *thd, ST_SCHEMA_TABLE *schema_table) |
8283 | { |
8284 | char tmp[128]; |
8285 | String buffer(tmp,sizeof(tmp), thd->charset()); |
8286 | LEX *lex= thd->lex; |
8287 | Name_resolution_context *context= &lex->select_lex.context; |
8288 | ST_FIELD_INFO *field_info= &schema_table->fields_info[2]; |
8289 | LEX_CSTRING field_name= {field_info->field_name, |
8290 | strlen(field_info->field_name) }; |
8291 | |
8292 | buffer.length(0); |
8293 | buffer.append(field_info->old_name); |
8294 | buffer.append(&lex->select_lex.db); |
8295 | if (lex->wild && lex->wild->ptr()) |
8296 | { |
8297 | buffer.append(STRING_WITH_LEN(" (" )); |
8298 | buffer.append(lex->wild->ptr()); |
8299 | buffer.append(')'); |
8300 | } |
8301 | Item_field *field= new (thd->mem_root) Item_field(thd, context, |
8302 | NullS, NullS, &field_name); |
8303 | if (add_item_to_list(thd, field)) |
8304 | return 1; |
8305 | field->set_name(thd, buffer.ptr(), buffer.length(), system_charset_info); |
8306 | if (thd->lex->verbose) |
8307 | { |
8308 | field_info= &schema_table->fields_info[3]; |
8309 | LEX_CSTRING field_name2= {field_info->field_name, |
8310 | strlen(field_info->field_name) }; |
8311 | field= new (thd->mem_root) Item_field(thd, context, NullS, NullS, |
8312 | &field_name2); |
8313 | if (add_item_to_list(thd, field)) |
8314 | return 1; |
8315 | field->set_name(thd, field_info->old_name, strlen(field_info->old_name), |
8316 | system_charset_info); |
8317 | } |
8318 | return 0; |
8319 | } |
8320 | |
8321 | |
8322 | int make_columns_old_format(THD *thd, ST_SCHEMA_TABLE *schema_table) |
8323 | { |
8324 | int fields_arr[]= {3, 15, 14, 6, 16, 5, 17, 18, 19, -1}; |
8325 | int *field_num= fields_arr; |
8326 | ST_FIELD_INFO *field_info; |
8327 | Name_resolution_context *context= &thd->lex->select_lex.context; |
8328 | |
8329 | for (; *field_num >= 0; field_num++) |
8330 | { |
8331 | field_info= &schema_table->fields_info[*field_num]; |
8332 | LEX_CSTRING field_name= {field_info->field_name, |
8333 | strlen(field_info->field_name)}; |
8334 | if (!thd->lex->verbose && (*field_num == 14 || |
8335 | *field_num == 18 || |
8336 | *field_num == 19)) |
8337 | continue; |
8338 | Item_field *field= new (thd->mem_root) Item_field(thd, context, |
8339 | NullS, NullS, &field_name); |
8340 | if (field) |
8341 | { |
8342 | field->set_name(thd, field_info->old_name, |
8343 | strlen(field_info->old_name), |
8344 | system_charset_info); |
8345 | if (add_item_to_list(thd, field)) |
8346 | return 1; |
8347 | } |
8348 | } |
8349 | return 0; |
8350 | } |
8351 | |
8352 | |
8353 | int make_character_sets_old_format(THD *thd, ST_SCHEMA_TABLE *schema_table) |
8354 | { |
8355 | int fields_arr[]= {0, 2, 1, 3, -1}; |
8356 | int *field_num= fields_arr; |
8357 | ST_FIELD_INFO *field_info; |
8358 | Name_resolution_context *context= &thd->lex->select_lex.context; |
8359 | |
8360 | for (; *field_num >= 0; field_num++) |
8361 | { |
8362 | field_info= &schema_table->fields_info[*field_num]; |
8363 | LEX_CSTRING field_name= {field_info->field_name, |
8364 | strlen(field_info->field_name)}; |
8365 | Item_field *field= new (thd->mem_root) Item_field(thd, context, |
8366 | NullS, NullS, &field_name); |
8367 | if (field) |
8368 | { |
8369 | field->set_name(thd, field_info->old_name, |
8370 | strlen(field_info->old_name), |
8371 | system_charset_info); |
8372 | if (add_item_to_list(thd, field)) |
8373 | return 1; |
8374 | } |
8375 | } |
8376 | return 0; |
8377 | } |
8378 | |
8379 | |
8380 | int make_proc_old_format(THD *thd, ST_SCHEMA_TABLE *schema_table) |
8381 | { |
8382 | int fields_arr[]= {2, 3, 4, 27, 24, 23, 22, 26, 28, 29, 30, -1}; |
8383 | int *field_num= fields_arr; |
8384 | ST_FIELD_INFO *field_info; |
8385 | Name_resolution_context *context= &thd->lex->select_lex.context; |
8386 | |
8387 | for (; *field_num >= 0; field_num++) |
8388 | { |
8389 | field_info= &schema_table->fields_info[*field_num]; |
8390 | LEX_CSTRING field_name= {field_info->field_name, |
8391 | strlen(field_info->field_name)}; |
8392 | Item_field *field= new (thd->mem_root) Item_field(thd, context, |
8393 | NullS, NullS, &field_name); |
8394 | if (field) |
8395 | { |
8396 | field->set_name(thd, field_info->old_name, |
8397 | strlen(field_info->old_name), |
8398 | system_charset_info); |
8399 | if (add_item_to_list(thd, field)) |
8400 | return 1; |
8401 | } |
8402 | } |
8403 | return 0; |
8404 | } |
8405 | |
8406 | |
8407 | /* |
8408 | Create information_schema table |
8409 | |
8410 | SYNOPSIS |
8411 | mysql_schema_table() |
8412 | thd thread handler |
8413 | lex pointer to LEX |
8414 | table_list pointer to table_list |
8415 | |
8416 | RETURN |
8417 | 0 success |
8418 | 1 error |
8419 | */ |
8420 | |
8421 | int mysql_schema_table(THD *thd, LEX *lex, TABLE_LIST *table_list) |
8422 | { |
8423 | TABLE *table; |
8424 | DBUG_ENTER("mysql_schema_table" ); |
8425 | if (!(table= create_schema_table(thd, table_list))) |
8426 | DBUG_RETURN(1); |
8427 | table->s->tmp_table= SYSTEM_TMP_TABLE; |
8428 | table->grant.privilege= SELECT_ACL; |
8429 | /* |
8430 | This test is necessary to make |
8431 | case insensitive file systems + |
8432 | upper case table names(information schema tables) + |
8433 | views |
8434 | working correctly |
8435 | */ |
8436 | if (table_list->schema_table_name.str) |
8437 | table->alias_name_used= my_strcasecmp(table_alias_charset, |
8438 | table_list->schema_table_name.str, |
8439 | table_list->alias.str); |
8440 | table_list->table_name= table->s->table_name; |
8441 | table_list->table= table; |
8442 | table->next= thd->derived_tables; |
8443 | thd->derived_tables= table; |
8444 | table_list->select_lex->options |= OPTION_SCHEMA_TABLE; |
8445 | lex->safe_to_cache_query= 0; |
8446 | |
8447 | if (table_list->schema_table_reformed) // show command |
8448 | { |
8449 | SELECT_LEX *sel= lex->current_select; |
8450 | Item *item; |
8451 | Field_translator *transl, *org_transl; |
8452 | |
8453 | if (table_list->field_translation) |
8454 | { |
8455 | Field_translator *end= table_list->field_translation_end; |
8456 | for (transl= table_list->field_translation; transl < end; transl++) |
8457 | { |
8458 | if (!transl->item->fixed && |
8459 | transl->item->fix_fields(thd, &transl->item)) |
8460 | DBUG_RETURN(1); |
8461 | } |
8462 | DBUG_RETURN(0); |
8463 | } |
8464 | List_iterator_fast<Item> it(sel->item_list); |
8465 | if (!(transl= |
8466 | (Field_translator*)(thd->stmt_arena-> |
8467 | alloc(sel->item_list.elements * |
8468 | sizeof(Field_translator))))) |
8469 | { |
8470 | DBUG_RETURN(1); |
8471 | } |
8472 | for (org_transl= transl; (item= it++); transl++) |
8473 | { |
8474 | transl->item= item; |
8475 | transl->name= item->name; |
8476 | if (!item->fixed && item->fix_fields(thd, &transl->item)) |
8477 | { |
8478 | DBUG_RETURN(1); |
8479 | } |
8480 | } |
8481 | table_list->field_translation= org_transl; |
8482 | table_list->field_translation_end= transl; |
8483 | } |
8484 | |
8485 | DBUG_RETURN(0); |
8486 | } |
8487 | |
8488 | |
8489 | /* |
8490 | Generate select from information_schema table |
8491 | |
8492 | SYNOPSIS |
8493 | make_schema_select() |
8494 | thd thread handler |
8495 | sel pointer to SELECT_LEX |
8496 | schema_table_idx index of 'schema_tables' element |
8497 | |
8498 | RETURN |
8499 | 0 success |
8500 | 1 error |
8501 | */ |
8502 | |
8503 | int make_schema_select(THD *thd, SELECT_LEX *sel, |
8504 | ST_SCHEMA_TABLE *schema_table) |
8505 | { |
8506 | LEX_CSTRING db, table; |
8507 | DBUG_ENTER("make_schema_select" ); |
8508 | DBUG_PRINT("enter" , ("mysql_schema_select: %s" , schema_table->table_name)); |
8509 | /* |
8510 | We have to make non const db_name & table_name |
8511 | because of lower_case_table_names |
8512 | */ |
8513 | if (!thd->make_lex_string(&db, INFORMATION_SCHEMA_NAME.str, |
8514 | INFORMATION_SCHEMA_NAME.length)) |
8515 | DBUG_RETURN(1); |
8516 | |
8517 | if (!thd->make_lex_string(&table, schema_table->table_name, |
8518 | strlen(schema_table->table_name))) |
8519 | DBUG_RETURN(1); |
8520 | |
8521 | if (schema_table->old_format(thd, schema_table)) |
8522 | DBUG_RETURN(1); |
8523 | |
8524 | if (!sel->add_table_to_list(thd, new Table_ident(thd, &db, &table, 0), |
8525 | 0, 0, TL_READ, MDL_SHARED_READ)) |
8526 | DBUG_RETURN(1); |
8527 | |
8528 | sel->table_list.first->schema_table_reformed= 1; |
8529 | DBUG_RETURN(0); |
8530 | } |
8531 | |
8532 | |
8533 | /* |
8534 | Optimize reading from an I_S table. |
8535 | |
8536 | @detail |
8537 | This function prepares a plan for populating an I_S table with |
8538 | get_all_tables(). |
8539 | |
8540 | The plan is in IS_table_read_plan structure, it is saved in |
8541 | tables->is_table_read_plan. |
8542 | |
8543 | @return |
8544 | false - Ok |
8545 | true - Out Of Memory |
8546 | |
8547 | */ |
8548 | |
8549 | static bool optimize_for_get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) |
8550 | { |
8551 | SELECT_LEX *lsel= tables->schema_select_lex; |
8552 | ST_SCHEMA_TABLE *schema_table= tables->schema_table; |
8553 | enum enum_schema_tables schema_table_idx; |
8554 | IS_table_read_plan *plan; |
8555 | DBUG_ENTER("get_all_tables" ); |
8556 | |
8557 | if (!(plan= new IS_table_read_plan())) |
8558 | DBUG_RETURN(1); |
8559 | |
8560 | tables->is_table_read_plan= plan; |
8561 | |
8562 | schema_table_idx= get_schema_table_idx(schema_table); |
8563 | tables->table_open_method= get_table_open_method(tables, schema_table, |
8564 | schema_table_idx); |
8565 | DBUG_PRINT("open_method" , ("%d" , tables->table_open_method)); |
8566 | |
8567 | /* |
8568 | this branch processes SHOW FIELDS, SHOW INDEXES commands. |
8569 | see sql_parse.cc, prepare_schema_table() function where |
8570 | this values are initialized |
8571 | */ |
8572 | if (lsel && lsel->table_list.first) |
8573 | { |
8574 | /* These do not need to have a query plan */ |
8575 | plan->trivial_show_command= true; |
8576 | goto end; |
8577 | } |
8578 | |
8579 | if (get_lookup_field_values(thd, cond, tables, &plan->lookup_field_vals)) |
8580 | { |
8581 | plan->no_rows= true; |
8582 | goto end; |
8583 | } |
8584 | |
8585 | DBUG_PRINT("info" ,("db_name='%s', table_name='%s'" , |
8586 | plan->lookup_field_vals.db_value.str, |
8587 | plan->lookup_field_vals.table_value.str)); |
8588 | |
8589 | if (!plan->lookup_field_vals.wild_db_value && |
8590 | !plan->lookup_field_vals.wild_table_value) |
8591 | { |
8592 | /* |
8593 | if lookup value is empty string then |
8594 | it's impossible table name or db name |
8595 | */ |
8596 | if ((plan->lookup_field_vals.db_value.str && |
8597 | !plan->lookup_field_vals.db_value.str[0]) || |
8598 | (plan->lookup_field_vals.table_value.str && |
8599 | !plan->lookup_field_vals.table_value.str[0])) |
8600 | { |
8601 | plan->no_rows= true; |
8602 | goto end; |
8603 | } |
8604 | } |
8605 | |
8606 | if (plan->has_db_lookup_value() && plan->has_table_lookup_value()) |
8607 | plan->partial_cond= 0; |
8608 | else |
8609 | plan->partial_cond= make_cond_for_info_schema(thd, cond, tables); |
8610 | |
8611 | end: |
8612 | DBUG_RETURN(0); |
8613 | } |
8614 | |
8615 | |
8616 | /* |
8617 | This is the optimizer part of get_schema_tables_result(). |
8618 | */ |
8619 | |
8620 | bool optimize_schema_tables_reads(JOIN *join) |
8621 | { |
8622 | THD *thd= join->thd; |
8623 | bool result= 0; |
8624 | DBUG_ENTER("optimize_schema_tables_reads" ); |
8625 | |
8626 | JOIN_TAB *tab; |
8627 | for (tab= first_linear_tab(join, WITHOUT_BUSH_ROOTS, WITH_CONST_TABLES); |
8628 | tab; |
8629 | tab= next_linear_tab(join, tab, WITH_BUSH_ROOTS)) |
8630 | { |
8631 | if (!tab->table || !tab->table->pos_in_table_list) |
8632 | continue; |
8633 | |
8634 | TABLE_LIST *table_list= tab->table->pos_in_table_list; |
8635 | if (table_list->schema_table && thd->fill_information_schema_tables()) |
8636 | { |
8637 | /* A value of 0 indicates a dummy implementation */ |
8638 | if (table_list->schema_table->fill_table == 0) |
8639 | continue; |
8640 | |
8641 | /* skip I_S optimizations specific to get_all_tables */ |
8642 | if (table_list->schema_table->fill_table != get_all_tables) |
8643 | continue; |
8644 | |
8645 | Item *cond= tab->select_cond; |
8646 | if (tab->cache_select && tab->cache_select->cond) |
8647 | { |
8648 | /* |
8649 | If join buffering is used, we should use the condition that is |
8650 | attached to the join cache. Cache condition has a part of WHERE that |
8651 | can be checked when we're populating this table. |
8652 | join_tab->select_cond is of no interest, because it only has |
8653 | conditions that depend on both this table and previous tables in the |
8654 | join order. |
8655 | */ |
8656 | cond= tab->cache_select->cond; |
8657 | } |
8658 | |
8659 | optimize_for_get_all_tables(thd, table_list, cond); |
8660 | } |
8661 | } |
8662 | DBUG_RETURN(result); |
8663 | } |
8664 | |
8665 | |
8666 | /* |
8667 | Fill temporary schema tables before SELECT |
8668 | |
8669 | SYNOPSIS |
8670 | get_schema_tables_result() |
8671 | join join which use schema tables |
8672 | executed_place place where I_S table processed |
8673 | |
8674 | SEE ALSO |
8675 | The optimization part is done by get_schema_tables_result(). This function |
8676 | is run on query execution. |
8677 | |
8678 | RETURN |
8679 | FALSE success |
8680 | TRUE error |
8681 | */ |
8682 | |
8683 | bool get_schema_tables_result(JOIN *join, |
8684 | enum enum_schema_table_state executed_place) |
8685 | { |
8686 | THD *thd= join->thd; |
8687 | LEX *lex= thd->lex; |
8688 | bool result= 0; |
8689 | PSI_stage_info org_stage; |
8690 | DBUG_ENTER("get_schema_tables_result" ); |
8691 | |
8692 | Warnings_only_error_handler err_handler; |
8693 | thd->push_internal_handler(&err_handler); |
8694 | thd->backup_stage(&org_stage); |
8695 | THD_STAGE_INFO(thd, stage_filling_schema_table); |
8696 | |
8697 | JOIN_TAB *tab; |
8698 | for (tab= first_linear_tab(join, WITHOUT_BUSH_ROOTS, WITH_CONST_TABLES); |
8699 | tab; |
8700 | tab= next_linear_tab(join, tab, WITH_BUSH_ROOTS)) |
8701 | { |
8702 | if (!tab->table || !tab->table->pos_in_table_list) |
8703 | break; |
8704 | |
8705 | TABLE_LIST *table_list= tab->table->pos_in_table_list; |
8706 | if (table_list->schema_table && thd->fill_information_schema_tables()) |
8707 | { |
8708 | /* |
8709 | I_S tables only need to be re-populated if make_cond_for_info_schema() |
8710 | preserves outer fields |
8711 | */ |
8712 | bool is_subselect= &lex->unit != lex->current_select->master_unit() && |
8713 | lex->current_select->master_unit()->item && |
8714 | tab->select_cond && |
8715 | tab->select_cond->used_tables() & OUTER_REF_TABLE_BIT; |
8716 | |
8717 | /* A value of 0 indicates a dummy implementation */ |
8718 | if (table_list->schema_table->fill_table == 0) |
8719 | continue; |
8720 | |
8721 | /* skip I_S optimizations specific to get_all_tables */ |
8722 | if (lex->describe && |
8723 | (table_list->schema_table->fill_table != get_all_tables)) |
8724 | continue; |
8725 | |
8726 | /* |
8727 | If schema table is already processed and |
8728 | the statement is not a subselect then |
8729 | we don't need to fill this table again. |
8730 | If schema table is already processed and |
8731 | schema_table_state != executed_place then |
8732 | table is already processed and |
8733 | we should skip second data processing. |
8734 | */ |
8735 | if (table_list->schema_table_state && |
8736 | (!is_subselect || table_list->schema_table_state != executed_place)) |
8737 | continue; |
8738 | |
8739 | /* |
8740 | if table is used in a subselect and |
8741 | table has been processed earlier with the same |
8742 | 'executed_place' value then we should refresh the table. |
8743 | */ |
8744 | if (table_list->schema_table_state && is_subselect) |
8745 | { |
8746 | table_list->table->file->extra(HA_EXTRA_NO_CACHE); |
8747 | table_list->table->file->extra(HA_EXTRA_RESET_STATE); |
8748 | table_list->table->file->ha_delete_all_rows(); |
8749 | table_list->table->null_row= 0; |
8750 | } |
8751 | else |
8752 | table_list->table->file->stats.records= 0; |
8753 | |
8754 | Item *cond= tab->select_cond; |
8755 | if (tab->cache_select && tab->cache_select->cond) |
8756 | { |
8757 | /* |
8758 | If join buffering is used, we should use the condition that is |
8759 | attached to the join cache. Cache condition has a part of WHERE that |
8760 | can be checked when we're populating this table. |
8761 | join_tab->select_cond is of no interest, because it only has |
8762 | conditions that depend on both this table and previous tables in the |
8763 | join order. |
8764 | */ |
8765 | cond= tab->cache_select->cond; |
8766 | } |
8767 | |
8768 | if (table_list->schema_table->fill_table(thd, table_list, cond)) |
8769 | { |
8770 | result= 1; |
8771 | join->error= 1; |
8772 | tab->read_record.table->file= table_list->table->file; |
8773 | table_list->schema_table_state= executed_place; |
8774 | break; |
8775 | } |
8776 | tab->read_record.table->file= table_list->table->file; |
8777 | table_list->schema_table_state= executed_place; |
8778 | } |
8779 | } |
8780 | thd->pop_internal_handler(); |
8781 | if (unlikely(thd->is_error())) |
8782 | { |
8783 | /* |
8784 | This hack is here, because I_S code uses thd->clear_error() a lot. |
8785 | Which means, a Warnings_only_error_handler cannot handle the error |
8786 | corectly as it does not know whether an error is real (e.g. caused |
8787 | by tab->select_cond->val_int()) or will be cleared later. |
8788 | Thus it ignores all errors, and the real one (that is, the error |
8789 | that was not cleared) is pushed now. |
8790 | |
8791 | It also means that an audit plugin cannot process the error correctly |
8792 | either. See also thd->clear_error() |
8793 | */ |
8794 | thd->get_stmt_da()->push_warning(thd, |
8795 | thd->get_stmt_da()->sql_errno(), |
8796 | thd->get_stmt_da()->get_sqlstate(), |
8797 | Sql_condition::WARN_LEVEL_ERROR, |
8798 | thd->get_stmt_da()->message()); |
8799 | } |
8800 | else if (result) |
8801 | my_error(ER_UNKNOWN_ERROR, MYF(0)); |
8802 | THD_STAGE_INFO(thd, org_stage); |
8803 | DBUG_RETURN(result); |
8804 | } |
8805 | |
8806 | struct run_hton_fill_schema_table_args |
8807 | { |
8808 | TABLE_LIST *tables; |
8809 | COND *cond; |
8810 | }; |
8811 | |
8812 | static my_bool run_hton_fill_schema_table(THD *thd, plugin_ref plugin, |
8813 | void *arg) |
8814 | { |
8815 | struct run_hton_fill_schema_table_args *args= |
8816 | (run_hton_fill_schema_table_args *) arg; |
8817 | handlerton *hton= plugin_hton(plugin); |
8818 | if (hton->fill_is_table && hton->state == SHOW_OPTION_YES) |
8819 | hton->fill_is_table(hton, thd, args->tables, args->cond, |
8820 | get_schema_table_idx(args->tables->schema_table)); |
8821 | return false; |
8822 | } |
8823 | |
8824 | int hton_fill_schema_table(THD *thd, TABLE_LIST *tables, COND *cond) |
8825 | { |
8826 | DBUG_ENTER("hton_fill_schema_table" ); |
8827 | |
8828 | struct run_hton_fill_schema_table_args args; |
8829 | args.tables= tables; |
8830 | args.cond= cond; |
8831 | |
8832 | plugin_foreach(thd, run_hton_fill_schema_table, |
8833 | MYSQL_STORAGE_ENGINE_PLUGIN, &args); |
8834 | |
8835 | DBUG_RETURN(0); |
8836 | } |
8837 | |
8838 | |
8839 | static |
8840 | int store_key_cache_table_record(THD *thd, TABLE *table, |
8841 | const char *name, size_t name_length, |
8842 | KEY_CACHE *key_cache, |
8843 | uint partitions, uint partition_no) |
8844 | { |
8845 | KEY_CACHE_STATISTICS keycache_stats; |
8846 | uint err; |
8847 | DBUG_ENTER("store_key_cache_table_record" ); |
8848 | |
8849 | get_key_cache_statistics(key_cache, partition_no, &keycache_stats); |
8850 | |
8851 | if (!key_cache->key_cache_inited || keycache_stats.mem_size == 0) |
8852 | DBUG_RETURN(0); |
8853 | |
8854 | restore_record(table, s->default_values); |
8855 | table->field[0]->store(name, name_length, system_charset_info); |
8856 | if (partitions == 0) |
8857 | table->field[1]->set_null(); |
8858 | else |
8859 | { |
8860 | table->field[1]->set_notnull(); |
8861 | table->field[1]->store((long) partitions, TRUE); |
8862 | } |
8863 | |
8864 | if (partition_no == 0) |
8865 | table->field[2]->set_null(); |
8866 | else |
8867 | { |
8868 | table->field[2]->set_notnull(); |
8869 | table->field[2]->store((long) partition_no, TRUE); |
8870 | } |
8871 | table->field[3]->store(keycache_stats.mem_size, TRUE); |
8872 | table->field[4]->store(keycache_stats.block_size, TRUE); |
8873 | table->field[5]->store(keycache_stats.blocks_used, TRUE); |
8874 | table->field[6]->store(keycache_stats.blocks_unused, TRUE); |
8875 | table->field[7]->store(keycache_stats.blocks_changed, TRUE); |
8876 | table->field[8]->store(keycache_stats.read_requests, TRUE); |
8877 | table->field[9]->store(keycache_stats.reads, TRUE); |
8878 | table->field[10]->store(keycache_stats.write_requests, TRUE); |
8879 | table->field[11]->store(keycache_stats.writes, TRUE); |
8880 | |
8881 | err= schema_table_store_record(thd, table); |
8882 | DBUG_RETURN(err); |
8883 | } |
8884 | |
8885 | int run_fill_key_cache_tables(const char *name, KEY_CACHE *key_cache, void *p) |
8886 | { |
8887 | DBUG_ENTER("run_fill_key_cache_tables" ); |
8888 | |
8889 | if (!key_cache->key_cache_inited) |
8890 | DBUG_RETURN(0); |
8891 | |
8892 | TABLE *table= (TABLE *)p; |
8893 | THD *thd= table->in_use; |
8894 | uint partitions= key_cache->partitions; |
8895 | size_t namelen= strlen(name); |
8896 | DBUG_ASSERT(partitions <= MAX_KEY_CACHE_PARTITIONS); |
8897 | |
8898 | if (partitions) |
8899 | { |
8900 | for (uint i= 0; i < partitions; i++) |
8901 | { |
8902 | if (store_key_cache_table_record(thd, table, name, namelen, |
8903 | key_cache, partitions, i+1)) |
8904 | DBUG_RETURN(1); |
8905 | } |
8906 | } |
8907 | |
8908 | if (store_key_cache_table_record(thd, table, name, namelen, |
8909 | key_cache, partitions, 0)) |
8910 | DBUG_RETURN(1); |
8911 | DBUG_RETURN(0); |
8912 | } |
8913 | |
8914 | int fill_key_cache_tables(THD *thd, TABLE_LIST *tables, COND *cond) |
8915 | { |
8916 | DBUG_ENTER("fill_key_cache_tables" ); |
8917 | |
8918 | int res= process_key_caches(run_fill_key_cache_tables, tables->table); |
8919 | |
8920 | DBUG_RETURN(res); |
8921 | } |
8922 | |
8923 | |
8924 | ST_FIELD_INFO schema_fields_info[]= |
8925 | { |
8926 | {"CATALOG_NAME" , FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, |
8927 | {"SCHEMA_NAME" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Database" , |
8928 | SKIP_OPEN_TABLE}, |
8929 | {"DEFAULT_CHARACTER_SET_NAME" , MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, 0, |
8930 | SKIP_OPEN_TABLE}, |
8931 | {"DEFAULT_COLLATION_NAME" , MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, 0, |
8932 | SKIP_OPEN_TABLE}, |
8933 | {"SQL_PATH" , FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE}, |
8934 | {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE} |
8935 | }; |
8936 | |
8937 | |
8938 | ST_FIELD_INFO tables_fields_info[]= |
8939 | { |
8940 | {"TABLE_CATALOG" , FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, |
8941 | {"TABLE_SCHEMA" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, |
8942 | {"TABLE_NAME" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Name" , |
8943 | SKIP_OPEN_TABLE}, |
8944 | {"TABLE_TYPE" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY}, |
8945 | {"ENGINE" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, "Engine" , OPEN_FRM_ONLY}, |
8946 | {"VERSION" , MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, |
8947 | (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Version" , OPEN_FRM_ONLY}, |
8948 | {"ROW_FORMAT" , 10, MYSQL_TYPE_STRING, 0, 1, "Row_format" , OPEN_FULL_TABLE}, |
8949 | {"TABLE_ROWS" , MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, |
8950 | (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Rows" , OPEN_FULL_TABLE}, |
8951 | {"AVG_ROW_LENGTH" , MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, |
8952 | (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Avg_row_length" , OPEN_FULL_TABLE}, |
8953 | {"DATA_LENGTH" , MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, |
8954 | (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Data_length" , OPEN_FULL_TABLE}, |
8955 | {"MAX_DATA_LENGTH" , MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, |
8956 | (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Max_data_length" , OPEN_FULL_TABLE}, |
8957 | {"INDEX_LENGTH" , MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, |
8958 | (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Index_length" , OPEN_FULL_TABLE}, |
8959 | {"DATA_FREE" , MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, |
8960 | (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Data_free" , OPEN_FULL_TABLE}, |
8961 | {"AUTO_INCREMENT" , MY_INT64_NUM_DECIMAL_DIGITS , MYSQL_TYPE_LONGLONG, 0, |
8962 | (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Auto_increment" , OPEN_FULL_TABLE}, |
8963 | {"CREATE_TIME" , 0, MYSQL_TYPE_DATETIME, 0, 1, "Create_time" , OPEN_FULL_TABLE}, |
8964 | {"UPDATE_TIME" , 0, MYSQL_TYPE_DATETIME, 0, 1, "Update_time" , OPEN_FULL_TABLE}, |
8965 | {"CHECK_TIME" , 0, MYSQL_TYPE_DATETIME, 0, 1, "Check_time" , OPEN_FULL_TABLE}, |
8966 | {"TABLE_COLLATION" , MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 1, "Collation" , |
8967 | OPEN_FRM_ONLY}, |
8968 | {"CHECKSUM" , MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, |
8969 | (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Checksum" , OPEN_FULL_TABLE}, |
8970 | {"CREATE_OPTIONS" , 2048, MYSQL_TYPE_STRING, 0, 1, "Create_options" , |
8971 | OPEN_FULL_TABLE}, |
8972 | {"TABLE_COMMENT" , TABLE_COMMENT_MAXLEN, MYSQL_TYPE_STRING, 0, 0, |
8973 | "Comment" , OPEN_FRM_ONLY}, |
8974 | {"MAX_INDEX_LENGTH" , MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, |
8975 | (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Max_index_length" , OPEN_FULL_TABLE}, |
8976 | {"TEMPORARY" , 1, MYSQL_TYPE_STRING, 0, MY_I_S_MAYBE_NULL, "Temporary" , OPEN_FRM_ONLY}, |
8977 | {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE} |
8978 | }; |
8979 | |
8980 | |
8981 | ST_FIELD_INFO columns_fields_info[]= |
8982 | { |
8983 | {"TABLE_CATALOG" , FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY}, |
8984 | {"TABLE_SCHEMA" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY}, |
8985 | {"TABLE_NAME" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY}, |
8986 | {"COLUMN_NAME" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Field" , |
8987 | OPEN_FRM_ONLY}, |
8988 | {"ORDINAL_POSITION" , MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, |
8989 | MY_I_S_UNSIGNED, 0, OPEN_FRM_ONLY}, |
8990 | {"COLUMN_DEFAULT" , MAX_FIELD_VARCHARLENGTH, MYSQL_TYPE_STRING, 0, |
8991 | 1, "Default" , OPEN_FRM_ONLY}, |
8992 | {"IS_NULLABLE" , 3, MYSQL_TYPE_STRING, 0, 0, "Null" , OPEN_FRM_ONLY}, |
8993 | {"DATA_TYPE" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY}, |
8994 | {"CHARACTER_MAXIMUM_LENGTH" , MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, |
8995 | 0, (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0, OPEN_FRM_ONLY}, |
8996 | {"CHARACTER_OCTET_LENGTH" , MY_INT64_NUM_DECIMAL_DIGITS , MYSQL_TYPE_LONGLONG, |
8997 | 0, (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0, OPEN_FRM_ONLY}, |
8998 | {"NUMERIC_PRECISION" , MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, |
8999 | 0, (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0, OPEN_FRM_ONLY}, |
9000 | {"NUMERIC_SCALE" , MY_INT64_NUM_DECIMAL_DIGITS , MYSQL_TYPE_LONGLONG, |
9001 | 0, (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0, OPEN_FRM_ONLY}, |
9002 | {"DATETIME_PRECISION" , MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, |
9003 | 0, (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0, OPEN_FRM_ONLY}, |
9004 | {"CHARACTER_SET_NAME" , MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 1, 0, |
9005 | OPEN_FRM_ONLY}, |
9006 | {"COLLATION_NAME" , MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 1, "Collation" , |
9007 | OPEN_FRM_ONLY}, |
9008 | {"COLUMN_TYPE" , 65535, MYSQL_TYPE_STRING, 0, 0, "Type" , OPEN_FRM_ONLY}, |
9009 | {"COLUMN_KEY" , 3, MYSQL_TYPE_STRING, 0, 0, "Key" , OPEN_FRM_ONLY}, |
9010 | {"EXTRA" , 30, MYSQL_TYPE_STRING, 0, 0, "Extra" , OPEN_FRM_ONLY}, |
9011 | {"PRIVILEGES" , 80, MYSQL_TYPE_STRING, 0, 0, "Privileges" , OPEN_FRM_ONLY}, |
9012 | {"COLUMN_COMMENT" , COLUMN_COMMENT_MAXLEN, MYSQL_TYPE_STRING, 0, 0, |
9013 | "Comment" , OPEN_FRM_ONLY}, |
9014 | {"IS_GENERATED" , 6, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY}, |
9015 | {"GENERATION_EXPRESSION" , MAX_FIELD_VARCHARLENGTH, MYSQL_TYPE_STRING, 0, 1, |
9016 | 0, OPEN_FRM_ONLY}, |
9017 | {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE} |
9018 | }; |
9019 | |
9020 | |
9021 | ST_FIELD_INFO charsets_fields_info[]= |
9022 | { |
9023 | {"CHARACTER_SET_NAME" , MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, "Charset" , |
9024 | SKIP_OPEN_TABLE}, |
9025 | {"DEFAULT_COLLATE_NAME" , MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, |
9026 | "Default collation" , SKIP_OPEN_TABLE}, |
9027 | {"DESCRIPTION" , 60, MYSQL_TYPE_STRING, 0, 0, "Description" , |
9028 | SKIP_OPEN_TABLE}, |
9029 | {"MAXLEN" , 3, MYSQL_TYPE_LONGLONG, 0, 0, "Maxlen" , SKIP_OPEN_TABLE}, |
9030 | {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE} |
9031 | }; |
9032 | |
9033 | |
9034 | ST_FIELD_INFO collation_fields_info[]= |
9035 | { |
9036 | {"COLLATION_NAME" , MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, "Collation" , |
9037 | SKIP_OPEN_TABLE}, |
9038 | {"CHARACTER_SET_NAME" , MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, "Charset" , |
9039 | SKIP_OPEN_TABLE}, |
9040 | {"ID" , MY_INT32_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, 0, "Id" , |
9041 | SKIP_OPEN_TABLE}, |
9042 | {"IS_DEFAULT" , 3, MYSQL_TYPE_STRING, 0, 0, "Default" , SKIP_OPEN_TABLE}, |
9043 | {"IS_COMPILED" , 3, MYSQL_TYPE_STRING, 0, 0, "Compiled" , SKIP_OPEN_TABLE}, |
9044 | {"SORTLEN" , 3, MYSQL_TYPE_LONGLONG, 0, 0, "Sortlen" , SKIP_OPEN_TABLE}, |
9045 | {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE} |
9046 | }; |
9047 | |
9048 | |
9049 | ST_FIELD_INFO applicable_roles_fields_info[]= |
9050 | { |
9051 | {"GRANTEE" , USERNAME_WITH_HOST_CHAR_LENGTH, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, |
9052 | {"ROLE_NAME" , USERNAME_CHAR_LENGTH, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, |
9053 | {"IS_GRANTABLE" , 3, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, |
9054 | {"IS_DEFAULT" , 3, MYSQL_TYPE_STRING, 0, MY_I_S_MAYBE_NULL, 0, SKIP_OPEN_TABLE}, |
9055 | {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE} |
9056 | }; |
9057 | |
9058 | |
9059 | ST_FIELD_INFO enabled_roles_fields_info[]= |
9060 | { |
9061 | {"ROLE_NAME" , USERNAME_CHAR_LENGTH, MYSQL_TYPE_STRING, 0, MY_I_S_MAYBE_NULL, 0, SKIP_OPEN_TABLE}, |
9062 | {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE} |
9063 | }; |
9064 | |
9065 | |
9066 | ST_FIELD_INFO engines_fields_info[]= |
9067 | { |
9068 | {"ENGINE" , 64, MYSQL_TYPE_STRING, 0, 0, "Engine" , SKIP_OPEN_TABLE}, |
9069 | {"SUPPORT" , 8, MYSQL_TYPE_STRING, 0, 0, "Support" , SKIP_OPEN_TABLE}, |
9070 | {"COMMENT" , 160, MYSQL_TYPE_STRING, 0, 0, "Comment" , SKIP_OPEN_TABLE}, |
9071 | {"TRANSACTIONS" , 3, MYSQL_TYPE_STRING, 0, 1, "Transactions" , SKIP_OPEN_TABLE}, |
9072 | {"XA" , 3, MYSQL_TYPE_STRING, 0, 1, "XA" , SKIP_OPEN_TABLE}, |
9073 | {"SAVEPOINTS" , 3 ,MYSQL_TYPE_STRING, 0, 1, "Savepoints" , SKIP_OPEN_TABLE}, |
9074 | {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE} |
9075 | }; |
9076 | |
9077 | |
9078 | ST_FIELD_INFO events_fields_info[]= |
9079 | { |
9080 | {"EVENT_CATALOG" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, |
9081 | {"EVENT_SCHEMA" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Db" , |
9082 | SKIP_OPEN_TABLE}, |
9083 | {"EVENT_NAME" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Name" , |
9084 | SKIP_OPEN_TABLE}, |
9085 | {"DEFINER" , DEFINER_CHAR_LENGTH, MYSQL_TYPE_STRING, 0, 0, "Definer" , SKIP_OPEN_TABLE}, |
9086 | {"TIME_ZONE" , 64, MYSQL_TYPE_STRING, 0, 0, "Time zone" , SKIP_OPEN_TABLE}, |
9087 | {"EVENT_BODY" , 8, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, |
9088 | {"EVENT_DEFINITION" , 65535, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, |
9089 | {"EVENT_TYPE" , 9, MYSQL_TYPE_STRING, 0, 0, "Type" , SKIP_OPEN_TABLE}, |
9090 | {"EXECUTE_AT" , 0, MYSQL_TYPE_DATETIME, 0, 1, "Execute at" , SKIP_OPEN_TABLE}, |
9091 | {"INTERVAL_VALUE" , 256, MYSQL_TYPE_STRING, 0, 1, "Interval value" , |
9092 | SKIP_OPEN_TABLE}, |
9093 | {"INTERVAL_FIELD" , 18, MYSQL_TYPE_STRING, 0, 1, "Interval field" , |
9094 | SKIP_OPEN_TABLE}, |
9095 | {"SQL_MODE" , 32*256, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, |
9096 | {"STARTS" , 0, MYSQL_TYPE_DATETIME, 0, 1, "Starts" , SKIP_OPEN_TABLE}, |
9097 | {"ENDS" , 0, MYSQL_TYPE_DATETIME, 0, 1, "Ends" , SKIP_OPEN_TABLE}, |
9098 | {"STATUS" , 18, MYSQL_TYPE_STRING, 0, 0, "Status" , SKIP_OPEN_TABLE}, |
9099 | {"ON_COMPLETION" , 12, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, |
9100 | {"CREATED" , 0, MYSQL_TYPE_DATETIME, 0, 0, 0, SKIP_OPEN_TABLE}, |
9101 | {"LAST_ALTERED" , 0, MYSQL_TYPE_DATETIME, 0, 0, 0, SKIP_OPEN_TABLE}, |
9102 | {"LAST_EXECUTED" , 0, MYSQL_TYPE_DATETIME, 0, 1, 0, SKIP_OPEN_TABLE}, |
9103 | {"EVENT_COMMENT" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, |
9104 | {"ORIGINATOR" , 10, MYSQL_TYPE_LONGLONG, 0, 0, "Originator" , SKIP_OPEN_TABLE}, |
9105 | {"CHARACTER_SET_CLIENT" , MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, |
9106 | "character_set_client" , SKIP_OPEN_TABLE}, |
9107 | {"COLLATION_CONNECTION" , MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, |
9108 | "collation_connection" , SKIP_OPEN_TABLE}, |
9109 | {"DATABASE_COLLATION" , MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, |
9110 | "Database Collation" , SKIP_OPEN_TABLE}, |
9111 | {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE} |
9112 | }; |
9113 | |
9114 | |
9115 | |
9116 | ST_FIELD_INFO coll_charset_app_fields_info[]= |
9117 | { |
9118 | {"COLLATION_NAME" , MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, 0, |
9119 | SKIP_OPEN_TABLE}, |
9120 | {"CHARACTER_SET_NAME" , MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, 0, |
9121 | SKIP_OPEN_TABLE}, |
9122 | {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE} |
9123 | }; |
9124 | |
9125 | |
9126 | ST_FIELD_INFO proc_fields_info[]= |
9127 | { |
9128 | {"SPECIFIC_NAME" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, |
9129 | {"ROUTINE_CATALOG" , FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, |
9130 | {"ROUTINE_SCHEMA" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Db" , |
9131 | SKIP_OPEN_TABLE}, |
9132 | {"ROUTINE_NAME" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Name" , |
9133 | SKIP_OPEN_TABLE}, |
9134 | {"ROUTINE_TYPE" , 13, MYSQL_TYPE_STRING, 0, 0, "Type" , SKIP_OPEN_TABLE}, |
9135 | {"DATA_TYPE" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, |
9136 | {"CHARACTER_MAXIMUM_LENGTH" , 21 , MYSQL_TYPE_LONG, 0, 1, 0, SKIP_OPEN_TABLE}, |
9137 | {"CHARACTER_OCTET_LENGTH" , 21 , MYSQL_TYPE_LONG, 0, 1, 0, SKIP_OPEN_TABLE}, |
9138 | {"NUMERIC_PRECISION" , 21 , MYSQL_TYPE_LONG, 0, 1, 0, SKIP_OPEN_TABLE}, |
9139 | {"NUMERIC_SCALE" , 21 , MYSQL_TYPE_LONG, 0, 1, 0, SKIP_OPEN_TABLE}, |
9140 | {"DATETIME_PRECISION" , MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, |
9141 | 0, (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0, OPEN_FRM_ONLY}, |
9142 | {"CHARACTER_SET_NAME" , 64, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE}, |
9143 | {"COLLATION_NAME" , 64, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE}, |
9144 | {"DTD_IDENTIFIER" , 65535, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE}, |
9145 | {"ROUTINE_BODY" , 8, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, |
9146 | {"ROUTINE_DEFINITION" , 65535, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE}, |
9147 | {"EXTERNAL_NAME" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE}, |
9148 | {"EXTERNAL_LANGUAGE" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, |
9149 | SKIP_OPEN_TABLE}, |
9150 | {"PARAMETER_STYLE" , 8, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, |
9151 | {"IS_DETERMINISTIC" , 3, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, |
9152 | {"SQL_DATA_ACCESS" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, |
9153 | SKIP_OPEN_TABLE}, |
9154 | {"SQL_PATH" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE}, |
9155 | {"SECURITY_TYPE" , 7, MYSQL_TYPE_STRING, 0, 0, "Security_type" , |
9156 | SKIP_OPEN_TABLE}, |
9157 | {"CREATED" , 0, MYSQL_TYPE_DATETIME, 0, 0, "Created" , SKIP_OPEN_TABLE}, |
9158 | {"LAST_ALTERED" , 0, MYSQL_TYPE_DATETIME, 0, 0, "Modified" , SKIP_OPEN_TABLE}, |
9159 | {"SQL_MODE" , 32*256, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, |
9160 | {"ROUTINE_COMMENT" , 65535, MYSQL_TYPE_STRING, 0, 0, "Comment" , |
9161 | SKIP_OPEN_TABLE}, |
9162 | {"DEFINER" , DEFINER_CHAR_LENGTH, MYSQL_TYPE_STRING, 0, 0, "Definer" , SKIP_OPEN_TABLE}, |
9163 | {"CHARACTER_SET_CLIENT" , MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, |
9164 | "character_set_client" , SKIP_OPEN_TABLE}, |
9165 | {"COLLATION_CONNECTION" , MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, |
9166 | "collation_connection" , SKIP_OPEN_TABLE}, |
9167 | {"DATABASE_COLLATION" , MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, |
9168 | "Database Collation" , SKIP_OPEN_TABLE}, |
9169 | {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE} |
9170 | }; |
9171 | |
9172 | |
9173 | ST_FIELD_INFO stat_fields_info[]= |
9174 | { |
9175 | {"TABLE_CATALOG" , FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY}, |
9176 | {"TABLE_SCHEMA" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY}, |
9177 | {"TABLE_NAME" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Table" , OPEN_FRM_ONLY}, |
9178 | {"NON_UNIQUE" , 1, MYSQL_TYPE_LONGLONG, 0, 0, "Non_unique" , OPEN_FRM_ONLY}, |
9179 | {"INDEX_SCHEMA" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY}, |
9180 | {"INDEX_NAME" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Key_name" , |
9181 | OPEN_FRM_ONLY}, |
9182 | {"SEQ_IN_INDEX" , 2, MYSQL_TYPE_LONGLONG, 0, 0, "Seq_in_index" , OPEN_FRM_ONLY}, |
9183 | {"COLUMN_NAME" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Column_name" , |
9184 | OPEN_FRM_ONLY}, |
9185 | {"COLLATION" , 1, MYSQL_TYPE_STRING, 0, 1, "Collation" , OPEN_FRM_ONLY}, |
9186 | {"CARDINALITY" , MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, 1, |
9187 | "Cardinality" , OPEN_FULL_TABLE}, |
9188 | {"SUB_PART" , 3, MYSQL_TYPE_LONGLONG, 0, 1, "Sub_part" , OPEN_FRM_ONLY}, |
9189 | {"PACKED" , 10, MYSQL_TYPE_STRING, 0, 1, "Packed" , OPEN_FRM_ONLY}, |
9190 | {"NULLABLE" , 3, MYSQL_TYPE_STRING, 0, 0, "Null" , OPEN_FRM_ONLY}, |
9191 | {"INDEX_TYPE" , 16, MYSQL_TYPE_STRING, 0, 0, "Index_type" , OPEN_FULL_TABLE}, |
9192 | {"COMMENT" , 16, MYSQL_TYPE_STRING, 0, 1, "Comment" , OPEN_FRM_ONLY}, |
9193 | {"INDEX_COMMENT" , INDEX_COMMENT_MAXLEN, MYSQL_TYPE_STRING, 0, 0, |
9194 | "Index_comment" , OPEN_FRM_ONLY}, |
9195 | {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE} |
9196 | }; |
9197 | |
9198 | |
9199 | ST_FIELD_INFO view_fields_info[]= |
9200 | { |
9201 | {"TABLE_CATALOG" , FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY}, |
9202 | {"TABLE_SCHEMA" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY}, |
9203 | {"TABLE_NAME" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY}, |
9204 | {"VIEW_DEFINITION" , 65535, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY}, |
9205 | {"CHECK_OPTION" , 8, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY}, |
9206 | {"IS_UPDATABLE" , 3, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, |
9207 | {"DEFINER" , DEFINER_CHAR_LENGTH, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY}, |
9208 | {"SECURITY_TYPE" , 7, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY}, |
9209 | {"CHARACTER_SET_CLIENT" , MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, 0, |
9210 | OPEN_FRM_ONLY}, |
9211 | {"COLLATION_CONNECTION" , MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, 0, |
9212 | OPEN_FRM_ONLY}, |
9213 | {"ALGORITHM" , 10, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY}, |
9214 | {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE} |
9215 | }; |
9216 | |
9217 | |
9218 | ST_FIELD_INFO user_privileges_fields_info[]= |
9219 | { |
9220 | {"GRANTEE" , USERNAME_WITH_HOST_CHAR_LENGTH, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, |
9221 | {"TABLE_CATALOG" , FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, |
9222 | {"PRIVILEGE_TYPE" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, |
9223 | {"IS_GRANTABLE" , 3, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, |
9224 | {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE} |
9225 | }; |
9226 | |
9227 | |
9228 | ST_FIELD_INFO schema_privileges_fields_info[]= |
9229 | { |
9230 | {"GRANTEE" , USERNAME_WITH_HOST_CHAR_LENGTH, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, |
9231 | {"TABLE_CATALOG" , FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, |
9232 | {"TABLE_SCHEMA" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, |
9233 | {"PRIVILEGE_TYPE" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, |
9234 | {"IS_GRANTABLE" , 3, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, |
9235 | {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE} |
9236 | }; |
9237 | |
9238 | |
9239 | ST_FIELD_INFO table_privileges_fields_info[]= |
9240 | { |
9241 | {"GRANTEE" , USERNAME_WITH_HOST_CHAR_LENGTH, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, |
9242 | {"TABLE_CATALOG" , FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, |
9243 | {"TABLE_SCHEMA" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, |
9244 | {"TABLE_NAME" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, |
9245 | {"PRIVILEGE_TYPE" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, |
9246 | {"IS_GRANTABLE" , 3, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, |
9247 | {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE} |
9248 | }; |
9249 | |
9250 | |
9251 | ST_FIELD_INFO column_privileges_fields_info[]= |
9252 | { |
9253 | {"GRANTEE" , USERNAME_WITH_HOST_CHAR_LENGTH, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, |
9254 | {"TABLE_CATALOG" , FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, |
9255 | {"TABLE_SCHEMA" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, |
9256 | {"TABLE_NAME" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, |
9257 | {"COLUMN_NAME" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, |
9258 | {"PRIVILEGE_TYPE" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, |
9259 | {"IS_GRANTABLE" , 3, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, |
9260 | {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE} |
9261 | }; |
9262 | |
9263 | |
9264 | ST_FIELD_INFO table_constraints_fields_info[]= |
9265 | { |
9266 | {"CONSTRAINT_CATALOG" , FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, |
9267 | {"CONSTRAINT_SCHEMA" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, |
9268 | OPEN_FULL_TABLE}, |
9269 | {"CONSTRAINT_NAME" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, |
9270 | OPEN_FULL_TABLE}, |
9271 | {"TABLE_SCHEMA" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, |
9272 | {"TABLE_NAME" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, |
9273 | {"CONSTRAINT_TYPE" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, |
9274 | OPEN_FULL_TABLE}, |
9275 | {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE} |
9276 | }; |
9277 | |
9278 | |
9279 | ST_FIELD_INFO key_column_usage_fields_info[]= |
9280 | { |
9281 | {"CONSTRAINT_CATALOG" , FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, |
9282 | {"CONSTRAINT_SCHEMA" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, |
9283 | OPEN_FULL_TABLE}, |
9284 | {"CONSTRAINT_NAME" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, |
9285 | OPEN_FULL_TABLE}, |
9286 | {"TABLE_CATALOG" , FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, |
9287 | {"TABLE_SCHEMA" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, |
9288 | {"TABLE_NAME" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, |
9289 | {"COLUMN_NAME" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, |
9290 | {"ORDINAL_POSITION" , 10 ,MYSQL_TYPE_LONGLONG, 0, 0, 0, OPEN_FULL_TABLE}, |
9291 | {"POSITION_IN_UNIQUE_CONSTRAINT" , 10 ,MYSQL_TYPE_LONGLONG, 0, 1, 0, |
9292 | OPEN_FULL_TABLE}, |
9293 | {"REFERENCED_TABLE_SCHEMA" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, |
9294 | OPEN_FULL_TABLE}, |
9295 | {"REFERENCED_TABLE_NAME" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, |
9296 | OPEN_FULL_TABLE}, |
9297 | {"REFERENCED_COLUMN_NAME" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, |
9298 | OPEN_FULL_TABLE}, |
9299 | {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE} |
9300 | }; |
9301 | |
9302 | |
9303 | ST_FIELD_INFO table_names_fields_info[]= |
9304 | { |
9305 | {"TABLE_CATALOG" , FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, |
9306 | {"TABLE_SCHEMA" ,NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, |
9307 | {"TABLE_NAME" , NAME_CHAR_LEN + MYSQL50_TABLE_NAME_PREFIX_LENGTH, |
9308 | MYSQL_TYPE_STRING, 0, 0, "Tables_in_" , SKIP_OPEN_TABLE}, |
9309 | {"TABLE_TYPE" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Table_type" , |
9310 | OPEN_FRM_ONLY}, |
9311 | {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE} |
9312 | }; |
9313 | |
9314 | |
9315 | ST_FIELD_INFO open_tables_fields_info[]= |
9316 | { |
9317 | {"Database" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Database" , |
9318 | SKIP_OPEN_TABLE}, |
9319 | {"Table" ,NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Table" , SKIP_OPEN_TABLE}, |
9320 | {"In_use" , 1, MYSQL_TYPE_LONGLONG, 0, 0, "In_use" , SKIP_OPEN_TABLE}, |
9321 | {"Name_locked" , 4, MYSQL_TYPE_LONGLONG, 0, 0, "Name_locked" , SKIP_OPEN_TABLE}, |
9322 | {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE} |
9323 | }; |
9324 | |
9325 | |
9326 | ST_FIELD_INFO triggers_fields_info[]= |
9327 | { |
9328 | {"TRIGGER_CATALOG" , FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY}, |
9329 | {"TRIGGER_SCHEMA" ,NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY}, |
9330 | {"TRIGGER_NAME" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Trigger" , |
9331 | OPEN_FRM_ONLY}, |
9332 | {"EVENT_MANIPULATION" , 6, MYSQL_TYPE_STRING, 0, 0, "Event" , OPEN_FRM_ONLY}, |
9333 | {"EVENT_OBJECT_CATALOG" , FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, |
9334 | OPEN_FRM_ONLY}, |
9335 | {"EVENT_OBJECT_SCHEMA" ,NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, |
9336 | OPEN_FRM_ONLY}, |
9337 | {"EVENT_OBJECT_TABLE" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Table" , |
9338 | OPEN_FRM_ONLY}, |
9339 | {"ACTION_ORDER" , 4, MYSQL_TYPE_LONGLONG, 0, 0, 0, OPEN_FRM_ONLY}, |
9340 | {"ACTION_CONDITION" , 65535, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FRM_ONLY}, |
9341 | {"ACTION_STATEMENT" , 65535, MYSQL_TYPE_STRING, 0, 0, "Statement" , |
9342 | OPEN_FRM_ONLY}, |
9343 | {"ACTION_ORIENTATION" , 9, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY}, |
9344 | {"ACTION_TIMING" , 6, MYSQL_TYPE_STRING, 0, 0, "Timing" , OPEN_FRM_ONLY}, |
9345 | {"ACTION_REFERENCE_OLD_TABLE" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, |
9346 | OPEN_FRM_ONLY}, |
9347 | {"ACTION_REFERENCE_NEW_TABLE" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, |
9348 | OPEN_FRM_ONLY}, |
9349 | {"ACTION_REFERENCE_OLD_ROW" , 3, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY}, |
9350 | {"ACTION_REFERENCE_NEW_ROW" , 3, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY}, |
9351 | /* 2 here indicates 2 decimals */ |
9352 | {"CREATED" , 2, MYSQL_TYPE_DATETIME, 0, 1, "Created" , OPEN_FRM_ONLY}, |
9353 | {"SQL_MODE" , 32*256, MYSQL_TYPE_STRING, 0, 0, "sql_mode" , OPEN_FRM_ONLY}, |
9354 | {"DEFINER" , DEFINER_CHAR_LENGTH, MYSQL_TYPE_STRING, 0, 0, "Definer" , OPEN_FRM_ONLY}, |
9355 | {"CHARACTER_SET_CLIENT" , MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, |
9356 | "character_set_client" , OPEN_FRM_ONLY}, |
9357 | {"COLLATION_CONNECTION" , MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, |
9358 | "collation_connection" , OPEN_FRM_ONLY}, |
9359 | {"DATABASE_COLLATION" , MY_CS_NAME_SIZE, MYSQL_TYPE_STRING, 0, 0, |
9360 | "Database Collation" , OPEN_FRM_ONLY}, |
9361 | {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE} |
9362 | }; |
9363 | |
9364 | |
9365 | ST_FIELD_INFO partitions_fields_info[]= |
9366 | { |
9367 | {"TABLE_CATALOG" , FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, |
9368 | {"TABLE_SCHEMA" ,NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, |
9369 | {"TABLE_NAME" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, |
9370 | {"PARTITION_NAME" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FULL_TABLE}, |
9371 | {"SUBPARTITION_NAME" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, |
9372 | OPEN_FULL_TABLE}, |
9373 | {"PARTITION_ORDINAL_POSITION" , 21 , MYSQL_TYPE_LONGLONG, 0, |
9374 | (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0, OPEN_FULL_TABLE}, |
9375 | {"SUBPARTITION_ORDINAL_POSITION" , 21 , MYSQL_TYPE_LONGLONG, 0, |
9376 | (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0, OPEN_FULL_TABLE}, |
9377 | {"PARTITION_METHOD" , 18, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FULL_TABLE}, |
9378 | {"SUBPARTITION_METHOD" , 12, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FULL_TABLE}, |
9379 | {"PARTITION_EXPRESSION" , 65535, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FULL_TABLE}, |
9380 | {"SUBPARTITION_EXPRESSION" , 65535, MYSQL_TYPE_STRING, 0, 1, 0, |
9381 | OPEN_FULL_TABLE}, |
9382 | {"PARTITION_DESCRIPTION" , 65535, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FULL_TABLE}, |
9383 | {"TABLE_ROWS" , 21 , MYSQL_TYPE_LONGLONG, 0, MY_I_S_UNSIGNED, 0, |
9384 | OPEN_FULL_TABLE}, |
9385 | {"AVG_ROW_LENGTH" , 21 , MYSQL_TYPE_LONGLONG, 0, MY_I_S_UNSIGNED, 0, |
9386 | OPEN_FULL_TABLE}, |
9387 | {"DATA_LENGTH" , 21 , MYSQL_TYPE_LONGLONG, 0, MY_I_S_UNSIGNED, 0, |
9388 | OPEN_FULL_TABLE}, |
9389 | {"MAX_DATA_LENGTH" , 21 , MYSQL_TYPE_LONGLONG, 0, |
9390 | (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0, OPEN_FULL_TABLE}, |
9391 | {"INDEX_LENGTH" , 21 , MYSQL_TYPE_LONGLONG, 0, MY_I_S_UNSIGNED, 0, |
9392 | OPEN_FULL_TABLE}, |
9393 | {"DATA_FREE" , 21 , MYSQL_TYPE_LONGLONG, 0, MY_I_S_UNSIGNED, 0, |
9394 | OPEN_FULL_TABLE}, |
9395 | {"CREATE_TIME" , 0, MYSQL_TYPE_DATETIME, 0, 1, 0, OPEN_FULL_TABLE}, |
9396 | {"UPDATE_TIME" , 0, MYSQL_TYPE_DATETIME, 0, 1, 0, OPEN_FULL_TABLE}, |
9397 | {"CHECK_TIME" , 0, MYSQL_TYPE_DATETIME, 0, 1, 0, OPEN_FULL_TABLE}, |
9398 | {"CHECKSUM" , 21 , MYSQL_TYPE_LONGLONG, 0, |
9399 | (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0, OPEN_FULL_TABLE}, |
9400 | {"PARTITION_COMMENT" , 80, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, |
9401 | {"NODEGROUP" , 12 , MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, |
9402 | {"TABLESPACE_NAME" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, |
9403 | OPEN_FULL_TABLE}, |
9404 | {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE} |
9405 | }; |
9406 | |
9407 | |
9408 | ST_FIELD_INFO variables_fields_info[]= |
9409 | { |
9410 | {"VARIABLE_NAME" , 64, MYSQL_TYPE_STRING, 0, 0, "Variable_name" , |
9411 | SKIP_OPEN_TABLE}, |
9412 | {"VARIABLE_VALUE" , 2048, MYSQL_TYPE_STRING, 0, 0, "Value" , SKIP_OPEN_TABLE}, |
9413 | {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE} |
9414 | }; |
9415 | |
9416 | |
9417 | ST_FIELD_INFO sysvars_fields_info[]= |
9418 | { |
9419 | {"VARIABLE_NAME" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, 0}, |
9420 | {"SESSION_VALUE" , 2048, MYSQL_TYPE_STRING, 0, MY_I_S_MAYBE_NULL, 0, 0}, |
9421 | {"GLOBAL_VALUE" , 2048, MYSQL_TYPE_STRING, 0, MY_I_S_MAYBE_NULL, 0, 0}, |
9422 | {"GLOBAL_VALUE_ORIGIN" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, 0}, |
9423 | {"DEFAULT_VALUE" , 2048, MYSQL_TYPE_STRING, 0, MY_I_S_MAYBE_NULL, 0, 0}, |
9424 | {"VARIABLE_SCOPE" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, 0}, |
9425 | {"VARIABLE_TYPE" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, 0}, |
9426 | {"VARIABLE_COMMENT" , TABLE_COMMENT_MAXLEN, MYSQL_TYPE_STRING, 0, 0, 0, 0}, |
9427 | {"NUMERIC_MIN_VALUE" , MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_STRING, 0, MY_I_S_MAYBE_NULL, 0, 0}, |
9428 | {"NUMERIC_MAX_VALUE" , MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_STRING, 0, MY_I_S_MAYBE_NULL, 0, 0}, |
9429 | {"NUMERIC_BLOCK_SIZE" , MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_STRING, 0, MY_I_S_MAYBE_NULL, 0, 0}, |
9430 | {"ENUM_VALUE_LIST" , 65535, MYSQL_TYPE_STRING, 0, MY_I_S_MAYBE_NULL, 0, 0}, |
9431 | {"READ_ONLY" , 3, MYSQL_TYPE_STRING, 0, 0, 0, 0}, |
9432 | {"COMMAND_LINE_ARGUMENT" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, MY_I_S_MAYBE_NULL, 0, 0}, |
9433 | {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, 0} |
9434 | }; |
9435 | |
9436 | |
9437 | ST_FIELD_INFO processlist_fields_info[]= |
9438 | { |
9439 | {"ID" , 4, MYSQL_TYPE_LONGLONG, 0, 0, "Id" , SKIP_OPEN_TABLE}, |
9440 | {"USER" , USERNAME_CHAR_LENGTH, MYSQL_TYPE_STRING, 0, 0, "User" , |
9441 | SKIP_OPEN_TABLE}, |
9442 | {"HOST" , LIST_PROCESS_HOST_LEN, MYSQL_TYPE_STRING, 0, 0, "Host" , |
9443 | SKIP_OPEN_TABLE}, |
9444 | {"DB" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, "Db" , SKIP_OPEN_TABLE}, |
9445 | {"COMMAND" , 16, MYSQL_TYPE_STRING, 0, 0, "Command" , SKIP_OPEN_TABLE}, |
9446 | {"TIME" , 7, MYSQL_TYPE_LONG, 0, 0, "Time" , SKIP_OPEN_TABLE}, |
9447 | {"STATE" , 64, MYSQL_TYPE_STRING, 0, 1, "State" , SKIP_OPEN_TABLE}, |
9448 | {"INFO" , PROCESS_LIST_INFO_WIDTH, MYSQL_TYPE_STRING, 0, 1, "Info" , |
9449 | SKIP_OPEN_TABLE}, |
9450 | {"TIME_MS" , 100 * (MY_INT64_NUM_DECIMAL_DIGITS + 1) + 3, MYSQL_TYPE_DECIMAL, |
9451 | 0, 0, "Time_ms" , SKIP_OPEN_TABLE}, |
9452 | {"STAGE" , 2, MYSQL_TYPE_TINY, 0, 0, "Stage" , SKIP_OPEN_TABLE}, |
9453 | {"MAX_STAGE" , 2, MYSQL_TYPE_TINY, 0, 0, "Max_stage" , SKIP_OPEN_TABLE}, |
9454 | {"PROGRESS" , 703, MYSQL_TYPE_DECIMAL, 0, 0, "Progress" , |
9455 | SKIP_OPEN_TABLE}, |
9456 | {"MEMORY_USED" , 7, MYSQL_TYPE_LONGLONG, 0, 0, "Memory_used" , SKIP_OPEN_TABLE}, |
9457 | {"MAX_MEMORY_USED" , 7, MYSQL_TYPE_LONGLONG, 0, 0, "Max_memory_used" , SKIP_OPEN_TABLE}, |
9458 | {"EXAMINED_ROWS" , 7, MYSQL_TYPE_LONG, 0, 0, "Examined_rows" , SKIP_OPEN_TABLE}, |
9459 | {"QUERY_ID" , 4, MYSQL_TYPE_LONGLONG, 0, 0, 0, SKIP_OPEN_TABLE}, |
9460 | {"INFO_BINARY" , PROCESS_LIST_INFO_WIDTH, MYSQL_TYPE_BLOB, 0, 1, |
9461 | "Info_binary" , SKIP_OPEN_TABLE}, |
9462 | {"TID" , 4, MYSQL_TYPE_LONGLONG, 0, 0, "Tid" , SKIP_OPEN_TABLE}, |
9463 | {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE} |
9464 | }; |
9465 | |
9466 | |
9467 | ST_FIELD_INFO plugin_fields_info[]= |
9468 | { |
9469 | {"PLUGIN_NAME" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Name" , |
9470 | SKIP_OPEN_TABLE}, |
9471 | {"PLUGIN_VERSION" , 20, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, |
9472 | {"PLUGIN_STATUS" , 16, MYSQL_TYPE_STRING, 0, 0, "Status" , SKIP_OPEN_TABLE}, |
9473 | {"PLUGIN_TYPE" , 80, MYSQL_TYPE_STRING, 0, 0, "Type" , SKIP_OPEN_TABLE}, |
9474 | {"PLUGIN_TYPE_VERSION" , 20, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, |
9475 | {"PLUGIN_LIBRARY" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, "Library" , |
9476 | SKIP_OPEN_TABLE}, |
9477 | {"PLUGIN_LIBRARY_VERSION" , 20, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE}, |
9478 | {"PLUGIN_AUTHOR" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE}, |
9479 | {"PLUGIN_DESCRIPTION" , 65535, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE}, |
9480 | {"PLUGIN_LICENSE" , 80, MYSQL_TYPE_STRING, 0, 0, "License" , SKIP_OPEN_TABLE}, |
9481 | {"LOAD_OPTION" , 64, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, |
9482 | {"PLUGIN_MATURITY" , 12, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, |
9483 | {"PLUGIN_AUTH_VERSION" , 80, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE}, |
9484 | {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE} |
9485 | }; |
9486 | |
9487 | ST_FIELD_INFO files_fields_info[]= |
9488 | { |
9489 | {"FILE_ID" , 4, MYSQL_TYPE_LONGLONG, 0, 0, 0, SKIP_OPEN_TABLE}, |
9490 | {"FILE_NAME" , FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE}, |
9491 | {"FILE_TYPE" , 20, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, |
9492 | {"TABLESPACE_NAME" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, |
9493 | SKIP_OPEN_TABLE}, |
9494 | {"TABLE_CATALOG" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, |
9495 | {"TABLE_SCHEMA" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE}, |
9496 | {"TABLE_NAME" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE}, |
9497 | {"LOGFILE_GROUP_NAME" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, |
9498 | SKIP_OPEN_TABLE}, |
9499 | {"LOGFILE_GROUP_NUMBER" , 4, MYSQL_TYPE_LONGLONG, 0, 1, 0, SKIP_OPEN_TABLE}, |
9500 | {"ENGINE" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, |
9501 | {"FULLTEXT_KEYS" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE}, |
9502 | {"DELETED_ROWS" , 4, MYSQL_TYPE_LONGLONG, 0, 1, 0, SKIP_OPEN_TABLE}, |
9503 | {"UPDATE_COUNT" , 4, MYSQL_TYPE_LONGLONG, 0, 1, 0, SKIP_OPEN_TABLE}, |
9504 | {"FREE_EXTENTS" , 4, MYSQL_TYPE_LONGLONG, 0, 1, 0, SKIP_OPEN_TABLE}, |
9505 | {"TOTAL_EXTENTS" , 4, MYSQL_TYPE_LONGLONG, 0, 1, 0, SKIP_OPEN_TABLE}, |
9506 | {"EXTENT_SIZE" , 4, MYSQL_TYPE_LONGLONG, 0, 0, 0, SKIP_OPEN_TABLE}, |
9507 | {"INITIAL_SIZE" , 21, MYSQL_TYPE_LONGLONG, 0, |
9508 | (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0, SKIP_OPEN_TABLE}, |
9509 | {"MAXIMUM_SIZE" , 21, MYSQL_TYPE_LONGLONG, 0, |
9510 | (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0, SKIP_OPEN_TABLE}, |
9511 | {"AUTOEXTEND_SIZE" , 21, MYSQL_TYPE_LONGLONG, 0, |
9512 | (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0, SKIP_OPEN_TABLE}, |
9513 | {"CREATION_TIME" , 0, MYSQL_TYPE_DATETIME, 0, 1, 0, SKIP_OPEN_TABLE}, |
9514 | {"LAST_UPDATE_TIME" , 0, MYSQL_TYPE_DATETIME, 0, 1, 0, SKIP_OPEN_TABLE}, |
9515 | {"LAST_ACCESS_TIME" , 0, MYSQL_TYPE_DATETIME, 0, 1, 0, SKIP_OPEN_TABLE}, |
9516 | {"RECOVER_TIME" , 4, MYSQL_TYPE_LONGLONG, 0, 1, 0, SKIP_OPEN_TABLE}, |
9517 | {"TRANSACTION_COUNTER" , 4, MYSQL_TYPE_LONGLONG, 0, 1, 0, SKIP_OPEN_TABLE}, |
9518 | {"VERSION" , 21 , MYSQL_TYPE_LONGLONG, 0, |
9519 | (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Version" , SKIP_OPEN_TABLE}, |
9520 | {"ROW_FORMAT" , 10, MYSQL_TYPE_STRING, 0, 1, "Row_format" , SKIP_OPEN_TABLE}, |
9521 | {"TABLE_ROWS" , 21 , MYSQL_TYPE_LONGLONG, 0, |
9522 | (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Rows" , SKIP_OPEN_TABLE}, |
9523 | {"AVG_ROW_LENGTH" , 21 , MYSQL_TYPE_LONGLONG, 0, |
9524 | (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Avg_row_length" , SKIP_OPEN_TABLE}, |
9525 | {"DATA_LENGTH" , 21 , MYSQL_TYPE_LONGLONG, 0, |
9526 | (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Data_length" , SKIP_OPEN_TABLE}, |
9527 | {"MAX_DATA_LENGTH" , 21 , MYSQL_TYPE_LONGLONG, 0, |
9528 | (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Max_data_length" , SKIP_OPEN_TABLE}, |
9529 | {"INDEX_LENGTH" , 21 , MYSQL_TYPE_LONGLONG, 0, |
9530 | (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Index_length" , SKIP_OPEN_TABLE}, |
9531 | {"DATA_FREE" , 21 , MYSQL_TYPE_LONGLONG, 0, |
9532 | (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Data_free" , SKIP_OPEN_TABLE}, |
9533 | {"CREATE_TIME" , 0, MYSQL_TYPE_DATETIME, 0, 1, "Create_time" , SKIP_OPEN_TABLE}, |
9534 | {"UPDATE_TIME" , 0, MYSQL_TYPE_DATETIME, 0, 1, "Update_time" , SKIP_OPEN_TABLE}, |
9535 | {"CHECK_TIME" , 0, MYSQL_TYPE_DATETIME, 0, 1, "Check_time" , SKIP_OPEN_TABLE}, |
9536 | {"CHECKSUM" , 21 , MYSQL_TYPE_LONGLONG, 0, |
9537 | (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), "Checksum" , SKIP_OPEN_TABLE}, |
9538 | {"STATUS" , 20, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, |
9539 | {"EXTRA" , 255, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE}, |
9540 | {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE} |
9541 | }; |
9542 | |
9543 | void init_fill_schema_files_row(TABLE* table) |
9544 | { |
9545 | int i; |
9546 | for(i=0; files_fields_info[i].field_name!=NULL; i++) |
9547 | table->field[i]->set_null(); |
9548 | |
9549 | table->field[IS_FILES_STATUS]->set_notnull(); |
9550 | table->field[IS_FILES_STATUS]->store("NORMAL" , 6, system_charset_info); |
9551 | } |
9552 | |
9553 | ST_FIELD_INFO referential_constraints_fields_info[]= |
9554 | { |
9555 | {"CONSTRAINT_CATALOG" , FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, |
9556 | {"CONSTRAINT_SCHEMA" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, |
9557 | OPEN_FULL_TABLE}, |
9558 | {"CONSTRAINT_NAME" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, |
9559 | OPEN_FULL_TABLE}, |
9560 | {"UNIQUE_CONSTRAINT_CATALOG" , FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, |
9561 | OPEN_FULL_TABLE}, |
9562 | {"UNIQUE_CONSTRAINT_SCHEMA" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, |
9563 | OPEN_FULL_TABLE}, |
9564 | {"UNIQUE_CONSTRAINT_NAME" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, |
9565 | MY_I_S_MAYBE_NULL, 0, OPEN_FULL_TABLE}, |
9566 | {"MATCH_OPTION" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, |
9567 | {"UPDATE_RULE" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, |
9568 | {"DELETE_RULE" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, |
9569 | {"TABLE_NAME" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, |
9570 | {"REFERENCED_TABLE_NAME" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, |
9571 | OPEN_FULL_TABLE}, |
9572 | {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE} |
9573 | }; |
9574 | |
9575 | |
9576 | ST_FIELD_INFO parameters_fields_info[]= |
9577 | { |
9578 | {"SPECIFIC_CATALOG" , FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, |
9579 | {"SPECIFIC_SCHEMA" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, |
9580 | OPEN_FULL_TABLE}, |
9581 | {"SPECIFIC_NAME" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, |
9582 | {"ORDINAL_POSITION" , 21 , MYSQL_TYPE_LONG, 0, 0, 0, OPEN_FULL_TABLE}, |
9583 | {"PARAMETER_MODE" , 5, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FULL_TABLE}, |
9584 | {"PARAMETER_NAME" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FULL_TABLE}, |
9585 | {"DATA_TYPE" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, |
9586 | {"CHARACTER_MAXIMUM_LENGTH" , 21 , MYSQL_TYPE_LONG, 0, 1, 0, OPEN_FULL_TABLE}, |
9587 | {"CHARACTER_OCTET_LENGTH" , 21 , MYSQL_TYPE_LONG, 0, 1, 0, OPEN_FULL_TABLE}, |
9588 | {"NUMERIC_PRECISION" , 21 , MYSQL_TYPE_LONG, 0, 1, 0, OPEN_FULL_TABLE}, |
9589 | {"NUMERIC_SCALE" , 21 , MYSQL_TYPE_LONG, 0, 1, 0, OPEN_FULL_TABLE}, |
9590 | {"DATETIME_PRECISION" , MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, |
9591 | 0, (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0, OPEN_FRM_ONLY}, |
9592 | {"CHARACTER_SET_NAME" , 64, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FULL_TABLE}, |
9593 | {"COLLATION_NAME" , 64, MYSQL_TYPE_STRING, 0, 1, 0, OPEN_FULL_TABLE}, |
9594 | {"DTD_IDENTIFIER" , 65535, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, |
9595 | {"ROUTINE_TYPE" , 9, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE}, |
9596 | {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FULL_TABLE} |
9597 | }; |
9598 | |
9599 | |
9600 | ST_FIELD_INFO tablespaces_fields_info[]= |
9601 | { |
9602 | {"TABLESPACE_NAME" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, |
9603 | SKIP_OPEN_TABLE}, |
9604 | {"ENGINE" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, |
9605 | {"TABLESPACE_TYPE" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, MY_I_S_MAYBE_NULL, |
9606 | 0, SKIP_OPEN_TABLE}, |
9607 | {"LOGFILE_GROUP_NAME" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, MY_I_S_MAYBE_NULL, |
9608 | 0, SKIP_OPEN_TABLE}, |
9609 | {"EXTENT_SIZE" , 21, MYSQL_TYPE_LONGLONG, 0, |
9610 | MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED, 0, SKIP_OPEN_TABLE}, |
9611 | {"AUTOEXTEND_SIZE" , 21, MYSQL_TYPE_LONGLONG, 0, |
9612 | MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED, 0, SKIP_OPEN_TABLE}, |
9613 | {"MAXIMUM_SIZE" , 21, MYSQL_TYPE_LONGLONG, 0, |
9614 | MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED, 0, SKIP_OPEN_TABLE}, |
9615 | {"NODEGROUP_ID" , 21, MYSQL_TYPE_LONGLONG, 0, |
9616 | MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED, 0, SKIP_OPEN_TABLE}, |
9617 | {"TABLESPACE_COMMENT" , 2048, MYSQL_TYPE_STRING, 0, MY_I_S_MAYBE_NULL, 0, |
9618 | SKIP_OPEN_TABLE}, |
9619 | {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE} |
9620 | }; |
9621 | |
9622 | |
9623 | ST_FIELD_INFO keycache_fields_info[]= |
9624 | { |
9625 | {"KEY_CACHE_NAME" , NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, |
9626 | {"SEGMENTS" , 3, MYSQL_TYPE_LONG, 0, |
9627 | (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED) , 0, SKIP_OPEN_TABLE}, |
9628 | {"SEGMENT_NUMBER" , 3, MYSQL_TYPE_LONG, 0, |
9629 | (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0, SKIP_OPEN_TABLE}, |
9630 | {"FULL_SIZE" , MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, |
9631 | (MY_I_S_UNSIGNED), 0, SKIP_OPEN_TABLE}, |
9632 | {"BLOCK_SIZE" , MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, |
9633 | (MY_I_S_UNSIGNED), 0, SKIP_OPEN_TABLE }, |
9634 | {"USED_BLOCKS" , MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, |
9635 | (MY_I_S_UNSIGNED), "Key_blocks_used" , SKIP_OPEN_TABLE}, |
9636 | {"UNUSED_BLOCKS" , MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, |
9637 | (MY_I_S_UNSIGNED), "Key_blocks_unused" , SKIP_OPEN_TABLE}, |
9638 | {"DIRTY_BLOCKS" , MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, |
9639 | (MY_I_S_UNSIGNED), "Key_blocks_not_flushed" , SKIP_OPEN_TABLE}, |
9640 | {"READ_REQUESTS" , MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, |
9641 | (MY_I_S_UNSIGNED), "Key_read_requests" , SKIP_OPEN_TABLE}, |
9642 | {"READS" , MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, |
9643 | (MY_I_S_UNSIGNED), "Key_reads" , SKIP_OPEN_TABLE}, |
9644 | {"WRITE_REQUESTS" , MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, |
9645 | (MY_I_S_UNSIGNED), "Key_write_requests" , SKIP_OPEN_TABLE}, |
9646 | {"WRITES" , MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, |
9647 | (MY_I_S_UNSIGNED), "Key_writes" , SKIP_OPEN_TABLE}, |
9648 | {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE} |
9649 | }; |
9650 | |
9651 | |
9652 | ST_FIELD_INFO show_explain_fields_info[]= |
9653 | { |
9654 | /* field_name, length, type, value, field_flags, old_name*/ |
9655 | {"id" , 3, MYSQL_TYPE_LONGLONG, 0 /*value*/, MY_I_S_MAYBE_NULL, "id" , |
9656 | SKIP_OPEN_TABLE}, |
9657 | {"select_type" , 19, MYSQL_TYPE_STRING, 0 /*value*/, 0, "select_type" , |
9658 | SKIP_OPEN_TABLE}, |
9659 | {"table" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0 /*value*/, MY_I_S_MAYBE_NULL, |
9660 | "table" , SKIP_OPEN_TABLE}, |
9661 | {"type" , 15, MYSQL_TYPE_STRING, 0, MY_I_S_MAYBE_NULL, "type" , SKIP_OPEN_TABLE}, |
9662 | {"possible_keys" , NAME_CHAR_LEN*MAX_KEY, MYSQL_TYPE_STRING, 0/*value*/, |
9663 | MY_I_S_MAYBE_NULL, "possible_keys" , SKIP_OPEN_TABLE}, |
9664 | {"key" , NAME_CHAR_LEN*MAX_KEY, MYSQL_TYPE_STRING, 0/*value*/, |
9665 | MY_I_S_MAYBE_NULL, "key" , SKIP_OPEN_TABLE}, |
9666 | {"key_len" , NAME_CHAR_LEN*MAX_KEY, MYSQL_TYPE_STRING, 0/*value*/, |
9667 | MY_I_S_MAYBE_NULL, "key_len" , SKIP_OPEN_TABLE}, |
9668 | {"ref" , NAME_CHAR_LEN*MAX_REF_PARTS, MYSQL_TYPE_STRING, 0/*value*/, |
9669 | MY_I_S_MAYBE_NULL, "ref" , SKIP_OPEN_TABLE}, |
9670 | {"rows" , 10, MYSQL_TYPE_LONGLONG, 0/*value*/, MY_I_S_MAYBE_NULL, "rows" , |
9671 | SKIP_OPEN_TABLE}, |
9672 | {"Extra" , 255, MYSQL_TYPE_STRING, 0/*value*/, 0 /*flags*/, "Extra" , |
9673 | SKIP_OPEN_TABLE}, |
9674 | {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE} |
9675 | }; |
9676 | |
9677 | |
9678 | #ifdef HAVE_SPATIAL |
9679 | ST_FIELD_INFO geometry_columns_fields_info[]= |
9680 | { |
9681 | {"F_TABLE_CATALOG" , FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY}, |
9682 | {"F_TABLE_SCHEMA" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY}, |
9683 | {"F_TABLE_NAME" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY}, |
9684 | {"F_GEOMETRY_COLUMN" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Field" , |
9685 | OPEN_FRM_ONLY}, |
9686 | {"G_TABLE_CATALOG" , FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY}, |
9687 | {"G_TABLE_SCHEMA" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY}, |
9688 | {"G_TABLE_NAME" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, OPEN_FRM_ONLY}, |
9689 | {"G_GEOMETRY_COLUMN" , NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Field" , |
9690 | OPEN_FRM_ONLY}, |
9691 | {"STORAGE_TYPE" , 2, MYSQL_TYPE_TINY, 0, 0, 0, OPEN_FRM_ONLY}, |
9692 | {"GEOMETRY_TYPE" , 7, MYSQL_TYPE_LONG, 0, 0, 0, OPEN_FRM_ONLY}, |
9693 | {"COORD_DIMENSION" , 2, MYSQL_TYPE_TINY, 0, 0, 0, OPEN_FRM_ONLY}, |
9694 | {"MAX_PPR" , 2, MYSQL_TYPE_TINY, 0, 0, 0, OPEN_FRM_ONLY}, |
9695 | {"SRID" , 5, MYSQL_TYPE_SHORT, 0, 0, 0, OPEN_FRM_ONLY}, |
9696 | {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, 0} |
9697 | }; |
9698 | |
9699 | |
9700 | ST_FIELD_INFO spatial_ref_sys_fields_info[]= |
9701 | { |
9702 | {"SRID" , 5, MYSQL_TYPE_SHORT, 0, 0, 0, SKIP_OPEN_TABLE}, |
9703 | {"AUTH_NAME" , FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, |
9704 | {"AUTH_SRID" , 5, MYSQL_TYPE_LONG, 0, 0, 0, SKIP_OPEN_TABLE}, |
9705 | {"SRTEXT" , 2048, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, |
9706 | {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, 0} |
9707 | }; |
9708 | #endif /*HAVE_SPATIAL*/ |
9709 | |
9710 | |
9711 | /* |
9712 | Description of ST_FIELD_INFO in table.h |
9713 | |
9714 | Make sure that the order of schema_tables and enum_schema_tables are the same. |
9715 | |
9716 | */ |
9717 | |
9718 | ST_SCHEMA_TABLE schema_tables[]= |
9719 | { |
9720 | {"ALL_PLUGINS" , plugin_fields_info, 0, |
9721 | fill_all_plugins, make_old_format, 0, 5, -1, 0, 0}, |
9722 | {"APPLICABLE_ROLES" , applicable_roles_fields_info, 0, |
9723 | fill_schema_applicable_roles, 0, 0, -1, -1, 0, 0}, |
9724 | {"CHARACTER_SETS" , charsets_fields_info, 0, |
9725 | fill_schema_charsets, make_character_sets_old_format, 0, -1, -1, 0, 0}, |
9726 | {"COLLATIONS" , collation_fields_info, 0, |
9727 | fill_schema_collation, make_old_format, 0, -1, -1, 0, 0}, |
9728 | {"COLLATION_CHARACTER_SET_APPLICABILITY" , coll_charset_app_fields_info, |
9729 | 0, fill_schema_coll_charset_app, 0, 0, -1, -1, 0, 0}, |
9730 | {"COLUMNS" , columns_fields_info, 0, |
9731 | get_all_tables, make_columns_old_format, get_schema_column_record, 1, 2, 0, |
9732 | OPTIMIZE_I_S_TABLE|OPEN_VIEW_FULL}, |
9733 | {"COLUMN_PRIVILEGES" , column_privileges_fields_info, 0, |
9734 | fill_schema_column_privileges, 0, 0, -1, -1, 0, 0}, |
9735 | {"ENABLED_ROLES" , enabled_roles_fields_info, 0, |
9736 | fill_schema_enabled_roles, 0, 0, -1, -1, 0, 0}, |
9737 | {"ENGINES" , engines_fields_info, 0, |
9738 | fill_schema_engines, make_old_format, 0, -1, -1, 0, 0}, |
9739 | #ifdef HAVE_EVENT_SCHEDULER |
9740 | {"EVENTS" , events_fields_info, 0, |
9741 | Events::fill_schema_events, make_old_format, 0, -1, -1, 0, 0}, |
9742 | #else |
9743 | {"EVENTS" , events_fields_info, 0, |
9744 | 0, make_old_format, 0, -1, -1, 0, 0}, |
9745 | #endif |
9746 | {"EXPLAIN" , show_explain_fields_info, 0, fill_show_explain, |
9747 | make_old_format, 0, -1, -1, TRUE /*hidden*/ , 0}, |
9748 | {"FILES" , files_fields_info, 0, |
9749 | hton_fill_schema_table, 0, 0, -1, -1, 0, 0}, |
9750 | {"GLOBAL_STATUS" , variables_fields_info, 0, |
9751 | fill_status, make_old_format, 0, 0, -1, 0, 0}, |
9752 | {"GLOBAL_VARIABLES" , variables_fields_info, 0, |
9753 | fill_variables, make_old_format, 0, 0, -1, 0, 0}, |
9754 | {"KEY_CACHES" , keycache_fields_info, 0, |
9755 | fill_key_cache_tables, 0, 0, -1,-1, 0, 0}, |
9756 | {"KEY_COLUMN_USAGE" , key_column_usage_fields_info, 0, |
9757 | get_all_tables, 0, get_schema_key_column_usage_record, 4, 5, 0, |
9758 | OPTIMIZE_I_S_TABLE|OPEN_TABLE_ONLY}, |
9759 | {"OPEN_TABLES" , open_tables_fields_info, 0, |
9760 | fill_open_tables, make_old_format, 0, -1, -1, 1, 0}, |
9761 | {"PARAMETERS" , parameters_fields_info, 0, |
9762 | fill_schema_proc, 0, 0, -1, -1, 0, 0}, |
9763 | {"PARTITIONS" , partitions_fields_info, 0, |
9764 | get_all_tables, 0, get_schema_partitions_record, 1, 2, 0, |
9765 | OPTIMIZE_I_S_TABLE|OPEN_TABLE_ONLY}, |
9766 | {"PLUGINS" , plugin_fields_info, 0, |
9767 | fill_plugins, make_old_format, 0, -1, -1, 0, 0}, |
9768 | {"PROCESSLIST" , processlist_fields_info, 0, |
9769 | fill_schema_processlist, make_old_format, 0, -1, -1, 0, 0}, |
9770 | {"PROFILING" , query_profile_statistics_info, 0, |
9771 | fill_query_profile_statistics_info, make_profile_table_for_show, |
9772 | NULL, -1, -1, false, 0}, |
9773 | {"REFERENTIAL_CONSTRAINTS" , referential_constraints_fields_info, |
9774 | 0, get_all_tables, 0, get_referential_constraints_record, |
9775 | 1, 9, 0, OPTIMIZE_I_S_TABLE|OPEN_TABLE_ONLY}, |
9776 | {"ROUTINES" , proc_fields_info, 0, |
9777 | fill_schema_proc, make_proc_old_format, 0, -1, -1, 0, 0}, |
9778 | {"SCHEMATA" , schema_fields_info, 0, |
9779 | fill_schema_schemata, make_schemata_old_format, 0, 1, -1, 0, 0}, |
9780 | {"SCHEMA_PRIVILEGES" , schema_privileges_fields_info, 0, |
9781 | fill_schema_schema_privileges, 0, 0, -1, -1, 0, 0}, |
9782 | {"SESSION_STATUS" , variables_fields_info, 0, |
9783 | fill_status, make_old_format, 0, 0, -1, 0, 0}, |
9784 | {"SESSION_VARIABLES" , variables_fields_info, 0, |
9785 | fill_variables, make_old_format, 0, 0, -1, 0, 0}, |
9786 | {"STATISTICS" , stat_fields_info, 0, |
9787 | get_all_tables, make_old_format, get_schema_stat_record, 1, 2, 0, |
9788 | OPEN_TABLE_ONLY|OPTIMIZE_I_S_TABLE}, |
9789 | {"SYSTEM_VARIABLES" , sysvars_fields_info, 0, |
9790 | fill_sysvars, make_old_format, 0, 0, -1, 0, 0}, |
9791 | {"TABLES" , tables_fields_info, 0, |
9792 | get_all_tables, make_old_format, get_schema_tables_record, 1, 2, 0, |
9793 | OPTIMIZE_I_S_TABLE}, |
9794 | {"TABLESPACES" , tablespaces_fields_info, 0, |
9795 | hton_fill_schema_table, 0, 0, -1, -1, 0, 0}, |
9796 | {"TABLE_CONSTRAINTS" , table_constraints_fields_info, 0, |
9797 | get_all_tables, 0, get_schema_constraints_record, 3, 4, 0, |
9798 | OPTIMIZE_I_S_TABLE|OPEN_TABLE_ONLY}, |
9799 | {"TABLE_NAMES" , table_names_fields_info, 0, |
9800 | get_all_tables, make_table_names_old_format, 0, 1, 2, 1, OPTIMIZE_I_S_TABLE}, |
9801 | {"TABLE_PRIVILEGES" , table_privileges_fields_info, 0, |
9802 | fill_schema_table_privileges, 0, 0, -1, -1, 0, 0}, |
9803 | {"TRIGGERS" , triggers_fields_info, 0, |
9804 | get_all_tables, make_old_format, get_schema_triggers_record, 5, 6, 0, |
9805 | OPEN_TRIGGER_ONLY|OPTIMIZE_I_S_TABLE}, |
9806 | {"USER_PRIVILEGES" , user_privileges_fields_info, 0, |
9807 | fill_schema_user_privileges, 0, 0, -1, -1, 0, 0}, |
9808 | {"VIEWS" , view_fields_info, 0, |
9809 | get_all_tables, 0, get_schema_views_record, 1, 2, 0, |
9810 | OPEN_VIEW_ONLY|OPTIMIZE_I_S_TABLE}, |
9811 | #ifdef HAVE_SPATIAL |
9812 | {"GEOMETRY_COLUMNS" , geometry_columns_fields_info, 0, |
9813 | get_all_tables, make_columns_old_format, get_geometry_column_record, |
9814 | 1, 2, 0, OPTIMIZE_I_S_TABLE|OPEN_VIEW_FULL}, |
9815 | {"SPATIAL_REF_SYS" , spatial_ref_sys_fields_info, 0, |
9816 | fill_spatial_ref_sys, make_old_format, 0, -1, -1, 0, 0}, |
9817 | #endif /*HAVE_SPATIAL*/ |
9818 | {0, 0, 0, 0, 0, 0, 0, 0, 0, 0} |
9819 | }; |
9820 | |
9821 | |
9822 | int initialize_schema_table(st_plugin_int *plugin) |
9823 | { |
9824 | ST_SCHEMA_TABLE *schema_table; |
9825 | DBUG_ENTER("initialize_schema_table" ); |
9826 | |
9827 | if (!(schema_table= (ST_SCHEMA_TABLE *)my_malloc(sizeof(ST_SCHEMA_TABLE), |
9828 | MYF(MY_WME | MY_ZEROFILL)))) |
9829 | DBUG_RETURN(1); |
9830 | /* Historical Requirement */ |
9831 | plugin->data= schema_table; // shortcut for the future |
9832 | if (plugin->plugin->init) |
9833 | { |
9834 | schema_table->idx_field1= -1, |
9835 | schema_table->idx_field2= -1; |
9836 | |
9837 | /* Make the name available to the init() function. */ |
9838 | schema_table->table_name= plugin->name.str; |
9839 | |
9840 | if (plugin->plugin->init(schema_table)) |
9841 | { |
9842 | sql_print_error("Plugin '%s' init function returned error." , |
9843 | plugin->name.str); |
9844 | plugin->data= NULL; |
9845 | my_free(schema_table); |
9846 | DBUG_RETURN(1); |
9847 | } |
9848 | |
9849 | if (!schema_table->old_format) |
9850 | for (ST_FIELD_INFO *f= schema_table->fields_info; f->field_name; f++) |
9851 | if (f->old_name && f->old_name[0]) |
9852 | { |
9853 | schema_table->old_format= make_old_format; |
9854 | break; |
9855 | } |
9856 | |
9857 | /* Make sure the plugin name is not set inside the init() function. */ |
9858 | schema_table->table_name= plugin->name.str; |
9859 | } |
9860 | DBUG_RETURN(0); |
9861 | } |
9862 | |
9863 | int finalize_schema_table(st_plugin_int *plugin) |
9864 | { |
9865 | ST_SCHEMA_TABLE *schema_table= (ST_SCHEMA_TABLE *)plugin->data; |
9866 | DBUG_ENTER("finalize_schema_table" ); |
9867 | |
9868 | if (schema_table) |
9869 | { |
9870 | if (plugin->plugin->deinit) |
9871 | { |
9872 | DBUG_PRINT("info" , ("Deinitializing plugin: '%s'" , plugin->name.str)); |
9873 | if (plugin->plugin->deinit(NULL)) |
9874 | { |
9875 | DBUG_PRINT("warning" , ("Plugin '%s' deinit function returned error." , |
9876 | plugin->name.str)); |
9877 | } |
9878 | } |
9879 | my_free(schema_table); |
9880 | } |
9881 | DBUG_RETURN(0); |
9882 | } |
9883 | |
9884 | /* |
9885 | This is used to create a timestamp field |
9886 | */ |
9887 | |
9888 | MYSQL_TIME zero_time={ 0,0,0,0,0,0,0,0, MYSQL_TIMESTAMP_TIME }; |
9889 | |
9890 | /** |
9891 | Output trigger information (SHOW CREATE TRIGGER) to the client. |
9892 | |
9893 | @param thd Thread context. |
9894 | @param trigger Trigger to dump |
9895 | |
9896 | @return Operation status |
9897 | @retval TRUE Error. |
9898 | @retval FALSE Success. |
9899 | */ |
9900 | |
9901 | static bool show_create_trigger_impl(THD *thd, Trigger *trigger) |
9902 | { |
9903 | int ret_code; |
9904 | Protocol *p= thd->protocol; |
9905 | List<Item> fields; |
9906 | LEX_CSTRING trg_sql_mode_str, trg_body; |
9907 | LEX_CSTRING trg_sql_original_stmt; |
9908 | LEX_STRING trg_definer; |
9909 | CHARSET_INFO *trg_client_cs; |
9910 | MEM_ROOT *mem_root= thd->mem_root; |
9911 | char definer_holder[USER_HOST_BUFF_SIZE]; |
9912 | trg_definer.str= definer_holder; |
9913 | |
9914 | /* |
9915 | TODO: Check privileges here. This functionality will be added by |
9916 | implementation of the following WL items: |
9917 | - WL#2227: New privileges for new objects |
9918 | - WL#3482: Protect SHOW CREATE PROCEDURE | FUNCTION | VIEW | TRIGGER |
9919 | properly |
9920 | |
9921 | SHOW TRIGGERS and I_S.TRIGGERS will be affected too. |
9922 | */ |
9923 | |
9924 | /* Prepare trigger "object". */ |
9925 | |
9926 | trigger->get_trigger_info(&trg_sql_original_stmt, &trg_body, &trg_definer); |
9927 | sql_mode_string_representation(thd, trigger->sql_mode, &trg_sql_mode_str); |
9928 | |
9929 | /* Resolve trigger client character set. */ |
9930 | |
9931 | if (resolve_charset(trigger->client_cs_name.str, NULL, &trg_client_cs)) |
9932 | return TRUE; |
9933 | |
9934 | /* Send header. */ |
9935 | |
9936 | fields.push_back(new (mem_root) Item_empty_string(thd, "Trigger" , NAME_LEN), |
9937 | mem_root); |
9938 | fields.push_back(new (mem_root) |
9939 | Item_empty_string(thd, "sql_mode" , (uint)trg_sql_mode_str.length), |
9940 | mem_root); |
9941 | |
9942 | { |
9943 | /* |
9944 | NOTE: SQL statement field must be not less than 1024 in order not to |
9945 | confuse old clients. |
9946 | */ |
9947 | |
9948 | Item_empty_string *stmt_fld= |
9949 | new (mem_root) Item_empty_string(thd, "SQL Original Statement" , |
9950 | (uint)MY_MAX(trg_sql_original_stmt.length, |
9951 | 1024)); |
9952 | |
9953 | stmt_fld->maybe_null= TRUE; |
9954 | |
9955 | fields.push_back(stmt_fld, mem_root); |
9956 | } |
9957 | |
9958 | fields.push_back(new (mem_root) |
9959 | Item_empty_string(thd, "character_set_client" , |
9960 | MY_CS_NAME_SIZE), |
9961 | mem_root); |
9962 | |
9963 | fields.push_back(new (mem_root) |
9964 | Item_empty_string(thd, "collation_connection" , |
9965 | MY_CS_NAME_SIZE), |
9966 | mem_root); |
9967 | |
9968 | fields.push_back(new (mem_root) |
9969 | Item_empty_string(thd, "Database Collation" , |
9970 | MY_CS_NAME_SIZE), |
9971 | mem_root); |
9972 | |
9973 | Item_datetime_literal *tmp= (new (mem_root) |
9974 | Item_datetime_literal(thd, &zero_time, 2)); |
9975 | tmp->set_name(thd, STRING_WITH_LEN("Created" ), system_charset_info); |
9976 | fields.push_back(tmp, mem_root); |
9977 | |
9978 | if (p->send_result_set_metadata(&fields, |
9979 | Protocol::SEND_NUM_ROWS | |
9980 | Protocol::SEND_EOF)) |
9981 | return TRUE; |
9982 | |
9983 | /* Send data. */ |
9984 | |
9985 | p->prepare_for_resend(); |
9986 | |
9987 | p->store(trigger->name.str, |
9988 | trigger->name.length, |
9989 | system_charset_info); |
9990 | |
9991 | p->store(trg_sql_mode_str.str, |
9992 | trg_sql_mode_str.length, |
9993 | system_charset_info); |
9994 | |
9995 | p->store(trg_sql_original_stmt.str, |
9996 | trg_sql_original_stmt.length, |
9997 | trg_client_cs); |
9998 | |
9999 | p->store(trigger->client_cs_name.str, |
10000 | trigger->client_cs_name.length, |
10001 | system_charset_info); |
10002 | |
10003 | p->store(trigger->connection_cl_name.str, |
10004 | trigger->connection_cl_name.length, |
10005 | system_charset_info); |
10006 | |
10007 | p->store(trigger->db_cl_name.str, |
10008 | trigger->db_cl_name.length, |
10009 | system_charset_info); |
10010 | |
10011 | if (trigger->create_time) |
10012 | { |
10013 | MYSQL_TIME timestamp; |
10014 | thd->variables.time_zone->gmt_sec_to_TIME(×tamp, |
10015 | (my_time_t)(trigger->create_time/100)); |
10016 | timestamp.second_part= (trigger->create_time % 100) * 10000; |
10017 | p->store(×tamp, 2); |
10018 | } |
10019 | else |
10020 | p->store_null(); |
10021 | |
10022 | |
10023 | ret_code= p->write(); |
10024 | |
10025 | if (!ret_code) |
10026 | my_eof(thd); |
10027 | |
10028 | return ret_code != 0; |
10029 | } |
10030 | |
10031 | |
10032 | /** |
10033 | Read TRN and TRG files to obtain base table name for the specified |
10034 | trigger name and construct TABE_LIST object for the base table. |
10035 | |
10036 | @param thd Thread context. |
10037 | @param trg_name Trigger name. |
10038 | |
10039 | @return TABLE_LIST object corresponding to the base table. |
10040 | |
10041 | TODO: This function is a copy&paste from add_table_to_list() and |
10042 | sp_add_to_query_tables(). The problem is that in order to be compatible |
10043 | with Stored Programs (Prepared Statements), we should not touch thd->lex. |
10044 | The "source" functions also add created TABLE_LIST object to the |
10045 | thd->lex->query_tables. |
10046 | |
10047 | The plan to eliminate this copy&paste is to: |
10048 | |
10049 | - get rid of sp_add_to_query_tables() and use Lex::add_table_to_list(). |
10050 | Only add_table_to_list() must be used to add tables from the parser |
10051 | into Lex::query_tables list. |
10052 | |
10053 | - do not update Lex::query_tables in add_table_to_list(). |
10054 | */ |
10055 | |
10056 | static |
10057 | TABLE_LIST *get_trigger_table(THD *thd, const sp_name *trg_name) |
10058 | { |
10059 | char trn_path_buff[FN_REFLEN]; |
10060 | LEX_CSTRING trn_path= { trn_path_buff, 0 }; |
10061 | LEX_CSTRING db; |
10062 | LEX_CSTRING tbl_name; |
10063 | TABLE_LIST *table; |
10064 | |
10065 | build_trn_path(thd, trg_name, (LEX_STRING*) &trn_path); |
10066 | |
10067 | if (check_trn_exists(&trn_path)) |
10068 | { |
10069 | my_error(ER_TRG_DOES_NOT_EXIST, MYF(0)); |
10070 | return NULL; |
10071 | } |
10072 | |
10073 | if (load_table_name_for_trigger(thd, trg_name, &trn_path, &tbl_name)) |
10074 | return NULL; |
10075 | |
10076 | /* We need to reset statement table list to be PS/SP friendly. */ |
10077 | if (!(table= (TABLE_LIST*) thd->alloc(sizeof(TABLE_LIST)))) |
10078 | return NULL; |
10079 | |
10080 | db= trg_name->m_db; |
10081 | |
10082 | db.str= thd->strmake(db.str, db.length); |
10083 | if (lower_case_table_names) |
10084 | db.length= my_casedn_str(files_charset_info, (char*) db.str); |
10085 | |
10086 | tbl_name.str= thd->strmake(tbl_name.str, tbl_name.length); |
10087 | |
10088 | if (db.str == NULL || tbl_name.str == NULL) |
10089 | return NULL; |
10090 | |
10091 | table->init_one_table(&db, &tbl_name, 0, TL_IGNORE); |
10092 | |
10093 | return table; |
10094 | } |
10095 | |
10096 | |
10097 | /** |
10098 | SHOW CREATE TRIGGER high-level implementation. |
10099 | |
10100 | @param thd Thread context. |
10101 | @param trg_name Trigger name. |
10102 | |
10103 | @return Operation status |
10104 | @retval TRUE Error. |
10105 | @retval FALSE Success. |
10106 | */ |
10107 | |
10108 | bool show_create_trigger(THD *thd, const sp_name *trg_name) |
10109 | { |
10110 | TABLE_LIST *lst= get_trigger_table(thd, trg_name); |
10111 | uint num_tables; /* NOTE: unused, only to pass to open_tables(). */ |
10112 | Table_triggers_list *triggers; |
10113 | Trigger *trigger; |
10114 | bool error= TRUE; |
10115 | |
10116 | if (!lst) |
10117 | return TRUE; |
10118 | |
10119 | if (check_table_access(thd, TRIGGER_ACL, lst, FALSE, 1, TRUE)) |
10120 | { |
10121 | my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "TRIGGER" ); |
10122 | return TRUE; |
10123 | } |
10124 | |
10125 | /* |
10126 | Metadata locks taken during SHOW CREATE TRIGGER should be released when |
10127 | the statement completes as it is an information statement. |
10128 | */ |
10129 | MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint(); |
10130 | |
10131 | /* |
10132 | Open the table by name in order to load Table_triggers_list object. |
10133 | */ |
10134 | if (open_tables(thd, &lst, &num_tables, |
10135 | MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL)) |
10136 | { |
10137 | my_error(ER_TRG_CANT_OPEN_TABLE, MYF(0), |
10138 | (const char *) trg_name->m_db.str, |
10139 | (const char *) lst->table_name.str); |
10140 | |
10141 | goto exit; |
10142 | |
10143 | /* Perform closing actions and return error status. */ |
10144 | } |
10145 | |
10146 | triggers= lst->table->triggers; |
10147 | |
10148 | if (!triggers) |
10149 | { |
10150 | my_error(ER_TRG_DOES_NOT_EXIST, MYF(0)); |
10151 | goto exit; |
10152 | } |
10153 | |
10154 | trigger= triggers->find_trigger(&trg_name->m_name, 0); |
10155 | |
10156 | if (!trigger) |
10157 | { |
10158 | my_error(ER_TRG_CORRUPTED_FILE, MYF(0), |
10159 | (const char *) trg_name->m_db.str, |
10160 | (const char *) lst->table_name.str); |
10161 | goto exit; |
10162 | } |
10163 | |
10164 | error= show_create_trigger_impl(thd, trigger); |
10165 | |
10166 | /* |
10167 | NOTE: if show_create_trigger_impl() failed, that means we could not |
10168 | send data to the client. In this case we simply raise the error |
10169 | status and client connection will be closed. |
10170 | */ |
10171 | |
10172 | exit: |
10173 | close_thread_tables(thd); |
10174 | /* Release any metadata locks taken during SHOW CREATE TRIGGER. */ |
10175 | thd->mdl_context.rollback_to_savepoint(mdl_savepoint); |
10176 | return error; |
10177 | } |
10178 | |
10179 | class IS_internal_schema_access : public ACL_internal_schema_access |
10180 | { |
10181 | public: |
10182 | IS_internal_schema_access() |
10183 | {} |
10184 | |
10185 | ~IS_internal_schema_access() |
10186 | {} |
10187 | |
10188 | ACL_internal_access_result check(ulong want_access, |
10189 | ulong *save_priv) const; |
10190 | |
10191 | const ACL_internal_table_access *lookup(const char *name) const; |
10192 | }; |
10193 | |
10194 | ACL_internal_access_result |
10195 | IS_internal_schema_access::check(ulong want_access, |
10196 | ulong *save_priv) const |
10197 | { |
10198 | want_access &= ~SELECT_ACL; |
10199 | |
10200 | /* |
10201 | We don't allow any simple privileges but SELECT_ACL on |
10202 | the information_schema database. |
10203 | */ |
10204 | if (unlikely(want_access & DB_ACLS)) |
10205 | return ACL_INTERNAL_ACCESS_DENIED; |
10206 | |
10207 | /* Always grant SELECT for the information schema. */ |
10208 | *save_priv|= SELECT_ACL; |
10209 | |
10210 | return want_access ? ACL_INTERNAL_ACCESS_CHECK_GRANT : |
10211 | ACL_INTERNAL_ACCESS_GRANTED; |
10212 | } |
10213 | |
10214 | const ACL_internal_table_access * |
10215 | IS_internal_schema_access::lookup(const char *name) const |
10216 | { |
10217 | /* There are no per table rules for the information schema. */ |
10218 | return NULL; |
10219 | } |
10220 | |
10221 | static IS_internal_schema_access is_internal_schema_access; |
10222 | |
10223 | void initialize_information_schema_acl() |
10224 | { |
10225 | ACL_internal_schema_registry::register_schema(&INFORMATION_SCHEMA_NAME, |
10226 | &is_internal_schema_access); |
10227 | } |
10228 | |
10229 | #ifdef WITH_PARTITION_STORAGE_ENGINE |
10230 | /* |
10231 | Convert a string in character set in column character set format |
10232 | to utf8 character set if possible, the utf8 character set string |
10233 | will later possibly be converted to character set used by client. |
10234 | Thus we attempt conversion from column character set to both |
10235 | utf8 and to character set client. |
10236 | |
10237 | Examples of strings that should fail conversion to utf8 are unassigned |
10238 | characters as e.g. 0x81 in cp1250 (Windows character set for for countries |
10239 | like Czech and Poland). Example of string that should fail conversion to |
10240 | character set on client (e.g. if this is latin1) is 0x2020 (daggger) in |
10241 | ucs2. |
10242 | |
10243 | If the conversion fails we will as a fall back convert the string to |
10244 | hex encoded format. The caller of the function can also ask for hex |
10245 | encoded format of output string unconditionally. |
10246 | |
10247 | SYNOPSIS |
10248 | get_cs_converted_string_value() |
10249 | thd Thread object |
10250 | input_str Input string in cs character set |
10251 | output_str Output string to be produced in utf8 |
10252 | cs Character set of input string |
10253 | use_hex Use hex string unconditionally |
10254 | |
10255 | |
10256 | RETURN VALUES |
10257 | No return value |
10258 | */ |
10259 | |
10260 | static void get_cs_converted_string_value(THD *thd, |
10261 | String *input_str, |
10262 | String *output_str, |
10263 | CHARSET_INFO *cs, |
10264 | bool use_hex) |
10265 | { |
10266 | |
10267 | output_str->length(0); |
10268 | if (input_str->length() == 0) |
10269 | { |
10270 | output_str->append("''" ); |
10271 | return; |
10272 | } |
10273 | if (!use_hex) |
10274 | { |
10275 | String try_val; |
10276 | uint try_conv_error= 0; |
10277 | |
10278 | try_val.copy(input_str->ptr(), input_str->length(), cs, |
10279 | thd->variables.character_set_client, &try_conv_error); |
10280 | if (likely(!try_conv_error)) |
10281 | { |
10282 | String val; |
10283 | uint conv_error= 0; |
10284 | |
10285 | val.copy(input_str->ptr(), input_str->length(), cs, |
10286 | system_charset_info, &conv_error); |
10287 | if (likely(!conv_error)) |
10288 | { |
10289 | append_unescaped(output_str, val.ptr(), val.length()); |
10290 | return; |
10291 | } |
10292 | } |
10293 | /* We had a conversion error, use hex encoded string for safety */ |
10294 | } |
10295 | { |
10296 | const uchar *ptr; |
10297 | uint i, len; |
10298 | char buf[3]; |
10299 | |
10300 | output_str->append("_" ); |
10301 | output_str->append(cs->csname); |
10302 | output_str->append(" " ); |
10303 | output_str->append("0x" ); |
10304 | len= input_str->length(); |
10305 | ptr= (uchar*)input_str->ptr(); |
10306 | for (i= 0; i < len; i++) |
10307 | { |
10308 | uint high, low; |
10309 | |
10310 | high= (*ptr) >> 4; |
10311 | low= (*ptr) & 0x0F; |
10312 | buf[0]= _dig_vec_upper[high]; |
10313 | buf[1]= _dig_vec_upper[low]; |
10314 | buf[2]= 0; |
10315 | output_str->append((const char*)buf); |
10316 | ptr++; |
10317 | } |
10318 | } |
10319 | return; |
10320 | } |
10321 | #endif |
10322 | |