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