1
0
mirror of https://github.com/MariaDB/server.git synced 2025-07-27 18:02:13 +03:00

Fixed a lot of wrong memory references as reported by valgrind

Portability fixes
Added new client function: mysql_get_server_version()
New server help code (From Victor Vagin)
Fixed wrong usage of binary()
Disabled RTREE usage for now.



BitKeeper/etc/ignore:
  added scripts/fill_help_tables.sql
client/mysql.cc:
  Some fixes when using 'help'
cmd-line-utils/libedit/compat.h:
  Portability fix
cmd-line-utils/libedit/fgetln.c:
  Portability fix
include/mysql.h:
  Added new client function: mysql_get_server_version()
libmysql/libmysql.c:
  Added new client function: mysql_get_server_version()
libmysqld/libmysqld.c:
  Fixed prototype
mysql-test/install_test_db.sh:
  Added creation of help tables
mysql-test/r/connect.result:
  Added help tables
mysql-test/r/myisam.result:
  Test of RTREE index
mysql-test/r/type_ranges.result:
  updated results
mysql-test/t/myisam.test:
  Test of RTREE index
mysql-test/t/type_ranges.test:
  Updated test
mysys/charset.c:
  Indentation change
mysys/my_symlink.c:
  Removed compiler warning
scripts/fill_help_tables.sh:
  Update for new help tables
sql/field.cc:
  Indentation changes
sql/filesort.cc:
  Optimized character set usage
sql/item_cmpfunc.cc:
  Fix wrong usage of binary()
sql/item_cmpfunc.h:
  Fix wrong usage of binary()
sql/item_func.cc:
  Fix wrong usage of binary()
sql/item_func.h:
  Fix wrong usage of binary()
sql/item_strfunc.cc:
  Fix wrong usage of binary()
sql/item_sum.cc:
  Fix wrong usage of binary()
sql/item_sum.h:
  Fix wrong usage of binary()
sql/key.cc:
  Indentation change
sql/lex.h:
  HELP -> HELP_SYM
sql/mysql_priv.h:
  Make get_field() more general
sql/password.c:
  Indentation change + variable initialisation moved
sql/sql_acl.cc:
  Make get_field() more general
sql/sql_base.cc:
  Added comments + assertion for double call to mysql_lock_tables
sql/sql_cache.cc:
  Indentation changes
sql/sql_class.h:
  Added need_strxnfrm to SORT_FIELD to be able to optimise character set handling in filesort
sql/sql_derived.cc:
  Renamed variables
sql/sql_help.cc:
  New help functions (from Victor Vagin)
sql/sql_lex.cc:
  Removed variables that doesn't have to be initialized for each query
sql/sql_lex.h:
  Removed not used variable (olap)
sql/sql_parse.cc:
  Fixed (not fatal) access of unitialized memory
  Indentation / code cleanup
sql/sql_prepare.cc:
  Indentaion cleanup
sql/sql_table.cc:
  Disabled RTREE until 5.0
sql/sql_udf.cc:
  Make get_field() more general
sql/sql_yacc.yy:
  Removed access to uninitialized memory
  Always set offset_limit and select_limit when using LIMIT (removed warnings)
  Allow usage of 'help week'
sql/table.cc:
  Make get_field() more general
  More comments
sql/table.h:
  Fixded type of TABLE_LIST->derived
sql/time.cc:
  Stricter date / datetime handling (to be able to handle timestamps with days and microseconds)
strings/ctype-bin.c:
  Added cha
This commit is contained in:
unknown
2003-02-12 21:55:37 +02:00
parent 363fd89b92
commit fcb61f5917
48 changed files with 1200 additions and 650 deletions

View File

@ -15,288 +15,294 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include "mysql_priv.h"
#include "sql_select.h" // For select_describe
#include "sql_acl.h"
/***************************************************************************
** Get help on string
***************************************************************************/
struct st_find_field
{
const char *table_name, *field_name;
Field *field;
};
/* Used fields */
static struct st_find_field init_used_fields[]=
{
{ "help_topic", "name", 0},
{ "help_topic","description", 0},
{ "help_topic","example", 0},
{ "help_topic", "help_topic_id", 0},
{ "help_category","name", 0},
{ "help_category","help_category_id", 0},
{ "help_relation","help_topic_id", 0},
{ "help_relation","help_category_id", 0}
};
enum enum_used_fields
{
help_topic_name=0, help_topic_description, help_topic_example,
help_topic_help_topic_id,
help_category_name, help_category_help_category_id,
help_relation_help_topic_id, help_relation_help_category_id
};
/*
Fill local used field structure with pointer to fields */
static bool init_fields(THD *thd, TABLE_LIST *tables,
struct st_find_field *find_field,
uint count)
{
for (; count-- ; find_field++)
{
TABLE_LIST *not_used;
/* We have to use 'new' here as field will be re_linked on free */
Item_field *field= new Item_field("mysql", find_field->table_name,
find_field->field_name);
if (!(find_field->field= find_field_in_tables(thd, field, tables,
&not_used,
TRUE)))
return 1;
}
return 0;
}
#define help_charset &my_charset_latin1
MI_INFO *open_help_file(THD *thd, const char *name)
{
char path[FN_REFLEN];
(void) sprintf(path,"%s/mysql_help/%s",mysql_data_home,name);
MI_INFO *res= 0;
if (!(res= mi_open(path,O_RDONLY,HA_OPEN_WAIT_IF_LOCKED)))
{
send_error(thd,ER_CORRUPT_HELP_DB);
return 0;
}
mi_extra(res,HA_EXTRA_WAIT_LOCK,0);
return res;
}
/*
Look for topics by mask
#define size_hf_func_id 4 /* func_id int unsigned, */
#define size_hf_name 64 /* name varchar(64), */
#define size_hf_url 128 /* url varchar(128), */
#define size_hf_description sizeof(char*) /* description text, */
#define size_hf_example sizeof(char*) /* example text, */
#define size_hf_min_args 16 /* min_args tinyint, */
#define size_hf_max_args 16 /* max_args tinyint, */
#define size_hf_date_created 8 /* date_created datetime, */
#define size_hf_last_modified 8 /* last_modified timestamp, */
SYNOPSIS
search_topics()
thd Thread handler
topics Table of topic
select Function to test for if matching help topic.
Normally 'help_topic.name like 'bit%'
pfname Pointer to Field structure for field "name"
names List of founded topic's names (out)
name Name of founded topic (out),
Only set if founded exactly one topic)
description Description of founded topic (out)
Only set if founded exactly one topic.
example Example for founded topic (out)
Only if founded exactly one topic.
RETURN VALUES
# number of topics founded
*/
#define offset_hf_func_id 1
#define offset_hf_name (offset_hf_func_id+size_hf_func_id)
#define offset_hf_url (offset_hf_name+size_hf_name)
#define offset_hf_description (offset_hf_url+size_hf_url)
#define offset_hf_example (offset_hf_description+size_hf_description)
#define offset_hf_min_args (offset_hf_example+size_hf_example)
#define offset_hf_max_args (offset_hf_min_args+size_hf_min_args)
#define offset_hf_date_created (offset_hf_max_args+size_hf_max_args)
#define offset_hf_last_modified (offset_hf_date_created+size_hf_date_created)
#define HELP_LEAF_SIZE (offset_hf_last_modified+size_hf_last_modified)
class help_leaf{
public:
char record[HELP_LEAF_SIZE];
inline const char *get_name()
{
return &record[offset_hf_name];
}
inline const char *get_description()
{
return *((char**)&record[199/*offset_hf_description*/]);
}
inline const char *get_example()
{
return *((char**)&record[209/*offset_hf_example*/]);
}
void prepare_fields()
{
const char *name= get_name();
const char *c= name + size_hf_name - 1;
while (*c==' ') c--;
int len= c-name+1;
((char*)name)[len]= '\0';
}
};
int search_functions(MI_INFO *file_leafs, const char *mask,
List<String> *names,
String **name, String **description, String **example)
int search_topics(THD *thd, TABLE *topics, struct st_find_field *find_field,
SQL_SELECT *select, List<char> *names,
char **name, char **description, char **example)
{
DBUG_ENTER("search_functions");
int count= 0;
if (mi_scan_init(file_leafs))
DBUG_RETURN(-1);
help_leaf leaf;
while (!mi_scan(file_leafs,(byte*)&leaf))
READ_RECORD read_record_info;
init_read_record(&read_record_info, thd, topics, select,1,0);
while (!read_record_info.read_record(&read_record_info))
{
leaf.prepare_fields();
const char *lname= leaf.get_name();
if (wild_case_compare(help_charset,lname,mask))
if (!select->cond->val_int()) // Dosn't match like
continue;
count++;
if (count>2)
char *lname= get_field(&thd->mem_root, find_field[help_topic_name].field);
count++;
if (count > 2)
{
String *s= new String(lname,help_charset);
if (!s->copy())
names->push_back(s);
names->push_back(lname);
}
else if (count==1)
else if (count == 1)
{
*description= new String(leaf.get_description(),help_charset);
*example= new String(leaf.get_example(),help_charset);
*name= new String(lname,help_charset);
(*description)->copy();
(*example)->copy();
(*name)->copy();
*description= get_field(&thd->mem_root,
find_field[help_topic_description].field);
*example= get_field(&thd->mem_root,
find_field[help_topic_example].field);
*name= lname;
}
else
{
names->push_back(*name);
delete *description;
delete *example;
names->push_back(lname);
*name= 0;
*description= 0;
*example= 0;
String *s= new String(lname,help_charset);
if (!s->copy())
names->push_back(s);
}
}
end_read_record(&read_record_info);
DBUG_RETURN(count);
}
/*
Look for categories by mask
SYNOPSIS
search_categories()
thd THD for init_read_record
categories Table of categories
select Function to test for if matching help topic.
Normally 'help_topic.name like 'bit%'
names List of founded topic's names (out)
res_id Primary index of founded category (only if
founded exactly one category)
RETURN VALUES
# Number of categories founded
*/
int search_categories(THD *thd, TABLE *categories,
struct st_find_field *find_fields,
SQL_SELECT *select, List<char> *names, int16 *res_id)
{
Field *pfname= find_fields[help_category_name].field;
DBUG_ENTER("search_categories");
int count= 0;
READ_RECORD read_record_info;
init_read_record(&read_record_info, thd, categories, select,1,0);
while (!read_record_info.read_record(&read_record_info))
{
if (select && !select->cond->val_int())
continue;
char *lname= get_field(&thd->mem_root,pfname);
if (++count == 1 && res_id)
{
Field *pcat_id= find_fields[help_category_help_category_id].field;
*res_id= (int16) pcat_id->val_int();
}
names->push_back(lname);
}
end_read_record(&read_record_info);
DBUG_RETURN(count);
}
#define size_hc_cat_id 2 /* cat_id smallint, */
#define size_hc_name 64 /* name varchar(64), */
#define size_hc_url 128 /* url varchar(128), */
#define size_hc_date_created 8 /* date_created datetime, */
#define size_hc_last_modified 8 /* last_modified timestamp, */
#define offset_hc_cat_id 0
#define offset_hc_name (offset_hc_cat_id+size_hc_cat_id)
#define offset_hc_url (offset_hc_name+size_hc_name)
#define offset_hc_date_created (offset_hc_url+size_hc_url)
#define offset_hc_last_modified (offset_hc_date_created+size_hc_date_created)
/*
Send to client rows in format:
column1 : <name>
column2 : <is_it_category>
#define HELP_CATEGORY_SIZE (offset_hc_last_modified+size_hc_last_modified)
SYNOPSIS
send_variant_2_list()
protocol Protocol for sending
names List of names
cat Value of the column <is_it_category>
class help_category{
public:
char record[HELP_CATEGORY_SIZE];
RETURN VALUES
-1 Writing fail
0 Data was successefully send
*/
inline int16 get_cat_id()
{
return sint2korr(&record[offset_hc_cat_id]);
}
inline const char *get_name()
{
return &record[offset_hc_name];
}
void prepare_fields()
{
const char *name= get_name();
const char *c= name + size_hc_name - 1;
while (*c==' ') c--;
int len= c-name+1;
((char*)name)[len]= '\0';
}
};
int search_categories(THD *thd,
const char *mask, List<String> *names, int16 *res_id)
{
DBUG_ENTER("search_categories");
int count= 0;
MI_INFO *file_categories= 0;
if (!(file_categories= open_help_file(thd,"function_category_name")))
DBUG_RETURN(-1);
if (mi_scan_init(file_categories))
{
mi_close(file_categories);
DBUG_RETURN(-1);
}
help_category category;
while (!mi_scan(file_categories,(byte*)&category))
{
category.prepare_fields();
const char *lname= category.get_name();
if (mask && wild_case_compare(help_charset,lname,mask))
continue;
count++;
if (count==1 && res_id)
*res_id= category.get_cat_id();
String *s= new String(lname,help_charset);
if (!s->copy())
names->push_back(s);
}
mi_close(file_categories);
DBUG_RETURN(count);
}
int send_variant_2_list(Protocol *protocol, List<String> *names,
my_bool is_category)
int send_variant_2_list(Protocol *protocol, List<char> *names,
const char *cat)
{
DBUG_ENTER("send_names");
List_iterator<String> it(*names);
String *cur_name;
while ((cur_name = it++))
List_iterator<char> it(*names);
const char *cur_name;
while ((cur_name= it++))
{
protocol->prepare_for_resend();
protocol->store(cur_name->ptr());
protocol->store(is_category ? "Y" : "N");
protocol->store(cur_name);
protocol->store(cat);
if (protocol->write())
DBUG_RETURN(-1);
}
DBUG_RETURN(0);
}
#define size_hcn_cat_id 2 /* cat_id smallint, */
#define size_hcn_func_id 4 /* func_id int, */
#define offset_hcn_cat_id 1
#define offset_hcn_func_id (offset_hcn_cat_id+size_hcn_cat_id)
/*
Look for all topics of category
#define HELP_CATEGORY_NAME_SIZE (offset_hcn_func_id + size_hcn_func_id)
SYNOPSIS
get_all_topics_for_category()
thd Thread handler
topics Table of topics
relations Table of m:m relation "topic/category"
cat_id Primary index looked for category
res List of founded topic's names (out)
class help_category_leaf{
public:
char record[HELP_CATEGORY_NAME_SIZE];
RETURN VALUES
-1 corrupt database
0 succesefull
*/
inline int16 get_cat_id()
{
return sint2korr(&record[offset_hcn_cat_id]);
}
inline int get_func_id()
{
return sint3korr(&record[offset_hcn_func_id]);
}
};
int get_all_names_for_category(THD *thd,MI_INFO *file_leafs,
int16 cat_id, List<String> *res)
int get_all_topics_for_category(THD *thd, TABLE *topics, TABLE *relations,
struct st_find_field *find_fields,
int16 cat_id, List<char> *res)
{
DBUG_ENTER("get_all_names_for_category");
MI_INFO *file_names_categories= 0;
if (!(file_names_categories= open_help_file(thd,"function_category")))
DBUG_RETURN(1);
help_category_leaf cat_leaf;
help_leaf leaf;
int key_res= mi_rkey(file_names_categories, (byte*)&cat_leaf, 0,
(const byte*)&cat_id,2,HA_READ_KEY_EXACT);
while (!key_res && cat_leaf.get_cat_id()==cat_id)
char buff[8]; // Max int length
DBUG_ENTER("get_all_topics_for_category");
int iindex_topic, iindex_relations;
Field *rtopic_id, *rcat_id;
if ((iindex_topic= find_type((char*) "PRIMARY",
&topics->keynames, 1+2)-1)<0 ||
(iindex_relations= find_type((char*) "PRIMARY",
&relations->keynames, 1+2)-1)<0)
{
int leaf_id= cat_leaf.get_func_id();
if (!mi_rkey(file_leafs, (byte*)&leaf, 0,
(const byte*)&leaf_id,4,HA_READ_KEY_EXACT))
{
leaf.prepare_fields();
String *s= new String(leaf.get_name(),help_charset);
if (!s->copy())
res->push_back(s);
}
key_res= mi_rnext(file_names_categories, (byte*)&cat_leaf, 0);
send_error(thd,ER_CORRUPT_HELP_DB);
DBUG_RETURN(-1);
}
rtopic_id= find_fields[help_relation_help_topic_id].field;
rcat_id= find_fields[help_relation_help_category_id].field;
topics->file->index_init(iindex_topic);
relations->file->index_init(iindex_relations);
rcat_id->store((longlong) cat_id);
rcat_id->get_key_image(buff, rcat_id->pack_length(), help_charset,
Field::itRAW);
int key_res= relations->file->index_read(relations->record[0],
buff, rcat_id->pack_length(),
HA_READ_KEY_EXACT);
for ( ; !key_res && cat_id == (int16) rcat_id->val_int() ;
key_res= relations->file->index_next(relations->record[0]))
{
char topic_id_buff[8];
longlong topic_id= rtopic_id->val_int();
Field *field= find_fields[help_topic_help_topic_id].field;
field->store((longlong) topic_id);
field->get_key_image(topic_id_buff, field->pack_length(), help_charset,
Field::itRAW);
mi_close(file_names_categories);
if (!topics->file->index_read(topics->record[0], topic_id_buff,
field->pack_length(),
HA_READ_KEY_EXACT))
res->push_back(get_field(&thd->mem_root,
find_fields[help_topic_name].field));
}
DBUG_RETURN(0);
}
/*
Send to client answer for help request
SYNOPSIS
send_answer_1()
protocol - protocol for sending
s1 - value of column "Name"
s2 - value of column "Category"
s3 - value of column "Description"
s4 - value of column "Example"
IMPLEMENTATION
Format used:
+----------+---------+------------+------------+
|Name: |Category |Description |Example |
+----------+---------+------------+------------+
|String(64)|String(1)|String(1000)|String(1000)|
+----------+---------+------------+------------+
with exactly one row!
RETURN VALUES
1 Writing of head failed
-1 Writing of row failed
0 Successeful send
*/
int send_answer_1(Protocol *protocol, const char *s1, const char *s2,
const char *s3, const char *s4)
{
@ -306,10 +312,10 @@ int send_answer_1(Protocol *protocol, const char *s1, const char *s2,
field_list.push_back(new Item_empty_string("Category",1));
field_list.push_back(new Item_empty_string("Description",1000));
field_list.push_back(new Item_empty_string("Example",1000));
if (protocol->send_fields(&field_list,1))
DBUG_RETURN(1);
protocol->prepare_for_resend();
protocol->store(s1);
protocol->store(s2);
@ -317,11 +323,28 @@ int send_answer_1(Protocol *protocol, const char *s1, const char *s2,
protocol->store(s4);
if (protocol->write())
DBUG_RETURN(-1);
DBUG_RETURN(0);
}
/*
Send to client help header
SYNOPSIS
send_header_2()
protocol - protocol for sending
IMPLEMENTATION
+----------+---------+
|Name: |Category |
+----------+---------+
|String(64)|String(1)|
+----------+---------+
RETURN VALUES
result of protocol->send_fields
*/
int send_header_2(Protocol *protocol)
{
DBUG_ENTER("send_header2");
@ -332,79 +355,165 @@ int send_header_2(Protocol *protocol)
}
/*
Server-side function 'help'
SYNOPSIS
mysqld_help()
thd Thread handler
RETURN VALUES
0 Success
1 Error and send_error already commited
-1 error && send_error should be issued (normal case)
*/
int mysqld_help(THD *thd, const char *mask)
{
Protocol *protocol= thd->protocol;
SQL_SELECT *select= 0, *select_cat= 0;
Item *cond_topic, *cond_cat;
st_find_field used_fields[array_elements(init_used_fields)];
DBUG_ENTER("mysqld_help");
MI_INFO *file_leafs= 0;
if (!(file_leafs= open_help_file(thd,"function")))
DBUG_RETURN(1);
List<String> function_list, categories_list;
String *name, *description, *example;
int res;
int count= search_functions(file_leafs, mask,
&function_list,&name,&description,&example);
if (count < 0)
TABLE_LIST tables[3];
bzero((gptr)tables,sizeof(tables));
tables[0].alias= tables[0].real_name= (char*) "help_topic";
tables[0].lock_type= TL_READ;
tables[0].db= (char*) "mysql";
tables[0].next= &tables[1];
tables[1].alias= tables[1].real_name= (char*) "help_category";
tables[1].lock_type= TL_READ;
tables[1].db= (char*) "mysql";
tables[1].next= &tables[2];
tables[2].alias= tables[2].real_name= (char*) "help_relation";
tables[2].lock_type= TL_READ;
tables[2].db= (char*) "mysql";
tables[2].next= 0;
List<char> function_list, categories_list;
char *name, *description, *example;
int res, count_topics, count_categories, error;
if (open_and_lock_tables(thd, tables))
{
res= 1;
res= -1;
goto end;
}
else if (count==0)
/* Init tables and fields to be usable from items */
setup_tables(tables);
memcpy((char*) used_fields, (char*) init_used_fields, sizeof(used_fields));
if (init_fields(thd, tables, used_fields, array_elements(used_fields)))
{
res= -1;
goto end;
}
/* TODO: Find out why these are needed (should not be) */
tables[0].table->file->init_table_handle_for_HANDLER();
tables[1].table->file->init_table_handle_for_HANDLER();
tables[2].table->file->init_table_handle_for_HANDLER();
cond_topic= new Item_func_like(new Item_field(used_fields[help_topic_name].
field),
new Item_string(mask, strlen(mask),
help_charset),
(char*) "\\");
cond_topic->fix_fields(thd, tables, &cond_topic); // can never fail
select= make_select(tables[0].table,0,0,cond_topic,&error);
if (error || (select && select->check_quick(0, HA_POS_ERROR)))
{
res= -1;
goto end;
}
cond_cat= new Item_func_like(new Item_field(used_fields[help_category_name].
field),
new Item_string(mask, strlen(mask),
help_charset),
(char*) "\\");
cond_cat->fix_fields(thd, tables, &cond_topic); // can never fail
select_cat= make_select(tables[1].table,0,0,cond_cat,&error);
if (error || (select_cat && select_cat->check_quick(0, HA_POS_ERROR)))
{
res= -1;
goto end;
}
res= 1;
count_topics= search_topics(thd,tables[0].table, used_fields, select,
&function_list, &name, &description, &example);
if (count_topics == 0)
{
int16 category_id;
count= search_categories(thd, mask, &categories_list, &category_id);
if (count<0)
Item *cond=
new Item_func_like(new
Item_field(used_fields[help_category_name].field),
new Item_string(mask, strlen(mask),
help_charset),
(char*) "\\");
(void) cond->fix_fields(thd, tables, &cond); // can never fail
count_categories= search_categories(thd, tables[1].table, used_fields,
select_cat, &categories_list,
&category_id);
if (count_categories == 1)
{
res= 1;
goto end;
}
else if (count==1)
{
if ((res= get_all_names_for_category(thd, file_leafs,
category_id,&function_list)))
goto end;
List_iterator<String> it(function_list);
String *cur_leaf, example;
while ((cur_leaf = it++))
if (get_all_topics_for_category(thd,tables[0].table,
tables[2].table, used_fields,
category_id, &function_list))
{
example.append(*cur_leaf);
res= -1;
goto end;
}
List_iterator<char> it(function_list);
char *cur_topic;
char buff[1024];
String example(buff, sizeof(buff), help_charset);
example.length(0);
while ((cur_topic= it++))
{
example.append(cur_topic);
example.append("\n",1);
}
if ((res= send_answer_1(protocol, categories_list.head()->ptr(),
"Y","",example.ptr())))
if ((send_answer_1(protocol, categories_list.head(),
"Y","",example.ptr())))
goto end;
}
else
{
if ((res= send_header_2(protocol)) ||
(count==0 &&
(search_categories(thd, 0, &categories_list, 0)<0 &&
((res= 1)))) ||
(res= send_variant_2_list(protocol,&categories_list,true)))
if (send_header_2(protocol))
goto end;
if (count_categories == 0)
search_categories(thd,tables[1].table, used_fields, (SQL_SELECT *) 0,
&categories_list, 0);
if (send_variant_2_list(protocol,&categories_list,"Y"))
goto end;
}
}
else if (count==1)
else if (count_topics == 1)
{
if ((res= send_answer_1(protocol,name->ptr(),"N",
description->ptr(), example->ptr())))
if (send_answer_1(protocol,name,"N",description, example))
goto end;
}
else if ((res= send_header_2(protocol)) ||
(res= send_variant_2_list(protocol,&function_list,false)) ||
(search_categories(thd, mask, &categories_list, 0)<0 &&
((res=1))) ||
(res= send_variant_2_list(protocol,&categories_list,true)))
else
{
goto end;
/* First send header and functions */
if (send_header_2(protocol) ||
send_variant_2_list(protocol, &function_list, "N"))
goto end;
search_categories(thd, tables[1].table, used_fields, select_cat,
&categories_list, 0);
/* Then send categories */
if (send_variant_2_list(protocol, &categories_list, "Y"))
goto end;
}
res= 0;
send_eof(thd);
end:
mi_close(file_leafs);
delete select;
delete select_cat;
DBUG_RETURN(res);
}