1 | /* Copyright (c) 2002, 2012, Oracle and/or its affiliates. |
2 | |
3 | This program is free software; you can redistribute it and/or modify |
4 | it under the terms of the GNU General Public License as published by |
5 | the Free Software Foundation; version 2 of the License. |
6 | |
7 | This program is distributed in the hope that it will be useful, |
8 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
9 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
10 | GNU General Public License for more details. |
11 | |
12 | You should have received a copy of the GNU General Public License |
13 | along with this program; if not, write to the Free Software |
14 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ |
15 | |
16 | #include "mariadb.h" |
17 | #include "sql_priv.h" |
18 | #include "unireg.h" |
19 | #include "sql_help.h" |
20 | #include "sql_table.h" // primary_key_name |
21 | #include "sql_base.h" // REPORT_ALL_ERRORS, setup_tables |
22 | #include "opt_range.h" // SQL_SELECT |
23 | #include "records.h" // init_read_record, end_read_record |
24 | |
25 | struct st_find_field |
26 | { |
27 | const char *table_name, *field_name; |
28 | Field *field; |
29 | }; |
30 | |
31 | /* Used fields */ |
32 | |
33 | static struct st_find_field init_used_fields[]= |
34 | { |
35 | { "help_topic" , "help_topic_id" , 0}, |
36 | { "help_topic" , "name" , 0}, |
37 | { "help_topic" , "help_category_id" , 0}, |
38 | { "help_topic" , "description" , 0}, |
39 | { "help_topic" , "example" , 0}, |
40 | |
41 | { "help_category" , "help_category_id" , 0}, |
42 | { "help_category" , "parent_category_id" , 0}, |
43 | { "help_category" , "name" , 0}, |
44 | |
45 | { "help_keyword" , "help_keyword_id" , 0}, |
46 | { "help_keyword" , "name" , 0}, |
47 | |
48 | { "help_relation" , "help_topic_id" , 0}, |
49 | { "help_relation" , "help_keyword_id" , 0} |
50 | }; |
51 | |
52 | enum enum_used_fields |
53 | { |
54 | help_topic_help_topic_id= 0, |
55 | help_topic_name, |
56 | help_topic_help_category_id, |
57 | help_topic_description, |
58 | help_topic_example, |
59 | |
60 | help_category_help_category_id, |
61 | help_category_parent_category_id, |
62 | help_category_name, |
63 | |
64 | help_keyword_help_keyword_id, |
65 | help_keyword_name, |
66 | |
67 | help_relation_help_topic_id, |
68 | help_relation_help_keyword_id |
69 | }; |
70 | |
71 | |
72 | /* |
73 | Fill st_find_field structure with pointers to fields |
74 | |
75 | SYNOPSIS |
76 | init_fields() |
77 | thd Thread handler |
78 | tables list of all tables for fields |
79 | find_fields array of structures |
80 | count size of previous array |
81 | |
82 | RETURN VALUES |
83 | 0 all ok |
84 | 1 one of the fileds was not found |
85 | */ |
86 | |
87 | static bool init_fields(THD *thd, TABLE_LIST *tables, |
88 | struct st_find_field *find_fields, uint count) |
89 | { |
90 | Name_resolution_context *context= &thd->lex->select_lex.context; |
91 | DBUG_ENTER("init_fields" ); |
92 | context->resolve_in_table_list_only(tables); |
93 | for (; count-- ; find_fields++) |
94 | { |
95 | LEX_CSTRING field_name= {find_fields->field_name, |
96 | strlen(find_fields->field_name) }; |
97 | /* We have to use 'new' here as field will be re_linked on free */ |
98 | Item_field *field= (new (thd->mem_root) |
99 | Item_field(thd, context, |
100 | "mysql" , |
101 | find_fields->table_name, |
102 | &field_name)); |
103 | if (!(find_fields->field= find_field_in_tables(thd, field, tables, NULL, |
104 | 0, REPORT_ALL_ERRORS, 1, |
105 | TRUE))) |
106 | DBUG_RETURN(1); |
107 | bitmap_set_bit(find_fields->field->table->read_set, |
108 | find_fields->field->field_index); |
109 | /* To make life easier when setting values in keys */ |
110 | bitmap_set_bit(find_fields->field->table->write_set, |
111 | find_fields->field->field_index); |
112 | } |
113 | DBUG_RETURN(0); |
114 | } |
115 | |
116 | |
117 | /* |
118 | Returns variants of found topic for help (if it is just single topic, |
119 | returns description and example, or else returns only names..) |
120 | |
121 | SYNOPSIS |
122 | memorize_variant_topic() |
123 | |
124 | thd Thread handler |
125 | topics Table of topics |
126 | count number of alredy found topics |
127 | find_fields Filled array of information for work with fields |
128 | |
129 | RETURN VALUES |
130 | names array of names of found topics (out) |
131 | |
132 | name name of found topic (out) |
133 | description description of found topic (out) |
134 | example example for found topic (out) |
135 | |
136 | NOTE |
137 | Field 'names' is set only if more than one topic is found. |
138 | Fields 'name', 'description', 'example' are set only if |
139 | found exactly one topic. |
140 | */ |
141 | |
142 | void memorize_variant_topic(THD *thd, TABLE *topics, int count, |
143 | struct st_find_field *find_fields, |
144 | List<String> *names, |
145 | String *name, String *description, String *example) |
146 | { |
147 | DBUG_ENTER("memorize_variant_topic" ); |
148 | MEM_ROOT *mem_root= thd->mem_root; |
149 | if (count==0) |
150 | { |
151 | get_field(mem_root,find_fields[help_topic_name].field, name); |
152 | get_field(mem_root,find_fields[help_topic_description].field, description); |
153 | get_field(mem_root,find_fields[help_topic_example].field, example); |
154 | } |
155 | else |
156 | { |
157 | if (count == 1) |
158 | names->push_back(name, thd->mem_root); |
159 | String *new_name= new (thd->mem_root) String; |
160 | get_field(mem_root,find_fields[help_topic_name].field,new_name); |
161 | names->push_back(new_name, thd->mem_root); |
162 | } |
163 | DBUG_VOID_RETURN; |
164 | } |
165 | |
166 | /* |
167 | Look for topics by mask |
168 | |
169 | SYNOPSIS |
170 | search_topics() |
171 | thd Thread handler |
172 | topics Table of topics |
173 | find_fields Filled array of info for fields |
174 | select Function to test for matching help topic. |
175 | Normally 'help_topic.name like 'bit%' |
176 | |
177 | RETURN VALUES |
178 | # number of topics found |
179 | |
180 | names array of names of found topics (out) |
181 | name name of found topic (out) |
182 | description description of found topic (out) |
183 | example example for found topic (out) |
184 | |
185 | NOTE |
186 | Field 'names' is set only if more than one topic was found. |
187 | Fields 'name', 'description', 'example' are set only if |
188 | exactly one topic was found. |
189 | |
190 | */ |
191 | |
192 | int search_topics(THD *thd, TABLE *topics, struct st_find_field *find_fields, |
193 | SQL_SELECT *select, List<String> *names, |
194 | String *name, String *description, String *example) |
195 | { |
196 | int count= 0; |
197 | READ_RECORD read_record_info; |
198 | DBUG_ENTER("search_topics" ); |
199 | |
200 | /* Should never happen. As this is part of help, we can ignore this */ |
201 | if (init_read_record(&read_record_info, thd, topics, select, NULL, 1, 0, |
202 | FALSE)) |
203 | DBUG_RETURN(0); |
204 | |
205 | while (!read_record_info.read_record()) |
206 | { |
207 | if (!select->cond->val_int()) // Doesn't match like |
208 | continue; |
209 | memorize_variant_topic(thd,topics,count,find_fields, |
210 | names,name,description,example); |
211 | count++; |
212 | } |
213 | end_read_record(&read_record_info); |
214 | |
215 | DBUG_RETURN(count); |
216 | } |
217 | |
218 | /* |
219 | Look for keyword by mask |
220 | |
221 | SYNOPSIS |
222 | search_keyword() |
223 | thd Thread handler |
224 | keywords Table of keywords |
225 | find_fields Filled array of info for fields |
226 | select Function to test for matching keyword. |
227 | Normally 'help_keyword.name like 'bit%' |
228 | |
229 | key_id help_keyword_if of found topics (out) |
230 | |
231 | RETURN VALUES |
232 | 0 didn't find any topics matching the mask |
233 | 1 found exactly one topic matching the mask |
234 | 2 found more then one topic matching the mask |
235 | */ |
236 | |
237 | int search_keyword(THD *thd, TABLE *keywords, |
238 | struct st_find_field *find_fields, |
239 | SQL_SELECT *select, int *key_id) |
240 | { |
241 | int count= 0; |
242 | READ_RECORD read_record_info; |
243 | DBUG_ENTER("search_keyword" ); |
244 | /* Should never happen. As this is part of help, we can ignore this */ |
245 | if (init_read_record(&read_record_info, thd, keywords, select, NULL, 1, 0, |
246 | FALSE)) |
247 | DBUG_RETURN(0); |
248 | |
249 | while (!read_record_info.read_record() && count<2) |
250 | { |
251 | if (!select->cond->val_int()) // Dosn't match like |
252 | continue; |
253 | |
254 | *key_id= (int)find_fields[help_keyword_help_keyword_id].field->val_int(); |
255 | |
256 | count++; |
257 | } |
258 | end_read_record(&read_record_info); |
259 | |
260 | DBUG_RETURN(count); |
261 | } |
262 | |
263 | /* |
264 | Look for all topics with keyword |
265 | |
266 | SYNOPSIS |
267 | get_topics_for_keyword() |
268 | thd Thread handler |
269 | topics Table of topics |
270 | relations Table of m:m relation "topic/keyword" |
271 | find_fields Filled array of info for fields |
272 | key_id Primary index to use to find for keyword |
273 | |
274 | RETURN VALUES |
275 | # number of topics found |
276 | |
277 | names array of name of found topics (out) |
278 | |
279 | name name of found topic (out) |
280 | description description of found topic (out) |
281 | example example for found topic (out) |
282 | |
283 | NOTE |
284 | Field 'names' is set only if more than one topic was found. |
285 | Fields 'name', 'description', 'example' are set only if |
286 | exactly one topic was found. |
287 | */ |
288 | |
289 | int get_topics_for_keyword(THD *thd, TABLE *topics, TABLE *relations, |
290 | struct st_find_field *find_fields, int16 key_id, |
291 | List<String> *names, |
292 | String *name, String *description, String *example) |
293 | { |
294 | uchar buff[8]; // Max int length |
295 | int count= 0; |
296 | int iindex_topic, iindex_relations; |
297 | Field *rtopic_id, *rkey_id; |
298 | DBUG_ENTER("get_topics_for_keyword" ); |
299 | |
300 | if ((iindex_topic= |
301 | find_type(primary_key_name, &topics->s->keynames, |
302 | FIND_TYPE_NO_PREFIX) - 1) < 0 || |
303 | (iindex_relations= |
304 | find_type(primary_key_name, &relations->s->keynames, |
305 | FIND_TYPE_NO_PREFIX) - 1) < 0) |
306 | { |
307 | my_message(ER_CORRUPT_HELP_DB, ER_THD(thd, ER_CORRUPT_HELP_DB), MYF(0)); |
308 | DBUG_RETURN(-1); |
309 | } |
310 | rtopic_id= find_fields[help_relation_help_topic_id].field; |
311 | rkey_id= find_fields[help_relation_help_keyword_id].field; |
312 | |
313 | if (topics->file->ha_index_init(iindex_topic,1) || |
314 | relations->file->ha_index_init(iindex_relations,1)) |
315 | { |
316 | if (topics->file->inited) |
317 | topics->file->ha_index_end(); |
318 | my_message(ER_CORRUPT_HELP_DB, ER_THD(thd, ER_CORRUPT_HELP_DB), MYF(0)); |
319 | DBUG_RETURN(-1); |
320 | } |
321 | |
322 | rkey_id->store((longlong) key_id, TRUE); |
323 | rkey_id->get_key_image(buff, rkey_id->pack_length(), Field::itRAW); |
324 | int key_res= relations->file->ha_index_read_map(relations->record[0], |
325 | buff, (key_part_map) 1, |
326 | HA_READ_KEY_EXACT); |
327 | |
328 | for ( ; |
329 | !key_res && key_id == (int16) rkey_id->val_int() ; |
330 | key_res= relations->file->ha_index_next(relations->record[0])) |
331 | { |
332 | uchar topic_id_buff[8]; |
333 | longlong topic_id= rtopic_id->val_int(); |
334 | Field *field= find_fields[help_topic_help_topic_id].field; |
335 | field->store((longlong) topic_id, TRUE); |
336 | field->get_key_image(topic_id_buff, field->pack_length(), Field::itRAW); |
337 | |
338 | if (!topics->file->ha_index_read_map(topics->record[0], topic_id_buff, |
339 | (key_part_map)1, HA_READ_KEY_EXACT)) |
340 | { |
341 | memorize_variant_topic(thd,topics,count,find_fields, |
342 | names,name,description,example); |
343 | count++; |
344 | } |
345 | } |
346 | topics->file->ha_index_end(); |
347 | relations->file->ha_index_end(); |
348 | DBUG_RETURN(count); |
349 | } |
350 | |
351 | /* |
352 | Look for categories by mask |
353 | |
354 | SYNOPSIS |
355 | search_categories() |
356 | thd THD for init_read_record |
357 | categories Table of categories |
358 | find_fields Filled array of info for fields |
359 | select Function to test for if matching help topic. |
360 | Normally 'help_vategory.name like 'bit%' |
361 | names List of found categories names (out) |
362 | res_id Primary index of found category (only if |
363 | found exactly one category) |
364 | |
365 | RETURN VALUES |
366 | # Number of categories found |
367 | */ |
368 | |
369 | int search_categories(THD *thd, TABLE *categories, |
370 | struct st_find_field *find_fields, |
371 | SQL_SELECT *select, List<String> *names, int16 *res_id) |
372 | { |
373 | Field *pfname= find_fields[help_category_name].field; |
374 | Field *pcat_id= find_fields[help_category_help_category_id].field; |
375 | int count= 0; |
376 | READ_RECORD read_record_info; |
377 | DBUG_ENTER("search_categories" ); |
378 | |
379 | /* Should never happen. As this is part of help, we can ignore this */ |
380 | if (init_read_record(&read_record_info, thd, categories, select, NULL, |
381 | 1, 0, FALSE)) |
382 | DBUG_RETURN(0); |
383 | while (!read_record_info.read_record()) |
384 | { |
385 | if (select && !select->cond->val_int()) |
386 | continue; |
387 | String *lname= new (thd->mem_root) String; |
388 | get_field(thd->mem_root,pfname,lname); |
389 | if (++count == 1 && res_id) |
390 | *res_id= (int16) pcat_id->val_int(); |
391 | names->push_back(lname, thd->mem_root); |
392 | } |
393 | end_read_record(&read_record_info); |
394 | |
395 | DBUG_RETURN(count); |
396 | } |
397 | |
398 | /* |
399 | Look for all topics or subcategories of category |
400 | |
401 | SYNOPSIS |
402 | get_all_items_for_category() |
403 | thd Thread handler |
404 | items Table of items |
405 | pfname Field "name" in items |
406 | select "where" part of query.. |
407 | res list of finded names |
408 | */ |
409 | |
410 | void get_all_items_for_category(THD *thd, TABLE *items, Field *pfname, |
411 | SQL_SELECT *select, List<String> *res) |
412 | { |
413 | READ_RECORD read_record_info; |
414 | DBUG_ENTER("get_all_items_for_category" ); |
415 | |
416 | /* Should never happen. As this is part of help, we can ignore this */ |
417 | if (init_read_record(&read_record_info, thd, items, select, NULL, 1, 0, |
418 | FALSE)) |
419 | DBUG_VOID_RETURN; |
420 | |
421 | while (!read_record_info.read_record()) |
422 | { |
423 | if (!select->cond->val_int()) |
424 | continue; |
425 | String *name= new (thd->mem_root) String(); |
426 | get_field(thd->mem_root,pfname,name); |
427 | res->push_back(name, thd->mem_root); |
428 | } |
429 | end_read_record(&read_record_info); |
430 | |
431 | DBUG_VOID_RETURN; |
432 | } |
433 | |
434 | /* |
435 | Send to client answer for help request |
436 | |
437 | SYNOPSIS |
438 | send_answer_1() |
439 | protocol - protocol for sending |
440 | s1 - value of column "Name" |
441 | s2 - value of column "Description" |
442 | s3 - value of column "Example" |
443 | |
444 | IMPLEMENTATION |
445 | Format used: |
446 | +----------+------------+------------+ |
447 | |name |description |example | |
448 | +----------+------------+------------+ |
449 | |String(64)|String(1000)|String(1000)| |
450 | +----------+------------+------------+ |
451 | with exactly one row! |
452 | |
453 | RETURN VALUES |
454 | 1 Writing of head failed |
455 | -1 Writing of row failed |
456 | 0 Successeful send |
457 | */ |
458 | |
459 | int send_answer_1(Protocol *protocol, String *s1, String *s2, String *s3) |
460 | { |
461 | THD *thd= protocol->thd; |
462 | MEM_ROOT *mem_root= thd->mem_root; |
463 | DBUG_ENTER("send_answer_1" ); |
464 | |
465 | List<Item> field_list; |
466 | field_list.push_back(new (mem_root) Item_empty_string(thd, "name" , 64), |
467 | mem_root); |
468 | field_list.push_back(new (mem_root) Item_empty_string(thd, "description" , 1000), |
469 | mem_root); |
470 | field_list.push_back(new (mem_root) Item_empty_string(thd, "example" , 1000), |
471 | mem_root); |
472 | |
473 | if (protocol->send_result_set_metadata(&field_list, |
474 | Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) |
475 | DBUG_RETURN(1); |
476 | |
477 | protocol->prepare_for_resend(); |
478 | protocol->store(s1); |
479 | protocol->store(s2); |
480 | protocol->store(s3); |
481 | if (protocol->write()) |
482 | DBUG_RETURN(-1); |
483 | DBUG_RETURN(0); |
484 | } |
485 | |
486 | |
487 | /* |
488 | Send to client help header |
489 | |
490 | SYNOPSIS |
491 | send_header_2() |
492 | protocol - protocol for sending |
493 | is_it_category - need column 'source_category_name' |
494 | |
495 | IMPLEMENTATION |
496 | +- -+ |
497 | |+-------------------- | +----------+--------------+ |
498 | ||source_category_name | |name |is_it_category| |
499 | |+-------------------- | +----------+--------------+ |
500 | ||String(64) | |String(64)|String(1) | |
501 | |+-------------------- | +----------+--------------+ |
502 | +- -+ |
503 | |
504 | RETURN VALUES |
505 | result of protocol->send_result_set_metadata |
506 | */ |
507 | |
508 | int (Protocol *protocol, bool for_category) |
509 | { |
510 | THD *thd= protocol->thd; |
511 | MEM_ROOT *mem_root= thd->mem_root; |
512 | DBUG_ENTER("send_header_2" ); |
513 | List<Item> field_list; |
514 | if (for_category) |
515 | field_list.push_back(new (mem_root) |
516 | Item_empty_string(thd, "source_category_name" , 64), |
517 | mem_root); |
518 | field_list.push_back(new (mem_root) |
519 | Item_empty_string(thd, "name" , 64), |
520 | mem_root); |
521 | field_list.push_back(new (mem_root) |
522 | Item_empty_string(thd, "is_it_category" , 1), |
523 | mem_root); |
524 | DBUG_RETURN(protocol->send_result_set_metadata(&field_list, |
525 | Protocol::SEND_NUM_ROWS | |
526 | Protocol::SEND_EOF)); |
527 | } |
528 | |
529 | /* |
530 | strcmp for using in qsort |
531 | |
532 | SYNOPSIS |
533 | strptrcmp() |
534 | ptr1 (const void*)&str1 |
535 | ptr2 (const void*)&str2 |
536 | |
537 | RETURN VALUES |
538 | same as strcmp |
539 | */ |
540 | |
541 | extern "C" int string_ptr_cmp(const void* ptr1, const void* ptr2) |
542 | { |
543 | String *str1= *(String**)ptr1; |
544 | String *str2= *(String**)ptr2; |
545 | return strcmp(str1->c_ptr(),str2->c_ptr()); |
546 | } |
547 | |
548 | /* |
549 | Send to client rows in format: |
550 | column1 : <name> |
551 | column2 : <is_it_category> |
552 | |
553 | SYNOPSIS |
554 | send_variant_2_list() |
555 | protocol Protocol for sending |
556 | names List of names |
557 | cat Value of the column <is_it_category> |
558 | source_name name of category for all items.. |
559 | |
560 | RETURN VALUES |
561 | -1 Writing fail |
562 | 0 Data was successefully send |
563 | */ |
564 | |
565 | int send_variant_2_list(MEM_ROOT *mem_root, Protocol *protocol, |
566 | List<String> *names, |
567 | const char *cat, String *source_name) |
568 | { |
569 | DBUG_ENTER("send_variant_2_list" ); |
570 | |
571 | String **pointers= (String**)alloc_root(mem_root, |
572 | sizeof(String*)*names->elements); |
573 | String **pos; |
574 | String **end= pointers + names->elements; |
575 | |
576 | List_iterator<String> it(*names); |
577 | for (pos= pointers; pos!=end; (*pos++= it++)) |
578 | ; |
579 | |
580 | my_qsort(pointers,names->elements,sizeof(String*),string_ptr_cmp); |
581 | |
582 | for (pos= pointers; pos!=end; pos++) |
583 | { |
584 | protocol->prepare_for_resend(); |
585 | if (source_name) |
586 | protocol->store(source_name); |
587 | protocol->store(*pos); |
588 | protocol->store(cat,1,&my_charset_latin1); |
589 | if (protocol->write()) |
590 | DBUG_RETURN(-1); |
591 | } |
592 | |
593 | DBUG_RETURN(0); |
594 | } |
595 | |
596 | /* |
597 | Prepare simple SQL_SELECT table.* WHERE <Item> |
598 | |
599 | SYNOPSIS |
600 | prepare_simple_select() |
601 | thd Thread handler |
602 | cond WHERE part of select |
603 | table goal table |
604 | |
605 | error code of error (out) |
606 | |
607 | RETURN VALUES |
608 | # created SQL_SELECT |
609 | */ |
610 | |
611 | SQL_SELECT *prepare_simple_select(THD *thd, Item *cond, |
612 | TABLE *table, int *error) |
613 | { |
614 | if (!cond->fixed) |
615 | cond->fix_fields(thd, &cond); // can never fail |
616 | |
617 | /* Assume that no indexes cover all required fields */ |
618 | table->covering_keys.clear_all(); |
619 | |
620 | SQL_SELECT *res= make_select(table, 0, 0, cond, 0, 0, error); |
621 | if (unlikely(*error) || |
622 | (likely(res) && unlikely(res->check_quick(thd, 0, HA_POS_ERROR))) || |
623 | (likely(res) && res->quick && unlikely(res->quick->reset()))) |
624 | { |
625 | delete res; |
626 | res=0; |
627 | } |
628 | return res; |
629 | } |
630 | |
631 | /* |
632 | Prepare simple SQL_SELECT table.* WHERE table.name LIKE mask |
633 | |
634 | SYNOPSIS |
635 | prepare_select_for_name() |
636 | thd Thread handler |
637 | mask mask for compare with name |
638 | mlen length of mask |
639 | tables list of tables, used in WHERE |
640 | table goal table |
641 | pfname field "name" in table |
642 | |
643 | error code of error (out) |
644 | |
645 | RETURN VALUES |
646 | # created SQL_SELECT |
647 | */ |
648 | |
649 | SQL_SELECT *prepare_select_for_name(THD *thd, const char *mask, size_t mlen, |
650 | TABLE_LIST *tables, TABLE *table, |
651 | Field *pfname, int *error) |
652 | { |
653 | MEM_ROOT *mem_root= thd->mem_root; |
654 | Item *cond= new (mem_root) |
655 | Item_func_like(thd, |
656 | new (mem_root) |
657 | Item_field(thd, pfname), |
658 | new (mem_root) Item_string(thd, mask, (uint)mlen, |
659 | pfname->charset()), |
660 | new (mem_root) Item_string_ascii(thd, "\\" ), |
661 | FALSE); |
662 | if (unlikely(thd->is_fatal_error)) |
663 | return 0; // OOM |
664 | return prepare_simple_select(thd, cond, table, error); |
665 | } |
666 | |
667 | |
668 | /* |
669 | Server-side function 'help' |
670 | |
671 | SYNOPSIS |
672 | mysqld_help() |
673 | thd Thread handler |
674 | |
675 | RETURN VALUES |
676 | FALSE Success |
677 | TRUE Error and send_error already commited |
678 | */ |
679 | |
680 | static bool mysqld_help_internal(THD *thd, const char *mask) |
681 | { |
682 | Protocol *protocol= thd->protocol; |
683 | SQL_SELECT *select; |
684 | st_find_field used_fields[array_elements(init_used_fields)]; |
685 | List<TABLE_LIST> leaves; |
686 | TABLE_LIST tables[4]; |
687 | List<String> topics_list, categories_list, subcategories_list; |
688 | String name, description, example; |
689 | int count_topics, count_categories, error; |
690 | size_t mlen= strlen(mask); |
691 | size_t i; |
692 | MEM_ROOT *mem_root= thd->mem_root; |
693 | LEX_CSTRING MYSQL_HELP_TOPIC_NAME= {STRING_WITH_LEN("help_topic" ) }; |
694 | LEX_CSTRING MYSQL_HELP_CATEGORY_NAME= {STRING_WITH_LEN("help_category" ) }; |
695 | LEX_CSTRING MYSQL_HELP_RELATION_NAME= {STRING_WITH_LEN("help_relation" ) }; |
696 | LEX_CSTRING MYSQL_HELP_KEYWORD_NAME= {STRING_WITH_LEN("help_keyword" ) }; |
697 | DBUG_ENTER("mysqld_help" ); |
698 | |
699 | tables[0].init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_HELP_TOPIC_NAME, 0, TL_READ); |
700 | tables[1].init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_HELP_CATEGORY_NAME, 0, TL_READ); |
701 | tables[2].init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_HELP_RELATION_NAME, 0, TL_READ); |
702 | tables[3].init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_HELP_KEYWORD_NAME, 0, TL_READ); |
703 | tables[0].next_global= tables[0].next_local= |
704 | tables[0].next_name_resolution_table= &tables[1]; |
705 | tables[1].next_global= tables[1].next_local= |
706 | tables[1].next_name_resolution_table= &tables[2]; |
707 | tables[2].next_global= tables[2].next_local= |
708 | tables[2].next_name_resolution_table= &tables[3]; |
709 | |
710 | /* |
711 | HELP must be available under LOCK TABLES. |
712 | Reset and backup the current open tables state to |
713 | make it possible. |
714 | */ |
715 | Open_tables_backup open_tables_state_backup; |
716 | if (open_system_tables_for_read(thd, tables, &open_tables_state_backup)) |
717 | goto error2; |
718 | |
719 | /* |
720 | Init tables and fields to be usable from items |
721 | tables do not contain VIEWs => we can pass 0 as conds |
722 | */ |
723 | thd->lex->select_lex.context.table_list= |
724 | thd->lex->select_lex.context.first_name_resolution_table= &tables[0]; |
725 | if (setup_tables(thd, &thd->lex->select_lex.context, |
726 | &thd->lex->select_lex.top_join_list, |
727 | tables, leaves, FALSE, FALSE)) |
728 | goto error; |
729 | memcpy((char*) used_fields, (char*) init_used_fields, sizeof(used_fields)); |
730 | if (init_fields(thd, tables, used_fields, array_elements(used_fields))) |
731 | goto error; |
732 | for (i=0; i<sizeof(tables)/sizeof(TABLE_LIST); i++) |
733 | tables[i].table->file->init_table_handle_for_HANDLER(); |
734 | |
735 | if (!(select= |
736 | prepare_select_for_name(thd,mask,mlen,tables,tables[0].table, |
737 | used_fields[help_topic_name].field,&error))) |
738 | goto error; |
739 | |
740 | count_topics= search_topics(thd,tables[0].table,used_fields, |
741 | select,&topics_list, |
742 | &name, &description, &example); |
743 | delete select; |
744 | |
745 | if (count_topics == 0) |
746 | { |
747 | int UNINIT_VAR(key_id); |
748 | if (!(select= |
749 | prepare_select_for_name(thd,mask,mlen,tables,tables[3].table, |
750 | used_fields[help_keyword_name].field, |
751 | &error))) |
752 | goto error; |
753 | |
754 | count_topics= search_keyword(thd,tables[3].table, used_fields, select, |
755 | &key_id); |
756 | delete select; |
757 | count_topics= (count_topics != 1) ? 0 : |
758 | get_topics_for_keyword(thd,tables[0].table,tables[2].table, |
759 | used_fields,key_id,&topics_list,&name, |
760 | &description,&example); |
761 | } |
762 | |
763 | if (count_topics == 0) |
764 | { |
765 | int16 category_id; |
766 | Field *cat_cat_id= used_fields[help_category_parent_category_id].field; |
767 | if (!(select= |
768 | prepare_select_for_name(thd,mask,mlen,tables,tables[1].table, |
769 | used_fields[help_category_name].field, |
770 | &error))) |
771 | goto error; |
772 | |
773 | count_categories= search_categories(thd, tables[1].table, used_fields, |
774 | select, |
775 | &categories_list,&category_id); |
776 | delete select; |
777 | if (!count_categories) |
778 | { |
779 | if (send_header_2(protocol,FALSE)) |
780 | goto error; |
781 | } |
782 | else if (count_categories > 1) |
783 | { |
784 | if (send_header_2(protocol,FALSE) || |
785 | send_variant_2_list(mem_root,protocol,&categories_list,"Y" ,0)) |
786 | goto error; |
787 | } |
788 | else |
789 | { |
790 | Field *topic_cat_id= used_fields[help_topic_help_category_id].field; |
791 | Item *cond_topic_by_cat= |
792 | new (mem_root) |
793 | Item_func_equal(thd, |
794 | new (mem_root) |
795 | Item_field(thd, topic_cat_id), |
796 | new (mem_root) |
797 | Item_int(thd, (int32) category_id)); |
798 | Item *cond_cat_by_cat= |
799 | new (mem_root) |
800 | Item_func_equal(thd, |
801 | new (mem_root) Item_field(thd, cat_cat_id), |
802 | new (mem_root) Item_int(thd, (int32) category_id)); |
803 | if (!(select= prepare_simple_select(thd, cond_topic_by_cat, |
804 | tables[0].table, &error))) |
805 | goto error; |
806 | get_all_items_for_category(thd,tables[0].table, |
807 | used_fields[help_topic_name].field, |
808 | select,&topics_list); |
809 | delete select; |
810 | if (!(select= prepare_simple_select(thd, cond_cat_by_cat, |
811 | tables[1].table, &error))) |
812 | goto error; |
813 | get_all_items_for_category(thd,tables[1].table, |
814 | used_fields[help_category_name].field, |
815 | select,&subcategories_list); |
816 | delete select; |
817 | String *cat= categories_list.head(); |
818 | if (send_header_2(protocol, TRUE) || |
819 | send_variant_2_list(mem_root,protocol,&topics_list, "N" ,cat) || |
820 | send_variant_2_list(mem_root,protocol,&subcategories_list,"Y" ,cat)) |
821 | goto error; |
822 | } |
823 | } |
824 | else if (count_topics == 1) |
825 | { |
826 | if (send_answer_1(protocol,&name,&description,&example)) |
827 | goto error; |
828 | } |
829 | else |
830 | { |
831 | /* First send header and functions */ |
832 | if (send_header_2(protocol, FALSE) || |
833 | send_variant_2_list(mem_root,protocol, &topics_list, "N" , 0)) |
834 | goto error; |
835 | if (!(select= |
836 | prepare_select_for_name(thd,mask,mlen,tables,tables[1].table, |
837 | used_fields[help_category_name].field,&error))) |
838 | goto error; |
839 | search_categories(thd, tables[1].table, used_fields, |
840 | select,&categories_list, 0); |
841 | delete select; |
842 | /* Then send categories */ |
843 | if (send_variant_2_list(mem_root,protocol, &categories_list, "Y" , 0)) |
844 | goto error; |
845 | } |
846 | my_eof(thd); |
847 | |
848 | close_system_tables(thd, &open_tables_state_backup); |
849 | DBUG_RETURN(FALSE); |
850 | |
851 | error: |
852 | close_system_tables(thd, &open_tables_state_backup); |
853 | |
854 | error2: |
855 | DBUG_RETURN(TRUE); |
856 | } |
857 | |
858 | |
859 | bool mysqld_help(THD *thd, const char *mask) |
860 | { |
861 | sql_mode_t sql_mode_backup= thd->variables.sql_mode; |
862 | thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH; |
863 | bool rc= mysqld_help_internal(thd, mask); |
864 | thd->variables.sql_mode= sql_mode_backup; |
865 | return rc; |
866 | } |
867 | |