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-1335  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 
init_fields(THD * thd,TABLE_LIST * tables,struct st_find_field * find_fields,uint count)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 
memorize_variant_topic(THD * thd,TABLE * topics,int count,struct st_find_field * find_fields,List<String> * names,String * name,String * description,String * example)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 
search_topics(THD * thd,TABLE * topics,struct st_find_field * find_fields,SQL_SELECT * select,List<String> * names,String * name,String * description,String * example)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 
search_keyword(THD * thd,TABLE * keywords,struct st_find_field * find_fields,SQL_SELECT * select,int * key_id)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 
get_topics_for_keyword(THD * thd,TABLE * topics,TABLE * relations,struct st_find_field * find_fields,int16 key_id,List<String> * names,String * name,String * description,String * example)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 
search_categories(THD * thd,TABLE * categories,struct st_find_field * find_fields,SQL_SELECT * select,List<String> * names,int16 * res_id)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 
get_all_items_for_category(THD * thd,TABLE * items,Field * pfname,SQL_SELECT * select,List<String> * res)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 
send_answer_1(Protocol * protocol,String * s1,String * s2,String * s3)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 
send_header_2(Protocol * protocol,bool for_category)508 int 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 
string_ptr_cmp(const void * ptr1,const void * ptr2)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 
send_variant_2_list(MEM_ROOT * mem_root,Protocol * protocol,List<String> * names,const char * cat,String * source_name)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 
prepare_simple_select(THD * thd,Item * cond,TABLE * table,int * error)611 SQL_SELECT *prepare_simple_select(THD *thd, Item *cond,
612 				  TABLE *table, int *error)
613 {
614   cond->fix_fields_if_needed(thd, &cond);  // can never fail
615 
616   /* Assume that no indexes cover all required fields */
617   table->covering_keys.clear_all();
618 
619   SQL_SELECT *res= make_select(table, 0, 0, cond, 0, 0, error);
620   if (unlikely(*error) ||
621       (likely(res) && unlikely(res->check_quick(thd, 0, HA_POS_ERROR))) ||
622       (likely(res) && res->quick && unlikely(res->quick->reset())))
623   {
624     delete res;
625     res=0;
626   }
627   return res;
628 }
629 
630 /*
631   Prepare simple SQL_SELECT table.* WHERE table.name LIKE mask
632 
633   SYNOPSIS
634     prepare_select_for_name()
635     thd      Thread handler
636     mask     mask for compare with name
637     mlen     length of mask
638     tables   list of tables, used in WHERE
639     table    goal table
640     pfname   field "name" in table
641 
642     error    code of error (out)
643 
644   RETURN VALUES
645     #  created SQL_SELECT
646 */
647 
prepare_select_for_name(THD * thd,const char * mask,size_t mlen,TABLE_LIST * tables,TABLE * table,Field * pfname,int * error)648 SQL_SELECT *prepare_select_for_name(THD *thd, const char *mask, size_t mlen,
649 				    TABLE_LIST *tables, TABLE *table,
650 				    Field *pfname, int *error)
651 {
652   MEM_ROOT *mem_root= thd->mem_root;
653   Item *cond= new (mem_root)
654     Item_func_like(thd,
655                    new (mem_root)
656                    Item_field(thd, pfname),
657                    new (mem_root) Item_string(thd, mask, (uint)mlen,
658                                               pfname->charset()),
659                    new (mem_root) Item_string_ascii(thd, "\\"),
660                    FALSE);
661   if (unlikely(thd->is_fatal_error))
662     return 0;					// OOM
663   return prepare_simple_select(thd, cond, table, error);
664 }
665 
666 
667 /*
668   Server-side function 'help'
669 
670   SYNOPSIS
671     mysqld_help()
672     thd			Thread handler
673 
674   RETURN VALUES
675     FALSE Success
676     TRUE  Error and send_error already commited
677 */
678 
mysqld_help_internal(THD * thd,const char * mask)679 static bool mysqld_help_internal(THD *thd, const char *mask)
680 {
681   Protocol *protocol= thd->protocol;
682   SQL_SELECT *select;
683   st_find_field used_fields[array_elements(init_used_fields)];
684   List<TABLE_LIST> leaves;
685   TABLE_LIST tables[4];
686   List<String> topics_list, categories_list, subcategories_list;
687   String name, description, example;
688   int count_topics, count_categories, error;
689   size_t mlen= strlen(mask);
690   size_t i;
691   MEM_ROOT *mem_root= thd->mem_root;
692   LEX_CSTRING MYSQL_HELP_TOPIC_NAME=    {STRING_WITH_LEN("help_topic") };
693   LEX_CSTRING MYSQL_HELP_CATEGORY_NAME= {STRING_WITH_LEN("help_category") };
694   LEX_CSTRING MYSQL_HELP_RELATION_NAME= {STRING_WITH_LEN("help_relation") };
695   LEX_CSTRING MYSQL_HELP_KEYWORD_NAME=  {STRING_WITH_LEN("help_keyword") };
696   DBUG_ENTER("mysqld_help");
697 
698   tables[0].init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_HELP_TOPIC_NAME, 0, TL_READ);
699   tables[1].init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_HELP_CATEGORY_NAME, 0, TL_READ);
700   tables[2].init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_HELP_RELATION_NAME, 0, TL_READ);
701   tables[3].init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_HELP_KEYWORD_NAME, 0, TL_READ);
702   tables[0].next_global= tables[0].next_local=
703     tables[0].next_name_resolution_table= &tables[1];
704   tables[1].next_global= tables[1].next_local=
705     tables[1].next_name_resolution_table= &tables[2];
706   tables[2].next_global= tables[2].next_local=
707     tables[2].next_name_resolution_table= &tables[3];
708 
709   /*
710     HELP must be available under LOCK TABLES.
711     Reset and backup the current open tables state to
712     make it possible.
713   */
714   Open_tables_backup open_tables_state_backup;
715   if (open_system_tables_for_read(thd, tables, &open_tables_state_backup))
716     goto error2;
717 
718   /*
719     Init tables and fields to be usable from items
720     tables do not contain VIEWs => we can pass 0 as conds
721   */
722   thd->lex->select_lex.context.table_list=
723     thd->lex->select_lex.context.first_name_resolution_table= &tables[0];
724   if (setup_tables(thd, &thd->lex->select_lex.context,
725                    &thd->lex->select_lex.top_join_list,
726                    tables, leaves, FALSE, FALSE))
727     goto error;
728   memcpy((char*) used_fields, (char*) init_used_fields, sizeof(used_fields));
729   if (init_fields(thd, tables, used_fields, array_elements(used_fields)))
730     goto error;
731   for (i=0; i<sizeof(tables)/sizeof(TABLE_LIST); i++)
732     tables[i].table->file->init_table_handle_for_HANDLER();
733 
734   if (!(select=
735 	prepare_select_for_name(thd,mask,mlen,tables,tables[0].table,
736 				used_fields[help_topic_name].field,&error)))
737     goto error;
738 
739   count_topics= search_topics(thd,tables[0].table,used_fields,
740 			      select,&topics_list,
741 			      &name, &description, &example);
742   delete select;
743 
744   if (thd->is_error())
745     goto error;
746 
747   if (count_topics == 0)
748   {
749     int UNINIT_VAR(key_id);
750     if (!(select=
751           prepare_select_for_name(thd,mask,mlen,tables,tables[3].table,
752                                   used_fields[help_keyword_name].field,
753                                   &error)))
754       goto error;
755 
756     count_topics= search_keyword(thd,tables[3].table, used_fields, select,
757                                  &key_id);
758     delete select;
759     count_topics= (count_topics != 1) ? 0 :
760                   get_topics_for_keyword(thd,tables[0].table,tables[2].table,
761                                          used_fields,key_id,&topics_list,&name,
762                                          &description,&example);
763   }
764 
765   if (count_topics == 0)
766   {
767     int16 category_id;
768     Field *cat_cat_id= used_fields[help_category_parent_category_id].field;
769     if (!(select=
770           prepare_select_for_name(thd,mask,mlen,tables,tables[1].table,
771                                   used_fields[help_category_name].field,
772                                   &error)))
773       goto error;
774 
775     count_categories= search_categories(thd, tables[1].table, used_fields,
776 					select,
777 					&categories_list,&category_id);
778     delete select;
779     if (!count_categories)
780     {
781       if (send_header_2(protocol,FALSE))
782 	goto error;
783     }
784     else if (count_categories > 1)
785     {
786       if (send_header_2(protocol,FALSE) ||
787 	  send_variant_2_list(mem_root,protocol,&categories_list,"Y",0))
788 	goto error;
789     }
790     else
791     {
792       Field *topic_cat_id= used_fields[help_topic_help_category_id].field;
793       Item *cond_topic_by_cat=
794         new (mem_root)
795         Item_func_equal(thd,
796                         new (mem_root)
797                         Item_field(thd, topic_cat_id),
798                         new (mem_root)
799                         Item_int(thd, (int32) category_id));
800       Item *cond_cat_by_cat=
801         new (mem_root)
802         Item_func_equal(thd,
803                         new (mem_root) Item_field(thd, cat_cat_id),
804                         new (mem_root) Item_int(thd, (int32) category_id));
805       if (!(select= prepare_simple_select(thd, cond_topic_by_cat,
806                                           tables[0].table, &error)))
807         goto error;
808       get_all_items_for_category(thd,tables[0].table,
809 				 used_fields[help_topic_name].field,
810 				 select,&topics_list);
811       delete select;
812       if (!(select= prepare_simple_select(thd, cond_cat_by_cat,
813                                           tables[1].table, &error)))
814         goto error;
815       get_all_items_for_category(thd,tables[1].table,
816 				 used_fields[help_category_name].field,
817 				 select,&subcategories_list);
818       delete select;
819       String *cat= categories_list.head();
820       if (send_header_2(protocol, TRUE) ||
821 	  send_variant_2_list(mem_root,protocol,&topics_list,       "N",cat) ||
822 	  send_variant_2_list(mem_root,protocol,&subcategories_list,"Y",cat))
823 	goto error;
824     }
825   }
826   else if (count_topics == 1)
827   {
828     if (send_answer_1(protocol,&name,&description,&example))
829       goto error;
830   }
831   else
832   {
833     /* First send header and functions */
834     if (send_header_2(protocol, FALSE) ||
835 	send_variant_2_list(mem_root,protocol, &topics_list, "N", 0))
836       goto error;
837     if (!(select=
838           prepare_select_for_name(thd,mask,mlen,tables,tables[1].table,
839                                   used_fields[help_category_name].field,&error)))
840       goto error;
841     search_categories(thd, tables[1].table, used_fields,
842 		      select,&categories_list, 0);
843     delete select;
844     /* Then send categories */
845     if (send_variant_2_list(mem_root,protocol, &categories_list, "Y", 0))
846       goto error;
847   }
848   my_eof(thd);
849 
850   close_system_tables(thd, &open_tables_state_backup);
851   DBUG_RETURN(FALSE);
852 
853 error:
854   close_system_tables(thd, &open_tables_state_backup);
855 
856 error2:
857   DBUG_RETURN(TRUE);
858 }
859 
860 
mysqld_help(THD * thd,const char * mask)861 bool mysqld_help(THD *thd, const char *mask)
862 {
863   sql_mode_t sql_mode_backup= thd->variables.sql_mode;
864   thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH;
865   bool rc= mysqld_help_internal(thd, mask);
866   thd->variables.sql_mode= sql_mode_backup;
867   return rc;
868 }
869