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 ' 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 ' 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/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index 5c5b8984102..b1bf134fdde 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -5,6 +5,9 @@ SELECT (SELECT 1) UNION SELECT (SELECT 2); (SELECT 1) 1 2 +SELECT (SELECT (SELECT 0 UNION SELECT 0)); +(SELECT (SELECT 0 UNION SELECT 0)) +0 drop table if exists t1,t2,t3,t4,t5,attend,clinic,inscrit; create table t1 (a int); create table t2 (a int, b int); diff --git a/mysql-test/t/subselect.test b/mysql-test/t/subselect.test index c74922d3d9e..841e98dd96c 100644 --- a/mysql-test/t/subselect.test +++ b/mysql-test/t/subselect.test @@ -1,6 +1,6 @@ - select (select 2); SELECT (SELECT 1) UNION SELECT (SELECT 2); +SELECT (SELECT (SELECT 0 UNION SELECT 0)); drop table if exists t1,t2,t3,t4,t5,attend,clinic,inscrit; create table t1 (a int); create table t2 (a int, b int); 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 *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 *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 *names, my_bool is_category) +{ + DBUG_ENTER("send_names"); + + List_iterator 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 *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 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 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 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 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.cc b/sql/sql_lex.cc index b68316a1e4a..52f1be6bddf 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -945,7 +945,7 @@ void st_select_lex_unit::init_query() select_limit_cnt= HA_POS_ERROR; offset_limit_cnt= 0; union_option= 0; - prepared= optimized= 0; + prepared= optimized= executed= 0; item= 0; } diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 070eecb1797..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 @@ -227,8 +227,9 @@ protected: select_result *result; int res; bool describe, found_rows_for_union, - prepared, //prepare phase already performed for UNION (unit) - optimized; // optimize phase already performed for UNION (unit) + prepared, // prepare phase already performed for UNION (unit) + optimized, // optimize phase already performed for UNION (unit) + executed; // already executed public: /* Pointer to 'last' select or pointer to unit where stored @@ -382,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_union.cc b/sql/sql_union.cc index f1c80bf8546..899dceab9bc 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -199,8 +199,12 @@ int st_select_lex_unit::exec() { DBUG_ENTER("st_select_lex_unit::exec"); SELECT_LEX *lex_select_save= thd->lex.select; - - if(depended || !item || !item->assigned()) + + if (executed && !depended) + DBUG_RETURN(0); + executed= 1; + + if (depended || !item || !item->assigned()) { if (optimized && item && item->assigned()) item->assigned(0); // We will reinit & rexecute unit 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 @@ -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 */