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
25struct st_find_field
26{
27 const char *table_name, *field_name;
28 Field *field;
29};
30
31/* Used fields */
32
33static 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
52enum 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
87static 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
142void 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
192int 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
237int 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
289int 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
369int 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
410void 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
459int 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
508int send_header_2(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
541extern "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
565int 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
611SQL_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
649SQL_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
680static 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
851error:
852 close_system_tables(thd, &open_tables_state_backup);
853
854error2:
855 DBUG_RETURN(TRUE);
856}
857
858
859bool 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