mirror of
https://github.com/MariaDB/server.git
synced 2025-01-16 20:12:31 +01:00
410 lines
11 KiB
C++
410 lines
11 KiB
C++
/* Copyright (C) 2000 MySQL AB
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
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
|
|
***************************************************************************/
|
|
|
|
#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;
|
|
}
|
|
|
|
#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, */
|
|
|
|
#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)
|
|
{
|
|
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))
|
|
{
|
|
leaf.prepare_fields();
|
|
|
|
const char *lname= leaf.get_name();
|
|
if (wild_case_compare(help_charset,lname,mask))
|
|
continue;
|
|
count++;
|
|
|
|
if (count>2)
|
|
{
|
|
String *s= new String(lname,help_charset);
|
|
if (!s->copy())
|
|
names->push_back(s);
|
|
}
|
|
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();
|
|
}
|
|
else
|
|
{
|
|
names->push_back(*name);
|
|
delete *description;
|
|
delete *example;
|
|
*name= 0;
|
|
*description= 0;
|
|
*example= 0;
|
|
|
|
String *s= new String(lname,help_charset);
|
|
if (!s->copy())
|
|
names->push_back(s);
|
|
}
|
|
}
|
|
|
|
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)
|
|
|
|
#define HELP_CATEGORY_SIZE (offset_hc_last_modified+size_hc_last_modified)
|
|
|
|
class help_category{
|
|
public:
|
|
char record[HELP_CATEGORY_SIZE];
|
|
|
|
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)
|
|
{
|
|
DBUG_ENTER("send_names");
|
|
|
|
List_iterator<String> it(*names);
|
|
String *cur_name;
|
|
while ((cur_name = it++))
|
|
{
|
|
protocol->prepare_for_resend();
|
|
protocol->store(cur_name->ptr());
|
|
protocol->store(is_category ? "Y" : "N");
|
|
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)
|
|
|
|
#define HELP_CATEGORY_NAME_SIZE (offset_hcn_func_id + size_hcn_func_id)
|
|
|
|
class help_category_leaf{
|
|
public:
|
|
char record[HELP_CATEGORY_NAME_SIZE];
|
|
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
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);
|
|
}
|
|
|
|
mi_close(file_names_categories);
|
|
|
|
DBUG_RETURN(0);
|
|
}
|
|
|
|
int send_answer_1(Protocol *protocol, const char *s1, const char *s2,
|
|
const char *s3, const char *s4)
|
|
{
|
|
DBUG_ENTER("send_answer_1");
|
|
List<Item> field_list;
|
|
field_list.push_back(new Item_empty_string("Name",64));
|
|
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);
|
|
protocol->store(s3);
|
|
protocol->store(s4);
|
|
if (protocol->write())
|
|
DBUG_RETURN(-1);
|
|
|
|
DBUG_RETURN(0);
|
|
}
|
|
|
|
|
|
int send_header_2(Protocol *protocol)
|
|
{
|
|
DBUG_ENTER("send_header2");
|
|
List<Item> field_list;
|
|
field_list.push_back(new Item_empty_string("Name",64));
|
|
field_list.push_back(new Item_empty_string("Category",1));
|
|
DBUG_RETURN(protocol->send_fields(&field_list,1));
|
|
}
|
|
|
|
|
|
int mysqld_help(THD *thd, const char *mask)
|
|
{
|
|
Protocol *protocol= thd->protocol;
|
|
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)
|
|
{
|
|
res= 1;
|
|
goto end;
|
|
}
|
|
else if (count==0)
|
|
{
|
|
int16 category_id;
|
|
count= search_categories(thd, mask, &categories_list, &category_id);
|
|
if (count<0)
|
|
{
|
|
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++))
|
|
{
|
|
example.append(*cur_leaf);
|
|
example.append("\n",1);
|
|
}
|
|
if ((res= send_answer_1(protocol, categories_list.head()->ptr(),
|
|
"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)))
|
|
goto end;
|
|
}
|
|
}
|
|
else if (count==1)
|
|
{
|
|
if ((res= send_answer_1(protocol,name->ptr(),"N",
|
|
description->ptr(), example->ptr())))
|
|
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)))
|
|
{
|
|
goto end;
|
|
}
|
|
|
|
send_eof(thd);
|
|
|
|
end:
|
|
mi_close(file_leafs);
|
|
DBUG_RETURN(res);
|
|
}
|