diff options
author | Michael Widenius <monty@askmonty.org> | 2013-06-15 18:32:08 +0300 |
---|---|---|
committer | Michael Widenius <monty@askmonty.org> | 2013-06-15 18:32:08 +0300 |
commit | 5f1f2fc0e443f098af24d21f7d1ec1a8166a4030 (patch) | |
tree | 7b870d0c390c05d6629f4813966e740ea073fcef | |
parent | 3143ad589a24ac7581e2195ba0dc13576cb3c9da (diff) | |
download | mariadb-git-5f1f2fc0e443f098af24d21f7d1ec1a8166a4030.tar.gz |
Applied all changes from Igor and Sanja
162 files changed, 11658 insertions, 6252 deletions
@@ -32,12 +32,10 @@ Short time TODO: - add support for host_error() - Enable performance_schema.host_cache in scripts/mysql_system_tables.sql -- Add full support for automatic timestamp. - (remove timestamp handling from ha_write()) - - Timour is working on this - - Add Sys_my_bind_addr(); Needed for perfschema +- Add support for format_section_buff in unireg.cc and table.cc + - mysql_socket_shutdown() was removed from vio/viosocket.cc. It was replaced with inline function in include/mysql/psi/mysql_socket.h but this doesn't call DisconnectEx(). We should check if we need to @@ -60,4 +58,3 @@ Sergei's notes: rpl_slave.cc XXX in mysql_client_test net_serv.cc - diff --git a/include/m_ctype.h b/include/m_ctype.h index 5d2a6f80b75..855212957bc 100644 --- a/include/m_ctype.h +++ b/include/m_ctype.h @@ -138,6 +138,38 @@ extern MY_UNI_CTYPE my_uni_ctype[256]; #define MY_REPERTOIRE_EXTENDED 2 /* Extended characters: U+0080..U+FFFF */ #define MY_REPERTOIRE_UNICODE30 3 /* ASCII | EXTENDED: U+0000..U+FFFF */ +/* Flags for strxfrm */ +#define MY_STRXFRM_LEVEL1 0x00000001 /* for primary weights */ +#define MY_STRXFRM_LEVEL2 0x00000002 /* for secondary weights */ +#define MY_STRXFRM_LEVEL3 0x00000004 /* for tertiary weights */ +#define MY_STRXFRM_LEVEL4 0x00000008 /* fourth level weights */ +#define MY_STRXFRM_LEVEL5 0x00000010 /* fifth level weights */ +#define MY_STRXFRM_LEVEL6 0x00000020 /* sixth level weights */ +#define MY_STRXFRM_LEVEL_ALL 0x0000003F /* Bit OR for the above six */ +#define MY_STRXFRM_NLEVELS 6 /* Number of possible levels*/ + +#define MY_STRXFRM_PAD_WITH_SPACE 0x00000040 /* if pad result with spaces */ +#define MY_STRXFRM_PAD_TO_MAXLEN 0x00000080 /* if pad tail(for filesort) */ + +#define MY_STRXFRM_DESC_LEVEL1 0x00000100 /* if desc order for level1 */ +#define MY_STRXFRM_DESC_LEVEL2 0x00000200 /* if desc order for level2 */ +#define MY_STRXFRM_DESC_LEVEL3 0x00000300 /* if desc order for level3 */ +#define MY_STRXFRM_DESC_LEVEL4 0x00000800 /* if desc order for level4 */ +#define MY_STRXFRM_DESC_LEVEL5 0x00001000 /* if desc order for level5 */ +#define MY_STRXFRM_DESC_LEVEL6 0x00002000 /* if desc order for level6 */ +#define MY_STRXFRM_DESC_SHIFT 8 + +#define MY_STRXFRM_UNUSED_00004000 0x00004000 /* for future extensions */ +#define MY_STRXFRM_UNUSED_00008000 0x00008000 /* for future extensions */ + +#define MY_STRXFRM_REVERSE_LEVEL1 0x00010000 /* if reverse order for level1 */ +#define MY_STRXFRM_REVERSE_LEVEL2 0x00020000 /* if reverse order for level2 */ +#define MY_STRXFRM_REVERSE_LEVEL3 0x00040000 /* if reverse order for level3 */ +#define MY_STRXFRM_REVERSE_LEVEL4 0x00080000 /* if reverse order for level4 */ +#define MY_STRXFRM_REVERSE_LEVEL5 0x00100000 /* if reverse order for level5 */ +#define MY_STRXFRM_REVERSE_LEVEL6 0x00200000 /* if reverse order for level6 */ +#define MY_STRXFRM_REVERSE_SHIFT 16 + struct my_uni_idx_st { uint16 from; diff --git a/include/my_bitmap.h b/include/my_bitmap.h index a6cd874255a..e7fe050a21e 100644 --- a/include/my_bitmap.h +++ b/include/my_bitmap.h @@ -63,6 +63,7 @@ extern uint bitmap_set_next(MY_BITMAP *map); extern uint bitmap_get_first(const MY_BITMAP *map); extern uint bitmap_get_first_set(const MY_BITMAP *map); extern uint bitmap_bits_set(const MY_BITMAP *map); +extern uint bitmap_get_next_set(const MY_BITMAP *map, uint bitmap_bit); extern void bitmap_free(MY_BITMAP *map); extern void bitmap_set_above(MY_BITMAP *map, uint from_byte, uint use_bit); extern void bitmap_set_prefix(MY_BITMAP *map, uint prefix_size); diff --git a/libevent/cmake_install.cmake b/libevent/cmake_install.cmake deleted file mode 100644 index 341d9b9d7b9..00000000000 --- a/libevent/cmake_install.cmake +++ /dev/null @@ -1,34 +0,0 @@ -# Install script for directory: /my/maria-10.0-merge/libevent - -# Set the install prefix -IF(NOT DEFINED CMAKE_INSTALL_PREFIX) - SET(CMAKE_INSTALL_PREFIX "/usr/local/mysql") -ENDIF(NOT DEFINED CMAKE_INSTALL_PREFIX) -STRING(REGEX REPLACE "/$" "" CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}") - -# Set the install configuration name. -IF(NOT DEFINED CMAKE_INSTALL_CONFIG_NAME) - IF(BUILD_TYPE) - STRING(REGEX REPLACE "^[^A-Za-z0-9_]+" "" - CMAKE_INSTALL_CONFIG_NAME "${BUILD_TYPE}") - ELSE(BUILD_TYPE) - SET(CMAKE_INSTALL_CONFIG_NAME "Debug") - ENDIF(BUILD_TYPE) - MESSAGE(STATUS "Install configuration: \"${CMAKE_INSTALL_CONFIG_NAME}\"") -ENDIF(NOT DEFINED CMAKE_INSTALL_CONFIG_NAME) - -# Set the component getting installed. -IF(NOT CMAKE_INSTALL_COMPONENT) - IF(COMPONENT) - MESSAGE(STATUS "Install component: \"${COMPONENT}\"") - SET(CMAKE_INSTALL_COMPONENT "${COMPONENT}") - ELSE(COMPONENT) - SET(CMAKE_INSTALL_COMPONENT) - ENDIF(COMPONENT) -ENDIF(NOT CMAKE_INSTALL_COMPONENT) - -# Install shared libraries without execute permission? -IF(NOT DEFINED CMAKE_INSTALL_SO_NO_EXE) - SET(CMAKE_INSTALL_SO_NO_EXE "0") -ENDIF(NOT DEFINED CMAKE_INSTALL_SO_NO_EXE) - diff --git a/libmysqld/CMakeLists.txt b/libmysqld/CMakeLists.txt index c1e4cdb0543..465626c2536 100644 --- a/libmysqld/CMakeLists.txt +++ b/libmysqld/CMakeLists.txt @@ -64,7 +64,8 @@ SET(SQL_EMBEDDED_SOURCES emb_qcache.cc libmysqld.c lib_sql.cc ../sql/sql_analyse.cc ../sql/sql_base.cc ../sql/sql_cache.cc ../sql/sql_class.cc ../sql/sql_crypt.cc ../sql/sql_cursor.cc ../sql/sql_db.cc ../sql/sql_delete.cc ../sql/sql_derived.cc - ../sql/sql_do.cc ../sql/sql_error.cc ../sql/sql_handler.cc + ../sql/sql_do.cc ../sql/sql_error.cc ../sql/sql_handler.cc + ../sql/sql_get_diagnostics.cc ../sql/sql_help.cc ../sql/sql_insert.cc ../sql/datadict.cc ../sql/sql_admin.cc ../sql/sql_truncate.cc ../sql/sql_reload.cc ../sql/sql_lex.cc ../sql/keycaches.cc @@ -118,7 +119,7 @@ ENDIF() SET(LIBS - dbug strings regex mysys vio + dbug strings regex mysys mysys_ssl vio ${ZLIB_LIBRARY} ${SSL_LIBRARIES} ${LIBWRAP} ${LIBCRYPT} ${LIBDL} ${MYSQLD_STATIC_PLUGIN_LIBS} diff --git a/libmysqld/emb_qcache.cc b/libmysqld/emb_qcache.cc index abb0631ebfb..db236d826c9 100644 --- a/libmysqld/emb_qcache.cc +++ b/libmysqld/emb_qcache.cc @@ -487,7 +487,7 @@ int emb_load_querycache_result(THD *thd, Querycache_stream *src) data->embedded_info->prev_ptr= prev_row; return_ok: net_send_eof(thd, thd->server_status, - thd->warning_info->statement_warn_count()); + thd->get_stmt_da()->statement_warn_count()); DBUG_RETURN(0); err: DBUG_RETURN(1); diff --git a/libmysqld/lib_sql.cc b/libmysqld/lib_sql.cc index 6f8f9ddcfd5..a12380b85db 100644 --- a/libmysqld/lib_sql.cc +++ b/libmysqld/lib_sql.cc @@ -130,7 +130,7 @@ emb_advanced_command(MYSQL *mysql, enum enum_server_command command, /* Clear result variables */ thd->clear_error(); - thd->stmt_da->reset_diagnostics_area(); + thd->get_stmt_da()->reset_diagnostics_area(); mysql->affected_rows= ~(my_ulonglong) 0; mysql->field_count= 0; net_clear_error(net); @@ -241,7 +241,7 @@ static my_bool emb_read_prepare_result(MYSQL *mysql, MYSQL_STMT *stmt) stmt->stmt_id= thd->client_stmt_id; stmt->param_count= thd->client_param_count; stmt->field_count= 0; - mysql->warning_count= thd->warning_info->statement_warn_count(); + mysql->warning_count= thd->get_stmt_da()->statement_warn_count(); if (thd->first_data) { @@ -428,7 +428,7 @@ static void emb_free_embedded_thd(MYSQL *mysql) static const char * emb_read_statistics(MYSQL *mysql) { THD *thd= (THD*)mysql->thd; - return thd->is_error() ? thd->stmt_da->message() : ""; + return thd->is_error() ? thd->get_stmt_da()->message() : ""; } @@ -1045,7 +1045,7 @@ bool Protocol::send_result_set_metadata(List<Item> *list, uint flags) if (flags & SEND_EOF) write_eof_packet(thd, thd->server_status, - thd->warning_info->statement_warn_count()); + thd->get_stmt_da()->statement_warn_count()); DBUG_RETURN(prepare_for_send(list->elements)); err: diff --git a/mysql-test/include/default_mysqld.cnf b/mysql-test/include/default_mysqld.cnf index 1aa551af57f..e517aeddcc8 100644 --- a/mysql-test/include/default_mysqld.cnf +++ b/mysql-test/include/default_mysqld.cnf @@ -1,4 +1,4 @@ -# Copyright (c) 2007, 2010, Oracle and/or its affiliates +# Copyright (c) 2007, 2012, Oracle and/or its affiliates # Copyright (c) 2010, 2012, Monty Program Ab # # This program is free software; you can redistribute it and/or modify @@ -50,18 +50,36 @@ loose-innodb_log_files_in_group= 2 slave-net-timeout=120 +# MAINTAINER: +# the loose- syntax is to make sure the cnf file is also +# valid when building without the performance schema. + # Run tests with the performance schema instrumentation loose-enable-performance-schema # Run tests with a small number of instrumented objects # to limit memory consumption with MTR +loose-performance-schema-accounts-size=100 +loose-performance-schema-digests-size=200 +loose-performance-schema-hosts-size=100 +loose-performance-schema-users-size=100 loose-performance-schema-max-mutex-instances=5000 loose-performance-schema-max-rwlock-instances=5000 +loose-performance-schema-max-cond-instances=1000 +loose-performance-schema-max-file-instances=10000 +loose-performance-schema-max-socket-instances=1000 loose-performance-schema-max-table-instances=500 loose-performance-schema-max-table-handles=1000 +loose-performance-schema-events-waits-history-size=10 +loose-performance-schema-events-waits-history-long-size=10000 +loose-performance-schema-events-stages-history-size=10 loose-performance-schema-events-stages-history-long-size=1000 +loose-performance-schema-events-statements-history-size=10 loose-performance-schema-events-statements-history-long-size=1000 loose-performance-schema-max-thread-instances=200 +loose-performance-schema-session-connect-attrs-size=2048 + +# Enable everything, for maximun code exposure during testing loose-performance-schema-instrument='%=ON' diff --git a/mysql-test/suite/perfschema/include/sizing_auto.inc b/mysql-test/suite/perfschema/include/sizing_auto.inc new file mode 100644 index 00000000000..61097a1d30d --- /dev/null +++ b/mysql-test/suite/perfschema/include/sizing_auto.inc @@ -0,0 +1,15 @@ + +show variables like "table_definition_cache"; +show variables like "table_open_cache"; +show variables like "max_connections"; +# Results vary by platform: +# show variables like "open_files_limit"; +show variables like "%performance_schema%"; +show status like "%performance_schema%"; + +# Each test script should provide a different test.cnf file, +# with different settings. +# This output will show the sizes computed automatically. +# Note that this output is very dependent on the platform. + +show engine performance_schema status; diff --git a/mysys/hash.c b/mysys/hash.c index 0a830d8e3f4..d1ffdb57f08 100644 --- a/mysys/hash.c +++ b/mysys/hash.c @@ -67,7 +67,8 @@ static my_hash_value_type calc_hash(const HASH *hash, @param[in] get_key get the key for the hash @param[in] free_element pointer to the function that does cleanup - @return inidicates success or failure of initialization + @param[in] flags flags set in the hash + @return indicates success or failure of initialization @retval 0 success @retval 1 failure */ diff --git a/mysys/md5.c b/mysys/md5.c deleted file mode 100644 index b3b0a470369..00000000000 --- a/mysys/md5.c +++ /dev/null @@ -1,329 +0,0 @@ -/* Copyright (C) 2000 MySQL AB - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; version 2 of the License. - - 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 */ - -/* - * This code implements the MD5 message-digest algorithm. - * The algorithm is due to Ron Rivest. This code was - * written by Colin Plumb in 1993, no copyright is claimed. - * This code is in the public domain; do with it what you wish. - * - * Equivalent code is available from RSA Data Security, Inc. - * This code has been tested against that, and is equivalent, - * except that you don't need to include two pages of legalese - * with every copy. - * - * To compute the message digest of a chunk of bytes, declare an - * MD5Context structure, pass it to MD5Init, call MD5Update as - * needed on buffers full of bytes, and then call MD5Final, which - * will fill a supplied 16-byte array with the digest. - */ - -/* This code was modified in 1997 by Jim Kingdon of Cyclic Software to - not require an integer type which is exactly 32 bits. This work - draws on the changes for the same purpose by Tatu Ylonen - <ylo@cs.hut.fi> as part of SSH, but since I didn't actually use - that code, there is no copyright issue. I hereby disclaim - copyright in any changes I have made; this code remains in the - public domain. */ - -/* - Skip entirely if built with OpenSSL/YaSSL support. -*/ -#if !defined(HAVE_OPENSSL) && !defined(HAVE_YASSL) - -#include <my_global.h> -#include <m_string.h> -#include "my_md5.h" - -static void -my_MD5Transform (cvs_uint32 buf[4], const unsigned char in[64]); - -/* Little-endian byte-swapping routines. Note that these do not - depend on the size of datatypes such as uint32, nor do they require - us to detect the endianness of the machine we are running on. It - is possible they should be macros for speed, but I would be - surprised if they were a performance bottleneck for MD5. */ - -static uint32 getu32 (const unsigned char *addr) -{ - return (((((unsigned long)addr[3] << 8) | addr[2]) << 8) - | addr[1]) << 8 | addr[0]; -} - -static void -putu32 (uint32 data, unsigned char *addr) -{ - addr[0] = (unsigned char)data; - addr[1] = (unsigned char)(data >> 8); - addr[2] = (unsigned char)(data >> 16); - addr[3] = (unsigned char)(data >> 24); -} - -/* - Start MD5 accumulation. Set bit count to 0 and buffer to mysterious - initialization constants. -*/ -void -my_MD5Init (my_MD5Context *ctx) -{ - ctx->buf[0] = 0x67452301; - ctx->buf[1] = 0xefcdab89; - ctx->buf[2] = 0x98badcfe; - ctx->buf[3] = 0x10325476; - - ctx->bits[0] = 0; - ctx->bits[1] = 0; -} - -/* - Update context to reflect the concatenation of another buffer full - of bytes. -*/ -void -my_MD5Update (my_MD5Context *ctx, unsigned char const *buf, unsigned len) -{ - uint32 t; - - /* Update bitcount */ - - t = ctx->bits[0]; - if ((ctx->bits[0] = (t + ((uint32)len << 3)) & 0xffffffff) < t) - ctx->bits[1]++; /* Carry from low to high */ - ctx->bits[1] += len >> 29; - - t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ - - /* Handle any leading odd-sized chunks */ - - if ( t ) { - unsigned char *p = ctx->in + t; - - t = 64-t; - if (len < t) { - memcpy(p, buf, len); - return; - } - memcpy(p, buf, t); - my_MD5Transform (ctx->buf, ctx->in); - buf += t; - len -= t; - } - - /* Process data in 64-byte chunks */ - - while (len >= 64) { - memcpy(ctx->in, buf, 64); - my_MD5Transform (ctx->buf, ctx->in); - buf += 64; - len -= 64; - } - - /* Handle any remaining bytes of data. */ - - memcpy(ctx->in, buf, len); -} - -/* - Final wrapup - pad to 64-byte boundary with the bit pattern - 1 0* (64-bit count of bits processed, MSB-first) -*/ -void -my_MD5Final (unsigned char digest[16], my_MD5Context *ctx) -{ - unsigned count; - unsigned char *p; - - /* Compute number of bytes mod 64 */ - count = (ctx->bits[0] >> 3) & 0x3F; - - /* Set the first char of padding to 0x80. This is safe since there is - always at least one byte free */ - p = ctx->in + count; - *p++ = 0x80; - - /* Bytes of padding needed to make 64 bytes */ - count = 64 - 1 - count; - - /* Pad out to 56 mod 64 */ - if (count < 8) { - /* Two lots of padding: Pad the first block to 64 bytes */ - memset(p, 0, count); - my_MD5Transform (ctx->buf, ctx->in); - - /* Now fill the next block with 56 bytes */ - memset(ctx->in, 0, 56); - } else { - /* Pad block to 56 bytes */ - memset(p, 0, count-8); - } - - /* Append length in bits and transform */ - putu32(ctx->bits[0], ctx->in + 56); - putu32(ctx->bits[1], ctx->in + 60); - - my_MD5Transform (ctx->buf, ctx->in); - putu32(ctx->buf[0], digest); - putu32(ctx->buf[1], digest + 4); - putu32(ctx->buf[2], digest + 8); - putu32(ctx->buf[3], digest + 12); - memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */ -} - -#ifndef ASM_MD5 - -/* The four core functions - F1 is optimized somewhat */ - -/* #define F1(x, y, z) (x & y | ~x & z) */ -#define F1(x, y, z) (z ^ (x & (y ^ z))) -#define F2(x, y, z) F1(z, x, y) -#define F3(x, y, z) (x ^ y ^ z) -#define F4(x, y, z) (y ^ (x | ~z)) - -/* This is the central step in the MD5 algorithm. */ -#define MD5STEP(f, w, x, y, z, data, s) \ - ( w += f(x, y, z) + data, w &= 0xffffffff, w = w<<s | w>>(32-s), w += x ) - -/* - * The core of the MD5 algorithm, this alters an existing MD5 hash to - * reflect the addition of 16 longwords of new data. MD5Update blocks - * the data and converts bytes into longwords for this routine. - */ -static void -my_MD5Transform (uint32 buf[4], const unsigned char inraw[64]) -{ - register uint32 a, b, c, d; - uint32 in[16]; - int i; - - for (i = 0; i < 16; ++i) - in[i] = getu32 (inraw + 4 * i); - - a = buf[0]; - b = buf[1]; - c = buf[2]; - d = buf[3]; - - MD5STEP(F1, a, b, c, d, in[ 0]+0xd76aa478, 7); - MD5STEP(F1, d, a, b, c, in[ 1]+0xe8c7b756, 12); - MD5STEP(F1, c, d, a, b, in[ 2]+0x242070db, 17); - MD5STEP(F1, b, c, d, a, in[ 3]+0xc1bdceee, 22); - MD5STEP(F1, a, b, c, d, in[ 4]+0xf57c0faf, 7); - MD5STEP(F1, d, a, b, c, in[ 5]+0x4787c62a, 12); - MD5STEP(F1, c, d, a, b, in[ 6]+0xa8304613, 17); - MD5STEP(F1, b, c, d, a, in[ 7]+0xfd469501, 22); - MD5STEP(F1, a, b, c, d, in[ 8]+0x698098d8, 7); - MD5STEP(F1, d, a, b, c, in[ 9]+0x8b44f7af, 12); - MD5STEP(F1, c, d, a, b, in[10]+0xffff5bb1, 17); - MD5STEP(F1, b, c, d, a, in[11]+0x895cd7be, 22); - MD5STEP(F1, a, b, c, d, in[12]+0x6b901122, 7); - MD5STEP(F1, d, a, b, c, in[13]+0xfd987193, 12); - MD5STEP(F1, c, d, a, b, in[14]+0xa679438e, 17); - MD5STEP(F1, b, c, d, a, in[15]+0x49b40821, 22); - - MD5STEP(F2, a, b, c, d, in[ 1]+0xf61e2562, 5); - MD5STEP(F2, d, a, b, c, in[ 6]+0xc040b340, 9); - MD5STEP(F2, c, d, a, b, in[11]+0x265e5a51, 14); - MD5STEP(F2, b, c, d, a, in[ 0]+0xe9b6c7aa, 20); - MD5STEP(F2, a, b, c, d, in[ 5]+0xd62f105d, 5); - MD5STEP(F2, d, a, b, c, in[10]+0x02441453, 9); - MD5STEP(F2, c, d, a, b, in[15]+0xd8a1e681, 14); - MD5STEP(F2, b, c, d, a, in[ 4]+0xe7d3fbc8, 20); - MD5STEP(F2, a, b, c, d, in[ 9]+0x21e1cde6, 5); - MD5STEP(F2, d, a, b, c, in[14]+0xc33707d6, 9); - MD5STEP(F2, c, d, a, b, in[ 3]+0xf4d50d87, 14); - MD5STEP(F2, b, c, d, a, in[ 8]+0x455a14ed, 20); - MD5STEP(F2, a, b, c, d, in[13]+0xa9e3e905, 5); - MD5STEP(F2, d, a, b, c, in[ 2]+0xfcefa3f8, 9); - MD5STEP(F2, c, d, a, b, in[ 7]+0x676f02d9, 14); - MD5STEP(F2, b, c, d, a, in[12]+0x8d2a4c8a, 20); - - MD5STEP(F3, a, b, c, d, in[ 5]+0xfffa3942, 4); - MD5STEP(F3, d, a, b, c, in[ 8]+0x8771f681, 11); - MD5STEP(F3, c, d, a, b, in[11]+0x6d9d6122, 16); - MD5STEP(F3, b, c, d, a, in[14]+0xfde5380c, 23); - MD5STEP(F3, a, b, c, d, in[ 1]+0xa4beea44, 4); - MD5STEP(F3, d, a, b, c, in[ 4]+0x4bdecfa9, 11); - MD5STEP(F3, c, d, a, b, in[ 7]+0xf6bb4b60, 16); - MD5STEP(F3, b, c, d, a, in[10]+0xbebfbc70, 23); - MD5STEP(F3, a, b, c, d, in[13]+0x289b7ec6, 4); - MD5STEP(F3, d, a, b, c, in[ 0]+0xeaa127fa, 11); - MD5STEP(F3, c, d, a, b, in[ 3]+0xd4ef3085, 16); - MD5STEP(F3, b, c, d, a, in[ 6]+0x04881d05, 23); - MD5STEP(F3, a, b, c, d, in[ 9]+0xd9d4d039, 4); - MD5STEP(F3, d, a, b, c, in[12]+0xe6db99e5, 11); - MD5STEP(F3, c, d, a, b, in[15]+0x1fa27cf8, 16); - MD5STEP(F3, b, c, d, a, in[ 2]+0xc4ac5665, 23); - - MD5STEP(F4, a, b, c, d, in[ 0]+0xf4292244, 6); - MD5STEP(F4, d, a, b, c, in[ 7]+0x432aff97, 10); - MD5STEP(F4, c, d, a, b, in[14]+0xab9423a7, 15); - MD5STEP(F4, b, c, d, a, in[ 5]+0xfc93a039, 21); - MD5STEP(F4, a, b, c, d, in[12]+0x655b59c3, 6); - MD5STEP(F4, d, a, b, c, in[ 3]+0x8f0ccc92, 10); - MD5STEP(F4, c, d, a, b, in[10]+0xffeff47d, 15); - MD5STEP(F4, b, c, d, a, in[ 1]+0x85845dd1, 21); - MD5STEP(F4, a, b, c, d, in[ 8]+0x6fa87e4f, 6); - MD5STEP(F4, d, a, b, c, in[15]+0xfe2ce6e0, 10); - MD5STEP(F4, c, d, a, b, in[ 6]+0xa3014314, 15); - MD5STEP(F4, b, c, d, a, in[13]+0x4e0811a1, 21); - MD5STEP(F4, a, b, c, d, in[ 4]+0xf7537e82, 6); - MD5STEP(F4, d, a, b, c, in[11]+0xbd3af235, 10); - MD5STEP(F4, c, d, a, b, in[ 2]+0x2ad7d2bb, 15); - MD5STEP(F4, b, c, d, a, in[ 9]+0xeb86d391, 21); - - buf[0] += a; - buf[1] += b; - buf[2] += c; - buf[3] += d; -} -#endif - -#ifdef TEST -/* - Simple test program. Can use it to manually run the tests from - RFC1321 for example. -*/ -#include <stdio.h> - -int -main (int argc, char **argv) -{ - my_MD5Context context; - unsigned char checksum[16]; - int i; - int j; - - if (argc < 2) - { - fprintf (stderr, "usage: %s string-to-hash\n", argv[0]); - exit (1); - } - for (j = 1; j < argc; ++j) - { - printf ("MD5 (\"%s\") = ", argv[j]); - my_MD5Init (&context); - my_MD5Update (&context, argv[j], strlen (argv[j])); - my_MD5Final (checksum, &context); - for (i = 0; i < 16; i++) - { - printf ("%02x", (unsigned int) checksum[i]); - } - printf ("\n"); - } - return 0; -} -#endif /* TEST */ - -#endif /* !defined(HAVE_OPENSSL) && !defined(HAVE_YASSL) */ diff --git a/mysys/mf_iocache2.c b/mysys/mf_iocache2.c index ff05b7fa485..d0c99186596 100644 --- a/mysys/mf_iocache2.c +++ b/mysys/mf_iocache2.c @@ -493,7 +493,7 @@ process_flags: if (my_b_write(info, (uchar*) buff, length2)) goto err; } - else if ((*fmt == 'l' && fmt[1] == 'd') || fmt[1] == 'u') + else if ((*fmt == 'l' && (fmt[1] == 'd' || fmt[1] == 'u'))) /* long parameter */ { register long iarg; diff --git a/mysys/my_bitmap.c b/mysys/my_bitmap.c index 8b4dd83ab21..10cd4a2a9ef 100644 --- a/mysys/my_bitmap.c +++ b/mysys/my_bitmap.c @@ -147,6 +147,26 @@ static inline void bitmap_unlock(MY_BITMAP *map __attribute__((unused))) } +static inline uint get_first_set(my_bitmap_map value, uint word_pos) +{ + uchar *byte_ptr= (uchar*)&value; + uchar byte_value; + uint byte_pos, bit_pos; + + DBUG_ASSERT(value); + for (byte_pos=0; ; byte_pos++, byte_ptr++) + { + if ((byte_value= *byte_ptr)) + { + for (bit_pos=0; ; bit_pos++) + if (byte_value & (1 << bit_pos)) + return (word_pos*32) + (byte_pos*8) + bit_pos; + } + } + return MY_BIT_NONE; /* Impossible */ +} + + my_bool bitmap_init(MY_BITMAP *map, my_bitmap_map *buf, uint n_bits, my_bool thread_safe __attribute__((unused))) { @@ -597,12 +617,10 @@ void bitmap_copy(MY_BITMAP *map, const MY_BITMAP *map2) uint bitmap_get_first_set(const MY_BITMAP *map) { - uchar *byte_ptr; - uint i,j,k; - my_bitmap_map *data_ptr, *end= map->last_word_ptr; + uint i; + my_bitmap_map *data_ptr= map->bitmap, *end= map->last_word_ptr; DBUG_ASSERT(map->bitmap); - data_ptr= map->bitmap; for (i=0; data_ptr < end; data_ptr++, i++) if (*data_ptr) @@ -611,25 +629,66 @@ uint bitmap_get_first_set(const MY_BITMAP *map) return MY_BIT_NONE; found: + return get_first_set(*data_ptr, i); +} + + +/** + Get the next set bit. + + @param map Bitmap + @param bitmap_bit Bit to start search from + + @return Index to first bit set after bitmap_bit +*/ + +uint bitmap_get_next_set(const MY_BITMAP *map, uint bitmap_bit) +{ + uint word_pos, byte_to_mask, i; + union { my_bitmap_map bitmap ; uchar bitmap_buff[sizeof(my_bitmap_map)]; } + first_word; + uchar *ptr= &first_word.bitmap_buff[0]; + my_bitmap_map *data_ptr, *end= map->last_word_ptr; + + DBUG_ASSERT(map->bitmap); + + /* Look for the next bit */ + bitmap_bit++; + if (bitmap_bit >= map->n_bits) + return MY_BIT_NONE; + word_pos= bitmap_bit / 32; + data_ptr= map->bitmap + word_pos; + first_word.bitmap= *data_ptr; + + /* Mask out previous bits from first_word */ + byte_to_mask= (bitmap_bit % 32) / 8; + for (i= 0; i < byte_to_mask; i++) + ptr[i]= 0; + ptr[byte_to_mask]&= 0xFFU << (bitmap_bit & 7); + + if (data_ptr == end) { - byte_ptr= (uchar*)data_ptr; - for (j=0; ; j++, byte_ptr++) - { - if (*byte_ptr) - { - for (k=0; ; k++) - { - if (*byte_ptr & (1 << k)) - return (i*32) + (j*8) + k; - } - } - } + if (first_word.bitmap & ~map->last_word_mask) + return get_first_set(first_word.bitmap, word_pos); + else + return MY_BIT_NONE; } - DBUG_ASSERT(0); - return MY_BIT_NONE; /* Impossible */ + + if (first_word.bitmap) + return get_first_set(first_word.bitmap, word_pos); + + for (data_ptr++, word_pos++; data_ptr < end; data_ptr++, word_pos++) + if (*data_ptr) + return get_first_set(*data_ptr, word_pos); + + if (!(*end & ~map->last_word_mask)) + return MY_BIT_NONE; + return get_first_set(*end, word_pos); } +/* Get first free bit */ + uint bitmap_get_first(const MY_BITMAP *map) { uchar *byte_ptr; @@ -647,17 +706,15 @@ uint bitmap_get_first(const MY_BITMAP *map) return MY_BIT_NONE; found: + byte_ptr= (uchar*)data_ptr; + for (j=0; ; j++, byte_ptr++) { - byte_ptr= (uchar*)data_ptr; - for (j=0; ; j++, byte_ptr++) + if (*byte_ptr != 0xFF) { - if (*byte_ptr != 0xFF) + for (k=0; ; k++) { - for (k=0; ; k++) - { - if (!(*byte_ptr & (1 << k))) - return (i*32) + (j*8) + k; - } + if (!(*byte_ptr & (1 << k))) + return (i*32) + (j*8) + k; } } } diff --git a/mysys_ssl/CTestTestfile.cmake b/mysys_ssl/CTestTestfile.cmake deleted file mode 100644 index fc98399082f..00000000000 --- a/mysys_ssl/CTestTestfile.cmake +++ /dev/null @@ -1,6 +0,0 @@ -# CMake generated Testfile for -# Source directory: /my/maria-10.0-merge/mysys_ssl -# Build directory: /my/maria-10.0-merge/mysys_ssl -# -# This file includes the relevent testing commands required for -# testing this directory and lists subdirectories to be tested as well. diff --git a/mysys_ssl/cmake_install.cmake b/mysys_ssl/cmake_install.cmake deleted file mode 100644 index 9617527ed80..00000000000 --- a/mysys_ssl/cmake_install.cmake +++ /dev/null @@ -1,34 +0,0 @@ -# Install script for directory: /my/maria-10.0-merge/mysys_ssl - -# Set the install prefix -IF(NOT DEFINED CMAKE_INSTALL_PREFIX) - SET(CMAKE_INSTALL_PREFIX "/usr/local/mysql") -ENDIF(NOT DEFINED CMAKE_INSTALL_PREFIX) -STRING(REGEX REPLACE "/$" "" CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}") - -# Set the install configuration name. -IF(NOT DEFINED CMAKE_INSTALL_CONFIG_NAME) - IF(BUILD_TYPE) - STRING(REGEX REPLACE "^[^A-Za-z0-9_]+" "" - CMAKE_INSTALL_CONFIG_NAME "${BUILD_TYPE}") - ELSE(BUILD_TYPE) - SET(CMAKE_INSTALL_CONFIG_NAME "Debug") - ENDIF(BUILD_TYPE) - MESSAGE(STATUS "Install configuration: \"${CMAKE_INSTALL_CONFIG_NAME}\"") -ENDIF(NOT DEFINED CMAKE_INSTALL_CONFIG_NAME) - -# Set the component getting installed. -IF(NOT CMAKE_INSTALL_COMPONENT) - IF(COMPONENT) - MESSAGE(STATUS "Install component: \"${COMPONENT}\"") - SET(CMAKE_INSTALL_COMPONENT "${COMPONENT}") - ELSE(COMPONENT) - SET(CMAKE_INSTALL_COMPONENT) - ENDIF(COMPONENT) -ENDIF(NOT CMAKE_INSTALL_COMPONENT) - -# Install shared libraries without execute permission? -IF(NOT DEFINED CMAKE_INSTALL_SO_NO_EXE) - SET(CMAKE_INSTALL_SO_NO_EXE "0") -ENDIF(NOT DEFINED CMAKE_INSTALL_SO_NO_EXE) - diff --git a/plugin/handler_socket/handlersocket/database.cpp b/plugin/handler_socket/handlersocket/database.cpp index 311eec55fa8..cc315149c5d 100644 --- a/plugin/handler_socket/handlersocket/database.cpp +++ b/plugin/handler_socket/handlersocket/database.cpp @@ -762,7 +762,7 @@ dbcontext::cmd_find_internal(dbcallback_i& cb, const prep_stmt& pst, return cb.dbcb_resp_short(2, "idxnum"); } KEY& kinfo = table->key_info[pst.get_idxnum()]; - if (args.kvalslen > kinfo.key_parts) { + if (args.kvalslen > kinfo.user_defined_key_parts) { return cb.dbcb_resp_short(2, "kpnum"); } uchar *const key_buf = DENA_ALLOCA_ALLOCATE(uchar, kinfo.key_length); diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index 070afbd9c38..e42ccc16bc9 100644 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -59,7 +59,8 @@ SET (SQL_SOURCE sp_rcontext.cc spatial.cc sql_acl.cc sql_analyse.cc sql_base.cc sql_cache.cc sql_class.cc sql_client.cc sql_crypt.cc sql_crypt.h sql_cursor.cc sql_db.cc sql_delete.cc sql_derived.cc sql_do.cc - sql_error.cc sql_handler.cc sql_help.cc sql_insert.cc sql_lex.cc + sql_error.cc sql_handler.cc sql_get_diagnostics.cc + sql_help.cc sql_insert.cc sql_lex.cc sql_list.cc sql_load.cc sql_manager.cc sql_parse.cc sql_bootstrap.cc sql_bootstrap.h sql_partition.cc sql_plugin.cc sql_prepare.cc sql_rename.cc diff --git a/sql/create_options.cc b/sql/create_options.cc index 5cedfa03a63..dd0a14cd476 100644 --- a/sql/create_options.cc +++ b/sql/create_options.cc @@ -86,7 +86,7 @@ static bool report_wrong_value(THD *thd, const char *name, const char *val, return 1; } - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_BAD_OPTION_VALUE, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_BAD_OPTION_VALUE, ER(ER_BAD_OPTION_VALUE), val, name); return 0; } @@ -109,7 +109,7 @@ static bool report_unknown_option(THD *thd, engine_option_value *val, DBUG_RETURN(TRUE); } - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_UNKNOWN_OPTION, ER(ER_UNKNOWN_OPTION), val->name.str); DBUG_RETURN(FALSE); } diff --git a/sql/debug_sync.cc b/sql/debug_sync.cc index 5e439839bca..1f3c86c5b67 100644 --- a/sql/debug_sync.cc +++ b/sql/debug_sync.cc @@ -734,11 +734,6 @@ static st_debug_sync_action *debug_sync_get_action(THD *thd, static bool debug_sync_set_action(THD *thd, st_debug_sync_action *action) { - if(!thd) - { - return; - } - st_debug_sync_control *ds_control= thd->debug_sync_control; bool is_dsp_now= FALSE; DBUG_ENTER("debug_sync_set_action"); @@ -1455,7 +1450,7 @@ static void debug_sync_execute(THD *thd, st_debug_sync_action *action) sig_wait, sig_glob, error));}); if (error == ETIMEDOUT || error == ETIME) { - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning(thd, Sql_condition::WARN_LEVEL_WARN, ER_DEBUG_SYNC_TIMEOUT, ER(ER_DEBUG_SYNC_TIMEOUT)); break; } diff --git a/sql/derror.cc b/sql/derror.cc index abe642bea79..74e8209496b 100644 --- a/sql/derror.cc +++ b/sql/derror.cc @@ -76,7 +76,7 @@ bool init_errmessage(void) &errmsgs, ER_ERROR_LAST - ER_ERROR_FIRST + 1) && !errmsgs) { - free(errmsgs); + my_free(errmsgs); if (org_errmsgs) { @@ -99,7 +99,7 @@ bool init_errmessage(void) } } else - free(org_errmsgs); // Free old language + my_free(org_errmsgs); // Free old language /* Register messages for use with my_error(). */ if (my_error_register(get_server_errmsgs, ER_ERROR_FIRST, ER_ERROR_LAST)) diff --git a/sql/event_data_objects.cc b/sql/event_data_objects.cc index 2e31d20d54e..e236319d757 100644 --- a/sql/event_data_objects.cc +++ b/sql/event_data_objects.cc @@ -608,7 +608,7 @@ Event_timed::load_from_row(THD *thd, TABLE *table) table, &creation_ctx)) { push_warning_printf(thd, - MYSQL_ERROR::WARN_LEVEL_WARN, + Sql_condition::WARN_LEVEL_WARN, ER_EVENT_INVALID_CREATION_CTX, ER(ER_EVENT_INVALID_CREATION_CTX), (const char *) dbname.str, diff --git a/sql/event_db_repository.cc b/sql/event_db_repository.cc index 2413a0e57ea..860f31e5c3a 100644 --- a/sql/event_db_repository.cc +++ b/sql/event_db_repository.cc @@ -687,7 +687,7 @@ Event_db_repository::create_event(THD *thd, Event_parse_data *parse_data, if (create_if_not) { *event_already_exists= true; - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_EVENT_ALREADY_EXISTS, ER(ER_EVENT_ALREADY_EXISTS), parse_data->name.str); ret= 0; @@ -912,7 +912,7 @@ Event_db_repository::drop_event(THD *thd, LEX_STRING db, LEX_STRING name, goto end; } - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST), "Event", name.str); ret= 0; @@ -958,7 +958,7 @@ Event_db_repository::find_named_event(LEX_STRING db, LEX_STRING name, if (db.length > table->field[ET_FIELD_DB]->field_length || name.length > table->field[ET_FIELD_NAME]->field_length || table->s->keys == 0 || - table->key_info[0].key_parts != 2 || + table->key_info[0].user_defined_key_parts != 2 || table->key_info[0].key_part[0].fieldnr != ET_FIELD_DB+1 || table->key_info[0].key_part[1].fieldnr != ET_FIELD_NAME+1) DBUG_RETURN(TRUE); diff --git a/sql/event_parse_data.cc b/sql/event_parse_data.cc index ad812a6aa5d..86c5c6ec1d0 100644 --- a/sql/event_parse_data.cc +++ b/sql/event_parse_data.cc @@ -126,7 +126,7 @@ Event_parse_data::check_if_in_the_past(THD *thd, my_time_t ltime_utc) { switch (thd->lex->sql_command) { case SQLCOM_CREATE_EVENT: - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + push_warning(thd, Sql_condition::WARN_LEVEL_NOTE, ER_EVENT_CANNOT_CREATE_IN_THE_PAST, ER(ER_EVENT_CANNOT_CREATE_IN_THE_PAST)); break; @@ -143,7 +143,7 @@ Event_parse_data::check_if_in_the_past(THD *thd, my_time_t ltime_utc) { status= Event_parse_data::DISABLED; status_changed= true; - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + push_warning(thd, Sql_condition::WARN_LEVEL_NOTE, ER_EVENT_EXEC_TIME_IN_THE_PAST, ER(ER_EVENT_EXEC_TIME_IN_THE_PAST)); } diff --git a/sql/event_scheduler.cc b/sql/event_scheduler.cc index ec96ca45a0e..da8e4388a21 100644 --- a/sql/event_scheduler.cc +++ b/sql/event_scheduler.cc @@ -75,9 +75,9 @@ struct scheduler_param { void Event_worker_thread::print_warnings(THD *thd, Event_job_data *et) { - MYSQL_ERROR *err; + const Sql_condition *err; DBUG_ENTER("evex_print_warnings"); - if (thd->warning_info->is_empty()) + if (thd->get_stmt_da()->is_warning_info_empty()) DBUG_VOID_RETURN; char msg_buf[10 * STRING_BUFFER_USUAL_SIZE]; @@ -93,7 +93,8 @@ Event_worker_thread::print_warnings(THD *thd, Event_job_data *et) prefix.append(et->name.str, et->name.length, system_charset_info); prefix.append("] ", 2); - List_iterator_fast<MYSQL_ERROR> it(thd->warning_info->warn_list()); + Diagnostics_area::Sql_condition_iterator it= + thd->get_stmt_da()->sql_conditions(); while ((err= it++)) { String err_msg(msg_buf, sizeof(msg_buf), system_charset_info); diff --git a/sql/field.cc b/sql/field.cc index 1ae5c95ad56..7c79fad8bbc 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -1076,7 +1076,7 @@ static void push_numerical_conversion_warning(THD* thd, const char* str, String tmp(buf, sizeof(buf), cs); tmp.copy(str, length, cs); - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, error, ER(error), typestr, tmp.c_ptr(), field_name, row_num); } @@ -1190,17 +1190,18 @@ int Field_num::check_int(CHARSET_INFO *cs, const char *str, int length, if (str == int_end || error == MY_ERRNO_EDOM) { ErrConvString err(str, length, cs); - push_warning_printf(get_thd(), MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(get_thd(), Sql_condition::WARN_LEVEL_WARN, ER_TRUNCATED_WRONG_VALUE_FOR_FIELD, ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD), "integer", err.ptr(), field_name, - (ulong) get_thd()->warning_info->current_row_for_warning()); + (ulong) table->in_use->get_stmt_da()-> + current_row_for_warning()); return 1; } /* Test if we have garbage at the end of the given string. */ if (test_if_important_data(cs, int_end, str + length)) { - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); + set_warning(Sql_condition::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); return 2; } return 0; @@ -1269,7 +1270,7 @@ bool Field_num::get_int(CHARSET_INFO *cs, const char *from, uint len, return 0; out_of_range: - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); + set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); return 1; } @@ -1289,12 +1290,12 @@ int Field::warn_if_overflow(int op_result) { if (op_result == E_DEC_OVERFLOW) { - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); + set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); return 1; } if (op_result == E_DEC_TRUNCATED) { - set_warning(MYSQL_ERROR::WARN_LEVEL_NOTE, WARN_DATA_TRUNCATED, 1); + set_warning(Sql_condition::WARN_LEVEL_NOTE, WARN_DATA_TRUNCATED, 1); /* We return 0 here as this is not a critical issue */ } return 0; @@ -1618,7 +1619,7 @@ longlong Field::convert_decimal2longlong(const my_decimal *val, { if (val->sign()) { - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); + set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); i= 0; *err= 1; } @@ -1926,7 +1927,7 @@ void Field_decimal::overflow(bool negative) uint len=field_length; uchar *to=ptr, filler= '9'; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); + set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); if (negative) { if (!unsigned_flag) @@ -2034,7 +2035,7 @@ int Field_decimal::store(const char *from_arg, uint len, CHARSET_INFO *cs) from++; if (from == end) { - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); + set_warning(Sql_condition::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); is_cuted_fields_incr=1; } else if (*from == '+' || *from == '-') // Found some sign ? @@ -2110,7 +2111,7 @@ int Field_decimal::store(const char *from_arg, uint len, CHARSET_INFO *cs) for (;from != end && my_isspace(&my_charset_bin, *from); from++) ; if (from != end) // If still something left, warn { - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); + set_warning(Sql_condition::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); is_cuted_fields_incr=1; } } @@ -2288,7 +2289,7 @@ int Field_decimal::store(const char *from_arg, uint len, CHARSET_INFO *cs) if (tmp_char != '0') // Losing a non zero digit ? { if (!is_cuted_fields_incr) - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, + set_warning(Sql_condition::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); return 0; } @@ -2311,7 +2312,7 @@ int Field_decimal::store(const char *from_arg, uint len, CHARSET_INFO *cs) This is a note, not a warning, as we don't want to abort when we cut decimals in strict mode */ - set_warning(MYSQL_ERROR::WARN_LEVEL_NOTE, WARN_DATA_TRUNCATED, 1); + set_warning(Sql_condition::WARN_LEVEL_NOTE, WARN_DATA_TRUNCATED, 1); } return 0; } @@ -2661,7 +2662,7 @@ bool Field_new_decimal::store_value(const my_decimal *decimal_value) if (unsigned_flag && decimal_value->sign()) { DBUG_PRINT("info", ("unsigned overflow")); - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); + set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; decimal_value= &decimal_zero; } @@ -2705,32 +2706,32 @@ int Field_new_decimal::store(const char *from, uint length, thd->abort_on_warning) { ErrConvString errmsg(from, length, &my_charset_bin); - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_TRUNCATED_WRONG_VALUE_FOR_FIELD, ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD), "decimal", errmsg.ptr(), field_name, - (ulong) thd->warning_info->current_row_for_warning()); - + static_cast<ulong>(thd->get_stmt_da()-> + current_row_for_warning())); DBUG_RETURN(err); } switch (err) { case E_DEC_TRUNCATED: - set_warning(MYSQL_ERROR::WARN_LEVEL_NOTE, WARN_DATA_TRUNCATED, 1); + set_warning(Sql_condition::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); break; case E_DEC_OVERFLOW: - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); + set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); set_value_on_overflow(&decimal_value, decimal_value.sign()); break; case E_DEC_BAD_NUM: { ErrConvString errmsg(from, length, &my_charset_bin); - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_TRUNCATED_WRONG_VALUE_FOR_FIELD, ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD), "decimal", errmsg.ptr(), field_name, - (ulong) thd->warning_info-> - current_row_for_warning()); + static_cast<ulong>(thd->get_stmt_da()-> + current_row_for_warning())); my_decimal_set_zero(&decimal_value); break; } @@ -3037,13 +3038,13 @@ int Field_tiny::store(double nr) if (nr < 0.0) { *ptr=0; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); + set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else if (nr > 255.0) { *ptr= (uchar) 255; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); + set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else @@ -3054,13 +3055,13 @@ int Field_tiny::store(double nr) if (nr < -128.0) { *ptr= (uchar) -128; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); + set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else if (nr > 127.0) { *ptr=127; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); + set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else @@ -3080,13 +3081,13 @@ int Field_tiny::store(longlong nr, bool unsigned_val) if (nr < 0 && !unsigned_val) { *ptr= 0; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); + set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else if ((ulonglong) nr > (ulonglong) 255) { *ptr= (char) 255; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); + set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else @@ -3099,13 +3100,13 @@ int Field_tiny::store(longlong nr, bool unsigned_val) if (nr < -128) { *ptr= (char) -128; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); + set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else if (nr > 127) { *ptr=127; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); + set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else @@ -3216,13 +3217,13 @@ int Field_short::store(double nr) if (nr < 0) { res=0; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); + set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else if (nr > (double) UINT_MAX16) { res=(int16) UINT_MAX16; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); + set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else @@ -3233,13 +3234,13 @@ int Field_short::store(double nr) if (nr < (double) INT_MIN16) { res=INT_MIN16; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); + set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else if (nr > (double) INT_MAX16) { res=INT_MAX16; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); + set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else @@ -3261,13 +3262,13 @@ int Field_short::store(longlong nr, bool unsigned_val) if (nr < 0L && !unsigned_val) { res=0; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); + set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else if ((ulonglong) nr > (ulonglong) UINT_MAX16) { res=(int16) UINT_MAX16; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); + set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else @@ -3281,13 +3282,13 @@ int Field_short::store(longlong nr, bool unsigned_val) if (nr < INT_MIN16) { res=INT_MIN16; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); + set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else if (nr > (longlong) INT_MAX16) { res=INT_MAX16; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); + set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else @@ -3404,14 +3405,14 @@ int Field_medium::store(double nr) if (nr < 0) { int3store(ptr,0); - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); + set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else if (nr >= (double) (long) (1L << 24)) { uint32 tmp=(uint32) (1L << 24)-1L; int3store(ptr,tmp); - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); + set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else @@ -3423,14 +3424,14 @@ int Field_medium::store(double nr) { long tmp=(long) INT_MIN24; int3store(ptr,tmp); - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); + set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else if (nr > (double) INT_MAX24) { long tmp=(long) INT_MAX24; int3store(ptr,tmp); - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); + set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else @@ -3450,14 +3451,14 @@ int Field_medium::store(longlong nr, bool unsigned_val) if (nr < 0 && !unsigned_val) { int3store(ptr,0); - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); + set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else if ((ulonglong) nr >= (ulonglong) (long) (1L << 24)) { long tmp= (long) (1L << 24)-1L; int3store(ptr,tmp); - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); + set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else @@ -3472,14 +3473,14 @@ int Field_medium::store(longlong nr, bool unsigned_val) { long tmp= (long) INT_MIN24; int3store(ptr,tmp); - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); + set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else if (nr > (longlong) INT_MAX24) { long tmp=(long) INT_MAX24; int3store(ptr,tmp); - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); + set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else @@ -3601,7 +3602,7 @@ int Field_long::store(double nr) else if (nr > (double) UINT_MAX32) { res= UINT_MAX32; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); + set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else @@ -3623,7 +3624,7 @@ int Field_long::store(double nr) res=(int32) (longlong) nr; } if (error) - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); + set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); int4store(ptr,res); return error; @@ -3669,7 +3670,7 @@ int Field_long::store(longlong nr, bool unsigned_val) res=(int32) nr; } if (error) - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); + set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); int4store(ptr,res); return error; @@ -3768,7 +3769,7 @@ int Field_longlong::store(const char *from,uint len,CHARSET_INFO *cs) tmp= cs->cset->strntoull10rnd(cs,from,len,unsigned_flag,&end,&error); if (error == MY_ERRNO_ERANGE) { - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); + set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else if (get_thd()->count_cuted_fields && @@ -3790,7 +3791,7 @@ int Field_longlong::store(double nr) res= double_to_longlong(nr, unsigned_flag, &error); if (error) - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); + set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); int8store(ptr,res); return error; @@ -3811,7 +3812,7 @@ int Field_longlong::store(longlong nr, bool unsigned_val) if (unsigned_flag != unsigned_val) { nr= unsigned_flag ? (ulonglong) 0 : (ulonglong) LONGLONG_MAX; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); + set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } } @@ -3925,7 +3926,7 @@ int Field_float::store(const char *from,uint len,CHARSET_INFO *cs) if (error || (!len || ((uint) (end-from) != len && get_thd()->count_cuted_fields))) { - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, + set_warning(Sql_condition::WARN_LEVEL_WARN, (error ? ER_WARN_DATA_OUT_OF_RANGE : WARN_DATA_TRUNCATED), 1); error= error ? 1 : 2; } @@ -3942,7 +3943,7 @@ int Field_float::store(double nr) unsigned_flag, FLT_MAX); if (error) { - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); + set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); if (error < 0) // Wrong double value { error= 1; @@ -4113,7 +4114,7 @@ int Field_double::store(const char *from,uint len,CHARSET_INFO *cs) if (error || (!len || ((uint) (end-from) != len && get_thd()->count_cuted_fields))) { - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, + set_warning(Sql_condition::WARN_LEVEL_WARN, (error ? ER_WARN_DATA_OUT_OF_RANGE : WARN_DATA_TRUNCATED), 1); error= error ? 1 : 2; } @@ -4130,7 +4131,7 @@ int Field_double::store(double nr) unsigned_flag, DBL_MAX); if (error) { - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); + set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); if (error < 0) // Wrong double value { error= 1; @@ -4295,7 +4296,7 @@ longlong Field_double::val_int(void) if (error) { ErrConvDouble err(j); - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, ER_TRUNCATED_WRONG_VALUE, ER(ER_TRUNCATED_WRONG_VALUE), "INTEGER", err.ptr()); @@ -4492,7 +4493,7 @@ int Field_timestamp::store_TIME_with_warning(THD *thd, MYSQL_TIME *l_time, if (was_cut || !have_smth_to_conv) { error= 1; - set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, + set_datetime_warning(Sql_condition::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, str, MYSQL_TIMESTAMP_DATETIME, 1); } /* Only convert a correct date (not a zero date) */ @@ -4504,7 +4505,7 @@ int Field_timestamp::store_TIME_with_warning(THD *thd, MYSQL_TIME *l_time, conversion_error= ER_WARN_DATA_OUT_OF_RANGE; if (conversion_error) { - set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, conversion_error, + set_datetime_warning(Sql_condition::WARN_LEVEL_WARN, conversion_error, str, MYSQL_TIMESTAMP_DATETIME, !error); error= 1; } @@ -4968,7 +4969,7 @@ int Field_temporal::store_TIME_with_warning(MYSQL_TIME *ltime, const ErrConv *str, int was_cut, int have_smth_to_conv) { - MYSQL_ERROR::enum_warning_level trunc_level= MYSQL_ERROR::WARN_LEVEL_WARN; + Sql_condition::enum_warning_level trunc_level= Sql_condition::WARN_LEVEL_WARN; int ret= 2; ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; @@ -4988,7 +4989,7 @@ int Field_temporal::store_TIME_with_warning(MYSQL_TIME *ltime, mysql_type_to_time_type(type()) == MYSQL_TIMESTAMP_DATE && (ltime->hour || ltime->minute || ltime->second || ltime->second_part)) { - trunc_level= MYSQL_ERROR::WARN_LEVEL_NOTE; + trunc_level= Sql_condition::WARN_LEVEL_NOTE; was_cut|= MYSQL_TIME_WARN_TRUNCATED; ret= 3; } @@ -4997,7 +4998,7 @@ int Field_temporal::store_TIME_with_warning(MYSQL_TIME *ltime, (ltime->year || ltime->month)) { ltime->year= ltime->month= ltime->day= 0; - trunc_level= MYSQL_ERROR::WARN_LEVEL_NOTE; + trunc_level= Sql_condition::WARN_LEVEL_NOTE; was_cut|= MYSQL_TIME_WARN_TRUNCATED; ret= 3; } @@ -5016,7 +5017,7 @@ int Field_temporal::store_TIME_with_warning(MYSQL_TIME *ltime, set_datetime_warning(trunc_level, WARN_DATA_TRUNCATED, str, mysql_type_to_time_type(type()), 1); if (was_cut & MYSQL_TIME_WARN_OUT_OF_RANGE) - set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, + set_datetime_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, str, mysql_type_to_time_type(type()), 1); store_TIME(ltime); @@ -5237,10 +5238,10 @@ bool Field_time::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) THD *thd= get_thd(); if (!(fuzzydate & (TIME_FUZZY_DATE|TIME_TIME_ONLY))) { - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, ER(ER_WARN_DATA_OUT_OF_RANGE), field_name, - thd->warning_info->current_row_for_warning()); + thd->get_stmt_da()->current_row_for_warning()); return 1; } long tmp=(long) sint3korr(ptr); @@ -5421,7 +5422,7 @@ int Field_year::store(const char *from, uint len,CHARSET_INFO *cs) error == MY_ERRNO_ERANGE) { *ptr=0; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); + set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); return 1; } if (get_thd()->count_cuted_fields && @@ -5464,7 +5465,7 @@ int Field_year::store(longlong nr, bool unsigned_val) if (nr < 0 || (nr >= 100 && nr <= 1900) || nr > 2155) { *ptr= 0; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); + set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); return 1; } if (nr != 0 || field_length != 4) // 0000 -> 0; 00 -> 2000 @@ -5485,7 +5486,7 @@ int Field_year::store_time_dec(MYSQL_TIME *ltime, uint dec) if (Field_year::store(ltime->year, 0)) return 1; - set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, + set_datetime_warning(Sql_condition::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, &str, ltime->time_type, 1); return 0; } @@ -6046,11 +6047,11 @@ check_string_copy_error(Field_str *field, convert_to_printable(tmp, sizeof(tmp), pos, (end - pos), cs, 6); push_warning_printf(thd, - MYSQL_ERROR::WARN_LEVEL_WARN, + Sql_condition::WARN_LEVEL_WARN, ER_TRUNCATED_WRONG_VALUE_FOR_FIELD, ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD), "string", tmp, field->field_name, - thd->warning_info->current_row_for_warning()); + thd->get_stmt_da()->current_row_for_warning()); return TRUE; } @@ -6085,14 +6086,14 @@ Field_longstr::report_if_important_data(const char *pstr, const char *end, if (test_if_important_data(field_charset, pstr, end)) { if (thd->abort_on_warning) - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_DATA_TOO_LONG, 1); + set_warning(Sql_condition::WARN_LEVEL_WARN, ER_DATA_TOO_LONG, 1); else - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); + set_warning(Sql_condition::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); return 2; } else if (count_spaces) { /* If we lost only spaces then produce a NOTE, not a WARNING */ - set_warning(MYSQL_ERROR::WARN_LEVEL_NOTE, WARN_DATA_TRUNCATED, 1); + set_warning(Sql_condition::WARN_LEVEL_NOTE, WARN_DATA_TRUNCATED, 1); return 2; } } @@ -6158,9 +6159,9 @@ int Field_str::store(double nr) if (error) { if (get_thd()->abort_on_warning) - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_DATA_TOO_LONG, 1); + set_warning(Sql_condition::WARN_LEVEL_WARN, ER_DATA_TOO_LONG, 1); else - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); + set_warning(Sql_condition::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); } return store(buff, length, &my_charset_numeric); } @@ -6223,7 +6224,7 @@ double Field_string::val_real(void) (char*) ptr + field_length)))) { ErrConvString err((char*) ptr, field_length, cs); - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, ER_TRUNCATED_WRONG_VALUE, ER(ER_TRUNCATED_WRONG_VALUE), "DOUBLE", err.ptr()); @@ -6247,7 +6248,7 @@ longlong Field_string::val_int(void) (char*) ptr + field_length)))) { ErrConvString err((char*) ptr, field_length, cs); - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, ER_TRUNCATED_WRONG_VALUE, ER(ER_TRUNCATED_WRONG_VALUE), "INTEGER", err.ptr()); @@ -6283,7 +6284,7 @@ my_decimal *Field_string::val_decimal(my_decimal *decimal_value) if (!get_thd()->no_errors && err) { ErrConvString errmsg((char*) ptr, field_length, charset()); - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, ER_TRUNCATED_WRONG_VALUE, ER(ER_TRUNCATED_WRONG_VALUE), "DECIMAL", errmsg.ptr()); @@ -7695,13 +7696,13 @@ int Field_enum::store(const char *from,uint length,CHARSET_INFO *cs) if (err || end != from+length || tmp > typelib->count) { tmp=0; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); + set_warning(Sql_condition::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); } if (!get_thd()->count_cuted_fields) err= 0; } else - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); + set_warning(Sql_condition::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); } store_type((ulonglong) tmp); return err; @@ -7720,7 +7721,7 @@ int Field_enum::store(longlong nr, bool unsigned_val) int error= 0; if ((ulonglong) nr > typelib->count || nr == 0) { - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); + set_warning(Sql_condition::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); if (nr != 0 || get_thd()->count_cuted_fields) { nr= 0; @@ -7874,11 +7875,11 @@ int Field_set::store(const char *from,uint length,CHARSET_INFO *cs) tmp > (ulonglong) (((longlong) 1 << typelib->count) - (longlong) 1)) { tmp=0; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); + set_warning(Sql_condition::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); } } else if (got_warning) - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); + set_warning(Sql_condition::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); store_type(tmp); return err; } @@ -7898,7 +7899,7 @@ int Field_set::store(longlong nr, bool unsigned_val) if ((ulonglong) nr > max_nr) { nr&= max_nr; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); + set_warning(Sql_condition::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); error=1; } store_type((ulonglong) nr); @@ -8252,9 +8253,9 @@ int Field_bit::store(const char *from, uint length, CHARSET_INFO *cs) set_rec_bits((1 << bit_len) - 1, bit_ptr, bit_ofs, bit_len); memset(ptr, 0xff, bytes_in_rec); if (get_thd()->really_abort_on_warning()) - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_DATA_TOO_LONG, 1); + set_warning(Sql_condition::WARN_LEVEL_WARN, ER_DATA_TOO_LONG, 1); else - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); + set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); return 1; } /* delta is >= -1 here */ @@ -8689,9 +8690,9 @@ int Field_bit_as_char::store(const char *from, uint length, CHARSET_INFO *cs) if (bits) *ptr&= ((1 << bits) - 1); /* set first uchar */ if (get_thd()->really_abort_on_warning()) - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_DATA_TOO_LONG, 1); + set_warning(Sql_condition::WARN_LEVEL_WARN, ER_DATA_TOO_LONG, 1); else - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); + set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); return 1; } bzero(ptr, delta); @@ -9092,7 +9093,7 @@ bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type, /* Otherwise a default of '' is just a warning. */ - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_BLOB_CANT_HAVE_DEFAULT, ER(ER_BLOB_CANT_HAVE_DEFAULT), fld_name); @@ -9610,7 +9611,7 @@ Create_field::Create_field(Field *old_field,Field *orig_field) { char buff[sizeof("YEAR()") + MY_INT64_NUM_DECIMAL_DIGITS + 1]; my_snprintf(buff, sizeof(buff), "YEAR(%lu)", length); - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_NOTE, ER_WARN_DEPRECATED_SYNTAX, ER(ER_WARN_DEPRECATED_SYNTAX), buff, "YEAR(4)"); @@ -9738,11 +9739,11 @@ uint32 Field_blob::max_display_length() *****************************************************************************/ /** - Produce warning or note about data saved into field. +* Produce warning or note about data saved into field. @param level - level of message (Note/Warning/Error) @param code - error code of message to be produced - @param cuted_increment - whenever we should increase cut fields count or not + @param cut_increment - whenever we should increase cut fields count @note This function won't produce warning and increase cut fields counter @@ -9750,11 +9751,16 @@ uint32 Field_blob::max_display_length() if count_cuted_fields == CHECK_FIELD_IGNORE then we ignore notes. This allows us to avoid notes in optimisation, like convert_constant_item(). + + @retval + 1 if count_cuted_fields == CHECK_FIELD_IGNORE and error level is not NOTE + @retval + 0 otherwise */ -void -Field::set_warning(MYSQL_ERROR::enum_warning_level level, uint code, - int cuted_increment) +bool +Field::set_warning(Sql_condition::enum_warning_level level, uint code, + int cut_increment) const { /* If this field was created only for type conversion purposes it @@ -9763,10 +9769,12 @@ Field::set_warning(MYSQL_ERROR::enum_warning_level level, uint code, THD *thd= table ? table->in_use : current_thd; if (thd->count_cuted_fields) { - thd->cuted_fields+= cuted_increment; + thd->cuted_fields+= cut_increment; push_warning_printf(thd, level, code, ER(code), field_name, - thd->warning_info->current_row_for_warning()); + thd->get_stmt_da()->current_row_for_warning()); + return 0; } + return level >= Sql_condition::WARN_LEVEL_WARN; } @@ -9788,12 +9796,12 @@ Field::set_warning(MYSQL_ERROR::enum_warning_level level, uint code, */ -void Field::set_datetime_warning(MYSQL_ERROR::enum_warning_level level, +void Field::set_datetime_warning(Sql_condition::enum_warning_level level, uint code, const ErrConv *str, timestamp_type ts_type, int cuted_increment) { THD *thd= get_thd(); - if (thd->really_abort_on_warning() && level >= MYSQL_ERROR::WARN_LEVEL_WARN) + if (thd->really_abort_on_warning() && level >= Sql_condition::WARN_LEVEL_WARN) make_truncated_value_warning(thd, level, str, ts_type, field_name); else set_warning(level, code, cuted_increment); diff --git a/sql/field.h b/sql/field.h index 162812adfba..f58833629b2 100644 --- a/sql/field.h +++ b/sql/field.h @@ -668,9 +668,9 @@ public: virtual uint repertoire(void) const { return MY_REPERTOIRE_UNICODE30; } virtual void set_derivation(enum Derivation derivation_arg) { } virtual int set_time() { return 1; } - void set_warning(MYSQL_ERROR::enum_warning_level, unsigned int code, - int cuted_increment); - void set_datetime_warning(MYSQL_ERROR::enum_warning_level, uint code, + bool set_warning(Sql_condition::enum_warning_level, unsigned int code, + int cuted_increment) const; + void set_datetime_warning(Sql_condition::enum_warning_level, uint code, const ErrConv *str, timestamp_type ts_type, int cuted_increment); inline bool check_overflow(int op_result) @@ -715,6 +715,30 @@ public: return GEOM_GEOMETRY; } + ha_storage_media field_storage_type() const + { + return (ha_storage_media) + ((flags >> FIELD_FLAGS_STORAGE_MEDIA) & 3); + } + + void set_storage_type(ha_storage_media storage_type_arg) + { + DBUG_ASSERT(field_storage_type() == HA_SM_DEFAULT); + flags |= (storage_type_arg << FIELD_FLAGS_STORAGE_MEDIA); + } + + column_format_type column_format() const + { + return (column_format_type) + ((flags >> FIELD_FLAGS_COLUMN_FORMAT) & 3); + } + + void set_column_format(column_format_type column_format_arg) + { + DBUG_ASSERT(column_format() == COLUMN_FORMAT_TYPE_DEFAULT); + flags |= (column_format_arg << FIELD_FLAGS_COLUMN_FORMAT); + } + key_map get_possible_keys(); /* Hash value */ @@ -2405,6 +2429,7 @@ public: uint8 row,col,sc_length,interval_id; // For rea_create_table uint offset,pack_flag; + bool create_if_not_exists; // Used in ALTER TABLE IF NOT EXISTS /* This is additinal data provided for any computed(virtual) field. @@ -2419,7 +2444,8 @@ public: */ bool stored_in_db; - Create_field() :after(0), option_list(NULL), option_struct(NULL) + Create_field() :after(0), option_list(NULL), option_struct(NULL), + create_if_not_exists(FALSE) {} Create_field(Field *field, Field *orig_field); /* Used to make a clone of this object for ALTER/CREATE TABLE */ @@ -2443,10 +2469,24 @@ public: { return (flags & (BINCMP_FLAG | BINARY_FLAG)) != 0; } + + ha_storage_media field_storage_type() const + { + return (ha_storage_media) + ((flags >> FIELD_FLAGS_STORAGE_MEDIA) & 3); + } + + column_format_type column_format() const + { + return (column_format_type) + ((flags >> FIELD_FLAGS_COLUMN_FORMAT) & 3); + } + uint virtual_col_expr_maxlen() { return 255 - FRM_VCOL_HEADER_SIZE(interval != NULL); } + private: const String empty_set_string; }; diff --git a/sql/field_conv.cc b/sql/field_conv.cc index 6685b334d06..c652ee12c93 100644 --- a/sql/field_conv.cc +++ b/sql/field_conv.cc @@ -133,7 +133,7 @@ set_field_to_null(Field *field) field->reset(); switch (field->table->in_use->count_cuted_fields) { case CHECK_FIELD_WARN: - field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); + field->set_warning(Sql_condition::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); /* fall through */ case CHECK_FIELD_IGNORE: return 0; @@ -202,7 +202,7 @@ set_field_to_null_with_conversions(Field *field, bool no_conversions) } switch (field->table->in_use->count_cuted_fields) { case CHECK_FIELD_WARN: - field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_BAD_NULL_ERROR, 1); + field->set_warning(Sql_condition::WARN_LEVEL_WARN, ER_BAD_NULL_ERROR, 1); /* fall through */ case CHECK_FIELD_IGNORE: return 0; @@ -272,7 +272,7 @@ static void do_copy_nullable_row_to_notnull(Copy_field *copy) if (*copy->null_row || (copy->from_null_ptr && (*copy->from_null_ptr & copy->from_bit))) { - copy->to_field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, + copy->to_field->set_warning(Sql_condition::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); copy->to_field->reset(); } @@ -288,7 +288,7 @@ static void do_copy_not_null(Copy_field *copy) { if (*copy->from_null_ptr & copy->from_bit) { - copy->to_field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, + copy->to_field->set_warning(Sql_condition::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); copy->to_field->reset(); } @@ -440,7 +440,7 @@ static void do_cut_string(Copy_field *copy) (char*) copy->from_ptr + copy->from_length, MY_SEQ_SPACES) < copy->from_length - copy->to_length) { - copy->to_field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, + copy->to_field->set_warning(Sql_condition::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); } } @@ -471,7 +471,7 @@ static void do_cut_string_complex(Copy_field *copy) (char*) from_end, MY_SEQ_SPACES) < (copy->from_length - copy_length)) { - copy->to_field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, + copy->to_field->set_warning(Sql_condition::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); } @@ -510,7 +510,7 @@ static void do_varstring1(Copy_field *copy) length=copy->to_length - 1; if (copy->from_field->table->in_use->count_cuted_fields && copy->to_field) - copy->to_field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, + copy->to_field->set_warning(Sql_condition::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); } *(uchar*) copy->to_ptr= (uchar) length; @@ -531,7 +531,7 @@ static void do_varstring1_mb(Copy_field *copy) if (length < from_length) { if (current_thd->count_cuted_fields) - copy->to_field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, + copy->to_field->set_warning(Sql_condition::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); } *copy->to_ptr= (uchar) length; @@ -547,7 +547,7 @@ static void do_varstring2(Copy_field *copy) length=copy->to_length-HA_KEY_BLOB_LENGTH; if (copy->from_field->table->in_use->count_cuted_fields && copy->to_field) - copy->to_field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, + copy->to_field->set_warning(Sql_condition::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); } int2store(copy->to_ptr,length); @@ -569,7 +569,7 @@ static void do_varstring2_mb(Copy_field *copy) if (length < from_length) { if (current_thd->count_cuted_fields) - copy->to_field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, + copy->to_field->set_warning(Sql_condition::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1); } int2store(copy->to_ptr, length); diff --git a/sql/filesort.cc b/sql/filesort.cc index 9195255c363..2ac784485ce 100644 --- a/sql/filesort.cc +++ b/sql/filesort.cc @@ -391,7 +391,8 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length, MYF(0), ER_THD(thd, ER_FILSORT_ABORT), kill_errno ? ER(kill_errno) : - thd->killed == ABORT_QUERY ? "" : thd->stmt_da->message()); + thd->killed == ABORT_QUERY ? "" : + thd->get_stmt_da()->message()); if (global_system_variables.log_warnings > 1) { diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index cc46219ccb8..287bcb0529c 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -379,11 +379,11 @@ static int ndb_to_mysql_error(const NdbError *ndberr) - Used by replication to see if the error was temporary */ if (ndberr->status == NdbError::TemporaryError) - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, ER_GET_TEMPORARY_ERRMSG, ER(ER_GET_TEMPORARY_ERRMSG), ndberr->code, ndberr->message, "NDB"); else - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, ER_GET_ERRMSG, ER(ER_GET_ERRMSG), ndberr->code, ndberr->message, "NDB"); return error; @@ -650,7 +650,7 @@ static void set_ndb_err(THD *thd, const NdbError &err) { char buf[FN_REFLEN]; ndb_error_string(thd_ndb->m_error_code, buf, sizeof(buf)); - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_GET_ERRMSG, ER(ER_GET_ERRMSG), thd_ndb->m_error_code, buf, "NDB"); } @@ -3206,7 +3206,7 @@ int ha_ndbcluster::update_row(const uchar *old_data, uchar *new_data) undo_res= write_row((uchar *)old_data); if (undo_res) push_warning(current_thd, - MYSQL_ERROR::WARN_LEVEL_WARN, + Sql_condition::WARN_LEVEL_WARN, undo_res, "NDB failed undoing delete at primary key update"); m_primary_key_update= FALSE; @@ -5416,7 +5416,7 @@ int ha_ndbcluster::create(const char *name, { if (create_info->storage_media == HA_SM_MEMORY) { - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_ILLEGAL_HA_CREATE_OPTION, ER(ER_ILLEGAL_HA_CREATE_OPTION), ndbcluster_hton_name, @@ -5471,7 +5471,7 @@ int ha_ndbcluster::create(const char *name, case ROW_TYPE_FIXED: if (field_type_forces_var_part(field->type())) { - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_ILLEGAL_HA_CREATE_OPTION, ER(ER_ILLEGAL_HA_CREATE_OPTION), ndbcluster_hton_name, @@ -5802,7 +5802,7 @@ int ha_ndbcluster::create_index(const char *name, KEY *key_info, case UNIQUE_INDEX: if (check_index_fields_not_null(key_info)) { - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, ER_NULL_COLUMN_IN_INDEX, "Ndb does not support unique index on NULL valued attributes, index access with NULL value will become full table scan"); } @@ -5811,7 +5811,7 @@ int ha_ndbcluster::create_index(const char *name, KEY *key_info, case ORDERED_INDEX: if (key_info->algorithm == HA_KEY_ALG_HASH) { - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, ER_ILLEGAL_HA_CREATE_OPTION, ER(ER_ILLEGAL_HA_CREATE_OPTION), ndbcluster_hton_name, @@ -7284,7 +7284,7 @@ int ndbcluster_find_files(handlerton *hton, THD *thd, file_name->str)); if (ndb_create_table_from_engine(thd, db, file_name->str)) { - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, ER_TABLE_EXISTS_ERROR, "Discover of table %s.%s failed", db, file_name->str); @@ -7310,7 +7310,7 @@ int ndbcluster_find_files(handlerton *hton, THD *thd, file_name->length); DBUG_ASSERT(record); my_hash_delete(&ndb_tables, record); - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, ER_TABLE_EXISTS_ERROR, "Local table %s.%s shadows ndb table", db, file_name->str); @@ -9827,11 +9827,11 @@ char* ha_ndbcluster::get_tablespace_name(THD *thd, char* name, uint name_len) } err: if (ndberr.status == NdbError::TemporaryError) - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_GET_TEMPORARY_ERRMSG, ER(ER_GET_TEMPORARY_ERRMSG), ndberr.code, ndberr.message, "NDB"); else - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_GET_ERRMSG, ER(ER_GET_ERRMSG), ndberr.code, ndberr.message, "NDB"); return 0; @@ -9957,7 +9957,7 @@ int ha_ndbcluster::get_default_no_partitions(HA_CREATE_INFO *create_info) if (adjusted_frag_count(no_fragments, no_nodes, reported_frags)) { push_warning(current_thd, - MYSQL_ERROR::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR, + Sql_condition::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR, "Ndb might have problems storing the max amount of rows specified"); } return (int)reported_frags; @@ -10146,7 +10146,7 @@ uint ha_ndbcluster::set_up_partition_info(partition_info *part_info, { if (!current_thd->variables.new_mode) { - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, ER_ILLEGAL_HA_CREATE_OPTION, ER(ER_ILLEGAL_HA_CREATE_OPTION), ndbcluster_hton_name, diff --git a/sql/ha_ndbcluster_binlog.cc b/sql/ha_ndbcluster_binlog.cc index 1544678de38..61dac31e52a 100644 --- a/sql/ha_ndbcluster_binlog.cc +++ b/sql/ha_ndbcluster_binlog.cc @@ -290,13 +290,13 @@ static void run_query(THD *thd, char *buf, char *end, Thd_ndb *thd_ndb= get_thd_ndb(thd); for (i= 0; no_print_error[i]; i++) if ((thd_ndb->m_error_code == no_print_error[i]) || - (thd->stmt_da->sql_errno() == (unsigned) no_print_error[i])) + (thd->get_stmt_da()->sql_errno() == (unsigned) no_print_error[i])) break; if (!no_print_error[i]) sql_print_error("NDB: %s: error %s %d(ndb: %d) %d %d", buf, - thd->stmt_da->message(), - thd->stmt_da->sql_errno(), + thd->get_stmt_da()->message(), + thd->get_stmt_da()->sql_errno(), thd_ndb->m_error_code, (int) thd->is_error(), thd->is_slave_error); } @@ -310,7 +310,7 @@ static void run_query(THD *thd, char *buf, char *end, is called from ndbcluster_reset_logs(), which is called from mysql_flush(). */ - thd->stmt_da->reset_diagnostics_area(); + thd->get_stmt_da()->reset_diagnostics_area(); thd->variables.option_bits= save_thd_options; thd->set_query(save_thd_query, save_thd_query_length); @@ -984,8 +984,8 @@ static void print_could_not_discover_error(THD *thd, "my_errno: %d", schema->db, schema->name, schema->query, schema->node_id, my_errno); - List_iterator_fast<MYSQL_ERROR> it(thd->warning_info->warn_list()); - MYSQL_ERROR *err; + List_iterator_fast<Sql_condition> it(thd->warning_info->warn_list()); + Sql_condition *err; while ((err= it++)) sql_print_warning("NDB Binlog: (%d)%s", err->get_sql_errno(), err->get_message_text()); @@ -1230,7 +1230,7 @@ ndbcluster_update_slock(THD *thd, char buf[1024]; my_snprintf(buf, sizeof(buf), "Could not release lock on '%s.%s'", db, table_name); - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_GET_ERRMSG, ER(ER_GET_ERRMSG), ndb_error->code, ndb_error->message, buf); } @@ -1559,7 +1559,7 @@ err: } end: if (ndb_error) - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_GET_ERRMSG, ER(ER_GET_ERRMSG), ndb_error->code, ndb_error->message, @@ -2349,8 +2349,8 @@ static int open_ndb_binlog_index(THD *thd, TABLE **ndb_binlog_index) sql_print_error("NDB Binlog: Opening ndb_binlog_index: killed"); else sql_print_error("NDB Binlog: Opening ndb_binlog_index: %d, '%s'", - thd->stmt_da->sql_errno(), - thd->stmt_da->message()); + thd->get_stmt_da()->sql_errno(), + thd->get_stmt_da()->message()); thd->proc_info= save_proc_info; return -1; } @@ -2406,9 +2406,9 @@ int ndb_add_ndb_binlog_index(THD *thd, void *_row) } add_ndb_binlog_index_err: - thd->stmt_da->can_overwrite_status= TRUE; + thd->get_stmt_da()->set_overwrite_status(true); thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd); - thd->stmt_da->can_overwrite_status= FALSE; + thd->get_stmt_da()->set_overwrite_status(false); close_thread_tables(thd); thd->mdl_context.release_transactional_locks(); ndb_binlog_index= 0; @@ -2730,7 +2730,7 @@ ndbcluster_create_event(Ndb *ndb, const NDBTAB *ndbtab, "with BLOB attribute and no PK is not supported", share->key); if (push_warning) - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_ILLEGAL_HA_CREATE_OPTION, ER(ER_ILLEGAL_HA_CREATE_OPTION), ndbcluster_hton_name, @@ -2774,7 +2774,7 @@ ndbcluster_create_event(Ndb *ndb, const NDBTAB *ndbtab, failed, print a warning */ if (push_warning > 1) - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_GET_ERRMSG, ER(ER_GET_ERRMSG), dict->getNdbError().code, dict->getNdbError().message, "NDB"); @@ -2802,7 +2802,7 @@ ndbcluster_create_event(Ndb *ndb, const NDBTAB *ndbtab, dict->dropEvent(my_event.getName())) { if (push_warning > 1) - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_GET_ERRMSG, ER(ER_GET_ERRMSG), dict->getNdbError().code, dict->getNdbError().message, "NDB"); @@ -2821,7 +2821,7 @@ ndbcluster_create_event(Ndb *ndb, const NDBTAB *ndbtab, if (dict->createEvent(my_event)) { if (push_warning > 1) - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_GET_ERRMSG, ER(ER_GET_ERRMSG), dict->getNdbError().code, dict->getNdbError().message, "NDB"); @@ -2834,7 +2834,7 @@ ndbcluster_create_event(Ndb *ndb, const NDBTAB *ndbtab, DBUG_RETURN(-1); } #ifdef NDB_BINLOG_EXTRA_WARNINGS - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_GET_ERRMSG, ER(ER_GET_ERRMSG), 0, "NDB Binlog: Removed trailing event", "NDB"); @@ -2945,7 +2945,7 @@ ndbcluster_create_event_ops(NDB_SHARE *share, const NDBTAB *ndbtab, { sql_print_error("NDB Binlog: Creating NdbEventOperation failed for" " %s",event_name); - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_GET_ERRMSG, ER(ER_GET_ERRMSG), ndb->getNdbError().code, ndb->getNdbError().message, @@ -2994,7 +2994,7 @@ ndbcluster_create_event_ops(NDB_SHARE *share, const NDBTAB *ndbtab, sql_print_error("NDB Binlog: Creating NdbEventOperation" " blob field %u handles failed (code=%d) for %s", j, op->getNdbError().code, event_name); - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_GET_ERRMSG, ER(ER_GET_ERRMSG), op->getNdbError().code, op->getNdbError().message, @@ -3033,7 +3033,7 @@ ndbcluster_create_event_ops(NDB_SHARE *share, const NDBTAB *ndbtab, retries= 0; if (retries == 0) { - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_GET_ERRMSG, ER(ER_GET_ERRMSG), op->getNdbError().code, op->getNdbError().message, "NDB"); @@ -3101,7 +3101,7 @@ ndbcluster_handle_drop_table(Ndb *ndb, const char *event_name, if (dict->getNdbError().code != 4710) { /* drop event failed for some reason, issue a warning */ - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_GET_ERRMSG, ER(ER_GET_ERRMSG), dict->getNdbError().code, dict->getNdbError().message, "NDB"); @@ -4277,9 +4277,9 @@ err: sql_print_information("Stopping Cluster Binlog"); DBUG_PRINT("info",("Shutting down cluster binlog thread")); thd->proc_info= "Shutting down"; - thd->stmt_da->can_overwrite_status= TRUE; + thd->get_stmt_da()->set_overwrite_status(true); thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd); - thd->stmt_da->can_overwrite_status= FALSE; + thd->get_stmt_da()->set_overwrite_status(false); close_thread_tables(thd); thd->mdl_context.release_transactional_locks(); mysql_mutex_lock(&injector_mutex); diff --git a/sql/ha_ndbcluster_cond.cc b/sql/ha_ndbcluster_cond.cc index 85dca73c046..4fb780f94d1 100644 --- a/sql/ha_ndbcluster_cond.cc +++ b/sql/ha_ndbcluster_cond.cc @@ -1375,7 +1375,7 @@ ha_ndbcluster_cond::generate_scan_filter(NdbScanOperation *op) { // err.message has static storage DBUG_PRINT("info", ("%s", err.message)); - push_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning(current_thd, Sql_condition::WARN_LEVEL_WARN, err.code, err.message); ret=0; } diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index a77909ffea0..86842aa6215 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -62,8 +62,6 @@ #include "sql_table.h" // tablename_to_filename #include "key.h" #include "sql_plugin.h" -#include "table.h" /* HA_DATA_PARTITION */ - #include "debug_sync.h" static const char *ha_par_ext= ".par"; @@ -79,6 +77,24 @@ static uint partition_flags(); static uint alter_table_flags(uint flags); +#ifdef HAVE_PSI_INTERFACE +PSI_mutex_key key_partition_auto_inc_mutex; + +static PSI_mutex_info all_partition_mutexes[]= +{ + { &key_partition_auto_inc_mutex, "Partition_share::auto_inc_mutex", 0} +}; + +static void init_partition_psi_keys(void) +{ + const char* category= "partition"; + int count; + + count= array_elements(all_partition_mutexes); + mysql_mutex_register(category, all_partition_mutexes, count); +} +#endif /* HAVE_PSI_INTERFACE */ + static int partition_initialize(void *p) { @@ -93,10 +109,44 @@ static int partition_initialize(void *p) partition_hton->flags= HTON_NOT_USER_SELECTABLE | HTON_HIDDEN | HTON_TEMPORARY_NOT_SUPPORTED; - +#ifdef HAVE_PSI_INTERFACE + init_partition_psi_keys(); +#endif return 0; } + +/** + Initialize and allocate space for partitions shares. + + @param num_parts Number of partitions to allocate storage for. + + @return Operation status. + @retval true Failure (out of memory). + @retval false Success. +*/ + +bool Partition_share::init(uint num_parts) +{ + DBUG_ENTER("Partition_share::init"); + mysql_mutex_init(key_partition_auto_inc_mutex, + &auto_inc_mutex, + MY_MUTEX_INIT_FAST); + auto_inc_initialized= false; + partition_name_hash_initialized= false; + next_auto_inc_val= 0; + partitions_share_refs= new Parts_share_refs; + if (!partitions_share_refs) + DBUG_RETURN(true); + if (partitions_share_refs->init(num_parts)) + { + delete partitions_share_refs; + DBUG_RETURN(true); + } + DBUG_RETURN(false); +} + + /* Create new partition handler @@ -151,7 +201,7 @@ static uint alter_table_flags(uint flags __attribute__((unused))) HA_FAST_CHANGE_PARTITION); } -const uint ha_partition::NO_CURRENT_PART_ID= 0xFFFFFFFF; +const uint32 ha_partition::NO_CURRENT_PART_ID= NOT_A_PARTITION_ID; /* Constructor method @@ -224,6 +274,8 @@ ha_partition::ha_partition(handlerton *hton, TABLE_SHARE *share, m_is_sub_partitioned= m_part_info->is_sub_partitioned(); m_is_clone_of= clone_arg; m_clone_mem_root= clone_mem_root_arg; + part_share= clone_arg->part_share; + m_tot_parts= clone_arg->m_tot_parts; DBUG_VOID_RETURN; } @@ -289,6 +341,8 @@ void ha_partition::init_handler_variables() m_is_sub_partitioned= 0; m_is_clone_of= NULL; m_clone_mem_root= NULL; + part_share= NULL; + m_new_partitions_share_refs.empty(); m_part_ids_sorted_by_num_of_records= NULL; #ifdef DONT_HAVE_TO_BE_INITALIZED @@ -311,6 +365,8 @@ void ha_partition::init_handler_variables() ha_partition::~ha_partition() { DBUG_ENTER("ha_partition::~ha_partition()"); + if (m_new_partitions_share_refs.elements) + m_new_partitions_share_refs.delete_elements(); if (m_file != NULL) { uint i; @@ -461,7 +517,7 @@ int ha_partition::delete_table(const char *name) { DBUG_ENTER("ha_partition::delete_table"); - DBUG_RETURN(del_ren_cre_table(name, NULL, NULL, NULL)); + DBUG_RETURN(del_ren_table(name, NULL)); } @@ -491,7 +547,7 @@ int ha_partition::rename_table(const char *from, const char *to) { DBUG_ENTER("ha_partition::rename_table"); - DBUG_RETURN(del_ren_cre_table(from, to, NULL, NULL)); + DBUG_RETURN(del_ren_table(from, to)); } @@ -581,24 +637,86 @@ int ha_partition::create_handler_files(const char *path, int ha_partition::create(const char *name, TABLE *table_arg, HA_CREATE_INFO *create_info) { - char t_name[FN_REFLEN]; + int error; + char name_buff[FN_REFLEN], name_lc_buff[FN_REFLEN]; + char *name_buffer_ptr; + const char *path; + uint i; + List_iterator_fast <partition_element> part_it(m_part_info->partitions); + partition_element *part_elem; + handler **file, **abort_file; DBUG_ENTER("ha_partition::create"); - if (create_info->used_fields & HA_CREATE_USED_CONNECTION) + DBUG_ASSERT(*fn_rext((char*)name) == '\0'); + + /* Not allowed to create temporary partitioned tables */ + if (create_info && create_info->options & HA_LEX_CREATE_TMP_TABLE) { - my_error(ER_CONNECT_TO_FOREIGN_DATA_SOURCE, MYF(0), - "CONNECTION not valid for partition"); - DBUG_RETURN(1); + my_error(ER_PARTITION_NO_TEMPORARY, MYF(0)); + DBUG_RETURN(TRUE); } - strmov(t_name, name); - DBUG_ASSERT(*fn_rext((char*)name) == '\0'); - if (del_ren_cre_table(t_name, NULL, table_arg, create_info)) + if (get_from_handler_file(name, ha_thd()->mem_root, false)) + DBUG_RETURN(TRUE); + DBUG_ASSERT(m_file_buffer); + DBUG_PRINT("enter", ("name: (%s)", name)); + name_buffer_ptr= m_name_buffer_ptr; + file= m_file; + /* + Since ha_partition has HA_FILE_BASED, it must alter underlying table names + if they do not have HA_FILE_BASED and lower_case_table_names == 2. + See Bug#37402, for Mac OS X. + The appended #P#<partname>[#SP#<subpartname>] will remain in current case. + Using the first partitions handler, since mixing handlers is not allowed. + */ + path= get_canonical_filename(*file, name, name_lc_buff); + for (i= 0; i < m_part_info->num_parts; i++) { - handler::delete_table(t_name); - DBUG_RETURN(1); + part_elem= part_it++; + if (m_is_sub_partitioned) + { + uint j; + List_iterator_fast <partition_element> sub_it(part_elem->subpartitions); + for (j= 0; j < m_part_info->num_subparts; j++) + { + part_elem= sub_it++; + create_partition_name(name_buff, path, name_buffer_ptr, + NORMAL_PART_NAME, FALSE); + if ((error= set_up_table_before_create(table_arg, name_buff, + create_info, part_elem)) || + ((error= (*file)->ha_create(name_buff, table_arg, create_info)))) + goto create_error; + + name_buffer_ptr= strend(name_buffer_ptr) + 1; + file++; + } + } + else + { + create_partition_name(name_buff, path, name_buffer_ptr, + NORMAL_PART_NAME, FALSE); + if ((error= set_up_table_before_create(table_arg, name_buff, + create_info, part_elem)) || + ((error= (*file)->ha_create(name_buff, table_arg, create_info)))) + goto create_error; + + name_buffer_ptr= strend(name_buffer_ptr) + 1; + file++; + } } DBUG_RETURN(0); + +create_error: + name_buffer_ptr= m_name_buffer_ptr; + for (abort_file= file, file= m_file; file < abort_file; file++) + { + create_partition_name(name_buff, path, name_buffer_ptr, NORMAL_PART_NAME, + FALSE); + (void) (*file)->ha_delete_table((const char*) name_buff); + name_buffer_ptr= strend(name_buffer_ptr) + 1; + } + handler::delete_table(name); + DBUG_RETURN(error); } @@ -1309,10 +1427,9 @@ int ha_partition::prepare_new_partition(TABLE *tbl, int error; DBUG_ENTER("prepare_new_partition"); - if ((error= set_up_table_before_create(tbl, part_name, create_info, - 0, p_elem))) + if ((error= set_up_table_before_create(tbl, part_name, create_info, p_elem))) goto error_create; - tbl->s->connect_string = p_elem->connect_string; + if ((error= file->ha_create(part_name, tbl, create_info))) { /* @@ -1327,7 +1444,8 @@ int ha_partition::prepare_new_partition(TABLE *tbl, goto error_create; } DBUG_PRINT("info", ("partition %s created", part_name)); - if ((error= file->ha_open(tbl, part_name, m_mode, m_open_test_lock))) + if ((error= file->ha_open(tbl, part_name, m_mode, + m_open_test_lock | HA_OPEN_NO_PSI_CALL))) goto error_open; DBUG_PRINT("info", ("partition %s opened", part_name)); /* @@ -1488,7 +1606,7 @@ int ha_partition::change_partitions(HA_CREATE_INFO *create_info, (m_reorged_parts + 1)))) { mem_alloc_error(sizeof(handler*)*(m_reorged_parts+1)); - DBUG_RETURN(ER_OUTOFMEMORY); + DBUG_RETURN(HA_ERR_OUT_OF_MEM); } /* @@ -1520,7 +1638,7 @@ int ha_partition::change_partitions(HA_CREATE_INFO *create_info, (2*(num_remain_partitions + 1))))) { mem_alloc_error(sizeof(handler*)*2*(num_remain_partitions+1)); - DBUG_RETURN(ER_OUTOFMEMORY); + DBUG_RETURN(HA_ERR_OUT_OF_MEM); } m_added_file= &new_file_array[num_remain_partitions + 1]; @@ -1590,15 +1708,32 @@ int ha_partition::change_partitions(HA_CREATE_INFO *create_info, part_elem->part_state == PART_TO_BE_ADDED) { uint j= 0; - do - { - if (!(new_file_array[part_count++]= + Parts_share_refs *p_share_refs; + /* + The Handler_shares for each partition's handler can be allocated + within this handler, since there will not be any more instances of the + new partitions, until the table is reopened after the ALTER succeeded. + */ + p_share_refs= new Parts_share_refs; + if (!p_share_refs) + DBUG_RETURN(HA_ERR_OUT_OF_MEM); + if (p_share_refs->init(num_subparts)) + DBUG_RETURN(HA_ERR_OUT_OF_MEM); + if (m_new_partitions_share_refs.push_back(p_share_refs)) + DBUG_RETURN(HA_ERR_OUT_OF_MEM); + { + handler **new_file= &new_file_array[part_count++]; + if (!(*new_file= get_new_handler(table->s, thd->mem_root, part_elem->engine_type))) { mem_alloc_error(sizeof(handler)); - DBUG_RETURN(ER_OUTOFMEMORY); + DBUG_RETURN(HA_ERR_OUT_OF_MEM); + } + if ((*new_file)->set_ha_share_ref(&p_share_refs->ha_shares[j])) + { + DBUG_RETURN(HA_ERR_OUT_OF_MEM); } } while (++j < num_subparts); if (part_elem->part_state == PART_CHANGED) @@ -1839,6 +1974,16 @@ void ha_partition::update_create_info(HA_CREATE_INFO *create_info) } +/** + Change the internal TABLE_SHARE pointer + + @param table_arg TABLE object + @param share New share to use + + @note Is used in error handling in ha_delete_table. + All handlers should exist (lock_partitions should not be used) +*/ + void ha_partition::change_table_ptr(TABLE *table_arg, TABLE_SHARE *share) { handler **file_array; @@ -1890,33 +2035,25 @@ char *ha_partition::update_table_comment(const char *comment) -/* - Handle delete, rename and create table +/** + Handle delete and rename table - SYNOPSIS - del_ren_cre_table() - from Full path of old table - to Full path of new table - table_arg Table object - create_info Create info + @param from Full path of old table + @param to Full path of new table - RETURN VALUE - >0 Error - 0 Success + @return Operation status + @retval >0 Error + @retval 0 Success - DESCRIPTION - Common routine to handle delete_table and rename_table. - The routine uses the partition handler file to get the - names of the partition instances. Both these routines - are called after creating the handler without table - object and thus the file is needed to discover the - names of the partitions and the underlying storage engines. + @note Common routine to handle delete_table and rename_table. + The routine uses the partition handler file to get the + names of the partition instances. Both these routines + are called after creating the handler without table + object and thus the file is needed to discover the + names of the partitions and the underlying storage engines. */ -uint ha_partition::del_ren_cre_table(const char *from, - const char *to, - TABLE *table_arg, - HA_CREATE_INFO *create_info) +uint ha_partition::del_ren_table(const char *from, const char *to) { int save_error= 0; int error; @@ -1927,14 +2064,7 @@ uint ha_partition::del_ren_cre_table(const char *from, const char *to_path= NULL; uint i; handler **file, **abort_file; - DBUG_ENTER("del_ren_cre_table()"); - - /* Not allowed to create temporary partitioned tables */ - if (create_info && create_info->options & HA_LEX_CREATE_TMP_TABLE) - { - my_error(ER_PARTITION_NO_TEMPORARY, MYF(0)); - DBUG_RETURN(TRUE); - } + DBUG_ENTER("ha_partition::del_ren_table"); if (get_from_handler_file(from, ha_thd()->mem_root, false)) DBUG_RETURN(TRUE); @@ -1942,7 +2072,7 @@ uint ha_partition::del_ren_cre_table(const char *from, DBUG_PRINT("enter", ("from: (%s) to: (%s)", from, to ? to : "(nil)")); name_buffer_ptr= m_name_buffer_ptr; file= m_file; - if (to == NULL && table_arg == NULL) + if (to == NULL) { /* Delete table, start by delete the .par file. If error, break, otherwise @@ -1975,16 +2105,9 @@ uint ha_partition::del_ren_cre_table(const char *from, if (error) goto rename_error; } - else if (table_arg == NULL) // delete branch - error= (*file)->ha_delete_table(from_buff); - else + else // delete branch { - if ((error= set_up_table_before_create(table_arg, from_buff, - create_info, i, NULL)) || - parse_engine_table_options(ha_thd(), (*file)->ht, - (*file)->table_share) || - ((error= (*file)->ha_create(from_buff, table_arg, create_info)))) - goto create_error; + error= (*file)->ha_delete_table(from_buff); } name_buffer_ptr= strend(name_buffer_ptr) + 1; if (error) @@ -2001,16 +2124,6 @@ uint ha_partition::del_ren_cre_table(const char *from, } } DBUG_RETURN(save_error); -create_error: - name_buffer_ptr= m_name_buffer_ptr; - for (abort_file= file, file= m_file; file < abort_file; file++) - { - create_partition_name(from_buff, from_path, name_buffer_ptr, NORMAL_PART_NAME, - FALSE); - (void) (*file)->ha_delete_table((const char*) from_buff); - name_buffer_ptr= strend(name_buffer_ptr) + 1; - } - DBUG_RETURN(error); rename_error: name_buffer_ptr= m_name_buffer_ptr; for (abort_file= file, file= m_file; file < abort_file; file++) @@ -2027,47 +2140,6 @@ rename_error: DBUG_RETURN(error); } -/* - Find partition based on partition id - - SYNOPSIS - find_partition_element() - part_id Partition id of partition looked for - - RETURN VALUE - >0 Reference to partition_element - 0 Partition not found -*/ - -partition_element *ha_partition::find_partition_element(uint part_id) -{ - uint i; - uint curr_part_id= 0; - List_iterator_fast <partition_element> part_it(m_part_info->partitions); - - for (i= 0; i < m_part_info->num_parts; i++) - { - partition_element *part_elem; - part_elem= part_it++; - if (m_is_sub_partitioned) - { - uint j; - List_iterator_fast <partition_element> sub_it(part_elem->subpartitions); - for (j= 0; j < m_part_info->num_subparts; j++) - { - part_elem= sub_it++; - if (part_id == curr_part_id++) - return part_elem; - } - } - else if (part_id == curr_part_id++) - return part_elem; - } - DBUG_ASSERT(0); - my_error(ER_OUT_OF_RESOURCES, MYF(ME_FATALERROR)); - return NULL; -} - uint ha_partition::count_query_cache_dependant_tables(uint8 *tables_type) { DBUG_ENTER("ha_partition::count_query_cache_dependant_tables"); @@ -2196,31 +2268,28 @@ my_bool ha_partition::register_query_cache_dependant_tables(THD *thd, } -/* - Set up table share object before calling create on underlying handler - - SYNOPSIS - set_up_table_before_create() - table Table object - info Create info - part_id Partition id of partition to set-up +/** + Set up table share object before calling create on underlying handler - RETURN VALUE - TRUE Error - FALSE Success + @param table Table object + @param info Create info + @param part_elem[in,out] Pointer to used partition_element, searched if NULL - DESCRIPTION - Set up - 1) Comment on partition - 2) MAX_ROWS, MIN_ROWS on partition - 3) Index file name on partition - 4) Data file name on partition + @return status + @retval TRUE Error + @retval FALSE Success + + @details + Set up + 1) Comment on partition + 2) MAX_ROWS, MIN_ROWS on partition + 3) Index file name on partition + 4) Data file name on partition */ int ha_partition::set_up_table_before_create(TABLE *tbl, const char *partition_name_with_path, HA_CREATE_INFO *info, - uint part_id, partition_element *part_elem) { int error= 0; @@ -2228,12 +2297,10 @@ int ha_partition::set_up_table_before_create(TABLE *tbl, THD *thd= ha_thd(); DBUG_ENTER("set_up_table_before_create"); + DBUG_ASSERT(part_elem); + if (!part_elem) - { - part_elem= find_partition_element(part_id); - if (!part_elem) - DBUG_RETURN(1); // Fatal error - } + DBUG_RETURN(1); tbl->s->max_rows= part_elem->part_max_rows; tbl->s->min_rows= part_elem->part_min_rows; partition_name= strrchr(partition_name_with_path, FN_LIBCHAR); @@ -2464,11 +2531,16 @@ bool ha_partition::create_handler_file(const char *name) void ha_partition::clear_handler_file() { if (m_engine_array) + { plugin_unlock_list(NULL, m_engine_array, m_tot_parts); - free_root(&m_mem_root, MYF(MY_KEEP_PREALLOC)); - m_file_buffer= NULL; - m_engine_array= NULL; - m_connect_string= NULL; + my_free(m_engine_array); + m_engine_array= NULL; + } + if (m_file_buffer) + { + my_free(m_file_buffer); + m_file_buffer= NULL; + } } @@ -2496,8 +2568,7 @@ bool ha_partition::create_handlers(MEM_ROOT *mem_root) for (i= 0; i < m_tot_parts; i++) { handlerton *hton= plugin_data(m_engine_array[i], handlerton*); - if (!(m_file[i]= get_new_handler(table_share, mem_root, - hton))) + if (!(m_file[i]= get_new_handler(table_share, mem_root, hton))) DBUG_RETURN(TRUE); DBUG_PRINT("info", ("engine_type: %u", hton->db_type)); } @@ -2780,17 +2851,296 @@ bool ha_partition::get_from_handler_file(const char *name, MEM_ROOT *mem_root, /** - A destructor for partition-specific TABLE_SHARE data. + Get the partition name. + + @param part Struct containing name and length + @param[out] length Length of the name + + @return Partition name +*/ + +static uchar *get_part_name(PART_NAME_DEF *part, size_t *length, + my_bool not_used __attribute__((unused))) +{ + *length= part->length; + return part->partition_name; +} + + +/** + Insert a partition name in the partition_name_hash. + + @param name Name of partition + @param part_id Partition id (number) + @param is_subpart Set if the name belongs to a subpartition + + @return Operation status + @retval true Failure + @retval false Sucess +*/ + +bool ha_partition::insert_partition_name_in_hash(const char *name, uint part_id, + bool is_subpart) +{ + PART_NAME_DEF *part_def; + uchar *part_name; + uint part_name_length; + DBUG_ENTER("ha_partition::insert_partition_name_in_hash"); + /* + Calculate and store the length here, to avoid doing it when + searching the hash. + */ + part_name_length= strlen(name); + /* + Must use memory that lives as long as table_share. + Freed in the Partition_share destructor. + Since we use my_multi_malloc, then my_free(part_def) will also free + part_name, as a part of my_hash_free. + */ + if (!my_multi_malloc(MY_WME, + &part_def, sizeof(PART_NAME_DEF), + &part_name, part_name_length + 1, + NULL)) + DBUG_RETURN(true); + memcpy(part_name, name, part_name_length + 1); + part_def->partition_name= part_name; + part_def->length= part_name_length; + part_def->part_id= part_id; + part_def->is_subpart= is_subpart; + if (my_hash_insert(&part_share->partition_name_hash, (uchar *) part_def)) + { + my_free(part_def); + DBUG_RETURN(true); + } + DBUG_RETURN(false); +} + + +/** + Populate the partition_name_hash in part_share. +*/ + +bool ha_partition::populate_partition_name_hash() +{ + List_iterator<partition_element> part_it(m_part_info->partitions); + uint num_parts= m_part_info->num_parts; + uint num_subparts= m_is_sub_partitioned ? m_part_info->num_subparts : 1; + uint tot_names; + uint i= 0; + DBUG_ASSERT(part_share); + + DBUG_ENTER("ha_partition::populate_partition_name_hash"); + + /* + partition_name_hash is only set once and never changed + -> OK to check without locking. + */ + + if (part_share->partition_name_hash_initialized) + DBUG_RETURN(false); + lock_shared_ha_data(); + if (part_share->partition_name_hash_initialized) + { + unlock_shared_ha_data(); + DBUG_RETURN(false); + } + tot_names= m_is_sub_partitioned ? m_tot_parts + num_parts : num_parts; + if (my_hash_init(&part_share->partition_name_hash, + system_charset_info, tot_names, 0, 0, + (my_hash_get_key) get_part_name, + my_free, HASH_UNIQUE)) + { + unlock_shared_ha_data(); + DBUG_RETURN(TRUE); + } + + do + { + partition_element *part_elem= part_it++; + DBUG_ASSERT(part_elem->part_state == PART_NORMAL); + if (part_elem->part_state == PART_NORMAL) + { + if (insert_partition_name_in_hash(part_elem->partition_name, + i * num_subparts, false)) + goto err; + if (m_is_sub_partitioned) + { + List_iterator<partition_element> + subpart_it(part_elem->subpartitions); + partition_element *sub_elem; + uint j= 0; + do + { + sub_elem= subpart_it++; + if (insert_partition_name_in_hash(sub_elem->partition_name, + i * num_subparts + j, true)) + goto err; + + } while (++j < num_subparts); + } + } + } while (++i < num_parts); + + part_share->partition_name_hash_initialized= true; + unlock_shared_ha_data(); + + DBUG_RETURN(FALSE); +err: + my_hash_free(&part_share->partition_name_hash); + unlock_shared_ha_data(); + + DBUG_RETURN(TRUE); +} + + +/** + Set Handler_share pointer and allocate Handler_share pointers + for each partition and set those. + + @param ha_share_arg Where to store/retrieve the Partitioning_share pointer + to be shared by all instances of the same table. + + @return Operation status + @retval true Failure + @retval false Sucess +*/ + +bool ha_partition::set_ha_share_ref(Handler_share **ha_share_arg) +{ + Handler_share **ha_shares; + uint i; + DBUG_ENTER("ha_partition::set_ha_share_ref"); + + DBUG_ASSERT(!part_share); + DBUG_ASSERT(table_share); + DBUG_ASSERT(!m_is_clone_of); + DBUG_ASSERT(m_tot_parts); + if (handler::set_ha_share_ref(ha_share_arg)) + DBUG_RETURN(true); + if (!(part_share= get_share())) + DBUG_RETURN(true); + DBUG_ASSERT(part_share->partitions_share_refs); + DBUG_ASSERT(part_share->partitions_share_refs->num_parts >= m_tot_parts); + ha_shares= part_share->partitions_share_refs->ha_shares; + for (i= 0; i < m_tot_parts; i++) + { + if (m_file[i]->set_ha_share_ref(&ha_shares[i])) + DBUG_RETURN(true); + } + DBUG_RETURN(false); +} + + +/** + Get the PARTITION_SHARE for the table. + + @return Operation status + @retval true Error + @retval false Success + + @note Gets or initializes the Partition_share object used by partitioning. + The Partition_share is used for handling the auto_increment etc. +*/ + +Partition_share *ha_partition::get_share() +{ + Partition_share *tmp_share; + DBUG_ENTER("ha_partition::get_share"); + DBUG_ASSERT(table_share); + + lock_shared_ha_data(); + if (!(tmp_share= static_cast<Partition_share*>(get_ha_share_ptr()))) + { + tmp_share= new Partition_share; + if (!tmp_share) + goto err; + if (tmp_share->init(m_tot_parts)) + { + delete tmp_share; + tmp_share= NULL; + goto err; + } + set_ha_share_ptr(static_cast<Handler_share*>(tmp_share)); + } +err: + unlock_shared_ha_data(); + DBUG_RETURN(tmp_share); +} + + +/** + Helper function for freeing all internal bitmaps. +*/ + +void ha_partition::free_partition_bitmaps() +{ + /* Initialize the bitmap we use to minimize ha_start_bulk_insert calls */ + bitmap_free(&m_bulk_insert_started); + bitmap_free(&m_locked_partitions); + bitmap_free(&m_partitions_to_reset); + bitmap_free(&m_key_not_found_partitions); +} + + +/** + Helper function for initializing all internal bitmaps. */ -void ha_data_partition_destroy(HA_DATA_PARTITION* ha_part_data) +bool ha_partition::init_partition_bitmaps() { - if (ha_part_data) + DBUG_ENTER("ha_partition::init_partition_bitmaps"); + /* Initialize the bitmap we use to minimize ha_start_bulk_insert calls */ + if (bitmap_init(&m_bulk_insert_started, NULL, m_tot_parts + 1, FALSE)) + DBUG_RETURN(true); + bitmap_clear_all(&m_bulk_insert_started); + + /* Initialize the bitmap we use to keep track of locked partitions */ + if (bitmap_init(&m_locked_partitions, NULL, m_tot_parts, FALSE)) + { + bitmap_free(&m_bulk_insert_started); + DBUG_RETURN(true); + } + bitmap_clear_all(&m_locked_partitions); + + /* + Initialize the bitmap we use to keep track of partitions which may have + something to reset in ha_reset(). + */ + if (bitmap_init(&m_partitions_to_reset, NULL, m_tot_parts, FALSE)) + { + bitmap_free(&m_bulk_insert_started); + bitmap_free(&m_locked_partitions); + DBUG_RETURN(true); + } + bitmap_clear_all(&m_partitions_to_reset); + + /* + Initialize the bitmap we use to keep track of partitions which returned + HA_ERR_KEY_NOT_FOUND from index_read_map. + */ + if (bitmap_init(&m_key_not_found_partitions, NULL, m_tot_parts, FALSE)) + { + bitmap_free(&m_bulk_insert_started); + bitmap_free(&m_locked_partitions); + bitmap_free(&m_partitions_to_reset); + DBUG_RETURN(true); + } + bitmap_clear_all(&m_key_not_found_partitions); + m_key_not_found= false; + /* Initialize the bitmap for read/lock_partitions */ + if (!m_is_clone_of) { - mysql_mutex_destroy(&ha_part_data->LOCK_auto_inc); + DBUG_ASSERT(!m_clone_mem_root); + if (m_part_info->set_partition_bitmaps(NULL)) + { + free_partition_bitmaps(); + DBUG_RETURN(true); + } } + DBUG_RETURN(false); } + /* Open handler object @@ -2820,7 +3170,6 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked) int error= HA_ERR_INITIALIZATION; handler **file; char name_buff[FN_REFLEN]; - bool is_not_tmp_table= (table_share->tmp_table == NO_TMP_TABLE); ulonglong check_table_flags; DBUG_ENTER("ha_partition::open"); @@ -2832,6 +3181,10 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked) if (get_from_handler_file(name, &table->mem_root, test(m_is_clone_of))) DBUG_RETURN(error); name_buffer_ptr= m_name_buffer_ptr; + if (populate_partition_name_hash()) + { + DBUG_RETURN(HA_ERR_INITIALIZATION); + } m_start_key.length= 0; m_rec0= table->record[0]; m_rec_length= table_share->stored_rec_length; @@ -2846,32 +3199,10 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked) m_part_ids_sorted_by_num_of_records[i]= i; } - /* Initialize the bitmap we use to minimize ha_start_bulk_insert calls */ - if (bitmap_init(&m_bulk_insert_started, NULL, m_tot_parts + 1, FALSE)) + if (init_partition_bitmaps()) DBUG_RETURN(error); - bitmap_clear_all(&m_bulk_insert_started); - /* - Initialize the bitmap we use to keep track of partitions which returned - HA_ERR_KEY_NOT_FOUND from index_read_map. - */ - if (bitmap_init(&m_key_not_found_partitions, NULL, m_tot_parts, FALSE)) - { - bitmap_free(&m_bulk_insert_started); - DBUG_RETURN(error); - } - bitmap_clear_all(&m_key_not_found_partitions); - m_key_not_found= false; - /* Initialize the bitmap we use to determine what partitions are used */ - if (!m_is_clone_of) - { - DBUG_ASSERT(!m_clone_mem_root); - if (bitmap_init(&(m_part_info->used_partitions), NULL, m_tot_parts, TRUE)) - { - bitmap_free(&m_bulk_insert_started); - DBUG_RETURN(error); - } - bitmap_set_all(&(m_part_info->used_partitions)); - } + + DBUG_ASSERT(m_part_info); if (m_is_clone_of) { @@ -2880,7 +3211,10 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked) /* Allocate an array of handler pointers for the partitions handlers. */ alloc_len= (m_tot_parts + 1) * sizeof(handler*); if (!(m_file= (handler **) alloc_root(m_clone_mem_root, alloc_len))) + { + error= HA_ERR_INITIALIZATION; goto err_alloc; + } memset(m_file, 0, alloc_len); /* Populate them by cloning the original partitions. This also opens them. @@ -2891,6 +3225,7 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked) { create_partition_name(name_buff, name, name_buffer_ptr, NORMAL_PART_NAME, FALSE); + /* ::clone() will also set ha_share from the original. */ if (!(m_file[i]= file[i]->clone(name_buff, m_clone_mem_root))) { error= HA_ERR_INITIALIZATION; @@ -2907,11 +3242,12 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked) { create_partition_name(name_buff, name, name_buffer_ptr, NORMAL_PART_NAME, FALSE); - table->s->connect_string = m_connect_string[(uint)(file-m_file)]; - if ((error= (*file)->ha_open(table, name_buff, mode, test_if_locked))) + if ((error= (*file)->ha_open(table, name_buff, mode, + test_if_locked | HA_OPEN_NO_PSI_CALL))) goto err_handler; - bzero(&table->s->connect_string, sizeof(LEX_STRING)); - m_num_locks+= (*file)->lock_count(); + if (m_file == file) + m_num_locks= (*file)->lock_count(); + DBUG_ASSERT(m_num_locks == (*file)->lock_count()); name_buffer_ptr+= strlen(name_buffer_ptr) + 1; } while (*(++file)); } @@ -2955,34 +3291,6 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked) clear_handler_file(); /* - Use table_share->ha_part_data to share auto_increment_value among - all handlers for the same table. - */ - if (is_not_tmp_table) - mysql_mutex_lock(&table_share->LOCK_ha_data); - if (!table_share->ha_part_data) - { - /* currently only needed for auto_increment */ - table_share->ha_part_data= (HA_DATA_PARTITION*) - alloc_root(&table_share->mem_root, - sizeof(HA_DATA_PARTITION)); - if (!table_share->ha_part_data) - { - if (is_not_tmp_table) - mysql_mutex_unlock(&table_share->LOCK_ha_data); - goto err_handler; - } - DBUG_PRINT("info", ("table_share->ha_part_data 0x%p", - table_share->ha_part_data)); - bzero(table_share->ha_part_data, sizeof(HA_DATA_PARTITION)); - table_share->ha_part_data_destroy= ha_data_partition_destroy; - mysql_mutex_init(key_PARTITION_LOCK_auto_inc, - &table_share->ha_part_data->LOCK_auto_inc, - MY_MUTEX_INIT_FAST); - } - if (is_not_tmp_table) - mysql_mutex_unlock(&table_share->LOCK_ha_data); - /* Some handlers update statistics as part of the open call. This will in some cases corrupt the statistics of the partition handler and thus to ensure we have correct statistics we call info from open after @@ -3002,10 +3310,7 @@ err_handler: while (file-- != m_file) (*file)->ha_close(); err_alloc: - bitmap_free(&m_bulk_insert_started); - bitmap_free(&m_key_not_found_partitions); - if (!m_is_clone_of) - bitmap_free(&(m_part_info->used_partitions)); + free_partition_bitmaps(); DBUG_RETURN(error); } @@ -3063,22 +3368,35 @@ handler *ha_partition::clone(const char *name, MEM_ROOT *mem_root) DBUG_ENTER("ha_partition::clone"); new_handler= new (mem_root) ha_partition(ht, table_share, m_part_info, this, mem_root); + if (!new_handler) + DBUG_RETURN(NULL); + + /* + We will not clone each partition's handler here, it will be done in + ha_partition::open() for clones. Also set_ha_share_ref is not needed + here, since 1) ha_share is copied in the constructor used above + 2) each partition's cloned handler will set it from its original. + */ + /* Allocate new_handler->ref here because otherwise ha_open will allocate it on this->table->mem_root and we will not be able to reclaim that memory when the clone handler object is destroyed. */ - if (new_handler && - !(new_handler->ref= (uchar*) alloc_root(mem_root, + if (!(new_handler->ref= (uchar*) alloc_root(mem_root, ALIGN_SIZE(m_ref_length)*2))) - new_handler= NULL; + goto err; - if (new_handler && - new_handler->ha_open(table, name, - table->db_stat, HA_OPEN_IGNORE_IF_LOCKED)) - new_handler= NULL; + if (new_handler->ha_open(table, name, + table->db_stat, + HA_OPEN_IGNORE_IF_LOCKED | HA_OPEN_NO_PSI_CALL)) + goto err; DBUG_RETURN((handler*) new_handler); + +err: + delete new_handler; + DBUG_RETURN(NULL); } @@ -3108,10 +3426,8 @@ int ha_partition::close(void) DBUG_ASSERT(table->s == table_share); destroy_record_priority_queue(); - bitmap_free(&m_bulk_insert_started); - bitmap_free(&m_key_not_found_partitions); - if (!m_is_clone_of) - bitmap_free(&(m_part_info->used_partitions)); + free_partition_bitmaps(); + DBUG_ASSERT(m_part_info); file= m_file; repeat: @@ -3173,41 +3489,64 @@ repeat: int ha_partition::external_lock(THD *thd, int lock_type) { - bool first= TRUE; uint error; - handler **file; + uint i, first_used_partition; + MY_BITMAP *used_partitions; DBUG_ENTER("ha_partition::external_lock"); DBUG_ASSERT(!auto_increment_lock && !auto_increment_safe_stmt_log_lock); - file= m_file; - m_lock_type= lock_type; -repeat: - do + if (lock_type == F_UNLCK) + used_partitions= &m_locked_partitions; + else + used_partitions= &(m_part_info->lock_partitions); + + first_used_partition= bitmap_get_first_set(used_partitions); + + for (i= first_used_partition; + i < m_tot_parts; + i= bitmap_get_next_set(used_partitions, i)) { - DBUG_PRINT("info", ("external_lock(thd, %d) iteration %d", - lock_type, (int) (file - m_file))); - if ((error= (*file)->ha_external_lock(thd, lock_type))) + DBUG_PRINT("info", ("external_lock(thd, %d) part %d", lock_type, i)); + if ((error= m_file[i]->ha_external_lock(thd, lock_type))) { - if (F_UNLCK != lock_type) + if (lock_type != F_UNLCK) goto err_handler; } - } while (*(++file)); + DBUG_PRINT("info", ("external_lock part %u lock %d", i, lock_type)); + if (lock_type != F_UNLCK) + bitmap_set_bit(&m_locked_partitions, i); + } + if (lock_type == F_UNLCK) + { + bitmap_clear_all(used_partitions); + } + else + { + /* Add touched partitions to be included in reset(). */ + bitmap_union(&m_partitions_to_reset, used_partitions); + } - if (first && m_added_file && m_added_file[0]) + if (m_added_file && m_added_file[0]) { + handler **file= m_added_file; DBUG_ASSERT(lock_type == F_UNLCK); - file= m_added_file; - first= FALSE; - goto repeat; + do + { + (void) (*file)->ha_external_lock(thd, lock_type); + } while (*(++file)); } DBUG_RETURN(0); err_handler: - while (file-- != m_file) + uint j; + for (j= first_used_partition; + j < i; + j= bitmap_get_next_set(&m_locked_partitions, j)) { - (*file)->ha_external_lock(thd, F_UNLCK); + (void) m_file[j]->ha_external_lock(thd, F_UNLCK); } + bitmap_clear_all(&m_locked_partitions); DBUG_RETURN(error); } @@ -3262,14 +3601,30 @@ THR_LOCK_DATA **ha_partition::store_lock(THD *thd, THR_LOCK_DATA **to, enum thr_lock_type lock_type) { - handler **file; + uint i; DBUG_ENTER("ha_partition::store_lock"); - file= m_file; - do + DBUG_ASSERT(thd == current_thd); + + /* + This can be called from get_lock_data() in mysql_lock_abort_for_thread(), + even when thd != table->in_use. In that case don't use partition pruning, + but use all partitions instead to avoid using another threads structures. + */ + if (thd != table->in_use) { - DBUG_PRINT("info", ("store lock %d iteration", (int) (file - m_file))); - to= (*file)->store_lock(thd, to, lock_type); - } while (*(++file)); + for (i= 0; i < m_tot_parts; i++) + to= m_file[i]->store_lock(thd, to, lock_type); + } + else + { + for (i= bitmap_get_first_set(&(m_part_info->lock_partitions)); + i < m_tot_parts; + i= bitmap_get_next_set(&m_part_info->lock_partitions, i)) + { + DBUG_PRINT("info", ("store lock %d iteration", i)); + to= m_file[i]->store_lock(thd, to, lock_type); + } + } DBUG_RETURN(to); } @@ -3293,15 +3648,28 @@ THR_LOCK_DATA **ha_partition::store_lock(THD *thd, int ha_partition::start_stmt(THD *thd, thr_lock_type lock_type) { int error= 0; - handler **file; + uint i; + /* Assert that read_partitions is included in lock_partitions */ + DBUG_ASSERT(bitmap_is_subset(&m_part_info->read_partitions, + &m_part_info->lock_partitions)); + /* + m_locked_partitions is set in previous external_lock/LOCK TABLES. + Current statement's lock requests must not include any partitions + not previously locked. + */ + DBUG_ASSERT(bitmap_is_subset(&m_part_info->lock_partitions, + &m_locked_partitions)); DBUG_ENTER("ha_partition::start_stmt"); - file= m_file; - do + for (i= bitmap_get_first_set(&(m_part_info->lock_partitions)); + i < m_tot_parts; + i= bitmap_get_next_set(&m_part_info->lock_partitions, i)) { - if ((error= (*file)->start_stmt(thd, lock_type))) + if ((error= m_file[i]->start_stmt(thd, lock_type))) break; - } while (*(++file)); + /* Add partition to be called in reset(). */ + bitmap_set_bit(&m_partitions_to_reset, i); + } DBUG_RETURN(error); } @@ -3378,7 +3746,7 @@ bool ha_partition::was_semi_consistent_read() { DBUG_ENTER("ha_partition::was_semi_consistent_read"); DBUG_ASSERT(m_last_part < m_tot_parts && - bitmap_is_set(&(m_part_info->used_partitions), m_last_part)); + bitmap_is_set(&(m_part_info->read_partitions), m_last_part)); DBUG_RETURN(m_file[m_last_part]->was_semi_consistent_read()); } @@ -3403,13 +3771,16 @@ bool ha_partition::was_semi_consistent_read() */ void ha_partition::try_semi_consistent_read(bool yes) { - handler **file; + uint i; DBUG_ENTER("ha_partition::try_semi_consistent_read"); - for (file= m_file; *file; file++) + i= bitmap_get_first_set(&(m_part_info->read_partitions)); + DBUG_ASSERT(i != MY_BIT_NONE); + for (; + i < m_tot_parts; + i= bitmap_get_next_set(&m_part_info->read_partitions, i)) { - if (bitmap_is_set(&(m_part_info->used_partitions), (file - m_file))) - (*file)->try_semi_consistent_read(yes); + m_file[i]->try_semi_consistent_read(yes); } DBUG_VOID_RETURN; } @@ -3466,9 +3837,6 @@ int ha_partition::write_row(uchar * buf) THD *thd= ha_thd(); ulonglong saved_sql_mode= thd->variables.sql_mode; bool saved_auto_inc_field_not_null= table->auto_increment_field_not_null; -#ifdef NOT_NEEDED - uchar *rec0= m_rec0; -#endif DBUG_ENTER("ha_partition::write_row"); DBUG_ASSERT(buf == m_rec0); @@ -3478,7 +3846,7 @@ int ha_partition::write_row(uchar * buf) */ if (have_auto_increment) { - if (!table_share->ha_part_data->auto_inc_initialized && + if (!part_share->auto_inc_initialized && !table_share->next_number_keypart) { /* @@ -3515,20 +3883,7 @@ int ha_partition::write_row(uchar * buf) } old_map= dbug_tmp_use_all_columns(table, table->read_set); -#ifdef NOT_NEEDED - if (likely(buf == rec0)) -#endif - error= m_part_info->get_partition_id(m_part_info, &part_id, - &func_value); -#ifdef NOT_NEEDED - else - { - set_field_ptr(m_part_field_array, buf, rec0); - error= m_part_info->get_partition_id(m_part_info, &part_id, - &func_value); - set_field_ptr(m_part_field_array, rec0, buf); - } -#endif + error= m_part_info->get_partition_id(m_part_info, &part_id, &func_value); dbug_tmp_restore_column_map(table->read_set, old_map); if (unlikely(error)) { @@ -3639,7 +3994,7 @@ int ha_partition::update_row(const uchar *old_data, uchar *new_data) exit: /* if updating an auto_increment column, update - table_share->ha_part_data->next_auto_inc_val if needed. + part_share->next_auto_inc_val if needed. (not to be used if auto_increment on secondary field in a multi-column index) mysql_update does not set table->next_number_field, so we use @@ -3652,7 +4007,7 @@ exit: bitmap_is_set(table->write_set, table->found_next_number_field->field_index)) { - if (!table_share->ha_part_data->auto_inc_initialized) + if (!part_share->auto_inc_initialized) info(HA_STATUS_AUTO); set_auto_increment_if_higher(table->found_next_number_field); } @@ -3695,11 +4050,18 @@ int ha_partition::delete_row(const uchar *buf) THD *thd= ha_thd(); DBUG_ENTER("ha_partition::delete_row"); + DBUG_ASSERT(bitmap_is_subset(&m_part_info->full_part_field_set, + table->read_set)); if ((error= get_part_for_delete(buf, m_rec0, m_part_info, &part_id))) { DBUG_RETURN(error); } m_last_part= part_id; + /* Should never call delete_row on a partition which is not read */ + DBUG_ASSERT(bitmap_is_set(&(m_part_info->read_partitions), part_id)); + DBUG_ASSERT(bitmap_is_set(&(m_part_info->lock_partitions), part_id)); + if (!bitmap_is_set(&(m_part_info->lock_partitions), part_id)) + DBUG_RETURN(HA_ERR_NOT_IN_LOCK_PARTITIONS); tmp_disable_binlog(thd); error= m_file[part_id]->ha_delete_row(buf); reenable_binlog(thd); @@ -3763,9 +4125,9 @@ int ha_partition::truncate() it so that it will be initialized again at the next use. */ lock_auto_increment(); - table_share->ha_part_data->next_auto_inc_val= 0; - table_share->ha_part_data->auto_inc_initialized= FALSE; - unlock_auto_increment(); + part_share->next_auto_inc_val= 0; + part_share->auto_inc_initialized= FALSE; + unlock_auto_increment(); file= m_file; do @@ -3805,8 +4167,8 @@ int ha_partition::truncate_partition(Alter_info *alter_info, bool *binlog_stmt) it so that it will be initialized again at the next use. */ lock_auto_increment(); - table_share->ha_part_data->next_auto_inc_val= 0; - table_share->ha_part_data->auto_inc_initialized= FALSE; + part_share->next_auto_inc_val= 0; + part_share->auto_inc_initialized= FALSE; unlock_auto_increment(); *binlog_stmt= true; @@ -3881,6 +4243,7 @@ void ha_partition::start_part_bulk_insert(THD *thd, uint part_id) if (!bitmap_is_set(&m_bulk_insert_started, part_id) && bitmap_is_set(&m_bulk_insert_started, m_tot_parts)) { + DBUG_ASSERT(bitmap_is_set(&(m_part_info->lock_partitions), part_id)); old_buffer_size= thd->variables.read_buff_size; /* Update read_buffer_size for this partition */ thd->variables.read_buff_size= estimate_read_buffer_size(old_buffer_size); @@ -4065,9 +4428,9 @@ int ha_partition::rnd_init(bool scan) } /* Now we see what the index of our first important partition is */ - DBUG_PRINT("info", ("m_part_info->used_partitions: 0x%lx", - (long) m_part_info->used_partitions.bitmap)); - part_id= bitmap_get_first_set(&(m_part_info->used_partitions)); + DBUG_PRINT("info", ("m_part_info->read_partitions: 0x%lx", + (long) m_part_info->read_partitions.bitmap)); + part_id= bitmap_get_first_set(&(m_part_info->read_partitions)); DBUG_PRINT("info", ("m_part_spec.start_part %d", part_id)); if (MY_BIT_NONE == part_id) @@ -4094,13 +4457,12 @@ int ha_partition::rnd_init(bool scan) } else { - for (i= part_id; i < m_tot_parts; i++) + for (i= part_id; + i < m_tot_parts; + i= bitmap_get_next_set(&m_part_info->read_partitions, i)) { - if (bitmap_is_set(&(m_part_info->used_partitions), i)) - { - if ((error= m_file[i]->ha_rnd_init(scan))) - goto err; - } + if ((error= m_file[i]->ha_rnd_init(scan))) + goto err; } } m_scan_value= scan; @@ -4110,10 +4472,12 @@ int ha_partition::rnd_init(bool scan) DBUG_RETURN(0); err: - while ((int)--i >= (int)part_id) + /* Call rnd_end for all previously inited partitions. */ + for (; + part_id < i; + part_id= bitmap_get_next_set(&m_part_info->read_partitions, part_id)) { - if (bitmap_is_set(&(m_part_info->used_partitions), i)) - m_file[i]->ha_rnd_end(); + m_file[part_id]->ha_rnd_end(); } err1: m_scan_value= 2; @@ -4135,7 +4499,6 @@ err1: int ha_partition::rnd_end() { - handler **file; DBUG_ENTER("ha_partition::rnd_end"); switch (m_scan_value) { case 2: // Error @@ -4148,12 +4511,13 @@ int ha_partition::rnd_end() } break; case 0: - file= m_file; - do + uint i; + for (i= bitmap_get_first_set(&m_part_info->read_partitions); + i < m_tot_parts; + i= bitmap_get_next_set(&m_part_info->read_partitions, i)) { - if (bitmap_is_set(&(m_part_info->used_partitions), (file - m_file))) - (*file)->ha_rnd_end(); - } while (*(++file)); + m_file[i]->ha_rnd_end(); + } break; } m_scan_value= 2; @@ -4231,9 +4595,7 @@ int ha_partition::rnd_next(uchar *buf) break; /* Shift to next partition */ - while (++part_id < m_tot_parts && - !bitmap_is_set(&(m_part_info->used_partitions), part_id)) - ; + part_id= bitmap_get_next_set(&m_part_info->read_partitions, part_id); if (part_id >= m_tot_parts) { result= HA_ERR_END_OF_FILE; @@ -4285,6 +4647,7 @@ void ha_partition::position(const uchar *record) { handler *file= m_file[m_last_part]; uint pad_length; + DBUG_ASSERT(bitmap_is_set(&(m_part_info->read_partitions), m_last_part)); DBUG_ENTER("ha_partition::position"); file->position(record); @@ -4337,6 +4700,7 @@ int ha_partition::rnd_pos(uchar * buf, uchar *pos) part_id= uint2korr((const uchar *) pos); DBUG_ASSERT(part_id < m_tot_parts); file= m_file[part_id]; + DBUG_ASSERT(bitmap_is_set(&(m_part_info->read_partitions), part_id)); m_last_part= part_id; DBUG_RETURN(file->rnd_pos(buf, (pos + PARTITION_BYTES_IN_POS))); } @@ -4406,8 +4770,8 @@ bool ha_partition::init_record_priority_queue() if (!m_ordered_rec_buffer) { uint alloc_len; - uint used_parts= bitmap_bits_set(&m_part_info->used_partitions); - /* Allocate record buffer for each used partition. */ + uint used_parts= bitmap_bits_set(&m_part_info->read_partitions); + /* Allocate record buffer for each used partition. */ alloc_len= used_parts * (m_rec_length + PARTITION_BYTES_IN_POS); /* Allocate a key for temporary use when setting up the scan. */ alloc_len+= table_share->max_key_length; @@ -4423,15 +4787,15 @@ bool ha_partition::init_record_priority_queue() setting up the scan. */ char *ptr= (char*) m_ordered_rec_buffer; - uint16 i= 0; - do + uint i; + for (i= bitmap_get_first_set(&m_part_info->read_partitions); + i < m_tot_parts; + i= bitmap_get_next_set(&m_part_info->read_partitions, i)) { - if (bitmap_is_set(&m_part_info->used_partitions, i)) - { - int2store(ptr, i); - ptr+= m_rec_length + PARTITION_BYTES_IN_POS; - } - } while (++i < m_tot_parts); + DBUG_PRINT("info", ("init rec-buf for part %u", i)); + int2store(ptr, i); + ptr+= m_rec_length + PARTITION_BYTES_IN_POS; + } m_start_key.key= (const uchar*)ptr; /* Initialize priority queue, initialized to reading forward. */ if (init_queue(&m_queue, used_parts, (uint) PARTITION_BYTES_IN_POS, @@ -4483,7 +4847,7 @@ void ha_partition::destroy_record_priority_queue() int ha_partition::index_init(uint inx, bool sorted) { int error= 0; - handler **file; + uint i; DBUG_ENTER("ha_partition::index_init"); DBUG_PRINT("info", ("inx %u sorted %u", inx, sorted)); @@ -4532,7 +4896,6 @@ int ha_partition::index_init(uint inx, bool sorted) TODO: handle COUNT(*) queries via unordered scan. */ - uint i; KEY **key_info= m_curr_key_info; do { @@ -4541,17 +4904,31 @@ int ha_partition::index_init(uint inx, bool sorted) (*key_info)->key_part[i].field->field_index); } while (*(++key_info)); } - file= m_file; - do + for (i= bitmap_get_first_set(&m_part_info->read_partitions); + i < m_tot_parts; + i= bitmap_get_next_set(&m_part_info->read_partitions, i)) { - /* TODO RONM: Change to index_init() when code is stable */ - if (bitmap_is_set(&(m_part_info->used_partitions), (file - m_file))) - if ((error= (*file)->ha_index_init(inx, sorted))) - { - DBUG_ASSERT(0); // Should never happen - break; - } - } while (*(++file)); + if ((error= m_file[i]->ha_index_init(inx, sorted))) + goto err; + + DBUG_EXECUTE_IF("ha_partition_fail_index_init", { + i++; + error= HA_ERR_NO_PARTITION_FOUND; + goto err; + }); + } +err: + if (error) + { + /* End the previously initialized indexes. */ + uint j; + for (j= bitmap_get_first_set(&m_part_info->read_partitions); + j < i; + j= bitmap_get_next_set(&m_part_info->read_partitions, j)) + { + (void) m_file[j]->ha_index_end(); + } + } DBUG_RETURN(error); } @@ -4574,19 +4951,19 @@ int ha_partition::index_init(uint inx, bool sorted) int ha_partition::index_end() { int error= 0; - handler **file; + uint i; DBUG_ENTER("ha_partition::index_end"); active_index= MAX_KEY; m_part_spec.start_part= NO_CURRENT_PART_ID; - file= m_file; - do + for (i= bitmap_get_first_set(&m_part_info->read_partitions); + i < m_tot_parts; + i= bitmap_get_next_set(&m_part_info->read_partitions, i)) { int tmp; - if (bitmap_is_set(&(m_part_info->used_partitions), (file - m_file))) - if ((tmp= (*file)->ha_index_end())) - error= tmp; - } while (*(++file)); + if ((tmp= m_file[i]->ha_index_end())) + error= tmp; + } destroy_record_priority_queue(); DBUG_RETURN(error); } @@ -4832,17 +5209,20 @@ int ha_partition::index_read_idx_map(uchar *buf, uint index, or no matching partitions (start_part > end_part) */ DBUG_ASSERT(m_part_spec.start_part >= m_part_spec.end_part); - - for (part= m_part_spec.start_part; part <= m_part_spec.end_part; part++) + /* The start part is must be marked as used. */ + DBUG_ASSERT(m_part_spec.start_part > m_part_spec.end_part || + bitmap_is_set(&(m_part_info->read_partitions), + m_part_spec.start_part)); + + for (part= m_part_spec.start_part; + part <= m_part_spec.end_part; + part= bitmap_get_next_set(&m_part_info->read_partitions, part)) { - if (bitmap_is_set(&(m_part_info->used_partitions), part)) - { - error= m_file[part]->index_read_idx_map(buf, index, key, - keypart_map, find_flag); - if (error != HA_ERR_KEY_NOT_FOUND && - error != HA_ERR_END_OF_FILE) - break; - } + error= m_file[part]->ha_index_read_idx_map(buf, index, key, + keypart_map, find_flag); + if (error != HA_ERR_KEY_NOT_FOUND && + error != HA_ERR_END_OF_FILE) + break; } if (part <= m_part_spec.end_part) m_last_part= part; @@ -5094,7 +5474,7 @@ int ha_partition::partition_scan_set_up(uchar * buf, bool idx_read_flag) Verify this, also bitmap must have at least one bit set otherwise the result from this table is the empty set. */ - uint start_part= bitmap_get_first_set(&(m_part_info->used_partitions)); + uint start_part= bitmap_get_first_set(&(m_part_info->read_partitions)); if (start_part == MY_BIT_NONE) { DBUG_PRINT("info", ("scan with no partition to scan")); @@ -5215,14 +5595,12 @@ int ha_partition::handle_unordered_scan_next_partition(uchar * buf) int saved_error= HA_ERR_END_OF_FILE; DBUG_ENTER("ha_partition::handle_unordered_scan_next_partition"); - for (i= m_part_spec.start_part; i <= m_part_spec.end_part; i++) + for (; + i <= m_part_spec.end_part; + i= bitmap_get_next_set(&m_part_info->read_partitions, i)) { int error; - handler *file; - - if (!(bitmap_is_set(&(m_part_info->used_partitions), i))) - continue; - file= m_file[i]; + handler *file= m_file[i]; m_part_spec.start_part= i; switch (m_index_scan_type) { case partition_read_range: @@ -5321,22 +5699,32 @@ int ha_partition::handle_ordered_index_scan(uchar *buf, bool reverse_order) } m_top_entry= NO_CURRENT_PART_ID; queue_remove_all(&m_queue); + DBUG_ASSERT(bitmap_is_set(&m_part_info->read_partitions, + m_part_spec.start_part)); - DBUG_PRINT("info", ("m_part_spec.start_part %d", m_part_spec.start_part)); + /* + Position part_rec_buf_ptr to point to the first used partition >= + start_part. There may be partitions marked by used_partitions, + but is before start_part. These partitions has allocated record buffers + but is dynamically pruned, so those buffers must be skipped. + */ + for (i= bitmap_get_first_set(&m_part_info->read_partitions); + i < m_part_spec.start_part; + i= bitmap_get_next_set(&m_part_info->read_partitions, i)) + { + part_rec_buf_ptr+= m_rec_length + PARTITION_BYTES_IN_POS; + } + DBUG_PRINT("info", ("m_part_spec.start_part %u first_used_part %u", + m_part_spec.start_part, i)); for (i= m_part_spec.start_part; i <= m_part_spec.end_part; i++) { - if (!(bitmap_is_set(&(m_part_info->used_partitions), i))) - continue; + DBUG_PRINT("info", ("reading from part %u (scan_type: %u)", + i, m_index_scan_type)); + DBUG_ASSERT(i == uint2korr(part_rec_buf_ptr)); uchar *rec_buf_ptr= part_rec_buf_ptr + PARTITION_BYTES_IN_POS; int error; handler *file= m_file[i]; - /* - Reset null bits (to avoid valgrind warnings) and to give a default - value for not read null fields. - */ - bfill(rec_buf_ptr, table->s->null_bytes, 255); - switch (m_index_scan_type) { case partition_index_read: error= file->ha_index_read_map(rec_buf_ptr, @@ -5397,7 +5785,7 @@ int ha_partition::handle_ordered_index_scan(uchar *buf, bool reverse_order) */ queue_set_max_at_top(&m_queue, reverse_order); queue_set_cmp_arg(&m_queue, (void*)m_curr_key_info); - m_queue.elements= j - queue_first_element(&m_queue); + m_queue.elements= j; queue_fix(&m_queue); return_top_record(buf); table->status= 0; @@ -5452,19 +5840,18 @@ int ha_partition::handle_ordered_index_scan_key_not_found() Loop over all used partitions to get the correct offset into m_ordered_rec_buffer. */ - for (i= 0; i < m_tot_parts; i++) + for (i= bitmap_get_first_set(&m_part_info->read_partitions); + i < m_tot_parts; + i= bitmap_get_next_set(&m_part_info->read_partitions, i)) { - if (!bitmap_is_set(&m_part_info->used_partitions, i)) - continue; - - if (bitmap_is_set(&m_key_not_found_partitions, i)) + if (bitmap_is_set(&m_key_not_found_partitions, i)) { /* This partition is used and did return HA_ERR_KEY_NOT_FOUND in index_read_map. */ curr_rec_buf= part_buf + PARTITION_BYTES_IN_POS; - error= m_file[i]->index_next(curr_rec_buf); + error= m_file[i]->ha_index_next(curr_rec_buf); /* HA_ERR_KEY_NOT_FOUND is not allowed from index_next! */ DBUG_ASSERT(error != HA_ERR_KEY_NOT_FOUND); if (!error) @@ -5473,6 +5860,7 @@ int ha_partition::handle_ordered_index_scan_key_not_found() DBUG_RETURN(error); } part_buf+= m_rec_length + PARTITION_BYTES_IN_POS; + part_buf+= m_rec_length + PARTITION_BYTES_IN_POS; } DBUG_ASSERT(curr_rec_buf); bitmap_clear_all(&m_key_not_found_partitions); @@ -5719,27 +6107,36 @@ int ha_partition::info(uint flag) uint extra_var_flag= flag & HA_STATUS_VARIABLE_EXTRA; DBUG_ENTER("ha_partition::info"); +#ifndef DBUG_OFF + if (bitmap_is_set_all(&(m_part_info->read_partitions))) + DBUG_PRINT("info", ("All partitions are used")); +#endif /* DBUG_OFF */ if (flag & HA_STATUS_AUTO) { bool auto_inc_is_first_in_idx= (table_share->next_number_keypart == 0); DBUG_PRINT("info", ("HA_STATUS_AUTO")); if (!table->found_next_number_field) stats.auto_increment_value= 0; - else if (table_share->ha_part_data->auto_inc_initialized) - { + else if (part_share->auto_inc_initialized) + { lock_auto_increment(); - stats.auto_increment_value= table_share->ha_part_data->next_auto_inc_val; + stats.auto_increment_value= part_share->next_auto_inc_val; unlock_auto_increment(); } else { lock_auto_increment(); /* to avoid two concurrent initializations, check again when locked */ - if (table_share->ha_part_data->auto_inc_initialized) - stats.auto_increment_value= - table_share->ha_part_data->next_auto_inc_val; + if (part_share->auto_inc_initialized) + stats.auto_increment_value= part_share->next_auto_inc_val; else { + /* + The auto-inc mutex in the table_share is locked, so we do not need + to have the handlers locked. + HA_STATUS_NO_LOCK is not checked, since we cannot skip locking + the mutex, because it is initialized. + */ handler *file, **file_array; ulonglong auto_increment_value= 0; file_array= m_file; @@ -5757,11 +6154,11 @@ int ha_partition::info(uint flag) stats.auto_increment_value= auto_increment_value; if (auto_inc_is_first_in_idx) { - set_if_bigger(table_share->ha_part_data->next_auto_inc_val, + set_if_bigger(part_share->next_auto_inc_val, auto_increment_value); - table_share->ha_part_data->auto_inc_initialized= TRUE; + part_share->auto_inc_initialized= true; DBUG_PRINT("info", ("initializing next_auto_inc_val to %lu", - (ulong) table_share->ha_part_data->next_auto_inc_val)); + (ulong) part_share->next_auto_inc_val)); } } unlock_auto_increment(); @@ -5769,6 +6166,7 @@ int ha_partition::info(uint flag) } if (flag & HA_STATUS_VARIABLE) { + uint i; DBUG_PRINT("info", ("HA_STATUS_VARIABLE")); /* Calculates statistical variables @@ -5789,29 +6187,27 @@ int ha_partition::info(uint flag) check_time: Time of last check (only applicable to MyISAM) We report last time of all underlying handlers */ - handler *file, **file_array; + handler *file; stats.records= 0; stats.deleted= 0; stats.data_file_length= 0; stats.index_file_length= 0; stats.check_time= 0; stats.delete_length= 0; - file_array= m_file; - do + for (i= bitmap_get_first_set(&m_part_info->read_partitions); + i < m_tot_parts; + i= bitmap_get_next_set(&m_part_info->read_partitions, i)) { - if (bitmap_is_set(&(m_part_info->used_partitions), (file_array - m_file))) - { - file= *file_array; - file->info(HA_STATUS_VARIABLE | no_lock_flag | extra_var_flag); - stats.records+= file->stats.records; - stats.deleted+= file->stats.deleted; - stats.data_file_length+= file->stats.data_file_length; - stats.index_file_length+= file->stats.index_file_length; - stats.delete_length+= file->stats.delete_length; - if (file->stats.check_time > stats.check_time) - stats.check_time= file->stats.check_time; - } - } while (*(++file_array)); + file= m_file[i]; + file->info(HA_STATUS_VARIABLE | no_lock_flag | extra_var_flag); + stats.records+= file->stats.records; + stats.deleted+= file->stats.deleted; + stats.data_file_length+= file->stats.data_file_length; + stats.index_file_length+= file->stats.index_file_length; + stats.delete_length+= file->stats.delete_length; + if (file->stats.check_time > stats.check_time) + stats.check_time= file->stats.check_time; + } if (stats.records && stats.records < 2 && !(m_file[0]->ha_table_flags() & HA_STATS_RECORDS_IS_EXACT)) stats.records= 2; @@ -5882,7 +6278,7 @@ int ha_partition::info(uint flag) file= *file_array; /* Get variables if not already done */ if (!(flag & HA_STATUS_VARIABLE) || - !bitmap_is_set(&(m_part_info->used_partitions), + !bitmap_is_set(&(m_part_info->read_partitions), (file_array - m_file))) file->info(HA_STATUS_VARIABLE | no_lock_flag | extra_var_flag); if (file->stats.records > max_records) @@ -5949,6 +6345,7 @@ void ha_partition::get_dynamic_partition_info(PARTITION_STATS *stat_info, uint part_id) { handler *file= m_file[part_id]; + DBUG_ASSERT(bitmap_is_set(&(m_part_info->read_partitions), part_id)); file->info(HA_STATUS_CONST | HA_STATUS_TIME | HA_STATUS_VARIABLE | HA_STATUS_VARIABLE_EXTRA | HA_STATUS_NO_LOCK); @@ -5972,7 +6369,6 @@ void ha_partition::get_dynamic_partition_info(PARTITION_STATS *stat_info, General function to prepare handler for certain behavior. @param[in] operation operation to execute - operation Operation type for extra call @return status @retval 0 success @@ -6443,17 +6839,19 @@ int ha_partition::extra(enum ha_extra_function operation) int ha_partition::reset(void) { - int result= 0, tmp; - handler **file; + int result= 0; + int tmp; + uint i; DBUG_ENTER("ha_partition::reset"); - if (m_part_info) - bitmap_set_all(&m_part_info->used_partitions); - file= m_file; - do + + for (i= bitmap_get_first_set(&m_partitions_to_reset); + i < m_tot_parts; + i= bitmap_get_next_set(&m_partitions_to_reset, i)) { - if ((tmp= (*file)->ha_reset())) + if ((tmp= m_file[i]->ha_reset())) result= tmp; - } while (*(++file)); + } + bitmap_clear_all(&m_partitions_to_reset); DBUG_RETURN(result); } @@ -6552,20 +6950,18 @@ int ha_partition::prepare_for_rename() int ha_partition::loop_extra(enum ha_extra_function operation) { int result= 0, tmp; - handler **file; - bool is_select; + uint i; DBUG_ENTER("ha_partition::loop_extra()"); - is_select= (thd_sql_command(ha_thd()) == SQLCOM_SELECT); - for (file= m_file; *file; file++) + for (i= bitmap_get_first_set(&m_part_info->lock_partitions); + i < m_tot_parts; + i= bitmap_get_next_set(&m_part_info->lock_partitions, i)) { - if (!is_select || - bitmap_is_set(&(m_part_info->used_partitions), file - m_file)) - { - if ((tmp= (*file)->extra(operation))) - result= tmp; - } + if ((tmp= m_file[i]->extra(operation))) + result= tmp; } + /* Add all used partitions to be called in reset(). */ + bitmap_union(&m_partitions_to_reset, &m_part_info->lock_partitions); DBUG_RETURN(result); } @@ -6639,20 +7035,18 @@ void ha_partition::late_extra_no_cache(uint partition_id) MODULE optimiser support ****************************************************************************/ -/* - Get keys to use for scanning +/** + Get keys to use for scanning. - SYNOPSIS - keys_to_use_for_scanning() + @return key_map of keys usable for scanning - RETURN VALUE - key_map of keys usable for scanning + @note No need to use read_partitions here, since it does not depend on + which partitions is used, only which storage engine used. */ const key_map *ha_partition::keys_to_use_for_scanning() { DBUG_ENTER("ha_partition::keys_to_use_for_scanning"); - DBUG_RETURN(m_file[0]->keys_to_use_for_scanning()); } @@ -6666,7 +7060,7 @@ ha_rows ha_partition::min_rows_for_estimate() uint i, max_used_partitions, tot_used_partitions; DBUG_ENTER("ha_partition::min_rows_for_estimate"); - tot_used_partitions= bitmap_bits_set(&m_part_info->used_partitions); + tot_used_partitions= bitmap_bits_set(&m_part_info->read_partitions); /* All partitions might have been left as unused during partition pruning @@ -6729,7 +7123,7 @@ uint ha_partition::get_biggest_used_partition(uint *part_index) while ((*part_index) < m_tot_parts) { part_id= m_part_ids_sorted_by_num_of_records[(*part_index)++]; - if (bitmap_is_set(&m_part_info->used_partitions, part_id)) + if (bitmap_is_set(&m_part_info->read_partitions, part_id)) return part_id; } return NO_CURRENT_PART_ID; @@ -6749,12 +7143,13 @@ uint ha_partition::get_biggest_used_partition(uint *part_index) double ha_partition::scan_time() { double scan_time= 0; - handler **file; + uint i; DBUG_ENTER("ha_partition::scan_time"); - for (file= m_file; *file; file++) - if (bitmap_is_set(&(m_part_info->used_partitions), (file - m_file))) - scan_time+= (*file)->scan_time(); + for (i= bitmap_get_first_set(&m_part_info->read_partitions); + i < m_tot_parts; + i= bitmap_get_next_set(&m_part_info->read_partitions, i)) + scan_time+= m_file[i]->scan_time(); DBUG_RETURN(scan_time); } @@ -6840,7 +7235,7 @@ ha_rows ha_partition::estimate_rows_upper_bound() do { - if (bitmap_is_set(&(m_part_info->used_partitions), (file - m_file))) + if (bitmap_is_set(&(m_part_info->read_partitions), (file - m_file))) { rows= (*file)->estimate_rows_upper_bound(); if (rows == HA_POS_ERROR) @@ -6882,27 +7277,25 @@ double ha_partition::read_time(uint index, uint ranges, ha_rows rows) /** Number of rows in table. see handler.h - SYNOPSIS - records() - RETURN VALUE - Number of total rows in a partitioned table. + @return Number of records in the table (after pruning!) */ ha_rows ha_partition::records() { ha_rows rows, tot_rows= 0; - handler **file; + uint i; DBUG_ENTER("ha_partition::records"); - file= m_file; - do + for (i= bitmap_get_first_set(&m_part_info->read_partitions); + i < m_tot_parts; + i= bitmap_get_next_set(&m_part_info->read_partitions, i)) { - rows= (*file)->records(); + rows= m_file[i]->records(); if (rows == HA_POS_ERROR) DBUG_RETURN(HA_POS_ERROR); tot_rows+= rows; - } while (*(++file)); + } DBUG_RETURN(tot_rows); } @@ -6959,9 +7352,18 @@ uint8 ha_partition::table_cache_type() const char *ha_partition::index_type(uint inx) { + uint first_used_partition; DBUG_ENTER("ha_partition::index_type"); - DBUG_RETURN(m_file[0]->index_type(inx)); + first_used_partition= bitmap_get_first_set(&(m_part_info->read_partitions)); + + if (first_used_partition == MY_BIT_NONE) + { + DBUG_ASSERT(0); // How can this happen? + DBUG_RETURN(handler::index_type(inx)); + } + + DBUG_RETURN(m_file[first_used_partition]->index_type(inx)); } @@ -7026,7 +7428,7 @@ bool ha_partition::get_error_message(int error, String *buf) /**************************************************************************** - MODULE handler characteristics + MODULE in-place ALTER ****************************************************************************/ /** alter_table_flags must be on handler/table level, not on hton level @@ -7097,228 +7499,297 @@ bool ha_partition::check_if_incompatible_data(HA_CREATE_INFO *create_info, /** - Helper class for [final_]add_index, see handler.h + Support of in-place alter table. */ -class ha_partition_add_index : public handler_add_index +/** + Helper class for in-place alter, see handler.h +*/ + +class ha_partition_inplace_ctx : public inplace_alter_handler_ctx { public: - handler_add_index **add_array; - ha_partition_add_index(TABLE* table_arg, KEY* key_info_arg, - uint num_of_keys_arg) - : handler_add_index(table_arg, key_info_arg, num_of_keys_arg) - {} - ~ha_partition_add_index() {} -}; + inplace_alter_handler_ctx **handler_ctx_array; + bool rollback_done; +private: + uint m_tot_parts; +public: + ha_partition_inplace_ctx(THD *thd, uint tot_parts) + : inplace_alter_handler_ctx(), + handler_ctx_array(NULL), + rollback_done(false), + m_tot_parts(tot_parts) + {} -/** - Support of in-place add/drop index - - @param table_arg Table to add index to - @param key_info Struct over the new keys to add - @param num_of_keys Number of keys to add - @param[out] add Data to be submitted with final_add_index + ~ha_partition_inplace_ctx() + { + if (handler_ctx_array) + { + for (uint index= 0; index < m_tot_parts; index++) + delete handler_ctx_array[index]; + } + } +}; - @return Operation status - @retval 0 Success - @retval != 0 Failure (error code returned, and all operations rollbacked) -*/ -int ha_partition::add_index(TABLE *table_arg, KEY *key_info, uint num_of_keys, - handler_add_index **add) +enum_alter_inplace_result +ha_partition::check_if_supported_inplace_alter(TABLE *altered_table, + Alter_inplace_info *ha_alter_info) { - uint i; - int ret= 0; +#ifdef PARTITION_SUPPORTS_INPLACE_ALTER + uint index= 0; + enum_alter_inplace_result result= HA_ALTER_INPLACE_NO_LOCK; + ha_partition_inplace_ctx *part_inplace_ctx; THD *thd= ha_thd(); - ha_partition_add_index *part_add_index; +#else + enum_alter_inplace_result result= HA_ALTER_INPLACE_NOT_SUPPORTED; +#endif - DBUG_ENTER("ha_partition::add_index"); - /* - There has already been a check in fix_partition_func in mysql_alter_table - before this call, which checks for unique/primary key violations of the - partitioning function. So no need for extra check here. - */ - + DBUG_ENTER("ha_partition::check_if_supported_inplace_alter"); + +#ifndef PARTITION_SUPPORTS_INPLACE_ALTER /* - This will be freed at the end of the statement. - And destroyed at final_add_index. (Sql_alloc does not free in delete). + Due to bug#14760210 partitions can be out-of-sync in case + commit_inplace_alter_table fails after the first partition. + + Until we can either commit all partitions at the same time or + have an atomic recover on failure/crash we don't support any + inplace alter. + + TODO: investigate what happens when indexes are out-of-sync + between partitions. If safe and possible to recover from, + then we could allow ADD/DROP INDEX. */ - part_add_index= new (thd->mem_root) - ha_partition_add_index(table_arg, key_info, num_of_keys); - if (!part_add_index) - DBUG_RETURN(HA_ERR_OUT_OF_MEM); - part_add_index->add_array= (handler_add_index **) - thd->alloc(sizeof(void *) * m_tot_parts); - if (!part_add_index->add_array) - { - delete part_add_index; - DBUG_RETURN(HA_ERR_OUT_OF_MEM); + DBUG_RETURN(result); +#else + part_inplace_ctx= + new (thd->mem_root) ha_partition_inplace_ctx(thd, m_tot_parts); + if (!part_inplace_ctx) + DBUG_RETURN(HA_ALTER_ERROR); + + part_inplace_ctx->handler_ctx_array= (inplace_alter_handler_ctx **) + thd->alloc(sizeof(inplace_alter_handler_ctx *) * m_tot_parts); + if (!part_inplace_ctx->handler_ctx_array) + DBUG_RETURN(HA_ALTER_ERROR); + + for (index= 0; index < m_tot_parts; index++) + part_inplace_ctx->handler_ctx_array[index]= NULL; + + for (index= 0; index < m_tot_parts; index++) + { + enum_alter_inplace_result p_result= + m_file[index]->check_if_supported_inplace_alter(altered_table, + ha_alter_info); + part_inplace_ctx->handler_ctx_array[index]= ha_alter_info->handler_ctx; + + if (p_result < result) + result= p_result; + if (result == HA_ALTER_ERROR) + break; } + ha_alter_info->handler_ctx= part_inplace_ctx; - for (i= 0; i < m_tot_parts; i++) - { - if ((ret= m_file[i]->add_index(table_arg, key_info, num_of_keys, - &part_add_index->add_array[i]))) - goto err; - } - *add= part_add_index; - DBUG_RETURN(ret); -err: - /* Rollback all prepared partitions. i - 1 .. 0 */ - while (i) - { - i--; - (void) m_file[i]->final_add_index(part_add_index->add_array[i], false); - } - delete part_add_index; - DBUG_RETURN(ret); + DBUG_RETURN(result); +#endif } -/** - Second phase of in-place add index. +bool ha_partition::prepare_inplace_alter_table(TABLE *altered_table, + Alter_inplace_info *ha_alter_info) +{ + uint index= 0; + bool error= false; + ha_partition_inplace_ctx *part_inplace_ctx; - @param add Info from add_index - @param commit Should we commit or rollback the add_index operation + DBUG_ENTER("ha_partition::prepare_inplace_alter_table"); - @return Operation status - @retval 0 Success - @retval != 0 Failure (error code returned) + part_inplace_ctx= + static_cast<class ha_partition_inplace_ctx*>(ha_alter_info->handler_ctx); + + for (index= 0; index < m_tot_parts && !error; index++) + { + ha_alter_info->handler_ctx= part_inplace_ctx->handler_ctx_array[index]; + if (m_file[index]->ha_prepare_inplace_alter_table(altered_table, + ha_alter_info)) + error= true; + part_inplace_ctx->handler_ctx_array[index]= ha_alter_info->handler_ctx; + } + ha_alter_info->handler_ctx= part_inplace_ctx; + + DBUG_RETURN(error); +} - @note If commit is false, index changes are rolled back by dropping the - added indexes. If commit is true, nothing is done as the indexes - were already made active in ::add_index() -*/ -int ha_partition::final_add_index(handler_add_index *add, bool commit) +bool ha_partition::inplace_alter_table(TABLE *altered_table, + Alter_inplace_info *ha_alter_info) { - ha_partition_add_index *part_add_index; - uint i; - int ret= 0; + uint index= 0; + bool error= false; + ha_partition_inplace_ctx *part_inplace_ctx; - DBUG_ENTER("ha_partition::final_add_index"); - - if (!add) + DBUG_ENTER("ha_partition::inplace_alter_table"); + + part_inplace_ctx= + static_cast<class ha_partition_inplace_ctx*>(ha_alter_info->handler_ctx); + + for (index= 0; index < m_tot_parts && !error; index++) { - DBUG_ASSERT(!commit); - DBUG_RETURN(0); + ha_alter_info->handler_ctx= part_inplace_ctx->handler_ctx_array[index]; + if (m_file[index]->ha_inplace_alter_table(altered_table, + ha_alter_info)) + error= true; + part_inplace_ctx->handler_ctx_array[index]= ha_alter_info->handler_ctx; } - part_add_index= static_cast<class ha_partition_add_index*>(add); + ha_alter_info->handler_ctx= part_inplace_ctx; - for (i= 0; i < m_tot_parts; i++) + DBUG_RETURN(error); +} + + +/* + Note that this function will try rollback failed ADD INDEX by + executing DROP INDEX for the indexes that were committed (if any) + before the error occured. This means that the underlying storage + engine must be able to drop index in-place with X-lock held. + (As X-lock will be held here if new indexes are to be committed) +*/ +bool ha_partition::commit_inplace_alter_table(TABLE *altered_table, + Alter_inplace_info *ha_alter_info, + bool commit) +{ + uint index= 0; + ha_partition_inplace_ctx *part_inplace_ctx; + + DBUG_ENTER("ha_partition::commit_inplace_alter_table"); + + part_inplace_ctx= + static_cast<class ha_partition_inplace_ctx*>(ha_alter_info->handler_ctx); + + if (!commit && part_inplace_ctx->rollback_done) + DBUG_RETURN(false); // We have already rolled back changes. + + for (index= 0; index < m_tot_parts; index++) { - if ((ret= m_file[i]->final_add_index(part_add_index->add_array[i], commit))) + ha_alter_info->handler_ctx= part_inplace_ctx->handler_ctx_array[index]; + if (m_file[index]->ha_commit_inplace_alter_table(altered_table, + ha_alter_info, commit)) + { + part_inplace_ctx->handler_ctx_array[index]= ha_alter_info->handler_ctx; goto err; + } + part_inplace_ctx->handler_ctx_array[index]= ha_alter_info->handler_ctx; DBUG_EXECUTE_IF("ha_partition_fail_final_add_index", { - /* Simulate a failure by rollback the second partition */ + /* Simulate failure by rollback of the second partition */ if (m_tot_parts > 1) { - i++; - m_file[i]->final_add_index(part_add_index->add_array[i], false); - /* Set an error that is specific to ha_partition. */ - ret= HA_ERR_NO_PARTITION_FOUND; + index++; + ha_alter_info->handler_ctx= part_inplace_ctx->handler_ctx_array[index]; + m_file[index]->ha_commit_inplace_alter_table(altered_table, + ha_alter_info, false); + part_inplace_ctx->handler_ctx_array[index]= ha_alter_info->handler_ctx; goto err; } }); } - delete part_add_index; - DBUG_RETURN(ret); -err: - uint j; - uint *key_numbers= NULL; - KEY *old_key_info= NULL; - uint num_of_keys= 0; - int error; - - /* How could this happen? Needed to create a covering test case :) */ - DBUG_ASSERT(ret == HA_ERR_NO_PARTITION_FOUND); + ha_alter_info->handler_ctx= part_inplace_ctx; - if (i > 0) - { - num_of_keys= part_add_index->num_of_keys; - key_numbers= (uint*) ha_thd()->alloc(sizeof(uint) * num_of_keys); - if (!key_numbers) + DBUG_RETURN(false); + +err: + ha_alter_info->handler_ctx= part_inplace_ctx; + /* + Reverting committed changes is (for now) only possible for ADD INDEX + For other changes we will just try to rollback changes. + */ + if (index > 0 && + ha_alter_info->handler_flags & (Alter_inplace_info::ADD_INDEX | + Alter_inplace_info::ADD_UNIQUE_INDEX | + Alter_inplace_info::ADD_PK_INDEX)) + { + Alter_inplace_info drop_info(ha_alter_info->create_info, + ha_alter_info->alter_info, + NULL, 0, + ha_alter_info->modified_part_info, + ha_alter_info->ignore); + + if (ha_alter_info->handler_flags & Alter_inplace_info::ADD_INDEX) + drop_info.handler_flags|= Alter_inplace_info::DROP_INDEX; + if (ha_alter_info->handler_flags & Alter_inplace_info::ADD_UNIQUE_INDEX) + drop_info.handler_flags|= Alter_inplace_info::DROP_UNIQUE_INDEX; + if (ha_alter_info->handler_flags & Alter_inplace_info::ADD_PK_INDEX) + drop_info.handler_flags|= Alter_inplace_info::DROP_PK_INDEX; + drop_info.index_drop_count= ha_alter_info->index_add_count; + drop_info.index_drop_buffer= + (KEY**) ha_thd()->alloc(sizeof(KEY*) * drop_info.index_drop_count); + if (!drop_info.index_drop_buffer) { sql_print_error("Failed with error handling of adding index:\n" "committing index failed, and when trying to revert " "already committed partitions we failed allocating\n" "memory for the index for table '%s'", table_share->table_name.str); - DBUG_RETURN(HA_ERR_OUT_OF_MEM); + DBUG_RETURN(true); } - old_key_info= table->key_info; - /* - Use the newly added key_info as table->key_info to remove them. - Note that this requires the subhandlers to use name lookup of the - index. They must use given table->key_info[key_number], they cannot - use their local view of the keys, since table->key_info only include - the indexes to be removed here. - */ - for (j= 0; j < num_of_keys; j++) - key_numbers[j]= j; - table->key_info= part_add_index->key_info; - } + for (uint i= 0; i < drop_info.index_drop_count; i++) + drop_info.index_drop_buffer[i]= + &ha_alter_info->key_info_buffer[ha_alter_info->index_add_buffer[i]]; - for (j= 0; j < m_tot_parts; j++) - { - if (j < i) + // Drop index for each partition where we already committed new index. + for (uint i= 0; i < index; i++) { - /* Remove the newly added index */ - error= m_file[j]->prepare_drop_index(table, key_numbers, num_of_keys); - if (error || m_file[j]->final_drop_index(table)) - { + bool error= m_file[i]->ha_prepare_inplace_alter_table(altered_table, + &drop_info); + error|= m_file[i]->ha_inplace_alter_table(altered_table, &drop_info); + error|= m_file[i]->ha_commit_inplace_alter_table(altered_table, + &drop_info, true); + if (error) sql_print_error("Failed with error handling of adding index:\n" "committing index failed, and when trying to revert " "already committed partitions we failed removing\n" "the index for table '%s' partition nr %d", - table_share->table_name.str, j); - } + table_share->table_name.str, i); } - else if (j > i) + + // Rollback uncommitted changes. + for (uint i= index+1; i < m_tot_parts; i++) { - /* Rollback non finished partitions */ - if (m_file[j]->final_add_index(part_add_index->add_array[j], false)) + ha_alter_info->handler_ctx= part_inplace_ctx->handler_ctx_array[i]; + if (m_file[i]->ha_commit_inplace_alter_table(altered_table, + ha_alter_info, false)) { /* How could this happen? */ sql_print_error("Failed with error handling of adding index:\n" "Rollback of add_index failed for table\n" "'%s' partition nr %d", - table_share->table_name.str, j); + table_share->table_name.str, i); } + part_inplace_ctx->handler_ctx_array[i]= ha_alter_info->handler_ctx; } + + // We have now reverted/rolled back changes. Set flag to prevent + // it from being done again. + part_inplace_ctx->rollback_done= true; + + print_error(HA_ERR_NO_PARTITION_FOUND, MYF(0)); } - if (i > 0) - table->key_info= old_key_info; - delete part_add_index; - DBUG_RETURN(ret); -} -int ha_partition::prepare_drop_index(TABLE *table_arg, uint *key_num, - uint num_of_keys) -{ - handler **file; - int ret= 0; + ha_alter_info->handler_ctx= part_inplace_ctx; - /* - DROP INDEX does not affect partitioning. - */ - for (file= m_file; *file; file++) - if ((ret= (*file)->prepare_drop_index(table_arg, key_num, num_of_keys))) - break; - return ret; + DBUG_RETURN(true); } -int ha_partition::final_drop_index(TABLE *table_arg) +void ha_partition::notify_table_changed() { handler **file; - int ret= HA_ERR_WRONG_COMMAND; + + DBUG_ENTER("ha_partition::notify_table_changed"); for (file= m_file; *file; file++) - if ((ret= (*file)->final_drop_index(table_arg))) - break; - return ret; + (*file)->ha_notify_table_changed(); + + DBUG_VOID_RETURN; } @@ -7473,8 +7944,8 @@ int ha_partition::reset_auto_increment(ulonglong value) int res; DBUG_ENTER("ha_partition::reset_auto_increment"); lock_auto_increment(); - table_share->ha_part_data->auto_inc_initialized= FALSE; - table_share->ha_part_data->next_auto_inc_val= 0; + part_share->auto_inc_initialized= false; + part_share->next_auto_inc_val= 0; do { if ((res= (*file)->ha_reset_auto_increment(value)) != 0) @@ -7488,7 +7959,7 @@ int ha_partition::reset_auto_increment(ulonglong value) /** This method is called by update_auto_increment which in turn is called by the individual handlers as part of write_row. We use the - table_share->ha_part_data->next_auto_inc_val, or search all + part_share->next_auto_inc_val, or search all partitions for the highest auto_increment_value if not initialized or if auto_increment field is a secondary part of a key, we must search every partition when holding a mutex to be sure of correctness. @@ -7544,9 +8015,9 @@ void ha_partition::get_auto_increment(ulonglong offset, ulonglong increment, /* This is initialized in the beginning of the first write_row call. */ - DBUG_ASSERT(table_share->ha_part_data->auto_inc_initialized); + DBUG_ASSERT(part_share->auto_inc_initialized); /* - Get a lock for handling the auto_increment in table_share->ha_part_data + Get a lock for handling the auto_increment in part_share for avoiding two concurrent statements getting the same number. */ @@ -7573,9 +8044,8 @@ void ha_partition::get_auto_increment(ulonglong offset, ulonglong increment, } /* this gets corrected (for offset/increment) in update_auto_increment */ - *first_value= table_share->ha_part_data->next_auto_inc_val; - table_share->ha_part_data->next_auto_inc_val+= - nb_desired_values * increment; + *first_value= part_share->next_auto_inc_val; + part_share->next_auto_inc_val+= nb_desired_values * increment; unlock_auto_increment(); DBUG_PRINT("info", ("*first_value: %lu", (ulong) *first_value)); @@ -7590,14 +8060,19 @@ void ha_partition::release_auto_increment() if (table->s->next_number_keypart) { - for (uint i= 0; i < m_tot_parts; i++) + uint i; + for (i= bitmap_get_first_set(&m_part_info->lock_partitions); + i < m_tot_parts; + i= bitmap_get_next_set(&m_part_info->lock_partitions, i)) + { m_file[i]->ha_release_auto_increment(); + } } else if (next_insert_id) { ulonglong next_auto_inc_val; lock_auto_increment(); - next_auto_inc_val= table_share->ha_part_data->next_auto_inc_val; + next_auto_inc_val= part_share->next_auto_inc_val; /* If the current auto_increment values is lower than the reserved value, and the reserved value was reserved by this thread, @@ -7612,10 +8087,11 @@ void ha_partition::release_auto_increment() with SET INSERT_ID, i.e. forced/non generated values. */ if (thd->auto_inc_intervals_forced.maximum() < next_insert_id) - table_share->ha_part_data->next_auto_inc_val= next_insert_id; + part_share->next_auto_inc_val= next_insert_id; } - DBUG_PRINT("info", ("table_share->ha_part_data->next_auto_inc_val: %lu", - (ulong) table_share->ha_part_data->next_auto_inc_val)); + DBUG_PRINT("info", ("part_share->next_auto_inc_val: %lu", + (ulong) part_share->next_auto_inc_val)); + /* Unlock the multi row statement lock taken in get_auto_increment */ if (auto_increment_safe_stmt_log_lock) @@ -7658,6 +8134,7 @@ int ha_partition::disable_indexes(uint mode) handler **file; int error= 0; + DBUG_ASSERT(bitmap_is_set_all(&(m_part_info->lock_partitions))); for (file= m_file; *file; file++) { if ((error= (*file)->ha_disable_indexes(mode))) @@ -7682,6 +8159,7 @@ int ha_partition::enable_indexes(uint mode) handler **file; int error= 0; + DBUG_ASSERT(bitmap_is_set_all(&(m_part_info->lock_partitions))); for (file= m_file; *file; file++) { if ((error= (*file)->ha_enable_indexes(mode))) @@ -7706,6 +8184,7 @@ int ha_partition::indexes_are_disabled(void) handler **file; int error= 0; + DBUG_ASSERT(bitmap_is_set_all(&(m_part_info->lock_partitions))); for (file= m_file; *file; file++) { if ((error= (*file)->indexes_are_disabled())) diff --git a/sql/ha_partition.h b/sql/ha_partition.h index 96e47d3d676..da7f33aae20 100644 --- a/sql/ha_partition.h +++ b/sql/ha_partition.h @@ -47,6 +47,92 @@ enum partition_keywords /* offset to the engines array */ #define PAR_ENGINES_OFFSET 12 +/** Struct used for partition_name_hash */ +typedef struct st_part_name_def +{ + uchar *partition_name; + uint length; + uint32 part_id; + my_bool is_subpart; +} PART_NAME_DEF; + +/** class where to save partitions Handler_share's */ +class Parts_share_refs +{ +public: + uint num_parts; /**< Size of ha_share array */ + Handler_share **ha_shares; /**< Storage for each part */ + Parts_share_refs() + { + num_parts= 0; + ha_shares= NULL; + } + ~Parts_share_refs() + { + uint i; + for (i= 0; i < num_parts; i++) + if (ha_shares[i]) + delete ha_shares[i]; + if (ha_shares) + delete [] ha_shares; + } + bool init(uint arg_num_parts) + { + DBUG_ASSERT(!num_parts && !ha_shares); + num_parts= arg_num_parts; + /* Allocate an array of Handler_share pointers */ + ha_shares= new Handler_share *[num_parts]; + if (!ha_shares) + { + num_parts= 0; + return true; + } + memset(ha_shares, 0, sizeof(Handler_share*) * num_parts); + return false; + } +}; + + +/** + Partition specific Handler_share. +*/ +class Partition_share : public Handler_share +{ +public: + bool auto_inc_initialized; + mysql_mutex_t auto_inc_mutex; /**< protecting auto_inc val */ + ulonglong next_auto_inc_val; /**< first non reserved value */ + /** + Hash of partition names. Initialized in the first ha_partition::open() + for the table_share. After that it is read-only, i.e. no locking required. + */ + bool partition_name_hash_initialized; + HASH partition_name_hash; + /** Storage for each partitions Handler_share */ + Parts_share_refs *partitions_share_refs; + Partition_share() {} + ~Partition_share() + { + DBUG_ENTER("Partition_share::~Partition_share"); + mysql_mutex_destroy(&auto_inc_mutex); + if (partition_name_hash_initialized) + my_hash_free(&partition_name_hash); + if (partitions_share_refs) + delete partitions_share_refs; + DBUG_VOID_RETURN; + } + bool init(uint num_parts); + void lock_auto_inc() + { + mysql_mutex_lock(&auto_inc_mutex); + } + void unlock_auto_inc() + { + mysql_mutex_unlock(&auto_inc_mutex); + } +}; + + class ha_partition :public handler { private: @@ -176,16 +262,25 @@ private: ha_rows m_bulk_inserted_rows; /** used for prediction of start_bulk_insert rows */ enum_monotonicity_info m_part_func_monotonicity_info; + /** keep track of locked partitions */ + MY_BITMAP m_locked_partitions; + /** Stores shared auto_increment etc. */ + Partition_share *part_share; + /** Temporary storage for new partitions Handler_shares during ALTER */ + List<Parts_share_refs> m_new_partitions_share_refs; /** Sorted array of partition ids in descending order of number of rows. */ uint32 *m_part_ids_sorted_by_num_of_records; /* Compare function for my_qsort2, for reversed order. */ static int compare_number_of_records(ha_partition *me, const uint32 *a, const uint32 *b); + /** keep track of partitions to call ha_reset */ + MY_BITMAP m_partitions_to_reset; /** partitions that returned HA_ERR_KEY_NOT_FOUND. */ MY_BITMAP m_key_not_found_partitions; bool m_key_not_found; public: + Partition_share *get_part_share() { return part_share; } handler *clone(const char *name, MEM_ROOT *mem_root); virtual void set_part_info(partition_info *part_info) { @@ -268,11 +363,10 @@ private: handler *file, const char *part_name, partition_element *p_elem); /* - delete_table, rename_table and create uses very similar logic which + delete_table and rename_table uses very similar logic which is packed into this routine. */ - uint del_ren_cre_table(const char *from, const char *to, - TABLE *table_arg, HA_CREATE_INFO *create_info); + uint del_ren_table(const char *from, const char *to); /* One method to create the table_name.par file containing the names of the underlying partitions, their engine and the number of partitions. @@ -289,9 +383,15 @@ private: int set_up_table_before_create(TABLE *table_arg, const char *partition_name_with_path, HA_CREATE_INFO *info, - uint part_id, partition_element *p_elem); partition_element *find_partition_element(uint part_id); + bool insert_partition_name_in_hash(const char *name, uint part_id, + bool is_subpart); + bool populate_partition_name_hash(); + Partition_share *get_share(); + bool set_ha_share_ref(Handler_share **ha_share); + bool init_partition_bitmaps(); + void free_partition_bitmaps(); public: @@ -397,7 +497,8 @@ public: virtual bool is_fatal_error(int error, uint flags) { if (!handler::is_fatal_error(error, flags) || - error == HA_ERR_NO_PARTITION_FOUND) + error == HA_ERR_NO_PARTITION_FOUND || + error == HA_ERR_NOT_IN_LOCK_PARTITIONS) return FALSE; return TRUE; } @@ -957,16 +1058,15 @@ private: /* lock already taken */ if (auto_increment_safe_stmt_log_lock) return; - DBUG_ASSERT(table_share->ha_part_data && !auto_increment_lock); + DBUG_ASSERT(!auto_increment_lock); if(table_share->tmp_table == NO_TMP_TABLE) { auto_increment_lock= TRUE; - mysql_mutex_lock(&table_share->ha_part_data->LOCK_auto_inc); + part_share->lock_auto_inc(); } } virtual void unlock_auto_increment() { - DBUG_ASSERT(table_share->ha_part_data); /* If auto_increment_safe_stmt_log_lock is true, we have to keep the lock. It will be set to false and thus unlocked at the end of the statement by @@ -974,7 +1074,7 @@ private: */ if(auto_increment_lock && !auto_increment_safe_stmt_log_lock) { - mysql_mutex_unlock(&table_share->ha_part_data->LOCK_auto_inc); + part_share->unlock_auto_inc(); auto_increment_lock= FALSE; } } @@ -983,10 +1083,10 @@ private: ulonglong nr= (((Field_num*) field)->unsigned_flag || field->val_int() > 0) ? field->val_int() : 0; lock_auto_increment(); - DBUG_ASSERT(table_share->ha_part_data->auto_inc_initialized == TRUE); + DBUG_ASSERT(part_share->auto_inc_initialized); /* must check when the mutex is taken */ - if (nr >= table_share->ha_part_data->next_auto_inc_val) - table_share->ha_part_data->next_auto_inc_val= nr + 1; + if (nr >= part_share->next_auto_inc_val) + part_share->next_auto_inc_val= nr + 1; unlock_auto_increment(); } @@ -1052,20 +1152,25 @@ public: /* ------------------------------------------------------------------------- - MODULE on-line ALTER TABLE - ------------------------------------------------------------------------- + MODULE in-place ALTER TABLE + ------------------------------------------------------------------------- These methods are in the handler interface. (used by innodb-plugin) - They are used for on-line/fast alter table add/drop index: - ------------------------------------------------------------------------- + They are used for in-place alter table: + ------------------------------------------------------------------------- */ - virtual int add_index(TABLE *table_arg, KEY *key_info, uint num_of_keys, - handler_add_index **add); - virtual int final_add_index(handler_add_index *add, bool commit); - virtual int prepare_drop_index(TABLE *table_arg, uint *key_num, - uint num_of_keys); - virtual int final_drop_index(TABLE *table_arg); - - /* + virtual enum_alter_inplace_result + check_if_supported_inplace_alter(TABLE *altered_table, + Alter_inplace_info *ha_alter_info); + virtual bool prepare_inplace_alter_table(TABLE *altered_table, + Alter_inplace_info *ha_alter_info); + virtual bool inplace_alter_table(TABLE *altered_table, + Alter_inplace_info *ha_alter_info); + virtual bool commit_inplace_alter_table(TABLE *altered_table, + Alter_inplace_info *ha_alter_info, + bool commit); + virtual void notify_table_changed(); + + /* ------------------------------------------------------------------------- MODULE tablespace support ------------------------------------------------------------------------- diff --git a/sql/handler.cc b/sql/handler.cc index e8b1fca1651..86647d02f70 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -61,7 +61,7 @@ static handlerton *installed_htons[128]; #define BITMAP_STACKBUF_SIZE (128/8) KEY_CREATE_INFO default_key_create_info= -{ HA_KEY_ALG_UNDEF, 0, {NullS, 0}, {NullS, 0}, 0 }; +{ HA_KEY_ALG_UNDEF, 0, {NullS, 0}, {NullS, 0}, true }; /* number of entries in handlertons[] */ ulong total_ha= 0; @@ -98,6 +98,7 @@ uint known_extensions_id= 0; static int commit_one_phase_2(THD *thd, bool all, THD_TRANS *trans, bool is_real_trans); + static plugin_ref ha_default_plugin(THD *thd) { if (thd->variables.table_plugin) @@ -1095,7 +1096,7 @@ int ha_prepare(THD *thd) } else { - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA), ha_resolve_storage_engine_name(ht)); } @@ -1203,7 +1204,7 @@ int ha_commit_trans(THD *thd, bool all) /* Just a random warning to test warnings pushed during autocommit. */ DBUG_EXECUTE_IF("warn_during_ha_commit_trans", - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning(thd, Sql_condition::WARN_LEVEL_WARN, ER_WARNING_NOT_COMPLETE_ROLLBACK, ER(ER_WARNING_NOT_COMPLETE_ROLLBACK));); @@ -1523,7 +1524,7 @@ int ha_rollback_trans(THD *thd, bool all) trans->no_2pc=0; if (is_real_trans && thd->transaction_rollback_request && thd->transaction.xid_state.xa_state != XA_NOTR) - thd->transaction.xid_state.rm_error= thd->stmt_da->sql_errno(); + thd->transaction.xid_state.rm_error= thd->get_stmt_da()->sql_errno(); } /* Always cleanup. Even if nht==0. There may be savepoints. */ if (is_real_trans) @@ -1546,7 +1547,7 @@ int ha_rollback_trans(THD *thd, bool all) */ if (is_real_trans && thd->transaction.all.modified_non_trans_table && !thd->slave_thread && thd->killed < KILL_CONNECTION) - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning(thd, Sql_condition::WARN_LEVEL_WARN, ER_WARNING_NOT_COMPLETE_ROLLBACK, ER(ER_WARNING_NOT_COMPLETE_ROLLBACK)); (void) RUN_HOOK(transaction, after_rollback, (thd, FALSE)); @@ -2039,7 +2040,7 @@ int ha_start_consistent_snapshot(THD *thd) exist: */ if (warn) - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR, + push_warning(thd, Sql_condition::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR, "This MySQL server does not support any " "consistent-read capable storage engine"); return 0; @@ -2135,9 +2136,9 @@ public: virtual bool handle_condition(THD *thd, uint sql_errno, const char* sqlstate, - MYSQL_ERROR::enum_warning_level level, + Sql_condition::enum_warning_level level, const char* msg, - MYSQL_ERROR ** cond_hdl); + Sql_condition ** cond_hdl); char buff[MYSQL_ERRMSG_SIZE]; }; @@ -2147,9 +2148,9 @@ Ha_delete_table_error_handler:: handle_condition(THD *, uint, const char*, - MYSQL_ERROR::enum_warning_level, + Sql_condition::enum_warning_level, const char* msg, - MYSQL_ERROR ** cond_hdl) + Sql_condition ** cond_hdl) { *cond_hdl= NULL; /* Grab the error message */ @@ -2213,7 +2214,7 @@ int ha_delete_table(THD *thd, handlerton *table_type, const char *path, XXX: should we convert *all* errors to warnings here? What if the error is fatal? */ - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, error, + push_warning(thd, Sql_condition::WARN_LEVEL_WARN, error, ha_delete_table_error_handler.buff); } delete file; @@ -2236,8 +2237,10 @@ int ha_delete_table(THD *thd, handlerton *table_type, const char *path, handler *handler::clone(const char *name, MEM_ROOT *mem_root) { handler *new_handler= get_new_handler(table->s, mem_root, ht); - if (! new_handler) + if (!new_handler) return NULL; + if (new_handler->set_ha_share_ref(ha_share)) + goto err; /* Allocate handler->ref here because otherwise ha_open will allocate it @@ -2247,7 +2250,7 @@ handler *handler::clone(const char *name, MEM_ROOT *mem_root) if (!(new_handler->ref= (uchar*) alloc_root(mem_root, ALIGN_SIZE(ref_length)*2))) - return NULL; + goto err; /* TODO: Implement a more efficient way to have more than one index open for @@ -2258,9 +2261,13 @@ handler *handler::clone(const char *name, MEM_ROOT *mem_root) */ if (new_handler->ha_open(table, name, table->db_stat, HA_OPEN_IGNORE_IF_LOCKED)) - return NULL; + goto err; return new_handler; + +err: + delete new_handler; + return NULL; } @@ -2342,6 +2349,8 @@ int handler::ha_open(TABLE *table_arg, const char *name, int mode, table= table_arg; DBUG_ASSERT(table->s == table_share); + DBUG_ASSERT(m_lock_type == F_UNLCK); + DBUG_PRINT("info", ("old m_lock_type: %d F_UNLCK %d", m_lock_type, F_UNLCK)); DBUG_ASSERT(alloc_root_inited(&table->mem_root)); if ((error=open(name,mode,test_if_locked))) @@ -2363,8 +2372,16 @@ int handler::ha_open(TABLE *table_arg, const char *name, int mode, DBUG_ASSERT(m_psi == NULL); DBUG_ASSERT(table_share != NULL); #ifdef HAVE_PSI_TABLE_INTERFACE - PSI_table_share *share_psi= ha_table_share_psi(table_share); - m_psi= PSI_TABLE_CALL(open_table)(share_psi, this); + /* + Do not call this for partitions handlers, since it may take too much + resources. + So only use the m_psi on table level, not for individual partitions. + */ + if (!(test_if_locked & HA_OPEN_NO_PSI_CALL)) + { + PSI_table_share *share_psi= ha_table_share_psi(table_share); + m_psi= PSI_TABLE_CALL(open_table)(share_psi, this); + } #endif if (table->s->db_options_in_use & HA_OPTION_READ_ONLY_DATA) @@ -2400,13 +2417,20 @@ int handler::ha_close(void) PSI_TABLE_CALL(close_table)(m_psi); m_psi= NULL; /* instrumentation handle, invalid after close_table() */ #endif - + // TODO: set table= NULL to mark the handler as closed? + DBUG_ASSERT(m_psi == NULL); + DBUG_ASSERT(m_lock_type == F_UNLCK); + DBUG_ASSERT(inited == NONE); DBUG_RETURN(close()); } int handler::ha_rnd_next(uchar *buf) { int result; + DBUG_ENTER("handler::ha_rnd_next"); + DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE || + m_lock_type != F_UNLCK); + DBUG_ASSERT(inited == RND); MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_FETCH_ROW, MAX_KEY, 0, { result= rnd_next(buf); }) @@ -2421,12 +2445,17 @@ int handler::ha_rnd_next(uchar *buf) increment_statistics(&SSV::ha_read_rnd_next_count); table->status=result ? STATUS_NOT_FOUND: 0; - return result; + DBUG_RETURN(result); } int handler::ha_rnd_pos(uchar *buf, uchar *pos) { int result; + DBUG_ENTER("handler::ha_rnd_pos"); + DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE || + m_lock_type != F_UNLCK); + /* TODO: Find out how to solve ha_rnd_pos when finding duplicate update. */ + /* DBUG_ASSERT(inited == RND); */ MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_FETCH_ROW, MAX_KEY, 0, { result= rnd_pos(buf, pos); }) @@ -2434,7 +2463,7 @@ int handler::ha_rnd_pos(uchar *buf, uchar *pos) if (!result) update_rows_read(); table->status=result ? STATUS_NOT_FOUND: 0; - return result; + DBUG_RETURN(result); } int handler::ha_index_read_map(uchar *buf, const uchar *key, @@ -2442,6 +2471,9 @@ int handler::ha_index_read_map(uchar *buf, const uchar *key, enum ha_rkey_function find_flag) { int result; + DBUG_ENTER("handler::ha_index_read_map"); + DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE || + m_lock_type != F_UNLCK); DBUG_ASSERT(inited==INDEX); MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_FETCH_ROW, active_index, 0, @@ -2450,7 +2482,7 @@ int handler::ha_index_read_map(uchar *buf, const uchar *key, if (!result) update_index_statistics(); table->status=result ? STATUS_NOT_FOUND: 0; - return result; + DBUG_RETURN(result); } /* @@ -2465,6 +2497,8 @@ int handler::ha_index_read_idx_map(uchar *buf, uint index, const uchar *key, { int result; DBUG_ASSERT(inited==NONE); + DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE || + m_lock_type != F_UNLCK); DBUG_ASSERT(end_range == NULL); MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_FETCH_ROW, index, 0, { result= index_read_idx_map(buf, index, key, keypart_map, find_flag); }) @@ -2481,6 +2515,9 @@ int handler::ha_index_read_idx_map(uchar *buf, uint index, const uchar *key, int handler::ha_index_next(uchar * buf) { int result; + DBUG_ENTER("handler::ha_index_next"); + DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE || + m_lock_type != F_UNLCK); DBUG_ASSERT(inited==INDEX); MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_FETCH_ROW, active_index, 0, @@ -2489,12 +2526,15 @@ int handler::ha_index_next(uchar * buf) if (!result) update_index_statistics(); table->status=result ? STATUS_NOT_FOUND: 0; - return result; + DBUG_RETURN(result); } int handler::ha_index_prev(uchar * buf) { int result; + DBUG_ENTER("handler::ha_index_prev"); + DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE || + m_lock_type != F_UNLCK); DBUG_ASSERT(inited==INDEX); MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_FETCH_ROW, active_index, 0, @@ -2503,12 +2543,14 @@ int handler::ha_index_prev(uchar * buf) if (!result) update_index_statistics(); table->status=result ? STATUS_NOT_FOUND: 0; - return result; + DBUG_RETURN(result); } int handler::ha_index_first(uchar * buf) { int result; + DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE || + m_lock_type != F_UNLCK); DBUG_ASSERT(inited==INDEX); MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_FETCH_ROW, active_index, 0, @@ -2523,6 +2565,8 @@ int handler::ha_index_first(uchar * buf) int handler::ha_index_last(uchar * buf) { int result; + DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE || + m_lock_type != F_UNLCK); DBUG_ASSERT(inited==INDEX); MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_FETCH_ROW, active_index, 0, @@ -2537,6 +2581,8 @@ int handler::ha_index_last(uchar * buf) int handler::ha_index_next_same(uchar *buf, const uchar *key, uint keylen) { int result; + DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE || + m_lock_type != F_UNLCK); DBUG_ASSERT(inited==INDEX); MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_FETCH_ROW, active_index, 0, @@ -3059,6 +3105,9 @@ void handler::get_auto_increment(ulonglong offset, ulonglong increment, void handler::ha_release_auto_increment() { DBUG_ENTER("ha_release_auto_increment"); + DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE || + m_lock_type != F_UNLCK || + (!next_insert_id && !insert_id_for_cur_row)); release_auto_increment(); insert_id_for_cur_row= 0; auto_inc_interval_for_cur_row.replace(0, 0, 0); @@ -3190,7 +3239,9 @@ void handler::print_error(int error, myf errflag) uint key_nr=get_dup_key(error); if ((int) key_nr >= 0) { - print_keydup_error(table, &table->key_info[key_nr], errflag); + print_keydup_error(table, + key_nr == MAX_KEY ? NULL : &table->key_info[key_nr], + errflag); DBUG_VOID_RETURN; } } @@ -3202,9 +3253,12 @@ void handler::print_error(int error, myf errflag) char rec_buf[MAX_KEY_LENGTH]; String rec(rec_buf, sizeof(rec_buf), system_charset_info); /* Table is opened and defined at this point */ - key_unpack(&rec, table, 0 /* just print the subset of fields that are - part of the first index, printing the whole - row from there is not easy */); + + /* + Just print the subset of fields that are part of the first index, + printing the whole row from there is not easy. + */ + key_unpack(&rec, table, &table->key_info[0]); char child_table_name[NAME_LEN + 1]; char child_key_name[NAME_LEN + 1]; @@ -3339,7 +3393,7 @@ void handler::print_error(int error, myf errflag) case HA_ERR_AUTOINC_ERANGE: textno= error; my_error(textno, errflag, table->next_number_field->field_name, - table->in_use->warning_info->current_row_for_warning()); + table->in_use->get_stmt_da()->current_row_for_warning()); DBUG_VOID_RETURN; break; case HA_ERR_TOO_MANY_CONCURRENT_TRXS: @@ -3348,6 +3402,9 @@ void handler::print_error(int error, myf errflag) case HA_ERR_INDEX_COL_TOO_LONG: textno= ER_INDEX_COLUMN_TOO_LONG; break; + case HA_ERR_NOT_IN_LOCK_PARTITIONS: + textno=ER_ROW_DOES_NOT_MATCH_GIVEN_PARTITION_SET; + break; case HA_ERR_INDEX_CORRUPT: textno= ER_INDEX_CORRUPT; break; @@ -3567,6 +3624,8 @@ err: */ uint handler::get_dup_key(int error) { + DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE || + m_lock_type != F_UNLCK); DBUG_ENTER("handler::get_dup_key"); table->file->errkey = (uint) -1; if (error == HA_ERR_FOUND_DUPP_KEY || error == HA_ERR_FOREIGN_DUPLICATE_KEY || @@ -3672,6 +3731,8 @@ void handler::drop_table(const char *name) int handler::ha_check(THD *thd, HA_CHECK_OPT *check_opt) { int error; + DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE || + m_lock_type != F_UNLCK); if ((table->s->mysql_version >= MYSQL_VERSION_ID) && (check_opt->sql_flags & TT_FOR_UPGRADE)) @@ -3758,6 +3819,8 @@ int handler::ha_bulk_update_row(const uchar *old_data, uchar *new_data, uint *dup_key_found) { + DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE || + m_lock_type == F_WRLCK); mark_trx_read_write(); return bulk_update_row(old_data, new_data, dup_key_found); @@ -3773,6 +3836,8 @@ handler::ha_bulk_update_row(const uchar *old_data, uchar *new_data, int handler::ha_delete_all_rows() { + DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE || + m_lock_type == F_WRLCK); mark_trx_read_write(); return delete_all_rows(); @@ -3788,6 +3853,8 @@ handler::ha_delete_all_rows() int handler::ha_truncate() { + DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE || + m_lock_type == F_WRLCK); mark_trx_read_write(); return truncate(); @@ -3803,6 +3870,8 @@ handler::ha_truncate() int handler::ha_reset_auto_increment(ulonglong value) { + DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE || + m_lock_type == F_WRLCK); mark_trx_read_write(); return reset_auto_increment(value); @@ -3818,6 +3887,8 @@ handler::ha_reset_auto_increment(ulonglong value) int handler::ha_optimize(THD* thd, HA_CHECK_OPT* check_opt) { + DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE || + m_lock_type == F_WRLCK); mark_trx_read_write(); return optimize(thd, check_opt); @@ -3833,6 +3904,8 @@ handler::ha_optimize(THD* thd, HA_CHECK_OPT* check_opt) int handler::ha_analyze(THD* thd, HA_CHECK_OPT* check_opt) { + DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE || + m_lock_type != F_UNLCK); mark_trx_read_write(); return analyze(thd, check_opt); @@ -3848,6 +3921,8 @@ handler::ha_analyze(THD* thd, HA_CHECK_OPT* check_opt) bool handler::ha_check_and_repair(THD *thd) { + DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE || + m_lock_type == F_UNLCK); mark_trx_read_write(); return check_and_repair(thd); @@ -3863,6 +3938,8 @@ handler::ha_check_and_repair(THD *thd) int handler::ha_disable_indexes(uint mode) { + DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE || + m_lock_type != F_UNLCK); mark_trx_read_write(); return disable_indexes(mode); @@ -3878,6 +3955,8 @@ handler::ha_disable_indexes(uint mode) int handler::ha_enable_indexes(uint mode) { + DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE || + m_lock_type != F_UNLCK); mark_trx_read_write(); return enable_indexes(mode); @@ -3893,26 +3972,116 @@ handler::ha_enable_indexes(uint mode) int handler::ha_discard_or_import_tablespace(my_bool discard) { + DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE || + m_lock_type == F_WRLCK); mark_trx_read_write(); return discard_or_import_tablespace(discard); } -/** - Prepare for alter: public interface. +bool handler::ha_prepare_inplace_alter_table(TABLE *altered_table, + Alter_inplace_info *ha_alter_info) +{ + DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE || + m_lock_type != F_UNLCK); + mark_trx_read_write(); + + return prepare_inplace_alter_table(altered_table, ha_alter_info); +} + - Called to prepare an *online* ALTER. +bool handler::ha_commit_inplace_alter_table(TABLE *altered_table, + Alter_inplace_info *ha_alter_info, + bool commit) +{ + /* + At this point we should have an exclusive metadata lock on the table. + The exception is if we're about to roll back changes (commit= false). + In this case, we might be rolling back after a failed lock upgrade, + so we could be holding the same lock level as for inplace_alter_table(). + */ + DBUG_ASSERT(ha_thd()->mdl_context.is_lock_owner(MDL_key::TABLE, + table->s->db.str, + table->s->table_name.str, + MDL_EXCLUSIVE) || + !commit); - @sa handler::prepare_for_alter() + return commit_inplace_alter_table(altered_table, ha_alter_info, commit); +} + + +/* + Default implementation to support in-place alter table + and old online add/drop index API */ -void -handler::ha_prepare_for_alter() +enum_alter_inplace_result +handler::check_if_supported_inplace_alter(TABLE *altered_table, + Alter_inplace_info *ha_alter_info) { - mark_trx_read_write(); + DBUG_ENTER("check_if_supported_alter"); + + HA_CREATE_INFO *create_info= ha_alter_info->create_info; - prepare_for_alter(); + Alter_inplace_info::HA_ALTER_FLAGS inplace_offline_operations= + Alter_inplace_info::ALTER_COLUMN_EQUAL_PACK_LENGTH | + Alter_inplace_info::ALTER_COLUMN_NAME | + Alter_inplace_info::ALTER_COLUMN_DEFAULT | + Alter_inplace_info::CHANGE_CREATE_OPTION | + Alter_inplace_info::ALTER_RENAME; + + /* Is there at least one operation that requires copy algorithm? */ + if (ha_alter_info->handler_flags & ~inplace_offline_operations) + DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED); + + /* + ALTER TABLE tbl_name CONVERT TO CHARACTER SET .. and + ALTER TABLE table_name DEFAULT CHARSET = .. most likely + change column charsets and so not supported in-place through + old API. + + Changing of PACK_KEYS, MAX_ROWS and ROW_FORMAT options were + not supported as in-place operations in old API either. + */ + if (create_info->used_fields & (HA_CREATE_USED_CHARSET | + HA_CREATE_USED_DEFAULT_CHARSET | + HA_CREATE_USED_PACK_KEYS | + HA_CREATE_USED_MAX_ROWS) || + (table->s->row_type != create_info->row_type)) + DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED); + + uint table_changes= (ha_alter_info->handler_flags & + Alter_inplace_info::ALTER_COLUMN_EQUAL_PACK_LENGTH) ? + IS_EQUAL_PACK_LENGTH : IS_EQUAL_YES; + if (table->file->check_if_incompatible_data(create_info, table_changes) + == COMPATIBLE_DATA_YES) + DBUG_RETURN(HA_ALTER_INPLACE_EXCLUSIVE_LOCK); + + DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED); +} + + +/* + Default implementation to support in-place alter table + and old online add/drop index API +*/ + +void handler::notify_table_changed() +{ + ha_create_handler_files(table->s->path.str, NULL, CHF_INDEX_FLAG, NULL); +} + + +void Alter_inplace_info::report_unsupported_error(const char *not_supported, + const char *try_instead) +{ + if (unsupported_reason == NULL) + my_error(ER_ALTER_OPERATION_NOT_SUPPORTED, MYF(0), + not_supported, try_instead); + else + my_error(ER_ALTER_OPERATION_NOT_SUPPORTED_REASON, MYF(0), + not_supported, unsupported_reason, try_instead); } @@ -3925,6 +4094,7 @@ handler::ha_prepare_for_alter() int handler::ha_rename_table(const char *from, const char *to) { + DBUG_ASSERT(m_lock_type == F_UNLCK); mark_trx_read_write(); return rename_table(from, to); @@ -3958,6 +4128,7 @@ handler::ha_delete_table(const char *name) void handler::ha_drop_table(const char *name) { + DBUG_ASSERT(m_lock_type == F_UNLCK); mark_trx_read_write(); return drop_table(name); @@ -3973,6 +4144,7 @@ handler::ha_drop_table(const char *name) int handler::ha_create(const char *name, TABLE *form, HA_CREATE_INFO *info) { + DBUG_ASSERT(m_lock_type == F_UNLCK); mark_trx_read_write(); return create(name, form, info); @@ -3989,6 +4161,13 @@ int handler::ha_create_handler_files(const char *name, const char *old_name, int action_flag, HA_CREATE_INFO *info) { + /* + Normally this is done when unlocked, but in fast_alter_partition_table, + it is done on an already locked handler when preparing to alter/rename + partitions. + */ + DBUG_ASSERT(m_lock_type == F_UNLCK || + (!old_name && strcmp(name, table_share->path.str))); mark_trx_read_write(); return create_handler_files(name, old_name, action_flag, info); @@ -4008,7 +4187,13 @@ handler::ha_change_partitions(HA_CREATE_INFO *create_info, ulonglong * const deleted, const uchar *pack_frm_data, size_t pack_frm_len) -{ +{ /* + Must have at least RDLCK or be a TMP table. Read lock is needed to read + from current partitions and write lock will be taken on new partitions. + */ + DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE || + m_lock_type != F_UNLCK); + mark_trx_read_write(); return change_partitions(create_info, path, copied, deleted, @@ -4025,6 +4210,8 @@ handler::ha_change_partitions(HA_CREATE_INFO *create_info, int handler::ha_drop_partitions(const char *path) { + DBUG_ASSERT(!table->db_stat); + mark_trx_read_write(); return drop_partitions(path); @@ -4040,6 +4227,8 @@ handler::ha_drop_partitions(const char *path) int handler::ha_rename_partitions(const char *path) { + DBUG_ASSERT(!table->db_stat); + mark_trx_read_write(); return rename_partitions(path); @@ -5345,6 +5534,12 @@ int handler::ha_external_lock(THD *thd, int lock_type) taken a table lock), ha_release_auto_increment() was too. */ DBUG_ASSERT(next_insert_id == 0); + /* Consecutive calls for lock without unlocking in between is not allowed */ + DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE || + ((lock_type != F_UNLCK && m_lock_type == F_UNLCK) || + lock_type == F_UNLCK)); + /* SQL HANDLER call locks/unlock while scanning (RND/INDEX). */ + DBUG_ASSERT(inited == NONE || table->open_by_handler); if (MYSQL_HANDLER_RDLOCK_START_ENABLED() || MYSQL_HANDLER_WRLOCK_START_ENABLED() || @@ -5377,7 +5572,14 @@ int handler::ha_external_lock(THD *thd, int lock_type) { error= external_lock(thd, lock_type); }) if (error == 0) + { + /* + The lock type is needed by MRR when creating a clone of this handler + object and for assert checking. + */ + m_lock_type= lock_type; cached_table_flags= table_flags(); + } if (MYSQL_HANDLER_RDLOCK_DONE_ENABLED() || MYSQL_HANDLER_WRLOCK_DONE_ENABLED() || @@ -5430,6 +5632,8 @@ int handler::ha_write_row(uchar *buf) { int error; Log_func *log_func= Write_rows_log_event::binlog_row_logging_function; + DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE || + m_lock_type == F_WRLCK); DBUG_ENTER("handler::ha_write_row"); DEBUG_SYNC_C("ha_write_row_start"); @@ -5456,6 +5660,8 @@ int handler::ha_update_row(const uchar *old_data, uchar *new_data) { int error; Log_func *log_func= Update_rows_log_event::binlog_row_logging_function; + DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE || + m_lock_type == F_WRLCK); /* Some storage engines require that the new record is in record[0] @@ -5483,6 +5689,8 @@ int handler::ha_delete_row(const uchar *buf) { int error; Log_func *log_func= Delete_rows_log_event::binlog_row_logging_function; + DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE || + m_lock_type == F_WRLCK); MYSQL_DELETE_ROW_START(table_share->db.str, table_share->table_name.str); mark_trx_read_write(); @@ -5513,6 +5721,77 @@ void handler::use_hidden_primary_key() } +/** + Get an initialized ha_share. + + @return Initialized ha_share + @retval NULL ha_share is not yet initialized. + @retval != NULL previous initialized ha_share. + + @note + If not a temp table, then LOCK_ha_data must be held. +*/ + +Handler_share *handler::get_ha_share_ptr() +{ + DBUG_ENTER("handler::get_ha_share_ptr"); + DBUG_ASSERT(ha_share && table_share); + +#ifndef DBUG_OFF + if (table_share->tmp_table == NO_TMP_TABLE) + mysql_mutex_assert_owner(&table_share->LOCK_ha_data); +#endif + + DBUG_RETURN(*ha_share); +} + + +/** + Set ha_share to be used by all instances of the same table/partition. + + @param ha_share Handler_share to be shared. + + @note + If not a temp table, then LOCK_ha_data must be held. +*/ + +void handler::set_ha_share_ptr(Handler_share *arg_ha_share) +{ + DBUG_ENTER("handler::set_ha_share_ptr"); + DBUG_ASSERT(ha_share); +#ifndef DBUG_OFF + if (table_share->tmp_table == NO_TMP_TABLE) + mysql_mutex_assert_owner(&table_share->LOCK_ha_data); +#endif + + *ha_share= arg_ha_share; + DBUG_VOID_RETURN; +} + + +/** + Take a lock for protecting shared handler data. +*/ + +void handler::lock_shared_ha_data() +{ + DBUG_ASSERT(table_share); + if (table_share->tmp_table == NO_TMP_TABLE) + mysql_mutex_lock(&table_share->LOCK_ha_data); +} + + +/** + Release lock for protecting ha_share. +*/ + +void handler::unlock_shared_ha_data() +{ + DBUG_ASSERT(table_share); + if (table_share->tmp_table == NO_TMP_TABLE) + mysql_mutex_unlock(&table_share->LOCK_ha_data); +} + /** @brief Dummy function which accept information about log files which is not need by handlers diff --git a/sql/handler.h b/sql/handler.h index e74a2c6c42c..8464bbb3823 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -404,6 +404,13 @@ enum row_type { ROW_TYPE_NOT_USED=-1, ROW_TYPE_DEFAULT, ROW_TYPE_FIXED, /** Unused. Reserved for future versions. */ ROW_TYPE_PAGE }; +/* Specifies data storage format for individual columns */ +enum column_format_type { + COLUMN_FORMAT_TYPE_DEFAULT= 0, /* Not specified (use engine default) */ + COLUMN_FORMAT_TYPE_FIXED= 1, /* FIXED format */ + COLUMN_FORMAT_TYPE_DYNAMIC= 2 /* DYNAMIC format */ +}; + enum enum_binlog_func { BFN_RESET_LOGS= 1, BFN_RESET_SLAVE= 2, @@ -1243,6 +1250,7 @@ inline LEX_STRING *hton_name(const handlerton *hton) #define HTON_ALTER_NOT_SUPPORTED (1 << 1) //Engine does not support alter #define HTON_CAN_RECREATE (1 << 2) //Delete all is used for truncate #define HTON_HIDDEN (1 << 3) //Engine does not appear in lists +#define HTON_FLUSH_AFTER_RENAME (1 << 4) #define HTON_NOT_USER_SELECTABLE (1 << 5) #define HTON_TEMPORARY_NOT_SUPPORTED (1 << 6) //Having temporary tables not supported #define HTON_SUPPORT_LOG_TABLES (1 << 7) //Engine supports log tables @@ -1304,6 +1312,22 @@ struct THD_TRANS void reset() { no_2pc= FALSE; modified_non_trans_table= FALSE; } bool is_empty() const { return ha_list == NULL; } THD_TRANS() {} /* Remove gcc warning */ + + unsigned int m_unsafe_rollback_flags; + /* + Define the type of statemens which cannot be rolled back safely. + Each type occupies one bit in m_unsafe_rollback_flags. + */ + static unsigned int const MODIFIED_NON_TRANS_TABLE= 0x01; + static unsigned int const CREATED_TEMP_TABLE= 0x02; + static unsigned int const DROPPED_TEMP_TABLE= 0x04; + + void mark_created_temp_table() + { + DBUG_PRINT("debug", ("mark_created_temp_table")); + m_unsafe_rollback_flags|= CREATED_TEMP_TABLE; + } + }; @@ -1427,7 +1451,6 @@ struct st_table_log_memory_entry; class partition_info; struct st_partition_iter; -#define NOT_A_PARTITION_ID ((uint32)-1) enum ha_choice { HA_CHOICE_UNDEF, HA_CHOICE_NO, HA_CHOICE_YES }; @@ -1438,6 +1461,7 @@ enum enum_stats_auto_recalc { HA_STATS_AUTO_RECALC_DEFAULT= 0, typedef struct st_ha_create_information { CHARSET_INFO *table_charset, *default_table_charset; + LEX_CUSTRING tabledef_version; LEX_STRING connect_string; const char *password, *tablespace; LEX_STRING comment; @@ -1445,7 +1469,7 @@ typedef struct st_ha_create_information const char *alias; ulonglong max_rows,min_rows; ulonglong auto_increment_value; - ulong table_options; + ulong table_options; ///< HA_OPTION_ values ulong avg_row_length; ulong used_fields; ulong key_block_size; @@ -1469,18 +1493,21 @@ typedef struct st_ha_create_information uint merge_insert_method; uint extra_size; /* length of extra data segment */ enum ha_choice transactional; - bool frm_only; ///< 1 if no ha_create_table() bool varchar; ///< 1 if table has a VARCHAR enum ha_storage_media storage_media; ///< DEFAULT, DISK or MEMORY enum ha_choice page_checksum; ///< If we have page_checksums engine_option_value *option_list; ///< list of table create options + /* the following three are only for ALTER TABLE, check_if_incompatible_data() */ ha_table_option_struct *option_struct; ///< structure with parsed table options ha_field_option_struct **fields_option_struct; ///< array of field option structures ha_index_option_struct **indexes_option_struct; ///< array of index option structures + + bool tmp_table() { return options & HA_LEX_CREATE_TMP_TABLE; } } HA_CREATE_INFO; + /** In-place alter handler context. @@ -2182,34 +2209,61 @@ uint calculate_key_len(TABLE *, uint, const uchar *, key_part_map); #define make_prev_keypart_map(N) (((key_part_map)1 << (N)) - 1) -/** - Index creation context. - Created by handler::add_index() and destroyed by handler::final_add_index(). - And finally freed at the end of the statement. - (Sql_alloc does not free in delete). -*/ - -class handler_add_index : public Sql_alloc +/** Base class to be used by handlers different shares */ +class Handler_share { public: - /* Table where the indexes are added */ - TABLE* const table; - /* Indexes being created */ - KEY* const key_info; - /* Size of key_info[] */ - const uint num_of_keys; - handler_add_index(TABLE *table_arg, KEY *key_info_arg, uint num_of_keys_arg) - : table (table_arg), key_info (key_info_arg), num_of_keys (num_of_keys_arg) - {} - virtual ~handler_add_index() {} + Handler_share() {} + virtual ~Handler_share() {} }; -class Query_cache; -struct Query_cache_block_table; + /** The handler class is the interface for dynamically loadable storage engines. Do not add ifdefs and take care when adding or changing virtual functions to avoid vtable confusion + + Functions in this class accept and return table columns data. Two data + representation formats are used: + 1. TableRecordFormat - Used to pass [partial] table records to/from + storage engine + + 2. KeyTupleFormat - used to pass index search tuples (aka "keys") to + storage engine. See opt_range.cc for description of this format. + + TableRecordFormat + ================= + [Warning: this description is work in progress and may be incomplete] + The table record is stored in a fixed-size buffer: + + record: null_bytes, column1_data, column2_data, ... + + The offsets of the parts of the buffer are also fixed: every column has + an offset to its column{i}_data, and if it is nullable it also has its own + bit in null_bytes. + + The record buffer only includes data about columns that are marked in the + relevant column set (table->read_set and/or table->write_set, depending on + the situation). + <not-sure>It could be that it is required that null bits of non-present + columns are set to 1</not-sure> + + VARIOUS EXCEPTIONS AND SPECIAL CASES + + If the table has no nullable columns, then null_bytes is still + present, its length is one byte <not-sure> which must be set to 0xFF + at all times. </not-sure> + + If the table has columns of type BIT, then certain bits from those columns + may be stored in null_bytes as well. Grep around for Field_bit for + details. + + For blob columns (see Field_blob), the record buffer stores length of the + data, following by memory pointer to the blob data. The pointer is owned + by the storage engine and is valid until the next operation. + + If a blob column has NULL value, then its length and blob data pointer + must be set to 0. */ class handler :public Sql_alloc @@ -2262,7 +2316,6 @@ public: uint ref_length; FT_INFO *ft_handler; enum {NONE=0, INDEX, RND} inited; - bool locked; bool implicit_emptied; /* Can be !=0 only if HEAP */ bool mark_trx_done; const COND *pushed_cond; @@ -2323,6 +2376,21 @@ public: virtual void unbind_psi(); virtual void rebind_psi(); +private: + /** + The lock type set by when calling::ha_external_lock(). This is + propagated down to the storage engine. The reason for also storing + it here, is that when doing MRR we need to create/clone a second handler + object. This cloned handler object needs to know about the lock_type used. + */ + int m_lock_type; + /** + Pointer where to store/retrieve the Handler_share pointer. + For non partitioned handlers this is &TABLE_SHARE::ha_share. + */ + Handler_share **ha_share; + +public: handler(handlerton *ht_arg, TABLE_SHARE *share_arg) :table_share(share_arg), table(0), estimation_rows_to_insert(0), ht(ht_arg), @@ -2330,18 +2398,21 @@ public: in_range_check_pushed_down(FALSE), ref_length(sizeof(my_off_t)), ft_handler(0), inited(NONE), - locked(FALSE), implicit_emptied(0), mark_trx_done(FALSE), + implicit_emptied(0), mark_trx_done(FALSE), pushed_cond(0), next_insert_id(0), insert_id_for_cur_row(0), pushed_idx_cond(NULL), pushed_idx_cond_keyno(MAX_KEY), auto_inc_intervals_count(0), - m_psi(NULL) + m_psi(NULL), m_lock_type(F_UNLCK), ha_share(NULL) { + DBUG_PRINT("info", + ("handler created F_UNLCK %d F_RDLCK %d F_WRLCK %d", + F_UNLCK, F_RDLCK, F_WRLCK)); reset_statistics(); } virtual ~handler(void) { - DBUG_ASSERT(locked == FALSE); + DBUG_ASSERT(m_lock_type == F_UNLCK); DBUG_ASSERT(inited == NONE); } virtual handler *clone(const char *name, MEM_ROOT *mem_root); @@ -2456,7 +2527,6 @@ public: int ha_disable_indexes(uint mode); int ha_enable_indexes(uint mode); int ha_discard_or_import_tablespace(my_bool discard); - void ha_prepare_for_alter(); int ha_rename_table(const char *from, const char *to); int ha_delete_table(const char *name); void ha_drop_table(const char *name); @@ -2756,6 +2826,7 @@ private: */ virtual int rnd_pos_by_record(uchar *record) { + DBUG_ASSERT(table_flags() & HA_PRIMARY_KEY_REQUIRED_FOR_POSITION); position(record); return rnd_pos(record, ref); } @@ -2871,10 +2942,15 @@ public: { return FALSE; } virtual char* get_foreign_key_create_info() { return(NULL);} /* gets foreign key create string from InnoDB */ - virtual char* get_tablespace_name(THD *thd, char *name, uint name_len) - { return(NULL);} /* gets tablespace name from handler */ - /** used in ALTER TABLE; 1 if changing storage engine is allowed */ - virtual bool can_switch_engines() { return 1; } + /** + Used in ALTER TABLE to check if changing storage engine is allowed. + + @note Called without holding thr_lock.c lock. + + @retval true Changing storage engine is allowed. + @retval false Changing storage engine not allowed. + */ + virtual bool can_switch_engines() { return true; } virtual int can_continue_handler_scan() { return 0; } /** Get the list of foreign keys in this table. @@ -2936,42 +3012,6 @@ public: virtual ulong index_flags(uint idx, uint part, bool all_parts) const =0; -/** - First phase of in-place add index. - Handlers are supposed to create new indexes here but not make them - visible. - - @param table_arg Table to add index to - @param key_info Information about new indexes - @param num_of_key Number of new indexes - @param add[out] Context of handler specific information needed - for final_add_index(). - - @note This function can be called with less than exclusive metadata - lock depending on which flags are listed in alter_table_flags. -*/ - virtual int add_index(TABLE *table_arg, KEY *key_info, uint num_of_keys, - handler_add_index **add) - { return (HA_ERR_WRONG_COMMAND); } - -/** - Second and last phase of in-place add index. - Commit or rollback pending new indexes. - - @param add Context of handler specific information from add_index(). - @param commit If true, commit. If false, rollback index changes. - - @note This function is called with exclusive metadata lock. -*/ - virtual int final_add_index(handler_add_index *add, bool commit) - { return (HA_ERR_WRONG_COMMAND); } - - virtual int prepare_drop_index(TABLE *table_arg, uint *key_num, - uint num_of_keys) - { return (HA_ERR_WRONG_COMMAND); } - virtual int final_drop_index(TABLE *table_arg) - { return (HA_ERR_WRONG_COMMAND); } - uint max_record_length() const { return MY_MIN(HA_MAX_REC_LENGTH, max_supported_record_length()); } uint max_keys() const @@ -3179,10 +3219,266 @@ public: pushed_idx_cond_keyno= MAX_KEY; in_range_check_pushed_down= false; } + /** + Part of old, deprecated in-place ALTER API. + */ virtual bool check_if_incompatible_data(HA_CREATE_INFO *create_info, uint table_changes) { return COMPATIBLE_DATA_NO; } + /* On-line/in-place ALTER TABLE interface. */ + + /* + Here is an outline of on-line/in-place ALTER TABLE execution through + this interface. + + Phase 1 : Initialization + ======================== + During this phase we determine which algorithm should be used + for execution of ALTER TABLE and what level concurrency it will + require. + + *) This phase starts by opening the table and preparing description + of the new version of the table. + *) Then we check if it is impossible even in theory to carry out + this ALTER TABLE using the in-place algorithm. For example, because + we need to change storage engine or the user has explicitly requested + usage of the "copy" algorithm. + *) If in-place ALTER TABLE is theoretically possible, we continue + by compiling differences between old and new versions of the table + in the form of HA_ALTER_FLAGS bitmap. We also build a few + auxiliary structures describing requested changes and store + all these data in the Alter_inplace_info object. + *) Then the handler::check_if_supported_inplace_alter() method is called + in order to find if the storage engine can carry out changes requested + by this ALTER TABLE using the in-place algorithm. To determine this, + the engine can rely on data in HA_ALTER_FLAGS/Alter_inplace_info + passed to it as well as on its own checks. If the in-place algorithm + can be used for this ALTER TABLE, the level of required concurrency for + its execution is also returned. + If any errors occur during the handler call, ALTER TABLE is aborted + and no further handler functions are called. + *) Locking requirements of the in-place algorithm are compared to any + concurrency requirements specified by user. If there is a conflict + between them, we either switch to the copy algorithm or emit an error. + + Phase 2 : Execution + =================== + + In this phase the operations are executed. + + *) As the first step, we acquire a lock corresponding to the concurrency + level which was returned by handler::check_if_supported_inplace_alter() + and requested by the user. This lock is held for most of the + duration of in-place ALTER (if HA_ALTER_INPLACE_SHARED_LOCK_AFTER_PREPARE + or HA_ALTER_INPLACE_NO_LOCK_AFTER_PREPARE were returned we acquire an + exclusive lock for duration of the next step only). + *) After that we call handler::ha_prepare_inplace_alter_table() to give the + storage engine a chance to update its internal structures with a higher + lock level than the one that will be used for the main step of algorithm. + After that we downgrade the lock if it is necessary. + *) After that, the main step of this phase and algorithm is executed. + We call the handler::ha_inplace_alter_table() method, which carries out the + changes requested by ALTER TABLE but does not makes them visible to other + connections yet. + *) We ensure that no other connection uses the table by upgrading our + lock on it to exclusive. + *) a) If the previous step succeeds, handler::ha_commit_inplace_alter_table() is + called to allow the storage engine to do any final updates to its structures, + to make all earlier changes durable and visible to other connections. + b) If we have failed to upgrade lock or any errors have occured during the + handler functions calls (including commit), we call + handler::ha_commit_inplace_alter_table() + to rollback all changes which were done during previous steps. + + Phase 3 : Final + =============== + + In this phase we: + + *) Update SQL-layer data-dictionary by installing .FRM file for the new version + of the table. + *) Inform the storage engine about this change by calling the + handler::ha_notify_table_changed() method. + *) Destroy the Alter_inplace_info and handler_ctx objects. + + */ + + /** + Check if a storage engine supports a particular alter table in-place + + @param altered_table TABLE object for new version of table. + @param ha_alter_info Structure describing changes to be done + by ALTER TABLE and holding data used + during in-place alter. + + @retval HA_ALTER_ERROR Unexpected error. + @retval HA_ALTER_INPLACE_NOT_SUPPORTED Not supported, must use copy. + @retval HA_ALTER_INPLACE_EXCLUSIVE_LOCK Supported, but requires X lock. + @retval HA_ALTER_INPLACE_SHARED_LOCK_AFTER_PREPARE + Supported, but requires SNW lock + during main phase. Prepare phase + requires X lock. + @retval HA_ALTER_INPLACE_SHARED_LOCK Supported, but requires SNW lock. + @retval HA_ALTER_INPLACE_NO_LOCK_AFTER_PREPARE + Supported, concurrent reads/writes + allowed. However, prepare phase + requires X lock. + @retval HA_ALTER_INPLACE_NO_LOCK Supported, concurrent + reads/writes allowed. + + @note The default implementation uses the old in-place ALTER API + to determine if the storage engine supports in-place ALTER or not. + + @note Called without holding thr_lock.c lock. + */ + virtual enum_alter_inplace_result + check_if_supported_inplace_alter(TABLE *altered_table, + Alter_inplace_info *ha_alter_info); + + + /** + Public functions wrapping the actual handler call. + @see prepare_inplace_alter_table() + */ + bool ha_prepare_inplace_alter_table(TABLE *altered_table, + Alter_inplace_info *ha_alter_info); + + + /** + Public function wrapping the actual handler call. + @see inplace_alter_table() + */ + bool ha_inplace_alter_table(TABLE *altered_table, + Alter_inplace_info *ha_alter_info) + { + return inplace_alter_table(altered_table, ha_alter_info); + } + + + /** + Public function wrapping the actual handler call. + Allows us to enforce asserts regardless of handler implementation. + @see commit_inplace_alter_table() + */ + bool ha_commit_inplace_alter_table(TABLE *altered_table, + Alter_inplace_info *ha_alter_info, + bool commit); + + + /** + Public function wrapping the actual handler call. + @see notify_table_changed() + */ + void ha_notify_table_changed() + { + notify_table_changed(); + } + + +protected: + /** + Allows the storage engine to update internal structures with concurrent + writes blocked. If check_if_supported_inplace_alter() returns + HA_ALTER_INPLACE_NO_LOCK_AFTER_PREPARE or + HA_ALTER_INPLACE_SHARED_AFTER_PREPARE, this function is called with + exclusive lock otherwise the same level of locking as for + inplace_alter_table() will be used. + + @note Storage engines are responsible for reporting any errors by + calling my_error()/print_error() + + @note If this function reports error, commit_inplace_alter_table() + will be called with commit= false. + + @note For partitioning, failing to prepare one partition, means that + commit_inplace_alter_table() will be called to roll back changes for + all partitions. This means that commit_inplace_alter_table() might be + called without prepare_inplace_alter_table() having been called first + for a given partition. + + @param altered_table TABLE object for new version of table. + @param ha_alter_info Structure describing changes to be done + by ALTER TABLE and holding data used + during in-place alter. + + @retval true Error + @retval false Success + */ + virtual bool prepare_inplace_alter_table(TABLE *altered_table, + Alter_inplace_info *ha_alter_info) + { return false; } + + + /** + Alter the table structure in-place with operations specified using HA_ALTER_FLAGS + and Alter_inplace_info. The level of concurrency allowed during this + operation depends on the return value from check_if_supported_inplace_alter(). + + @note Storage engines are responsible for reporting any errors by + calling my_error()/print_error() + + @note If this function reports error, commit_inplace_alter_table() + will be called with commit= false. + + @param altered_table TABLE object for new version of table. + @param ha_alter_info Structure describing changes to be done + by ALTER TABLE and holding data used + during in-place alter. + + @retval true Error + @retval false Success + */ + virtual bool inplace_alter_table(TABLE *altered_table, + Alter_inplace_info *ha_alter_info) + { return false; } + + + /** + Commit or rollback the changes made during prepare_inplace_alter_table() + and inplace_alter_table() inside the storage engine. + Note that in case of rollback the allowed level of concurrency during + this operation will be the same as for inplace_alter_table() and thus + might be higher than during prepare_inplace_alter_table(). (For example, + concurrent writes were blocked during prepare, but might not be during + rollback). + + @note Storage engines are responsible for reporting any errors by + calling my_error()/print_error() + + @note If this function with commit= true reports error, it will be called + again with commit= false. + + @note In case of partitioning, this function might be called for rollback + without prepare_inplace_alter_table() having been called first. + @see prepare_inplace_alter_table(). + + @param altered_table TABLE object for new version of table. + @param ha_alter_info Structure describing changes to be done + by ALTER TABLE and holding data used + during in-place alter. + @param commit True => Commit, False => Rollback. + + @retval true Error + @retval false Success + */ + virtual bool commit_inplace_alter_table(TABLE *altered_table, + Alter_inplace_info *ha_alter_info, + bool commit) + { return false; } + + + /** + Notify the storage engine that the table structure (.FRM) has been updated. + + @note No errors are allowed during notify_table_changed(). + */ + virtual void notify_table_changed(); + +public: + /* End of On-line/in-place ALTER TABLE interface. */ + + /** use_hidden_primary_key() is called in case of an update/delete when (table_flags() and HA_PRIMARY_KEY_REQUIRED_FOR_DELETE) is defined @@ -3226,32 +3522,6 @@ protected: */ PSI_table_share *ha_table_share_psi(const TABLE_SHARE *share) const; - inline void psi_open() - { - DBUG_ASSERT(m_psi == NULL); - DBUG_ASSERT(table_share != NULL); -#ifdef HAVE_PSI_INTERFACE - if (PSI_server) - { - PSI_table_share *share_psi= ha_table_share_psi(table_share); - if (share_psi) - m_psi= PSI_server->open_table(share_psi, this); - } -#endif - } - - inline void psi_close() - { -#ifdef HAVE_PSI_INTERFACE - if (PSI_server && m_psi) - { - PSI_server->close_table(m_psi); - m_psi= NULL; /* instrumentation handle, invalid after close_table() */ - } -#endif - DBUG_ASSERT(m_psi == NULL); - } - /** Default rename_table() and delete_table() rename/delete files with a given name and extensions from bas_ext(). @@ -3301,6 +3571,14 @@ private: return HA_ERR_WRONG_COMMAND; } + /** + Update a single row. + + Note: If HA_ERR_FOUND_DUPP_KEY is returned, the handler must read + all columns of the row so MySQL can create an error message. If + the columns required for the error message are not read, the error + message will contain garbage. + */ virtual int update_row(const uchar *old_data __attribute__((unused)), uchar *new_data __attribute__((unused))) { @@ -3364,9 +3642,12 @@ private: } virtual void start_bulk_insert(ha_rows rows, uint flags) {} virtual int end_bulk_insert() { return 0; } +protected: virtual int index_read(uchar * buf, const uchar * key, uint key_len, enum ha_rkey_function find_flag) { return HA_ERR_WRONG_COMMAND; } + friend class ha_partition; +public: /** This method is similar to update_row, however the handler doesn't need to execute the updates at this point in time. The handler can be certain @@ -3451,7 +3732,16 @@ private: { return HA_ERR_WRONG_COMMAND; } virtual int rename_partitions(const char *path) { return HA_ERR_WRONG_COMMAND; } - friend class ha_partition; + virtual bool set_ha_share_ref(Handler_share **arg_ha_share) + { + DBUG_ASSERT(!ha_share); + DBUG_ASSERT(arg_ha_share); + if (ha_share || !arg_ha_share) + return true; + ha_share= arg_ha_share; + return false; + } + int get_lock_type() const { return m_lock_type; } public: /* XXX to be removed, see ha_partition::partition_ht() */ virtual handlerton *partition_ht() const @@ -3460,6 +3750,11 @@ public: inline int ha_update_tmp_row(const uchar * old_data, uchar * new_data); friend enum icp_result handler_index_cond_check(void* h_arg); +protected: + Handler_share *get_ha_share_ptr(); + void set_ha_share_ptr(Handler_share *arg_ha_share); + void lock_shared_ha_data(); + void unlock_shared_ha_data(); }; #include "multi_range_read.h" @@ -3541,6 +3836,8 @@ int ha_discover(THD* thd, const char* dbname, const char* name, int ha_find_files(THD *thd,const char *db,const char *path, const char *wild, bool dir, List<LEX_STRING>* files); int ha_table_exists_in_engine(THD* thd, const char* db, const char* name); +bool ha_check_if_supported_system_table(handlerton *hton, const char* db, + const char* table_name); /* key cache */ extern "C" int ha_init_key_cache(const char *name, KEY_CACHE *key_cache, void *); diff --git a/sql/item.cc b/sql/item.cc index 80b3269dc63..035f017464c 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -341,7 +341,7 @@ my_decimal *Item::val_decimal_from_string(my_decimal *decimal_value) decimal_value) & E_DEC_BAD_NUM) { ErrConvString err(res); - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, ER_TRUNCATED_WRONG_VALUE, ER(ER_TRUNCATED_WRONG_VALUE), "DECIMAL", err.ptr()); @@ -980,11 +980,11 @@ void Item::set_name(const char *str, uint length, CHARSET_INFO *cs) MY_MIN(sizeof(buff)-1, length + (int) (str-str_start))); if (length == 0) - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, ER_NAME_BECOMES_EMPTY, ER(ER_NAME_BECOMES_EMPTY), buff); else - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, ER_REMOVED_SPACES, ER(ER_REMOVED_SPACES), buff); } @@ -1127,11 +1127,26 @@ Item *Item_static_float_func::safe_charset_converter(CHARSET_INFO *tocs) Item *Item_string::safe_charset_converter(CHARSET_INFO *tocs) { + return charset_converter(tocs, true); +} + + +/** + Convert a string item into the requested character set. + + @param tocs Character set to to convert the string to. + @param lossless Whether data loss is acceptable. + + @return A new item representing the converted string. +*/ +Item *Item_string::charset_converter(CHARSET_INFO *tocs, bool lossless) +{ Item_string *conv; uint conv_errors; char *ptr; String tmp, cstr, *ostr= val_str(&tmp); cstr.copy(ostr->ptr(), ostr->length(), ostr->charset(), tocs, &conv_errors); + conv_errors= lossless && conv_errors; if (conv_errors || !(conv= new Item_string(cstr.ptr(), cstr.length(), cstr.charset(), collation.derivation))) @@ -1152,7 +1167,6 @@ Item *Item_string::safe_charset_converter(CHARSET_INFO *tocs) return conv; } - Item *Item_param::safe_charset_converter(CHARSET_INFO *tocs) { if (const_item()) @@ -2966,7 +2980,7 @@ double_from_string_with_check(CHARSET_INFO *cs, const char *cptr, We can use err.ptr() here as ErrConvString is guranteed to put an end \0 here. */ - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, ER_TRUNCATED_WRONG_VALUE, ER(ER_TRUNCATED_WRONG_VALUE), "DOUBLE", err.ptr()); @@ -3003,7 +3017,7 @@ longlong_from_string_with_check(CHARSET_INFO *cs, const char *cptr, (end != end_of_num && !check_if_only_end_space(cs, end_of_num, end)))) { ErrConvString err(cptr, end - cptr, cs); - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, ER_TRUNCATED_WRONG_VALUE, ER(ER_TRUNCATED_WRONG_VALUE), "INTEGER", err.ptr()); @@ -3216,7 +3230,7 @@ void Item_param::set_time(MYSQL_TIME *tm, timestamp_type time_type, value.time.second_part > TIME_MAX_SECOND_PART) { ErrConvTime str(&value.time); - make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + make_truncated_value_warning(current_thd, Sql_condition::WARN_LEVEL_WARN, &str, time_type, 0); set_zero_time(&value.time, MYSQL_TIMESTAMP_ERROR); } @@ -4293,7 +4307,7 @@ static bool mark_as_dependent(THD *thd, SELECT_LEX *last, SELECT_LEX *current, return TRUE; if (thd->lex->describe & DESCRIBE_EXTENDED) { - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_WARN_FIELD_RESOLVED, ER(ER_WARN_FIELD_RESOLVED), db_name, (db_name[0] ? "." : ""), table_name, (table_name [0] ? "." : ""), @@ -4541,7 +4555,7 @@ resolve_ref_in_select_and_group(THD *thd, Item_ident *ref, SELECT_LEX *select) !((*group_by_ref)->eq(*select_ref, 0))) { ambiguous_fields= TRUE; - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_NON_UNIQ_ERROR, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_NON_UNIQ_ERROR, ER(ER_NON_UNIQ_ERROR), ref->full_name(), current_thd->where); @@ -5523,7 +5537,7 @@ String *Item::check_well_formed_result(String *str, bool send_error) { str->length(wlen); } - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_INVALID_CHARACTER_STRING, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_INVALID_CHARACTER_STRING, ER(ER_INVALID_CHARACTER_STRING), cs->csname, hexbuf); } return str; @@ -6203,7 +6217,7 @@ int Item_hex_string::save_in_field(Field *field, bool no_conversions) warn: if (!field->store((longlong) nr, TRUE)) - field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, + field->set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); return 1; } @@ -8134,7 +8148,7 @@ int Item_default_value::save_in_field(Field *field_arg, bool no_conversions) { TABLE_LIST *view= cached_table->top_table(); push_warning_printf(field_arg->table->in_use, - MYSQL_ERROR::WARN_LEVEL_WARN, + Sql_condition::WARN_LEVEL_WARN, ER_NO_DEFAULT_FOR_VIEW_FIELD, ER(ER_NO_DEFAULT_FOR_VIEW_FIELD), view->view_db.str, @@ -8143,7 +8157,7 @@ int Item_default_value::save_in_field(Field *field_arg, bool no_conversions) else { push_warning_printf(field_arg->table->in_use, - MYSQL_ERROR::WARN_LEVEL_WARN, + Sql_condition::WARN_LEVEL_WARN, ER_NO_DEFAULT_FOR_FIELD, ER(ER_NO_DEFAULT_FOR_FIELD), field_arg->field_name); diff --git a/sql/item.h b/sql/item.h index 8ec701e0255..2d6cf68d818 100644 --- a/sql/item.h +++ b/sql/item.h @@ -1365,7 +1365,7 @@ public: virtual void bring_value() {} Field *tmp_table_field_from_field_type(TABLE *table, bool fixed_length); - virtual Item_field *filed_for_view_update() { return 0; } + virtual Item_field *field_for_view_update() { return 0; } virtual Item *neg_transformer(THD *thd) { return NULL; } virtual Item *update_value_transformer(uchar *select_arg) { return this; } @@ -2079,7 +2079,7 @@ public: bool set_no_const_sub(uchar *arg); Item *replace_equal_field(uchar *arg); inline uint32 max_disp_length() { return field->max_display_length(); } - Item_field *filed_for_view_update() { return this; } + Item_field *field_for_view_update() { return this; } Item *safe_charset_converter(CHARSET_INFO *tocs); int fix_outer_field(THD *thd, Field **field, Item **reference); virtual Item *update_value_transformer(uchar *select_arg); @@ -2570,6 +2570,7 @@ public: str_value.length(), collation.collation); } Item *safe_charset_converter(CHARSET_INFO *tocs); + Item *charset_converter(CHARSET_INFO *tocs, bool lossless); inline void append(char *str, uint length) { str_value.append(str, length); @@ -2914,8 +2915,8 @@ public: } virtual void print(String *str, enum_query_type query_type); void cleanup(); - Item_field *filed_for_view_update() - { return (*ref)->filed_for_view_update(); } + Item_field *field_for_view_update() + { return (*ref)->field_for_view_update(); } virtual Ref_Type ref_type() { return REF; } // Row emulation: forwarding of ROW-related calls to ref @@ -3121,8 +3122,8 @@ public: } bool enumerate_field_refs_processor(uchar *arg) { return orig_item->enumerate_field_refs_processor(arg); } - Item_field *filed_for_view_update() - { return orig_item->filed_for_view_update(); } + Item_field *field_for_view_update() + { return orig_item->field_for_view_update(); } /* Row emulation: forwarding of ROW-related calls to orig_item */ uint cols() diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index d49af9bc2a0..ec785eaed49 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -746,7 +746,7 @@ bool get_mysql_time_from_str(THD *thd, String *str, timestamp_type warn_type, } if (error > 0) - make_truncated_value_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + make_truncated_value_warning(thd, Sql_condition::WARN_LEVEL_WARN, &err, warn_type, warn_name); return value; diff --git a/sql/item_create.cc b/sql/item_create.cc index 1475a44f32a..0a28c6414ec 100644 --- a/sql/item_create.cc +++ b/sql/item_create.cc @@ -2053,19 +2053,6 @@ protected: }; -class Create_func_row_count : public Create_func_arg0 -{ -public: - virtual Item *create_builder(THD *thd); - - static Create_func_row_count s_singleton; - -protected: - Create_func_row_count() {} - virtual ~Create_func_row_count() {} -}; - - class Create_func_rpad : public Create_func_arg3 { public: @@ -4795,18 +4782,6 @@ Create_func_round::create_native(THD *thd, LEX_STRING name, } -Create_func_row_count Create_func_row_count::s_singleton; - -Item* -Create_func_row_count::create_builder(THD *thd) -{ - DBUG_ENTER("Create_func_row_count::create"); - thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION); - thd->lex->safe_to_cache_query= 0; - DBUG_RETURN(new (thd->mem_root) Item_func_row_count()); -} - - Create_func_rpad Create_func_rpad::s_singleton; Item* @@ -5475,7 +5450,6 @@ static Native_func_registry func_array[] = { { C_STRING_WITH_LEN("RELEASE_LOCK") }, BUILDER(Create_func_release_lock)}, { { C_STRING_WITH_LEN("REVERSE") }, BUILDER(Create_func_reverse)}, { { C_STRING_WITH_LEN("ROUND") }, BUILDER(Create_func_round)}, - { { C_STRING_WITH_LEN("ROW_COUNT") }, BUILDER(Create_func_row_count)}, { { C_STRING_WITH_LEN("RPAD") }, BUILDER(Create_func_rpad)}, { { C_STRING_WITH_LEN("RTRIM") }, BUILDER(Create_func_rtrim)}, { { C_STRING_WITH_LEN("SEC_TO_TIME") }, BUILDER(Create_func_sec_to_time)}, diff --git a/sql/item_func.cc b/sql/item_func.cc index a8631093f3e..27a27727be7 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -695,7 +695,7 @@ void Item_func::signal_divide_by_null() { THD *thd= current_thd; if (thd->variables.sql_mode & MODE_ERROR_FOR_DIVISION_BY_ZERO) - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_DIVISION_BY_ZERO, + push_warning(thd, Sql_condition::WARN_LEVEL_WARN, ER_DIVISION_BY_ZERO, ER(ER_DIVISION_BY_ZERO)); null_value= 1; } @@ -1030,7 +1030,7 @@ longlong Item_func_signed::val_int_from_str(int *error) char err_buff[128]; String err_tmp(err_buff,(uint32) sizeof(err_buff), system_charset_info); err_tmp.copy(start, length, system_charset_info); - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, ER_TRUNCATED_WRONG_VALUE, ER(ER_TRUNCATED_WRONG_VALUE), "INTEGER", err_tmp.c_ptr()); @@ -1067,7 +1067,7 @@ longlong Item_func_signed::val_int() return value; err: - push_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_NOTE, ER_UNKNOWN_ERROR, + push_warning(current_thd, Sql_condition::WARN_LEVEL_NOTE, ER_UNKNOWN_ERROR, "Cast to signed converted positive out-of-range integer to " "it's negative complement"); return value; @@ -1123,7 +1123,7 @@ longlong Item_func_unsigned::val_int() return value; err: - push_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_NOTE, ER_UNKNOWN_ERROR, + push_warning(current_thd, Sql_condition::WARN_LEVEL_NOTE, ER_UNKNOWN_ERROR, "Cast to unsigned converted negative integer to it's " "positive complement"); return value; @@ -1191,7 +1191,7 @@ my_decimal *Item_decimal_typecast::val_decimal(my_decimal *dec) return dec; err: - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, ER(ER_WARN_DATA_OUT_OF_RANGE), name, 1L); @@ -1233,7 +1233,7 @@ double Item_double_typecast::val_real() if ((error= truncate_double(&tmp, max_length, decimals, 0, DBL_MAX))) { push_warning_printf(current_thd, - MYSQL_ERROR::WARN_LEVEL_WARN, + Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, ER(ER_WARN_DATA_OUT_OF_RANGE), name, 1); @@ -3891,7 +3891,7 @@ longlong Item_master_pos_wait::val_int() connection_name= thd->variables.default_master_connection; if (!(mi= master_info_index->get_master_info(&connection_name, - MYSQL_ERROR::WARN_LEVEL_WARN))) + Sql_condition::WARN_LEVEL_WARN))) goto err; if ((event_count = mi->rli.wait_for_pos(thd, log_name, pos, timeout)) == -2) { @@ -4229,7 +4229,7 @@ longlong Item_func_benchmark::val_int() { char buff[22]; llstr(((longlong) loop_count), buff); - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, ER_WRONG_VALUE_FOR_TYPE, ER(ER_WRONG_VALUE_FOR_TYPE), "count", buff, "benchmark"); } diff --git a/sql/item_geofunc.cc b/sql/item_geofunc.cc index e3e80bdf59f..a988426d72c 100644 --- a/sql/item_geofunc.cc +++ b/sql/item_geofunc.cc @@ -500,7 +500,7 @@ String *Item_func_spatial_collection::val_str(String *str) } if (str->length() > current_thd->variables.max_allowed_packet) { - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, ER_WARN_ALLOWED_PACKET_OVERFLOWED, ER(ER_WARN_ALLOWED_PACKET_OVERFLOWED), func_name(), current_thd->variables.max_allowed_packet); diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index e853faae88f..9b88e45be0e 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -282,9 +282,9 @@ String *Item_func_sha2::val_str_ascii(String *str) default: if (!args[1]->const_item()) push_warning_printf(current_thd, - MYSQL_ERROR::WARN_LEVEL_WARN, - ER_WRONG_PARAMETERS_TO_NATIVE_FCT, - ER(ER_WRONG_PARAMETERS_TO_NATIVE_FCT), "sha2"); + Sql_condition::WARN_LEVEL_WARN, + ER_WRONG_PARAMETERS_TO_NATIVE_FCT, + ER(ER_WRONG_PARAMETERS_TO_NATIVE_FCT), "sha2"); null_value= TRUE; return NULL; } @@ -306,7 +306,7 @@ String *Item_func_sha2::val_str_ascii(String *str) #else push_warning_printf(current_thd, - MYSQL_ERROR::WARN_LEVEL_WARN, + Sql_condition::WARN_LEVEL_WARN, ER_FEATURE_DISABLED, ER(ER_FEATURE_DISABLED), "sha2", "--with-ssl"); @@ -344,7 +344,7 @@ void Item_func_sha2::fix_length_and_dec() #endif default: push_warning_printf(current_thd, - MYSQL_ERROR::WARN_LEVEL_WARN, + Sql_condition::WARN_LEVEL_WARN, ER_WRONG_PARAMETERS_TO_NATIVE_FCT, ER(ER_WRONG_PARAMETERS_TO_NATIVE_FCT), "sha2"); } @@ -363,7 +363,7 @@ void Item_func_sha2::fix_length_and_dec() DERIVATION_COERCIBLE); #else push_warning_printf(current_thd, - MYSQL_ERROR::WARN_LEVEL_WARN, + Sql_condition::WARN_LEVEL_WARN, ER_FEATURE_DISABLED, ER(ER_FEATURE_DISABLED), "sha2", "--with-ssl"); @@ -489,7 +489,7 @@ String *Item_func_concat::val_str(String *str) if (res->length()+res2->length() > current_thd->variables.max_allowed_packet) { - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, ER_WARN_ALLOWED_PACKET_OVERFLOWED, ER(ER_WARN_ALLOWED_PACKET_OVERFLOWED), func_name(), current_thd->variables.max_allowed_packet); @@ -695,11 +695,11 @@ String *Item_func_des_encrypt::val_str(String *str) return &tmp_value; error: - push_warning_printf(current_thd,MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(current_thd,Sql_condition::WARN_LEVEL_WARN, code, ER(code), "des_encrypt"); #else - push_warning_printf(current_thd,MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(current_thd,Sql_condition::WARN_LEVEL_WARN, ER_FEATURE_DISABLED, ER(ER_FEATURE_DISABLED), "des_encrypt", "--with-ssl"); #endif /* defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY) */ @@ -773,12 +773,12 @@ String *Item_func_des_decrypt::val_str(String *str) return &tmp_value; error: - push_warning_printf(current_thd,MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(current_thd,Sql_condition::WARN_LEVEL_WARN, code, ER(code), "des_decrypt"); wrong_key: #else - push_warning_printf(current_thd,MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(current_thd,Sql_condition::WARN_LEVEL_WARN, ER_FEATURE_DISABLED, ER(ER_FEATURE_DISABLED), "des_decrypt", "--with-ssl"); #endif /* defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY) */ @@ -829,7 +829,7 @@ String *Item_func_concat_ws::val_str(String *str) if (res->length() + sep_str->length() + res2->length() > current_thd->variables.max_allowed_packet) { - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, ER_WARN_ALLOWED_PACKET_OVERFLOWED, ER(ER_WARN_ALLOWED_PACKET_OVERFLOWED), func_name(), current_thd->variables.max_allowed_packet); @@ -1088,7 +1088,7 @@ redo: if (res->length()-from_length + to_length > current_thd->variables.max_allowed_packet) { - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, ER_WARN_ALLOWED_PACKET_OVERFLOWED, ER(ER_WARN_ALLOWED_PACKET_OVERFLOWED), func_name(), @@ -1117,7 +1117,7 @@ skip: if (res->length()-from_length + to_length > current_thd->variables.max_allowed_packet) { - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, ER_WARN_ALLOWED_PACKET_OVERFLOWED, ER(ER_WARN_ALLOWED_PACKET_OVERFLOWED), func_name(), current_thd->variables.max_allowed_packet); @@ -1204,7 +1204,7 @@ String *Item_func_insert::val_str(String *str) if ((ulonglong) (res->length() - length + res2->length()) > (ulonglong) current_thd->variables.max_allowed_packet) { - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, ER_WARN_ALLOWED_PACKET_OVERFLOWED, ER(ER_WARN_ALLOWED_PACKET_OVERFLOWED), func_name(), current_thd->variables.max_allowed_packet); @@ -2230,7 +2230,7 @@ MY_LOCALE *Item_func_format::get_locale(Item *item) if (!locale_name || !(lc= my_locale_by_name(locale_name->c_ptr_safe()))) { - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, ER_UNKNOWN_LOCALE, ER(ER_UNKNOWN_LOCALE), locale_name ? locale_name->c_ptr_safe() : "NULL"); @@ -2675,7 +2675,7 @@ String *Item_func_repeat::val_str(String *str) // Safe length check if (length > current_thd->variables.max_allowed_packet / (uint) count) { - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, ER_WARN_ALLOWED_PACKET_OVERFLOWED, ER(ER_WARN_ALLOWED_PACKET_OVERFLOWED), func_name(), current_thd->variables.max_allowed_packet); @@ -2768,7 +2768,7 @@ String *Item_func_rpad::val_str(String *str) byte_count= count * collation.collation->mbmaxlen; if ((ulonglong) byte_count > current_thd->variables.max_allowed_packet) { - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, ER_WARN_ALLOWED_PACKET_OVERFLOWED, ER(ER_WARN_ALLOWED_PACKET_OVERFLOWED), func_name(), current_thd->variables.max_allowed_packet); @@ -2876,7 +2876,7 @@ String *Item_func_lpad::val_str(String *str) if ((ulonglong) byte_count > current_thd->variables.max_allowed_packet) { - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, ER_WARN_ALLOWED_PACKET_OVERFLOWED, ER(ER_WARN_ALLOWED_PACKET_OVERFLOWED), func_name(), current_thd->variables.max_allowed_packet); @@ -3254,7 +3254,7 @@ String *Item_load_file::val_str(String *str) } if (stat_info.st_size > (long) current_thd->variables.max_allowed_packet) { - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, ER_WARN_ALLOWED_PACKET_OVERFLOWED, ER(ER_WARN_ALLOWED_PACKET_OVERFLOWED), func_name(), current_thd->variables.max_allowed_packet); @@ -3345,7 +3345,7 @@ String* Item_func_export_set::val_str(String* str) if (unlikely(max_total_length > max_allowed_packet)) { - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, ER_WARN_ALLOWED_PACKET_OVERFLOWED, ER(ER_WARN_ALLOWED_PACKET_OVERFLOWED), func_name(), max_allowed_packet); @@ -3593,7 +3593,7 @@ longlong Item_func_uncompressed_length::val_int() */ if (res->length() <= 4) { - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, ER_ZLIB_Z_DATA_ERROR, ER(ER_ZLIB_Z_DATA_ERROR)); null_value= 1; @@ -3670,7 +3670,7 @@ String *Item_func_compress::val_str(String *str) res->length())) != Z_OK) { code= err==Z_MEM_ERROR ? ER_ZLIB_Z_MEM_ERROR : ER_ZLIB_Z_BUF_ERROR; - push_warning(current_thd,MYSQL_ERROR::WARN_LEVEL_WARN,code,ER(code)); + push_warning(current_thd,Sql_condition::WARN_LEVEL_WARN,code,ER(code)); null_value= 1; return 0; } @@ -3708,7 +3708,7 @@ String *Item_func_uncompress::val_str(String *str) /* If length is less than 4 bytes, data is corrupt */ if (res->length() <= 4) { - push_warning_printf(current_thd,MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(current_thd,Sql_condition::WARN_LEVEL_WARN, ER_ZLIB_Z_DATA_ERROR, ER(ER_ZLIB_Z_DATA_ERROR)); goto err; @@ -3718,7 +3718,7 @@ String *Item_func_uncompress::val_str(String *str) new_size= uint4korr(res->ptr()) & 0x3FFFFFFF; if (new_size > current_thd->variables.max_allowed_packet) { - push_warning_printf(current_thd,MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(current_thd,Sql_condition::WARN_LEVEL_WARN, ER_TOO_BIG_FOR_UNCOMPRESS, ER(ER_TOO_BIG_FOR_UNCOMPRESS), static_cast<int>(current_thd->variables. @@ -3737,7 +3737,7 @@ String *Item_func_uncompress::val_str(String *str) code= ((err == Z_BUF_ERROR) ? ER_ZLIB_Z_BUF_ERROR : ((err == Z_MEM_ERROR) ? ER_ZLIB_Z_MEM_ERROR : ER_ZLIB_Z_DATA_ERROR)); - push_warning(current_thd,MYSQL_ERROR::WARN_LEVEL_WARN,code,ER(code)); + push_warning(current_thd,Sql_condition::WARN_LEVEL_WARN,code,ER(code)); err: null_value= 1; @@ -4423,7 +4423,7 @@ longlong Item_dyncol_get::val_int() { char buff[30]; sprintf(buff, "%lg", val.x.double_value); - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, ER_DATA_OVERFLOW, ER(ER_DATA_OVERFLOW), buff, @@ -4443,7 +4443,7 @@ longlong Item_dyncol_get::val_int() char buff[80]; strmake(buff, val.x.string.value.str, MY_MIN(sizeof(buff)-1, val.x.string.value.length)); - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, ER_BAD_DATA, ER(ER_BAD_DATA), buff, @@ -4507,7 +4507,7 @@ double Item_dyncol_get::val_real() char buff[80]; strmake(buff, val.x.string.value.str, MY_MIN(sizeof(buff)-1, val.x.string.value.length)); - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, ER_BAD_DATA, ER(ER_BAD_DATA), buff, "DOUBLE"); @@ -4565,7 +4565,7 @@ my_decimal *Item_dyncol_get::val_decimal(my_decimal *decimal_value) val.x.string.value.length)); if (rc != E_DEC_OK) { - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, ER_BAD_DATA, ER(ER_BAD_DATA), buff, "DECIMAL"); diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index c6b8397100b..44be352bb05 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -897,7 +897,7 @@ public: Item_field *filed_for_view_update() { /* this function is transparent for view updating */ - return args[0]->filed_for_view_update(); + return args[0]->field_for_view_update(); } }; diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 5652c921d27..fef5d09f8fc 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -1063,7 +1063,7 @@ Item_singlerow_subselect::select_transformer(JOIN *join) { char warn_buff[MYSQL_ERRMSG_SIZE]; sprintf(warn_buff, ER(ER_SELECT_REDUCED), select_lex->select_number); - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + push_warning(thd, Sql_condition::WARN_LEVEL_NOTE, ER_SELECT_REDUCED, warn_buff); } substitution= select_lex->item_list.head(); @@ -1727,7 +1727,7 @@ Item_in_subselect::single_value_transformer(JOIN *join) { char warn_buff[MYSQL_ERRMSG_SIZE]; sprintf(warn_buff, ER(ER_SELECT_REDUCED), select_lex->select_number); - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + push_warning(thd, Sql_condition::WARN_LEVEL_NOTE, ER_SELECT_REDUCED, warn_buff); } DBUG_RETURN(false); diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 165f9f4a5f8..13f97d56759 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -3067,7 +3067,7 @@ int dump_leaf_key(void* key_arg, element_count count __attribute__((unused)), &well_formed_error); result->length(old_length + add_length); item->warning_for_row= TRUE; - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, ER_CUT_VALUE_GROUP_CONCAT, ER(ER_CUT_VALUE_GROUP_CONCAT), item->row_count); diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index c3e8204fd37..117904d8b82 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -425,7 +425,7 @@ static bool extract_date_time(DATE_TIME_FORMAT *format, { if (!my_isspace(&my_charset_latin1,*val)) { - make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + make_truncated_value_warning(current_thd, Sql_condition::WARN_LEVEL_WARN, val_begin, length, cached_timestamp_type, NullS); break; @@ -438,7 +438,7 @@ err: { char buff[128]; strmake(buff, val_begin, MY_MIN(length, sizeof(buff)-1)); - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, ER_WRONG_VALUE_FOR_TYPE, ER(ER_WRONG_VALUE_FOR_TYPE), date_time_type, buff, "str_to_date"); } @@ -1721,7 +1721,7 @@ overflow: ltime->hour= TIME_MAX_HOUR+1; check_time_range(ltime, decimals, &unused); - make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + make_truncated_value_warning(current_thd, Sql_condition::WARN_LEVEL_WARN, err->ptr(), err->length(), MYSQL_TIMESTAMP_TIME, NullS); return 0; @@ -2270,7 +2270,7 @@ String *Item_char_typecast::val_str(String *str) if (cast_length != ~0U && cast_length > current_thd->variables.max_allowed_packet) { - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, ER_WARN_ALLOWED_PACKET_OVERFLOWED, ER(ER_WARN_ALLOWED_PACKET_OVERFLOWED), cast_cs == &my_charset_bin ? @@ -2328,7 +2328,7 @@ String *Item_char_typecast::val_str(String *str) res= &str_value; } ErrConvString err(res); - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, ER_TRUNCATED_WRONG_VALUE, ER(ER_TRUNCATED_WRONG_VALUE), char_type, err.ptr()); @@ -2350,7 +2350,7 @@ String *Item_char_typecast::val_str(String *str) if (res->length() > current_thd->variables.max_allowed_packet) { - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, ER_WARN_ALLOWED_PACKET_OVERFLOWED, ER(ER_WARN_ALLOWED_PACKET_OVERFLOWED), cast_cs == &my_charset_bin ? @@ -2442,7 +2442,7 @@ bool Item_date_typecast::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) fuzzy_date, &unused)) { ErrConvTime str(ltime); - make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + make_truncated_value_warning(current_thd, Sql_condition::WARN_LEVEL_WARN, &str, MYSQL_TIMESTAMP_DATE, 0); return (null_value= 1); } @@ -2468,7 +2468,7 @@ bool Item_datetime_typecast::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) if (ltime->neg) { ErrConvTime str(ltime); - make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + make_truncated_value_warning(current_thd, Sql_condition::WARN_LEVEL_WARN, &str, MYSQL_TIMESTAMP_DATETIME, 0); return (null_value= 1); } @@ -2622,7 +2622,7 @@ bool Item_func_add_time::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) check_time_range(ltime, decimals, &was_cut); if (was_cut) - make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + make_truncated_value_warning(current_thd, Sql_condition::WARN_LEVEL_WARN, &str, MYSQL_TIMESTAMP_TIME, NullS); return (null_value= 0); @@ -2710,7 +2710,7 @@ bool Item_func_timediff::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) check_time_range(ltime, decimals, &was_cut); if (was_cut) - make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + make_truncated_value_warning(current_thd, Sql_condition::WARN_LEVEL_WARN, &str, MYSQL_TIMESTAMP_TIME, NullS); return (null_value= 0); } @@ -2762,7 +2762,7 @@ bool Item_func_maketime::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) char buf[28]; char *ptr= longlong10_to_str(hour, buf, args[0]->unsigned_flag ? 10 : -10); int len = (int)(ptr - buf) + sprintf(ptr, ":%02u:%02u", (uint)minute, (uint)second); - make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + make_truncated_value_warning(current_thd, Sql_condition::WARN_LEVEL_WARN, buf, len, MYSQL_TIMESTAMP_TIME, NullS); } diff --git a/sql/item_xmlfunc.cc b/sql/item_xmlfunc.cc index 5a824e48b7b..b05d1da82a5 100644 --- a/sql/item_xmlfunc.cc +++ b/sql/item_xmlfunc.cc @@ -1037,7 +1037,7 @@ static char simpletok[128]= /* ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ - ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~ € + ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~ \200 */ 0,1,0,0,1,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,1,0,1,1,1,0, 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0, @@ -2811,7 +2811,7 @@ String *Item_xml_str_func::parse_xml(String *raw_xml, String *parsed_xml_buf) my_xml_error_lineno(&p) + 1, (ulong) my_xml_error_pos(&p) + 1, my_xml_error_string(&p)); - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, ER_WRONG_VALUE, ER(ER_WRONG_VALUE), "XML", buf); } diff --git a/sql/lex.h b/sql/lex.h index 101880597d5..0f20df71576 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -150,6 +150,7 @@ static SYMBOL symbols[] = { { "CREATE", SYM(CREATE)}, { "CROSS", SYM(CROSS)}, { "CUBE", SYM(CUBE_SYM)}, + { "CURRENT", SYM(CURRENT_SYM)}, { "CURRENT_DATE", SYM(CURDATE)}, { "CURRENT_TIME", SYM(CURTIME)}, { "CURRENT_TIMESTAMP", SYM(NOW_SYM)}, @@ -180,6 +181,7 @@ static SYMBOL symbols[] = { { "DESCRIBE", SYM(DESCRIBE)}, { "DES_KEY_FILE", SYM(DES_KEY_FILE)}, { "DETERMINISTIC", SYM(DETERMINISTIC_SYM)}, + { "DIAGNOSTICS", SYM(DIAGNOSTICS_SYM)}, { "DIRECTORY", SYM(DIRECTORY_SYM)}, { "DISABLE", SYM(DISABLE_SYM)}, { "DISCARD", SYM(DISCARD)}, @@ -212,6 +214,7 @@ static SYMBOL symbols[] = { { "EVENTS", SYM(EVENTS_SYM)}, { "EVERY", SYM(EVERY_SYM)}, { "EXAMINED", SYM(EXAMINED_SYM)}, + { "EXCHANGE", SYM(EXCHANGE_SYM)}, { "EXECUTE", SYM(EXECUTE_SYM)}, { "EXISTS", SYM(EXISTS)}, { "EXIT", SYM(EXIT_SYM)}, @@ -244,6 +247,7 @@ static SYMBOL symbols[] = { { "GEOMETRY", SYM(GEOMETRY_SYM)}, { "GEOMETRYCOLLECTION",SYM(GEOMETRYCOLLECTION)}, { "GET_FORMAT", SYM(GET_FORMAT)}, + { "GET", SYM(GET_SYM)}, { "GLOBAL", SYM(GLOBAL_SYM)}, { "GRANT", SYM(GRANT)}, { "GRANTS", SYM(GRANTS)}, @@ -394,6 +398,7 @@ static SYMBOL symbols[] = { { "NOT", SYM(NOT_SYM)}, { "NO_WRITE_TO_BINLOG", SYM(NO_WRITE_TO_BINLOG)}, { "NULL", SYM(NULL_SYM)}, + { "NUMBER", SYM(NUMBER_SYM)}, { "NUMERIC", SYM(NUMERIC_SYM)}, { "NVARCHAR", SYM(NVARCHAR_SYM)}, { "OFFSET", SYM(OFFSET_SYM)}, @@ -480,6 +485,7 @@ static SYMBOL symbols[] = { { "RESTORE", SYM(RESTORE_SYM)}, { "RESTRICT", SYM(RESTRICT)}, { "RESUME", SYM(RESUME_SYM)}, + { "RETURNED_SQLSTATE",SYM(RETURNED_SQLSTATE_SYM)}, { "RETURN", SYM(RETURN_SYM)}, { "RETURNS", SYM(RETURNS_SYM)}, { "REVOKE", SYM(REVOKE)}, @@ -489,6 +495,7 @@ static SYMBOL symbols[] = { { "ROLLUP", SYM(ROLLUP_SYM)}, { "ROUTINE", SYM(ROUTINE_SYM)}, { "ROW", SYM(ROW_SYM)}, + { "ROW_COUNT", SYM(ROW_COUNT_SYM)}, { "ROWS", SYM(ROWS_SYM)}, { "ROW_FORMAT", SYM(ROW_FORMAT_SYM)}, { "RTREE", SYM(RTREE_SYM)}, diff --git a/sql/log.cc b/sql/log.cc index 2572dc61894..3c1e830402a 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -170,9 +170,9 @@ public: virtual bool handle_condition(THD *thd, uint sql_errno, const char* sql_state, - MYSQL_ERROR::enum_warning_level level, + Sql_condition::enum_warning_level level, const char* msg, - MYSQL_ERROR ** cond_hdl); + Sql_condition ** cond_hdl); const char *message() const { return m_message; } }; @@ -180,9 +180,9 @@ bool Silence_log_table_errors::handle_condition(THD *, uint, const char*, - MYSQL_ERROR::enum_warning_level, + Sql_condition::enum_warning_level, const char* msg, - MYSQL_ERROR ** cond_hdl) + Sql_condition ** cond_hdl) { *cond_hdl= NULL; strmake(m_message, msg, sizeof(m_message)-1); @@ -2057,7 +2057,7 @@ bool MYSQL_BIN_LOG::check_write_error(THD *thd) if (!thd->is_error()) DBUG_RETURN(checked); - switch (thd->stmt_da->sql_errno()) + switch (thd->get_stmt_da()->sql_errno()) { case ER_TRANS_CACHE_FULL: case ER_STMT_CACHE_FULL: @@ -3778,7 +3778,7 @@ bool MYSQL_BIN_LOG::reset_logs(THD* thd, bool create_new_log) { if (my_errno == ENOENT) { - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, ER_LOG_PURGE_NO_FILE, ER(ER_LOG_PURGE_NO_FILE), linfo.log_file_name); sql_print_information("Failed to delete file '%s'", @@ -3788,7 +3788,7 @@ bool MYSQL_BIN_LOG::reset_logs(THD* thd, bool create_new_log) } else { - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, ER_BINLOG_PURGE_FATAL_ERR, "a problem with deleting %s; " "consider examining correspondence " @@ -3809,7 +3809,7 @@ bool MYSQL_BIN_LOG::reset_logs(THD* thd, bool create_new_log) { if (my_errno == ENOENT) { - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, ER_LOG_PURGE_NO_FILE, ER(ER_LOG_PURGE_NO_FILE), index_file_name); sql_print_information("Failed to delete file '%s'", @@ -3819,7 +3819,7 @@ bool MYSQL_BIN_LOG::reset_logs(THD* thd, bool create_new_log) } else { - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, ER_BINLOG_PURGE_FATAL_ERR, "a problem with deleting %s; " "consider examining correspondence " @@ -4261,7 +4261,7 @@ int MYSQL_BIN_LOG::purge_index_entry(THD *thd, ulonglong *decrease_log_space, */ if (thd) { - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_LOG_PURGE_NO_FILE, ER(ER_LOG_PURGE_NO_FILE), log_info.log_file_name); } @@ -4276,7 +4276,7 @@ int MYSQL_BIN_LOG::purge_index_entry(THD *thd, ulonglong *decrease_log_space, */ if (thd) { - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_BINLOG_PURGE_FATAL_ERR, "a problem with getting info on being purged %s; " "consider examining correspondence " @@ -4304,7 +4304,7 @@ int MYSQL_BIN_LOG::purge_index_entry(THD *thd, ulonglong *decrease_log_space, { if (thd) { - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_BINLOG_PURGE_FATAL_ERR, "a problem with deleting %s and " "reading the binlog index file", @@ -4340,7 +4340,7 @@ int MYSQL_BIN_LOG::purge_index_entry(THD *thd, ulonglong *decrease_log_space, { if (thd) { - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_LOG_PURGE_NO_FILE, ER(ER_LOG_PURGE_NO_FILE), log_info.log_file_name); } @@ -4352,7 +4352,7 @@ int MYSQL_BIN_LOG::purge_index_entry(THD *thd, ulonglong *decrease_log_space, { if (thd) { - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_BINLOG_PURGE_FATAL_ERR, "a problem with deleting %s; " "consider examining correspondence " @@ -4442,7 +4442,7 @@ int MYSQL_BIN_LOG::purge_logs_before_date(time_t purge_time) */ if (thd) { - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_BINLOG_PURGE_FATAL_ERR, "a problem with getting info on being purged %s; " "consider examining correspondence " @@ -6066,9 +6066,9 @@ int query_error_code(THD *thd, bool not_killed) if (not_killed || (killed_mask_hard(thd->killed) == KILL_BAD_DATA)) { - error= thd->is_error() ? thd->stmt_da->sql_errno() : 0; + error= thd->is_error() ? thd->get_stmt_da()->sql_errno() : 0; - /* thd->stmt_da->sql_errno() might be ER_SERVER_SHUTDOWN or + /* thd->get_get_stmt_da()->sql_errno() might be ER_SERVER_SHUTDOWN or ER_QUERY_INTERRUPTED, So here we need to make sure that error is not set to these errors when specified not_killed by the caller. diff --git a/sql/log_event.cc b/sql/log_event.cc index 9481261d3ef..4983263f589 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -215,8 +215,9 @@ static void inline slave_rows_error_report(enum loglevel level, int ha_error, char buff[MAX_SLAVE_ERRMSG], *slider; const char *buff_end= buff + sizeof(buff); uint len; - List_iterator_fast<MYSQL_ERROR> it(thd->warning_info->warn_list()); - MYSQL_ERROR *err; + Diagnostics_area::Sql_condition_iterator it= + thd->get_stmt_da()->sql_conditions(); + const Sql_condition *err; buff[0]= 0; for (err= it++, slider= buff; err && slider < buff_end - 1; @@ -228,7 +229,7 @@ static void inline slave_rows_error_report(enum loglevel level, int ha_error, } if (ha_error != 0) - rli->report(level, thd->is_error() ? thd->stmt_da->sql_errno() : 0, + rli->report(level, thd->is_error() ? thd->get_stmt_da()->sql_errno() : 0, "Could not execute %s event on table %s.%s;" "%s handler error %s; " "the event's master log %s, end_log_pos %lu", @@ -236,7 +237,7 @@ static void inline slave_rows_error_report(enum loglevel level, int ha_error, buff, handler_error == NULL ? "<unknown>" : handler_error, log_name, pos); else - rli->report(level, thd->is_error() ? thd->stmt_da->sql_errno() : 0, + rli->report(level, thd->is_error() ? thd->get_stmt_da()->sql_errno() : 0, "Could not execute %s event on table %s.%s;" "%s the event's master log %s, end_log_pos %lu", type, table->s->db.str, table->s->table_name.str, @@ -440,13 +441,13 @@ inline int ignored_error_code(int err_code) */ int convert_handler_error(int error, THD* thd, TABLE *table) { - uint actual_error= (thd->is_error() ? thd->stmt_da->sql_errno() : + uint actual_error= (thd->is_error() ? thd->get_stmt_da()->sql_errno() : 0); if (actual_error == 0) { table->file->print_error(error, MYF(0)); - actual_error= (thd->is_error() ? thd->stmt_da->sql_errno() : + actual_error= (thd->is_error() ? thd->get_stmt_da()->sql_errno() : ER_UNKNOWN_ERROR); if (actual_error == ER_UNKNOWN_ERROR) if (global_system_variables.log_warnings) @@ -2108,7 +2109,8 @@ log_event_print_value(IO_CACHE *file, const uchar *ptr, { uint i32= uint3korr(ptr); my_b_printf(file , "'%04d:%02d:%02d'", - (i32 / (16L * 32L)), (i32 / 32L % 16L), (i32 % 32L)); + (int)(i32 / (16L * 32L)), (int)(i32 / 32L % 16L), + (int)(i32 % 32L)); my_snprintf(typestr, typestr_length, "DATE"); return 3; } @@ -2236,11 +2238,11 @@ Rows_log_event::print_verbose_one_row(IO_CACHE *file, table_def *td, if (is_null) { - my_b_printf(file, "### @%d=NULL", i + 1); + my_b_printf(file, "### @%lu=NULL", (ulong)i + 1); } else { - my_b_printf(file, "### @%d=", i + 1); + my_b_printf(file, "### @%lu=", (ulong)i + 1); size_t size= log_event_print_value(file, value, td->type(i), td->field_metadata(i), typestr, sizeof(typestr)); @@ -2311,14 +2313,23 @@ void Rows_log_event::print_verbose(IO_CACHE *file, if (!(map= print_event_info->m_table_map.get_table(m_table_id)) || !(td= map->create_table_def())) { - my_b_printf(file, "### Row event for unknown table #%d", m_table_id); + my_b_printf(file, "### Row event for unknown table #%lu", + (ulong) m_table_id); return; } + /* If the write rows event contained no values for the AI */ + if (((type_code == WRITE_ROWS_EVENT) && (m_rows_buf==m_rows_end))) + { + my_b_printf(file, "### INSERT INTO `%s`.`%s` VALUES ()\n", + map->get_db_name(), map->get_table_name()); + goto end; + } + for (const uchar *value= m_rows_buf; value < m_rows_end; ) { size_t length; - my_b_printf(file, "### %s %`s.%`s\n", + my_b_printf(file, "### %s '%s'.'%s'\n", sql_command, map->get_db_name(), map->get_table_name()); /* Print the first image */ @@ -3467,7 +3478,7 @@ void Query_log_event::print_query_header(IO_CACHE* file, if (different_db) memcpy(print_event_info->db, db, db_len + 1); if (db[0] && different_db) - my_b_printf(file, "use %`s%s\n", db, print_event_info->delimiter); + my_b_printf(file, "use %s%s\n", db, print_event_info->delimiter); } end=int10_to_str((long) when, strmov(buff,"SET TIMESTAMP="),10); @@ -3943,7 +3954,8 @@ START SLAVE; . Query: '%s'", expected_error, thd->query()); } /* If the query was not ignored, it is printed to the general log */ - if (!thd->is_error() || thd->stmt_da->sql_errno() != ER_SLAVE_IGNORED_TABLE) + if (!thd->is_error() || + thd->get_stmt_da()->sql_errno() != ER_SLAVE_IGNORED_TABLE) general_log_write(thd, COM_QUERY, thd->query(), thd->query_length()); else { @@ -3968,14 +3980,14 @@ compare_errors: not exist errors", we silently clear the error if TEMPORARY was used. */ if (thd->lex->sql_command == SQLCOM_DROP_TABLE && thd->lex->drop_temporary && - thd->is_error() && thd->stmt_da->sql_errno() == ER_BAD_TABLE_ERROR && + thd->is_error() && thd->get_stmt_da()->sql_errno() == ER_BAD_TABLE_ERROR && !expected_error) - thd->stmt_da->reset_diagnostics_area(); + thd->get_stmt_da()->reset_diagnostics_area(); /* If we expected a non-zero error code, and we don't get the same error code, and it should be ignored or is related to a concurrency issue. */ - actual_error= thd->is_error() ? thd->stmt_da->sql_errno() : 0; + actual_error= thd->is_error() ? thd->get_stmt_da()->sql_errno() : 0; DBUG_PRINT("info",("expected_error: %d sql_errno: %d", expected_error, actual_error)); @@ -3993,7 +4005,7 @@ Error on slave: actual message='%s', error code=%d. \ Default database: '%s'. Query: '%s'", ER_SAFE(expected_error), expected_error, - actual_error ? thd->stmt_da->message() : "no error", + actual_error ? thd->get_stmt_da()->message() : "no error", actual_error, print_slave_db_safe(db), query_arg); thd->is_slave_error= 1; @@ -4017,7 +4029,7 @@ Default database: '%s'. Query: '%s'", { rli->report(ERROR_LEVEL, actual_error, "Error '%s' on query. Default database: '%s'. Query: '%s'", - (actual_error ? thd->stmt_da->message() : + (actual_error ? thd->get_stmt_da()->message() : "unexpected success or fatal error"), print_slave_db_safe(thd->db), query_arg); thd->is_slave_error= 1; @@ -5301,7 +5313,7 @@ void Load_log_event::print(FILE* file_arg, PRINT_EVENT_INFO* print_event_info, } if (db && db[0] && different_db) - my_b_printf(&cache, "%suse %`s%s\n", + my_b_printf(&cache, "%suse %s%s\n", commented ? "# " : "", db, print_event_info->delimiter); @@ -5353,7 +5365,7 @@ void Load_log_event::print(FILE* file_arg, PRINT_EVENT_INFO* print_event_info, { if (i) my_b_printf(&cache, ","); - my_b_printf(&cache, "%`s", field); + my_b_printf(&cache, "%s", field); field += field_lens[i] + 1; } @@ -5484,7 +5496,7 @@ int Load_log_event::do_apply_event(NET* net, Relay_log_info const *rli, { thd->set_time(when, when_sec_part); thd->set_query_id(next_query_id()); - thd->warning_info->opt_clear_warning_info(thd->query_id); + thd->get_stmt_da()->opt_clear_warning_info(thd->query_id); TABLE_LIST tables; tables.init_one_table(thd->strmake(thd->db, thd->db_length), @@ -5630,9 +5642,9 @@ error: thd->catalog= 0; thd->set_db(NULL, 0); /* will free the current database */ thd->reset_query(); - thd->stmt_da->can_overwrite_status= TRUE; + thd->get_stmt_da()->set_overwrite_status(true); thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd); - thd->stmt_da->can_overwrite_status= FALSE; + thd->get_stmt_da()->set_overwrite_status(false); close_thread_tables(thd); /* - If inside a multi-statement transaction, @@ -5659,8 +5671,8 @@ error: int sql_errno; if (thd->is_error()) { - err= thd->stmt_da->message(); - sql_errno= thd->stmt_da->sql_errno(); + err= thd->get_stmt_da()->message(); + sql_errno= thd->get_stmt_da()->sql_errno(); } else { @@ -8496,7 +8508,7 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli) if (open_and_lock_tables(thd, rli->tables_to_lock, FALSE, 0)) { - uint actual_error= thd->stmt_da->sql_errno(); + uint actual_error= thd->get_stmt_da()->sql_errno(); if (thd->is_slave_error || thd->is_fatal_error) { /* @@ -8507,7 +8519,7 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli) */ rli->report(ERROR_LEVEL, actual_error, "Error executing row event: '%s'", - (actual_error ? thd->stmt_da->message() : + (actual_error ? thd->get_stmt_da()->message() : "unexpected success or fatal error")); thd->is_slave_error= 1; } @@ -9835,8 +9847,8 @@ void Table_map_log_event::print(FILE *, PRINT_EVENT_INFO *print_event_info) { print_header(&print_event_info->head_cache, print_event_info, TRUE); my_b_printf(&print_event_info->head_cache, - "\tTable_map: %`s.%`s mapped to number %lu\n", - m_dbnam, m_tblnam, m_table_id); + "\tTable_map: '%s'.'%s' mapped to number %lu\n", + m_dbnam, m_tblnam, (ulong) m_table_id); print_base64(&print_event_info->body_cache, print_event_info, TRUE); } } diff --git a/sql/log_event_old.cc b/sql/log_event_old.cc index 3511c44c3b9..a4ab111f613 100644 --- a/sql/log_event_old.cc +++ b/sql/log_event_old.cc @@ -99,7 +99,7 @@ Old_rows_log_event::do_apply_event(Old_rows_log_event *ev, const Relay_log_info if (open_and_lock_tables(ev_thd, rli->tables_to_lock, FALSE, 0)) { - uint actual_error= ev_thd->stmt_da->sql_errno(); + uint actual_error= ev_thd->get_stmt_da()->sql_errno(); if (ev_thd->is_slave_error || ev_thd->is_fatal_error) { /* @@ -108,7 +108,7 @@ Old_rows_log_event::do_apply_event(Old_rows_log_event *ev, const Relay_log_info */ rli->report(ERROR_LEVEL, actual_error, "Error '%s' on opening tables", - (actual_error ? ev_thd->stmt_da->message() : + (actual_error ? ev_thd->get_stmt_da()->message() : "unexpected success or fatal error")); ev_thd->is_slave_error= 1; } @@ -243,10 +243,10 @@ Old_rows_log_event::do_apply_event(Old_rows_log_event *ev, const Relay_log_info break; default: - rli->report(ERROR_LEVEL, ev_thd->stmt_da->sql_errno(), + rli->report(ERROR_LEVEL, ev_thd->get_stmt_da()->sql_errno(), "Error in %s event: row application failed. %s", ev->get_type_str(), - ev_thd->is_error() ? ev_thd->stmt_da->message() : ""); + ev_thd->is_error() ? ev_thd->get_stmt_da()->message() : ""); thd->is_slave_error= 1; break; } @@ -260,12 +260,12 @@ Old_rows_log_event::do_apply_event(Old_rows_log_event *ev, const Relay_log_info if (error) { /* error has occured during the transaction */ - rli->report(ERROR_LEVEL, ev_thd->stmt_da->sql_errno(), + rli->report(ERROR_LEVEL, ev_thd->get_stmt_da()->sql_errno(), "Error in %s event: error during transaction execution " "on table %s.%s. %s", ev->get_type_str(), table->s->db.str, table->s->table_name.str, - ev_thd->is_error() ? ev_thd->stmt_da->message() : ""); + ev_thd->is_error() ? ev_thd->get_stmt_da()->message() : ""); /* If one day we honour --skip-slave-errors in row-based replication, and diff --git a/sql/mdl.cc b/sql/mdl.cc index a18df0fede1..65595c351a2 100644 --- a/sql/mdl.cc +++ b/sql/mdl.cc @@ -317,7 +317,7 @@ Deadlock_detection_visitor::opt_change_victim_to(MDL_context *new_victim) class MDL_lock { public: - typedef uchar bitmap_t; + typedef unsigned short bitmap_t; class Ticket_list { @@ -1108,7 +1108,7 @@ void MDL_ticket::destroy(MDL_ticket *ticket) uint MDL_ticket::get_deadlock_weight() const { return (m_lock->key.mdl_namespace() == MDL_key::GLOBAL || - m_type >= MDL_SHARED_NO_WRITE ? + m_type >= MDL_SHARED_UPGRADABLE ? DEADLOCK_WEIGHT_DDL : DEADLOCK_WEIGHT_DML); } @@ -1440,16 +1440,6 @@ void MDL_lock::reschedule_waiters() lock. Arrays of bitmaps which elements specify which granted/waiting locks are incompatible with type of lock being requested. - Here is how types of individual locks are translated to type of scoped lock: - - ----------------+-------------+ - Type of request | Correspond. | - for indiv. lock | scoped lock | - ----------------+-------------+ - S, SH, SR, SW | IS | - SNW, SNRW, X | IX | - SNW, SNRW -> X | IX (*) | - The first array specifies if particular type of request can be satisfied if there is granted scoped lock of certain type. @@ -1478,24 +1468,33 @@ void MDL_lock::reschedule_waiters() Here: "+" -- means that request can be satisfied "-" -- means that request can't be satisfied and should wait - (*) Since for upgradable locks we always take intention exclusive scoped - lock at the same time when obtaining the shared lock, there is no - need to obtain such lock during the upgrade itself. - (**) Since intention shared scoped locks are compatible with all other - type of locks we don't even have any accounting for them. + (*) Since intention shared scoped locks are compatible with all other + type of locks we don't even have any accounting for them. + + Note that relation between scoped locks and objects locks requested + by statement is not straightforward and is therefore fully defined + by SQL-layer. + For example, in order to support global read lock implementation + SQL-layer acquires IX lock in GLOBAL namespace for each statement + that can modify metadata or data (i.e. for each statement that + needs SW, SU, SNW, SNRW or X object locks). OTOH, to ensure that + DROP DATABASE works correctly with concurrent DDL, IX metadata locks + in SCHEMA namespace are acquired for DDL statements which can update + metadata in the schema (i.e. which acquire SU, SNW, SNRW and X locks + on schema objects) and aren't acquired for DML. */ const MDL_lock::bitmap_t MDL_scoped_lock::m_granted_incompatible[MDL_TYPE_END] = { MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED), - MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_INTENTION_EXCLUSIVE), 0, 0, 0, 0, 0, + MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_INTENTION_EXCLUSIVE), 0, 0, 0, 0, 0, 0, MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED) | MDL_BIT(MDL_INTENTION_EXCLUSIVE) }; const MDL_lock::bitmap_t MDL_scoped_lock::m_waiting_incompatible[MDL_TYPE_END] = { MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED), - MDL_BIT(MDL_EXCLUSIVE), 0, 0, 0, 0, 0, 0 + MDL_BIT(MDL_EXCLUSIVE), 0, 0, 0, 0, 0, 0, 0 }; @@ -1507,35 +1506,39 @@ const MDL_lock::bitmap_t MDL_scoped_lock::m_waiting_incompatible[MDL_TYPE_END] = The first array specifies if particular type of request can be satisfied if there is granted lock of certain type. - Request | Granted requests for lock | - type | S SH SR SW SNW SNRW X | - ----------+------------------------------+ - S | + + + + + + - | - SH | + + + + + + - | - SR | + + + + + - - | - SW | + + + + - - - | - SNW | + + + - - - - | - SNRW | + + - - - - - | - X | - - - - - - - | - SNW -> X | - - - 0 0 0 0 | - SNRW -> X | - - 0 0 0 0 0 | + Request | Granted requests for lock | + type | S SH SR SW SU SNW SNRW X | + ----------+----------------------------------+ + S | + + + + + + + - | + SH | + + + + + + + - | + SR | + + + + + + - - | + SW | + + + + + - - - | + SU | + + + + - - - - | + SNW | + + + - - - - - | + SNRW | + + - - - - - - | + X | - - - - - - - - | + SU -> X | - - - - 0 0 0 0 | + SNW -> X | - - - 0 0 0 0 0 | + SNRW -> X | - - 0 0 0 0 0 0 | The second array specifies if particular type of request can be satisfied if there is waiting request for the same lock of certain type. In other words it specifies what is the priority of different lock types. - Request | Pending requests for lock | - type | S SH SR SW SNW SNRW X | - ----------+-----------------------------+ - S | + + + + + + - | - SH | + + + + + + + | - SR | + + + + + - - | - SW | + + + + - - - | - SNW | + + + + + + - | - SNRW | + + + + + + - | - X | + + + + + + + | - SNW -> X | + + + + + + + | - SNRW -> X | + + + + + + + | + Request | Pending requests for lock | + type | S SH SR SW SU SNW SNRW X | + ----------+---------------------------------+ + S | + + + + + + + - | + SH | + + + + + + + + | + SR | + + + + + + - - | + SW | + + + + + - - - | + SU | + + + + + + + - | + SNW | + + + + + + + - | + SNRW | + + + + + + + - | + X | + + + + + + + + | + SU -> X | + + + + + + + + | + SNW -> X | + + + + + + + + | + SNRW -> X | + + + + + + + + | Here: "+" -- means that request can be satisfied "-" -- means that request can't be satisfied and should wait @@ -1544,6 +1547,9 @@ const MDL_lock::bitmap_t MDL_scoped_lock::m_waiting_incompatible[MDL_TYPE_END] = @note In cases then current context already has "stronger" type of lock on the object it will be automatically granted thanks to usage of the MDL_context::find_ticket() method. + + @note IX locks are excluded since they are not used for per-object + metadata locks. */ const MDL_lock::bitmap_t @@ -1556,14 +1562,17 @@ MDL_object_lock::m_granted_incompatible[MDL_TYPE_END] = MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED_NO_READ_WRITE) | MDL_BIT(MDL_SHARED_NO_WRITE), MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED_NO_READ_WRITE) | - MDL_BIT(MDL_SHARED_NO_WRITE) | MDL_BIT(MDL_SHARED_WRITE), + MDL_BIT(MDL_SHARED_NO_WRITE) | MDL_BIT(MDL_SHARED_UPGRADABLE), MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED_NO_READ_WRITE) | - MDL_BIT(MDL_SHARED_NO_WRITE) | MDL_BIT(MDL_SHARED_WRITE) | - MDL_BIT(MDL_SHARED_READ), + MDL_BIT(MDL_SHARED_NO_WRITE) | MDL_BIT(MDL_SHARED_UPGRADABLE) | + MDL_BIT(MDL_SHARED_WRITE), MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED_NO_READ_WRITE) | - MDL_BIT(MDL_SHARED_NO_WRITE) | MDL_BIT(MDL_SHARED_WRITE) | - MDL_BIT(MDL_SHARED_READ) | MDL_BIT(MDL_SHARED_HIGH_PRIO) | - MDL_BIT(MDL_SHARED) + MDL_BIT(MDL_SHARED_NO_WRITE) | MDL_BIT(MDL_SHARED_UPGRADABLE) | + MDL_BIT(MDL_SHARED_WRITE) | MDL_BIT(MDL_SHARED_READ), + MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED_NO_READ_WRITE) | + MDL_BIT(MDL_SHARED_NO_WRITE) | MDL_BIT(MDL_SHARED_UPGRADABLE) | + MDL_BIT(MDL_SHARED_WRITE) | MDL_BIT(MDL_SHARED_READ) | + MDL_BIT(MDL_SHARED_HIGH_PRIO) | MDL_BIT(MDL_SHARED) }; @@ -1578,6 +1587,7 @@ MDL_object_lock::m_waiting_incompatible[MDL_TYPE_END] = MDL_BIT(MDL_SHARED_NO_WRITE), MDL_BIT(MDL_EXCLUSIVE), MDL_BIT(MDL_EXCLUSIVE), + MDL_BIT(MDL_EXCLUSIVE), 0 }; @@ -1990,7 +2000,7 @@ void MDL_object_lock::notify_conflicting_locks(MDL_context *ctx) { /* Only try to abort locks on which we back off. */ if (conflicting_ticket->get_ctx() != ctx && - conflicting_ticket->get_type() < MDL_SHARED_NO_WRITE) + conflicting_ticket->get_type() < MDL_SHARED_UPGRADABLE) { MDL_context *conflicting_ctx= conflicting_ticket->get_ctx(); @@ -2258,11 +2268,12 @@ err: /** - Upgrade a shared metadata lock to exclusive. + Upgrade a shared metadata lock. - Used in ALTER TABLE, when a copy of the table with the - new definition has been constructed. + Used in ALTER TABLE. + @param mdl_ticket Lock to upgrade. + @param new_type Lock type to upgrade to. @param lock_wait_timeout Seconds to wait before timeout. @note In case of failure to upgrade lock (e.g. because upgrader @@ -2270,7 +2281,7 @@ err: shared mode). @note There can be only one upgrader for a lock or we will have deadlock. - This invariant is ensured by the fact that upgradeable locks SNW + This invariant is ensured by the fact that upgradeable locks SU, SNW and SNRW are not compatible with each other and themselves. @retval FALSE Success @@ -2278,28 +2289,30 @@ err: */ bool -MDL_context::upgrade_shared_lock_to_exclusive(MDL_ticket *mdl_ticket, - ulong lock_wait_timeout) +MDL_context::upgrade_shared_lock(MDL_ticket *mdl_ticket, + enum_mdl_type new_type, + ulong lock_wait_timeout) { MDL_request mdl_xlock_request; MDL_savepoint mdl_svp= mdl_savepoint(); bool is_new_ticket; - DBUG_ENTER("MDL_ticket::upgrade_shared_lock_to_exclusive"); - DEBUG_SYNC(get_thd(), "mdl_upgrade_shared_lock_to_exclusive"); + DBUG_ENTER("MDL_context::upgrade_shared_lock"); + DEBUG_SYNC(get_thd(), "mdl_upgrade_lock"); /* Do nothing if already upgraded. Used when we FLUSH TABLE under LOCK TABLES and a table is listed twice in LOCK TABLES list. */ - if (mdl_ticket->m_type == MDL_EXCLUSIVE) + if (mdl_ticket->has_stronger_or_equal_type(new_type)) DBUG_RETURN(FALSE); - /* Only allow upgrades from MDL_SHARED_NO_WRITE/NO_READ_WRITE */ - DBUG_ASSERT(mdl_ticket->m_type == MDL_SHARED_NO_WRITE || + /* Only allow upgrades from SHARED_UPGRADABLE/NO_WRITE/NO_READ_WRITE */ + DBUG_ASSERT(mdl_ticket->m_type == MDL_SHARED_UPGRADABLE || + mdl_ticket->m_type == MDL_SHARED_NO_WRITE || mdl_ticket->m_type == MDL_SHARED_NO_READ_WRITE); - mdl_xlock_request.init(&mdl_ticket->m_lock->key, MDL_EXCLUSIVE, + mdl_xlock_request.init(&mdl_ticket->m_lock->key, new_type, MDL_TRANSACTION); if (acquire_lock(&mdl_xlock_request, lock_wait_timeout)) @@ -2317,7 +2330,7 @@ MDL_context::upgrade_shared_lock_to_exclusive(MDL_ticket *mdl_ticket, ticket from the granted queue and then include it back. */ mdl_ticket->m_lock->m_granted.remove_ticket(mdl_ticket); - mdl_ticket->m_type= MDL_EXCLUSIVE; + mdl_ticket->m_type= new_type; mdl_ticket->m_lock->m_granted.add_ticket(mdl_ticket); mysql_prlock_unlock(&mdl_ticket->m_lock->m_rwlock); @@ -2681,22 +2694,29 @@ void MDL_context::release_all_locks_for_name(MDL_ticket *name) /** - Downgrade an exclusive lock to shared metadata lock. + Downgrade an EXCLUSIVE or SHARED_NO_WRITE lock to shared metadata lock. @param type Type of lock to which exclusive lock should be downgraded. */ -void MDL_ticket::downgrade_exclusive_lock(enum_mdl_type type) +void MDL_ticket::downgrade_lock(enum_mdl_type type) { mysql_mutex_assert_not_owner(&LOCK_open); /* Do nothing if already downgraded. Used when we FLUSH TABLE under LOCK TABLES and a table is listed twice in LOCK TABLES list. + Note that this code might even try to "downgrade" a weak lock + (e.g. SW) to a stronger one (e.g SNRW). So we can't even assert + here that target lock is weaker than existing lock. */ - if (m_type != MDL_EXCLUSIVE) + if (m_type == type || !has_stronger_or_equal_type(type)) return; + /* Only allow downgrade from EXCLUSIVE and SHARED_NO_WRITE. */ + DBUG_ASSERT(m_type == MDL_EXCLUSIVE || + m_type == MDL_SHARED_NO_WRITE); + mysql_prlock_wrlock(&m_lock->m_rwlock); /* To update state of MDL_lock object correctly we need to temporarily diff --git a/sql/mdl.h b/sql/mdl.h index c778dbbc1d7..82d764bc66c 100644 --- a/sql/mdl.h +++ b/sql/mdl.h @@ -132,6 +132,15 @@ enum enum_mdl_type { */ MDL_SHARED_WRITE, /* + An upgradable shared metadata lock for cases when there is an intention + to modify (and not just read) data in the table. + Can be upgraded to MDL_SHARED_NO_WRITE and MDL_EXCLUSIVE. + A connection holding SU lock can read table metadata and modify or read + table data (after acquiring appropriate table and row-level locks). + To be used for the first phase of ALTER TABLE. + */ + MDL_SHARED_UPGRADABLE, + /* An upgradable shared metadata lock which blocks all attempts to update table data, allowing reads. A connection holding this kind of lock can read table metadata and read @@ -502,13 +511,14 @@ public: MDL_context *get_ctx() const { return m_ctx; } bool is_upgradable_or_exclusive() const { - return m_type == MDL_SHARED_NO_WRITE || + return m_type == MDL_SHARED_UPGRADABLE || + m_type == MDL_SHARED_NO_WRITE || m_type == MDL_SHARED_NO_READ_WRITE || m_type == MDL_EXCLUSIVE; } enum_mdl_type get_type() const { return m_type; } MDL_lock *get_lock() const { return m_lock; } - void downgrade_exclusive_lock(enum_mdl_type type); + void downgrade_lock(enum_mdl_type type); bool has_stronger_or_equal_type(enum_mdl_type type) const; @@ -660,8 +670,9 @@ public: bool try_acquire_lock(MDL_request *mdl_request); bool acquire_lock(MDL_request *mdl_request, ulong lock_wait_timeout); bool acquire_locks(MDL_request_list *requests, ulong lock_wait_timeout); - bool upgrade_shared_lock_to_exclusive(MDL_ticket *mdl_ticket, - ulong lock_wait_timeout); + bool upgrade_shared_lock(MDL_ticket *mdl_ticket, + enum_mdl_type new_type, + ulong lock_wait_timeout); bool clone_ticket(MDL_request *mdl_request); diff --git a/sql/multi_range_read.cc b/sql/multi_range_read.cc index f1823ed69fa..2f41f2ebaf2 100644 --- a/sql/multi_range_read.cc +++ b/sql/multi_range_read.cc @@ -1496,7 +1496,7 @@ ha_rows DsMrr_impl::dsmrr_info_const(uint keyno, RANGE_SEQ_IF *seq, bool key_uses_partial_cols(TABLE *table, uint keyno) { KEY_PART_INFO *kp= table->key_info[keyno].key_part; - KEY_PART_INFO *kp_end= kp + table->key_info[keyno].key_parts; + KEY_PART_INFO *kp_end= kp + table->key_info[keyno].user_defined_key_parts; for (; kp != kp_end; kp++) { if (!kp->field->part_of_key.is_set(keyno)) diff --git a/sql/my_decimal.cc b/sql/my_decimal.cc index 21611afd87b..c11bf671cb1 100644 --- a/sql/my_decimal.cc +++ b/sql/my_decimal.cc @@ -45,21 +45,21 @@ int decimal_operation_results(int result, const char *value, const char *type) case E_DEC_OK: break; case E_DEC_TRUNCATED: - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, ER_DATA_TRUNCATED, ER(ER_DATA_TRUNCATED), value, type); break; case E_DEC_OVERFLOW: - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, ER_DATA_OVERFLOW, ER(ER_DATA_OVERFLOW), value, type); break; case E_DEC_DIV_ZERO: - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, ER_DIVISION_BY_ZERO, ER(ER_DIVISION_BY_ZERO)); break; case E_DEC_BAD_NUM: - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, ER_BAD_DATA, ER(ER_BAD_DATA), value, type); break; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index a5202cb8888..65cce38e788 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -738,6 +738,7 @@ static char **remaining_argv; int orig_argc; char **orig_argv; +#ifndef EMBEDDED_LIBRARY static struct my_option pfs_early_options[]= { {"performance_schema_instrument", OPT_PFS_INSTRUMENT, @@ -805,8 +806,7 @@ static struct my_option pfs_early_options[]= &pfs_param.m_consumer_statement_digest_enabled, 0, GET_BOOL, OPT_ARG, TRUE, 0, 0, 0, 0, 0} }; - - +#endif #ifdef HAVE_PSI_INTERFACE #ifdef HAVE_MMAP @@ -1086,7 +1086,8 @@ void net_after_header_psi(struct st_net *net, void *user_data, size_t /* unused: { thd->m_statement_psi= MYSQL_START_STATEMENT(&thd->m_statement_state, stmt_info_new_packet.m_key, - thd->db, thd->db_length); + thd->db, thd->db_length, + thd->charset()); THD_STAGE_INFO(thd, stage_init); } @@ -1118,12 +1119,6 @@ void init_net_server_extension(THD *thd) } #endif /* EMBEDDED_LIBRARY */ -/* - Since buffered_option_error_reporter is only used currently - for parsing performance schema options, this code is not needed - when the performance schema is not compiled in. -*/ -#ifdef WITH_PERFSCHEMA_STORAGE_ENGINE /** A log message for the error log, buffered in memory. Log messages are temporarily buffered when generated before the error log @@ -1259,13 +1254,16 @@ void Buffered_logs::print() /** Logs reported before a logger is available. */ static Buffered_logs buffered_logs; +static MYSQL_SOCKET unix_sock, base_ip_sock, extra_ip_sock; +struct my_rnd_struct sql_rand; ///< used by sql_class.cc:THD::THD() + #ifndef EMBEDDED_LIBRARY /** Error reporter that buffer log messages. @param level log message level @param format log message format string */ -C_MODE_START + static void buffered_option_error_reporter(enum loglevel level, const char *format, ...) { @@ -1277,14 +1275,7 @@ static void buffered_option_error_reporter(enum loglevel level, va_end(args); buffered_logs.buffer(level, buffer); } -C_MODE_END -#endif /* !EMBEDDED_LIBRARY */ -#endif /* WITH_PERFSCHEMA_STORAGE_ENGINE */ - -static MYSQL_SOCKET unix_sock, base_ip_sock, extra_ip_sock; -struct my_rnd_struct sql_rand; ///< used by sql_class.cc:THD::THD() -#ifndef EMBEDDED_LIBRARY struct passwd *user_info; static pthread_t select_thread; #endif @@ -3246,7 +3237,7 @@ extern "C" void my_message_sql(uint error, const char *str, myf MyFlags); void my_message_sql(uint error, const char *str, myf MyFlags) { THD *thd= current_thd; - MYSQL_ERROR::enum_warning_level level; + Sql_condition::enum_warning_level level; sql_print_message_func func; DBUG_ENTER("my_message_sql"); DBUG_PRINT("error", ("error: %u message: '%s' Flag: %lu", error, str, @@ -3258,17 +3249,17 @@ void my_message_sql(uint error, const char *str, myf MyFlags) mysql_audit_general(thd, MYSQL_AUDIT_GENERAL_ERROR, error, str); if (MyFlags & ME_JUST_INFO) { - level= MYSQL_ERROR::WARN_LEVEL_NOTE; + level= Sql_condition::WARN_LEVEL_NOTE; func= sql_print_information; } else if (MyFlags & ME_JUST_WARNING) { - level= MYSQL_ERROR::WARN_LEVEL_WARN; + level= Sql_condition::WARN_LEVEL_WARN; func= sql_print_warning; } else { - level= MYSQL_ERROR::WARN_LEVEL_ERROR; + level= Sql_condition::WARN_LEVEL_ERROR; func= sql_print_error; } @@ -3428,6 +3419,7 @@ SHOW_VAR com_status_vars[]= { {"empty_query", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_EMPTY_QUERY]), SHOW_LONG_STATUS}, {"execute_sql", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_EXECUTE]), SHOW_LONG_STATUS}, {"flush", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_FLUSH]), SHOW_LONG_STATUS}, + {"get_diagnostics", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_GET_DIAGNOSTICS]), SHOW_LONG_STATUS}, {"grant", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_GRANT]), SHOW_LONG_STATUS}, {"ha_close", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_HA_CLOSE]), SHOW_LONG_STATUS}, {"ha_open", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_HA_OPEN]), SHOW_LONG_STATUS}, @@ -4916,7 +4908,6 @@ int mysqld_main(int argc, char **argv) sys_var_init(); -#ifdef WITH_PERFSCHEMA_STORAGE_ENGINE /* The performance schema needs to be initialized as early as possible, before to-be-instrumented objects of the server are initialized. @@ -4944,22 +4935,30 @@ int mysqld_main(int argc, char **argv) my_charset_error_reporter= buffered_option_error_reporter; pfs_param.m_pfs_instrument= const_cast<char*>(""); +#ifdef WITH_PERFSCHEMA_STORAGE_ENGINE /* Initialize the array of performance schema instrument configurations. */ init_pfs_instrument_array(); +#endif /* WITH_PERFSCHEMA_STORAGE_ENGINE */ ho_error= handle_options(&remaining_argc, &remaining_argv, (my_option*)(all_early_options.buffer), mysqld_get_one_option); delete_dynamic(&all_early_options); +#ifdef WITH_PERFSCHEMA_STORAGE_ENGINE if (ho_error == 0) { /* Add back the program name handle_options removes */ remaining_argc++; remaining_argv--; - if (pfs_param.m_enabled) + if (pfs_param.m_enabled && !opt_help && !opt_bootstrap) { + /* Add sizing hints from the server sizing parameters. */ + pfs_param.m_hints.m_table_definition_cache= table_def_size; + pfs_param.m_hints.m_table_open_cache= table_cache_size; + pfs_param.m_hints.m_max_connections= max_connections; + pfs_param.m_hints.m_open_files_limit= open_files_limit; PSI_hook= initialize_performance_schema(&pfs_param); if (PSI_hook == NULL) { @@ -6853,7 +6852,7 @@ static int show_slave_running(THD *thd, SHOW_VAR *var, char *buff) mysql_mutex_lock(&LOCK_active_mi); mi= master_info_index-> get_master_info(&thd->variables.default_master_connection, - MYSQL_ERROR::WARN_LEVEL_NOTE); + Sql_condition::WARN_LEVEL_NOTE); if (mi) *((my_bool *)buff)= (my_bool) (mi->slave_running == MYSQL_SLAVE_RUN_CONNECT && @@ -6873,7 +6872,7 @@ static int show_slave_received_heartbeats(THD *thd, SHOW_VAR *var, char *buff) mysql_mutex_lock(&LOCK_active_mi); mi= master_info_index-> get_master_info(&thd->variables.default_master_connection, - MYSQL_ERROR::WARN_LEVEL_NOTE); + Sql_condition::WARN_LEVEL_NOTE); if (mi) *((longlong *)buff)= mi->received_heartbeats; else @@ -6891,7 +6890,7 @@ static int show_heartbeat_period(THD *thd, SHOW_VAR *var, char *buff) mysql_mutex_lock(&LOCK_active_mi); mi= master_info_index-> get_master_info(&thd->variables.default_master_connection, - MYSQL_ERROR::WARN_LEVEL_NOTE); + Sql_condition::WARN_LEVEL_NOTE); if (mi) sprintf(buff, "%.3f", mi->heartbeat_period); else @@ -8892,6 +8891,9 @@ static PSI_file_info all_server_files[]= PSI_stage_info stage_after_create= { 0, "After create", 0}; PSI_stage_info stage_allocating_local_table= { 0, "allocating local table", 0}; +PSI_stage_info stage_alter_inplace_prepare= { 0, "preparing for alter table", 0}; +PSI_stage_info stage_alter_inplace= { 0, "altering table", 0}; +PSI_stage_info stage_alter_inplace_commit= { 0, "committing alter table to storage engine", 0}; PSI_stage_info stage_changing_master= { 0, "Changing master", 0}; PSI_stage_info stage_checking_master_version= { 0, "Checking master version", 0}; PSI_stage_info stage_checking_permissions= { 0, "checking permissions", 0}; diff --git a/sql/mysqld.h b/sql/mysqld.h index 67c9f4e68ba..86b67994668 100644 --- a/sql/mysqld.h +++ b/sql/mysqld.h @@ -306,6 +306,9 @@ void init_server_psi_keys(); */ extern PSI_stage_info stage_after_create; extern PSI_stage_info stage_allocating_local_table; +extern PSI_stage_info stage_alter_inplace_prepare; +extern PSI_stage_info stage_alter_inplace; +extern PSI_stage_info stage_alter_inplace_commit; extern PSI_stage_info stage_changing_master; extern PSI_stage_info stage_checking_master_version; extern PSI_stage_info stage_checking_permissions; diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 8629ea57fb9..1de603193c4 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -112,7 +112,7 @@ #include "key.h" // is_key_used, key_copy, key_cmp, key_restore #include "sql_parse.h" // check_stack_overrun #include "sql_partition.h" // get_part_id_func, PARTITION_ITERATOR, - // struct partition_info + // struct partition_info, NOT_A_PARTITION_ID #include "sql_base.h" // free_io_cache #include "records.h" // init_read_record, end_read_record #include <m_ctype.h> @@ -3386,29 +3386,26 @@ static void dbug_print_singlepoint_range(SEL_ARG **start, uint num); #endif -/* +/** Perform partition pruning for a given table and condition. - SYNOPSIS - prune_partitions() - thd Thread handle - table Table to perform partition pruning for - pprune_cond Condition to use for partition pruning + @param thd Thread handle + @param table Table to perform partition pruning for + @param pprune_cond Condition to use for partition pruning - DESCRIPTION - This function assumes that all partitions are marked as unused when it - is invoked. The function analyzes the condition, finds partitions that - need to be used to retrieve the records that match the condition, and - marks them as used by setting appropriate bit in part_info->used_partitions - In the worst case all partitions are marked as used. - - NOTE - This function returns promptly if called for non-partitioned table. - - RETURN - TRUE We've inferred that no partitions need to be used (i.e. no table - records will satisfy pprune_cond) - FALSE Otherwise + @note This function assumes that lock_partitions are setup when it + is invoked. The function analyzes the condition, finds partitions that + need to be used to retrieve the records that match the condition, and + marks them as used by setting appropriate bit in part_info->read_partitions + In the worst case all partitions are marked as used. If the table is not + yet locked, it will also unset bits in part_info->lock_partitions that is + not set in read_partitions. + + This function returns promptly if called for non-partitioned table. + + @return Operation status + @retval true Failure + @retval false Success */ bool prune_partitions(THD *thd, TABLE *table, Item *pprune_cond) @@ -3461,7 +3458,7 @@ bool prune_partitions(THD *thd, TABLE *table, Item *pprune_cond) thd->no_errors=1; // Don't warn about NULL thd->mem_root=&alloc; - bitmap_clear_all(&part_info->used_partitions); + bitmap_clear_all(&part_info->read_partitions); prune_param.key= prune_param.range_param.key_parts; SEL_TREE *tree; @@ -3545,6 +3542,30 @@ end: thd->mem_root= range_par->old_root; free_root(&alloc,MYF(0)); // Return memory & allocator DBUG_RETURN(retval); + /* + Must be a subset of the locked partitions. + lock_partitions contains the partitions marked by explicit partition + selection (... t PARTITION (pX) ...) and we must only use partitions + within that set. + */ + bitmap_intersect(&prune_param.part_info->read_partitions, + &prune_param.part_info->lock_partitions); + /* + If not yet locked, also prune partitions to lock if not UPDATEing + partition key fields. This will also prune lock_partitions if we are under + LOCK TABLES, so prune away calls to start_stmt(). + TODO: enhance this prune locking to also allow pruning of + 'UPDATE t SET part_key = const WHERE cond_is_prunable' so it adds + a lock for part_key partition. + */ + if (!thd->lex->is_query_tables_locked() && + !partition_key_modified(table, table->write_set)) + { + bitmap_copy(&prune_param.part_info->lock_partitions, + &prune_param.part_info->read_partitions); + } + if (bitmap_is_clear_all(&(prune_param.part_info->read_partitions))) + table->all_partitions_pruned_away= true; } @@ -3619,7 +3640,7 @@ static void mark_full_partition_used_no_parts(partition_info* part_info, { DBUG_ENTER("mark_full_partition_used_no_parts"); DBUG_PRINT("enter", ("Mark partition %u as used", part_id)); - bitmap_set_bit(&part_info->used_partitions, part_id); + bitmap_set_bit(&part_info->read_partitions, part_id); DBUG_VOID_RETURN; } @@ -3635,7 +3656,7 @@ static void mark_full_partition_used_with_parts(partition_info *part_info, for (; start != end; start++) { DBUG_PRINT("info", ("1:Mark subpartition %u as used", start)); - bitmap_set_bit(&part_info->used_partitions, start); + bitmap_set_bit(&part_info->read_partitions, start); } DBUG_VOID_RETURN; } @@ -3663,7 +3684,7 @@ static int find_used_partitions_imerge_list(PART_PRUNE_PARAM *ppar, MY_BITMAP all_merges; uint bitmap_bytes; my_bitmap_map *bitmap_buf; - uint n_bits= ppar->part_info->used_partitions.n_bits; + uint n_bits= ppar->part_info->read_partitions.n_bits; bitmap_bytes= bitmap_buffer_size(n_bits); if (!(bitmap_buf= (my_bitmap_map*) alloc_root(ppar->range_param.mem_root, bitmap_bytes))) @@ -3689,14 +3710,15 @@ static int find_used_partitions_imerge_list(PART_PRUNE_PARAM *ppar, } if (res != -1) - bitmap_intersect(&all_merges, &ppar->part_info->used_partitions); + bitmap_intersect(&all_merges, &ppar->part_info->read_partitions); + if (bitmap_is_clear_all(&all_merges)) return 0; - bitmap_clear_all(&ppar->part_info->used_partitions); + bitmap_clear_all(&ppar->part_info->read_partitions); } - memcpy(ppar->part_info->used_partitions.bitmap, all_merges.bitmap, + memcpy(ppar->part_info->read_partitions.bitmap, all_merges.bitmap, bitmap_bytes); return 1; } @@ -4056,7 +4078,7 @@ int find_used_partitions(PART_PRUNE_PARAM *ppar, SEL_ARG *key_tree) { for (uint i= 0; i < ppar->part_info->num_subparts; i++) if (bitmap_is_set(&ppar->subparts_bitmap, i)) - bitmap_set_bit(&ppar->part_info->used_partitions, + bitmap_set_bit(&ppar->part_info->read_partitions, part_id * ppar->part_info->num_subparts + i); } goto pop_and_go_right; @@ -4118,7 +4140,7 @@ int find_used_partitions(PART_PRUNE_PARAM *ppar, SEL_ARG *key_tree) while ((part_id= ppar->part_iter.get_next(&ppar->part_iter)) != NOT_A_PARTITION_ID) { - bitmap_set_bit(&part_info->used_partitions, + bitmap_set_bit(&part_info->read_partitions, part_id * part_info->num_subparts + subpart_id); } res= 1; /* Some partitions were marked as used */ @@ -4204,7 +4226,8 @@ pop_and_go_right: static void mark_all_partitions_as_used(partition_info *part_info) { - bitmap_set_all(&part_info->used_partitions); + bitmap_copy(&(part_info->read_partitions), + &(part_info->lock_partitions)); } diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc index 7780a7921e5..4d3d60fc2f5 100644 --- a/sql/opt_subselect.cc +++ b/sql/opt_subselect.cc @@ -2370,7 +2370,7 @@ bool find_eq_ref_candidate(TABLE *table, table_map sj_inner_tables) keyuse++; } while (keyuse->key == key && keyuse->table == table); - if (bound_parts == PREV_BITS(uint, keyinfo->key_parts)) + if (bound_parts == PREV_BITS(uint, keyinfo->user_defined_key_parts)) return TRUE; } else @@ -3521,7 +3521,7 @@ bool setup_sj_materialization_part2(JOIN_TAB *sjm_tab) KEY *tmp_key; /* The only index on the temporary table. */ uint tmp_key_parts; /* Number of keyparts in tmp_key. */ tmp_key= sjm->table->key_info; - tmp_key_parts= tmp_key->key_parts; + tmp_key_parts= tmp_key->user_defined_key_parts; /* Create/initialize everything we will need to index lookups into the @@ -4057,7 +4057,7 @@ SJ_TMP_TABLE::create_sj_weedout_tmp_table(THD *thd) table->key_info=keyinfo; keyinfo->key_part=key_part_info; keyinfo->flags=HA_NOSAME; - keyinfo->usable_key_parts= keyinfo->key_parts= 1; + keyinfo->usable_key_parts= keyinfo->user_defined_key_parts= 1; keyinfo->key_length=0; keyinfo->rec_per_key=0; keyinfo->algorithm= HA_KEY_ALG_UNDEF; diff --git a/sql/opt_sum.cc b/sql/opt_sum.cc index fa3a07b72c5..5d78b689e58 100644 --- a/sql/opt_sum.cc +++ b/sql/opt_sum.cc @@ -464,7 +464,7 @@ int opt_sum_query(THD *thd, } if (thd->is_error()) - DBUG_RETURN(thd->stmt_da->sql_errno()); + DBUG_RETURN(thd->get_stmt_da()->sql_errno()); /* If we have a where clause, we can only ignore searching in the diff --git a/sql/opt_table_elimination.cc b/sql/opt_table_elimination.cc index e44b6fdf5e0..afb07b9a6cb 100644 --- a/sql/opt_table_elimination.cc +++ b/sql/opt_table_elimination.cc @@ -1563,7 +1563,7 @@ Dep_value_table *Dep_analysis_context::create_table_value(TABLE *table) if (key->flags & HA_NOSAME) { Dep_module_key *key_dep; - if (!(key_dep= new Dep_module_key(tbl_dep, i, key->key_parts))) + if (!(key_dep= new Dep_module_key(tbl_dep, i, key->user_defined_key_parts))) return NULL; *key_list= key_dep; key_list= &(key_dep->next_table_key); diff --git a/sql/partition_info.cc b/sql/partition_info.cc index 028afd7899e..002d5ebca79 100644 --- a/sql/partition_info.cc +++ b/sql/partition_info.cc @@ -23,10 +23,12 @@ #include "sql_priv.h" // Required to get server definitions for mysql/plugin.h right #include "sql_plugin.h" -#include "sql_partition.h" /* partition_info.h: LIST_PART_ENTRY */ +#include "sql_partition.h" // partition_info.h: LIST_PART_ENTRY + // NOT_A_PARTITION_ID #include "partition_info.h" #include "sql_parse.h" // test_if_data_home_dir #include "sql_acl.h" // *_ACL +#include "sql_base.h" // fill_record #ifdef WITH_PARTITION_STORAGE_ENGINE #include "ha_partition.h" @@ -34,17 +36,21 @@ partition_info *partition_info::get_clone() { + DBUG_ENTER("partition_info::get_clone"); if (!this) - return 0; + DBUG_RETURN(NULL); List_iterator<partition_element> part_it(partitions); partition_element *part; partition_info *clone= new partition_info(); if (!clone) { mem_alloc_error(sizeof(partition_info)); - return NULL; + DBUG_RETURN(NULL); } memcpy(clone, this, sizeof(partition_info)); + memset(&(clone->read_partitions), 0, sizeof(clone->read_partitions)); + memset(&(clone->lock_partitions), 0, sizeof(clone->lock_partitions)); + clone->bitmaps_are_initialized= FALSE; clone->partitions.empty(); while ((part= (part_it++))) @@ -55,7 +61,7 @@ partition_info *partition_info::get_clone() if (!part_clone) { mem_alloc_error(sizeof(partition_element)); - return NULL; + DBUG_RETURN(NULL); } memcpy(part_clone, part, sizeof(partition_element)); part_clone->subpartitions.empty(); @@ -65,16 +71,427 @@ partition_info *partition_info::get_clone() if (!subpart_clone) { mem_alloc_error(sizeof(partition_element)); - return NULL; + DBUG_RETURN(NULL); } memcpy(subpart_clone, subpart, sizeof(partition_element)); part_clone->subpartitions.push_back(subpart_clone); } clone->partitions.push_back(part_clone); } - return clone; + DBUG_RETURN(clone); +} + +/** + Mark named [sub]partition to be used/locked. + + @param part_name Partition name to match. + @param length Partition name length. + + @return Success if partition found + @retval true Partition found + @retval false Partition not found +*/ + +bool partition_info::add_named_partition(const char *part_name, + uint length) +{ + HASH *part_name_hash; + PART_NAME_DEF *part_def; + Partition_share *part_share; + DBUG_ENTER("partition_info::add_named_partition"); + DBUG_ASSERT(table && table->s && table->s->ha_share); + part_share= static_cast<Partition_share*>((table->s->ha_share)); + DBUG_ASSERT(part_share->partition_name_hash_initialized); + part_name_hash= &part_share->partition_name_hash; + DBUG_ASSERT(part_name_hash->records); + + part_def= (PART_NAME_DEF*) my_hash_search(part_name_hash, + (const uchar*) part_name, + length); + if (!part_def) + { +// my_error(ER_UNKNOWN_PARTITION, MYF(0), part_name, table->alias); + DBUG_RETURN(true); + } + + if (part_def->is_subpart) + { + bitmap_set_bit(&read_partitions, part_def->part_id); + } + else + { + if (is_sub_partitioned()) + { + /* Mark all subpartitions in the partition */ + uint j, start= part_def->part_id; + uint end= start + num_subparts; + for (j= start; j < end; j++) + bitmap_set_bit(&read_partitions, j); + } + else + bitmap_set_bit(&read_partitions, part_def->part_id); + } + DBUG_PRINT("info", ("Found partition %u is_subpart %d for name %s", + part_def->part_id, part_def->is_subpart, + part_name)); + DBUG_RETURN(false); +} + + +/** + Mark named [sub]partition to be used/locked. + + @param part_elem Partition element that matched. +*/ + +bool partition_info::set_named_partition_bitmap(const char *part_name, + uint length) +{ + DBUG_ENTER("partition_info::set_named_partition_bitmap"); + bitmap_clear_all(&read_partitions); + if (add_named_partition(part_name, length)) + DBUG_RETURN(true); + bitmap_copy(&lock_partitions, &read_partitions); + DBUG_RETURN(false); +} + + + +/** + Prune away partitions not mentioned in the PARTITION () clause, + if used. + + @param table_list Table list pointing to table to prune. + + @return Operation status + @retval true Failure + @retval false Success +*/ +bool partition_info::prune_partition_bitmaps(TABLE_LIST *table_list) +{ + List_iterator<String> partition_names_it(*(table_list->partition_names)); + uint num_names= table_list->partition_names->elements; + uint i= 0; + DBUG_ENTER("partition_info::prune_partition_bitmaps"); + + if (num_names < 1) + DBUG_RETURN(true); + + /* + TODO: When adding support for FK in partitioned tables, the referenced + table must probably lock all partitions for read, and also write depending + of ON DELETE/UPDATE. + */ + bitmap_clear_all(&read_partitions); + + /* No check for duplicate names or overlapping partitions/subpartitions. */ + + DBUG_PRINT("info", ("Searching through partition_name_hash")); + do + { + String *part_name_str= partition_names_it++; + if (add_named_partition(part_name_str->c_ptr(), part_name_str->length())) + DBUG_RETURN(true); + } while (++i < num_names); + DBUG_RETURN(false); +} + + +/** + Set read/lock_partitions bitmap over non pruned partitions + + @param table_list Possible TABLE_LIST which can contain + list of partition names to query + + @return Operation status + @retval FALSE OK + @retval TRUE Failed to allocate memory for bitmap or list of partitions + did not match + + @note OK to call multiple times without the need for free_bitmaps. +*/ + +bool partition_info::set_partition_bitmaps(TABLE_LIST *table_list) +{ + DBUG_ENTER("partition_info::set_partition_bitmaps"); + + DBUG_ASSERT(bitmaps_are_initialized); + DBUG_ASSERT(table); + is_pruning_completed= false; + if (!bitmaps_are_initialized) + DBUG_RETURN(TRUE); + + if (table_list && + table_list->partition_names && + table_list->partition_names->elements) + { + if (table->s->db_type()->partition_flags() & HA_USE_AUTO_PARTITION) + { + /* + Don't allow PARTITION () clause on a NDB tables yet. + TODO: Add partition name handling to NDB/partition_info. + which is currently ha_partition specific. + */ + my_error(ER_PARTITION_CLAUSE_ON_NONPARTITIONED, MYF(0)); + DBUG_RETURN(true); + } + if (prune_partition_bitmaps(table_list)) + DBUG_RETURN(TRUE); + } + else + { + bitmap_set_all(&read_partitions); + DBUG_PRINT("info", ("Set all partitions")); + } + bitmap_copy(&lock_partitions, &read_partitions); + DBUG_ASSERT(bitmap_get_first_set(&lock_partitions) != MY_BIT_NONE); + DBUG_RETURN(FALSE); +} + + +/** + Checks if possible to do prune partitions on insert. + + @param thd Thread context + @param duplic How to handle duplicates + @param update In case of ON DUPLICATE UPDATE, default function fields + @param update_fields In case of ON DUPLICATE UPDATE, which fields to update + @param fields Listed fields + @param empty_values True if values is empty (only defaults) + @param[out] prune_needs_default_values Set on return if copying of default + values is needed + @param[out] can_prune_partitions Enum showing if possible to prune + @param[inout] used_partitions If possible to prune the bitmap + is initialized and cleared + + @return Operation status + @retval false Success + @retval true Failure +*/ + +bool partition_info::can_prune_insert(THD* thd, + enum_duplicates duplic, + COPY_INFO &update, + List<Item> &update_fields, + List<Item> &fields, + bool empty_values, + enum_can_prune *can_prune_partitions, + bool *prune_needs_default_values, + MY_BITMAP *used_partitions) +{ + uint32 *bitmap_buf; + uint bitmap_bytes; + uint num_partitions= 0; + *can_prune_partitions= PRUNE_NO; + DBUG_ASSERT(bitmaps_are_initialized); + DBUG_ENTER("partition_info::can_prune_insert"); + + if (table->s->db_type()->partition_flags() & HA_USE_AUTO_PARTITION) + DBUG_RETURN(false); /* Should not insert prune NDB tables */ + + /* + If under LOCK TABLES pruning will skip start_stmt instead of external_lock + for unused partitions. + + Cannot prune if there are BEFORE INSERT triggers that changes any + partitioning column, since they may change the row to be in another + partition. + */ + if (table->triggers && + table->triggers->has_triggers(TRG_EVENT_INSERT, TRG_ACTION_BEFORE) && + table->triggers->is_fields_updated_in_trigger(&full_part_field_set, + TRG_EVENT_INSERT, + TRG_ACTION_BEFORE)) + DBUG_RETURN(false); + + if (table->found_next_number_field) + { + /* + If the field is used in the partitioning expression, we cannot prune. + TODO: If all rows have not null values and + is not 0 (with NO_AUTO_VALUE_ON_ZERO sql_mode), then pruning is possible! + */ + if (bitmap_is_set(&full_part_field_set, + table->found_next_number_field->field_index)) + DBUG_RETURN(false); + } + + /* + If updating a field in the partitioning expression, we cannot prune. + + Note: TIMESTAMP_AUTO_SET_ON_INSERT is handled by converting Item_null + to the start time of the statement. Which will be the same as in + write_row(). So pruning of TIMESTAMP DEFAULT CURRENT_TIME will work. + But TIMESTAMP_AUTO_SET_ON_UPDATE cannot be pruned if the timestamp + column is a part of any part/subpart expression. + */ + if (duplic == DUP_UPDATE) + { + /* + TODO: add check for static update values, which can be pruned. + */ + if (is_field_in_part_expr(update_fields)) + DBUG_RETURN(false); + + /* + Cannot prune if there are BEFORE UPDATE triggers that changes any + partitioning column, since they may change the row to be in another + partition. + */ + if (table->triggers && + table->triggers->has_triggers(TRG_EVENT_UPDATE, + TRG_ACTION_BEFORE) && + table->triggers->is_fields_updated_in_trigger(&full_part_field_set, + TRG_EVENT_UPDATE, + TRG_ACTION_BEFORE)) + { + DBUG_RETURN(false); + } + } + + /* + If not all partitioning fields are given, + we also must set all non given partitioning fields + to get correct defaults. + TODO: If any gain, we could enhance this by only copy the needed default + fields by + 1) check which fields needs to be set. + 2) only copy those fields from the default record. + */ + *prune_needs_default_values= false; + if (fields.elements) + { + if (!is_full_part_expr_in_fields(fields)) + *prune_needs_default_values= true; + } + else if (empty_values) + { + *prune_needs_default_values= true; // like 'INSERT INTO t () VALUES ()' + } + else + { + /* + In case of INSERT INTO t VALUES (...) we must get values for + all fields in table from VALUES (...) part, so no defaults + are needed. + */ + } + + /* Pruning possible, have to initialize the used_partitions bitmap. */ + num_partitions= lock_partitions.n_bits; + bitmap_bytes= bitmap_buffer_size(num_partitions); + if (!(bitmap_buf= (uint32*) thd->alloc(bitmap_bytes))) + { + mem_alloc_error(bitmap_bytes); + DBUG_RETURN(true); + } + /* Also clears all bits. */ + if (bitmap_init(used_partitions, bitmap_buf, num_partitions, false)) + { + /* purecov: begin deadcode */ + /* Cannot happen, due to pre-alloc. */ + mem_alloc_error(bitmap_bytes); + DBUG_RETURN(true); + /* purecov: end */ + } + /* + If no partitioning field in set (e.g. defaults) check pruning only once. + */ + if (fields.elements && + !is_field_in_part_expr(fields)) + *can_prune_partitions= PRUNE_DEFAULTS; + else + *can_prune_partitions= PRUNE_YES; + + DBUG_RETURN(false); } + +/** + Mark the partition, the record belongs to, as used. + + @param fields Fields to set + @param values Values to use + @param info COPY_INFO used for default values handling + @param copy_default_values True if we should copy default values + @param used_partitions Bitmap to set + + @returns Operational status + @retval false Success + @retval true Failure +*/ + +bool partition_info::set_used_partition(List<Item> &fields, + List<Item> &values, + COPY_INFO &info, + bool copy_default_values, + MY_BITMAP *used_partitions) +{ + THD *thd= table->in_use; + uint32 part_id; + longlong func_value; + Dummy_error_handler error_handler; + bool ret= true; + DBUG_ENTER("set_partition"); + DBUG_ASSERT(thd); + + /* Only allow checking of constant values */ + List_iterator_fast<Item> v(values); + Item *item; + thd->push_internal_handler(&error_handler); + while ((item= v++)) + { + if (!item->const_item()) + goto err; + } + + if (copy_default_values) + restore_record(table,s->default_values); + + if (fields.elements || !values.elements) + { + if (fill_record(thd, table, fields, values, false)) + goto err; + } + else + { + if (fill_record(thd, table, table->field, values, false, false)) + goto err; + } + DBUG_ASSERT(!table->auto_increment_field_not_null); + + /* + Evaluate DEFAULT functions like CURRENT_TIMESTAMP. + TODO: avoid setting non partitioning fields default value, to avoid + overhead. Not yet done, since mostly only one DEFAULT function per + table, or at least very few such columns. + */ +// if (info.function_defaults_apply_on_columns(&full_part_field_set)) +// info.set_function_defaults(table); + + { + /* + This function is used in INSERT; 'values' are supplied by user, + or are default values, not values read from a table, so read_set is + irrelevant. + */ + my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set); + const int rc= get_partition_id(this, &part_id, &func_value); + dbug_tmp_restore_column_map(table->read_set, old_map); + if (rc) + goto err; + } + + DBUG_PRINT("info", ("Insert into partition %u", part_id)); + bitmap_set_bit(used_partitions, part_id); + ret= false; + +err: + thd->pop_internal_handler(); + DBUG_RETURN(ret); +} + + /* Create a memory area where default partition names are stored and fill it up with the names. @@ -160,8 +577,9 @@ void partition_info::set_show_version_string(String *packet) /* Create a unique name for the subpartition as part_name'sp''subpart_no' + SYNOPSIS - create_subpartition_name() + create_default_subpartition_name() subpart_no Number of subpartition part_name Name of partition RETURN VALUES @@ -169,12 +587,12 @@ void partition_info::set_show_version_string(String *packet) 0 Memory allocation error */ -char *partition_info::create_subpartition_name(uint subpart_no, +char *partition_info::create_default_subpartition_name(uint subpart_no, const char *part_name) { uint size_alloc= strlen(part_name) + MAX_PART_NAME_SIZE; char *ptr= (char*) sql_calloc(size_alloc); - DBUG_ENTER("create_subpartition_name"); + DBUG_ENTER("create_default_subpartition_name"); if (likely(ptr != NULL)) { @@ -320,7 +738,8 @@ bool partition_info::set_up_default_subpartitions(handler *file, if (likely(subpart_elem != 0 && (!part_elem->subpartitions.push_back(subpart_elem)))) { - char *ptr= create_subpartition_name(j, part_elem->partition_name); + char *ptr= create_default_subpartition_name(j, + part_elem->partition_name); if (!ptr) goto end; subpart_elem->engine_type= default_engine_type; @@ -380,7 +799,7 @@ bool partition_info::set_up_defaults_for_partitioning(handler *file, Support routine for check_partition_info SYNOPSIS - has_unique_fields + find_duplicate_field no parameters RETURN VALUE @@ -391,13 +810,13 @@ bool partition_info::set_up_defaults_for_partitioning(handler *file, Check that the user haven't defined the same field twice in key or column list partitioning. */ -char* partition_info::has_unique_fields() +char* partition_info::find_duplicate_field() { char *field_name_outer, *field_name_inner; List_iterator<char> it_outer(part_field_list); uint num_fields= part_field_list.elements; uint i,j; - DBUG_ENTER("partition_info::has_unique_fields"); + DBUG_ENTER("partition_info::find_duplicate_field"); for (i= 0; i < num_fields; i++) { @@ -419,88 +838,149 @@ char* partition_info::has_unique_fields() DBUG_RETURN(NULL); } -/* - A support function to check if a partition element's name is unique - - SYNOPSIS - has_unique_name() - partition_element element to check - RETURN VALUES - TRUE Has unique name - FALSE Doesn't -*/ +/** + @brief Get part_elem and part_id from partition name + + @param partition_name Name of partition to search for. + @param file_name[out] Partition file name (part after table name, + #P#<part>[#SP#<subpart>]), skipped if NULL. + @param part_id[out] Id of found partition or NOT_A_PARTITION_ID. -bool partition_info::has_unique_name(partition_element *element) + @retval Pointer to part_elem of [sub]partition, if not found NULL + + @note Since names of partitions AND subpartitions must be unique, + this function searches both partitions and subpartitions and if name of + a partition is given for a subpartitioned table, part_elem will be + the partition, but part_id will be NOT_A_PARTITION_ID and file_name not set. +*/ +partition_element *partition_info::get_part_elem(const char *partition_name, + char *file_name, + uint32 *part_id) { - DBUG_ENTER("partition_info::has_unique_name"); - - const char *name_to_check= element->partition_name; - List_iterator<partition_element> parts_it(partitions); - - partition_element *el; - while ((el= (parts_it++))) + List_iterator<partition_element> part_it(partitions); + uint i= 0; + DBUG_ENTER("partition_info::get_part_elem"); + DBUG_ASSERT(part_id); + *part_id= NOT_A_PARTITION_ID; + do { - if (!(my_strcasecmp(system_charset_info, el->partition_name, - name_to_check)) && el != element) - DBUG_RETURN(FALSE); - - if (!el->subpartitions.is_empty()) + partition_element *part_elem= part_it++; + if (is_sub_partitioned()) { - partition_element *sub_el; - List_iterator<partition_element> subparts_it(el->subpartitions); - while ((sub_el= (subparts_it++))) + List_iterator<partition_element> sub_part_it(part_elem->subpartitions); + uint j= 0; + do { - if (!(my_strcasecmp(system_charset_info, sub_el->partition_name, - name_to_check)) && sub_el != element) - DBUG_RETURN(FALSE); - } + partition_element *sub_part_elem= sub_part_it++; + if (!my_strcasecmp(system_charset_info, + sub_part_elem->partition_name, partition_name)) + { + if (file_name) + create_subpartition_name(file_name, "", + part_elem->partition_name, + partition_name, + NORMAL_PART_NAME); + *part_id= j + (i * num_subparts); + DBUG_RETURN(sub_part_elem); + } + } while (++j < num_subparts); + + /* Naming a partition (first level) on a subpartitioned table. */ + if (!my_strcasecmp(system_charset_info, + part_elem->partition_name, partition_name)) + DBUG_RETURN(part_elem); } - } - DBUG_RETURN(TRUE); + else if (!my_strcasecmp(system_charset_info, + part_elem->partition_name, partition_name)) + { + if (file_name) + create_partition_name(file_name, "", partition_name, + NORMAL_PART_NAME, TRUE); + *part_id= i; + DBUG_RETURN(part_elem); + } + } while (++i < num_parts); + DBUG_RETURN(NULL); } +/** + Helper function to find_duplicate_name. +*/ + +static const char *get_part_name_from_elem(const char *name, size_t *length, + my_bool not_used __attribute__((unused))) +{ + *length= strlen(name); + return name; +} + /* A support function to check partition names for duplication in a partitioned table SYNOPSIS - has_unique_names() + find_duplicate_name() RETURN VALUES - TRUE Has unique part and subpart names - FALSE Doesn't + NULL Has unique part and subpart names + !NULL Pointer to duplicated name DESCRIPTION Checks that the list of names in the partitions doesn't contain any duplicated names. */ -char *partition_info::has_unique_names() +char *partition_info::find_duplicate_name() { - DBUG_ENTER("partition_info::has_unique_names"); - + HASH partition_names; + uint max_names; + const uchar *curr_name= NULL; List_iterator<partition_element> parts_it(partitions); + partition_element *p_elem; + + DBUG_ENTER("partition_info::find_duplicate_name"); + + /* + TODO: If table->s->ha_part_data->partition_name_hash.elements is > 0, + then we could just return NULL, but that has not been verified. + And this only happens when in ALTER TABLE with full table copy. + */ - partition_element *el; - while ((el= (parts_it++))) + max_names= num_parts; + if (is_sub_partitioned()) + max_names+= num_parts * num_subparts; + if (my_hash_init(&partition_names, system_charset_info, max_names, 0, 0, + (my_hash_get_key) get_part_name_from_elem, 0, HASH_UNIQUE)) + { + DBUG_ASSERT(0); + curr_name= (const uchar*) "Internal failure"; + goto error; + } + while ((p_elem= (parts_it++))) { - if (! has_unique_name(el)) - DBUG_RETURN(el->partition_name); - - if (!el->subpartitions.is_empty()) + curr_name= (const uchar*) p_elem->partition_name; + if (my_hash_insert(&partition_names, curr_name)) + goto error; + + if (!p_elem->subpartitions.is_empty()) { - List_iterator<partition_element> subparts_it(el->subpartitions); - partition_element *subel; - while ((subel= (subparts_it++))) + List_iterator<partition_element> subparts_it(p_elem->subpartitions); + partition_element *subp_elem; + while ((subp_elem= (subparts_it++))) { - if (! has_unique_name(subel)) - DBUG_RETURN(subel->partition_name); + curr_name= (const uchar*) subp_elem->partition_name; + if (my_hash_insert(&partition_names, curr_name)) + goto error; } } - } + } + my_hash_free(&partition_names); DBUG_RETURN(NULL); +error: + my_hash_free(&partition_names); + DBUG_RETURN((char*) curr_name); } @@ -1058,11 +1538,11 @@ static void warn_if_dir_in_part_elem(THD *thd, partition_element *part_elem) #endif { if (part_elem->data_file_name) - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, WARN_OPTION_IGNORED, ER(WARN_OPTION_IGNORED), "DATA DIRECTORY"); if (part_elem->index_file_name) - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, WARN_OPTION_IGNORED, ER(WARN_OPTION_IGNORED), "INDEX DIRECTORY"); part_elem->data_file_name= part_elem->index_file_name= NULL; @@ -1188,12 +1668,12 @@ bool partition_info::check_partition_info(THD *thd, handlerton **eng_type, } if (part_field_list.elements > 0 && - (same_name= has_unique_fields())) + (same_name= find_duplicate_field())) { my_error(ER_SAME_NAME_PARTITION_FIELD, MYF(0), same_name); goto end; } - if ((same_name= has_unique_names())) + if ((same_name= find_duplicate_name())) { my_error(ER_SAME_NAME_PARTITION, MYF(0), same_name); goto end; @@ -1653,6 +2133,71 @@ void partition_info::report_part_expr_error(bool use_subpart_expr) } +/** + Check if fields are in the partitioning expression. + + @param fields List of Items (fields) + + @return True if any field in the fields list is used by a partitioning expr. + @retval true At least one field in the field list is found. + @retval false No field is within any partitioning expression. +*/ + +bool partition_info::is_field_in_part_expr(List<Item> &fields) +{ + List_iterator<Item> it(fields); + Item *item; + Item_field *field; + DBUG_ENTER("is_fields_in_part_expr"); + while ((item= it++)) + { + field= item->field_for_view_update(); + DBUG_ASSERT(field->field->table == table); + if (bitmap_is_set(&full_part_field_set, field->field->field_index)) + DBUG_RETURN(true); + } + DBUG_RETURN(false); +} + + +/** + Check if all partitioning fields are included. +*/ + +bool partition_info::is_full_part_expr_in_fields(List<Item> &fields) +{ + Field **part_field= full_part_field_array; + DBUG_ASSERT(*part_field); + DBUG_ENTER("is_full_part_expr_in_fields"); + /* + It is very seldom many fields in full_part_field_array, so it is OK + to loop over all of them instead of creating a bitmap fields argument + to compare with. + */ + do + { + List_iterator<Item> it(fields); + Item *item; + Item_field *field; + bool found= false; + + while ((item= it++)) + { + field= item->field_for_view_update(); + DBUG_ASSERT(field->field->table == table); + if (*part_field == field->field) + { + found= true; + break; + } + } + if (!found) + DBUG_RETURN(false); + } while (*(++part_field)); + DBUG_RETURN(true); +} + + /* Create a new column value in current list with maxvalue Called from parser diff --git a/sql/partition_info.h b/sql/partition_info.h index e59d4ec8ba4..e3e88a67a27 100644 --- a/sql/partition_info.h +++ b/sql/partition_info.h @@ -20,10 +20,11 @@ #pragma interface /* gcc class implementation */ #endif +#include "sql_class.h" #include "partition_element.h" class partition_info; - +struct TABLE_LIST; /* Some function typedefs */ typedef int (*get_part_id_func)(partition_info *part_info, uint32 *part_id, @@ -111,14 +112,30 @@ public: struct st_ddl_log_memory_entry *frm_log_entry; /* - A bitmap of partitions used by the current query. + Bitmaps of partitions used by the current query. + * read_partitions - partitions to be used for reading. + * lock_partitions - partitions that must be locked (read or write). + Usually read_partitions is the same set as lock_partitions, but + in case of UPDATE the WHERE clause can limit the read_partitions set, + but not neccesarily the lock_partitions set. Usage pattern: - * The handler->extra(HA_EXTRA_RESET) call at query start/end sets all - partitions to be unused. - * Before index/rnd_init(), partition pruning code sets the bits for used - partitions. + * Initialized in ha_partition::open(). + * read+lock_partitions is set according to explicit PARTITION, + WL#5217, in open_and_lock_tables(). + * Bits in read_partitions can be cleared in prune_partitions() + in the optimizing step. + (WL#4443 is about allowing prune_partitions() to affect lock_partitions + and be done before locking too). + * When the partition enabled handler get an external_lock call it locks + all partitions in lock_partitions (and remembers which partitions it + locked, so that it can unlock them later). In case of LOCK TABLES it will + lock all partitions, and keep them locked while lock_partitions can + change for each statement under LOCK TABLES. + * Freed at the same time item_free_list is freed. */ - MY_BITMAP used_partitions; + MY_BITMAP read_partitions; + MY_BITMAP lock_partitions; + bool bitmaps_are_initialized; union { longlong *range_int_array; @@ -157,6 +174,7 @@ public: uint curr_list_object; uint num_columns; + TABLE *table; /* These key_map's are used for Partitioning to enable quick decisions on whether we can derive more information about which partition to @@ -207,6 +225,15 @@ public: bool from_openfrm; bool has_null_value; bool column_list; + /** + True if pruning has been completed and can not be pruned any further, + even if there are subqueries or stored programs in the condition. + + Some times it is needed to run prune_partitions() a second time to prune + read partitions after tables are locked, when subquery and + stored functions might have been evaluated. + */ + bool is_pruning_completed; partition_info() : get_partition_id(NULL), get_part_partition_id(NULL), @@ -219,6 +246,7 @@ public: restore_part_field_ptrs(NULL), restore_subpart_field_ptrs(NULL), part_expr(NULL), subpart_expr(NULL), item_free_list(NULL), first_log_entry(NULL), exec_log_entry(NULL), frm_log_entry(NULL), + bitmaps_are_initialized(FALSE), list_array(NULL), err_value(0), part_info_string(NULL), part_func_string(NULL), subpart_func_string(NULL), @@ -238,7 +266,7 @@ public: list_of_part_fields(FALSE), list_of_subpart_fields(FALSE), linear_hash_ind(FALSE), fixed(FALSE), is_auto_partitioned(FALSE), from_openfrm(FALSE), - has_null_value(FALSE), column_list(FALSE) + has_null_value(FALSE), column_list(FALSE), is_pruning_completed(false) { all_fields_in_PF.clear_all(); all_fields_in_PPF.clear_all(); @@ -252,6 +280,8 @@ public: ~partition_info() {} partition_info *get_clone(); + bool set_named_partition_bitmap(const char *part_name, uint length); + bool set_partition_bitmaps(TABLE_LIST *table_list); /* Answers the question if subpartitioning is used for a certain table */ bool is_sub_partitioned() { @@ -266,8 +296,8 @@ public: bool set_up_defaults_for_partitioning(handler *file, HA_CREATE_INFO *info, uint start_no); - char *has_unique_fields(); - char *has_unique_names(); + char *find_duplicate_field(); + char *find_duplicate_name(); bool check_engine_mix(handlerton *engine_type, bool default_engine); bool check_range_constants(THD *thd); bool check_list_constants(THD *thd); @@ -297,7 +327,34 @@ public: bool init_column_part(); bool add_column_list_value(THD *thd, Item *item); void set_show_version_string(String *packet); + partition_element *get_part_elem(const char *partition_name, + char *file_name, + uint32 *part_id); void report_part_expr_error(bool use_subpart_expr); + bool set_used_partition(List<Item> &fields, + List<Item> &values, + COPY_INFO &info, + bool copy_default_values, + MY_BITMAP *used_partitions); + /** + PRUNE_NO - Unable to prune. + PRUNE_DEFAULTS - Partitioning field is only set to + DEFAULT values, only need to check + pruning for one row where the DEFAULTS + values are set. + PRUNE_YES - Pruning is possible, calculate the used partition set + by evaluate the partition_id on row by row basis. + */ + enum enum_can_prune {PRUNE_NO=0, PRUNE_DEFAULTS, PRUNE_YES}; + bool can_prune_insert(THD *thd, + enum_duplicates duplic, + COPY_INFO &update, + List<Item> &update_fields, + List<Item> &fields, + bool empty_values, + enum_can_prune *can_prune_partitions, + bool *prune_needs_default_values, + MY_BITMAP *used_partitions); private: static int list_part_cmp(const void* a, const void* b); bool set_up_default_partitions(handler *file, HA_CREATE_INFO *info, @@ -305,7 +362,13 @@ private: bool set_up_default_subpartitions(handler *file, HA_CREATE_INFO *info); char *create_default_partition_names(uint part_no, uint num_parts, uint start_no); - char *create_subpartition_name(uint subpart_no, const char *part_name); + char *create_default_subpartition_name(uint subpart_no, + const char *part_name); + bool prune_partition_bitmaps(TABLE_LIST *table_list); + bool add_named_partition(const char *part_name, uint length); + bool is_field_in_part_expr(List<Item> &fields); + bool is_full_part_expr_in_fields(List<Item> &fields); +public: bool has_unique_name(partition_element *element); }; diff --git a/sql/protocol.cc b/sql/protocol.cc index bc3133881af..ee90cc46941 100644 --- a/sql/protocol.cc +++ b/sql/protocol.cc @@ -161,14 +161,14 @@ bool net_send_error(THD *thd, uint sql_errno, const char *err, It's one case when we can push an error even though there is an OK or EOF already. */ - thd->stmt_da->can_overwrite_status= TRUE; + thd->get_stmt_da()->set_overwrite_status(true); /* Abort multi-result sets */ thd->server_status&= ~SERVER_MORE_RESULTS_EXISTS; error= net_send_error_packet(thd, sql_errno, err, sqlstate); - thd->stmt_da->can_overwrite_status= FALSE; + thd->get_stmt_da()->set_overwrite_status(false); DBUG_RETURN(error); } @@ -242,7 +242,7 @@ net_send_ok(THD *thd, int2store(pos, server_status); pos+=2; } - thd->stmt_da->can_overwrite_status= TRUE; + thd->get_stmt_da()->set_overwrite_status(true); if (message && message[0]) pos= net_store_data(pos, (uchar*) message, strlen(message)); @@ -251,7 +251,7 @@ net_send_ok(THD *thd, error= net_flush(net); - thd->stmt_da->can_overwrite_status= FALSE; + thd->get_stmt_da()->set_overwrite_status(false); DBUG_PRINT("info", ("OK sent, so no more error sending allowed")); DBUG_RETURN(error); @@ -291,11 +291,11 @@ net_send_eof(THD *thd, uint server_status, uint statement_warn_count) /* Set to TRUE if no active vio, to work well in case of --init-file */ if (net->vio != 0) { - thd->stmt_da->can_overwrite_status= TRUE; + thd->get_stmt_da()->set_overwrite_status(true); error= write_eof_packet(thd, net, server_status, statement_warn_count); if (!error) error= net_flush(net); - thd->stmt_da->can_overwrite_status= FALSE; + thd->get_stmt_da()->set_overwrite_status(false); DBUG_PRINT("info", ("EOF sent, so no more error sending allowed")); } DBUG_RETURN(error); @@ -486,30 +486,30 @@ static uchar *net_store_length_fast(uchar *packet, uint length) void Protocol::end_statement() { DBUG_ENTER("Protocol::end_statement"); - DBUG_ASSERT(! thd->stmt_da->is_sent); + DBUG_ASSERT(! thd->get_stmt_da()->is_sent()); bool error= FALSE; /* Can not be true, but do not take chances in production. */ - if (thd->stmt_da->is_sent) + if (thd->get_stmt_da()->is_sent()) DBUG_VOID_RETURN; - switch (thd->stmt_da->status()) { + switch (thd->get_stmt_da()->status()) { case Diagnostics_area::DA_ERROR: /* The query failed, send error to log and abort bootstrap. */ - error= send_error(thd->stmt_da->sql_errno(), - thd->stmt_da->message(), - thd->stmt_da->get_sqlstate()); + error= send_error(thd->get_stmt_da()->sql_errno(), + thd->get_stmt_da()->message(), + thd->get_stmt_da()->get_sqlstate()); break; case Diagnostics_area::DA_EOF: error= send_eof(thd->server_status, - thd->stmt_da->statement_warn_count()); + thd->get_stmt_da()->statement_warn_count()); break; case Diagnostics_area::DA_OK: error= send_ok(thd->server_status, - thd->stmt_da->statement_warn_count(), - thd->stmt_da->affected_rows(), - thd->stmt_da->last_insert_id(), - thd->stmt_da->message()); + thd->get_stmt_da()->statement_warn_count(), + thd->get_stmt_da()->affected_rows(), + thd->get_stmt_da()->last_insert_id(), + thd->get_stmt_da()->message()); break; case Diagnostics_area::DA_DISABLED: break; @@ -520,7 +520,7 @@ void Protocol::end_statement() break; } if (!error) - thd->stmt_da->is_sent= TRUE; + thd->get_stmt_da()->set_is_sent(true); DBUG_VOID_RETURN; } @@ -688,9 +688,9 @@ bool Protocol::flush() { #ifndef EMBEDDED_LIBRARY bool error; - thd->stmt_da->can_overwrite_status= TRUE; + thd->get_stmt_da()->set_overwrite_status(true); error= net_flush(&thd->net); - thd->stmt_da->can_overwrite_status= FALSE; + thd->get_stmt_da()->set_overwrite_status(false); return error; #else return 0; @@ -856,7 +856,7 @@ bool Protocol::send_result_set_metadata(List<Item> *list, uint flags) Send no warning information, as it will be sent at statement end. */ if (write_eof_packet(thd, &thd->net, thd->server_status, - thd->warning_info->statement_warn_count())) + thd->get_stmt_da()->current_statement_warn_count())) DBUG_RETURN(1); } DBUG_RETURN(prepare_for_send(list->elements)); diff --git a/sql/rpl_mi.cc b/sql/rpl_mi.cc index c0393300fcf..ad058fa4933 100644 --- a/sql/rpl_mi.cc +++ b/sql/rpl_mi.cc @@ -818,7 +818,7 @@ bool Master_info_index::init_all_master_info() sql_print_error("Initialized Master_info from '%s' failed", buf_master_info_file); if (!master_info_index->get_master_info(&connection_name, - MYSQL_ERROR::WARN_LEVEL_NOTE)) + Sql_condition::WARN_LEVEL_NOTE)) { /* Master_info is not in HASH; Add it */ if (master_info_index->add_master_info(mi, FALSE)) @@ -843,7 +843,7 @@ bool Master_info_index::init_all_master_info() sql_print_information("Initialized Master_info from '%s'", buf_master_info_file); if (master_info_index->get_master_info(&connection_name, - MYSQL_ERROR::WARN_LEVEL_NOTE)) + Sql_condition::WARN_LEVEL_NOTE)) { /* Master_info was already registered */ sql_print_error(ER(ER_CONNECTION_ALREADY_EXISTS), @@ -940,7 +940,7 @@ bool Master_info_index::write_master_name_to_index_file(LEX_STRING *name, Master_info * Master_info_index::get_master_info(LEX_STRING *connection_name, - MYSQL_ERROR::enum_warning_level warning) + Sql_condition::enum_warning_level warning) { Master_info *mi; char buff[MAX_CONNECTION_NAME+1], *res; @@ -957,10 +957,10 @@ Master_info_index::get_master_info(LEX_STRING *connection_name, mi= (Master_info*) my_hash_search(&master_info_hash, (uchar*) buff, buff_length); - if (!mi && warning != MYSQL_ERROR::WARN_LEVEL_NOTE) + if (!mi && warning != Sql_condition::WARN_LEVEL_NOTE) { my_error(WARN_NO_MASTER_INFO, - MYF(warning == MYSQL_ERROR::WARN_LEVEL_WARN ? ME_JUST_WARNING : + MYF(warning == Sql_condition::WARN_LEVEL_WARN ? ME_JUST_WARNING : 0), (int) connection_name->length, connection_name->str); @@ -979,7 +979,7 @@ bool Master_info_index::check_duplicate_master_info(LEX_STRING *name_arg, /* Get full host and port name */ if ((mi= master_info_index->get_master_info(name_arg, - MYSQL_ERROR::WARN_LEVEL_NOTE))) + Sql_condition::WARN_LEVEL_NOTE))) { if (!host) host= mi->host; @@ -1043,7 +1043,7 @@ bool Master_info_index::remove_master_info(LEX_STRING *name) Master_info* mi; DBUG_ENTER("remove_master_info"); - if ((mi= get_master_info(name, MYSQL_ERROR::WARN_LEVEL_WARN))) + if ((mi= get_master_info(name, Sql_condition::WARN_LEVEL_WARN))) { // Delete Master_info and rewrite others to file if (!my_hash_delete(&master_info_hash, (uchar*) mi)) @@ -1155,7 +1155,7 @@ bool Master_info_index::start_all_slaves(THD *thd) break; } else - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_SLAVE_STARTED, ER(ER_SLAVE_STARTED), (int) mi->connection_name.length, mi->connection_name.str); @@ -1200,7 +1200,7 @@ bool Master_info_index::stop_all_slaves(THD *thd) break; } else - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_SLAVE_STOPPED, ER(ER_SLAVE_STOPPED), (int) mi->connection_name.length, mi->connection_name.str); diff --git a/sql/rpl_mi.h b/sql/rpl_mi.h index 108bd51ff47..09c7fce5b14 100644 --- a/sql/rpl_mi.h +++ b/sql/rpl_mi.h @@ -164,7 +164,7 @@ public: bool add_master_info(Master_info *mi, bool write_to_file); bool remove_master_info(LEX_STRING *connection_name); Master_info *get_master_info(LEX_STRING *connection_name, - MYSQL_ERROR::enum_warning_level warning); + Sql_condition::enum_warning_level warning); bool give_error_if_slave_running(); bool start_all_slaves(THD *thd); bool stop_all_slaves(THD *thd); diff --git a/sql/rpl_record.cc b/sql/rpl_record.cc index e219177cd80..aa8c118cfe6 100644 --- a/sql/rpl_record.cc +++ b/sql/rpl_record.cc @@ -287,7 +287,7 @@ unpack_row(Relay_log_info const *rli, else { f->set_default(); - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, ER_BAD_NULL_ERROR, ER(ER_BAD_NULL_ERROR), f->field_name); } @@ -447,7 +447,7 @@ int prepare_record(TABLE *const table, const uint skip, const bool check) { f->set_default(); push_warning_printf(current_thd, - MYSQL_ERROR::WARN_LEVEL_WARN, + Sql_condition::WARN_LEVEL_WARN, ER_NO_DEFAULT_FOR_FIELD, ER(ER_NO_DEFAULT_FOR_FIELD), f->field_name); diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc index 5a9e342e458..a11bdde9d19 100644 --- a/sql/rpl_rli.cc +++ b/sql/rpl_rli.cc @@ -247,7 +247,7 @@ a file name for --relay-log-index option", opt_relaylog_index_name); { sql_print_error("Failed to create a new relay log info file (\ file '%s', errno %d)", fname, my_errno); - msg= current_thd->stmt_da->message(); + msg= current_thd->get_stmt_da()->message(); goto err; } if (init_io_cache(&rli->info_file, info_fd, IO_SIZE*2, READ_CACHE, 0L,0, @@ -255,7 +255,7 @@ file '%s', errno %d)", fname, my_errno); { sql_print_error("Failed to create a cache on relay log info file '%s'", fname); - msg= current_thd->stmt_da->message(); + msg= current_thd->get_stmt_da()->message(); goto err; } @@ -1334,9 +1334,9 @@ void Relay_log_info::clear_tables_to_lock() void Relay_log_info::slave_close_thread_tables(THD *thd) { DBUG_ENTER("Relay_log_info::slave_close_thread_tables(THD *thd)"); - thd->stmt_da->can_overwrite_status= TRUE; + thd->get_stmt_da()->set_overwrite_status(true); thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd); - thd->stmt_da->can_overwrite_status= FALSE; + thd->get_stmt_da()->set_overwrite_status(false); close_thread_tables(thd); /* diff --git a/sql/set_var.cc b/sql/set_var.cc index 9f5404d75ce..c808e2dc11c 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -279,7 +279,7 @@ void sys_var::do_deprecated_warning(THD *thd) ? ER_WARN_DEPRECATED_SYNTAX_NO_REPLACEMENT : ER_WARN_DEPRECATED_SYNTAX; if (thd) - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_WARN_DEPRECATED_SYNTAX, ER(errmsg), buf1, deprecation_substitute); else @@ -316,7 +316,7 @@ bool throw_bounds_warning(THD *thd, const char *name, my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), name, buf); return true; } - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_TRUNCATED_WRONG_VALUE, ER(ER_TRUNCATED_WRONG_VALUE), name, buf); } @@ -336,7 +336,7 @@ bool throw_bounds_warning(THD *thd, const char *name, bool fixed, double v) my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), name, buf); return true; } - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_TRUNCATED_WRONG_VALUE, ER(ER_TRUNCATED_WRONG_VALUE), name, buf); } diff --git a/sql/slave.cc b/sql/slave.cc index a79514a0d8b..38f41685e6c 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -2650,7 +2650,7 @@ static int has_temporary_error(THD *thd) DBUG_ENTER("has_temporary_error"); DBUG_EXECUTE_IF("all_errors_are_temporary_errors", - if (thd->stmt_da->is_error()) + if (thd->get_stmt_da()->is_error()) { thd->clear_error(); my_error(ER_LOCK_DEADLOCK, MYF(0)); @@ -2669,16 +2669,16 @@ static int has_temporary_error(THD *thd) currently, InnoDB deadlock detected by InnoDB or lock wait timeout (innodb_lock_wait_timeout exceeded */ - if (thd->stmt_da->sql_errno() == ER_LOCK_DEADLOCK || - thd->stmt_da->sql_errno() == ER_LOCK_WAIT_TIMEOUT) + if (thd->get_stmt_da()->sql_errno() == ER_LOCK_DEADLOCK || + thd->get_stmt_da()->sql_errno() == ER_LOCK_WAIT_TIMEOUT) DBUG_RETURN(1); #ifdef HAVE_NDB_BINLOG /* currently temporary error set in ndbcluster */ - List_iterator_fast<MYSQL_ERROR> it(thd->warning_info->warn_list()); - MYSQL_ERROR *err; + List_iterator_fast<Sql_condition> it(thd->warning_info->warn_list()); + Sql_condition *err; while ((err= it++)) { DBUG_PRINT("info", ("has condition %d %s", err->get_sql_errno(), @@ -3738,9 +3738,9 @@ log '%s' at position %s, relay log '%s' position: %s", RPL_LOG_NAME, if (check_temp_dir(rli->slave_patternload_file)) { - rli->report(ERROR_LEVEL, thd->stmt_da->sql_errno(), + rli->report(ERROR_LEVEL, thd->get_stmt_da()->sql_errno(), "Unable to use slave's temporary directory %s - %s", - slave_load_tmpdir, thd->stmt_da->message()); + slave_load_tmpdir, thd->get_stmt_da()->message()); goto err; } @@ -3750,7 +3750,7 @@ log '%s' at position %s, relay log '%s' position: %s", RPL_LOG_NAME, execute_init_command(thd, &opt_init_slave, &LOCK_sys_init_slave); if (thd->is_slave_error) { - rli->report(ERROR_LEVEL, thd->stmt_da->sql_errno(), + rli->report(ERROR_LEVEL, thd->get_stmt_da()->sql_errno(), "Slave SQL thread aborted. Can't execute init_slave query"); goto err; } @@ -3817,20 +3817,20 @@ log '%s' at position %s, relay log '%s' position: %s", RPL_LOG_NAME, if (thd->is_error()) { - char const *const errmsg= thd->stmt_da->message(); + char const *const errmsg= thd->get_stmt_da()->message(); DBUG_PRINT("info", - ("thd->stmt_da->sql_errno()=%d; rli->last_error.number=%d", - thd->stmt_da->sql_errno(), last_errno)); + ("thd->get_stmt_da()->sql_errno()=%d; rli->last_error.number=%d", + thd->get_stmt_da()->sql_errno(), last_errno)); if (last_errno == 0) { /* This function is reporting an error which was not reported while executing exec_relay_log_event(). */ - rli->report(ERROR_LEVEL, thd->stmt_da->sql_errno(), "%s", errmsg); + rli->report(ERROR_LEVEL, thd->get_stmt_da()->sql_errno(), "%s", errmsg); } - else if (last_errno != thd->stmt_da->sql_errno()) + else if (last_errno != thd->get_stmt_da()->sql_errno()) { /* * An error was reported while executing exec_relay_log_event() @@ -3839,13 +3839,14 @@ log '%s' at position %s, relay log '%s' position: %s", RPL_LOG_NAME, * what caused the problem. */ sql_print_error("Slave (additional info): %s Error_code: %d", - errmsg, thd->stmt_da->sql_errno()); + errmsg, thd->get_stmt_da()->sql_errno()); } } /* Print any warnings issued */ - List_iterator_fast<MYSQL_ERROR> it(thd->warning_info->warn_list()); - MYSQL_ERROR *err; + Diagnostics_area::Sql_condition_iterator it= + thd->get_stmt_da()->sql_conditions(); + const Sql_condition *err; /* Added controlled slave thread cancel for replication of user-defined variables. diff --git a/sql/sp.cc b/sql/sp.cc index 9cc68339d6f..32998e42df9 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -325,7 +325,7 @@ Stored_routine_creation_ctx::load_from_db(THD *thd, if (invalid_creation_ctx) { push_warning_printf(thd, - MYSQL_ERROR::WARN_LEVEL_WARN, + Sql_condition::WARN_LEVEL_WARN, ER_SR_INVALID_CREATION_CTX, ER(ER_SR_INVALID_CREATION_CTX), (const char *) db_name, @@ -683,9 +683,9 @@ public: virtual bool handle_condition(THD *thd, uint sql_errno, const char* sqlstate, - MYSQL_ERROR::enum_warning_level level, + Sql_condition::enum_warning_level level, const char* msg, - MYSQL_ERROR ** cond_hdl); + Sql_condition ** cond_hdl); }; bool @@ -693,13 +693,13 @@ Silence_deprecated_warning::handle_condition( THD *, uint sql_errno, const char*, - MYSQL_ERROR::enum_warning_level level, + Sql_condition::enum_warning_level level, const char*, - MYSQL_ERROR ** cond_hdl) + Sql_condition ** cond_hdl) { *cond_hdl= NULL; if (sql_errno == ER_WARN_DEPRECATED_SYNTAX && - level == MYSQL_ERROR::WARN_LEVEL_WARN) + level == Sql_condition::WARN_LEVEL_WARN) return TRUE; return FALSE; @@ -772,9 +772,9 @@ public: virtual bool handle_condition(THD *thd, uint sql_errno, const char* sqlstate, - MYSQL_ERROR::enum_warning_level level, + Sql_condition::enum_warning_level level, const char* message, - MYSQL_ERROR ** cond_hdl); + Sql_condition ** cond_hdl); bool error_caught() const { return m_error_caught; } @@ -786,9 +786,9 @@ bool Bad_db_error_handler::handle_condition(THD *thd, uint sql_errno, const char* sqlstate, - MYSQL_ERROR::enum_warning_level level, + Sql_condition::enum_warning_level level, const char* message, - MYSQL_ERROR ** cond_hdl) + Sql_condition ** cond_hdl) { if (sql_errno == ER_BAD_DB_ERROR) { @@ -1390,9 +1390,9 @@ public: bool handle_condition(THD *thd, uint sql_errno, const char* sqlstate, - MYSQL_ERROR::enum_warning_level level, + Sql_condition::enum_warning_level level, const char* msg, - MYSQL_ERROR ** cond_hdl) + Sql_condition ** cond_hdl) { if (sql_errno == ER_NO_SUCH_TABLE || sql_errno == ER_NO_SUCH_TABLE_IN_ENGINE || @@ -1757,7 +1757,7 @@ sp_exist_routines(THD *thd, TABLE_LIST *routines, bool any) &thd->sp_proc_cache, FALSE) != NULL || sp_find_routine(thd, TYPE_ENUM_FUNCTION, name, &thd->sp_func_cache, FALSE) != NULL; - thd->warning_info->clear_warning_info(thd->query_id); + thd->get_stmt_da()->clear_warning_info(thd->query_id); if (sp_object_found) { if (any) diff --git a/sql/sp_head.cc b/sql/sp_head.cc index e2901125f14..e54bd796bbb 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -1148,19 +1148,20 @@ find_handler_after_execution(THD *thd, sp_rcontext *ctx) if (thd->is_error()) { ctx->find_handler(thd, - thd->stmt_da->sql_errno(), - thd->stmt_da->get_sqlstate(), - MYSQL_ERROR::WARN_LEVEL_ERROR, - thd->stmt_da->message()); + thd->get_stmt_da()->sql_errno(), + thd->get_stmt_da()->get_sqlstate(), + Sql_condition::WARN_LEVEL_ERROR, + thd->get_stmt_da()->message()); } - else if (thd->warning_info->statement_warn_count()) + else if (thd->get_stmt_da()->statement_warn_count()) { - List_iterator<MYSQL_ERROR> it(thd->warning_info->warn_list()); - MYSQL_ERROR *err; + Diagnostics_area::Sql_condition_iterator it= + thd->get_stmt_da()->sql_conditions(); + const Sql_condition *err; while ((err= it++)) { - if (err->get_level() != MYSQL_ERROR::WARN_LEVEL_WARN && - err->get_level() != MYSQL_ERROR::WARN_LEVEL_NOTE) + if (err->get_level() != Sql_condition::WARN_LEVEL_WARN && + err->get_level() != Sql_condition::WARN_LEVEL_NOTE) continue; if (ctx->find_handler(thd, @@ -1221,8 +1222,8 @@ sp_head::execute(THD *thd, bool merge_da_on_success) String old_packet; Reprepare_observer *save_reprepare_observer= thd->m_reprepare_observer; Object_creation_ctx *saved_creation_ctx; - Warning_info *saved_warning_info; - Warning_info warning_info(thd->warning_info->warn_id(), false); + Diagnostics_area *da= thd->get_stmt_da(); + Warning_info sp_wi(da->warning_info_id(), false, true); /* Just reporting a stack overrun error @@ -1293,9 +1294,8 @@ sp_head::execute(THD *thd, bool merge_da_on_success) old_arena= thd->stmt_arena; /* Push a new warning information area. */ - warning_info.append_warning_info(thd, thd->warning_info); - saved_warning_info= thd->warning_info; - thd->warning_info= &warning_info; + da->copy_sql_conditions_to_wi(thd, &sp_wi); + da->push_warning_info(&sp_wi); /* Switch query context. This has to be done early as this is sometimes @@ -1395,7 +1395,7 @@ sp_head::execute(THD *thd, bool merge_da_on_success) } /* Reset number of warnings for this query. */ - thd->warning_info->reset_for_next_command(); + thd->get_stmt_da()->reset_for_next_command(); DBUG_PRINT("execute", ("Instruction %u", ip)); @@ -1502,9 +1502,40 @@ sp_head::execute(THD *thd, bool merge_da_on_success) - if there was an exception during execution, warning info should be propagated to the caller in any case. */ + da->pop_warning_info(); + if (err_status || merge_da_on_success) - saved_warning_info->merge_with_routine_info(thd, thd->warning_info); - thd->warning_info= saved_warning_info; + { + /* + If a routine body is empty or if a routine did not generate any warnings, + do not duplicate our own contents by appending the contents of the called + routine. We know that the called routine did not change its warning info. + + On the other hand, if the routine body is not empty and some statement in + the routine generates a warning or uses tables, warning info is guaranteed + to have changed. In this case we know that the routine warning info + contains only new warnings, and thus we perform a copy. + */ + if (da->warning_info_changed(&sp_wi)) + { + /* + If the invocation of the routine was a standalone statement, + rather than a sub-statement, in other words, if it's a CALL + of a procedure, rather than invocation of a function or a + trigger, we need to clear the current contents of the caller's + warning info. + + This is per MySQL rules: if a statement generates a warning, + warnings from the previous statement are flushed. Normally + it's done in push_warning(). However, here we don't use + push_warning() to avoid invocation of condition handlers or + escalation of warnings to errors. + */ + da->opt_clear_warning_info(thd->query_id); + da->copy_sql_conditions_from_wi(thd, &sp_wi); + da->remove_marked_sql_conditions(); + } + } done: DBUG_PRINT("info", ("err_status: %d killed: %d is_slave_error: %d report_error: %d", @@ -1958,7 +1989,7 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, if (mysql_bin_log.write(&qinfo) && thd->binlog_evt_union.unioned_events_trans) { - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR, + push_warning(thd, Sql_condition::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR, "Invoked ROUTINE modified a transactional table but MySQL " "failed to reflect this change in the binary log"); err_status= TRUE; @@ -2137,9 +2168,9 @@ sp_head::execute_procedure(THD *thd, List<Item> *args) if (!thd->in_sub_stmt) { - thd->stmt_da->can_overwrite_status= TRUE; + thd->get_stmt_da()->set_overwrite_status(true); thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd); - thd->stmt_da->can_overwrite_status= FALSE; + thd->get_stmt_da()->set_overwrite_status(false); } thd_proc_info(thd, "closing tables"); @@ -2884,7 +2915,7 @@ sp_head::show_routine_code(THD *thd) Since this is for debugging purposes only, we don't bother to introduce a special error code for it. */ - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR, tmp); + push_warning(thd, Sql_condition::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR, tmp); } protocol->prepare_for_resend(); protocol->store((longlong)ip); @@ -2991,9 +3022,9 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp, /* Here we also commit or rollback the current statement. */ if (! thd->in_sub_stmt) { - thd->stmt_da->can_overwrite_status= TRUE; + thd->get_stmt_da()->set_overwrite_status(true); thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd); - thd->stmt_da->can_overwrite_status= FALSE; + thd->get_stmt_da()->set_overwrite_status(false); } thd_proc_info(thd, "closing tables"); close_thread_tables(thd); @@ -3027,10 +3058,10 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp, open_tables stage. */ if (!res || !thd->is_error() || - (thd->stmt_da->sql_errno() != ER_CANT_REOPEN_TABLE && - thd->stmt_da->sql_errno() != ER_NO_SUCH_TABLE && - thd->stmt_da->sql_errno() != ER_NO_SUCH_TABLE_IN_ENGINE && - thd->stmt_da->sql_errno() != ER_UPDATE_TABLE_USED)) + (thd->get_stmt_da()->sql_errno() != ER_CANT_REOPEN_TABLE && + thd->get_stmt_da()->sql_errno() != ER_NO_SUCH_TABLE && + thd->get_stmt_da()->sql_errno() != ER_NO_SUCH_TABLE_IN_ENGINE && + thd->get_stmt_da()->sql_errno() != ER_UPDATE_TABLE_USED)) thd->stmt_arena->state= Query_arena::STMT_EXECUTED; /* @@ -3117,7 +3148,7 @@ sp_instr_stmt::execute(THD *thd, uint *nextp) { res= m_lex_keeper.reset_lex_and_exec_core(thd, nextp, FALSE, this); - if (thd->stmt_da->is_eof()) + if (thd->get_stmt_da()->is_eof()) { /* Finalize server status flags after executing a statement. */ thd->update_server_status(); @@ -3136,7 +3167,7 @@ sp_instr_stmt::execute(THD *thd, uint *nextp) thd->query_name_consts= 0; if (!thd->is_error()) - thd->stmt_da->reset_diagnostics_area(); + thd->get_stmt_da()->reset_diagnostics_area(); } DBUG_RETURN(res || thd->is_error()); } diff --git a/sql/sp_head.h b/sql/sp_head.h index 409db33ef02..f5254c2ead2 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -274,6 +274,15 @@ public: */ Security_context m_security_ctx; + /** + List of all items (Item_trigger_field objects) representing fields in + old/new version of row in trigger. We use this list for checking whenever + all such fields are valid at trigger creation time and for binding these + fields to TABLE object at table open (although for latter pointer to table + being opened is probably enough). + */ + SQL_I_List<Item_trigger_field> m_trg_table_fields; + static void * operator new(size_t size) throw (); diff --git a/sql/sp_rcontext.cc b/sql/sp_rcontext.cc index 30acfebabb2..fc6a67fb496 100644 --- a/sql/sp_rcontext.cc +++ b/sql/sp_rcontext.cc @@ -204,7 +204,7 @@ bool sp_rcontext::find_handler(THD *thd, uint sql_errno, const char *sqlstate, - MYSQL_ERROR::enum_warning_level level, + Sql_condition::enum_warning_level level, const char *msg) { int i= m_hcount; @@ -248,7 +248,7 @@ sp_rcontext::find_handler(THD *thd, break; case sp_cond_type_t::warning: if ((IS_WARNING_CONDITION(sqlstate) || - level == MYSQL_ERROR::WARN_LEVEL_WARN) && + level == Sql_condition::WARN_LEVEL_WARN) && m_hfound < 0) m_hfound= i; break; @@ -258,7 +258,7 @@ sp_rcontext::find_handler(THD *thd, break; case sp_cond_type_t::exception: if (IS_EXCEPTION_CONDITION(sqlstate) && - level == MYSQL_ERROR::WARN_LEVEL_ERROR && + level == Sql_condition::WARN_LEVEL_ERROR && m_hfound < 0) m_hfound= i; break; @@ -281,7 +281,7 @@ sp_rcontext::find_handler(THD *thd, (warning or "not found") we will simply resume execution. */ if (m_prev_runtime_ctx && IS_EXCEPTION_CONDITION(sqlstate) && - level == MYSQL_ERROR::WARN_LEVEL_ERROR) + level == Sql_condition::WARN_LEVEL_ERROR) { return m_prev_runtime_ctx->find_handler(thd, sql_errno, sqlstate, level, msg); @@ -575,7 +575,7 @@ sp_cursor::fetch(THD *thd, List<struct sp_variable> *vars) } DBUG_EXECUTE_IF("bug23032_emit_warning", - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning(thd, Sql_condition::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR, ER(ER_UNKNOWN_ERROR));); diff --git a/sql/sp_rcontext.h b/sql/sp_rcontext.h index 5008a73d96c..dd1d625a46c 100644 --- a/sql/sp_rcontext.h +++ b/sql/sp_rcontext.h @@ -66,7 +66,7 @@ public: uint m_sql_errno; /** Error level. */ - MYSQL_ERROR::enum_warning_level m_level; + Sql_condition::enum_warning_level m_level; /** SQLSTATE. */ char m_sql_state[SQLSTATE_LENGTH + 1]; @@ -75,7 +75,7 @@ public: char m_message[MYSQL_ERRMSG_SIZE]; void set(uint sql_errno, const char* sqlstate, - MYSQL_ERROR::enum_warning_level level, + Sql_condition::enum_warning_level level, const char* msg) { m_sql_errno= sql_errno; @@ -90,7 +90,7 @@ public: void clear() { m_sql_errno= 0; - m_level= MYSQL_ERROR::WARN_LEVEL_ERROR; + m_level= Sql_condition::WARN_LEVEL_ERROR; m_sql_state[0]= '\0'; m_message[0]= '\0'; @@ -183,7 +183,7 @@ class sp_rcontext : public Sql_alloc find_handler(THD *thd, uint sql_errno, const char *sqlstate, - MYSQL_ERROR::enum_warning_level level, + Sql_condition::enum_warning_level level, const char *msg); Sql_condition_info *raised_condition() const; diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 9f085133059..a33e087fdf4 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -1207,9 +1207,9 @@ my_bool acl_reload(THD *thd) Execution might have been interrupted; only print the error message if an error condition has been raised. */ - if (thd->stmt_da->is_error()) + if (thd->get_stmt_da()->is_error()) sql_print_error("Fatal error: Can't open and lock privilege tables: %s", - thd->stmt_da->message()); + thd->get_stmt_da()->message()); goto end; } @@ -1968,7 +1968,7 @@ bool change_password(THD *thd, const char *host, const char *user, set_user_plugin(acl_user, new_password_len); } else - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + push_warning(thd, Sql_condition::WARN_LEVEL_NOTE, ER_SET_PASSWORD_AUTH_PLUGIN, ER(ER_SET_PASSWORD_AUTH_PLUGIN)); if (update_user_table(thd, table, @@ -6857,9 +6857,9 @@ public: virtual bool handle_condition(THD *thd, uint sql_errno, const char* sqlstate, - MYSQL_ERROR::enum_warning_level level, + Sql_condition::enum_warning_level level, const char* msg, - MYSQL_ERROR ** cond_hdl); + Sql_condition ** cond_hdl); bool has_errors() { return is_grave; } @@ -6872,18 +6872,18 @@ Silence_routine_definer_errors::handle_condition( THD *thd, uint sql_errno, const char*, - MYSQL_ERROR::enum_warning_level level, + Sql_condition::enum_warning_level level, const char* msg, - MYSQL_ERROR ** cond_hdl) + Sql_condition ** cond_hdl) { *cond_hdl= NULL; - if (level == MYSQL_ERROR::WARN_LEVEL_ERROR) + if (level == Sql_condition::WARN_LEVEL_ERROR) { switch (sql_errno) { case ER_NONEXISTING_PROC_GRANT: /* Convert the error into a warning. */ - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning(thd, Sql_condition::WARN_LEVEL_WARN, sql_errno, msg); return TRUE; default: @@ -7050,7 +7050,7 @@ bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name, } else { - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_PASSWD_LENGTH, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_PASSWD_LENGTH, ER(ER_PASSWD_LENGTH), SCRAMBLED_PASSWORD_CHAR_LENGTH); return TRUE; } @@ -9156,15 +9156,14 @@ bool acl_authenticate(THD *thd, uint connect_errors, sctx->external_user= my_strdup(mpvio.auth_info.external_user, MYF(0)); if (res == CR_OK_HANDSHAKE_COMPLETE) - thd->stmt_da->disable_status(); + thd->get_stmt_da()->disable_status(); else my_ok(thd); #ifdef HAVE_PSI_THREAD_INTERFACE - PSI_CALL(set_thread_user_host)(thd->main_security_ctx.user, - strlen(thd->main_security_ctx.user), - thd->main_security_ctx.host_or_ip, - strlen(thd->main_security_ctx.host_or_ip)); + PSI_THREAD_CALL(set_thread_user_host) + (thd->main_security_ctx.user, strlen(thd->main_security_ctx.user), + thd->main_security_ctx.host_or_ip, strlen(thd->main_security_ctx.host_or_ip)); #endif /* Ready to handle queries */ diff --git a/sql/sql_admin.cc b/sql/sql_admin.cc index e6bbef482a7..4e5a3873df9 100644 --- a/sql/sql_admin.cc +++ b/sql/sql_admin.cc @@ -52,8 +52,8 @@ static bool admin_recreate_table(THD *thd, TABLE_LIST *table_list) we will store the error message in a result set row and then clear. */ - if (thd->stmt_da->is_ok()) - thd->stmt_da->reset_diagnostics_area(); + if (thd->get_stmt_da()->is_ok()) + thd->get_stmt_da()->reset_diagnostics_area(); table_list->table= NULL; result_code= result_code ? HA_ADMIN_FAILED : HA_ADMIN_OK; DBUG_RETURN(result_code); @@ -98,7 +98,7 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list, if (!(table= table_list->table)) { - char key[MAX_DBKEY_LENGTH]; + const char *key; uint key_length; /* If the table didn't exist, we have a shared metadata lock @@ -114,7 +114,8 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list, */ my_hash_value_type hash_value; - key_length= create_table_def_key(thd, key, table_list, 0); + key_length= get_table_def_key(table_list, &key); + table_list->mdl_request.init(MDL_key::TABLE, table_list->db, table_list->table_name, MDL_EXCLUSIVE, MDL_TRANSACTION); @@ -203,7 +204,7 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list, goto end; /* Close table but don't remove from locked list */ close_all_tables_for_name(thd, table_list->table->s, - HA_EXTRA_NOT_USED); + HA_EXTRA_NOT_USED, NULL); table_list->table= 0; } /* @@ -398,14 +399,14 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, because it's already known that the table is badly damaged. */ - Warning_info wi(thd->query_id, false); - Warning_info *wi_saved= thd->warning_info; + Diagnostics_area *da= thd->get_stmt_da(); + Warning_info tmp_wi(thd->query_id, false, true); - thd->warning_info= &wi; + da->push_warning_info(&tmp_wi); open_error= open_and_lock_tables(thd, table, TRUE, 0); - thd->warning_info= wi_saved; + da->pop_warning_info(&tmp_wi); } else { @@ -452,7 +453,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, */ Alter_info *alter_info= &lex->alter_info; - if (alter_info->flags & ALTER_ADMIN_PARTITION) + if (alter_info->flags & Alter_info::ALTER_ADMIN_PARTITION) { if (!table->table->part_info) { @@ -516,16 +517,16 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, if (!table->table) { DBUG_PRINT("admin", ("open table failed")); - if (thd->warning_info->is_empty()) - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + if (thd->get_stmt_da()->is_warning_info_empty()) + push_warning(thd, Sql_condition::WARN_LEVEL_WARN, ER_CHECK_NO_SUCH_TABLE, ER(ER_CHECK_NO_SUCH_TABLE)); /* if it was a view will check md5 sum */ if (table->view && view_checksum(thd, table) == HA_ADMIN_WRONG_CHECKSUM) - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning(thd, Sql_condition::WARN_LEVEL_WARN, ER_VIEW_CHECKSUM, ER(ER_VIEW_CHECKSUM)); - if (thd->stmt_da->is_error() && - table_not_corrupt_error(thd->stmt_da->sql_errno())) + if (thd->get_stmt_da()->is_error() && + table_not_corrupt_error(thd->get_stmt_da()->sql_errno())) result_code= HA_ADMIN_FAILED; else /* Default failure code is corrupt table */ @@ -573,7 +574,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, table->table=0; // For query cache if (protocol->write()) goto err; - thd->stmt_da->reset_diagnostics_area(); + thd->get_stmt_da()->reset_diagnostics_area(); continue; /* purecov: end */ } @@ -744,8 +745,9 @@ send_result: lex->cleanup_after_one_table_open(); thd->clear_error(); // these errors shouldn't get client { - List_iterator_fast<MYSQL_ERROR> it(thd->warning_info->warn_list()); - MYSQL_ERROR *err; + Diagnostics_area::Sql_condition_iterator it= + thd->get_stmt_da()->sql_conditions(); + const Sql_condition *err; while ((err= it++)) { protocol->prepare_for_resend(); @@ -758,7 +760,7 @@ send_result: if (protocol->write()) goto err; } - thd->warning_info->clear_warning_info(thd->query_id); + thd->get_stmt_da()->clear_warning_info(thd->query_id); } protocol->prepare_for_resend(); protocol->store(table_name, system_charset_info); @@ -875,7 +877,7 @@ send_result_message: DBUG_ASSERT(thd->is_error() || thd->killed); if (thd->is_error()) { - const char *err_msg= thd->stmt_da->message(); + const char *err_msg= thd->get_stmt_da()->message(); if (!thd->vio_ok()) { sql_print_error("%s", err_msg); @@ -1072,12 +1074,13 @@ bool mysql_preload_keys(THD* thd, TABLE_LIST* tables) } -bool Analyze_table_statement::execute(THD *thd) +bool Sql_cmd_analyze_table::execute(THD *thd) { + LEX *m_lex= thd->lex; TABLE_LIST *first_table= m_lex->select_lex.table_list.first; bool res= TRUE; thr_lock_type lock_type = TL_READ_NO_INSERT; - DBUG_ENTER("Analyze_table_statement::execute"); + DBUG_ENTER("Sql_cmd_analyze_table::execute"); if (check_table_access(thd, SELECT_ACL | INSERT_ACL, first_table, FALSE, UINT_MAX, FALSE)) @@ -1102,12 +1105,13 @@ error: } -bool Check_table_statement::execute(THD *thd) +bool Sql_cmd_check_table::execute(THD *thd) { + LEX *m_lex= thd->lex; TABLE_LIST *first_table= m_lex->select_lex.table_list.first; thr_lock_type lock_type = TL_READ_NO_INSERT; bool res= TRUE; - DBUG_ENTER("Check_table_statement::execute"); + DBUG_ENTER("Sql_cmd_check_table::execute"); if (check_table_access(thd, SELECT_ACL, first_table, TRUE, UINT_MAX, FALSE)) @@ -1126,17 +1130,18 @@ error: } -bool Optimize_table_statement::execute(THD *thd) +bool Sql_cmd_optimize_table::execute(THD *thd) { + LEX *m_lex= thd->lex; TABLE_LIST *first_table= m_lex->select_lex.table_list.first; bool res= TRUE; - DBUG_ENTER("Optimize_table_statement::execute"); + DBUG_ENTER("Sql_cmd_optimize_table::execute"); if (check_table_access(thd, SELECT_ACL | INSERT_ACL, first_table, FALSE, UINT_MAX, FALSE)) goto error; /* purecov: inspected */ thd->enable_slow_log= opt_log_slow_admin_statements; - res= (specialflag & (SPECIAL_SAFE_MODE | SPECIAL_NO_NEW_FUNC)) ? + res= (specialflag & SPECIAL_NO_NEW_FUNC) ? mysql_recreate_table(thd, first_table) : mysql_admin_table(thd, first_table, &m_lex->check_opt, "optimize", TL_WRITE, 1, 0, 0, 0, @@ -1157,11 +1162,12 @@ error: } -bool Repair_table_statement::execute(THD *thd) +bool Sql_cmd_repair_table::execute(THD *thd) { + LEX *m_lex= thd->lex; TABLE_LIST *first_table= m_lex->select_lex.table_list.first; bool res= TRUE; - DBUG_ENTER("Repair_table_statement::execute"); + DBUG_ENTER("Sql_cmd_repair_table::execute"); if (check_table_access(thd, SELECT_ACL | INSERT_ACL, first_table, FALSE, UINT_MAX, FALSE)) diff --git a/sql/sql_admin.h b/sql/sql_admin.h index f7ec76efd5e..1f884866ddc 100644 --- a/sql/sql_admin.h +++ b/sql/sql_admin.h @@ -24,109 +24,100 @@ int reassign_keycache_tables(THD* thd, KEY_CACHE *src_cache, KEY_CACHE *dst_cache); /** - Analyze_statement represents the ANALYZE TABLE statement. + Sql_cmd_analyze_table represents the ANALYZE TABLE statement. */ -class Analyze_table_statement : public Sql_statement +class Sql_cmd_analyze_table : public Sql_cmd { public: /** Constructor, used to represent a ANALYZE TABLE statement. - @param lex the LEX structure for this statement. */ - Analyze_table_statement(LEX *lex) - : Sql_statement(lex) + Sql_cmd_analyze_table() {} - ~Analyze_table_statement() + ~Sql_cmd_analyze_table() {} - /** - Execute a ANALYZE TABLE statement at runtime. - @param thd the current thread. - @return false on success. - */ bool execute(THD *thd); + + virtual enum_sql_command sql_command_code() const + { + return SQLCOM_ANALYZE; + } }; /** - Check_table_statement represents the CHECK TABLE statement. + Sql_cmd_check_table represents the CHECK TABLE statement. */ -class Check_table_statement : public Sql_statement +class Sql_cmd_check_table : public Sql_cmd { public: /** Constructor, used to represent a CHECK TABLE statement. - @param lex the LEX structure for this statement. */ - Check_table_statement(LEX *lex) - : Sql_statement(lex) + Sql_cmd_check_table() {} - ~Check_table_statement() + ~Sql_cmd_check_table() {} - /** - Execute a CHECK TABLE statement at runtime. - @param thd the current thread. - @return false on success. - */ bool execute(THD *thd); -}; + virtual enum_sql_command sql_command_code() const + { + return SQLCOM_CHECK; + } +}; /** - Optimize_table_statement represents the OPTIMIZE TABLE statement. + Sql_cmd_optimize_table represents the OPTIMIZE TABLE statement. */ -class Optimize_table_statement : public Sql_statement +class Sql_cmd_optimize_table : public Sql_cmd { public: /** Constructor, used to represent a OPTIMIZE TABLE statement. - @param lex the LEX structure for this statement. */ - Optimize_table_statement(LEX *lex) - : Sql_statement(lex) + Sql_cmd_optimize_table() {} - ~Optimize_table_statement() + ~Sql_cmd_optimize_table() {} - /** - Execute a OPTIMIZE TABLE statement at runtime. - @param thd the current thread. - @return false on success. - */ bool execute(THD *thd); + + virtual enum_sql_command sql_command_code() const + { + return SQLCOM_OPTIMIZE; + } }; /** - Repair_table_statement represents the REPAIR TABLE statement. + Sql_cmd_repair_table represents the REPAIR TABLE statement. */ -class Repair_table_statement : public Sql_statement +class Sql_cmd_repair_table : public Sql_cmd { public: /** Constructor, used to represent a REPAIR TABLE statement. - @param lex the LEX structure for this statement. */ - Repair_table_statement(LEX *lex) - : Sql_statement(lex) + Sql_cmd_repair_table() {} - ~Repair_table_statement() + ~Sql_cmd_repair_table() {} - /** - Execute a REPAIR TABLE statement at runtime. - @param thd the current thread. - @return false on success. - */ bool execute(THD *thd); + + virtual enum_sql_command sql_command_code() const + { + return SQLCOM_REPAIR; + } }; #endif diff --git a/sql/sql_alter.cc b/sql/sql_alter.cc index c6c02773286..01bffaf132f 100644 --- a/sql/sql_alter.cc +++ b/sql/sql_alter.cc @@ -16,9 +16,176 @@ #include "sql_parse.h" // check_access #include "sql_table.h" // mysql_alter_table, // mysql_exchange_partition +#include "sql_base.h" // open_temporary_tables #include "sql_alter.h" -bool Alter_table_statement::execute(THD *thd) +Alter_info::Alter_info(const Alter_info &rhs, MEM_ROOT *mem_root) + :drop_list(rhs.drop_list, mem_root), + alter_list(rhs.alter_list, mem_root), + key_list(rhs.key_list, mem_root), + create_list(rhs.create_list, mem_root), + flags(rhs.flags), + keys_onoff(rhs.keys_onoff), + partition_names(rhs.partition_names, mem_root), + num_parts(rhs.num_parts), + requested_algorithm(rhs.requested_algorithm), + requested_lock(rhs.requested_lock) +{ + /* + Make deep copies of used objects. + This is not a fully deep copy - clone() implementations + of Alter_drop, Alter_column, Key, foreign_key, Key_part_spec + do not copy string constants. At the same length the only + reason we make a copy currently is that ALTER/CREATE TABLE + code changes input Alter_info definitions, but string + constants never change. + */ + list_copy_and_replace_each_value(drop_list, mem_root); + list_copy_and_replace_each_value(alter_list, mem_root); + list_copy_and_replace_each_value(key_list, mem_root); + list_copy_and_replace_each_value(create_list, mem_root); + /* partition_names are not deeply copied currently */ +} + + +bool Alter_info::set_requested_algorithm(const LEX_STRING *str) +{ + // To avoid adding new keywords to the grammar, we match strings here. + if (!my_strcasecmp(system_charset_info, str->str, "INPLACE")) + requested_algorithm= ALTER_TABLE_ALGORITHM_INPLACE; + else if (!my_strcasecmp(system_charset_info, str->str, "COPY")) + requested_algorithm= ALTER_TABLE_ALGORITHM_COPY; + else if (!my_strcasecmp(system_charset_info, str->str, "DEFAULT")) + requested_algorithm= ALTER_TABLE_ALGORITHM_DEFAULT; + else + return true; + return false; +} + + +bool Alter_info::set_requested_lock(const LEX_STRING *str) +{ + // To avoid adding new keywords to the grammar, we match strings here. + if (!my_strcasecmp(system_charset_info, str->str, "NONE")) + requested_lock= ALTER_TABLE_LOCK_NONE; + else if (!my_strcasecmp(system_charset_info, str->str, "SHARED")) + requested_lock= ALTER_TABLE_LOCK_SHARED; + else if (!my_strcasecmp(system_charset_info, str->str, "EXCLUSIVE")) + requested_lock= ALTER_TABLE_LOCK_EXCLUSIVE; + else if (!my_strcasecmp(system_charset_info, str->str, "DEFAULT")) + requested_lock= ALTER_TABLE_LOCK_DEFAULT; + else + return true; + return false; +} + + +Alter_table_ctx::Alter_table_ctx() + : datetime_field(NULL), error_if_not_empty(false), + tables_opened(0), + db(NULL), table_name(NULL), alias(NULL), + new_db(NULL), new_name(NULL), new_alias(NULL), + fk_error_if_delete_row(false), fk_error_id(NULL), + fk_error_table(NULL) +#ifndef DBUG_OFF + , tmp_table(false) +#endif +{ +} + + +Alter_table_ctx::Alter_table_ctx(THD *thd, TABLE_LIST *table_list, + uint tables_opened_arg, + char *new_db_arg, char *new_name_arg) + : datetime_field(NULL), error_if_not_empty(false), + tables_opened(tables_opened_arg), + new_db(new_db_arg), new_name(new_name_arg), + fk_error_if_delete_row(false), fk_error_id(NULL), + fk_error_table(NULL) +#ifndef DBUG_OFF + , tmp_table(false) +#endif +{ + /* + Assign members db, table_name, new_db and new_name + to simplify further comparisions: we want to see if it's a RENAME + later just by comparing the pointers, avoiding the need for strcmp. + */ + db= table_list->db; + table_name= table_list->table_name; + alias= (lower_case_table_names == 2) ? table_list->alias : table_name; + + if (!new_db || !my_strcasecmp(table_alias_charset, new_db, db)) + new_db= db; + + if (new_name) + { + DBUG_PRINT("info", ("new_db.new_name: '%s'.'%s'", new_db, new_name)); + + if (lower_case_table_names == 1) // Convert new_name/new_alias to lower case + { + my_casedn_str(files_charset_info, new_name); + new_alias= new_name; + } + else if (lower_case_table_names == 2) // Convert new_name to lower case + { + strmov(new_alias= new_alias_buff, new_name); + my_casedn_str(files_charset_info, new_name); + } + else + new_alias= new_name; // LCTN=0 => case sensitive + case preserving + + if (!is_database_changed() && + !my_strcasecmp(table_alias_charset, new_name, table_name)) + { + /* + Source and destination table names are equal: + make is_table_renamed() more efficient. + */ + new_alias= table_name; + new_name= table_name; + } + } + else + { + new_alias= alias; + new_name= table_name; + } + + my_snprintf(tmp_name, sizeof(tmp_name), "%s-%lx_%lx", tmp_file_prefix, + current_pid, thd->thread_id); + /* Safety fix for InnoDB */ + if (lower_case_table_names) + my_casedn_str(files_charset_info, tmp_name); + + if (table_list->table->s->tmp_table == NO_TMP_TABLE) + { + build_table_filename(path, sizeof(path) - 1, db, table_name, "", 0); + + build_table_filename(new_path, sizeof(new_path) - 1, new_db, new_name, "", 0); + + build_table_filename(new_filename, sizeof(new_filename) - 1, + new_db, new_name, reg_ext, 0); + + build_table_filename(tmp_path, sizeof(tmp_path) - 1, new_db, tmp_name, "", + FN_IS_TMP); + } + else + { + /* + We are not filling path, new_path and new_filename members if + we are altering temporary table as these members are not used in + this case. This fact is enforced with assert. + */ + build_tmptable_filename(thd, tmp_path, sizeof(tmp_path)); +#ifndef DBUG_OFF + tmp_table= true; +#endif + } +} + + +bool Sql_cmd_alter_table::execute(THD *thd) { LEX *lex= thd->lex; /* first SELECT_LEX (have special meaning for many of non-SELECTcommands) */ @@ -38,7 +205,7 @@ bool Alter_table_statement::execute(THD *thd) ulong priv_needed= ALTER_ACL; bool result; - DBUG_ENTER("Alter_table_statement::execute"); + DBUG_ENTER("Sql_cmd_alter_table::execute"); if (thd->is_fatal_error) /* out of memory creating a copy of alter_info */ DBUG_RETURN(TRUE); @@ -46,12 +213,14 @@ bool Alter_table_statement::execute(THD *thd) We also require DROP priv for ALTER TABLE ... DROP PARTITION, as well as for RENAME TO, as being done by SQLCOM_RENAME_TABLE */ - if (alter_info.flags & (ALTER_DROP_PARTITION | ALTER_RENAME)) + if (alter_info.flags & (Alter_info::ALTER_DROP_PARTITION | + Alter_info::ALTER_RENAME)) priv_needed|= DROP_ACL; /* Must be set in the parser */ DBUG_ASSERT(select_lex->db); - DBUG_ASSERT(!(alter_info.flags & ALTER_ADMIN_PARTITION)); + DBUG_ASSERT(!(alter_info.flags & Alter_info::ALTER_EXCHANGE_PARTITION)); + DBUG_ASSERT(!(alter_info.flags & Alter_info::ALTER_ADMIN_PARTITION)); if (check_access(thd, priv_needed, first_table->db, &first_table->grant.privilege, &first_table->grant.m_internal, @@ -63,10 +232,47 @@ bool Alter_table_statement::execute(THD *thd) DBUG_RETURN(TRUE); /* purecov: inspected */ /* If it is a merge table, check privileges for merge children. */ - if (create_info.merge_list.first && - check_table_access(thd, SELECT_ACL | UPDATE_ACL | DELETE_ACL, - create_info.merge_list.first, FALSE, UINT_MAX, FALSE)) - DBUG_RETURN(TRUE); + if (create_info.merge_list.first) + { + /* + The user must have (SELECT_ACL | UPDATE_ACL | DELETE_ACL) on the + underlying base tables, even if there are temporary tables with the same + names. + + From user's point of view, it might look as if the user must have these + privileges on temporary tables to create a merge table over them. This is + one of two cases when a set of privileges is required for operations on + temporary tables (see also CREATE TABLE). + + The reason for this behavior stems from the following facts: + + - For merge tables, the underlying table privileges are checked only + at CREATE TABLE / ALTER TABLE time. + + In other words, once a merge table is created, the privileges of + the underlying tables can be revoked, but the user will still have + access to the merge table (provided that the user has privileges on + the merge table itself). + + - Temporary tables shadow base tables. + + I.e. there might be temporary and base tables with the same name, and + the temporary table takes the precedence in all operations. + + - For temporary MERGE tables we do not track if their child tables are + base or temporary. As result we can't guarantee that privilege check + which was done in presence of temporary child will stay relevant later + as this temporary table might be removed. + + If SELECT_ACL | UPDATE_ACL | DELETE_ACL privileges were not checked for + the underlying *base* tables, it would create a security breach as in + Bug#12771903. + */ + + if (check_table_access(thd, SELECT_ACL | UPDATE_ACL | DELETE_ACL, + create_info.merge_list.first, FALSE, UINT_MAX, FALSE)) + DBUG_RETURN(TRUE); + } if (check_grant(thd, priv_needed, first_table, FALSE, UINT_MAX, FALSE)) DBUG_RETURN(TRUE); /* purecov: inspected */ @@ -75,7 +281,7 @@ bool Alter_table_statement::execute(THD *thd) { // Rename of table TABLE_LIST tmp_table; - bzero((char*) &tmp_table,sizeof(tmp_table)); + memset(&tmp_table, 0, sizeof(tmp_table)); tmp_table.table_name= lex->name.str; tmp_table.db= select_lex->db; tmp_table.grant.privilege= priv; @@ -86,11 +292,11 @@ bool Alter_table_statement::execute(THD *thd) /* Don't yet allow changing of symlinks with ALTER TABLE */ if (create_info.data_file_name) - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, WARN_OPTION_IGNORED, ER(WARN_OPTION_IGNORED), "DATA DIRECTORY"); if (create_info.index_file_name) - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, WARN_OPTION_IGNORED, ER(WARN_OPTION_IGNORED), "INDEX DIRECTORY"); create_info.data_file_name= create_info.index_file_name= NULL; @@ -103,7 +309,50 @@ bool Alter_table_statement::execute(THD *thd) &alter_info, select_lex->order_list.elements, select_lex->order_list.first, - lex->ignore, lex->online); + lex->ignore); DBUG_RETURN(result); } + +bool Sql_cmd_discard_import_tablespace::execute(THD *thd) +{ + /* first SELECT_LEX (have special meaning for many of non-SELECTcommands) */ + SELECT_LEX *select_lex= &thd->lex->select_lex; + /* first table of first SELECT_LEX */ + TABLE_LIST *table_list= (TABLE_LIST*) select_lex->table_list.first; + + if (check_access(thd, ALTER_ACL, table_list->db, + &table_list->grant.privilege, + &table_list->grant.m_internal, + 0, 0)) + return true; + + if (check_grant(thd, ALTER_ACL, table_list, false, UINT_MAX, false)) + return true; + + thd->enable_slow_log= opt_log_slow_admin_statements; + + /* + Check if we attempt to alter mysql.slow_log or + mysql.general_log table and return an error if + it is the case. + TODO: this design is obsolete and will be removed. + */ + int table_kind= check_if_log_table(table_list->db_length, table_list->db, + table_list->table_name_length, + table_list->table_name, false); + + if (table_kind) + { + /* Disable alter of enabled log tables */ + if (logger.is_log_table_enabled(table_kind)) + { + my_error(ER_BAD_LOG_STATEMENT, MYF(0), "ALTER"); + return true; + } + } + + return + mysql_discard_or_import_tablespace(thd, table_list, + m_tablespace_op == DISCARD_TABLESPACE); +} diff --git a/sql/sql_audit.h b/sql/sql_audit.h index 46afe4b7596..00de3d52261 100644 --- a/sql/sql_audit.h +++ b/sql/sql_audit.h @@ -122,7 +122,7 @@ void mysql_audit_general(THD *thd, uint event_subtype, query= thd->query_string; user= user_buff; userlen= make_user_name(thd, user_buff); - rows= thd->warning_info->current_row_for_warning(); + rows= thd->get_stmt_da()->current_row_for_warning(); } else { @@ -139,7 +139,7 @@ void mysql_audit_general(THD *thd, uint event_subtype, #define MYSQL_AUDIT_NOTIFY_CONNECTION_CONNECT(thd) mysql_audit_notify(\ (thd), MYSQL_AUDIT_CONNECTION_CLASS, MYSQL_AUDIT_CONNECTION_CONNECT,\ - (thd)->stmt_da->is_error() ? (thd)->stmt_da->sql_errno() : 0,\ + (thd)->get_stmt_da()->is_error() ? (thd)->get_stmt_da()->sql_errno() : 0,\ (thd)->thread_id, (thd)->security_ctx->user,\ (thd)->security_ctx->user ? strlen((thd)->security_ctx->user) : 0,\ (thd)->security_ctx->priv_user, strlen((thd)->security_ctx->priv_user),\ @@ -160,7 +160,7 @@ void mysql_audit_general(THD *thd, uint event_subtype, #define MYSQL_AUDIT_NOTIFY_CONNECTION_CHANGE_USER(thd) mysql_audit_notify(\ (thd), MYSQL_AUDIT_CONNECTION_CLASS, MYSQL_AUDIT_CONNECTION_CHANGE_USER,\ - (thd)->stmt_da->is_error() ? (thd)->stmt_da->sql_errno() : 0,\ + (thd)->get_stmt_da()->is_error() ? (thd)->get_stmt_da()->sql_errno() : 0,\ (thd)->thread_id, (thd)->security_ctx->user,\ (thd)->security_ctx->user ? strlen((thd)->security_ctx->user) : 0,\ (thd)->security_ctx->priv_user, strlen((thd)->security_ctx->priv_user),\ diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 969e2f2c7e4..28f4b7e0ef8 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -66,9 +66,9 @@ bool No_such_table_error_handler::handle_condition(THD *, uint sql_errno, const char*, - MYSQL_ERROR::enum_warning_level level, + Sql_condition::enum_warning_level level, const char*, - MYSQL_ERROR ** cond_hdl) + Sql_condition ** cond_hdl) { *cond_hdl= NULL; if (sql_errno == ER_NO_SUCH_TABLE || sql_errno == ER_NO_SUCH_TABLE_IN_ENGINE) @@ -77,7 +77,7 @@ No_such_table_error_handler::handle_condition(THD *, return TRUE; } - if (level == MYSQL_ERROR::WARN_LEVEL_ERROR) + if (level == Sql_condition::WARN_LEVEL_ERROR) m_unhandled_errors++; return FALSE; } @@ -110,9 +110,9 @@ public: bool handle_condition(THD *thd, uint sql_errno, const char* sqlstate, - MYSQL_ERROR::enum_warning_level level, + Sql_condition::enum_warning_level level, const char* msg, - MYSQL_ERROR ** cond_hdl); + Sql_condition ** cond_hdl); /** Returns TRUE if there were ER_NO_SUCH_/WRONG_MRG_TABLE and there @@ -140,9 +140,9 @@ bool Repair_mrg_table_error_handler::handle_condition(THD *, uint sql_errno, const char*, - MYSQL_ERROR::enum_warning_level level, + Sql_condition::enum_warning_level level, const char*, - MYSQL_ERROR ** cond_hdl) + Sql_condition ** cond_hdl) { *cond_hdl= NULL; if (sql_errno == ER_NO_SUCH_TABLE || @@ -164,9 +164,37 @@ Repair_mrg_table_error_handler::handle_condition(THD *, */ /** - Protects table_def_hash, used and unused lists in the - TABLE_SHARE object, LRU lists of used TABLEs and used - TABLE_SHAREs, refresh_version and the table id counter. + LOCK_open protects the following variables/objects: + + 1) The table_def_hash + This is the hash table mapping table name to a table + share object. The hash table can only be manipulated + while holding LOCK_open. + 2) last_table_id + Generation of a new unique table_map_id for a table + share is done through incrementing last_table_id, a + global variable used for this purpose. + 3) LOCK_open protects the initialisation of the table share + object and all its members and also protects reading the + .frm file from where the table share is initialised. + 4) In particular the share->ref_count is updated each time + a new table object is created that refers to a table share. + This update is protected by LOCK_open. + 5) oldest_unused_share, end_of_unused_share and share->next + and share->prev are variables to handle the lists of table + share objects, these can only be read and manipulated while + holding the LOCK_open mutex. + 6) table_def_shutdown_in_progress can be updated only while + holding LOCK_open and ALL table cache mutexes. + 7) refresh_version + This variable can only be updated while holding LOCK_open AND + all table cache mutexes. + 8) share->version + This variable is initialised while holding LOCK_open. It can only + be updated while holding LOCK_open AND all table cache mutexes. + So if a table share is found through a reference its version won't + change if any of those mutexes are held. + 9) share->m_flush_tickets */ mysql_mutex_t LOCK_open; @@ -186,11 +214,8 @@ static void init_tdc_psi_keys(void) const char *category= "sql"; int count; - if (PSI_server == NULL) - return; - count= array_elements(all_tdc_mutexes); - PSI_server->register_mutex(category, all_tdc_mutexes, count); + mysql_mutex_register(category, all_tdc_mutexes, count); } #endif /* HAVE_PSI_INTERFACE */ @@ -257,7 +282,7 @@ static void check_unused(THD *thd) { share= (TABLE_SHARE*) my_hash_element(&table_def_cache, idx); - I_P_List_iterator<TABLE, TABLE_share> it(share->free_tables); + TABLE_SHARE::TABLE_list::Iterator it(share->free_tables); while ((entry= it++)) { /* @@ -267,7 +292,9 @@ static void check_unused(THD *thd) /* Merge children should be detached from a merge parent */ if (entry->in_use) { - DBUG_PRINT("error",("Used table is in share's list of unused tables")); /* purecov: inspected */ + /* purecov: begin inspected */ + DBUG_PRINT("error",("Used table is in share's list of unused tables")); + /* purecov: end */ } /* extra() may assume that in_use is set */ entry->in_use= thd; @@ -297,18 +324,17 @@ static void check_unused(THD *thd) #define check_unused(A) #endif +/** + Create a table cache/table definition cache key -/* - Create a table cache key - - SYNOPSIS - create_table_def_key() - thd Thread handler - key Create key here (must be of size MAX_DBKEY_LENGTH) - table_list Table definition - tmp_table Set if table is a tmp table + @param thd Thread context + @param key Buffer for the key to be created (must be of + size MAX_DBKEY_LENGTH). + @param db_name Database name. + @param table_name Table name. + @param tmp_table Set if table is a tmp table. - IMPLEMENTATION + @note The table cache_key is created from: db_name + \0 table_name + \0 @@ -319,20 +345,23 @@ static void check_unused(THD *thd) 4 bytes for master thread id 4 bytes pseudo thread id - RETURN - Length of key + @return Length of key. */ -uint create_table_def_key(THD *thd, char *key, - const TABLE_LIST *table_list, - bool tmp_table) +static uint create_table_def_key(THD *thd, char *key, + const char *db_name, const char *table_name, + bool tmp_table) { - char *db_end= strnmov(key, table_list->db, MAX_DBKEY_LENGTH - 2); - *db_end++= '\0'; - char *table_end= strnmov(db_end, table_list->table_name, - key + MAX_DBKEY_LENGTH - 1 - db_end); - *table_end++= '\0'; - uint key_length= (uint) (table_end-key); + /* + In theory caller should ensure that both db and table_name are + not longer than NAME_LEN bytes. In practice we play safe to avoid + buffer overruns. + */ + DBUG_ASSERT(strlen(db_name) <= NAME_LEN && strlen(table_name) <= NAME_LEN); + uint key_length= static_cast<uint>(strmake(strmake(key, db_name, NAME_LEN) + + 1, table_name, NAME_LEN) - key + + 1); + if (tmp_table) { int4store(key + key_length, thd->server_id); @@ -343,9 +372,46 @@ uint create_table_def_key(THD *thd, char *key, } +/** + Get table cache key for a table list element. + + @param table_list[in] Table list element. + @param key[out] On return points to table cache key for the table. + + @note Unlike create_table_def_key() call this function doesn't construct + key in a buffer provider by caller. Instead it relies on the fact + that table list element for which key is requested has properly + initialized MDL_request object and the fact that table definition + cache key is suffix of key used in MDL subsystem. So to get table + definition key it simply needs to return pointer to appropriate + part of MDL_key object nested in this table list element. + Indeed, this means that lifetime of key produced by this call is + limited by the lifetime of table list element which it got as + parameter. + + @return Length of key. +*/ + +uint get_table_def_key(const TABLE_LIST *table_list, const char **key) +{ + /* + This call relies on the fact that TABLE_LIST::mdl_request::key object + is properly initialized, so table definition cache can be produced + from key used by MDL subsystem. + */ + DBUG_ASSERT(!strcmp(table_list->get_db_name(), + table_list->mdl_request.key.db_name()) && + !strcmp(table_list->get_table_name(), + table_list->mdl_request.key.name())); + + *key= (const char*)table_list->mdl_request.key.ptr() + 1; + return table_list->mdl_request.key.length() - 1; +} + + /***************************************************************************** - Functions to handle table definition cach (TABLE_SHARE) + Functions to handle table definition cache (TABLE_SHARE) *****************************************************************************/ extern "C" uchar *table_def_key(const uchar *record, size_t *length, @@ -374,7 +440,6 @@ static void table_def_free_entry(TABLE_SHARE *share) bool table_def_init(void) { - table_def_inited= 1; #ifdef HAVE_PSI_INTERFACE init_tdc_psi_keys(); #endif @@ -382,7 +447,6 @@ bool table_def_init(void) oldest_unused_share= &end_of_unused_share; end_of_unused_share.prev= &oldest_unused_share; - return my_hash_init(&table_def_cache, &my_charset_bin, table_def_size, 0, 0, table_def_key, (my_hash_free_key) table_def_free_entry, 0) != 0; @@ -578,8 +642,9 @@ static void table_def_unuse_table(TABLE *table) # Share for table */ -TABLE_SHARE *get_table_share(THD *thd, TABLE_LIST *table_list, char *key, - uint key_length, uint db_flags, int *error, +TABLE_SHARE *get_table_share(THD *thd, TABLE_LIST *table_list, + const char *key, uint key_length, + uint db_flags, int *error, my_hash_value_type hash_value) { TABLE_SHARE *share; @@ -635,7 +700,7 @@ TABLE_SHARE *get_table_share(THD *thd, TABLE_LIST *table_list, char *key, share->ref_count++; // Mark in use #ifdef HAVE_PSI_TABLE_INTERFACE - share->m_psi= PSI_CALL(get_table_share)(false, share); + share->m_psi= PSI_TABLE_CALL(get_table_share)(false, share); #else share->m_psi= NULL; #endif @@ -695,10 +760,9 @@ found: static TABLE_SHARE * get_table_share_with_discover(THD *thd, TABLE_LIST *table_list, - char *key, uint key_length, + const char *key, uint key_length, uint db_flags, int *error, my_hash_value_type hash_value) - { TABLE_SHARE *share; bool exists; @@ -731,8 +795,8 @@ get_table_share_with_discover(THD *thd, TABLE_LIST *table_list, @todo Rework alternative ways to deal with ER_NO_SUCH TABLE. */ if (share || - (thd->is_error() && thd->stmt_da->sql_errno() != ER_NO_SUCH_TABLE && - thd->stmt_da->sql_errno() != ER_NO_SUCH_TABLE_IN_ENGINE)) + (thd->is_error() && thd->get_stmt_da()->sql_errno() != ER_NO_SUCH_TABLE && + thd->get_stmt_da()->sql_errno() != ER_NO_SUCH_TABLE_IN_ENGINE)) DBUG_RETURN(share); *error= 0; @@ -842,14 +906,11 @@ void release_table_share(TABLE_SHARE *share) TABLE_SHARE *get_cached_table_share(const char *db, const char *table_name) { - char key[SAFE_NAME_LEN*2+2]; - TABLE_LIST table_list; + char key[MAX_DBKEY_LENGTH]; uint key_length; mysql_mutex_assert_owner(&LOCK_open); - table_list.db= (char*) db; - table_list.table_name= (char*) table_name; - key_length= create_table_def_key((THD*) 0, key, &table_list, 0); + key_length= create_table_def_key((THD*) 0, key, db, table_name, 0); return (TABLE_SHARE*) my_hash_search(&table_def_cache, (uchar*) key, key_length); } @@ -913,7 +974,7 @@ OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *db, const char *wild) share->db.str)+1, share->table_name.str); (*start_list)->in_use= 0; - I_P_List_iterator<TABLE, TABLE_share> it(share->used_tables); + TABLE_SHARE::TABLE_list::Iterator it(share->used_tables); while (it++) ++(*start_list)->in_use; (*start_list)->locked= 0; /* Obsolete. */ @@ -995,7 +1056,7 @@ void free_io_cache(TABLE *table) static void kill_delayed_threads_for_table(TABLE_SHARE *share) { - I_P_List_iterator<TABLE, TABLE_share> it(share->used_tables); + TABLE_SHARE::TABLE_list::Iterator it(share->used_tables); TABLE *tab; mysql_mutex_assert_owner(&LOCK_open); @@ -1136,7 +1197,7 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables, result= TRUE; goto err_with_reopen; } - close_all_tables_for_name(thd, table->s, HA_EXTRA_NOT_USED); + close_all_tables_for_name(thd, table->s, HA_EXTRA_NOT_USED, NULL); } } @@ -1209,12 +1270,12 @@ err_with_reopen: */ thd->locked_tables_list.reopen_tables(thd); /* - Since downgrade_exclusive_lock() won't do anything with shared + Since downgrade_lock() won't do anything with shared metadata lock it is much simpler to go through all open tables rather than picking only those tables that were flushed. */ for (TABLE *tab= thd->open_tables; tab; tab= tab->next) - tab->mdl_ticket->downgrade_exclusive_lock(MDL_SHARED_NO_READ_WRITE); + tab->mdl_ticket->downgrade_lock(MDL_SHARED_NO_READ_WRITE); } DBUG_RETURN(result); } @@ -1415,7 +1476,8 @@ static void close_open_tables(THD *thd) void close_all_tables_for_name(THD *thd, TABLE_SHARE *share, - ha_extra_function extra) + ha_extra_function extra, + TABLE *skip_table) { char key[MAX_DBKEY_LENGTH]; uint key_length= share->table_cache_key.length; @@ -1430,7 +1492,8 @@ close_all_tables_for_name(THD *thd, TABLE_SHARE *share, TABLE *table= *prev; if (table->s->table_cache_key.length == key_length && - !memcmp(table->s->table_cache_key.str, key, key_length)) + !memcmp(table->s->table_cache_key.str, key, key_length) && + table != skip_table) { thd->locked_tables_list.unlink_from_list(thd, table->pos_in_locked_tables, @@ -1456,9 +1519,12 @@ close_all_tables_for_name(THD *thd, TABLE_SHARE *share, prev= &table->next; } } - /* Remove the table share from the cache. */ - tdc_remove_table(thd, TDC_RT_REMOVE_ALL, db, table_name, - FALSE); + if (skip_table == NULL) + { + /* Remove the table share from the cache. */ + tdc_remove_table(thd, TDC_RT_REMOVE_ALL, db, table_name, + FALSE); + } } @@ -1608,9 +1674,8 @@ void close_thread_tables(THD *thd) /* move one table to free list */ -bool close_thread_table(THD *thd, TABLE **table_ptr) +void close_thread_table(THD *thd, TABLE **table_ptr) { - bool found_old_table= 0; TABLE *table= *table_ptr; DBUG_ENTER("close_thread_table"); DBUG_PRINT("tcache", ("table: '%s'.'%s' 0x%lx", table->s->db.str, @@ -1656,10 +1721,7 @@ bool close_thread_table(THD *thd, TABLE **table_ptr) if (table->s->has_old_version() || table->needs_reopen() || table_def_shutdown_in_progress) - { free_cache_entry(table); - found_old_table= 1; - } else { DBUG_ASSERT(table->file); @@ -1672,7 +1734,7 @@ bool close_thread_table(THD *thd, TABLE **table_ptr) free_cache_entry(unused_tables); } mysql_mutex_unlock(&LOCK_open); - DBUG_RETURN(found_old_table); + DBUG_VOID_RETURN; } @@ -1815,7 +1877,7 @@ bool close_temporary_tables(THD *thd) qinfo.db_len= db.length(); thd->variables.character_set_client= cs_save; - thd->stmt_da->can_overwrite_status= TRUE; + thd->get_stmt_da()->set_overwrite_status(true); if ((error= (mysql_bin_log.write(&qinfo) || error))) { /* @@ -1833,7 +1895,7 @@ bool close_temporary_tables(THD *thd) sql_print_error("Failed to write the DROP statement for " "temporary tables to binary log"); } - thd->stmt_da->can_overwrite_status= FALSE; + thd->get_stmt_da()->set_overwrite_status(false); thd->variables.pseudo_thread_id= save_pseudo_thread_id; thd->thread_specific_used= save_thread_specific_used; @@ -2119,12 +2181,9 @@ void update_non_unique_table_error(TABLE_LIST *update, TABLE *find_temporary_table(THD *thd, const char *db, const char *table_name) { - TABLE_LIST tl; - - tl.db= (char*) db; - tl.table_name= (char*) table_name; - - return find_temporary_table(thd, &tl); + char key[MAX_DBKEY_LENGTH]; + uint key_length= create_table_def_key(thd, key, db, table_name, 1); + return find_temporary_table(thd, key, key_length); } @@ -2137,10 +2196,26 @@ TABLE *find_temporary_table(THD *thd, const char *db, const char *table_name) TABLE *find_temporary_table(THD *thd, const TABLE_LIST *tl) { - char key[MAX_DBKEY_LENGTH]; - uint key_length= create_table_def_key(thd, key, tl, 1); + const char *key; + uint key_length; + char key_suffix[TMP_TABLE_KEY_EXTRA]; + TABLE *table; - return find_temporary_table(thd, key, key_length); + key_length= get_table_def_key(tl, &key); + + int4store(key_suffix, thd->server_id); + int4store(key_suffix + 4, thd->variables.pseudo_thread_id); + + for (table= thd->temporary_tables; table; table= table->next) + { + if ((table->s->table_cache_key.length == key_length + + TMP_TABLE_KEY_EXTRA) && + !memcmp(table->s->table_cache_key.str, key, key_length) && + !memcmp(table->s->table_cache_key.str + key_length, key_suffix, + TMP_TABLE_KEY_EXTRA)) + return table; + } + return NULL; } @@ -2318,15 +2393,12 @@ bool rename_temporary_table(THD* thd, TABLE *table, const char *db, char *key; uint key_length; TABLE_SHARE *share= table->s; - TABLE_LIST table_list; DBUG_ENTER("rename_temporary_table"); if (!(key=(char*) alloc_root(&share->mem_root, MAX_DBKEY_LENGTH))) DBUG_RETURN(1); /* purecov: inspected */ - table_list.db= (char*) db; - table_list.table_name= (char*) table_name; - key_length= create_table_def_key(thd, key, &table_list, 1); + key_length= create_table_def_key(thd, key, db, table_name, 1); share->set_table_cache_key(key, key_length); DBUG_RETURN(0); } @@ -2358,8 +2430,9 @@ bool wait_while_table_is_used(THD *thd, TABLE *table, table->s->table_name.str, (ulong) table->s, table->db_stat, table->s->version)); - if (thd->mdl_context.upgrade_shared_lock_to_exclusive( - table->mdl_ticket, thd->variables.lock_wait_timeout)) + if (thd->mdl_context.upgrade_shared_lock( + table->mdl_ticket, MDL_EXCLUSIVE, + thd->variables.lock_wait_timeout)) DBUG_RETURN(TRUE); tdc_remove_table(thd, TDC_RT_REMOVE_NOT_OWN, @@ -2408,8 +2481,8 @@ void drop_open_table(THD *thd, TABLE *table, const char *db_name, tdc_remove_table(thd, TDC_RT_REMOVE_ALL, db_name, table_name, FALSE); /* Remove the table from the storage engine and rm the .frm. */ - quick_rm_table(table_type, db_name, table_name, 0); - } + quick_rm_table(thd, table_type, db_name, table_name, 0); + } DBUG_VOID_RETURN; } @@ -2497,9 +2570,9 @@ public: virtual bool handle_condition(THD *thd, uint sql_errno, const char* sqlstate, - MYSQL_ERROR::enum_warning_level level, + Sql_condition::enum_warning_level level, const char* msg, - MYSQL_ERROR ** cond_hdl); + Sql_condition ** cond_hdl); private: /** Open table context to be used for back-off request. */ @@ -2516,9 +2589,9 @@ private: bool MDL_deadlock_handler::handle_condition(THD *, uint sql_errno, const char*, - MYSQL_ERROR::enum_warning_level, + Sql_condition::enum_warning_level, const char*, - MYSQL_ERROR ** cond_hdl) + Sql_condition ** cond_hdl) { *cond_hdl= NULL; if (! m_is_active && sql_errno == ER_LOCK_DEADLOCK) @@ -2703,51 +2776,46 @@ tdc_wait_for_old_version(THD *thd, const char *db, const char *table_name, } -/* - Open a table. - - SYNOPSIS - open_table() - thd Thread context. - table_list Open first table in list. - action INOUT Pointer to variable of enum_open_table_action type - which will be set according to action which is - required to remedy problem appeared during attempt - to open table. - flags Bitmap of flags to modify how open works: - MYSQL_OPEN_IGNORE_FLUSH - Open table even if - someone has done a flush or there is a pending - exclusive metadata lock requests against it - (i.e. request high priority metadata lock). - No version number checking is done. - MYSQL_OPEN_TEMPORARY_ONLY - Open only temporary - table not the base table or view. - MYSQL_OPEN_TAKE_UPGRADABLE_MDL - Obtain upgradable - metadata lock for tables on which we are going to - take some kind of write table-level lock. - - IMPLEMENTATION - Uses a cache of open tables to find a table not in use. - - If TABLE_LIST::open_strategy is set to OPEN_IF_EXISTS, the table is opened - only if it exists. If the open strategy is OPEN_STUB, the underlying table - is never opened. In both cases, metadata locks are always taken according - to the lock strategy. - - RETURN - TRUE Open failed. "action" parameter may contain type of action - needed to remedy problem before retrying again. - FALSE Success. Members of TABLE_LIST structure are filled properly (e.g. - TABLE_LIST::table is set for real tables and TABLE_LIST::view is - set for views). +/** + Open a base table. + + @param thd Thread context. + @param table_list Open first table in list. + @param mem_root Temporary MEM_ROOT to be used for + parsing .FRMs for views. + @param ot_ctx Context with flags which modify how open works + and which is used to recover from a failed + open_table() attempt. + Some examples of flags: + MYSQL_OPEN_IGNORE_FLUSH - Open table even if + someone has done a flush. No version number + checking is done. + MYSQL_OPEN_HAS_MDL_LOCK - instead of acquiring + metadata locks rely on that caller already has + appropriate ones. + + Uses a cache of open tables to find a TABLE instance not in use. + + If TABLE_LIST::open_strategy is set to OPEN_IF_EXISTS, the table is + opened only if it exists. If the open strategy is OPEN_STUB, the + underlying table is never opened. In both cases, metadata locks are + always taken according to the lock strategy. + + The function used to open temporary tables, but now it opens base tables + only. + + @retval TRUE Open failed. "action" parameter may contain type of action + needed to remedy problem before retrying again. + @retval FALSE Success. Members of TABLE_LIST structure are filled properly + (e.g. TABLE_LIST::table is set for real tables and + TABLE_LIST::view is set for views). */ - bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, Open_table_context *ot_ctx) { reg1 TABLE *table; - char key[MAX_DBKEY_LENGTH]; + const char *key; uint key_length; char *alias= table_list->alias; uint flags= ot_ctx->get_flags(); @@ -2756,76 +2824,44 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, TABLE_SHARE *share; my_hash_value_type hash_value; bool recycled_free_table; - DBUG_ENTER("open_table"); + /* + The table must not be opened already. The table can be pre-opened for + some statements if it is a temporary table. + + open_temporary_table() must be used to open temporary tables. + */ + DBUG_ASSERT(!table_list->table); + /* an open table operation needs a lot of the stack space */ if (check_stack_overrun(thd, STACK_MIN_SIZE_FOR_OPEN, (uchar *)&alias)) DBUG_RETURN(TRUE); - if (thd->killed) + if (!(flags & MYSQL_OPEN_IGNORE_KILLED) && thd->killed) DBUG_RETURN(TRUE); - key_length= (create_table_def_key(thd, key, table_list, 1) - - TMP_TABLE_KEY_EXTRA); - /* - Unless requested otherwise, try to resolve this table in the list - of temporary tables of this thread. In MySQL temporary tables - are always thread-local and "shadow" possible base tables with the - same name. This block implements the behaviour. - TODO: move this block into a separate function. + Check if we're trying to take a write lock in a read only transaction. + + Note that we allow write locks on log tables as otherwise logging + to general/slow log would be disabled in read only transactions. */ - if (table_list->open_type != OT_BASE_ONLY && - ! (flags & MYSQL_OPEN_SKIP_TEMPORARY)) + if (table_list->mdl_request.type >= MDL_SHARED_WRITE && + thd->tx_read_only && + !(flags & (MYSQL_LOCK_LOG_TABLE | MYSQL_OPEN_HAS_MDL_LOCK))) { - for (table= thd->temporary_tables; table ; table=table->next) - { - if (table->s->table_cache_key.length == key_length + - TMP_TABLE_KEY_EXTRA && - !memcmp(table->s->table_cache_key.str, key, - key_length + TMP_TABLE_KEY_EXTRA)) - { - /* - We're trying to use the same temporary table twice in a query. - Right now we don't support this because a temporary table - is always represented by only one TABLE object in THD, and - it can not be cloned. Emit an error for an unsupported behaviour. - */ - if (table->query_id) - { - DBUG_PRINT("error", - ("query_id: %lu server_id: %u pseudo_thread_id: %lu", - (ulong) table->query_id, (uint) thd->server_id, - (ulong) thd->variables.pseudo_thread_id)); - my_error(ER_CANT_REOPEN_TABLE, MYF(0), table->alias.c_ptr()); - DBUG_RETURN(TRUE); - } - table->query_id= thd->query_id; - thd->thread_specific_used= TRUE; - DBUG_PRINT("info",("Using temporary table")); - goto reset; - } - } + my_error(ER_CANT_EXECUTE_IN_READ_ONLY_TRANSACTION, MYF(0)); + DBUG_RETURN(true); } - if (table_list->open_type == OT_TEMPORARY_ONLY || - (flags & MYSQL_OPEN_TEMPORARY_ONLY)) - { - if (table_list->open_strategy == TABLE_LIST::OPEN_NORMAL) - { - my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db, table_list->table_name); - DBUG_RETURN(TRUE); - } - else - DBUG_RETURN(FALSE); - } + key_length= get_table_def_key(table_list, &key); /* - The table is not temporary - if we're in pre-locked or LOCK TABLES - mode, let's try to find the requested table in the list of pre-opened - and locked tables. If the table is not there, return an error - we can't - open not pre-opened tables in pre-locked/LOCK TABLES mode. + If we're in pre-locked or LOCK TABLES mode, let's try to find the + requested table in the list of pre-opened and locked tables. If the + table is not there, return an error - we can't open not pre-opened + tables in pre-locked/LOCK TABLES mode. TODO: move this block into a separate function. */ if (thd->locked_tables_mode && @@ -2912,7 +2948,7 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, if (dd_frm_type(thd, path, ¬_used) == FRMTYPE_VIEW) { if (!tdc_open_view(thd, table_list, alias, key, key_length, - mem_root, 0)) + mem_root, CHECK_METADATA_VERSION)) { DBUG_ASSERT(table_list->view != 0); DBUG_RETURN(FALSE); // VIEW @@ -3141,12 +3177,12 @@ retry_share: Release our reference to share, wait until old version of share goes away and then try to get new version of table share. */ - MDL_deadlock_handler mdl_deadlock_handler(ot_ctx); - bool wait_result; - release_table_share(share); mysql_mutex_unlock(&LOCK_open); + MDL_deadlock_handler mdl_deadlock_handler(ot_ctx); + bool wait_result; + thd->push_internal_handler(&mdl_deadlock_handler); wait_result= tdc_wait_for_old_version(thd, table_list->db, table_list->table_name, @@ -3216,22 +3252,18 @@ retry_share: else if (share->crashed) (void) ot_ctx->request_backoff_action(Open_table_context::OT_REPAIR, table_list); - goto err_lock; } - if (open_table_entry_fini(thd, share, table)) { closefrm(table, 0); my_free(table); goto err_lock; } - mysql_mutex_lock(&LOCK_open); /* Add table to the share's used tables list. */ table_def_add_used_table(thd, table); } - mysql_mutex_unlock(&LOCK_open); /* Call rebind_psi outside of the LOCK_open critical section. */ @@ -3257,6 +3289,21 @@ retry_share: table_list->updatable= 1; // It is not derived table nor non-updatable VIEW table_list->table= table; +#ifdef WITH_PARTITION_STORAGE_ENGINE + if (table->part_info) + { + /* Set all [named] partitions as used. */ + if (table->part_info->set_partition_bitmaps(table_list)) + DBUG_RETURN(true); + } + else if (table_list->partition_names) + { + /* Don't allow PARTITION () clause on a nonpartitioned table */ + my_error(ER_PARTITION_CLAUSE_ON_NONPARTITIONED, MYF(0)); + DBUG_RETURN(true); + } +#endif + table->init(thd, table_list); DBUG_RETURN(FALSE); @@ -3312,9 +3359,9 @@ TABLE *find_locked_table(TABLE *list, const char *db, const char *table_name) upgrade the lock and ER_TABLE_NOT_LOCKED_FOR_WRITE will be reported. - @return Pointer to TABLE instance with MDL_SHARED_NO_WRITE, - MDL_SHARED_NO_READ_WRITE, or MDL_EXCLUSIVE metadata - lock, NULL otherwise. + @return Pointer to TABLE instance with MDL_SHARED_UPGRADABLE + MDL_SHARED_NO_WRITE, MDL_SHARED_NO_READ_WRITE, or + MDL_EXCLUSIVE metadata lock, NULL otherwise. */ TABLE *find_table_for_mdl_upgrade(THD *thd, const char *db, @@ -3888,7 +3935,7 @@ check_and_update_routine_version(THD *thd, Sroutine_hash_entry *rt, */ bool tdc_open_view(THD *thd, TABLE_LIST *table_list, const char *alias, - char *cache_key, uint cache_key_length, + const char *cache_key, uint cache_key_length, MEM_ROOT *mem_root, uint flags) { TABLE not_used; @@ -3906,6 +3953,24 @@ bool tdc_open_view(THD *thd, TABLE_LIST *table_list, const char *alias, hash_value))) goto err; + if ((flags & CHECK_METADATA_VERSION)) + { + /* + Check TABLE_SHARE-version of view only if we have been instructed to do + so. We do not need to check the version if we're executing CREATE VIEW or + ALTER VIEW statements. + + In the future, this functionality should be moved out from + tdc_open_view(), and tdc_open_view() should became a part of a clean + table-definition-cache interface. + */ + if (check_and_update_table_version(thd, table_list, share)) + { + release_table_share(share); + goto err; + } + } + if (share->is_view && !open_new_frm(thd, share, alias, (uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE | @@ -3977,7 +4042,7 @@ static bool open_table_entry_fini(THD *thd, TABLE_SHARE *share, TABLE *entry) static bool auto_repair_table(THD *thd, TABLE_LIST *table_list) { - char cache_key[MAX_DBKEY_LENGTH]; + const char *cache_key; uint cache_key_length; TABLE_SHARE *share; TABLE *entry; @@ -3985,7 +4050,7 @@ static bool auto_repair_table(THD *thd, TABLE_LIST *table_list) bool result= TRUE; my_hash_value_type hash_value; - cache_key_length= create_table_def_key(thd, cache_key, table_list, 0); + cache_key_length= get_table_def_key(table_list, &cache_key); thd->clear_error(); @@ -4188,7 +4253,7 @@ recover_from_failed_open(THD *thd) ha_create_table_from_engine(thd, m_failed_table->db, m_failed_table->table_name); - thd->warning_info->clear_warning_info(thd->query_id); + thd->get_stmt_da()->clear_warning_info(thd->query_id); thd->clear_error(); // Clear error message thd->mdl_context.release_transactional_locks(); break; @@ -4788,29 +4853,62 @@ lock_table_names(THD *thd, for (table= tables_start; table && table != tables_end; table= table->next_global) { - if (table->mdl_request.type >= MDL_SHARED_NO_WRITE && - !(table->open_type == OT_TEMPORARY_ONLY || - (flags & MYSQL_OPEN_TEMPORARY_ONLY) || - (table->open_type != OT_BASE_ONLY && - ! (flags & MYSQL_OPEN_SKIP_TEMPORARY) && - find_temporary_table(thd, table)))) + if (table->mdl_request.type < MDL_SHARED_UPGRADABLE || + table->open_type == OT_TEMPORARY_ONLY || + (table->open_type == OT_TEMPORARY_OR_BASE && is_temporary_table(table))) { - /* - Write lock on normal tables is not allowed in a read only transaction. - */ - if (thd->tx_read_only) - { - my_error(ER_CANT_EXECUTE_IN_READ_ONLY_TRANSACTION, MYF(0)); - DBUG_RETURN(true); - } + continue; + } - if (! (flags & MYSQL_OPEN_SKIP_SCOPED_MDL_LOCK) && - schema_set.insert(table)) - DBUG_RETURN(TRUE); - mdl_requests.push_front(&table->mdl_request); + /* + Write lock on normal tables is not allowed in a read only transaction. + */ + if (thd->tx_read_only) + { + my_error(ER_CANT_EXECUTE_IN_READ_ONLY_TRANSACTION, MYF(0)); + DBUG_RETURN(true); } + + if (! (flags & MYSQL_OPEN_SKIP_SCOPED_MDL_LOCK) && schema_set.insert(table)) + DBUG_RETURN(TRUE); + + mdl_requests.push_front(&table->mdl_request); + } + + if (! (flags & MYSQL_OPEN_SKIP_SCOPED_MDL_LOCK) && + ! mdl_requests.is_empty()) + { + /* + Scoped locks: Take intention exclusive locks on all involved + schemas. + */ + Hash_set<TABLE_LIST, schema_set_get_key>::Iterator it(schema_set); + while ((table= it++)) + { + MDL_request *schema_request= new (thd->mem_root) MDL_request; + if (schema_request == NULL) + return TRUE; + schema_request->init(MDL_key::SCHEMA, table->db, "", + MDL_INTENTION_EXCLUSIVE, + MDL_TRANSACTION); + mdl_requests.push_front(schema_request); + } + + /* + Protect this statement against concurrent global read lock + by acquiring global intention exclusive lock with statement + duration. + */ + if (thd->global_read_lock.can_acquire_protection()) + return TRUE; + global_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE, + MDL_STATEMENT); + mdl_requests.push_front(&global_request); } + if (thd->mdl_context.acquire_locks(&mdl_requests, lock_wait_timeout)) + return TRUE; + if (mdl_requests.is_empty()) DBUG_RETURN(FALSE); @@ -4878,7 +4976,7 @@ lock_table_names(THD *thd, { if (thd->lex->create_info.options & HA_LEX_CREATE_IF_NOT_EXISTS) { - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_TABLE_EXISTS_ERROR, ER(ER_TABLE_EXISTS_ERROR), tables_start->table_name); } @@ -4928,34 +5026,33 @@ open_tables_check_upgradable_mdl(THD *thd, TABLE_LIST *tables_start, for (table= tables_start; table && table != tables_end; table= table->next_global) { - if (table->mdl_request.type >= MDL_SHARED_NO_WRITE && - !(table->open_type == OT_TEMPORARY_ONLY || - (flags & MYSQL_OPEN_TEMPORARY_ONLY) || - (table->open_type != OT_BASE_ONLY && - ! (flags & MYSQL_OPEN_SKIP_TEMPORARY) && - find_temporary_table(thd, table)))) + if (table->mdl_request.type < MDL_SHARED_UPGRADABLE || + table->open_type == OT_TEMPORARY_ONLY || + (table->open_type == OT_TEMPORARY_OR_BASE && is_temporary_table(table))) { - /* - We don't need to do anything about the found TABLE instance as it - will be handled later in open_tables(), we only need to check that - an upgradable lock is already acquired. When we enter LOCK TABLES - mode, SNRW locks are acquired before all other locks. So if under - LOCK TABLES we find that there is TABLE instance with upgradeable - lock, all other instances of TABLE for the same table will have the - same ticket. - - Note that this works OK even for CREATE TABLE statements which - request X type of metadata lock. This is because under LOCK TABLES - such statements don't create the table but only check if it exists - or, in most complex case, only insert into it. - Thus SNRW lock should be enough. - - Note that find_table_for_mdl_upgrade() will report an error if - no suitable ticket is found. - */ - if (!find_table_for_mdl_upgrade(thd, table->db, table->table_name, false)) - return TRUE; + continue; } + + /* + We don't need to do anything about the found TABLE instance as it + will be handled later in open_tables(), we only need to check that + an upgradable lock is already acquired. When we enter LOCK TABLES + mode, SNRW locks are acquired before all other locks. So if under + LOCK TABLES we find that there is TABLE instance with upgradeable + lock, all other instances of TABLE for the same table will have the + same ticket. + + Note that this works OK even for CREATE TABLE statements which + request X type of metadata lock. This is because under LOCK TABLES + such statements don't create the table but only check if it exists + or, in most complex case, only insert into it. + Thus SNRW lock should be enough. + + Note that find_table_for_mdl_upgrade() will report an error if + no suitable ticket is found. + */ + if (!find_table_for_mdl_upgrade(thd, table->db, table->table_name, false)) + return TRUE; } return FALSE; @@ -4995,11 +5092,12 @@ bool open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags, Prelocking_strategy *prelocking_strategy) { /* - We use pointers to "next_global" member in the last processed TABLE_LIST - element and to the "next" member in the last processed Sroutine_hash_entry - element as iterators over, correspondingly, the table list and stored routines - list which stay valid and allow to continue iteration when new elements are - added to the tail of the lists. + We use pointers to "next_global" member in the last processed + TABLE_LIST element and to the "next" member in the last processed + Sroutine_hash_entry element as iterators over, correspondingly, + the table list and stored routines list which stay valid and allow + to continue iteration when new elements are added to the tail of + the lists. */ TABLE_LIST **table_to_open; Sroutine_hash_entry **sroutine_to_open; @@ -5088,7 +5186,7 @@ restart: for (table= *start; table && table != thd->lex->first_not_own_table(); table= table->next_global) { - if (table->mdl_request.type >= MDL_SHARED_NO_WRITE) + if (table->mdl_request.type >= MDL_SHARED_UPGRADABLE) table->mdl_request.ticket= NULL; } } @@ -5619,7 +5717,7 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type, table_list->required_type= FRMTYPE_TABLE; /* This function can't properly handle requests for such metadata locks. */ - DBUG_ASSERT(table_list->mdl_request.type < MDL_SHARED_NO_WRITE); + DBUG_ASSERT(table_list->mdl_request.type < MDL_SHARED_UPGRADABLE); while ((error= open_table(thd, table_list, thd->mem_root, &ot_ctx)) && ot_ctx.can_recover_from_failed_open()) @@ -6125,6 +6223,9 @@ void close_tables_for_reopen(THD *thd, TABLE_LIST **tables, @param add_to_temporary_tables_list Specifies if the opened TABLE instance should be linked into THD::temporary_tables list. + @param open_in_engine Indicates that we need to open table + in storage engine in addition to + constructing TABLE object for it. @note This function is used: - by alter_table() to open a temporary table; @@ -6136,7 +6237,8 @@ void close_tables_for_reopen(THD *thd, TABLE_LIST **tables, TABLE *open_table_uncached(THD *thd, const char *path, const char *db, const char *table_name, - bool add_to_temporary_tables_list) + bool add_to_temporary_tables_list, + bool open_in_engine) { TABLE *tmp_table; TABLE_SHARE *share; @@ -6153,13 +6255,20 @@ TABLE *open_table_uncached(THD *thd, const char *path, const char *db, table_list.db= (char*) db; table_list.table_name= (char*) table_name; /* Create the cache_key for temporary tables */ - key_length= create_table_def_key(thd, cache_key, &table_list, 1); + key_length= create_table_def_key(thd, cache_key, db, table_name, 1); if (!(tmp_table= (TABLE*) my_malloc(sizeof(*tmp_table) + sizeof(*share) + strlen(path)+1 + key_length, MYF(MY_WME)))) DBUG_RETURN(0); /* purecov: inspected */ +#ifndef DBUG_OFF + mysql_mutex_lock(&LOCK_open); + DBUG_ASSERT(!my_hash_search(&table_def_cache, (uchar*) cache_key, + key_length)); + mysql_mutex_unlock(&LOCK_open); +#endif + share= (TABLE_SHARE*) (tmp_table+1); tmp_path= (char*) (share+1); saved_cache_key= strmov(tmp_path, path)+1; @@ -6177,17 +6286,23 @@ TABLE *open_table_uncached(THD *thd, const char *path, const char *db, } #ifdef HAVE_PSI_TABLE_INTERFACE - share->m_psi= PSI_CALL(get_table_share)(true, share); + share->m_psi= PSI_TABLE_CALL(get_table_share)(true, share); #else share->m_psi= NULL; #endif if (open_table_from_share(thd, share, table_name, + open_in_engine ? (uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE | - HA_GET_INDEX), + HA_GET_INDEX) : 0, READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD, ha_open_options, - tmp_table, FALSE)) + tmp_table, + /* + Set "is_create_table" if the table does not + exist in SE + */ + open_in_engine ? false : true)) { /* No need to lock share->mutex as this is not needed for tmp tables */ free_table_share(share); @@ -6217,17 +6332,27 @@ TABLE *open_table_uncached(THD *thd, const char *path, const char *db, } -bool rm_temporary_table(handlerton *base, char *path) +/** + Delete a temporary table. + + @param base Handlerton for table to be deleted. + @param path Path to the table to be deleted (i.e. path + to its .frm without an extension). + + @retval false - success. + @retval true - failure. +*/ + +bool rm_temporary_table(handlerton *base, const char *path) { bool error=0; handler *file; - char *ext; + char frm_path[FN_REFLEN + 1]; DBUG_ENTER("rm_temporary_table"); - strmov(ext= strend(path), reg_ext); - if (mysql_file_delete(key_file_frm, path, MYF(0))) + strxnmov(frm_path, sizeof(frm_path) - 1, path, reg_ext, NullS); + if (mysql_file_delete(key_file_frm, frm_path, MYF(0))) error=1; /* purecov: inspected */ - *ext= 0; // remove extension file= get_new_handler((TABLE_SHARE*) 0, current_thd->mem_root, base); if (file && file->ha_delete_table(path)) { @@ -6308,6 +6433,143 @@ static void update_field_dependencies(THD *thd, Field *field, TABLE *table) } +/** + Find a temporary table specified by TABLE_LIST instance in the cache and + prepare its TABLE instance for use. + + This function tries to resolve this table in the list of temporary tables + of this thread. Temporary tables are thread-local and "shadow" base + tables with the same name. + + @note In most cases one should use open_temporary_tables() instead + of this call. + + @note One should finalize process of opening temporary table for table + list element by calling open_and_process_table(). This function + is responsible for table version checking and handling of merge + tables. + + @note We used to check global_read_lock before opening temporary tables. + However, that limitation was artificial and is removed now. + + @return Error status. + @retval FALSE On success. If a temporary table exists for the given + key, tl->table is set. + @retval TRUE On error. my_error() has been called. +*/ + +bool open_temporary_table(THD *thd, TABLE_LIST *tl) +{ + TABLE *table; + DBUG_ENTER("open_temporary_table"); + DBUG_PRINT("enter", ("table: '%s'.'%s'", tl->db, tl->table_name)); + + /* + Code in open_table() assumes that TABLE_LIST::table can + be non-zero only for pre-opened temporary tables. + */ + DBUG_ASSERT(tl->table == NULL); + + /* + This function should not be called for cases when derived or I_S + tables can be met since table list elements for such tables can + have invalid db or table name. + Instead open_temporary_tables() should be used. + */ + DBUG_ASSERT(!tl->derived && !tl->schema_table); + + if (tl->open_type == OT_BASE_ONLY) + { + DBUG_PRINT("info", ("skip_temporary is set")); + DBUG_RETURN(FALSE); + } + + if (!(table= find_temporary_table(thd, tl))) + { + if (tl->open_type == OT_TEMPORARY_ONLY && + tl->open_strategy == TABLE_LIST::OPEN_NORMAL) + { + my_error(ER_NO_SUCH_TABLE, MYF(0), tl->db, tl->table_name); + DBUG_RETURN(TRUE); + } + DBUG_RETURN(FALSE); + } + +#ifdef WITH_PARTITION_STORAGE_ENGINE + if (tl->partition_names) + { + /* Partitioned temporary tables is not supported. */ + DBUG_ASSERT(!table->part_info); + my_error(ER_PARTITION_CLAUSE_ON_NONPARTITIONED, MYF(0)); + DBUG_RETURN(true); + } +#endif + + if (table->query_id) + { + /* + We're trying to use the same temporary table twice in a query. + Right now we don't support this because a temporary table is always + represented by only one TABLE object in THD, and it can not be + cloned. Emit an error for an unsupported behaviour. + */ + + DBUG_PRINT("error", + ("query_id: %lu server_id: %u pseudo_thread_id: %lu", + (ulong) table->query_id, (uint) thd->server_id, + (ulong) thd->variables.pseudo_thread_id)); + my_error(ER_CANT_REOPEN_TABLE, MYF(0), table->alias.c_ptr()); + DBUG_RETURN(TRUE); + } + + table->query_id= thd->query_id; + thd->thread_specific_used= TRUE; + + tl->updatable= 1; // It is not derived table nor non-updatable VIEW. + tl->table= table; + + table->init(thd, tl); + + DBUG_PRINT("info", ("Using temporary table")); + DBUG_RETURN(FALSE); +} + + +/** + Pre-open temporary tables corresponding to table list elements. + + @note One should finalize process of opening temporary tables + by calling open_tables(). This function is responsible + for table version checking and handling of merge tables. + + @return Error status. + @retval FALSE On success. If a temporary tables exists for the + given element, tl->table is set. + @retval TRUE On error. my_error() has been called. +*/ + +bool open_temporary_tables(THD *thd, TABLE_LIST *tl_list) +{ + TABLE_LIST *first_not_own= thd->lex->first_not_own_table(); + DBUG_ENTER("open_temporary_tables"); + + for (TABLE_LIST *tl= tl_list; tl && tl != first_not_own; tl= tl->next_global) + { + if (tl->derived || tl->schema_table) + { + /* + Derived and I_S tables will be handled by a later call to open_tables(). + */ + continue; + } + + if (open_temporary_table(thd, tl)) + DBUG_RETURN(TRUE); + } + + DBUG_RETURN(FALSE); +} + /* Find a field by name in a view that uses merge algorithm. @@ -8976,7 +9238,7 @@ err_no_arena: @retval false OK. */ -static bool +bool fill_record(THD * thd, TABLE *table_arg, List<Item> &fields, List<Item> &values, bool ignore_errors) { @@ -9000,7 +9262,7 @@ fill_record(THD * thd, TABLE *table_arg, List<Item> &fields, List<Item> &values, thus we safely can take table from the first field. */ fld= (Item_field*)f++; - if (!(field= fld->filed_for_view_update())) + if (!(field= fld->field_for_view_update())) { my_error(ER_NONUPDATEABLE_COLUMN, MYF(0), fld->name); goto err; @@ -9014,7 +9276,7 @@ fill_record(THD * thd, TABLE *table_arg, List<Item> &fields, List<Item> &values, while ((fld= f++)) { - if (!(field= fld->filed_for_view_update())) + if (!(field= fld->field_for_view_update())) { my_error(ER_NONUPDATEABLE_COLUMN, MYF(0), fld->name); goto err; @@ -9029,7 +9291,7 @@ fill_record(THD * thd, TABLE *table_arg, List<Item> &fields, List<Item> &values, value->type() != Item::NULL_ITEM && table->s->table_category != TABLE_CATEGORY_TEMPORARY) { - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_WARNING_NON_DEFAULT_VALUE_FOR_VIRTUAL_COLUMN, ER(ER_WARNING_NON_DEFAULT_VALUE_FOR_VIRTUAL_COLUMN), rfield->field_name, table->s->table_name.str); @@ -9106,7 +9368,7 @@ fill_record_n_invoke_before_triggers(THD *thd, TABLE *table, List<Item> &fields, if (fields.elements) { fld= (Item_field*)f++; - item_field= fld->filed_for_view_update(); + item_field= fld->field_for_view_update(); if (item_field && item_field->field && table && table->vfield) { DBUG_ASSERT(table == item_field->field->table); @@ -9181,7 +9443,7 @@ fill_record(THD *thd, TABLE *table, Field **ptr, List<Item> &values, value->type() != Item::NULL_ITEM && table->s->table_category != TABLE_CATEGORY_TEMPORARY) { - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_WARNING_NON_DEFAULT_VALUE_FOR_VIRTUAL_COLUMN, ER(ER_WARNING_NON_DEFAULT_VALUE_FOR_VIRTUAL_COLUMN), field->field_name, table->s->table_name.str); @@ -9445,6 +9707,15 @@ bool mysql_notify_thread_having_shared_lock(THD *thd, THD *in_use, instances (if there are no used instances will also remove TABLE_SHARE). + TDC_RT_REMOVE_NOT_OWN_KEEP_SHARE - + remove all TABLE instances + except those that belong to + this thread, but don't mark + TABLE_SHARE as old. There + should be no TABLE objects + used by other threads and + caller should have exclusive + metadata lock on the table. @param db Name of database @param table_name Name of table @param has_lock If TRUE, LOCK_open is already acquired @@ -9474,27 +9745,30 @@ void tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type, thd->mdl_context.is_lock_owner(MDL_key::TABLE, db, table_name, MDL_EXCLUSIVE)); - key_length=(uint) (strmov(strmov(key,db)+1,table_name)-key)+1; + key_length= create_table_def_key(thd, key, db, table_name, false); if ((share= (TABLE_SHARE*) my_hash_search(&table_def_cache,(uchar*) key, key_length))) { if (share->ref_count) { - I_P_List_iterator<TABLE, TABLE_share> it(share->free_tables); + TABLE_SHARE::TABLE_list::Iterator it(share->free_tables); #ifndef DBUG_OFF if (remove_type == TDC_RT_REMOVE_ALL) { DBUG_ASSERT(share->used_tables.is_empty()); } - else if (remove_type == TDC_RT_REMOVE_NOT_OWN) + else if (remove_type == TDC_RT_REMOVE_NOT_OWN || + remove_type == TDC_RT_REMOVE_NOT_OWN_KEEP_SHARE) { - I_P_List_iterator<TABLE, TABLE_share> it2(share->used_tables); + TABLE_SHARE::TABLE_list::Iterator it2(share->used_tables); while ((table= it2++)) + { if (table->in_use != thd) { DBUG_ASSERT(0); } + } } #endif /* @@ -9508,13 +9782,17 @@ void tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type, TDC does not contain old shares which don't have any tables used. */ - share->version= 0; + if (remove_type != TDC_RT_REMOVE_NOT_OWN_KEEP_SHARE) + share->version= 0; while ((table= it++)) free_cache_entry(table); } else + { + DBUG_ASSERT(remove_type != TDC_RT_REMOVE_NOT_OWN_KEEP_SHARE); (void) my_hash_delete(&table_def_cache, (uchar*) share); + } } if (! has_lock) @@ -9588,12 +9866,14 @@ open_new_frm(THD *thd, TABLE_SHARE *share, const char *alias, { LEX_STRING pathstr; File_parser *parser; - char path[FN_REFLEN]; + char path[FN_REFLEN+1]; DBUG_ENTER("open_new_frm"); /* Create path with extension */ - pathstr.length= (uint) (strxmov(path, share->normalized_path.str, reg_ext, - NullS)- path); + pathstr.length= (uint) (strxnmov(path, sizeof(path) - 1, + share->normalized_path.str, + reg_ext, + NullS) - path); pathstr.str= path; if ((parser= sql_parse_prepare(&pathstr, mem_root, 1))) diff --git a/sql/sql_base.h b/sql/sql_base.h index 78ab8c7df24..e4bb60d20a0 100644 --- a/sql/sql_base.h +++ b/sql/sql_base.h @@ -60,7 +60,8 @@ enum find_item_error_report_type {REPORT_ALL_ERRORS, REPORT_EXCEPT_NOT_FOUND, IGNORE_EXCEPT_NON_UNIQUE}; enum enum_tdc_remove_table_type {TDC_RT_REMOVE_ALL, TDC_RT_REMOVE_NOT_OWN, - TDC_RT_REMOVE_UNUSED}; + TDC_RT_REMOVE_UNUSED, + TDC_RT_REMOVE_NOT_OWN_KEEP_SHARE}; /* bits for last argument to remove_table_from_cache() */ #define RTFC_NO_FLAG 0x0000 @@ -76,13 +77,12 @@ bool table_def_init(void); void table_def_free(void); void table_def_start_shutdown(void); void assign_new_table_id(TABLE_SHARE *share); -uint cached_open_tables(void); uint cached_table_definitions(void); -uint create_table_def_key(THD *thd, char *key, - const TABLE_LIST *table_list, - bool tmp_table); -TABLE_SHARE *get_table_share(THD *thd, TABLE_LIST *table_list, char *key, - uint key_length, uint db_flags, int *error, +uint cached_open_tables(void); +uint get_table_def_key(const TABLE_LIST *table_list, const char **key); +TABLE_SHARE *get_table_share(THD *thd, TABLE_LIST *table_list, + const char *key, uint key_length, + uint db_flags, int *error, my_hash_value_type hash_value); void release_table_share(TABLE_SHARE *share); TABLE_SHARE *get_cached_table_share(const char *db, const char *table_name); @@ -128,6 +128,11 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type update, */ #define MYSQL_OPEN_SKIP_SCOPED_MDL_LOCK 0x1000 #define MYSQL_LOCK_NOT_TEMPORARY 0x2000 +/** + Only check THD::killed if waits happen (e.g. wait on MDL, wait on + table flush, wait on thr_lock.c locks) while opening and locking table. +*/ +#define MYSQL_OPEN_IGNORE_KILLED 0x4000 /** Please refer to the internals manual. */ #define MYSQL_OPEN_REOPEN (MYSQL_OPEN_IGNORE_FLUSH |\ @@ -140,6 +145,7 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type update, bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, Open_table_context *ot_ctx); + bool open_new_frm(THD *thd, TABLE_SHARE *share, const char *alias, uint db_stat, uint prgflag, uint ha_open_flags, TABLE *outparam, TABLE_LIST *table_desc, @@ -149,7 +155,8 @@ bool get_key_map_from_key_list(key_map *map, TABLE *table, List<String> *index_list); TABLE *open_table_uncached(THD *thd, const char *path, const char *db, const char *table_name, - bool add_to_temporary_tables_list); + bool add_to_temporary_tables_list, + bool open_in_engine); TABLE *find_locked_table(TABLE *list, const char *db, const char *table_name); TABLE *find_write_locked_table(TABLE *list, const char *db, const char *table_name); @@ -158,7 +165,7 @@ thr_lock_type read_lock_type_for_table(THD *thd, TABLE_LIST *table_list); my_bool mysql_rm_tmp_tables(void); -bool rm_temporary_table(handlerton *base, char *path); +bool rm_temporary_table(handlerton *base, const char *path); void close_tables_for_reopen(THD *thd, TABLE_LIST **tables, const MDL_savepoint &start_of_statement_svp); TABLE_LIST *find_table_in_list(TABLE_LIST *table, @@ -191,6 +198,8 @@ bool setup_fields(THD *thd, Item** ref_pointer_array, List<Item> &item, enum_mark_columns mark_used_columns, List<Item> *sum_func_list, bool allow_sum_func); void unfix_fields(List<Item> &items); +bool fill_record(THD * thd, TABLE *table_arg, List<Item> &fields, + List<Item> &values, bool ignore_errors); bool fill_record(THD *thd, TABLE *table, Field **field, List<Item> &values, bool ignore_errors, bool use_value); @@ -260,7 +269,7 @@ bool lock_tables(THD *thd, TABLE_LIST *tables, uint counter, uint flags); int decide_logging_format(THD *thd, TABLE_LIST *tables); void free_io_cache(TABLE *entry); void intern_close_table(TABLE *entry); -bool close_thread_table(THD *thd, TABLE **table_ptr); +void close_thread_table(THD *thd, TABLE **table_ptr); bool close_temporary_tables(THD *thd); TABLE_LIST *unique_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list, bool check_alias); @@ -270,6 +279,8 @@ void close_temporary_table(THD *thd, TABLE *table, bool free_share, void close_temporary(TABLE *table, bool free_share, bool delete_table); bool rename_temporary_table(THD* thd, TABLE *table, const char *new_db, const char *table_name); +bool open_temporary_tables(THD *thd, TABLE_LIST *tl_list); +bool open_temporary_table(THD *thd, TABLE_LIST *tl); bool is_equal(const LEX_STRING *a, const LEX_STRING *b); class Open_tables_backup; @@ -290,13 +301,14 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables, bool wait_for_refresh, ulong timeout); bool close_cached_connection_tables(THD *thd, LEX_STRING *connect_string); void close_all_tables_for_name(THD *thd, TABLE_SHARE *share, - ha_extra_function extra); + ha_extra_function extra, + TABLE *skip_table); OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *db, const char *wild); void tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type, const char *db, const char *table_name, bool has_lock); bool tdc_open_view(THD *thd, TABLE_LIST *table_list, const char *alias, - char *cache_key, uint cache_key_length, + const char *cache_key, uint cache_key_length, MEM_ROOT *mem_root, uint flags); void tdc_flush_unused_tables(); TABLE *find_table_for_mdl_upgrade(THD *thd, const char *db, @@ -457,11 +469,6 @@ class Lock_tables_prelocking_strategy : public DML_prelocking_strategy class Alter_table_prelocking_strategy : public Prelocking_strategy { public: - - Alter_table_prelocking_strategy(Alter_info *alter_info) - : m_alter_info(alter_info) - {} - virtual bool handle_routine(THD *thd, Query_tables_list *prelocking_ctx, Sroutine_hash_entry *rt, sp_head *sp, bool *need_prelocking); @@ -469,9 +476,6 @@ public: TABLE_LIST *table_list, bool *need_prelocking); virtual bool handle_view(THD *thd, Query_tables_list *prelocking_ctx, TABLE_LIST *table_list, bool *need_prelocking); - -private: - Alter_info *m_alter_info; }; @@ -593,6 +597,30 @@ private: /** + Check if a TABLE_LIST instance represents a pre-opened temporary table. +*/ + +inline bool is_temporary_table(TABLE_LIST *tl) +{ + if (tl->view || tl->schema_table) + return FALSE; + + if (!tl->table) + return FALSE; + + /* + NOTE: 'table->s' might be NULL for specially constructed TABLE + instances. See SHOW TRIGGERS for example. + */ + + if (!tl->table->s) + return FALSE; + + return tl->table->s->tmp_table != NO_TMP_TABLE; +} + + +/** This internal handler is used to trap ER_NO_SUCH_TABLE. */ @@ -606,9 +634,9 @@ public: bool handle_condition(THD *thd, uint sql_errno, const char* sqlstate, - MYSQL_ERROR::enum_warning_level level, + Sql_condition::enum_warning_level level, const char* msg, - MYSQL_ERROR ** cond_hdl); + Sql_condition ** cond_hdl); /** Returns TRUE if one or more ER_NO_SUCH_TABLE errors have been diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index dca0da651f7..3dd57562e08 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -1173,7 +1173,7 @@ void Query_cache::end_of_result(THD *thd) DBUG_VOID_RETURN; /* Ensure that only complete results are cached. */ - DBUG_ASSERT(thd->stmt_da->is_eof()); + DBUG_ASSERT(thd->get_stmt_da()->is_eof()); if (thd->killed) { @@ -2089,8 +2089,8 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d", response, we can't handle it anyway. */ (void) trans_commit_stmt(thd); - if (!thd->stmt_da->is_set()) - thd->stmt_da->disable_status(); + if (!thd->get_stmt_da()->is_set()) + thd->get_stmt_da()->disable_status(); BLOCK_UNLOCK_RD(query_block); MYSQL_QUERY_CACHE_HIT(thd->query(), (ulong) thd->limit_found_rows); diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 3f95f13c464..253e2388d65 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -691,7 +691,7 @@ int thd_tx_is_read_only(const THD *thd) extern "C" void thd_inc_row_count(THD *thd) { - thd->warning_info->inc_current_row_for_warning(); + thd->get_stmt_da()->inc_current_row_for_warning(); } @@ -810,9 +810,9 @@ char *thd_security_context(THD *thd, char *buffer, unsigned int length, bool Drop_table_error_handler::handle_condition(THD *thd, uint sql_errno, const char* sqlstate, - MYSQL_ERROR::enum_warning_level level, + Sql_condition::enum_warning_level level, const char* msg, - MYSQL_ERROR ** cond_hdl) + Sql_condition ** cond_hdl) { *cond_hdl= NULL; return ((sql_errno == EE_DELETE && my_errno == ENOENT) || @@ -835,8 +835,6 @@ THD::THD() stmt_depends_on_first_successful_insert_id_in_prev_stmt(FALSE), m_examined_row_count(0), accessed_rows_and_keys(0), - warning_info(&main_warning_info), - stmt_da(&main_da), m_statement_psi(NULL), m_idle_psi(NULL), m_server_idle(false), @@ -856,7 +854,8 @@ THD::THD() #if defined(ENABLED_DEBUG_SYNC) debug_sync_control(0), #endif /* defined(ENABLED_DEBUG_SYNC) */ - main_warning_info(0, false, false) + main_da(0, false), + m_stmt_da(&main_da) { ulong tmp; @@ -868,8 +867,8 @@ THD::THD() THD *old_THR_THD= current_thd; set_current_thd(this); status_var.memory_used= 0; + main_da.init(); - main_warning_info.init(); /* Pass nominal parameters to init_alloc_root only to ensure that the destructor works OK in case of an error. The main_mem_root @@ -1029,9 +1028,9 @@ void THD::push_internal_handler(Internal_error_handler *handler) bool THD::handle_condition(uint sql_errno, const char* sqlstate, - MYSQL_ERROR::enum_warning_level level, + Sql_condition::enum_warning_level level, const char* msg, - MYSQL_ERROR ** cond_hdl) + Sql_condition ** cond_hdl) { if (!m_internal_handler) { @@ -1068,7 +1067,7 @@ void THD::raise_error(uint sql_errno) const char* msg= ER(sql_errno); (void) raise_condition(sql_errno, NULL, - MYSQL_ERROR::WARN_LEVEL_ERROR, + Sql_condition::WARN_LEVEL_ERROR, msg); } @@ -1084,7 +1083,7 @@ void THD::raise_error_printf(uint sql_errno, ...) va_end(args); (void) raise_condition(sql_errno, NULL, - MYSQL_ERROR::WARN_LEVEL_ERROR, + Sql_condition::WARN_LEVEL_ERROR, ebuff); DBUG_VOID_RETURN; } @@ -1094,7 +1093,7 @@ void THD::raise_warning(uint sql_errno) const char* msg= ER(sql_errno); (void) raise_condition(sql_errno, NULL, - MYSQL_ERROR::WARN_LEVEL_WARN, + Sql_condition::WARN_LEVEL_WARN, msg); } @@ -1110,7 +1109,7 @@ void THD::raise_warning_printf(uint sql_errno, ...) va_end(args); (void) raise_condition(sql_errno, NULL, - MYSQL_ERROR::WARN_LEVEL_WARN, + Sql_condition::WARN_LEVEL_WARN, ebuff); DBUG_VOID_RETURN; } @@ -1124,7 +1123,7 @@ void THD::raise_note(uint sql_errno) const char* msg= ER(sql_errno); (void) raise_condition(sql_errno, NULL, - MYSQL_ERROR::WARN_LEVEL_NOTE, + Sql_condition::WARN_LEVEL_NOTE, msg); DBUG_VOID_RETURN; } @@ -1143,24 +1142,25 @@ void THD::raise_note_printf(uint sql_errno, ...) va_end(args); (void) raise_condition(sql_errno, NULL, - MYSQL_ERROR::WARN_LEVEL_NOTE, + Sql_condition::WARN_LEVEL_NOTE, ebuff); DBUG_VOID_RETURN; } -MYSQL_ERROR* THD::raise_condition(uint sql_errno, +Sql_condition* THD::raise_condition(uint sql_errno, const char* sqlstate, - MYSQL_ERROR::enum_warning_level level, + Sql_condition::enum_warning_level level, const char* msg) { - MYSQL_ERROR *cond= NULL; + Diagnostics_area *da= get_stmt_da(); + Sql_condition *cond= NULL; DBUG_ENTER("THD::raise_condition"); if (!(variables.option_bits & OPTION_SQL_NOTES) && - (level == MYSQL_ERROR::WARN_LEVEL_NOTE)) + (level == Sql_condition::WARN_LEVEL_NOTE)) DBUG_RETURN(NULL); - warning_info->opt_clear_warning_info(query_id); + da->opt_clear_warning_info(query_id); /* TODO: replace by DBUG_ASSERT(sql_errno != 0) once all bugs similar to @@ -1174,24 +1174,24 @@ MYSQL_ERROR* THD::raise_condition(uint sql_errno, if (sqlstate == NULL) sqlstate= mysql_errno_to_sqlstate(sql_errno); - if ((level == MYSQL_ERROR::WARN_LEVEL_WARN) && + if ((level == Sql_condition::WARN_LEVEL_WARN) && really_abort_on_warning()) { /* FIXME: push_warning and strict SQL_MODE case. */ - level= MYSQL_ERROR::WARN_LEVEL_ERROR; + level= Sql_condition::WARN_LEVEL_ERROR; killed= KILL_BAD_DATA; } switch (level) { - case MYSQL_ERROR::WARN_LEVEL_NOTE: - case MYSQL_ERROR::WARN_LEVEL_WARN: + case Sql_condition::WARN_LEVEL_NOTE: + case Sql_condition::WARN_LEVEL_WARN: got_warning= 1; break; - case MYSQL_ERROR::WARN_LEVEL_ERROR: + case Sql_condition::WARN_LEVEL_ERROR: break; default: DBUG_ASSERT(FALSE); @@ -1200,14 +1200,14 @@ MYSQL_ERROR* THD::raise_condition(uint sql_errno, if (handle_condition(sql_errno, sqlstate, level, msg, &cond)) DBUG_RETURN(cond); - if (level == MYSQL_ERROR::WARN_LEVEL_ERROR) + if (level == Sql_condition::WARN_LEVEL_ERROR) { is_slave_error= 1; // needed to catch query errors during replication - if (! stmt_da->is_error()) + if (!da->is_error()) { set_row_count_func(-1); - stmt_da->set_error_status(this, sql_errno, msg, sqlstate); + da->set_error_status(sql_errno, msg, sqlstate, cond); } } @@ -1216,7 +1216,7 @@ MYSQL_ERROR* THD::raise_condition(uint sql_errno, /* When simulating OOM, skip writing to error log to avoid mtr errors */ DBUG_EXECUTE_IF("simulate_out_of_memory", DBUG_RETURN(NULL);); - cond= warning_info->push_warning(this, sql_errno, sqlstate, level, msg); + da->push_warning(this, sql_errno, sqlstate, level, msg); DBUG_RETURN(cond); } @@ -1553,7 +1553,7 @@ THD::~THD() #endif free_root(&main_mem_root, MYF(0)); - main_warning_info.free_memory(); + main_da.free_memory(); if (status_var.memory_used != 0) { DBUG_PRINT("error", ("memory_used: %lld", status_var.memory_used)); @@ -1942,42 +1942,6 @@ void THD::cleanup_after_query() table_map_for_update= 0; m_binlog_invoker= FALSE; -extern "C" enum durability_properties thd_get_durability_property(const MYSQL_THD thd) -{ - enum durability_properties ret= HA_REGULAR_DURABILITY; - - if (thd != NULL) - ret= thd->durability_property; - - return ret; -} - -/** Get the auto_increment_offset auto_increment_increment. -Needed by InnoDB. -@param thd Thread object -@param off auto_increment_offset -@param inc auto_increment_increment */ -extern "C" void thd_get_autoinc(const MYSQL_THD thd, ulong* off, ulong* inc) -{ - *off = thd->variables.auto_increment_offset; - *inc = thd->variables.auto_increment_increment; -} - - -/** - Is strict sql_mode set. - Needed by InnoDB. - @param thd Thread object - @return True if sql_mode has strict mode (all or trans). - @retval true sql_mode has strict mode (all or trans). - @retval false sql_mode has not strict mode (all or trans). -*/ -extern "C" bool thd_is_strict_mode(const MYSQL_THD thd) -{ - return thd->is_strict_mode(); -} - - #ifndef EMBEDDED_LIBRARY if (rli_slave) rli_slave->cleanup_after_query(); @@ -2691,7 +2655,7 @@ select_export::prepare(List<Item> &list, SELECT_LEX_UNIT *u) Non-ASCII separator arguments are not fully supported */ - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning(thd, Sql_condition::WARN_LEVEL_WARN, WARN_NON_ASCII_SEPARATOR_NOT_IMPLEMENTED, ER(WARN_NON_ASCII_SEPARATOR_NOT_IMPLEMENTED)); } @@ -2722,7 +2686,7 @@ select_export::prepare(List<Item> &list, SELECT_LEX_UNIT *u) (exchange->opt_enclosed && non_string_results && field_term_length && strchr(NUMERIC_CHARS, field_term_char))) { - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning(thd, Sql_condition::WARN_LEVEL_WARN, ER_AMBIGUOUS_FIELD_TERM, ER(ER_AMBIGUOUS_FIELD_TERM)); is_ambiguous_field_term= TRUE; } @@ -2805,7 +2769,7 @@ int select_export::send_data(List<Item> &items) convert_to_printable(printable_buff, sizeof(printable_buff), error_pos, res->ptr() + res->length() - error_pos, res->charset(), 6); - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_TRUNCATED_WRONG_VALUE_FOR_FIELD, ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD), "string", printable_buff, @@ -2816,7 +2780,7 @@ int select_export::send_data(List<Item> &items) /* result is longer than UINT_MAX32 and doesn't fit into String */ - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, ER(WARN_DATA_TRUNCATED), item->full_name(), static_cast<long>(row_count)); } @@ -3621,7 +3585,7 @@ int select_dumpvar::send_data(List<Item> &items) bool select_dumpvar::send_eof() { if (! row_count) - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning(thd, Sql_condition::WARN_LEVEL_WARN, ER_SP_FETCH_NO_DATA, ER(ER_SP_FETCH_NO_DATA)); /* Don't send EOF if we're in error condition (which implies we've already @@ -4202,6 +4166,41 @@ extern "C" bool thd_sqlcom_can_generate_row_events(const MYSQL_THD thd) } +extern "C" enum durability_properties thd_get_durability_property(const MYSQL_THD thd) +{ + enum durability_properties ret= HA_REGULAR_DURABILITY; + + if (thd != NULL) + ret= thd->durability_property; + + return ret; +} + +/** Get the auto_increment_offset auto_increment_increment. +Needed by InnoDB. +@param thd Thread object +@param off auto_increment_offset +@param inc auto_increment_increment */ +extern "C" void thd_get_autoinc(const MYSQL_THD thd, ulong* off, ulong* inc) +{ + *off = thd->variables.auto_increment_offset; + *inc = thd->variables.auto_increment_increment; +} + + +/** + Is strict sql_mode set. + Needed by InnoDB. + @param thd Thread object + @return True if sql_mode has strict mode (all or trans). + @retval true sql_mode has strict mode (all or trans). + @retval false sql_mode has not strict mode (all or trans). +*/ +extern "C" bool thd_is_strict_mode(const MYSQL_THD thd) +{ + return thd->is_strict_mode(); +} + /* Interface for MySQL Server, plugins and storage engines to report @@ -5691,7 +5690,7 @@ void THD::issue_unsafe_warnings() { if ((unsafe_type_flags & (1 << unsafe_type)) != 0) { - push_warning_printf(this, MYSQL_ERROR::WARN_LEVEL_NOTE, + push_warning_printf(this, Sql_condition::WARN_LEVEL_NOTE, ER_BINLOG_UNSAFE_STATEMENT, ER(ER_BINLOG_UNSAFE_STATEMENT), ER(LEX::binlog_stmt_unsafe_errcode[unsafe_type])); diff --git a/sql/sql_class.h b/sql/sql_class.h index ba9f75b5d84..c2b2723f78e 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -72,7 +72,6 @@ class Sroutine_hash_entry; class User_level_lock; class user_var_entry; -enum enum_enable_or_disable { LEAVE_AS_IS, ENABLE, DISABLE }; enum enum_ha_read_modes { RFIRST, RNEXT, RPREV, RLAST, RKEY, RNEXT_SAME }; enum enum_duplicates { DUP_ERROR, DUP_REPLACE, DUP_UPDATE }; enum enum_delay_key_write { DELAY_KEY_WRITE_NONE, DELAY_KEY_WRITE_ON, @@ -235,6 +234,7 @@ public: enum drop_type {KEY, COLUMN, FOREIGN_KEY }; const char *name; enum drop_type type; + bool drop_if_exists; Alter_drop(enum drop_type par_type,const char *par_name) :name(par_name), type(par_type) { @@ -303,7 +303,6 @@ public: { return new (mem_root) Key(*this, mem_root); } }; -class Table_ident; class Foreign_key: public Key { public: @@ -318,23 +317,25 @@ public: uint delete_opt, update_opt, match_opt; Foreign_key(const LEX_STRING &name_arg, List<Key_part_spec> &cols, const LEX_STRING &ref_db_arg, const LEX_STRING &ref_table_arg, - List<Key_part_spec> &ref_cols, + List<Key_part_spec> &ref_cols, uint delete_opt_arg, uint update_opt_arg, uint match_opt_arg) :Key(FOREIGN_KEY, name_arg, &default_key_create_info, 0, cols, NULL), ref_db(ref_db_arg), ref_table(ref_table_arg), ref_columns(ref_cols), delete_opt(delete_opt_arg), update_opt(update_opt_arg), match_opt(match_opt_arg) - { + { // We don't check for duplicate FKs. key_create_info.check_for_duplicate_indexes= false; } - Foreign_key(const Foreign_key &rhs, MEM_ROOT *mem_root); + Foreign_key(const Foreign_key &rhs, MEM_ROOT *mem_root); /** Used to make a clone of this object for ALTER/CREATE TABLE @sa comment for Key_part_spec::clone */ virtual Key *clone(MEM_ROOT *mem_root) const { return new (mem_root) Foreign_key(*this, mem_root); } + /* Used to validate foreign key options */ + bool validate(List<Create_field> &table_fields); }; typedef struct st_mysql_lock @@ -1348,9 +1349,9 @@ public: virtual bool handle_condition(THD *thd, uint sql_errno, const char* sqlstate, - MYSQL_ERROR::enum_warning_level level, + Sql_condition::enum_warning_level level, const char* msg, - MYSQL_ERROR ** cond_hdl) = 0; + Sql_condition ** cond_hdl) = 0; private: Internal_error_handler *m_prev_internal_handler; @@ -1369,9 +1370,9 @@ public: bool handle_condition(THD *thd, uint sql_errno, const char* sqlstate, - MYSQL_ERROR::enum_warning_level level, + Sql_condition::enum_warning_level level, const char* msg, - MYSQL_ERROR ** cond_hdl) + Sql_condition ** cond_hdl) { /* Ignore error */ return TRUE; @@ -1396,9 +1397,9 @@ public: bool handle_condition(THD *thd, uint sql_errno, const char* sqlstate, - MYSQL_ERROR::enum_warning_level level, + Sql_condition::enum_warning_level level, const char* msg, - MYSQL_ERROR ** cond_hdl); + Sql_condition ** cond_hdl); private: }; @@ -2216,8 +2217,6 @@ public: USER_CONN *user_connect; CHARSET_INFO *db_charset; - Warning_info *warning_info; - Diagnostics_area *stmt_da; #if defined(ENABLED_PROFILING) PROFILING profiling; #endif @@ -2756,8 +2755,8 @@ public: inline void clear_error() { DBUG_ENTER("clear_error"); - if (stmt_da->is_error()) - stmt_da->reset_diagnostics_area(); + if (get_stmt_da()->is_error()) + get_stmt_da()->reset_diagnostics_area(); is_slave_error= 0; DBUG_VOID_RETURN; } @@ -2784,7 +2783,7 @@ public: */ inline void fatal_error() { - DBUG_ASSERT(stmt_da->is_error() || killed); + DBUG_ASSERT(get_stmt_da()->is_error() || killed); is_fatal_error= 1; DBUG_PRINT("error",("Fatal error set")); } @@ -2801,11 +2800,19 @@ public: To raise this flag, use my_error(). */ - inline bool is_error() const { return stmt_da->is_error(); } + inline bool is_error() const { return m_stmt_da->is_error(); } /// Returns Diagnostics-area for the current statement. Diagnostics_area *get_stmt_da() - { return stmt_da; } + { return m_stmt_da; } + + /// Returns Diagnostics-area for the current statement. + const Diagnostics_area *get_stmt_da() const + { return m_stmt_da; } + + /// Sets Diagnostics-area for the current statement. + void set_stmt_da(Diagnostics_area *da) + { m_stmt_da= da; } inline CHARSET_INFO *charset() { return variables.character_set_client; } void update_charset(); @@ -3096,6 +3103,7 @@ public: */ void push_internal_handler(Internal_error_handler *handler); +private: /** Handle a sql condition. @param sql_errno the condition error number @@ -3105,12 +3113,13 @@ public: @param[out] cond_hdl the sql condition raised, if any @return true if the condition is handled */ - virtual bool handle_condition(uint sql_errno, - const char* sqlstate, - MYSQL_ERROR::enum_warning_level level, - const char* msg, - MYSQL_ERROR ** cond_hdl); + bool handle_condition(uint sql_errno, + const char* sqlstate, + Sql_condition::enum_warning_level level, + const char* msg, + Sql_condition ** cond_hdl); +public: /** Remove the error handler last pushed. */ @@ -3160,10 +3169,10 @@ private: To raise a SQL condition, the code should use the public raise_error() or raise_warning() methods provided by class THD. */ - friend class Signal_common; - friend class Signal_statement; - friend class Resignal_statement; - friend void push_warning(THD*, MYSQL_ERROR::enum_warning_level, uint, const char*); + friend class Sql_cmd_common_signal; + friend class Sql_cmd_signal; + friend class Sql_cmd_resignal; + friend void push_warning(THD*, Sql_condition::enum_warning_level, uint, const char*); friend void my_message_sql(uint, const char *, myf); /** @@ -3174,10 +3183,10 @@ private: @param msg the condition message text @return The condition raised, or NULL */ - MYSQL_ERROR* + Sql_condition* raise_condition(uint sql_errno, const char* sqlstate, - MYSQL_ERROR::enum_warning_level level, + Sql_condition::enum_warning_level level, const char* msg); public: @@ -3307,8 +3316,8 @@ private: tree itself is reused between executions and thus is stored elsewhere. */ MEM_ROOT main_mem_root; - Warning_info main_warning_info; Diagnostics_area main_da; + Diagnostics_area *m_stmt_da; /** It will be set TURE if CURRENT_USER() is called in account management @@ -3341,24 +3350,24 @@ private: }; -/** A short cut for thd->stmt_da->set_ok_status(). */ +/** A short cut for thd->get_stmt_da()->set_ok_status(). */ inline void my_ok(THD *thd, ulonglong affected_rows= 0, ulonglong id= 0, const char *message= NULL) { thd->set_row_count_func(affected_rows); - thd->stmt_da->set_ok_status(thd, affected_rows, id, message); + thd->get_stmt_da()->set_ok_status(affected_rows, id, message); } -/** A short cut for thd->stmt_da->set_eof_status(). */ +/** A short cut for thd->get_stmt_da()->set_eof_status(). */ inline void my_eof(THD *thd) { thd->set_row_count_func(-1); - thd->stmt_da->set_eof_status(thd); + thd->get_stmt_da()->set_eof_status(thd); } #define tmp_disable_binlog(A) \ diff --git a/sql/sql_cmd.h b/sql/sql_cmd.h index 5cf88d2566c..f21fc399b94 100644 --- a/sql/sql_cmd.h +++ b/sql/sql_cmd.h @@ -88,6 +88,7 @@ enum enum_sql_command { SQLCOM_SHOW_PROFILE, SQLCOM_SHOW_PROFILES, SQLCOM_SIGNAL, SQLCOM_RESIGNAL, SQLCOM_SHOW_RELAYLOG_EVENTS, + SQLCOM_GET_DIAGNOSTICS, SQLCOM_SHOW_USER_STATS, SQLCOM_SHOW_TABLE_STATS, SQLCOM_SHOW_INDEX_STATS, SQLCOM_SHOW_CLIENT_STATS, SQLCOM_SLAVE_ALL_START, SQLCOM_SLAVE_ALL_STOP, diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc index 33b5394de9f..a8327f0d1f6 100644 --- a/sql/sql_connect.cc +++ b/sql/sql_connect.cc @@ -1120,7 +1120,7 @@ void prepare_new_connection_state(THD* thd) { thd->killed= KILL_CONNECTION; thd->print_aborted_warning(0, "init_connect command failed"); - sql_print_warning("%s", thd->stmt_da->message()); + sql_print_warning("%s", thd->get_stmt_da()->message()); /* now let client to send its first command, diff --git a/sql/sql_crypt.h b/sql/sql_crypt.h index 3a12d603601..3df554e9d31 100644 --- a/sql/sql_crypt.h +++ b/sql/sql_crypt.h @@ -22,7 +22,7 @@ #endif #include "sql_list.h" /* Sql_alloc */ -#include "mysql_com.h" /* rand_struct */ +#include "my_rnd.h" /* rand_struct */ class SQL_CRYPT :public Sql_alloc { diff --git a/sql/sql_db.cc b/sql/sql_db.cc index 87650e643f2..f0bb5448520 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -32,6 +32,7 @@ #include "log_event.h" // Query_log_event #include "sql_base.h" // lock_table_names, tdc_remove_table #include "sql_handler.h" // mysql_ha_rm_tables +#include "sql_class.h" #include <mysys_err.h> #include "sp_head.h" #include "sp.h" @@ -575,7 +576,7 @@ int mysql_create_db(THD *thd, char *db, HA_CREATE_INFO *create_info, error= -1; goto exit; } - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_DB_CREATE_EXISTS, ER(ER_DB_CREATE_EXISTS), db); error= 0; goto not_silent; @@ -784,7 +785,7 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent) } else { - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_DB_DROP_EXISTS, ER(ER_DB_DROP_EXISTS), db); error= false; goto update_binlog; @@ -1537,7 +1538,7 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch) { /* Throw a warning and free new_db_file_name. */ - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_BAD_DB_ERROR, ER(ER_BAD_DB_ERROR), new_db_file_name.str); diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc index 526d2445d3d..5997aa03cb0 100644 --- a/sql/sql_derived.cc +++ b/sql/sql_derived.cc @@ -666,9 +666,9 @@ exit: if (derived->view) { if (thd->is_error() && - (thd->stmt_da->sql_errno() == ER_BAD_FIELD_ERROR || - thd->stmt_da->sql_errno() == ER_FUNC_INEXISTENT_NAME_COLLISION || - thd->stmt_da->sql_errno() == ER_SP_DOES_NOT_EXIST)) + (thd->get_stmt_da()->sql_errno() == ER_BAD_FIELD_ERROR || + thd->get_stmt_da()->sql_errno() == ER_FUNC_INEXISTENT_NAME_COLLISION || + thd->get_stmt_da()->sql_errno() == ER_SP_DOES_NOT_EXIST)) { thd->clear_error(); my_error(ER_VIEW_INVALID, MYF(0), derived->db, diff --git a/sql/sql_error.cc b/sql/sql_error.cc index 038f4876b14..fc5beb31218 100644 --- a/sql/sql_error.cc +++ b/sql/sql_error.cc @@ -47,12 +47,12 @@ This file contains the implementation of error and warnings related #include "sp_rcontext.h" /* - Design notes about MYSQL_ERROR::m_message_text. + Design notes about Sql_condition::m_message_text. - The member MYSQL_ERROR::m_message_text contains the text associated with + The member Sql_condition::m_message_text contains the text associated with an error, warning or note (which are all SQL 'conditions') - Producer of MYSQL_ERROR::m_message_text: + Producer of Sql_condition::m_message_text: ---------------------------------------- (#1) the server implementation itself, when invoking functions like @@ -78,16 +78,16 @@ This file contains the implementation of error and warnings related - a RESIGNAL statement, the message text is provided by the user logic, and is expressed in UTF8. - Storage of MYSQL_ERROR::m_message_text: + Storage of Sql_condition::m_message_text: --------------------------------------- - (#4) The class MYSQL_ERROR is used to hold the message text member. + (#4) The class Sql_condition is used to hold the message text member. This class represents a single SQL condition. (#5) The class Warning_info represents a SQL condition area, and contains a collection of SQL conditions in the Warning_info::m_warn_list - Consumer of MYSQL_ERROR::m_message_text: + Consumer of Sql_condition::m_message_text: ---------------------------------------- (#6) The statements SHOW WARNINGS and SHOW ERRORS display the content of @@ -97,9 +97,9 @@ This file contains the implementation of error and warnings related also read the content of: - the top level statement condition area (when executed in a query), - a sub statement (when executed in a stored program) - and return the data stored in a MYSQL_ERROR. + and return the data stored in a Sql_condition. - (#8) The RESIGNAL statement reads the MYSQL_ERROR caught by an exception + (#8) The RESIGNAL statement reads the Sql_condition caught by an exception handler, to raise a new or modified condition (in #3). The big picture @@ -113,7 +113,7 @@ This file contains the implementation of error and warnings related ----------------------------|---------------------------- | | | V | - MYSQL_ERROR(#4) | + Sql_condition(#4) | | | | | V | @@ -151,10 +151,10 @@ This file contains the implementation of error and warnings related As a result, the design choice for (#4) and (#5) is to store data in the 'error_message_charset_info' CHARSET, to minimize impact on the code base. - This is implemented by using 'String MYSQL_ERROR::m_message_text'. + This is implemented by using 'String Sql_condition::m_message_text'. The UTF8 -> error_message_charset_info conversion is implemented in - Signal_common::eval_signal_informations() (for path #B and #C). + Sql_cmd_common_signal::eval_signal_informations() (for path #B and #C). Future work ----------- @@ -164,14 +164,14 @@ This file contains the implementation of error and warnings related - Change (#4 and #5) to store message text in UTF8 natively. In practice, this means changing the type of the message text to - '<UTF8 String 128 class> MYSQL_ERROR::m_message_text', and is a direct + '<UTF8 String 128 class> Sql_condition::m_message_text', and is a direct consequence of WL#751. - Implement (#9) (GET DIAGNOSTICS). See WL#2111 (Stored Procedures: Implement GET DIAGNOSTICS) */ -MYSQL_ERROR::MYSQL_ERROR() +Sql_condition::Sql_condition() : Sql_alloc(), m_class_origin((const char*) NULL, 0, & my_charset_utf8_bin), m_subclass_origin((const char*) NULL, 0, & my_charset_utf8_bin), @@ -185,20 +185,20 @@ MYSQL_ERROR::MYSQL_ERROR() m_cursor_name((const char*) NULL, 0, & my_charset_utf8_bin), m_message_text(), m_sql_errno(0), - m_level(MYSQL_ERROR::WARN_LEVEL_ERROR), + m_level(Sql_condition::WARN_LEVEL_ERROR), m_mem_root(NULL) { memset(m_returned_sqlstate, 0, sizeof(m_returned_sqlstate)); } -void MYSQL_ERROR::init(MEM_ROOT *mem_root) +void Sql_condition::init(MEM_ROOT *mem_root) { DBUG_ASSERT(mem_root != NULL); DBUG_ASSERT(m_mem_root == NULL); m_mem_root= mem_root; } -void MYSQL_ERROR::clear() +void Sql_condition::clear() { m_class_origin.length(0); m_subclass_origin.length(0); @@ -212,10 +212,10 @@ void MYSQL_ERROR::clear() m_cursor_name.length(0); m_message_text.length(0); m_sql_errno= 0; - m_level= MYSQL_ERROR::WARN_LEVEL_ERROR; + m_level= Sql_condition::WARN_LEVEL_ERROR; } -MYSQL_ERROR::MYSQL_ERROR(MEM_ROOT *mem_root) +Sql_condition::Sql_condition(MEM_ROOT *mem_root) : Sql_alloc(), m_class_origin((const char*) NULL, 0, & my_charset_utf8_bin), m_subclass_origin((const char*) NULL, 0, & my_charset_utf8_bin), @@ -229,7 +229,7 @@ MYSQL_ERROR::MYSQL_ERROR(MEM_ROOT *mem_root) m_cursor_name((const char*) NULL, 0, & my_charset_utf8_bin), m_message_text(), m_sql_errno(0), - m_level(MYSQL_ERROR::WARN_LEVEL_ERROR), + m_level(Sql_condition::WARN_LEVEL_ERROR), m_mem_root(mem_root) { DBUG_ASSERT(mem_root != NULL); @@ -254,7 +254,7 @@ static void copy_string(MEM_ROOT *mem_root, String* dst, const String* src) } void -MYSQL_ERROR::copy_opt_attributes(const MYSQL_ERROR *cond) +Sql_condition::copy_opt_attributes(const Sql_condition *cond) { DBUG_ASSERT(this != cond); copy_string(m_mem_root, & m_class_origin, & cond->m_class_origin); @@ -270,8 +270,8 @@ MYSQL_ERROR::copy_opt_attributes(const MYSQL_ERROR *cond) } void -MYSQL_ERROR::set(uint sql_errno, const char* sqlstate, - MYSQL_ERROR::enum_warning_level level, const char* msg) +Sql_condition::set(uint sql_errno, const char* sqlstate, + Sql_condition::enum_warning_level level, const char* msg) { DBUG_ASSERT(sql_errno != 0); DBUG_ASSERT(sqlstate != NULL); @@ -286,11 +286,11 @@ MYSQL_ERROR::set(uint sql_errno, const char* sqlstate, } void -MYSQL_ERROR::set_builtin_message_text(const char* str) +Sql_condition::set_builtin_message_text(const char* str) { /* See the comments - "Design notes about MYSQL_ERROR::m_message_text." + "Design notes about Sql_condition::m_message_text." */ const char* copy; @@ -300,24 +300,41 @@ MYSQL_ERROR::set_builtin_message_text(const char* str) } const char* -MYSQL_ERROR::get_message_text() const +Sql_condition::get_message_text() const { return m_message_text.ptr(); } int -MYSQL_ERROR::get_message_octet_length() const +Sql_condition::get_message_octet_length() const { return m_message_text.length(); } void -MYSQL_ERROR::set_sqlstate(const char* sqlstate) +Sql_condition::set_sqlstate(const char* sqlstate) { memcpy(m_returned_sqlstate, sqlstate, SQLSTATE_LENGTH); m_returned_sqlstate[SQLSTATE_LENGTH]= '\0'; } +Diagnostics_area::Diagnostics_area(bool initialize) + : m_main_wi(0, false, initialize) +{ + push_warning_info(&m_main_wi); + + reset_diagnostics_area(); +} + +Diagnostics_area::Diagnostics_area(ulonglong warning_info_id, + bool allow_unlimited_warnings) + : m_main_wi(warning_info_id, allow_unlimited_warnings, true) +{ + push_warning_info(&m_main_wi); + + reset_diagnostics_area(); +} + /** Clear this diagnostics area. @@ -337,7 +354,8 @@ Diagnostics_area::reset_diagnostics_area() m_last_insert_id= 0; m_statement_warn_count= 0; #endif - is_sent= FALSE; + get_warning_info()->clear_error_condition(); + set_is_sent(false); /** Tiny reset in debug mode to see garbage right away */ m_status= DA_EMPTY; DBUG_VOID_RETURN; @@ -350,9 +368,9 @@ Diagnostics_area::reset_diagnostics_area() */ void -Diagnostics_area::set_ok_status(THD *thd, ulonglong affected_rows_arg, - ulonglong last_insert_id_arg, - const char *message_arg) +Diagnostics_area::set_ok_status(ulonglong affected_rows, + ulonglong last_insert_id, + const char *message) { DBUG_ENTER("set_ok_status"); DBUG_ASSERT(! is_set()); @@ -363,11 +381,11 @@ Diagnostics_area::set_ok_status(THD *thd, ulonglong affected_rows_arg, if (is_error() || is_disabled()) return; - m_statement_warn_count= thd->warning_info->statement_warn_count(); - m_affected_rows= affected_rows_arg; - m_last_insert_id= last_insert_id_arg; - if (message_arg) - strmake(m_message, message_arg, sizeof(m_message) - 1); + m_statement_warn_count= current_statement_warn_count(); + m_affected_rows= affected_rows; + m_last_insert_id= last_insert_id; + if (message) + strmake(m_message, message, sizeof(m_message) - 1); else m_message[0]= '\0'; m_status= DA_OK; @@ -398,20 +416,48 @@ Diagnostics_area::set_eof_status(THD *thd) anyway. */ m_statement_warn_count= (thd->spcont ? - 0 : thd->warning_info->statement_warn_count()); + 0 : + current_statement_warn_count()); m_status= DA_EOF; DBUG_VOID_RETURN; } /** - Set ERROR status. + Set ERROR status in the Diagnostics Area. This function should be used to + report fatal errors (such as out-of-memory errors) when no further + processing is possible. + + @param sql_errno SQL-condition error number */ void -Diagnostics_area::set_error_status(THD *thd, uint sql_errno_arg, - const char *message_arg, - const char *sqlstate) +Diagnostics_area::set_error_status(uint sql_errno) +{ + set_error_status(sql_errno, + ER(sql_errno), + mysql_errno_to_sqlstate(sql_errno), + NULL); +} + + +/** + Set ERROR status in the Diagnostics Area. + + @note error_condition may be NULL. It happens if a) OOM error is being + reported; or b) when Warning_info is full. + + @param sql_errno SQL-condition error number + @param message SQL-condition message + @param sqlstate SQL-condition state + @param error_condition SQL-condition object representing the error state +*/ + +void +Diagnostics_area::set_error_status(uint sql_errno, + const char *message, + const char *sqlstate, + const Sql_condition *error_condition) { DBUG_ENTER("set_error_status"); /* @@ -419,7 +465,14 @@ Diagnostics_area::set_error_status(THD *thd, uint sql_errno_arg, The only exception is when we flush the message to the client, an error can happen during the flush. */ - DBUG_ASSERT(! is_set() || can_overwrite_status); + DBUG_ASSERT(! is_set() || m_can_overwrite_status); + + // message must be set properly by the caller. + DBUG_ASSERT(message); + + // sqlstate must be set properly by the caller. + DBUG_ASSERT(sqlstate); + #ifdef DBUG_OFF /* In production, refuse to overwrite a custom response with an @@ -429,19 +482,17 @@ Diagnostics_area::set_error_status(THD *thd, uint sql_errno_arg, return; #endif - if (sqlstate == NULL) - sqlstate= mysql_errno_to_sqlstate(sql_errno_arg); - - m_sql_errno= sql_errno_arg; + m_sql_errno= sql_errno; memcpy(m_sqlstate, sqlstate, SQLSTATE_LENGTH); m_sqlstate[SQLSTATE_LENGTH]= '\0'; - strmake(m_message, message_arg, sizeof(m_message)-1); + strmake(m_message, message, sizeof(m_message)-1); + + get_warning_info()->set_error_condition(error_condition); m_status= DA_ERROR; DBUG_VOID_RETURN; } - /** Mark the diagnostics area as 'DISABLED'. @@ -459,15 +510,16 @@ Diagnostics_area::disable_status() Warning_info::Warning_info(ulonglong warn_id_arg, bool allow_unlimited_warnings, bool initialize) - :m_statement_warn_count(0), + :m_current_statement_warn_count(0), m_current_row_for_warning(1), m_warn_id(warn_id_arg), + m_error_condition(NULL), m_allow_unlimited_warnings(allow_unlimited_warnings), initialized(0), m_read_only(FALSE) { m_warn_list.empty(); - bzero((char*) m_warn_count, sizeof(m_warn_count)); + memset(m_warn_count, 0, sizeof(m_warn_count)); if (initialize) init(); } @@ -492,92 +544,164 @@ Warning_info::~Warning_info() } -/** - Reset the warning information of this connection. -*/ +bool Warning_info::has_sql_condition(const char *message_str, + ulong message_length) const +{ + Diagnostics_area::Sql_condition_iterator it(m_warn_list); + const Sql_condition *err; -void Warning_info::clear_warning_info(ulonglong warn_id_arg) + while ((err= it++)) + { + if (strncmp(message_str, err->get_message_text(), message_length) == 0) + return true; + } + + return false; +} + + +void Warning_info::clear(ulonglong new_id) { - m_warn_id= warn_id_arg; - free_memory(); - bzero((char*) m_warn_count, sizeof(m_warn_count)); + id(new_id); m_warn_list.empty(); - m_statement_warn_count= 0; + m_marked_sql_conditions.empty(); + free_memory(); + memset(m_warn_count, 0, sizeof(m_warn_count)); + m_current_statement_warn_count= 0; m_current_row_for_warning= 1; /* Start counting from the first row */ + clear_error_condition(); } -/** - Append warnings only if the original contents of the routine - warning info was replaced. -*/ -void Warning_info::merge_with_routine_info(THD *thd, Warning_info *source) +void Warning_info::append_warning_info(THD *thd, const Warning_info *source) { - /* - If a routine body is empty or if a routine did not - generate any warnings (thus m_warn_id didn't change), - do not duplicate our own contents by appending the - contents of the called routine. We know that the called - routine did not change its warning info. - - On the other hand, if the routine body is not empty and - some statement in the routine generates a warning or - uses tables, m_warn_id is guaranteed to have changed. - In this case we know that the routine warning info - contains only new warnings, and thus we perform a copy. - */ - if (m_warn_id != source->m_warn_id) + const Sql_condition *err; + Diagnostics_area::Sql_condition_iterator it(source->m_warn_list); + const Sql_condition *src_error_condition = source->get_error_condition(); + + while ((err= it++)) { - /* - If the invocation of the routine was a standalone statement, - rather than a sub-statement, in other words, if it's a CALL - of a procedure, rather than invocation of a function or a - trigger, we need to clear the current contents of the caller's - warning info. - - This is per MySQL rules: if a statement generates a warning, - warnings from the previous statement are flushed. Normally - it's done in push_warning(). However, here we don't use - push_warning() to avoid invocation of condition handlers or - escalation of warnings to errors. - */ - opt_clear_warning_info(thd->query_id); - append_warning_info(thd, source); + // Do not use ::push_warning() to avoid invocation of THD-internal-handlers. + Sql_condition *new_error= Warning_info::push_warning(thd, err); + + if (src_error_condition && src_error_condition == err) + set_error_condition(new_error); + + if (source->is_marked_for_removal(err)) + mark_condition_for_removal(new_error); } } + /** - Add a warning to the list of warnings. Increment the respective - counters. + Copy Sql_conditions that are not WARN_LEVEL_ERROR from the source + Warning_info to the current Warning_info. + + @param thd Thread context. + @param sp_wi Stored-program Warning_info + @param thd Thread context. + @param src_wi Warning_info to copy from. */ -MYSQL_ERROR *Warning_info::push_warning(THD *thd, - uint sql_errno, const char* sqlstate, - MYSQL_ERROR::enum_warning_level level, - const char *msg) +void Diagnostics_area::copy_non_errors_from_wi(THD *thd, + const Warning_info *src_wi) +{ + Sql_condition_iterator it(src_wi->m_warn_list); + const Sql_condition *cond; + Warning_info *wi= get_warning_info(); + + while ((cond= it++)) + { + if (cond->get_level() == Sql_condition::WARN_LEVEL_ERROR) + continue; + + Sql_condition *new_condition= wi->push_warning(thd, cond); + + if (src_wi->is_marked_for_removal(cond)) + wi->mark_condition_for_removal(new_condition); + } +} + + +void Warning_info::mark_sql_conditions_for_removal() +{ + Sql_condition_list::Iterator it(m_warn_list); + Sql_condition *cond; + + while ((cond= it++)) + mark_condition_for_removal(cond); +} + + +void Warning_info::remove_marked_sql_conditions() { - MYSQL_ERROR *cond= NULL; + List_iterator_fast<Sql_condition> it(m_marked_sql_conditions); + Sql_condition *cond; + + while ((cond= it++)) + { + m_warn_list.remove(cond); + m_warn_count[cond->get_level()]--; + m_current_statement_warn_count--; + if (cond == m_error_condition) + m_error_condition= NULL; + } + + m_marked_sql_conditions.empty(); +} + + +bool Warning_info::is_marked_for_removal(const Sql_condition *cond) const +{ + List_iterator_fast<Sql_condition> it( + const_cast<List<Sql_condition>&> (m_marked_sql_conditions)); + Sql_condition *c; + + while ((c= it++)) + { + if (c == cond) + return true; + } + + return false; +} + + +void Warning_info::reserve_space(THD *thd, uint count) +{ + while (m_warn_list.elements() && + (m_warn_list.elements() + count) > thd->variables.max_error_count) + m_warn_list.remove(m_warn_list.front()); +} + +Sql_condition *Warning_info::push_warning(THD *thd, + uint sql_errno, const char* sqlstate, + Sql_condition::enum_warning_level level, + const char *msg) +{ + Sql_condition *cond= NULL; if (! m_read_only) { if (m_allow_unlimited_warnings || - m_warn_list.elements < thd->variables.max_error_count) + m_warn_list.elements() < thd->variables.max_error_count) { - cond= new (& m_warn_root) MYSQL_ERROR(& m_warn_root); + cond= new (& m_warn_root) Sql_condition(& m_warn_root); if (cond) { cond->set(sql_errno, sqlstate, level, msg); - m_warn_list.push_back(cond, &m_warn_root); + m_warn_list.push_back(cond); } } m_warn_count[(uint) level]++; } - m_statement_warn_count++; + m_current_statement_warn_count++; return cond; } -MYSQL_ERROR *Warning_info::push_warning(THD *thd, const MYSQL_ERROR *sql_condition) + +Sql_condition *Warning_info::push_warning(THD *thd, const Sql_condition *sql_condition) { - MYSQL_ERROR *new_condition= push_warning(thd, + Sql_condition *new_condition= push_warning(thd, sql_condition->get_sql_errno(), sql_condition->get_sqlstate(), sql_condition->get_level(), @@ -600,7 +724,7 @@ MYSQL_ERROR *Warning_info::push_warning(THD *thd, const MYSQL_ERROR *sql_conditi msg Clear error message */ -void push_warning(THD *thd, MYSQL_ERROR::enum_warning_level level, +void push_warning(THD *thd, Sql_condition::enum_warning_level level, uint code, const char *msg) { DBUG_ENTER("push_warning"); @@ -611,15 +735,15 @@ void push_warning(THD *thd, MYSQL_ERROR::enum_warning_level level, WARN_LEVEL_ERROR *is* a bug. Either use my_printf_error(), my_error(), or WARN_LEVEL_WARN. */ - DBUG_ASSERT(level != MYSQL_ERROR::WARN_LEVEL_ERROR); + DBUG_ASSERT(level != Sql_condition::WARN_LEVEL_ERROR); - if (level == MYSQL_ERROR::WARN_LEVEL_ERROR) - level= MYSQL_ERROR::WARN_LEVEL_WARN; + if (level == Sql_condition::WARN_LEVEL_ERROR) + level= Sql_condition::WARN_LEVEL_WARN; (void) thd->raise_condition(code, NULL, level, msg); /* Make sure we also count warnings pushed after calling set_ok_status(). */ - thd->stmt_da->increment_warning(); + thd->get_stmt_da()->increment_warning(); DBUG_VOID_RETURN; } @@ -636,7 +760,7 @@ void push_warning(THD *thd, MYSQL_ERROR::enum_warning_level level, msg Clear error message */ -void push_warning_printf(THD *thd, MYSQL_ERROR::enum_warning_level level, +void push_warning_printf(THD *thd, Sql_condition::enum_warning_level level, uint code, const char *format, ...) { va_list args; @@ -685,7 +809,7 @@ bool mysqld_show_warnings(THD *thd, ulong levels_to_show) List<Item> field_list; DBUG_ENTER("mysqld_show_warnings"); - DBUG_ASSERT(thd->warning_info->is_read_only()); + DBUG_ASSERT(thd->get_stmt_da()->is_warning_info_read_only()); field_list.push_back(new Item_empty_string("Level", 7)); field_list.push_back(new Item_return_int("Code",4, MYSQL_TYPE_LONG)); @@ -695,7 +819,7 @@ bool mysqld_show_warnings(THD *thd, ulong levels_to_show) Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(TRUE); - MYSQL_ERROR *err; + const Sql_condition *err; SELECT_LEX *sel= &thd->lex->select_lex; SELECT_LEX_UNIT *unit= &thd->lex->unit; ulonglong idx= 0; @@ -703,7 +827,8 @@ bool mysqld_show_warnings(THD *thd, ulong levels_to_show) unit->set_limit(sel); - List_iterator_fast<MYSQL_ERROR> it(thd->warning_info->warn_list()); + Diagnostics_area::Sql_condition_iterator it= + thd->get_stmt_da()->sql_conditions(); while ((err= it++)) { /* Skip levels that the user is not interested in */ @@ -726,7 +851,7 @@ bool mysqld_show_warnings(THD *thd, ulong levels_to_show) } my_eof(thd); - thd->warning_info->set_read_only(FALSE); + thd->get_stmt_da()->set_warning_info_read_only(FALSE); DBUG_RETURN(FALSE); } diff --git a/sql/sql_error.h b/sql/sql_error.h index fadd3b51ec6..fb6cdfcfede 100644 --- a/sql/sql_error.h +++ b/sql/sql_error.h @@ -19,126 +19,13 @@ #include "sql_list.h" /* Sql_alloc, MEM_ROOT */ #include "m_string.h" /* LEX_STRING */ #include "sql_string.h" /* String */ +#include "sql_plist.h" /* I_P_List */ #include "mysql_com.h" /* MYSQL_ERRMSG_SIZE */ #include "my_time.h" /* MYSQL_TIME */ #include "decimal.h" class THD; - -/** - Stores status of the currently executed statement. - Cleared at the beginning of the statement, and then - can hold either OK, ERROR, or EOF status. - Can not be assigned twice per statement. -*/ - -class Diagnostics_area -{ -public: - enum enum_diagnostics_status - { - /** The area is cleared at start of a statement. */ - DA_EMPTY= 0, - /** Set whenever one calls my_ok(). */ - DA_OK, - /** Set whenever one calls my_eof(). */ - DA_EOF, - /** Set whenever one calls my_error() or my_message(). */ - DA_ERROR, - /** Set in case of a custom response, such as one from COM_STMT_PREPARE. */ - DA_DISABLED - }; - /** True if status information is sent to the client. */ - bool is_sent; - /** Set to make set_error_status after set_{ok,eof}_status possible. */ - bool can_overwrite_status; - - void set_ok_status(THD *thd, ulonglong affected_rows_arg, - ulonglong last_insert_id_arg, - const char *message); - void set_eof_status(THD *thd); - void set_error_status(THD *thd, uint sql_errno_arg, const char *message_arg, - const char *sqlstate); - - void disable_status(); - - void reset_diagnostics_area(); - - bool is_set() const { return m_status != DA_EMPTY; } - bool is_error() const { return m_status == DA_ERROR; } - bool is_eof() const { return m_status == DA_EOF; } - bool is_ok() const { return m_status == DA_OK; } - bool is_disabled() const { return m_status == DA_DISABLED; } - enum_diagnostics_status status() const { return m_status; } - - const char *message() const - { DBUG_ASSERT(m_status == DA_ERROR || m_status == DA_OK); return m_message; } - - uint sql_errno() const - { DBUG_ASSERT(m_status == DA_ERROR); return m_sql_errno; } - - const char* get_sqlstate() const - { DBUG_ASSERT(m_status == DA_ERROR); return m_sqlstate; } - - ulonglong affected_rows() const - { DBUG_ASSERT(m_status == DA_OK); return m_affected_rows; } - - ulonglong last_insert_id() const - { DBUG_ASSERT(m_status == DA_OK); return m_last_insert_id; } - - uint statement_warn_count() const - { - DBUG_ASSERT(m_status == DA_OK || m_status == DA_EOF); - return m_statement_warn_count; - } - - /* Used to count any warnings pushed after calling set_ok_status(). */ - void increment_warning() - { - if (m_status != DA_EMPTY) - m_statement_warn_count++; - } - - Diagnostics_area() { reset_diagnostics_area(); } - -private: - /** Message buffer. Can be used by OK or ERROR status. */ - char m_message[MYSQL_ERRMSG_SIZE]; - /** - SQL error number. One of ER_ codes from share/errmsg.txt. - Set by set_error_status. - */ - uint m_sql_errno; - - char m_sqlstate[SQLSTATE_LENGTH+1]; - - /** - The number of rows affected by the last statement. This is - semantically close to thd->row_count_func, but has a different - life cycle. thd->row_count_func stores the value returned by - function ROW_COUNT() and is cleared only by statements that - update its value, such as INSERT, UPDATE, DELETE and few others. - This member is cleared at the beginning of the next statement. - - We could possibly merge the two, but life cycle of thd->row_count_func - can not be changed. - */ - ulonglong m_affected_rows; - /** - Similarly to the previous member, this is a replacement of - thd->first_successful_insert_id_in_prev_stmt, which is used - to implement LAST_INSERT_ID(). - */ - ulonglong m_last_insert_id; - /** - Number of warnings of this last statement. May differ from - the number of warnings returned by SHOW WARNINGS e.g. in case - the statement doesn't clear the warnings, and doesn't generate - them. - */ - uint m_statement_warn_count; - enum_diagnostics_status m_status; -}; +class my_decimal; /////////////////////////////////////////////////////////////////////////// @@ -146,10 +33,8 @@ private: Representation of a SQL condition. A SQL condition can be a completion condition (note, warning), or an exception condition (error, not found). - @note This class is named MYSQL_ERROR instead of SQL_condition for - historical reasons, to facilitate merging code with previous releases. */ -class MYSQL_ERROR : public Sql_alloc +class Sql_condition : public Sql_alloc { public: /* @@ -160,6 +45,7 @@ public: */ enum enum_warning_level { WARN_LEVEL_NOTE, WARN_LEVEL_WARN, WARN_LEVEL_ERROR, WARN_LEVEL_END}; + /** Get the MESSAGE_TEXT of this condition. @return the message text. @@ -190,19 +76,15 @@ public: Get the error level of this condition. @return the error level condition item. */ - MYSQL_ERROR::enum_warning_level get_level() const + Sql_condition::enum_warning_level get_level() const { return m_level; } - /** Destructor. */ - ~MYSQL_ERROR() - {} - private: /* - The interface of MYSQL_ERROR is mostly private, by design, + The interface of Sql_condition is mostly private, by design, so that only the following code: - various raise_error() or raise_warning() methods in class THD, - - the implementation of SIGNAL / RESIGNAL + - the implementation of SIGNAL / RESIGNAL / GET DIAGNOSTICS - catch / re-throw of SQL conditions in stored procedures (sp_rcontext) is allowed to create / modify a SQL condition. Enforcing this policy prevents confusion, since the only public @@ -212,20 +94,21 @@ private: */ friend class THD; friend class Warning_info; - friend class Signal_common; - friend class Signal_statement; - friend class Resignal_statement; + friend class Sql_cmd_common_signal; + friend class Sql_cmd_signal; + friend class Sql_cmd_resignal; friend class sp_rcontext; + friend class Condition_information_item; /** Default constructor. This constructor is usefull when allocating arrays. - Note that the init() method should be called to complete the MYSQL_ERROR. + Note that the init() method should be called to complete the Sql_condition. */ - MYSQL_ERROR(); + Sql_condition(); /** - Complete the MYSQL_ERROR initialisation. + Complete the Sql_condition initialisation. @param mem_root The memory root to use for the condition items of this condition */ @@ -236,15 +119,17 @@ private: @param mem_root The memory root to use for the condition items of this condition */ - MYSQL_ERROR(MEM_ROOT *mem_root); - + Sql_condition(MEM_ROOT *mem_root); + /** Destructor. */ + ~Sql_condition() + {} /** Copy optional condition items attributes. @param cond the condition to copy. */ - void copy_opt_attributes(const MYSQL_ERROR *cond); + void copy_opt_attributes(const Sql_condition *cond); /** Set this condition area with a fixed message text. @@ -255,7 +140,7 @@ private: @param MyFlags additional flags. */ void set(uint sql_errno, const char* sqlstate, - MYSQL_ERROR::enum_warning_level level, + Sql_condition::enum_warning_level level, const char* msg); /** @@ -268,6 +153,12 @@ private: /** Set the SQLSTATE of this condition. */ void set_sqlstate(const char* sqlstate); + /** Set the CLASS_ORIGIN of this condition. */ + void set_class_origin(); + + /** Set the SUBCLASS_ORIGIN of this condition. */ + void set_subclass_origin(); + /** Clear this SQL condition. */ @@ -317,44 +208,48 @@ private: char m_returned_sqlstate[SQLSTATE_LENGTH+1]; /** Severity (error, warning, note) of this condition. */ - MYSQL_ERROR::enum_warning_level m_level; + Sql_condition::enum_warning_level m_level; + + /** Pointers for participating in the list of conditions. */ + Sql_condition *next_in_wi; + Sql_condition **prev_in_wi; /** Memory root to use to hold condition item values. */ MEM_ROOT *m_mem_root; }; -class Sql_condition : public MYSQL_ERROR -{ - /* - Wrapper class to allow one to use Sql_condition in handlers instead of - MYSQL_ERROR - */ -}; - /////////////////////////////////////////////////////////////////////////// /** Information about warnings of the current connection. */ - class Warning_info { + /** The type of the counted and doubly linked list of conditions. */ + typedef I_P_List<Sql_condition, + I_P_List_adapter<Sql_condition, + &Sql_condition::next_in_wi, + &Sql_condition::prev_in_wi>, + I_P_List_counter, + I_P_List_fast_push_back<Sql_condition> > + Sql_condition_list; + /** A memory root to allocate warnings and errors */ MEM_ROOT m_warn_root; /** List of warnings of all severities (levels). */ - List <MYSQL_ERROR> m_warn_list; + Sql_condition_list m_warn_list; /** A break down of the number of warnings per severity (level). */ - uint m_warn_count[(uint) MYSQL_ERROR::WARN_LEVEL_END]; + uint m_warn_count[(uint) Sql_condition::WARN_LEVEL_END]; /** The number of warnings of the current statement. Warning_info life cycle differs from statement life cycle -- it may span multiple statements. In that case we get - m_statement_warn_count 0, whereas m_warn_list is not empty. + m_current_statement_warn_count 0, whereas m_warn_list is not empty. */ - uint m_statement_warn_count; + uint m_current_statement_warn_count; /* Row counter, to print in errors and warnings. Not increased in @@ -365,29 +260,67 @@ class Warning_info /** Used to optionally clear warnings only once per statement. */ ulonglong m_warn_id; + /** + A pointer to an element of m_warn_list. It determines SQL-condition + instance which corresponds to the error state in Diagnostics_area. + + This is needed for properly processing SQL-conditions in SQL-handlers. + When an SQL-handler is found for the current error state in Diagnostics_area, + this pointer is needed to remove the corresponding SQL-condition from the + Warning_info list. + + @note m_error_condition might be NULL in the following cases: + - Diagnostics_area set to fatal error state (like OOM); + - Max number of Warning_info elements has been reached (thus, there is + no corresponding SQL-condition object in Warning_info). + */ + const Sql_condition *m_error_condition; + /** Indicates if push_warning() allows unlimited number of warnings. */ bool m_allow_unlimited_warnings; bool initialized; /* Set to 1 if init() has been called */ -private: - Warning_info(const Warning_info &rhs); /* Not implemented */ - Warning_info& operator=(const Warning_info &rhs); /* Not implemented */ -public: + /** Read only status. */ + bool m_read_only; + + /** Pointers for participating in the stack of Warning_info objects. */ + Warning_info *m_next_in_da; + Warning_info **m_prev_in_da; + + List<Sql_condition> m_marked_sql_conditions; +public: Warning_info(ulonglong warn_id_arg, bool allow_unlimited_warnings, - bool initialize=true); + bool initialized); ~Warning_info(); - /* Allocate memory for structures */ void init(); void free_memory(); +private: + Warning_info(const Warning_info &rhs); /* Not implemented */ + Warning_info& operator=(const Warning_info &rhs); /* Not implemented */ + + /** + Checks if Warning_info contains SQL-condition with the given message. + + @param message_str Message string. + @param message_length Length of message string. + + @return true if the Warning_info contains an SQL-condition with the given + message. + */ + bool has_sql_condition(const char *message_str, ulong message_length) const; + /** Reset the warning information. Clear all warnings, the number of warnings, reset current row counter to point to the first row. + + @param new_id new Warning_info id. */ - void clear_warning_info(ulonglong warn_id_arg); + void clear(ulonglong new_id); + /** Only clear warning info if haven't yet done that already for the current query. Allows to be issued at any time @@ -396,46 +329,72 @@ public: @todo: This is a sign of sloppy coding. Instead we need to designate one place in a statement life cycle where we call - clear_warning_info(). + Warning_info::clear(). + + @param query_id Current query id. */ - void opt_clear_warning_info(ulonglong query_id) + void opt_clear(ulonglong query_id) { if (query_id != m_warn_id) - clear_warning_info(query_id); - } - - void append_warning_info(THD *thd, Warning_info *source) - { - append_warnings(thd, & source->warn_list()); + clear(query_id); } /** Concatenate the list of warnings. - It's considered tolerable to lose a warning. - */ - void append_warnings(THD *thd, List<MYSQL_ERROR> *src) - { - MYSQL_ERROR *err; - List_iterator_fast<MYSQL_ERROR> it(*src); - /* - Don't use ::push_warning() to avoid invocation of condition - handlers or escalation of warnings to errors. - */ - while ((err= it++)) - Warning_info::push_warning(thd, err); - } - /** - Conditional merge of related warning information areas. + It's considered tolerable to lose an SQL-condition in case of OOM-error, + or if the number of SQL-conditions in the Warning_info reached top limit. + + @param thd Thread context. + @param source Warning_info object to copy SQL-conditions from. */ - void merge_with_routine_info(THD *thd, Warning_info *source); + void append_warning_info(THD *thd, const Warning_info *source); /** Reset between two COM_ commands. Warnings are preserved between commands, but statement_warn_count indicates the number of warnings of this particular statement only. */ - void reset_for_next_command() { m_statement_warn_count= 0; } + void reset_for_next_command() + { m_current_statement_warn_count= 0; } + + /** + Mark active SQL-conditions for later removal. + This is done to simulate stacked DAs for HANDLER statements. + */ + void mark_sql_conditions_for_removal(); + + /** + Unmark SQL-conditions, which were marked for later removal. + This is done to simulate stacked DAs for HANDLER statements. + */ + void unmark_sql_conditions_from_removal() + { m_marked_sql_conditions.empty(); } + + /** + Remove SQL-conditions that are marked for deletion. + This is done to simulate stacked DAs for HANDLER statements. + */ + void remove_marked_sql_conditions(); + + /** + Check if the given SQL-condition is marked for removal in this Warning_info + instance. + + @param cond the SQL-condition. + + @retval true if the given SQL-condition is marked for removal in this + Warning_info instance. + @retval false otherwise. + */ + bool is_marked_for_removal(const Sql_condition *cond) const; + + /** + Mark a single SQL-condition for removal (add the given SQL-condition to the + removal list of this Warning_info instance). + */ + void mark_condition_for_removal(Sql_condition *cond) + { m_marked_sql_conditions.push_back(cond, &m_warn_root); } /** Used for @@warning_count system variable, which prints @@ -444,52 +403,82 @@ public: ulong warn_count() const { /* - This may be higher than warn_list.elements if we have + This may be higher than warn_list.elements() if we have had more warnings than thd->variables.max_error_count. */ - return (m_warn_count[(uint) MYSQL_ERROR::WARN_LEVEL_NOTE] + - m_warn_count[(uint) MYSQL_ERROR::WARN_LEVEL_ERROR] + - m_warn_count[(uint) MYSQL_ERROR::WARN_LEVEL_WARN]); + return (m_warn_count[(uint) Sql_condition::WARN_LEVEL_NOTE] + + m_warn_count[(uint) Sql_condition::WARN_LEVEL_ERROR] + + m_warn_count[(uint) Sql_condition::WARN_LEVEL_WARN]); } /** - This is for iteration purposes. We return a non-constant reference - since List doesn't have constant iterators. - */ - List<MYSQL_ERROR> &warn_list() { return m_warn_list; } - - /** The number of errors, or number of rows returned by SHOW ERRORS, also the value of session variable @@error_count. */ ulong error_count() const + { return m_warn_count[(uint) Sql_condition::WARN_LEVEL_ERROR]; } + + /** + The number of conditions (errors, warnings and notes) in the list. + */ + uint cond_count() const { - return m_warn_count[(uint) MYSQL_ERROR::WARN_LEVEL_ERROR]; + return m_warn_list.elements(); } /** Id of the warning information area. */ - ulonglong warn_id() const { return m_warn_id; } + ulonglong id() const { return m_warn_id; } + + /** Set id of the warning information area. */ + void id(ulonglong id) { m_warn_id= id; } /** Do we have any errors and warnings that we can *show*? */ - bool is_empty() const { return m_warn_list.elements == 0; } + bool is_empty() const { return m_warn_list.is_empty(); } /** Increment the current row counter to point at the next row. */ void inc_current_row_for_warning() { m_current_row_for_warning++; } + /** Reset the current row counter. Start counting from the first row. */ void reset_current_row_for_warning() { m_current_row_for_warning= 1; } + /** Return the current counter value. */ ulong current_row_for_warning() const { return m_current_row_for_warning; } - ulong statement_warn_count() const { return m_statement_warn_count; } + /** Return the number of warnings thrown by the current statement. */ + ulong current_statement_warn_count() const + { return m_current_statement_warn_count; } - /** Add a new condition to the current list. */ - MYSQL_ERROR *push_warning(THD *thd, - uint sql_errno, const char* sqlstate, - MYSQL_ERROR::enum_warning_level level, - const char* msg); + /** Make sure there is room for the given number of conditions. */ + void reserve_space(THD *thd, uint count); - /** Add a new condition to the current list. */ - MYSQL_ERROR *push_warning(THD *thd, const MYSQL_ERROR *sql_condition); + /** + Add a new SQL-condition to the current list and increment the respective + counters. + + @param thd Thread context. + @param sql_errno SQL-condition error number. + @param sqlstate SQL-condition state. + @param level SQL-condition level. + @param msg SQL-condition message. + + @return a pointer to the added SQL-condition. + */ + Sql_condition *push_warning(THD *thd, + uint sql_errno, + const char* sqlstate, + Sql_condition::enum_warning_level level, + const char* msg); + + /** + Add a new SQL-condition to the current list and increment the respective + counters. + + @param thd Thread context. + @param sql_condition SQL-condition to copy values from. + + @return a pointer to the added SQL-condition. + */ + Sql_condition *push_warning(THD *thd, const Sql_condition *sql_condition); /** Set the read only status for this statement area. @@ -500,25 +489,51 @@ public: - SHOW WARNINGS - SHOW ERRORS - GET DIAGNOSTICS - @param read_only the read only property to set + @param read_only the read only property to set. */ void set_read_only(bool read_only) { m_read_only= read_only; } /** Read only status. - @return the read only property + @return the read only property. */ bool is_read_only() const { return m_read_only; } -private: - /** Read only status. */ - bool m_read_only; + /** + @return SQL-condition, which corresponds to the error state in + Diagnostics_area. + + @see m_error_condition. + */ + const Sql_condition *get_error_condition() const + { return m_error_condition; } + + /** + Set SQL-condition, which corresponds to the error state in Diagnostics_area. + + @see m_error_condition. + */ + void set_error_condition(const Sql_condition *error_condition) + { m_error_condition= error_condition; } + + /** + Reset SQL-condition, which corresponds to the error state in + Diagnostics_area. + + @see m_error_condition. + */ + void clear_error_condition() + { m_error_condition= NULL; } - friend class Resignal_statement; + // for: + // - m_next_in_da / m_prev_in_da + // - is_marked_for_removal() + friend class Diagnostics_area; }; + extern char *err_conv(char *buff, uint to_length, const char *from, uint from_length, CHARSET_INFO *from_cs); @@ -592,15 +607,352 @@ public: } }; -void push_warning(THD *thd, MYSQL_ERROR::enum_warning_level level, +/////////////////////////////////////////////////////////////////////////// + +/** + Stores status of the currently executed statement. + Cleared at the beginning of the statement, and then + can hold either OK, ERROR, or EOF status. + Can not be assigned twice per statement. +*/ + +class Diagnostics_area +{ +private: + /** The type of the counted and doubly linked list of conditions. */ + typedef I_P_List<Warning_info, + I_P_List_adapter<Warning_info, + &Warning_info::m_next_in_da, + &Warning_info::m_prev_in_da>, + I_P_List_counter, + I_P_List_fast_push_back<Warning_info> > + Warning_info_list; + +public: + /** Const iterator used to iterate through the warning list. */ + typedef Warning_info::Sql_condition_list::Const_Iterator + Sql_condition_iterator; + + enum enum_diagnostics_status + { + /** The area is cleared at start of a statement. */ + DA_EMPTY= 0, + /** Set whenever one calls my_ok(). */ + DA_OK, + /** Set whenever one calls my_eof(). */ + DA_EOF, + /** Set whenever one calls my_error() or my_message(). */ + DA_ERROR, + /** Set in case of a custom response, such as one from COM_STMT_PREPARE. */ + DA_DISABLED + }; + + void set_overwrite_status(bool can_overwrite_status) + { m_can_overwrite_status= can_overwrite_status; } + + /** True if status information is sent to the client. */ + bool is_sent() const { return m_is_sent; } + + void set_is_sent(bool is_sent) { m_is_sent= is_sent; } + + void set_ok_status(ulonglong affected_rows, + ulonglong last_insert_id, + const char *message); + + void set_eof_status(THD *thd); + + void set_error_status(uint sql_errno); + + void set_error_status(uint sql_errno, + const char *message, + const char *sqlstate, + const Sql_condition *error_condition); + + void disable_status(); + + void reset_diagnostics_area(); + + bool is_set() const { return m_status != DA_EMPTY; } + + bool is_error() const { return m_status == DA_ERROR; } + + bool is_eof() const { return m_status == DA_EOF; } + + bool is_ok() const { return m_status == DA_OK; } + + bool is_disabled() const { return m_status == DA_DISABLED; } + + enum_diagnostics_status status() const { return m_status; } + + const char *message() const + { DBUG_ASSERT(m_status == DA_ERROR || m_status == DA_OK); return m_message; } + + uint sql_errno() const + { DBUG_ASSERT(m_status == DA_ERROR); return m_sql_errno; } + + const char* get_sqlstate() const + { DBUG_ASSERT(m_status == DA_ERROR); return m_sqlstate; } + + ulonglong affected_rows() const + { DBUG_ASSERT(m_status == DA_OK); return m_affected_rows; } + + ulonglong last_insert_id() const + { DBUG_ASSERT(m_status == DA_OK); return m_last_insert_id; } + + uint statement_warn_count() const + { + DBUG_ASSERT(m_status == DA_OK || m_status == DA_EOF); + return m_statement_warn_count; + } + + /* Used to count any warnings pushed after calling set_ok_status(). */ + void increment_warning() + { + if (m_status != DA_EMPTY) + m_statement_warn_count++; + } + + Diagnostics_area(bool initialize); + Diagnostics_area(ulonglong warning_info_id, bool allow_unlimited_warnings); + void init() { m_main_wi.init() ; } + void free_memory() { m_main_wi.free_memory() ; } + + void push_warning_info(Warning_info *wi) + { m_wi_stack.push_front(wi); } + + void pop_warning_info() + { + DBUG_ASSERT(m_wi_stack.elements() > 0); + m_wi_stack.remove(m_wi_stack.front()); + } + + void set_warning_info_id(ulonglong id) + { get_warning_info()->id(id); } + + ulonglong warning_info_id() const + { return get_warning_info()->id(); } + + /** + Compare given current warning info and current warning info + and see if they are different. They will be different if + warnings have been generated or statements that use tables + have been executed. This is checked by comparing m_warn_id. + + @param wi Warning info to compare with current Warning info. + + @return false if they are equal, true if they are not. + */ + bool warning_info_changed(const Warning_info *wi) const + { return get_warning_info()->id() != wi->id(); } + + bool is_warning_info_empty() const + { return get_warning_info()->is_empty(); } + + ulong current_statement_warn_count() const + { return get_warning_info()->current_statement_warn_count(); } + + bool has_sql_condition(const char *message_str, ulong message_length) const + { return get_warning_info()->has_sql_condition(message_str, message_length); } + + void reset_for_next_command() + { get_warning_info()->reset_for_next_command(); } + + void clear_warning_info(ulonglong id) + { get_warning_info()->clear(id); } + + void opt_clear_warning_info(ulonglong query_id) + { get_warning_info()->opt_clear(query_id); } + + ulong current_row_for_warning() const + { return get_warning_info()->current_row_for_warning(); } + + void inc_current_row_for_warning() + { get_warning_info()->inc_current_row_for_warning(); } + + void reset_current_row_for_warning() + { get_warning_info()->reset_current_row_for_warning(); } + + bool is_warning_info_read_only() const + { return get_warning_info()->is_read_only(); } + + void set_warning_info_read_only(bool read_only) + { get_warning_info()->set_read_only(read_only); } + + ulong error_count() const + { return get_warning_info()->error_count(); } + + ulong warn_count() const + { return get_warning_info()->warn_count(); } + + uint cond_count() const + { return get_warning_info()->cond_count(); } + + Sql_condition_iterator sql_conditions() const + { return get_warning_info()->m_warn_list; } + + void reserve_space(THD *thd, uint count) + { get_warning_info()->reserve_space(thd, count); } + + Sql_condition *push_warning(THD *thd, const Sql_condition *sql_condition) + { return get_warning_info()->push_warning(thd, sql_condition); } + + Sql_condition *push_warning(THD *thd, + uint sql_errno, + const char* sqlstate, + Sql_condition::enum_warning_level level, + const char* msg) + { + return get_warning_info()->push_warning(thd, + sql_errno, sqlstate, level, msg); + } + + void mark_sql_conditions_for_removal() + { get_warning_info()->mark_sql_conditions_for_removal(); } + + void unmark_sql_conditions_from_removal() + { get_warning_info()->unmark_sql_conditions_from_removal(); } + + void remove_marked_sql_conditions() + { get_warning_info()->remove_marked_sql_conditions(); } + + const Sql_condition *get_error_condition() const + { return get_warning_info()->get_error_condition(); } + + void copy_sql_conditions_to_wi(THD *thd, Warning_info *dst_wi) const + { dst_wi->append_warning_info(thd, get_warning_info()); } + + void copy_sql_conditions_from_wi(THD *thd, const Warning_info *src_wi) + { get_warning_info()->append_warning_info(thd, src_wi); } + + void copy_non_errors_from_wi(THD *thd, const Warning_info *src_wi); + +private: + Warning_info *get_warning_info() { return m_wi_stack.front(); } + + const Warning_info *get_warning_info() const { return m_wi_stack.front(); } + +private: + /** True if status information is sent to the client. */ + bool m_is_sent; + + /** Set to make set_error_status after set_{ok,eof}_status possible. */ + bool m_can_overwrite_status; + + /** Message buffer. Can be used by OK or ERROR status. */ + char m_message[MYSQL_ERRMSG_SIZE]; + + /** + SQL error number. One of ER_ codes from share/errmsg.txt. + Set by set_error_status. + */ + uint m_sql_errno; + + char m_sqlstate[SQLSTATE_LENGTH+1]; + + /** + The number of rows affected by the last statement. This is + semantically close to thd->row_count_func, but has a different + life cycle. thd->row_count_func stores the value returned by + function ROW_COUNT() and is cleared only by statements that + update its value, such as INSERT, UPDATE, DELETE and few others. + This member is cleared at the beginning of the next statement. + + We could possibly merge the two, but life cycle of thd->row_count_func + can not be changed. + */ + ulonglong m_affected_rows; + + /** + Similarly to the previous member, this is a replacement of + thd->first_successful_insert_id_in_prev_stmt, which is used + to implement LAST_INSERT_ID(). + */ + + ulonglong m_last_insert_id; + /** + Number of warnings of this last statement. May differ from + the number of warnings returned by SHOW WARNINGS e.g. in case + the statement doesn't clear the warnings, and doesn't generate + them. + */ + uint m_statement_warn_count; + + enum_diagnostics_status m_status; + + Warning_info m_main_wi; + + Warning_info_list m_wi_stack; +}; + +/////////////////////////////////////////////////////////////////////////// + + +void push_warning(THD *thd, Sql_condition::enum_warning_level level, uint code, const char *msg); -void push_warning_printf(THD *thd, MYSQL_ERROR::enum_warning_level level, - uint code, const char *format, ...); + +void push_warning_printf(THD *thd, Sql_condition::enum_warning_level level, + uint code, const char *format, ...); + bool mysqld_show_warnings(THD *thd, ulong levels_to_show); -uint32 convert_error_message(char *to, uint32 to_length, CHARSET_INFO *to_cs, + +uint32 convert_error_message(char *to, uint32 to_length, + CHARSET_INFO *to_cs, const char *from, uint32 from_length, CHARSET_INFO *from_cs, uint *errors); extern const LEX_STRING warning_level_names[]; +bool is_sqlstate_valid(const LEX_STRING *sqlstate); +/** + Checks if the specified SQL-state-string defines COMPLETION condition. + This function assumes that the given string contains a valid SQL-state. + + @param s the condition SQLSTATE. + + @retval true if the given string defines COMPLETION condition. + @retval false otherwise. +*/ +inline bool is_sqlstate_completion(const char *s) +{ return s[0] == '0' && s[1] == '0'; } + + +/** + Checks if the specified SQL-state-string defines WARNING condition. + This function assumes that the given string contains a valid SQL-state. + + @param s the condition SQLSTATE. + + @retval true if the given string defines WARNING condition. + @retval false otherwise. +*/ +inline bool is_sqlstate_warning(const char *s) +{ return s[0] == '0' && s[1] == '1'; } + + +/** + Checks if the specified SQL-state-string defines NOT FOUND condition. + This function assumes that the given string contains a valid SQL-state. + + @param s the condition SQLSTATE. + + @retval true if the given string defines NOT FOUND condition. + @retval false otherwise. +*/ +inline bool is_sqlstate_not_found(const char *s) +{ return s[0] == '0' && s[1] == '2'; } + + +/** + Checks if the specified SQL-state-string defines EXCEPTION condition. + This function assumes that the given string contains a valid SQL-state. + + @param s the condition SQLSTATE. + + @retval true if the given string defines EXCEPTION condition. + @retval false otherwise. +*/ +inline bool is_sqlstate_exception(const char *s) +{ return s[0] != '0' || s[1] > '2'; } + + #endif // SQL_ERROR_H diff --git a/sql/sql_get_diagnostics.cc b/sql/sql_get_diagnostics.cc new file mode 100644 index 00000000000..24850e44d59 --- /dev/null +++ b/sql/sql_get_diagnostics.cc @@ -0,0 +1,340 @@ +/* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + + 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; version 2 of the License. + + 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., 51 Franklin St, Fifth Floor, Boston, MA 02111-1307 USA */ + +#include "sql_list.h" // Sql_alloc, List, List_iterator +#include "sql_cmd.h" // Sql_cmd +#include "sql_class.h" // Diagnostics_area +#include "sql_get_diagnostics.h" // Sql_cmd_get_diagnostics + +/** + Execute this GET DIAGNOSTICS statement. + + @param thd The current thread. + + @remark Errors or warnings occurring during the execution of the GET + DIAGNOSTICS statement should not affect the diagnostics area + of a previous statement as the diagnostics information there + would be wiped out. Thus, in order to preserve the contents + of the diagnostics area from which information is being + retrieved, the GET DIAGNOSTICS statement is executed under + a separate diagnostics area. If any errors or warnings occur + during the execution of the GET DIAGNOSTICS statement, these + error or warnings (conditions) are appended to the list of + the original diagnostics area. The only exception to this is + fatal errors, which must always cause the statement to fail. + + @retval false on success. + @retval true on error +*/ + +bool +Sql_cmd_get_diagnostics::execute(THD *thd) +{ + bool rv; + Diagnostics_area new_stmt_da(thd->query_id, false); + Diagnostics_area *save_stmt_da= thd->get_stmt_da(); + DBUG_ENTER("Sql_cmd_get_diagnostics::execute"); + + /* Disable the unneeded read-only mode of the original DA. */ + save_stmt_da->set_warning_info_read_only(false); + + /* Set new diagnostics area, execute statement and restore. */ + thd->set_stmt_da(&new_stmt_da); + rv= m_info->aggregate(thd, save_stmt_da); + thd->set_stmt_da(save_stmt_da); + + /* Bail out early if statement succeeded. */ + if (! rv) + { + thd->get_stmt_da()->set_ok_status(0, 0, NULL); + DBUG_RETURN(false); + } + + /* Statement failed, retrieve the error information for propagation. */ + uint sql_errno= new_stmt_da.sql_errno(); + const char *message= new_stmt_da.message(); + const char *sqlstate= new_stmt_da.get_sqlstate(); + + /* In case of a fatal error, set it into the original DA.*/ + if (thd->is_fatal_error) + { + save_stmt_da->set_error_status(sql_errno, message, sqlstate, NULL); + DBUG_RETURN(true); + } + + /* Otherwise, just append the new error as a exception condition. */ + save_stmt_da->push_warning(thd, sql_errno, sqlstate, + Sql_condition::WARN_LEVEL_ERROR, + message); + + /* Appending might have failed. */ + if (! (rv= thd->is_error())) + thd->get_stmt_da()->set_ok_status(0, 0, NULL); + + DBUG_RETURN(rv); +} + + +/** + Set a value for this item. + + @param thd The current thread. + @param value The obtained value. + + @retval false on success. + @retval true on error. +*/ + +bool +Diagnostics_information_item::set_value(THD *thd, Item **value) +{ + bool rv; + Settable_routine_parameter *srp; + DBUG_ENTER("Diagnostics_information_item::set_value"); + + /* Get a settable reference to the target. */ + srp= m_target->get_settable_routine_parameter(); + + DBUG_ASSERT(srp); + + /* Set variable/parameter value. */ + rv= srp->set_value(thd, thd->spcont, value); + + DBUG_RETURN(rv); +} + + +/** + Obtain statement information in the context of a given diagnostics area. + + @param thd The current thread. + @param da The diagnostics area. + + @retval false on success. + @retval true on error +*/ + +bool +Statement_information::aggregate(THD *thd, const Diagnostics_area *da) +{ + bool rv= false; + Statement_information_item *stmt_info_item; + List_iterator<Statement_information_item> it(*m_items); + DBUG_ENTER("Statement_information::aggregate"); + + /* + Each specified target gets the value of each given + information item obtained from the diagnostics area. + */ + while ((stmt_info_item= it++)) + { + if ((rv= evaluate(thd, stmt_info_item, da))) + break; + } + + DBUG_RETURN(rv); +} + + +/** + Obtain the value of this statement information item in the context of + a given diagnostics area. + + @param thd The current thread. + @param da The diagnostics area. + + @retval Item representing the value. + @retval NULL on error. +*/ + +Item * +Statement_information_item::get_value(THD *thd, const Diagnostics_area *da) +{ + Item *value= NULL; + DBUG_ENTER("Statement_information_item::get_value"); + + switch (m_name) + { + /* + The number of condition areas that have information. That is, + the number of errors and warnings within the diagnostics area. + */ + case NUMBER: + { + ulong count= da->cond_count(); + value= new (thd->mem_root) Item_uint(count); + break; + } + /* + Number that shows how many rows were directly affected by + a data-change statement (INSERT, UPDATE, DELETE, MERGE, + REPLACE, LOAD). + */ + case ROW_COUNT: + value= new (thd->mem_root) Item_int(thd->get_row_count_func()); + break; + } + + DBUG_RETURN(value); +} + + +/** + Obtain condition information in the context of a given diagnostics area. + + @param thd The current thread. + @param da The diagnostics area. + + @retval false on success. + @retval true on error +*/ + +bool +Condition_information::aggregate(THD *thd, const Diagnostics_area *da) +{ + bool rv= false; + longlong cond_number; + const Sql_condition *cond= NULL; + Condition_information_item *cond_info_item; + Diagnostics_area::Sql_condition_iterator it_conds= da->sql_conditions(); + List_iterator_fast<Condition_information_item> it_items(*m_items); + DBUG_ENTER("Condition_information::aggregate"); + + /* Prepare the expression for evaluation. */ + if (!m_cond_number_expr->fixed && + m_cond_number_expr->fix_fields(thd, &m_cond_number_expr)) + DBUG_RETURN(true); + + cond_number= m_cond_number_expr->val_int(); + + /* + Limit to the number of available conditions. Warning_info::warn_count() + is not used because it indicates the number of condition regardless of + @@max_error_count, which prevents conditions from being pushed, but not + counted. + */ + if (cond_number < 1 || (ulonglong) cond_number > da->cond_count()) + { + my_error(ER_DA_INVALID_CONDITION_NUMBER, MYF(0)); + DBUG_RETURN(true); + } + + /* Advance to the requested condition. */ + while (cond_number--) + cond= it_conds++; + + DBUG_ASSERT(cond); + + /* Evaluate the requested information in the context of the condition. */ + while ((cond_info_item= it_items++)) + { + if ((rv= evaluate(thd, cond_info_item, cond))) + break; + } + + DBUG_RETURN(rv); +} + + +/** + Create an UTF-8 string item to represent a condition item string. + + @remark The string might not have a associated charset. For example, + this can be the case if the server does not or fails to process + the error message file. + + @remark See "Design notes about Sql_condition::m_message_text." in sql_error.cc + + @return Pointer to an string item, NULL on failure. +*/ + +Item * +Condition_information_item::make_utf8_string_item(THD *thd, const String *str) +{ + /* Default is utf8 character set and utf8_general_ci collation. */ + CHARSET_INFO *to_cs= &my_charset_utf8_general_ci; + /* If a charset was not set, assume that no conversion is needed. */ + CHARSET_INFO *from_cs= str->charset() ? str->charset() : to_cs; + Item_string *item= new Item_string(str->ptr(), str->length(), from_cs); + /* If necessary, convert the string (ignoring errors), then copy it over. */ + return item ? item->charset_converter(to_cs, false) : NULL; +} + + +/** + Obtain the value of this condition information item in the context of + a given condition. + + @param thd The current thread. + @param da The diagnostics area. + + @retval Item representing the value. + @retval NULL on error. +*/ + +Item * +Condition_information_item::get_value(THD *thd, const Sql_condition *cond) +{ + String str; + Item *value= NULL; + DBUG_ENTER("Condition_information_item::get_value"); + + switch (m_name) + { + case CLASS_ORIGIN: + value= make_utf8_string_item(thd, &(cond->m_class_origin)); + break; + case SUBCLASS_ORIGIN: + value= make_utf8_string_item(thd, &(cond->m_subclass_origin)); + break; + case CONSTRAINT_CATALOG: + value= make_utf8_string_item(thd, &(cond->m_constraint_catalog)); + break; + case CONSTRAINT_SCHEMA: + value= make_utf8_string_item(thd, &(cond->m_constraint_schema)); + break; + case CONSTRAINT_NAME: + value= make_utf8_string_item(thd, &(cond->m_constraint_name)); + break; + case CATALOG_NAME: + value= make_utf8_string_item(thd, &(cond->m_catalog_name)); + break; + case SCHEMA_NAME: + value= make_utf8_string_item(thd, &(cond->m_schema_name)); + break; + case TABLE_NAME: + value= make_utf8_string_item(thd, &(cond->m_table_name)); + break; + case COLUMN_NAME: + value= make_utf8_string_item(thd, &(cond->m_column_name)); + break; + case CURSOR_NAME: + value= make_utf8_string_item(thd, &(cond->m_cursor_name)); + break; + case MESSAGE_TEXT: + value= make_utf8_string_item(thd, &(cond->m_message_text)); + break; + case MYSQL_ERRNO: + value= new (thd->mem_root) Item_uint(cond->m_sql_errno); + break; + case RETURNED_SQLSTATE: + str.set_ascii(cond->get_sqlstate(), strlen(cond->get_sqlstate())); + value= make_utf8_string_item(thd, &str); + break; + } + + DBUG_RETURN(value); +} + diff --git a/sql/sql_get_diagnostics.h b/sql/sql_get_diagnostics.h new file mode 100644 index 00000000000..f34820757f5 --- /dev/null +++ b/sql/sql_get_diagnostics.h @@ -0,0 +1,318 @@ +/* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + + 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; version 2 of the License. + + 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., 51 Franklin St, Fifth Floor, Boston, MA 02111-1307 USA */ + +#ifndef SQL_GET_DIAGNOSTICS_H +#define SQL_GET_DIAGNOSTICS_H + +/** Diagnostics information forward reference. */ +class Diagnostics_information; + + +/** + Sql_cmd_get_diagnostics represents a GET DIAGNOSTICS statement. + + The GET DIAGNOSTICS statement retrieves exception or completion + condition information from a diagnostics area, usually pertaining + to the last non-diagnostic SQL statement that was executed. +*/ +class Sql_cmd_get_diagnostics : public Sql_cmd +{ +public: + /** + Constructor, used to represent a GET DIAGNOSTICS statement. + + @param info Diagnostics information to be obtained. + */ + Sql_cmd_get_diagnostics(Diagnostics_information *info) + : m_info(info) + {} + + virtual enum_sql_command sql_command_code() const + { + return SQLCOM_GET_DIAGNOSTICS; + } + + virtual bool execute(THD *thd); + +private: + /** The information to be obtained. */ + Diagnostics_information *m_info; +}; + + +/** + Represents the diagnostics information to be obtained. + + Diagnostic information is made available through statement + information and condition information items. +*/ +class Diagnostics_information : public Sql_alloc +{ +public: + /** + Which diagnostics area to access. + Only CURRENT is supported for now. + */ + enum Which_area + { + /** Access the first diagnostics area. */ + CURRENT_AREA + }; + + /** Set which diagnostics area to access. */ + void set_which_da(Which_area area) + { m_area= area; } + + /** Get which diagnostics area to access. */ + Which_area get_which_da(void) const + { return m_area; } + + /** + Aggregate diagnostics information. + + @param thd The current thread. + @param da The diagnostics area. + + @retval false on success. + @retval true on error + */ + virtual bool aggregate(THD *thd, const Diagnostics_area *da) = 0; + +protected: + /** + Diagnostics_information objects are allocated in thd->mem_root. + Do not rely on the destructor for any cleanup. + */ + virtual ~Diagnostics_information() + { + DBUG_ASSERT(false); + } + + /** + Evaluate a diagnostics information item in a specific context. + + @param thd The current thread. + @param diag_item The diagnostics information item. + @param ctx The context to evaluate the item. + + @retval false on success. + @retval true on error. + */ + template <typename Diag_item, typename Context> + bool evaluate(THD *thd, Diag_item *diag_item, Context ctx) + { + Item *value; + + /* Get this item's value. */ + if (! (value= diag_item->get_value(thd, ctx))) + return true; + + /* Set variable/parameter value. */ + return diag_item->set_value(thd, &value); + } + +private: + /** Which diagnostics area to access. */ + Which_area m_area; +}; + + +/** + A diagnostics information item. Used to associate a specific + diagnostics information item to a target variable. +*/ +class Diagnostics_information_item : public Sql_alloc +{ +public: + /** + Set a value for this item. + + @param thd The current thread. + @param value The obtained value. + + @retval false on success. + @retval true on error. + */ + bool set_value(THD *thd, Item **value); + +protected: + /** + Constructor, used to represent a diagnostics information item. + + @param target A target that gets the value of this item. + */ + Diagnostics_information_item(Item *target) + : m_target(target) + {} + + /** + Diagnostics_information_item objects are allocated in thd->mem_root. + Do not rely on the destructor for any cleanup. + */ + virtual ~Diagnostics_information_item() + { + DBUG_ASSERT(false); + } + +private: + /** The target variable that will receive the value of this item. */ + Item *m_target; +}; + + +/** + A statement information item. +*/ +class Statement_information_item : public Diagnostics_information_item +{ +public: + /** The name of a statement information item. */ + enum Name + { + NUMBER, + ROW_COUNT + }; + + /** + Constructor, used to represent a statement information item. + + @param name The name of this item. + @param target A target that gets the value of this item. + */ + Statement_information_item(Name name, Item *target) + : Diagnostics_information_item(target), m_name(name) + {} + + /** Obtain value of this statement information item. */ + Item *get_value(THD *thd, const Diagnostics_area *da); + +private: + /** The name of this statement information item. */ + Name m_name; +}; + + +/** + Statement information. + + @remark Provides information about the execution of a statement. +*/ +class Statement_information : public Diagnostics_information +{ +public: + /** + Constructor, used to represent the statement information of a + GET DIAGNOSTICS statement. + + @param items List of requested statement information items. + */ + Statement_information(List<Statement_information_item> *items) + : m_items(items) + {} + + /** Obtain statement information in the context of a diagnostics area. */ + bool aggregate(THD *thd, const Diagnostics_area *da); + +private: + /* List of statement information items. */ + List<Statement_information_item> *m_items; +}; + + +/** + A condition information item. +*/ +class Condition_information_item : public Diagnostics_information_item +{ +public: + /** + The name of a condition information item. + */ + enum Name + { + CLASS_ORIGIN, + SUBCLASS_ORIGIN, + CONSTRAINT_CATALOG, + CONSTRAINT_SCHEMA, + CONSTRAINT_NAME, + CATALOG_NAME, + SCHEMA_NAME, + TABLE_NAME, + COLUMN_NAME, + CURSOR_NAME, + MESSAGE_TEXT, + MYSQL_ERRNO, + RETURNED_SQLSTATE + }; + + /** + Constructor, used to represent a condition information item. + + @param name The name of this item. + @param target A target that gets the value of this item. + */ + Condition_information_item(Name name, Item *target) + : Diagnostics_information_item(target), m_name(name) + {} + + /** Obtain value of this condition information item. */ + Item *get_value(THD *thd, const Sql_condition *cond); + +private: + /** The name of this condition information item. */ + Name m_name; + + /** Create an string item to represent a condition item string. */ + Item *make_utf8_string_item(THD *thd, const String *str); +}; + + +/** + Condition information. + + @remark Provides information about conditions raised during the + execution of a statement. +*/ +class Condition_information : public Diagnostics_information +{ +public: + /** + Constructor, used to represent the condition information of a + GET DIAGNOSTICS statement. + + @param cond_number_expr Number that identifies the diagnostic condition. + @param items List of requested condition information items. + */ + Condition_information(Item *cond_number_expr, + List<Condition_information_item> *items) + : m_cond_number_expr(cond_number_expr), m_items(items) + {} + + /** Obtain condition information in the context of a diagnostics area. */ + bool aggregate(THD *thd, const Diagnostics_area *da); + +private: + /** + Number that identifies the diagnostic condition for which + information is to be obtained. + */ + Item *m_cond_number_expr; + + /** List of condition information items. */ + List<Condition_information_item> *m_items; +}; + +#endif + diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc index 1c93a59904c..64b21ea6ab0 100644 --- a/sql/sql_handler.cc +++ b/sql/sql_handler.cc @@ -171,7 +171,7 @@ static void mysql_ha_close_table(SQL_HANDLER *handler) table->file->ha_index_or_rnd_end(); table->open_by_handler= 0; - (void) close_thread_table(thd, &table); + close_thread_table(thd, &table); thd->mdl_context.release_lock(handler->mdl_request.ticket); } else @@ -501,9 +501,9 @@ public: bool handle_condition(THD *thd, uint sql_errno, const char *sqlstate, - MYSQL_ERROR::enum_warning_level level, + Sql_condition::enum_warning_level level, const char* msg, - MYSQL_ERROR **cond_hdl); + Sql_condition **cond_hdl); bool need_reopen() const { return m_need_reopen; }; void init() { m_need_reopen= FALSE; }; @@ -522,9 +522,9 @@ Sql_handler_lock_error_handler:: handle_condition(THD *thd, uint sql_errno, const char *sqlstate, - MYSQL_ERROR::enum_warning_level level, + Sql_condition::enum_warning_level level, const char* msg, - MYSQL_ERROR **cond_hdl) + Sql_condition **cond_hdl) { *cond_hdl= NULL; if (sql_errno == ER_LOCK_ABORTED) @@ -639,9 +639,10 @@ mysql_ha_fix_cond_and_key(SQL_HANDLER *handler, key_part_map keypart_map; uint key_len; - if (key_expr->elements > keyinfo->key_parts) + if (key_expr->elements > keyinfo->user_defined_key_parts) { - my_error(ER_TOO_MANY_KEY_PARTS, MYF(0), keyinfo->key_parts); + my_error(ER_TOO_MANY_KEY_PARTS, MYF(0), + keyinfo->user_defined_key_parts); return 1; } for (keypart_map= key_len=0 ; (item=it_ke++) ; key_part++) diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 09e4953cdc4..8c5fb96012d 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -963,7 +963,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, error=write_record(thd, table ,&info); if (error) break; - thd->warning_info->inc_current_row_for_warning(); + thd->get_stmt_da()->inc_current_row_for_warning(); } free_underlaid_joins(thd, &thd->lex->select_lex); @@ -1120,11 +1120,11 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, sprintf(buff, ER(ER_INSERT_INFO), (ulong) info.records, (lock_type == TL_WRITE_DELAYED) ? (ulong) 0 : (ulong) (info.records - info.copied), - (ulong) thd->warning_info->statement_warn_count()); + (long) thd->get_stmt_da()->current_statement_warn_count()); else sprintf(buff, ER(ER_INSERT_INFO), (ulong) info.records, (ulong) (info.deleted + updated), - (ulong) thd->warning_info->statement_warn_count()); + (long) thd->get_stmt_da()->current_statement_warn_count()); ::my_ok(thd, info.copied + info.deleted + updated, id, buff); } thd->abort_on_warning= 0; @@ -1209,7 +1209,7 @@ static bool check_view_insertability(THD * thd, TABLE_LIST *view) } Item_field *field; /* simple SELECT list entry (field without expression) */ - if (!(field= trans->item->filed_for_view_update())) + if (!(field= trans->item->field_for_view_update())) { thd->mark_used_columns= save_mark_used_columns; DBUG_RETURN(TRUE); @@ -1645,7 +1645,7 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) } } key_copy((uchar*) key,table->record[0],table->key_info+key_nr,0); - key_part_map keypart_map= (1 << table->key_info[key_nr].key_parts) - 1; + key_part_map keypart_map= (1 << table->key_info[key_nr].user_defined_key_parts) - 1; if ((error= (table->file->ha_index_read_idx_map(table->record[1], key_nr, (uchar*) key, keypart_map, @@ -1894,7 +1894,7 @@ int check_that_all_fields_are_given_values(THD *thd, TABLE *entry, } if (view) { - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_NO_DEFAULT_FOR_VIEW_FIELD, ER(ER_NO_DEFAULT_FOR_VIEW_FIELD), table_list->view_db.str, @@ -1902,7 +1902,7 @@ int check_that_all_fields_are_given_values(THD *thd, TABLE *entry, } else { - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_NO_DEFAULT_FOR_FIELD, ER(ER_NO_DEFAULT_FOR_FIELD), (*field)->field_name); @@ -2253,7 +2253,8 @@ bool delayed_get_table(THD *thd, MDL_request *grl_protection_request, want to send "Server shutdown in progress" in the INSERT THREAD. */ - my_message(di->thd.stmt_da->sql_errno(), di->thd.stmt_da->message(), + my_message(di->thd.get_stmt_da()->sql_errno(), + di->thd.get_stmt_da()->message(), MYF(0)); } di->unlock(); @@ -2343,7 +2344,8 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd) if (!thd.is_error()) my_message(ER_QUERY_INTERRUPTED, ER(ER_QUERY_INTERRUPTED), MYF(0)); else - my_message(thd.stmt_da->sql_errno(), thd.stmt_da->message(), MYF(0)); + my_message(thd.get_stmt_da()->sql_errno(), + thd.get_stmt_da()->message(), MYF(0)); goto error; } } @@ -2765,8 +2767,7 @@ pthread_handler_t handle_delayed_insert(void *arg) if (my_thread_init()) { /* Can't use my_error since store_globals has not yet been called */ - thd->stmt_da->set_error_status(thd, ER_OUT_OF_RESOURCES, - ER(ER_OUT_OF_RESOURCES), NULL); + thd->get_stmt_da()->set_error_status(ER_OUT_OF_RESOURCES); di->handler_thread_initialized= TRUE; } else @@ -2776,8 +2777,7 @@ pthread_handler_t handle_delayed_insert(void *arg) if (init_thr_lock() || thd->store_globals()) { /* Can't use my_error since store_globals has perhaps failed */ - thd->stmt_da->set_error_status(thd, ER_OUT_OF_RESOURCES, - ER(ER_OUT_OF_RESOURCES), NULL); + thd->get_stmt_da()->set_error_status(ER_OUT_OF_RESOURCES); di->handler_thread_initialized= TRUE; thd->fatal_error(); goto err; @@ -3167,7 +3167,7 @@ bool Delayed_insert::handle_inserts(void) { /* This should never happen */ table->file->print_error(error,MYF(0)); - sql_print_error("%s", thd.stmt_da->message()); + sql_print_error("%s", thd.get_stmt_da()->message()); DBUG_PRINT("error", ("HA_EXTRA_NO_CACHE failed in loop")); goto err; } @@ -3213,7 +3213,7 @@ bool Delayed_insert::handle_inserts(void) if ((error=table->file->extra(HA_EXTRA_NO_CACHE))) { // This shouldn't happen table->file->print_error(error,MYF(0)); - sql_print_error("%s", thd.stmt_da->message()); + sql_print_error("%s", thd.get_stmt_da()->message()); DBUG_PRINT("error", ("HA_EXTRA_NO_CACHE failed after loop")); goto err; } @@ -3644,7 +3644,7 @@ bool select_insert::send_eof() error= (thd->locked_tables_mode <= LTM_LOCK_TABLES ? table->file->ha_end_bulk_insert() : 0); if (!error && thd->is_error()) - error= thd->stmt_da->sql_errno(); + error= thd->get_stmt_da()->sql_errno(); table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY); table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE); @@ -3697,11 +3697,11 @@ bool select_insert::send_eof() if (info.ignore) sprintf(buff, ER(ER_INSERT_INFO), (ulong) info.records, (ulong) (info.records - info.copied), - (ulong) thd->warning_info->statement_warn_count()); + (long) thd->get_stmt_da()->current_statement_warn_count()); else sprintf(buff, ER(ER_INSERT_INFO), (ulong) info.records, (ulong) (info.deleted+info.updated), - (ulong) thd->warning_info->statement_warn_count()); + (long) thd->get_stmt_da()->current_statement_warn_count()); row_count= info.copied + info.deleted + ((thd->client_capabilities & CLIENT_FOUND_ROWS) ? info.touched : info.updated); @@ -3891,8 +3891,8 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, { if (!mysql_create_table_no_lock(thd, create_table->db, create_table->table_name, - create_info, alter_info, 0, - select_field_count, NULL)) + create_info, alter_info, NULL, + select_field_count)) { DEBUG_SYNC(thd,"create_table_select_before_open"); @@ -3905,7 +3905,7 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, */ if (open_table(thd, create_table, thd->mem_root, &ot_ctx)) { - quick_rm_table(create_info->db_type, create_table->db, + quick_rm_table(thd, create_info->db_type, create_table->db, table_case_name(create_info, create_table->table_name), 0); } diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 82cdd4ead7b..9b12b6dcbeb 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -494,12 +494,13 @@ void lex_start(THD *thd) lex->select_lex.ftfunc_list= &lex->select_lex.ftfunc_list_alloc; lex->select_lex.group_list.empty(); lex->select_lex.order_list.empty(); + lex->m_sql_cmd= NULL; lex->duplicates= DUP_ERROR; lex->ignore= 0; lex->spname= NULL; lex->sphead= NULL; lex->spcont= NULL; - lex->m_stmt= NULL; + lex->m_sql_cmd= NULL; lex->proc_list.first= 0; lex->escape_used= FALSE; lex->query_tables= 0; @@ -1746,50 +1747,6 @@ int lex_one_token(void *arg, void *yythd) } -/** - Construct a copy of this object to be used for mysql_alter_table - and mysql_create_table. - - Historically, these two functions modify their Alter_info - arguments. This behaviour breaks re-execution of prepared - statements and stored procedures and is compensated by always - supplying a copy of Alter_info to these functions. - - @return You need to use check the error in THD for out - of memory condition after calling this function. -*/ - -Alter_info::Alter_info(const Alter_info &rhs, MEM_ROOT *mem_root) - :drop_list(rhs.drop_list, mem_root), - alter_list(rhs.alter_list, mem_root), - key_list(rhs.key_list, mem_root), - create_list(rhs.create_list, mem_root), - flags(rhs.flags), - keys_onoff(rhs.keys_onoff), - tablespace_op(rhs.tablespace_op), - partition_names(rhs.partition_names, mem_root), - num_parts(rhs.num_parts), - change_level(rhs.change_level), - datetime_field(rhs.datetime_field), - error_if_not_empty(rhs.error_if_not_empty) -{ - /* - Make deep copies of used objects. - This is not a fully deep copy - clone() implementations - of Alter_drop, Alter_column, Key, foreign_key, Key_part_spec - do not copy string constants. At the same length the only - reason we make a copy currently is that ALTER/CREATE TABLE - code changes input Alter_info definitions, but string - constants never change. - */ - list_copy_and_replace_each_value(drop_list, mem_root); - list_copy_and_replace_each_value(alter_list, mem_root); - list_copy_and_replace_each_value(key_list, mem_root); - list_copy_and_replace_each_value(create_list, mem_root); - /* partition_names are not deeply copied currently */ -} - - void trim_whitespace(CHARSET_INFO *cs, LEX_STRING *str) { /* @@ -2195,12 +2152,13 @@ bool st_select_lex_node::inc_in_sum_expr() { return 1; } uint st_select_lex_node::get_in_sum_expr() { return 0; } TABLE_LIST* st_select_lex_node::get_table_list() { return 0; } List<Item>* st_select_lex_node::get_item_list() { return 0; } -TABLE_LIST *st_select_lex_node::add_table_to_list (THD *thd, Table_ident *table, +TABLE_LIST *st_select_lex_node::add_table_to_list(THD *thd, Table_ident *table, LEX_STRING *alias, ulong table_join_options, thr_lock_type flags, enum_mdl_type mdl_type, List<Index_hint> *hints, + List<String> *partition_names, LEX_STRING *option) { return 0; @@ -4299,8 +4257,8 @@ int st_select_lex_unit::print_explain(select_result_sink *output, bool LEX::is_partition_management() const { return (sql_command == SQLCOM_ALTER_TABLE && - (alter_info.flags == ALTER_ADD_PARTITION || - alter_info.flags == ALTER_REORGANIZE_PARTITION)); + (alter_info.flags == Alter_info::ALTER_ADD_PARTITION || + alter_info.flags == Alter_info::ALTER_REORGANIZE_PARTITION)); } #ifdef MYSQL_SERVER diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 61230113506..964acddfc23 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -44,9 +44,6 @@ class Event_parse_data; class set_var_base; class sys_var; class Item_func_match; -class Alter_drop; -class Alter_column; -class Key; class File_parser; class Key_part_spec; @@ -117,6 +114,7 @@ struct sys_var_with_base #include "lex_symbol.h" #if MYSQL_LEX #include "item_func.h" /* Cast_target used in sql_yacc.h */ +#include "sql_get_diagnostics.h" /* Types used in sql_yacc.h */ #include "sql_yacc.h" #define LEX_YYSTYPE YYSTYPE * #else @@ -257,11 +255,6 @@ enum olap_type UNSPECIFIED_OLAP_TYPE, CUBE_TYPE, ROLLUP_TYPE }; -enum tablespace_op_type -{ - NO_TABLESPACE_OP, DISCARD_TABLESPACE, IMPORT_TABLESPACE -}; - /* String names used to print a statement with index hints. Keep in sync with index_hint_type. @@ -505,6 +498,7 @@ public: thr_lock_type flags= TL_UNLOCK, enum_mdl_type mdl_type= MDL_SHARED_READ, List<Index_hint> *hints= 0, + List<String> *partition_names= 0, LEX_STRING *option= 0); virtual void set_lock_for_tables(thr_lock_type lock_type) {} @@ -861,6 +855,7 @@ public: thr_lock_type flags= TL_UNLOCK, enum_mdl_type mdl_type= MDL_SHARED_READ, List<Index_hint> *hints= 0, + List<String> *partition_names= 0, LEX_STRING *option= 0); TABLE_LIST* get_table_list(); bool init_nested_join(THD *thd); @@ -1074,7 +1069,38 @@ public: Sroutine_hash_entry **sroutines_list_own_last; uint sroutines_list_own_elements; - /* + /** + Locking state of tables in this particular statement. + + If we under LOCK TABLES or in prelocked mode we consider tables + for the statement to be "locked" if there was a call to lock_tables() + (which called handler::start_stmt()) for tables of this statement + and there was no matching close_thread_tables() call. + + As result this state may differ significantly from one represented + by Open_tables_state::lock/locked_tables_mode more, which are always + "on" under LOCK TABLES or in prelocked mode. + */ + enum enum_lock_tables_state { + LTS_NOT_LOCKED = 0, + LTS_LOCKED + }; + enum_lock_tables_state lock_tables_state; + bool is_query_tables_locked() + { + return (lock_tables_state == LTS_LOCKED); + } + + /** + Number of tables which were open by open_tables() and to be locked + by lock_tables(). + Note that we set this member only in some cases, when this value + needs to be passed from open_tables() to lock_tables() which are + separated by some amount of code. + */ + uint table_count; + + /* These constructor and destructor serve for creation/destruction of Query_tables_list instances which are used as backup storage. */ @@ -2274,7 +2300,7 @@ struct LEX: public Query_tables_list */ nesting_map allow_sum_func; - Sql_statement *m_stmt; + Sql_cmd *m_sql_cmd; /* Usually `expr` rule of yacc is quite reused but some commands better @@ -2331,6 +2357,7 @@ struct LEX: public Query_tables_list uint8 create_view_check; uint8 context_analysis_only; bool drop_if_exists, drop_temporary, local_file, one_shot_set; + bool check_exists; bool autocommit; bool verbose, no_write_to_binlog; diff --git a/sql/sql_load.cc b/sql/sql_load.cc index 64505b82413..3e80dee40df 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -218,7 +218,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, !field_term->is_ascii() || !ex->line_term->is_ascii() || !ex->line_start->is_ascii()) { - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning(thd, Sql_condition::WARN_LEVEL_WARN, WARN_NON_ASCII_SEPARATOR_NOT_IMPLEMENTED, ER(WARN_NON_ASCII_SEPARATOR_NOT_IMPLEMENTED)); } @@ -588,7 +588,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, } sprintf(name, ER(ER_LOAD_INFO), (ulong) info.records, (ulong) info.deleted, (ulong) (info.records - info.copied), - (ulong) thd->warning_info->statement_warn_count()); + (long) thd->get_stmt_da()->current_statement_warn_count()); if (thd->transaction.stmt.modified_non_trans_table) thd->transaction.all.modified_non_trans_table= TRUE; @@ -829,10 +829,10 @@ read_fixed_length(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, if (pos == read_info.row_end) { thd->cuted_fields++; /* Not enough fields */ - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_WARN_TOO_FEW_RECORDS, ER(ER_WARN_TOO_FEW_RECORDS), - thd->warning_info->current_row_for_warning()); + thd->get_stmt_da()->current_row_for_warning()); /* Timestamp fields that are NOT NULL are autoupdated if there is no corresponding value in the data file. @@ -859,10 +859,10 @@ read_fixed_length(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, if (pos != read_info.row_end) { thd->cuted_fields++; /* To long row */ - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_WARN_TOO_MANY_RECORDS, ER(ER_WARN_TOO_MANY_RECORDS), - thd->warning_info->current_row_for_warning()); + thd->get_stmt_da()->current_row_for_warning()); } if (thd->killed || @@ -895,12 +895,12 @@ read_fixed_length(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, if (read_info.line_cuted) { thd->cuted_fields++; /* To long row */ - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_WARN_TOO_MANY_RECORDS, ER(ER_WARN_TOO_MANY_RECORDS), - thd->warning_info->current_row_for_warning()); + thd->get_stmt_da()->current_row_for_warning()); } - thd->warning_info->inc_current_row_for_warning(); + thd->get_stmt_da()->inc_current_row_for_warning(); continue_loop:; } DBUG_RETURN(test(read_info.error)); @@ -980,7 +980,7 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, if (field->reset()) { my_error(ER_WARN_NULL_TO_NOTNULL, MYF(0), field->field_name, - thd->warning_info->current_row_for_warning()); + thd->get_stmt_da()->current_row_for_warning()); DBUG_RETURN(1); } field->set_null(); @@ -993,7 +993,7 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, if (field->type() == MYSQL_TYPE_TIMESTAMP) field->set_time(); else if (field != table->next_number_field) - field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, + field->set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_NULL_TO_NOTNULL, 1); } /* Do not auto-update this field. */ @@ -1059,7 +1059,7 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, if (field->reset()) { my_error(ER_WARN_NULL_TO_NOTNULL, MYF(0),field->field_name, - thd->warning_info->current_row_for_warning()); + thd->get_stmt_da()->current_row_for_warning()); DBUG_RETURN(1); } if (!field->maybe_null() && field->type() == FIELD_TYPE_TIMESTAMP) @@ -1072,10 +1072,10 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, in the end ?) */ thd->cuted_fields++; - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_WARN_TOO_FEW_RECORDS, ER(ER_WARN_TOO_FEW_RECORDS), - thd->warning_info->current_row_for_warning()); + thd->get_stmt_da()->current_row_for_warning()); } else if (item->type() == Item::STRING_ITEM) { @@ -1119,13 +1119,13 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, if (read_info.line_cuted) { thd->cuted_fields++; /* To long row */ - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_WARN_TOO_MANY_RECORDS, ER(ER_WARN_TOO_MANY_RECORDS), - thd->warning_info->current_row_for_warning()); + thd->get_stmt_da()->current_row_for_warning()); if (thd->killed) DBUG_RETURN(1); } - thd->warning_info->inc_current_row_for_warning(); + thd->get_stmt_da()->inc_current_row_for_warning(); continue_loop:; } DBUG_RETURN(test(read_info.error)); @@ -1206,7 +1206,7 @@ read_xml_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, if (field->type() == FIELD_TYPE_TIMESTAMP) field->set_time(); else if (field != table->next_number_field) - field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, + field->set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_NULL_TO_NOTNULL, 1); } /* Do not auto-update this field. */ @@ -1259,10 +1259,10 @@ read_xml_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, in the end ?) */ thd->cuted_fields++; - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_WARN_TOO_FEW_RECORDS, ER(ER_WARN_TOO_FEW_RECORDS), - thd->warning_info->current_row_for_warning()); + thd->get_stmt_da()->current_row_for_warning()); } else ((Item_user_var_as_out_param *)item)->set_null_value(cs); @@ -1293,7 +1293,7 @@ read_xml_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, its default value at the beginning of each loop iteration. */ thd->transaction.stmt.modified_non_trans_table= no_trans_update_stmt; - thd->warning_info->inc_current_row_for_warning(); + thd->get_stmt_da()->inc_current_row_for_warning(); continue_loop:; } DBUG_RETURN(test(read_info.error) || thd->is_error()); diff --git a/sql/sql_locale.cc b/sql/sql_locale.cc index 13e00c99f19..b2b112ed4ba 100644 --- a/sql/sql_locale.cc +++ b/sql/sql_locale.cc @@ -3422,7 +3422,7 @@ MY_LOCALE *my_locale_by_name(const char *name) if (thd) { // Send a warning to the client - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_WARN_DEPRECATED_SYNTAX, ER(ER_WARN_DEPRECATED_SYNTAX), name, locale->name); } diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 5beba029b20..e80a221f3c4 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -887,7 +887,7 @@ bool do_command(THD *thd) Consider moving to init_connect() instead. */ thd->clear_error(); // Clear error message - thd->stmt_da->reset_diagnostics_area(); + thd->get_stmt_da()->reset_diagnostics_area(); net_new_transaction(net); @@ -1315,10 +1315,10 @@ bool dispatch_command(enum enum_server_command command, THD *thd, (char *) thd->security_ctx->host_or_ip); /* PSI begin */ - thd->m_statement_psi= - MYSQL_START_STATEMENT(&thd->m_statement_state, - com_statement_info[command].m_key, - thd->db, thd->db_length); + thd->m_statement_psi= MYSQL_START_STATEMENT(&thd->m_statement_state, + com_statement_info[command].m_key, + thd->db, thd->db_length, + thd->charset()); THD_STAGE_INFO(thd, stage_init); MYSQL_SET_STATEMENT_TEXT(thd->m_statement_psi, beginning_of_next_stmt, length); @@ -1435,7 +1435,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, /* We don't calculate statistics for this command */ general_log_print(thd, command, NullS); net->error=0; // Don't give 'abort' message - thd->stmt_da->disable_status(); // Don't send anything back + thd->get_stmt_da()->disable_status(); // Don't send anything back error=TRUE; // End server break; #ifndef EMBEDDED_LIBRARY @@ -1589,7 +1589,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, #else (void) my_net_write(net, (uchar*) buff, length); (void) net_flush(net); - thd->stmt_da->disable_status(); + thd->get_stmt_da()->disable_status(); #endif break; } @@ -1665,7 +1665,8 @@ bool dispatch_command(enum enum_server_command command, THD *thd, mysql_audit_general(thd, MYSQL_AUDIT_GENERAL_RESULT, 0, 0); mysql_audit_general(thd, MYSQL_AUDIT_GENERAL_STATUS, - thd->stmt_da->is_error() ? thd->stmt_da->sql_errno() : 0, + thd->get_stmt_da()->is_error() ? + thd->get_stmt_da()->sql_errno() : 0, command_name[command].str); thd->update_all_stats(); @@ -2039,7 +2040,7 @@ bool sp_process_definer(THD *thd) if (!is_acl_user(lex->definer->host.str, lex->definer->user.str)) { push_warning_printf(thd, - MYSQL_ERROR::WARN_LEVEL_NOTE, + Sql_condition::WARN_LEVEL_NOTE, ER_NO_SUCH_USER, ER(ER_NO_SUCH_USER), lex->definer->user.str, @@ -2184,12 +2185,12 @@ mysql_execute_command(THD *thd) variables, but for now this is probably good enough. */ if ((sql_command_flags[lex->sql_command] & CF_DIAGNOSTIC_STMT) != 0) - thd->warning_info->set_read_only(TRUE); + thd->get_stmt_da()->set_warning_info_read_only(TRUE); else { - thd->warning_info->set_read_only(FALSE); + thd->get_stmt_da()->set_warning_info_read_only(FALSE); if (all_tables) - thd->warning_info->opt_clear_warning_info(thd->query_id); + thd->get_stmt_da()->opt_clear_warning_info(thd->query_id); } #ifdef HAVE_REPLICATION @@ -2535,16 +2536,16 @@ case SQLCOM_PREPARE: case SQLCOM_SHOW_WARNS: { res= mysqld_show_warnings(thd, (ulong) - ((1L << (uint) MYSQL_ERROR::WARN_LEVEL_NOTE) | - (1L << (uint) MYSQL_ERROR::WARN_LEVEL_WARN) | - (1L << (uint) MYSQL_ERROR::WARN_LEVEL_ERROR) + ((1L << (uint) Sql_condition::WARN_LEVEL_NOTE) | + (1L << (uint) Sql_condition::WARN_LEVEL_WARN) | + (1L << (uint) Sql_condition::WARN_LEVEL_ERROR) )); break; } case SQLCOM_SHOW_ERRORS: { res= mysqld_show_warnings(thd, (ulong) - (1L << (uint) MYSQL_ERROR::WARN_LEVEL_ERROR)); + (1L << (uint) Sql_condition::WARN_LEVEL_ERROR)); break; } case SQLCOM_SHOW_PROFILES: @@ -2614,7 +2615,7 @@ case SQLCOM_PREPARE: mysql_mutex_lock(&LOCK_active_mi); mi= master_info_index->get_master_info(&lex_mi->connection_name, - MYSQL_ERROR::WARN_LEVEL_NOTE); + Sql_condition::WARN_LEVEL_NOTE); if (mi == NULL) { @@ -2662,7 +2663,7 @@ case SQLCOM_PREPARE: LEX_MASTER_INFO *lex_mi= &thd->lex->mi; Master_info *mi; mi= master_info_index->get_master_info(&lex_mi->connection_name, - MYSQL_ERROR::WARN_LEVEL_ERROR); + Sql_condition::WARN_LEVEL_ERROR); if (mi != NULL) { res= show_master_info(thd, mi, 0); @@ -2820,7 +2821,7 @@ case SQLCOM_PREPARE: */ if (splocal_refs != thd->query_name_consts) push_warning(thd, - MYSQL_ERROR::WARN_LEVEL_WARN, + Sql_condition::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR, "Invoked routine ran a statement that may cause problems with " "binary log, see 'NAME_CONST issues' in 'Binary Logging of Stored Programs' " @@ -2857,7 +2858,7 @@ case SQLCOM_PREPARE: { if (create_info.options & HA_LEX_CREATE_IF_NOT_EXISTS) { - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_TABLE_EXISTS_ERROR, ER(ER_TABLE_EXISTS_ERROR), create_info.alias); @@ -2967,7 +2968,7 @@ end_with_restore_list: res= mysql_alter_table(thd, first_table->db, first_table->table_name, &create_info, first_table, &alter_info, - 0, (ORDER*) 0, 0, 0); + 0, (ORDER*) 0, 0); break; } #ifdef HAVE_REPLICATION @@ -2979,7 +2980,7 @@ end_with_restore_list: if ((mi= (master_info_index-> get_master_info(&lex_mi->connection_name, - MYSQL_ERROR::WARN_LEVEL_ERROR)))) + Sql_condition::WARN_LEVEL_ERROR)))) if (!start_slave(thd, mi, 1 /* net report*/)) my_ok(thd); mysql_mutex_unlock(&LOCK_active_mi); @@ -3015,7 +3016,7 @@ end_with_restore_list: mysql_mutex_lock(&LOCK_active_mi); if ((mi= (master_info_index-> get_master_info(&lex_mi->connection_name, - MYSQL_ERROR::WARN_LEVEL_ERROR)))) + Sql_condition::WARN_LEVEL_ERROR)))) if (!stop_slave(thd, mi, 1/* net report*/)) my_ok(thd); mysql_mutex_unlock(&LOCK_active_mi); @@ -3922,7 +3923,7 @@ end_with_restore_list: goto error; if (specialflag & SPECIAL_NO_RESOLVE && hostname_requires_resolving(user->host.str)) - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_WARN_HOSTNAME_WONT_WORK, ER(ER_WARN_HOSTNAME_WONT_WORK)); // Are we trying to change a password of another user @@ -4317,7 +4318,7 @@ end_with_restore_list: { if (sp_grant_privileges(thd, lex->sphead->m_db.str, name, lex->sql_command == SQLCOM_CREATE_PROCEDURE)) - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning(thd, Sql_condition::WARN_LEVEL_WARN, ER_PROC_AUTO_GRANT_FAIL, ER(ER_PROC_AUTO_GRANT_FAIL)); thd->clear_error(); } @@ -4522,7 +4523,7 @@ create_sp_error: { if (lex->drop_if_exists) { - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST), "FUNCTION (UDF)", lex->spname->m_name.str); res= FALSE; @@ -4575,7 +4576,7 @@ create_sp_error: sp_revoke_privileges(thd, db, name, lex->sql_command == SQLCOM_DROP_PROCEDURE)) { - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning(thd, Sql_condition::WARN_LEVEL_WARN, ER_PROC_AUTO_REVOKE_FAIL, ER(ER_PROC_AUTO_REVOKE_FAIL)); /* If this happens, an error should have been reported. */ @@ -4592,7 +4593,7 @@ create_sp_error: if (lex->drop_if_exists) { res= write_bin_log(thd, TRUE, thd->query(), thd->query_length()); - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST), SP_COM_STRING(lex), lex->spname->m_qname.str); if (!res) @@ -4830,10 +4831,12 @@ create_sp_error: /* fall through */ case SQLCOM_SIGNAL: case SQLCOM_RESIGNAL: - DBUG_ASSERT(lex->m_stmt != NULL); - res= lex->m_stmt->execute(thd); + case SQLCOM_GET_DIAGNOSTICS: + DBUG_ASSERT(lex->m_sql_cmd != NULL); + res= lex->m_sql_cmd->execute(thd); break; default: + #ifndef EMBEDDED_LIBRARY DBUG_ASSERT(0); /* Impossible */ #endif @@ -4875,7 +4878,7 @@ finish: if (thd->killed_errno()) { /* If we already sent 'ok', we can ignore any kill query statements */ - if (! thd->stmt_da->is_set()) + if (! thd->get_stmt_da()->is_set()) thd->send_kill_message(); } if (thd->killed < KILL_CONNECTION) @@ -4889,9 +4892,9 @@ finish: else { /* If commit fails, we should be able to reset the OK status. */ - thd->stmt_da->can_overwrite_status= TRUE; + thd->get_stmt_da()->set_overwrite_status(true); trans_commit_stmt(thd); - thd->stmt_da->can_overwrite_status= FALSE; + thd->get_stmt_da()->set_overwrite_status(false); } #ifdef WITH_ARIA_STORAGE_ENGINE ha_maria::implicit_commit(thd, FALSE); @@ -4918,10 +4921,10 @@ finish: /* No transaction control allowed in sub-statements. */ DBUG_ASSERT(! thd->in_sub_stmt); /* If commit fails, we should be able to reset the OK status. */ - thd->stmt_da->can_overwrite_status= TRUE; + thd->get_stmt_da()->set_overwrite_status(true); /* Commit the normal transaction if one is active. */ trans_commit_implicit(thd); - thd->stmt_da->can_overwrite_status= FALSE; + thd->get_stmt_da()->set_overwrite_status(false); thd->mdl_context.release_transactional_locks(); } else if (! thd->in_sub_stmt && ! thd->in_multi_stmt_transaction_mode()) @@ -4987,7 +4990,7 @@ static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables) mysqld_show_warnings(). */ thd->lex->unit.print(&str, QT_TO_SYSTEM_CHARSET); - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + push_warning(thd, Sql_condition::WARN_LEVEL_NOTE, ER_YES, str.c_ptr_safe()); } if (res) @@ -5844,8 +5847,8 @@ void THD::reset_for_next_command(bool calculate_userstat) thd->user_var_events_alloc= thd->mem_root; } thd->clear_error(); - thd->stmt_da->reset_diagnostics_area(); - thd->warning_info->reset_for_next_command(); + thd->get_stmt_da()->reset_diagnostics_area(); + thd->get_stmt_da()->reset_for_next_command(); thd->rand_used= 0; thd->m_sent_row_count= thd->m_examined_row_count= 0; thd->accessed_rows_and_keys= 0; @@ -6370,6 +6373,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, thr_lock_type lock_type, enum_mdl_type mdl_type, List<Index_hint> *index_hints_arg, + List<String> *partition_names, LEX_STRING *option) { register TABLE_LIST *ptr; @@ -6514,6 +6518,9 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, */ table_list.link_in_list(ptr, &ptr->next_local); ptr->next_name_resolution_table= NULL; +#ifdef WITH_PARTITION_STORAGE_ENGINE + ptr->partition_names= partition_names; +#endif /* WITH_PARTITION_STORAGE_ENGINE */ /* Link table in global list (all used tables) */ lex->add_to_query_tables(ptr); @@ -8006,7 +8013,7 @@ bool parse_sql(THD *thd, bool ret_value; DBUG_ENTER("parse_sql"); DBUG_ASSERT(thd->m_parser_state == NULL); - DBUG_ASSERT(thd->lex->m_stmt == NULL); + DBUG_ASSERT(thd->lex->m_sql_cmd == NULL); MYSQL_QUERY_PARSE_START(thd->query()); /* Backup creation context. */ diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index f5f72fcdd9c..bb94e2392c5 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -59,6 +59,7 @@ #include <m_ctype.h> #include "my_md5.h" #include "transaction.h" +#include "debug_sync.h" #include "sql_base.h" // close_all_tables_for_name #include "sql_table.h" // build_table_filename, @@ -67,6 +68,8 @@ // mysql_*_alter_copy_data #include "opt_range.h" // store_key_image_to_rec #include "sql_analyse.h" // append_escaped +#include "sql_alter.h" // Alter_table_ctx + #ifdef WITH_PARTITION_STORAGE_ENGINE #include "ha_partition.h" @@ -575,7 +578,13 @@ static bool set_up_field_array(TABLE *table, } while (++inx < num_fields); if (inx == num_fields) { - mem_alloc_error(1); + /* + Should not occur since it should already been checked in either + add_column_list_values, handle_list_of_fields, + check_partition_info etc. + */ + DBUG_ASSERT(0); + my_error(ER_FIELD_NOT_FOUND_PART_ERROR, MYF(0)); result= TRUE; continue; } @@ -879,7 +888,8 @@ static bool handle_list_of_fields(List_iterator<char> it, uint primary_key= table->s->primary_key; if (primary_key != MAX_KEY) { - uint num_key_parts= table->key_info[primary_key].key_parts, i; + uint num_key_parts= table->key_info[primary_key].user_defined_key_parts; + uint i; /* In the case of an empty list we use primary key as partition key. */ @@ -1072,7 +1082,7 @@ static bool fix_fields_part_func(THD *thd, Item* func_expr, TABLE *table, goto end; } else - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning(thd, Sql_condition::WARN_LEVEL_WARN, ER_WRONG_EXPR_IN_PARTITION_FUNC_ERROR, ER(ER_WRONG_EXPR_IN_PARTITION_FUNC_ERROR)); } @@ -1241,20 +1251,17 @@ void check_range_capable_PF(TABLE *table) } -/* - Set up partition bitmap +/** + Set up partition bitmaps - SYNOPSIS - set_up_partition_bitmap() - thd Thread object - part_info Reference to partitioning data structure + @param thd Thread object + @param part_info Reference to partitioning data structure - RETURN VALUE - TRUE Memory allocation failure - FALSE Success + @return Operation status + @retval TRUE Memory allocation failure + @retval FALSE Success - DESCRIPTION - Allocate memory for bitmap of the partitioned table + Allocate memory for bitmaps of the partitioned table and initialise it. */ @@ -1265,15 +1272,23 @@ static bool set_up_partition_bitmap(THD *thd, partition_info *part_info) (part_info->num_subparts* part_info->num_parts): part_info->num_parts; uint bitmap_bytes= bitmap_buffer_size(bitmap_bits); - DBUG_ENTER("set_up_partition_bitmap"); + DBUG_ENTER("set_up_partition_bitmaps"); - if (!(bitmap_buf= (uint32*)thd->alloc(bitmap_bytes))) + DBUG_ASSERT(!part_info->bitmaps_are_initialized); + + /* Allocate for both read and lock_partitions */ + if (!(bitmap_buf= (uint32*) alloc_root(&part_info->table->mem_root, + bitmap_bytes * 2))) { - mem_alloc_error(bitmap_bytes); + mem_alloc_error(bitmap_bytes * 2); DBUG_RETURN(TRUE); } - bitmap_init(&part_info->used_partitions, bitmap_buf, bitmap_bytes*8, FALSE); - bitmap_set_all(&part_info->used_partitions); + bitmap_init(&part_info->read_partitions, bitmap_buf, bitmap_bits, FALSE); + /* Use the second half of the allocated buffer for lock_partitions */ + bitmap_init(&part_info->lock_partitions, bitmap_buf + (bitmap_bytes / 4), + bitmap_bits, FALSE); + part_info->bitmaps_are_initialized= TRUE; + part_info->set_partition_bitmaps(NULL); DBUG_RETURN(FALSE); } @@ -3824,6 +3839,92 @@ void get_full_part_id_from_key(const TABLE *table, uchar *buf, DBUG_VOID_RETURN; } + +/** + @brief Verify that all rows in a table is in the given partition + + @param table Table which contains the data that will be checked if + it is matching the partition definition. + @param part_table Partitioned table containing the partition to check. + @param part_id Which partition to match with. + + @return Operation status + @retval TRUE Not all rows match the given partition + @retval FALSE OK +*/ +bool verify_data_with_partition(TABLE *table, TABLE *part_table, + uint32 part_id) +{ + uint32 found_part_id; + longlong func_value; /* Unused */ + handler *file; + int error; + uchar *old_rec; + partition_info *part_info; + DBUG_ENTER("verify_data_with_partition"); + DBUG_ASSERT(table && table->file && part_table && part_table->part_info && + part_table->file); + + /* + Verify all table rows. + First implementation uses full scan + evaluates partition functions for + every row. TODO: add optimization to use index if possible, see WL#5397. + + 1) Open both tables (already done) and set the row buffers to use + the same buffer (to avoid copy). + 2) Init rnd on table. + 3) loop over all rows. + 3.1) verify that partition_id on the row is correct. Break if error. + */ + file= table->file; + part_info= part_table->part_info; + bitmap_union(table->read_set, &part_info->full_part_field_set); + old_rec= part_table->record[0]; + part_table->record[0]= table->record[0]; + set_field_ptr(part_info->full_part_field_array, table->record[0], old_rec); + if ((error= file->ha_rnd_init(TRUE))) + { + file->print_error(error, MYF(0)); + goto err; + } + + do + { + if ((error= file->ha_rnd_next(table->record[0]))) + { + if (error == HA_ERR_RECORD_DELETED) + continue; + if (error == HA_ERR_END_OF_FILE) + error= 0; + else + file->print_error(error, MYF(0)); + break; + } + if ((error= part_info->get_partition_id(part_info, &found_part_id, + &func_value))) + { + part_table->file->print_error(error, MYF(0)); + break; + } + DEBUG_SYNC(current_thd, "swap_partition_first_row_read"); + if (found_part_id != part_id) + { + my_error(ER_ROW_DOES_NOT_MATCH_PARTITION, MYF(0)); + error= 1; + break; + } + } while (TRUE); + (void) file->ha_rnd_end(); +err: + set_field_ptr(part_info->full_part_field_array, old_rec, + table->record[0]); + part_table->record[0]= old_rec; + if (error) + DBUG_RETURN(TRUE); + DBUG_RETURN(FALSE); +} + + /* Prune the set of partitions to use in query @@ -3834,7 +3935,7 @@ void get_full_part_id_from_key(const TABLE *table, uchar *buf, DESCRIPTION This function is called to prune the range of partitions to scan by - checking the used_partitions bitmap. + checking the read_partitions bitmap. If start_part > end_part at return it means no partition needs to be scanned. If start_part == end_part it always means a single partition needs to be scanned. @@ -3851,7 +3952,7 @@ void prune_partition_set(const TABLE *table, part_id_range *part_spec) DBUG_ENTER("prune_partition_set"); for (i= part_spec->start_part; i <= part_spec->end_part; i++) { - if (bitmap_is_set(&(part_info->used_partitions), i)) + if (bitmap_is_set(&(part_info->read_partitions), i)) { DBUG_PRINT("info", ("Partition %d is set", i)); if (last_partition == -1) @@ -3933,7 +4034,7 @@ void get_partition_set(const TABLE *table, uchar *buf, const uint index, */ get_full_part_id_from_key(table,buf,key_info,key_spec,part_spec); /* - Check if range can be adjusted by looking in used_partitions + Check if range can be adjusted by looking in read_partitions */ prune_partition_set(table, part_spec); DBUG_VOID_RETURN; @@ -3985,7 +4086,7 @@ void get_partition_set(const TABLE *table, uchar *buf, const uint index, get_full_part_id_from_key(table,buf,key_info,key_spec,part_spec); clear_indicator_in_key_fields(key_info); /* - Check if range can be adjusted by looking in used_partitions + Check if range can be adjusted by looking in read_partitions */ prune_partition_set(table, part_spec); DBUG_VOID_RETURN; @@ -4055,7 +4156,7 @@ void get_partition_set(const TABLE *table, uchar *buf, const uint index, if (found_part_field) clear_indicator_in_key_fields(key_info); /* - Check if range can be adjusted by looking in used_partitions + Check if range can be adjusted by looking in read_partitions */ prune_partition_set(table, part_spec); DBUG_VOID_RETURN; @@ -4203,6 +4304,7 @@ bool mysql_unpack_partition(THD *thd, *work_part_info_used= true; } table->part_info= part_info; + part_info->table= table; table->file->set_part_info(part_info); if (!part_info->default_engine_type) part_info->default_engine_type= default_db_type; @@ -4420,7 +4522,7 @@ bool set_part_state(Alter_info *alter_info, partition_info *tab_part_info, do { partition_element *part_elem= part_it++; - if ((alter_info->flags & ALTER_ALL_PARTITION) || + if ((alter_info->flags & Alter_info::ALTER_ALL_PARTITION) || (is_name_in_list(part_elem->partition_name, alter_info->partition_names))) { @@ -4439,7 +4541,7 @@ bool set_part_state(Alter_info *alter_info, partition_info *tab_part_info, } while (++part_count < tab_part_info->num_parts); if (num_parts_found != alter_info->partition_names.elements && - !(alter_info->flags & ALTER_ALL_PARTITION)) + !(alter_info->flags & Alter_info::ALTER_ALL_PARTITION)) { /* Not all given partitions found, revert and return failure */ part_it.rewind(); @@ -4456,16 +4558,60 @@ bool set_part_state(Alter_info *alter_info, partition_info *tab_part_info, /** + @brief Check if partition is exchangable with table by checking table options + + @param table_create_info Table options from table. + @param part_elem All the info of the partition. + + @retval FALSE if they are equal, otherwise TRUE. + + @note Any differens that would cause a change in the frm file is prohibited. + Such options as data_file_name, index_file_name, min_rows, max_rows etc. are + not allowed to differ. But comment is allowed to differ. +*/ +bool compare_partition_options(HA_CREATE_INFO *table_create_info, + partition_element *part_elem) +{ +#define MAX_COMPARE_PARTITION_OPTION_ERRORS 5 + const char *option_diffs[MAX_COMPARE_PARTITION_OPTION_ERRORS + 1]; + int i, errors= 0; + DBUG_ENTER("compare_partition_options"); + DBUG_ASSERT(!part_elem->tablespace_name && + !table_create_info->tablespace); + + /* + Note that there are not yet any engine supporting tablespace together + with partitioning. TODO: when there are, add compare. + */ + if (part_elem->tablespace_name || table_create_info->tablespace) + option_diffs[errors++]= "TABLESPACE"; + if (part_elem->part_max_rows != table_create_info->max_rows) + option_diffs[errors++]= "MAX_ROWS"; + if (part_elem->part_min_rows != table_create_info->min_rows) + option_diffs[errors++]= "MIN_ROWS"; + if (part_elem->data_file_name || table_create_info->data_file_name) + option_diffs[errors++]= "DATA DIRECTORY"; + if (part_elem->index_file_name || table_create_info->index_file_name) + option_diffs[errors++]= "INDEX DIRECTORY"; + + for (i= 0; i < errors; i++) + my_error(ER_PARTITION_EXCHANGE_DIFFERENT_OPTION, MYF(0), + option_diffs[i]); + DBUG_RETURN(errors != 0); +} + + +/** Prepare for ALTER TABLE of partition structure @param[in] thd Thread object @param[in] table Table object @param[in,out] alter_info Alter information @param[in,out] create_info Create info for CREATE TABLE - @param[in] old_db_type Old engine type + @param[in] alter_ctx ALTER TABLE runtime context @param[out] partition_changed Boolean indicating whether partition changed - @param[out] fast_alter_table Internal temporary table allowing fast - partition change or NULL if not possible + @param[out] fast_alter_table Boolean indicating if fast partition alter is + possible. @return Operation status @retval TRUE Error @@ -4483,22 +4629,27 @@ bool set_part_state(Alter_info *alter_info, partition_info *tab_part_info, uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info, HA_CREATE_INFO *create_info, - handlerton *old_db_type, + Alter_table_ctx *alter_ctx, bool *partition_changed, - char *db, - const char *table_name, - const char *path, - TABLE **fast_alter_table) + bool *fast_alter_table) { TABLE *new_table= NULL; DBUG_ENTER("prep_alter_part_table"); /* Foreign keys on partitioned tables are not supported, waits for WL#148 */ - if (table->part_info && (alter_info->flags & ALTER_FOREIGN_KEY)) + if (table->part_info && (alter_info->flags & Alter_info::ADD_FOREIGN_KEY || + alter_info->flags & Alter_info::DROP_FOREIGN_KEY)) { my_error(ER_FOREIGN_KEY_ON_PARTITIONED, MYF(0)); DBUG_RETURN(TRUE); } + /* Remove partitioning on a not partitioned table is not possible */ + if (!table->part_info && (alter_info->flags & + Alter_info::ALTER_REMOVE_PARTITIONING)) + { + my_error(ER_PARTITION_MGMT_ON_NONPARTITIONED, MYF(0)); + DBUG_RETURN(TRUE); + } thd->work_part_info= thd->lex->part_info; @@ -4507,12 +4658,15 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info, DBUG_RETURN(TRUE); /* ALTER_ADMIN_PARTITION is handled in mysql_admin_table */ - DBUG_ASSERT(!(alter_info->flags & ALTER_ADMIN_PARTITION)); + DBUG_ASSERT(!(alter_info->flags & Alter_info::ALTER_ADMIN_PARTITION)); if (alter_info->flags & - (ALTER_ADD_PARTITION | ALTER_DROP_PARTITION | - ALTER_COALESCE_PARTITION | ALTER_REORGANIZE_PARTITION | - ALTER_TABLE_REORG | ALTER_REBUILD_PARTITION)) + (Alter_info::ALTER_ADD_PARTITION | + Alter_info::ALTER_DROP_PARTITION | + Alter_info::ALTER_COALESCE_PARTITION | + Alter_info::ALTER_REORGANIZE_PARTITION | + Alter_info::ALTER_TABLE_REORG | + Alter_info::ALTER_REBUILD_PARTITION)) { partition_info *tab_part_info; partition_info *alt_part_info= thd->work_part_info; @@ -4534,21 +4688,16 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info, Open it as a copy of the original table, and modify its partition_info object to allow fast_alter_partition_table to perform the changes. */ - DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, db, table_name, + DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, + alter_ctx->db, + alter_ctx->table_name, MDL_INTENTION_EXCLUSIVE)); - new_table= open_table_uncached(thd, path, db, table_name, 0); - if (!new_table) - DBUG_RETURN(TRUE); - /* - This table may be used for copy rows between partitions - and also read/write columns when fixing the partition_info struct. - */ new_table->use_all_columns(); tab_part_info= new_table->part_info; - if (alter_info->flags & ALTER_TABLE_REORG) + if (alter_info->flags & Alter_info::ALTER_TABLE_REORG) { uint new_part_no, curr_part_no; if (tab_part_info->part_type != HASH_PARTITION || @@ -4557,7 +4706,7 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info, my_error(ER_REORG_NO_PARAM_ERROR, MYF(0)); goto err; } - new_part_no= new_table->file->get_default_no_partitions(create_info); + new_part_no= table->file->get_default_no_partitions(create_info); curr_part_no= tab_part_info->num_parts; if (new_part_no == curr_part_no) { @@ -4566,7 +4715,7 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info, after the change as before. Thus we can reply ok immediately without any changes at all. */ - *fast_alter_table= new_table; + *fast_alter_table= true; thd->work_part_info= tab_part_info; DBUG_RETURN(FALSE); } @@ -4576,7 +4725,7 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info, We will add more partitions, we use the ADD PARTITION without setting the flag for no default number of partitions */ - alter_info->flags|= ALTER_ADD_PARTITION; + alter_info->flags|= Alter_info::ALTER_ADD_PARTITION; thd->work_part_info->num_parts= new_part_no - curr_part_no; } else @@ -4585,21 +4734,17 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info, We will remove hash partitions, we use the COALESCE PARTITION without setting the flag for no default number of partitions */ - alter_info->flags|= ALTER_COALESCE_PARTITION; + alter_info->flags|= Alter_info::ALTER_COALESCE_PARTITION; alter_info->num_parts= curr_part_no - new_part_no; } } - if (!(flags= new_table->file->alter_table_flags(alter_info->flags))) + if (!(flags= table->file->alter_table_flags(alter_info->flags))) { my_error(ER_PARTITION_FUNCTION_FAILURE, MYF(0)); goto err; } - if ((flags & (HA_FAST_CHANGE_PARTITION | HA_PARTITION_ONE_PHASE)) != 0) - *fast_alter_table= new_table; - DBUG_PRINT("info", ("*fast_alter_table: %p flags: 0x%x", - *fast_alter_table, flags)); - if ((alter_info->flags & ALTER_ADD_PARTITION) || - (alter_info->flags & ALTER_REORGANIZE_PARTITION)) + if ((alter_info->flags & Alter_info::ALTER_ADD_PARTITION) || + (alter_info->flags & Alter_info::ALTER_REORGANIZE_PARTITION)) { if (thd->work_part_info->part_type != tab_part_info->part_type) { @@ -4666,7 +4811,7 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info, goto err; } } - if (alter_info->flags & ALTER_ADD_PARTITION) + if (alter_info->flags & Alter_info::ALTER_ADD_PARTITION) { /* We start by moving the new partitions to the list of temporary @@ -4717,8 +4862,8 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info, } alt_part_info->part_type= tab_part_info->part_type; alt_part_info->subpart_type= tab_part_info->subpart_type; - if (alt_part_info->set_up_defaults_for_partitioning(new_table->file, - ULL(0), + if (alt_part_info->set_up_defaults_for_partitioning(table->file, + ULL(0), tab_part_info->num_parts)) { goto err; @@ -4906,7 +5051,7 @@ that are reorganised. of partitions anymore. We use this code also for Table reorganisations and here we don't set any default flags to FALSE. */ - if (!(alter_info->flags & ALTER_TABLE_REORG)) + if (!(alter_info->flags & Alter_info::ALTER_TABLE_REORG)) { if (!alt_part_info->use_default_partitions) { @@ -4917,7 +5062,7 @@ that are reorganised. tab_part_info->is_auto_partitioned= FALSE; } } - else if (alter_info->flags & ALTER_DROP_PARTITION) + else if (alter_info->flags & Alter_info::ALTER_DROP_PARTITION) { /* Drop a partition from a range partition and list partitioning is @@ -4961,14 +5106,14 @@ that are reorganised. my_error(ER_DROP_PARTITION_NON_EXISTENT, MYF(0), "DROP"); goto err; } - if (new_table->file->is_fk_defined_on_table_or_index(MAX_KEY)) + if (table->file->is_fk_defined_on_table_or_index(MAX_KEY)) { my_error(ER_ROW_IS_REFERENCED, MYF(0)); goto err; } tab_part_info->num_parts-= num_parts_dropped; } - else if (alter_info->flags & ALTER_REBUILD_PARTITION) + else if (alter_info->flags & Alter_info::ALTER_REBUILD_PARTITION) { if (set_part_state(alter_info, tab_part_info, PART_CHANGED)) { @@ -4977,11 +5122,11 @@ that are reorganised. } if (!(*fast_alter_table)) { - new_table->file->print_error(HA_ERR_WRONG_COMMAND, MYF(0)); + table->file->print_error(HA_ERR_WRONG_COMMAND, MYF(0)); goto err; } } - else if (alter_info->flags & ALTER_COALESCE_PARTITION) + else if (alter_info->flags & Alter_info::ALTER_COALESCE_PARTITION) { uint num_parts_coalesced= alter_info->num_parts; uint num_parts_remain= tab_part_info->num_parts - num_parts_coalesced; @@ -5079,13 +5224,13 @@ state of p1. } while (part_count < tab_part_info->num_parts); tab_part_info->num_parts= num_parts_remain; } - if (!(alter_info->flags & ALTER_TABLE_REORG)) + if (!(alter_info->flags & Alter_info::ALTER_TABLE_REORG)) { tab_part_info->use_default_num_partitions= FALSE; tab_part_info->is_auto_partitioned= FALSE; } } - else if (alter_info->flags & ALTER_REORGANIZE_PARTITION) + else if (alter_info->flags & Alter_info::ALTER_REORGANIZE_PARTITION) { /* Reorganise partitions takes a number of partitions that are next @@ -5133,8 +5278,8 @@ state of p1. alt_part_info->subpart_type= tab_part_info->subpart_type; alt_part_info->num_subparts= tab_part_info->num_subparts; DBUG_ASSERT(!alt_part_info->use_default_partitions); - if (alt_part_info->set_up_defaults_for_partitioning(new_table->file, - ULL(0), + if (alt_part_info->set_up_defaults_for_partitioning(table->file, + ULL(0), 0)) { goto err; @@ -5258,8 +5403,8 @@ the generated partition syntax in a correct manner. } *partition_changed= TRUE; thd->work_part_info= tab_part_info; - if (alter_info->flags & ALTER_ADD_PARTITION || - alter_info->flags & ALTER_REORGANIZE_PARTITION) + if (alter_info->flags & Alter_info::ALTER_ADD_PARTITION || + alter_info->flags & Alter_info::ALTER_REORGANIZE_PARTITION) { if (tab_part_info->use_default_subpartitions && !alt_part_info->use_default_subpartitions) @@ -5268,7 +5413,7 @@ the generated partition syntax in a correct manner. tab_part_info->use_default_num_subpartitions= FALSE; } if (tab_part_info->check_partition_info(thd, (handlerton**)NULL, - new_table->file, ULL(0), TRUE)) + table->file, ULL(0), TRUE)) { goto err; } @@ -5277,7 +5422,7 @@ the generated partition syntax in a correct manner. since this function "fixes" the item trees of the new partitions to reorganize into */ - if (alter_info->flags == ALTER_REORGANIZE_PARTITION && + if (alter_info->flags == Alter_info::ALTER_REORGANIZE_PARTITION && tab_part_info->part_type == RANGE_PARTITION && ((is_last_partition_reorged && (tab_part_info->column_list ? @@ -5358,7 +5503,7 @@ the generated partition syntax in a correct manner. */ if (table->part_info) { - if (alter_info->flags & ALTER_REMOVE_PARTITIONING) + if (alter_info->flags & Alter_info::ALTER_REMOVE_PARTITIONING) { DBUG_PRINT("info", ("Remove partitioning")); if (!(create_info->used_fields & HA_CREATE_USED_ENGINE)) @@ -5451,15 +5596,7 @@ the generated partition syntax in a correct manner. } DBUG_RETURN(FALSE); err: - if (new_table) - { - /* - Only remove the intermediate table object and its share object, - do not remove the .frm file, since it is the original one. - */ - close_temporary(new_table, 1, 0); - } - *fast_alter_table= NULL; + *fast_alter_table= false; DBUG_RETURN(TRUE); } @@ -5500,9 +5637,6 @@ static bool mysql_change_partitions(ALTER_PARTITION_PARAM_TYPE *lpt) build_table_filename(path, sizeof(path) - 1, lpt->db, lpt->table_name, "", 0); - /* First lock the original tables */ - if (file->ha_external_lock(thd, F_WRLCK)) - DBUG_RETURN(TRUE); /* Disable transactions for all new tables */ if (mysql_trans_prepare_alter_copy_data(thd)) @@ -5520,8 +5654,6 @@ static bool mysql_change_partitions(ALTER_PARTITION_PARAM_TYPE *lpt) if (mysql_trans_commit_alter_copy_data(thd)) error= 1; /* The error has been reported */ - if (file->ha_external_lock(thd, F_UNLCK)) - error= 1; DBUG_RETURN(test(error)); } @@ -5593,6 +5725,11 @@ static bool mysql_drop_partitions(ALTER_PARTITION_PARAM_TYPE *lpt) int error; DBUG_ENTER("mysql_drop_partitions"); + DBUG_ASSERT(lpt->thd->mdl_context.is_lock_owner(MDL_key::TABLE, + lpt->table->s->db.str, + lpt->table->s->table_name.str, + MDL_EXCLUSIVE)); + build_table_filename(path, sizeof(path) - 1, lpt->db, lpt->table_name, "", 0); if ((error= lpt->table->file->ha_drop_partitions(path))) { @@ -6174,7 +6311,7 @@ static bool write_log_final_change_partition(ALTER_PARTITION_PARAM_TYPE *lpt) if (write_log_changed_partitions(lpt, &next_entry, (const char*)path)) goto error; if (write_log_dropped_partitions(lpt, &next_entry, (const char*)path, - lpt->alter_info->flags & ALTER_REORGANIZE_PARTITION)) + lpt->alter_info->flags & Alter_info::ALTER_REORGANIZE_PARTITION)) goto error; if (write_log_replace_delete_frm(lpt, next_entry, shadow_path, path, TRUE)) goto error; @@ -6271,47 +6408,54 @@ static void alter_partition_lock_handling(ALTER_PARTITION_PARAM_TYPE *lpt) { THD *thd= lpt->thd; - if (lpt->old_table) - close_all_tables_for_name(thd, lpt->old_table->s, HA_EXTRA_NOT_USED); if (lpt->table) { /* - Only remove the intermediate table object and its share object, - do not remove the .frm file, since it is the original one. + Remove all instances of the table and its locks and other resources. */ - close_temporary(lpt->table, 1, 0); + close_all_tables_for_name(thd, lpt->table->s, HA_EXTRA_NOT_USED, NULL); } lpt->table= 0; - lpt->old_table= 0; lpt->table_list->table= 0; - if (thd->locked_tables_list.reopen_tables(thd)) - sql_print_warning("We failed to reacquire LOCKs in ALTER TABLE"); + if (thd->locked_tables_mode) + { + Diagnostics_area *stmt_da= NULL; + Diagnostics_area tmp_stmt_da(true); + + if (thd->is_error()) + { + /* reopen might fail if we have a previous error, use a temporary da. */ + stmt_da= thd->get_stmt_da(); + thd->set_stmt_da(&tmp_stmt_da); + } + + if (thd->locked_tables_list.reopen_tables(thd)) + sql_print_warning("We failed to reacquire LOCKs in ALTER TABLE"); + + if (stmt_da) + thd->set_stmt_da(stmt_da); + } } -/* - Unlock and close table before renaming and dropping partitions - SYNOPSIS - alter_close_tables() - lpt Struct carrying parameters - close_old Close original table too - RETURN VALUES - 0 +/** + Unlock and close table before renaming and dropping partitions. + + @param lpt Struct carrying parameters + + @return Always 0. */ -static int alter_close_tables(ALTER_PARTITION_PARAM_TYPE *lpt, bool close_old) +static int alter_close_table(ALTER_PARTITION_PARAM_TYPE *lpt) { - DBUG_ENTER("alter_close_tables"); + DBUG_ENTER("alter_close_table"); + if (lpt->table->db_stat) { + mysql_lock_remove(lpt->thd, lpt->thd->lock, lpt->table); lpt->table->file->ha_close(); lpt->table->db_stat= 0; // Mark file closed } - if (close_old && lpt->old_table) - { - close_all_tables_for_name(lpt->thd, lpt->old_table->s, HA_EXTRA_NOT_USED); - lpt->old_table= 0; - } DBUG_RETURN(0); } @@ -6333,23 +6477,55 @@ void handle_alter_part_error(ALTER_PARTITION_PARAM_TYPE *lpt, bool close_table) { partition_info *part_info= lpt->part_info; + THD *thd= lpt->thd; + TABLE *table= lpt->table; DBUG_ENTER("handle_alter_part_error"); + DBUG_ASSERT(table->m_needs_reopen); if (close_table) { /* - Since the error handling (ddl_log) needs to drop newly created - partitions they must be closed first to not issue errors. - But we still need some information from the part_info object, - so we clone it first to have a copy. + All instances of this table needs to be closed. + Better to do that here, than leave the cleaning up to others. + Aquire EXCLUSIVE mdl lock if not already aquired. */ + if (!thd->mdl_context.is_lock_owner(MDL_key::TABLE, lpt->db, + lpt->table_name, + MDL_EXCLUSIVE)) + { + if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN)) + { + /* At least remove this instance on failure */ + goto err_exclusive_lock; + } + } + /* Ensure the share is destroyed and reopened. */ + part_info= lpt->part_info->get_clone(); + close_all_tables_for_name(thd, table->s, HA_EXTRA_NOT_USED, NULL); + } + + else + { +err_exclusive_lock: + /* + Temporarily remove it from the locked table list, so that it will get + reopened. + */ + thd->locked_tables_list.unlink_from_list(thd, + table->pos_in_locked_tables, + false); + /* + Make sure that the table is unlocked, closed and removed from + the table cache. + */ + mysql_lock_remove(thd, thd->lock, table); part_info= lpt->part_info->get_clone(); - alter_close_tables(lpt, action_completed); + close_thread_table(thd, &thd->open_tables); + lpt->table_list->table= NULL; } if (part_info->first_log_entry && - execute_ddl_log_entry(lpt->thd, - part_info->first_log_entry->entry_pos)) + execute_ddl_log_entry(thd, part_info->first_log_entry->entry_pos)) { /* We couldn't recover from error, most likely manual interaction @@ -6362,14 +6538,14 @@ void handle_alter_part_error(ALTER_PARTITION_PARAM_TYPE *lpt, if (drop_partition) { /* Table is still ok, but we left a shadow frm file behind. */ - push_warning_printf(lpt->thd, MYSQL_ERROR::WARN_LEVEL_WARN, 1, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, 1, "%s %s", "Operation was unsuccessful, table is still intact,", "but it is possible that a shadow frm file was left behind"); } else { - push_warning_printf(lpt->thd, MYSQL_ERROR::WARN_LEVEL_WARN, 1, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, 1, "%s %s %s %s", "Operation was unsuccessful, table is still intact,", "but it is possible that a shadow frm file was left behind.", @@ -6385,7 +6561,7 @@ void handle_alter_part_error(ALTER_PARTITION_PARAM_TYPE *lpt, Failed during install of shadow frm file, table isn't intact and dropped partitions are still there */ - push_warning_printf(lpt->thd, MYSQL_ERROR::WARN_LEVEL_WARN, 1, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, 1, "%s %s %s", "Failed during alter of partitions, table is no longer intact.", "The frm file is in an unknown state, and a backup", @@ -6399,7 +6575,7 @@ void handle_alter_part_error(ALTER_PARTITION_PARAM_TYPE *lpt, ask the user to perform the action manually. We remove the log records and ask the user to perform the action manually. */ - push_warning_printf(lpt->thd, MYSQL_ERROR::WARN_LEVEL_WARN, 1, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, 1, "%s %s", "Failed during drop of partitions, table is intact.", "Manual drop of remaining partitions is required"); @@ -6411,7 +6587,7 @@ void handle_alter_part_error(ALTER_PARTITION_PARAM_TYPE *lpt, certainly in a very bad state so we give user warning and disable the table by writing an ancient frm version into it. */ - push_warning_printf(lpt->thd, MYSQL_ERROR::WARN_LEVEL_WARN, 1, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, 1, "%s %s %s", "Failed during renaming of partitions. We are now in a position", "where table is not reusable", @@ -6440,11 +6616,31 @@ void handle_alter_part_error(ALTER_PARTITION_PARAM_TYPE *lpt, even though we reported an error the operation was successfully completed. */ - push_warning_printf(lpt->thd, MYSQL_ERROR::WARN_LEVEL_WARN, 1,"%s %s", + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, 1,"%s %s", "Operation was successfully completed by failure handling,", "after failure of normal operation"); } } + + if (thd->locked_tables_mode) + { + Diagnostics_area *stmt_da= NULL; + Diagnostics_area tmp_stmt_da(true); + + if (thd->is_error()) + { + /* reopen might fail if we have a previous error, use a temporary da. */ + stmt_da= thd->get_stmt_da(); + thd->set_stmt_da(&tmp_stmt_da); + } + + if (thd->locked_tables_list.reopen_tables(thd)) + sql_print_warning("We failed to reacquire LOCKs in ALTER TABLE"); + + if (stmt_da) + thd->set_stmt_da(stmt_da); + } + DBUG_VOID_RETURN; } @@ -6461,7 +6657,7 @@ static void downgrade_mdl_if_lock_tables_mode(THD *thd, MDL_ticket *ticket, enum_mdl_type type) { if (thd->locked_tables_mode) - ticket->downgrade_exclusive_lock(type); + ticket->downgrade_lock(type); } @@ -6470,14 +6666,12 @@ static void downgrade_mdl_if_lock_tables_mode(THD *thd, MDL_ticket *ticket, previously prepared. @param thd Thread object - @param table Original table object + @param table Original table object with new part_info @param alter_info ALTER TABLE info @param create_info Create info for CREATE TABLE @param table_list List of the table involved @param db Database name of new table @param table_name Table name of new table - @param fast_alter_table Prepared table object - @return Operation status @retval TRUE Error @retval FALSE Success @@ -6492,8 +6686,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, HA_CREATE_INFO *create_info, TABLE_LIST *table_list, char *db, - const char *table_name, - TABLE *fast_alter_table) + const char *table_name) { /* Set-up struct used to write frm files */ partition_info *part_info; @@ -6503,10 +6696,10 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, bool close_table_on_failure= FALSE; bool frm_install= FALSE; MDL_ticket *mdl_ticket= table->mdl_ticket; - DBUG_ASSERT(fast_alter_table); DBUG_ENTER("fast_alter_partition_table"); + DBUG_ASSERT(table->m_needs_reopen); - part_info= fast_alter_table->part_info; + part_info= table->part_info; lpt->thd= thd; lpt->table_list= table_list; lpt->part_info= part_info; @@ -6515,8 +6708,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, lpt->db_options= create_info->table_options; if (create_info->row_type == ROW_TYPE_DYNAMIC) lpt->db_options|= HA_OPTION_PACK_RECORD; - lpt->table= fast_alter_table; - lpt->old_table= table; + lpt->table= table; lpt->key_info_buffer= 0; lpt->key_count= 0; lpt->db= db; @@ -6574,7 +6766,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, goto err; } } - else if (alter_info->flags & ALTER_DROP_PARTITION) + else if (alter_info->flags & Alter_info::ALTER_DROP_PARTITION) { /* Now after all checks and setting state on dropped partitions we can @@ -6609,9 +6801,10 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, 3) Write the ddl log to ensure that the operation is completed even in the presence of a MySQL Server crash (the log is executed before any other threads are started, so there are no locking issues). - 4) Close all tables that have already been opened but didn't stumble on + 4) Close the table that have already been opened but didn't stumble on + the abort locked previously. This is done as part of the the abort locked previously. This is done as part of the - alter_close_tables call. + alter_close_table call. 5) Write the bin log Unfortunately the writing of the binlog is not synchronised with other logging activities. So no matter in which order the binlog @@ -6647,7 +6840,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, (action_completed= TRUE, FALSE) || ERROR_INJECT_CRASH("crash_drop_partition_4") || ERROR_INJECT_ERROR("fail_drop_partition_4") || - alter_close_tables(lpt, action_completed) || + alter_close_table(lpt) || (close_table_on_failure= FALSE, FALSE) || ERROR_INJECT_CRASH("crash_drop_partition_5") || ERROR_INJECT_ERROR("fail_drop_partition_5") || @@ -6674,7 +6867,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, goto err; } } - else if ((alter_info->flags & ALTER_ADD_PARTITION) && + else if ((alter_info->flags & Alter_info::ALTER_ADD_PARTITION) && (part_info->part_type == RANGE_PARTITION || part_info->part_type == LIST_PARTITION)) { @@ -6724,7 +6917,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, ERROR_INJECT_CRASH("crash_add_partition_5") || ERROR_INJECT_ERROR("fail_add_partition_5") || (close_table_on_failure= FALSE, FALSE) || - alter_close_tables(lpt, action_completed) || + alter_close_table(lpt) || ERROR_INJECT_CRASH("crash_add_partition_6") || ERROR_INJECT_ERROR("fail_add_partition_6") || ((!thd->lex->no_write_to_binlog) && @@ -6788,24 +6981,24 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, 1) Write the shadow frm file of new partitioning 2) Log such that temporary partitions added in change phase are removed in a crash situation - 3) Add the new partitions - Copy from the reorganised partitions to the new partitions + 3) Add the new partitions. + Copy from the reorganised partitions to the new partitions. 4) Get an exclusive metadata lock on the table (waits for all active transactions using this table). This ensures that we can release all other locks on the table and since no one can open the table, there can be no new threads accessing the table. They will be hanging on this exclusive lock. - 5) Log that operation is completed and log all complete actions - needed to complete operation from here - 6) Write bin log - 7) Close all instances of the table and remove them from the table cache. - 8) Prepare handlers for rename and delete of partitions + 5) Close the table. + 6) Log that operation is completed and log all complete actions + needed to complete operation from here. + 7) Write bin log. + 8) Prepare handlers for rename and delete of partitions. 9) Rename and drop the reorged partitions such that they are no longer used and rename those added to their real new names. - 10) Install the shadow frm file - 11) Reopen the table if under lock tables - 12) Complete query - */ + 10) Install the shadow frm file. + 11) Reopen the table if under lock tables. + 12) Complete query. + */ if (write_log_drop_shadow_frm(lpt) || ERROR_INJECT_CRASH("crash_change_partition_1") || ERROR_INJECT_ERROR("fail_change_partition_1") || @@ -6822,22 +7015,22 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, wait_while_table_is_used(thd, table, HA_EXTRA_NOT_USED) || ERROR_INJECT_CRASH("crash_change_partition_5") || ERROR_INJECT_ERROR("fail_change_partition_5") || - write_log_final_change_partition(lpt) || - (action_completed= TRUE, FALSE) || + alter_close_table(lpt) || + (close_table_on_failure= FALSE, FALSE) || ERROR_INJECT_CRASH("crash_change_partition_6") || ERROR_INJECT_ERROR("fail_change_partition_6") || + write_log_final_change_partition(lpt) || + (action_completed= TRUE, FALSE) || + ERROR_INJECT_CRASH("crash_change_partition_7") || + ERROR_INJECT_ERROR("fail_change_partition_7") || ((!thd->lex->no_write_to_binlog) && (write_bin_log(thd, FALSE, thd->query(), thd->query_length()), FALSE)) || - ERROR_INJECT_CRASH("crash_change_partition_7") || - ERROR_INJECT_ERROR("fail_change_partition_7") || + ERROR_INJECT_CRASH("crash_change_partition_8") || + ERROR_INJECT_ERROR("fail_change_partition_8") || ((frm_install= TRUE), FALSE) || mysql_write_frm(lpt, WFRM_INSTALL_SHADOW) || (frm_install= FALSE, FALSE) || - ERROR_INJECT_CRASH("crash_change_partition_8") || - ERROR_INJECT_ERROR("fail_change_partition_8") || - alter_close_tables(lpt, action_completed) || - (close_table_on_failure= FALSE, FALSE) || ERROR_INJECT_CRASH("crash_change_partition_9") || ERROR_INJECT_ERROR("fail_change_partition_9") || mysql_drop_partitions(lpt) || @@ -6863,22 +7056,6 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, */ DBUG_RETURN(fast_end_partition(thd, lpt->copied, lpt->deleted, table_list)); err: - if (action_completed) - { - /* - Although error occurred, the action was forced to retry for completion. - Therefore we must close+reopen all instances of the table. - */ - (void) alter_partition_lock_handling(lpt); - } - else - { - /* - The failed action was reverted, leave the original table as is and - close/destroy the intermediate table object and its share. - */ - close_temporary(lpt->table, 1, 0); - } downgrade_mdl_if_lock_tables_mode(thd, mdl_ticket, MDL_SHARED_NO_READ_WRITE); DBUG_RETURN(TRUE); } @@ -6976,20 +7153,19 @@ void mem_alloc_error(size_t size) } #ifdef WITH_PARTITION_STORAGE_ENGINE -/* - Return comma-separated list of used partitions in the provided given string +/** + Return comma-separated list of used partitions in the provided given string. - SYNOPSIS - make_used_partitions_str() - part_info IN Partitioning info - parts_str OUT The string to fill + @param part_info Partitioning info + @param[out] parts The resulting list of string to fill - DESCRIPTION - Generate a list of used partitions (from bits in part_info->used_partitions - bitmap), asd store it into the provided String object. + Generate a list of used partitions (from bits in part_info->read_partitions + bitmap), and store it into the provided String object. - NOTE + @note The produced string must not be longer then MAX_PARTITIONS * (1 + FN_LEN). + In case of UPDATE, only the partitions read is given, not the partitions + that was written or locked. */ void make_used_partitions_str(partition_info *part_info, String *parts_str) @@ -7007,7 +7183,7 @@ void make_used_partitions_str(partition_info *part_info, String *parts_str) List_iterator<partition_element> it2(head_pe->subpartitions); while ((pe= it2++)) { - if (bitmap_is_set(&part_info->used_partitions, partition_id)) + if (bitmap_is_set(&part_info->read_partitions, partition_id)) { if (parts_str->length()) parts_str->append(','); @@ -7027,7 +7203,7 @@ void make_used_partitions_str(partition_info *part_info, String *parts_str) { while ((pe= it++)) { - if (bitmap_is_set(&part_info->used_partitions, partition_id)) + if (bitmap_is_set(&part_info->read_partitions, partition_id)) { if (parts_str->length()) parts_str->append(','); diff --git a/sql/sql_partition.h b/sql/sql_partition.h index 2fd9b4c3d75..0850d795443 100644 --- a/sql/sql_partition.h +++ b/sql/sql_partition.h @@ -25,6 +25,7 @@ #include "table.h" /* TABLE_LIST */ class Alter_info; +class Alter_table_ctx; class Field; class String; class handler; @@ -55,7 +56,6 @@ typedef struct st_lock_param_type HA_CREATE_INFO *create_info; Alter_info *alter_info; TABLE *table; - TABLE *old_table; KEY *key_info_buffer; const char *db; const char *table_name; @@ -77,7 +77,7 @@ typedef struct { } part_id_range; struct st_partition_iter; -#define NOT_A_PARTITION_ID ((uint32)-1) +#define NOT_A_PARTITION_ID UINT_MAX32 bool is_partition_in_list(char *part_name, List<char> list_part_names); char *are_partitions_in_table(partition_info *new_part_info, @@ -252,23 +252,23 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, HA_CREATE_INFO *create_info, TABLE_LIST *table_list, char *db, - const char *table_name, - TABLE *fast_alter_table); + const char *table_name); bool set_part_state(Alter_info *alter_info, partition_info *tab_part_info, enum partition_state part_state); uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info, HA_CREATE_INFO *create_info, - handlerton *old_db_type, + Alter_table_ctx *alter_ctx, bool *partition_changed, - char *db, - const char *table_name, - const char *path, - TABLE **fast_alter_table); + bool *fast_alter_table); char *generate_partition_syntax(partition_info *part_info, uint *buf_length, bool use_sql_alloc, bool show_partition_options, HA_CREATE_INFO *create_info, Alter_info *alter_info); +bool verify_data_with_partition(TABLE *table, TABLE *part_table, + uint32 part_id); +bool compare_partition_options(HA_CREATE_INFO *table_create_info, + partition_element *part_elem); bool partition_key_modified(TABLE *table, const MY_BITMAP *fields); #else #define partition_key_modified(X,Y) 0 diff --git a/sql/sql_partition_admin.cc b/sql/sql_partition_admin.cc index 4edd47de855..d9128fab8ab 100644 --- a/sql/sql_partition_admin.cc +++ b/sql/sql_partition_admin.cc @@ -14,8 +14,15 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "sql_parse.h" // check_one_table_access + // check_merge_table_access + // check_one_table_access #include "sql_table.h" // mysql_alter_table, etc. -#include "sql_lex.h" // Sql_statement +#include "sql_cmd.h" // Sql_cmd +#include "sql_alter.h" // Sql_cmd_alter_table +#include "sql_partition.h" // struct partition_info, etc. +#include "debug_sync.h" // DEBUG_SYNC +#include "sql_truncate.h" // mysql_truncate_table, + // Sql_cmd_truncate_table #include "sql_admin.h" // Analyze/Check/.._table_statement #include "sql_partition_admin.h" // Alter_table_*_partition #ifdef WITH_PARTITION_STORAGE_ENGINE @@ -36,41 +43,665 @@ bool Partition_statement_unsupported::execute(THD *) #else -bool Alter_table_analyze_partition_statement::execute(THD *thd) +bool Sql_cmd_alter_table_exchange_partition::execute(THD *thd) +{ + /* Moved from mysql_execute_command */ + LEX *lex= thd->lex; + /* first SELECT_LEX (have special meaning for many of non-SELECTcommands) */ + SELECT_LEX *select_lex= &lex->select_lex; + /* first table of first SELECT_LEX */ + TABLE_LIST *first_table= (TABLE_LIST*) select_lex->table_list.first; + /* + Code in mysql_alter_table() may modify its HA_CREATE_INFO argument, + so we have to use a copy of this structure to make execution + prepared statement- safe. A shallow copy is enough as no memory + referenced from this structure will be modified. + @todo move these into constructor... + */ + HA_CREATE_INFO create_info(lex->create_info); + Alter_info alter_info(lex->alter_info, thd->mem_root); + ulong priv_needed= ALTER_ACL | DROP_ACL | INSERT_ACL | CREATE_ACL; + + DBUG_ENTER("Sql_cmd_alter_table_exchange_partition::execute"); + + if (thd->is_fatal_error) /* out of memory creating a copy of alter_info */ + DBUG_RETURN(TRUE); + + /* Must be set in the parser */ + DBUG_ASSERT(select_lex->db); + /* also check the table to be exchanged with the partition */ + DBUG_ASSERT(alter_info.flags & Alter_info::ALTER_EXCHANGE_PARTITION); + + if (check_access(thd, priv_needed, first_table->db, + &first_table->grant.privilege, + &first_table->grant.m_internal, + 0, 0) || + check_access(thd, priv_needed, first_table->next_local->db, + &first_table->next_local->grant.privilege, + &first_table->next_local->grant.m_internal, + 0, 0)) + DBUG_RETURN(TRUE); + + if (check_grant(thd, priv_needed, first_table, FALSE, UINT_MAX, FALSE)) + DBUG_RETURN(TRUE); + + /* Not allowed with EXCHANGE PARTITION */ + DBUG_ASSERT(!create_info.data_file_name && !create_info.index_file_name); + + thd->enable_slow_log= opt_log_slow_admin_statements; + DBUG_RETURN(exchange_partition(thd, first_table, &alter_info)); +} + + +/** + @brief Checks that the tables will be able to be used for EXCHANGE PARTITION. + @param table Non partitioned table. + @param part_table Partitioned table. + + @retval FALSE if OK, otherwise error is reported and TRUE is returned. +*/ +static bool check_exchange_partition(TABLE *table, TABLE *part_table) +{ + DBUG_ENTER("check_exchange_partition"); + + /* Both tables must exist */ + if (!part_table || !table) + { + my_error(ER_CHECK_NO_SUCH_TABLE, MYF(0)); + DBUG_RETURN(TRUE); + } + + /* The first table must be partitioned, and the second must not */ + if (!part_table->part_info) + { + my_error(ER_PARTITION_MGMT_ON_NONPARTITIONED, MYF(0)); + DBUG_RETURN(TRUE); + } + if (table->part_info) + { + my_error(ER_PARTITION_EXCHANGE_PART_TABLE, MYF(0), + table->s->table_name.str); + DBUG_RETURN(TRUE); + } + + if (part_table->file->ht != partition_hton) + { + /* + Only allowed on partitioned tables throught the generic ha_partition + handler, i.e not yet for native partitioning (NDB). + */ + my_error(ER_PARTITION_MGMT_ON_NONPARTITIONED, MYF(0)); + DBUG_RETURN(TRUE); + } + + if (table->file->ht != part_table->part_info->default_engine_type) + { + my_error(ER_MIX_HANDLER_ERROR, MYF(0)); + DBUG_RETURN(TRUE); + } + + /* Verify that table is not tmp table, partitioned tables cannot be tmp. */ + if (table->s->tmp_table != NO_TMP_TABLE) + { + my_error(ER_PARTITION_EXCHANGE_TEMP_TABLE, MYF(0), + table->s->table_name.str); + DBUG_RETURN(TRUE); + } + + /* The table cannot have foreign keys constraints or be referenced */ + if(!table->file->can_switch_engines()) + { + my_error(ER_PARTITION_EXCHANGE_FOREIGN_KEY, MYF(0), + table->s->table_name.str); + DBUG_RETURN(TRUE); + } + DBUG_RETURN(FALSE); +} + + +/** + @brief Compare table structure/options between a non partitioned table + and a specific partition of a partitioned table. + + @param thd Thread object. + @param table Non partitioned table. + @param part_table Partitioned table. + @param part_elem Partition element to use for partition specific compare. +*/ +static bool compare_table_with_partition(THD *thd, TABLE *table, + TABLE *part_table, + partition_element *part_elem) +{ + HA_CREATE_INFO table_create_info, part_create_info; + Alter_info part_alter_info; + Alter_table_ctx part_alter_ctx; // Not used + DBUG_ENTER("compare_table_with_partition"); + + bool metadata_equal= false; + memset(&part_create_info, 0, sizeof(HA_CREATE_INFO)); + memset(&table_create_info, 0, sizeof(HA_CREATE_INFO)); + + update_create_info_from_table(&table_create_info, table); + /* get the current auto_increment value */ + table->file->update_create_info(&table_create_info); + /* mark all columns used, since they are used when preparing the new table */ + part_table->use_all_columns(); + table->use_all_columns(); + if (mysql_prepare_alter_table(thd, part_table, &part_create_info, + &part_alter_info, &part_alter_ctx)) + { + my_error(ER_TABLES_DIFFERENT_METADATA, MYF(0)); + DBUG_RETURN(TRUE); + } + /* db_type is not set in prepare_alter_table */ + part_create_info.db_type= part_table->part_info->default_engine_type; + /* + Since we exchange the partition with the table, allow exchanging + auto_increment value as well. + */ + part_create_info.auto_increment_value= + table_create_info.auto_increment_value; + + /* Check compatible row_types and set create_info accordingly. */ + { + enum row_type part_row_type= part_table->file->get_row_type(); + enum row_type table_row_type= table->file->get_row_type(); + if (part_row_type != table_row_type) + { + my_error(ER_PARTITION_EXCHANGE_DIFFERENT_OPTION, MYF(0), + "ROW_FORMAT"); + DBUG_RETURN(true); + } + part_create_info.row_type= table->s->row_type; + } + + /* + NOTE: ha_blackhole does not support check_if_compatible_data, + so this always fail for blackhole tables. + ha_myisam compares pointers to verify that DATA/INDEX DIRECTORY is + the same, so any table using data/index_file_name will fail. + */ + if (mysql_compare_tables(table, &part_alter_info, &part_create_info, + &metadata_equal)) + { + my_error(ER_TABLES_DIFFERENT_METADATA, MYF(0)); + DBUG_RETURN(TRUE); + } + + DEBUG_SYNC(thd, "swap_partition_after_compare_tables"); + if (!metadata_equal) + { + my_error(ER_TABLES_DIFFERENT_METADATA, MYF(0)); + DBUG_RETURN(TRUE); + } + DBUG_ASSERT(table->s->db_create_options == + part_table->s->db_create_options); + DBUG_ASSERT(table->s->db_options_in_use == + part_table->s->db_options_in_use); + + if (table_create_info.avg_row_length != part_create_info.avg_row_length) + { + my_error(ER_PARTITION_EXCHANGE_DIFFERENT_OPTION, MYF(0), + "AVG_ROW_LENGTH"); + DBUG_RETURN(TRUE); + } + + if (table_create_info.table_options != part_create_info.table_options) + { + my_error(ER_PARTITION_EXCHANGE_DIFFERENT_OPTION, MYF(0), + "TABLE OPTION"); + DBUG_RETURN(TRUE); + } + + if (table->s->table_charset != part_table->s->table_charset) + { + my_error(ER_PARTITION_EXCHANGE_DIFFERENT_OPTION, MYF(0), + "CHARACTER SET"); + DBUG_RETURN(TRUE); + } + + /* + NOTE: We do not support update of frm-file, i.e. change + max/min_rows, data/index_file_name etc. + The workaround is to use REORGANIZE PARTITION to rewrite + the frm file and then use EXCHANGE PARTITION when they are the same. + */ + if (compare_partition_options(&table_create_info, part_elem)) + DBUG_RETURN(TRUE); + + DBUG_RETURN(FALSE); +} + + +/** + @brief Exchange partition/table with ddl log. + + @details How to handle a crash in the middle of the rename (break on error): + 1) register in ddl_log that we are going to exchange swap_table with part. + 2) do the first rename (swap_table -> tmp-name) and sync the ddl_log. + 3) do the second rename (part -> swap_table) and sync the ddl_log. + 4) do the last rename (tmp-name -> part). + 5) mark the entry done. + + Recover by: + 5) is done, All completed. Nothing to recover. + 4) is done see 3). (No mark or sync in the ddl_log...) + 3) is done -> try rename part -> tmp-name (ignore failure) goto 2). + 2) is done -> try rename swap_table -> part (ignore failure) goto 1). + 1) is done -> try rename tmp-name -> swap_table (ignore failure). + before 1) Nothing to recover... + + @param thd Thread handle + @param name name of table/partition 1 (to be exchanged with 2) + @param from_name name of table/partition 2 (to be exchanged with 1) + @param tmp_name temporary name to use while exchaning + @param ht handlerton of the table/partitions + + @return Operation status + @retval TRUE Error + @retval FALSE Success + + @note ha_heap always succeeds in rename (since it is created upon usage). + This is OK when to recover from a crash since all heap are empty and the + recover is done early in the startup of the server (right before + read_init_file which can populate the tables). + + And if no crash we can trust the syncs in the ddl_log. + + What about if the rename is put into a background thread? That will cause + corruption and is avoided by the exlusive metadata lock. +*/ +static bool exchange_name_with_ddl_log(THD *thd, + const char *name, + const char *from_name, + const char *tmp_name, + handlerton *ht) +{ + DDL_LOG_ENTRY exchange_entry; + DDL_LOG_MEMORY_ENTRY *log_entry= NULL; + DDL_LOG_MEMORY_ENTRY *exec_log_entry= NULL; + bool error= TRUE; + bool error_set= FALSE; + handler *file= NULL; + DBUG_ENTER("exchange_name_with_ddl_log"); + + if (!(file= get_new_handler(NULL, thd->mem_root, ht))) + { + mem_alloc_error(sizeof(handler)); + DBUG_RETURN(TRUE); + } + + /* prepare the action entry */ + exchange_entry.entry_type= DDL_LOG_ENTRY_CODE; + exchange_entry.action_type= DDL_LOG_EXCHANGE_ACTION; + exchange_entry.next_entry= 0; + exchange_entry.name= name; + exchange_entry.from_name= from_name; + exchange_entry.tmp_name= tmp_name; + exchange_entry.handler_name= ha_resolve_storage_engine_name(ht); + exchange_entry.phase= EXCH_PHASE_NAME_TO_TEMP; + + mysql_mutex_lock(&LOCK_gdl); + /* + write to the ddl log what to do by: + 1) write the action entry (i.e. which names to be exchanged) + 2) write the execution entry with a link to the action entry + */ + DBUG_EXECUTE_IF("exchange_partition_fail_1", goto err_no_action_written;); + DBUG_EXECUTE_IF("exchange_partition_abort_1", DBUG_SUICIDE();); + if (write_ddl_log_entry(&exchange_entry, &log_entry)) + goto err_no_action_written; + + DBUG_EXECUTE_IF("exchange_partition_fail_2", goto err_no_execute_written;); + DBUG_EXECUTE_IF("exchange_partition_abort_2", DBUG_SUICIDE();); + if (write_execute_ddl_log_entry(log_entry->entry_pos, FALSE, &exec_log_entry)) + goto err_no_execute_written; + /* ddl_log is written and synced */ + + mysql_mutex_unlock(&LOCK_gdl); + /* + Execute the name exchange. + Do one rename, increase the phase, update the action entry and sync. + In case of errors in the ddl_log we must fail and let the ddl_log try + to revert the changes, since otherwise it could revert the command after + we sent OK to the client. + */ + /* call rename table from table to tmp-name */ + DBUG_EXECUTE_IF("exchange_partition_fail_3", + my_error(ER_ERROR_ON_RENAME, MYF(0), + name, tmp_name, 0, "n/a"); + error_set= TRUE; + goto err_rename;); + DBUG_EXECUTE_IF("exchange_partition_abort_3", DBUG_SUICIDE();); + if (file->ha_rename_table(name, tmp_name)) + { + char errbuf[MYSYS_STRERROR_SIZE]; + my_strerror(errbuf, sizeof(errbuf), my_errno); + my_error(ER_ERROR_ON_RENAME, MYF(0), name, tmp_name, + my_errno, errbuf); + error_set= TRUE; + goto err_rename; + } + DBUG_EXECUTE_IF("exchange_partition_fail_4", goto err_rename;); + DBUG_EXECUTE_IF("exchange_partition_abort_4", DBUG_SUICIDE();); + if (deactivate_ddl_log_entry(log_entry->entry_pos)) + goto err_rename; + + /* call rename table from partition to table */ + DBUG_EXECUTE_IF("exchange_partition_fail_5", + my_error(ER_ERROR_ON_RENAME, MYF(0), + from_name, name, 0, "n/a"); + error_set= TRUE; + goto err_rename;); + DBUG_EXECUTE_IF("exchange_partition_abort_5", DBUG_SUICIDE();); + if (file->ha_rename_table(from_name, name)) + { + char errbuf[MYSYS_STRERROR_SIZE]; + my_strerror(errbuf, sizeof(errbuf), my_errno); + my_error(ER_ERROR_ON_RENAME, MYF(0), from_name, name, + my_errno, errbuf); + error_set= TRUE; + goto err_rename; + } + DBUG_EXECUTE_IF("exchange_partition_fail_6", goto err_rename;); + DBUG_EXECUTE_IF("exchange_partition_abort_6", DBUG_SUICIDE();); + if (deactivate_ddl_log_entry(log_entry->entry_pos)) + goto err_rename; + + /* call rename table from tmp-nam to partition */ + DBUG_EXECUTE_IF("exchange_partition_fail_7", + my_error(ER_ERROR_ON_RENAME, MYF(0), + tmp_name, from_name, 0, "n/a"); + error_set= TRUE; + goto err_rename;); + DBUG_EXECUTE_IF("exchange_partition_abort_7", DBUG_SUICIDE();); + if (file->ha_rename_table(tmp_name, from_name)) + { + char errbuf[MYSYS_STRERROR_SIZE]; + my_strerror(errbuf, sizeof(errbuf), my_errno); + my_error(ER_ERROR_ON_RENAME, MYF(0), tmp_name, from_name, + my_errno, errbuf); + error_set= TRUE; + goto err_rename; + } + DBUG_EXECUTE_IF("exchange_partition_fail_8", goto err_rename;); + DBUG_EXECUTE_IF("exchange_partition_abort_8", DBUG_SUICIDE();); + if (deactivate_ddl_log_entry(log_entry->entry_pos)) + goto err_rename; + + /* The exchange is complete and ddl_log is deactivated */ + DBUG_EXECUTE_IF("exchange_partition_fail_9", goto err_rename;); + DBUG_EXECUTE_IF("exchange_partition_abort_9", DBUG_SUICIDE();); + /* all OK */ + error= FALSE; + delete file; + DBUG_RETURN(error); +err_rename: + /* + Nothing to do if any of these commands fails :( the commands itselfs + will log to the error log about the failures... + */ + /* execute the ddl log entry to revert the renames */ + (void) execute_ddl_log_entry(current_thd, log_entry->entry_pos); + mysql_mutex_lock(&LOCK_gdl); + /* mark the execute log entry done */ + (void) write_execute_ddl_log_entry(0, TRUE, &exec_log_entry); + /* release the execute log entry */ + (void) release_ddl_log_memory_entry(exec_log_entry); +err_no_execute_written: + /* release the action log entry */ + (void) release_ddl_log_memory_entry(log_entry); +err_no_action_written: + mysql_mutex_unlock(&LOCK_gdl); + delete file; + if (!error_set) + my_error(ER_DDL_LOG_ERROR, MYF(0)); + DBUG_RETURN(error); +} + + +/** + @brief Swap places between a partition and a table. + + @details Verify that the tables are compatible (same engine, definition etc), + verify that all rows in the table will fit in the partition, + if all OK, rename table to tmp name, rename partition to table + and finally rename tmp name to partition. + + 1) Take upgradable mdl, open tables and then lock them (inited in parse) + 2) Verify that metadata matches + 3) verify data + 4) Upgrade to exclusive mdl for both tables + 5) Rename table <-> partition + 6) Rely on close_thread_tables to release mdl and table locks + + @param thd Thread handle + @param table_list Table where the partition exists as first table, + Table to swap with the partition as second table + @param alter_info Contains partition name to swap + + @note This is a DDL operation so triggers will not be used. +*/ +bool Sql_cmd_alter_table_exchange_partition:: + exchange_partition(THD *thd, TABLE_LIST *table_list, Alter_info *alter_info) +{ + TABLE *part_table, *swap_table; + TABLE_LIST *swap_table_list; + handlerton *table_hton; + partition_element *part_elem; + char *partition_name; + char temp_name[FN_REFLEN+1]; + char part_file_name[FN_REFLEN+1]; + char swap_file_name[FN_REFLEN+1]; + char temp_file_name[FN_REFLEN+1]; + uint swap_part_id; + uint part_file_name_len; + Alter_table_prelocking_strategy alter_prelocking_strategy; + MDL_ticket *swap_table_mdl_ticket= NULL; + MDL_ticket *part_table_mdl_ticket= NULL; + uint table_counter; + bool error= TRUE; + DBUG_ENTER("mysql_exchange_partition"); + DBUG_ASSERT(alter_info->flags & Alter_info::ALTER_EXCHANGE_PARTITION); + + /* Don't allow to exchange with log table */ + swap_table_list= table_list->next_local; + if (check_if_log_table(swap_table_list->db_length, swap_table_list->db, + swap_table_list->table_name_length, + swap_table_list->table_name, 0)) + { + my_error(ER_WRONG_USAGE, MYF(0), "PARTITION", "log table"); + DBUG_RETURN(TRUE); + } + + /* + Currently no MDL lock that allows both read and write and is upgradeable + to exclusive, so leave the lock type to TL_WRITE_ALLOW_READ also on the + partitioned table. + + TODO: add MDL lock that allows both read and write and is upgradable to + exclusive lock. This would allow to continue using the partitioned table + also with update/insert/delete while the verification of the swap table + is running. + */ + + /* + NOTE: It is not possible to exchange a crashed partition/table since + we need some info from the engine, which we can only access after open, + to be able to verify the structure/metadata. + */ + table_list->mdl_request.set_type(MDL_SHARED_NO_WRITE); + if (open_tables(thd, &table_list, &table_counter, 0, + &alter_prelocking_strategy)) + DBUG_RETURN(true); + + part_table= table_list->table; + swap_table= swap_table_list->table; + + if (check_exchange_partition(swap_table, part_table)) + DBUG_RETURN(TRUE); + + /* set lock pruning on first table */ + partition_name= alter_info->partition_names.head(); + if (table_list->table->part_info-> + set_named_partition_bitmap(partition_name, strlen(partition_name))) + DBUG_RETURN(true); + + if (lock_tables(thd, table_list, table_counter, 0)) + DBUG_RETURN(true); + + + table_hton= swap_table->file->ht; + + THD_STAGE_INFO(thd, stage_verifying_table); + + /* Will append the partition name later in part_info->get_part_elem() */ + part_file_name_len= build_table_filename(part_file_name, + sizeof(part_file_name), + table_list->db, + table_list->table_name, + "", 0); + build_table_filename(swap_file_name, + sizeof(swap_file_name), + swap_table_list->db, + swap_table_list->table_name, + "", 0); + /* create a unique temp name #sqlx-nnnn_nnnn, x for eXchange */ + my_snprintf(temp_name, sizeof(temp_name), "%sx-%lx_%lx", + tmp_file_prefix, current_pid, thd->thread_id); + if (lower_case_table_names) + my_casedn_str(files_charset_info, temp_name); + build_table_filename(temp_file_name, sizeof(temp_file_name), + table_list->next_local->db, + temp_name, "", FN_IS_TMP); + + if (!(part_elem= part_table->part_info->get_part_elem(partition_name, + part_file_name + + part_file_name_len, + &swap_part_id))) + { + // my_error(ER_UNKNOWN_PARTITION, MYF(0), partition_name, + // part_table->alias); + DBUG_RETURN(TRUE); + } + + if (swap_part_id == NOT_A_PARTITION_ID) + { + DBUG_ASSERT(part_table->part_info->is_sub_partitioned()); + my_error(ER_PARTITION_INSTEAD_OF_SUBPARTITION, MYF(0)); + DBUG_RETURN(TRUE); + } + + if (compare_table_with_partition(thd, swap_table, part_table, part_elem)) + DBUG_RETURN(TRUE); + + /* Table and partition has same structure/options, OK to exchange */ + + thd_proc_info(thd, "verifying data with partition"); + + if (verify_data_with_partition(swap_table, part_table, swap_part_id)) + DBUG_RETURN(TRUE); + + /* + Get exclusive mdl lock on both tables, alway the non partitioned table + first. Remember the tickets for downgrading locks later. + */ + swap_table_mdl_ticket= swap_table->mdl_ticket; + part_table_mdl_ticket= part_table->mdl_ticket; + + /* + No need to set used_partitions to only propagate + HA_EXTRA_PREPARE_FOR_RENAME to one part since no built in engine uses + that flag. And the action would probably be to force close all other + instances which is what we are doing any way. + */ + if (wait_while_table_is_used(thd, swap_table, HA_EXTRA_PREPARE_FOR_RENAME) || + wait_while_table_is_used(thd, part_table, HA_EXTRA_PREPARE_FOR_RENAME)) + goto err; + + DEBUG_SYNC(thd, "swap_partition_after_wait"); + + close_all_tables_for_name(thd, swap_table->s, HA_EXTRA_NOT_USED, NULL); + close_all_tables_for_name(thd, part_table->s, HA_EXTRA_NOT_USED, NULL); + + DEBUG_SYNC(thd, "swap_partition_before_rename"); + + if (exchange_name_with_ddl_log(thd, swap_file_name, part_file_name, + temp_file_name, table_hton)) + goto err; + + /* + Reopen tables under LOCK TABLES. Ignore the return value for now. It's + better to keep master/slave in consistent state. Alternative would be to + try to revert the exchange operation and issue error. + */ + (void) thd->locked_tables_list.reopen_tables(thd); + + if ((error= write_bin_log(thd, TRUE, thd->query(), thd->query_length()))) + { + /* + The error is reported in write_bin_log(). + We try to revert to make it easier to keep the master/slave in sync. + */ + (void) exchange_name_with_ddl_log(thd, part_file_name, swap_file_name, + temp_file_name, table_hton); + } + +err: + if (thd->locked_tables_mode) + { + if (swap_table_mdl_ticket) + swap_table_mdl_ticket->downgrade_lock(MDL_SHARED_NO_READ_WRITE); + if (part_table_mdl_ticket) + part_table_mdl_ticket->downgrade_lock(MDL_SHARED_NO_READ_WRITE); + } + + if (!error) + my_ok(thd); + + // For query cache + table_list->table= NULL; + table_list->next_local->table= NULL; + query_cache_invalidate3(thd, table_list, FALSE); + + DBUG_RETURN(error); +} + +bool Sql_cmd_alter_table_analyze_partition::execute(THD *thd) { bool res; - DBUG_ENTER("Alter_table_analyze_partition_statement::execute"); + DBUG_ENTER("Sql_cmd_alter_table_analyze_partition::execute"); /* Flag that it is an ALTER command which administrates partitions, used by ha_partition */ - m_lex->alter_info.flags|= ALTER_ADMIN_PARTITION; - - res= Analyze_table_statement::execute(thd); + thd->lex->alter_info.flags|= Alter_info::ALTER_ADMIN_PARTITION; + res= Sql_cmd_analyze_table::execute(thd); + DBUG_RETURN(res); } -bool Alter_table_check_partition_statement::execute(THD *thd) +bool Sql_cmd_alter_table_check_partition::execute(THD *thd) { bool res; - DBUG_ENTER("Alter_table_check_partition_statement::execute"); + DBUG_ENTER("Sql_cmd_alter_table_check_partition::execute"); /* Flag that it is an ALTER command which administrates partitions, used by ha_partition */ - m_lex->alter_info.flags|= ALTER_ADMIN_PARTITION; + thd->lex->alter_info.flags|= Alter_info::ALTER_ADMIN_PARTITION; - res= Check_table_statement::execute(thd); + res= Sql_cmd_check_table::execute(thd); DBUG_RETURN(res); } -bool Alter_table_optimize_partition_statement::execute(THD *thd) +bool Sql_cmd_alter_table_optimize_partition::execute(THD *thd) { bool res; DBUG_ENTER("Alter_table_optimize_partition_statement::execute"); @@ -79,46 +710,49 @@ bool Alter_table_optimize_partition_statement::execute(THD *thd) Flag that it is an ALTER command which administrates partitions, used by ha_partition */ - m_lex->alter_info.flags|= ALTER_ADMIN_PARTITION; + thd->lex->alter_info.flags|= Alter_info::ALTER_ADMIN_PARTITION; - res= Optimize_table_statement::execute(thd); + res= Sql_cmd_optimize_table::execute(thd); DBUG_RETURN(res); } -bool Alter_table_repair_partition_statement::execute(THD *thd) +bool Sql_cmd_alter_table_repair_partition::execute(THD *thd) { bool res; - DBUG_ENTER("Alter_table_repair_partition_statement::execute"); + DBUG_ENTER("Sql_cmd_alter_table_repair_partition::execute"); /* Flag that it is an ALTER command which administrates partitions, used by ha_partition */ - m_lex->alter_info.flags|= ALTER_ADMIN_PARTITION; + thd->lex->alter_info.flags|= Alter_info::ALTER_ADMIN_PARTITION; - res= Repair_table_statement::execute(thd); + res= Sql_cmd_repair_table::execute(thd); DBUG_RETURN(res); } -bool Alter_table_truncate_partition_statement::execute(THD *thd) +bool Sql_cmd_alter_table_truncate_partition::execute(THD *thd) { int error; ha_partition *partition; ulong timeout= thd->variables.lock_wait_timeout; TABLE_LIST *first_table= thd->lex->select_lex.table_list.first; + Alter_info *alter_info= &thd->lex->alter_info; + uint table_counter, i; + List<String> partition_names_list; bool binlog_stmt; - DBUG_ENTER("Alter_table_truncate_partition_statement::execute"); + DBUG_ENTER("Sql_cmd_alter_table_truncate_partition::execute"); /* Flag that it is an ALTER command which administrates partitions, used by ha_partition. */ - m_lex->alter_info.flags|= ALTER_ADMIN_PARTITION | - ALTER_TRUNCATE_PARTITION; + thd->lex->alter_info.flags|= Alter_info::ALTER_ADMIN_PARTITION | + Alter_info::ALTER_TRUNCATE_PARTITION; /* Fix the lock types (not the same as ordinary ALTER TABLE). */ first_table->lock_type= TL_WRITE; @@ -134,8 +768,8 @@ bool Alter_table_truncate_partition_statement::execute(THD *thd) if (check_one_table_access(thd, DROP_ACL, first_table)) DBUG_RETURN(TRUE); - if (open_and_lock_tables(thd, first_table, FALSE, 0)) - DBUG_RETURN(TRUE); + if (open_tables(thd, &first_table, &table_counter, 0)) + DBUG_RETURN(true); /* TODO: Add support for TRUNCATE PARTITION for NDB and other @@ -147,24 +781,45 @@ bool Alter_table_truncate_partition_statement::execute(THD *thd) DBUG_RETURN(TRUE); } + + /* + Prune all, but named partitions, + to avoid excessive calls to external_lock(). + */ + List_iterator<char> partition_names_it(alter_info->partition_names); + uint num_names= alter_info->partition_names.elements; + for (i= 0; i < num_names; i++) + { + char *partition_name= partition_names_it++; + String *str_partition_name= new (thd->mem_root) + String(partition_name, system_charset_info); + if (!str_partition_name) + DBUG_RETURN(true); + partition_names_list.push_back(str_partition_name); + } + first_table->partition_names= &partition_names_list; + if (first_table->table->part_info->set_partition_bitmaps(first_table)) + DBUG_RETURN(true); + + if (lock_tables(thd, first_table, table_counter, 0)) + DBUG_RETURN(true); + /* Under locked table modes this might still not be an exclusive lock. Hence, upgrade the lock since the handler truncate method mandates an exclusive metadata lock. */ MDL_ticket *ticket= first_table->table->mdl_ticket; - if (thd->mdl_context.upgrade_shared_lock_to_exclusive(ticket, timeout)) + if (thd->mdl_context.upgrade_shared_lock(ticket, MDL_EXCLUSIVE, timeout)) DBUG_RETURN(TRUE); tdc_remove_table(thd, TDC_RT_REMOVE_NOT_OWN, first_table->db, first_table->table_name, FALSE); - partition= (ha_partition *) first_table->table->file; - + partition= (ha_partition*) first_table->table->file; /* Invoke the handler method responsible for truncating the partition. */ - if ((error= partition->truncate_partition(&thd->lex->alter_info, - &binlog_stmt))) - first_table->table->file->print_error(error, MYF(0)); + if ((error= partition->truncate_partition(alter_info, &binlog_stmt))) + partition->print_error(error, MYF(0)); /* All effects of a truncate operation are committed even if the @@ -182,11 +837,15 @@ bool Alter_table_truncate_partition_statement::execute(THD *thd) to a shared one. */ if (thd->locked_tables_mode) - ticket->downgrade_exclusive_lock(MDL_SHARED_NO_READ_WRITE); + ticket->downgrade_lock(MDL_SHARED_NO_READ_WRITE); if (! error) my_ok(thd); + // Invalidate query cache + DBUG_ASSERT(!first_table->next_local); + query_cache_invalidate3(thd, first_table, FALSE); + DBUG_RETURN(error); } diff --git a/sql/sql_partition_admin.h b/sql/sql_partition_admin.h index 479371c3b4d..9c53744d9bc 100644 --- a/sql/sql_partition_admin.h +++ b/sql/sql_partition_admin.h @@ -22,214 +22,247 @@ Stub class that returns a error if the partition storage engine is not supported. */ -class Partition_statement_unsupported : public Sql_statement +class Sql_cmd_partition_unsupported : public Sql_cmd { public: - Partition_statement_unsupported(LEX *lex) - : Sql_statement(lex) + Sql_cmd_partition_unsupported() {} - ~Partition_statement_unsupported() + ~Sql_cmd_partition_unsupported() {} + /* Override SQLCOM_*, since it is an ALTER command */ + virtual enum_sql_command sql_command_code() const + { + return SQLCOM_ALTER_TABLE; + } + bool execute(THD *thd); }; -class Alter_table_analyze_partition_statement : - public Partition_statement_unsupported +class Sql_cmd_alter_table_exchange_partition : + public Sql_cmd_partition_unsupported { public: - Alter_table_analyze_partition_statement(LEX *lex) - : Partition_statement_unsupported(lex) + Sql_cmd_alter_table_exchange_partition() {} - ~Alter_table_analyze_partition_statement() + ~Sql_cmd_alter_table_exchange_partition() {} }; -class Alter_table_check_partition_statement : - public Partition_statement_unsupported +class Sql_cmd_alter_table_analyze_partition : + public Sql_cmd_partition_unsupported { public: - Alter_table_check_partition_statement(LEX *lex) - : Partition_statement_unsupported(lex) + Sql_cmd_alter_table_analyze_partition() {} - ~Alter_table_check_partition_statement() + ~Sql_cmd_alter_table_analyze_partition() {} }; -class Alter_table_optimize_partition_statement : - public Partition_statement_unsupported +class Sql_cmd_alter_table_check_partition : + public Sql_cmd_partition_unsupported { public: - Alter_table_optimize_partition_statement(LEX *lex) - : Partition_statement_unsupported(lex) + Sql_cmd_alter_table_check_partition() {} - ~Alter_table_optimize_partition_statement() + ~Sql_cmd_alter_table_check_partition() {} }; -class Alter_table_repair_partition_statement : - public Partition_statement_unsupported +class Sql_cmd_alter_table_optimize_partition : + public Sql_cmd_partition_unsupported { public: - Alter_table_repair_partition_statement(LEX *lex) - : Partition_statement_unsupported(lex) + Sql_cmd_alter_table_optimize_partition() {} - ~Alter_table_repair_partition_statement() + ~Sql_cmd_alter_table_optimize_partition() {} }; -class Alter_table_truncate_partition_statement : - public Partition_statement_unsupported +class Sql_cmd_alter_table_repair_partition : + public Sql_cmd_partition_unsupported { public: - Alter_table_truncate_partition_statement(LEX *lex) - : Partition_statement_unsupported(lex) + Sql_cmd_alter_table_repair_partition() {} - ~Alter_table_truncate_partition_statement() + ~Sql_cmd_alter_table_repair_partition() {} }; +class Sql_cmd_alter_table_truncate_partition : + public Sql_cmd_partition_unsupported +{ +public: + Sql_cmd_alter_table_truncate_partition() + {} + + ~Sql_cmd_alter_table_truncate_partition() + {} +}; + #else /** Class that represents the ALTER TABLE t1 ANALYZE PARTITION p statement. */ -class Alter_table_analyze_partition_statement : public Analyze_table_statement +class Sql_cmd_alter_table_exchange_partition : public Sql_cmd_common_alter_table { public: /** - Constructor, used to represent a ALTER TABLE ANALYZE PARTITION statement. - @param lex the LEX structure for this statement. + Constructor, used to represent a ALTER TABLE EXCHANGE PARTITION statement. */ - Alter_table_analyze_partition_statement(LEX *lex) - : Analyze_table_statement(lex) + Sql_cmd_alter_table_exchange_partition() + : Sql_cmd_common_alter_table() {} - ~Alter_table_analyze_partition_statement() + ~Sql_cmd_alter_table_exchange_partition() {} + bool execute(THD *thd); + +private: + bool exchange_partition(THD *thd, TABLE_LIST *, Alter_info *); +}; + + +/** + Class that represents the ALTER TABLE t1 ANALYZE PARTITION p statement. +*/ +class Sql_cmd_alter_table_analyze_partition : public Sql_cmd_analyze_table +{ +public: /** - Execute a ALTER TABLE ANALYZE PARTITION statement at runtime. - @param thd the current thread. - @return false on success. + Constructor, used to represent a ALTER TABLE ANALYZE PARTITION statement. */ + Sql_cmd_alter_table_analyze_partition() + : Sql_cmd_analyze_table() + {} + + ~Sql_cmd_alter_table_analyze_partition() + {} + bool execute(THD *thd); + + /* Override SQLCOM_ANALYZE, since it is an ALTER command */ + virtual enum_sql_command sql_command_code() const + { + return SQLCOM_ALTER_TABLE; + } }; /** Class that represents the ALTER TABLE t1 CHECK PARTITION p statement. */ -class Alter_table_check_partition_statement : public Check_table_statement +class Sql_cmd_alter_table_check_partition : public Sql_cmd_check_table { public: /** Constructor, used to represent a ALTER TABLE CHECK PARTITION statement. - @param lex the LEX structure for this statement. */ - Alter_table_check_partition_statement(LEX *lex) - : Check_table_statement(lex) + Sql_cmd_alter_table_check_partition() + : Sql_cmd_check_table() {} - ~Alter_table_check_partition_statement() + ~Sql_cmd_alter_table_check_partition() {} - /** - Execute a ALTER TABLE CHECK PARTITION statement at runtime. - @param thd the current thread. - @return false on success. - */ bool execute(THD *thd); + + /* Override SQLCOM_CHECK, since it is an ALTER command */ + virtual enum_sql_command sql_command_code() const + { + return SQLCOM_ALTER_TABLE; + } }; /** Class that represents the ALTER TABLE t1 OPTIMIZE PARTITION p statement. */ -class Alter_table_optimize_partition_statement : public Optimize_table_statement +class Sql_cmd_alter_table_optimize_partition : public Sql_cmd_optimize_table { public: /** Constructor, used to represent a ALTER TABLE OPTIMIZE PARTITION statement. - @param lex the LEX structure for this statement. */ - Alter_table_optimize_partition_statement(LEX *lex) - : Optimize_table_statement(lex) + Sql_cmd_alter_table_optimize_partition() + : Sql_cmd_optimize_table() {} - ~Alter_table_optimize_partition_statement() + ~Sql_cmd_alter_table_optimize_partition() {} - /** - Execute a ALTER TABLE OPTIMIZE PARTITION statement at runtime. - @param thd the current thread. - @return false on success. - */ bool execute(THD *thd); + + /* Override SQLCOM_OPTIMIZE, since it is an ALTER command */ + virtual enum_sql_command sql_command_code() const + { + return SQLCOM_ALTER_TABLE; + } }; /** Class that represents the ALTER TABLE t1 REPAIR PARTITION p statement. */ -class Alter_table_repair_partition_statement : public Repair_table_statement +class Sql_cmd_alter_table_repair_partition : public Sql_cmd_repair_table { public: /** Constructor, used to represent a ALTER TABLE REPAIR PARTITION statement. - @param lex the LEX structure for this statement. */ - Alter_table_repair_partition_statement(LEX *lex) - : Repair_table_statement(lex) + Sql_cmd_alter_table_repair_partition() + : Sql_cmd_repair_table() {} - ~Alter_table_repair_partition_statement() + ~Sql_cmd_alter_table_repair_partition() {} - /** - Execute a ALTER TABLE REPAIR PARTITION statement at runtime. - @param thd the current thread. - @return false on success. - */ bool execute(THD *thd); + + /* Override SQLCOM_REPAIR, since it is an ALTER command */ + virtual enum_sql_command sql_command_code() const + { + return SQLCOM_ALTER_TABLE; + } }; /** Class that represents the ALTER TABLE t1 TRUNCATE PARTITION p statement. */ -class Alter_table_truncate_partition_statement : public Sql_statement +class Sql_cmd_alter_table_truncate_partition : public Sql_cmd_truncate_table { public: /** Constructor, used to represent a ALTER TABLE TRUNCATE PARTITION statement. - @param lex the LEX structure for this statement. */ - Alter_table_truncate_partition_statement(LEX *lex) - : Sql_statement(lex) + Sql_cmd_alter_table_truncate_partition() {} - virtual ~Alter_table_truncate_partition_statement() + virtual ~Sql_cmd_alter_table_truncate_partition() {} - /** - Execute a ALTER TABLE TRUNCATE PARTITION statement at runtime. - @param thd the current thread. - @return false on success. - */ bool execute(THD *thd); + + /* Override SQLCOM_TRUNCATE, since it is an ALTER command */ + virtual enum_sql_command sql_command_code() const + { + return SQLCOM_ALTER_TABLE; + } }; #endif /* WITH_PARTITION_STORAGE_ENGINE */ diff --git a/sql/sql_plist.h b/sql/sql_plist.h index 2b6f1067321..8e8c7fcaefb 100644 --- a/sql/sql_plist.h +++ b/sql/sql_plist.h @@ -18,7 +18,7 @@ #include <my_global.h> -template <typename T, typename B, typename C, typename I> +template <typename T, typename L> class I_P_List_iterator; class I_P_List_null_counter; template <typename T> class I_P_List_no_push_back; @@ -151,10 +151,14 @@ public: I::set_last(&rhs.m_first); C::swap(rhs); } + typedef B Adapter; + typedef I_P_List<T, B, C, I> Base; + typedef I_P_List_iterator<T, Base> Iterator; + typedef I_P_List_iterator<const T, Base> Const_Iterator; #ifndef _lint - friend class I_P_List_iterator<T, B, C, I>; + friend class I_P_List_iterator<T, Base>; + friend class I_P_List_iterator<const T, Base>; #endif - typedef I_P_List_iterator<T, B, C, I> Iterator; }; @@ -162,33 +166,33 @@ public: Iterator for I_P_List. */ -template <typename T, typename B, - typename C = I_P_List_null_counter, - typename I = I_P_List_no_push_back<T> > +template <typename T, typename L> class I_P_List_iterator { - const I_P_List<T, B, C, I> *list; + const L *list; T *current; public: - I_P_List_iterator(const I_P_List<T, B, C, I> &a) + I_P_List_iterator(const L &a) : list(&a), current(a.m_first) {} - I_P_List_iterator(const I_P_List<T, B, C, I> &a, T* current_arg) + I_P_List_iterator(const L &a, T* current_arg) : list(&a), current(current_arg) {} - inline void init(const I_P_List<T, B, C, I> &a) + inline void init(const L &a) { list= &a; current= a.m_first; } + /* Operator for it++ */ inline T* operator++(int) { T *result= current; if (result) - current= *B::next_ptr(current); + current= *L::Adapter::next_ptr(current); return result; } + /* Operator for ++it */ inline T* operator++() { - current= *B::next_ptr(current); + current= *L::Adapter::next_ptr(current); return current; } inline void rewind() @@ -207,7 +211,7 @@ template <typename T, T* T::*next, T** T::*prev> struct I_P_List_adapter { static inline T **next_ptr(T *el) { return &(el->*next); } - + static inline const T* const* next_ptr(const T *el) { return &(el->*next); } static inline T ***prev_ptr(T *el) { return &(el->*prev); } }; diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc index a81c05c5513..fbd0f0426c9 100644 --- a/sql/sql_plugin.cc +++ b/sql/sql_plugin.cc @@ -2036,7 +2036,7 @@ static bool finalize_install(THD *thd, TABLE *table, const LEX_STRING *name) if (tmp->state == PLUGIN_IS_DISABLED) { if (global_system_variables.log_warnings) - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_CANT_INITIALIZE_UDF, ER(ER_CANT_INITIALIZE_UDF), name->str, "Plugin is disabled"); } @@ -2182,7 +2182,7 @@ static bool do_uninstall(THD *thd, TABLE *table, const LEX_STRING *name) } if (!plugin->plugin_dl) { - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning(thd, Sql_condition::WARN_LEVEL_WARN, WARN_PLUGIN_DELETE_BUILTIN, ER(WARN_PLUGIN_DELETE_BUILTIN)); my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "PLUGIN", name->str); return 1; @@ -2195,7 +2195,7 @@ static bool do_uninstall(THD *thd, TABLE *table, const LEX_STRING *name) plugin->state= PLUGIN_IS_DELETED; if (plugin->ref_count) - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning(thd, Sql_condition::WARN_LEVEL_WARN, WARN_PLUGIN_BUSY, ER(WARN_PLUGIN_BUSY)); else reap_needed= true; diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 002fdb40036..fe3a35b3101 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -343,7 +343,7 @@ static bool send_prep_stmt(Prepared_statement *stmt, uint columns) int2store(buff+5, columns); int2store(buff+7, stmt->param_count); buff[9]= 0; // Guard against a 4.1 client - tmp= MY_MIN(stmt->thd->warning_info->statement_warn_count(), 65535); + tmp= MY_MIN(stmt->thd->get_stmt_da()->current_statement_warn_count(), 65535); int2store(buff+10, tmp); /* @@ -360,7 +360,7 @@ static bool send_prep_stmt(Prepared_statement *stmt, uint columns) if (!error) /* Flag that a response has already been sent */ - thd->stmt_da->disable_status(); + thd->get_stmt_da()->disable_status(); DBUG_RETURN(error); } @@ -373,7 +373,7 @@ static bool send_prep_stmt(Prepared_statement *stmt, thd->client_stmt_id= stmt->id; thd->client_param_count= stmt->param_count; thd->clear_error(); - thd->stmt_da->disable_status(); + thd->get_stmt_da()->disable_status(); return 0; } @@ -2054,7 +2054,7 @@ static bool check_prepared_statement(Prepared_statement *stmt) /* Reset warning count for each query that uses tables */ if (tables) - thd->warning_info->opt_clear_warning_info(thd->query_id); + thd->get_stmt_da()->opt_clear_warning_info(thd->query_id); switch (sql_command) { case SQLCOM_REPLACE: @@ -2857,7 +2857,7 @@ void mysqld_stmt_close(THD *thd, char *packet) Prepared_statement *stmt; DBUG_ENTER("mysqld_stmt_close"); - thd->stmt_da->disable_status(); + thd->get_stmt_da()->disable_status(); if (!(stmt= find_prepared_statement(thd, stmt_id))) DBUG_VOID_RETURN; @@ -2933,7 +2933,7 @@ void mysql_stmt_get_longdata(THD *thd, char *packet, ulong packet_length) status_var_increment(thd->status_var.com_stmt_send_long_data); - thd->stmt_da->disable_status(); + thd->get_stmt_da()->disable_status(); #ifndef EMBEDDED_LIBRARY /* Minimal size of long data packet is 6 bytes */ if (packet_length < MYSQL_LONG_DATA_HEADER) @@ -2962,12 +2962,12 @@ void mysql_stmt_get_longdata(THD *thd, char *packet, ulong packet_length) param= stmt->param_array[param_number]; - Diagnostics_area new_stmt_da, *save_stmt_da= thd->stmt_da; - Warning_info new_warnning_info(thd->query_id, false); - Warning_info *save_warinig_info= thd->warning_info; + Diagnostics_area new_stmt_da(true), *save_stmt_da= thd->stmt_da; + Warning_info new_warning_info(thd->query_id, false, true); + Warning_info *save_warning_info= thd->warning_info; thd->stmt_da= &new_stmt_da; - thd->warning_info= &new_warnning_info; + thd->warning_info= &new_warning_info; #ifndef EMBEDDED_LIBRARY param->set_longdata(packet, (ulong) (packet_end - packet)); @@ -2981,7 +2981,7 @@ void mysql_stmt_get_longdata(THD *thd, char *packet, ulong packet_length) strncpy(stmt->last_error, thd->stmt_da->message(), MYSQL_ERRMSG_SIZE); } thd->stmt_da= save_stmt_da; - thd->warning_info= save_warinig_info; + thd->warning_info= save_warning_info; general_log_print(thd, thd->get_command(), NullS); @@ -3057,8 +3057,7 @@ Reprepare_observer::report_error(THD *thd) that this thread execution stops and returns to the caller, backtracking all the way to Prepared_statement::execute_loop(). */ - thd->stmt_da->set_error_status(thd, ER_NEED_REPREPARE, - ER(ER_NEED_REPREPARE), "HY000"); + thd->get_stmt_da()->set_error_status(ER_NEED_REPREPARE); m_invalidated= TRUE; return TRUE; @@ -3543,7 +3542,7 @@ reexecute: we need to repeat it when reexecuting, to recreate these warnings. */ - need_set_parameters= thd->warning_info->statement_warn_count(); + need_set_parameters= thd->get_stmt_da()->statement_warn_count(); reprepare_observer.reset_reprepare_observer(); @@ -3575,7 +3574,7 @@ reexecute: reprepare_observer.is_invalidated() && reprepare_attempt++ < MAX_REPREPARE_ATTEMPTS) { - DBUG_ASSERT(thd->stmt_da->sql_errno() == ER_NEED_REPREPARE); + DBUG_ASSERT(thd->get_stmt_da()->sql_errno() == ER_NEED_REPREPARE); thd->clear_error(); error= reprepare(); @@ -3677,7 +3676,7 @@ Prepared_statement::reprepare() Sic: we can't simply silence warnings during reprepare, because if it's failed, we need to return all the warnings to the user. */ - thd->warning_info->clear_warning_info(thd->query_id); + thd->get_stmt_da()->clear_warning_info(thd->query_id); } return error; } @@ -4039,7 +4038,7 @@ Ed_result_set::Ed_result_set(List<Ed_row> *rows_arg, */ Ed_connection::Ed_connection(THD *thd) - :m_warning_info(thd->query_id, false, true), + :m_diagnostics_area(thd->query_id, false), m_thd(thd), m_rsets(0), m_current_rset(0) @@ -4065,7 +4064,7 @@ Ed_connection::free_old_result() } m_current_rset= m_rsets; m_diagnostics_area.reset_diagnostics_area(); - m_warning_info.clear_warning_info(m_thd->query_id); + m_diagnostics_area.clear_warning_info(m_thd->query_id); } @@ -4102,23 +4101,20 @@ bool Ed_connection::execute_direct(Server_runnable *server_runnable) Protocol_local protocol_local(m_thd, this); Prepared_statement stmt(m_thd); Protocol *save_protocol= m_thd->protocol; - Diagnostics_area *save_diagnostics_area= m_thd->stmt_da; - Warning_info *save_warning_info= m_thd->warning_info; + Diagnostics_area *save_diagnostics_area= m_thd->get_stmt_da(); DBUG_ENTER("Ed_connection::execute_direct"); free_old_result(); /* Delete all data from previous execution, if any */ m_thd->protocol= &protocol_local; - m_thd->stmt_da= &m_diagnostics_area; - m_thd->warning_info= &m_warning_info; + m_thd->set_stmt_da(&m_diagnostics_area); rc= stmt.execute_server_runnable(server_runnable); m_thd->protocol->end_statement(); m_thd->protocol= save_protocol; - m_thd->stmt_da= save_diagnostics_area; - m_thd->warning_info= save_warning_info; + m_thd->set_stmt_da(save_diagnostics_area); /* Protocol_local makes use of m_current_rset to keep track of the last result set, while adding result sets to the end. diff --git a/sql/sql_prepare.h b/sql/sql_prepare.h index e0891bbd188..ea5ebddb561 100644 --- a/sql/sql_prepare.h +++ b/sql/sql_prepare.h @@ -253,16 +253,9 @@ public: */ ulong get_warn_count() const { - return m_warning_info.warn_count(); + return m_diagnostics_area.warn_count(); } - /** - Get the server warnings as a result set. - The result set has fixed metadata: - The first column is the level. - The second is a numeric code. - The third is warning text. - */ - List<MYSQL_ERROR> *get_warn_list() { return &m_warning_info.warn_list(); } + /** The following members are only valid if execute_direct() or move_to_next_result() returned an error. @@ -311,7 +304,6 @@ public: ~Ed_connection() { free_old_result(); } private: Diagnostics_area m_diagnostics_area; - Warning_info m_warning_info; /** Execute direct interface does not support multi-statements, only multi-results. So we never have a situation when we have diff --git a/sql/sql_priv.h b/sql/sql_priv.h index 345556a0b7f..a44f46a1a02 100644 --- a/sql/sql_priv.h +++ b/sql/sql_priv.h @@ -50,7 +50,7 @@ do { \ compile_time_assert(MYSQL_VERSION_ID < VerHi * 10000 + VerLo * 100); \ if (((THD *) Thd) != NULL) \ - push_warning_printf(((THD *) Thd), MYSQL_ERROR::WARN_LEVEL_WARN, \ + push_warning_printf(((THD *) Thd), Sql_condition::WARN_LEVEL_WARN, \ ER_WARN_DEPRECATED_SYNTAX, \ ER(ER_WARN_DEPRECATED_SYNTAX), \ (Old), (New)); \ diff --git a/sql/sql_reload.cc b/sql/sql_reload.cc index 2720dc7cd74..052e303798c 100644 --- a/sql/sql_reload.cc +++ b/sql/sql_reload.cc @@ -176,7 +176,7 @@ bool reload_acl_and_cache(THD *thd, unsigned long options, mysql_mutex_lock(&LOCK_active_mi); if (!(mi= (master_info_index-> get_master_info(&connection_name, - MYSQL_ERROR::WARN_LEVEL_ERROR)))) + Sql_condition::WARN_LEVEL_ERROR)))) { result= 1; } @@ -348,7 +348,7 @@ bool reload_acl_and_cache(THD *thd, unsigned long options, if (!(mi= (master_info_index-> get_master_info(&lex_mi->connection_name, - MYSQL_ERROR::WARN_LEVEL_ERROR)))) + Sql_condition::WARN_LEVEL_ERROR)))) { result= 1; } diff --git a/sql/sql_rename.cc b/sql/sql_rename.cc index c91623cee6e..53dbbb43bb0 100644 --- a/sql/sql_rename.cc +++ b/sql/sql_rename.cc @@ -37,8 +37,8 @@ static TABLE_LIST *rename_tables(THD *thd, TABLE_LIST *table_list, static TABLE_LIST *reverse_table_list(TABLE_LIST *table_list); /* - Every second entry in the table_list is the original name and every - second entry is the new name. + Every two entries in the table_list form a pair of original name and + the new name. */ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent) diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index 9e6cd55fe07..4067e765316 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -1393,7 +1393,7 @@ int start_slave(THD* thd , Master_info* mi, bool net_report) /* Issuing warning then started without --skip-slave-start */ if (!opt_skip_slave_start) - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + push_warning(thd, Sql_condition::WARN_LEVEL_NOTE, ER_MISSING_SKIP_SLAVE, ER(ER_MISSING_SKIP_SLAVE)); } @@ -1401,7 +1401,7 @@ int start_slave(THD* thd , Master_info* mi, bool net_report) mysql_mutex_unlock(&mi->rli.data_lock); } else if (thd->lex->mi.pos || thd->lex->mi.relay_log_pos) - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, ER_UNTIL_COND_IGNORED, + push_warning(thd, Sql_condition::WARN_LEVEL_NOTE, ER_UNTIL_COND_IGNORED, ER(ER_UNTIL_COND_IGNORED)); if (!slave_errno) @@ -1418,7 +1418,7 @@ int start_slave(THD* thd , Master_info* mi, bool net_report) else { /* no error if all threads are already started, only a warning */ - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, ER_SLAVE_WAS_RUNNING, + push_warning(thd, Sql_condition::WARN_LEVEL_NOTE, ER_SLAVE_WAS_RUNNING, ER(ER_SLAVE_WAS_RUNNING)); } @@ -1483,7 +1483,7 @@ int stop_slave(THD* thd, Master_info* mi, bool net_report ) { //no error if both threads are already stopped, only a warning slave_errno= 0; - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, ER_SLAVE_WAS_NOT_RUNNING, + push_warning(thd, Sql_condition::WARN_LEVEL_NOTE, ER_SLAVE_WAS_NOT_RUNNING, ER(ER_SLAVE_WAS_NOT_RUNNING)); } unlock_slave_threads(mi); @@ -1737,7 +1737,7 @@ bool change_master(THD* thd, Master_info* mi, bool *master_info_added) /* if new Master_info doesn't exists, add it */ if (!master_info_index->get_master_info(&mi->connection_name, - MYSQL_ERROR::WARN_LEVEL_NOTE)) + Sql_condition::WARN_LEVEL_NOTE)) { if (master_info_index->add_master_info(mi, TRUE)) { @@ -1875,7 +1875,7 @@ bool change_master(THD* thd, Master_info* mi, bool *master_info_added) if (lex_mi->ssl || lex_mi->ssl_ca || lex_mi->ssl_capath || lex_mi->ssl_cert || lex_mi->ssl_cipher || lex_mi->ssl_key || lex_mi->ssl_verify_server_cert || lex_mi->ssl_crl || lex_mi->ssl_crlpath) - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + push_warning(thd, Sql_condition::WARN_LEVEL_NOTE, ER_SLAVE_IGNORED_SSL_PARAMS, ER(ER_SLAVE_IGNORED_SSL_PARAMS)); #endif @@ -2093,7 +2093,7 @@ bool mysql_show_binlog_events(THD* thd) mysql_mutex_lock(&LOCK_active_mi); if (!(mi= master_info_index-> get_master_info(&thd->variables.default_master_connection, - MYSQL_ERROR::WARN_LEVEL_ERROR))) + Sql_condition::WARN_LEVEL_ERROR))) { mysql_mutex_unlock(&LOCK_active_mi); DBUG_RETURN(TRUE); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index ab0fc173ca9..b913520213d 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -373,7 +373,7 @@ bool handle_select(THD *thd, LEX *lex, select_result *result, If LIMIT ROWS EXAMINED interrupted query execution, issue a warning, continue with normal processing and produce an incomplete query result. */ - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_QUERY_EXCEEDED_ROWS_EXAMINED_LIMIT, ER(ER_QUERY_EXCEEDED_ROWS_EXAMINED_LIMIT), thd->accessed_rows_and_keys, @@ -1180,9 +1180,10 @@ JOIN::optimize_inner() if (!tbl->embedding) { Item *prune_cond= tbl->on_expr? tbl->on_expr : conds; - tbl->table->no_partitions_used= prune_partitions(thd, tbl->table, - prune_cond); - } + tbl->table->all_partitions_pruned_away= prune_partitions(thd, + tbl->table, + prune_cond); + } } } #endif @@ -3322,9 +3323,9 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list, set_statistics_for_table(join->thd, table); #ifdef WITH_PARTITION_STORAGE_ENGINE - const bool no_partitions_used= table->no_partitions_used; + const bool all_partitions_pruned_away= table->all_partitions_pruned_away; #else - const bool no_partitions_used= FALSE; + const bool all_partitions_pruned_away= FALSE; #endif DBUG_EXECUTE_IF("bug11747970_raise_error", @@ -3361,7 +3362,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list, if (!table->is_filled_at_execution() && ((!table->file->stats.records && (table->file->ha_table_flags() & HA_STATS_RECORDS_IS_EXACT)) || - no_partitions_used) && !embedding) + all_partitions_pruned_away) && !embedding) { // Empty table s->dependent= 0; // Ignore LEFT JOIN depend. no_rows_const_tables |= table->map; @@ -3405,7 +3406,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list, (table->s->system || (table->file->stats.records <= 1 && (table->file->ha_table_flags() & HA_STATS_RECORDS_IS_EXACT)) || - no_partitions_used) && + all_partitions_pruned_away) && !s->dependent && !table->fulltext_searched && !join->no_const_tables) { @@ -5604,7 +5605,7 @@ best_access_path(JOIN *join, */ if ((found_part & 1) && (!(table->file->index_flags(key, 0, 0) & HA_ONLY_WHOLE_INDEX) || - found_part == PREV_BITS(uint,keyinfo->key_parts))) + found_part == PREV_BITS(uint,keyinfo->user_defined_key_parts))) { max_key_part= max_part_bit(found_part); /* @@ -5698,7 +5699,7 @@ best_access_path(JOIN *join, */ double rec_per_key; if (!(rec_per_key=(double) - keyinfo->rec_per_key[keyinfo->key_parts-1])) + keyinfo->rec_per_key[keyinfo->user_defined_key_parts-1])) rec_per_key=(double) s->records/rec+1; if (!s->records) @@ -5708,10 +5709,10 @@ best_access_path(JOIN *join, else { double a=s->records*0.01; - if (keyinfo->key_parts > 1) + if (keyinfo->user_defined_key_parts > 1) tmp= (max_key_part * (rec_per_key - a) + - a*keyinfo->key_parts - rec_per_key)/ - (keyinfo->key_parts-1); + a*keyinfo->user_defined_key_parts - rec_per_key)/ + (keyinfo->user_defined_key_parts-1); else tmp= a; set_if_bigger(tmp,1.0); @@ -7866,8 +7867,8 @@ static bool create_hj_key_for_table(JOIN *join, JOIN_TAB *join_tab, !(key_part_info = (KEY_PART_INFO *) thd->alloc(sizeof(KEY_PART_INFO)* key_parts))) DBUG_RETURN(TRUE); - keyinfo->usable_key_parts= keyinfo->key_parts = key_parts; - keyinfo->ext_key_parts= keyinfo->key_parts; + keyinfo->usable_key_parts= keyinfo->user_defined_key_parts = key_parts; + keyinfo->ext_key_parts= keyinfo->user_defined_key_parts; keyinfo->key_part= key_part_info; keyinfo->key_length=0; keyinfo->algorithm= HA_KEY_ALG_UNDEF; @@ -7913,7 +7914,7 @@ static bool create_hj_key_for_table(JOIN *join, JOIN_TAB *join_tab, keyuse++; } while (keyuse->table == table && keyuse->is_for_hash_join()); - keyinfo->ext_key_parts= keyinfo->key_parts; + keyinfo->ext_key_parts= keyinfo->user_defined_key_parts; keyinfo->ext_key_flags= keyinfo->flags; keyinfo->ext_key_part_map= 0; @@ -10570,7 +10571,7 @@ bool TABLE_REF::tmp_table_index_lookup_init(THD *thd, bool value, uint skip) { - uint tmp_key_parts= tmp_key->key_parts; + uint tmp_key_parts= tmp_key->user_defined_key_parts; uint i; DBUG_ENTER("TABLE_REF::tmp_table_index_lookup_init"); @@ -14864,8 +14865,8 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields, keyinfo->key_part=key_part_info; keyinfo->flags=HA_NOSAME | HA_BINARY_PACK_KEY | HA_PACK_KEY; keyinfo->ext_key_flags= keyinfo->flags; - keyinfo->usable_key_parts=keyinfo->key_parts= param->group_parts; - keyinfo->ext_key_parts= keyinfo->key_parts; + keyinfo->usable_key_parts=keyinfo->user_defined_key_parts= param->group_parts; + keyinfo->ext_key_parts= keyinfo->user_defined_key_parts; keyinfo->key_length=0; keyinfo->rec_per_key=NULL; keyinfo->read_stats= NULL; @@ -14965,16 +14966,17 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields, share->uniques= 1; } null_pack_length-=hidden_null_pack_length; - keyinfo->key_parts= ((field_count-param->hidden_field_count)+ - (share->uniques ? test(null_pack_length) : 0)); - keyinfo->ext_key_parts= keyinfo->key_parts; + keyinfo->user_defined_key_parts= + ((field_count-param->hidden_field_count)+ + (share->uniques ? test(null_pack_length) : 0)); + keyinfo->ext_key_parts= keyinfo->user_defined_key_parts; table->distinct= 1; share->keys= 1; if (!(key_part_info= (KEY_PART_INFO*) alloc_root(&table->mem_root, - keyinfo->key_parts * sizeof(KEY_PART_INFO)))) + keyinfo->user_defined_key_parts * sizeof(KEY_PART_INFO)))) goto err; - bzero((void*) key_part_info, keyinfo->key_parts * sizeof(KEY_PART_INFO)); + bzero((void*) key_part_info, keyinfo->user_defined_key_parts * sizeof(KEY_PART_INFO)); table->keys_in_use_for_query.set_bit(0); share->keys_in_use.set_bit(0); table->key_info= table->s->key_info= keyinfo; @@ -15311,13 +15313,13 @@ bool create_internal_tmp_table(TABLE *table, KEY *keyinfo, { // Get keys for ni_create bool using_unique_constraint=0; HA_KEYSEG *seg= (HA_KEYSEG*) alloc_root(&table->mem_root, - sizeof(*seg) * keyinfo->key_parts); + sizeof(*seg) * keyinfo->user_defined_key_parts); if (!seg) goto err; - bzero(seg, sizeof(*seg) * keyinfo->key_parts); + bzero(seg, sizeof(*seg) * keyinfo->user_defined_key_parts); if (keyinfo->key_length >= table->file->max_key_length() || - keyinfo->key_parts > table->file->max_key_parts() || + keyinfo->user_defined_key_parts > table->file->max_key_parts() || share->uniques) { if (!share->uniques && !(keyinfo->flags & HA_NOSAME)) @@ -15332,7 +15334,7 @@ bool create_internal_tmp_table(TABLE *table, KEY *keyinfo, share->uniques= 1; using_unique_constraint=1; bzero((char*) &uniquedef,sizeof(uniquedef)); - uniquedef.keysegs=keyinfo->key_parts; + uniquedef.keysegs=keyinfo->user_defined_key_parts; uniquedef.seg=seg; uniquedef.null_are_equal=1; @@ -15348,10 +15350,10 @@ bool create_internal_tmp_table(TABLE *table, KEY *keyinfo, /* Create a key */ bzero((char*) &keydef,sizeof(keydef)); keydef.flag= keyinfo->flags & HA_NOSAME; - keydef.keysegs= keyinfo->key_parts; + keydef.keysegs= keyinfo->user_defined_key_parts; keydef.seg= seg; } - for (uint i=0; i < keyinfo->key_parts ; i++,seg++) + for (uint i=0; i < keyinfo->user_defined_key_parts ; i++,seg++) { Field *field=keyinfo->key_part[i].field; seg->flag= 0; @@ -15479,13 +15481,13 @@ bool create_internal_tmp_table(TABLE *table, KEY *keyinfo, { // Get keys for ni_create bool using_unique_constraint=0; HA_KEYSEG *seg= (HA_KEYSEG*) alloc_root(&table->mem_root, - sizeof(*seg) * keyinfo->key_parts); + sizeof(*seg) * keyinfo->user_defined_key_parts); if (!seg) goto err; - bzero(seg, sizeof(*seg) * keyinfo->key_parts); + bzero(seg, sizeof(*seg) * keyinfo->user_defined_key_parts); if (keyinfo->key_length >= table->file->max_key_length() || - keyinfo->key_parts > table->file->max_key_parts() || + keyinfo->user_defined_key_parts > table->file->max_key_parts() || share->uniques) { /* Can't create a key; Make a unique constraint instead of a key */ @@ -15493,7 +15495,7 @@ bool create_internal_tmp_table(TABLE *table, KEY *keyinfo, share->uniques= 1; using_unique_constraint=1; bzero((char*) &uniquedef,sizeof(uniquedef)); - uniquedef.keysegs=keyinfo->key_parts; + uniquedef.keysegs=keyinfo->user_defined_key_parts; uniquedef.seg=seg; uniquedef.null_are_equal=1; @@ -15510,10 +15512,10 @@ bool create_internal_tmp_table(TABLE *table, KEY *keyinfo, bzero((char*) &keydef,sizeof(keydef)); keydef.flag= ((keyinfo->flags & HA_NOSAME) | HA_BINARY_PACK_KEY | HA_PACK_KEY); - keydef.keysegs= keyinfo->key_parts; + keydef.keysegs= keyinfo->user_defined_key_parts; keydef.seg= seg; } - for (uint i=0; i < keyinfo->key_parts ; i++,seg++) + for (uint i=0; i < keyinfo->user_defined_key_parts ; i++,seg++) { Field *field=keyinfo->key_part[i].field; seg->flag= 0; @@ -16242,7 +16244,7 @@ sub_select(JOIN *join,JOIN_TAB *join_tab,bool end_of_records) if (join_tab->on_precond && !join_tab->on_precond->val_int()) rc= NESTED_LOOP_NO_MORE_ROWS; } - join->thd->warning_info->reset_current_row_for_warning(); + join->thd->get_stmt_da()->reset_current_row_for_warning(); if (rc != NESTED_LOOP_NO_MORE_ROWS && (rc= join_tab_execution_startup(join_tab)) < 0) @@ -16476,7 +16478,7 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab, enum enum_nested_loop_state rc; /* A match from join_tab is found for the current partial join. */ rc= (*join_tab->next_select)(join, join_tab+1, 0); - join->thd->warning_info->inc_current_row_for_warning(); + join->thd->get_stmt_da()->inc_current_row_for_warning(); if (rc != NESTED_LOOP_OK && rc != NESTED_LOOP_NO_MORE_ROWS) DBUG_RETURN(rc); if (return_tab < join->return_tab) @@ -16494,7 +16496,7 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab, } else { - join->thd->warning_info->inc_current_row_for_warning(); + join->thd->get_stmt_da()->inc_current_row_for_warning(); join_tab->read_record.unlock_row(join_tab); } } @@ -16505,7 +16507,7 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab, with the beginning coinciding with the current partial join. */ join->examined_rows++; - join->thd->warning_info->inc_current_row_for_warning(); + join->thd->get_stmt_da()->inc_current_row_for_warning(); join_tab->read_record.unlock_row(join_tab); } DBUG_RETURN(NESTED_LOOP_OK); @@ -16618,7 +16620,7 @@ int report_error(TABLE *table, int error) if (error != HA_ERR_LOCK_DEADLOCK && error != HA_ERR_LOCK_WAIT_TIMEOUT && !table->in_use->killed) { - push_warning_printf(table->in_use, MYSQL_ERROR::WARN_LEVEL_WARN, error, + push_warning_printf(table->in_use, Sql_condition::WARN_LEVEL_WARN, error, "Got error %d when reading table %`s.%`s", error, table->s->db.str, table->s->table_name.str); sql_print_error("Got error %d when reading table '%s'", @@ -18284,7 +18286,7 @@ static int test_if_order_by_key(ORDER *order, TABLE *table, uint idx, { KEY_PART_INFO *key_part,*key_part_end; key_part=table->key_info[idx].key_part; - key_part_end=key_part+table->key_info[idx].key_parts; + key_part_end=key_part+table->key_info[idx].user_defined_key_parts; key_part_map const_key_parts=table->const_key_parts[idx]; int reverse=0; uint key_parts; @@ -18326,7 +18328,7 @@ static int test_if_order_by_key(ORDER *order, TABLE *table, uint idx, (we have to stop as first not continous primary key part) */ for (key_part_end= key_part, - end= key_part+table->key_info[table->s->primary_key].key_parts; + end= key_part+table->key_info[table->s->primary_key].user_defined_key_parts; key_part_end < end; key_part_end++, pk_part_idx++) { /* Found hole in the pk_parts; Abort */ @@ -18343,7 +18345,7 @@ static int test_if_order_by_key(ORDER *order, TABLE *table, uint idx, Test if the primary key parts were all const (i.e. there's one row). The sorting doesn't matter. */ - if (key_part == start+table->key_info[table->s->primary_key].key_parts && + if (key_part == start+table->key_info[table->s->primary_key].user_defined_key_parts && reverse == 0) { key_parts= 0; @@ -18369,7 +18371,7 @@ static int test_if_order_by_key(ORDER *order, TABLE *table, uint idx, } if (on_pk_suffix) { - uint used_key_parts_secondary= table->key_info[idx].key_parts; + uint used_key_parts_secondary= table->key_info[idx].user_defined_key_parts; uint used_key_parts_pk= (uint) (key_part - table->key_info[table->s->primary_key].key_part); key_parts= used_key_parts_pk + used_key_parts_secondary; @@ -18480,7 +18482,7 @@ test_if_subkey(ORDER *order, TABLE *table, uint ref, uint ref_key_parts, { if (usable_keys->is_set(nr) && table->key_info[nr].key_length < min_length && - table->key_info[nr].key_parts >= ref_key_parts && + table->key_info[nr].user_defined_key_parts >= ref_key_parts && is_subkey(table->key_info[nr].key_part, ref_key_part, ref_key_part_end) && test_if_order_by_key(order, table, nr)) @@ -18538,7 +18540,7 @@ list_contains_unique_index(TABLE *table, KEY_PART_INFO *key_part, *key_part_end; for (key_part=keyinfo->key_part, - key_part_end=key_part+ keyinfo->key_parts; + key_part_end=key_part+ keyinfo->user_defined_key_parts; key_part < key_part_end; key_part++) { @@ -18846,7 +18848,7 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit, order_direction= best_key_direction; /* saved_best_key_parts is actual number of used keyparts found by the - test_if_order_by_key function. It could differ from keyinfo->key_parts, + test_if_order_by_key function. It could differ from keyinfo->user_defined_key_parts, thus we have to restore it in case of desc order as it affects QUICK_SELECT_DESC behaviour. */ @@ -19810,7 +19812,7 @@ find_order_in_list(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables, warning so the user knows that the field from the FROM clause overshadows the column reference from the SELECT list. */ - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_NON_UNIQ_ERROR, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_NON_UNIQ_ERROR, ER(ER_NON_UNIQ_ERROR), ((Item_ident*) order_item)->field_name, current_thd->where); @@ -23189,12 +23191,12 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table, if (group) { /* - Used_key_parts can be larger than keyinfo->key_parts + Used_key_parts can be larger than keyinfo->user_defined_key_parts when using a secondary index clustered with a primary key (e.g. as in Innodb). See Bug #28591 for details. */ - uint used_index_parts= keyinfo->key_parts; + uint used_index_parts= keyinfo->user_defined_key_parts; uint used_pk_parts= 0; if (used_key_parts > used_index_parts) used_pk_parts= used_key_parts-used_index_parts; @@ -23209,7 +23211,7 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table, of the primary key are considered unknown we assume they are equal to 1. */ - if (used_key_parts == pkinfo->key_parts || + if (used_key_parts == pkinfo->user_defined_key_parts || pkinfo->rec_per_key[0] == 0) rec_per_key= 1; if (rec_per_key > 1) @@ -23276,7 +23278,7 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table, select_limit= (ha_rows) (select_limit * (double) table_records / table->quick_condition_rows); - rec_per_key= keyinfo->actual_rec_per_key(keyinfo->key_parts-1); + rec_per_key= keyinfo->actual_rec_per_key(keyinfo->user_defined_key_parts-1); set_if_bigger(rec_per_key, 1); /* Here we take into account the fact that rows are @@ -23302,12 +23304,12 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table, quick_records= table->quick_rows[nr]; if (best_key < 0 || (select_limit <= MY_MIN(quick_records,best_records) ? - keyinfo->key_parts < best_key_parts : + keyinfo->user_defined_key_parts < best_key_parts : quick_records < best_records) || (!is_best_covering && is_covering)) { best_key= nr; - best_key_parts= keyinfo->key_parts; + best_key_parts= keyinfo->user_defined_key_parts; if (saved_best_key_parts) *saved_best_key_parts= used_key_parts; best_records= quick_records; diff --git a/sql/sql_servers.cc b/sql/sql_servers.cc index b5b7f9866c5..cf96297391c 100644 --- a/sql/sql_servers.cc +++ b/sql/sql_servers.cc @@ -265,9 +265,9 @@ bool servers_reload(THD *thd) Execution might have been interrupted; only print the error message if an error condition has been raised. */ - if (thd->stmt_da->is_error()) + if (thd->get_stmt_da()->is_error()) sql_print_error("Can't open and lock privilege tables: %s", - thd->stmt_da->message()); + thd->get_stmt_da()->message()); return_val= FALSE; goto end; } @@ -631,7 +631,7 @@ int drop_server(THD *thd, LEX_SERVER_OPTIONS *server_options) if (close_cached_connection_tables(thd, &name)) { - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR, "Server connection in use"); } @@ -1060,7 +1060,7 @@ int alter_server(THD *thd, LEX_SERVER_OPTIONS *server_options) if (close_cached_connection_tables(thd, &name)) { - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR, "Server connection in use"); } diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 9a6717ff318..1fc24b61c47 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -910,8 +910,8 @@ public: } bool handle_condition(THD *thd, uint sql_errno, const char * /* sqlstate */, - MYSQL_ERROR::enum_warning_level level, - const char *message, MYSQL_ERROR ** /* cond_hdl */) + Sql_condition::enum_warning_level level, + const char *message, Sql_condition ** /* cond_hdl */) { /* The handler does not handle the errors raised by itself. @@ -942,7 +942,7 @@ public: case ER_NO_SUCH_TABLE: case ER_NO_SUCH_TABLE_IN_ENGINE: /* Established behavior: warn if underlying tables are missing. */ - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_VIEW_INVALID, ER(ER_VIEW_INVALID), m_top_view->get_db_name(), @@ -952,7 +952,7 @@ public: case ER_SP_DOES_NOT_EXIST: /* Established behavior: warn if underlying functions are missing. */ - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_VIEW_INVALID, ER(ER_VIEW_INVALID), m_top_view->get_db_name(), @@ -2471,7 +2471,7 @@ int fill_show_explain(THD *thd, TABLE_LIST *table, COND *cond) else warning_text= explain_req.query_str.c_ptr_safe(); - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + push_warning(thd, Sql_condition::WARN_LEVEL_NOTE, ER_YES, warning_text); } DBUG_RETURN(bres); @@ -4085,7 +4085,7 @@ fill_schema_table_by_open(THD *thd, bool is_show_fields_or_keys, of backward compatibility. */ if (!is_show_fields_or_keys && result && thd->is_error() && - thd->stmt_da->sql_errno() == ER_NO_SUCH_TABLE) + thd->get_stmt_da()->sql_errno() == ER_NO_SUCH_TABLE) { /* Hide error for a non-existing table. @@ -4181,7 +4181,7 @@ static int fill_schema_table_names(THD *thd, TABLE_LIST *tables, default: DBUG_ASSERT(0); } - if (thd->is_error() && thd->stmt_da->sql_errno() == ER_NO_SUCH_TABLE) + if (thd->is_error() && thd->get_stmt_da()->sql_errno() == ER_NO_SUCH_TABLE) { thd->clear_error(); return 0; @@ -4337,7 +4337,7 @@ static int fill_schema_table_from_frm(THD *thd, TABLE_LIST *tables, uint res= 0; int not_used; my_hash_value_type hash_value; - char key[MAX_DBKEY_LENGTH]; + const char *key; uint key_length; char db_name_buff[NAME_LEN + 1], table_name_buff[NAME_LEN + 1]; @@ -4388,7 +4388,7 @@ static int fill_schema_table_from_frm(THD *thd, TABLE_LIST *tables, */ DBUG_ASSERT(can_deadlock); - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_WARN_I_S_SKIPPED_TABLE, ER(ER_WARN_I_S_SKIPPED_TABLE), table_list.db, table_list.table_name); @@ -4410,7 +4410,7 @@ static int fill_schema_table_from_frm(THD *thd, TABLE_LIST *tables, goto end; } - key_length= create_table_def_key(thd, key, &table_list, 0); + key_length= get_table_def_key(&table_list, &key); hash_value= my_calc_hash(&table_def_cache, (uchar*) key, key_length); mysql_mutex_lock(&LOCK_open); share= get_table_share(thd, &table_list, key, @@ -4522,9 +4522,9 @@ public: bool handle_condition(THD *thd, uint sql_errno, const char* sqlstate, - MYSQL_ERROR::enum_warning_level level, + Sql_condition::enum_warning_level level, const char* msg, - MYSQL_ERROR ** cond_hdl) + Sql_condition ** cond_hdl) { if (sql_errno == ER_PARSE_ERROR || sql_errno == ER_TRG_NO_DEFINER || @@ -5110,13 +5110,14 @@ err: column with the error text, and clear the error so that the operation can continue. */ - const char *error= thd->is_error() ? thd->stmt_da->message() : ""; + const char *error= thd->is_error() ? thd->get_stmt_da()->message() : ""; table->field[20]->store(error, strlen(error), cs); if (thd->is_error()) { - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, - thd->stmt_da->sql_errno(), thd->stmt_da->message()); + push_warning(thd, Sql_condition::WARN_LEVEL_WARN, + thd->get_stmt_da()->sql_errno(), + thd->get_stmt_da()->message()); thd->clear_error(); } } @@ -5280,8 +5281,9 @@ static int get_schema_column_record(THD *thd, TABLE_LIST *tables, rather than in SHOW COLUMNS */ if (thd->is_error()) - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, - thd->stmt_da->sql_errno(), thd->stmt_da->message()); + push_warning(thd, Sql_condition::WARN_LEVEL_WARN, + thd->get_stmt_da()->sql_errno(), + thd->get_stmt_da()->message()); thd->clear_error(); res= 0; } @@ -5951,8 +5953,9 @@ static int get_schema_stat_record(THD *thd, TABLE_LIST *tables, rather than in SHOW KEYS */ if (thd->is_error()) - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, - thd->stmt_da->sql_errno(), thd->stmt_da->message()); + push_warning(thd, Sql_condition::WARN_LEVEL_WARN, + thd->get_stmt_da()->sql_errno(), + thd->get_stmt_da()->message()); thd->clear_error(); res= 0; } @@ -6136,7 +6139,7 @@ static int get_schema_views_record(THD *thd, TABLE_LIST *tables, */ while ((item= it++)) { - if ((field= item->filed_for_view_update()) && field->field && + if ((field= item->field_for_view_update()) && field->field && !field->field->table->pos_in_table_list->schema_table) { updatable_view= 1; @@ -6173,8 +6176,9 @@ static int get_schema_views_record(THD *thd, TABLE_LIST *tables, if (schema_table_store_record(thd, table)) DBUG_RETURN(1); if (res && thd->is_error()) - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, - thd->stmt_da->sql_errno(), thd->stmt_da->message()); + push_warning(thd, Sql_condition::WARN_LEVEL_WARN, + thd->get_stmt_da()->sql_errno(), + thd->get_stmt_da()->message()); } if (res) thd->clear_error(); @@ -6207,8 +6211,9 @@ static int get_schema_constraints_record(THD *thd, TABLE_LIST *tables, if (res) { if (thd->is_error()) - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, - thd->stmt_da->sql_errno(), thd->stmt_da->message()); + push_warning(thd, Sql_condition::WARN_LEVEL_WARN, + thd->get_stmt_da()->sql_errno(), + thd->get_stmt_da()->message()); thd->clear_error(); DBUG_RETURN(0); } @@ -6313,8 +6318,9 @@ static int get_schema_triggers_record(THD *thd, TABLE_LIST *tables, if (res) { if (thd->is_error()) - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, - thd->stmt_da->sql_errno(), thd->stmt_da->message()); + push_warning(thd, Sql_condition::WARN_LEVEL_WARN, + thd->get_stmt_da()->sql_errno(), + thd->get_stmt_da()->message()); thd->clear_error(); DBUG_RETURN(0); } @@ -6394,8 +6400,9 @@ static int get_schema_key_column_usage_record(THD *thd, if (res) { if (thd->is_error()) - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, - thd->stmt_da->sql_errno(), thd->stmt_da->message()); + push_warning(thd, Sql_condition::WARN_LEVEL_WARN, + thd->get_stmt_da()->sql_errno(), + thd->get_stmt_da()->message()); thd->clear_error(); DBUG_RETURN(0); } @@ -6609,7 +6616,7 @@ static void store_schema_partitions_record(THD *thd, TABLE *schema_table, strlen(part_elem->tablespace_name), cs); else { - char *ts= showing_table->file->get_tablespace_name(thd,0,0); + char *ts= showing_table->s->tablespace; if(ts) table->field[24]->store(ts, strlen(ts), cs); else @@ -6684,8 +6691,9 @@ static int get_schema_partitions_record(THD *thd, TABLE_LIST *tables, if (res) { if (thd->is_error()) - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, - thd->stmt_da->sql_errno(), thd->stmt_da->message()); + push_warning(thd, Sql_condition::WARN_LEVEL_WARN, + thd->get_stmt_da()->sql_errno(), + thd->get_stmt_da()->message()); thd->clear_error(); DBUG_RETURN(0); } @@ -7216,8 +7224,9 @@ get_referential_constraints_record(THD *thd, TABLE_LIST *tables, if (res) { if (thd->is_error()) - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, - thd->stmt_da->sql_errno(), thd->stmt_da->message()); + push_warning(thd, Sql_condition::WARN_LEVEL_WARN, + thd->get_stmt_da()->sql_errno(), + thd->get_stmt_da()->message()); thd->clear_error(); DBUG_RETURN(0); } @@ -7919,41 +7928,32 @@ static bool do_fill_table(THD *thd, // Warning_info, so "useful warnings" get rejected. In order to avoid // that problem we create a Warning_info instance, which is capable of // storing "unlimited" number of warnings. - Warning_info wi(thd->query_id, true); - Warning_info *wi_saved= thd->warning_info; + Diagnostics_area *da= thd->get_stmt_da(); + Warning_info wi_tmp(thd->query_id, true, true); - thd->warning_info= &wi; + da->push_warning_info(&wi_tmp); bool res= table_list->schema_table->fill_table( thd, table_list, join_table->select_cond); - thd->warning_info= wi_saved; + da->pop_warning_info(); // Pass an error if any. - if (thd->stmt_da->is_error()) + if (da->is_error()) { - thd->warning_info->push_warning(thd, - thd->stmt_da->sql_errno(), - thd->stmt_da->get_sqlstate(), - MYSQL_ERROR::WARN_LEVEL_ERROR, - thd->stmt_da->message()); + da->push_warning(thd, + da->sql_errno(), + da->get_sqlstate(), + Sql_condition::WARN_LEVEL_ERROR, + da->message()); } // Pass warnings (if any). // // Filter out warnings with WARN_LEVEL_ERROR level, because they // correspond to the errors which were filtered out in fill_table(). - - - List_iterator_fast<MYSQL_ERROR> it(wi.warn_list()); - MYSQL_ERROR *err; - - while ((err= it++)) - { - if (err->get_level() != MYSQL_ERROR::WARN_LEVEL_ERROR) - thd->warning_info->push_warning(thd, err); - } + da->copy_non_errors_from_wi(thd, &wi_tmp); return res; } diff --git a/sql/sql_signal.cc b/sql/sql_signal.cc index ed4d2c23d53..81e16cec774 100644 --- a/sql/sql_signal.cc +++ b/sql/sql_signal.cc @@ -88,9 +88,10 @@ void Set_signal_information::clear() memset(m_item, 0, sizeof(m_item)); } -void Signal_common::assign_defaults(MYSQL_ERROR *cond, +void Sql_cmd_common_signal::assign_defaults( + Sql_condition *cond, bool set_level_code, - MYSQL_ERROR::enum_warning_level level, + Sql_condition::enum_warning_level level, int sqlcode) { if (set_level_code) @@ -102,7 +103,7 @@ void Signal_common::assign_defaults(MYSQL_ERROR *cond, cond->set_builtin_message_text(ER(sqlcode)); } -void Signal_common::eval_defaults(THD *thd, MYSQL_ERROR *cond) +void Sql_cmd_common_signal::eval_defaults(THD *thd, Sql_condition *cond) { DBUG_ASSERT(cond); @@ -129,19 +130,19 @@ void Signal_common::eval_defaults(THD *thd, MYSQL_ERROR *cond) { /* SQLSTATE class "01": warning. */ assign_defaults(cond, set_defaults, - MYSQL_ERROR::WARN_LEVEL_WARN, ER_SIGNAL_WARN); + Sql_condition::WARN_LEVEL_WARN, ER_SIGNAL_WARN); } else if ((sqlstate[0] == '0') && (sqlstate[1] == '2')) { /* SQLSTATE class "02": not found. */ assign_defaults(cond, set_defaults, - MYSQL_ERROR::WARN_LEVEL_ERROR, ER_SIGNAL_NOT_FOUND); + Sql_condition::WARN_LEVEL_ERROR, ER_SIGNAL_NOT_FOUND); } else { /* other SQLSTATE classes : error. */ assign_defaults(cond, set_defaults, - MYSQL_ERROR::WARN_LEVEL_ERROR, ER_SIGNAL_EXCEPTION); + Sql_condition::WARN_LEVEL_ERROR, ER_SIGNAL_EXCEPTION); } } @@ -256,26 +257,26 @@ static int assign_condition_item(MEM_ROOT *mem_root, const char* name, THD *thd, } -int Signal_common::eval_signal_informations(THD *thd, MYSQL_ERROR *cond) +int Sql_cmd_common_signal::eval_signal_informations(THD *thd, Sql_condition *cond) { struct cond_item_map { enum enum_diag_condition_item_name m_item; - String MYSQL_ERROR::*m_member; + String Sql_condition::*m_member; }; static cond_item_map map[]= { - { DIAG_CLASS_ORIGIN, & MYSQL_ERROR::m_class_origin }, - { DIAG_SUBCLASS_ORIGIN, & MYSQL_ERROR::m_subclass_origin }, - { DIAG_CONSTRAINT_CATALOG, & MYSQL_ERROR::m_constraint_catalog }, - { DIAG_CONSTRAINT_SCHEMA, & MYSQL_ERROR::m_constraint_schema }, - { DIAG_CONSTRAINT_NAME, & MYSQL_ERROR::m_constraint_name }, - { DIAG_CATALOG_NAME, & MYSQL_ERROR::m_catalog_name }, - { DIAG_SCHEMA_NAME, & MYSQL_ERROR::m_schema_name }, - { DIAG_TABLE_NAME, & MYSQL_ERROR::m_table_name }, - { DIAG_COLUMN_NAME, & MYSQL_ERROR::m_column_name }, - { DIAG_CURSOR_NAME, & MYSQL_ERROR::m_cursor_name } + { DIAG_CLASS_ORIGIN, & Sql_condition::m_class_origin }, + { DIAG_SUBCLASS_ORIGIN, & Sql_condition::m_subclass_origin }, + { DIAG_CONSTRAINT_CATALOG, & Sql_condition::m_constraint_catalog }, + { DIAG_CONSTRAINT_SCHEMA, & Sql_condition::m_constraint_schema }, + { DIAG_CONSTRAINT_NAME, & Sql_condition::m_constraint_name }, + { DIAG_CATALOG_NAME, & Sql_condition::m_catalog_name }, + { DIAG_SCHEMA_NAME, & Sql_condition::m_schema_name }, + { DIAG_TABLE_NAME, & Sql_condition::m_table_name }, + { DIAG_COLUMN_NAME, & Sql_condition::m_column_name }, + { DIAG_CURSOR_NAME, & Sql_condition::m_cursor_name } }; Item *set; @@ -288,7 +289,7 @@ int Signal_common::eval_signal_informations(THD *thd, MYSQL_ERROR *cond) String *member; const LEX_STRING *name; - DBUG_ENTER("Signal_common::eval_signal_informations"); + DBUG_ENTER("Sql_cmd_common_signal::eval_signal_informations"); for (i= FIRST_DIAG_SET_PROPERTY; i <= LAST_DIAG_SET_PROPERTY; @@ -360,7 +361,7 @@ int Signal_common::eval_signal_informations(THD *thd, MYSQL_ERROR *cond) /* See the comments - "Design notes about MYSQL_ERROR::m_message_text." + "Design notes about Sql_condition::m_message_text." in file sql_error.cc */ String converted_text; @@ -413,23 +414,23 @@ end: DBUG_RETURN(result); } -bool Signal_common::raise_condition(THD *thd, MYSQL_ERROR *cond) +bool Sql_cmd_common_signal::raise_condition(THD *thd, Sql_condition *cond) { bool result= TRUE; - DBUG_ENTER("Signal_common::raise_condition"); + DBUG_ENTER("Sql_cmd_common_signal::raise_condition"); - DBUG_ASSERT(m_lex->query_tables == NULL); + DBUG_ASSERT(thd->lex->query_tables == NULL); eval_defaults(thd, cond); if (eval_signal_informations(thd, cond)) DBUG_RETURN(result); /* SIGNAL should not signal WARN_LEVEL_NOTE */ - DBUG_ASSERT((cond->m_level == MYSQL_ERROR::WARN_LEVEL_WARN) || - (cond->m_level == MYSQL_ERROR::WARN_LEVEL_ERROR)); + DBUG_ASSERT((cond->m_level == Sql_condition::WARN_LEVEL_WARN) || + (cond->m_level == Sql_condition::WARN_LEVEL_ERROR)); - MYSQL_ERROR *raised= NULL; + Sql_condition *raised= NULL; raised= thd->raise_condition(cond->get_sql_errno(), cond->get_sqlstate(), cond->get_level(), @@ -437,7 +438,7 @@ bool Signal_common::raise_condition(THD *thd, MYSQL_ERROR *cond) if (raised) raised->copy_opt_attributes(cond); - if (cond->m_level == MYSQL_ERROR::WARN_LEVEL_WARN) + if (cond->m_level == Sql_condition::WARN_LEVEL_WARN) { my_ok(thd); result= FALSE; @@ -446,12 +447,12 @@ bool Signal_common::raise_condition(THD *thd, MYSQL_ERROR *cond) DBUG_RETURN(result); } -bool Signal_statement::execute(THD *thd) +bool Sql_cmd_signal::execute(THD *thd) { bool result= TRUE; - MYSQL_ERROR cond(thd->mem_root); + Sql_condition cond(thd->mem_root); - DBUG_ENTER("Signal_statement::execute"); + DBUG_ENTER("Sql_cmd_signal::execute"); /* WL#2110 SIGNAL specification says: @@ -465,9 +466,9 @@ bool Signal_statement::execute(THD *thd) This has roots in the SQL standard specification for SIGNAL. */ - thd->stmt_da->reset_diagnostics_area(); + thd->get_stmt_da()->reset_diagnostics_area(); thd->set_row_count_func(0); - thd->warning_info->clear_warning_info(thd->query_id); + thd->get_stmt_da()->clear_warning_info(thd->query_id); result= raise_condition(thd, &cond); @@ -475,14 +476,27 @@ bool Signal_statement::execute(THD *thd) } -bool Resignal_statement::execute(THD *thd) +/** + Execute RESIGNAL SQL-statement. + + @param thd Thread context. + + @return Error status + @retval true in case of error + @retval false on success +*/ + +bool Sql_cmd_resignal::execute(THD *thd) { Sql_condition_info *signaled; + Diagnostics_area *da= thd->get_stmt_da(); int result= TRUE; DBUG_ENTER("Resignal_statement::execute"); - thd->warning_info->m_warn_id= thd->query_id; + // This is a way to force sql_conditions from the current Warning_info to be + // passed to the caller's Warning_info. + da->set_warning_info_id(thd->query_id); if (! thd->spcont || ! (signaled= thd->spcont->raised_condition())) { @@ -490,7 +504,7 @@ bool Resignal_statement::execute(THD *thd) DBUG_RETURN(result); } - MYSQL_ERROR signaled_err(thd->mem_root); + Sql_condition signaled_err(thd->mem_root); signaled_err.set(signaled->m_sql_errno, signaled->m_sql_state, signaled->m_level, @@ -507,5 +521,6 @@ bool Resignal_statement::execute(THD *thd) result= raise_condition(thd, &signaled_err); DBUG_RETURN(result); + } diff --git a/sql/sql_signal.h b/sql/sql_signal.h index 058457a3639..6c77d4cb0bb 100644 --- a/sql/sql_signal.h +++ b/sql/sql_signal.h @@ -18,27 +18,25 @@ #define SQL_SIGNAL_H /** - Signal_common represents the common properties of the SIGNAL and RESIGNAL - statements. + Sql_cmd_common_signal represents the common properties of the + SIGNAL and RESIGNAL statements. */ -class Signal_common : public Sql_statement +class Sql_cmd_common_signal : public Sql_cmd { protected: /** Constructor. - @param lex the LEX structure for this statement. @param cond the condition signaled if any, or NULL. @param set collection of signal condition item assignments. */ - Signal_common(LEX *lex, - const sp_cond_type_t *cond, - const Set_signal_information& set) - : Sql_statement(lex), + Sql_cmd_common_signal(const sp_cond_type *cond, + const Set_signal_information& set) + : Sql_cmd(), m_cond(cond), m_set_signal_information(set) {} - virtual ~Signal_common() + virtual ~Sql_cmd_common_signal() {} /** @@ -49,9 +47,9 @@ protected: @param level the level to assign @param sqlcode the sql code to assign */ - static void assign_defaults(MYSQL_ERROR *cond, + static void assign_defaults(Sql_condition *cond, bool set_level_code, - MYSQL_ERROR::enum_warning_level level, + Sql_condition::enum_warning_level level, int sqlcode); /** @@ -60,7 +58,7 @@ protected: @param thd the current thread. @param cond the condition to update. */ - void eval_defaults(THD *thd, MYSQL_ERROR *cond); + void eval_defaults(THD *thd, Sql_condition *cond); /** Evaluate each signal condition items for this statement. @@ -68,7 +66,7 @@ protected: @param cond the condition to update. @return 0 on success. */ - int eval_signal_informations(THD *thd, MYSQL_ERROR *cond); + int eval_signal_informations(THD *thd, Sql_condition *cond); /** Raise a SQL condition. @@ -76,13 +74,13 @@ protected: @param cond the condition to raise. @return false on success. */ - bool raise_condition(THD *thd, MYSQL_ERROR *cond); + bool raise_condition(THD *thd, Sql_condition *cond); /** The condition to signal or resignal. This member is optional and can be NULL (RESIGNAL). */ - const sp_cond_type_t *m_cond; + const sp_cond_type *m_cond; /** Collection of 'SET item = value' assignments in the @@ -92,60 +90,56 @@ protected: }; /** - Signal_statement represents a SIGNAL statement. + Sql_cmd_signal represents a SIGNAL statement. */ -class Signal_statement : public Signal_common +class Sql_cmd_signal : public Sql_cmd_common_signal { public: /** Constructor, used to represent a SIGNAL statement. - @param lex the LEX structure for this statement. @param cond the SQL condition to signal (required). @param set the collection of signal informations to signal. */ - Signal_statement(LEX *lex, - const sp_cond_type_t *cond, - const Set_signal_information& set) - : Signal_common(lex, cond, set) + Sql_cmd_signal(const sp_cond_type *cond, + const Set_signal_information& set) + : Sql_cmd_common_signal(cond, set) {} - virtual ~Signal_statement() + virtual ~Sql_cmd_signal() {} - /** - Execute a SIGNAL statement at runtime. - @param thd the current thread. - @return false on success. - */ + virtual enum_sql_command sql_command_code() const + { + return SQLCOM_SIGNAL; + } + virtual bool execute(THD *thd); }; /** - Resignal_statement represents a RESIGNAL statement. + Sql_cmd_resignal represents a RESIGNAL statement. */ -class Resignal_statement : public Signal_common +class Sql_cmd_resignal : public Sql_cmd_common_signal { public: /** Constructor, used to represent a RESIGNAL statement. - @param lex the LEX structure for this statement. @param cond the SQL condition to resignal (optional, may be NULL). @param set the collection of signal informations to resignal. */ - Resignal_statement(LEX *lex, - const sp_cond_type_t *cond, - const Set_signal_information& set) - : Signal_common(lex, cond, set) + Sql_cmd_resignal(const sp_cond_type *cond, + const Set_signal_information& set) + : Sql_cmd_common_signal(cond, set) {} - virtual ~Resignal_statement() + virtual ~Sql_cmd_resignal() {} - /** - Execute a RESIGNAL statement at runtime. - @param thd the current thread. - @return 0 on success. - */ + virtual enum_sql_command sql_command_code() const + { + return SQLCOM_RESIGNAL; + } + virtual bool execute(THD *thd); }; diff --git a/sql/sql_statistics.cc b/sql/sql_statistics.cc index 553146b1f7f..14efb0265cc 100644 --- a/sql/sql_statistics.cc +++ b/sql/sql_statistics.cc @@ -1388,7 +1388,7 @@ public: is_single_comp_pk= FALSE; uint pk= table->s->primary_key; if ((uint) (table->key_info - key_info) == pk && - table->key_info[pk].key_parts == 1) + table->key_info[pk].user_defined_key_parts == 1) { prefixes= 1; is_single_comp_pk= TRUE; @@ -1926,7 +1926,7 @@ void Column_statistics_collected::init(THD *thd, Field *table_field) is_single_pk_col= FALSE; - if (pk != MAX_KEY && table->key_info[pk].key_parts == 1 && + if (pk != MAX_KEY && table->key_info[pk].user_defined_key_parts == 1 && table->key_info[pk].key_part[0].fieldnr == table_field->field_index + 1) is_single_pk_col= TRUE; diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 1c064d612ba..962bc715764 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -33,6 +33,7 @@ #include "sql_partition.h" // mem_alloc_error, // generate_partition_syntax, // partition_info + // NOT_A_PARTITION_ID #include "sql_db.h" // load_db_opt_by_name #include "sql_time.h" // make_truncated_value_warning #include "records.h" // init_read_record, end_read_record @@ -64,16 +65,22 @@ const char *primary_key_name="PRIMARY"; static bool check_if_keyname_exists(const char *name,KEY *start, KEY *end); static char *make_unique_key_name(const char *field_name,KEY *start,KEY *end); -static int copy_data_between_tables(THD *thd, TABLE *,TABLE *, - List<Create_field> &, bool, - uint, ORDER *, ha_rows *,ha_rows *, - enum enum_enable_or_disable, bool); +static int copy_data_between_tables(THD *thd, TABLE *from,TABLE *to, + List<Create_field> &create, bool ignore, + uint order_num, ORDER *order, + ha_rows *copied,ha_rows *deleted, + Alter_info::enum_enable_or_disable keys_onoff, + Alter_table_ctx *alter_ctx); static bool prepare_blob_field(THD *thd, Create_field *sql_field); static bool check_engine(THD *, const char *, const char *, HA_CREATE_INFO *); -static int mysql_prepare_create_table(THD *, HA_CREATE_INFO *, Alter_info *, - bool, uint *, handler *, KEY **, uint *, - int); +static int +mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, + Alter_info *alter_info, + bool tmp_table, + uint *db_options, + handler *file, KEY **key_info_buffer, + uint *key_count, int create_table_mode); /** @brief Helper function for explain_filename @@ -582,22 +589,19 @@ uint build_table_filename(char *buff, size_t bufflen, const char *db, } -/* - Creates path to a file: mysql_tmpdir/#sql1234_12_1.ext +/** + Create path to a temporary table mysql_tmpdir/#sql1234_12_1 + (i.e. to its .FRM file but without an extension). - SYNOPSIS - build_tmptable_filename() - thd The thread handle. - buff Where to write result in my_charset_filename. - bufflen buff size - - NOTES + @param thd The thread handle. + @param buff Where to write result in my_charset_filename. + @param bufflen buff size + @note Uses current_pid, thread_id, and tmp_table counter to create a file name in mysql_tmpdir. - RETURN - path length + @return Path length. */ uint build_tmptable_filename(THD* thd, char *buff, size_t bufflen) @@ -646,7 +650,6 @@ uint build_tmptable_filename(THD* thd, char *buff, size_t bufflen) -------------------------------------------------------------------------- */ - struct st_global_ddl_log { /* @@ -702,6 +705,7 @@ static bool read_ddl_log_file_entry(uint entry_no) uint io_size= global_ddl_log.io_size; DBUG_ENTER("read_ddl_log_file_entry"); + mysql_mutex_assert_owner(&LOCK_gdl); if (mysql_file_pread(file_id, file_entry_buf, io_size, io_size * entry_no, MYF(MY_WME)) != io_size) error= TRUE; @@ -723,9 +727,10 @@ static bool write_ddl_log_file_entry(uint entry_no) { bool error= FALSE; File file_id= global_ddl_log.file_id; - char *file_entry_buf= (char*)global_ddl_log.file_entry_buf; + uchar *file_entry_buf= (uchar*)global_ddl_log.file_entry_buf; DBUG_ENTER("write_ddl_log_file_entry"); + mysql_mutex_assert_owner(&LOCK_gdl); if (mysql_file_pwrite(file_id, (uchar*)file_entry_buf, IO_SIZE, IO_SIZE * entry_no, MYF(MY_WME)) != IO_SIZE) error= TRUE; @@ -733,19 +738,33 @@ static bool write_ddl_log_file_entry(uint entry_no) } -/* - Write ddl log header - SYNOPSIS - write_ddl_log_header() - RETURN VALUES - TRUE Error - FALSE Success +/** + Sync the ddl log file. + + @return Operation status + @retval FALSE Success + @retval TRUE Error +*/ + + +static bool sync_ddl_log_file() +{ + DBUG_ENTER("sync_ddl_log_file"); + DBUG_RETURN(mysql_file_sync(global_ddl_log.file_id, MYF(MY_WME))); +} + + +/** + Write ddl log header. + + @return Operation status + @retval TRUE Error + @retval FALSE Success */ static bool write_ddl_log_header() { uint16 const_var; - bool error= FALSE; DBUG_ENTER("write_ddl_log_header"); int4store(&global_ddl_log.file_entry_buf[DDL_LOG_NUM_ENTRY_POS], @@ -761,8 +780,7 @@ static bool write_ddl_log_header() sql_print_error("Error writing ddl log header"); DBUG_RETURN(TRUE); } - (void) sync_ddl_log(); - DBUG_RETURN(error); + DBUG_RETURN(sync_ddl_log_file()); } @@ -796,7 +814,7 @@ static inline void create_ddl_log_file_name(char *file_name) static uint read_ddl_log_header() { - char *file_entry_buf= (char*)global_ddl_log.file_entry_buf; + uchar *file_entry_buf= (uchar*)global_ddl_log.file_entry_buf; char file_name[FN_REFLEN]; uint entry_no; bool successful_open= FALSE; @@ -923,6 +941,108 @@ end: } +/** + Sync ddl log file. + + @return Operation status + @retval TRUE Error + @retval FALSE Success +*/ + +static bool sync_ddl_log_no_lock() +{ + DBUG_ENTER("sync_ddl_log_no_lock"); + + mysql_mutex_assert_owner(&LOCK_gdl); + if ((!global_ddl_log.recovery_phase) && + init_ddl_log()) + { + DBUG_RETURN(TRUE); + } + DBUG_RETURN(sync_ddl_log_file()); +} + + +/** + @brief Deactivate an individual entry. + + @details For complex rename operations we need to deactivate individual + entries. + + During replace operations where we start with an existing table called + t1 and a replacement table called t1#temp or something else and where + we want to delete t1 and rename t1#temp to t1 this is not possible to + do in a safe manner unless the ddl log is informed of the phases in + the change. + + Delete actions are 1-phase actions that can be ignored immediately after + being executed. + Rename actions from x to y is also a 1-phase action since there is no + interaction with any other handlers named x and y. + Replace action where drop y and x -> y happens needs to be a two-phase + action. Thus the first phase will drop y and the second phase will + rename x -> y. + + @param entry_no Entry position of record to change + + @return Operation status + @retval TRUE Error + @retval FALSE Success +*/ + +static bool deactivate_ddl_log_entry_no_lock(uint entry_no) +{ + uchar *file_entry_buf= (uchar*)global_ddl_log.file_entry_buf; + DBUG_ENTER("deactivate_ddl_log_entry_no_lock"); + + mysql_mutex_assert_owner(&LOCK_gdl); + if (!read_ddl_log_file_entry(entry_no)) + { + if (file_entry_buf[DDL_LOG_ENTRY_TYPE_POS] == DDL_LOG_ENTRY_CODE) + { + /* + Log entry, if complete mark it done (IGNORE). + Otherwise increase the phase by one. + */ + if (file_entry_buf[DDL_LOG_ACTION_TYPE_POS] == DDL_LOG_DELETE_ACTION || + file_entry_buf[DDL_LOG_ACTION_TYPE_POS] == DDL_LOG_RENAME_ACTION || + (file_entry_buf[DDL_LOG_ACTION_TYPE_POS] == DDL_LOG_REPLACE_ACTION && + file_entry_buf[DDL_LOG_PHASE_POS] == 1) || + (file_entry_buf[DDL_LOG_ACTION_TYPE_POS] == DDL_LOG_EXCHANGE_ACTION && + file_entry_buf[DDL_LOG_PHASE_POS] >= EXCH_PHASE_TEMP_TO_FROM)) + file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]= DDL_IGNORE_LOG_ENTRY_CODE; + else if (file_entry_buf[DDL_LOG_ACTION_TYPE_POS] == DDL_LOG_REPLACE_ACTION) + { + DBUG_ASSERT(file_entry_buf[DDL_LOG_PHASE_POS] == 0); + file_entry_buf[DDL_LOG_PHASE_POS]= 1; + } + else if (file_entry_buf[DDL_LOG_ACTION_TYPE_POS] == DDL_LOG_EXCHANGE_ACTION) + { + DBUG_ASSERT(file_entry_buf[DDL_LOG_PHASE_POS] <= + EXCH_PHASE_FROM_TO_NAME); + file_entry_buf[DDL_LOG_PHASE_POS]++; + } + else + { + DBUG_ASSERT(0); + } + if (write_ddl_log_file_entry(entry_no)) + { + sql_print_error("Error in deactivating log entry. Position = %u", + entry_no); + DBUG_RETURN(TRUE); + } + } + } + else + { + sql_print_error("Failed in reading entry before deactivating it"); + DBUG_RETURN(TRUE); + } + DBUG_RETURN(FALSE); +} + + /* Execute one action in a ddl log entry SYNOPSIS @@ -953,12 +1073,14 @@ static int execute_ddl_log_action(THD *thd, DDL_LOG_ENTRY *ddl_log_entry) DBUG_RETURN(FALSE); } DBUG_PRINT("ddl_log", - ("execute type %c next %u name '%s' from_name '%s' handler '%s'", + ("execute type %c next %u name '%s' from_name '%s' handler '%s'" + " tmp_name '%s'", ddl_log_entry->action_type, ddl_log_entry->next_entry, ddl_log_entry->name, ddl_log_entry->from_name, - ddl_log_entry->handler_name)); + ddl_log_entry->handler_name, + ddl_log_entry->tmp_name)); handler_name.str= (char*)ddl_log_entry->handler_name; handler_name.length= strlen(ddl_log_entry->handler_name); init_sql_alloc(&mem_root, TABLE_ALLOC_BLOCK_SIZE, 0, MYF(MY_THREAD_SPECIFIC)); @@ -1008,7 +1130,7 @@ static int execute_ddl_log_action(THD *thd, DDL_LOG_ENTRY *ddl_log_entry) break; } } - if ((deactivate_ddl_log_entry(ddl_log_entry->entry_pos))) + if ((deactivate_ddl_log_entry_no_lock(ddl_log_entry->entry_pos))) break; (void) sync_ddl_log(); error= FALSE; @@ -1049,6 +1171,58 @@ static int execute_ddl_log_action(THD *thd, DDL_LOG_ENTRY *ddl_log_entry) error= FALSE; break; } + case DDL_LOG_EXCHANGE_ACTION: + { + /* We hold LOCK_gdl, so we can alter global_ddl_log.file_entry_buf */ + char *file_entry_buf= (char*)&global_ddl_log.file_entry_buf; + /* not yet implemented for frm */ + DBUG_ASSERT(!frm_action); + /* + Using a case-switch here to revert all currently done phases, + since it will fall through until the first phase is undone. + */ + switch (ddl_log_entry->phase) { + case EXCH_PHASE_TEMP_TO_FROM: + /* tmp_name -> from_name possibly done */ + (void) file->ha_rename_table(ddl_log_entry->from_name, + ddl_log_entry->tmp_name); + /* decrease the phase and sync */ + file_entry_buf[DDL_LOG_PHASE_POS]--; + if (write_ddl_log_file_entry(ddl_log_entry->entry_pos)) + break; + if (sync_ddl_log_no_lock()) + break; + /* fall through */ + case EXCH_PHASE_FROM_TO_NAME: + /* from_name -> name possibly done */ + (void) file->ha_rename_table(ddl_log_entry->name, + ddl_log_entry->from_name); + /* decrease the phase and sync */ + file_entry_buf[DDL_LOG_PHASE_POS]--; + if (write_ddl_log_file_entry(ddl_log_entry->entry_pos)) + break; + if (sync_ddl_log_no_lock()) + break; + /* fall through */ + case EXCH_PHASE_NAME_TO_TEMP: + /* name -> tmp_name possibly done */ + (void) file->ha_rename_table(ddl_log_entry->tmp_name, + ddl_log_entry->name); + /* disable the entry and sync */ + file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]= DDL_IGNORE_LOG_ENTRY_CODE; + if (write_ddl_log_file_entry(ddl_log_entry->entry_pos)) + break; + if (sync_ddl_log_no_lock()) + break; + error= FALSE; + break; + default: + DBUG_ASSERT(0); + break; + } + + break; + } default: DBUG_ASSERT(0); break; @@ -1656,15 +1830,12 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags) strxmov(shadow_frm_name, shadow_path, reg_ext, NullS); if (flags & WFRM_WRITE_SHADOW) { - if (mysql_prepare_create_table(lpt->thd, lpt->create_info, - lpt->alter_info, + if (mysql_prepare_create_table(lpt->thd, lpt->create_info, lpt->alter_info, /*tmp_table*/ 1, - &lpt->db_options, - lpt->table->file, - &lpt->key_info_buffer, - &lpt->key_count, - /*select_field_count*/ 0)) - { + &lpt->db_options, lpt->table->file, + &lpt->key_info_buffer, &lpt->key_count, + C_ALTER_TABLE)) + { DBUG_RETURN(TRUE); } #ifdef WITH_PARTITION_STORAGE_ENGINE @@ -2213,7 +2384,7 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, goto err; } close_all_tables_for_name(thd, table->table->s, - HA_EXTRA_PREPARE_FOR_DROP); + HA_EXTRA_PREPARE_FOR_DROP, NULL); table->table= 0; } @@ -2277,7 +2448,7 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, . ./sql/datadict.cc +32 /Alfranio - TODO: We need to test this. */ if (if_exists) - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_BAD_TABLE_ERROR, ER(ER_BAD_TABLE_ERROR), table->table_name); else @@ -2446,22 +2617,20 @@ end: } -/* +/** Quickly remove a table. - SYNOPSIS - quick_rm_table() - base The handlerton handle. - db The database name. - table_name The table name. - flags flags for build_table_filename(). + @param thd Thread context. + @param base The handlerton handle. + @param db The database name. + @param table_name The table name. + @param flags Flags for build_table_filename() as well as describing + if handler files / .FRM should be deleted as well. - RETURN - 0 OK - != 0 Error + @return False in case of success, True otherwise. */ -bool quick_rm_table(handlerton *base,const char *db, +bool quick_rm_table(THD *thd, handlerton *base, const char *db, const char *table_name, uint flags) { char path[FN_REFLEN + 1]; @@ -2473,11 +2642,20 @@ bool quick_rm_table(handlerton *base,const char *db, if (mysql_file_delete(key_file_frm, path, MYF(0))) error= 1; /* purecov: inspected */ path[path_length - reg_ext_length]= '\0'; // Remove reg_ext - if (!(flags & FRM_ONLY)) + if (flags & NO_HA_TABLE) + { + handler *file= get_new_handler((TABLE_SHARE*) 0, thd->mem_root, base); + if (!file) + DBUG_RETURN(true); + (void) file->ha_create_handler_files(path, NULL, CHF_DELETE_FLAG, NULL); + delete file; + } + if (!(flags & (FRM_ONLY|NO_HA_TABLE))) error|= ha_delete_table(current_thd, base, path, db, table_name, 0); DBUG_RETURN(error); } + /* Sort keys in the following order: - PRIMARY KEY @@ -2571,7 +2749,7 @@ bool check_duplicates_in_interval(const char *set_or_name, name, err.ptr(), set_or_name); return 1; } - push_warning_printf(thd,MYSQL_ERROR::WARN_LEVEL_NOTE, + push_warning_printf(thd,Sql_condition::WARN_LEVEL_NOTE, ER_DUPLICATED_VALUE_IN_TYPE, ER(ER_DUPLICATED_VALUE_IN_TYPE), name, err.ptr(), set_or_name); @@ -2813,21 +2991,6 @@ CHARSET_INFO* get_sql_field_charset(Create_field *sql_field, } -bool check_duplicate_warning(THD *thd, char *msg, ulong length) -{ - List_iterator_fast<MYSQL_ERROR> it(thd->warning_info->warn_list()); - MYSQL_ERROR *err; - while ((err= it++)) - { - if (strncmp(msg, err->get_message_text(), length) == 0) - { - return true; - } - } - return false; -} - - /** Modifies the first column definition whose SQL type is TIMESTAMP by adding the features DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP. @@ -2894,7 +3057,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, bool tmp_table, uint *db_options, handler *file, KEY **key_info_buffer, - uint *key_count, int select_field_count) + uint *key_count, int create_table_mode) { const char *key_name; Create_field *sql_field,*dup_field; @@ -2907,6 +3070,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, List_iterator<Create_field> it(alter_info->create_list); List_iterator<Create_field> it2(alter_info->create_list); uint total_uneven_bit_length= 0; + int select_field_count= C_CREATE_SELECT(create_table_mode); DBUG_ENTER("mysql_prepare_create_table"); select_field_pos= alter_info->create_list.elements - select_field_count; @@ -2924,6 +3088,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, executing a prepared statement for the second time. */ sql_field->length= sql_field->char_length; + /* Set field charset. */ save_cs= sql_field->charset= get_sql_field_charset(sql_field, create_info); if ((sql_field->flags & BINCMP_FLAG) && @@ -3626,7 +3791,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, char warn_buff[MYSQL_ERRMSG_SIZE]; my_snprintf(warn_buff, sizeof(warn_buff), ER(ER_TOO_LONG_KEY), length); - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning(thd, Sql_condition::WARN_LEVEL_WARN, ER_TOO_LONG_KEY, warn_buff); /* Align key length to multibyte char boundary */ length-= length % sql_field->charset->mbmaxlen; @@ -3673,7 +3838,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, char warn_buff[MYSQL_ERRMSG_SIZE]; my_snprintf(warn_buff, sizeof(warn_buff), ER(ER_TOO_LONG_KEY), length); - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning(thd, Sql_condition::WARN_LEVEL_WARN, ER_TOO_LONG_KEY, warn_buff); /* Align key length to multibyte char boundary */ length-= length % sql_field->charset->mbmaxlen; @@ -3763,8 +3928,8 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, my_snprintf(warn_buff, sizeof(warn_buff), ER(ER_TOO_LONG_INDEX_COMMENT), key_info->name, static_cast<ulong>(INDEX_COMMENT_MAXLEN)); /* do not push duplicate warnings */ - if (!check_duplicate_warning(thd, warn_buff, strlen(warn_buff))) - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + if (!thd->get_stmt_da()->has_sql_condition(warn_buff, strlen(warn_buff))) + push_warning(thd, Sql_condition::WARN_LEVEL_WARN, ER_TOO_LONG_INDEX_COMMENT, warn_buff); key->key_create_info.comment.length= tmp_len; @@ -3903,7 +4068,7 @@ static bool prepare_blob_field(THD *thd, Create_field *sql_field) my_snprintf(warn_buff, sizeof(warn_buff), ER(ER_AUTO_CONVERT), sql_field->field_name, (sql_field->charset == &my_charset_bin) ? "VARBINARY" : "VARCHAR", (sql_field->charset == &my_charset_bin) ? "BLOB" : "TEXT"); - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, ER_AUTO_CONVERT, + push_warning(thd, Sql_condition::WARN_LEVEL_NOTE, ER_AUTO_CONVERT, warn_buff); } @@ -3974,47 +4139,6 @@ void sp_prepare_create_field(THD *thd, Create_field *sql_field) } -#ifdef WITH_PARTITION_STORAGE_ENGINE -/** - Auxiliary function which allows to check if freshly created .FRM - file for table can be opened. - - @retval FALSE - Success. - @retval TRUE - Failure. -*/ - -static bool check_if_created_table_can_be_opened(THD *thd, - const char *path, - const char *db, - const char *table_name, - HA_CREATE_INFO *create_info, - handler *file) -{ - TABLE table; - TABLE_SHARE share; - bool result; - - /* - It is impossible to open definition of partitioned table without .par file. - */ - if (file->ha_create_handler_files(path, NULL, CHF_CREATE_FLAG, create_info)) - return TRUE; - - init_tmp_table_share(thd, &share, db, 0, table_name, path); - - result= (open_table_def(thd, &share, 0) || - open_table_from_share(thd, &share, "", 0, (uint) READ_ALL, - 0, &table, TRUE)); - if (! result) - (void) closefrm(&table, 0); - - free_table_share(&share); - (void) file->ha_create_handler_files(path, NULL, CHF_DELETE_FLAG, create_info); - return result; -} -#endif - - /** Check that there is no frm file for given table @@ -4077,57 +4201,59 @@ bool check_table_file_presence(char *old_path, } -/* +/** Create a table - SYNOPSIS - mysql_create_table_no_lock() - thd Thread object - db Database - table_name Table name - create_info Create information (like MAX_ROWS) - fields List of fields to create - keys List of keys to create - internal_tmp_table Set to 1 if this is an internal temporary table - (From ALTER TABLE) - select_field_count - is_trans identifies the type of engine where the table - was created: either trans or non-trans. - - DESCRIPTION - If one creates a temporary table, this is automatically opened - - Note that this function assumes that caller already have taken - exclusive metadata lock on table being created or used some other - way to ensure that concurrent operations won't intervene. - mysql_create_table() is a wrapper that can be used for this. - - no_log is needed for the case of CREATE ... SELECT, - as the logging will be done later in sql_insert.cc - select_field_count is also used for CREATE ... SELECT, - and must be zero for standard create of table. - - RETURN VALUES - FALSE OK - TRUE error + @param thd Thread object + @param db Database + @param table_name Table name + @param path Path to table (i.e. to its .FRM file without + the extension). + @param create_info Create information (like MAX_ROWS) + @param alter_info Description of fields and keys for new table + @param internal_tmp_table Set to true if this is an internal temporary table + (From ALTER TABLE) + @param select_field_count Number of fields coming from SELECT part of + CREATE TABLE ... SELECT statement. Must be zero + for standard create of table. + @param no_ha_table Indicates that only .FRM file (and PAR file if table + is partitioned) needs to be created and not a table + in the storage engine. + @param[out] is_trans Identifies the type of engine where the table + was created: either trans or non-trans. + @param[out] key_info Array of KEY objects describing keys in table + which was created. + @param[out] key_count Number of keys in table which was created. + + If one creates a temporary table, this is automatically opened + + Note that this function assumes that caller already have taken + exclusive metadata lock on table being created or used some other + way to ensure that concurrent operations won't intervene. + mysql_create_table() is a wrapper that can be used for this. + + @retval false OK + @retval true error */ -bool mysql_create_table_no_lock(THD *thd, - const char *db, const char *table_name, - HA_CREATE_INFO *create_info, - Alter_info *alter_info, - bool internal_tmp_table, - uint select_field_count, - bool *is_trans) +static +bool create_table_impl(THD *thd, + const char *db, const char *table_name, + const char *path, + HA_CREATE_INFO *create_info, + Alter_info *alter_info, + bool internal_tmp_table, + bool no_ha_table, + bool *is_trans, + KEY **key_info, + uint *key_count, + int create_table_mode) { - char path[FN_REFLEN + 1]; - uint path_length; const char *alias; - uint db_options, key_count; - KEY *key_info_buffer; + uint db_options; handler *file; bool error= TRUE; - DBUG_ENTER("mysql_create_table_no_lock"); + DBUG_ENTER("create_table_impl"); DBUG_PRINT("enter", ("db: '%s' table: '%s' tmp: %d", db, table_name, internal_tmp_table)); @@ -4145,10 +4271,8 @@ bool mysql_create_table_no_lock(THD *thd, set_table_default_charset(thd, create_info, (char*) db); db_options= create_info->table_options; - if (!create_info->frm_only && - create_info->row_type != ROW_TYPE_FIXED && - create_info->row_type != ROW_TYPE_DEFAULT) - db_options|= HA_OPTION_PACK_RECORD; + if (create_info->row_type == ROW_TYPE_DYNAMIC) + db_options|=HA_OPTION_PACK_RECORD; alias= table_case_name(create_info, table_name); if (!(file= get_new_handler((TABLE_SHARE*) 0, thd->mem_root, create_info->db_type))) @@ -4186,11 +4310,7 @@ bool mysql_create_table_no_lock(THD *thd, partitions also in the call to check_partition_info. We transport this information in the default_db_type variable, it is either DB_TYPE_DEFAULT or the engine set in the ALTER TABLE command. - - Check that we don't use foreign keys in the table since it won't - work even with InnoDB beneath it. */ - List_iterator<Key> key_iterator(alter_info->key_list); Key *key; handlerton *part_engine_type= create_info->db_type; char *part_syntax_buf; @@ -4201,15 +4321,6 @@ bool mysql_create_table_no_lock(THD *thd, my_error(ER_PARTITION_NO_TEMPORARY, MYF(0)); goto err; } - while ((key= key_iterator++)) - { - if (key->type == Key::FOREIGN_KEY && - !part_info->is_auto_partitioned) - { - my_error(ER_FOREIGN_KEY_ON_PARTITIONED, MYF(0)); - goto err; - } - } if ((part_engine_type == partition_hton) && part_info->default_engine_type) { @@ -4315,58 +4426,64 @@ bool mysql_create_table_no_lock(THD *thd, DBUG_RETURN(TRUE); } } + /* + Unless table's storage engine supports partitioning natively + don't allow foreign keys on partitioned tables (they won't + work work even with InnoDB beneath of partitioning engine). + If storage engine handles partitioning natively (like NDB) + foreign keys support is possible, so we let the engine decide. + */ + if (create_info->db_type == partition_hton) + { + List_iterator_fast<Key> key_iterator(alter_info->key_list); + while ((key= key_iterator++)) + { + if (key->type == Key::FOREIGN_KEY) + { + my_error(ER_FOREIGN_KEY_ON_PARTITIONED, MYF(0)); + goto err; + } + } + } } #endif if (mysql_prepare_create_table(thd, create_info, alter_info, internal_tmp_table, &db_options, file, - &key_info_buffer, &key_count, - select_field_count)) + key_info, key_count, + create_table_mode)) goto err; - /* Check if table exists */ if (create_info->options & HA_LEX_CREATE_TMP_TABLE) - { - path_length= build_tmptable_filename(thd, path, sizeof(path)); create_info->table_options|=HA_CREATE_DELAY_KEY_WRITE; - } - else - { - path_length= build_table_filename(path, sizeof(path) - 1, db, alias, reg_ext, - internal_tmp_table ? FN_IS_TMP : 0); - } /* Check if table already exists */ if ((create_info->options & HA_LEX_CREATE_TMP_TABLE) && find_temporary_table(thd, db, table_name)) { if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS) - goto warn; + { + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, + ER_TABLE_EXISTS_ERROR, ER(ER_TABLE_EXISTS_ERROR), + alias); + error= 0; + goto err; + } my_error(ER_TABLE_EXISTS_ERROR, MYF(0), alias); goto err; } - /* Give warnings for not supported table options */ -#if defined(WITH_ARIA_STORAGE_ENGINE) - extern handlerton *maria_hton; - if (file->ht != maria_hton) -#endif - if (create_info->transactional) - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, - ER_ILLEGAL_HA_CREATE_OPTION, - ER(ER_ILLEGAL_HA_CREATE_OPTION), - file->engine_name()->str, - "TRANSACTIONAL=1"); - if (!internal_tmp_table && !(create_info->options & HA_LEX_CREATE_TMP_TABLE)) { - if (check_table_file_presence(NULL, path, db, table_name, table_name, - !(create_info->options & - HA_LEX_CREATE_IF_NOT_EXISTS))) + char frm_name[FN_REFLEN+1]; + strxnmov(frm_name, sizeof(frm_name) - 1, path, reg_ext, NullS); + + if (!access(frm_name, F_OK)) { if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS) goto warn; + my_error(ER_TABLE_EXISTS_ERROR,MYF(0),table_name); goto err; } /* @@ -4414,6 +4531,7 @@ bool mysql_create_table_no_lock(THD *thd, goto warn; my_error(ER_TABLE_EXISTS_ERROR,MYF(0),table_name); goto err; + break; default: DBUG_PRINT("info", ("error: %u from storage engine", retcode)); my_error(retcode, MYF(0),table_name); @@ -4423,7 +4541,6 @@ bool mysql_create_table_no_lock(THD *thd, THD_STAGE_INFO(thd, stage_creating_table); -#ifdef HAVE_READLINK { size_t dirlen; char dirpath[FN_REFLEN]; @@ -4470,35 +4587,37 @@ bool mysql_create_table_no_lock(THD *thd, } #endif /* WITH_PARTITION_STORAGE_ENGINE */ - if (!my_use_symdir || (thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE)) -#endif /* HAVE_READLINK */ + if (thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE) { if (create_info->data_file_name) - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, WARN_OPTION_IGNORED, ER(WARN_OPTION_IGNORED), "DATA DIRECTORY"); if (create_info->index_file_name) - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, WARN_OPTION_IGNORED, ER(WARN_OPTION_IGNORED), "INDEX DIRECTORY"); create_info->data_file_name= create_info->index_file_name= 0; } create_info->table_options=db_options; - path[path_length - reg_ext_length]= '\0'; // Remove .frm extension + /* + Create .FRM (and .PAR file for partitioned table). + If "no_ha_table" is false also create table in storage engine. + */ if (rea_create_table(thd, path, db, table_name, create_info, alter_info->create_list, - key_count, key_info_buffer, file)) + *key_count, *key_info, file, no_ha_table)) goto err; - if (create_info->options & HA_LEX_CREATE_TMP_TABLE) + if (!no_ha_table && create_info->options & HA_LEX_CREATE_TMP_TABLE) { /* Open a table (skipping table cache) and add it into THD::temporary_tables list. */ - TABLE *table= open_table_uncached(thd, path, db, table_name, TRUE); + TABLE *table= open_table_uncached(thd, path, db, table_name, true, true); if (!table) { @@ -4512,7 +4631,7 @@ bool mysql_create_table_no_lock(THD *thd, thd->thread_specific_used= TRUE; } #ifdef WITH_PARTITION_STORAGE_ENGINE - else if (part_info && create_info->frm_only) + else if (part_info && no_ha_table) { /* For partitioned tables we can't find some problems with table @@ -4524,12 +4643,26 @@ bool mysql_create_table_no_lock(THD *thd, In cases when we create .FRM without SE part we have to open table explicitly. */ - if (check_if_created_table_can_be_opened(thd, path, db, table_name, - create_info, file)) + TABLE table; + TABLE_SHARE share; + + init_tmp_table_share(thd, &share, db, 0, table_name, path); + + bool result= (open_table_def(thd, &share, 0) || + open_table_from_share(thd, &share, "", 0, (uint) READ_ALL, + 0, &table, true)); + if (!result) + (void) closefrm(&table, 0); + + free_table_share(&share); + + if (result) { - char frm_name[FN_REFLEN]; - strxmov(frm_name, path, reg_ext, NullS); + char frm_name[FN_REFLEN + 1]; + strxnmov(frm_name, sizeof(frm_name) - 1, path, reg_ext, NullS); (void) mysql_file_delete(key_file_frm, frm_name, MYF(0)); + (void) file->ha_create_handler_files(path, NULL, CHF_DELETE_FLAG, + create_info); goto err; } } @@ -4543,7 +4676,7 @@ err: warn: error= FALSE; - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_TABLE_EXISTS_ERROR, ER(ER_TABLE_EXISTS_ERROR), alias); goto err; @@ -4551,6 +4684,42 @@ warn: /** + Simple wrapper around create_table_impl() to be used + in various version of CREATE TABLE statement. +*/ +bool mysql_create_table_no_lock(THD *thd, + const char *db, const char *table_name, + HA_CREATE_INFO *create_info, + Alter_info *alter_info, bool *is_trans, + int create_table_mode) +{ + KEY *not_used_1; + uint not_used_2; + char path[FN_REFLEN + 1]; + + if (create_info->options & HA_LEX_CREATE_TMP_TABLE) + build_tmptable_filename(thd, path, sizeof(path)); + else + { + int length; + const char *alias= table_case_name(create_info, table_name); + length= build_table_filename(path, sizeof(path) - 1, db, alias, + "", 0); + // Check if we hit FN_REFLEN bytes along with file extension. + if (length+reg_ext_length > FN_REFLEN) + { + my_error(ER_IDENT_CAUSES_TOO_LONG_PATH, MYF(0), sizeof(path)-1, path); + return true; + } + } + + return create_table_impl(thd, db, table_name, path, create_info, alter_info, + false, false, is_trans, + ¬_used_1, ¬_used_2, create_table_mode); +} + + +/** Implementation of SQLCOM_CREATE_TABLE. Take the metadata locks (including a shared lock on the affected @@ -4566,6 +4735,7 @@ bool mysql_create_table(THD *thd, TABLE_LIST *create_table, { bool result; bool is_trans= FALSE; + int create_table_mode; DBUG_ENTER("mysql_create_table"); /* @@ -4581,23 +4751,21 @@ bool mysql_create_table(THD *thd, TABLE_LIST *create_table, /* Got lock. */ DEBUG_SYNC(thd, "locked_table_name"); + if (alter_info->create_list.elements || alter_info->key_list.elements) + create_table_mode= C_ORDINARY_CREATE; + else + create_table_mode= C_ASSISTED_DISCOVERY; + promote_first_timestamp_column(&alter_info->create_list); result= mysql_create_table_no_lock(thd, create_table->db, create_table->table_name, create_info, - alter_info, FALSE, 0, &is_trans); + alter_info, &is_trans, create_table_mode); - /* - Don't write statement if: - - Table creation has failed - - Row-based logging is used and we are creating a temporary table - Otherwise, the statement shall be binlogged. - */ - if (!result && - (!thd->is_current_stmt_binlog_format_row() || - (thd->is_current_stmt_binlog_format_row() && - !(create_info->options & HA_LEX_CREATE_TMP_TABLE)))) - result= write_bin_log(thd, TRUE, thd->query(), thd->query_length(), is_trans); + /* In RBR we don't need to log CREATE TEMPORARY TABLE */ + if (thd->is_current_stmt_binlog_format_row() && create_info->tmp_table()) + DBUG_RETURN(0); + result= write_bin_log(thd, TRUE, thd->query(), thd->query_length(), is_trans); end: DBUG_RETURN(result); } @@ -4647,25 +4815,23 @@ make_unique_key_name(const char *field_name,KEY *start,KEY *end) ****************************************************************************/ -/* +/** Rename a table. - SYNOPSIS - mysql_rename_table() - base The handlerton handle. - old_db The old database name. - old_name The old table name. - new_db The new database name. - new_name The new table name. - flags flags for build_table_filename(). - FN_FROM_IS_TMP old_name is temporary. - FN_TO_IS_TMP new_name is temporary. - NO_FRM_RENAME Don't rename the FRM file - but only the table in the storage engine. - - RETURN - FALSE OK - TRUE Error + @param base The handlerton handle. + @param old_db The old database name. + @param old_name The old table name. + @param new_db The new database name. + @param new_name The new table name. + @param flags flags + FN_FROM_IS_TMP old_name is temporary. + FN_TO_IS_TMP new_name is temporary. + NO_FRM_RENAME Don't rename the FRM file + but only the table in the storage engine. + NO_HA_TABLE Don't rename table in engine. + + @return false OK + @return true Error */ bool @@ -4680,6 +4846,7 @@ mysql_rename_table(handlerton *base, const char *old_db, char tmp_name[SAFE_NAME_LEN+1]; handler *file; int error=0; + int length; DBUG_ENTER("mysql_rename_table"); DBUG_PRINT("enter", ("old: '%s'.'%s' new: '%s'.'%s'", old_db, old_name, new_db, new_name)); @@ -4689,8 +4856,14 @@ mysql_rename_table(handlerton *base, const char *old_db, build_table_filename(from, sizeof(from) - 1, old_db, old_name, "", flags & FN_FROM_IS_TMP); - build_table_filename(to, sizeof(to) - 1, new_db, new_name, "", - flags & FN_TO_IS_TMP); + length= build_table_filename(to, sizeof(to) - 1, new_db, new_name, "", + flags & FN_TO_IS_TMP); + // Check if we hit FN_REFLEN bytes along with file extension. + if (length+reg_ext_length > FN_REFLEN) + { + my_error(ER_IDENT_CAUSES_TOO_LONG_PATH, MYF(0), sizeof(to)-1, to); + DBUG_RETURN(TRUE); + } /* If lower_case_table_names == 2 (case-preserving but case-insensitive @@ -4713,7 +4886,13 @@ mysql_rename_table(handlerton *base, const char *old_db, to_base= lc_to; } - if (!file || !(error=file->ha_rename_table(from_base, to_base))) + if (flags & NO_HA_TABLE) + { + if (rename_file_ext(from,to,reg_ext)) + error= my_errno; + (void) file->ha_create_handler_files(to, from, CHF_RENAME_FLAG, NULL); + } + else if (!file || !(error=file->ha_rename_table(from_base, to_base))) { if (!(flags & NO_FRM_RENAME) && rename_file_ext(from,to,reg_ext)) { @@ -4729,6 +4908,7 @@ mysql_rename_table(handlerton *base, const char *old_db, else if (error) my_error(ER_ERROR_ON_RENAME, MYF(0), from, to, error); + #ifdef HAVE_PSI_TABLE_INTERFACE /* Remove the old table share from the pfs table share array. The new table @@ -4737,8 +4917,8 @@ mysql_rename_table(handlerton *base, const char *old_db, if (likely(error == 0)) { my_bool temp_table= (my_bool)is_prefix(old_name, tmp_file_prefix); - PSI_TABLE_CALL(drop_table_share)(temp_table, old_db, strlen(old_db), - old_name, strlen(old_name)); + PSI_TABLE_CALL(drop_table_share) + (temp_table, old_db, strlen(old_db), old_name, strlen(old_name)); } #endif @@ -4766,6 +4946,7 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table, { HA_CREATE_INFO local_create_info; Alter_info local_alter_info; + Alter_table_ctx local_alter_ctx; // Not used bool res= TRUE; bool is_trans= FALSE; uint not_used; @@ -4797,7 +4978,7 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table, local_create_info.db_type= src_table->table->s->db_type(); local_create_info.row_type= src_table->table->s->row_type; if (mysql_prepare_alter_table(thd, src_table->table, &local_create_info, - &local_alter_info)) + &local_alter_info, &local_alter_ctx)) goto err; #ifdef WITH_PARTITION_STORAGE_ENGINE /* Partition info is not handled by mysql_prepare_alter_table() call. */ @@ -4829,7 +5010,7 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table, if ((res= mysql_create_table_no_lock(thd, table->db, table->table_name, &local_create_info, &local_alter_info, - FALSE, 0, &is_trans))) + &is_trans, C_ORDINARY_CREATE))) goto err; /* @@ -4922,13 +5103,11 @@ err: /* table_list should contain just one table */ -static int -mysql_discard_or_import_tablespace(THD *thd, - TABLE_LIST *table_list, - enum tablespace_op_type tablespace_op) +int mysql_discard_or_import_tablespace(THD *thd, + TABLE_LIST *table_list, + bool discard) { - TABLE *table; - my_bool discard; + Alter_table_prelocking_strategy alter_prelocking_strategy; int error; DBUG_ENTER("mysql_discard_or_import_tablespace"); @@ -4939,21 +5118,28 @@ mysql_discard_or_import_tablespace(THD *thd, THD_STAGE_INFO(thd, stage_discard_or_import_tablespace); - discard= test(tablespace_op == DISCARD_TABLESPACE); - /* We set this flag so that ha_innobase::open and ::external_lock() do not complain when we lock the table */ thd->tablespace_op= TRUE; - table_list->mdl_request.set_type(MDL_SHARED_WRITE); - if (!(table=open_ltable(thd, table_list, TL_WRITE, 0))) + /* + Adjust values of table-level and metadata which was set in parser + for the case general ALTER TABLE. + */ + table_list->mdl_request.set_type(MDL_EXCLUSIVE); + table_list->lock_type= TL_WRITE; + /* Do not open views. */ + table_list->required_type= FRMTYPE_TABLE; + + if (open_and_lock_tables(thd, table_list, FALSE, 0, + &alter_prelocking_strategy)) { thd->tablespace_op=FALSE; DBUG_RETURN(-1); } - error= table->file->ha_discard_or_import_tablespace(discard); + error= table_list->table->file->ha_discard_or_import_tablespace(discard); THD_STAGE_INFO(thd, stage_end); @@ -4984,321 +5170,305 @@ err: DBUG_RETURN(0); } - table->file->print_error(error, MYF(0)); + table_list->table->file->print_error(error, MYF(0)); DBUG_RETURN(-1); } + /** - @brief Check if both DROP and CREATE are present for an index in ALTER TABLE - - @details Checks if any index is being modified (present as both DROP INDEX - and ADD INDEX) in the current ALTER TABLE statement. Needed for disabling - in-place ALTER TABLE. - - @param table The table being altered - @param alter_info The ALTER TABLE structure - @return presence of index being altered - @retval FALSE No such index - @retval TRUE Have at least 1 index modified + Check if key is a candidate key, i.e. a unique index with no index + fields partial or nullable. */ -static bool -is_index_maintenance_unique (TABLE *table, Alter_info *alter_info) +static bool is_candidate_key(KEY *key) { - List_iterator<Key> key_it(alter_info->key_list); - List_iterator<Alter_drop> drop_it(alter_info->drop_list); - Key *key; + KEY_PART_INFO *key_part; + KEY_PART_INFO *key_part_end= key->key_part + key->user_defined_key_parts; - while ((key= key_it++)) - { - if (key->name.str) - { - Alter_drop *drop; + if (!(key->flags & HA_NOSAME) || (key->flags & HA_NULL_PART_KEY)) + return false; - drop_it.rewind(); - while ((drop= drop_it++)) - { - if (drop->type == Alter_drop::KEY && - !my_strcasecmp(system_charset_info, key->name.str, drop->name)) - return TRUE; - } - } + for (key_part= key->key_part; key_part < key_part_end; key_part++) + { + if (key_part->key_part_flag & HA_PART_KEY_SEG) + return false; } - return FALSE; -} - - -/* - SYNOPSIS - mysql_compare_tables() - table The original table. - alter_info Alter options, fields and keys for the new - table. - create_info Create options for the new table. - order_num Number of order list elements. - need_copy_table OUT Result of the comparison. Undefined if error. - Otherwise is one of: - ALTER_TABLE_METADATA_ONLY No copy needed - ALTER_TABLE_DATA_CHANGED Data changes, - copy needed - ALTER_TABLE_INDEX_CHANGED Index changes, - copy might be needed - key_info_buffer OUT An array of KEY structs for new indexes - index_drop_buffer OUT An array of offsets into table->key_info. - index_drop_count OUT The number of elements in the array. - index_add_buffer OUT An array of offsets into key_info_buffer. - index_add_count OUT The number of elements in the array. - candidate_key_count OUT The number of candidate keys in original table. - DESCRIPTION - 'table' (first argument) contains information of the original - table, which includes all corresponding parts that the new - table has in arguments create_list, key_list and create_info. + return true; +} - By comparing the changes between the original and new table - we can determine how much it has changed after ALTER TABLE - and whether we need to make a copy of the table, or just change - the .frm file. - If there are no data changes, but index changes, 'index_drop_buffer' - and/or 'index_add_buffer' are populated with offsets into - table->key_info or key_info_buffer respectively for the indexes - that need to be dropped and/or (re-)created. +/** + Get Create_field object for newly created table by field index. - RETURN VALUES - TRUE The tables are not compatible; We have to do a full alter table - FALSE The tables are compatible; We only have to modify the .frm + @param alter_info Alter_info describing newly created table. + @param idx Field index. */ -bool -mysql_compare_tables(TABLE *table, - Alter_info *alter_info, - HA_CREATE_INFO *create_info, - uint order_num, - enum_alter_table_change_level *need_copy_table, - KEY **key_info_buffer, - uint **index_drop_buffer, uint *index_drop_count, - uint **index_add_buffer, uint *index_add_count, - uint *candidate_key_count) +static Create_field *get_field_by_index(Alter_info *alter_info, uint idx) { - Field **f_ptr, *field; - uint changes= 0, tmp; - uint key_count; - List_iterator_fast<Create_field> new_field_it, tmp_new_field_it; - Create_field *new_field, *tmp_new_field; - KEY_PART_INFO *key_part; - KEY_PART_INFO *end; - THD *thd= table->in_use; - uint i; - /* - Remember if the new definition has new VARCHAR column; - create_info->varchar will be reset in mysql_prepare_create_table. - */ - bool varchar= create_info->varchar; - bool not_nullable= true; - DBUG_ENTER("mysql_compare_tables"); + List_iterator_fast<Create_field> field_it(alter_info->create_list); + uint field_idx= 0; + Create_field *field; - /* - Create a copy of alter_info. - To compare the new and old table definitions, we need to "prepare" - the new definition - transform it from parser output to a format - that describes the final table layout (all column defaults are - initialized, duplicate columns are removed). This is done by - mysql_prepare_create_table. Unfortunately, - mysql_prepare_create_table performs its transformations - "in-place", that is, modifies the argument. Since we would - like to keep mysql_compare_tables() idempotent (not altering any - of the arguments) we create a copy of alter_info here and - pass it to mysql_prepare_create_table, then use the result - to evaluate possibility of in-place ALTER TABLE, and then - destroy the copy. - */ - Alter_info tmp_alter_info(*alter_info, thd->mem_root); - uint db_options= 0; /* not used */ + while ((field= field_it++) && field_idx < idx) + { field_idx++; } - /* Set default value for return value (to ensure it's always set) */ - *need_copy_table= ALTER_TABLE_DATA_CHANGED; + return field; +} - /* Create the prepared information. */ - if (mysql_prepare_create_table(thd, create_info, - &tmp_alter_info, - (table->s->tmp_table != NO_TMP_TABLE), - &db_options, - table->file, key_info_buffer, - &key_count, 0)) - DBUG_RETURN(1); - /* Allocate result buffers. */ - if (! (*index_drop_buffer= - (uint*) thd->alloc(sizeof(uint) * table->s->keys)) || - ! (*index_add_buffer= - (uint*) thd->alloc(sizeof(uint) * tmp_alter_info.key_list.elements))) - DBUG_RETURN(1); - - /* - Some very basic checks. If number of fields changes, or the - handler, we need to run full ALTER TABLE. In the future - new fields can be added and old dropped without copy, but - not yet. - Test also that engine was not given during ALTER TABLE, or - we are force to run regular alter table (copy). - E.g. ALTER TABLE tbl_name ENGINE=MyISAM. +static int compare_uint(const uint *s, const uint *t) +{ + return (*s < *t) ? -1 : ((*s > *t) ? 1 : 0); +} - For the following ones we also want to run regular alter table: - ALTER TABLE tbl_name ORDER BY .. - ALTER TABLE tbl_name CONVERT TO CHARACTER SET .. - At the moment we can't handle altering temporary tables without a copy. - We also test if OPTIMIZE TABLE was given and was mapped to alter table. - In that case we always do full copy. +/** + Compare original and new versions of a table and fill Alter_inplace_info + describing differences between those versions. + + @param thd Thread + @param table The original table. + @param varchar Indicates that new definition has new + VARCHAR column. + @param[in/out] ha_alter_info Data structure which already contains + basic information about create options, + field and keys for the new version of + table and which should be completed with + more detailed information needed for + in-place ALTER. + + First argument 'table' contains information of the original + table, which includes all corresponding parts that the new + table has in arguments create_list, key_list and create_info. + + Compare the changes between the original and new table definitions. + The result of this comparison is then passed to SE which determines + whether it can carry out these changes in-place. + + Mark any changes detected in the ha_alter_flags. + We generally try to specify handler flags only if there are real + changes. But in cases when it is cumbersome to determine if some + attribute has really changed we might choose to set flag + pessimistically, for example, relying on parser output only. + + If there are no data changes, but index changes, 'index_drop_buffer' + and/or 'index_add_buffer' are populated with offsets into + table->key_info or key_info_buffer respectively for the indexes + that need to be dropped and/or (re-)created. + + Note that this function assumes that it is OK to change Alter_info + and HA_CREATE_INFO which it gets. It is caller who is responsible + for creating copies for this structures if he needs them unchanged. + + @retval true error + @retval false success +*/ - There was a bug prior to mysql-4.0.25. Number of null fields was - calculated incorrectly. As a result frm and data files gets out of - sync after in-place alter table. There is no way to determine by which - mysql version (in 4.0 and 4.1 branches) table was created, thus we - disable in-place alter table for all tables created by mysql versions - prior to 5.0 branch. - See BUG#6236. - */ - if (table->s->fields != alter_info->create_list.elements || - table->s->db_type() != create_info->db_type || - table->s->tmp_table || - create_info->used_fields & HA_CREATE_USED_ENGINE || - create_info->used_fields & HA_CREATE_USED_CHARSET || - create_info->used_fields & HA_CREATE_USED_DEFAULT_CHARSET || - (table->s->row_type != create_info->row_type) || - create_info->used_fields & HA_CREATE_USED_PAGE_CHECKSUM || - create_info->used_fields & HA_CREATE_USED_TRANSACTIONAL || - create_info->used_fields & HA_CREATE_USED_PACK_KEYS || - create_info->used_fields & HA_CREATE_USED_MAX_ROWS || - (alter_info->flags & (ALTER_RECREATE | ALTER_FOREIGN_KEY)) || - order_num || - !table->s->mysql_version || - (table->s->frm_version < FRM_VER_TRUE_VARCHAR && varchar)) - { - DBUG_PRINT("info", ("Basic checks -> ALTER_TABLE_DATA_CHANGED")); - DBUG_RETURN(0); - } +static bool fill_alter_inplace_info(THD *thd, + TABLE *table, + bool varchar, + Alter_inplace_info *ha_alter_info) +{ + Field **f_ptr, *field; + List_iterator_fast<Create_field> new_field_it; + Create_field *new_field; + KEY_PART_INFO *key_part, *new_part; + KEY_PART_INFO *end; + uint candidate_key_count= 0; + Alter_info *alter_info= ha_alter_info->alter_info; + DBUG_ENTER("fill_alter_inplace_info"); - if ((create_info->fields_option_struct= (ha_field_option_struct**) - thd->calloc(sizeof(void*) * table->s->fields)) == NULL || - (create_info->indexes_option_struct= (ha_index_option_struct**) - thd->calloc(sizeof(void*) * table->s->keys)) == NULL) - DBUG_RETURN(1); + /* Allocate result buffers. */ + if (! (ha_alter_info->index_drop_buffer= + (KEY**) thd->alloc(sizeof(KEY*) * table->s->keys)) || + ! (ha_alter_info->index_add_buffer= + (uint*) thd->alloc(sizeof(uint) * + alter_info->key_list.elements))) + DBUG_RETURN(true); - tmp_new_field_it.init(tmp_alter_info.create_list); - for (i= 0, f_ptr= table->field, tmp_new_field= tmp_new_field_it++; - (field= *f_ptr); - i++, f_ptr++, tmp_new_field= tmp_new_field_it++) - { - if (field->is_equal(tmp_new_field) == IS_EQUAL_NO && - table->s->tmp_table == NO_TMP_TABLE) - (void) delete_statistics_for_column(thd, table, field); - else if (my_strcasecmp(system_charset_info, - field->field_name, - tmp_new_field->field_name)) - (void) rename_column_in_stat_tables(thd, table, field, - tmp_new_field->field_name); - } + /* First we setup ha_alter_flags based on what was detected by parser. */ + if (alter_info->flags & Alter_info::ALTER_ADD_COLUMN) + ha_alter_info->handler_flags|= Alter_inplace_info::ADD_COLUMN; + if (alter_info->flags & Alter_info::ALTER_DROP_COLUMN) + ha_alter_info->handler_flags|= Alter_inplace_info::DROP_COLUMN; + /* + Comparing new and old default values of column is cumbersome. + So instead of using such a comparison for detecting if default + has really changed we rely on flags set by parser to get an + approximate value for storage engine flag. + */ + if (alter_info->flags & (Alter_info::ALTER_CHANGE_COLUMN | + Alter_info::ALTER_CHANGE_COLUMN_DEFAULT)) + ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_COLUMN_DEFAULT; + if (alter_info->flags & Alter_info::ADD_FOREIGN_KEY) + ha_alter_info->handler_flags|= Alter_inplace_info::ADD_FOREIGN_KEY; + if (alter_info->flags & Alter_info::DROP_FOREIGN_KEY) + ha_alter_info->handler_flags|= Alter_inplace_info::DROP_FOREIGN_KEY; + if (alter_info->flags & Alter_info::ALTER_OPTIONS) + ha_alter_info->handler_flags|= Alter_inplace_info::CHANGE_CREATE_OPTION; + if (alter_info->flags & Alter_info::ALTER_RENAME) + ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_RENAME; + /* Check partition changes */ + if (alter_info->flags & Alter_info::ALTER_ADD_PARTITION) + ha_alter_info->handler_flags|= Alter_inplace_info::ADD_PARTITION; + if (alter_info->flags & Alter_info::ALTER_DROP_PARTITION) + ha_alter_info->handler_flags|= Alter_inplace_info::DROP_PARTITION; + if (alter_info->flags & Alter_info::ALTER_PARTITION) + ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_PARTITION; + if (alter_info->flags & Alter_info::ALTER_COALESCE_PARTITION) + ha_alter_info->handler_flags|= Alter_inplace_info::COALESCE_PARTITION; + if (alter_info->flags & Alter_info::ALTER_REORGANIZE_PARTITION) + ha_alter_info->handler_flags|= Alter_inplace_info::REORGANIZE_PARTITION; + if (alter_info->flags & Alter_info::ALTER_TABLE_REORG) + ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_TABLE_REORG; + if (alter_info->flags & Alter_info::ALTER_REMOVE_PARTITIONING) + ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_REMOVE_PARTITIONING; + if (alter_info->flags & Alter_info::ALTER_ALL_PARTITION) + ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_ALL_PARTITION; /* - Use transformed info to evaluate possibility of in-place ALTER TABLE - but use the preserved field to persist modifications. + If we altering table with old VARCHAR fields we will be automatically + upgrading VARCHAR column types. */ - new_field_it.init(alter_info->create_list); - tmp_new_field_it.init(tmp_alter_info.create_list); + if (table->s->frm_version < FRM_VER_TRUE_VARCHAR && varchar) + ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_COLUMN_TYPE; /* - Go through fields and check if the original ones are compatible - with new table. + Go through fields in old version of table and detect changes to them. + We don't want to rely solely on Alter_info flags for this since: + a) new definition of column can be fully identical to the old one + despite the fact that this column is mentioned in MODIFY clause. + b) even if new column type differs from its old column from metadata + point of view, it might be identical from storage engine point + of view (e.g. when ENUM('a','b') is changed to ENUM('a','b',c')). + c) flags passed to storage engine contain more detailed information + about nature of changes than those provided from parser. */ - for (i= 0, f_ptr= table->field, new_field= new_field_it++, - tmp_new_field= tmp_new_field_it++; - (field= *f_ptr); - i++, f_ptr++, new_field= new_field_it++, - tmp_new_field= tmp_new_field_it++) + for (f_ptr= table->field; (field= *f_ptr); f_ptr++) { - DBUG_ASSERT(i < table->s->fields); - create_info->fields_option_struct[i]= tmp_new_field->option_struct; - - /* reset common markers of how field changed */ - field->flags&= ~(FIELD_IS_RENAMED | FIELD_IN_ADD_INDEX); + /* Clear marker for renamed or dropped field + which we are going to set later. */ + field->flags&= ~(FIELD_IS_RENAMED | FIELD_IS_DROPPED); - /* Make sure we have at least the default charset in use. */ - if (!new_field->charset) - new_field->charset= create_info->default_table_charset; - - /* Check that NULL behavior is same for old and new fields */ - if ((tmp_new_field->flags & NOT_NULL_FLAG) != - (uint) (field->flags & NOT_NULL_FLAG)) + /* Use transformed info to evaluate flags for storage engine. */ + uint new_field_index= 0; + new_field_it.init(alter_info->create_list); + while ((new_field= new_field_it++)) { - DBUG_PRINT("info", ("NULL behaviour difference in field '%s' -> " - "ALTER_TABLE_DATA_CHANGED", new_field->field_name)); - DBUG_RETURN(0); + if (new_field->field == field) + break; + new_field_index++; } - /* - Check if the altered column is computed and either - is stored or is used in the partitioning expression. - TODO: Mark such a column with an alter flag only if - the defining expression has changed. - */ - if (field->vcol_info && - (field->stored_in_db || field->vcol_info->is_in_partitioning_expr())) + if (new_field) { - *need_copy_table= ALTER_TABLE_DATA_CHANGED; - DBUG_RETURN(0); - } + /* Field is not dropped. Evaluate changes bitmap for it. */ - /* Don't pack rows in old tables if the user has requested this. */ - if (create_info->row_type == ROW_TYPE_DYNAMIC || - (tmp_new_field->flags & BLOB_FLAG) || - (tmp_new_field->sql_type == MYSQL_TYPE_VARCHAR && - create_info->row_type != ROW_TYPE_FIXED)) - create_info->table_options|= HA_OPTION_PACK_RECORD; + /* + Check if type of column has changed to some incompatible type. + */ + switch (field->is_equal(new_field)) + { + case IS_EQUAL_NO: + /* New column type is incompatible with old one. */ + ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_COLUMN_TYPE; + break; + case IS_EQUAL_YES: + /* + New column is the same as the old one or the fully compatible with + it (for example, ENUM('a','b') was changed to ENUM('a','b','c')). + Such a change if any can ALWAYS be carried out by simply updating + data-dictionary without even informing storage engine. + No flag is set in this case. + */ + break; + case IS_EQUAL_PACK_LENGTH: + /* + New column type differs from the old one, but has compatible packed + data representation. Depending on storage engine, such a change can + be carried out by simply updating data dictionary without changing + actual data (for example, VARCHAR(300) is changed to VARCHAR(400)). + */ + ha_alter_info->handler_flags|= Alter_inplace_info:: + ALTER_COLUMN_EQUAL_PACK_LENGTH; + break; + default: + DBUG_ASSERT(0); + /* Safety. */ + ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_COLUMN_TYPE; + } - /* Check if field was renamed */ - if (my_strcasecmp(system_charset_info, - field->field_name, - tmp_new_field->field_name)) - { - field->flags|= FIELD_IS_RENAMED; - if (table->s->tmp_table == NO_TMP_TABLE) - rename_column_in_stat_tables(thd, table, field, - tmp_new_field->field_name); - } + /* Check if field was renamed */ + if (my_strcasecmp(system_charset_info, field->field_name, + new_field->field_name)) + { + field->flags|= FIELD_IS_RENAMED; + ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_COLUMN_NAME; + } - /* Evaluate changes bitmap and send to check_if_incompatible_data() */ - if (!(tmp= field->is_equal(tmp_new_field))) - { - if (table->s->tmp_table == NO_TMP_TABLE) + /* Check that NULL behavior is same for old and new fields */ + if ((new_field->flags & NOT_NULL_FLAG) != + (uint) (field->flags & NOT_NULL_FLAG)) { - KEY *key_info= table->key_info; - for (uint i=0; i < table->s->keys; i++, key_info++) - { - if (field->part_of_key.is_set(i)) - { - uint key_parts= table->actual_n_key_parts(key_info); - for (uint j= 0; j < key_parts; j++) - { - if (key_info->key_part[j].fieldnr-1 == field->field_index) - { - (void) delete_statistics_for_index(thd, table, key_info, - j >= key_info->user_defined_key_parts); - break; - } - } - } - } + if (new_field->flags & NOT_NULL_FLAG) + ha_alter_info->handler_flags|= + Alter_inplace_info::ALTER_COLUMN_NOT_NULLABLE; + else + ha_alter_info->handler_flags|= + Alter_inplace_info::ALTER_COLUMN_NULLABLE; } - DBUG_PRINT("info", ("!field_is_equal('%s') -> ALTER_TABLE_DATA_CHANGED", - new_field->field_name)); - DBUG_RETURN(0); + + /* + We do not detect changes to default values in this loop. + See comment above for more details. + */ + + /* + Detect changes in column order. + */ + if (field->field_index != new_field_index) + ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_COLUMN_ORDER; + + /* Detect changes in storage type of column */ + if (new_field->field_storage_type() != field->field_storage_type()) + ha_alter_info->handler_flags|= + Alter_inplace_info::ALTER_COLUMN_STORAGE_TYPE; + + /* Detect changes in column format of column */ + if (new_field->column_format() != field->column_format()) + ha_alter_info->handler_flags|= + Alter_inplace_info::ALTER_COLUMN_COLUMN_FORMAT; + } + else + { + /* + Field is not present in new version of table and therefore was dropped. + Corresponding storage engine flag should be already set. + */ + DBUG_ASSERT(ha_alter_info->handler_flags & Alter_inplace_info::DROP_COLUMN); + field->flags|= FIELD_IS_DROPPED; } - changes|= tmp; } +#ifndef DBUG_OFF + new_field_it.init(alter_info->create_list); + while ((new_field= new_field_it++)) + { + if (! new_field->field) + { + /* + Field is not present in old version of table and therefore was added. + Again corresponding storage engine flag should be already set. + */ + DBUG_ASSERT(ha_alter_info->handler_flags & Alter_inplace_info::ADD_COLUMN); + break; + } + } +#endif /* DBUG_OFF */ + /* Go through keys and check if the original ones are compatible with new table. @@ -5306,106 +5476,86 @@ mysql_compare_tables(TABLE *table, KEY *table_key; KEY *table_key_end= table->key_info + table->s->keys; KEY *new_key; - KEY *new_key_end= *key_info_buffer + key_count; + KEY *new_key_end= + ha_alter_info->key_info_buffer + ha_alter_info->key_count; DBUG_PRINT("info", ("index count old: %d new: %d", - table->s->keys, key_count)); + table->s->keys, ha_alter_info->key_count)); + /* Step through all keys of the old table and search matching new keys. */ - *index_drop_count= 0; - *index_add_count= 0; - *candidate_key_count= 0; + ha_alter_info->index_drop_count= 0; + ha_alter_info->index_add_count= 0; for (table_key= table->key_info; table_key < table_key_end; table_key++) { - KEY_PART_INFO *table_part; - KEY_PART_INFO *table_part_end= (table_key->key_part + - table_key->user_defined_key_parts); - KEY_PART_INFO *new_part; - - /* - Check if key is a candidate key, i.e. a unique index with no index - fields nullable, then key is either already primary key or could - be promoted to primary key if the original primary key is dropped. - Count all candidate keys. - */ - not_nullable= true; - for (table_part= table_key->key_part; - table_part < table_part_end; - table_part++) - { - not_nullable= not_nullable && (! table_part->field->maybe_null()); - } - if ((table_key->flags & HA_NOSAME) && not_nullable) - (*candidate_key_count)++; - /* Search a new key with the same name. */ - for (new_key= *key_info_buffer; new_key < new_key_end; new_key++) + for (new_key= ha_alter_info->key_info_buffer; + new_key < new_key_end; + new_key++) { if (! strcmp(table_key->name, new_key->name)) break; } if (new_key >= new_key_end) { - /* Key not found. Add the offset of the key to the drop buffer. */ - (*index_drop_buffer)[(*index_drop_count)++]= table_key - table->key_info; + /* Key not found. Add the key to the drop buffer. */ + ha_alter_info->index_drop_buffer + [ha_alter_info->index_drop_count++]= + table_key; DBUG_PRINT("info", ("index dropped: '%s'", table_key->name)); continue; } /* Check that the key types are compatible between old and new tables. */ if ((table_key->algorithm != new_key->algorithm) || - ((table_key->flags & HA_KEYFLAG_MASK) != + ((table_key->flags & HA_KEYFLAG_MASK) != (new_key->flags & HA_KEYFLAG_MASK)) || - (table_key->user_defined_key_parts != new_key->user_defined_key_parts)) + (table_key->user_defined_key_parts != + new_key->user_defined_key_parts)) goto index_changed; /* Check that the key parts remain compatible between the old and new tables. */ - for (table_part= table_key->key_part, new_part= new_key->key_part; - table_part < table_part_end; - table_part++, new_part++) + end= table_key->key_part + table_key->user_defined_key_parts; + for (key_part= table_key->key_part, new_part= new_key->key_part; + key_part < end; + key_part++, new_part++) { /* - Key definition has changed if we are using a different field or - if the used key part length is different. We know that the fields - did not change. Comparing field numbers is sufficient. + Key definition has changed if we are using a different field or + if the used key part length is different. It makes sense to + check lengths first as in case when fields differ it is likely + that lengths differ too and checking fields is more expensive + in general case. */ - if ((table_part->length != new_part->length) || - (table_part->fieldnr - 1 != new_part->fieldnr)) - goto index_changed; + if (key_part->length != new_part->length) + goto index_changed; + + new_field= get_field_by_index(alter_info, new_part->fieldnr); + + /* + For prefix keys KEY_PART_INFO::field points to cloned Field + object with adjusted length. So below we have to check field + indexes instead of simply comparing pointers to Field objects. + */ + if (! new_field->field || + new_field->field->field_index != key_part->fieldnr - 1) + goto index_changed; } continue; index_changed: - /* Key modified. Add the offset of the key to both buffers. */ - (*index_drop_buffer)[(*index_drop_count)++]= table_key - table->key_info; - (*index_add_buffer)[(*index_add_count)++]= new_key - *key_info_buffer; - key_part= new_key->key_part; - end= key_part + new_key->user_defined_key_parts; - for(; key_part != end; key_part++) - { - // Mark field to be part of new key - field= table->field[key_part->fieldnr]; - field->flags|= FIELD_IN_ADD_INDEX; - } - if (table->s->tmp_table == NO_TMP_TABLE) - { - (void) delete_statistics_for_index(thd, table, table_key, FALSE); - if ((uint) (table_key - table->key_info) == table->s->primary_key) - { - KEY *tab_key_info= table->key_info; - for (uint j=0; j < table->s->keys; j++, tab_key_info++) - { - if (tab_key_info->user_defined_key_parts != tab_key_info->ext_key_parts) - (void) delete_statistics_for_index(thd, table, tab_key_info, - TRUE); - } - } - } - + /* Key modified. Add the key / key offset to both buffers. */ + ha_alter_info->index_drop_buffer + [ha_alter_info->index_drop_count++]= + table_key; + ha_alter_info->index_add_buffer + [ha_alter_info->index_add_count++]= + new_key - ha_alter_info->key_info_buffer; + /* Mark all old fields which are used in newly created index. */ DBUG_PRINT("info", ("index changed: '%s'", table_key->name)); } /*end of for (; table_key < table_key_end;) */ @@ -5413,12 +5563,12 @@ mysql_compare_tables(TABLE *table, /* Step through all keys of the new table and find matching old keys. */ - for (new_key= *key_info_buffer; new_key < new_key_end; new_key++) + for (new_key= ha_alter_info->key_info_buffer; + new_key < new_key_end; + new_key++) { /* Search an old key with the same name. */ - for (i= 0, table_key= table->key_info; - table_key < table_key_end; - i++, table_key++) + for (table_key= table->key_info; table_key < table_key_end; table_key++) { if (! strcmp(table_key->name, new_key->name)) break; @@ -5426,44 +5576,307 @@ mysql_compare_tables(TABLE *table, if (table_key >= table_key_end) { /* Key not found. Add the offset of the key to the add buffer. */ - (*index_add_buffer)[(*index_add_count)++]= new_key - *key_info_buffer; - key_part= new_key->key_part; - end= key_part + new_key->user_defined_key_parts; - for(; key_part != end; key_part++) + ha_alter_info->index_add_buffer + [ha_alter_info->index_add_count++]= + new_key - ha_alter_info->key_info_buffer; + DBUG_PRINT("info", ("index added: '%s'", new_key->name)); + } + } + + /* + Sort index_add_buffer according to how key_info_buffer is sorted. + I.e. with primary keys first - see sort_keys(). + */ + my_qsort(ha_alter_info->index_add_buffer, + ha_alter_info->index_add_count, + sizeof(uint), (qsort_cmp) compare_uint); + + /* Now let us calculate flags for storage engine API. */ + + /* Count all existing candidate keys. */ + for (table_key= table->key_info; table_key < table_key_end; table_key++) + { + /* + Check if key is a candidate key, This key is either already primary key + or could be promoted to primary key if the original primary key is + dropped. + In MySQL one is allowed to create primary key with partial fields (i.e. + primary key which is not considered candidate). For simplicity we count + such key as a candidate key here. + */ + if (((uint) (table_key - table->key_info) == table->s->primary_key) || + is_candidate_key(table_key)) + candidate_key_count++; + } + + /* Figure out what kind of indexes we are dropping. */ + KEY **dropped_key; + KEY **dropped_key_end= ha_alter_info->index_drop_buffer + + ha_alter_info->index_drop_count; + + for (dropped_key= ha_alter_info->index_drop_buffer; + dropped_key < dropped_key_end; dropped_key++) + { + table_key= *dropped_key; + + if (table_key->flags & HA_NOSAME) + { + /* + Unique key. Check for PRIMARY KEY. Also see comment about primary + and candidate keys above. + */ + if ((uint) (table_key - table->key_info) == table->s->primary_key) { - // Mark field to be part of new key - field= table->field[key_part->fieldnr]; - field->flags|= FIELD_IN_ADD_INDEX; + ha_alter_info->handler_flags|= Alter_inplace_info::DROP_PK_INDEX; + candidate_key_count--; + } + else + { + ha_alter_info->handler_flags|= Alter_inplace_info::DROP_UNIQUE_INDEX; + if (is_candidate_key(table_key)) + candidate_key_count--; } - DBUG_PRINT("info", ("index added: '%s'", new_key->name)); } else + ha_alter_info->handler_flags|= Alter_inplace_info::DROP_INDEX; + } + + /* Now figure out what kind of indexes we are adding. */ + for (uint add_key_idx= 0; add_key_idx < ha_alter_info->index_add_count; add_key_idx++) + { + new_key= ha_alter_info->key_info_buffer + ha_alter_info->index_add_buffer[add_key_idx]; + + if (new_key->flags & HA_NOSAME) { - DBUG_ASSERT(i < table->s->keys); - create_info->indexes_option_struct[i]= new_key->option_struct; + bool is_pk= !my_strcasecmp(system_charset_info, new_key->name, primary_key_name); + + if ((!(new_key->flags & HA_KEY_HAS_PART_KEY_SEG) && + !(new_key->flags & HA_NULL_PART_KEY)) || + is_pk) + { + /* Candidate key or primary key! */ + if (candidate_key_count == 0 || is_pk) + ha_alter_info->handler_flags|= Alter_inplace_info::ADD_PK_INDEX; + else + ha_alter_info->handler_flags|= Alter_inplace_info::ADD_UNIQUE_INDEX; + candidate_key_count++; + } + else + { + ha_alter_info->handler_flags|= Alter_inplace_info::ADD_UNIQUE_INDEX; + } } + else + ha_alter_info->handler_flags|= Alter_inplace_info::ADD_INDEX; } - /* Check if changes are compatible with current handler without a copy */ + DBUG_RETURN(false); +} + + +/** + Mark fields participating in newly added indexes in TABLE object which + corresponds to new version of altered table. + + @param ha_alter_info Alter_inplace_info describing in-place ALTER. + @param altered_table TABLE object for new version of TABLE in which + fields should be marked. +*/ + +static void update_altered_table(const Alter_inplace_info &ha_alter_info, + TABLE *altered_table) +{ + uint field_idx, add_key_idx; + KEY *key; + KEY_PART_INFO *end, *key_part; + + /* + Clear marker for all fields, as we are going to set it only + for fields which participate in new indexes. + */ + for (field_idx= 0; field_idx < altered_table->s->fields; ++field_idx) + altered_table->field[field_idx]->flags&= ~FIELD_IN_ADD_INDEX; + + /* + Go through array of newly added indexes and mark fields + participating in them. + */ + for (add_key_idx= 0; add_key_idx < ha_alter_info.index_add_count; + add_key_idx++) + { + key= ha_alter_info.key_info_buffer + + ha_alter_info.index_add_buffer[add_key_idx]; + + end= key->key_part + key->user_defined_key_parts; + for (key_part= key->key_part; key_part < end; key_part++) + altered_table->field[key_part->fieldnr]->flags|= FIELD_IN_ADD_INDEX; + } +} + + +/** + Compare two tables to see if their metadata are compatible. + One table specified by a TABLE instance, the other using Alter_info + and HA_CREATE_INFO. + + @param[in] table The first table. + @param[in] alter_info Alter options, fields and keys for the + second table. + @param[in] create_info Create options for the second table. + @param[out] metadata_equal Result of comparison. + + @retval true error + @retval false success +*/ + +bool mysql_compare_tables(TABLE *table, + Alter_info *alter_info, + HA_CREATE_INFO *create_info, + bool *metadata_equal) +{ + DBUG_ENTER("mysql_compare_tables"); + + uint changes= IS_EQUAL_NO; + uint key_count; + List_iterator_fast<Create_field> tmp_new_field_it; + THD *thd= table->in_use; + *metadata_equal= false; + + /* + Create a copy of alter_info. + To compare definitions, we need to "prepare" the definition - transform it + from parser output to a format that describes the table layout (all column + defaults are initialized, duplicate columns are removed). This is done by + mysql_prepare_create_table. Unfortunately, mysql_prepare_create_table + performs its transformations "in-place", that is, modifies the argument. + Since we would like to keep mysql_compare_tables() idempotent (not altering + any of the arguments) we create a copy of alter_info here and pass it to + mysql_prepare_create_table, then use the result to compare the tables, and + then destroy the copy. + */ + Alter_info tmp_alter_info(*alter_info, thd->mem_root); + uint db_options= 0; /* not used */ + KEY *key_info_buffer= NULL; + + /* Create the prepared information. */ + int create_table_mode= table->s->tmp_table == NO_TMP_TABLE ? + C_ORDINARY_CREATE : C_ALTER_TABLE; + if (mysql_prepare_create_table(thd, create_info, &tmp_alter_info, + (table->s->tmp_table != NO_TMP_TABLE), + &db_options, table->file, &key_info_buffer, + &key_count, create_table_mode)) + DBUG_RETURN(true); + + /* Some very basic checks. */ + if (table->s->fields != alter_info->create_list.elements || + table->s->db_type() != create_info->db_type || + table->s->tmp_table || + (table->s->row_type != create_info->row_type)) + DBUG_RETURN(false); + + /* Go through fields and check if they are compatible. */ + tmp_new_field_it.init(tmp_alter_info.create_list); + for (Field **f_ptr= table->field; *f_ptr; f_ptr++) + { + Field *field= *f_ptr; + Create_field *tmp_new_field= tmp_new_field_it++; + + /* Check that NULL behavior is the same. */ + if ((tmp_new_field->flags & NOT_NULL_FLAG) != + (uint) (field->flags & NOT_NULL_FLAG)) + DBUG_RETURN(false); + + /* + mysql_prepare_alter_table() clears HA_OPTION_PACK_RECORD bit when + preparing description of existing table. In ALTER TABLE it is later + updated to correct value by create_table_impl() call. + So to get correct value of this bit in this function we have to + mimic behavior of create_table_impl(). + */ + if (create_info->row_type == ROW_TYPE_DYNAMIC || + (tmp_new_field->flags & BLOB_FLAG) || + (tmp_new_field->sql_type == MYSQL_TYPE_VARCHAR && + create_info->row_type != ROW_TYPE_FIXED)) + create_info->table_options|= HA_OPTION_PACK_RECORD; + + /* Check if field was renamed */ + if (my_strcasecmp(system_charset_info, + field->field_name, + tmp_new_field->field_name)) + DBUG_RETURN(false); + + /* Evaluate changes bitmap and send to check_if_incompatible_data() */ + uint field_changes= field->is_equal(tmp_new_field); + if (field_changes != IS_EQUAL_YES) + DBUG_RETURN(false); + + changes|= field_changes; + } + + /* Check if changes are compatible with current handler. */ if (table->file->check_if_incompatible_data(create_info, changes)) + DBUG_RETURN(false); + + /* Go through keys and check if they are compatible. */ + KEY *table_key; + KEY *table_key_end= table->key_info + table->s->keys; + KEY *new_key; + KEY *new_key_end= key_info_buffer + key_count; + + /* Step through all keys of the first table and search matching keys. */ + for (table_key= table->key_info; table_key < table_key_end; table_key++) { - DBUG_PRINT("info", ("check_if_incompatible_data() -> " - "ALTER_TABLE_DATA_CHANGED")); - DBUG_RETURN(0); + /* Search a key with the same name. */ + for (new_key= key_info_buffer; new_key < new_key_end; new_key++) + { + if (! strcmp(table_key->name, new_key->name)) + break; + } + if (new_key >= new_key_end) + DBUG_RETURN(false); + + /* Check that the key types are compatible. */ + if ((table_key->algorithm != new_key->algorithm) || + ((table_key->flags & HA_KEYFLAG_MASK) != + (new_key->flags & HA_KEYFLAG_MASK)) || + (table_key->user_defined_key_parts != + new_key->user_defined_key_parts)) + DBUG_RETURN(false); + + /* Check that the key parts remain compatible. */ + KEY_PART_INFO *table_part; + KEY_PART_INFO *table_part_end= table_key->key_part + table_key->user_defined_key_parts; + KEY_PART_INFO *new_part; + for (table_part= table_key->key_part, new_part= new_key->key_part; + table_part < table_part_end; + table_part++, new_part++) + { + /* + Key definition is different if we are using a different field or + if the used key part length is different. We know that the fields + are equal. Comparing field numbers is sufficient. + */ + if ((table_part->length != new_part->length) || + (table_part->fieldnr - 1 != new_part->fieldnr)) + DBUG_RETURN(false); + } } - if (*index_drop_count || *index_add_count) + /* Step through all keys of the second table and find matching keys. */ + for (new_key= key_info_buffer; new_key < new_key_end; new_key++) { - DBUG_PRINT("info", ("Index dropped=%u added=%u -> " - "ALTER_TABLE_INDEX_CHANGED", - *index_drop_count, *index_add_count)); - *need_copy_table= ALTER_TABLE_INDEX_CHANGED; - DBUG_RETURN(0); + /* Search a key with the same name. */ + for (table_key= table->key_info; table_key < table_key_end; table_key++) + { + if (! strcmp(table_key->name, new_key->name)) + break; + } + if (table_key >= table_key_end) + DBUG_RETURN(false); } - DBUG_PRINT("info", (" -> ALTER_TABLE_METADATA_ONLY")); - *need_copy_table= ALTER_TABLE_METADATA_ONLY; // Tables are compatible - DBUG_RETURN(0); + *metadata_equal= true; // Tables are compatible + DBUG_RETURN(false); } @@ -5484,7 +5897,7 @@ mysql_compare_tables(TABLE *table, static bool alter_table_manage_keys(TABLE *table, int indexes_were_disabled, - enum enum_enable_or_disable keys_onoff) + Alter_info::enum_enable_or_disable keys_onoff) { int error= 0; DBUG_ENTER("alter_table_manage_keys"); @@ -5492,20 +5905,20 @@ bool alter_table_manage_keys(TABLE *table, int indexes_were_disabled, table, indexes_were_disabled, keys_onoff)); switch (keys_onoff) { - case ENABLE: + case Alter_info::ENABLE: error= table->file->ha_enable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE); break; - case LEAVE_AS_IS: + case Alter_info::LEAVE_AS_IS: if (!indexes_were_disabled) break; /* fall-through: disabled indexes */ - case DISABLE: + case Alter_info::DISABLE: error= table->file->ha_disable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE); } if (error == HA_ERR_WRONG_COMMAND) { - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_NOTE, ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA), table->s->table_name.str); error= 0; @@ -5515,6 +5928,399 @@ bool alter_table_manage_keys(TABLE *table, int indexes_were_disabled, DBUG_RETURN(error); } + +/** + Check if the pending ALTER TABLE operations support the in-place + algorithm based on restrictions in the SQL layer or given the + nature of the operations themselves. If in-place isn't supported, + it won't be necessary to check with the storage engine. + + @param table The original TABLE. + @param create_info Information from the parsing phase about new + table properties. + @param alter_info Data related to detected changes. + + @return false In-place is possible, check with storage engine. + @return true Incompatible operations, must use table copy. +*/ + +static bool is_inplace_alter_impossible(TABLE *table, + HA_CREATE_INFO *create_info, + const Alter_info *alter_info) +{ + DBUG_ENTER("is_inplace_alter_impossible"); + + /* At the moment we can't handle altering temporary tables without a copy. */ + if (table->s->tmp_table) + DBUG_RETURN(true); + + + /* + We also test if OPTIMIZE TABLE was given and was mapped to alter table. + In that case we always do full copy (ALTER_RECREATE is set in this case). + + For the ALTER TABLE tbl_name ORDER BY ... we also always use copy + algorithm. In theory, this operation can be done in-place by some + engine, but since a) no current engine does this and b) our current + API lacks infrastructure for passing information about table ordering + to storage engine we simply always do copy now. + + ENABLE/DISABLE KEYS is a MyISAM/Heap specific operation that is + not supported for in-place in combination with other operations. + Alone, it will be done by simple_rename_or_index_change(). + */ + if (alter_info->flags & (Alter_info::ALTER_RECREATE | + Alter_info::ALTER_ORDER | + Alter_info::ALTER_KEYS_ONOFF)) + DBUG_RETURN(true); + + /* + Test also that engine was not given during ALTER TABLE, or + we are force to run regular alter table (copy). + E.g. ALTER TABLE tbl_name ENGINE=MyISAM. + Note that in addition to checking flag in HA_CREATE_INFO we + also check HA_CREATE_INFO::db_type value. This is done + to cover cases in which engine is changed implicitly + (e.g. when non-partitioned table becomes partitioned). + + Note that we do copy even if the table is already using the + given engine. Many users and tools depend on using ENGINE + to force a table rebuild. + */ + if (create_info->db_type != table->s->db_type() || + create_info->used_fields & HA_CREATE_USED_ENGINE) + DBUG_RETURN(true); + + /* + There was a bug prior to mysql-4.0.25. Number of null fields was + calculated incorrectly. As a result frm and data files gets out of + sync after fast alter table. There is no way to determine by which + mysql version (in 4.0 and 4.1 branches) table was created, thus we + disable fast alter table for all tables created by mysql versions + prior to 5.0 branch. + See BUG#6236. + */ + if (!table->s->mysql_version) + DBUG_RETURN(true); + + DBUG_RETURN(false); +} + + +/** + Perform in-place alter table. + + @param thd Thread handle. + @param table_list TABLE_LIST for the table to change. + @param table The original TABLE. + @param altered_table TABLE object for new version of the table. + @param ha_alter_info Structure describing ALTER TABLE to be carried + out and serving as a storage place for data + used during different phases. + @param inplace_supported Enum describing the locking requirements. + @param target_mdl_request Metadata request/lock on the target table name. + @param alter_ctx ALTER TABLE runtime context. + + @retval true Error + @retval false Success + + @note + If mysql_alter_table does not need to copy the table, it is + either an alter table where the storage engine does not + need to know about the change, only the frm will change, + or the storage engine supports performing the alter table + operation directly, in-place without mysql having to copy + the table. + + @note This function frees the TABLE object associated with the new version of + the table and removes the .FRM file for it in case of both success and + failure. +*/ + +static bool mysql_inplace_alter_table(THD *thd, + TABLE_LIST *table_list, + TABLE *table, + TABLE *altered_table, + Alter_inplace_info *ha_alter_info, + enum_alter_inplace_result inplace_supported, + MDL_request *target_mdl_request, + Alter_table_ctx *alter_ctx) +{ + Open_table_context ot_ctx(thd, MYSQL_OPEN_REOPEN); + handlerton *db_type= table->s->db_type(); + MDL_ticket *mdl_ticket= table->mdl_ticket; + HA_CREATE_INFO *create_info= ha_alter_info->create_info; + Alter_info *alter_info= ha_alter_info->alter_info; + bool reopen_tables= false; + + DBUG_ENTER("mysql_inplace_alter_table"); + + /* + Upgrade to EXCLUSIVE lock if: + - This is requested by the storage engine + - Or the storage engine needs exclusive lock for just the prepare + phase + - Or requested by the user + + Note that we handle situation when storage engine needs exclusive + lock for prepare phase under LOCK TABLES in the same way as when + exclusive lock is required for duration of the whole statement. + */ + if (inplace_supported == HA_ALTER_INPLACE_EXCLUSIVE_LOCK || + ((inplace_supported == HA_ALTER_INPLACE_SHARED_LOCK_AFTER_PREPARE || + inplace_supported == HA_ALTER_INPLACE_NO_LOCK_AFTER_PREPARE) && + (thd->locked_tables_mode == LTM_LOCK_TABLES || + thd->locked_tables_mode == LTM_PRELOCKED_UNDER_LOCK_TABLES)) || + alter_info->requested_lock == Alter_info::ALTER_TABLE_LOCK_EXCLUSIVE) + { + if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN)) + goto cleanup; + /* + Get rid of all TABLE instances belonging to this thread + except one to be used for in-place ALTER TABLE. + + This is mostly needed to satisfy InnoDB assumptions/asserts. + */ + close_all_tables_for_name(thd, table->s, + alter_ctx->is_table_renamed() ? + HA_EXTRA_PREPARE_FOR_RENAME : + HA_EXTRA_NOT_USED, + table); + /* + If we are under LOCK TABLES we will need to reopen tables which we + just have closed in case of error. + */ + reopen_tables= true; + } + else if (inplace_supported == HA_ALTER_INPLACE_SHARED_LOCK_AFTER_PREPARE || + inplace_supported == HA_ALTER_INPLACE_NO_LOCK_AFTER_PREPARE) + { + /* + Storage engine has requested exclusive lock only for prepare phase + and we are not under LOCK TABLES. + Don't mark TABLE_SHARE as old in this case, as this won't allow opening + of table by other threads during main phase of in-place ALTER TABLE. + */ + if (thd->mdl_context.upgrade_shared_lock(table->mdl_ticket, MDL_EXCLUSIVE, + thd->variables.lock_wait_timeout)) + goto cleanup; + + tdc_remove_table(thd, TDC_RT_REMOVE_NOT_OWN_KEEP_SHARE, + table->s->db.str, table->s->table_name.str, + false); + } + + /* + Upgrade to SHARED_NO_WRITE lock if: + - The storage engine needs writes blocked for the whole duration + - Or this is requested by the user + Note that under LOCK TABLES, we will already have SHARED_NO_READ_WRITE. + */ + if ((inplace_supported == HA_ALTER_INPLACE_SHARED_LOCK || + alter_info->requested_lock == Alter_info::ALTER_TABLE_LOCK_SHARED) && + thd->mdl_context.upgrade_shared_lock(table->mdl_ticket, + MDL_SHARED_NO_WRITE, + thd->variables.lock_wait_timeout)) + { + goto cleanup; + } + + // It's now safe to take the table level lock. + if (lock_tables(thd, table_list, alter_ctx->tables_opened, 0)) + goto cleanup; + + DEBUG_SYNC(thd, "alter_table_inplace_after_lock_upgrade"); + THD_STAGE_INFO(thd, stage_alter_inplace_prepare); + + switch (inplace_supported) { + case HA_ALTER_ERROR: + case HA_ALTER_INPLACE_NOT_SUPPORTED: + DBUG_ASSERT(0); + // fall through + case HA_ALTER_INPLACE_NO_LOCK: + case HA_ALTER_INPLACE_NO_LOCK_AFTER_PREPARE: + switch (alter_info->requested_lock) { + case Alter_info::ALTER_TABLE_LOCK_DEFAULT: + case Alter_info::ALTER_TABLE_LOCK_NONE: + ha_alter_info->online= true; + break; + case Alter_info::ALTER_TABLE_LOCK_SHARED: + case Alter_info::ALTER_TABLE_LOCK_EXCLUSIVE: + break; + } + break; + case HA_ALTER_INPLACE_EXCLUSIVE_LOCK: + case HA_ALTER_INPLACE_SHARED_LOCK_AFTER_PREPARE: + case HA_ALTER_INPLACE_SHARED_LOCK: + break; + } + + if (table->file->ha_prepare_inplace_alter_table(altered_table, + ha_alter_info)) + { + goto rollback; + } + + /* + Downgrade the lock if storage engine has told us that exclusive lock was + necessary only for prepare phase (unless we are not under LOCK TABLES) and + user has not explicitly requested exclusive lock. + */ + if ((inplace_supported == HA_ALTER_INPLACE_SHARED_LOCK_AFTER_PREPARE || + inplace_supported == HA_ALTER_INPLACE_NO_LOCK_AFTER_PREPARE) && + !(thd->locked_tables_mode == LTM_LOCK_TABLES || + thd->locked_tables_mode == LTM_PRELOCKED_UNDER_LOCK_TABLES) && + (alter_info->requested_lock != Alter_info::ALTER_TABLE_LOCK_EXCLUSIVE)) + { + /* If storage engine or user requested shared lock downgrade to SNW. */ + if (inplace_supported == HA_ALTER_INPLACE_SHARED_LOCK_AFTER_PREPARE || + alter_info->requested_lock == Alter_info::ALTER_TABLE_LOCK_SHARED) + table->mdl_ticket->downgrade_lock(MDL_SHARED_NO_WRITE); + else + { + DBUG_ASSERT(inplace_supported == HA_ALTER_INPLACE_NO_LOCK_AFTER_PREPARE); + table->mdl_ticket->downgrade_lock(MDL_SHARED_UPGRADABLE); + } + } + + DEBUG_SYNC(thd, "alter_table_inplace_after_lock_downgrade"); + THD_STAGE_INFO(thd, stage_alter_inplace); + + if (table->file->ha_inplace_alter_table(altered_table, + ha_alter_info)) + { + goto rollback; + } + + // Upgrade to EXCLUSIVE before commit. + if (wait_while_table_is_used(thd, table, HA_EXTRA_PREPARE_FOR_RENAME)) + goto rollback; + + /* + If we are killed after this point, we should ignore and continue. + We have mostly completed the operation at this point, there should + be no long waits left. + */ + + DBUG_EXECUTE_IF("alter_table_rollback_new_index", { + table->file->ha_commit_inplace_alter_table(altered_table, + ha_alter_info, + false); + my_error(ER_UNKNOWN_ERROR, MYF(0)); + goto cleanup; + }); + + DEBUG_SYNC(thd, "alter_table_inplace_before_commit"); + THD_STAGE_INFO(thd, stage_alter_inplace_commit); + + if (table->file->ha_commit_inplace_alter_table(altered_table, + ha_alter_info, + true)) + { + goto rollback; + } + + close_all_tables_for_name(thd, table->s, + alter_ctx->is_table_renamed() ? + HA_EXTRA_PREPARE_FOR_RENAME : + HA_EXTRA_NOT_USED, + NULL); + table_list->table= table= NULL; + close_temporary_table(thd, altered_table, true, false); + + /* + Replace the old .FRM with the new .FRM, but keep the old name for now. + Rename to the new name (if needed) will be handled separately below. + */ + if (mysql_rename_table(db_type, alter_ctx->new_db, alter_ctx->tmp_name, + alter_ctx->db, alter_ctx->alias, + FN_FROM_IS_TMP | NO_HA_TABLE)) + { + // Since changes were done in-place, we can't revert them. + (void) quick_rm_table(thd, db_type, + alter_ctx->new_db, alter_ctx->tmp_name, + FN_IS_TMP | NO_HA_TABLE); + DBUG_RETURN(true); + } + + table_list->mdl_request.ticket= mdl_ticket; + if (open_table(thd, table_list, thd->mem_root, &ot_ctx)) + DBUG_RETURN(true); + + /* + Tell the handler that the changed frm is on disk and table + has been re-opened + */ + table_list->table->file->ha_notify_table_changed(); + + /* + We might be going to reopen table down on the road, so we have to + restore state of the TABLE object which we used for obtaining of + handler object to make it usable for later reopening. + */ + close_thread_table(thd, &thd->open_tables); + table_list->table= NULL; + + // Rename altered table if requested. + if (alter_ctx->is_table_renamed()) + { + // Remove TABLE and TABLE_SHARE for old name from TDC. + tdc_remove_table(thd, TDC_RT_REMOVE_ALL, + alter_ctx->db, alter_ctx->table_name, false); + + if (mysql_rename_table(db_type, alter_ctx->db, alter_ctx->table_name, + alter_ctx->new_db, alter_ctx->new_alias, 0)) + { + /* + If the rename fails we will still have a working table + with the old name, but with other changes applied. + */ + DBUG_RETURN(true); + } + if (Table_triggers_list::change_table_name(thd, + alter_ctx->db, + alter_ctx->alias, + alter_ctx->table_name, + alter_ctx->new_db, + alter_ctx->new_alias)) + { + /* + If the rename of trigger files fails, try to rename the table + back so we at least have matching table and trigger files. + */ + (void) mysql_rename_table(db_type, + alter_ctx->new_db, alter_ctx->new_alias, + alter_ctx->db, alter_ctx->alias, 0); + DBUG_RETURN(true); + } + } + + DBUG_RETURN(false); + + rollback: + table->file->ha_commit_inplace_alter_table(altered_table, + ha_alter_info, + false); + cleanup: + if (reopen_tables) + { + /* Close the only table instance which is still around. */ + close_all_tables_for_name(thd, table->s, + alter_ctx->is_table_renamed() ? + HA_EXTRA_PREPARE_FOR_RENAME : + HA_EXTRA_NOT_USED, + NULL); + if (thd->locked_tables_list.reopen_tables(thd)) + thd->locked_tables_list.unlink_all_closed_tables(thd, NULL, 0); + /* QQ; do something about metadata locks ? */ + } + close_temporary_table(thd, altered_table, true, false); + // Delete temporary .frm/.par + (void) quick_rm_table(thd, create_info->db_type, alter_ctx->new_db, + alter_ctx->tmp_name, FN_IS_TMP | NO_HA_TABLE); + DBUG_RETURN(true); +} + /** maximum possible length for certain blob types. @@ -5573,6 +6379,7 @@ blob_length_by_type(enum_field_types type) But since ALTER might end-up doing CREATE, this distinction is gone and we just carry around two structures. + @param[in,out] alter_ctx Runtime context for ALTER TABLE. @return Fills various create_info members based on information retrieved @@ -5589,7 +6396,8 @@ blob_length_by_type(enum_field_types type) bool mysql_prepare_alter_table(THD *thd, TABLE *table, HA_CREATE_INFO *create_info, - Alter_info *alter_info) + Alter_info *alter_info, + Alter_table_ctx *alter_ctx) { /* New column definitions are added here */ List<Create_field> new_create_list; @@ -5667,12 +6475,6 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, if (table->s->tmp_table == NO_TMP_TABLE) (void) delete_statistics_for_column(thd, table, field); drop_it.remove(); - /* - ALTER TABLE DROP COLUMN always changes table data even in cases - when new version of the table has the same structure as the old - one. - */ - alter_info->change_level= ALTER_TABLE_DATA_CHANGED; continue; } /* Check if field is changed */ @@ -5746,45 +6548,58 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, if ((def->sql_type == MYSQL_TYPE_DATE || def->sql_type == MYSQL_TYPE_NEWDATE || def->sql_type == MYSQL_TYPE_DATETIME) && - !alter_info->datetime_field && + !alter_ctx->datetime_field && !(~def->flags & (NO_DEFAULT_VALUE_FLAG | NOT_NULL_FLAG)) && thd->variables.sql_mode & MODE_NO_ZERO_DATE) { - alter_info->datetime_field= def; - alter_info->error_if_not_empty= TRUE; + alter_ctx->datetime_field= def; + alter_ctx->error_if_not_empty= TRUE; } if (!def->after) new_create_list.push_back(def); - else if (def->after == first_keyword) - { - new_create_list.push_front(def); - /* - Re-ordering columns in table can't be done using in-place algorithm - as it always changes table data. - */ - alter_info->change_level= ALTER_TABLE_DATA_CHANGED; - } else { Create_field *find; - find_it.rewind(); - while ((find=find_it++)) // Add new columns + if (def->change) { - if (!my_strcasecmp(system_charset_info,def->after, find->field_name)) - break; + find_it.rewind(); + /* + For columns being modified with AFTER clause we should first remove + these columns from the list and then add them back at their correct + positions. + */ + while ((find=find_it++)) + { + /* + Create_fields representing changed columns are added directly + from Alter_info::create_list to new_create_list. We can therefore + safely use pointer equality rather than name matching here. + This prevents removing the wrong column in case of column rename. + */ + if (find == def) + { + find_it.remove(); + break; + } + } } - if (!find) + if (def->after == first_keyword) + new_create_list.push_front(def); + else { - my_error(ER_BAD_FIELD_ERROR, MYF(0), def->after, - table->s->table_name.str); - goto err; + find_it.rewind(); + while ((find=find_it++)) + { + if (!my_strcasecmp(system_charset_info, def->after, find->field_name)) + break; + } + if (!find) + { + my_error(ER_BAD_FIELD_ERROR, MYF(0), def->after, table->s->table_name.str); + goto err; + } + find_it.after(def); // Put column after this } - find_it.after(def); // Put element after this - /* - Re-ordering columns in table can't be done using in-place algorithm - as it always changes table data. - */ - alter_info->change_level= ALTER_TABLE_DATA_CHANGED; } } if (alter_info->alter_list.elements) @@ -5826,7 +6641,8 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, KEY *tab_key_info= table->key_info; for (uint j=0; j < table->s->keys; j++, tab_key_info++) { - if (tab_key_info->user_defined_key_parts != tab_key_info->ext_key_parts) + if (tab_key_info->user_defined_key_parts != + tab_key_info->ext_key_parts) (void) delete_statistics_for_index(thd, table, tab_key_info, TRUE); } @@ -5973,9 +6789,20 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, if (alter_info->drop_list.elements) { - my_error(ER_CANT_DROP_FIELD_OR_KEY, MYF(0), - alter_info->drop_list.head()->name); - goto err; + Alter_drop *drop; + drop_it.rewind(); + while ((drop=drop_it++)) { + switch (drop->type) { + case Alter_drop::KEY: + case Alter_drop::COLUMN: + my_error(ER_CANT_DROP_FIELD_OR_KEY, MYF(0), + alter_info->drop_list.head()->name); + goto err; + case Alter_drop::FOREIGN_KEY: + // Leave the DROP FOREIGN KEY names in the alter_info->drop_list. + break; + } + } } if (alter_info->alter_list.elements) { @@ -6015,90 +6842,492 @@ err: } -/* - Alter table +/** + Get Create_field object for newly created table by its name + in the old version of table. - SYNOPSIS - mysql_alter_table() - thd Thread handle - new_db If there is a RENAME clause - new_name If there is a RENAME clause - create_info Information from the parsing phase about new - table properties. - table_list The table to change. - alter_info Lists of fields, keys to be changed, added - or dropped. - order_num How many ORDER BY fields has been specified. - order List of fields to ORDER BY. - ignore Whether we have ALTER IGNORE TABLE - require_online Give an error if we can't do operation online + @param alter_info Alter_info describing newly created table. + @param old_name Name of field in old table. - DESCRIPTION - This is a veery long function and is everything but the kitchen sink :) - It is used to alter a table and not only by ALTER TABLE but also - CREATE|DROP INDEX are mapped on this function. - - When the ALTER TABLE statement just does a RENAME or ENABLE|DISABLE KEYS, - or both, then this function short cuts its operation by renaming - the table and/or enabling/disabling the keys. In this case, the FRM is - not changed, directly by mysql_alter_table. However, if there is a - RENAME + change of a field, or an index, the short cut is not used. - See how `create_list` is used to generate the new FRM regarding the - structure of the fields. The same is done for the indices of the table. - - Important is the fact, that this function tries to do as little work as - possible, by finding out whether a intermediate table is needed to copy - data into and when finishing the altering to use it as the original table. - For this reason the function mysql_compare_tables() is called, which decides - based on all kind of data how similar are the new and the original - tables. + @returns Pointer to Create_field object, NULL - if field is + not present in new version of table. +*/ - RETURN VALUES - FALSE OK - TRUE Error +static Create_field *get_field_by_old_name(Alter_info *alter_info, + const char *old_name) +{ + List_iterator_fast<Create_field> new_field_it(alter_info->create_list); + Create_field *new_field; + + while ((new_field= new_field_it++)) + { + if (new_field->field && + (my_strcasecmp(system_charset_info, + new_field->field->field_name, + old_name) == 0)) + break; + } + return new_field; +} + + +/** Type of change to foreign key column, */ + +enum fk_column_change_type +{ + FK_COLUMN_NO_CHANGE, FK_COLUMN_DATA_CHANGE, + FK_COLUMN_RENAMED, FK_COLUMN_DROPPED +}; + +/** + Check that ALTER TABLE's changes on columns of a foreign key are allowed. + + @param[in] thd Thread context. + @param[in] alter_info Alter_info describing changes to be done + by ALTER TABLE. + @param[in] fk_columns List of columns of the foreign key to check. + @param[out] bad_column_name Name of field on which ALTER TABLE tries to + do prohibited operation. + + @note This function takes into account value of @@foreign_key_checks + setting. + + @retval FK_COLUMN_NO_CHANGE No significant changes are to be done on + foreign key columns. + @retval FK_COLUMN_DATA_CHANGE ALTER TABLE might result in value + change in foreign key column (and + foreign_key_checks is on). + @retval FK_COLUMN_RENAMED Foreign key column is renamed. + @retval FK_COLUMN_DROPPED Foreign key column is dropped. +*/ + +static enum fk_column_change_type +fk_check_column_changes(THD *thd, Alter_info *alter_info, + List<LEX_STRING> &fk_columns, + const char **bad_column_name) +{ + List_iterator_fast<LEX_STRING> column_it(fk_columns); + LEX_STRING *column; + + *bad_column_name= NULL; + + while ((column= column_it++)) + { + Create_field *new_field= get_field_by_old_name(alter_info, column->str); + + if (new_field) + { + Field *old_field= new_field->field; + + if (my_strcasecmp(system_charset_info, old_field->field_name, + new_field->field_name)) + { + /* + Copy algorithm doesn't support proper renaming of columns in + the foreign key yet. At the moment we lack API which will tell + SE that foreign keys should be updated to use new name of column + like it happens in case of in-place algorithm. + */ + *bad_column_name= column->str; + return FK_COLUMN_RENAMED; + } + + if ((old_field->is_equal(new_field) == IS_EQUAL_NO) || + ((new_field->flags & NOT_NULL_FLAG) && + !(old_field->flags & NOT_NULL_FLAG))) + { + if (!(thd->variables.option_bits & OPTION_NO_FOREIGN_KEY_CHECKS)) + { + /* + Column in a FK has changed significantly. Unless + foreign_key_checks are off we prohibit this since this + means values in this column might be changed by ALTER + and thus referential integrity might be broken, + */ + *bad_column_name= column->str; + return FK_COLUMN_DATA_CHANGE; + } + } + } + else + { + /* + Column in FK was dropped. Most likely this will break + integrity constraints of InnoDB data-dictionary (and thus + InnoDB will emit an error), so we prohibit this right away + even if foreign_key_checks are off. + This also includes a rare case when another field replaces + field being dropped since it is easy to break referential + integrity in this case. + */ + *bad_column_name= column->str; + return FK_COLUMN_DROPPED; + } + } + + return FK_COLUMN_NO_CHANGE; +} + + +/** + Check if ALTER TABLE we are about to execute using COPY algorithm + is not supported as it might break referential integrity. + + @note If foreign_key_checks is disabled (=0), we allow to break + referential integrity. But we still disallow some operations + like dropping or renaming columns in foreign key since they + are likely to break consistency of InnoDB data-dictionary + and thus will end-up in error anyway. + + @param[in] thd Thread context. + @param[in] table Table to be altered. + @param[in] alter_info Lists of fields, keys to be changed, added + or dropped. + @param[out] alter_ctx ALTER TABLE runtime context. + Alter_table_ctx::fk_error_if_delete flag + is set if deletion during alter can break + foreign key integrity. + + @retval false Success. + @retval true Error, ALTER - tries to do change which is not compatible + with foreign key definitions on the table. +*/ + +static bool fk_prepare_copy_alter_table(THD *thd, TABLE *table, + Alter_info *alter_info, + Alter_table_ctx *alter_ctx) +{ + List <FOREIGN_KEY_INFO> fk_parent_key_list; + List <FOREIGN_KEY_INFO> fk_child_key_list; + FOREIGN_KEY_INFO *f_key; + + DBUG_ENTER("fk_prepare_copy_alter_table"); + + table->file->get_parent_foreign_key_list(thd, &fk_parent_key_list); + + /* OOM when building list. */ + if (thd->is_error()) + DBUG_RETURN(true); + + /* + Remove from the list all foreign keys in which table participates as + parent which are to be dropped by this ALTER TABLE. This is possible + when a foreign key has the same table as child and parent. + */ + List_iterator<FOREIGN_KEY_INFO> fk_parent_key_it(fk_parent_key_list); + + while ((f_key= fk_parent_key_it++)) + { + Alter_drop *drop; + List_iterator_fast<Alter_drop> drop_it(alter_info->drop_list); + + while ((drop= drop_it++)) + { + /* + InnoDB treats foreign key names in case-insensitive fashion. + So we do it here too. For database and table name type of + comparison used depends on lower-case-table-names setting. + For l_c_t_n = 0 we use case-sensitive comparison, for + l_c_t_n > 0 modes case-insensitive comparison is used. + */ + if ((drop->type == Alter_drop::FOREIGN_KEY) && + (my_strcasecmp(system_charset_info, f_key->foreign_id->str, + drop->name) == 0) && + (my_strcasecmp(table_alias_charset, f_key->foreign_db->str, + table->s->db.str) == 0) && + (my_strcasecmp(table_alias_charset, f_key->foreign_table->str, + table->s->table_name.str) == 0)) + fk_parent_key_it.remove(); + } + } + + /* + If there are FKs in which this table is parent which were not + dropped we need to prevent ALTER deleting rows from the table, + as it might break referential integrity. OTOH it is OK to do + so if foreign_key_checks are disabled. + */ + if (!fk_parent_key_list.is_empty() && + !(thd->variables.option_bits & OPTION_NO_FOREIGN_KEY_CHECKS)) + alter_ctx->set_fk_error_if_delete_row(fk_parent_key_list.head()); + + fk_parent_key_it.rewind(); + while ((f_key= fk_parent_key_it++)) + { + enum fk_column_change_type changes; + const char *bad_column_name; + + changes= fk_check_column_changes(thd, alter_info, + f_key->referenced_fields, + &bad_column_name); + + switch(changes) + { + case FK_COLUMN_NO_CHANGE: + /* No significant changes. We can proceed with ALTER! */ + break; + case FK_COLUMN_DATA_CHANGE: + { + char buff[NAME_LEN*2+2]; + strxnmov(buff, sizeof(buff)-1, f_key->foreign_db->str, ".", + f_key->foreign_table->str, NullS); + my_error(ER_FK_COLUMN_CANNOT_CHANGE_CHILD, MYF(0), bad_column_name, + f_key->foreign_id->str, buff); + DBUG_RETURN(true); + } + case FK_COLUMN_RENAMED: + my_error(ER_ALTER_OPERATION_NOT_SUPPORTED_REASON, MYF(0), + "ALGORITHM=COPY", + ER(ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_FK_RENAME), + "ALGORITHM=INPLACE"); + DBUG_RETURN(true); + case FK_COLUMN_DROPPED: + { + char buff[NAME_LEN*2+2]; + strxnmov(buff, sizeof(buff)-1, f_key->foreign_db->str, ".", + f_key->foreign_table->str, NullS); + my_error(ER_FK_COLUMN_CANNOT_DROP_CHILD, MYF(0), bad_column_name, + f_key->foreign_id->str, buff); + DBUG_RETURN(true); + } + default: + DBUG_ASSERT(0); + } + } + + table->file->get_foreign_key_list(thd, &fk_child_key_list); + + /* OOM when building list. */ + if (thd->is_error()) + DBUG_RETURN(true); + + /* + Remove from the list all foreign keys which are to be dropped + by this ALTER TABLE. + */ + List_iterator<FOREIGN_KEY_INFO> fk_key_it(fk_child_key_list); + + while ((f_key= fk_key_it++)) + { + Alter_drop *drop; + List_iterator_fast<Alter_drop> drop_it(alter_info->drop_list); + + while ((drop= drop_it++)) + { + /* Names of foreign keys in InnoDB are case-insensitive. */ + if ((drop->type == Alter_drop::FOREIGN_KEY) && + (my_strcasecmp(system_charset_info, f_key->foreign_id->str, + drop->name) == 0)) + fk_key_it.remove(); + } + } + + fk_key_it.rewind(); + while ((f_key= fk_key_it++)) + { + enum fk_column_change_type changes; + const char *bad_column_name; + + changes= fk_check_column_changes(thd, alter_info, + f_key->foreign_fields, + &bad_column_name); + + switch(changes) + { + case FK_COLUMN_NO_CHANGE: + /* No significant changes. We can proceed with ALTER! */ + break; + case FK_COLUMN_DATA_CHANGE: + my_error(ER_FK_COLUMN_CANNOT_CHANGE, MYF(0), bad_column_name, + f_key->foreign_id->str); + DBUG_RETURN(true); + case FK_COLUMN_RENAMED: + my_error(ER_ALTER_OPERATION_NOT_SUPPORTED_REASON, MYF(0), + "ALGORITHM=COPY", + ER(ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_FK_RENAME), + "ALGORITHM=INPLACE"); + DBUG_RETURN(true); + case FK_COLUMN_DROPPED: + my_error(ER_FK_COLUMN_CANNOT_DROP, MYF(0), bad_column_name, + f_key->foreign_id->str); + DBUG_RETURN(true); + default: + DBUG_ASSERT(0); + } + } + + DBUG_RETURN(false); +} + + +/** + Rename table and/or turn indexes on/off without touching .FRM + + @param thd Thread handler + @param table_list TABLE_LIST for the table to change + @param keys_onoff ENABLE or DISABLE KEYS? + @param alter_ctx ALTER TABLE runtime context. + + @return Operation status + @retval false Success + @retval true Failure +*/ + +static bool +simple_rename_or_index_change(THD *thd, TABLE_LIST *table_list, + Alter_info::enum_enable_or_disable keys_onoff, + Alter_table_ctx *alter_ctx) +{ + TABLE *table= table_list->table; + MDL_ticket *mdl_ticket= table->mdl_ticket; + int error= 0; + DBUG_ENTER("simple_rename_or_index_change"); + + if (keys_onoff != Alter_info::LEAVE_AS_IS) + { + if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN)) + DBUG_RETURN(true); + + // It's now safe to take the table level lock. + if (lock_tables(thd, table_list, alter_ctx->tables_opened, 0)) + DBUG_RETURN(true); + + if (keys_onoff == Alter_info::ENABLE) + { + DEBUG_SYNC(thd,"alter_table_enable_indexes"); + DBUG_EXECUTE_IF("sleep_alter_enable_indexes", my_sleep(6000000);); + error= table->file->ha_enable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE); + } + else if (keys_onoff == Alter_info::DISABLE) + error=table->file->ha_disable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE); + + if (error == HA_ERR_WRONG_COMMAND) + { + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, + ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA), + table->file->table_type(), + table->s->db.str, table->s->table_name.str); + error= 0; + } + else if (error > 0) + { + table->file->print_error(error, MYF(0)); + error= -1; + } + } + + if (!error && alter_ctx->is_table_renamed()) + { + THD_STAGE_INFO(thd, stage_rename); + handlerton *old_db_type= table->s->db_type(); + /* + Then do a 'simple' rename of the table. First we need to close all + instances of 'source' table. + Note that if wait_while_table_is_used() returns error here (i.e. if + this thread was killed) then it must be that previous step of + simple rename did nothing and therefore we can safely return + without additional clean-up. + */ + if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN)) + DBUG_RETURN(true); + close_all_tables_for_name(thd, table->s, HA_EXTRA_PREPARE_FOR_RENAME, NULL); + + LEX_STRING old_db_name= { alter_ctx->db, strlen(alter_ctx->db) }; + LEX_STRING old_table_name= + { alter_ctx->table_name, strlen(alter_ctx->table_name) }; + LEX_STRING new_db_name= { alter_ctx->new_db, strlen(alter_ctx->new_db) }; + LEX_STRING new_table_name= + { alter_ctx->new_alias, strlen(alter_ctx->new_alias) }; + (void) rename_table_in_stat_tables(thd, &old_db_name, &old_table_name, + &new_db_name, &new_table_name); + + if (mysql_rename_table(old_db_type, alter_ctx->db, alter_ctx->table_name, + alter_ctx->new_db, alter_ctx->new_alias, 0)) + error= -1; + else if (Table_triggers_list::change_table_name(thd, + alter_ctx->db, + alter_ctx->alias, + alter_ctx->table_name, + alter_ctx->new_db, + alter_ctx->new_alias)) + { + (void) mysql_rename_table(old_db_type, + alter_ctx->new_db, alter_ctx->new_alias, + alter_ctx->db, alter_ctx->table_name, 0); + error= -1; + } + } + + if (!error) + { + error= write_bin_log(thd, TRUE, thd->query(), thd->query_length()); + if (!error) + my_ok(thd); + } + table_list->table= NULL; // For query cache + query_cache_invalidate3(thd, table_list, 0); + + if ((thd->locked_tables_mode == LTM_LOCK_TABLES || + thd->locked_tables_mode == LTM_PRELOCKED_UNDER_LOCK_TABLES)) + { + /* + Under LOCK TABLES we should adjust meta-data locks before finishing + statement. Otherwise we can rely on them being released + along with the implicit commit. + */ + if (alter_ctx->is_table_renamed()) + thd->mdl_context.release_all_locks_for_name(mdl_ticket); + else + mdl_ticket->downgrade_lock(MDL_SHARED_NO_READ_WRITE); + } + DBUG_RETURN(error != 0); +} + + +/** + Alter table + + @param thd Thread handle + @param new_db If there is a RENAME clause + @param new_name If there is a RENAME clause + @param create_info Information from the parsing phase about new + table properties. + @param table_list The table to change. + @param alter_info Lists of fields, keys to be changed, added + or dropped. + @param order_num How many ORDER BY fields has been specified. + @param order List of fields to ORDER BY. + @param ignore Whether we have ALTER IGNORE TABLE + + @retval true Error + @retval false Success + + This is a veery long function and is everything but the kitchen sink :) + It is used to alter a table and not only by ALTER TABLE but also + CREATE|DROP INDEX are mapped on this function. + + When the ALTER TABLE statement just does a RENAME or ENABLE|DISABLE KEYS, + or both, then this function short cuts its operation by renaming + the table and/or enabling/disabling the keys. In this case, the FRM is + not changed, directly by mysql_alter_table. However, if there is a + RENAME + change of a field, or an index, the short cut is not used. + See how `create_list` is used to generate the new FRM regarding the + structure of the fields. The same is done for the indices of the table. + + Altering a table can be done in two ways. The table can be modified + directly using an in-place algorithm, or the changes can be done using + an intermediate temporary table (copy). In-place is the preferred + algorithm as it avoids copying table data. The storage engine + selects which algorithm to use in check_if_supported_inplace_alter() + based on information about the table changes from fill_alter_inplace_info(). */ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, HA_CREATE_INFO *create_info, TABLE_LIST *table_list, Alter_info *alter_info, - uint order_num, ORDER *order, bool ignore, - bool require_online) + uint order_num, ORDER *order, bool ignore) { - TABLE *table, *new_table= 0; - MDL_ticket *mdl_ticket; - MDL_request target_mdl_request; - int error= 0; - char tmp_name[80],old_name[32],new_name_buff[FN_REFLEN + 1]; - char old_name_buff[FN_REFLEN + 1]; - char new_alias_buff[FN_REFLEN], *table_name, *db, *new_alias, *alias; - char index_file[FN_REFLEN], data_file[FN_REFLEN]; - char path[FN_REFLEN + 1]; - char reg_path[FN_REFLEN+1]; - ha_rows copied,deleted; - handlerton *old_db_type, *new_db_type, *save_old_db_type; - enum_alter_table_change_level need_copy_table= ALTER_TABLE_METADATA_ONLY; -#ifdef WITH_PARTITION_STORAGE_ENGINE - TABLE *table_for_fast_alter_partition= NULL; - bool partition_changed= FALSE; -#endif - bool need_lock_for_indexes __attribute__((unused)) = TRUE; - KEY *key_info_buffer; - uint index_drop_count= 0; - uint *index_drop_buffer= NULL; - uint index_add_count= 0; - handler_add_index *add= NULL; - bool pending_inplace_add_index= false; - uint *index_add_buffer= NULL; - uint candidate_key_count= 0; - bool no_pk; - ulong explicit_used_fields= 0; - enum ha_extra_function extra_func= thd->locked_tables_mode - ? HA_EXTRA_NOT_USED - : HA_EXTRA_FORCE_REOPEN; - LEX_STRING old_db_name= { table_list->db, table_list->db_length }; - LEX_STRING old_table_name= { table_list->table_name, - table_list->table_name_length }; DBUG_ENTER("mysql_alter_table"); /* @@ -6107,63 +7336,39 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, it is the case. TODO: this design is obsolete and will be removed. */ - if (table_list && table_list->db && table_list->table_name) - { - int table_kind= 0; + int table_kind= check_if_log_table(table_list->db_length, table_list->db, + table_list->table_name_length, + table_list->table_name, false); - table_kind= check_if_log_table(table_list->db_length, table_list->db, - table_list->table_name_length, - table_list->table_name, 0); - - if (table_kind) + if (table_kind) + { + /* Disable alter of enabled log tables */ + if (logger.is_log_table_enabled(table_kind)) { - /* Disable alter of enabled log tables */ - if (logger.is_log_table_enabled(table_kind)) - { - my_error(ER_BAD_LOG_STATEMENT, MYF(0), "ALTER"); - DBUG_RETURN(TRUE); - } + my_error(ER_BAD_LOG_STATEMENT, MYF(0), "ALTER"); + DBUG_RETURN(true); + } - /* Disable alter of log tables to unsupported engine */ - if ((create_info->used_fields & HA_CREATE_USED_ENGINE) && - (!create_info->db_type || /* unknown engine */ - !(create_info->db_type->flags & HTON_SUPPORT_LOG_TABLES))) - { - my_error(ER_UNSUPORTED_LOG_ENGINE, MYF(0)); - DBUG_RETURN(TRUE); - } + /* Disable alter of log tables to unsupported engine */ + if ((create_info->used_fields & HA_CREATE_USED_ENGINE) && + (!create_info->db_type || /* unknown engine */ + !(create_info->db_type->flags & HTON_SUPPORT_LOG_TABLES))) + { + my_error(ER_UNSUPORTED_LOG_ENGINE, MYF(0), + hton_name(create_info->db_type)->str); + DBUG_RETURN(true); + } #ifdef WITH_PARTITION_STORAGE_ENGINE - if (alter_info->flags & ALTER_PARTITION) - { - my_error(ER_WRONG_USAGE, MYF(0), "PARTITION", "log table"); - DBUG_RETURN(TRUE); - } -#endif + if (alter_info->flags & Alter_info::ALTER_PARTITION) + { + my_error(ER_WRONG_USAGE, MYF(0), "PARTITION", "log table"); + DBUG_RETURN(true); } +#endif } - /* - Assign variables table_name, new_name, db, new_db, path, reg_path - to simplify further comparisions: we want to see if it's a RENAME - later just by comparing the pointers, avoiding the need for strcmp. - */ THD_STAGE_INFO(thd, stage_init); - table_name=table_list->table_name; - alias= (lower_case_table_names == 2) ? table_list->alias : table_name; - db=table_list->db; - if (!new_db || !my_strcasecmp(table_alias_charset, new_db, db)) - new_db= db; - build_table_filename(reg_path, sizeof(reg_path) - 1, db, table_name, reg_ext, 0); - build_table_filename(path, sizeof(path) - 1, db, table_name, "", 0); - - mysql_ha_rm_tables(thd, table_list); - - /* DISCARD/IMPORT TABLESPACE is always alone in an ALTER TABLE */ - if (alter_info->tablespace_op != NO_TABLESPACE_OP) - /* Conditionally writes to binlog. */ - DBUG_RETURN(mysql_discard_or_import_tablespace(thd,table_list, - alter_info->tablespace_op)); /* Code below can handle only base tables so ensure that we won't open a view. @@ -6172,20 +7377,21 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, */ table_list->required_type= FRMTYPE_TABLE; - Alter_table_prelocking_strategy alter_prelocking_strategy(alter_info); + Alter_table_prelocking_strategy alter_prelocking_strategy; DEBUG_SYNC(thd, "alter_table_before_open_tables"); - error= open_and_lock_tables(thd, table_list, FALSE, 0, - &alter_prelocking_strategy); + uint tables_opened; + bool error= open_tables(thd, &table_list, &tables_opened, 0, + &alter_prelocking_strategy); + + DEBUG_SYNC(thd, "alter_opened_table"); if (error) - { - DBUG_RETURN(TRUE); - } + DBUG_RETURN(true); - table= table_list->table; + TABLE *table= table_list->table; table->use_all_columns(); - mdl_ticket= table->mdl_ticket; + MDL_ticket *mdl_ticket= table->mdl_ticket; /* Prohibit changing of the UNION list of a non-temporary MERGE table @@ -6199,102 +7405,73 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, (table->s->tmp_table == NO_TMP_TABLE)) { my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0)); - DBUG_RETURN(TRUE); + DBUG_RETURN(true); } + Alter_table_ctx alter_ctx(thd, table_list, tables_opened, new_db, new_name); + + MDL_request target_mdl_request; + /* Check that we are not trying to rename to an existing table */ - if (new_name) + if (alter_ctx.is_table_renamed()) { - DBUG_PRINT("info", ("new_db.new_name: '%s'.'%s'", new_db, new_name)); - strmov(new_name_buff,new_name); - strmov(new_alias= new_alias_buff, new_name); - if (lower_case_table_names) + if (table->s->tmp_table != NO_TMP_TABLE) { - if (lower_case_table_names != 2) + if (find_temporary_table(thd, alter_ctx.new_db, alter_ctx.new_name)) { - my_casedn_str(files_charset_info, new_name_buff); - new_alias= new_name; // Create lower case table name + my_error(ER_TABLE_EXISTS_ERROR, MYF(0), alter_ctx.new_alias); + DBUG_RETURN(true); } - my_casedn_str(files_charset_info, new_name); } - if (new_db == db && - !my_strcasecmp(table_alias_charset, new_name_buff, table_name)) + else { + MDL_request_list mdl_requests; + MDL_request target_db_mdl_request; + + target_mdl_request.init(MDL_key::TABLE, + alter_ctx.new_db, alter_ctx.new_name, + MDL_EXCLUSIVE, MDL_TRANSACTION); + mdl_requests.push_front(&target_mdl_request); + /* - Source and destination table names are equal: make later check - easier. + If we are moving the table to a different database, we also + need IX lock on the database name so that the target database + is protected by MDL while the table is moved. */ - new_alias= new_name= table_name; - } - else - { - if (table->s->tmp_table != NO_TMP_TABLE) + if (alter_ctx.is_database_changed()) { - if (find_temporary_table(thd,new_db,new_name_buff)) - { - my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_name_buff); - DBUG_RETURN(TRUE); - } + target_db_mdl_request.init(MDL_key::SCHEMA, alter_ctx.new_db, "", + MDL_INTENTION_EXCLUSIVE, + MDL_TRANSACTION); + mdl_requests.push_front(&target_db_mdl_request); } - else - { - MDL_request_list mdl_requests; - MDL_request target_db_mdl_request; - - target_mdl_request.init(MDL_key::TABLE, new_db, new_name, - MDL_EXCLUSIVE, MDL_TRANSACTION); - mdl_requests.push_front(&target_mdl_request); - - /* - If we are moving the table to a different database, we also - need IX lock on the database name so that the target database - is protected by MDL while the table is moved. - */ - if (new_db != db) - { - target_db_mdl_request.init(MDL_key::SCHEMA, new_db, "", - MDL_INTENTION_EXCLUSIVE, - MDL_TRANSACTION); - mdl_requests.push_front(&target_db_mdl_request); - } - /* - Global intention exclusive lock must have been already acquired when - table to be altered was open, so there is no need to do it here. - */ - DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::GLOBAL, - "", "", - MDL_INTENTION_EXCLUSIVE)); + /* + Global intention exclusive lock must have been already acquired when + table to be altered was open, so there is no need to do it here. + */ + DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::GLOBAL, + "", "", + MDL_INTENTION_EXCLUSIVE)); - if (thd->mdl_context.acquire_locks(&mdl_requests, - thd->variables.lock_wait_timeout)) - DBUG_RETURN(TRUE); + if (thd->mdl_context.acquire_locks(&mdl_requests, + thd->variables.lock_wait_timeout)) + DBUG_RETURN(true); - DEBUG_SYNC(thd, "locked_table_name"); - /* - Table maybe does not exist, but we got an exclusive lock - on the name, now we can safely try to find out for sure. - */ - build_table_filename(new_name_buff, sizeof(new_name_buff) - 1, - new_db, new_name_buff, reg_ext, 0); - build_table_filename(old_name_buff, sizeof(old_name_buff) - 1, - db, table_name, reg_ext, 0); - if (check_table_file_presence(old_name_buff, new_name_buff, new_db, - new_name, new_alias, TRUE)) - { - /* Table will be closed in do_command() */ - goto err; - } + DEBUG_SYNC(thd, "locked_table_name"); + /* + Table maybe does not exist, but we got an exclusive lock + on the name, now we can safely try to find out for sure. + */ + if (!access(alter_ctx.get_new_filename(), F_OK)) + { + /* Table will be closed in do_command() */ + my_error(ER_TABLE_EXISTS_ERROR, MYF(0), alter_ctx.new_alias); + DBUG_RETURN(true); } } } - else - { - new_alias= (lower_case_table_names == 2) ? alias : table_name; - new_name= table_name; - } - old_db_type= table->s->db_type(); if (!create_info->db_type) { #ifdef WITH_PARTITION_STORAGE_ENGINE @@ -6312,451 +7489,191 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, } else #endif - create_info->db_type= old_db_type; + create_info->db_type= table->s->db_type(); } - if (check_engine(thd, new_db, new_name, create_info)) - goto err; - new_db_type= create_info->db_type; + if (check_engine(thd, alter_ctx.new_db, alter_ctx.new_name, create_info)) + DBUG_RETURN(true); - if ((new_db_type != old_db_type || - alter_info->flags & ALTER_PARTITION) && + if ((create_info->db_type != table->s->db_type() || + alter_info->flags & Alter_info::ALTER_PARTITION) && !table->file->can_switch_engines()) { my_error(ER_ROW_IS_REFERENCED, MYF(0)); - goto err; + DBUG_RETURN(true); } /* - If this is an ALTER TABLE and no explicit row type specified reuse - the table's row type. - Note: this is the same as if the row type was specified explicitly and - we must thus set HA_CREATE_USED_ROW_FORMAT! + If this is an ALTER TABLE and no explicit row type specified reuse + the table's row type. + Note : this is the same as if the row type was specified explicitly. */ if (create_info->row_type == ROW_TYPE_NOT_USED) { /* ALTER TABLE without explicit row type */ create_info->row_type= table->s->row_type; - /* - We have to mark the row type as used, as otherwise the engine may - change the row format in update_create_info(). - */ - create_info->used_fields|= HA_CREATE_USED_ROW_FORMAT; - explicit_used_fields|= HA_CREATE_USED_ROW_FORMAT; + } + else + { + /* ALTER TABLE with specific row type */ + create_info->used_fields |= HA_CREATE_USED_ROW_FORMAT; } DBUG_PRINT("info", ("old type: %s new type: %s", - ha_resolve_storage_engine_name(old_db_type), - ha_resolve_storage_engine_name(new_db_type))); - if (ha_check_storage_engine_flag(old_db_type, HTON_ALTER_NOT_SUPPORTED) || - ha_check_storage_engine_flag(new_db_type, HTON_ALTER_NOT_SUPPORTED)) + ha_resolve_storage_engine_name(table->s->db_type()), + ha_resolve_storage_engine_name(create_info->db_type))); + if (ha_check_storage_engine_flag(table->s->db_type(), HTON_ALTER_NOT_SUPPORTED)) { DBUG_PRINT("info", ("doesn't support alter")); - my_error(ER_ILLEGAL_HA, MYF(0), table_name); - goto err; + my_error(ER_ILLEGAL_HA, MYF(0), hton_name(table->s->db_type())->str, + alter_ctx.db, alter_ctx.table_name); + DBUG_RETURN(true); } - + + if (ha_check_storage_engine_flag(create_info->db_type, + HTON_ALTER_NOT_SUPPORTED)) + { + DBUG_PRINT("info", ("doesn't support alter")); + my_error(ER_ILLEGAL_HA, MYF(0), hton_name(create_info->db_type)->str, + alter_ctx.new_db, alter_ctx.new_name); + DBUG_RETURN(true); + } + THD_STAGE_INFO(thd, stage_setup); - if (!(alter_info->flags & ~(ALTER_RENAME | ALTER_KEYS_ONOFF)) && + if (!(alter_info->flags & ~(Alter_info::ALTER_RENAME | + Alter_info::ALTER_KEYS_ONOFF)) && + alter_info->requested_algorithm != + Alter_info::ALTER_TABLE_ALGORITHM_COPY && !table->s->tmp_table) // no need to touch frm { - switch (alter_info->keys_onoff) { - case LEAVE_AS_IS: - break; - case ENABLE: - if (wait_while_table_is_used(thd, table, extra_func)) - goto err; - DEBUG_SYNC(thd,"alter_table_enable_indexes"); - error= table->file->ha_enable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE); - break; - case DISABLE: - if (wait_while_table_is_used(thd, table, extra_func)) - goto err; - error=table->file->ha_disable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE); - break; - default: - DBUG_ASSERT(FALSE); - error= 0; - break; - } - if (error == HA_ERR_WRONG_COMMAND) - { - error= 0; - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, - ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA), - table->alias.c_ptr()); - } - - if (!error && (new_name != table_name || new_db != db)) + // This requires X-lock, no other lock levels supported. + if (alter_info->requested_lock != Alter_info::ALTER_TABLE_LOCK_DEFAULT && + alter_info->requested_lock != Alter_info::ALTER_TABLE_LOCK_EXCLUSIVE) { - THD_STAGE_INFO(thd, stage_rename); - /* - Then do a 'simple' rename of the table. First we need to close all - instances of 'source' table. - Note that if wait_while_table_is_used() returns error here (i.e. if - this thread was killed) then it must be that previous step of - simple rename did nothing and therefore we can safely return - without additional clean-up. - */ - if (wait_while_table_is_used(thd, table, extra_func)) - goto err; - close_all_tables_for_name(thd, table->s, HA_EXTRA_PREPARE_FOR_RENAME); - /* - Then, we want check once again that target table does not exist. - Actually the order of these two steps does not matter since - earlier we took exclusive metadata lock on the target table, so - we do them in this particular order only to be consistent with 5.0, - in which we don't take this lock and where this order really matters. - TODO: Investigate if we need this access() check at all. - */ - if (!access(new_name_buff,F_OK)) - { - my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_name); - error= -1; - } - else - { - *fn_ext(new_name)=0; - - LEX_STRING new_db_name= { new_db, strlen(new_db) }; - LEX_STRING new_table_name= { new_alias, strlen(new_alias) }; - (void) rename_table_in_stat_tables(thd, &old_db_name, &old_table_name, - &new_db_name, &new_table_name); - - if (mysql_rename_table(old_db_type,db,table_name,new_db,new_alias, 0)) - error= -1; - else if (Table_triggers_list::change_table_name(thd, db, - alias, table_name, - new_db, new_alias)) - { - (void) mysql_rename_table(old_db_type, new_db, new_alias, db, - table_name, 0); - error= -1; - } - } - } - - if (error == HA_ERR_WRONG_COMMAND) - { - error= 0; - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, - ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA), - table->alias.c_ptr()); - } - - if (!error) - { - error= write_bin_log(thd, TRUE, thd->query(), thd->query_length()); - if (!error) - my_ok(thd); - } - else if (error > 0) - { - table->file->print_error(error, MYF(0)); - error= -1; - } - table_list->table= NULL; // For query cache - query_cache_invalidate3(thd, table_list, 0); - - if ((thd->locked_tables_mode == LTM_LOCK_TABLES || - thd->locked_tables_mode == LTM_PRELOCKED_UNDER_LOCK_TABLES)) - { - /* - Under LOCK TABLES we should adjust meta-data locks before finishing - statement. Otherwise we can rely on them being released - along with the implicit commit. - */ - if (new_name != table_name || new_db != db) - thd->mdl_context.release_all_locks_for_name(mdl_ticket); - else - mdl_ticket->downgrade_exclusive_lock(MDL_SHARED_NO_READ_WRITE); + my_error(ER_ALTER_OPERATION_NOT_SUPPORTED, MYF(0), + "LOCK=NONE/SHARED", "LOCK=EXCLUSIVE"); + DBUG_RETURN(true); } - DBUG_RETURN(error); + DBUG_RETURN(simple_rename_or_index_change(thd, table_list, + alter_info->keys_onoff, + &alter_ctx)); } /* We have to do full alter table. */ #ifdef WITH_PARTITION_STORAGE_ENGINE - if (prep_alter_part_table(thd, table, alter_info, create_info, old_db_type, - &partition_changed, - db, table_name, path, - &table_for_fast_alter_partition)) - goto err; + bool partition_changed= false; + bool fast_alter_partition= false; + { + if (prep_alter_part_table(thd, table, alter_info, create_info, + &alter_ctx, &partition_changed, + &fast_alter_partition)) + { + DBUG_RETURN(true); + } + } #endif - /* - If the old table had partitions and we are doing ALTER TABLE ... - engine= <new_engine>, the new table must preserve the original - partitioning. That means that the new engine is still the - partitioning engine, not the engine specified in the parser. - This is discovered in prep_alter_part_table, which in such case - updates create_info->db_type. - Now we need to update the stack copy of create_info->db_type, - as otherwise we won't be able to correctly move the files of the - temporary table to the result table files. - */ - new_db_type= create_info->db_type; - - if (is_index_maintenance_unique (table, alter_info)) - need_copy_table= ALTER_TABLE_DATA_CHANGED; - if (mysql_prepare_alter_table(thd, table, create_info, alter_info)) - goto err; - - /* Remove markers set for update_create_info */ - create_info->used_fields&= ~explicit_used_fields; - - if (need_copy_table == ALTER_TABLE_METADATA_ONLY) - need_copy_table= alter_info->change_level; + if (mysql_prepare_alter_table(thd, table, create_info, alter_info, + &alter_ctx)) + { + DBUG_RETURN(true); + } - set_table_default_charset(thd, create_info, db); + set_table_default_charset(thd, create_info, alter_ctx.db); promote_first_timestamp_column(&alter_info->create_list); - if (thd->variables.old_alter_table - || (table->s->db_type() != create_info->db_type) #ifdef WITH_PARTITION_STORAGE_ENGINE - || partition_changed -#endif - ) - need_copy_table= ALTER_TABLE_DATA_CHANGED; - else + if (fast_alter_partition) { - enum_alter_table_change_level need_copy_table_res; - /* Check how much the tables differ. */ - if (mysql_compare_tables(table, alter_info, - create_info, order_num, - &need_copy_table_res, - &key_info_buffer, - &index_drop_buffer, &index_drop_count, - &index_add_buffer, &index_add_count, - &candidate_key_count)) - goto err; - - DBUG_EXECUTE_IF("alter_table_only_metadata_change", { - if (need_copy_table_res != ALTER_TABLE_METADATA_ONLY) - goto err; }); - DBUG_EXECUTE_IF("alter_table_only_index_change", { - if (need_copy_table_res != ALTER_TABLE_INDEX_CHANGED) - goto err; }); - - if (need_copy_table == ALTER_TABLE_METADATA_ONLY) - need_copy_table= need_copy_table_res; - } - - /* - If there are index changes only, try to do them in-place. "Index - changes only" means also that the handler for the table does not - change. The table is open and locked. The handler can be accessed. - */ - if (need_copy_table == ALTER_TABLE_INDEX_CHANGED) - { - int pk_changed= 0; - ulong alter_flags= 0; - ulong needed_inplace_with_read_flags= 0; - ulong needed_inplace_flags= 0; - KEY *key; - uint *idx_p; - uint *idx_end_p; - - alter_flags= table->file->alter_table_flags(alter_info->flags); - DBUG_PRINT("info", ("alter_flags: %lu", alter_flags)); - /* Check dropped indexes. */ - for (idx_p= index_drop_buffer, idx_end_p= idx_p + index_drop_count; - idx_p < idx_end_p; - idx_p++) - { - key= table->key_info + *idx_p; - DBUG_PRINT("info", ("index dropped: '%s'", key->name)); - if (key->flags & HA_NOSAME) - { - /* - Unique key. Check for "PRIMARY". - or if dropping last unique key - */ - if ((uint) (key - table->key_info) == table->s->primary_key) - { - DBUG_PRINT("info", ("Dropping primary key")); - /* Primary key. */ - needed_inplace_with_read_flags|= HA_INPLACE_DROP_PK_INDEX_NO_WRITE; - needed_inplace_flags|= HA_INPLACE_DROP_PK_INDEX_NO_READ_WRITE; - pk_changed++; - candidate_key_count--; - } - else - { - KEY_PART_INFO *part_end= key->key_part + key->user_defined_key_parts; - bool is_candidate_key= true; - - /* Non-primary unique key. */ - needed_inplace_with_read_flags|= - HA_INPLACE_DROP_UNIQUE_INDEX_NO_WRITE; - needed_inplace_flags|= HA_INPLACE_DROP_UNIQUE_INDEX_NO_READ_WRITE; - - /* - Check if all fields in key are declared - NOT NULL and adjust candidate_key_count - */ - for (KEY_PART_INFO *key_part= key->key_part; - key_part < part_end; - key_part++) - is_candidate_key= - (is_candidate_key && - (! table->field[key_part->fieldnr-1]->maybe_null())); - if (is_candidate_key) - candidate_key_count--; - } - } - else - { - /* Non-unique key. */ - needed_inplace_with_read_flags|= HA_INPLACE_DROP_INDEX_NO_WRITE; - needed_inplace_flags|= HA_INPLACE_DROP_INDEX_NO_READ_WRITE; - } - } - no_pk= ((table->s->primary_key == MAX_KEY) || - (needed_inplace_with_read_flags & - HA_INPLACE_DROP_PK_INDEX_NO_WRITE)); - /* Check added indexes. */ - for (idx_p= index_add_buffer, idx_end_p= idx_p + index_add_count; - idx_p < idx_end_p; - idx_p++) - { - key= key_info_buffer + *idx_p; - DBUG_PRINT("info", ("index added: '%s'", key->name)); - if (key->flags & HA_NOSAME) - { - /* Unique key */ - - KEY_PART_INFO *part_end= key->key_part + key->user_defined_key_parts; - bool is_candidate_key= true; - - /* - Check if all fields in key are declared - NOT NULL - */ - for (KEY_PART_INFO *key_part= key->key_part; - key_part < part_end; - key_part++) - is_candidate_key= - (is_candidate_key && - (! table->field[key_part->fieldnr]->maybe_null())); - - /* - Check for "PRIMARY" - or if adding first unique key - defined on non-nullable fields - */ - - if ((!my_strcasecmp(system_charset_info, - key->name, primary_key_name)) || - (no_pk && candidate_key_count == 0 && is_candidate_key)) - { - DBUG_PRINT("info", ("Adding primary key")); - /* Primary key. */ - needed_inplace_with_read_flags|= HA_INPLACE_ADD_PK_INDEX_NO_WRITE; - needed_inplace_flags|= HA_INPLACE_ADD_PK_INDEX_NO_READ_WRITE; - pk_changed++; - no_pk= false; - } - else - { - /* Non-primary unique key. */ - needed_inplace_with_read_flags|= HA_INPLACE_ADD_UNIQUE_INDEX_NO_WRITE; - needed_inplace_flags|= HA_INPLACE_ADD_UNIQUE_INDEX_NO_READ_WRITE; - if (ignore) - { - /* - If ignore is used, we have to remove all duplicate rows, - which require a full table copy. - */ - need_copy_table= ALTER_TABLE_DATA_CHANGED; - pk_changed= 2; // Don't change need_copy_table - break; - } - } - } - else - { - /* Non-unique key. */ - needed_inplace_with_read_flags|= HA_INPLACE_ADD_INDEX_NO_WRITE; - needed_inplace_flags|= HA_INPLACE_ADD_INDEX_NO_READ_WRITE; - } + /* + ALGORITHM and LOCK clauses are generally not allowed by the + parser for operations related to partitioning. + The exceptions are ALTER_PARTITION and ALTER_REMOVE_PARTITIONING. + For consistency, we report ER_ALTER_OPERATION_NOT_SUPPORTED here. + */ + if (alter_info->requested_lock != + Alter_info::ALTER_TABLE_LOCK_DEFAULT) + { + my_error(ER_ALTER_OPERATION_NOT_SUPPORTED_REASON, MYF(0), + "LOCK=NONE/SHARED/EXCLUSIVE", + ER(ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_PARTITION), + "LOCK=DEFAULT"); + DBUG_RETURN(true); } - - if ((candidate_key_count > 0) && - (needed_inplace_with_read_flags & HA_INPLACE_DROP_PK_INDEX_NO_WRITE)) + else if (alter_info->requested_algorithm != + Alter_info::ALTER_TABLE_ALGORITHM_DEFAULT) { - /* - Dropped primary key when there is some other unique - not null key that should be converted to primary key - */ - needed_inplace_with_read_flags|= HA_INPLACE_ADD_PK_INDEX_NO_WRITE; - needed_inplace_flags|= HA_INPLACE_ADD_PK_INDEX_NO_READ_WRITE; - pk_changed= 2; + my_error(ER_ALTER_OPERATION_NOT_SUPPORTED_REASON, MYF(0), + "ALGORITHM=COPY/INPLACE", + ER(ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_PARTITION), + "ALGORITHM=DEFAULT"); + DBUG_RETURN(true); } - DBUG_PRINT("info", - ("needed_inplace_with_read_flags: 0x%lx, needed_inplace_flags: 0x%lx", - needed_inplace_with_read_flags, needed_inplace_flags)); /* - In-place add/drop index is possible only if - the primary key is not added and dropped in the same statement. - Otherwise we have to recreate the table. - need_copy_table is no-zero at this place. - - Also, in-place is not possible if we add a primary key - and drop another key in the same statement. If the drop fails, - we will not be able to revert adding of primary key. + Upgrade from MDL_SHARED_UPGRADABLE to MDL_SHARED_NO_WRITE. + Afterwards it's safe to take the table level lock. */ - if ( pk_changed < 2 ) + if (thd->mdl_context.upgrade_shared_lock(mdl_ticket, MDL_SHARED_NO_WRITE, + thd->variables.lock_wait_timeout) + || lock_tables(thd, table_list, alter_ctx.tables_opened, 0)) { - if ((needed_inplace_with_read_flags & HA_INPLACE_ADD_PK_INDEX_NO_WRITE) && - index_drop_count > 0) - { - /* - Do copy, not in-place ALTER. - Avoid setting ALTER_TABLE_METADATA_ONLY. - */ - } - else if ((alter_flags & needed_inplace_with_read_flags) == - needed_inplace_with_read_flags) - { - /* All required in-place flags to allow concurrent reads are present. */ - need_copy_table= ALTER_TABLE_METADATA_ONLY; - need_lock_for_indexes= FALSE; - } - else if ((alter_flags & needed_inplace_flags) == needed_inplace_flags) - { - /* All required in-place flags are present. */ - need_copy_table= ALTER_TABLE_METADATA_ONLY; - } + DBUG_RETURN(true); } - DBUG_PRINT("info", ("need_copy_table: %u need_lock: %d", - need_copy_table, need_lock_for_indexes)); + + // In-place execution of ALTER TABLE for partitioning. + DBUG_RETURN(fast_alter_partition_table(thd, table, alter_info, + create_info, table_list, + alter_ctx.db, + alter_ctx.table_name)); } +#endif /* - better have a negative test here, instead of positive, like - alter_info->flags & ALTER_ADD_COLUMN|ALTER_ADD_INDEX|... - so that ALTER TABLE won't break when somebody will add new flag + Use copy algorithm if: + - old_alter_table system variable is set without in-place requested using + the ALGORITHM clause. + - Or if in-place is impossible for given operation. + - Changes to partitioning which were not handled by fast_alter_part_table() + needs to be handled using table copying algorithm unless the engine + supports auto-partitioning as such engines can do some changes + using in-place API. */ - if (need_copy_table == ALTER_TABLE_METADATA_ONLY) - create_info->frm_only= 1; - + if ((thd->variables.old_alter_table && + alter_info->requested_algorithm != + Alter_info::ALTER_TABLE_ALGORITHM_INPLACE) + || is_inplace_alter_impossible(table, create_info, alter_info) #ifdef WITH_PARTITION_STORAGE_ENGINE - if (table_for_fast_alter_partition) + || (partition_changed && + !(table->s->db_type()->partition_flags() & HA_USE_AUTO_PARTITION)) +#endif + ) { - DBUG_RETURN(fast_alter_partition_table(thd, table, alter_info, - create_info, table_list, - db, table_name, - table_for_fast_alter_partition)); + if (alter_info->requested_algorithm == + Alter_info::ALTER_TABLE_ALGORITHM_INPLACE) + { + my_error(ER_ALTER_OPERATION_NOT_SUPPORTED, MYF(0), + "ALGORITHM=INPLACE", "ALGORITHM=COPY"); + DBUG_RETURN(true); + } + alter_info->requested_algorithm= Alter_info::ALTER_TABLE_ALGORITHM_COPY; } -#endif - my_snprintf(tmp_name, sizeof(tmp_name), "%s-%lx_%lx", tmp_file_prefix, - current_pid, thd->thread_id); - /* Safety fix for innodb */ - if (lower_case_table_names) - my_casedn_str(files_charset_info, tmp_name); + /* + If the old table had partitions and we are doing ALTER TABLE ... + engine= <new_engine>, the new table must preserve the original + partitioning. This means that the new engine is still the + partitioning engine, not the engine specified in the parser. + This is discovered in prep_alter_part_table, which in such case + updates create_info->db_type. + It's therefore important that the assignment below is done + after prep_alter_part_table. + */ + handlerton *new_db_type= create_info->db_type; + handlerton *old_db_type= table->s->db_type(); + TABLE *new_table= NULL; + ha_rows copied=0,deleted=0; /* Handling of symlinked tables: @@ -6782,287 +7699,337 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, Copy data. Remove old table and symlinks. */ - if (!strcmp(db, new_db)) // Ignore symlink if db changed + char index_file[FN_REFLEN], data_file[FN_REFLEN]; + + if (!alter_ctx.is_database_changed()) { if (create_info->index_file_name) { /* Fix index_file_name to have 'tmp_name' as basename */ - strmov(index_file, tmp_name); + strmov(index_file, alter_ctx.tmp_name); create_info->index_file_name=fn_same(index_file, - create_info->index_file_name, - 1); + create_info->index_file_name, + 1); } if (create_info->data_file_name) { /* Fix data_file_name to have 'tmp_name' as basename */ - strmov(data_file, tmp_name); + strmov(data_file, alter_ctx.tmp_name); create_info->data_file_name=fn_same(data_file, - create_info->data_file_name, - 1); + create_info->data_file_name, + 1); } } else + { + /* Ignore symlink if db is changed. */ create_info->data_file_name=create_info->index_file_name=0; + } DEBUG_SYNC(thd, "alter_table_before_create_table_no_lock"); DBUG_EXECUTE_IF("sleep_before_create_table_no_lock", my_sleep(100000);); + /* We can abort alter table for any table type */ + thd->abort_on_warning= !ignore && thd->is_strict_mode(); + /* - Create a table with a temporary name. - With create_info->frm_only == 1 this creates a .frm file only and - we keep the original row format. + Create .FRM for new version of table with a temporary name. We don't log the statement, it will be logged later. + + Keep information about keys in newly created table as it + will be used later to construct Alter_inplace_info object + and by fill_alter_inplace_info() call. */ - if (need_copy_table == ALTER_TABLE_METADATA_ONLY) - { - DBUG_ASSERT(create_info->frm_only); - /* Ensure we keep the original table format */ - create_info->table_options= ((create_info->table_options & - ~HA_OPTION_PACK_RECORD) | - (table->s->db_create_options & - HA_OPTION_PACK_RECORD)); - } + KEY *key_info; + uint key_count; + /* + Remember if the new definition has new VARCHAR column; + create_info->varchar will be reset in create_table_impl()/ + mysql_prepare_create_table(). + */ + bool varchar= create_info->varchar; + tmp_disable_binlog(thd); - error= mysql_create_table_no_lock(thd, new_db, tmp_name, - create_info, - alter_info, - 1, 0, NULL); + error= create_table_impl(thd, alter_ctx.new_db, alter_ctx.tmp_name, + alter_ctx.get_tmp_path(), + create_info, alter_info, + true, true, NULL, + &key_info, &key_count, FALSE); reenable_binlog(thd); + thd->abort_on_warning= false; if (error) - goto err; + DBUG_RETURN(true); + + /* Remember that we have not created table in storage engine yet. */ + bool no_ha_table= true; - /* Open the table if we need to copy the data. */ - DBUG_PRINT("info", ("need_copy_table: %u", need_copy_table)); - if (need_copy_table != ALTER_TABLE_METADATA_ONLY) + if (alter_info->requested_algorithm != Alter_info::ALTER_TABLE_ALGORITHM_COPY) { - if (table->s->tmp_table) + Alter_inplace_info ha_alter_info(create_info, alter_info, + key_info, key_count, +#ifdef WITH_PARTITION_STORAGE_ENGINE + thd->work_part_info, +#else + NULL, +#endif + ignore); + TABLE *altered_table= NULL; + bool use_inplace= true; + + /* Fill the Alter_inplace_info structure. */ + if (fill_alter_inplace_info(thd, table, varchar, &ha_alter_info)) + goto err_new_table_cleanup; + + // We assume that the table is non-temporary. + DBUG_ASSERT(!table->s->tmp_table); + + if (!(altered_table= open_table_uncached(thd, alter_ctx.get_tmp_path(), + alter_ctx.new_db, + alter_ctx.tmp_name, + true, false))) + goto err_new_table_cleanup; + + /* Set markers for fields in TABLE object for altered table. */ + update_altered_table(ha_alter_info, altered_table); + + /* + Mark all columns in 'altered_table' as used to allow usage + of its record[0] buffer and Field objects during in-place + ALTER TABLE. + */ + altered_table->column_bitmaps_set_no_signal(&altered_table->s->all_set, + &altered_table->s->all_set); + + if (ha_alter_info.handler_flags == 0) { - Open_table_context ot_ctx(thd, (MYSQL_OPEN_IGNORE_FLUSH | - MYSQL_LOCK_IGNORE_TIMEOUT)); - TABLE_LIST tbl; - bzero((void*) &tbl, sizeof(tbl)); - tbl.db= new_db; - tbl.table_name= tbl.alias= tmp_name; - /* Table is in thd->temporary_tables */ - (void) open_table(thd, &tbl, thd->mem_root, &ot_ctx); - new_table= tbl.table; + /* + No-op ALTER, no need to call handler API functions. + + If this code path is entered for an ALTER statement that + should not be a real no-op, new handler flags should be added + and fill_alter_inplace_info() adjusted. + + Note that we can end up here if an ALTER statement has clauses + that cancel each other out (e.g. ADD/DROP identically index). + + Also note that we ignore the LOCK clause here. + */ + close_temporary_table(thd, altered_table, true, false); + goto end_inplace; + } + + // Ask storage engine whether to use copy or in-place + enum_alter_inplace_result inplace_supported= + table->file->check_if_supported_inplace_alter(altered_table, + &ha_alter_info); + + switch (inplace_supported) { + case HA_ALTER_INPLACE_EXCLUSIVE_LOCK: + // If SHARED lock and no particular algorithm was requested, use COPY. + if (alter_info->requested_lock == + Alter_info::ALTER_TABLE_LOCK_SHARED && + alter_info->requested_algorithm == + Alter_info::ALTER_TABLE_ALGORITHM_DEFAULT) + { + use_inplace= false; + } + // Otherwise, if weaker lock was requested, report errror. + else if (alter_info->requested_lock == + Alter_info::ALTER_TABLE_LOCK_NONE || + alter_info->requested_lock == + Alter_info::ALTER_TABLE_LOCK_SHARED) + { + ha_alter_info.report_unsupported_error("LOCK=NONE/SHARED", + "LOCK=EXCLUSIVE"); + close_temporary_table(thd, altered_table, true, false); + goto err_new_table_cleanup; + } + break; + case HA_ALTER_INPLACE_SHARED_LOCK_AFTER_PREPARE: + case HA_ALTER_INPLACE_SHARED_LOCK: + // If weaker lock was requested, report errror. + if (alter_info->requested_lock == + Alter_info::ALTER_TABLE_LOCK_NONE) + { + ha_alter_info.report_unsupported_error("LOCK=NONE", "LOCK=SHARED"); + close_temporary_table(thd, altered_table, true, false); + goto err_new_table_cleanup; + } + break; + case HA_ALTER_INPLACE_NO_LOCK_AFTER_PREPARE: + case HA_ALTER_INPLACE_NO_LOCK: + break; + case HA_ALTER_INPLACE_NOT_SUPPORTED: + // If INPLACE was requested, report error. + if (alter_info->requested_algorithm == + Alter_info::ALTER_TABLE_ALGORITHM_INPLACE) + { + ha_alter_info.report_unsupported_error("ALGORITHM=INPLACE", + "ALGORITHM=COPY"); + close_temporary_table(thd, altered_table, true, false); + goto err_new_table_cleanup; + } + // COPY with LOCK=NONE is not supported, no point in trying. + if (alter_info->requested_lock == + Alter_info::ALTER_TABLE_LOCK_NONE) + { + ha_alter_info.report_unsupported_error("LOCK=NONE", "LOCK=SHARED"); + close_temporary_table(thd, altered_table, true, false); + goto err_new_table_cleanup; + } + // Otherwise use COPY + use_inplace= false; + break; + case HA_ALTER_ERROR: + default: + close_temporary_table(thd, altered_table, true, false); + goto err_new_table_cleanup; + } + + if (use_inplace) + { + if (mysql_inplace_alter_table(thd, table_list, table, + altered_table, + &ha_alter_info, + inplace_supported, &target_mdl_request, + &alter_ctx)) + { + DBUG_RETURN(true); + } + + goto end_inplace; } else { - char path[FN_REFLEN + 1]; - /* table is a normal table: Create temporary table in same directory */ - build_table_filename(path, sizeof(path) - 1, new_db, tmp_name, "", - FN_IS_TMP); - /* Open our intermediate table. */ - new_table= open_table_uncached(thd, path, new_db, tmp_name, TRUE); + close_temporary_table(thd, altered_table, true, false); + } + } + + /* ALTER TABLE using copy algorithm. */ + + /* Check if ALTER TABLE is compatible with foreign key definitions. */ + if (fk_prepare_copy_alter_table(thd, table, alter_info, &alter_ctx)) + goto err_new_table_cleanup; + + if (!table->s->tmp_table) + { + // COPY algorithm doesn't work with concurrent writes. + if (alter_info->requested_lock == Alter_info::ALTER_TABLE_LOCK_NONE) + { + my_error(ER_ALTER_OPERATION_NOT_SUPPORTED_REASON, MYF(0), + "LOCK=NONE", + ER(ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_COPY), + "LOCK=SHARED"); + goto err_new_table_cleanup; } - if (!new_table) + + // If EXCLUSIVE lock is requested, upgrade already. + if (alter_info->requested_lock == Alter_info::ALTER_TABLE_LOCK_EXCLUSIVE && + wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN)) goto err_new_table_cleanup; + /* - Note: In case of MERGE table, we do not attach children. We do not - copy data for MERGE tables. Only the children have data. + Otherwise upgrade to SHARED_NO_WRITE. + Note that under LOCK TABLES, we will already have SHARED_NO_READ_WRITE. */ + if (alter_info->requested_lock != Alter_info::ALTER_TABLE_LOCK_EXCLUSIVE && + thd->mdl_context.upgrade_shared_lock(mdl_ticket, MDL_SHARED_NO_WRITE, + thd->variables.lock_wait_timeout)) + goto err_new_table_cleanup; + + DEBUG_SYNC(thd, "alter_table_copy_after_lock_upgrade"); } - /* Check if we can do the ALTER TABLE as online */ - if (require_online) + // It's now safe to take the table level lock. + if (lock_tables(thd, table_list, alter_ctx.tables_opened, 0)) + goto err_new_table_cleanup; + { - if (index_add_count || index_drop_count || - (new_table && - !(new_table->file->ha_table_flags() & HA_NO_COPY_ON_ALTER))) - { - my_error(ER_CANT_DO_ONLINE, MYF(0), "ALTER"); + if (ha_create_table(thd, alter_ctx.get_tmp_path(), + alter_ctx.new_db, alter_ctx.tmp_name, + create_info, false)) goto err_new_table_cleanup; + + /* Mark that we have created table in storage engine. */ + no_ha_table= false; + + if (create_info->options & HA_LEX_CREATE_TMP_TABLE) + { + if (!open_table_uncached(thd, alter_ctx.get_tmp_path(), + alter_ctx.new_db, alter_ctx.tmp_name, + true, true)) + goto err_new_table_cleanup; } } + + /* Open the table since we need to copy the data. */ + if (table->s->tmp_table != NO_TMP_TABLE) + { + TABLE_LIST tbl; + tbl.init_one_table(alter_ctx.new_db, strlen(alter_ctx.new_db), + alter_ctx.tmp_name, strlen(alter_ctx.tmp_name), + alter_ctx.tmp_name, TL_READ_NO_INSERT); + /* Table is in thd->temporary_tables */ + (void) open_temporary_table(thd, &tbl); + new_table= tbl.table; + } + else + { + /* table is a normal table: Create temporary table in same directory */ + /* Open our intermediate table. */ + new_table= open_table_uncached(thd, alter_ctx.get_tmp_path(), + alter_ctx.new_db, alter_ctx.tmp_name, + true, true); + } + if (!new_table) + goto err_new_table_cleanup; + /* + Note: In case of MERGE table, we do not attach children. We do not + copy data for MERGE tables. Only the children have data. + */ + /* Copy the data if necessary. */ thd->count_cuted_fields= CHECK_FIELD_WARN; // calc cuted fields thd->cuted_fields=0L; - copied=deleted=0; /* We do not copy data for MERGE tables. Only the children have data. MERGE tables have HA_NO_COPY_ON_ALTER set. */ - if (new_table && !(new_table->file->ha_table_flags() & HA_NO_COPY_ON_ALTER)) + if (!(new_table->file->ha_table_flags() & HA_NO_COPY_ON_ALTER)) { new_table->next_number_field=new_table->found_next_number_field; + THD_STAGE_INFO(thd, stage_copy_to_tmp_table); DBUG_EXECUTE_IF("abort_copy_table", { my_error(ER_LOCK_WAIT_TIMEOUT, MYF(0)); goto err_new_table_cleanup; }); - error= copy_data_between_tables(thd, table, new_table, - alter_info->create_list, ignore, - order_num, order, &copied, &deleted, - alter_info->keys_onoff, - alter_info->error_if_not_empty); + if (copy_data_between_tables(thd, table, new_table, + alter_info->create_list, ignore, + order_num, order, &copied, &deleted, + alter_info->keys_onoff, + &alter_ctx)) + goto err_new_table_cleanup; } else { - /* - Ensure that we will upgrade the metadata lock if - handler::enable/disable_indexes() will be called. - */ - if (alter_info->keys_onoff != LEAVE_AS_IS || - table->file->indexes_are_disabled()) - need_lock_for_indexes= true; - if (!table->s->tmp_table && need_lock_for_indexes && - wait_while_table_is_used(thd, table, extra_func)) + /* Should be MERGE only */ + DBUG_ASSERT(new_table->file->ht->db_type == DB_TYPE_MRG_MYISAM); + if (!table->s->tmp_table && + wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN)) goto err_new_table_cleanup; THD_STAGE_INFO(thd, stage_manage_keys); DEBUG_SYNC(thd, "alter_table_manage_keys"); alter_table_manage_keys(table, table->file->indexes_are_disabled(), alter_info->keys_onoff); - error= trans_commit_stmt(thd); - if (trans_commit_implicit(thd)) - error= 1; - } - thd->count_cuted_fields= CHECK_FIELD_IGNORE; - - if (error) - goto err_new_table_cleanup; - - /* If we did not need to copy, we might still need to add/drop indexes. */ - if (! new_table) - { - uint *key_numbers; - uint *keyno_p; - KEY *key_info; - KEY *key; - uint *idx_p; - uint *idx_end_p; - KEY_PART_INFO *key_part; - KEY_PART_INFO *part_end; - DBUG_PRINT("info", ("No new_table, checking add/drop index")); - - table->file->ha_prepare_for_alter(); - if (index_add_count) - { - /* The add_index() method takes an array of KEY structs. */ - key_info= (KEY*) thd->alloc(sizeof(KEY) * index_add_count); - key= key_info; - for (idx_p= index_add_buffer, idx_end_p= idx_p + index_add_count; - idx_p < idx_end_p; - idx_p++, key++) - { - /* Copy the KEY struct. */ - *key= key_info_buffer[*idx_p]; - /* Fix the key parts. */ - part_end= key->key_part + key->user_defined_key_parts; - for (key_part= key->key_part; key_part < part_end; key_part++) - key_part->field= table->field[key_part->fieldnr]; - } - /* Add the indexes. */ - if ((error= table->file->add_index(table, key_info, index_add_count, - &add))) - { - /* Only report error if handler has not already reported an error */ - if (!thd->is_error()) - { - /* - Exchange the key_info for the error message. If we exchange - key number by key name in the message later, we need correct info. - */ - KEY *save_key_info= table->key_info; - table->key_info= key_info; - table->file->print_error(error, MYF(0)); - table->key_info= save_key_info; - } - goto err_new_table_cleanup; - } - pending_inplace_add_index= true; - } - /*end of if (index_add_count)*/ - - if (index_drop_count) - { - /* Currently we must finalize add index if we also drop indexes */ - if (pending_inplace_add_index) - { - /* Committing index changes needs exclusive metadata lock. */ - DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, - table_list->db, - table_list->table_name, - MDL_EXCLUSIVE)); - if ((error= table->file->final_add_index(add, true))) - { - table->file->print_error(error, MYF(0)); - goto err_new_table_cleanup; - } - pending_inplace_add_index= false; - } - /* The prepare_drop_index() method takes an array of key numbers. */ - key_numbers= (uint*) thd->alloc(sizeof(uint) * index_drop_count); - keyno_p= key_numbers; - /* Get the number of each key. */ - for (idx_p= index_drop_buffer, idx_end_p= idx_p + index_drop_count; - idx_p < idx_end_p; - idx_p++, keyno_p++) - *keyno_p= *idx_p; - /* - Tell the handler to prepare for drop indexes. - This re-numbers the indexes to get rid of gaps. - */ - error= table->file->prepare_drop_index(table, key_numbers, - index_drop_count); - if (!error) - { - /* Tell the handler to finally drop the indexes. */ - error= table->file->final_drop_index(table); - } - - if (error) - { - table->file->print_error(error, MYF(0)); - if (index_add_count) // Drop any new indexes added. - { - /* - Temporarily set table-key_info to include information about the - indexes added above that we now need to drop. - */ - KEY *save_key_info= table->key_info; - table->key_info= key_info_buffer; - if ((error= table->file->prepare_drop_index(table, index_add_buffer, - index_add_count))) - table->file->print_error(error, MYF(0)); - else if ((error= table->file->final_drop_index(table))) - table->file->print_error(error, MYF(0)); - table->key_info= save_key_info; - } - - /* - Mark this TABLE instance as stale to avoid - out-of-sync index information. - */ - table->m_needs_reopen= true; - goto err_new_table_cleanup; - } - } - /*end of if (index_drop_count)*/ - - /* - The final .frm file is already created as a temporary file - and will be renamed to the original table name later. - */ - - /* Need to commit before a table is unlocked (NDB requirement). */ - DBUG_PRINT("info", ("Committing before unlocking table")); if (trans_commit_stmt(thd) || trans_commit_implicit(thd)) goto err_new_table_cleanup; } - /*end of if (! new_table) for add/drop index*/ - - DBUG_ASSERT(error == 0); + thd->count_cuted_fields= CHECK_FIELD_IGNORE; if (table->s->tmp_table != NO_TMP_TABLE) { - /* - In-place operations are not supported for temporary tables, so - we don't have to call final_add_index() in this case. The assert - verifies that in-place add index has not been done. - */ - DBUG_ASSERT(!pending_inplace_add_index); /* Close lock if this is a transactional table */ if (thd->lock) { @@ -7070,7 +8037,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, thd->locked_tables_mode != LTM_PRELOCKED_UNDER_LOCK_TABLES) { mysql_unlock_tables(thd, thd->lock); - thd->lock=0; + thd->lock= NULL; } else { @@ -7082,14 +8049,15 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, } } /* Remove link to old table and rename the new one */ - close_temporary_table(thd, table, 1, 1); + close_temporary_table(thd, table, true, true); /* Should pass the 'new_name' as we store table name in the cache */ - if (rename_temporary_table(thd, new_table, new_db, new_name)) + if (rename_temporary_table(thd, new_table, + alter_ctx.new_db, alter_ctx.new_name)) goto err_new_table_cleanup; /* We don't replicate alter table statement on temporary tables */ if (!thd->is_current_stmt_binlog_format_row() && - write_bin_log(thd, TRUE, thd->query(), thd->query_length())) - DBUG_RETURN(TRUE); + write_bin_log(thd, true, thd->query(), thd->query_length())) + DBUG_RETURN(true); goto end_temporary; } @@ -7098,11 +8066,9 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, not delete it! Even altough MERGE tables do not have their children attached here it is safe to call close_temporary_table(). */ - if (new_table) - { - close_temporary_table(thd, new_table, 1, 0); - new_table= 0; - } + close_temporary_table(thd, new_table, true, false); + new_table= NULL; + DEBUG_SYNC(thd, "alter_table_before_rename_result_table"); /* @@ -7123,177 +8089,80 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, */ THD_STAGE_INFO(thd, stage_rename_result_table); - my_snprintf(old_name, sizeof(old_name), "%s2-%lx-%lx", tmp_file_prefix, - current_pid, thd->thread_id); - if (lower_case_table_names) - my_casedn_str(files_charset_info, old_name); if (wait_while_table_is_used(thd, table, HA_EXTRA_PREPARE_FOR_RENAME)) - { - if (pending_inplace_add_index) - { - pending_inplace_add_index= false; - table->file->final_add_index(add, false); - } - // Mark this TABLE instance as stale to avoid out-of-sync index information. - table->m_needs_reopen= true; goto err_new_table_cleanup; - } - if (pending_inplace_add_index) - { - pending_inplace_add_index= false; - DBUG_EXECUTE_IF("alter_table_rollback_new_index", { - table->file->final_add_index(add, false); - my_error(ER_UNKNOWN_ERROR, MYF(0)); - goto err_new_table_cleanup; - }); - if ((error= table->file->final_add_index(add, true))) - { - table->file->print_error(error, MYF(0)); - goto err_new_table_cleanup; - } - } close_all_tables_for_name(thd, table->s, - new_name != table_name || new_db != db ? - HA_EXTRA_PREPARE_FOR_RENAME : - HA_EXTRA_NOT_USED); - - error=0; - table_list->table= table= 0; /* Safety */ - save_old_db_type= old_db_type; + alter_ctx.is_table_renamed() ? + HA_EXTRA_PREPARE_FOR_RENAME: + HA_EXTRA_NOT_USED, + NULL); + table_list->table= table= NULL; /* Safety */ /* - This leads to the storage engine (SE) not being notified for renames in - mysql_rename_table(), because we just juggle with the FRM and nothing - more. If we have an intermediate table, then we notify the SE that - it should become the actual table. Later, we will recycle the old table. - However, in case of ALTER TABLE RENAME there might be no intermediate - table. This is when the old and new tables are compatible, according to - mysql_compare_table(). Then, we need one additional call to - mysql_rename_table() with flag NO_FRM_RENAME, which does nothing else but - actual rename in the SE and the FRM is not touched. Note that, if the - table is renamed and the SE is also changed, then an intermediate table - is created and the additional call will not take place. + Rename the old table to temporary name to have a backup in case + anything goes wrong while renaming the new table. */ - - if (new_name != table_name || new_db != db) + char backup_name[32]; + my_snprintf(backup_name, sizeof(backup_name), "%s2-%lx-%lx", tmp_file_prefix, + current_pid, thd->thread_id); + if (lower_case_table_names) + my_casedn_str(files_charset_info, backup_name); + if (mysql_rename_table(old_db_type, alter_ctx.db, alter_ctx.table_name, + alter_ctx.db, backup_name, FN_TO_IS_TMP)) { - LEX_STRING new_db_name= { new_db, strlen(new_db) }; - LEX_STRING new_table_name= { new_name, strlen(new_name) }; - (void) rename_table_in_stat_tables(thd, &old_db_name, &old_table_name, - &new_db_name, &new_table_name); + // Rename to temporary name failed, delete the new table, abort ALTER. + (void) quick_rm_table(thd, new_db_type, alter_ctx.new_db, + alter_ctx.tmp_name, FN_IS_TMP); + goto err_with_mdl; } - if (need_copy_table == ALTER_TABLE_METADATA_ONLY) - { - DBUG_ASSERT(new_db_type == old_db_type); - /* This type cannot happen in regular ALTER. */ - new_db_type= old_db_type= NULL; - } - if (mysql_rename_table(old_db_type, db, table_name, db, old_name, - FN_TO_IS_TMP)) - { - error=1; - (void) quick_rm_table(new_db_type, new_db, tmp_name, FN_IS_TMP); - } - else if (mysql_rename_table(new_db_type, new_db, tmp_name, new_db, - new_alias, FN_FROM_IS_TMP)) - { - /* Try to get everything back. */ - error= 1; - (void) quick_rm_table(new_db_type, new_db, tmp_name, FN_IS_TMP); - (void) mysql_rename_table(old_db_type, db, old_name, db, alias, - FN_FROM_IS_TMP); - } - else if (new_name != table_name || new_db != db) + // Rename the new table to the correct name. + if (mysql_rename_table(new_db_type, alter_ctx.new_db, alter_ctx.tmp_name, + alter_ctx.new_db, alter_ctx.new_alias, + FN_FROM_IS_TMP)) { - if (need_copy_table == ALTER_TABLE_METADATA_ONLY && - mysql_rename_table(save_old_db_type, db, table_name, new_db, - new_alias, NO_FRM_RENAME)) - { - /* Try to get everything back. */ - error= 1; - (void) quick_rm_table(new_db_type, new_db, new_alias, 0); - (void) mysql_rename_table(old_db_type, db, old_name, db, alias, - FN_FROM_IS_TMP); - } - else if (Table_triggers_list::change_table_name(thd, db, alias, - table_name, new_db, - new_alias)) - { - /* Try to get everything back. */ - error= 1; - (void) quick_rm_table(new_db_type, new_db, new_alias, 0); - (void) mysql_rename_table(old_db_type, db, old_name, db, - alias, FN_FROM_IS_TMP); - /* - If we were performing "fast"/in-place ALTER TABLE we also need - to restore old name of table in storage engine as a separate - step, as the above rename affects .FRM only. - */ - if (need_copy_table == ALTER_TABLE_METADATA_ONLY) - { - (void) mysql_rename_table(save_old_db_type, new_db, new_alias, - db, table_name, NO_FRM_RENAME); - } - } + // Rename failed, delete the temporary table. + (void) quick_rm_table(thd, new_db_type, alter_ctx.new_db, + alter_ctx.tmp_name, FN_IS_TMP); + // Restore the backup of the original table to the old name. + (void) mysql_rename_table(old_db_type, alter_ctx.db, backup_name, + alter_ctx.db, alter_ctx.alias, FN_FROM_IS_TMP); + goto err_with_mdl; } - if (! error) - (void) quick_rm_table(old_db_type, db, old_name, FN_IS_TMP); - - if (error) - { - /* This shouldn't happen. But let us play it safe. */ + // Check if we renamed the table and if so update trigger files. + if (alter_ctx.is_table_renamed() && + Table_triggers_list::change_table_name(thd, + alter_ctx.db, + alter_ctx.alias, + alter_ctx.table_name, + alter_ctx.new_db, + alter_ctx.new_alias)) + { + // Rename succeeded, delete the new table. + (void) quick_rm_table(thd, new_db_type, + alter_ctx.new_db, alter_ctx.new_alias, 0); + // Restore the backup of the original table to the old name. + (void) mysql_rename_table(old_db_type, alter_ctx.db, backup_name, + alter_ctx.db, alter_ctx.alias, FN_FROM_IS_TMP); goto err_with_mdl; } - if (need_copy_table == ALTER_TABLE_METADATA_ONLY) + // ALTER TABLE succeeded, delete the backup of the old table. + if (quick_rm_table(thd, old_db_type, alter_ctx.db, backup_name, FN_IS_TMP)) { /* - Now we have to inform handler that new .FRM file is in place. - To do this we need to obtain a handler object for it. - NO need to tamper with MERGE tables. The real open is done later. + The fact that deletion of the backup failed is not critical + error, but still worth reporting as it might indicate serious + problem with server. */ - Open_table_context ot_ctx(thd, MYSQL_OPEN_REOPEN); - TABLE_LIST temp_table_list; - TABLE_LIST *t_table_list; - if (new_name != table_name || new_db != db) - { - temp_table_list.init_one_table(new_db, strlen(new_db), - new_name, strlen(new_name), - new_name, TL_READ_NO_INSERT); - temp_table_list.mdl_request.ticket= target_mdl_request.ticket; - t_table_list= &temp_table_list; - } - else - { - /* - Under LOCK TABLES, we have a different mdl_lock_ticket - points to a different instance than the one set initially - to request the lock. - */ - table_list->mdl_request.ticket= mdl_ticket; - t_table_list= table_list; - } - if (open_table(thd, t_table_list, thd->mem_root, &ot_ctx)) - { - goto err_with_mdl; - } - - /* Tell the handler that a new frm file is in place. */ - error= t_table_list->table->file->ha_create_handler_files(path, NULL, - CHF_INDEX_FLAG, - create_info); + goto err_with_mdl; + } - DBUG_ASSERT(thd->open_tables == t_table_list->table); - close_thread_table(thd, &thd->open_tables); - t_table_list->table= NULL; +end_inplace: - if (error) - goto err_with_mdl; - } if (thd->locked_tables_list.reopen_tables(thd)) goto err_with_mdl; @@ -7304,61 +8173,75 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, ha_binlog_log_query(thd, create_info->db_type, LOGCOM_ALTER_TABLE, thd->query(), thd->query_length(), - db, table_name); + alter_ctx.db, alter_ctx.table_name); DBUG_ASSERT(!(mysql_bin_log.is_open() && thd->is_current_stmt_binlog_format_row() && (create_info->options & HA_LEX_CREATE_TMP_TABLE))); - if (write_bin_log(thd, TRUE, thd->query(), thd->query_length())) - DBUG_RETURN(TRUE); + if (write_bin_log(thd, true, thd->query(), thd->query_length())) + DBUG_RETURN(true); - table_list->table=0; // For query cache - query_cache_invalidate3(thd, table_list, 0); + if (ha_check_storage_engine_flag(old_db_type, HTON_FLUSH_AFTER_RENAME)) + { + /* + For the alter table to be properly flushed to the logs, we + have to open the new table. If not, we get a problem on server + shutdown. But we do not need to attach MERGE children. + */ + TABLE *t_table; + t_table= open_table_uncached(thd, alter_ctx.get_new_path(), + alter_ctx.new_db, alter_ctx.new_name, + false, true); + if (t_table) + intern_close_table(t_table); + else + sql_print_warning("Could not open table %s.%s after rename\n", + alter_ctx.new_db, alter_ctx.table_name); + ha_flush_logs(old_db_type); + } + table_list->table= NULL; // For query cache + query_cache_invalidate3(thd, table_list, false); if (thd->locked_tables_mode == LTM_LOCK_TABLES || thd->locked_tables_mode == LTM_PRELOCKED_UNDER_LOCK_TABLES) { - if ((new_name != table_name || new_db != db)) + if (alter_ctx.is_table_renamed()) thd->mdl_context.release_all_locks_for_name(mdl_ticket); else - mdl_ticket->downgrade_exclusive_lock(MDL_SHARED_NO_READ_WRITE); + mdl_ticket->downgrade_lock(MDL_SHARED_NO_READ_WRITE); } end_temporary: - my_snprintf(tmp_name, sizeof(tmp_name), ER(ER_INSERT_INFO), + my_snprintf(alter_ctx.tmp_name, sizeof(alter_ctx.tmp_name), + ER(ER_INSERT_INFO), (ulong) (copied + deleted), (ulong) deleted, - (ulong) thd->warning_info->statement_warn_count()); - my_ok(thd, copied + deleted, 0L, tmp_name); - DBUG_RETURN(FALSE); + (ulong) thd->get_stmt_da()->current_statement_warn_count()); + my_ok(thd, copied + deleted, 0L, alter_ctx.tmp_name); + DBUG_RETURN(false); err_new_table_cleanup: if (new_table) { /* close_temporary_table() frees the new_table pointer. */ - close_temporary_table(thd, new_table, 1, 1); + close_temporary_table(thd, new_table, true, true); } else - (void) quick_rm_table(new_db_type, new_db, tmp_name, - create_info->frm_only ? FN_IS_TMP | FRM_ONLY : FN_IS_TMP); + (void) quick_rm_table(thd, new_db_type, + alter_ctx.new_db, alter_ctx.tmp_name, + (FN_IS_TMP | (no_ha_table ? NO_HA_TABLE : 0))); -err: -#ifdef WITH_PARTITION_STORAGE_ENGINE - /* If prep_alter_part_table created an intermediate table, destroy it. */ - if (table_for_fast_alter_partition) - close_temporary(table_for_fast_alter_partition, 1, 0); -#endif /* WITH_PARTITION_STORAGE_ENGINE */ /* No default value was provided for a DATE/DATETIME field, the current sql_mode doesn't allow the '0000-00-00' value and the table to be altered isn't empty. Report error here. */ - if (alter_info->error_if_not_empty && - thd->warning_info->current_row_for_warning()) + if (alter_ctx.error_if_not_empty && + thd->get_stmt_da()->current_row_for_warning()) { const char *f_val= 0; enum enum_mysql_timestamp_type t_type= MYSQL_TIMESTAMP_DATE; - switch (alter_info->datetime_field->sql_type) + switch (alter_ctx.datetime_field->sql_type) { case MYSQL_TYPE_DATE: case MYSQL_TYPE_NEWDATE: @@ -7374,14 +8257,14 @@ err: DBUG_ASSERT(0); } bool save_abort_on_warning= thd->abort_on_warning; - thd->abort_on_warning= TRUE; - make_truncated_value_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + thd->abort_on_warning= true; + make_truncated_value_warning(thd, Sql_condition::WARN_LEVEL_WARN, f_val, strlength(f_val), t_type, - alter_info->datetime_field->field_name); + alter_ctx.datetime_field->field_name); thd->abort_on_warning= save_abort_on_warning; } - DBUG_RETURN(TRUE); + DBUG_RETURN(true); err_with_mdl: /* @@ -7392,13 +8275,10 @@ err_with_mdl: */ thd->locked_tables_list.unlink_all_closed_tables(thd, NULL, 0); thd->mdl_context.release_all_locks_for_name(mdl_ticket); - DBUG_RETURN(TRUE); + DBUG_RETURN(true); } -/* Copy all rows from one table to another */ - - /** Prepare the transaction for the alter table's copy phase. @@ -7447,16 +8327,14 @@ bool mysql_trans_commit_alter_copy_data(THD *thd) static int -copy_data_between_tables(THD *thd, TABLE *from,TABLE *to, - List<Create_field> &create, - bool ignore, +copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, + List<Create_field> &create, bool ignore, uint order_num, ORDER *order, - ha_rows *copied, - ha_rows *deleted, - enum enum_enable_or_disable keys_onoff, - bool error_if_not_empty) + ha_rows *copied, ha_rows *deleted, + Alter_info::enum_enable_or_disable keys_onoff, + Alter_table_ctx *alter_ctx) { - int error= 1, errpos= 0; + int error= 1; Copy_field *copy= NULL, *copy_end; ha_rows found_count= 0, delete_count= 0; uint length= 0; @@ -7470,8 +8348,6 @@ copy_data_between_tables(THD *thd, TABLE *from,TABLE *to, bool auto_increment_field_copied= 0; ulonglong save_sql_mode= thd->variables.sql_mode; ulonglong prev_insert_id, time_to_report_progress; - List_iterator<Create_field> it(create); - Create_field *def; Field **dfield_ptr= to->default_field; DBUG_ENTER("copy_data_between_tables"); @@ -7479,15 +8355,13 @@ copy_data_between_tables(THD *thd, TABLE *from,TABLE *to, thd_progress_init(thd, 2 + test(order)); if (mysql_trans_prepare_alter_copy_data(thd)) - goto err; - errpos=1; + DBUG_RETURN(-1); if (!(copy= new Copy_field[to->s->fields])) - goto err; /* purecov: inspected */ + DBUG_RETURN(-1); /* purecov: inspected */ if (to->file->ha_external_lock(thd, F_WRLCK)) - goto err; - errpos= 2; + DBUG_RETURN(-1); /* We need external lock before we can disable/enable keys */ alter_table_manage_keys(to, from->file->indexes_are_disabled(), keys_onoff); @@ -7498,8 +8372,9 @@ copy_data_between_tables(THD *thd, TABLE *from,TABLE *to, from->file->info(HA_STATUS_VARIABLE); to->file->ha_start_bulk_insert(from->file->stats.records, ignore ? 0 : HA_CREATE_UNIQUE_INDEX_BY_SORT); - errpos= 3; + List_iterator<Create_field> it(create); + Create_field *def; copy_end=copy; to->s->default_fields= 0; for (Field **ptr=to->field ; *ptr ; ptr++) @@ -7548,7 +8423,7 @@ copy_data_between_tables(THD *thd, TABLE *from,TABLE *to, my_snprintf(warn_buff, sizeof(warn_buff), "ORDER BY ignored as there is a user-defined clustered index" " in the table '%-.192s'", from->s->table_name.str); - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR, + push_warning(thd, Sql_condition::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR, warn_buff); } else @@ -7581,11 +8456,13 @@ copy_data_between_tables(THD *thd, TABLE *from,TABLE *to, to->use_all_columns(); to->mark_virtual_columns_for_write(TRUE); if (init_read_record(&info, thd, from, (SQL_SELECT *) 0, 1, 1, FALSE)) + { + error= 1; goto err; - errpos= 4; - if (ignore) + } + if (ignore && !alter_ctx->fk_error_if_delete_row) to->file->extra(HA_EXTRA_IGNORE_DUP_KEY); - thd->warning_info->reset_current_row_for_warning(); + thd->get_stmt_da()->reset_current_row_for_warning(); restore_record(to, s->default_values); // Create empty record thd->progress.max_counter= from->file->records(); @@ -7609,7 +8486,7 @@ copy_data_between_tables(THD *thd, TABLE *from,TABLE *to, } /* Return error if source table isn't empty. */ - if (error_if_not_empty) + if (alter_ctx->error_if_not_empty) { error= 1; break; @@ -7643,39 +8520,62 @@ copy_data_between_tables(THD *thd, TABLE *from,TABLE *to, to->auto_increment_field_not_null= FALSE; if (error) { - if (!ignore || - to->file->is_fatal_error(error, HA_CHECK_DUP)) + if (to->file->is_fatal_error(error, HA_CHECK_DUP)) { - if (!to->file->is_fatal_error(error, HA_CHECK_DUP)) - { - uint key_nr= to->file->get_dup_key(error); - if ((int) key_nr >= 0) - { - const char *err_msg= ER(ER_DUP_ENTRY_WITH_KEY_NAME); - if (key_nr == 0 && - (to->key_info[0].key_part[0].field->flags & - AUTO_INCREMENT_FLAG)) - err_msg= ER(ER_DUP_ENTRY_AUTOINCREMENT_CASE); - to->file->print_keydup_error(table, &table->key_info[key_nr], - err_msg, MYF(0)); - break; - } - } - - to->file->print_error(error,MYF(0)); + /* Not a duplicate key error. */ + to->file->print_error(error, MYF(0)); break; } - to->file->restore_auto_increment(prev_insert_id); - delete_count++; + else + { + /* Duplicate key error. */ + if (alter_ctx->fk_error_if_delete_row) + { + /* + We are trying to omit a row from the table which serves as parent + in a foreign key. This might have broken referential integrity so + emit an error. Note that we can't ignore this error even if we are + executing ALTER IGNORE TABLE. IGNORE allows to skip rows, but + doesn't allow to break unique or foreign key constraints, + */ + my_error(ER_FK_CANNOT_DELETE_PARENT, MYF(0), + alter_ctx->fk_error_id, + alter_ctx->fk_error_table); + break; + } + + if (ignore) + { + /* This ALTER IGNORE TABLE. Simply skip row and continue. */ + to->file->restore_auto_increment(prev_insert_id); + delete_count++; + } + else + { + /* Ordinary ALTER TABLE. Report duplicate key error. */ + uint key_nr= to->file->get_dup_key(error); + if ((int) key_nr >= 0) + { + const char *err_msg= ER(ER_DUP_ENTRY_WITH_KEY_NAME); + if (key_nr == 0 && + (to->key_info[0].key_part[0].field->flags & + AUTO_INCREMENT_FLAG)) + err_msg= ER(ER_DUP_ENTRY_AUTOINCREMENT_CASE); + print_keydup_error(to, key_nr == MAX_KEY ? NULL : + &to->key_info[key_nr], + err_msg, MYF(0)); + } + else + to->file->print_error(error, MYF(0)); + break; + } + } } else found_count++; - thd->warning_info->inc_current_row_for_warning(); + thd->get_stmt_da()->inc_current_row_for_warning(); } - -err: - if (errpos >= 4) - end_read_record(&info); + end_read_record(&info); free_io_cache(from); delete [] copy; @@ -7687,22 +8587,23 @@ err: /* We are going to drop the temporary table */ to->file->extra(HA_EXTRA_PREPARE_FOR_DROP); } - if (errpos >= 3 && to->file->ha_end_bulk_insert() && error <= 0) + if (to->file->ha_end_bulk_insert() && error <= 0) { to->file->print_error(my_errno,MYF(0)); error= 1; } to->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY); - if (errpos >= 1 && mysql_trans_commit_alter_copy_data(thd)) + if (mysql_trans_commit_alter_copy_data(thd)) error= 1; + err: thd->variables.sql_mode= save_sql_mode; thd->abort_on_warning= 0; *copied= found_count; *deleted=delete_count; to->file->ha_release_auto_increment(); - if (errpos >= 2 && to->file->ha_external_lock(thd,F_UNLCK)) + if (to->file->ha_external_lock(thd,F_UNLCK)) error=1; if (error < 0 && to->file->extra(HA_EXTRA_PREPARE_FOR_RENAME)) error= 1; @@ -7729,13 +8630,6 @@ bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list) DBUG_ENTER("mysql_recreate_table"); DBUG_ASSERT(!table_list->next_global); - /* - table_list->table has been closed and freed. Do not reference - uninitialized data. open_tables() could fail. - */ - table_list->table= NULL; - /* Same applies to MDL ticket. */ - table_list->mdl_request.ticket= NULL; /* Set lock type which is appropriate for ALTER TABLE. */ table_list->lock_type= TL_READ_NO_INSERT; /* Same applies to MDL request. */ @@ -7745,10 +8639,11 @@ bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list) create_info.row_type=ROW_TYPE_NOT_USED; create_info.default_table_charset=default_charset_info; /* Force alter table to recreate table */ - alter_info.flags= (ALTER_CHANGE_COLUMN | ALTER_RECREATE); + alter_info.flags= (Alter_info::ALTER_CHANGE_COLUMN | + Alter_info::ALTER_RECREATE); DBUG_RETURN(mysql_alter_table(thd, NullS, NullS, &create_info, table_list, &alter_info, 0, - (ORDER *) 0, 0, 0)); + (ORDER *) 0, 0)); } @@ -7763,23 +8658,48 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables, field_list.push_back(item = new Item_empty_string("Table", NAME_LEN*2)); item->maybe_null= 1; - field_list.push_back(item= new Item_int("Checksum", (longlong) 1, + field_list.push_back(item= new Item_int("Checksum", + (longlong) 1, MY_INT64_NUM_DECIMAL_DIGITS)); item->maybe_null= 1; if (protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(TRUE); + /* + Close all temporary tables which were pre-open to simplify + privilege checking. Clear all references to closed tables. + */ + close_thread_tables(thd); + for (table= tables; table; table= table->next_local) + table->table= NULL; + /* Open one table after the other to keep lock time as short as possible. */ for (table= tables; table; table= table->next_local) { char table_name[SAFE_NAME_LEN*2+2]; TABLE *t; + TABLE_LIST *save_next_global; strxmov(table_name, table->db ,".", table->table_name, NullS); - t= table->table= open_n_lock_single_table(thd, table, TL_READ, 0); - thd->clear_error(); // these errors shouldn't get client + /* Remember old 'next' pointer and break the list. */ + save_next_global= table->next_global; + table->next_global= NULL; + table->lock_type= TL_READ; + /* Allow to open real tables only. */ + table->required_type= FRMTYPE_TABLE; + + if (open_temporary_tables(thd, table) || + open_and_lock_tables(thd, table, FALSE, 0)) + { + t= NULL; + thd->clear_error(); // these errors shouldn't get client + } + else + t= table->table; + + table->next_global= save_next_global; protocol->prepare_for_resend(); protocol->store(table_name, system_charset_info); @@ -7926,7 +8846,7 @@ static bool check_engine(THD *thd, const char *db_name, if (req_engine && req_engine != *new_engine) { - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_WARN_USING_OTHER_HANDLER, ER(ER_WARN_USING_OTHER_HANDLER), ha_resolve_storage_engine_name(*new_engine), diff --git a/sql/sql_table.h b/sql/sql_table.h index 9d5e768a5a3..5e836602b0d 100644 --- a/sql/sql_table.h +++ b/sql/sql_table.h @@ -20,6 +20,7 @@ #include "my_sys.h" // pthread_mutex_t class Alter_info; +class Alter_table_ctx; class Create_field; struct TABLE_LIST; class THD; @@ -31,7 +32,6 @@ typedef struct st_key KEY; typedef struct st_key_cache KEY_CACHE; typedef struct st_lock_param_type ALTER_PARTITION_PARAM_TYPE; typedef struct st_order ORDER; -class Alter_table_change_level; enum ddl_log_entry_code { @@ -63,10 +63,19 @@ enum ddl_log_action_code DDL_LOG_REPLACE_ACTION: Rename an entity after removing the previous entry with the new name, that is replace this entry. + DDL_LOG_EXCHANGE_ACTION: + Exchange two entities by renaming them a -> tmp, b -> a, tmp -> b. */ DDL_LOG_DELETE_ACTION = 'd', DDL_LOG_RENAME_ACTION = 'r', - DDL_LOG_REPLACE_ACTION = 's' + DDL_LOG_REPLACE_ACTION = 's', + DDL_LOG_EXCHANGE_ACTION = 'e' +}; + +enum enum_ddl_log_exchange_phase { + EXCH_PHASE_NAME_TO_TEMP= 0, + EXCH_PHASE_FROM_TO_NAME= 1, + EXCH_PHASE_TEMP_TO_FROM= 2 }; @@ -75,6 +84,7 @@ typedef struct st_ddl_log_entry const char *name; const char *from_name; const char *handler_name; + const char *tmp_name; uint next_entry; uint entry_pos; enum ddl_log_entry_code entry_type; @@ -113,11 +123,15 @@ enum enum_explain_filename_mode #define WFRM_KEEP_SHARE 8 /* Flags for conversion functions. */ -#define FN_FROM_IS_TMP (1 << 0) -#define FN_TO_IS_TMP (1 << 1) -#define FN_IS_TMP (FN_FROM_IS_TMP | FN_TO_IS_TMP) -#define NO_FRM_RENAME (1 << 2) -#define FRM_ONLY (1 << 3) +static const uint FN_FROM_IS_TMP= 1 << 0; +static const uint FN_TO_IS_TMP= 1 << 1; +static const uint FN_IS_TMP= FN_FROM_IS_TMP | FN_TO_IS_TMP; +static const uint NO_FRM_RENAME= 1 << 2; +static const uint FRM_ONLY= 1 << 3; +/** Don't remove table in engine. Remove only .FRM and maybe .PAR files. */ +static const uint NO_HA_TABLE= 1 << 4; +/** Don't resolve MySQL's fake "foo.sym" symbolic directory names. */ +static const uint SKIP_SYMDIR_ACCESS= 1 << 5; uint filename_to_tablename(const char *from, char *to, uint to_length #ifndef DBUG_OFF @@ -131,38 +145,73 @@ uint build_table_filename(char *buff, size_t bufflen, const char *db, const char *table, const char *ext, uint flags); uint build_table_shadow_filename(char *buff, size_t bufflen, ALTER_PARTITION_PARAM_TYPE *lpt); +uint build_tmptable_filename(THD* thd, char *buff, size_t bufflen); bool check_table_file_presence(char *old_path, char *path, const char *db, const char *table_name, const char *alias, bool issue_error); bool mysql_create_table(THD *thd, TABLE_LIST *create_table, HA_CREATE_INFO *create_info, Alter_info *alter_info); +/* + mysql_create_table_no_lock can be called in one of the following + mutually exclusive situations: + + - Just a normal ordinary CREATE TABLE statement that explicitly + defines the table structure. + + - CREATE TABLE ... SELECT. It is special, because only in this case, + the list of fields is allowed to have duplicates, as long as one of the + duplicates comes from the select list, and the other doesn't. For + example in + + CREATE TABLE t1 (a int(5) NOT NUL) SELECT b+10 as a FROM t2; + + the list in alter_info->create_list will have two fields `a`. + + - ALTER TABLE, that creates a temporary table #sql-xxx, which will be later + renamed to replace the original table. + + - ALTER TABLE as above, but which only modifies the frm file, it only + creates an frm file for the #sql-xxx, the table in the engine is not + created. + + - Assisted discovery, CREATE TABLE statement without the table structure. + + These situations are distinguished by the following "create table mode" + values, where a CREATE ... SELECT is denoted by any non-negative number + (which should be the number of fields in the SELECT ... part), and other + cases use constants as defined below. +*/ +#define C_CREATE_SELECT(X) ((X) > 0 ? (X) : 0) +#define C_ORDINARY_CREATE 0 +#define C_ALTER_TABLE -1 +#define C_ALTER_TABLE_FRM_ONLY -2 +#define C_ASSISTED_DISCOVERY -3 + bool mysql_create_table_no_lock(THD *thd, const char *db, const char *table_name, HA_CREATE_INFO *create_info, - Alter_info *alter_info, - bool tmp_table, uint select_field_count, - bool *is_trans); + Alter_info *alter_info, bool *is_trans, + int create_table_mode); + +int mysql_discard_or_import_tablespace(THD *thd, + TABLE_LIST *table_list, + bool discard); bool mysql_prepare_alter_table(THD *thd, TABLE *table, HA_CREATE_INFO *create_info, - Alter_info *alter_info); + Alter_info *alter_info, + Alter_table_ctx *alter_ctx); bool mysql_trans_prepare_alter_copy_data(THD *thd); bool mysql_trans_commit_alter_copy_data(THD *thd); bool mysql_alter_table(THD *thd, char *new_db, char *new_name, HA_CREATE_INFO *create_info, TABLE_LIST *table_list, Alter_info *alter_info, - uint order_num, ORDER *order, bool ignore, - bool require_online); + uint order_num, ORDER *order, bool ignore); bool mysql_compare_tables(TABLE *table, Alter_info *alter_info, HA_CREATE_INFO *create_info, - uint order_num, - Alter_table_change_level *need_copy_table, - KEY **key_info_buffer, - uint **index_drop_buffer, uint *index_drop_count, - uint **index_add_buffer, uint *index_add_count, - uint *candidate_key_count); + bool *metadata_equal); bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list); bool mysql_create_like_table(THD *thd, TABLE_LIST *table, TABLE_LIST *src_table, @@ -181,7 +230,7 @@ bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists, int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, bool drop_temporary, bool drop_view, bool log_query); -bool quick_rm_table(handlerton *base,const char *db, +bool quick_rm_table(THD *thd, handlerton *base, const char *db, const char *table_name, uint flags); void close_cached_table(THD *thd, TABLE *table); void sp_prepare_create_field(THD *thd, Create_field *sql_field); @@ -205,6 +254,9 @@ bool sync_ddl_log(); void release_ddl_log(); void execute_ddl_log_recovery(); bool execute_ddl_log_entry(THD *thd, uint first_entry); +bool validate_comment_length(THD *thd, const char *comment_str, + size_t *comment_len, uint max_len, + uint err_code, const char *comment_name); bool check_duplicate_warning(THD *thd, char *msg, ulong length); template<typename T> class List; diff --git a/sql/sql_tablespace.cc b/sql/sql_tablespace.cc index 3f6daf7a9ec..48eeb94f7c9 100644 --- a/sql/sql_tablespace.cc +++ b/sql/sql_tablespace.cc @@ -35,7 +35,7 @@ int mysql_alter_tablespace(THD *thd, st_alter_tablespace *ts_info) { hton= ha_default_handlerton(thd); if (ts_info->storage_engine != 0) - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_WARN_USING_OTHER_HANDLER, ER(ER_WARN_USING_OTHER_HANDLER), hton_name(hton)->str, @@ -65,7 +65,7 @@ int mysql_alter_tablespace(THD *thd, st_alter_tablespace *ts_info) } else { - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_ILLEGAL_HA_CREATE_OPTION, ER(ER_ILLEGAL_HA_CREATE_OPTION), hton_name(hton)->str, diff --git a/sql/sql_test.cc b/sql/sql_test.cc index 5b3286d77b0..c79362838e4 100644 --- a/sql/sql_test.cc +++ b/sql/sql_test.cc @@ -92,7 +92,7 @@ static void print_cached_tables(void) { share= (TABLE_SHARE*) my_hash_element(&table_def_cache, idx); - I_P_List_iterator<TABLE, TABLE_share> it(share->used_tables); + TABLE_SHARE::TABLE_list::Iterator it(share->used_tables); while ((entry= it++)) { printf("%-14.14s %-32s%6ld%8ld%6d %s\n", diff --git a/sql/sql_time.cc b/sql/sql_time.cc index 57dbd979933..e989ce6f3a0 100644 --- a/sql/sql_time.cc +++ b/sql/sql_time.cc @@ -299,7 +299,7 @@ str_to_datetime_with_warn(CHARSET_INFO *cs, (flags | (sql_mode_for_dates(thd))), &was_cut); if (was_cut || ts_type <= MYSQL_TIMESTAMP_ERROR) - make_truncated_value_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + make_truncated_value_warning(thd, Sql_condition::WARN_LEVEL_WARN, str, length, flags & TIME_TIME_ONLY ? MYSQL_TIMESTAMP_TIME : ts_type, NullS); return ts_type; @@ -342,7 +342,7 @@ static bool number_to_time_with_warn(bool neg, ulonglong nr, ulong sec_part, if (res < 0 || (was_cut && !(fuzzydate & TIME_FUZZY_DATE))) { make_truncated_value_warning(current_thd, - MYSQL_ERROR::WARN_LEVEL_WARN, str, + Sql_condition::WARN_LEVEL_WARN, str, res < 0 ? MYSQL_TIMESTAMP_ERROR : mysql_type_to_time_type(f_type), field_name); @@ -796,7 +796,7 @@ const char *get_date_time_format_str(KNOWN_DATE_TIME_FORMAT *format, } void make_truncated_value_warning(THD *thd, - MYSQL_ERROR::enum_warning_level level, + Sql_condition::enum_warning_level level, const ErrConv *sval, timestamp_type time_type, const char *field_name) @@ -821,7 +821,7 @@ void make_truncated_value_warning(THD *thd, cs->cset->snprintf(cs, warn_buff, sizeof(warn_buff), ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD), type_str, sval->ptr(), field_name, - (ulong) thd->warning_info->current_row_for_warning()); + (ulong) thd->get_stmt_da()->current_row_for_warning()); else { if (time_type > MYSQL_TIMESTAMP_ERROR) @@ -950,7 +950,7 @@ bool date_add_interval(MYSQL_TIME *ltime, interval_type int_type, return 0; // Ok invalid_date: - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, ER_DATETIME_FUNCTION_OVERFLOW, ER(ER_DATETIME_FUNCTION_OVERFLOW), ltime->time_type == MYSQL_TIMESTAMP_TIME ? diff --git a/sql/sql_time.h b/sql/sql_time.h index c1a75bb2ad3..8ce85712256 100644 --- a/sql/sql_time.h +++ b/sql/sql_time.h @@ -19,7 +19,7 @@ #include "my_global.h" /* ulong */ #include "my_time.h" #include "mysql_time.h" /* timestamp_type */ -#include "sql_error.h" /* MYSQL_ERROR */ +#include "sql_error.h" /* Sql_condition */ #include "structs.h" /* INTERVAL */ typedef enum enum_mysql_timestamp_type timestamp_type; @@ -50,13 +50,14 @@ bool int_to_datetime_with_warn(longlong value, MYSQL_TIME *ltime, ulonglong fuzzydate, const char *name); -void make_truncated_value_warning(THD *thd, MYSQL_ERROR::enum_warning_level level, +void make_truncated_value_warning(THD *thd, + Sql_condition::enum_warning_level level, const ErrConv *str_val, timestamp_type time_type, const char *field_name); static inline void make_truncated_value_warning(THD *thd, - MYSQL_ERROR::enum_warning_level level, const char *str_val, + Sql_condition::enum_warning_level level, const char *str_val, uint str_length, timestamp_type time_type, const char *field_name) { diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index d7d902bc6b0..dafb7ed3eb1 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -157,7 +157,7 @@ Trigger_creation_ctx::create(THD *thd, if (invalid_creation_ctx) { push_warning_printf(thd, - MYSQL_ERROR::WARN_LEVEL_WARN, + Sql_condition::WARN_LEVEL_WARN, ER_TRG_INVALID_CREATION_CTX, ER(ER_TRG_INVALID_CREATION_CTX), (const char *) db_name, @@ -329,9 +329,9 @@ public: virtual bool handle_condition(THD *thd, uint sql_errno, const char* sqlstate, - MYSQL_ERROR::enum_warning_level level, + Sql_condition::enum_warning_level level, const char* message, - MYSQL_ERROR ** cond_hdl) + Sql_condition ** cond_hdl) { if (sql_errno != EE_OUTOFMEMORY && sql_errno != ER_OUT_OF_RESOURCES) @@ -561,7 +561,7 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) if (result) goto end; - close_all_tables_for_name(thd, table->s, HA_EXTRA_NOT_USED); + close_all_tables_for_name(thd, table->s, HA_EXTRA_NOT_USED, NULL); /* Reopen the table if we were under LOCK TABLES. Ignore the return value for now. It's better to @@ -588,7 +588,7 @@ end: with the implicit commit. */ if (thd->locked_tables_mode && tables && lock_upgrade_done) - mdl_ticket->downgrade_exclusive_lock(MDL_SHARED_NO_READ_WRITE); + mdl_ticket->downgrade_lock(MDL_SHARED_NO_READ_WRITE); /* Restore the query table list. Used only for drop trigger. */ if (!create) @@ -802,7 +802,7 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables, lex->definer->user.str)) { push_warning_printf(thd, - MYSQL_ERROR::WARN_LEVEL_NOTE, + Sql_condition::WARN_LEVEL_NOTE, ER_NO_SUCH_USER, ER(ER_NO_SUCH_USER), lex->definer->user.str, @@ -1277,7 +1277,7 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, DBUG_RETURN(1); // EOM } - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_TRG_NO_CREATION_CTX, ER(ER_TRG_NO_CREATION_CTX), (const char*) db, @@ -1461,7 +1461,7 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, warning here. */ - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_TRG_NO_DEFINER, ER(ER_TRG_NO_DEFINER), (const char*) db, (const char*) sp->m_name.str); @@ -1734,7 +1734,7 @@ bool add_table_for_trigger(THD *thd, if (if_exists) { push_warning_printf(thd, - MYSQL_ERROR::WARN_LEVEL_NOTE, + Sql_condition::WARN_LEVEL_NOTE, ER_TRG_DOES_NOT_EXIST, ER(ER_TRG_DOES_NOT_EXIST)); @@ -2214,6 +2214,37 @@ add_tables_and_routines_for_triggers(THD *thd, /** + Check if any of the marked fields are used in the trigger. + + @param used_fields Bitmap over fields to check + @param event_type Type of event triggers for which we are going to inspect + @param action_time Type of trigger action time we are going to inspect +*/ + +bool Table_triggers_list::is_fields_updated_in_trigger(MY_BITMAP *used_fields, + trg_event_type event_type, + trg_action_time_type action_time) +{ + Item_trigger_field *trg_field; + sp_head *sp= bodies[event_type][action_time]; + DBUG_ASSERT(used_fields->n_bits == trigger_table->s->fields); + + for (trg_field= sp->m_trg_table_fields.first; trg_field; + trg_field= trg_field->next_trg_field) + { + /* We cannot check fields which does not present in table. */ + if (trg_field->field_idx != (uint)-1) + { + if (bitmap_is_set(used_fields, trg_field->field_idx) && + trg_field->get_settable_routine_parameter()) + return true; + } + } + return false; +} + + +/** Mark fields of subject table which we read/set in its triggers as such. @@ -2305,7 +2336,7 @@ Handle_old_incorrect_sql_modes_hook::process_unknown_string(char *&unknown_key, DBUG_PRINT("info", ("sql_modes affected by BUG#14090 detected")); push_warning_printf(current_thd, - MYSQL_ERROR::WARN_LEVEL_NOTE, + Sql_condition::WARN_LEVEL_NOTE, ER_OLD_FILE_FORMAT, ER(ER_OLD_FILE_FORMAT), (char *)path, "TRIGGER"); @@ -2346,7 +2377,7 @@ process_unknown_string(char *&unknown_key, uchar* base, MEM_ROOT *mem_root, DBUG_PRINT("info", ("trigger_table affected by BUG#15921 detected")); push_warning_printf(current_thd, - MYSQL_ERROR::WARN_LEVEL_NOTE, + Sql_condition::WARN_LEVEL_NOTE, ER_OLD_FILE_FORMAT, ER(ER_OLD_FILE_FORMAT), (char *)path, "TRIGGER"); diff --git a/sql/sql_trigger.h b/sql/sql_trigger.h index 47b1d19ae54..52892550d35 100644 --- a/sql/sql_trigger.h +++ b/sql/sql_trigger.h @@ -207,6 +207,10 @@ public: Query_tables_list *prelocking_ctx, TABLE_LIST *table_list); + bool is_fields_updated_in_trigger(MY_BITMAP *used_fields, + trg_event_type event_type, + trg_action_time_type action_time); + private: bool prepare_record1_accessors(TABLE *table); LEX_STRING* change_table_name_in_trignames(const char *old_db_name, diff --git a/sql/sql_truncate.cc b/sql/sql_truncate.cc index 4b77344c042..ce6a8c09cdd 100644 --- a/sql/sql_truncate.cc +++ b/sql/sql_truncate.cc @@ -186,12 +186,12 @@ fk_truncate_illegal_if_parent(THD *thd, TABLE *table) @retval > 0 Error code. */ -int Truncate_statement::handler_truncate(THD *thd, TABLE_LIST *table_ref, - bool is_tmp_table) +int Sql_cmd_truncate_table::handler_truncate(THD *thd, TABLE_LIST *table_ref, + bool is_tmp_table) { int error= 0; uint flags; - DBUG_ENTER("Truncate_statement::handler_truncate"); + DBUG_ENTER("Sql_cmd_truncate_table::handler_truncate"); /* Can't recreate, the engine must mechanically delete all rows @@ -279,7 +279,7 @@ static bool recreate_temporary_table(THD *thd, TABLE *table) share->table_name.str, &create_info, 1); if (open_table_uncached(thd, share->path.str, share->db.str, - share->table_name.str, TRUE)) + share->table_name.str, true, true)) { error= FALSE; thd->thread_specific_used= TRUE; @@ -307,11 +307,11 @@ static bool recreate_temporary_table(THD *thd, TABLE *table) @retval TRUE Error. */ -bool Truncate_statement::lock_table(THD *thd, TABLE_LIST *table_ref, - bool *hton_can_recreate) +bool Sql_cmd_truncate_table::lock_table(THD *thd, TABLE_LIST *table_ref, + bool *hton_can_recreate) { TABLE *table= NULL; - DBUG_ENTER("Truncate_statement::lock_table"); + DBUG_ENTER("Sql_cmd_truncate_table::lock_table"); /* Lock types are set in the parser. */ DBUG_ASSERT(table_ref->lock_type == TL_WRITE); @@ -369,7 +369,7 @@ bool Truncate_statement::lock_table(THD *thd, TABLE_LIST *table_ref, m_ticket_downgrade= table->mdl_ticket; /* Close if table is going to be recreated. */ if (*hton_can_recreate) - close_all_tables_for_name(thd, table->s, HA_EXTRA_NOT_USED); + close_all_tables_for_name(thd, table->s, HA_EXTRA_NOT_USED, NULL); } else { @@ -396,12 +396,12 @@ bool Truncate_statement::lock_table(THD *thd, TABLE_LIST *table_ref, @retval TRUE Error. */ -bool Truncate_statement::truncate_table(THD *thd, TABLE_LIST *table_ref) +bool Sql_cmd_truncate_table::truncate_table(THD *thd, TABLE_LIST *table_ref) { int error; TABLE *table; bool binlog_stmt; - DBUG_ENTER("Truncate_statement::truncate_table"); + DBUG_ENTER("Sql_cmd_truncate_table::truncate_table"); /* Initialize, or reinitialize in case of reexecution (SP). */ m_ticket_downgrade= NULL; @@ -498,7 +498,7 @@ bool Truncate_statement::truncate_table(THD *thd, TABLE_LIST *table_ref) to a shared one. */ if (m_ticket_downgrade) - m_ticket_downgrade->downgrade_exclusive_lock(MDL_SHARED_NO_READ_WRITE); + m_ticket_downgrade->downgrade_lock(MDL_SHARED_NO_READ_WRITE); DBUG_RETURN(error); } @@ -512,11 +512,11 @@ bool Truncate_statement::truncate_table(THD *thd, TABLE_LIST *table_ref) @return FALSE on success. */ -bool Truncate_statement::execute(THD *thd) +bool Sql_cmd_truncate_table::execute(THD *thd) { bool res= TRUE; TABLE_LIST *first_table= thd->lex->select_lex.table_list.first; - DBUG_ENTER("Truncate_statement::execute"); + DBUG_ENTER("Sql_cmd_truncate_table::execute"); if (check_one_table_access(thd, DROP_ACL, first_table)) DBUG_RETURN(res); diff --git a/sql/sql_truncate.h b/sql/sql_truncate.h index 95a2f35df4f..061c561b8ea 100644 --- a/sql/sql_truncate.h +++ b/sql/sql_truncate.h @@ -19,9 +19,9 @@ class THD; struct TABLE_LIST; /** - Truncate_statement represents the TRUNCATE statement. + Sql_cmd_truncate_table represents the TRUNCATE statement. */ -class Truncate_statement : public Sql_statement +class Sql_cmd_truncate_table : public Sql_cmd { private: /* Set if a lock must be downgraded after truncate is done. */ @@ -29,14 +29,12 @@ private: public: /** - Constructor, used to represent a ALTER TABLE statement. - @param lex the LEX structure for this statement. + Constructor, used to represent a TRUNCATE statement. */ - Truncate_statement(LEX *lex) - : Sql_statement(lex) + Sql_cmd_truncate_table() {} - virtual ~Truncate_statement() + virtual ~Sql_cmd_truncate_table() {} /** @@ -46,6 +44,11 @@ public: */ bool execute(THD *thd); + virtual enum_sql_command sql_command_code() const + { + return SQLCOM_TRUNCATE; + } + protected: /** Handle locking a base table for truncate. */ bool lock_table(THD *, TABLE_LIST *, bool *); diff --git a/sql/sql_union.cc b/sql/sql_union.cc index 106c134223e..6a1e4d745e8 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -711,7 +711,7 @@ bool st_select_lex_unit::exec() Stop execution of the remaining queries in the UNIONS, and produce the current result. */ - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_QUERY_EXCEEDED_ROWS_EXAMINED_LIMIT, ER(ER_QUERY_EXCEEDED_ROWS_EXAMINED_LIMIT), thd->accessed_rows_and_keys, diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 0d1cb7de5f2..eab8b3af84c 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -138,7 +138,7 @@ static bool check_fields(THD *thd, List<Item> &items) while ((item= it++)) { - if (!(field= item->filed_for_view_update())) + if (!(field= item->field_for_view_update())) { /* item has name, because it comes from VIEW SELECT list */ my_error(ER_NONUPDATEABLE_COLUMN, MYF(0), item->name); @@ -824,7 +824,7 @@ int mysql_update(THD *thd, } else table->file->unlock_row(); - thd->warning_info->inc_current_row_for_warning(); + thd->get_stmt_da()->inc_current_row_for_warning(); if (thd->is_error()) { error= 1; @@ -932,7 +932,7 @@ int mysql_update(THD *thd, char buff[MYSQL_ERRMSG_SIZE]; my_snprintf(buff, sizeof(buff), ER(ER_UPDATE_INFO), (ulong) found, (ulong) updated, - (ulong) thd->warning_info->statement_warn_count()); + (ulong) thd->get_stmt_da()->statement_warn_count()); my_ok(thd, (thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated, id, buff); DBUG_PRINT("info",("%ld records updated", (long) updated)); @@ -1117,7 +1117,7 @@ bool unsafe_key_update(List<TABLE_LIST> leaves, table_map tables_for_update) // The primary key can cover multiple columns KEY key_info= table1->key_info[table1->s->primary_key]; KEY_PART_INFO *key_part= key_info.key_part; - KEY_PART_INFO *key_part_end= key_part + key_info.key_parts; + KEY_PART_INFO *key_part_end= key_part + key_info.user_defined_key_parts; for (;key_part != key_part_end; ++key_part) { diff --git a/sql/sql_view.cc b/sql/sql_view.cc index c1953ba20f5..38d6b41d92b 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -211,13 +211,14 @@ static void make_valid_column_names(List<Item> &item_list) static bool fill_defined_view_parts (THD *thd, TABLE_LIST *view) { - char key[MAX_DBKEY_LENGTH]; + const char *key; uint key_length; LEX *lex= thd->lex; TABLE_LIST decoy; memcpy (&decoy, view, sizeof (TABLE_LIST)); - key_length= create_table_def_key(thd, key, view, 0); + + key_length= get_table_def_key(view, &key); if (tdc_open_view(thd, &decoy, decoy.alias, key, key_length, thd->mem_root, OPEN_VIEW_NO_PARSE)) @@ -354,7 +355,7 @@ bool create_view_precheck(THD *thd, TABLE_LIST *tables, TABLE_LIST *view, while ((item= it++)) { Item_field *field; - if ((field= item->filed_for_view_update())) + if ((field= item->field_for_view_update())) { /* any_privileges may be reset later by the Item_field::set_field @@ -515,7 +516,7 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views, if (!is_acl_user(lex->definer->host.str, lex->definer->user.str)) { - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_NO_SUCH_USER, ER(ER_NO_SUCH_USER), lex->definer->user.str, @@ -640,7 +641,7 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views, Item *item; while ((item= it++)) { - Item_field *fld= item->filed_for_view_update(); + Item_field *fld= item->field_for_view_update(); uint priv= (get_column_grant(thd, &view->grant, view->db, view->table_name, item->name) & VIEW_ANY_ACL); @@ -891,7 +892,7 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view, if (lex->create_view_algorithm == VIEW_ALGORITHM_MERGE && !lex->can_be_merged()) { - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_VIEW_MERGE, + push_warning(thd, Sql_condition::WARN_LEVEL_WARN, ER_WARN_VIEW_MERGE, ER(ER_WARN_VIEW_MERGE)); lex->create_view_algorithm= DTYPE_ALGORITHM_UNDEFINED; } @@ -1168,7 +1169,7 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table, DBUG_ASSERT(!table->definer.host.str && !table->definer.user.length && !table->definer.host.length); - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_VIEW_FRM_NO_USER, ER(ER_VIEW_FRM_NO_USER), table->db, table->table_name); get_default_definer(thd, &table->definer); @@ -1564,7 +1565,7 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table, lex->select_lex.order_list.elements && !table->select_lex->master_unit()->is_union()) { - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_VIEW_ORDERBY_IGNORED, ER(ER_VIEW_ORDERBY_IGNORED), table->db, table->table_name); @@ -1680,7 +1681,7 @@ bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode) my_snprintf(name, sizeof(name), "%s.%s", view->db, view->table_name); if (thd->lex->drop_if_exists) { - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_BAD_TABLE_ERROR, ER(ER_BAD_TABLE_ERROR), name); continue; @@ -1825,7 +1826,7 @@ bool check_key_in_view(THD *thd, TABLE_LIST *view) for (k= trans; k < end_of_trans; k++) { Item_field *field; - if ((field= k->item->filed_for_view_update()) && + if ((field= k->item->field_for_view_update()) && field->field == key_part->field) break; } @@ -1847,7 +1848,7 @@ bool check_key_in_view(THD *thd, TABLE_LIST *view) for (fld= trans; fld < end_of_trans; fld++) { Item_field *field; - if ((field= fld->item->filed_for_view_update()) && + if ((field= fld->item->field_for_view_update()) && field->field == *field_ptr) break; } @@ -1861,7 +1862,7 @@ bool check_key_in_view(THD *thd, TABLE_LIST *view) if (thd->variables.updatable_views_with_limit) { /* update allowed, but issue warning */ - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + push_warning(thd, Sql_condition::WARN_LEVEL_NOTE, ER_WARN_VIEW_WITHOUT_KEY, ER(ER_WARN_VIEW_WITHOUT_KEY)); DBUG_RETURN(FALSE); } @@ -1901,7 +1902,7 @@ bool insert_view_fields(THD *thd, List<Item> *list, TABLE_LIST *view) for (Field_translator *entry= trans; entry < trans_end; entry++) { Item_field *fld; - if ((fld= entry->item->filed_for_view_update())) + if ((fld= entry->item->field_for_view_update())) list->push_back(fld); else { diff --git a/sql/sql_view.h b/sql/sql_view.h index 2e9c77252e8..abe95c63e6e 100644 --- a/sql/sql_view.h +++ b/sql/sql_view.h @@ -37,6 +37,7 @@ bool mysql_create_view(THD *thd, TABLE_LIST *view, bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table, uint flags); + bool mysql_drop_view(THD *thd, TABLE_LIST *view, enum_drop_mode drop_mode); bool check_key_in_view(THD *thd, TABLE_LIST * view); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 0fa18b46ff4..8e5ec870ac3 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -54,11 +54,13 @@ #include "sp_pcontext.h" #include "sp_rcontext.h" #include "sp.h" -#include "sql_alter.h" // Alter_table*_statement -#include "sql_truncate.h" // Truncate_statement -#include "sql_admin.h" // Analyze/Check..._table_stmt -#include "sql_partition_admin.h" // Alter_table_*_partition_stmt +#include "sql_alter.h" // Sql_cmd_alter_table* +#include "sql_truncate.h" // Sql_cmd_truncate_table +#include "sql_admin.h" // Sql_cmd_analyze/Check..._table +#include "sql_partition_admin.h" // Sql_cmd_alter_table_*_part. +#include "sql_handler.h" // Sql_cmd_handler_* #include "sql_signal.h" +#include "sql_get_diagnostics.h" // Sql_cmd_get_diagnostics #include "event_parse_data.h" #include "create_options.h" #include <myisam.h> @@ -542,6 +544,57 @@ set_trigger_new_row(THD *thd, LEX_STRING *name, Item *val) /** + Create an object to represent a SP variable in the Item-hierarchy. + + @param thd The current thread. + @param name The SP variable name. + @param spvar The SP variable (optional). + @param start_in_q Start position of the SP variable name in the query. + @param end_in_q End position of the SP variable name in the query. + + @remark If spvar is not specified, the name is used to search for the + variable in the parse-time context. If the variable does not + exist, a error is set and NULL is returned to the caller. + + @return An Item_splocal object representing the SP variable, or NULL on error. +*/ +static Item_splocal* +create_item_for_sp_var(THD *thd, LEX_STRING name, sp_variable_t *spvar, + const char *start_in_q, const char *end_in_q) +{ + Item_splocal *item; + LEX *lex= thd->lex; + uint pos_in_q, len_in_q; + sp_pcontext *spc = lex->spcont; + + /* If necessary, look for the variable. */ + if (spc && !spvar) + spvar= spc->find_variable(&name); + + if (!spvar) + { + my_error(ER_SP_UNDECLARED_VAR, MYF(0), name.str); + return NULL; + } + + DBUG_ASSERT(spc && spvar); + + /* Position and length of the SP variable name in the query. */ + pos_in_q= start_in_q - lex->sphead->m_tmp_query; + len_in_q= end_in_q - start_in_q; + + item= new (thd->mem_root) + Item_splocal(name, spvar->offset, spvar->type, pos_in_q, len_in_q); + +#ifndef DBUG_OFF + if (item) + item->m_sp= lex->sphead; +#endif + + return item; +} + +/** Helper to resolve the SQL:2003 Syntax exception 1) in <in predicate>. See SQL:2003, Part 2, section 8.4 <in predicate>, Note 184, page 383. This function returns the proper item for the SQL expression @@ -706,10 +759,10 @@ static bool add_create_index_prepare (LEX *lex, Table_ident *table) if (!lex->current_select->add_table_to_list(lex->thd, table, NULL, TL_OPTION_UPDATING, TL_READ_NO_INSERT, - MDL_SHARED_NO_WRITE)) + MDL_SHARED_UPGRADABLE)) return TRUE; lex->alter_info.reset(); - lex->alter_info.flags= ALTER_ADD_INDEX; + lex->alter_info.flags= Alter_info::ALTER_ADD_INDEX; lex->col_list.empty(); lex->change= NullS; lex->option_list= NULL; @@ -889,6 +942,14 @@ static bool sp_create_assignment_instr(THD *thd, bool no_lookahead) enum Foreign_key::fk_option m_fk_option; enum enum_yes_no_unknown m_yes_no_unk; Diag_condition_item_name diag_condition_item_name; + Diagnostics_information::Which_area diag_area; + Diagnostics_information *diag_info; + Statement_information_item *stmt_info_item; + Statement_information_item::Name stmt_info_item_name; + List<Statement_information_item> *stmt_info_list; + Condition_information_item *cond_info_item; + Condition_information_item::Name cond_info_item_name; + List<Condition_information_item> *cond_info_list; DYNCALL_CREATE_DEF *dyncol_def; List<DYNCALL_CREATE_DEF> *dyncol_def_list; } @@ -899,10 +960,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %pure_parser /* We have threads */ /* - Currently there are 171 shift/reduce conflicts. + Currently there are 167 shift/reduce conflicts. We should not introduce new conflicts any more. */ -%expect 171 +%expect 167 /* Comments for TOKENS. @@ -1024,6 +1085,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token CROSS /* SQL-2003-R */ %token CUBE_SYM /* SQL-2003-R */ %token CURDATE /* MYSQL-FUNC */ +%token CURRENT_SYM /* SQL-2003-R */ %token CURRENT_USER /* SQL-2003-R */ %token CURSOR_SYM /* SQL-2003-R */ %token CURSOR_NAME_SYM /* SQL-2003-N */ @@ -1054,6 +1116,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token DESCRIBE /* SQL-2003-R */ %token DES_KEY_FILE %token DETERMINISTIC_SYM /* SQL-2003-R */ +%token DIAGNOSTICS_SYM /* SQL-2003-N */ %token DIRECTORY_SYM %token DISABLE_SYM %token DISCARD @@ -1087,6 +1150,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token EVENTS_SYM %token EVENT_SYM %token EVERY_SYM /* SQL-2003-N */ +%token EXCHANGE_SYM %token EXAMINED_SYM %token EXECUTE_SYM /* SQL-2003-R */ %token EXISTS /* SQL-2003-R */ @@ -1119,6 +1183,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token GEOMETRYCOLLECTION %token GEOMETRY_SYM %token GET_FORMAT /* MYSQL-FUNC */ +%token GET_SYM /* SQL-2003-R */ %token GLOBAL_SYM /* SQL-2003-R */ %token GRANT /* SQL-2003-R */ %token GRANTS @@ -1276,6 +1341,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token NO_WRITE_TO_BINLOG %token NULL_SYM /* SQL-2003-R */ %token NUM +%token NUMBER_SYM /* SQL-2003-N */ %token NUMERIC_SYM /* SQL-2003-R */ %token NVARCHAR_SYM %token OFFSET_SYM @@ -1304,9 +1370,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token PARSER_SYM %token PARSE_VCOL_EXPR_SYM %token PARTIAL /* SQL-2003-N */ -%token PARTITIONING_SYM -%token PARTITIONS_SYM %token PARTITION_SYM /* SQL-2003-R */ +%token PARTITIONS_SYM +%token PARTITIONING_SYM %token PASSWORD %token PERSISTENT_SYM %token PHASE_SYM @@ -1367,6 +1433,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token RESTORE_SYM %token RESTRICT %token RESUME_SYM +%token RETURNED_SQLSTATE_SYM /* SQL-2003-N */ %token RETURNS_SYM /* SQL-2003-R */ %token RETURN_SYM /* SQL-2003-R */ %token REVOKE /* SQL-2003-R */ @@ -1377,6 +1444,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token ROWS_SYM /* SQL-2003-R */ %token ROW_FORMAT_SYM %token ROW_SYM /* SQL-2003-R */ +%token ROW_COUNT_SYM /* SQL-2003-N */ %token RTREE_SYM %token SAVEPOINT_SYM /* SQL-2003-R */ %token SCHEDULE_SYM @@ -1638,6 +1706,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); function_call_generic function_call_conflict kill_expr signal_allowed_expr + simple_target_specification + condition_number %type <item_num> NUM_literal @@ -1656,7 +1726,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); btree_or_rtree %type <string_list> - using_list + using_list opt_use_partition use_partition %type <key_part> key_part @@ -1768,7 +1838,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); key_using_alg part_column_list server_def server_options_list server_option - definer_opt no_definer definer + definer_opt no_definer definer get_diagnostics parse_vcol_expr vcol_opt_specifier vcol_opt_attribute vcol_opt_attribute_list vcol_attribute END_OF_INPUT @@ -1795,6 +1865,15 @@ END_OF_INPUT %type <NONE> signal_stmt resignal_stmt %type <diag_condition_item_name> signal_condition_information_item_name +%type <diag_area> which_area; +%type <diag_info> diagnostics_information; +%type <stmt_info_item> statement_information_item; +%type <stmt_info_item_name> statement_information_item_name; +%type <stmt_info_list> statement_information; +%type <cond_info_item> condition_information_item; +%type <cond_info_item_name> condition_information_item_name; +%type <cond_info_list> condition_information; + %type <NONE> '-' '+' '*' '/' '%' '(' ')' ',' '!' '{' '}' '&' '|' AND_SYM OR_SYM OR_OR_SYM BETWEEN_SYM CASE_SYM @@ -1895,6 +1974,7 @@ statement: | drop | execute | flush + | get_diagnostics | grant | handler | help @@ -2117,7 +2197,7 @@ master_def: } if (Lex->mi.heartbeat_period > slave_net_timeout) { - push_warning_printf(YYTHD, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(YYTHD, Sql_condition::WARN_LEVEL_WARN, ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE_MAX, ER(ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE_MAX)); } @@ -2125,7 +2205,7 @@ master_def: { if (Lex->mi.heartbeat_period != 0.0) { - push_warning_printf(YYTHD, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(YYTHD, Sql_condition::WARN_LEVEL_WARN, ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE_MIN, ER(ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE_MIN)); Lex->mi.heartbeat_period= 0.0; @@ -2246,7 +2326,7 @@ create: !lex->create_info.db_type) { lex->create_info.db_type= ha_default_handlerton(YYTHD); - push_warning_printf(YYTHD, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(YYTHD, Sql_condition::WARN_LEVEL_WARN, ER_WARN_USING_OTHER_HANDLER, ER(ER_WARN_USING_OTHER_HANDLER), hton_name(lex->create_info.db_type)->str, @@ -2264,6 +2344,7 @@ create: if (add_create_index(Lex, $2, $4)) MYSQL_YYABORT; } + opt_index_lock_algorithm { } | CREATE fulltext INDEX_SYM ident init_key_options ON table_ident { @@ -2275,6 +2356,7 @@ create: if (add_create_index(Lex, $2, $4)) MYSQL_YYABORT; } + opt_index_lock_algorithm { } | CREATE spatial INDEX_SYM ident init_key_options ON table_ident { @@ -2286,6 +2368,7 @@ create: if (add_create_index(Lex, $2, $4)) MYSQL_YYABORT; } + opt_index_lock_algorithm { } | CREATE DATABASE opt_if_not_exists ident { Lex->create_info.default_table_charset= NULL; @@ -3141,9 +3224,9 @@ signal_stmt: Yacc_state *state= & thd->m_parser_state->m_yacc; lex->sql_command= SQLCOM_SIGNAL; - lex->m_stmt= new (thd->mem_root) Signal_statement(lex, $2, - state->m_set_signal_info); - if (lex->m_stmt == NULL) + lex->m_sql_cmd= + new (thd->mem_root) Sql_cmd_signal($2, state->m_set_signal_info); + if (lex->m_sql_cmd == NULL) MYSQL_YYABORT; } ; @@ -3280,13 +3363,160 @@ resignal_stmt: Yacc_state *state= & thd->m_parser_state->m_yacc; lex->sql_command= SQLCOM_RESIGNAL; - lex->m_stmt= new (thd->mem_root) Resignal_statement(lex, $2, - state->m_set_signal_info); - if (lex->m_stmt == NULL) + lex->m_sql_cmd= + new (thd->mem_root) Sql_cmd_resignal($2, + state->m_set_signal_info); + if (lex->m_sql_cmd == NULL) + MYSQL_YYABORT; + } + ; + +get_diagnostics: + GET_SYM which_area DIAGNOSTICS_SYM diagnostics_information + { + Diagnostics_information *info= $4; + + info->set_which_da($2); + + Lex->sql_command= SQLCOM_GET_DIAGNOSTICS; + Lex->m_sql_cmd= new (YYTHD->mem_root) Sql_cmd_get_diagnostics(info); + + if (Lex->m_sql_cmd == NULL) + MYSQL_YYABORT; + } + ; + +which_area: + /* If <which area> is not specified, then CURRENT is implicit. */ + { $$= Diagnostics_information::CURRENT_AREA; } + | CURRENT_SYM + { $$= Diagnostics_information::CURRENT_AREA; } + ; + +diagnostics_information: + statement_information + { + $$= new (YYTHD->mem_root) Statement_information($1); + if ($$ == NULL) + MYSQL_YYABORT; + } + | CONDITION_SYM condition_number condition_information + { + $$= new (YYTHD->mem_root) Condition_information($2, $3); + if ($$ == NULL) MYSQL_YYABORT; } ; +statement_information: + statement_information_item + { + $$= new (YYTHD->mem_root) List<Statement_information_item>; + if ($$ == NULL || $$->push_back($1)) + MYSQL_YYABORT; + } + | statement_information ',' statement_information_item + { + if ($1->push_back($3)) + MYSQL_YYABORT; + $$= $1; + } + ; + +statement_information_item: + simple_target_specification EQ statement_information_item_name + { + $$= new (YYTHD->mem_root) Statement_information_item($3, $1); + if ($$ == NULL) + MYSQL_YYABORT; + } + +simple_target_specification: + ident + { + Lex_input_stream *lip= &YYTHD->m_parser_state->m_lip; + $$= create_item_for_sp_var(YYTHD, $1, NULL, + lip->get_tok_start(), lip->get_ptr()); + + if ($$ == NULL) + MYSQL_YYABORT; + } + | '@' ident_or_text + { + $$= new (YYTHD->mem_root) Item_func_get_user_var($2); + if ($$ == NULL) + MYSQL_YYABORT; + } + ; + +statement_information_item_name: + NUMBER_SYM + { $$= Statement_information_item::NUMBER; } + | ROW_COUNT_SYM + { $$= Statement_information_item::ROW_COUNT; } + ; + +/* + Only a limited subset of <expr> are allowed in GET DIAGNOSTICS + <condition number>, same subset as for SIGNAL/RESIGNAL. +*/ +condition_number: + signal_allowed_expr + { $$= $1; } + ; + +condition_information: + condition_information_item + { + $$= new (YYTHD->mem_root) List<Condition_information_item>; + if ($$ == NULL || $$->push_back($1)) + MYSQL_YYABORT; + } + | condition_information ',' condition_information_item + { + if ($1->push_back($3)) + MYSQL_YYABORT; + $$= $1; + } + ; + +condition_information_item: + simple_target_specification EQ condition_information_item_name + { + $$= new (YYTHD->mem_root) Condition_information_item($3, $1); + if ($$ == NULL) + MYSQL_YYABORT; + } + +condition_information_item_name: + CLASS_ORIGIN_SYM + { $$= Condition_information_item::CLASS_ORIGIN; } + | SUBCLASS_ORIGIN_SYM + { $$= Condition_information_item::SUBCLASS_ORIGIN; } + | CONSTRAINT_CATALOG_SYM + { $$= Condition_information_item::CONSTRAINT_CATALOG; } + | CONSTRAINT_SCHEMA_SYM + { $$= Condition_information_item::CONSTRAINT_SCHEMA; } + | CONSTRAINT_NAME_SYM + { $$= Condition_information_item::CONSTRAINT_NAME; } + | CATALOG_NAME_SYM + { $$= Condition_information_item::CATALOG_NAME; } + | SCHEMA_NAME_SYM + { $$= Condition_information_item::SCHEMA_NAME; } + | TABLE_NAME_SYM + { $$= Condition_information_item::TABLE_NAME; } + | COLUMN_NAME_SYM + { $$= Condition_information_item::COLUMN_NAME; } + | CURSOR_NAME_SYM + { $$= Condition_information_item::CURSOR_NAME; } + | MESSAGE_TEXT_SYM + { $$= Condition_information_item::MESSAGE_TEXT; } + | MYSQL_ERRNO_SYM + { $$= Condition_information_item::MYSQL_ERRNO; } + | RETURNED_SQLSTATE_SYM + { $$= Condition_information_item::RETURNED_SQLSTATE; } + ; + sp_decl_idents: ident { @@ -4528,7 +4758,7 @@ partitioning: } if (lex->sql_command == SQLCOM_ALTER_TABLE) { - lex->alter_info.flags|= ALTER_PARTITION; + lex->alter_info.flags|= Alter_info::ALTER_PARTITION; } } partition @@ -5467,7 +5697,7 @@ storage_engines: MYSQL_YYABORT; } $$= 0; - push_warning_printf(YYTHD, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(YYTHD, Sql_condition::WARN_LEVEL_WARN, ER_UNKNOWN_STORAGE_ENGINE, ER(ER_UNKNOWN_STORAGE_ENGINE), $1.str); @@ -5579,7 +5809,8 @@ key_def: { LEX *lex=Lex; Key *key= new Foreign_key($4.str ? $4 : $1, lex->col_list, - $8, + $8->db, + $8->table, lex->ref_list, lex->fk_delete_opt, lex->fk_update_opt, @@ -5592,7 +5823,7 @@ key_def: &default_key_create_info, 1)) MYSQL_YYABORT; /* Only used for ALTER TABLE. Ignored otherwise. */ - lex->alter_info.flags|= ALTER_FOREIGN_KEY; + lex->alter_info.flags|= Alter_info::ADD_FOREIGN_KEY; } | opt_constraint check_constraint { @@ -5690,13 +5921,13 @@ vcol_attribute: { LEX *lex=Lex; lex->type|= UNIQUE_FLAG; - lex->alter_info.flags|= ALTER_ADD_INDEX; + lex->alter_info.flags|= Alter_info::ALTER_ADD_INDEX; } | UNIQUE_SYM KEY_SYM { LEX *lex=Lex; lex->type|= UNIQUE_KEY_FLAG; - lex->alter_info.flags|= ALTER_ADD_INDEX; + lex->alter_info.flags|= Alter_info::ALTER_ADD_INDEX; } | COMMENT_SYM TEXT_STRING_sys { Lex->comment= $2; } ; @@ -5811,7 +6042,7 @@ type: { char buff[sizeof("YEAR()") + MY_INT64_NUM_DECIMAL_DIGITS + 1]; my_snprintf(buff, sizeof(buff), "YEAR(%lu)", length); - push_warning_printf(YYTHD, MYSQL_ERROR::WARN_LEVEL_NOTE, + push_warning_printf(YYTHD, Sql_condition::WARN_LEVEL_NOTE, ER_WARN_DEPRECATED_SYNTAX, ER(ER_WARN_DEPRECATED_SYNTAX), buff, "YEAR(4)"); @@ -6042,25 +6273,25 @@ attribute: { LEX *lex=Lex; lex->type|= AUTO_INCREMENT_FLAG | NOT_NULL_FLAG | UNIQUE_FLAG; - lex->alter_info.flags|= ALTER_ADD_INDEX; + lex->alter_info.flags|= Alter_info::ALTER_ADD_INDEX; } | opt_primary KEY_SYM { LEX *lex=Lex; lex->type|= PRI_KEY_FLAG | NOT_NULL_FLAG; - lex->alter_info.flags|= ALTER_ADD_INDEX; + lex->alter_info.flags|= Alter_info::ALTER_ADD_INDEX; } | UNIQUE_SYM { LEX *lex=Lex; lex->type|= UNIQUE_FLAG; - lex->alter_info.flags|= ALTER_ADD_INDEX; + lex->alter_info.flags|= Alter_info::ALTER_ADD_INDEX; } | UNIQUE_SYM KEY_SYM { LEX *lex=Lex; lex->type|= UNIQUE_KEY_FLAG; - lex->alter_info.flags|= ALTER_ADD_INDEX; + lex->alter_info.flags|= Alter_info::ALTER_ADD_INDEX; } | COMMENT_SYM TEXT_STRING_sys { Lex->comment= $2; } | COLLATE_SYM collation_name @@ -6269,7 +6500,6 @@ opt_bin_mod: | BINARY { Lex->type|= BINCMP_FLAG; } ; - opt_primary: /* empty */ | PRIMARY_SYM @@ -6577,7 +6807,7 @@ alter: if (!lex->select_lex.add_table_to_list(thd, $4, NULL, TL_OPTION_UPDATING, TL_READ_NO_INSERT, - MDL_SHARED_NO_WRITE)) + MDL_SHARED_UPGRADABLE)) MYSQL_YYABORT; lex->col_list.empty(); lex->select_lex.init_order(); @@ -6590,17 +6820,17 @@ alter: lex->no_write_to_binlog= 0; lex->create_info.storage_media= HA_SM_DEFAULT; lex->create_last_non_select_table= lex->last_table(); - DBUG_ASSERT(!lex->m_stmt); + DBUG_ASSERT(!lex->m_sql_cmd); } alter_commands { THD *thd= YYTHD; LEX *lex= thd->lex; - if (!lex->m_stmt) + if (!lex->m_sql_cmd) { /* Create a generic ALTER TABLE statment. */ - lex->m_stmt= new (thd->mem_root) Alter_table_statement(lex); - if (lex->m_stmt == NULL) + lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_alter_table(); + if (lex->m_sql_cmd == NULL) MYSQL_YYABORT; } } @@ -6791,8 +7021,22 @@ ident_or_empty: alter_commands: /* empty */ - | DISCARD TABLESPACE { Lex->alter_info.tablespace_op= DISCARD_TABLESPACE; } - | IMPORT TABLESPACE { Lex->alter_info.tablespace_op= IMPORT_TABLESPACE; } + | DISCARD TABLESPACE + { + Lex->m_sql_cmd= new (YYTHD->mem_root) + Sql_cmd_discard_import_tablespace( + Sql_cmd_discard_import_tablespace::DISCARD_TABLESPACE); + if (Lex->m_sql_cmd == NULL) + MYSQL_YYABORT; + } + | IMPORT TABLESPACE + { + Lex->m_sql_cmd= new (YYTHD->mem_root) + Sql_cmd_discard_import_tablespace( + Sql_cmd_discard_import_tablespace::IMPORT_TABLESPACE); + if (Lex->m_sql_cmd == NULL) + MYSQL_YYABORT; + } | alter_list opt_partitioning | alter_list @@ -6804,19 +7048,18 @@ alter_commands: From here we insert a number of commands to manage the partitions of a partitioned table such as adding partitions, dropping partitions, reorganising partitions in various manners. In future releases the list - will be longer and also include moving partitions to a - new table and so forth. + will be longer. */ | add_partition_rule | DROP PARTITION_SYM alt_part_name_list { - Lex->alter_info.flags|= ALTER_DROP_PARTITION; + Lex->alter_info.flags|= Alter_info::ALTER_DROP_PARTITION; } | REBUILD_SYM PARTITION_SYM opt_no_write_to_binlog all_or_alt_part_name_list { LEX *lex= Lex; - lex->alter_info.flags|= ALTER_REBUILD_PARTITION; + lex->alter_info.flags|= Alter_info::ALTER_REBUILD_PARTITION; lex->no_write_to_binlog= $3; } | OPTIMIZE PARTITION_SYM opt_no_write_to_binlog @@ -6826,10 +7069,10 @@ alter_commands: LEX *lex= thd->lex; lex->no_write_to_binlog= $3; lex->check_opt.init(); - DBUG_ASSERT(!lex->m_stmt); - lex->m_stmt= new (thd->mem_root) - Alter_table_optimize_partition_statement(lex); - if (lex->m_stmt == NULL) + DBUG_ASSERT(!lex->m_sql_cmd); + lex->m_sql_cmd= new (thd->mem_root) + Sql_cmd_alter_table_optimize_partition(); + if (lex->m_sql_cmd == NULL) MYSQL_YYABORT; } opt_no_write_to_binlog @@ -6840,21 +7083,21 @@ alter_commands: LEX *lex= thd->lex; lex->no_write_to_binlog= $3; lex->check_opt.init(); - DBUG_ASSERT(!lex->m_stmt); - lex->m_stmt= new (thd->mem_root) - Alter_table_analyze_partition_statement(lex); - if (lex->m_stmt == NULL) - MYSQL_YYABORT; + DBUG_ASSERT(!lex->m_sql_cmd); + lex->m_sql_cmd= new (thd->mem_root) + Sql_cmd_alter_table_analyze_partition(); + if (lex->m_sql_cmd == NULL) + MYSQL_YYABORT; } | CHECK_SYM PARTITION_SYM all_or_alt_part_name_list { THD *thd= YYTHD; LEX *lex= thd->lex; lex->check_opt.init(); - DBUG_ASSERT(!lex->m_stmt); - lex->m_stmt= new (thd->mem_root) - Alter_table_check_partition_statement(lex); - if (lex->m_stmt == NULL) + DBUG_ASSERT(!lex->m_sql_cmd); + lex->m_sql_cmd= new (thd->mem_root) + Sql_cmd_alter_table_check_partition(); + if (lex->m_sql_cmd == NULL) MYSQL_YYABORT; } opt_mi_check_type @@ -6865,17 +7108,17 @@ alter_commands: LEX *lex= thd->lex; lex->no_write_to_binlog= $3; lex->check_opt.init(); - DBUG_ASSERT(!lex->m_stmt); - lex->m_stmt= new (thd->mem_root) - Alter_table_repair_partition_statement(lex); - if (lex->m_stmt == NULL) + DBUG_ASSERT(!lex->m_sql_cmd); + lex->m_sql_cmd= new (thd->mem_root) + Sql_cmd_alter_table_repair_partition(); + if (lex->m_sql_cmd == NULL) MYSQL_YYABORT; } opt_mi_repair_type | COALESCE PARTITION_SYM opt_no_write_to_binlog real_ulong_num { LEX *lex= Lex; - lex->alter_info.flags|= ALTER_COALESCE_PARTITION; + lex->alter_info.flags|= Alter_info::ALTER_COALESCE_PARTITION; lex->no_write_to_binlog= $3; lex->alter_info.num_parts= $4; } @@ -6884,26 +7127,51 @@ alter_commands: THD *thd= YYTHD; LEX *lex= thd->lex; lex->check_opt.init(); - DBUG_ASSERT(!lex->m_stmt); - lex->m_stmt= new (thd->mem_root) - Alter_table_truncate_partition_statement(lex); - if (lex->m_stmt == NULL) + DBUG_ASSERT(!lex->m_sql_cmd); + lex->m_sql_cmd= new (thd->mem_root) + Sql_cmd_alter_table_truncate_partition(); + if (lex->m_sql_cmd == NULL) MYSQL_YYABORT; } | reorg_partition_rule + | EXCHANGE_SYM PARTITION_SYM alt_part_name_item + WITH TABLE_SYM table_ident have_partitioning + { + THD *thd= YYTHD; + LEX *lex= thd->lex; + size_t dummy; + lex->select_lex.db=$6->db.str; + if (lex->select_lex.db == NULL && + lex->copy_db_to(&lex->select_lex.db, &dummy)) + { + MYSQL_YYABORT; + } + lex->name= $6->table; + lex->alter_info.flags|= Alter_info::ALTER_EXCHANGE_PARTITION; + if (!lex->select_lex.add_table_to_list(thd, $6, NULL, + TL_OPTION_UPDATING, + TL_READ_NO_INSERT, + MDL_SHARED_NO_WRITE)) + MYSQL_YYABORT; + DBUG_ASSERT(!lex->m_sql_cmd); + lex->m_sql_cmd= new (thd->mem_root) + Sql_cmd_alter_table_exchange_partition(); + if (lex->m_sql_cmd == NULL) + MYSQL_YYABORT; + } ; remove_partitioning: REMOVE_SYM PARTITIONING_SYM { - Lex->alter_info.flags|= ALTER_REMOVE_PARTITIONING; + Lex->alter_info.flags|= Alter_info::ALTER_REMOVE_PARTITIONING; } ; all_or_alt_part_name_list: ALL { - Lex->alter_info.flags|= ALTER_ALL_PARTITION; + Lex->alter_info.flags|= Alter_info::ALTER_ALL_PARTITION; } | alt_part_name_list ; @@ -6918,7 +7186,7 @@ add_partition_rule: mem_alloc_error(sizeof(partition_info)); MYSQL_YYABORT; } - lex->alter_info.flags|= ALTER_ADD_PARTITION; + lex->alter_info.flags|= Alter_info::ALTER_ADD_PARTITION; lex->no_write_to_binlog= $3; } add_part_extra @@ -6956,11 +7224,11 @@ reorg_partition_rule: reorg_parts_rule: /* empty */ { - Lex->alter_info.flags|= ALTER_TABLE_REORG; + Lex->alter_info.flags|= Alter_info::ALTER_TABLE_REORG; } | alt_part_name_list { - Lex->alter_info.flags|= ALTER_REORGANIZE_PARTITION; + Lex->alter_info.flags|= Alter_info::ALTER_REORGANIZE_PARTITION; } INTO '(' part_def_list ')' { @@ -6999,7 +7267,7 @@ add_column: { LEX *lex=Lex; lex->change=0; - lex->alter_info.flags|= ALTER_ADD_COLUMN; + lex->alter_info.flags|= Alter_info::ALTER_ADD_COLUMN; } ; @@ -7011,17 +7279,18 @@ alter_list_item: | ADD key_def { Lex->create_last_non_select_table= Lex->last_table(); - Lex->alter_info.flags|= ALTER_ADD_INDEX; + Lex->alter_info.flags|= Alter_info::ALTER_ADD_INDEX; } | add_column '(' create_field_list ')' { - Lex->alter_info.flags|= ALTER_ADD_COLUMN | ALTER_ADD_INDEX; + Lex->alter_info.flags|= Alter_info::ALTER_ADD_COLUMN | + Alter_info::ALTER_ADD_INDEX; } | CHANGE opt_column field_ident { LEX *lex=Lex; lex->change= $3.str; - lex->alter_info.flags|= ALTER_CHANGE_COLUMN; + lex->alter_info.flags|= Alter_info::ALTER_CHANGE_COLUMN; lex->option_list= NULL; } field_spec opt_place @@ -7035,7 +7304,7 @@ alter_list_item: lex->default_value= lex->on_update_value= 0; lex->comment=null_lex_str; lex->charset= NULL; - lex->alter_info.flags|= ALTER_CHANGE_COLUMN; + lex->alter_info.flags|= Alter_info::ALTER_CHANGE_COLUMN; lex->vcol_info= 0; lex->option_list= NULL; } @@ -7063,11 +7332,16 @@ alter_list_item: if (ad == NULL) MYSQL_YYABORT; lex->alter_info.drop_list.push_back(ad); - lex->alter_info.flags|= ALTER_DROP_COLUMN; + lex->alter_info.flags|= Alter_info::ALTER_DROP_COLUMN; } - | DROP FOREIGN KEY_SYM opt_ident + | DROP FOREIGN KEY_SYM field_ident { - Lex->alter_info.flags|= ALTER_DROP_INDEX | ALTER_FOREIGN_KEY; + LEX *lex=Lex; + Alter_drop *ad= new Alter_drop(Alter_drop::FOREIGN_KEY, $4.str); + if (ad == NULL) + MYSQL_YYABORT; + lex->alter_info.drop_list.push_back(ad); + lex->alter_info.flags|= Alter_info::DROP_FOREIGN_KEY; } | DROP PRIMARY_SYM KEY_SYM { @@ -7076,7 +7350,7 @@ alter_list_item: if (ad == NULL) MYSQL_YYABORT; lex->alter_info.drop_list.push_back(ad); - lex->alter_info.flags|= ALTER_DROP_INDEX; + lex->alter_info.flags|= Alter_info::ALTER_DROP_INDEX; } | DROP key_or_index field_ident { @@ -7085,19 +7359,19 @@ alter_list_item: if (ad == NULL) MYSQL_YYABORT; lex->alter_info.drop_list.push_back(ad); - lex->alter_info.flags|= ALTER_DROP_INDEX; + lex->alter_info.flags|= Alter_info::ALTER_DROP_INDEX; } | DISABLE_SYM KEYS { LEX *lex=Lex; - lex->alter_info.keys_onoff= DISABLE; - lex->alter_info.flags|= ALTER_KEYS_ONOFF; + lex->alter_info.keys_onoff= Alter_info::DISABLE; + lex->alter_info.flags|= Alter_info::ALTER_KEYS_ONOFF; } | ENABLE_SYM KEYS { LEX *lex=Lex; - lex->alter_info.keys_onoff= ENABLE; - lex->alter_info.flags|= ALTER_KEYS_ONOFF; + lex->alter_info.keys_onoff= Alter_info::ENABLE; + lex->alter_info.flags|= Alter_info::ALTER_KEYS_ONOFF; } | ALTER opt_column field_ident SET DEFAULT signed_literal { @@ -7106,7 +7380,7 @@ alter_list_item: if (ac == NULL) MYSQL_YYABORT; lex->alter_info.alter_list.push_back(ac); - lex->alter_info.flags|= ALTER_CHANGE_COLUMN_DEFAULT; + lex->alter_info.flags|= Alter_info::ALTER_CHANGE_COLUMN_DEFAULT; } | ALTER opt_column field_ident DROP DEFAULT { @@ -7115,7 +7389,7 @@ alter_list_item: if (ac == NULL) MYSQL_YYABORT; lex->alter_info.alter_list.push_back(ac); - lex->alter_info.flags|= ALTER_CHANGE_COLUMN_DEFAULT; + lex->alter_info.flags|= Alter_info::ALTER_CHANGE_COLUMN_DEFAULT; } | RENAME opt_to table_ident { @@ -7134,7 +7408,7 @@ alter_list_item: MYSQL_YYABORT; } lex->name= $3->table; - lex->alter_info.flags|= ALTER_RENAME; + lex->alter_info.flags|= Alter_info::ALTER_RENAME; } | CONVERT_SYM TO_SYM charset charset_name_or_default opt_collate { @@ -7155,12 +7429,12 @@ alter_list_item: lex->create_info.default_table_charset= $5; lex->create_info.used_fields|= (HA_CREATE_USED_CHARSET | HA_CREATE_USED_DEFAULT_CHARSET); - lex->alter_info.flags|= ALTER_CONVERT; + lex->alter_info.flags|= Alter_info::ALTER_CONVERT; } | create_table_options_space_separated { LEX *lex=Lex; - lex->alter_info.flags|= ALTER_OPTIONS; + lex->alter_info.flags|= Alter_info::ALTER_OPTIONS; if ((lex->create_info.used_fields & HA_CREATE_USED_ENGINE) && !lex->create_info.db_type) { @@ -7169,12 +7443,53 @@ alter_list_item: } | FORCE_SYM { - Lex->alter_info.flags|= ALTER_RECREATE; + Lex->alter_info.flags|= Alter_info::ALTER_RECREATE; } | alter_order_clause { LEX *lex=Lex; - lex->alter_info.flags|= ALTER_ORDER; + lex->alter_info.flags|= Alter_info::ALTER_ORDER; + } + | alter_algorithm_option + | alter_lock_option + ; + +opt_index_lock_algorithm: + /* empty */ + | alter_lock_option + | alter_algorithm_option + | alter_lock_option alter_algorithm_option + | alter_algorithm_option alter_lock_option + +alter_algorithm_option: + ALGORITHM_SYM opt_equal DEFAULT + { + Lex->alter_info.requested_algorithm= + Alter_info::ALTER_TABLE_ALGORITHM_DEFAULT; + } + | ALGORITHM_SYM opt_equal ident + { + if (Lex->alter_info.set_requested_algorithm(&$3)) + { + my_error(ER_UNKNOWN_ALTER_ALGORITHM, MYF(0), $3.str); + MYSQL_YYABORT; + } + } + ; + +alter_lock_option: + LOCK_SYM opt_equal DEFAULT + { + Lex->alter_info.requested_lock= + Alter_info::ALTER_TABLE_LOCK_DEFAULT; + } + | LOCK_SYM opt_equal ident + { + if (Lex->alter_info.set_requested_lock(&$3)) + { + my_error(ER_UNKNOWN_ALTER_LOCK, MYF(0), $3.str); + MYSQL_YYABORT; + } } ; @@ -7215,8 +7530,16 @@ opt_restrict: opt_place: /* empty */ {} - | AFTER_SYM ident { store_position_for_column($2.str); } - | FIRST_SYM { store_position_for_column(first_keyword); } + | AFTER_SYM ident + { + store_position_for_column($2.str); + Lex->alter_info.flags |= Alter_info::ALTER_COLUMN_ORDER; + } + | FIRST_SYM + { + store_position_for_column(first_keyword); + Lex->alter_info.flags |= Alter_info::ALTER_COLUMN_ORDER; + } ; opt_to: @@ -7402,9 +7725,9 @@ repair: { THD *thd= YYTHD; LEX* lex= thd->lex; - DBUG_ASSERT(!lex->m_stmt); - lex->m_stmt= new (thd->mem_root) Repair_table_statement(lex); - if (lex->m_stmt == NULL) + DBUG_ASSERT(!lex->m_sql_cmd); + lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_repair_table(); + if (lex->m_sql_cmd == NULL) MYSQL_YYABORT; } ; @@ -7440,9 +7763,9 @@ analyze: { THD *thd= YYTHD; LEX* lex= thd->lex; - DBUG_ASSERT(!lex->m_stmt); - lex->m_stmt= new (thd->mem_root) Analyze_table_statement(lex); - if (lex->m_stmt == NULL) + DBUG_ASSERT(!lex->m_sql_cmd); + lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_analyze_table(); + if (lex->m_sql_cmd == NULL) MYSQL_YYABORT; } ; @@ -7565,9 +7888,9 @@ check: { THD *thd= YYTHD; LEX* lex= thd->lex; - DBUG_ASSERT(!lex->m_stmt); - lex->m_stmt= new (thd->mem_root) Check_table_statement(lex); - if (lex->m_stmt == NULL) + DBUG_ASSERT(!lex->m_sql_cmd); + lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_check_table(); + if (lex->m_sql_cmd == NULL) MYSQL_YYABORT; } ; @@ -7606,9 +7929,9 @@ optimize: { THD *thd= YYTHD; LEX* lex= thd->lex; - DBUG_ASSERT(!lex->m_stmt); - lex->m_stmt= new (thd->mem_root) Optimize_table_statement(lex); - if (lex->m_stmt == NULL) + DBUG_ASSERT(!lex->m_sql_cmd); + lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_optimize_table(); + if (lex->m_sql_cmd == NULL) MYSQL_YYABORT; } ; @@ -7755,7 +8078,7 @@ preload_keys_parts: adm_partition: PARTITION_SYM have_partitioning { - Lex->alter_info.flags|= ALTER_ADMIN_PARTITION; + Lex->alter_info.flags|= Alter_info::ALTER_ADMIN_PARTITION; } '(' all_or_alt_part_name_list ')' ; @@ -9241,6 +9564,14 @@ function_call_conflict: if ($$ == NULL) MYSQL_YYABORT; } + | ROW_COUNT_SYM '(' ')' + { + $$= new (YYTHD->mem_root) Item_func_row_count(); + if ($$ == NULL) + MYSQL_YYABORT; + Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION); + Lex->safe_to_cache_query= 0; + } | TRUNCATE_SYM '(' expr ',' expr ')' { $$= new (YYTHD->mem_root) Item_func_round($3,$5,1); @@ -10044,6 +10375,22 @@ normal_join: | CROSS JOIN_SYM {} ; +/* + table PARTITION (list of partitions), reusing using_list instead of creating + a new rule for partition_list. +*/ +opt_use_partition: + /* empty */ { $$= 0;} + | use_partition + ; + +use_partition: + PARTITION_SYM '(' using_list ')' have_partitioning + { + $$= $3; + } + ; + /* This is a flattening of the rules <table factor> and <table primary> in the SQL:2003 standard, since we don't have <sample clause> @@ -10057,13 +10404,14 @@ table_factor: SELECT_LEX *sel= Select; sel->table_join_options= 0; } - table_ident opt_table_alias opt_key_definition + table_ident opt_use_partition opt_table_alias opt_key_definition { - if (!($$= Select->add_table_to_list(YYTHD, $2, $3, + if (!($$= Select->add_table_to_list(YYTHD, $2, $4, Select->get_table_join_options(), YYPS->m_lock_type, YYPS->m_mdl_type, - Select->pop_index_hints()))) + Select->pop_index_hints(), + $3))) MYSQL_YYABORT; Select->add_joined_table($$); } @@ -10133,7 +10481,7 @@ table_factor: if (ti == NULL) MYSQL_YYABORT; if (!($$= sel->add_table_to_list(lex->thd, - new Table_ident(unit), $5, 0, + ti, $5, 0, TL_READ, MDL_SHARED_READ))) MYSQL_YYABORT; @@ -11021,12 +11369,12 @@ drop: MYSQL_YYABORT; lex->sql_command= SQLCOM_DROP_INDEX; lex->alter_info.reset(); - lex->alter_info.flags= ALTER_DROP_INDEX; + lex->alter_info.flags= Alter_info::ALTER_DROP_INDEX; lex->alter_info.drop_list.push_back(ad); if (!lex->current_select->add_table_to_list(lex->thd, $5, NULL, TL_OPTION_UPDATING, TL_READ_NO_INSERT, - MDL_SHARED_NO_WRITE)) + MDL_SHARED_UPGRADABLE)) MYSQL_YYABORT; } | DROP DATABASE if_exists ident @@ -11154,6 +11502,19 @@ table_name: } ; +table_name_with_opt_use_partition: + table_ident opt_use_partition + { + if (!Select->add_table_to_list(YYTHD, $1, NULL, + TL_OPTION_UPDATING, + YYPS->m_lock_type, + YYPS->m_mdl_type, + NULL, + $2)) + MYSQL_YYABORT; + } + ; + table_alias_ref_list: table_alias_ref | table_alias_ref_list ',' table_alias_ref @@ -11258,7 +11619,7 @@ insert2: ; insert_table: - table_name + table_name_with_opt_use_partition { LEX *lex=Lex; lex->field_list.empty(); @@ -11458,11 +11819,13 @@ delete: ; single_multi: - FROM table_ident + FROM table_ident opt_use_partition { if (!Select->add_table_to_list(YYTHD, $2, NULL, TL_OPTION_UPDATING, YYPS->m_lock_type, - YYPS->m_mdl_type)) + YYPS->m_mdl_type, + NULL, + $3)) MYSQL_YYABORT; YYPS->m_lock_type= TL_READ_DEFAULT; YYPS->m_mdl_type= MDL_SHARED_READ; @@ -11559,9 +11922,9 @@ truncate: { THD *thd= YYTHD; LEX* lex= thd->lex; - DBUG_ASSERT(!lex->m_stmt); - lex->m_stmt= new (thd->mem_root) Truncate_statement(lex); - if (lex->m_stmt == NULL) + DBUG_ASSERT(!lex->m_sql_cmd); + lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_truncate_table(); + if (lex->m_sql_cmd == NULL) MYSQL_YYABORT; } ; @@ -11750,7 +12113,7 @@ show_param: { LEX *lex=Lex; lex->sql_command= SQLCOM_SHOW_AUTHORS; - push_warning_printf(YYTHD, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(YYTHD, Sql_condition::WARN_LEVEL_WARN, ER_WARN_DEPRECATED_SYNTAX_NO_REPLACEMENT, ER(ER_WARN_DEPRECATED_SYNTAX_NO_REPLACEMENT), "SHOW AUTHORS"); @@ -11759,7 +12122,7 @@ show_param: { LEX *lex=Lex; lex->sql_command= SQLCOM_SHOW_CONTRIBUTORS; - push_warning_printf(YYTHD, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(YYTHD, Sql_condition::WARN_LEVEL_WARN, ER_WARN_DEPRECATED_SYNTAX_NO_REPLACEMENT, ER(ER_WARN_DEPRECATED_SYNTAX_NO_REPLACEMENT), "SHOW CONTRIBUTORS"); @@ -12332,18 +12695,18 @@ load: if (!(lex->exchange= new sql_exchange($7.str, 0, $2))) MYSQL_YYABORT; } - opt_duplicate INTO TABLE_SYM table_ident + opt_duplicate INTO TABLE_SYM table_ident opt_use_partition { LEX *lex=Lex; if (!Select->add_table_to_list(YYTHD, $12, NULL, TL_OPTION_UPDATING, - $4, MDL_SHARED_WRITE)) + $4, MDL_SHARED_WRITE, NULL, $13)) MYSQL_YYABORT; lex->field_list.empty(); lex->update_list.empty(); lex->value_list.empty(); } opt_load_data_charset - { Lex->exchange->cs= $14; } + { Lex->exchange->cs= $15; } opt_xml_rows_identified_by opt_field_term opt_line_term opt_ignore_lines opt_field_or_var_spec opt_load_data_set_spec @@ -13300,7 +13663,6 @@ keyword: | OPTIONS_SYM {} | OWNER_SYM {} | PARSER_SYM {} - | PARTITION_SYM {} | PORT_SYM {} | PREPARE_SYM {} | REMOVE_SYM {} @@ -13380,6 +13742,11 @@ keyword_sp: | CONTRIBUTORS_SYM {} | CPU_SYM {} | CUBE_SYM {} + /* + Although a reserved keyword in SQL:2003 (and :2008), + not reserved in MySQL per WL#2111 specification. + */ + | CURRENT_SYM {} | CURSOR_NAME_SYM {} | DATA_SYM {} | DATAFILE_SYM {} @@ -13389,6 +13756,7 @@ keyword_sp: | DEFINER_SYM {} | DELAY_KEY_WRITE_SYM {} | DES_KEY_FILE {} + | DIAGNOSTICS_SYM {} | DIRECTORY_SYM {} | DISABLE_SYM {} | DISCARD {} @@ -13406,6 +13774,7 @@ keyword_sp: | EVENT_SYM {} | EVENTS_SYM {} | EVERY_SYM {} + | EXCHANGE_SYM {} | EXPANSION_SYM {} | EXTENDED_SYM {} | EXTENT_SIZE_SYM {} @@ -13502,6 +13871,7 @@ keyword_sp: | NO_WAIT_SYM {} | NODEGROUP_SYM {} | NONE_SYM {} + | NUMBER_SYM {} | NVARCHAR_SYM {} | OFFSET_SYM {} | OLD_PASSWORD {} @@ -13548,10 +13918,12 @@ keyword_sp: | REPLICATION {} | RESOURCES {} | RESUME_SYM {} + | RETURNED_SQLSTATE_SYM {} | RETURNS_SYM {} | ROLLUP_SYM {} | ROUTINE_SYM {} | ROWS_SYM {} + | ROW_COUNT_SYM {} | ROW_FORMAT_SYM {} | ROW_SYM {} | RTREE_SYM {} @@ -15406,7 +15778,7 @@ sf_tail: If a collision exists, it should not be silenced but fixed. */ push_warning_printf(thd, - MYSQL_ERROR::WARN_LEVEL_NOTE, + Sql_condition::WARN_LEVEL_NOTE, ER_NATIVE_FCT_NAME_COLLISION, ER(ER_NATIVE_FCT_NAME_COLLISION), sp->m_name.str); diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index 5bdb0a4a235..42e44b4706d 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -72,23 +72,25 @@ static Sys_var_mybool Sys_pfs_enabled( "performance_schema", "Enable the performance schema.", - PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_enabled), - CMD_LINE(OPT_ARG), DEFAULT(FALSE)); + READ_ONLY GLOBAL_VAR(pfs_param.m_enabled), + CMD_LINE(OPT_ARG), DEFAULT(TRUE)); -static Sys_var_ulong Sys_pfs_events_waits_history_long_size( +static Sys_var_long Sys_pfs_events_waits_history_long_size( "performance_schema_events_waits_history_long_size", - "Number of rows in EVENTS_WAITS_HISTORY_LONG.", + "Number of rows in EVENTS_WAITS_HISTORY_LONG." + " Use 0 to disable, -1 for automated sizing.", PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_events_waits_history_long_sizing), - CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 1024*1024), - DEFAULT(PFS_WAITS_HISTORY_LONG_SIZE), BLOCK_SIZE(1)); + CMD_LINE(REQUIRED_ARG), VALID_RANGE(-1, 1024*1024), + DEFAULT(-1), BLOCK_SIZE(1)); -static Sys_var_ulong Sys_pfs_events_waits_history_size( +static Sys_var_long Sys_pfs_events_waits_history_size( "performance_schema_events_waits_history_size", - "Number of rows per thread in EVENTS_WAITS_HISTORY.", + "Number of rows per thread in EVENTS_WAITS_HISTORY." + " Use 0 to disable, -1 for automated sizing.", PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_events_waits_history_sizing), - CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 1024), - DEFAULT(PFS_WAITS_HISTORY_SIZE), BLOCK_SIZE(1)); + CMD_LINE(REQUIRED_ARG), VALID_RANGE(-1, 1024), + DEFAULT(-1), BLOCK_SIZE(1)); static Sys_var_ulong Sys_pfs_max_cond_classes( "performance_schema_max_cond_classes", @@ -97,12 +99,13 @@ static Sys_var_ulong Sys_pfs_max_cond_classes( CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 256), DEFAULT(PFS_MAX_COND_CLASS), BLOCK_SIZE(1)); -static Sys_var_ulong Sys_pfs_max_cond_instances( +static Sys_var_long Sys_pfs_max_cond_instances( "performance_schema_max_cond_instances", - "Maximum number of instrumented condition objects.", + "Maximum number of instrumented condition objects." + " Use 0 to disable, -1 for automated sizing.", PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_cond_sizing), - CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 1024*1024), - DEFAULT(PFS_MAX_COND), BLOCK_SIZE(1)); + CMD_LINE(REQUIRED_ARG), VALID_RANGE(-1, 1024*1024), + DEFAULT(-1), BLOCK_SIZE(1)); static Sys_var_ulong Sys_pfs_max_file_classes( "performance_schema_max_file_classes", @@ -118,19 +121,21 @@ static Sys_var_ulong Sys_pfs_max_file_handles( CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 1024*1024), DEFAULT(PFS_MAX_FILE_HANDLE), BLOCK_SIZE(1)); -static Sys_var_ulong Sys_pfs_max_file_instances( +static Sys_var_long Sys_pfs_max_file_instances( "performance_schema_max_file_instances", - "Maximum number of instrumented files.", + "Maximum number of instrumented files." + " Use 0 to disable, -1 for automated sizing.", PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_file_sizing), - CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 1024*1024), - DEFAULT(PFS_MAX_FILE), BLOCK_SIZE(1)); + CMD_LINE(REQUIRED_ARG), VALID_RANGE(-1, 1024*1024), + DEFAULT(-1), BLOCK_SIZE(1)); -static Sys_var_ulong Sys_pfs_max_sockets( +static Sys_var_long Sys_pfs_max_sockets( "performance_schema_max_socket_instances", - "Maximum number of opened instrumented sockets.", + "Maximum number of opened instrumented sockets." + " Use 0 to disable, -1 for automated sizing.", PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_socket_sizing), - CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 1024*1024), - DEFAULT(PFS_MAX_SOCKETS), + CMD_LINE(REQUIRED_ARG), VALID_RANGE(-1, 1024*1024), + DEFAULT(-1), BLOCK_SIZE(1)); static Sys_var_ulong Sys_pfs_max_socket_classes( @@ -148,12 +153,13 @@ static Sys_var_ulong Sys_pfs_max_mutex_classes( CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 256), DEFAULT(PFS_MAX_MUTEX_CLASS), BLOCK_SIZE(1)); -static Sys_var_ulong Sys_pfs_max_mutex_instances( +static Sys_var_long Sys_pfs_max_mutex_instances( "performance_schema_max_mutex_instances", - "Maximum number of instrumented MUTEX objects.", + "Maximum number of instrumented MUTEX objects." + " Use 0 to disable, -1 for automated sizing.", PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_mutex_sizing), - CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 100*1024*1024), - DEFAULT(PFS_MAX_MUTEX), BLOCK_SIZE(1)); + CMD_LINE(REQUIRED_ARG), VALID_RANGE(-1, 100*1024*1024), + DEFAULT(-1), BLOCK_SIZE(1)); static Sys_var_ulong Sys_pfs_max_rwlock_classes( "performance_schema_max_rwlock_classes", @@ -162,26 +168,29 @@ static Sys_var_ulong Sys_pfs_max_rwlock_classes( CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 256), DEFAULT(PFS_MAX_RWLOCK_CLASS), BLOCK_SIZE(1)); -static Sys_var_ulong Sys_pfs_max_rwlock_instances( +static Sys_var_long Sys_pfs_max_rwlock_instances( "performance_schema_max_rwlock_instances", - "Maximum number of instrumented RWLOCK objects.", + "Maximum number of instrumented RWLOCK objects." + " Use 0 to disable, -1 for automated sizing.", PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_rwlock_sizing), - CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 100*1024*1024), - DEFAULT(PFS_MAX_RWLOCK), BLOCK_SIZE(1)); + CMD_LINE(REQUIRED_ARG), VALID_RANGE(-1, 100*1024*1024), + DEFAULT(-1), BLOCK_SIZE(1)); -static Sys_var_ulong Sys_pfs_max_table_handles( +static Sys_var_long Sys_pfs_max_table_handles( "performance_schema_max_table_handles", - "Maximum number of opened instrumented tables.", + "Maximum number of opened instrumented tables." + " Use 0 to disable, -1 for automated sizing.", PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_table_sizing), - CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 1024*1024), - DEFAULT(PFS_MAX_TABLE), BLOCK_SIZE(1)); + CMD_LINE(REQUIRED_ARG), VALID_RANGE(-1, 1024*1024), + DEFAULT(-1), BLOCK_SIZE(1)); -static Sys_var_ulong Sys_pfs_max_table_instances( +static Sys_var_long Sys_pfs_max_table_instances( "performance_schema_max_table_instances", - "Maximum number of instrumented tables.", + "Maximum number of instrumented tables." + " Use 0 to disable, -1 for automated sizing.", PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_table_share_sizing), - CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 1024*1024), - DEFAULT(PFS_MAX_TABLE_SHARE), BLOCK_SIZE(1)); + CMD_LINE(REQUIRED_ARG), VALID_RANGE(-1, 1024*1024), + DEFAULT(-1), BLOCK_SIZE(1)); static Sys_var_ulong Sys_pfs_max_thread_classes( "performance_schema_max_thread_classes", @@ -190,12 +199,13 @@ static Sys_var_ulong Sys_pfs_max_thread_classes( CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 256), DEFAULT(PFS_MAX_THREAD_CLASS), BLOCK_SIZE(1)); -static Sys_var_ulong Sys_pfs_max_thread_instances( +static Sys_var_long Sys_pfs_max_thread_instances( "performance_schema_max_thread_instances", - "Maximum number of instrumented threads.", + "Maximum number of instrumented threads." + " Use 0 to disable, -1 for automated sizing.", PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_thread_sizing), - CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 1024*1024), - DEFAULT(PFS_MAX_THREAD), BLOCK_SIZE(1)); + CMD_LINE(REQUIRED_ARG), VALID_RANGE(-1, 1024*1024), + DEFAULT(-1), BLOCK_SIZE(1)); static Sys_var_ulong Sys_pfs_setup_actors_size( "performance_schema_setup_actors_size", @@ -213,28 +223,31 @@ static Sys_var_ulong Sys_pfs_setup_objects_size( DEFAULT(PFS_MAX_SETUP_OBJECT), BLOCK_SIZE(1)); -static Sys_var_ulong Sys_pfs_accounts_size( +static Sys_var_long Sys_pfs_accounts_size( "performance_schema_accounts_size", - "Maximum number of instrumented user@host accounts.", + "Maximum number of instrumented user@host accounts." + " Use 0 to disable, -1 for automated sizing.", PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_account_sizing), - CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 1024*1024), - DEFAULT(PFS_MAX_ACCOUNT), + CMD_LINE(REQUIRED_ARG), VALID_RANGE(-1, 1024*1024), + DEFAULT(-1), BLOCK_SIZE(1)); -static Sys_var_ulong Sys_pfs_hosts_size( +static Sys_var_long Sys_pfs_hosts_size( "performance_schema_hosts_size", - "Maximum number of instrumented hosts.", + "Maximum number of instrumented hosts." + " Use 0 to disable, -1 for automated sizing.", PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_host_sizing), - CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 1024*1024), - DEFAULT(PFS_MAX_HOST), + CMD_LINE(REQUIRED_ARG), VALID_RANGE(-1, 1024*1024), + DEFAULT(-1), BLOCK_SIZE(1)); -static Sys_var_ulong Sys_pfs_users_size( +static Sys_var_long Sys_pfs_users_size( "performance_schema_users_size", - "Maximum number of instrumented users.", + "Maximum number of instrumented users." + " Use 0 to disable, -1 for automated sizing.", PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_user_sizing), - CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 1024*1024), - DEFAULT(PFS_MAX_USER), + CMD_LINE(REQUIRED_ARG), VALID_RANGE(-1, 1024*1024), + DEFAULT(-1), BLOCK_SIZE(1)); static Sys_var_ulong Sys_pfs_max_stage_classes( @@ -245,20 +258,22 @@ static Sys_var_ulong Sys_pfs_max_stage_classes( DEFAULT(PFS_MAX_STAGE_CLASS), BLOCK_SIZE(1)); -static Sys_var_ulong Sys_pfs_events_stages_history_long_size( +static Sys_var_long Sys_pfs_events_stages_history_long_size( "performance_schema_events_stages_history_long_size", - "Number of rows in EVENTS_STAGES_HISTORY_LONG.", + "Number of rows in EVENTS_STAGES_HISTORY_LONG." + " Use 0 to disable, -1 for automated sizing.", PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_events_stages_history_long_sizing), - CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 1024*1024), - DEFAULT(PFS_STAGES_HISTORY_LONG_SIZE), + CMD_LINE(REQUIRED_ARG), VALID_RANGE(-1, 1024*1024), + DEFAULT(-1), BLOCK_SIZE(1)); -static Sys_var_ulong Sys_pfs_events_stages_history_size( +static Sys_var_long Sys_pfs_events_stages_history_size( "performance_schema_events_stages_history_size", - "Number of rows per thread in EVENTS_STAGES_HISTORY.", + "Number of rows per thread in EVENTS_STAGES_HISTORY." + " Use 0 to disable, -1 for automated sizing.", PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_events_stages_history_sizing), - CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 1024), - DEFAULT(PFS_STAGES_HISTORY_SIZE), + CMD_LINE(REQUIRED_ARG), VALID_RANGE(-1, 1024), + DEFAULT(-1), BLOCK_SIZE(1)); /** @@ -278,28 +293,31 @@ static Sys_var_ulong Sys_pfs_max_statement_classes( DEFAULT((ulong) SQLCOM_END + (ulong) COM_END + 3), BLOCK_SIZE(1)); -static Sys_var_ulong Sys_pfs_events_statements_history_long_size( +static Sys_var_long Sys_pfs_events_statements_history_long_size( "performance_schema_events_statements_history_long_size", - "Number of rows in EVENTS_STATEMENTS_HISTORY_LONG.", + "Number of rows in EVENTS_STATEMENTS_HISTORY_LONG." + " Use 0 to disable, -1 for automated sizing.", PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_events_statements_history_long_sizing), - CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 1024*1024), - DEFAULT(PFS_STATEMENTS_HISTORY_LONG_SIZE), + CMD_LINE(REQUIRED_ARG), VALID_RANGE(-1, 1024*1024), + DEFAULT(-1), BLOCK_SIZE(1)); -static Sys_var_ulong Sys_pfs_events_statements_history_size( +static Sys_var_long Sys_pfs_events_statements_history_size( "performance_schema_events_statements_history_size", - "Number of rows per thread in EVENTS_STATEMENTS_HISTORY.", + "Number of rows per thread in EVENTS_STATEMENTS_HISTORY." + " Use 0 to disable, -1 for automated sizing.", PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_events_statements_history_sizing), - CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 1024), - DEFAULT(PFS_STATEMENTS_HISTORY_SIZE), + CMD_LINE(REQUIRED_ARG), VALID_RANGE(-1, 1024), + DEFAULT(-1), BLOCK_SIZE(1)); -static Sys_var_ulong Sys_pfs_digest_size( +static Sys_var_long Sys_pfs_digest_size( "performance_schema_digests_size", - "Size of the statement digest.", + "Size of the statement digest." + " Use 0 to disable, -1 for automated sizing.", PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_digest_sizing), - CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 200), - DEFAULT(PFS_DIGEST_SIZE), + CMD_LINE(REQUIRED_ARG), VALID_RANGE(-1, 200), + DEFAULT(-1), BLOCK_SIZE(1)); #endif /* WITH_PERFSCHEMA_STORAGE_ENGINE */ @@ -1187,7 +1205,7 @@ static bool check_max_allowed_packet(sys_var *self, THD *thd, set_var *var) val= var->save_result.ulonglong_value; if (val < (longlong) global_system_variables.net_buffer_length) { - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, WARN_OPTION_BELOW_LIMIT, ER(WARN_OPTION_BELOW_LIMIT), "max_allowed_packet", "net_buffer_length"); } @@ -1454,7 +1472,7 @@ static bool check_net_buffer_length(sys_var *self, THD *thd, set_var *var) val= var->save_result.ulonglong_value; if (val > (longlong) global_system_variables.max_allowed_packet) { - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, WARN_OPTION_BELOW_LIMIT, ER(WARN_OPTION_BELOW_LIMIT), "max_allowed_packet", "net_buffer_length"); } @@ -1986,7 +2004,7 @@ static bool fix_query_cache_size(sys_var *self, THD *thd, enum_var_type type) requested cache size. See also query_cache_size_arg */ if (query_cache_size != new_cache_size) - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN, ER_WARN_QC_RESIZE, ER(ER_WARN_QC_RESIZE), query_cache_size, new_cache_size); @@ -3026,7 +3044,7 @@ static Sys_var_session_special Sys_rand_seed2( static ulonglong read_error_count(THD *thd) { - return thd->warning_info->error_count(); + return thd->get_stmt_da()->error_count(); } // this really belongs to the SHOW STATUS static Sys_var_session_special Sys_error_count( @@ -3038,7 +3056,7 @@ static Sys_var_session_special Sys_error_count( static ulonglong read_warning_count(THD *thd) { - return thd->warning_info->warn_count(); + return thd->get_stmt_da()->warn_count(); } // this really belongs to the SHOW STATUS static Sys_var_session_special Sys_warning_count( @@ -3542,7 +3560,7 @@ get_master_info_uint_value(THD *thd, ptrdiff_t offset) mysql_mutex_lock(&LOCK_active_mi); mi= master_info_index-> get_master_info(&thd->variables.default_master_connection, - MYSQL_ERROR::WARN_LEVEL_WARN); + Sql_condition::WARN_LEVEL_WARN); if (mi) { mysql_mutex_lock(&mi->rli.data_lock); @@ -3564,7 +3582,7 @@ bool update_multi_source_variable(sys_var *self_var, THD *thd, mysql_mutex_lock(&LOCK_active_mi); mi= master_info_index-> get_master_info(&thd->variables.default_master_connection, - MYSQL_ERROR::WARN_LEVEL_ERROR); + Sql_condition::WARN_LEVEL_ERROR); if (mi) { mysql_mutex_lock(&mi->rli.run_lock); @@ -3711,7 +3729,7 @@ static bool check_locale(sys_var *self, THD *thd, set_var *var) mysql_mutex_unlock(&LOCK_error_messages); if (res) { - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR, "Can't process error message file for locale '%s'", locale->name); return true; diff --git a/sql/sys_vars.h b/sql/sys_vars.h index ffa7de118c8..54916bb929e 100644 --- a/sql/sys_vars.h +++ b/sql/sys_vars.h @@ -223,6 +223,8 @@ typedef Sys_var_integer<uint, GET_UINT, SHOW_UINT> Sys_var_uint; typedef Sys_var_integer<ulong, GET_ULONG, SHOW_ULONG> Sys_var_ulong; typedef Sys_var_integer<ha_rows, GET_HA_ROWS, SHOW_HA_ROWS> Sys_var_harows; typedef Sys_var_integer<ulonglong, GET_ULL, SHOW_ULONGLONG> Sys_var_ulonglong; +typedef Sys_var_integer<long, GET_LONG, SHOW_LONG> Sys_var_long; + /** Helper class for variables that take values from a TYPELIB diff --git a/sql/table.cc b/sql/table.cc index d7fd370dde5..85e0bb9a60d 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -154,7 +154,7 @@ View_creation_ctx * View_creation_ctx::create(THD *thd, if (!view->view_client_cs_name.str || !view->view_connection_cl_name.str) { - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_VIEW_NO_CREATION_CTX, ER(ER_VIEW_NO_CREATION_CTX), (const char *) view->db, @@ -188,7 +188,7 @@ View_creation_ctx * View_creation_ctx::create(THD *thd, (const char *) view->view_client_cs_name.str, (const char *) view->view_connection_cl_name.str); - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_VIEW_INVALID_CREATION_CTX, ER(ER_VIEW_INVALID_CREATION_CTX), (const char *) view->db, @@ -292,7 +292,7 @@ TABLE_CATEGORY get_table_category(const LEX_STRING *db, const LEX_STRING *name) # Share */ -TABLE_SHARE *alloc_table_share(TABLE_LIST *table_list, char *key, +TABLE_SHARE *alloc_table_share(TABLE_LIST *table_list, const char *key, uint key_length) { MEM_ROOT mem_root; @@ -426,14 +426,12 @@ void TABLE_SHARE::destroy() { uint idx; KEY *info_it; + DBUG_ENTER("TABLE_SHARE::destroy"); + DBUG_PRINT("info", ("db: %s table: %s", db.str, table_name.str)); - if (tmp_table == NO_TMP_TABLE) - mysql_mutex_lock(&LOCK_ha_data); free_root(&stats_cb.mem_root, MYF(0)); stats_cb.stats_can_be_read= FALSE; stats_cb.stats_is_read= FALSE; - if (tmp_table == NO_TMP_TABLE) - mysql_mutex_unlock(&LOCK_ha_data); /* The mutex is initialized only for shares that are part of the TDC */ if (tmp_table == NO_TMP_TABLE) @@ -453,24 +451,23 @@ void TABLE_SHARE::destroy() info_it->flags= 0; } } - if (ha_data_destroy) - { - ha_data_destroy(ha_data); - ha_data_destroy= NULL; - } -#ifdef WITH_PARTITION_STORAGE_ENGINE - if (ha_part_data_destroy) + if (ha_share) { - ha_part_data_destroy(ha_part_data); - ha_part_data_destroy= NULL; + delete ha_share; + ha_share= NULL; // Safety } -#endif /* WITH_PARTITION_STORAGE_ENGINE */ + +#ifdef HAVE_PSI_TABLE_INTERFACE + PSI_TABLE_CALL(release_table_share)(m_psi); +#endif + /* Make a copy since the share is allocated in its own root, and free_root() updates its argument after freeing the memory. */ MEM_ROOT own_root= mem_root; free_root(&own_root, MYF(0)); + DBUG_VOID_RETURN; } /* @@ -885,12 +882,12 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, if (disk_buff[0] & 0x80) { share->keys= keys= (disk_buff[1] << 7) | (disk_buff[0] & 0x7f); - share->user_defined_key_parts= key_parts= uint2korr(disk_buff+2); + share->key_parts= key_parts= uint2korr(disk_buff+2); } else { share->keys= keys= disk_buff[0]; - share->user_defined_key_parts= key_parts= disk_buff[1]; + share->key_parts= key_parts= disk_buff[1]; } share->keys_for_keyread.init(0); share->keys_in_use.init(keys); @@ -950,7 +947,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, if (i == 0) { ext_key_parts= key_parts + - (share->use_ext_keys ? first_keyinfo.key_parts*(keys-1) : 0); + (share->use_ext_keys ? first_keyinfo.user_defined_key_parts*(keys-1) : 0); n_length=keys * sizeof(KEY) + ext_key_parts * sizeof(KEY_PART_INFO); if (!(keyinfo= (KEY*) alloc_root(&share->mem_root, @@ -964,10 +961,10 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, sizeof(ulong) * ext_key_parts))) goto err; first_key_part= key_part; - first_key_parts= first_keyinfo.key_parts; + first_key_parts= first_keyinfo.user_defined_key_parts; keyinfo->flags= first_keyinfo.flags; keyinfo->key_length= first_keyinfo.key_length; - keyinfo->user_defined_key_parts= first_keyinfo.key_parts; + keyinfo->user_defined_key_parts= first_keyinfo.user_defined_key_parts; keyinfo->algorithm= first_keyinfo.algorithm; if (new_frm_ver >= 3) keyinfo->block_size= first_keyinfo.block_size; @@ -1555,7 +1552,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, "Please do \"ALTER TABLE '%s' FORCE\" to fix it!", share->fieldnames.type_names[i], share->table_name.str, share->table_name.str); - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_CRASHED_ON_USAGE, "Found incompatible DECIMAL field '%s' in %s; " "Please do \"ALTER TABLE '%s' FORCE\" to fix it!", @@ -1691,7 +1688,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, keyinfo->name_length+1); } - if (ext_key_parts > share->user_defined_key_parts && key) + if (ext_key_parts > share->key_parts && key) { KEY_PART_INFO *new_key_part= (keyinfo-1)->key_part + (keyinfo-1)->ext_key_parts; @@ -1863,7 +1860,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, "Please do \"ALTER TABLE '%s' FORCE \" to fix it!", share->table_name.str, share->table_name.str); - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_CRASHED_ON_USAGE, "Found wrong key definition in %s; " "Please do \"ALTER TABLE '%s' FORCE\" to fix " @@ -1909,7 +1906,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, If we are using an integer as the primary key then allow the user to refer to it as '_rowid' */ - if (share->key_info[primary_key].key_parts == 1) + if (share->key_info[primary_key].user_defined_key_parts == 1) { Field *field= share->key_info[primary_key].key_part[0].field; if (field && field->result_type() == INT_RESULT) @@ -2014,18 +2011,6 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, delete crypted; delete handler_file; my_hash_free(&share->name_hash); - if (share->ha_data_destroy) - { - share->ha_data_destroy(share->ha_data); - share->ha_data_destroy= NULL; - } -#ifdef WITH_PARTITION_STORAGE_ENGINE - if (share->ha_part_data_destroy) - { - share->ha_part_data_destroy(share->ha_part_data); - share->ha_data_destroy= NULL; - } -#endif /* WITH_PARTITION_STORAGE_ENGINE */ open_table_error(share, error, share->open_errno, errarg); DBUG_RETURN(error); @@ -2452,7 +2437,7 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias, outparam->field[(uint) (share->found_next_number_field - share->field)]; /* Fix key->name and key_part->field */ - if (share->user_defined_key_parts) + if (share->key_parts) { KEY *key_info, *key_info_end; KEY_PART_INFO *key_part; @@ -2588,8 +2573,9 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias, } outparam->part_info->is_auto_partitioned= share->auto_partitioned; DBUG_PRINT("info", ("autopartitioned: %u", share->auto_partitioned)); - /* we should perform the fix_partition_func in either local or - caller's arena depending on work_part_info_used value + /* + We should perform the fix_partition_func in either local or + caller's arena depending on work_part_info_used value. */ if (!work_part_info_used) tmp= fix_partition_func(thd, outparam, is_create_table); @@ -2763,6 +2749,7 @@ int closefrm(register TABLE *table, bool free_share) #ifdef WITH_PARTITION_STORAGE_ENGINE if (table->part_info) { + /* Allocated through table->mem_root, freed below */ free_items(table->part_info->item_free_list); table->part_info->item_free_list= 0; table->part_info= 0; @@ -3796,7 +3783,7 @@ bool TABLE_SHARE::visit_subgraph(Wait_for_flush *wait_for_flush, if (gvisitor->m_lock_open_count++ == 0) mysql_mutex_lock(&LOCK_open); - I_P_List_iterator <TABLE, TABLE_share> tables_it(used_tables); + TABLE_list::Iterator tables_it(used_tables); /* In case of multiple searches running in parallel, avoid going @@ -4455,27 +4442,32 @@ void TABLE_LIST::hide_view_error(THD *thd) return; /* Hide "Unknown column" or "Unknown function" error */ DBUG_ASSERT(thd->is_error()); + switch (thd->get_stmt_da()->sql_errno()) { + case ER_BAD_FIELD_ERROR: + case ER_SP_DOES_NOT_EXIST: + case ER_FUNC_INEXISTENT_NAME_COLLISION: + case ER_PROCACCESS_DENIED_ERROR: + case ER_COLUMNACCESS_DENIED_ERROR: + case ER_TABLEACCESS_DENIED_ERROR: + case ER_TABLE_NOT_LOCKED: + case ER_NO_SUCH_TABLE: + { + TABLE_LIST *top= top_table(); + thd->clear_error(); + my_error(ER_VIEW_INVALID, MYF(0), + top->view_db.str, top->view_name.str); + break; + } - if (thd->stmt_da->sql_errno() == ER_BAD_FIELD_ERROR || - thd->stmt_da->sql_errno() == ER_SP_DOES_NOT_EXIST || - thd->stmt_da->sql_errno() == ER_FUNC_INEXISTENT_NAME_COLLISION || - thd->stmt_da->sql_errno() == ER_PROCACCESS_DENIED_ERROR || - thd->stmt_da->sql_errno() == ER_COLUMNACCESS_DENIED_ERROR || - thd->stmt_da->sql_errno() == ER_TABLEACCESS_DENIED_ERROR || - thd->stmt_da->sql_errno() == ER_TABLE_NOT_LOCKED || - thd->stmt_da->sql_errno() == ER_NO_SUCH_TABLE) - { - TABLE_LIST *top= top_table(); - thd->clear_error(); - my_error(ER_VIEW_INVALID, MYF(0), top->view_db.str, top->view_name.str); - } - else if (thd->stmt_da->sql_errno() == ER_NO_DEFAULT_FOR_FIELD) - { - TABLE_LIST *top= top_table(); - thd->clear_error(); - // TODO: make correct error message - my_error(ER_NO_DEFAULT_FOR_VIEW_FIELD, MYF(0), - top->view_db.str, top->view_name.str); + case ER_NO_DEFAULT_FOR_FIELD: + { + TABLE_LIST *top= top_table(); + thd->clear_error(); + // TODO: make correct error message + my_error(ER_NO_DEFAULT_FOR_VIEW_FIELD, MYF(0), + top->view_db.str, top->view_name.str); + break; + } } } @@ -4551,7 +4543,7 @@ int TABLE_LIST::view_check_option(THD *thd, bool ignore_failure) TABLE_LIST *main_view= top_table(); if (ignore_failure) { - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_VIEW_CHECK_FAILED, ER(ER_VIEW_CHECK_FAILED), main_view->view_db.str, main_view->view_name.str); return(VIEW_CHECK_SKIP); @@ -4844,7 +4836,7 @@ bool TABLE_LIST::prepare_view_securety_context(THD *thd) if ((thd->lex->sql_command == SQLCOM_SHOW_CREATE) || (thd->lex->sql_command == SQLCOM_SHOW_FIELDS)) { - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_NO_SUCH_USER, ER(ER_NO_SUCH_USER), definer.user.str, definer.host.str); @@ -4873,6 +4865,7 @@ bool TABLE_LIST::prepare_view_securety_context(THD *thd) } } DBUG_RETURN(FALSE); + } #endif @@ -5639,7 +5632,7 @@ void TABLE::mark_columns_used_by_index_no_reset(uint index, { KEY_PART_INFO *key_part= key_info[index].key_part; KEY_PART_INFO *key_part_end= (key_part + - key_info[index].key_parts); + key_info[index].user_defined_key_parts); for (;key_part != key_part_end; key_part++) { bitmap_set_bit(bitmap, key_part->fieldnr-1); @@ -6486,7 +6479,7 @@ bool TABLE::update_const_key_parts(COND *conds) for (uint index= 0; index < s->keys; index++) { KEY_PART_INFO *keyinfo= key_info[index].key_part; - KEY_PART_INFO *keyinfo_end= keyinfo + key_info[index].key_parts; + KEY_PART_INFO *keyinfo_end= keyinfo + key_info[index].user_defined_key_parts; for (key_part_map part_map= (key_part_map)1; keyinfo < keyinfo_end; diff --git a/sql/table.h b/sql/table.h index 531b08bf813..aad0117b26a 100644 --- a/sql/table.h +++ b/sql/table.h @@ -496,19 +496,6 @@ typedef struct st_table_field_def } TABLE_FIELD_DEF; -#ifdef WITH_PARTITION_STORAGE_ENGINE -/** - Partition specific ha_data struct. -*/ -typedef struct st_ha_data_partition -{ - bool auto_inc_initialized; - mysql_mutex_t LOCK_auto_inc; /**< protecting auto_inc val */ - ulonglong next_auto_inc_val; /**< first non reserved value */ -} HA_DATA_PARTITION; -#endif - - class Table_check_intact { protected: @@ -602,8 +589,9 @@ struct TABLE_SHARE Doubly-linked (back-linked) lists of used and unused TABLE objects for this share. */ - I_P_List <TABLE, TABLE_share> used_tables; - I_P_List <TABLE, TABLE_share> free_tables; + typedef I_P_List <TABLE, TABLE_share> TABLE_list; + TABLE_list used_tables; + TABLE_list free_tables; engine_option_value *option_list; /* text options for table */ ha_table_option_struct *option_struct; /* structure with parsed options */ @@ -721,6 +709,9 @@ struct TABLE_SHARE */ int cached_row_logging_check; + /* Name of the tablespace used for this table */ + char *tablespace; + #ifdef WITH_PARTITION_STORAGE_ENGINE /* filled in when reading from frm */ bool auto_partitioned; @@ -742,16 +733,8 @@ struct TABLE_SHARE */ const TABLE_FIELD_DEF *table_field_def_cache; - /** place to store storage engine specific data */ - void *ha_data; - void (*ha_data_destroy)(void *); /* An optional destructor for ha_data */ - -#ifdef WITH_PARTITION_STORAGE_ENGINE - /** place to store partition specific data, LOCK_ha_data hold while init. */ - HA_DATA_PARTITION *ha_part_data; - /* Destructor for ha_part_data */ - void (*ha_part_data_destroy)(HA_DATA_PARTITION *); -#endif + /** Main handler's share */ + Handler_share *ha_share; /** Instrumentation for this table share. */ PSI_table_share *m_psi; @@ -1197,7 +1180,8 @@ public: Query_arena *expr_arena; #ifdef WITH_PARTITION_STORAGE_ENGINE partition_info *part_info; /* Partition related information */ - bool no_partitions_used; /* If true, all partitions have been pruned away */ + /* If true, all partitions have been pruned away */ + bool all_partitions_pruned_away; #endif uint max_keys; /* Size of allocated key_info array. */ bool stats_is_read; /* Persistent statistics is read for the table */ @@ -1967,6 +1951,11 @@ struct TABLE_LIST MDL_request mdl_request; +#ifdef WITH_PARTITION_STORAGE_ENGINE + /* List to carry partition names from PARTITION (...) clause in statement */ + List<String> *partition_names; +#endif /* WITH_PARTITION_STORAGE_ENGINE */ + void calc_md5(char *buffer); int view_check_option(THD *thd, bool ignore_failure); bool create_field_translation(THD *thd); @@ -2122,7 +2111,7 @@ struct TABLE_LIST @brief Returns the name of the database that the referenced table belongs to. */ - char *get_db_name() { return view != NULL ? view_db.str : db; } + char *get_db_name() const { return view != NULL ? view_db.str : db; } /** @brief Returns the name of the table that this TABLE_LIST represents. @@ -2130,7 +2119,7 @@ struct TABLE_LIST @details The unqualified table name or view name for a table or view, respectively. */ - char *get_table_name() { return view != NULL ? view_name.str : table_name; } + char *get_table_name() const { return view != NULL ? view_name.str : table_name; } bool is_active_sjm(); bool is_jtbm() { return test(jtbm_subselect!=NULL); } st_select_lex_unit *get_unit(); @@ -2419,7 +2408,7 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias, bool unpack_vcol_info_from_frm(THD *thd, MEM_ROOT *mem_root, TABLE *table, Field *field, LEX_STRING *vcol_expr, bool *error_reported); -TABLE_SHARE *alloc_table_share(TABLE_LIST *table_list, char *key, +TABLE_SHARE *alloc_table_share(TABLE_LIST *table_list, const char *key, uint key_length); void init_tmp_table_share(THD *thd, TABLE_SHARE *share, const char *key, uint key_length, diff --git a/sql/thr_malloc.cc b/sql/thr_malloc.cc index 548426587c5..a14ed36837b 100644 --- a/sql/thr_malloc.cc +++ b/sql/thr_malloc.cc @@ -46,10 +46,7 @@ extern "C" { returned in the error packet. - SHOW ERROR/SHOW WARNINGS may be empty. */ - thd->stmt_da->set_error_status(thd, - ER_OUT_OF_RESOURCES, - ER(ER_OUT_OF_RESOURCES), - NULL); + thd->get_stmt_da()->set_error_status(ER_OUT_OF_RESOURCES); } } diff --git a/sql/transaction.cc b/sql/transaction.cc index 7d8fc89ec8c..2265b42be1c 100644 --- a/sql/transaction.cc +++ b/sql/transaction.cc @@ -524,7 +524,7 @@ bool trans_rollback_to_savepoint(THD *thd, LEX_STRING name) else if (((thd->variables.option_bits & OPTION_KEEP_LOG) || thd->transaction.all.modified_non_trans_table) && !thd->slave_thread) - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + push_warning(thd, Sql_condition::WARN_LEVEL_WARN, ER_WARNING_NOT_COMPLETE_ROLLBACK, ER(ER_WARNING_NOT_COMPLETE_ROLLBACK)); @@ -801,7 +801,7 @@ bool trans_xa_rollback(THD *thd) ha_commit_or_rollback_by_xid(thd->lex->xid, 0); xid_cache_delete(xs); } - DBUG_RETURN(thd->stmt_da->is_error()); + DBUG_RETURN(thd->get_stmt_da()->is_error()); } if (xa_state != XA_IDLE && xa_state != XA_PREPARED && xa_state != XA_ROLLBACK_ONLY) diff --git a/sql/tztime.cc b/sql/tztime.cc index 665e624cd41..4fd68ca389e 100644 --- a/sql/tztime.cc +++ b/sql/tztime.cc @@ -1690,7 +1690,8 @@ my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap) MYSQL_OPEN_IGNORE_FLUSH | MYSQL_LOCK_IGNORE_TIMEOUT)) { sql_print_warning("Can't open and lock time zone table: %s " - "trying to live without them", thd->stmt_da->message()); + "trying to live without them", + thd->get_stmt_da()->message()); /* We will try emulate that everything is ok */ return_val= time_zone_tables_exist= 0; goto end_with_setting_default_tz; diff --git a/sql/unireg.cc b/sql/unireg.cc index 4596bb52747..a546fb769b3 100644 --- a/sql/unireg.cc +++ b/sql/unireg.cc @@ -67,9 +67,9 @@ struct Pack_header_error_handler: public Internal_error_handler virtual bool handle_condition(THD *thd, uint sql_errno, const char* sqlstate, - MYSQL_ERROR::enum_warning_level level, + Sql_condition::enum_warning_level level, const char* msg, - MYSQL_ERROR ** cond_hdl); + Sql_condition ** cond_hdl); bool is_handled; Pack_header_error_handler() :is_handled(FALSE) {} }; @@ -80,9 +80,9 @@ Pack_header_error_handler:: handle_condition(THD *, uint sql_errno, const char*, - MYSQL_ERROR::enum_warning_level, + Sql_condition::enum_warning_level, const char*, - MYSQL_ERROR ** cond_hdl) + Sql_condition ** cond_hdl) { *cond_hdl= NULL; is_handled= (sql_errno == ER_TOO_MANY_FIELDS); @@ -252,8 +252,8 @@ bool mysql_create_frm(THD *thd, const char *file_name, my_snprintf(warn_buff, sizeof(warn_buff), ER(ER_TOO_LONG_TABLE_COMMENT), real_table_name, static_cast<ulong>(TABLE_COMMENT_MAXLEN)); /* do not push duplicate warnings */ - if (!check_duplicate_warning(current_thd, warn_buff, strlen(warn_buff))) - push_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + if (!thd->get_stmt_da()->has_sql_condition(warn_buff, strlen(warn_buff))) + push_warning(current_thd, Sql_condition::WARN_LEVEL_WARN, ER_TOO_LONG_TABLE_COMMENT, warn_buff); create_info->comment.length= tmp_len; } @@ -457,31 +457,32 @@ err3: } /* mysql_create_frm */ -/* +/** Create a frm (table definition) file and the tables - SYNOPSIS - rea_create_table() - thd Thread handler - path Name of file (including database, without .frm) - db Data base name - table_name Table name - create_info create info parameters - create_fields Fields to create - keys number of keys to create - key_info Keys to create - file Handler to use - - RETURN - 0 ok - 1 error + @param thd Thread handler + @param path Name of file (including database, without .frm) + @param db Data base name + @param table_name Table name + @param create_info create info parameters + @param create_fields Fields to create + @param keys number of keys to create + @param key_info Keys to create + @param file Handler to use + @param no_ha_table Indicates that only .FRM file (and PAR file if table + is partitioned) needs to be created and not a table + in the storage engine. + + @retval 0 ok + @retval 1 error */ int rea_create_table(THD *thd, const char *path, const char *db, const char *table_name, HA_CREATE_INFO *create_info, List<Create_field> &create_fields, - uint keys, KEY *key_info, handler *file) + uint keys, KEY *key_info, handler *file, + bool no_ha_table) { DBUG_ENTER("rea_create_table"); @@ -496,15 +497,20 @@ int rea_create_table(THD *thd, const char *path, DBUG_ASSERT(*fn_rext(frm_name)); if (thd->variables.keep_files_on_create) create_info->options|= HA_CREATE_KEEP_FILES; - if (!create_info->frm_only && - (file->ha_create_handler_files(path, NULL, CHF_CREATE_FLAG, - create_info) || - ha_create_table(thd, path, db, table_name, create_info, 0))) + + if (file->ha_create_handler_files(path, NULL, CHF_CREATE_FLAG, + create_info)) + goto err_handler_frm; + + if (!no_ha_table && + ha_create_table(thd, path, db, table_name, create_info, 0)) goto err_handler; DBUG_RETURN(0); err_handler: (void) file->ha_create_handler_files(path, NULL, CHF_DELETE_FLAG, create_info); + +err_handler_frm: mysql_file_delete(key_file_frm, frm_name, MYF(0)); DBUG_RETURN(1); } /* rea_create_table */ @@ -603,15 +609,15 @@ static uint pack_keys(uchar *keybuff, uint key_count, KEY *keyinfo, { int2store(pos, (key->flags ^ HA_NOSAME)); int2store(pos+2,key->key_length); - pos[4]= (uchar) key->key_parts; + pos[4]= (uchar) key->user_defined_key_parts; pos[5]= (uchar) key->algorithm; int2store(pos+6, key->block_size); pos+=8; - key_parts+=key->key_parts; + key_parts+=key->user_defined_key_parts; DBUG_PRINT("loop", ("flags: %lu key_parts: %d key_part: 0x%lx", - key->flags, key->key_parts, + key->flags, key->user_defined_key_parts, (long) key->key_part)); - for (key_part=key->key_part,key_part_end=key_part+key->key_parts ; + for (key_part=key->key_part,key_part_end=key_part+key->user_defined_key_parts ; key_part != key_part_end ; key_part++) @@ -706,7 +712,9 @@ static bool pack_header(uchar *forminfo, enum legacy_db_type table_type, COLUMN_COMMENT_MAXLEN); if (tmp_len < field->comment.length) { - if (current_thd->is_strict_mode()) + THD *thd= current_thd; + + if (thd->is_strict_mode()) { my_error(ER_TOO_LONG_FIELD_COMMENT, MYF(0), field->field_name, static_cast<ulong>(COLUMN_COMMENT_MAXLEN)); @@ -717,8 +725,8 @@ static bool pack_header(uchar *forminfo, enum legacy_db_type table_type, field->field_name, static_cast<ulong>(COLUMN_COMMENT_MAXLEN)); /* do not push duplicate warnings */ - if (!check_duplicate_warning(current_thd, warn_buff, strlen(warn_buff))) - push_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + if (!thd->get_stmt_da()->has_sql_condition(warn_buff, strlen(warn_buff))) + push_warning(current_thd, Sql_condition::WARN_LEVEL_WARN, ER_TOO_LONG_FIELD_COMMENT, warn_buff); field->comment.length= tmp_len; } diff --git a/sql/unireg.h b/sql/unireg.h index da510bb4e6d..a1dffcf3d78 100644 --- a/sql/unireg.h +++ b/sql/unireg.h @@ -134,6 +134,10 @@ typedef struct st_ha_create_information HA_CREATE_INFO; The flag means that I_S table uses optimization algorithm. */ #define OPTIMIZE_I_S_TABLE OPEN_VIEW_FULL*2 +/** + This flag is used to instruct tdc_open_view() to check metadata version. +*/ +#define CHECK_METADATA_VERSION OPEN_TRIGGER_ONLY*2 /* The flag means that we need to process trigger files only. @@ -179,5 +183,6 @@ int rea_create_table(THD *thd, const char *path, HA_CREATE_INFO *create_info, List<Create_field> &create_field, uint key_count,KEY *key_info, - handler *file); + handler *file, + bool no_ha_table); #endif diff --git a/storage/archive/ha_archive.cc b/storage/archive/ha_archive.cc index 5a6251a5de6..291cd6a398b 100644 --- a/storage/archive/ha_archive.cc +++ b/storage/archive/ha_archive.cc @@ -97,15 +97,20 @@ -Brian */ -/* Variables for archive share methods */ -mysql_mutex_t archive_mutex; -static HASH archive_open_tables; /* The file extension */ #define ARZ ".ARZ" // The data file #define ARN ".ARN" // Files used during an optimize call #define ARM ".ARM" // Meta file (deprecated) +/* 5.0 compatibility */ +#define META_V1_OFFSET_CHECK_HEADER 0 +#define META_V1_OFFSET_VERSION 1 +#define META_V1_OFFSET_ROWS_RECORDED 2 +#define META_V1_OFFSET_CHECK_POINT 10 +#define META_V1_OFFSET_CRASHED 18 +#define META_V1_LENGTH 19 + /* uchar + uchar */ @@ -142,23 +147,12 @@ static handler *archive_create_handler(handlerton *hton, return new (mem_root) ha_archive(hton, table); } -/* - Used for hash table that tracks open tables. -*/ -static uchar* archive_get_key(ARCHIVE_SHARE *share, size_t *length, - my_bool not_used __attribute__((unused))) -{ - *length=share->table_name_length; - return (uchar*) share->table_name; -} - #ifdef HAVE_PSI_INTERFACE -PSI_mutex_key az_key_mutex_archive_mutex, az_key_mutex_ARCHIVE_SHARE_mutex; +PSI_mutex_key az_key_mutex_Archive_share_mutex; static PSI_mutex_info all_archive_mutexes[]= { - { &az_key_mutex_archive_mutex, "archive_mutex", PSI_FLAG_GLOBAL}, - { &az_key_mutex_ARCHIVE_SHARE_mutex, "ARCHIVE_SHARE::mutex", 0} + { &az_key_mutex_Archive_share_mutex, "Archive_share::mutex", 0} }; PSI_file_key arch_key_file_metadata, arch_key_file_data, arch_key_file_frm; @@ -214,39 +208,24 @@ int archive_db_init(void *p) archive_hton->flags= HTON_NO_FLAGS; archive_hton->discover= archive_discover; - if (mysql_mutex_init(az_key_mutex_archive_mutex, - &archive_mutex, MY_MUTEX_INIT_FAST)) - goto error; - if (my_hash_init(&archive_open_tables, table_alias_charset, 32, 0, 0, - (my_hash_get_key) archive_get_key, 0, 0)) - { - mysql_mutex_destroy(&archive_mutex); - } - else - { - DBUG_RETURN(FALSE); - } -error: - DBUG_RETURN(TRUE); + DBUG_RETURN(0); } -/* - Release the archive handler. - - SYNOPSIS - archive_db_done() - void - RETURN - FALSE OK -*/ - -int archive_db_done(void *p) +Archive_share::Archive_share() { - my_hash_free(&archive_open_tables); - mysql_mutex_destroy(&archive_mutex); - - return 0; + crashed= false; + in_optimize= false; + archive_write_open= false; + dirty= false; + DBUG_PRINT("ha_archive", ("Archive_share: %p", + this)); + thr_lock_init(&lock); + /* + We will use this lock for rows. + */ + mysql_mutex_init(az_key_mutex_Archive_share_mutex, + &mutex, MY_MUTEX_INIT_FAST); } @@ -301,6 +280,103 @@ err: DBUG_RETURN(1); } +/** + @brief Read version 1 meta file (5.0 compatibility routine). + + @return Completion status + @retval 0 Success + @retval !0 Failure +*/ + +int Archive_share::read_v1_metafile() +{ + char file_name[FN_REFLEN]; + uchar buf[META_V1_LENGTH]; + File fd; + DBUG_ENTER("Archive_share::read_v1_metafile"); + + fn_format(file_name, data_file_name, "", ARM, MY_REPLACE_EXT); + if ((fd= mysql_file_open(arch_key_file_metadata, file_name, O_RDONLY, MYF(0))) == -1) + DBUG_RETURN(-1); + + if (mysql_file_read(fd, buf, sizeof(buf), MYF(0)) != sizeof(buf)) + { + mysql_file_close(fd, MYF(0)); + DBUG_RETURN(-1); + } + + rows_recorded= uint8korr(buf + META_V1_OFFSET_ROWS_RECORDED); + crashed= buf[META_V1_OFFSET_CRASHED]; + mysql_file_close(fd, MYF(0)); + DBUG_RETURN(0); +} + + +/** + @brief Write version 1 meta file (5.0 compatibility routine). + + @return Completion status + @retval 0 Success + @retval !0 Failure +*/ + +int Archive_share::write_v1_metafile() +{ + char file_name[FN_REFLEN]; + uchar buf[META_V1_LENGTH]; + File fd; + DBUG_ENTER("Archive_share::write_v1_metafile"); + + buf[META_V1_OFFSET_CHECK_HEADER]= ARCHIVE_CHECK_HEADER; + buf[META_V1_OFFSET_VERSION]= 1; + int8store(buf + META_V1_OFFSET_ROWS_RECORDED, rows_recorded); + int8store(buf + META_V1_OFFSET_CHECK_POINT, (ulonglong) 0); + buf[META_V1_OFFSET_CRASHED]= crashed; + + fn_format(file_name, data_file_name, "", ARM, MY_REPLACE_EXT); + if ((fd= mysql_file_open(arch_key_file_metadata, file_name, O_WRONLY, MYF(0))) == -1) + DBUG_RETURN(-1); + + if (mysql_file_write(fd, buf, sizeof(buf), MYF(0)) != sizeof(buf)) + { + mysql_file_close(fd, MYF(0)); + DBUG_RETURN(-1); + } + + mysql_file_close(fd, MYF(0)); + DBUG_RETURN(0); +} + +/** + @brief Pack version 1 row (5.0 compatibility routine). + + @param[in] record the record to pack + + @return Length of packed row +*/ + +unsigned int ha_archive::pack_row_v1(uchar *record) +{ + uint *blob, *end; + uchar *pos; + DBUG_ENTER("pack_row_v1"); + memcpy(record_buffer->buffer, record, table->s->reclength); + pos= record_buffer->buffer + table->s->reclength; + for (blob= table->s->blob_field, end= blob + table->s->blob_fields; + blob != end; blob++) + { + uint32 length= ((Field_blob *) table->field[*blob])->get_length(); + if (length) + { + uchar *data_ptr; + ((Field_blob *) table->field[*blob])->get_ptr(&data_ptr); + memcpy(pos, data_ptr, length); + pos+= length; + } + } + DBUG_RETURN(pos - record_buffer->buffer); +} + /* This method reads the header of a datafile and returns whether or not it was successful. */ @@ -353,156 +429,102 @@ int ha_archive::read_data_header(azio_stream *file_to_read) See ha_example.cc for a longer description. */ -ARCHIVE_SHARE *ha_archive::get_share(const char *table_name, int *rc) +Archive_share *ha_archive::get_share(const char *table_name, int *rc) { - uint length; - DBUG_ENTER("ha_archive::get_share"); + Archive_share *tmp_share; - mysql_mutex_lock(&archive_mutex); - length=(uint) strlen(table_name); + DBUG_ENTER("ha_archive::get_share"); - if (!(share=(ARCHIVE_SHARE*) my_hash_search(&archive_open_tables, - (uchar*) table_name, - length))) + lock_shared_ha_data(); + if (!(tmp_share= static_cast<Archive_share*>(get_ha_share_ptr()))) { - char *tmp_name; azio_stream archive_tmp; - if (!my_multi_malloc(MYF(MY_WME | MY_ZEROFILL), - &share, sizeof(*share), - &tmp_name, length+1, - NullS)) + tmp_share= new Archive_share; + + if (!tmp_share) { - mysql_mutex_unlock(&archive_mutex); *rc= HA_ERR_OUT_OF_MEM; - DBUG_RETURN(NULL); + goto err; } + DBUG_PRINT("ha_archive", ("new Archive_share: %p", + tmp_share)); - share->use_count= 0; - share->table_name_length= length; - share->table_name= tmp_name; - share->crashed= FALSE; - share->archive_write_open= FALSE; - fn_format(share->data_file_name, table_name, "", + fn_format(tmp_share->data_file_name, table_name, "", ARZ, MY_REPLACE_EXT | MY_UNPACK_FILENAME); - strmov(share->table_name, table_name); - DBUG_PRINT("ha_archive", ("Data File %s", - share->data_file_name)); - /* - We will use this lock for rows. - */ - mysql_mutex_init(az_key_mutex_ARCHIVE_SHARE_mutex, - &share->mutex, MY_MUTEX_INIT_FAST); - + strmov(tmp_share->table_name, table_name); + DBUG_PRINT("ha_archive", ("Data File %s", + tmp_share->data_file_name)); + /* We read the meta file, but do not mark it dirty. Since we are not doing a write we won't mark it dirty (and we won't open it for anything but reading... open it for write and we will generate null compression writes). */ - if (!(azopen(&archive_tmp, share->data_file_name, O_RDONLY|O_BINARY))) + if (!(azopen(&archive_tmp, tmp_share->data_file_name, O_RDONLY|O_BINARY))) { - *rc= my_errno ? my_errno : -1; - mysql_mutex_unlock(&archive_mutex); - mysql_mutex_destroy(&share->mutex); - my_free(share); - DBUG_RETURN(NULL); + delete tmp_share; + *rc= my_errno ? my_errno : HA_ERR_CRASHED; + tmp_share= NULL; + goto err; } - share->version= archive_tmp.version; - if (archive_tmp.version == ARCHIVE_VERSION) - { - stats.auto_increment_value= archive_tmp.auto_increment + 1; - share->rows_recorded= (ha_rows)archive_tmp.rows; - share->crashed= archive_tmp.dirty; - } - else - { - /* Used by repair */ - share->rows_recorded= ~(ha_rows) 0; - stats.auto_increment_value= 0; - } - /* - If archive version is less than 3, It should be upgraded before - use. - */ - if (archive_tmp.version < ARCHIVE_VERSION) - *rc= HA_ERR_TABLE_NEEDS_UPGRADE; + stats.auto_increment_value= archive_tmp.auto_increment + 1; + tmp_share->rows_recorded= (ha_rows)archive_tmp.rows; + tmp_share->crashed= archive_tmp.dirty; + share= tmp_share; + if (archive_tmp.version == 1) + share->read_v1_metafile(); azclose(&archive_tmp); - (void) my_hash_insert(&archive_open_tables, (uchar*) share); - thr_lock_init(&share->lock); + set_ha_share_ptr(static_cast<Handler_share*>(tmp_share)); } - share->use_count++; - DBUG_PRINT("ha_archive", ("archive table %.*s has %d open handles now", - share->table_name_length, share->table_name, - share->use_count)); - if (share->crashed) + if (tmp_share->crashed) *rc= HA_ERR_CRASHED_ON_USAGE; - mysql_mutex_unlock(&archive_mutex); - - DBUG_RETURN(share); -} - +err: + unlock_shared_ha_data(); -/* - Free the share. - See ha_example.cc for a description. -*/ -int ha_archive::free_share() -{ - int rc= 0; - DBUG_ENTER("ha_archive::free_share"); - DBUG_PRINT("ha_archive", - ("archive table %.*s has %d open handles on entrance", - share->table_name_length, share->table_name, - share->use_count)); - - mysql_mutex_lock(&archive_mutex); - if (!--share->use_count) - { - my_hash_delete(&archive_open_tables, (uchar*) share); - thr_lock_delete(&share->lock); - mysql_mutex_destroy(&share->mutex); - /* - We need to make sure we don't reset the crashed state. - If we open a crashed file, wee need to close it as crashed unless - it has been repaired. - Since we will close the data down after this, we go on and count - the flush on close; - */ - if (share->archive_write_open) - { - if (azclose(&(share->archive_write))) - rc= 1; - } - my_free(share); - } - mysql_mutex_unlock(&archive_mutex); + DBUG_ASSERT(tmp_share || *rc); - DBUG_RETURN(rc); + DBUG_RETURN(tmp_share); } -int ha_archive::init_archive_writer() + +int Archive_share::init_archive_writer() { - DBUG_ENTER("ha_archive::init_archive_writer"); - /* + DBUG_ENTER("Archive_share::init_archive_writer"); + /* It is expensive to open and close the data files and since you can't have a gzip file that can be both read and written we keep a writer open that is shared amoung all open tables. */ - if (!(azopen(&(share->archive_write), share->data_file_name, + if (!(azopen(&archive_write, data_file_name, O_RDWR|O_BINARY))) { DBUG_PRINT("ha_archive", ("Could not open archive write file")); - share->crashed= TRUE; + crashed= true; DBUG_RETURN(1); } - share->archive_write_open= TRUE; + archive_write_open= true; DBUG_RETURN(0); } +void Archive_share::close_archive_writer() +{ + mysql_mutex_assert_owner(&mutex); + if (archive_write_open) + { + if (archive_write.version == 1) + (void) write_v1_metafile(); + azclose(&archive_write); + archive_write_open= false; + dirty= false; + } +} + + /* No locks are required because it is associated with just one handler instance */ @@ -512,7 +534,8 @@ int ha_archive::init_archive_reader() /* It is expensive to open and close the data files and since you can't have a gzip file that can be both read and written we keep a writer open - that is shared amoung all open tables. + that is shared amoung all open tables, but have one reader open for + each handler instance. */ if (!archive_reader_open) { @@ -557,6 +580,8 @@ int ha_archive::open(const char *name, int mode, uint open_options) DBUG_PRINT("ha_archive", ("archive table was opened for crash: %s", (open_options & HA_OPEN_FOR_REPAIR) ? "yes" : "no")); share= get_share(name, &rc); + if (!share) + DBUG_RETURN(rc); /* Allow open on crashed table in repair mode only. @@ -583,7 +608,6 @@ int ha_archive::open(const char *name, int mode, uint open_options) rc= 0; break; } - free_share(); /* fall through */ default: DBUG_RETURN(rc); @@ -595,13 +619,17 @@ int ha_archive::open(const char *name, int mode, uint open_options) ARCHIVE_ROW_HEADER_SIZE); if (!record_buffer) - { - free_share(); DBUG_RETURN(HA_ERR_OUT_OF_MEM); - } thr_lock_data_init(&share->lock, &lock, NULL); + DBUG_PRINT("ha_archive", ("archive table was crashed %s", + rc == HA_ERR_CRASHED_ON_USAGE ? "yes" : "no")); + if (rc == HA_ERR_CRASHED_ON_USAGE && open_options & HA_OPEN_FOR_REPAIR) + { + DBUG_RETURN(0); + } + DBUG_RETURN(rc); } @@ -636,8 +664,6 @@ int ha_archive::close(void) if (azclose(&archive)) rc= 1; } - /* then also close share */ - rc|= free_share(); DBUG_RETURN(rc); } @@ -812,7 +838,7 @@ int ha_archive::real_write_row(uchar *buf, azio_stream *writer) DBUG_ENTER("ha_archive::real_write_row"); /* We pack the row for writing */ - r_pack_length= pack_row(buf); + r_pack_length= pack_row(buf, writer); written= azwrite(writer, record_buffer->buffer, r_pack_length); if (written != r_pack_length) @@ -853,7 +879,7 @@ uint32 ha_archive::max_row_length(const uchar *buf) } -unsigned int ha_archive::pack_row(uchar *record) +unsigned int ha_archive::pack_row(uchar *record, azio_stream *writer) { uchar *ptr; @@ -863,6 +889,9 @@ unsigned int ha_archive::pack_row(uchar *record) if (fix_rec_buff(max_row_length(record))) DBUG_RETURN(HA_ERR_OUT_OF_MEM); /* purecov: inspected */ + if (writer->version == 1) + DBUG_RETURN(pack_row_v1(record)); + /* Copy null bits */ memcpy(record_buffer->buffer+ARCHIVE_ROW_HEADER_SIZE, record, table->s->null_bytes); @@ -907,8 +936,8 @@ int ha_archive::write_row(uchar *buf) mysql_mutex_lock(&share->mutex); if (!share->archive_write_open) - if (init_archive_writer()) - DBUG_RETURN(errno); + if (share->init_archive_writer()) + DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE); if (table->next_number_field && record == table->record[0]) @@ -1373,11 +1402,26 @@ end: DBUG_RETURN(rc); } + +/** + @brief Check for upgrade + + @param[in] check_opt check options + + @return Completion status + @retval HA_ADMIN_OK No upgrade required + @retval HA_ADMIN_CORRUPT Cannot read meta-data + @retval HA_ADMIN_NEEDS_UPGRADE Upgrade required +*/ + int ha_archive::check_for_upgrade(HA_CHECK_OPT *check_opt) { - if (share->version < ARCHIVE_VERSION) - return HA_ADMIN_NEEDS_ALTER; - return 0; + DBUG_ENTER("ha_archive::check_for_upgrade"); + if (init_archive_reader()) + DBUG_RETURN(HA_ADMIN_CORRUPT); + if (archive.version < ARCHIVE_VERSION) + DBUG_RETURN(HA_ADMIN_NEEDS_UPGRADE); + DBUG_RETURN(HA_ADMIN_OK); } @@ -1573,6 +1617,7 @@ THR_LOCK_DATA **ha_archive::store_lock(THD *thd, void ha_archive::update_create_info(HA_CREATE_INFO *create_info) { + char tmp_real_path[FN_REFLEN]; DBUG_ENTER("ha_archive::update_create_info"); ha_archive::info(HA_STATUS_AUTO); @@ -1581,8 +1626,8 @@ void ha_archive::update_create_info(HA_CREATE_INFO *create_info) create_info->auto_increment_value= stats.auto_increment_value; } - if (!(my_readlink(share->real_path, share->data_file_name, MYF(0)))) - create_info->data_file_name= share->real_path; + if (!(my_readlink(tmp_real_path, share->data_file_name, MYF(0)))) + create_info->data_file_name= sql_strdup(tmp_real_path); DBUG_VOID_RETURN; } @@ -1806,6 +1851,20 @@ void ha_archive::destroy_record_buffer(archive_record_buffer *r) DBUG_VOID_RETURN; } +bool ha_archive::check_if_incompatible_data(HA_CREATE_INFO *info, + uint table_changes) +{ + if (info->auto_increment_value != stats.auto_increment_value || + (info->used_fields & HA_CREATE_USED_DATADIR) || + info->data_file_name || + (info->used_fields & HA_CREATE_USED_COMMENT) || + table_changes != IS_EQUAL_YES) + return COMPATIBLE_DATA_NO; + + return COMPATIBLE_DATA_YES; +} + + struct st_mysql_storage_engine archive_storage_engine= { MYSQL_HANDLERTON_INTERFACE_VERSION }; @@ -1818,7 +1877,7 @@ mysql_declare_plugin(archive) "Archive storage engine", PLUGIN_LICENSE_GPL, archive_db_init, /* Plugin Init */ - archive_db_done, /* Plugin Deinit */ + NULL, /* Plugin Deinit */ 0x0300 /* 3.0 */, NULL, /* status variables */ NULL, /* system variables */ @@ -1835,7 +1894,7 @@ maria_declare_plugin(archive) "Archive storage engine", PLUGIN_LICENSE_GPL, archive_db_init, /* Plugin Init */ - archive_db_done, /* Plugin Deinit */ + NULL, /* Plugin Deinit */ 0x0300 /* 3.0 */, NULL, /* status variables */ NULL, /* system variables */ diff --git a/storage/archive/ha_archive.h b/storage/archive/ha_archive.h index 627267c7306..0b6c9eab472 100644 --- a/storage/archive/ha_archive.h +++ b/storage/archive/ha_archive.h @@ -32,20 +32,38 @@ typedef struct st_archive_record_buffer { } archive_record_buffer; -typedef struct st_archive_share { - char *table_name; - char data_file_name[FN_REFLEN]; - uint table_name_length,use_count, version; +class Archive_share : public Handler_share +{ +public: mysql_mutex_t mutex; THR_LOCK lock; azio_stream archive_write; /* Archive file we are working with */ + ha_rows rows_recorded; /* Number of rows in tables */ + char table_name[FN_REFLEN]; + char data_file_name[FN_REFLEN]; + bool in_optimize; bool archive_write_open; bool dirty; /* Flag for if a flush should occur */ bool crashed; /* Meta file is crashed */ - ha_rows rows_recorded; /* Number of rows in tables */ - ulonglong mean_rec_length; - char real_path[FN_REFLEN]; -} ARCHIVE_SHARE; + Archive_share(); + ~Archive_share() + { + DBUG_PRINT("ha_archive", ("~Archive_share: %p", + this)); + if (archive_write_open) + { + mysql_mutex_lock(&mutex); + (void) close_archive_writer(); + mysql_mutex_unlock(&mutex); + } + thr_lock_delete(&lock); + mysql_mutex_destroy(&mutex); + } + int init_archive_writer(); + void close_archive_writer(); + int write_v1_metafile(); + int read_v1_metafile(); +}; /* Version for file format. @@ -58,7 +76,7 @@ typedef struct st_archive_share { class ha_archive: public handler { THR_LOCK_DATA lock; /* MySQL lock */ - ARCHIVE_SHARE *share; /* Shared lock info */ + Archive_share *share; /* Shared lock info */ azio_stream archive; /* Archive file we are working with */ my_off_t current_position; /* The position of the row we just read */ @@ -76,6 +94,7 @@ class ha_archive: public handler archive_record_buffer *create_record_buffer(unsigned int length); void destroy_record_buffer(archive_record_buffer *r); int frm_copy(azio_stream *src, azio_stream *dst); + unsigned int pack_row_v1(uchar *record); public: ha_archive(handlerton *hton, TABLE_SHARE *table_arg); @@ -121,9 +140,7 @@ public: int get_row(azio_stream *file_to_read, uchar *buf); int get_row_version2(azio_stream *file_to_read, uchar *buf); int get_row_version3(azio_stream *file_to_read, uchar *buf); - ARCHIVE_SHARE *get_share(const char *table_name, int *rc); - int free_share(); - int init_archive_writer(); + Archive_share *get_share(const char *table_name, int *rc); int init_archive_reader(); // Always try auto_repair in case of HA_ERR_CRASHED_ON_USAGE bool auto_repair(int error) const @@ -150,6 +167,7 @@ public: uint32 max_row_length(const uchar *buf); bool fix_rec_buff(unsigned int length); int unpack_row(azio_stream *file_to_read, uchar *record); - unsigned int pack_row(uchar *record); + unsigned int pack_row(uchar *record, azio_stream *writer); + bool check_if_incompatible_data(HA_CREATE_INFO *info, uint table_changes); }; diff --git a/storage/cassandra/ha_cassandra.cc b/storage/cassandra/ha_cassandra.cc index e8c5b844e3d..2998accc874 100644 --- a/storage/cassandra/ha_cassandra.cc +++ b/storage/cassandra/ha_cassandra.cc @@ -2344,7 +2344,7 @@ int ha_cassandra::multi_range_read_explain_info(uint mrr_mode, char *str, size_t if (!(mrr_mode & HA_MRR_USE_DEFAULT_IMPL)) { uint mrr_str_len= strlen(mrr_str); - uint copy_len= min(mrr_str_len, size); + uint copy_len= MY_MIN(mrr_str_len, size); memcpy(str, mrr_str, size); return copy_len; } diff --git a/storage/example/ha_example.cc b/storage/example/ha_example.cc index 98968d0b5b5..b37845bf9ec 100644 --- a/storage/example/ha_example.cc +++ b/storage/example/ha_example.cc @@ -107,17 +107,6 @@ static handler *example_create_handler(handlerton *hton, handlerton *example_hton; -/* Variables for example share methods */ - -/* - Hash used to track the number of open tables; variable for example share - methods -*/ -static HASH example_open_tables; - -/* The mutex used to init the hash; variable for example share methods */ -mysql_mutex_t example_mutex; - /** Structure for CREATE TABLE options (table options). @@ -199,20 +188,12 @@ ha_create_table_option example_field_option_list[]= Function we use in the creation of our hash to get key. */ -static uchar* example_get_key(EXAMPLE_SHARE *share, size_t *length, - my_bool not_used __attribute__((unused))) -{ - *length=share->table_name_length; - return (uchar*) share->table_name; -} - #ifdef HAVE_PSI_INTERFACE -static PSI_mutex_key ex_key_mutex_example, ex_key_mutex_EXAMPLE_SHARE_mutex; +static PSI_mutex_key ex_key_mutex_Example_share_mutex; static PSI_mutex_info all_example_mutexes[]= { - { &ex_key_mutex_example, "example", PSI_FLAG_GLOBAL}, - { &ex_key_mutex_EXAMPLE_SHARE_mutex, "EXAMPLE_SHARE::mutex", 0} + { &ex_key_mutex_Example_share_mutex, "Example_share::mutex", 0} }; static void init_example_psi_keys() @@ -220,15 +201,20 @@ static void init_example_psi_keys() const char* category= "example"; int count; - if (PSI_server == NULL) - return; - count= array_elements(all_example_mutexes); - PSI_server->register_mutex(category, all_example_mutexes, count); + mysql_mutex_register(category, all_example_mutexes, count); } #endif +Example_share::Example_share() +{ + thr_lock_init(&lock); + mysql_mutex_init(ex_key_mutex_Example_share_mutex, + &mutex, MY_MUTEX_INIT_FAST); +} + + static int example_init_func(void *p) { DBUG_ENTER("example_init_func"); @@ -238,10 +224,6 @@ static int example_init_func(void *p) #endif example_hton= (handlerton *)p; - mysql_mutex_init(ex_key_mutex_example, &example_mutex, MY_MUTEX_INIT_FAST); - (void) my_hash_init(&example_open_tables,system_charset_info,32,0,0, - (my_hash_get_key) example_get_key,0,0); - example_hton->state= SHOW_OPTION_YES; example_hton->create= example_create_handler; example_hton->flags= HTON_CAN_RECREATE; @@ -252,20 +234,6 @@ static int example_init_func(void *p) } -static int example_done_func(void *p) -{ - int error= 0; - DBUG_ENTER("example_done_func"); - - if (example_open_tables.records) - error= 1; - my_hash_free(&example_open_tables); - mysql_mutex_destroy(&example_mutex); - - DBUG_RETURN(error); -} - - /** @brief Example of simple lock controls. The "share" it creates is a @@ -274,71 +242,24 @@ static int example_done_func(void *p) they are needed to function. */ -static EXAMPLE_SHARE *get_share(const char *table_name, TABLE *table) +Example_share *ha_example::get_share() { - EXAMPLE_SHARE *share; - uint length; - char *tmp_name; + Example_share *tmp_share; - mysql_mutex_lock(&example_mutex); - length=(uint) strlen(table_name); + DBUG_ENTER("ha_example::get_share()"); - if (!(share=(EXAMPLE_SHARE*) my_hash_search(&example_open_tables, - (uchar*) table_name, - length))) + lock_shared_ha_data(); + if (!(tmp_share= static_cast<Example_share*>(get_ha_share_ptr()))) { - if (!(share=(EXAMPLE_SHARE *) - my_multi_malloc(MYF(MY_WME | MY_ZEROFILL), - &share, sizeof(*share), - &tmp_name, length+1, - NullS))) - { - mysql_mutex_unlock(&example_mutex); - return NULL; - } - - share->use_count=0; - share->table_name_length=length; - share->table_name=tmp_name; - strmov(share->table_name,table_name); - if (my_hash_insert(&example_open_tables, (uchar*) share)) - goto error; - thr_lock_init(&share->lock); - mysql_mutex_init(ex_key_mutex_EXAMPLE_SHARE_mutex, - &share->mutex, MY_MUTEX_INIT_FAST); - } - share->use_count++; - mysql_mutex_unlock(&example_mutex); - - return share; + tmp_share= new Example_share; + if (!tmp_share) + goto err; -error: - mysql_mutex_destroy(&share->mutex); - my_free(share); - - return NULL; -} - - -/** - @brief - Free lock controls. We call this whenever we close a table. If the table had - the last reference to the share, then we free memory associated with it. -*/ - -static int free_share(EXAMPLE_SHARE *share) -{ - mysql_mutex_lock(&example_mutex); - if (!--share->use_count) - { - my_hash_delete(&example_open_tables, (uchar*) share); - thr_lock_delete(&share->lock); - mysql_mutex_destroy(&share->mutex); - my_free(share); + set_ha_share_ptr(static_cast<Handler_share*>(tmp_share)); } - mysql_mutex_unlock(&example_mutex); - - return 0; +err: + unlock_shared_ha_data(); + DBUG_RETURN(tmp_share); } static handler* example_create_handler(handlerton *hton, @@ -400,7 +321,7 @@ int ha_example::open(const char *name, int mode, uint test_if_locked) { DBUG_ENTER("ha_example::open"); - if (!(share = get_share(name, table))) + if (!(share = get_share())) DBUG_RETURN(1); thr_lock_data_init(&share->lock,&lock,NULL); @@ -420,8 +341,7 @@ int ha_example::open(const char *name, int mode, uint test_if_locked) /** @brief - Closes a table. We call the free_share() function to free any resources - that we have allocated in the "shared" structure. + Closes a table. @details Called from sql_base.cc, sql_select.cc, and table.cc. In sql_select.cc it is @@ -437,7 +357,7 @@ int ha_example::open(const char *name, int mode, uint test_if_locked) int ha_example::close(void) { DBUG_ENTER("ha_example::close"); - DBUG_RETURN(free_share(share)); + DBUG_RETURN(0); } @@ -979,76 +899,6 @@ int ha_example::create(const char *name, TABLE *table_arg, } -/** - check_if_incompatible_data() called if ALTER TABLE can't detect otherwise - if new and old definition are compatible - - @details If there are no other explicit signs like changed number of - fields this function will be called by compare_tables() - (sql/sql_tables.cc) to decide should we rewrite whole table or only .frm - file. - -*/ - -bool ha_example::check_if_incompatible_data(HA_CREATE_INFO *info, - uint table_changes) -{ - ha_table_option_struct *param_old, *param_new; - DBUG_ENTER("ha_example::check_if_incompatible_data"); - /* - This example shows how custom engine specific table and field - options can be accessed from this function to be compared. - */ - param_new= info->option_struct; - DBUG_PRINT("info", ("new strparam: '%-.64s' ullparam: %llu enumparam: %u " - "boolparam: %u", - (param_new->strparam ? param_new->strparam : "<NULL>"), - param_new->ullparam, param_new->enumparam, - param_new->boolparam)); - - param_old= table->s->option_struct; - DBUG_PRINT("info", ("old strparam: '%-.64s' ullparam: %llu enumparam: %u " - "boolparam: %u", - (param_old->strparam ? param_old->strparam : "<NULL>"), - param_old->ullparam, param_old->enumparam, - param_old->boolparam)); - - /* - check important parameters: - for this example engine, we'll assume that changing ullparam or - boolparam requires a table to be rebuilt, while changing strparam - or enumparam - does not. - */ - if (param_new->ullparam != param_old->ullparam || - param_new->boolparam != param_old->boolparam) - DBUG_RETURN(COMPATIBLE_DATA_NO); - -#ifndef DBUG_OFF - for (uint i= 0; i < table->s->fields; i++) - { - ha_field_option_struct *f_old, *f_new; - f_old= table->s->field[i]->option_struct; - DBUG_ASSERT(f_old); - DBUG_PRINT("info", ("old field: %u old complex: '%-.64s'", i, - (f_old->complex_param_to_parse_it_in_engine ? - f_old->complex_param_to_parse_it_in_engine : - "<NULL>"))); - if (info->fields_option_struct[i]) - { - f_new= info->fields_option_struct[i]; - DBUG_PRINT("info", ("old field: %u new complex: '%-.64s'", i, - (f_new->complex_param_to_parse_it_in_engine ? - f_new->complex_param_to_parse_it_in_engine : - "<NULL>"))); - } - else - DBUG_PRINT("info", ("old field %i did not changed", i)); - } -#endif - - DBUG_RETURN(COMPATIBLE_DATA_YES); -} - struct st_mysql_storage_engine example_storage_engine= { MYSQL_HANDLERTON_INTERFACE_VERSION }; @@ -1126,7 +976,7 @@ mysql_declare_plugin(example) "Example storage engine", PLUGIN_LICENSE_GPL, example_init_func, /* Plugin Init */ - example_done_func, /* Plugin Deinit */ + NULL, /* Plugin Deinit */ 0x0001 /* 0.1 */, func_status, /* status variables */ example_system_variables, /* system variables */ @@ -1143,7 +993,7 @@ maria_declare_plugin(example) "Example storage engine", PLUGIN_LICENSE_GPL, example_init_func, /* Plugin Init */ - example_done_func, /* Plugin Deinit */ + NULL, /* Plugin Deinit */ 0x0001, /* version number (0.1) */ func_status, /* status variables */ example_system_variables, /* system variables */ diff --git a/storage/example/ha_example.h b/storage/example/ha_example.h index 9be370edfe3..361e4b3a848 100644 --- a/storage/example/ha_example.h +++ b/storage/example/ha_example.h @@ -42,15 +42,20 @@ #include "my_base.h" /* ha_rows */ /** @brief - EXAMPLE_SHARE is a structure that will be shared among all open handlers. + Example_share is a class that will be shared among all open handlers. This example implements the minimum of what you will probably need. */ -typedef struct st_example_share { - char *table_name; - uint table_name_length,use_count; +class Example_share : public Handler_share { +public: mysql_mutex_t mutex; THR_LOCK lock; -} EXAMPLE_SHARE; + Example_share(); + ~Example_share() + { + thr_lock_delete(&lock); + mysql_mutex_destroy(&mutex); + } +}; /** @brief Class definition for the storage engine @@ -58,7 +63,8 @@ typedef struct st_example_share { class ha_example: public handler { THR_LOCK_DATA lock; ///< MySQL lock - EXAMPLE_SHARE *share; ///< Shared lock info + Example_share *share; ///< Shared lock info + Example_share *get_share(); ///< Get the share public: ha_example(handlerton *hton, TABLE_SHARE *table_arg); @@ -246,8 +252,6 @@ public: int delete_table(const char *from); int create(const char *name, TABLE *form, HA_CREATE_INFO *create_info); ///< required - bool check_if_incompatible_data(HA_CREATE_INFO *info, - uint table_changes); THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to, enum thr_lock_type lock_type); ///< required diff --git a/storage/innobase/btr/btr0sea.cc b/storage/innobase/btr/btr0sea.cc index 432fef05dd5..dcb508a7f29 100644 --- a/storage/innobase/btr/btr0sea.cc +++ b/storage/innobase/btr/btr0sea.cc @@ -351,7 +351,7 @@ void btr_search_info_update_hash( /*========================*/ btr_search_t* info, /*!< in/out: search info */ - btr_cur_t* cursor) /*!< in: cursor which was just positioned */ + const btr_cur_t* cursor)/*!< in: cursor which was just positioned */ { dict_index_t* index; ulint n_unique; @@ -620,7 +620,7 @@ void btr_search_info_update_slow( /*========================*/ btr_search_t* info, /*!< in/out: search info */ - btr_cur_t* cursor) /*!< in: cursor which was just positioned */ + btr_cur_t* cursor) /*!< in: cursor which was just positioned */ { buf_block_t* block; ibool build_index; @@ -864,7 +864,7 @@ btr_search_guess_on_hash( { buf_pool_t* buf_pool; buf_block_t* block; - rec_t* rec; + const rec_t* rec; ulint fold; index_id_t index_id; #ifdef notdefined @@ -950,7 +950,7 @@ btr_search_guess_on_hash( ut_ad(page_rec_is_user_rec(rec)); - btr_cur_position(index, rec, block, cursor); + btr_cur_position(index, (rec_t*) rec, block, cursor); /* Check the validity of the guess within the page */ diff --git a/storage/innobase/ha/ha0ha.cc b/storage/innobase/ha/ha0ha.cc index 3ec778f3bec..ae1eb55982a 100644 --- a/storage/innobase/ha/ha0ha.cc +++ b/storage/innobase/ha/ha0ha.cc @@ -173,7 +173,7 @@ ha_insert_for_fold_func( #if defined UNIV_AHI_DEBUG || defined UNIV_DEBUG buf_block_t* block, /*!< in: buffer block containing the data */ #endif /* UNIV_AHI_DEBUG || UNIV_DEBUG */ - rec_t* data) /*!< in: data, must not be NULL */ + const rec_t* data) /*!< in: data, must not be NULL */ { hash_cell_t* cell; ha_node_t* node; @@ -298,11 +298,11 @@ ha_search_and_update_if_found_func( /*===============================*/ hash_table_t* table, /*!< in/out: hash table */ ulint fold, /*!< in: folded value of the searched data */ - rec_t* data, /*!< in: pointer to the data */ + const rec_t* data, /*!< in: pointer to the data */ #if defined UNIV_AHI_DEBUG || defined UNIV_DEBUG buf_block_t* new_block,/*!< in: block containing new_data */ #endif /* UNIV_AHI_DEBUG || UNIV_DEBUG */ - rec_t* new_data)/*!< in: new pointer to the data */ + const rec_t* new_data)/*!< in: new pointer to the data */ { ha_node_t* node; diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 44bbe20c8d3..68d5cb512f6 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -1564,7 +1564,7 @@ innobase_get_cset_width( /* Fix bug#46256: allow tables to be dropped if the collation is not found, but issue a warning. */ - if ((log_warnings) + if ((global_system_variables.log_warnings) && (cset != 0)){ sql_print_warning( @@ -3969,7 +3969,7 @@ innobase_close_connection( "but transaction is active"); } - if (trx_is_started(trx) && log_warnings) { + if (trx_is_started(trx) && global_system_variables.log_warnings) { sql_print_warning( "MySQL is closing a connection that has an active " @@ -16482,7 +16482,7 @@ static struct st_mysql_sys_var* innobase_system_variables[]= { NULL }; -mysql_declare_plugin(innobase) +maria_declare_plugin(innobase) { MYSQL_STORAGE_ENGINE_PLUGIN, &innobase_storage_engine, @@ -16495,8 +16495,8 @@ mysql_declare_plugin(innobase) INNODB_VERSION_SHORT, innodb_status_variables_export,/* status variables */ innobase_system_variables, /* system variables */ - NULL, /* reserved */ - 0, /* flags */ + INNODB_VERSION_STR, /* string version */ + MariaDB_PLUGIN_MATURITY_STABLE /* maturity */ }, i_s_innodb_trx, i_s_innodb_locks, @@ -16528,7 +16528,7 @@ i_s_innodb_sys_foreign_cols, i_s_innodb_sys_tablespaces, i_s_innodb_sys_datafiles -mysql_declare_plugin_end; +maria_declare_plugin_end; /** @brief Initialize the default value of innodb_commit_concurrency. diff --git a/storage/innobase/handler/i_s.cc b/storage/innobase/handler/i_s.cc index 4f84f477b3a..25c0793b445 100644 --- a/storage/innobase/handler/i_s.cc +++ b/storage/innobase/handler/i_s.cc @@ -747,7 +747,7 @@ static struct st_mysql_information_schema i_s_info = MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION }; -UNIV_INTERN struct st_mysql_plugin i_s_innodb_trx = +UNIV_INTERN struct st_maria_plugin i_s_innodb_trx = { /* the plugin type (a MYSQL_XXX_PLUGIN value) */ /* int */ @@ -791,13 +791,9 @@ UNIV_INTERN struct st_mysql_plugin i_s_innodb_trx = /* struct st_mysql_sys_var** */ STRUCT_FLD(system_vars, NULL), - /* reserved for dependency checking */ - /* void* */ - STRUCT_FLD(__reserved1, NULL), - - /* Plugin flags */ - /* unsigned long */ - STRUCT_FLD(flags, 0UL), + /* Maria extension */ + STRUCT_FLD(version_info, INNODB_VERSION_STR), + STRUCT_FLD(maturity, MariaDB_PLUGIN_MATURITY_STABLE), }; /* Fields of the dynamic table INFORMATION_SCHEMA.innodb_locks */ @@ -1010,7 +1006,7 @@ innodb_locks_init( DBUG_RETURN(0); } -UNIV_INTERN struct st_mysql_plugin i_s_innodb_locks = +UNIV_INTERN struct st_maria_plugin i_s_innodb_locks = { /* the plugin type (a MYSQL_XXX_PLUGIN value) */ /* int */ @@ -1054,13 +1050,9 @@ UNIV_INTERN struct st_mysql_plugin i_s_innodb_locks = /* struct st_mysql_sys_var** */ STRUCT_FLD(system_vars, NULL), - /* reserved for dependency checking */ - /* void* */ - STRUCT_FLD(__reserved1, NULL), - - /* Plugin flags */ - /* unsigned long */ - STRUCT_FLD(flags, 0UL), + /* Maria extension */ + STRUCT_FLD(version_info, INNODB_VERSION_STR), + STRUCT_FLD(maturity, MariaDB_PLUGIN_MATURITY_STABLE), }; /* Fields of the dynamic table INFORMATION_SCHEMA.innodb_lock_waits */ @@ -1197,7 +1189,7 @@ innodb_lock_waits_init( DBUG_RETURN(0); } -UNIV_INTERN struct st_mysql_plugin i_s_innodb_lock_waits = +UNIV_INTERN struct st_maria_plugin i_s_innodb_lock_waits = { /* the plugin type (a MYSQL_XXX_PLUGIN value) */ /* int */ @@ -1241,13 +1233,9 @@ UNIV_INTERN struct st_mysql_plugin i_s_innodb_lock_waits = /* struct st_mysql_sys_var** */ STRUCT_FLD(system_vars, NULL), - /* reserved for dependency checking */ - /* void* */ - STRUCT_FLD(__reserved1, NULL), - - /* Plugin flags */ - /* unsigned long */ - STRUCT_FLD(flags, 0UL), + /* Maria extension */ + STRUCT_FLD(version_info, INNODB_VERSION_STR), + STRUCT_FLD(maturity, MariaDB_PLUGIN_MATURITY_STABLE), }; /*******************************************************************//** @@ -1534,7 +1522,7 @@ i_s_cmp_reset_init( DBUG_RETURN(0); } -UNIV_INTERN struct st_mysql_plugin i_s_innodb_cmp = +UNIV_INTERN struct st_maria_plugin i_s_innodb_cmp = { /* the plugin type (a MYSQL_XXX_PLUGIN value) */ /* int */ @@ -1578,16 +1566,12 @@ UNIV_INTERN struct st_mysql_plugin i_s_innodb_cmp = /* struct st_mysql_sys_var** */ STRUCT_FLD(system_vars, NULL), - /* reserved for dependency checking */ - /* void* */ - STRUCT_FLD(__reserved1, NULL), - - /* Plugin flags */ - /* unsigned long */ - STRUCT_FLD(flags, 0UL), + /* Maria extension */ + STRUCT_FLD(version_info, INNODB_VERSION_STR), + STRUCT_FLD(maturity, MariaDB_PLUGIN_MATURITY_STABLE), }; -UNIV_INTERN struct st_mysql_plugin i_s_innodb_cmp_reset = +UNIV_INTERN struct st_maria_plugin i_s_innodb_cmp_reset = { /* the plugin type (a MYSQL_XXX_PLUGIN value) */ /* int */ @@ -1632,13 +1616,9 @@ UNIV_INTERN struct st_mysql_plugin i_s_innodb_cmp_reset = /* struct st_mysql_sys_var** */ STRUCT_FLD(system_vars, NULL), - /* reserved for dependency checking */ - /* void* */ - STRUCT_FLD(__reserved1, NULL), - - /* Plugin flags */ - /* unsigned long */ - STRUCT_FLD(flags, 0UL), + /* Maria extension */ + STRUCT_FLD(version_info, INNODB_VERSION_STR), + STRUCT_FLD(maturity, MariaDB_PLUGIN_MATURITY_STABLE), }; /* Fields of the dynamic tables @@ -1892,7 +1872,7 @@ i_s_cmp_per_index_reset_init( DBUG_RETURN(0); } -UNIV_INTERN struct st_mysql_plugin i_s_innodb_cmp_per_index = +UNIV_INTERN struct st_maria_plugin i_s_innodb_cmp_per_index = { /* the plugin type (a MYSQL_XXX_PLUGIN value) */ /* int */ @@ -1936,16 +1916,12 @@ UNIV_INTERN struct st_mysql_plugin i_s_innodb_cmp_per_index = /* struct st_mysql_sys_var** */ STRUCT_FLD(system_vars, NULL), - /* reserved for dependency checking */ - /* void* */ - STRUCT_FLD(__reserved1, NULL), - - /* Plugin flags */ - /* unsigned long */ - STRUCT_FLD(flags, 0UL), + /* Maria extension */ + STRUCT_FLD(version_info, INNODB_VERSION_STR), + STRUCT_FLD(maturity, MariaDB_PLUGIN_MATURITY_STABLE), }; -UNIV_INTERN struct st_mysql_plugin i_s_innodb_cmp_per_index_reset = +UNIV_INTERN struct st_maria_plugin i_s_innodb_cmp_per_index_reset = { /* the plugin type (a MYSQL_XXX_PLUGIN value) */ /* int */ @@ -1990,13 +1966,9 @@ UNIV_INTERN struct st_mysql_plugin i_s_innodb_cmp_per_index_reset = /* struct st_mysql_sys_var** */ STRUCT_FLD(system_vars, NULL), - /* reserved for dependency checking */ - /* void* */ - STRUCT_FLD(__reserved1, NULL), - - /* Plugin flags */ - /* unsigned long */ - STRUCT_FLD(flags, 0UL), + /* Maria extension */ + STRUCT_FLD(version_info, INNODB_VERSION_STR), + STRUCT_FLD(maturity, MariaDB_PLUGIN_MATURITY_STABLE), }; /* Fields of the dynamic table information_schema.innodb_cmpmem. */ @@ -2191,7 +2163,7 @@ i_s_cmpmem_reset_init( DBUG_RETURN(0); } -UNIV_INTERN struct st_mysql_plugin i_s_innodb_cmpmem = +UNIV_INTERN struct st_maria_plugin i_s_innodb_cmpmem = { /* the plugin type (a MYSQL_XXX_PLUGIN value) */ /* int */ @@ -2235,16 +2207,12 @@ UNIV_INTERN struct st_mysql_plugin i_s_innodb_cmpmem = /* struct st_mysql_sys_var** */ STRUCT_FLD(system_vars, NULL), - /* reserved for dependency checking */ - /* void* */ - STRUCT_FLD(__reserved1, NULL), - - /* Plugin flags */ - /* unsigned long */ - STRUCT_FLD(flags, 0UL), + /* Maria extension */ + STRUCT_FLD(version_info, INNODB_VERSION_STR), + STRUCT_FLD(maturity, MariaDB_PLUGIN_MATURITY_STABLE), }; -UNIV_INTERN struct st_mysql_plugin i_s_innodb_cmpmem_reset = +UNIV_INTERN struct st_maria_plugin i_s_innodb_cmpmem_reset = { /* the plugin type (a MYSQL_XXX_PLUGIN value) */ /* int */ @@ -2289,13 +2257,9 @@ UNIV_INTERN struct st_mysql_plugin i_s_innodb_cmpmem_reset = /* struct st_mysql_sys_var** */ STRUCT_FLD(system_vars, NULL), - /* reserved for dependency checking */ - /* void* */ - STRUCT_FLD(__reserved1, NULL), - - /* Plugin flags */ - /* unsigned long */ - STRUCT_FLD(flags, 0UL), + /* Maria extension */ + STRUCT_FLD(version_info, INNODB_VERSION_STR), + STRUCT_FLD(maturity, MariaDB_PLUGIN_MATURITY_STABLE), }; /* Fields of the dynamic table INFORMATION_SCHEMA.innodb_metrics */ @@ -2773,7 +2737,7 @@ innodb_metrics_init( DBUG_RETURN(0); } -UNIV_INTERN struct st_mysql_plugin i_s_innodb_metrics = +UNIV_INTERN struct st_maria_plugin i_s_innodb_metrics = { /* the plugin type (a MYSQL_XXX_PLUGIN value) */ /* int */ @@ -2817,13 +2781,9 @@ UNIV_INTERN struct st_mysql_plugin i_s_innodb_metrics = /* struct st_mysql_sys_var** */ STRUCT_FLD(system_vars, NULL), - /* reserved for dependency checking */ - /* void* */ - STRUCT_FLD(__reserved1, NULL), - - /* Plugin flags */ - /* unsigned long */ - STRUCT_FLD(flags, 0UL), + /* Maria extension */ + STRUCT_FLD(version_info, INNODB_VERSION_STR), + STRUCT_FLD(maturity, MariaDB_PLUGIN_MATURITY_STABLE), }; /* Fields of the dynamic table INFORMATION_SCHEMA.innodb_ft_default_stopword */ static ST_FIELD_INFO i_s_stopword_fields_info[] = @@ -2890,7 +2850,7 @@ i_s_stopword_init( DBUG_RETURN(0); } -UNIV_INTERN struct st_mysql_plugin i_s_innodb_ft_default_stopword = +UNIV_INTERN struct st_maria_plugin i_s_innodb_ft_default_stopword = { /* the plugin type (a MYSQL_XXX_PLUGIN value) */ /* int */ @@ -2934,13 +2894,9 @@ UNIV_INTERN struct st_mysql_plugin i_s_innodb_ft_default_stopword = /* struct st_mysql_sys_var** */ STRUCT_FLD(system_vars, NULL), - /* reserved for dependency checking */ - /* void* */ - STRUCT_FLD(__reserved1, NULL), - - /* Plugin flags */ - /* unsigned long */ - STRUCT_FLD(flags, 0UL), + /* Maria extension */ + STRUCT_FLD(version_info, INNODB_VERSION_STR), + STRUCT_FLD(maturity, MariaDB_PLUGIN_MATURITY_STABLE), }; /* Fields of the dynamic table INFORMATION_SCHEMA.INNODB_FT_DELETED @@ -3063,7 +3019,7 @@ i_s_fts_deleted_init( DBUG_RETURN(0); } -UNIV_INTERN struct st_mysql_plugin i_s_innodb_ft_deleted = +UNIV_INTERN struct st_maria_plugin i_s_innodb_ft_deleted = { /* the plugin type (a MYSQL_XXX_PLUGIN value) */ /* int */ @@ -3107,13 +3063,9 @@ UNIV_INTERN struct st_mysql_plugin i_s_innodb_ft_deleted = /* struct st_mysql_sys_var** */ STRUCT_FLD(system_vars, NULL), - /* reserved for dependency checking */ - /* void* */ - STRUCT_FLD(__reserved1, NULL), - - /* Plugin flags */ - /* unsigned long */ - STRUCT_FLD(flags, 0UL), + /* Maria extension */ + STRUCT_FLD(version_info, INNODB_VERSION_STR), + STRUCT_FLD(maturity, MariaDB_PLUGIN_MATURITY_STABLE), }; /*******************************************************************//** @@ -3150,7 +3102,7 @@ i_s_fts_being_deleted_init( DBUG_RETURN(0); } -UNIV_INTERN struct st_mysql_plugin i_s_innodb_ft_being_deleted = +UNIV_INTERN struct st_maria_plugin i_s_innodb_ft_being_deleted = { /* the plugin type (a MYSQL_XXX_PLUGIN value) */ /* int */ @@ -3194,13 +3146,9 @@ UNIV_INTERN struct st_mysql_plugin i_s_innodb_ft_being_deleted = /* struct st_mysql_sys_var** */ STRUCT_FLD(system_vars, NULL), - /* reserved for dependency checking */ - /* void* */ - STRUCT_FLD(__reserved1, NULL), - - /* Plugin flags */ - /* unsigned long */ - STRUCT_FLD(flags, 0UL), + /* Maria extension */ + STRUCT_FLD(version_info, INNODB_VERSION_STR), + STRUCT_FLD(maturity, MariaDB_PLUGIN_MATURITY_STABLE), }; /*******************************************************************//** @@ -3287,7 +3235,7 @@ i_s_fts_inserted_init( DBUG_RETURN(0); } -UNIV_INTERN struct st_mysql_plugin i_s_innodb_ft_inserted = +UNIV_INTERN struct st_maria_plugin i_s_innodb_ft_inserted = { /* the plugin type (a MYSQL_XXX_PLUGIN value) */ /* int */ @@ -3331,13 +3279,9 @@ UNIV_INTERN struct st_mysql_plugin i_s_innodb_ft_inserted = /* struct st_mysql_sys_var** */ STRUCT_FLD(system_vars, NULL), - /* reserved for dependency checking */ - /* void* */ - STRUCT_FLD(__reserved1, NULL), - - /* Plugin flags */ - /* unsigned long */ - STRUCT_FLD(flags, 0UL), + /* Maria extension */ + STRUCT_FLD(version_info, INNODB_VERSION_STR), + STRUCT_FLD(maturity, MariaDB_PLUGIN_MATURITY_STABLE), }; /* Fields of the dynamic table INFORMATION_SCHEMA.INNODB_FT_INDEX_CACHED and @@ -3554,7 +3498,7 @@ i_s_fts_index_cache_init( DBUG_RETURN(0); } -UNIV_INTERN struct st_mysql_plugin i_s_innodb_ft_index_cache = +UNIV_INTERN struct st_maria_plugin i_s_innodb_ft_index_cache = { /* the plugin type (a MYSQL_XXX_PLUGIN value) */ /* int */ @@ -3598,13 +3542,9 @@ UNIV_INTERN struct st_mysql_plugin i_s_innodb_ft_index_cache = /* struct st_mysql_sys_var** */ STRUCT_FLD(system_vars, NULL), - /* reserved for dependency checking */ - /* void* */ - STRUCT_FLD(__reserved1, NULL), - - /* Plugin flags */ - /* unsigned long */ - STRUCT_FLD(flags, 0UL), + /* Maria extension */ + STRUCT_FLD(version_info, INNODB_VERSION_STR), + STRUCT_FLD(maturity, MariaDB_PLUGIN_MATURITY_STABLE), }; /*******************************************************************//** @@ -3859,7 +3799,7 @@ i_s_fts_index_table_init( DBUG_RETURN(0); } -UNIV_INTERN struct st_mysql_plugin i_s_innodb_ft_index_table = +UNIV_INTERN struct st_maria_plugin i_s_innodb_ft_index_table = { /* the plugin type (a MYSQL_XXX_PLUGIN value) */ /* int */ @@ -3903,13 +3843,9 @@ UNIV_INTERN struct st_mysql_plugin i_s_innodb_ft_index_table = /* struct st_mysql_sys_var** */ STRUCT_FLD(system_vars, NULL), - /* reserved for dependency checking */ - /* void* */ - STRUCT_FLD(__reserved1, NULL), - - /* Plugin flags */ - /* unsigned long */ - STRUCT_FLD(flags, 0UL), + /* Maria extension */ + STRUCT_FLD(version_info, INNODB_VERSION_STR), + STRUCT_FLD(maturity, MariaDB_PLUGIN_MATURITY_STABLE), }; /* Fields of the dynamic table INFORMATION_SCHEMA.INNODB_FT_CONFIG */ @@ -4063,7 +3999,7 @@ i_s_fts_config_init( DBUG_RETURN(0); } -UNIV_INTERN struct st_mysql_plugin i_s_innodb_ft_config = +UNIV_INTERN struct st_maria_plugin i_s_innodb_ft_config = { /* the plugin type (a MYSQL_XXX_PLUGIN value) */ /* int */ @@ -4107,13 +4043,9 @@ UNIV_INTERN struct st_mysql_plugin i_s_innodb_ft_config = /* struct st_mysql_sys_var** */ STRUCT_FLD(system_vars, NULL), - /* reserved for dependency checking */ - /* void* */ - STRUCT_FLD(__reserved1, NULL), - - /* Plugin flags */ - /* unsigned long */ - STRUCT_FLD(flags, 0UL), + /* Maria extension */ + STRUCT_FLD(version_info, INNODB_VERSION_STR), + STRUCT_FLD(maturity, MariaDB_PLUGIN_MATURITY_STABLE), }; /* Fields of the dynamic table INNODB_BUFFER_POOL_STATS. */ @@ -4586,7 +4518,7 @@ i_s_innodb_buffer_pool_stats_init( DBUG_RETURN(0); } -UNIV_INTERN struct st_mysql_plugin i_s_innodb_buffer_stats = +UNIV_INTERN struct st_maria_plugin i_s_innodb_buffer_stats = { /* the plugin type (a MYSQL_XXX_PLUGIN value) */ /* int */ @@ -4630,13 +4562,9 @@ UNIV_INTERN struct st_mysql_plugin i_s_innodb_buffer_stats = /* struct st_mysql_sys_var** */ STRUCT_FLD(system_vars, NULL), - /* reserved for dependency checking */ - /* void* */ - STRUCT_FLD(__reserved1, NULL), - - /* Plugin flags */ - /* unsigned long */ - STRUCT_FLD(flags, 0UL), + /* Maria extension */ + STRUCT_FLD(version_info, INNODB_VERSION_STR), + STRUCT_FLD(maturity, MariaDB_PLUGIN_MATURITY_STABLE), }; /* Fields of the dynamic table INNODB_BUFFER_POOL_PAGE. */ @@ -5280,7 +5208,7 @@ i_s_innodb_buffer_page_init( DBUG_RETURN(0); } -UNIV_INTERN struct st_mysql_plugin i_s_innodb_buffer_page = +UNIV_INTERN struct st_maria_plugin i_s_innodb_buffer_page = { /* the plugin type (a MYSQL_XXX_PLUGIN value) */ /* int */ @@ -5324,13 +5252,9 @@ UNIV_INTERN struct st_mysql_plugin i_s_innodb_buffer_page = /* struct st_mysql_sys_var** */ STRUCT_FLD(system_vars, NULL), - /* reserved for dependency checking */ - /* void* */ - STRUCT_FLD(__reserved1, NULL), - - /* Plugin flags */ - /* unsigned long */ - STRUCT_FLD(flags, 0UL), + /* Maria extension */ + STRUCT_FLD(version_info, INNODB_VERSION_STR), + STRUCT_FLD(maturity, MariaDB_PLUGIN_MATURITY_STABLE), }; static ST_FIELD_INFO i_s_innodb_buf_page_lru_fields_info[] = @@ -5826,7 +5750,7 @@ i_s_innodb_buffer_page_lru_init( DBUG_RETURN(0); } -UNIV_INTERN struct st_mysql_plugin i_s_innodb_buffer_page_lru = +UNIV_INTERN struct st_maria_plugin i_s_innodb_buffer_page_lru = { /* the plugin type (a MYSQL_XXX_PLUGIN value) */ /* int */ @@ -5870,13 +5794,9 @@ UNIV_INTERN struct st_mysql_plugin i_s_innodb_buffer_page_lru = /* struct st_mysql_sys_var** */ STRUCT_FLD(system_vars, NULL), - /* reserved for dependency checking */ - /* void* */ - STRUCT_FLD(__reserved1, NULL), - - /* Plugin flags */ - /* unsigned long */ - STRUCT_FLD(flags, 0UL), + /* Maria extension */ + STRUCT_FLD(version_info, INNODB_VERSION_STR), + STRUCT_FLD(maturity, MariaDB_PLUGIN_MATURITY_STABLE), }; /*******************************************************************//** @@ -6122,7 +6042,7 @@ innodb_sys_tables_init( DBUG_RETURN(0); } -UNIV_INTERN struct st_mysql_plugin i_s_innodb_sys_tables = +UNIV_INTERN struct st_maria_plugin i_s_innodb_sys_tables = { /* the plugin type (a MYSQL_XXX_PLUGIN value) */ /* int */ @@ -6166,13 +6086,9 @@ UNIV_INTERN struct st_mysql_plugin i_s_innodb_sys_tables = /* struct st_mysql_sys_var** */ STRUCT_FLD(system_vars, NULL), - /* reserved for dependency checking */ - /* void* */ - STRUCT_FLD(__reserved1, NULL), - - /* Plugin flags */ - /* unsigned long */ - STRUCT_FLD(flags, 0UL), + /* Maria extension */ + STRUCT_FLD(version_info, INNODB_VERSION_STR), + STRUCT_FLD(maturity, MariaDB_PLUGIN_MATURITY_STABLE), }; /** SYS_TABLESTATS ***********************************************/ @@ -6415,7 +6331,7 @@ innodb_sys_tablestats_init( DBUG_RETURN(0); } -UNIV_INTERN struct st_mysql_plugin i_s_innodb_sys_tablestats = +UNIV_INTERN struct st_maria_plugin i_s_innodb_sys_tablestats = { /* the plugin type (a MYSQL_XXX_PLUGIN value) */ /* int */ @@ -6459,13 +6375,9 @@ UNIV_INTERN struct st_mysql_plugin i_s_innodb_sys_tablestats = /* struct st_mysql_sys_var** */ STRUCT_FLD(system_vars, NULL), - /* reserved for dependency checking */ - /* void* */ - STRUCT_FLD(__reserved1, NULL), - - /* Plugin flags */ - /* unsigned long */ - STRUCT_FLD(flags, 0UL), + /* Maria extension */ + STRUCT_FLD(version_info, INNODB_VERSION_STR), + STRUCT_FLD(maturity, MariaDB_PLUGIN_MATURITY_STABLE), }; /** SYS_INDEXES **************************************************/ @@ -6670,7 +6582,7 @@ innodb_sys_indexes_init( DBUG_RETURN(0); } -UNIV_INTERN struct st_mysql_plugin i_s_innodb_sys_indexes = +UNIV_INTERN struct st_maria_plugin i_s_innodb_sys_indexes = { /* the plugin type (a MYSQL_XXX_PLUGIN value) */ /* int */ @@ -6714,13 +6626,9 @@ UNIV_INTERN struct st_mysql_plugin i_s_innodb_sys_indexes = /* struct st_mysql_sys_var** */ STRUCT_FLD(system_vars, NULL), - /* reserved for dependency checking */ - /* void* */ - STRUCT_FLD(__reserved1, NULL), - - /* Plugin flags */ - /* unsigned long */ - STRUCT_FLD(flags, 0UL), + /* Maria extension */ + STRUCT_FLD(version_info, INNODB_VERSION_STR), + STRUCT_FLD(maturity, MariaDB_PLUGIN_MATURITY_STABLE), }; /** SYS_COLUMNS **************************************************/ @@ -6910,7 +6818,7 @@ innodb_sys_columns_init( DBUG_RETURN(0); } -UNIV_INTERN struct st_mysql_plugin i_s_innodb_sys_columns = +UNIV_INTERN struct st_maria_plugin i_s_innodb_sys_columns = { /* the plugin type (a MYSQL_XXX_PLUGIN value) */ /* int */ @@ -6954,13 +6862,9 @@ UNIV_INTERN struct st_mysql_plugin i_s_innodb_sys_columns = /* struct st_mysql_sys_var** */ STRUCT_FLD(system_vars, NULL), - /* reserved for dependency checking */ - /* void* */ - STRUCT_FLD(__reserved1, NULL), - - /* Plugin flags */ - /* unsigned long */ - STRUCT_FLD(flags, 0UL), + /* Maria extension */ + STRUCT_FLD(version_info, INNODB_VERSION_STR), + STRUCT_FLD(maturity, MariaDB_PLUGIN_MATURITY_STABLE), }; /** SYS_FIELDS ***************************************************/ @@ -7123,7 +7027,7 @@ innodb_sys_fields_init( DBUG_RETURN(0); } -UNIV_INTERN struct st_mysql_plugin i_s_innodb_sys_fields = +UNIV_INTERN struct st_maria_plugin i_s_innodb_sys_fields = { /* the plugin type (a MYSQL_XXX_PLUGIN value) */ /* int */ @@ -7167,13 +7071,9 @@ UNIV_INTERN struct st_mysql_plugin i_s_innodb_sys_fields = /* struct st_mysql_sys_var** */ STRUCT_FLD(system_vars, NULL), - /* reserved for dependency checking */ - /* void* */ - STRUCT_FLD(__reserved1, NULL), - - /* Plugin flags */ - /* unsigned long */ - STRUCT_FLD(flags, 0UL), + /* Maria extension */ + STRUCT_FLD(version_info, INNODB_VERSION_STR), + STRUCT_FLD(maturity, MariaDB_PLUGIN_MATURITY_STABLE), }; /** SYS_FOREIGN ********************************************/ @@ -7351,7 +7251,7 @@ innodb_sys_foreign_init( DBUG_RETURN(0); } -UNIV_INTERN struct st_mysql_plugin i_s_innodb_sys_foreign = +UNIV_INTERN struct st_maria_plugin i_s_innodb_sys_foreign = { /* the plugin type (a MYSQL_XXX_PLUGIN value) */ /* int */ @@ -7395,13 +7295,9 @@ UNIV_INTERN struct st_mysql_plugin i_s_innodb_sys_foreign = /* struct st_mysql_sys_var** */ STRUCT_FLD(system_vars, NULL), - /* reserved for dependency checking */ - /* void* */ - STRUCT_FLD(__reserved1, NULL), - - /* Plugin flags */ - /* unsigned long */ - STRUCT_FLD(flags, 0UL), + /* Maria extension */ + STRUCT_FLD(version_info, INNODB_VERSION_STR), + STRUCT_FLD(maturity, MariaDB_PLUGIN_MATURITY_STABLE), }; /** SYS_FOREIGN_COLS ********************************************/ @@ -7571,7 +7467,7 @@ innodb_sys_foreign_cols_init( DBUG_RETURN(0); } -UNIV_INTERN struct st_mysql_plugin i_s_innodb_sys_foreign_cols = +UNIV_INTERN struct st_maria_plugin i_s_innodb_sys_foreign_cols = { /* the plugin type (a MYSQL_XXX_PLUGIN value) */ /* int */ @@ -7615,13 +7511,9 @@ UNIV_INTERN struct st_mysql_plugin i_s_innodb_sys_foreign_cols = /* struct st_mysql_sys_var** */ STRUCT_FLD(system_vars, NULL), - /* reserved for dependency checking */ - /* void* */ - STRUCT_FLD(__reserved1, NULL), - - /* Plugin flags */ - /* unsigned long */ - STRUCT_FLD(flags, 0UL), + /* Maria extension */ + STRUCT_FLD(version_info, INNODB_VERSION_STR), + STRUCT_FLD(maturity, MariaDB_PLUGIN_MATURITY_STABLE), }; /** SYS_TABLESPACES ********************************************/ @@ -7838,7 +7730,7 @@ innodb_sys_tablespaces_init( DBUG_RETURN(0); } -UNIV_INTERN struct st_mysql_plugin i_s_innodb_sys_tablespaces = +UNIV_INTERN struct st_maria_plugin i_s_innodb_sys_tablespaces = { /* the plugin type (a MYSQL_XXX_PLUGIN value) */ /* int */ @@ -7882,13 +7774,9 @@ UNIV_INTERN struct st_mysql_plugin i_s_innodb_sys_tablespaces = /* struct st_mysql_sys_var** */ STRUCT_FLD(system_vars, NULL), - /* reserved for dependency checking */ - /* void* */ - STRUCT_FLD(__reserved1, NULL), - - /* Plugin flags */ - /* unsigned long */ - STRUCT_FLD(flags, 0UL), + /* Maria extension */ + STRUCT_FLD(version_info, INNODB_VERSION_STR), + STRUCT_FLD(maturity, MariaDB_PLUGIN_MATURITY_STABLE), }; /** SYS_DATAFILES ************************************************/ @@ -8030,7 +7918,7 @@ innodb_sys_datafiles_init( DBUG_RETURN(0); } -UNIV_INTERN struct st_mysql_plugin i_s_innodb_sys_datafiles = +UNIV_INTERN struct st_maria_plugin i_s_innodb_sys_datafiles = { /* the plugin type (a MYSQL_XXX_PLUGIN value) */ /* int */ @@ -8074,11 +7962,7 @@ UNIV_INTERN struct st_mysql_plugin i_s_innodb_sys_datafiles = /* struct st_mysql_sys_var** */ STRUCT_FLD(system_vars, NULL), - /* reserved for dependency checking */ - /* void* */ - STRUCT_FLD(__reserved1, NULL), - - /* Plugin flags */ - /* unsigned long */ - STRUCT_FLD(flags, 0UL), + /* Maria extension */ + STRUCT_FLD(version_info, INNODB_VERSION_STR), + STRUCT_FLD(maturity, MariaDB_PLUGIN_MATURITY_STABLE), }; diff --git a/storage/innobase/handler/i_s.h b/storage/innobase/handler/i_s.h index 9e3e651706a..05f6fd8ecd2 100644 --- a/storage/innobase/handler/i_s.h +++ b/storage/innobase/handler/i_s.h @@ -28,34 +28,34 @@ Created July 18, 2007 Vasil Dimov const char plugin_author[] = "Oracle Corporation"; -extern struct st_mysql_plugin i_s_innodb_trx; -extern struct st_mysql_plugin i_s_innodb_locks; -extern struct st_mysql_plugin i_s_innodb_lock_waits; -extern struct st_mysql_plugin i_s_innodb_cmp; -extern struct st_mysql_plugin i_s_innodb_cmp_reset; -extern struct st_mysql_plugin i_s_innodb_cmp_per_index; -extern struct st_mysql_plugin i_s_innodb_cmp_per_index_reset; -extern struct st_mysql_plugin i_s_innodb_cmpmem; -extern struct st_mysql_plugin i_s_innodb_cmpmem_reset; -extern struct st_mysql_plugin i_s_innodb_metrics; -extern struct st_mysql_plugin i_s_innodb_ft_default_stopword; -extern struct st_mysql_plugin i_s_innodb_ft_inserted; -extern struct st_mysql_plugin i_s_innodb_ft_deleted; -extern struct st_mysql_plugin i_s_innodb_ft_being_deleted; -extern struct st_mysql_plugin i_s_innodb_ft_index_cache; -extern struct st_mysql_plugin i_s_innodb_ft_index_table; -extern struct st_mysql_plugin i_s_innodb_ft_config; -extern struct st_mysql_plugin i_s_innodb_buffer_page; -extern struct st_mysql_plugin i_s_innodb_buffer_page_lru; -extern struct st_mysql_plugin i_s_innodb_buffer_stats; -extern struct st_mysql_plugin i_s_innodb_sys_tables; -extern struct st_mysql_plugin i_s_innodb_sys_tablestats; -extern struct st_mysql_plugin i_s_innodb_sys_indexes; -extern struct st_mysql_plugin i_s_innodb_sys_columns; -extern struct st_mysql_plugin i_s_innodb_sys_fields; -extern struct st_mysql_plugin i_s_innodb_sys_foreign; -extern struct st_mysql_plugin i_s_innodb_sys_foreign_cols; -extern struct st_mysql_plugin i_s_innodb_sys_tablespaces; -extern struct st_mysql_plugin i_s_innodb_sys_datafiles; +extern struct st_maria_plugin i_s_innodb_trx; +extern struct st_maria_plugin i_s_innodb_locks; +extern struct st_maria_plugin i_s_innodb_lock_waits; +extern struct st_maria_plugin i_s_innodb_cmp; +extern struct st_maria_plugin i_s_innodb_cmp_reset; +extern struct st_maria_plugin i_s_innodb_cmp_per_index; +extern struct st_maria_plugin i_s_innodb_cmp_per_index_reset; +extern struct st_maria_plugin i_s_innodb_cmpmem; +extern struct st_maria_plugin i_s_innodb_cmpmem_reset; +extern struct st_maria_plugin i_s_innodb_metrics; +extern struct st_maria_plugin i_s_innodb_ft_default_stopword; +extern struct st_maria_plugin i_s_innodb_ft_inserted; +extern struct st_maria_plugin i_s_innodb_ft_deleted; +extern struct st_maria_plugin i_s_innodb_ft_being_deleted; +extern struct st_maria_plugin i_s_innodb_ft_index_cache; +extern struct st_maria_plugin i_s_innodb_ft_index_table; +extern struct st_maria_plugin i_s_innodb_ft_config; +extern struct st_maria_plugin i_s_innodb_buffer_page; +extern struct st_maria_plugin i_s_innodb_buffer_page_lru; +extern struct st_maria_plugin i_s_innodb_buffer_stats; +extern struct st_maria_plugin i_s_innodb_sys_tables; +extern struct st_maria_plugin i_s_innodb_sys_tablestats; +extern struct st_maria_plugin i_s_innodb_sys_indexes; +extern struct st_maria_plugin i_s_innodb_sys_columns; +extern struct st_maria_plugin i_s_innodb_sys_fields; +extern struct st_maria_plugin i_s_innodb_sys_foreign; +extern struct st_maria_plugin i_s_innodb_sys_foreign_cols; +extern struct st_maria_plugin i_s_innodb_sys_tablespaces; +extern struct st_maria_plugin i_s_innodb_sys_datafiles; #endif /* i_s_h */ diff --git a/storage/innobase/include/ha0ha.h b/storage/innobase/include/ha0ha.h index 2e4397ea5fc..07ab20ab995 100644 --- a/storage/innobase/include/ha0ha.h +++ b/storage/innobase/include/ha0ha.h @@ -38,7 +38,7 @@ Looks for an element in a hash table. @return pointer to the data of the first hash table node in chain having the fold number, NULL if not found */ UNIV_INLINE -rec_t* +const rec_t* ha_search_and_get_data( /*===================*/ hash_table_t* table, /*!< in: hash table */ @@ -53,11 +53,11 @@ ha_search_and_update_if_found_func( /*===============================*/ hash_table_t* table, /*!< in/out: hash table */ ulint fold, /*!< in: folded value of the searched data */ - rec_t* data, /*!< in: pointer to the data */ + const rec_t* data, /*!< in: pointer to the data */ #if defined UNIV_AHI_DEBUG || defined UNIV_DEBUG buf_block_t* new_block,/*!< in: block containing new_data */ #endif /* UNIV_AHI_DEBUG || UNIV_DEBUG */ - rec_t* new_data);/*!< in: new pointer to the data */ + const rec_t* new_data);/*!< in: new pointer to the data */ #if defined UNIV_AHI_DEBUG || defined UNIV_DEBUG /** Looks for an element when we know the pointer to the data and @@ -226,7 +226,7 @@ struct ha_node_t { #if defined UNIV_AHI_DEBUG || defined UNIV_DEBUG buf_block_t* block; /*!< buffer block containing the data, or NULL */ #endif /* UNIV_AHI_DEBUG || UNIV_DEBUG */ - rec_t* data; /*!< pointer to the data */ + const rec_t* data; /*!< pointer to the data */ ulint fold; /*!< fold value for the data */ }; diff --git a/storage/innobase/include/ha0ha.ic b/storage/innobase/include/ha0ha.ic index 91794e8f1fc..c478ff54303 100644 --- a/storage/innobase/include/ha0ha.ic +++ b/storage/innobase/include/ha0ha.ic @@ -58,7 +58,7 @@ ha_node_set_data_func( #if defined UNIV_AHI_DEBUG || defined UNIV_DEBUG buf_block_t* block, /*!< in: buffer block containing the data */ #endif /* UNIV_AHI_DEBUG || UNIV_DEBUG */ - rec_t* data) /*!< in: pointer to the data */ + const rec_t* data) /*!< in: pointer to the data */ { #if defined UNIV_AHI_DEBUG || defined UNIV_DEBUG node->block = block; @@ -161,7 +161,7 @@ Looks for an element in a hash table. @return pointer to the data of the first hash table node in chain having the fold number, NULL if not found */ UNIV_INLINE -rec_t* +const rec_t* ha_search_and_get_data( /*===================*/ hash_table_t* table, /*!< in: hash table */ diff --git a/storage/maria/ha_maria.cc b/storage/maria/ha_maria.cc index fb44307fa44..b17c1dd7907 100644 --- a/storage/maria/ha_maria.cc +++ b/storage/maria/ha_maria.cc @@ -1189,7 +1189,7 @@ int ha_maria::open(const char *name, int mode, uint test_if_locked) { if (my_errno == HA_ERR_OLD_FILE) { - push_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + push_warning(current_thd, Sql_condition::WARN_LEVEL_NOTE, ER_CRASHED_ON_USAGE, zerofill_error_msg); } @@ -2193,8 +2193,8 @@ bool ha_maria::check_and_repair(THD *thd) STATE_MOVED) { /* Remove error about crashed table */ - thd->warning_info->clear_warning_info(thd->query_id); - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + thd->get_stmt_da()->clear_warning_info(thd->query_id); + push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_NOTE, ER_CRASHED_ON_USAGE, "Zerofilling moved table %s", table->s->path.str); sql_print_information("Zerofilling moved table: '%s'", @@ -2728,7 +2728,7 @@ int ha_maria::external_lock(THD *thd, int lock_type) This is a bit excessive, ACID requires this only if there are some changes to commit (rollback shouldn't be tested). */ - DBUG_ASSERT(!thd->stmt_da->is_sent || + DBUG_ASSERT(!thd->get_stmt_da()->is_sent() || thd->killed == KILL_CONNECTION); /* autocommit ? rollback a transaction */ #ifdef MARIA_CANNOT_ROLLBACK @@ -3022,7 +3022,7 @@ int ha_maria::create(const char *name, register TABLE *table_arg, ha_create_info->row_type != ROW_TYPE_PAGE && ha_create_info->row_type != ROW_TYPE_NOT_USED && ha_create_info->row_type != ROW_TYPE_DEFAULT) - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + push_warning(thd, Sql_condition::WARN_LEVEL_NOTE, ER_ILLEGAL_HA_CREATE_OPTION, "Row format set to PAGE because of TRANSACTIONAL=1 option"); diff --git a/storage/maria/ma_ft_boolean_search.c b/storage/maria/ma_ft_boolean_search.c index eb5813f84f1..e69c90c671c 100644 --- a/storage/maria/ma_ft_boolean_search.c +++ b/storage/maria/ma_ft_boolean_search.c @@ -338,7 +338,7 @@ static int _ftb_no_dupes_cmp(void* not_used __attribute__((unused)), /* returns 1 if the search was finished (must-word wasn't found) */ -static int _ft2_search(FTB *ftb, FTB_WORD *ftbw, my_bool init_search) +static int _ft2_search_no_lock(FTB *ftb, FTB_WORD *ftbw, my_bool init_search) { int r; int subkeys=1; @@ -439,7 +439,7 @@ static int _ft2_search(FTB *ftb, FTB_WORD *ftbw, my_bool init_search) ftbw->key_root=info->s->state.key_root[ftb->keynr]; ftbw->keyinfo=info->s->keyinfo+ftb->keynr; ftbw->off=0; - return _ft2_search(ftb, ftbw, 0); + return _ft2_search_no_lock(ftb, ftbw, 0); } /* matching key found */ @@ -469,6 +469,19 @@ static int _ft2_search(FTB *ftb, FTB_WORD *ftbw, my_bool init_search) return 0; } +static int _ft2_search(FTB *ftb, FTB_WORD *ftbw, my_bool init_search) +{ + int r; + MARIA_SHARE *share= ftb->info->s; + if (share->lock_key_trees) + mysql_rwlock_rdlock(&share->keyinfo[ftb->keynr].root_lock); + r= _ft2_search_no_lock(ftb, ftbw, init_search); + if (share->lock_key_trees) + mysql_rwlock_unlock(&share->keyinfo[ftb->keynr].root_lock); + return r; +} + + static void _ftb_init_index_search(FT_INFO *ftb) { int i; diff --git a/storage/myisam/ha_myisam.cc b/storage/myisam/ha_myisam.cc index 3e73bb7c801..9d53372d467 100644 --- a/storage/myisam/ha_myisam.cc +++ b/storage/myisam/ha_myisam.cc @@ -1849,9 +1849,12 @@ int ha_myisam::info(uint flag) share->db_options_in_use= misam_info.options; stats.block_size= myisam_block_size; /* record block size */ - /* Update share */ - if (share->tmp_table == NO_TMP_TABLE) - mysql_mutex_lock(&share->LOCK_ha_data); + /* + Update share. + lock_shared_ha_data is slighly abused here, since there is no other + way of locking the TABLE_SHARE. + */ + lock_shared_ha_data(); share->keys_in_use.set_prefix(share->keys); share->keys_in_use.intersect_extended(misam_info.key_map); share->keys_for_keyread.intersect(share->keys_in_use); @@ -1860,8 +1863,7 @@ int ha_myisam::info(uint flag) memcpy((char*) table->key_info[0].rec_per_key, (char*) misam_info.rec_per_key, sizeof(table->key_info[0].rec_per_key[0])*share->key_parts); - if (share->tmp_table == NO_TMP_TABLE) - mysql_mutex_unlock(&share->LOCK_ha_data); + unlock_shared_ha_data(); /* Set data_file_name and index_file_name to point at the symlink value diff --git a/storage/perfschema/pfs_digest.cc b/storage/perfschema/pfs_digest.cc index c5df64d9243..b2cbd02bd3f 100644 --- a/storage/perfschema/pfs_digest.cc +++ b/storage/perfschema/pfs_digest.cc @@ -30,6 +30,7 @@ #include "table_helper.h" #include "my_md5.h" #include "sql_lex.h" +#include "sql_get_diagnostics.h" #include "sql_string.h" #include <string.h> diff --git a/storage/perfschema/pfs_instr.cc b/storage/perfschema/pfs_instr.cc index 25e78ee7b5e..68ec8b149a3 100644 --- a/storage/perfschema/pfs_instr.cc +++ b/storage/perfschema/pfs_instr.cc @@ -1230,7 +1230,7 @@ search: if (! create) { /* No lost counter, just looking for the file existence. */ - return NULL; + DBUG_RETURN(NULL); } while (++attempts <= file_max) @@ -1280,7 +1280,7 @@ search: /* OOM in lf_hash_insert */ file_lost++; - return NULL; + DBUG_RETURN(NULL); } } } diff --git a/storage/perfschema/unittest/CMakeLists.txt b/storage/perfschema/unittest/CMakeLists.txt index c3a7fe5c72f..08cc21c7483 100644 --- a/storage/perfschema/unittest/CMakeLists.txt +++ b/storage/perfschema/unittest/CMakeLists.txt @@ -25,29 +25,3 @@ ADD_DEFINITIONS(-DMYSQL_SERVER ${SSL_DEFINES}) MY_ADD_TESTS(pfs_instr_class pfs_instr_class-oom pfs_instr pfs_instr-oom pfs_account-oom pfs_host-oom pfs_user-oom pfs EXT "cc" LINK_LIBRARIES perfschema mysys) - -IF(WIN32) - SET(MYSQLD_EXTRA_SOURCES ${CMAKE_SOURCE_DIR}/sql/nt_servc.cc) -ENDIF() - -# We need the server libs to test the blob parser. -# Add sql_builtin.cc here, to force linkage of plugins below. -# Also add mysys/string.c (see Bug#45488) -ADD_EXECUTABLE(pfs_connect_attr-t - pfs_connect_attr-t.cc - ${CMAKE_BINARY_DIR}/sql/sql_builtin.cc - ${CMAKE_SOURCE_DIR}/mysys/string.c - ${MYSQLD_EXTRA_SOURCES} -) -ADD_DEPENDENCIES(pfs_connect_attr-t GenServerSource) -TARGET_LINK_LIBRARIES(pfs_connect_attr-t mytap perfschema) -# We need to explicitly link in everything referenced in sql/sql_builtin.cc -TARGET_LINK_LIBRARIES(pfs_connect_attr-t ${MYSQLD_STATIC_PLUGIN_LIBS}) -TARGET_LINK_LIBRARIES(pfs_connect_attr-t sql binlog rpl master slave sql) -TARGET_LINK_LIBRARIES(pfs_connect_attr-t mysys mysys_ssl) -TARGET_LINK_LIBRARIES(pfs_connect_attr-t vio ${SSL_LIBRARIES}) -TARGET_LINK_LIBRARIES(pfs_connect_attr-t strings dbug regex mysys zlib) -ADD_TEST(pfs_connect_attr pfs_connect_attr-t) - -# On windows, pfs_connect_attr-t may depend on openssl dlls. -COPY_OPENSSL_DLLS(copy_openssl_pfs_unittest) diff --git a/tests/mysql_client_fw.c b/tests/mysql_client_fw.c index 207eaead135..e4490660d0f 100644 --- a/tests/mysql_client_fw.c +++ b/tests/mysql_client_fw.c @@ -582,7 +582,7 @@ static int my_process_stmt_result(MYSQL_STMT *stmt) return row_count; } - field_count= min(mysql_num_fields(result), MAX_RES_FIELDS); + field_count= MY_MIN(mysql_num_fields(result), MAX_RES_FIELDS); bzero((char*) buffer, sizeof(buffer)); bzero((char*) length, sizeof(length)); |