From 813033338c451748a0e32af386d5b344940d6277 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 6 Jul 2005 16:29:31 -0700 Subject: [PATCH 01/26] Make it possible to change reconnect setting with mysql_options(..., MYSQL_OPT_RECONNECT, ...). (Bug #11787) client/mysqltest.c: Use mysql_options(..., MYSQL_OPT_RECONNECT, ...) to change reconnect include/mysql.h: Add MYSQL_OPT_RECONNECT option sql-common/client.c: Allow changing reconnect using MYSQL_OPT_RECONNECT. --- client/mysqltest.c | 37 +++++++++++++++++++++++++------------ include/mysql.h | 2 +- sql-common/client.c | 3 +++ 3 files changed, 29 insertions(+), 13 deletions(-) diff --git a/client/mysqltest.c b/client/mysqltest.c index 67fa931a3f3..dbc8c0048ba 100644 --- a/client/mysqltest.c +++ b/client/mysqltest.c @@ -1731,14 +1731,15 @@ int safe_connect(MYSQL* con, const char* host, const char* user, const char* pass, const char* db, int port, const char* sock) { - int con_error = 1; + int con_error= 1; + my_bool reconnect= 1; int i; - for (i = 0; i < MAX_CON_TRIES; ++i) + for (i= 0; i < MAX_CON_TRIES; ++i) { if (mysql_real_connect(con, host,user, pass, db, port, sock, CLIENT_MULTI_STATEMENTS)) { - con_error = 0; + con_error= 0; break; } sleep(CON_RETRY_SLEEP); @@ -1747,7 +1748,7 @@ int safe_connect(MYSQL* con, const char* host, const char* user, TODO: change this to 0 in future versions, but the 'kill' test relies on existing behavior */ - con->reconnect= 1; + mysql_options(con, MYSQL_OPT_RECONNECT, (char *)&reconnect); return con_error; } @@ -1781,6 +1782,7 @@ int connect_n_handle_errors(struct st_query *q, MYSQL* con, const char* host, int* create_conn) { DYNAMIC_STRING ds_tmp, *ds; + my_bool reconnect= 1; int error= 0; /* @@ -1846,7 +1848,7 @@ int connect_n_handle_errors(struct st_query *q, MYSQL* con, const char* host, TODO: change this to 0 in future versions, but the 'kill' test relies on existing behavior */ - con->reconnect= 1; + mysql_options(con, MYSQL_OPT_RECONNECT, (char *)&reconnect); if (record) { @@ -2090,6 +2092,7 @@ int read_line(char* buf, int size) enum {R_NORMAL, R_Q, R_Q_IN_Q, R_SLASH_IN_Q, R_COMMENT, R_LINE_START} state= R_LINE_START; DBUG_ENTER("read_line"); + LINT_INIT(quote); start_lineno= *lineno; for (; p < buf_end ;) @@ -2772,6 +2775,7 @@ static int run_query_normal(MYSQL* mysql, struct st_query* q, int flags) if (!disable_result_log) { ulong affected_rows; /* Ok to be undef if 'disable_info' is set */ + LINT_INIT(affected_rows); if (res) { @@ -3924,7 +3928,7 @@ int main(int argc, char **argv) select_connection(q->first_argument); else { - char buf[] = "slave"; + char buf[]= "slave"; select_connection(buf); } do_sync_with_master2(""); @@ -3936,7 +3940,7 @@ int main(int argc, char **argv) case Q_PING: (void) mysql_ping(&cur_con->mysql); break; - case Q_EXEC: + case Q_EXEC: do_exec(q); break; case Q_START_TIMER: @@ -3948,7 +3952,7 @@ int main(int argc, char **argv) timer_output(); got_end_timer= TRUE; break; - case Q_CHARACTER_SET: + case Q_CHARACTER_SET: set_charset(q); break; case Q_DISABLE_PS_PROTOCOL: @@ -3958,22 +3962,31 @@ int main(int argc, char **argv) ps_protocol_enabled= ps_protocol; break; case Q_DISABLE_RECONNECT: - cur_con->mysql.reconnect= 0; + { + my_bool reconnect= 0; + mysql_options(&cur_con->mysql, MYSQL_OPT_RECONNECT, (char *)&reconnect); break; + } case Q_ENABLE_RECONNECT: - cur_con->mysql.reconnect= 1; + { + my_bool reconnect= 1; + mysql_options(&cur_con->mysql, MYSQL_OPT_RECONNECT, (char *)&reconnect); break; + } case Q_EXIT: abort_flag= 1; break; - default: processed = 0; break; + + default: + processed= 0; + break; } } if (!processed) { - current_line_inc = 0; + current_line_inc= 0; switch (q->type) { case Q_WHILE: do_block(cmd_while, q); break; case Q_IF: do_block(cmd_if, q); break; diff --git a/include/mysql.h b/include/mysql.h index 84284fa0661..c77d531d0c1 100644 --- a/include/mysql.h +++ b/include/mysql.h @@ -146,7 +146,7 @@ enum mysql_option MYSQL_OPT_WRITE_TIMEOUT, MYSQL_OPT_USE_RESULT, MYSQL_OPT_USE_REMOTE_CONNECTION, MYSQL_OPT_USE_EMBEDDED_CONNECTION, MYSQL_OPT_GUESS_CONNECTION, MYSQL_SET_CLIENT_IP, MYSQL_SECURE_AUTH, - MYSQL_REPORT_DATA_TRUNCATION + MYSQL_REPORT_DATA_TRUNCATION, MYSQL_OPT_RECONNECT }; struct st_mysql_options { diff --git a/sql-common/client.c b/sql-common/client.c index c736acae857..927da747392 100644 --- a/sql-common/client.c +++ b/sql-common/client.c @@ -2750,6 +2750,9 @@ mysql_options(MYSQL *mysql,enum mysql_option option, const char *arg) case MYSQL_REPORT_DATA_TRUNCATION: mysql->options.report_data_truncation= test(*(my_bool *) arg); break; + case MYSQL_OPT_RECONNECT: + mysql->reconnect= *(my_bool *) arg; + break; default: DBUG_RETURN(1); } From 6c08c1176f3c5ae43fd8f178922c477a7edf7c9a Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 19 Jul 2005 10:03:19 +0300 Subject: [PATCH 02/26] Added InnoDB offline file checksum utility. extra/Makefile.am: Added innochecksum utility. BitKeeper/etc/ignore: Added extra/innochecksum to the ignore list --- .bzrignore | 1 + extra/Makefile.am | 2 +- extra/innochecksum.c | 302 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 304 insertions(+), 1 deletion(-) create mode 100644 extra/innochecksum.c diff --git a/.bzrignore b/.bzrignore index 9c6cf2c965c..1a527396cd6 100644 --- a/.bzrignore +++ b/.bzrignore @@ -1117,3 +1117,4 @@ vio/test-sslclient vio/test-sslserver vio/viotest-ssl ndb/src/dummy.cpp +extra/innochecksum diff --git a/extra/Makefile.am b/extra/Makefile.am index cb06e7495f9..457fddce673 100644 --- a/extra/Makefile.am +++ b/extra/Makefile.am @@ -38,7 +38,7 @@ $(top_builddir)/include/mysqld_ername.h: $(top_builddir)/include/mysqld_error.h $(top_builddir)/include/sql_state.h: $(top_builddir)/include/mysqld_error.h bin_PROGRAMS = replace comp_err perror resolveip my_print_defaults \ - resolve_stack_dump mysql_waitpid + resolve_stack_dump mysql_waitpid innochecksum noinst_PROGRAMS = charset2html # Don't update the files from bitkeeper diff --git a/extra/innochecksum.c b/extra/innochecksum.c new file mode 100644 index 00000000000..bce4214847d --- /dev/null +++ b/extra/innochecksum.c @@ -0,0 +1,302 @@ +/* Copyright (C) 2000-2005 MySQL AB & Innobase Oy + + 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 */ + +/* + InnoDB offline file checksum utility. 85% of the code in this file + was taken wholesale fron the InnoDB codebase. + + The final 15% was originally written by Mark Smith of Danga + Interactive, Inc. + + Published with a permission. +*/ + +// needed to have access to 64 bit file functions +#define _LARGEFILE_SOURCE +#define _LARGEFILE64_SOURCE + +#include +#include +#include +#include +#include +#include + +// all of these ripped from InnoDB code from MySQL 4.0.22 +#define UT_HASH_RANDOM_MASK 1463735687 +#define UT_HASH_RANDOM_MASK2 1653893711 +#define FIL_PAGE_LSN 16 +#define FIL_PAGE_FILE_FLUSH_LSN 26 +#define FIL_PAGE_OFFSET 4 +#define FIL_PAGE_DATA 38 +#define FIL_PAGE_END_LSN_OLD_CHKSUM 8 +#define FIL_PAGE_SPACE_OR_CHKSUM 0 +#define UNIV_PAGE_SIZE (2 * 8192) + +// command line argument to do page checks (that's it) +// another argument to specify page ranges... seek to right spot and go from there + +typedef unsigned long int ulint; +typedef unsigned char byte; + +/* innodb function in name; modified slightly to not have the ASM version (lots of #ifs that didn't apply) */ +ulint mach_read_from_4(byte *b) { + return( ((ulint)(b[0]) << 24) + + ((ulint)(b[1]) << 16) + + ((ulint)(b[2]) << 8) + + (ulint)(b[3]) + ); +} + +ulint +ut_fold_ulint_pair( +/*===============*/ + /* out: folded value */ + ulint n1, /* in: ulint */ + ulint n2) /* in: ulint */ +{ + return(((((n1 ^ n2 ^ UT_HASH_RANDOM_MASK2) << 8) + n1) + ^ UT_HASH_RANDOM_MASK) + n2); +} + +ulint +ut_fold_binary( +/*===========*/ + /* out: folded value */ + byte* str, /* in: string of bytes */ + ulint len) /* in: length */ +{ + ulint i; + ulint fold = 0; + + for (i = 0; i < len; i++) { + fold = ut_fold_ulint_pair(fold, (ulint)(*str)); + + str++; + } + + return(fold); +} + +ulint +buf_calc_page_new_checksum( +/*=======================*/ + /* out: checksum */ + byte* page) /* in: buffer page */ +{ + ulint checksum; + + /* Since the fields FIL_PAGE_FILE_FLUSH_LSN and ..._ARCH_LOG_NO + are written outside the buffer pool to the first pages of data + files, we have to skip them in the page checksum calculation. + We must also skip the field FIL_PAGE_SPACE_OR_CHKSUM where the + checksum is stored, and also the last 8 bytes of page because + there we store the old formula checksum. */ + + checksum = ut_fold_binary(page + FIL_PAGE_OFFSET, + FIL_PAGE_FILE_FLUSH_LSN - FIL_PAGE_OFFSET) + + ut_fold_binary(page + FIL_PAGE_DATA, + UNIV_PAGE_SIZE - FIL_PAGE_DATA + - FIL_PAGE_END_LSN_OLD_CHKSUM); + checksum = checksum & 0xFFFFFFFF; + + return(checksum); +} + +ulint +buf_calc_page_old_checksum( +/*=======================*/ + /* out: checksum */ + byte* page) /* in: buffer page */ +{ + ulint checksum; + + checksum = ut_fold_binary(page, FIL_PAGE_FILE_FLUSH_LSN); + + checksum = checksum & 0xFFFFFFFF; + + return(checksum); +} + + +int main(int argc, char **argv) { + FILE *f; // our input file + byte *p; // storage of pages read + int bytes; // bytes read count + ulint ct; // current page number (0 based) + int now; // current time + int lastt; // last time + ulint oldcsum, oldcsumfield, csum, csumfield, logseq, logseqfield; // ulints for checksum storage + struct stat64 st; // for stat, if you couldn't guess + unsigned long long int size; // size of file (has to be 64 bits) + ulint pages; // number of pages in file + ulint start_page = 0, end_page = 0, use_end_page = 0; // for starting and ending at certain pages + int just_count = 0; // if true, just print page count + int verbose = 0; + int debug = 0; + int c; + int fd; + + // remove arguments + while ((c = getopt(argc, argv, "cvds:e:p:")) != -1) { + switch (c) { + case 'v': + verbose = 1; + break; + case 'c': + just_count = 1; + break; + case 's': + start_page = atoi(optarg); + break; + case 'e': + end_page = atoi(optarg); + use_end_page = 1; + break; + case 'p': + start_page = atoi(optarg); + end_page = atoi(optarg); + use_end_page = 1; + break; + case 'd': + debug = 1; + break; + case ':': + fprintf(stderr, "option -%c requires an argument\n", optopt); + return 1; + break; + case '?': + fprintf(stderr, "unrecognized option: -%c\n", optopt); + return 1; + break; + } + } + + // debug implies verbose... + if (debug) verbose = 1; + + // make sure we have the right arguments + if (optind >= argc) { + printf("InnoDB offline file checksum utility.\n"); + printf("usage: %s [-c] [-s ] [-e ] [-p ] [-v] [-d] \n", argv[0]); + printf("\t-c\tprint the count of pages in the file\n"); + printf("\t-s n\tstart on this page number (0 based)\n"); + printf("\t-e n\tend at this page number (0 based)\n"); + printf("\t-p n\tcheck only this page (0 based)\n"); + printf("\t-v\tverbose (prints progress every 5 seconds)\n"); + printf("\t-d\tdebug mode (prints checksums for each page)\n"); + return 1; + } + + // stat the file to get size and page count + if (stat64(argv[optind], &st)) { + perror("error statting file"); + return 1; + } + size = st.st_size; + pages = size / UNIV_PAGE_SIZE; + if (just_count) { + printf("%lu\n", pages); + return 0; + } else if (verbose) { + printf("file %s = %llu bytes (%lu pages)...\n", argv[1], size, pages); + printf("checking pages in range %lu to %lu\n", start_page, use_end_page ? end_page : (pages - 1)); + } + + // open the file for reading + f = fopen64(argv[optind], "r"); + if (!f) { + perror("error opening file"); + return 1; + } + + // seek to the necessary position + if (start_page) { + fd = fileno(f); + if (!fd) { + perror("unable to obtain file descriptor number"); + return 1; + } + if (lseek64(fd, start_page * UNIV_PAGE_SIZE, SEEK_SET) != (start_page * UNIV_PAGE_SIZE)) { + perror("unable to seek to necessary offset"); + return 1; + } + } + + // allocate buffer for reading (so we don't realloc every time) + p = (byte *)malloc(UNIV_PAGE_SIZE); + + // main checksumming loop + ct = start_page; + lastt = 0; + while (!feof(f)) { + bytes = fread(p, 1, UNIV_PAGE_SIZE, f); + if (!bytes && feof(f)) return 0; + if (bytes != UNIV_PAGE_SIZE) { + fprintf(stderr, "bytes read (%d) doesn't match universal page size (%d)\n", bytes, UNIV_PAGE_SIZE); + return 1; + } + + // check the "stored log sequence numbers" + logseq = mach_read_from_4(p + FIL_PAGE_LSN + 4); + logseqfield = mach_read_from_4(p + UNIV_PAGE_SIZE - FIL_PAGE_END_LSN_OLD_CHKSUM + 4); + if (debug) + printf("page %lu: log sequence number: first = %lu; second = %lu\n", ct, logseq, logseqfield); + if (logseq != logseqfield) { + fprintf(stderr, "page %lu invalid (fails log sequence number check)\n", ct); + return 1; + } + + // check old method of checksumming + oldcsum = buf_calc_page_old_checksum(p); + oldcsumfield = mach_read_from_4(p + UNIV_PAGE_SIZE - FIL_PAGE_END_LSN_OLD_CHKSUM); + if (debug) + printf("page %lu: old style: calculated = %lu; recorded = %lu\n", ct, oldcsum, oldcsumfield); + if (oldcsumfield != mach_read_from_4(p + FIL_PAGE_LSN) && oldcsumfield != oldcsum) { + fprintf(stderr, "page %lu invalid (fails old style checksum)\n", ct); + return 1; + } + + // now check the new method + csum = buf_calc_page_new_checksum(p); + csumfield = mach_read_from_4(p + FIL_PAGE_SPACE_OR_CHKSUM); + if (debug) + printf("page %lu: new style: calculated = %lu; recorded = %lu\n", ct, csum, csumfield); + if (csumfield != 0 && csum != csumfield) { + fprintf(stderr, "page %lu invalid (fails new style checksum)\n", ct); + return 1; + } + + // end if this was the last page we were supposed to check + if (use_end_page && (ct >= end_page)) + return 0; + + // do counter increase and progress printing + ct++; + if (verbose) { + if (ct % 64 == 0) { + now = time(0); + if (!lastt) lastt = now; + if (now - lastt >= 1) { + printf("page %lu okay: %.3f%% done\n", (ct - 1), (float) ct / pages * 100); + lastt = now; + } + } + } + } + return 0; +} + From 705118d1f627c6aa9a14f2a00f080b13af7b3484 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 2 Aug 2005 22:54:49 +0300 Subject: [PATCH 03/26] issue correct error message in case of view presence for duplicated table on update (BUG#10773) frequently used command sequence replaced with inline function BitKeeper/etc/config: logging switching off mysql-test/r/lowercase_view.result: hided view underlying tables from error message mysql-test/r/view.result: hided view underlying tables from error message mysql-test/t/lowercase_view.test: hided view underlying tables from error message mysql-test/t/view.test: hided view underlying tables from error message sql/mysql_priv.h: subroutine which return correct error message sql/share/errmsg.txt: new error message sql/sql_base.cc: subroutine which issue correct error message in case of view presence for duplicated table on update sql/sql_delete.cc: issue correct error message in case of view presence for duplicated table on update sql/sql_insert.cc: issue correct error message in case of view presence for duplicated table on update sql/sql_parse.cc: issue correct error message in case of view presence for duplicated table on update sql/sql_update.cc: issue correct error message in case of view presence for duplicated table on update sql/sql_view.cc: frequently used command sequence replaced with inline function sql/table.cc: frequently used command sequence replaced with inline function sql/table.h: frequently used command sequence replaced with inline function --- BitKeeper/etc/config | 2 +- mysql-test/r/lowercase_view.result | 98 +++++++++++++++--------------- mysql-test/r/view.result | 82 ++++++++++++++----------- mysql-test/t/lowercase_view.test | 68 ++++++++++----------- mysql-test/t/view.test | 87 +++++++++++++++----------- sql/mysql_priv.h | 3 + sql/share/errmsg.txt | 2 + sql/sql_base.cc | 58 +++++++++++++++++- sql/sql_delete.cc | 21 ++++--- sql/sql_insert.cc | 10 ++- sql/sql_parse.cc | 18 +++--- sql/sql_update.cc | 23 ++++--- sql/sql_view.cc | 7 +-- sql/table.cc | 2 +- sql/table.h | 2 + 15 files changed, 293 insertions(+), 190 deletions(-) diff --git a/BitKeeper/etc/config b/BitKeeper/etc/config index 1ac24031dca..56ae08e5ffa 100644 --- a/BitKeeper/etc/config +++ b/BitKeeper/etc/config @@ -24,7 +24,7 @@ description: MySQL - fast and reliable SQL database # repository is commercial it can be an internal email address or "none" # to disable logging. # -logging: logging@openlogging.org +logging: none # # If this field is set, all checkins will appear to be made by this user, # in effect making this a single user package. Single user packages are diff --git a/mysql-test/r/lowercase_view.result b/mysql-test/r/lowercase_view.result index 37252c6dde7..1baf3246c13 100644 --- a/mysql-test/r/lowercase_view.result +++ b/mysql-test/r/lowercase_view.result @@ -16,103 +16,103 @@ create view v1Aa as select * from t1aA; create view v2aA as select * from v1aA; create view v3Aa as select v2Aa.col1 from v2aA,t2Aa where v2Aa.col1 = t2aA.col1; update v2aA set col1 = (select max(col1) from v1Aa); -ERROR HY000: You can't specify target table 'v2aa' for update in FROM clause +ERROR HY000: The definition of table 'v1Aa' prevents operation UPDATE on table 'v2aA'. update v2Aa set col1 = (select max(col1) from t1Aa); -ERROR HY000: You can't specify target table 'v2aa' for update in FROM clause +ERROR HY000: The definition of table 'v2Aa' prevents operation UPDATE on table 'v2Aa'. update v2aA set col1 = (select max(col1) from v2Aa); -ERROR HY000: You can't specify target table 'v2aa' for update in FROM clause +ERROR HY000: You can't specify target table 'v2aA' for update in FROM clause update v2aA,t2Aa set v2Aa.col1 = (select max(col1) from v1aA) where v2aA.col1 = t2aA.col1; -ERROR HY000: You can't specify target table 'v2aa' for update in FROM clause +ERROR HY000: The definition of table 'v1aA' prevents operation UPDATE on table 'v2aA'. update t1aA,t2Aa set t1Aa.col1 = (select max(col1) from v1Aa) where t1aA.col1 = t2aA.col1; -ERROR HY000: You can't specify target table 't1aa' for update in FROM clause +ERROR HY000: The definition of table 'v1Aa' prevents operation UPDATE on table 't1aA'. update v1aA,t2Aa set v1Aa.col1 = (select max(col1) from v1aA) where v1Aa.col1 = t2aA.col1; -ERROR HY000: You can't specify target table 'v1aa' for update in FROM clause +ERROR HY000: You can't specify target table 'v1aA' for update in FROM clause update t2Aa,v2Aa set v2aA.col1 = (select max(col1) from v1aA) where v2Aa.col1 = t2aA.col1; -ERROR HY000: You can't specify target table 't2aa' for update in FROM clause +ERROR HY000: The definition of table 'v1aA' prevents operation UPDATE on table 't2Aa'. update t2Aa,t1Aa set t1aA.col1 = (select max(col1) from v1Aa) where t1Aa.col1 = t2aA.col1; -ERROR HY000: You can't specify target table 't2aa' for update in FROM clause +ERROR HY000: The definition of table 'v1Aa' prevents operation UPDATE on table 't2Aa'. update t2Aa,v1aA set v1Aa.col1 = (select max(col1) from v1aA) where v1Aa.col1 = t2aA.col1; -ERROR HY000: You can't specify target table 't2aa' for update in FROM clause +ERROR HY000: The definition of table 'v1aA' prevents operation UPDATE on table 't2Aa'. update v2aA,t2Aa set v2Aa.col1 = (select max(col1) from t1aA) where v2aA.col1 = t2aA.col1; -ERROR HY000: You can't specify target table 'v2aa' for update in FROM clause +ERROR HY000: The definition of table 'v2aA' prevents operation UPDATE on table 'v2aA'. update t1Aa,t2Aa set t1aA.col1 = (select max(col1) from t1Aa) where t1aA.col1 = t2aA.col1; -ERROR HY000: You can't specify target table 't1aa' for update in FROM clause +ERROR HY000: You can't specify target table 't1Aa' for update in FROM clause update v1aA,t2Aa set v1Aa.col1 = (select max(col1) from t1Aa) where v1aA.col1 = t2aA.col1; -ERROR HY000: You can't specify target table 'v1aa' for update in FROM clause +ERROR HY000: The definition of table 'v1aA' prevents operation UPDATE on table 'v1aA'. update t2Aa,v2Aa set v2aA.col1 = (select max(col1) from t1aA) where v2Aa.col1 = t2aA.col1; -ERROR HY000: You can't specify target table 't2aa' for update in FROM clause +ERROR HY000: You can't specify target table 't2Aa' for update in FROM clause update t2Aa,t1Aa set t1aA.col1 = (select max(col1) from t1Aa) where t1aA.col1 = t2aA.col1; -ERROR HY000: You can't specify target table 't2aa' for update in FROM clause +ERROR HY000: You can't specify target table 't2Aa' for update in FROM clause update t2Aa,v1Aa set v1aA.col1 = (select max(col1) from t1Aa) where v1Aa.col1 = t2aA.col1; -ERROR HY000: You can't specify target table 't2aa' for update in FROM clause +ERROR HY000: You can't specify target table 't2Aa' for update in FROM clause update v2aA,t2Aa set v2Aa.col1 = (select max(col1) from v2aA) where v2Aa.col1 = t2aA.col1; -ERROR HY000: You can't specify target table 'v2aa' for update in FROM clause +ERROR HY000: You can't specify target table 'v2aA' for update in FROM clause update t1aA,t2Aa set t1Aa.col1 = (select max(col1) from v2aA) where t1aA.col1 = t2aA.col1; -ERROR HY000: You can't specify target table 't1aa' for update in FROM clause +ERROR HY000: The definition of table 'v2aA' prevents operation UPDATE on table 't1aA'. update v1aA,t2Aa set v1Aa.col1 = (select max(col1) from v2Aa) where v1aA.col1 = t2aA.col1; -ERROR HY000: You can't specify target table 'v1aa' for update in FROM clause +ERROR HY000: The definition of table 'v2Aa' prevents operation UPDATE on table 'v1aA'. update t2Aa,v2aA set v2Aa.col1 = (select max(col1) from v2aA) where v2Aa.col1 = t2aA.col1; -ERROR HY000: You can't specify target table 't2aa' for update in FROM clause +ERROR HY000: The definition of table 'v2aA' prevents operation UPDATE on table 't2Aa'. update t2Aa,t1Aa set t1aA.col1 = (select max(col1) from v2aA) where t1Aa.col1 = t2aA.col1; -ERROR HY000: You can't specify target table 't2aa' for update in FROM clause +ERROR HY000: The definition of table 'v2aA' prevents operation UPDATE on table 't2Aa'. update t2Aa,v1Aa set v1aA.col1 = (select max(col1) from v2Aa) where v1Aa.col1 = t2aA.col1; -ERROR HY000: You can't specify target table 't2aa' for update in FROM clause +ERROR HY000: The definition of table 'v2Aa' prevents operation UPDATE on table 't2Aa'. update v3aA set v3Aa.col1 = (select max(col1) from v1aA); -ERROR HY000: You can't specify target table 'v3aa' for update in FROM clause +ERROR HY000: The definition of table 'v1aA' prevents operation UPDATE on table 'v3aA'. update v3aA set v3Aa.col1 = (select max(col1) from t1aA); -ERROR HY000: You can't specify target table 'v3aa' for update in FROM clause +ERROR HY000: The definition of table 'v3aA' prevents operation UPDATE on table 'v3aA'. update v3aA set v3Aa.col1 = (select max(col1) from v2aA); -ERROR HY000: You can't specify target table 'v3aa' for update in FROM clause +ERROR HY000: The definition of table 'v2aA' prevents operation UPDATE on table 'v3aA'. update v3aA set v3Aa.col1 = (select max(col1) from v3aA); -ERROR HY000: You can't specify target table 'v3aa' for update in FROM clause +ERROR HY000: You can't specify target table 'v3aA' for update in FROM clause delete from v2Aa where col1 = (select max(col1) from v1Aa); -ERROR HY000: You can't specify target table 'v2aa' for update in FROM clause +ERROR HY000: The definition of table 'v1Aa' prevents operation DELETE on table 'v2Aa'. delete from v2aA where col1 = (select max(col1) from t1Aa); -ERROR HY000: You can't specify target table 'v2aa' for update in FROM clause +ERROR HY000: The definition of table 'v2aA' prevents operation DELETE on table 'v2aA'. delete from v2Aa where col1 = (select max(col1) from v2aA); -ERROR HY000: You can't specify target table 'v2aa' for update in FROM clause +ERROR HY000: You can't specify target table 'v2Aa' for update in FROM clause delete v2Aa from v2aA,t2Aa where (select max(col1) from v1aA) > 0 and v2Aa.col1 = t2aA.col1; -ERROR HY000: You can't specify target table 'v2aa' for update in FROM clause +ERROR HY000: The definition of table 'v1aA' prevents operation DELETE on table 'v2aA'. delete t1aA from t1Aa,t2Aa where (select max(col1) from v1Aa) > 0 and t1aA.col1 = t2aA.col1; -ERROR HY000: You can't specify target table 't1aa' for update in FROM clause +ERROR HY000: The definition of table 'v1Aa' prevents operation DELETE on table 't1Aa'. delete v1aA from v1Aa,t2Aa where (select max(col1) from v1aA) > 0 and v1Aa.col1 = t2aA.col1; -ERROR HY000: You can't specify target table 'v1aa' for update in FROM clause +ERROR HY000: You can't specify target table 'v1Aa' for update in FROM clause delete v2aA from v2Aa,t2Aa where (select max(col1) from t1Aa) > 0 and v2aA.col1 = t2aA.col1; -ERROR HY000: You can't specify target table 'v2aa' for update in FROM clause +ERROR HY000: The definition of table 'v2Aa' prevents operation DELETE on table 'v2Aa'. delete t1aA from t1Aa,t2Aa where (select max(col1) from t1aA) > 0 and t1Aa.col1 = t2aA.col1; -ERROR HY000: You can't specify target table 't1aa' for update in FROM clause +ERROR HY000: You can't specify target table 't1Aa' for update in FROM clause delete v1aA from v1Aa,t2Aa where (select max(col1) from t1aA) > 0 and v1aA.col1 = t2aA.col1; -ERROR HY000: You can't specify target table 'v1aa' for update in FROM clause +ERROR HY000: The definition of table 'v1Aa' prevents operation DELETE on table 'v1Aa'. delete v2Aa from v2aA,t2Aa where (select max(col1) from v2Aa) > 0 and v2aA.col1 = t2aA.col1; -ERROR HY000: You can't specify target table 'v2aa' for update in FROM clause +ERROR HY000: You can't specify target table 'v2aA' for update in FROM clause delete t1Aa from t1aA,t2Aa where (select max(col1) from v2Aa) > 0 and t1Aa.col1 = t2aA.col1; -ERROR HY000: You can't specify target table 't1aa' for update in FROM clause +ERROR HY000: The definition of table 'v2Aa' prevents operation DELETE on table 't1aA'. delete v1Aa from v1aA,t2Aa where (select max(col1) from v2aA) > 0 and v1Aa.col1 = t2aA.col1; -ERROR HY000: You can't specify target table 'v1aa' for update in FROM clause +ERROR HY000: The definition of table 'v2aA' prevents operation DELETE on table 'v1aA'. insert into v2Aa values ((select max(col1) from v1aA)); -ERROR HY000: You can't specify target table 'v2aa' for update in FROM clause +ERROR HY000: The definition of table 'v1aA' prevents operation INSERT on table 'v2Aa'. insert into t1aA values ((select max(col1) from v1Aa)); -ERROR HY000: You can't specify target table 't1aa' for update in FROM clause +ERROR HY000: The definition of table 'v1Aa' prevents operation INSERT on table 't1aA'. insert into v2aA values ((select max(col1) from v1aA)); -ERROR HY000: You can't specify target table 'v2aa' for update in FROM clause +ERROR HY000: The definition of table 'v1aA' prevents operation INSERT on table 'v2aA'. insert into v2Aa values ((select max(col1) from t1Aa)); -ERROR HY000: You can't specify target table 'v2aa' for update in FROM clause +ERROR HY000: The definition of table 'v2Aa' prevents operation INSERT on table 'v2Aa'. insert into t1aA values ((select max(col1) from t1Aa)); -ERROR HY000: You can't specify target table 't1aa' for update in FROM clause +ERROR HY000: You can't specify target table 't1aA' for update in FROM clause insert into v2aA values ((select max(col1) from t1aA)); -ERROR HY000: You can't specify target table 'v2aa' for update in FROM clause +ERROR HY000: The definition of table 'v2aA' prevents operation INSERT on table 'v2aA'. insert into v2Aa values ((select max(col1) from v2aA)); -ERROR HY000: You can't specify target table 'v2aa' for update in FROM clause +ERROR HY000: You can't specify target table 'v2Aa' for update in FROM clause insert into t1Aa values ((select max(col1) from v2Aa)); -ERROR HY000: You can't specify target table 't1aa' for update in FROM clause +ERROR HY000: The definition of table 'v2Aa' prevents operation INSERT on table 't1Aa'. insert into v2aA values ((select max(col1) from v2Aa)); -ERROR HY000: You can't specify target table 'v2aa' for update in FROM clause +ERROR HY000: You can't specify target table 'v2aA' for update in FROM clause insert into v3Aa (col1) values ((select max(col1) from v1Aa)); -ERROR HY000: You can't specify target table 'v3aa' for update in FROM clause +ERROR HY000: The definition of table 'v1Aa' prevents operation INSERT on table 'v3Aa'. insert into v3aA (col1) values ((select max(col1) from t1aA)); -ERROR HY000: You can't specify target table 'v3aa' for update in FROM clause +ERROR HY000: The definition of table 'v3aA' prevents operation INSERT on table 'v3aA'. insert into v3Aa (col1) values ((select max(col1) from v2aA)); -ERROR HY000: You can't specify target table 'v3aa' for update in FROM clause +ERROR HY000: The definition of table 'v2aA' prevents operation INSERT on table 'v3Aa'. drop view v3aA,v2Aa,v1aA; drop table t1Aa,t2Aa; create table t1Aa (col1 int); diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result index d4d6eb08cad..8c09b9bcf97 100644 --- a/mysql-test/r/view.result +++ b/mysql-test/r/view.result @@ -870,29 +870,29 @@ create view v1 as select * from t1; create view v2 as select * from v1; create view v3 as select v2.col1 from v2,t2 where v2.col1 = t2.col1; update v2 set col1 = (select max(col1) from v1); -ERROR HY000: You can't specify target table 'v2' for update in FROM clause +ERROR HY000: The definition of table 'v1' prevents operation UPDATE on table 'v2'. update v2 set col1 = (select max(col1) from t1); -ERROR HY000: You can't specify target table 'v2' for update in FROM clause +ERROR HY000: The definition of table 'v2' prevents operation UPDATE on table 'v2'. update v2 set col1 = (select max(col1) from v2); ERROR HY000: You can't specify target table 'v2' for update in FROM clause update v2,t2 set v2.col1 = (select max(col1) from v1) where v2.col1 = t2.col1; -ERROR HY000: You can't specify target table 'v2' for update in FROM clause +ERROR HY000: The definition of table 'v1' prevents operation UPDATE on table 'v2'. update t1,t2 set t1.col1 = (select max(col1) from v1) where t1.col1 = t2.col1; -ERROR HY000: You can't specify target table 't1' for update in FROM clause +ERROR HY000: The definition of table 'v1' prevents operation UPDATE on table 't1'. update v1,t2 set v1.col1 = (select max(col1) from v1) where v1.col1 = t2.col1; ERROR HY000: You can't specify target table 'v1' for update in FROM clause update t2,v2 set v2.col1 = (select max(col1) from v1) where v2.col1 = t2.col1; -ERROR HY000: You can't specify target table 't2' for update in FROM clause +ERROR HY000: The definition of table 'v1' prevents operation UPDATE on table 't2'. update t2,t1 set t1.col1 = (select max(col1) from v1) where t1.col1 = t2.col1; -ERROR HY000: You can't specify target table 't2' for update in FROM clause +ERROR HY000: The definition of table 'v1' prevents operation UPDATE on table 't2'. update t2,v1 set v1.col1 = (select max(col1) from v1) where v1.col1 = t2.col1; -ERROR HY000: You can't specify target table 't2' for update in FROM clause +ERROR HY000: The definition of table 'v1' prevents operation UPDATE on table 't2'. update v2,t2 set v2.col1 = (select max(col1) from t1) where v2.col1 = t2.col1; -ERROR HY000: You can't specify target table 'v2' for update in FROM clause +ERROR HY000: The definition of table 'v2' prevents operation UPDATE on table 'v2'. update t1,t2 set t1.col1 = (select max(col1) from t1) where t1.col1 = t2.col1; ERROR HY000: You can't specify target table 't1' for update in FROM clause update v1,t2 set v1.col1 = (select max(col1) from t1) where v1.col1 = t2.col1; -ERROR HY000: You can't specify target table 'v1' for update in FROM clause +ERROR HY000: The definition of table 'v1' prevents operation UPDATE on table 'v1'. update t2,v2 set v2.col1 = (select max(col1) from t1) where v2.col1 = t2.col1; ERROR HY000: You can't specify target table 't2' for update in FROM clause update t2,t1 set t1.col1 = (select max(col1) from t1) where t1.col1 = t2.col1; @@ -902,73 +902,73 @@ ERROR HY000: You can't specify target table 't2' for update in FROM clause update v2,t2 set v2.col1 = (select max(col1) from v2) where v2.col1 = t2.col1; ERROR HY000: You can't specify target table 'v2' for update in FROM clause update t1,t2 set t1.col1 = (select max(col1) from v2) where t1.col1 = t2.col1; -ERROR HY000: You can't specify target table 't1' for update in FROM clause +ERROR HY000: The definition of table 'v2' prevents operation UPDATE on table 't1'. update v1,t2 set v1.col1 = (select max(col1) from v2) where v1.col1 = t2.col1; -ERROR HY000: You can't specify target table 'v1' for update in FROM clause +ERROR HY000: The definition of table 'v2' prevents operation UPDATE on table 'v1'. update t2,v2 set v2.col1 = (select max(col1) from v2) where v2.col1 = t2.col1; -ERROR HY000: You can't specify target table 't2' for update in FROM clause +ERROR HY000: The definition of table 'v2' prevents operation UPDATE on table 't2'. update t2,t1 set t1.col1 = (select max(col1) from v2) where t1.col1 = t2.col1; -ERROR HY000: You can't specify target table 't2' for update in FROM clause +ERROR HY000: The definition of table 'v2' prevents operation UPDATE on table 't2'. update t2,v1 set v1.col1 = (select max(col1) from v2) where v1.col1 = t2.col1; -ERROR HY000: You can't specify target table 't2' for update in FROM clause +ERROR HY000: The definition of table 'v2' prevents operation UPDATE on table 't2'. update v3 set v3.col1 = (select max(col1) from v1); -ERROR HY000: You can't specify target table 'v3' for update in FROM clause +ERROR HY000: The definition of table 'v1' prevents operation UPDATE on table 'v3'. update v3 set v3.col1 = (select max(col1) from t1); -ERROR HY000: You can't specify target table 'v3' for update in FROM clause +ERROR HY000: The definition of table 'v3' prevents operation UPDATE on table 'v3'. update v3 set v3.col1 = (select max(col1) from v2); -ERROR HY000: You can't specify target table 'v3' for update in FROM clause +ERROR HY000: The definition of table 'v2' prevents operation UPDATE on table 'v3'. update v3 set v3.col1 = (select max(col1) from v3); ERROR HY000: You can't specify target table 'v3' for update in FROM clause delete from v2 where col1 = (select max(col1) from v1); -ERROR HY000: You can't specify target table 'v2' for update in FROM clause +ERROR HY000: The definition of table 'v1' prevents operation DELETE on table 'v2'. delete from v2 where col1 = (select max(col1) from t1); -ERROR HY000: You can't specify target table 'v2' for update in FROM clause +ERROR HY000: The definition of table 'v2' prevents operation DELETE on table 'v2'. delete from v2 where col1 = (select max(col1) from v2); ERROR HY000: You can't specify target table 'v2' for update in FROM clause delete v2 from v2,t2 where (select max(col1) from v1) > 0 and v2.col1 = t2.col1; -ERROR HY000: You can't specify target table 'v2' for update in FROM clause +ERROR HY000: The definition of table 'v1' prevents operation DELETE on table 'v2'. delete t1 from t1,t2 where (select max(col1) from v1) > 0 and t1.col1 = t2.col1; -ERROR HY000: You can't specify target table 't1' for update in FROM clause +ERROR HY000: The definition of table 'v1' prevents operation DELETE on table 't1'. delete v1 from v1,t2 where (select max(col1) from v1) > 0 and v1.col1 = t2.col1; ERROR HY000: You can't specify target table 'v1' for update in FROM clause delete v2 from v2,t2 where (select max(col1) from t1) > 0 and v2.col1 = t2.col1; -ERROR HY000: You can't specify target table 'v2' for update in FROM clause +ERROR HY000: The definition of table 'v2' prevents operation DELETE on table 'v2'. delete t1 from t1,t2 where (select max(col1) from t1) > 0 and t1.col1 = t2.col1; ERROR HY000: You can't specify target table 't1' for update in FROM clause delete v1 from v1,t2 where (select max(col1) from t1) > 0 and v1.col1 = t2.col1; -ERROR HY000: You can't specify target table 'v1' for update in FROM clause +ERROR HY000: The definition of table 'v1' prevents operation DELETE on table 'v1'. delete v2 from v2,t2 where (select max(col1) from v2) > 0 and v2.col1 = t2.col1; ERROR HY000: You can't specify target table 'v2' for update in FROM clause delete t1 from t1,t2 where (select max(col1) from v2) > 0 and t1.col1 = t2.col1; -ERROR HY000: You can't specify target table 't1' for update in FROM clause +ERROR HY000: The definition of table 'v2' prevents operation DELETE on table 't1'. delete v1 from v1,t2 where (select max(col1) from v2) > 0 and v1.col1 = t2.col1; -ERROR HY000: You can't specify target table 'v1' for update in FROM clause +ERROR HY000: The definition of table 'v2' prevents operation DELETE on table 'v1'. insert into v2 values ((select max(col1) from v1)); -ERROR HY000: You can't specify target table 'v2' for update in FROM clause +ERROR HY000: The definition of table 'v1' prevents operation INSERT on table 'v2'. insert into t1 values ((select max(col1) from v1)); -ERROR HY000: You can't specify target table 't1' for update in FROM clause +ERROR HY000: The definition of table 'v1' prevents operation INSERT on table 't1'. insert into v2 values ((select max(col1) from v1)); -ERROR HY000: You can't specify target table 'v2' for update in FROM clause +ERROR HY000: The definition of table 'v1' prevents operation INSERT on table 'v2'. insert into v2 values ((select max(col1) from t1)); -ERROR HY000: You can't specify target table 'v2' for update in FROM clause +ERROR HY000: The definition of table 'v2' prevents operation INSERT on table 'v2'. insert into t1 values ((select max(col1) from t1)); ERROR HY000: You can't specify target table 't1' for update in FROM clause insert into v2 values ((select max(col1) from t1)); -ERROR HY000: You can't specify target table 'v2' for update in FROM clause +ERROR HY000: The definition of table 'v2' prevents operation INSERT on table 'v2'. insert into v2 values ((select max(col1) from v2)); ERROR HY000: You can't specify target table 'v2' for update in FROM clause insert into t1 values ((select max(col1) from v2)); -ERROR HY000: You can't specify target table 't1' for update in FROM clause +ERROR HY000: The definition of table 'v2' prevents operation INSERT on table 't1'. insert into v2 values ((select max(col1) from v2)); ERROR HY000: You can't specify target table 'v2' for update in FROM clause insert into v3 (col1) values ((select max(col1) from v1)); -ERROR HY000: You can't specify target table 'v3' for update in FROM clause +ERROR HY000: The definition of table 'v1' prevents operation INSERT on table 'v3'. insert into v3 (col1) values ((select max(col1) from t1)); -ERROR HY000: You can't specify target table 'v3' for update in FROM clause +ERROR HY000: The definition of table 'v3' prevents operation INSERT on table 'v3'. insert into v3 (col1) values ((select max(col1) from v2)); -ERROR HY000: You can't specify target table 'v3' for update in FROM clause +ERROR HY000: The definition of table 'v2' prevents operation INSERT on table 'v3'. insert into v3 (col1) values ((select CONVERT_TZ('20050101000000','UTC','MET') from v2)); -ERROR HY000: You can't specify target table 'v3' for update in FROM clause +ERROR HY000: The definition of table 'v2' prevents operation INSERT on table 'v3'. insert into v3 (col1) values ((select CONVERT_TZ('20050101000000','UTC','MET') from t2)); insert into mysql.time_zone values ('', (select CONVERT_TZ('20050101000000','UTC','MET') from t2)); ERROR 23000: Column 'Use_leap_seconds' cannot be null @@ -1327,7 +1327,7 @@ insert into v3 values (30); ERROR HY000: The target table v3 of the INSERT is not updatable create view v4 as select * from v2 where 20 < (select (s1) from t1); insert into v4 values (30); -ERROR HY000: You can't specify target table 'v4' for update in FROM clause +ERROR HY000: The target table v4 of the INSERT is not updatable drop view v4, v3, v2, v1; drop table t1; create table t1 (a int); @@ -1839,3 +1839,13 @@ SELECT * FROM v1; SUBSTRING_INDEX("dkjhgd:kjhdjh", ":", 1) dkjhgd drop view v1; +create table t1 (f59 int, f60 int, f61 int); +insert into t1 values (19,41,32); +create view v1 as select f59, f60 from t1 where f59 in +(select f59 from t1); +update v1 set f60=2345; +ERROR HY000: The target table v1 of the UPDATE is not updatable +update t1 set f60=(select max(f60) from v1); +ERROR HY000: The definition of table 'v1' prevents operation UPDATE on table 't1'. +drop view v1; +drop table t1; diff --git a/mysql-test/t/lowercase_view.test b/mysql-test/t/lowercase_view.test index b39223f71d5..846c828fb09 100644 --- a/mysql-test/t/lowercase_view.test +++ b/mysql-test/t/lowercase_view.test @@ -23,29 +23,29 @@ create table t2aA (col1 int); create view v1Aa as select * from t1aA; create view v2aA as select * from v1aA; create view v3Aa as select v2Aa.col1 from v2aA,t2Aa where v2Aa.col1 = t2aA.col1; --- error 1093 +-- error 1423 update v2aA set col1 = (select max(col1) from v1Aa); --- error 1093 +-- error 1423 update v2Aa set col1 = (select max(col1) from t1Aa); -- error 1093 update v2aA set col1 = (select max(col1) from v2Aa); --- error 1093 +-- error 1423 update v2aA,t2Aa set v2Aa.col1 = (select max(col1) from v1aA) where v2aA.col1 = t2aA.col1; --- error 1093 +-- error 1423 update t1aA,t2Aa set t1Aa.col1 = (select max(col1) from v1Aa) where t1aA.col1 = t2aA.col1; -- error 1093 update v1aA,t2Aa set v1Aa.col1 = (select max(col1) from v1aA) where v1Aa.col1 = t2aA.col1; --- error 1093 +-- error 1423 update t2Aa,v2Aa set v2aA.col1 = (select max(col1) from v1aA) where v2Aa.col1 = t2aA.col1; --- error 1093 +-- error 1423 update t2Aa,t1Aa set t1aA.col1 = (select max(col1) from v1Aa) where t1Aa.col1 = t2aA.col1; --- error 1093 +-- error 1423 update t2Aa,v1aA set v1Aa.col1 = (select max(col1) from v1aA) where v1Aa.col1 = t2aA.col1; --- error 1093 +-- error 1423 update v2aA,t2Aa set v2Aa.col1 = (select max(col1) from t1aA) where v2aA.col1 = t2aA.col1; -- error 1093 update t1Aa,t2Aa set t1aA.col1 = (select max(col1) from t1Aa) where t1aA.col1 = t2aA.col1; --- error 1093 +-- error 1423 update v1aA,t2Aa set v1Aa.col1 = (select max(col1) from t1Aa) where v1aA.col1 = t2aA.col1; -- error 1093 update t2Aa,v2Aa set v2aA.col1 = (select max(col1) from t1aA) where v2Aa.col1 = t2aA.col1; @@ -55,71 +55,71 @@ update t2Aa,t1Aa set t1aA.col1 = (select max(col1) from t1Aa) where t1aA.col1 = update t2Aa,v1Aa set v1aA.col1 = (select max(col1) from t1Aa) where v1Aa.col1 = t2aA.col1; -- error 1093 update v2aA,t2Aa set v2Aa.col1 = (select max(col1) from v2aA) where v2Aa.col1 = t2aA.col1; --- error 1093 +-- error 1423 update t1aA,t2Aa set t1Aa.col1 = (select max(col1) from v2aA) where t1aA.col1 = t2aA.col1; --- error 1093 +-- error 1423 update v1aA,t2Aa set v1Aa.col1 = (select max(col1) from v2Aa) where v1aA.col1 = t2aA.col1; --- error 1093 +-- error 1423 update t2Aa,v2aA set v2Aa.col1 = (select max(col1) from v2aA) where v2Aa.col1 = t2aA.col1; --- error 1093 +-- error 1423 update t2Aa,t1Aa set t1aA.col1 = (select max(col1) from v2aA) where t1Aa.col1 = t2aA.col1; --- error 1093 +-- error 1423 update t2Aa,v1Aa set v1aA.col1 = (select max(col1) from v2Aa) where v1Aa.col1 = t2aA.col1; --- error 1093 +-- error 1423 update v3aA set v3Aa.col1 = (select max(col1) from v1aA); --- error 1093 +-- error 1423 update v3aA set v3Aa.col1 = (select max(col1) from t1aA); --- error 1093 +-- error 1423 update v3aA set v3Aa.col1 = (select max(col1) from v2aA); -- error 1093 update v3aA set v3Aa.col1 = (select max(col1) from v3aA); --- error 1093 +-- error 1423 delete from v2Aa where col1 = (select max(col1) from v1Aa); --- error 1093 +-- error 1423 delete from v2aA where col1 = (select max(col1) from t1Aa); -- error 1093 delete from v2Aa where col1 = (select max(col1) from v2aA); --- error 1093 +-- error 1423 delete v2Aa from v2aA,t2Aa where (select max(col1) from v1aA) > 0 and v2Aa.col1 = t2aA.col1; --- error 1093 +-- error 1423 delete t1aA from t1Aa,t2Aa where (select max(col1) from v1Aa) > 0 and t1aA.col1 = t2aA.col1; -- error 1093 delete v1aA from v1Aa,t2Aa where (select max(col1) from v1aA) > 0 and v1Aa.col1 = t2aA.col1; --- error 1093 +-- error 1423 delete v2aA from v2Aa,t2Aa where (select max(col1) from t1Aa) > 0 and v2aA.col1 = t2aA.col1; -- error 1093 delete t1aA from t1Aa,t2Aa where (select max(col1) from t1aA) > 0 and t1Aa.col1 = t2aA.col1; --- error 1093 +-- error 1423 delete v1aA from v1Aa,t2Aa where (select max(col1) from t1aA) > 0 and v1aA.col1 = t2aA.col1; -- error 1093 delete v2Aa from v2aA,t2Aa where (select max(col1) from v2Aa) > 0 and v2aA.col1 = t2aA.col1; --- error 1093 +-- error 1423 delete t1Aa from t1aA,t2Aa where (select max(col1) from v2Aa) > 0 and t1Aa.col1 = t2aA.col1; --- error 1093 +-- error 1423 delete v1Aa from v1aA,t2Aa where (select max(col1) from v2aA) > 0 and v1Aa.col1 = t2aA.col1; --- error 1093 +-- error 1423 insert into v2Aa values ((select max(col1) from v1aA)); --- error 1093 +-- error 1423 insert into t1aA values ((select max(col1) from v1Aa)); --- error 1093 +-- error 1423 insert into v2aA values ((select max(col1) from v1aA)); --- error 1093 +-- error 1423 insert into v2Aa values ((select max(col1) from t1Aa)); -- error 1093 insert into t1aA values ((select max(col1) from t1Aa)); --- error 1093 +-- error 1423 insert into v2aA values ((select max(col1) from t1aA)); -- error 1093 insert into v2Aa values ((select max(col1) from v2aA)); --- error 1093 +-- error 1423 insert into t1Aa values ((select max(col1) from v2Aa)); -- error 1093 insert into v2aA values ((select max(col1) from v2Aa)); --- error 1093 +-- error 1423 insert into v3Aa (col1) values ((select max(col1) from v1Aa)); --- error 1093 +-- error 1423 insert into v3aA (col1) values ((select max(col1) from t1aA)); --- error 1093 +-- error 1423 insert into v3Aa (col1) values ((select max(col1) from v2aA)); drop view v3aA,v2Aa,v1aA; drop table t1Aa,t2Aa; diff --git a/mysql-test/t/view.test b/mysql-test/t/view.test index f131f9d2604..ca1d2a2c978 100644 --- a/mysql-test/t/view.test +++ b/mysql-test/t/view.test @@ -827,29 +827,29 @@ create table t2 (col1 int); create view v1 as select * from t1; create view v2 as select * from v1; create view v3 as select v2.col1 from v2,t2 where v2.col1 = t2.col1; --- error 1093 +-- error 1423 update v2 set col1 = (select max(col1) from v1); --- error 1093 +-- error 1423 update v2 set col1 = (select max(col1) from t1); -- error 1093 update v2 set col1 = (select max(col1) from v2); --- error 1093 +-- error 1423 update v2,t2 set v2.col1 = (select max(col1) from v1) where v2.col1 = t2.col1; --- error 1093 +-- error 1423 update t1,t2 set t1.col1 = (select max(col1) from v1) where t1.col1 = t2.col1; -- error 1093 update v1,t2 set v1.col1 = (select max(col1) from v1) where v1.col1 = t2.col1; --- error 1093 +-- error 1423 update t2,v2 set v2.col1 = (select max(col1) from v1) where v2.col1 = t2.col1; --- error 1093 +-- error 1423 update t2,t1 set t1.col1 = (select max(col1) from v1) where t1.col1 = t2.col1; --- error 1093 +-- error 1423 update t2,v1 set v1.col1 = (select max(col1) from v1) where v1.col1 = t2.col1; --- error 1093 +-- error 1423 update v2,t2 set v2.col1 = (select max(col1) from t1) where v2.col1 = t2.col1; -- error 1093 update t1,t2 set t1.col1 = (select max(col1) from t1) where t1.col1 = t2.col1; --- error 1093 +-- error 1423 update v1,t2 set v1.col1 = (select max(col1) from t1) where v1.col1 = t2.col1; -- error 1093 update t2,v2 set v2.col1 = (select max(col1) from t1) where v2.col1 = t2.col1; @@ -859,74 +859,74 @@ update t2,t1 set t1.col1 = (select max(col1) from t1) where t1.col1 = t2.col1; update t2,v1 set v1.col1 = (select max(col1) from t1) where v1.col1 = t2.col1; -- error 1093 update v2,t2 set v2.col1 = (select max(col1) from v2) where v2.col1 = t2.col1; --- error 1093 +-- error 1423 update t1,t2 set t1.col1 = (select max(col1) from v2) where t1.col1 = t2.col1; --- error 1093 +-- error 1423 update v1,t2 set v1.col1 = (select max(col1) from v2) where v1.col1 = t2.col1; --- error 1093 +-- error 1423 update t2,v2 set v2.col1 = (select max(col1) from v2) where v2.col1 = t2.col1; --- error 1093 +-- error 1423 update t2,t1 set t1.col1 = (select max(col1) from v2) where t1.col1 = t2.col1; --- error 1093 +-- error 1423 update t2,v1 set v1.col1 = (select max(col1) from v2) where v1.col1 = t2.col1; --- error 1093 +-- error 1423 update v3 set v3.col1 = (select max(col1) from v1); --- error 1093 +-- error 1423 update v3 set v3.col1 = (select max(col1) from t1); --- error 1093 +-- error 1423 update v3 set v3.col1 = (select max(col1) from v2); -- error 1093 update v3 set v3.col1 = (select max(col1) from v3); --- error 1093 +-- error 1423 delete from v2 where col1 = (select max(col1) from v1); --- error 1093 +-- error 1423 delete from v2 where col1 = (select max(col1) from t1); -- error 1093 delete from v2 where col1 = (select max(col1) from v2); --- error 1093 +-- error 1423 delete v2 from v2,t2 where (select max(col1) from v1) > 0 and v2.col1 = t2.col1; --- error 1093 +-- error 1423 delete t1 from t1,t2 where (select max(col1) from v1) > 0 and t1.col1 = t2.col1; -- error 1093 delete v1 from v1,t2 where (select max(col1) from v1) > 0 and v1.col1 = t2.col1; --- error 1093 +-- error 1423 delete v2 from v2,t2 where (select max(col1) from t1) > 0 and v2.col1 = t2.col1; -- error 1093 delete t1 from t1,t2 where (select max(col1) from t1) > 0 and t1.col1 = t2.col1; --- error 1093 +-- error 1423 delete v1 from v1,t2 where (select max(col1) from t1) > 0 and v1.col1 = t2.col1; -- error 1093 delete v2 from v2,t2 where (select max(col1) from v2) > 0 and v2.col1 = t2.col1; --- error 1093 +-- error 1423 delete t1 from t1,t2 where (select max(col1) from v2) > 0 and t1.col1 = t2.col1; --- error 1093 +-- error 1423 delete v1 from v1,t2 where (select max(col1) from v2) > 0 and v1.col1 = t2.col1; --- error 1093 +-- error 1423 insert into v2 values ((select max(col1) from v1)); --- error 1093 +-- error 1423 insert into t1 values ((select max(col1) from v1)); --- error 1093 +-- error 1423 insert into v2 values ((select max(col1) from v1)); --- error 1093 +-- error 1423 insert into v2 values ((select max(col1) from t1)); -- error 1093 insert into t1 values ((select max(col1) from t1)); --- error 1093 +-- error 1423 insert into v2 values ((select max(col1) from t1)); -- error 1093 insert into v2 values ((select max(col1) from v2)); --- error 1093 +-- error 1423 insert into t1 values ((select max(col1) from v2)); -- error 1093 insert into v2 values ((select max(col1) from v2)); --- error 1093 +-- error 1423 insert into v3 (col1) values ((select max(col1) from v1)); --- error 1093 +-- error 1423 insert into v3 (col1) values ((select max(col1) from t1)); --- error 1093 +-- error 1423 insert into v3 (col1) values ((select max(col1) from v2)); #check with TZ tables in list --- error 1093 +-- error 1423 insert into v3 (col1) values ((select CONVERT_TZ('20050101000000','UTC','MET') from v2)); insert into v3 (col1) values ((select CONVERT_TZ('20050101000000','UTC','MET') from t2)); -- error 1048 @@ -1242,7 +1242,7 @@ create view v3 as select * from t1 where 20 < (select (s1) from v2); -- error 1288 insert into v3 values (30); create view v4 as select * from v2 where 20 < (select (s1) from t1); --- error 1093 +-- error 1288 insert into v4 values (30); drop view v4, v3, v2, v1; drop table t1; @@ -1685,3 +1685,18 @@ drop view v1; CREATE VIEW v1 AS SELECT SUBSTRING_INDEX("dkjhgd:kjhdjh", ":", 1); SELECT * FROM v1; drop view v1; + +# +# hide underlying tables names in case of imposibility to update (BUG#10773) +# +create table t1 (f59 int, f60 int, f61 int); +insert into t1 values (19,41,32); +create view v1 as select f59, f60 from t1 where f59 in + (select f59 from t1); +-- error 1288 +update v1 set f60=2345; +-- error 1423 +update t1 set f60=(select max(f60) from v1); +drop view v1; +drop table t1; + diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 2add9ac9269..28aa9e616b9 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -882,6 +882,9 @@ void add_join_on(TABLE_LIST *b,Item *expr); void add_join_natural(TABLE_LIST *a,TABLE_LIST *b); bool add_proc_to_list(THD *thd, Item *item); TABLE *unlink_open_table(THD *thd,TABLE *list,TABLE *find); +void update_non_unique_table_error(TABLE_LIST *update, + const char *operation, + TABLE_LIST *duplicate); SQL_SELECT *make_select(TABLE *head, table_map const_tables, table_map read_tables, COND *conds, diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index fcc04c950aa..386688de911 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -5358,3 +5358,5 @@ ER_STMT_HAS_NO_OPEN_CURSOR eng "The statement (%lu) has no open cursor." ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG eng "Explicit or implicit commit is not allowed in stored function or trigger." +ER_VIEW_PREVENT_UPDATE + eng "The definition of table '%-.64s' prevents operation %s on table '%-.64s'." diff --git a/sql/sql_base.cc b/sql/sql_base.cc index e9af684767d..a4753e46bb6 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -775,6 +775,60 @@ TABLE_LIST* unique_table(TABLE_LIST *table, TABLE_LIST *table_list) } +/* + Issue correct error message in case we found 2 duplicate tables which + prevent some update operation + + SYNOPSIS + update_non_unique_table_error() + update table which we try to update + operation name of update operation + duplicate duplicate table which we found + + NOTE: + here we hide view underlying tables if we have them +*/ + +void update_non_unique_table_error(TABLE_LIST *update, + const char *operation, + TABLE_LIST *duplicate) +{ + update= update->top_table(); + duplicate= duplicate->top_table(); + if (!update->view || !duplicate->view || + update->view == duplicate->view || + update->view_name.length != duplicate->view_name.length || + update->view_db.length != duplicate->view_db.length || + my_strcasecmp(table_alias_charset, + update->view_name.str, duplicate->view_name.str) != 0 || + my_strcasecmp(table_alias_charset, + update->view_db.str, duplicate->view_db.str) != 0) + { + /* + it is not the same view repeated (but it can be parts of the same copy + of view), so we have to hide underlying tables. + */ + if (update->view) + { + if (update->view == duplicate->view) + my_error(ER_NON_UPDATABLE_TABLE, MYF(0), update->alias, operation); + else + my_error(ER_VIEW_PREVENT_UPDATE, MYF(0), + (duplicate->view ? duplicate->alias : update->alias), + operation, update->alias); + return; + } + if (duplicate->view) + { + my_error(ER_VIEW_PREVENT_UPDATE, MYF(0), duplicate->alias, operation, + update->alias); + return; + } + } + my_error(ER_UPDATE_TABLE_USED, MYF(0), update->alias); +} + + TABLE **find_temporary_table(THD *thd, const char *db, const char *table_name) { char key[MAX_DBKEY_LENGTH]; @@ -3212,9 +3266,7 @@ bool setup_tables(THD *thd, TABLE_LIST *tables, Item **conds, { TABLE *table= table_list->table; if (first_select_table && - (table_list->belong_to_view ? - table_list->belong_to_view : - table_list) == first_select_table) + table_list->top_table() == first_select_table) { /* new counting for SELECT of INSERT ... SELECT command */ first_select_table= 0; diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 25fef8b0ad6..ddf5aee0977 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -309,10 +309,13 @@ bool mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds) my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "DELETE"); DBUG_RETURN(TRUE); } - if (unique_table(table_list, table_list->next_global)) { - my_error(ER_UPDATE_TABLE_USED, MYF(0), table_list->table_name); - DBUG_RETURN(TRUE); + TABLE_LIST *duplicate; + if ((duplicate= unique_table(table_list, table_list->next_global))) + { + update_non_unique_table_error(table_list, "DELETE", duplicate); + DBUG_RETURN(TRUE); + } } select_lex->fix_prepare_information(thd, conds); DBUG_RETURN(FALSE); @@ -393,11 +396,15 @@ bool mysql_multi_delete_prepare(THD *thd) Check that table from which we delete is not used somewhere inside subqueries/view. */ - if (unique_table(target_tbl->correspondent_table, lex->query_tables)) { - my_error(ER_UPDATE_TABLE_USED, MYF(0), - target_tbl->correspondent_table->table_name); - DBUG_RETURN(TRUE); + TABLE_LIST *duplicate; + if ((duplicate= unique_table(target_tbl->correspondent_table, + lex->query_tables))) + { + update_non_unique_table_error(target_tbl->correspondent_table, + "DELETE", duplicate); + DBUG_RETURN(TRUE); + } } } DBUG_RETURN(FALSE); diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 2ce81d8815e..e4c96bc3dcd 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -778,10 +778,14 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, TABLE *table, if (!table) table= table_list->table; - if (!select_insert && unique_table(table_list, table_list->next_global)) + if (!select_insert) { - my_error(ER_UPDATE_TABLE_USED, MYF(0), table_list->table_name); - DBUG_RETURN(TRUE); + TABLE_LIST *duplicate; + if ((duplicate= unique_table(table_list, table_list->next_global))) + { + update_non_unique_table_error(table_list, "INSERT", duplicate); + DBUG_RETURN(TRUE); + } } if (duplic == DUP_UPDATE || duplic == DUP_REPLACE) table->file->extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY); diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 33339cf0882..ffe36f3b6bd 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -2845,12 +2845,15 @@ mysql_execute_command(THD *thd) Is table which we are changing used somewhere in other parts of query */ - if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) && - unique_table(create_table, select_tables)) + if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE)) { - my_error(ER_UPDATE_TABLE_USED, MYF(0), create_table->table_name); - res= 1; - goto end_with_restart_wait; + TABLE_LIST *duplicate; + if ((duplicate= unique_table(create_table, select_tables))) + { + update_non_unique_table_error(create_table, "CREATE", duplicate); + res= 1; + goto end_with_restart_wait; + } } /* If we create merge table, we have to test tables in merge, too */ if (lex->create_info.used_fields & HA_CREATE_USED_UNION) @@ -2860,9 +2863,10 @@ mysql_execute_command(THD *thd) tab; tab= tab->next_local) { - if (unique_table(tab, select_tables)) + TABLE_LIST *duplicate; + if ((duplicate= unique_table(tab, select_tables))) { - my_error(ER_UPDATE_TABLE_USED, MYF(0), tab->table_name); + update_non_unique_table_error(tab, "CREATE", duplicate); res= 1; goto end_with_restart_wait; } diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 6b9a8ddfcb6..b448d56c254 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -566,10 +566,14 @@ bool mysql_prepare_update(THD *thd, TABLE_LIST *table_list, DBUG_RETURN(TRUE); /* Check that we are not using table that we are updating in a sub select */ - if (unique_table(table_list, table_list->next_global)) { - my_error(ER_UPDATE_TABLE_USED, MYF(0), table_list->table_name); - DBUG_RETURN(TRUE); + TABLE_LIST *duplicate; + if ((duplicate= unique_table(table_list, table_list->next_global))) + { + update_non_unique_table_error(table_list, "UPDATE", duplicate); + my_error(ER_UPDATE_TABLE_USED, MYF(0), table_list->table_name); + DBUG_RETURN(TRUE); + } } select_lex->fix_prepare_information(thd, conds); DBUG_RETURN(FALSE); @@ -781,7 +785,7 @@ bool mysql_multi_update_prepare(THD *thd) { TABLE *table= tl->table; TABLE_LIST *tlist; - if (!(tlist= tl->belong_to_view ? tl->belong_to_view : tl)->derived) + if (!(tlist= tl->top_table())->derived) { tlist->grant.want_privilege= (SELECT_ACL & ~tlist->grant.privilege); @@ -790,11 +794,14 @@ bool mysql_multi_update_prepare(THD *thd) DBUG_PRINT("info", ("table: %s want_privilege: %u", tl->alias, (uint) table->grant.want_privilege)); if (tl->lock_type != TL_READ && - tl->lock_type != TL_READ_NO_INSERT && - unique_table(tl, table_list)) + tl->lock_type != TL_READ_NO_INSERT) { - my_error(ER_UPDATE_TABLE_USED, MYF(0), table_list->table_name); - DBUG_RETURN(TRUE); + TABLE_LIST *duplicate; + if ((duplicate= unique_table(tl, table_list))) + { + update_non_unique_table_error(table_list, "UPDATE", duplicate); + DBUG_RETURN(TRUE); + } } } diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 0b351407c13..cfd882f5308 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -720,9 +720,7 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table) } if (!res && !thd->is_fatal_error) { - TABLE_LIST *top_view= (table->belong_to_view ? - table->belong_to_view : - table); + TABLE_LIST *top_view= table->top_table(); TABLE_LIST *view_tables= lex->query_tables; TABLE_LIST *view_tables_tail= 0; TABLE_LIST *tbl; @@ -1072,8 +1070,7 @@ bool check_key_in_view(THD *thd, TABLE_LIST *view) thd->lex->select_lex.select_limit == 0) DBUG_RETURN(FALSE); /* it is normal table or query without LIMIT */ table= view->table; - if (view->belong_to_view) - view= view->belong_to_view; + view= view->top_table(); trans= view->field_translation; key_info_end= (key_info= table->key_info)+ table->s->keys; diff --git a/sql/table.cc b/sql/table.cc index 6677453969b..11b2b9d7e48 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -2118,7 +2118,7 @@ int st_table_list::view_check_option(THD *thd, bool ignore_failure) { if (check_option && check_option->val_int() == 0) { - TABLE_LIST *view= (belong_to_view ? belong_to_view : this); + TABLE_LIST *view= top_table(); if (ignore_failure) { push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR, diff --git a/sql/table.h b/sql/table.h index 8bc4e01852f..c6a74c67ea8 100644 --- a/sql/table.h +++ b/sql/table.h @@ -459,6 +459,8 @@ typedef struct st_table_list st_table_list *view); bool set_insert_values(MEM_ROOT *mem_root); st_table_list *find_underlying_table(TABLE *table); + inline st_table_list *top_table() + { return belong_to_view ? belong_to_view : this; } } TABLE_LIST; class Item; From bda02a17e79f3346ce2680ed04670e11f2bfcf63 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 30 Aug 2005 17:11:59 +0500 Subject: [PATCH 04/26] Bug#12363 character_set_results is nullable, but value_ptr returns string "NULL" set_var.cc: Create Item_null instead of Item_string for NULL values variables.result, variables.test: adding test case sql/set_var.cc: Bug#12363 character_set_results is nullable, but value_ptr returns string "NULL" Create Item_null instead of Item_string for NULL values mysql-test/t/variables.test: fixing tests accordingly mysql-test/r/variables.result: fixing tests accordingly --- mysql-test/r/variables.result | 5 +++++ mysql-test/t/variables.test | 8 ++++++++ sql/set_var.cc | 14 ++++++++++---- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/mysql-test/r/variables.result b/mysql-test/r/variables.result index 14260cd04f9..b01a8c1f8bf 100644 --- a/mysql-test/r/variables.result +++ b/mysql-test/r/variables.result @@ -491,3 +491,8 @@ SHOW VARIABLES LIKE 'table_cache'; Variable_name Value table_cache 1 SET GLOBAL table_cache=DEFAULT; +set character_set_results=NULL; +select ifnull(@@character_set_results,"really null"); +ifnull(@@character_set_results,"really null") +really null +set names latin1; diff --git a/mysql-test/t/variables.test b/mysql-test/t/variables.test index 7743b70d0cd..40cade15c9f 100644 --- a/mysql-test/t/variables.test +++ b/mysql-test/t/variables.test @@ -380,4 +380,12 @@ SET GLOBAL table_cache=-1; SHOW VARIABLES LIKE 'table_cache'; SET GLOBAL table_cache=DEFAULT; +# +# Bugs12363: character_set_results is nullable, +# but value_ptr returns string "NULL" +# +set character_set_results=NULL; +select ifnull(@@character_set_results,"really null"); +set names latin1; + # End of 4.1 tests diff --git a/sql/set_var.cc b/sql/set_var.cc index 99985c3647c..50f5c6b840e 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -1598,11 +1598,17 @@ Item *sys_var::item(THD *thd, enum_var_type var_type, LEX_STRING *base) return new Item_int((int32) *(my_bool*) value_ptr(thd, var_type, base),1); case SHOW_CHAR: { - Item_string *tmp; + Item *tmp; pthread_mutex_lock(&LOCK_global_system_variables); char *str= (char*) value_ptr(thd, var_type, base); - tmp= new Item_string(str, strlen(str), - system_charset_info, DERIVATION_SYSCONST); + if (str) + tmp= new Item_string(str, strlen(str), + system_charset_info, DERIVATION_SYSCONST); + else + { + tmp= new Item_null(); + tmp->collation.set(system_charset_info, DERIVATION_SYSCONST); + } pthread_mutex_unlock(&LOCK_global_system_variables); return tmp; } @@ -1892,7 +1898,7 @@ byte *sys_var_character_set::value_ptr(THD *thd, enum_var_type type, LEX_STRING *base) { CHARSET_INFO *cs= ci_ptr(thd,type)[0]; - return cs ? (byte*) cs->csname : (byte*) "NULL"; + return cs ? (byte*) cs->csname : (byte*) NULL; } From 20108d6f4bb3003dfb7edec9e4319e89d86dbfa9 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 31 Aug 2005 18:08:45 +0200 Subject: [PATCH 05/26] BUG#12542: All set statements should be executed by slave thd rpl_replicate_do.result: New test case sql_parse.cc: All SET statements should be executed by slave thd rpl_replicate_do.test: New test case mysql-test/t/rpl_replicate_do.test: Added test sql/sql_parse.cc: All SET commands should be executed by slave thd mysql-test/r/rpl_replicate_do.result: New test case --- mysql-test/r/rpl_replicate_do.result | 11 +++++ mysql-test/t/rpl_replicate_do.test | 18 +++++++ sql/sql_parse.cc | 73 +++++++++++++++++----------- 3 files changed, 74 insertions(+), 28 deletions(-) diff --git a/mysql-test/r/rpl_replicate_do.result b/mysql-test/r/rpl_replicate_do.result index ca290d46fda..45ef6cb9b7b 100644 --- a/mysql-test/r/rpl_replicate_do.result +++ b/mysql-test/r/rpl_replicate_do.result @@ -29,3 +29,14 @@ drop table if exists t1,t2,t11; show slave status; Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master # 127.0.0.1 root MASTER_PORT 1 master-bin.000001 1340 slave-relay-bin.000002 1384 master-bin.000001 Yes Yes test.t1 0 0 1340 1384 None 0 No # +create table t1 (ts timestamp); +set one_shot time_zone='met'; +insert into t1 values('2005-08-12 00:00:00'); +set one_shot time_zone='met'; +select * from t1; +ts +2005-08-12 00:00:00 +set one_shot time_zone='met'; +select * from t1; +ts +2005-08-12 00:00:00 diff --git a/mysql-test/t/rpl_replicate_do.test b/mysql-test/t/rpl_replicate_do.test index 98374240dd5..9bbaa9f0076 100644 --- a/mysql-test/t/rpl_replicate_do.test +++ b/mysql-test/t/rpl_replicate_do.test @@ -36,4 +36,22 @@ sync_with_master; --replace_column 1 # 33 # show slave status; +# +# BUG#12542 +# TEST: "SET ONE_SHOT should always be executed on slave" +# +# We could use any timezone different than server default in this test +# +connection master; +create table t1 (ts timestamp); +set one_shot time_zone='met'; +insert into t1 values('2005-08-12 00:00:00'); +set one_shot time_zone='met'; +select * from t1; +sync_slave_with_master; + +connection slave; +set one_shot time_zone='met'; +select * from t1; + # End of 4.1 tests diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index caf76b19eb2..8e457b67f00 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -120,6 +120,9 @@ static bool end_active_trans(THD *thd) #ifdef HAVE_REPLICATION +/* + Returns true if all tables should be ignored +*/ inline bool all_tables_not_ok(THD *thd, TABLE_LIST *tables) { return (table_rules_on && tables && !tables_ok(thd,tables) && @@ -1915,6 +1918,23 @@ bool alloc_query(THD *thd, char *packet, ulong packet_length) return 0; } +static void reset_one_shot_variables(THD *thd) +{ + thd->variables.character_set_client= + global_system_variables.character_set_client; + thd->variables.collation_connection= + global_system_variables.collation_connection; + thd->variables.collation_database= + global_system_variables.collation_database; + thd->variables.collation_server= + global_system_variables.collation_server; + thd->update_charset(); + thd->variables.time_zone= + global_system_variables.time_zone; + thd->one_shot_set= 0; +} + + /**************************************************************************** ** mysql_execute_command ** Execute command saved in thd and current_lex->sql_command @@ -1975,16 +1995,21 @@ mysql_execute_command(THD *thd) /* Skip if we are in the slave thread, some table rules have been given and the table list says the query should not be replicated. - Exception is DROP TEMPORARY TABLE IF EXISTS: we always execute it - (otherwise we have stale files on slave caused by exclusion of one tmp - table). + + Exceptions are: + - SET: we always execute it (Not that many SET commands exists in + the binary log anyway) + - DROP TEMPORARY TABLE IF EXISTS: we always execute it (otherwise we + have stale files on slave caused by exclusion of one tmp table). */ - if (!(lex->sql_command == SQLCOM_DROP_TABLE && + if (!(lex->sql_command == SQLCOM_SET_OPTION) && + !(lex->sql_command == SQLCOM_DROP_TABLE && lex->drop_temporary && lex->drop_if_exists) && all_tables_not_ok(thd,tables)) { /* we warn the slave SQL thread */ my_error(ER_SLAVE_IGNORED_TABLE, MYF(0)); + reset_one_shot_variables(thd); DBUG_VOID_RETURN; } #ifndef TO_BE_DELETED @@ -3309,6 +3334,7 @@ purposes internal to the MySQL server", MYF(0)); !db_ok_with_wild_table(lex->name))) { my_error(ER_SLAVE_IGNORED_TABLE, MYF(0)); + reset_one_shot_variables(thd); break; } #endif @@ -3344,6 +3370,7 @@ purposes internal to the MySQL server", MYF(0)); !db_ok_with_wild_table(lex->name))) { my_error(ER_SLAVE_IGNORED_TABLE, MYF(0)); + reset_one_shot_variables(thd); break; } #endif @@ -3384,6 +3411,7 @@ purposes internal to the MySQL server", MYF(0)); !db_ok_with_wild_table(db))) { my_error(ER_SLAVE_IGNORED_TABLE, MYF(0)); + reset_one_shot_variables(thd); break; } #endif @@ -3718,30 +3746,19 @@ purposes internal to the MySQL server", MYF(0)); break; } thd->proc_info="query end"; // QQ - if (thd->one_shot_set) - { - /* - If this is a SET, do nothing. This is to allow mysqlbinlog to print - many SET commands (in this case we want the charset temp setting to - live until the real query). This is also needed so that SET - CHARACTER_SET_CLIENT... does not cancel itself immediately. - */ - if (lex->sql_command != SQLCOM_SET_OPTION) - { - thd->variables.character_set_client= - global_system_variables.character_set_client; - thd->variables.collation_connection= - global_system_variables.collation_connection; - thd->variables.collation_database= - global_system_variables.collation_database; - thd->variables.collation_server= - global_system_variables.collation_server; - thd->update_charset(); - thd->variables.time_zone= - global_system_variables.time_zone; - thd->one_shot_set= 0; - } - } + + /* + Reset system variables temporarily modified by SET ONE SHOT. + + Exception: If this is a SET, do nothing. This is to allow + mysqlbinlog to print many SET commands (in this case we want the + charset temp setting to live until the real query). This is also + needed so that SET CHARACTER_SET_CLIENT... does not cancel itself + immediately. + */ + if (thd->one_shot_set && lex->sql_command != SQLCOM_SET_OPTION) + reset_one_shot_variables(thd); + if (res < 0) send_error(thd,thd->killed ? ER_SERVER_SHUTDOWN : 0); From bcc9c6fd93cd4f8d5ee6d441c6351fa78368e2b8 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 31 Aug 2005 10:23:29 -0700 Subject: [PATCH 06/26] Fix quoting of options passed to external commands by mysqld_multi. (Bug #11280) scripts/mysqld_multi.sh: Fix quoting of options passed to mysqld and mysqladmin --- scripts/mysqld_multi.sh | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/scripts/mysqld_multi.sh b/scripts/mysqld_multi.sh index ba46fd6fa29..7fff4cbb731 100644 --- a/scripts/mysqld_multi.sh +++ b/scripts/mysqld_multi.sh @@ -289,8 +289,10 @@ sub start_mysqlds() } else { - $options[$j]=~ s/;/\\;/g; - $tmp.= " $options[$j]"; + # we single-quote the argument, but first convert single-quotes to + # '"'"' so they are passed through correctly + $options[$j]=~ s/'/'"'"'/g; + $tmp.= " '$options[$j]'"; } } if ($opt_verbose && $com =~ m/\/safe_mysqld$/ && !$info_sent) @@ -374,7 +376,12 @@ sub get_mysqladmin_options $mysqladmin_found= 0 if (!length($mysqladmin)); $com = "$mysqladmin"; $tmp = " -u $opt_user"; - $tmp.= defined($opt_password) ? " -p$opt_password" : ""; + if (defined($opt_password)) { + my $pw= $opt_password; + # Protect single quotes in password + $pw =~ s/'/'"'"'/g; + $tmp.= " -p'$pw'"; + } $tmp.= $opt_tcp_ip ? " -h 127.0.0.1" : ""; for ($j = 0; defined($options[$j]); $j++) { From f8aadb60947b6f94ffd6ddec77036091593b54a9 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 1 Sep 2005 17:58:00 +0200 Subject: [PATCH 07/26] BUG#12542: Added comments sql/log_event.cc: Added comments sql/sql_parse.cc: Added comments --- sql/log_event.cc | 20 ++++++++++++++++++++ sql/sql_parse.cc | 3 ++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/sql/log_event.cc b/sql/log_event.cc index 66c732e8cb0..b08439a20b8 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -1008,6 +1008,16 @@ int Query_log_event::exec_event(struct st_relay_log_info* rli) #endif clear_all_errors(thd, rli); + /* + Note: We do not need to execute reset_one_shot_variables() if this + db_ok() test fails. + Reason: The db stored in binlog events is the same for SET and for + its companion query. If the SET is ignored because of + db_ok(), the companion query will also be ignored, and if + the companion query is ignored in the db_ok() test of + ::exec_event(), then the companion SET also have so we + don't need to reset_one_shot_variables(). + */ if (db_ok(thd->db, replicate_do_db, replicate_ignore_db)) { thd->set_time((time_t)when); @@ -1762,6 +1772,16 @@ int Load_log_event::exec_event(NET* net, struct st_relay_log_info* rli, Create_file_log_event::exec_event() and then discarding Append_block and al. Another way is do the filtering in the I/O thread (more efficient: no disk writes at all). + + + Note: We do not need to execute reset_one_shot_variables() if this + db_ok() test fails. + Reason: The db stored in binlog events is the same for SET and for + its companion query. If the SET is ignored because of + db_ok(), the companion query will also be ignored, and if + the companion query is ignored in the db_ok() test of + ::exec_event(), then the companion SET also have so we + don't need to reset_one_shot_variables(). */ if (db_ok(thd->db, replicate_do_db, replicate_ignore_db)) { diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 8e457b67f00..94064d5ddfa 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1998,7 +1998,8 @@ mysql_execute_command(THD *thd) Exceptions are: - SET: we always execute it (Not that many SET commands exists in - the binary log anyway) + the binary log anyway -- only 4.1 masters write SET statements, + in 5.0 there are no SET statements in the binary log) - DROP TEMPORARY TABLE IF EXISTS: we always execute it (otherwise we have stale files on slave caused by exclusion of one tmp table). */ From 7e2024b4f4838eb544f58376760fceb03a3f1676 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 2 Sep 2005 09:50:17 +0300 Subject: [PATCH 08/26] postmerge changes mysql-test/t/lowercase_view.test: error code changed after merege mysql-test/t/view.test: error code changed after merege sql/item.cc: inline function used instead of frequently used expression removed old TODO sql/sql_base.cc: layoutfixed inline function used instead of frequently used expression sql/sql_insert.cc: inline function used instead of frequently used expression --- mysql-test/t/lowercase_view.test | 68 +++++++++++++++--------------- mysql-test/t/view.test | 72 ++++++++++++++++---------------- sql/item.cc | 5 +-- sql/sql_base.cc | 6 +-- sql/sql_insert.cc | 4 +- 5 files changed, 74 insertions(+), 81 deletions(-) diff --git a/mysql-test/t/lowercase_view.test b/mysql-test/t/lowercase_view.test index 846c828fb09..e9cc26bec18 100644 --- a/mysql-test/t/lowercase_view.test +++ b/mysql-test/t/lowercase_view.test @@ -23,29 +23,29 @@ create table t2aA (col1 int); create view v1Aa as select * from t1aA; create view v2aA as select * from v1aA; create view v3Aa as select v2Aa.col1 from v2aA,t2Aa where v2Aa.col1 = t2aA.col1; --- error 1423 +-- error 1443 update v2aA set col1 = (select max(col1) from v1Aa); --- error 1423 +-- error 1443 update v2Aa set col1 = (select max(col1) from t1Aa); -- error 1093 update v2aA set col1 = (select max(col1) from v2Aa); --- error 1423 +-- error 1443 update v2aA,t2Aa set v2Aa.col1 = (select max(col1) from v1aA) where v2aA.col1 = t2aA.col1; --- error 1423 +-- error 1443 update t1aA,t2Aa set t1Aa.col1 = (select max(col1) from v1Aa) where t1aA.col1 = t2aA.col1; -- error 1093 update v1aA,t2Aa set v1Aa.col1 = (select max(col1) from v1aA) where v1Aa.col1 = t2aA.col1; --- error 1423 +-- error 1443 update t2Aa,v2Aa set v2aA.col1 = (select max(col1) from v1aA) where v2Aa.col1 = t2aA.col1; --- error 1423 +-- error 1443 update t2Aa,t1Aa set t1aA.col1 = (select max(col1) from v1Aa) where t1Aa.col1 = t2aA.col1; --- error 1423 +-- error 1443 update t2Aa,v1aA set v1Aa.col1 = (select max(col1) from v1aA) where v1Aa.col1 = t2aA.col1; --- error 1423 +-- error 1443 update v2aA,t2Aa set v2Aa.col1 = (select max(col1) from t1aA) where v2aA.col1 = t2aA.col1; -- error 1093 update t1Aa,t2Aa set t1aA.col1 = (select max(col1) from t1Aa) where t1aA.col1 = t2aA.col1; --- error 1423 +-- error 1443 update v1aA,t2Aa set v1Aa.col1 = (select max(col1) from t1Aa) where v1aA.col1 = t2aA.col1; -- error 1093 update t2Aa,v2Aa set v2aA.col1 = (select max(col1) from t1aA) where v2Aa.col1 = t2aA.col1; @@ -55,71 +55,71 @@ update t2Aa,t1Aa set t1aA.col1 = (select max(col1) from t1Aa) where t1aA.col1 = update t2Aa,v1Aa set v1aA.col1 = (select max(col1) from t1Aa) where v1Aa.col1 = t2aA.col1; -- error 1093 update v2aA,t2Aa set v2Aa.col1 = (select max(col1) from v2aA) where v2Aa.col1 = t2aA.col1; --- error 1423 +-- error 1443 update t1aA,t2Aa set t1Aa.col1 = (select max(col1) from v2aA) where t1aA.col1 = t2aA.col1; --- error 1423 +-- error 1443 update v1aA,t2Aa set v1Aa.col1 = (select max(col1) from v2Aa) where v1aA.col1 = t2aA.col1; --- error 1423 +-- error 1443 update t2Aa,v2aA set v2Aa.col1 = (select max(col1) from v2aA) where v2Aa.col1 = t2aA.col1; --- error 1423 +-- error 1443 update t2Aa,t1Aa set t1aA.col1 = (select max(col1) from v2aA) where t1Aa.col1 = t2aA.col1; --- error 1423 +-- error 1443 update t2Aa,v1Aa set v1aA.col1 = (select max(col1) from v2Aa) where v1Aa.col1 = t2aA.col1; --- error 1423 +-- error 1443 update v3aA set v3Aa.col1 = (select max(col1) from v1aA); --- error 1423 +-- error 1443 update v3aA set v3Aa.col1 = (select max(col1) from t1aA); --- error 1423 +-- error 1443 update v3aA set v3Aa.col1 = (select max(col1) from v2aA); -- error 1093 update v3aA set v3Aa.col1 = (select max(col1) from v3aA); --- error 1423 +-- error 1443 delete from v2Aa where col1 = (select max(col1) from v1Aa); --- error 1423 +-- error 1443 delete from v2aA where col1 = (select max(col1) from t1Aa); -- error 1093 delete from v2Aa where col1 = (select max(col1) from v2aA); --- error 1423 +-- error 1443 delete v2Aa from v2aA,t2Aa where (select max(col1) from v1aA) > 0 and v2Aa.col1 = t2aA.col1; --- error 1423 +-- error 1443 delete t1aA from t1Aa,t2Aa where (select max(col1) from v1Aa) > 0 and t1aA.col1 = t2aA.col1; -- error 1093 delete v1aA from v1Aa,t2Aa where (select max(col1) from v1aA) > 0 and v1Aa.col1 = t2aA.col1; --- error 1423 +-- error 1443 delete v2aA from v2Aa,t2Aa where (select max(col1) from t1Aa) > 0 and v2aA.col1 = t2aA.col1; -- error 1093 delete t1aA from t1Aa,t2Aa where (select max(col1) from t1aA) > 0 and t1Aa.col1 = t2aA.col1; --- error 1423 +-- error 1443 delete v1aA from v1Aa,t2Aa where (select max(col1) from t1aA) > 0 and v1aA.col1 = t2aA.col1; -- error 1093 delete v2Aa from v2aA,t2Aa where (select max(col1) from v2Aa) > 0 and v2aA.col1 = t2aA.col1; --- error 1423 +-- error 1443 delete t1Aa from t1aA,t2Aa where (select max(col1) from v2Aa) > 0 and t1Aa.col1 = t2aA.col1; --- error 1423 +-- error 1443 delete v1Aa from v1aA,t2Aa where (select max(col1) from v2aA) > 0 and v1Aa.col1 = t2aA.col1; --- error 1423 +-- error 1443 insert into v2Aa values ((select max(col1) from v1aA)); --- error 1423 +-- error 1443 insert into t1aA values ((select max(col1) from v1Aa)); --- error 1423 +-- error 1443 insert into v2aA values ((select max(col1) from v1aA)); --- error 1423 +-- error 1443 insert into v2Aa values ((select max(col1) from t1Aa)); -- error 1093 insert into t1aA values ((select max(col1) from t1Aa)); --- error 1423 +-- error 1443 insert into v2aA values ((select max(col1) from t1aA)); -- error 1093 insert into v2Aa values ((select max(col1) from v2aA)); --- error 1423 +-- error 1443 insert into t1Aa values ((select max(col1) from v2Aa)); -- error 1093 insert into v2aA values ((select max(col1) from v2Aa)); --- error 1423 +-- error 1443 insert into v3Aa (col1) values ((select max(col1) from v1Aa)); --- error 1423 +-- error 1443 insert into v3aA (col1) values ((select max(col1) from t1aA)); --- error 1423 +-- error 1443 insert into v3Aa (col1) values ((select max(col1) from v2aA)); drop view v3aA,v2Aa,v1aA; drop table t1Aa,t2Aa; diff --git a/mysql-test/t/view.test b/mysql-test/t/view.test index 61170c7118a..97625632618 100644 --- a/mysql-test/t/view.test +++ b/mysql-test/t/view.test @@ -827,29 +827,29 @@ create table t2 (col1 int); create view v1 as select * from t1; create view v2 as select * from v1; create view v3 as select v2.col1 from v2,t2 where v2.col1 = t2.col1; --- error 1423 +-- error 1443 update v2 set col1 = (select max(col1) from v1); --- error 1423 +-- error 1443 update v2 set col1 = (select max(col1) from t1); -- error 1093 update v2 set col1 = (select max(col1) from v2); --- error 1423 +-- error 1443 update v2,t2 set v2.col1 = (select max(col1) from v1) where v2.col1 = t2.col1; --- error 1423 +-- error 1443 update t1,t2 set t1.col1 = (select max(col1) from v1) where t1.col1 = t2.col1; -- error 1093 update v1,t2 set v1.col1 = (select max(col1) from v1) where v1.col1 = t2.col1; --- error 1423 +-- error 1443 update t2,v2 set v2.col1 = (select max(col1) from v1) where v2.col1 = t2.col1; --- error 1423 +-- error 1443 update t2,t1 set t1.col1 = (select max(col1) from v1) where t1.col1 = t2.col1; --- error 1423 +-- error 1443 update t2,v1 set v1.col1 = (select max(col1) from v1) where v1.col1 = t2.col1; --- error 1423 +-- error 1443 update v2,t2 set v2.col1 = (select max(col1) from t1) where v2.col1 = t2.col1; -- error 1093 update t1,t2 set t1.col1 = (select max(col1) from t1) where t1.col1 = t2.col1; --- error 1423 +-- error 1443 update v1,t2 set v1.col1 = (select max(col1) from t1) where v1.col1 = t2.col1; -- error 1093 update t2,v2 set v2.col1 = (select max(col1) from t1) where v2.col1 = t2.col1; @@ -859,74 +859,74 @@ update t2,t1 set t1.col1 = (select max(col1) from t1) where t1.col1 = t2.col1; update t2,v1 set v1.col1 = (select max(col1) from t1) where v1.col1 = t2.col1; -- error 1093 update v2,t2 set v2.col1 = (select max(col1) from v2) where v2.col1 = t2.col1; --- error 1423 +-- error 1443 update t1,t2 set t1.col1 = (select max(col1) from v2) where t1.col1 = t2.col1; --- error 1423 +-- error 1443 update v1,t2 set v1.col1 = (select max(col1) from v2) where v1.col1 = t2.col1; --- error 1423 +-- error 1443 update t2,v2 set v2.col1 = (select max(col1) from v2) where v2.col1 = t2.col1; --- error 1423 +-- error 1443 update t2,t1 set t1.col1 = (select max(col1) from v2) where t1.col1 = t2.col1; --- error 1423 +-- error 1443 update t2,v1 set v1.col1 = (select max(col1) from v2) where v1.col1 = t2.col1; --- error 1423 +-- error 1443 update v3 set v3.col1 = (select max(col1) from v1); --- error 1423 +-- error 1443 update v3 set v3.col1 = (select max(col1) from t1); --- error 1423 +-- error 1443 update v3 set v3.col1 = (select max(col1) from v2); -- error 1093 update v3 set v3.col1 = (select max(col1) from v3); --- error 1423 +-- error 1443 delete from v2 where col1 = (select max(col1) from v1); --- error 1423 +-- error 1443 delete from v2 where col1 = (select max(col1) from t1); -- error 1093 delete from v2 where col1 = (select max(col1) from v2); --- error 1423 +-- error 1443 delete v2 from v2,t2 where (select max(col1) from v1) > 0 and v2.col1 = t2.col1; --- error 1423 +-- error 1443 delete t1 from t1,t2 where (select max(col1) from v1) > 0 and t1.col1 = t2.col1; -- error 1093 delete v1 from v1,t2 where (select max(col1) from v1) > 0 and v1.col1 = t2.col1; --- error 1423 +-- error 1443 delete v2 from v2,t2 where (select max(col1) from t1) > 0 and v2.col1 = t2.col1; -- error 1093 delete t1 from t1,t2 where (select max(col1) from t1) > 0 and t1.col1 = t2.col1; --- error 1423 +-- error 1443 delete v1 from v1,t2 where (select max(col1) from t1) > 0 and v1.col1 = t2.col1; -- error 1093 delete v2 from v2,t2 where (select max(col1) from v2) > 0 and v2.col1 = t2.col1; --- error 1423 +-- error 1443 delete t1 from t1,t2 where (select max(col1) from v2) > 0 and t1.col1 = t2.col1; --- error 1423 +-- error 1443 delete v1 from v1,t2 where (select max(col1) from v2) > 0 and v1.col1 = t2.col1; --- error 1423 +-- error 1443 insert into v2 values ((select max(col1) from v1)); --- error 1423 +-- error 1443 insert into t1 values ((select max(col1) from v1)); --- error 1423 +-- error 1443 insert into v2 values ((select max(col1) from v1)); --- error 1423 +-- error 1443 insert into v2 values ((select max(col1) from t1)); -- error 1093 insert into t1 values ((select max(col1) from t1)); --- error 1423 +-- error 1443 insert into v2 values ((select max(col1) from t1)); -- error 1093 insert into v2 values ((select max(col1) from v2)); --- error 1423 +-- error 1443 insert into t1 values ((select max(col1) from v2)); -- error 1093 insert into v2 values ((select max(col1) from v2)); --- error 1423 +-- error 1443 insert into v3 (col1) values ((select max(col1) from v1)); --- error 1423 +-- error 1443 insert into v3 (col1) values ((select max(col1) from t1)); --- error 1423 +-- error 1443 insert into v3 (col1) values ((select max(col1) from v2)); #check with TZ tables in list --- error 1423 +-- error 1443 insert into v3 (col1) values ((select CONVERT_TZ('20050101000000','UTC','MET') from v2)); insert into v3 (col1) values ((select CONVERT_TZ('20050101000000','UTC','MET') from t2)); -- error 1048 @@ -1769,7 +1769,7 @@ create view v1 as select f59, f60 from t1 where f59 in (select f59 from t1); -- error 1288 update v1 set f60=2345; --- error 1423 +-- error 1443 update t1 set f60=(select max(f60) from v1); drop view v1; drop table t1; diff --git a/sql/item.cc b/sql/item.cc index 1bbef46c0c9..995ade53620 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -4980,10 +4980,7 @@ int Item_default_value::save_in_field(Field *field_arg, bool no_conversions) { if (context->error_processor == &view_error_processor) { - TABLE_LIST *view= (cached_table->belong_to_view ? - cached_table->belong_to_view : - cached_table); - // TODO: make correct error message + TABLE_LIST *view= cached_table->top_table(); push_warning_printf(field_arg->table->in_use, MYSQL_ERROR::WARN_LEVEL_WARN, ER_NO_DEFAULT_FOR_VIEW_FIELD, diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 1f9ecd6e0f1..88835ad935c 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -1864,7 +1864,7 @@ err: if (thd->net.last_errno == ER_NO_SUCH_TABLE && table_desc && table_desc->belong_to_view) { - TABLE_LIST * view= table_desc->belong_to_view; + TABLE_LIST *view= table_desc->belong_to_view; thd->clear_error(); my_error(ER_VIEW_INVALID, MYF(0), view->view_db.str, view->view_name.str); } @@ -4624,9 +4624,7 @@ int setup_conds(THD *thd, TABLE_LIST *tables, TABLE_LIST *leaves, /* process CHECK OPTION */ if (it_is_update) { - TABLE_LIST *view= table->belong_to_view; - if (!view) - view= table; + TABLE_LIST *view= table->top_table(); if (view->effective_with_check) { if (view->prepare_check_option(thd)) diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 43a23dd31f0..9421cc4bb6b 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -1195,9 +1195,7 @@ int check_that_all_fields_are_given_values(THD *thd, TABLE *entry, bool view= FALSE; if (table_list) { - table_list= (table_list->belong_to_view ? - table_list->belong_to_view : - table_list); + table_list= table_list->top_table(); view= test(table_list->view); } if (view) From 7056f0b3f01f8ef73216c1e21a082e9fea8714fd Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 2 Sep 2005 11:47:24 +0200 Subject: [PATCH 09/26] ndb - bug #10987 make sure not to cut log to early (specificly not use LCP with maxGciStarted that has not yet completed) ndb/include/kernel/signaldata/SignalData.hpp: Add signal data printer for START_FRAG_REQ ndb/include/kernel/signaldata/StartFragReq.hpp: Add signal data printer for START_FRAG_REQ ndb/src/common/debugger/signaldata/SignalDataPrint.cpp: Add signal data printer for START_FRAG_REQ ndb/src/common/debugger/signaldata/StartRec.cpp: Add signal data printer for START_FRAG_REQ ndb/src/kernel/blocks/dbdih/DbdihMain.cpp: Add maxGciStarted/Completed to event report bug: dont use LCP for calcKeepGci in maxGciSTarted is not completed --- ndb/include/kernel/signaldata/SignalData.hpp | 1 + .../kernel/signaldata/StartFragReq.hpp | 2 ++ .../debugger/signaldata/SignalDataPrint.cpp | 1 + .../common/debugger/signaldata/StartRec.cpp | 25 +++++++++++++++++++ ndb/src/kernel/blocks/dbdih/DbdihMain.cpp | 12 ++++++--- 5 files changed, 38 insertions(+), 3 deletions(-) diff --git a/ndb/include/kernel/signaldata/SignalData.hpp b/ndb/include/kernel/signaldata/SignalData.hpp index f9d3a6faa64..b0cfbc1540c 100644 --- a/ndb/include/kernel/signaldata/SignalData.hpp +++ b/ndb/include/kernel/signaldata/SignalData.hpp @@ -215,5 +215,6 @@ GSN_PRINT_SIGNATURE(printSCAN_FRAGREQ); GSN_PRINT_SIGNATURE(printCONTINUEB_NDBFS); GSN_PRINT_SIGNATURE(printCONTINUEB_DBDIH); +GSN_PRINT_SIGNATURE(printSTART_FRAG_REQ); #endif diff --git a/ndb/include/kernel/signaldata/StartFragReq.hpp b/ndb/include/kernel/signaldata/StartFragReq.hpp index ec05c1ee366..ab17a147195 100644 --- a/ndb/include/kernel/signaldata/StartFragReq.hpp +++ b/ndb/include/kernel/signaldata/StartFragReq.hpp @@ -32,6 +32,8 @@ class StartFragReq { public: STATIC_CONST( SignalLength = 19 ); + friend bool printSTART_FRAG_REQ(FILE *, const Uint32 *, Uint32, Uint16); + private: Uint32 userPtr; Uint32 userRef; diff --git a/ndb/src/common/debugger/signaldata/SignalDataPrint.cpp b/ndb/src/common/debugger/signaldata/SignalDataPrint.cpp index a4cee38e06f..bd1dff074f9 100644 --- a/ndb/src/common/debugger/signaldata/SignalDataPrint.cpp +++ b/ndb/src/common/debugger/signaldata/SignalDataPrint.cpp @@ -195,6 +195,7 @@ SignalDataPrintFunctions[] = { ,{ GSN_ACC_LOCKREQ, printACC_LOCKREQ } ,{ GSN_LQH_TRANSCONF, printLQH_TRANSCONF } ,{ GSN_SCAN_FRAGREQ, printSCAN_FRAGREQ } + ,{ GSN_START_FRAGREQ, printSTART_FRAG_REQ } ,{ 0, 0 } }; diff --git a/ndb/src/common/debugger/signaldata/StartRec.cpp b/ndb/src/common/debugger/signaldata/StartRec.cpp index 482e3cb0728..54830e533c5 100644 --- a/ndb/src/common/debugger/signaldata/StartRec.cpp +++ b/ndb/src/common/debugger/signaldata/StartRec.cpp @@ -17,6 +17,7 @@ #include #include +#include bool printSTART_REC_REQ(FILE * output, @@ -50,3 +51,27 @@ printSTART_REC_CONF(FILE * output, return true; } + +bool +printSTART_FRAG_REQ(FILE * output, + const Uint32 * theData, + Uint32 len, + Uint16 recBlockNo) +{ + StartFragReq* sig = (StartFragReq*)theData; + + fprintf(output, " table: %d frag: %d lcpId: %d lcpNo: %d #nodes: %d \n", + sig->tableId, sig->fragId, sig->lcpId, sig->lcpNo, + sig->noOfLogNodes); + + for(Uint32 i = 0; inoOfLogNodes; i++) + { + fprintf(output, " (node: %d startGci: %d lastGci: %d)", + sig->lqhLogNode[i], + sig->startGci[i], + sig->lastGci[i]); + } + + fprintf(output, "\n"); + return true; +} diff --git a/ndb/src/kernel/blocks/dbdih/DbdihMain.cpp b/ndb/src/kernel/blocks/dbdih/DbdihMain.cpp index 669be2b48f0..ca066b588e7 100644 --- a/ndb/src/kernel/blocks/dbdih/DbdihMain.cpp +++ b/ndb/src/kernel/blocks/dbdih/DbdihMain.cpp @@ -9640,6 +9640,9 @@ void Dbdih::execLCP_FRAG_REP(Signal* signal) } bool tableDone = reportLcpCompletion(lcpReport); + + Uint32 started = lcpReport->maxGciStarted; + Uint32 completed = lcpReport->maxGciCompleted; if(tableDone){ jam(); @@ -9673,7 +9676,9 @@ void Dbdih::execLCP_FRAG_REP(Signal* signal) signal->theData[1] = nodeId; signal->theData[2] = tableId; signal->theData[3] = fragId; - sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 4, JBB); + signal->theData[4] = started; + signal->theData[5] = completed; + sendSignal(CMVMI_REF, GSN_EVENT_REP, signal, 6, JBB); #endif bool ok = false; @@ -10790,7 +10795,9 @@ void Dbdih::findMinGci(ReplicaRecordPtr fmgReplicaPtr, lcpNo = fmgReplicaPtr.p->nextLcp; do { ndbrequire(lcpNo < MAX_LCP_STORED); - if (fmgReplicaPtr.p->lcpStatus[lcpNo] == ZVALID) { + if (fmgReplicaPtr.p->lcpStatus[lcpNo] == ZVALID && + fmgReplicaPtr.p->maxGciStarted[lcpNo] <= coldgcp) + { jam(); keepGci = fmgReplicaPtr.p->maxGciCompleted[lcpNo]; oldestRestorableGci = fmgReplicaPtr.p->maxGciStarted[lcpNo]; @@ -10798,7 +10805,6 @@ void Dbdih::findMinGci(ReplicaRecordPtr fmgReplicaPtr, return; } else { jam(); - ndbrequire(fmgReplicaPtr.p->lcpStatus[lcpNo] == ZINVALID); if (fmgReplicaPtr.p->createGci[0] == fmgReplicaPtr.p->initialGci) { jam(); /*------------------------------------------------------------------- From 21ce9ede3c6081b134421854a1776436e68985c4 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 2 Sep 2005 11:50:50 +0200 Subject: [PATCH 10/26] ndb - bug#12608 Always abort if node failure occured between startTransaction and commit (can later be improved by wl#2610) ndb/src/kernel/blocks/dbtc/DbtcMain.cpp: Always abort if node failure occured between startTransaction and commit --- ndb/src/kernel/blocks/dbtc/DbtcMain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ndb/src/kernel/blocks/dbtc/DbtcMain.cpp b/ndb/src/kernel/blocks/dbtc/DbtcMain.cpp index 93b122b9a99..f0861d1f0cc 100644 --- a/ndb/src/kernel/blocks/dbtc/DbtcMain.cpp +++ b/ndb/src/kernel/blocks/dbtc/DbtcMain.cpp @@ -4404,7 +4404,7 @@ Dbtc::DIVER_node_fail_handling(Signal* signal, UintR Tgci) *------------------------------------------------------------------------*/ tabortInd = ZFALSE; setupFailData(signal); - if (tabortInd == ZFALSE) { + if (false && tabortInd == ZFALSE) { jam(); commitGciHandling(signal, Tgci); toCommitHandlingLab(signal); From f9d29cfeedeafea81e7cc368648b3179dc419349 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 2 Sep 2005 11:55:41 +0200 Subject: [PATCH 11/26] ndb - fix printout related to bug#10987 ndb/src/common/debugger/EventLogger.cpp: Fix extended printout of LCP_FRAGMENT_COMPLETED --- ndb/src/common/debugger/EventLogger.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ndb/src/common/debugger/EventLogger.cpp b/ndb/src/common/debugger/EventLogger.cpp index 97945394690..f7cef644c64 100644 --- a/ndb/src/common/debugger/EventLogger.cpp +++ b/ndb/src/common/debugger/EventLogger.cpp @@ -585,11 +585,13 @@ EventLogger::getText(char * m_text, size_t m_text_len, BaseString::snprintf(m_text, m_text_len, "%sTable ID = %u, fragment ID = %u has completed LCP " - "on Node %u", + "on Node %u maxGciStarted: %d maxGciCompleted: %d", theNodeId, theData[2], theData[3], - theData[1]); + theData[1], + theData[4], + theData[5]); break; case EventReport::TransReportCounters: // ------------------------------------------------------------------- From 8f0530d0150b0c1beb2a9531a5f97bbfa29c4b67 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 2 Sep 2005 12:20:18 +0200 Subject: [PATCH 12/26] Removed redundant case in Makefile.am Makefile.am: Removed redundant case --- Makefile.am | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Makefile.am b/Makefile.am index 353a05eeb14..ee0353349ea 100644 --- a/Makefile.am +++ b/Makefile.am @@ -97,12 +97,6 @@ tags: support-files/build-tags .PHONY: init-db bin-dist -# Test installation - -test: - cd mysql-test; \ - ./mysql-test-run && ./mysql-test-run --ps-protocol - # Test installation. Ports are configurable from the environment. MYSQL_TEST_MANAGER_PORT = 9305 From dc92221300d4d5b9e72d85373187d14d7f398c19 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 2 Sep 2005 17:21:19 +0400 Subject: [PATCH 13/26] Rename: - current_arena to stmt_arena: the thread may have more than one 'current' arenas: one for runtime data, and one for the parsed tree of a statement. Only one of them is active at any moment. - set_item_arena -> set_query_arena, because Item_arena was renamed to Query_arena a while ago - set_n_backup_item_arena -> set_n_backup_active_arena; the active arena is the arena thd->mem_root and thd->free_list are currently pointing at. - restore_backup_item_arena -> restore_active_arena (with the same rationale) - change_arena_if_needed -> activate_stmt_arena_if_needed; this method sets thd->stmt_arena active if it's not done yet. sql/item.cc: Rename. sql/item_cmpfunc.cc: Rename. sql/item_func.cc: Rename. sql/item_subselect.cc: Rename. sql/item_subselect.h: Remove an unused forward declaration. sql/item_sum.h: Remove an unused forward declaration. sql/mysql_priv.h: Remove an unused forward declaration. sql/sp.cc: Rename. sql/sp_head.cc: Rename. sql/sql_base.cc: Rename. sql/sql_class.cc: Rename. sql/sql_class.h: Rename. sql/sql_lex.cc: Rename. sql/sql_parse.cc: Rename. sql/sql_prepare.cc: Rename. sql/sql_select.cc: Rename. sql/sql_show.cc: Rename. sql/sql_union.cc: Rename. sql/sql_view.cc: Rename. sql/table.cc: Rename. --- sql/item.cc | 6 ++-- sql/item_cmpfunc.cc | 2 +- sql/item_func.cc | 4 +-- sql/item_subselect.cc | 12 ++++---- sql/item_subselect.h | 1 - sql/item_sum.h | 2 -- sql/mysql_priv.h | 1 - sql/sp.cc | 4 +-- sql/sp_head.cc | 72 ++++++++++++++++++++----------------------- sql/sql_base.cc | 32 +++++++++---------- sql/sql_class.cc | 22 ++++++------- sql/sql_class.h | 35 ++++++++++----------- sql/sql_lex.cc | 6 ++-- sql/sql_parse.cc | 6 ++-- sql/sql_prepare.cc | 22 ++++++------- sql/sql_select.cc | 12 ++++---- sql/sql_show.cc | 2 +- sql/sql_union.cc | 8 ++--- sql/sql_view.cc | 8 ++--- sql/table.cc | 8 ++--- 20 files changed, 128 insertions(+), 137 deletions(-) diff --git a/sql/item.cc b/sql/item.cc index 1bbef46c0c9..3099a13e57c 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -1296,7 +1296,7 @@ bool agg_item_charsets(DTCollation &coll, const char *fname, In case we're in statement prepare, create conversion item in its memory: it will be reused on each execute. */ - arena= thd->change_arena_if_needed(&backup); + arena= thd->activate_stmt_arena_if_needed(&backup); for (arg= args, last= args + nargs; arg < last; arg++) { @@ -1342,7 +1342,7 @@ bool agg_item_charsets(DTCollation &coll, const char *fname, conv->fix_fields(thd, arg); } if (arena) - thd->restore_backup_item_arena(arena, &backup); + thd->restore_active_arena(arena, &backup); return res; } @@ -1385,7 +1385,7 @@ Item_field::Item_field(THD *thd, Name_resolution_context *context_arg, structure can go away and pop up again between subsequent executions of a prepared statement). */ - if (thd->current_arena->is_stmt_prepare_or_first_sp_execute()) + if (thd->stmt_arena->is_stmt_prepare_or_first_sp_execute()) { if (db_name) orig_db_name= thd->strdup(db_name); diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 9443a2949d8..cc2849ff7e6 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -671,7 +671,7 @@ bool Item_in_optimizer::fix_left(THD *thd, Item **ref) If it is preparation PS only then we do not know values of parameters => cant't get there values and do not need that values. */ - if (!thd->current_arena->is_stmt_prepare()) + if (!thd->stmt_arena->is_stmt_prepare()) cache->store(args[0]); if (cache->cols() == 1) { diff --git a/sql/item_func.cc b/sql/item_func.cc index 8125264ab15..00ce70f6508 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -1875,7 +1875,7 @@ bool Item_func_rand::fix_fields(THD *thd,Item **ref) if (arg_count) { // Only use argument once in query /* - Allocate rand structure once: we must use thd->current_arena + Allocate rand structure once: we must use thd->stmt_arena to create rand in proper mem_root if it's a prepared statement or stored procedure. @@ -1883,7 +1883,7 @@ bool Item_func_rand::fix_fields(THD *thd,Item **ref) as it will be replicated in the query as such. */ if (!rand && !(rand= (struct rand_struct*) - thd->current_arena->alloc(sizeof(*rand)))) + thd->stmt_arena->alloc(sizeof(*rand)))) return TRUE; /* PARAM_ITEM is returned if we're in statement prepare and consequently diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index a5985a6c4a9..52a74b6f4c6 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -333,7 +333,7 @@ Item_singlerow_subselect::select_transformer(JOIN *join) return RES_OK; SELECT_LEX *select_lex= join->select_lex; - Query_arena *arena= thd->current_arena; + Query_arena *arena= thd->stmt_arena; if (!select_lex->master_unit()->first_select()->next_select() && !select_lex->table_list.elements && @@ -1287,10 +1287,10 @@ Item_in_subselect::select_in_like_transformer(JOIN *join, Comp_creator *func) */ if (!optimizer) { - arena= thd->change_arena_if_needed(&backup); + arena= thd->activate_stmt_arena_if_needed(&backup); result= (!(optimizer= new Item_in_optimizer(left_expr, this))); if (arena) - thd->restore_backup_item_arena(arena, &backup); + thd->restore_active_arena(arena, &backup); if (result) goto err; } @@ -1306,7 +1306,7 @@ Item_in_subselect::select_in_like_transformer(JOIN *join, Comp_creator *func) goto err; transformed= 1; - arena= thd->change_arena_if_needed(&backup); + arena= thd->activate_stmt_arena_if_needed(&backup); /* Both transformers call fix_fields() only for Items created inside them, and all that items do not make permanent changes in current item arena @@ -1322,14 +1322,14 @@ Item_in_subselect::select_in_like_transformer(JOIN *join, Comp_creator *func) if (func != &eq_creator) { if (arena) - thd->restore_backup_item_arena(arena, &backup); + thd->restore_active_arena(arena, &backup); my_error(ER_OPERAND_COLUMNS, MYF(0), 1); DBUG_RETURN(RES_ERROR); } res= row_value_transformer(join); } if (arena) - thd->restore_backup_item_arena(arena, &backup); + thd->restore_active_arena(arena, &backup); err: thd->where= save_where; DBUG_RETURN(res); diff --git a/sql/item_subselect.h b/sql/item_subselect.h index 46623f76170..5b22930ae1f 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -26,7 +26,6 @@ class JOIN; class select_subselect; class subselect_engine; class Item_bool_func2; -class Item_arena; /* base class for subselects */ diff --git a/sql/item_sum.h b/sql/item_sum.h index 32a1d8dd923..0da9178eabf 100644 --- a/sql/item_sum.h +++ b/sql/item_sum.h @@ -23,8 +23,6 @@ #include -class Item_arena; - class Item_sum :public Item_result_field { public: diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 7e21a19bd66..1ab753e0938 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -408,7 +408,6 @@ enum enum_parsing_place struct st_table; class THD; -class Item_arena; /* Struct to handle simple linked lists */ diff --git a/sql/sp.cc b/sql/sp.cc index 56da38e6cab..016703662a5 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -1338,7 +1338,7 @@ static void sp_update_stmt_used_routines(THD *thd, LEX *lex, HASH *src) for (uint i=0 ; i < src->records ; i++) { Sroutine_hash_entry *rt= (Sroutine_hash_entry *)hash_element(src, i); - (void)add_used_routine(lex, thd->current_arena, &rt->key); + (void)add_used_routine(lex, thd->stmt_arena, &rt->key); } } @@ -1485,7 +1485,7 @@ void sp_cache_routines_and_add_tables_for_triggers(THD *thd, LEX *lex, Table_triggers_list *triggers) { - if (add_used_routine(lex, thd->current_arena, &triggers->sroutines_key)) + if (add_used_routine(lex, thd->stmt_arena, &triggers->sroutines_key)) { Sroutine_hash_entry **last_cached_routine_ptr= (Sroutine_hash_entry **)lex->sroutines_list.next; diff --git a/sql/sp_head.cc b/sql/sp_head.cc index e04523902db..59169f45f1b 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -131,12 +131,12 @@ sp_prepare_func_item(THD* thd, Item **it_addr) do \ { \ if (condition) \ - thd->set_n_backup_item_arena(thd->spcont->callers_arena, \ - backup_arena); \ + thd->set_n_backup_active_arena(thd->spcont->callers_arena, \ + backup_arena); \ new_command; \ if (condition) \ - thd->restore_backup_item_arena(thd->spcont->callers_arena, \ - &backup_current_arena); \ + thd->restore_active_arena(thd->spcont->callers_arena, \ + backup_arena); \ } while(0) /* @@ -167,7 +167,7 @@ sp_eval_func_item(THD *thd, Item **it_addr, enum enum_field_types type, DBUG_ENTER("sp_eval_func_item"); Item *it= sp_prepare_func_item(thd, it_addr); uint rsize; - Query_arena backup_current_arena; + Query_arena backup_arena; DBUG_PRINT("info", ("type: %d", type)); if (!it) @@ -187,7 +187,7 @@ sp_eval_func_item(THD *thd, Item **it_addr, enum enum_field_types type, } DBUG_PRINT("info", ("INT_RESULT: %d", i)); CREATE_ON_CALLERS_ARENA(it= new(reuse, &rsize) Item_int(i), - use_callers_arena, &backup_current_arena); + use_callers_arena, &backup_arena); break; } case REAL_RESULT: @@ -210,7 +210,7 @@ sp_eval_func_item(THD *thd, Item **it_addr, enum enum_field_types type, max_length= it->max_length; DBUG_PRINT("info", ("REAL_RESULT: %g", d)); CREATE_ON_CALLERS_ARENA(it= new(reuse, &rsize) Item_float(d), - use_callers_arena, &backup_current_arena); + use_callers_arena, &backup_arena); it->decimals= decimals; it->max_length= max_length; break; @@ -221,7 +221,7 @@ sp_eval_func_item(THD *thd, Item **it_addr, enum enum_field_types type, if (it->null_value) goto return_null_item; CREATE_ON_CALLERS_ARENA(it= new(reuse, &rsize) Item_decimal(val), - use_callers_arena, &backup_current_arena); + use_callers_arena, &backup_arena); #ifndef DBUG_OFF { char dbug_buff[DECIMAL_MAX_STR_LENGTH+1]; @@ -246,7 +246,7 @@ sp_eval_func_item(THD *thd, Item **it_addr, enum enum_field_types type, s->length(), s->c_ptr_quick())); CHARSET_INFO *itcs= it->collation.collation; CREATE_ON_CALLERS_ARENA(it= new(reuse, &rsize) Item_string(itcs), - use_callers_arena, &backup_current_arena); + use_callers_arena, &backup_arena); /* We have to use special constructor and allocate string on system heap here. This is because usual Item_string @@ -276,7 +276,7 @@ sp_eval_func_item(THD *thd, Item **it_addr, enum enum_field_types type, return_null_item: CREATE_ON_CALLERS_ARENA(it= new(reuse, &rsize) Item_null(), - use_callers_arena, &backup_current_arena); + use_callers_arena, &backup_arena); it->rsize= rsize; DBUG_RETURN(it); @@ -765,7 +765,7 @@ int sp_head::execute(THD *thd) /* per-instruction arena */ MEM_ROOT execute_mem_root; Query_arena execute_arena(&execute_mem_root, INITIALIZED_FOR_SP), - execute_backup_arena; + backup_arena; query_id_t old_query_id; TABLE *old_derived_tables; LEX *old_lex; @@ -812,7 +812,7 @@ int sp_head::execute(THD *thd) if ((ctx= thd->spcont)) ctx->clear_handler(); thd->query_error= 0; - old_arena= thd->current_arena; + old_arena= thd->stmt_arena; /* We have to save/restore this info when we are changing call level to @@ -848,13 +848,13 @@ int sp_head::execute(THD *thd) Switch to per-instruction arena here. We can do it since we cleanup arena after every instruction. */ - thd->set_n_backup_item_arena(&execute_arena, &execute_backup_arena); + thd->set_n_backup_active_arena(&execute_arena, &backup_arena); /* Save callers arena in order to store instruction results and out parameters in it later during sp_eval_func_item() */ - thd->spcont->callers_arena= &execute_backup_arena; + thd->spcont->callers_arena= &backup_arena; do { @@ -869,12 +869,12 @@ int sp_head::execute(THD *thd) if (!thd->in_sub_stmt) thd->set_time(); // Make current_time() et al work /* - We have to set thd->current_arena before executing the instruction + We have to set thd->stmt_arena before executing the instruction to store in the instruction free_list all new items, created during the first execution (for example expanding of '*' or the items made during other permanent subquery transformations). */ - thd->current_arena= i; + thd->stmt_arena= i; ret= i->execute(thd, &ip); /* @@ -907,9 +907,9 @@ int sp_head::execute(THD *thd) case SP_HANDLER_NONE: break; case SP_HANDLER_CONTINUE: - thd->restore_backup_item_arena(&execute_arena, &execute_backup_arena); + thd->restore_active_arena(&execute_arena, &backup_arena); ctx->save_variables(hf); - thd->set_n_backup_item_arena(&execute_arena, &execute_backup_arena); + thd->set_n_backup_active_arena(&execute_arena, &backup_arena); ctx->push_hstack(ip); // Fall through default: @@ -924,7 +924,7 @@ int sp_head::execute(THD *thd) } } while (ret == 0 && !thd->killed); - thd->restore_backup_item_arena(&execute_arena, &execute_backup_arena); + thd->restore_active_arena(&execute_arena, &backup_arena); /* Restore all saved */ @@ -939,7 +939,7 @@ int sp_head::execute(THD *thd) thd->derived_tables= old_derived_tables; thd->variables.sql_mode= save_sql_mode; - thd->current_arena= old_arena; + thd->stmt_arena= old_arena; state= EXECUTED; done: @@ -1532,7 +1532,7 @@ sp_head::restore_thd_mem_root(THD *thd) { DBUG_ENTER("sp_head::restore_thd_mem_root"); Item *flist= free_list; // The old list - set_item_arena(thd); // Get new free_list and mem_root + set_query_arena(thd); // Get new free_list and mem_root state= INITIALIZED_FOR_SP; DBUG_PRINT("info", ("mem_root 0x%lx returned from thd mem root 0x%lx", @@ -2359,20 +2359,18 @@ sp_instr_hreturn::opt_mark(sp_head *sp) int sp_instr_cpush::execute(THD *thd, uint *nextp) { - Query_arena backup_current_arena; + Query_arena backup_arena; DBUG_ENTER("sp_instr_cpush::execute"); /* We should create cursors in the callers arena, as it could be (and usually is) used in several instructions. */ - thd->set_n_backup_item_arena(thd->spcont->callers_arena, - &backup_current_arena); + thd->set_n_backup_active_arena(thd->spcont->callers_arena, &backup_arena); thd->spcont->push_cursor(&m_lex_keeper, this); - thd->restore_backup_item_arena(thd->spcont->callers_arena, - &backup_current_arena); + thd->restore_active_arena(thd->spcont->callers_arena, &backup_arena); *nextp= m_ip+1; @@ -2439,19 +2437,19 @@ sp_instr_copen::execute(THD *thd, uint *nextp) } else { - Query_arena *old_arena= thd->current_arena; + Query_arena *old_arena= thd->stmt_arena; /* Get the Query_arena from the cpush instruction, which contains the free_list of the query, so new items (if any) are stored in the right free_list, and we can cleanup after each open. */ - thd->current_arena= c->get_instr(); + thd->stmt_arena= c->get_instr(); res= lex_keeper->reset_lex_and_exec_core(thd, nextp, FALSE, this); /* Cleanup the query's items */ - if (thd->current_arena->free_list) - cleanup_items(thd->current_arena->free_list); - thd->current_arena= old_arena; + if (thd->stmt_arena->free_list) + cleanup_items(thd->stmt_arena->free_list); + thd->stmt_arena= old_arena; /* Work around the fact that errors in selects are not returned properly (but instead converted into a warning), so if a condition handler @@ -2526,18 +2524,16 @@ sp_instr_cfetch::execute(THD *thd, uint *nextp) { sp_cursor *c= thd->spcont->get_cursor(m_cursor); int res; - Query_arena backup_current_arena; + Query_arena backup_arena; DBUG_ENTER("sp_instr_cfetch::execute"); if (! c) res= -1; else { - thd->set_n_backup_item_arena(thd->spcont->callers_arena, - &backup_current_arena); + thd->set_n_backup_active_arena(thd->spcont->callers_arena, &backup_arena); res= c->fetch(thd, &m_varlist); - thd->restore_backup_item_arena(thd->spcont->callers_arena, - &backup_current_arena); + thd->restore_active_arena(thd->spcont->callers_arena, &backup_arena); } *nextp= m_ip+1; @@ -2790,7 +2786,7 @@ sp_head::add_used_tables_to_table_list(THD *thd, /* Use persistent arena for table list allocation to be PS friendly. */ - arena= thd->change_arena_if_needed(&backup); + arena= thd->activate_stmt_arena_if_needed(&backup); for (i=0 ; i < m_sptabs.records ; i++) { @@ -2835,7 +2831,7 @@ sp_head::add_used_tables_to_table_list(THD *thd, } if (arena) - thd->restore_backup_item_arena(arena, &backup); + thd->restore_active_arena(arena, &backup); DBUG_RETURN(result); } diff --git a/sql/sql_base.cc b/sql/sql_base.cc index b8b96f14205..27b6fc5389d 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -3422,7 +3422,7 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2, table_ref_1->alias, table_ref_2->alias)); *found_using_fields= 0; - arena= thd->change_arena_if_needed(&backup); + arena= thd->activate_stmt_arena_if_needed(&backup); /* TABLE_LIST::join_columns could be allocated by the previous call to @@ -3585,7 +3585,7 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2, err: if (arena) - thd->restore_backup_item_arena(arena, &backup); + thd->restore_active_arena(arena, &backup); DBUG_RETURN(result); } @@ -3643,7 +3643,7 @@ store_natural_using_join_columns(THD *thd, TABLE_LIST *natural_using_join, DBUG_ASSERT(!natural_using_join->join_columns); - arena= thd->change_arena_if_needed(&backup); + arena= thd->activate_stmt_arena_if_needed(&backup); if (!(non_join_columns= new List) || !(natural_using_join->join_columns= new List)) @@ -3728,7 +3728,7 @@ store_natural_using_join_columns(THD *thd, TABLE_LIST *natural_using_join, err: if (arena) - thd->restore_backup_item_arena(arena, &backup); + thd->restore_active_arena(arena, &backup); DBUG_RETURN(result); } @@ -3773,7 +3773,7 @@ store_top_level_join_columns(THD *thd, TABLE_LIST *table_ref, DBUG_ENTER("store_top_level_join_columns"); - arena= thd->change_arena_if_needed(&backup); + arena= thd->activate_stmt_arena_if_needed(&backup); /* Call the procedure recursively for each nested table reference. */ if (table_ref->nested_join) @@ -3886,7 +3886,7 @@ store_top_level_join_columns(THD *thd, TABLE_LIST *table_ref, err: if (arena) - thd->restore_backup_item_arena(arena, &backup); + thd->restore_active_arena(arena, &backup); DBUG_RETURN(result); } @@ -3985,7 +3985,7 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List &fields, Don't use arena if we are not in prepared statements or stored procedures For PS/SP we have to use arena to remember the changes */ - arena= thd->change_arena_if_needed(&backup); + arena= thd->activate_stmt_arena_if_needed(&backup); while (wild_num && (item= it++)) { @@ -4013,7 +4013,7 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List &fields, any_privileges)) { if (arena) - thd->restore_backup_item_arena(arena, &backup); + thd->restore_active_arena(arena, &backup); DBUG_RETURN(-1); } if (sum_func_list) @@ -4035,7 +4035,7 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List &fields, select_lex->with_wild= 0; select_lex->item_list= fields; - thd->restore_backup_item_arena(arena, &backup); + thd->restore_active_arena(arena, &backup); } DBUG_RETURN(0); } @@ -4213,15 +4213,15 @@ bool setup_tables(THD *thd, Name_resolution_context *context, if (table_list->ancestor) { DBUG_ASSERT(table_list->view); - Query_arena *arena= thd->current_arena, backup; + Query_arena *arena= thd->stmt_arena, backup; bool res; if (arena->is_conventional()) arena= 0; // For easier test else - thd->set_n_backup_item_arena(arena, &backup); + thd->set_n_backup_active_arena(arena, &backup); res= table_list->setup_ancestor(thd); if (arena) - thd->restore_backup_item_arena(arena, &backup); + thd->restore_active_arena(arena, &backup); if (res) DBUG_RETURN(1); } @@ -4302,7 +4302,7 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name, bool found; char name_buff[NAME_LEN+1]; DBUG_ENTER("insert_fields"); - DBUG_PRINT("arena", ("current arena: 0x%lx", (ulong)thd->current_arena)); + DBUG_PRINT("arena", ("stmt arena: 0x%lx", (ulong)thd->stmt_arena)); if (db_name && lower_case_table_names) { @@ -4508,7 +4508,7 @@ int setup_conds(THD *thd, TABLE_LIST *tables, TABLE_LIST *leaves, COND **conds) { SELECT_LEX *select_lex= thd->lex->current_select; - Query_arena *arena= thd->current_arena, backup; + Query_arena *arena= thd->stmt_arena, backup; TABLE_LIST *table= NULL; // For HP compilers /* it_is_update set to TRUE when tables of primary SELECT_LEX (SELECT_LEX @@ -4584,7 +4584,7 @@ int setup_conds(THD *thd, TABLE_LIST *tables, TABLE_LIST *leaves, } } - if (!thd->current_arena->is_conventional()) + if (!thd->stmt_arena->is_conventional()) { /* We are in prepared statement preparation code => we should store @@ -4599,7 +4599,7 @@ int setup_conds(THD *thd, TABLE_LIST *tables, TABLE_LIST *leaves, err: if (arena) - thd->restore_backup_item_arena(arena, &backup); + thd->restore_active_arena(arena, &backup); err_no_arena: DBUG_RETURN(1); } diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 4089042315f..17a48fb4925 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -180,7 +180,7 @@ THD::THD() in_lock_tables(0), bootstrap(0), derived_tables_processing(FALSE), spcont(NULL) { - current_arena= this; + stmt_arena= this; host= user= priv_user= db= ip= 0; catalog= (char*)"std"; // the only catalog we have for now host_or_ip= "connecting host"; @@ -794,7 +794,7 @@ struct Item_change_record: public ilink /* Register an item tree tree transformation, performed by the query optimizer. We need a pointer to runtime_memroot because it may be != - thd->mem_root (due to possible set_n_backup_item_arena called for thd). + thd->mem_root (due to possible set_n_backup_active_arena called for thd). */ void THD::nocheck_register_item_tree_change(Item **place, Item *old_value, @@ -1602,13 +1602,13 @@ void THD::end_statement() } -void Query_arena::set_n_backup_item_arena(Query_arena *set, Query_arena *backup) +void THD::set_n_backup_active_arena(Query_arena *set, Query_arena *backup) { - DBUG_ENTER("Query_arena::set_n_backup_item_arena"); + DBUG_ENTER("THD::set_n_backup_active_arena"); DBUG_ASSERT(backup->is_backup_arena == FALSE); - backup->set_item_arena(this); - set_item_arena(set); + backup->set_query_arena(this); + set_query_arena(set); #ifndef DBUG_OFF backup->is_backup_arena= TRUE; #endif @@ -1616,19 +1616,19 @@ void Query_arena::set_n_backup_item_arena(Query_arena *set, Query_arena *backup) } -void Query_arena::restore_backup_item_arena(Query_arena *set, Query_arena *backup) +void THD::restore_active_arena(Query_arena *set, Query_arena *backup) { - DBUG_ENTER("Query_arena::restore_backup_item_arena"); + DBUG_ENTER("THD::restore_active_arena"); DBUG_ASSERT(backup->is_backup_arena); - set->set_item_arena(this); - set_item_arena(backup); + set->set_query_arena(this); + set_query_arena(backup); #ifndef DBUG_OFF backup->is_backup_arena= FALSE; #endif DBUG_VOID_RETURN; } -void Query_arena::set_item_arena(Query_arena *set) +void Query_arena::set_query_arena(Query_arena *set) { mem_root= set->mem_root; free_list= set->free_list; diff --git a/sql/sql_class.h b/sql/sql_class.h index 305a03a9e8d..b7073ecf5c5 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -733,9 +733,7 @@ public: return ptr; } - void set_n_backup_item_arena(Query_arena *set, Query_arena *backup); - void restore_backup_item_arena(Query_arena *set, Query_arena *backup); - void set_item_arena(Query_arena *set); + void set_query_arena(Query_arena *set); void free_items(); }; @@ -1230,16 +1228,16 @@ public: /* A permanent memory area of the statement. For conventional execution, the parsed tree and execution runtime reside in the same - memory root. In this case current_arena points to THD. In case of + memory root. In this case stmt_arena points to THD. In case of a prepared statement or a stored procedure statement, thd->mem_root - conventionally points to runtime memory, and thd->current_arena + conventionally points to runtime memory, and thd->stmt_arena points to the memory of the PS/SP, where the parsed tree of the statement resides. Whenever you need to perform a permanent transformation of a parsed tree, you should allocate new memory in - current_arena, to allow correct re-execution of PS/SP. - Note: in the parser, current_arena == thd, even for PS/SP. + stmt_arena, to allow correct re-execution of PS/SP. + Note: in the parser, stmt_arena == thd, even for PS/SP. */ - Query_arena *current_arena; + Query_arena *stmt_arena; /* next_insert_id is set on SET INSERT_ID= #. This is used as the next generated auto_increment value in handler.cc @@ -1467,7 +1465,7 @@ public: } inline bool fill_derived_tables() { - return !current_arena->is_stmt_prepare() && !lex->only_view_structure(); + return !stmt_arena->is_stmt_prepare() && !lex->only_view_structure(); } inline gptr trans_alloc(unsigned int size) { @@ -1506,17 +1504,16 @@ public: inline CHARSET_INFO *charset() { return variables.character_set_client; } void update_charset(); - inline Query_arena *change_arena_if_needed(Query_arena *backup) + inline Query_arena *activate_stmt_arena_if_needed(Query_arena *backup) { /* - use new arena if we are in a prepared statements and we have not - already changed to use this arena. + Use the persistent arena if we are in a prepared statement or a stored + procedure statement and we have not already changed to use this arena. */ - if (!current_arena->is_conventional() && - mem_root != current_arena->mem_root) + if (!stmt_arena->is_conventional() && mem_root != stmt_arena->mem_root) { - set_n_backup_item_arena(current_arena, backup); - return current_arena; + set_n_backup_active_arena(stmt_arena, backup); + return stmt_arena; } return 0; } @@ -1524,7 +1521,7 @@ public: void change_item_tree(Item **place, Item *new_value) { /* TODO: check for OOM condition here */ - if (!current_arena->is_conventional()) + if (!stmt_arena->is_conventional()) nocheck_register_item_tree_change(place, *place, mem_root); *place= new_value; } @@ -1556,11 +1553,13 @@ public: } void set_status_var_init(); bool is_context_analysis_only() - { return current_arena->is_stmt_prepare() || lex->view_prepare_mode; } + { return stmt_arena->is_stmt_prepare() || lex->view_prepare_mode; } void reset_n_backup_open_tables_state(Open_tables_state *backup); void restore_backup_open_tables_state(Open_tables_state *backup); void reset_sub_statement_state(Sub_statement_state *backup, uint new_state); void restore_sub_statement_state(Sub_statement_state *backup); + void set_n_backup_active_arena(Query_arena *set, Query_arena *backup); + void restore_active_arena(Query_arena *set, Query_arena *backup); }; diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 86823919378..455c592241a 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -1504,7 +1504,7 @@ bool st_select_lex::setup_ref_array(THD *thd, uint order_group_num) We have to create array in prepared statement memory if it is prepared statement */ - Query_arena *arena= thd->current_arena; + Query_arena *arena= thd->stmt_arena; return (ref_pointer_array= (Item **)arena->alloc(sizeof(Item*) * (item_list.elements + @@ -1826,7 +1826,7 @@ void st_select_lex_unit::set_limit(SELECT_LEX *sl) { ha_rows select_limit_val; - DBUG_ASSERT(! thd->current_arena->is_stmt_prepare()); + DBUG_ASSERT(! thd->stmt_arena->is_stmt_prepare()); select_limit_val= (ha_rows)(sl->select_limit ? sl->select_limit->val_uint() : HA_POS_ERROR); offset_limit_cnt= (ha_rows)(sl->offset_limit ? sl->offset_limit->val_uint() : @@ -2038,7 +2038,7 @@ void st_lex::cleanup_after_one_table_open() void st_select_lex::fix_prepare_information(THD *thd, Item **conds) { - if (!thd->current_arena->is_conventional() && first_execution) + if (!thd->stmt_arena->is_conventional() && first_execution) { first_execution= 0; if (*conds) diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index c8f636c1411..937fd736282 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -5275,7 +5275,7 @@ mysql_new_select(LEX *lex, bool move_down) it's a constant one. The flag is switched off in the end of mysql_stmt_prepare. */ - if (thd->current_arena->is_stmt_prepare()) + if (thd->stmt_arena->is_stmt_prepare()) select_lex->uncacheable|= UNCACHEABLE_PREPARE; if (move_down) { @@ -6081,7 +6081,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, ptr->db= empty_c_string; ptr->db_length= 0; } - if (thd->current_arena->is_stmt_prepare_or_first_sp_execute()) + if (thd->stmt_arena->is_stmt_prepare_or_first_sp_execute()) ptr->db= thd->strdup(ptr->db); ptr->alias= alias_str; @@ -7301,7 +7301,7 @@ bool create_table_precheck(THD *thd, TABLE_LIST *tables, against the opened tables to ensure we don't use a table that is part of the view (which can only be done after the table has been opened). */ - if (thd->current_arena->is_stmt_prepare_or_first_sp_execute()) + if (thd->stmt_arena->is_stmt_prepare_or_first_sp_execute()) { /* For temporary tables we don't have to check if the created table exists diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 112c4a3dd47..bf0bea804c4 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -1767,12 +1767,12 @@ bool mysql_stmt_prepare(THD *thd, char *packet, uint packet_length, both of backup_statement() and backup_item_area() here. */ thd->set_n_backup_statement(stmt, &stmt_backup); - thd->set_n_backup_item_arena(stmt, &stmt_backup); + thd->set_n_backup_active_arena(stmt, &stmt_backup); if (alloc_query(thd, packet, packet_length)) { thd->restore_backup_statement(stmt, &stmt_backup); - thd->restore_backup_item_arena(stmt, &stmt_backup); + thd->restore_active_arena(stmt, &stmt_backup); /* Statement map deletes statement on erase */ thd->stmt_map.erase(stmt); DBUG_RETURN(TRUE); @@ -1780,7 +1780,7 @@ bool mysql_stmt_prepare(THD *thd, char *packet, uint packet_length, mysql_log.write(thd, thd->command, "[%lu] %s", stmt->id, packet); - thd->current_arena= stmt; + thd->stmt_arena= stmt; mysql_init_query(thd, (uchar *) thd->query, thd->query_length); /* Reset warnings from previous command */ mysql_reset_errors(thd, 0); @@ -1800,7 +1800,7 @@ bool mysql_stmt_prepare(THD *thd, char *packet, uint packet_length, transformation can be reused on execute, we set again thd->mem_root from stmt->mem_root (see setup_wild for one place where we do that). */ - thd->restore_backup_item_arena(stmt, &stmt_backup); + thd->restore_active_arena(stmt, &stmt_backup); if (!error) error= check_prepared_statement(stmt, test(name)); @@ -1817,7 +1817,7 @@ bool mysql_stmt_prepare(THD *thd, char *packet, uint packet_length, close_thread_tables(thd); cleanup_stmt_and_thd_after_use(stmt, thd); thd->restore_backup_statement(stmt, &stmt_backup); - thd->current_arena= thd; + thd->stmt_arena= thd; if (error) { @@ -2063,7 +2063,7 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length) goto set_params_data_err; #endif thd->set_n_backup_statement(stmt, &stmt_backup); - thd->current_arena= stmt; + thd->stmt_arena= stmt; reinit_stmt_before_use(thd, stmt->lex); /* From now cursors assume that thd->mem_root is clean */ if (expanded_query.length() && @@ -2110,7 +2110,7 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length) thd->set_statement(&stmt_backup); thd->lock_id= &thd->main_lock_id; - thd->current_arena= thd; + thd->stmt_arena= thd; DBUG_VOID_RETURN; set_params_data_err: @@ -2205,7 +2205,7 @@ static void execute_stmt(THD *thd, Prepared_statement *stmt, transformations of the query tree (i.e. negations elimination). This should be done permanently on the parse tree of this statement. */ - thd->current_arena= stmt; + thd->stmt_arena= stmt; if (!(specialflag & SPECIAL_NO_PRIOR)) my_pthread_setprio(pthread_self(),QUERY_PRIOR); @@ -2224,7 +2224,7 @@ static void execute_stmt(THD *thd, Prepared_statement *stmt, close_thread_tables(thd); // to close derived tables cleanup_stmt_and_thd_after_use(stmt, thd); reset_stmt_params(stmt); - thd->current_arena= thd; + thd->stmt_arena= thd; if (stmt->state == Query_arena::PREPARED) stmt->state= Query_arena::EXECUTED; @@ -2263,7 +2263,7 @@ void mysql_stmt_fetch(THD *thd, char *packet, uint packet_length) DBUG_VOID_RETURN; } - thd->current_arena= stmt; + thd->stmt_arena= stmt; thd->set_n_backup_statement(stmt, &stmt_backup); if (!(specialflag & SPECIAL_NO_PRIOR)) @@ -2291,7 +2291,7 @@ void mysql_stmt_fetch(THD *thd, char *packet, uint packet_length) } thd->restore_backup_statement(stmt, &stmt_backup); - thd->current_arena= thd; + thd->stmt_arena= thd; DBUG_VOID_RETURN; } diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 5ba2722482b..0de06ea395a 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -581,11 +581,11 @@ JOIN::optimize() MEMROOT for prepared statements and stored procedures. */ - Query_arena *arena= thd->current_arena, backup; + Query_arena *arena= thd->stmt_arena, backup; if (arena->is_conventional()) arena= 0; // For easier test else - thd->set_n_backup_item_arena(arena, &backup); + thd->set_n_backup_active_arena(arena, &backup); sel->first_cond_optimization= 0; @@ -595,7 +595,7 @@ JOIN::optimize() sel->prep_where= conds ? conds->copy_andor_structure(thd) : 0; if (arena) - thd->restore_backup_item_arena(arena, &backup); + thd->restore_active_arena(arena, &backup); } conds= optimize_cond(this, conds, join_list, &cond_value); @@ -1744,7 +1744,7 @@ Cursor::init_from_thd(THD *thd) things that are already allocated in thd->mem_root for Cursor::fetch() */ main_mem_root= *thd->mem_root; - state= thd->current_arena->state; + state= thd->stmt_arena->state; /* Allocate new memory root for thd */ init_sql_alloc(thd->mem_root, thd->variables.query_alloc_block_size, @@ -1871,7 +1871,7 @@ Cursor::fetch(ulong num_rows) thd->query_id= query_id; thd->change_list= change_list; /* save references to memory, allocated during fetch */ - thd->set_n_backup_item_arena(this, &backup_arena); + thd->set_n_backup_active_arena(this, &backup_arena); for (info= ht_info; info->read_view ; info++) (info->ht->set_cursor_read_view)(info->read_view); @@ -1890,7 +1890,7 @@ Cursor::fetch(ulong num_rows) ha_release_temporary_latches(thd); #endif /* Grab free_list here to correctly free it in close */ - thd->restore_backup_item_arena(this, &backup_arena); + thd->restore_active_arena(this, &backup_arena); for (info= ht_info; info->read_view; info++) (info->ht->set_cursor_read_view)(0); diff --git a/sql/sql_show.cc b/sql/sql_show.cc index f75d035e46c..bc3c8fbdc5d 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -3610,7 +3610,7 @@ int mysql_schema_table(THD *thd, LEX *lex, TABLE_LIST *table_list) } List_iterator_fast it(sel->item_list); if (!(transl= - (Field_translator*)(thd->current_arena-> + (Field_translator*)(thd->stmt_arena-> alloc(sel->item_list.elements * sizeof(Field_translator))))) { diff --git a/sql/sql_union.cc b/sql/sql_union.cc index 72c96e81682..556493f4fc8 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -287,7 +287,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, all collations together for UNION. */ List_iterator_fast tp(types); - Query_arena *arena= thd->current_arena; + Query_arena *arena= thd->stmt_arena; Item *type; ulonglong create_options; @@ -332,7 +332,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, { Field **field; Query_arena *tmp_arena,backup; - tmp_arena= thd->change_arena_if_needed(&backup); + tmp_arena= thd->activate_stmt_arena_if_needed(&backup); for (field= table->field; *field; field++) { @@ -340,12 +340,12 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, if (!item || item_list.push_back(item)) { if (tmp_arena) - thd->restore_backup_item_arena(tmp_arena, &backup); + thd->restore_active_arena(tmp_arena, &backup); DBUG_RETURN(TRUE); } } if (tmp_arena) - thd->restore_backup_item_arena(tmp_arena, &backup); + thd->restore_active_arena(tmp_arena, &backup); if (arena->is_stmt_prepare_or_first_sp_execute()) { /* prepare fake select to initialize it correctly */ diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 8269c16916a..4955361182a 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -700,11 +700,11 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table) For now we assume that tables will not be changed during PS life (it will be TRUE as far as we make new table cache). */ - Query_arena *arena= thd->current_arena, backup; + Query_arena *arena= thd->stmt_arena, backup; if (arena->is_conventional()) arena= 0; else - thd->set_n_backup_item_arena(arena, &backup); + thd->set_n_backup_active_arena(arena, &backup); /* init timestamp */ if (!table->timestamp.str) @@ -997,13 +997,13 @@ ok: ok2: if (arena) - thd->restore_backup_item_arena(arena, &backup); + thd->restore_active_arena(arena, &backup); thd->lex= old_lex; DBUG_RETURN(0); err: if (arena) - thd->restore_backup_item_arena(arena, &backup); + thd->restore_active_arena(arena, &backup); delete table->view; table->view= 0; // now it is not VIEW placeholder thd->lex= old_lex; diff --git a/sql/table.cc b/sql/table.cc index 66fccec9c24..165596bf46d 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -1780,7 +1780,7 @@ bool st_table_list::setup_ancestor(THD *thd) /* Create view fields translation table */ if (!(transl= - (Field_translator*)(thd->current_arena-> + (Field_translator*)(thd->stmt_arena-> alloc(select->item_list.elements * sizeof(Field_translator))))) { @@ -1856,8 +1856,8 @@ bool st_table_list::prep_where(THD *thd, Item **conds, if (!no_where_clause && !where_processed) { TABLE_LIST *tbl= this; - Query_arena *arena= thd->current_arena, backup; - arena= thd->change_arena_if_needed(&backup); // For easier test + Query_arena *arena= thd->stmt_arena, backup; + arena= thd->activate_stmt_arena_if_needed(&backup); // For easier test /* Go up to join tree and try to find left join */ for (; tbl; tbl= tbl->embedding) @@ -1877,7 +1877,7 @@ bool st_table_list::prep_where(THD *thd, Item **conds, if (tbl == 0) *conds= and_conds(*conds, where); if (arena) - thd->restore_backup_item_arena(arena, &backup); + thd->restore_active_arena(arena, &backup); where_processed= TRUE; } } From 5d99ed0e881ba31e9608ded3ec6a8263d8696c2d Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 2 Sep 2005 20:07:08 +0200 Subject: [PATCH 14/26] Compile error fix. client/mysqltest.c: Have variable declarations before "DBUG_ENTER", the opposite order will fail to compile on some platforms. --- client/mysqltest.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/mysqltest.c b/client/mysqltest.c index 33702e9d1d2..92735c97fea 100644 --- a/client/mysqltest.c +++ b/client/mysqltest.c @@ -812,9 +812,9 @@ int var_set(const char *var_name, const char *var_name_end, int open_file(const char *name) { + char buff[FN_REFLEN]; DBUG_ENTER("open_file"); DBUG_PRINT("enter", ("name: %s", name)); - char buff[FN_REFLEN]; if (!test_if_hard_path(name)) { strxmov(buff, opt_basedir, name, NullS); @@ -843,9 +843,9 @@ int open_file(const char *name) int check_eol_junk(const char *eol) { + const char *p= eol; DBUG_ENTER("check_eol_junk"); DBUG_PRINT("enter", ("eol: %s", eol)); - const char *p= eol; /* Remove all spacing chars except new line */ while (*p && my_isspace(charset_info, *p) && (*p != '\n')) p++; From 387c2589225e20427b73f8d324d0cb8ef8b3e712 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 3 Sep 2005 01:38:37 +0300 Subject: [PATCH 15/26] row0sel.c: Fix bug #12947 : a consistent read could return inconsistent results; this bug was introduced in 5.0.5 innobase/row/row0sel.c: Fix bug #12947 : a consistent read could return inconsistent results; this bug was introduced in 5.0.5 --- innobase/row/row0sel.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/innobase/row/row0sel.c b/innobase/row/row0sel.c index a77010d939b..0f64126f1fc 100644 --- a/innobase/row/row0sel.c +++ b/innobase/row/row0sel.c @@ -3055,6 +3055,7 @@ row_search_for_mysql( cursor 'direction' should be 0. */ { dict_index_t* index = prebuilt->index; + ibool comp = index->table->comp; dtuple_t* search_tuple = prebuilt->search_tuple; btr_pcur_t* pcur = prebuilt->pcur; trx_t* trx = prebuilt->trx; @@ -3491,7 +3492,7 @@ rec_loop: /* PHASE 4: Look for matching records in a loop */ rec = btr_pcur_get_rec(pcur); - ut_ad(!!page_rec_is_comp(rec) == index->table->comp); + ut_ad(!!page_rec_is_comp(rec) == comp); #ifdef UNIV_SEARCH_DEBUG /* fputs("Using ", stderr); @@ -3544,7 +3545,7 @@ rec_loop: /* Do sanity checks in case our cursor has bumped into page corruption */ - if (page_rec_is_comp(rec)) { + if (comp) { next_offs = rec_get_next_offs(rec, TRUE); if (UNIV_UNLIKELY(next_offs < PAGE_NEW_SUPREMUM)) { goto wrong_offs; @@ -3711,7 +3712,7 @@ rec_loop: if (!set_also_gap_locks || srv_locks_unsafe_for_binlog || (unique_search && !UNIV_UNLIKELY(rec_get_deleted_flag( - rec, page_rec_is_comp(rec))))) { + rec, comp)))) { goto no_gap_lock; } else { @@ -3803,7 +3804,12 @@ no_gap_lock: } } - if (UNIV_UNLIKELY(rec_get_deleted_flag(rec, page_rec_is_comp(rec)))) { + /* NOTE that at this point rec can be an old version of a clustered + index record built for a consistent read. We cannot assume after this + point that rec is on a buffer pool page. Functions like + page_rec_is_comp() cannot be used then! */ + + if (UNIV_UNLIKELY(rec_get_deleted_flag(rec, comp))) { /* The record is delete-marked: we can skip it if this is not a consistent read which might see an earlier version @@ -3836,6 +3842,7 @@ requires_clust_rec: the clustered index ("clust_index"). However, after this "if" block, "rec" may be pointing to "clust_rec" of "clust_index". */ + ut_ad(rec_offs_validate(rec, index, offsets)); /* It was a non-clustered index and we must fetch also the @@ -3858,8 +3865,7 @@ requires_clust_rec: goto next_rec; } - if (UNIV_UNLIKELY(rec_get_deleted_flag(clust_rec, - page_rec_is_comp(clust_rec)))) { + if (UNIV_UNLIKELY(rec_get_deleted_flag(clust_rec, comp))) { /* The record is delete marked: we can skip it */ From c910050a769a20db0eee0d5a4f7069959d3e3470 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 3 Sep 2005 03:13:18 +0400 Subject: [PATCH 16/26] Implement WL#2661 "Prepared Statements: Dynamic SQL in Stored Procedures". The idea of the patch is to separate statement processing logic, such as parsing, validation of the parsed tree, execution and cleanup, from global query processing logic, such as logging, resetting priorities of a thread, resetting stored procedure cache, resetting thread count of errors and warnings. This makes PREPARE and EXECUTE behave similarly to the rest of SQL statements and allows their use in stored procedures. This patch contains a change in behaviour: until recently for each SQL prepared statement command, 2 queries were written to the general log, e.g. [Query] prepare stmt from @stmt_text; [Prepare] select * from t1 <-- contents of @stmt_text The chagne was necessary to prevent [Prepare] commands from being written to the general log when executing a stored procedure with Dynamic SQL. We should consider whether the old behavior is preferrable and probably restore it. This patch refixes Bug#7115, Bug#10975 (partially), Bug#10605 (various bugs in Dynamic SQL reported before it was disabled). mysql-test/r/not_embedded_server.result: Since we don't want to log Dynamic SQL in stored procedures, now the general log gets only one log entry per SQL statement. mysql-test/r/sp-error.result: - remove obsolete tests - a better error message for the case when a stored procedure that returns a result set is called from a function mysql-test/r/trigger.result: - a better error message for the case when a stored procedure that returns a result set is called from a trigger mysql-test/t/sp-error.test: - a better error message for the case when a stored procedure that returns a result set is called from a function. - move the comment to its place (end of file). mysql-test/t/trigger.test: - a better error message for the case when a stored procedure that returns a result set is called from a trigger sql/item_func.cc: - we need to pass sql_command explicitly to get_var_with_binlog, because when creating a query for SQL prepared statement thd->lex->sql_command points at SQLCOM_EXECUTE, which is not listed in the list of update queries. sql/log_event.h: - remove an extra copy of the previous sentence sql/mysql_priv.h: - fix declarations of sql_prepare.cc API sql/share/errmsg.txt: - a new error message, when one attempts to execute a prepared statement which is currently being executed (this can happen only in Dynamic SQL at the moment). sql/sp_head.cc: - extend sp_multi_results_command to return different flags for a command (and rename it) - add support for SQLCOM_PREPARE,SQLCOM_EXECUTE, SQLCOM_DEALLOCATE to sp_get_flags_for_command - replace multiple boolean sp_head members with uint m_flags - a fix for a crash when user variables are used in a stored procedure and binlog is on. A temporary fix for Bug#12637 "SP crashes the server if it has update query with user var & binlog is enabled", which actually stands for stored functions: now instead of a crash we break replication if a user variable is used in a stored function which is executed in prelocked mode. sql/sp_head.h: - replace multiple boolean flags of sp_head with uint m_flags; - add flag CONTAINS_DYNAMIC_SQL - use this flag to error if a stored procedure with Dynamic SQL is called from a function or trigger. sql/sql_class.cc: - Statement_map::insert should not delete a statement if it exists, now it's done externally to be able to handle the case when the statement being deleted is in use. - remove extra code (free_list is already reset in free_items) sql/sql_lex.cc: - add lex->stmt_prepare_mode; we can't rely on thd->command any more, because we don't reset it any more (Dynamic SQL requirement is that PS are as little intrusive as possible). sql/sql_lex.h: - declare bool LEX::stmt_prepare_mode sql/sql_parse.cc: - move prepared statement code to sql_prepare.cc - change declarations (refactored code) - better error message when one attempts to use Dynamic SQL or a stored procedure that returns a result set in a function or trigger. sql/sql_prepare.cc: - major refactoring to ensure PREPARE/EXECUTE commands do not reset global THD state and allow their use in stored procedures. - add Prepared_statement::flags and use it to ensure no recursive execution of a prepared statement is possible - better comments sql/sql_yacc.yy: - enable PREPARE/EXECUTE/DEALLOCATE in stored procedures - produce an error message on attempt to use PREPARE/EXECUTE/DEALLOCATE in a stored function or trigger mysql-test/r/sp-dynamic.result: - sp-dynamic.test results mysql-test/t/sp-dynamic.test: - a new test for PREPARE/EXECUTE/DEALLOCATE in stored procedures. --- mysql-test/r/not_embedded_server.result | 2 +- mysql-test/r/sp-dynamic.result | 364 +++++++ mysql-test/r/sp-error.result | 16 +- mysql-test/r/trigger.result | 2 +- mysql-test/t/sp-dynamic.test | 335 ++++++ mysql-test/t/sp-error.test | 40 +- mysql-test/t/trigger.test | 2 +- sql/item_func.cc | 16 +- sql/log_event.h | 3 - sql/mysql_priv.h | 17 +- sql/share/errmsg.txt | 2 + sql/sp_head.cc | 75 +- sql/sp_head.h | 33 +- sql/sql_class.cc | 17 +- sql/sql_lex.cc | 7 +- sql/sql_lex.h | 5 + sql/sql_parse.cc | 135 +-- sql/sql_prepare.cc | 1253 +++++++++++++++-------- sql/sql_yacc.yy | 55 +- 19 files changed, 1679 insertions(+), 700 deletions(-) create mode 100644 mysql-test/r/sp-dynamic.result create mode 100644 mysql-test/t/sp-dynamic.test diff --git a/mysql-test/r/not_embedded_server.result b/mysql-test/r/not_embedded_server.result index 082ebe72ba4..e471b5a3afa 100644 --- a/mysql-test/r/not_embedded_server.result +++ b/mysql-test/r/not_embedded_server.result @@ -1,5 +1,5 @@ prepare stmt1 from ' show full processlist '; execute stmt1; Id User Host db Command Time State Info -number root localhost test Execute time NULL show full processlist +number root localhost test Query time NULL show full processlist deallocate prepare stmt1; diff --git a/mysql-test/r/sp-dynamic.result b/mysql-test/r/sp-dynamic.result new file mode 100644 index 00000000000..8fe469431cc --- /dev/null +++ b/mysql-test/r/sp-dynamic.result @@ -0,0 +1,364 @@ +create procedure p1() +begin +prepare stmt from "select 1"; +execute stmt; +execute stmt; +execute stmt; +deallocate prepare stmt; +end| +call p1()| +1 +1 +1 +1 +1 +1 +call p1()| +1 +1 +1 +1 +1 +1 +call p1()| +1 +1 +1 +1 +1 +1 +drop procedure p1| +create procedure p1() +begin +execute stmt; +end| +prepare stmt from "call p1()"| +execute stmt| +ERROR HY000: The prepared statement contains a stored routine call that refers to that same statement. It's not allowed to execute a prepared statement in such a recursive manner +execute stmt| +ERROR HY000: The prepared statement contains a stored routine call that refers to that same statement. It's not allowed to execute a prepared statement in such a recursive manner +execute stmt| +ERROR HY000: The prepared statement contains a stored routine call that refers to that same statement. It's not allowed to execute a prepared statement in such a recursive manner +call p1()| +ERROR HY000: Recursive stored routines are not allowed. +call p1()| +ERROR HY000: Recursive stored routines are not allowed. +call p1()| +ERROR HY000: Recursive stored routines are not allowed. +drop procedure p1| +create procedure p1() +begin +prepare stmt from "create procedure p2() begin select 1; end"; +execute stmt; +deallocate prepare stmt; +end| +call p1()| +ERROR HY000: This command is not supported in the prepared statement protocol yet +call p1()| +ERROR HY000: This command is not supported in the prepared statement protocol yet +drop procedure p1| +create procedure p1() +begin +prepare stmt from "drop procedure p2"; +execute stmt; +deallocate prepare stmt; +end| +call p1()| +ERROR HY000: This command is not supported in the prepared statement protocol yet +call p1()| +ERROR HY000: This command is not supported in the prepared statement protocol yet +drop procedure p1| +create procedure p1() +begin +prepare stmt_drop from "drop table if exists t1"; +execute stmt_drop; +prepare stmt from "create table t1 (a int)"; +execute stmt; +insert into t1 (a) values (1); +select * from t1; +deallocate prepare stmt; +deallocate prepare stmt_drop; +end| +call p1()| +a +1 +Warnings: +Note 1051 Unknown table 't1' +call p1()| +a +1 +drop procedure p1| +create procedure p1() +begin +set @tab_name=concat("tab_", replace(curdate(), '-', '_')); +set @drop_sql=concat("drop table if exists ", @tab_name); +set @create_sql=concat("create table ", @tab_name, " (a int)"); +set @insert_sql=concat("insert into ", @tab_name, " values (1), (2), (3)"); +set @select_sql=concat("select * from ", @tab_name); +select @tab_name; +select @drop_sql; +select @create_sql; +select @insert_sql; +select @select_sql; +prepare stmt_drop from @drop_sql; +execute stmt_drop; +prepare stmt from @create_sql; +execute stmt; +prepare stmt from @insert_sql; +execute stmt; +prepare stmt from @select_sql; +execute stmt; +execute stmt_drop; +deallocate prepare stmt; +deallocate prepare stmt_drop; +end| +call p1()| +call p1()| +drop procedure p1| +create procedure p1() +begin +prepare stmt_drop from "drop table if exists t1"; +execute stmt_drop; +prepare stmt from "create table t1 (a int)"; +execute stmt; +deallocate prepare stmt; +deallocate prepare stmt_drop; +end| +drop function if exists f1| +create function f1(a int) returns int +begin +call p1(); +return 1; +end| +select f1(0)| +ERROR 0A000: Dynamic SQL is not allowed in stored function or trigger +select f1(f1(0))| +ERROR 0A000: Dynamic SQL is not allowed in stored function or trigger +select f1(f1(f1(0)))| +ERROR 0A000: Dynamic SQL is not allowed in stored function or trigger +drop function f1| +drop procedure p1| +create procedure p1() +begin +drop table if exists t1; +create table t1 (id integer not null primary key, +name varchar(20) not null); +insert into t1 (id, name) values (1, 'aaa'), (2, 'bbb'), (3, 'ccc'); +prepare stmt from "select name from t1"; +execute stmt; +select name from t1; +execute stmt; +prepare stmt from +"select name from t1 where name=(select name from t1 where id=2)"; +execute stmt; +select name from t1 where name=(select name from t1 where id=2); +execute stmt; +end| +call p1()| +name +aaa +bbb +ccc +name +aaa +bbb +ccc +name +aaa +bbb +ccc +name +bbb +name +bbb +name +bbb +call p1()| +name +aaa +bbb +ccc +name +aaa +bbb +ccc +name +aaa +bbb +ccc +name +bbb +name +bbb +name +bbb +drop procedure p1| +prepare stmt from "select * from t1"| +create procedure p1() +begin +execute stmt; +deallocate prepare stmt; +end| +call p1()| +id name +1 aaa +2 bbb +3 ccc +call p1()| +ERROR HY000: Unknown prepared statement handler (stmt) given to EXECUTE +drop procedure p1| +create procedure p1() +begin +declare a char(10); +set a="sp-variable"; +set @a="mysql-variable"; +prepare stmt from "select 'dynamic sql:', @a, a"; +execute stmt; +end| +call p1()| +ERROR 42S22: Unknown column 'a' in 'field list' +call p1()| +ERROR 42S22: Unknown column 'a' in 'field list' +drop procedure p1| +create procedure p1() +begin +prepare stmt from 'select ? as a'; +execute stmt using @a; +end| +set @a=1| +call p1()| +a +1 +call p1()| +a +1 +drop procedure p1| +drop table if exists t1| +create table t1 (id integer primary key auto_increment, +stmt_text char(35), status varchar(20))| +insert into t1 (stmt_text) values +("select 1"), ("flush tables"), ("handler t1 open as ha"), +("analyze table t1"), ("check table t1"), ("checksum table t1"), +("check table t1"), ("optimize table t1"), ("repair table t1"), +("describe extended select * from t1"), +("help help"), ("show databases"), ("show tables"), +("show table status"), ("show open tables"), ("show storage engines"), +("insert into t1 (id) values (1)"), ("update t1 set status=''"), +("delete from t1"), ("truncate t1"), ("call p1()"), ("foo bar")| +create procedure p1() +begin +declare v_stmt_text varchar(255); +declare v_id integer; +declare done int default 0; +declare c cursor for select id, stmt_text from t1; +declare continue handler for 1295 -- ER_UNSUPPORTED_PS +set @status='not supported'; +declare continue handler for 1064 -- ER_SYNTAX_ERROR +set @status='syntax error'; +declare continue handler for sqlstate '02000' set done = 1; +prepare update_stmt from "update t1 set status=? where id=?"; +open c; +repeat +if not done then +fetch c into v_id, v_stmt_text; +set @id=v_id, @stmt_text=v_stmt_text; +set @status="supported"; +prepare stmt from @stmt_text; +execute update_stmt using @status, @id; +end if; +until done end repeat; +deallocate prepare update_stmt; +end| +call p1()| +select * from t1| +id stmt_text status +1 select 1 supported +2 flush tables not supported +3 handler t1 open as ha not supported +4 analyze table t1 not supported +5 check table t1 not supported +6 checksum table t1 not supported +7 check table t1 not supported +8 optimize table t1 not supported +9 repair table t1 not supported +10 describe extended select * from t1 supported +11 help help not supported +12 show databases supported +13 show tables supported +14 show table status supported +15 show open tables supported +16 show storage engines supported +17 insert into t1 (id) values (1) supported +18 update t1 set status='' supported +19 delete from t1 supported +20 truncate t1 supported +21 call p1() supported +22 foo bar syntax error +drop procedure p1| +drop table t1| +prepare stmt from 'select 1'| +create procedure p1() execute stmt| +call p1()| +1 +1 +call p1()| +1 +1 +drop procedure p1| +create function f1() returns int +begin +deallocate prepare stmt; +return 1; +end| +ERROR 0A000: Dynamic SQL is not allowed in stored function or trigger +create procedure p1() +begin +prepare stmt from 'select 1 A'; +execute stmt; +end| +prepare stmt from 'call p1()'| +execute stmt| +ERROR HY000: The prepared statement contains a stored routine call that refers to that same statement. It's not allowed to execute a prepared statement in such a recursive manner +execute stmt| +ERROR HY000: The prepared statement contains a stored routine call that refers to that same statement. It's not allowed to execute a prepared statement in such a recursive manner +drop procedure p1| +drop table if exists t1, t2| +create procedure p1 (a int) language sql deterministic +begin +declare rsql varchar(100); +drop table if exists t1, t2; +set @rsql= "create table t1 (a int)"; +select @rsql; +prepare pst from @rsql; +execute pst; +set @rsql= null; +set @rsql= "create table t2 (a int)"; +select @rsql; +prepare pst from @rsql; +execute pst; +drop table if exists t1, t2; +end| +set @a:=0| +call p1(@a)| +@rsql +create table t1 (a int) +@rsql +create table t2 (a int) +Warnings: +Note 1051 Unknown table 't1' +Note 1051 Unknown table 't2' +select @a| +@a +0 +call p1(@a)| +@rsql +create table t1 (a int) +@rsql +create table t2 (a int) +Warnings: +Note 1051 Unknown table 't1' +Note 1051 Unknown table 't2' +select @a| +@a +0 +drop procedure if exists p1| diff --git a/mysql-test/r/sp-error.result b/mysql-test/r/sp-error.result index 23644f57353..61e931e146c 100644 --- a/mysql-test/r/sp-error.result +++ b/mysql-test/r/sp-error.result @@ -618,7 +618,7 @@ select * from t1| call bug8408_p()| val x select bug8408_f()| -ERROR 0A000: PROCEDURE test.bug8408_p can't return a result set in the given context +ERROR 0A000: Not allowed to return a result set from a function drop procedure bug8408_p| drop function bug8408_f| create function bug8408() returns int @@ -665,20 +665,6 @@ select default(t30.s1) from t30; end| drop procedure bug10969| drop table t1| -prepare stmt from "select 1"; -create procedure p() deallocate prepare stmt; -ERROR 0A000: DEALLOCATE is not allowed in stored procedures -create function f() returns int begin deallocate prepare stmt; -ERROR 0A000: DEALLOCATE is not allowed in stored procedures -create procedure p() prepare stmt from "select 1"; -ERROR 0A000: PREPARE is not allowed in stored procedures -create function f() returns int begin prepare stmt from "select 1"; -ERROR 0A000: PREPARE is not allowed in stored procedures -create procedure p() execute stmt; -ERROR 0A000: EXECUTE is not allowed in stored procedures -create function f() returns int begin execute stmt; -ERROR 0A000: EXECUTE is not allowed in stored procedures -deallocate prepare stmt; create table t1(f1 int); create table t2(f1 int); CREATE PROCEDURE SP001() diff --git a/mysql-test/r/trigger.result b/mysql-test/r/trigger.result index 312a7a90fc9..b305691fa18 100644 --- a/mysql-test/r/trigger.result +++ b/mysql-test/r/trigger.result @@ -689,7 +689,7 @@ call bug11587(); set new.c2= '2004-04-02'; end| insert into t1 (c1) values (4),(5),(6); -ERROR 0A000: PROCEDURE test.bug11587 can't return a result set in the given context +ERROR 0A000: Not allowed to return a result set from a trigger select * from t1; c1 c2 1 NULL diff --git a/mysql-test/t/sp-dynamic.test b/mysql-test/t/sp-dynamic.test new file mode 100644 index 00000000000..e9816ee3ef0 --- /dev/null +++ b/mysql-test/t/sp-dynamic.test @@ -0,0 +1,335 @@ +delimiter |; +###################################################################### +# Test Dynamic SQL in stored procedures. ############################# +###################################################################### +# +# A. Basics +# +create procedure p1() +begin + prepare stmt from "select 1"; + execute stmt; + execute stmt; + execute stmt; + deallocate prepare stmt; +end| +call p1()| +call p1()| +call p1()| +drop procedure p1| +# +# B. Recursion. Recusion is disabled in SP, and recursive use of PS is not +# possible as well. +# +create procedure p1() +begin + execute stmt; +end| +prepare stmt from "call p1()"| +--error ER_PS_NO_RECURSION +execute stmt| +--error ER_PS_NO_RECURSION +execute stmt| +--error ER_PS_NO_RECURSION +execute stmt| +--error ER_SP_NO_RECURSION +call p1()| +--error ER_SP_NO_RECURSION +call p1()| +--error ER_SP_NO_RECURSION +call p1()| +drop procedure p1| +# +# C. Create/drop a stored procedure in Dynamic SQL. +# One cannot create stored procedure from a stored procedure because of +# the way MySQL SP cache works: it's important that this limitation is not +# possible to circumvent by means of Dynamic SQL. +# +create procedure p1() +begin + prepare stmt from "create procedure p2() begin select 1; end"; + execute stmt; + deallocate prepare stmt; +end| +--error ER_UNSUPPORTED_PS +call p1()| +--error ER_UNSUPPORTED_PS +call p1()| +drop procedure p1| +create procedure p1() +begin + prepare stmt from "drop procedure p2"; + execute stmt; + deallocate prepare stmt; +end| +--error ER_UNSUPPORTED_PS +call p1()| +--error ER_UNSUPPORTED_PS +call p1()| +drop procedure p1| +# +# D. Create/Drop a table (a DDL that issues a commit) in Dynamic SQL. +# (should work ok). +# +create procedure p1() +begin + prepare stmt_drop from "drop table if exists t1"; + execute stmt_drop; + prepare stmt from "create table t1 (a int)"; + execute stmt; + insert into t1 (a) values (1); + select * from t1; + deallocate prepare stmt; + deallocate prepare stmt_drop; +end| +call p1()| +call p1()| +drop procedure p1| +# +# A more real example (a case similar to submitted by 24/7). +# +create procedure p1() +begin + set @tab_name=concat("tab_", replace(curdate(), '-', '_')); + set @drop_sql=concat("drop table if exists ", @tab_name); + set @create_sql=concat("create table ", @tab_name, " (a int)"); + set @insert_sql=concat("insert into ", @tab_name, " values (1), (2), (3)"); + set @select_sql=concat("select * from ", @tab_name); + select @tab_name; + select @drop_sql; + select @create_sql; + select @insert_sql; + select @select_sql; + prepare stmt_drop from @drop_sql; + execute stmt_drop; + prepare stmt from @create_sql; + execute stmt; + prepare stmt from @insert_sql; + execute stmt; + prepare stmt from @select_sql; + execute stmt; + execute stmt_drop; + deallocate prepare stmt; + deallocate prepare stmt_drop; +end| +--disable_result_log +call p1()| +call p1()| +--enable_result_log +drop procedure p1| +# +# E. Calling a stored procedure with Dynamic SQL +# from a stored function (currently disabled). +# +create procedure p1() +begin + prepare stmt_drop from "drop table if exists t1"; + execute stmt_drop; + prepare stmt from "create table t1 (a int)"; + execute stmt; + deallocate prepare stmt; + deallocate prepare stmt_drop; +end| +--disable_warnings +drop function if exists f1| +--enable_warnings +create function f1(a int) returns int +begin + call p1(); + return 1; +end| + +# Every stored procedure that contains Dynamic SQL is marked as +# such. Stored procedures that contain Dynamic SQL are not +# allowed in a stored function or trigger, and here we get the +# corresponding error message. + +--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG +select f1(0)| +--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG +select f1(f1(0))| +--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG +select f1(f1(f1(0)))| +drop function f1| +drop procedure p1| +# +# F. Rollback and cleanup lists management in Dynamic SQL. +# +create procedure p1() +begin + drop table if exists t1; + create table t1 (id integer not null primary key, + name varchar(20) not null); + insert into t1 (id, name) values (1, 'aaa'), (2, 'bbb'), (3, 'ccc'); + prepare stmt from "select name from t1"; + execute stmt; + select name from t1; + execute stmt; + prepare stmt from + "select name from t1 where name=(select name from t1 where id=2)"; + execute stmt; + select name from t1 where name=(select name from t1 where id=2); + execute stmt; +end| +call p1()| +call p1()| +drop procedure p1| +# +# H. Executing a statement prepared externally in SP. +# +prepare stmt from "select * from t1"| +create procedure p1() +begin + execute stmt; + deallocate prepare stmt; +end| +call p1()| +--error ER_UNKNOWN_STMT_HANDLER +call p1()| +drop procedure p1| +# +# I. Use of an SP variable in Dynamic SQL is not possible and +# this limitation is necessary for correct binary logging: prepared +# statements do not substitute SP variables with their values for binlog, so +# SP variables must be not accessible in Dynamic SQL. +# +create procedure p1() +begin + declare a char(10); + set a="sp-variable"; + set @a="mysql-variable"; + prepare stmt from "select 'dynamic sql:', @a, a"; + execute stmt; +end| +--error ER_BAD_FIELD_ERROR +call p1()| +--error ER_BAD_FIELD_ERROR +call p1()| +drop procedure p1| +# +# J. Use of placeholders in Dynamic SQL. +# +create procedure p1() +begin + prepare stmt from 'select ? as a'; + execute stmt using @a; +end| +set @a=1| +call p1()| +call p1()| +drop procedure p1| +# +# K. Use of continue handlers with Dynamic SQL. +# +drop table if exists t1| +create table t1 (id integer primary key auto_increment, + stmt_text char(35), status varchar(20))| +insert into t1 (stmt_text) values + ("select 1"), ("flush tables"), ("handler t1 open as ha"), + ("analyze table t1"), ("check table t1"), ("checksum table t1"), + ("check table t1"), ("optimize table t1"), ("repair table t1"), + ("describe extended select * from t1"), + ("help help"), ("show databases"), ("show tables"), + ("show table status"), ("show open tables"), ("show storage engines"), + ("insert into t1 (id) values (1)"), ("update t1 set status=''"), + ("delete from t1"), ("truncate t1"), ("call p1()"), ("foo bar")| +create procedure p1() +begin + declare v_stmt_text varchar(255); + declare v_id integer; + declare done int default 0; + declare c cursor for select id, stmt_text from t1; + declare continue handler for 1295 -- ER_UNSUPPORTED_PS + set @status='not supported'; + declare continue handler for 1064 -- ER_SYNTAX_ERROR + set @status='syntax error'; + declare continue handler for sqlstate '02000' set done = 1; + + prepare update_stmt from "update t1 set status=? where id=?"; + open c; + repeat + if not done then + fetch c into v_id, v_stmt_text; + set @id=v_id, @stmt_text=v_stmt_text; + set @status="supported"; + prepare stmt from @stmt_text; + execute update_stmt using @status, @id; + end if; + until done end repeat; + deallocate prepare update_stmt; +end| +call p1()| +select * from t1| +drop procedure p1| +drop table t1| +# +# Bug#7115 "Prepared Statements: packet error if execution within stored +# procedure". +# +prepare stmt from 'select 1'| +create procedure p1() execute stmt| +call p1()| +call p1()| +drop procedure p1| +# +# Bug#10975 "Prepared statements: crash if function deallocates" +# Check that a prepared statement that is currently in use +# can't be deallocated. +# +# a) Prepared statements and stored procedure cache: +# +# TODO: add when the corresponding bug (Bug #12093 "SP not found on second +# PS execution if another thread drops other SP in between") is fixed. +# +# b) attempt to deallocate a prepared statement that is being executed +--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG +create function f1() returns int +begin + deallocate prepare stmt; + return 1; +end| + +# b)-2 a crash (#1) spotted by Sergey Petrunia during code review +create procedure p1() +begin + prepare stmt from 'select 1 A'; + execute stmt; +end| +prepare stmt from 'call p1()'| +--error ER_PS_NO_RECURSION +execute stmt| +--error ER_PS_NO_RECURSION +execute stmt| +drop procedure p1| + +# +# Bug#10605 "Stored procedure with multiple SQL prepared statements +# disconnects client" +# +--disable_warnings +drop table if exists t1, t2| +--enable_warnings +create procedure p1 (a int) language sql deterministic +begin + declare rsql varchar(100); + drop table if exists t1, t2; + set @rsql= "create table t1 (a int)"; + select @rsql; + prepare pst from @rsql; + execute pst; + set @rsql= null; + set @rsql= "create table t2 (a int)"; + select @rsql; + prepare pst from @rsql; + execute pst; + drop table if exists t1, t2; +end| +set @a:=0| +call p1(@a)| +select @a| +call p1(@a)| +select @a| +drop procedure if exists p1| + +# End of the test +delimiter ;| diff --git a/mysql-test/t/sp-error.test b/mysql-test/t/sp-error.test index 5921d59b284..e289748ba2f 100644 --- a/mysql-test/t/sp-error.test +++ b/mysql-test/t/sp-error.test @@ -875,7 +875,7 @@ create procedure bug8408_p() select * from t1| call bug8408_p()| ---error ER_SP_BADSELECT +--error ER_SP_NO_RETSET select bug8408_f()| drop procedure bug8408_p| @@ -956,39 +956,10 @@ end| drop procedure bug10969| -# -# BUG#NNNN: New bug synopsis -# -#--disable_warnings -#drop procedure if exists bugNNNN| -#--enable_warnings -#create procedure bugNNNN... - - drop table t1| delimiter ;| -# -# Bug#10975, #10605, #7115: Dynamic SQL by means of -# PREPARE/EXECUTE/DEALLOCATE is not supported yet. -# Check that an error message is returned. -# -prepare stmt from "select 1"; ---error ER_SP_BADSTATEMENT -create procedure p() deallocate prepare stmt; ---error ER_SP_BADSTATEMENT -create function f() returns int begin deallocate prepare stmt; ---error ER_SP_BADSTATEMENT -create procedure p() prepare stmt from "select 1"; ---error ER_SP_BADSTATEMENT -create function f() returns int begin prepare stmt from "select 1"; ---error ER_SP_BADSTATEMENT -create procedure p() execute stmt; ---error ER_SP_BADSTATEMENT -create function f() returns int begin execute stmt; -deallocate prepare stmt; - # BUG#9814: Closing a cursor that is not open create table t1(f1 int); create table t2(f1 int); @@ -1114,3 +1085,12 @@ drop function bug11834_1; execute stmt; deallocate prepare stmt; drop function bug11834_2; +# +# BUG#NNNN: New bug synopsis +# +#--disable_warnings +#drop procedure if exists bugNNNN| +#--enable_warnings +#create procedure bugNNNN... + + diff --git a/mysql-test/t/trigger.test b/mysql-test/t/trigger.test index 9920f203c94..a770782e5db 100644 --- a/mysql-test/t/trigger.test +++ b/mysql-test/t/trigger.test @@ -723,7 +723,7 @@ begin end| delimiter ;| ---error 1312 +--error ER_SP_NO_RETSET insert into t1 (c1) values (4),(5),(6); select * from t1; diff --git a/sql/item_func.cc b/sql/item_func.cc index 00ce70f6508..22d9fbbad34 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -3834,21 +3834,21 @@ longlong Item_func_get_user_var::val_int() stores this variable and its value in thd->user_var_events, so that it can be written to the binlog (will be written just before the query is written, see log.cc). - + RETURN - 0 OK + 0 OK 1 Failed to put appropriate record into binary log - + */ -int get_var_with_binlog(THD *thd, LEX_STRING &name, - user_var_entry **out_entry) +int get_var_with_binlog(THD *thd, enum_sql_command sql_command, + LEX_STRING &name, user_var_entry **out_entry) { BINLOG_USER_VAR_EVENT *user_var_event; user_var_entry *var_entry; var_entry= get_variable(&thd->user_vars, name, 0); - - if (!(opt_bin_log && is_update_query(thd->lex->sql_command))) + + if (!(opt_bin_log && is_update_query(sql_command))) { *out_entry= var_entry; return 0; @@ -3941,7 +3941,7 @@ void Item_func_get_user_var::fix_length_and_dec() decimals=NOT_FIXED_DEC; max_length=MAX_BLOB_WIDTH; - error= get_var_with_binlog(thd, name, &var_entry); + error= get_var_with_binlog(thd, thd->lex->sql_command, name, &var_entry); if (var_entry) { diff --git a/sql/log_event.h b/sql/log_event.h index 1d8941e65ac..29580589a34 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -1177,9 +1177,6 @@ class Xid_log_event: public Log_event Every time a query uses the value of a user variable, a User_var_log_event is written before the Query_log_event, to set the user variable. - Every time a query uses the value of a user variable, a User_var_log_event is - written before the Query_log_event, to set the user variable. - ****************************************************************************/ class User_var_log_event: public Log_event diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 1ab753e0938..154909a275d 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -589,7 +589,7 @@ bool mysql_change_db(THD *thd,const char *name,bool no_access_check); void mysql_parse(THD *thd,char *inBuf,uint length); bool mysql_test_parse_for_slave(THD *thd,char *inBuf,uint length); bool is_update_query(enum enum_sql_command command); -bool alloc_query(THD *thd, char *packet, ulong packet_length); +bool alloc_query(THD *thd, const char *packet, uint packet_length); void mysql_init_select(LEX *lex); void mysql_reset_thd_for_next_command(THD *thd); void mysql_init_query(THD *thd, uchar *buf, uint length); @@ -848,16 +848,17 @@ int fill_schema_column_privileges(THD *thd, TABLE_LIST *tables, COND *cond); bool get_schema_tables_result(JOIN *join); /* sql_prepare.cc */ -bool mysql_stmt_prepare(THD *thd, char *packet, uint packet_length, - LEX_STRING *name); + +void mysql_stmt_prepare(THD *thd, const char *packet, uint packet_length); void mysql_stmt_execute(THD *thd, char *packet, uint packet_length); -void mysql_sql_stmt_execute(THD *thd, LEX_STRING *stmt_name); -void mysql_stmt_fetch(THD *thd, char *packet, uint packet_length); void mysql_stmt_close(THD *thd, char *packet); +void mysql_sql_stmt_prepare(THD *thd); +void mysql_sql_stmt_execute(THD *thd); +void mysql_sql_stmt_close(THD *thd); +void mysql_stmt_fetch(THD *thd, char *packet, uint packet_length); void mysql_stmt_reset(THD *thd, char *packet); void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length); void reinit_stmt_before_use(THD *thd, LEX *lex); -void init_stmt_after_parse(THD*, LEX*); /* sql_handler.cc */ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen); @@ -1361,8 +1362,8 @@ extern int sql_cache_hit(THD *thd, char *inBuf, uint length); /* item_func.cc */ Item *get_system_var(THD *thd, enum_var_type var_type, LEX_STRING name, LEX_STRING component); -int get_var_with_binlog(THD *thd, LEX_STRING &name, - user_var_entry **out_entry); +int get_var_with_binlog(THD *thd, enum_sql_command sql_command, + LEX_STRING &name, user_var_entry **out_entry); /* log.cc */ bool flush_error_log(void); diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index b49b7248021..89ffac33055 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -5399,3 +5399,5 @@ ER_DATETIME_FUNCTION_OVERFLOW 22008 eng "Datetime function: %-.32s field overflow" ER_CANT_UPDATE_USED_TABLE_IN_SF_OR_TRG eng "Can't update table '%-.64s' in stored function/trigger because it is already used by statement which invoked this stored function/trigger." +ER_PS_NO_RECURSION + eng "The prepared statement contains a stored routine call that refers to that same statement. It's not allowed to execute a prepared statement in such a recursive manner" diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 59169f45f1b..14956138cbf 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -47,15 +47,30 @@ sp_map_result_type(enum enum_field_types type) } /* - * Returns TRUE if the 'cmd' is a command that might result in - * multiple result sets being sent back. - * Note: This does not include SQLCOM_SELECT which is treated - * separately in sql_yacc.yy. - */ -bool -sp_multi_results_command(enum enum_sql_command cmd) + SYNOPSIS + sp_get_flags_for_command() + + DESCRIPTION + Returns a combination of: + * sp_head::MULTI_RESULTS: added if the 'cmd' is a command that might + result in multiple result sets being sent back. + * sp_head::CONTAINS_DYNAMIC_SQL: added if 'cmd' is one of PREPARE, + EXECUTE, DEALLOCATE. +*/ + +uint +sp_get_flags_for_command(LEX *lex) { - switch (cmd) { + uint flags; + + switch (lex->sql_command) { + case SQLCOM_SELECT: + if (lex->result) + { + flags= 0; /* This is a SELECT with INTO clause */ + break; + } + /* fallthrough */ case SQLCOM_ANALYZE: case SQLCOM_CHECKSUM: case SQLCOM_HA_READ: @@ -90,10 +105,26 @@ sp_multi_results_command(enum enum_sql_command cmd) case SQLCOM_SHOW_TABLES: case SQLCOM_SHOW_VARIABLES: case SQLCOM_SHOW_WARNS: - return TRUE; + flags= sp_head::MULTI_RESULTS; + break; + /* + EXECUTE statement may return a result set, but doesn't have to. + We can't, however, know it in advance, and therefore must add + this statement here. This is ok, as is equivalent to a result-set + statement within an IF condition. + */ + case SQLCOM_EXECUTE: + flags= sp_head::MULTI_RESULTS | sp_head::CONTAINS_DYNAMIC_SQL; + break; + case SQLCOM_PREPARE: + case SQLCOM_DEALLOCATE_PREPARE: + flags= sp_head::CONTAINS_DYNAMIC_SQL; + break; default: - return FALSE; + flags= 0; + break; } + return flags; } @@ -364,9 +395,7 @@ sp_head::operator delete(void *ptr, size_t size) sp_head::sp_head() :Query_arena(&main_mem_root, INITIALIZED_FOR_SP), - m_returns_cs(NULL), m_has_return(FALSE), - m_simple_case(FALSE), m_multi_results(FALSE), m_in_handler(FALSE), - m_is_invoked(FALSE) + m_flags(0), m_returns_cs(NULL) { extern byte * sp_table_key(const byte *ptr, uint *plen, my_bool first); @@ -782,7 +811,7 @@ int sp_head::execute(THD *thd) DBUG_RETURN(-1); } - if (m_is_invoked) + if (m_flags & IS_INVOKED) { /* We have to disable recursion for stored routines since in @@ -802,7 +831,7 @@ int sp_head::execute(THD *thd) my_error(ER_SP_NO_RECURSION, MYF(0)); DBUG_RETURN(-1); } - m_is_invoked= TRUE; + m_flags|= IS_INVOKED; dbchanged= FALSE; if (m_db.length && @@ -889,6 +918,15 @@ int sp_head::execute(THD *thd) /* we should cleanup free_list and memroot, used by instruction */ thd->free_items(); + /* + FIXME: we must free user var events only if the routine is executed + in non-prelocked mode and statement-by-statement replication is used. + But if we don't free them now, the server crashes because user var + events are allocated in execute_mem_root. This is Bug#12637, and when + it's fixed, please add if (thd->options & OPTION_BIN_LOG) here. + */ + if (opt_bin_log) + reset_dynamic(&thd->user_var_events); free_root(&execute_mem_root, MYF(0)); /* @@ -955,7 +993,7 @@ int sp_head::execute(THD *thd) if (! thd->killed) ret= mysql_change_db(thd, olddb, 0); } - m_is_invoked= FALSE; + m_flags&= ~IS_INVOKED; DBUG_RETURN(ret); } @@ -1172,7 +1210,7 @@ int sp_head::execute_procedure(THD *thd, List *args) thd->spcont= save_spcont; DBUG_RETURN(-1); } - + if (csize > 0 || hmax > 0 || cmax > 0) { Item_null *nit= NULL; // Re-use this, and only create if needed @@ -1349,7 +1387,7 @@ int sp_head::execute_procedure(THD *thd, List *args) nctx->pop_all_cursors(); // To avoid memory leaks after an error delete nctx; // Does nothing thd->spcont= save_spcont; - + DBUG_RETURN(ret); } @@ -1397,7 +1435,6 @@ sp_head::restore_lex(THD *thd) LEX *sublex= thd->lex; LEX *oldlex= (LEX *)m_lex.pop(); - init_stmt_after_parse(thd, sublex); if (! oldlex) return; // Nothing to restore diff --git a/sql/sp_head.h b/sql/sp_head.h index 1d4bfb514d3..1c54b1a567d 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -33,8 +33,8 @@ Item_result sp_map_result_type(enum enum_field_types type); -bool -sp_multi_results_command(enum enum_sql_command cmd); +uint +sp_get_flags_for_command(LEX *lex); struct sp_label; class sp_instr; @@ -107,18 +107,23 @@ class sp_head :private Query_arena MEM_ROOT main_mem_root; public: + /* Possible values of m_flags */ + const static int + HAS_RETURN= 1, // For FUNCTIONs only: is set if has RETURN + IN_SIMPLE_CASE= 2, // Is set if parsing a simple CASE + IN_HANDLER= 4, // Is set if the parser is in a handler body + MULTI_RESULTS= 8, // Is set if a procedure with SELECT(s) + CONTAINS_DYNAMIC_SQL= 16, // Is set if a procedure with PREPARE/EXECUTE + IS_INVOKED= 32; // Is set if this sp_head is being used. int m_type; // TYPE_ENUM_FUNCTION or TYPE_ENUM_PROCEDURE + uint m_flags; // Boolean attributes of a stored routine enum enum_field_types m_returns; // For FUNCTIONs only Field::geometry_type m_geom_returns; CHARSET_INFO *m_returns_cs; // For FUNCTIONs only TYPELIB *m_returns_typelib; // For FUNCTIONs only uint m_returns_len; // For FUNCTIONs only uint m_returns_pack; // For FUNCTIONs only - my_bool m_has_return; // For FUNCTIONs only - my_bool m_simple_case; // TRUE if parsing simple case, FALSE otherwise - my_bool m_multi_results; // TRUE if a procedure with SELECT(s) - my_bool m_in_handler; // TRUE if parser in a handler body uchar *m_tmp_query; // Temporary pointer to sub query string uint m_old_cmq; // Old CLIENT_MULTI_QUERIES value st_sp_chistics *m_chistics; @@ -265,6 +270,19 @@ public: bool add_used_tables_to_table_list(THD *thd, TABLE_LIST ***query_tables_last_ptr); + /* + Check if this stored routine contains statements disallowed + in a stored function or trigger, and set an appropriate error message + if this is the case. + */ + bool is_not_allowed_in_function(const char *where) + { + if (m_flags & CONTAINS_DYNAMIC_SQL) + my_error(ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0), "Dynamic SQL"); + else if (m_flags & MULTI_RESULTS) + my_error(ER_SP_NO_RETSET, MYF(0), where); + return test(m_flags & (CONTAINS_DYNAMIC_SQL|MULTI_RESULTS)); + } private: MEM_ROOT *m_thd_root; // Temp. store for thd's mem_root @@ -290,9 +308,6 @@ private: */ HASH m_sptabs; - /* Used for tracking of routine invocations and preventing recursion. */ - bool m_is_invoked; - int execute(THD *thd); diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 17a48fb4925..2ff0413e05e 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -551,11 +551,6 @@ void THD::cleanup_after_query() } /* Free Items that were created during this execution */ free_items(); - /* - In the rest of code we assume that free_list never points to garbage: - Keep this predicate true. - */ - free_list= 0; } /* @@ -1686,23 +1681,17 @@ Statement_map::Statement_map() : NULL,MYF(0)); } + int Statement_map::insert(Statement *statement) { int rc= my_hash_insert(&st_hash, (byte *) statement); - if (rc == 0) - last_found_statement= statement; if (statement->name.str) { - /* - If there is a statement with the same name, remove it. It is ok to - remove old and fail to insert new one at the same time. - */ - Statement *old_stmt; - if ((old_stmt= find_by_name(&statement->name))) - erase(old_stmt); if ((rc= my_hash_insert(&names_hash, (byte*)statement))) hash_delete(&st_hash, (byte*)statement); } + if (rc == 0) + last_found_statement= statement; return rc; } diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 455c592241a..e579ee9f8bd 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -128,6 +128,7 @@ void lex_start(THD *thd, uchar *buf,uint length) lex->update_list.empty(); lex->param_list.empty(); lex->view_list.empty(); + lex->prepared_stmt_params.empty(); lex->unit.next= lex->unit.master= lex->unit.link_next= lex->unit.return_to= 0; lex->unit.prev= lex->unit.link_prev= 0; @@ -143,6 +144,7 @@ void lex_start(THD *thd, uchar *buf,uint length) lex->describe= 0; lex->subqueries= FALSE; lex->view_prepare_mode= FALSE; + lex->stmt_prepare_mode= FALSE; lex->derived_tables= 0; lex->lock_option= TL_READ; lex->found_semicolon= 0; @@ -568,8 +570,7 @@ int yylex(void *arg, void *yythd) its value in a query for the binlog, the query must stay grammatically correct. */ - else if (c == '?' && ((THD*) yythd)->command == COM_STMT_PREPARE && - !ident_map[yyPeek()]) + else if (c == '?' && lex->stmt_prepare_mode && !ident_map[yyPeek()]) return(PARAM_MARKER); return((int) c); @@ -981,7 +982,7 @@ int yylex(void *arg, void *yythd) { THD* thd= (THD*)yythd; if ((thd->client_capabilities & CLIENT_MULTI_STATEMENTS) && - (thd->command != COM_STMT_PREPARE)) + !lex->stmt_prepare_mode) { lex->safe_to_cache_query= 0; lex->found_semicolon=(char*) lex->ptr; diff --git a/sql/sql_lex.h b/sql/sql_lex.h index d777abca29a..6c91045189c 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -808,6 +808,11 @@ typedef struct st_lex to an .frm file. We need this definition to stay untouched. */ bool view_prepare_mode; + /* + TRUE if we're parsing a prepared statement: in this mode + we should allow placeholders and disallow multistatements. + */ + bool stmt_prepare_mode; bool safe_to_cache_query; bool subqueries, ignore; bool variables_used; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 937fd736282..7d430f2a44a 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1644,7 +1644,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, } case COM_STMT_PREPARE: { - mysql_stmt_prepare(thd, packet, packet_length, 0); + mysql_stmt_prepare(thd, packet, packet_length); break; } case COM_STMT_CLOSE: @@ -1664,6 +1664,10 @@ bool dispatch_command(enum enum_server_command command, THD *thd, char *packet_end= thd->query + thd->query_length; mysql_log.write(thd,command,"%s",thd->query); DBUG_PRINT("query",("%-.4096s",thd->query)); + + if (!(specialflag & SPECIAL_NO_PRIOR)) + my_pthread_setprio(pthread_self(),QUERY_PRIOR); + mysql_parse(thd,thd->query, thd->query_length); while (!thd->killed && thd->lex->found_semicolon && !thd->net.report_error) @@ -2220,7 +2224,7 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident, TRUE error; In this case thd->fatal_error is set */ -bool alloc_query(THD *thd, char *packet, ulong packet_length) +bool alloc_query(THD *thd, const char *packet, uint packet_length) { packet_length--; // Remove end null /* Remove garbage at start and end of query */ @@ -2229,7 +2233,7 @@ bool alloc_query(THD *thd, char *packet, ulong packet_length) packet++; packet_length--; } - char *pos=packet+packet_length; // Point at end null + const char *pos= packet + packet_length; // Point at end null while (packet_length > 0 && (pos[-1] == ';' || my_isspace(thd->charset() ,pos[-1]))) { @@ -2250,8 +2254,6 @@ bool alloc_query(THD *thd, char *packet, ulong packet_length) thd->packet.shrink(thd->variables.net_buffer_length); thd->convert_buffer.shrink(thd->variables.net_buffer_length); - if (!(specialflag & SPECIAL_NO_PRIOR)) - my_pthread_setprio(pthread_self(),QUERY_PRIOR); return FALSE; } @@ -2466,112 +2468,17 @@ mysql_execute_command(THD *thd) } case SQLCOM_PREPARE: { - char *query_str; - uint query_len; - if (lex->prepared_stmt_code_is_varref) - { - /* This is PREPARE stmt FROM @var. */ - String str; - CHARSET_INFO *to_cs= thd->variables.collation_connection; - bool need_conversion; - user_var_entry *entry; - String *pstr= &str; - uint32 unused; - /* - Convert @var contents to string in connection character set. Although - it is known that int/real/NULL value cannot be a valid query we still - convert it for error messages to uniform. - */ - if ((entry= - (user_var_entry*)hash_search(&thd->user_vars, - (byte*)lex->prepared_stmt_code.str, - lex->prepared_stmt_code.length)) - && entry->value) - { - my_bool is_var_null; - pstr= entry->val_str(&is_var_null, &str, NOT_FIXED_DEC); - /* - NULL value of variable checked early as entry->value so here - we can't get NULL in normal conditions - */ - DBUG_ASSERT(!is_var_null); - if (!pstr) - goto error; - } - else - { - /* - variable absent or equal to NULL, so we need to set variable to - something reasonable to get readable error message during parsing - */ - str.set("NULL", 4, &my_charset_latin1); - } - - need_conversion= - String::needs_conversion(pstr->length(), pstr->charset(), - to_cs, &unused); - - query_len= need_conversion? (pstr->length() * to_cs->mbmaxlen) : - pstr->length(); - if (!(query_str= alloc_root(thd->mem_root, query_len+1))) - goto error; - - if (need_conversion) - { - uint dummy_errors; - query_len= copy_and_convert(query_str, query_len, to_cs, - pstr->ptr(), pstr->length(), - pstr->charset(), &dummy_errors); - } - else - memcpy(query_str, pstr->ptr(), pstr->length()); - query_str[query_len]= 0; - } - else - { - query_str= lex->prepared_stmt_code.str; - query_len= lex->prepared_stmt_code.length; - DBUG_PRINT("info", ("PREPARE: %.*s FROM '%.*s' \n", - lex->prepared_stmt_name.length, - lex->prepared_stmt_name.str, - query_len, query_str)); - } - thd->command= COM_STMT_PREPARE; - if (!(res= mysql_stmt_prepare(thd, query_str, query_len + 1, - &lex->prepared_stmt_name))) - send_ok(thd, 0L, 0L, "Statement prepared"); + mysql_sql_stmt_prepare(thd); break; } case SQLCOM_EXECUTE: { - DBUG_PRINT("info", ("EXECUTE: %.*s\n", - lex->prepared_stmt_name.length, - lex->prepared_stmt_name.str)); - mysql_sql_stmt_execute(thd, &lex->prepared_stmt_name); - lex->prepared_stmt_params.empty(); + mysql_sql_stmt_execute(thd); break; } case SQLCOM_DEALLOCATE_PREPARE: { - Statement* stmt; - DBUG_PRINT("info", ("DEALLOCATE PREPARE: %.*s\n", - lex->prepared_stmt_name.length, - lex->prepared_stmt_name.str)); - /* We account deallocate in the same manner as mysql_stmt_close */ - statistic_increment(thd->status_var.com_stmt_close, &LOCK_status); - if ((stmt= thd->stmt_map.find_by_name(&lex->prepared_stmt_name))) - { - thd->stmt_map.erase(stmt); - send_ok(thd); - } - else - { - my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), - lex->prepared_stmt_name.length, - lex->prepared_stmt_name.str, - "DEALLOCATE PREPARE"); - goto error; - } + mysql_sql_stmt_close(thd); break; } case SQLCOM_DO: @@ -4124,7 +4031,7 @@ end_with_restore_list: } #endif if (lex->sphead->m_type == TYPE_ENUM_FUNCTION && - !lex->sphead->m_has_return) + !(lex->sphead->m_flags & sp_head::HAS_RETURN)) { my_error(ER_SP_NORETURN, MYF(0), name); delete lex->sphead; @@ -4213,15 +4120,31 @@ end_with_restore_list: ha_rows select_limit; /* bits that should be cleared in thd->server_status */ uint bits_to_be_cleared= 0; + /* + Check that the stored procedure doesn't contain Dynamic SQL + and doesn't return result sets: such stored procedures can't + be called from a function or trigger. + */ + if (thd->in_sub_stmt) + { + const char *where= (thd->in_sub_stmt & SUB_STMT_TRIGGER ? + "trigger" : "function"); + if (sp->is_not_allowed_in_function(where)) + goto error; + } #ifndef EMBEDDED_LIBRARY my_bool nsok= thd->net.no_send_ok; thd->net.no_send_ok= TRUE; #endif - if (sp->m_multi_results) + if (sp->m_flags & sp_head::MULTI_RESULTS) { if (! (thd->client_capabilities & CLIENT_MULTI_RESULTS)) { + /* + The client does not support multiple result sets being sent + back + */ my_error(ER_SP_BADSELECT, MYF(0), sp->m_qname.str); #ifndef EMBEDDED_LIBRARY thd->net.no_send_ok= nsok; @@ -4265,7 +4188,7 @@ end_with_restore_list: thd->row_count_func= 0; /* - We never write CALL statements int binlog: + We never write CALL statements into binlog: - If the mode is non-prelocked, each statement will be logged separately. - If the mode is prelocked, the invoking statement will care diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index bf0bea804c4..879ea626494 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -15,9 +15,9 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /********************************************************************** -This file contains the implementation of prepare and executes. +This file contains the implementation of prepared statements. -Prepare: +When one prepares a statement: - Server gets the query from client with command 'COM_STMT_PREPARE'; in the following format: @@ -25,21 +25,21 @@ Prepare: - Parse the query and recognize any parameter markers '?' and store its information list in lex->param_list - Allocate a new statement for this prepare; and keep this in - 'thd->prepared_statements' pool. + 'thd->stmt_map'. - Without executing the query, return back to client the total number of parameters along with result-set metadata information (if any) in the following format: [STMT_ID:4] [Column_count:2] [Param_count:2] + [Params meta info (stubs only for now)] (if Param_count > 0) [Columns meta info] (if Column_count > 0) - [Params meta info] (if Param_count > 0 ) (TODO : 4.1.1) -Prepare-execute: +When one executes a statement: - Server gets the command 'COM_STMT_EXECUTE' to execute the - previously prepared query. If there is any param markers; then client - will send the data in the following format: + previously prepared query. If there are any parameter markers, then the + client will send the data in the following format: [COM_STMT_EXECUTE:1] [STMT_ID:4] [NULL_BITS:(param_count+7)/8)] @@ -48,29 +48,29 @@ Prepare-execute: [[length]data] .. [[length]data]. (Note: Except for string/binary types; all other types will not be supplied with length field) - - Replace the param items with this new data. If it is a first execute - or types altered by client; then setup the conversion routines. + - If it is a first execute or types of parameters were altered by client, + then setup the conversion routines. + - Assign parameter items from the supplied data. - Execute the query without re-parsing and send back the results to client -Long data handling: +When one supplies long data for a placeholder: - Server gets the long data in pieces with command type 'COM_STMT_SEND_LONG_DATA'. - The packet recieved will have the format as: [COM_STMT_SEND_LONG_DATA:1][STMT_ID:4][parameter_number:2][data] - - data from the packet is appended to long data value buffer for this + - data from the packet is appended to the long data value buffer for this placeholder. - - It's up to the client to check for read data ended. The server doesn't - care; and also server doesn't notify to the client that it got the - data or not; if there is any error; then during execute; the error - will be returned + - It's up to the client to stop supplying data chunks at any point. The + server doesn't care; also, the server doesn't notify the client whether + it got the data or not; if there is any error, then it will be returned + at statement execute. ***********************************************************************/ #include "mysql_priv.h" #include "sql_select.h" // for JOIN -#include // for isspace() #include "sp_head.h" #include "sp.h" #include "sp_cache.h" @@ -82,7 +82,7 @@ Long data handling: #endif /****************************************************************************** - Prepared_statement: statement which can contain placeholders + Prepared_statement: a statement that can contain placeholders ******************************************************************************/ class Prepared_statement: public Statement @@ -93,6 +93,7 @@ public: Item_param **param_array; uint param_count; uint last_errno; + uint flags; char last_error[MYSQL_ERRMSG_SIZE]; #ifndef EMBEDDED_LIBRARY bool (*set_params)(Prepared_statement *st, uchar *data, uchar *data_end, @@ -104,15 +105,21 @@ public: List& varnames, String *expanded_query); public: - Prepared_statement(THD *thd_arg); + Prepared_statement(THD *thd_arg, Protocol *protocol_arg); virtual ~Prepared_statement(); void setup_set_params(); virtual Query_arena::Type type() const; virtual void close_cursor(); -}; + bool set_name(LEX_STRING *name); -static void execute_stmt(THD *thd, Prepared_statement *stmt, - String *expanded_query); + bool prepare(const char *packet, uint packet_length); + bool execute(String *expanded_query, bool open_cursor); + /* Destroy this statement */ + bool deallocate(); + + /* Possible values of flags */ + static const int IS_IN_USE= 1; +}; /****************************************************************************** Implementation @@ -127,13 +134,30 @@ inline bool is_param_null(const uchar *pos, ulong param_no) enum { STMT_QUERY_LOG_LENGTH= 8192 }; /* - Seek prepared statement in statement map by id: returns zero if statement - was not found, pointer otherwise. + Find a prepared statement in the statement map by id. + + SYNOPSIS + find_prepared_statement() + thd thread handle + id statement id + where the place from which this function is called (for + error reporting). + + DESCRIPTION + Try to find a prepared statement and set THD error if it's not found. + + RETURN VALUE + 0 if the statement was not found, a pointer otherwise. */ static Prepared_statement * find_prepared_statement(THD *thd, ulong id, const char *where) { + /* + To strictly separate namespaces of SQL prepared statements and C API + prepared statements find() will return 0 if there is a named prepared + statement with such id. + */ Statement *stmt= thd->stmt_map.find(id); if (stmt == 0 || stmt->type() != Query_arena::PREPARED_STATEMENT) @@ -148,7 +172,13 @@ find_prepared_statement(THD *thd, ulong id, const char *where) /* - Send prepared stmt info to client after prepare + Send prepared statement id and metadata to the client after prepare. + + SYNOPSIS + send_prep_stmt() + + RETURN VALUE + 0 in case of success, 1 otherwise */ #ifndef EMBEDDED_LIBRARY @@ -193,8 +223,20 @@ static bool send_prep_stmt(Prepared_statement *stmt, /* - Read the length of the parameter data and return back to - caller by positing the pointer to param data. + Read the length of the parameter data and return it back to + the caller. + + SYNOPSIS + get_param_length() + packet a pointer to the data + len remaining packet length + + DESCRIPTION + Read data length, position the packet to the first byte after it, + and return the length to the caller. + + RETURN VALUE + Length of data piece. */ #ifndef EMBEDDED_LIBRARY @@ -239,19 +281,21 @@ static ulong get_param_length(uchar **packet, ulong len) #endif /*!EMBEDDED_LIBRARY*/ /* - Data conversion routines + Data conversion routines. + SYNOPSIS - set_param_xx() - param parameter item - pos input data buffer - len length of data in the buffer + set_param_xx() + param parameter item + pos input data buffer + len length of data in the buffer - All these functions read the data from pos, convert it to requested type - and assign to param; pos is advanced to predefined length. + DESCRIPTION + All these functions read the data from pos, convert it to requested + type and assign to param; pos is advanced to predefined length. - Make a note that the NULL handling is examined at first execution - (i.e. when input types altered) and for all subsequent executions - we don't read any values for this. + Make a note that the NULL handling is examined at first execution + (i.e. when input types altered) and for all subsequent executions + we don't read any values for this. RETURN VALUE none @@ -594,8 +638,35 @@ static void setup_one_conversion_function(THD *thd, Item_param *param, #ifndef EMBEDDED_LIBRARY /* - Update the parameter markers by reading data from client packet - and if binary/update log is set, generate the valid query. + Routines to assign parameters from data supplied by the client. + + DESCRIPTION + Update the parameter markers by reading data from the packet and + and generate a valid query for logging. + + NOTES + This function, along with other _withlog functions is called when one of + binary, slow or general logs is open. Logging of prepared statements in + all cases is performed by means of conventional queries: if parameter + data was supplied from C API, each placeholder in the query is + replaced with its actual value; if we're logging a [Dynamic] SQL + prepared statement, parameter markers are replaced with variable names. + Example: + mysql_stmt_prepare("UPDATE t1 SET a=a*1.25 WHERE a=?") + --> general logs gets [Prepare] UPDATE t1 SET a*1.25 WHERE a=?" + mysql_stmt_execute(stmt); + --> general and binary logs get + [Execute] UPDATE t1 SET a*1.25 WHERE a=1" + If a statement has been prepared using SQL syntax: + PREPARE stmt FROM "UPDATE t1 SET a=a*1.25 WHERE a=?" + --> general log gets + [Query] PREPARE stmt FROM "UPDATE ..." + EXECUTE stmt USING @a + --> general log gets + [Query] EXECUTE stmt USING @a; + + RETURN VALUE + 0 if success, 1 otherwise */ static bool insert_params_withlog(Prepared_statement *stmt, uchar *null_array, @@ -707,6 +778,17 @@ static bool setup_conversion_functions(Prepared_statement *stmt, #else +/* + Embedded counterparts of parameter assignment routines. + + DESCRIPTION + The main difference between the embedded library and the server is + that in embedded case we don't serialize/deserialize parameters data. + Additionally, for unknown reason, the client-side flag raised for + changed types of placeholders is ignored and we simply setup conversion + functions at each execute (TODO: fix). +*/ + static bool emb_insert_params(Prepared_statement *stmt, String *expanded_query) { THD *thd= stmt->thd; @@ -791,7 +873,8 @@ static bool emb_insert_params_withlog(Prepared_statement *stmt, String *query) /* - Set prepared statement parameters from user variables. + Assign prepared statement parameters from user variables. + SYNOPSIS insert_params_from_vars() stmt Statement @@ -829,12 +912,14 @@ static bool insert_params_from_vars(Prepared_statement *stmt, /* Do the same as insert_params_from_vars but also construct query text for binary log. + SYNOPSIS insert_params_from_vars() - stmt Statement + stmt Prepared statement varnames List of variables. Caller must ensure that number of variables in the list is equal to number of statement parameters - query The query with parameter markers replaced with their values + query The query with parameter markers replaced with corresponding + user variables that were used to execute the query. */ static bool insert_params_from_vars_with_log(Prepared_statement *stmt, @@ -845,12 +930,13 @@ static bool insert_params_from_vars_with_log(Prepared_statement *stmt, Item_param **end= begin + stmt->param_count; user_var_entry *entry; LEX_STRING *varname; - DBUG_ENTER("insert_params_from_vars"); - List_iterator var_it(varnames); String buf; const String *val; uint32 length= 0; + + DBUG_ENTER("insert_params_from_vars"); + if (query->copy(stmt->query, stmt->query_length, default_charset_info)) DBUG_RETURN(1); @@ -858,7 +944,8 @@ static bool insert_params_from_vars_with_log(Prepared_statement *stmt, { Item_param *param= *it; varname= var_it++; - if (get_var_with_binlog(stmt->thd, *varname, &entry)) + if (get_var_with_binlog(stmt->thd, stmt->lex->sql_command, + *varname, &entry)) DBUG_RETURN(1); if (param->set_from_user_var(stmt->thd, entry)) @@ -895,16 +982,16 @@ static bool insert_params_from_vars_with_log(Prepared_statement *stmt, } /* - Validate INSERT statement: + Validate INSERT statement. SYNOPSIS mysql_test_insert() - stmt prepared statemen handler - tables global/local table list + stmt prepared statement + tables global/local table list RETURN VALUE - FALSE success - TRUE error, error message is set in THD + FALSE success + TRUE error, error message is set in THD */ static bool mysql_test_insert(Prepared_statement *stmt, @@ -988,13 +1075,13 @@ error: SYNOPSIS mysql_test_update() - stmt prepared statemen handler - tables list of tables queries + stmt prepared statement + tables list of tables used in this query RETURN VALUE - 0 success - 1 error, error message is set in THD - 2 convert to multi_update + 0 success + 1 error, error message is set in THD + 2 convert to multi_update */ static int mysql_test_update(Prepared_statement *stmt, @@ -1068,16 +1155,16 @@ error: /* - Validate DELETE statement + Validate DELETE statement. SYNOPSIS mysql_test_delete() - stmt prepared statemen handler - tables list of tables queries + stmt prepared statement + tables list of tables used in this query RETURN VALUE - FALSE success - TRUE error, error message is set in THD + FALSE success + TRUE error, error message is set in THD */ static bool mysql_test_delete(Prepared_statement *stmt, @@ -1106,21 +1193,24 @@ error: /* Validate SELECT statement. - In case of success, if this query is not EXPLAIN, send column list info - back to client. SYNOPSIS mysql_test_select() - stmt prepared statemen handler - tables list of tables queries + stmt prepared statement + tables list of tables used in the query + + DESCRIPTION + In case of success, if this query is not EXPLAIN, send column list info + back to the client. RETURN VALUE - FALSE success - TRUE error, sent to client + 0 success + 1 error, error message is set in THD + 2 success, and statement metadata has been sent */ -static bool mysql_test_select(Prepared_statement *stmt, - TABLE_LIST *tables, bool text_protocol) +static int mysql_test_select(Prepared_statement *stmt, + TABLE_LIST *tables, bool text_protocol) { THD *thd= stmt->thd; LEX *lex= stmt->lex; @@ -1158,51 +1248,43 @@ static bool mysql_test_select(Prepared_statement *stmt, */ if (unit->prepare(thd, 0, 0, "")) goto error; - if (!text_protocol) + if (!lex->describe && !text_protocol) { - if (lex->describe) - { - if (send_prep_stmt(stmt, 0) || thd->protocol->flush()) - goto error; - } - else - { - /* Make copy of item list, as change_columns may change it */ - List fields(lex->select_lex.item_list); + /* Make copy of item list, as change_columns may change it */ + List fields(lex->select_lex.item_list); - /* Change columns if a procedure like analyse() */ - if (unit->last_procedure && - unit->last_procedure->change_columns(fields)) - goto error; + /* Change columns if a procedure like analyse() */ + if (unit->last_procedure && unit->last_procedure->change_columns(fields)) + goto error; - /* - We can use lex->result as it should've been - prepared in unit->prepare call above. - */ - if (send_prep_stmt(stmt, lex->result->field_count(fields)) || - lex->result->send_fields(fields, Protocol::SEND_EOF) || - thd->protocol->flush()) - goto error; - } + /* + We can use lex->result as it should've been prepared in + unit->prepare call above. + */ + if (send_prep_stmt(stmt, lex->result->field_count(fields)) || + lex->result->send_fields(fields, Protocol::SEND_EOF) || + thd->protocol->flush()) + goto error; + DBUG_RETURN(2); } - DBUG_RETURN(FALSE); + DBUG_RETURN(0); error: - DBUG_RETURN(TRUE); + DBUG_RETURN(1); } /* - Validate and prepare for execution DO statement expressions + Validate and prepare for execution DO statement expressions. SYNOPSIS mysql_test_do_fields() - stmt prepared statemen handler - tables list of tables queries - values list of expressions + stmt prepared statement + tables list of tables used in this query + values list of expressions RETURN VALUE - FALSE success - TRUE error, error message is set in THD + FALSE success + TRUE error, error message is set in THD */ static bool mysql_test_do_fields(Prepared_statement *stmt, @@ -1226,13 +1308,13 @@ static bool mysql_test_do_fields(Prepared_statement *stmt, SYNOPSIS mysql_test_set_fields() - stmt prepared statemen handler - tables list of tables queries - values list of expressions + stmt prepared statement + tables list of tables used in this query + values list of expressions RETURN VALUE - FALSE success - TRUE error + FALSE success + TRUE error, error message is set in THD */ static bool mysql_test_set_fields(Prepared_statement *stmt, @@ -1264,9 +1346,9 @@ error: SYNOPSIS select_like_stmt_test() - stmt - prepared statement handler - specific_prepare - function of command specific prepare - setup_tables_done_option - options to be passed to LEX::unit.prepare() + stmt prepared statement + specific_prepare function of command specific prepare + setup_tables_done_option options to be passed to LEX::unit.prepare() NOTE This function won't directly open tables used in select. They should @@ -1275,8 +1357,8 @@ error: "specific_prepare" call (like this happens in case of multi-update). RETURN VALUE - FALSE success - TRUE error, error message is set in THD + FALSE success + TRUE error, error message is set in THD */ static bool select_like_stmt_test(Prepared_statement *stmt, @@ -1300,19 +1382,19 @@ static bool select_like_stmt_test(Prepared_statement *stmt, /* Check internal SELECT of the prepared command (with opening and - locking tables used). + locking of used tables). SYNOPSIS select_like_stmt_test_with_open_n_lock() - stmt - prepared statement handler - tables - list of tables to be opened and locked - before calling specific_prepare function - specific_prepare - function of command specific prepare - setup_tables_done_option - options to be passed to LEX::unit.prepare() + stmt prepared statement + tables list of tables to be opened and locked + before calling specific_prepare function + specific_prepare function of command specific prepare + setup_tables_done_option options to be passed to LEX::unit.prepare() RETURN VALUE - FALSE success - TRUE error + FALSE success + TRUE error */ static bool @@ -1341,12 +1423,12 @@ select_like_stmt_test_with_open_n_lock(Prepared_statement *stmt, SYNOPSIS mysql_test_create_table() - stmt prepared statemen handler - tables list of tables queries + stmt prepared statement + tables list of tables used in this query RETURN VALUE - FALSE success - TRUE error, error message is set in THD + FALSE success + TRUE error, error message is set in THD */ static bool mysql_test_create_table(Prepared_statement *stmt) @@ -1377,17 +1459,17 @@ static bool mysql_test_create_table(Prepared_statement *stmt) /* - Validate and prepare for execution multi update statement + Validate and prepare for execution a multi update statement. SYNOPSIS mysql_test_multiupdate() - stmt prepared statemen handler - tables list of tables queries - converted converted to multi-update from usual update + stmt prepared statement + tables list of tables used in this query + converted converted to multi-update from usual update RETURN VALUE - FALSE success - TRUE error + FALSE success + TRUE error, error message is set in THD */ static bool mysql_test_multiupdate(Prepared_statement *stmt, @@ -1404,16 +1486,16 @@ static bool mysql_test_multiupdate(Prepared_statement *stmt, /* - Validate and prepare for execution multi delete statement + Validate and prepare for execution a multi delete statement. SYNOPSIS mysql_test_multidelete() - stmt prepared statemen handler - tables list of tables queries + stmt prepared statement + tables list of tables used in this query RETURN VALUE - 0 success - 1 error, error message in THD is set. + FALSE success + TRUE error, error message in THD is set. */ static bool mysql_test_multidelete(Prepared_statement *stmt, @@ -1449,10 +1531,11 @@ error: SYNOPSIS mysql_insert_select_prepare_tester() - thd thread handler + thd thread handle - NOTE: we need remove first local tables after open_and_lock_tables, - because mysql_handle_derived use local tables lists + NOTE + We need to remove the first local table after open_and_lock_tables, + because mysql_handle_derived uses local tables lists. */ static bool mysql_insert_select_prepare_tester(THD *thd) @@ -1476,21 +1559,20 @@ static bool mysql_insert_select_prepare_tester(THD *thd) /* - Validate and prepare for execution INSERT ... SELECT statement + Validate and prepare for execution INSERT ... SELECT statement. SYNOPSIS mysql_test_insert_select() - stmt prepared statemen handler - tables list of tables of query + stmt prepared statement + tables list of tables used in this query RETURN VALUE - 0 success - 1 error, sent to client - -1 error, not sent to client + FALSE success + TRUE error, error message is set in THD */ -static int mysql_test_insert_select(Prepared_statement *stmt, - TABLE_LIST *tables) +static bool mysql_test_insert_select(Prepared_statement *stmt, + TABLE_LIST *tables) { int res; LEX *lex= stmt->lex; @@ -1525,7 +1607,7 @@ static int mysql_test_insert_select(Prepared_statement *stmt, SYNOPSIS check_prepared_statement() - stmt prepared statement + stmt prepared statement DESCRIPTION This function @@ -1534,8 +1616,8 @@ static int mysql_test_insert_select(Prepared_statement *stmt, by calling fix_fields. RETURN VALUE - FALSE success, statement metadata is sent to client - TRUE error, error message is set (but not sent) + FALSE success, statement metadata is sent to client + TRUE error, error message is set in THD (but not sent) */ static bool check_prepared_statement(Prepared_statement *stmt, @@ -1582,11 +1664,13 @@ static bool check_prepared_statement(Prepared_statement *stmt, break; case SQLCOM_SELECT: - if ((res= mysql_test_select(stmt, tables, text_protocol))) - goto error; - /* Statement and field info has already been sent */ - DBUG_RETURN(FALSE); - + res= mysql_test_select(stmt, tables, text_protocol); + if (res == 2) + { + /* Statement and field info has already been sent */ + DBUG_RETURN(FALSE); + } + break; case SQLCOM_CREATE_TABLE: res= mysql_test_create_table(stmt); break; @@ -1695,21 +1779,19 @@ static void cleanup_stmt_and_thd_after_use(Statement *stmt, THD *thd) DBUG_VOID_RETURN; } + /* - Given a query string with parameter markers, create a Prepared Statement - from it and send PS info back to the client. + COM_STMT_PREPARE handler. SYNOPSIS mysql_stmt_prepare() - packet query to be prepared - packet_length query string length, including ignored trailing NULL or - quote char. - name NULL or statement name. For unnamed statements binary PS - protocol is used, for named statements text protocol is - used. - RETURN - FALSE OK, statement prepared successfully - TRUE Error + packet query to be prepared + packet_length query string length, including ignored + trailing NULL or quote char. + + DESCRIPTION + Given a query string with parameter markers, create a prepared + statement from it and send PS info back to the client. NOTES This function parses the query and sends the total number of parameters @@ -1722,124 +1804,147 @@ static void cleanup_stmt_and_thd_after_use(Statement *stmt, THD *thd) that a fast and direct retrieval can be made without going through all field items. + RETURN VALUE + none: in case of success a new statement id and metadata is sent + to the client, otherwise an error message is set in THD. */ -bool mysql_stmt_prepare(THD *thd, char *packet, uint packet_length, - LEX_STRING *name) +void mysql_stmt_prepare(THD *thd, const char *packet, uint packet_length) { - LEX *lex; - Statement stmt_backup; - Prepared_statement *stmt= new Prepared_statement(thd); - bool error; + Prepared_statement *stmt= new Prepared_statement(thd, &thd->protocol_prep); + bool rc; DBUG_ENTER("mysql_stmt_prepare"); DBUG_PRINT("prep_query", ("%s", packet)); - /* - If this is an SQLCOM_PREPARE, we also increase Com_prepare_sql. - However, it seems handy if com_stmt_prepare is increased always, - no matter what kind of prepare is processed. - */ - statistic_increment(thd->status_var.com_stmt_prepare, &LOCK_status); - if (stmt == 0) - DBUG_RETURN(TRUE); - - if (name) - { - stmt->name.length= name->length; - if (!(stmt->name.str= memdup_root(stmt->mem_root, (char*)name->str, - name->length))) - { - delete stmt; - DBUG_RETURN(TRUE); - } - } + DBUG_VOID_RETURN; /* out of memory: error is set in Sql_alloc */ if (thd->stmt_map.insert(stmt)) { delete stmt; - DBUG_RETURN(TRUE); + DBUG_VOID_RETURN; /* out of memory */ } - /* - alloc_query() uses thd->memroot && thd->query, so we have to call - both of backup_statement() and backup_item_area() here. - */ - thd->set_n_backup_statement(stmt, &stmt_backup); - thd->set_n_backup_active_arena(stmt, &stmt_backup); - - if (alloc_query(thd, packet, packet_length)) - { - thd->restore_backup_statement(stmt, &stmt_backup); - thd->restore_active_arena(stmt, &stmt_backup); - /* Statement map deletes statement on erase */ - thd->stmt_map.erase(stmt); - DBUG_RETURN(TRUE); - } - - mysql_log.write(thd, thd->command, "[%lu] %s", stmt->id, packet); - - thd->stmt_arena= stmt; - mysql_init_query(thd, (uchar *) thd->query, thd->query_length); + mysql_reset_thd_for_next_command(thd); /* Reset warnings from previous command */ mysql_reset_errors(thd, 0); - lex= thd->lex; - lex->safe_to_cache_query= 0; - sp_cache_flush_obsolete(&thd->sp_proc_cache); sp_cache_flush_obsolete(&thd->sp_func_cache); - error= yyparse((void *)thd) || thd->is_fatal_error || - thd->net.report_error || init_param_array(stmt); - /* - While doing context analysis of the query (in check_prepared_statement) - we allocate a lot of additional memory: for open tables, JOINs, derived - tables, etc. Let's save a snapshot of current parse tree to the - statement and restore original THD. In cases when some tree - transformation can be reused on execute, we set again thd->mem_root from - stmt->mem_root (see setup_wild for one place where we do that). - */ - thd->restore_active_arena(stmt, &stmt_backup); + if (!(specialflag & SPECIAL_NO_PRIOR)) + my_pthread_setprio(pthread_self(),QUERY_PRIOR); - if (!error) - error= check_prepared_statement(stmt, test(name)); + rc= stmt->prepare(packet, packet_length); - /* restore to WAIT_PRIOR: QUERY_PRIOR is set inside alloc_query */ if (!(specialflag & SPECIAL_NO_PRIOR)) my_pthread_setprio(pthread_self(),WAIT_PRIOR); - if (error && thd->lex->sphead) - { - delete thd->lex->sphead; - thd->lex->sphead= NULL; - } - lex_end(lex); - close_thread_tables(thd); - cleanup_stmt_and_thd_after_use(stmt, thd); - thd->restore_backup_statement(stmt, &stmt_backup); - thd->stmt_arena= thd; - if (error) + if (rc) { /* Statement map deletes statement on erase */ thd->stmt_map.erase(stmt); - stmt= NULL; + } + else + mysql_log.write(thd, COM_STMT_PREPARE, "[%lu] %s", stmt->id, packet); + + /* check_prepared_statemnt sends the metadata packet in case of success */ + DBUG_VOID_RETURN; +} + +/* + SYNOPSIS + get_dynamic_sql_string() + lex in main lex + query_len out length of the SQL statement (is set only + in case of success) + + DESCRIPTION + Get an SQL statement text from a user variable or from plain + text. If the statement is plain text, just assign the + pointers, otherwise allocate memory in thd->mem_root and copy + the contents of the variable, possibly with character + set conversion. + + RETURN VALUE + non-zero success, 0 in case of error (out of memory) +*/ + +static const char *get_dynamic_sql_string(LEX *lex, uint *query_len) +{ + THD *thd= lex->thd; + char *query_str= 0; + + if (lex->prepared_stmt_code_is_varref) + { + /* This is PREPARE stmt FROM or EXECUTE IMMEDIATE @var. */ + String str; + CHARSET_INFO *to_cs= thd->variables.collation_connection; + bool needs_conversion; + user_var_entry *entry; + String *pstr= &str; + uint32 unused, len; + /* + Convert @var contents to string in connection character set. Although + it is known that int/real/NULL value cannot be a valid query we still + convert it for error messages to be uniform. + */ + if ((entry= + (user_var_entry*)hash_search(&thd->user_vars, + (byte*)lex->prepared_stmt_code.str, + lex->prepared_stmt_code.length)) + && entry->value) + { + my_bool is_var_null; + pstr= entry->val_str(&is_var_null, &str, NOT_FIXED_DEC); + /* + NULL value of variable checked early as entry->value so here + we can't get NULL in normal conditions + */ + DBUG_ASSERT(!is_var_null); + if (!pstr) + goto end; + } + else + { + /* + variable absent or equal to NULL, so we need to set variable to + something reasonable to get a readable error message during parsing + */ + str.set("NULL", 4, &my_charset_latin1); + } + + needs_conversion= String::needs_conversion(pstr->length(), + pstr->charset(), to_cs, &unused); + + len= needs_conversion ? pstr->length() * to_cs->mbmaxlen : pstr->length(); + if (!(query_str= alloc_root(thd->mem_root, len+1))) + goto end; + + if (needs_conversion) + { + uint dummy_errors; + len= copy_and_convert(query_str, len, to_cs, pstr->ptr(), pstr->length(), + pstr->charset(), &dummy_errors); + } + else + memcpy(query_str, pstr->ptr(), pstr->length()); + query_str[len]= '\0'; + *query_len= len; } else { - stmt->setup_set_params(); - init_stmt_after_parse(thd, stmt->lex); - stmt->state= Query_arena::PREPARED; + query_str= lex->prepared_stmt_code.str; + *query_len= lex->prepared_stmt_code.length; } - DBUG_RETURN(!stmt); +end: + return query_str; } -/* - Init PS/SP specific parse tree members. -*/ +/* Init PS/SP specific parse tree members. */ -void init_stmt_after_parse(THD *thd, LEX *lex) +static void init_stmt_after_parse(LEX *lex) { SELECT_LEX *sl= lex->all_selects_list; /* @@ -1850,6 +1955,66 @@ void init_stmt_after_parse(THD *thd, LEX *lex) sl->uncacheable&= ~UNCACHEABLE_PREPARE; } +/* + SQLCOM_PREPARE implementation. + + SYNOPSIS + mysql_sql_stmt_prepare() + thd thread handle + + DESCRIPTION + Prepare an SQL prepared statement. This is called from + mysql_execute_command and should therefore behave like an + ordinary query (e.g. should not reset any global THD data). + + RETURN VALUE + none: in case of success, OK packet is sent to the client, + otherwise an error message is set in THD +*/ + +void mysql_sql_stmt_prepare(THD *thd) +{ + LEX *lex= thd->lex; + LEX_STRING *name= &lex->prepared_stmt_name; + Prepared_statement *stmt; + const char *query; + uint query_len; + + DBUG_ENTER("mysql_sql_stmt_prepare"); + + DBUG_ASSERT(thd->protocol == &thd->protocol_simple); + if ((stmt= (Prepared_statement*) thd->stmt_map.find_by_name(name))) + { + /* + If there is a statement with the same name, remove it. It is ok to + remove old and fail to insert a new one at the same time. + */ + if (stmt->deallocate()) + DBUG_VOID_RETURN; + } + + if (! (query= get_dynamic_sql_string(lex, &query_len)) || + ! (stmt= new Prepared_statement(thd, &thd->protocol_simple))) + { + DBUG_VOID_RETURN; /* out of memory */ + } + + if (stmt->set_name(name) || thd->stmt_map.insert(stmt)) + { + delete stmt; + DBUG_VOID_RETURN; + } + + if (stmt->prepare(query, query_len+1)) + { + /* Statement map deletes the statement on erase */ + thd->stmt_map.erase(stmt); + } + else + send_ok(thd, 0L, 0L, "Statement prepared"); + + DBUG_VOID_RETURN; +} /* Reinit prepared statement/stored procedure before execution */ @@ -1956,7 +2121,8 @@ void reinit_stmt_before_use(THD *thd, LEX *lex) SYNOPSIS reset_stmt_params() - stmt prepared statement for which parameters should be reset + stmt prepared statement for which parameters should + be reset */ static void reset_stmt_params(Prepared_statement *stmt) @@ -1969,81 +2135,50 @@ static void reset_stmt_params(Prepared_statement *stmt) /* - Executes previously prepared query. - If there is any parameters, then replace markers with the data supplied - from client, and then execute the query. + COM_STMT_EXECUTE handler: execute a previously prepared statement. SYNOPSIS mysql_stmt_execute() - thd Current thread - packet Query string - packet_length Query string length, including terminator character. + thd current thread + packet parameter types and data, if any + packet_length packet length, including the terminator character. + + DESCRIPTION + If there are any parameters, then replace parameter markers with the + data supplied from the client, and then execute the statement. + This function uses binary protocol to send a possible result set + to the client. + + RETURN VALUE + none: in case of success OK packet or a result set is sent to the + client, otherwise an error message is set in THD. */ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length) { ulong stmt_id= uint4korr(packet); ulong flags= (ulong) ((uchar) packet[4]); - Statement stmt_backup; - Cursor *cursor; - /* - Query text for binary log, or empty string if the query is not put into - binary log. - */ + /* Query text for binary, general or slow log, if any of them is open */ String expanded_query; #ifndef EMBEDDED_LIBRARY uchar *packet_end= (uchar *) packet + packet_length - 1; #endif Prepared_statement *stmt; + bool rc; DBUG_ENTER("mysql_stmt_execute"); packet+= 9; /* stmt_id + 5 bytes of flags */ - statistic_increment(thd->status_var.com_stmt_execute, &LOCK_status); if (!(stmt= find_prepared_statement(thd, stmt_id, "mysql_stmt_execute"))) DBUG_VOID_RETURN; DBUG_PRINT("exec_query", ("%s", stmt->query)); DBUG_PRINT("info",("stmt: %p", stmt)); - /* Check if we got an error when sending long data */ - if (stmt->state == Query_arena::ERROR) - { - my_message(stmt->last_errno, stmt->last_error, MYF(0)); - DBUG_VOID_RETURN; - } - - cursor= stmt->cursor; - if (cursor && cursor->is_open()) - stmt->close_cursor(); - - DBUG_ASSERT(thd->free_list == NULL); mysql_reset_thd_for_next_command(thd); - if (flags & (ulong) CURSOR_TYPE_READ_ONLY) - { - if (!stmt->lex->result || !stmt->lex->result->simple_select()) - { - DBUG_PRINT("info",("Cursor asked for not SELECT stmt")); - /* - If lex->result is set in the parser, this is not a SELECT - statement: we can't open a cursor for it. - */ - flags= 0; - my_error(ER_SP_BAD_CURSOR_QUERY, MYF(0)); - goto err; - } - else - { - DBUG_PRINT("info",("Using READ_ONLY cursor")); - if (!cursor && - !(cursor= stmt->cursor= new (stmt->mem_root) Cursor(thd))) - DBUG_VOID_RETURN; - /* If lex->result is set, mysql_execute_command will use it */ - stmt->lex->result= &cursor->result; - stmt->protocol= &cursor->protocol; - thd->lock_id= &cursor->lock_id; - } - } + sp_cache_flush_obsolete(&thd->sp_proc_cache); + sp_cache_flush_obsolete(&thd->sp_func_cache); + #ifndef EMBEDDED_LIBRARY if (stmt->param_count) { @@ -2062,93 +2197,67 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length) if (stmt->param_count && stmt->set_params_data(stmt, &expanded_query)) goto set_params_data_err; #endif - thd->set_n_backup_statement(stmt, &stmt_backup); - thd->stmt_arena= stmt; - reinit_stmt_before_use(thd, stmt->lex); - /* From now cursors assume that thd->mem_root is clean */ - if (expanded_query.length() && - alloc_query(thd, (char *)expanded_query.ptr(), - expanded_query.length()+1)) - { - my_error(ER_OUTOFMEMORY, 0, expanded_query.length()); - goto err; - } - mysql_log.write(thd, thd->command, "[%lu] %s", stmt->id, thd->query); - - thd->protocol= stmt->protocol; // Switch to binary protocol if (!(specialflag & SPECIAL_NO_PRIOR)) my_pthread_setprio(pthread_self(),QUERY_PRIOR); - sp_cache_flush_obsolete(&thd->sp_proc_cache); - sp_cache_flush_obsolete(&thd->sp_func_cache); - mysql_execute_command(thd); + rc= stmt->execute(&expanded_query, + test(flags & (ulong) CURSOR_TYPE_READ_ONLY)); if (!(specialflag & SPECIAL_NO_PRIOR)) my_pthread_setprio(pthread_self(), WAIT_PRIOR); - thd->protocol= &thd->protocol_simple; // Use normal protocol + if (rc) + goto err; - if (cursor && cursor->is_open()) - { - /* - It's safer if we grab THD state after mysql_execute_command is - finished and not in Cursor::open(), because currently the call to - Cursor::open is buried deep in JOIN::exec of the top level join. - */ - cursor->init_from_thd(thd); + mysql_log.write(thd, COM_STMT_EXECUTE, "[%lu] %s", stmt->id, thd->query); - if (cursor->close_at_commit) - thd->stmt_map.add_transient_cursor(stmt); - } - else - { - close_thread_tables(thd); - cleanup_stmt_and_thd_after_use(stmt, thd); - reset_stmt_params(stmt); - } - - log_slow_statement(thd); - /* Prevent from second logging in the end of dispatch_command */ - thd->enable_slow_log= FALSE; - - thd->set_statement(&stmt_backup); - thd->lock_id= &thd->main_lock_id; - thd->stmt_arena= thd; DBUG_VOID_RETURN; set_params_data_err: - reset_stmt_params(stmt); my_error(ER_WRONG_ARGUMENTS, MYF(0), "mysql_stmt_execute"); err: + reset_stmt_params(stmt); DBUG_VOID_RETURN; } /* - Execute prepared statement using parameter values from - lex->prepared_stmt_params and send result to the client using text protocol. + SQLCOM_EXECUTE implementation. + + SYNOPSIS + mysql_sql_stmt_execute() + thd thread handle + + DESCRIPTION + Execute prepared statement using parameter values from + lex->prepared_stmt_params and send result to the client using + text protocol. This is called from mysql_execute_command and + therefore should behave like an ordinary query (e.g. not change + global THD data, such as warning count, server status, etc). + This function uses text protocol to send a possible result set. + + RETURN + none: in case of success, OK (or result set) packet is sent to the + client, otherwise an error is set in THD */ -void mysql_sql_stmt_execute(THD *thd, LEX_STRING *stmt_name) +void mysql_sql_stmt_execute(THD *thd) { + LEX *lex= thd->lex; Prepared_statement *stmt; - /* - Query text for binary log, or empty string if the query is not put into - binary log. - */ + LEX_STRING *name= &lex->prepared_stmt_name; + /* Query text for binary, general or slow log, if any of them is open */ String expanded_query; - Statement stmt_backup; + DBUG_ENTER("mysql_sql_stmt_execute"); - DBUG_ASSERT(thd->free_list == NULL); - /* See comment for statistic_increment in mysql_stmt_prepare */ - statistic_increment(thd->status_var.com_stmt_execute, &LOCK_status); + DBUG_PRINT("info", ("EXECUTE: %.*s\n", name->length, name->str)); - if (!(stmt= (Prepared_statement*)thd->stmt_map.find_by_name(stmt_name))) + if (!(stmt= (Prepared_statement*) thd->stmt_map.find_by_name(name))) { - my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), stmt_name->length, - stmt_name->str, "EXECUTE"); + my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), + name->length, name->str, "EXECUTE"); DBUG_VOID_RETURN; } - if (stmt->param_count != thd->lex->prepared_stmt_params.elements) + if (stmt->param_count != lex->prepared_stmt_params.elements) { my_error(ER_WRONG_ARGUMENTS, MYF(0), "EXECUTE"); DBUG_VOID_RETURN; @@ -2156,78 +2265,15 @@ void mysql_sql_stmt_execute(THD *thd, LEX_STRING *stmt_name) DBUG_PRINT("info",("stmt: %p", stmt)); - /* Must go before setting variables, as it clears thd->user_var_events */ - mysql_reset_thd_for_next_command(thd); - thd->set_n_backup_statement(stmt, &stmt_backup); - if (stmt->set_params_from_vars(stmt, - stmt_backup.lex->prepared_stmt_params, + if (stmt->set_params_from_vars(stmt, lex->prepared_stmt_params, &expanded_query)) { my_error(ER_WRONG_ARGUMENTS, MYF(0), "EXECUTE"); - } - thd->command= COM_STMT_EXECUTE; /* For nice messages in general log */ - execute_stmt(thd, stmt, &expanded_query); - thd->set_statement(&stmt_backup); - DBUG_VOID_RETURN; -} - - -/* - Execute prepared statement. - SYNOPSIS - execute_stmt() - thd Current thread - stmt Statement to execute - expanded_query If binary log is enabled, query string with parameter - placeholders replaced with actual values. Otherwise empty - string. - NOTES - Caller must set parameter values and thd::protocol. -*/ - -static void execute_stmt(THD *thd, Prepared_statement *stmt, - String *expanded_query) -{ - DBUG_ENTER("execute_stmt"); - - reinit_stmt_before_use(thd, stmt->lex); - - if (expanded_query->length() && - alloc_query(thd, (char *)expanded_query->ptr(), - expanded_query->length()+1)) - { - my_error(ER_OUTOFMEMORY, MYF(0), expanded_query->length()); DBUG_VOID_RETURN; } - mysql_log.write(thd, thd->command, "[%lu] %s", stmt->id, thd->query); - /* - At first execution of prepared statement we will perform logical - transformations of the query tree (i.e. negations elimination). - This should be done permanently on the parse tree of this statement. - */ - thd->stmt_arena= stmt; - if (!(specialflag & SPECIAL_NO_PRIOR)) - my_pthread_setprio(pthread_self(),QUERY_PRIOR); - mysql_execute_command(thd); - if (!(specialflag & SPECIAL_NO_PRIOR)) - my_pthread_setprio(pthread_self(), WAIT_PRIOR); - /* - 'start_time' is set in dispatch_command, but THD::query will - be freed when we return from this function. So let's log the slow - query here. - */ - log_slow_statement(thd); - /* Prevent from second logging in the end of dispatch_command */ - thd->enable_slow_log= FALSE; + (void) stmt->execute(&expanded_query, FALSE); - close_thread_tables(thd); // to close derived tables - cleanup_stmt_and_thd_after_use(stmt, thd); - reset_stmt_params(stmt); - thd->stmt_arena= thd; - - if (stmt->state == Query_arena::PREPARED) - stmt->state= Query_arena::EXECUTED; DBUG_VOID_RETURN; } @@ -2237,9 +2283,9 @@ static void execute_stmt(THD *thd, Prepared_statement *stmt, SYNOPSIS mysql_stmt_fetch() - thd Thread handler - packet Packet from client (with stmt_id & num_rows) - packet_length Length of packet + thd Thread handle + packet Packet from client (with stmt_id & num_rows) + packet_length Length of packet */ void mysql_stmt_fetch(THD *thd, char *packet, uint packet_length) @@ -2301,8 +2347,8 @@ void mysql_stmt_fetch(THD *thd, char *packet, uint packet_length) Reset a prepared statement in case there was a recoverable error. SYNOPSIS mysql_stmt_reset() - thd Thread handle - packet Packet with stmt id + thd Thread handle + packet Packet with stmt id DESCRIPTION This function resets statement to the state it was right after prepare. @@ -2310,6 +2356,7 @@ void mysql_stmt_fetch(THD *thd, char *packet, uint packet_length) - clear an error happened during mysql_stmt_send_long_data - cancel long data stream for all placeholders without having to call mysql_stmt_execute. + - close an open cursor Sends 'OK' packet in case of success (statement was reset) or 'ERROR' packet (unrecoverable error/statement not found/etc). */ @@ -2339,7 +2386,7 @@ void mysql_stmt_reset(THD *thd, char *packet) /* Delete a prepared statement from memory. - Note: we don't send any reply to that command. + Note: we don't send any reply to this command. */ void mysql_stmt_close(THD *thd, char *packet) @@ -2350,33 +2397,65 @@ void mysql_stmt_close(THD *thd, char *packet) DBUG_ENTER("mysql_stmt_close"); - statistic_increment(thd->status_var.com_stmt_close, &LOCK_status); if (!(stmt= find_prepared_statement(thd, stmt_id, "mysql_stmt_close"))) DBUG_VOID_RETURN; - /* Statement map deletes statement on erase */ - thd->stmt_map.erase(stmt); + /* + The only way currently a statement can be deallocated when it's + in use is from within Dynamic SQL. + */ + DBUG_ASSERT(! (stmt->flags & Prepared_statement::IS_IN_USE)); + (void) stmt->deallocate(); + DBUG_VOID_RETURN; } /* - Long data in pieces from client + SQLCOM_DEALLOCATE implementation. + + DESCRIPTION + Close an SQL prepared statement. As this can be called from Dynamic + SQL, we should be careful to not close a statement that is currently + being executed. + + RETURN VALUE + none: OK packet is sent in case of success, otherwise an error + message is set in THD +*/ + +void mysql_sql_stmt_close(THD *thd) +{ + Prepared_statement* stmt; + LEX_STRING *name= &thd->lex->prepared_stmt_name; + DBUG_PRINT("info", ("DEALLOCATE PREPARE: %.*s\n", name->length, name->str)); + + if (! (stmt= (Prepared_statement*) thd->stmt_map.find_by_name(name))) + { + my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), + name->length, name->str, "DEALLOCATE PREPARE"); + return; + } + + if (stmt->deallocate() == 0) + send_ok(thd); +} + +/* + Handle long data in pieces from client. SYNOPSIS mysql_stmt_get_longdata() - thd Thread handle - pos String to append - packet_length Length of string + thd Thread handle + packet String to append + packet_length Length of string DESCRIPTION - Get a part of a long data. - To make the protocol efficient, we are not sending any return packages - here. - If something goes wrong, then we will send the error on 'execute' - - We assume that the client takes care of checking that all parts are sent - to the server. (No checking that we get a 'end of column' in the server) + Get a part of a long data. To make the protocol efficient, we are + not sending any return packets here. If something goes wrong, then + we will send the error on 'execute' We assume that the client takes + care of checking that all parts are sent to the server. (No checking + that we get a 'end of column' in the server is performed). */ void mysql_stmt_get_longdata(THD *thd, char *packet, ulong packet_length) @@ -2436,15 +2515,16 @@ void mysql_stmt_get_longdata(THD *thd, char *packet, ulong packet_length) } -Prepared_statement::Prepared_statement(THD *thd_arg) +Prepared_statement::Prepared_statement(THD *thd_arg, Protocol *protocol_arg) :Statement(INITIALIZED, ++thd_arg->statement_id_counter, thd_arg->variables.query_alloc_block_size, thd_arg->variables.query_prealloc_size), thd(thd_arg), - protocol(&thd_arg->protocol_prep), + protocol(protocol_arg), param_array(0), param_count(0), - last_errno(0) + last_errno(0), + flags(IS_IN_USE) { *last_error= '\0'; } @@ -2475,6 +2555,14 @@ void Prepared_statement::setup_set_params() } +/* + DESCRIPTION + Destroy this prepared statement, cleaning up all used memory + and resources. This is called from ::deallocate() to + handle COM_STMT_CLOSE and DEALLOCATE PREPARE or when + THD ends and all prepared statements are freed. +*/ + Prepared_statement::~Prepared_statement() { DBUG_ENTER("Prepared_statement::~Prepared_statement"); @@ -2527,3 +2615,284 @@ void Prepared_statement::close_cursor() reset_stmt_params(this); DBUG_VOID_RETURN; } + + +bool Prepared_statement::set_name(LEX_STRING *name_arg) +{ + name.length= name_arg->length; + name.str= memdup_root(mem_root, (char*) name_arg->str, name_arg->length); + return name.str == 0; +} + +/************************************************************************** + Common parts of mysql_[sql]_stmt_prepare, mysql_[sql]_stmt_execute. + Essentially, these functions do all the magic of preparing/executing + a statement, leaving network communication, input data handling and + global THD state management to the caller. +***************************************************************************/ + +/* + Parse statement text, validate the statement, and prepare it for execution. + + SYNOPSIS + Prepared_statement::prepare() + packet statement text + packet_len + + DESCRIPTION + You should not change global THD state in this function, if at all + possible: it may be called from any context, e.g. when executing + a COM_* command, and SQLCOM_* command, or a stored procedure. + + NOTES + Precondition. + ------------- + The caller must ensure that thd->change_list and thd->free_list + is empty: this function will not back them up but will free + in the end of its execution. + + Postcondition. + -------------- + thd->mem_root contains unused memory allocated during validation. +*/ + +bool Prepared_statement::prepare(const char *packet, uint packet_len) +{ + bool rc; + Statement stmt_backup; + Query_arena *old_stmt_arena; + DBUG_ENTER("Prepared_statement::prepare"); + /* + If this is an SQLCOM_PREPARE, we also increase Com_prepare_sql. + However, it seems handy if com_stmt_prepare is increased always, + no matter what kind of prepare is processed. + */ + statistic_increment(thd->status_var.com_stmt_prepare, &LOCK_status); + + /* + alloc_query() uses thd->memroot && thd->query, so we should call + both of backup_statement() and backup_query_arena() here. + */ + thd->set_n_backup_statement(this, &stmt_backup); + thd->set_n_backup_active_arena(this, &stmt_backup); + + if (alloc_query(thd, packet, packet_len)) + { + thd->restore_backup_statement(this, &stmt_backup); + thd->restore_active_arena(this, &stmt_backup); + DBUG_RETURN(TRUE); + } + + old_stmt_arena= thd->stmt_arena; + thd->stmt_arena= this; + lex_start(thd, (uchar*) thd->query, thd->query_length); + lex->safe_to_cache_query= FALSE; + lex->stmt_prepare_mode= TRUE; + + rc= yyparse((void *)thd) || thd->is_fatal_error || + thd->net.report_error || init_param_array(this); + /* + While doing context analysis of the query (in check_prepared_statement) + we allocate a lot of additional memory: for open tables, JOINs, derived + tables, etc. Let's save a snapshot of current parse tree to the + statement and restore original THD. In cases when some tree + transformation can be reused on execute, we set again thd->mem_root from + stmt->mem_root (see setup_wild for one place where we do that). + */ + thd->restore_active_arena(this, &stmt_backup); + + /* + If called from a stored procedure, ensure that we won't rollback + external changes when cleaning up after validation. + */ + DBUG_ASSERT(thd->change_list.is_empty()); + /* + If the free_list is not empty, we'll wrongly free some externally + allocated items when cleaning up after validation of the prepared + statement. + */ + DBUG_ASSERT(thd->free_list == NULL); + + if (rc == 0) + rc= check_prepared_statement(this, name.str != 0); + + if (rc && thd->lex->sphead) + { + delete thd->lex->sphead; + thd->lex->sphead= NULL; + } + lex_end(lex); + close_thread_tables(thd); + cleanup_stmt_and_thd_after_use(this, thd); + thd->restore_backup_statement(this, &stmt_backup); + thd->stmt_arena= old_stmt_arena; + + if (rc == 0) + { + setup_set_params(); + init_stmt_after_parse(lex); + state= Query_arena::PREPARED; + flags&= ~IS_IN_USE; + } + DBUG_RETURN(rc); +} + +/* + Execute a prepared statement. + + SYNOPSIS + Prepared_statement::execute() + expanded_query A query for binlogging which has all parameter + markers ('?') replaced with their actual values. + open_cursor True if an attempt to open a cursor should be made. + Currenlty used only in the binary protocol. + + DESCRIPTION + You should not change global THD state in this function, if at all + possible: it may be called from any context, e.g. when executing + a COM_* command, and SQLCOM_* command, or a stored procedure. + + NOTES + Preconditions, postconditions. + ------------------------------ + See the comment for Prepared_statement::prepare(). +*/ + +bool Prepared_statement::execute(String *expanded_query, bool open_cursor) +{ + Statement stmt_backup; + Query_arena *old_stmt_arena; + Item *old_free_list; + bool rc= 1; + + statistic_increment(thd->status_var.com_stmt_execute, &LOCK_status); + + /* Check if we got an error when sending long data */ + if (state == Query_arena::ERROR) + { + my_message(last_errno, last_error, MYF(0)); + return 1; + } + if (flags & IS_IN_USE) + { + my_error(ER_PS_NO_RECURSION, MYF(0)); + return 1; + } + /* In case the command has a call to SP which re-uses this statement name */ + flags|= IS_IN_USE; + + if (cursor && cursor->is_open()) + close_cursor(); + + /* + If the free_list is not empty, we'll wrongly free some externally + allocated items when cleaning up after execution of this statement. + */ + DBUG_ASSERT(thd->change_list.is_empty()); + DBUG_ASSERT(thd->free_list == NULL); + if (open_cursor) + { + if (!lex->result || !lex->result->simple_select()) + { + DBUG_PRINT("info",("Cursor asked for not SELECT stmt")); + /* + If lex->result is set in the parser, this is not a SELECT + statement: we can't open a cursor for it. + */ + my_error(ER_SP_BAD_CURSOR_QUERY, MYF(0)); + goto error; + } + + DBUG_PRINT("info",("Using READ_ONLY cursor")); + if (!cursor && !(cursor= new (mem_root) Cursor(thd))) + goto error; + /* If lex->result is set, mysql_execute_command will use it */ + lex->result= &cursor->result; + protocol= &cursor->protocol; + thd->lock_id= &cursor->lock_id; + /* + Currently cursors can be used only from C API, so + we don't have to create an own memory root for them: + the one in THD is clean and can be used. + */ + } + thd->set_n_backup_statement(this, &stmt_backup); + if (expanded_query->length() && + alloc_query(thd, (char*) expanded_query->ptr(), + expanded_query->length()+1)) + { + my_error(ER_OUTOFMEMORY, 0, expanded_query->length()); + goto error; + } + /* + Expanded query is needed for slow logging, so we want thd->query + to point at it even after we restore from backup. This is ok, as + expanded query was allocated in thd->mem_root. + */ + stmt_backup.query= thd->query; + stmt_backup.query_length= thd->query_length; + + /* + At first execution of prepared statement we may perform logical + transformations of the query tree. Such changes should be performed + on the parse tree of current prepared statement and new items should + be allocated in its memory root. Set the appropriate pointer in THD + to the arena of the statement. + */ + old_stmt_arena= thd->stmt_arena; + thd->stmt_arena= this; + reinit_stmt_before_use(thd, lex); + + thd->protocol= protocol; /* activate stmt protocol */ + mysql_execute_command(thd); + thd->protocol= &thd->protocol_simple; /* use normal protocol */ + + if (cursor && cursor->is_open()) + { + /* + It's safer if we grab THD state after mysql_execute_command is + finished and not in Cursor::open(), because currently the call to + Cursor::open is buried deep in JOIN::exec of the top level join. + */ + cursor->init_from_thd(thd); + + if (cursor->close_at_commit) + thd->stmt_map.add_transient_cursor(this); + } + else + { + close_thread_tables(thd); + cleanup_stmt_and_thd_after_use(this, thd); + reset_stmt_params(this); + } + + thd->set_statement(&stmt_backup); + thd->lock_id= &thd->main_lock_id; + thd->stmt_arena= old_stmt_arena; + + if (state == Query_arena::PREPARED) + state= Query_arena::EXECUTED; + + rc= 0; +error: + thd->lock_id= &thd->main_lock_id; + flags&= ~IS_IN_USE; + return rc; +} + + +/* Common part of DEALLOCATE PREPARE and mysql_stmt_close */ + +bool Prepared_statement::deallocate() +{ + /* We account deallocate in the same manner as mysql_stmt_close */ + statistic_increment(thd->status_var.com_stmt_close, &LOCK_status); + if (flags & IS_IN_USE) + { + my_error(ER_PS_NO_RECURSION, MYF(0)); + return TRUE; + } + /* Statement map calls delete stmt on erase */ + thd->stmt_map.erase(this); + return FALSE; +} diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 90066318c60..c9b41cb495b 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -921,16 +921,11 @@ deallocate: { THD *thd=YYTHD; LEX *lex= thd->lex; - if (thd->command == COM_STMT_PREPARE) + if (lex->stmt_prepare_mode) { yyerror(ER(ER_SYNTAX_ERROR)); YYABORT; } - if (lex->sphead) - { - my_error(ER_SP_BADSTATEMENT, MYF(0), "DEALLOCATE"); - YYABORT; - } lex->sql_command= SQLCOM_DEALLOCATE_PREPARE; lex->prepared_stmt_name= $3; }; @@ -946,16 +941,11 @@ prepare: { THD *thd=YYTHD; LEX *lex= thd->lex; - if (thd->command == COM_STMT_PREPARE) + if (lex->stmt_prepare_mode) { yyerror(ER(ER_SYNTAX_ERROR)); YYABORT; } - if (lex->sphead) - { - my_error(ER_SP_BADSTATEMENT, MYF(0), "PREPARE"); - YYABORT; - } lex->sql_command= SQLCOM_PREPARE; lex->prepared_stmt_name= $2; }; @@ -981,16 +971,11 @@ execute: { THD *thd=YYTHD; LEX *lex= thd->lex; - if (thd->command == COM_STMT_PREPARE) + if (lex->stmt_prepare_mode) { yyerror(ER(ER_SYNTAX_ERROR)); YYABORT; } - if (lex->sphead) - { - my_error(ER_SP_BADSTATEMENT, MYF(0), "EXECUTE"); - YYABORT; - } lex->sql_command= SQLCOM_EXECUTE; lex->prepared_stmt_name= $2; } @@ -1324,11 +1309,8 @@ create: YYTHD->client_capabilities |= CLIENT_MULTI_QUERIES; sp->restore_thd_mem_root(YYTHD); - if (sp->m_multi_results) - { - my_error(ER_SP_NO_RETSET, MYF(0), "trigger"); - YYABORT; - } + if (sp->is_not_allowed_in_function("trigger")) + YYABORT; /* We have to do it after parsing trigger body, because some of @@ -1481,11 +1463,9 @@ create_function_tail: LEX *lex= Lex; sp_head *sp= lex->sphead; - if (sp->m_multi_results) - { - my_error(ER_SP_NO_RETSET, MYF(0), "function"); - YYABORT; - } + if (sp->is_not_allowed_in_function("function")) + YYABORT; + if (sp->check_backpatch(YYTHD)) YYABORT; lex->sql_command= SQLCOM_CREATE_SPFUNCTION; @@ -1735,7 +1715,7 @@ sp_decl: sp->add_instr(i); sp->push_backpatch(i, ctx->push_label((char *)"", 0)); - sp->m_in_handler= TRUE; + sp->m_flags|= sp_head::IN_HANDLER; } sp_hcond_list sp_proc_stmt { @@ -1759,7 +1739,7 @@ sp_decl: sp->push_backpatch(i, lex->spcont->last_label()); /* Block end */ } lex->sphead->backpatch(hlab); - sp->m_in_handler= FALSE; + sp->m_flags&= ~sp_head::IN_HANDLER; $$.vars= $$.conds= $$.curs= 0; $$.hndlrs= $6; ctx->add_handlers($6); @@ -1971,12 +1951,7 @@ sp_proc_stmt: LEX *lex= Lex; sp_head *sp= lex->sphead; - if ((lex->sql_command == SQLCOM_SELECT && !lex->result) || - sp_multi_results_command(lex->sql_command)) - { - /* We maybe have one or more SELECT without INTO */ - sp->m_multi_results= TRUE; - } + sp->m_flags|= sp_get_flags_for_command(lex); if (lex->sql_command == SQLCOM_CHANGE_DB) { /* "USE db" doesn't work in a procedure */ my_error(ER_SP_BADSTATEMENT, MYF(0), "USE"); @@ -2026,14 +2001,14 @@ sp_proc_stmt: i= new sp_instr_freturn(sp->instructions(), lex->spcont, $3, sp->m_returns, lex); sp->add_instr(i); - sp->m_has_return= TRUE; + sp->m_flags|= sp_head::HAS_RETURN; } sp->restore_lex(YYTHD); } | IF sp_if END IF {} | CASE_SYM WHEN_SYM { - Lex->sphead->m_simple_case= FALSE; + Lex->sphead->m_flags&= ~sp_head::IN_SIMPLE_CASE; } sp_case END CASE_SYM {} | CASE_SYM @@ -2053,7 +2028,7 @@ sp_proc_stmt: lex->spcont->push_pvar(&dummy, MYSQL_TYPE_STRING, sp_param_in); lex->sphead->add_instr(i); - lex->sphead->m_simple_case= TRUE; + lex->sphead->m_flags|= sp_head::IN_SIMPLE_CASE; lex->sphead->restore_lex(YYTHD); } sp_case END CASE_SYM @@ -2367,7 +2342,7 @@ sp_case: uint ip= sp->instructions(); sp_instr_jump_if_not *i; - if (! sp->m_simple_case) + if (! (sp->m_flags & sp_head::IN_SIMPLE_CASE)) i= new sp_instr_jump_if_not(ip, ctx, $2, lex); else { /* Simple case: = */ From cf4a47c00cd52bab357e4de1bcd1ae30026a45bb Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 3 Sep 2005 12:32:38 +0300 Subject: [PATCH 17/26] row0sel.c: Add more comments to row_search_for_mysql() and remove the ugly double use of the variable rec in that function innobase/row/row0sel.c: Add more comments to row_search_for_mysql() and remove the ugly double use of the variable rec in that function --- innobase/row/row0sel.c | 78 ++++++++++++++++++++++++++---------------- 1 file changed, 49 insertions(+), 29 deletions(-) diff --git a/innobase/row/row0sel.c b/innobase/row/row0sel.c index 0f64126f1fc..de66e6a6219 100644 --- a/innobase/row/row0sel.c +++ b/innobase/row/row0sel.c @@ -2724,7 +2724,9 @@ row_sel_get_clust_rec_for_mysql( if (trx->isolation_level > TRX_ISO_READ_UNCOMMITTED && !lock_clust_rec_cons_read_sees(clust_rec, clust_index, *offsets, trx->read_view)) { - + + /* The following call returns 'offsets' associated with + 'old_vers' */ err = row_sel_build_prev_vers_for_mysql( trx->read_view, clust_index, prebuilt, clust_rec, @@ -3062,7 +3064,7 @@ row_search_for_mysql( dict_index_t* clust_index; que_thr_t* thr; rec_t* rec; - rec_t* index_rec; + rec_t* result_rec; rec_t* clust_rec; rec_t* old_vers; ulint err = DB_SUCCESS; @@ -3548,16 +3550,20 @@ rec_loop: if (comp) { next_offs = rec_get_next_offs(rec, TRUE); if (UNIV_UNLIKELY(next_offs < PAGE_NEW_SUPREMUM)) { + goto wrong_offs; } } else { next_offs = rec_get_next_offs(rec, FALSE); if (UNIV_UNLIKELY(next_offs < PAGE_OLD_SUPREMUM)) { + goto wrong_offs; } } + if (UNIV_UNLIKELY(next_offs >= UNIV_PAGE_SIZE - PAGE_DIR)) { - wrong_offs: + +wrong_offs: if (srv_force_recovery == 0 || moves_up == FALSE) { ut_print_timestamp(stderr); buf_page_print(buf_frame_align(rec)); @@ -3600,6 +3606,9 @@ rec_loop: goto next_rec; } } + /*-------------------------------------------------------------*/ + + /* Calculate the 'offsets' associated with 'rec' */ offsets = rec_get_offsets(rec, index, offsets, ULINT_UNDEFINED, &heap); @@ -3620,8 +3629,6 @@ rec_loop: } } - /*-------------------------------------------------------------*/ - /* Note that we cannot trust the up_match value in the cursor at this place because we can arrive here after moving the cursor! Thus we have to recompare rec and search_tuple to determine if they @@ -3768,6 +3775,8 @@ no_gap_lock: && !lock_clust_rec_cons_read_sees(rec, index, offsets, trx->read_view)) { + /* The following call returns 'offsets' + associated with 'old_vers' */ err = row_sel_build_prev_vers_for_mysql( trx->read_view, clust_index, prebuilt, rec, @@ -3796,8 +3805,6 @@ no_gap_lock: is necessary, because we can only get the undo information via the clustered index record. */ - /* Get the clustered index record if needed */ - index_rec = rec; ut_ad(index != clust_index); goto requires_clust_rec; @@ -3807,7 +3814,7 @@ no_gap_lock: /* NOTE that at this point rec can be an old version of a clustered index record built for a consistent read. We cannot assume after this point that rec is on a buffer pool page. Functions like - page_rec_is_comp() cannot be used then! */ + page_rec_is_comp() cannot be used! */ if (UNIV_UNLIKELY(rec_get_deleted_flag(rec, comp))) { @@ -3829,19 +3836,15 @@ no_gap_lock: goto next_rec; } - /* Get the clustered index record if needed and if we did - not do the search using the clustered index */ - - index_rec = rec; + /* Get the clustered index record if needed, if we did not do the + search using the clustered index. */ if (index != clust_index && prebuilt->need_to_access_clustered) { requires_clust_rec: - /* Before and after this "if" block, "offsets" will be - related to "rec", which may be in a secondary index "index" or - the clustered index ("clust_index"). However, after this - "if" block, "rec" may be pointing to - "clust_rec" of "clust_index". */ + /* We use a 'goto' to the preceding label if a consistent + read of a secondary index record requires us to look up old + versions of the associated clustered index record. */ ut_ad(rec_offs_validate(rec, index, offsets)); @@ -3849,6 +3852,10 @@ requires_clust_rec: clustered index record */ mtr_has_extra_clust_latch = TRUE; + + /* The following call returns 'offsets' associated with + 'clust_rec'. Note that 'clust_rec' can be an old version + built for a consistent read. */ err = row_sel_get_clust_rec_for_mysql(prebuilt, index, rec, thr, &clust_rec, @@ -3885,17 +3892,26 @@ requires_clust_rec: } if (prebuilt->need_to_access_clustered) { - rec = clust_rec; + + result_rec = clust_rec; + ut_ad(rec_offs_validate(rec, clust_index, offsets)); } else { + /* We used 'offsets' for the clust rec, recalculate + them for 'rec' */ offsets = rec_get_offsets(rec, index, offsets, ULINT_UNDEFINED, &heap); + result_rec = rec; } + } else { + result_rec = rec; } - /* We found a qualifying row */ - ut_ad(rec_offs_validate(rec, - rec == clust_rec ? clust_index : index, + /* We found a qualifying record 'result_rec'. At this point, + 'offsets' are associated with 'result_rec'. */ + + ut_ad(rec_offs_validate(result_rec, + result_rec != rec ? clust_index : index, offsets)); if ((match_mode == ROW_SEL_EXACT @@ -3916,8 +3932,8 @@ requires_clust_rec: not cache rows because there the cursor is a scrollable cursor. */ - row_sel_push_cache_row_for_mysql(prebuilt, rec, offsets); - + row_sel_push_cache_row_for_mysql(prebuilt, result_rec, + offsets); if (prebuilt->n_fetch_cached == MYSQL_FETCH_CACHE_SIZE) { goto got_row; @@ -3926,13 +3942,14 @@ requires_clust_rec: goto next_rec; } else { if (prebuilt->template_type == ROW_MYSQL_DUMMY_TEMPLATE) { - memcpy(buf + 4, rec - rec_offs_extra_size(offsets), + memcpy(buf + 4, result_rec + - rec_offs_extra_size(offsets), rec_offs_size(offsets)); mach_write_to_4(buf, rec_offs_extra_size(offsets) + 4); } else { if (!row_sel_store_mysql_rec(buf, prebuilt, - rec, offsets)) { + result_rec, offsets)) { err = DB_TOO_BIG_RECORD; goto lock_wait_or_error; @@ -3940,15 +3957,18 @@ requires_clust_rec: } if (prebuilt->clust_index_was_generated) { - if (rec != index_rec) { + if (result_rec != rec) { offsets = rec_get_offsets( - index_rec, index, offsets, + rec, index, offsets, ULINT_UNDEFINED, &heap); } - row_sel_store_row_id_to_prebuilt(prebuilt, index_rec, + row_sel_store_row_id_to_prebuilt(prebuilt, rec, index, offsets); } } + + /* From this point on, 'offsets' are invalid. */ + got_row: /* We have an optimization to save CPU time: if this is a consistent read on a unique condition on the clustered index, then we do not @@ -3999,7 +4019,7 @@ next_rec: if (moves_up) { if (UNIV_UNLIKELY(!btr_pcur_move_to_next(pcur, &mtr))) { - not_moved: +not_moved: btr_pcur_store_position(pcur, &mtr); if (match_mode != 0) { From 80ea9db87bdef050f319a9a30e8a213bb099b451 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 3 Sep 2005 12:48:41 +0300 Subject: [PATCH 18/26] row0sel.c: Fix a wrong debug assert in the previous patch innobase/row/row0sel.c: Fix a wrong debug assert in the previous patch --- innobase/row/row0sel.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/innobase/row/row0sel.c b/innobase/row/row0sel.c index de66e6a6219..1b66f14f5d7 100644 --- a/innobase/row/row0sel.c +++ b/innobase/row/row0sel.c @@ -3818,9 +3818,7 @@ no_gap_lock: if (UNIV_UNLIKELY(rec_get_deleted_flag(rec, comp))) { - /* The record is delete-marked: we can skip it if this is - not a consistent read which might see an earlier version - of a non-clustered index record */ + /* The record is delete-marked: we can skip it */ if (srv_locks_unsafe_for_binlog && prebuilt->select_lock_type != LOCK_NONE) { @@ -3895,7 +3893,8 @@ requires_clust_rec: result_rec = clust_rec; - ut_ad(rec_offs_validate(rec, clust_index, offsets)); + ut_ad(rec_offs_validate(result_rec, clust_index, + offsets)); } else { /* We used 'offsets' for the clust rec, recalculate them for 'rec' */ From 8b2b0e2418b2edb1f4732eea4848c5807ef799f9 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 3 Sep 2005 19:15:37 +0300 Subject: [PATCH 19/26] univ.i, page0page.ic: Add a sanity check to page_rec_is_comp; a new univ.i macro UNIV_RELEASE_NOT_YET_STABLE controls such sanity checks that we will remove after the release is stable enough innobase/include/page0page.ic: Add a sanity check to page_rec_is_comp; a new univ.i macro UNIV_RELEASE_NOT_YET_STABLE controls such sanity checks that we will remove after the release is stable enough innobase/include/univ.i: Add a sanity check to page_rec_is_comp; a new univ.i macro UNIV_RELEASE_NOT_YET_STABLE controls such sanity checks that we will remove after the release is stable enough --- innobase/include/page0page.ic | 13 +++++++++++++ innobase/include/univ.i | 4 ++++ 2 files changed, 17 insertions(+) diff --git a/innobase/include/page0page.ic b/innobase/include/page0page.ic index fd5281fdbec..655ff245aa8 100644 --- a/innobase/include/page0page.ic +++ b/innobase/include/page0page.ic @@ -175,6 +175,19 @@ page_rec_is_comp( /* out: nonzero if in compact format */ const rec_t* rec) /* in: record */ { +#ifdef UNIV_RELEASE_NOT_YET_STABLE + if (UNIV_UNLIKELY((ulint)rec < (ulint)(buf_pool->frame_zero)) + || UNIV_UNLIKELY((ulint)rec >= (ulint)(buf_pool->high_end))) { + + ut_print_timestamp(stderr); + fprintf(stderr, +"InnoDB: Error: trying to read a stray page rec %p\n" +"InnoDB: buf pool start is at %p, end at %p\n", + rec, buf_pool->frame_zero, + buf_pool->high_end); + ut_error; + } +#endif return(page_is_comp(ut_align_down((rec_t*) rec, UNIV_PAGE_SIZE))); } diff --git a/innobase/include/univ.i b/innobase/include/univ.i index 6849dcd9c51..15650f22ed8 100644 --- a/innobase/include/univ.i +++ b/innobase/include/univ.i @@ -80,6 +80,10 @@ memory is read outside the allocated blocks. */ /* Make a non-inline debug version */ +/* You can remove this define when the release is stable. This define adds +some consistency checks to code. They use a little CPU time. */ +#define UNIV_RELEASE_NOT_YET_STABLE + /* #define UNIV_DEBUG #define UNIV_MEM_DEBUG From 53379f982d30d7f2f21077e341f5eacdedf854ef Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 4 Sep 2005 12:04:21 +0300 Subject: [PATCH 20/26] ha_innodb.cc: Fix bug #12973 : set the table handle field auto_increment_value if ::info() is called with the flag HA_STATUS_AUTO sql/ha_innodb.cc: Fix bug #12973 : set the table handle field auto_increment_value if ::info() is called with the flag HA_STATUS_AUTO --- sql/ha_innodb.cc | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc index 404ac95b81a..df21d82bb77 100644 --- a/sql/ha_innodb.cc +++ b/sql/ha_innodb.cc @@ -5537,6 +5537,33 @@ ha_innobase::info( trx_get_error_info(prebuilt->trx)); } + if (flag & HA_STATUS_AUTO && table->found_next_number_field) { + longlong auto_inc; + int ret; + + /* The following function call can the first time fail in + a lock wait timeout error because it reserves the auto-inc + lock on the table. If it fails, then someone has already inited + the auto-inc counter, and the second call is guaranteed to + succeed. */ + + ret = innobase_read_and_init_auto_inc(&auto_inc); + + if (ret != 0) { + ret = innobase_read_and_init_auto_inc(&auto_inc); + + if (ret != 0) { + ut_print_timestamp(stderr); + sql_print_error("Cannot get table %s auto-inc" + "counter value in ::info\n", + ib_table->name); + auto_inc = 0; + } + } + + auto_increment_value = auto_inc; + } + prebuilt->trx->op_info = (char*)""; DBUG_VOID_RETURN; @@ -6845,8 +6872,13 @@ ha_innobase::innobase_read_and_init_auto_inc( goto func_exit; } } else { - /* Initialize to max(col) + 1 */ - auto_inc = (longlong) table->next_number_field-> + /* Initialize to max(col) + 1; we use + 'found_next_number_field' below because MySQL in SHOW TABLE + STATUS does not seem to set 'next_number_field'. The comment + in table.h says that 'next_number_field' is set when it is + 'active'. */ + + auto_inc = (longlong) table->found_next_number_field-> val_int_offset(table->s->rec_buff_length) + 1; } From d6b70bf4aeee0bfbb74a2ae6a3d7e8617531b1b7 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 4 Sep 2005 13:00:03 +0300 Subject: [PATCH 21/26] ha_innodb.cc: Better comments about auto-inc and SHOW TABLE STATUS sql/ha_innodb.cc: Better comments about auto-inc and SHOW TABLE STATUS --- sql/ha_innodb.cc | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc index df21d82bb77..75348d39208 100644 --- a/sql/ha_innodb.cc +++ b/sql/ha_innodb.cc @@ -5543,7 +5543,7 @@ ha_innobase::info( /* The following function call can the first time fail in a lock wait timeout error because it reserves the auto-inc - lock on the table. If it fails, then someone has already inited + lock on the table. If it fails, then someone is already initing the auto-inc counter, and the second call is guaranteed to succeed. */ @@ -6893,9 +6893,11 @@ func_exit: func_exit_early: /* Since MySQL does not seem to call autocommit after SHOW TABLE - STATUS (even if we would register the trx here), we must commit our + STATUS (even if we would register the trx here), we commit our transaction here if it was started here. This is to eliminate a - dangling transaction. */ + dangling transaction. If the user had AUTOCOMMIT=0, then SHOW + TABLE STATUS does leave a dangling transaction if the user does not + himself call COMMIT. */ if (trx_was_not_started) { From 7425899f53d31c637ee12bac68b4a2a7e1d0c74b Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 4 Sep 2005 11:25:13 -0700 Subject: [PATCH 22/26] This patch disables the query cache for the federated engine. sql/ha_federated.h: Fix cache issues --- sql/ha_federated.h | 1 + 1 file changed, 1 insertion(+) diff --git a/sql/ha_federated.h b/sql/ha_federated.h index 58b78ab0dde..f75fa21b1d6 100644 --- a/sql/ha_federated.h +++ b/sql/ha_federated.h @@ -282,6 +282,7 @@ public: HA_CREATE_INFO *create_info); //required ha_rows records_in_range(uint inx, key_range *start_key, key_range *end_key); + uint8 table_cache_type() { return HA_CACHE_TBL_NOCACHE; } THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to, enum thr_lock_type lock_type); //required From c02509b17b7f9249148b9b951c33b6999b948af7 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 5 Sep 2005 01:13:44 +0300 Subject: [PATCH 23/26] row0mysql.c, ha_innodb.cc: Fix bug #12308 : do not roll back the whhole transaction in a lock wait timeout error, just roll back the latest SQL statement; note that the locks set in the latest SQL statements remain, as InnoDB does not know what locks were set in which SQL statement sql/ha_innodb.cc: Fix bug #12308 : do not roll back the whhole transaction in a lock wait timeout error, just roll back the latest SQL statement; note that the locks set in the latest SQL statements remain, as InnoDB does not know what locks were set in which SQL statement innobase/row/row0mysql.c: Fix bug #12308 : do not roll back the whhole transaction in a lock wait timeout error, just roll back the latest SQL statement; note that the locks set in the latest SQL statements remain, as InnoDB does not know what locks were set in which SQL statement --- innobase/row/row0mysql.c | 5 +++-- sql/ha_innodb.cc | 10 +++------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/innobase/row/row0mysql.c b/innobase/row/row0mysql.c index 2ac0824b331..29239210183 100644 --- a/innobase/row/row0mysql.c +++ b/innobase/row/row0mysql.c @@ -513,14 +513,15 @@ handle_new_error: return(TRUE); - } else if (err == DB_DEADLOCK || err == DB_LOCK_WAIT_TIMEOUT + } else if (err == DB_DEADLOCK || err == DB_LOCK_TABLE_FULL) { /* Roll back the whole transaction; this resolution was added to version 3.23.43 */ trx_general_rollback_for_mysql(trx, FALSE, NULL); - } else if (err == DB_OUT_OF_FILE_SPACE) { + } else if (err == DB_OUT_OF_FILE_SPACE + || err == DB_LOCK_WAIT_TIMEOUT) { if (savept) { /* Roll back the latest, possibly incomplete insertion or update */ diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc index 75348d39208..aa53b69a617 100644 --- a/sql/ha_innodb.cc +++ b/sql/ha_innodb.cc @@ -463,13 +463,9 @@ convert_error_code_to_mysql( } else if (error == (int) DB_LOCK_WAIT_TIMEOUT) { - /* Since we rolled back the whole transaction, we must - tell it also to MySQL so that MySQL knows to empty the - cached binlog for this transaction */ - - if (thd) { - ha_rollback(thd); - } + /* Starting from 5.0.13, we let MySQL just roll back the + latest SQL statement in a lock wait timeout. Previously, we + rolled back the whole transaction. */ return(HA_ERR_LOCK_WAIT_TIMEOUT); From 0ef858082027a636910f27492ff6b2481862ddd6 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 5 Sep 2005 09:13:28 +0200 Subject: [PATCH 24/26] fixed small bug in ndb redolog printer + added option to dumpe rest of page after exnd of data ndb/src/kernel/blocks/dblqh/redoLogReader/records.cpp: fixed small bug in ndb redolog printer ndb/src/kernel/blocks/dblqh/redoLogReader/records.hpp: fixed small bug in ndb redolog printer --- .../blocks/dblqh/redoLogReader/records.cpp | 4 ++- .../blocks/dblqh/redoLogReader/records.hpp | 2 +- .../dblqh/redoLogReader/redoLogFileReader.cpp | 25 ++++++++++++++----- 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/ndb/src/kernel/blocks/dblqh/redoLogReader/records.cpp b/ndb/src/kernel/blocks/dblqh/redoLogReader/records.cpp index ba6d65ca838..b7e2ab072b5 100644 --- a/ndb/src/kernel/blocks/dblqh/redoLogReader/records.cpp +++ b/ndb/src/kernel/blocks/dblqh/redoLogReader/records.cpp @@ -134,7 +134,9 @@ bool PrepareOperationRecord::check() { return true; } -Uint32 PrepareOperationRecord::getLogRecordSize() { +Uint32 PrepareOperationRecord::getLogRecordSize(Uint32 wordsRead) { + if (wordsRead < 2) + return 2; // make sure we read more return m_logRecordSize; } diff --git a/ndb/src/kernel/blocks/dblqh/redoLogReader/records.hpp b/ndb/src/kernel/blocks/dblqh/redoLogReader/records.hpp index 11b8dc4a6fa..b2da7427f4e 100644 --- a/ndb/src/kernel/blocks/dblqh/redoLogReader/records.hpp +++ b/ndb/src/kernel/blocks/dblqh/redoLogReader/records.hpp @@ -83,7 +83,7 @@ class PrepareOperationRecord { friend NdbOut& operator<<(NdbOut&, const PrepareOperationRecord&); public: bool check(); - Uint32 getLogRecordSize(); + Uint32 getLogRecordSize(Uint32 wordsRead); protected: Uint32 m_recordType; diff --git a/ndb/src/kernel/blocks/dblqh/redoLogReader/redoLogFileReader.cpp b/ndb/src/kernel/blocks/dblqh/redoLogReader/redoLogFileReader.cpp index aa8b1d25e4e..751d27db74e 100644 --- a/ndb/src/kernel/blocks/dblqh/redoLogReader/redoLogFileReader.cpp +++ b/ndb/src/kernel/blocks/dblqh/redoLogReader/redoLogFileReader.cpp @@ -41,6 +41,7 @@ void doExit(); FILE * f= 0; char fileName[256]; +bool theDumpFlag = false; bool thePrintFlag = true; bool theCheckFlag = true; bool onlyPageHeaders = false; @@ -208,7 +209,7 @@ NDB_COMMAND(redoLogFileReader, "redoLogFileReader", "redoLogFileReader", "Read case ZPREP_OP_TYPE: poRecord = (PrepareOperationRecord *) redoLogPagePos; - wordIndex += poRecord->getLogRecordSize(); + wordIndex += poRecord->getLogRecordSize(PAGESIZE-wordIndex); if (wordIndex <= PAGESIZE) { if (thePrintFlag) ndbout << (*poRecord); if (theCheckFlag) { @@ -277,10 +278,9 @@ NDB_COMMAND(redoLogFileReader, "redoLogFileReader", "redoLogFileReader", "Read ndbout << " ------ERROR: UNKNOWN RECORD TYPE------" << endl; // Print out remaining data in this page - for (int j = wordIndex; j < PAGESIZE; j++){ - Uint32 unknown = redoLogPage[i*PAGESIZE + j]; - - ndbout_c("%-30d%-12u%-12x", j, unknown, unknown); + for (int k = wordIndex; k < PAGESIZE; k++){ + Uint32 unknown = redoLogPage[i*PAGESIZE + k]; + ndbout_c("%-30d%-12u%-12x", k, unknown, unknown); } doExit(); @@ -289,8 +289,19 @@ NDB_COMMAND(redoLogFileReader, "redoLogFileReader", "redoLogFileReader", "Read if (lastPage) + { + if (theDumpFlag) + { + ndbout << " ------PAGE END: DUMPING REST OF PAGE------" << endl; + for (int k = wordIndex > PAGESIZE ? oldWordIndex : wordIndex; + k < PAGESIZE; k++) + { + Uint32 word = redoLogPage[i*PAGESIZE + k]; + ndbout_c("%-30d%-12u%-12x", k, word, word); + } + } break; - + } if (wordIndex > PAGESIZE) { words_from_previous_page = PAGESIZE - oldWordIndex; ndbout << " ----------- Record continues on next page -----------" << endl; @@ -353,6 +364,8 @@ void readArguments(int argc, const char** argv) { if (strcmp(argv[i], "-noprint") == 0) { thePrintFlag = false; + } else if (strcmp(argv[i], "-dump") == 0) { + theDumpFlag = true; } else if (strcmp(argv[i], "-nocheck") == 0) { theCheckFlag = false; } else if (strcmp(argv[i], "-mbyteheaders") == 0) { From 4345853ee29c0faa8cf1cef74ee971e395e7520f Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 5 Sep 2005 09:29:28 +0200 Subject: [PATCH 25/26] added timestamp to shutdown messages in ndbd --- ndb/src/kernel/main.cpp | 2 +- ndb/src/kernel/vm/Emulator.cpp | 26 ++++++++++++++------------ 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/ndb/src/kernel/main.cpp b/ndb/src/kernel/main.cpp index 1e5cd5270e4..1cab3c96d19 100644 --- a/ndb/src/kernel/main.cpp +++ b/ndb/src/kernel/main.cpp @@ -57,7 +57,7 @@ int main(int argc, char** argv) NDB_INIT(argv[0]); // Print to stdout/console g_eventLogger.createConsoleHandler(); - g_eventLogger.setCategory("NDB"); + g_eventLogger.setCategory("ndbd"); g_eventLogger.enable(Logger::LL_ON, Logger::LL_CRITICAL); g_eventLogger.enable(Logger::LL_ON, Logger::LL_ERROR); g_eventLogger.enable(Logger::LL_ON, Logger::LL_WARNING); diff --git a/ndb/src/kernel/vm/Emulator.cpp b/ndb/src/kernel/vm/Emulator.cpp index d6ed6c0dafd..f52233fc276 100644 --- a/ndb/src/kernel/vm/Emulator.cpp +++ b/ndb/src/kernel/vm/Emulator.cpp @@ -30,13 +30,15 @@ #include #include -#include #include #include +#include + extern "C" { extern void (* ndb_new_handler)(); } +extern EventLogger g_eventLogger; /** * Declare the global variables @@ -141,23 +143,23 @@ NdbShutdown(NdbShutdownType type, switch(type){ case NST_Normal: - ndbout << "Shutdown initiated" << endl; + g_eventLogger.info("Shutdown initiated"); break; case NST_Watchdog: - ndbout << "Watchdog " << shutting << " system" << endl; + g_eventLogger.info("Watchdog %s system", shutting); break; case NST_ErrorHandler: - ndbout << "Error handler " << shutting << " system" << endl; + g_eventLogger.info("Error handler %s system", shutting); break; case NST_ErrorHandlerSignal: - ndbout << "Error handler signal " << shutting << " system" << endl; + g_eventLogger.info("Error handler signal %s system", shutting); break; case NST_Restart: - ndbout << "Restarting system" << endl; + g_eventLogger.info("Restarting system"); break; default: - ndbout << "Error handler " << shutting << " system" - << " (unknown type: " << (unsigned)type << ")" << endl; + g_eventLogger.info("Error handler %s system (unknown type: %u)", + shutting, (unsigned)type); type = NST_ErrorHandler; break; } @@ -173,7 +175,7 @@ NdbShutdown(NdbShutdownType type, /** * Very serious, don't attempt to free, just die!! */ - ndbout << "Watchdog shutdown completed - " << exitAbort << endl; + g_eventLogger.info("Watchdog shutdown completed - %s", exitAbort); #if defined VM_TRACE && ( ! ( defined NDB_OSE || defined NDB_SOFTOSE) ) signal(6, SIG_DFL); abort(); @@ -227,7 +229,7 @@ NdbShutdown(NdbShutdownType type, } if(type != NST_Normal && type != NST_Restart){ - ndbout << "Error handler shutdown completed - " << exitAbort << endl; + g_eventLogger.info("Error handler shutdown completed - %s", exitAbort); #if ( defined VM_TRACE || defined ERROR_INSERT ) && ( ! ( defined NDB_OSE || defined NDB_SOFTOSE) ) signal(6, SIG_DFL); abort(); @@ -243,7 +245,7 @@ NdbShutdown(NdbShutdownType type, exit(restartType); } - ndbout << "Shutdown completed - exiting" << endl; + g_eventLogger.info("Shutdown completed - exiting"); } else { /** * Shutdown is already in progress @@ -253,7 +255,7 @@ NdbShutdown(NdbShutdownType type, * If this is the watchdog, kill system the hard way */ if (type== NST_Watchdog){ - ndbout << "Watchdog is killing system the hard way" << endl; + g_eventLogger.info("Watchdog is killing system the hard way"); #if defined VM_TRACE && ( ! ( defined NDB_OSE || defined NDB_SOFTOSE) ) signal(6, SIG_DFL); abort(); From 943f8335f11c30b6188589e484ceacd1b1b786f8 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 5 Sep 2005 16:31:42 +0500 Subject: [PATCH 26/26] a fix (bug #4214: Table corruption with myisampack and large BLOB objects). myisam/mi_check.c: a fix (bug #4214: Table corruption with myisampack and large BLOB objects). - pass version to the save_pack_length(). myisam/mi_packrec.c: a fix (bug #4214: Table corruption with myisampack and large BLOB objects). - code cleanup: read_pack_length() and calc_pack_length() introduced, save_pack_length() modified: now the behavior depends on packing version - save packing version in the share->pack.version - pass it to the read_pack_length() myisam/mi_static.c: a fix (bug #4214: Table corruption with myisampack and large BLOB objects). - packing version set to 2 myisam/myisamdef.h: a fix (bug #4214: Table corruption with myisampack and large BLOB objects). - packing version slot introduced (see MI_PACK) myisam/myisampack.c: a fix (bug #4214: Table corruption with myisampack and large BLOB objects). - code cleanup - pass version to the calc_pack_length() and save_pack_length() --- myisam/mi_check.c | 6 ++- myisam/mi_packrec.c | 113 +++++++++++++++++++++----------------------- myisam/mi_static.c | 2 +- myisam/myisamdef.h | 5 +- myisam/myisampack.c | 25 +++------- 5 files changed, 69 insertions(+), 82 deletions(-) diff --git a/myisam/mi_check.c b/myisam/mi_check.c index 60a2b664c70..711331518a7 100644 --- a/myisam/mi_check.c +++ b/myisam/mi_check.c @@ -3189,9 +3189,11 @@ int sort_write_record(MI_SORT_PARAM *sort_param) break; case COMPRESSED_RECORD: reclength=info->packed_length; - length=save_pack_length(block_buff,reclength); + length= save_pack_length((uint) share->pack.version, block_buff, + reclength); if (info->s->base.blobs) - length+=save_pack_length(block_buff+length,info->blob_length); + length+= save_pack_length((uint) share->pack.version, + block_buff + length, info->blob_length); if (my_b_write(&info->rec_cache,block_buff,length) || my_b_write(&info->rec_cache,(byte*) sort_param->rec_buff,reclength)) { diff --git a/myisam/mi_packrec.c b/myisam/mi_packrec.c index 1a71d43a7f1..322420b71db 100644 --- a/myisam/mi_packrec.c +++ b/myisam/mi_packrec.c @@ -149,11 +149,12 @@ my_bool _mi_read_pack_info(MI_INFO *info, pbool fix_keys) my_errno=HA_ERR_END_OF_FILE; goto err0; } - if (memcmp((byte*) header,(byte*) myisam_pack_file_magic,4)) + if (memcmp((byte*) header, (byte*) myisam_pack_file_magic, 3)) { my_errno=HA_ERR_WRONG_IN_RECORD; goto err0; } + share->pack.version= header[3]; share->pack.header_length= uint4korr(header+4); share->min_pack_length=(uint) uint4korr(header+8); share->max_pack_length=(uint) uint4korr(header+12); @@ -1040,38 +1041,12 @@ uint _mi_pack_get_block_info(MI_INFO *myisam, MI_BLOCK_INFO *info, File file, return BLOCK_FATAL_ERROR; DBUG_DUMP("header",(byte*) header,ref_length); } - if (header[0] < 254) - { - info->rec_len=header[0]; - head_length=1; - } - else if (header[0] == 254) - { - info->rec_len=uint2korr(header+1); - head_length=3; - } - else - { - info->rec_len=uint3korr(header+1); - head_length=4; - } + head_length= read_pack_length((uint) myisam->s->pack.version, header, + &info->rec_len); if (myisam->s->base.blobs) { - if (header[head_length] < 254) - { - info->blob_len=header[head_length]; - head_length++; - } - else if (header[head_length] == 254) - { - info->blob_len=uint2korr(header+head_length+1); - head_length+=3; - } - else - { - info->blob_len=uint3korr(header+head_length+1); - head_length+=4; - } + head_length+= read_pack_length((uint) myisam->s->pack.version, + header + head_length, &info->blob_len); if (!(mi_alloc_rec_buff(myisam,info->rec_len + info->blob_len, &myisam->rec_buff))) return BLOCK_FATAL_ERROR; /* not enough memory */ @@ -1220,34 +1195,12 @@ void _mi_unmap_file(MI_INFO *info) static uchar *_mi_mempack_get_block_info(MI_INFO *myisam,MI_BLOCK_INFO *info, uchar *header) { - if (header[0] < 254) - info->rec_len= *header++; - else if (header[0] == 254) - { - info->rec_len=uint2korr(header+1); - header+=3; - } - else - { - info->rec_len=uint3korr(header+1); - header+=4; - } + header+= read_pack_length((uint) myisam->s->pack.version, header, + &info->rec_len); if (myisam->s->base.blobs) { - if (header[0] < 254) - { - info->blob_len= *header++; - } - else if (header[0] == 254) - { - info->blob_len=uint2korr(header+1); - header+=3; - } - else - { - info->blob_len=uint3korr(header+1); - header+=4; - } + header+= read_pack_length((uint) myisam->s->pack.version, header, + &info->blob_len); /* mi_alloc_rec_buff sets my_errno on error */ if (!(mi_alloc_rec_buff(myisam, info->blob_len, &myisam->rec_buff))) @@ -1319,7 +1272,7 @@ static int _mi_read_rnd_mempack_record(MI_INFO *info, byte *buf, /* Save length of row */ -uint save_pack_length(byte *block_buff,ulong length) +uint save_pack_length(uint version, byte *block_buff, ulong length) { if (length < 254) { @@ -1333,6 +1286,46 @@ uint save_pack_length(byte *block_buff,ulong length) return 3; } *(uchar*) block_buff=255; - int3store(block_buff+1,(ulong) length); - return 4; + if (version == 1) /* old format */ + { + DBUG_ASSERT(length <= 0xFFFFFF); + int3store(block_buff + 1, (ulong) length); + return 4; + } + else + { + int4store(block_buff + 1, (ulong) length); + return 5; + } +} + + +uint read_pack_length(uint version, const uchar *buf, ulong *length) +{ + if (buf[0] < 254) + { + *length= buf[0]; + return 1; + } + else if (buf[0] == 254) + { + *length= uint2korr(buf + 1); + return 3; + } + if (version == 1) /* old format */ + { + *length= uint3korr(buf + 1); + return 4; + } + else + { + *length= uint4korr(buf + 1); + return 5; + } +} + + +uint calc_pack_length(uint version, ulong length) +{ + return (length < 254) ? 1 : (length < 65536) ? 3 : (version == 1) ? 4 : 5; } diff --git a/myisam/mi_static.c b/myisam/mi_static.c index f41aeff8453..9725c120f44 100644 --- a/myisam/mi_static.c +++ b/myisam/mi_static.c @@ -27,7 +27,7 @@ LIST *myisam_open_list=0; uchar NEAR myisam_file_magic[]= { (uchar) 254, (uchar) 254,'\007', '\001', }; uchar NEAR myisam_pack_file_magic[]= -{ (uchar) 254, (uchar) 254,'\010', '\001', }; +{ (uchar) 254, (uchar) 254,'\010', '\002', }; my_string myisam_log_filename=(char*) "myisam.log"; File myisam_log_file= -1; uint myisam_quick_table_bits=9; diff --git a/myisam/myisamdef.h b/myisam/myisamdef.h index a41bcf5449b..15b310e907e 100644 --- a/myisam/myisamdef.h +++ b/myisam/myisamdef.h @@ -149,6 +149,7 @@ typedef struct st_mi_blob /* Info of record */ typedef struct st_mi_isam_pack { ulong header_length; uint ref_length; + uchar version; } MI_PACK; @@ -669,7 +670,9 @@ extern void _myisam_log_record(enum myisam_log_commands command,MI_INFO *info, int result); extern my_bool _mi_memmap_file(MI_INFO *info); extern void _mi_unmap_file(MI_INFO *info); -extern uint save_pack_length(byte *block_buff,ulong length); +extern uint save_pack_length(uint version, byte *block_buff, ulong length); +extern uint read_pack_length(uint version, const uchar *buf, ulong *length); +extern uint calc_pack_length(uint version, ulong length); uint mi_state_info_write(File file, MI_STATE_INFO *state, uint pWrite); char *mi_state_info_read(char *ptr, MI_STATE_INFO *state); diff --git a/myisam/myisampack.c b/myisam/myisampack.c index 405c69544e7..60522c6f364 100644 --- a/myisam/myisampack.c +++ b/myisam/myisampack.c @@ -1666,6 +1666,7 @@ static int compress_isam_file(PACK_MRG_INFO *mrg, HUFF_COUNTS *huff_counts) HUFF_COUNTS *count,*end_count; HUFF_TREE *tree; MI_INFO *isam_file=mrg->file[0]; + uint pack_version= (uint) isam_file->s->pack.version; DBUG_ENTER("compress_isam_file"); if (!(record=(byte*) my_alloca(isam_file->s->base.reclength))) @@ -1693,23 +1694,10 @@ static int compress_isam_file(PACK_MRG_INFO *mrg, HUFF_COUNTS *huff_counts) huff_counts[i].tree->height+huff_counts[i].length_bits; } max_calc_length/=8; - if (max_calc_length < 254) - pack_ref_length=1; - else if (max_calc_length <= 65535) - pack_ref_length=3; - else - pack_ref_length=4; + pack_ref_length= calc_pack_length(pack_version, max_calc_length); record_count=0; - pack_blob_length=0; - if (isam_file->s->base.blobs) - { - if (mrg->max_blob_length < 254) - pack_blob_length=1; - else if (mrg->max_blob_length <= 65535) - pack_blob_length=3; - else - pack_blob_length=4; - } + pack_blob_length= isam_file->s->base.blobs ? + calc_pack_length(pack_version, mrg->max_blob_length) : 0; max_pack_length=pack_ref_length+pack_blob_length; mrg_reset(mrg); @@ -1865,9 +1853,10 @@ static int compress_isam_file(PACK_MRG_INFO *mrg, HUFF_COUNTS *huff_counts) } flush_bits(); length=(ulong) (file_buffer.pos-record_pos)-max_pack_length; - pack_length=save_pack_length(record_pos,length); + pack_length= save_pack_length(pack_version, record_pos, length); if (pack_blob_length) - pack_length+=save_pack_length(record_pos+pack_length,tot_blob_length); + pack_length+= save_pack_length(pack_version, record_pos + pack_length, + tot_blob_length); /* Correct file buffer if the header was smaller */ if (pack_length != max_pack_length)