diff --git a/.bzrignore b/.bzrignore index 10bfea9154d..00d207919a8 100644 --- a/.bzrignore +++ b/.bzrignore @@ -312,6 +312,7 @@ emacs.h extra/charset2html extra/comp_err extra/created_include_files +extra/innochecksum extra/my_print_defaults extra/mysql_install extra/mysql_tzinfo_to_sql diff --git a/Makefile.am b/Makefile.am index 80f95d6afff..d6245ac95d7 100644 --- a/Makefile.am +++ b/Makefile.am @@ -99,12 +99,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 diff --git a/client/mysqltest.c b/client/mysqltest.c index c968fb2a33a..c00ecc3cc75 100644 --- a/client/mysqltest.c +++ b/client/mysqltest.c @@ -1974,14 +1974,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 | CLIENT_REMEMBER_OPTIONS)) { - con_error = 0; + con_error= 0; break; } sleep(CON_RETRY_SLEEP); @@ -1990,7 +1991,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; } @@ -2024,6 +2025,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; /* @@ -2089,7 +2091,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) { @@ -2368,6 +2370,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 ;) @@ -4264,11 +4267,17 @@ 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_DISABLE_PARSING: parsing_disabled++; break; @@ -4284,13 +4293,16 @@ int main(int argc, char **argv) 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/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; +} + diff --git a/include/mysql.h b/include/mysql.h index 2ba41e6d367..c4b4e026e5b 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/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/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/rpl_replicate_do.result b/mysql-test/r/rpl_replicate_do.result index 8bcae3d25ad..d8666080a71 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 1658 # # master-bin.000001 Yes Yes test.t1 0 0 1658 # 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/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/r/variables.result b/mysql-test/r/variables.result index 4983e0ae3e8..a5b76c03b29 100644 --- a/mysql-test/r/variables.result +++ b/mysql-test/r/variables.result @@ -513,6 +513,11 @@ 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; create table t1 (a int); select a into @x from t1; Warnings: diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result index 2b71a58482f..a544fb4b020 100644 --- a/mysql-test/r/view.result +++ b/mysql-test/r/view.result @@ -875,29 +875,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; @@ -907,73 +907,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 @@ -1332,7 +1332,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); @@ -1935,6 +1935,16 @@ 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; create table t1 (s1 int); create view v1 as select var_samp(s1) from t1; show create view v1; diff --git a/mysql-test/t/lowercase_view.test b/mysql-test/t/lowercase_view.test index b39223f71d5..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 1093 +-- error 1443 update v2aA set col1 = (select max(col1) from v1Aa); --- error 1093 +-- error 1443 update v2Aa set col1 = (select max(col1) from t1Aa); -- error 1093 update v2aA set col1 = (select max(col1) from v2Aa); --- error 1093 +-- error 1443 update v2aA,t2Aa set v2Aa.col1 = (select max(col1) from v1aA) where v2aA.col1 = t2aA.col1; --- error 1093 +-- 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 1093 +-- error 1443 update t2Aa,v2Aa set v2aA.col1 = (select max(col1) from v1aA) where v2Aa.col1 = t2aA.col1; --- error 1093 +-- error 1443 update t2Aa,t1Aa set t1aA.col1 = (select max(col1) from v1Aa) where t1Aa.col1 = t2aA.col1; --- error 1093 +-- error 1443 update t2Aa,v1aA set v1Aa.col1 = (select max(col1) from v1aA) where v1Aa.col1 = t2aA.col1; --- error 1093 +-- 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 1093 +-- 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 1093 +-- error 1443 update t1aA,t2Aa set t1Aa.col1 = (select max(col1) from v2aA) where t1aA.col1 = t2aA.col1; --- error 1093 +-- error 1443 update v1aA,t2Aa set v1Aa.col1 = (select max(col1) from v2Aa) where v1aA.col1 = t2aA.col1; --- error 1093 +-- error 1443 update t2Aa,v2aA set v2Aa.col1 = (select max(col1) from v2aA) where v2Aa.col1 = t2aA.col1; --- error 1093 +-- error 1443 update t2Aa,t1Aa set t1aA.col1 = (select max(col1) from v2aA) where t1Aa.col1 = t2aA.col1; --- error 1093 +-- error 1443 update t2Aa,v1Aa set v1aA.col1 = (select max(col1) from v2Aa) where v1Aa.col1 = t2aA.col1; --- error 1093 +-- error 1443 update v3aA set v3Aa.col1 = (select max(col1) from v1aA); --- error 1093 +-- error 1443 update v3aA set v3Aa.col1 = (select max(col1) from t1aA); --- error 1093 +-- 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 1093 +-- error 1443 delete from v2Aa where col1 = (select max(col1) from v1Aa); --- error 1093 +-- 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 1093 +-- error 1443 delete v2Aa from v2aA,t2Aa where (select max(col1) from v1aA) > 0 and v2Aa.col1 = t2aA.col1; --- error 1093 +-- 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 1093 +-- 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 1093 +-- 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 1093 +-- error 1443 delete t1Aa from t1aA,t2Aa where (select max(col1) from v2Aa) > 0 and t1Aa.col1 = t2aA.col1; --- error 1093 +-- error 1443 delete v1Aa from v1aA,t2Aa where (select max(col1) from v2aA) > 0 and v1Aa.col1 = t2aA.col1; --- error 1093 +-- error 1443 insert into v2Aa values ((select max(col1) from v1aA)); --- error 1093 +-- error 1443 insert into t1aA values ((select max(col1) from v1Aa)); --- error 1093 +-- error 1443 insert into v2aA values ((select max(col1) from v1aA)); --- error 1093 +-- error 1443 insert into v2Aa values ((select max(col1) from t1Aa)); -- error 1093 insert into t1aA values ((select max(col1) from t1Aa)); --- error 1093 +-- error 1443 insert into v2aA values ((select max(col1) from t1aA)); -- error 1093 insert into v2Aa values ((select max(col1) from v2aA)); --- error 1093 +-- error 1443 insert into t1Aa values ((select max(col1) from v2Aa)); -- error 1093 insert into v2aA values ((select max(col1) from v2Aa)); --- error 1093 +-- error 1443 insert into v3Aa (col1) values ((select max(col1) from v1Aa)); --- error 1093 +-- error 1443 insert into v3aA (col1) values ((select max(col1) from t1aA)); --- error 1093 +-- 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/rpl_replicate_do.test b/mysql-test/t/rpl_replicate_do.test index ff5af71ea5b..b8559af2394 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 # 8 # 9 # 23 # 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/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/mysql-test/t/variables.test b/mysql-test/t/variables.test index 7ce2cf22db0..372e865467e 100644 --- a/mysql-test/t/variables.test +++ b/mysql-test/t/variables.test @@ -390,6 +390,14 @@ 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/mysql-test/t/view.test b/mysql-test/t/view.test index c4180ab5969..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 1093 +-- error 1443 update v2 set col1 = (select max(col1) from v1); --- error 1093 +-- error 1443 update v2 set col1 = (select max(col1) from t1); -- error 1093 update v2 set col1 = (select max(col1) from v2); --- error 1093 +-- error 1443 update v2,t2 set v2.col1 = (select max(col1) from v1) where v2.col1 = t2.col1; --- error 1093 +-- 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 1093 +-- error 1443 update t2,v2 set v2.col1 = (select max(col1) from v1) where v2.col1 = t2.col1; --- error 1093 +-- error 1443 update t2,t1 set t1.col1 = (select max(col1) from v1) where t1.col1 = t2.col1; --- error 1093 +-- error 1443 update t2,v1 set v1.col1 = (select max(col1) from v1) where v1.col1 = t2.col1; --- error 1093 +-- 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 1093 +-- 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 1093 +-- error 1443 update t1,t2 set t1.col1 = (select max(col1) from v2) where t1.col1 = t2.col1; --- error 1093 +-- error 1443 update v1,t2 set v1.col1 = (select max(col1) from v2) where v1.col1 = t2.col1; --- error 1093 +-- error 1443 update t2,v2 set v2.col1 = (select max(col1) from v2) where v2.col1 = t2.col1; --- error 1093 +-- error 1443 update t2,t1 set t1.col1 = (select max(col1) from v2) where t1.col1 = t2.col1; --- error 1093 +-- error 1443 update t2,v1 set v1.col1 = (select max(col1) from v2) where v1.col1 = t2.col1; --- error 1093 +-- error 1443 update v3 set v3.col1 = (select max(col1) from v1); --- error 1093 +-- error 1443 update v3 set v3.col1 = (select max(col1) from t1); --- error 1093 +-- 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 1093 +-- error 1443 delete from v2 where col1 = (select max(col1) from v1); --- error 1093 +-- 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 1093 +-- error 1443 delete v2 from v2,t2 where (select max(col1) from v1) > 0 and v2.col1 = t2.col1; --- error 1093 +-- 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 1093 +-- 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 1093 +-- 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 1093 +-- error 1443 delete t1 from t1,t2 where (select max(col1) from v2) > 0 and t1.col1 = t2.col1; --- error 1093 +-- error 1443 delete v1 from v1,t2 where (select max(col1) from v2) > 0 and v1.col1 = t2.col1; --- error 1093 +-- error 1443 insert into v2 values ((select max(col1) from v1)); --- error 1093 +-- error 1443 insert into t1 values ((select max(col1) from v1)); --- error 1093 +-- error 1443 insert into v2 values ((select max(col1) from v1)); --- error 1093 +-- error 1443 insert into v2 values ((select max(col1) from t1)); -- error 1093 insert into t1 values ((select max(col1) from t1)); --- error 1093 +-- error 1443 insert into v2 values ((select max(col1) from t1)); -- error 1093 insert into v2 values ((select max(col1) from v2)); --- error 1093 +-- error 1443 insert into t1 values ((select max(col1) from v2)); -- error 1093 insert into v2 values ((select max(col1) from v2)); --- error 1093 +-- error 1443 insert into v3 (col1) values ((select max(col1) from v1)); --- error 1093 +-- error 1443 insert into v3 (col1) values ((select max(col1) from t1)); --- error 1093 +-- error 1443 insert into v3 (col1) values ((select max(col1) from v2)); #check with TZ tables in list --- error 1093 +-- 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 @@ -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; @@ -1760,6 +1760,20 @@ 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 1443 +update t1 set f60=(select max(f60) from v1); +drop view v1; +drop table t1; + # # Using var_samp with view (BUG#10651) # diff --git a/scripts/mysqld_multi.sh b/scripts/mysqld_multi.sh index 642772bca44..098cd894916 100644 --- a/scripts/mysqld_multi.sh +++ b/scripts/mysqld_multi.sh @@ -412,7 +412,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++) { diff --git a/sql-common/client.c b/sql-common/client.c index 993978f132f..cc70bbc7523 100644 --- a/sql-common/client.c +++ b/sql-common/client.c @@ -2760,6 +2760,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); } diff --git a/sql/ha_federated.h b/sql/ha_federated.h index ecaa59d1268..c94a28219ae 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 diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc index 6811ab0934a..e2cf14bca17 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); @@ -5539,6 +5535,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 is already initing + 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; @@ -6847,8 +6870,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; } @@ -6863,9 +6891,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) { diff --git a/sql/item.cc b/sql/item.cc index 7fdb92214e9..66552fa7b04 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); @@ -4985,10 +4985,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/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..22d9fbbad34 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 @@ -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/item_subselect.cc b/sql/item_subselect.cc index 7e47de494db..aead2d67c2c 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/log_event.cc b/sql/log_event.cc index 0b7d9d81bf1..7ee505939f0 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -1555,6 +1555,16 @@ int Query_log_event::exec_event(struct st_relay_log_info* rli, const char *query 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 (rpl_filter->db_ok(thd->db)) { thd->set_time((time_t)when); @@ -2704,6 +2714,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 (rpl_filter->db_ok(thd->db)) { 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 7cfb3b90c51..8354f3bdc59 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -415,7 +415,6 @@ enum enum_parsing_place struct st_table; class THD; -class Item_arena; /* Struct to handle simple linked lists */ @@ -597,7 +596,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); @@ -876,16 +875,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); @@ -924,6 +924,9 @@ void add_join_on(TABLE_LIST *b,Item *expr); void add_join_natural(TABLE_LIST *a,TABLE_LIST *b,List *using_fields); 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, @@ -1390,8 +1393,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/set_var.cc b/sql/set_var.cc index 7de848098e6..0f1b523529f 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -1730,11 +1730,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; } @@ -2024,7 +2030,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; } diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index ba9005638e1..6c1b7c08a1c 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -5399,6 +5399,10 @@ 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_VIEW_PREVENT_UPDATE + eng "The definition of table '%-.64s' prevents operation %s on table '%-.64s'." +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" ER_PARTITION_REQUIRES_VALUES_ERROR eng "%s PARTITIONING requires definition of VALUES %s for each partition" swe "%s PARTITIONering kräver definition av VALUES %s för varje partition" diff --git a/sql/sp.cc b/sql/sp.cc index 38130d01dab..d635d9ad728 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..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; } @@ -131,12 +162,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 +198,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 +218,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 +241,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 +252,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 +277,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 +307,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); @@ -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); @@ -765,7 +794,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; @@ -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 && @@ -812,7 +841,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 +877,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 +898,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); /* @@ -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)); /* @@ -907,9 +945,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 +962,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 +977,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: @@ -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 @@ -1532,7 +1569,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 +2396,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 +2474,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 +2561,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 +2823,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 +2868,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/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_base.cc b/sql/sql_base.cc index b0bf35ce331..b7831a2a5b5 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -774,6 +774,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]; @@ -1810,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); } @@ -3463,7 +3517,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 @@ -3630,7 +3684,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); } @@ -3688,7 +3742,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)) @@ -3773,7 +3827,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); } @@ -3818,7 +3872,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) @@ -3931,7 +3985,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); } @@ -4030,7 +4084,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++)) { @@ -4058,7 +4112,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) @@ -4080,7 +4134,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); } @@ -4218,9 +4272,7 @@ bool setup_tables(THD *thd, Name_resolution_context *context, { 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; @@ -4258,15 +4310,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); } @@ -4347,7 +4399,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) { @@ -4557,7 +4609,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 @@ -4621,9 +4673,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)) @@ -4633,7 +4683,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 @@ -4648,7 +4698,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..2ff0413e05e 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"; @@ -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; } /* @@ -794,7 +789,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 +1597,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 +1611,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; @@ -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_class.h b/sql/sql_class.h index 2bba37afa04..4f2b3dad5a3 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -735,9 +735,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(); }; @@ -1239,16 +1237,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 @@ -1476,7 +1474,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) { @@ -1515,17 +1513,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; } @@ -1533,7 +1530,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; } @@ -1565,11 +1562,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_delete.cc b/sql/sql_delete.cc index 5accc9607fe..00d82bcdfda 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -340,10 +340,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); @@ -426,11 +429,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 869ae688e34..77741d0ed3c 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -930,9 +930,10 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, if (!select_insert) { Item *fake_conds= 0; - if (unique_table(table_list, table_list->next_global)) + TABLE_LIST *duplicate; + if ((duplicate= unique_table(table_list, table_list->next_global))) { - my_error(ER_UPDATE_TABLE_USED, MYF(0), table_list->table_name); + update_non_unique_table_error(table_list, "INSERT", duplicate); DBUG_RETURN(TRUE); } select_lex->fix_prepare_information(thd, &fake_conds); @@ -1207,9 +1208,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) diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 623dd48b39d..3f1e237b653 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; @@ -569,8 +571,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); @@ -982,7 +983,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; @@ -1505,7 +1506,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 + @@ -1827,7 +1828,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() : @@ -2039,7 +2040,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_lex.h b/sql/sql_lex.h index 5f384f8d144..e5567d0d7c4 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -824,6 +824,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 9db651bb507..4b1686f0020 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -175,6 +175,9 @@ static bool begin_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 (rpl_filter->is_on() && tables && @@ -1645,7 +1648,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: @@ -1665,6 +1668,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) @@ -2221,7 +2228,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 */ @@ -2230,7 +2237,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]))) { @@ -2251,11 +2258,25 @@ 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; } +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 @@ -2339,16 +2360,22 @@ 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 -- 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). */ - 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, all_tables)) { /* we warn the slave SQL thread */ my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0)); + reset_one_shot_variables(thd); DBUG_RETURN(0); } #ifndef TO_BE_DELETED @@ -2445,112 +2472,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: @@ -2859,12 +2791,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) @@ -2874,9 +2809,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; } @@ -3576,6 +3512,7 @@ end_with_restore_list: !rpl_filter->db_ok_with_wild_table(lex->name))) { my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0)); + reset_one_shot_variables(thd); break; } #endif @@ -3610,6 +3547,7 @@ end_with_restore_list: !rpl_filter->db_ok_with_wild_table(lex->name))) { my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0)); + reset_one_shot_variables(thd); break; } #endif @@ -3650,6 +3588,7 @@ end_with_restore_list: !rpl_filter->db_ok_with_wild_table(lex->name))) { my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0)); + reset_one_shot_variables(thd); break; } #endif @@ -4100,7 +4039,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; @@ -4189,15 +4128,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; @@ -4241,7 +4196,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 @@ -4706,30 +4661,19 @@ end_with_restore_list: } thd->proc_info="query end"; /* Two binlog-related cleanups: */ - 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); + /* The return value for ROW_COUNT() is "implementation dependent" if @@ -5262,7 +5206,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) { @@ -6068,7 +6012,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; @@ -7288,7 +7232,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 082ae783ca8..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_item_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); - /* 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->current_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; - 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); - lex->safe_to_cache_query= 0; - /* - 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_backup_item_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->current_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->current_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->current_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->current_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->current_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) @@ -2263,7 +2309,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 +2337,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; } @@ -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_select.cc b/sql/sql_select.cc index e31ab59694c..2d62abf436f 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); @@ -1756,7 +1756,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, @@ -1883,7 +1883,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); @@ -1902,7 +1902,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 a5dbd8e3d13..934f8bc9952 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -3632,7 +3632,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_update.cc b/sql/sql_update.cc index 993b1a409ba..61185c5d710 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -671,10 +671,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); @@ -884,7 +888,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); @@ -893,11 +897,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 8269c16916a..eb5d64e0fe0 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) @@ -774,9 +774,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; @@ -997,13 +995,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; @@ -1145,8 +1143,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/sql_yacc.yy b/sql/sql_yacc.yy index 27f19d80325..41949c37e77 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -937,16 +937,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; }; @@ -962,16 +957,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; }; @@ -997,16 +987,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; } @@ -1340,11 +1325,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 @@ -1497,11 +1479,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; @@ -1751,7 +1731,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 { @@ -1775,7 +1755,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); @@ -1987,12 +1967,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"); @@ -2042,14 +2017,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 @@ -2069,7 +2044,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 @@ -2383,7 +2358,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: = */ diff --git a/sql/table.cc b/sql/table.cc index 810ade44b6c..ce22a09cf6c 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -1815,7 +1815,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))))) { @@ -1891,8 +1891,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) @@ -1912,7 +1912,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; } } @@ -2091,7 +2091,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 b6b3821273d..09b64398fd9 100644 --- a/sql/table.h +++ b/sql/table.h @@ -609,6 +609,8 @@ typedef struct st_table_list st_table_list *first_leaf_for_name_resolution(); st_table_list *last_leaf_for_name_resolution(); bool is_leaf_for_name_resolution(); + inline st_table_list *top_table() + { return belong_to_view ? belong_to_view : this; } inline bool prepare_check_option(THD *thd) { bool res= FALSE; diff --git a/storage/innobase/include/page0page.ic b/storage/innobase/include/page0page.ic index fd5281fdbec..655ff245aa8 100644 --- a/storage/innobase/include/page0page.ic +++ b/storage/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/storage/innobase/include/univ.i b/storage/innobase/include/univ.i index 6849dcd9c51..15650f22ed8 100644 --- a/storage/innobase/include/univ.i +++ b/storage/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 diff --git a/storage/innobase/row/row0mysql.c b/storage/innobase/row/row0mysql.c index 2ac0824b331..29239210183 100644 --- a/storage/innobase/row/row0mysql.c +++ b/storage/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/storage/innobase/row/row0sel.c b/storage/innobase/row/row0sel.c index a77010d939b..1b66f14f5d7 100644 --- a/storage/innobase/row/row0sel.c +++ b/storage/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, @@ -3055,13 +3057,14 @@ 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; 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; @@ -3491,7 +3494,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,19 +3547,23 @@ 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; } } 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)); @@ -3599,6 +3606,9 @@ rec_loop: goto next_rec; } } + /*-------------------------------------------------------------*/ + + /* Calculate the 'offsets' associated with 'rec' */ offsets = rec_get_offsets(rec, index, offsets, ULINT_UNDEFINED, &heap); @@ -3619,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 @@ -3711,7 +3719,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 { @@ -3767,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, @@ -3795,19 +3805,20 @@ 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; } } - 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! */ - /* 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 */ + if (UNIV_UNLIKELY(rec_get_deleted_flag(rec, comp))) { + + /* The record is delete-marked: we can skip it */ if (srv_locks_unsafe_for_binlog && prebuilt->select_lock_type != LOCK_NONE) { @@ -3823,25 +3834,26 @@ 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)); /* It was a non-clustered index and we must fetch also the 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, @@ -3858,8 +3870,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 */ @@ -3879,17 +3890,27 @@ requires_clust_rec: } if (prebuilt->need_to_access_clustered) { - rec = clust_rec; - ut_ad(rec_offs_validate(rec, clust_index, offsets)); + + result_rec = clust_rec; + + ut_ad(rec_offs_validate(result_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 @@ -3910,8 +3931,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; @@ -3920,13 +3941,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; @@ -3934,15 +3956,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 @@ -3993,7 +4018,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) { diff --git a/storage/myisam/mi_check.c b/storage/myisam/mi_check.c index ffb7cdd503f..ee64f9b9979 100644 --- a/storage/myisam/mi_check.c +++ b/storage/myisam/mi_check.c @@ -3194,9 +3194,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/storage/myisam/mi_packrec.c b/storage/myisam/mi_packrec.c index c251e4dda4a..e242e9d506d 100644 --- a/storage/myisam/mi_packrec.c +++ b/storage/myisam/mi_packrec.c @@ -151,11 +151,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); @@ -1070,38 +1071,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 */ @@ -1251,34 +1226,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))) @@ -1350,7 +1303,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) { @@ -1364,6 +1317,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/storage/myisam/mi_static.c b/storage/myisam/mi_static.c index 4c9d814f7d6..fc585eb5543 100644 --- a/storage/myisam/mi_static.c +++ b/storage/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/storage/myisam/myisamdef.h b/storage/myisam/myisamdef.h index 74463ec065a..82f7fd7360e 100644 --- a/storage/myisam/myisamdef.h +++ b/storage/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; @@ -673,7 +674,9 @@ extern void _myisam_log_record(enum myisam_log_commands command,MI_INFO *info, extern void mi_report_error(int errcode, const char *file_name); 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(uchar *ptr, MI_STATE_INFO *state); diff --git a/storage/myisam/myisampack.c b/storage/myisam/myisampack.c index b8f21392f8a..3b091cd6ea2 100644 --- a/storage/myisam/myisampack.c +++ b/storage/myisam/myisampack.c @@ -2417,6 +2417,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"); /* Allocate a buffer for the records (excluding blobs). */ @@ -2455,25 +2456,11 @@ 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= (max_calc_length + 7) / 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; /* 'max_blob_length' is the max length of all blobs of a record. */ - 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; DBUG_PRINT("fields", ("===")); @@ -2746,9 +2733,10 @@ static int compress_isam_file(PACK_MRG_INFO *mrg, HUFF_COUNTS *huff_counts) } flush_bits(); length=(ulong) ((byte*) 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); DBUG_PRINT("fields", ("record: %lu length: %lu blob-length: %lu " "length-bytes: %lu", (ulong) record_count, length, tot_blob_length, pack_length)); diff --git a/storage/ndb/include/kernel/signaldata/SignalData.hpp b/storage/ndb/include/kernel/signaldata/SignalData.hpp index f825b0feb7b..0591a85d6e6 100644 --- a/storage/ndb/include/kernel/signaldata/SignalData.hpp +++ b/storage/ndb/include/kernel/signaldata/SignalData.hpp @@ -222,5 +222,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/storage/ndb/include/kernel/signaldata/StartFragReq.hpp b/storage/ndb/include/kernel/signaldata/StartFragReq.hpp index ec05c1ee366..ab17a147195 100644 --- a/storage/ndb/include/kernel/signaldata/StartFragReq.hpp +++ b/storage/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/storage/ndb/src/common/debugger/EventLogger.cpp b/storage/ndb/src/common/debugger/EventLogger.cpp index 5a534b36b59..d18b0feb1ad 100644 --- a/storage/ndb/src/common/debugger/EventLogger.cpp +++ b/storage/ndb/src/common/debugger/EventLogger.cpp @@ -33,7 +33,6 @@ EventLoggerBase::~EventLoggerBase() } - #define QQQQ char *m_text, size_t m_text_len, const Uint32* theData void getTextConnected(QQQQ) { @@ -434,10 +433,12 @@ void getTextNR_CopyFragsCompleted(QQQQ) { void getTextLCPFragmentCompleted(QQQQ) { BaseString::snprintf(m_text, m_text_len, "Table ID = %u, fragment ID = %u has completed LCP " - "on Node %u", + "on Node %u maxGciStarted: %d maxGciCompleted: %d", theData[2], theData[3], - theData[1]); + theData[1], + theData[4], + theData[5]); } void getTextTransReportCounters(QQQQ) { // ------------------------------------------------------------------- diff --git a/storage/ndb/src/common/debugger/signaldata/SignalDataPrint.cpp b/storage/ndb/src/common/debugger/signaldata/SignalDataPrint.cpp index ab23c04bffa..34cae9f618f 100644 --- a/storage/ndb/src/common/debugger/signaldata/SignalDataPrint.cpp +++ b/storage/ndb/src/common/debugger/signaldata/SignalDataPrint.cpp @@ -198,6 +198,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/storage/ndb/src/common/debugger/signaldata/StartRec.cpp b/storage/ndb/src/common/debugger/signaldata/StartRec.cpp index 482e3cb0728..54830e533c5 100644 --- a/storage/ndb/src/common/debugger/signaldata/StartRec.cpp +++ b/storage/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/storage/ndb/src/kernel/blocks/dbdih/DbdihMain.cpp b/storage/ndb/src/kernel/blocks/dbdih/DbdihMain.cpp index 57af5ebec72..21d6a1182be 100644 --- a/storage/ndb/src/kernel/blocks/dbdih/DbdihMain.cpp +++ b/storage/ndb/src/kernel/blocks/dbdih/DbdihMain.cpp @@ -9716,6 +9716,9 @@ void Dbdih::execLCP_FRAG_REP(Signal* signal) } bool tableDone = reportLcpCompletion(lcpReport); + + Uint32 started = lcpReport->maxGciStarted; + Uint32 completed = lcpReport->maxGciCompleted; if(tableDone){ jam(); @@ -9749,7 +9752,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; @@ -10946,7 +10951,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]; @@ -10954,7 +10961,6 @@ void Dbdih::findMinGci(ReplicaRecordPtr fmgReplicaPtr, return; } else { jam(); - ndbrequire(fmgReplicaPtr.p->lcpStatus[lcpNo] == ZINVALID); if (fmgReplicaPtr.p->createGci[0] == fmgReplicaPtr.p->initialGci) { jam(); /*------------------------------------------------------------------- diff --git a/storage/ndb/src/kernel/blocks/dblqh/redoLogReader/records.cpp b/storage/ndb/src/kernel/blocks/dblqh/redoLogReader/records.cpp index ba6d65ca838..b7e2ab072b5 100644 --- a/storage/ndb/src/kernel/blocks/dblqh/redoLogReader/records.cpp +++ b/storage/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/storage/ndb/src/kernel/blocks/dblqh/redoLogReader/records.hpp b/storage/ndb/src/kernel/blocks/dblqh/redoLogReader/records.hpp index 11b8dc4a6fa..b2da7427f4e 100644 --- a/storage/ndb/src/kernel/blocks/dblqh/redoLogReader/records.hpp +++ b/storage/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/storage/ndb/src/kernel/blocks/dblqh/redoLogReader/redoLogFileReader.cpp b/storage/ndb/src/kernel/blocks/dblqh/redoLogReader/redoLogFileReader.cpp index aa8b1d25e4e..751d27db74e 100644 --- a/storage/ndb/src/kernel/blocks/dblqh/redoLogReader/redoLogFileReader.cpp +++ b/storage/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) { diff --git a/storage/ndb/src/kernel/blocks/dbtc/DbtcMain.cpp b/storage/ndb/src/kernel/blocks/dbtc/DbtcMain.cpp index 8c32c87e05e..27087b4c012 100644 --- a/storage/ndb/src/kernel/blocks/dbtc/DbtcMain.cpp +++ b/storage/ndb/src/kernel/blocks/dbtc/DbtcMain.cpp @@ -4454,7 +4454,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); diff --git a/storage/ndb/src/kernel/main.cpp b/storage/ndb/src/kernel/main.cpp index f679646e14a..b35929247d9 100644 --- a/storage/ndb/src/kernel/main.cpp +++ b/storage/ndb/src/kernel/main.cpp @@ -60,7 +60,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/storage/ndb/src/kernel/vm/Emulator.cpp b/storage/ndb/src/kernel/vm/Emulator.cpp index d6ed6c0dafd..f52233fc276 100644 --- a/storage/ndb/src/kernel/vm/Emulator.cpp +++ b/storage/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();