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