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. |
---|