sql_help.cpp
上传用户:romrleung
上传日期:2022-05-23
资源大小:18897k
文件大小:21k
源码类别:

MySQL数据库

开发平台:

Visual C++

  1. /* Copyright (C) 2000 MySQL AB
  2.    This program is free software; you can redistribute it and/or modify
  3.    it under the terms of the GNU General Public License as published by
  4.    the Free Software Foundation; either version 2 of the License, or
  5.    (at your option) any later version.
  6.    This program is distributed in the hope that it will be useful,
  7.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  8.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  9.    GNU General Public License for more details.
  10.    You should have received a copy of the GNU General Public License
  11.    along with this program; if not, write to the Free Software
  12.    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
  13. #include "mysql_priv.h"
  14. struct st_find_field
  15. {
  16.   const char *table_name, *field_name;
  17.   Field *field;
  18. };
  19. /* Used fields */
  20. static struct st_find_field init_used_fields[]=
  21. {
  22.   { "help_topic",    "help_topic_id",      0},
  23.   { "help_topic",    "name",               0},
  24.   { "help_topic",    "help_category_id",   0},
  25.   { "help_topic",    "description",        0},
  26.   { "help_topic",    "example",            0},
  27.   { "help_category", "help_category_id",   0},
  28.   { "help_category", "parent_category_id", 0},
  29.   { "help_category", "name",               0},
  30.   { "help_keyword",  "help_keyword_id",    0},
  31.   { "help_keyword",  "name",               0},
  32.   { "help_relation", "help_topic_id",      0},
  33.   { "help_relation", "help_keyword_id",    0}
  34. };
  35. enum enum_used_fields
  36. {
  37.   help_topic_help_topic_id= 0,
  38.   help_topic_name,
  39.   help_topic_help_category_id,
  40.   help_topic_description,
  41.   help_topic_example,
  42.   help_category_help_category_id,
  43.   help_category_parent_category_id,
  44.   help_category_name,
  45.   help_keyword_help_keyword_id,
  46.   help_keyword_name,
  47.   help_relation_help_topic_id,
  48.   help_relation_help_keyword_id
  49. };
  50. /*
  51.   Fill st_find_field structure with pointers to fields
  52.   SYNOPSIS
  53.     init_fields()
  54.     thd          Thread handler
  55.     tables       list of all tables for fields
  56.     find_fields  array of structures
  57.     count        size of previous array
  58.   RETURN VALUES
  59.     0           all ok
  60.     1           one of the fileds didn't finded
  61. */
  62. static bool init_fields(THD *thd, TABLE_LIST *tables,
  63. struct st_find_field *find_fields, uint count)
  64. {
  65.   DBUG_ENTER("init_fields");
  66.   for (; count-- ; find_fields++)
  67.   {
  68.     TABLE_LIST *not_used;
  69.     /* We have to use 'new' here as field will be re_linked on free */
  70.     Item_field *field= new Item_field("mysql", find_fields->table_name,
  71.                                       find_fields->field_name);
  72.     if (!(find_fields->field= find_field_in_tables(thd, field, tables,
  73.    &not_used, TRUE)))
  74.       DBUG_RETURN(1);
  75.   }
  76.   DBUG_RETURN(0);
  77. }
  78. /*
  79.   Returns variants of found topic for help (if it is just single topic,
  80.     returns description and example, or else returns only names..)
  81.   SYNOPSIS
  82.     memorize_variant_topic()
  83.     thd           Thread handler
  84.     topics        Table of topics
  85.     count         number of alredy found topics
  86.     find_fields   Filled array of information for work with fields
  87.   RETURN VALUES
  88.     names         array of names of found topics (out)
  89.     name          name of found topic (out)
  90.     description   description of found topic (out)
  91.     example       example for found topic (out)
  92.   NOTE
  93.     Field 'names' is set only if more than one topic is found.
  94.     Fields 'name', 'description', 'example' are set only if
  95.     found exactly one topic.
  96. */
  97. void memorize_variant_topic(THD *thd, TABLE *topics, int count,
  98.     struct st_find_field *find_fields,
  99.     List<String> *names,
  100.     String *name, String *description, String *example)
  101. {
  102.   DBUG_ENTER("memorize_variant_topic");
  103.   MEM_ROOT *mem_root= thd->mem_root;
  104.   if (count==0)
  105.   {
  106.     get_field(mem_root,find_fields[help_topic_name].field,        name);
  107.     get_field(mem_root,find_fields[help_topic_description].field, description);
  108.     get_field(mem_root,find_fields[help_topic_example].field,     example);
  109.   }
  110.   else
  111.   {
  112.     if (count == 1)
  113.       names->push_back(name);
  114.     String *new_name= new (thd->mem_root) String;
  115.     get_field(mem_root,find_fields[help_topic_name].field,new_name);
  116.     names->push_back(new_name);
  117.   }
  118.   DBUG_VOID_RETURN;
  119. }
  120. /*
  121.   Look for topics by mask
  122.   SYNOPSIS
  123.     search_topics()
  124.     thd   Thread handler
  125.     topics  Table of topics
  126.     find_fields  Filled array of info for fields
  127.     select  Function to test for matching help topic.
  128.  Normally 'help_topic.name like 'bit%'
  129.   RETURN VALUES
  130.     #   number of topics found
  131.     names        array of names of found topics (out)
  132.     name         name of found topic (out)
  133.     description  description of found topic (out)
  134.     example      example for found topic (out)
  135.   NOTE
  136.     Field 'names' is set only if more than one topic was found.
  137.     Fields 'name', 'description', 'example' are set only if
  138.     exactly one topic was found.
  139. */
  140. int search_topics(THD *thd, TABLE *topics, struct st_find_field *find_fields,
  141.   SQL_SELECT *select, List<String> *names,
  142.   String *name, String *description, String *example)
  143. {
  144.   DBUG_ENTER("search_topics");
  145.   int count= 0;
  146.   READ_RECORD read_record_info;
  147.   init_read_record(&read_record_info, thd, topics, select,1,0);
  148.   while (!read_record_info.read_record(&read_record_info))
  149.   {
  150.     if (!select->cond->val_int()) // Doesn't match like
  151.       continue;
  152.     memorize_variant_topic(thd,topics,count,find_fields,
  153.    names,name,description,example);
  154.     count++;
  155.   }
  156.   end_read_record(&read_record_info);
  157.   DBUG_RETURN(count);
  158. }
  159. /*
  160.   Look for keyword by mask
  161.   SYNOPSIS
  162.     search_keyword()
  163.     thd          Thread handler
  164.     keywords     Table of keywords
  165.     find_fields  Filled array of info for fields
  166.     select       Function to test for matching keyword.
  167.          Normally 'help_keyword.name like 'bit%'
  168.     key_id       help_keyword_if of found topics (out)
  169.   RETURN VALUES
  170.     0   didn't find any topics matching the mask
  171.     1   found exactly one topic matching the mask
  172.     2   found more then one topic matching the mask
  173. */
  174. int search_keyword(THD *thd, TABLE *keywords, struct st_find_field *find_fields,
  175.                    SQL_SELECT *select, int *key_id)
  176. {
  177.   DBUG_ENTER("search_keyword");
  178.   int count= 0;
  179.   READ_RECORD read_record_info;
  180.   init_read_record(&read_record_info, thd, keywords, select,1,0);
  181.   while (!read_record_info.read_record(&read_record_info) && count<2)
  182.   {
  183.     if (!select->cond->val_int()) // Dosn't match like
  184.       continue;
  185.     *key_id= (int)find_fields[help_keyword_help_keyword_id].field->val_int();
  186.     count++;
  187.   }
  188.   end_read_record(&read_record_info);
  189.   DBUG_RETURN(count);
  190. }
  191. /*
  192.   Look for all topics with keyword
  193.   SYNOPSIS
  194.     get_topics_for_keyword()
  195.     thd  Thread handler
  196.     topics  Table of topics
  197.     relations  Table of m:m relation "topic/keyword"
  198.     find_fields  Filled array of info for fields
  199.     key_id  Primary index to use to find for keyword
  200.   RETURN VALUES
  201.     #   number of topics found
  202.     names        array of name of found topics (out)
  203.     name         name of found topic (out)
  204.     description  description of found topic (out)
  205.     example      example for found topic (out)
  206.   NOTE
  207.     Field 'names' is set only if more than one topic was found.
  208.     Fields 'name', 'description', 'example' are set only if
  209.     exactly one topic was found.
  210. */
  211. int get_topics_for_keyword(THD *thd, TABLE *topics, TABLE *relations,
  212.    struct st_find_field *find_fields, int16 key_id,
  213.    List<String> *names,
  214.    String *name, String *description, String *example)
  215. {
  216.   char buff[8]; // Max int length
  217.   int count= 0;
  218.   int iindex_topic, iindex_relations;
  219.   Field *rtopic_id, *rkey_id;
  220.   DBUG_ENTER("get_topics_for_keyword");
  221.   if ((iindex_topic= find_type((char*) primary_key_name,
  222.        &topics->keynames, 1+2)-1)<0 ||
  223.       (iindex_relations= find_type((char*) primary_key_name,
  224.    &relations->keynames, 1+2)-1)<0)
  225.   {
  226.     send_error(thd,ER_CORRUPT_HELP_DB);
  227.     DBUG_RETURN(-1);
  228.   }
  229.   rtopic_id= find_fields[help_relation_help_topic_id].field;
  230.   rkey_id=   find_fields[help_relation_help_keyword_id].field;
  231.   topics->file->ha_index_init(iindex_topic);
  232.   relations->file->ha_index_init(iindex_relations);
  233.   rkey_id->store((longlong) key_id);
  234.   rkey_id->get_key_image(buff, rkey_id->pack_length(), rkey_id->charset(),
  235.  Field::itRAW);
  236.   int key_res= relations->file->index_read(relations->record[0],
  237.    (byte *)buff, rkey_id->pack_length(),
  238.    HA_READ_KEY_EXACT);
  239.   for ( ;
  240.         !key_res && key_id == (int16) rkey_id->val_int() ;
  241. key_res= relations->file->index_next(relations->record[0]))
  242.   {
  243.     char topic_id_buff[8];
  244.     longlong topic_id= rtopic_id->val_int();
  245.     Field *field= find_fields[help_topic_help_topic_id].field;
  246.     field->store((longlong) topic_id);
  247.     field->get_key_image(topic_id_buff, field->pack_length(), field->charset(),
  248.  Field::itRAW);
  249.     if (!topics->file->index_read(topics->record[0], (byte *)topic_id_buff,
  250.   field->pack_length(), HA_READ_KEY_EXACT))
  251.     {
  252.       memorize_variant_topic(thd,topics,count,find_fields,
  253.      names,name,description,example);
  254.       count++;
  255.     }
  256.   }
  257.   topics->file->ha_index_end();
  258.   relations->file->ha_index_end();
  259.   DBUG_RETURN(count);
  260. }
  261. /*
  262.   Look for categories by mask
  263.   SYNOPSIS
  264.     search_categories()
  265.     thd THD for init_read_record
  266.     categories Table of categories
  267.     find_fields         Filled array of info for fields
  268.     select Function to test for if matching help topic.
  269. Normally 'help_vategory.name like 'bit%'
  270.     names List of found categories names (out)
  271.     res_id Primary index of found category (only if
  272. found exactly one category)
  273.   RETURN VALUES
  274.     # Number of categories found
  275. */
  276. int search_categories(THD *thd, TABLE *categories,
  277.       struct st_find_field *find_fields,
  278.       SQL_SELECT *select, List<String> *names, int16 *res_id)
  279. {
  280.   Field *pfname= find_fields[help_category_name].field;
  281.   Field *pcat_id= find_fields[help_category_help_category_id].field;
  282.   int count= 0;
  283.   READ_RECORD read_record_info;
  284.   DBUG_ENTER("search_categories");
  285.   init_read_record(&read_record_info, thd, categories, select,1,0);
  286.   while (!read_record_info.read_record(&read_record_info))
  287.   {
  288.     if (select && !select->cond->val_int())
  289.       continue;
  290.     String *lname= new (thd->mem_root) String;
  291.     get_field(thd->mem_root,pfname,lname);
  292.     if (++count == 1 && res_id)
  293.       *res_id= (int16) pcat_id->val_int();
  294.     names->push_back(lname);
  295.   }
  296.   end_read_record(&read_record_info);
  297.   DBUG_RETURN(count);
  298. }
  299. /*
  300.   Look for all topics or subcategories of category
  301.   SYNOPSIS
  302.     get_all_items_for_category()
  303.     thd     Thread handler
  304.     items   Table of items
  305.     pfname  Field "name" in items
  306.     select  "where" part of query..
  307.     res     list of finded names
  308. */
  309. void get_all_items_for_category(THD *thd, TABLE *items, Field *pfname,
  310. SQL_SELECT *select, List<String> *res)
  311. {
  312.   DBUG_ENTER("get_all_items_for_category");
  313.   READ_RECORD read_record_info;
  314.   init_read_record(&read_record_info, thd, items, select,1,0);
  315.   while (!read_record_info.read_record(&read_record_info))
  316.   {
  317.     if (!select->cond->val_int())
  318.       continue;
  319.     String *name= new (thd->mem_root) String();
  320.     get_field(thd->mem_root,pfname,name);
  321.     res->push_back(name);
  322.   }
  323.   end_read_record(&read_record_info);
  324.   DBUG_VOID_RETURN;
  325. }
  326. /*
  327.   Send to client answer for help request
  328.   SYNOPSIS
  329.     send_answer_1()
  330.     protocol - protocol for sending
  331.     s1 - value of column "Name"
  332.     s2 - value of column "Description"
  333.     s3 - value of column "Example"
  334.   IMPLEMENTATION
  335.    Format used:
  336.    +----------+------------+------------+
  337.    |name      |description |example     |
  338.    +----------+------------+------------+
  339.    |String(64)|String(1000)|String(1000)|
  340.    +----------+------------+------------+
  341.    with exactly one row!
  342.   RETURN VALUES
  343.     1 Writing of head failed
  344.     -1 Writing of row failed
  345.     0 Successeful send
  346. */
  347. int send_answer_1(Protocol *protocol, String *s1, String *s2, String *s3)
  348. {
  349.   DBUG_ENTER("send_answer_1");
  350.   List<Item> field_list;
  351.   field_list.push_back(new Item_empty_string("name",64));
  352.   field_list.push_back(new Item_empty_string("description",1000));
  353.   field_list.push_back(new Item_empty_string("example",1000));
  354.   if (protocol->send_fields(&field_list,1))
  355.     DBUG_RETURN(1);
  356.   protocol->prepare_for_resend();
  357.   protocol->store(s1);
  358.   protocol->store(s2);
  359.   protocol->store(s3);
  360.   if (protocol->write())
  361.     DBUG_RETURN(-1);
  362.   DBUG_RETURN(0);
  363. }
  364. /*
  365.   Send to client help header
  366.   SYNOPSIS
  367.    send_header_2()
  368.     protocol       - protocol for sending
  369.     is_it_category - need column 'source_category_name'
  370.   IMPLEMENTATION
  371.    +-                    -+
  372.    |+-------------------- | +----------+--------------+
  373.    ||source_category_name | |name      |is_it_category|
  374.    |+-------------------- | +----------+--------------+
  375.    ||String(64)           | |String(64)|String(1)     |
  376.    |+-------------------- | +----------+--------------+
  377.    +-                    -+
  378.   RETURN VALUES
  379.     result of protocol->send_fields
  380. */
  381. int send_header_2(Protocol *protocol, bool for_category)
  382. {
  383.   DBUG_ENTER("send_header_2");
  384.   List<Item> field_list;
  385.   if (for_category)
  386.     field_list.push_back(new Item_empty_string("source_category_name",64));
  387.   field_list.push_back(new Item_empty_string("name",64));
  388.   field_list.push_back(new Item_empty_string("is_it_category",1));
  389.   DBUG_RETURN(protocol->send_fields(&field_list,1));
  390. }
  391. /*
  392.   strcmp for using in qsort
  393.   SYNOPSIS
  394.     strptrcmp()
  395.     ptr1   (const void*)&str1
  396.     ptr2   (const void*)&str2
  397.   RETURN VALUES
  398.     same as strcmp
  399. */
  400. extern "C" int string_ptr_cmp(const void* ptr1, const void* ptr2)
  401. {
  402.   String *str1= *(String**)ptr1;
  403.   String *str2= *(String**)ptr2;
  404.   return strcmp(str1->c_ptr(),str2->c_ptr());
  405. }
  406. /*
  407.   Send to client rows in format:
  408.    column1 : <name>
  409.    column2 : <is_it_category>
  410.   SYNOPSIS
  411.     send_variant_2_list()
  412.     protocol     Protocol for sending
  413.     names        List of names
  414.     cat          Value of the column <is_it_category>
  415.     source_name  name of category for all items..
  416.   RETURN VALUES
  417.     -1  Writing fail
  418.     0 Data was successefully send
  419. */
  420. int send_variant_2_list(MEM_ROOT *mem_root, Protocol *protocol,
  421. List<String> *names,
  422. const char *cat, String *source_name)
  423. {
  424.   DBUG_ENTER("send_variant_2_list");
  425.   String **pointers= (String**)alloc_root(mem_root,
  426.   sizeof(String*)*names->elements);
  427.   String **pos;
  428.   String **end= pointers + names->elements;
  429.   List_iterator<String> it(*names);
  430.   for (pos= pointers; pos!=end; (*pos++= it++));
  431.   qsort(pointers,names->elements,sizeof(String*),string_ptr_cmp);
  432.   for (pos= pointers; pos!=end; pos++)
  433.   {
  434.     protocol->prepare_for_resend();
  435.     if (source_name)
  436.       protocol->store(source_name);
  437.     protocol->store(*pos);
  438.     protocol->store(cat,1,&my_charset_latin1);
  439.     if (protocol->write())
  440.       DBUG_RETURN(-1);
  441.   }
  442.   DBUG_RETURN(0);
  443. }
  444. /*
  445.   Prepare simple SQL_SELECT table.* WHERE <Item>
  446.   SYNOPSIS
  447.     prepare_simple_select()
  448.     thd      Thread handler
  449.     cond     WHERE part of select
  450.     tables   list of tables, used in WHERE
  451.     table    goal table
  452.     error    code of error (out)
  453.   RETURN VALUES
  454.     #  created SQL_SELECT
  455. */
  456. SQL_SELECT *prepare_simple_select(THD *thd, Item *cond, TABLE_LIST *tables,
  457.   TABLE *table, int *error)
  458. {
  459.   if (!cond->fixed)
  460.     cond->fix_fields(thd, tables, &cond); // can never fail
  461.   SQL_SELECT *res= make_select(table,0,0,cond,error);
  462.   if (*error || (res && res->check_quick(thd, 0, HA_POS_ERROR)))
  463.   {
  464.     delete res;
  465.     res=0;
  466.   }
  467.   return res;
  468. }
  469. /*
  470.   Prepare simple SQL_SELECT table.* WHERE table.name LIKE mask
  471.   SYNOPSIS
  472.     prepare_select_for_name()
  473.     thd      Thread handler
  474.     mask     mask for compare with name
  475.     mlen     length of mask
  476.     tables   list of tables, used in WHERE
  477.     table    goal table
  478.     pfname   field "name" in table
  479.     error    code of error (out)
  480.   RETURN VALUES
  481.     #  created SQL_SELECT
  482. */
  483. SQL_SELECT *prepare_select_for_name(THD *thd, const char *mask, uint mlen,
  484.     TABLE_LIST *tables, TABLE *table,
  485.     Field *pfname, int *error)
  486. {
  487.   Item *cond= new Item_func_like(new Item_field(pfname),
  488.  new Item_string(mask,mlen,pfname->charset()),
  489.  new Item_string("\",1,&my_charset_latin1));
  490.   if (thd->is_fatal_error)
  491.     return 0; // OOM
  492.   return prepare_simple_select(thd,cond,tables,table,error);
  493. }
  494. /*
  495.   Server-side function 'help'
  496.   SYNOPSIS
  497.     mysqld_help()
  498.     thd Thread handler
  499.   RETURN VALUES
  500.     0 Success
  501.     1 Error and send_error already commited
  502.     -1 error && send_error should be issued (normal case)
  503. */
  504. int mysqld_help(THD *thd, const char *mask)
  505. {
  506.   Protocol *protocol= thd->protocol;
  507.   SQL_SELECT *select;
  508.   st_find_field used_fields[array_elements(init_used_fields)];
  509.   DBUG_ENTER("mysqld_help");
  510.   TABLE_LIST tables[4];
  511.   bzero((gptr)tables,sizeof(tables));
  512.   tables[0].alias= tables[0].real_name= (char*) "help_topic";
  513.   tables[0].lock_type= TL_READ;
  514.   tables[0].next= &tables[1];
  515.   tables[1].alias= tables[1].real_name= (char*) "help_category";
  516.   tables[1].lock_type= TL_READ;
  517.   tables[1].next= &tables[2];
  518.   tables[2].alias= tables[2].real_name= (char*) "help_relation";
  519.   tables[2].lock_type= TL_READ;
  520.   tables[2].next=  &tables[3];
  521.   tables[3].alias= tables[3].real_name= (char*) "help_keyword";
  522.   tables[3].lock_type= TL_READ;
  523.   tables[3].next= 0;
  524.   tables[0].db= tables[1].db= tables[2].db= tables[3].db= (char*) "mysql";
  525.   List<String> topics_list, categories_list, subcategories_list;
  526.   String name, description, example;
  527.   int res, count_topics, count_categories, error;
  528.   uint mlen= strlen(mask);
  529.   MEM_ROOT *mem_root= thd->mem_root;
  530.   if (open_and_lock_tables(thd, tables))
  531.   {
  532.     res= -1;
  533.     goto end;
  534.   }
  535.   /* Init tables and fields to be usable from items */
  536.   setup_tables(tables);
  537.   memcpy((char*) used_fields, (char*) init_used_fields, sizeof(used_fields));
  538.   if (init_fields(thd, tables, used_fields, array_elements(used_fields)))
  539.   {
  540.     res= -1;
  541.     goto end;
  542.   }
  543.   size_t i;
  544.   for (i=0; i<sizeof(tables)/sizeof(TABLE_LIST); i++)
  545.     tables[i].table->file->init_table_handle_for_HANDLER();
  546.   if (!(select=
  547. prepare_select_for_name(thd,mask,mlen,tables,tables[0].table,
  548. used_fields[help_topic_name].field,&error)))
  549.   {
  550.     res= -1;
  551.     goto end;
  552.   }
  553.   res= 1;
  554.   count_topics= search_topics(thd,tables[0].table,used_fields,
  555.       select,&topics_list,
  556.       &name, &description, &example);
  557.   delete select;
  558.   if (count_topics == 0)
  559.   {
  560.     int key_id;
  561.     if (!(select=
  562.           prepare_select_for_name(thd,mask,mlen,tables,tables[3].table,
  563.                                   used_fields[help_keyword_name].field,&error)))
  564.     {
  565.       res= -1;
  566.       goto end;
  567.     }
  568.     count_topics=search_keyword(thd,tables[3].table,used_fields,select,&key_id);
  569.     delete select;
  570.     count_topics= (count_topics != 1) ? 0 :
  571.                   get_topics_for_keyword(thd,tables[0].table,tables[2].table,
  572.                                          used_fields,key_id,&topics_list,&name,
  573.                                          &description,&example);
  574.   }
  575.   if (count_topics == 0)
  576.   {
  577.     int16 category_id;
  578.     Field *cat_cat_id= used_fields[help_category_parent_category_id].field;
  579.     if (!(select=
  580.           prepare_select_for_name(thd,mask,mlen,tables,tables[1].table,
  581.                                   used_fields[help_category_name].field,&error)))
  582.     {
  583.       res= -1;
  584.       goto end;
  585.     }
  586.     count_categories= search_categories(thd, tables[1].table, used_fields,
  587. select,
  588. &categories_list,&category_id);
  589.     delete select;
  590.     if (!count_categories)
  591.     {
  592.       if (send_header_2(protocol,FALSE))
  593. goto end;
  594.     }
  595.     else if (count_categories > 1)
  596.     {
  597.       if (send_header_2(protocol,FALSE) ||
  598.   send_variant_2_list(mem_root,protocol,&categories_list,"Y",0))
  599. goto end;
  600.     }
  601.     else
  602.     {
  603.       Field *topic_cat_id= used_fields[help_topic_help_category_id].field;
  604.       Item *cond_topic_by_cat=
  605. new Item_func_equal(new Item_field(topic_cat_id),
  606.     new Item_int((int32)category_id));
  607.       Item *cond_cat_by_cat=
  608. new Item_func_equal(new Item_field(cat_cat_id),
  609.     new Item_int((int32)category_id));
  610.       if (!(select= prepare_simple_select(thd,cond_topic_by_cat,
  611.                                           tables,tables[0].table,&error)))
  612.       {
  613. res= -1;
  614. goto end;
  615.       }
  616.       get_all_items_for_category(thd,tables[0].table,
  617.  used_fields[help_topic_name].field,
  618.  select,&topics_list);
  619.       delete select;
  620.       if (!(select= prepare_simple_select(thd,cond_cat_by_cat,tables,
  621.      tables[1].table,&error)))
  622.       {
  623. res= -1;
  624. goto end;
  625.       }
  626.       get_all_items_for_category(thd,tables[1].table,
  627.  used_fields[help_category_name].field,
  628.  select,&subcategories_list);
  629.       delete select;
  630.       String *cat= categories_list.head();
  631.       if (send_header_2(protocol, TRUE) ||
  632.   send_variant_2_list(mem_root,protocol,&topics_list,       "N",cat) ||
  633.   send_variant_2_list(mem_root,protocol,&subcategories_list,"Y",cat))
  634. goto end;
  635.     }
  636.   }
  637.   else if (count_topics == 1)
  638.   {
  639.     if (send_answer_1(protocol,&name,&description,&example))
  640.       goto end;
  641.   }
  642.   else
  643.   {
  644.     /* First send header and functions */
  645.     if (send_header_2(protocol, FALSE) ||
  646. send_variant_2_list(mem_root,protocol, &topics_list, "N", 0))
  647.       goto end;
  648.     if (!(select=
  649.           prepare_select_for_name(thd,mask,mlen,tables,tables[1].table,
  650.                                   used_fields[help_category_name].field,&error)))
  651.     {
  652.       res= -1;
  653.       goto end;
  654.     }
  655.     search_categories(thd, tables[1].table, used_fields,
  656.       select,&categories_list, 0);
  657.     delete select;
  658.     /* Then send categories */
  659.     if (send_variant_2_list(mem_root,protocol, &categories_list, "Y", 0))
  660.       goto end;
  661.   }
  662.   res= 0;
  663.   send_eof(thd);
  664. end:
  665.   DBUG_RETURN(res);
  666. }