summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--client/mysql.cc195
-rw-r--r--include/mysqld_error.h1
-rw-r--r--scripts/Makefile.am14
-rw-r--r--scripts/fill_func_tables.sh234
-rw-r--r--scripts/mysql_install_db.sh7
-rw-r--r--sql/Makefile.am2
-rw-r--r--sql/lex.h1
-rw-r--r--sql/mysql_priv.h1
-rw-r--r--sql/share/czech/errmsg.txt1
-rw-r--r--sql/share/danish/errmsg.txt1
-rw-r--r--sql/share/dutch/errmsg.txt1
-rw-r--r--sql/share/english/errmsg.txt1
-rw-r--r--sql/share/estonian/errmsg.txt1
-rw-r--r--sql/share/french/errmsg.txt1
-rw-r--r--sql/share/german/errmsg.txt1
-rw-r--r--sql/share/greek/errmsg.txt1
-rw-r--r--sql/share/hungarian/errmsg.txt1
-rw-r--r--sql/share/italian/errmsg.txt1
-rw-r--r--sql/share/japanese/errmsg.txt1
-rw-r--r--sql/share/korean/errmsg.txt1
-rw-r--r--sql/share/norwegian-ny/errmsg.txt1
-rw-r--r--sql/share/norwegian/errmsg.txt1
-rw-r--r--sql/share/polish/errmsg.txt1
-rw-r--r--sql/share/portuguese/errmsg.txt1
-rw-r--r--sql/share/romanian/errmsg.txt1
-rw-r--r--sql/share/russian/errmsg.txt1
-rw-r--r--sql/share/serbian/errmsg.txt1
-rw-r--r--sql/share/slovak/errmsg.txt1
-rw-r--r--sql/share/spanish/errmsg.txt1
-rw-r--r--sql/share/swedish/errmsg.txt1
-rw-r--r--sql/share/ukrainian/errmsg.txt1
-rw-r--r--sql/sql_help.cc408
-rw-r--r--sql/sql_lex.h3
-rw-r--r--sql/sql_parse.cc4
-rw-r--r--sql/sql_yacc.yy17
35 files changed, 860 insertions, 50 deletions
diff --git a/client/mysql.cc b/client/mysql.cc
index 07c1bfeb817..fbd3b13ca83 100644
--- a/client/mysql.cc
+++ b/client/mysql.cc
@@ -205,8 +205,8 @@ typedef struct {
} COMMANDS;
static COMMANDS commands[] = {
- { "help", 'h', com_help, 0, "Display this help." },
- { "?", '?', com_help, 0, "Synonym for `help'." },
+ { "help", 'h', com_help, 1, "Display this help." },
+ { "?", '?', com_help, 1, "Synonym for `help'." },
{ "clear", 'c', com_clear, 0, "Clear command."},
{ "connect",'r', com_connect,1,
"Reconnect to the server. Optional arguments are db and host." },
@@ -382,8 +382,9 @@ int main(int argc,char *argv[])
}
}
#endif
- sprintf(buff,
- "Type 'help;' or '\\h' for help. Type '\\c' to clear the buffer.\n");
+ sprintf(buff, "%s%s",
+ "Type 'help;' or '\\h' for help. Type '\\c' to clear the buffer.\n",
+ "Type 'help [[%]function name[%]]' to get help on usage of function.\n");
put_info(buff,INFO_INFO);
status.exit_status=read_lines(1); // read lines and execute them
if (opt_outfile)
@@ -1322,31 +1323,154 @@ static int reconnect(void)
The different commands
***************************************************************************/
+int mysql_real_query_for_lazy(const char *buf, int length)
+{
+ for (uint retry=0;; retry++)
+ {
+ if (!mysql_real_query(&mysql,buf,length))
+ return 0;
+ uint error=put_info(mysql_error(&mysql),INFO_ERROR, mysql_errno(&mysql));
+ if (mysql_errno(&mysql) != CR_SERVER_GONE_ERROR || retry > 1
+ || status.batch)
+ return error;
+ if (reconnect())
+ return error;
+ }
+}
+
+int mysql_store_result_for_lazy(MYSQL_RES **result)
+{
+ if ((*result=mysql_store_result(&mysql)))
+ return 0;
+
+ if (mysql_error(&mysql)[0])
+ return put_info(mysql_error(&mysql),INFO_ERROR,mysql_errno(&mysql));
+
+ return 0;
+}
+
+static int com_server_help(String *buffer __attribute__((unused)),
+ char *line __attribute__((unused)), char *help_arg)
+{
+ MYSQL_ROW cur;
+ const char *server_cmd= buffer->ptr();
+ char cmd_buf[100];
+
+ if (help_arg[0]!='\'')
+ {
+ (void*)sprintf(cmd_buf,"help \'%s\';",help_arg);
+ server_cmd= cmd_buf;
+ }
+
+ char buff[16], time_buf[32];
+ MYSQL_RES *result;
+ ulong timer;
+ uint error= 0;
+
+ if (!status.batch)
+ {
+ old_buffer= *buffer;
+ old_buffer.copy();
+ }
+
+ if (!connected && reconnect())
+ return 1;
+
+ timer= start_timer();
+
+ error= mysql_real_query_for_lazy(server_cmd,strlen(server_cmd));
+ if (error)
+ return error;
+
+ error= mysql_store_result_for_lazy(&result);
+ if (error)
+ return error;
+
+ if (result)
+ {
+ int num_rows= mysql_num_rows(result);
+ if (num_rows==1)
+ {
+ if (!(cur= mysql_fetch_row(result)))
+ return -1;
+
+ init_pager();
+ if (cur[1][0]=='Y')
+ {
+ tee_fprintf(PAGER, "\nHelp topic \'%s\'\n", cur[0]);
+ tee_fprintf(PAGER, "%s\n", cur[2]);
+ tee_fprintf(PAGER, "For help on specific function please type 'help <function>' where function is one of next :\n%s\n", cur[3]);
+ }
+ else
+ {
+ tee_fprintf(PAGER, "\nName : \'%s\'\n\n", cur[0]);
+ tee_fprintf(PAGER, "Description : \n%s\n\n", cur[2]);
+ tee_fprintf(PAGER, "Examples : \n%s\n", cur[3]);
+ }
+ end_pager();
+ }
+ else if (num_rows>1)
+ {
+ put_info("\nMany help items for your request exist", INFO_INFO);
+ put_info("For more specific request please type 'help <item>' where item is one of next :", INFO_INFO);
+
+ init_pager();
+ char last_char= '_';
+ while ((cur= mysql_fetch_row(result))){
+ if (cur[1][0]!=last_char){
+ put_info("-------------------------------------------", INFO_INFO);
+ put_info(cur[1][0]=='Y' ?
+ "categories:" : "functions:", INFO_INFO);
+ put_info("-------------------------------------------", INFO_INFO);
+ }
+ last_char= cur[1][0];
+ tee_fprintf(PAGER, "%s\n", cur[0]);
+ }
+ tee_fprintf(PAGER, "\n");
+ end_pager();
+ }
+ else
+ {
+ put_info("\nNothing found\n", INFO_INFO);
+ }
+ }
+
+ mysql_free_result(result);
+ return error;
+}
+
static int
com_help (String *buffer __attribute__((unused)),
char *line __attribute__((unused)))
{
reg1 int i;
+ char * help_arg= strchr(line,' ');
- put_info("\nFor the complete MySQL Manual online visit:\n http://www.mysql.com/documentation\n", INFO_INFO);
- put_info("For info on technical support from MySQL developers visit:\n http://www.mysql.com/support\n", INFO_INFO);
- put_info("For info on MySQL books, utilities, consultants, etc. visit:\n http://www.mysql.com/portal\n", INFO_INFO);
- put_info("List of all MySQL commands:", INFO_INFO);
- if (!named_cmds)
- put_info(" (Commands must appear first on line and end with ';')\n",
- INFO_INFO);
- for (i = 0; commands[i].name; i++)
+ if (help_arg)
{
- if (commands[i].func)
- tee_fprintf(stdout, "%s\t(\\%c)\t%s\n", commands[i].name,
- commands[i].cmd_char, commands[i].doc);
+ return com_server_help(buffer,line,help_arg+1);
}
- if (connected)
- tee_fprintf(stdout,
+ else
+ {
+ put_info("\nFor the complete MySQL Manual online visit:\n http://www.mysql.com/documentation\n", INFO_INFO);
+ put_info("For info on technical support from MySQL developers visit:\n http://www.mysql.com/support\n", INFO_INFO);
+ put_info("For info on MySQL books, utilities, consultants, etc. visit:\n http://www.mysql.com/portal\n", INFO_INFO);
+ put_info("List of all MySQL commands:", INFO_INFO);
+ if (!named_cmds)
+ put_info("Note that all text commands must be first on line and end with ';'",INFO_INFO);
+ for (i = 0; commands[i].name; i++)
+ {
+ if (commands[i].func)
+ tee_fprintf(stdout, "%s\t(\\%c)\t%s\n", commands[i].name,
+ commands[i].cmd_char, commands[i].doc);
+ }
+ if (connected)
+ tee_fprintf(stdout,
"\nConnection id: %ld (Can be used with mysqladmin kill)\n\n",
mysql_thread_id(&mysql));
- else
- tee_fprintf(stdout, "Not connected! Reconnect with 'connect'!\n\n");
+ else
+ tee_fprintf(stdout, "Not connected! Reconnect with 'connect'!\n\n");
+ }
return 0;
}
@@ -1411,23 +1535,14 @@ com_go(String *buffer,char *line __attribute__((unused)))
}
timer=start_timer();
- for (uint retry=0;; retry++)
+
+ error= mysql_real_query_for_lazy(buffer->ptr(),buffer->length());
+ if (error)
{
- if (!mysql_real_query(&mysql,buffer->ptr(),buffer->length()))
- break;
- error=put_info(mysql_error(&mysql),INFO_ERROR, mysql_errno(&mysql));
- if (mysql_errno(&mysql) != CR_SERVER_GONE_ERROR || retry > 1
- || status.batch)
- {
- buffer->length(0); // Remove query on error
- return error;
- }
- if (reconnect())
- {
- buffer->length(0); // Remove query on error
- return error;
- }
+ buffer->length(0); // Remove query on error
+ return error;
}
+
error=0;
buffer->length(0);
@@ -1440,13 +1555,9 @@ com_go(String *buffer,char *line __attribute__((unused)))
}
else
{
- if (!(result=mysql_store_result(&mysql)))
- {
- if (mysql_error(&mysql)[0])
- {
- return put_info(mysql_error(&mysql),INFO_ERROR,mysql_errno(&mysql));
- }
- }
+ error= mysql_store_result_for_lazy(&result);
+ if (error)
+ return error;
}
if (verbose >= 3 || !opt_silent)
@@ -2772,3 +2883,5 @@ void sql_element_free(void *ptr)
my_free((gptr) ptr,MYF(0));
}
#endif /* EMBEDDED_LIBRARY */
+
+
diff --git a/include/mysqld_error.h b/include/mysqld_error.h
index fc5df43f309..7fb741e5be2 100644
--- a/include/mysqld_error.h
+++ b/include/mysqld_error.h
@@ -258,4 +258,5 @@
#define ER_SUBSELECT_NO_1_COL 1239
#define ER_SUBSELECT_NO_1_ROW 1240
#define ER_UNKNOWN_STMT_HANDLER 1241
+#define ER_CORRUPT_HELP_DB 1242
#define ER_ERROR_MESSAGES 242
diff --git a/scripts/Makefile.am b/scripts/Makefile.am
index 1469f3f2f2d..72c869f3f64 100644
--- a/scripts/Makefile.am
+++ b/scripts/Makefile.am
@@ -52,11 +52,13 @@ EXTRA_SCRIPTS = make_binary_distribution.sh \
mysql_explain_log.sh \
mysqld_multi.sh \
mysql_tableinfo.sh \
- mysqld_safe.sh
+ mysqld_safe.sh \
+ fill_func_tables.sh
EXTRA_DIST = $(EXTRA_SCRIPTS) \
mysqlaccess.conf \
- mysqlbug
+ mysqlbug \
+ fill_func_tables.sql
pkgdata_DATA = make_binary_distribution
@@ -76,7 +78,8 @@ CLEANFILES = @server_scripts@ \
mysql_find_rows \
mysqlhotcopy \
mysqldumpslow \
- mysqld_multi
+ mysqld_multi \
+ fill_func_tables.sql
SUPERCLEANFILES = mysqlbug
@@ -127,3 +130,8 @@ SUFFIXES = .sh
# Don't update the files from bitkeeper
%::SCCS/s.%
+
+all: fill_func_tables.sql
+
+fill_func_tables.sql: fill_func_tables ../Docs/manual.texi
+ ./fill_func_tables < ../Docs/manual.texi > fill_func_tables.sql \ No newline at end of file
diff --git a/scripts/fill_func_tables.sh b/scripts/fill_func_tables.sh
new file mode 100644
index 00000000000..9831e68df27
--- /dev/null
+++ b/scripts/fill_func_tables.sh
@@ -0,0 +1,234 @@
+#!@PERL@
+# fill_func_tables - parse ../Docs/manual.texi
+
+# Original version by vva
+
+my $cat_name= "";
+my $func_name= "";
+my $text= "";
+my $example= "";
+
+local $mode= "";
+
+sub prepare_name
+{
+ my ($a)= @_;
+
+ $a =~ s/(\@itemize \@bullet)/ /g;
+ $a =~ s/(\@end itemize)/ /g;
+ $a =~ s/(\@end multitable)/ /g;
+ $a =~ s/(\@end table)/ /g;
+ $a =~ s/(\@cindex(.*?)\n)/ /g;
+ $a =~ s/(\@multitable \@columnfractions(.*?)\n)/ /g;
+ $a =~ s/(\@node(.*?)\n)/ /g;
+ $a =~ s/(\@tab)/\t/g;
+ $a =~ s/\@item/ /g;
+ $a =~ s/\@code\{((.|\n)+?)\}/$1/go;
+ $a =~ s/\@strong\{(.+?)\}/$1/go;
+ $a =~ s/\@samp\{(.+?)\}/$1/go;
+ $a =~ s/\@emph\{((.|\n)+?)\}/\/$1\//go;
+ $a =~ s/\@xref\{((.|\n)+?)\}/See also : [$1]/go;
+ $a =~ s/\@ref\{((.|\n)+?)\}/[$1]/go;
+ $a =~ s/\'/\'\'/g;
+ $a =~ s/\\/\\\\/g;
+ $a =~ s/\`/\`\`/g;
+
+ $a =~ s/\@table \@code/ /g;
+
+ $a =~ s/\(\)//g;
+
+ $a =~ s/((\w|\s)+)\(([\+-=><\/%*!<>\s]+)\)/$3/gxs; #$a =~ s/((\w|\s)+)\(([\+-=><\/%*!<>\s]+)\)/$3 $1/gxs;
+ $a =~ s/([\+-=><\/%*!<>\s]+)\(((\w|\s)+)\)/$1/gxs;#$a =~ s/([\+-=><\/%*!<>\s]+)\(((\w|\s)+)\)/$1 $2/gxs;
+ $a =~ s/((\w|\s)+)\((.+)\)/$1/gxs;
+
+ return $a;
+}
+
+sub prepare_text
+{
+ my ($a)= @_;
+
+ $a =~ s/(\@itemize \@bullet)/ /g;
+ $a =~ s/(\@end itemize)/ /g;
+ $a =~ s/(\@end multitable)/ /g;
+ $a =~ s/(\@end table)/ /g;
+ $a =~ s/(\@cindex(.*?)\n)/ /g;
+ $a =~ s/(\@multitable \@columnfractions(.*?)\n)/ /g;
+ $a =~ s/(\@node(.*?)\n)/ /g;
+ $a =~ s/(\@tab)/\t/g;
+ $a =~ s/\@itemx/ /g;
+ $a =~ s/\@item/ /g;
+ $a =~ s/\@code\{((.|\n)+?)\}/$1/go;
+ $a =~ s/\@strong\{(.+?)\}/$1/go;
+ $a =~ s/\@samp\{(.+?)\}/$1/go;
+ $a =~ s/\@emph\{((.|\n)+?)\}/\/$1\//go;
+ $a =~ s/\@xref\{((.|\n)+?)\}/See also : [$1]/go;
+ $a =~ s/\@ref\{((.|\n)+?)\}/[$1]/go;
+ $a =~ s/\'/\'\'/g;
+ $a =~ s/\\/\\\\/g;
+ $a =~ s/\`/\`\`/g;
+ $a =~ s/(\n*?)$//g;
+ $a =~ s/\n/\\n/g;
+
+ $a =~ s/\@table \@code/ /g;
+
+ return $a;
+}
+
+sub prepare_example
+{
+ my ($a)= @_;
+
+ $a =~ s/\'/\'\'/g;
+ $a =~ s/\\/\\\\/g;
+ $a =~ s/\`/\`\`/g;
+ $a =~ s/(\n*?)$//g;
+ $a =~ s/\n/\\n/g;
+
+ return $a;
+}
+
+sub flush_all
+{
+ my ($mode) = @_;
+
+ if ($mode eq ""){return;}
+
+ $func_name= prepare_name($func_name);
+ $text= prepare_text($text);
+ $example= prepare_example($example);
+
+ if ($func_name ne "" && $text ne "" && !($func_name =~ /[abcdefghikjlmnopqrstuvwxyz]/)){
+ print "INSERT INTO function (name,description,example) VALUES (";
+ print "'$func_name',";
+ print "'$text',";
+ print "'$example'";
+ print ");\n";
+ print "INSERT INTO function_category (cat_id,func_id) VALUES (\@cur_category,LAST_INSERT_ID());\n";
+ }
+
+ $func_name= "";
+ $text= "";
+ $example= "";
+ $mode= "";
+}
+
+sub new_category
+{
+ my ($category)= @_;
+
+ $category= prepare_text($category);
+
+ print "INSERT INTO function_category_name (name) VALUES (\'$category\');\n";
+ print "SELECT \@cur_category:=LAST_INSERT_ID();\n";
+}
+
+print "INSERT INTO db (Host,DB,User,Select_priv) VALUES ('%','mysql_help','','Y');\n";
+print "CREATE DATABASE mysql_help;\n";
+
+print "USE mysql_help;\n";
+
+print "DROP TABLE IF EXISTS function;\n";
+print "CREATE TABLE function (";
+print " func_id int unsigned not null auto_increment,";
+print " name varchar(64) not null,";
+print " url varchar(128) not null,";
+print " description text not null,";
+print " example text not null,";
+print " min_args tinyint not null,";
+print " max_args tinyint,";
+print " date_created datetime not null,";
+print " last_modified timestamp not null,";
+print " primary key (func_id)";
+print ") type=myisam;\n\n";
+
+print "DROP TABLE IF EXISTS function_category_name;\n";
+print "CREATE TABLE function_category_name (";
+print " cat_id smallint unsigned not null auto_increment,";
+print " name varchar(64) not null,";
+print " url varchar(128) not null,";
+print " date_created datetime not null,";
+print " last_modified timestamp not null,";
+print " primary key (cat_id)";
+print ") type=myisam;\n\n";
+
+print "DROP TABLE IF EXISTS function_category;\n";
+print "CREATE TABLE function_category (";
+print " cat_id smallint unsigned not null references function_category_name,";
+print " func_id int unsigned not null references function,";
+print " primary key (cat_id, func_id)";
+print ") type=myisam;\n\n";
+
+print "DELETE FROM function_category_name;\n";
+print "DELETE FROM function_category;\n";
+print "DELETE FROM function;\n";
+print "SELECT \@cur_category:=null;\n\n";
+
+my $in_section_6_3= 0;
+
+for(<>)
+{
+ if ($_=~/\@section Functions for Use in \@code{SELECT} and \@code{WHERE} Clauses/ &&
+ !$in_section_6_3){
+ $in_section_6_3= 1;
+ next;
+ }
+
+ if ($_=~/\@section/ && $in_section_6_3){
+ $in_section_6_3= 0;
+ next;
+ }
+
+ if (!$in_section_6_3) { next; }
+
+ my $c_name= "";
+
+ ($c_name)=m|\@c for_mysql_help,(.+?)$|;
+ if (!($c_name eq "") && ! ($c_name =~ m/$cat_name/i)){
+ ($cat_name)= $c_name;
+ new_category($cat_name);
+ next;
+ }
+
+ ($c_name)=m|\@subsubsection (.+?)$|;
+ if (!($c_name eq "") && ! ($c_name =~ m/$cat_name/i)){
+ ($cat_name)= $c_name;
+ new_category($cat_name);
+ next;
+ }
+
+ ($c_name)=m|\@subsection (.+?)$|;
+ if (!($c_name eq "") && ! ($c_name =~ m/$cat_name/i)){
+ ($cat_name)= $c_name;
+ new_category($cat_name);
+ next;
+ }
+
+ ($f_name)=m|\@findex (.+?)$|;
+ if (!($f_name eq "")){
+ flush_all($mode);
+ ($func_name)= ($f_name);
+ $mode= "text";
+ next;
+ }
+
+ if ($_=~/\@example/ && ($mode eq "text")){
+ $mode= "example";
+ next;
+ }
+
+ if ($_=~/\@end example/ && ($mode eq "example")){
+ flush_all($mode);
+ next;
+ }
+
+ if ($mode eq "text") { $text .= $_; }
+ if ($mode eq "example") { $example .= $_; }
+}
+
+
+print "DELETE function_category_name ";
+print "FROM function_category_name ";
+print "LEFT JOIN function_category ON function_category.cat_id=function_category_name.cat_id ";
+print "WHERE function_category.cat_id is null;"
+
diff --git a/scripts/mysql_install_db.sh b/scripts/mysql_install_db.sh
index 2f27f5d7c1a..4822e816a12 100644
--- a/scripts/mysql_install_db.sh
+++ b/scripts/mysql_install_db.sh
@@ -307,8 +307,8 @@ then
fi
echo "Installing all prepared tables"
-if eval "$execdir/mysqld $defaults --bootstrap --skip-grant-tables \
- --basedir=$basedir --datadir=$ldata --skip-innodb --skip-bdb $args" << END_OF_DATA
+if (
+ cat << END_OF_DATA
use mysql;
$c_d
$i_d
@@ -325,6 +325,9 @@ $i_f
$c_t
$c_c
END_OF_DATA
+ cat fill_func_tables.sql
+) | eval "$execdir/mysqld $defaults --bootstrap --skip-grant-tables \
+ --basedir=$basedir --datadir=$ldata --skip-innodb --skip-bdb $args"
then
echo ""
if test "$IN_RPM" -eq 0
diff --git a/sql/Makefile.am b/sql/Makefile.am
index 606cc3ec7c0..09fedd393c2 100644
--- a/sql/Makefile.am
+++ b/sql/Makefile.am
@@ -84,7 +84,7 @@ mysqld_SOURCES = sql_lex.cc sql_handler.cc \
slave.cc sql_repl.cc sql_union.cc sql_derived.cc \
mini_client.cc mini_client_errors.c \
stacktrace.c repl_failsafe.h repl_failsafe.cc sql_olap.cc\
- gstream.cc spatial.cc
+ gstream.cc spatial.cc sql_help.cc
gen_lex_hash_SOURCES = gen_lex_hash.cc
gen_lex_hash_LDADD = $(LDADD) $(CXXLDFLAGS)
diff --git a/sql/lex.h b/sql/lex.h
index 6e36e01fbe7..4b56eb4b5d8 100644
--- a/sql/lex.h
+++ b/sql/lex.h
@@ -172,6 +172,7 @@ static SYMBOL symbols[] = {
{ "HANDLER", SYM(HANDLER_SYM),0,0},
{ "HASH", SYM(HASH_SYM),0,0},
{ "HEAP", SYM(HEAP_SYM),0,0},
+ { "HELP", SYM(HELP),0,0},
{ "HIGH_PRIORITY", SYM(HIGH_PRIORITY),0,0},
{ "HOUR", SYM(HOUR_SYM),0,0},
{ "HOUR_MINUTE", SYM(HOUR_MINUTE_SYM),0,0},
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index d1a3c7e235a..f261c9f4632 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -505,6 +505,7 @@ int mysqld_show_charsets(THD *thd,const char *wild);
int mysqld_show_table_types(THD *thd);
int mysqld_show_privileges(THD *thd);
int mysqld_show_column_types(THD *thd);
+int mysqld_help (THD *thd, const char *text);
/* sql_prepare.cc */
int compare_prep_stmt(PREP_STMT *a, PREP_STMT *b, void *not_used);
diff --git a/sql/share/czech/errmsg.txt b/sql/share/czech/errmsg.txt
index f9cde2e9cd7..62ce0e83f7b 100644
--- a/sql/share/czech/errmsg.txt
+++ b/sql/share/czech/errmsg.txt
@@ -252,3 +252,4 @@
"Subselect returns more than 1 field",
"Subselect returns more than 1 record",
"Unknown prepared statement handler (%ld) given to %s",
+"Corrupt or doesn\'t exist help database",
diff --git a/sql/share/danish/errmsg.txt b/sql/share/danish/errmsg.txt
index 650d3729530..d779f4673d7 100644
--- a/sql/share/danish/errmsg.txt
+++ b/sql/share/danish/errmsg.txt
@@ -246,3 +246,4 @@
"Subselect returns more than 1 field",
"Subselect returns more than 1 record",
"Unknown prepared statement handler (%ld) given to %s",
+"Corrupt or doesn\'t exist help database",
diff --git a/sql/share/dutch/errmsg.txt b/sql/share/dutch/errmsg.txt
index 3b6e136a537..76146a5c840 100644
--- a/sql/share/dutch/errmsg.txt
+++ b/sql/share/dutch/errmsg.txt
@@ -254,3 +254,4 @@
"Subselect returns more than 1 field",
"Subselect returns more than 1 record",
"Unknown prepared statement handler (%ld) given to %s",
+"Corrupt or doesn\'t exist help database",
diff --git a/sql/share/english/errmsg.txt b/sql/share/english/errmsg.txt
index a0c11fe7fb3..ea145d2183b 100644
--- a/sql/share/english/errmsg.txt
+++ b/sql/share/english/errmsg.txt
@@ -243,3 +243,4 @@
"Subselect returns more than 1 field",
"Subselect returns more than 1 record",
"Unknown prepared statement handler (%ld) given to %s",
+"Corrupt or doesn\'t exist help database",
diff --git a/sql/share/estonian/errmsg.txt b/sql/share/estonian/errmsg.txt
index 1a96fdca15e..bed65b7b843 100644
--- a/sql/share/estonian/errmsg.txt
+++ b/sql/share/estonian/errmsg.txt
@@ -248,3 +248,4 @@
"Subselect returns more than 1 field",
"Subselect returns more than 1 record",
"Unknown prepared statement handler (%ld) given to %s",
+"Corrupt or doesn\'t exist help database",
diff --git a/sql/share/french/errmsg.txt b/sql/share/french/errmsg.txt
index 5a02393a39b..8a2f89dc141 100644
--- a/sql/share/french/errmsg.txt
+++ b/sql/share/french/errmsg.txt
@@ -243,3 +243,4 @@
"Subselect returns more than 1 field",
"Subselect returns more than 1 record",
"Unknown prepared statement handler (%ld) given to %s",
+"Corrupt or doesn\'t exist help database",
diff --git a/sql/share/german/errmsg.txt b/sql/share/german/errmsg.txt
index 464f711eb96..2f18dd4176d 100644
--- a/sql/share/german/errmsg.txt
+++ b/sql/share/german/errmsg.txt
@@ -246,3 +246,4 @@
"Subselect return more than 1 field",
"Subselect return more than 1 record",
"Unknown prepared statement handler (%ld) given to %s",
+"Corrupt or doesn\'t exist help database",
diff --git a/sql/share/greek/errmsg.txt b/sql/share/greek/errmsg.txt
index b48544f2f02..7d423638ca1 100644
--- a/sql/share/greek/errmsg.txt
+++ b/sql/share/greek/errmsg.txt
@@ -243,3 +243,4 @@
"Subselect returns more than 1 field",
"Subselect returns more than 1 record",
"Unknown prepared statement handler (%ld) given to %s",
+"Corrupt or doesn\'t exist help database",
diff --git a/sql/share/hungarian/errmsg.txt b/sql/share/hungarian/errmsg.txt
index 38427826823..75255b06288 100644
--- a/sql/share/hungarian/errmsg.txt
+++ b/sql/share/hungarian/errmsg.txt
@@ -245,3 +245,4 @@
"Subselect returns more than 1 field",
"Subselect returns more than 1 record",
"Unknown prepared statement handler (%ld) given to %s",
+"Corrupt or doesn\'t exist help database",
diff --git a/sql/share/italian/errmsg.txt b/sql/share/italian/errmsg.txt
index 504d8af08d6..a2ee5ebd264 100644
--- a/sql/share/italian/errmsg.txt
+++ b/sql/share/italian/errmsg.txt
@@ -243,3 +243,4 @@
"Subselect returns more than 1 field",
"Subselect returns more than 1 record",
"Unknown prepared statement handler (%ld) given to %s",
+"Corrupt or doesn\'t exist help database",
diff --git a/sql/share/japanese/errmsg.txt b/sql/share/japanese/errmsg.txt
index 1d9f0c5b884..00a63fe392d 100644
--- a/sql/share/japanese/errmsg.txt
+++ b/sql/share/japanese/errmsg.txt
@@ -245,3 +245,4 @@
"Subselect returns more than 1 field",
"Subselect returns more than 1 record",
"Unknown prepared statement handler (%ld) given to %s",
+"Corrupt or doesn\'t exist help database",
diff --git a/sql/share/korean/errmsg.txt b/sql/share/korean/errmsg.txt
index 6154d42ff15..abdcf5cba25 100644
--- a/sql/share/korean/errmsg.txt
+++ b/sql/share/korean/errmsg.txt
@@ -243,3 +243,4 @@
"Subselect returns more than 1 field",
"Subselect returns more than 1 record",
"Unknown prepared statement handler (%ld) given to %s",
+"Corrupt or doesn\'t exist help database",
diff --git a/sql/share/norwegian-ny/errmsg.txt b/sql/share/norwegian-ny/errmsg.txt
index e3bafc5f53b..b7bae956d0d 100644
--- a/sql/share/norwegian-ny/errmsg.txt
+++ b/sql/share/norwegian-ny/errmsg.txt
@@ -245,3 +245,4 @@
"Subselect returns more than 1 field",
"Subselect returns more than 1 record",
"Unknown prepared statement handler (%ld) given to %s",
+"Corrupt or doesn\'t exist help database",
diff --git a/sql/share/norwegian/errmsg.txt b/sql/share/norwegian/errmsg.txt
index b763d5600be..5513e272d1a 100644
--- a/sql/share/norwegian/errmsg.txt
+++ b/sql/share/norwegian/errmsg.txt
@@ -245,3 +245,4 @@
"Subselect returns more than 1 field",
"Subselect returns more than 1 record",
"Unknown prepared statement handler (%ld) given to %s",
+"Corrupt or doesn\'t exist help database",
diff --git a/sql/share/polish/errmsg.txt b/sql/share/polish/errmsg.txt
index 03f5cc260ec..298fe0daf6e 100644
--- a/sql/share/polish/errmsg.txt
+++ b/sql/share/polish/errmsg.txt
@@ -247,3 +247,4 @@
"Subselect returns more than 1 field",
"Subselect returns more than 1 record",
"Unknown prepared statement handler (%ld) given to %s",
+"Corrupt or doesn\'t exist help database",
diff --git a/sql/share/portuguese/errmsg.txt b/sql/share/portuguese/errmsg.txt
index c16a66e6fe1..26779883f38 100644
--- a/sql/share/portuguese/errmsg.txt
+++ b/sql/share/portuguese/errmsg.txt
@@ -243,3 +243,4 @@
"Subselect returns more than 1 field",
"Subselect returns more than 1 record",
"Unknown prepared statement handler (%ld) given to %s",
+"Corrupt or doesn\'t exist help database",
diff --git a/sql/share/romanian/errmsg.txt b/sql/share/romanian/errmsg.txt
index 4c4b173a3a8..408899985af 100644
--- a/sql/share/romanian/errmsg.txt
+++ b/sql/share/romanian/errmsg.txt
@@ -247,3 +247,4 @@
"Subselect returns more than 1 field",
"Subselect returns more than 1 record",
"Unknown prepared statement handler (%ld) given to %s",
+"Corrupt or doesn\'t exist help database",
diff --git a/sql/share/russian/errmsg.txt b/sql/share/russian/errmsg.txt
index a87374542d3..aec08139312 100644
--- a/sql/share/russian/errmsg.txt
+++ b/sql/share/russian/errmsg.txt
@@ -246,3 +246,4 @@
"Подзапрос возвращает более одного поля",
"Подзапрос возвращает более одной записи",
"Unknown prepared statement handler (%ld) given to %s",
+"Corrupt or doesn\'t exist help database",
diff --git a/sql/share/serbian/errmsg.txt b/sql/share/serbian/errmsg.txt
index be775b6c59e..3a42fab5c81 100644
--- a/sql/share/serbian/errmsg.txt
+++ b/sql/share/serbian/errmsg.txt
@@ -239,3 +239,4 @@
"Subselect returns more than 1 field",
"Subselect returns more than 1 record",
"Unknown prepared statement handler (%ld) given to %s",
+"Corrupt or doesn\'t exist help database",
diff --git a/sql/share/slovak/errmsg.txt b/sql/share/slovak/errmsg.txt
index 300cb488255..51a67fa673d 100644
--- a/sql/share/slovak/errmsg.txt
+++ b/sql/share/slovak/errmsg.txt
@@ -251,3 +251,4 @@
"Subselect returns more than 1 field",
"Subselect returns more than 1 record",
"Unknown prepared statement handler (%ld) given to %s",
+"Corrupt or doesn\'t exist help database",
diff --git a/sql/share/spanish/errmsg.txt b/sql/share/spanish/errmsg.txt
index f24d7997cf5..01243179d30 100644
--- a/sql/share/spanish/errmsg.txt
+++ b/sql/share/spanish/errmsg.txt
@@ -244,3 +244,4 @@
"Subselect returns more than 1 field",
"Subselect returns more than 1 record",
"Unknown prepared statement handler (%ld) given to %s",
+"Corrupt or doesn\'t exist help database",
diff --git a/sql/share/swedish/errmsg.txt b/sql/share/swedish/errmsg.txt
index 33f88c7c147..8d01e4f402a 100644
--- a/sql/share/swedish/errmsg.txt
+++ b/sql/share/swedish/errmsg.txt
@@ -243,3 +243,4 @@
"Subselect returns more than 1 field",
"Subselect returns more than 1 record",
"Unknown prepared statement handler (%ld) given to %s",
+"Corrupt or doesn\'t exist help database",
diff --git a/sql/share/ukrainian/errmsg.txt b/sql/share/ukrainian/errmsg.txt
index 539a40acc7a..473928aaf0a 100644
--- a/sql/share/ukrainian/errmsg.txt
+++ b/sql/share/ukrainian/errmsg.txt
@@ -248,3 +248,4 @@
"Пiдзапит поверта╓ бiльш нiж 1 стовбець",
"Пiдзапит поверта╓ бiльш нiж 1 запис",
"Unknown prepared statement handler (%ld) given to %s",
+"Corrupt or doesn\'t exist help database",
diff --git a/sql/sql_help.cc b/sql/sql_help.cc
new file mode 100644
index 00000000000..72cf1fdff7d
--- /dev/null
+++ b/sql/sql_help.cc
@@ -0,0 +1,408 @@
+/* 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
+***************************************************************************/
+
+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(system_charset_info,lname,mask))
+ continue;
+ count++;
+
+ if (count>2)
+ {
+ String *s= new String(lname,system_charset_info);
+ if (!s->copy())
+ names->push_back(s);
+ }
+ else if (count==1)
+ {
+ *description= new String(leaf.get_description(),system_charset_info);
+ *example= new String(leaf.get_example(),system_charset_info);
+ *name= new String(lname,system_charset_info);
+ (*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,system_charset_info);
+ 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(system_charset_info,lname,mask))
+ continue;
+ count++;
+
+ if (count==1 && res_id)
+ *res_id= category.get_cat_id();
+
+ String *s= new String(lname,system_charset_info);
+ if (!s->copy())
+ names->push_back(s);
+ }
+
+ mi_close(file_categories);
+ DBUG_RETURN(count);
+}
+
+int send_variant_2_list(THD *thd, List<String> *names, my_bool is_category)
+{
+ DBUG_ENTER("send_names");
+
+ List_iterator<String> it(*names);
+ String *cur_name;
+ String *packet= &thd->packet;
+ while ((cur_name = it++))
+ {
+ packet->length(0);
+ net_store_data(packet, cur_name->ptr());
+ net_store_data(packet, is_category ? "Y" : "N");
+ if (my_net_write(&thd->net,(char*) packet->ptr(),packet->length()))
+ 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(),system_charset_info);
+ 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(THD *thd, 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("is_category",1));
+ field_list.push_back(new Item_empty_string("description",1000));
+ field_list.push_back(new Item_empty_string("example",1000));
+
+ if (send_fields(thd,field_list,1))
+ DBUG_RETURN(1);
+
+ String *packet= &thd->packet;
+ packet->length(0);
+ net_store_data(packet, s1);
+ net_store_data(packet, s2);
+ net_store_data(packet, s3);
+ net_store_data(packet, s4);
+
+ if (my_net_write(&thd->net,(char*) packet->ptr(),packet->length()))
+ DBUG_RETURN(-1);
+
+ DBUG_RETURN(0);
+}
+
+int send_header_2(THD *thd)
+{
+ 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("is_category",1));
+ DBUG_RETURN(send_fields(thd,field_list,1));
+}
+
+int mysqld_help (THD *thd, const char *mask)
+{
+ 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(thd, categories_list.head()->ptr(),
+ "Y","",example.ptr()))
+ goto end;
+ }
+ else
+ {
+ if ((res= send_header_2(thd)) ||
+ (count==0 &&
+ (search_categories(thd, 0, &categories_list, 0)<0 &&
+ (res= 1))) ||
+ (res= send_variant_2_list(thd,&categories_list,true)))
+ goto end;
+ }
+ }
+ else if (count==1)
+ {
+ if (res= send_answer_1(thd,name->ptr(),"N",
+ description->ptr(), example->ptr()))
+ goto end;
+ }
+ else if((res= send_header_2(thd)) ||
+ (res= send_variant_2_list(thd,&function_list,false)) ||
+ (search_categories(thd, mask, &categories_list, 0)<0 &&
+ (res=1)) ||
+ (res= send_variant_2_list(thd,&categories_list,true)))
+ {
+ goto end;
+ }
+
+ send_eof(thd);
+
+end:
+ mi_close(file_leafs);
+ DBUG_RETURN(res);
+}
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index fa886f9d60a..dca3fe7e7df 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -66,7 +66,7 @@ enum enum_sql_command {
SQLCOM_SHOW_BINLOG_EVENTS, SQLCOM_SHOW_NEW_MASTER, SQLCOM_DO,
SQLCOM_SHOW_WARNS, SQLCOM_EMPTY_QUERY, SQLCOM_SHOW_ERRORS,
SQLCOM_SHOW_COLUMN_TYPES, SQLCOM_SHOW_TABLE_TYPES, SQLCOM_SHOW_PRIVILEGES,
- SQLCOM_END
+ SQLCOM_END, SQLCOM_HELP,
};
enum lex_states
@@ -383,6 +383,7 @@ typedef struct st_lex
bool derived_tables, describe;
uint slave_thd_opt;
CHARSET_INFO *charset;
+ char *help_arg;
} LEX;
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index fc9838f57b1..69f1eae9ac8 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -1486,6 +1486,10 @@ mysql_execute_command(THD *thd)
send_ok(thd);
break;
+ case SQLCOM_HELP:
+ res= mysqld_help(thd,lex->help_arg);
+ break;
+
case SQLCOM_PURGE:
{
if (check_global_access(thd, SUPER_ACL))
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 7d68278e39f..a3fe6dd7b79 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -505,6 +505,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%token SUBJECT_SYM
%token CIPHER_SYM
+%token HELP
+
%left SET_VAR
%left OR_OR_CONCAT OR
%left AND
@@ -637,7 +639,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
handler_rkey_function handler_read_or_scan
single_multi table_wild_list table_wild_one opt_wild union union_list
precision union_option opt_on_delete_item subselect_start opt_and
- subselect_end select_var_list select_var_list_init
+ subselect_end select_var_list select_var_list_init help
END_OF_INPUT
%type <NONE>
@@ -699,7 +701,18 @@ verb_clause:
| handler
| unlock
| update
- | use;
+ | use
+ | help;
+
+/* help */
+
+help:
+ HELP TEXT_STRING
+ {
+ LEX *lex= Lex;
+ lex->sql_command= SQLCOM_HELP;
+ lex->help_arg= $2.str;
+ }
/* change master */