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