diff options
author | Sachin Setiya <sachin.setiya@mariadb.com> | 2017-03-17 02:05:20 +0530 |
---|---|---|
committer | Sachin Setiya <sachin.setiya@mariadb.com> | 2017-03-17 02:05:20 +0530 |
commit | f66395f7c06f357e7ace74317e6563a1e5dbc3ae (patch) | |
tree | d1669b069aa722c9c73abe52e5924b7bbba41b39 | |
parent | c401773c8dd97bd9f6bf6cff9fcafdf24fbd0ee1 (diff) | |
parent | c4f3e64c23fe7f7fd18c0a79f87f9771df15fe9f (diff) | |
download | mariadb-git-f66395f7c06f357e7ace74317e6563a1e5dbc3ae.tar.gz |
Merge tag 'mariadb-10.0.30' into bb-sachin-10.0-galera-merge
Signed-off-by: Sachin Setiya <sachin.setiya@mariadb.com>
487 files changed, 14212 insertions, 7437 deletions
diff --git a/.gitignore b/.gitignore index 191dbe23fad..f1c300a89c6 100644 --- a/.gitignore +++ b/.gitignore @@ -214,7 +214,7 @@ support-files/mysql.spec support-files/mysqld_multi.server support-files/wsrep.cnf support-files/wsrep_notify -support-files/SELinux/centos6-mariadb.pp +support-files/SELinux/mariadb.pp tags tests/async_queries tests/bug25714 diff --git a/CMakeLists.txt b/CMakeLists.txt index 9d71539f5cd..59f0833bf4e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -356,6 +356,9 @@ SET(CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE ON) # Common defines and includes ADD_DEFINITIONS(-DHAVE_CONFIG_H) +IF(_FILE_OFFSET_BITS) + ADD_DEFINITIONS(-D_FILE_OFFSET_BITS=${_FILE_OFFSET_BITS}) +ENDIF() INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}/include) # Add bundled or system zlib. @@ -3,16 +3,18 @@ organization registered in the USA. The current main sponsors of the MariaDB Foundation are: -Booking.com http://www.booking.com (2013 - 2016) -Development Bank of Singapore http://dbs.com (2016) -MariaDB Corporation https://www.mariadb.com (2013 - 2016) -Visma http://visma.com (2015 - 2016) -Acronis http://acronis.com (2016) -Nexedi https://www.nexedi.com (2016) -Automattic https://automattic.com (2014 - 2016) -Tencent Game DBA http://tencentdba.com/about (2016) -Verkkokauppa.com https://www.verkkokauppa.com (2015 - 2016) -Virtuozzo https://virtuozzo.com (2016) +Alibaba Cloud https://intl.aliyun.com (2017) +Booking.com https://www.booking.com (2013 - 2017) +Development Bank of Singapore https://dbs.com (2016 - 2017) +MariaDB Corporation https://www.mariadb.com (2013 - 2017) +Visma https://visma.com (2015 - 2017) +Acronis http://acronis.com (2016 - 2017) +Nexedi https://www.nexedi.com (2016 - 2017) +Automattic https://automattic.com (2014 - 2017) +Tencent Game DBA http://tencentdba.com/about (2016 - 2017) +Tencent TDSQL http://tdsql.org/ (2016 - 2017) +Verkkokauppa.com https://www.verkkokauppa.com (2015 - 2017) +Virtuozzo https://virtuozzo.com (2016 - 2017) For a full list of sponsors, see https://mariadb.org/about/supporters/ @@ -1,3 +1,3 @@ MYSQL_VERSION_MAJOR=10 MYSQL_VERSION_MINOR=0 -MYSQL_VERSION_PATCH=29 +MYSQL_VERSION_PATCH=30 diff --git a/client/mysqladmin.cc b/client/mysqladmin.cc index 7f32658f1b2..78a73c01218 100644 --- a/client/mysqladmin.cc +++ b/client/mysqladmin.cc @@ -38,8 +38,8 @@ char ex_var_names[MAX_MYSQL_VAR][FN_REFLEN]; ulonglong last_values[MAX_MYSQL_VAR]; static int interval=0; static my_bool option_force=0,interrupted=0,new_line=0, - opt_compress=0, opt_relative=0, opt_verbose=0, opt_vertical=0, - tty_password= 0, opt_nobeep; + opt_compress= 0, opt_local= 0, opt_relative= 0, opt_verbose= 0, + opt_vertical= 0, tty_password= 0, opt_nobeep; static my_bool debug_info_flag= 0, debug_check_flag= 0; static uint tcp_port = 0, option_wait = 0, option_silent=0, nr_iterations; static uint opt_count_iterations= 0, my_end_arg; @@ -102,9 +102,12 @@ enum commands { ADMIN_PING, ADMIN_EXTENDED_STATUS, ADMIN_FLUSH_STATUS, ADMIN_FLUSH_PRIVILEGES, ADMIN_START_SLAVE, ADMIN_STOP_SLAVE, ADMIN_START_ALL_SLAVES, ADMIN_STOP_ALL_SLAVES, - ADMIN_FLUSH_THREADS, ADMIN_OLD_PASSWORD, ADMIN_FLUSH_SLOW_LOG, + ADMIN_FLUSH_THREADS, ADMIN_OLD_PASSWORD, ADMIN_FLUSH_BINARY_LOG, + ADMIN_FLUSH_ENGINE_LOG, ADMIN_FLUSH_ERROR_LOG, ADMIN_FLUSH_GENERAL_LOG, + ADMIN_FLUSH_RELAY_LOG, ADMIN_FLUSH_SLOW_LOG, ADMIN_FLUSH_TABLE_STATISTICS, ADMIN_FLUSH_INDEX_STATISTICS, ADMIN_FLUSH_USER_STATISTICS, ADMIN_FLUSH_CLIENT_STATISTICS, + ADMIN_FLUSH_USER_RESOURCES, ADMIN_FLUSH_ALL_STATUS, ADMIN_FLUSH_ALL_STATISTICS }; static const char *command_names[]= { @@ -116,9 +119,10 @@ static const char *command_names[]= { "ping", "extended-status", "flush-status", "flush-privileges", "start-slave", "stop-slave", "start-all-slaves", "stop-all-slaves", - "flush-threads", "old-password", "flush-slow-log", + "flush-threads", "old-password", "flush-binary-log", "flush-engine-log", + "flush-error-log", "flush-general-log", "flush-relay-log", "flush-slow-log", "flush-table-statistics", "flush-index-statistics", - "flush-user-statistics", "flush-client-statistics", + "flush-user-statistics", "flush-client-statistics", "flush-user-resources", "flush-all-status", "flush-all-statistics", NullS }; @@ -160,6 +164,9 @@ static struct my_option my_long_options[] = NO_ARG, 0, 0, 0, 0, 0, 0}, {"host", 'h', "Connect to host.", &host, &host, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"local", 'l', "Local command, don't write to binlog.", + &opt_local, &opt_local, 0, GET_BOOL, NO_ARG, 0, 0, 0, + 0, 0, 0}, {"no-beep", 'b', "Turn off beep on error.", &opt_nobeep, &opt_nobeep, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"password", 'p', @@ -617,6 +624,18 @@ static int execute_commands(MYSQL *mysql,int argc, char **argv) */ struct my_rnd_struct rand_st; + char buff[FN_REFLEN + 20]; + + if (opt_local) + { + sprintf(buff, "set local sql_log_bin=0"); + if (mysql_query(mysql, buff)) + { + my_printf_error(0, "SET LOCAL SQL_LOG_BIN=0 failed; error: '%-.200s'", + error_flags, mysql_error(mysql)); + return -1; + } + } for (; argc > 0 ; argv++,argc--) { @@ -624,7 +643,6 @@ static int execute_commands(MYSQL *mysql,int argc, char **argv) switch ((command= find_type(argv[0],&command_typelib,FIND_TYPE_BASIC))) { case ADMIN_CREATE: { - char buff[FN_REFLEN+20]; if (argc < 2) { my_printf_error(0, "Too few arguments to create", error_flags); @@ -902,6 +920,56 @@ static int execute_commands(MYSQL *mysql,int argc, char **argv) } break; } + case ADMIN_FLUSH_BINARY_LOG: + { + if (mysql_query(mysql, "flush binary logs")) + { + my_printf_error(0, "flush failed; error: '%s'", error_flags, + mysql_error(mysql)); + return -1; + } + break; + } + case ADMIN_FLUSH_ENGINE_LOG: + { + if (mysql_query(mysql,"flush engine logs")) + { + my_printf_error(0, "flush failed; error: '%s'", error_flags, + mysql_error(mysql)); + return -1; + } + break; + } + case ADMIN_FLUSH_ERROR_LOG: + { + if (mysql_query(mysql, "flush error logs")) + { + my_printf_error(0, "flush failed; error: '%s'", error_flags, + mysql_error(mysql)); + return -1; + } + break; + } + case ADMIN_FLUSH_GENERAL_LOG: + { + if (mysql_query(mysql, "flush general logs")) + { + my_printf_error(0, "flush failed; error: '%s'", error_flags, + mysql_error(mysql)); + return -1; + } + break; + } + case ADMIN_FLUSH_RELAY_LOG: + { + if (mysql_query(mysql, "flush relay logs")) + { + my_printf_error(0, "flush failed; error: '%s'", error_flags, + mysql_error(mysql)); + return -1; + } + break; + } case ADMIN_FLUSH_SLOW_LOG: { if (mysql_query(mysql,"flush slow logs")) @@ -972,6 +1040,16 @@ static int execute_commands(MYSQL *mysql,int argc, char **argv) } break; } + case ADMIN_FLUSH_USER_RESOURCES: + { + if (mysql_query(mysql, "flush user_resources")) + { + my_printf_error(0, "flush failed; error: '%s'", error_flags, + mysql_error(mysql)); + return -1; + } + break; + } case ADMIN_FLUSH_CLIENT_STATISTICS: { if (mysql_query(mysql,"flush client_statistics")) @@ -1295,12 +1373,18 @@ static void usage(void) flush-index-statistics Flush index statistics\n\ flush-logs Flush all logs\n\ flush-privileges Reload grant tables (same as reload)\n\ + flush-binary-log Flush binary log\n\ + flush-engine-log Flush engine log(s)\n\ + flush-error-log Flush error log\n\ + flush-general-log Flush general log\n\ + flush-relay-log Flush relay log\n\ flush-slow-log Flush slow query log\n\ - flush-status Clear status variables\n\ + flush-status Clear status variables\n\ flush-table-statistics Clear table statistics\n\ flush-tables Flush all tables\n\ flush-threads Flush the thread cache\n\ flush-user-statistics Flush user statistics\n\ + flush-user-resources Flush user resources\n\ kill id,id,... Kill mysql threads"); #if MYSQL_VERSION_ID >= 32200 puts("\ diff --git a/client/mysqlbinlog.cc b/client/mysqlbinlog.cc index 4be31421f9b..29355d013c7 100644 --- a/client/mysqlbinlog.cc +++ b/client/mysqlbinlog.cc @@ -106,7 +106,7 @@ static const char* sock= 0; static char *opt_plugindir= 0, *opt_default_auth= 0; #ifdef HAVE_SMEM -static char *shared_memory_base_name= 0; +static const char *shared_memory_base_name= 0; #endif static char* user = 0; static char* pass = 0; diff --git a/client/mysqldump.c b/client/mysqldump.c index 64ed21ac7fc..27aeea5a7b3 100644 --- a/client/mysqldump.c +++ b/client/mysqldump.c @@ -698,8 +698,9 @@ static void write_header(FILE *sql_file, char *db_name) "-- MySQL dump %s Distrib %s, for %s (%s)\n--\n", DUMP_VERSION, MYSQL_SERVER_VERSION, SYSTEM_TYPE, MACHINE_TYPE); - print_comment(sql_file, 0, "-- Host: %s Database: %s\n", - fix_for_comment(current_host ? current_host : "localhost"), + print_comment(sql_file, 0, "-- Host: %s ", + fix_for_comment(current_host ? current_host : "localhost")); + print_comment(sql_file, 0, "Database: %s\n", fix_for_comment(db_name ? db_name : "")); print_comment(sql_file, 0, "-- ------------------------------------------------------\n" diff --git a/client/mysqltest.cc b/client/mysqltest.cc index 15db5759ac9..ecc8c07605e 100644 --- a/client/mysqltest.cc +++ b/client/mysqltest.cc @@ -3341,6 +3341,8 @@ void do_exec(struct st_command *command) DBUG_ENTER("do_exec"); DBUG_PRINT("enter", ("cmd: '%s'", cmd)); + var_set_int("$sys_errno",0); + /* Skip leading space */ while (*cmd && my_isspace(charset_info, *cmd)) cmd++; @@ -3457,6 +3459,7 @@ void do_exec(struct st_command *command) report_or_die("command \"%s\" failed with wrong error: %d", command->first_argument, status); } + var_set_int("$sys_errno",status); } else if (command->expected_errors.err[0].type == ERR_ERRNO && command->expected_errors.err[0].code.errnum != 0) diff --git a/config.h.cmake b/config.h.cmake index 65dd2e2d384..79f85ebc12e 100644 --- a/config.h.cmake +++ b/config.h.cmake @@ -485,7 +485,6 @@ #cmakedefine _LARGE_FILES 1 #cmakedefine _LARGEFILE_SOURCE 1 #cmakedefine _LARGEFILE64_SOURCE 1 -#cmakedefine _FILE_OFFSET_BITS @_FILE_OFFSET_BITS@ #cmakedefine TIME_WITH_SYS_TIME 1 diff --git a/debian/mariadb-galera-server-10.0.mysql-server.logrotate b/debian/mariadb-galera-server-10.0.mysql-server.logrotate index a19e9ec46a2..dbe2a7e7dfe 100644 --- a/debian/mariadb-galera-server-10.0.mysql-server.logrotate +++ b/debian/mariadb-galera-server-10.0.mysql-server.logrotate @@ -14,7 +14,8 @@ if [ -f `my_print_defaults --mysqld | grep -oP "pid-file=\K[^$]+"` ]; then # If this fails, check debian.conf! - mysqladmin --defaults-file=/etc/mysql/debian.cnf flush-logs + mysqladmin --defaults-file=/etc/mysql/debian.cnf --local flush-error-log \ + flush-engine-log flush-general-log flush-slow-log fi endscript } diff --git a/extra/yassl/src/ssl.cpp b/extra/yassl/src/ssl.cpp index 7069140dcda..982d462e3b8 100644 --- a/extra/yassl/src/ssl.cpp +++ b/extra/yassl/src/ssl.cpp @@ -774,7 +774,6 @@ int SSL_CTX_load_verify_locations(SSL_CTX* ctx, const char* file, const char* path) { int ret = SSL_FAILURE; - const int HALF_PATH = 128; if (file) ret = read_file(ctx, file, SSL_FILETYPE_PEM, CA); diff --git a/include/my_global.h b/include/my_global.h index 6222467901e..535051d6d8b 100644 --- a/include/my_global.h +++ b/include/my_global.h @@ -1084,10 +1084,9 @@ typedef ulong myf; /* Type of MyFlags in my_funcs */ static inline char *dlerror(void) { static char win_errormsg[2048]; - if(FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, - 0, GetLastError(), 0, win_errormsg, 2048, NULL)) - return win_errormsg; - return ""; + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, + 0, GetLastError(), 0, win_errormsg, 2048, NULL); + return win_errormsg; } #define HAVE_DLOPEN 1 #define HAVE_DLERROR 1 diff --git a/include/my_sys.h b/include/my_sys.h index 5e1f6baac77..4e129cc6dc1 100644 --- a/include/my_sys.h +++ b/include/my_sys.h @@ -64,9 +64,9 @@ typedef struct my_aio_result { #define MY_FAE 8 /* Fatal if any error */ #define MY_WME 16 /* Write message on error */ #define MY_WAIT_IF_FULL 32 /* Wait and try again if disk full error */ -#define MY_IGNORE_BADFD 32 /* my_sync: ignore 'bad descriptor' errors */ -#define MY_UNUSED 64 /* Unused (was support for RAID) */ -#define MY_FULL_IO 512 /* For my_read - loop intil I/O is complete */ +#define MY_IGNORE_BADFD 32 /* my_sync(): ignore 'bad descriptor' errors */ +#define MY_NOSYMLINKS 512 /* my_open(): don't follow symlinks */ +#define MY_FULL_IO 512 /* my_read(): loop intil I/O is complete */ #define MY_DONT_CHECK_FILESIZE 128 /* Option to init_io_cache() */ #define MY_LINK_WARNING 32 /* my_redel() gives warning if links */ #define MY_COPYTIME 64 /* my_redel() copys time */ @@ -253,7 +253,7 @@ extern ulong my_file_opened,my_stream_opened, my_tmp_file_created; extern ulong my_file_total_opened; extern ulong my_sync_count; extern uint mysys_usage_id; -extern my_bool my_init_done; +extern my_bool my_init_done, my_thr_key_mysys_exists; extern my_bool my_assert_on_error; extern myf my_global_flags; /* Set to MY_WME for more error messages */ /* Point to current my_message() */ @@ -567,6 +567,7 @@ my_off_t my_b_safe_tell(IO_CACHE* info); /* picks the correct tell() */ typedef uint32 ha_checksum; extern ulong my_crc_dbug_check; +extern int (*mysys_test_invalid_symlink)(const char *filename); #include <my_alloc.h> /* Prototypes for mysys and my_func functions */ @@ -594,9 +595,11 @@ extern int my_realpath(char *to, const char *filename, myf MyFlags); extern File my_create_with_symlink(const char *linkname, const char *filename, int createflags, int access_flags, myf MyFlags); -extern int my_delete_with_symlink(const char *name, myf MyFlags); extern int my_rename_with_symlink(const char *from,const char *to,myf MyFlags); extern int my_symlink(const char *content, const char *linkname, myf MyFlags); +extern int my_handler_delete_with_symlink(PSI_file_key key, const char *name, + const char *ext, myf sync_dir); + extern size_t my_read(File Filedes,uchar *Buffer,size_t Count,myf MyFlags); extern size_t my_pread(File Filedes,uchar *Buffer,size_t Count,my_off_t offset, myf MyFlags); diff --git a/include/mysql/psi/mysql_file.h b/include/mysql/psi/mysql_file.h index c839b2b019b..aca66bd4974 100644 --- a/include/mysql/psi/mysql_file.h +++ b/include/mysql/psi/mysql_file.h @@ -442,20 +442,6 @@ #endif /** - @def mysql_file_delete_with_symlink(K, P1, P2) - Instrumented delete with symbolic link. - @c mysql_file_delete_with_symlink is a replacement - for @c my_delete_with_symlink. -*/ -#ifdef HAVE_PSI_FILE_INTERFACE - #define mysql_file_delete_with_symlink(K, P1, P2) \ - inline_mysql_file_delete_with_symlink(K, __FILE__, __LINE__, P1, P2) -#else - #define mysql_file_delete_with_symlink(K, P1, P2) \ - inline_mysql_file_delete_with_symlink(P1, P2) -#endif - -/** @def mysql_file_rename_with_symlink(K, P1, P2, P3) Instrumented rename with symbolic link. @c mysql_file_rename_with_symlink is a replacement @@ -1337,31 +1323,6 @@ inline_mysql_file_create_with_symlink( return file; } -static inline int -inline_mysql_file_delete_with_symlink( -#ifdef HAVE_PSI_FILE_INTERFACE - PSI_file_key key, const char *src_file, uint src_line, -#endif - const char *name, myf flags) -{ - int result; -#ifdef HAVE_PSI_FILE_INTERFACE - struct PSI_file_locker *locker; - PSI_file_locker_state state; - locker= PSI_FILE_CALL(get_thread_file_name_locker) - (&state, key, PSI_FILE_DELETE, name, &locker); - if (likely(locker != NULL)) - { - PSI_FILE_CALL(start_file_close_wait)(locker, src_file, src_line); - result= my_delete_with_symlink(name, flags); - PSI_FILE_CALL(end_file_close_wait)(locker, result); - return result; - } -#endif - - result= my_delete_with_symlink(name, flags); - return result; -} static inline int inline_mysql_file_rename_with_symlink( diff --git a/include/mysql/psi/psi.h b/include/mysql/psi/psi.h index 7fcff89c8b6..3f43445e08a 100644 --- a/include/mysql/psi/psi.h +++ b/include/mysql/psi/psi.h @@ -417,7 +417,7 @@ enum PSI_file_operation PSI_FILE_FSTAT= 12, /** File chsize, as in @c my_chsize(). */ PSI_FILE_CHSIZE= 13, - /** File delete, such as @c my_delete() or @c my_delete_with_symlink(). */ + /** File delete, such as @c my_delete() or @c my_handler_delete_with_symlink(). */ PSI_FILE_DELETE= 14, /** File rename, such as @c my_rename() or @c my_rename_with_symlink(). */ PSI_FILE_RENAME= 15, diff --git a/man/mysqladmin.1 b/man/mysqladmin.1 index e2b1753ff9f..65f348cab0c 100644 --- a/man/mysqladmin.1 +++ b/man/mysqladmin.1 @@ -816,6 +816,18 @@ Connect to the MariaDB server on the given host\&. .sp -1 .IP \(bu 2.3 .\} +.\" mysqladmin: local option +.\" local option: mysqladmin +\fB\-\-local\fR, +\fB\-l\fR +.sp +Suppress the SQL command(s) from being written to the binary log by enabled sql_log_bin=0 for the session\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} .\" mysqladmin: no-beep option .\" no-beep option: mysqladmin \fB\-\-no\-beep\fR, diff --git a/mysql-test/include/gap_lock_error_all.inc b/mysql-test/include/gap_lock_error_all.inc new file mode 100644 index 00000000000..fc69dfb56f0 --- /dev/null +++ b/mysql-test/include/gap_lock_error_all.inc @@ -0,0 +1,27 @@ +--source include/have_partition.inc +--source include/gap_lock_error_init.inc + +let $select_lock=for update; +let $autocommit = 0; +--source include/gap_lock_error_select.inc +let $autocommit = 1; +--source include/gap_lock_error_select.inc + +let $select_lock=lock in share mode; +let $autocommit = 0; +--source include/gap_lock_error_select.inc +let $autocommit = 1; +--source include/gap_lock_error_select.inc + +let $select_lock=; +let $autocommit = 0; +--source include/gap_lock_error_select.inc +let $autocommit = 1; +--source include/gap_lock_error_select.inc + +let $autocommit = 0; +--source include/gap_lock_error_update.inc +let $autocommit = 1; +--source include/gap_lock_error_update.inc + +--source include/gap_lock_error_cleanup.inc diff --git a/mysql-test/include/gap_lock_error_cleanup.inc b/mysql-test/include/gap_lock_error_cleanup.inc new file mode 100644 index 00000000000..8558b5a528f --- /dev/null +++ b/mysql-test/include/gap_lock_error_cleanup.inc @@ -0,0 +1 @@ +drop table gap1, gap2, gap3, gap4; diff --git a/mysql-test/include/gap_lock_error_init.inc b/mysql-test/include/gap_lock_error_init.inc new file mode 100644 index 00000000000..26aa852a398 --- /dev/null +++ b/mysql-test/include/gap_lock_error_init.inc @@ -0,0 +1,24 @@ +eval CREATE TABLE gap1 (id1 INT, id2 INT, id3 INT, c1 INT, value INT, + PRIMARY KEY (id1, id2, id3), + INDEX i (c1)) ENGINE=$engine; +CREATE TABLE gap2 like gap1; +eval CREATE TABLE gap3 (id INT, value INT, + PRIMARY KEY (id), + UNIQUE KEY ui(value)) ENGINE=$engine; +eval CREATE TABLE gap4 (id INT, value INT, + PRIMARY KEY (id)) ENGINE=$engine + PARTITION BY HASH(id) PARTITIONS 2; +--disable_query_log +let $max = 1000; +let $i = 1; +while ($i <= $max) { + eval INSERT INTO gap1 (id1, id2, id3, c1, value) + VALUES ($i div 2, $i div 10, $i, $i, $i); + eval INSERT INTO gap2 (id1, id2, id3, c1, value) + VALUES ($i div 2, $i div 10, $i, $i, $i); + inc $i; +} +--enable_query_log + +insert into gap3 values (1,1), (2,2),(3,3),(4,4),(5,5); +insert into gap4 values (1,1), (2,2),(3,3),(4,4),(5,5); diff --git a/mysql-test/include/gap_lock_error_select.inc b/mysql-test/include/gap_lock_error_select.inc new file mode 100644 index 00000000000..91db9bed68a --- /dev/null +++ b/mysql-test/include/gap_lock_error_select.inc @@ -0,0 +1,89 @@ +eval set session autocommit=$autocommit; +let $is_gaplock_target = `SELECT @@autocommit = 0 && '$select_lock' != '' && '$expect_gap_lock_errors' = 1`; + +if ($is_gaplock_target) +{ +# rnd_init +--error ER_UNKNOWN_ERROR +eval select * from gap1 limit 1 $select_lock; +--error ER_UNKNOWN_ERROR +eval select * from gap1 where value != 100 limit 1 $select_lock; +# index_read_map +--error ER_UNKNOWN_ERROR +eval select * from gap1 where id1=1 $select_lock; +--error ER_UNKNOWN_ERROR +eval select * from gap1 where id1=1 and id2= 1 $select_lock; +# read_range_first +--error ER_UNKNOWN_ERROR +eval select * from gap1 where id1=1 and id2= 1 and id3 != 1 $select_lock; +--error ER_UNKNOWN_ERROR +eval select * from gap1 where id1=1 and id2= 1 and id3 + between 1 and 3 $select_lock; +--error ER_UNKNOWN_ERROR +eval select * from gap1 where id1=1 and id2= 1 order by id3 asc + limit 1 $select_lock; +--error ER_UNKNOWN_ERROR +eval select * from gap1 where id1=1 and id2= 1 order by id3 desc + limit 1 $select_lock; +# index_first +--error ER_UNKNOWN_ERROR +eval select * from gap1 order by id1 asc limit 1 $select_lock; +--error ER_UNKNOWN_ERROR +eval select * from gap1 order by id1 asc, id2 asc, id3 asc limit 1 $select_lock; +# index_last +--error ER_UNKNOWN_ERROR +eval select * from gap1 order by id1 desc limit 1 $select_lock; +--error ER_UNKNOWN_ERROR +eval select * from gap1 order by id1 desc, id2 desc, id3 desc + limit 1 $select_lock; +# secondary index lookup +--error ER_UNKNOWN_ERROR +eval select * from gap1 force index(i) where c1=1 $select_lock; +# unique index lookup, ensure no gap lock errors as this is effectively a +# single point select that does not lock ranges or gaps of keys +eval select * from gap3 force index(ui) where value=1 $select_lock; +# primary key lookup, ensure no gap lock errors as these are effectively +# single point selects that do not lock ranges or gaps of keys +eval select * from gap1 where id1=1 and id2=1 and id3=1 $select_lock; +eval select * from gap1 where id1=1 and id2=1 and id3 in (1, 2, 3) $select_lock; +eval select * from gap1 where id1=1 and id2=1 and id3=1 and value=1 + order by c1 $select_lock; +eval select * from gap3 where id=1 $select_lock; +eval select * from gap4 where id=1 $select_lock; +eval select * from gap4 where id in (1, 2, 3) $select_lock; +--error ER_UNKNOWN_ERROR +eval select * from gap4 $select_lock; +--error ER_UNKNOWN_ERROR +eval select * from gap4 where id between 3 and 7 $select_lock; +} + +if (!$is_gaplock_target) +{ +eval select * from gap1 limit 1 $select_lock; +eval select * from gap1 where value != 100 limit 1 $select_lock; +eval select * from gap1 where id1=1 $select_lock; +eval select * from gap1 where id1=1 and id2= 1 $select_lock; +eval select * from gap1 where id1=1 and id2= 1 and id3 != 1 $select_lock; +eval select * from gap1 where id1=1 and id2= 1 and id3 + between 1 and 3 $select_lock; +eval select * from gap1 where id1=1 and id2= 1 order by id3 asc + limit 1 $select_lock; +eval select * from gap1 where id1=1 and id2= 1 order by id3 desc + limit 1 $select_lock; +eval select * from gap1 order by id1 asc limit 1 $select_lock; +eval select * from gap1 order by id1 asc, id2 asc, id3 asc limit 1 $select_lock; +eval select * from gap1 order by id1 desc limit 1 $select_lock; +eval select * from gap1 order by id1 desc, id2 desc, id3 desc + limit 1 $select_lock; +eval select * from gap1 force index(i) where c1=1 $select_lock; +eval select * from gap3 force index(ui) where value=1 $select_lock; +eval select * from gap1 where id1=1 and id2=1 and id3=1 $select_lock; +eval select * from gap1 where id1=1 and id2=1 and id3 in (1, 2, 3) $select_lock; +eval select * from gap1 where id1=1 and id2=1 and id3=1 and value=1 + order by c1 $select_lock; +eval select * from gap3 where id=1 $select_lock; +eval select * from gap4 where id=1 $select_lock; +eval select * from gap4 where id in (1, 2, 3) $select_lock; +eval select * from gap4 $select_lock; +eval select * from gap4 where id between 3 and 7 $select_lock; +} diff --git a/mysql-test/include/gap_lock_error_update.inc b/mysql-test/include/gap_lock_error_update.inc new file mode 100644 index 00000000000..d456cf81588 --- /dev/null +++ b/mysql-test/include/gap_lock_error_update.inc @@ -0,0 +1,91 @@ +eval set session autocommit=$autocommit; +let $is_gaplock_target = `SELECT @@autocommit = 0 && '$expect_gap_lock_errors' = 1`; + +if ($is_gaplock_target) +{ +## single-table insert,update,delete +insert into gap1 (id1, id2, id3) values (-1,-1,-1); +insert into gap1 (id1, id2, id3) values (-1,-1,-1) + on duplicate key update value=100; +--error ER_UNKNOWN_ERROR +update gap1 set value=100 where id1=1; +update gap1 set value=100 where id1=1 and id2=1 and id3=1; +--error ER_UNKNOWN_ERROR +delete from gap1 where id1=2; +delete from gap1 where id1=-1 and id2=-1 and id3=-1; +commit; + +## multi-table statements (preventing all gap locks with autocommit) +# insert into select +--error ER_UNKNOWN_ERROR +insert into gap2 select * from gap1; +--error ER_UNKNOWN_ERROR +insert into gap2 select * from gap1 where id1=1; +insert into gap2 select * from gap1 where id1=1 and id2=1 and id3=1; + +# create table select +create table t4 select * from gap1 where id1=1 and id2=1 and id3=1; +drop table t4; +--error ER_UNKNOWN_ERROR +create table t4 select * from gap1; +--error ER_UNKNOWN_ERROR +create table t4 select * from gap1 where id1=1; + +# update join +update gap1 join gap2 on gap1.id1 and gap1.id2=gap2.id2 set gap1.value=100 where gap2.id1=3 + and gap2.id2=3 and gap2.id3=3; +--error ER_UNKNOWN_ERROR +update gap1 join gap2 on gap1.id1 and gap1.id2=gap2.id2 set gap1.value=100 where gap2.id1=3; +--error ER_UNKNOWN_ERROR +update gap1 join gap2 on gap1.id1 and gap1.id2=gap2.id2 join gap3 on gap1.id1=gap3.id + set gap1.value=100 where gap2.id1=3; +--error ER_UNKNOWN_ERROR +update gap1 set gap1.value= (select count(*) from gap2); + +# delete join +delete gap1 from gap1 join gap2 on gap1.id1 and gap1.id2=gap2.id2 where gap2.id1=3 + and gap2.id2=3 and gap2.id3=3; +--error ER_UNKNOWN_ERROR +delete gap1 from gap1 join gap2 on gap1.id1 and gap1.id2=gap2.id2 where gap2.id1=3; + +# select join / self join +--error ER_UNKNOWN_ERROR +select * from gap1, gap2 limit 1 for update; +--error ER_UNKNOWN_ERROR +select * from gap1 a, gap1 b limit 1 for update; + +# unique secondary key +create table u1( + c1 int, + c2 int, + c3 int, + c4 int, + primary key (c1, c2, c3), + unique key (c3, c1) +); +begin; +insert into u1 values (1,1,1,1); +commit; +begin; +insert into u1 values (1,2,1,1) on duplicate key update c4=10; +commit; +begin; +select * from u1 where c3=1 and c1 = 1 for update; +--error ER_UNKNOWN_ERROR +select * from u1 where c3=1 for update; +commit; +drop table u1; +} + +if (!$is_gaplock_target) +{ +# autocommit doesn't prevent single table operations +insert into gap1 (id1, id2, id3) values (-1,-1,-1); +insert into gap1 (id1, id2, id3) values (-1,-1,-1) + on duplicate key update value=100; +update gap1 set value=100 where id1=1; +update gap1 set value=100 where id1=1 and id2=1 and id3=1; +delete from gap1 where id1=2; +delete from gap1 where id1=-1 and id2=-1 and id3=-1; +commit; +} diff --git a/mysql-test/include/kill_and_restart_mysqld.inc b/mysql-test/include/kill_and_restart_mysqld.inc new file mode 100644 index 00000000000..f2ac9b504d2 --- /dev/null +++ b/mysql-test/include/kill_and_restart_mysqld.inc @@ -0,0 +1,19 @@ +--let $_server_id= `SELECT @@server_id` +--let $_expect_file_name= $MYSQLTEST_VARDIR/tmp/mysqld.$_server_id.expect + +if ($restart_parameters) +{ + --echo # Kill and restart: $restart_parameters + --exec echo "restart: $restart_parameters" > $_expect_file_name +} +if (!$restart_parameters) +{ + --echo # Kill and restart + --exec echo "restart" > $_expect_file_name +} + +--shutdown_server 0 +--source include/wait_until_disconnected.inc +--enable_reconnect +--source include/wait_until_connected_again.inc +--disable_reconnect diff --git a/mysql-test/include/kill_mysqld.inc b/mysql-test/include/kill_mysqld.inc new file mode 100644 index 00000000000..86ee048a0f1 --- /dev/null +++ b/mysql-test/include/kill_mysqld.inc @@ -0,0 +1,7 @@ +--let $_server_id= `SELECT @@server_id` +--let $_expect_file_name= $MYSQLTEST_VARDIR/tmp/mysqld.$_server_id.expect + +--echo # Kill the server +--exec echo "wait" > $_expect_file_name +--shutdown_server 0 +--source include/wait_until_disconnected.inc diff --git a/mysql-test/include/mtr_warnings.sql b/mysql-test/include/mtr_warnings.sql index 8f5a4fe6685..0382edf8097 100644 --- a/mysql-test/include/mtr_warnings.sql +++ b/mysql-test/include/mtr_warnings.sql @@ -293,6 +293,7 @@ CREATE DEFINER=root@localhost PROCEDURE add_suppression(pattern VARCHAR(255)) BEGIN INSERT INTO test_suppressions (pattern) VALUES (pattern); + FLUSH NO_WRITE_TO_BINLOG TABLE test_suppressions; END */|| diff --git a/mysql-test/include/restart_mysqld.inc b/mysql-test/include/restart_mysqld.inc index 71efb141917..dcaf47c55a2 100644 --- a/mysql-test/include/restart_mysqld.inc +++ b/mysql-test/include/restart_mysqld.inc @@ -35,7 +35,14 @@ if ($shutdown_timeout == 0) shutdown_server $server_shutdown_timeout; # Write file to make mysql-test-run.pl start up the server again ---exec echo "restart" > $_expect_file_name +if ($restart_parameters) +{ + --exec echo "restart: $restart_parameters" > $_expect_file_name +} +if (!$restart_parameters) +{ + --exec echo "restart" > $_expect_file_name +} # Turn on reconnect --enable_reconnect diff --git a/mysql-test/include/search_pattern_in_file.inc b/mysql-test/include/search_pattern_in_file.inc index 84237026ed0..f77a7c60916 100644 --- a/mysql-test/include/search_pattern_in_file.inc +++ b/mysql-test/include/search_pattern_in_file.inc @@ -60,25 +60,36 @@ perl; use strict; - my $search_file= $ENV{'SEARCH_FILE'} or die "SEARCH_FILE not set"; + die "SEARCH_FILE not set" unless $ENV{'SEARCH_FILE'}; + my @search_files= glob($ENV{'SEARCH_FILE'}); my $search_pattern= $ENV{'SEARCH_PATTERN'} or die "SEARCH_PATTERN not set"; my $search_range= $ENV{'SEARCH_RANGE'}; - my $file_content; + my $content; $search_range= 50000 unless $search_range =~ /-?[0-9]+/; - open(FILE, '<', $search_file) or die("Unable to open '$search_file': $!\n"); - if ($search_range >= 0) { - read(FILE, $file_content, $search_range, 0); - } else { - my $size= -s $search_file; - $search_range = -$size if $size > -$search_range; - seek(FILE, $search_range, 2); - read(FILE, $file_content, -$search_range, 0); + foreach my $search_file (@search_files) { + open(FILE, '<', $search_file) or die("Unable to open '$search_file': $!\n"); + my $file_content; + if ($search_range >= 0) { + read(FILE, $file_content, $search_range, 0); + } else { + my $size= -s $search_file; + $search_range = -$size if $size > -$search_range; + seek(FILE, $search_range, 2); + read(FILE, $file_content, -$search_range, 0); + } + close(FILE); + $content.= $file_content; } - close(FILE); - $search_file =~ s{^.*?([^/\\]+)$}{$1}; - if ($file_content =~ m{$search_pattern}) { - print "FOUND /$search_pattern/ in $search_file\n" + $ENV{'SEARCH_FILE'} =~ s{^.*?([^/\\]+)$}{$1}; + if ($content =~ m{$search_pattern}) { + die "FOUND /$search_pattern/ in $ENV{'SEARCH_FILE'}\n" + if $ENV{SEARCH_ABORT} eq 'FOUND'; + print "FOUND /$search_pattern/ in $ENV{'SEARCH_FILE'}\n" + unless defined $ENV{SEARCH_ABORT}; } else { - print "NOT FOUND /$search_pattern/ in $search_file\n" + die "NOT FOUND /$search_pattern/ in $ENV{'SEARCH_FILE'}\n" + if $ENV{SEARCH_ABORT} eq 'NOT FOUND'; + print "NOT FOUND /$search_pattern/ in $ENV{'SEARCH_FILE'}\n" + unless defined $ENV{SEARCH_ABORT}; } EOF diff --git a/mysql-test/include/start_mysqld.inc b/mysql-test/include/start_mysqld.inc index 983c566821e..e31f26aad8c 100644 --- a/mysql-test/include/start_mysqld.inc +++ b/mysql-test/include/start_mysqld.inc @@ -1,7 +1,14 @@ # Include this script only after using shutdown_mysqld.inc # where $_expect_file_name was initialized. # Write file to make mysql-test-run.pl start up the server again ---exec echo "restart" > $_expect_file_name +if ($restart_parameters) +{ + --exec echo "restart: $restart_parameters" > $_expect_file_name +} +if (!$restart_parameters) +{ + --exec echo "restart" > $_expect_file_name +} # Turn on reconnect --enable_reconnect diff --git a/mysql-test/lib/My/CoreDump.pm b/mysql-test/lib/My/CoreDump.pm index f9f7b3d8d4b..a421d51ec98 100644 --- a/mysql-test/lib/My/CoreDump.pm +++ b/mysql-test/lib/My/CoreDump.pm @@ -53,9 +53,19 @@ sub _verify_binpath { sub _gdb { my ($core_name)= @_; - print "\nTrying 'gdb' to get a backtrace\n"; + # Check that gdb exists + `gdb --version`; + if ($?) { + print "gdb not found, cannot get the stack trace\n"; + return; + } - return unless -f $core_name; + if (-f $core_name) { + print "\nTrying 'gdb' to get a backtrace from coredump $core_name\n"; + } else { + print "\nCoredump $core_name does not exist, cannot run 'gdb'\n"; + return; + } # Find out name of binary that generated core `gdb -c '$core_name' --batch 2>&1` =~ diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index b6263415d9d..87b39333da6 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -1059,7 +1059,7 @@ sub print_global_resfile { resfile_global("gprof", $opt_gprof ? 1 : 0); resfile_global("valgrind", $opt_valgrind ? 1 : 0); resfile_global("callgrind", $opt_callgrind ? 1 : 0); - resfile_global("mem", $opt_mem ? 1 : 0); + resfile_global("mem", $opt_mem); resfile_global("tmpdir", $opt_tmpdir); resfile_global("vardir", $opt_vardir); resfile_global("fast", $opt_fast ? 1 : 0); @@ -1458,12 +1458,14 @@ sub command_line_setup { # Search through list of locations that are known # to be "fast disks" to find a suitable location - # Use --mem=<dir> as first location to look. - my @tmpfs_locations= ($opt_mem,"/run/shm", "/dev/shm", "/tmp"); + my @tmpfs_locations= ("/run/shm", "/dev/shm", "/tmp"); + + # Use $ENV{'MTR_MEM'} as first location to look (if defined) + unshift(@tmpfs_locations, $ENV{'MTR_MEM'}) if defined $ENV{'MTR_MEM'}; foreach my $fs (@tmpfs_locations) { - if ( -d $fs ) + if ( -d $fs && ! -l $fs ) { my $template= "var_${opt_build_thread}_XXXX"; $opt_mem= tempdir( $template, DIR => $fs, CLEANUP => 0); @@ -4798,6 +4800,7 @@ sub extract_warning_lines ($$) { qr/InnoDB: Error: table `test`.`t[12]` .*does not exist in the InnoDB internal/, qr/InnoDB: Warning: Setting innodb_use_sys_malloc/, qr/InnoDB: Warning: a long semaphore wait:/, + qr/InnoDB: Warning: Writer thread is waiting this semaphore:/, qr/Slave: Unknown table 't1' .* 1051/, qr/Slave SQL:.*(Internal MariaDB error code: [[:digit:]]+|Query:.*)/, qr/slave SQL thread aborted/, @@ -6433,9 +6436,9 @@ Options to control directories to use vardir=DIR The directory where files generated from the test run is stored (default: ./var). Specifying a ramdisk or tmpfs will speed up tests. - mem Run testsuite in "memory" using tmpfs or ramdisk - Attempts to find a suitable location - using a builtin list of standard locations + mem[=DIR] Run testsuite in "memory" using tmpfs or ramdisk + Attempts to use DIR first if specified else + uses a builtin list of standard locations for tmpfs (/run/shm, /dev/shm, /tmp) The option can also be set using environment variable MTR_MEM=[DIR] diff --git a/mysql-test/r/alter_table.result b/mysql-test/r/alter_table.result index 72e81895816..cac4c477b5a 100644 --- a/mysql-test/r/alter_table.result +++ b/mysql-test/r/alter_table.result @@ -2091,3 +2091,24 @@ Warnings: Note 1061 Duplicate key name 'id1' DROP TABLE t2; DROP TABLE t1; +# +# MDEV-6390 CONVERT TO CHARACTER SET utf8 doesn't change DEFAULT CHARSET. +# +CREATE TABLE t1 (id int(11) NOT NULL, a int(11) NOT NULL, b int(11)) +ENGINE=InnoDB DEFAULT CHARSET=latin1; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `id` int(11) NOT NULL, + `a` int(11) NOT NULL, + `b` int(11) DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +ALTER TABLE t1 CONVERT TO CHARACTER SET utf8; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `id` int(11) NOT NULL, + `a` int(11) NOT NULL, + `b` int(11) DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8 +DROP TABLE t1; diff --git a/mysql-test/r/contributors.result b/mysql-test/r/contributors.result index f3f5e227d3a..4a26d0f19dd 100644 --- a/mysql-test/r/contributors.result +++ b/mysql-test/r/contributors.result @@ -1,15 +1,17 @@ SHOW CONTRIBUTORS; Name Location Comment -Booking.com http://www.booking.com Founding member, Platinum Sponsor of the MariaDB Foundation +Booking.com https://www.booking.com Founding member, Platinum Sponsor of the MariaDB Foundation +Alibaba Cloud https://intl.aliyun.com Platinum Sponsor of the MariaDB Foundation MariaDB Corporation https://mariadb.com Founding member, Gold Sponsor of the MariaDB Foundation -Visma http://visma.com Gold Sponsor of the MariaDB Foundation -DBS http://dbs.com Gold Sponsor of the MariaDB Foundation +Visma https://visma.com Gold Sponsor of the MariaDB Foundation +DBS https://dbs.com Gold Sponsor of the MariaDB Foundation Nexedi https://www.nexedi.com Silver Sponsor of the MariaDB Foundation Acronis http://www.acronis.com Silver Sponsor of the MariaDB Foundation Auttomattic https://automattic.com Bronze Sponsor of the MariaDB Foundation -Verkkokauppa.com https://virtuozzo.com Bronze Sponsor of the MariaDB Foundation -Virtuozzo https://virtuozzo.com/ Bronze Sponsor of the MariaDB Foundation -Tencent Game DBA http://tencentdba.com/about/ Bronze Sponsor of the MariaDB Foundation +Verkkokauppa.com https://www.verkkokauppa.com Bronze Sponsor of the MariaDB Foundation +Virtuozzo https://virtuozzo.com Bronze Sponsor of the MariaDB Foundation +Tencent Game DBA http://tencentdba.com/about Bronze Sponsor of the MariaDB Foundation +Tencent TDSQL http://tdsql.org Bronze Sponsor of the MariaDB Foundation Google USA Sponsoring encryption, parallel replication and GTID Facebook USA Sponsoring non-blocking API, LIMIT ROWS EXAMINED etc Ronald Bradford Brisbane, Australia EFF contribution for UC2006 Auction diff --git a/mysql-test/r/derived.result b/mysql-test/r/derived.result index 0b392305bbf..ce94fe49f5c 100644 --- a/mysql-test/r/derived.result +++ b/mysql-test/r/derived.result @@ -1014,4 +1014,27 @@ David Yes 210 Edward Yes 150 DROP TABLE example1463; set sql_mode= @save_sql_mode; +# +# MDEV-9028: SELECT DISTINCT constant column of derived table +# used as the second operand of LEFT JOIN +# +create table t1 (id int, data varchar(255)); +insert into t1 values (1,'yes'),(2,'yes'); +select distinct t1.id, tt.id, tt.data +from t1 +left join +(select t1.id, 'yes' as data from t1) as tt +on t1.id = tt.id; +id id data +1 1 yes +2 2 yes +select distinct t1.id, tt.id, tt.data +from t1 +left join +(select t1.id, 'yes' as data from t1 where id > 1) as tt +on t1.id = tt.id; +id id data +2 2 yes +1 NULL NULL +drop table t1; # end of 5.5 diff --git a/mysql-test/r/grant.result b/mysql-test/r/grant.result index 3194573e2ef..0b17e82e5a9 100644 --- a/mysql-test/r/grant.result +++ b/mysql-test/r/grant.result @@ -2527,6 +2527,54 @@ DROP USER mysqltest_u1@localhost; # End of Bug#38347. # +# BUG#11759114 - '51401: GRANT TREATS NONEXISTENT FUNCTIONS/PRIVILEGES +# DIFFERENTLY'. +# +drop database if exists mysqltest_db1; +create database mysqltest_db1; +create user mysqltest_u1; +# Both GRANT statements below should fail with the same error. +grant execute on function mysqltest_db1.f1 to mysqltest_u1; +ERROR 42000: FUNCTION or PROCEDURE f1 does not exist +grant execute on procedure mysqltest_db1.p1 to mysqltest_u1; +ERROR 42000: FUNCTION or PROCEDURE p1 does not exist +# Let us show that GRANT behaviour for routines is consistent +# with GRANT behaviour for tables. Attempt to grant privilege +# on non-existent table also results in an error. +grant select on mysqltest_db1.t1 to mysqltest_u1; +ERROR 42S02: Table 'mysqltest_db1.t1' doesn't exist +show grants for mysqltest_u1; +Grants for mysqltest_u1@% +GRANT USAGE ON *.* TO 'mysqltest_u1'@'%' +drop database mysqltest_db1; +drop user mysqltest_u1; +# +# Bug#12766319 - 61865: RENAME USER DOES NOT WORK CORRECTLY - +# REQUIRES FLUSH PRIVILEGES +# +CREATE USER foo@'127.0.0.1'; +GRANT ALL ON *.* TO foo@'127.0.0.1'; +# First attempt, should connect successfully +SELECT user(), current_user(); +user() current_user() +foo@localhost foo@127.0.0.1 +# Rename the user +RENAME USER foo@'127.0.0.1' to foo@'127.0.0.0/255.0.0.0'; +# Second attempt, should connect successfully as its valid mask +# This was failing without fix +SELECT user(), current_user(); +user() current_user() +foo@localhost foo@127.0.0.0/255.0.0.0 +# Rename the user back to original +RENAME USER foo@'127.0.0.0/255.0.0.0' to foo@'127.0.0.1'; +# Third attempt, should connect successfully +SELECT user(), current_user(); +user() current_user() +foo@localhost foo@127.0.0.1 +# Clean-up +DROP USER foo@'127.0.0.1'; +# End of Bug#12766319 +# # Bug#11756966 - 48958: STORED PROCEDURES CAN BE LEVERAGED TO BYPASS # DATABASE SECURITY # @@ -2552,25 +2600,3 @@ ERROR 42000: Access denied for user 'untrusted'@'localhost' to database 'secret' # Connection default DROP USER untrusted@localhost; DROP DATABASE secret; -# -# BUG#11759114 - '51401: GRANT TREATS NONEXISTENT FUNCTIONS/PRIVILEGES -# DIFFERENTLY'. -# -drop database if exists mysqltest_db1; -create database mysqltest_db1; -create user mysqltest_u1; -# Both GRANT statements below should fail with the same error. -grant execute on function mysqltest_db1.f1 to mysqltest_u1; -ERROR 42000: FUNCTION or PROCEDURE f1 does not exist -grant execute on procedure mysqltest_db1.p1 to mysqltest_u1; -ERROR 42000: FUNCTION or PROCEDURE p1 does not exist -# Let us show that GRANT behaviour for routines is consistent -# with GRANT behaviour for tables. Attempt to grant privilege -# on non-existent table also results in an error. -grant select on mysqltest_db1.t1 to mysqltest_u1; -ERROR 42S02: Table 'mysqltest_db1.t1' doesn't exist -show grants for mysqltest_u1; -Grants for mysqltest_u1@% -GRANT USAGE ON *.* TO 'mysqltest_u1'@'%' -drop database mysqltest_db1; -drop user mysqltest_u1; diff --git a/mysql-test/r/join_nested.result b/mysql-test/r/join_nested.result index 84b6ff640e9..6ddd39cbfec 100644 --- a/mysql-test/r/join_nested.result +++ b/mysql-test/r/join_nested.result @@ -1870,4 +1870,99 @@ f4 NULL NULL DROP TABLE t1,t2,t3,t4,t5; +# +# MDEV-7992: Nested left joins + 'not exists' optimization +# +CREATE TABLE t1( +K1 INT PRIMARY KEY, +Name VARCHAR(15) +); +INSERT INTO t1 VALUES +(1,'T1Row1'), (2,'T1Row2'); +CREATE TABLE t2( +K2 INT PRIMARY KEY, +K1r INT, +rowTimestamp DATETIME, +Event VARCHAR(15) +); +INSERT INTO t2 VALUES +(1, 1, '2015-04-13 10:42:11' ,'T1Row1Event1'), +(2, 1, '2015-04-13 10:42:12' ,'T1Row1Event2'), +(3, 1, '2015-04-13 10:42:12' ,'T1Row1Event3'); +SELECT t1a.*, t2a.*, +t2i.K2 AS K2B, t2i.K1r AS K1rB, +t2i.rowTimestamp AS rowTimestampB, t2i.Event AS EventB +FROM +t1 t1a JOIN t2 t2a ON t2a.K1r = t1a.K1 +LEFT JOIN +( t1 t1i LEFT JOIN t2 t2i ON t2i.K1r = t1i.K1) +ON (t1i.K1 = 1) AND +(((t2i.K1r = t1a.K1 AND t2i.rowTimestamp > t2a.rowTimestamp ) OR +(t2i.rowTimestamp = t2a.rowTimestamp AND t2i.K2 > t2a.K2)) +OR (t2i.K2 IS NULL)) +WHERE +t2a.K1r = 1 AND t2i.K2 IS NULL; +K1 Name K2 K1r rowTimestamp Event K2B K1rB rowTimestampB EventB +1 T1Row1 3 1 2015-04-13 10:42:12 T1Row1Event3 NULL NULL NULL NULL +EXPLAIN EXTENDED SELECT t1a.*, t2a.*, +t2i.K2 AS K2B, t2i.K1r AS K1rB, +t2i.rowTimestamp AS rowTimestampB, t2i.Event AS EventB +FROM +t1 t1a JOIN t2 t2a ON t2a.K1r = t1a.K1 +LEFT JOIN +( t1 t1i LEFT JOIN t2 t2i ON t2i.K1r = t1i.K1) +ON (t1i.K1 = 1) AND +(((t2i.K1r = t1a.K1 AND t2i.rowTimestamp > t2a.rowTimestamp ) OR +(t2i.rowTimestamp = t2a.rowTimestamp AND t2i.K2 > t2a.K2)) +OR (t2i.K2 IS NULL)) +WHERE +t2a.K1r = 1 AND t2i.K2 IS NULL; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1a const PRIMARY PRIMARY 4 const 1 100.00 +1 SIMPLE t2a ALL NULL NULL NULL NULL 3 100.00 Using where +1 SIMPLE t1i const PRIMARY PRIMARY 4 const 1 100.00 Using index +1 SIMPLE t2i ALL NULL NULL NULL NULL 3 100.00 Using where; Not exists +Warnings: +Note 1003 select 1 AS `K1`,'T1Row1' AS `Name`,`test`.`t2a`.`K2` AS `K2`,`test`.`t2a`.`K1r` AS `K1r`,`test`.`t2a`.`rowTimestamp` AS `rowTimestamp`,`test`.`t2a`.`Event` AS `Event`,`test`.`t2i`.`K2` AS `K2B`,`test`.`t2i`.`K1r` AS `K1rB`,`test`.`t2i`.`rowTimestamp` AS `rowTimestampB`,`test`.`t2i`.`Event` AS `EventB` from `test`.`t1` `t1a` join `test`.`t2` `t2a` left join (`test`.`t1` `t1i` left join `test`.`t2` `t2i` on((`test`.`t2i`.`K1r` = 1))) on(((`test`.`t1i`.`K1` = 1) and (((`test`.`t2i`.`K1r` = 1) and (`test`.`t2i`.`rowTimestamp` > `test`.`t2a`.`rowTimestamp`)) or ((`test`.`t2i`.`rowTimestamp` = `test`.`t2a`.`rowTimestamp`) and (`test`.`t2i`.`K2` > `test`.`t2a`.`K2`)) or isnull(`test`.`t2i`.`K2`)))) where ((`test`.`t2a`.`K1r` = 1) and isnull(`test`.`t2i`.`K2`)) +CREATE VIEW v1 AS +SELECT t2i.* +FROM t1 as t1i LEFT JOIN t2 as t2i ON t2i.K1r = t1i.K1 +WHERE t1i.K1 = 1 ; +SELECT +t1a.*, t2a.*, t2b.K2 as K2B, t2b.K1r as K1rB, +t2b.rowTimestamp as rowTimestampB, t2b.Event as EventB +FROM +t1 as t1a JOIN t2 as t2a ON t2a.K1r = t1a.K1 +LEFT JOIN +v1 as t2b +ON ((t2b.K1r = t1a.K1 AND t2b.rowTimestamp > t2a.rowTimestamp) OR +(t2b.rowTimestamp = t2a.rowTimestamp AND t2b.K2 > t2a.K2)) +OR (t2b.K2 IS NULL) +WHERE +t1a.K1 = 1 AND +t2b.K2 IS NULL; +K1 Name K2 K1r rowTimestamp Event K2B K1rB rowTimestampB EventB +1 T1Row1 3 1 2015-04-13 10:42:12 T1Row1Event3 NULL NULL NULL NULL +EXPLAIN EXTENDED SELECT +t1a.*, t2a.*, t2b.K2 as K2B, t2b.K1r as K1rB, +t2b.rowTimestamp as rowTimestampB, t2b.Event as EventB +FROM +t1 as t1a JOIN t2 as t2a ON t2a.K1r = t1a.K1 +LEFT JOIN +v1 as t2b +ON ((t2b.K1r = t1a.K1 AND t2b.rowTimestamp > t2a.rowTimestamp) OR +(t2b.rowTimestamp = t2a.rowTimestamp AND t2b.K2 > t2a.K2)) +OR (t2b.K2 IS NULL) +WHERE +t1a.K1 = 1 AND +t2b.K2 IS NULL; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1a const PRIMARY PRIMARY 4 const 1 100.00 +1 SIMPLE t2a ALL NULL NULL NULL NULL 3 100.00 Using where +1 SIMPLE t1i const PRIMARY PRIMARY 4 const 1 100.00 Using index +1 SIMPLE t2i ALL NULL NULL NULL NULL 3 100.00 Using where; Not exists +Warnings: +Note 1003 select 1 AS `K1`,'T1Row1' AS `Name`,`t2a`.`K2` AS `K2`,`t2a`.`K1r` AS `K1r`,`t2a`.`rowTimestamp` AS `rowTimestamp`,`t2a`.`Event` AS `Event`,`test`.`t2i`.`K2` AS `K2B`,`test`.`t2i`.`K1r` AS `K1rB`,`test`.`t2i`.`rowTimestamp` AS `rowTimestampB`,`test`.`t2i`.`Event` AS `EventB` from `test`.`t1` `t1a` join `test`.`t2` `t2a` left join (`test`.`t1` `t1i` left join `test`.`t2` `t2i` on((`test`.`t2i`.`K1r` = 1))) on(((`test`.`t1i`.`K1` = 1) and (((`test`.`t2i`.`K1r` = 1) and (`test`.`t2i`.`rowTimestamp` > `t2a`.`rowTimestamp`)) or ((`test`.`t2i`.`rowTimestamp` = `t2a`.`rowTimestamp`) and (`test`.`t2i`.`K2` > `t2a`.`K2`)) or isnull(`test`.`t2i`.`K2`)))) where ((`t2a`.`K1r` = 1) and isnull(`test`.`t2i`.`K2`)) +DROP VIEW v1; +DROP TABLE t1,t2; set optimizer_search_depth= @tmp_mdev621; diff --git a/mysql-test/r/join_nested_jcl6.result b/mysql-test/r/join_nested_jcl6.result index 3b47645ca79..bac8e1cb7db 100644 --- a/mysql-test/r/join_nested_jcl6.result +++ b/mysql-test/r/join_nested_jcl6.result @@ -1881,6 +1881,101 @@ f4 NULL NULL DROP TABLE t1,t2,t3,t4,t5; +# +# MDEV-7992: Nested left joins + 'not exists' optimization +# +CREATE TABLE t1( +K1 INT PRIMARY KEY, +Name VARCHAR(15) +); +INSERT INTO t1 VALUES +(1,'T1Row1'), (2,'T1Row2'); +CREATE TABLE t2( +K2 INT PRIMARY KEY, +K1r INT, +rowTimestamp DATETIME, +Event VARCHAR(15) +); +INSERT INTO t2 VALUES +(1, 1, '2015-04-13 10:42:11' ,'T1Row1Event1'), +(2, 1, '2015-04-13 10:42:12' ,'T1Row1Event2'), +(3, 1, '2015-04-13 10:42:12' ,'T1Row1Event3'); +SELECT t1a.*, t2a.*, +t2i.K2 AS K2B, t2i.K1r AS K1rB, +t2i.rowTimestamp AS rowTimestampB, t2i.Event AS EventB +FROM +t1 t1a JOIN t2 t2a ON t2a.K1r = t1a.K1 +LEFT JOIN +( t1 t1i LEFT JOIN t2 t2i ON t2i.K1r = t1i.K1) +ON (t1i.K1 = 1) AND +(((t2i.K1r = t1a.K1 AND t2i.rowTimestamp > t2a.rowTimestamp ) OR +(t2i.rowTimestamp = t2a.rowTimestamp AND t2i.K2 > t2a.K2)) +OR (t2i.K2 IS NULL)) +WHERE +t2a.K1r = 1 AND t2i.K2 IS NULL; +K1 Name K2 K1r rowTimestamp Event K2B K1rB rowTimestampB EventB +1 T1Row1 3 1 2015-04-13 10:42:12 T1Row1Event3 NULL NULL NULL NULL +EXPLAIN EXTENDED SELECT t1a.*, t2a.*, +t2i.K2 AS K2B, t2i.K1r AS K1rB, +t2i.rowTimestamp AS rowTimestampB, t2i.Event AS EventB +FROM +t1 t1a JOIN t2 t2a ON t2a.K1r = t1a.K1 +LEFT JOIN +( t1 t1i LEFT JOIN t2 t2i ON t2i.K1r = t1i.K1) +ON (t1i.K1 = 1) AND +(((t2i.K1r = t1a.K1 AND t2i.rowTimestamp > t2a.rowTimestamp ) OR +(t2i.rowTimestamp = t2a.rowTimestamp AND t2i.K2 > t2a.K2)) +OR (t2i.K2 IS NULL)) +WHERE +t2a.K1r = 1 AND t2i.K2 IS NULL; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1a const PRIMARY PRIMARY 4 const 1 100.00 +1 SIMPLE t2a ALL NULL NULL NULL NULL 3 100.00 Using where +1 SIMPLE t1i const PRIMARY PRIMARY 4 const 1 100.00 Using index +1 SIMPLE t2i ALL NULL NULL NULL NULL 3 100.00 Using where; Not exists +Warnings: +Note 1003 select 1 AS `K1`,'T1Row1' AS `Name`,`test`.`t2a`.`K2` AS `K2`,`test`.`t2a`.`K1r` AS `K1r`,`test`.`t2a`.`rowTimestamp` AS `rowTimestamp`,`test`.`t2a`.`Event` AS `Event`,`test`.`t2i`.`K2` AS `K2B`,`test`.`t2i`.`K1r` AS `K1rB`,`test`.`t2i`.`rowTimestamp` AS `rowTimestampB`,`test`.`t2i`.`Event` AS `EventB` from `test`.`t1` `t1a` join `test`.`t2` `t2a` left join (`test`.`t1` `t1i` left join `test`.`t2` `t2i` on((`test`.`t2i`.`K1r` = 1))) on(((`test`.`t1i`.`K1` = 1) and (((`test`.`t2i`.`K1r` = 1) and (`test`.`t2i`.`rowTimestamp` > `test`.`t2a`.`rowTimestamp`)) or ((`test`.`t2i`.`rowTimestamp` = `test`.`t2a`.`rowTimestamp`) and (`test`.`t2i`.`K2` > `test`.`t2a`.`K2`)) or isnull(`test`.`t2i`.`K2`)))) where ((`test`.`t2a`.`K1r` = 1) and isnull(`test`.`t2i`.`K2`)) +CREATE VIEW v1 AS +SELECT t2i.* +FROM t1 as t1i LEFT JOIN t2 as t2i ON t2i.K1r = t1i.K1 +WHERE t1i.K1 = 1 ; +SELECT +t1a.*, t2a.*, t2b.K2 as K2B, t2b.K1r as K1rB, +t2b.rowTimestamp as rowTimestampB, t2b.Event as EventB +FROM +t1 as t1a JOIN t2 as t2a ON t2a.K1r = t1a.K1 +LEFT JOIN +v1 as t2b +ON ((t2b.K1r = t1a.K1 AND t2b.rowTimestamp > t2a.rowTimestamp) OR +(t2b.rowTimestamp = t2a.rowTimestamp AND t2b.K2 > t2a.K2)) +OR (t2b.K2 IS NULL) +WHERE +t1a.K1 = 1 AND +t2b.K2 IS NULL; +K1 Name K2 K1r rowTimestamp Event K2B K1rB rowTimestampB EventB +1 T1Row1 3 1 2015-04-13 10:42:12 T1Row1Event3 NULL NULL NULL NULL +EXPLAIN EXTENDED SELECT +t1a.*, t2a.*, t2b.K2 as K2B, t2b.K1r as K1rB, +t2b.rowTimestamp as rowTimestampB, t2b.Event as EventB +FROM +t1 as t1a JOIN t2 as t2a ON t2a.K1r = t1a.K1 +LEFT JOIN +v1 as t2b +ON ((t2b.K1r = t1a.K1 AND t2b.rowTimestamp > t2a.rowTimestamp) OR +(t2b.rowTimestamp = t2a.rowTimestamp AND t2b.K2 > t2a.K2)) +OR (t2b.K2 IS NULL) +WHERE +t1a.K1 = 1 AND +t2b.K2 IS NULL; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1a const PRIMARY PRIMARY 4 const 1 100.00 +1 SIMPLE t2a ALL NULL NULL NULL NULL 3 100.00 Using where +1 SIMPLE t1i const PRIMARY PRIMARY 4 const 1 100.00 Using index +1 SIMPLE t2i ALL NULL NULL NULL NULL 3 100.00 Using where; Not exists +Warnings: +Note 1003 select 1 AS `K1`,'T1Row1' AS `Name`,`t2a`.`K2` AS `K2`,`t2a`.`K1r` AS `K1r`,`t2a`.`rowTimestamp` AS `rowTimestamp`,`t2a`.`Event` AS `Event`,`test`.`t2i`.`K2` AS `K2B`,`test`.`t2i`.`K1r` AS `K1rB`,`test`.`t2i`.`rowTimestamp` AS `rowTimestampB`,`test`.`t2i`.`Event` AS `EventB` from `test`.`t1` `t1a` join `test`.`t2` `t2a` left join (`test`.`t1` `t1i` left join `test`.`t2` `t2i` on((`test`.`t2i`.`K1r` = 1))) on(((`test`.`t1i`.`K1` = 1) and (((`test`.`t2i`.`K1r` = 1) and (`test`.`t2i`.`rowTimestamp` > `t2a`.`rowTimestamp`)) or ((`test`.`t2i`.`rowTimestamp` = `t2a`.`rowTimestamp`) and (`test`.`t2i`.`K2` > `t2a`.`K2`)) or isnull(`test`.`t2i`.`K2`)))) where ((`t2a`.`K1r` = 1) and isnull(`test`.`t2i`.`K2`)) +DROP VIEW v1; +DROP TABLE t1,t2; set optimizer_search_depth= @tmp_mdev621; CREATE TABLE t5 (a int, b int, c int, PRIMARY KEY(a), KEY b_i (b)); CREATE TABLE t6 (a int, b int, c int, PRIMARY KEY(a), KEY b_i (b)); diff --git a/mysql-test/r/mysqldump.result b/mysql-test/r/mysqldump.result index cb3c28f42cd..6bf8759de98 100644 --- a/mysql-test/r/mysqldump.result +++ b/mysql-test/r/mysqldump.result @@ -5340,3 +5340,4 @@ DELIMITER ; /*!50003 SET collation_connection = @saved_col_connection */ ; ALTER DATABASE `a\"'``b` CHARACTER SET utf8 COLLATE utf8_general_ci ; DROP DATABASE `a\"'``b`; +FOUND /Database: mysql/ in bug11505.sql diff --git a/mysql-test/r/partition_innodb.result b/mysql-test/r/partition_innodb.result index e1bc075f14a..f863ec5522a 100644 --- a/mysql-test/r/partition_innodb.result +++ b/mysql-test/r/partition_innodb.result @@ -732,6 +732,97 @@ SELECT * FROM t1 WHERE d = '1991-01-01'; d 1991-01-01 DROP TABLE t1; +set global default_storage_engine=default; +# +# MDEV-9455: [ERROR] mysqld got signal 11 +# +CREATE TABLE `t1` ( +`DIARY_TOTAL_DAY_SEQ` bigint(20) unsigned NOT NULL AUTO_INCREMENT, +`IMORY_ID` bigint(20) NOT NULL, +`NAME` varchar(75) DEFAULT NULL, +`DATETIME` varchar(10) NOT NULL DEFAULT '', +`DAILY_CALL_CNT` int(11) DEFAULT NULL, +`DAILY_SMS_CNT` int(11) DEFAULT NULL, +`NUMBER` varchar(64) DEFAULT NULL, +`DURATION` varchar(16) DEFAULT NULL, +PRIMARY KEY (`DIARY_TOTAL_DAY_SEQ`,`DATETIME`), +KEY `IDX_t1_01` (`IMORY_ID`,`DATETIME`) +) AUTO_INCREMENT=328702514 DEFAULT CHARSET=utf8mb4 +PARTITION BY RANGE COLUMNS(`DATETIME`) +(PARTITION p0 VALUES LESS THAN ('2015-10-01') ENGINE = InnoDB, +PARTITION p1 VALUES LESS THAN ('2015-11-01') ENGINE = InnoDB, +PARTITION p2 VALUES LESS THAN ('2015-12-01') ENGINE = InnoDB, +PARTITION p3 VALUES LESS THAN ('2016-01-01') ENGINE = InnoDB, +PARTITION p4 VALUES LESS THAN ('2016-02-01') ENGINE = InnoDB, +PARTITION p5 VALUES LESS THAN ('2016-03-01') ENGINE = InnoDB, +PARTITION p6 VALUES LESS THAN ('2016-04-01') ENGINE = InnoDB, +PARTITION p7 VALUES LESS THAN ('2016-05-01') ENGINE = InnoDB, +PARTITION p8 VALUES LESS THAN ('2016-06-01') ENGINE = InnoDB, +PARTITION p9 VALUES LESS THAN ('2016-07-01') ENGINE = InnoDB, +PARTITION p10 VALUES LESS THAN ('2016-08-01') ENGINE = InnoDB) +; +CREATE TABLE `t2` ( +`DIARY_SEQ` bigint(20) unsigned NOT NULL AUTO_INCREMENT, +`IMORY_ID` bigint(20) NOT NULL, +`CALL_TYPE` varchar(1) DEFAULT NULL, +`DATA_TYPE` varchar(1) DEFAULT NULL, +`FEATURES` varchar(1) DEFAULT NULL, +`NAME` varchar(75) DEFAULT NULL, +`NUMBER` varchar(64) DEFAULT NULL, +`DATETIME` datetime NOT NULL, +`REG_DATE` datetime NOT NULL, +`TITLE` varchar(50) DEFAULT NULL, +`BODY` varchar(4200) DEFAULT NULL, +`MIME_TYPE` varchar(32) DEFAULT NULL, +`DURATION` varchar(16) DEFAULT NULL, +`DEVICE_ID` varchar(64) DEFAULT NULL, +`DEVICE_NAME` varchar(32) DEFAULT NULL, +PRIMARY KEY (`DIARY_SEQ`,`DATETIME`,`REG_DATE`), +KEY `IDX_TB_DIARY_01` (`IMORY_ID`,`DATETIME`,`CALL_TYPE`,`NUMBER`), +KEY `IDX_TB_DIARY_02` (`REG_DATE`) +) AUTO_INCREMENT=688799006 DEFAULT CHARSET=utf8mb4 +PARTITION BY RANGE COLUMNS(REG_DATE) +(PARTITION p0 VALUES LESS THAN ('2015-10-01') ENGINE = InnoDB, +PARTITION p1 VALUES LESS THAN ('2015-11-01') ENGINE = InnoDB, +PARTITION p2 VALUES LESS THAN ('2015-12-01') ENGINE = InnoDB, +PARTITION p3 VALUES LESS THAN ('2016-01-01') ENGINE = InnoDB, +PARTITION p4 VALUES LESS THAN ('2016-02-01') ENGINE = InnoDB, +PARTITION p5 VALUES LESS THAN ('2016-03-01') ENGINE = InnoDB, +PARTITION p6 VALUES LESS THAN ('2016-04-01') ENGINE = InnoDB, +PARTITION p7 VALUES LESS THAN ('2016-05-01') ENGINE = InnoDB, +PARTITION p8 VALUES LESS THAN ('2016-06-01') ENGINE = InnoDB, +PARTITION p9 VALUES LESS THAN ('2016-07-01') ENGINE = InnoDB, +PARTITION p10 VALUES LESS THAN ('2016-08-01') ENGINE = InnoDB) +; +SELECT +A.IMORY_ID, +A.NUMBER, +A.NAME, +DATE_FORMAT(A.DATETIME, '%Y-%m-%d') AS TARGET_DATE, +SUM( CASE WHEN A.DATA_TYPE='1' THEN 1 ELSE 0 END) AS CALL_CNT, +SUM( CASE WHEN A.DATA_TYPE IN ('2', '3') THEN 1 ELSE 0 END) AS SMS_CNT, +SUM(CAST(A.DURATION AS INT)) AS DURATION, +( SELECT COUNT(*) +FROM t1 +WHERE IMORY_ID=A.IMORY_ID +AND NUMBER=A.NUMBER +AND NAME=A.NAME +AND DATETIME = DATE_FORMAT(A.DATETIME, '%Y-%m-%d') +) STATS_COUNT +FROM t2 A +WHERE A.IMORY_ID = 55094102 +AND A.DATETIME LIKE ( +SELECT CONCAT (DATE_FORMAT(DATETIME, '%Y-%m-%d') ,'%') +FROM t2 +WHERE IMORY_ID=55094102 +AND DIARY_SEQ IN ( 608351221, 608351225, 608351229 ) +group by DATE_FORMAT(DATETIME, '%Y-%m-%d') +) +GROUP BY A.IMORY_ID, A.NUMBER, A.NAME, DATE_FORMAT(A.DATETIME, '%Y-%m-%d') +; +IMORY_ID NUMBER NAME TARGET_DATE CALL_CNT SMS_CNT DURATION STATS_COUNT +drop table t2, t1; +set global default_storage_engine='innodb'; # # MDEV-5963: InnoDB: Assertion failure in file row0sel.cc line 2503, # Failing assertion: 0 with "key ptr now exceeds key end by 762 bytes" diff --git a/mysql-test/r/partition_myisam.result b/mysql-test/r/partition_myisam.result index bb1a7b19a9d..08a5bf72f64 100644 --- a/mysql-test/r/partition_myisam.result +++ b/mysql-test/r/partition_myisam.result @@ -230,6 +230,22 @@ PARTITION pMax VALUES LESS THAN MAXVALUE); INSERT INTO t1 VALUES (1, "Partition p1, first row"); DROP TABLE t1; # +# MDEV-10418 Assertion `m_extra_cache' failed +# in ha_partition::late_extra_cache(uint) +# +CREATE TABLE t1 (f1 INT) ENGINE=MyISAM; +INSERT INTO t1 VALUES (1),(2); +CREATE TABLE t2 (f2 INT) ENGINE=MyISAM PARTITION BY RANGE(f2) (PARTITION pmax VALUES LESS THAN MAXVALUE); +INSERT INTO t2 VALUES (8); +CREATE ALGORITHM = MERGE VIEW v AS SELECT f2 FROM t2, t1; +UPDATE v SET f2 = 1; +SELECT * FROM t2; +f2 +1 +DROP VIEW v; +DROP TABLE t2; +DROP TABLE t1; +# # bug#11760213-52599: ALTER TABLE REMOVE PARTITIONING ON NON-PARTITIONED # TABLE CORRUPTS MYISAM DROP TABLE if exists `t1`; diff --git a/mysql-test/r/ps.result b/mysql-test/r/ps.result index eb5c8ca9377..c8fa17a4e9e 100644 --- a/mysql-test/r/ps.result +++ b/mysql-test/r/ps.result @@ -4107,4 +4107,76 @@ NULL NULL deallocate prepare stmt; drop table t1,t2,t3,t4; +# +# MDEV-11859: the plans for the first and the second executions +# of PS are not the same +# +create table t1 (id int, c varchar(3), key idx(c))engine=myisam; +insert into t1 values (3,'bar'), (1,'xxx'), (2,'foo'), (5,'yyy'); +prepare stmt1 from +"explain extended + select * from t1 where (1, 2) in ( select 3, 4 ) or c = 'foo'"; +execute stmt1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ref idx idx 6 const 1 100.00 Using index condition +2 SUBQUERY NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select `test`.`t1`.`id` AS `id`,`test`.`t1`.`c` AS `c` from `test`.`t1` where (`test`.`t1`.`c` = 'foo') +execute stmt1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t1 ref idx idx 6 const 1 100.00 Using index condition +2 SUBQUERY NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select `test`.`t1`.`id` AS `id`,`test`.`t1`.`c` AS `c` from `test`.`t1` where (`test`.`t1`.`c` = 'foo') +deallocate prepare stmt1; +prepare stmt1 from +"select * from t1 where (1, 2) in ( select 3, 4 ) or c = 'foo'"; +flush status; +execute stmt1; +id c +2 foo +show status like '%Handler_read%'; +Variable_name Value +Handler_read_first 0 +Handler_read_key 1 +Handler_read_last 0 +Handler_read_next 1 +Handler_read_prev 0 +Handler_read_retry 0 +Handler_read_rnd 0 +Handler_read_rnd_deleted 0 +Handler_read_rnd_next 0 +flush status; +execute stmt1; +id c +2 foo +show status like '%Handler_read%'; +Variable_name Value +Handler_read_first 0 +Handler_read_key 1 +Handler_read_last 0 +Handler_read_next 1 +Handler_read_prev 0 +Handler_read_retry 0 +Handler_read_rnd 0 +Handler_read_rnd_deleted 0 +Handler_read_rnd_next 0 +deallocate prepare stmt1; +prepare stmt2 from +"explain extended + select * from t1 where (1, 2) in ( select 3, 4 )"; +execute stmt2; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY NULL NULL NULL NULL NULL NULL NULL NULL Impossible WHERE +2 SUBQUERY NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select `test`.`t1`.`id` AS `id`,`test`.`t1`.`c` AS `c` from `test`.`t1` where 0 +execute stmt2; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY NULL NULL NULL NULL NULL NULL NULL NULL Impossible WHERE +2 SUBQUERY NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select `test`.`t1`.`id` AS `id`,`test`.`t1`.`c` AS `c` from `test`.`t1` where 0 +deallocate prepare stmt2; +drop table t1; # End of 5.5 tests diff --git a/mysql-test/r/range_vs_index_merge.result b/mysql-test/r/range_vs_index_merge.result index 9af359a55f3..6813c40a5cf 100644 --- a/mysql-test/r/range_vs_index_merge.result +++ b/mysql-test/r/range_vs_index_merge.result @@ -60,11 +60,11 @@ id select_type table type possible_keys key key_len ref rows Extra EXPLAIN SELECT * FROM City WHERE Population > 100000 AND Name LIKE 'Aba%' OR -Country IN ('CAN', 'ARG') AND ID < 3800 OR -Country < 'U' AND Name LIKE 'Zhu%' OR -ID BETWEEN 3800 AND 3810; +Country IN ('CAN', 'ARG') AND ID BETWEEN 120 AND 130 OR +Country <= 'ALB' AND Name LIKE 'L%' OR +ID BETWEEN 3807 AND 3810; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE City index_merge PRIMARY,Population,Country,Name Name,Country,PRIMARY 35,3,4 NULL 132 Using sort_union(Name,Country,PRIMARY); Using where +1 SIMPLE City index_merge PRIMARY,Population,Country,Name Name,PRIMARY,Country 35,4,3 NULL 31 Using sort_union(Name,PRIMARY,Country); Using where EXPLAIN SELECT * FROM City WHERE (Population > 101000 AND Population < 115000); @@ -1769,4 +1769,42 @@ a b 167 9999 168 10000 DROP TABLE t1; +# +# MDEV-8603: Wrong result OR/AND condition over index fields +# +CREATE TABLE t1 ( +id INT NOT NULL, +state VARCHAR(64), +capital VARCHAR(64), +UNIQUE KEY (id), +KEY state (state,id), +KEY capital (capital, id) +); +INSERT INTO t1 VALUES +(1,'Arizona','Phoenix'), +(2,'Hawaii','Honolulu'), +(3,'Georgia','Atlanta'), +(4,'Florida','Tallahassee'), +(5,'Alaska','Juneau'), +(6,'Michigan','Lansing'), +(7,'Pennsylvania','Harrisburg'), +(8,'Virginia','Richmond') +; +EXPLAIN +SELECT * FROM t1 FORCE KEY (state,capital) +WHERE ( state = 'Alabama' OR state >= 'Colorado' ) AND id != 9 +OR ( capital >= 'Topeka' OR state = 'Kansas' ) AND state != 'Texas'; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range state,capital state 71 NULL 12 Using index condition; Using where +SELECT * FROM t1 FORCE KEY (state,capital) +WHERE ( state = 'Alabama' OR state >= 'Colorado' ) AND id != 9 +OR ( capital >= 'Topeka' OR state = 'Kansas' ) AND state != 'Texas'; +id state capital +4 Florida Tallahassee +3 Georgia Atlanta +2 Hawaii Honolulu +6 Michigan Lansing +7 Pennsylvania Harrisburg +8 Virginia Richmond +DROP TABLE t1; set session optimizer_switch='index_merge_sort_intersection=default'; diff --git a/mysql-test/r/range_vs_index_merge_innodb.result b/mysql-test/r/range_vs_index_merge_innodb.result index 601ae9b7613..13fbc0ac3ef 100644 --- a/mysql-test/r/range_vs_index_merge_innodb.result +++ b/mysql-test/r/range_vs_index_merge_innodb.result @@ -61,11 +61,11 @@ id select_type table type possible_keys key key_len ref rows Extra EXPLAIN SELECT * FROM City WHERE Population > 100000 AND Name LIKE 'Aba%' OR -Country IN ('CAN', 'ARG') AND ID < 3800 OR -Country < 'U' AND Name LIKE 'Zhu%' OR -ID BETWEEN 3800 AND 3810; +Country IN ('CAN', 'ARG') AND ID BETWEEN 120 AND 130 OR +Country <= 'ALB' AND Name LIKE 'L%' OR +ID BETWEEN 3807 AND 3810; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE City index_merge PRIMARY,Population,Country,Name Name,Country,PRIMARY 35,7,4 NULL 123 Using sort_union(Name,Country,PRIMARY); Using where +1 SIMPLE City index_merge PRIMARY,Population,Country,Name Name,Country,PRIMARY 35,3,4 NULL 33 Using sort_union(Name,Country,PRIMARY); Using where EXPLAIN SELECT * FROM City WHERE (Population > 101000 AND Population < 115000); @@ -369,7 +369,7 @@ WHERE ((ID < 200) AND (Name LIKE 'Ha%' OR (Country > 'A' AND Country < 'ARG'))) OR ((ID BETWEEN 100 AND 200) AND (Name LIKE 'Pa%' OR (Population > 103000 AND Population < 104000))); id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE City range PRIMARY,Population,Country,Name PRIMARY 4 NULL 200 Using where +1 SIMPLE City index_merge PRIMARY,Population,Country,Name Name,Population,PRIMARY 39,4,4 NULL 305 Using sort_union(Name,Population,PRIMARY); Using where SELECT * FROM City USE INDEX () WHERE ((ID < 10) AND (Name LIKE 'H%' OR (Country > 'A' AND Country < 'ARG'))) OR ((ID BETWEEN 100 AND 110) AND @@ -1770,5 +1770,43 @@ a b 167 9999 168 10000 DROP TABLE t1; +# +# MDEV-8603: Wrong result OR/AND condition over index fields +# +CREATE TABLE t1 ( +id INT NOT NULL, +state VARCHAR(64), +capital VARCHAR(64), +UNIQUE KEY (id), +KEY state (state,id), +KEY capital (capital, id) +); +INSERT INTO t1 VALUES +(1,'Arizona','Phoenix'), +(2,'Hawaii','Honolulu'), +(3,'Georgia','Atlanta'), +(4,'Florida','Tallahassee'), +(5,'Alaska','Juneau'), +(6,'Michigan','Lansing'), +(7,'Pennsylvania','Harrisburg'), +(8,'Virginia','Richmond') +; +EXPLAIN +SELECT * FROM t1 FORCE KEY (state,capital) +WHERE ( state = 'Alabama' OR state >= 'Colorado' ) AND id != 9 +OR ( capital >= 'Topeka' OR state = 'Kansas' ) AND state != 'Texas'; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range state,capital state 71 NULL 10 Using index condition; Using where +SELECT * FROM t1 FORCE KEY (state,capital) +WHERE ( state = 'Alabama' OR state >= 'Colorado' ) AND id != 9 +OR ( capital >= 'Topeka' OR state = 'Kansas' ) AND state != 'Texas'; +id state capital +4 Florida Tallahassee +3 Georgia Atlanta +2 Hawaii Honolulu +6 Michigan Lansing +7 Pennsylvania Harrisburg +8 Virginia Richmond +DROP TABLE t1; set session optimizer_switch='index_merge_sort_intersection=default'; SET SESSION STORAGE_ENGINE=DEFAULT; diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index c059428be16..2cb1b701e2d 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -7952,3 +7952,41 @@ set global table_open_cache= @tmp_toc; set global table_definition_cache= @tmp_tdc; drop procedure p1; drop table t1,t2,t3,t4,t5,t6; +# +# MDEV-11935: Queries in stored procedures with and +# EXISTS(SELECT * FROM VIEW) crashes and closes hte conneciton. +# +CREATE TABLE ANY_TABLE ( +ENTITY_UID BIGINT NOT NULL +); +CREATE TABLE SECURITY_PATH( +origid BIGINT UNSIGNED NOT NULL, +destid BIGINT UNSIGNED NOT NULL, +KEY (destid) +); +CREATE VIEW ENTITY_ACCESS ( +ENTITY_UID, +OWNER_UID +) AS +SELECT SP1.origid, +SP2.destid +FROM SECURITY_PATH SP1 +JOIN SECURITY_PATH SP2 ON SP1.destid = SP2.origid +; +CREATE PROCEDURE SP_EXAMPLE_SELECT () +BEGIN +SELECT * +FROM ANY_TABLE AT1 +WHERE EXISTS ( SELECT * +FROM ENTITY_ACCESS EA +WHERE AT1.ENTITY_UID = EA.ENTITY_UID +AND EA.OWNER_UID IS NULL ); +END +// +CALL SP_EXAMPLE_SELECT (); +ENTITY_UID +CALL SP_EXAMPLE_SELECT (); +ENTITY_UID +drop procedure SP_EXAMPLE_SELECT; +drop view ENTITY_ACCESS; +drop table ANY_TABLE, SECURITY_PATH; diff --git a/mysql-test/r/symlink-aria-11902.result b/mysql-test/r/symlink-aria-11902.result new file mode 100644 index 00000000000..66405b1c25f --- /dev/null +++ b/mysql-test/r/symlink-aria-11902.result @@ -0,0 +1,39 @@ +set default_storage_engine=Aria; +call mtr.add_suppression("File.*t1.* not found"); +create table mysql.t1 (a int, b char(16), index(a)); +insert mysql.t1 values (100, 'test'),(101,'test'); +create table t1 (a int, b char(16), index(a)) +data directory="MYSQLTEST_VARDIR/tmp/foo"; +insert t1 values (200, 'some'),(201,'some'); +select * from t1; +a b +200 some +201 some +flush tables; +set debug_sync='mi_open_datafile SIGNAL ok WAIT_FOR go'; +select * from t1; +set debug_sync='now WAIT_FOR ok'; +set debug_sync='now SIGNAL go'; +ERROR HY000: File 'MYSQLTEST_VARDIR/tmp/foo/t1.MAD' not found (Errcode: 20 "Not a directory") +flush tables; +drop table if exists t1; +create table t1 (a int, b char(16), index (a)) +index directory="MYSQLTEST_VARDIR/tmp/foo"; +insert t1 values (200, 'some'),(201,'some'); +explain select a from t1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 index NULL a 5 NULL 2 Using index +select a from t1; +a +200 +201 +flush tables; +set debug_sync='mi_open_kfile SIGNAL waiting WAIT_FOR run'; +select a from t1; +set debug_sync='now WAIT_FOR waiting'; +set debug_sync='now SIGNAL run'; +ERROR HY000: Can't find file: './test/t1.MAI' (errno: 20 "Not a directory") +flush tables; +drop table if exists t1; +drop table mysql.t1; +set debug_sync='RESET'; diff --git a/mysql-test/r/symlink-myisam-11902.result b/mysql-test/r/symlink-myisam-11902.result new file mode 100644 index 00000000000..4b07aa3f4a7 --- /dev/null +++ b/mysql-test/r/symlink-myisam-11902.result @@ -0,0 +1,38 @@ +call mtr.add_suppression("File.*t1.* not found"); +create table mysql.t1 (a int, b char(16), index(a)); +insert mysql.t1 values (100, 'test'),(101,'test'); +create table t1 (a int, b char(16), index(a)) +data directory="MYSQLTEST_VARDIR/tmp/foo"; +insert t1 values (200, 'some'),(201,'some'); +select * from t1; +a b +200 some +201 some +flush tables; +set debug_sync='mi_open_datafile SIGNAL ok WAIT_FOR go'; +select * from t1; +set debug_sync='now WAIT_FOR ok'; +set debug_sync='now SIGNAL go'; +ERROR HY000: File 'MYSQLTEST_VARDIR/tmp/foo/t1.MYD' not found (Errcode: 20 "Not a directory") +flush tables; +drop table if exists t1; +create table t1 (a int, b char(16), index (a)) +index directory="MYSQLTEST_VARDIR/tmp/foo"; +insert t1 values (200, 'some'),(201,'some'); +explain select a from t1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 index NULL a 5 NULL 2 Using index +select a from t1; +a +200 +201 +flush tables; +set debug_sync='mi_open_kfile SIGNAL waiting WAIT_FOR run'; +select a from t1; +set debug_sync='now WAIT_FOR waiting'; +set debug_sync='now SIGNAL run'; +ERROR HY000: Can't find file: './test/t1.MYI' (errno: 20 "Not a directory") +flush tables; +drop table if exists t1; +drop table mysql.t1; +set debug_sync='RESET'; diff --git a/mysql-test/r/table_elim.result b/mysql-test/r/table_elim.result index c633261bcd3..3e68a877f02 100644 --- a/mysql-test/r/table_elim.result +++ b/mysql-test/r/table_elim.result @@ -597,7 +597,8 @@ CREATE TABLE t1 (a int(11), b varchar(1)) ; INSERT IGNORE INTO t1 VALUES (0,'g'); CREATE TABLE t3 ( a varchar(1)) ; INSERT IGNORE INTO t3 VALUES ('g'); -CREATE TABLE t2 ( a int(11) NOT NULL, PRIMARY KEY (a)) ; +CREATE TABLE t2 ( a int(11) NOT NULL, PRIMARY KEY (a)); +INSERT INTO t2 VALUES (9), (10); create view v1 as SELECT t1.* FROM t1 LEFT JOIN t2 ON ( t1.a = t2.a ) WHERE t2.a <> 0; SELECT alias1.* FROM t3 LEFT JOIN v1 as alias1 ON ( t3.a = alias1.b ); a b @@ -606,7 +607,7 @@ EXPLAIN SELECT alias1.* FROM t3 LEFT JOIN v1 as alias1 ON ( t3.a = alias1.b ); id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t3 system NULL NULL NULL NULL 1 1 SIMPLE t1 ALL NULL NULL NULL NULL 1 Using where -1 SIMPLE t2 eq_ref PRIMARY PRIMARY 4 test.t1.a 1 Using where; Using index +1 SIMPLE t2 eq_ref PRIMARY PRIMARY 4 test.t1.a 1 Using index drop view v1; DROP TABLE t1,t2,t3; # diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result index 6530fd10a6a..3c6eb235a57 100644 --- a/mysql-test/r/view.result +++ b/mysql-test/r/view.result @@ -5536,6 +5536,89 @@ Warnings: Warning 1356 View 'test.v1' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them drop view v1; drop table t1,t2; +# +# MDEV-12099: usage of mergeable view with LEFT JOIN +# that can be converted to INNER JOIN +# +create table t1 (a int, b int, key(a)) engine=myisam; +insert into t1 values +(3,20), (7,10), (2,10), (4,30), (8,70), +(7,70), (9,100), (9,60), (8,80), (7,60); +create table t2 (c int, d int, key (c)) engine=myisam; +insert into t2 values +(50,100), (20, 200), (10,300), +(150,100), (120, 200), (110,300), +(250,100), (220, 200), (210,300); +create table t3(e int, f int not null, key(e), unique (f)) engine=myisam; +insert into t3 values +(100, 3), (300, 5), (400, 4), (300,7), +(300,2), (600, 13), (800, 15), (700, 14), +(600, 23), (800, 25), (700, 24); +create view v1 as +select * from t2 left join t3 on t3.e=t2.d where t3.f is not null; +select * +from t1 left join v1 on v1.c=t1.b +where t1.a < 5; +a b c d e f +2 10 10 300 300 5 +2 10 10 300 300 7 +2 10 10 300 300 2 +3 20 NULL NULL NULL NULL +4 30 NULL NULL NULL NULL +select * +from t1 left join ( t2 left join t3 on t3.e=t2.d ) +on t2.c=t1.b and t3.f is not null +where t1.a < 5; +a b c d e f +2 10 10 300 300 5 +2 10 10 300 300 7 +2 10 10 300 300 2 +3 20 NULL NULL NULL NULL +4 30 NULL NULL NULL NULL +explain extended +select * +from t1 left join v1 on v1.c=t1.b +where t1.a < 5; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 range a a 5 NULL 3 100.00 Using index condition +1 SIMPLE t2 ref c c 5 test.t1.b 2 100.00 Using where +1 SIMPLE t3 ref f,e e 5 test.t2.d 2 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t2`.`c` AS `c`,`test`.`t2`.`d` AS `d`,`test`.`t3`.`e` AS `e`,`test`.`t3`.`f` AS `f` from `test`.`t1` left join (`test`.`t2` join `test`.`t3`) on(((`test`.`t2`.`c` = `test`.`t1`.`b`) and (`test`.`t3`.`e` = `test`.`t2`.`d`) and (`test`.`t3`.`f` is not null) and (`test`.`t1`.`b` is not null) and (`test`.`t2`.`d` is not null))) where (`test`.`t1`.`a` < 5) +explain extended +select * +from t1 left join ( t2 left join t3 on t3.e=t2.d ) +on t2.c=t1.b and t3.f is not null +where t1.a < 5; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 range a a 5 NULL 3 100.00 Using index condition +1 SIMPLE t2 ref c c 5 test.t1.b 2 100.00 Using where +1 SIMPLE t3 ref f,e e 5 test.t2.d 2 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t2`.`c` AS `c`,`test`.`t2`.`d` AS `d`,`test`.`t3`.`e` AS `e`,`test`.`t3`.`f` AS `f` from `test`.`t1` left join (`test`.`t2` join `test`.`t3`) on(((`test`.`t2`.`c` = `test`.`t1`.`b`) and (`test`.`t3`.`e` = `test`.`t2`.`d`) and (`test`.`t3`.`f` is not null) and (`test`.`t1`.`b` is not null) and (`test`.`t2`.`d` is not null))) where (`test`.`t1`.`a` < 5) +explain extended +select * +from t1 left join v1 on v1.c=t1.b and v1.f=t1.a +where t1.a < 5; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 range a a 5 NULL 3 100.00 Using index condition +1 SIMPLE t3 eq_ref f,e f 4 test.t1.a 1 100.00 Using where +1 SIMPLE t2 ref c c 5 test.t1.b 2 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t2`.`c` AS `c`,`test`.`t2`.`d` AS `d`,`test`.`t3`.`e` AS `e`,`test`.`t3`.`f` AS `f` from `test`.`t1` left join (`test`.`t2` join `test`.`t3`) on(((`test`.`t2`.`c` = `test`.`t1`.`b`) and (`test`.`t3`.`f` = `test`.`t1`.`a`) and (`test`.`t2`.`d` = `test`.`t3`.`e`) and (`test`.`t1`.`a` is not null) and (`test`.`t1`.`a` is not null) and (`test`.`t1`.`b` is not null))) where (`test`.`t1`.`a` < 5) +explain extended +select * +from t1 left join ( t2 left join t3 on t3.e=t2.d ) +on t2.c=t1.b and t3.f=t1.a and t3.f is not null +where t1.a < 5; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 range a a 5 NULL 3 100.00 Using index condition +1 SIMPLE t3 eq_ref f,e f 4 test.t1.a 1 100.00 Using where +1 SIMPLE t2 ref c c 5 test.t1.b 2 100.00 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t2`.`c` AS `c`,`test`.`t2`.`d` AS `d`,`test`.`t3`.`e` AS `e`,`test`.`t3`.`f` AS `f` from `test`.`t1` left join (`test`.`t2` join `test`.`t3`) on(((`test`.`t2`.`c` = `test`.`t1`.`b`) and (`test`.`t3`.`f` = `test`.`t1`.`a`) and (`test`.`t2`.`d` = `test`.`t3`.`e`) and (`test`.`t1`.`a` is not null) and (`test`.`t1`.`a` is not null) and (`test`.`t1`.`b` is not null))) where (`test`.`t1`.`a` < 5) +drop view v1; +drop table t1,t2,t3; # ----------------------------------------------------------------- # -- End of 5.5 tests. # ----------------------------------------------------------------- diff --git a/mysql-test/suite/binlog/r/binlog_max_binlog_stmt_cache_size.result b/mysql-test/suite/binlog/r/binlog_max_binlog_stmt_cache_size.result new file mode 100644 index 00000000000..00f01b59732 --- /dev/null +++ b/mysql-test/suite/binlog/r/binlog_max_binlog_stmt_cache_size.result @@ -0,0 +1,22 @@ +# +# MDEV-4774: Strangeness with max_binlog_stmt_cache_size Settings +# +CALL mtr.add_suppression("unsigned value 18446744073709547520 adjusted to 4294963200"); +SELECT @@global.max_binlog_stmt_cache_size; +@@global.max_binlog_stmt_cache_size +MAX_BINLOG_STMT_CACHE_SIZE +SET @cache_size= @@max_binlog_stmt_cache_size; +SET @@global.max_binlog_stmt_cache_size= @cache_size+1; +SELECT @@global.max_binlog_stmt_cache_size; +@@global.max_binlog_stmt_cache_size +MAX_BINLOG_STMT_CACHE_SIZE +SET @@global.max_binlog_stmt_cache_size = @cache_size+4095; +SELECT @@global.max_binlog_stmt_cache_size; +@@global.max_binlog_stmt_cache_size +MAX_BINLOG_STMT_CACHE_SIZE +SET @@global.max_binlog_stmt_cache_size= @cache_size-1; +SELECT @@global.max_binlog_stmt_cache_size = @cache_size-4096; +@@global.max_binlog_stmt_cache_size = @cache_size-4096 +1 +SET @@global.max_binlog_stmt_cache_size= @cache_size; +# End of test diff --git a/mysql-test/suite/binlog/t/binlog_max_binlog_stmt_cache_size.opt b/mysql-test/suite/binlog/t/binlog_max_binlog_stmt_cache_size.opt new file mode 100644 index 00000000000..c52ef14d5d0 --- /dev/null +++ b/mysql-test/suite/binlog/t/binlog_max_binlog_stmt_cache_size.opt @@ -0,0 +1 @@ +--max_binlog_stmt_cache_size=18446744073709547520 diff --git a/mysql-test/suite/binlog/t/binlog_max_binlog_stmt_cache_size.test b/mysql-test/suite/binlog/t/binlog_max_binlog_stmt_cache_size.test new file mode 100644 index 00000000000..ca3f45c154c --- /dev/null +++ b/mysql-test/suite/binlog/t/binlog_max_binlog_stmt_cache_size.test @@ -0,0 +1,36 @@ + +--echo # +--echo # MDEV-4774: Strangeness with max_binlog_stmt_cache_size Settings +--echo # + +CALL mtr.add_suppression("unsigned value 18446744073709547520 adjusted to 4294963200"); + +--replace_result 18446744073709547520 MAX_BINLOG_STMT_CACHE_SIZE 4294963200 MAX_BINLOG_STMT_CACHE_SIZE +SELECT @@global.max_binlog_stmt_cache_size; + +# Save the initial value of @@global.max_binlog_stmt_cache_size. +--replace_result 18446744073709547520 MAX_BINLOG_STMT_CACHE_SIZE 4294963200 MAX_BINLOG_STMT_CACHE_SIZE +SET @cache_size= @@max_binlog_stmt_cache_size; + +--disable_warnings +SET @@global.max_binlog_stmt_cache_size= @cache_size+1; +--enable_warnings +--replace_result 18446744073709547520 MAX_BINLOG_STMT_CACHE_SIZE 4294963200 MAX_BINLOG_STMT_CACHE_SIZE +SELECT @@global.max_binlog_stmt_cache_size; + +--disable_warnings +SET @@global.max_binlog_stmt_cache_size = @cache_size+4095; +--enable_warnings +--replace_result 4294963200 MAX_BINLOG_STMT_CACHE_SIZE 18446744073709547520 MAX_BINLOG_STMT_CACHE_SIZE +SELECT @@global.max_binlog_stmt_cache_size; + +--disable_warnings +SET @@global.max_binlog_stmt_cache_size= @cache_size-1; +--enable_warnings +SELECT @@global.max_binlog_stmt_cache_size = @cache_size-4096; + +# Restore @@global.max_binlog_stmt_cache_size to its initial value. +SET @@global.max_binlog_stmt_cache_size= @cache_size; + +--echo # End of test + diff --git a/mysql-test/suite/federated/federated_bug_35333.test b/mysql-test/suite/federated/federated_bug_35333.test index 47feefd75a1..d43f4e930f8 100644 --- a/mysql-test/suite/federated/federated_bug_35333.test +++ b/mysql-test/suite/federated/federated_bug_35333.test @@ -61,6 +61,7 @@ let $MYSQLD_DATADIR= `SELECT @@datadir`; --echo # --echo # Trigger a MyISAM system error during an INFORMATION_SCHEMA.TABLES query --echo # +--replace_result 20 2 SELECT TABLE_SCHEMA, TABLE_NAME, TABLE_TYPE, ENGINE, ROW_FORMAT, TABLE_ROWS, DATA_LENGTH, TABLE_COMMENT FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 't1'; diff --git a/mysql-test/suite/innodb/r/alter_key_block_size-11757.result b/mysql-test/suite/innodb/r/alter_key_block_size-11757.result new file mode 100644 index 00000000000..400a3d0df3c --- /dev/null +++ b/mysql-test/suite/innodb/r/alter_key_block_size-11757.result @@ -0,0 +1,47 @@ +set global innodb_file_format=barracuda; +create table t1 ( +id1 bigint(20) not null, +id2 bigint(20) not null, +primary key (id1), +unique key id2 (id2) +) engine=innodb row_format=compressed key_block_size=8; +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `id1` bigint(20) NOT NULL, + `id2` bigint(20) NOT NULL, + PRIMARY KEY (`id1`), + UNIQUE KEY `id2` (`id2`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=8 +alter table t1 row_format=dynamic; +Warnings: +Warning 1478 InnoDB: ignoring KEY_BLOCK_SIZE=8 unless ROW_FORMAT=COMPRESSED. +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `id1` bigint(20) NOT NULL, + `id2` bigint(20) NOT NULL, + PRIMARY KEY (`id1`), + UNIQUE KEY `id2` (`id2`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 ROW_FORMAT=DYNAMIC KEY_BLOCK_SIZE=8 +alter table t1 key_block_size=0; +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `id1` bigint(20) NOT NULL, + `id2` bigint(20) NOT NULL, + PRIMARY KEY (`id1`) KEY_BLOCK_SIZE=8, + UNIQUE KEY `id2` (`id2`) KEY_BLOCK_SIZE=8 +) ENGINE=InnoDB DEFAULT CHARSET=latin1 ROW_FORMAT=DYNAMIC +alter table t1 drop primary key, add primary key (id1), +drop key id2, add unique (id2); +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `id1` bigint(20) NOT NULL, + `id2` bigint(20) NOT NULL, + PRIMARY KEY (`id1`), + UNIQUE KEY `id2` (`id2`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 ROW_FORMAT=DYNAMIC +drop table t1; +set global innodb_file_format=default; diff --git a/mysql-test/suite/innodb/r/innodb-get-fk.result b/mysql-test/suite/innodb/r/innodb-get-fk.result index 0dca82c2d10..f34fb8bcb67 100644 --- a/mysql-test/suite/innodb/r/innodb-get-fk.result +++ b/mysql-test/suite/innodb/r/innodb-get-fk.result @@ -26,7 +26,8 @@ KEY `fk_crewRoleAssigned_roleCode` (`role_code`), CONSTRAINT `fk_crewRoleAssigned_crewId` FOREIGN KEY (`crew_id`) REFERENCES `repro`.`crew` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT `fk_crewRoleAssigned_pilotId` FOREIGN KEY (`crew_id`) REFERENCES `repro`.`pilot` (`id`) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE=InnoDB COMMENT="This is a comment about tables"; -# Restart mysqld --innodb_read_only_mode=1 +ALTER TABLE `repro`.`crew_role_assigned` COMMENT = 'innodb_read_only'; +ERROR HY000: Can't lock file (errno: 165 "Table is read only") SHOW CREATE TABLE `repro`.`crew_role_assigned`; Table Create Table crew_role_assigned CREATE TABLE `crew_role_assigned` ( @@ -52,7 +53,6 @@ crew_role_assigned CREATE TABLE `crew_role_assigned` ( CONSTRAINT `fk_crewRoleAssigned_crewId` FOREIGN KEY (`crew_id`) REFERENCES `crew` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT `fk_crewRoleAssigned_pilotId` FOREIGN KEY (`crew_id`) REFERENCES `pilot` (`id`) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=latin1 COMMENT='This is a new comment about tables' -# Restart mysqld --innodb_read_only_mode=1 SHOW CREATE TABLE `repro`.`crew_role_assigned`; Table Create Table crew_role_assigned CREATE TABLE `crew_role_assigned` ( diff --git a/mysql-test/suite/innodb/r/innodb_blob_unrecoverable_crash.result b/mysql-test/suite/innodb/r/innodb_blob_unrecoverable_crash.result index 9f6b7ca6a23..d69359e8978 100644 --- a/mysql-test/suite/innodb/r/innodb_blob_unrecoverable_crash.result +++ b/mysql-test/suite/innodb/r/innodb_blob_unrecoverable_crash.result @@ -1,4 +1,3 @@ -call mtr.add_suppression("InnoDB: The total blob data length"); SET GLOBAL max_allowed_packet = 100*1024*1024; # Connection big_packets: CREATE TABLE t1 (a BIGINT PRIMARY KEY, b LONGBLOB) ENGINE=InnoDB; @@ -10,9 +9,7 @@ INSERT INTO t1 (a, b) VALUES (5, '5'); start transaction; INSERT INTO t1 (a, b) VALUES (6, REPEAT('a', 20*1024*1024)); ERROR 42000: The size of BLOB/TEXT data inserted in one transaction is greater than 10% of redo log size. Increase the redo log size using innodb_log_file_size. -# Connection default: -# Quick shutdown and restart server -# Connection default: +# Kill and restart SELECT a FROM t1; a 1 diff --git a/mysql-test/suite/innodb/r/innodb_bug14676111.result b/mysql-test/suite/innodb/r/innodb_bug14676111.result index c2fdfee5522..1135259cfbf 100644 --- a/mysql-test/suite/innodb/r/innodb_bug14676111.result +++ b/mysql-test/suite/innodb/r/innodb_bug14676111.result @@ -1,7 +1,4 @@ -drop table if exists t1; -call mtr.add_suppression("option 'innodb-purge-threads': unsigned value 0 adjusted to*"); -set global innodb_stats_persistent = false; -CREATE TABLE t1 (a int not null primary key) engine=InnoDB; +CREATE TABLE t1 (a INT PRIMARY KEY) ENGINE=InnoDB STATS_PERSISTENT=0; set global innodb_limit_optimistic_insert_debug = 2; insert into t1 values (1); insert into t1 values (5); diff --git a/mysql-test/suite/innodb/r/innodb_bug59641.result b/mysql-test/suite/innodb/r/innodb_bug59641.result index 5062c69558b..f4c2199ae88 100644 --- a/mysql-test/suite/innodb/r/innodb_bug59641.result +++ b/mysql-test/suite/innodb/r/innodb_bug59641.result @@ -1,5 +1,3 @@ -call mtr.add_suppression("Found 3 prepared XA transactions"); -flush tables; CREATE TABLE t(a INT PRIMARY KEY, b INT)ENGINE=InnoDB; INSERT INTO t VALUES(2,2),(4,4),(8,8),(16,16),(32,32); COMMIT; @@ -16,6 +14,7 @@ XA START '789'; UPDATE t SET b=4*a WHERE a=32; XA END '789'; XA PREPARE '789'; +# Kill and restart SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; SELECT * FROM t; a b diff --git a/mysql-test/suite/innodb/r/log_file_size.result b/mysql-test/suite/innodb/r/log_file_size.result new file mode 100644 index 00000000000..d0b389379e7 --- /dev/null +++ b/mysql-test/suite/innodb/r/log_file_size.result @@ -0,0 +1,73 @@ +CREATE TABLE t1(a INT PRIMARY KEY) ENGINE=InnoDB; +BEGIN; +INSERT INTO t1 VALUES (42); +# Kill and restart: --innodb-log-file-size=6M +SELECT * FROM t1; +a +INSERT INTO t1 VALUES (42); +BEGIN; +DELETE FROM t1; +# Kill and restart: --innodb-log-files-in-group=3 --innodb-log-file-size=5M +SELECT * FROM t1; +a +42 +INSERT INTO t1 VALUES (123); +BEGIN; +DELETE FROM t1; +# Kill the server +SELECT * FROM t1; +ERROR 42000: Unknown storage engine 'InnoDB' +FOUND /syntax error in innodb_log_group_home_dir/ in mysqld.1.err +SELECT * FROM t1; +ERROR 42000: Unknown storage engine 'InnoDB' +FOUND /InnoDB: Starting an apply batch of log records/ in mysqld.1.err +SELECT * FROM t1; +ERROR 42000: Unknown storage engine 'InnoDB' +FOUND /InnoDB: Starting an apply batch of log records/ in mysqld.1.err +SELECT * FROM t1; +ERROR 42000: Unknown storage engine 'InnoDB' +FOUND /InnoDB: innodb_read_only prevents crash recovery/ in mysqld.1.err +SELECT * FROM t1; +ERROR 42000: Unknown storage engine 'InnoDB' +FOUND /InnoDB: Starting an apply batch of log records/ in mysqld.1.err +FOUND /InnoDB: Resizing redo log from 3\*[0-9]+ to 2\*[0-9]+ pages/ in mysqld.1.err +SELECT * FROM t1; +ERROR 42000: Unknown storage engine 'InnoDB' +FOUND /InnoDB: Starting an apply batch of log records/ in mysqld.1.err +FOUND /InnoDB: Resizing redo log from 3\*[0-9]+ to 2\*[0-9]+ pages/ in mysqld.1.err +SELECT * FROM t1; +ERROR 42000: Unknown storage engine 'InnoDB' +FOUND /InnoDB: innodb_read_only prevents crash recovery/ in mysqld.1.err +SELECT * FROM t1; +ERROR 42000: Unknown storage engine 'InnoDB' +FOUND /InnoDB: Starting an apply batch of log records/ in mysqld.1.err +FOUND /InnoDB: Resizing redo log from 3\*[0-9]+ to 2\*[0-9]+ pages/ in mysqld.1.err +SELECT * FROM t1; +ERROR 42000: Unknown storage engine 'InnoDB' +SELECT * FROM t1; +ERROR 42000: Unknown storage engine 'InnoDB' +FOUND /InnoDB: Cannot create log files in read-only mode/ in mysqld.1.err +SELECT * FROM t1; +ERROR 42000: Unknown storage engine 'InnoDB' +FOUND /InnoDB: Setting log file .*ib_logfile[0-9]+ size to/ in mysqld.1.err +SELECT * FROM t1; +ERROR 42000: Unknown storage engine 'InnoDB' +FOUND /InnoDB: Setting log file .*ib_logfile[0-9]+ size to/ in mysqld.1.err +SELECT * FROM t1; +ERROR 42000: Unknown storage engine 'InnoDB' +FOUND /InnoDB: Only one log file found/ in mysqld.1.err +SELECT * FROM t1; +ERROR 42000: Unknown storage engine 'InnoDB' +FOUND /InnoDB: Log file .*ib_logfile0 size 7 is not a multiple of innodb_page_size/ in mysqld.1.err +SELECT * FROM t1; +ERROR 42000: Unknown storage engine 'InnoDB' +FOUND /InnoDB: Log file .*ib_logfile1 is of different size 1048576 bytes than other log files/ in mysqld.1.err +SELECT * FROM t1; +ERROR 42000: Unknown storage engine 'InnoDB' +FOUND /InnoDB: Setting log file .*ib_logfile[0-9]+ size to/ in mysqld.1.err +FOUND /InnoDB: Renaming log file .*ib_logfile101 to .*ib_logfile0/ in mysqld.1.err +SELECT * FROM t1; +a +42 +123 +DROP TABLE t1; diff --git a/mysql-test/suite/innodb/r/read_only_recovery.result b/mysql-test/suite/innodb/r/read_only_recovery.result new file mode 100644 index 00000000000..c54c3b5ab7f --- /dev/null +++ b/mysql-test/suite/innodb/r/read_only_recovery.result @@ -0,0 +1,29 @@ +CREATE TABLE t(a INT PRIMARY KEY) ENGINE=InnoDB; +BEGIN; +INSERT INTO t VALUES(1),(2); +DELETE FROM t WHERE a=2; +# Normal MariaDB shutdown would roll back the above transaction. +# We want the transaction to remain open, so we will kill the server +# after ensuring that any non-transactional files are clean. +FLUSH TABLES; +# Ensure that the above incomplete transaction becomes durable. +SET GLOBAL innodb_flush_log_at_trx_commit=1; +BEGIN; +INSERT INTO t VALUES(0); +ROLLBACK; +# Kill and restart: --innodb-force-recovery=3 +SELECT * FROM t; +a +SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; +SELECT * FROM t; +a +1 +SELECT * FROM t; +a +SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; +SELECT * FROM t; +a +1 +SELECT * FROM t; +a +DROP TABLE t; diff --git a/mysql-test/suite/innodb/r/xa_recovery.result b/mysql-test/suite/innodb/r/xa_recovery.result index 84cb37ef7c9..4441701f961 100644 --- a/mysql-test/suite/innodb/r/xa_recovery.result +++ b/mysql-test/suite/innodb/r/xa_recovery.result @@ -4,7 +4,7 @@ XA START 'x'; UPDATE t1 set a=2; XA END 'x'; XA PREPARE 'x'; -call mtr.add_suppression("Found 1 prepared XA transactions"); +# Kill and restart SELECT * FROM t1 LOCK IN SHARE MODE; SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; SELECT * FROM t1; diff --git a/mysql-test/suite/innodb/t/alter_key_block_size-11757.test b/mysql-test/suite/innodb/t/alter_key_block_size-11757.test new file mode 100644 index 00000000000..50a909870ba --- /dev/null +++ b/mysql-test/suite/innodb/t/alter_key_block_size-11757.test @@ -0,0 +1,23 @@ +# +# MDEV-11757 KEY_BLOCK_SIZE strangeness when UNCOMPRESSing COMPRESSed InnoDB tables +# +source include/have_innodb.inc; +set global innodb_file_format=barracuda; + +create table t1 ( + id1 bigint(20) not null, + id2 bigint(20) not null, + primary key (id1), + unique key id2 (id2) +) engine=innodb row_format=compressed key_block_size=8; +show create table t1; +alter table t1 row_format=dynamic; +show create table t1; +alter table t1 key_block_size=0; +show create table t1; +alter table t1 drop primary key, add primary key (id1), + drop key id2, add unique (id2); +show create table t1; +drop table t1; + +set global innodb_file_format=default; diff --git a/mysql-test/suite/innodb/t/innodb-get-fk.test b/mysql-test/suite/innodb/t/innodb-get-fk.test index 4245bef289f..c1ab54fab45 100644 --- a/mysql-test/suite/innodb/t/innodb-get-fk.test +++ b/mysql-test/suite/innodb/t/innodb-get-fk.test @@ -33,19 +33,20 @@ CONSTRAINT `fk_crewRoleAssigned_crewId` FOREIGN KEY (`crew_id`) REFERENCES `repr CONSTRAINT `fk_crewRoleAssigned_pilotId` FOREIGN KEY (`crew_id`) REFERENCES `repro`.`pilot` (`id`) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE=InnoDB COMMENT="This is a comment about tables"; ---echo # Restart mysqld --innodb_read_only_mode=1 --- let $restart_parameters=--innodb-read-only-mode=1 +-- let $restart_parameters=--innodb-read-only -- source include/restart_mysqld.inc +--error ER_CANT_LOCK +ALTER TABLE `repro`.`crew_role_assigned` COMMENT = 'innodb_read_only'; SHOW CREATE TABLE `repro`.`crew_role_assigned`; +-- let $restart_parameters= -- source include/restart_mysqld.inc ALTER TABLE `repro`.`crew_role_assigned` COMMENT = "This is a new comment about tables"; SHOW CREATE TABLE `repro`.`crew_role_assigned`; ---echo # Restart mysqld --innodb_read_only_mode=1 --- let $restart_parameters=--innodb-read-only-mode=1 +-- let $restart_parameters=--innodb-read-only -- source include/restart_mysqld.inc # @@ -53,6 +54,7 @@ SHOW CREATE TABLE `repro`.`crew_role_assigned`; # SHOW CREATE TABLE `repro`.`crew_role_assigned`; +-- let $restart_parameters= -- source include/restart_mysqld.inc DROP TABLE `repro`.`crew_role_assigned`; diff --git a/mysql-test/suite/innodb/t/innodb_blob_unrecoverable_crash.test b/mysql-test/suite/innodb/t/innodb_blob_unrecoverable_crash.test index 16fb570737d..dc6d2f6f65c 100644 --- a/mysql-test/suite/innodb/t/innodb_blob_unrecoverable_crash.test +++ b/mysql-test/suite/innodb/t/innodb_blob_unrecoverable_crash.test @@ -2,11 +2,13 @@ --source include/not_crashrep.inc --source include/have_innodb.inc -call mtr.add_suppression("InnoDB: The total blob data length"); - -let $old_max_allowed_packet = `select @@max_allowed_packet`; SET GLOBAL max_allowed_packet = 100*1024*1024; +--disable_query_log +call mtr.add_suppression("InnoDB: The total blob data length"); +FLUSH TABLES; +--enable_query_log + --echo # Connection big_packets: connect(big_packets,localhost,root,,); connection big_packets; @@ -28,28 +30,12 @@ start transaction; --error ER_TOO_BIG_ROWSIZE INSERT INTO t1 (a, b) VALUES (6, REPEAT('a', 20*1024*1024)); ---echo # Connection default: -connection default; - -# We expect a restart. ---exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect - ---echo # Quick shutdown and restart server ---shutdown_server 0 - -# Wait for the server to come back up, and reconnect. ---enable_reconnect ---source include/wait_until_connected_again.inc - ---echo # Connection default: connection default; +--source include/kill_and_restart_mysqld.inc +disconnect big_packets; # We should see (1,2,3,4,5) here. SELECT a FROM t1; # Clean up. DROP TABLE t1; - ---disable_query_log -eval set global max_allowed_packet = $old_max_allowed_packet; ---enable_query_log diff --git a/mysql-test/suite/innodb/t/innodb_bug14676111-master.opt b/mysql-test/suite/innodb/t/innodb_bug14676111-master.opt deleted file mode 100644 index e16b9b0b895..00000000000 --- a/mysql-test/suite/innodb/t/innodb_bug14676111-master.opt +++ /dev/null @@ -1 +0,0 @@ ---loose-innodb-purge-threads=0 diff --git a/mysql-test/suite/innodb/t/innodb_bug14676111.test b/mysql-test/suite/innodb/t/innodb_bug14676111.test index ba04d421fde..02ef5e5e7f0 100644 --- a/mysql-test/suite/innodb/t/innodb_bug14676111.test +++ b/mysql-test/suite/innodb/t/innodb_bug14676111.test @@ -10,20 +10,12 @@ if (`select count(*)=0 from information_schema.global_variables where variable_n --disable_query_log set @old_innodb_limit_optimistic_insert_debug = @@innodb_limit_optimistic_insert_debug; -set @old_innodb_stats_persistent = @@innodb_stats_persistent; set @old_innodb_undo_logs = @@innodb_undo_logs; # Limit undo segments for stable progress of purge. set global innodb_undo_logs = 1; --enable_query_log ---disable_warnings -drop table if exists t1; ---enable_warnings -call mtr.add_suppression("option 'innodb-purge-threads': unsigned value 0 adjusted to*"); - -set global innodb_stats_persistent = false; - -CREATE TABLE t1 (a int not null primary key) engine=InnoDB; +CREATE TABLE t1 (a INT PRIMARY KEY) ENGINE=InnoDB STATS_PERSISTENT=0; # # make 4 leveled straight tree @@ -139,6 +131,5 @@ drop table t1; --disable_query_log set global innodb_limit_optimistic_insert_debug = @old_innodb_limit_optimistic_insert_debug; -set global innodb_stats_persistent = @old_innodb_stats_persistent; set global innodb_undo_logs = @old_innodb_undo_logs; --enable_query_log diff --git a/mysql-test/suite/innodb/t/innodb_bug59641.test b/mysql-test/suite/innodb/t/innodb_bug59641.test index a8d35cd1029..7fa32d2ec35 100644 --- a/mysql-test/suite/innodb/t/innodb_bug59641.test +++ b/mysql-test/suite/innodb/t/innodb_bug59641.test @@ -2,11 +2,11 @@ # Bug #59641 Prepared XA transaction causes shutdown hang after a crash -- source include/not_embedded.inc -# The server would issue this warning on restart. -call mtr.add_suppression("Found 3 prepared XA transactions"); -# Close tables used by other tests (to not get crashed myisam tables) -flush tables; +--disable_query_log +call mtr.add_suppression("Found 3 prepared XA transactions"); +FLUSH TABLES; +--enable_query_log CREATE TABLE t(a INT PRIMARY KEY, b INT)ENGINE=InnoDB; INSERT INTO t VALUES(2,2),(4,4),(8,8),(16,16),(32,32); @@ -17,7 +17,6 @@ XA END '123'; XA PREPARE '123'; CONNECT (con1,localhost,root,,); -CONNECTION con1; XA START '456'; INSERT INTO t VALUES(3,47),(5,67); @@ -26,7 +25,6 @@ XA END '456'; XA PREPARE '456'; CONNECT (con2,localhost,root,,); -CONNECTION con2; XA START '789'; UPDATE t SET b=4*a WHERE a=32; @@ -34,30 +32,13 @@ XA END '789'; XA PREPARE '789'; CONNECT (con3,localhost,root,,); -CONNECTION con3; -# Kill the server without sending a shutdown command --- exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect --- shutdown_server 0 --- source include/wait_until_disconnected.inc - -# Restart the server. --- exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect --- enable_reconnect --- source include/wait_until_connected_again.inc +--source include/kill_and_restart_mysqld.inc SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; SELECT * FROM t; COMMIT; -# Shut down the server. This would hang because of the bug. --- exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect --- shutdown_server --- source include/wait_until_disconnected.inc - -# Restart the server. --- exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect --- enable_reconnect --- source include/wait_until_connected_again.inc +--source include/restart_mysqld.inc SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; SELECT * FROM t; diff --git a/mysql-test/suite/innodb/t/log_file_size.test b/mysql-test/suite/innodb/t/log_file_size.test new file mode 100644 index 00000000000..ae63ee6e133 --- /dev/null +++ b/mysql-test/suite/innodb/t/log_file_size.test @@ -0,0 +1,222 @@ +# Test resizing the InnoDB redo log. + +--source include/have_innodb.inc + +# Embedded server does not support crashing +--source include/not_embedded.inc +# Avoid CrashReporter popup on Mac +--source include/not_crashrep.inc +# DBUG_EXECUTE_IF is needed +--source include/have_debug.inc + +if (`SELECT @@innodb_log_file_size = 1048576`) { + --skip Test requires innodb_log_file_size>1M. +} + +--disable_query_log +call mtr.add_suppression("InnoDB: Resizing redo log"); +call mtr.add_suppression("InnoDB: Starting to delete and rewrite log files"); +call mtr.add_suppression("InnoDB: New log files created"); +# This message is output by 10.0 and 10.1, not by 10.2 +call mtr.add_suppression("InnoDB: The log sequence number in the ibdata files is higher than the log sequence number in the ib_logfiles"); +call mtr.add_suppression("InnoDB: The log sequence numbers [0-9]+ and [0-9]+ in ibdata files do not match the log sequence number [0-9]+ in the ib_logfiles"); +call mtr.add_suppression("syntax error in innodb_log_group_home_dir"); +call mtr.add_suppression("Plugin 'InnoDB' init function returned error"); +call mtr.add_suppression("Plugin 'InnoDB' registration as a STORAGE ENGINE failed"); +call mtr.add_suppression("InnoDB: Plugin initialization aborted"); +call mtr.add_suppression("InnoDB: innodb_read_only prevents crash recovery"); +call mtr.add_suppression("InnoDB: Are you sure you are using the right ib_logfiles"); +call mtr.add_suppression("InnoDB: Cannot create log files in read-only mode"); +call mtr.add_suppression("InnoDB: Only one log file found"); +call mtr.add_suppression("InnoDB: Log file .*ib_logfile[01].* size"); +call mtr.add_suppression("InnoDB: Unable to open .*ib_logfile0. to check native AIO read support"); +# InnoDB shutdown after refused startup is not clean in 10.0 or 10.1! +call mtr.add_suppression("mysqld got signal 11"); +call mtr.add_suppression("Attempting backtrace"); +FLUSH TABLES; +--enable_query_log + +CREATE TABLE t1(a INT PRIMARY KEY) ENGINE=InnoDB; +BEGIN; +INSERT INTO t1 VALUES (42); + +let $restart_parameters = --innodb-log-file-size=6M; +--source include/kill_and_restart_mysqld.inc + +SELECT * FROM t1; + +INSERT INTO t1 VALUES (42); +BEGIN; +DELETE FROM t1; + +let $restart_parameters = --innodb-log-files-in-group=3 --innodb-log-file-size=5M; +--source include/kill_and_restart_mysqld.inc + +SELECT * FROM t1; + +INSERT INTO t1 VALUES (123); + +let MYSQLD_DATADIR= `select @@datadir`; +let SEARCH_RANGE= -50000; +let SEARCH_FILE= $MYSQLTEST_VARDIR/log/mysqld.1.err; + +BEGIN; +DELETE FROM t1; + +--source include/kill_mysqld.inc + +--let $restart_parameters= --innodb-log-group-home-dir=foo\;bar +--source include/start_mysqld.inc +--error ER_UNKNOWN_STORAGE_ENGINE +SELECT * FROM t1; +let SEARCH_PATTERN= syntax error in innodb_log_group_home_dir; +--source include/search_pattern_in_file.inc + +--let $restart_parameters= --debug=d,innodb_log_abort_1 +--source include/restart_mysqld.inc +--error ER_UNKNOWN_STORAGE_ENGINE +SELECT * FROM t1; +let SEARCH_PATTERN= InnoDB: Starting an apply batch of log records; +--source include/search_pattern_in_file.inc + +--let $restart_parameters= --debug=d,innodb_log_abort_3 +--source include/restart_mysqld.inc +--error ER_UNKNOWN_STORAGE_ENGINE +SELECT * FROM t1; +let SEARCH_PATTERN= InnoDB: Starting an apply batch of log records; +--source include/search_pattern_in_file.inc + +--let $restart_parameters= --innodb-read-only +--source include/restart_mysqld.inc + +--error ER_UNKNOWN_STORAGE_ENGINE +SELECT * FROM t1; +let SEARCH_PATTERN= InnoDB: innodb_read_only prevents crash recovery; +--source include/search_pattern_in_file.inc + +--let $restart_parameters= --debug=d,innodb_log_abort_4 +--source include/restart_mysqld.inc +--error ER_UNKNOWN_STORAGE_ENGINE +SELECT * FROM t1; +let SEARCH_PATTERN= InnoDB: Starting an apply batch of log records; +--source include/search_pattern_in_file.inc +let SEARCH_PATTERN= InnoDB: Resizing redo log from 3\*[0-9]+ to 2\*[0-9]+ pages; +--source include/search_pattern_in_file.inc + +--let $restart_parameters= --debug=d,innodb_log_abort_5 +--source include/restart_mysqld.inc +--error ER_UNKNOWN_STORAGE_ENGINE +SELECT * FROM t1; +let SEARCH_PATTERN= InnoDB: Starting an apply batch of log records; +--source include/search_pattern_in_file.inc +let SEARCH_PATTERN= InnoDB: Resizing redo log from 3\*[0-9]+ to 2\*[0-9]+ pages; +--source include/search_pattern_in_file.inc + +--let $restart_parameters= --innodb-read-only +--source include/restart_mysqld.inc +--error ER_UNKNOWN_STORAGE_ENGINE +SELECT * FROM t1; +let SEARCH_PATTERN= InnoDB: innodb_read_only prevents crash recovery; +--source include/search_pattern_in_file.inc + +--let $restart_parameters= --debug=d,innodb_log_abort_6 +--source include/restart_mysqld.inc +--error ER_UNKNOWN_STORAGE_ENGINE +SELECT * FROM t1; + +let SEARCH_PATTERN= InnoDB: Starting an apply batch of log records; +--source include/search_pattern_in_file.inc +let SEARCH_PATTERN= InnoDB: Resizing redo log from 3\*[0-9]+ to 2\*[0-9]+ pages; +--source include/search_pattern_in_file.inc + +--let $restart_parameters= --debug=d,innodb_log_abort_7 +--source include/restart_mysqld.inc +--error ER_UNKNOWN_STORAGE_ENGINE +SELECT * FROM t1; + +# this aborts right after deleting all log files + +--let $restart_parameters= --innodb-read-only +--source include/restart_mysqld.inc +--error ER_UNKNOWN_STORAGE_ENGINE +SELECT * FROM t1; + +let SEARCH_PATTERN= InnoDB: Cannot create log files in read-only mode; +--source include/search_pattern_in_file.inc + +--let $restart_parameters= --debug=d,innodb_log_abort_8 +--source include/restart_mysqld.inc +--error ER_UNKNOWN_STORAGE_ENGINE +SELECT * FROM t1; + +let SEARCH_PATTERN= InnoDB: Setting log file .*ib_logfile[0-9]+ size to; +--source include/search_pattern_in_file.inc + +--let $restart_parameters= --debug=d,innodb_log_abort_9 +--source include/restart_mysqld.inc +--error ER_UNKNOWN_STORAGE_ENGINE +SELECT * FROM t1; + +let SEARCH_PATTERN= InnoDB: Setting log file .*ib_logfile[0-9]+ size to; +--source include/search_pattern_in_file.inc +--source include/shutdown_mysqld.inc + +# We should have perfectly synced files here. +# Rename the log files, and trigger an error in recovery. +--move_file $MYSQLD_DATADIR/ib_logfile101 $MYSQLD_DATADIR/ib_logfile0 +--move_file $MYSQLD_DATADIR/ib_logfile1 $MYSQLD_DATADIR/ib_logfile1_hidden + +--let $restart_parameters= +--source include/start_mysqld.inc +--error ER_UNKNOWN_STORAGE_ENGINE +SELECT * FROM t1; + +let SEARCH_PATTERN= InnoDB: Only one log file found; +--source include/search_pattern_in_file.inc +--move_file $MYSQLD_DATADIR/ib_logfile0 $MYSQLD_DATADIR/ib_logfile101 + +perl; +die unless open(FILE, ">$ENV{MYSQLD_DATADIR}/ib_logfile0"); +print FILE "garbage"; +close(FILE); +EOF + +--source include/restart_mysqld.inc +--error ER_UNKNOWN_STORAGE_ENGINE +SELECT * FROM t1; +let SEARCH_PATTERN= InnoDB: Log file .*ib_logfile0 size 7 is not a multiple of innodb_page_size; +--source include/search_pattern_in_file.inc +--remove_file $MYSQLD_DATADIR/ib_logfile0 +--move_file $MYSQLD_DATADIR/ib_logfile101 $MYSQLD_DATADIR/ib_logfile0 + +perl; +die unless open(FILE, ">$ENV{MYSQLD_DATADIR}/ib_logfile1"); +print FILE "junkfill" x 131072; +close(FILE); +EOF + +--source include/restart_mysqld.inc +--error ER_UNKNOWN_STORAGE_ENGINE +SELECT * FROM t1; + +let SEARCH_PATTERN= InnoDB: Log file .*ib_logfile1 is of different size 1048576 bytes than other log files; +--source include/search_pattern_in_file.inc +--remove_file $MYSQLD_DATADIR/ib_logfile1 +--move_file $MYSQLD_DATADIR/ib_logfile0 $MYSQLD_DATADIR/ib_logfile101 +--move_file $MYSQLD_DATADIR/ib_logfile1_hidden $MYSQLD_DATADIR/ib_logfile1 + +--let $restart_parameters= --debug=d,innodb_log_abort_10 +--source include/restart_mysqld.inc +--error ER_UNKNOWN_STORAGE_ENGINE +SELECT * FROM t1; + +let SEARCH_PATTERN= InnoDB: Setting log file .*ib_logfile[0-9]+ size to; +--source include/search_pattern_in_file.inc +let SEARCH_PATTERN= InnoDB: Renaming log file .*ib_logfile101 to .*ib_logfile0; +--source include/search_pattern_in_file.inc + +--let $restart_parameters= +--source include/restart_mysqld.inc + +SELECT * FROM t1; +DROP TABLE t1; diff --git a/mysql-test/suite/innodb/t/read_only_recovery.test b/mysql-test/suite/innodb/t/read_only_recovery.test new file mode 100644 index 00000000000..f41081e5a94 --- /dev/null +++ b/mysql-test/suite/innodb/t/read_only_recovery.test @@ -0,0 +1,36 @@ +--source include/have_innodb.inc +# need to restart server +--source include/not_embedded.inc + +--connect(con1, localhost, root) +CREATE TABLE t(a INT PRIMARY KEY) ENGINE=InnoDB; +BEGIN; +# Generate insert_undo log. +INSERT INTO t VALUES(1),(2); +# Generate update_undo log. +DELETE FROM t WHERE a=2; +--connection default +--echo # Normal MariaDB shutdown would roll back the above transaction. +--echo # We want the transaction to remain open, so we will kill the server +--echo # after ensuring that any non-transactional files are clean. +FLUSH TABLES; +--echo # Ensure that the above incomplete transaction becomes durable. +SET GLOBAL innodb_flush_log_at_trx_commit=1; +BEGIN; +INSERT INTO t VALUES(0); +ROLLBACK; +--let $restart_parameters= --innodb-force-recovery=3 +--source include/kill_and_restart_mysqld.inc +--disconnect con1 +SELECT * FROM t; +SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; +SELECT * FROM t; +--let $restart_parameters= --innodb-read-only +--source include/restart_mysqld.inc +SELECT * FROM t; +SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; +SELECT * FROM t; +--let $restart_parameters= +--source include/restart_mysqld.inc +SELECT * FROM t; +DROP TABLE t; diff --git a/mysql-test/suite/innodb/t/xa_recovery.test b/mysql-test/suite/innodb/t/xa_recovery.test index 2c1034f3c4d..f5c2b655545 100644 --- a/mysql-test/suite/innodb/t/xa_recovery.test +++ b/mysql-test/suite/innodb/t/xa_recovery.test @@ -1,7 +1,3 @@ -if (`select plugin_auth_version <= "5.6.24" from information_schema.plugins where plugin_name='innodb'`) -{ - --skip Not fixed in InnoDB as of 5.6.24 or earlier -} --source include/have_innodb.inc # Embedded server does not support restarting. --source include/not_embedded.inc @@ -9,6 +5,7 @@ if (`select plugin_auth_version <= "5.6.24" from information_schema.plugins wher # MDEV-8841 - close tables opened by previous tests, # so they don't get marked crashed when the server gets crashed --disable_query_log +call mtr.add_suppression("Found 1 prepared XA transactions"); FLUSH TABLES; --enable_query_log @@ -18,17 +15,7 @@ connect (con1,localhost,root); XA START 'x'; UPDATE t1 set a=2; XA END 'x'; XA PREPARE 'x'; connection default; -call mtr.add_suppression("Found 1 prepared XA transactions"); - -# Kill and restart the server. --- exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect --- shutdown_server 0 --- source include/wait_until_disconnected.inc - --- exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect --- enable_reconnect --- source include/wait_until_connected_again.inc --- disable_reconnect +--source include/kill_and_restart_mysqld.inc disconnect con1; connect (con1,localhost,root); diff --git a/mysql-test/suite/innodb_fts/r/create.result b/mysql-test/suite/innodb_fts/r/create.result new file mode 100644 index 00000000000..c537aa81efd --- /dev/null +++ b/mysql-test/suite/innodb_fts/r/create.result @@ -0,0 +1,168 @@ +SET NAMES utf8mb4; +# +# MDEV-11233 CREATE FULLTEXT INDEX with a token +# longer than 127 bytes crashes server +# +CREATE TABLE t(t TEXT CHARACTER SET utf8mb3) ENGINE=InnoDB; +INSERT INTO t SET t=REPEAT(CONCAT(REPEAT(_utf8mb3 0xE0B987, 4), REPEAT(_utf8mb3 0xE0B989, 5)), 5); +INSERT INTO t SET t=REPEAT(_utf8 0xefbc90,84); +INSERT INTO t SET t=REPEAT('befor',17); +INSERT INTO t SET t='BeforeTheIndexCreation'; +CREATE FULLTEXT INDEX ft ON t(t); +Warnings: +Warning 124 InnoDB rebuilding table to add column FTS_DOC_ID +INSERT INTO t SET t='this was inserted after creating the index'; +INSERT INTO t SET t=REPEAT(_utf8 0xefbc91,84); +INSERT INTO t SET t=REPEAT('after',17); +INSERT INTO t SET t=REPEAT(_utf8mb3 0xe794b2e9aaa8e69687, 15); +# The data below is not 3-byte UTF-8, but 4-byte chars. +INSERT INTO t SET t=REPEAT(_utf8mb4 0xf09f9695, 84); +Warnings: +Warning 1366 Incorrect string value: '\xF0\x9F\x96\x95\xF0\x9F...' for column 't' at row 1 +INSERT INTO t SET t=REPEAT(_utf8mb4 0xf09f9696, 85); +Warnings: +Warning 1366 Incorrect string value: '\xF0\x9F\x96\x96\xF0\x9F...' for column 't' at row 1 +SELECT COUNT(*) FROM t WHERE MATCH t AGAINST +(REPEAT(CONCAT(REPEAT(_utf8mb3 0xE0B987, 4), REPEAT(_utf8mb3 0xE0B989, 5)), 5)); +COUNT(*) +1 +SELECT COUNT(*) FROM t WHERE MATCH t AGAINST ('BeforeTheIndexCreation'); +COUNT(*) +1 +SELECT COUNT(*) FROM t WHERE MATCH t AGAINST (REPEAT('befor',17)); +COUNT(*) +0 +SELECT COUNT(*) FROM t WHERE MATCH t AGAINST ('after'); +COUNT(*) +1 +SELECT COUNT(*) FROM t WHERE MATCH t AGAINST (REPEAT('after',17)); +COUNT(*) +0 +SELECT COUNT(*) FROM t WHERE MATCH t AGAINST (REPEAT(_utf8 0xefbc90, 83)); +COUNT(*) +0 +SELECT COUNT(*) FROM t WHERE MATCH t AGAINST (REPEAT(_utf8 0xefbc90, 84)); +COUNT(*) +1 +SELECT COUNT(*) FROM t WHERE MATCH t AGAINST (REPEAT(_utf8 0xefbc90, 85)); +COUNT(*) +0 +SELECT COUNT(*) FROM t WHERE MATCH t AGAINST (REPEAT(_utf8 0xefbc91, 83)); +COUNT(*) +0 +SELECT COUNT(*) FROM t WHERE MATCH t AGAINST (REPEAT(_utf8 0xefbc91, 84)); +COUNT(*) +1 +SELECT COUNT(*) FROM t WHERE MATCH t AGAINST (REPEAT(_utf8 0xefbc91, 85)); +COUNT(*) +0 +SELECT COUNT(*) FROM t WHERE MATCH t AGAINST (REPEAT(_utf8mb4 0xf09f9695, 83)); +COUNT(*) +0 +SELECT COUNT(*) FROM t WHERE MATCH t AGAINST (REPEAT(_utf8mb4 0xf09f9695, 84)); +COUNT(*) +0 +SELECT COUNT(*) FROM t WHERE MATCH t AGAINST (REPEAT(_utf8mb4 0xf09f9696, 84)); +COUNT(*) +0 +SELECT COUNT(*) FROM t WHERE MATCH t AGAINST (REPEAT(_utf8mb4 0xf09f9696, 85)); +COUNT(*) +0 +SELECT * FROM t; +t +็็็็้้้้้็็็็้้้้้็็็็้้้้้็็็็้้้้้็็็็้้้้้ +ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ +beforbeforbeforbeforbeforbeforbeforbeforbeforbeforbeforbeforbeforbeforbeforbeforbefor +BeforeTheIndexCreation +this was inserted after creating the index +111111111111111111111111111111111111111111111111111111111111111111111111111111111111 +afterafterafterafterafterafterafterafterafterafterafterafterafterafterafterafterafter +甲骨文甲骨文甲骨文甲骨文甲骨文甲骨文甲骨文甲骨文甲骨文甲骨文甲骨文甲骨文甲骨文甲骨文甲骨文 +???????????????????????????????????????????????????????????????????????????????????? +????????????????????????????????????????????????????????????????????????????????????? +SELECT len,COUNT(*) FROM INFORMATION_SCHEMA.INNODB_SYS_COLUMNS where name='word' GROUP BY len; +len COUNT(*) +252 6 +DROP TABLE t; +CREATE TABLE t(t TEXT CHARACTER SET utf8mb4) ENGINE=InnoDB; +INSERT INTO t SET t=REPEAT(_utf8mb3 0xe794b2e9aaa8e69687, 15); +INSERT INTO t SET t=REPEAT(_utf8 0xefbc90,84); +INSERT INTO t SET t=REPEAT('befor',17); +INSERT INTO t SET t='BeforeTheIndexCreation'; +CREATE FULLTEXT INDEX ft ON t(t); +Warnings: +Warning 124 InnoDB rebuilding table to add column FTS_DOC_ID +INSERT INTO t SET t='this was inserted after creating the index'; +INSERT INTO t SET t=REPEAT(_utf8 0xefbc91,84); +INSERT INTO t SET t=REPEAT('after',17); +INSERT INTO t SET t=REPEAT(concat(repeat(_utf8mb3 0xE0B987, 4), repeat(_utf8mb3 0xE0B989, 5)), 5); +INSERT INTO t SET t=REPEAT(_utf8mb4 0xf09f9695, 84); +# The token below exceeds the 84-character limit. +INSERT INTO t SET t=REPEAT(_utf8mb4 0xf09f9696, 85); +SELECT COUNT(*) FROM t WHERE MATCH t AGAINST (REPEAT(_utf8mb3 0xe794b2e9aaa8e69687, 15)); +COUNT(*) +1 +SELECT COUNT(*) FROM t WHERE MATCH t AGAINST ('BeforeTheIndexCreation'); +COUNT(*) +1 +SELECT COUNT(*) FROM t WHERE MATCH t AGAINST (REPEAT('befor',17)); +COUNT(*) +0 +SELECT COUNT(*) FROM t WHERE MATCH t AGAINST ('after'); +COUNT(*) +1 +SELECT COUNT(*) FROM t WHERE MATCH t AGAINST (REPEAT('after',17)); +COUNT(*) +0 +SELECT COUNT(*) FROM t WHERE MATCH t AGAINST (REPEAT(_utf8 0xefbc90, 83)); +COUNT(*) +0 +SELECT COUNT(*) FROM t WHERE MATCH t AGAINST (REPEAT(_utf8 0xefbc90, 84)); +COUNT(*) +1 +SELECT COUNT(*) FROM t WHERE MATCH t AGAINST (REPEAT(_utf8 0xefbc90, 85)); +COUNT(*) +0 +SELECT COUNT(*) FROM t WHERE MATCH t AGAINST (REPEAT(_utf8 0xefbc91, 83)); +COUNT(*) +0 +SELECT COUNT(*) FROM t WHERE MATCH t AGAINST (REPEAT(_utf8 0xefbc91, 84)); +COUNT(*) +1 +SELECT COUNT(*) FROM t WHERE MATCH t AGAINST (REPEAT(_utf8 0xefbc91, 85)); +COUNT(*) +0 +SELECT COUNT(*) FROM t WHERE MATCH t AGAINST (REPEAT(_utf8mb4 0xf09f9695, 83)); +COUNT(*) +0 +SELECT COUNT(*) FROM t WHERE MATCH t AGAINST (REPEAT(_utf8mb4 0xf09f9695, 84)); +COUNT(*) +0 +SELECT COUNT(*) FROM t WHERE MATCH t AGAINST (REPEAT(_utf8mb4 0xf09f9696, 84)); +COUNT(*) +0 +SELECT COUNT(*) FROM t WHERE MATCH t AGAINST (REPEAT(_utf8mb4 0xf09f9696, 85)); +COUNT(*) +0 +SELECT * FROM t; +t +甲骨文甲骨文甲骨文甲骨文甲骨文甲骨文甲骨文甲骨文甲骨文甲骨文甲骨文甲骨文甲骨文甲骨文甲骨文 +ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ï¼ +beforbeforbeforbeforbeforbeforbeforbeforbeforbeforbeforbeforbeforbeforbeforbeforbefor +BeforeTheIndexCreation +this was inserted after creating the index +111111111111111111111111111111111111111111111111111111111111111111111111111111111111 +afterafterafterafterafterafterafterafterafterafterafterafterafterafterafterafterafter +็็็็้้้้้็็็็้้้้้็็็็้้้้้็็็็้้้้้็็็็้้้้้ +🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕🖕 +🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖🖖 +SELECT len,COUNT(*) FROM INFORMATION_SCHEMA.INNODB_SYS_COLUMNS where name='word' GROUP BY len; +len COUNT(*) +336 6 +DROP TABLE t; +CREATE TABLE t(t TEXT CHARACTER SET latin1, FULLTEXT INDEX(t)) +ENGINE=InnoDB; +SELECT len,COUNT(*) FROM INFORMATION_SCHEMA.INNODB_SYS_COLUMNS where name='word' GROUP BY len; +len COUNT(*) +84 6 +DROP TABLE t; diff --git a/mysql-test/suite/innodb_fts/t/create.opt b/mysql-test/suite/innodb_fts/t/create.opt new file mode 100644 index 00000000000..3ad568c816e --- /dev/null +++ b/mysql-test/suite/innodb_fts/t/create.opt @@ -0,0 +1 @@ +--loose-innodb-sys-columns diff --git a/mysql-test/suite/innodb_fts/t/create.test b/mysql-test/suite/innodb_fts/t/create.test new file mode 100644 index 00000000000..f0329602ed1 --- /dev/null +++ b/mysql-test/suite/innodb_fts/t/create.test @@ -0,0 +1,92 @@ +--source include/have_innodb.inc +SET NAMES utf8mb4; + +--echo # +--echo # MDEV-11233 CREATE FULLTEXT INDEX with a token +--echo # longer than 127 bytes crashes server +--echo # + +# This bug is the result of merging the Oracle MySQL follow-up fix +# BUG#22963169 MYSQL CRASHES ON CREATE FULLTEXT INDEX +# without merging a fix of Bug#79475 Insert a token of 84 4-bytes +# chars into fts index causes server crash. + +# Oracle did not publish tests for either of the above MySQL bugs. +# The tests below were developed for MariaDB Server. +# The maximum length of a fulltext-indexed word is 84 characters. + +CREATE TABLE t(t TEXT CHARACTER SET utf8mb3) ENGINE=InnoDB; +INSERT INTO t SET t=REPEAT(CONCAT(REPEAT(_utf8mb3 0xE0B987, 4), REPEAT(_utf8mb3 0xE0B989, 5)), 5); +INSERT INTO t SET t=REPEAT(_utf8 0xefbc90,84); +INSERT INTO t SET t=REPEAT('befor',17); # too long, will not be indexed +INSERT INTO t SET t='BeforeTheIndexCreation'; +CREATE FULLTEXT INDEX ft ON t(t); +INSERT INTO t SET t='this was inserted after creating the index'; +INSERT INTO t SET t=REPEAT(_utf8 0xefbc91,84); +INSERT INTO t SET t=REPEAT('after',17); # too long, will not be indexed +INSERT INTO t SET t=REPEAT(_utf8mb3 0xe794b2e9aaa8e69687, 15); +--echo # The data below is not 3-byte UTF-8, but 4-byte chars. +INSERT INTO t SET t=REPEAT(_utf8mb4 0xf09f9695, 84); +INSERT INTO t SET t=REPEAT(_utf8mb4 0xf09f9696, 85); +SELECT COUNT(*) FROM t WHERE MATCH t AGAINST +(REPEAT(CONCAT(REPEAT(_utf8mb3 0xE0B987, 4), REPEAT(_utf8mb3 0xE0B989, 5)), 5)); +SELECT COUNT(*) FROM t WHERE MATCH t AGAINST ('BeforeTheIndexCreation'); +SELECT COUNT(*) FROM t WHERE MATCH t AGAINST (REPEAT('befor',17)); +SELECT COUNT(*) FROM t WHERE MATCH t AGAINST ('after'); +SELECT COUNT(*) FROM t WHERE MATCH t AGAINST (REPEAT('after',17)); +SELECT COUNT(*) FROM t WHERE MATCH t AGAINST (REPEAT(_utf8 0xefbc90, 83)); +SELECT COUNT(*) FROM t WHERE MATCH t AGAINST (REPEAT(_utf8 0xefbc90, 84)); +SELECT COUNT(*) FROM t WHERE MATCH t AGAINST (REPEAT(_utf8 0xefbc90, 85)); +SELECT COUNT(*) FROM t WHERE MATCH t AGAINST (REPEAT(_utf8 0xefbc91, 83)); +SELECT COUNT(*) FROM t WHERE MATCH t AGAINST (REPEAT(_utf8 0xefbc91, 84)); +SELECT COUNT(*) FROM t WHERE MATCH t AGAINST (REPEAT(_utf8 0xefbc91, 85)); +SELECT COUNT(*) FROM t WHERE MATCH t AGAINST (REPEAT(_utf8mb4 0xf09f9695, 83)); +SELECT COUNT(*) FROM t WHERE MATCH t AGAINST (REPEAT(_utf8mb4 0xf09f9695, 84)); +SELECT COUNT(*) FROM t WHERE MATCH t AGAINST (REPEAT(_utf8mb4 0xf09f9696, 84)); +SELECT COUNT(*) FROM t WHERE MATCH t AGAINST (REPEAT(_utf8mb4 0xf09f9696, 85)); +SELECT * FROM t; + +# The column length should be 252 bytes (84 characters * 3 bytes/character). +SELECT len,COUNT(*) FROM INFORMATION_SCHEMA.INNODB_SYS_COLUMNS where name='word' GROUP BY len; +DROP TABLE t; + +CREATE TABLE t(t TEXT CHARACTER SET utf8mb4) ENGINE=InnoDB; +INSERT INTO t SET t=REPEAT(_utf8mb3 0xe794b2e9aaa8e69687, 15); +INSERT INTO t SET t=REPEAT(_utf8 0xefbc90,84); +INSERT INTO t SET t=REPEAT('befor',17); # too long, will not be indexed +INSERT INTO t SET t='BeforeTheIndexCreation'; +CREATE FULLTEXT INDEX ft ON t(t); +INSERT INTO t SET t='this was inserted after creating the index'; +INSERT INTO t SET t=REPEAT(_utf8 0xefbc91,84); +INSERT INTO t SET t=REPEAT('after',17); # too long, will not be indexed +INSERT INTO t SET t=REPEAT(concat(repeat(_utf8mb3 0xE0B987, 4), repeat(_utf8mb3 0xE0B989, 5)), 5); +INSERT INTO t SET t=REPEAT(_utf8mb4 0xf09f9695, 84); +--echo # The token below exceeds the 84-character limit. +INSERT INTO t SET t=REPEAT(_utf8mb4 0xf09f9696, 85); +SELECT COUNT(*) FROM t WHERE MATCH t AGAINST (REPEAT(_utf8mb3 0xe794b2e9aaa8e69687, 15)); +SELECT COUNT(*) FROM t WHERE MATCH t AGAINST ('BeforeTheIndexCreation'); +SELECT COUNT(*) FROM t WHERE MATCH t AGAINST (REPEAT('befor',17)); +SELECT COUNT(*) FROM t WHERE MATCH t AGAINST ('after'); +SELECT COUNT(*) FROM t WHERE MATCH t AGAINST (REPEAT('after',17)); +SELECT COUNT(*) FROM t WHERE MATCH t AGAINST (REPEAT(_utf8 0xefbc90, 83)); +SELECT COUNT(*) FROM t WHERE MATCH t AGAINST (REPEAT(_utf8 0xefbc90, 84)); +SELECT COUNT(*) FROM t WHERE MATCH t AGAINST (REPEAT(_utf8 0xefbc90, 85)); +SELECT COUNT(*) FROM t WHERE MATCH t AGAINST (REPEAT(_utf8 0xefbc91, 83)); +SELECT COUNT(*) FROM t WHERE MATCH t AGAINST (REPEAT(_utf8 0xefbc91, 84)); +SELECT COUNT(*) FROM t WHERE MATCH t AGAINST (REPEAT(_utf8 0xefbc91, 85)); +SELECT COUNT(*) FROM t WHERE MATCH t AGAINST (REPEAT(_utf8mb4 0xf09f9695, 83)); +SELECT COUNT(*) FROM t WHERE MATCH t AGAINST (REPEAT(_utf8mb4 0xf09f9695, 84)); +SELECT COUNT(*) FROM t WHERE MATCH t AGAINST (REPEAT(_utf8mb4 0xf09f9696, 84)); +SELECT COUNT(*) FROM t WHERE MATCH t AGAINST (REPEAT(_utf8mb4 0xf09f9696, 85)); +SELECT * FROM t; + +# The column length should be 336 bytes (84 characters * 4 bytes/character). +SELECT len,COUNT(*) FROM INFORMATION_SCHEMA.INNODB_SYS_COLUMNS where name='word' GROUP BY len; +DROP TABLE t; + +CREATE TABLE t(t TEXT CHARACTER SET latin1, FULLTEXT INDEX(t)) +ENGINE=InnoDB; + +# The column length should be 84 bytes (84 characters * 1 byte/character). +SELECT len,COUNT(*) FROM INFORMATION_SCHEMA.INNODB_SYS_COLUMNS where name='word' GROUP BY len; +DROP TABLE t; diff --git a/mysql-test/suite/parts/r/partition_bigint_innodb.result b/mysql-test/suite/parts/r/partition_bigint_innodb.result new file mode 100644 index 00000000000..bb0f08d9356 --- /dev/null +++ b/mysql-test/suite/parts/r/partition_bigint_innodb.result @@ -0,0 +1,121 @@ +create table t1 (a bigint unsigned not null, primary key(a)) engine='InnoDB' +partition by key (a) ( +partition pa1 max_rows=20 min_rows=2, +partition pa2 max_rows=30 min_rows=3, +partition pa3 max_rows=30 min_rows=4, +partition pa4 max_rows=40 min_rows=2); +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` bigint(20) unsigned NOT NULL, + PRIMARY KEY (`a`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +/*!50100 PARTITION BY KEY (a) +(PARTITION pa1 MAX_ROWS = 20 MIN_ROWS = 2 ENGINE = InnoDB, + PARTITION pa2 MAX_ROWS = 30 MIN_ROWS = 3 ENGINE = InnoDB, + PARTITION pa3 MAX_ROWS = 30 MIN_ROWS = 4 ENGINE = InnoDB, + PARTITION pa4 MAX_ROWS = 40 MIN_ROWS = 2 ENGINE = InnoDB) */ +insert into t1 values (18446744073709551615), (0xFFFFFFFFFFFFFFFE), (18446744073709551613), (18446744073709551612), (1), (2), (65535); +select * from t1; +a +1 +18446744073709551612 +18446744073709551613 +18446744073709551614 +18446744073709551615 +2 +65535 +select * from t1 where a=-2; +a +delete from t1 where a=-2; +select * from t1; +a +1 +18446744073709551612 +18446744073709551613 +18446744073709551614 +18446744073709551615 +2 +65535 +select * from t1 where a=18446744073709551615; +a +18446744073709551615 +delete from t1 where a=18446744073709551615; +select * from t1; +a +1 +18446744073709551612 +18446744073709551613 +18446744073709551614 +2 +65535 +drop table t1; +create table t2 (a bigint unsigned not null, primary key(a)) engine='InnoDB' +partition by key (a) partitions 8; +show create table t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `a` bigint(20) unsigned NOT NULL, + PRIMARY KEY (`a`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +/*!50100 PARTITION BY KEY (a) +PARTITIONS 8 */ +insert into t2 values (18446744073709551615), (0xFFFFFFFFFFFFFFFE), (18446744073709551613), (18446744073709551612); +select * from t2; +a +18446744073709551612 +18446744073709551613 +18446744073709551614 +18446744073709551615 +select * from t2 where a=18446744073709551615; +a +18446744073709551615 +delete from t2 where a=18446744073709551615; +select * from t2; +a +18446744073709551612 +18446744073709551613 +18446744073709551614 +delete from t2; +1024 inserts; +select count(*) from t2; +count(*) +1024 +drop table t2; +create table t3 (a bigint not null, primary key(a)) engine='InnoDB' +partition by key (a) partitions 7; +show create table t3; +Table Create Table +t3 CREATE TABLE `t3` ( + `a` bigint(20) NOT NULL, + PRIMARY KEY (`a`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +/*!50100 PARTITION BY KEY (a) +PARTITIONS 7 */ +insert into t3 values (9223372036854775807), (9223372036854775806), (9223372036854775805), (9223372036854775804), (-9223372036854775808), (-9223372036854775807), (1), (-1), (0); +select * from t3; +a +-1 +-9223372036854775807 +-9223372036854775808 +0 +1 +9223372036854775804 +9223372036854775805 +9223372036854775806 +9223372036854775807 +select * from t3 where a=9223372036854775806; +a +9223372036854775806 +delete from t3 where a=9223372036854775806; +select * from t3; +a +-1 +-9223372036854775807 +-9223372036854775808 +0 +1 +9223372036854775804 +9223372036854775805 +9223372036854775807 +drop table t3; diff --git a/mysql-test/suite/parts/r/partition_bigint_myisam.result b/mysql-test/suite/parts/r/partition_bigint_myisam.result new file mode 100644 index 00000000000..5938bcaf7d8 --- /dev/null +++ b/mysql-test/suite/parts/r/partition_bigint_myisam.result @@ -0,0 +1,121 @@ +create table t1 (a bigint unsigned not null, primary key(a)) engine='MYISAM' +partition by key (a) ( +partition pa1 max_rows=20 min_rows=2, +partition pa2 max_rows=30 min_rows=3, +partition pa3 max_rows=30 min_rows=4, +partition pa4 max_rows=40 min_rows=2); +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` bigint(20) unsigned NOT NULL, + PRIMARY KEY (`a`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +/*!50100 PARTITION BY KEY (a) +(PARTITION pa1 MAX_ROWS = 20 MIN_ROWS = 2 ENGINE = MyISAM, + PARTITION pa2 MAX_ROWS = 30 MIN_ROWS = 3 ENGINE = MyISAM, + PARTITION pa3 MAX_ROWS = 30 MIN_ROWS = 4 ENGINE = MyISAM, + PARTITION pa4 MAX_ROWS = 40 MIN_ROWS = 2 ENGINE = MyISAM) */ +insert into t1 values (18446744073709551615), (0xFFFFFFFFFFFFFFFE), (18446744073709551613), (18446744073709551612), (1), (2), (65535); +select * from t1; +a +1 +18446744073709551612 +18446744073709551613 +18446744073709551614 +18446744073709551615 +2 +65535 +select * from t1 where a=-2; +a +delete from t1 where a=-2; +select * from t1; +a +1 +18446744073709551612 +18446744073709551613 +18446744073709551614 +18446744073709551615 +2 +65535 +select * from t1 where a=18446744073709551615; +a +18446744073709551615 +delete from t1 where a=18446744073709551615; +select * from t1; +a +1 +18446744073709551612 +18446744073709551613 +18446744073709551614 +2 +65535 +drop table t1; +create table t2 (a bigint unsigned not null, primary key(a)) engine='MYISAM' +partition by key (a) partitions 8; +show create table t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `a` bigint(20) unsigned NOT NULL, + PRIMARY KEY (`a`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +/*!50100 PARTITION BY KEY (a) +PARTITIONS 8 */ +insert into t2 values (18446744073709551615), (0xFFFFFFFFFFFFFFFE), (18446744073709551613), (18446744073709551612); +select * from t2; +a +18446744073709551612 +18446744073709551613 +18446744073709551614 +18446744073709551615 +select * from t2 where a=18446744073709551615; +a +18446744073709551615 +delete from t2 where a=18446744073709551615; +select * from t2; +a +18446744073709551612 +18446744073709551613 +18446744073709551614 +delete from t2; +65535 inserts; +select count(*) from t2; +count(*) +65535 +drop table t2; +create table t3 (a bigint not null, primary key(a)) engine='MYISAM' +partition by key (a) partitions 7; +show create table t3; +Table Create Table +t3 CREATE TABLE `t3` ( + `a` bigint(20) NOT NULL, + PRIMARY KEY (`a`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +/*!50100 PARTITION BY KEY (a) +PARTITIONS 7 */ +insert into t3 values (9223372036854775807), (9223372036854775806), (9223372036854775805), (9223372036854775804), (-9223372036854775808), (-9223372036854775807), (1), (-1), (0); +select * from t3; +a +-1 +-9223372036854775807 +-9223372036854775808 +0 +1 +9223372036854775804 +9223372036854775805 +9223372036854775806 +9223372036854775807 +select * from t3 where a=9223372036854775806; +a +9223372036854775806 +delete from t3 where a=9223372036854775806; +select * from t3; +a +-1 +-9223372036854775807 +-9223372036854775808 +0 +1 +9223372036854775804 +9223372036854775805 +9223372036854775807 +drop table t3; diff --git a/mysql-test/suite/parts/r/partition_double_innodb.result b/mysql-test/suite/parts/r/partition_double_innodb.result new file mode 100644 index 00000000000..8c0daf929dd --- /dev/null +++ b/mysql-test/suite/parts/r/partition_double_innodb.result @@ -0,0 +1,82 @@ +create table t1 (a double not null, primary key(a)) engine='InnoDB' +partition by key (a) ( +partition pa1 max_rows=20 min_rows=2, +partition pa2 max_rows=30 min_rows=3, +partition pa3 max_rows=30 min_rows=4, +partition pa4 max_rows=40 min_rows=2); +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` double NOT NULL, + PRIMARY KEY (`a`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +/*!50100 PARTITION BY KEY (a) +(PARTITION pa1 MAX_ROWS = 20 MIN_ROWS = 2 ENGINE = InnoDB, + PARTITION pa2 MAX_ROWS = 30 MIN_ROWS = 3 ENGINE = InnoDB, + PARTITION pa3 MAX_ROWS = 30 MIN_ROWS = 4 ENGINE = InnoDB, + PARTITION pa4 MAX_ROWS = 40 MIN_ROWS = 2 ENGINE = InnoDB) */ +insert into t1 values (-2.2250738585072014E+208), (-2.2250738585072014E-208), (-1.5), (-1), (0), (1.5), (1234.567), (2.2250738585072014E+208); +select * from t1; +a +-2.2250738585072016e208 +-1.5 +-1 +-2.2250738585072014e-208 +0 +1.5 +1234.567 +2.2250738585072016e208 +select * from t1 where a=1.5; +a +1.5 +delete from t1 where a=1.5; +select * from t1; +a +-2.2250738585072016e208 +-1.5 +-1 +-2.2250738585072014e-208 +0 +1234.567 +2.2250738585072016e208 +drop table t1; +create table t2 (a double not null, primary key(a)) engine='InnoDB' +partition by key (a) partitions 10; +show create table t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `a` double NOT NULL, + PRIMARY KEY (`a`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +/*!50100 PARTITION BY KEY (a) +PARTITIONS 10 */ +insert into t2 values (-2.2250738585072014E+208), (-2.2250738585072014E-208), (-1.5), (-1), (0), (1.5), (1234.567), (2.2250738585072014E+208); +select * from t2; +a +-2.2250738585072016e208 +-1.5 +-1 +-2.2250738585072014e-208 +0 +1.5 +1234.567 +2.2250738585072016e208 +select * from t2 where a=1234.567; +a +1234.567 +delete from t2 where a=1234.567; +select * from t2; +a +-2.2250738585072016e208 +-1.5 +-1 +-2.2250738585072014e-208 +0 +1.5 +2.2250738585072016e208 +delete from t2; +1024*3 inserts; +select count(*) from t2; +count(*) +3072 +drop table t2; diff --git a/mysql-test/suite/parts/r/partition_double_myisam.result b/mysql-test/suite/parts/r/partition_double_myisam.result new file mode 100644 index 00000000000..045763e5ef9 --- /dev/null +++ b/mysql-test/suite/parts/r/partition_double_myisam.result @@ -0,0 +1,82 @@ +create table t1 (a double not null, primary key(a)) engine='MYISAM' +partition by key (a) ( +partition pa1 max_rows=20 min_rows=2, +partition pa2 max_rows=30 min_rows=3, +partition pa3 max_rows=30 min_rows=4, +partition pa4 max_rows=40 min_rows=2); +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` double NOT NULL, + PRIMARY KEY (`a`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +/*!50100 PARTITION BY KEY (a) +(PARTITION pa1 MAX_ROWS = 20 MIN_ROWS = 2 ENGINE = MyISAM, + PARTITION pa2 MAX_ROWS = 30 MIN_ROWS = 3 ENGINE = MyISAM, + PARTITION pa3 MAX_ROWS = 30 MIN_ROWS = 4 ENGINE = MyISAM, + PARTITION pa4 MAX_ROWS = 40 MIN_ROWS = 2 ENGINE = MyISAM) */ +insert into t1 values (-2.2250738585072014E+208), (-2.2250738585072014E-208), (-1.5), (-1), (0), (1.5), (1234.567), (2.2250738585072014E+208); +select * from t1; +a +-2.2250738585072016e208 +-1.5 +-1 +-2.2250738585072014e-208 +0 +1.5 +1234.567 +2.2250738585072016e208 +select * from t1 where a=1.5; +a +1.5 +delete from t1 where a=1.5; +select * from t1; +a +-2.2250738585072016e208 +-1.5 +-1 +-2.2250738585072014e-208 +0 +1234.567 +2.2250738585072016e208 +drop table t1; +create table t2 (a double not null, primary key(a)) engine='MYISAM' +partition by key (a) partitions 10; +show create table t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `a` double NOT NULL, + PRIMARY KEY (`a`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +/*!50100 PARTITION BY KEY (a) +PARTITIONS 10 */ +insert into t2 values (-2.2250738585072014E+208), (-2.2250738585072014E-208), (-1.5), (-1), (0), (1.5), (1234.567), (2.2250738585072014E+208); +select * from t2; +a +-2.2250738585072016e208 +-1.5 +-1 +-2.2250738585072014e-208 +0 +1.5 +1234.567 +2.2250738585072016e208 +select * from t2 where a=1234.567; +a +1234.567 +delete from t2 where a=1234.567; +select * from t2; +a +-2.2250738585072016e208 +-1.5 +-1 +-2.2250738585072014e-208 +0 +1.5 +2.2250738585072016e208 +delete from t2; +16384*3 inserts; +select count(*) from t2; +count(*) +49152 +drop table t2; diff --git a/mysql-test/suite/parts/r/partition_float_innodb.result b/mysql-test/suite/parts/r/partition_float_innodb.result index d2f04a68629..b0870992c41 100644 --- a/mysql-test/suite/parts/r/partition_float_innodb.result +++ b/mysql-test/suite/parts/r/partition_float_innodb.result @@ -88,85 +88,3 @@ select count(*) from t2; count(*) 3072 drop table t2; -create table t1 (a double not null, primary key(a)) engine='InnoDB' -partition by key (a) ( -partition pa1 max_rows=20 min_rows=2, -partition pa2 max_rows=30 min_rows=3, -partition pa3 max_rows=30 min_rows=4, -partition pa4 max_rows=40 min_rows=2); -show create table t1; -Table Create Table -t1 CREATE TABLE `t1` ( - `a` double NOT NULL, - PRIMARY KEY (`a`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1 -/*!50100 PARTITION BY KEY (a) -(PARTITION pa1 MAX_ROWS = 20 MIN_ROWS = 2 ENGINE = InnoDB, - PARTITION pa2 MAX_ROWS = 30 MIN_ROWS = 3 ENGINE = InnoDB, - PARTITION pa3 MAX_ROWS = 30 MIN_ROWS = 4 ENGINE = InnoDB, - PARTITION pa4 MAX_ROWS = 40 MIN_ROWS = 2 ENGINE = InnoDB) */ -insert into t1 values (-2.2250738585072014E+208), (-2.2250738585072014E-208), (-1.5), (-1), (0), (1.5), (1234.567), (2.2250738585072014E+208); -select * from t1; -a --2.2250738585072016e208 --1.5 --1 --2.2250738585072014e-208 -0 -1.5 -1234.567 -2.2250738585072016e208 -select * from t1 where a=1.5; -a -1.5 -delete from t1 where a=1.5; -select * from t1; -a --2.2250738585072016e208 --1.5 --1 --2.2250738585072014e-208 -0 -1234.567 -2.2250738585072016e208 -drop table t1; -create table t2 (a double not null, primary key(a)) engine='InnoDB' -partition by key (a) partitions 10; -show create table t2; -Table Create Table -t2 CREATE TABLE `t2` ( - `a` double NOT NULL, - PRIMARY KEY (`a`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1 -/*!50100 PARTITION BY KEY (a) -PARTITIONS 10 */ -insert into t2 values (-2.2250738585072014E+208), (-2.2250738585072014E-208), (-1.5), (-1), (0), (1.5), (1234.567), (2.2250738585072014E+208); -select * from t2; -a --2.2250738585072016e208 --1.5 --1 --2.2250738585072014e-208 -0 -1.5 -1234.567 -2.2250738585072016e208 -select * from t2 where a=1234.567; -a -1234.567 -delete from t2 where a=1234.567; -select * from t2; -a --2.2250738585072016e208 --1.5 --1 --2.2250738585072014e-208 -0 -1.5 -2.2250738585072016e208 -delete from t2; -1024*3 inserts; -select count(*) from t2; -count(*) -3072 -drop table t2; diff --git a/mysql-test/suite/parts/r/partition_float_myisam.result b/mysql-test/suite/parts/r/partition_float_myisam.result index 2d52d095989..931c4ef0394 100644 --- a/mysql-test/suite/parts/r/partition_float_myisam.result +++ b/mysql-test/suite/parts/r/partition_float_myisam.result @@ -88,85 +88,3 @@ select count(*) from t2; count(*) 49152 drop table t2; -create table t1 (a double not null, primary key(a)) engine='MYISAM' -partition by key (a) ( -partition pa1 max_rows=20 min_rows=2, -partition pa2 max_rows=30 min_rows=3, -partition pa3 max_rows=30 min_rows=4, -partition pa4 max_rows=40 min_rows=2); -show create table t1; -Table Create Table -t1 CREATE TABLE `t1` ( - `a` double NOT NULL, - PRIMARY KEY (`a`) -) ENGINE=MyISAM DEFAULT CHARSET=latin1 -/*!50100 PARTITION BY KEY (a) -(PARTITION pa1 MAX_ROWS = 20 MIN_ROWS = 2 ENGINE = MyISAM, - PARTITION pa2 MAX_ROWS = 30 MIN_ROWS = 3 ENGINE = MyISAM, - PARTITION pa3 MAX_ROWS = 30 MIN_ROWS = 4 ENGINE = MyISAM, - PARTITION pa4 MAX_ROWS = 40 MIN_ROWS = 2 ENGINE = MyISAM) */ -insert into t1 values (-2.2250738585072014E+208), (-2.2250738585072014E-208), (-1.5), (-1), (0), (1.5), (1234.567), (2.2250738585072014E+208); -select * from t1; -a --2.2250738585072016e208 --1.5 --1 --2.2250738585072014e-208 -0 -1.5 -1234.567 -2.2250738585072016e208 -select * from t1 where a=1.5; -a -1.5 -delete from t1 where a=1.5; -select * from t1; -a --2.2250738585072016e208 --1.5 --1 --2.2250738585072014e-208 -0 -1234.567 -2.2250738585072016e208 -drop table t1; -create table t2 (a double not null, primary key(a)) engine='MYISAM' -partition by key (a) partitions 10; -show create table t2; -Table Create Table -t2 CREATE TABLE `t2` ( - `a` double NOT NULL, - PRIMARY KEY (`a`) -) ENGINE=MyISAM DEFAULT CHARSET=latin1 -/*!50100 PARTITION BY KEY (a) -PARTITIONS 10 */ -insert into t2 values (-2.2250738585072014E+208), (-2.2250738585072014E-208), (-1.5), (-1), (0), (1.5), (1234.567), (2.2250738585072014E+208); -select * from t2; -a --2.2250738585072016e208 --1.5 --1 --2.2250738585072014e-208 -0 -1.5 -1234.567 -2.2250738585072016e208 -select * from t2 where a=1234.567; -a -1234.567 -delete from t2 where a=1234.567; -select * from t2; -a --2.2250738585072016e208 --1.5 --1 --2.2250738585072014e-208 -0 -1.5 -2.2250738585072016e208 -delete from t2; -16384*3 inserts; -select count(*) from t2; -count(*) -49152 -drop table t2; diff --git a/mysql-test/suite/parts/r/partition_int_innodb.result b/mysql-test/suite/parts/r/partition_int_innodb.result index 7a51b80d5d7..c1798e5f711 100644 --- a/mysql-test/suite/parts/r/partition_int_innodb.result +++ b/mysql-test/suite/parts/r/partition_int_innodb.result @@ -1,221 +1,3 @@ -create table t1 (a tinyint unsigned not null, primary key(a)) engine='InnoDB' -partition by key (a) ( -partition pa1 max_rows=20 min_rows=2, -partition pa2 max_rows=30 min_rows=3, -partition pa3 max_rows=30 min_rows=4, -partition pa4 max_rows=40 min_rows=2); -show create table t1; -Table Create Table -t1 CREATE TABLE `t1` ( - `a` tinyint(3) unsigned NOT NULL, - PRIMARY KEY (`a`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1 -/*!50100 PARTITION BY KEY (a) -(PARTITION pa1 MAX_ROWS = 20 MIN_ROWS = 2 ENGINE = InnoDB, - PARTITION pa2 MAX_ROWS = 30 MIN_ROWS = 3 ENGINE = InnoDB, - PARTITION pa3 MAX_ROWS = 30 MIN_ROWS = 4 ENGINE = InnoDB, - PARTITION pa4 MAX_ROWS = 40 MIN_ROWS = 2 ENGINE = InnoDB) */ -insert into t1 values (255), (254), (253), (252), (1), (2), (128); -select * from t1; -a -1 -128 -2 -252 -253 -254 -255 -select * from t1 where a=253; -a -253 -delete from t1 where a=253; -select * from t1; -a -1 -128 -2 -252 -254 -255 -drop table t1; -create table t2 (a tinyint unsigned not null, primary key(a)) engine='InnoDB' -partition by key (a) partitions 8; -show create table t2; -Table Create Table -t2 CREATE TABLE `t2` ( - `a` tinyint(3) unsigned NOT NULL, - PRIMARY KEY (`a`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1 -/*!50100 PARTITION BY KEY (a) -PARTITIONS 8 */ -insert into t2 values (255), (254), (253), (252); -select * from t2; -a -252 -253 -254 -255 -select * from t2 where a=253; -a -253 -delete from t2 where a=253; -select * from t2; -a -252 -254 -255 -delete from t2; -255 inserts; -select count(*) from t2; -count(*) -255 -drop table t2; -create table t3 (a tinyint not null, primary key(a)) engine='InnoDB' -partition by key (a) partitions 7; -show create table t3; -Table Create Table -t3 CREATE TABLE `t3` ( - `a` tinyint(4) NOT NULL, - PRIMARY KEY (`a`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1 -/*!50100 PARTITION BY KEY (a) -PARTITIONS 7 */ -insert into t3 values (127), (126), (125), (124), (-128), (-127), (1), (-1), (0); -select * from t3; -a --1 --127 --128 -0 -1 -124 -125 -126 -127 -select * from t3 where a=125; -a -125 -delete from t3 where a=125; -select * from t3; -a --1 --127 --128 -0 -1 -124 -126 -127 -drop table t3; -create table t1 (a smallint unsigned not null, primary key(a)) engine='InnoDB' -partition by key (a) ( -partition pa1 max_rows=20 min_rows=2, -partition pa2 max_rows=30 min_rows=3, -partition pa3 max_rows=30 min_rows=4, -partition pa4 max_rows=40 min_rows=2); -show create table t1; -Table Create Table -t1 CREATE TABLE `t1` ( - `a` smallint(5) unsigned NOT NULL, - PRIMARY KEY (`a`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1 -/*!50100 PARTITION BY KEY (a) -(PARTITION pa1 MAX_ROWS = 20 MIN_ROWS = 2 ENGINE = InnoDB, - PARTITION pa2 MAX_ROWS = 30 MIN_ROWS = 3 ENGINE = InnoDB, - PARTITION pa3 MAX_ROWS = 30 MIN_ROWS = 4 ENGINE = InnoDB, - PARTITION pa4 MAX_ROWS = 40 MIN_ROWS = 2 ENGINE = InnoDB) */ -insert into t1 values (65535), (65534), (65533), (65532), (1), (2), (256); -select * from t1; -a -1 -2 -256 -65532 -65533 -65534 -65535 -select * from t1 where a=65533; -a -65533 -delete from t1 where a=65533; -select * from t1; -a -1 -2 -256 -65532 -65534 -65535 -drop table t1; -create table t2 (a smallint unsigned not null, primary key(a)) engine='InnoDB' -partition by key (a) partitions 8; -show create table t2; -Table Create Table -t2 CREATE TABLE `t2` ( - `a` smallint(5) unsigned NOT NULL, - PRIMARY KEY (`a`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1 -/*!50100 PARTITION BY KEY (a) -PARTITIONS 8 */ -insert into t2 values (65535), (65534), (65533), (65532); -select * from t2; -a -65532 -65533 -65534 -65535 -select * from t2 where a=65533; -a -65533 -delete from t2 where a=65533; -select * from t2; -a -65532 -65534 -65535 -delete from t2; -1024 inserts; -select count(*) from t2; -count(*) -1024 -drop table t2; -create table t3 (a smallint not null, primary key(a)) engine='InnoDB' -partition by key (a) partitions 7; -show create table t3; -Table Create Table -t3 CREATE TABLE `t3` ( - `a` smallint(6) NOT NULL, - PRIMARY KEY (`a`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1 -/*!50100 PARTITION BY KEY (a) -PARTITIONS 7 */ -insert into t3 values (32767), (32766), (32765), (32764), (-32768), (-32767), (1), (-1), (0); -select * from t3; -a --1 --32767 --32768 -0 -1 -32764 -32765 -32766 -32767 -select * from t3 where a=32765; -a -32765 -delete from t3 where a=32765; -select * from t3; -a --1 --32767 --32768 -0 -1 -32764 -32766 -32767 -drop table t3; create table t1 (a int unsigned not null, primary key(a)) engine='InnoDB' partition by key (a) ( partition pa1 max_rows=20 min_rows=2, @@ -325,233 +107,3 @@ a 2147483646 2147483647 drop table t3; -create table t1 (a mediumint unsigned not null, primary key(a)) engine='InnoDB' -partition by key (a) ( -partition pa1 max_rows=20 min_rows=2, -partition pa2 max_rows=30 min_rows=3, -partition pa3 max_rows=30 min_rows=4, -partition pa4 max_rows=40 min_rows=2); -show create table t1; -Table Create Table -t1 CREATE TABLE `t1` ( - `a` mediumint(8) unsigned NOT NULL, - PRIMARY KEY (`a`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1 -/*!50100 PARTITION BY KEY (a) -(PARTITION pa1 MAX_ROWS = 20 MIN_ROWS = 2 ENGINE = InnoDB, - PARTITION pa2 MAX_ROWS = 30 MIN_ROWS = 3 ENGINE = InnoDB, - PARTITION pa3 MAX_ROWS = 30 MIN_ROWS = 4 ENGINE = InnoDB, - PARTITION pa4 MAX_ROWS = 40 MIN_ROWS = 2 ENGINE = InnoDB) */ -insert into t1 values (16777215), (16777214), (16777213), (16777212), (1), (2), (65535); -select * from t1; -a -1 -16777212 -16777213 -16777214 -16777215 -2 -65535 -select * from t1 where a=16777213; -a -16777213 -delete from t1 where a=16777213; -select * from t1; -a -1 -16777212 -16777214 -16777215 -2 -65535 -drop table t1; -create table t2 (a mediumint unsigned not null, primary key(a)) engine='InnoDB' -partition by key (a) partitions 8; -show create table t2; -Table Create Table -t2 CREATE TABLE `t2` ( - `a` mediumint(8) unsigned NOT NULL, - PRIMARY KEY (`a`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1 -/*!50100 PARTITION BY KEY (a) -PARTITIONS 8 */ -insert into t2 values (16777215), (16777214), (16777213), (16777212); -select * from t2; -a -16777212 -16777213 -16777214 -16777215 -select * from t2 where a=16777213; -a -16777213 -delete from t2 where a=16777213; -select * from t2; -a -16777212 -16777214 -16777215 -delete from t2; -1024 inserts; -select count(*) from t2; -count(*) -1024 -drop table t2; -create table t3 (a mediumint not null, primary key(a)) engine='InnoDB' -partition by key (a) partitions 7; -show create table t3; -Table Create Table -t3 CREATE TABLE `t3` ( - `a` mediumint(9) NOT NULL, - PRIMARY KEY (`a`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1 -/*!50100 PARTITION BY KEY (a) -PARTITIONS 7 */ -insert into t3 values (8388607), (8388606), (8388605), (8388604), (-8388608), (-8388607), (1), (-1), (0); -select * from t3; -a --1 --8388607 --8388608 -0 -1 -8388604 -8388605 -8388606 -8388607 -select * from t3 where a=8388605; -a -8388605 -delete from t3 where a=8388605; -select * from t3; -a --1 --8388607 --8388608 -0 -1 -8388604 -8388606 -8388607 -drop table t3; -create table t1 (a bigint unsigned not null, primary key(a)) engine='InnoDB' -partition by key (a) ( -partition pa1 max_rows=20 min_rows=2, -partition pa2 max_rows=30 min_rows=3, -partition pa3 max_rows=30 min_rows=4, -partition pa4 max_rows=40 min_rows=2); -show create table t1; -Table Create Table -t1 CREATE TABLE `t1` ( - `a` bigint(20) unsigned NOT NULL, - PRIMARY KEY (`a`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1 -/*!50100 PARTITION BY KEY (a) -(PARTITION pa1 MAX_ROWS = 20 MIN_ROWS = 2 ENGINE = InnoDB, - PARTITION pa2 MAX_ROWS = 30 MIN_ROWS = 3 ENGINE = InnoDB, - PARTITION pa3 MAX_ROWS = 30 MIN_ROWS = 4 ENGINE = InnoDB, - PARTITION pa4 MAX_ROWS = 40 MIN_ROWS = 2 ENGINE = InnoDB) */ -insert into t1 values (18446744073709551615), (0xFFFFFFFFFFFFFFFE), (18446744073709551613), (18446744073709551612), (1), (2), (65535); -select * from t1; -a -1 -18446744073709551612 -18446744073709551613 -18446744073709551614 -18446744073709551615 -2 -65535 -select * from t1 where a=-2; -a -delete from t1 where a=-2; -select * from t1; -a -1 -18446744073709551612 -18446744073709551613 -18446744073709551614 -18446744073709551615 -2 -65535 -select * from t1 where a=18446744073709551615; -a -18446744073709551615 -delete from t1 where a=18446744073709551615; -select * from t1; -a -1 -18446744073709551612 -18446744073709551613 -18446744073709551614 -2 -65535 -drop table t1; -create table t2 (a bigint unsigned not null, primary key(a)) engine='InnoDB' -partition by key (a) partitions 8; -show create table t2; -Table Create Table -t2 CREATE TABLE `t2` ( - `a` bigint(20) unsigned NOT NULL, - PRIMARY KEY (`a`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1 -/*!50100 PARTITION BY KEY (a) -PARTITIONS 8 */ -insert into t2 values (18446744073709551615), (0xFFFFFFFFFFFFFFFE), (18446744073709551613), (18446744073709551612); -select * from t2; -a -18446744073709551612 -18446744073709551613 -18446744073709551614 -18446744073709551615 -select * from t2 where a=18446744073709551615; -a -18446744073709551615 -delete from t2 where a=18446744073709551615; -select * from t2; -a -18446744073709551612 -18446744073709551613 -18446744073709551614 -delete from t2; -1024 inserts; -select count(*) from t2; -count(*) -1024 -drop table t2; -create table t3 (a bigint not null, primary key(a)) engine='InnoDB' -partition by key (a) partitions 7; -show create table t3; -Table Create Table -t3 CREATE TABLE `t3` ( - `a` bigint(20) NOT NULL, - PRIMARY KEY (`a`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1 -/*!50100 PARTITION BY KEY (a) -PARTITIONS 7 */ -insert into t3 values (9223372036854775807), (9223372036854775806), (9223372036854775805), (9223372036854775804), (-9223372036854775808), (-9223372036854775807), (1), (-1), (0); -select * from t3; -a --1 --9223372036854775807 --9223372036854775808 -0 -1 -9223372036854775804 -9223372036854775805 -9223372036854775806 -9223372036854775807 -select * from t3 where a=9223372036854775806; -a -9223372036854775806 -delete from t3 where a=9223372036854775806; -select * from t3; -a --1 --9223372036854775807 --9223372036854775808 -0 -1 -9223372036854775804 -9223372036854775805 -9223372036854775807 -drop table t3; diff --git a/mysql-test/suite/parts/r/partition_int_myisam.result b/mysql-test/suite/parts/r/partition_int_myisam.result index 4387bbfdd78..8b8352ebc38 100644 --- a/mysql-test/suite/parts/r/partition_int_myisam.result +++ b/mysql-test/suite/parts/r/partition_int_myisam.result @@ -1,221 +1,3 @@ -create table t1 (a tinyint unsigned not null, primary key(a)) engine='MYISAM' -partition by key (a) ( -partition pa1 max_rows=20 min_rows=2, -partition pa2 max_rows=30 min_rows=3, -partition pa3 max_rows=30 min_rows=4, -partition pa4 max_rows=40 min_rows=2); -show create table t1; -Table Create Table -t1 CREATE TABLE `t1` ( - `a` tinyint(3) unsigned NOT NULL, - PRIMARY KEY (`a`) -) ENGINE=MyISAM DEFAULT CHARSET=latin1 -/*!50100 PARTITION BY KEY (a) -(PARTITION pa1 MAX_ROWS = 20 MIN_ROWS = 2 ENGINE = MyISAM, - PARTITION pa2 MAX_ROWS = 30 MIN_ROWS = 3 ENGINE = MyISAM, - PARTITION pa3 MAX_ROWS = 30 MIN_ROWS = 4 ENGINE = MyISAM, - PARTITION pa4 MAX_ROWS = 40 MIN_ROWS = 2 ENGINE = MyISAM) */ -insert into t1 values (255), (254), (253), (252), (1), (2), (128); -select * from t1; -a -1 -128 -2 -252 -253 -254 -255 -select * from t1 where a=253; -a -253 -delete from t1 where a=253; -select * from t1; -a -1 -128 -2 -252 -254 -255 -drop table t1; -create table t2 (a tinyint unsigned not null, primary key(a)) engine='MYISAM' -partition by key (a) partitions 8; -show create table t2; -Table Create Table -t2 CREATE TABLE `t2` ( - `a` tinyint(3) unsigned NOT NULL, - PRIMARY KEY (`a`) -) ENGINE=MyISAM DEFAULT CHARSET=latin1 -/*!50100 PARTITION BY KEY (a) -PARTITIONS 8 */ -insert into t2 values (255), (254), (253), (252); -select * from t2; -a -252 -253 -254 -255 -select * from t2 where a=253; -a -253 -delete from t2 where a=253; -select * from t2; -a -252 -254 -255 -delete from t2; -255 inserts; -select count(*) from t2; -count(*) -255 -drop table t2; -create table t3 (a tinyint not null, primary key(a)) engine='MYISAM' -partition by key (a) partitions 7; -show create table t3; -Table Create Table -t3 CREATE TABLE `t3` ( - `a` tinyint(4) NOT NULL, - PRIMARY KEY (`a`) -) ENGINE=MyISAM DEFAULT CHARSET=latin1 -/*!50100 PARTITION BY KEY (a) -PARTITIONS 7 */ -insert into t3 values (127), (126), (125), (124), (-128), (-127), (1), (-1), (0); -select * from t3; -a --1 --127 --128 -0 -1 -124 -125 -126 -127 -select * from t3 where a=125; -a -125 -delete from t3 where a=125; -select * from t3; -a --1 --127 --128 -0 -1 -124 -126 -127 -drop table t3; -create table t1 (a smallint unsigned not null, primary key(a)) engine='MYISAM' -partition by key (a) ( -partition pa1 max_rows=20 min_rows=2, -partition pa2 max_rows=30 min_rows=3, -partition pa3 max_rows=30 min_rows=4, -partition pa4 max_rows=40 min_rows=2); -show create table t1; -Table Create Table -t1 CREATE TABLE `t1` ( - `a` smallint(5) unsigned NOT NULL, - PRIMARY KEY (`a`) -) ENGINE=MyISAM DEFAULT CHARSET=latin1 -/*!50100 PARTITION BY KEY (a) -(PARTITION pa1 MAX_ROWS = 20 MIN_ROWS = 2 ENGINE = MyISAM, - PARTITION pa2 MAX_ROWS = 30 MIN_ROWS = 3 ENGINE = MyISAM, - PARTITION pa3 MAX_ROWS = 30 MIN_ROWS = 4 ENGINE = MyISAM, - PARTITION pa4 MAX_ROWS = 40 MIN_ROWS = 2 ENGINE = MyISAM) */ -insert into t1 values (65535), (65534), (65533), (65532), (1), (2), (256); -select * from t1; -a -1 -2 -256 -65532 -65533 -65534 -65535 -select * from t1 where a=65533; -a -65533 -delete from t1 where a=65533; -select * from t1; -a -1 -2 -256 -65532 -65534 -65535 -drop table t1; -create table t2 (a smallint unsigned not null, primary key(a)) engine='MYISAM' -partition by key (a) partitions 8; -show create table t2; -Table Create Table -t2 CREATE TABLE `t2` ( - `a` smallint(5) unsigned NOT NULL, - PRIMARY KEY (`a`) -) ENGINE=MyISAM DEFAULT CHARSET=latin1 -/*!50100 PARTITION BY KEY (a) -PARTITIONS 8 */ -insert into t2 values (65535), (65534), (65533), (65532); -select * from t2; -a -65532 -65533 -65534 -65535 -select * from t2 where a=65533; -a -65533 -delete from t2 where a=65533; -select * from t2; -a -65532 -65534 -65535 -delete from t2; -65535 inserts; -select count(*) from t2; -count(*) -65535 -drop table t2; -create table t3 (a smallint not null, primary key(a)) engine='MYISAM' -partition by key (a) partitions 7; -show create table t3; -Table Create Table -t3 CREATE TABLE `t3` ( - `a` smallint(6) NOT NULL, - PRIMARY KEY (`a`) -) ENGINE=MyISAM DEFAULT CHARSET=latin1 -/*!50100 PARTITION BY KEY (a) -PARTITIONS 7 */ -insert into t3 values (32767), (32766), (32765), (32764), (-32768), (-32767), (1), (-1), (0); -select * from t3; -a --1 --32767 --32768 -0 -1 -32764 -32765 -32766 -32767 -select * from t3 where a=32765; -a -32765 -delete from t3 where a=32765; -select * from t3; -a --1 --32767 --32768 -0 -1 -32764 -32766 -32767 -drop table t3; create table t1 (a int unsigned not null, primary key(a)) engine='MYISAM' partition by key (a) ( partition pa1 max_rows=20 min_rows=2, @@ -325,233 +107,3 @@ a 2147483646 2147483647 drop table t3; -create table t1 (a mediumint unsigned not null, primary key(a)) engine='MYISAM' -partition by key (a) ( -partition pa1 max_rows=20 min_rows=2, -partition pa2 max_rows=30 min_rows=3, -partition pa3 max_rows=30 min_rows=4, -partition pa4 max_rows=40 min_rows=2); -show create table t1; -Table Create Table -t1 CREATE TABLE `t1` ( - `a` mediumint(8) unsigned NOT NULL, - PRIMARY KEY (`a`) -) ENGINE=MyISAM DEFAULT CHARSET=latin1 -/*!50100 PARTITION BY KEY (a) -(PARTITION pa1 MAX_ROWS = 20 MIN_ROWS = 2 ENGINE = MyISAM, - PARTITION pa2 MAX_ROWS = 30 MIN_ROWS = 3 ENGINE = MyISAM, - PARTITION pa3 MAX_ROWS = 30 MIN_ROWS = 4 ENGINE = MyISAM, - PARTITION pa4 MAX_ROWS = 40 MIN_ROWS = 2 ENGINE = MyISAM) */ -insert into t1 values (16777215), (16777214), (16777213), (16777212), (1), (2), (65535); -select * from t1; -a -1 -16777212 -16777213 -16777214 -16777215 -2 -65535 -select * from t1 where a=16777213; -a -16777213 -delete from t1 where a=16777213; -select * from t1; -a -1 -16777212 -16777214 -16777215 -2 -65535 -drop table t1; -create table t2 (a mediumint unsigned not null, primary key(a)) engine='MYISAM' -partition by key (a) partitions 8; -show create table t2; -Table Create Table -t2 CREATE TABLE `t2` ( - `a` mediumint(8) unsigned NOT NULL, - PRIMARY KEY (`a`) -) ENGINE=MyISAM DEFAULT CHARSET=latin1 -/*!50100 PARTITION BY KEY (a) -PARTITIONS 8 */ -insert into t2 values (16777215), (16777214), (16777213), (16777212); -select * from t2; -a -16777212 -16777213 -16777214 -16777215 -select * from t2 where a=16777213; -a -16777213 -delete from t2 where a=16777213; -select * from t2; -a -16777212 -16777214 -16777215 -delete from t2; -65535 inserts; -select count(*) from t2; -count(*) -65535 -drop table t2; -create table t3 (a mediumint not null, primary key(a)) engine='MYISAM' -partition by key (a) partitions 7; -show create table t3; -Table Create Table -t3 CREATE TABLE `t3` ( - `a` mediumint(9) NOT NULL, - PRIMARY KEY (`a`) -) ENGINE=MyISAM DEFAULT CHARSET=latin1 -/*!50100 PARTITION BY KEY (a) -PARTITIONS 7 */ -insert into t3 values (8388607), (8388606), (8388605), (8388604), (-8388608), (-8388607), (1), (-1), (0); -select * from t3; -a --1 --8388607 --8388608 -0 -1 -8388604 -8388605 -8388606 -8388607 -select * from t3 where a=8388605; -a -8388605 -delete from t3 where a=8388605; -select * from t3; -a --1 --8388607 --8388608 -0 -1 -8388604 -8388606 -8388607 -drop table t3; -create table t1 (a bigint unsigned not null, primary key(a)) engine='MYISAM' -partition by key (a) ( -partition pa1 max_rows=20 min_rows=2, -partition pa2 max_rows=30 min_rows=3, -partition pa3 max_rows=30 min_rows=4, -partition pa4 max_rows=40 min_rows=2); -show create table t1; -Table Create Table -t1 CREATE TABLE `t1` ( - `a` bigint(20) unsigned NOT NULL, - PRIMARY KEY (`a`) -) ENGINE=MyISAM DEFAULT CHARSET=latin1 -/*!50100 PARTITION BY KEY (a) -(PARTITION pa1 MAX_ROWS = 20 MIN_ROWS = 2 ENGINE = MyISAM, - PARTITION pa2 MAX_ROWS = 30 MIN_ROWS = 3 ENGINE = MyISAM, - PARTITION pa3 MAX_ROWS = 30 MIN_ROWS = 4 ENGINE = MyISAM, - PARTITION pa4 MAX_ROWS = 40 MIN_ROWS = 2 ENGINE = MyISAM) */ -insert into t1 values (18446744073709551615), (0xFFFFFFFFFFFFFFFE), (18446744073709551613), (18446744073709551612), (1), (2), (65535); -select * from t1; -a -1 -18446744073709551612 -18446744073709551613 -18446744073709551614 -18446744073709551615 -2 -65535 -select * from t1 where a=-2; -a -delete from t1 where a=-2; -select * from t1; -a -1 -18446744073709551612 -18446744073709551613 -18446744073709551614 -18446744073709551615 -2 -65535 -select * from t1 where a=18446744073709551615; -a -18446744073709551615 -delete from t1 where a=18446744073709551615; -select * from t1; -a -1 -18446744073709551612 -18446744073709551613 -18446744073709551614 -2 -65535 -drop table t1; -create table t2 (a bigint unsigned not null, primary key(a)) engine='MYISAM' -partition by key (a) partitions 8; -show create table t2; -Table Create Table -t2 CREATE TABLE `t2` ( - `a` bigint(20) unsigned NOT NULL, - PRIMARY KEY (`a`) -) ENGINE=MyISAM DEFAULT CHARSET=latin1 -/*!50100 PARTITION BY KEY (a) -PARTITIONS 8 */ -insert into t2 values (18446744073709551615), (0xFFFFFFFFFFFFFFFE), (18446744073709551613), (18446744073709551612); -select * from t2; -a -18446744073709551612 -18446744073709551613 -18446744073709551614 -18446744073709551615 -select * from t2 where a=18446744073709551615; -a -18446744073709551615 -delete from t2 where a=18446744073709551615; -select * from t2; -a -18446744073709551612 -18446744073709551613 -18446744073709551614 -delete from t2; -65535 inserts; -select count(*) from t2; -count(*) -65535 -drop table t2; -create table t3 (a bigint not null, primary key(a)) engine='MYISAM' -partition by key (a) partitions 7; -show create table t3; -Table Create Table -t3 CREATE TABLE `t3` ( - `a` bigint(20) NOT NULL, - PRIMARY KEY (`a`) -) ENGINE=MyISAM DEFAULT CHARSET=latin1 -/*!50100 PARTITION BY KEY (a) -PARTITIONS 7 */ -insert into t3 values (9223372036854775807), (9223372036854775806), (9223372036854775805), (9223372036854775804), (-9223372036854775808), (-9223372036854775807), (1), (-1), (0); -select * from t3; -a --1 --9223372036854775807 --9223372036854775808 -0 -1 -9223372036854775804 -9223372036854775805 -9223372036854775806 -9223372036854775807 -select * from t3 where a=9223372036854775806; -a -9223372036854775806 -delete from t3 where a=9223372036854775806; -select * from t3; -a --1 --9223372036854775807 --9223372036854775808 -0 -1 -9223372036854775804 -9223372036854775805 -9223372036854775807 -drop table t3; diff --git a/mysql-test/suite/parts/r/partition_mediumint_innodb.result b/mysql-test/suite/parts/r/partition_mediumint_innodb.result new file mode 100644 index 00000000000..8e3e5543ddc --- /dev/null +++ b/mysql-test/suite/parts/r/partition_mediumint_innodb.result @@ -0,0 +1,109 @@ +create table t1 (a mediumint unsigned not null, primary key(a)) engine='InnoDB' +partition by key (a) ( +partition pa1 max_rows=20 min_rows=2, +partition pa2 max_rows=30 min_rows=3, +partition pa3 max_rows=30 min_rows=4, +partition pa4 max_rows=40 min_rows=2); +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` mediumint(8) unsigned NOT NULL, + PRIMARY KEY (`a`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +/*!50100 PARTITION BY KEY (a) +(PARTITION pa1 MAX_ROWS = 20 MIN_ROWS = 2 ENGINE = InnoDB, + PARTITION pa2 MAX_ROWS = 30 MIN_ROWS = 3 ENGINE = InnoDB, + PARTITION pa3 MAX_ROWS = 30 MIN_ROWS = 4 ENGINE = InnoDB, + PARTITION pa4 MAX_ROWS = 40 MIN_ROWS = 2 ENGINE = InnoDB) */ +insert into t1 values (16777215), (16777214), (16777213), (16777212), (1), (2), (65535); +select * from t1; +a +1 +16777212 +16777213 +16777214 +16777215 +2 +65535 +select * from t1 where a=16777213; +a +16777213 +delete from t1 where a=16777213; +select * from t1; +a +1 +16777212 +16777214 +16777215 +2 +65535 +drop table t1; +create table t2 (a mediumint unsigned not null, primary key(a)) engine='InnoDB' +partition by key (a) partitions 8; +show create table t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `a` mediumint(8) unsigned NOT NULL, + PRIMARY KEY (`a`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +/*!50100 PARTITION BY KEY (a) +PARTITIONS 8 */ +insert into t2 values (16777215), (16777214), (16777213), (16777212); +select * from t2; +a +16777212 +16777213 +16777214 +16777215 +select * from t2 where a=16777213; +a +16777213 +delete from t2 where a=16777213; +select * from t2; +a +16777212 +16777214 +16777215 +delete from t2; +1024 inserts; +select count(*) from t2; +count(*) +1024 +drop table t2; +create table t3 (a mediumint not null, primary key(a)) engine='InnoDB' +partition by key (a) partitions 7; +show create table t3; +Table Create Table +t3 CREATE TABLE `t3` ( + `a` mediumint(9) NOT NULL, + PRIMARY KEY (`a`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +/*!50100 PARTITION BY KEY (a) +PARTITIONS 7 */ +insert into t3 values (8388607), (8388606), (8388605), (8388604), (-8388608), (-8388607), (1), (-1), (0); +select * from t3; +a +-1 +-8388607 +-8388608 +0 +1 +8388604 +8388605 +8388606 +8388607 +select * from t3 where a=8388605; +a +8388605 +delete from t3 where a=8388605; +select * from t3; +a +-1 +-8388607 +-8388608 +0 +1 +8388604 +8388606 +8388607 +drop table t3; diff --git a/mysql-test/suite/parts/r/partition_mediumint_myisam.result b/mysql-test/suite/parts/r/partition_mediumint_myisam.result new file mode 100644 index 00000000000..4853680610b --- /dev/null +++ b/mysql-test/suite/parts/r/partition_mediumint_myisam.result @@ -0,0 +1,109 @@ +create table t1 (a mediumint unsigned not null, primary key(a)) engine='MYISAM' +partition by key (a) ( +partition pa1 max_rows=20 min_rows=2, +partition pa2 max_rows=30 min_rows=3, +partition pa3 max_rows=30 min_rows=4, +partition pa4 max_rows=40 min_rows=2); +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` mediumint(8) unsigned NOT NULL, + PRIMARY KEY (`a`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +/*!50100 PARTITION BY KEY (a) +(PARTITION pa1 MAX_ROWS = 20 MIN_ROWS = 2 ENGINE = MyISAM, + PARTITION pa2 MAX_ROWS = 30 MIN_ROWS = 3 ENGINE = MyISAM, + PARTITION pa3 MAX_ROWS = 30 MIN_ROWS = 4 ENGINE = MyISAM, + PARTITION pa4 MAX_ROWS = 40 MIN_ROWS = 2 ENGINE = MyISAM) */ +insert into t1 values (16777215), (16777214), (16777213), (16777212), (1), (2), (65535); +select * from t1; +a +1 +16777212 +16777213 +16777214 +16777215 +2 +65535 +select * from t1 where a=16777213; +a +16777213 +delete from t1 where a=16777213; +select * from t1; +a +1 +16777212 +16777214 +16777215 +2 +65535 +drop table t1; +create table t2 (a mediumint unsigned not null, primary key(a)) engine='MYISAM' +partition by key (a) partitions 8; +show create table t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `a` mediumint(8) unsigned NOT NULL, + PRIMARY KEY (`a`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +/*!50100 PARTITION BY KEY (a) +PARTITIONS 8 */ +insert into t2 values (16777215), (16777214), (16777213), (16777212); +select * from t2; +a +16777212 +16777213 +16777214 +16777215 +select * from t2 where a=16777213; +a +16777213 +delete from t2 where a=16777213; +select * from t2; +a +16777212 +16777214 +16777215 +delete from t2; +65535 inserts; +select count(*) from t2; +count(*) +65535 +drop table t2; +create table t3 (a mediumint not null, primary key(a)) engine='MYISAM' +partition by key (a) partitions 7; +show create table t3; +Table Create Table +t3 CREATE TABLE `t3` ( + `a` mediumint(9) NOT NULL, + PRIMARY KEY (`a`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +/*!50100 PARTITION BY KEY (a) +PARTITIONS 7 */ +insert into t3 values (8388607), (8388606), (8388605), (8388604), (-8388608), (-8388607), (1), (-1), (0); +select * from t3; +a +-1 +-8388607 +-8388608 +0 +1 +8388604 +8388605 +8388606 +8388607 +select * from t3 where a=8388605; +a +8388605 +delete from t3 where a=8388605; +select * from t3; +a +-1 +-8388607 +-8388608 +0 +1 +8388604 +8388606 +8388607 +drop table t3; diff --git a/mysql-test/suite/parts/r/partition_smallint_innodb.result b/mysql-test/suite/parts/r/partition_smallint_innodb.result new file mode 100644 index 00000000000..fbf23fe582c --- /dev/null +++ b/mysql-test/suite/parts/r/partition_smallint_innodb.result @@ -0,0 +1,109 @@ +create table t1 (a smallint unsigned not null, primary key(a)) engine='InnoDB' +partition by key (a) ( +partition pa1 max_rows=20 min_rows=2, +partition pa2 max_rows=30 min_rows=3, +partition pa3 max_rows=30 min_rows=4, +partition pa4 max_rows=40 min_rows=2); +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` smallint(5) unsigned NOT NULL, + PRIMARY KEY (`a`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +/*!50100 PARTITION BY KEY (a) +(PARTITION pa1 MAX_ROWS = 20 MIN_ROWS = 2 ENGINE = InnoDB, + PARTITION pa2 MAX_ROWS = 30 MIN_ROWS = 3 ENGINE = InnoDB, + PARTITION pa3 MAX_ROWS = 30 MIN_ROWS = 4 ENGINE = InnoDB, + PARTITION pa4 MAX_ROWS = 40 MIN_ROWS = 2 ENGINE = InnoDB) */ +insert into t1 values (65535), (65534), (65533), (65532), (1), (2), (256); +select * from t1; +a +1 +2 +256 +65532 +65533 +65534 +65535 +select * from t1 where a=65533; +a +65533 +delete from t1 where a=65533; +select * from t1; +a +1 +2 +256 +65532 +65534 +65535 +drop table t1; +create table t2 (a smallint unsigned not null, primary key(a)) engine='InnoDB' +partition by key (a) partitions 8; +show create table t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `a` smallint(5) unsigned NOT NULL, + PRIMARY KEY (`a`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +/*!50100 PARTITION BY KEY (a) +PARTITIONS 8 */ +insert into t2 values (65535), (65534), (65533), (65532); +select * from t2; +a +65532 +65533 +65534 +65535 +select * from t2 where a=65533; +a +65533 +delete from t2 where a=65533; +select * from t2; +a +65532 +65534 +65535 +delete from t2; +1024 inserts; +select count(*) from t2; +count(*) +1024 +drop table t2; +create table t3 (a smallint not null, primary key(a)) engine='InnoDB' +partition by key (a) partitions 7; +show create table t3; +Table Create Table +t3 CREATE TABLE `t3` ( + `a` smallint(6) NOT NULL, + PRIMARY KEY (`a`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +/*!50100 PARTITION BY KEY (a) +PARTITIONS 7 */ +insert into t3 values (32767), (32766), (32765), (32764), (-32768), (-32767), (1), (-1), (0); +select * from t3; +a +-1 +-32767 +-32768 +0 +1 +32764 +32765 +32766 +32767 +select * from t3 where a=32765; +a +32765 +delete from t3 where a=32765; +select * from t3; +a +-1 +-32767 +-32768 +0 +1 +32764 +32766 +32767 +drop table t3; diff --git a/mysql-test/suite/parts/r/partition_smallint_myisam.result b/mysql-test/suite/parts/r/partition_smallint_myisam.result new file mode 100644 index 00000000000..a405d025919 --- /dev/null +++ b/mysql-test/suite/parts/r/partition_smallint_myisam.result @@ -0,0 +1,109 @@ +create table t1 (a smallint unsigned not null, primary key(a)) engine='MYISAM' +partition by key (a) ( +partition pa1 max_rows=20 min_rows=2, +partition pa2 max_rows=30 min_rows=3, +partition pa3 max_rows=30 min_rows=4, +partition pa4 max_rows=40 min_rows=2); +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` smallint(5) unsigned NOT NULL, + PRIMARY KEY (`a`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +/*!50100 PARTITION BY KEY (a) +(PARTITION pa1 MAX_ROWS = 20 MIN_ROWS = 2 ENGINE = MyISAM, + PARTITION pa2 MAX_ROWS = 30 MIN_ROWS = 3 ENGINE = MyISAM, + PARTITION pa3 MAX_ROWS = 30 MIN_ROWS = 4 ENGINE = MyISAM, + PARTITION pa4 MAX_ROWS = 40 MIN_ROWS = 2 ENGINE = MyISAM) */ +insert into t1 values (65535), (65534), (65533), (65532), (1), (2), (256); +select * from t1; +a +1 +2 +256 +65532 +65533 +65534 +65535 +select * from t1 where a=65533; +a +65533 +delete from t1 where a=65533; +select * from t1; +a +1 +2 +256 +65532 +65534 +65535 +drop table t1; +create table t2 (a smallint unsigned not null, primary key(a)) engine='MYISAM' +partition by key (a) partitions 8; +show create table t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `a` smallint(5) unsigned NOT NULL, + PRIMARY KEY (`a`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +/*!50100 PARTITION BY KEY (a) +PARTITIONS 8 */ +insert into t2 values (65535), (65534), (65533), (65532); +select * from t2; +a +65532 +65533 +65534 +65535 +select * from t2 where a=65533; +a +65533 +delete from t2 where a=65533; +select * from t2; +a +65532 +65534 +65535 +delete from t2; +65535 inserts; +select count(*) from t2; +count(*) +65535 +drop table t2; +create table t3 (a smallint not null, primary key(a)) engine='MYISAM' +partition by key (a) partitions 7; +show create table t3; +Table Create Table +t3 CREATE TABLE `t3` ( + `a` smallint(6) NOT NULL, + PRIMARY KEY (`a`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +/*!50100 PARTITION BY KEY (a) +PARTITIONS 7 */ +insert into t3 values (32767), (32766), (32765), (32764), (-32768), (-32767), (1), (-1), (0); +select * from t3; +a +-1 +-32767 +-32768 +0 +1 +32764 +32765 +32766 +32767 +select * from t3 where a=32765; +a +32765 +delete from t3 where a=32765; +select * from t3; +a +-1 +-32767 +-32768 +0 +1 +32764 +32766 +32767 +drop table t3; diff --git a/mysql-test/suite/parts/r/partition_tinyint_innodb.result b/mysql-test/suite/parts/r/partition_tinyint_innodb.result new file mode 100644 index 00000000000..d7138539b78 --- /dev/null +++ b/mysql-test/suite/parts/r/partition_tinyint_innodb.result @@ -0,0 +1,109 @@ +create table t1 (a tinyint unsigned not null, primary key(a)) engine='InnoDB' +partition by key (a) ( +partition pa1 max_rows=20 min_rows=2, +partition pa2 max_rows=30 min_rows=3, +partition pa3 max_rows=30 min_rows=4, +partition pa4 max_rows=40 min_rows=2); +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` tinyint(3) unsigned NOT NULL, + PRIMARY KEY (`a`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +/*!50100 PARTITION BY KEY (a) +(PARTITION pa1 MAX_ROWS = 20 MIN_ROWS = 2 ENGINE = InnoDB, + PARTITION pa2 MAX_ROWS = 30 MIN_ROWS = 3 ENGINE = InnoDB, + PARTITION pa3 MAX_ROWS = 30 MIN_ROWS = 4 ENGINE = InnoDB, + PARTITION pa4 MAX_ROWS = 40 MIN_ROWS = 2 ENGINE = InnoDB) */ +insert into t1 values (255), (254), (253), (252), (1), (2), (128); +select * from t1; +a +1 +128 +2 +252 +253 +254 +255 +select * from t1 where a=253; +a +253 +delete from t1 where a=253; +select * from t1; +a +1 +128 +2 +252 +254 +255 +drop table t1; +create table t2 (a tinyint unsigned not null, primary key(a)) engine='InnoDB' +partition by key (a) partitions 8; +show create table t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `a` tinyint(3) unsigned NOT NULL, + PRIMARY KEY (`a`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +/*!50100 PARTITION BY KEY (a) +PARTITIONS 8 */ +insert into t2 values (255), (254), (253), (252); +select * from t2; +a +252 +253 +254 +255 +select * from t2 where a=253; +a +253 +delete from t2 where a=253; +select * from t2; +a +252 +254 +255 +delete from t2; +255 inserts; +select count(*) from t2; +count(*) +255 +drop table t2; +create table t3 (a tinyint not null, primary key(a)) engine='InnoDB' +partition by key (a) partitions 7; +show create table t3; +Table Create Table +t3 CREATE TABLE `t3` ( + `a` tinyint(4) NOT NULL, + PRIMARY KEY (`a`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1 +/*!50100 PARTITION BY KEY (a) +PARTITIONS 7 */ +insert into t3 values (127), (126), (125), (124), (-128), (-127), (1), (-1), (0); +select * from t3; +a +-1 +-127 +-128 +0 +1 +124 +125 +126 +127 +select * from t3 where a=125; +a +125 +delete from t3 where a=125; +select * from t3; +a +-1 +-127 +-128 +0 +1 +124 +126 +127 +drop table t3; diff --git a/mysql-test/suite/parts/r/partition_tinyint_myisam.result b/mysql-test/suite/parts/r/partition_tinyint_myisam.result new file mode 100644 index 00000000000..08a688e8f36 --- /dev/null +++ b/mysql-test/suite/parts/r/partition_tinyint_myisam.result @@ -0,0 +1,109 @@ +create table t1 (a tinyint unsigned not null, primary key(a)) engine='MYISAM' +partition by key (a) ( +partition pa1 max_rows=20 min_rows=2, +partition pa2 max_rows=30 min_rows=3, +partition pa3 max_rows=30 min_rows=4, +partition pa4 max_rows=40 min_rows=2); +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` tinyint(3) unsigned NOT NULL, + PRIMARY KEY (`a`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +/*!50100 PARTITION BY KEY (a) +(PARTITION pa1 MAX_ROWS = 20 MIN_ROWS = 2 ENGINE = MyISAM, + PARTITION pa2 MAX_ROWS = 30 MIN_ROWS = 3 ENGINE = MyISAM, + PARTITION pa3 MAX_ROWS = 30 MIN_ROWS = 4 ENGINE = MyISAM, + PARTITION pa4 MAX_ROWS = 40 MIN_ROWS = 2 ENGINE = MyISAM) */ +insert into t1 values (255), (254), (253), (252), (1), (2), (128); +select * from t1; +a +1 +128 +2 +252 +253 +254 +255 +select * from t1 where a=253; +a +253 +delete from t1 where a=253; +select * from t1; +a +1 +128 +2 +252 +254 +255 +drop table t1; +create table t2 (a tinyint unsigned not null, primary key(a)) engine='MYISAM' +partition by key (a) partitions 8; +show create table t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `a` tinyint(3) unsigned NOT NULL, + PRIMARY KEY (`a`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +/*!50100 PARTITION BY KEY (a) +PARTITIONS 8 */ +insert into t2 values (255), (254), (253), (252); +select * from t2; +a +252 +253 +254 +255 +select * from t2 where a=253; +a +253 +delete from t2 where a=253; +select * from t2; +a +252 +254 +255 +delete from t2; +255 inserts; +select count(*) from t2; +count(*) +255 +drop table t2; +create table t3 (a tinyint not null, primary key(a)) engine='MYISAM' +partition by key (a) partitions 7; +show create table t3; +Table Create Table +t3 CREATE TABLE `t3` ( + `a` tinyint(4) NOT NULL, + PRIMARY KEY (`a`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +/*!50100 PARTITION BY KEY (a) +PARTITIONS 7 */ +insert into t3 values (127), (126), (125), (124), (-128), (-127), (1), (-1), (0); +select * from t3; +a +-1 +-127 +-128 +0 +1 +124 +125 +126 +127 +select * from t3 where a=125; +a +125 +delete from t3 where a=125; +select * from t3; +a +-1 +-127 +-128 +0 +1 +124 +126 +127 +drop table t3; diff --git a/mysql-test/suite/parts/t/partition_bigint_innodb.test b/mysql-test/suite/parts/t/partition_bigint_innodb.test new file mode 100644 index 00000000000..348ee0add05 --- /dev/null +++ b/mysql-test/suite/parts/t/partition_bigint_innodb.test @@ -0,0 +1,46 @@ +################################################################################ +# t/partition_bigint_innodb.test # +# # +# Purpose: # +# Tests around integer type # +# INNODB branch # +# # +#------------------------------------------------------------------------------# +# Original Author: HH # +# Original Date: 2006-08-01 # +# Change Author: Elena Stepanova # +# Change Date: 2017-02-18 # +# Change: The test file is spawned from the mega-test partition_int_innodb # +################################################################################ + +# +# NOTE: PLEASE DO NOT ADD NOT INNODB SPECIFIC TESTCASES HERE ! +# TESTCASES WHICH MUST BE APPLIED TO ALL STORAGE ENGINES MUST BE ADDED IN +# THE SOURCED FILES ONLY. +# +# Please read the README at the end of inc/partition.pre before changing +# any of the variables. +# + +#------------------------------------------------------------------------------# +# General not engine specific settings and requirements + +##### Options, for debugging support ##### +let $debug= 0; + +# The server must support partitioning. +--source include/have_partition.inc + +#------------------------------------------------------------------------------# +# Engine specific settings and requirements + +##### Storage engine to be tested +--source include/have_innodb.inc +let $engine= 'InnoDB'; + +##### max rows to be inserted +let $maxrows=1024; + +#------------------------------------------------------------------------------# +# Execute the tests to be applied to all storage engines +--source suite/parts/inc/partition_bigint.inc diff --git a/mysql-test/suite/parts/t/partition_bigint_myisam.test b/mysql-test/suite/parts/t/partition_bigint_myisam.test new file mode 100644 index 00000000000..f427ffce08d --- /dev/null +++ b/mysql-test/suite/parts/t/partition_bigint_myisam.test @@ -0,0 +1,46 @@ +################################################################################ +# t/partition_bigint_myisam.test # +# # +# Purpose: # +# Tests around integer type # +# MyISAM branch # +# # +#------------------------------------------------------------------------------# +# Original Author: HH # +# Original Date: 2006-08-01 # +# Change Author: Elena Stepanova # +# Change Date: 2017-02-18 # +# Change: The test file is spawned from the mega-test partition_int_myisam # +################################################################################ + +# +# NOTE: PLEASE DO NOT ADD NOT MYISAM SPECIFIC TESTCASES HERE ! +# TESTCASES WHICH MUST BE APPLIED TO ALL STORAGE ENGINES MUST BE ADDED IN +# THE SOURCED FILES ONLY. +# +# Please read the README at the end of inc/partition.pre before changing +# any of the variables. +# + +--source include/long_test.inc + +#------------------------------------------------------------------------------# +# General not engine specific settings and requirements + +##### Options, for debugging support ##### +let $debug= 0; + +# The server must support partitioning. +--source include/have_partition.inc + +#------------------------------------------------------------------------------# +# Engine specific settings and requirements + +##### Storage engine to be tested +let $engine= 'MYISAM'; +##### number of rows to be inserted +let $maxrows=65535; + +#------------------------------------------------------------------------------# +# Execute the tests to be applied to all storage engines +--source suite/parts/inc/partition_bigint.inc diff --git a/mysql-test/suite/parts/t/partition_double_innodb.test b/mysql-test/suite/parts/t/partition_double_innodb.test new file mode 100644 index 00000000000..e31f7049502 --- /dev/null +++ b/mysql-test/suite/parts/t/partition_double_innodb.test @@ -0,0 +1,46 @@ +################################################################################ +# t/partition_double_innodb.test # +# # +# Purpose: # +# Tests around float type # +# INNODB branch # +# # +#------------------------------------------------------------------------------# +# Original Author: HH # +# Original Date: 2006-08-01 # +# Change Author: Elena Stepanova # +# Change Date: 2017-02-18 # +# Change: The test file is spawned from the mega-test partition_float_innodb # +################################################################################ + +# +# NOTE: PLEASE DO NOT ADD NOT INNODB SPECIFIC TESTCASES HERE ! +# TESTCASES WHICH MUST BE APPLIED TO ALL STORAGE ENGINES MUST BE ADDED IN +# THE SOURCED FILES ONLY. +# +# Please read the README at the end of inc/partition.pre before changing +# any of the variables. +# + +#------------------------------------------------------------------------------# +# General not engine specific settings and requirements + +##### Options, for debugging support ##### +let $debug= 0; + +# The server must support partitioning. +--source include/have_partition.inc + +#------------------------------------------------------------------------------# +# Engine specific settings and requirements + +##### Storage engine to be tested +--source include/have_innodb.inc +let $engine= 'InnoDB'; + +##### Number of row to be inserted. +let $maxrows=1024; + +#------------------------------------------------------------------------------# +# Execute the tests to be applied to all storage engines +--source suite/parts/inc/partition_double.inc diff --git a/mysql-test/suite/parts/t/partition_double_myisam.test b/mysql-test/suite/parts/t/partition_double_myisam.test new file mode 100644 index 00000000000..6228d657c48 --- /dev/null +++ b/mysql-test/suite/parts/t/partition_double_myisam.test @@ -0,0 +1,46 @@ +################################################################################ +# t/partition_double_myisam.test # +# # +# Purpose: # +# Tests around float type # +# MyISAM branch # +# # +#------------------------------------------------------------------------------# +# Original Author: HH # +# Original Date: 2006-08-01 # +# Change Author: Elena Stepanova # +# Change Date: 2017-02-18 # +# Change: The test file is spawned from the mega-test partition_float_myisam # +################################################################################ + +# +# NOTE: PLEASE DO NOT ADD NOT MYISAM SPECIFIC TESTCASES HERE ! +# TESTCASES WHICH MUST BE APPLIED TO ALL STORAGE ENGINES MUST BE ADDED IN +# THE SOURCED FILES ONLY. +# +# Please read the README at the end of inc/partition.pre before changing +# any of the variables. +# + +--source include/long_test.inc + +#------------------------------------------------------------------------------# +# General not engine specific settings and requirements + +##### Options, for debugging support ##### +let $debug= 0; + +# The server must support partitioning. +--source include/have_partition.inc + +#------------------------------------------------------------------------------# +# Engine specific settings and requirements + +##### Storage engine to be tested +let $engine= 'MYISAM'; +##### Number of row to be inserted. +let $maxrows=16384; + +#------------------------------------------------------------------------------# +# Execute the tests to be applied to all storage engines +--source suite/parts/inc/partition_double.inc diff --git a/mysql-test/suite/parts/t/partition_float_innodb.test b/mysql-test/suite/parts/t/partition_float_innodb.test index 2f1fe723dad..8d96eafbb06 100644 --- a/mysql-test/suite/parts/t/partition_float_innodb.test +++ b/mysql-test/suite/parts/t/partition_float_innodb.test @@ -8,9 +8,9 @@ #------------------------------------------------------------------------------# # Original Author: HH # # Original Date: 2006-08-01 # -# Change Author: # -# Change Date: # -# Change: # +# Change Author: Elena Stepanova # +# Change Date: 2017-02-18 # +# Change: The test for double type has been spawned into a separate test file # ################################################################################ # @@ -44,4 +44,3 @@ let $maxrows=1024; #------------------------------------------------------------------------------# # Execute the tests to be applied to all storage engines --source suite/parts/inc/partition_float.inc ---source suite/parts/inc/partition_double.inc diff --git a/mysql-test/suite/parts/t/partition_float_myisam.test b/mysql-test/suite/parts/t/partition_float_myisam.test index f15e6ad3636..bdc0edd41bb 100644 --- a/mysql-test/suite/parts/t/partition_float_myisam.test +++ b/mysql-test/suite/parts/t/partition_float_myisam.test @@ -8,9 +8,9 @@ #------------------------------------------------------------------------------# # Original Author: HH # # Original Date: 2006-08-01 # -# Change Author: # -# Change Date: # -# Change: # +# Change Author: Elena Stepanova # +# Change Date: 2017-02-18 # +# Change: The test for double type has been spawned into a separate test file # ################################################################################ # @@ -44,4 +44,3 @@ let $maxrows=16384; #------------------------------------------------------------------------------# # Execute the tests to be applied to all storage engines --source suite/parts/inc/partition_float.inc ---source suite/parts/inc/partition_double.inc diff --git a/mysql-test/suite/parts/t/partition_int_innodb.test b/mysql-test/suite/parts/t/partition_int_innodb.test index 698a2c93c22..fd00e6a0d80 100644 --- a/mysql-test/suite/parts/t/partition_int_innodb.test +++ b/mysql-test/suite/parts/t/partition_int_innodb.test @@ -8,9 +8,9 @@ #------------------------------------------------------------------------------# # Original Author: HH # # Original Date: 2006-08-01 # -# Change Author: # -# Change Date: # -# Change: # +# Change Author: Elena Stepanova # +# Change Date: 2017-02-18 # +# Change: Int subtypes (tinyint etc.) have been spawned into separate tests # ################################################################################ # @@ -43,8 +43,4 @@ let $maxrows=1024; #------------------------------------------------------------------------------# # Execute the tests to be applied to all storage engines ---source suite/parts/inc/partition_tinyint.inc ---source suite/parts/inc/partition_smallint.inc --source suite/parts/inc/partition_int.inc ---source suite/parts/inc/partition_mediumint.inc ---source suite/parts/inc/partition_bigint.inc diff --git a/mysql-test/suite/parts/t/partition_int_myisam.test b/mysql-test/suite/parts/t/partition_int_myisam.test index 5f29b575244..e8de09f1bf3 100644 --- a/mysql-test/suite/parts/t/partition_int_myisam.test +++ b/mysql-test/suite/parts/t/partition_int_myisam.test @@ -8,9 +8,9 @@ #------------------------------------------------------------------------------# # Original Author: HH # # Original Date: 2006-08-01 # -# Change Author: # -# Change Date: # -# Change: # +# Change Author: Elena Stepanova # +# Change Date: 2017-02-18 # +# Change: Int subtypes (tinyint etc.) have been spawned into separate tests # ################################################################################ # @@ -43,8 +43,4 @@ let $maxrows=65535; #------------------------------------------------------------------------------# # Execute the tests to be applied to all storage engines ---source suite/parts/inc/partition_tinyint.inc ---source suite/parts/inc/partition_smallint.inc --source suite/parts/inc/partition_int.inc ---source suite/parts/inc/partition_mediumint.inc ---source suite/parts/inc/partition_bigint.inc diff --git a/mysql-test/suite/parts/t/partition_mediumint_innodb.test b/mysql-test/suite/parts/t/partition_mediumint_innodb.test new file mode 100644 index 00000000000..9218b55fa78 --- /dev/null +++ b/mysql-test/suite/parts/t/partition_mediumint_innodb.test @@ -0,0 +1,46 @@ +################################################################################ +# t/partition_mediumint_innodb.test # +# # +# Purpose: # +# Tests around integer type # +# INNODB branch # +# # +#------------------------------------------------------------------------------# +# Original Author: HH # +# Original Date: 2006-08-01 # +# Change Author: Elena Stepanova # +# Change Date: 2017-02-18 # +# Change: The test file is spawned from the mega-test partition_int_innodb # +################################################################################ + +# +# NOTE: PLEASE DO NOT ADD NOT INNODB SPECIFIC TESTCASES HERE ! +# TESTCASES WHICH MUST BE APPLIED TO ALL STORAGE ENGINES MUST BE ADDED IN +# THE SOURCED FILES ONLY. +# +# Please read the README at the end of inc/partition.pre before changing +# any of the variables. +# + +#------------------------------------------------------------------------------# +# General not engine specific settings and requirements + +##### Options, for debugging support ##### +let $debug= 0; + +# The server must support partitioning. +--source include/have_partition.inc + +#------------------------------------------------------------------------------# +# Engine specific settings and requirements + +##### Storage engine to be tested +--source include/have_innodb.inc +let $engine= 'InnoDB'; + +##### max rows to be inserted +let $maxrows=1024; + +#------------------------------------------------------------------------------# +# Execute the tests to be applied to all storage engines +--source suite/parts/inc/partition_mediumint.inc diff --git a/mysql-test/suite/parts/t/partition_mediumint_myisam.test b/mysql-test/suite/parts/t/partition_mediumint_myisam.test new file mode 100644 index 00000000000..bbf1775ba97 --- /dev/null +++ b/mysql-test/suite/parts/t/partition_mediumint_myisam.test @@ -0,0 +1,46 @@ +################################################################################ +# t/partition_mediumint_myisam.test # +# # +# Purpose: # +# Tests around integer type # +# MyISAM branch # +# # +#------------------------------------------------------------------------------# +# Original Author: HH # +# Original Date: 2006-08-01 # +# Change Author: Elena Stepanova # +# Change Date: 2017-02-18 # +# Change: The test file is spawned from the mega-test partition_int_myisam # +################################################################################ + +# +# NOTE: PLEASE DO NOT ADD NOT MYISAM SPECIFIC TESTCASES HERE ! +# TESTCASES WHICH MUST BE APPLIED TO ALL STORAGE ENGINES MUST BE ADDED IN +# THE SOURCED FILES ONLY. +# +# Please read the README at the end of inc/partition.pre before changing +# any of the variables. +# + +--source include/long_test.inc + +#------------------------------------------------------------------------------# +# General not engine specific settings and requirements + +##### Options, for debugging support ##### +let $debug= 0; + +# The server must support partitioning. +--source include/have_partition.inc + +#------------------------------------------------------------------------------# +# Engine specific settings and requirements + +##### Storage engine to be tested +let $engine= 'MYISAM'; +##### number of rows to be inserted +let $maxrows=65535; + +#------------------------------------------------------------------------------# +# Execute the tests to be applied to all storage engines +--source suite/parts/inc/partition_mediumint.inc diff --git a/mysql-test/suite/parts/t/partition_smallint_innodb.test b/mysql-test/suite/parts/t/partition_smallint_innodb.test new file mode 100644 index 00000000000..22d16cf9d55 --- /dev/null +++ b/mysql-test/suite/parts/t/partition_smallint_innodb.test @@ -0,0 +1,46 @@ +################################################################################ +# t/partition_smallint_innodb.test # +# # +# Purpose: # +# Tests around integer type # +# INNODB branch # +# # +#------------------------------------------------------------------------------# +# Original Author: HH # +# Original Date: 2006-08-01 # +# Change Author: Elena Stepanova # +# Change Date: 2017-02-18 # +# Change: The test file is spawned from the mega-test partition_int_innodb # +################################################################################ + +# +# NOTE: PLEASE DO NOT ADD NOT INNODB SPECIFIC TESTCASES HERE ! +# TESTCASES WHICH MUST BE APPLIED TO ALL STORAGE ENGINES MUST BE ADDED IN +# THE SOURCED FILES ONLY. +# +# Please read the README at the end of inc/partition.pre before changing +# any of the variables. +# + +#------------------------------------------------------------------------------# +# General not engine specific settings and requirements + +##### Options, for debugging support ##### +let $debug= 0; + +# The server must support partitioning. +--source include/have_partition.inc + +#------------------------------------------------------------------------------# +# Engine specific settings and requirements + +##### Storage engine to be tested +--source include/have_innodb.inc +let $engine= 'InnoDB'; + +##### max rows to be inserted +let $maxrows=1024; + +#------------------------------------------------------------------------------# +# Execute the tests to be applied to all storage engines +--source suite/parts/inc/partition_smallint.inc diff --git a/mysql-test/suite/parts/t/partition_smallint_myisam.test b/mysql-test/suite/parts/t/partition_smallint_myisam.test new file mode 100644 index 00000000000..f473a6772f8 --- /dev/null +++ b/mysql-test/suite/parts/t/partition_smallint_myisam.test @@ -0,0 +1,46 @@ +################################################################################ +# t/partition_smallint_myisam.test # +# # +# Purpose: # +# Tests around integer type # +# MyISAM branch # +# # +#------------------------------------------------------------------------------# +# Original Author: HH # +# Original Date: 2006-08-01 # +# Change Author: Elena Stepanova # +# Change Date: 2017-02-18 # +# Change: The test file is spawned from the mega-test partition_int_myisam # +################################################################################ + +# +# NOTE: PLEASE DO NOT ADD NOT MYISAM SPECIFIC TESTCASES HERE ! +# TESTCASES WHICH MUST BE APPLIED TO ALL STORAGE ENGINES MUST BE ADDED IN +# THE SOURCED FILES ONLY. +# +# Please read the README at the end of inc/partition.pre before changing +# any of the variables. +# + +--source include/long_test.inc + +#------------------------------------------------------------------------------# +# General not engine specific settings and requirements + +##### Options, for debugging support ##### +let $debug= 0; + +# The server must support partitioning. +--source include/have_partition.inc + +#------------------------------------------------------------------------------# +# Engine specific settings and requirements + +##### Storage engine to be tested +let $engine= 'MYISAM'; +##### number of rows to be inserted +let $maxrows=65535; + +#------------------------------------------------------------------------------# +# Execute the tests to be applied to all storage engines +--source suite/parts/inc/partition_smallint.inc diff --git a/mysql-test/suite/parts/t/partition_tinyint_innodb.test b/mysql-test/suite/parts/t/partition_tinyint_innodb.test new file mode 100644 index 00000000000..aec10c1aea5 --- /dev/null +++ b/mysql-test/suite/parts/t/partition_tinyint_innodb.test @@ -0,0 +1,46 @@ +################################################################################ +# t/partition_tinyint_innodb.test # +# # +# Purpose: # +# Tests around integer type # +# INNODB branch # +# # +#------------------------------------------------------------------------------# +# Original Author: HH # +# Original Date: 2006-08-01 # +# Change Author: Elena Stepanova # +# Change Date: 2017-02-18 # +# Change: The test file is spawned from the mega-test partition_int_innodb # +################################################################################ + +# +# NOTE: PLEASE DO NOT ADD NOT INNODB SPECIFIC TESTCASES HERE ! +# TESTCASES WHICH MUST BE APPLIED TO ALL STORAGE ENGINES MUST BE ADDED IN +# THE SOURCED FILES ONLY. +# +# Please read the README at the end of inc/partition.pre before changing +# any of the variables. +# + +#------------------------------------------------------------------------------# +# General not engine specific settings and requirements + +##### Options, for debugging support ##### +let $debug= 0; + +# The server must support partitioning. +--source include/have_partition.inc + +#------------------------------------------------------------------------------# +# Engine specific settings and requirements + +##### Storage engine to be tested +--source include/have_innodb.inc +let $engine= 'InnoDB'; + +##### max rows to be inserted +let $maxrows=1024; + +#------------------------------------------------------------------------------# +# Execute the tests to be applied to all storage engines +--source suite/parts/inc/partition_tinyint.inc diff --git a/mysql-test/suite/parts/t/partition_tinyint_myisam.test b/mysql-test/suite/parts/t/partition_tinyint_myisam.test new file mode 100644 index 00000000000..9807bffb1da --- /dev/null +++ b/mysql-test/suite/parts/t/partition_tinyint_myisam.test @@ -0,0 +1,46 @@ +################################################################################ +# t/partition_tinyint_myisam.test # +# # +# Purpose: # +# Tests around integer type # +# MyISAM branch # +# # +#------------------------------------------------------------------------------# +# Original Author: HH # +# Original Date: 2006-08-01 # +# Change Author: Elena Stepanova # +# Change Date: 2017-02-18 # +# Change: The test file is spawned from the mega-test partition_int_myisam # +################################################################################ + +# +# NOTE: PLEASE DO NOT ADD NOT MYISAM SPECIFIC TESTCASES HERE ! +# TESTCASES WHICH MUST BE APPLIED TO ALL STORAGE ENGINES MUST BE ADDED IN +# THE SOURCED FILES ONLY. +# +# Please read the README at the end of inc/partition.pre before changing +# any of the variables. +# + +--source include/long_test.inc + +#------------------------------------------------------------------------------# +# General not engine specific settings and requirements + +##### Options, for debugging support ##### +let $debug= 0; + +# The server must support partitioning. +--source include/have_partition.inc + +#------------------------------------------------------------------------------# +# Engine specific settings and requirements + +##### Storage engine to be tested +let $engine= 'MYISAM'; +##### number of rows to be inserted +let $maxrows=65535; + +#------------------------------------------------------------------------------# +# Execute the tests to be applied to all storage engines +--source suite/parts/inc/partition_tinyint.inc diff --git a/mysql-test/suite/perfschema/include/pfs_no_running_event_scheduler.inc b/mysql-test/suite/perfschema/include/pfs_no_running_event_scheduler.inc new file mode 100644 index 00000000000..eff3d7df854 --- /dev/null +++ b/mysql-test/suite/perfschema/include/pfs_no_running_event_scheduler.inc @@ -0,0 +1,10 @@ +# threads are removed from: +# - information_schema.processlist +# - performance_schema.threads +# at different times, so we may have to wait a little more +# for the event_scheduler to shutdown +# +let $wait_condition= + SELECT COUNT(*) = 0 FROM performance_schema.threads + WHERE name like 'thread/sql/event%'; +--source include/wait_condition.inc diff --git a/mysql-test/suite/perfschema/include/pfs_running_event_scheduler.inc b/mysql-test/suite/perfschema/include/pfs_running_event_scheduler.inc new file mode 100644 index 00000000000..219a41051fb --- /dev/null +++ b/mysql-test/suite/perfschema/include/pfs_running_event_scheduler.inc @@ -0,0 +1,10 @@ +# threads are removed from: +# - information_schema.processlist +# - performance_schema.threads +# at different times, so we may have to wait a little more +# for the event_scheduler to shutdown +# +let $wait_condition= + SELECT COUNT(*) = 1 FROM performance_schema.threads + WHERE name like 'thread/sql/event%'; +--source include/wait_condition.inc diff --git a/mysql-test/suite/perfschema/t/table_name.test b/mysql-test/suite/perfschema/t/table_name.test index a8179f2d1f8..5fb8ccd0f7f 100644 --- a/mysql-test/suite/perfschema/t/table_name.test +++ b/mysql-test/suite/perfschema/t/table_name.test @@ -31,6 +31,7 @@ INSERT INTO `sql_1` VALUES(1,'one'); --echo --echo # Verify that the tables are treated as normal tables . --echo +--sorted_result SELECT object_type, object_schema, object_name FROM performance_schema.objects_summary_global_by_type WHERE object_schema="test"; diff --git a/mysql-test/suite/perfschema/t/threads_mysql.test b/mysql-test/suite/perfschema/t/threads_mysql.test index 8576c8767d6..c33f421863e 100644 --- a/mysql-test/suite/perfschema/t/threads_mysql.test +++ b/mysql-test/suite/perfschema/t/threads_mysql.test @@ -9,22 +9,10 @@ # Ensure that the event scheduler (started via threads_mysql-master.opt) # is really running. ---source include/running_event_scheduler.inc +--source include/pfs_running_event_scheduler.inc SET GLOBAL event_scheduler = OFF; ---source include/no_running_event_scheduler.inc - -# threads are removed from: -# - information_schema.processlist -# - performance_schema.threads -# at different times, so we may have to wait a little more -# for the event_scheduler to shutdown -# -let $wait_timeout= 1; -let $wait_condition= - SELECT COUNT(*) = 0 FROM performance_schema.threads - WHERE name like 'thread/sql/event%'; ---source include/wait_condition.inc +--source include/pfs_no_running_event_scheduler.inc --vertical_results @@ -59,7 +47,7 @@ WHERE name LIKE 'thread/sql%'; SET GLOBAL event_scheduler = ON; ---source include/running_event_scheduler.inc +--source include/pfs_running_event_scheduler.inc # Show entries belonging to the just started event scheduler SELECT name, type, processlist_user, processlist_host, processlist_db, diff --git a/mysql-test/suite/rpl/r/rpl_heartbeat_basic.result b/mysql-test/suite/rpl/r/rpl_heartbeat_basic.result index 91ed6d7a0cb..0cb1aed9905 100644 --- a/mysql-test/suite/rpl/r/rpl_heartbeat_basic.result +++ b/mysql-test/suite/rpl/r/rpl_heartbeat_basic.result @@ -7,7 +7,6 @@ RESET SLAVE; SET @restore_slave_net_timeout=@@global.slave_net_timeout; RESET MASTER; SET @restore_slave_net_timeout=@@global.slave_net_timeout; -SET @restore_event_scheduler=@@global.event_scheduler; *** Default value *** CHANGE MASTER TO MASTER_HOST='127.0.0.1', MASTER_PORT=MASTER_PORT, MASTER_USER='root'; @@ -221,7 +220,7 @@ RESET SLAVE; CHANGE MASTER TO MASTER_HOST='127.0.0.1', MASTER_PORT=MASTER_PORT, MASTER_USER='root', MASTER_CONNECT_RETRY=20, MASTER_HEARTBEAT_PERIOD=5; include/start_slave.inc SET @@global.event_scheduler=1; -Number of received heartbeat events: 0 +Received heartbeats meet expectations: TRUE DELETE FROM t1; DROP EVENT e1; diff --git a/mysql-test/suite/rpl/r/rpl_mdev6386.result b/mysql-test/suite/rpl/r/rpl_mdev6386.result index fa49d9a9c03..c8bd6f51822 100644 --- a/mysql-test/suite/rpl/r/rpl_mdev6386.result +++ b/mysql-test/suite/rpl/r/rpl_mdev6386.result @@ -1,7 +1,6 @@ include/master-slave.inc [connection master] ALTER TABLE mysql.gtid_slave_pos ENGINE = InnoDB; -FLUSH LOGS; CREATE TABLE t1 (a INT PRIMARY KEY, b INT) Engine=InnoDB; include/stop_slave.inc SET sql_log_bin= 0; @@ -22,6 +21,7 @@ INSERT INTO t2 VALUE (4, 1); INSERT INTO t2 VALUE (5, 1); INSERT INTO t1 SELECT * FROM t2; DROP TEMPORARY TABLE t2; +INSERT INTO t1 VALUE (6, 3); include/save_master_gtid.inc Contents on master: SELECT * FROM t1 ORDER BY a; @@ -31,6 +31,7 @@ a b 3 1 4 1 5 1 +6 3 START SLAVE; include/wait_for_slave_sql_error.inc [errno=1062] STOP SLAVE IO_THREAD; @@ -51,6 +52,7 @@ a b 3 1 4 1 5 1 +6 3 DROP TABLE t1; include/stop_slave.inc SET GLOBAL slave_parallel_threads= @old_parallel; diff --git a/mysql-test/suite/rpl/t/rpl_heartbeat_basic.test b/mysql-test/suite/rpl/t/rpl_heartbeat_basic.test index 5b55f11da85..4c8d3a1fedb 100644 --- a/mysql-test/suite/rpl/t/rpl_heartbeat_basic.test +++ b/mysql-test/suite/rpl/t/rpl_heartbeat_basic.test @@ -34,7 +34,6 @@ eval SET @restore_slave_heartbeat_timeout=$slave_heartbeat_timeout; --connection master RESET MASTER; SET @restore_slave_net_timeout=@@global.slave_net_timeout; -SET @restore_event_scheduler=@@global.event_scheduler; --echo # @@ -352,21 +351,54 @@ eval CHANGE MASTER TO MASTER_HOST='127.0.0.1', MASTER_PORT=$MASTER_MYPORT, MASTE --connection master # Enable scheduler SET @@global.event_scheduler=1; + --sync_slave_with_master let $rcvd_heartbeats_before= query_get_value(SHOW STATUS LIKE 'slave_received_heartbeats', Value, 1); -# Wait some updates for table t1 from master -let $wait_condition= SELECT COUNT(*)=1 FROM t1 WHERE a > 5; ---source include/wait_condition.inc + +--connection master + +# Whether or not to send a heartbeat is decided on the master, based on +# whether the binlog was updated during the period or not. +# Even with the 1-second event, we cannot make the master to write binary +# logs (or execute SQL) in a timely manner. We can only check that they +# were executed in a timely manner, and if they were not, neutralize the +# heartbeat check on the slave. +# We will wait for 5 events, and keep checking 'Binlog_commits' on master. +# Time interval between consequent events will be measured. +# We can only expect that no heartbeats have been sent if the interval +# between events never exceeded MASTER_HEARTBEAT_PERIOD. +# If it has exceeded the value at least once, the slave can legitimately +# receive a heartbeat (but we cannot require it, because the delay +# could have occurred somewhere else, e.g. upon checking the status). +# So, if the delay is detected, we will signal slave to ignore possible +# heartbeats. + +let $possible_heartbeats= 0; +let $commits_to_wait= 5; +while ($commits_to_wait) +{ + let $tm= `SELECT UNIX_TIMESTAMP(NOW(3))`; + let $binlog_commits= query_get_value(SHOW STATUS LIKE 'Binlog_commits', Value, 1); + let $wait_condition= SELECT VARIABLE_VALUE > $binlog_commits FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME= 'BINLOG_COMMITS'; + --source include/wait_condition.inc + dec $commits_to_wait; + if (`SELECT UNIX_TIMESTAMP(NOW(3)) > $tm + 5`) + { + let $possible_heartbeats= 1; + let $commits_to_wait= 0; + } +} + +--connection slave let $rcvd_heartbeats_after= query_get_value(SHOW STATUS LIKE 'slave_received_heartbeats', Value, 1); -let $result= query_get_value(SELECT ($rcvd_heartbeats_after - $rcvd_heartbeats_before) > 0 AS Result, Result, 1); ---echo Number of received heartbeat events: $result +let $result= `SELECT CASE WHEN $possible_heartbeats THEN 'TRUE' WHEN $rcvd_heartbeats_after - $rcvd_heartbeats_before > 0 THEN 'FALSE' ELSE 'TRUE' END`; +--echo Received heartbeats meet expectations: $result --connection master DELETE FROM t1; DROP EVENT e1; --sync_slave_with_master --echo - # Check received heartbeat events while logs flushed on slave --echo *** Flush logs on slave *** STOP SLAVE; diff --git a/mysql-test/suite/rpl/t/rpl_mdev6386.test b/mysql-test/suite/rpl/t/rpl_mdev6386.test index 3e4e79ea5a3..e85b1ae0132 100644 --- a/mysql-test/suite/rpl/t/rpl_mdev6386.test +++ b/mysql-test/suite/rpl/t/rpl_mdev6386.test @@ -2,9 +2,7 @@ --source include/master-slave.inc --connection master -# ToDo: Remove this FLUSH LOGS when MDEV-6403 is fixed. ALTER TABLE mysql.gtid_slave_pos ENGINE = InnoDB; -FLUSH LOGS; CREATE TABLE t1 (a INT PRIMARY KEY, b INT) Engine=InnoDB; --sync_slave_with_master @@ -31,6 +29,7 @@ INSERT INTO t2 VALUE (4, 1); INSERT INTO t2 VALUE (5, 1); INSERT INTO t1 SELECT * FROM t2; DROP TEMPORARY TABLE t2; +INSERT INTO t1 VALUE (6, 3); --source include/save_master_gtid.inc --echo Contents on master: SELECT * FROM t1 ORDER BY a; diff --git a/mysql-test/suite/sys_vars/r/innodb_force_recovery_crash_basic.result b/mysql-test/suite/sys_vars/r/innodb_force_recovery_crash_basic.result deleted file mode 100644 index 5af00f21c74..00000000000 --- a/mysql-test/suite/sys_vars/r/innodb_force_recovery_crash_basic.result +++ /dev/null @@ -1,33 +0,0 @@ -select @@global.innodb_force_recovery_crash in (0, 1); -@@global.innodb_force_recovery_crash in (0, 1) -1 -select @@global.innodb_force_recovery_crash; -@@global.innodb_force_recovery_crash -0 -select @@session.innodb_force_recovery_crash; -ERROR HY000: Variable 'innodb_force_recovery_crash' is a GLOBAL variable -show global variables like 'innodb_force_recovery_crash'; -Variable_name Value -innodb_force_recovery_crash 0 -show session variables like 'innodb_force_recovery_crash'; -Variable_name Value -innodb_force_recovery_crash 0 -select * from information_schema.global_variables where variable_name='innodb_force_recovery_crash'; -VARIABLE_NAME VARIABLE_VALUE -INNODB_FORCE_RECOVERY_CRASH 0 -select * from information_schema.session_variables where variable_name='innodb_force_recovery_crash'; -VARIABLE_NAME VARIABLE_VALUE -INNODB_FORCE_RECOVERY_CRASH 0 -set global innodb_force_recovery_crash=1; -ERROR HY000: Variable 'innodb_force_recovery_crash' is a read only variable -set global innodb_force_recovery_crash=0; -ERROR HY000: Variable 'innodb_force_recovery_crash' is a read only variable -select @@global.innodb_force_recovery_crash; -@@global.innodb_force_recovery_crash -0 -set session innodb_force_recovery_crash='some'; -ERROR HY000: Variable 'innodb_force_recovery_crash' is a read only variable -set @@session.innodb_force_recovery_crash='some'; -ERROR HY000: Variable 'innodb_force_recovery_crash' is a read only variable -set global innodb_force_recovery_crash='some'; -ERROR HY000: Variable 'innodb_force_recovery_crash' is a read only variable diff --git a/mysql-test/suite/sys_vars/r/innodb_stats_include_delete_marked_basic.result b/mysql-test/suite/sys_vars/r/innodb_stats_include_delete_marked_basic.result new file mode 100644 index 00000000000..ffd208e7927 --- /dev/null +++ b/mysql-test/suite/sys_vars/r/innodb_stats_include_delete_marked_basic.result @@ -0,0 +1,25 @@ +SELECT @@innodb_stats_include_delete_marked; +@@innodb_stats_include_delete_marked +0 +SET GLOBAL innodb_stats_include_delete_marked=1; +SELECT @@innodb_stats_include_delete_marked; +@@innodb_stats_include_delete_marked +1 +SET SESSION innodb_stats_include_delete_marked=1; +ERROR HY000: Variable 'innodb_stats_include_delete_marked' is a GLOBAL variable and should be set with SET GLOBAL +SET GLOBAL innodb_stats_include_delete_marked=100; +ERROR 42000: Variable 'innodb_stats_include_delete_marked' can't be set to the value of '100' +SET GLOBAL innodb_stats_include_delete_marked=foo; +ERROR 42000: Variable 'innodb_stats_include_delete_marked' can't be set to the value of 'foo' +SET GLOBAL innodb_stats_include_delete_marked=OFF ; +SELECT @@innodb_stats_include_delete_marked; +@@innodb_stats_include_delete_marked +0 +SET GLOBAL innodb_stats_include_delete_marked=ON ; +SELECT @@innodb_stats_include_delete_marked; +@@innodb_stats_include_delete_marked +1 +SET GLOBAL innodb_stats_include_delete_marked=Default ; +SELECT @@innodb_stats_include_delete_marked; +@@innodb_stats_include_delete_marked +0 diff --git a/mysql-test/suite/sys_vars/t/innodb_force_recovery_crash_basic.test b/mysql-test/suite/sys_vars/t/innodb_force_recovery_crash_basic.test deleted file mode 100644 index 5eefe1b9219..00000000000 --- a/mysql-test/suite/sys_vars/t/innodb_force_recovery_crash_basic.test +++ /dev/null @@ -1,28 +0,0 @@ ---source include/have_innodb.inc ---source include/have_debug.inc - -# -# exists as global only -# -select @@global.innodb_force_recovery_crash in (0, 1); -select @@global.innodb_force_recovery_crash; ---error ER_INCORRECT_GLOBAL_LOCAL_VAR -select @@session.innodb_force_recovery_crash; -show global variables like 'innodb_force_recovery_crash'; -show session variables like 'innodb_force_recovery_crash'; -select * from information_schema.global_variables where variable_name='innodb_force_recovery_crash'; -select * from information_schema.session_variables where variable_name='innodb_force_recovery_crash'; - -# show that it's read-only -# ---error ER_INCORRECT_GLOBAL_LOCAL_VAR -set global innodb_force_recovery_crash=1; ---error ER_INCORRECT_GLOBAL_LOCAL_VAR -set global innodb_force_recovery_crash=0; -select @@global.innodb_force_recovery_crash; ---error ER_INCORRECT_GLOBAL_LOCAL_VAR -set session innodb_force_recovery_crash='some'; ---error ER_INCORRECT_GLOBAL_LOCAL_VAR -set @@session.innodb_force_recovery_crash='some'; ---error ER_INCORRECT_GLOBAL_LOCAL_VAR -set global innodb_force_recovery_crash='some'; diff --git a/mysql-test/suite/sys_vars/t/innodb_stats_include_delete_marked_basic.test b/mysql-test/suite/sys_vars/t/innodb_stats_include_delete_marked_basic.test new file mode 100644 index 00000000000..2a3a0f9b44e --- /dev/null +++ b/mysql-test/suite/sys_vars/t/innodb_stats_include_delete_marked_basic.test @@ -0,0 +1,53 @@ +############################################################################### +# # +# Variable Name: innodb_stats_include_delete_marked # +# Scope: Global # +# Access Type: Dynamic # +# Data Type: numeric # +# # +# # +# Creation Date: 2016-08-29 # +# Author : Aditya # +# # +# # +# Description: # +# * Value check # +# * Scope check # +# # +############################################################################### + +--source include/have_innodb.inc + +#################################################################### +# Display default value # +#################################################################### +SELECT @@innodb_stats_include_delete_marked; + +SET GLOBAL innodb_stats_include_delete_marked=1; + +SELECT @@innodb_stats_include_delete_marked; + +# check error +--error ER_GLOBAL_VARIABLE +SET SESSION innodb_stats_include_delete_marked=1; + +# check error +--error ER_WRONG_VALUE_FOR_VAR +SET GLOBAL innodb_stats_include_delete_marked=100; + +# check error +--error ER_WRONG_VALUE_FOR_VAR +SET GLOBAL innodb_stats_include_delete_marked=foo; + +SET GLOBAL innodb_stats_include_delete_marked=OFF ; + +SELECT @@innodb_stats_include_delete_marked; + +SET GLOBAL innodb_stats_include_delete_marked=ON ; + +SELECT @@innodb_stats_include_delete_marked; + +# Check with default setting +SET GLOBAL innodb_stats_include_delete_marked=Default ; + +SELECT @@innodb_stats_include_delete_marked; diff --git a/mysql-test/suite/sys_vars/t/secure_file_priv.test b/mysql-test/suite/sys_vars/t/secure_file_priv.test index 5c53da58275..a5a465d8c98 100644 --- a/mysql-test/suite/sys_vars/t/secure_file_priv.test +++ b/mysql-test/suite/sys_vars/t/secure_file_priv.test @@ -21,6 +21,9 @@ SHOW VARIABLES LIKE 'secure_file_priv'; --perl use File::Basename; my $protected_file= dirname($ENV{MYSQLTEST_VARDIR}).'/bug50373.txt'; +# Ensure bug50373.txt does not exist (e.g. leftover from previous +# test runs). +unlink $protected_file; open(FILE, ">", "$ENV{MYSQL_TMP_DIR}/bug50373.inc") or die; print FILE "SELECT * FROM t1 INTO OUTFILE '".$protected_file."';\n"; print FILE "DELETE FROM t1;\n"; diff --git a/mysql-test/suite/vcol/inc/vcol_trigger_sp.inc b/mysql-test/suite/vcol/inc/vcol_trigger_sp.inc index eb7e6ad32b9..b9a6ba28a5f 100644 --- a/mysql-test/suite/vcol/inc/vcol_trigger_sp.inc +++ b/mysql-test/suite/vcol/inc/vcol_trigger_sp.inc @@ -110,7 +110,7 @@ drop table t1,t2; drop procedure p1; --echo # ---echo # Bug mdev-3845: values of virtual columns are not computed for triggers +--echo # MDEV-3845 values of virtual columns are not computed for triggers --echo # CREATE TABLE t1 ( @@ -149,3 +149,10 @@ DROP TRIGGER t1_ins_aft; DROP TRIGGER t1_del_bef; DROP TABLE t1,t2; +# +# MDEV-11706 Assertion `is_stat_field || !table || (!table->write_set || bitmap_is_set(table->write_set, field_index) || (table->vcol_set && bitmap_is_set(table->vcol_set, field_index)))' failed in Field_time::store_TIME_with_warning +# +create table t1 (i int, t time not null, vt time(4) as (t) virtual); +create trigger trg before update on t1 for each row set @a = 1; +insert ignore into t1 (i) values (1); +drop table t1; diff --git a/mysql-test/suite/vcol/r/vcol_misc.result b/mysql-test/suite/vcol/r/vcol_misc.result index 0aaed59ed6c..699b6d4efe3 100644 --- a/mysql-test/suite/vcol/r/vcol_misc.result +++ b/mysql-test/suite/vcol/r/vcol_misc.result @@ -330,3 +330,10 @@ t1 CREATE TABLE `t1` ( `c1` varchar(50) COLLATE latin1_general_ci DEFAULT NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci drop table t1; +set sql_mode='no_zero_date'; +create table t1 ( +ts timestamp not null default current_timestamp, +tsv timestamp as (adddate(ts, interval 1 day)) virtual +); +drop table t1; +set sql_mode=default; diff --git a/mysql-test/suite/vcol/r/vcol_select_myisam.result b/mysql-test/suite/vcol/r/vcol_select_myisam.result index 934d047f6bf..6dee132b3e5 100644 --- a/mysql-test/suite/vcol/r/vcol_select_myisam.result +++ b/mysql-test/suite/vcol/r/vcol_select_myisam.result @@ -295,3 +295,112 @@ Note 1003 select `test`.`t1`.`b` AS `b`,`test`.`t2`.`a` AS `a` from `test`.`t1` SELECT * FROM t1 NATURAL JOIN t2; b a DROP TABLE t1,t2; +create table t1 ( +pk integer auto_increment, +bi integer not null, +vi integer generated always as (bi) persistent, +bc varchar(1) not null, +vc varchar(2) generated always as (concat(bc, bc)) persistent, +primary key (pk), +key (vi, vc)); +insert t1 (bi, bc) values (0, 'x'), (0, 'n'), (1, 'w'), (7, 's'), (0, 'a'), (4, 'd'), (1, 'w'), (1, 'j'), (1, 'm'), (4, 'k'), (7, 't'), (4, 'k'), (2, 'e'), (0, 'i'), (1, 't'), (6, 'z'), (3, 'c'), (6, 'i'), (8, 'v'); +create table t2 ( +pk integer auto_increment, +bi integer not null, +vi integer generated always as (bi) persistent, +bc varchar(257) not null, +vc varchar(2) generated always as (concat(bc, bc)) persistent, +primary key (pk), +key (vi, vc)); +insert t2 (bi, bc) values (1, 'c'), (8, 'm'), (9, 'd'), (6, 'y'), (1, 't'), (6, 'd'), (2, 's'), (4, 'r'), (8, 'm'), (4, 'b'), (4, 'x'), (7, 'g'), (4, 'p'), (1, 'q'), (9, 'w'), (4, 'd'), (8, 'e'), (4, 'b'), (8, 'y'); +explain # should be using join buffer +select t2.vi from (t2 as t3 right join (t2 left join t1 on (t1.bi = t2.vi)) on (t1.vc = t2.vc)); +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t2 index NULL vi 10 NULL 19 Using index +1 SIMPLE t1 ALL NULL NULL NULL NULL 19 Using where; Using join buffer (flat, BNL join) +1 SIMPLE t3 index NULL PRIMARY 4 NULL 19 Using where; Using index; Using join buffer (incremental, BNL join) +select t2.vi from (t2 as t3 right join (t2 left join t1 on (t1.bi = t2.vi)) on (t1.vc = t2.vc)); +vi +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +2 +4 +4 +4 +4 +4 +4 +4 +4 +4 +4 +4 +4 +4 +4 +4 +4 +4 +4 +4 +4 +4 +4 +4 +4 +4 +4 +4 +4 +4 +4 +4 +4 +4 +4 +4 +4 +6 +6 +6 +6 +7 +7 +8 +8 +8 +8 +9 +9 +drop table t2,t1; diff --git a/mysql-test/suite/vcol/r/vcol_trigger_sp_innodb.result b/mysql-test/suite/vcol/r/vcol_trigger_sp_innodb.result index 1d78bbf50e4..034a67a9bb0 100644 --- a/mysql-test/suite/vcol/r/vcol_trigger_sp_innodb.result +++ b/mysql-test/suite/vcol/r/vcol_trigger_sp_innodb.result @@ -86,7 +86,7 @@ a b c drop table t1,t2; drop procedure p1; # -# Bug mdev-3845: values of virtual columns are not computed for triggers +# MDEV-3845 values of virtual columns are not computed for triggers # CREATE TABLE t1 ( a INTEGER UNSIGNED NULL DEFAULT NULL, @@ -125,3 +125,9 @@ c DROP TRIGGER t1_ins_aft; DROP TRIGGER t1_del_bef; DROP TABLE t1,t2; +create table t1 (i int, t time not null, vt time(4) as (t) virtual); +create trigger trg before update on t1 for each row set @a = 1; +insert ignore into t1 (i) values (1); +Warnings: +Warning 1364 Field 't' doesn't have a default value +drop table t1; diff --git a/mysql-test/suite/vcol/r/vcol_trigger_sp_myisam.result b/mysql-test/suite/vcol/r/vcol_trigger_sp_myisam.result index 77efa8fe6b9..07d011ac64c 100644 --- a/mysql-test/suite/vcol/r/vcol_trigger_sp_myisam.result +++ b/mysql-test/suite/vcol/r/vcol_trigger_sp_myisam.result @@ -86,7 +86,7 @@ a b c drop table t1,t2; drop procedure p1; # -# Bug mdev-3845: values of virtual columns are not computed for triggers +# MDEV-3845 values of virtual columns are not computed for triggers # CREATE TABLE t1 ( a INTEGER UNSIGNED NULL DEFAULT NULL, @@ -125,3 +125,9 @@ c DROP TRIGGER t1_ins_aft; DROP TRIGGER t1_del_bef; DROP TABLE t1,t2; +create table t1 (i int, t time not null, vt time(4) as (t) virtual); +create trigger trg before update on t1 for each row set @a = 1; +insert ignore into t1 (i) values (1); +Warnings: +Warning 1364 Field 't' doesn't have a default value +drop table t1; diff --git a/mysql-test/suite/vcol/r/wrong_arena.result b/mysql-test/suite/vcol/r/wrong_arena.result new file mode 100644 index 00000000000..172b59d6c4c --- /dev/null +++ b/mysql-test/suite/vcol/r/wrong_arena.result @@ -0,0 +1,61 @@ +create table t1 (a datetime, +# get_datetime_value +b int as (a > 1), # Arg_comparator +c int as (a in (1,2,3)), # in_datetime +d int as ((a,a) in ((1,1),(2,1),(NULL,1))), # cmp_item_datetime +# other issues +e int as ((a,1) in ((1,1),(2,1),(NULL,1))) # cmp_item_row::alloc_comparators() +); +Warnings: +Warning 1292 Incorrect datetime value: '1' +Warning 1292 Incorrect datetime value: '2' +Warning 1292 Incorrect datetime value: '3' +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` datetime DEFAULT NULL, + `b` int(11) AS (a > 1) VIRTUAL, + `c` int(11) AS (a in (1,2,3)) VIRTUAL, + `d` int(11) AS ((a,a) in ((1,1),(2,1),(NULL,1))) VIRTUAL, + `e` int(11) AS ((a,1) in ((1,1),(2,1),(NULL,1))) VIRTUAL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +Warnings: +Warning 1292 Incorrect datetime value: '1' +Warning 1292 Incorrect datetime value: '2' +Warning 1292 Incorrect datetime value: '3' +insert t1 (a) values ('2010-10-10 10:10:10'); +select * from t1; +a b c d e +2010-10-10 10:10:10 1 0 0 NULL +Warnings: +Warning 1292 Incorrect datetime value: '1' +Warning 1292 Incorrect datetime value: '1' +Warning 1292 Incorrect datetime value: '2' +Warning 1292 Incorrect datetime value: '1' +Warning 1292 Incorrect datetime value: '1' +Warning 1292 Incorrect datetime value: '2' +select * from t1; +a b c d e +2010-10-10 10:10:10 1 0 0 NULL +Warnings: +Warning 1292 Incorrect datetime value: '1' +Warning 1292 Incorrect datetime value: '2' +Warning 1292 Incorrect datetime value: '1' +Warning 1292 Incorrect datetime value: '1' +Warning 1292 Incorrect datetime value: '2' +drop table t1; +create table t1 (a datetime, +b datetime as (least(a,1)) # Item_func_min_max::get_date +); +insert t1 (a) values ('2010-10-10 10:10:10'); +select * from t1; +a b +2010-10-10 10:10:10 0000-00-00 00:00:00 +Warnings: +Warning 1292 Incorrect datetime value: '1' +select * from t1; +a b +2010-10-10 10:10:10 0000-00-00 00:00:00 +Warnings: +Warning 1292 Incorrect datetime value: '1' +drop table t1; diff --git a/mysql-test/suite/vcol/t/vcol_misc.test b/mysql-test/suite/vcol/t/vcol_misc.test index 12f46e9b002..80a36d9c623 100644 --- a/mysql-test/suite/vcol/t/vcol_misc.test +++ b/mysql-test/suite/vcol/t/vcol_misc.test @@ -290,3 +290,14 @@ create table t1 (a int, b int as (b is null) virtual); create table t1 (v1 varchar(255) as (c1) persistent, c1 varchar(50)) collate=latin1_general_ci; show create table t1; drop table t1; + +# +# MDEV-11527 Virtual columns do not get along well with NO_ZERO_DATE +# +set sql_mode='no_zero_date'; +create table t1 ( + ts timestamp not null default current_timestamp, + tsv timestamp as (adddate(ts, interval 1 day)) virtual +); +drop table t1; +set sql_mode=default; diff --git a/mysql-test/suite/vcol/t/vcol_select_myisam.test b/mysql-test/suite/vcol/t/vcol_select_myisam.test index c14faba576d..b392b74c2d9 100644 --- a/mysql-test/suite/vcol/t/vcol_select_myisam.test +++ b/mysql-test/suite/vcol/t/vcol_select_myisam.test @@ -68,3 +68,35 @@ SELECT * FROM t1 NATURAL JOIN t2; SELECT * FROM t1 NATURAL JOIN t2; DROP TABLE t1,t2; + +# +# MDEV-11525 Assertion `cp + len <= buff + buff_size' failed in JOIN_CACHE::write_record_data +# + +create table t1 ( + pk integer auto_increment, + bi integer not null, + vi integer generated always as (bi) persistent, + bc varchar(1) not null, + vc varchar(2) generated always as (concat(bc, bc)) persistent, + primary key (pk), + key (vi, vc)); +insert t1 (bi, bc) values (0, 'x'), (0, 'n'), (1, 'w'), (7, 's'), (0, 'a'), (4, 'd'), (1, 'w'), (1, 'j'), (1, 'm'), (4, 'k'), (7, 't'), (4, 'k'), (2, 'e'), (0, 'i'), (1, 't'), (6, 'z'), (3, 'c'), (6, 'i'), (8, 'v'); +create table t2 ( + pk integer auto_increment, + bi integer not null, + vi integer generated always as (bi) persistent, + bc varchar(257) not null, + vc varchar(2) generated always as (concat(bc, bc)) persistent, + primary key (pk), + key (vi, vc)); +insert t2 (bi, bc) values (1, 'c'), (8, 'm'), (9, 'd'), (6, 'y'), (1, 't'), (6, 'd'), (2, 's'), (4, 'r'), (8, 'm'), (4, 'b'), (4, 'x'), (7, 'g'), (4, 'p'), (1, 'q'), (9, 'w'), (4, 'd'), (8, 'e'), (4, 'b'), (8, 'y'); +explain # should be using join buffer +select t2.vi from (t2 as t3 right join (t2 left join t1 on (t1.bi = t2.vi)) on (t1.vc = t2.vc)); +--sorted_result +select t2.vi from (t2 as t3 right join (t2 left join t1 on (t1.bi = t2.vi)) on (t1.vc = t2.vc)); +drop table t2,t1; + +# +# End of 5.5 tests +# diff --git a/mysql-test/suite/vcol/t/wrong_arena.test b/mysql-test/suite/vcol/t/wrong_arena.test new file mode 100644 index 00000000000..484f1fe685d --- /dev/null +++ b/mysql-test/suite/vcol/t/wrong_arena.test @@ -0,0 +1,35 @@ +# +# This tests various issues when vcol items allocate memory (e.g. more items) +# not in the TABLE::expr_arena. +# + +# +# MDEV-9690 concurrent queries with virtual columns crash in temporal code +# +create table t1 (a datetime, + # get_datetime_value + b int as (a > 1), # Arg_comparator + c int as (a in (1,2,3)), # in_datetime + d int as ((a,a) in ((1,1),(2,1),(NULL,1))), # cmp_item_datetime + # other issues + e int as ((a,1) in ((1,1),(2,1),(NULL,1))) # cmp_item_row::alloc_comparators() +); +show create table t1; +connect con1, localhost, root; +insert t1 (a) values ('2010-10-10 10:10:10'); +select * from t1; +disconnect con1; +connection default; +select * from t1; +drop table t1; + +connect con1, localhost, root; +create table t1 (a datetime, + b datetime as (least(a,1)) # Item_func_min_max::get_date +); +insert t1 (a) values ('2010-10-10 10:10:10'); +select * from t1; +disconnect con1; +connection default; +select * from t1; +drop table t1; diff --git a/mysql-test/t/alter_table.test b/mysql-test/t/alter_table.test index 6fe0f4d30ee..3670e871bb0 100644 --- a/mysql-test/t/alter_table.test +++ b/mysql-test/t/alter_table.test @@ -1756,3 +1756,14 @@ REFERENCES t1 (id); DROP TABLE t2; DROP TABLE t1; + +--echo # +--echo # MDEV-6390 CONVERT TO CHARACTER SET utf8 doesn't change DEFAULT CHARSET. +--echo # + +CREATE TABLE t1 (id int(11) NOT NULL, a int(11) NOT NULL, b int(11)) + ENGINE=InnoDB DEFAULT CHARSET=latin1; +SHOW CREATE TABLE t1; +ALTER TABLE t1 CONVERT TO CHARACTER SET utf8; +SHOW CREATE TABLE t1; +DROP TABLE t1; diff --git a/mysql-test/t/derived.test b/mysql-test/t/derived.test index b517dfd29d1..28e48bf03c0 100644 --- a/mysql-test/t/derived.test +++ b/mysql-test/t/derived.test @@ -875,4 +875,27 @@ SELECT Customer, Success, SUM(OrderSize) DROP TABLE example1463; set sql_mode= @save_sql_mode; +--echo # +--echo # MDEV-9028: SELECT DISTINCT constant column of derived table +--echo # used as the second operand of LEFT JOIN +--echo # + +create table t1 (id int, data varchar(255)); +insert into t1 values (1,'yes'),(2,'yes'); + +select distinct t1.id, tt.id, tt.data + from t1 + left join + (select t1.id, 'yes' as data from t1) as tt + on t1.id = tt.id; + +select distinct t1.id, tt.id, tt.data + from t1 + left join + (select t1.id, 'yes' as data from t1 where id > 1) as tt + on t1.id = tt.id; + +drop table t1; + + --echo # end of 5.5 diff --git a/mysql-test/t/grant.test b/mysql-test/t/grant.test index 548fd0f810e..0be4c254269 100644 --- a/mysql-test/t/grant.test +++ b/mysql-test/t/grant.test @@ -2176,6 +2176,68 @@ DROP USER mysqltest_u1@localhost; --echo # End of Bug#38347. --echo + +--echo # +--echo # BUG#11759114 - '51401: GRANT TREATS NONEXISTENT FUNCTIONS/PRIVILEGES +--echo # DIFFERENTLY'. +--echo # +--disable_warnings +drop database if exists mysqltest_db1; +--enable_warnings +create database mysqltest_db1; +create user mysqltest_u1; +--echo # Both GRANT statements below should fail with the same error. +--error ER_SP_DOES_NOT_EXIST +grant execute on function mysqltest_db1.f1 to mysqltest_u1; +--error ER_SP_DOES_NOT_EXIST +grant execute on procedure mysqltest_db1.p1 to mysqltest_u1; +--echo # Let us show that GRANT behaviour for routines is consistent +--echo # with GRANT behaviour for tables. Attempt to grant privilege +--echo # on non-existent table also results in an error. +--error ER_NO_SUCH_TABLE +grant select on mysqltest_db1.t1 to mysqltest_u1; +show grants for mysqltest_u1; +drop database mysqltest_db1; +drop user mysqltest_u1; + + +--echo # +--echo # Bug#12766319 - 61865: RENAME USER DOES NOT WORK CORRECTLY - +--echo # REQUIRES FLUSH PRIVILEGES +--echo # + +CREATE USER foo@'127.0.0.1'; +GRANT ALL ON *.* TO foo@'127.0.0.1'; + +--echo # First attempt, should connect successfully +connect (conn1, '127.0.0.1', foo,,test); +SELECT user(), current_user(); + +--echo # Rename the user +RENAME USER foo@'127.0.0.1' to foo@'127.0.0.0/255.0.0.0'; + +--echo # Second attempt, should connect successfully as its valid mask +--echo # This was failing without fix +connect (conn2, '127.0.0.1', foo,,test); +SELECT user(), current_user(); + +--echo # Rename the user back to original +RENAME USER foo@'127.0.0.0/255.0.0.0' to foo@'127.0.0.1'; + +--echo # Third attempt, should connect successfully +connect (conn3, '127.0.0.1', foo,,test); +SELECT user(), current_user(); + +--echo # Clean-up +connection default; +disconnect conn1; +disconnect conn2; +disconnect conn3; +DROP USER foo@'127.0.0.1'; + +--echo # End of Bug#12766319 + + --echo # --echo # Bug#11756966 - 48958: STORED PROCEDURES CAN BE LEVERAGED TO BYPASS --echo # DATABASE SECURITY @@ -2210,26 +2272,3 @@ DROP DATABASE secret; # Wait till we reached the initial number of concurrent sessions --source include/wait_until_count_sessions.inc - ---echo # ---echo # BUG#11759114 - '51401: GRANT TREATS NONEXISTENT FUNCTIONS/PRIVILEGES ---echo # DIFFERENTLY'. ---echo # ---disable_warnings -drop database if exists mysqltest_db1; ---enable_warnings -create database mysqltest_db1; -create user mysqltest_u1; ---echo # Both GRANT statements below should fail with the same error. ---error ER_SP_DOES_NOT_EXIST -grant execute on function mysqltest_db1.f1 to mysqltest_u1; ---error ER_SP_DOES_NOT_EXIST -grant execute on procedure mysqltest_db1.p1 to mysqltest_u1; ---echo # Let us show that GRANT behaviour for routines is consistent ---echo # with GRANT behaviour for tables. Attempt to grant privilege ---echo # on non-existent table also results in an error. ---error ER_NO_SUCH_TABLE -grant select on mysqltest_db1.t1 to mysqltest_u1; -show grants for mysqltest_u1; -drop database mysqltest_db1; -drop user mysqltest_u1; diff --git a/mysql-test/t/join_nested.test b/mysql-test/t/join_nested.test index 7b7d9236835..e60b7827f75 100644 --- a/mysql-test/t/join_nested.test +++ b/mysql-test/t/join_nested.test @@ -1309,5 +1309,74 @@ LEFT JOIN t4 AS alias5 JOIN t5 ON alias5.f5 ON alias2.f3 ON alias1.f2; DROP TABLE t1,t2,t3,t4,t5; -set optimizer_search_depth= @tmp_mdev621; +--echo # +--echo # MDEV-7992: Nested left joins + 'not exists' optimization +--echo # + +CREATE TABLE t1( + K1 INT PRIMARY KEY, + Name VARCHAR(15) +); + +INSERT INTO t1 VALUES + (1,'T1Row1'), (2,'T1Row2'); + + +CREATE TABLE t2( + K2 INT PRIMARY KEY, + K1r INT, + rowTimestamp DATETIME, + Event VARCHAR(15) +); + +INSERT INTO t2 VALUES + (1, 1, '2015-04-13 10:42:11' ,'T1Row1Event1'), + (2, 1, '2015-04-13 10:42:12' ,'T1Row1Event2'), + (3, 1, '2015-04-13 10:42:12' ,'T1Row1Event3'); + +let $q1= +SELECT t1a.*, t2a.*, + t2i.K2 AS K2B, t2i.K1r AS K1rB, + t2i.rowTimestamp AS rowTimestampB, t2i.Event AS EventB +FROM + t1 t1a JOIN t2 t2a ON t2a.K1r = t1a.K1 + LEFT JOIN + ( t1 t1i LEFT JOIN t2 t2i ON t2i.K1r = t1i.K1) + ON (t1i.K1 = 1) AND + (((t2i.K1r = t1a.K1 AND t2i.rowTimestamp > t2a.rowTimestamp ) OR + (t2i.rowTimestamp = t2a.rowTimestamp AND t2i.K2 > t2a.K2)) + OR (t2i.K2 IS NULL)) +WHERE +t2a.K1r = 1 AND t2i.K2 IS NULL; + +eval $q1; +eval EXPLAIN EXTENDED $q1; + +CREATE VIEW v1 AS + SELECT t2i.* + FROM t1 as t1i LEFT JOIN t2 as t2i ON t2i.K1r = t1i.K1 + WHERE t1i.K1 = 1 ; + +let $q2= +SELECT + t1a.*, t2a.*, t2b.K2 as K2B, t2b.K1r as K1rB, + t2b.rowTimestamp as rowTimestampB, t2b.Event as EventB +FROM + t1 as t1a JOIN t2 as t2a ON t2a.K1r = t1a.K1 + LEFT JOIN + v1 as t2b + ON ((t2b.K1r = t1a.K1 AND t2b.rowTimestamp > t2a.rowTimestamp) OR + (t2b.rowTimestamp = t2a.rowTimestamp AND t2b.K2 > t2a.K2)) + OR (t2b.K2 IS NULL) +WHERE + t1a.K1 = 1 AND + t2b.K2 IS NULL; + +eval $q2; +eval EXPLAIN EXTENDED $q2; + +DROP VIEW v1; +DROP TABLE t1,t2; + +set optimizer_search_depth= @tmp_mdev621; diff --git a/mysql-test/t/mysqldump.test b/mysql-test/t/mysqldump.test index fd50896b71f..708ed25f89c 100644 --- a/mysql-test/t/mysqldump.test +++ b/mysql-test/t/mysqldump.test @@ -2512,3 +2512,11 @@ if (`select convert(@@version_compile_os using latin1) IN ("Win32","Win64","Wind } --exec $MYSQL_DUMP --routines --compact $shell_ready_db_name DROP DATABASE `a\"'``b`; + +#" +# MDEV-11505 wrong databasename in mysqldump comment +# +let SEARCH_FILE=$MYSQLTEST_VARDIR/tmp/bug11505.sql; +let SEARCH_PATTERN=Database: mysql; +exec $MYSQL_DUMP mysql func > $SEARCH_FILE; +source include/search_pattern_in_file.inc; diff --git a/mysql-test/t/partition_innodb.test b/mysql-test/t/partition_innodb.test index 6845cca10fb..f6faa4cb0e6 100644 --- a/mysql-test/t/partition_innodb.test +++ b/mysql-test/t/partition_innodb.test @@ -820,6 +820,104 @@ INSERT INTO t1 (d) VALUES ('1991-01-01'); SELECT * FROM t1 WHERE d = '1991-01-01'; DROP TABLE t1; +set global default_storage_engine=default; + +--echo # +--echo # MDEV-9455: [ERROR] mysqld got signal 11 +--echo # + +CREATE TABLE `t1` ( + `DIARY_TOTAL_DAY_SEQ` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `IMORY_ID` bigint(20) NOT NULL, + `NAME` varchar(75) DEFAULT NULL, + `DATETIME` varchar(10) NOT NULL DEFAULT '', + `DAILY_CALL_CNT` int(11) DEFAULT NULL, + `DAILY_SMS_CNT` int(11) DEFAULT NULL, + `NUMBER` varchar(64) DEFAULT NULL, + `DURATION` varchar(16) DEFAULT NULL, + PRIMARY KEY (`DIARY_TOTAL_DAY_SEQ`,`DATETIME`), + KEY `IDX_t1_01` (`IMORY_ID`,`DATETIME`) +) AUTO_INCREMENT=328702514 DEFAULT CHARSET=utf8mb4 +PARTITION BY RANGE COLUMNS(`DATETIME`) +(PARTITION p0 VALUES LESS THAN ('2015-10-01') ENGINE = InnoDB, + PARTITION p1 VALUES LESS THAN ('2015-11-01') ENGINE = InnoDB, + PARTITION p2 VALUES LESS THAN ('2015-12-01') ENGINE = InnoDB, + PARTITION p3 VALUES LESS THAN ('2016-01-01') ENGINE = InnoDB, + PARTITION p4 VALUES LESS THAN ('2016-02-01') ENGINE = InnoDB, + PARTITION p5 VALUES LESS THAN ('2016-03-01') ENGINE = InnoDB, + PARTITION p6 VALUES LESS THAN ('2016-04-01') ENGINE = InnoDB, + PARTITION p7 VALUES LESS THAN ('2016-05-01') ENGINE = InnoDB, + PARTITION p8 VALUES LESS THAN ('2016-06-01') ENGINE = InnoDB, + PARTITION p9 VALUES LESS THAN ('2016-07-01') ENGINE = InnoDB, + PARTITION p10 VALUES LESS THAN ('2016-08-01') ENGINE = InnoDB) +; + +CREATE TABLE `t2` ( + `DIARY_SEQ` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `IMORY_ID` bigint(20) NOT NULL, + `CALL_TYPE` varchar(1) DEFAULT NULL, + `DATA_TYPE` varchar(1) DEFAULT NULL, + `FEATURES` varchar(1) DEFAULT NULL, + `NAME` varchar(75) DEFAULT NULL, + `NUMBER` varchar(64) DEFAULT NULL, + `DATETIME` datetime NOT NULL, + `REG_DATE` datetime NOT NULL, + `TITLE` varchar(50) DEFAULT NULL, + `BODY` varchar(4200) DEFAULT NULL, + `MIME_TYPE` varchar(32) DEFAULT NULL, + `DURATION` varchar(16) DEFAULT NULL, + `DEVICE_ID` varchar(64) DEFAULT NULL, + `DEVICE_NAME` varchar(32) DEFAULT NULL, + PRIMARY KEY (`DIARY_SEQ`,`DATETIME`,`REG_DATE`), + KEY `IDX_TB_DIARY_01` (`IMORY_ID`,`DATETIME`,`CALL_TYPE`,`NUMBER`), + KEY `IDX_TB_DIARY_02` (`REG_DATE`) +) AUTO_INCREMENT=688799006 DEFAULT CHARSET=utf8mb4 +PARTITION BY RANGE COLUMNS(REG_DATE) +(PARTITION p0 VALUES LESS THAN ('2015-10-01') ENGINE = InnoDB, + PARTITION p1 VALUES LESS THAN ('2015-11-01') ENGINE = InnoDB, + PARTITION p2 VALUES LESS THAN ('2015-12-01') ENGINE = InnoDB, + PARTITION p3 VALUES LESS THAN ('2016-01-01') ENGINE = InnoDB, + PARTITION p4 VALUES LESS THAN ('2016-02-01') ENGINE = InnoDB, + PARTITION p5 VALUES LESS THAN ('2016-03-01') ENGINE = InnoDB, + PARTITION p6 VALUES LESS THAN ('2016-04-01') ENGINE = InnoDB, + PARTITION p7 VALUES LESS THAN ('2016-05-01') ENGINE = InnoDB, + PARTITION p8 VALUES LESS THAN ('2016-06-01') ENGINE = InnoDB, + PARTITION p9 VALUES LESS THAN ('2016-07-01') ENGINE = InnoDB, + PARTITION p10 VALUES LESS THAN ('2016-08-01') ENGINE = InnoDB) +; + +SELECT + A.IMORY_ID, + A.NUMBER, + A.NAME, + DATE_FORMAT(A.DATETIME, '%Y-%m-%d') AS TARGET_DATE, + SUM( CASE WHEN A.DATA_TYPE='1' THEN 1 ELSE 0 END) AS CALL_CNT, + SUM( CASE WHEN A.DATA_TYPE IN ('2', '3') THEN 1 ELSE 0 END) AS SMS_CNT, + SUM(CAST(A.DURATION AS INT)) AS DURATION, + ( SELECT COUNT(*) + FROM t1 + WHERE IMORY_ID=A.IMORY_ID + AND NUMBER=A.NUMBER + AND NAME=A.NAME + AND DATETIME = DATE_FORMAT(A.DATETIME, '%Y-%m-%d') + ) STATS_COUNT +FROM t2 A +WHERE A.IMORY_ID = 55094102 + AND A.DATETIME LIKE ( + SELECT CONCAT (DATE_FORMAT(DATETIME, '%Y-%m-%d') ,'%') + FROM t2 + WHERE IMORY_ID=55094102 + AND DIARY_SEQ IN ( 608351221, 608351225, 608351229 ) + group by DATE_FORMAT(DATETIME, '%Y-%m-%d') + ) +GROUP BY A.IMORY_ID, A.NUMBER, A.NAME, DATE_FORMAT(A.DATETIME, '%Y-%m-%d') +; + +drop table t2, t1; + + +set global default_storage_engine='innodb'; + --echo # --echo # MDEV-5963: InnoDB: Assertion failure in file row0sel.cc line 2503, --echo # Failing assertion: 0 with "key ptr now exceeds key end by 762 bytes" diff --git a/mysql-test/t/partition_myisam.test b/mysql-test/t/partition_myisam.test index d07637057e0..4d083c37b68 100644 --- a/mysql-test/t/partition_myisam.test +++ b/mysql-test/t/partition_myisam.test @@ -216,6 +216,28 @@ PARTITION BY RANGE (a) PARTITION pMax VALUES LESS THAN MAXVALUE); INSERT INTO t1 VALUES (1, "Partition p1, first row"); DROP TABLE t1; + +--echo # +--echo # MDEV-10418 Assertion `m_extra_cache' failed +--echo # in ha_partition::late_extra_cache(uint) +--echo # + +CREATE TABLE t1 (f1 INT) ENGINE=MyISAM; +INSERT INTO t1 VALUES (1),(2); + +CREATE TABLE t2 (f2 INT) ENGINE=MyISAM PARTITION BY RANGE(f2) (PARTITION pmax VALUES LESS THAN MAXVALUE); +INSERT INTO t2 VALUES (8); + +CREATE ALGORITHM = MERGE VIEW v AS SELECT f2 FROM t2, t1; + +UPDATE v SET f2 = 1; + +SELECT * FROM t2; + +DROP VIEW v; +DROP TABLE t2; +DROP TABLE t1; + --echo # --echo # bug#11760213-52599: ALTER TABLE REMOVE PARTITIONING ON NON-PARTITIONED --echo # TABLE CORRUPTS MYISAM diff --git a/mysql-test/t/ps.test b/mysql-test/t/ps.test index a6b238b84c6..d7ca29083b5 100644 --- a/mysql-test/t/ps.test +++ b/mysql-test/t/ps.test @@ -3680,5 +3680,38 @@ EXECUTE stmt; deallocate prepare stmt; drop table t1,t2,t3,t4; +--echo # +--echo # MDEV-11859: the plans for the first and the second executions +--echo # of PS are not the same +--echo # + +create table t1 (id int, c varchar(3), key idx(c))engine=myisam; +insert into t1 values (3,'bar'), (1,'xxx'), (2,'foo'), (5,'yyy'); + +prepare stmt1 from +"explain extended + select * from t1 where (1, 2) in ( select 3, 4 ) or c = 'foo'"; +execute stmt1; +execute stmt1; +deallocate prepare stmt1; + +prepare stmt1 from +"select * from t1 where (1, 2) in ( select 3, 4 ) or c = 'foo'"; +flush status; +execute stmt1; +show status like '%Handler_read%'; +flush status; +execute stmt1; +show status like '%Handler_read%'; +deallocate prepare stmt1; + +prepare stmt2 from +"explain extended + select * from t1 where (1, 2) in ( select 3, 4 )"; +execute stmt2; +execute stmt2; +deallocate prepare stmt2; + +drop table t1; --echo # End of 5.5 tests diff --git a/mysql-test/t/range_vs_index_merge.test b/mysql-test/t/range_vs_index_merge.test index 7ecca454f4c..5d12d46c9e9 100644 --- a/mysql-test/t/range_vs_index_merge.test +++ b/mysql-test/t/range_vs_index_merge.test @@ -57,9 +57,9 @@ SELECT * FROM City EXPLAIN SELECT * FROM City WHERE Population > 100000 AND Name LIKE 'Aba%' OR - Country IN ('CAN', 'ARG') AND ID < 3800 OR - Country < 'U' AND Name LIKE 'Zhu%' OR - ID BETWEEN 3800 AND 3810; + Country IN ('CAN', 'ARG') AND ID BETWEEN 120 AND 130 OR + Country <= 'ALB' AND Name LIKE 'L%' OR + ID BETWEEN 3807 AND 3810; # The output of the next 3 commands tells us about selectivities # of the conditions utilized in 2 queries following after them @@ -1206,6 +1206,41 @@ SELECT * FROM t1 DROP TABLE t1; + +--echo # +--echo # MDEV-8603: Wrong result OR/AND condition over index fields +--echo # + +CREATE TABLE t1 ( + id INT NOT NULL, + state VARCHAR(64), + capital VARCHAR(64), + UNIQUE KEY (id), + KEY state (state,id), + KEY capital (capital, id) +); + +INSERT INTO t1 VALUES + (1,'Arizona','Phoenix'), + (2,'Hawaii','Honolulu'), + (3,'Georgia','Atlanta'), + (4,'Florida','Tallahassee'), + (5,'Alaska','Juneau'), + (6,'Michigan','Lansing'), + (7,'Pennsylvania','Harrisburg'), + (8,'Virginia','Richmond') +; + +EXPLAIN +SELECT * FROM t1 FORCE KEY (state,capital) +WHERE ( state = 'Alabama' OR state >= 'Colorado' ) AND id != 9 + OR ( capital >= 'Topeka' OR state = 'Kansas' ) AND state != 'Texas'; +SELECT * FROM t1 FORCE KEY (state,capital) +WHERE ( state = 'Alabama' OR state >= 'Colorado' ) AND id != 9 + OR ( capital >= 'Topeka' OR state = 'Kansas' ) AND state != 'Texas'; + +DROP TABLE t1; + #the following command must be the last one in the file set session optimizer_switch='index_merge_sort_intersection=default'; diff --git a/mysql-test/t/repair_symlink-5543.test b/mysql-test/t/repair_symlink-5543.test index bad65a4175a..6bdf72b4d40 100644 --- a/mysql-test/t/repair_symlink-5543.test +++ b/mysql-test/t/repair_symlink-5543.test @@ -9,7 +9,7 @@ eval create table t1 (a int) engine=myisam data directory='$MYSQL_TMP_DIR'; insert t1 values (1); --system ln -s $MYSQL_TMP_DIR/foobar5543 $MYSQL_TMP_DIR/t1.TMD ---replace_result $MYSQL_TMP_DIR MYSQL_TMP_DIR +--replace_regex / '.*\/t1/ 'MYSQL_TMP_DIR\/t1/ repair table t1; drop table t1; @@ -17,7 +17,7 @@ drop table t1; eval create table t2 (a int) engine=aria data directory='$MYSQL_TMP_DIR'; insert t2 values (1); --system ln -s $MYSQL_TMP_DIR/foobar5543 $MYSQL_TMP_DIR/t2.TMD ---replace_result $MYSQL_TMP_DIR MYSQL_TMP_DIR +--replace_regex / '.*\/t2/ 'MYSQL_TMP_DIR\/t2/ repair table t2; drop table t2; diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test index 10c95f9a919..c5d37d1017d 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -9422,3 +9422,43 @@ drop procedure p1; drop table t1,t2,t3,t4,t5,t6; +--echo # +--echo # MDEV-11935: Queries in stored procedures with and +--echo # EXISTS(SELECT * FROM VIEW) crashes and closes hte conneciton. +--echo # + +CREATE TABLE ANY_TABLE ( + ENTITY_UID BIGINT NOT NULL +); +CREATE TABLE SECURITY_PATH( +origid BIGINT UNSIGNED NOT NULL, +destid BIGINT UNSIGNED NOT NULL, +KEY (destid) +); +CREATE VIEW ENTITY_ACCESS ( +ENTITY_UID, +OWNER_UID +) AS +SELECT SP1.origid, + SP2.destid +FROM SECURITY_PATH SP1 +JOIN SECURITY_PATH SP2 ON SP1.destid = SP2.origid +; +--delimiter // +CREATE PROCEDURE SP_EXAMPLE_SELECT () +BEGIN + SELECT * + FROM ANY_TABLE AT1 + WHERE EXISTS ( SELECT * + FROM ENTITY_ACCESS EA + WHERE AT1.ENTITY_UID = EA.ENTITY_UID + AND EA.OWNER_UID IS NULL ); +END +// +--delimiter ; +CALL SP_EXAMPLE_SELECT (); +CALL SP_EXAMPLE_SELECT (); + +drop procedure SP_EXAMPLE_SELECT; +drop view ENTITY_ACCESS; +drop table ANY_TABLE, SECURITY_PATH; diff --git a/mysql-test/t/symlink-aria-11902.test b/mysql-test/t/symlink-aria-11902.test new file mode 100644 index 00000000000..a2a266cbb25 --- /dev/null +++ b/mysql-test/t/symlink-aria-11902.test @@ -0,0 +1,6 @@ +# +# MDEV-11902 mi_open race condition +# +source include/have_maria.inc; +set default_storage_engine=Aria; +source symlink-myisam-11902.test; diff --git a/mysql-test/t/symlink-myisam-11902.test b/mysql-test/t/symlink-myisam-11902.test new file mode 100644 index 00000000000..426f8e61edc --- /dev/null +++ b/mysql-test/t/symlink-myisam-11902.test @@ -0,0 +1,60 @@ +# +# MDEV-11902 mi_open race condition +# +source include/have_debug_sync.inc; +source include/have_symlink.inc; +source include/not_windows.inc; +call mtr.add_suppression("File.*t1.* not found"); + +create table mysql.t1 (a int, b char(16), index(a)); +insert mysql.t1 values (100, 'test'),(101,'test'); +let $datadir=`select @@datadir`; + +exec mkdir $MYSQLTEST_VARDIR/tmp/foo; +replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR; +eval create table t1 (a int, b char(16), index(a)) + data directory="$MYSQLTEST_VARDIR/tmp/foo"; +insert t1 values (200, 'some'),(201,'some'); +select * from t1; +flush tables; +set debug_sync='mi_open_datafile SIGNAL ok WAIT_FOR go'; +send select * from t1; +connect con1, localhost, root; +set debug_sync='now WAIT_FOR ok'; +exec rm -r $MYSQLTEST_VARDIR/tmp/foo; +exec ln -s $datadir/mysql $MYSQLTEST_VARDIR/tmp/foo; +set debug_sync='now SIGNAL go'; +connection default; +replace_regex / '.*\/tmp\// 'MYSQLTEST_VARDIR\/tmp\// /31/20/; +error 29; +reap; +flush tables; +drop table if exists t1; +exec rm -r $MYSQLTEST_VARDIR/tmp/foo; + +# same with INDEX DIRECTORY +exec mkdir $MYSQLTEST_VARDIR/tmp/foo; +replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR; +eval create table t1 (a int, b char(16), index (a)) + index directory="$MYSQLTEST_VARDIR/tmp/foo"; +insert t1 values (200, 'some'),(201,'some'); +explain select a from t1; +select a from t1; +flush tables; +set debug_sync='mi_open_kfile SIGNAL waiting WAIT_FOR run'; +send select a from t1; +connection con1; +set debug_sync='now WAIT_FOR waiting'; +exec rm -r $MYSQLTEST_VARDIR/tmp/foo; +exec ln -s $datadir/mysql $MYSQLTEST_VARDIR/tmp/foo; +set debug_sync='now SIGNAL run'; +connection default; +replace_regex / '.*\/test\// '.\/test\// /31/20/; +error ER_FILE_NOT_FOUND; +reap; +flush tables; +drop table if exists t1; +exec rm -r $MYSQLTEST_VARDIR/tmp/foo; + +drop table mysql.t1; +set debug_sync='RESET'; diff --git a/mysql-test/t/table_elim.test b/mysql-test/t/table_elim.test index 24f48206013..717aecb42e2 100644 --- a/mysql-test/t/table_elim.test +++ b/mysql-test/t/table_elim.test @@ -534,12 +534,12 @@ INSERT IGNORE INTO t1 VALUES (0,'g'); CREATE TABLE t3 ( a varchar(1)) ; INSERT IGNORE INTO t3 VALUES ('g'); -CREATE TABLE t2 ( a int(11) NOT NULL, PRIMARY KEY (a)) ; +CREATE TABLE t2 ( a int(11) NOT NULL, PRIMARY KEY (a)); +INSERT INTO t2 VALUES (9), (10); create view v1 as SELECT t1.* FROM t1 LEFT JOIN t2 ON ( t1.a = t2.a ) WHERE t2.a <> 0; SELECT alias1.* FROM t3 LEFT JOIN v1 as alias1 ON ( t3.a = alias1.b ); EXPLAIN SELECT alias1.* FROM t3 LEFT JOIN v1 as alias1 ON ( t3.a = alias1.b ); - drop view v1; DROP TABLE t1,t2,t3; diff --git a/mysql-test/t/view.test b/mysql-test/t/view.test index 5ea97f6465e..630c247e85b 100644 --- a/mysql-test/t/view.test +++ b/mysql-test/t/view.test @@ -5505,6 +5505,66 @@ SHOW CREATE VIEW v1; drop view v1; drop table t1,t2; + +--echo # +--echo # MDEV-12099: usage of mergeable view with LEFT JOIN +--echo # that can be converted to INNER JOIN +--echo # + +create table t1 (a int, b int, key(a)) engine=myisam; +insert into t1 values + (3,20), (7,10), (2,10), (4,30), (8,70), + (7,70), (9,100), (9,60), (8,80), (7,60); + +create table t2 (c int, d int, key (c)) engine=myisam; +insert into t2 values + (50,100), (20, 200), (10,300), + (150,100), (120, 200), (110,300), + (250,100), (220, 200), (210,300); + +create table t3(e int, f int not null, key(e), unique (f)) engine=myisam; +insert into t3 values + (100, 3), (300, 5), (400, 4), (300,7), + (300,2), (600, 13), (800, 15), (700, 14), + (600, 23), (800, 25), (700, 24); + +create view v1 as + select * from t2 left join t3 on t3.e=t2.d where t3.f is not null; + +select * + from t1 left join v1 on v1.c=t1.b + where t1.a < 5; + +select * + from t1 left join ( t2 left join t3 on t3.e=t2.d ) + on t2.c=t1.b and t3.f is not null + where t1.a < 5; + +explain extended +select * + from t1 left join v1 on v1.c=t1.b + where t1.a < 5; + +explain extended +select * + from t1 left join ( t2 left join t3 on t3.e=t2.d ) + on t2.c=t1.b and t3.f is not null + where t1.a < 5; + +explain extended +select * + from t1 left join v1 on v1.c=t1.b and v1.f=t1.a + where t1.a < 5; + +explain extended +select * + from t1 left join ( t2 left join t3 on t3.e=t2.d ) + on t2.c=t1.b and t3.f=t1.a and t3.f is not null + where t1.a < 5; + +drop view v1; +drop table t1,t2,t3; + --echo # ----------------------------------------------------------------- --echo # -- End of 5.5 tests. --echo # ----------------------------------------------------------------- diff --git a/mysql-test/unstable-tests b/mysql-test/unstable-tests index e4c57d9f9d7..6c84d8d3ba6 100644 --- a/mysql-test/unstable-tests +++ b/mysql-test/unstable-tests @@ -23,78 +23,62 @@ # ############################################################################## +main.alter_table : Modified in 10.0.30 main.count_distinct2 : MDEV-11768 - timeout -main.create : Modified in 10.0.29 main.create_delayed : MDEV-10605 - failed with timeout -main.ctype_ucs2_def : Modified in 10.0.29 -main.ctype_ucs2_query_cache : Modified in 10.0.29 -main.ctype_utf16_def : Modified in 10.0.29 -main.ctype_utf8 : Modified in 10.0.29 -main.ctype_utf8mb4 : Modified in 10.0.29 -main.ctype_utf8mb4 : Modified in 10.0.29 (load file changed) main.debug_sync : MDEV-10607 - internal error -main.default : Modified in 10.0.29 -main.derived : Modified in 10.0.29 +main.derived : Modified in 10.0.30 main.derived_opt : MDEV-11768 - timeout -main.derived_view : Modified in 10.0.29 -main.events_restart : MDEV-11221 - Assertion failure -main.events_slowlog : Added in 10.0.29 -main.fulltext_charsets : Added in 10.0.29 -main.func_time : Modified in 10.0.29 -main.group_by : Modified in 10.0.29 -main.group_by_innodb : Modified in 10.0.29 +main.events_restart : MDEV-11221 - assertion failure +main.grant : Modified in 10.0.30 main.host_cache_size_functionality : MDEV-10606 - sporadic failure on shutdown main.index_intersect_innodb : MDEV-10643 - failed with timeout main.index_merge_innodb : MDEV-7142 - wrong result -main.information_schema_part : Modified in 10.0.29 main.innodb_mysql_lock : MDEV-7861 - sporadic lock detection failure -main.join_cache : Modified in 10.0.29 -main.loaddata : Modified in 10.0.29 -main.log_slow : Modified in 10.0.29 +main.join_nested : Modified in 10.0.30 main.mdev-504 : MDEV-10607 - sporadic "can't connect" main.mdev375 : MDEV-10607 - sporadic "can't connect" main.merge : MDEV-10607 - sporadic "can't connect" +main.mysqldump : Modified in 10.0.30 main.mysqlhotcopy_myisam : MDEV-10995 - test hangs on debug build main.mysqltest : MDEV-9269 - fails on Alpha -main.order_by : Modified in 10.0.29 -main.parser : Modified in 10.0.29 -main.pool_of_threads : Modified in 10.0.29 +main.partition_innodb : Modified in 10.0.30 +main.partition_myisam : Modified in 10.0.30 main.ps : MDEV-11017 - sporadic wrong Prepared_stmt_count -main.selectivity : Modified in 10.0.29 +main.range_vs_index_merge : Modified in 10.0.30 +main.repair_symlink-5543 : Modified in 10.0.30 +main.show_explain : MDEV-10674 - wrong result main.signal_demo3 : MDEV-11720 - Thread stack overrun on Solaris -main.sp : Modified in 10.0.29 +main.sp : Modified in 10.0.30 main.sp_notembedded : MDEV-10607 - internal error -main.sp-prelocking : Modified in 10.0.29 main.sp-security : MDEV-10607 - sporadic "can't connect" -main.stat_tables_par_innodb : MDEV-10515 - sporadic wrong results -main.subselect : Modified in 10.0.29 -main.subselect2 : Modified in 10.0.29 -main.subselect4 : Modified in 10.0.29 main.subselect_innodb : MDEV-10614 - sporadic wrong results -main.subselect_sj2_jcl6 : MDEV-11766 - unexpected warnings -main.type_bit_innodb : MDEV-11766 - unexpected warnings -main.type_decimal : Modified in 10.0.29 -main.union : Modified in 10.0.29 -main.view : Modified in 10.0.29 +main.symlink-aria-11902 : Added in 10.0.30 +main.symlink-myisam-11902 : Added in 10.0.30 +main.table_elim : Modified in 10.0.30 +main.view : Modified in 10.0.30 main.xa : MDEV-11769 - lock wait timeout #---------------------------------------------------------------- archive.archive-big : MDEV-10615 - table is marked as crashed archive.archive_bitfield : MDEV-11771 - table is marked as crashed +archive.archive_symlink : MDEV-12170 - unexpected error on rmdir archive.discover : MDEV-10510 - table is marked as crashed archive.mysqlhotcopy_archive : MDEV-10995 - test hangs on debug build #---------------------------------------------------------------- binlog.binlog_commit_wait : MDEV-10150 - Error: too much time elapsed +binlog.binlog_max_binlog_stmt_cache_size : Added in 10.0.30 binlog.binlog_xa_recover : MDEV-8517 - Extra checkpoint #---------------------------------------------------------------- -connect.jdbc : re-enabled in 10.0.29 -connect.jdbc_new : re-enabled in 10.0.29 connect.tbl : MDEV-9844, MDEV-10179 - sporadic crashes, valgrind warnings, wrong results +connect.xml : Uses xsample2.xml modified in 10.0.30 +connect.xml_zip : Added in 10.0.30 +connect.zip : Added in 10.0.30 #---------------------------------------------------------------- @@ -102,6 +86,7 @@ engines/rr_trx.* : MDEV-10998 - tests not maintained #---------------------------------------------------------------- +federated_bug_35333 : Modified in 10.0.30 federated.federatedx : MDEV-10617 - Wrong checksum, timeouts federated.federated_innodb : MDEV-10617, MDEV-10417 - Wrong checksum, timeouts, fails on Mips federated.federated_partition : MDEV-10417 - Fails on Mips @@ -109,23 +94,29 @@ federated.federated_transactions : MDEV-10617, MDEV-10417 - Wrong checksum, time #---------------------------------------------------------------- -funcs_1.memory_views : MDEV-11773 - timeout -funcs_2/charset.* : MDEV-10999 - test not maintained +funcs_1.memory_views : MDEV-11773 - timeout +funcs_1.processlist_val_ps : MDEV-12175 - Wrong result + +funcs_2/charset.* : MDEV-10999 - test not maintained #---------------------------------------------------------------- -innodb.binlog_consistent : MDEV-10618 - Server fails to start +innodb.alter_key_block_size-11757 : Added in 10.0.30 +innodb.binlog_consistent : MDEV-10618 - Server fails to start innodb.group_commit_crash_no_optimize_thread : MDEV-11770 - checksum mismatch -innodb.innodb-alter-table : MDEV-10619 - Testcase timeout -innodb.innodb_bug30423 : MDEV-7311 - Wrong number of rows in the plan -innodb.innodb_bug53290 : MDEV-11767 - timeout -innodb.innodb_bug56143 : MDEV-11766 - unexpected warnings -innodb.innodb_monitor : MDEV-10939 - Testcase timeout -innodb.innodb-wl5522-debug-zip : Modified in 10.0.29 -innodb.table_index_statistics : Modified in 10.0.29 - -innodb_fts.innodb_fts_misc : MDEV-11767 - timeout -innodb_fts.innodb_fts_plugin : MDEV-11766 - unexpected warnings +innodb.innodb-alter-table : MDEV-10619 - Testcase timeout +innodb.innodb_blob_unrecoverable_crash : Modified in 10.0.30 +innodb.innodb_bug14676111 : Modified in 10.0.30 +innodb.innodb_bug30423 : MDEV-7311 - Wrong number of rows in the plan +innodb.innodb_bug59641 : Modified in 10.0.30 +innodb.innodb-get-fk : Modified in 10.0.30 +innodb.innodb_monitor : MDEV-10939 - Testcase timeout +innodb.log_file_size : Added in 10.0.30 +innodb.read_only_recovery : Added in 10.0.30 +innodb.xa_recovery : Modified in 10.0.30 + +innodb_fts.create : Added in 10.0.30 +innodb_fts.innodb_fts_stopword_charset : MDEV-12052 - Crash on shutdown #---------------------------------------------------------------- @@ -138,28 +129,43 @@ mroonga/storage.index_multiple_column_unique_date_order_32bit_desc : Wrong resul #---------------------------------------------------------------- -multi_source.gtid : MDEV-10620, MDEV-10417 - Timeout in wait condition, fails on Mips +multi_source.gtid : MDEV-10417 - Fails on Mips multi_source.multisource : MDEV-10417 - Fails on Mips multi_source.simple : MDEV-4633 - Wrong slave status output multi_source.status_vars : MDEV-4632 - failed while waiting for Slave_received_heartbeats + #---------------------------------------------------------------- -parts.partition_exch_qa_10 : MDEV-11765 - wrong result -parts.partition_float_myisam : MDEV-10621 - Testcase timeout -parts.partition_int_myisam : MDEV-10621 - Testcase timeout +oqgraph.regression_mdev6282 : Modified in 10.0.30 +oqgraph.regression_mdev6345 : Modified in 10.0.30 #---------------------------------------------------------------- -percona.percona_xtradb_bug317074 : MDEV-11767 - timeout +parts.partition_bigint_innodb : Added in 10.0.30 +parts.partition_bigint_myisam : Added in 10.0.30 +parts.partition_double_innodb : Added in 10.0.30 +parts.partition_double_myisam : Added in 10.0.30 +parts.partition_exch_qa_10 : MDEV-11765 - wrong result +parts.partition_float_innodb : Modified in 10.0.30 +parts.partition_float_myisam : Modified in 10.0.30 +parts.partition_int_innodb : Modified in 10.0.30 +parts.partition_int_myisam : Modified in 10.0.30 +parts.partition_mediumint_innodb : Added in 10.0.30 +parts.partition_mediumint_myisam : Added in 10.0.30 +parts.partition_smallint_innodb : Added in 10.0.30 +parts.partition_smallint_myisam : Added in 10.0.30 +parts.partition_tinyint_innodb : Added in 10.0.30 +parts.partition_tinyint_myisam : Added in 10.0.30 #---------------------------------------------------------------- perfschema.func_file_io : MDEV-5708 - fails for s390x perfschema.func_mutex : MDEV-5708 - fails for s390x perfschema.hostcache_ipv6_ssl : MDEV-10696 - crash on shutdown -perfschema.table_name : MDEV-11764 - wrong result perfschema.socket_summary_by_event_name_func : MDEV-10622 - Socket summary tables do not match +perfschema.stage_mdl_procedure : MDEV-11545 - Wrong result +perfschema.threads_mysql : MDEV-12177 - Wrong result perfschema_stress.* : MDEV-10996 - tests not maintained @@ -169,49 +175,41 @@ plugins.feedback_plugin_send : MDEV-7932 - ssl failed for url, MDEV-11118 - plugins.server_audit : MDEV-9562 - crashes on sol10-sparc plugins.thread_pool_server_audit : MDEV-9562 - crashes on sol10-sparc - #---------------------------------------------------------------- -roles.create_and_drop_role : Modified in 10.0.29 roles.create_and_grant_role : MDEV-11772 - wrong result -roles.role_case_sensitive-10744 : Added in 10.0.29 #---------------------------------------------------------------- rpl.last_insert_id : MDEV-10625 - warnings in error log -rpl.rpl_alter_extra_persistent : Added in 10.0.29 rpl.rpl_auto_increment : MDEV-10417 - Fails on Mips rpl.rpl_auto_increment_bug45679 : MDEV-10417 - Fails on Mips rpl.rpl_auto_increment_update_failure : MDEV-10625 - warnings in error log rpl.rpl_binlog_index : MDEV-9501 - Warning: failed registering on master -rpl.rpl_checksum_cache : MDEV-10626 - Testcase timeout -rpl.rpl_circular_for_4_hosts : MDEV-10627 - Testcase timeout +rpl.rpl_checksum_cache : MDEV-12173 - InnoDB error rpl.rpl_ddl : MDEV-10417 - Fails on Mips rpl.rpl_gtid_crash : MDEV-9501 - Warning: failed registering on master -rpl.rpl_gtid_master_promote : MDEV-10628 - Timeout in sync_with_master rpl.rpl_gtid_stop_start : MDEV-10629 - Crash on shutdown rpl.rpl_gtid_until : MDEV-10625 - warnings in error log -rpl.rpl_heartbeat_basic : MDEV-11668 - Wrong result +rpl.rpl_heartbeat_basic : Modified in 10.0.30 rpl.rpl_innodb_bug30888 : MDEV-10417 - Fails on Mips rpl.rpl_insert : MDEV-9329 - Fails on Ubuntu/s390x rpl.rpl_insert_delayed : MDEV-9329 - Fails on Ubuntu/s390x rpl.rpl_invoked_features : MDEV-10417 - Fails on Mips -rpl.rpl_mdev10863 : Added in 10.0.29 rpl.rpl_mdev6020 : MDEV-10630, MDEV-10417 - Timeouts, fails on Mips -rpl.rpl_mdev6386 : MDEV-10631 - Wrong result on slave -rpl.rpl_parallel : MDEV-10632, MDEV-10653 - Failures to sync, timeouts +rpl.rpl_mdev6386 : Modified in 10.0.30 +rpl.rpl_parallel : MDEV-10653 - Timeouts rpl.rpl_parallel_temptable : MDEV-10356 - Crash in close_thread_tables rpl.rpl_partition_innodb : MDEV-10417 - Fails on Mips -rpl.rpl_row_drop_create_temp_table : MDEV-10626 - Testcase timeout -rpl.rpl_row_mysqlbinlog : Modified in 10.0.29 +rpl.rpl_row_basic_11bugs : MDEV-12171 - Server failed to start rpl.rpl_row_sp001 : MDEV-9329 - Fails on Ubuntu/s390x +rpl.rpl_semi_sync : MDEV-11220 - Wrong result rpl.rpl_semi_sync_uninstall_plugin : MDEV-7140 - Wrong plugin status +rpl.rpl_show_slave_hosts : MDEV-12171 - Server failed to start rpl.rpl_slave_grp_exec : MDEV-10514 - Unexpected deadlock -rpl.rpl_special_charset : Modified in 10.0.29 rpl.rpl_sync : MDEV-10633 - Database page corruption rpl.rpl_temporary_error2 : MDEV-10634 - Wrong number of retries rpl.rpl_skip_replication : MDEV-9268 - Fails with timeout in sync_slave_with_master on Alpha -rpl.sec_behind_master-5114 : Modified in 10.0.29 rpl/extra/rpl_tests.* : MDEV-10994 - tests not maintained @@ -240,28 +238,31 @@ stress.ddl_innodb : MDEV-10635 - Testcase timeout sys_vars.autocommit_func2 : MDEV-9329 - Fails on Ubuntu/s390x sys_vars.innodb_buffer_pool_dump_pct_basic : MDEV-10651 - sporadic failure on file_exists -sys_vars.replicate_do_db_basic : Modified in 10.0.29 -sys_vars.replicate_do_table_basic : Modified in 10.0.29 -sys_vars.replicate_ignore_db_basic : Modified in 10.0.29 -sys_vars.replicate_ignore_table_basic : Modified in 10.0.29 -sys_vars.replicate_wild_do_table_basic : Modified in 10.0.29 -sys_vars.replicate_wild_ignore_table_basic : Modified in 10.0.29 +sys_vars.innodb_force_recovery_crash_basic : Modified in 10.0.30 +sys_vars.innodb_stats_include_delete_marked_basic : Added in 10.0.30 +sys_vars.innodb_status_output_basic : MDEV-12174 - Timeout +sys_vars.secure_file_priv : Modified in 10.0.30 sys_vars.thread_cache_size_func : MDEV-11775 - wrong result #---------------------------------------------------------------- tokudb.cluster_filter_unpack_varchar : MDEV-10636 - Wrong execution plan tokudb.dir_per_db : MDEV-11537 - wrong result -tokudb.locks-select-update-3 : MDEV-11774 - lock wait timeout -tokudb.table_index_statistics : Added in 10.0.29 +tokudb.dir_per_db_rename_to_nonexisting_schema : Added in 10.0.30 +tokudb.gap_lock_error : Added in 10.0.30 +tokudb.locks-select-update-3 : Modified in 10.0.30 +tokudb.percona_kill_idle_trx_tokudb : Added in 10.0.30 tokudb_backup.* : MDEV-11001 - tests don't work tokudb_bugs.checkpoint_lock : MDEV-10637 - Wrong processlist output tokudb_bugs.checkpoint_lock_3 : MDEV-10637 - Wrong processlist output +tokudb_bugs.xa : MDEV-11804 - Lock wait timeout tokudb_rpl.* : MDEV-11001 - tests don't work tokudb_sys_vars.* : MDEV-11001 - tests don't work +rpl-tokudb.rpl_extra_col_slave_tokudb : Result file modified in 10.0.30 + #---------------------------------------------------------------- unit.ma_test_loghandler : MDEV-10638 - record read not ok @@ -271,5 +272,10 @@ unit.pfs : MySQL:84457 - unittest pft-t failing vcol.not_supported : MDEV-10639 - Testcase timeout vcol.vcol_keys_innodb : MDEV-10639 - Testcase timeout +vcol.vcol_misc : Modified in 10.0.30 +vcol.vcol_select_myisam : Modified in 10.0.30 +vcol.vcol_trigger_sp_innodb : Include file modified in 10.0.30 +vcol.vcol_trigger_sp_myisam : Include file modified in 10.0.30 +vcol.wrong_arena : Added in 10.0.30 #---------------------------------------------------------------- diff --git a/mysql-test/valgrind.supp b/mysql-test/valgrind.supp index bbef8a0fa04..ac8a2a80bed 100644 --- a/mysql-test/valgrind.supp +++ b/mysql-test/valgrind.supp @@ -1089,6 +1089,15 @@ fun:SSL_library_init } +{ + OpenSSL still reachable. + Memcheck:Leak + fun:*alloc + fun:CRYPTO_malloc + fun:sk_new + fun:SSL_COMP_get_compression_methods + fun:SSL_library_init +} { OpenSSL still reachable. @@ -1135,6 +1144,17 @@ ... fun:pthread_create* } +<<<<<<< HEAD + +{ + Memory Leak in loader and valgrind malloc + Memcheck:Leak + match-leak-kinds:reachable + obj:*/vgpreload_memcheck*.so + ... + obj:*/ld-*.so + ... +} { ConnectSE: unixODBC SQLAllocEnv leaves some "still reachable" pointers diff --git a/mysys/hash.c b/mysys/hash.c index 344b698a433..c2f3555396e 100644 --- a/mysys/hash.c +++ b/mysys/hash.c @@ -115,14 +115,19 @@ my_hash_init2(HASH *hash, uint growth_size, CHARSET_INFO *charset, static inline void my_hash_free_elements(HASH *hash) { + uint records= hash->records; + /* + Set records to 0 early to guard against anyone looking at the structure + during the free process + */ + hash->records= 0; if (hash->free) { HASH_LINK *data=dynamic_element(&hash->array,0,HASH_LINK*); - HASH_LINK *end= data + hash->records; + HASH_LINK *end= data + records; while (data < end) (*hash->free)((data++)->data); } - hash->records=0; } @@ -519,6 +524,9 @@ my_bool my_hash_insert(HASH *info, const uchar *record) The record with the same record ptr is removed. If there is a free-function it's called if record was found. + hash->free() is guarantee to be called only after the row has been + deleted from the hash and the hash can be reused by other threads. + @return @retval 0 ok @retval 1 Record not found diff --git a/mysys/mf_format.c b/mysys/mf_format.c index 91354db0b64..6672a4386e4 100644 --- a/mysys/mf_format.c +++ b/mysys/mf_format.c @@ -97,13 +97,8 @@ char * fn_format(char * to, const char *name, const char *dir, pos=strmake(strmov(to,dev),name,length); (void) strmov(pos,ext); /* Don't convert extension */ } - /* - If MY_RETURN_REAL_PATH and MY_RESOLVE_SYMLINK is given, only do - realpath if the file is a symbolic link - */ if (flag & MY_RETURN_REAL_PATH) - (void) my_realpath(to, to, MYF(flag & MY_RESOLVE_SYMLINKS ? - MY_RESOLVE_LINK: 0)); + (void) my_realpath(to, to, MYF(0)); else if (flag & MY_RESOLVE_SYMLINKS) { strmov(buff,to); diff --git a/mysys/mf_iocache.c b/mysys/mf_iocache.c index f891a22b17d..8687c2e0c48 100644 --- a/mysys/mf_iocache.c +++ b/mysys/mf_iocache.c @@ -1815,6 +1815,7 @@ int my_b_flush_io_cache(IO_CACHE *info, It's currently safe to call this if one has called init_io_cache() on the 'info' object, even if init_io_cache() failed. This function is also safe to call twice with the same handle. + Note that info->file is not reset as the caller may still use ut for my_close() RETURN 0 ok @@ -1850,10 +1851,12 @@ int end_io_cache(IO_CACHE *info) if (info->type == SEQ_READ_APPEND) { /* Destroy allocated mutex */ - info->type= TYPE_NOT_SET; mysql_mutex_destroy(&info->append_buffer_lock); } info->share= 0; + info->type= TYPE_NOT_SET; /* Ensure that flush_io_cache() does nothing */ + info->write_end= 0; /* Ensure that my_b_write() fails */ + info->write_function= 0; /* my_b_write will crash if used */ DBUG_RETURN(error); } /* end_io_cache */ diff --git a/mysys/my_create.c b/mysys/my_create.c index 6a3bcd63557..014b65c4e14 100644 --- a/mysys/my_create.c +++ b/mysys/my_create.c @@ -36,7 +36,7 @@ File my_create(const char *FileName, int CreateFlags, int access_flags, myf MyFlags) { - int fd, rc; + int fd; DBUG_ENTER("my_create"); DBUG_PRINT("my",("Name: '%s' CreateFlags: %d AccessFlags: %d MyFlags: %lu", FileName, CreateFlags, access_flags, MyFlags)); @@ -54,21 +54,7 @@ File my_create(const char *FileName, int CreateFlags, int access_flags, fd= -1; } - rc= my_register_filename(fd, FileName, FILE_BY_CREATE, + fd= my_register_filename(fd, FileName, FILE_BY_CREATE, EE_CANTCREATEFILE, MyFlags); - /* - my_register_filename() may fail on some platforms even if the call to - *open() above succeeds. In this case, don't leave the stale file because - callers assume the file to not exist if my_create() fails, so they don't - do any cleanups. - */ - if (unlikely(fd >= 0 && rc < 0)) - { - int tmp= my_errno; - my_close(fd, MyFlags); - my_delete(FileName, MyFlags); - my_errno= tmp; - } - - DBUG_RETURN(rc); + DBUG_RETURN(fd); } /* my_create */ diff --git a/mysys/my_delete.c b/mysys/my_delete.c index 3dfe290dabe..0faf6079d98 100644 --- a/mysys/my_delete.c +++ b/mysys/my_delete.c @@ -21,6 +21,12 @@ static int my_win_unlink(const char *name); #endif +CREATE_NOSYMLINK_FUNCTION( + unlink_nosymlinks(const char *pathname), + unlinkat(dfd, filename, 0), + unlink(pathname) +); + int my_delete(const char *name, myf MyFlags) { int err; @@ -30,7 +36,10 @@ int my_delete(const char *name, myf MyFlags) #ifdef _WIN32 err = my_win_unlink(name); #else - err = unlink(name); + if (MyFlags & MY_NOSYMLINKS) + err= unlink_nosymlinks(name); + else + err= unlink(name); #endif if(err) diff --git a/mysys/my_div.c b/mysys/my_div.c index 660b87e5ab4..44eb5392421 100644 --- a/mysys/my_div.c +++ b/mysys/my_div.c @@ -27,7 +27,7 @@ char * my_filename(File fd) { DBUG_ENTER("my_filename"); - if ((uint) fd >= (uint) my_file_limit) + if ((uint) fd >= (uint) my_file_limit || !my_file_info[fd].name) DBUG_RETURN((char*) "UNKNOWN"); if (fd >= 0 && my_file_info[fd].type != UNOPEN) { diff --git a/mysys/my_fopen.c b/mysys/my_fopen.c index a24f5161168..533c223be66 100644 --- a/mysys/my_fopen.c +++ b/mysys/my_fopen.c @@ -69,19 +69,13 @@ FILE *my_fopen(const char *filename, int flags, myf MyFlags) DBUG_RETURN(fd); /* safeguard */ } mysql_mutex_lock(&THR_LOCK_open); - if ((my_file_info[filedesc].name= (char*) - my_strdup(filename,MyFlags))) - { - my_stream_opened++; - my_file_total_opened++; - my_file_info[filedesc].type= STREAM_BY_FOPEN; - mysql_mutex_unlock(&THR_LOCK_open); - DBUG_PRINT("exit",("stream: 0x%lx", (long) fd)); - DBUG_RETURN(fd); - } + my_file_info[filedesc].name= (char*) my_strdup(filename,MyFlags); + my_stream_opened++; + my_file_total_opened++; + my_file_info[filedesc].type= STREAM_BY_FOPEN; mysql_mutex_unlock(&THR_LOCK_open); - (void) my_fclose(fd,MyFlags); - my_errno=ENOMEM; + DBUG_PRINT("exit",("stream: 0x%lx", (long) fd)); + DBUG_RETURN(fd); } else my_errno=errno; diff --git a/mysys/my_getopt.c b/mysys/my_getopt.c index 2a4557118b0..e68a08ee735 100644 --- a/mysys/my_getopt.c +++ b/mysys/my_getopt.c @@ -895,15 +895,39 @@ my_bool getopt_compare_strings(register const char *s, register const char *t, /* function: eval_num_suffix + Transforms suffix like k/m/g to their real value. +*/ + +static inline long eval_num_suffix(char *suffix, int *error) +{ + long num= 1; + if (*suffix == 'k' || *suffix == 'K') + num*= 1024L; + else if (*suffix == 'm' || *suffix == 'M') + num*= 1024L * 1024L; + else if (*suffix == 'g' || *suffix == 'G') + num*= 1024L * 1024L * 1024L; + else if (*suffix) + { + *error= 1; + return 0; + } + return num; +} + +/* + function: eval_num_suffix_ll + Transforms a number with a suffix to real number. Suffix can be k|K for kilo, m|M for mega or g|G for giga. */ -static longlong eval_num_suffix(char *argument, int *error, char *option_name) +static longlong eval_num_suffix_ll(char *argument, + int *error, char *option_name) { char *endchar; longlong num; - DBUG_ENTER("eval_num_suffix"); + DBUG_ENTER("eval_num_suffix_ll"); *error= 0; @@ -916,23 +940,47 @@ static longlong eval_num_suffix(char *argument, int *error, char *option_name) *error= 1; DBUG_RETURN(0); } - if (*endchar == 'k' || *endchar == 'K') - num*= 1024L; - else if (*endchar == 'm' || *endchar == 'M') - num*= 1024L * 1024L; - else if (*endchar == 'g' || *endchar == 'G') - num*= 1024L * 1024L * 1024L; - else if (*endchar) - { + num*= eval_num_suffix(endchar, error); + if (*error) fprintf(stderr, "Unknown suffix '%c' used for variable '%s' (value '%s')\n", *endchar, option_name, argument); + DBUG_RETURN(num); +} + +/* + function: eval_num_suffix_ull + + Transforms a number with a suffix to positive Integer. Suffix can + be k|K for kilo, m|M for mega or g|G for giga. +*/ + +static ulonglong eval_num_suffix_ull(char *argument, + int *error, char *option_name) +{ + char *endchar; + ulonglong num; + DBUG_ENTER("eval_num_suffix_ull"); + + *error= 0; + errno= 0; + num= strtoull(argument, &endchar, 10); + if (errno == ERANGE) + { + my_getopt_error_reporter(ERROR_LEVEL, + "Incorrect integer value: '%s'", argument); *error= 1; DBUG_RETURN(0); } + num*= eval_num_suffix(endchar, error); + if (*error) + fprintf(stderr, + "Unknown suffix '%c' used for variable '%s' (value '%s')\n", + *endchar, option_name, argument); DBUG_RETURN(num); } + /* function: getopt_ll @@ -946,7 +994,7 @@ static longlong eval_num_suffix(char *argument, int *error, char *option_name) static longlong getopt_ll(char *arg, const struct my_option *optp, int *err) { - longlong num=eval_num_suffix(arg, err, (char*) optp->name); + longlong num=eval_num_suffix_ll(arg, err, (char*) optp->name); return getopt_ll_limit_value(num, optp, NULL); } @@ -1023,7 +1071,7 @@ longlong getopt_ll_limit_value(longlong num, const struct my_option *optp, static ulonglong getopt_ull(char *arg, const struct my_option *optp, int *err) { - ulonglong num= eval_num_suffix(arg, err, (char*) optp->name); + ulonglong num= eval_num_suffix_ull(arg, err, (char*) optp->name); return getopt_ull_limit_value(num, optp, NULL); } diff --git a/mysys/my_init.c b/mysys/my_init.c index 2c06425f6fb..dee41e1202a 100644 --- a/mysys/my_init.c +++ b/mysys/my_init.c @@ -227,7 +227,7 @@ Voluntary context switches %ld, Involuntary context switches %ld\n", /* At very last, delete mysys key, it is used everywhere including DBUG */ pthread_key_delete(THR_KEY_mysys); - my_init_done=0; + my_init_done= my_thr_key_mysys_exists= 0; } /* my_end */ #ifndef DBUG_OFF diff --git a/mysys/my_open.c b/mysys/my_open.c index b6d8f08bfc1..b1327a316e9 100644 --- a/mysys/my_open.c +++ b/mysys/my_open.c @@ -15,9 +15,14 @@ #include "mysys_priv.h" #include "mysys_err.h" -#include <my_dir.h> +#include <m_string.h> #include <errno.h> +CREATE_NOSYMLINK_FUNCTION( + open_nosymlinks(const char *pathname, int flags, int mode), + openat(dfd, filename, O_NOFOLLOW | flags, mode), + open(pathname, O_NOFOLLOW | flags, mode) +); /* Open a file @@ -45,10 +50,11 @@ File my_open(const char *FileName, int Flags, myf MyFlags) MyFlags|= my_global_flags; #if defined(_WIN32) fd= my_win_open(FileName, Flags); -#elif !defined(NO_OPEN_3) - fd = open(FileName, Flags | O_CLOEXEC, my_umask); /* Normal unix */ #else - fd = open((char *) FileName, Flags | O_CLOEXEC); + if (MyFlags & MY_NOSYMLINKS) + fd = open_nosymlinks(FileName, Flags, my_umask); + else + fd = open(FileName, Flags, my_umask); #endif fd= my_register_filename(fd, FileName, FILE_BY_OPEN, @@ -131,25 +137,16 @@ File my_register_filename(File fd, const char *FileName, enum file_type thread_safe_increment(my_file_opened,&THR_LOCK_open); DBUG_RETURN(fd); /* safeguard */ } - else - { - mysql_mutex_lock(&THR_LOCK_open); - if ((my_file_info[fd].name = (char*) my_strdup(FileName,MyFlags))) - { - my_file_opened++; - my_file_total_opened++; - my_file_info[fd].type = type_of_file; - mysql_mutex_unlock(&THR_LOCK_open); - DBUG_PRINT("exit",("fd: %d",fd)); - DBUG_RETURN(fd); - } - mysql_mutex_unlock(&THR_LOCK_open); - my_errno= ENOMEM; - } - (void) my_close(fd, MyFlags); + mysql_mutex_lock(&THR_LOCK_open); + my_file_info[fd].name = (char*) my_strdup(FileName, MyFlags); + my_file_opened++; + my_file_total_opened++; + my_file_info[fd].type = type_of_file; + mysql_mutex_unlock(&THR_LOCK_open); + DBUG_PRINT("exit",("fd: %d",fd)); + DBUG_RETURN(fd); } - else - my_errno= errno; + my_errno= errno; DBUG_PRINT("error",("Got error %d on open", my_errno)); if (MyFlags & (MY_FFNF | MY_FAE | MY_WME)) diff --git a/mysys/my_symlink.c b/mysys/my_symlink.c index b0e910f7ba0..72648d4c9a8 100644 --- a/mysys/my_symlink.c +++ b/mysys/my_symlink.c @@ -1,5 +1,6 @@ /* Copyright (c) 2001, 2011, Oracle and/or its affiliates + Copyright (c) 2010, 2017, MariaDB 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 @@ -23,6 +24,14 @@ #include <sys/stat.h> #endif +static int always_valid(const char *filename __attribute__((unused))) +{ + return 0; +} + +int (*mysys_test_invalid_symlink)(const char *filename)= always_valid; + + /* Reads the content of a symbolic link If the file is not a symbolic link, return the original file name in to. @@ -120,6 +129,11 @@ int my_is_symlink(const char *filename __attribute__((unused))) to is guaranteed to never set to a string longer than FN_REFLEN (including the end \0) + + On error returns -1, unless error is file not found, in which case it + is 1. + + Sets my_errno to specific error number. */ int my_realpath(char *to, const char *filename, myf MyFlags) @@ -145,7 +159,10 @@ int my_realpath(char *to, const char *filename, myf MyFlags) if (MyFlags & MY_WME) my_error(EE_REALPATH, MYF(0), filename, my_errno); my_load_path(to, filename, NullS); - result= -1; + if (my_errno == ENOENT) + result= 1; + else + result= -1; } DBUG_RETURN(result); #elif defined(_WIN32) @@ -168,3 +185,78 @@ int my_realpath(char *to, const char *filename, myf MyFlags) #endif return 0; } + +#ifdef HAVE_OPEN_PARENT_DIR_NOSYMLINKS +/** opens the parent dir. walks the path, and does not resolve symlinks + + returns the pointer to the file name (basename) within the pathname + or NULL in case of an error + + stores the parent dir (dirname) file descriptor in pdfd. + It can be -1 even if there was no error! + + This is used for symlinked tables for DATA/INDEX DIRECTORY. + The paths there have been realpath()-ed. So, we can assume here that + + * `pathname` is an absolute path + * no '.', '..', and '//' in the path + * file exists +*/ + +const char *my_open_parent_dir_nosymlinks(const char *pathname, int *pdfd) +{ + char buf[PATH_MAX+1]; + char *s= buf, *e= buf+1, *end= strnmov(buf, pathname, sizeof(buf)); + int fd, dfd= -1; + + if (*end) + { + errno= ENAMETOOLONG; + return NULL; + } + + if (*s != '/') /* not an absolute path */ + { + errno= ENOENT; + return NULL; + } + + for (;;) + { + if (*e == '/') /* '//' in the path */ + { + errno= ENOENT; + goto err; + } + while (*e && *e != '/') + e++; + *e= 0; + + if (!memcmp(s, ".", 2) || !memcmp(s, "..", 3)) + { + errno= ENOENT; + goto err; + } + + if (++e >= end) + { + *pdfd= dfd; + return pathname + (s - buf); + } + + fd = openat(dfd, s, O_NOFOLLOW | O_PATH); + if (fd < 0) + goto err; + + if (dfd >= 0) + close(dfd); + + dfd= fd; + s= e; + } +err: + if (dfd >= 0) + close(dfd); + return NULL; +} +#endif diff --git a/mysys/my_symlink2.c b/mysys/my_symlink2.c index fcaf78ccff6..defcb5962d8 100644 --- a/mysys/my_symlink2.c +++ b/mysys/my_symlink2.c @@ -92,27 +92,6 @@ File my_create_with_symlink(const char *linkname, const char *filename, } /* - If the file was a symlink, delete both symlink and the file which the - symlink pointed to. -*/ - -int my_delete_with_symlink(const char *name, myf MyFlags) -{ - char link_name[FN_REFLEN]; - int was_symlink= (!my_disable_symlinks && - !my_readlink(link_name, name, MYF(0))); - int result; - DBUG_ENTER("my_delete_with_symlink"); - - if (!(result=my_delete(name, MyFlags))) - { - if (was_symlink) - result=my_delete(link_name, MyFlags); - } - DBUG_RETURN(result); -} - -/* If the file is a normal file, just rename it. If the file is a symlink: - Create a new file with the name 'to' that points at @@ -182,3 +161,31 @@ int my_rename_with_symlink(const char *from, const char *to, myf MyFlags) DBUG_RETURN(result); #endif /* HAVE_READLINK */ } + +/** delete a - possibly symlinked - table file + + This is used to delete a file that is part of a table (e.g. MYI or MYD + file of MyISAM) when dropping a table. A file might be a symlink - + if the table was created with DATA DIRECTORY or INDEX DIRECTORY - + in this case both the symlink and the symlinked file are deleted, + but only if the symlinked file is not in the datadir. +*/ +int my_handler_delete_with_symlink(PSI_file_key key, const char *name, + const char *ext, myf sync_dir) +{ + char orig[FN_REFLEN], real[FN_REFLEN]; + int res= 0; + DBUG_ENTER("my_handler_delete_with_symlink"); + + fn_format(orig, name, "", ext, MY_UNPACK_FILENAME | MY_APPEND_EXT); + if (my_is_symlink(orig)) + { + /* + Delete the symlinked file only if the symlink is not + pointing into datadir. + */ + if (!(my_realpath(real, orig, MYF(0)) || mysys_test_invalid_symlink(real))) + res= mysql_file_delete(key, real, MYF(MY_NOSYMLINKS | sync_dir)); + } + DBUG_RETURN(mysql_file_delete(key, orig, MYF(sync_dir)) || res); +} diff --git a/mysys/my_sync.c b/mysys/my_sync.c index c0afd587ada..d1e239692f1 100644 --- a/mysys/my_sync.c +++ b/mysys/my_sync.c @@ -196,7 +196,7 @@ int my_sync_dir_by_file(const char *file_name __attribute__((unused)), char dir_name[FN_REFLEN]; size_t dir_name_length; dirname_part(dir_name, file_name, &dir_name_length); - return my_sync_dir(dir_name, my_flags); + return my_sync_dir(dir_name, my_flags & ~MY_NOSYMLINKS); #else return 0; #endif diff --git a/mysys/my_thr_init.c b/mysys/my_thr_init.c index 1e4b85583b1..be8af98e2b9 100644 --- a/mysys/my_thr_init.c +++ b/mysys/my_thr_init.c @@ -44,6 +44,8 @@ static uint get_thread_lib(void); /** True if @c my_thread_global_init() has been called. */ static my_bool my_thread_global_init_done= 0; +/* True if THR_KEY_mysys is created */ +my_bool my_thr_key_mysys_exists= 0; /* @@ -167,11 +169,20 @@ my_bool my_thread_global_init(void) return 0; my_thread_global_init_done= 1; - if ((pth_ret= pthread_key_create(&THR_KEY_mysys, NULL)) != 0) + /* + THR_KEY_mysys is deleted in my_end() as DBUG libraries are using it even + after my_thread_global_end() is called. + my_thr_key_mysys_exist is used to protect against application like QT + that calls my_thread_global_init() + my_thread_global_end() multiple times + without calling my_init() + my_end(). + */ + if (!my_thr_key_mysys_exists && + (pth_ret= pthread_key_create(&THR_KEY_mysys, NULL)) != 0) { fprintf(stderr, "Can't initialize threads: error %d\n", pth_ret); return 1; } + my_thr_key_mysys_exists= 1; /* Mutex used by my_thread_init() and after my_thread_destroy_mutex() */ my_thread_init_internal_mutex(); diff --git a/mysys/mysys_priv.h b/mysys/mysys_priv.h index 9c6855bb92f..0072b8c77f0 100644 --- a/mysys/mysys_priv.h +++ b/mysys/mysys_priv.h @@ -92,6 +92,34 @@ size_t sf_malloc_usable_size(void *ptr, my_bool *is_thread_specific); void my_error_unregister_all(void); +#if !defined(O_PATH) && defined(O_EXEC) /* FreeBSD */ +#define O_PATH O_EXEC +#endif + +#ifdef O_PATH +#define HAVE_OPEN_PARENT_DIR_NOSYMLINKS +const char *my_open_parent_dir_nosymlinks(const char *pathname, int *pdfd); +#define NOSYMLINK_FUNCTION_BODY(AT,NOAT) \ + int dfd, res; \ + const char *filename= my_open_parent_dir_nosymlinks(pathname, &dfd); \ + if (filename == NULL) return -1; \ + res= AT; \ + if (dfd >= 0) close(dfd); \ + return res; +#elif defined(HAVE_REALPATH) +#define NOSYMLINK_FUNCTION_BODY(AT,NOAT) \ + char buf[PATH_MAX+1]; \ + if (realpath(pathname, buf) == NULL) return -1; \ + if (strcmp(pathname, buf)) { errno= ENOTDIR; return -1; } \ + return NOAT; +#else +#define NOSYMLINK_FUNCTION_BODY(AT,NOAT) \ + return NOAT; +#endif + +#define CREATE_NOSYMLINK_FUNCTION(PROTO,AT,NOAT) \ +static int PROTO { NOSYMLINK_FUNCTION_BODY(AT,NOAT) } + #ifdef _WIN32 #include <sys/stat.h> /* my_winfile.c exports, should not be used outside mysys */ diff --git a/pcre/AUTHORS b/pcre/AUTHORS index 342417a8a19..291657caef1 100644 --- a/pcre/AUTHORS +++ b/pcre/AUTHORS @@ -8,7 +8,7 @@ Email domain: cam.ac.uk University of Cambridge Computing Service, Cambridge, England. -Copyright (c) 1997-2016 University of Cambridge +Copyright (c) 1997-2017 University of Cambridge All rights reserved @@ -19,7 +19,7 @@ Written by: Zoltan Herczeg Email local part: hzmester Emain domain: freemail.hu -Copyright(c) 2010-2016 Zoltan Herczeg +Copyright(c) 2010-2017 Zoltan Herczeg All rights reserved. @@ -30,7 +30,7 @@ Written by: Zoltan Herczeg Email local part: hzmester Emain domain: freemail.hu -Copyright(c) 2009-2016 Zoltan Herczeg +Copyright(c) 2009-2017 Zoltan Herczeg All rights reserved. diff --git a/pcre/CMakeLists.txt b/pcre/CMakeLists.txt index 91e2e781fc2..30b06a46fef 100644 --- a/pcre/CMakeLists.txt +++ b/pcre/CMakeLists.txt @@ -66,6 +66,7 @@ # 2013-10-08 PH got rid of the "source" command, which is a bash-ism (use ".") # 2013-11-05 PH added support for PARENS_NEST_LIMIT # 2016-03-01 PH applied Chris Wilson's patch for MSVC static build +# 2016-06-24 PH applied Chris Wilson's revised patch (adds a separate option) PROJECT(PCRE C CXX) diff --git a/pcre/ChangeLog b/pcre/ChangeLog index a34f845f8a1..01511b1327c 100644 --- a/pcre/ChangeLog +++ b/pcre/ChangeLog @@ -4,6 +4,53 @@ ChangeLog for PCRE Note that the PCRE 8.xx series (PCRE1) is now in a bugfix-only state. All development is happening in the PCRE2 10.xx series. +Version 8.40 11-January-2017 +---------------------------- + +1. Using -o with -M in pcregrep could cause unnecessary repeated output when + the match extended over a line boundary. + +2. Applied Chris Wilson's second patch (Bugzilla #1681) to CMakeLists.txt for + MSVC static compilation, putting the first patch under a new option. + +3. Fix register overwite in JIT when SSE2 acceleration is enabled. + +4. Ignore "show all captures" (/=) for DFA matching. + +5. Fix JIT unaligned accesses on x86. Patch by Marc Mutz. + +6. In any wide-character mode (8-bit UTF or any 16-bit or 32-bit mode), + without PCRE_UCP set, a negative character type such as \D in a positive + class should cause all characters greater than 255 to match, whatever else + is in the class. There was a bug that caused this not to happen if a + Unicode property item was added to such a class, for example [\D\P{Nd}] or + [\W\pL]. + +7. When pcretest was outputing information from a callout, the caret indicator + for the current position in the subject line was incorrect if it was after + an escape sequence for a character whose code point was greater than + \x{ff}. + +8. A pattern such as (?<RA>abc)(?(R)xyz) was incorrectly compiled such that + the conditional was interpreted as a reference to capturing group 1 instead + of a test for recursion. Any group whose name began with R was + misinterpreted in this way. (The reference interpretation should only + happen if the group's name is precisely "R".) + +9. A number of bugs have been mended relating to match start-up optimizations + when the first thing in a pattern is a positive lookahead. These all + applied only when PCRE_NO_START_OPTIMIZE was *not* set: + + (a) A pattern such as (?=.*X)X$ was incorrectly optimized as if it needed + both an initial 'X' and a following 'X'. + (b) Some patterns starting with an assertion that started with .* were + incorrectly optimized as having to match at the start of the subject or + after a newline. There are cases where this is not true, for example, + (?=.*[A-Z])(?=.{8,16})(?!.*[\s]) matches after the start in lines that + start with spaces. Starting .* in an assertion is no longer taken as an + indication of matching at the start (or after a newline). + + Version 8.39 14-June-2016 ------------------------- diff --git a/pcre/LICENCE b/pcre/LICENCE index dd977af971b..dd9071a8dd8 100644 --- a/pcre/LICENCE +++ b/pcre/LICENCE @@ -25,7 +25,7 @@ Email domain: cam.ac.uk University of Cambridge Computing Service, Cambridge, England. -Copyright (c) 1997-2016 University of Cambridge +Copyright (c) 1997-2017 University of Cambridge All rights reserved. @@ -36,7 +36,7 @@ Written by: Zoltan Herczeg Email local part: hzmester Emain domain: freemail.hu -Copyright(c) 2010-2016 Zoltan Herczeg +Copyright(c) 2010-2017 Zoltan Herczeg All rights reserved. @@ -47,7 +47,7 @@ Written by: Zoltan Herczeg Email local part: hzmester Emain domain: freemail.hu -Copyright(c) 2009-2016 Zoltan Herczeg +Copyright(c) 2009-2017 Zoltan Herczeg All rights reserved. diff --git a/pcre/NEWS b/pcre/NEWS index 0ca1bab2a4c..b92c4f9605e 100644 --- a/pcre/NEWS +++ b/pcre/NEWS @@ -1,6 +1,12 @@ News about PCRE releases ------------------------ +Release 8.40 11-January-2017 +---------------------------- + +This is a bug-fix release. + + Release 8.39 14-June-2016 ------------------------- diff --git a/pcre/configure.ac b/pcre/configure.ac index 3cefaf100f9..24ef7271e05 100644 --- a/pcre/configure.ac +++ b/pcre/configure.ac @@ -9,17 +9,17 @@ dnl The PCRE_PRERELEASE feature is for identifying release candidates. It might dnl be defined as -RC2, for example. For real releases, it should be empty. m4_define(pcre_major, [8]) -m4_define(pcre_minor, [39]) +m4_define(pcre_minor, [40]) m4_define(pcre_prerelease, []) -m4_define(pcre_date, [2016-06-14]) +m4_define(pcre_date, [2017-01-11]) # NOTE: The CMakeLists.txt file searches for the above variables in the first # 50 lines of this file. Please update that if the variables above are moved. # Libtool shared library interface versions (current:revision:age) -m4_define(libpcre_version, [3:7:2]) -m4_define(libpcre16_version, [2:7:2]) -m4_define(libpcre32_version, [0:7:0]) +m4_define(libpcre_version, [3:8:2]) +m4_define(libpcre16_version, [2:8:2]) +m4_define(libpcre32_version, [0:8:0]) m4_define(libpcreposix_version, [0:4:0]) m4_define(libpcrecpp_version, [0:1:0]) diff --git a/pcre/doc/html/pcrecompat.html b/pcre/doc/html/pcrecompat.html index 3e6226692ee..d95570ef179 100644 --- a/pcre/doc/html/pcrecompat.html +++ b/pcre/doc/html/pcrecompat.html @@ -128,7 +128,7 @@ the pattern /^(a(b)?)+$/ in Perl leaves $2 unset, but in PCRE it is set to "b". 14. PCRE's handling of duplicate subpattern numbers and duplicate subpattern names is not as general as Perl's. This is a consequence of the fact the PCRE works internally just with numbers, using an external table to translate -between numbers and names. In particular, a pattern such as (?|(?<a>A)|(?<b)B), +between numbers and names. In particular, a pattern such as (?|(?<a>A)|(?<b>B), where the two capturing parentheses have the same number but different names, is not supported, and causes an error at compile time. If it were allowed, it would not be possible to distinguish which parentheses matched, because both diff --git a/pcre/doc/html/pcrepattern.html b/pcre/doc/html/pcrepattern.html index 55034a7edf6..96fc72986f6 100644 --- a/pcre/doc/html/pcrepattern.html +++ b/pcre/doc/html/pcrepattern.html @@ -358,24 +358,24 @@ When PCRE is compiled in EBCDIC mode, \a, \e, \f, \n, \r, and \t generate the appropriate EBCDIC code values. The \c escape is processed as specified for Perl in the <b>perlebcdic</b> document. The only characters that are allowed after \c are A-Z, a-z, or one of @, [, \, ], ^, _, or ?. Any -other character provokes a compile-time error. The sequence \@ encodes -character code 0; the letters (in either case) encode characters 1-26 (hex 01 -to hex 1A); [, \, ], ^, and _ encode characters 27-31 (hex 1B to hex 1F), and -\? becomes either 255 (hex FF) or 95 (hex 5F). +other character provokes a compile-time error. The sequence \c@ encodes +character code 0; after \c the letters (in either case) encode characters 1-26 +(hex 01 to hex 1A); [, \, ], ^, and _ encode characters 27-31 (hex 1B to hex +1F), and \c? becomes either 255 (hex FF) or 95 (hex 5F). </P> <P> -Thus, apart from \?, these escapes generate the same character code values as +Thus, apart from \c?, these escapes generate the same character code values as they do in an ASCII environment, though the meanings of the values mostly -differ. For example, \G always generates code value 7, which is BEL in ASCII +differ. For example, \cG always generates code value 7, which is BEL in ASCII but DEL in EBCDIC. </P> <P> -The sequence \? generates DEL (127, hex 7F) in an ASCII environment, but +The sequence \c? generates DEL (127, hex 7F) in an ASCII environment, but because 127 is not a control character in EBCDIC, Perl makes it generate the APC character. Unfortunately, there are several variants of EBCDIC. In most of them the APC character has the value 255 (hex FF), but in the one Perl calls POSIX-BC its value is 95 (hex 5F). If certain other characters have POSIX-BC -values, PCRE makes \? generate 95; otherwise it generates 255. +values, PCRE makes \c? generate 95; otherwise it generates 255. </P> <P> After \0 up to two further octal digits are read. If there are fewer than two @@ -1512,13 +1512,8 @@ J, U and X respectively. <P> When one of these option changes occurs at top level (that is, not inside subpattern parentheses), the change applies to the remainder of the pattern -that follows. If the change is placed right at the start of a pattern, PCRE -extracts it into the global options (and it will therefore show up in data -extracted by the <b>pcre_fullinfo()</b> function). -</P> -<P> -An option change within a subpattern (see below for a description of -subpatterns) affects only that part of the subpattern that follows it, so +that follows. An option change within a subpattern (see below for a description +of subpatterns) affects only that part of the subpattern that follows it, so <pre> (a(?i)b)c </pre> @@ -2160,6 +2155,14 @@ capturing is carried out only for positive assertions. (Perl sometimes, but not always, does do capturing in negative assertions.) </P> <P> +WARNING: If a positive assertion containing one or more capturing subpatterns +succeeds, but failure to match later in the pattern causes backtracking over +this assertion, the captures within the assertion are reset only if no higher +numbered captures are already set. This is, unfortunately, a fundamental +limitation of the current implementation, and as PCRE1 is now in +maintenance-only status, it is unlikely ever to change. +</P> +<P> For compatibility with Perl, assertion subpatterns may be repeated; though it makes no sense to assert the same thing several times, the side effect of capturing parentheses may occasionally be useful. In practice, there only three @@ -3264,9 +3267,9 @@ Cambridge CB2 3QH, England. </P> <br><a name="SEC30" href="#TOC1">REVISION</a><br> <P> -Last updated: 14 June 2015 +Last updated: 23 October 2016 <br> -Copyright © 1997-2015 University of Cambridge. +Copyright © 1997-2016 University of Cambridge. <br> <p> Return to the <a href="index.html">PCRE index page</a>. diff --git a/pcre/doc/pcre.txt b/pcre/doc/pcre.txt index f5eb347e18a..d68d5033ceb 100644 --- a/pcre/doc/pcre.txt +++ b/pcre/doc/pcre.txt @@ -4640,7 +4640,7 @@ DIFFERENCES BETWEEN PCRE AND PERL pattern names is not as general as Perl's. This is a consequence of the fact the PCRE works internally just with numbers, using an external ta- ble to translate between numbers and names. In particular, a pattern - such as (?|(?<a>A)|(?<b)B), where the two capturing parentheses have + such as (?|(?<a>A)|(?<b>B), where the two capturing parentheses have the same number but different names, is not supported, and causes an error at compile time. If it were allowed, it would not be possible to distinguish which parentheses matched, because both names map to cap- @@ -5028,55 +5028,56 @@ BACKSLASH ate the appropriate EBCDIC code values. The \c escape is processed as specified for Perl in the perlebcdic document. The only characters that are allowed after \c are A-Z, a-z, or one of @, [, \, ], ^, _, or ?. - Any other character provokes a compile-time error. The sequence \@ - encodes character code 0; the letters (in either case) encode charac- - ters 1-26 (hex 01 to hex 1A); [, \, ], ^, and _ encode characters 27-31 - (hex 1B to hex 1F), and \? becomes either 255 (hex FF) or 95 (hex 5F). - - Thus, apart from \?, these escapes generate the same character code - values as they do in an ASCII environment, though the meanings of the - values mostly differ. For example, \G always generates code value 7, + Any other character provokes a compile-time error. The sequence \c@ + encodes character code 0; after \c the letters (in either case) encode + characters 1-26 (hex 01 to hex 1A); [, \, ], ^, and _ encode characters + 27-31 (hex 1B to hex 1F), and \c? becomes either 255 (hex FF) or 95 + (hex 5F). + + Thus, apart from \c?, these escapes generate the same character code + values as they do in an ASCII environment, though the meanings of the + values mostly differ. For example, \cG always generates code value 7, which is BEL in ASCII but DEL in EBCDIC. - The sequence \? generates DEL (127, hex 7F) in an ASCII environment, - but because 127 is not a control character in EBCDIC, Perl makes it - generate the APC character. Unfortunately, there are several variants - of EBCDIC. In most of them the APC character has the value 255 (hex - FF), but in the one Perl calls POSIX-BC its value is 95 (hex 5F). If - certain other characters have POSIX-BC values, PCRE makes \? generate + The sequence \c? generates DEL (127, hex 7F) in an ASCII environment, + but because 127 is not a control character in EBCDIC, Perl makes it + generate the APC character. Unfortunately, there are several variants + of EBCDIC. In most of them the APC character has the value 255 (hex + FF), but in the one Perl calls POSIX-BC its value is 95 (hex 5F). If + certain other characters have POSIX-BC values, PCRE makes \c? generate 95; otherwise it generates 255. - After \0 up to two further octal digits are read. If there are fewer - than two digits, just those that are present are used. Thus the + After \0 up to two further octal digits are read. If there are fewer + than two digits, just those that are present are used. Thus the sequence \0\x\015 specifies two binary zeros followed by a CR character (code value 13). Make sure you supply two digits after the initial zero if the pattern character that follows is itself an octal digit. - The escape \o must be followed by a sequence of octal digits, enclosed - in braces. An error occurs if this is not the case. This escape is a - recent addition to Perl; it provides way of specifying character code - points as octal numbers greater than 0777, and it also allows octal + The escape \o must be followed by a sequence of octal digits, enclosed + in braces. An error occurs if this is not the case. This escape is a + recent addition to Perl; it provides way of specifying character code + points as octal numbers greater than 0777, and it also allows octal numbers and back references to be unambiguously specified. For greater clarity and unambiguity, it is best to avoid following \ by a digit greater than zero. Instead, use \o{} or \x{} to specify charac- - ter numbers, and \g{} to specify back references. The following para- + ter numbers, and \g{} to specify back references. The following para- graphs describe the old, ambiguous syntax. The handling of a backslash followed by a digit other than 0 is compli- - cated, and Perl has changed in recent releases, causing PCRE also to + cated, and Perl has changed in recent releases, causing PCRE also to change. Outside a character class, PCRE reads the digit and any follow- - ing digits as a decimal number. If the number is less than 8, or if - there have been at least that many previous capturing left parentheses - in the expression, the entire sequence is taken as a back reference. A - description of how this works is given later, following the discussion + ing digits as a decimal number. If the number is less than 8, or if + there have been at least that many previous capturing left parentheses + in the expression, the entire sequence is taken as a back reference. A + description of how this works is given later, following the discussion of parenthesized subpatterns. - Inside a character class, or if the decimal number following \ is + Inside a character class, or if the decimal number following \ is greater than 7 and there have not been that many capturing subpatterns, - PCRE handles \8 and \9 as the literal characters "8" and "9", and oth- + PCRE handles \8 and \9 as the literal characters "8" and "9", and oth- erwise re-reads up to three octal digits following the backslash, using - them to generate a data character. Any subsequent digits stand for + them to generate a data character. Any subsequent digits stand for themselves. For example: \040 is another way of writing an ASCII space @@ -5094,31 +5095,31 @@ BACKSLASH \81 is either a back reference, or the two characters "8" and "1" - Note that octal values of 100 or greater that are specified using this - syntax must not be introduced by a leading zero, because no more than + Note that octal values of 100 or greater that are specified using this + syntax must not be introduced by a leading zero, because no more than three octal digits are ever read. - By default, after \x that is not followed by {, from zero to two hexa- - decimal digits are read (letters can be in upper or lower case). Any + By default, after \x that is not followed by {, from zero to two hexa- + decimal digits are read (letters can be in upper or lower case). Any number of hexadecimal digits may appear between \x{ and }. If a charac- - ter other than a hexadecimal digit appears between \x{ and }, or if + ter other than a hexadecimal digit appears between \x{ and }, or if there is no terminating }, an error occurs. - If the PCRE_JAVASCRIPT_COMPAT option is set, the interpretation of \x - is as just described only when it is followed by two hexadecimal dig- - its. Otherwise, it matches a literal "x" character. In JavaScript + If the PCRE_JAVASCRIPT_COMPAT option is set, the interpretation of \x + is as just described only when it is followed by two hexadecimal dig- + its. Otherwise, it matches a literal "x" character. In JavaScript mode, support for code points greater than 256 is provided by \u, which - must be followed by four hexadecimal digits; otherwise it matches a + must be followed by four hexadecimal digits; otherwise it matches a literal "u" character. Characters whose value is less than 256 can be defined by either of the - two syntaxes for \x (or by \u in JavaScript mode). There is no differ- + two syntaxes for \x (or by \u in JavaScript mode). There is no differ- ence in the way they are handled. For example, \xdc is exactly the same as \x{dc} (or \u00dc in JavaScript mode). Constraints on character values - Characters that are specified using octal or hexadecimal numbers are + Characters that are specified using octal or hexadecimal numbers are limited to certain values, as follows: 8-bit non-UTF mode less than 0x100 @@ -5128,44 +5129,44 @@ BACKSLASH 32-bit non-UTF mode less than 0x100000000 32-bit UTF-32 mode less than 0x10ffff and a valid codepoint - Invalid Unicode codepoints are the range 0xd800 to 0xdfff (the so- + Invalid Unicode codepoints are the range 0xd800 to 0xdfff (the so- called "surrogate" codepoints), and 0xffef. Escape sequences in character classes All the sequences that define a single character value can be used both - inside and outside character classes. In addition, inside a character + inside and outside character classes. In addition, inside a character class, \b is interpreted as the backspace character (hex 08). - \N is not allowed in a character class. \B, \R, and \X are not special - inside a character class. Like other unrecognized escape sequences, - they are treated as the literal characters "B", "R", and "X" by - default, but cause an error if the PCRE_EXTRA option is set. Outside a + \N is not allowed in a character class. \B, \R, and \X are not special + inside a character class. Like other unrecognized escape sequences, + they are treated as the literal characters "B", "R", and "X" by + default, but cause an error if the PCRE_EXTRA option is set. Outside a character class, these sequences have different meanings. Unsupported escape sequences - In Perl, the sequences \l, \L, \u, and \U are recognized by its string - handler and used to modify the case of following characters. By - default, PCRE does not support these escape sequences. However, if the - PCRE_JAVASCRIPT_COMPAT option is set, \U matches a "U" character, and + In Perl, the sequences \l, \L, \u, and \U are recognized by its string + handler and used to modify the case of following characters. By + default, PCRE does not support these escape sequences. However, if the + PCRE_JAVASCRIPT_COMPAT option is set, \U matches a "U" character, and \u can be used to define a character by code point, as described in the previous section. Absolute and relative back references - The sequence \g followed by an unsigned or a negative number, option- - ally enclosed in braces, is an absolute or relative back reference. A + The sequence \g followed by an unsigned or a negative number, option- + ally enclosed in braces, is an absolute or relative back reference. A named back reference can be coded as \g{name}. Back references are dis- cussed later, following the discussion of parenthesized subpatterns. Absolute and relative subroutine calls - For compatibility with Oniguruma, the non-Perl syntax \g followed by a + For compatibility with Oniguruma, the non-Perl syntax \g followed by a name or a number enclosed either in angle brackets or single quotes, is - an alternative syntax for referencing a subpattern as a "subroutine". - Details are discussed later. Note that \g{...} (Perl syntax) and - \g<...> (Oniguruma syntax) are not synonymous. The former is a back + an alternative syntax for referencing a subpattern as a "subroutine". + Details are discussed later. Note that \g{...} (Perl syntax) and + \g<...> (Oniguruma syntax) are not synonymous. The former is a back reference; the latter is a subroutine call. Generic character types @@ -5184,59 +5185,59 @@ BACKSLASH \W any "non-word" character There is also the single sequence \N, which matches a non-newline char- - acter. This is the same as the "." metacharacter when PCRE_DOTALL is - not set. Perl also uses \N to match characters by name; PCRE does not + acter. This is the same as the "." metacharacter when PCRE_DOTALL is + not set. Perl also uses \N to match characters by name; PCRE does not support this. - Each pair of lower and upper case escape sequences partitions the com- - plete set of characters into two disjoint sets. Any given character - matches one, and only one, of each pair. The sequences can appear both - inside and outside character classes. They each match one character of - the appropriate type. If the current matching point is at the end of - the subject string, all of them fail, because there is no character to + Each pair of lower and upper case escape sequences partitions the com- + plete set of characters into two disjoint sets. Any given character + matches one, and only one, of each pair. The sequences can appear both + inside and outside character classes. They each match one character of + the appropriate type. If the current matching point is at the end of + the subject string, all of them fail, because there is no character to match. - For compatibility with Perl, \s did not used to match the VT character - (code 11), which made it different from the the POSIX "space" class. - However, Perl added VT at release 5.18, and PCRE followed suit at - release 8.34. The default \s characters are now HT (9), LF (10), VT - (11), FF (12), CR (13), and space (32), which are defined as white + For compatibility with Perl, \s did not used to match the VT character + (code 11), which made it different from the the POSIX "space" class. + However, Perl added VT at release 5.18, and PCRE followed suit at + release 8.34. The default \s characters are now HT (9), LF (10), VT + (11), FF (12), CR (13), and space (32), which are defined as white space in the "C" locale. This list may vary if locale-specific matching - is taking place. For example, in some locales the "non-breaking space" - character (\xA0) is recognized as white space, and in others the VT + is taking place. For example, in some locales the "non-breaking space" + character (\xA0) is recognized as white space, and in others the VT character is not. - A "word" character is an underscore or any character that is a letter - or digit. By default, the definition of letters and digits is con- - trolled by PCRE's low-valued character tables, and may vary if locale- - specific matching is taking place (see "Locale support" in the pcreapi - page). For example, in a French locale such as "fr_FR" in Unix-like - systems, or "french" in Windows, some character codes greater than 127 - are used for accented letters, and these are then matched by \w. The + A "word" character is an underscore or any character that is a letter + or digit. By default, the definition of letters and digits is con- + trolled by PCRE's low-valued character tables, and may vary if locale- + specific matching is taking place (see "Locale support" in the pcreapi + page). For example, in a French locale such as "fr_FR" in Unix-like + systems, or "french" in Windows, some character codes greater than 127 + are used for accented letters, and these are then matched by \w. The use of locales with Unicode is discouraged. - By default, characters whose code points are greater than 127 never + By default, characters whose code points are greater than 127 never match \d, \s, or \w, and always match \D, \S, and \W, although this may - vary for characters in the range 128-255 when locale-specific matching - is happening. These escape sequences retain their original meanings - from before Unicode support was available, mainly for efficiency rea- - sons. If PCRE is compiled with Unicode property support, and the - PCRE_UCP option is set, the behaviour is changed so that Unicode prop- + vary for characters in the range 128-255 when locale-specific matching + is happening. These escape sequences retain their original meanings + from before Unicode support was available, mainly for efficiency rea- + sons. If PCRE is compiled with Unicode property support, and the + PCRE_UCP option is set, the behaviour is changed so that Unicode prop- erties are used to determine character types, as follows: \d any character that matches \p{Nd} (decimal digit) \s any character that matches \p{Z} or \h or \v \w any character that matches \p{L} or \p{N}, plus underscore - The upper case escapes match the inverse sets of characters. Note that - \d matches only decimal digits, whereas \w matches any Unicode digit, - as well as any Unicode letter, and underscore. Note also that PCRE_UCP - affects \b, and \B because they are defined in terms of \w and \W. + The upper case escapes match the inverse sets of characters. Note that + \d matches only decimal digits, whereas \w matches any Unicode digit, + as well as any Unicode letter, and underscore. Note also that PCRE_UCP + affects \b, and \B because they are defined in terms of \w and \W. Matching these sequences is noticeably slower when PCRE_UCP is set. - The sequences \h, \H, \v, and \V are features that were added to Perl - at release 5.10. In contrast to the other sequences, which match only - ASCII characters by default, these always match certain high-valued + The sequences \h, \H, \v, and \V are features that were added to Perl + at release 5.10. In contrast to the other sequences, which match only + ASCII characters by default, these always match certain high-valued code points, whether or not PCRE_UCP is set. The horizontal space char- acters are: @@ -5275,110 +5276,110 @@ BACKSLASH Newline sequences - Outside a character class, by default, the escape sequence \R matches - any Unicode newline sequence. In 8-bit non-UTF-8 mode \R is equivalent + Outside a character class, by default, the escape sequence \R matches + any Unicode newline sequence. In 8-bit non-UTF-8 mode \R is equivalent to the following: (?>\r\n|\n|\x0b|\f|\r|\x85) - This is an example of an "atomic group", details of which are given + This is an example of an "atomic group", details of which are given below. This particular group matches either the two-character sequence - CR followed by LF, or one of the single characters LF (linefeed, - U+000A), VT (vertical tab, U+000B), FF (form feed, U+000C), CR (car- - riage return, U+000D), or NEL (next line, U+0085). The two-character + CR followed by LF, or one of the single characters LF (linefeed, + U+000A), VT (vertical tab, U+000B), FF (form feed, U+000C), CR (car- + riage return, U+000D), or NEL (next line, U+0085). The two-character sequence is treated as a single unit that cannot be split. - In other modes, two additional characters whose codepoints are greater + In other modes, two additional characters whose codepoints are greater than 255 are added: LS (line separator, U+2028) and PS (paragraph sepa- - rator, U+2029). Unicode character property support is not needed for + rator, U+2029). Unicode character property support is not needed for these characters to be recognized. It is possible to restrict \R to match only CR, LF, or CRLF (instead of - the complete set of Unicode line endings) by setting the option + the complete set of Unicode line endings) by setting the option PCRE_BSR_ANYCRLF either at compile time or when the pattern is matched. (BSR is an abbrevation for "backslash R".) This can be made the default - when PCRE is built; if this is the case, the other behaviour can be - requested via the PCRE_BSR_UNICODE option. It is also possible to - specify these settings by starting a pattern string with one of the + when PCRE is built; if this is the case, the other behaviour can be + requested via the PCRE_BSR_UNICODE option. It is also possible to + specify these settings by starting a pattern string with one of the following sequences: (*BSR_ANYCRLF) CR, LF, or CRLF only (*BSR_UNICODE) any Unicode newline sequence These override the default and the options given to the compiling func- - tion, but they can themselves be overridden by options given to a - matching function. Note that these special settings, which are not - Perl-compatible, are recognized only at the very start of a pattern, - and that they must be in upper case. If more than one of them is - present, the last one is used. They can be combined with a change of + tion, but they can themselves be overridden by options given to a + matching function. Note that these special settings, which are not + Perl-compatible, are recognized only at the very start of a pattern, + and that they must be in upper case. If more than one of them is + present, the last one is used. They can be combined with a change of newline convention; for example, a pattern can start with: (*ANY)(*BSR_ANYCRLF) - They can also be combined with the (*UTF8), (*UTF16), (*UTF32), (*UTF) + They can also be combined with the (*UTF8), (*UTF16), (*UTF32), (*UTF) or (*UCP) special sequences. Inside a character class, \R is treated as - an unrecognized escape sequence, and so matches the letter "R" by + an unrecognized escape sequence, and so matches the letter "R" by default, but causes an error if PCRE_EXTRA is set. Unicode character properties When PCRE is built with Unicode character property support, three addi- - tional escape sequences that match characters with specific properties - are available. When in 8-bit non-UTF-8 mode, these sequences are of - course limited to testing characters whose codepoints are less than + tional escape sequences that match characters with specific properties + are available. When in 8-bit non-UTF-8 mode, these sequences are of + course limited to testing characters whose codepoints are less than 256, but they do work in this mode. The extra escape sequences are: \p{xx} a character with the xx property \P{xx} a character without the xx property \X a Unicode extended grapheme cluster - The property names represented by xx above are limited to the Unicode + The property names represented by xx above are limited to the Unicode script names, the general category properties, "Any", which matches any - character (including newline), and some special PCRE properties - (described in the next section). Other Perl properties such as "InMu- - sicalSymbols" are not currently supported by PCRE. Note that \P{Any} + character (including newline), and some special PCRE properties + (described in the next section). Other Perl properties such as "InMu- + sicalSymbols" are not currently supported by PCRE. Note that \P{Any} does not match any characters, so always causes a match failure. Sets of Unicode characters are defined as belonging to certain scripts. - A character from one of these sets can be matched using a script name. + A character from one of these sets can be matched using a script name. For example: \p{Greek} \P{Han} - Those that are not part of an identified script are lumped together as + Those that are not part of an identified script are lumped together as "Common". The current list of scripts is: - Arabic, Armenian, Avestan, Balinese, Bamum, Bassa_Vah, Batak, Bengali, - Bopomofo, Brahmi, Braille, Buginese, Buhid, Canadian_Aboriginal, Car- + Arabic, Armenian, Avestan, Balinese, Bamum, Bassa_Vah, Batak, Bengali, + Bopomofo, Brahmi, Braille, Buginese, Buhid, Canadian_Aboriginal, Car- ian, Caucasian_Albanian, Chakma, Cham, Cherokee, Common, Coptic, Cunei- form, Cypriot, Cyrillic, Deseret, Devanagari, Duployan, Egyptian_Hiero- glyphs, Elbasan, Ethiopic, Georgian, Glagolitic, Gothic, Grantha, - Greek, Gujarati, Gurmukhi, Han, Hangul, Hanunoo, Hebrew, Hiragana, - Imperial_Aramaic, Inherited, Inscriptional_Pahlavi, Inscrip- - tional_Parthian, Javanese, Kaithi, Kannada, Katakana, Kayah_Li, - Kharoshthi, Khmer, Khojki, Khudawadi, Lao, Latin, Lepcha, Limbu, Lin- - ear_A, Linear_B, Lisu, Lycian, Lydian, Mahajani, Malayalam, Mandaic, - Manichaean, Meetei_Mayek, Mende_Kikakui, Meroitic_Cursive, - Meroitic_Hieroglyphs, Miao, Modi, Mongolian, Mro, Myanmar, Nabataean, - New_Tai_Lue, Nko, Ogham, Ol_Chiki, Old_Italic, Old_North_Arabian, + Greek, Gujarati, Gurmukhi, Han, Hangul, Hanunoo, Hebrew, Hiragana, + Imperial_Aramaic, Inherited, Inscriptional_Pahlavi, Inscrip- + tional_Parthian, Javanese, Kaithi, Kannada, Katakana, Kayah_Li, + Kharoshthi, Khmer, Khojki, Khudawadi, Lao, Latin, Lepcha, Limbu, Lin- + ear_A, Linear_B, Lisu, Lycian, Lydian, Mahajani, Malayalam, Mandaic, + Manichaean, Meetei_Mayek, Mende_Kikakui, Meroitic_Cursive, + Meroitic_Hieroglyphs, Miao, Modi, Mongolian, Mro, Myanmar, Nabataean, + New_Tai_Lue, Nko, Ogham, Ol_Chiki, Old_Italic, Old_North_Arabian, Old_Permic, Old_Persian, Old_South_Arabian, Old_Turkic, Oriya, Osmanya, Pahawh_Hmong, Palmyrene, Pau_Cin_Hau, Phags_Pa, Phoenician, - Psalter_Pahlavi, Rejang, Runic, Samaritan, Saurashtra, Sharada, Sha- - vian, Siddham, Sinhala, Sora_Sompeng, Sundanese, Syloti_Nagri, Syriac, - Tagalog, Tagbanwa, Tai_Le, Tai_Tham, Tai_Viet, Takri, Tamil, Telugu, - Thaana, Thai, Tibetan, Tifinagh, Tirhuta, Ugaritic, Vai, Warang_Citi, + Psalter_Pahlavi, Rejang, Runic, Samaritan, Saurashtra, Sharada, Sha- + vian, Siddham, Sinhala, Sora_Sompeng, Sundanese, Syloti_Nagri, Syriac, + Tagalog, Tagbanwa, Tai_Le, Tai_Tham, Tai_Viet, Takri, Tamil, Telugu, + Thaana, Thai, Tibetan, Tifinagh, Tirhuta, Ugaritic, Vai, Warang_Citi, Yi. Each character has exactly one Unicode general category property, spec- - ified by a two-letter abbreviation. For compatibility with Perl, nega- - tion can be specified by including a circumflex between the opening - brace and the property name. For example, \p{^Lu} is the same as + ified by a two-letter abbreviation. For compatibility with Perl, nega- + tion can be specified by including a circumflex between the opening + brace and the property name. For example, \p{^Lu} is the same as \P{Lu}. If only one letter is specified with \p or \P, it includes all the gen- - eral category properties that start with that letter. In this case, in - the absence of negation, the curly brackets in the escape sequence are + eral category properties that start with that letter. In this case, in + the absence of negation, the curly brackets in the escape sequence are optional; these two examples have the same effect: \p{L} @@ -5430,73 +5431,73 @@ BACKSLASH Zp Paragraph separator Zs Space separator - The special property L& is also supported: it matches a character that - has the Lu, Ll, or Lt property, in other words, a letter that is not + The special property L& is also supported: it matches a character that + has the Lu, Ll, or Lt property, in other words, a letter that is not classified as a modifier or "other". - The Cs (Surrogate) property applies only to characters in the range - U+D800 to U+DFFF. Such characters are not valid in Unicode strings and - so cannot be tested by PCRE, unless UTF validity checking has been + The Cs (Surrogate) property applies only to characters in the range + U+D800 to U+DFFF. Such characters are not valid in Unicode strings and + so cannot be tested by PCRE, unless UTF validity checking has been turned off (see the discussion of PCRE_NO_UTF8_CHECK, - PCRE_NO_UTF16_CHECK and PCRE_NO_UTF32_CHECK in the pcreapi page). Perl + PCRE_NO_UTF16_CHECK and PCRE_NO_UTF32_CHECK in the pcreapi page). Perl does not support the Cs property. - The long synonyms for property names that Perl supports (such as - \p{Letter}) are not supported by PCRE, nor is it permitted to prefix + The long synonyms for property names that Perl supports (such as + \p{Letter}) are not supported by PCRE, nor is it permitted to prefix any of these properties with "Is". No character that is in the Unicode table has the Cn (unassigned) prop- erty. Instead, this property is assumed for any code point that is not in the Unicode table. - Specifying caseless matching does not affect these escape sequences. - For example, \p{Lu} always matches only upper case letters. This is + Specifying caseless matching does not affect these escape sequences. + For example, \p{Lu} always matches only upper case letters. This is different from the behaviour of current versions of Perl. - Matching characters by Unicode property is not fast, because PCRE has - to do a multistage table lookup in order to find a character's prop- + Matching characters by Unicode property is not fast, because PCRE has + to do a multistage table lookup in order to find a character's prop- erty. That is why the traditional escape sequences such as \d and \w do not use Unicode properties in PCRE by default, though you can make them - do so by setting the PCRE_UCP option or by starting the pattern with + do so by setting the PCRE_UCP option or by starting the pattern with (*UCP). Extended grapheme clusters - The \X escape matches any number of Unicode characters that form an + The \X escape matches any number of Unicode characters that form an "extended grapheme cluster", and treats the sequence as an atomic group - (see below). Up to and including release 8.31, PCRE matched an ear- + (see below). Up to and including release 8.31, PCRE matched an ear- lier, simpler definition that was equivalent to (?>\PM\pM*) - That is, it matched a character without the "mark" property, followed - by zero or more characters with the "mark" property. Characters with - the "mark" property are typically non-spacing accents that affect the + That is, it matched a character without the "mark" property, followed + by zero or more characters with the "mark" property. Characters with + the "mark" property are typically non-spacing accents that affect the preceding character. - This simple definition was extended in Unicode to include more compli- - cated kinds of composite character by giving each character a grapheme - breaking property, and creating rules that use these properties to - define the boundaries of extended grapheme clusters. In releases of + This simple definition was extended in Unicode to include more compli- + cated kinds of composite character by giving each character a grapheme + breaking property, and creating rules that use these properties to + define the boundaries of extended grapheme clusters. In releases of PCRE later than 8.31, \X matches one of these clusters. - \X always matches at least one character. Then it decides whether to + \X always matches at least one character. Then it decides whether to add additional characters according to the following rules for ending a cluster: 1. End at the end of the subject string. - 2. Do not end between CR and LF; otherwise end after any control char- + 2. Do not end between CR and LF; otherwise end after any control char- acter. - 3. Do not break Hangul (a Korean script) syllable sequences. Hangul - characters are of five types: L, V, T, LV, and LVT. An L character may - be followed by an L, V, LV, or LVT character; an LV or V character may + 3. Do not break Hangul (a Korean script) syllable sequences. Hangul + characters are of five types: L, V, T, LV, and LVT. An L character may + be followed by an L, V, LV, or LVT character; an LV or V character may be followed by a V or T character; an LVT or T character may be follwed only by a T character. - 4. Do not end before extending characters or spacing marks. Characters - with the "mark" property always have the "extend" grapheme breaking + 4. Do not end before extending characters or spacing marks. Characters + with the "mark" property always have the "extend" grapheme breaking property. 5. Do not end after prepend characters. @@ -5505,9 +5506,9 @@ BACKSLASH PCRE's additional properties - As well as the standard Unicode properties described above, PCRE sup- - ports four more that make it possible to convert traditional escape - sequences such as \w and \s to use Unicode properties. PCRE uses these + As well as the standard Unicode properties described above, PCRE sup- + ports four more that make it possible to convert traditional escape + sequences such as \w and \s to use Unicode properties. PCRE uses these non-standard, non-Perl properties internally when PCRE_UCP is set. How- ever, they may also be used explicitly. These properties are: @@ -5516,54 +5517,54 @@ BACKSLASH Xsp Any Perl space character Xwd Any Perl "word" character - Xan matches characters that have either the L (letter) or the N (num- - ber) property. Xps matches the characters tab, linefeed, vertical tab, - form feed, or carriage return, and any other character that has the Z - (separator) property. Xsp is the same as Xps; it used to exclude ver- - tical tab, for Perl compatibility, but Perl changed, and so PCRE fol- - lowed at release 8.34. Xwd matches the same characters as Xan, plus + Xan matches characters that have either the L (letter) or the N (num- + ber) property. Xps matches the characters tab, linefeed, vertical tab, + form feed, or carriage return, and any other character that has the Z + (separator) property. Xsp is the same as Xps; it used to exclude ver- + tical tab, for Perl compatibility, but Perl changed, and so PCRE fol- + lowed at release 8.34. Xwd matches the same characters as Xan, plus underscore. - There is another non-standard property, Xuc, which matches any charac- - ter that can be represented by a Universal Character Name in C++ and - other programming languages. These are the characters $, @, ` (grave - accent), and all characters with Unicode code points greater than or - equal to U+00A0, except for the surrogates U+D800 to U+DFFF. Note that - most base (ASCII) characters are excluded. (Universal Character Names - are of the form \uHHHH or \UHHHHHHHH where H is a hexadecimal digit. + There is another non-standard property, Xuc, which matches any charac- + ter that can be represented by a Universal Character Name in C++ and + other programming languages. These are the characters $, @, ` (grave + accent), and all characters with Unicode code points greater than or + equal to U+00A0, except for the surrogates U+D800 to U+DFFF. Note that + most base (ASCII) characters are excluded. (Universal Character Names + are of the form \uHHHH or \UHHHHHHHH where H is a hexadecimal digit. Note that the Xuc property does not match these sequences but the char- acters that they represent.) Resetting the match start - The escape sequence \K causes any previously matched characters not to + The escape sequence \K causes any previously matched characters not to be included in the final matched sequence. For example, the pattern: foo\Kbar - matches "foobar", but reports that it has matched "bar". This feature - is similar to a lookbehind assertion (described below). However, in - this case, the part of the subject before the real match does not have - to be of fixed length, as lookbehind assertions do. The use of \K does - not interfere with the setting of captured substrings. For example, + matches "foobar", but reports that it has matched "bar". This feature + is similar to a lookbehind assertion (described below). However, in + this case, the part of the subject before the real match does not have + to be of fixed length, as lookbehind assertions do. The use of \K does + not interfere with the setting of captured substrings. For example, when the pattern (foo)\Kbar matches "foobar", the first substring is still set to "foo". - Perl documents that the use of \K within assertions is "not well - defined". In PCRE, \K is acted upon when it occurs inside positive - assertions, but is ignored in negative assertions. Note that when a - pattern such as (?=ab\K) matches, the reported start of the match can + Perl documents that the use of \K within assertions is "not well + defined". In PCRE, \K is acted upon when it occurs inside positive + assertions, but is ignored in negative assertions. Note that when a + pattern such as (?=ab\K) matches, the reported start of the match can be greater than the end of the match. Simple assertions - The final use of backslash is for certain simple assertions. An asser- - tion specifies a condition that has to be met at a particular point in - a match, without consuming any characters from the subject string. The - use of subpatterns for more complicated assertions is described below. + The final use of backslash is for certain simple assertions. An asser- + tion specifies a condition that has to be met at a particular point in + a match, without consuming any characters from the subject string. The + use of subpatterns for more complicated assertions is described below. The backslashed assertions are: \b matches at a word boundary @@ -5574,161 +5575,161 @@ BACKSLASH \z matches only at the end of the subject \G matches at the first matching position in the subject - Inside a character class, \b has a different meaning; it matches the - backspace character. If any other of these assertions appears in a - character class, by default it matches the corresponding literal char- + Inside a character class, \b has a different meaning; it matches the + backspace character. If any other of these assertions appears in a + character class, by default it matches the corresponding literal char- acter (for example, \B matches the letter B). However, if the - PCRE_EXTRA option is set, an "invalid escape sequence" error is gener- + PCRE_EXTRA option is set, an "invalid escape sequence" error is gener- ated instead. - A word boundary is a position in the subject string where the current - character and the previous character do not both match \w or \W (i.e. - one matches \w and the other matches \W), or the start or end of the - string if the first or last character matches \w, respectively. In a - UTF mode, the meanings of \w and \W can be changed by setting the - PCRE_UCP option. When this is done, it also affects \b and \B. Neither - PCRE nor Perl has a separate "start of word" or "end of word" metase- - quence. However, whatever follows \b normally determines which it is. + A word boundary is a position in the subject string where the current + character and the previous character do not both match \w or \W (i.e. + one matches \w and the other matches \W), or the start or end of the + string if the first or last character matches \w, respectively. In a + UTF mode, the meanings of \w and \W can be changed by setting the + PCRE_UCP option. When this is done, it also affects \b and \B. Neither + PCRE nor Perl has a separate "start of word" or "end of word" metase- + quence. However, whatever follows \b normally determines which it is. For example, the fragment \ba matches "a" at the start of a word. - The \A, \Z, and \z assertions differ from the traditional circumflex + The \A, \Z, and \z assertions differ from the traditional circumflex and dollar (described in the next section) in that they only ever match - at the very start and end of the subject string, whatever options are - set. Thus, they are independent of multiline mode. These three asser- + at the very start and end of the subject string, whatever options are + set. Thus, they are independent of multiline mode. These three asser- tions are not affected by the PCRE_NOTBOL or PCRE_NOTEOL options, which - affect only the behaviour of the circumflex and dollar metacharacters. - However, if the startoffset argument of pcre_exec() is non-zero, indi- + affect only the behaviour of the circumflex and dollar metacharacters. + However, if the startoffset argument of pcre_exec() is non-zero, indi- cating that matching is to start at a point other than the beginning of - the subject, \A can never match. The difference between \Z and \z is + the subject, \A can never match. The difference between \Z and \z is that \Z matches before a newline at the end of the string as well as at the very end, whereas \z matches only at the end. - The \G assertion is true only when the current matching position is at - the start point of the match, as specified by the startoffset argument - of pcre_exec(). It differs from \A when the value of startoffset is - non-zero. By calling pcre_exec() multiple times with appropriate argu- + The \G assertion is true only when the current matching position is at + the start point of the match, as specified by the startoffset argument + of pcre_exec(). It differs from \A when the value of startoffset is + non-zero. By calling pcre_exec() multiple times with appropriate argu- ments, you can mimic Perl's /g option, and it is in this kind of imple- mentation where \G can be useful. - Note, however, that PCRE's interpretation of \G, as the start of the + Note, however, that PCRE's interpretation of \G, as the start of the current match, is subtly different from Perl's, which defines it as the - end of the previous match. In Perl, these can be different when the - previously matched string was empty. Because PCRE does just one match + end of the previous match. In Perl, these can be different when the + previously matched string was empty. Because PCRE does just one match at a time, it cannot reproduce this behaviour. - If all the alternatives of a pattern begin with \G, the expression is + If all the alternatives of a pattern begin with \G, the expression is anchored to the starting match position, and the "anchored" flag is set in the compiled regular expression. CIRCUMFLEX AND DOLLAR - The circumflex and dollar metacharacters are zero-width assertions. - That is, they test for a particular condition being true without con- + The circumflex and dollar metacharacters are zero-width assertions. + That is, they test for a particular condition being true without con- suming any characters from the subject string. Outside a character class, in the default matching mode, the circumflex - character is an assertion that is true only if the current matching - point is at the start of the subject string. If the startoffset argu- - ment of pcre_exec() is non-zero, circumflex can never match if the - PCRE_MULTILINE option is unset. Inside a character class, circumflex + character is an assertion that is true only if the current matching + point is at the start of the subject string. If the startoffset argu- + ment of pcre_exec() is non-zero, circumflex can never match if the + PCRE_MULTILINE option is unset. Inside a character class, circumflex has an entirely different meaning (see below). - Circumflex need not be the first character of the pattern if a number - of alternatives are involved, but it should be the first thing in each - alternative in which it appears if the pattern is ever to match that - branch. If all possible alternatives start with a circumflex, that is, - if the pattern is constrained to match only at the start of the sub- - ject, it is said to be an "anchored" pattern. (There are also other + Circumflex need not be the first character of the pattern if a number + of alternatives are involved, but it should be the first thing in each + alternative in which it appears if the pattern is ever to match that + branch. If all possible alternatives start with a circumflex, that is, + if the pattern is constrained to match only at the start of the sub- + ject, it is said to be an "anchored" pattern. (There are also other constructs that can cause a pattern to be anchored.) - The dollar character is an assertion that is true only if the current - matching point is at the end of the subject string, or immediately - before a newline at the end of the string (by default). Note, however, - that it does not actually match the newline. Dollar need not be the + The dollar character is an assertion that is true only if the current + matching point is at the end of the subject string, or immediately + before a newline at the end of the string (by default). Note, however, + that it does not actually match the newline. Dollar need not be the last character of the pattern if a number of alternatives are involved, - but it should be the last item in any branch in which it appears. Dol- + but it should be the last item in any branch in which it appears. Dol- lar has no special meaning in a character class. - The meaning of dollar can be changed so that it matches only at the - very end of the string, by setting the PCRE_DOLLAR_ENDONLY option at + The meaning of dollar can be changed so that it matches only at the + very end of the string, by setting the PCRE_DOLLAR_ENDONLY option at compile time. This does not affect the \Z assertion. The meanings of the circumflex and dollar characters are changed if the - PCRE_MULTILINE option is set. When this is the case, a circumflex - matches immediately after internal newlines as well as at the start of - the subject string. It does not match after a newline that ends the - string. A dollar matches before any newlines in the string, as well as - at the very end, when PCRE_MULTILINE is set. When newline is specified - as the two-character sequence CRLF, isolated CR and LF characters do + PCRE_MULTILINE option is set. When this is the case, a circumflex + matches immediately after internal newlines as well as at the start of + the subject string. It does not match after a newline that ends the + string. A dollar matches before any newlines in the string, as well as + at the very end, when PCRE_MULTILINE is set. When newline is specified + as the two-character sequence CRLF, isolated CR and LF characters do not indicate newlines. - For example, the pattern /^abc$/ matches the subject string "def\nabc" - (where \n represents a newline) in multiline mode, but not otherwise. - Consequently, patterns that are anchored in single line mode because - all branches start with ^ are not anchored in multiline mode, and a - match for circumflex is possible when the startoffset argument of - pcre_exec() is non-zero. The PCRE_DOLLAR_ENDONLY option is ignored if + For example, the pattern /^abc$/ matches the subject string "def\nabc" + (where \n represents a newline) in multiline mode, but not otherwise. + Consequently, patterns that are anchored in single line mode because + all branches start with ^ are not anchored in multiline mode, and a + match for circumflex is possible when the startoffset argument of + pcre_exec() is non-zero. The PCRE_DOLLAR_ENDONLY option is ignored if PCRE_MULTILINE is set. - Note that the sequences \A, \Z, and \z can be used to match the start - and end of the subject in both modes, and if all branches of a pattern - start with \A it is always anchored, whether or not PCRE_MULTILINE is + Note that the sequences \A, \Z, and \z can be used to match the start + and end of the subject in both modes, and if all branches of a pattern + start with \A it is always anchored, whether or not PCRE_MULTILINE is set. FULL STOP (PERIOD, DOT) AND \N Outside a character class, a dot in the pattern matches any one charac- - ter in the subject string except (by default) a character that signi- + ter in the subject string except (by default) a character that signi- fies the end of a line. - When a line ending is defined as a single character, dot never matches - that character; when the two-character sequence CRLF is used, dot does - not match CR if it is immediately followed by LF, but otherwise it - matches all characters (including isolated CRs and LFs). When any Uni- - code line endings are being recognized, dot does not match CR or LF or + When a line ending is defined as a single character, dot never matches + that character; when the two-character sequence CRLF is used, dot does + not match CR if it is immediately followed by LF, but otherwise it + matches all characters (including isolated CRs and LFs). When any Uni- + code line endings are being recognized, dot does not match CR or LF or any of the other line ending characters. - The behaviour of dot with regard to newlines can be changed. If the - PCRE_DOTALL option is set, a dot matches any one character, without + The behaviour of dot with regard to newlines can be changed. If the + PCRE_DOTALL option is set, a dot matches any one character, without exception. If the two-character sequence CRLF is present in the subject string, it takes two dots to match it. - The handling of dot is entirely independent of the handling of circum- - flex and dollar, the only relationship being that they both involve + The handling of dot is entirely independent of the handling of circum- + flex and dollar, the only relationship being that they both involve newlines. Dot has no special meaning in a character class. - The escape sequence \N behaves like a dot, except that it is not - affected by the PCRE_DOTALL option. In other words, it matches any - character except one that signifies the end of a line. Perl also uses + The escape sequence \N behaves like a dot, except that it is not + affected by the PCRE_DOTALL option. In other words, it matches any + character except one that signifies the end of a line. Perl also uses \N to match characters by name; PCRE does not support this. MATCHING A SINGLE DATA UNIT - Outside a character class, the escape sequence \C matches any one data - unit, whether or not a UTF mode is set. In the 8-bit library, one data - unit is one byte; in the 16-bit library it is a 16-bit unit; in the - 32-bit library it is a 32-bit unit. Unlike a dot, \C always matches - line-ending characters. The feature is provided in Perl in order to + Outside a character class, the escape sequence \C matches any one data + unit, whether or not a UTF mode is set. In the 8-bit library, one data + unit is one byte; in the 16-bit library it is a 16-bit unit; in the + 32-bit library it is a 32-bit unit. Unlike a dot, \C always matches + line-ending characters. The feature is provided in Perl in order to match individual bytes in UTF-8 mode, but it is unclear how it can use- - fully be used. Because \C breaks up characters into individual data - units, matching one unit with \C in a UTF mode means that the rest of + fully be used. Because \C breaks up characters into individual data + units, matching one unit with \C in a UTF mode means that the rest of the string may start with a malformed UTF character. This has undefined results, because PCRE assumes that it is dealing with valid UTF strings - (and by default it checks this at the start of processing unless the - PCRE_NO_UTF8_CHECK, PCRE_NO_UTF16_CHECK or PCRE_NO_UTF32_CHECK option + (and by default it checks this at the start of processing unless the + PCRE_NO_UTF8_CHECK, PCRE_NO_UTF16_CHECK or PCRE_NO_UTF32_CHECK option is used). - PCRE does not allow \C to appear in lookbehind assertions (described - below) in a UTF mode, because this would make it impossible to calcu- + PCRE does not allow \C to appear in lookbehind assertions (described + below) in a UTF mode, because this would make it impossible to calcu- late the length of the lookbehind. In general, the \C escape sequence is best avoided. However, one way of - using it that avoids the problem of malformed UTF characters is to use - a lookahead to check the length of the next character, as in this pat- - tern, which could be used with a UTF-8 string (ignore white space and + using it that avoids the problem of malformed UTF characters is to use + a lookahead to check the length of the next character, as in this pat- + tern, which could be used with a UTF-8 string (ignore white space and line breaks): (?| (?=[\x00-\x7f])(\C) | @@ -5736,11 +5737,11 @@ MATCHING A SINGLE DATA UNIT (?=[\x{800}-\x{ffff}])(\C)(\C)(\C) | (?=[\x{10000}-\x{1fffff}])(\C)(\C)(\C)(\C)) - A group that starts with (?| resets the capturing parentheses numbers - in each alternative (see "Duplicate Subpattern Numbers" below). The - assertions at the start of each branch check the next UTF-8 character - for values whose encoding uses 1, 2, 3, or 4 bytes, respectively. The - character's individual bytes are then captured by the appropriate num- + A group that starts with (?| resets the capturing parentheses numbers + in each alternative (see "Duplicate Subpattern Numbers" below). The + assertions at the start of each branch check the next UTF-8 character + for values whose encoding uses 1, 2, 3, or 4 bytes, respectively. The + character's individual bytes are then captured by the appropriate num- ber of groups. @@ -5750,109 +5751,109 @@ SQUARE BRACKETS AND CHARACTER CLASSES closing square bracket. A closing square bracket on its own is not spe- cial by default. However, if the PCRE_JAVASCRIPT_COMPAT option is set, a lone closing square bracket causes a compile-time error. If a closing - square bracket is required as a member of the class, it should be the - first data character in the class (after an initial circumflex, if + square bracket is required as a member of the class, it should be the + first data character in the class (after an initial circumflex, if present) or escaped with a backslash. - A character class matches a single character in the subject. In a UTF - mode, the character may be more than one data unit long. A matched + A character class matches a single character in the subject. In a UTF + mode, the character may be more than one data unit long. A matched character must be in the set of characters defined by the class, unless - the first character in the class definition is a circumflex, in which + the first character in the class definition is a circumflex, in which case the subject character must not be in the set defined by the class. - If a circumflex is actually required as a member of the class, ensure + If a circumflex is actually required as a member of the class, ensure it is not the first character, or escape it with a backslash. - For example, the character class [aeiou] matches any lower case vowel, - while [^aeiou] matches any character that is not a lower case vowel. + For example, the character class [aeiou] matches any lower case vowel, + while [^aeiou] matches any character that is not a lower case vowel. Note that a circumflex is just a convenient notation for specifying the - characters that are in the class by enumerating those that are not. A - class that starts with a circumflex is not an assertion; it still con- - sumes a character from the subject string, and therefore it fails if + characters that are in the class by enumerating those that are not. A + class that starts with a circumflex is not an assertion; it still con- + sumes a character from the subject string, and therefore it fails if the current pointer is at the end of the string. In UTF-8 (UTF-16, UTF-32) mode, characters with values greater than 255 - (0xffff) can be included in a class as a literal string of data units, + (0xffff) can be included in a class as a literal string of data units, or by using the \x{ escaping mechanism. - When caseless matching is set, any letters in a class represent both - their upper case and lower case versions, so for example, a caseless - [aeiou] matches "A" as well as "a", and a caseless [^aeiou] does not - match "A", whereas a caseful version would. In a UTF mode, PCRE always - understands the concept of case for characters whose values are less - than 128, so caseless matching is always possible. For characters with - higher values, the concept of case is supported if PCRE is compiled - with Unicode property support, but not otherwise. If you want to use - caseless matching in a UTF mode for characters 128 and above, you must - ensure that PCRE is compiled with Unicode property support as well as + When caseless matching is set, any letters in a class represent both + their upper case and lower case versions, so for example, a caseless + [aeiou] matches "A" as well as "a", and a caseless [^aeiou] does not + match "A", whereas a caseful version would. In a UTF mode, PCRE always + understands the concept of case for characters whose values are less + than 128, so caseless matching is always possible. For characters with + higher values, the concept of case is supported if PCRE is compiled + with Unicode property support, but not otherwise. If you want to use + caseless matching in a UTF mode for characters 128 and above, you must + ensure that PCRE is compiled with Unicode property support as well as with UTF support. - Characters that might indicate line breaks are never treated in any - special way when matching character classes, whatever line-ending - sequence is in use, and whatever setting of the PCRE_DOTALL and + Characters that might indicate line breaks are never treated in any + special way when matching character classes, whatever line-ending + sequence is in use, and whatever setting of the PCRE_DOTALL and PCRE_MULTILINE options is used. A class such as [^a] always matches one of these characters. - The minus (hyphen) character can be used to specify a range of charac- - ters in a character class. For example, [d-m] matches any letter - between d and m, inclusive. If a minus character is required in a - class, it must be escaped with a backslash or appear in a position - where it cannot be interpreted as indicating a range, typically as the + The minus (hyphen) character can be used to specify a range of charac- + ters in a character class. For example, [d-m] matches any letter + between d and m, inclusive. If a minus character is required in a + class, it must be escaped with a backslash or appear in a position + where it cannot be interpreted as indicating a range, typically as the first or last character in the class, or immediately after a range. For - example, [b-d-z] matches letters in the range b to d, a hyphen charac- + example, [b-d-z] matches letters in the range b to d, a hyphen charac- ter, or z. It is not possible to have the literal character "]" as the end charac- - ter of a range. A pattern such as [W-]46] is interpreted as a class of - two characters ("W" and "-") followed by a literal string "46]", so it - would match "W46]" or "-46]". However, if the "]" is escaped with a - backslash it is interpreted as the end of range, so [W-\]46] is inter- - preted as a class containing a range followed by two other characters. - The octal or hexadecimal representation of "]" can also be used to end + ter of a range. A pattern such as [W-]46] is interpreted as a class of + two characters ("W" and "-") followed by a literal string "46]", so it + would match "W46]" or "-46]". However, if the "]" is escaped with a + backslash it is interpreted as the end of range, so [W-\]46] is inter- + preted as a class containing a range followed by two other characters. + The octal or hexadecimal representation of "]" can also be used to end a range. - An error is generated if a POSIX character class (see below) or an - escape sequence other than one that defines a single character appears - at a point where a range ending character is expected. For example, + An error is generated if a POSIX character class (see below) or an + escape sequence other than one that defines a single character appears + at a point where a range ending character is expected. For example, [z-\xff] is valid, but [A-\d] and [A-[:digit:]] are not. - Ranges operate in the collating sequence of character values. They can - also be used for characters specified numerically, for example - [\000-\037]. Ranges can include any characters that are valid for the + Ranges operate in the collating sequence of character values. They can + also be used for characters specified numerically, for example + [\000-\037]. Ranges can include any characters that are valid for the current mode. If a range that includes letters is used when caseless matching is set, it matches the letters in either case. For example, [W-c] is equivalent - to [][\\^_`wxyzabc], matched caselessly, and in a non-UTF mode, if - character tables for a French locale are in use, [\xc8-\xcb] matches - accented E characters in both cases. In UTF modes, PCRE supports the - concept of case for characters with values greater than 128 only when + to [][\\^_`wxyzabc], matched caselessly, and in a non-UTF mode, if + character tables for a French locale are in use, [\xc8-\xcb] matches + accented E characters in both cases. In UTF modes, PCRE supports the + concept of case for characters with values greater than 128 only when it is compiled with Unicode property support. - The character escape sequences \d, \D, \h, \H, \p, \P, \s, \S, \v, \V, + The character escape sequences \d, \D, \h, \H, \p, \P, \s, \S, \v, \V, \w, and \W may appear in a character class, and add the characters that - they match to the class. For example, [\dABCDEF] matches any hexadeci- - mal digit. In UTF modes, the PCRE_UCP option affects the meanings of - \d, \s, \w and their upper case partners, just as it does when they - appear outside a character class, as described in the section entitled + they match to the class. For example, [\dABCDEF] matches any hexadeci- + mal digit. In UTF modes, the PCRE_UCP option affects the meanings of + \d, \s, \w and their upper case partners, just as it does when they + appear outside a character class, as described in the section entitled "Generic character types" above. The escape sequence \b has a different - meaning inside a character class; it matches the backspace character. - The sequences \B, \N, \R, and \X are not special inside a character - class. Like any other unrecognized escape sequences, they are treated - as the literal characters "B", "N", "R", and "X" by default, but cause + meaning inside a character class; it matches the backspace character. + The sequences \B, \N, \R, and \X are not special inside a character + class. Like any other unrecognized escape sequences, they are treated + as the literal characters "B", "N", "R", and "X" by default, but cause an error if the PCRE_EXTRA option is set. - A circumflex can conveniently be used with the upper case character - types to specify a more restricted set of characters than the matching - lower case type. For example, the class [^\W_] matches any letter or + A circumflex can conveniently be used with the upper case character + types to specify a more restricted set of characters than the matching + lower case type. For example, the class [^\W_] matches any letter or digit, but not underscore, whereas [\w] includes underscore. A positive character class should be read as "something OR something OR ..." and a negative class as "NOT something AND NOT something AND NOT ...". - The only metacharacters that are recognized in character classes are - backslash, hyphen (only where it can be interpreted as specifying a - range), circumflex (only at the start), opening square bracket (only - when it can be interpreted as introducing a POSIX class name, or for a - special compatibility feature - see the next two sections), and the + The only metacharacters that are recognized in character classes are + backslash, hyphen (only where it can be interpreted as specifying a + range), circumflex (only at the start), opening square bracket (only + when it can be interpreted as introducing a POSIX class name, or for a + special compatibility feature - see the next two sections), and the terminating closing square bracket. However, escaping other non- alphanumeric characters does no harm. @@ -5860,7 +5861,7 @@ SQUARE BRACKETS AND CHARACTER CLASSES POSIX CHARACTER CLASSES Perl supports the POSIX notation for character classes. This uses names - enclosed by [: and :] within the enclosing square brackets. PCRE also + enclosed by [: and :] within the enclosing square brackets. PCRE also supports this notation. For example, [01[:alpha:]%] @@ -5883,28 +5884,28 @@ POSIX CHARACTER CLASSES word "word" characters (same as \w) xdigit hexadecimal digits - The default "space" characters are HT (9), LF (10), VT (11), FF (12), - CR (13), and space (32). If locale-specific matching is taking place, - the list of space characters may be different; there may be fewer or + The default "space" characters are HT (9), LF (10), VT (11), FF (12), + CR (13), and space (32). If locale-specific matching is taking place, + the list of space characters may be different; there may be fewer or more of them. "Space" used to be different to \s, which did not include VT, for Perl compatibility. However, Perl changed at release 5.18, and - PCRE followed at release 8.34. "Space" and \s now match the same set + PCRE followed at release 8.34. "Space" and \s now match the same set of characters. - The name "word" is a Perl extension, and "blank" is a GNU extension - from Perl 5.8. Another Perl extension is negation, which is indicated + The name "word" is a Perl extension, and "blank" is a GNU extension + from Perl 5.8. Another Perl extension is negation, which is indicated by a ^ character after the colon. For example, [12[:^digit:]] - matches "1", "2", or any non-digit. PCRE (and Perl) also recognize the + matches "1", "2", or any non-digit. PCRE (and Perl) also recognize the POSIX syntax [.ch.] and [=ch=] where "ch" is a "collating element", but these are not supported, and an error is given if they are encountered. By default, characters with values greater than 128 do not match any of - the POSIX character classes. However, if the PCRE_UCP option is passed - to pcre_compile(), some of the classes are changed so that Unicode - character properties are used. This is achieved by replacing certain + the POSIX character classes. However, if the PCRE_UCP option is passed + to pcre_compile(), some of the classes are changed so that Unicode + character properties are used. This is achieved by replacing certain POSIX classes by other sequences, as follows: [:alnum:] becomes \p{Xan} @@ -5916,10 +5917,10 @@ POSIX CHARACTER CLASSES [:upper:] becomes \p{Lu} [:word:] becomes \p{Xwd} - Negated versions, such as [:^alpha:] use \P instead of \p. Three other + Negated versions, such as [:^alpha:] use \P instead of \p. Three other POSIX classes are handled specially in UCP mode: - [:graph:] This matches characters that have glyphs that mark the page + [:graph:] This matches characters that have glyphs that mark the page when printed. In Unicode property terms, it matches all char- acters with the L, M, N, P, S, or Cf properties, except for: @@ -5928,58 +5929,58 @@ POSIX CHARACTER CLASSES U+2066 - U+2069 Various "isolate"s - [:print:] This matches the same characters as [:graph:] plus space - characters that are not controls, that is, characters with + [:print:] This matches the same characters as [:graph:] plus space + characters that are not controls, that is, characters with the Zs property. [:punct:] This matches all characters that have the Unicode P (punctua- - tion) property, plus those characters whose code points are + tion) property, plus those characters whose code points are less than 128 that have the S (Symbol) property. - The other POSIX classes are unchanged, and match only characters with + The other POSIX classes are unchanged, and match only characters with code points less than 128. COMPATIBILITY FEATURE FOR WORD BOUNDARIES - In the POSIX.2 compliant library that was included in 4.4BSD Unix, the - ugly syntax [[:<:]] and [[:>:]] is used for matching "start of word" + In the POSIX.2 compliant library that was included in 4.4BSD Unix, the + ugly syntax [[:<:]] and [[:>:]] is used for matching "start of word" and "end of word". PCRE treats these items as follows: [[:<:]] is converted to \b(?=\w) [[:>:]] is converted to \b(?<=\w) Only these exact character sequences are recognized. A sequence such as - [a[:<:]b] provokes error for an unrecognized POSIX class name. This - support is not compatible with Perl. It is provided to help migrations + [a[:<:]b] provokes error for an unrecognized POSIX class name. This + support is not compatible with Perl. It is provided to help migrations from other environments, and is best not used in any new patterns. Note - that \b matches at the start and the end of a word (see "Simple asser- - tions" above), and in a Perl-style pattern the preceding or following - character normally shows which is wanted, without the need for the - assertions that are used above in order to give exactly the POSIX be- + that \b matches at the start and the end of a word (see "Simple asser- + tions" above), and in a Perl-style pattern the preceding or following + character normally shows which is wanted, without the need for the + assertions that are used above in order to give exactly the POSIX be- haviour. VERTICAL BAR - Vertical bar characters are used to separate alternative patterns. For + Vertical bar characters are used to separate alternative patterns. For example, the pattern gilbert|sullivan - matches either "gilbert" or "sullivan". Any number of alternatives may - appear, and an empty alternative is permitted (matching the empty + matches either "gilbert" or "sullivan". Any number of alternatives may + appear, and an empty alternative is permitted (matching the empty string). The matching process tries each alternative in turn, from left - to right, and the first one that succeeds is used. If the alternatives - are within a subpattern (defined below), "succeeds" means matching the + to right, and the first one that succeeds is used. If the alternatives + are within a subpattern (defined below), "succeeds" means matching the rest of the main pattern as well as the alternative in the subpattern. INTERNAL OPTION SETTING - The settings of the PCRE_CASELESS, PCRE_MULTILINE, PCRE_DOTALL, and - PCRE_EXTENDED options (which are Perl-compatible) can be changed from - within the pattern by a sequence of Perl option letters enclosed + The settings of the PCRE_CASELESS, PCRE_MULTILINE, PCRE_DOTALL, and + PCRE_EXTENDED options (which are Perl-compatible) can be changed from + within the pattern by a sequence of Perl option letters enclosed between "(?" and ")". The option letters are i for PCRE_CASELESS @@ -5989,51 +5990,47 @@ INTERNAL OPTION SETTING For example, (?im) sets caseless, multiline matching. It is also possi- ble to unset these options by preceding the letter with a hyphen, and a - combined setting and unsetting such as (?im-sx), which sets PCRE_CASE- - LESS and PCRE_MULTILINE while unsetting PCRE_DOTALL and PCRE_EXTENDED, - is also permitted. If a letter appears both before and after the + combined setting and unsetting such as (?im-sx), which sets PCRE_CASE- + LESS and PCRE_MULTILINE while unsetting PCRE_DOTALL and PCRE_EXTENDED, + is also permitted. If a letter appears both before and after the hyphen, the option is unset. - The PCRE-specific options PCRE_DUPNAMES, PCRE_UNGREEDY, and PCRE_EXTRA - can be changed in the same way as the Perl-compatible options by using + The PCRE-specific options PCRE_DUPNAMES, PCRE_UNGREEDY, and PCRE_EXTRA + can be changed in the same way as the Perl-compatible options by using the characters J, U and X respectively. - When one of these option changes occurs at top level (that is, not - inside subpattern parentheses), the change applies to the remainder of - the pattern that follows. If the change is placed right at the start of - a pattern, PCRE extracts it into the global options (and it will there- - fore show up in data extracted by the pcre_fullinfo() function). - - An option change within a subpattern (see below for a description of - subpatterns) affects only that part of the subpattern that follows it, - so + When one of these option changes occurs at top level (that is, not + inside subpattern parentheses), the change applies to the remainder of + the pattern that follows. An option change within a subpattern (see + below for a description of subpatterns) affects only that part of the + subpattern that follows it, so (a(?i)b)c matches abc and aBc and no other strings (assuming PCRE_CASELESS is not - used). By this means, options can be made to have different settings - in different parts of the pattern. Any changes made in one alternative - do carry on into subsequent branches within the same subpattern. For + used). By this means, options can be made to have different settings + in different parts of the pattern. Any changes made in one alternative + do carry on into subsequent branches within the same subpattern. For example, (a(?i)b|c) - matches "ab", "aB", "c", and "C", even though when matching "C" the - first branch is abandoned before the option setting. This is because - the effects of option settings happen at compile time. There would be + matches "ab", "aB", "c", and "C", even though when matching "C" the + first branch is abandoned before the option setting. This is because + the effects of option settings happen at compile time. There would be some very weird behaviour otherwise. - Note: There are other PCRE-specific options that can be set by the - application when the compiling or matching functions are called. In - some cases the pattern can contain special leading sequences such as - (*CRLF) to override what the application has set or what has been - defaulted. Details are given in the section entitled "Newline - sequences" above. There are also the (*UTF8), (*UTF16),(*UTF32), and - (*UCP) leading sequences that can be used to set UTF and Unicode prop- - erty modes; they are equivalent to setting the PCRE_UTF8, PCRE_UTF16, - PCRE_UTF32 and the PCRE_UCP options, respectively. The (*UTF) sequence - is a generic version that can be used with any of the libraries. How- - ever, the application can set the PCRE_NEVER_UTF option, which locks + Note: There are other PCRE-specific options that can be set by the + application when the compiling or matching functions are called. In + some cases the pattern can contain special leading sequences such as + (*CRLF) to override what the application has set or what has been + defaulted. Details are given in the section entitled "Newline + sequences" above. There are also the (*UTF8), (*UTF16),(*UTF32), and + (*UCP) leading sequences that can be used to set UTF and Unicode prop- + erty modes; they are equivalent to setting the PCRE_UTF8, PCRE_UTF16, + PCRE_UTF32 and the PCRE_UCP options, respectively. The (*UTF) sequence + is a generic version that can be used with any of the libraries. How- + ever, the application can set the PCRE_NEVER_UTF option, which locks out the use of the (*UTF) sequences. @@ -6046,18 +6043,18 @@ SUBPATTERNS cat(aract|erpillar|) - matches "cataract", "caterpillar", or "cat". Without the parentheses, + matches "cataract", "caterpillar", or "cat". Without the parentheses, it would match "cataract", "erpillar" or an empty string. - 2. It sets up the subpattern as a capturing subpattern. This means - that, when the whole pattern matches, that portion of the subject + 2. It sets up the subpattern as a capturing subpattern. This means + that, when the whole pattern matches, that portion of the subject string that matched the subpattern is passed back to the caller via the - ovector argument of the matching function. (This applies only to the - traditional matching functions; the DFA matching functions do not sup- + ovector argument of the matching function. (This applies only to the + traditional matching functions; the DFA matching functions do not sup- port capturing.) Opening parentheses are counted from left to right (starting from 1) to - obtain numbers for the capturing subpatterns. For example, if the + obtain numbers for the capturing subpatterns. For example, if the string "the red king" is matched against the pattern the ((red|white) (king|queen)) @@ -6065,12 +6062,12 @@ SUBPATTERNS the captured substrings are "red king", "red", and "king", and are num- bered 1, 2, and 3, respectively. - The fact that plain parentheses fulfil two functions is not always - helpful. There are often times when a grouping subpattern is required - without a capturing requirement. If an opening parenthesis is followed - by a question mark and a colon, the subpattern does not do any captur- - ing, and is not counted when computing the number of any subsequent - capturing subpatterns. For example, if the string "the white queen" is + The fact that plain parentheses fulfil two functions is not always + helpful. There are often times when a grouping subpattern is required + without a capturing requirement. If an opening parenthesis is followed + by a question mark and a colon, the subpattern does not do any captur- + ing, and is not counted when computing the number of any subsequent + capturing subpatterns. For example, if the string "the white queen" is matched against the pattern the ((?:red|white) (king|queen)) @@ -6078,37 +6075,37 @@ SUBPATTERNS the captured substrings are "white queen" and "queen", and are numbered 1 and 2. The maximum number of capturing subpatterns is 65535. - As a convenient shorthand, if any option settings are required at the - start of a non-capturing subpattern, the option letters may appear + As a convenient shorthand, if any option settings are required at the + start of a non-capturing subpattern, the option letters may appear between the "?" and the ":". Thus the two patterns (?i:saturday|sunday) (?:(?i)saturday|sunday) match exactly the same set of strings. Because alternative branches are - tried from left to right, and options are not reset until the end of - the subpattern is reached, an option setting in one branch does affect - subsequent branches, so the above patterns match "SUNDAY" as well as + tried from left to right, and options are not reset until the end of + the subpattern is reached, an option setting in one branch does affect + subsequent branches, so the above patterns match "SUNDAY" as well as "Saturday". DUPLICATE SUBPATTERN NUMBERS Perl 5.10 introduced a feature whereby each alternative in a subpattern - uses the same numbers for its capturing parentheses. Such a subpattern - starts with (?| and is itself a non-capturing subpattern. For example, + uses the same numbers for its capturing parentheses. Such a subpattern + starts with (?| and is itself a non-capturing subpattern. For example, consider this pattern: (?|(Sat)ur|(Sun))day - Because the two alternatives are inside a (?| group, both sets of cap- - turing parentheses are numbered one. Thus, when the pattern matches, - you can look at captured substring number one, whichever alternative - matched. This construct is useful when you want to capture part, but + Because the two alternatives are inside a (?| group, both sets of cap- + turing parentheses are numbered one. Thus, when the pattern matches, + you can look at captured substring number one, whichever alternative + matched. This construct is useful when you want to capture part, but not all, of one of a number of alternatives. Inside a (?| group, paren- - theses are numbered as usual, but the number is reset at the start of - each branch. The numbers of any capturing parentheses that follow the - subpattern start after the highest number used in any branch. The fol- + theses are numbered as usual, but the number is reset at the start of + each branch. The numbers of any capturing parentheses that follow the + subpattern start after the highest number used in any branch. The fol- lowing example is taken from the Perl documentation. The numbers under- neath show in which buffer the captured content will be stored. @@ -6116,58 +6113,58 @@ DUPLICATE SUBPATTERN NUMBERS / ( a ) (?| x ( y ) z | (p (q) r) | (t) u (v) ) ( z ) /x # 1 2 2 3 2 3 4 - A back reference to a numbered subpattern uses the most recent value - that is set for that number by any subpattern. The following pattern + A back reference to a numbered subpattern uses the most recent value + that is set for that number by any subpattern. The following pattern matches "abcabc" or "defdef": /(?|(abc)|(def))\1/ - In contrast, a subroutine call to a numbered subpattern always refers - to the first one in the pattern with the given number. The following + In contrast, a subroutine call to a numbered subpattern always refers + to the first one in the pattern with the given number. The following pattern matches "abcabc" or "defabc": /(?|(abc)|(def))(?1)/ - If a condition test for a subpattern's having matched refers to a non- - unique number, the test is true if any of the subpatterns of that num- + If a condition test for a subpattern's having matched refers to a non- + unique number, the test is true if any of the subpatterns of that num- ber have matched. - An alternative approach to using this "branch reset" feature is to use + An alternative approach to using this "branch reset" feature is to use duplicate named subpatterns, as described in the next section. NAMED SUBPATTERNS - Identifying capturing parentheses by number is simple, but it can be - very hard to keep track of the numbers in complicated regular expres- - sions. Furthermore, if an expression is modified, the numbers may - change. To help with this difficulty, PCRE supports the naming of sub- + Identifying capturing parentheses by number is simple, but it can be + very hard to keep track of the numbers in complicated regular expres- + sions. Furthermore, if an expression is modified, the numbers may + change. To help with this difficulty, PCRE supports the naming of sub- patterns. This feature was not added to Perl until release 5.10. Python - had the feature earlier, and PCRE introduced it at release 4.0, using - the Python syntax. PCRE now supports both the Perl and the Python syn- - tax. Perl allows identically numbered subpatterns to have different + had the feature earlier, and PCRE introduced it at release 4.0, using + the Python syntax. PCRE now supports both the Perl and the Python syn- + tax. Perl allows identically numbered subpatterns to have different names, but PCRE does not. - In PCRE, a subpattern can be named in one of three ways: (?<name>...) - or (?'name'...) as in Perl, or (?P<name>...) as in Python. References - to capturing parentheses from other parts of the pattern, such as back - references, recursion, and conditions, can be made by name as well as + In PCRE, a subpattern can be named in one of three ways: (?<name>...) + or (?'name'...) as in Perl, or (?P<name>...) as in Python. References + to capturing parentheses from other parts of the pattern, such as back + references, recursion, and conditions, can be made by name as well as by number. - Names consist of up to 32 alphanumeric characters and underscores, but - must start with a non-digit. Named capturing parentheses are still - allocated numbers as well as names, exactly as if the names were not - present. The PCRE API provides function calls for extracting the name- - to-number translation table from a compiled pattern. There is also a + Names consist of up to 32 alphanumeric characters and underscores, but + must start with a non-digit. Named capturing parentheses are still + allocated numbers as well as names, exactly as if the names were not + present. The PCRE API provides function calls for extracting the name- + to-number translation table from a compiled pattern. There is also a convenience function for extracting a captured substring by name. - By default, a name must be unique within a pattern, but it is possible + By default, a name must be unique within a pattern, but it is possible to relax this constraint by setting the PCRE_DUPNAMES option at compile - time. (Duplicate names are also always permitted for subpatterns with - the same number, set up as described in the previous section.) Dupli- - cate names can be useful for patterns where only one instance of the - named parentheses can match. Suppose you want to match the name of a - weekday, either as a 3-letter abbreviation or as the full name, and in + time. (Duplicate names are also always permitted for subpatterns with + the same number, set up as described in the previous section.) Dupli- + cate names can be useful for patterns where only one instance of the + named parentheses can match. Suppose you want to match the name of a + weekday, either as a 3-letter abbreviation or as the full name, and in both cases you want to extract the abbreviation. This pattern (ignoring the line breaks) does the job: @@ -6177,18 +6174,18 @@ NAMED SUBPATTERNS (?<DN>Thu)(?:rsday)?| (?<DN>Sat)(?:urday)? - There are five capturing substrings, but only one is ever set after a + There are five capturing substrings, but only one is ever set after a match. (An alternative way of solving this problem is to use a "branch reset" subpattern, as described in the previous section.) - The convenience function for extracting the data by name returns the - substring for the first (and in this example, the only) subpattern of - that name that matched. This saves searching to find which numbered + The convenience function for extracting the data by name returns the + substring for the first (and in this example, the only) subpattern of + that name that matched. This saves searching to find which numbered subpattern it was. - If you make a back reference to a non-unique named subpattern from - elsewhere in the pattern, the subpatterns to which the name refers are - checked in the order in which they appear in the overall pattern. The + If you make a back reference to a non-unique named subpattern from + elsewhere in the pattern, the subpatterns to which the name refers are + checked in the order in which they appear in the overall pattern. The first one that is set is used for the reference. For example, this pat- tern matches both "foofoo" and "barbar" but not "foobar" or "barfoo": @@ -6196,29 +6193,29 @@ NAMED SUBPATTERNS If you make a subroutine call to a non-unique named subpattern, the one - that corresponds to the first occurrence of the name is used. In the + that corresponds to the first occurrence of the name is used. In the absence of duplicate numbers (see the previous section) this is the one with the lowest number. If you use a named reference in a condition test (see the section about conditions below), either to check whether a subpattern has matched, or - to check for recursion, all subpatterns with the same name are tested. - If the condition is true for any one of them, the overall condition is - true. This is the same behaviour as testing by number. For further - details of the interfaces for handling named subpatterns, see the + to check for recursion, all subpatterns with the same name are tested. + If the condition is true for any one of them, the overall condition is + true. This is the same behaviour as testing by number. For further + details of the interfaces for handling named subpatterns, see the pcreapi documentation. Warning: You cannot use different names to distinguish between two sub- - patterns with the same number because PCRE uses only the numbers when + patterns with the same number because PCRE uses only the numbers when matching. For this reason, an error is given at compile time if differ- - ent names are given to subpatterns with the same number. However, you + ent names are given to subpatterns with the same number. However, you can always give the same name to subpatterns with the same number, even when PCRE_DUPNAMES is not set. REPETITION - Repetition is specified by quantifiers, which can follow any of the + Repetition is specified by quantifiers, which can follow any of the following items: a literal data character @@ -6232,17 +6229,17 @@ REPETITION a parenthesized subpattern (including assertions) a subroutine call to a subpattern (recursive or otherwise) - The general repetition quantifier specifies a minimum and maximum num- - ber of permitted matches, by giving the two numbers in curly brackets - (braces), separated by a comma. The numbers must be less than 65536, + The general repetition quantifier specifies a minimum and maximum num- + ber of permitted matches, by giving the two numbers in curly brackets + (braces), separated by a comma. The numbers must be less than 65536, and the first must be less than or equal to the second. For example: z{2,4} - matches "zz", "zzz", or "zzzz". A closing brace on its own is not a - special character. If the second number is omitted, but the comma is - present, there is no upper limit; if the second number and the comma - are both omitted, the quantifier specifies an exact number of required + matches "zz", "zzz", or "zzzz". A closing brace on its own is not a + special character. If the second number is omitted, but the comma is + present, there is no upper limit; if the second number and the comma + are both omitted, the quantifier specifies an exact number of required matches. Thus [aeiou]{3,} @@ -6251,50 +6248,50 @@ REPETITION \d{8} - matches exactly 8 digits. An opening curly bracket that appears in a - position where a quantifier is not allowed, or one that does not match - the syntax of a quantifier, is taken as a literal character. For exam- + matches exactly 8 digits. An opening curly bracket that appears in a + position where a quantifier is not allowed, or one that does not match + the syntax of a quantifier, is taken as a literal character. For exam- ple, {,6} is not a quantifier, but a literal string of four characters. In UTF modes, quantifiers apply to characters rather than to individual - data units. Thus, for example, \x{100}{2} matches two characters, each + data units. Thus, for example, \x{100}{2} matches two characters, each of which is represented by a two-byte sequence in a UTF-8 string. Simi- - larly, \X{3} matches three Unicode extended grapheme clusters, each of - which may be several data units long (and they may be of different + larly, \X{3} matches three Unicode extended grapheme clusters, each of + which may be several data units long (and they may be of different lengths). The quantifier {0} is permitted, causing the expression to behave as if the previous item and the quantifier were not present. This may be use- - ful for subpatterns that are referenced as subroutines from elsewhere + ful for subpatterns that are referenced as subroutines from elsewhere in the pattern (but see also the section entitled "Defining subpatterns - for use by reference only" below). Items other than subpatterns that + for use by reference only" below). Items other than subpatterns that have a {0} quantifier are omitted from the compiled pattern. - For convenience, the three most common quantifiers have single-charac- + For convenience, the three most common quantifiers have single-charac- ter abbreviations: * is equivalent to {0,} + is equivalent to {1,} ? is equivalent to {0,1} - It is possible to construct infinite loops by following a subpattern + It is possible to construct infinite loops by following a subpattern that can match no characters with a quantifier that has no upper limit, for example: (a?)* Earlier versions of Perl and PCRE used to give an error at compile time - for such patterns. However, because there are cases where this can be - useful, such patterns are now accepted, but if any repetition of the - subpattern does in fact match no characters, the loop is forcibly bro- + for such patterns. However, because there are cases where this can be + useful, such patterns are now accepted, but if any repetition of the + subpattern does in fact match no characters, the loop is forcibly bro- ken. - By default, the quantifiers are "greedy", that is, they match as much - as possible (up to the maximum number of permitted times), without - causing the rest of the pattern to fail. The classic example of where + By default, the quantifiers are "greedy", that is, they match as much + as possible (up to the maximum number of permitted times), without + causing the rest of the pattern to fail. The classic example of where this gives problems is in trying to match comments in C programs. These - appear between /* and */ and within the comment, individual * and / - characters may appear. An attempt to match C comments by applying the + appear between /* and */ and within the comment, individual * and / + characters may appear. An attempt to match C comments by applying the pattern /\*.*\*/ @@ -6303,19 +6300,19 @@ REPETITION /* first comment */ not comment /* second comment */ - fails, because it matches the entire string owing to the greediness of + fails, because it matches the entire string owing to the greediness of the .* item. - However, if a quantifier is followed by a question mark, it ceases to + However, if a quantifier is followed by a question mark, it ceases to be greedy, and instead matches the minimum number of times possible, so the pattern /\*.*?\*/ - does the right thing with the C comments. The meaning of the various - quantifiers is not otherwise changed, just the preferred number of - matches. Do not confuse this use of question mark with its use as a - quantifier in its own right. Because it has two uses, it can sometimes + does the right thing with the C comments. The meaning of the various + quantifiers is not otherwise changed, just the preferred number of + matches. Do not confuse this use of question mark with its use as a + quantifier in its own right. Because it has two uses, it can sometimes appear doubled, as in \d??\d @@ -6323,45 +6320,45 @@ REPETITION which matches one digit by preference, but can match two if that is the only way the rest of the pattern matches. - If the PCRE_UNGREEDY option is set (an option that is not available in - Perl), the quantifiers are not greedy by default, but individual ones - can be made greedy by following them with a question mark. In other + If the PCRE_UNGREEDY option is set (an option that is not available in + Perl), the quantifiers are not greedy by default, but individual ones + can be made greedy by following them with a question mark. In other words, it inverts the default behaviour. - When a parenthesized subpattern is quantified with a minimum repeat - count that is greater than 1 or with a limited maximum, more memory is - required for the compiled pattern, in proportion to the size of the + When a parenthesized subpattern is quantified with a minimum repeat + count that is greater than 1 or with a limited maximum, more memory is + required for the compiled pattern, in proportion to the size of the minimum or maximum. If a pattern starts with .* or .{0,} and the PCRE_DOTALL option (equiv- - alent to Perl's /s) is set, thus allowing the dot to match newlines, - the pattern is implicitly anchored, because whatever follows will be - tried against every character position in the subject string, so there - is no point in retrying the overall match at any position after the - first. PCRE normally treats such a pattern as though it were preceded + alent to Perl's /s) is set, thus allowing the dot to match newlines, + the pattern is implicitly anchored, because whatever follows will be + tried against every character position in the subject string, so there + is no point in retrying the overall match at any position after the + first. PCRE normally treats such a pattern as though it were preceded by \A. - In cases where it is known that the subject string contains no new- - lines, it is worth setting PCRE_DOTALL in order to obtain this opti- + In cases where it is known that the subject string contains no new- + lines, it is worth setting PCRE_DOTALL in order to obtain this opti- mization, or alternatively using ^ to indicate anchoring explicitly. - However, there are some cases where the optimization cannot be used. + However, there are some cases where the optimization cannot be used. When .* is inside capturing parentheses that are the subject of a back reference elsewhere in the pattern, a match at the start may fail where a later one succeeds. Consider, for example: (.*)abc\1 - If the subject is "xyz123abc123" the match point is the fourth charac- + If the subject is "xyz123abc123" the match point is the fourth charac- ter. For this reason, such a pattern is not implicitly anchored. - Another case where implicit anchoring is not applied is when the lead- - ing .* is inside an atomic group. Once again, a match at the start may + Another case where implicit anchoring is not applied is when the lead- + ing .* is inside an atomic group. Once again, a match at the start may fail where a later one succeeds. Consider this pattern: (?>.*?a)b - It matches "ab" in the subject "aab". The use of the backtracking con- + It matches "ab" in the subject "aab". The use of the backtracking con- trol verbs (*PRUNE) and (*SKIP) also disable this optimization. When a capturing subpattern is repeated, the value captured is the sub- @@ -6370,8 +6367,8 @@ REPETITION (tweedle[dume]{3}\s*)+ has matched "tweedledum tweedledee" the value of the captured substring - is "tweedledee". However, if there are nested capturing subpatterns, - the corresponding captured values may have been set in previous itera- + is "tweedledee". However, if there are nested capturing subpatterns, + the corresponding captured values may have been set in previous itera- tions. For example, after /(a|(b))+/ @@ -6381,53 +6378,53 @@ REPETITION ATOMIC GROUPING AND POSSESSIVE QUANTIFIERS - With both maximizing ("greedy") and minimizing ("ungreedy" or "lazy") - repetition, failure of what follows normally causes the repeated item - to be re-evaluated to see if a different number of repeats allows the - rest of the pattern to match. Sometimes it is useful to prevent this, - either to change the nature of the match, or to cause it fail earlier - than it otherwise might, when the author of the pattern knows there is + With both maximizing ("greedy") and minimizing ("ungreedy" or "lazy") + repetition, failure of what follows normally causes the repeated item + to be re-evaluated to see if a different number of repeats allows the + rest of the pattern to match. Sometimes it is useful to prevent this, + either to change the nature of the match, or to cause it fail earlier + than it otherwise might, when the author of the pattern knows there is no point in carrying on. - Consider, for example, the pattern \d+foo when applied to the subject + Consider, for example, the pattern \d+foo when applied to the subject line 123456bar After matching all 6 digits and then failing to match "foo", the normal - action of the matcher is to try again with only 5 digits matching the - \d+ item, and then with 4, and so on, before ultimately failing. - "Atomic grouping" (a term taken from Jeffrey Friedl's book) provides - the means for specifying that once a subpattern has matched, it is not + action of the matcher is to try again with only 5 digits matching the + \d+ item, and then with 4, and so on, before ultimately failing. + "Atomic grouping" (a term taken from Jeffrey Friedl's book) provides + the means for specifying that once a subpattern has matched, it is not to be re-evaluated in this way. - If we use atomic grouping for the previous example, the matcher gives - up immediately on failing to match "foo" the first time. The notation + If we use atomic grouping for the previous example, the matcher gives + up immediately on failing to match "foo" the first time. The notation is a kind of special parenthesis, starting with (?> as in this example: (?>\d+)foo - This kind of parenthesis "locks up" the part of the pattern it con- - tains once it has matched, and a failure further into the pattern is - prevented from backtracking into it. Backtracking past it to previous + This kind of parenthesis "locks up" the part of the pattern it con- + tains once it has matched, and a failure further into the pattern is + prevented from backtracking into it. Backtracking past it to previous items, however, works as normal. - An alternative description is that a subpattern of this type matches - the string of characters that an identical standalone pattern would + An alternative description is that a subpattern of this type matches + the string of characters that an identical standalone pattern would match, if anchored at the current point in the subject string. Atomic grouping subpatterns are not capturing subpatterns. Simple cases such as the above example can be thought of as a maximizing repeat that - must swallow everything it can. So, while both \d+ and \d+? are pre- - pared to adjust the number of digits they match in order to make the + must swallow everything it can. So, while both \d+ and \d+? are pre- + pared to adjust the number of digits they match in order to make the rest of the pattern match, (?>\d+) can only match an entire sequence of digits. - Atomic groups in general can of course contain arbitrarily complicated - subpatterns, and can be nested. However, when the subpattern for an + Atomic groups in general can of course contain arbitrarily complicated + subpatterns, and can be nested. However, when the subpattern for an atomic group is just a single repeated item, as in the example above, a - simpler notation, called a "possessive quantifier" can be used. This - consists of an additional + character following a quantifier. Using + simpler notation, called a "possessive quantifier" can be used. This + consists of an additional + character following a quantifier. Using this notation, the previous example can be rewritten as \d++foo @@ -6437,45 +6434,45 @@ ATOMIC GROUPING AND POSSESSIVE QUANTIFIERS (abc|xyz){2,3}+ - Possessive quantifiers are always greedy; the setting of the + Possessive quantifiers are always greedy; the setting of the PCRE_UNGREEDY option is ignored. They are a convenient notation for the - simpler forms of atomic group. However, there is no difference in the - meaning of a possessive quantifier and the equivalent atomic group, - though there may be a performance difference; possessive quantifiers + simpler forms of atomic group. However, there is no difference in the + meaning of a possessive quantifier and the equivalent atomic group, + though there may be a performance difference; possessive quantifiers should be slightly faster. - The possessive quantifier syntax is an extension to the Perl 5.8 syn- - tax. Jeffrey Friedl originated the idea (and the name) in the first + The possessive quantifier syntax is an extension to the Perl 5.8 syn- + tax. Jeffrey Friedl originated the idea (and the name) in the first edition of his book. Mike McCloskey liked it, so implemented it when he - built Sun's Java package, and PCRE copied it from there. It ultimately + built Sun's Java package, and PCRE copied it from there. It ultimately found its way into Perl at release 5.10. PCRE has an optimization that automatically "possessifies" certain sim- - ple pattern constructs. For example, the sequence A+B is treated as - A++B because there is no point in backtracking into a sequence of A's + ple pattern constructs. For example, the sequence A+B is treated as + A++B because there is no point in backtracking into a sequence of A's when B must follow. - When a pattern contains an unlimited repeat inside a subpattern that - can itself be repeated an unlimited number of times, the use of an - atomic group is the only way to avoid some failing matches taking a + When a pattern contains an unlimited repeat inside a subpattern that + can itself be repeated an unlimited number of times, the use of an + atomic group is the only way to avoid some failing matches taking a very long time indeed. The pattern (\D+|<\d+>)*[!?] - matches an unlimited number of substrings that either consist of non- - digits, or digits enclosed in <>, followed by either ! or ?. When it + matches an unlimited number of substrings that either consist of non- + digits, or digits enclosed in <>, followed by either ! or ?. When it matches, it runs quickly. However, if it is applied to aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - it takes a long time before reporting failure. This is because the - string can be divided between the internal \D+ repeat and the external - * repeat in a large number of ways, and all have to be tried. (The - example uses [!?] rather than a single character at the end, because - both PCRE and Perl have an optimization that allows for fast failure - when a single character is used. They remember the last single charac- - ter that is required for a match, and fail early if it is not present - in the string.) If the pattern is changed so that it uses an atomic + it takes a long time before reporting failure. This is because the + string can be divided between the internal \D+ repeat and the external + * repeat in a large number of ways, and all have to be tried. (The + example uses [!?] rather than a single character at the end, because + both PCRE and Perl have an optimization that allows for fast failure + when a single character is used. They remember the last single charac- + ter that is required for a match, and fail early if it is not present + in the string.) If the pattern is changed so that it uses an atomic group, like this: ((?>\D+)|<\d+>)*[!?] @@ -6487,28 +6484,28 @@ BACK REFERENCES Outside a character class, a backslash followed by a digit greater than 0 (and possibly further digits) is a back reference to a capturing sub- - pattern earlier (that is, to its left) in the pattern, provided there + pattern earlier (that is, to its left) in the pattern, provided there have been that many previous capturing left parentheses. However, if the decimal number following the backslash is less than 10, - it is always taken as a back reference, and causes an error only if - there are not that many capturing left parentheses in the entire pat- - tern. In other words, the parentheses that are referenced need not be - to the left of the reference for numbers less than 10. A "forward back - reference" of this type can make sense when a repetition is involved - and the subpattern to the right has participated in an earlier itera- + it is always taken as a back reference, and causes an error only if + there are not that many capturing left parentheses in the entire pat- + tern. In other words, the parentheses that are referenced need not be + to the left of the reference for numbers less than 10. A "forward back + reference" of this type can make sense when a repetition is involved + and the subpattern to the right has participated in an earlier itera- tion. - It is not possible to have a numerical "forward back reference" to a - subpattern whose number is 10 or more using this syntax because a - sequence such as \50 is interpreted as a character defined in octal. + It is not possible to have a numerical "forward back reference" to a + subpattern whose number is 10 or more using this syntax because a + sequence such as \50 is interpreted as a character defined in octal. See the subsection entitled "Non-printing characters" above for further - details of the handling of digits following a backslash. There is no - such problem when named parentheses are used. A back reference to any + details of the handling of digits following a backslash. There is no + such problem when named parentheses are used. A back reference to any subpattern is possible using named parentheses (see below). - Another way of avoiding the ambiguity inherent in the use of digits - following a backslash is to use the \g escape sequence. This escape + Another way of avoiding the ambiguity inherent in the use of digits + following a backslash is to use the \g escape sequence. This escape must be followed by an unsigned number or a negative number, optionally enclosed in braces. These examples are all identical: @@ -6516,7 +6513,7 @@ BACK REFERENCES (ring), \g1 (ring), \g{1} - An unsigned number specifies an absolute reference without the ambigu- + An unsigned number specifies an absolute reference without the ambigu- ity that is present in the older syntax. It is also useful when literal digits follow the reference. A negative number is a relative reference. Consider this example: @@ -6525,33 +6522,33 @@ BACK REFERENCES The sequence \g{-1} is a reference to the most recently started captur- ing subpattern before \g, that is, is it equivalent to \2 in this exam- - ple. Similarly, \g{-2} would be equivalent to \1. The use of relative - references can be helpful in long patterns, and also in patterns that - are created by joining together fragments that contain references + ple. Similarly, \g{-2} would be equivalent to \1. The use of relative + references can be helpful in long patterns, and also in patterns that + are created by joining together fragments that contain references within themselves. - A back reference matches whatever actually matched the capturing sub- - pattern in the current subject string, rather than anything matching + A back reference matches whatever actually matched the capturing sub- + pattern in the current subject string, rather than anything matching the subpattern itself (see "Subpatterns as subroutines" below for a way of doing that). So the pattern (sens|respons)e and \1ibility - matches "sense and sensibility" and "response and responsibility", but - not "sense and responsibility". If caseful matching is in force at the - time of the back reference, the case of letters is relevant. For exam- + matches "sense and sensibility" and "response and responsibility", but + not "sense and responsibility". If caseful matching is in force at the + time of the back reference, the case of letters is relevant. For exam- ple, ((?i)rah)\s+\1 - matches "rah rah" and "RAH RAH", but not "RAH rah", even though the + matches "rah rah" and "RAH RAH", but not "RAH rah", even though the original capturing subpattern is matched caselessly. - There are several different ways of writing back references to named - subpatterns. The .NET syntax \k{name} and the Perl syntax \k<name> or - \k'name' are supported, as is the Python syntax (?P=name). Perl 5.10's + There are several different ways of writing back references to named + subpatterns. The .NET syntax \k{name} and the Perl syntax \k<name> or + \k'name' are supported, as is the Python syntax (?P=name). Perl 5.10's unified back reference syntax, in which \g can be used for both numeric - and named references, is also supported. We could rewrite the above + and named references, is also supported. We could rewrite the above example in any of the following ways: (?<p1>(?i)rah)\s+\k<p1> @@ -6559,84 +6556,92 @@ BACK REFERENCES (?P<p1>(?i)rah)\s+(?P=p1) (?<p1>(?i)rah)\s+\g{p1} - A subpattern that is referenced by name may appear in the pattern + A subpattern that is referenced by name may appear in the pattern before or after the reference. - There may be more than one back reference to the same subpattern. If a - subpattern has not actually been used in a particular match, any back + There may be more than one back reference to the same subpattern. If a + subpattern has not actually been used in a particular match, any back references to it always fail by default. For example, the pattern (a|(bc))\2 - always fails if it starts to match "a" rather than "bc". However, if + always fails if it starts to match "a" rather than "bc". However, if the PCRE_JAVASCRIPT_COMPAT option is set at compile time, a back refer- ence to an unset value matches an empty string. - Because there may be many capturing parentheses in a pattern, all dig- - its following a backslash are taken as part of a potential back refer- - ence number. If the pattern continues with a digit character, some - delimiter must be used to terminate the back reference. If the - PCRE_EXTENDED option is set, this can be white space. Otherwise, the + Because there may be many capturing parentheses in a pattern, all dig- + its following a backslash are taken as part of a potential back refer- + ence number. If the pattern continues with a digit character, some + delimiter must be used to terminate the back reference. If the + PCRE_EXTENDED option is set, this can be white space. Otherwise, the \g{ syntax or an empty comment (see "Comments" below) can be used. Recursive back references - A back reference that occurs inside the parentheses to which it refers - fails when the subpattern is first used, so, for example, (a\1) never - matches. However, such references can be useful inside repeated sub- + A back reference that occurs inside the parentheses to which it refers + fails when the subpattern is first used, so, for example, (a\1) never + matches. However, such references can be useful inside repeated sub- patterns. For example, the pattern (a|b\1)+ matches any number of "a"s and also "aba", "ababbaa" etc. At each iter- - ation of the subpattern, the back reference matches the character - string corresponding to the previous iteration. In order for this to - work, the pattern must be such that the first iteration does not need - to match the back reference. This can be done using alternation, as in + ation of the subpattern, the back reference matches the character + string corresponding to the previous iteration. In order for this to + work, the pattern must be such that the first iteration does not need + to match the back reference. This can be done using alternation, as in the example above, or by a quantifier with a minimum of zero. - Back references of this type cause the group that they reference to be - treated as an atomic group. Once the whole group has been matched, a - subsequent matching failure cannot cause backtracking into the middle + Back references of this type cause the group that they reference to be + treated as an atomic group. Once the whole group has been matched, a + subsequent matching failure cannot cause backtracking into the middle of the group. ASSERTIONS - An assertion is a test on the characters following or preceding the - current matching point that does not actually consume any characters. - The simple assertions coded as \b, \B, \A, \G, \Z, \z, ^ and $ are + An assertion is a test on the characters following or preceding the + current matching point that does not actually consume any characters. + The simple assertions coded as \b, \B, \A, \G, \Z, \z, ^ and $ are described above. - More complicated assertions are coded as subpatterns. There are two - kinds: those that look ahead of the current position in the subject - string, and those that look behind it. An assertion subpattern is - matched in the normal way, except that it does not cause the current + More complicated assertions are coded as subpatterns. There are two + kinds: those that look ahead of the current position in the subject + string, and those that look behind it. An assertion subpattern is + matched in the normal way, except that it does not cause the current matching position to be changed. - Assertion subpatterns are not capturing subpatterns. If such an asser- - tion contains capturing subpatterns within it, these are counted for - the purposes of numbering the capturing subpatterns in the whole pat- - tern. However, substring capturing is carried out only for positive + Assertion subpatterns are not capturing subpatterns. If such an asser- + tion contains capturing subpatterns within it, these are counted for + the purposes of numbering the capturing subpatterns in the whole pat- + tern. However, substring capturing is carried out only for positive assertions. (Perl sometimes, but not always, does do capturing in nega- tive assertions.) - For compatibility with Perl, assertion subpatterns may be repeated; - though it makes no sense to assert the same thing several times, the - side effect of capturing parentheses may occasionally be useful. In + WARNING: If a positive assertion containing one or more capturing sub- + patterns succeeds, but failure to match later in the pattern causes + backtracking over this assertion, the captures within the assertion are + reset only if no higher numbered captures are already set. This is, + unfortunately, a fundamental limitation of the current implementation, + and as PCRE1 is now in maintenance-only status, it is unlikely ever to + change. + + For compatibility with Perl, assertion subpatterns may be repeated; + though it makes no sense to assert the same thing several times, the + side effect of capturing parentheses may occasionally be useful. In practice, there only three cases: - (1) If the quantifier is {0}, the assertion is never obeyed during - matching. However, it may contain internal capturing parenthesized + (1) If the quantifier is {0}, the assertion is never obeyed during + matching. However, it may contain internal capturing parenthesized groups that are called from elsewhere via the subroutine mechanism. - (2) If quantifier is {0,n} where n is greater than zero, it is treated - as if it were {0,1}. At run time, the rest of the pattern match is + (2) If quantifier is {0,n} where n is greater than zero, it is treated + as if it were {0,1}. At run time, the rest of the pattern match is tried with and without the assertion, the order depending on the greed- iness of the quantifier. - (3) If the minimum repetition is greater than zero, the quantifier is - ignored. The assertion is obeyed just once when encountered during + (3) If the minimum repetition is greater than zero, the quantifier is + ignored. The assertion is obeyed just once when encountered during matching. Lookahead assertions @@ -6646,38 +6651,38 @@ ASSERTIONS \w+(?=;) - matches a word followed by a semicolon, but does not include the semi- + matches a word followed by a semicolon, but does not include the semi- colon in the match, and foo(?!bar) - matches any occurrence of "foo" that is not followed by "bar". Note + matches any occurrence of "foo" that is not followed by "bar". Note that the apparently similar pattern (?!foo)bar - does not find an occurrence of "bar" that is preceded by something - other than "foo"; it finds any occurrence of "bar" whatsoever, because + does not find an occurrence of "bar" that is preceded by something + other than "foo"; it finds any occurrence of "bar" whatsoever, because the assertion (?!foo) is always true when the next three characters are "bar". A lookbehind assertion is needed to achieve the other effect. If you want to force a matching failure at some point in a pattern, the - most convenient way to do it is with (?!) because an empty string - always matches, so an assertion that requires there not to be an empty + most convenient way to do it is with (?!) because an empty string + always matches, so an assertion that requires there not to be an empty string must always fail. The backtracking control verb (*FAIL) or (*F) is a synonym for (?!). Lookbehind assertions - Lookbehind assertions start with (?<= for positive assertions and (?<! + Lookbehind assertions start with (?<= for positive assertions and (?<! for negative assertions. For example, (?<!foo)bar - does find an occurrence of "bar" that is not preceded by "foo". The - contents of a lookbehind assertion are restricted such that all the + does find an occurrence of "bar" that is not preceded by "foo". The + contents of a lookbehind assertion are restricted such that all the strings it matches must have a fixed length. However, if there are sev- - eral top-level alternatives, they do not all have to have the same + eral top-level alternatives, they do not all have to have the same fixed length. Thus (?<=bullock|donkey) @@ -6686,62 +6691,62 @@ ASSERTIONS (?<!dogs?|cats?) - causes an error at compile time. Branches that match different length - strings are permitted only at the top level of a lookbehind assertion. + causes an error at compile time. Branches that match different length + strings are permitted only at the top level of a lookbehind assertion. This is an extension compared with Perl, which requires all branches to match the same length of string. An assertion such as (?<=ab(c|de)) - is not permitted, because its single top-level branch can match two + is not permitted, because its single top-level branch can match two different lengths, but it is acceptable to PCRE if rewritten to use two top-level branches: (?<=abc|abde) - In some cases, the escape sequence \K (see above) can be used instead + In some cases, the escape sequence \K (see above) can be used instead of a lookbehind assertion to get round the fixed-length restriction. - The implementation of lookbehind assertions is, for each alternative, - to temporarily move the current position back by the fixed length and + The implementation of lookbehind assertions is, for each alternative, + to temporarily move the current position back by the fixed length and then try to match. If there are insufficient characters before the cur- rent position, the assertion fails. - In a UTF mode, PCRE does not allow the \C escape (which matches a sin- - gle data unit even in a UTF mode) to appear in lookbehind assertions, - because it makes it impossible to calculate the length of the lookbe- - hind. The \X and \R escapes, which can match different numbers of data + In a UTF mode, PCRE does not allow the \C escape (which matches a sin- + gle data unit even in a UTF mode) to appear in lookbehind assertions, + because it makes it impossible to calculate the length of the lookbe- + hind. The \X and \R escapes, which can match different numbers of data units, are also not permitted. - "Subroutine" calls (see below) such as (?2) or (?&X) are permitted in - lookbehinds, as long as the subpattern matches a fixed-length string. + "Subroutine" calls (see below) such as (?2) or (?&X) are permitted in + lookbehinds, as long as the subpattern matches a fixed-length string. Recursion, however, is not supported. - Possessive quantifiers can be used in conjunction with lookbehind + Possessive quantifiers can be used in conjunction with lookbehind assertions to specify efficient matching of fixed-length strings at the end of subject strings. Consider a simple pattern such as abcd$ - when applied to a long string that does not match. Because matching + when applied to a long string that does not match. Because matching proceeds from left to right, PCRE will look for each "a" in the subject - and then see if what follows matches the rest of the pattern. If the + and then see if what follows matches the rest of the pattern. If the pattern is specified as ^.*abcd$ - the initial .* matches the entire string at first, but when this fails + the initial .* matches the entire string at first, but when this fails (because there is no following "a"), it backtracks to match all but the - last character, then all but the last two characters, and so on. Once - again the search for "a" covers the entire string, from right to left, + last character, then all but the last two characters, and so on. Once + again the search for "a" covers the entire string, from right to left, so we are no better off. However, if the pattern is written as ^.*+(?<=abcd) - there can be no backtracking for the .*+ item; it can match only the - entire string. The subsequent lookbehind assertion does a single test - on the last four characters. If it fails, the match fails immediately. - For long strings, this approach makes a significant difference to the + there can be no backtracking for the .*+ item; it can match only the + entire string. The subsequent lookbehind assertion does a single test + on the last four characters. If it fails, the match fails immediately. + For long strings, this approach makes a significant difference to the processing time. Using multiple assertions @@ -6750,18 +6755,18 @@ ASSERTIONS (?<=\d{3})(?<!999)foo - matches "foo" preceded by three digits that are not "999". Notice that - each of the assertions is applied independently at the same point in - the subject string. First there is a check that the previous three - characters are all digits, and then there is a check that the same + matches "foo" preceded by three digits that are not "999". Notice that + each of the assertions is applied independently at the same point in + the subject string. First there is a check that the previous three + characters are all digits, and then there is a check that the same three characters are not "999". This pattern does not match "foo" pre- - ceded by six characters, the first of which are digits and the last - three of which are not "999". For example, it doesn't match "123abc- + ceded by six characters, the first of which are digits and the last + three of which are not "999". For example, it doesn't match "123abc- foo". A pattern to do that is (?<=\d{3}...)(?<!999)foo - This time the first assertion looks at the preceding six characters, + This time the first assertion looks at the preceding six characters, checking that the first three are digits, and then the second assertion checks that the preceding three characters are not "999". @@ -6769,29 +6774,29 @@ ASSERTIONS (?<=(?<!foo)bar)baz - matches an occurrence of "baz" that is preceded by "bar" which in turn + matches an occurrence of "baz" that is preceded by "bar" which in turn is not preceded by "foo", while (?<=\d{3}(?!999)...)foo - is another pattern that matches "foo" preceded by three digits and any + is another pattern that matches "foo" preceded by three digits and any three characters that are not "999". CONDITIONAL SUBPATTERNS - It is possible to cause the matching process to obey a subpattern con- - ditionally or to choose between two alternative subpatterns, depending - on the result of an assertion, or whether a specific capturing subpat- - tern has already been matched. The two possible forms of conditional + It is possible to cause the matching process to obey a subpattern con- + ditionally or to choose between two alternative subpatterns, depending + on the result of an assertion, or whether a specific capturing subpat- + tern has already been matched. The two possible forms of conditional subpattern are: (?(condition)yes-pattern) (?(condition)yes-pattern|no-pattern) - If the condition is satisfied, the yes-pattern is used; otherwise the - no-pattern (if present) is used. If there are more than two alterna- - tives in the subpattern, a compile-time error occurs. Each of the two + If the condition is satisfied, the yes-pattern is used; otherwise the + no-pattern (if present) is used. If there are more than two alterna- + tives in the subpattern, a compile-time error occurs. Each of the two alternatives may itself contain nested subpatterns of any form, includ- ing conditional subpatterns; the restriction to two alternatives applies only at the level of the condition. This pattern fragment is an @@ -6800,68 +6805,68 @@ CONDITIONAL SUBPATTERNS (?(1) (A|B|C) | (D | (?(2)E|F) | E) ) - There are four kinds of condition: references to subpatterns, refer- + There are four kinds of condition: references to subpatterns, refer- ences to recursion, a pseudo-condition called DEFINE, and assertions. Checking for a used subpattern by number - If the text between the parentheses consists of a sequence of digits, + If the text between the parentheses consists of a sequence of digits, the condition is true if a capturing subpattern of that number has pre- - viously matched. If there is more than one capturing subpattern with - the same number (see the earlier section about duplicate subpattern - numbers), the condition is true if any of them have matched. An alter- - native notation is to precede the digits with a plus or minus sign. In - this case, the subpattern number is relative rather than absolute. The - most recently opened parentheses can be referenced by (?(-1), the next - most recent by (?(-2), and so on. Inside loops it can also make sense + viously matched. If there is more than one capturing subpattern with + the same number (see the earlier section about duplicate subpattern + numbers), the condition is true if any of them have matched. An alter- + native notation is to precede the digits with a plus or minus sign. In + this case, the subpattern number is relative rather than absolute. The + most recently opened parentheses can be referenced by (?(-1), the next + most recent by (?(-2), and so on. Inside loops it can also make sense to refer to subsequent groups. The next parentheses to be opened can be - referenced as (?(+1), and so on. (The value zero in any of these forms + referenced as (?(+1), and so on. (The value zero in any of these forms is not used; it provokes a compile-time error.) - Consider the following pattern, which contains non-significant white + Consider the following pattern, which contains non-significant white space to make it more readable (assume the PCRE_EXTENDED option) and to divide it into three parts for ease of discussion: ( \( )? [^()]+ (?(1) \) ) - The first part matches an optional opening parenthesis, and if that + The first part matches an optional opening parenthesis, and if that character is present, sets it as the first captured substring. The sec- - ond part matches one or more characters that are not parentheses. The - third part is a conditional subpattern that tests whether or not the - first set of parentheses matched. If they did, that is, if subject - started with an opening parenthesis, the condition is true, and so the - yes-pattern is executed and a closing parenthesis is required. Other- - wise, since no-pattern is not present, the subpattern matches nothing. - In other words, this pattern matches a sequence of non-parentheses, + ond part matches one or more characters that are not parentheses. The + third part is a conditional subpattern that tests whether or not the + first set of parentheses matched. If they did, that is, if subject + started with an opening parenthesis, the condition is true, and so the + yes-pattern is executed and a closing parenthesis is required. Other- + wise, since no-pattern is not present, the subpattern matches nothing. + In other words, this pattern matches a sequence of non-parentheses, optionally enclosed in parentheses. - If you were embedding this pattern in a larger one, you could use a + If you were embedding this pattern in a larger one, you could use a relative reference: ...other stuff... ( \( )? [^()]+ (?(-1) \) ) ... - This makes the fragment independent of the parentheses in the larger + This makes the fragment independent of the parentheses in the larger pattern. Checking for a used subpattern by name - Perl uses the syntax (?(<name>)...) or (?('name')...) to test for a - used subpattern by name. For compatibility with earlier versions of - PCRE, which had this facility before Perl, the syntax (?(name)...) is + Perl uses the syntax (?(<name>)...) or (?('name')...) to test for a + used subpattern by name. For compatibility with earlier versions of + PCRE, which had this facility before Perl, the syntax (?(name)...) is also recognized. Rewriting the above example to use a named subpattern gives this: (?<OPEN> \( )? [^()]+ (?(<OPEN>) \) ) - If the name used in a condition of this kind is a duplicate, the test - is applied to all subpatterns of the same name, and is true if any one + If the name used in a condition of this kind is a duplicate, the test + is applied to all subpatterns of the same name, and is true if any one of them has matched. Checking for pattern recursion If the condition is the string (R), and there is no subpattern with the - name R, the condition is true if a recursive call to the whole pattern + name R, the condition is true if a recursive call to the whole pattern or any subpattern has been made. If digits or a name preceded by amper- sand follow the letter R, for example: @@ -6869,51 +6874,51 @@ CONDITIONAL SUBPATTERNS the condition is true if the most recent recursion is into a subpattern whose number or name is given. This condition does not check the entire - recursion stack. If the name used in a condition of this kind is a + recursion stack. If the name used in a condition of this kind is a duplicate, the test is applied to all subpatterns of the same name, and is true if any one of them is the most recent recursion. - At "top level", all these recursion test conditions are false. The + At "top level", all these recursion test conditions are false. The syntax for recursive patterns is described below. Defining subpatterns for use by reference only - If the condition is the string (DEFINE), and there is no subpattern - with the name DEFINE, the condition is always false. In this case, - there may be only one alternative in the subpattern. It is always - skipped if control reaches this point in the pattern; the idea of - DEFINE is that it can be used to define subroutines that can be refer- - enced from elsewhere. (The use of subroutines is described below.) For - example, a pattern to match an IPv4 address such as "192.168.23.245" + If the condition is the string (DEFINE), and there is no subpattern + with the name DEFINE, the condition is always false. In this case, + there may be only one alternative in the subpattern. It is always + skipped if control reaches this point in the pattern; the idea of + DEFINE is that it can be used to define subroutines that can be refer- + enced from elsewhere. (The use of subroutines is described below.) For + example, a pattern to match an IPv4 address such as "192.168.23.245" could be written like this (ignore white space and line breaks): (?(DEFINE) (?<byte> 2[0-4]\d | 25[0-5] | 1\d\d | [1-9]?\d) ) \b (?&byte) (\.(?&byte)){3} \b - The first part of the pattern is a DEFINE group inside which a another - group named "byte" is defined. This matches an individual component of - an IPv4 address (a number less than 256). When matching takes place, - this part of the pattern is skipped because DEFINE acts like a false - condition. The rest of the pattern uses references to the named group - to match the four dot-separated components of an IPv4 address, insist- + The first part of the pattern is a DEFINE group inside which a another + group named "byte" is defined. This matches an individual component of + an IPv4 address (a number less than 256). When matching takes place, + this part of the pattern is skipped because DEFINE acts like a false + condition. The rest of the pattern uses references to the named group + to match the four dot-separated components of an IPv4 address, insist- ing on a word boundary at each end. Assertion conditions - If the condition is not in any of the above formats, it must be an - assertion. This may be a positive or negative lookahead or lookbehind - assertion. Consider this pattern, again containing non-significant + If the condition is not in any of the above formats, it must be an + assertion. This may be a positive or negative lookahead or lookbehind + assertion. Consider this pattern, again containing non-significant white space, and with the two alternatives on the second line: (?(?=[^a-z]*[a-z]) \d{2}-[a-z]{3}-\d{2} | \d{2}-\d{2}-\d{2} ) - The condition is a positive lookahead assertion that matches an - optional sequence of non-letters followed by a letter. In other words, - it tests for the presence of at least one letter in the subject. If a - letter is found, the subject is matched against the first alternative; - otherwise it is matched against the second. This pattern matches - strings in one of the two forms dd-aaa-dd or dd-dd-dd, where aaa are + The condition is a positive lookahead assertion that matches an + optional sequence of non-letters followed by a letter. In other words, + it tests for the presence of at least one letter in the subject. If a + letter is found, the subject is matched against the first alternative; + otherwise it is matched against the second. This pattern matches + strings in one of the two forms dd-aaa-dd or dd-dd-dd, where aaa are letters and dd are digits. @@ -6922,41 +6927,41 @@ COMMENTS There are two ways of including comments in patterns that are processed by PCRE. In both cases, the start of the comment must not be in a char- acter class, nor in the middle of any other sequence of related charac- - ters such as (?: or a subpattern name or number. The characters that + ters such as (?: or a subpattern name or number. The characters that make up a comment play no part in the pattern matching. - The sequence (?# marks the start of a comment that continues up to the - next closing parenthesis. Nested parentheses are not permitted. If the + The sequence (?# marks the start of a comment that continues up to the + next closing parenthesis. Nested parentheses are not permitted. If the PCRE_EXTENDED option is set, an unescaped # character also introduces a - comment, which in this case continues to immediately after the next - newline character or character sequence in the pattern. Which charac- + comment, which in this case continues to immediately after the next + newline character or character sequence in the pattern. Which charac- ters are interpreted as newlines is controlled by the options passed to - a compiling function or by a special sequence at the start of the pat- + a compiling function or by a special sequence at the start of the pat- tern, as described in the section entitled "Newline conventions" above. Note that the end of this type of comment is a literal newline sequence - in the pattern; escape sequences that happen to represent a newline do - not count. For example, consider this pattern when PCRE_EXTENDED is + in the pattern; escape sequences that happen to represent a newline do + not count. For example, consider this pattern when PCRE_EXTENDED is set, and the default newline convention is in force: abc #comment \n still comment - On encountering the # character, pcre_compile() skips along, looking - for a newline in the pattern. The sequence \n is still literal at this - stage, so it does not terminate the comment. Only an actual character + On encountering the # character, pcre_compile() skips along, looking + for a newline in the pattern. The sequence \n is still literal at this + stage, so it does not terminate the comment. Only an actual character with the code value 0x0a (the default newline) does so. RECURSIVE PATTERNS - Consider the problem of matching a string in parentheses, allowing for - unlimited nested parentheses. Without the use of recursion, the best - that can be done is to use a pattern that matches up to some fixed - depth of nesting. It is not possible to handle an arbitrary nesting + Consider the problem of matching a string in parentheses, allowing for + unlimited nested parentheses. Without the use of recursion, the best + that can be done is to use a pattern that matches up to some fixed + depth of nesting. It is not possible to handle an arbitrary nesting depth. For some time, Perl has provided a facility that allows regular expres- - sions to recurse (amongst other things). It does this by interpolating - Perl code in the expression at run time, and the code can refer to the + sions to recurse (amongst other things). It does this by interpolating + Perl code in the expression at run time, and the code can refer to the expression itself. A Perl pattern using code interpolation to solve the parentheses problem can be created like this: @@ -6966,201 +6971,201 @@ RECURSIVE PATTERNS refers recursively to the pattern in which it appears. Obviously, PCRE cannot support the interpolation of Perl code. Instead, - it supports special syntax for recursion of the entire pattern, and - also for individual subpattern recursion. After its introduction in - PCRE and Python, this kind of recursion was subsequently introduced + it supports special syntax for recursion of the entire pattern, and + also for individual subpattern recursion. After its introduction in + PCRE and Python, this kind of recursion was subsequently introduced into Perl at release 5.10. - A special item that consists of (? followed by a number greater than - zero and a closing parenthesis is a recursive subroutine call of the - subpattern of the given number, provided that it occurs inside that - subpattern. (If not, it is a non-recursive subroutine call, which is - described in the next section.) The special item (?R) or (?0) is a + A special item that consists of (? followed by a number greater than + zero and a closing parenthesis is a recursive subroutine call of the + subpattern of the given number, provided that it occurs inside that + subpattern. (If not, it is a non-recursive subroutine call, which is + described in the next section.) The special item (?R) or (?0) is a recursive call of the entire regular expression. - This PCRE pattern solves the nested parentheses problem (assume the + This PCRE pattern solves the nested parentheses problem (assume the PCRE_EXTENDED option is set so that white space is ignored): \( ( [^()]++ | (?R) )* \) - First it matches an opening parenthesis. Then it matches any number of - substrings which can either be a sequence of non-parentheses, or a - recursive match of the pattern itself (that is, a correctly parenthe- + First it matches an opening parenthesis. Then it matches any number of + substrings which can either be a sequence of non-parentheses, or a + recursive match of the pattern itself (that is, a correctly parenthe- sized substring). Finally there is a closing parenthesis. Note the use of a possessive quantifier to avoid backtracking into sequences of non- parentheses. - If this were part of a larger pattern, you would not want to recurse + If this were part of a larger pattern, you would not want to recurse the entire pattern, so instead you could use this: ( \( ( [^()]++ | (?1) )* \) ) - We have put the pattern into parentheses, and caused the recursion to + We have put the pattern into parentheses, and caused the recursion to refer to them instead of the whole pattern. - In a larger pattern, keeping track of parenthesis numbers can be - tricky. This is made easier by the use of relative references. Instead + In a larger pattern, keeping track of parenthesis numbers can be + tricky. This is made easier by the use of relative references. Instead of (?1) in the pattern above you can write (?-2) to refer to the second - most recently opened parentheses preceding the recursion. In other - words, a negative number counts capturing parentheses leftwards from + most recently opened parentheses preceding the recursion. In other + words, a negative number counts capturing parentheses leftwards from the point at which it is encountered. - It is also possible to refer to subsequently opened parentheses, by - writing references such as (?+2). However, these cannot be recursive - because the reference is not inside the parentheses that are refer- - enced. They are always non-recursive subroutine calls, as described in + It is also possible to refer to subsequently opened parentheses, by + writing references such as (?+2). However, these cannot be recursive + because the reference is not inside the parentheses that are refer- + enced. They are always non-recursive subroutine calls, as described in the next section. - An alternative approach is to use named parentheses instead. The Perl - syntax for this is (?&name); PCRE's earlier syntax (?P>name) is also + An alternative approach is to use named parentheses instead. The Perl + syntax for this is (?&name); PCRE's earlier syntax (?P>name) is also supported. We could rewrite the above example as follows: (?<pn> \( ( [^()]++ | (?&pn) )* \) ) - If there is more than one subpattern with the same name, the earliest + If there is more than one subpattern with the same name, the earliest one is used. - This particular example pattern that we have been looking at contains + This particular example pattern that we have been looking at contains nested unlimited repeats, and so the use of a possessive quantifier for matching strings of non-parentheses is important when applying the pat- - tern to strings that do not match. For example, when this pattern is + tern to strings that do not match. For example, when this pattern is applied to (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa() - it yields "no match" quickly. However, if a possessive quantifier is - not used, the match runs for a very long time indeed because there are - so many different ways the + and * repeats can carve up the subject, + it yields "no match" quickly. However, if a possessive quantifier is + not used, the match runs for a very long time indeed because there are + so many different ways the + and * repeats can carve up the subject, and all have to be tested before failure can be reported. - At the end of a match, the values of capturing parentheses are those - from the outermost level. If you want to obtain intermediate values, a - callout function can be used (see below and the pcrecallout documenta- + At the end of a match, the values of capturing parentheses are those + from the outermost level. If you want to obtain intermediate values, a + callout function can be used (see below and the pcrecallout documenta- tion). If the pattern above is matched against (ab(cd)ef) - the value for the inner capturing parentheses (numbered 2) is "ef", - which is the last value taken on at the top level. If a capturing sub- - pattern is not matched at the top level, its final captured value is - unset, even if it was (temporarily) set at a deeper level during the + the value for the inner capturing parentheses (numbered 2) is "ef", + which is the last value taken on at the top level. If a capturing sub- + pattern is not matched at the top level, its final captured value is + unset, even if it was (temporarily) set at a deeper level during the matching process. - If there are more than 15 capturing parentheses in a pattern, PCRE has - to obtain extra memory to store data during a recursion, which it does + If there are more than 15 capturing parentheses in a pattern, PCRE has + to obtain extra memory to store data during a recursion, which it does by using pcre_malloc, freeing it via pcre_free afterwards. If no memory can be obtained, the match fails with the PCRE_ERROR_NOMEMORY error. - Do not confuse the (?R) item with the condition (R), which tests for - recursion. Consider this pattern, which matches text in angle brack- - ets, allowing for arbitrary nesting. Only digits are allowed in nested - brackets (that is, when recursing), whereas any characters are permit- + Do not confuse the (?R) item with the condition (R), which tests for + recursion. Consider this pattern, which matches text in angle brack- + ets, allowing for arbitrary nesting. Only digits are allowed in nested + brackets (that is, when recursing), whereas any characters are permit- ted at the outer level. < (?: (?(R) \d++ | [^<>]*+) | (?R)) * > - In this pattern, (?(R) is the start of a conditional subpattern, with - two different alternatives for the recursive and non-recursive cases. + In this pattern, (?(R) is the start of a conditional subpattern, with + two different alternatives for the recursive and non-recursive cases. The (?R) item is the actual recursive call. Differences in recursion processing between PCRE and Perl - Recursion processing in PCRE differs from Perl in two important ways. - In PCRE (like Python, but unlike Perl), a recursive subpattern call is + Recursion processing in PCRE differs from Perl in two important ways. + In PCRE (like Python, but unlike Perl), a recursive subpattern call is always treated as an atomic group. That is, once it has matched some of the subject string, it is never re-entered, even if it contains untried - alternatives and there is a subsequent matching failure. This can be - illustrated by the following pattern, which purports to match a palin- - dromic string that contains an odd number of characters (for example, + alternatives and there is a subsequent matching failure. This can be + illustrated by the following pattern, which purports to match a palin- + dromic string that contains an odd number of characters (for example, "a", "aba", "abcba", "abcdcba"): ^(.|(.)(?1)\2)$ The idea is that it either matches a single character, or two identical - characters surrounding a sub-palindrome. In Perl, this pattern works; - in PCRE it does not if the pattern is longer than three characters. + characters surrounding a sub-palindrome. In Perl, this pattern works; + in PCRE it does not if the pattern is longer than three characters. Consider the subject string "abcba": - At the top level, the first character is matched, but as it is not at + At the top level, the first character is matched, but as it is not at the end of the string, the first alternative fails; the second alterna- tive is taken and the recursion kicks in. The recursive call to subpat- - tern 1 successfully matches the next character ("b"). (Note that the + tern 1 successfully matches the next character ("b"). (Note that the beginning and end of line tests are not part of the recursion). - Back at the top level, the next character ("c") is compared with what - subpattern 2 matched, which was "a". This fails. Because the recursion - is treated as an atomic group, there are now no backtracking points, - and so the entire match fails. (Perl is able, at this point, to re- - enter the recursion and try the second alternative.) However, if the + Back at the top level, the next character ("c") is compared with what + subpattern 2 matched, which was "a". This fails. Because the recursion + is treated as an atomic group, there are now no backtracking points, + and so the entire match fails. (Perl is able, at this point, to re- + enter the recursion and try the second alternative.) However, if the pattern is written with the alternatives in the other order, things are different: ^((.)(?1)\2|.)$ - This time, the recursing alternative is tried first, and continues to - recurse until it runs out of characters, at which point the recursion - fails. But this time we do have another alternative to try at the - higher level. That is the big difference: in the previous case the + This time, the recursing alternative is tried first, and continues to + recurse until it runs out of characters, at which point the recursion + fails. But this time we do have another alternative to try at the + higher level. That is the big difference: in the previous case the remaining alternative is at a deeper recursion level, which PCRE cannot use. - To change the pattern so that it matches all palindromic strings, not - just those with an odd number of characters, it is tempting to change + To change the pattern so that it matches all palindromic strings, not + just those with an odd number of characters, it is tempting to change the pattern to this: ^((.)(?1)\2|.?)$ - Again, this works in Perl, but not in PCRE, and for the same reason. - When a deeper recursion has matched a single character, it cannot be - entered again in order to match an empty string. The solution is to - separate the two cases, and write out the odd and even cases as alter- + Again, this works in Perl, but not in PCRE, and for the same reason. + When a deeper recursion has matched a single character, it cannot be + entered again in order to match an empty string. The solution is to + separate the two cases, and write out the odd and even cases as alter- natives at the higher level: ^(?:((.)(?1)\2|)|((.)(?3)\4|.)) - If you want to match typical palindromic phrases, the pattern has to + If you want to match typical palindromic phrases, the pattern has to ignore all non-word characters, which can be done like this: ^\W*+(?:((.)\W*+(?1)\W*+\2|)|((.)\W*+(?3)\W*+\4|\W*+.\W*+))\W*+$ If run with the PCRE_CASELESS option, this pattern matches phrases such as "A man, a plan, a canal: Panama!" and it works well in both PCRE and - Perl. Note the use of the possessive quantifier *+ to avoid backtrack- - ing into sequences of non-word characters. Without this, PCRE takes a - great deal longer (ten times or more) to match typical phrases, and + Perl. Note the use of the possessive quantifier *+ to avoid backtrack- + ing into sequences of non-word characters. Without this, PCRE takes a + great deal longer (ten times or more) to match typical phrases, and Perl takes so long that you think it has gone into a loop. - WARNING: The palindrome-matching patterns above work only if the sub- - ject string does not start with a palindrome that is shorter than the - entire string. For example, although "abcba" is correctly matched, if - the subject is "ababa", PCRE finds the palindrome "aba" at the start, - then fails at top level because the end of the string does not follow. - Once again, it cannot jump back into the recursion to try other alter- + WARNING: The palindrome-matching patterns above work only if the sub- + ject string does not start with a palindrome that is shorter than the + entire string. For example, although "abcba" is correctly matched, if + the subject is "ababa", PCRE finds the palindrome "aba" at the start, + then fails at top level because the end of the string does not follow. + Once again, it cannot jump back into the recursion to try other alter- natives, so the entire match fails. - The second way in which PCRE and Perl differ in their recursion pro- - cessing is in the handling of captured values. In Perl, when a subpat- - tern is called recursively or as a subpattern (see the next section), - it has no access to any values that were captured outside the recur- - sion, whereas in PCRE these values can be referenced. Consider this + The second way in which PCRE and Perl differ in their recursion pro- + cessing is in the handling of captured values. In Perl, when a subpat- + tern is called recursively or as a subpattern (see the next section), + it has no access to any values that were captured outside the recur- + sion, whereas in PCRE these values can be referenced. Consider this pattern: ^(.)(\1|a(?2)) - In PCRE, this pattern matches "bab". The first capturing parentheses - match "b", then in the second group, when the back reference \1 fails - to match "b", the second alternative matches "a" and then recurses. In - the recursion, \1 does now match "b" and so the whole match succeeds. - In Perl, the pattern fails to match because inside the recursive call + In PCRE, this pattern matches "bab". The first capturing parentheses + match "b", then in the second group, when the back reference \1 fails + to match "b", the second alternative matches "a" and then recurses. In + the recursion, \1 does now match "b" and so the whole match succeeds. + In Perl, the pattern fails to match because inside the recursive call \1 cannot access the externally set value. SUBPATTERNS AS SUBROUTINES - If the syntax for a recursive subpattern call (either by number or by - name) is used outside the parentheses to which it refers, it operates - like a subroutine in a programming language. The called subpattern may - be defined before or after the reference. A numbered reference can be + If the syntax for a recursive subpattern call (either by number or by + name) is used outside the parentheses to which it refers, it operates + like a subroutine in a programming language. The called subpattern may + be defined before or after the reference. A numbered reference can be absolute or relative, as in these examples: (...(absolute)...)...(?2)... @@ -7171,79 +7176,79 @@ SUBPATTERNS AS SUBROUTINES (sens|respons)e and \1ibility - matches "sense and sensibility" and "response and responsibility", but + matches "sense and sensibility" and "response and responsibility", but not "sense and responsibility". If instead the pattern (sens|respons)e and (?1)ibility - is used, it does match "sense and responsibility" as well as the other - two strings. Another example is given in the discussion of DEFINE + is used, it does match "sense and responsibility" as well as the other + two strings. Another example is given in the discussion of DEFINE above. - All subroutine calls, whether recursive or not, are always treated as - atomic groups. That is, once a subroutine has matched some of the sub- + All subroutine calls, whether recursive or not, are always treated as + atomic groups. That is, once a subroutine has matched some of the sub- ject string, it is never re-entered, even if it contains untried alter- - natives and there is a subsequent matching failure. Any capturing - parentheses that are set during the subroutine call revert to their + natives and there is a subsequent matching failure. Any capturing + parentheses that are set during the subroutine call revert to their previous values afterwards. - Processing options such as case-independence are fixed when a subpat- - tern is defined, so if it is used as a subroutine, such options cannot + Processing options such as case-independence are fixed when a subpat- + tern is defined, so if it is used as a subroutine, such options cannot be changed for different calls. For example, consider this pattern: (abc)(?i:(?-1)) - It matches "abcabc". It does not match "abcABC" because the change of + It matches "abcabc". It does not match "abcABC" because the change of processing option does not affect the called subpattern. ONIGURUMA SUBROUTINE SYNTAX - For compatibility with Oniguruma, the non-Perl syntax \g followed by a + For compatibility with Oniguruma, the non-Perl syntax \g followed by a name or a number enclosed either in angle brackets or single quotes, is - an alternative syntax for referencing a subpattern as a subroutine, - possibly recursively. Here are two of the examples used above, rewrit- + an alternative syntax for referencing a subpattern as a subroutine, + possibly recursively. Here are two of the examples used above, rewrit- ten using this syntax: (?<pn> \( ( (?>[^()]+) | \g<pn> )* \) ) (sens|respons)e and \g'1'ibility - PCRE supports an extension to Oniguruma: if a number is preceded by a + PCRE supports an extension to Oniguruma: if a number is preceded by a plus or a minus sign it is taken as a relative reference. For example: (abc)(?i:\g<-1>) - Note that \g{...} (Perl syntax) and \g<...> (Oniguruma syntax) are not - synonymous. The former is a back reference; the latter is a subroutine + Note that \g{...} (Perl syntax) and \g<...> (Oniguruma syntax) are not + synonymous. The former is a back reference; the latter is a subroutine call. CALLOUTS Perl has a feature whereby using the sequence (?{...}) causes arbitrary - Perl code to be obeyed in the middle of matching a regular expression. + Perl code to be obeyed in the middle of matching a regular expression. This makes it possible, amongst other things, to extract different sub- strings that match the same pair of parentheses when there is a repeti- tion. PCRE provides a similar feature, but of course it cannot obey arbitrary Perl code. The feature is called "callout". The caller of PCRE provides - an external function by putting its entry point in the global variable - pcre_callout (8-bit library) or pcre[16|32]_callout (16-bit or 32-bit - library). By default, this variable contains NULL, which disables all + an external function by putting its entry point in the global variable + pcre_callout (8-bit library) or pcre[16|32]_callout (16-bit or 32-bit + library). By default, this variable contains NULL, which disables all calling out. - Within a regular expression, (?C) indicates the points at which the - external function is to be called. If you want to identify different - callout points, you can put a number less than 256 after the letter C. - The default value is zero. For example, this pattern has two callout + Within a regular expression, (?C) indicates the points at which the + external function is to be called. If you want to identify different + callout points, you can put a number less than 256 after the letter C. + The default value is zero. For example, this pattern has two callout points: (?C1)abc(?C2)def - If the PCRE_AUTO_CALLOUT flag is passed to a compiling function, call- - outs are automatically installed before each item in the pattern. They - are all numbered 255. If there is a conditional group in the pattern + If the PCRE_AUTO_CALLOUT flag is passed to a compiling function, call- + outs are automatically installed before each item in the pattern. They + are all numbered 255. If there is a conditional group in the pattern whose condition is an assertion, an additional callout is inserted just before the condition. An explicit callout may also be set at this posi- tion, as in this example: @@ -7253,120 +7258,120 @@ CALLOUTS Note that this applies only to assertion conditions, not to other types of condition. - During matching, when PCRE reaches a callout point, the external func- - tion is called. It is provided with the number of the callout, the - position in the pattern, and, optionally, one item of data originally - supplied by the caller of the matching function. The callout function + During matching, when PCRE reaches a callout point, the external func- + tion is called. It is provided with the number of the callout, the + position in the pattern, and, optionally, one item of data originally + supplied by the caller of the matching function. The callout function may cause matching to proceed, to backtrack, or to fail altogether. - By default, PCRE implements a number of optimizations at compile time - and matching time, and one side-effect is that sometimes callouts are - skipped. If you need all possible callouts to happen, you need to set - options that disable the relevant optimizations. More details, and a - complete description of the interface to the callout function, are + By default, PCRE implements a number of optimizations at compile time + and matching time, and one side-effect is that sometimes callouts are + skipped. If you need all possible callouts to happen, you need to set + options that disable the relevant optimizations. More details, and a + complete description of the interface to the callout function, are given in the pcrecallout documentation. BACKTRACKING CONTROL - Perl 5.10 introduced a number of "Special Backtracking Control Verbs", - which are still described in the Perl documentation as "experimental - and subject to change or removal in a future version of Perl". It goes - on to say: "Their usage in production code should be noted to avoid - problems during upgrades." The same remarks apply to the PCRE features + Perl 5.10 introduced a number of "Special Backtracking Control Verbs", + which are still described in the Perl documentation as "experimental + and subject to change or removal in a future version of Perl". It goes + on to say: "Their usage in production code should be noted to avoid + problems during upgrades." The same remarks apply to the PCRE features described in this section. - The new verbs make use of what was previously invalid syntax: an open- + The new verbs make use of what was previously invalid syntax: an open- ing parenthesis followed by an asterisk. They are generally of the form - (*VERB) or (*VERB:NAME). Some may take either form, possibly behaving - differently depending on whether or not a name is present. A name is + (*VERB) or (*VERB:NAME). Some may take either form, possibly behaving + differently depending on whether or not a name is present. A name is any sequence of characters that does not include a closing parenthesis. The maximum length of name is 255 in the 8-bit library and 65535 in the - 16-bit and 32-bit libraries. If the name is empty, that is, if the - closing parenthesis immediately follows the colon, the effect is as if - the colon were not there. Any number of these verbs may occur in a + 16-bit and 32-bit libraries. If the name is empty, that is, if the + closing parenthesis immediately follows the colon, the effect is as if + the colon were not there. Any number of these verbs may occur in a pattern. - Since these verbs are specifically related to backtracking, most of - them can be used only when the pattern is to be matched using one of - the traditional matching functions, because these use a backtracking - algorithm. With the exception of (*FAIL), which behaves like a failing - negative assertion, the backtracking control verbs cause an error if + Since these verbs are specifically related to backtracking, most of + them can be used only when the pattern is to be matched using one of + the traditional matching functions, because these use a backtracking + algorithm. With the exception of (*FAIL), which behaves like a failing + negative assertion, the backtracking control verbs cause an error if encountered by a DFA matching function. - The behaviour of these verbs in repeated groups, assertions, and in + The behaviour of these verbs in repeated groups, assertions, and in subpatterns called as subroutines (whether or not recursively) is docu- mented below. Optimizations that affect backtracking verbs - PCRE contains some optimizations that are used to speed up matching by + PCRE contains some optimizations that are used to speed up matching by running some checks at the start of each match attempt. For example, it - may know the minimum length of matching subject, or that a particular + may know the minimum length of matching subject, or that a particular character must be present. When one of these optimizations bypasses the - running of a match, any included backtracking verbs will not, of + running of a match, any included backtracking verbs will not, of course, be processed. You can suppress the start-of-match optimizations - by setting the PCRE_NO_START_OPTIMIZE option when calling pcre_com- + by setting the PCRE_NO_START_OPTIMIZE option when calling pcre_com- pile() or pcre_exec(), or by starting the pattern with (*NO_START_OPT). There is more discussion of this option in the section entitled "Option bits for pcre_exec()" in the pcreapi documentation. - Experiments with Perl suggest that it too has similar optimizations, + Experiments with Perl suggest that it too has similar optimizations, sometimes leading to anomalous results. Verbs that act immediately - The following verbs act as soon as they are encountered. They may not + The following verbs act as soon as they are encountered. They may not be followed by a name. (*ACCEPT) - This verb causes the match to end successfully, skipping the remainder - of the pattern. However, when it is inside a subpattern that is called - as a subroutine, only that subpattern is ended successfully. Matching + This verb causes the match to end successfully, skipping the remainder + of the pattern. However, when it is inside a subpattern that is called + as a subroutine, only that subpattern is ended successfully. Matching then continues at the outer level. If (*ACCEPT) in triggered in a posi- - tive assertion, the assertion succeeds; in a negative assertion, the + tive assertion, the assertion succeeds; in a negative assertion, the assertion fails. - If (*ACCEPT) is inside capturing parentheses, the data so far is cap- + If (*ACCEPT) is inside capturing parentheses, the data so far is cap- tured. For example: A((?:A|B(*ACCEPT)|C)D) - This matches "AB", "AAD", or "ACD"; when it matches "AB", "B" is cap- + This matches "AB", "AAD", or "ACD"; when it matches "AB", "B" is cap- tured by the outer parentheses. (*FAIL) or (*F) - This verb causes a matching failure, forcing backtracking to occur. It - is equivalent to (?!) but easier to read. The Perl documentation notes - that it is probably useful only when combined with (?{}) or (??{}). - Those are, of course, Perl features that are not present in PCRE. The - nearest equivalent is the callout feature, as for example in this pat- + This verb causes a matching failure, forcing backtracking to occur. It + is equivalent to (?!) but easier to read. The Perl documentation notes + that it is probably useful only when combined with (?{}) or (??{}). + Those are, of course, Perl features that are not present in PCRE. The + nearest equivalent is the callout feature, as for example in this pat- tern: a+(?C)(*FAIL) - A match with the string "aaaa" always fails, but the callout is taken + A match with the string "aaaa" always fails, but the callout is taken before each backtrack happens (in this example, 10 times). Recording which path was taken - There is one verb whose main purpose is to track how a match was - arrived at, though it also has a secondary use in conjunction with + There is one verb whose main purpose is to track how a match was + arrived at, though it also has a secondary use in conjunction with advancing the match starting point (see (*SKIP) below). (*MARK:NAME) or (*:NAME) - A name is always required with this verb. There may be as many - instances of (*MARK) as you like in a pattern, and their names do not + A name is always required with this verb. There may be as many + instances of (*MARK) as you like in a pattern, and their names do not have to be unique. - When a match succeeds, the name of the last-encountered (*MARK:NAME), - (*PRUNE:NAME), or (*THEN:NAME) on the matching path is passed back to - the caller as described in the section entitled "Extra data for - pcre_exec()" in the pcreapi documentation. Here is an example of - pcretest output, where the /K modifier requests the retrieval and out- + When a match succeeds, the name of the last-encountered (*MARK:NAME), + (*PRUNE:NAME), or (*THEN:NAME) on the matching path is passed back to + the caller as described in the section entitled "Extra data for + pcre_exec()" in the pcreapi documentation. Here is an example of + pcretest output, where the /K modifier requests the retrieval and out- putting of (*MARK) data: re> /X(*MARK:A)Y|X(*MARK:B)Z/K @@ -7378,73 +7383,73 @@ BACKTRACKING CONTROL MK: B The (*MARK) name is tagged with "MK:" in this output, and in this exam- - ple it indicates which of the two alternatives matched. This is a more - efficient way of obtaining this information than putting each alterna- + ple it indicates which of the two alternatives matched. This is a more + efficient way of obtaining this information than putting each alterna- tive in its own capturing parentheses. - If a verb with a name is encountered in a positive assertion that is - true, the name is recorded and passed back if it is the last-encoun- + If a verb with a name is encountered in a positive assertion that is + true, the name is recorded and passed back if it is the last-encoun- tered. This does not happen for negative assertions or failing positive assertions. - After a partial match or a failed match, the last encountered name in + After a partial match or a failed match, the last encountered name in the entire match process is returned. For example: re> /X(*MARK:A)Y|X(*MARK:B)Z/K data> XP No match, mark = B - Note that in this unanchored example the mark is retained from the + Note that in this unanchored example the mark is retained from the match attempt that started at the letter "X" in the subject. Subsequent match attempts starting at "P" and then with an empty string do not get as far as the (*MARK) item, but nevertheless do not reset it. - If you are interested in (*MARK) values after failed matches, you - should probably set the PCRE_NO_START_OPTIMIZE option (see above) to + If you are interested in (*MARK) values after failed matches, you + should probably set the PCRE_NO_START_OPTIMIZE option (see above) to ensure that the match is always attempted. Verbs that act after backtracking The following verbs do nothing when they are encountered. Matching con- - tinues with what follows, but if there is no subsequent match, causing - a backtrack to the verb, a failure is forced. That is, backtracking - cannot pass to the left of the verb. However, when one of these verbs + tinues with what follows, but if there is no subsequent match, causing + a backtrack to the verb, a failure is forced. That is, backtracking + cannot pass to the left of the verb. However, when one of these verbs appears inside an atomic group or an assertion that is true, its effect - is confined to that group, because once the group has been matched, - there is never any backtracking into it. In this situation, backtrack- - ing can "jump back" to the left of the entire atomic group or asser- - tion. (Remember also, as stated above, that this localization also + is confined to that group, because once the group has been matched, + there is never any backtracking into it. In this situation, backtrack- + ing can "jump back" to the left of the entire atomic group or asser- + tion. (Remember also, as stated above, that this localization also applies in subroutine calls.) - These verbs differ in exactly what kind of failure occurs when back- - tracking reaches them. The behaviour described below is what happens - when the verb is not in a subroutine or an assertion. Subsequent sec- + These verbs differ in exactly what kind of failure occurs when back- + tracking reaches them. The behaviour described below is what happens + when the verb is not in a subroutine or an assertion. Subsequent sec- tions cover these special cases. (*COMMIT) - This verb, which may not be followed by a name, causes the whole match + This verb, which may not be followed by a name, causes the whole match to fail outright if there is a later matching failure that causes back- - tracking to reach it. Even if the pattern is unanchored, no further + tracking to reach it. Even if the pattern is unanchored, no further attempts to find a match by advancing the starting point take place. If - (*COMMIT) is the only backtracking verb that is encountered, once it + (*COMMIT) is the only backtracking verb that is encountered, once it has been passed pcre_exec() is committed to finding a match at the cur- rent starting point, or not at all. For example: a+(*COMMIT)b - This matches "xxaab" but not "aacaab". It can be thought of as a kind + This matches "xxaab" but not "aacaab". It can be thought of as a kind of dynamic anchor, or "I've started, so I must finish." The name of the - most recently passed (*MARK) in the path is passed back when (*COMMIT) + most recently passed (*MARK) in the path is passed back when (*COMMIT) forces a match failure. - If there is more than one backtracking verb in a pattern, a different - one that follows (*COMMIT) may be triggered first, so merely passing + If there is more than one backtracking verb in a pattern, a different + one that follows (*COMMIT) may be triggered first, so merely passing (*COMMIT) during a match does not always guarantee that a match must be at this starting point. - Note that (*COMMIT) at the start of a pattern is not the same as an - anchor, unless PCRE's start-of-match optimizations are turned off, as + Note that (*COMMIT) at the start of a pattern is not the same as an + anchor, unless PCRE's start-of-match optimizations are turned off, as shown in this output from pcretest: re> /(*COMMIT)abc/ @@ -7455,207 +7460,207 @@ BACKTRACKING CONTROL For this pattern, PCRE knows that any match must start with "a", so the optimization skips along the subject to "a" before applying the pattern - to the first set of data. The match attempt then succeeds. In the sec- - ond set of data, the escape sequence \Y is interpreted by the pcretest - program. It causes the PCRE_NO_START_OPTIMIZE option to be set when + to the first set of data. The match attempt then succeeds. In the sec- + ond set of data, the escape sequence \Y is interpreted by the pcretest + program. It causes the PCRE_NO_START_OPTIMIZE option to be set when pcre_exec() is called. This disables the optimization that skips along to the first character. The pattern is now applied starting at "x", and - so the (*COMMIT) causes the match to fail without trying any other + so the (*COMMIT) causes the match to fail without trying any other starting points. (*PRUNE) or (*PRUNE:NAME) - This verb causes the match to fail at the current starting position in + This verb causes the match to fail at the current starting position in the subject if there is a later matching failure that causes backtrack- - ing to reach it. If the pattern is unanchored, the normal "bumpalong" - advance to the next starting character then happens. Backtracking can - occur as usual to the left of (*PRUNE), before it is reached, or when - matching to the right of (*PRUNE), but if there is no match to the - right, backtracking cannot cross (*PRUNE). In simple cases, the use of - (*PRUNE) is just an alternative to an atomic group or possessive quan- + ing to reach it. If the pattern is unanchored, the normal "bumpalong" + advance to the next starting character then happens. Backtracking can + occur as usual to the left of (*PRUNE), before it is reached, or when + matching to the right of (*PRUNE), but if there is no match to the + right, backtracking cannot cross (*PRUNE). In simple cases, the use of + (*PRUNE) is just an alternative to an atomic group or possessive quan- tifier, but there are some uses of (*PRUNE) that cannot be expressed in - any other way. In an anchored pattern (*PRUNE) has the same effect as + any other way. In an anchored pattern (*PRUNE) has the same effect as (*COMMIT). The behaviour of (*PRUNE:NAME) is the not the same as - (*MARK:NAME)(*PRUNE). It is like (*MARK:NAME) in that the name is - remembered for passing back to the caller. However, (*SKIP:NAME) + (*MARK:NAME)(*PRUNE). It is like (*MARK:NAME) in that the name is + remembered for passing back to the caller. However, (*SKIP:NAME) searches only for names set with (*MARK). (*SKIP) - This verb, when given without a name, is like (*PRUNE), except that if - the pattern is unanchored, the "bumpalong" advance is not to the next + This verb, when given without a name, is like (*PRUNE), except that if + the pattern is unanchored, the "bumpalong" advance is not to the next character, but to the position in the subject where (*SKIP) was encoun- - tered. (*SKIP) signifies that whatever text was matched leading up to + tered. (*SKIP) signifies that whatever text was matched leading up to it cannot be part of a successful match. Consider: a+(*SKIP)b - If the subject is "aaaac...", after the first match attempt fails - (starting at the first character in the string), the starting point + If the subject is "aaaac...", after the first match attempt fails + (starting at the first character in the string), the starting point skips on to start the next attempt at "c". Note that a possessive quan- - tifer does not have the same effect as this example; although it would - suppress backtracking during the first match attempt, the second - attempt would start at the second character instead of skipping on to + tifer does not have the same effect as this example; although it would + suppress backtracking during the first match attempt, the second + attempt would start at the second character instead of skipping on to "c". (*SKIP:NAME) When (*SKIP) has an associated name, its behaviour is modified. When it is triggered, the previous path through the pattern is searched for the - most recent (*MARK) that has the same name. If one is found, the + most recent (*MARK) that has the same name. If one is found, the "bumpalong" advance is to the subject position that corresponds to that (*MARK) instead of to where (*SKIP) was encountered. If no (*MARK) with a matching name is found, the (*SKIP) is ignored. - Note that (*SKIP:NAME) searches only for names set by (*MARK:NAME). It + Note that (*SKIP:NAME) searches only for names set by (*MARK:NAME). It ignores names that are set by (*PRUNE:NAME) or (*THEN:NAME). (*THEN) or (*THEN:NAME) - This verb causes a skip to the next innermost alternative when back- - tracking reaches it. That is, it cancels any further backtracking - within the current alternative. Its name comes from the observation + This verb causes a skip to the next innermost alternative when back- + tracking reaches it. That is, it cancels any further backtracking + within the current alternative. Its name comes from the observation that it can be used for a pattern-based if-then-else block: ( COND1 (*THEN) FOO | COND2 (*THEN) BAR | COND3 (*THEN) BAZ ) ... - If the COND1 pattern matches, FOO is tried (and possibly further items - after the end of the group if FOO succeeds); on failure, the matcher - skips to the second alternative and tries COND2, without backtracking - into COND1. If that succeeds and BAR fails, COND3 is tried. If subse- - quently BAZ fails, there are no more alternatives, so there is a back- - track to whatever came before the entire group. If (*THEN) is not + If the COND1 pattern matches, FOO is tried (and possibly further items + after the end of the group if FOO succeeds); on failure, the matcher + skips to the second alternative and tries COND2, without backtracking + into COND1. If that succeeds and BAR fails, COND3 is tried. If subse- + quently BAZ fails, there are no more alternatives, so there is a back- + track to whatever came before the entire group. If (*THEN) is not inside an alternation, it acts like (*PRUNE). - The behaviour of (*THEN:NAME) is the not the same as - (*MARK:NAME)(*THEN). It is like (*MARK:NAME) in that the name is - remembered for passing back to the caller. However, (*SKIP:NAME) + The behaviour of (*THEN:NAME) is the not the same as + (*MARK:NAME)(*THEN). It is like (*MARK:NAME) in that the name is + remembered for passing back to the caller. However, (*SKIP:NAME) searches only for names set with (*MARK). - A subpattern that does not contain a | character is just a part of the - enclosing alternative; it is not a nested alternation with only one - alternative. The effect of (*THEN) extends beyond such a subpattern to - the enclosing alternative. Consider this pattern, where A, B, etc. are - complex pattern fragments that do not contain any | characters at this + A subpattern that does not contain a | character is just a part of the + enclosing alternative; it is not a nested alternation with only one + alternative. The effect of (*THEN) extends beyond such a subpattern to + the enclosing alternative. Consider this pattern, where A, B, etc. are + complex pattern fragments that do not contain any | characters at this level: A (B(*THEN)C) | D - If A and B are matched, but there is a failure in C, matching does not + If A and B are matched, but there is a failure in C, matching does not backtrack into A; instead it moves to the next alternative, that is, D. - However, if the subpattern containing (*THEN) is given an alternative, + However, if the subpattern containing (*THEN) is given an alternative, it behaves differently: A (B(*THEN)C | (*FAIL)) | D - The effect of (*THEN) is now confined to the inner subpattern. After a + The effect of (*THEN) is now confined to the inner subpattern. After a failure in C, matching moves to (*FAIL), which causes the whole subpat- - tern to fail because there are no more alternatives to try. In this + tern to fail because there are no more alternatives to try. In this case, matching does now backtrack into A. - Note that a conditional subpattern is not considered as having two - alternatives, because only one is ever used. In other words, the | + Note that a conditional subpattern is not considered as having two + alternatives, because only one is ever used. In other words, the | character in a conditional subpattern has a different meaning. Ignoring white space, consider: ^.*? (?(?=a) a | b(*THEN)c ) - If the subject is "ba", this pattern does not match. Because .*? is - ungreedy, it initially matches zero characters. The condition (?=a) - then fails, the character "b" is matched, but "c" is not. At this - point, matching does not backtrack to .*? as might perhaps be expected - from the presence of the | character. The conditional subpattern is + If the subject is "ba", this pattern does not match. Because .*? is + ungreedy, it initially matches zero characters. The condition (?=a) + then fails, the character "b" is matched, but "c" is not. At this + point, matching does not backtrack to .*? as might perhaps be expected + from the presence of the | character. The conditional subpattern is part of the single alternative that comprises the whole pattern, and so - the match fails. (If there was a backtrack into .*?, allowing it to + the match fails. (If there was a backtrack into .*?, allowing it to match "b", the match would succeed.) - The verbs just described provide four different "strengths" of control + The verbs just described provide four different "strengths" of control when subsequent matching fails. (*THEN) is the weakest, carrying on the - match at the next alternative. (*PRUNE) comes next, failing the match - at the current starting position, but allowing an advance to the next - character (for an unanchored pattern). (*SKIP) is similar, except that + match at the next alternative. (*PRUNE) comes next, failing the match + at the current starting position, but allowing an advance to the next + character (for an unanchored pattern). (*SKIP) is similar, except that the advance may be more than one character. (*COMMIT) is the strongest, causing the entire match to fail. More than one backtracking verb - If more than one backtracking verb is present in a pattern, the one - that is backtracked onto first acts. For example, consider this pat- + If more than one backtracking verb is present in a pattern, the one + that is backtracked onto first acts. For example, consider this pat- tern, where A, B, etc. are complex pattern fragments: (A(*COMMIT)B(*THEN)C|ABD) - If A matches but B fails, the backtrack to (*COMMIT) causes the entire + If A matches but B fails, the backtrack to (*COMMIT) causes the entire match to fail. However, if A and B match, but C fails, the backtrack to - (*THEN) causes the next alternative (ABD) to be tried. This behaviour - is consistent, but is not always the same as Perl's. It means that if - two or more backtracking verbs appear in succession, all the the last + (*THEN) causes the next alternative (ABD) to be tried. This behaviour + is consistent, but is not always the same as Perl's. It means that if + two or more backtracking verbs appear in succession, all the the last of them has no effect. Consider this example: ...(*COMMIT)(*PRUNE)... If there is a matching failure to the right, backtracking onto (*PRUNE) - causes it to be triggered, and its action is taken. There can never be + causes it to be triggered, and its action is taken. There can never be a backtrack onto (*COMMIT). Backtracking verbs in repeated groups - PCRE differs from Perl in its handling of backtracking verbs in + PCRE differs from Perl in its handling of backtracking verbs in repeated groups. For example, consider: /(a(*COMMIT)b)+ac/ - If the subject is "abac", Perl matches, but PCRE fails because the + If the subject is "abac", Perl matches, but PCRE fails because the (*COMMIT) in the second repeat of the group acts. Backtracking verbs in assertions - (*FAIL) in an assertion has its normal effect: it forces an immediate + (*FAIL) in an assertion has its normal effect: it forces an immediate backtrack. (*ACCEPT) in a positive assertion causes the assertion to succeed with- - out any further processing. In a negative assertion, (*ACCEPT) causes + out any further processing. In a negative assertion, (*ACCEPT) causes the assertion to fail without any further processing. - The other backtracking verbs are not treated specially if they appear - in a positive assertion. In particular, (*THEN) skips to the next - alternative in the innermost enclosing group that has alternations, + The other backtracking verbs are not treated specially if they appear + in a positive assertion. In particular, (*THEN) skips to the next + alternative in the innermost enclosing group that has alternations, whether or not this is within the assertion. - Negative assertions are, however, different, in order to ensure that - changing a positive assertion into a negative assertion changes its + Negative assertions are, however, different, in order to ensure that + changing a positive assertion into a negative assertion changes its result. Backtracking into (*COMMIT), (*SKIP), or (*PRUNE) causes a neg- ative assertion to be true, without considering any further alternative branches in the assertion. Backtracking into (*THEN) causes it to skip - to the next enclosing alternative within the assertion (the normal be- - haviour), but if the assertion does not have such an alternative, + to the next enclosing alternative within the assertion (the normal be- + haviour), but if the assertion does not have such an alternative, (*THEN) behaves like (*PRUNE). Backtracking verbs in subroutines - These behaviours occur whether or not the subpattern is called recur- + These behaviours occur whether or not the subpattern is called recur- sively. Perl's treatment of subroutines is different in some cases. - (*FAIL) in a subpattern called as a subroutine has its normal effect: + (*FAIL) in a subpattern called as a subroutine has its normal effect: it forces an immediate backtrack. - (*ACCEPT) in a subpattern called as a subroutine causes the subroutine - match to succeed without any further processing. Matching then contin- + (*ACCEPT) in a subpattern called as a subroutine causes the subroutine + match to succeed without any further processing. Matching then contin- ues after the subroutine call. (*COMMIT), (*SKIP), and (*PRUNE) in a subpattern called as a subroutine cause the subroutine match to fail. - (*THEN) skips to the next alternative in the innermost enclosing group - within the subpattern that has alternatives. If there is no such group + (*THEN) skips to the next alternative in the innermost enclosing group + within the subpattern that has alternatives. If there is no such group within the subpattern, (*THEN) causes the subroutine match to fail. SEE ALSO - pcreapi(3), pcrecallout(3), pcrematching(3), pcresyntax(3), pcre(3), + pcreapi(3), pcrecallout(3), pcrematching(3), pcresyntax(3), pcre(3), pcre16(3), pcre32(3). @@ -7668,8 +7673,8 @@ AUTHOR REVISION - Last updated: 14 June 2015 - Copyright (c) 1997-2015 University of Cambridge. + Last updated: 23 October 2016 + Copyright (c) 1997-2016 University of Cambridge. ------------------------------------------------------------------------------ diff --git a/pcre/doc/pcrecompat.3 b/pcre/doc/pcrecompat.3 index 0cc40198235..6156e776f53 100644 --- a/pcre/doc/pcrecompat.3 +++ b/pcre/doc/pcrecompat.3 @@ -113,7 +113,7 @@ the pattern /^(a(b)?)+$/ in Perl leaves $2 unset, but in PCRE it is set to "b". 14. PCRE's handling of duplicate subpattern numbers and duplicate subpattern names is not as general as Perl's. This is a consequence of the fact the PCRE works internally just with numbers, using an external table to translate -between numbers and names. In particular, a pattern such as (?|(?<a>A)|(?<b)B), +between numbers and names. In particular, a pattern such as (?|(?<a>A)|(?<b>B), where the two capturing parentheses have the same number but different names, is not supported, and causes an error at compile time. If it were allowed, it would not be possible to distinguish which parentheses matched, because both diff --git a/pcre/doc/pcrepattern.3 b/pcre/doc/pcrepattern.3 index 3b8c6393d21..97df217fdb2 100644 --- a/pcre/doc/pcrepattern.3 +++ b/pcre/doc/pcrepattern.3 @@ -1,4 +1,4 @@ -.TH PCREPATTERN 3 "14 June 2015" "PCRE 8.38" +.TH PCREPATTERN 3 "23 October 2016" "PCRE 8.40" .SH NAME PCRE - Perl-compatible regular expressions .SH "PCRE REGULAR EXPRESSION DETAILS" @@ -336,22 +336,22 @@ When PCRE is compiled in EBCDIC mode, \ea, \ee, \ef, \en, \er, and \et generate the appropriate EBCDIC code values. The \ec escape is processed as specified for Perl in the \fBperlebcdic\fP document. The only characters that are allowed after \ec are A-Z, a-z, or one of @, [, \e, ], ^, _, or ?. Any -other character provokes a compile-time error. The sequence \e@ encodes -character code 0; the letters (in either case) encode characters 1-26 (hex 01 -to hex 1A); [, \e, ], ^, and _ encode characters 27-31 (hex 1B to hex 1F), and -\e? becomes either 255 (hex FF) or 95 (hex 5F). +other character provokes a compile-time error. The sequence \ec@ encodes +character code 0; after \ec the letters (in either case) encode characters 1-26 +(hex 01 to hex 1A); [, \e, ], ^, and _ encode characters 27-31 (hex 1B to hex +1F), and \ec? becomes either 255 (hex FF) or 95 (hex 5F). .P -Thus, apart from \e?, these escapes generate the same character code values as +Thus, apart from \ec?, these escapes generate the same character code values as they do in an ASCII environment, though the meanings of the values mostly -differ. For example, \eG always generates code value 7, which is BEL in ASCII +differ. For example, \ecG always generates code value 7, which is BEL in ASCII but DEL in EBCDIC. .P -The sequence \e? generates DEL (127, hex 7F) in an ASCII environment, but +The sequence \ec? generates DEL (127, hex 7F) in an ASCII environment, but because 127 is not a control character in EBCDIC, Perl makes it generate the APC character. Unfortunately, there are several variants of EBCDIC. In most of them the APC character has the value 255 (hex FF), but in the one Perl calls POSIX-BC its value is 95 (hex 5F). If certain other characters have POSIX-BC -values, PCRE makes \e? generate 95; otherwise it generates 255. +values, PCRE makes \ec? generate 95; otherwise it generates 255. .P After \e0 up to two further octal digits are read. If there are fewer than two digits, just those that are present are used. Thus the sequence \e0\ex\e015 @@ -1511,12 +1511,8 @@ J, U and X respectively. .P When one of these option changes occurs at top level (that is, not inside subpattern parentheses), the change applies to the remainder of the pattern -that follows. If the change is placed right at the start of a pattern, PCRE -extracts it into the global options (and it will therefore show up in data -extracted by the \fBpcre_fullinfo()\fP function). -.P -An option change within a subpattern (see below for a description of -subpatterns) affects only that part of the subpattern that follows it, so +that follows. An option change within a subpattern (see below for a description +of subpatterns) affects only that part of the subpattern that follows it, so .sp (a(?i)b)c .sp @@ -2171,6 +2167,13 @@ numbering the capturing subpatterns in the whole pattern. However, substring capturing is carried out only for positive assertions. (Perl sometimes, but not always, does do capturing in negative assertions.) .P +WARNING: If a positive assertion containing one or more capturing subpatterns +succeeds, but failure to match later in the pattern causes backtracking over +this assertion, the captures within the assertion are reset only if no higher +numbered captures are already set. This is, unfortunately, a fundamental +limitation of the current implementation, and as PCRE1 is now in +maintenance-only status, it is unlikely ever to change. +.P For compatibility with Perl, assertion subpatterns may be repeated; though it makes no sense to assert the same thing several times, the side effect of capturing parentheses may occasionally be useful. In practice, there only three @@ -3296,6 +3299,6 @@ Cambridge CB2 3QH, England. .rs .sp .nf -Last updated: 14 June 2015 -Copyright (c) 1997-2015 University of Cambridge. +Last updated: 23 October 2016 +Copyright (c) 1997-2016 University of Cambridge. .fi diff --git a/pcre/pcre_compile.c b/pcre/pcre_compile.c index 7cd39501230..de92313e2f8 100644 --- a/pcre/pcre_compile.c +++ b/pcre/pcre_compile.c @@ -5579,6 +5579,34 @@ for (;; ptr++) #endif #if defined SUPPORT_UTF || !defined COMPILE_PCRE8 { + /* For non-UCP wide characters, in a non-negative class containing \S or + similar (should_flip_negation is set), all characters greater than 255 + must be in the class. */ + + if ( +#if defined COMPILE_PCRE8 + utf && +#endif + should_flip_negation && !negate_class && (options & PCRE_UCP) == 0) + { + *class_uchardata++ = XCL_RANGE; + if (utf) /* Will always be utf in the 8-bit library */ + { + class_uchardata += PRIV(ord2utf)(0x100, class_uchardata); + class_uchardata += PRIV(ord2utf)(0x10ffff, class_uchardata); + } + else /* Can only happen for the 16-bit & 32-bit libraries */ + { +#if defined COMPILE_PCRE16 + *class_uchardata++ = 0x100; + *class_uchardata++ = 0xffffu; +#elif defined COMPILE_PCRE32 + *class_uchardata++ = 0x100; + *class_uchardata++ = 0xffffffffu; +#endif + } + } + *class_uchardata++ = XCL_END; /* Marks the end of extra data */ *code++ = OP_XCLASS; code += LINK_SIZE; @@ -6923,7 +6951,8 @@ for (;; ptr++) slot = cd->name_table; for (i = 0; i < cd->names_found; i++) { - if (STRNCMP_UC_UC(name, slot+IMM2_SIZE, namelen) == 0) break; + if (STRNCMP_UC_UC(name, slot+IMM2_SIZE, namelen) == 0 && + slot[IMM2_SIZE+namelen] == 0) break; slot += cd->name_entry_size; } @@ -7889,15 +7918,17 @@ for (;; ptr++) } } - /* For a forward assertion, we take the reqchar, if set. This can be - helpful if the pattern that follows the assertion doesn't set a different - char. For example, it's useful for /(?=abcde).+/. We can't set firstchar - for an assertion, however because it leads to incorrect effect for patterns - such as /(?=a)a.+/ when the "real" "a" would then become a reqchar instead - of a firstchar. This is overcome by a scan at the end if there's no - firstchar, looking for an asserted first char. */ - - else if (bravalue == OP_ASSERT && subreqcharflags >= 0) + /* For a forward assertion, we take the reqchar, if set, provided that the + group has also set a first char. This can be helpful if the pattern that + follows the assertion doesn't set a different char. For example, it's + useful for /(?=abcde).+/. We can't set firstchar for an assertion, however + because it leads to incorrect effect for patterns such as /(?=a)a.+/ when + the "real" "a" would then become a reqchar instead of a firstchar. This is + overcome by a scan at the end if there's no firstchar, looking for an + asserted first char. */ + + else if (bravalue == OP_ASSERT && subreqcharflags >= 0 && + subfirstcharflags >= 0) { reqchar = subreqchar; reqcharflags = subreqcharflags; @@ -8686,8 +8717,8 @@ matching and for non-DOTALL patterns that start with .* (which must start at the beginning or after \n). As in the case of is_anchored() (see above), we have to take account of back references to capturing brackets that contain .* because in that case we can't make the assumption. Also, the appearance of .* -inside atomic brackets or in a pattern that contains *PRUNE or *SKIP does not -count, because once again the assumption no longer holds. +inside atomic brackets or in an assertion, or in a pattern that contains *PRUNE +or *SKIP does not count, because once again the assumption no longer holds. Arguments: code points to start of expression (the bracket) @@ -8696,13 +8727,14 @@ Arguments: the less precise approach cd points to the compile data atomcount atomic group level + inassert TRUE if in an assertion Returns: TRUE or FALSE */ static BOOL is_startline(const pcre_uchar *code, unsigned int bracket_map, - compile_data *cd, int atomcount) + compile_data *cd, int atomcount, BOOL inassert) { do { const pcre_uchar *scode = first_significant_code( @@ -8729,7 +8761,7 @@ do { return FALSE; default: /* Assertion */ - if (!is_startline(scode, bracket_map, cd, atomcount)) return FALSE; + if (!is_startline(scode, bracket_map, cd, atomcount, TRUE)) return FALSE; do scode += GET(scode, 1); while (*scode == OP_ALT); scode += 1 + LINK_SIZE; break; @@ -8743,7 +8775,7 @@ do { if (op == OP_BRA || op == OP_BRAPOS || op == OP_SBRA || op == OP_SBRAPOS) { - if (!is_startline(scode, bracket_map, cd, atomcount)) return FALSE; + if (!is_startline(scode, bracket_map, cd, atomcount, inassert)) return FALSE; } /* Capturing brackets */ @@ -8753,33 +8785,33 @@ do { { int n = GET2(scode, 1+LINK_SIZE); int new_map = bracket_map | ((n < 32)? (1 << n) : 1); - if (!is_startline(scode, new_map, cd, atomcount)) return FALSE; + if (!is_startline(scode, new_map, cd, atomcount, inassert)) return FALSE; } /* Positive forward assertions */ else if (op == OP_ASSERT) { - if (!is_startline(scode, bracket_map, cd, atomcount)) return FALSE; + if (!is_startline(scode, bracket_map, cd, atomcount, TRUE)) return FALSE; } /* Atomic brackets */ else if (op == OP_ONCE || op == OP_ONCE_NC) { - if (!is_startline(scode, bracket_map, cd, atomcount + 1)) return FALSE; + if (!is_startline(scode, bracket_map, cd, atomcount + 1, inassert)) return FALSE; } /* .* means "start at start or after \n" if it isn't in atomic brackets or - brackets that may be referenced, as long as the pattern does not contain - *PRUNE or *SKIP, because these break the feature. Consider, for example, - /.*?a(*PRUNE)b/ with the subject "aab", which matches "ab", i.e. not at the - start of a line. */ + brackets that may be referenced or an assertion, as long as the pattern does + not contain *PRUNE or *SKIP, because these break the feature. Consider, for + example, /.*?a(*PRUNE)b/ with the subject "aab", which matches "ab", i.e. + not at the start of a line. */ else if (op == OP_TYPESTAR || op == OP_TYPEMINSTAR || op == OP_TYPEPOSSTAR) { if (scode[1] != OP_ANY || (bracket_map & cd->backref_map) != 0 || - atomcount > 0 || cd->had_pruneorskip) + atomcount > 0 || cd->had_pruneorskip || inassert) return FALSE; } @@ -9634,7 +9666,7 @@ if ((re->options & PCRE_ANCHORED) == 0) re->flags |= PCRE_FIRSTSET; } - else if (is_startline(codestart, 0, cd, 0)) re->flags |= PCRE_STARTLINE; + else if (is_startline(codestart, 0, cd, 0, FALSE)) re->flags |= PCRE_STARTLINE; } } diff --git a/pcre/pcre_jit_compile.c b/pcre/pcre_jit_compile.c index 4f15a27ac28..46ce6c65d54 100644 --- a/pcre/pcre_jit_compile.c +++ b/pcre/pcre_jit_compile.c @@ -4004,12 +4004,12 @@ sljit_emit_op_custom(compiler, instruction, 4); if (load_twice) { - OP1(SLJIT_MOV, TMP3, 0, TMP2, 0); + OP1(SLJIT_MOV, RETURN_ADDR, 0, TMP2, 0); instruction[3] = 0xc0 | (tmp2_ind << 3) | 1; sljit_emit_op_custom(compiler, instruction, 4); OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0); - OP1(SLJIT_MOV, TMP2, 0, TMP3, 0); + OP1(SLJIT_MOV, TMP2, 0, RETURN_ADDR, 0); } OP2(SLJIT_ASHR, TMP1, 0, TMP1, 0, TMP2, 0); diff --git a/pcre/pcre_jit_test.c b/pcre/pcre_jit_test.c index 9b61ec000fa..034cb52697f 100644 --- a/pcre/pcre_jit_test.c +++ b/pcre/pcre_jit_test.c @@ -687,6 +687,7 @@ static struct regression_test_case regression_test_cases[] = { { PCRE_FIRSTLINE | PCRE_NEWLINE_LF | PCRE_DOTALL, 0 | F_NOMATCH, "ab.", "ab" }, { MUA | PCRE_FIRSTLINE, 1 | F_NOMATCH, "^[a-d0-9]", "\nxx\nd" }, { PCRE_NEWLINE_ANY | PCRE_FIRSTLINE | PCRE_DOTALL, 0, "....a", "012\n0a" }, + { MUA | PCRE_FIRSTLINE, 0, "[aC]", "a" }, /* Recurse. */ { MUA, 0, "(a)(?1)", "aa" }, diff --git a/pcre/pcregrep.c b/pcre/pcregrep.c index cd53c648da2..fd2a67622ba 100644 --- a/pcre/pcregrep.c +++ b/pcre/pcregrep.c @@ -1803,6 +1803,12 @@ while (ptr < endptr) match = FALSE; if (line_buffered) fflush(stdout); rc = 0; /* Had some success */ + + /* If the current match ended past the end of the line (only possible + in multiline mode), we are done with this line. */ + + if ((unsigned int)offsets[1] > linelength) goto END_ONE_MATCH; + startoffset = offsets[1]; /* Restart after the match */ if (startoffset <= oldstartoffset) { diff --git a/pcre/pcretest.c b/pcre/pcretest.c index 78ef5177df7..5b73a918075 100644 --- a/pcre/pcretest.c +++ b/pcre/pcretest.c @@ -1982,6 +1982,7 @@ return(result); static int pchar(pcre_uint32 c, FILE *f) { int n = 0; +char tempbuffer[16]; if (PRINTOK(c)) { if (f != NULL) fprintf(f, "%c", c); @@ -2003,6 +2004,8 @@ if (c < 0x100) } if (f != NULL) n = fprintf(f, "\\x{%02x}", c); + else n = sprintf(tempbuffer, "\\x{%02x}", c); + return n >= 0 ? n : 0; } @@ -5042,7 +5045,7 @@ while (!done) if ((all_use_dfa || use_dfa) && find_match_limit) { - printf("**Match limit not relevant for DFA matching: ignored\n"); + printf("** Match limit not relevant for DFA matching: ignored\n"); find_match_limit = 0; } @@ -5255,10 +5258,17 @@ while (!done) if (do_allcaps) { - if (new_info(re, NULL, PCRE_INFO_CAPTURECOUNT, &count) < 0) - goto SKIP_DATA; - count++; /* Allow for full match */ - if (count * 2 > use_size_offsets) count = use_size_offsets/2; + if (all_use_dfa || use_dfa) + { + fprintf(outfile, "** Show all captures ignored after DFA matching\n"); + } + else + { + if (new_info(re, NULL, PCRE_INFO_CAPTURECOUNT, &count) < 0) + goto SKIP_DATA; + count++; /* Allow for full match */ + if (count * 2 > use_size_offsets) count = use_size_offsets/2; + } } /* Output the captured substrings. Note that, for the matched string, diff --git a/pcre/testdata/testinput1 b/pcre/testdata/testinput1 index 8379ce04d5b..93abab3c851 100644 --- a/pcre/testdata/testinput1 +++ b/pcre/testdata/testinput1 @@ -5733,4 +5733,10 @@ AbcdCBefgBhiBqz "(?|(\k'Pm')|(?'Pm'))" abcd +/(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])(?=.*[,;:])(?=.{8,16})(?!.*[\s])/ + \ Fred:099 + +/(?=.*X)X$/ + \ X + /-- End of testinput1 --/ diff --git a/pcre/testdata/testinput16 b/pcre/testdata/testinput16 index 15419e63fa6..7ccde0a8c80 100644 --- a/pcre/testdata/testinput16 +++ b/pcre/testdata/testinput16 @@ -38,4 +38,30 @@ /s+/i8SI SSss\x{17f} +/[\W\p{Any}]/BZ + abc + 123 + +/[\W\pL]/BZ + abc + ** Failers + 123 + +/[\D]/8 + \x{1d7cf} + +/[\D\P{Nd}]/8 + \x{1d7cf} + +/[^\D]/8 + a9b + ** Failers + \x{1d7cf} + +/[^\D\P{Nd}]/8 + a9b + \x{1d7cf} + ** Failers + \x{10000} + /-- End of testinput16 --/ diff --git a/pcre/testdata/testinput19 b/pcre/testdata/testinput19 index ce45afcb595..dfe8c7befb6 100644 --- a/pcre/testdata/testinput19 +++ b/pcre/testdata/testinput19 @@ -25,4 +25,21 @@ /s+/i8SI SSss\x{17f} +/[\D]/8 + \x{1d7cf} + +/[\D\P{Nd}]/8 + \x{1d7cf} + +/[^\D]/8 + a9b + ** Failers + \x{1d7cf} + +/[^\D\P{Nd}]/8 + a9b + \x{1d7cf} + ** Failers + \x{10000} + /-- End of testinput19 --/ diff --git a/pcre/testdata/testinput2 b/pcre/testdata/testinput2 index 75e402ee9b3..08c6f39a565 100644 --- a/pcre/testdata/testinput2 +++ b/pcre/testdata/testinput2 @@ -4243,4 +4243,10 @@ backtracking verbs. --/ /\N(?(?C)0?!.)*/ +/(?<RA>abc)(?(R)xyz)/BZ + +/(?<R>abc)(?(R)xyz)/BZ + +/(?=.*[A-Z])/I + /-- End of testinput2 --/ diff --git a/pcre/testdata/testinput6 b/pcre/testdata/testinput6 index a178d3d67b4..22ed1e64d57 100644 --- a/pcre/testdata/testinput6 +++ b/pcre/testdata/testinput6 @@ -1562,4 +1562,10 @@ \x{389} \x{20ac} +/(?=.*b)\pL/ + 11bb + +/(?(?=.*b)(?=.*b)\pL|.*c)/ + 11bb + /-- End of testinput6 --/ diff --git a/pcre/testdata/testinput7 b/pcre/testdata/testinput7 index 00b9738a371..f44a810f0f2 100644 --- a/pcre/testdata/testinput7 +++ b/pcre/testdata/testinput7 @@ -838,15 +838,6 @@ of case for anything other than the ASCII letters. --/ /^s?c/mi8I scat -/[\W\p{Any}]/BZ - abc - 123 - -/[\W\pL]/BZ - abc - ** Failers - 123 - /a[[:punct:]b]/WBZ /a[[:punct:]b]/8WBZ diff --git a/pcre/testdata/testinput8 b/pcre/testdata/testinput8 index 931dd717e74..7f8fa8292c5 100644 --- a/pcre/testdata/testinput8 +++ b/pcre/testdata/testinput8 @@ -4841,4 +4841,8 @@ bbb aaa +/()()a+/O= + aaa\D + a\D + /-- End of testinput8 --/ diff --git a/pcre/testdata/testoutput1 b/pcre/testdata/testoutput1 index e852ab9544c..a2b3cffe9d4 100644 --- a/pcre/testdata/testoutput1 +++ b/pcre/testdata/testoutput1 @@ -9434,4 +9434,12 @@ No match 0: 1: +/(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])(?=.*[,;:])(?=.{8,16})(?!.*[\s])/ + \ Fred:099 + 0: + +/(?=.*X)X$/ + \ X + 0: X + /-- End of testinput1 --/ diff --git a/pcre/testdata/testoutput16 b/pcre/testdata/testoutput16 index fd184cdbeee..e6ba26acfd4 100644 --- a/pcre/testdata/testoutput16 +++ b/pcre/testdata/testoutput16 @@ -138,4 +138,56 @@ Starting chars: S s \xc5 SSss\x{17f} 0: SSss\x{17f} +/[\W\p{Any}]/BZ +------------------------------------------------------------------ + Bra + [\x00-/:-@[-^`{-\xff\p{Any}] + Ket + End +------------------------------------------------------------------ + abc + 0: a + 123 + 0: 1 + +/[\W\pL]/BZ +------------------------------------------------------------------ + Bra + [\x00-/:-@[-^`{-\xff\p{L}] + Ket + End +------------------------------------------------------------------ + abc + 0: a + ** Failers + 0: * + 123 +No match + +/[\D]/8 + \x{1d7cf} + 0: \x{1d7cf} + +/[\D\P{Nd}]/8 + \x{1d7cf} + 0: \x{1d7cf} + +/[^\D]/8 + a9b + 0: 9 + ** Failers +No match + \x{1d7cf} +No match + +/[^\D\P{Nd}]/8 + a9b + 0: 9 + \x{1d7cf} + 0: \x{1d7cf} + ** Failers +No match + \x{10000} +No match + /-- End of testinput16 --/ diff --git a/pcre/testdata/testoutput19 b/pcre/testdata/testoutput19 index eb8a8f6cd34..982bea4c136 100644 --- a/pcre/testdata/testoutput19 +++ b/pcre/testdata/testoutput19 @@ -105,4 +105,30 @@ Starting chars: S s \xff SSss\x{17f} 0: SSss\x{17f} +/[\D]/8 + \x{1d7cf} + 0: \x{1d7cf} + +/[\D\P{Nd}]/8 + \x{1d7cf} + 0: \x{1d7cf} + +/[^\D]/8 + a9b + 0: 9 + ** Failers +No match + \x{1d7cf} +No match + +/[^\D\P{Nd}]/8 + a9b + 0: 9 + \x{1d7cf} + 0: \x{1d7cf} + ** Failers +No match + \x{10000} +No match + /-- End of testinput19 --/ diff --git a/pcre/testdata/testoutput2 b/pcre/testdata/testoutput2 index 5e88d1a7091..811bbefc84c 100644 --- a/pcre/testdata/testoutput2 +++ b/pcre/testdata/testoutput2 @@ -9380,7 +9380,7 @@ No need char /(?(?=.*b).*b|^d)/I Capturing subpattern count = 0 No options -First char at start or follows newline +No first char No need char /xyz/C @@ -14670,4 +14670,39 @@ No match /\N(?(?C)0?!.)*/ Failed: assertion expected after (?( or (?(?C) at offset 4 +/(?<RA>abc)(?(R)xyz)/BZ +------------------------------------------------------------------ + Bra + CBra 1 + abc + Ket + Cond + Cond recurse any + xyz + Ket + Ket + End +------------------------------------------------------------------ + +/(?<R>abc)(?(R)xyz)/BZ +------------------------------------------------------------------ + Bra + CBra 1 + abc + Ket + Cond + 1 Cond ref + xyz + Ket + Ket + End +------------------------------------------------------------------ + +/(?=.*[A-Z])/I +Capturing subpattern count = 0 +May match empty string +No options +No first char +No need char + /-- End of testinput2 --/ diff --git a/pcre/testdata/testoutput6 b/pcre/testdata/testoutput6 index b64dc0dc366..422d3833578 100644 --- a/pcre/testdata/testoutput6 +++ b/pcre/testdata/testoutput6 @@ -2573,4 +2573,12 @@ No match \x{20ac} No match +/(?=.*b)\pL/ + 11bb + 0: b + +/(?(?=.*b)(?=.*b)\pL|.*c)/ + 11bb + 0: b + /-- End of testinput6 --/ diff --git a/pcre/testdata/testoutput7 b/pcre/testdata/testoutput7 index fdfff646d3e..2b167b28d1c 100644 --- a/pcre/testdata/testoutput7 +++ b/pcre/testdata/testoutput7 @@ -2295,32 +2295,6 @@ Need char = 'c' (caseless) scat 0: sc -/[\W\p{Any}]/BZ ------------------------------------------------------------------- - Bra - [\x00-/:-@[-^`{-\xff\p{Any}] - Ket - End ------------------------------------------------------------------- - abc - 0: a - 123 - 0: 1 - -/[\W\pL]/BZ ------------------------------------------------------------------- - Bra - [\x00-/:-@[-^`{-\xff\p{L}] - Ket - End ------------------------------------------------------------------- - abc - 0: a - ** Failers - 0: * - 123 -No match - /a[[:punct:]b]/WBZ ------------------------------------------------------------------ Bra diff --git a/pcre/testdata/testoutput8 b/pcre/testdata/testoutput8 index e4fa4977561..17b667a980c 100644 --- a/pcre/testdata/testoutput8 +++ b/pcre/testdata/testoutput8 @@ -7791,4 +7791,14 @@ Matched, but offsets vector is too small to show all matches aaa No match +/()()a+/O= + aaa\D +** Show all captures ignored after DFA matching + 0: aaa + 1: aa + 2: a + a\D +** Show all captures ignored after DFA matching + 0: a + /-- End of testinput8 --/ diff --git a/plugin/server_audit/server_audit.c b/plugin/server_audit/server_audit.c index 4c5e6992a32..3b93d3277a6 100644 --- a/plugin/server_audit/server_audit.c +++ b/plugin/server_audit/server_audit.c @@ -156,10 +156,8 @@ static File loc_open(const char *FileName, int Flags) File fd; #if defined(_WIN32) fd= my_win_open(FileName, Flags); -#elif !defined(NO_OPEN_3) - fd = open(FileName, Flags, my_umask); /* Normal unix */ #else - fd = open((char *) FileName, Flags); + fd = open(FileName, Flags, my_umask); #endif my_errno= errno; return fd; @@ -2295,10 +2293,10 @@ typedef struct loc_system_variables } LOC_SV; +static int init_done= 0; + static int server_audit_init(void *p __attribute__((unused))) { - const void *my_hash_init_ptr; - if (!serv_ver) { #ifdef _WIN32 @@ -2307,11 +2305,16 @@ static int server_audit_init(void *p __attribute__((unused))) serv_ver= server_version; #endif /*_WIN32*/ } - my_hash_init_ptr= dlsym(RTLD_DEFAULT, "_my_hash_init"); - if (!my_hash_init_ptr) + if (!mysql_57_started) { - maria_above_5= 1; - my_hash_init_ptr= dlsym(RTLD_DEFAULT, "my_hash_init2"); + const void *my_hash_init_ptr= dlsym(RTLD_DEFAULT, "_my_hash_init"); + if (!my_hash_init_ptr) + { + maria_above_5= 1; + my_hash_init_ptr= dlsym(RTLD_DEFAULT, "my_hash_init2"); + } + if (!my_hash_init_ptr) + return 1; } if(!(int_mysql_data_home= dlsym(RTLD_DEFAULT, "mysql_data_home"))) @@ -2320,7 +2323,7 @@ static int server_audit_init(void *p __attribute__((unused))) int_mysql_data_home= &default_home; } - if (!serv_ver || !my_hash_init_ptr) + if (!serv_ver) return 1; if (!started_mysql) @@ -2400,6 +2403,7 @@ static int server_audit_init(void *p __attribute__((unused))) if (logging) start_logging(); + init_done= 1; return 0; } @@ -2415,6 +2419,10 @@ static int server_audit_init_mysql(void *p) static int server_audit_deinit(void *p __attribute__((unused))) { + if (!init_done) + return 0; + + init_done= 0; coll_free(&incl_user_coll); coll_free(&excl_user_coll); @@ -2837,13 +2845,15 @@ void __attribute__ ((constructor)) audit_plugin_so_init(void) if (sc >= 24) use_event_data_for_disconnect= 1; } - else if (serv_ver[0] == '5' && serv_ver[2] == '7') + else if ((serv_ver[0] == '5' && serv_ver[2] == '7') || + (serv_ver[0] == '8' && serv_ver[2] == '0')) { mysql_57_started= 1; _mysql_plugin_declarations_[0].info= mysql_v4_descriptor; use_event_data_for_disconnect= 1; } - MYSQL_SYSVAR_NAME(loc_info).flags= PLUGIN_VAR_READONLY | PLUGIN_VAR_MEMALLOC; + MYSQL_SYSVAR_NAME(loc_info).flags= PLUGIN_VAR_STR | PLUGIN_VAR_THDLOCAL | + PLUGIN_VAR_READONLY | PLUGIN_VAR_MEMALLOC; } memset(locinfo_ini_value, 'O', sizeof(locinfo_ini_value)-1); diff --git a/sql-common/client.c b/sql-common/client.c index 78c5426367c..0b65853f61d 100644 --- a/sql-common/client.c +++ b/sql-common/client.c @@ -1,5 +1,5 @@ /* Copyright (c) 2003, 2016, Oracle and/or its affiliates. - Copyright (c) 2009, 2016, MariaDB + Copyright (c) 2009, 2017, MariaDB 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 @@ -3867,8 +3867,6 @@ static void mysql_close_free(MYSQL *mysql) static void mysql_prune_stmt_list(MYSQL *mysql) { LIST *element= mysql->stmts; - LIST *pruned_list= 0; - for (; element; element= element->next) { MYSQL_STMT *stmt= (MYSQL_STMT *) element->data; @@ -3878,14 +3876,9 @@ static void mysql_prune_stmt_list(MYSQL *mysql) stmt->last_errno= CR_SERVER_LOST; strmov(stmt->last_error, ER(CR_SERVER_LOST)); strmov(stmt->sqlstate, unknown_sqlstate); - } - else - { - pruned_list= list_add(pruned_list, element); + mysql->stmts= list_delete(mysql->stmts, element); } } - - mysql->stmts= pruned_list; } diff --git a/sql/contributors.h b/sql/contributors.h index 0359ec54022..3a771e2b493 100644 --- a/sql/contributors.h +++ b/sql/contributors.h @@ -37,16 +37,18 @@ struct show_table_contributors_st { struct show_table_contributors_st show_table_contributors[]= { /* MariaDB foundation sponsors, in contribution, size , time order */ - {"Booking.com", "http://www.booking.com", "Founding member, Platinum Sponsor of the MariaDB Foundation"}, + {"Booking.com", "https://www.booking.com", "Founding member, Platinum Sponsor of the MariaDB Foundation"}, + {"Alibaba Cloud", "https://intl.aliyun.com", "Platinum Sponsor of the MariaDB Foundation"}, {"MariaDB Corporation", "https://mariadb.com", "Founding member, Gold Sponsor of the MariaDB Foundation"}, - {"Visma", "http://visma.com", "Gold Sponsor of the MariaDB Foundation"}, - {"DBS", "http://dbs.com", "Gold Sponsor of the MariaDB Foundation"}, + {"Visma", "https://visma.com", "Gold Sponsor of the MariaDB Foundation"}, + {"DBS", "https://dbs.com", "Gold Sponsor of the MariaDB Foundation"}, {"Nexedi", "https://www.nexedi.com", "Silver Sponsor of the MariaDB Foundation"}, {"Acronis", "http://www.acronis.com", "Silver Sponsor of the MariaDB Foundation"}, {"Auttomattic", "https://automattic.com", "Bronze Sponsor of the MariaDB Foundation"}, - {"Verkkokauppa.com", "https://virtuozzo.com", "Bronze Sponsor of the MariaDB Foundation"}, - {"Virtuozzo", "https://virtuozzo.com/", "Bronze Sponsor of the MariaDB Foundation"}, - {"Tencent Game DBA", "http://tencentdba.com/about/", "Bronze Sponsor of the MariaDB Foundation"}, + {"Verkkokauppa.com", "https://www.verkkokauppa.com", "Bronze Sponsor of the MariaDB Foundation"}, + {"Virtuozzo", "https://virtuozzo.com", "Bronze Sponsor of the MariaDB Foundation"}, + {"Tencent Game DBA", "http://tencentdba.com/about", "Bronze Sponsor of the MariaDB Foundation"}, + {"Tencent TDSQL", "http://tdsql.org", "Bronze Sponsor of the MariaDB Foundation"}, /* Sponsors of important features */ {"Google", "USA", "Sponsoring encryption, parallel replication and GTID"}, diff --git a/sql/event_scheduler.cc b/sql/event_scheduler.cc index 5c4926c830c..2423fac4ca6 100644 --- a/sql/event_scheduler.cc +++ b/sql/event_scheduler.cc @@ -133,12 +133,6 @@ post_init_event_thread(THD *thd) thd->cleanup(); return TRUE; } - - thread_safe_increment32(&thread_count, &thread_count_lock); - mysql_mutex_lock(&LOCK_thread_count); - threads.append(thd); - mysql_mutex_unlock(&LOCK_thread_count); - inc_thread_running(); return FALSE; } @@ -157,7 +151,13 @@ deinit_event_thread(THD *thd) thd->proc_info= "Clearing"; DBUG_PRINT("exit", ("Event thread finishing")); - delete_running_thd(thd); + mysql_mutex_lock(&LOCK_thread_count); + thd->unlink(); + mysql_mutex_unlock(&LOCK_thread_count); + + delete thd; + thread_safe_decrement32(&thread_count, &thread_count_lock); + signal_thd_deleted(); } @@ -191,8 +191,10 @@ pre_init_event_thread(THD* thd) thd->net.read_timeout= slave_net_timeout; thd->variables.option_bits|= OPTION_AUTO_IS_NULL; thd->client_capabilities|= CLIENT_MULTI_RESULTS; + thread_safe_increment32(&thread_count, &thread_count_lock); mysql_mutex_lock(&LOCK_thread_count); thd->thread_id= thd->variables.pseudo_thread_id= thread_id++; + threads.append(thd); mysql_mutex_unlock(&LOCK_thread_count); /* @@ -240,13 +242,8 @@ event_scheduler_thread(void *arg) my_free(arg); if (!res) scheduler->run(thd); - else - { - thd->proc_info= "Clearing"; - net_end(&thd->net); - delete thd; - } + deinit_event_thread(thd); DBUG_LEAVE; // Against gcc warnings my_thread_end(); return 0; @@ -310,6 +307,7 @@ Event_worker_thread::run(THD *thd, Event_queue_element_for_exec *event) DBUG_ENTER("Event_worker_thread::run"); DBUG_PRINT("info", ("Time is %ld, THD: 0x%lx", (long) my_time(0), (long) thd)); + inc_thread_running(); if (res) goto end; @@ -338,6 +336,7 @@ end: event->name.str)); delete event; + dec_thread_running(); deinit_event_thread(thd); DBUG_VOID_RETURN; @@ -442,13 +441,9 @@ Event_scheduler::start(int *err_no) " Can not create thread for event scheduler (errno=%d)", *err_no); - new_thd->proc_info= "Clearing"; - DBUG_ASSERT(new_thd->net.buff != 0); - net_end(&new_thd->net); - state= INITIALIZED; scheduler_thd= NULL; - delete new_thd; + deinit_event_thread(new_thd); delete scheduler_param_value; ret= true; @@ -515,7 +510,6 @@ Event_scheduler::run(THD *thd) } LOCK_DATA(); - deinit_event_thread(thd); scheduler_thd= NULL; state= INITIALIZED; DBUG_PRINT("info", ("Broadcasting COND_state back to the stoppers")); @@ -575,10 +569,7 @@ Event_scheduler::execute_top(Event_queue_element_for_exec *event_name) sql_print_error("Event_scheduler::execute_top: Can not create event worker" " thread (errno=%d). Stopping event scheduler", res); - new_thd->proc_info= "Clearing"; - DBUG_ASSERT(new_thd->net.buff != 0); - net_end(&new_thd->net); - + deinit_event_thread(new_thd); goto error; } @@ -590,9 +581,6 @@ Event_scheduler::execute_top(Event_queue_element_for_exec *event_name) error: DBUG_PRINT("error", ("Event_scheduler::execute_top() res: %d", res)); - if (new_thd) - delete new_thd; - delete event_name; DBUG_RETURN(TRUE); } diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index af81962904f..e854045661b 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -7279,6 +7279,7 @@ int ha_partition::reset(void) result= tmp; } bitmap_clear_all(&m_partitions_to_reset); + m_extra_prepare_for_update= FALSE; DBUG_RETURN(result); } diff --git a/sql/handler.cc b/sql/handler.cc index 79649316e73..47c5889431d 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -3441,6 +3441,7 @@ void handler::print_error(int error, myf errflag) textno=ER_FILE_USED; break; case ENOENT: + case ENOTDIR: textno=ER_FILE_NOT_FOUND; break; case ENOSPC: @@ -3919,8 +3920,7 @@ int handler::delete_table(const char *name) for (const char **ext=bas_ext(); *ext ; ext++) { - fn_format(buff, name, "", *ext, MY_UNPACK_FILENAME|MY_APPEND_EXT); - if (mysql_file_delete_with_symlink(key_file_misc, buff, MYF(0))) + if (my_handler_delete_with_symlink(key_file_misc, name, *ext, 0)) { if (my_errno != ENOENT) { @@ -4279,7 +4279,7 @@ enum_alter_inplace_result handler::check_if_supported_inplace_alter(TABLE *altered_table, Alter_inplace_info *ha_alter_info) { - DBUG_ENTER("check_if_supported_alter"); + DBUG_ENTER("handler::check_if_supported_inplace_alter"); HA_CREATE_INFO *create_info= ha_alter_info->create_info; diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index e0dbf2c5a73..192e06566ff 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -798,7 +798,7 @@ int Arg_comparator::set_cmp_func(Item_result_field *owner_arg, Item **a1, Item **a2, Item_result type) { - thd= current_thd; + THD *thd= current_thd; owner= owner_arg; set_null= set_null && owner_arg; a= a1; @@ -868,7 +868,6 @@ Item** Arg_comparator::cache_converted_constant(THD *thd_arg, Item **value, void Arg_comparator::set_datetime_cmp_func(Item_result_field *owner_arg, Item **a1, Item **b1) { - thd= current_thd; owner= owner_arg; a= a1; b= b1; @@ -943,12 +942,10 @@ get_datetime_value(THD *thd, Item ***item_arg, Item **cache_arg, if (cache_arg && item->const_item() && !(item->type() == Item::CACHE_ITEM && item->cmp_type() == TIME_RESULT)) { - Query_arena backup; - Query_arena *save_arena= thd->switch_to_arena_for_cached_items(&backup); - Item_cache_temporal *cache= new Item_cache_temporal(f_type); - if (save_arena) - thd->set_query_arena(save_arena); + if (!thd) + thd= current_thd; + Item_cache_temporal *cache= new Item_cache_temporal(f_type); cache->store_packed(value, item); *cache_arg= cache; *item_arg= cache_arg; @@ -983,12 +980,12 @@ int Arg_comparator::compare_datetime() owner->null_value= 1; /* Get DATE/DATETIME/TIME value of the 'a' item. */ - a_value= get_datetime_value(thd, &a, &a_cache, *b, &a_is_null); + a_value= get_datetime_value(0, &a, &a_cache, *b, &a_is_null); if (a_is_null) return -1; /* Get DATE/DATETIME/TIME value of the 'b' item. */ - b_value= get_datetime_value(thd, &b, &b_cache, *a, &b_is_null); + b_value= get_datetime_value(0, &b, &b_cache, *a, &b_is_null); if (b_is_null) return -1; @@ -1006,10 +1003,10 @@ int Arg_comparator::compare_e_datetime() longlong a_value, b_value; /* Get DATE/DATETIME/TIME value of the 'a' item. */ - a_value= get_datetime_value(thd, &a, &a_cache, *b, &a_is_null); + a_value= get_datetime_value(0, &a, &a_cache, *b, &a_is_null); /* Get DATE/DATETIME/TIME value of the 'b' item. */ - b_value= get_datetime_value(thd, &b, &b_cache, *a, &b_is_null); + b_value= get_datetime_value(0, &b, &b_cache, *a, &b_is_null); return a_is_null || b_is_null ? a_is_null == b_is_null : a_value == b_value; } @@ -3663,7 +3660,7 @@ void in_datetime::set(uint pos,Item *item) bool is_null; struct packed_longlong *buff= &((packed_longlong*) base)[pos]; - buff->val= get_datetime_value(thd, &tmp_item, 0, warn_item, &is_null); + buff->val= get_datetime_value(0, &tmp_item, 0, warn_item, &is_null); buff->unsigned_flag= 1L; } @@ -3671,7 +3668,7 @@ uchar *in_datetime::get_value(Item *item) { bool is_null; Item **tmp_item= lval_cache ? &lval_cache : &item; - tmp.val= get_datetime_value(thd, &tmp_item, &lval_cache, warn_item, &is_null); + tmp.val= get_datetime_value(0, &tmp_item, &lval_cache, warn_item, &is_null); if (item->null_value) return 0; tmp.unsigned_flag= 1L; @@ -3915,7 +3912,7 @@ void cmp_item_datetime::store_value(Item *item) { bool is_null; Item **tmp_item= lval_cache ? &lval_cache : &item; - value= get_datetime_value(thd, &tmp_item, &lval_cache, warn_item, &is_null); + value= get_datetime_value(0, &tmp_item, &lval_cache, warn_item, &is_null); } @@ -3924,7 +3921,7 @@ int cmp_item_datetime::cmp(Item *arg) bool is_null; Item **tmp_item= &arg; return value != - get_datetime_value(thd, &tmp_item, 0, warn_item, &is_null); + get_datetime_value(0, &tmp_item, 0, warn_item, &is_null); } diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 9e83b732dc7..b84cb26fb9c 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -45,7 +45,6 @@ class Arg_comparator: public Sql_alloc Arg_comparator *comparators; // used only for compare_row() double precision; /* Fields used in DATE/DATETIME comparison. */ - THD *thd; Item *a_cache, *b_cache; // Cached values of a and b items // when one of arguments is NULL. int set_compare_func(Item_result_field *owner, Item_result type); @@ -61,10 +60,10 @@ public: /* Allow owner function to use string buffers. */ String value1, value2; - Arg_comparator(): set_null(TRUE), comparators(0), thd(0), + Arg_comparator(): set_null(TRUE), comparators(0), a_cache(0), b_cache(0) {}; Arg_comparator(Item **a1, Item **a2): a(a1), b(a2), set_null(TRUE), - comparators(0), thd(0), a_cache(0), b_cache(0) {}; + comparators(0), a_cache(0), b_cache(0) {}; int set_cmp_func(Item_result_field *owner_arg, Item **a1, Item **a2, @@ -963,15 +962,13 @@ public: class in_datetime :public in_longlong { public: - THD *thd; /* An item used to issue warnings. */ Item *warn_item; /* Cache for the left item. */ Item *lval_cache; in_datetime(Item *warn_item_arg, uint elements) - :in_longlong(elements), thd(current_thd), warn_item(warn_item_arg), - lval_cache(0) {}; + :in_longlong(elements), warn_item(warn_item_arg), lval_cache(0) {}; void set(uint pos,Item *item); uchar *get_value(Item *item); Item* create_item() @@ -1131,14 +1128,13 @@ class cmp_item_datetime :public cmp_item { longlong value; public: - THD *thd; /* Item used for issuing warnings. */ Item *warn_item; /* Cache for the left item. */ Item *lval_cache; cmp_item_datetime(Item *warn_item_arg) - :thd(current_thd), warn_item(warn_item_arg), lval_cache(0) {} + : warn_item(warn_item_arg), lval_cache(0) {} void store_value(Item *item); int cmp(Item *arg); int compare(cmp_item *ci); diff --git a/sql/item_func.cc b/sql/item_func.cc index 07acaacc862..ef164d060d2 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -2865,7 +2865,6 @@ void Item_func_min_max::fix_length_and_dec() decimals=0; max_length=0; maybe_null=0; - thd= current_thd; cmp_type=args[0]->result_type(); for (uint i=0 ; i < arg_count ; i++) @@ -2938,13 +2937,11 @@ bool Item_func_min_max::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) { Item **arg= args + i; bool is_null; - longlong res= get_datetime_value(thd, &arg, 0, compare_as_dates, &is_null); + longlong res= get_datetime_value(0, &arg, 0, compare_as_dates, &is_null); /* Check if we need to stop (because of error or KILL) and stop the loop */ - if (thd->is_error() || args[i]->null_value) - { + if (args[i]->null_value) return (null_value= 1); - } if (i == 0 || (res < min_max ? cmp_sign : -cmp_sign) > 0) min_max= res; @@ -3973,12 +3970,7 @@ longlong Item_master_pos_wait::val_int() else connection_name= thd->variables.default_master_connection; - mysql_mutex_lock(&LOCK_active_mi); - if (master_info_index) // master_info_index is set to NULL on shutdown. - mi= master_info_index->get_master_info(&connection_name, - Sql_condition::WARN_LEVEL_WARN); - mysql_mutex_unlock(&LOCK_active_mi); - if (!mi) + if (!(mi= get_master_info(&connection_name, Sql_condition::WARN_LEVEL_WARN))) goto err; if ((event_count = mi->rli.wait_for_pos(thd, log_name, pos, timeout)) == -2) @@ -3986,6 +3978,7 @@ longlong Item_master_pos_wait::val_int() null_value = 1; event_count=0; } + mi->release(); #endif return event_count; diff --git a/sql/item_func.h b/sql/item_func.h index 3d0cdca4472..0141619dced 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -1077,7 +1077,6 @@ class Item_func_min_max :public Item_func int cmp_sign; /* An item used for issuing warnings while string to DATETIME conversion. */ Item *compare_as_dates; - THD *thd; protected: enum_field_types cached_field_type; public: diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index f3a98785a0b..8cff6a1e6c8 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -894,7 +894,7 @@ void Item_subselect::update_used_tables() if (!forced_const) { recalc_used_tables(parent_select, FALSE); - if (!engine->uncacheable()) + if (!(engine->uncacheable() & ~UNCACHEABLE_EXPLAIN)) { // did all used tables become static? if (!(used_tables_cache & ~engine->upper_select_const_tables())) diff --git a/sql/log.cc b/sql/log.cc index 02e6ef98a65..3f3e0201c0c 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -2835,16 +2835,16 @@ int MYSQL_LOG::generate_new_name(char *new_name, const char *log_name) void MYSQL_QUERY_LOG::reopen_file() { char *save_name; - DBUG_ENTER("MYSQL_LOG::reopen_file"); + + mysql_mutex_lock(&LOCK_log); if (!is_open()) { DBUG_PRINT("info",("log is closed")); + mysql_mutex_unlock(&LOCK_log); DBUG_VOID_RETURN; } - mysql_mutex_lock(&LOCK_log); - save_name= name; name= 0; // Don't free name close(LOG_CLOSE_TO_BE_OPENED); @@ -3003,13 +3003,6 @@ bool MYSQL_QUERY_LOG::write(THD *thd, time_t current_time, DBUG_ENTER("MYSQL_QUERY_LOG::write"); mysql_mutex_lock(&LOCK_log); - - if (!is_open()) - { - mysql_mutex_unlock(&LOCK_log); - DBUG_RETURN(0); - } - if (is_open()) { // Safety agains reopen int tmp_errno= 0; @@ -3233,7 +3226,9 @@ void MYSQL_BIN_LOG::cleanup() } inited= 0; + mysql_mutex_lock(&LOCK_log); close(LOG_CLOSE_INDEX|LOG_CLOSE_STOP_EVENT); + mysql_mutex_unlock(&LOCK_log); delete description_event_for_queue; delete description_event_for_exec; @@ -3390,10 +3385,11 @@ bool MYSQL_BIN_LOG::open(const char *log_name, { File file= -1; xid_count_per_binlog *new_xid_list_entry= NULL, *b; - DBUG_ENTER("MYSQL_BIN_LOG::open"); DBUG_PRINT("enter",("log_type: %d",(int) log_type_arg)); + mysql_mutex_assert_owner(&LOCK_log); + if (!is_relay_log) { if (!binlog_state_recover_done) @@ -4286,7 +4282,7 @@ void MYSQL_BIN_LOG::wait_for_last_checkpoint_event() int MYSQL_BIN_LOG::purge_first_log(Relay_log_info* rli, bool included) { - int error; + int error, errcode; char *to_purge_if_included= NULL; inuse_relaylog *ir; ulonglong log_space_reclaimed= 0; @@ -4357,7 +4353,8 @@ int MYSQL_BIN_LOG::purge_first_log(Relay_log_info* rli, bool included) } /* Store where we are in the new file for the execution thread */ - flush_relay_log_info(rli); + if (flush_relay_log_info(rli)) + error= LOG_INFO_IO; DBUG_EXECUTE_IF("crash_before_purge_logs", DBUG_SUICIDE();); @@ -4373,11 +4370,13 @@ int MYSQL_BIN_LOG::purge_first_log(Relay_log_info* rli, bool included) * Need to update the log pos because purge logs has been called * after fetching initially the log pos at the begining of the method. */ - if((error=find_log_pos(&rli->linfo, rli->event_relay_log_name, 0))) + if ((errcode= find_log_pos(&rli->linfo, rli->event_relay_log_name, 0))) { char buff[22]; + if (!error) + error= errcode; sql_print_error("next log error: %d offset: %s log: %s included: %d", - error, + errcode, llstr(rli->linfo.index_file_offset,buff), rli->group_relay_log_name, included); @@ -4996,21 +4995,21 @@ int MYSQL_BIN_LOG::new_file_impl(bool need_lock) bool delay_close= false; File old_file; LINT_INIT(old_file); - DBUG_ENTER("MYSQL_BIN_LOG::new_file_impl"); + + if (need_lock) + mysql_mutex_lock(&LOCK_log); + mysql_mutex_assert_owner(&LOCK_log); + if (!is_open()) { DBUG_PRINT("info",("log is closed")); + mysql_mutex_unlock(&LOCK_log); DBUG_RETURN(error); } - if (need_lock) - mysql_mutex_lock(&LOCK_log); mysql_mutex_lock(&LOCK_index); - mysql_mutex_assert_owner(&LOCK_log); - mysql_mutex_assert_owner(&LOCK_index); - /* Reuse old name if not binlog and not update log */ new_name_ptr= name; @@ -5143,9 +5142,9 @@ end: new_name_ptr, errno); } + mysql_mutex_unlock(&LOCK_index); if (need_lock) mysql_mutex_unlock(&LOCK_log); - mysql_mutex_unlock(&LOCK_index); DBUG_RETURN(error); } @@ -7974,9 +7973,11 @@ int MYSQL_BIN_LOG::wait_for_update_bin_log(THD* thd, void MYSQL_BIN_LOG::close(uint exiting) { // One can't set log_type here! bool failed_to_save_state= false; - DBUG_ENTER("MYSQL_BIN_LOG::close"); DBUG_PRINT("enter",("exiting: %d", (int) exiting)); + + mysql_mutex_assert_owner(&LOCK_log); + if (log_state == LOG_OPENED) { #ifdef HAVE_REPLICATION diff --git a/sql/log_event.cc b/sql/log_event.cc index d2f3b2c60f6..1ae65a02f15 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -6273,9 +6273,11 @@ bool Rotate_log_event::write(IO_CACHE* file) @retval 0 ok + 1 error */ int Rotate_log_event::do_update_pos(rpl_group_info *rgi) { + int error= 0; Relay_log_info *rli= rgi->rli; DBUG_ENTER("Rotate_log_event::do_update_pos"); #ifndef DBUG_OFF @@ -6327,7 +6329,7 @@ int Rotate_log_event::do_update_pos(rpl_group_info *rgi) (ulong) rli->group_master_log_pos)); mysql_mutex_unlock(&rli->data_lock); rpl_global_gtid_slave_state->record_and_update_gtid(thd, rgi); - flush_relay_log_info(rli); + error= flush_relay_log_info(rli); /* Reset thd->variables.option_bits and sql_mode etc, because this could @@ -6345,8 +6347,7 @@ int Rotate_log_event::do_update_pos(rpl_group_info *rgi) else rgi->inc_event_relay_log_pos(); - - DBUG_RETURN(0); + DBUG_RETURN(error); } @@ -6944,6 +6945,7 @@ Gtid_list_log_event::do_apply_event(rpl_group_info *rgi) rli->abort_slave= true; rli->stop_for_until= true; } + free_root(thd->mem_root, MYF(MY_KEEP_PREALLOC)); return ret; } @@ -8220,6 +8222,7 @@ void Stop_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) int Stop_log_event::do_update_pos(rpl_group_info *rgi) { + int error= 0; Relay_log_info *rli= rgi->rli; DBUG_ENTER("Stop_log_event::do_update_pos"); /* @@ -8235,9 +8238,10 @@ int Stop_log_event::do_update_pos(rpl_group_info *rgi) { rpl_global_gtid_slave_state->record_and_update_gtid(thd, rgi); rli->inc_group_relay_log_pos(0, rgi); - flush_relay_log_info(rli); + if (flush_relay_log_info(rli)) + error= 1; } - DBUG_RETURN(0); + DBUG_RETURN(error); } #endif /* !MYSQL_CLIENT */ @@ -10273,8 +10277,8 @@ int Rows_log_event::do_update_pos(rpl_group_info *rgi) { Relay_log_info *rli= rgi->rli; - DBUG_ENTER("Rows_log_event::do_update_pos"); int error= 0; + DBUG_ENTER("Rows_log_event::do_update_pos"); DBUG_PRINT("info", ("flags: %s", get_flags(STMT_END_F) ? "STMT_END_F " : "")); @@ -10286,7 +10290,7 @@ Rows_log_event::do_update_pos(rpl_group_info *rgi) Step the group log position if we are not in a transaction, otherwise increase the event log position. */ - rli->stmt_done(log_pos, thd, rgi); + error= rli->stmt_done(log_pos, thd, rgi); /* Clear any errors in thd->net.last_err*. It is not known if this is needed or not. It is believed that any errors that may exist in diff --git a/sql/log_event_old.cc b/sql/log_event_old.cc index 22ef970dab9..8bdb81f5283 100644 --- a/sql/log_event_old.cc +++ b/sql/log_event_old.cc @@ -1843,8 +1843,8 @@ int Old_rows_log_event::do_update_pos(rpl_group_info *rgi) { Relay_log_info *rli= rgi->rli; - DBUG_ENTER("Old_rows_log_event::do_update_pos"); int error= 0; + DBUG_ENTER("Old_rows_log_event::do_update_pos"); DBUG_PRINT("info", ("flags: %s", get_flags(STMT_END_F) ? "STMT_END_F " : "")); @@ -1856,7 +1856,7 @@ Old_rows_log_event::do_update_pos(rpl_group_info *rgi) Step the group log position if we are not in a transaction, otherwise increase the event log position. */ - rli->stmt_done(log_pos, thd, rgi); + error= rli->stmt_done(log_pos, thd, rgi); /* Clear any errors in thd->net.last_err*. It is not known if this is needed or not. It is believed that any errors that may exist in diff --git a/sql/mysql_install_db.cc b/sql/mysql_install_db.cc index c23a20ebac9..de799874a8f 100644 --- a/sql/mysql_install_db.cc +++ b/sql/mysql_install_db.cc @@ -386,8 +386,8 @@ static int register_service() CloseServiceHandle(sc_manager); die("CreateService failed (%u)", GetLastError()); } - - SERVICE_DESCRIPTION sd= { "MariaDB database server" }; + char description[] = "MariaDB database server"; + SERVICE_DESCRIPTION sd= { description }; ChangeServiceConfig2(sc_service, SERVICE_CONFIG_DESCRIPTION, &sd); CloseServiceHandle(sc_service); CloseServiceHandle(sc_manager); diff --git a/sql/mysqld.cc b/sql/mysqld.cc index f252d19f50b..349971e86fa 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -21,7 +21,7 @@ #ifndef __WIN__ #include <netdb.h> // getservbyname, servent #endif -#include "sql_parse.h" // test_if_data_home_dir +#include "sql_parse.h" // path_starts_from_data_home_dir #include "sql_cache.h" // query_cache, query_cache_* #include "sql_locale.h" // MY_LOCALES, my_locales, my_locale_by_name #include "sql_show.h" // free_status_vars, add_status_vars, @@ -721,12 +721,15 @@ mysql_mutex_t LOCK_delayed_insert, LOCK_delayed_status, LOCK_delayed_create, LOCK_crypt, LOCK_global_system_variables, - LOCK_user_conn, LOCK_slave_list, LOCK_active_mi, + LOCK_user_conn, LOCK_slave_list, LOCK_connection_count, LOCK_error_messages, LOCK_slave_init; mysql_mutex_t LOCK_stats, LOCK_global_user_client_stats, LOCK_global_table_stats, LOCK_global_index_stats; +/* This protects against changes in master_info_index */ +mysql_mutex_t LOCK_active_mi; + /** The below lock protects access to two global server variables: max_prepared_stmt_count and prepared_stmt_count. These variables @@ -896,7 +899,7 @@ PSI_mutex_key key_BINLOG_LOCK_index, key_BINLOG_LOCK_xid_list, key_LOCK_system_variables_hash, key_LOCK_thd_data, key_LOCK_user_conn, key_LOCK_uuid_short_generator, key_LOG_LOCK_log, key_master_info_data_lock, key_master_info_run_lock, - key_master_info_sleep_lock, + key_master_info_sleep_lock, key_master_info_start_stop_lock, key_mutex_slave_reporting_capability_err_lock, key_relay_log_info_data_lock, key_rpl_group_info_sleep_lock, key_relay_log_info_log_space_lock, key_relay_log_info_run_lock, @@ -972,6 +975,7 @@ static PSI_mutex_info all_server_mutexes[]= { &key_LOCK_uuid_short_generator, "LOCK_uuid_short_generator", PSI_FLAG_GLOBAL}, { &key_LOG_LOCK_log, "LOG::LOCK_log", 0}, { &key_master_info_data_lock, "Master_info::data_lock", 0}, + { &key_master_info_start_stop_lock, "Master_info::start_stop_lock", 0}, { &key_master_info_run_lock, "Master_info::run_lock", 0}, { &key_master_info_sleep_lock, "Master_info::sleep_lock", 0}, { &key_mutex_slave_reporting_capability_err_lock, "Slave_reporting_capability::err_lock", 0}, @@ -1483,7 +1487,7 @@ ulong query_cache_min_res_unit= QUERY_CACHE_MIN_RESULT_DATA_SIZE; Query_cache query_cache; #endif #ifdef HAVE_SMEM -char *shared_memory_base_name= default_shared_memory_base_name; +const char *shared_memory_base_name= default_shared_memory_base_name; my_bool opt_enable_shared_memory; HANDLE smem_event_connect_request= 0; #endif @@ -1725,7 +1729,7 @@ static void close_connections(void) mysql_mutex_unlock(&LOCK_thread_count); // For unlink from list Events::deinit(); - end_slave(); + slave_prepare_for_shutdown(); /* Give threads time to die. @@ -1801,6 +1805,7 @@ static void close_connections(void) DBUG_PRINT("quit",("Unlocking LOCK_thread_count")); mysql_mutex_unlock(&LOCK_thread_count); } + end_slave(); /* All threads has now been aborted */ DBUG_PRINT("quit",("Waiting for threads to die (count=%u)",thread_count)); mysql_mutex_lock(&LOCK_thread_count); @@ -2802,23 +2807,6 @@ void dec_connection_count(THD *thd) /* - Delete THD and decrement thread counters, including thread_running -*/ - -void delete_running_thd(THD *thd) -{ - mysql_mutex_lock(&LOCK_thread_count); - thd->unlink(); - mysql_mutex_unlock(&LOCK_thread_count); - - delete thd; - dec_thread_running(); - thread_safe_decrement32(&thread_count, &thread_count_lock); - signal_thd_deleted(); -} - - -/* Send a signal to unblock close_conneciton() if there is no more threads running with a THD attached @@ -5252,9 +5240,17 @@ static int init_server_components() unireg_abort(1); } - if (opt_bin_log && mysql_bin_log.open(opt_bin_logname, LOG_BIN, 0, - WRITE_CACHE, max_binlog_size, 0, TRUE)) - unireg_abort(1); + if (opt_bin_log) + { + int error; + mysql_mutex_t *log_lock= mysql_bin_log.get_log_lock(); + mysql_mutex_lock(log_lock); + error= mysql_bin_log.open(opt_bin_logname, LOG_BIN, 0, + WRITE_CACHE, max_binlog_size, 0, TRUE); + mysql_mutex_unlock(log_lock); + if (error) + unireg_abort(1); + } #ifdef HAVE_REPLICATION if (opt_bin_log && expire_logs_days) @@ -8025,17 +8021,14 @@ static int show_slave_running(THD *thd, SHOW_VAR *var, char *buff) var->type= SHOW_MY_BOOL; var->value= buff; - mysql_mutex_lock(&LOCK_active_mi); - if (master_info_index) + + if ((mi= get_master_info(&thd->variables.default_master_connection, + Sql_condition::WARN_LEVEL_NOTE))) { - mi= master_info_index-> - get_master_info(&thd->variables.default_master_connection, - Sql_condition::WARN_LEVEL_NOTE); - if (mi) - tmp= (my_bool) (mi->slave_running == MYSQL_SLAVE_RUN_READING && - mi->rli.slave_running != MYSQL_SLAVE_NOT_RUN); + tmp= (my_bool) (mi->slave_running == MYSQL_SLAVE_RUN_READING && + mi->rli.slave_running != MYSQL_SLAVE_NOT_RUN); + mi->release(); } - mysql_mutex_unlock(&LOCK_active_mi); if (mi) *((my_bool *)buff)= tmp; else @@ -8067,38 +8060,26 @@ static int show_slaves_running(THD *thd, SHOW_VAR *var, char *buff) { var->type= SHOW_LONGLONG; var->value= buff; - mysql_mutex_lock(&LOCK_active_mi); - if (master_info_index) - *((longlong *)buff)= master_info_index->any_slave_sql_running(); - else - *((longlong *)buff)= 0; + *((longlong *)buff)= any_slave_sql_running(); - mysql_mutex_unlock(&LOCK_active_mi); return 0; } static int show_slave_received_heartbeats(THD *thd, SHOW_VAR *var, char *buff) { - Master_info *mi= NULL; - longlong tmp; - LINT_INIT(tmp); + Master_info *mi; var->type= SHOW_LONGLONG; var->value= buff; - mysql_mutex_lock(&LOCK_active_mi); - if (master_info_index) + + if ((mi= get_master_info(&thd->variables.default_master_connection, + Sql_condition::WARN_LEVEL_NOTE))) { - mi= master_info_index-> - get_master_info(&thd->variables.default_master_connection, - Sql_condition::WARN_LEVEL_NOTE); - if (mi) - tmp= mi->received_heartbeats; + *((longlong *)buff)= mi->received_heartbeats; + mi->release(); } - mysql_mutex_unlock(&LOCK_active_mi); - if (mi) - *((longlong *)buff)= tmp; else var->type= SHOW_UNDEF; return 0; @@ -8108,23 +8089,16 @@ static int show_slave_received_heartbeats(THD *thd, SHOW_VAR *var, char *buff) static int show_heartbeat_period(THD *thd, SHOW_VAR *var, char *buff) { Master_info *mi= NULL; - float tmp; - LINT_INIT(tmp); var->type= SHOW_CHAR; var->value= buff; - mysql_mutex_lock(&LOCK_active_mi); - if (master_info_index) + + if ((mi= get_master_info(&thd->variables.default_master_connection, + Sql_condition::WARN_LEVEL_NOTE))) { - mi= master_info_index-> - get_master_info(&thd->variables.default_master_connection, - Sql_condition::WARN_LEVEL_NOTE); - if (mi) - tmp= mi->heartbeat_period; + sprintf(buff, "%.3f", mi->heartbeat_period); + mi->release(); } - mysql_mutex_unlock(&LOCK_active_mi); - if (mi) - sprintf(buff, "%.3f", tmp); else var->type= SHOW_UNDEF; return 0; @@ -8965,7 +8939,7 @@ static int mysql_init_variables(void) mysql_home[0]= pidfile_name[0]= log_error_file[0]= 0; #if defined(HAVE_REALPATH) && !defined(HAVE_valgrind) && !defined(HAVE_BROKEN_REALPATH) /* We can only test for sub paths if my_symlink.c is using realpath */ - myisam_test_invalid_symlink= test_if_data_home_dir; + mysys_test_invalid_symlink= path_starts_from_data_home_dir; #endif opt_log= opt_slow_log= 0; opt_bin_log= opt_bin_log_used= 0; diff --git a/sql/mysqld.h b/sql/mysqld.h index c68cab26374..731391490e0 100644 --- a/sql/mysqld.h +++ b/sql/mysqld.h @@ -59,7 +59,6 @@ void kill_mysql(void); void close_connection(THD *thd, uint sql_errno= 0); void handle_connection_in_main_thread(THD *thd); void create_thread_to_handle_connection(THD *thd); -void delete_running_thd(THD *thd); void signal_thd_deleted(); void unlink_thd(THD *thd); bool one_thread_per_connection_end(THD *thd, bool put_in_cache); @@ -118,7 +117,8 @@ extern my_bool sp_automatic_privileges, opt_noacl; extern ulong use_stat_tables; extern my_bool opt_old_style_user_limits, trust_function_creators; extern uint opt_crash_binlog_innodb; -extern char *shared_memory_base_name, *mysqld_unix_port; +extern const char *shared_memory_base_name; +extern char *mysqld_unix_port; extern my_bool opt_enable_shared_memory; extern ulong opt_replicate_events_marked_for_skip; extern char *default_tz_name; @@ -269,7 +269,7 @@ extern PSI_mutex_key key_BINLOG_LOCK_index, key_BINLOG_LOCK_xid_list, key_LOCK_thd_data, key_LOCK_user_conn, key_LOG_LOCK_log, key_master_info_data_lock, key_master_info_run_lock, - key_master_info_sleep_lock, + key_master_info_sleep_lock, key_master_info_start_stop_lock, key_mutex_slave_reporting_capability_err_lock, key_relay_log_info_data_lock, key_relay_log_info_log_space_lock, key_relay_log_info_run_lock, key_rpl_group_info_sleep_lock, diff --git a/sql/nt_servc.cc b/sql/nt_servc.cc index d6a8eac7ed5..e05e43a0a59 100644 --- a/sql/nt_servc.cc +++ b/sql/nt_servc.cc @@ -508,7 +508,7 @@ BOOL NTService::IsService(LPCSTR ServiceName) } /* ------------------------------------------------------------------------ -------------------------------------------------------------------------- */ -BOOL NTService::got_service_option(char **argv, char *service_option) +BOOL NTService::got_service_option(char **argv, const char *service_option) { char *option; for (option= argv[1]; *option; option++) diff --git a/sql/nt_servc.h b/sql/nt_servc.h index 949499d8d7f..6781fe0ddfa 100644 --- a/sql/nt_servc.h +++ b/sql/nt_servc.h @@ -61,7 +61,7 @@ class NTService BOOL SeekStatus(LPCSTR szInternName, int OperationType); BOOL Remove(LPCSTR szInternName); BOOL IsService(LPCSTR ServiceName); - BOOL got_service_option(char **argv, char *service_option); + BOOL got_service_option(char **argv, const char *service_option); BOOL is_super_user(); /* diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 5d6891a1edf..8f9d5abfa4d 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -8067,8 +8067,15 @@ static SEL_TREE *get_mm_tree(RANGE_OPT_PARAM *param,COND *cond) if (cond_func->functype() == Item_func::BETWEEN || cond_func->functype() == Item_func::IN_FUNC) inv= ((Item_func_opt_neg *) cond_func)->negated; - else if (cond_func->select_optimize() == Item_func::OPTIMIZE_NONE) - DBUG_RETURN(0); + else + { + MEM_ROOT *tmp_root= param->mem_root; + param->thd->mem_root= param->old_root; + Item_func::optimize_type opt_res= cond_func->select_optimize(); + param->thd->mem_root= tmp_root; + if (opt_res == Item_func::OPTIMIZE_NONE) + DBUG_RETURN(0); + } param->cond= cond; @@ -9923,6 +9930,13 @@ key_or(RANGE_OPT_PARAM *param, SEL_ARG *key1,SEL_ARG *key2) if (!tmp->next_key_part) { + if (key2->use_count) + { + SEL_ARG *key2_cpy= new SEL_ARG(*key2); + if (key2_cpy) + return 0; + key2= key2_cpy; + } /* tmp->next_key_part is empty: cut the range that is covered by tmp from key2. @@ -9954,13 +9968,6 @@ key_or(RANGE_OPT_PARAM *param, SEL_ARG *key1,SEL_ARG *key2) key2: [---] tmp: [---------] */ - if (key2->use_count) - { - SEL_ARG *key2_cpy= new SEL_ARG(*key2); - if (key2_cpy) - return 0; - key2= key2_cpy; - } key2->copy_max_to_min(tmp); continue; } diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc index a81b091461f..564a108c766 100644 --- a/sql/opt_subselect.cc +++ b/sql/opt_subselect.cc @@ -4044,13 +4044,13 @@ SJ_TMP_TABLE::create_sj_weedout_tmp_table(THD *thd) share->db_plugin= ha_lock_engine(0, TMP_ENGINE_HTON); table->file= get_new_handler(share, &table->mem_root, share->db_type()); - DBUG_ASSERT(uniq_tuple_length_arg <= table->file->max_key_length()); } else { share->db_plugin= ha_lock_engine(0, heap_hton); table->file= get_new_handler(share, &table->mem_root, share->db_type()); + DBUG_ASSERT(uniq_tuple_length_arg <= table->file->max_key_length()); } if (!table->file) goto err; diff --git a/sql/partition_info.cc b/sql/partition_info.cc index 1607b1937df..1eee5df2bc5 100644 --- a/sql/partition_info.cc +++ b/sql/partition_info.cc @@ -27,7 +27,7 @@ #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_parse.h" #include "sql_acl.h" // *_ACL #include "sql_base.h" // fill_record diff --git a/sql/rpl_mi.cc b/sql/rpl_mi.cc index c9a9ba3fc6d..f1508024c22 100644 --- a/sql/rpl_mi.cc +++ b/sql/rpl_mi.cc @@ -40,7 +40,9 @@ Master_info::Master_info(LEX_STRING *connection_name_arg, sync_counter(0), heartbeat_period(0), received_heartbeats(0), master_id(0), prev_master_id(0), using_gtid(USE_GTID_NO), events_queued_since_last_gtid(0), - gtid_reconnect_event_skip_count(0), gtid_event_seen(false) + gtid_reconnect_event_skip_count(0), gtid_event_seen(false), + in_start_all_slaves(0), in_stop_all_slaves(0), + users(0), killed(0) { host[0] = 0; user[0] = 0; password[0] = 0; ssl_ca[0]= 0; ssl_capath[0]= 0; ssl_cert[0]= 0; @@ -78,6 +80,8 @@ Master_info::Master_info(LEX_STRING *connection_name_arg, bzero((char*) &file, sizeof(file)); mysql_mutex_init(key_master_info_run_lock, &run_lock, MY_MUTEX_INIT_FAST); mysql_mutex_init(key_master_info_data_lock, &data_lock, MY_MUTEX_INIT_FAST); + mysql_mutex_init(key_master_info_start_stop_lock, &start_stop_lock, + MY_MUTEX_INIT_SLOW); mysql_mutex_setflags(&run_lock, MYF_NO_DEADLOCK_DETECTION); mysql_mutex_setflags(&data_lock, MYF_NO_DEADLOCK_DETECTION); mysql_mutex_init(key_master_info_sleep_lock, &sleep_lock, MY_MUTEX_INIT_FAST); @@ -87,6 +91,24 @@ Master_info::Master_info(LEX_STRING *connection_name_arg, mysql_cond_init(key_master_info_sleep_cond, &sleep_cond, NULL); } + +/** + Wait until no one is using Master_info +*/ + +void Master_info::wait_until_free() +{ + mysql_mutex_lock(&sleep_lock); + killed= 1; + while (users) + mysql_cond_wait(&sleep_cond, &sleep_lock); + mysql_mutex_unlock(&sleep_lock); +} + +/** + Delete master_info +*/ + Master_info::~Master_info() { #ifdef WITH_WSREP @@ -103,6 +125,7 @@ Master_info::~Master_info() mysql_mutex_destroy(&run_lock); mysql_mutex_destroy(&data_lock); mysql_mutex_destroy(&sleep_lock); + mysql_mutex_destroy(&start_stop_lock); mysql_cond_destroy(&data_cond); mysql_cond_destroy(&start_cond); mysql_cond_destroy(&stop_cond); @@ -713,12 +736,28 @@ uchar *get_key_master_info(Master_info *mi, size_t *length, return (uchar*) mi->cmp_connection_name.str; } +/* + Delete a master info + + Called from my_hash_delete(&master_info_hash) + Stops associated slave threads and frees master_info +*/ + void free_key_master_info(Master_info *mi) { DBUG_ENTER("free_key_master_info"); + mysql_mutex_unlock(&LOCK_active_mi); + + /* Ensure that we are not in reset_slave while this is done */ + mi->lock_slave_threads(); terminate_slave_threads(mi,SLAVE_FORCE_ALL); + /* We use 2 here instead of 1 just to make it easier when debugging */ + mi->killed= 2; end_master_info(mi); + mi->unlock_slave_threads(); delete mi; + + mysql_mutex_lock(&LOCK_active_mi); DBUG_VOID_RETURN; } @@ -874,9 +913,28 @@ Master_info_index::Master_info_index() index_file.file= -1; } + +/** + Free all connection threads + + This is done during early stages of shutdown + to give connection threads and slave threads time + to die before ~Master_info_index is called +*/ + +void Master_info_index::free_connections() +{ + mysql_mutex_assert_owner(&LOCK_active_mi); + my_hash_reset(&master_info_hash); +} + + +/** + Free all connection threads and free structures +*/ + Master_info_index::~Master_info_index() { - /* This will close connection for all objects in the cache */ my_hash_free(&master_info_hash); end_io_cache(&index_file); if (index_file.file >= 0) @@ -899,7 +957,6 @@ bool Master_info_index::init_all_master_info() File index_file_nr; DBUG_ENTER("init_all_master_info"); - mysql_mutex_assert_owner(&LOCK_active_mi); DBUG_ASSERT(master_info_index); if ((index_file_nr= my_open(index_file_name, @@ -947,7 +1004,6 @@ bool Master_info_index::init_all_master_info() DBUG_RETURN(1); } - lock_slave_threads(mi); init_thread_mask(&thread_mask,mi,0 /*not inverse*/); create_logfile_name_with_suffix(buf_master_info_file, @@ -962,6 +1018,7 @@ bool Master_info_index::init_all_master_info() sql_print_information("Reading Master_info: '%s' Relay_info:'%s'", buf_master_info_file, buf_relay_log_info_file); + mi->lock_slave_threads(); if (init_master_info(mi, buf_master_info_file, buf_relay_log_info_file, 0, thread_mask)) { @@ -975,14 +1032,15 @@ bool Master_info_index::init_all_master_info() if (master_info_index->add_master_info(mi, FALSE)) DBUG_RETURN(1); succ_num++; - unlock_slave_threads(mi); + mi->unlock_slave_threads(); } else { /* Master_info already in HASH */ sql_print_error(ER(ER_CONNECTION_ALREADY_EXISTS), + (int) connection_name.length, connection_name.str, (int) connection_name.length, connection_name.str); - unlock_slave_threads(mi); + mi->unlock_slave_threads(); delete mi; } continue; @@ -998,8 +1056,9 @@ bool Master_info_index::init_all_master_info() { /* Master_info was already registered */ sql_print_error(ER(ER_CONNECTION_ALREADY_EXISTS), + (int) connection_name.length, connection_name.str, (int) connection_name.length, connection_name.str); - unlock_slave_threads(mi); + mi->unlock_slave_threads(); delete mi; continue; } @@ -1008,7 +1067,6 @@ bool Master_info_index::init_all_master_info() if (master_info_index->add_master_info(mi, FALSE)) DBUG_RETURN(1); succ_num++; - unlock_slave_threads(mi); if (!opt_skip_slave_start) { @@ -1029,6 +1087,7 @@ bool Master_info_index::init_all_master_info() (int) connection_name.length, connection_name.str); } + mi->unlock_slave_threads(); } } @@ -1080,6 +1139,71 @@ bool Master_info_index::write_master_name_to_index_file(LEX_STRING *name, /** + Get Master_info for a connection and lock the object from deletion + + @param + connection_name Connection name + warning WARN_LEVEL_NOTE -> Don't print anything + WARN_LEVEL_WARN -> Issue warning if not exists + WARN_LEVEL_ERROR-> Issue error if not exists +*/ + +Master_info *get_master_info(LEX_STRING *connection_name, + Sql_condition::enum_warning_level warning) +{ + Master_info *mi; + DBUG_ENTER("get_master_info"); + + /* Protect against inserts into hash */ + mysql_mutex_lock(&LOCK_active_mi); + /* + The following can only be true during shutdown when slave has been killed + but some other threads are still trying to access slave statistics. + */ + if (unlikely(!master_info_index)) + { + if (warning != Sql_condition::WARN_LEVEL_NOTE) + my_error(WARN_NO_MASTER_INFO, + MYF(warning == Sql_condition::WARN_LEVEL_WARN ? + ME_JUST_WARNING : 0), + (int) connection_name->length, connection_name->str); + mysql_mutex_unlock(&LOCK_active_mi); + DBUG_RETURN(0); + } + if ((mi= master_info_index->get_master_info(connection_name, warning))) + { + /* + We have to use sleep_lock here. If we would use LOCK_active_mi + then we would take locks in wrong order in Master_info::release() + */ + mysql_mutex_lock(&mi->sleep_lock); + mi->users++; + DBUG_PRINT("info",("users: %d", mi->users)); + mysql_mutex_unlock(&mi->sleep_lock); + } + mysql_mutex_unlock(&LOCK_active_mi); + DBUG_RETURN(mi); +} + + +/** + Release master info. + Signals ~Master_info that it's now safe to delete it +*/ + +void Master_info::release() +{ + mysql_mutex_lock(&sleep_lock); + if (!--users && killed) + { + /* Signal ~Master_info that it's ok to now free it */ + mysql_cond_signal(&sleep_cond); + } + mysql_mutex_unlock(&sleep_lock); +} + + +/** Get Master_info for a connection @param @@ -1101,8 +1225,6 @@ Master_info_index::get_master_info(LEX_STRING *connection_name, ("connection_name: '%.*s'", (int) connection_name->length, connection_name->str)); - mysql_mutex_assert_owner(&LOCK_active_mi); - /* Make name lower case for comparison */ res= strmake(buff, connection_name->str, connection_name->length); my_casedn_str(system_charset_info, buff); @@ -1168,7 +1290,12 @@ bool Master_info_index::check_duplicate_master_info(LEX_STRING *name_arg, /* Add a Master_info class to Hash Table */ bool Master_info_index::add_master_info(Master_info *mi, bool write_to_file) { - if (!my_hash_insert(&master_info_hash, (uchar*) mi)) + /* + We have to protect against shutdown to ensure we are not calling + my_hash_insert() while my_hash_free() is in progress + */ + if (unlikely(shutdown_in_progress) || + !my_hash_insert(&master_info_hash, (uchar*) mi)) { if (global_system_variables.log_warnings > 1) sql_print_information("Added new Master_info '%.*s' to hash table", @@ -1194,105 +1321,131 @@ bool Master_info_index::add_master_info(Master_info *mi, bool write_to_file) atomic */ -bool Master_info_index::remove_master_info(LEX_STRING *name) +bool Master_info_index::remove_master_info(Master_info *mi) { - Master_info* mi; DBUG_ENTER("remove_master_info"); + mysql_mutex_assert_owner(&LOCK_active_mi); - 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)) { - // Delete Master_info and rewrite others to file - if (!my_hash_delete(&master_info_hash, (uchar*) mi)) + File index_file_nr; + + // Close IO_CACHE and FILE handler fisrt + end_io_cache(&index_file); + my_close(index_file.file, MYF(MY_WME)); + + // Reopen File and truncate it + if ((index_file_nr= my_open(index_file_name, + O_RDWR | O_CREAT | O_TRUNC | O_BINARY , + MYF(MY_WME))) < 0 || + init_io_cache(&index_file, index_file_nr, + IO_SIZE, WRITE_CACHE, + my_seek(index_file_nr,0L,MY_SEEK_END,MYF(0)), + 0, MYF(MY_WME | MY_WAIT_IF_FULL))) { - File index_file_nr; - - // Close IO_CACHE and FILE handler fisrt - end_io_cache(&index_file); - my_close(index_file.file, MYF(MY_WME)); - - // Reopen File and truncate it - if ((index_file_nr= my_open(index_file_name, - O_RDWR | O_CREAT | O_TRUNC | O_BINARY , - MYF(MY_WME))) < 0 || - init_io_cache(&index_file, index_file_nr, - IO_SIZE, WRITE_CACHE, - my_seek(index_file_nr,0L,MY_SEEK_END,MYF(0)), - 0, MYF(MY_WME | MY_WAIT_IF_FULL))) - { - int error= my_errno; - if (index_file_nr >= 0) - my_close(index_file_nr,MYF(0)); - - sql_print_error("Create of Master Info Index file '%s' failed with " - "error: %M", - index_file_name, error); - DBUG_RETURN(TRUE); - } + int error= my_errno; + if (index_file_nr >= 0) + my_close(index_file_nr,MYF(0)); - // Rewrite Master_info.index - for (uint i= 0; i< master_info_hash.records; ++i) - { - Master_info *tmp_mi; - tmp_mi= (Master_info *) my_hash_element(&master_info_hash, i); - write_master_name_to_index_file(&tmp_mi->connection_name, 0); - } - my_sync(index_file_nr, MYF(MY_WME)); + sql_print_error("Create of Master Info Index file '%s' failed with " + "error: %M", + index_file_name, error); + DBUG_RETURN(TRUE); + } + + // Rewrite Master_info.index + for (uint i= 0; i< master_info_hash.records; ++i) + { + Master_info *tmp_mi; + tmp_mi= (Master_info *) my_hash_element(&master_info_hash, i); + write_master_name_to_index_file(&tmp_mi->connection_name, 0); } + if (my_sync(index_file_nr, MYF(MY_WME))) + DBUG_RETURN(TRUE); } DBUG_RETURN(FALSE); } /** - Master_info_index::give_error_if_slave_running() + give_error_if_slave_running() + + @param + already_locked 0 if we need to lock, 1 if we have LOCK_active_mi_locked @return TRUE If some slave is running. An error is printed FALSE No slave is running */ -bool Master_info_index::give_error_if_slave_running() +bool give_error_if_slave_running(bool already_locked) { + bool ret= 0; DBUG_ENTER("give_error_if_slave_running"); - mysql_mutex_assert_owner(&LOCK_active_mi); - for (uint i= 0; i< master_info_hash.records; ++i) + if (!already_locked) + mysql_mutex_lock(&LOCK_active_mi); + if (!master_info_index) { - Master_info *mi; - mi= (Master_info *) my_hash_element(&master_info_hash, i); - if (mi->rli.slave_running != MYSQL_SLAVE_NOT_RUN) + my_error(ER_SERVER_SHUTDOWN, MYF(0)); + ret= 1; + } + else + { + HASH *hash= &master_info_index->master_info_hash; + for (uint i= 0; i< hash->records; ++i) { - my_error(ER_SLAVE_MUST_STOP, MYF(0), (int) mi->connection_name.length, - mi->connection_name.str); - DBUG_RETURN(TRUE); + Master_info *mi; + mi= (Master_info *) my_hash_element(hash, i); + if (mi->rli.slave_running != MYSQL_SLAVE_NOT_RUN) + { + my_error(ER_SLAVE_MUST_STOP, MYF(0), (int) mi->connection_name.length, + mi->connection_name.str); + ret= 1; + break; + } } } - DBUG_RETURN(FALSE); + if (!already_locked) + mysql_mutex_unlock(&LOCK_active_mi); + DBUG_RETURN(ret); } /** - Master_info_index::any_slave_sql_running() - - The LOCK_active_mi must be held while calling this function. + any_slave_sql_running() @return 0 No Slave SQL thread is running # Number of slave SQL thread running + + Note that during shutdown we return 1. This is needed to ensure we + don't try to resize thread pool during shutdown as during shutdown + master_info_hash may be freeing the hash and during that time + hash entries can't be accessed. */ -uint Master_info_index::any_slave_sql_running() +uint any_slave_sql_running() { uint count= 0; + HASH *hash; DBUG_ENTER("any_slave_sql_running"); - mysql_mutex_assert_owner(&LOCK_active_mi); - for (uint i= 0; i< master_info_hash.records; ++i) + mysql_mutex_lock(&LOCK_active_mi); + if (unlikely(shutdown_in_progress || !master_info_index)) + { + mysql_mutex_unlock(&LOCK_active_mi); + DBUG_RETURN(1); + } + hash= &master_info_index->master_info_hash; + for (uint i= 0; i< hash->records; ++i) { - Master_info *mi= (Master_info *)my_hash_element(&master_info_hash, i); + Master_info *mi= (Master_info *)my_hash_element(hash, i); if (mi->rli.slave_running != MYSQL_SLAVE_NOT_RUN) count++; } + mysql_mutex_unlock(&LOCK_active_mi); DBUG_RETURN(count); } @@ -1305,15 +1458,25 @@ uint Master_info_index::any_slave_sql_running() @return TRUE Error FALSE Everything ok. + + This code is written so that we don't keep LOCK_active_mi active + while we are starting a slave. */ bool Master_info_index::start_all_slaves(THD *thd) { bool result= FALSE; - DBUG_ENTER("warn_if_slave_running"); + DBUG_ENTER("start_all_slaves"); mysql_mutex_assert_owner(&LOCK_active_mi); - for (uint i= 0; i< master_info_hash.records; ++i) + for (uint i= 0; i< master_info_hash.records; i++) + { + Master_info *mi; + mi= (Master_info *) my_hash_element(&master_info_hash, i); + mi->in_start_all_slaves= 0; + } + + for (uint i= 0; i< master_info_hash.records; ) { int error; Master_info *mi; @@ -1323,25 +1486,40 @@ bool Master_info_index::start_all_slaves(THD *thd) Try to start all slaves that are configured (host is defined) and are not already running */ - if ((mi->slave_running == MYSQL_SLAVE_NOT_RUN || - !mi->rli.slave_running) && *mi->host) + if (!((mi->slave_running == MYSQL_SLAVE_NOT_RUN || + !mi->rli.slave_running) && *mi->host) || + mi->in_start_all_slaves) { - if ((error= start_slave(thd, mi, 1))) - { - my_error(ER_CANT_START_STOP_SLAVE, MYF(0), - "START", - (int) mi->connection_name.length, - mi->connection_name.str); - result= 1; - if (error < 0) // fatal error - break; - } - else - 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); + i++; + continue; } + mi->in_start_all_slaves= 1; + + mysql_mutex_lock(&mi->sleep_lock); + mi->users++; // Mark used + mysql_mutex_unlock(&mi->sleep_lock); + mysql_mutex_unlock(&LOCK_active_mi); + error= start_slave(thd, mi, 1); + mi->release(); + mysql_mutex_lock(&LOCK_active_mi); + if (error) + { + my_error(ER_CANT_START_STOP_SLAVE, MYF(0), + "START", + (int) mi->connection_name.length, + mi->connection_name.str); + result= 1; + if (error < 0) // fatal error + break; + } + else + 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); + /* Restart from first element as master_info_hash may have changed */ + i= 0; + continue; } DBUG_RETURN(result); } @@ -1355,38 +1533,63 @@ bool Master_info_index::start_all_slaves(THD *thd) @return TRUE Error FALSE Everything ok. + + This code is written so that we don't keep LOCK_active_mi active + while we are stopping a slave. */ bool Master_info_index::stop_all_slaves(THD *thd) { bool result= FALSE; - DBUG_ENTER("warn_if_slave_running"); + DBUG_ENTER("stop_all_slaves"); mysql_mutex_assert_owner(&LOCK_active_mi); - for (uint i= 0; i< master_info_hash.records; ++i) + for (uint i= 0; i< master_info_hash.records; i++) + { + Master_info *mi; + mi= (Master_info *) my_hash_element(&master_info_hash, i); + mi->in_stop_all_slaves= 0; + } + + for (uint i= 0; i< master_info_hash.records ;) { int error; Master_info *mi; mi= (Master_info *) my_hash_element(&master_info_hash, i); - if ((mi->slave_running != MYSQL_SLAVE_NOT_RUN || - mi->rli.slave_running)) + if (!(mi->slave_running != MYSQL_SLAVE_NOT_RUN || + mi->rli.slave_running) || + mi->in_stop_all_slaves) { - if ((error= stop_slave(thd, mi, 1))) - { - my_error(ER_CANT_START_STOP_SLAVE, MYF(0), - "STOP", - (int) mi->connection_name.length, - mi->connection_name.str); - result= 1; - if (error < 0) // Fatal error - break; - } - else - 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); + i++; + continue; } + mi->in_stop_all_slaves= 1; // Protection for loops + + mysql_mutex_lock(&mi->sleep_lock); + mi->users++; // Mark used + mysql_mutex_unlock(&mi->sleep_lock); + mysql_mutex_unlock(&LOCK_active_mi); + error= stop_slave(thd, mi, 1); + mi->release(); + mysql_mutex_lock(&LOCK_active_mi); + if (error) + { + my_error(ER_CANT_START_STOP_SLAVE, MYF(0), + "STOP", + (int) mi->connection_name.length, + mi->connection_name.str); + result= 1; + if (error < 0) // Fatal error + break; + } + else + 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); + /* Restart from first element as master_info_hash may have changed */ + i= 0; + continue; } DBUG_RETURN(result); } diff --git a/sql/rpl_mi.h b/sql/rpl_mi.h index e58df0150f5..5c42378ca2c 100644 --- a/sql/rpl_mi.h +++ b/sql/rpl_mi.h @@ -79,6 +79,10 @@ class Master_info : public Slave_reporting_capability { return opt_slave_parallel_threads > 0; } + void release(); + void wait_until_free(); + void lock_slave_threads(); + void unlock_slave_threads(); /* the variables below are needed because we can change masters on the fly */ char master_log_name[FN_REFLEN+6]; /* Room for multi-*/ @@ -97,7 +101,7 @@ class Master_info : public Slave_reporting_capability File fd; // we keep the file open, so we need to remember the file pointer IO_CACHE file; - mysql_mutex_t data_lock, run_lock, sleep_lock; + mysql_mutex_t data_lock, run_lock, sleep_lock, start_stop_lock; mysql_cond_t data_cond, start_cond, stop_cond, sleep_cond; THD *io_thd; MYSQL* mysql; @@ -182,7 +186,11 @@ class Master_info : public Slave_reporting_capability uint64 gtid_reconnect_event_skip_count; /* gtid_event_seen is false until we receive first GTID event from master. */ bool gtid_event_seen; + bool in_start_all_slaves, in_stop_all_slaves; + uint users; /* Active user for object */ + uint killed; }; + int init_master_info(Master_info* mi, const char* master_info_fname, const char* slave_info_fname, bool abort_if_no_master_info_file, @@ -218,13 +226,12 @@ public: bool check_duplicate_master_info(LEX_STRING *connection_name, const char *host, uint port); bool add_master_info(Master_info *mi, bool write_to_file); - bool remove_master_info(LEX_STRING *connection_name); + bool remove_master_info(Master_info *mi); Master_info *get_master_info(LEX_STRING *connection_name, Sql_condition::enum_warning_level warning); - bool give_error_if_slave_running(); - uint any_slave_sql_running(); bool start_all_slaves(THD *thd); bool stop_all_slaves(THD *thd); + void free_connections(); }; @@ -237,6 +244,8 @@ public: }; +Master_info *get_master_info(LEX_STRING *connection_name, + Sql_condition::enum_warning_level warning); bool check_master_connection_name(LEX_STRING *name); void create_logfile_name_with_suffix(char *res_file_name, size_t length, const char *info_file, @@ -246,7 +255,8 @@ void create_logfile_name_with_suffix(char *res_file_name, size_t length, uchar *get_key_master_info(Master_info *mi, size_t *length, my_bool not_used __attribute__((unused))); void free_key_master_info(Master_info *mi); - +uint any_slave_sql_running(); +bool give_error_if_slave_running(bool already_lock); #endif /* HAVE_REPLICATION */ #endif /* RPL_MI_H */ diff --git a/sql/rpl_parallel.cc b/sql/rpl_parallel.cc index 23f61a82a90..44f60e4482b 100644 --- a/sql/rpl_parallel.cc +++ b/sql/rpl_parallel.cc @@ -1253,39 +1253,16 @@ handle_rpl_parallel_thread(void *arg) */ rpt->batch_free(); - for (;;) + if ((events= rpt->event_queue) != NULL) { - if ((events= rpt->event_queue) != NULL) - { - /* - Take next group of events from the replication pool. - This is faster than having to wakeup the pool manager thread to give - us a new event. - */ - rpt->dequeue1(events); - mysql_mutex_unlock(&rpt->LOCK_rpl_thread); - goto more_events; - } - if (!rpt->pause_for_ftwrl || - (in_event_group && !group_rgi->parallel_entry->force_abort)) - break; /* - We are currently in the delicate process of pausing parallel - replication while FLUSH TABLES WITH READ LOCK is starting. We must - not de-allocate the thread (setting rpt->current_owner= NULL) until - rpl_unpause_after_ftwrl() has woken us up. + Take next group of events from the replication pool. + This is faster than having to wakeup the pool manager thread to give + us a new event. */ - mysql_mutex_lock(&rpt->current_entry->LOCK_parallel_entry); + rpt->dequeue1(events); mysql_mutex_unlock(&rpt->LOCK_rpl_thread); - if (rpt->pause_for_ftwrl) - mysql_cond_wait(&rpt->current_entry->COND_parallel_entry, - &rpt->current_entry->LOCK_parallel_entry); - mysql_mutex_unlock(&rpt->current_entry->LOCK_parallel_entry); - mysql_mutex_lock(&rpt->LOCK_rpl_thread); - /* - Now loop to check again for more events available, since we released - and re-aquired the LOCK_rpl_thread mutex. - */ + goto more_events; } rpt->inuse_relaylog_refcount_update(); @@ -1312,11 +1289,36 @@ handle_rpl_parallel_thread(void *arg) } if (!in_event_group) { + /* If we are in a FLUSH TABLES FOR READ LOCK, wait for it */ + while (rpt->current_entry && rpt->pause_for_ftwrl) + { + /* + We are currently in the delicate process of pausing parallel + replication while FLUSH TABLES WITH READ LOCK is starting. We must + not de-allocate the thread (setting rpt->current_owner= NULL) until + rpl_unpause_after_ftwrl() has woken us up. + */ + rpl_parallel_entry *e= rpt->current_entry; + /* + Ensure that we will unblock rpl_pause_for_ftrwl() + e->pause_sub_id may be LONGLONG_MAX if rpt->current_entry has changed + */ + DBUG_ASSERT(e->pause_sub_id == (uint64)ULONGLONG_MAX || + e->last_committed_sub_id >= e->pause_sub_id); + mysql_mutex_lock(&e->LOCK_parallel_entry); + mysql_mutex_unlock(&rpt->LOCK_rpl_thread); + if (rpt->pause_for_ftwrl) + mysql_cond_wait(&e->COND_parallel_entry, &e->LOCK_parallel_entry); + mysql_mutex_unlock(&e->LOCK_parallel_entry); + mysql_mutex_lock(&rpt->LOCK_rpl_thread); + } + rpt->current_owner= NULL; /* Tell wait_for_done() that we are done, if it is waiting. */ if (likely(rpt->current_entry) && unlikely(rpt->current_entry->force_abort)) mysql_cond_broadcast(&rpt->COND_rpl_thread_stop); + rpt->current_entry= NULL; if (!rpt->stop) rpt->pool->release_thread(rpt); @@ -1355,10 +1357,24 @@ dealloc_gco(group_commit_orderer *gco) my_free(gco); } +/** + Change thread count for global parallel worker threads + + @param pool parallel thread pool + @param new_count Number of threads to be in pool. 0 in shutdown + @param force Force thread count to new_count even if slave + threads are running + + By default we don't resize pool of there are running threads. + However during shutdown we will always do it. + This is needed as any_slave_sql_running() returns 1 during shutdown + as we don't want to access master_info while + Master_info_index::free_connections are running. +*/ static int rpl_parallel_change_thread_count(rpl_parallel_thread_pool *pool, - uint32 new_count) + uint32 new_count, bool force) { uint32 i; rpl_parallel_thread **new_list= NULL; @@ -1369,6 +1385,28 @@ rpl_parallel_change_thread_count(rpl_parallel_thread_pool *pool, if ((res= pool_mark_busy(pool, current_thd))) return res; + /* Protect against parallel pool resizes */ + if (pool->count == new_count) + { + pool_mark_not_busy(pool); + return 0; + } + + /* + If we are about to delete pool, do an extra check that there are no new + slave threads running since we marked pool busy + */ + if (!new_count && !force) + { + if (any_slave_sql_running()) + { + DBUG_PRINT("warning", + ("SQL threads running while trying to reset parallel pool")); + pool_mark_not_busy(pool); + return 0; // Ok to not resize pool + } + } + /* Allocate the new list of threads up-front. That way, if we fail half-way, we only need to free whatever we managed @@ -1382,7 +1420,7 @@ rpl_parallel_change_thread_count(rpl_parallel_thread_pool *pool, { my_error(ER_OUTOFMEMORY, MYF(0), (int(new_count*sizeof(*new_list) + new_count*sizeof(*rpt_array)))); - goto err;; + goto err; } for (i= 0; i < new_count; ++i) @@ -1503,12 +1541,26 @@ err: return 1; } +/* + Deactivate the parallel replication thread pool, if there are now no more + SQL threads running. +*/ + +int rpl_parallel_resize_pool_if_no_slaves(void) +{ + /* master_info_index is set to NULL on shutdown */ + if (opt_slave_parallel_threads > 0 && !any_slave_sql_running()) + return rpl_parallel_inactivate_pool(&global_rpl_thread_pool); + return 0; +} + int rpl_parallel_activate_pool(rpl_parallel_thread_pool *pool) { if (!pool->count) - return rpl_parallel_change_thread_count(pool, opt_slave_parallel_threads); + return rpl_parallel_change_thread_count(pool, opt_slave_parallel_threads, + 0); return 0; } @@ -1516,7 +1568,7 @@ rpl_parallel_activate_pool(rpl_parallel_thread_pool *pool) int rpl_parallel_inactivate_pool(rpl_parallel_thread_pool *pool) { - return rpl_parallel_change_thread_count(pool, 0); + return rpl_parallel_change_thread_count(pool, 0, 0); } @@ -1795,7 +1847,7 @@ rpl_parallel_thread_pool::destroy() { if (!inited) return; - rpl_parallel_change_thread_count(this, 0); + rpl_parallel_change_thread_count(this, 0, 1); mysql_mutex_destroy(&LOCK_rpl_thread_pool); mysql_cond_destroy(&COND_rpl_thread_pool); inited= false; @@ -1814,6 +1866,7 @@ rpl_parallel_thread_pool::get_thread(rpl_parallel_thread **owner, { rpl_parallel_thread *rpt; + DBUG_ASSERT(count > 0); mysql_mutex_lock(&LOCK_rpl_thread_pool); while (unlikely(busy) || !(rpt= free_list)) mysql_cond_wait(&COND_rpl_thread_pool, &LOCK_rpl_thread_pool); @@ -2042,6 +2095,11 @@ rpl_parallel::find(uint32 domain_id) return e; } +/** + Wait until all sql worker threads has stopped processing + + This is called when sql thread has been killed/stopped +*/ void rpl_parallel::wait_for_done(THD *thd, Relay_log_info *rli) diff --git a/sql/rpl_parallel.h b/sql/rpl_parallel.h index a02c1af3b3e..0d7cd4f2e9b 100644 --- a/sql/rpl_parallel.h +++ b/sql/rpl_parallel.h @@ -342,6 +342,7 @@ struct rpl_parallel { extern struct rpl_parallel_thread_pool global_rpl_thread_pool; +extern int rpl_parallel_resize_pool_if_no_slaves(void); extern int rpl_parallel_activate_pool(rpl_parallel_thread_pool *pool); extern int rpl_parallel_inactivate_pool(rpl_parallel_thread_pool *pool); extern bool process_gtid_for_restart_pos(Relay_log_info *rli, rpl_gtid *gtid); diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc index c570105cdf6..928fbd3d7c1 100644 --- a/sql/rpl_rli.cc +++ b/sql/rpl_rli.cc @@ -208,6 +208,7 @@ a file name for --relay-log-index option", opt_relaylog_index_name); Master_info* mi= rli->mi; char buf_relay_logname[FN_REFLEN], buf_relaylog_index_name_buff[FN_REFLEN]; char *buf_relaylog_index_name= opt_relaylog_index_name; + mysql_mutex_t *log_lock; create_logfile_name_with_suffix(buf_relay_logname, sizeof(buf_relay_logname), @@ -227,14 +228,18 @@ a file name for --relay-log-index option", opt_relaylog_index_name); note, that if open() fails, we'll still have index file open but a destructor will take care of that */ + log_lock= rli->relay_log.get_log_lock(); + mysql_mutex_lock(log_lock); if (rli->relay_log.open_index_file(buf_relaylog_index_name, ln, TRUE) || rli->relay_log.open(ln, LOG_BIN, 0, SEQ_READ_APPEND, mi->rli.max_relay_log_size, 1, TRUE)) { + mysql_mutex_unlock(log_lock); mysql_mutex_unlock(&rli->data_lock); sql_print_error("Failed when trying to open logs for '%s' in init_relay_log_info(). Error: %M", ln, my_errno); DBUG_RETURN(1); } + mysql_mutex_unlock(log_lock); } /* if file does not exist */ @@ -432,7 +437,7 @@ Failed to open the existing relay log info file '%s' (errno %d)", } rli->inited= 1; mysql_mutex_unlock(&rli->data_lock); - DBUG_RETURN(error); + DBUG_RETURN(0); err: sql_print_error("%s", msg); @@ -1304,9 +1309,10 @@ bool Relay_log_info::is_until_satisfied(THD *thd, Log_event *ev) } -void Relay_log_info::stmt_done(my_off_t event_master_log_pos, THD *thd, +bool Relay_log_info::stmt_done(my_off_t event_master_log_pos, THD *thd, rpl_group_info *rgi) { + int error= 0; DBUG_ENTER("Relay_log_info::stmt_done"); DBUG_ASSERT(rgi->rli == this); @@ -1358,10 +1364,11 @@ void Relay_log_info::stmt_done(my_off_t event_master_log_pos, THD *thd, } DBUG_EXECUTE_IF("inject_crash_before_flush_rli", DBUG_SUICIDE();); if (mi->using_gtid == Master_info::USE_GTID_NO) - flush_relay_log_info(this); + if (flush_relay_log_info(this)) + error= 1; DBUG_EXECUTE_IF("inject_crash_after_flush_rli", DBUG_SUICIDE();); } - DBUG_VOID_RETURN; + DBUG_RETURN(error); } diff --git a/sql/rpl_rli.h b/sql/rpl_rli.h index efcec83b880..7fc41786957 100644 --- a/sql/rpl_rli.h +++ b/sql/rpl_rli.h @@ -416,7 +416,7 @@ public: relay log info and used to produce information for <code>SHOW SLAVE STATUS</code>. */ - void stmt_done(my_off_t event_log_pos, THD *thd, rpl_group_info *rgi); + bool stmt_done(my_off_t event_log_pos, THD *thd, rpl_group_info *rgi); int alloc_inuse_relaylog(const char *name); void free_inuse_relaylog(inuse_relaylog *ir); void reset_inuse_relaylog(); diff --git a/sql/slave.cc b/sql/slave.cc index ae335a24811..694e9a2e673 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -231,16 +231,14 @@ void init_thread_mask(int* mask,Master_info* mi,bool inverse) /* - lock_slave_threads() + lock_slave_threads() against other threads doing STOP, START or RESET SLAVE + */ -void lock_slave_threads(Master_info* mi) +void Master_info::lock_slave_threads() { DBUG_ENTER("lock_slave_threads"); - - //TODO: see if we can do this without dual mutex - mysql_mutex_lock(&mi->run_lock); - mysql_mutex_lock(&mi->rli.run_lock); + mysql_mutex_lock(&start_stop_lock); DBUG_VOID_RETURN; } @@ -249,13 +247,10 @@ void lock_slave_threads(Master_info* mi) unlock_slave_threads() */ -void unlock_slave_threads(Master_info* mi) +void Master_info::unlock_slave_threads() { DBUG_ENTER("unlock_slave_threads"); - - //TODO: see if we can do this without dual mutex - mysql_mutex_unlock(&mi->rli.run_lock); - mysql_mutex_unlock(&mi->run_lock); + mysql_mutex_unlock(&start_stop_lock); DBUG_VOID_RETURN; } @@ -377,7 +372,6 @@ int init_slave() accepted. However bootstrap may conflict with us if it does START SLAVE. So it's safer to take the lock. */ - mysql_mutex_lock(&LOCK_active_mi); if (pthread_key_create(&RPL_MASTER_INFO, NULL)) goto err; @@ -386,7 +380,6 @@ int init_slave() if (!master_info_index || master_info_index->init_all_master_info()) { sql_print_error("Failed to initialize multi master structures"); - mysql_mutex_unlock(&LOCK_active_mi); DBUG_RETURN(1); } if (!(active_mi= new Master_info(&default_master_connection_name, @@ -444,7 +437,6 @@ int init_slave() } end: - mysql_mutex_unlock(&LOCK_active_mi); DBUG_RETURN(error); err: @@ -617,6 +609,7 @@ int terminate_slave_threads(Master_info* mi,int thread_mask,bool skip_lock) if (!mi->inited) DBUG_RETURN(0); /* successfully do nothing */ int error,force_all = (thread_mask & SLAVE_FORCE_ALL); + int retval= 0; mysql_mutex_t *sql_lock = &mi->rli.run_lock, *io_lock = &mi->run_lock; mysql_mutex_t *log_lock= mi->rli.relay_log.get_log_lock(); @@ -636,24 +629,19 @@ int terminate_slave_threads(Master_info* mi,int thread_mask,bool skip_lock) skip_lock)) && !force_all) DBUG_RETURN(error); + retval= error; mysql_mutex_lock(log_lock); DBUG_PRINT("info",("Flushing relay-log info file.")); if (current_thd) THD_STAGE_INFO(current_thd, stage_flushing_relay_log_info_file); - if (flush_relay_log_info(&mi->rli)) - DBUG_RETURN(ER_ERROR_DURING_FLUSH_LOGS); - - if (my_sync(mi->rli.info_fd, MYF(MY_WME))) - DBUG_RETURN(ER_ERROR_DURING_FLUSH_LOGS); + if (flush_relay_log_info(&mi->rli) || + my_sync(mi->rli.info_fd, MYF(MY_WME))) + retval= ER_ERROR_DURING_FLUSH_LOGS; mysql_mutex_unlock(log_lock); } - if (opt_slave_parallel_threads > 0 && - master_info_index &&// master_info_index is set to NULL on server shutdown - !master_info_index->any_slave_sql_running()) - rpl_parallel_inactivate_pool(&global_rpl_thread_pool); if (thread_mask & (SLAVE_IO|SLAVE_FORCE_ALL)) { DBUG_PRINT("info",("Terminating IO thread")); @@ -664,25 +652,26 @@ int terminate_slave_threads(Master_info* mi,int thread_mask,bool skip_lock) skip_lock)) && !force_all) DBUG_RETURN(error); + if (!retval) + retval= error; mysql_mutex_lock(log_lock); DBUG_PRINT("info",("Flushing relay log and master info file.")); if (current_thd) THD_STAGE_INFO(current_thd, stage_flushing_relay_log_and_master_info_repository); - if (flush_master_info(mi, TRUE, FALSE)) - DBUG_RETURN(ER_ERROR_DURING_FLUSH_LOGS); - + if (likely(mi->fd >= 0)) + { + if (flush_master_info(mi, TRUE, FALSE) || my_sync(mi->fd, MYF(MY_WME))) + retval= ER_ERROR_DURING_FLUSH_LOGS; + } if (mi->rli.relay_log.is_open() && my_sync(mi->rli.relay_log.get_log_file()->file, MYF(MY_WME))) - DBUG_RETURN(ER_ERROR_DURING_FLUSH_LOGS); - - if (my_sync(mi->fd, MYF(MY_WME))) - DBUG_RETURN(ER_ERROR_DURING_FLUSH_LOGS); + retval= ER_ERROR_DURING_FLUSH_LOGS; mysql_mutex_unlock(log_lock); } - DBUG_RETURN(0); + DBUG_RETURN(retval); } @@ -845,6 +834,15 @@ int start_slave_thread( mysql_mutex_unlock(start_lock); DBUG_RETURN(ER_SLAVE_THREAD); } + + /* + In the following loop we can't check for thd->killed as we have to + wait until THD structures for the slave thread are created + before we can return. + This should be ok as there is no major work done in the slave + threads before they signal that we can stop waiting. + */ + if (start_cond && cond_lock) // caller has cond_lock { THD* thd = current_thd; @@ -862,16 +860,9 @@ int start_slave_thread( registered, we could otherwise go waiting though thd->killed is set. */ - if (!thd->killed) - mysql_cond_wait(start_cond, cond_lock); + mysql_cond_wait(start_cond, cond_lock); thd->EXIT_COND(& saved_stage); mysql_mutex_lock(cond_lock); // re-acquire it as exit_cond() released - if (thd->killed) - { - if (start_lock) - mysql_mutex_unlock(start_lock); - DBUG_RETURN(thd->killed_errno()); - } } } if (start_lock) @@ -959,10 +950,7 @@ int start_slave_threads(bool need_slave_mutex, bool wait_for_start, mi); if (!error && (thread_mask & SLAVE_SQL)) { - if (opt_slave_parallel_threads > 0) - error= rpl_parallel_activate_pool(&global_rpl_thread_pool); - if (!error) - error= start_slave_thread( + error= start_slave_thread( #ifdef HAVE_PSI_INTERFACE key_thread_slave_sql, #endif @@ -978,10 +966,18 @@ int start_slave_threads(bool need_slave_mutex, bool wait_for_start, /* - Release slave threads at time of executing shutdown. + Kill slaves preparing for shutdown +*/ - SYNOPSIS - end_slave() +void slave_prepare_for_shutdown() +{ + mysql_mutex_lock(&LOCK_active_mi); + master_info_index->free_connections(); + mysql_mutex_unlock(&LOCK_active_mi); +} + +/* + Release slave threads at time of executing shutdown. */ void end_slave() @@ -999,7 +995,10 @@ void end_slave() startup parameter to the server was wrong. */ mysql_mutex_lock(&LOCK_active_mi); - /* This will call terminate_slave_threads() on all connections */ + /* + master_info_index should not have any threads anymore as they where + killed as part of slave_prepare_for_shutdown() + */ delete master_info_index; master_info_index= 0; active_mi= 0; @@ -2663,7 +2662,9 @@ static bool send_show_master_info_data(THD *thd, Master_info *mi, bool full, mysql_mutex_lock(&mi->data_lock); mysql_mutex_lock(&mi->rli.data_lock); + /* err_lock is to protect mi->last_error() */ mysql_mutex_lock(&mi->err_lock); + /* err_lock is to protect mi->rli.last_error() */ mysql_mutex_lock(&mi->rli.err_lock); protocol->store(mi->host, &my_charset_bin); protocol->store(mi->user, &my_charset_bin); @@ -4532,6 +4533,16 @@ pthread_handler_t handle_slave_sql(void *arg) rli->slave_running= MYSQL_SLAVE_RUN_NOT_CONNECT; pthread_detach_this_thread(); + + if (opt_slave_parallel_threads > 0 && + rpl_parallel_activate_pool(&global_rpl_thread_pool)) + { + mysql_cond_broadcast(&rli->start_cond); + rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR, NULL, + "Failed during parallel slave pool activation"); + goto err_during_init; + } + if (init_slave_thread(thd, mi, SLAVE_THD_SQL)) { /* @@ -4840,8 +4851,15 @@ log '%s' at position %s, relay log '%s' position: %s%s", RPL_LOG_NAME, if (rli->mi->using_gtid != Master_info::USE_GTID_NO) { ulong domain_count; + my_bool save_log_all_errors= thd->log_all_errors; + /* + We don't need to check return value for flush_relay_log_info() + as any errors should be logged to stderr + */ + thd->log_all_errors= 1; flush_relay_log_info(rli); + thd->log_all_errors= save_log_all_errors; if (mi->using_parallel()) { /* @@ -4950,17 +4968,7 @@ err_during_init: DBUG_EXECUTE_IF("simulate_slave_delay_at_terminate_bug38694", sleep(5);); mysql_mutex_unlock(&rli->run_lock); // tell the world we are done - /* - Deactivate the parallel replication thread pool, if there are now no more - SQL threads running. Do this here, when we have released all locks, but - while our THD (and current_thd) is still valid. - */ - mysql_mutex_lock(&LOCK_active_mi); - if (opt_slave_parallel_threads > 0 && - master_info_index &&// master_info_index is set to NULL on server shutdown - !master_info_index->any_slave_sql_running()) - rpl_parallel_inactivate_pool(&global_rpl_thread_pool); - mysql_mutex_unlock(&LOCK_active_mi); + rpl_parallel_resize_pool_if_no_slaves(); mysql_mutex_lock(&LOCK_thread_count); delete thd; @@ -6070,6 +6078,7 @@ err: void end_relay_log_info(Relay_log_info* rli) { + mysql_mutex_t *log_lock; DBUG_ENTER("end_relay_log_info"); if (!rli->inited) @@ -6087,8 +6096,11 @@ void end_relay_log_info(Relay_log_info* rli) rli->cur_log_fd = -1; } rli->inited = 0; + log_lock= rli->relay_log.get_log_lock(); + mysql_mutex_lock(log_lock); rli->relay_log.close(LOG_CLOSE_INDEX | LOG_CLOSE_STOP_EVENT); rli->relay_log.harvest_bytes_written(&rli->log_space_total); + mysql_mutex_unlock(log_lock); /* Delete the slave's temporary tables from memory. In the future there will be other actions than this, to ensure persistance @@ -6240,7 +6252,7 @@ static int connect_to_master(THD* thd, MYSQL* mysql, Master_info* mi, suppress_warnings= 0; mi->report(ERROR_LEVEL, last_errno, NULL, "error %s to master '%s@%s:%d'" - " - retry-time: %d retries: %lu message: %s", + " - retry-time: %d maximum-retries: %lu message: %s", (reconnect ? "reconnecting" : "connecting"), mi->user, mi->host, mi->port, mi->connect_retry, master_retry_count, @@ -6804,9 +6816,12 @@ static Log_event* next_event(rpl_group_info *rgi, ulonglong *event_size) } rli->event_relay_log_pos = BIN_LOG_HEADER_SIZE; strmake_buf(rli->event_relay_log_name,rli->linfo.log_file_name); - flush_relay_log_info(rli); + if (flush_relay_log_info(rli)) + { + errmsg= "error flushing relay log"; + goto err; + } } - /* Now we want to open this next log. To know if it's a hot log (the one being written by the I/O thread now) or a cold log, we can use diff --git a/sql/slave.h b/sql/slave.h index c6b78b96aca..abcb4df984b 100644 --- a/sql/slave.h +++ b/sql/slave.h @@ -213,13 +213,12 @@ bool rpl_master_erroneous_autoinc(THD* thd); const char *print_slave_db_safe(const char *db); void skip_load_data_infile(NET* net); +void slave_prepare_for_shutdown(); void end_slave(); /* release slave threads */ void close_active_mi(); /* clean up slave threads data */ void clear_until_condition(Relay_log_info* rli); void clear_slave_error(Relay_log_info* rli); void end_relay_log_info(Relay_log_info* rli); -void lock_slave_threads(Master_info* mi); -void unlock_slave_threads(Master_info* mi); void init_thread_mask(int* mask,Master_info* mi,bool inverse); Format_description_log_event * read_relay_log_description_event(IO_CACHE *cur_log, ulonglong start_pos, diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index be8b0c5a022..d4b23a9fbc2 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -8975,13 +8975,13 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop, case USER_ACL: acl_user->user.str= strdup_root(&acl_memroot, user_to->user.str); acl_user->user.length= user_to->user.length; - acl_user->host.hostname= strdup_root(&acl_memroot, user_to->host.str); - acl_user->hostname_length= user_to->host.length; + update_hostname(&acl_user->host, strdup_root(&acl_memroot, user_to->host.str)); + acl_user->hostname_length= strlen(acl_user->host.hostname); break; case DB_ACL: acl_db->user= strdup_root(&acl_memroot, user_to->user.str); - acl_db->host.hostname= strdup_root(&acl_memroot, user_to->host.str); + update_hostname(&acl_db->host, strdup_root(&acl_memroot, user_to->host.str)); break; case COLUMN_PRIVILEGES_HASH: diff --git a/sql/sql_alter.h b/sql/sql_alter.h index 526442e83e2..7114694124b 100644 --- a/sql/sql_alter.h +++ b/sql/sql_alter.h @@ -70,59 +70,56 @@ public: // Set for DISABLE KEYS | ENABLE KEYS static const uint ALTER_KEYS_ONOFF = 1L << 9; - // Set for CONVERT TO CHARACTER SET - static const uint ALTER_CONVERT = 1L << 10; - // Set for FORCE // Set for ENGINE(same engine) // Set by mysql_recreate_table() - static const uint ALTER_RECREATE = 1L << 11; + static const uint ALTER_RECREATE = 1L << 10; // Set for ADD PARTITION - static const uint ALTER_ADD_PARTITION = 1L << 12; + static const uint ALTER_ADD_PARTITION = 1L << 11; // Set for DROP PARTITION - static const uint ALTER_DROP_PARTITION = 1L << 13; + static const uint ALTER_DROP_PARTITION = 1L << 12; // Set for COALESCE PARTITION - static const uint ALTER_COALESCE_PARTITION = 1L << 14; + static const uint ALTER_COALESCE_PARTITION = 1L << 13; // Set for REORGANIZE PARTITION ... INTO - static const uint ALTER_REORGANIZE_PARTITION = 1L << 15; + static const uint ALTER_REORGANIZE_PARTITION = 1L << 14; // Set for partition_options - static const uint ALTER_PARTITION = 1L << 16; + static const uint ALTER_PARTITION = 1L << 15; // Set for LOAD INDEX INTO CACHE ... PARTITION // Set for CACHE INDEX ... PARTITION - static const uint ALTER_ADMIN_PARTITION = 1L << 17; + static const uint ALTER_ADMIN_PARTITION = 1L << 16; // Set for REORGANIZE PARTITION - static const uint ALTER_TABLE_REORG = 1L << 18; + static const uint ALTER_TABLE_REORG = 1L << 17; // Set for REBUILD PARTITION - static const uint ALTER_REBUILD_PARTITION = 1L << 19; + static const uint ALTER_REBUILD_PARTITION = 1L << 18; // Set for partitioning operations specifying ALL keyword - static const uint ALTER_ALL_PARTITION = 1L << 20; + static const uint ALTER_ALL_PARTITION = 1L << 19; // Set for REMOVE PARTITIONING - static const uint ALTER_REMOVE_PARTITIONING = 1L << 21; + static const uint ALTER_REMOVE_PARTITIONING = 1L << 20; // Set for ADD FOREIGN KEY - static const uint ADD_FOREIGN_KEY = 1L << 22; + static const uint ADD_FOREIGN_KEY = 1L << 21; // Set for DROP FOREIGN KEY - static const uint DROP_FOREIGN_KEY = 1L << 23; + static const uint DROP_FOREIGN_KEY = 1L << 22; // Set for EXCHANGE PARITION - static const uint ALTER_EXCHANGE_PARTITION = 1L << 24; + static const uint ALTER_EXCHANGE_PARTITION = 1L << 23; // Set by Sql_cmd_alter_table_truncate_partition::execute() - static const uint ALTER_TRUNCATE_PARTITION = 1L << 25; + static const uint ALTER_TRUNCATE_PARTITION = 1L << 24; // Set for ADD [COLUMN] FIRST | AFTER - static const uint ALTER_COLUMN_ORDER = 1L << 26; + static const uint ALTER_COLUMN_ORDER = 1L << 25; enum enum_enable_or_disable { LEAVE_AS_IS, ENABLE, DISABLE }; diff --git a/sql/sql_base.cc b/sql/sql_base.cc index efe9ac6f7f4..2c7f3147901 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -6672,7 +6672,7 @@ find_field_in_tables(THD *thd, Item_ident *item, if (!table_ref->belong_to_view && !table_ref->belong_to_derived) { - SELECT_LEX *current_sel= thd->lex->current_select; + SELECT_LEX *current_sel= item->context->select_lex; SELECT_LEX *last_select= table_ref->select_lex; bool all_merged= TRUE; for (SELECT_LEX *sl= current_sel; sl && sl!=last_select; @@ -8758,9 +8758,7 @@ fill_record(THD * thd, TABLE *table_arg, List<Item> &fields, List<Item> &values, /* Update virtual fields*/ thd->abort_on_warning= FALSE; if (vcol_table && vcol_table->vfield && - update_virtual_fields(thd, vcol_table, - vcol_table->triggers ? VCOL_UPDATE_ALL : - VCOL_UPDATE_FOR_WRITE)) + update_virtual_fields(thd, vcol_table, VCOL_UPDATE_FOR_WRITE)) goto err; thd->abort_on_warning= save_abort_on_warning; thd->no_errors= save_no_errors; @@ -8821,9 +8819,7 @@ fill_record_n_invoke_before_triggers(THD *thd, TABLE *table, List<Item> &fields, if (item_field && item_field->field && table && table->vfield) { DBUG_ASSERT(table == item_field->field->table); - result= update_virtual_fields(thd, table, - table->triggers ? VCOL_UPDATE_ALL : - VCOL_UPDATE_FOR_WRITE); + result= update_virtual_fields(thd, table, VCOL_UPDATE_FOR_WRITE); } } } @@ -8908,9 +8904,7 @@ fill_record(THD *thd, TABLE *table, Field **ptr, List<Item> &values, /* Update virtual fields*/ thd->abort_on_warning= FALSE; if (table->vfield && - update_virtual_fields(thd, table, - table->triggers ? VCOL_UPDATE_ALL : - VCOL_UPDATE_FOR_WRITE)) + update_virtual_fields(thd, table, VCOL_UPDATE_FOR_WRITE)) goto err; thd->abort_on_warning= abort_on_warning_saved; DBUG_RETURN(thd->is_error()); @@ -8961,9 +8955,7 @@ fill_record_n_invoke_before_triggers(THD *thd, TABLE *table, Field **ptr, { DBUG_ASSERT(table == (*ptr)->table); if (table->vfield) - result= update_virtual_fields(thd, table, - table->triggers ? VCOL_UPDATE_ALL : - VCOL_UPDATE_FOR_WRITE); + result= update_virtual_fields(thd, table, VCOL_UPDATE_FOR_WRITE); } return result; diff --git a/sql/sql_class.cc b/sql/sql_class.cc index b210a4d32dc..57c228900fe 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -1272,7 +1272,6 @@ THD::THD() m_internal_handler= NULL; m_binlog_invoker= INVOKER_NONE; - arena_for_cached_items= 0; memset(&invoker_user, 0, sizeof(invoker_user)); memset(&invoker_host, 0, sizeof(invoker_host)); prepare_derived_at_open= FALSE; @@ -6849,7 +6848,13 @@ wait_for_commit::reinit() So in this case, do a re-init of the mutex. In release builds, we want to avoid the overhead of a re-init though. + + To ensure that no one is locking the mutex, we take a lock of it first. + For full explanation, see wait_for_commit::~wait_for_commit() */ + mysql_mutex_lock(&LOCK_wait_commit); + mysql_mutex_unlock(&LOCK_wait_commit); + mysql_mutex_destroy(&LOCK_wait_commit); mysql_mutex_init(key_LOCK_wait_commit, &LOCK_wait_commit, MY_MUTEX_INIT_FAST); #endif diff --git a/sql/sql_class.h b/sql/sql_class.h index aed75d94972..beef22a8140 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -3702,26 +3702,7 @@ public: } } -private: - /* - This reference points to the table arena when the expression - for a virtual column is being evaluated - */ - Query_arena *arena_for_cached_items; - public: - void reset_arena_for_cached_items(Query_arena *new_arena) - { - arena_for_cached_items= new_arena; - } - Query_arena *switch_to_arena_for_cached_items(Query_arena *backup) - { - if (!arena_for_cached_items) - return 0; - set_n_backup_active_arena(arena_for_cached_items, backup); - return backup; - } - void clear_wakeup_ready() { wakeup_ready= false; } /* Sleep waiting for others to wake us up with signal_wakeup_ready(). diff --git a/sql/sql_db.cc b/sql/sql_db.cc index 0a3ff64113f..3f43a33ab7c 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -815,7 +815,8 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent) if there exists a table with the name 'db', so let's just do it separately. We know this file exists and needs to be deleted anyway. */ - if (my_delete_with_symlink(path, MYF(0)) && my_errno != ENOENT) + if (my_handler_delete_with_symlink(key_file_misc, path, "", MYF(0)) && + my_errno != ENOENT) { my_error(EE_DELETE, MYF(0), path, my_errno); DBUG_RETURN(true); @@ -1116,9 +1117,9 @@ static bool find_db_tables_and_rm_known_files(THD *thd, MY_DIR *dirp, strxmov(filePath, path, "/", file->name, NullS); /* We ignore ENOENT error in order to skip files that was deleted - by concurrently running statement like REAPIR TABLE ... + by concurrently running statement like REPAIR TABLE ... */ - if (my_delete_with_symlink(filePath, MYF(0)) && + if (my_handler_delete_with_symlink(key_file_misc, filePath, "", MYF(0)) && my_errno != ENOENT) { my_error(EE_DELETE, MYF(0), filePath, my_errno); @@ -1234,7 +1235,7 @@ long mysql_rm_arc_files(THD *thd, MY_DIR *dirp, const char *org_path) continue; } strxmov(filePath, org_path, "/", file->name, NullS); - if (mysql_file_delete_with_symlink(key_file_misc, filePath, MYF(MY_WME))) + if (my_handler_delete_with_symlink(key_file_misc, filePath, "", MYF(MY_WME))) { goto err; } diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 9ac7e64ec23..ad5bc23a31b 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -548,9 +548,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ! thd->is_error()) { if (table->vfield) - update_virtual_fields(thd, table, - table->triggers ? VCOL_UPDATE_ALL : - VCOL_UPDATE_FOR_READ); + update_virtual_fields(thd, table, VCOL_UPDATE_FOR_READ); thd->inc_examined_row_count(1); // thd->is_error() is tested to disallow delete row on error if (!select || select->skip_record(thd) > 0) @@ -1309,4 +1307,3 @@ bool multi_delete::send_eof() } return 0; } - diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc index f2674cb8dab..af5b016df9d 100644 --- a/sql/sql_derived.cc +++ b/sql/sql_derived.cc @@ -447,6 +447,9 @@ bool mysql_derived_merge(THD *thd, LEX *lex, TABLE_LIST *derived) { Item *expr= derived->on_expr; expr= and_conds(expr, dt_select->join ? dt_select->join->conds : 0); + if (expr) + expr->top_level_item(); + if (expr && (derived->prep_on_expr || expr != derived->on_expr)) { derived->on_expr= expr; diff --git a/sql/sql_join_cache.cc b/sql/sql_join_cache.cc index f701c424f63..7584b42c904 100644 --- a/sql/sql_join_cache.cc +++ b/sql/sql_join_cache.cc @@ -589,6 +589,11 @@ void JOIN_CACHE::create_remaining_fields() { MY_BITMAP *rem_field_set; TABLE *table= tab->table; +#if MYSQL_VERSION_ID < 100204 + empty_record(table); +#else +#error remove +#endif if (all_read_fields) rem_field_set= table->read_set; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index c906a941867..3db0f37974a 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -2119,12 +2119,12 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident, #endif case SCH_COLUMNS: case SCH_STATISTICS: - { #ifdef DONT_ALLOW_SHOW_COMMANDS my_message(ER_NOT_ALLOWED_COMMAND, ER(ER_NOT_ALLOWED_COMMAND), MYF(0)); /* purecov: inspected */ DBUG_RETURN(1); #else + { DBUG_ASSERT(table_ident); TABLE_LIST **query_tables_last= lex->query_tables_last; schema_select_lex= new SELECT_LEX(); @@ -2424,7 +2424,7 @@ static bool wsrep_is_show_query(enum enum_sql_command command) int mysql_execute_command(THD *thd) { - int res= FALSE; + int res= 0; int up_result= 0; LEX *lex= thd->lex; /* first SELECT_LEX (have special meaning for many of non-SELECTcommands) */ @@ -2983,10 +2983,17 @@ case SQLCOM_PREPARE: if (check_global_access(thd, SUPER_ACL)) goto error; + /* + In this code it's ok to use LOCK_active_mi as we are adding new things + into master_info_index + */ mysql_mutex_lock(&LOCK_active_mi); - if (!master_info_index) + { + mysql_mutex_unlock(&LOCK_active_mi); + my_error(ER_SERVER_SHUTDOWN, MYF(0)); goto error; + } mi= master_info_index->get_master_info(&lex_mi->connection_name, Sql_condition::WARN_LEVEL_NOTE); @@ -3015,7 +3022,7 @@ case SQLCOM_PREPARE: If new master was not added, we still need to free mi. */ if (master_info_added) - master_info_index->remove_master_info(&lex_mi->connection_name); + master_info_index->remove_master_info(mi); else delete mi; } @@ -3033,22 +3040,24 @@ case SQLCOM_PREPARE: /* Accept one of two privileges */ if (check_global_access(thd, SUPER_ACL | REPL_CLIENT_ACL)) goto error; - mysql_mutex_lock(&LOCK_active_mi); if (lex->verbose) + { + mysql_mutex_lock(&LOCK_active_mi); res= show_all_master_info(thd); + mysql_mutex_unlock(&LOCK_active_mi); + } else { LEX_MASTER_INFO *lex_mi= &thd->lex->mi; Master_info *mi; - mi= master_info_index->get_master_info(&lex_mi->connection_name, - Sql_condition::WARN_LEVEL_ERROR); - if (mi != NULL) + if ((mi= get_master_info(&lex_mi->connection_name, + Sql_condition::WARN_LEVEL_ERROR))) { res= show_master_info(thd, mi, 0); + mi->release(); } } - mysql_mutex_unlock(&LOCK_active_mi); break; } case SQLCOM_SHOW_MASTER_STAT: @@ -3383,22 +3392,23 @@ end_with_restore_list: load_error= rpl_load_gtid_slave_state(thd); - mysql_mutex_lock(&LOCK_active_mi); - - if ((mi= (master_info_index-> - get_master_info(&lex_mi->connection_name, - Sql_condition::WARN_LEVEL_ERROR)))) + /* + We don't need to ensure that only one user is using master_info + as start_slave is protected against simultaneous usage + */ + if ((mi= get_master_info(&lex_mi->connection_name, + Sql_condition::WARN_LEVEL_ERROR))) { if (load_error) { /* - We cannot start a slave using GTID if we cannot load the GTID position - from the mysql.gtid_slave_pos table. But we can allow non-GTID - replication (useful eg. during upgrade). + We cannot start a slave using GTID if we cannot load the + GTID position from the mysql.gtid_slave_pos table. But we + can allow non-GTID replication (useful eg. during upgrade). */ if (mi->using_gtid != Master_info::USE_GTID_NO) { - mysql_mutex_unlock(&LOCK_active_mi); + mi->release(); break; } else @@ -3406,8 +3416,8 @@ end_with_restore_list: } if (!start_slave(thd, mi, 1 /* net report*/)) my_ok(thd); + mi->release(); } - mysql_mutex_unlock(&LOCK_active_mi); break; } case SQLCOM_SLAVE_STOP: @@ -3437,13 +3447,17 @@ end_with_restore_list: } lex_mi= &thd->lex->mi; - mysql_mutex_lock(&LOCK_active_mi); - if ((mi= (master_info_index-> - get_master_info(&lex_mi->connection_name, - Sql_condition::WARN_LEVEL_ERROR)))) - if (!stop_slave(thd, mi, 1/* net report*/)) + if ((mi= get_master_info(&lex_mi->connection_name, + Sql_condition::WARN_LEVEL_ERROR))) + { + if (stop_slave(thd, mi, 1/* net report*/)) + res= 1; + mi->release(); + if (rpl_parallel_resize_pool_if_no_slaves()) + res= 1; + if (!res) my_ok(thd); - mysql_mutex_unlock(&LOCK_active_mi); + } break; } case SQLCOM_SLAVE_ALL_START: @@ -4744,11 +4758,13 @@ end_with_restore_list: reload_acl_and_cache binlog interactions failed */ res= 1; - } + } if (!res) my_ok(thd); } + else + res= 1; // reload_acl_and_cache failed #ifdef HAVE_REPLICATION if (lex->type & REFRESH_READ_LOCK) rpl_unpause_after_ftwrl(thd); @@ -7123,7 +7139,7 @@ static void wsrep_mysql_parse(THD *thd, char *rawbuf, uint length, #endif /* WITH_WSREP */ /* - When you modify mysql_parse(), you may need to mofify + When you modify mysql_parse(), you may need to modify mysql_test_parse_for_slave() in this same file. */ @@ -9113,48 +9129,24 @@ bool check_ident_length(LEX_STRING *ident) } -C_MODE_START - /* Check if path does not contain mysql data home directory SYNOPSIS - test_if_data_home_dir() - dir directory + path_starts_from_data_home_dir() + dir directory, with all symlinks resolved RETURN VALUES 0 ok 1 error ; Given path contains data directory */ +extern "C" { -int test_if_data_home_dir(const char *dir) +int path_starts_from_data_home_dir(const char *path) { - char path[FN_REFLEN]; - int dir_len; - DBUG_ENTER("test_if_data_home_dir"); - - if (!dir) - DBUG_RETURN(0); + int dir_len= strlen(path); + DBUG_ENTER("path_starts_from_data_home_dir"); - /* - data_file_name and index_file_name include the table name without - extension. Mostly this does not refer to an existing file. When - comparing data_file_name or index_file_name against the data - directory, we try to resolve all symbolic links. On some systems, - we use realpath(3) for the resolution. This returns ENOENT if the - resolved path does not refer to an existing file. my_realpath() - does then copy the requested path verbatim, without symlink - resolution. Thereafter the comparison can fail even if the - requested path is within the data directory. E.g. if symlinks to - another file system are used. To make realpath(3) return the - resolved path, we strip the table name and compare the directory - path only. If the directory doesn't exist either, table creation - will fail anyway. - */ - - (void) fn_format(path, dir, "", "", - (MY_RETURN_REAL_PATH|MY_RESOLVE_SYMLINKS)); - dir_len= strlen(path); if (mysql_unpacked_real_data_home_len<= dir_len) { if (dir_len > mysql_unpacked_real_data_home_len && @@ -9182,7 +9174,31 @@ int test_if_data_home_dir(const char *dir) DBUG_RETURN(0); } -C_MODE_END +} + +/* + Check if path does not contain mysql data home directory + + SYNOPSIS + test_if_data_home_dir() + dir directory + + RETURN VALUES + 0 ok + 1 error ; Given path contains data directory +*/ + +int test_if_data_home_dir(const char *dir) +{ + char path[FN_REFLEN]; + DBUG_ENTER("test_if_data_home_dir"); + + if (!dir) + DBUG_RETURN(0); + + (void) fn_format(path, dir, "", "", MY_RETURN_REAL_PATH); + DBUG_RETURN(path_starts_from_data_home_dir(path)); +} int error_if_data_home_dir(const char *path, const char *what) diff --git a/sql/sql_parse.h b/sql/sql_parse.h index 0e60a5cc884..368bba91c20 100644 --- a/sql/sql_parse.h +++ b/sql/sql_parse.h @@ -33,7 +33,8 @@ enum enum_mysql_completiontype { COMMIT_RELEASE=-1, COMMIT=0, COMMIT_AND_CHAIN=6 }; -extern "C" int test_if_data_home_dir(const char *dir); +extern "C" int path_starts_from_data_home_dir(const char *dir); +int test_if_data_home_dir(const char *dir); int error_if_data_home_dir(const char *path, const char *what); bool multi_update_precheck(THD *thd, TABLE_LIST *tables); diff --git a/sql/sql_reload.cc b/sql/sql_reload.cc index a83e91680da..704e2f84437 100644 --- a/sql/sql_reload.cc +++ b/sql/sql_reload.cc @@ -181,24 +181,20 @@ bool reload_acl_and_cache(THD *thd, unsigned long long options, slave is not likely to have the same connection names. */ tmp_write_to_binlog= 0; - mysql_mutex_lock(&LOCK_active_mi); - if (master_info_index) + + if (!(mi= (get_master_info(&connection_name, + Sql_condition::WARN_LEVEL_ERROR)))) { - if (!(mi= (master_info_index-> - get_master_info(&connection_name, - Sql_condition::WARN_LEVEL_ERROR)))) - { - result= 1; - } - else - { - mysql_mutex_lock(&mi->data_lock); - if (rotate_relay_log(mi)) - *write_to_binlog= -1; - mysql_mutex_unlock(&mi->data_lock); - } + result= 1; + } + else + { + mysql_mutex_lock(&mi->data_lock); + if (rotate_relay_log(mi)) + *write_to_binlog= -1; + mysql_mutex_unlock(&mi->data_lock); + mi->release(); } - mysql_mutex_unlock(&LOCK_active_mi); #endif } #ifdef HAVE_QUERY_CACHE @@ -377,27 +373,33 @@ bool reload_acl_and_cache(THD *thd, unsigned long long options, LEX_MASTER_INFO* lex_mi= &thd->lex->mi; Master_info *mi; tmp_write_to_binlog= 0; - mysql_mutex_lock(&LOCK_active_mi); - if (master_info_index) + + if (!(mi= get_master_info(&lex_mi->connection_name, + Sql_condition::WARN_LEVEL_ERROR))) { - if (!(mi= (master_info_index-> - get_master_info(&lex_mi->connection_name, - Sql_condition::WARN_LEVEL_ERROR)))) - { - result= 1; - } - else if (reset_slave(thd, mi)) + result= 1; + } + else + { + /* The following will fail if slave is running */ + if (reset_slave(thd, mi)) { + mi->release(); /* NOTE: my_error() has been already called by reset_slave(). */ result= 1; } else if (mi->connection_name.length && thd->lex->reset_slave_info.all) { /* If not default connection and 'all' is used */ - master_info_index->remove_master_info(&mi->connection_name); + mi->release(); + mysql_mutex_lock(&LOCK_active_mi); + if (master_info_index->remove_master_info(mi)) + result= 1; + mysql_mutex_unlock(&LOCK_active_mi); } + else + mi->release(); } - mysql_mutex_unlock(&LOCK_active_mi); } #endif if (options & REFRESH_USER_RESOURCES) diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index 4ee55b59c11..ece137aa380 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -2833,7 +2833,16 @@ int start_slave(THD* thd , Master_info* mi, bool net_report) relay_log_info_file, 0, &mi->cmp_connection_name); - lock_slave_threads(mi); // this allows us to cleanly read slave_running + mi->lock_slave_threads(); + if (mi->killed) + { + /* connection was deleted while we waited for lock_slave_threads */ + mi->unlock_slave_threads(); + my_error(WARN_NO_MASTER_INFO, mi->connection_name.length, + mi->connection_name.str); + DBUG_RETURN(-1); + } + // Get a mask of _stopped_ threads init_thread_mask(&thread_mask,mi,1 /* inverse */); @@ -2968,7 +2977,7 @@ int start_slave(THD* thd , Master_info* mi, bool net_report) ER(ER_UNTIL_COND_IGNORED)); if (!slave_errno) - slave_errno = start_slave_threads(0 /*no mutex */, + slave_errno = start_slave_threads(1, 1 /* wait for start */, mi, master_info_file_tmp, @@ -2984,7 +2993,7 @@ int start_slave(THD* thd , Master_info* mi, bool net_report) } err: - unlock_slave_threads(mi); + mi->unlock_slave_threads(); #ifdef WITH_WSREP if (WSREP(thd)) @@ -3033,8 +3042,12 @@ int stop_slave(THD* thd, Master_info* mi, bool net_report ) DBUG_RETURN(-1); THD_STAGE_INFO(thd, stage_killing_slave); int thread_mask; - lock_slave_threads(mi); - // Get a mask of _running_ threads + mi->lock_slave_threads(); + /* + Get a mask of _running_ threads. + We don't have to test for mi->killed as the thread_mask will take care + of checking if threads exists + */ init_thread_mask(&thread_mask,mi,0 /* not inverse*/); /* Below we will stop all running threads. @@ -3047,8 +3060,7 @@ int stop_slave(THD* thd, Master_info* mi, bool net_report ) if (thread_mask) { - slave_errno= terminate_slave_threads(mi,thread_mask, - 1 /*skip lock */); + slave_errno= terminate_slave_threads(mi,thread_mask, 0 /* get lock */); } else { @@ -3057,7 +3069,8 @@ int stop_slave(THD* thd, Master_info* mi, bool net_report ) push_warning(thd, Sql_condition::WARN_LEVEL_NOTE, ER_SLAVE_WAS_NOT_RUNNING, ER(ER_SLAVE_WAS_NOT_RUNNING)); } - unlock_slave_threads(mi); + + mi->unlock_slave_threads(); if (slave_errno) { @@ -3092,11 +3105,20 @@ int reset_slave(THD *thd, Master_info* mi) char relay_log_info_file_tmp[FN_REFLEN]; DBUG_ENTER("reset_slave"); - lock_slave_threads(mi); + mi->lock_slave_threads(); + if (mi->killed) + { + /* connection was deleted while we waited for lock_slave_threads */ + mi->unlock_slave_threads(); + my_error(WARN_NO_MASTER_INFO, mi->connection_name.length, + mi->connection_name.str); + DBUG_RETURN(-1); + } + init_thread_mask(&thread_mask,mi,0 /* not inverse */); if (thread_mask) // We refuse if any slave thread is running { - unlock_slave_threads(mi); + mi->unlock_slave_threads(); my_error(ER_SLAVE_MUST_STOP, MYF(0), (int) mi->connection_name.length, mi->connection_name.str); DBUG_RETURN(ER_SLAVE_MUST_STOP); @@ -3161,7 +3183,7 @@ int reset_slave(THD *thd, Master_info* mi) RUN_HOOK(binlog_relay_io, after_reset_slave, (thd, mi)); err: - unlock_slave_threads(mi); + mi->unlock_slave_threads(); if (error) my_error(sql_errno, MYF(0), errmsg); DBUG_RETURN(error); @@ -3274,8 +3296,8 @@ bool change_master(THD* thd, Master_info* mi, bool *master_info_added) LEX_MASTER_INFO* lex_mi= &thd->lex->mi; DBUG_ENTER("change_master"); - mysql_mutex_assert_owner(&LOCK_active_mi); DBUG_ASSERT(master_info_index); + mysql_mutex_assert_owner(&LOCK_active_mi); *master_info_added= false; /* @@ -3295,7 +3317,16 @@ bool change_master(THD* thd, Master_info* mi, bool *master_info_added) lex_mi->port)) DBUG_RETURN(TRUE); - lock_slave_threads(mi); + mi->lock_slave_threads(); + if (mi->killed) + { + /* connection was deleted while we waited for lock_slave_threads */ + mi->unlock_slave_threads(); + my_error(WARN_NO_MASTER_INFO, mi->connection_name.length, + mi->connection_name.str); + DBUG_RETURN(TRUE); + } + init_thread_mask(&thread_mask,mi,0 /*not inverse*/); if (thread_mask) // We refuse if any slave thread is running { @@ -3597,12 +3628,13 @@ bool change_master(THD* thd, Master_info* mi, bool *master_info_added) in-memory value at restart (thus causing errors, as the old relay log does not exist anymore). */ - flush_relay_log_info(&mi->rli); + if (flush_relay_log_info(&mi->rli)) + ret= 1; mysql_cond_broadcast(&mi->data_cond); mysql_mutex_unlock(&mi->rli.data_lock); err: - unlock_slave_threads(mi); + mi->unlock_slave_threads(); if (ret == FALSE) my_ok(thd); DBUG_RETURN(ret); @@ -3655,7 +3687,6 @@ bool mysql_show_binlog_events(THD* thd) int old_max_allowed_packet= thd->variables.max_allowed_packet; Master_info *mi= 0; LOG_INFO linfo; - DBUG_ENTER("mysql_show_binlog_events"); Log_event::init_show_field_list(&field_list); @@ -3683,13 +3714,9 @@ bool mysql_show_binlog_events(THD* thd) } else /* showing relay log contents */ { - mysql_mutex_lock(&LOCK_active_mi); - if (!master_info_index || - !(mi= master_info_index-> - get_master_info(&thd->variables.default_master_connection, - Sql_condition::WARN_LEVEL_ERROR))) + if (!(mi= get_master_info(&thd->variables.default_master_connection, + Sql_condition::WARN_LEVEL_ERROR))) { - mysql_mutex_unlock(&LOCK_active_mi); DBUG_RETURN(TRUE); } binary_log= &(mi->rli.relay_log); @@ -3709,7 +3736,7 @@ bool mysql_show_binlog_events(THD* thd) if (mi) { /* We can unlock the mutex as we have a lock on the file */ - mysql_mutex_unlock(&LOCK_active_mi); + mi->release(); mi= 0; } @@ -3731,6 +3758,7 @@ bool mysql_show_binlog_events(THD* thd) goto err; } + /* These locks is here to enable syncronization with log_in_use() */ mysql_mutex_lock(&LOCK_thread_count); thd->current_linfo = &linfo; mysql_mutex_unlock(&LOCK_thread_count); @@ -3808,7 +3836,7 @@ bool mysql_show_binlog_events(THD* thd) mysql_mutex_unlock(log_lock); } else if (mi) - mysql_mutex_unlock(&LOCK_active_mi); + mi->release(); // Check that linfo is still on the function scope. DEBUG_SYNC(thd, "after_show_binlog_events"); @@ -3829,8 +3857,9 @@ err: else my_eof(thd); + /* These locks is here to enable syncronization with log_in_use() */ mysql_mutex_lock(&LOCK_thread_count); - thd->current_linfo = 0; + thd->current_linfo= 0; mysql_mutex_unlock(&LOCK_thread_count); thd->variables.max_allowed_packet= old_max_allowed_packet; DBUG_RETURN(ret); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 7850c7a0dcf..d459d0b88bf 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -8510,8 +8510,6 @@ get_best_combination(JOIN *join) form=join->table[tablenr]=j->table; used_tables|= form->map; form->reginfo.join_tab=j; - if (!*j->on_expr_ref) - form->reginfo.not_exists_optimize=0; // Only with LEFT JOIN DBUG_PRINT("info",("type: %d", j->type)); if (j->type == JT_CONST) goto loop_end; // Handled in make_join_stat.. @@ -9355,7 +9353,10 @@ make_outerjoin_info(JOIN *join) tab->cond_equal= tbl->cond_equal; if (embedding && !embedding->is_active_sjm()) tab->first_upper= embedding->nested_join->first_nested; - } + } + else if (!embedding) + tab->table->reginfo.not_exists_optimize= 0; + for ( ; embedding ; embedding= embedding->embedding) { if (embedding->is_active_sjm()) @@ -9365,7 +9366,10 @@ make_outerjoin_info(JOIN *join) } /* Ignore sj-nests: */ if (!(embedding->on_expr && embedding->outer_join)) + { + tab->table->reginfo.not_exists_optimize= 0; continue; + } NESTED_JOIN *nested_join= embedding->nested_join; if (!nested_join->counter) { @@ -9381,17 +9385,10 @@ make_outerjoin_info(JOIN *join) } if (!tab->first_inner) tab->first_inner= nested_join->first_nested; - if (tab->table->reginfo.not_exists_optimize) - tab->first_inner->table->reginfo.not_exists_optimize= 1; if (++nested_join->counter < nested_join->n_tables) break; /* Table tab is the last inner table for nested join. */ nested_join->first_nested->last_inner= tab; - if (tab->first_inner->table->reginfo.not_exists_optimize) - { - for (JOIN_TAB *join_tab= tab->first_inner; join_tab <= tab; join_tab++) - join_tab->table->reginfo.not_exists_optimize= 1; - } } } DBUG_RETURN(FALSE); @@ -15510,7 +15507,9 @@ static Field *create_tmp_field_from_item(THD *thd, Item *item, TABLE *table, if (new_field) new_field->init(table); - if (copy_func && item->real_item()->is_result_field()) + if (copy_func && + (item->is_result_field() || + (item->real_item()->is_result_field()))) *((*copy_func)++) = item; // Save for copy_funcs if (modify_item) item->set_result_field(new_field); @@ -17982,32 +17981,41 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab, first_unmatched->found= 1; for (JOIN_TAB *tab= first_unmatched; tab <= join_tab; tab++) { + /* + Check whether 'not exists' optimization can be used here. + If tab->table->reginfo.not_exists_optimize is set to true + then WHERE contains a conjunctive predicate IS NULL over + a non-nullable field of tab. When activated this predicate + will filter out all records with matches for the left part + of the outer join whose inner tables start from the + first_unmatched table and include table tab. To safely use + 'not exists' optimization we have to check that the + IS NULL predicate is really activated, i.e. all guards + that wrap it are in the 'open' state. + */ + bool not_exists_opt_is_applicable= + tab->table->reginfo.not_exists_optimize; + for (JOIN_TAB *first_upper= first_unmatched->first_upper; + not_exists_opt_is_applicable && first_upper; + first_upper= first_upper->first_upper) + { + if (!first_upper->found) + not_exists_opt_is_applicable= false; + } /* Check all predicates that has just been activated. */ /* Actually all predicates non-guarded by first_unmatched->found will be re-evaluated again. It could be fixed, but, probably, it's not worth doing now. */ - /* - not_exists_optimize has been created from a - select_cond containing 'is_null'. This 'is_null' - predicate is still present on any 'tab' with - 'not_exists_optimize'. Furthermore, the usual rules - for condition guards also applies for - 'not_exists_optimize' -> When 'is_null==false' we - know all cond. guards are open and we can apply - the 'not_exists_optimize'. - */ - DBUG_ASSERT(!(tab->table->reginfo.not_exists_optimize && - !tab->select_cond)); - if (tab->select_cond && !tab->select_cond->val_int()) { /* The condition attached to table tab is false */ - if (tab == join_tab) { found= 0; + if (not_exists_opt_is_applicable) + DBUG_RETURN(NESTED_LOOP_NO_MORE_ROWS); } else { @@ -18016,21 +18024,10 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab, not to the last table of the current nest level. */ join->return_tab= tab; - } - - if (tab->table->reginfo.not_exists_optimize) - { - /* - When not_exists_optimize is set: No need to further - explore more rows of 'tab' for this partial result. - Any found 'tab' matches are known to evaluate to 'false'. - Returning .._NO_MORE_ROWS will skip rem. 'tab' rows. - */ - DBUG_RETURN(NESTED_LOOP_NO_MORE_ROWS); - } - else if (tab != join_tab) - { - DBUG_RETURN(NESTED_LOOP_OK); + if (not_exists_opt_is_applicable) + DBUG_RETURN(NESTED_LOOP_NO_MORE_ROWS); + else + DBUG_RETURN(NESTED_LOOP_OK); } } } diff --git a/sql/sql_table.cc b/sql/sql_table.cc index aec81e17bd3..0732d6b74f0 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -4149,7 +4149,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, Field::utype type= (Field::utype) MTYP_TYPENR(sql_field->unireg_check); if (thd->variables.sql_mode & MODE_NO_ZERO_DATE && - !sql_field->def && + !sql_field->def && !sql_field->vcol_info && is_timestamp_type(sql_field->sql_type) && (sql_field->flags & NOT_NULL_FLAG) && (type == Field::NONE || type == Field::TIMESTAMP_UN_FIELD)) @@ -6492,6 +6492,9 @@ static bool fill_alter_inplace_info(THD *thd, new_key->user_defined_key_parts)) goto index_changed; + if (table_key->block_size != new_key->block_size) + goto index_changed; + if (engine_options_differ(table_key->option_struct, new_key->option_struct, table->file->ht->index_options)) goto index_changed; diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index fec90d7ddf3..327b5bb0260 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -2230,6 +2230,9 @@ void Table_triggers_list::mark_fields_used(trg_event_type event) bitmap_set_bit(trigger_table->read_set, trg_field->field_idx); if (trg_field->get_settable_routine_parameter()) bitmap_set_bit(trigger_table->write_set, trg_field->field_idx); + if (trigger_table->field[trg_field->field_idx]->vcol_info) + trigger_table->mark_virtual_col(trigger_table-> + field[trg_field->field_idx]); } } } diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 65a94d1da60..1a136e5158b 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -629,9 +629,7 @@ int mysql_update(THD *thd, while (!(error=info.read_record(&info)) && !thd->killed) { if (table->vfield) - update_virtual_fields(thd, table, - table->triggers ? VCOL_UPDATE_ALL : - VCOL_UPDATE_FOR_READ); + update_virtual_fields(thd, table, VCOL_UPDATE_FOR_READ); thd->inc_examined_row_count(1); if (!select || (error= select->skip_record(thd)) > 0) { @@ -744,9 +742,7 @@ int mysql_update(THD *thd, while (!(error=info.read_record(&info)) && !thd->killed) { if (table->vfield) - update_virtual_fields(thd, table, - table->triggers ? VCOL_UPDATE_ALL : - VCOL_UPDATE_FOR_READ); + update_virtual_fields(thd, table, VCOL_UPDATE_FOR_READ); thd->inc_examined_row_count(1); if (!select || select->skip_record(thd) > 0) { @@ -2407,8 +2403,7 @@ int multi_update::do_updates() if (table->default_field && (error= table->update_default_fields())) goto err2; if (table->vfield && - update_virtual_fields(thd, table, - (table->triggers ? VCOL_UPDATE_ALL : VCOL_UPDATE_FOR_WRITE))) + update_virtual_fields(thd, table, VCOL_UPDATE_FOR_WRITE)) goto err2; if ((error= cur_table->view_check_option(thd, ignore)) != VIEW_CHECK_OK) diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index a7408f7bf50..b5d9604fd41 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -7649,7 +7649,7 @@ alter_list_item: LEX *lex= Lex; if (lex->create_info.add_alter_list_item_convert_to_charset($5)) MYSQL_YYABORT; - lex->alter_info.flags|= Alter_info::ALTER_CONVERT; + lex->alter_info.flags|= Alter_info::ALTER_OPTIONS; } | create_table_options_space_separated { diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index 379294fa7b2..6457a8597bd 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -1548,7 +1548,6 @@ bool Sys_var_gtid_slave_pos::do_check(THD *thd, set_var *var) { String str, *res; - bool running; DBUG_ASSERT(var->type == OPT_GLOBAL); @@ -1559,11 +1558,7 @@ Sys_var_gtid_slave_pos::do_check(THD *thd, set_var *var) return true; } - mysql_mutex_lock(&LOCK_active_mi); - running= (!master_info_index || - master_info_index->give_error_if_slave_running()); - mysql_mutex_unlock(&LOCK_active_mi); - if (running) + if (give_error_if_slave_running(0)) return true; if (!(res= var->value->val_str(&str))) return true; @@ -1601,7 +1596,7 @@ Sys_var_gtid_slave_pos::global_update(THD *thd, set_var *var) mysql_mutex_unlock(&LOCK_global_system_variables); mysql_mutex_lock(&LOCK_active_mi); - if (!master_info_index || master_info_index->give_error_if_slave_running()) + if (give_error_if_slave_running(1)) err= true; else err= rpl_gtid_pos_update(thd, var->save_result.string_value.str, @@ -1787,16 +1782,7 @@ Sys_var_last_gtid::session_value_ptr(THD *thd, LEX_STRING *base) static bool check_slave_parallel_threads(sys_var *self, THD *thd, set_var *var) { - bool running; - - mysql_mutex_lock(&LOCK_active_mi); - running= (!master_info_index || - master_info_index->give_error_if_slave_running()); - mysql_mutex_unlock(&LOCK_active_mi); - if (running) - return true; - - return false; + return give_error_if_slave_running(0); } static bool @@ -1805,10 +1791,7 @@ fix_slave_parallel_threads(sys_var *self, THD *thd, enum_var_type type) bool err; mysql_mutex_unlock(&LOCK_global_system_variables); - mysql_mutex_lock(&LOCK_active_mi); - err= (!master_info_index || - master_info_index->give_error_if_slave_running()); - mysql_mutex_unlock(&LOCK_active_mi); + err= give_error_if_slave_running(0); mysql_mutex_lock(&LOCK_global_system_variables); return err; @@ -1831,16 +1814,7 @@ static Sys_var_ulong Sys_slave_parallel_threads( static bool check_slave_domain_parallel_threads(sys_var *self, THD *thd, set_var *var) { - bool running; - - mysql_mutex_lock(&LOCK_active_mi); - running= (!master_info_index || - master_info_index->give_error_if_slave_running()); - mysql_mutex_unlock(&LOCK_active_mi); - if (running) - return true; - - return false; + return give_error_if_slave_running(0); } static bool @@ -1849,13 +1823,10 @@ fix_slave_domain_parallel_threads(sys_var *self, THD *thd, enum_var_type type) bool running; mysql_mutex_unlock(&LOCK_global_system_variables); - mysql_mutex_lock(&LOCK_active_mi); - running= (!master_info_index || - master_info_index->give_error_if_slave_running()); - mysql_mutex_unlock(&LOCK_active_mi); + running= give_error_if_slave_running(0); mysql_mutex_lock(&LOCK_global_system_variables); - return running ? true : false; + return running; } @@ -1886,16 +1857,7 @@ static Sys_var_ulong Sys_slave_parallel_max_queued( static bool check_gtid_ignore_duplicates(sys_var *self, THD *thd, set_var *var) { - bool running; - - mysql_mutex_lock(&LOCK_active_mi); - running= (!master_info_index || - master_info_index->give_error_if_slave_running()); - mysql_mutex_unlock(&LOCK_active_mi); - if (running) - return true; - - return false; + return give_error_if_slave_running(0); } static bool @@ -1904,13 +1866,10 @@ fix_gtid_ignore_duplicates(sys_var *self, THD *thd, enum_var_type type) bool running; mysql_mutex_unlock(&LOCK_global_system_variables); - mysql_mutex_lock(&LOCK_active_mi); - running= (!master_info_index || - master_info_index->give_error_if_slave_running()); - mysql_mutex_unlock(&LOCK_active_mi); + running= give_error_if_slave_running(0); mysql_mutex_lock(&LOCK_global_system_variables); - return running ? true : false; + return running; } @@ -2858,10 +2817,8 @@ Sys_var_replicate_events_marked_for_skip::global_update(THD *thd, set_var *var) DBUG_ENTER("Sys_var_replicate_events_marked_for_skip::global_update"); mysql_mutex_unlock(&LOCK_global_system_variables); - mysql_mutex_lock(&LOCK_active_mi); - if (master_info_index && !master_info_index->give_error_if_slave_running()) + if (!give_error_if_slave_running(0)) result= Sys_var_enum::global_update(thd, var); - mysql_mutex_unlock(&LOCK_active_mi); mysql_mutex_lock(&LOCK_global_system_variables); DBUG_RETURN(result); } @@ -4137,19 +4094,16 @@ bool Sys_var_rpl_filter::global_update(THD *thd, set_var *var) Master_info *mi; mysql_mutex_unlock(&LOCK_global_system_variables); - mysql_mutex_lock(&LOCK_active_mi); if (!var->base.length) // no base name { - mi= master_info_index-> - get_master_info(&thd->variables.default_master_connection, - Sql_condition::WARN_LEVEL_ERROR); + mi= get_master_info(&thd->variables.default_master_connection, + Sql_condition::WARN_LEVEL_ERROR); } else // has base name { - mi= master_info_index-> - get_master_info(&var->base, - Sql_condition::WARN_LEVEL_WARN); + mi= get_master_info(&var->base, + Sql_condition::WARN_LEVEL_WARN); } if (mi) @@ -4157,17 +4111,17 @@ bool Sys_var_rpl_filter::global_update(THD *thd, set_var *var) if (mi->rli.slave_running) { my_error(ER_SLAVE_MUST_STOP, MYF(0), - mi->connection_name.length, - mi->connection_name.str); + mi->connection_name.length, + mi->connection_name.str); result= true; } else { result= set_filter_value(var->save_result.string_value.str, mi); } + mi->release(); } - mysql_mutex_unlock(&LOCK_active_mi); mysql_mutex_lock(&LOCK_global_system_variables); return result; } @@ -4175,8 +4129,10 @@ bool Sys_var_rpl_filter::global_update(THD *thd, set_var *var) bool Sys_var_rpl_filter::set_filter_value(const char *value, Master_info *mi) { bool status= true; - Rpl_filter* rpl_filter= mi ? mi->rpl_filter : global_rpl_filter; + Rpl_filter* rpl_filter= mi->rpl_filter; + /* Proctect against other threads */ + mysql_mutex_lock(&LOCK_active_mi); switch (opt_id) { case OPT_REPLICATE_DO_DB: status= rpl_filter->set_do_db(value); @@ -4197,7 +4153,7 @@ bool Sys_var_rpl_filter::set_filter_value(const char *value, Master_info *mi) status= rpl_filter->set_wild_ignore_table(value); break; } - + mysql_mutex_unlock(&LOCK_active_mi); return status; } @@ -4210,29 +4166,24 @@ uchar *Sys_var_rpl_filter::global_value_ptr(THD *thd, LEX_STRING *base) Rpl_filter *rpl_filter; mysql_mutex_unlock(&LOCK_global_system_variables); - mysql_mutex_lock(&LOCK_active_mi); if (!base->length) // no base name { - mi= master_info_index-> - get_master_info(&thd->variables.default_master_connection, - Sql_condition::WARN_LEVEL_ERROR); + mi= get_master_info(&thd->variables.default_master_connection, + Sql_condition::WARN_LEVEL_ERROR); } else // has base name - { - mi= master_info_index-> - get_master_info(base, - Sql_condition::WARN_LEVEL_WARN); - } - mysql_mutex_lock(&LOCK_global_system_variables); + mi= get_master_info(base, Sql_condition::WARN_LEVEL_WARN); if (!mi) { - mysql_mutex_unlock(&LOCK_active_mi); + mysql_mutex_lock(&LOCK_global_system_variables); return 0; } + rpl_filter= mi->rpl_filter; tmp.length(0); + mysql_mutex_lock(&LOCK_active_mi); switch (opt_id) { case OPT_REPLICATE_DO_DB: rpl_filter->get_do_db(&tmp); @@ -4253,9 +4204,12 @@ uchar *Sys_var_rpl_filter::global_value_ptr(THD *thd, LEX_STRING *base) rpl_filter->get_wild_ignore_table(&tmp); break; } + mysql_mutex_unlock(&LOCK_active_mi); + mysql_mutex_lock(&LOCK_global_system_variables); + + mi->release(); ret= (uchar *) thd->strmake(tmp.ptr(), tmp.length()); - mysql_mutex_unlock(&LOCK_active_mi); return ret; } @@ -4326,17 +4280,12 @@ get_master_info_ulonglong_value(THD *thd, ptrdiff_t offset) Master_info *mi; ulonglong res= 0; // Default value mysql_mutex_unlock(&LOCK_global_system_variables); - mysql_mutex_lock(&LOCK_active_mi); - mi= master_info_index-> - get_master_info(&thd->variables.default_master_connection, - Sql_condition::WARN_LEVEL_WARN); - if (mi) + if ((mi= get_master_info(&thd->variables.default_master_connection, + Sql_condition::WARN_LEVEL_WARN))) { - mysql_mutex_lock(&mi->rli.data_lock); res= *((ulonglong*) (((uchar*) mi) + master_info_offset)); - mysql_mutex_unlock(&mi->rli.data_lock); + mi->release(); } - mysql_mutex_unlock(&LOCK_active_mi); mysql_mutex_lock(&LOCK_global_system_variables); return res; } @@ -4351,19 +4300,16 @@ bool update_multi_source_variable(sys_var *self_var, THD *thd, if (type == OPT_GLOBAL) mysql_mutex_unlock(&LOCK_global_system_variables); - mysql_mutex_lock(&LOCK_active_mi); - mi= master_info_index-> - get_master_info(&thd->variables.default_master_connection, - Sql_condition::WARN_LEVEL_ERROR); - if (mi) + if ((mi= (get_master_info(&thd->variables.default_master_connection, + Sql_condition::WARN_LEVEL_ERROR)))) { mysql_mutex_lock(&mi->rli.run_lock); mysql_mutex_lock(&mi->rli.data_lock); result= self->update_variable(thd, mi); mysql_mutex_unlock(&mi->rli.data_lock); mysql_mutex_unlock(&mi->rli.run_lock); + mi->release(); } - mysql_mutex_unlock(&LOCK_active_mi); if (type == OPT_GLOBAL) mysql_mutex_lock(&LOCK_global_system_variables); return result; diff --git a/sql/table.cc b/sql/table.cc index 57effed81ff..60334740790 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -572,7 +572,7 @@ enum open_frm_error open_table_def(THD *thd, TABLE_SHARE *share, uint flags) { DBUG_ASSERT(flags & GTS_TABLE); DBUG_ASSERT(flags & GTS_USE_DISCOVERY); - mysql_file_delete_with_symlink(key_file_frm, path, MYF(0)); + my_handler_delete_with_symlink(key_file_frm, path, "", MYF(0)); file= -1; } else @@ -6689,11 +6689,9 @@ bool is_simple_order(ORDER *order) @details The function computes the values of the virtual columns of the table and stores them in the table record buffer. - If vcol_update_mode is set to VCOL_UPDATE_ALL then all virtual column are - computed. Otherwise, only fields from vcol_set are computed: all of them, - if vcol_update_mode is set to VCOL_UPDATE_FOR_WRITE, and, only those with - the stored_in_db flag set to false, if vcol_update_mode is equal to - VCOL_UPDATE_FOR_READ. + Only fields from vcol_set are computed: all of them, if vcol_update_mode is + set to VCOL_UPDATE_FOR_WRITE, and, only those with the stored_in_db flag + set to false, if vcol_update_mode is equal to VCOL_UPDATE_FOR_READ. @retval 0 Success @@ -6709,15 +6707,16 @@ int update_virtual_fields(THD *thd, TABLE *table, int error __attribute__ ((unused))= 0; DBUG_ASSERT(table && table->vfield); - thd->reset_arena_for_cached_items(table->expr_arena); + Query_arena backup_arena; + thd->set_n_backup_active_arena(table->expr_arena, &backup_arena); + /* Iterate over virtual fields in the table */ for (vfield_ptr= table->vfield; *vfield_ptr; vfield_ptr++) { vfield= (*vfield_ptr); DBUG_ASSERT(vfield->vcol_info && vfield->vcol_info->expr_item); - if ((bitmap_is_set(table->vcol_set, vfield->field_index) && - (vcol_update_mode == VCOL_UPDATE_FOR_WRITE || !vfield->stored_in_db)) || - vcol_update_mode == VCOL_UPDATE_ALL) + if (bitmap_is_set(table->vcol_set, vfield->field_index) && + (vcol_update_mode == VCOL_UPDATE_FOR_WRITE || !vfield->stored_in_db)) { /* Compute the actual value of the virtual fields */ error= vfield->vcol_info->expr_item->save_in_field(vfield, 0); @@ -6728,7 +6727,7 @@ int update_virtual_fields(THD *thd, TABLE *table, DBUG_PRINT("info", ("field '%s' - skipped", vfield->field_name)); } } - thd->reset_arena_for_cached_items(0); + thd->restore_active_arena(table->expr_arena, &backup_arena); DBUG_RETURN(0); } diff --git a/sql/table.h b/sql/table.h index 39faa8b9765..bf98f08842a 100644 --- a/sql/table.h +++ b/sql/table.h @@ -315,8 +315,7 @@ enum release_type { RELEASE_NORMAL, RELEASE_WAIT_FOR_DROP }; enum enum_vcol_update_mode { VCOL_UPDATE_FOR_READ= 0, - VCOL_UPDATE_FOR_WRITE, - VCOL_UPDATE_ALL + VCOL_UPDATE_FOR_WRITE }; class Filesort_info diff --git a/storage/connect/CMakeLists.txt b/storage/connect/CMakeLists.txt index ce6de424421..a602084b5bd 100644 --- a/storage/connect/CMakeLists.txt +++ b/storage/connect/CMakeLists.txt @@ -22,16 +22,16 @@ fmdlex.c osutil.c plugutil.c rcmsg.c rcmsg.h array.cpp blkfil.cpp colblk.cpp csort.cpp filamap.cpp filamdbf.cpp filamfix.cpp filamgz.cpp filamtxt.cpp filter.cpp json.cpp jsonudf.cpp maputil.cpp myconn.cpp myutil.cpp plgdbutl.cpp -reldef.cpp tabcol.cpp tabdos.cpp tabfix.cpp tabfmt.cpp tabjson.cpp table.cpp -tabmul.cpp tabmysql.cpp taboccur.cpp tabpivot.cpp tabsys.cpp tabtbl.cpp tabutil.cpp -tabvir.cpp tabxcl.cpp valblk.cpp value.cpp xindex.cpp xobject.cpp +reldef.cpp tabcol.cpp tabdos.cpp tabext.cpp tabfix.cpp tabfmt.cpp tabjson.cpp +table.cpp tabmul.cpp tabmysql.cpp taboccur.cpp tabpivot.cpp tabsys.cpp tabtbl.cpp +tabutil.cpp tabvir.cpp tabxcl.cpp valblk.cpp value.cpp xindex.cpp xobject.cpp array.h blkfil.h block.h catalog.h checklvl.h colblk.h connect.h csort.h engmsg.h filamap.h filamdbf.h filamfix.h filamgz.h filamtxt.h filter.h global.h ha_connect.h inihandl.h json.h jsonudf.h maputil.h msgid.h mycat.h myconn.h myutil.h os.h osutil.h plgcnx.h plgdbsem.h preparse.h reldef.h -resource.h tabcol.h tabdos.h tabfix.h tabfmt.h tabjson.h tabmul.h tabmysql.h -taboccur.h tabpivot.h tabsys.h tabtbl.h tabutil.h tabvir.h tabxcl.h +resource.h tabcol.h tabdos.h tabext.h tabfix.h tabfmt.h tabjson.h tabmul.h +tabmysql.h taboccur.h tabpivot.h tabsys.h tabtbl.h tabutil.h tabvir.h tabxcl.h user_connect.h valblk.h value.h xindex.h xobject.h xtable.h) # diff --git a/storage/connect/array.cpp b/storage/connect/array.cpp index 193514eeb99..1998ab890e9 100644 --- a/storage/connect/array.cpp +++ b/storage/connect/array.cpp @@ -1,7 +1,7 @@ /************* Array C++ Functions Source Code File (.CPP) *************/ /* Name: ARRAY.CPP Version 2.3 */ /* */ -/* (C) Copyright to the author Olivier BERTRAND 2005-2015 */ +/* (C) Copyright to the author Olivier BERTRAND 2005-2017 */ /* */ /* This file contains the XOBJECT derived class ARRAY functions. */ /* ARRAY is used for elaborate type of processing, such as sorting */ @@ -141,7 +141,7 @@ PARRAY MakeValueArray(PGLOBAL g, PPARM pp) /* ARRAY public constructor. */ /***********************************************************************/ ARRAY::ARRAY(PGLOBAL g, int type, int size, int length, int prec) - : CSORT(FALSE) + : CSORT(false) { Nval = 0; Ndif = 0; @@ -188,14 +188,14 @@ ARRAY::ARRAY(PGLOBAL g, int type, int size, int length, int prec) else if (type != TYPE_PCHAR) Value = AllocateValue(g, type, Len, prec); - Constant = TRUE; + Constant = true; } // end of ARRAY constructor #if 0 /***********************************************************************/ /* ARRAY public constructor from a QUERY. */ /***********************************************************************/ -ARRAY::ARRAY(PGLOBAL g, PQUERY qryp) : CSORT(FALSE) +ARRAY::ARRAY(PGLOBAL g, PQUERY qryp) : CSORT(false) { Type = qryp->GetColType(0); Nval = qryp->GetNblin(); @@ -206,7 +206,7 @@ ARRAY::ARRAY(PGLOBAL g, PQUERY qryp) : CSORT(FALSE) Xsize = -1; Len = qryp->GetColLength(0); X = Inf = Sup = 0; - Correlated = FALSE; + Correlated = false; switch (Type) { case TYPE_STRING: @@ -229,13 +229,13 @@ ARRAY::ARRAY(PGLOBAL g, PQUERY qryp) : CSORT(FALSE) // The error message was built by ??? Type = TYPE_ERROR; - Constant = TRUE; + Constant = true; } // end of ARRAY constructor /***********************************************************************/ /* ARRAY constructor from a TYPE_LIST subarray. */ /***********************************************************************/ -ARRAY::ARRAY(PGLOBAL g, PARRAY par, int k) : CSORT(FALSE) +ARRAY::ARRAY(PGLOBAL g, PARRAY par, int k) : CSORT(false) { int prec; LSTBLK *lp; @@ -260,7 +260,7 @@ ARRAY::ARRAY(PGLOBAL g, PARRAY par, int k) : CSORT(FALSE) Len = (Type == TYPE_STRING) ? Vblp->GetVlen() : 0; prec = (Type == TYPE_FLOAT) ? 2 : 0; Value = AllocateValue(g, Type, Len, prec, NULL); - Constant = TRUE; + Constant = true; } // end of ARRAY constructor /***********************************************************************/ @@ -283,7 +283,7 @@ bool ARRAY::AddValue(PGLOBAL g, PSZ strp) { if (Type != TYPE_STRING) { sprintf(g->Message, MSG(ADD_BAD_TYPE), GetTypeName(Type), "CHAR"); - return TRUE; + return true; } // endif Type if (trace) @@ -292,7 +292,7 @@ bool ARRAY::AddValue(PGLOBAL g, PSZ strp) //Value->SetValue_psz(strp); //Vblp->SetValue(valp, Nval++); Vblp->SetValue(strp, Nval++); - return FALSE; + return false; } // end of AddValue /***********************************************************************/ @@ -302,14 +302,14 @@ bool ARRAY::AddValue(PGLOBAL g, void *p) { if (Type != TYPE_PCHAR) { sprintf(g->Message, MSG(ADD_BAD_TYPE), GetTypeName(Type), "PCHAR"); - return TRUE; + return true; } // endif Type if (trace) htrc(" adding pointer(%d): %p\n", Nval, p); Vblp->SetValue((PSZ)p, Nval++); - return FALSE; + return false; } // end of AddValue /***********************************************************************/ @@ -319,7 +319,7 @@ bool ARRAY::AddValue(PGLOBAL g, short n) { if (Type != TYPE_SHORT) { sprintf(g->Message, MSG(ADD_BAD_TYPE), GetTypeName(Type), "SHORT"); - return TRUE; + return true; } // endif Type if (trace) @@ -328,7 +328,7 @@ bool ARRAY::AddValue(PGLOBAL g, short n) //Value->SetValue(n); //Vblp->SetValue(valp, Nval++); Vblp->SetValue(n, Nval++); - return FALSE; + return false; } // end of AddValue /***********************************************************************/ @@ -338,7 +338,7 @@ bool ARRAY::AddValue(PGLOBAL g, int n) { if (Type != TYPE_INT) { sprintf(g->Message, MSG(ADD_BAD_TYPE), GetTypeName(Type), "INTEGER"); - return TRUE; + return true; } // endif Type if (trace) @@ -347,7 +347,7 @@ bool ARRAY::AddValue(PGLOBAL g, int n) //Value->SetValue(n); //Vblp->SetValue(valp, Nval++); Vblp->SetValue(n, Nval++); - return FALSE; + return false; } // end of AddValue /***********************************************************************/ @@ -357,7 +357,7 @@ bool ARRAY::AddValue(PGLOBAL g, double d) { if (Type != TYPE_DOUBLE) { sprintf(g->Message, MSG(ADD_BAD_TYPE), GetTypeName(Type), "DOUBLE"); - return TRUE; + return true; } // endif Type if (trace) @@ -365,7 +365,7 @@ bool ARRAY::AddValue(PGLOBAL g, double d) Value->SetValue(d); Vblp->SetValue(Value, Nval++); - return FALSE; + return false; } // end of AddValue /***********************************************************************/ @@ -376,7 +376,7 @@ bool ARRAY::AddValue(PGLOBAL g, PXOB xp) if (Type != xp->GetResultType()) { sprintf(g->Message, MSG(ADD_BAD_TYPE), GetTypeName(xp->GetResultType()), GetTypeName(Type)); - return TRUE; + return true; } // endif Type if (trace) @@ -384,7 +384,7 @@ bool ARRAY::AddValue(PGLOBAL g, PXOB xp) //AddValue(xp->GetValue()); Vblp->SetValue(xp->GetValue(), Nval++); - return FALSE; + return false; } // end of AddValue /***********************************************************************/ @@ -395,14 +395,14 @@ bool ARRAY::AddValue(PGLOBAL g, PVAL vp) if (Type != vp->GetType()) { sprintf(g->Message, MSG(ADD_BAD_TYPE), GetTypeName(vp->GetType()), GetTypeName(Type)); - return TRUE; + return true; } // endif Type if (trace) htrc(" adding (%d) from vp=%p\n", Nval, vp); Vblp->SetValue(vp, Nval++); - return FALSE; + return false; } // end of AddValue /***********************************************************************/ @@ -423,12 +423,12 @@ bool ARRAY::GetSubValue(PGLOBAL g, PVAL valp, int *kp) if (Type != TYPE_LIST) { sprintf(g->Message, MSG(NO_SUB_VAL), Type); - return TRUE; + return true; } // endif Type vblp = ((LSTBLK*)Vblp)->Mbvk[kp[0]]->Vblk; valp->SetValue_pvblk(vblp, kp[1]); - return FALSE; + return false; } // end of GetSubValue #endif // 0 @@ -476,11 +476,11 @@ bool ARRAY::Find(PVAL valp) else if (n > 0) Inf = X; else - return TRUE; + return true; } // endwhile - return FALSE; + return false; } // end of Find /***********************************************************************/ @@ -504,9 +504,9 @@ bool ARRAY::FilTest(PGLOBAL g, PVAL valp, OPVAL opc, int opm) int top = Nval - 1; if (top < 0) // Array is empty - // Return TRUE for ALL because it means that there are no item that + // Return true for ALL because it means that there are no item that // does not verify the condition, which is true indeed. - // Return FALSE for ANY because TRUE means that there is at least + // Return false for ANY because true means that there is at least // one item that verifies the condition, which is false. return opm == 2; @@ -528,9 +528,9 @@ bool ARRAY::FilTest(PGLOBAL g, PVAL valp, OPVAL opc, int opm) else if (opc == OP_NE && opm == 2) return !Find(vp); else if (opc == OP_EQ && opm == 2) - return (Ndif == 1) ? !(Vcompare(vp, 0) & bt) : FALSE; + return (Ndif == 1) ? !(Vcompare(vp, 0) & bt) : false; else if (opc == OP_NE && opm == 1) - return (Ndif == 1) ? !(Vcompare(vp, 0) & bt) : TRUE; + return (Ndif == 1) ? !(Vcompare(vp, 0) & bt) : true; if (Type != TYPE_LIST) { if (opc == OP_GT || opc == OP_GE) @@ -544,15 +544,15 @@ bool ARRAY::FilTest(PGLOBAL g, PVAL valp, OPVAL opc, int opm) if (opm == 2) { for (i = 0; i < Nval; i++) if (Vcompare(vp, i) & bt) - return FALSE; + return false; - return TRUE; + return true; } else { // opm == 1 for (i = 0; i < Nval; i++) if (!(Vcompare(vp, i) & bt)) - return TRUE; + return true; - return FALSE; + return false; } // endif opm } // end of FilTest @@ -566,7 +566,7 @@ bool ARRAY::CanBeShort(void) int* To_Val = (int*)Valblk->GetMemp(); if (Type != TYPE_INT || !Ndif) - return FALSE; + return false; // Because the array is sorted, this is true if all the array // int values are in the range of SHORT values @@ -582,7 +582,7 @@ bool ARRAY::CanBeShort(void) int ARRAY::Convert(PGLOBAL g, int k, PVAL vp) { int i, prec = 0; - bool b = FALSE; + bool b = false; PMBV ovblk = Valblk; PVBLK ovblp = Vblp; @@ -619,7 +619,7 @@ int ARRAY::Convert(PGLOBAL g, int k, PVAL vp) if (((DTVAL*)Value)->SetFormat(g, vp)) return TYPE_ERROR; else - b = TRUE; // Sort the new array on date internal values + b = true; // Sort the new array on date internal values /*********************************************************************/ /* Do the actual conversion. */ @@ -706,7 +706,7 @@ void ARRAY::SetPrecision(PGLOBAL g, int p) /***********************************************************************/ /* Sort and eliminate distinct values from an array. */ /* Note: this is done by making a sorted index on distinct values. */ -/* Returns FALSE if Ok or TRUE in case of error. */ +/* Returns false if Ok or true in case of error. */ /***********************************************************************/ bool ARRAY::Sort(PGLOBAL g) { @@ -789,14 +789,14 @@ bool ARRAY::Sort(PGLOBAL g) Bot = -1; // For non optimized search Top = Ndif; // Find searches the whole array. - return FALSE; + return false; error: Nval = Ndif = 0; Valblk->Free(); PlgDBfree(Index); PlgDBfree(Offset); - return TRUE; + return true; } // end of Sort /***********************************************************************/ @@ -839,9 +839,9 @@ void *ARRAY::GetSortIndex(PGLOBAL g) /***********************************************************************/ /* Block filter testing for IN operator on Column/Array operands. */ -/* Here we call Find that returns TRUE if the value is in the array */ +/* Here we call Find that returns true if the value is in the array */ /* with X equal to the index of the found value in the array, or */ -/* FALSE if the value is not in the array with Inf and Sup being the */ +/* false if the value is not in the array with Inf and Sup being the */ /* indexes of the array values that are immediately below and over */ /* the not found value. This enables to restrict the array to the */ /* values that are between the min and max block values and to return */ @@ -854,9 +854,9 @@ int ARRAY::BlockTest(PGLOBAL, int opc, int opm, bool bin, bax, pin, pax, veq, all = (opm == 2); if (Ndif == 0) // Array is empty - // Return TRUE for ALL because it means that there are no item that + // Return true for ALL because it means that there are no item that // does not verify the condition, which is true indeed. - // Return FALSE for ANY because TRUE means that there is at least + // Return false for ANY because true means that there is at least // one item that verifies the condition, which is false. return (all) ? 2 : -2; else if (opc == OP_EQ && all && Ndif > 1) @@ -864,7 +864,7 @@ int ARRAY::BlockTest(PGLOBAL, int opc, int opm, else if (opc == OP_NE && !all && Ndif > 1) return 2; // else if (Ndif == 1) -// all = FALSE; +// all = false; // veq is true when all values in the block are equal switch (Type) { @@ -874,7 +874,7 @@ int ARRAY::BlockTest(PGLOBAL, int opc, int opm, case TYPE_SHORT: veq = *(short*)minp == *(short*)maxp; break; case TYPE_INT: veq = *(int*)minp == *(int*)maxp; break; case TYPE_DOUBLE: veq = *(double*)minp == *(double*)maxp; break; - default: veq = FALSE; // Error ? + default: veq = false; // Error ? } // endswitch type if (!s) @@ -898,7 +898,7 @@ int ARRAY::BlockTest(PGLOBAL, int opc, int opm, case OP_GT: return -1; break; } // endswitch opc - pax = (opc == OP_GE) ? (X < Ndif - 1) : TRUE; + pax = (opc == OP_GE) ? (X < Ndif - 1) : true; } else if (Inf == Bot) { // Max value is smaller than min list value return (opc == OP_LT || opc == OP_LE || opc == OP_NE) ? 1 : -1; @@ -924,7 +924,7 @@ int ARRAY::BlockTest(PGLOBAL, int opc, int opm, case OP_LT: return (s) ? -2 : -1; break; } // endswitch opc - pin = (opc == OP_LE) ? (X > 0) : TRUE; + pin = (opc == OP_LE) ? (X > 0) : true; } else if (Sup == Ndif) { // Min value is greater than max list value if (opc == OP_GT || opc == OP_GE || opc == OP_NE) @@ -956,7 +956,7 @@ int ARRAY::BlockTest(PGLOBAL, int opc, int opm, // the only possible overlaps between the array and the block are: // Array: +-------+ +-------+ +-------+ +-----+ // Block: +-----+ +---+ +------+ +--------+ - // TRUE: pax pin pax pin + // true: pax pin pax pin if (all) switch (opc) { case OP_GT: case OP_GE: return (pax) ? -1 : 0; break; @@ -1052,7 +1052,7 @@ void ARRAY::Print(PGLOBAL, char *ps, uint z) /***********************************************************************/ /* MULAR public constructor. */ /***********************************************************************/ -MULAR::MULAR(PGLOBAL g, int n) : CSORT(FALSE) +MULAR::MULAR(PGLOBAL g, int n) : CSORT(false) { Narray = n; Pars = (PARRAY*)PlugSubAlloc(g, NULL, n * sizeof(PARRAY)); @@ -1075,7 +1075,7 @@ int MULAR::Qcompare(int *i1, int *i2) /***********************************************************************/ /* Sort and eliminate distinct values from multiple arrays. */ /* Note: this is done by making a sorted index on distinct values. */ -/* Returns FALSE if Ok or TRUE in case of error. */ +/* Returns false if Ok or true in case of error. */ /***********************************************************************/ bool MULAR::Sort(PGLOBAL g) { @@ -1087,7 +1087,7 @@ bool MULAR::Sort(PGLOBAL g) for (n = 1; n < Narray; n++) if (Pars[n]->Nval != nval) { strcpy(g->Message, MSG(BAD_ARRAY_VAL)); - return TRUE; + return true; } // endif nval // Prepare non conservative sort with offet values @@ -1161,10 +1161,10 @@ bool MULAR::Sort(PGLOBAL g) Pars[n]->Top = ndif; // Find searches the whole array. } // endfor n - return FALSE; + return false; error: PlgDBfree(Index); PlgDBfree(Offset); - return TRUE; + return true; } // end of Sort diff --git a/storage/connect/array.h b/storage/connect/array.h index 6fb38ae6b47..dfc3638de8a 100644 --- a/storage/connect/array.h +++ b/storage/connect/array.h @@ -1,7 +1,7 @@ /**************** Array H Declares Source Code File (.H) ***************/ /* Name: ARRAY.H Version 3.1 */ /* */ -/* (C) Copyright to the author Olivier BERTRAND 2005-2014 */ +/* (C) Copyright to the author Olivier BERTRAND 2005-2017 */ /* */ /* This file contains the ARRAY and VALBASE derived classes declares. */ /***********************************************************************/ @@ -53,8 +53,8 @@ class DllExport ARRAY : public XOBJECT, public CSORT { // Array descblock using XOBJECT::GetIntValue; virtual void Reset(void) {Bot = -1;} virtual int Qcompare(int *, int *); - virtual bool Compare(PXOB) {assert(FALSE); return FALSE;} - virtual bool SetFormat(PGLOBAL, FORMAT&) {assert(FALSE); return FALSE;} + virtual bool Compare(PXOB) {assert(false); return false;} + virtual bool SetFormat(PGLOBAL, FORMAT&) {assert(false); return false;} //virtual int CheckSpcCol(PTDB, int) {return 0;} virtual void Print(PGLOBAL g, FILE *f, uint n); virtual void Print(PGLOBAL g, char *ps, uint z); diff --git a/storage/connect/colblk.cpp b/storage/connect/colblk.cpp index 80b405be041..58841387249 100644 --- a/storage/connect/colblk.cpp +++ b/storage/connect/colblk.cpp @@ -1,7 +1,7 @@ /************* Colblk C++ Functions Source Code File (.CPP) ************/ -/* Name: COLBLK.CPP Version 2.1 */ +/* Name: COLBLK.CPP Version 2.2 */ /* */ -/* (C) Copyright to the author Olivier BERTRAND 1998-2015 */ +/* (C) Copyright to the author Olivier BERTRAND 1998-2017 */ /* */ /* This file contains the COLBLK class functions. */ /***********************************************************************/ @@ -300,7 +300,7 @@ FIDBLK::FIDBLK(PCOLUMN cp, OPVAL op) : SPCBLK(cp), Op(op) #if defined(__WIN__) Format.Prec = 1; // Case insensitive #endif // __WIN__ - Constant = (!((PTDBASE)To_Tdb)->GetDef()->GetMultiple() && + Constant = (!To_Tdb->GetDef()->GetMultiple() && To_Tdb->GetAmType() != TYPE_AM_PLG && To_Tdb->GetAmType() != TYPE_AM_PLM); Fn = NULL; @@ -312,11 +312,11 @@ FIDBLK::FIDBLK(PCOLUMN cp, OPVAL op) : SPCBLK(cp), Op(op) /***********************************************************************/ void FIDBLK::ReadColumn(PGLOBAL g) { - if (Fn != ((PTDBASE)To_Tdb)->GetFile(g)) { + if (Fn != To_Tdb->GetFile(g)) { char filename[_MAX_PATH]; - Fn = ((PTDBASE)To_Tdb)->GetFile(g); - PlugSetPath(filename, Fn, ((PTDBASE)To_Tdb)->GetPath()); + Fn = To_Tdb->GetFile(g); + PlugSetPath(filename, Fn, To_Tdb->GetPath()); if (Op != OP_XX) { char buff[_MAX_PATH]; @@ -378,10 +378,8 @@ void PRTBLK::ReadColumn(PGLOBAL g) { if (Pname == NULL) { char *p; - PTDBASE tdbp = (PTDBASE)To_Tdb; - - Pname = tdbp->GetDef()->GetStringCatInfo(g, "partname", "?"); + Pname = To_Tdb->GetDef()->GetStringCatInfo(g, "partname", "?"); p = strrchr(Pname, '#'); Value->SetValue_psz((p) ? p + 1 : Pname); } // endif Pname diff --git a/storage/connect/connect.cc b/storage/connect/connect.cc index 460d47bcf62..a17c5dafa43 100644 --- a/storage/connect/connect.cc +++ b/storage/connect/connect.cc @@ -157,23 +157,22 @@ bool CntCheckDB(PGLOBAL g, PHC handler, const char *pathname) /* Returns valid: true if this is a table info. */ /***********************************************************************/ bool CntInfo(PGLOBAL g, PTDB tp, PXF info) - { - bool b; - PTDBDOS tdbp= (PTDBDOS)tp; +{ + if (tp) { + bool b = (tp->GetFtype() == RECFM_NAF); + PTDBDOS tdbp = b ? NULL : (PTDBDOS)tp; - if (tdbp) { - b= tdbp->GetFtype() != RECFM_NAF; - info->data_file_length= (b) ? (ulonglong)tdbp->GetFileLength(g) : 0; + info->data_file_length = (b) ? 0 : (ulonglong)tdbp->GetFileLength(g); - if (!b || info->data_file_length) - info->records= (unsigned)tdbp->Cardinality(g); -// info->records= (unsigned)tdbp->GetMaxSize(g); + if (b || info->data_file_length) + info->records= (unsigned)tp->Cardinality(g); +// info->records= (unsigned)tp->GetMaxSize(g); else info->records= 0; // info->mean_rec_length= tdbp->GetLrecl(); info->mean_rec_length= 0; - info->data_file_name= (b) ? tdbp->GetFile(g) : NULL; + info->data_file_name= (b) ? NULL : tdbp->GetFile(g); return true; } else { info->data_file_length= 0; @@ -183,7 +182,7 @@ bool CntInfo(PGLOBAL g, PTDB tp, PXF info) return false; } // endif tdbp - } // end of CntInfo +} // end of CntInfo /***********************************************************************/ /* GetTDB: Get the table description block of a CONNECT table. */ @@ -332,9 +331,9 @@ bool CntOpenTable(PGLOBAL g, PTDB tdbp, MODE mode, char *c1, char *c2, } // endfor colp // Attach the updated columns list to the main table - ((PTDBASE)tdbp)->SetSetCols(utp->GetColumns()); + tdbp->SetSetCols(utp->GetColumns()); } else if (tdbp && mode == MODE_INSERT) - ((PTDBASE)tdbp)->SetSetCols(tdbp->GetColumns()); + tdbp->SetSetCols(tdbp->GetColumns()); // Now do open the physical table if (trace) @@ -343,7 +342,7 @@ bool CntOpenTable(PGLOBAL g, PTDB tdbp, MODE mode, char *c1, char *c2, //tdbp->SetMode(mode); - if (del/* && ((PTDBASE)tdbp)->GetFtype() != RECFM_NAF*/) { + if (del/* && (tdbp->GetFtype() != RECFM_NAF*/) { // To avoid erasing the table when doing a partial delete // make a fake Next // PDOSDEF ddp= new(g) DOSDEF; @@ -436,7 +435,7 @@ RCODE CntReadNext(PGLOBAL g, PTDB tdbp) if (!tdbp) return RC_FX; - else if (((PTDBASE)tdbp)->GetKindex()) { + else if (tdbp->GetKindex()) { // Reading sequencially an indexed table. This happens after the // handler function records_in_range was called and MySQL decides // to quit using the index (!!!) Drop the index. @@ -483,7 +482,7 @@ RCODE CntWriteRow(PGLOBAL g, PTDB tdbp) { RCODE rc; PCOL colp; - PTDBASE tp= (PTDBASE)tdbp; +//PTDBASE tp= (PTDBASE)tdbp; if (!tdbp) return RC_FX; @@ -501,13 +500,13 @@ RCODE CntWriteRow(PGLOBAL g, PTDB tdbp) } // endif rc // Store column values in table write buffer(s) - for (colp= tp->GetSetCols(); colp; colp= colp->GetNext()) + for (colp= tdbp->GetSetCols(); colp; colp= colp->GetNext()) if (!colp->GetColUse(U_VIRTUAL)) colp->WriteColumn(g); - if (tp->IsIndexed()) + if (tdbp->IsIndexed()) // Index values must be sorted before updating - rc= (RCODE)((PTDBDOS)tp)->GetTxfp()->StoreValues(g, true); + rc= (RCODE)((PTDBDOS)tdbp)->GetTxfp()->StoreValues(g, true); else // Return result code from write operation rc= (RCODE)tdbp->WriteDB(g); @@ -535,7 +534,7 @@ RCODE CntUpdateRow(PGLOBAL g, PTDB tdbp) RCODE CntDeleteRow(PGLOBAL g, PTDB tdbp, bool all) { RCODE rc; - PTDBASE tp= (PTDBASE)tdbp; +//PTDBASE tp= (PTDBASE)tdbp; if (!tdbp || tdbp->GetMode() != MODE_DELETE) return RC_FX; @@ -543,16 +542,16 @@ RCODE CntDeleteRow(PGLOBAL g, PTDB tdbp, bool all) return RC_NF; if (all) { - if (((PTDBASE)tdbp)->GetDef()->Indexable()) + if (tdbp->GetDef()->Indexable()) ((PTDBDOS)tdbp)->Cardinal= 0; // Note: if all, this call will be done when closing the table rc= (RCODE)tdbp->DeleteDB(g, RC_FX); -//} else if (tp->GetKindex() && !tp->GetKindex()->IsSorted() && -// tp->Txfp->GetAmType() != TYPE_AM_DBF) { - } else if(tp->IsIndexed()) { +//} else if (tdbp->GetKindex() && !((PTDBASE)tdbp)->GetKindex()->IsSorted() && +// ((PTDBASE)tdbp)->Txfp->GetAmType() != TYPE_AM_DBF) { + } else if(tdbp->IsIndexed()) { // Index values must be sorted before updating - rc= (RCODE)((PTDBDOS)tp)->GetTxfp()->StoreValues(g, false); + rc= (RCODE)((PTDBDOS)tdbp)->GetTxfp()->StoreValues(g, false); } else // Return result code from delete operation rc= (RCODE)tdbp->DeleteDB(g, RC_OK); @@ -565,7 +564,7 @@ RCODE CntDeleteRow(PGLOBAL g, PTDB tdbp, bool all) int CntCloseTable(PGLOBAL g, PTDB tdbp, bool nox, bool abort) { int rc= RC_OK; - TDBASE *tbxp= (PTDBASE)tdbp; +//TDBASE *tbxp= (PTDBASE)tdbp; if (!tdbp) return rc; // Nothing to do @@ -581,13 +580,13 @@ int CntCloseTable(PGLOBAL g, PTDB tdbp, bool nox, bool abort) tdbp, tdbp->GetMode(), nox, abort); if (tdbp->GetMode() == MODE_DELETE && tdbp->GetUse() == USE_OPEN) { - if (tbxp->IsIndexed()) + if (tdbp->IsIndexed()) rc= ((PTDBDOS)tdbp)->GetTxfp()->DeleteSortedRows(g); if (!rc) rc= tdbp->DeleteDB(g, RC_EF); // Specific A.M. delete routine - } else if (tbxp->GetMode() == MODE_UPDATE && tbxp->IsIndexed()) + } else if (tdbp->GetMode() == MODE_UPDATE && tdbp->IsIndexed()) rc= ((PTDBDOX)tdbp)->Txfp->UpdateSortedRows(g); switch(rc) { @@ -595,7 +594,7 @@ int CntCloseTable(PGLOBAL g, PTDB tdbp, bool nox, bool abort) abort= true; break; case RC_INFO: - PushWarning(g, tbxp); + PushWarning(g, tdbp); break; } // endswitch rc @@ -631,11 +630,13 @@ int CntCloseTable(PGLOBAL g, PTDB tdbp, bool nox, bool abort) if (trace > 1) printf("About to reset opt\n"); - // Make all the eventual indexes - tbxp= (TDBDOX*)tdbp; - tbxp->ResetKindex(g, NULL); - tbxp->SetKey_Col(NULL); - rc= tbxp->ResetTableOpt(g, true, tbxp->GetDef()->Indexable() == 1); + if (!tdbp->IsRemote()) { + // Make all the eventual indexes + PTDBDOX tbxp = (PTDBDOX)tdbp; + tbxp->ResetKindex(g, NULL); + tbxp->SetKey_Col(NULL); + rc = tbxp->ResetTableOpt(g, true, tbxp->GetDef()->Indexable() == 1); + } // endif remote err: if (trace > 1) @@ -657,10 +658,10 @@ int CntIndexInit(PGLOBAL g, PTDB ptdb, int id, bool sorted) if (!ptdb) return -1; - else if (!((PTDBASE)ptdb)->GetDef()->Indexable()) { + else if (!ptdb->GetDef()->Indexable()) { sprintf(g->Message, MSG(TABLE_NO_INDEX), ptdb->GetName()); return 0; - } else if (((PTDBASE)ptdb)->GetDef()->Indexable() == 3) { + } else if (ptdb->GetDef()->Indexable() == 3) { return 1; } else tdbp= (PTDBDOX)ptdb; @@ -745,7 +746,7 @@ RCODE CntIndexRead(PGLOBAL g, PTDB ptdb, OPVAL op, if (!ptdb) return RC_FX; else - x= ((PTDBASE)ptdb)->GetDef()->Indexable(); + x= ptdb->GetDef()->Indexable(); if (!x) { sprintf(g->Message, MSG(TABLE_NO_INDEX), ptdb->GetName()); @@ -875,7 +876,7 @@ int CntIndexRange(PGLOBAL g, PTDB ptdb, const uchar* *key, uint *len, if (!ptdb) return -1; - x= ((PTDBASE)ptdb)->GetDef()->Indexable(); + x= ptdb->GetDef()->Indexable(); if (!x) { sprintf(g->Message, MSG(TABLE_NO_INDEX), ptdb->GetName()); diff --git a/storage/connect/domdoc.cpp b/storage/connect/domdoc.cpp index eb9660b439d..1622ec16c68 100644 --- a/storage/connect/domdoc.cpp +++ b/storage/connect/domdoc.cpp @@ -116,7 +116,9 @@ bool DOMDOC::ParseFile(PGLOBAL g, char *fn) // Parse an in memory document char *xdoc = GetMemDoc(g, fn); - b = (xdoc) ? (bool)Docp->loadXML((_bstr_t)xdoc) : false; + // This is not equivalent to load for UTF8 characters + // It is why get node content is not the same + b = (xdoc) ? (bool)Docp->loadXML((_bstr_t)xdoc) : false; } else // Load the document b = (bool)Docp->load((_bstr_t)fn); @@ -266,6 +268,7 @@ DOMNODE::DOMNODE(PXDOC dp, MSXML2::IXMLDOMNodePtr np) : XMLNODE(dp) Nodep = np; Ws = NULL; Len = 0; + Zip = (bool)dp->zip; } // end of DOMNODE constructor /******************************************************************/ @@ -316,8 +319,10 @@ RCODE DOMNODE::GetContent(PGLOBAL g, char *buf, int len) RCODE rc = RC_OK; // Nodep can be null for a missing HTML table column - if (Nodep) { - if (!WideCharToMultiByte(CP_UTF8, 0, Nodep->text, -1, + if (Nodep) { + if (Zip) { + strcpy(buf, Nodep->text); + } else if (!WideCharToMultiByte(CP_UTF8, 0, Nodep->text, -1, buf, len, NULL, NULL)) { DWORD lsr = GetLastError(); diff --git a/storage/connect/domdoc.h b/storage/connect/domdoc.h index cfec98a9422..7f269002d59 100644 --- a/storage/connect/domdoc.h +++ b/storage/connect/domdoc.h @@ -93,6 +93,7 @@ class DOMNODE : public XMLNODE { char Name[64]; WCHAR *Ws; int Len; + bool Zip; }; // end of class DOMNODE /******************************************************************/ diff --git a/storage/connect/filamap.cpp b/storage/connect/filamap.cpp index 94c562a9981..8fffaca3d06 100644 --- a/storage/connect/filamap.cpp +++ b/storage/connect/filamap.cpp @@ -5,7 +5,7 @@ /* */ /* COPYRIGHT: */ /* ---------- */ -/* (C) Copyright to the author Olivier BERTRAND 2005-2015 */ +/* (C) Copyright to the author Olivier BERTRAND 2005-2017 */ /* */ /* WHAT THIS PROGRAM DOES: */ /* ----------------------- */ @@ -45,6 +45,7 @@ #include "maputil.h" #include "filamap.h" #include "tabdos.h" +#include "tabfmt.h" /* --------------------------- Class MAPFAM -------------------------- */ @@ -322,17 +323,20 @@ int MAPFAM::ReadBuffer(PGLOBAL g) int rc, len; // Are we at the end of the memory - if (Mempos >= Top) + if (Mempos >= Top) { if ((rc = GetNext(g)) != RC_OK) return rc; + else if (Tdbp->GetAmType() == TYPE_AM_CSV && ((PTDBCSV)Tdbp)->Header) + if ((rc = SkipRecord(g, true)) != RC_OK) + return rc; + + } // endif Mempos if (!Placed) { /*******************************************************************/ /* Record file position in case of UPDATE or DELETE. */ /*******************************************************************/ - int rc; - next: Fpos = Mempos; CurBlk = (int)Rows++; diff --git a/storage/connect/filamdbf.cpp b/storage/connect/filamdbf.cpp index a4557facbd8..9feb61d7d61 100644 --- a/storage/connect/filamdbf.cpp +++ b/storage/connect/filamdbf.cpp @@ -5,7 +5,7 @@ /* */ /* COPYRIGHT: */ /* ---------- */ -/* (C) Copyright to the author Olivier BERTRAND 2005-2015 */ +/* (C) Copyright to the author Olivier BERTRAND 2005-2017 */ /* */ /* WHAT THIS PROGRAM DOES: */ /* ----------------------- */ @@ -281,15 +281,25 @@ PQRYRES DBFColumns(PGLOBAL g, char *dp, const char *fn, bool info) /************************************************************************/ switch (thisfield.Type) { case 'C': // Characters - case 'L': // Logical 'T' or 'F' - type = TYPE_STRING; + case 'L': // Logical 'T' or 'F' or space + type = TYPE_STRING; + break; + case 'M': // Memo a .DBT block number + case 'B': // Binary a .DBT block number + case 'G': // Ole a .DBT block number + type = TYPE_STRING; break; + //case 'I': // Long + //case '+': // Autoincrement + // type = TYPE_INT; + // break; case 'N': type = (thisfield.Decimals) ? TYPE_DOUBLE : (len > 10) ? TYPE_BIGINT : TYPE_INT; break; - case 'F': - type = TYPE_DOUBLE; + case 'F': // Float + //case 'O': // Double + type = TYPE_DOUBLE; break; case 'D': type = TYPE_DATE; // Is this correct ??? @@ -441,6 +451,7 @@ int DBFFAM::Cardinality(PGLOBAL g) if (Accept) { Lrecl = rln; + Blksize = Nrec * rln; PushWarning(g, Tdbp); } else return -1; @@ -582,6 +593,7 @@ bool DBFFAM::AllocateBuffer(PGLOBAL g) if (Accept) { Lrecl = reclen; + Blksize = Nrec * Lrecl; PushWarning(g, Tdbp); } else return true; @@ -598,7 +610,7 @@ bool DBFFAM::AllocateBuffer(PGLOBAL g) header->Filedate[1] = datm->tm_mon + 1; header->Filedate[2] = datm->tm_mday; header->SetHeadlen((ushort)hlen); - header->SetReclen((ushort)reclen); + header->SetReclen(reclen); descp = (DESCRIPTOR*)header; // Currently only standard Xbase types are supported @@ -664,6 +676,7 @@ bool DBFFAM::AllocateBuffer(PGLOBAL g) if (Accept) { Lrecl = header.Reclen(); + Blksize = Nrec * Lrecl; PushWarning(g, Tdbp); } else return true; @@ -956,6 +969,7 @@ int DBMFAM::Cardinality(PGLOBAL g) if (Accept) { Lrecl = rln; + Blksize = Nrec * Lrecl; PushWarning(g, Tdbp); } else return -1; @@ -1008,6 +1022,7 @@ bool DBMFAM::AllocateBuffer(PGLOBAL g) if (Accept) { Lrecl = hp->Reclen(); + Blksize = Nrec * Lrecl; PushWarning(g, Tdbp); } else return true; diff --git a/storage/connect/filamgz.cpp b/storage/connect/filamgz.cpp index 07242ea633c..dc6f277ee27 100644 --- a/storage/connect/filamgz.cpp +++ b/storage/connect/filamgz.cpp @@ -724,20 +724,20 @@ void ZBKFAM::Rewind(void) /***********************************************************************/ /* Constructors. */ /***********************************************************************/ -ZIXFAM::ZIXFAM(PDOSDEF tdp) : ZBKFAM(tdp) +GZXFAM::GZXFAM(PDOSDEF tdp) : ZBKFAM(tdp) { //Block = tdp->GetBlock(); //Last = tdp->GetLast(); Nrec = (tdp->GetElemt()) ? tdp->GetElemt() : DOS_BUFF_LEN; Blksize = Nrec * Lrecl; - } // end of ZIXFAM standard constructor + } // end of GZXFAM standard constructor /***********************************************************************/ /* ZIX Cardinality: returns table cardinality in number of rows. */ /* This function can be called with a null argument to test the */ /* availability of Cardinality implementation (1 yes, 0 no). */ /***********************************************************************/ -int ZIXFAM::Cardinality(PGLOBAL g) +int GZXFAM::Cardinality(PGLOBAL g) { if (Last) return (g) ? (int)((Block - 1) * Nrec + Last) : 1; @@ -750,7 +750,7 @@ int ZIXFAM::Cardinality(PGLOBAL g) /* Allocate the line buffer. For mode Delete a bigger buffer has to */ /* be allocated because is it also used to move lines into the file. */ /***********************************************************************/ -bool ZIXFAM::AllocateBuffer(PGLOBAL g) +bool GZXFAM::AllocateBuffer(PGLOBAL g) { Buflen = Blksize; To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen); @@ -788,7 +788,7 @@ bool ZIXFAM::AllocateBuffer(PGLOBAL g) /***********************************************************************/ /* ReadBuffer: Read one line from a compressed text file. */ /***********************************************************************/ -int ZIXFAM::ReadBuffer(PGLOBAL g) +int GZXFAM::ReadBuffer(PGLOBAL g) { int n, rc = RC_OK; @@ -850,7 +850,7 @@ int ZIXFAM::ReadBuffer(PGLOBAL g) /* WriteDB: Data Base write routine for ZDOS access method. */ /* Update is not possible without using a temporary file (NIY). */ /***********************************************************************/ -int ZIXFAM::WriteBuffer(PGLOBAL g) +int GZXFAM::WriteBuffer(PGLOBAL g) { /*********************************************************************/ /* In Insert mode, blocs are added sequentialy to the file end. */ diff --git a/storage/connect/filamgz.h b/storage/connect/filamgz.h index d667fdddcc2..7a00c0d4bc7 100644 --- a/storage/connect/filamgz.h +++ b/storage/connect/filamgz.h @@ -12,7 +12,7 @@ typedef class GZFAM *PGZFAM; typedef class ZBKFAM *PZBKFAM; -typedef class ZIXFAM *PZIXFAM; +typedef class GZXFAM *PZIXFAM; typedef class ZLBFAM *PZLBFAM; /***********************************************************************/ @@ -101,16 +101,16 @@ class DllExport ZBKFAM : public GZFAM { /* length files compressed using the gzip library functions. */ /* The file is always accessed by block. */ /***********************************************************************/ -class DllExport ZIXFAM : public ZBKFAM { +class DllExport GZXFAM : public ZBKFAM { public: // Constructor - ZIXFAM(PDOSDEF tdp); - ZIXFAM(PZIXFAM txfp) : ZBKFAM(txfp) {} + GZXFAM(PDOSDEF tdp); + GZXFAM(PZIXFAM txfp) : ZBKFAM(txfp) {} // Implementation virtual int GetNextPos(void) {return 0;} virtual PTXF Duplicate(PGLOBAL g) - {return (PTXF)new(g) ZIXFAM(this);} + {return (PTXF)new(g) GZXFAM(this);} // Methods virtual int Cardinality(PGLOBAL g); @@ -120,7 +120,7 @@ class DllExport ZIXFAM : public ZBKFAM { protected: // No additional Members - }; // end of class ZIXFAM + }; // end of class GZXFAM /***********************************************************************/ /* This is the DOS/UNIX Access Method class declaration for PlugDB */ diff --git a/storage/connect/filamzip.cpp b/storage/connect/filamzip.cpp index 65013e170e4..3d157da5e87 100644 --- a/storage/connect/filamzip.cpp +++ b/storage/connect/filamzip.cpp @@ -1,11 +1,11 @@ /*********** File AM Zip C++ Program Source Code File (.CPP) ***********/ /* PROGRAM NAME: FILAMZIP */ /* ------------- */ -/* Version 1.0 */ +/* Version 1.1 */ /* */ /* COPYRIGHT: */ /* ---------- */ -/* (C) Copyright to the author Olivier BERTRAND 2016 */ +/* (C) Copyright to the author Olivier BERTRAND 2016-2017 */ /* */ /* WHAT THIS PROGRAM DOES: */ /* ----------------------- */ @@ -19,13 +19,16 @@ #include "my_global.h" #if !defined(__WIN__) #if defined(UNIX) +#include <fnmatch.h> #include <errno.h> +#include <dirent.h> #include <unistd.h> #else // !UNIX #include <io.h> #endif // !UNIX #include <fcntl.h> #endif // !__WIN__ +#include <time.h> /***********************************************************************/ /* Include application header files: */ @@ -40,12 +43,346 @@ //#include "tabzip.h" #include "filamzip.h" +#define WRITEBUFFERSIZE (16384) + +bool ZipLoadFile(PGLOBAL g, char *zfn, char *fn, char *entry, bool append, bool mul); + +/***********************************************************************/ +/* Compress a file in zip when creating a table. */ +/***********************************************************************/ +static bool ZipFile(PGLOBAL g, ZIPUTIL *zutp, char *fn, char *entry, char *buf) +{ + int rc = RC_OK, size_read, size_buf = WRITEBUFFERSIZE; + FILE *fin; + + if (zutp->addEntry(g, entry)) + return true; + else if (!(fin = fopen(fn, "rb"))) { + sprintf(g->Message, "error in opening %s for reading", fn); + return true; + } // endif fin + + do { + size_read = (int)fread(buf, 1, size_buf, fin); + + if (size_read < size_buf && feof(fin) == 0) { + sprintf(g->Message, "error in reading %s", fn); + rc = RC_FX; + } // endif size_read + + if (size_read > 0) { + rc = zutp->writeEntry(g, buf, size_read); + + if (rc == RC_FX) + sprintf(g->Message, "error in writing %s in the zipfile", fn); + + } // endif size_read + + } while (rc == RC_OK && size_read > 0); + + fclose(fin); + zutp->closeEntry(); + return rc != RC_OK; +} // end of ZipFile + +/***********************************************************************/ +/* Find and Compress several files in zip when creating a table. */ +/***********************************************************************/ +static bool ZipFiles(PGLOBAL g, ZIPUTIL *zutp, char *pat, char *buf) +{ + char filename[_MAX_PATH]; + int rc; + + /*********************************************************************/ + /* pat is a multiple file name with wildcard characters */ + /*********************************************************************/ + strcpy(filename, pat); + +#if defined(__WIN__) + char drive[_MAX_DRIVE], direc[_MAX_DIR]; + WIN32_FIND_DATA FileData; + HANDLE hSearch; + + _splitpath(filename, drive, direc, NULL, NULL); + + // Start searching files in the target directory. + hSearch = FindFirstFile(filename, &FileData); + + if (hSearch == INVALID_HANDLE_VALUE) { + rc = GetLastError(); + + if (rc != ERROR_FILE_NOT_FOUND) { + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, GetLastError(), 0, (LPTSTR)&filename, sizeof(filename), NULL); + sprintf(g->Message, MSG(BAD_FILE_HANDLE), filename); + return true; + } else { + strcpy(g->Message, "Cannot find any file to load"); + return true; + } // endif rc + + } // endif hSearch + + while (true) { + if (!(FileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { + strcat(strcat(strcpy(filename, drive), direc), FileData.cFileName); + + if (ZipFile(g, zutp, filename, FileData.cFileName, buf)) { + FindClose(hSearch); + return true; + } // endif ZipFile + + } // endif dwFileAttributes + + if (!FindNextFile(hSearch, &FileData)) { + rc = GetLastError(); + + if (rc != ERROR_NO_MORE_FILES) { + sprintf(g->Message, MSG(NEXT_FILE_ERROR), rc); + FindClose(hSearch); + return true; + } // endif rc + + break; + } // endif FindNextFile + + } // endwhile n + + // Close the search handle. + if (!FindClose(hSearch)) { + strcpy(g->Message, MSG(SRCH_CLOSE_ERR)); + return true; + } // endif FindClose + +#else // !__WIN__ + struct stat fileinfo; + char fn[FN_REFLEN], direc[FN_REFLEN], pattern[FN_HEADLEN], ftype[FN_EXTLEN]; + DIR *dir; + struct dirent *entry; + + _splitpath(filename, NULL, direc, pattern, ftype); + strcat(pattern, ftype); + + // Start searching files in the target directory. + if (!(dir = opendir(direc))) { + sprintf(g->Message, MSG(BAD_DIRECTORY), direc, strerror(errno)); + return true; + } // endif dir + + while ((entry = readdir(dir))) { + strcat(strcpy(fn, direc), entry->d_name); + + if (lstat(fn, &fileinfo) < 0) { + sprintf(g->Message, "%s: %s", fn, strerror(errno)); + return true; + } else if (!S_ISREG(fileinfo.st_mode)) + continue; // Not a regular file (should test for links) + + /*******************************************************************/ + /* Test whether the file name matches the table name filter. */ + /*******************************************************************/ + if (fnmatch(pattern, entry->d_name, 0)) + continue; // Not a match + + strcat(strcpy(filename, direc), entry->d_name); + + if (ZipFile(g, zutp, filename, entry->d_name, buf)) { + closedir(dir); + return true; + } // endif ZipFile + + } // endwhile readdir + + // Close the dir handle. + closedir(dir); +#endif // !__WIN__ + + return false; +} // end of ZipFiles + +/***********************************************************************/ +/* Load and Compress a file in zip when creating a table. */ +/***********************************************************************/ +bool ZipLoadFile(PGLOBAL g, char *zfn, char *fn, char *entry, bool append, bool mul) +{ + char *buf; + bool err; + ZIPUTIL *zutp = new(g) ZIPUTIL(NULL); + + if (zutp->open(g, zfn, append)) + return true; + + buf = (char*)PlugSubAlloc(g, NULL, WRITEBUFFERSIZE); + + if (mul) + err = ZipFiles(g, zutp, fn, buf); + else + err = ZipFile(g, zutp, fn, entry, buf); + + zutp->close(); + return err; +} // end of ZipLoadFile + /* -------------------------- class ZIPUTIL -------------------------- */ /***********************************************************************/ /* Constructors. */ /***********************************************************************/ -ZIPUTIL::ZIPUTIL(PSZ tgt, bool mul) +ZIPUTIL::ZIPUTIL(PSZ tgt) +{ + zipfile = NULL; + target = tgt; + fp = NULL; + entryopen = false; +} // end of ZIPUTIL standard constructor + +#if 0 +ZIPUTIL::ZIPUTIL(ZIPUTIL *zutp) +{ + zipfile = zutp->zipfile; + target = zutp->target; + fp = zutp->fp; + entryopen = zutp->entryopen; +} // end of UNZIPUTL copy constructor +#endif // 0 + +/***********************************************************************/ +/* Fill the zip time structure */ +/* param: tmZip time structure to be filled */ +/***********************************************************************/ +void ZIPUTIL::getTime(tm_zip& tmZip) +{ + time_t rawtime; + time(&rawtime); + struct tm *timeinfo = localtime(&rawtime); + tmZip.tm_sec = timeinfo->tm_sec; + tmZip.tm_min = timeinfo->tm_min; + tmZip.tm_hour = timeinfo->tm_hour; + tmZip.tm_mday = timeinfo->tm_mday; + tmZip.tm_mon = timeinfo->tm_mon; + tmZip.tm_year = timeinfo->tm_year; +} // end of getTime + +/***********************************************************************/ +/* open a zip file for deflate. */ +/* param: filename path and the filename of the zip file to open. */ +/* append: set true to append the zip file */ +/* return: true if open, false otherwise. */ +/***********************************************************************/ +bool ZIPUTIL::open(PGLOBAL g, char *filename, bool append) +{ + if (!zipfile && !(zipfile = zipOpen64(filename, + append ? APPEND_STATUS_ADDINZIP + : APPEND_STATUS_CREATE))) + sprintf(g->Message, "Zipfile open error on %s", filename); + + return (zipfile == NULL); +} // end of open + +/***********************************************************************/ +/* Close the zip file. */ +/***********************************************************************/ +void ZIPUTIL::close() +{ + if (zipfile) { + closeEntry(); + zipClose(zipfile, 0); + zipfile = NULL; + } // endif zipfile + +} // end of close + +/***********************************************************************/ +/* OpenTableFile: Open a DOS/UNIX table file from a ZIP file. */ +/***********************************************************************/ +bool ZIPUTIL::OpenTable(PGLOBAL g, MODE mode, char *fn, bool append) +{ + /*********************************************************************/ + /* The file will be compressed. */ + /*********************************************************************/ + if (mode == MODE_INSERT) { + bool b = open(g, fn, append); + + if (!b) { + if (addEntry(g, target)) + return true; + + /*****************************************************************/ + /* Link a Fblock. This make possible to automatically close it */ + /* in case of error g->jump. */ + /*****************************************************************/ + PDBUSER dbuserp = (PDBUSER)g->Activityp->Aptr; + + fp = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK)); + fp->Type = TYPE_FB_ZIP; + fp->Fname = PlugDup(g, fn); + fp->Next = dbuserp->Openlist; + dbuserp->Openlist = fp; + fp->Count = 1; + fp->Length = 0; + fp->Memory = NULL; + fp->Mode = mode; + fp->File = this; + fp->Handle = 0; + } else + return true; + + } else { + strcpy(g->Message, "Only INSERT mode supported for ZIPPING files"); + return true; + } // endif mode + + return false; +} // end of OpenTableFile + +/***********************************************************************/ +/* Add target in zip file. */ +/***********************************************************************/ +bool ZIPUTIL::addEntry(PGLOBAL g, char *entry) +{ + //?? we dont need the stinking time + zip_fileinfo zi = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + + getTime(zi.tmz_date); + target = entry; + + int err = zipOpenNewFileInZip(zipfile, target, &zi, + NULL, 0, NULL, 0, NULL, Z_DEFLATED, Z_DEFAULT_COMPRESSION); + + return !(entryopen = (err == ZIP_OK)); +} // end of addEntry + +/***********************************************************************/ +/* writeEntry: Deflate the buffer to the zip file. */ +/***********************************************************************/ +int ZIPUTIL::writeEntry(PGLOBAL g, char *buf, int len) +{ + if (zipWriteInFileInZip(zipfile, buf, len) < 0) { + sprintf(g->Message, "Error writing %s in the zipfile", target); + return RC_FX; + } // endif zipWriteInFileInZip + + return RC_OK; +} // end of writeEntry + +/***********************************************************************/ +/* Close the zip file. */ +/***********************************************************************/ +void ZIPUTIL::closeEntry() +{ + if (entryopen) { + zipCloseFileInZip(zipfile); + entryopen = false; + } // endif entryopen + +} // end of closeEntry + +/* ------------------------- class UNZIPUTL -------------------------- */ + +/***********************************************************************/ +/* Constructors. */ +/***********************************************************************/ +UNZIPUTL::UNZIPUTL(PSZ tgt, bool mul) { zipfile = NULL; target = tgt; @@ -62,10 +399,10 @@ ZIPUTIL::ZIPUTIL(PSZ tgt, bool mul) #else for (int i = 0; i < 256; ++i) mapCaseTable[i] = i; #endif -} // end of ZIPUTIL standard constructor +} // end of UNZIPUTL standard constructor #if 0 -ZIPUTIL::ZIPUTIL(PZIPUTIL zutp) +UNZIPUTL::UNZIPUTL(PZIPUTIL zutp) { zipfile = zutp->zipfile; target = zutp->target; @@ -74,14 +411,14 @@ ZIPUTIL::ZIPUTIL(PZIPUTIL zutp) entryopen = zutp->entryopen; multiple = zutp->multiple; for (int i = 0; i < 256; ++i) mapCaseTable[i] = zutp->mapCaseTable[i]; -} // end of ZIPUTIL copy constructor +} // end of UNZIPUTL copy constructor #endif // 0 /***********************************************************************/ /* This code is the copyright property of Alessandro Felice Cantatore. */ /* http://xoomer.virgilio.it/acantato/dev/wildcard/wildmatch.html */ /***********************************************************************/ -bool ZIPUTIL::WildMatch(PSZ pat, PSZ str) { +bool UNZIPUTL::WildMatch(PSZ pat, PSZ str) { PSZ s, p; bool star = FALSE; @@ -97,7 +434,7 @@ loopStart: if (!*++pat) return TRUE; goto loopStart; default: - if (mapCaseTable[*s] != mapCaseTable[*p]) + if (mapCaseTable[(uchar)*s] != mapCaseTable[(uchar)*p]) goto starCheck; break; } /* endswitch */ @@ -116,7 +453,7 @@ starCheck: /* param: filename path and the filename of the zip file to open. */ /* return: true if open, false otherwise. */ /***********************************************************************/ -bool ZIPUTIL::open(PGLOBAL g, char *filename) +bool UNZIPUTL::open(PGLOBAL g, char *filename) { if (!zipfile && !(zipfile = unzOpen64(filename))) sprintf(g->Message, "Zipfile open error on %s", filename); @@ -127,7 +464,7 @@ bool ZIPUTIL::open(PGLOBAL g, char *filename) /***********************************************************************/ /* Close the zip file. */ /***********************************************************************/ -void ZIPUTIL::close() +void UNZIPUTL::close() { if (zipfile) { closeEntry(); @@ -140,7 +477,7 @@ void ZIPUTIL::close() /***********************************************************************/ /* Find next entry matching target pattern. */ /***********************************************************************/ -int ZIPUTIL::findEntry(PGLOBAL g, bool next) +int UNZIPUTL::findEntry(PGLOBAL g, bool next) { int rc; @@ -151,7 +488,7 @@ int ZIPUTIL::findEntry(PGLOBAL g, bool next) if (rc == UNZ_END_OF_LIST_OF_FILE) return RC_EF; else if (rc != UNZ_OK) { - sprintf(g->Message, "unzGoToNextFile rc = ", rc); + sprintf(g->Message, "unzGoToNextFile rc = %d", rc); return RC_FX; } // endif rc @@ -183,7 +520,7 @@ int ZIPUTIL::findEntry(PGLOBAL g, bool next) /***********************************************************************/ /* Get the next used entry. */ /***********************************************************************/ -int ZIPUTIL::nextEntry(PGLOBAL g) +int UNZIPUTL::nextEntry(PGLOBAL g) { if (multiple) { int rc; @@ -206,7 +543,7 @@ int ZIPUTIL::nextEntry(PGLOBAL g) /***********************************************************************/ /* OpenTableFile: Open a DOS/UNIX table file from a ZIP file. */ /***********************************************************************/ -bool ZIPUTIL::OpenTable(PGLOBAL g, MODE mode, char *fn) +bool UNZIPUTL::OpenTable(PGLOBAL g, MODE mode, char *fn) { /*********************************************************************/ /* The file will be decompressed into virtual memory. */ @@ -261,14 +598,14 @@ bool ZIPUTIL::OpenTable(PGLOBAL g, MODE mode, char *fn) fp->Memory = memory; fp->Mode = mode; fp->File = this; - fp->Handle = NULL; + fp->Handle = 0; } // endif fp } else return true; } else { - strcpy(g->Message, "Only READ mode supported for ZIP files"); + strcpy(g->Message, "Only READ mode supported for ZIPPED tables"); return true; } // endif mode @@ -278,7 +615,7 @@ bool ZIPUTIL::OpenTable(PGLOBAL g, MODE mode, char *fn) /***********************************************************************/ /* Open target in zip file. */ /***********************************************************************/ -bool ZIPUTIL::openEntry(PGLOBAL g) +bool UNZIPUTL::openEntry(PGLOBAL g) { int rc; @@ -297,7 +634,7 @@ bool ZIPUTIL::openEntry(PGLOBAL g) memory = new char[size + 1]; if ((rc = unzReadCurrentFile(zipfile, memory, size)) < 0) { - sprintf(g->Message, "unzReadCurrentFile rc = ", rc); + sprintf(g->Message, "unzReadCurrentFile rc = %d", rc); unzCloseCurrentFile(zipfile); free(memory); memory = NULL; @@ -316,7 +653,7 @@ bool ZIPUTIL::openEntry(PGLOBAL g) /***********************************************************************/ /* Close the zip file. */ /***********************************************************************/ -void ZIPUTIL::closeEntry() +void UNZIPUTL::closeEntry() { if (entryopen) { unzCloseCurrentFile(zipfile); @@ -330,36 +667,29 @@ void ZIPUTIL::closeEntry() } // end of closeEntry -/* -------------------------- class ZIPFAM --------------------------- */ +/* -------------------------- class UNZFAM --------------------------- */ /***********************************************************************/ /* Constructors. */ /***********************************************************************/ -ZIPFAM::ZIPFAM(PDOSDEF tdp) : MAPFAM(tdp) +UNZFAM::UNZFAM(PDOSDEF tdp) : MAPFAM(tdp) { zutp = NULL; target = tdp->GetEntry(); mul = tdp->GetMul(); -} // end of ZIPFAM standard constructor - -ZIPFAM::ZIPFAM(PZIPFAM txfp) : MAPFAM(txfp) -{ - zutp = txfp->zutp; - target = txfp->target; - mul = txfp->mul; -} // end of ZIPFAM copy constructor +} // end of UNZFAM standard constructor -ZIPFAM::ZIPFAM(PDOSDEF tdp, PZPXFAM txfp) : MAPFAM(tdp) +UNZFAM::UNZFAM(PUNZFAM txfp) : MAPFAM(txfp) { zutp = txfp->zutp; target = txfp->target; mul = txfp->mul; -} // end of ZIPFAM constructor used in ResetTableOpt +} // end of UNZFAM copy constructor /***********************************************************************/ /* ZIP GetFileLength: returns file size in number of bytes. */ /***********************************************************************/ -int ZIPFAM::GetFileLength(PGLOBAL g) +int UNZFAM::GetFileLength(PGLOBAL g) { int len = (zutp && zutp->entryopen) ? Top - Memory : TXTFAM::GetFileLength(g) * 3; @@ -373,7 +703,7 @@ int ZIPFAM::GetFileLength(PGLOBAL g) /***********************************************************************/ /* ZIP Cardinality: return the number of rows if possible. */ /***********************************************************************/ -int ZIPFAM::Cardinality(PGLOBAL g) +int UNZFAM::Cardinality(PGLOBAL g) { if (!g) return 1; @@ -388,7 +718,7 @@ int ZIPFAM::Cardinality(PGLOBAL g) /***********************************************************************/ /* OpenTableFile: Open a DOS/UNIX table file from a ZIP file. */ /***********************************************************************/ -bool ZIPFAM::OpenTableFile(PGLOBAL g) +bool UNZFAM::OpenTableFile(PGLOBAL g) { char filename[_MAX_PATH]; MODE mode = Tdbp->GetMode(); @@ -396,7 +726,7 @@ bool ZIPFAM::OpenTableFile(PGLOBAL g) /*********************************************************************/ /* Allocate the ZIP utility class. */ /*********************************************************************/ - zutp = new(g) ZIPUTIL(target, mul); + zutp = new(g) UNZIPUTL(target, mul); // We used the file name relative to recorded datapath PlugSetPath(filename, To_File, Tdbp->GetPath()); @@ -415,7 +745,7 @@ bool ZIPFAM::OpenTableFile(PGLOBAL g) /***********************************************************************/ /* GetNext: go to next entry. */ /***********************************************************************/ -int ZIPFAM::GetNext(PGLOBAL g) +int UNZFAM::GetNext(PGLOBAL g) { int rc = zutp->nextEntry(g); @@ -431,7 +761,7 @@ int ZIPFAM::GetNext(PGLOBAL g) /***********************************************************************/ /* ReadBuffer: Read one line for a ZIP file. */ /***********************************************************************/ -int ZIPFAM::ReadBuffer(PGLOBAL g) +int UNZFAM::ReadBuffer(PGLOBAL g) { int rc, len; @@ -497,37 +827,37 @@ int ZIPFAM::ReadBuffer(PGLOBAL g) /***********************************************************************/ /* Table file close routine for MAP access method. */ /***********************************************************************/ -void ZIPFAM::CloseTableFile(PGLOBAL g, bool) +void UNZFAM::CloseTableFile(PGLOBAL g, bool) { close(); } // end of CloseTableFile #endif // 0 -/* -------------------------- class ZPXFAM --------------------------- */ +/* -------------------------- class UZXFAM --------------------------- */ /***********************************************************************/ /* Constructors. */ /***********************************************************************/ -ZPXFAM::ZPXFAM(PDOSDEF tdp) : MPXFAM(tdp) +UZXFAM::UZXFAM(PDOSDEF tdp) : MPXFAM(tdp) { zutp = NULL; target = tdp->GetEntry(); mul = tdp->GetMul(); //Lrecl = tdp->GetLrecl(); -} // end of ZPXFAM standard constructor +} // end of UZXFAM standard constructor -ZPXFAM::ZPXFAM(PZPXFAM txfp) : MPXFAM(txfp) +UZXFAM::UZXFAM(PUZXFAM txfp) : MPXFAM(txfp) { zutp = txfp->zutp; target = txfp->target; mul = txfp->mul; //Lrecl = txfp->Lrecl; -} // end of ZPXFAM copy constructor +} // end of UZXFAM copy constructor /***********************************************************************/ /* ZIP GetFileLength: returns file size in number of bytes. */ /***********************************************************************/ -int ZPXFAM::GetFileLength(PGLOBAL g) +int UZXFAM::GetFileLength(PGLOBAL g) { int len; @@ -545,7 +875,7 @@ int ZPXFAM::GetFileLength(PGLOBAL g) /***********************************************************************/ /* ZIP Cardinality: return the number of rows if possible. */ /***********************************************************************/ -int ZPXFAM::Cardinality(PGLOBAL g) +int UZXFAM::Cardinality(PGLOBAL g) { if (!g) return 1; @@ -566,7 +896,7 @@ int ZPXFAM::Cardinality(PGLOBAL g) /***********************************************************************/ /* OpenTableFile: Open a DOS/UNIX table file from a ZIP file. */ /***********************************************************************/ -bool ZPXFAM::OpenTableFile(PGLOBAL g) +bool UZXFAM::OpenTableFile(PGLOBAL g) { // May have been already opened in GetFileLength if (!zutp || !zutp->zipfile) { @@ -577,7 +907,7 @@ bool ZPXFAM::OpenTableFile(PGLOBAL g) /* Allocate the ZIP utility class. */ /*********************************************************************/ if (!zutp) - zutp = new(g)ZIPUTIL(target, mul); + zutp = new(g)UNZIPUTL(target, mul); // We used the file name relative to recorded datapath PlugSetPath(filename, To_File, Tdbp->GetPath()); @@ -600,7 +930,7 @@ bool ZPXFAM::OpenTableFile(PGLOBAL g) /***********************************************************************/ /* GetNext: go to next entry. */ /***********************************************************************/ -int ZPXFAM::GetNext(PGLOBAL g) +int UZXFAM::GetNext(PGLOBAL g) { int rc = zutp->nextEntry(g); @@ -620,3 +950,146 @@ int ZPXFAM::GetNext(PGLOBAL g) return RC_OK; } // end of GetNext +/* -------------------------- class ZIPFAM --------------------------- */ + +/***********************************************************************/ +/* Constructor. */ +/***********************************************************************/ +ZIPFAM::ZIPFAM(PDOSDEF tdp) : DOSFAM(tdp) +{ + zutp = NULL; + target = tdp->GetEntry(); + append = tdp->GetAppend(); +} // end of ZIPFAM standard constructor + +/***********************************************************************/ +/* OpenTableFile: Open a DOS/UNIX table file from a ZIP file. */ +/***********************************************************************/ +bool ZIPFAM::OpenTableFile(PGLOBAL g) +{ + char filename[_MAX_PATH]; + MODE mode = Tdbp->GetMode(); + + /*********************************************************************/ + /* Allocate the ZIP utility class. */ + /*********************************************************************/ + zutp = new(g) ZIPUTIL(target); + + // We used the file name relative to recorded datapath + PlugSetPath(filename, To_File, Tdbp->GetPath()); + + if (!zutp->OpenTable(g, mode, filename, append)) { + To_Fb = zutp->fp; // Useful when closing + } else + return true; + + return AllocateBuffer(g); +} // end of OpenTableFile + +/***********************************************************************/ +/* ReadBuffer: Read one line for a ZIP file. */ +/***********************************************************************/ +int ZIPFAM::ReadBuffer(PGLOBAL g) +{ + strcpy(g->Message, "ReadBuffer should not been called when zipping"); + return RC_FX; +} // end of ReadBuffer + +/***********************************************************************/ +/* WriteBuffer: Deflate the buffer to the zip file. */ +/***********************************************************************/ +int ZIPFAM::WriteBuffer(PGLOBAL g) +{ + int len; + + // Prepare to write the new line + strcat(strcpy(To_Buf, Tdbp->GetLine()), (Bin) ? CrLf : "\n"); + len = strchr(To_Buf, '\n') - To_Buf + 1; + return zutp->writeEntry(g, To_Buf, len); +} // end of WriteBuffer + +/***********************************************************************/ +/* Table file close routine for ZIP access method. */ +/***********************************************************************/ +void ZIPFAM::CloseTableFile(PGLOBAL g, bool) +{ + To_Fb->Count = 0; + zutp->close(); +} // end of CloseTableFile + +/* -------------------------- class ZPXFAM --------------------------- */ + +/***********************************************************************/ +/* Constructor. */ +/***********************************************************************/ +ZPXFAM::ZPXFAM(PDOSDEF tdp) : FIXFAM(tdp) +{ + zutp = NULL; + target = tdp->GetEntry(); + append = tdp->GetAppend(); + //Lrecl = tdp->GetLrecl(); +} // end of UZXFAM standard constructor + +/***********************************************************************/ +/* OpenTableFile: Open a DOS/UNIX table file from a ZIP file. */ +/***********************************************************************/ +bool ZPXFAM::OpenTableFile(PGLOBAL g) +{ + char filename[_MAX_PATH]; + MODE mode = Tdbp->GetMode(); + + /*********************************************************************/ + /* Allocate the ZIP utility class. */ + /*********************************************************************/ + zutp = new(g) ZIPUTIL(target); + + // We used the file name relative to recorded datapath + PlugSetPath(filename, To_File, Tdbp->GetPath()); + + if (!zutp->OpenTable(g, mode, filename, append)) { + To_Fb = zutp->fp; // Useful when closing + } else + return true; + + return AllocateBuffer(g); +} // end of OpenTableFile + +/***********************************************************************/ +/* WriteBuffer: Deflate the buffer to the zip file. */ +/***********************************************************************/ +int ZPXFAM::WriteBuffer(PGLOBAL g) +{ + /*********************************************************************/ + /* In Insert mode, we write only full blocks. */ + /*********************************************************************/ + if (++CurNum != Rbuf) { + Tdbp->IncLine(Lrecl); // Used by DOSCOL functions + return RC_OK; + } // endif CurNum + + // Now start the compress process. + if (zutp->writeEntry(g, To_Buf, Lrecl * Rbuf) != RC_OK) { + Closing = true; + return RC_FX; + } // endif writeEntry + + CurBlk++; + CurNum = 0; + Tdbp->SetLine(To_Buf); + return RC_OK; +} // end of WriteBuffer + +/***********************************************************************/ +/* Table file close routine for ZIP access method. */ +/***********************************************************************/ +void ZPXFAM::CloseTableFile(PGLOBAL g, bool) +{ + if (CurNum && !Closing) { + // Some more inserted lines remain to be written + Rbuf = CurNum--; + WriteBuffer(g); + } // endif Curnum + + To_Fb->Count = 0; + zutp->close(); +} // end of CloseTableFile diff --git a/storage/connect/filamzip.h b/storage/connect/filamzip.h index 9312fb2f70e..3160703bd20 100644 --- a/storage/connect/filamzip.h +++ b/storage/connect/filamzip.h @@ -1,7 +1,7 @@ /************** filamzip H Declares Source Code File (.H) **************/ -/* Name: filamzip.h Version 1.0 */ +/* Name: filamzip.h Version 1.1 */ /* */ -/* (C) Copyright to the author Olivier BERTRAND 2016 */ +/* (C) Copyright to the author Olivier BERTRAND 2016-2017 */ /* */ /* This file contains the ZIP file access method classes declares. */ /***********************************************************************/ @@ -10,10 +10,14 @@ #include "block.h" #include "filamap.h" +#include "filamfix.h" +#include "zip.h" #include "unzip.h" #define DLLEXPORT extern "C" +typedef class UNZFAM *PUNZFAM; +typedef class UZXFAM *PUZXFAM; typedef class ZIPFAM *PZIPFAM; typedef class ZPXFAM *PZPXFAM; @@ -21,16 +25,50 @@ typedef class ZPXFAM *PZPXFAM; /* This is the ZIP utility fonctions class. */ /***********************************************************************/ class DllExport ZIPUTIL : public BLOCK { -public: + public: // Constructor - ZIPUTIL(PSZ tgt, bool mul); -//ZIPUTIL(ZIPUTIL *zutp); + ZIPUTIL(PSZ tgt); + //ZIPUTIL(ZIPUTIL *zutp); // Implementation -//PTXF Duplicate(PGLOBAL g) { return (PTXF) new(g)ZIPFAM(this); } + //PTXF Duplicate(PGLOBAL g) { return (PTXF) new(g)UNZFAM(this); } // Methods - virtual bool OpenTable(PGLOBAL g, MODE mode, char *fn); + bool OpenTable(PGLOBAL g, MODE mode, char *fn, bool append); + bool open(PGLOBAL g, char *fn, bool append); + bool addEntry(PGLOBAL g, char *entry); + void close(void); + void closeEntry(void); + int writeEntry(PGLOBAL g, char *buf, int len); + void getTime(tm_zip& tmZip); + + // Members + zipFile zipfile; // The ZIP container file + PSZ target; // The target file name +//unz_file_info finfo; // The current file info + PFBLOCK fp; +//char *memory; +//uint size; +//int multiple; // Multiple targets + bool entryopen; // True when open current entry +//char fn[FILENAME_MAX]; // The current entry file name +//char mapCaseTable[256]; +}; // end of ZIPUTIL + +/***********************************************************************/ +/* This is the unZIP utility fonctions class. */ +/***********************************************************************/ +class DllExport UNZIPUTL : public BLOCK { + public: + // Constructor + UNZIPUTL(PSZ tgt, bool mul); +//UNZIPUTL(UNZIPUTL *zutp); + + // Implementation +//PTXF Duplicate(PGLOBAL g) { return (PTXF) new(g)UNZFAM(this); } + + // Methods + bool OpenTable(PGLOBAL g, MODE mode, char *fn); bool open(PGLOBAL g, char *fn); bool openEntry(PGLOBAL g); void close(void); @@ -50,68 +88,120 @@ public: bool entryopen; // True when open current entry char fn[FILENAME_MAX]; // The current entry file name char mapCaseTable[256]; -}; // end of ZIPFAM +}; // end of UNZIPUTL /***********************************************************************/ -/* This is the ZIP file access method. */ +/* This is the unzip file access method. */ /***********************************************************************/ -class DllExport ZIPFAM : public MAPFAM { - friend class ZPXFAM; -public: +class DllExport UNZFAM : public MAPFAM { +//friend class UZXFAM; + public: // Constructors - ZIPFAM(PDOSDEF tdp); - ZIPFAM(PZIPFAM txfp); - ZIPFAM(PDOSDEF tdp, PZPXFAM txfp); + UNZFAM(PDOSDEF tdp); + UNZFAM(PUNZFAM txfp); // Implementation - virtual AMT GetAmType(void) { return TYPE_AM_ZIP; } - virtual PTXF Duplicate(PGLOBAL g) { return (PTXF) new(g)ZIPFAM(this); } + virtual AMT GetAmType(void) {return TYPE_AM_ZIP;} + virtual PTXF Duplicate(PGLOBAL g) {return (PTXF) new(g) UNZFAM(this);} // Methods virtual int Cardinality(PGLOBAL g); virtual int GetFileLength(PGLOBAL g); -//virtual int MaxBlkSize(PGLOBAL g, int s) {return s;} + //virtual int MaxBlkSize(PGLOBAL g, int s) {return s;} virtual bool OpenTableFile(PGLOBAL g); virtual bool DeferReading(void) { return false; } virtual int GetNext(PGLOBAL g); -//virtual int ReadBuffer(PGLOBAL g); -//virtual int WriteBuffer(PGLOBAL g); -//virtual int DeleteRecords(PGLOBAL g, int irc); -//virtual void CloseTableFile(PGLOBAL g, bool abort); + //virtual int ReadBuffer(PGLOBAL g); + //virtual int WriteBuffer(PGLOBAL g); + //virtual int DeleteRecords(PGLOBAL g, int irc); + //virtual void CloseTableFile(PGLOBAL g, bool abort); -protected: + protected: // Members - ZIPUTIL *zutp; - PSZ target; - bool mul; -}; // end of ZIPFAM + UNZIPUTL *zutp; + PSZ target; + bool mul; +}; // end of UNZFAM /***********************************************************************/ -/* This is the fixed ZIP file access method. */ +/* This is the fixed unzip file access method. */ /***********************************************************************/ -class DllExport ZPXFAM : public MPXFAM { - friend class ZIPFAM; -public: +class DllExport UZXFAM : public MPXFAM { +//friend class UNZFAM; + public: // Constructors - ZPXFAM(PDOSDEF tdp); - ZPXFAM(PZPXFAM txfp); + UZXFAM(PDOSDEF tdp); + UZXFAM(PUZXFAM txfp); // Implementation virtual AMT GetAmType(void) { return TYPE_AM_ZIP; } - virtual PTXF Duplicate(PGLOBAL g) { return (PTXF) new(g)ZPXFAM(this); } + virtual PTXF Duplicate(PGLOBAL g) { return (PTXF) new(g)UZXFAM(this); } // Methods virtual int GetFileLength(PGLOBAL g); virtual int Cardinality(PGLOBAL g); virtual bool OpenTableFile(PGLOBAL g); virtual int GetNext(PGLOBAL g); -//virtual int ReadBuffer(PGLOBAL g); + //virtual int ReadBuffer(PGLOBAL g); + + protected: + // Members + UNZIPUTL *zutp; + PSZ target; + bool mul; +}; // end of UZXFAM + +/***********************************************************************/ +/* This is the zip file access method. */ +/***********************************************************************/ +class DllExport ZIPFAM : public DOSFAM { + public: + // Constructors + ZIPFAM(PDOSDEF tdp); + + // Implementation + virtual AMT GetAmType(void) {return TYPE_AM_ZIP;} + + // Methods + virtual int Cardinality(PGLOBAL g) {return 0;} + virtual int GetFileLength(PGLOBAL g) {return g ? 0 : 1;} + //virtual int MaxBlkSize(PGLOBAL g, int s) {return s;} + virtual bool OpenTableFile(PGLOBAL g); + virtual int ReadBuffer(PGLOBAL g); + virtual int WriteBuffer(PGLOBAL g); + //virtual int DeleteRecords(PGLOBAL g, int irc); + virtual void CloseTableFile(PGLOBAL g, bool abort); + + protected: + // Members + ZIPUTIL *zutp; + PSZ target; + bool append; +}; // end of ZIPFAM + +/***********************************************************************/ +/* This is the fixed zip file access method. */ +/***********************************************************************/ +class DllExport ZPXFAM : public FIXFAM { + public: + // Constructors + ZPXFAM(PDOSDEF tdp); + + // Implementation + virtual AMT GetAmType(void) {return TYPE_AM_ZIP;} + + // Methods + virtual int Cardinality(PGLOBAL g) {return 0;} + virtual int GetFileLength(PGLOBAL g) {return g ? 0 : 1;} + virtual bool OpenTableFile(PGLOBAL g); + virtual int WriteBuffer(PGLOBAL g); + virtual void CloseTableFile(PGLOBAL g, bool abort); -protected: + protected: // Members ZIPUTIL *zutp; PSZ target; - bool mul; + bool append; }; // end of ZPXFAM #endif // __FILAMZIP_H diff --git a/storage/connect/ha_connect.cc b/storage/connect/ha_connect.cc index 33f02d8338c..d1ab18f52d5 100644 --- a/storage/connect/ha_connect.cc +++ b/storage/connect/ha_connect.cc @@ -1,4 +1,4 @@ -/* Copyright (C) Olivier Bertrand 2004 - 2016 +/* Copyright (C) Olivier Bertrand 2004 - 2017 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 @@ -125,6 +125,8 @@ #endif // UNIX #include "global.h" #include "plgdbsem.h" +#include "xtable.h" +#include "tabext.h" #if defined(ODBC_SUPPORT) #include "odbccat.h" #endif // ODBC_SUPPORT @@ -132,12 +134,11 @@ #include "tabjdbc.h" #include "jdbconn.h" #endif // JDBC_SUPPORT -#include "xtable.h" #include "tabmysql.h" #include "filamdbf.h" #include "tabxcl.h" #include "tabfmt.h" -#include "reldef.h" +//#include "reldef.h" #include "tabcol.h" #include "xindex.h" #if defined(__WIN__) @@ -171,9 +172,9 @@ #define JSONMAX 10 // JSON Default max grp size extern "C" { - char version[]= "Version 1.05.0001 December 13, 2016"; + char version[]= "Version 1.05.0003 February 27, 2017"; #if defined(__WIN__) - char compver[]= "Version 1.05.0001 " __DATE__ " " __TIME__; + char compver[]= "Version 1.05.0003 " __DATE__ " " __TIME__; char slash= '\\'; #else // !__WIN__ char slash= '/'; @@ -214,6 +215,7 @@ int TranslateJDBCType(int stp, char *tn, int prec, int& len, char& v); void PushWarning(PGLOBAL g, THD *thd, int level); bool CheckSelf(PGLOBAL g, TABLE_SHARE *s, const char *host, const char *db, char *tab, const char *src, int port); +bool ZipLoadFile(PGLOBAL, char*, char*, char*, bool, bool); bool ExactInfo(void); USETEMP UseTemp(void); int GetConvSize(void); @@ -556,7 +558,7 @@ ha_create_table_option connect_index_option_list[]= /***********************************************************************/ /* Push G->Message as a MySQL warning. */ /***********************************************************************/ -bool PushWarning(PGLOBAL g, PTDBASE tdbp, int level) +bool PushWarning(PGLOBAL g, PTDB tdbp, int level) { PHC phc; THD *thd; @@ -1024,7 +1026,7 @@ char *GetListOption(PGLOBAL g, const char *opname, char key[16], val[256]; char *pk, *pv, *pn; - char *opval= (char*) def; + char *opval= (char*)def; int n; for (pk= (char*)oplist; pk; pk= ++pn) { @@ -1032,26 +1034,17 @@ char *GetListOption(PGLOBAL g, const char *opname, pv= strchr(pk, '='); if (pv && (!pn || pv < pn)) { - n= pv - pk; + n= MY_MIN(pv - pk, sizeof(key) - 1); memcpy(key, pk, n); key[n]= 0; pv++; - - if (pn) { - n= pn - pv; - memcpy(val, pv, n); - val[n]= 0; - } else - strcpy(val, pv); - + n= MY_MIN((pn ? pn - pv : strlen(pv)), sizeof(val) - 1); + memcpy(val, pv, n); + val[n]= 0; } else { - if (pn) { - n= MY_MIN(pn - pk, 15); - memcpy(key, pk, n); - key[n]= 0; - } else - strcpy(key, pk); - + n= MY_MIN((pn ? pn - pk : strlen(pk)), sizeof(key) - 1); + memcpy(key, pk, n); + key[n]= 0; val[0]= 0; } // endif pv @@ -1105,7 +1098,7 @@ char *GetStringTableOption(PGLOBAL g, PTOS options, char *opname, char *sdef) else if (!stricmp(opname, "Data_charset")) opval= options->data_charset; - if (!opval && options && options->oplist) + if (!opval && options->oplist) opval= GetListOption(g, opname, options->oplist); return opval ? (char*)opval : sdef; @@ -2113,7 +2106,7 @@ int ha_connect::ScanRecord(PGLOBAL g, uchar *) PCOL colp; PVAL value, sdvalin; Field *fp; - PTDBASE tp= (PTDBASE)tdbp; +//PTDBASE tp= (PTDBASE)tdbp; String attribute(attr_buffer, sizeof(attr_buffer), table->s->table_charset); my_bitmap_map *bmap= dbug_tmp_use_all_columns(table, table->read_set); @@ -2132,7 +2125,7 @@ int ha_connect::ScanRecord(PGLOBAL g, uchar *) && tdbp->GetAmType() != TYPE_AM_ODBC && tdbp->GetAmType() != TYPE_AM_JDBC) || bitmap_is_set(table->write_set, fp->field_index)) { - for (colp= tp->GetSetCols(); colp; colp= colp->GetNext()) + for (colp= tdbp->GetSetCols(); colp; colp= colp->GetNext()) if (!stricmp(colp->GetName(), fp->field_name)) break; @@ -2219,7 +2212,7 @@ int ha_connect::ScanRecord(PGLOBAL g, uchar *) } else if (xmod == MODE_UPDATE) { PCOL cp; - for (cp= tp->GetColumns(); cp; cp= cp->GetNext()) + for (cp= tdbp->GetColumns(); cp; cp= cp->GetNext()) if (!stricmp(colp->GetName(), cp->GetName())) break; @@ -2686,7 +2679,8 @@ PCFIL ha_connect::CheckCond(PGLOBAL g, PCFIL filp, const Item *cond) { AMT tty = filp->Type; char *body= filp->Body; - unsigned int i; + char *havg= filp->Having; + unsigned int i; bool ismul= false, x= (tty == TYPE_AM_MYX || tty == TYPE_AM_XDBC); bool nonul= ((tty == TYPE_AM_ODBC || tty == TYPE_AM_JDBC) && (tdbp->GetMode() == MODE_INSERT || tdbp->GetMode() == MODE_DELETE)); @@ -2699,7 +2693,8 @@ PCFIL ha_connect::CheckCond(PGLOBAL g, PCFIL filp, const Item *cond) htrc("Cond type=%d\n", cond->type()); if (cond->type() == COND::COND_ITEM) { - char *p1, *p2; + char *pb0, *pb1, *pb2, *ph0, *ph1, *ph2; + bool bb = false, bh = false; Item_cond *cond_item= (Item_cond *)cond; if (x) @@ -2719,38 +2714,78 @@ PCFIL ha_connect::CheckCond(PGLOBAL g, PCFIL filp, const Item *cond) List_iterator<Item> li(*arglist); const Item *subitem; - p1= body + strlen(body); - strcpy(p1, "("); - p2= p1 + 1; + pb0= pb1= body + strlen(body); + strcpy(pb0, "("); + pb2= pb1 + 1; + + if (havg) { + ph0= ph1= havg + strlen(havg); + strcpy(ph0, "("); + ph2= ph1 + 1; + } // endif havg for (i= 0; i < arglist->elements; i++) if ((subitem= li++)) { if (!CheckCond(g, filp, subitem)) { if (vop == OP_OR || nonul) return NULL; - else - *p2= 0; + else { + *pb2= 0; + if (havg) *ph2= 0; + } // endelse } else { - p1= p2 + strlen(p2); - strcpy(p1, GetValStr(vop, false)); - p2= p1 + strlen(p1); + if (filp->Bd) { + pb1= pb2 + strlen(pb2); + strcpy(pb1, GetValStr(vop, false)); + pb2= pb1 + strlen(pb1); + } // endif Bd + + if (filp->Hv) { + ph1= ph2 + strlen(ph2); + strcpy(ph1, GetValStr(vop, false)); + ph2= ph1 + strlen(ph1); + } // endif Hv + } // endif CheckCond + bb |= filp->Bd; + bh |= filp->Hv; + filp->Bd = filp->Hv = false; } else return NULL; - if (*p1 != '(') - strcpy(p1, ")"); - else - return NULL; + if (bb) { + strcpy(pb1, ")"); + filp->Bd = bb; + } else + *pb0= 0; + + if (havg) { + if (bb && bh && vop == OP_OR) { + // Cannot or'ed a where clause with a having clause + bb= bh= 0; + *pb0 = 0; + *ph0 = 0; + } else if (bh) { + strcpy(ph1, ")"); + filp->Hv= bh; + } else + *ph0 = 0; + + } // endif havg + + if (!bb && !bh) + return NULL; } else if (cond->type() == COND::FUNC_ITEM) { unsigned int i; - bool iscol, neg= FALSE; + bool iscol, ishav= false, neg= false; Item_func *condf= (Item_func *)cond; Item* *args= condf->arguments(); + filp->Bd = filp->Hv = false; + if (trace) htrc("Func type=%d argnum=%d\n", condf->functype(), condf->argument_count()); @@ -2799,8 +2834,9 @@ PCFIL ha_connect::CheckCond(PGLOBAL g, PCFIL filp, const Item *cond) ha_field_option_struct *fop; Item_field *pField= (Item_field *)args[i]; - if (x && i) - return NULL; + // IN and BETWEEN clauses should be col VOP list + if (i && (x || ismul)) + return NULL; // IN and BETWEEN clauses should be col VOP list else if (pField->field->table != table) return NULL; // Field does not belong to this table else if (tty != TYPE_AM_WMI && IsIndexed(pField->field)) @@ -2816,10 +2852,19 @@ PCFIL ha_connect::CheckCond(PGLOBAL g, PCFIL filp, const Item *cond) else return NULL; - } else if (tty == TYPE_AM_TBL) - return NULL; - else - fnm= pField->field->field_name; + } else if (tty == TYPE_AM_TBL) { + return NULL; + } else { + bool h; + + fnm = filp->Chk(pField->field->field_name, &h); + + if (h && i && !ishav) + return NULL; // Having should be col VOP arg + else + ishav = h; + + } // endif's if (trace) { htrc("Field index=%d\n", pField->field->field_index); @@ -2828,11 +2873,7 @@ PCFIL ha_connect::CheckCond(PGLOBAL g, PCFIL filp, const Item *cond) htrc("Field_type=%d\n", args[i]->field_type()); } // endif trace - // IN and BETWEEN clauses should be col VOP list - if (i && ismul) - return NULL; - - strcat(body, fnm); + strcat((ishav ? havg : body), fnm); } else if (args[i]->type() == COND::FUNC_ITEM) { if (tty == TYPE_AM_MYSQL) { if (!CheckCond(g, filp, args[i])) @@ -2871,32 +2912,34 @@ PCFIL ha_connect::CheckCond(PGLOBAL g, PCFIL filp, const Item *cond) return NULL; if (!x) { + char *s = (ishav) ? havg : body; + // Append the value to the filter switch (args[i]->field_type()) { case MYSQL_TYPE_TIMESTAMP: case MYSQL_TYPE_DATETIME: if (tty == TYPE_AM_ODBC) { - strcat(body, "{ts '"); - strncat(body, res->ptr(), res->length()); + strcat(s, "{ts '"); + strncat(s, res->ptr(), res->length()); if (res->length() < 19) - strcat(body, "1970-01-01 00:00:00" + res->length()); + strcat(s, "1970-01-01 00:00:00" + res->length()); - strcat(body, "'}"); + strcat(s, "'}"); break; } // endif ODBC case MYSQL_TYPE_DATE: if (tty == TYPE_AM_ODBC) { - strcat(body, "{d '"); - strcat(strncat(body, res->ptr(), res->length()), "'}"); + strcat(s, "{d '"); + strcat(strncat(s, res->ptr(), res->length()), "'}"); break; } // endif ODBC case MYSQL_TYPE_TIME: if (tty == TYPE_AM_ODBC) { - strcat(body, "{t '"); - strcat(strncat(body, res->ptr(), res->length()), "'}"); + strcat(s, "{t '"); + strcat(strncat(s, res->ptr(), res->length()), "'}"); break; } // endif ODBC @@ -2905,39 +2948,39 @@ PCFIL ha_connect::CheckCond(PGLOBAL g, PCFIL filp, const Item *cond) switch (args[0]->field_type()) { case MYSQL_TYPE_TIMESTAMP: case MYSQL_TYPE_DATETIME: - strcat(body, "{ts '"); - strncat(body, res->ptr(), res->length()); + strcat(s, "{ts '"); + strncat(s, res->ptr(), res->length()); if (res->length() < 19) - strcat(body, "1970-01-01 00:00:00" + res->length()); + strcat(s, "1970-01-01 00:00:00" + res->length()); - strcat(body, "'}"); + strcat(s, "'}"); break; case MYSQL_TYPE_DATE: - strcat(body, "{d '"); - strncat(body, res->ptr(), res->length()); - strcat(body, "'}"); + strcat(s, "{d '"); + strncat(s, res->ptr(), res->length()); + strcat(s, "'}"); break; case MYSQL_TYPE_TIME: - strcat(body, "{t '"); - strncat(body, res->ptr(), res->length()); - strcat(body, "'}"); + strcat(s, "{t '"); + strncat(s, res->ptr(), res->length()); + strcat(s, "'}"); break; default: - strcat(body, "'"); - strncat(body, res->ptr(), res->length()); - strcat(body, "'"); + strcat(s, "'"); + strncat(s, res->ptr(), res->length()); + strcat(s, "'"); } // endswitch field type } else { - strcat(body, "'"); - strncat(body, res->ptr(), res->length()); - strcat(body, "'"); + strcat(s, "'"); + strncat(s, res->ptr(), res->length()); + strcat(s, "'"); } // endif tty break; default: - strncat(body, res->ptr(), res->length()); + strncat(s, res->ptr(), res->length()); } // endswitch field type } else { @@ -2953,22 +2996,28 @@ PCFIL ha_connect::CheckCond(PGLOBAL g, PCFIL filp, const Item *cond) } // endif x - } // endif + } // endif's Type if (!x) { - if (!i) - strcat(body, GetValStr(vop, neg)); + char *s = (ishav) ? havg : body; + + if (!i) + strcat(s, GetValStr(vop, neg)); else if (vop == OP_XX && i == 1) - strcat(body, " AND "); + strcat(s, " AND "); else if (vop == OP_IN) - strcat(body, (i == condf->argument_count() - 1) ? ")" : ","); + strcat(s, (i == condf->argument_count() - 1) ? ")" : ","); } // endif x } // endfor i - if (x) - filp->Op= vop; + if (x) + filp->Op = vop; + else if (ishav) + filp->Hv = true; + else + filp->Bd = true; } else { if (trace) @@ -3025,16 +3074,28 @@ const COND *ha_connect::cond_push(const COND *cond) if (b) { PCFIL filp; + int rc; if ((filp= tdbp->GetCondFil()) && filp->Cond == cond && filp->Idx == active_index && filp->Type == tty) goto fin; // Already done filp= new(g) CONDFIL(cond, active_index, tty); - filp->Body= (char*)PlugSubAlloc(g, NULL, (x) ? 128 : 0); - *filp->Body= 0; + rc = filp->Init(g, this); + + if (rc == RC_INFO) { + filp->Having = (char*)PlugSubAlloc(g, NULL, 256); + *filp->Having = 0; + } else if (rc == RC_FX) + goto fin; + + filp->Body = (char*)PlugSubAlloc(g, NULL, (x) ? 128 : 0); + *filp->Body = 0; if (CheckCond(g, filp, cond)) { + if (filp->Having && strlen(filp->Having) > 255) + goto fin; // Memory collapse + if (trace) htrc("cond_push: %s\n", filp->Body); @@ -3207,9 +3268,9 @@ int ha_connect::optimize(THD* thd, HA_CHECK_OPT*) tdbp= GetTDB(g); dup->Check |= CHK_OPT; - if (tdbp) { + if (tdbp && !tdbp->IsRemote()) { bool dop= IsTypeIndexable(GetRealType(NULL)); - bool dox= (((PTDBASE)tdbp)->GetDef()->Indexable() == 1); + bool dox= (tdbp->GetDef()->Indexable() == 1); if ((rc= ((PTDBASE)tdbp)->ResetTableOpt(g, dop, dox))) { if (rc == RC_INFO) { @@ -3220,7 +3281,7 @@ int ha_connect::optimize(THD* thd, HA_CHECK_OPT*) } // endif rc - } else + } else if (!tdbp) rc= HA_ERR_INTERNAL_ERROR; return rc; @@ -3463,9 +3524,9 @@ int ha_connect::index_init(uint idx, bool sorted) htrc("index_init CONNECT: %s\n", g->Message); active_index= MAX_KEY; rc= HA_ERR_INTERNAL_ERROR; - } else if (((PTDBDOX)tdbp)->To_Kindex) { + } else if (tdbp->GetKindex()) { if (((PTDBDOX)tdbp)->To_Kindex->GetNum_K()) { - if (((PTDBASE)tdbp)->GetFtype() != RECFM_NAF) + if (tdbp->GetFtype() != RECFM_NAF) ((PTDBDOX)tdbp)->GetTxfp()->ResetBuffer(g); active_index= idx; @@ -3879,11 +3940,10 @@ int ha_connect::rnd_next(uchar *buf) void ha_connect::position(const uchar *) { DBUG_ENTER("ha_connect::position"); -//if (((PTDBASE)tdbp)->GetDef()->Indexable()) - my_store_ptr(ref, ref_length, (my_off_t)((PTDBASE)tdbp)->GetRecpos()); + my_store_ptr(ref, ref_length, (my_off_t)tdbp->GetRecpos()); if (trace > 1) - htrc("position: pos=%d\n", ((PTDBASE)tdbp)->GetRecpos()); + htrc("position: pos=%d\n", tdbp->GetRecpos()); DBUG_VOID_RETURN; } // end of position @@ -3908,14 +3968,14 @@ void ha_connect::position(const uchar *) int ha_connect::rnd_pos(uchar *buf, uchar *pos) { int rc; - PTDBASE tp= (PTDBASE)tdbp; +//PTDBASE tp= (PTDBASE)tdbp; DBUG_ENTER("ha_connect::rnd_pos"); - if (!tp->SetRecpos(xp->g, (int)my_get_ptr(pos, ref_length))) { + if (!tdbp->SetRecpos(xp->g, (int)my_get_ptr(pos, ref_length))) { if (trace) - htrc("rnd_pos: %d\n", tp->GetRecpos()); + htrc("rnd_pos: %d\n", tdbp->GetRecpos()); - tp->SetFilter(NULL); + tdbp->SetFilter(NULL); rc= rnd_next(buf); } else rc= HA_ERR_KEY_NOT_FOUND; @@ -4093,7 +4153,7 @@ int ha_connect::delete_all_rows() if (tdbp && tdbp->GetUse() == USE_OPEN && tdbp->GetAmType() != TYPE_AM_XML && - ((PTDBASE)tdbp)->GetFtype() != RECFM_NAF) + tdbp->GetFtype() != RECFM_NAF) // Close and reopen the table so it will be deleted rc= CloseTable(g); @@ -4471,12 +4531,12 @@ int ha_connect::external_lock(THD *thd, int lock_type) if (!tdbp) { if (!(tdbp= GetTDB(g))) DBUG_RETURN(HA_ERR_INTERNAL_ERROR); - else if (!((PTDBASE)tdbp)->GetDef()->Indexable()) { + else if (!tdbp->GetDef()->Indexable()) { sprintf(g->Message, "external_lock: Table %s is not indexable", tdbp->GetName()); // DBUG_RETURN(HA_ERR_INTERNAL_ERROR); causes assert error push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 0, g->Message); DBUG_RETURN(0); - } else if (((PTDBASE)tdbp)->GetDef()->Indexable() == 1) { + } else if (tdbp->GetDef()->Indexable() == 1) { bool oldsep= ((PCHK)g->Xchk)->oldsep; bool newsep= ((PCHK)g->Xchk)->newsep; PTDBDOS tdp= (PTDBDOS)tdbp; @@ -4557,7 +4617,7 @@ int ha_connect::external_lock(THD *thd, int lock_type) rc= 0; } // endif MakeIndex - } else if (((PTDBASE)tdbp)->GetDef()->Indexable() == 3) { + } else if (tdbp->GetDef()->Indexable() == 3) { if (CheckVirtualIndex(NULL)) { // Make it a warning to avoid crash push_warning(thd, Sql_condition::WARN_LEVEL_WARN, @@ -5173,7 +5233,7 @@ static int connect_assisted_discovery(handlerton *, THD* thd, TABLE_SHARE *table_s, HA_CREATE_INFO *create_info) { - char v=0, spc= ',', qch= 0; + char v=0; const char *fncn= "?"; const char *user, *fn, *db, *host, *pwd, *sep, *tbl, *src; const char *col, *ocl, *rnk, *pic, *fcl, *skc, *zfn; @@ -5225,8 +5285,6 @@ static int connect_assisted_discovery(handlerton *, THD* thd, fncn= topt->catfunc; fnc= GetFuncID(fncn); sep= topt->separator; - spc= (!sep) ? ',' : *sep; - qch= topt->qchar ? *topt->qchar : (signed)topt->quoted >= 0 ? '"' : 0; mul = (int)topt->multiple; tbl= topt->tablist; col= topt->colist; @@ -5426,8 +5484,8 @@ static int connect_assisted_discovery(handlerton *, THD* thd, if (mydef->GetPassword()) pwd= mydef->GetPassword(); - if (mydef->GetDatabase()) - db= mydef->GetDatabase(); + if (mydef->GetTabschema()) + db = mydef->GetTabschema(); if (mydef->GetTabname()) tab= mydef->GetTabname(); @@ -6053,8 +6111,8 @@ int ha_connect::create(const char *name, TABLE *table_arg, if (mydef->GetHostname()) host= mydef->GetHostname(); - if (mydef->GetDatabase()) - db= mydef->GetDatabase(); + if (mydef->GetTabschema()) + db = mydef->GetTabschema(); if (mydef->GetTabname()) tab= mydef->GetTabname(); @@ -6267,21 +6325,26 @@ int ha_connect::create(const char *name, TABLE *table_arg, // Check for incompatible options if (options->sepindex) { my_message(ER_UNKNOWN_ERROR, - "SEPINDEX is incompatible with unspecified file name", - MYF(0)); + "SEPINDEX is incompatible with unspecified file name", MYF(0)); DBUG_RETURN(HA_ERR_UNSUPPORTED); - } else if (GetTypeID(options->type) == TAB_VEC) - if (!table->s->max_rows || options->split) { - my_printf_error(ER_UNKNOWN_ERROR, - "%s tables whose file name is unspecified cannot be split", - MYF(0), options->type); - DBUG_RETURN(HA_ERR_UNSUPPORTED); - } else if (options->header == 2) { - my_printf_error(ER_UNKNOWN_ERROR, - "header=2 is not allowed for %s tables whose file name is unspecified", - MYF(0), options->type); - DBUG_RETURN(HA_ERR_UNSUPPORTED); - } // endif's + } else if (GetTypeID(options->type) == TAB_VEC) { + if (!table->s->max_rows || options->split) { + my_printf_error(ER_UNKNOWN_ERROR, + "%s tables whose file name is unspecified cannot be split", + MYF(0), options->type); + DBUG_RETURN(HA_ERR_UNSUPPORTED); + } else if (options->header == 2) { + my_printf_error(ER_UNKNOWN_ERROR, + "header=2 is not allowed for %s tables whose file name is unspecified", + MYF(0), options->type); + DBUG_RETURN(HA_ERR_UNSUPPORTED); + } // endif's + + } else if (options->zipped) { + my_message(ER_UNKNOWN_ERROR, + "ZIPPED is incompatible with unspecified file name", MYF(0)); + DBUG_RETURN(HA_ERR_UNSUPPORTED); + } // endif's options // Fold type to lower case for (int i= 0; i < 12; i++) @@ -6334,6 +6397,36 @@ int ha_connect::create(const char *name, TABLE *table_arg, if (trace) htrc("xchk=%p createas=%d\n", g->Xchk, g->Createas); + if (options->zipped) { + // Check whether the zip entry must be made from a file + char *fn = GetListOption(g, "Load", options->oplist, NULL); + + if (fn) { + char zbuf[_MAX_PATH], buf[_MAX_PATH], dbpath[_MAX_PATH]; + char *entry = GetListOption(g, "Entry", options->oplist, NULL); + char *a = GetListOption(g, "Append", options->oplist, "NO"); + bool append = *a == '1' || *a == 'Y' || *a == 'y' || !stricmp(a, "ON"); + char *m = GetListOption(g, "Mulentries", options->oplist, "NO"); + bool mul = *m == '1' || *m == 'Y' || *m == 'y' || !stricmp(m, "ON"); + + if (!entry && !mul) { + my_message(ER_UNKNOWN_ERROR, "Missing entry name", MYF(0)); + DBUG_RETURN(HA_ERR_INTERNAL_ERROR); + } // endif entry + + strcat(strcat(strcpy(dbpath, "./"), table->s->db.str), "/"); + PlugSetPath(zbuf, options->filename, dbpath); + PlugSetPath(buf, fn, dbpath); + + if (ZipLoadFile(g, zbuf, buf, entry, append, mul)) { + my_message(ER_UNKNOWN_ERROR, g->Message, MYF(0)); + DBUG_RETURN(HA_ERR_INTERNAL_ERROR); + } // endif LoadFile + + } // endif fn + + } // endif zipped + // To check whether indexes have to be made or remade if (!g->Xchk) { PIXDEF xdp; @@ -6952,10 +7045,10 @@ maria_declare_plugin(connect) PLUGIN_LICENSE_GPL, connect_init_func, /* Plugin Init */ connect_done_func, /* Plugin Deinit */ - 0x0104, /* version number (1.04) */ + 0x0105, /* version number (1.05) */ NULL, /* status variables */ connect_system_variables, /* system variables */ - "1.05.0001", /* string version */ + "1.05.0003", /* string version */ MariaDB_PLUGIN_MATURITY_GAMMA /* maturity */ } maria_declare_plugin_end; diff --git a/storage/connect/ioapi.c b/storage/connect/ioapi.c index 7f5c191b2af..a49da91f7f0 100644 --- a/storage/connect/ioapi.c +++ b/storage/connect/ioapi.c @@ -27,6 +27,7 @@ #include "ioapi.h" +#include "my_attribute.h" voidpf call_zopen64 (const zlib_filefunc64_32_def* pfilefunc,const void*filename,int mode) { @@ -92,7 +93,7 @@ static long ZCALLBACK fseek64_file_func OF((voidpf opaque, voidpf stream, ZPO static int ZCALLBACK fclose_file_func OF((voidpf opaque, voidpf stream)); static int ZCALLBACK ferror_file_func OF((voidpf opaque, voidpf stream)); -static voidpf ZCALLBACK fopen_file_func (voidpf opaque, const char* filename, int mode) +static voidpf ZCALLBACK fopen_file_func (voidpf opaque __attribute__((unused)), const char* filename, int mode) { FILE* file = NULL; const char* mode_fopen = NULL; @@ -110,7 +111,7 @@ static voidpf ZCALLBACK fopen_file_func (voidpf opaque, const char* filename, in return file; } -static voidpf ZCALLBACK fopen64_file_func (voidpf opaque, const void* filename, int mode) +static voidpf ZCALLBACK fopen64_file_func (voidpf opaque __attribute__((unused)), const void* filename, int mode) { FILE* file = NULL; const char* mode_fopen = NULL; @@ -129,21 +130,21 @@ static voidpf ZCALLBACK fopen64_file_func (voidpf opaque, const void* filename, } -static uLong ZCALLBACK fread_file_func (voidpf opaque, voidpf stream, void* buf, uLong size) +static uLong ZCALLBACK fread_file_func (voidpf opaque __attribute__((unused)), voidpf stream, void* buf, uLong size) { uLong ret; ret = (uLong)fread(buf, 1, (size_t)size, (FILE *)stream); return ret; } -static uLong ZCALLBACK fwrite_file_func (voidpf opaque, voidpf stream, const void* buf, uLong size) +static uLong ZCALLBACK fwrite_file_func (voidpf opaque __attribute__((unused)), voidpf stream, const void* buf, uLong size) { uLong ret; ret = (uLong)fwrite(buf, 1, (size_t)size, (FILE *)stream); return ret; } -static long ZCALLBACK ftell_file_func (voidpf opaque, voidpf stream) +static long ZCALLBACK ftell_file_func (voidpf opaque __attribute__((unused)), voidpf stream) { long ret; ret = ftell((FILE *)stream); @@ -151,14 +152,14 @@ static long ZCALLBACK ftell_file_func (voidpf opaque, voidpf stream) } -static ZPOS64_T ZCALLBACK ftell64_file_func (voidpf opaque, voidpf stream) +static ZPOS64_T ZCALLBACK ftell64_file_func (voidpf opaque __attribute__((unused)), voidpf stream) { ZPOS64_T ret; ret = FTELLO_FUNC((FILE *)stream); return ret; } -static long ZCALLBACK fseek_file_func (voidpf opaque, voidpf stream, uLong offset, int origin) +static long ZCALLBACK fseek_file_func (voidpf opaque __attribute__((unused)), voidpf stream, uLong offset, int origin) { int fseek_origin=0; long ret; @@ -181,7 +182,7 @@ static long ZCALLBACK fseek_file_func (voidpf opaque, voidpf stream, uLong offs return ret; } -static long ZCALLBACK fseek64_file_func (voidpf opaque, voidpf stream, ZPOS64_T offset, int origin) +static long ZCALLBACK fseek64_file_func (voidpf opaque __attribute__((unused)), voidpf stream, ZPOS64_T offset, int origin) { int fseek_origin=0; long ret; @@ -207,14 +208,14 @@ static long ZCALLBACK fseek64_file_func (voidpf opaque, voidpf stream, ZPOS64_T } -static int ZCALLBACK fclose_file_func (voidpf opaque, voidpf stream) +static int ZCALLBACK fclose_file_func (voidpf opaque __attribute__((unused)), voidpf stream) { int ret; ret = fclose((FILE *)stream); return ret; } -static int ZCALLBACK ferror_file_func (voidpf opaque, voidpf stream) +static int ZCALLBACK ferror_file_func (voidpf opaque __attribute__((unused)), voidpf stream) { int ret; ret = ferror((FILE *)stream); diff --git a/storage/connect/ioapi.h b/storage/connect/ioapi.h index 8dcbdb06e35..4fa73002053 100644 --- a/storage/connect/ioapi.h +++ b/storage/connect/ioapi.h @@ -129,8 +129,9 @@ extern "C" { #endif #endif - - +#ifndef OF +#define OF(args) args +#endif typedef voidpf (ZCALLBACK *open_file_func) OF((voidpf opaque, const char* filename, int mode)); typedef uLong (ZCALLBACK *read_file_func) OF((voidpf opaque, voidpf stream, void* buf, uLong size)); diff --git a/storage/connect/jdbconn.cpp b/storage/connect/jdbconn.cpp index a69f84a94a1..c1d077406b7 100644 --- a/storage/connect/jdbconn.cpp +++ b/storage/connect/jdbconn.cpp @@ -1,7 +1,7 @@ /************ Jdbconn C++ Functions Source Code File (.CPP) ************/ -/* Name: JDBCONN.CPP Version 1.0 */ +/* Name: JDBCONN.CPP Version 1.1 */ /* */ -/* (C) Copyright to the author Olivier BERTRAND 2016 */ +/* (C) Copyright to the author Olivier BERTRAND 2016-2017 */ /* */ /* This file contains the JDBC connection classes functions. */ /***********************************************************************/ @@ -45,9 +45,9 @@ #include "plgdbsem.h" #include "xobject.h" #include "xtable.h" +#include "tabext.h" #include "tabjdbc.h" //#include "jdbconn.h" -//#include "plgcnx.h" // For DB types #include "resource.h" #include "valblk.h" #include "osutil.h" @@ -318,13 +318,21 @@ PQRYRES JDBCColumns(PGLOBAL g, char *db, char *table, char *colpat, /**************************************************************************/ PQRYRES JDBCSrcCols(PGLOBAL g, char *src, PJPARM sjp) { + char *sqry; PQRYRES qrp; JDBConn *jcp = new(g)JDBConn(g, NULL); if (jcp->Open(sjp)) return NULL; - qrp = jcp->GetMetaData(g, src); + if (strstr(src, "%s")) { + // Place holder for an eventual where clause + sqry = (char*)PlugSubAlloc(g, NULL, strlen(src) + 2); + sprintf(sqry, src, "1=1"); // dummy where clause + } else + sqry = src; + + qrp = jcp->GetMetaData(g, sqry); jcp->Close(); return qrp; } // end of JDBCSrcCols @@ -818,6 +826,11 @@ int JDBConn::Open(PJPARM sop) jpop->Append(GetPluginDir()); jpop->Append("JdbcInterface.jar"); + // All wrappers are pre-compiled in JavaWrappers.jar in the plugin dir + jpop->Append(sep); + jpop->Append(GetPluginDir()); + jpop->Append("JavaWrappers.jar"); + //================== prepare loading of Java VM ============================ JavaVMInitArgs vm_args; // Initialization arguments JavaVMOption* options = new JavaVMOption[N]; // JVM invocation options @@ -1157,6 +1170,9 @@ void JDBConn::Close() jint rc; jmethodID did = nullptr; + // Could have been detached in case of join + rc = jvm->AttachCurrentThread((void**)&env, nullptr); + if (gmID(m_G, did, "JdbcDisconnect", "()I")) printf("%s\n", Msg); else if (Check(env->CallIntMethod(job, did))) diff --git a/storage/connect/json.cpp b/storage/connect/json.cpp index c45630129f1..b473871e9f7 100644 --- a/storage/connect/json.cpp +++ b/storage/connect/json.cpp @@ -1,7 +1,7 @@ /*************** json CPP Declares Source Code File (.H) ***************/ -/* Name: json.cpp Version 1.2 */ +/* Name: json.cpp Version 1.3 */ /* */ -/* (C) Copyright to the author Olivier BERTRAND 2014 - 2015 */ +/* (C) Copyright to the author Olivier BERTRAND 2014 - 2017 */ /* */ /* This file contains the JSON classes functions. */ /***********************************************************************/ @@ -27,8 +27,33 @@ #define EL "\r\n" #else #define EL "\n" +#undef SE_CATCH // Does not work for Linux #endif +#if defined(SE_CATCH) +/**************************************************************************/ +/* This is the support of catching C interrupts to prevent crashes. */ +/**************************************************************************/ +#include <eh.h> + +class SE_Exception { +public: + SE_Exception(unsigned int n, PEXCEPTION_RECORD p) : nSE(n), eRec(p) {} + ~SE_Exception() {} + + unsigned int nSE; + PEXCEPTION_RECORD eRec; +}; // end of class SE_Exception + +void trans_func(unsigned int u, _EXCEPTION_POINTERS* pExp) +{ + throw SE_Exception(u, pExp->ExceptionRecord); +} // end of trans_func + +char *GetExceptionDesc(PGLOBAL g, unsigned int e); +#endif // SE_CATCH + + /***********************************************************************/ /* Parse a json string. */ /* Note: when pretty is not known, the caller set pretty to 3. */ @@ -40,6 +65,9 @@ PJSON ParseJson(PGLOBAL g, char *s, int len, int *ptyp, bool *comma) PJSON jsp = NULL; STRG src; + if (trace) + htrc("ParseJson: s=%.10s len=%d\n", s, len); + if (!s || !len) { strcpy(g->Message, "Void JSON object"); return NULL; @@ -53,15 +81,37 @@ PJSON ParseJson(PGLOBAL g, char *s, int len, int *ptyp, bool *comma) if (s[0] == '[' && (s[1] == '\n' || (s[1] == '\r' && s[2] == '\n'))) pty[0] = false; + // Save stack and allocation environment and prepare error return if (g->jump_level == MAX_JUMP) { strcpy(g->Message, MSG(TOO_MANY_JUMPS)); return NULL; } // endif jump_level - if ((rc= setjmp(g->jumper[++g->jump_level])) != 0) { - goto err; - } // endif rc +#if defined(SE_CATCH) + // Let's try to recover from any kind of interrupt + _se_translator_function f = _set_se_translator(trans_func); + + try { +#endif // SE_CATCH --------------------- try section -------------------- + if ((rc = setjmp(g->jumper[++g->jump_level])) != 0) { + goto err; + } // endif rc + +#if defined(SE_CATCH) // ------------- end of try section ----------------- + } catch (SE_Exception e) { + sprintf(g->Message, "ParseJson: exception doing setjmp: %s (rc=%hd)", + GetExceptionDesc(g, e.nSE), e.nSE); + _set_se_translator(f); + goto err; + } catch (...) { + strcpy(g->Message, "Exception doing setjmp"); + _set_se_translator(f); + goto err; + } // end of try-catches + + _set_se_translator(f); +#endif // SE_CATCH for (i = 0; i < len; i++) switch (s[i]) { @@ -140,7 +190,7 @@ tryit: strcpy(g->Message, "More than one item in file"); err: - g->jump_level--; + g->jump_level--; return NULL; } // end of ParseJson @@ -390,14 +440,14 @@ char *ParseString(PGLOBAL g, int& i, STRG& src) // if (charset == utf8) { char xs[5]; uint hex; - + xs[0] = s[++i]; xs[1] = s[++i]; xs[2] = s[++i]; xs[3] = s[++i]; xs[4] = 0; hex = strtoul(xs, NULL, 16); - + if (hex < 0x80) { p[n] = (uchar)hex; } else if (hex < 0x800) { @@ -414,7 +464,7 @@ char *ParseString(PGLOBAL g, int& i, STRG& src) } else { char xs[3]; UINT hex; - + i += 2; xs[0] = s[++i]; xs[1] = s[++i]; @@ -468,7 +518,7 @@ PVAL ParseNumeric(PGLOBAL g, int& i, STRG& src) case '.': if (!found_digit || has_dot || has_e) goto err; - + has_dot = true; break; case 'e': @@ -769,7 +819,7 @@ bool JOUTSTR::Escape(const char *s) for (unsigned int i = 0; s[i]; i++) switch (s[i]) { - case '"': + case '"': case '\\': case '\t': case '\n': @@ -1057,7 +1107,7 @@ void JARRAY::InitArray(PGLOBAL g) int i; PJVAL jvp, *pjvp = &First; - for (Size = 0, jvp = First; jvp; jvp = jvp->Next) + for (Size = 0, jvp = First; jvp; jvp = jvp->Next) if (!jvp->Del) Size++; @@ -1191,8 +1241,8 @@ bool JARRAY::IsNull(void) /***********************************************************************/ JVALUE::JVALUE(PGLOBAL g, PVAL valp) : JSON() { - Jsp = NULL; - Value = AllocateValue(g, valp); + Jsp = NULL; + Value = AllocateValue(g, valp); Next = NULL; Del = false; } // end of JVALUE constructor @@ -1297,7 +1347,7 @@ PSZ JVALUE::GetText(PGLOBAL g, PSZ text) } // end of GetText void JVALUE::SetValue(PJSON jsp) -{ +{ if (jsp && jsp->GetType() == TYPE_JVAL) { Jsp = jsp->GetJsp(); Value = jsp->GetValue(); diff --git a/storage/connect/jsonudf.cpp b/storage/connect/jsonudf.cpp index f9034f25739..0f693c3c0d6 100644 --- a/storage/connect/jsonudf.cpp +++ b/storage/connect/jsonudf.cpp @@ -1,6 +1,6 @@ /****************** jsonudf C++ Program Source Code File (.CPP) ******************/ -/* PROGRAM NAME: jsonudf Version 1.4 */ -/* (C) Copyright to the author Olivier BERTRAND 2015-2016 */ +/* PROGRAM NAME: jsonudf Version 1.5 */ +/* (C) Copyright to the author Olivier BERTRAND 2015-2017 */ /* This program are the JSON User Defined Functions . */ /*********************************************************************************/ @@ -242,13 +242,16 @@ my_bool JSNX::ParseJpath(PGLOBAL g) // Jpath = Name; return true; - pbuf = PlugDup(g, Jpath); + if (!(pbuf = PlgDBDup(g, Jpath))) + return true; // The Jpath must be analyzed for (i = 0, p = pbuf; (p = strchr(p, ':')); i++, p++) Nod++; // One path node found - Nodes = (PJNODE)PlugSubAlloc(g, NULL, (++Nod) * sizeof(JNODE)); + if (!(Nodes = (PJNODE)PlgDBSubAlloc(g, NULL, (++Nod) * sizeof(JNODE)))) + return true; + memset(Nodes, 0, (Nod)* sizeof(JNODE)); // Analyze the Jpath for this column @@ -1086,9 +1089,10 @@ inline void JsonFreeMem(PGLOBAL g) /*********************************************************************************/ static my_bool JsonInit(UDF_INIT *initid, UDF_ARGS *args, char *message, my_bool mbn, - unsigned long reslen, unsigned long memlen) + unsigned long reslen, unsigned long memlen, + unsigned long more = 0) { - PGLOBAL g = PlugInit(NULL, memlen); + PGLOBAL g = PlugInit(NULL, memlen + more); if (!g) { strcpy(message, "Allocation error"); @@ -1100,6 +1104,7 @@ static my_bool JsonInit(UDF_INIT *initid, UDF_ARGS *args, } // endif g g->Mrr = (args->arg_count && args->args[0]) ? 1 : 0; + g->ActivityStart = (PACTIVITY)more; initid->maybe_null = mbn; initid->max_length = reslen; initid->ptr = (char*)g; @@ -1444,6 +1449,8 @@ static my_bool CheckMemory(PGLOBAL g, UDF_INIT *initid, UDF_ARGS *args, uint n, } // endif b + ml += (unsigned long)g->ActivityStart; // more + if (ml > g->Sarea_Size) { free(g->Sarea); @@ -2758,7 +2765,7 @@ void json_item_merge_deinit(UDF_INIT* initid) /*********************************************************************************/ my_bool json_get_item_init(UDF_INIT *initid, UDF_ARGS *args, char *message) { - unsigned long reslen, memlen; + unsigned long reslen, memlen, more; int n = IsJson(args, 0); if (args->arg_count < 2) { @@ -2767,7 +2774,7 @@ my_bool json_get_item_init(UDF_INIT *initid, UDF_ARGS *args, char *message) } else if (!n && args->arg_type[0] != STRING_RESULT) { strcpy(message, "First argument must be a json item"); return true; - } else if (args->arg_type[1] != STRING_RESULT) { + } else if (args->arg_type[1] != STRING_RESULT) { strcpy(message, "Second argument is not a string (jpath)"); return true; } else @@ -2780,11 +2787,13 @@ my_bool json_get_item_init(UDF_INIT *initid, UDF_ARGS *args, char *message) memcpy(fn, args->args[0], args->lengths[0]); fn[args->lengths[0]] = 0; fl = GetFileLength(fn); - memlen += fl * 3; - } else if (n != 3) - memlen += args->lengths[0] * 3; + more = fl * 3; + } else if (n != 3) { + more = args->lengths[0] * 3; + } else + more = 0; - return JsonInit(initid, args, message, true, reslen, memlen); + return JsonInit(initid, args, message, true, reslen, memlen, more); } // end of json_get_item_init char *json_get_item(UDF_INIT *initid, UDF_ARGS *args, char *result, @@ -2885,7 +2894,7 @@ my_bool jsonget_string_init(UDF_INIT *initid, UDF_ARGS *args, char *message) } // endif's CalcLen(args, false, reslen, memlen); - memlen += more; +//memlen += more; if (n == 2 && args->args[0]) { char fn[_MAX_PATH]; @@ -2894,11 +2903,11 @@ my_bool jsonget_string_init(UDF_INIT *initid, UDF_ARGS *args, char *message) memcpy(fn, args->args[0], args->lengths[0]); fn[args->lengths[0]] = 0; fl = GetFileLength(fn); - memlen += fl * 3; + more += fl * 3; } else if (n != 3) - memlen += args->lengths[0] * 3; + more += args->lengths[0] * 3; - return JsonInit(initid, args, message, true, reslen, memlen); + return JsonInit(initid, args, message, true, reslen, memlen, more); } // end of jsonget_string_init char *jsonget_string(UDF_INIT *initid, UDF_ARGS *args, char *result, @@ -2994,7 +3003,7 @@ void jsonget_string_deinit(UDF_INIT* initid) /*********************************************************************************/ my_bool jsonget_int_init(UDF_INIT *initid, UDF_ARGS *args, char *message) { - unsigned long reslen, memlen; + unsigned long reslen, memlen, more; if (args->arg_count != 2) { strcpy(message, "This function must have 2 arguments"); @@ -3008,10 +3017,10 @@ my_bool jsonget_int_init(UDF_INIT *initid, UDF_ARGS *args, char *message) } else CalcLen(args, false, reslen, memlen); - if (IsJson(args, 0) != 3) - memlen += 1000; // TODO: calculate this + // TODO: calculate this + more = (IsJson(args, 0) != 3) ? 1000 : 0; - return JsonInit(initid, args, message, true, reslen, memlen); + return JsonInit(initid, args, message, true, reslen, memlen, more); } // end of jsonget_int_init long long jsonget_int(UDF_INIT *initid, UDF_ARGS *args, @@ -3100,7 +3109,7 @@ void jsonget_int_deinit(UDF_INIT* initid) /*********************************************************************************/ my_bool jsonget_real_init(UDF_INIT *initid, UDF_ARGS *args, char *message) { - unsigned long reslen, memlen; + unsigned long reslen, memlen, more; if (args->arg_count < 2) { strcpy(message, "At least 2 arguments required"); @@ -3123,10 +3132,10 @@ my_bool jsonget_real_init(UDF_INIT *initid, UDF_ARGS *args, char *message) CalcLen(args, false, reslen, memlen); - if (IsJson(args, 0) != 3) - memlen += 1000; // TODO: calculate this + // TODO: calculate this + more = (IsJson(args, 0) != 3) ? 1000 : 0; - return JsonInit(initid, args, message, true, reslen, memlen); + return JsonInit(initid, args, message, true, reslen, memlen, more); } // end of jsonget_real_init double jsonget_real(UDF_INIT *initid, UDF_ARGS *args, @@ -3234,10 +3243,11 @@ my_bool jsonlocate_init(UDF_INIT *initid, UDF_ARGS *args, char *message) CalcLen(args, false, reslen, memlen); - if (IsJson(args, 0) != 3) - memlen += more; // TODO: calculate this + // TODO: calculate this + if (IsJson(args, 0) == 3) + more = 0; - return JsonInit(initid, args, message, true, reslen, memlen); + return JsonInit(initid, args, message, true, reslen, memlen, more); } // end of jsonlocate_init char *jsonlocate(UDF_INIT *initid, UDF_ARGS *args, char *result, @@ -3358,10 +3368,11 @@ my_bool json_locate_all_init(UDF_INIT *initid, UDF_ARGS *args, char *message) CalcLen(args, false, reslen, memlen); - if (IsJson(args, 0) != 3) - memlen += more; // TODO: calculate this + // TODO: calculate this + if (IsJson(args, 0) == 3) + more = 0; - return JsonInit(initid, args, message, true, reslen, memlen); + return JsonInit(initid, args, message, true, reslen, memlen, more); } // end of json_locate_all_init char *json_locate_all(UDF_INIT *initid, UDF_ARGS *args, char *result, @@ -3485,12 +3496,12 @@ my_bool jsoncontains_init(UDF_INIT *initid, UDF_ARGS *args, char *message) } // endif's CalcLen(args, false, reslen, memlen); - memlen += more; +//memlen += more; - if (IsJson(args, 0) != 3) - memlen += 1000; // TODO: calculate this + // TODO: calculate this + more += (IsJson(args, 0) != 3 ? 1000 : 0); - return JsonInit(initid, args, message, false, reslen, memlen); + return JsonInit(initid, args, message, false, reslen, memlen, more); } // end of jsoncontains_init long long jsoncontains(UDF_INIT *initid, UDF_ARGS *args, char *result, @@ -3537,12 +3548,12 @@ my_bool jsoncontains_path_init(UDF_INIT *initid, UDF_ARGS *args, char *message) } // endif's CalcLen(args, false, reslen, memlen); - memlen += more; +//memlen += more; - if (IsJson(args, 0) != 3) - memlen += 1000; // TODO: calculate this + // TODO: calculate this + more += (IsJson(args, 0) != 3 ? 1000 : 0); - return JsonInit(initid, args, message, true, reslen, memlen); + return JsonInit(initid, args, message, true, reslen, memlen, more); } // end of jsoncontains_path_init long long jsoncontains_path(UDF_INIT *initid, UDF_ARGS *args, char *result, @@ -3736,7 +3747,7 @@ fin: /*********************************************************************************/ my_bool json_set_item_init(UDF_INIT *initid, UDF_ARGS *args, char *message) { - unsigned long reslen, memlen; + unsigned long reslen, memlen, more = 0; int n = IsJson(args, 0); if (!(args->arg_count % 2)) { @@ -3755,11 +3766,11 @@ my_bool json_set_item_init(UDF_INIT *initid, UDF_ARGS *args, char *message) memcpy(fn, args->args[0], args->lengths[0]); fn[args->lengths[0]] = 0; fl = GetFileLength(fn); - memlen += fl * 3; + more += fl * 3; } else if (n != 3) - memlen += args->lengths[0] * 3; + more += args->lengths[0] * 3; - if (!JsonInit(initid, args, message, true, reslen, memlen)) { + if (!JsonInit(initid, args, message, true, reslen, memlen, more)) { PGLOBAL g = (PGLOBAL)initid->ptr; // This is a constant function @@ -3954,7 +3965,7 @@ void json_file_deinit(UDF_INIT* initid) /*********************************************************************************/ my_bool jfile_make_init(UDF_INIT *initid, UDF_ARGS *args, char *message) { - unsigned long reslen, memlen, more = 1024; + unsigned long reslen, memlen; if (args->arg_count < 1 || args->arg_count > 3) { strcpy(message, "Wrong number of arguments"); @@ -4993,7 +5004,7 @@ fin: /*********************************************************************************/ my_bool jbin_set_item_init(UDF_INIT *initid, UDF_ARGS *args, char *message) { - unsigned long reslen, memlen; + unsigned long reslen, memlen, more = 0; int n = IsJson(args, 0); if (!(args->arg_count % 2)) { @@ -5012,11 +5023,11 @@ my_bool jbin_set_item_init(UDF_INIT *initid, UDF_ARGS *args, char *message) memcpy(fn, args->args[0], args->lengths[0]); fn[args->lengths[0]] = 0; fl = GetFileLength(fn); - memlen += fl * 3; + more = fl * 3; } else if (n != 3) - memlen += args->lengths[0] * 3; + more = args->lengths[0] * 3; - return JsonInit(initid, args, message, true, reslen, memlen); + return JsonInit(initid, args, message, true, reslen, memlen, more); } // end of jbin_set_item_init char *jbin_set_item(UDF_INIT *initid, UDF_ARGS *args, char *result, @@ -5104,8 +5115,8 @@ my_bool jbin_file_init(UDF_INIT *initid, UDF_ARGS *args, char *message) fl = GetFileLength(args->args[0]); reslen += fl; more += fl * M; - memlen += more; - return JsonInit(initid, args, message, true, reslen, memlen); +//memlen += more; + return JsonInit(initid, args, message, true, reslen, memlen, more); } // end of jbin_file_init char *jbin_file(UDF_INIT *initid, UDF_ARGS *args, char *result, diff --git a/storage/connect/mycat.cc b/storage/connect/mycat.cc index 497fe5e1aa8..30ac7613dd6 100644 --- a/storage/connect/mycat.cc +++ b/storage/connect/mycat.cc @@ -1,4 +1,4 @@ -/* Copyright (C) Olivier Bertrand 2004 - 2016 +/* Copyright (C) Olivier Bertrand 2004 - 2017 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 @@ -16,9 +16,9 @@ /*************** Mycat CC Program Source Code File (.CC) ***************/ /* PROGRAM NAME: MYCAT */ /* ------------- */ -/* Version 1.5 */ +/* Version 1.6 */ /* */ -/* Author: Olivier Bertrand 2012 - 2016 */ +/* Author: Olivier Bertrand 2012 - 2017 */ /* */ /* WHAT THIS PROGRAM DOES: */ /* ----------------------- */ @@ -58,9 +58,10 @@ #endif // UNIX #include "global.h" #include "plgdbsem.h" -#include "reldef.h" -#include "tabcol.h" +//#include "reldef.h" #include "xtable.h" +#include "tabext.h" +#include "tabcol.h" #include "filamtxt.h" #include "tabdos.h" #include "tabfmt.h" @@ -559,13 +560,13 @@ PRELDEF MYCAT::MakeTableDesc(PGLOBAL g, PTABLE tablep, LPCSTR am) case TAB_XML: tdp= new(g) XMLDEF; break; #endif // XML_SUPPORT #if defined(VCT_SUPPORT) - case TAB_VEC: tdp = new(g)VCTDEF; break; + case TAB_VEC: tdp = new(g) VCTDEF; break; #endif // VCT_SUPPORT #if defined(ODBC_SUPPORT) case TAB_ODBC: tdp= new(g) ODBCDEF; break; #endif // ODBC_SUPPORT #if defined(JDBC_SUPPORT) - case TAB_JDBC: tdp= new(g)JDBCDEF; break; + case TAB_JDBC: tdp= new(g) JDBCDEF; break; #endif // JDBC_SUPPORT #if defined(__WIN__) case TAB_MAC: tdp= new(g) MACDEF; break; diff --git a/storage/connect/myconn.cpp b/storage/connect/myconn.cpp index 644ca019e4a..d05254a32a6 100644 --- a/storage/connect/myconn.cpp +++ b/storage/connect/myconn.cpp @@ -1,11 +1,11 @@ /************** MyConn C++ Program Source Code File (.CPP) **************/ /* PROGRAM NAME: MYCONN */ /* ------------- */ -/* Version 1.8 */ +/* Version 1.9 */ /* */ /* COPYRIGHT: */ /* ---------- */ -/* (C) Copyright to the author Olivier BERTRAND 2007-2016 */ +/* (C) Copyright to the author Olivier BERTRAND 2007-2017 */ /* */ /* WHAT THIS PROGRAM DOES: */ /* ----------------------- */ @@ -375,10 +375,18 @@ PQRYRES SrcColumns(PGLOBAL g, const char *host, const char *db, if (!port) port = mysqld_port; - if (!strnicmp(srcdef, "select ", 7)) { - query = (char *)PlugSubAlloc(g, NULL, strlen(srcdef) + 9); - strcat(strcpy(query, srcdef), " LIMIT 0"); - } else + if (!strnicmp(srcdef, "select ", 7) || strstr(srcdef, "%s")) { + query = (char *)PlugSubAlloc(g, NULL, strlen(srcdef) + 10); + + if (strstr(srcdef, "%s")) + sprintf(query, srcdef, "1=1"); // dummy where clause + else + strcpy(query, srcdef); + + if (!strnicmp(srcdef, "select ", 7)) + strcat(query, " LIMIT 0"); + + } else query = (char *)srcdef; // Open a MySQL connection for this table diff --git a/storage/connect/mysql-test/connect/r/xml_zip.result b/storage/connect/mysql-test/connect/r/xml_zip.result new file mode 100644 index 00000000000..f176149c53f --- /dev/null +++ b/storage/connect/mysql-test/connect/r/xml_zip.result @@ -0,0 +1,98 @@ +Warnings: +Warning 1105 No file name. Table will use t1.xml +# +# Testing zipped XML tables +# +CREATE TABLE t1 ( +ISBN CHAR(13) NOT NULL FIELD_FORMAT='@', +LANG CHAR(2) NOT NULL FIELD_FORMAT='@', +SUBJECT CHAR(12) NOT NULL FIELD_FORMAT='@', +AUTHOR_FIRSTNAME CHAR(15) NOT NULL FIELD_FORMAT='AUTHOR/FIRSTNAME', +AUTHOR_LASTNAME CHAR(8) NOT NULL FIELD_FORMAT='AUTHOR/LASTNAME', +TRANSLATOR_PREFIX CHAR(24) DEFAULT NULL FIELD_FORMAT='TRANSLATOR/@PREFIX', +TRANSLATOR_FIRSTNAME CHAR(6) DEFAULT NULL FIELD_FORMAT='TRANSLATOR/FIRSTNAME', +TRANSLATOR_LASTNAME CHAR(6) DEFAULT NULL FIELD_FORMAT='TRANSLATOR/LASTNAME', +TITLE CHAR(30) NOT NULL, +PUBLISHER_NAME CHAR(15) NOT NULL FIELD_FORMAT='PUBLISHER/NAME', +PUBLISHER_PLACE CHAR(5) NOT NULL FIELD_FORMAT='PUBLISHER/PLACE', +DATEPUB CHAR(4) NOT NULL +) ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='xsample2.zip' ZIPPED=YES +OPTION_LIST='entry=xsample2.xml,load=xsample2.xml,rownode=BOOK,xmlsup=libxml2,expand=1,mulnode=AUTHOR'; +SELECT * FROM t1; +ISBN 9782212090819 +LANG fr +SUBJECT applications +AUTHOR_FIRSTNAME Jean-Christophe +AUTHOR_LASTNAME Bernadac +TRANSLATOR_PREFIX NULL +TRANSLATOR_FIRSTNAME NULL +TRANSLATOR_LASTNAME NULL +TITLE Construire une application XML +PUBLISHER_NAME Eyrolles +PUBLISHER_PLACE Paris +DATEPUB 1999 +ISBN 9782212090819 +LANG fr +SUBJECT applications +AUTHOR_FIRSTNAME François +AUTHOR_LASTNAME Knab +TRANSLATOR_PREFIX NULL +TRANSLATOR_FIRSTNAME NULL +TRANSLATOR_LASTNAME NULL +TITLE Construire une application XML +PUBLISHER_NAME Eyrolles +PUBLISHER_PLACE Paris +DATEPUB 1999 +ISBN 9782840825685 +LANG fr +SUBJECT applications +AUTHOR_FIRSTNAME William J. +AUTHOR_LASTNAME Pardi +TRANSLATOR_PREFIX adapté de l'anglais par +TRANSLATOR_FIRSTNAME James +TRANSLATOR_LASTNAME Guerin +TITLE XML en Action +PUBLISHER_NAME Microsoft Press +PUBLISHER_PLACE Paris +DATEPUB 1999 +ISBN 9782212090529 +LANG fr +SUBJECT général +AUTHOR_FIRSTNAME Alain +AUTHOR_LASTNAME Michard +TRANSLATOR_PREFIX NULL +TRANSLATOR_FIRSTNAME NULL +TRANSLATOR_LASTNAME NULL +TITLE XML, Langage et Applications +PUBLISHER_NAME Eyrolles +PUBLISHER_PLACE Paris +DATEPUB 2003 +CREATE TABLE t2 +ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='xsample2.zip' ZIPPED=YES +OPTION_LIST='xmlsup=libxml2'; +SELECT * FROM t2; +ISBN 9782212090819 +LANG fr +SUBJECT applications +AUTHOR Jean-Christophe Bernadac +TRANSLATOR NULL +TITLE Construire une application XML +PUBLISHER Eyrolles Paris +DATEPUB 1999 +ISBN 9782840825685 +LANG fr +SUBJECT applications +AUTHOR William J. Pardi +TRANSLATOR James Guerin +TITLE XML en Action +PUBLISHER Microsoft Press Paris +DATEPUB 1999 +ISBN 9782212090529 +LANG fr +SUBJECT général +AUTHOR Alain Michard +TRANSLATOR NULL +TITLE XML, Langage et Applications +PUBLISHER Eyrolles Paris +DATEPUB 2003 +DROP TABLE t1,t2; diff --git a/storage/connect/mysql-test/connect/r/zip.result b/storage/connect/mysql-test/connect/r/zip.result new file mode 100644 index 00000000000..c03b27bd428 --- /dev/null +++ b/storage/connect/mysql-test/connect/r/zip.result @@ -0,0 +1,240 @@ +# +# Testing zipped DOS tables +# +CREATE TABLE t1 ( +digit INT(3) NOT NULL, +letter CHAR(16) NOT NULL) +ENGINE=CONNECT TABLE_TYPE=DOS FILE_NAME='newdos.zip' +OPTION_LIST='ENTRY=new1.dos' ZIPPED=1; +INSERT INTO t1 VALUES(1,'One'),(2,'Two'),(3,'Three'),(4,'Four'),(5,'Five'),(6,'Six'),(7,'Seven'),(8,'Eight'),(9,'Nine'),(10,'Ten'); +SELECT * FROM t1; +digit letter +1 One +2 Two +3 Three +4 Four +5 Five +6 Six +7 Seven +8 Eight +9 Nine +10 Ten +CREATE TABLE t2 ( +digit INT(3) NOT NULL, +letter CHAR(16) NOT NULL) +ENGINE=CONNECT TABLE_TYPE=DOS FILE_NAME='newdos.zip' +OPTION_LIST='ENTRY=new2.dos,APPEND=1' ZIPPED=1; +INSERT INTO t2 VALUES(11,'Eleven'),(12,'Twelve'),(13,'Thirteen'),(14,'Fourteen'),(15,'Fiften'),(16,'Sixteen'),(17,'Seventeen'),(18,'Eighteen'),(19,'Nineteen'),(20,'Twenty'); +SELECT * FROM t2; +digit letter +11 Eleven +12 Twelve +13 Thirteen +14 Fourteen +15 Fiften +16 Sixteen +17 Seventeen +18 Eighteen +19 Nineteen +20 Twenty +CREATE TABLE t3 ( +digit INT(3) NOT NULL, +letter CHAR(16) NOT NULL) +ENGINE=CONNECT TABLE_TYPE=DOS FILE_NAME='newdos.zip' +OPTION_LIST='MULENTRIES=1' ZIPPED=1; +SELECT * FROM t3; +digit letter +1 One +2 Two +3 Three +4 Four +5 Five +6 Six +7 Seven +8 Eight +9 Nine +10 Ten +11 Eleven +12 Twelve +13 Thirteen +14 Fourteen +15 Fiften +16 Sixteen +17 Seventeen +18 Eighteen +19 Nineteen +20 Twenty +CREATE TABLE t4 ( +fn VARCHAR(256)NOT NULL, +cmpsize BIGINT NOT NULL FLAG=1, +uncsize BIGINT NOT NULL FLAG=2, +method INT NOT NULL FLAG=3) +ENGINE=CONNECT TABLE_TYPE=ZIP FILE_NAME='newdos.zip'; +SELECT * FROM t4; +fn cmpsize uncsize method +new1.dos 67 79 8 +new2.dos 77 112 8 +DROP TABLE t1,t2,t3,t4; +# +# Testing zipped CSV tables +# +CREATE TABLE t1 ( +digit INT(3) NOT NULL, +letter CHAR(16) NOT NULL) +ENGINE=CONNECT TABLE_TYPE=CSV FILE_NAME='newcsv.zip' +OPTION_LIST='ENTRY=new1.csv' HEADER=1 ZIPPED=1; +INSERT INTO t1 VALUES(1,'One'),(2,'Two'),(3,'Three'),(4,'Four'),(5,'Five'),(6,'Six'),(7,'Seven'),(8,'Eight'),(9,'Nine'),(10,'Ten'); +SELECT * FROM t1; +digit letter +1 One +2 Two +3 Three +4 Four +5 Five +6 Six +7 Seven +8 Eight +9 Nine +10 Ten +CREATE TABLE td1 +ENGINE=CONNECT TABLE_TYPE=CSV FILE_NAME='newcsv.zip' +OPTION_LIST='ENTRY=new1.csv' HEADER=1 ZIPPED=1; +SELECT * FROM td1; +digit letter +1 One +2 Two +3 Three +4 Four +5 Five +6 Six +7 Seven +8 Eight +9 Nine +10 Ten +DROP TABLE td1; +CREATE TABLE t2 ( +digit INT(3) NOT NULL, +letter CHAR(16) NOT NULL) +ENGINE=CONNECT TABLE_TYPE=CSV FILE_NAME='newcsv.zip' +OPTION_LIST='ENTRY=new2.csv,APPEND=1' HEADER=1 ZIPPED=1; +INSERT INTO t2 VALUES(11,'Eleven'),(12,'Twelve'),(13,'Thirteen'),(14,'Fourteen'),(15,'Fiften'),(16,'Sixteen'),(17,'Seventeen'),(18,'Eighteen'),(19,'Nineteen'),(20,'Twenty'); +SELECT * FROM t2; +digit letter +11 Eleven +12 Twelve +13 Thirteen +14 Fourteen +15 Fiften +16 Sixteen +17 Seventeen +18 Eighteen +19 Nineteen +20 Twenty +CREATE TABLE t3 +ENGINE=CONNECT TABLE_TYPE=CSV FILE_NAME='newcsv.zip' +OPTION_LIST='MULENTRIES=1' HEADER=1 ZIPPED=1; +SELECT * FROM t3; +digit letter +1 One +2 Two +3 Three +4 Four +5 Five +6 Six +7 Seven +8 Eight +9 Nine +10 Ten +11 Eleven +12 Twelve +13 Thirteen +14 Fourteen +15 Fiften +16 Sixteen +17 Seventeen +18 Eighteen +19 Nineteen +20 Twenty +CREATE TABLE t4 ( +fn VARCHAR(256)NOT NULL, +cmpsize BIGINT NOT NULL FLAG=1, +uncsize BIGINT NOT NULL FLAG=2, +method INT NOT NULL FLAG=3) +ENGINE=CONNECT TABLE_TYPE=ZIP FILE_NAME='newcsv.zip'; +SELECT * FROM t4; +fn cmpsize uncsize method +new1.csv 79 83 8 +new2.csv 94 125 8 +DROP TABLE t1,t2,t3,t4; +# +# Testing zipped JSON tables +# +CREATE TABLE t1 ( +_id INT(2) NOT NULL, +name_first CHAR(9) NOT NULL FIELD_FORMAT='name:first', +name_aka CHAR(4) DEFAULT NULL FIELD_FORMAT='name:aka', +name_last CHAR(10) NOT NULL FIELD_FORMAT='name:last', +title CHAR(12) DEFAULT NULL, +birth CHAR(20) DEFAULT NULL, +death CHAR(20) DEFAULT NULL, +contribs CHAR(7) NOT NULL FIELD_FORMAT='contribs:', +awards_award CHAR(42) DEFAULT NULL FIELD_FORMAT='awards::award', +awards_year CHAR(4) DEFAULT NULL FIELD_FORMAT='awards::year', +awards_by CHAR(38) DEFAULT NULL FIELD_FORMAT='awards::by' +) ENGINE=CONNECT TABLE_TYPE=JSON FILE_NAME='bios.zip' OPTION_LIST='ENTRY=bios.json,LOAD=bios.json' ZIPPED=YES; +SELECT * FROM t1; +_id name_first name_aka name_last title birth death contribs awards_award awards_year awards_by +1 John NULL Backus NULL 1924-12-03T05:00:00Z 2007-03-17T04:00:00Z Fortran W.W. McDowell Award 1967 IEEE Computer Society +2 John NULL McCarthy NULL 1927-09-04T04:00:00Z 2011-12-24T05:00:00Z Lisp Turing Award 1971 ACM +3 Grace NULL Hopper Rear Admiral 1906-12-09T05:00:00Z 1992-01-01T05:00:00Z UNIVAC Computer Sciences Man of the Year 1969 Data Processing Management Association +4 Kristen NULL Nygaard NULL 1926-08-27T04:00:00Z 2002-08-10T04:00:00Z OOP Rosing Prize 1999 Norwegian Data Association +5 Ole-Johan NULL Dahl NULL 1931-10-12T04:00:00Z 2002-06-29T04:00:00Z OOP Rosing Prize 1999 Norwegian Data Association +6 Guido NULL van Rossum NULL 1956-01-31T05:00:00Z NULL Python Award for the Advancement of Free Software 2001 Free Software Foundation +7 Dennis NULL Ritchie NULL 1941-09-09T04:00:00Z 2011-10-12T04:00:00Z UNIX Turing Award 1983 ACM +8 Yukihiro Matz Matsumoto NULL 1965-04-14T04:00:00Z NULL Ruby Award for the Advancement of Free Software 2011 Free Software Foundation +9 James NULL Gosling NULL 1955-05-19T04:00:00Z NULL Java The Economist Innovation Award 2002 The Economist +10 Martin NULL Odersky NULL NULL NULL Scala NULL NULL NULL +CREATE TABLE t2 +ENGINE=CONNECT TABLE_TYPE=JSON FILE_NAME='bios.zip' ZIPPED=1 +OPTION_LIST='LEVEL=5'; +SELECT * FROM t2; +_id name_first name_aka name_last title birth death contribs awards_award awards_year awards_by +1 John NULL Backus NULL 1924-12-03T05:00:00Z 2007-03-17T04:00:00Z Fortran W.W. McDowell Award 1967 IEEE Computer Society +2 John NULL McCarthy NULL 1927-09-04T04:00:00Z 2011-12-24T05:00:00Z Lisp Turing Award 1971 ACM +3 Grace NULL Hopper Rear Admiral 1906-12-09T05:00:00Z 1992-01-01T05:00:00Z UNIVAC Computer Sciences Man of the Year 1969 Data Processing Management Association +4 Kristen NULL Nygaard NULL 1926-08-27T04:00:00Z 2002-08-10T04:00:00Z OOP Rosing Prize 1999 Norwegian Data Association +5 Ole-Johan NULL Dahl NULL 1931-10-12T04:00:00Z 2002-06-29T04:00:00Z OOP Rosing Prize 1999 Norwegian Data Association +6 Guido NULL van Rossum NULL 1956-01-31T05:00:00Z NULL Python Award for the Advancement of Free Software 2001 Free Software Foundation +7 Dennis NULL Ritchie NULL 1941-09-09T04:00:00Z 2011-10-12T04:00:00Z UNIX Turing Award 1983 ACM +8 Yukihiro Matz Matsumoto NULL 1965-04-14T04:00:00Z NULL Ruby Award for the Advancement of Free Software 2011 Free Software Foundation +9 James NULL Gosling NULL 1955-05-19T04:00:00Z NULL Java The Economist Innovation Award 2002 The Economist +10 Martin NULL Odersky NULL NULL NULL Scala NULL NULL NULL +CREATE TABLE t3 ( +_id INT(2) NOT NULL, +firstname CHAR(9) NOT NULL FIELD_FORMAT='name:first', +aka CHAR(4) DEFAULT NULL FIELD_FORMAT='name:aka', +lastname CHAR(10) NOT NULL FIELD_FORMAT='name:last', +title CHAR(12) DEFAULT NULL, +birth date DEFAULT NULL date_format="YYYY-DD-MM'T'hh:mm:ss'Z'", +death date DEFAULT NULL date_format="YYYY-DD-MM'T'hh:mm:ss'Z'", +contribs CHAR(64) NOT NULL FIELD_FORMAT='contribs:[", "]', +award CHAR(42) DEFAULT NULL FIELD_FORMAT='awards:[x]:award', +year CHAR(4) DEFAULT NULL FIELD_FORMAT='awards:[x]:year', +`by` CHAR(38) DEFAULT NULL FIELD_FORMAT='awards:[x]:by' +) ENGINE=CONNECT TABLE_TYPE='json' FILE_NAME='bios.zip' ZIPPED=YES; +SELECT * FROM t3 WHERE _id = 1; +_id firstname aka lastname title birth death contribs award year by +1 John NULL Backus NULL 1924-03-12 2008-05-03 Fortran, ALGOL, Backus-Naur Form, FP W.W. McDowell Award 1967 IEEE Computer Society +1 John NULL Backus NULL 1924-03-12 2008-05-03 Fortran, ALGOL, Backus-Naur Form, FP National Medal of Science 1975 National Science Foundation +1 John NULL Backus NULL 1924-03-12 2008-05-03 Fortran, ALGOL, Backus-Naur Form, FP Turing Award 1977 ACM +1 John NULL Backus NULL 1924-03-12 2008-05-03 Fortran, ALGOL, Backus-Naur Form, FP Draper Prize 1993 National Academy of Engineering +CREATE TABLE t4 ( +fn VARCHAR(256)NOT NULL, +cmpsize BIGINT NOT NULL FLAG=1, +uncsize BIGINT NOT NULL FLAG=2, +method INT NOT NULL FLAG=3) +ENGINE=CONNECT TABLE_TYPE=ZIP FILE_NAME='bios.zip'; +SELECT * FROM t4; +fn cmpsize uncsize method +bios.json 1096 6848 8 +DROP TABLE t1,t2,t3,t4; diff --git a/storage/connect/mysql-test/connect/std_data/bios.json b/storage/connect/mysql-test/connect/std_data/bios.json new file mode 100644 index 00000000000..85e4ecb933f --- /dev/null +++ b/storage/connect/mysql-test/connect/std_data/bios.json @@ -0,0 +1,273 @@ +[ + { + "_id" : 1, + "name" : { + "first" : "John", + "last" : "Backus" + }, + "birth" : "1924-12-03T05:00:00Z", + "death" : "2007-03-17T04:00:00Z", + "contribs" : [ + "Fortran", + "ALGOL", + "Backus-Naur Form", + "FP" + ], + "awards" : [ + { + "award" : "W.W. McDowell Award", + "year" : 1967, + "by" : "IEEE Computer Society" + }, + { + "award" : "National Medal of Science", + "year" : 1975, + "by" : "National Science Foundation" + }, + { + "award" : "Turing Award", + "year" : 1977, + "by" : "ACM" + }, + { + "award" : "Draper Prize", + "year" : 1993, + "by" : "National Academy of Engineering" + } + ] + }, + { + "_id" : 2, + "name" : { + "first" : "John", + "last" : "McCarthy" + }, + "birth" : "1927-09-04T04:00:00Z", + "death" : "2011-12-24T05:00:00Z", + "contribs" : [ + "Lisp", + "Artificial Intelligence", + "ALGOL" + ], + "awards" : [ + { + "award" : "Turing Award", + "year" : 1971, + "by" : "ACM" + }, + { + "award" : "Kyoto Prize", + "year" : 1988, + "by" : "Inamori Foundation" + }, + { + "award" : "National Medal of Science", + "year" : 1990, + "by" : "National Science Foundation" + } + ] + }, + { + "_id" : 3, + "name" : { + "first" : "Grace", + "last" : "Hopper" + }, + "title" : "Rear Admiral", + "birth" : "1906-12-09T05:00:00Z", + "death" : "1992-01-01T05:00:00Z", + "contribs" : [ + "UNIVAC", + "compiler", + "FLOW-MATIC", + "COBOL" + ], + "awards" : [ + { + "award" : "Computer Sciences Man of the Year", + "year" : 1969, + "by" : "Data Processing Management Association" + }, + { + "award" : "Distinguished Fellow", + "year" : 1973, + "by" : " British Computer Society" + }, + { + "award" : "W. W. McDowell Award", + "year" : 1976, + "by" : "IEEE Computer Society" + }, + { + "award" : "National Medal of Technology", + "year" : 1991, + "by" : "United States" + } + ] + }, + { + "_id" : 4, + "name" : { + "first" : "Kristen", + "last" : "Nygaard" + }, + "birth" : "1926-08-27T04:00:00Z", + "death" : "2002-08-10T04:00:00Z", + "contribs" : [ + "OOP", + "Simula" + ], + "awards" : [ + { + "award" : "Rosing Prize", + "year" : 1999, + "by" : "Norwegian Data Association" + }, + { + "award" : "Turing Award", + "year" : 2001, + "by" : "ACM" + }, + { + "award" : "IEEE John von Neumann Medal", + "year" : 2001, + "by" : "IEEE" + } + ] + }, + { + "_id" : 5, + "name" : { + "first" : "Ole-Johan", + "last" : "Dahl" + }, + "birth" : "1931-10-12T04:00:00Z", + "death" : "2002-06-29T04:00:00Z", + "contribs" : [ + "OOP", + "Simula" + ], + "awards" : [ + { + "award" : "Rosing Prize", + "year" : 1999, + "by" : "Norwegian Data Association" + }, + { + "award" : "Turing Award", + "year" : 2001, + "by" : "ACM" + }, + { + "award" : "IEEE John von Neumann Medal", + "year" : 2001, + "by" : "IEEE" + } + ] + }, + { + "_id" : 6, + "name" : { + "first" : "Guido", + "last" : "van Rossum" + }, + "birth" : "1956-01-31T05:00:00Z", + "contribs" : [ + "Python" + ], + "awards" : [ + { + "award" : "Award for the Advancement of Free Software", + "year" : 2001, + "by" : "Free Software Foundation" + }, + { + "award" : "NLUUG Award", + "year" : 2003, + "by" : "NLUUG" + } + ] + }, + { + "_id" : 7, + "name" : { + "first" : "Dennis", + "last" : "Ritchie" + }, + "birth" : "1941-09-09T04:00:00Z", + "death" : "2011-10-12T04:00:00Z", + "contribs" : [ + "UNIX", + "C" + ], + "awards" : [ + { + "award" : "Turing Award", + "year" : 1983, + "by" : "ACM" + }, + { + "award" : "National Medal of Technology", + "year" : 1998, + "by" : "United States" + }, + { + "award" : "Japan Prize", + "year" : 2011, + "by" : "The Japan Prize Foundation" + } + ] + }, + { + "_id" : 8, + "name" : { + "first" : "Yukihiro", + "aka" : "Matz", + "last" : "Matsumoto" + }, + "birth" : "1965-04-14T04:00:00Z", + "contribs" : [ + "Ruby" + ], + "awards" : [ + { + "award" : "Award for the Advancement of Free Software", + "year" : "2011", + "by" : "Free Software Foundation" + } + ] + }, + { + "_id" : 9, + "name" : { + "first" : "James", + "last" : "Gosling" + }, + "birth" : "1955-05-19T04:00:00Z", + "contribs" : [ + "Java" + ], + "awards" : [ + { + "award" : "The Economist Innovation Award", + "year" : 2002, + "by" : "The Economist" + }, + { + "award" : "Officer of the Order of Canada", + "year" : 2007, + "by" : "Canada" + } + ] + }, + { + "_id" : 10, + "name" : { + "first" : "Martin", + "last" : "Odersky" + }, + "contribs" : [ + "Scala" + ] + } +] diff --git a/storage/connect/mysql-test/connect/std_data/xsample2.xml b/storage/connect/mysql-test/connect/std_data/xsample2.xml new file mode 100644 index 00000000000..35295844370 --- /dev/null +++ b/storage/connect/mysql-test/connect/std_data/xsample2.xml @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="UTF-8"?> +<BIBLIO SUBJECT="XML"> + <BOOK ISBN="9782212090819" LANG="fr" SUBJECT="applications"> + <AUTHOR> + <FIRSTNAME>Jean-Christophe</FIRSTNAME> + <LASTNAME>Bernadac</LASTNAME> + </AUTHOR> + <AUTHOR> + <FIRSTNAME>François</FIRSTNAME> + <LASTNAME>Knab</LASTNAME> + </AUTHOR> + <TITLE>Construire une application XML</TITLE> + <PUBLISHER> + <NAME>Eyrolles</NAME> + <PLACE>Paris</PLACE> + </PUBLISHER> + <DATEPUB>1999</DATEPUB> + </BOOK> + <BOOK ISBN="9782840825685" LANG="fr" SUBJECT="applications"> + <AUTHOR> + <FIRSTNAME>William J.</FIRSTNAME> + <LASTNAME>Pardi</LASTNAME> + </AUTHOR> + <TRANSLATOR PREFIX="adapté de l'anglais par"> + <FIRSTNAME>James</FIRSTNAME> + <LASTNAME>Guerin</LASTNAME> + </TRANSLATOR> + <TITLE>XML en Action</TITLE> + <PUBLISHER> + <NAME>Microsoft Press</NAME> + <PLACE>Paris</PLACE> + </PUBLISHER> + <DATEPUB>1999</DATEPUB> + </BOOK> + <BOOK ISBN="9782212090529" LANG="fr" SUBJECT="général"> + <AUTHOR> + <FIRSTNAME>Alain</FIRSTNAME> + <LASTNAME>Michard</LASTNAME> + </AUTHOR> + <TITLE>XML, Langage et Applications</TITLE> + <PUBLISHER> + <NAME>Eyrolles</NAME> + <PLACE>Paris</PLACE> + </PUBLISHER> + <DATEPUB>2003</DATEPUB> + </BOOK> +</BIBLIO> diff --git a/storage/connect/mysql-test/connect/t/have_zip.inc b/storage/connect/mysql-test/connect/t/have_zip.inc new file mode 100644 index 00000000000..d1283fc1d38 --- /dev/null +++ b/storage/connect/mysql-test/connect/t/have_zip.inc @@ -0,0 +1,19 @@ +--disable_query_log +--error 0,ER_UNKNOWN_ERROR +CREATE TABLE t1 (a CHAR(10)) ENGINE=CONNECT TABLE_TYPE=ZIP FILE_NAME='test.zip'; +if ($mysql_errno) +{ + Skip No ZIP support; +} +#if (!`SELECT count(*) FROM INFORMATION_SCHEMA.TABLES +# WHERE TABLE_SCHEMA='test' AND TABLE_NAME='t1' +# AND ENGINE='CONNECT' +# AND CREATE_OPTIONS LIKE '%`table_type`=ZIP%' +# AND CREATE OPTIONS LIKE "%`file_name`='test.zip'%"`) +#{ +# DROP TABLE IF EXISTS t1; +# Skip Need ZIP support; +#} +DROP TABLE t1; +--enable_query_log + diff --git a/storage/connect/mysql-test/connect/t/xml_zip.test b/storage/connect/mysql-test/connect/t/xml_zip.test new file mode 100644 index 00000000000..d8c7894f861 --- /dev/null +++ b/storage/connect/mysql-test/connect/t/xml_zip.test @@ -0,0 +1,41 @@ +--source have_zip.inc +--source have_libxml2.inc + +let $MYSQLD_DATADIR= `select @@datadir`; + +--vertical_results + +--copy_file $MTR_SUITE_DIR/std_data/xsample2.xml $MYSQLD_DATADIR/test/xsample2.xml + +--echo # +--echo # Testing zipped XML tables +--echo # +CREATE TABLE t1 ( +ISBN CHAR(13) NOT NULL FIELD_FORMAT='@', +LANG CHAR(2) NOT NULL FIELD_FORMAT='@', +SUBJECT CHAR(12) NOT NULL FIELD_FORMAT='@', +AUTHOR_FIRSTNAME CHAR(15) NOT NULL FIELD_FORMAT='AUTHOR/FIRSTNAME', +AUTHOR_LASTNAME CHAR(8) NOT NULL FIELD_FORMAT='AUTHOR/LASTNAME', +TRANSLATOR_PREFIX CHAR(24) DEFAULT NULL FIELD_FORMAT='TRANSLATOR/@PREFIX', +TRANSLATOR_FIRSTNAME CHAR(6) DEFAULT NULL FIELD_FORMAT='TRANSLATOR/FIRSTNAME', +TRANSLATOR_LASTNAME CHAR(6) DEFAULT NULL FIELD_FORMAT='TRANSLATOR/LASTNAME', +TITLE CHAR(30) NOT NULL, +PUBLISHER_NAME CHAR(15) NOT NULL FIELD_FORMAT='PUBLISHER/NAME', +PUBLISHER_PLACE CHAR(5) NOT NULL FIELD_FORMAT='PUBLISHER/PLACE', +DATEPUB CHAR(4) NOT NULL +) ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='xsample2.zip' ZIPPED=YES +OPTION_LIST='entry=xsample2.xml,load=xsample2.xml,rownode=BOOK,xmlsup=libxml2,expand=1,mulnode=AUTHOR'; +SELECT * FROM t1; + +#testing discovery +CREATE TABLE t2 +ENGINE=CONNECT TABLE_TYPE=XML FILE_NAME='xsample2.zip' ZIPPED=YES +OPTION_LIST='xmlsup=libxml2'; +SELECT * FROM t2; +DROP TABLE t1,t2; + +# +# Clean up +# +--remove_file $MYSQLD_DATADIR/test/xsample2.xml +--remove_file $MYSQLD_DATADIR/test/xsample2.zip diff --git a/storage/connect/mysql-test/connect/t/zip.test b/storage/connect/mysql-test/connect/t/zip.test new file mode 100644 index 00000000000..a4892e9ed4e --- /dev/null +++ b/storage/connect/mysql-test/connect/t/zip.test @@ -0,0 +1,136 @@ +--source have_zip.inc +let $MYSQLD_DATADIR= `select @@datadir`; + +--copy_file $MTR_SUITE_DIR/std_data/bios.json $MYSQLD_DATADIR/test/bios.json + +--echo # +--echo # Testing zipped DOS tables +--echo # +CREATE TABLE t1 ( +digit INT(3) NOT NULL, +letter CHAR(16) NOT NULL) +ENGINE=CONNECT TABLE_TYPE=DOS FILE_NAME='newdos.zip' +OPTION_LIST='ENTRY=new1.dos' ZIPPED=1; +INSERT INTO t1 VALUES(1,'One'),(2,'Two'),(3,'Three'),(4,'Four'),(5,'Five'),(6,'Six'),(7,'Seven'),(8,'Eight'),(9,'Nine'),(10,'Ten'); +SELECT * FROM t1; + +CREATE TABLE t2 ( +digit INT(3) NOT NULL, +letter CHAR(16) NOT NULL) +ENGINE=CONNECT TABLE_TYPE=DOS FILE_NAME='newdos.zip' +OPTION_LIST='ENTRY=new2.dos,APPEND=1' ZIPPED=1; +INSERT INTO t2 VALUES(11,'Eleven'),(12,'Twelve'),(13,'Thirteen'),(14,'Fourteen'),(15,'Fiften'),(16,'Sixteen'),(17,'Seventeen'),(18,'Eighteen'),(19,'Nineteen'),(20,'Twenty'); +SELECT * FROM t2; + +CREATE TABLE t3 ( +digit INT(3) NOT NULL, +letter CHAR(16) NOT NULL) +ENGINE=CONNECT TABLE_TYPE=DOS FILE_NAME='newdos.zip' +OPTION_LIST='MULENTRIES=1' ZIPPED=1; +SELECT * FROM t3; + +CREATE TABLE t4 ( +fn VARCHAR(256)NOT NULL, +cmpsize BIGINT NOT NULL FLAG=1, +uncsize BIGINT NOT NULL FLAG=2, +method INT NOT NULL FLAG=3) +ENGINE=CONNECT TABLE_TYPE=ZIP FILE_NAME='newdos.zip'; +SELECT * FROM t4; +DROP TABLE t1,t2,t3,t4; + +--echo # +--echo # Testing zipped CSV tables +--echo # +CREATE TABLE t1 ( +digit INT(3) NOT NULL, +letter CHAR(16) NOT NULL) +ENGINE=CONNECT TABLE_TYPE=CSV FILE_NAME='newcsv.zip' +OPTION_LIST='ENTRY=new1.csv' HEADER=1 ZIPPED=1; +INSERT INTO t1 VALUES(1,'One'),(2,'Two'),(3,'Three'),(4,'Four'),(5,'Five'),(6,'Six'),(7,'Seven'),(8,'Eight'),(9,'Nine'),(10,'Ten'); +SELECT * FROM t1; + +# Test discovery +CREATE TABLE td1 +ENGINE=CONNECT TABLE_TYPE=CSV FILE_NAME='newcsv.zip' +OPTION_LIST='ENTRY=new1.csv' HEADER=1 ZIPPED=1; +SELECT * FROM td1; +DROP TABLE td1; + +CREATE TABLE t2 ( +digit INT(3) NOT NULL, +letter CHAR(16) NOT NULL) +ENGINE=CONNECT TABLE_TYPE=CSV FILE_NAME='newcsv.zip' +OPTION_LIST='ENTRY=new2.csv,APPEND=1' HEADER=1 ZIPPED=1; +INSERT INTO t2 VALUES(11,'Eleven'),(12,'Twelve'),(13,'Thirteen'),(14,'Fourteen'),(15,'Fiften'),(16,'Sixteen'),(17,'Seventeen'),(18,'Eighteen'),(19,'Nineteen'),(20,'Twenty'); +SELECT * FROM t2; + +CREATE TABLE t3 +ENGINE=CONNECT TABLE_TYPE=CSV FILE_NAME='newcsv.zip' +OPTION_LIST='MULENTRIES=1' HEADER=1 ZIPPED=1; +SELECT * FROM t3; + +CREATE TABLE t4 ( +fn VARCHAR(256)NOT NULL, +cmpsize BIGINT NOT NULL FLAG=1, +uncsize BIGINT NOT NULL FLAG=2, +method INT NOT NULL FLAG=3) +ENGINE=CONNECT TABLE_TYPE=ZIP FILE_NAME='newcsv.zip'; +SELECT * FROM t4; +DROP TABLE t1,t2,t3,t4; + +--echo # +--echo # Testing zipped JSON tables +--echo # +CREATE TABLE t1 ( +_id INT(2) NOT NULL, +name_first CHAR(9) NOT NULL FIELD_FORMAT='name:first', +name_aka CHAR(4) DEFAULT NULL FIELD_FORMAT='name:aka', +name_last CHAR(10) NOT NULL FIELD_FORMAT='name:last', +title CHAR(12) DEFAULT NULL, +birth CHAR(20) DEFAULT NULL, +death CHAR(20) DEFAULT NULL, +contribs CHAR(7) NOT NULL FIELD_FORMAT='contribs:', +awards_award CHAR(42) DEFAULT NULL FIELD_FORMAT='awards::award', +awards_year CHAR(4) DEFAULT NULL FIELD_FORMAT='awards::year', +awards_by CHAR(38) DEFAULT NULL FIELD_FORMAT='awards::by' +) ENGINE=CONNECT TABLE_TYPE=JSON FILE_NAME='bios.zip' OPTION_LIST='ENTRY=bios.json,LOAD=bios.json' ZIPPED=YES; +SELECT * FROM t1; + +# Test discovery +CREATE TABLE t2 +ENGINE=CONNECT TABLE_TYPE=JSON FILE_NAME='bios.zip' ZIPPED=1 +OPTION_LIST='LEVEL=5'; +SELECT * FROM t2; + +CREATE TABLE t3 ( +_id INT(2) NOT NULL, +firstname CHAR(9) NOT NULL FIELD_FORMAT='name:first', +aka CHAR(4) DEFAULT NULL FIELD_FORMAT='name:aka', +lastname CHAR(10) NOT NULL FIELD_FORMAT='name:last', +title CHAR(12) DEFAULT NULL, +birth date DEFAULT NULL date_format="YYYY-DD-MM'T'hh:mm:ss'Z'", +death date DEFAULT NULL date_format="YYYY-DD-MM'T'hh:mm:ss'Z'", +contribs CHAR(64) NOT NULL FIELD_FORMAT='contribs:[", "]', +award CHAR(42) DEFAULT NULL FIELD_FORMAT='awards:[x]:award', +year CHAR(4) DEFAULT NULL FIELD_FORMAT='awards:[x]:year', +`by` CHAR(38) DEFAULT NULL FIELD_FORMAT='awards:[x]:by' +) ENGINE=CONNECT TABLE_TYPE='json' FILE_NAME='bios.zip' ZIPPED=YES; +SELECT * FROM t3 WHERE _id = 1; + +CREATE TABLE t4 ( +fn VARCHAR(256)NOT NULL, +cmpsize BIGINT NOT NULL FLAG=1, +uncsize BIGINT NOT NULL FLAG=2, +method INT NOT NULL FLAG=3) +ENGINE=CONNECT TABLE_TYPE=ZIP FILE_NAME='bios.zip'; +SELECT * FROM t4; +DROP TABLE t1,t2,t3,t4; + +# +# Clean up +# +--remove_file $MYSQLD_DATADIR/test/newdos.zip +--remove_file $MYSQLD_DATADIR/test/newcsv.zip +--remove_file $MYSQLD_DATADIR/test/bios.zip +--remove_file $MYSQLD_DATADIR/test/bios.json + diff --git a/storage/connect/odbconn.cpp b/storage/connect/odbconn.cpp index 7320f4cc1d9..433e392eace 100644 --- a/storage/connect/odbconn.cpp +++ b/storage/connect/odbconn.cpp @@ -35,8 +35,8 @@ #include "global.h" #include "plgdbsem.h" #include "xobject.h" -//#include "kindex.h" #include "xtable.h" +#include "tabext.h" #include "odbccat.h" #include "tabodbc.h" #include "plgcnx.h" // For DB types @@ -413,12 +413,20 @@ PQRYRES ODBCColumns(PGLOBAL g, char *dsn, char *db, char *table, /**************************************************************************/ PQRYRES ODBCSrcCols(PGLOBAL g, char *dsn, char *src, POPARM sop) { + char *sqry; ODBConn *ocp = new(g) ODBConn(g, NULL); if (ocp->Open(dsn, sop, 10) < 1) // openReadOnly + noOdbcDialog return NULL; - return ocp->GetMetaData(g, dsn, src); + if (strstr(src, "%s")) { + // Place holder for an eventual where clause + sqry = (char*)PlugSubAlloc(g, NULL, strlen(src) + 3); + sprintf(sqry, src, "1=1", "1=1"); // dummy where clause + } else + sqry = src; + + return ocp->GetMetaData(g, dsn, sqry); } // end of ODBCSrcCols #if 0 @@ -1417,7 +1425,7 @@ int ODBConn::ExecDirectSQL(char *sql, ODBCCOL *tocols) b = true; if (trace) - htrc("ExecDirect hstmt=%p %.64s\n", hstmt, sql); + htrc("ExecDirect hstmt=%p %.256s\n", hstmt, sql); if (m_Tdb->Srcdef) { // Be sure this is a query returning a result set diff --git a/storage/connect/plgdbsem.h b/storage/connect/plgdbsem.h index cb408494319..800b1098d50 100644 --- a/storage/connect/plgdbsem.h +++ b/storage/connect/plgdbsem.h @@ -1,7 +1,7 @@ /************** PlgDBSem H Declares Source Code File (.H) **************/ /* Name: PLGDBSEM.H Version 3.7 */ /* */ -/* (C) Copyright to the author Olivier BERTRAND 1998-2016 */ +/* (C) Copyright to the author Olivier BERTRAND 1998-2017 */ /* */ /* This file contains the CONNECT storage engine definitions. */ /***********************************************************************/ @@ -57,7 +57,7 @@ enum TABTYPE {TAB_UNDEF = 0, /* Table of undefined type */ TAB_FIX = 2, /* Fixed column offset, fixed LRECL */ TAB_BIN = 3, /* Like FIX but can have binary fields */ TAB_CSV = 4, /* DOS files with CSV records */ - TAB_FMT = 5, /* DOS files with formatted recordss */ + TAB_FMT = 5, /* DOS files with formatted records */ TAB_DBF = 6, /* DBF Dbase or Foxpro files */ TAB_XML = 7, /* XML or HTML files */ TAB_INI = 8, /* INI or CFG files */ @@ -212,11 +212,24 @@ enum OPVAL {OP_EQ = 1, /* Filtering operator = */ OP_SUB = 17, /* Expression Substract operator */ OP_MULT = 18, /* Expression Multiply operator */ OP_DIV = 19, /* Expression Divide operator */ - OP_NOP = 21, /* Scalar function is nopped */ OP_NUM = 22, /* Scalar function Op Num */ - OP_ABS = 23, /* Scalar function Op Abs */ OP_MAX = 24, /* Scalar function Op Max */ OP_MIN = 25, /* Scalar function Op Min */ + OP_EXP = 36, /* Scalar function Op Exp */ + OP_FDISK = 94, /* Operator Disk of fileid */ + OP_FPATH = 95, /* Operator Path of fileid */ + OP_FNAME = 96, /* Operator Name of fileid */ + OP_FTYPE = 97, /* Operator Type of fileid */ + OP_LAST = 82, /* Index operator Find Last */ + OP_FIRST = 106, /* Index operator Find First */ + OP_NEXT = 107, /* Index operator Find Next */ + OP_SAME = 108, /* Index operator Find Next Same */ + OP_FSTDIF = 109, /* Index operator Find First dif */ + OP_NXTDIF = 110, /* Index operator Find Next dif */ + OP_PREV = 116}; /* Index operator Find Previous */ +#if 0 + OP_NOP = 21, /* Scalar function is nopped */ + OP_ABS = 23, /* Scalar function Op Abs */ OP_CEIL = 26, /* Scalar function Op Ceil */ OP_FLOOR = 27, /* Scalar function Op Floor */ OP_MOD = 28, /* Scalar function Op Mod */ @@ -312,6 +325,7 @@ enum OPVAL {OP_EQ = 1, /* Filtering operator = */ OP_REMOVE = 201, /* Scalar function Op Remove */ OP_RENAME = 202, /* Scalar function Op Rename */ OP_FCOMP = 203}; /* Scalar function Op Compare */ +#endif // 0 enum TUSE {USE_NO = 0, /* Table is not yet linearized */ USE_LIN = 1, /* Table is linearized */ @@ -356,6 +370,7 @@ typedef class XOBJECT *PXOB; typedef class COLBLK *PCOL; typedef class TDB *PTDB; typedef class TDBASE *PTDBASE; +typedef class TDBEXT *PTDBEXT; typedef class TDBDOS *PTDBDOS; typedef class TDBFIX *PTDBFIX; typedef class TDBFMT *PTDBFMT; @@ -374,6 +389,7 @@ typedef class KXYCOL *PXCOL; typedef class CATALOG *PCATLG; typedef class RELDEF *PRELDEF; typedef class TABDEF *PTABDEF; +typedef class EXTDEF *PEXTBDEF; typedef class DOSDEF *PDOSDEF; typedef class CSVDEF *PCSVDEF; typedef class VCTDEF *PVCTDEF; @@ -619,4 +635,4 @@ int global_open(GLOBAL *g, int msgid, const char *filename, int flags, int mode) DllExport LPCSTR PlugSetPath(LPSTR to, LPCSTR name, LPCSTR dir); char *MakeEscape(PGLOBAL g, char* str, char q); -DllExport bool PushWarning(PGLOBAL, PTDBASE, int level = 1); +DllExport bool PushWarning(PGLOBAL, PTDB, int level = 1); diff --git a/storage/connect/plgdbutl.cpp b/storage/connect/plgdbutl.cpp index 83975c6d8fa..1910cdcdec8 100644 --- a/storage/connect/plgdbutl.cpp +++ b/storage/connect/plgdbutl.cpp @@ -1,11 +1,11 @@ /********** PlgDBUtl Fpe C++ Program Source Code File (.CPP) ***********/ /* PROGRAM NAME: PLGDBUTL */ /* ------------- */ -/* Version 3.9 */ +/* Version 4.0 */ /* */ /* COPYRIGHT: */ /* ---------- */ -/* (C) Copyright to the author Olivier BERTRAND 1998-2016 */ +/* (C) Copyright to the author Olivier BERTRAND 1998-2017 */ /* */ /* WHAT THIS PROGRAM DOES: */ /* ----------------------- */ @@ -939,7 +939,11 @@ int PlugCloseFile(PGLOBAL g __attribute__((unused)), PFBLOCK fp, bool all) #endif // LIBXML2_SUPPORT #ifdef ZIP_SUPPORT case TYPE_FB_ZIP: - ((ZIPUTIL*)fp->File)->close(); + if (fp->Mode == MODE_INSERT) + ((ZIPUTIL*)fp->File)->close(); + else + ((UNZIPUTL*)fp->File)->close(); + fp->Memory = NULL; fp->Mode = MODE_ANY; fp->Count = 0; @@ -1119,7 +1123,7 @@ char *GetAmName(PGLOBAL g, AMT am, void *memp) return amn; } // end of GetAmName -#if defined(__WIN__) && !defined(NOCATCH) +#if defined(SE_CATCH) /***********************************************************************/ /* GetExceptionDesc: return the description of an exception code. */ /***********************************************************************/ @@ -1207,7 +1211,7 @@ char *GetExceptionDesc(PGLOBAL g, unsigned int e) return p; } // end of GetExceptionDesc -#endif // __WIN__ && !NOCATCH +#endif // SE_CATCH /***********************************************************************/ /* PlgDBalloc: allocates or suballocates memory conditionally. */ diff --git a/storage/connect/plgxml.cpp b/storage/connect/plgxml.cpp index 71b72621b06..eb31e24235b 100644 --- a/storage/connect/plgxml.cpp +++ b/storage/connect/plgxml.cpp @@ -1,6 +1,6 @@ /******************************************************************/ /* Implementation of XML document processing using PdbXML. */ -/* Author: Olivier Bertrand 2007-2012 */ +/* Author: Olivier Bertrand 2007-2017 */ /******************************************************************/ #include "my_global.h" #include "global.h" @@ -49,7 +49,7 @@ bool XMLDOCUMENT::InitZip(PGLOBAL g, char *entry) { #if defined(ZIP_SUPPORT) bool mul = (entry) ? strchr(entry, '*') || strchr(entry, '?') : false; - zip = new(g) ZIPUTIL(entry, mul); + zip = new(g) UNZIPUTL(entry, mul); return zip == NULL; #else // !ZIP_SUPPORT sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "ZIP"); diff --git a/storage/connect/plgxml.h b/storage/connect/plgxml.h index db7dfa6bda5..6870764c503 100644 --- a/storage/connect/plgxml.h +++ b/storage/connect/plgxml.h @@ -101,7 +101,7 @@ class XMLDOCUMENT : public BLOCK { // Members #if defined(ZIP_SUPPORT) - ZIPUTIL *zip; /* Used for zipped file */ + UNZIPUTL *zip; /* Used for zipped file */ #else // !ZIP_SUPPORT bool zip; /* Always false */ #endif // !ZIP_SUPPORT diff --git a/storage/connect/plugutil.c b/storage/connect/plugutil.c index 2551b603349..bfac8a5fd99 100644 --- a/storage/connect/plugutil.c +++ b/storage/connect/plugutil.c @@ -244,6 +244,9 @@ LPCSTR PlugSetPath(LPSTR pBuff, LPCSTR prefix, LPCSTR FileName, LPCSTR defpath) char *drive = NULL, *defdrv = NULL; #endif + if (trace > 1) + htrc("prefix=%s fn=%s path=%s\n", prefix, FileName, defpath); + if (!strncmp(FileName, "//", 2) || !strncmp(FileName, "\\\\", 2)) { strcpy(pBuff, FileName); // Remote file return pBuff; diff --git a/storage/connect/reldef.cpp b/storage/connect/reldef.cpp index 30e4d49d249..5bb7848ab1c 100644 --- a/storage/connect/reldef.cpp +++ b/storage/connect/reldef.cpp @@ -621,8 +621,8 @@ bool OEMDEF::DefineAM(PGLOBAL g, LPCSTR, int) /***********************************************************************/ PTDB OEMDEF::GetTable(PGLOBAL g, MODE mode) { - RECFM rfm; - PTDBASE tdbp = NULL; + RECFM rfm; + PTDB tdbp = NULL; // If define block not here yet, get it now if (!Pxdef && !(Pxdef = GetXdef(g))) @@ -632,7 +632,7 @@ PTDB OEMDEF::GetTable(PGLOBAL g, MODE mode) /* Allocate a TDB of the proper type. */ /* Column blocks will be allocated only when needed. */ /*********************************************************************/ - if (!(tdbp = (PTDBASE)Pxdef->GetTable(g, mode))) + if (!(tdbp = Pxdef->GetTable(g, mode))) return NULL; else rfm = tdbp->GetFtype(); diff --git a/storage/connect/reldef.h b/storage/connect/reldef.h index bc1bd2ddd74..52a131dbf3d 100644 --- a/storage/connect/reldef.h +++ b/storage/connect/reldef.h @@ -64,15 +64,16 @@ class DllExport RELDEF : public BLOCK { // Relation definition block }; // end of RELDEF /***********************************************************************/ -/* These classes correspond to the data base description contained in */ -/* a .XDB file the A.M. DOS, FIX, CSV, MAP, BIN, VCT, PLG, ODBC, DOM. */ +/* This class corresponds to the data base description for tables */ +/* of type DOS, FIX, CSV, DBF, BIN, VCT, JSON, XML... */ /***********************************************************************/ class DllExport TABDEF : public RELDEF { /* Logical table descriptor */ friend class CATALOG; friend class PLUGCAT; friend class MYCAT; - friend class TDBASE; - public: + friend class TDB; + friend class TDBEXT; +public: // Constructor TABDEF(void); // Constructor @@ -112,11 +113,11 @@ class DllExport TABDEF : public RELDEF { /* Logical table descriptor */ int Sort; /* Table already sorted ??? */ int Multiple; /* 0: No 1: DIR 2: Section 3: filelist */ int Degree; /* Number of columns in the table */ - int Pseudo; /* Bit: 1 ROWID Ok, 2 FILEID Ok */ + int Pseudo; /* Bit: 1 ROWID }Ok, 2 FILEID Ok */ bool Read_Only; /* true for read only tables */ const CHARSET_INFO *m_data_charset; const char *csname; /* Table charset name */ - }; // end of TABDEF +}; // end of TABDEF /***********************************************************************/ /* Externally defined OEM tables. */ @@ -190,11 +191,12 @@ class DllExport COLCRT : public BLOCK { /* Column description block /***********************************************************************/ /* Column definition block. */ /***********************************************************************/ -class DllExport COLDEF : public COLCRT { /* Column description block */ +class DllExport COLDEF : public COLCRT { /* Column description block */ friend class TABDEF; friend class COLBLK; friend class DBFFAM; - friend class TDBASE; + friend class TDB; + friend class TDBASE; friend class TDBDOS; public: COLDEF(void); // Constructor diff --git a/storage/connect/tabdos.cpp b/storage/connect/tabdos.cpp index 16cc6c33b44..d2bb3d7a4af 100644 --- a/storage/connect/tabdos.cpp +++ b/storage/connect/tabdos.cpp @@ -102,6 +102,7 @@ DOSDEF::DOSDEF(void) Mapped = false; Zipped = false; Mulentries = false; + Append = false; Padded = false; Huge = false; Accept = false; @@ -132,10 +133,13 @@ bool DOSDEF::DefineAM(PGLOBAL g, LPCSTR am, int) : (am && (*am == 'B' || *am == 'b')) ? "B" : (am && !stricmp(am, "DBF")) ? "D" : "V"; - if ((Zipped = GetBoolCatInfo("Zipped", false))) - Mulentries = ((Entry = GetStringCatInfo(g, "Entry", NULL))) - ? strchr(Entry, '*') || strchr(Entry, '?') - : GetBoolCatInfo("Mulentries", false); + if ((Zipped = GetBoolCatInfo("Zipped", false))) { + Entry = GetStringCatInfo(g, "Entry", NULL); + Mulentries = (Entry && *Entry) ? strchr(Entry, '*') || strchr(Entry, '?') + : false; + Mulentries = GetBoolCatInfo("Mulentries", Mulentries); + Append = GetBoolCatInfo("Append", false); + } Desc = Fn = GetStringCatInfo(g, "Filename", NULL); Ofn = GetStringCatInfo(g, "Optname", Fn); @@ -347,10 +351,26 @@ PTDB DOSDEF::GetTable(PGLOBAL g, MODE mode) if (Zipped) { #if defined(ZIP_SUPPORT) if (Recfm == RECFM_VAR) { - txfp = new(g)ZIPFAM(this); - tdbp = new(g)TDBDOS(this, txfp); + if (mode == MODE_READ || mode == MODE_ANY) { + txfp = new(g) UNZFAM(this); + } else if (mode == MODE_INSERT) { + txfp = new(g) ZIPFAM(this); + } else { + strcpy(g->Message, "UPDATE/DELETE not supported for ZIP"); + return NULL; + } // endif's mode + + tdbp = new(g) TDBDOS(this, txfp); } else { - txfp = new(g)ZPXFAM(this); + if (mode == MODE_READ || mode == MODE_ANY) { + txfp = new(g) UZXFAM(this); + } else if (mode == MODE_INSERT) { + txfp = new(g) ZPXFAM(this); + } else { + strcpy(g->Message, "UPDATE/DELETE not supported for ZIP"); + return NULL; + } // endif's mode + tdbp = new(g)TDBFIX(this, txfp); } // endif Recfm @@ -376,7 +396,7 @@ PTDB DOSDEF::GetTable(PGLOBAL g, MODE mode) txfp = new(g) MPXFAM(this); else if (Compressed) { #if defined(GZ_SUPPORT) - txfp = new(g) ZIXFAM(this); + txfp = new(g) GZXFAM(this); #else // !GZ_SUPPORT sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "GZ"); return NULL; @@ -484,7 +504,7 @@ TDBDOS::TDBDOS(PGLOBAL g, PTDBDOS tdbp) : TDBASE(tdbp) } // end of TDBDOS copy constructor // Method -PTDB TDBDOS::CopyOne(PTABS t) +PTDB TDBDOS::Clone(PTABS t) { PTDB tp; PDOSCOL cp1, cp2; @@ -498,7 +518,7 @@ PTDB TDBDOS::CopyOne(PTABS t) } // endfor cp1 return tp; - } // end of CopyOne + } // end of Clone /***********************************************************************/ /* Allocate DOS column description block. */ diff --git a/storage/connect/tabdos.h b/storage/connect/tabdos.h index 4c8eb438a26..922d52ee399 100644 --- a/storage/connect/tabdos.h +++ b/storage/connect/tabdos.h @@ -28,7 +28,7 @@ class DllExport DOSDEF : public TABDEF { /* Logical table description */ friend class TDBFIX; friend class TXTFAM; friend class DBFBASE; - friend class ZIPUTIL; + friend class UNZIPUTL; public: // Constructor DOSDEF(void); @@ -43,7 +43,8 @@ class DllExport DOSDEF : public TABDEF { /* Logical table description */ PSZ GetOfn(void) {return Ofn;} PSZ GetEntry(void) {return Entry;} bool GetMul(void) {return Mulentries;} - void SetBlock(int block) {Block = block;} + bool GetAppend(void) {return Append;} + void SetBlock(int block) { Block = block; } int GetBlock(void) {return Block;} int GetLast(void) {return Last;} void SetLast(int last) {Last = last;} @@ -81,6 +82,7 @@ class DllExport DOSDEF : public TABDEF { /* Logical table description */ bool Mapped; /* 0: disk file, 1: memory mapped file */ bool Zipped; /* true for zipped table file */ bool Mulentries; /* true for multiple entries */ + bool Append; /* Used when creating zipped table */ bool Padded; /* true for padded table file */ bool Huge; /* true for files larger than 2GB */ bool Accept; /* true if wrong lines are accepted */ @@ -140,7 +142,7 @@ class DllExport TDBDOS : public TDBASE { {return (PTDB)new(g) TDBDOS(g, this);} // Methods - virtual PTDB CopyOne(PTABS t); + virtual PTDB Clone(PTABS t); virtual void ResetDB(void) {Txfp->Reset();} virtual bool IsUsingTemp(PGLOBAL g); virtual bool IsIndexed(void) {return Indxd;} diff --git a/storage/connect/tabext.cpp b/storage/connect/tabext.cpp new file mode 100644 index 00000000000..e3518126a49 --- /dev/null +++ b/storage/connect/tabext.cpp @@ -0,0 +1,640 @@ +/************* Tabext C++ Functions Source Code File (.CPP) ************/ +/* Name: TABEXT.CPP Version 1.0 */ +/* */ +/* (C) Copyright to the author Olivier BERTRAND 2017 */ +/* */ +/* This file contains the TBX, TDB and OPJOIN classes functions. */ +/***********************************************************************/ + +/***********************************************************************/ +/* Include relevant MariaDB header file. */ +/***********************************************************************/ +#define MYSQL_SERVER 1 +#include "my_global.h" +#include "sql_class.h" +#include "sql_servers.h" +#include "sql_string.h" +#if !defined(__WIN__) +#include "osutil.h" +#endif + +/***********************************************************************/ +/* Include required application header files */ +/* global.h is header containing all global Plug declarations. */ +/* plgdbsem.h is header containing the DB applic. declarations. */ +/* xobject.h is header containing XOBJECT derived classes declares. */ +/***********************************************************************/ +#include "global.h" +#include "plgdbsem.h" +#include "xtable.h" +#include "tabext.h" +#include "ha_connect.h" + +/* -------------------------- Class CONDFIL -------------------------- */ + +/***********************************************************************/ +/* CONDFIL Constructor. */ +/***********************************************************************/ +CONDFIL::CONDFIL(const Item *cond, uint idx, AMT type) +{ + Cond = cond; + Idx = idx; + Type = type; + Op = OP_XX; + Cmds = NULL; + Alist = NULL; + All = true; + Bd = false; + Hv = false; + Body = NULL, + Having = NULL; +} // end of CONDFIL constructor + +/***********************************************************************/ +/* Make and allocate the alias list. */ +/***********************************************************************/ +int CONDFIL::Init(PGLOBAL g, PHC hc) +{ + PTOS options = hc->GetTableOptionStruct(); + char *p, *cn, *cal, *alt = NULL; + int rc = RC_OK; + bool h; + + if (options) + alt = GetListOption(g, "Alias", options->oplist, NULL); + + while (alt) { + if (!(p = strchr(alt, '='))) { + strcpy(g->Message, "Invalid alias list"); + rc = RC_FX; + break; + } // endif !p + + cal = alt; // Alias + *p++ = 0; + + if ((h = *p == '*')) { + rc = RC_INFO; + p++; + } // endif h + + cn = p; // Remote column name + + if ((alt = strchr(p, ';'))) + *alt++ = 0; + + if (*cn == 0) + cn = alt; + + Alist = new(g) ALIAS(Alist, cn, cal, h); + } // endwhile alt + + return rc; +} // end of Init + +/***********************************************************************/ +/* Make and allocate the alias list. */ +/***********************************************************************/ +const char *CONDFIL::Chk(const char *fln, bool *h) +{ + for (PAL pal = Alist; pal; pal = pal->Next) + if (!stricmp(fln, pal->Alias)) { + *h = pal->Having; + return pal->Name; + } // endif fln + + *h = false; + return fln; +} // end of Chk + +/* --------------------------- Class EXTDEF -------------------------- */ + +/***********************************************************************/ +/* EXTDEF Constructor. */ +/***********************************************************************/ +EXTDEF::EXTDEF(void) +{ + Tabname = Tabschema = Username = Password = Tabcat = Tabtyp = NULL; + Colpat = Srcdef = Qchar = Qrystr = Sep = Phpos = NULL; + Options = Cto = Qto = Quoted = Maxerr = Maxres = Memory = 0; + Scrollable = Xsrc = false; +} // end of EXTDEF constructor + +/***********************************************************************/ +/* DefineAM: define specific AM block values from XDB file. */ +/***********************************************************************/ +bool EXTDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff) +{ + Desc = NULL; + Tabname = GetStringCatInfo(g, "Name", + (Catfunc & (FNC_TABLE | FNC_COL)) ? NULL : Name); + Tabname = GetStringCatInfo(g, "Tabname", Tabname); + Tabschema = GetStringCatInfo(g, "Dbname", NULL); + Tabschema = GetStringCatInfo(g, "Schema", Tabschema); + Tabcat = GetStringCatInfo(g, "Qualifier", NULL); + Tabcat = GetStringCatInfo(g, "Catalog", Tabcat); + Username = GetStringCatInfo(g, "User", NULL); + Password = GetStringCatInfo(g, "Password", NULL); + + if ((Srcdef = GetStringCatInfo(g, "Srcdef", NULL))) + Read_Only = true; + + Qrystr = GetStringCatInfo(g, "Query_String", "?"); + Sep = GetStringCatInfo(g, "Separator", NULL); +//Alias = GetStringCatInfo(g, "Alias", NULL); + Phpos = GetStringCatInfo(g, "Phpos", NULL); + Xsrc = GetBoolCatInfo("Execsrc", FALSE); + Maxerr = GetIntCatInfo("Maxerr", 0); + Maxres = GetIntCatInfo("Maxres", 0); + Quoted = GetIntCatInfo("Quoted", 0); + Options = 0; + Cto = 0; + Qto = 0; + + if ((Scrollable = GetBoolCatInfo("Scrollable", false)) && !Elemt) + Elemt = 1; // Cannot merge SQLFetch and SQLExtendedFetch + + if (Catfunc == FNC_COL) + Colpat = GetStringCatInfo(g, "Colpat", NULL); + + if (Catfunc == FNC_TABLE) + Tabtyp = GetStringCatInfo(g, "Tabtype", NULL); + + // Memory was Boolean, it is now integer + if (!(Memory = GetIntCatInfo("Memory", 0))) + Memory = GetBoolCatInfo("Memory", false) ? 1 : 0; + + Pseudo = 2; // FILID is Ok but not ROWID + return false; +} // end of DefineAM + +/* ---------------------------TDBEXT class --------------------------- */ + +/***********************************************************************/ +/* Implementation of the TDBEXT class. */ +/***********************************************************************/ +TDBEXT::TDBEXT(EXTDEF *tdp) : TDB(tdp) +{ + Qrp = NULL; + + if (tdp) { + TableName = tdp->Tabname; + Schema = tdp->Tabschema; + User = tdp->Username; + Pwd = tdp->Password; + Catalog = tdp->Tabcat; + Srcdef = tdp->Srcdef; + Qrystr = tdp->Qrystr; + Sep = tdp->GetSep(); + Options = tdp->Options; + Cto = tdp->Cto; + Qto = tdp->Qto; + Quoted = MY_MAX(0, tdp->GetQuoted()); + Rows = tdp->GetElemt(); + Memory = tdp->Memory; + Scrollable = tdp->Scrollable; + } else { + TableName = NULL; + Schema = NULL; + User = NULL; + Pwd = NULL; + Catalog = NULL; + Srcdef = NULL; + Qrystr = NULL; + Sep = 0; + Options = 0; + Cto = 0; + Qto = 0; + Quoted = 0; + Rows = 0; + Memory = 0; + Scrollable = false; + } // endif tdp + + Quote = NULL; + Query = NULL; + Count = NULL; + //Where = NULL; + MulConn = NULL; + DBQ = NULL; + Qrp = NULL; + Fpos = 0; + Curpos = 0; + AftRows = 0; + CurNum = 0; + Rbuf = 0; + BufSize = 0; + Nparm = 0; + Ncol = 0; + Placed = false; +} // end of TDBEXT constructor + +TDBEXT::TDBEXT(PTDBEXT tdbp) : TDB(tdbp) +{ + Qrp = tdbp->Qrp; + TableName = tdbp->TableName; + Schema = tdbp->Schema; + User = tdbp->User; + Pwd = tdbp->Pwd; + Catalog = tdbp->Catalog; + Srcdef = tdbp->Srcdef; + Qrystr = tdbp->Qrystr; + Sep = tdbp->Sep; + Options = tdbp->Options; + Cto = tdbp->Cto; + Qto = tdbp->Qto; + Quoted = tdbp->Quoted; + Rows = tdbp->Rows; + Memory = tdbp->Memory; + Scrollable = tdbp->Scrollable; + Quote = tdbp->Quote; + Query = tdbp->Query; + Count = tdbp->Count; + //Where = tdbp->Where; + MulConn = tdbp->MulConn; + DBQ = tdbp->DBQ; + Fpos = 0; + Curpos = 0; + AftRows = 0; + CurNum = 0; + Rbuf = 0; + BufSize = tdbp->BufSize; + Nparm = tdbp->Nparm; + Ncol = tdbp->Ncol; + Placed = false; +} // end of TDBEXT copy constructor + +/******************************************************************/ +/* Convert an UTF-8 string to latin characters. */ +/******************************************************************/ +int TDBEXT::Decode(char *txt, char *buf, size_t n) +{ + uint dummy_errors; + uint32 len = copy_and_convert(buf, n, &my_charset_latin1, + txt, strlen(txt), + &my_charset_utf8_general_ci, + &dummy_errors); + buf[len] = '\0'; + return 0; +} // end of Decode + +/***********************************************************************/ +/* MakeSQL: make the SQL statement use with remote connection. */ +/* TODO: when implementing remote filtering, column only used in */ +/* local filter should be removed from column list. */ +/***********************************************************************/ +bool TDBEXT::MakeSQL(PGLOBAL g, bool cnt) +{ + char *schmp = NULL, *catp = NULL, buf[NAM_LEN * 3]; + int len; + bool oom = false, first = true; + PTABLE tablep = To_Table; + PCOL colp; + + if (Srcdef) { + if ((catp = strstr(Srcdef, "%s"))) { + char *fil1, *fil2; + PSZ ph = ((EXTDEF*)To_Def)->Phpos; + + if (!ph) + ph = (strstr(catp + 2, "%s")) ? const_cast<char*>("WH") : + const_cast<char*>("W"); + + if (stricmp(ph, "H")) { + fil1 = (To_CondFil && *To_CondFil->Body) + ? To_CondFil->Body : PlugDup(g, "1=1"); + } // endif ph + + if (stricmp(ph, "W")) { + fil2 = (To_CondFil && To_CondFil->Having && *To_CondFil->Having) + ? To_CondFil->Having : PlugDup(g, "1=1"); + } // endif ph + + if (!stricmp(ph, "W")) { + Query = new(g)STRING(g, strlen(Srcdef) + strlen(fil1)); + Query->SetLength(sprintf(Query->GetStr(), Srcdef, fil1)); + } else if (!stricmp(ph, "WH")) { + Query = new(g)STRING(g, strlen(Srcdef) + strlen(fil1) + strlen(fil2)); + Query->SetLength(sprintf(Query->GetStr(), Srcdef, fil1, fil2)); + } else if (!stricmp(ph, "H")) { + Query = new(g)STRING(g, strlen(Srcdef) + strlen(fil2)); + Query->SetLength(sprintf(Query->GetStr(), Srcdef, fil2)); + } else if (!stricmp(ph, "HW")) { + Query = new(g)STRING(g, strlen(Srcdef) + strlen(fil1) + strlen(fil2)); + Query->SetLength(sprintf(Query->GetStr(), Srcdef, fil2, fil1)); + } else { + strcpy(g->Message, "MakeSQL: Wrong place holders specification"); + return true; + } // endif's ph + + } else + Query = new(g)STRING(g, 0, Srcdef); + + return false; + } // endif Srcdef + + // Allocate the string used to contain the Query + Query = new(g)STRING(g, 1023, "SELECT "); + + if (!cnt) { + if (Columns) { + // Normal SQL statement to retrieve results + for (colp = Columns; colp; colp = colp->GetNext()) + if (!colp->IsSpecial()) { + if (!first) + oom |= Query->Append(", "); + else + first = false; + + // Column name can be encoded in UTF-8 + Decode(colp->GetName(), buf, sizeof(buf)); + + if (Quote) { + // Put column name between identifier quotes in case in contains blanks + oom |= Query->Append(Quote); + oom |= Query->Append(buf); + oom |= Query->Append(Quote); + } else + oom |= Query->Append(buf); + + ((PEXTCOL)colp)->SetRank(++Ncol); + } // endif colp + + } else + // !Columns can occur for queries such that sql count(*) from... + // for which we will count the rows from sql * from... + oom |= Query->Append('*'); + + } else + // SQL statement used to retrieve the size of the result + oom |= Query->Append("count(*)"); + + oom |= Query->Append(" FROM "); + + if (Catalog && *Catalog) + catp = Catalog; + + //if (tablep->GetSchema()) + // schmp = (char*)tablep->GetSchema(); + //else + if (Schema && *Schema) + schmp = Schema; + + if (catp) { + oom |= Query->Append(catp); + + if (schmp) { + oom |= Query->Append('.'); + oom |= Query->Append(schmp); + } // endif schmp + + oom |= Query->Append('.'); + } else if (schmp) { + oom |= Query->Append(schmp); + oom |= Query->Append('.'); + } // endif schmp + + // Table name can be encoded in UTF-8 + Decode(TableName, buf, sizeof(buf)); + + if (Quote) { + // Put table name between identifier quotes in case in contains blanks + oom |= Query->Append(Quote); + oom |= Query->Append(buf); + oom |= Query->Append(Quote); + } else + oom |= Query->Append(buf); + + len = Query->GetLength(); + + if (To_CondFil) { + if (Mode == MODE_READ) { + oom |= Query->Append(" WHERE "); + oom |= Query->Append(To_CondFil->Body); + len = Query->GetLength() + 1; + } else + len += (strlen(To_CondFil->Body) + 256); + + } else + len += ((Mode == MODE_READX) ? 256 : 1); + + if (oom || Query->Resize(len)) { + strcpy(g->Message, "MakeSQL: Out of memory"); + return true; + } // endif oom + + if (trace) + htrc("Query=%s\n", Query->GetStr()); + + return false; +} // end of MakeSQL + +/***********************************************************************/ +/* MakeCommand: make the Update or Delete statement to send to the */ +/* MySQL server. Limited to remote values and filtering. */ +/***********************************************************************/ +bool TDBEXT::MakeCommand(PGLOBAL g) +{ + char *p, *stmt, name[68], *body = NULL; + char *qrystr = (char*)PlugSubAlloc(g, NULL, strlen(Qrystr) + 1); + bool qtd = Quoted > 0; + int i = 0, k = 0; + + // Make a lower case copy of the originale query and change + // back ticks to the data source identifier quoting character + do { + qrystr[i] = (Qrystr[i] == '`') ? *Quote : tolower(Qrystr[i]); + } while (Qrystr[i++]); + + if (To_CondFil && (p = strstr(qrystr, " where "))) { + p[7] = 0; // Remove where clause + Qrystr[(p - qrystr) + 7] = 0; + body = To_CondFil->Body; + stmt = (char*)PlugSubAlloc(g, NULL, strlen(qrystr) + + strlen(body) + 64); + } else + stmt = (char*)PlugSubAlloc(g, NULL, strlen(Qrystr) + 64); + + // Check whether the table name is equal to a keyword + // If so, it must be quoted in the original query + strlwr(strcat(strcat(strcpy(name, " "), Name), " ")); + + if (strstr(" update delete low_priority ignore quick from ", name)) { + strlwr(strcat(strcat(strcpy(name, Quote), Name), Quote)); + k += 2; + } else + strlwr(strcpy(name, Name)); // Not a keyword + + if ((p = strstr(qrystr, name))) { + for (i = 0; i < p - qrystr; i++) + stmt[i] = (Qrystr[i] == '`') ? *Quote : Qrystr[i]; + + stmt[i] = 0; + k += i + (int)strlen(Name); + + if (qtd && *(p - 1) == ' ') + strcat(strcat(strcat(stmt, Quote), TableName), Quote); + else + strcat(stmt, TableName); + + i = (int)strlen(stmt); + + do { + stmt[i++] = (Qrystr[k] == '`') ? *Quote : Qrystr[k]; + } while (Qrystr[k++]); + + if (body) + strcat(stmt, body); + + } else { + sprintf(g->Message, "Cannot use this %s command", + (Mode == MODE_UPDATE) ? "UPDATE" : "DELETE"); + return true; + } // endif p + + if (trace) + htrc("Command=%s\n", stmt); + + Query = new(g)STRING(g, 0, stmt); + return (!Query->GetSize()); +} // end of MakeCommand + +/***********************************************************************/ +/* GetRecpos: return the position of last read record. */ +/***********************************************************************/ +int TDBEXT::GetRecpos(void) +{ + return Fpos; +} // end of GetRecpos + +/***********************************************************************/ +/* ODBC GetMaxSize: returns table size estimate in number of lines. */ +/***********************************************************************/ +int TDBEXT::GetMaxSize(PGLOBAL g) +{ + if (MaxSize < 0) { + if (Mode == MODE_DELETE) + // Return 0 in mode DELETE in case of delete all. + MaxSize = 0; + else if (!Cardinality(NULL)) + MaxSize = 10; // To make MySQL happy + else if ((MaxSize = Cardinality(g)) < 0) + MaxSize = 12; // So we can see an error occurred + + } // endif MaxSize + + return MaxSize; +} // end of GetMaxSize + +/***********************************************************************/ +/* Return max size value. */ +/***********************************************************************/ +int TDBEXT::GetProgMax(PGLOBAL g) +{ + return GetMaxSize(g); +} // end of GetProgMax + +/* ---------------------------EXTCOL class --------------------------- */ + +/***********************************************************************/ +/* EXTCOL public constructor. */ +/***********************************************************************/ +EXTCOL::EXTCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am) + : COLBLK(cdp, tdbp, i) +{ + if (cprec) { + Next = cprec->GetNext(); + cprec->SetNext(this); + } else { + Next = tdbp->GetColumns(); + tdbp->SetColumns(this); + } // endif cprec + + if (trace) + htrc(" making new %sCOL C%d %s at %p\n", am, Index, Name, this); + + // Set additional remote access method information for column. + Crp = NULL; + Long = Precision; + To_Val = NULL; + Bufp = NULL; + Blkp = NULL; + Rank = 0; // Not known yet +} // end of JDBCCOL constructor + +/***********************************************************************/ +/* EXTCOL private constructor. */ +/***********************************************************************/ +EXTCOL::EXTCOL(void) : COLBLK() +{ + Crp = NULL; + Buf_Type = TYPE_INT; // This is a count(*) column + + // Set additional Dos access method information for column. + Long = sizeof(int); + To_Val = NULL; + Bufp = NULL; + Blkp = NULL; + Rank = 1; +} // end of EXTCOL constructor + +/***********************************************************************/ +/* EXTCOL constructor used for copying columns. */ +/* tdbp is the pointer to the new table descriptor. */ +/***********************************************************************/ +EXTCOL::EXTCOL(PEXTCOL col1, PTDB tdbp) : COLBLK(col1, tdbp) +{ + Crp = col1->Crp; + Long = col1->Long; + To_Val = col1->To_Val; + Bufp = col1->Bufp; + Blkp = col1->Blkp; + Rank = col1->Rank; +} // end of JDBCCOL copy constructor + +/***********************************************************************/ +/* SetBuffer: prepare a column block for write operation. */ +/***********************************************************************/ +bool EXTCOL::SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check) +{ + if (!(To_Val = value)) { + sprintf(g->Message, MSG(VALUE_ERROR), Name); + return true; + } else if (Buf_Type == value->GetType()) { + // Values are of the (good) column type + if (Buf_Type == TYPE_DATE) { + // If any of the date values is formatted + // output format must be set for the receiving table + if (GetDomain() || ((DTVAL *)value)->IsFormatted()) + goto newval; // This will make a new value; + + } else if (Buf_Type == TYPE_DOUBLE) + // Float values must be written with the correct (column) precision + // Note: maybe this should be forced by ShowValue instead of this ? + value->SetPrec(GetScale()); + + Value = value; // Directly access the external value + } else { + // Values are not of the (good) column type + if (check) { + sprintf(g->Message, MSG(TYPE_VALUE_ERR), Name, + GetTypeName(Buf_Type), GetTypeName(value->GetType())); + return true; + } // endif check + + newval: + if (InitValue(g)) // Allocate the matching value block + return true; + + } // endif's Value, Buf_Type + + // Because Colblk's have been made from a copy of the original TDB in + // case of Update, we must reset them to point to the original one. + if (To_Tdb->GetOrig()) + To_Tdb = (PTDB)To_Tdb->GetOrig(); + + // Set the Column + Status = (ok) ? BUF_EMPTY : BUF_NO; + return false; +} // end of SetBuffer + diff --git a/storage/connect/tabext.h b/storage/connect/tabext.h new file mode 100644 index 00000000000..2ef20c89f2c --- /dev/null +++ b/storage/connect/tabext.h @@ -0,0 +1,200 @@ +/*************** Tabext H Declares Source Code File (.H) ***************/ +/* Name: TABEXT.H Version 1.0 */ +/* */ +/* (C) Copyright to the author Olivier BERTRAND 2017 */ +/* */ +/* This is the EXTDEF, TABEXT and EXTCOL classes definitions. */ +/***********************************************************************/ + +#ifndef __TABEXT_H +#define __TABEXTF_H + +#include "reldef.h" + +typedef class ALIAS *PAL; + +class ALIAS : public BLOCK { + public: + ALIAS(PAL x, PSZ n, PSZ a, bool h) + {Next = x, Name = n, Alias = a, Having = h;} + + PAL Next; + PSZ Name; + PSZ Alias; + bool Having; +}; // end of class ALIAS + +// Condition filter structure +class CONDFIL : public BLOCK { + public: + // Constructor + CONDFIL(const Item *cond, uint idx, AMT type); + + // Functions + int Init(PGLOBAL g, PHC hc); + const char *Chk(const char *cln, bool *h); + + // Members + const Item *Cond; + AMT Type; + uint Idx; + OPVAL Op; + PCMD Cmds; + PAL Alist; + bool All; + bool Bd; + bool Hv; + char *Body; + char *Having; +}; // end of class CONDFIL + +/***********************************************************************/ +/* This class corresponds to the data base description for external */ +/* tables of type MYSQL, ODBC, JDBC... */ +/***********************************************************************/ +class DllExport EXTDEF : public TABDEF { /* EXT table */ + friend class TDBEXT; +public: + // Constructor + EXTDEF(void); // Constructor + + // Implementation + virtual const char *GetType(void) { return "EXT"; } + inline PSZ GetTabname(void) { return Tabname; } + inline PSZ GetTabschema(void) { return Tabschema; } + inline PSZ GetUsername(void) { return Username; }; + inline PSZ GetPassword(void) { return Password; }; + inline PSZ GetTabcat(void) { return Tabcat; } + inline PSZ GetSrcdef(void) { return Srcdef; } + inline char GetSep(void) { return (Sep) ? *Sep : 0; } + inline int GetQuoted(void) { return Quoted; } + inline int GetOptions(void) { return Options; } + + // Methods + virtual int Indexable(void) { return 2; } + virtual bool DefineAM(PGLOBAL g, LPCSTR am, int poff); + +protected: + // Members + PSZ Tabname; /* External table name */ + PSZ Tabschema; /* External table schema */ + PSZ Username; /* User connect name */ + PSZ Password; /* Password connect info */ + PSZ Tabcat; /* External table catalog */ + PSZ Tabtyp; /* Catalog table type */ + PSZ Colpat; /* Catalog column pattern */ + PSZ Srcdef; /* The source table SQL definition */ + PSZ Qchar; /* Identifier quoting character */ + PSZ Qrystr; /* The original query */ + PSZ Sep; /* Decimal separator */ +//PSZ Alias; /* Column alias list */ + PSZ Phpos; /* Place holer positions */ + int Options; /* Open connection options */ + int Cto; /* Open connection timeout */ + int Qto; /* Query (command) timeout */ + int Quoted; /* Identifier quoting level */ + int Maxerr; /* Maxerr for an Exec table */ + int Maxres; /* Maxres for a catalog table */ + int Memory; /* Put result set in memory */ + bool Scrollable; /* Use scrollable cursor */ + bool Xsrc; /* Execution type */ +}; // end of EXTDEF + +/***********************************************************************/ +/* This is the base class for all external tables. */ +/***********************************************************************/ +class DllExport TDBEXT : public TDB { +public: + // Constructors + TDBEXT(EXTDEF *tdp); + TDBEXT(PTDBEXT tdbp); + + // Implementation + + // Properties + virtual bool IsRemote(void) { return true; } + + // Methods + virtual PSZ GetServer(void) { return "Remote"; } + virtual int GetRecpos(void); + + // Database routines + virtual int GetMaxSize(PGLOBAL g); + virtual int GetProgMax(PGLOBAL g); + +protected: + // Internal functions + virtual bool MakeSQL(PGLOBAL g, bool cnt); + //virtual bool MakeInsert(PGLOBAL g); + virtual bool MakeCommand(PGLOBAL g); + int Decode(char *utf, char *buf, size_t n); + + // Members + PQRYRES Qrp; // Points to storage result + PSTRG Query; // Constructed SQL query + char *TableName; // Points to ODBC table name + char *Schema; // Points to ODBC table Schema + char *User; // User connect info + char *Pwd; // Password connect info + char *Catalog; // Points to ODBC table Catalog + char *Srcdef; // The source table SQL definition + char *Count; // Points to count(*) SQL statement + //char *Where; // Points to local where clause + char *Quote; // The identifier quoting character + char *MulConn; // Used for multiple ODBC tables + char *DBQ; // The address part of Connect string + char *Qrystr; // The original query + char Sep; // The decimal separator + int Options; // Connect options + int Cto; // Connect timeout + int Qto; // Query timeout + int Quoted; // The identifier quoting level + int Fpos; // Position of last read record + int Curpos; // Cursor position of last fetch + int AftRows; // The number of affected rows + int Rows; // Rowset size + int CurNum; // Current buffer line number + int Rbuf; // Number of lines read in buffer + int BufSize; // Size of connect string buffer + int Nparm; // The number of statement parameters + int Memory; // 0: No 1: Alloc 2: Put 3: Get + int Ncol; // The column number (JDBC) + bool Scrollable; // Use scrollable cursor + bool Placed; // True for position reading +}; // end of class TDBEXT + +/***********************************************************************/ +/* Virual class EXTCOL: external column. */ +/***********************************************************************/ +class DllExport EXTCOL : public COLBLK { + friend class TDBEXT; +public: + // Constructor + EXTCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am); + EXTCOL(PEXTCOL colp, PTDB tdbp); // Constructor used in copy process + + // Implementation + inline int GetRank(void) { return Rank; } + inline void SetRank(int k) { Rank = k; } + //inline PVBLK GetBlkp(void) {return Blkp;} + inline void SetCrp(PCOLRES crp) { Crp = crp; } + + // Methods + virtual bool SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check); + virtual void ReadColumn(PGLOBAL) = 0; + virtual void WriteColumn(PGLOBAL) = 0; + +protected: + // Constructor for count(*) column + EXTCOL(void); + + // Members + PCOLRES Crp; // To storage result + void *Bufp; // To extended buffer + PVBLK Blkp; // To Value Block + PVAL To_Val; // To value used for Insert + int Rank; // Rank (position) number in the query + //int Flag; // ??? +}; // end of class EXTCOL + +#endif // __TABEXT_H diff --git a/storage/connect/tabfix.cpp b/storage/connect/tabfix.cpp index d99f7800f26..bf123cd36c8 100644 --- a/storage/connect/tabfix.cpp +++ b/storage/connect/tabfix.cpp @@ -77,7 +77,7 @@ TDBFIX::TDBFIX(PGLOBAL g, PTDBFIX tdbp) : TDBDOS(g, tdbp) } // end of TDBFIX copy constructor // Method -PTDB TDBFIX::CopyOne(PTABS t) +PTDB TDBFIX::Clone(PTABS t) { PTDB tp; PGLOBAL g = t->G; @@ -105,7 +105,7 @@ PTDB TDBFIX::CopyOne(PTABS t) } // endif Ftype return tp; - } // end of CopyOne + } // end of Clone /***********************************************************************/ /* Reset read/write position values. */ diff --git a/storage/connect/tabfix.h b/storage/connect/tabfix.h index 49956ba0711..4b9f9689992 100644 --- a/storage/connect/tabfix.h +++ b/storage/connect/tabfix.h @@ -34,7 +34,7 @@ class DllExport TDBFIX : public TDBDOS { {return (PTDB)new(g) TDBFIX(g, this);} // Methods - virtual PTDB CopyOne(PTABS t); + virtual PTDB Clone(PTABS t); virtual void ResetDB(void); virtual bool IsUsingTemp(PGLOBAL g); virtual int RowNumber(PGLOBAL g, bool b = false); diff --git a/storage/connect/tabfmt.cpp b/storage/connect/tabfmt.cpp index b24375443f6..0da67ef5e7f 100644 --- a/storage/connect/tabfmt.cpp +++ b/storage/connect/tabfmt.cpp @@ -5,7 +5,7 @@ /* */ /* COPYRIGHT: */ /* ---------- */ -/* (C) Copyright to the author Olivier BERTRAND 2001 - 2016 */ +/* (C) Copyright to the author Olivier BERTRAND 2001 - 2017 */ /* */ /* WHAT THIS PROGRAM DOES: */ /* ----------------------- */ @@ -98,8 +98,9 @@ PQRYRES CSVColumns(PGLOBAL g, char *dp, PTOS topt, bool info) int num_read = 0, num_max = 10000000; // Statistics int len[MAXCOL], typ[MAXCOL], prc[MAXCOL]; PCSVDEF tdp; - PTDBCSV tdbp; - PQRYRES qrp; + PTDBCSV tcvp; + PTDBASE tdbp; + PQRYRES qrp; PCOLRES crp; if (info) { @@ -108,10 +109,10 @@ PQRYRES CSVColumns(PGLOBAL g, char *dp, PTOS topt, bool info) goto skipit; } // endif info - if (GetIntegerTableOption(g, topt, "Multiple", 0)) { - strcpy(g->Message, "Cannot find column definition for multiple table"); - return NULL; - } // endif Multiple + //if (GetIntegerTableOption(g, topt, "Multiple", 0)) { + // strcpy(g->Message, "Cannot find column definition for multiple table"); + // return NULL; + //} // endif Multiple // num_max = atoi(p+1); // Max num of record to test imax = hmax = nerr = 0; @@ -127,10 +128,20 @@ PQRYRES CSVColumns(PGLOBAL g, char *dp, PTOS topt, bool info) /* Get the CSV table description block. */ /*********************************************************************/ tdp = new(g) CSVDEF; + tdp->Database = dp; + + if ((tdp->Zipped = GetBooleanTableOption(g, topt, "Zipped", false))) { #if defined(ZIP_SUPPORT) - tdp->Entry = GetStringTableOption(g, topt, "Entry", NULL); - tdp->Zipped = GetBooleanTableOption(g, topt, "Zipped", false); -#endif // ZIP_SUPPORT + tdp->Entry = GetStringTableOption(g, topt, "Entry", NULL); + tdp->Mulentries = (tdp->Entry) + ? strchr(tdp->Entry, '*') || strchr(tdp->Entry, '?') + : GetBooleanTableOption(g, topt, "Mulentries", false); +#else // !ZIP_SUPPORT + strcpy(g->Message, "ZIP not supported by this version"); + return NULL; +#endif // !ZIP_SUPPORT + } // endif // Zipped + fn = tdp->Fn = GetStringTableOption(g, topt, "Filename", NULL); if (!tdp->Fn) { @@ -141,6 +152,7 @@ PQRYRES CSVColumns(PGLOBAL g, char *dp, PTOS topt, bool info) if (!(tdp->Lrecl = GetIntegerTableOption(g, topt, "Lrecl", 0))) tdp->Lrecl = 4096; + tdp->Multiple = GetIntegerTableOption(g, topt, "Multiple", 0); p = GetStringTableOption(g, topt, "Separator", ","); tdp->Sep = (strlen(p) == 2 && p[0] == '\\' && p[1] == 't') ? '\t' : *p; @@ -177,17 +189,18 @@ PQRYRES CSVColumns(PGLOBAL g, char *dp, PTOS topt, bool info) htrc("File %s Sep=%c Qot=%c Header=%d maxerr=%d\n", SVP(tdp->Fn), tdp->Sep, tdp->Qot, tdp->Header, tdp->Maxerr); - if (tdp->Zipped) { -#if defined(ZIP_SUPPORT) - tdbp = new(g)TDBCSV(tdp, new(g)ZIPFAM(tdp)); -#else // !ZIP_SUPPORT - sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "ZIP"); - return NULL; -#endif // !ZIP_SUPPORT - } else - tdbp = new(g) TDBCSV(tdp, new(g) DOSFAM(tdp)); + if (tdp->Zipped) + tcvp = new(g)TDBCSV(tdp, new(g)UNZFAM(tdp)); + else + tcvp = new(g) TDBCSV(tdp, new(g) DOSFAM(tdp)); + + tcvp->SetMode(MODE_READ); - tdbp->SetMode(MODE_READ); + if (tdp->Multiple) { + tdbp = new(g)TDBMUL(tcvp); + tdbp->SetMode(MODE_READ); + } else + tdbp = tcvp; /*********************************************************************/ /* Open the CSV file. */ @@ -202,7 +215,7 @@ PQRYRES CSVColumns(PGLOBAL g, char *dp, PTOS topt, bool info) phase = 0; if ((rc = tdbp->ReadDB(g)) == RC_OK) { - p = PlgDBDup(g, tdbp->To_Line); + p = PlgDBDup(g, tcvp->To_Line); //skip leading blanks for (; *p == ' '; p++) ; @@ -245,6 +258,7 @@ PQRYRES CSVColumns(PGLOBAL g, char *dp, PTOS topt, bool info) for (i = 0; i < hmax; i++) length[0] = MY_MAX(length[0], strlen(colname[i])); + tcvp->Header = true; // In case of multiple table } // endif hdr for (num_read++; num_read <= num_max; num_read++) { @@ -265,7 +279,7 @@ PQRYRES CSVColumns(PGLOBAL g, char *dp, PTOS topt, bool info) /*******************************************************************/ i = n = phase = blank = digit = dec = 0; - for (p = tdbp->To_Line; *p; p++) + for (p = tcvp->To_Line; *p; p++) if (*p == sep) { if (phase != 1) { if (i == MAXCOL - 1) { @@ -503,7 +517,14 @@ PTDB CSVDEF::GetTable(PGLOBAL g, MODE mode) /*******************************************************************/ if (Zipped) { #if defined(ZIP_SUPPORT) - txfp = new(g) ZIPFAM(this); + if (mode == MODE_READ || mode == MODE_ANY) { + txfp = new(g) UNZFAM(this); + } else if (mode == MODE_INSERT) { + txfp = new(g) ZIPFAM(this); + } else { + strcpy(g->Message, "UPDATE/DELETE not supported for ZIP"); + return NULL; + } // endif's mode #else // !ZIP_SUPPORT strcpy(g->Message, "ZIP not supported"); return NULL; @@ -640,7 +661,7 @@ TDBCSV::TDBCSV(PGLOBAL g, PTDBCSV tdbp) : TDBDOS(g, tdbp) } // end of TDBCSV copy constructor // Method -PTDB TDBCSV::CopyOne(PTABS t) +PTDB TDBCSV::Clone(PTABS t) { PTDB tp; PCSVCOL cp1, cp2; @@ -654,7 +675,7 @@ PTDB TDBCSV::CopyOne(PTABS t) } // endfor cp1 return tp; - } // end of CopyOne + } // end of Clone /***********************************************************************/ /* Allocate CSV column description block. */ @@ -1148,7 +1169,7 @@ TDBFMT::TDBFMT(PGLOBAL g, PTDBFMT tdbp) : TDBCSV(g, tdbp) } // end of TDBFMT copy constructor // Method -PTDB TDBFMT::CopyOne(PTABS t) +PTDB TDBFMT::Clone(PTABS t) { PTDB tp; PCSVCOL cp1, cp2; @@ -1165,7 +1186,7 @@ PTDB TDBFMT::CopyOne(PTABS t) } // endfor cp1 return tp; - } // end of CopyOne + } // end of Clone /***********************************************************************/ /* Allocate FMT column description block. */ diff --git a/storage/connect/tabfmt.h b/storage/connect/tabfmt.h index 5ce8d399a64..e5655435be7 100644 --- a/storage/connect/tabfmt.h +++ b/storage/connect/tabfmt.h @@ -52,6 +52,7 @@ public: /***********************************************************************/ class DllExport TDBCSV : public TDBDOS { friend class CSVCOL; + friend class MAPFAM; friend PQRYRES CSVColumns(PGLOBAL, char *, PTOS, bool); public: // Constructor @@ -64,7 +65,7 @@ public: {return (PTDB)new(g) TDBCSV(g, this);} // Methods - virtual PTDB CopyOne(PTABS t); + virtual PTDB Clone(PTABS t); //virtual bool IsUsingTemp(PGLOBAL g); virtual int GetBadLines(void) {return (int)Nerr;} @@ -147,7 +148,7 @@ class DllExport TDBFMT : public TDBCSV { {return (PTDB)new(g) TDBFMT(g, this);} // Methods - virtual PTDB CopyOne(PTABS t); + virtual PTDB Clone(PTABS t); // Database routines virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n); diff --git a/storage/connect/tabjdbc.cpp b/storage/connect/tabjdbc.cpp index 912e6c7d530..5431e35e0ec 100644 --- a/storage/connect/tabjdbc.cpp +++ b/storage/connect/tabjdbc.cpp @@ -1,11 +1,11 @@ /************* TabJDBC C++ Program Source Code File (.CPP) *************/ /* PROGRAM NAME: TABJDBC */ /* ------------- */ -/* Version 1.1 */ +/* Version 1.2 */ /* */ /* COPYRIGHT: */ /* ---------- */ -/* (C) Copyright to the author Olivier BERTRAND 2016 */ +/* (C) Copyright to the author Olivier BERTRAND 2016-2017 */ /* */ /* WHAT THIS PROGRAM DOES: */ /* ----------------------- */ @@ -69,9 +69,10 @@ #include "plgdbsem.h" #include "mycat.h" #include "xtable.h" +#include "tabext.h" #include "tabjdbc.h" #include "tabmul.h" -#include "reldef.h" +//#include "reldef.h" #include "tabcol.h" #include "valblk.h" #include "ha_connect.h" @@ -96,10 +97,7 @@ bool ExactInfo(void); /***********************************************************************/ JDBCDEF::JDBCDEF(void) { - Driver = Url = Wrapname =Tabname = Tabschema = Username = Colpat = NULL; - Password = Tabcat = Tabtype = Srcdef = Qchar = Qrystr = Sep = NULL; - Options = Quoted = Maxerr = Maxres = Memory = 0; - Scrollable = Xsrc = false; + Driver = Url = Wrapname = NULL; } // end of JDBCDEF constructor /***********************************************************************/ @@ -134,23 +132,26 @@ bool JDBCDEF::SetParms(PJPARM sjp) int JDBCDEF::ParseURL(PGLOBAL g, char *url, bool b) { if (strncmp(url, "jdbc:", 5)) { + PSZ p; + // No "jdbc:" in connection string. Must be a straight // "server" or "server/table" // ok, so we do a little parsing, but not completely! - if ((Tabname= strchr(url, '/'))) { + if ((p = strchr(url, '/'))) { // If there is a single '/' in the connection string, // this means the user is specifying a table name - *Tabname++= '\0'; + *p++= '\0'; // there better not be any more '/'s ! - if (strchr(Tabname, '/')) + if (strchr(p, '/')) return RC_FX; - } else if (b) { - // Otherwise, straight server name, - Tabname = GetStringCatInfo(g, "Name", NULL); - Tabname = GetStringCatInfo(g, "Tabname", Tabname); - } // endelse + Tabname = p; +// } else if (b) { +// // Otherwise, straight server name, +// Tabname = GetStringCatInfo(g, "Name", NULL); +// Tabname = GetStringCatInfo(g, "Tabname", Tabname); + } // endif if (trace) htrc("server: %s Tabname: %s", url, Tabname); @@ -204,6 +205,9 @@ bool JDBCDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff) { int rc = RC_OK; + if (EXTDEF::DefineAM(g, am, poff)) + return true; + Driver = GetStringCatInfo(g, "Driver", NULL); Desc = Url = GetStringCatInfo(g, "Connect", NULL); @@ -223,41 +227,41 @@ bool JDBCDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff) if (rc == RC_FX) // Error return true; - else if (rc == RC_OK) { // Url was not a server name - Tabname = GetStringCatInfo(g, "Name", - (Catfunc & (FNC_TABLE | FNC_COL)) ? NULL : Name); - Tabname = GetStringCatInfo(g, "Tabname", Tabname); - Username = GetStringCatInfo(g, "User", NULL); - Password = GetStringCatInfo(g, "Password", NULL); - } // endif rc +//else if (rc == RC_OK) { // Url was not a server name +// Tabname = GetStringCatInfo(g, "Name", +// (Catfunc & (FNC_TABLE | FNC_COL)) ? NULL : Name); +// Tabname = GetStringCatInfo(g, "Tabname", Tabname); +// Username = GetStringCatInfo(g, "User", NULL); +// Password = GetStringCatInfo(g, "Password", NULL); +//} // endif rc - if ((Srcdef = GetStringCatInfo(g, "Srcdef", NULL))) - Read_Only = true; +//if ((Srcdef = GetStringCatInfo(g, "Srcdef", NULL))) +// Read_Only = true; Wrapname = GetStringCatInfo(g, "Wrapper", NULL); //Prop = GetStringCatInfo(g, "Properties", NULL); - Tabcat = GetStringCatInfo(g, "Qualifier", NULL); - Tabcat = GetStringCatInfo(g, "Catalog", Tabcat); - Tabschema = GetStringCatInfo(g, "Dbname", NULL); - Tabschema = GetStringCatInfo(g, "Schema", Tabschema); - - if (Catfunc == FNC_COL) - Colpat = GetStringCatInfo(g, "Colpat", NULL); - - if (Catfunc == FNC_TABLE) - Tabtype = GetStringCatInfo(g, "Tabtype", NULL); - - Qrystr = GetStringCatInfo(g, "Query_String", "?"); - Sep = GetStringCatInfo(g, "Separator", NULL); - Xsrc = GetBoolCatInfo("Execsrc", FALSE); - Maxerr = GetIntCatInfo("Maxerr", 0); - Maxres = GetIntCatInfo("Maxres", 0); - Quoted = GetIntCatInfo("Quoted", 0); -//Cto= GetIntCatInfo("ConnectTimeout", DEFAULT_LOGIN_TIMEOUT); -//Qto= GetIntCatInfo("QueryTimeout", DEFAULT_QUERY_TIMEOUT); - Scrollable = GetBoolCatInfo("Scrollable", false); - Memory = GetIntCatInfo("Memory", 0); - Pseudo = 2; // FILID is Ok but not ROWID +//Tabcat = GetStringCatInfo(g, "Qualifier", NULL); +//Tabcat = GetStringCatInfo(g, "Catalog", Tabcat); +//Tabschema = GetStringCatInfo(g, "Dbname", NULL); +//Tabschema = GetStringCatInfo(g, "Schema", Tabschema); + +//if (Catfunc == FNC_COL) +// Colpat = GetStringCatInfo(g, "Colpat", NULL); + +//if (Catfunc == FNC_TABLE) +// Tabtyp = GetStringCatInfo(g, "Tabtype", NULL); + +//Qrystr = GetStringCatInfo(g, "Query_String", "?"); +//Sep = GetStringCatInfo(g, "Separator", NULL); +//Xsrc = GetBoolCatInfo("Execsrc", FALSE); +//Maxerr = GetIntCatInfo("Maxerr", 0); +//Maxres = GetIntCatInfo("Maxres", 0); +//Quoted = GetIntCatInfo("Quoted", 0); +// Cto= GetIntCatInfo("ConnectTimeout", DEFAULT_LOGIN_TIMEOUT); +// Qto= GetIntCatInfo("QueryTimeout", DEFAULT_QUERY_TIMEOUT); +//Scrollable = GetBoolCatInfo("Scrollable", false); +//Memory = GetIntCatInfo("Memory", 0); +//Pseudo = 2; // FILID is Ok but not ROWID return false; } // end of DefineAM @@ -266,7 +270,7 @@ bool JDBCDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff) /***********************************************************************/ PTDB JDBCDEF::GetTable(PGLOBAL g, MODE m) { - PTDBASE tdbp = NULL; + PTDB tdbp = NULL; /*********************************************************************/ /* Allocate a TDB of the proper type. */ @@ -326,7 +330,7 @@ int JDBCPARM::CheckSize(int rows) /***********************************************************************/ /* Implementation of the TDBJDBC class. */ /***********************************************************************/ -TDBJDBC::TDBJDBC(PJDBCDEF tdp) : TDBASE(tdp) +TDBJDBC::TDBJDBC(PJDBCDEF tdp) : TDBEXT(tdp) { Jcp = NULL; Cnp = NULL; @@ -335,101 +339,45 @@ TDBJDBC::TDBJDBC(PJDBCDEF tdp) : TDBASE(tdp) Ops.Driver = tdp->Driver; Ops.Url = tdp->Url; WrapName = tdp->Wrapname; - TableName = tdp->Tabname; - Schema = tdp->Tabschema; Ops.User = tdp->Username; Ops.Pwd = tdp->Password; // Ops.Properties = tdp->Prop; - Catalog = tdp->Tabcat; - Srcdef = tdp->Srcdef; - Qrystr = tdp->Qrystr; - Sep = tdp->GetSep(); - Options = tdp->Options; // Ops.Cto = tdp->Cto; // Ops.Qto = tdp->Qto; - Quoted = MY_MAX(0, tdp->GetQuoted()); - Rows = tdp->GetElemt(); - Memory = tdp->Memory; Ops.Scrollable = tdp->Scrollable; } else { WrapName = NULL; - TableName = NULL; - Schema = NULL; Ops.Driver = NULL; Ops.Url = NULL; Ops.User = NULL; Ops.Pwd = NULL; // Ops.Properties = NULL; - Catalog = NULL; - Srcdef = NULL; - Qrystr = NULL; - Sep = 0; - Options = 0; // Ops.Cto = DEFAULT_LOGIN_TIMEOUT; // Ops.Qto = DEFAULT_QUERY_TIMEOUT; - Quoted = 0; - Rows = 0; - Memory = 0; Ops.Scrollable = false; } // endif tdp - Quote = NULL; - Query = NULL; - Count = NULL; -//Where = NULL; - MulConn = NULL; - DBQ = NULL; - Qrp = NULL; - Fpos = 0; - Curpos = 0; - AftRows = 0; - CurNum = 0; - Rbuf = 0; - BufSize = 0; - Ncol = 0; - Nparm = 0; - Placed = false; +//Ncol = 0; Prepared = false; Werr = false; Rerr = false; Ops.Fsize = Ops.CheckSize(Rows); } // end of TDBJDBC standard constructor -TDBJDBC::TDBJDBC(PTDBJDBC tdbp) : TDBASE(tdbp) +TDBJDBC::TDBJDBC(PTDBJDBC tdbp) : TDBEXT(tdbp) { Jcp = tdbp->Jcp; // is that right ? Cnp = tdbp->Cnp; WrapName = tdbp->WrapName; - TableName = tdbp->TableName; - Schema = tdbp->Schema; Ops = tdbp->Ops; - Catalog = tdbp->Catalog; - Srcdef = tdbp->Srcdef; - Qrystr = tdbp->Qrystr; - Memory = tdbp->Memory; -//Scrollable = tdbp->Scrollable; - Quote = tdbp->Quote; - Query = tdbp->Query; - Count = tdbp->Count; -//Where = tdbp->Where; - MulConn = tdbp->MulConn; - DBQ = tdbp->DBQ; - Options = tdbp->Options; - Quoted = tdbp->Quoted; - Rows = tdbp->Rows; - Fpos = 0; - Curpos = 0; - AftRows = 0; - CurNum = 0; - Rbuf = 0; - BufSize = tdbp->BufSize; - Nparm = tdbp->Nparm; - Qrp = tdbp->Qrp; - Placed = false; +//Ncol = tdbp->Ncol; + Prepared = tdbp->Prepared; + Werr = tdbp->Werr; + Rerr = tdbp->Rerr; } // end of TDBJDBC copy constructor // Method -PTDB TDBJDBC::CopyOne(PTABS t) +PTDB TDBJDBC::Clone(PTABS t) { PTDB tp; PJDBCCOL cp1, cp2; @@ -443,7 +391,7 @@ PTDB TDBJDBC::CopyOne(PTABS t) } // endfor cp1 return tp; -} // end of CopyOne +} // end of Clone /***********************************************************************/ /* Allocate JDBC column description block. */ @@ -453,134 +401,6 @@ PCOL TDBJDBC::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n) return new(g)JDBCCOL(cdp, this, cprec, n); } // end of MakeCol -/******************************************************************/ -/* Convert an UTF-8 string to latin characters. */ -/******************************************************************/ -int TDBJDBC::Decode(char *txt, char *buf, size_t n) -{ - uint dummy_errors; - uint32 len= copy_and_convert(buf, n, &my_charset_latin1, - txt, strlen(txt), - &my_charset_utf8_general_ci, - &dummy_errors); - buf[len]= '\0'; - return 0; -} // end of Decode - -/***********************************************************************/ -/* MakeSQL: make the SQL statement use with JDBC connection. */ -/* TODO: when implementing EOM filtering, column only used in local */ -/* filter should be removed from column list. */ -/***********************************************************************/ -bool TDBJDBC::MakeSQL(PGLOBAL g, bool cnt) -{ - char *schmp = NULL, *catp = NULL, buf[NAM_LEN * 3]; - int len; - bool oom = false, first = true; - PTABLE tablep = To_Table; - PCOL colp; - - if (Srcdef) { - Query = new(g)STRING(g, 0, Srcdef); - return false; - } // endif Srcdef - - // Allocate the string used to contain the Query - Query = new(g)STRING(g, 1023, "SELECT "); - - if (!cnt) { - if (Columns) { - // Normal SQL statement to retrieve results - for (colp = Columns; colp; colp = colp->GetNext()) - if (!colp->IsSpecial()) { - if (!first) - oom |= Query->Append(", "); - else - first = false; - - // Column name can be encoded in UTF-8 - Decode(colp->GetName(), buf, sizeof(buf)); - - if (Quote) { - // Put column name between identifier quotes in case in contains blanks - oom |= Query->Append(Quote); - oom |= Query->Append(buf); - oom |= Query->Append(Quote); - } else - oom |= Query->Append(buf); - - ((PJDBCCOL)colp)->Rank = ++Ncol; - } // endif colp - - } else - // !Columns can occur for queries such that sql count(*) from... - // for which we will count the rows from sql * from... - oom |= Query->Append('*'); - - } else - // SQL statement used to retrieve the size of the result - oom |= Query->Append("count(*)"); - - oom |= Query->Append(" FROM "); - - if (Catalog && *Catalog) - catp = Catalog; - - //if (tablep->GetSchema()) - // schmp = (char*)tablep->GetSchema(); - //else - if (Schema && *Schema) - schmp = Schema; - - if (catp) { - oom |= Query->Append(catp); - - if (schmp) { - oom |= Query->Append('.'); - oom |= Query->Append(schmp); - } // endif schmp - - oom |= Query->Append('.'); - } else if (schmp) { - oom |= Query->Append(schmp); - oom |= Query->Append('.'); - } // endif schmp - - // Table name can be encoded in UTF-8 - Decode(TableName, buf, sizeof(buf)); - - if (Quote) { - // Put table name between identifier quotes in case in contains blanks - oom |= Query->Append(Quote); - oom |= Query->Append(buf); - oom |= Query->Append(Quote); - } else - oom |= Query->Append(buf); - - len = Query->GetLength(); - - if (To_CondFil) { - if (Mode == MODE_READ) { - oom |= Query->Append(" WHERE "); - oom |= Query->Append(To_CondFil->Body); - len = Query->GetLength() + 1; - } else - len += (strlen(To_CondFil->Body) + 256); - - } else - len += ((Mode == MODE_READX) ? 256 : 1); - - if (oom || Query->Resize(len)) { - strcpy(g->Message, "MakeSQL: Out of memory"); - return true; - } // endif oom - - if (trace) - htrc("Query=%s\n", Query->GetStr()); - - return false; -} // end of MakeSQL - /***********************************************************************/ /* MakeInsert: make the Insert statement used with JDBC connection. */ /***********************************************************************/ @@ -601,7 +421,7 @@ bool TDBJDBC::MakeInsert(PGLOBAL g) // Column name can be encoded in UTF-8 Decode(colp->GetName(), buf, sizeof(buf)); len += (strlen(buf) + 6); // comma + quotes + valist - ((PJDBCCOL)colp)->Rank = ++Nparm; + ((PEXTCOL)colp)->SetRank(++Nparm); } // endif colp // Below 32 is enough to contain the fixed part of the query @@ -711,76 +531,6 @@ bool TDBJDBC::SetParameters(PGLOBAL g) } // end of SetParameters /***********************************************************************/ -/* MakeCommand: make the Update or Delete statement to send to the */ -/* MySQL server. Limited to remote values and filtering. */ -/***********************************************************************/ -bool TDBJDBC::MakeCommand(PGLOBAL g) -{ - char *p, *stmt, name[68], *body = NULL, *qc = Jcp->GetQuoteChar(); - char *qrystr = (char*)PlugSubAlloc(g, NULL, strlen(Qrystr) + 1); - bool qtd = Quoted > 0; - int i = 0, k = 0; - - // Make a lower case copy of the originale query and change - // back ticks to the data source identifier quoting character - do { - qrystr[i] = (Qrystr[i] == '`') ? *qc : tolower(Qrystr[i]); - } while (Qrystr[i++]); - - if (To_CondFil && (p = strstr(qrystr, " where "))) { - p[7] = 0; // Remove where clause - Qrystr[(p - qrystr) + 7] = 0; - body = To_CondFil->Body; - stmt = (char*)PlugSubAlloc(g, NULL, strlen(qrystr) - + strlen(body) + 64); - } else - stmt = (char*)PlugSubAlloc(g, NULL, strlen(Qrystr) + 64); - - // Check whether the table name is equal to a keyword - // If so, it must be quoted in the original query - strlwr(strcat(strcat(strcpy(name, " "), Name), " ")); - - if (strstr(" update delete low_priority ignore quick from ", name)) { - strlwr(strcat(strcat(strcpy(name, qc), Name), qc)); - k += 2; - } else - strlwr(strcpy(name, Name)); // Not a keyword - - if ((p = strstr(qrystr, name))) { - for (i = 0; i < p - qrystr; i++) - stmt[i] = (Qrystr[i] == '`') ? *qc : Qrystr[i]; - - stmt[i] = 0; - k += i + (int)strlen(Name); - - if (qtd && *(p-1) == ' ') - strcat(strcat(strcat(stmt, qc), TableName), qc); - else - strcat(stmt, TableName); - - i = (int)strlen(stmt); - - do { - stmt[i++] = (Qrystr[k] == '`') ? *qc : Qrystr[k]; - } while (Qrystr[k++]); - - if (body) - strcat(stmt, body); - - } else { - sprintf(g->Message, "Cannot use this %s command", - (Mode == MODE_UPDATE) ? "UPDATE" : "DELETE"); - return NULL; - } // endif p - - if (trace) - htrc("Command=%s\n", stmt); - - Query = new(g)STRING(g, 0, stmt); - return (!Query->GetSize()); -} // end of MakeCommand - -/***********************************************************************/ /* ResetSize: call by TDBMUL when calculating size estimate. */ /***********************************************************************/ void TDBJDBC::ResetSize(void) @@ -834,33 +584,6 @@ int TDBJDBC::Cardinality(PGLOBAL g) } // end of Cardinality /***********************************************************************/ -/* JDBC GetMaxSize: returns table size estimate in number of lines. */ -/***********************************************************************/ -int TDBJDBC::GetMaxSize(PGLOBAL g) -{ - if (MaxSize < 0) { - if (Mode == MODE_DELETE) - // Return 0 in mode DELETE in case of delete all. - MaxSize = 0; - else if (!Cardinality(NULL)) - MaxSize = 10; // To make MySQL happy - else if ((MaxSize = Cardinality(g)) < 0) - MaxSize = 12; // So we can see an error occured - - } // endif MaxSize - - return MaxSize; -} // end of GetMaxSize - -/***********************************************************************/ -/* Return max size value. */ -/***********************************************************************/ -int TDBJDBC::GetProgMax(PGLOBAL g) -{ - return GetMaxSize(g); -} // end of GetProgMax - -/***********************************************************************/ /* JDBC Access Method opening routine. */ /* New method now that this routine is called recursively (last table */ /* first in reverse order): index blocks are immediately linked to */ @@ -997,6 +720,7 @@ bool TDBJDBC::OpenDB(PGLOBAL g) return false; } // end of OpenDB +#if 0 /***********************************************************************/ /* GetRecpos: return the position of last read record. */ /***********************************************************************/ @@ -1004,6 +728,7 @@ int TDBJDBC::GetRecpos(void) { return Fpos; } // end of GetRecpos +#endif // 0 /***********************************************************************/ /* SetRecpos: set the position of next read record. */ @@ -1105,8 +830,7 @@ int TDBJDBC::ReadDB(PGLOBAL g) int rc; if (trace > 1) - htrc("JDBC ReadDB: R%d Mode=%d key=%p link=%p Kindex=%p\n", - GetTdb_No(), Mode, To_Key_Col, To_Link, To_Kindex); + htrc("JDBC ReadDB: R%d Mode=%d\n", GetTdb_No(), Mode); if (Mode == MODE_UPDATE || Mode == MODE_DELETE) { if (!Query && MakeCommand(g)) @@ -1125,12 +849,6 @@ int TDBJDBC::ReadDB(PGLOBAL g) } // endif Mode - if (To_Kindex) { - // Direct access of JDBC tables is not implemented - strcpy(g->Message, "No JDBC direct access"); - return RC_FX; - } // endif To_Kindex - /*********************************************************************/ /* Now start the reading process. */ /* Here is the place to fetch the line(s). */ @@ -1302,70 +1020,26 @@ void TDBJDBC::CloseDB(PGLOBAL g) /* JDBCCOL public constructor. */ /***********************************************************************/ JDBCCOL::JDBCCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am) - : COLBLK(cdp, tdbp, i) + : EXTCOL(cdp, tdbp, cprec, i, am) { - if (cprec) { - Next = cprec->GetNext(); - cprec->SetNext(this); - } else { - Next = tdbp->GetColumns(); - tdbp->SetColumns(this); - } // endif cprec - - // Set additional JDBC access method information for column. - Crp = NULL; - //Long = cdp->GetLong(); - Long = Precision; - //strcpy(F_Date, cdp->F_Date); - To_Val = NULL; -//Slen = 0; -//StrLen = &Slen; -//Sqlbuf = NULL; - Bufp = NULL; - Blkp = NULL; - Rank = 0; // Not known yet - - if (trace) - htrc(" making new %sCOL C%d %s at %p\n", am, Index, Name, this); - } // end of JDBCCOL constructor /***********************************************************************/ /* JDBCCOL private constructor. */ /***********************************************************************/ -JDBCCOL::JDBCCOL(void) : COLBLK() +JDBCCOL::JDBCCOL(void) : EXTCOL() { - Crp = NULL; - Buf_Type = TYPE_INT; // This is a count(*) column - // Set additional Dos access method information for column. - Long = sizeof(int); - To_Val = NULL; -//Slen = 0; -//StrLen = &Slen; -//Sqlbuf = NULL; - Bufp = NULL; - Blkp = NULL; - Rank = 1; } // end of JDBCCOL constructor /***********************************************************************/ /* JDBCCOL constructor used for copying columns. */ /* tdbp is the pointer to the new table descriptor. */ /***********************************************************************/ -JDBCCOL::JDBCCOL(JDBCCOL *col1, PTDB tdbp) : COLBLK(col1, tdbp) +JDBCCOL::JDBCCOL(JDBCCOL *col1, PTDB tdbp) : EXTCOL(col1, tdbp) { - Crp = col1->Crp; - Long = col1->Long; - //strcpy(F_Date, col1->F_Date); - To_Val = col1->To_Val; -//Slen = col1->Slen; -//StrLen = col1->StrLen; -//Sqlbuf = col1->Sqlbuf; - Bufp = col1->Bufp; - Blkp = col1->Blkp; - Rank = col1->Rank; } // end of JDBCCOL copy constructor +#if 0 /***********************************************************************/ /* SetBuffer: prepare a column block for write operation. */ /***********************************************************************/ @@ -1411,6 +1085,7 @@ bool JDBCCOL::SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check) Status = (ok) ? BUF_EMPTY : BUF_NO; return false; } // end of SetBuffer +#endif // 0 /***********************************************************************/ /* ReadColumn: when SQLFetch is used there is nothing to do as the */ @@ -1456,72 +1131,8 @@ void JDBCCOL::ReadColumn(PGLOBAL g) } // end of ReadColumn -#if 0 /***********************************************************************/ -/* AllocateBuffers: allocate the extended buffer for SQLExtendedFetch */ -/* or Fetch. Note: we use Long+1 here because JDBC must have space */ -/* for the ending null character. */ -/***********************************************************************/ -void JDBCCOL::AllocateBuffers(PGLOBAL g, int rows) -{ - if (Buf_Type == TYPE_DATE) - Sqlbuf = (TIMESTAMP_STRUCT*)PlugSubAlloc(g, NULL, - sizeof(TIMESTAMP_STRUCT)); - - if (!rows) - return; - - if (Buf_Type == TYPE_DATE) - Bufp = PlugSubAlloc(g, NULL, rows * sizeof(TIMESTAMP_STRUCT)); - else { - Blkp = AllocValBlock(g, NULL, Buf_Type, rows, GetBuflen(), - GetScale(), true, false, false); - Bufp = Blkp->GetValPointer(); - } // endelse - - if (rows > 1) - StrLen = (SQLLEN *)PlugSubAlloc(g, NULL, rows * sizeof(SQLLEN)); - -} // end of AllocateBuffers - -/***********************************************************************/ -/* Returns the buffer to use for Fetch or Extended Fetch. */ -/***********************************************************************/ -void *JDBCCOL::GetBuffer(DWORD rows) -{ - if (rows && To_Tdb) { - assert(rows == (DWORD)((TDBJDBC*)To_Tdb)->Rows); - return Bufp; - } else - return (Buf_Type == TYPE_DATE) ? Sqlbuf : Value->GetTo_Val(); - -} // end of GetBuffer - -/***********************************************************************/ -/* Returns the buffer length to use for Fetch or Extended Fetch. */ -/***********************************************************************/ -SWORD JDBCCOL::GetBuflen(void) -{ - SWORD flen; - - switch (Buf_Type) { - case TYPE_DATE: - flen = (SWORD)sizeof(TIMESTAMP_STRUCT); - break; - case TYPE_STRING: - case TYPE_DECIM: - flen = (SWORD)Value->GetClen() + 1; - break; - default: - flen = (SWORD)Value->GetClen(); - } // endswitch Buf_Type - - return flen; -} // end of GetBuflen -#endif // 0 - -/***********************************************************************/ -/* WriteColumn: make sure the bind buffer is updated. */ +/* WriteColumn: Convert if necessary. */ /***********************************************************************/ void JDBCCOL::WriteColumn(PGLOBAL g) { @@ -1531,30 +1142,6 @@ void JDBCCOL::WriteColumn(PGLOBAL g) if (Value != To_Val) Value->SetValue_pval(To_Val, FALSE); // Convert the inserted value -#if 0 - if (Buf_Type == TYPE_DATE) { - struct tm tm, *dbtime = ((DTVAL*)Value)->GetGmTime(&tm); - - Sqlbuf->second = dbtime->tm_sec; - Sqlbuf->minute = dbtime->tm_min; - Sqlbuf->hour = dbtime->tm_hour; - Sqlbuf->day = dbtime->tm_mday; - Sqlbuf->month = dbtime->tm_mon + 1; - Sqlbuf->year = dbtime->tm_year + 1900; - Sqlbuf->fraction = 0; - } else if (Buf_Type == TYPE_DECIM) { - // Some data sources require local decimal separator - char *p, sep = ((PTDBJDBC)To_Tdb)->Sep; - - if (sep && (p = strchr(Value->GetCharValue(), '.'))) - *p = sep; - - } // endif Buf_Type - - if (Nullable) - *StrLen = (Value->IsNull()) ? SQL_NULL_DATA : - (IsTypeChar(Buf_Type)) ? SQL_NTS : 0; -#endif // 0 } // end of WriteColumn /* -------------------------- Class TDBXJDC -------------------------- */ @@ -1795,7 +1382,7 @@ TDBJTB::TDBJTB(PJDBCDEF tdp) : TDBJDRV(tdp) { Schema = tdp->Tabschema; Tab = tdp->Tabname; - Tabtype = tdp->Tabtype; + Tabtype = tdp->Tabtyp; Ops.Driver = tdp->Driver; Ops.Url = tdp->Url; Ops.User = tdp->Username; diff --git a/storage/connect/tabjdbc.h b/storage/connect/tabjdbc.h index fee8223abaf..46d2073e923 100644 --- a/storage/connect/tabjdbc.h +++ b/storage/connect/tabjdbc.h @@ -21,7 +21,7 @@ typedef class JSRCCOL *PJSRCCOL; /***********************************************************************/ /* JDBC table. */ /***********************************************************************/ -class DllExport JDBCDEF : public TABDEF { /* Logical table description */ +class DllExport JDBCDEF : public EXTDEF { /* Logical table description */ friend class TDBJDBC; friend class TDBXJDC; friend class TDBJDRV; @@ -33,17 +33,8 @@ public: // Implementation virtual const char *GetType(void) { return "JDBC"; } - PSZ GetTabname(void) { return Tabname; } - PSZ GetTabschema(void) { return Tabschema; } - PSZ GetTabcat(void) { return Tabcat; } - PSZ GetSrcdef(void) { return Srcdef; } - char GetSep(void) { return (Sep) ? *Sep : 0; } - int GetQuoted(void) { return Quoted; } -//int GetCatver(void) { return Catver; } - int GetOptions(void) { return Options; } // Methods - virtual int Indexable(void) { return 2; } virtual bool DefineAM(PGLOBAL g, LPCSTR am, int poff); virtual PTDB GetTable(PGLOBAL g, MODE m); int ParseURL(PGLOBAL g, char *url, bool b = true); @@ -53,28 +44,7 @@ protected: // Members PSZ Driver; /* JDBC driver */ PSZ Url; /* JDBC driver URL */ - PSZ Tabname; /* External table name */ PSZ Wrapname; /* Java wrapper name */ - PSZ Tabschema; /* External table schema */ - PSZ Username; /* User connect name */ - PSZ Password; /* Password connect info */ -//PSZ Prop; /* Connection Properties */ - PSZ Tabcat; /* External table catalog */ - PSZ Tabtype; /* External table type */ - PSZ Colpat; /* Catalog column pattern */ - PSZ Srcdef; /* The source table SQL definition */ - PSZ Qchar; /* Identifier quoting character */ - PSZ Qrystr; /* The original query */ - PSZ Sep; /* Decimal separator */ - int Options; /* Open connection options */ -//int Cto; /* Open connection timeout */ -//int Qto; /* Query (command) timeout */ - int Quoted; /* Identifier quoting level */ - int Maxerr; /* Maxerr for an Exec table */ - int Maxres; /* Maxres for a catalog table */ - int Memory; /* Put result set in memory */ - bool Scrollable; /* Use scrollable cursor */ - bool Xsrc; /* Execution type */ }; // end of JDBCDEF #if !defined(NJDBC) @@ -84,34 +54,34 @@ protected: /* This is the JDBC Access Method class declaration for files from */ /* other DB drivers to be accessed via JDBC. */ /***********************************************************************/ -class TDBJDBC : public TDBASE { +class TDBJDBC : public TDBEXT { friend class JDBCCOL; friend class JDBConn; public: // Constructor TDBJDBC(PJDBCDEF tdp = NULL); - TDBJDBC(PTDBJDBC tdbp); + TDBJDBC(PTDBJDBC tdbp); // Implementation - virtual AMT GetAmType(void) { return TYPE_AM_JDBC; } - virtual PTDB Duplicate(PGLOBAL g) { return (PTDB)new(g)TDBJDBC(this); } + virtual AMT GetAmType(void) {return TYPE_AM_JDBC;} + virtual PTDB Duplicate(PGLOBAL g) {return (PTDB)new(g) TDBJDBC(this);} // Methods - virtual PTDB CopyOne(PTABS t); - virtual int GetRecpos(void); + virtual PTDB Clone(PTABS t); +//virtual int GetRecpos(void); virtual bool SetRecpos(PGLOBAL g, int recpos); //virtual PSZ GetFile(PGLOBAL g); //virtual void SetFile(PGLOBAL g, PSZ fn); virtual void ResetSize(void); - //virtual int GetAffectedRows(void) {return AftRows;} +//virtual int GetAffectedRows(void) {return AftRows;} virtual PSZ GetServer(void) { return "JDBC"; } virtual int Indexable(void) { return 2; } // Database routines virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n); virtual int Cardinality(PGLOBAL g); - virtual int GetMaxSize(PGLOBAL g); - virtual int GetProgMax(PGLOBAL g); +//virtual int GetMaxSize(PGLOBAL g); +//virtual int GetProgMax(PGLOBAL g); virtual bool OpenDB(PGLOBAL g); virtual int ReadDB(PGLOBAL g); virtual int WriteDB(PGLOBAL g); @@ -121,97 +91,50 @@ public: protected: // Internal functions - int Decode(char *utf, char *buf, size_t n); - bool MakeSQL(PGLOBAL g, bool cnt); +//int Decode(char *utf, char *buf, size_t n); +//bool MakeSQL(PGLOBAL g, bool cnt); bool MakeInsert(PGLOBAL g); - bool MakeCommand(PGLOBAL g); - //bool MakeFilter(PGLOBAL g, bool c); +//virtual bool MakeCommand(PGLOBAL g); +//bool MakeFilter(PGLOBAL g, bool c); bool SetParameters(PGLOBAL g); - //char *MakeUpdate(PGLOBAL g); - //char *MakeDelete(PGLOBAL g); +//char *MakeUpdate(PGLOBAL g); +//char *MakeDelete(PGLOBAL g); // Members JDBConn *Jcp; // Points to a JDBC connection class JDBCCOL *Cnp; // Points to count(*) column JDBCPARM Ops; // Additional parameters - PSTRG Query; // Constructed SQL query char *WrapName; // Points to Java wrapper name - char *TableName; // Points to JDBC table name - char *Schema; // Points to JDBC table Schema - char *User; // User connect info - char *Pwd; // Password connect info - char *Catalog; // Points to JDBC table Catalog - char *Srcdef; // The source table SQL definition - char *Count; // Points to count(*) SQL statement -//char *Where; // Points to local where clause - char *Quote; // The identifier quoting character - char *MulConn; // Used for multiple JDBC tables - char *DBQ; // The address part of Connect string - char *Qrystr; // The original query - char Sep; // The decimal separator - int Options; // Connect options -//int Cto; // Connect timeout -//int Qto; // Query timeout - int Quoted; // The identifier quoting level - int Fpos; // Position of last read record - int Curpos; // Cursor position of last fetch - int AftRows; // The number of affected rows - int Rows; // Rowset size - int CurNum; // Current buffer line number - int Rbuf; // Number of lines read in buffer - int BufSize; // Size of connect string buffer - int Ncol; // The column number - int Nparm; // The number of statement parameters - int Memory; // 0: No 1: Alloc 2: Put 3: Get -//bool Scrollable; // Use scrollable cursor --> in Ops - bool Placed; // True for position reading +//int Ncol; // The column number bool Prepared; // True when using prepared statement bool Werr; // Write error bool Rerr; // Rewind error - PQRYRES Qrp; // Points to storage result }; // end of class TDBJDBC /***********************************************************************/ /* Class JDBCCOL: JDBC access method column descriptor. */ /* This A.M. is used for JDBC tables. */ /***********************************************************************/ -class JDBCCOL : public COLBLK { +class JDBCCOL : public EXTCOL { friend class TDBJDBC; public: // Constructors JDBCCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am = "JDBC"); - JDBCCOL(JDBCCOL *colp, PTDB tdbp); // Constructor used in copy process + JDBCCOL(JDBCCOL *colp, PTDB tdbp); // Constructor used in copy process // Implementation - virtual int GetAmType(void) { return TYPE_AM_JDBC; } -//SQLLEN *GetStrLen(void) { return StrLen; } - int GetRank(void) { return Rank; } -//PVBLK GetBlkp(void) {return Blkp;} - void SetCrp(PCOLRES crp) { Crp = crp; } + virtual int GetAmType(void) { return TYPE_AM_JDBC; } // Methods - virtual bool SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check); - virtual void ReadColumn(PGLOBAL g); - virtual void WriteColumn(PGLOBAL g); -//void AllocateBuffers(PGLOBAL g, int rows); -//void *GetBuffer(DWORD rows); -//SWORD GetBuflen(void); - // void Print(PGLOBAL g, FILE *, uint); +//virtual bool SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check); + virtual void ReadColumn(PGLOBAL g); + virtual void WriteColumn(PGLOBAL g); protected: - // Constructor used by GetMaxSize - JDBCCOL(void); + // Constructor for count(*) column + JDBCCOL(void); // Members - //TIMESTAMP_STRUCT *Sqlbuf; // To get SQL_TIMESTAMP's - PCOLRES Crp; // To storage result - void *Bufp; // To extended buffer - PVBLK Blkp; // To Value Block - //char F_Date[12]; // Internal Date format - PVAL To_Val; // To value used for Insert -//SQLLEN *StrLen; // As returned by JDBC -//SQLLEN Slen; // Used with Fetch - int Rank; // Rank (position) number in the query }; // end of class JDBCCOL /***********************************************************************/ @@ -268,7 +191,7 @@ public: JSRCCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am = "JDBC"); // Implementation - //virtual int GetAmType(void) {return TYPE_AM_JDBC;} + virtual int GetAmType(void) {return TYPE_AM_JDBC;} // Methods virtual void ReadColumn(PGLOBAL g); diff --git a/storage/connect/tabjson.cpp b/storage/connect/tabjson.cpp index 1b9ce8b64c9..1e11d454cfc 100644 --- a/storage/connect/tabjson.cpp +++ b/storage/connect/tabjson.cpp @@ -129,7 +129,7 @@ PQRYRES JSONColumns(PGLOBAL g, char *db, PTOS topt, bool info) if (tdp->Pretty == 2) { if (tdp->Zipped) { #if defined(ZIP_SUPPORT) - tjsp = new(g) TDBJSON(tdp, new(g) ZIPFAM(tdp)); + tjsp = new(g) TDBJSON(tdp, new(g) UNZFAM(tdp)); #else // !ZIP_SUPPORT sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "ZIP"); return NULL; @@ -151,7 +151,7 @@ PQRYRES JSONColumns(PGLOBAL g, char *db, PTOS topt, bool info) if (tdp->Zipped) { #if defined(ZIP_SUPPORT) - tjnp = new(g)TDBJSN(tdp, new(g)ZIPFAM(tdp)); + tjnp = new(g)TDBJSN(tdp, new(g)UNZFAM(tdp)); #else // !ZIP_SUPPORT sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "ZIP"); return NULL; @@ -441,7 +441,14 @@ PTDB JSONDEF::GetTable(PGLOBAL g, MODE m) if (Zipped) { #if defined(ZIP_SUPPORT) - txfp = new(g) ZIPFAM(this); + if (m == MODE_READ || m == MODE_UPDATE) { + txfp = new(g) UNZFAM(this); + } else if (m == MODE_INSERT) { + txfp = new(g) ZIPFAM(this); + } else { + strcpy(g->Message, "UPDATE/DELETE not supported for ZIP"); + return NULL; + } // endif's m #else // !ZIP_SUPPORT sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "ZIP"); return NULL; @@ -479,7 +486,15 @@ PTDB JSONDEF::GetTable(PGLOBAL g, MODE m) } else { if (Zipped) { #if defined(ZIP_SUPPORT) - txfp = new(g)ZIPFAM(this); + if (m == MODE_READ || m == MODE_UPDATE) { + txfp = new(g) UNZFAM(this); + } else if (m == MODE_INSERT) { + strcpy(g->Message, "INSERT supported only for zipped JSON when pretty=0"); + return NULL; + } else { + strcpy(g->Message, "UPDATE/DELETE not supported for ZIP"); + return NULL; + } // endif's m #else // !ZIP_SUPPORT sprintf(g->Message, MSG(NO_FEAT_SUPPORT), "ZIP"); return NULL; @@ -559,7 +574,7 @@ TDBJSN::TDBJSN(TDBJSN *tdbp) : TDBDOS(NULL, tdbp) } // end of TDBJSN copy constructor // Used for update -PTDB TDBJSN::CopyOne(PTABS t) +PTDB TDBJSN::Clone(PTABS t) { G = NULL; PTDB tp; @@ -574,7 +589,7 @@ PTDB TDBJSN::CopyOne(PTABS t) } // endfor cp1 return tp; - } // end of CopyOne + } // end of Clone /***********************************************************************/ /* Allocate JSN column description block. */ @@ -1563,7 +1578,7 @@ TDBJSON::TDBJSON(PJTDB tdbp) : TDBJSN(tdbp) } // end of TDBJSON copy constructor // Used for update -PTDB TDBJSON::CopyOne(PTABS t) +PTDB TDBJSON::Clone(PTABS t) { PTDB tp; PJCOL cp1, cp2; @@ -1577,7 +1592,7 @@ PTDB TDBJSON::CopyOne(PTABS t) } // endfor cp1 return tp; - } // end of CopyOne + } // end of Clone /***********************************************************************/ /* Make the document tree from the object path. */ diff --git a/storage/connect/tabjson.h b/storage/connect/tabjson.h index c9d30d48f2a..924ce387900 100644 --- a/storage/connect/tabjson.h +++ b/storage/connect/tabjson.h @@ -82,7 +82,7 @@ public: void SetG(PGLOBAL g) {G = g;} // Methods - virtual PTDB CopyOne(PTABS t); + virtual PTDB Clone(PTABS t); virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n); virtual PCOL InsertSpecialColumn(PCOL colp); virtual int RowNumber(PGLOBAL g, bool b = FALSE) @@ -188,7 +188,7 @@ class TDBJSON : public TDBJSN { PJAR GetDoc(void) {return Doc;} // Methods - virtual PTDB CopyOne(PTABS t); + virtual PTDB Clone(PTABS t); // Database routines virtual int Cardinality(PGLOBAL g); diff --git a/storage/connect/table.cpp b/storage/connect/table.cpp index c21bb1660ea..916449be6c6 100644 --- a/storage/connect/table.cpp +++ b/storage/connect/table.cpp @@ -1,7 +1,7 @@ /************** Table C++ Functions Source Code File (.CPP) ************/ -/* Name: TABLE.CPP Version 2.7 */ +/* Name: TABLE.CPP Version 2.8 */ /* */ -/* (C) Copyright to the author Olivier BERTRAND 1999-2016 */ +/* (C) Copyright to the author Olivier BERTRAND 1999-2017 */ /* */ /* This file contains the TBX, TDB and OPJOIN classes functions. */ /***********************************************************************/ @@ -10,6 +10,7 @@ /* Include relevant MariaDB header file. */ /***********************************************************************/ #include "my_global.h" +#include "sql_string.h" /***********************************************************************/ /* Include required application header files */ @@ -40,8 +41,9 @@ void AddPointer(PTABS, void *); /* TDB public constructors. */ /***********************************************************************/ TDB::TDB(PTABDEF tdp) : Tdb_No(++Tnum) - { - Use = USE_NO; +{ + To_Def = tdp; + Use = USE_NO; To_Orig = NULL; To_Filter = NULL; To_CondFil = NULL; @@ -49,14 +51,20 @@ TDB::TDB(PTABDEF tdp) : Tdb_No(++Tnum) Name = (tdp) ? tdp->GetName() : NULL; To_Table = NULL; Columns = NULL; - Degree = (tdp) ? tdp->GetDegree() : 0; + To_SetCols = NULL; + Degree = (tdp) ? tdp->GetDegree() : 0; Mode = MODE_ANY; Cardinal = -1; - } // end of TDB standard constructor + MaxSize = -1; + Read_Only = (tdp) ? tdp->IsReadOnly() : false; + m_data_charset = (tdp) ? tdp->data_charset() : NULL; + csname = (tdp) ? tdp->csname : NULL; +} // end of TDB standard constructor TDB::TDB(PTDB tdbp) : Tdb_No(++Tnum) - { - Use = tdbp->Use; +{ + To_Def = tdbp->To_Def; + Use = tdbp->Use; To_Orig = tdbp; To_Filter = NULL; To_CondFil = NULL; @@ -64,12 +72,192 @@ TDB::TDB(PTDB tdbp) : Tdb_No(++Tnum) Name = tdbp->Name; To_Table = tdbp->To_Table; Columns = NULL; - Degree = tdbp->Degree; + To_SetCols = tdbp->To_SetCols; // ??? + Degree = tdbp->Degree; Mode = tdbp->Mode; Cardinal = tdbp->Cardinal; - } // end of TDB copy constructor + MaxSize = tdbp->MaxSize; + Read_Only = tdbp->IsReadOnly(); + m_data_charset = tdbp->data_charset(); + csname = tdbp->csname; +} // end of TDB copy constructor // Methods +/***********************************************************************/ +/* Return the pointer on the charset of this table. */ +/***********************************************************************/ +CHARSET_INFO *TDB::data_charset(void) +{ + // If no DATA_CHARSET is specified, we assume that character + // set of the remote data is the same with CHARACTER SET + // definition of the SQL column. + return m_data_charset ? m_data_charset : &my_charset_bin; +} // end of data_charset + +/***********************************************************************/ +/* Return the datapath of the DB this table belongs to. */ +/***********************************************************************/ +PSZ TDB::GetPath(void) +{ + return To_Def->GetPath(); +} // end of GetPath + +/***********************************************************************/ +/* Return true if name is a special column of this table. */ +/***********************************************************************/ +bool TDB::IsSpecial(PSZ name) +{ + for (PCOLDEF cdp = To_Def->GetCols(); cdp; cdp = cdp->GetNext()) + if (!stricmp(cdp->GetName(), name) && (cdp->Flags & U_SPECIAL)) + return true; // Special column to ignore while inserting + + return false; // Not found or not special or not inserting +} // end of IsSpecial + +/***********************************************************************/ +/* Initialize TDB based column description block construction. */ +/* name is used to call columns by name. */ +/* num is used by TBL to construct columns by index number. */ +/* Note: name=Null and num=0 for constructing all columns (select *) */ +/***********************************************************************/ +PCOL TDB::ColDB(PGLOBAL g, PSZ name, int num) +{ + int i; + PCOLDEF cdp; + PCOL cp, colp = NULL, cprec = NULL; + + if (trace) + htrc("ColDB: am=%d colname=%s tabname=%s num=%d\n", + GetAmType(), SVP(name), Name, num); + + for (cdp = To_Def->GetCols(), i = 1; cdp; cdp = cdp->GetNext(), i++) + if ((!name && !num) || + (name && !stricmp(cdp->GetName(), name)) || num == i) { + /*****************************************************************/ + /* Check for existence of desired column. */ + /* Also find where to insert the new block. */ + /*****************************************************************/ + for (cp = Columns; cp; cp = cp->GetNext()) + if ((num && cp->GetIndex() == i) || + (name && !stricmp(cp->GetName(), name))) + break; // Found + else if (cp->GetIndex() < i) + cprec = cp; + + if (trace) + htrc("cdp(%d).Name=%s cp=%p\n", i, cdp->GetName(), cp); + + /*****************************************************************/ + /* Now take care of Column Description Block. */ + /*****************************************************************/ + if (cp) + colp = cp; + else if (!(cdp->Flags & U_SPECIAL)) + colp = MakeCol(g, cdp, cprec, i); + else if (Mode != MODE_INSERT) + colp = InsertSpcBlk(g, cdp); + + if (trace) + htrc("colp=%p\n", colp); + + if (name || num) + break; + else if (colp && !colp->IsSpecial()) + cprec = colp; + + } // endif Name + + return (colp); +} // end of ColDB + +/***********************************************************************/ +/* InsertSpecialColumn: Put a special column ahead of the column list.*/ +/***********************************************************************/ +PCOL TDB::InsertSpecialColumn(PCOL colp) +{ + if (!colp->IsSpecial()) + return NULL; + + colp->SetNext(Columns); + Columns = colp; + return colp; +} // end of InsertSpecialColumn + +/***********************************************************************/ +/* Make a special COLBLK to insert in a table. */ +/***********************************************************************/ +PCOL TDB::InsertSpcBlk(PGLOBAL g, PCOLDEF cdp) +{ + //char *name = cdp->GetName(); + char *name = cdp->GetFmt(); + PCOLUMN cp; + PCOL colp; + + cp = new(g)COLUMN(cdp->GetName()); + + if (!To_Table) { + strcpy(g->Message, "Cannot make special column: To_Table is NULL"); + return NULL; + } else + cp->SetTo_Table(To_Table); + + if (!stricmp(name, "FILEID") || !stricmp(name, "FDISK") || + !stricmp(name, "FPATH") || !stricmp(name, "FNAME") || + !stricmp(name, "FTYPE") || !stricmp(name, "SERVID")) { + if (!To_Def || !(To_Def->GetPseudo() & 2)) { + sprintf(g->Message, MSG(BAD_SPEC_COLUMN)); + return NULL; + } // endif Pseudo + + if (!stricmp(name, "FILEID")) + colp = new(g)FIDBLK(cp, OP_XX); + else if (!stricmp(name, "FDISK")) + colp = new(g)FIDBLK(cp, OP_FDISK); + else if (!stricmp(name, "FPATH")) + colp = new(g)FIDBLK(cp, OP_FPATH); + else if (!stricmp(name, "FNAME")) + colp = new(g)FIDBLK(cp, OP_FNAME); + else if (!stricmp(name, "FTYPE")) + colp = new(g)FIDBLK(cp, OP_FTYPE); + else + colp = new(g)SIDBLK(cp); + + } else if (!stricmp(name, "TABID")) { + colp = new(g)TIDBLK(cp); + } else if (!stricmp(name, "PARTID")) { + colp = new(g)PRTBLK(cp); + //} else if (!stricmp(name, "CONID")) { + // colp = new(g) CIDBLK(cp); + } else if (!stricmp(name, "ROWID")) { + colp = new(g)RIDBLK(cp, false); + } else if (!stricmp(name, "ROWNUM")) { + colp = new(g)RIDBLK(cp, true); + } else { + sprintf(g->Message, MSG(BAD_SPECIAL_COL), name); + return NULL; + } // endif's name + + if (!(colp = InsertSpecialColumn(colp))) { + sprintf(g->Message, MSG(BAD_SPECIAL_COL), name); + return NULL; + } // endif Insert + + return (colp); +} // end of InsertSpcBlk + +/***********************************************************************/ +/* Marks DOS/MAP table columns used in internal joins. */ +/* tdb2 is the top of tree or first tdb in chained tdb's and tdbp */ +/* points to the currently marked tdb. */ +/* Two questions here: exact meaning of U_J_INT ? */ +/* Why is the eventual reference to To_Key_Col not marked U_J_EXT ? */ +/***********************************************************************/ +void TDB::MarkDB(PGLOBAL, PTDB tdb2) +{ + if (trace) + htrc("DOS MarkDB: tdbp=%p tdb2=%p\n", this, tdb2); + +} // end of MarkDB /***********************************************************************/ /* RowNumber: returns the current row ordinal number. */ @@ -86,7 +274,7 @@ PTDB TDB::Copy(PTABS t) //PGLOBAL g = t->G; // Is this really useful ??? for (tdb1 = this; tdb1; tdb1 = tdb1->Next) { - tp = tdb1->CopyOne(t); + tp = tdb1->Clone(t); if (!outp) outp = tp; @@ -100,6 +288,15 @@ PTDB TDB::Copy(PTABS t) return outp; } // end of Copy +/***********************************************************************/ +/* SetRecpos: Replace the table at the specified position. */ +/***********************************************************************/ +bool TDB::SetRecpos(PGLOBAL g, int) +{ + strcpy(g->Message, MSG(SETRECPOS_NIY)); + return true; +} // end of SetRecpos + void TDB::Print(PGLOBAL g, FILE *f, uint n) { PCOL cp; @@ -135,34 +332,34 @@ void TDB::Print(PGLOBAL, char *ps, uint) /***********************************************************************/ TDBASE::TDBASE(PTABDEF tdp) : TDB(tdp) { - To_Def = tdp; +//To_Def = tdp; To_Link = NULL; To_Key_Col = NULL; To_Kindex = NULL; To_Xdp = NULL; - To_SetCols = NULL; +//To_SetCols = NULL; Ftype = RECFM_NAF; - MaxSize = -1; +//MaxSize = -1; Knum = 0; - Read_Only = (tdp) ? tdp->IsReadOnly() : false; - m_data_charset= (tdp) ? tdp->data_charset() : NULL; - csname = (tdp) ? tdp->csname : NULL; +//Read_Only = (tdp) ? tdp->IsReadOnly() : false; +//m_data_charset= (tdp) ? tdp->data_charset() : NULL; +//csname = (tdp) ? tdp->csname : NULL; } // end of TDBASE constructor TDBASE::TDBASE(PTDBASE tdbp) : TDB(tdbp) { - To_Def = tdbp->To_Def; +//To_Def = tdbp->To_Def; To_Link = tdbp->To_Link; To_Key_Col = tdbp->To_Key_Col; To_Kindex = tdbp->To_Kindex; To_Xdp = tdbp->To_Xdp; - To_SetCols = tdbp->To_SetCols; // ??? +//To_SetCols = tdbp->To_SetCols; // ??? Ftype = tdbp->Ftype; - MaxSize = tdbp->MaxSize; +//MaxSize = tdbp->MaxSize; Knum = tdbp->Knum; - Read_Only = tdbp->Read_Only; - m_data_charset= tdbp->m_data_charset; - csname = tdbp->csname; +//Read_Only = tdbp->Read_Only; +//m_data_charset= tdbp->m_data_charset; +//csname = tdbp->csname; } // end of TDBASE copy constructor /***********************************************************************/ @@ -173,6 +370,7 @@ PCATLG TDBASE::GetCat(void) return (To_Def) ? To_Def->GetCat() : NULL; } // end of GetCat +#if 0 /***********************************************************************/ /* Return the pointer on the charset of this table. */ /***********************************************************************/ @@ -334,6 +532,7 @@ PCOL TDBASE::InsertSpcBlk(PGLOBAL g, PCOLDEF cdp) return (colp); } // end of InsertSpcBlk +#endif // 0 /***********************************************************************/ /* ResetTableOpt: Wrong for this table type. */ @@ -362,6 +561,7 @@ void TDBASE::ResetKindex(PGLOBAL g, PKXBASE kxp) To_Kindex = kxp; } // end of ResetKindex +#if 0 /***********************************************************************/ /* SetRecpos: Replace the table at the specified position. */ /***********************************************************************/ @@ -370,6 +570,7 @@ bool TDBASE::SetRecpos(PGLOBAL g, int) strcpy(g->Message, MSG(SETRECPOS_NIY)); return true; } // end of SetRecpos +#endif // 0 /***********************************************************************/ /* Methods */ @@ -379,6 +580,7 @@ void TDBASE::PrintAM(FILE *f, char *m) fprintf(f, "%s AM(%d): mode=%d\n", m, GetAmType(), Mode); } // end of PrintAM +#if 0 /***********************************************************************/ /* Marks DOS/MAP table columns used in internal joins. */ /* tdb2 is the top of tree or first tdb in chained tdb's and tdbp */ @@ -392,6 +594,7 @@ void TDBASE::MarkDB(PGLOBAL, PTDB tdb2) htrc("DOS MarkDB: tdbp=%p tdb2=%p\n", this, tdb2); } // end of MarkDB +#endif // 0 /* ---------------------------TDBCAT class --------------------------- */ diff --git a/storage/connect/tabmac.cpp b/storage/connect/tabmac.cpp index e6e2abb54e2..bbaba591540 100644 --- a/storage/connect/tabmac.cpp +++ b/storage/connect/tabmac.cpp @@ -12,7 +12,7 @@ #include "global.h" #include "plgdbsem.h" //#include "catalog.h" -#include "reldef.h" +//#include "reldef.h" #include "xtable.h" #include "colblk.h" #include "tabmac.h" diff --git a/storage/connect/tabmac.h b/storage/connect/tabmac.h index f9a66e82eaa..47565bb2541 100644 --- a/storage/connect/tabmac.h +++ b/storage/connect/tabmac.h @@ -52,7 +52,7 @@ class TDBMAC : public TDBASE { //virtual PTDB Duplicate(PGLOBAL g) {return (PTDB)new(g) TDBMAC(g, this);} // Methods -//virtual PTDB CopyOne(PTABS t); +//virtual PTDB Clone(PTABS t); virtual int GetRecpos(void) {return N;} virtual int RowNumber(PGLOBAL g, bool b = false) {return N;} diff --git a/storage/connect/tabmul.cpp b/storage/connect/tabmul.cpp index ea287558b44..78adde81d12 100644 --- a/storage/connect/tabmul.cpp +++ b/storage/connect/tabmul.cpp @@ -1,11 +1,11 @@ /************* TabMul C++ Program Source Code File (.CPP) **************/ /* PROGRAM NAME: TABMUL */ /* ------------- */ -/* Version 1.7 */ +/* Version 1.8 */ /* */ /* COPYRIGHT: */ /* ---------- */ -/* (C) Copyright to PlugDB Software Development 2003 - 2015 */ +/* (C) Copyright to PlugDB Software Development 2003 - 2017 */ /* Author: Olivier BERTRAND */ /* */ /* WHAT THIS PROGRAM DOES: */ @@ -73,7 +73,7 @@ /***********************************************************************/ /* TABMUL constructors. */ /***********************************************************************/ -TDBMUL::TDBMUL(PTDBASE tdbp) : TDBASE(tdbp->GetDef()) +TDBMUL::TDBMUL(PTDB tdbp) : TDBASE(tdbp->GetDef()) { Tdbp = tdbp; Filenames = NULL; @@ -94,22 +94,22 @@ TDBMUL::TDBMUL(PTDBMUL tdbp) : TDBASE(tdbp) } // end of TDBMUL copy constructor // Method -PTDB TDBMUL::CopyOne(PTABS t) +PTDB TDBMUL::Clone(PTABS t) { PTDBMUL tp; PGLOBAL g = t->G; // Is this really useful ??? tp = new(g) TDBMUL(this); - tp->Tdbp = (PTDBASE)Tdbp->CopyOne(t); + tp->Tdbp = Tdbp->Clone(t); tp->Columns = tp->Tdbp->GetColumns(); return tp; - } // end of CopyOne + } // end of Clone PTDB TDBMUL::Duplicate(PGLOBAL g) { PTDBMUL tmup = new(g) TDBMUL(this); - tmup->Tdbp = (PTDBASE)Tdbp->Duplicate(g); + tmup->Tdbp = Tdbp->Duplicate(g); return tmup; } // end of Duplicate @@ -658,7 +658,7 @@ TDBDIR::TDBDIR(PTDBDIR tdbp) : TDBASE(tdbp) } // end of TDBDIR copy constructor // Method -PTDB TDBDIR::CopyOne(PTABS t) +PTDB TDBDIR::Clone(PTABS t) { PTDB tp; PGLOBAL g = t->G; // Is this really useful ??? @@ -666,7 +666,7 @@ PTDB TDBDIR::CopyOne(PTABS t) tp = new(g) TDBDIR(this); tp->SetColumns(Columns); return tp; - } // end of CopyOne + } // end of Clone /***********************************************************************/ /* Initialize/get the components of the search file pattern. */ @@ -974,7 +974,7 @@ TDBSDR::TDBSDR(PTDBSDR tdbp) : TDBDIR(tdbp) } // end of TDBSDR copy constructor // Method -PTDB TDBSDR::CopyOne(PTABS t) +PTDB TDBSDR::Clone(PTABS t) { PTDB tp; PGLOBAL g = t->G; // Is this really useful ??? @@ -982,7 +982,7 @@ PTDB TDBSDR::CopyOne(PTABS t) tp = new(g) TDBSDR(this); tp->SetColumns(Columns); return tp; - } // end of CopyOne + } // end of Clone /***********************************************************************/ /* SDR GetMaxSize: returns the number of retrieved files. */ @@ -1251,7 +1251,7 @@ TDBDHR::TDBDHR(PTDBDHR tdbp) : TDBASE(tdbp) } // end of TDBDHR copy constructor // Method -PTDB TDBDHR::CopyOne(PTABS t) +PTDB TDBDHR::Clone(PTABS t) { PTDB tp; PGLOBAL g = t->G; // Is this really useful ??? @@ -1259,7 +1259,7 @@ PTDB TDBDHR::CopyOne(PTABS t) tp = new(g) TDBDHR(this); tp->Columns = Columns; return tp; - } // end of CopyOne + } // end of Clone /***********************************************************************/ /* Allocate DHR column description block. */ diff --git a/storage/connect/tabmul.h b/storage/connect/tabmul.h index 433cc3a2ee3..51fa7f9000a 100644 --- a/storage/connect/tabmul.h +++ b/storage/connect/tabmul.h @@ -1,7 +1,7 @@ /*************** Tabmul H Declares Source Code File (.H) ***************/ -/* Name: TABMUL.H Version 1.4 */ +/* Name: TABMUL.H Version 1.5 */ /* */ -/* (C) Copyright to PlugDB Software Development 2003-2012 */ +/* (C) Copyright to PlugDB Software Development 2003-2017 */ /* Author: Olivier BERTRAND */ /* */ /* This file contains the TDBMUL and TDBDIR classes declares. */ @@ -28,7 +28,7 @@ class DllExport TDBMUL : public TDBASE { //friend class MULCOL; public: // Constructor - TDBMUL(PTDBASE tdbp); + TDBMUL(PTDB tdbp); TDBMUL(PTDBMUL tdbp); // Implementation @@ -37,7 +37,7 @@ class DllExport TDBMUL : public TDBASE { // Methods virtual void ResetDB(void); - virtual PTDB CopyOne(PTABS t); + virtual PTDB Clone(PTABS t); virtual bool IsSame(PTDB tp) {return tp == (PTDB)Tdbp;} virtual PSZ GetFile(PGLOBAL g) {return Tdbp->GetFile(g);} virtual int GetRecpos(void) {return 0;} @@ -61,7 +61,7 @@ class DllExport TDBMUL : public TDBASE { protected: // Members - TDBASE *Tdbp; // Points to a (file) table class + PTDB Tdbp; // Points to a (file) table class char* *Filenames; // Points to file names int Rows; // Total rows of already read files int Mul; // Type of multiple file list @@ -112,7 +112,7 @@ class TDBDIR : public TDBASE { {return (PTDB)new(g) TDBDIR(this);} // Methods - virtual PTDB CopyOne(PTABS t); + virtual PTDB Clone(PTABS t); virtual int GetRecpos(void) {return iFile;} // Database routines @@ -134,7 +134,7 @@ class TDBDIR : public TDBASE { int iFile; // Index of currently retrieved file #if defined(__WIN__) _finddata_t FileData; // Find data structure - int Hsearch; // Search handle + intptr_t Hsearch; // Search handle char Drive[_MAX_DRIVE]; // Drive name #else // !__WIN__ struct stat Fileinfo; // File info structure @@ -168,7 +168,7 @@ class TDBSDR : public TDBDIR { {return (PTDB)new(g) TDBSDR(this);} // Methods - virtual PTDB CopyOne(PTABS t); + virtual PTDB Clone(PTABS t); // Database routines virtual int GetMaxSize(PGLOBAL g); @@ -184,7 +184,7 @@ class TDBSDR : public TDBDIR { struct _Sub_Dir *Next; struct _Sub_Dir *Prev; #if defined(__WIN__) - int H; // Search handle + intptr_t H; // Search handle #else // !__WIN__ DIR *D; #endif // !__WIN__ diff --git a/storage/connect/tabmysql.cpp b/storage/connect/tabmysql.cpp index 98a476bf94f..1a715819fc8 100644 --- a/storage/connect/tabmysql.cpp +++ b/storage/connect/tabmysql.cpp @@ -1,11 +1,11 @@ /************* TabMySQL C++ Program Source Code File (.CPP) *************/ /* PROGRAM NAME: TABMYSQL */ /* ------------- */ -/* Version 1.9 */ +/* Version 2.0 */ /* */ /* AUTHOR: */ /* ------- */ -/* Olivier BERTRAND 2007-2015 */ +/* Olivier BERTRAND 2007-2017 */ /* */ /* WHAT THIS PROGRAM DOES: */ /* ----------------------- */ @@ -54,9 +54,10 @@ #include "global.h" #include "plgdbsem.h" #include "xtable.h" +#include "tabext.h" #include "tabcol.h" #include "colblk.h" -#include "reldef.h" +//#include "reldef.h" #include "tabmysql.h" #include "valblk.h" #include "tabutil.h" @@ -84,16 +85,16 @@ MYSQLDEF::MYSQLDEF(void) { Pseudo = 2; // SERVID is Ok but not ROWID Hostname = NULL; - Database = NULL; - Tabname = NULL; - Srcdef = NULL; - Username = NULL; - Password = NULL; +//Tabschema = NULL; +//Tabname = NULL; +//Srcdef = NULL; +//Username = NULL; +//Password = NULL; Portnumber = 0; Isview = false; Bind = false; Delayed = false; - Xsrc = false; +//Xsrc = false; Huge = false; } // end of MYSQLDEF constructor @@ -128,7 +129,7 @@ bool MYSQLDEF::GetServerInfo(PGLOBAL g, const char *server_name) // TODO: We need to examine which of these can really be NULL Hostname = PlugDup(g, server->host); - Database = PlugDup(g, server->db); + Tabschema = PlugDup(g, server->db); Username = PlugDup(g, server->username); Password = PlugDup(g, server->password); Portnumber = (server->port) ? server->port : GetDefaultPort(); @@ -200,7 +201,7 @@ bool MYSQLDEF::ParseURL(PGLOBAL g, char *url, bool b) Tabname = (b) ? GetStringCatInfo(g, "Tabname", Name) : NULL; if (trace) - htrc("server: %s Tabname: %s", url, Tabname); + htrc("server: %s TableName: %s", url, Tabname); Server = url; return GetServerInfo(g, url); @@ -253,10 +254,10 @@ bool MYSQLDEF::ParseURL(PGLOBAL g, char *url, bool b) return true; } // endif - if ((Database = strchr(Hostname, '/'))) { - *Database++ = 0; + if ((Tabschema = strchr(Hostname, '/'))) { + *Tabschema++ = 0; - if ((Tabname = strchr(Database, '/'))) { + if ((Tabname = strchr(Tabschema, '/'))) { *Tabname++ = 0; // Make sure there's not an extra / @@ -265,7 +266,7 @@ bool MYSQLDEF::ParseURL(PGLOBAL g, char *url, bool b) return true; } // endif / - } // endif Tabname + } // endif TableName } // endif database @@ -283,8 +284,8 @@ bool MYSQLDEF::ParseURL(PGLOBAL g, char *url, bool b) if (Hostname[0] == 0) Hostname = (b) ? GetStringCatInfo(g, "Host", "localhost") : NULL; - if (!Database || !*Database) - Database = (b) ? GetStringCatInfo(g, "Database", "*") : NULL; + if (!Tabschema || !*Tabschema) + Tabschema = (b) ? GetStringCatInfo(g, "Database", "*") : NULL; if (!Tabname || !*Tabname) Tabname = (b) ? GetStringCatInfo(g, "Tabname", Name) : NULL; @@ -320,7 +321,7 @@ bool MYSQLDEF::DefineAM(PGLOBAL g, LPCSTR am, int) if (!url || !*url) { // Not using the connection URL Hostname = GetStringCatInfo(g, "Host", "localhost"); - Database = GetStringCatInfo(g, "Database", "*"); + Tabschema = GetStringCatInfo(g, "Database", "*"); Tabname = GetStringCatInfo(g, "Name", Name); // Deprecated Tabname = GetStringCatInfo(g, "Tabname", Tabname); Username = GetStringCatInfo(g, "User", "*"); @@ -334,7 +335,7 @@ bool MYSQLDEF::DefineAM(PGLOBAL g, LPCSTR am, int) Delayed = !!GetIntCatInfo("Delayed", 0); } else { // MYSQL access from a PROXY table - Database = GetStringCatInfo(g, "Database", Schema ? Schema : PlugDup(g, "*")); + Tabschema = GetStringCatInfo(g, "Database", Tabschema ? Tabschema : PlugDup(g, "*")); Isview = GetBoolCatInfo("View", false); // We must get other connection parms from the calling table @@ -348,12 +349,12 @@ bool MYSQLDEF::DefineAM(PGLOBAL g, LPCSTR am, int) Portnumber = GetIntCatInfo("Port", GetDefaultPort()); Server = Hostname; } else { - char *locdb = Database; + char *locdb = Tabschema; if (ParseURL(g, url)) return true; - Database = locdb; + Tabschema = locdb; } // endif url Tabname = Name; @@ -362,7 +363,7 @@ bool MYSQLDEF::DefineAM(PGLOBAL g, LPCSTR am, int) if ((Srcdef = GetStringCatInfo(g, "Srcdef", NULL))) { Read_Only = true; Isview = true; - } else if (CheckSelf(g, Hc->GetTable()->s, Hostname, Database, + } else if (CheckSelf(g, Hc->GetTable()->s, Hostname, Tabschema, Tabname, Srcdef, Portnumber)) return true; @@ -372,7 +373,7 @@ bool MYSQLDEF::DefineAM(PGLOBAL g, LPCSTR am, int) // Specific for command executing tables Xsrc = GetBoolCatInfo("Execsrc", false); - Mxr = GetIntCatInfo("Maxerr", 0); + Maxerr = GetIntCatInfo("Maxerr", 0); Huge = GetBoolCatInfo("Huge", false); return false; } // end of DefineAM @@ -396,17 +397,17 @@ PTDB MYSQLDEF::GetTable(PGLOBAL g, MODE) /***********************************************************************/ /* Implementation of the TDBMYSQL class. */ /***********************************************************************/ -TDBMYSQL::TDBMYSQL(PMYDEF tdp) : TDBASE(tdp) +TDBMYSQL::TDBMYSQL(PMYDEF tdp) : TDBEXT(tdp) { if (tdp) { Host = tdp->Hostname; - Database = tdp->Database; - Tabname = tdp->Tabname; - Srcdef = tdp->Srcdef; - User = tdp->Username; - Pwd = tdp->Password; +// Schema = tdp->Tabschema; +// TableName = tdp->Tabname; +// Srcdef = tdp->Srcdef; +// User = tdp->Username; +// Pwd = tdp->Password; Server = tdp->Server; - Qrystr = tdp->Qrystr; +// Qrystr = tdp->Qrystr; Quoted = MY_MAX(0, tdp->Quoted); Port = tdp->Portnumber; Isview = tdp->Isview; @@ -415,14 +416,14 @@ TDBMYSQL::TDBMYSQL(PMYDEF tdp) : TDBASE(tdp) Myc.m_Use = tdp->Huge; } else { Host = NULL; - Database = NULL; - Tabname = NULL; - Srcdef = NULL; - User = NULL; - Pwd = NULL; +// Schema = NULL; +// TableName = NULL; +// Srcdef = NULL; +// User = NULL; +// Pwd = NULL; Server = NULL; - Qrystr = NULL; - Quoted = 0; +// Qrystr = NULL; +// Quoted = 0; Port = 0; Isview = false; Prep = false; @@ -430,39 +431,40 @@ TDBMYSQL::TDBMYSQL(PMYDEF tdp) : TDBASE(tdp) } // endif tdp Bind = NULL; - Query = NULL; +//Query = NULL; Fetched = false; m_Rc = RC_FX; - AftRows = 0; +//AftRows = 0; N = -1; - Nparm = 0; +//Nparm = 0; } // end of TDBMYSQL constructor -TDBMYSQL::TDBMYSQL(PTDBMY tdbp) : TDBASE(tdbp) +TDBMYSQL::TDBMYSQL(PTDBMY tdbp) : TDBEXT(tdbp) { Host = tdbp->Host; - Database = tdbp->Database; - Tabname = tdbp->Tabname; - Srcdef = tdbp->Srcdef; - User = tdbp->User; - Pwd = tdbp->Pwd; - Qrystr = tdbp->Qrystr; - Quoted = tdbp->Quoted; +//Schema = tdbp->Schema; +//TableName = tdbp->TableName; +//Srcdef = tdbp->Srcdef; +//User = tdbp->User; +//Pwd = tdbp->Pwd; +//Qrystr = tdbp->Qrystr; +//Quoted = tdbp->Quoted; + Server = tdbp->Server; Port = tdbp->Port; Isview = tdbp->Isview; Prep = tdbp->Prep; Delayed = tdbp->Delayed; Bind = NULL; - Query = tdbp->Query; +//Query = tdbp->Query; Fetched = tdbp->Fetched; m_Rc = tdbp->m_Rc; - AftRows = tdbp->AftRows; +//AftRows = tdbp->AftRows; N = tdbp->N; - Nparm = tdbp->Nparm; +//Nparm = tdbp->Nparm; } // end of TDBMYSQL copy constructor -// Is this really useful ??? -PTDB TDBMYSQL::CopyOne(PTABS t) +// Is this really useful ??? --> Yes for UPDATE +PTDB TDBMYSQL::Clone(PTABS t) { PTDB tp; PCOL cp1, cp2; @@ -477,7 +479,7 @@ PTDB TDBMYSQL::CopyOne(PTABS t) } // endfor cp1 return tp; - } // end of CopyOne + } // end of Clone /***********************************************************************/ /* Allocate MYSQL column description block. */ @@ -504,10 +506,18 @@ bool TDBMYSQL::MakeSelect(PGLOBAL g, bool mx) if (Query) return false; // already done - if (Srcdef) { - Query = new(g)STRING(g, 0, Srcdef); - return false; - } // endif Srcdef + if (Srcdef) { + if (strstr(Srcdef, "%s")) { + char *fil; + + fil = (To_CondFil) ? To_CondFil->Body : PlugDup(g, "1=1"); + Query = new(g)STRING(g, strlen(Srcdef) + strlen(fil)); + Query->SetLength(sprintf(Query->GetStr(), Srcdef, fil)); + } else + Query = new(g)STRING(g, 0, Srcdef); + + return false; + } // endif Srcdef // Allocate the string used to contain Query Query = new(g) STRING(g, 1023, "SELECT "); @@ -540,7 +550,7 @@ bool TDBMYSQL::MakeSelect(PGLOBAL g, bool mx) oom |= Query->Append(" FROM "); oom |= Query->Append(tk); - oom |= Query->Append(Tabname); + oom |= Query->Append(TableName); oom |= Query->Append(tk); len = Query->GetLength(); @@ -608,7 +618,7 @@ bool TDBMYSQL::MakeInsert(PGLOBAL g) } // endif colp // Below 40 is enough to contain the fixed part of the query - len += (strlen(Tabname) + 40); + len += (strlen(TableName) + 40); Query = new(g) STRING(g, len); if (Delayed) @@ -617,7 +627,7 @@ bool TDBMYSQL::MakeInsert(PGLOBAL g) oom = Query->Set("INSERT INTO "); oom |= Query->Append(tk); - oom |= Query->Append(Tabname); + oom |= Query->Append(TableName); oom |= Query->Append("` ("); for (colp = Columns; colp; colp = colp->GetNext()) { @@ -653,11 +663,11 @@ bool TDBMYSQL::MakeInsert(PGLOBAL g) /* MakeCommand: make the Update or Delete statement to send to the */ /* MySQL server. Limited to remote values and filtering. */ /***********************************************************************/ -int TDBMYSQL::MakeCommand(PGLOBAL g) +bool TDBMYSQL::MakeCommand(PGLOBAL g) { Query = new(g) STRING(g, strlen(Qrystr) + 64); - if (Quoted > 0 || stricmp(Name, Tabname)) { + if (Quoted > 0 || stricmp(Name, TableName)) { char *p, *qrystr, name[68]; bool qtd = Quoted > 0; @@ -678,29 +688,29 @@ int TDBMYSQL::MakeCommand(PGLOBAL g) if (qtd && *(p-1) == ' ') { oom |= Query->Append('`'); - oom |= Query->Append(Tabname); + oom |= Query->Append(TableName); oom |= Query->Append('`'); } else - oom |= Query->Append(Tabname); + oom |= Query->Append(TableName); oom |= Query->Append(Qrystr + (p - qrystr) + strlen(name)); if (oom) { strcpy(g->Message, "MakeCommand: Out of memory"); - return RC_FX; + return true; } else strlwr(strcpy(qrystr, Query->GetStr())); } else { sprintf(g->Message, "Cannot use this %s command", (Mode == MODE_UPDATE) ? "UPDATE" : "DELETE"); - return RC_FX; + return true; } // endif p } else (void)Query->Set(Qrystr); - return RC_OK; + return false; } // end of MakeCommand #if 0 @@ -727,7 +737,7 @@ int TDBMYSQL::MakeUpdate(PGLOBAL g) } // endif sscanf assert(!stricmp(cmd, "update")); - strcat(strcat(strcat(strcpy(Query, "UPDATE "), qc), Tabname), qc); + strcat(strcat(strcat(strcpy(Query, "UPDATE "), qc), TableName), qc); strcat(Query, end); return RC_OK; } // end of MakeUpdate @@ -754,7 +764,7 @@ int TDBMYSQL::MakeDelete(PGLOBAL g) } // endif sscanf assert(!stricmp(cmd, "delete") && !stricmp(from, "from")); - strcat(strcat(strcat(strcpy(Query, "DELETE FROM "), qc), Tabname), qc); + strcat(strcat(strcat(strcpy(Query, "DELETE FROM "), qc), TableName), qc); if (*end) strcat(Query, end); @@ -776,15 +786,15 @@ int TDBMYSQL::Cardinality(PGLOBAL g) char query[96]; MYSQLC myc; - if (myc.Open(g, Host, Database, User, Pwd, Port, csname)) + if (myc.Open(g, Host, Schema, User, Pwd, Port, csname)) return -1; strcpy(query, "SELECT COUNT(*) FROM "); if (Quoted > 0) - strcat(strcat(strcat(query, "`"), Tabname), "`"); + strcat(strcat(strcat(query, "`"), TableName), "`"); else - strcat(query, Tabname); + strcat(query, TableName); Cardinal = myc.GetTableSize(g, query); myc.Close(); @@ -794,6 +804,7 @@ int TDBMYSQL::Cardinality(PGLOBAL g) return Cardinal; } // end of Cardinality +#if 0 /***********************************************************************/ /* MYSQL GetMaxSize: returns the maximum number of rows in the table. */ /***********************************************************************/ @@ -812,6 +823,7 @@ int TDBMYSQL::GetMaxSize(PGLOBAL g) return MaxSize; } // end of GetMaxSize +#endif // 0 /***********************************************************************/ /* This a fake routine as ROWID does not exist in MySQL. */ @@ -872,7 +884,7 @@ bool TDBMYSQL::OpenDB(PGLOBAL g) /* servers allowing concurency in getting results ??? */ /*********************************************************************/ if (!Myc.Connected()) { - if (Myc.Open(g, Host, Database, User, Pwd, Port, csname)) + if (Myc.Open(g, Host, Schema, User, Pwd, Port, csname)) return true; } // endif Connected @@ -931,14 +943,14 @@ bool TDBMYSQL::OpenDB(PGLOBAL g) char cmd[64]; int w; - sprintf(cmd, "ALTER TABLE `%s` DISABLE KEYS", Tabname); + sprintf(cmd, "ALTER TABLE `%s` DISABLE KEYS", TableName); m_Rc = Myc.ExecSQL(g, cmd, &w); // may fail for some engines } // endif m_Rc } else // m_Rc = (Mode == MODE_DELETE) ? MakeDelete(g) : MakeUpdate(g); - m_Rc = MakeCommand(g); + m_Rc = (MakeCommand(g)) ? RC_FX : RC_OK; if (m_Rc == RC_FX) { Myc.Close(); @@ -1030,7 +1042,7 @@ int TDBMYSQL::SendCommand(PGLOBAL g) if (Myc.ExecSQLcmd(g, Query->GetStr(), &w) == RC_NF) { AftRows = Myc.m_Afrw; - sprintf(g->Message, "%s: %d affected rows", Tabname, AftRows); + sprintf(g->Message, "%s: %d affected rows", TableName, AftRows); PushWarning(g, this, 0); // 0 means a Note if (trace) @@ -1039,7 +1051,7 @@ int TDBMYSQL::SendCommand(PGLOBAL g) if (w && Myc.ExecSQL(g, "SHOW WARNINGS") == RC_OK) { // We got warnings from the remote server while (Myc.Fetch(g, -1) == RC_OK) { - sprintf(g->Message, "%s: (%s) %s", Tabname, + sprintf(g->Message, "%s: (%s) %s", TableName, Myc.GetCharField(1), Myc.GetCharField(2)); PushWarning(g, this); } // endwhile Fetch @@ -1116,8 +1128,7 @@ int TDBMYSQL::ReadDB(PGLOBAL g) int rc; if (trace > 1) - htrc("MySQL ReadDB: R%d Mode=%d key=%p link=%p Kindex=%p\n", - GetTdb_No(), Mode, To_Key_Col, To_Link, To_Kindex); + htrc("MySQL ReadDB: R%d Mode=%d\n", GetTdb_No(), Mode); if (Mode == MODE_UPDATE || Mode == MODE_DELETE) return SendCommand(g); @@ -1205,7 +1216,7 @@ void TDBMYSQL::CloseDB(PGLOBAL g) PDBUSER dup = PlgGetUser(g); dup->Step = "Enabling indexes"; - sprintf(cmd, "ALTER TABLE `%s` ENABLE KEYS", Tabname); + sprintf(cmd, "ALTER TABLE `%s` ENABLE KEYS", TableName); Myc.m_Rows = -1; // To execute the query m_Rc = Myc.ExecSQL(g, cmd, &w); // May fail for some engines } // endif m_Rc @@ -1463,7 +1474,7 @@ TDBMYEXC::TDBMYEXC(PMYDEF tdp) : TDBMYSQL(tdp) Havew = false; Isw = false; Warnings = 0; - Mxr = tdp->Mxr; + Mxr = tdp->Maxerr; Nerr = 0; } // end of TDBMYEXC constructor @@ -1479,7 +1490,7 @@ TDBMYEXC::TDBMYEXC(PTDBMYX tdbp) : TDBMYSQL(tdbp) } // end of TDBMYEXC copy constructor // Is this really useful ??? -PTDB TDBMYEXC::CopyOne(PTABS t) +PTDB TDBMYEXC::Clone(PTABS t) { PTDB tp; PCOL cp1, cp2; @@ -1494,7 +1505,7 @@ PTDB TDBMYEXC::CopyOne(PTABS t) } // endfor cp1 return tp; - } // end of CopyOne + } // end of Clone /***********************************************************************/ /* Allocate MYSQL column description block. */ @@ -1565,7 +1576,7 @@ bool TDBMYEXC::OpenDB(PGLOBAL g) /* servers allowing concurency in getting results ??? */ /*********************************************************************/ if (!Myc.Connected()) - if (Myc.Open(g, Host, Database, User, Pwd, Port)) + if (Myc.Open(g, Host, Schema, User, Pwd, Port)) return true; Use = USE_OPEN; // Do it now in case we are recursively called @@ -1728,7 +1739,7 @@ void MYXCOL::WriteColumn(PGLOBAL) TDBMCL::TDBMCL(PMYDEF tdp) : TDBCAT(tdp) { Host = tdp->Hostname; - Db = tdp->Database; + Db = tdp->Tabschema; Tab = tdp->Tabname; User = tdp->Username; Pwd = tdp->Password; diff --git a/storage/connect/tabmysql.h b/storage/connect/tabmysql.h index edb15b5cca6..050fa59259b 100644 --- a/storage/connect/tabmysql.h +++ b/storage/connect/tabmysql.h @@ -1,4 +1,4 @@ -// TDBMYSQL.H Olivier Bertrand 2007-2014 +// TDBMYSQL.H Olivier Bertrand 2007-2017 #include "myconn.h" // MySQL connection declares typedef class MYSQLDEF *PMYDEF; @@ -18,7 +18,7 @@ typedef class MYSQLC *PMYC; /***********************************************************************/ /* MYSQL table. */ /***********************************************************************/ -class MYSQLDEF : public TABDEF {/* Logical table description */ +class MYSQLDEF : public EXTDEF {/* Logical table description */ friend class TDBMYSQL; friend class TDBMYEXC; friend class TDBMCL; @@ -27,19 +27,18 @@ class MYSQLDEF : public TABDEF {/* Logical table description */ // Constructor MYSQLDEF(void); - // Implementation virtual const char *GetType(void) {return "MYSQL";} inline PSZ GetHostname(void) {return Hostname;}; - inline PSZ GetDatabase(void) {return Database;}; - inline PSZ GetTabname(void) {return Tabname;} - inline PSZ GetSrcdef(void) {return Srcdef;} - inline PSZ GetUsername(void) {return Username;}; - inline PSZ GetPassword(void) {return Password;}; +//inline PSZ GetDatabase(void) {return Tabschema;}; +//inline PSZ GetTabname(void) {return Tabname;} +//inline PSZ GetSrcdef(void) {return Srcdef;} +//inline PSZ GetUsername(void) {return Username;}; +//inline PSZ GetPassword(void) {return Password;}; inline int GetPortnumber(void) {return Portnumber;} // Methods - virtual int Indexable(void) {return 2;} +//virtual int Indexable(void) {return 2;} virtual bool DefineAM(PGLOBAL g, LPCSTR am, int poff); virtual PTDB GetTable(PGLOBAL g, MODE m); bool ParseURL(PGLOBAL g, char *url, bool b = true); @@ -48,27 +47,27 @@ class MYSQLDEF : public TABDEF {/* Logical table description */ protected: // Members PSZ Hostname; /* Host machine to use */ - PSZ Database; /* Database to be used by server */ - PSZ Tabname; /* External table name */ - PSZ Srcdef; /* The source table SQL definition */ - PSZ Username; /* User logon name */ - PSZ Password; /* Password logon info */ +//PSZ Tabschema; /* Database to be used by server */ +//PSZ Tabname; /* External table name */ +//PSZ Srcdef; /* The source table SQL definition */ +//PSZ Username; /* User logon name */ +//PSZ Password; /* Password logon info */ PSZ Server; /* PServerID */ - PSZ Qrystr; /* The original query */ +//PSZ Qrystr; /* The original query */ int Portnumber; /* MySQL port number (0 = default) */ - int Mxr; /* Maxerr for an Exec table */ - int Quoted; /* Identifier quoting level */ +//int Maxerr; /* Maxerr for an Exec table */ +//int Quoted; /* Identifier quoting level */ bool Isview; /* true if this table is a MySQL view */ bool Bind; /* Use prepared statement on insert */ bool Delayed; /* Delayed insert */ - bool Xsrc; /* Execution type */ +//bool Xsrc; /* Execution type */ bool Huge; /* True for big table */ }; // end of MYSQLDEF /***********************************************************************/ /* This is the class declaration for the MYSQL table. */ /***********************************************************************/ -class TDBMYSQL : public TDBASE { +class TDBMYSQL : public TDBEXT { friend class MYSQLCOL; public: // Constructor @@ -80,7 +79,7 @@ class TDBMYSQL : public TDBASE { virtual PTDB Duplicate(PGLOBAL g) {return (PTDB)new(g) TDBMYSQL(this);} // Methods - virtual PTDB CopyOne(PTABS t); + virtual PTDB Clone(PTABS t); //virtual int GetAffectedRows(void) {return AftRows;} virtual int GetRecpos(void) {return N;} virtual int GetProgMax(PGLOBAL g); @@ -88,12 +87,12 @@ class TDBMYSQL : public TDBASE { virtual int RowNumber(PGLOBAL g, bool b = false); virtual bool IsView(void) {return Isview;} virtual PSZ GetServer(void) {return Server;} - void SetDatabase(LPCSTR db) {Database = (char*)db;} + void SetDatabase(LPCSTR db) {Schema = (char*)db;} - // Database routines + // Schema routines virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n); virtual int Cardinality(PGLOBAL g); - virtual int GetMaxSize(PGLOBAL g); +//virtual int GetMaxSize(PGLOBAL g); virtual bool OpenDB(PGLOBAL g); virtual int ReadDB(PGLOBAL g); virtual int WriteDB(PGLOBAL g); @@ -111,7 +110,7 @@ class TDBMYSQL : public TDBASE { bool MakeSelect(PGLOBAL g, bool mx); bool MakeInsert(PGLOBAL g); int BindColumns(PGLOBAL g); - int MakeCommand(PGLOBAL g); + virtual bool MakeCommand(PGLOBAL g); //int MakeUpdate(PGLOBAL g); //int MakeDelete(PGLOBAL g); int SendCommand(PGLOBAL g); @@ -119,25 +118,25 @@ class TDBMYSQL : public TDBASE { // Members MYSQLC Myc; // MySQL connection class MYSQL_BIND *Bind; // To the MySQL bind structure array - PSTRG Query; // Constructed SQL query +//PSTRG Query; // Constructed SQL query char *Host; // Host machine to use - char *User; // User logon info - char *Pwd; // Password logon info - char *Database; // Database to be used by server - char *Tabname; // External table name - char *Srcdef; // The source table SQL definition +//char *User; // User logon info +//char *Pwd; // Password logon info +//char *Schema; // Database to be used by server +//char *TableName; // External table name +//char *Srcdef; // The source table SQL definition char *Server; // The server ID - char *Qrystr; // The original query +//char *Qrystr; // The original query bool Fetched; // True when fetch was done bool Isview; // True if this table is a MySQL view bool Prep; // Use prepared statement on insert bool Delayed; // Use delayed insert int m_Rc; // Return code from command - int AftRows; // The number of affected rows +//int AftRows; // The number of affected rows int N; // The current table index int Port; // MySQL port number (0 = default) - int Nparm; // The number of statement parameters - int Quoted; // The identifier quoting level +//int Nparm; // The number of statement parameters +//int Quoted; // The identifier quoting level }; // end of class TDBMYSQL /***********************************************************************/ @@ -162,9 +161,6 @@ class MYSQLCOL : public COLBLK { bool FindRank(PGLOBAL g); protected: - // Default constructor not to be used - MYSQLCOL(void) {} - // Members MYSQL_BIND *Bind; // This column bind structure pointer PVAL To_Val; // To value used for Update/Insert @@ -187,7 +183,7 @@ class TDBMYEXC : public TDBMYSQL { virtual PTDB Duplicate(PGLOBAL g) {return (PTDB)new(g) TDBMYEXC(this);} // Methods - virtual PTDB CopyOne(PTABS t); + virtual PTDB Clone(PTABS t); virtual bool IsView(void) {return Isview;} // Database routines @@ -228,9 +224,6 @@ class MYXCOL : public MYSQLCOL { virtual void WriteColumn(PGLOBAL g); protected: - // Default constructor not to be used - MYXCOL(void) {} - // Members char *Buffer; // To get returned message int Flag; // Column content desc diff --git a/storage/connect/taboccur.cpp b/storage/connect/taboccur.cpp index 07e260154e0..07272d1b298 100644 --- a/storage/connect/taboccur.cpp +++ b/storage/connect/taboccur.cpp @@ -1,7 +1,7 @@ /************ TabOccur CPP Declares Source Code File (.CPP) ************/ -/* Name: TABOCCUR.CPP Version 1.1 */ +/* Name: TABOCCUR.CPP Version 1.2 */ /* */ -/* (C) Copyright to the author Olivier BERTRAND 2013 - 2015 */ +/* (C) Copyright to the author Olivier BERTRAND 2013 - 2017 */ /* */ /* OCCUR: Table that provides a view of a source table where the */ /* contain of several columns of the source table is placed in only */ @@ -39,12 +39,13 @@ /***********************************************************************/ #include "global.h" #include "plgdbsem.h" -#include "reldef.h" +#include "xtable.h" +#include "tabext.h" +//#include "reldef.h" #include "filamtxt.h" #include "tabdos.h" #include "tabcol.h" #include "taboccur.h" -#include "xtable.h" #include "tabmysql.h" #include "ha_connect.h" diff --git a/storage/connect/tabodbc.cpp b/storage/connect/tabodbc.cpp index f3ffc99ac15..488acdd330d 100644 --- a/storage/connect/tabodbc.cpp +++ b/storage/connect/tabodbc.cpp @@ -67,10 +67,11 @@ #include "plgdbsem.h" #include "mycat.h" #include "xtable.h" +#include "tabext.h" #include "odbccat.h" #include "tabodbc.h" #include "tabmul.h" -#include "reldef.h" +//#include "reldef.h" #include "tabcol.h" #include "valblk.h" #include "ha_connect.h" @@ -95,10 +96,9 @@ bool ExactInfo(void); /***********************************************************************/ ODBCDEF::ODBCDEF(void) { - Connect = Tabname = Tabschema = Username = Password = NULL; - Tabcat = Colpat = Srcdef = Qchar = Qrystr = Sep = NULL; - Catver = Options = Cto = Qto = Quoted = Maxerr = Maxres = Memory = 0; - Scrollable = Xsrc = UseCnc = false; + Connect = NULL; + Catver = 0; + UseCnc = false; } // end of ODBCDEF constructor /***********************************************************************/ @@ -113,47 +113,50 @@ bool ODBCDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff) return true; } // endif Connect - Tabname = GetStringCatInfo(g, "Name", - (Catfunc & (FNC_TABLE | FNC_COL)) ? NULL : Name); - Tabname = GetStringCatInfo(g, "Tabname", Tabname); - Tabschema = GetStringCatInfo(g, "Dbname", NULL); - Tabschema = GetStringCatInfo(g, "Schema", Tabschema); - Tabcat = GetStringCatInfo(g, "Qualifier", NULL); - Tabcat = GetStringCatInfo(g, "Catalog", Tabcat); - Username = GetStringCatInfo(g, "User", NULL); - Password = GetStringCatInfo(g, "Password", NULL); - - if ((Srcdef = GetStringCatInfo(g, "Srcdef", NULL))) - Read_Only = true; - - Qrystr = GetStringCatInfo(g, "Query_String", "?"); - Sep = GetStringCatInfo(g, "Separator", NULL); + if (EXTDEF::DefineAM(g, am, poff)) + return true; + + // Tabname = GetStringCatInfo(g, "Name", + // (Catfunc & (FNC_TABLE | FNC_COL)) ? NULL : Name); + // Tabname = GetStringCatInfo(g, "Tabname", Tabname); + // Tabschema = GetStringCatInfo(g, "Dbname", NULL); + // Tabschema = GetStringCatInfo(g, "Schema", Tabschema); + // Tabcat = GetStringCatInfo(g, "Qualifier", NULL); + // Tabcat = GetStringCatInfo(g, "Catalog", Tabcat); + //Username = GetStringCatInfo(g, "User", NULL); + // Password = GetStringCatInfo(g, "Password", NULL); + + // if ((Srcdef = GetStringCatInfo(g, "Srcdef", NULL))) + // Read_Only = true; + + // Qrystr = GetStringCatInfo(g, "Query_String", "?"); + // Sep = GetStringCatInfo(g, "Separator", NULL); Catver = GetIntCatInfo("Catver", 2); - Xsrc = GetBoolCatInfo("Execsrc", FALSE); - Maxerr = GetIntCatInfo("Maxerr", 0); - Maxres = GetIntCatInfo("Maxres", 0); - Quoted = GetIntCatInfo("Quoted", 0); + //Xsrc = GetBoolCatInfo("Execsrc", FALSE); + //Maxerr = GetIntCatInfo("Maxerr", 0); + //Maxres = GetIntCatInfo("Maxres", 0); + //Quoted = GetIntCatInfo("Quoted", 0); Options = ODBConn::noOdbcDialog; //Options = ODBConn::noOdbcDialog | ODBConn::useCursorLib; Cto= GetIntCatInfo("ConnectTimeout", DEFAULT_LOGIN_TIMEOUT); Qto= GetIntCatInfo("QueryTimeout", DEFAULT_QUERY_TIMEOUT); - if ((Scrollable = GetBoolCatInfo("Scrollable", false)) && !Elemt) - Elemt = 1; // Cannot merge SQLFetch and SQLExtendedFetch + //if ((Scrollable = GetBoolCatInfo("Scrollable", false)) && !Elemt) + // Elemt = 1; // Cannot merge SQLFetch and SQLExtendedFetch - if (Catfunc == FNC_COL) - Colpat = GetStringCatInfo(g, "Colpat", NULL); + //if (Catfunc == FNC_COL) + // Colpat = GetStringCatInfo(g, "Colpat", NULL); - if (Catfunc == FNC_TABLE) - Tabtyp = GetStringCatInfo(g, "Tabtype", NULL); + //if (Catfunc == FNC_TABLE) + // Tabtyp = GetStringCatInfo(g, "Tabtype", NULL); UseCnc = GetBoolCatInfo("UseDSN", false); // Memory was Boolean, it is now integer - if (!(Memory = GetIntCatInfo("Memory", 0))) - Memory = GetBoolCatInfo("Memory", false) ? 1 : 0; + //if (!(Memory = GetIntCatInfo("Memory", 0))) + // Memory = GetBoolCatInfo("Memory", false) ? 1 : 0; - Pseudo = 2; // FILID is Ok but not ROWID + //Pseudo = 2; // FILID is Ok but not ROWID return false; } // end of DefineAM @@ -162,7 +165,7 @@ bool ODBCDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff) /***********************************************************************/ PTDB ODBCDEF::GetTable(PGLOBAL g, MODE m) { - PTDBASE tdbp = NULL; + PTDB tdbp = NULL; /*********************************************************************/ /* Allocate a TDB of the proper type. */ @@ -200,103 +203,103 @@ PTDB ODBCDEF::GetTable(PGLOBAL g, MODE m) /***********************************************************************/ /* Implementation of the TDBODBC class. */ /***********************************************************************/ -TDBODBC::TDBODBC(PODEF tdp) : TDBASE(tdp) +TDBODBC::TDBODBC(PODEF tdp) : TDBEXT(tdp) { Ocp = NULL; Cnp = NULL; if (tdp) { Connect = tdp->Connect; - TableName = tdp->Tabname; - Schema = tdp->Tabschema; + //TableName = tdp->Tabname; + //Schema = tdp->Tabschema; Ops.User = tdp->Username; Ops.Pwd = tdp->Password; - Catalog = tdp->Tabcat; - Srcdef = tdp->Srcdef; - Qrystr = tdp->Qrystr; - Sep = tdp->GetSep(); - Options = tdp->Options; + //Catalog = tdp->Tabcat; + //Srcdef = tdp->Srcdef; + //Qrystr = tdp->Qrystr; + //Sep = tdp->GetSep(); + //Options = tdp->Options; Ops.Cto = tdp->Cto; Ops.Qto = tdp->Qto; - Quoted = MY_MAX(0, tdp->GetQuoted()); - Rows = tdp->GetElemt(); + //Quoted = MY_MAX(0, tdp->GetQuoted()); + //Rows = tdp->GetElemt(); Catver = tdp->Catver; - Memory = tdp->Memory; - Scrollable = tdp->Scrollable; + //Memory = tdp->Memory; + //Scrollable = tdp->Scrollable; Ops.UseCnc = tdp->UseCnc; } else { Connect = NULL; - TableName = NULL; - Schema = NULL; + //TableName = NULL; + //Schema = NULL; Ops.User = NULL; Ops.Pwd = NULL; - Catalog = NULL; - Srcdef = NULL; - Qrystr = NULL; - Sep = 0; - Options = 0; + //Catalog = NULL; + //Srcdef = NULL; + //Qrystr = NULL; + //Sep = 0; + //Options = 0; Ops.Cto = DEFAULT_LOGIN_TIMEOUT; Ops.Qto = DEFAULT_QUERY_TIMEOUT; - Quoted = 0; - Rows = 0; + //Quoted = 0; + //Rows = 0; Catver = 0; - Memory = 0; - Scrollable = false; + //Memory = 0; + //Scrollable = false; Ops.UseCnc = false; } // endif tdp - Quote = NULL; - Query = NULL; - Count = NULL; + //Quote = NULL; + //Query = NULL; + //Count = NULL; //Where = NULL; - MulConn = NULL; - DBQ = NULL; - Qrp = NULL; - Fpos = 0; - Curpos = 0; - AftRows = 0; - CurNum = 0; - Rbuf = 0; - BufSize = 0; - Nparm = 0; - Placed = false; + //MulConn = NULL; + //DBQ = NULL; + //Qrp = NULL; + //Fpos = 0; + //Curpos = 0; + //AftRows = 0; + //CurNum = 0; + //Rbuf = 0; + //BufSize = 0; + //Nparm = 0; + //Placed = false; } // end of TDBODBC standard constructor -TDBODBC::TDBODBC(PTDBODBC tdbp) : TDBASE(tdbp) +TDBODBC::TDBODBC(PTDBODBC tdbp) : TDBEXT(tdbp) { Ocp = tdbp->Ocp; // is that right ? Cnp = tdbp->Cnp; Connect = tdbp->Connect; - TableName = tdbp->TableName; - Schema = tdbp->Schema; + //TableName = tdbp->TableName; + //Schema = tdbp->Schema; Ops = tdbp->Ops; - Catalog = tdbp->Catalog; - Srcdef = tdbp->Srcdef; - Qrystr = tdbp->Qrystr; - Memory = tdbp->Memory; - Scrollable = tdbp->Scrollable; - Quote = tdbp->Quote; - Query = tdbp->Query; - Count = tdbp->Count; + //Catalog = tdbp->Catalog; + //Srcdef = tdbp->Srcdef; + //Qrystr = tdbp->Qrystr; + //Memory = tdbp->Memory; + //Scrollable = tdbp->Scrollable; + //Quote = tdbp->Quote; + //Query = tdbp->Query; + //Count = tdbp->Count; //Where = tdbp->Where; - MulConn = tdbp->MulConn; - DBQ = tdbp->DBQ; - Options = tdbp->Options; - Quoted = tdbp->Quoted; - Rows = tdbp->Rows; - Fpos = 0; - Curpos = 0; - AftRows = 0; - CurNum = 0; - Rbuf = 0; - BufSize = tdbp->BufSize; - Nparm = tdbp->Nparm; - Qrp = tdbp->Qrp; - Placed = false; + //MulConn = tdbp->MulConn; + //DBQ = tdbp->DBQ; + //Options = tdbp->Options; + //Quoted = tdbp->Quoted; + //Rows = tdbp->Rows; + //Fpos = 0; + //Curpos = 0; + //AftRows = 0; + //CurNum = 0; + //Rbuf = 0; + //BufSize = tdbp->BufSize; + //Nparm = tdbp->Nparm; + //Qrp = tdbp->Qrp; + //Placed = false; } // end of TDBODBC copy constructor // Method -PTDB TDBODBC::CopyOne(PTABS t) +PTDB TDBODBC::Clone(PTABS t) { PTDB tp; PODBCCOL cp1, cp2; @@ -386,6 +389,7 @@ void TDBODBC::SetFile(PGLOBAL g, PSZ fn) DBQ = fn; } // end of SetFile +#if 0 /******************************************************************/ /* Convert an UTF-8 string to latin characters. */ /******************************************************************/ @@ -414,7 +418,15 @@ bool TDBODBC::MakeSQL(PGLOBAL g, bool cnt) PCOL colp; if (Srcdef) { - Query = new(g)STRING(g, 0, Srcdef); + if (strstr(Srcdef, "%s")) { + char *fil; + + fil = (To_CondFil) ? To_CondFil->Body : PlugDup(g, "1=1"); + Query = new(g)STRING(g, strlen(Srcdef) + strlen(fil)); + Query->SetLength(sprintf(Query->GetStr(), Srcdef, fil)); + } else + Query = new(g)STRING(g, 0, Srcdef); + return false; } // endif Srcdef @@ -442,7 +454,8 @@ bool TDBODBC::MakeSQL(PGLOBAL g, bool cnt) } else oom |= Query->Append(buf); - } // endif colp + ((PEXTCOL)colp)->SetRank(++Ncol); + } // endif colp } else // !Columns can occur for queries such that sql count(*) from... @@ -458,10 +471,6 @@ bool TDBODBC::MakeSQL(PGLOBAL g, bool cnt) if (Catalog && *Catalog) catp = Catalog; - // Following lines are commented because of MSDEV-10520 - // Indeed the schema in the tablep is the local table database and - // is normally not related to the remote table database. - // TODO: Try to remember why this was done and if it was useful in some case. //if (tablep->GetSchema()) // schmp = (char*)tablep->GetSchema(); //else @@ -516,6 +525,7 @@ bool TDBODBC::MakeSQL(PGLOBAL g, bool cnt) return false; } // end of MakeSQL +#endif // 0 /***********************************************************************/ /* MakeInsert: make the Insert statement used with ODBC connection. */ @@ -536,7 +546,7 @@ bool TDBODBC::MakeInsert(PGLOBAL g) // Column name can be encoded in UTF-8 Decode(colp->GetName(), buf, sizeof(buf)); len += (strlen(buf) + 6); // comma + quotes + valist - ((PODBCCOL)colp)->Rank = ++Nparm; + ((PEXTCOL)colp)->SetRank(++Nparm); } // endif colp // Below 32 is enough to contain the fixed part of the query @@ -555,7 +565,7 @@ bool TDBODBC::MakeInsert(PGLOBAL g) if (schmp) len += strlen(schmp) + 1; - // Column name can be encoded in UTF-8 + // Table name can be encoded in UTF-8 Decode(TableName, buf, sizeof(buf)); len += (strlen(buf) + 32); Query = new(g) STRING(g, len, "INSERT INTO "); @@ -634,6 +644,7 @@ bool TDBODBC::BindParameters(PGLOBAL g) return false; } // end of BindParameters +#if 0 /***********************************************************************/ /* MakeCommand: make the Update or Delete statement to send to the */ /* MySQL server. Limited to remote values and filtering. */ @@ -664,19 +675,20 @@ bool TDBODBC::MakeCommand(PGLOBAL g) // If so, it must be quoted in the original query strlwr(strcat(strcat(strcpy(name, " "), Name), " ")); - if (!strstr(" update delete low_priority ignore quick from ", name)) - strlwr(strcpy(name, Name)); // Not a keyword - else - strlwr(strcat(strcat(strcpy(name, qc), Name), qc)); + if (strstr(" update delete low_priority ignore quick from ", name)) { + strlwr(strcat(strcat(strcpy(name, qc), Name), qc)); + k += 2; + } else + strlwr(strcpy(name, Name)); // Not a keyword if ((p = strstr(qrystr, name))) { for (i = 0; i < p - qrystr; i++) stmt[i] = (Qrystr[i] == '`') ? *qc : Qrystr[i]; stmt[i] = 0; - k = i + (int)strlen(Name); + k += i + (int)strlen(Name); - if (qtd && *(p-1) == ' ') + if (qtd && *(p - 1) == ' ') strcat(strcat(strcat(stmt, qc), TableName), qc); else strcat(stmt, TableName); @@ -692,15 +704,14 @@ bool TDBODBC::MakeCommand(PGLOBAL g) } else { sprintf(g->Message, "Cannot use this %s command", - (Mode == MODE_UPDATE) ? "UPDATE" : "DELETE"); - return false; + (Mode == MODE_UPDATE) ? "UPDATE" : "DELETE"); + return true; } // endif p Query = new(g) STRING(g, 0, stmt); return (!Query->GetSize()); } // end of MakeCommand -#if 0 /***********************************************************************/ /* MakeUpdate: make the SQL statement to send to ODBC connection. */ /***********************************************************************/ @@ -818,6 +829,7 @@ int TDBODBC::Cardinality(PGLOBAL g) return Cardinal; } // end of Cardinality +#if 0 /***********************************************************************/ /* ODBC GetMaxSize: returns table size estimate in number of lines. */ /***********************************************************************/ @@ -844,6 +856,7 @@ int TDBODBC::GetProgMax(PGLOBAL g) { return GetMaxSize(g); } // end of GetProgMax +#endif // 0 /***********************************************************************/ /* ODBC Access Method opening routine. */ @@ -981,6 +994,7 @@ bool TDBODBC::OpenDB(PGLOBAL g) return false; } // end of OpenDB +#if 0 /***********************************************************************/ /* GetRecpos: return the position of last read record. */ /***********************************************************************/ @@ -988,6 +1002,7 @@ int TDBODBC::GetRecpos(void) { return Fpos; } // end of GetRecpos +#endif // 0 /***********************************************************************/ /* SetRecpos: set the position of next read record. */ @@ -1081,8 +1096,9 @@ int TDBODBC::ReadDB(PGLOBAL g) int rc; if (trace > 1) - htrc("ODBC ReadDB: R%d Mode=%d key=%p link=%p Kindex=%p\n", - GetTdb_No(), Mode, To_Key_Col, To_Link, To_Kindex); + htrc("ODBC ReadDB: R%d Mode=%d\n", GetTdb_No(), Mode); + //htrc("ODBC ReadDB: R%d Mode=%d key=%p link=%p Kindex=%p\n", + // GetTdb_No(), Mode, To_Key_Col, To_Link, To_Kindex); if (Mode == MODE_UPDATE || Mode == MODE_DELETE) { if (!Query && MakeCommand(g)) @@ -1102,11 +1118,11 @@ int TDBODBC::ReadDB(PGLOBAL g) } // endif Mode - if (To_Kindex) { - // Direct access of ODBC tables is not implemented yet - strcpy(g->Message, MSG(NO_ODBC_DIRECT)); - return RC_FX; - } // endif To_Kindex + //if (To_Kindex) { + // // Direct access of ODBC tables is not implemented yet + // strcpy(g->Message, MSG(NO_ODBC_DIRECT)); + // return RC_FX; + // } // endif To_Kindex /*********************************************************************/ /* Now start the reading process. */ @@ -1212,70 +1228,58 @@ void TDBODBC::CloseDB(PGLOBAL g) /* ODBCCOL public constructor. */ /***********************************************************************/ ODBCCOL::ODBCCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PSZ am) - : COLBLK(cdp, tdbp, i) + : EXTCOL(cdp, tdbp, cprec, i, am) { - if (cprec) { - Next = cprec->GetNext(); - cprec->SetNext(this); - } else { - Next = tdbp->GetColumns(); - tdbp->SetColumns(this); - } // endif cprec - // Set additional ODBC access method information for column. - Crp = NULL; -//Long = cdp->GetLong(); - Long = Precision; +//Crp = NULL; +//Long = Precision; //strcpy(F_Date, cdp->F_Date); - To_Val = NULL; +//To_Val = NULL; Slen = 0; StrLen = &Slen; Sqlbuf = NULL; - Bufp = NULL; - Blkp = NULL; - Rank = 0; // Not known yet - - if (trace) - htrc(" making new %sCOL C%d %s at %p\n", am, Index, Name, this); - +//Bufp = NULL; +//Blkp = NULL; +//Rank = 0; // Not known yet } // end of ODBCCOL constructor /***********************************************************************/ /* ODBCCOL private constructor. */ /***********************************************************************/ -ODBCCOL::ODBCCOL(void) : COLBLK() +ODBCCOL::ODBCCOL(void) : EXTCOL() { - Crp = NULL; - Buf_Type = TYPE_INT; // This is a count(*) column - // Set additional Dos access method information for column. - Long = sizeof(int); - To_Val = NULL; +//Crp = NULL; +//Buf_Type = TYPE_INT; // This is a count(*) column +//// Set additional Dos access method information for column. +//Long = sizeof(int); +//To_Val = NULL; Slen = 0; StrLen = &Slen; Sqlbuf = NULL; - Bufp = NULL; - Blkp = NULL; - Rank = 1; +//Bufp = NULL; +//Blkp = NULL; +//Rank = 1; } // end of ODBCCOL constructor /***********************************************************************/ /* ODBCCOL constructor used for copying columns. */ /* tdbp is the pointer to the new table descriptor. */ /***********************************************************************/ -ODBCCOL::ODBCCOL(ODBCCOL *col1, PTDB tdbp) : COLBLK(col1, tdbp) +ODBCCOL::ODBCCOL(ODBCCOL *col1, PTDB tdbp) : EXTCOL(col1, tdbp) { - Crp = col1->Crp; - Long = col1->Long; +//Crp = col1->Crp; +//Long = col1->Long; //strcpy(F_Date, col1->F_Date); - To_Val = col1->To_Val; +//To_Val = col1->To_Val; Slen = col1->Slen; StrLen = col1->StrLen; Sqlbuf = col1->Sqlbuf; - Bufp = col1->Bufp; - Blkp = col1->Blkp; - Rank = col1->Rank; +//Bufp = col1->Bufp; +//Blkp = col1->Blkp; +//Rank = col1->Rank; } // end of ODBCCOL copy constructor +#if 0 /***********************************************************************/ /* SetBuffer: prepare a column block for write operation. */ /***********************************************************************/ @@ -1321,6 +1325,7 @@ bool ODBCCOL::SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check) Status = (ok) ? BUF_EMPTY : BUF_NO; return false; } // end of SetBuffer +#endif // 0 /***********************************************************************/ /* ReadColumn: when SQLFetch is used there is nothing to do as the */ @@ -1526,7 +1531,7 @@ TDBXDBC::TDBXDBC(PTDBXDBC tdbp) : TDBODBC(tdbp) Nerr = tdbp->Nerr; } // end of TDBXDBC copy constructor -PTDB TDBXDBC::CopyOne(PTABS t) +PTDB TDBXDBC::Clone(PTABS t) { PTDB tp; PXSRCCOL cp1, cp2; diff --git a/storage/connect/tabodbc.h b/storage/connect/tabodbc.h index aa6592d8abf..fcefad5647b 100644 --- a/storage/connect/tabodbc.h +++ b/storage/connect/tabodbc.h @@ -20,7 +20,7 @@ typedef class TDBSRC *PTDBSRC; /***********************************************************************/ /* ODBC table. */ /***********************************************************************/ -class DllExport ODBCDEF : public TABDEF { /* Logical table description */ +class DllExport ODBCDEF : public EXTDEF { /* Logical table description */ friend class TDBODBC; friend class TDBXDBC; friend class TDBDRV; @@ -33,14 +33,14 @@ public: // Implementation virtual const char *GetType(void) {return "ODBC";} PSZ GetConnect(void) {return Connect;} - PSZ GetTabname(void) {return Tabname;} - PSZ GetTabschema(void) {return Tabschema;} - PSZ GetTabcat(void) {return Tabcat;} - PSZ GetSrcdef(void) {return Srcdef;} - char GetSep(void) {return (Sep) ? *Sep : 0;} - int GetQuoted(void) {return Quoted;} + //PSZ GetTabname(void) {return Tabname;} + //PSZ GetTabschema(void) {return Tabschema;} + //PSZ GetTabcat(void) {return Tabcat;} + //PSZ GetSrcdef(void) {return Srcdef;} + //char GetSep(void) {return (Sep) ? *Sep : 0;} + //int GetQuoted(void) {return Quoted;} int GetCatver(void) {return Catver;} - int GetOptions(void) {return Options;} + //int GetOptions(void) {return Options;} // Methods virtual int Indexable(void) {return 2;} @@ -50,27 +50,27 @@ public: protected: // Members PSZ Connect; /* ODBC connection string */ - PSZ Tabname; /* External table name */ - PSZ Tabschema; /* External table schema */ - PSZ Username; /* User connect name */ - PSZ Password; /* Password connect info */ - PSZ Tabcat; /* External table catalog */ - PSZ Tabtyp; /* Catalog table type */ - PSZ Colpat; /* Catalog column pattern */ - PSZ Srcdef; /* The source table SQL definition */ - PSZ Qchar; /* Identifier quoting character */ - PSZ Qrystr; /* The original query */ - PSZ Sep; /* Decimal separator */ + //PSZ Tabname; /* External table name */ + //PSZ Tabschema; /* External table schema */ + //PSZ Username; /* User connect name */ + //PSZ Password; /* Password connect info */ + //PSZ Tabcat; /* External table catalog */ + //PSZ Tabtyp; /* Catalog table type */ + //PSZ Colpat; /* Catalog column pattern */ + //PSZ Srcdef; /* The source table SQL definition */ + //PSZ Qchar; /* Identifier quoting character */ + //PSZ Qrystr; /* The original query */ + //PSZ Sep; /* Decimal separator */ int Catver; /* ODBC version for catalog functions */ - int Options; /* Open connection options */ - int Cto; /* Open connection timeout */ - int Qto; /* Query (command) timeout */ - int Quoted; /* Identifier quoting level */ - int Maxerr; /* Maxerr for an Exec table */ - int Maxres; /* Maxres for a catalog table */ - int Memory; /* Put result set in memory */ - bool Scrollable; /* Use scrollable cursor */ - bool Xsrc; /* Execution type */ + //int Options; /* Open connection options */ + //int Cto; /* Open connection timeout */ + //int Qto; /* Query (command) timeout */ + //int Quoted; /* Identifier quoting level */ + //int Maxerr; /* Maxerr for an Exec table */ + //int Maxres; /* Maxres for a catalog table */ + //int Memory; /* Put result set in memory */ + //bool Scrollable; /* Use scrollable cursor */ + //bool Xsrc; /* Execution type */ bool UseCnc; /* Use SQLConnect (!SQLDriverConnect) */ }; // end of ODBCDEF @@ -81,7 +81,7 @@ public: /* This is the ODBC Access Method class declaration for files from */ /* other DB drivers to be accessed via ODBC. */ /***********************************************************************/ -class TDBODBC : public TDBASE { +class TDBODBC : public TDBEXT { friend class ODBCCOL; friend class ODBConn; public: @@ -95,8 +95,8 @@ class TDBODBC : public TDBASE { {return (PTDB)new(g) TDBODBC(this);} // Methods - virtual PTDB CopyOne(PTABS t); - virtual int GetRecpos(void); + virtual PTDB Clone(PTABS t); +//virtual int GetRecpos(void); virtual bool SetRecpos(PGLOBAL g, int recpos); virtual PSZ GetFile(PGLOBAL g); virtual void SetFile(PGLOBAL g, PSZ fn); @@ -108,8 +108,8 @@ class TDBODBC : public TDBASE { // Database routines virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n); virtual int Cardinality(PGLOBAL g); - virtual int GetMaxSize(PGLOBAL g); - virtual int GetProgMax(PGLOBAL g); +//virtual int GetMaxSize(PGLOBAL g); +//virtual int GetProgMax(PGLOBAL g); virtual bool OpenDB(PGLOBAL g); virtual int ReadDB(PGLOBAL g); virtual int WriteDB(PGLOBAL g); @@ -119,10 +119,10 @@ class TDBODBC : public TDBASE { protected: // Internal functions - int Decode(char *utf, char *buf, size_t n); - bool MakeSQL(PGLOBAL g, bool cnt); +//int Decode(char *utf, char *buf, size_t n); +//bool MakeSQL(PGLOBAL g, bool cnt); bool MakeInsert(PGLOBAL g); - bool MakeCommand(PGLOBAL g); +//virtual bool MakeCommand(PGLOBAL g); //bool MakeFilter(PGLOBAL g, bool c); bool BindParameters(PGLOBAL g); //char *MakeUpdate(PGLOBAL g); @@ -132,46 +132,16 @@ class TDBODBC : public TDBASE { ODBConn *Ocp; // Points to an ODBC connection class ODBCCOL *Cnp; // Points to count(*) column ODBCPARM Ops; // Additional parameters - PSTRG Query; // Constructed SQL query char *Connect; // Points to connection string - char *TableName; // Points to ODBC table name - char *Schema; // Points to ODBC table Schema - char *User; // User connect info - char *Pwd; // Password connect info - char *Catalog; // Points to ODBC table Catalog - char *Srcdef; // The source table SQL definition - char *Count; // Points to count(*) SQL statement -//char *Where; // Points to local where clause - char *Quote; // The identifier quoting character - char *MulConn; // Used for multiple ODBC tables - char *DBQ; // The address part of Connect string - char *Qrystr; // The original query - char Sep; // The decimal separator - int Options; // Connect options - int Cto; // Connect timeout - int Qto; // Query timeout - int Quoted; // The identifier quoting level - int Fpos; // Position of last read record - int Curpos; // Cursor position of last fetch - int AftRows; // The number of affected rows - int Rows; // Rowset size int Catver; // Catalog ODBC version - int CurNum; // Current buffer line number - int Rbuf; // Number of lines read in buffer - int BufSize; // Size of connect string buffer - int Nparm; // The number of statement parameters - int Memory; // 0: No 1: Alloc 2: Put 3: Get - bool Scrollable; // Use scrollable cursor - bool Placed; // True for position reading bool UseCnc; // Use SQLConnect (!SQLDriverConnect) - PQRYRES Qrp; // Points to storage result }; // end of class TDBODBC /***********************************************************************/ /* Class ODBCCOL: ODBC access method column descriptor. */ /* This A.M. is used for ODBC tables. */ /***********************************************************************/ -class ODBCCOL : public COLBLK { +class ODBCCOL : public EXTCOL { friend class TDBODBC; public: // Constructors @@ -181,12 +151,12 @@ class ODBCCOL : public COLBLK { // Implementation virtual int GetAmType(void) {return TYPE_AM_ODBC;} SQLLEN *GetStrLen(void) {return StrLen;} - int GetRank(void) {return Rank;} +// int GetRank(void) {return Rank;} // PVBLK GetBlkp(void) {return Blkp;} - void SetCrp(PCOLRES crp) {Crp = crp;} +// void SetCrp(PCOLRES crp) {Crp = crp;} // Methods - virtual bool SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check); +//virtual bool SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check); virtual void ReadColumn(PGLOBAL g); virtual void WriteColumn(PGLOBAL g); void AllocateBuffers(PGLOBAL g, int rows); @@ -195,19 +165,19 @@ class ODBCCOL : public COLBLK { // void Print(PGLOBAL g, FILE *, uint); protected: - // Constructor used by GetMaxSize + // Constructor for count(*) column ODBCCOL(void); // Members TIMESTAMP_STRUCT *Sqlbuf; // To get SQL_TIMESTAMP's - PCOLRES Crp; // To storage result - void *Bufp; // To extended buffer - PVBLK Blkp; // To Value Block +//PCOLRES Crp; // To storage result +//void *Bufp; // To extended buffer +//PVBLK Blkp; // To Value Block //char F_Date[12]; // Internal Date format - PVAL To_Val; // To value used for Insert +//PVAL To_Val; // To value used for Insert SQLLEN *StrLen; // As returned by ODBC SQLLEN Slen; // Used with Fetch - int Rank; // Rank (position) number in the query +//int Rank; // Rank (position) number in the query }; // end of class ODBCCOL /***********************************************************************/ @@ -228,28 +198,19 @@ class TDBXDBC : public TDBODBC { {return (PTDB)new(g) TDBXDBC(this);} // Methods - virtual PTDB CopyOne(PTABS t); -//virtual int GetRecpos(void); -//virtual PSZ GetFile(PGLOBAL g); -//virtual void SetFile(PGLOBAL g, PSZ fn); -//virtual void ResetSize(void); -//virtual int GetAffectedRows(void) {return AftRows;} -//virtual PSZ GetServer(void) {return "ODBC";} + virtual PTDB Clone(PTABS t); // Database routines virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n); -//virtual int GetProgMax(PGLOBAL g); virtual int GetMaxSize(PGLOBAL g); virtual bool OpenDB(PGLOBAL g); virtual int ReadDB(PGLOBAL g); virtual int WriteDB(PGLOBAL g); virtual int DeleteDB(PGLOBAL g, int irc); -//virtual void CloseDB(PGLOBAL g); protected: // Internal functions PCMD MakeCMD(PGLOBAL g); -//bool BindParameters(PGLOBAL g); // Members PCMD Cmdlist; // The commands to execute diff --git a/storage/connect/tabpivot.cpp b/storage/connect/tabpivot.cpp index 256b454741c..c6d32884417 100644 --- a/storage/connect/tabpivot.cpp +++ b/storage/connect/tabpivot.cpp @@ -1,11 +1,11 @@ /************ TabPivot C++ Program Source Code File (.CPP) *************/ /* PROGRAM NAME: TABPIVOT */ /* ------------- */ -/* Version 1.6 */ +/* Version 1.7 */ /* */ /* COPYRIGHT: */ /* ---------- */ -/* (C) Copyright to the author Olivier BERTRAND 2005-2015 */ +/* (C) Copyright to the author Olivier BERTRAND 2005-2017 */ /* */ /* WHAT THIS PROGRAM DOES: */ /* ----------------------- */ @@ -41,6 +41,7 @@ #include "global.h" #include "plgdbsem.h" #include "xtable.h" +#include "tabext.h" #include "tabcol.h" #include "colblk.h" #include "tabmysql.h" @@ -883,7 +884,7 @@ SRCCOL::SRCCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int n) /***********************************************************************/ /* Initialize the column as pointing to the source column. */ /***********************************************************************/ -bool SRCCOL::Init(PGLOBAL g, PTDBASE tp) +bool SRCCOL::Init(PGLOBAL g, PTDB tp) { if (PRXCOL::Init(g, tp)) return true; diff --git a/storage/connect/tabpivot.h b/storage/connect/tabpivot.h index c397af05234..07d5c3e456b 100644 --- a/storage/connect/tabpivot.h +++ b/storage/connect/tabpivot.h @@ -183,7 +183,7 @@ class SRCCOL : public PRXCOL { using PRXCOL::Init; virtual void Reset(void) {} void SetColumn(void); - virtual bool Init(PGLOBAL g, PTDBASE tp); + virtual bool Init(PGLOBAL g, PTDB tp); bool CompareLast(void); protected: diff --git a/storage/connect/tabsys.cpp b/storage/connect/tabsys.cpp index 76890e84429..2ddd1c3c753 100644 --- a/storage/connect/tabsys.cpp +++ b/storage/connect/tabsys.cpp @@ -159,7 +159,7 @@ TDBINI::TDBINI(PTDBINI tdbp) : TDBASE(tdbp) } // end of TDBINI copy constructor // Is this really useful ??? -PTDB TDBINI::CopyOne(PTABS t) +PTDB TDBINI::Clone(PTABS t) { PTDB tp; PINICOL cp1, cp2; @@ -173,7 +173,7 @@ PTDB TDBINI::CopyOne(PTABS t) } // endfor cp1 return tp; - } // end of CopyOne + } // end of Clone /***********************************************************************/ /* Get the section list from the INI file. */ @@ -565,7 +565,7 @@ TDBXIN::TDBXIN(PTDBXIN tdbp) : TDBINI(tdbp) } // end of TDBXIN copy constructor // Is this really useful ??? -PTDB TDBXIN::CopyOne(PTABS t) +PTDB TDBXIN::Clone(PTABS t) { PTDB tp; PXINCOL cp1, cp2; @@ -579,7 +579,7 @@ PTDB TDBXIN::CopyOne(PTABS t) } // endfor cp1 return tp; - } // end of CopyOne + } // end of Clone /***********************************************************************/ /* Get the key list from the INI file. */ diff --git a/storage/connect/tabsys.h b/storage/connect/tabsys.h index 6b454322906..ff1b8335690 100644 --- a/storage/connect/tabsys.h +++ b/storage/connect/tabsys.h @@ -57,7 +57,7 @@ class TDBINI : public TDBASE { virtual PTDB Duplicate(PGLOBAL g) {return (PTDB)new(g) TDBINI(this);} // Methods - virtual PTDB CopyOne(PTABS t); + virtual PTDB Clone(PTABS t); virtual int GetRecpos(void) {return N;} virtual int GetProgCur(void) {return N;} //virtual int GetAffectedRows(void) {return 0;} @@ -136,7 +136,7 @@ class TDBXIN : public TDBINI { virtual PTDB Duplicate(PGLOBAL g) {return (PTDB)new(g) TDBXIN(this);} // Methods - virtual PTDB CopyOne(PTABS t); + virtual PTDB Clone(PTABS t); virtual int GetRecpos(void); virtual bool SetRecpos(PGLOBAL g, int recpos); virtual void ResetDB(void) diff --git a/storage/connect/tabtbl.cpp b/storage/connect/tabtbl.cpp index e3baf7c3da5..0bf3f6beb43 100644 --- a/storage/connect/tabtbl.cpp +++ b/storage/connect/tabtbl.cpp @@ -1,11 +1,11 @@ /************* TabTbl C++ Program Source Code File (.CPP) **************/ /* PROGRAM NAME: TABTBL */ /* ------------- */ -/* Version 1.7 */ +/* Version 1.8 */ /* */ /* COPYRIGHT: */ /* ---------- */ -/* (C) Copyright to PlugDB Software Development 2008-2016 */ +/* (C) Copyright to PlugDB Software Development 2008-2017 */ /* Author: Olivier BERTRAND */ /* */ /* WHAT THIS PROGRAM DOES: */ @@ -70,6 +70,7 @@ #include "tabcol.h" #include "tabdos.h" // TDBDOS and DOSCOL class dcls #include "tabtbl.h" +#include "tabext.h" #include "tabmysql.h" #include "ha_connect.h" @@ -411,9 +412,9 @@ void TDBTBL::ResetDB(void) colp->COLBLK::Reset(); for (PTABLE tabp = Tablist; tabp; tabp = tabp->GetNext()) - ((PTDBASE)tabp->GetTo_Tdb())->ResetDB(); + tabp->GetTo_Tdb()->ResetDB(); - Tdbp = (PTDBASE)Tablist->GetTo_Tdb(); + Tdbp = Tablist->GetTo_Tdb(); Crp = 0; } // end of ResetDB @@ -458,7 +459,7 @@ bool TDBTBL::OpenDB(PGLOBAL g) return TRUE; if ((CurTable = Tablist)) { - Tdbp = (PTDBASE)CurTable->GetTo_Tdb(); + Tdbp = CurTable->GetTo_Tdb(); // Tdbp->SetMode(Mode); // Tdbp->ResetDB(); // Tdbp->ResetSize(); @@ -515,7 +516,7 @@ int TDBTBL::ReadDB(PGLOBAL g) /* Continue reading from next table file. */ /***************************************************************/ Tdbp->CloseDB(g); - Tdbp = (PTDBASE)CurTable->GetTo_Tdb(); + Tdbp = CurTable->GetTo_Tdb(); // Check and initialize the subtable columns for (PCOL cp = Columns; cp; cp = cp->GetNext()) @@ -609,13 +610,13 @@ void TDBTBM::ResetDB(void) // Local tables for (PTABLE tabp = Tablist; tabp; tabp = tabp->GetNext()) - ((PTDBASE)tabp->GetTo_Tdb())->ResetDB(); + tabp->GetTo_Tdb()->ResetDB(); // Remote tables for (PTBMT tp = Tmp; tp; tp = tp->Next) - ((PTDBASE)tp->Tap->GetTo_Tdb())->ResetDB(); + tp->Tap->GetTo_Tdb()->ResetDB(); - Tdbp = (Tablist) ? (PTDBASE)Tablist->GetTo_Tdb() : NULL; + Tdbp = (Tablist) ? Tablist->GetTo_Tdb() : NULL; Crp = 0; } // end of ResetDB @@ -716,7 +717,7 @@ bool TDBTBM::OpenDB(PGLOBAL g) /* Proceed with local tables. */ /*********************************************************************/ if ((CurTable = Tablist)) { - Tdbp = (PTDBASE)CurTable->GetTo_Tdb(); + Tdbp = CurTable->GetTo_Tdb(); // Tdbp->SetMode(Mode); // Check and initialize the subtable columns @@ -808,7 +809,7 @@ int TDBTBM::ReadNextRemote(PGLOBAL g) } // endif Curtable - Tdbp = (PTDBASE)Cmp->Tap->GetTo_Tdb(); + Tdbp = Cmp->Tap->GetTo_Tdb(); // Check and initialize the subtable columns for (PCOL cp = Columns; cp; cp = cp->GetNext()) diff --git a/storage/connect/tabutil.cpp b/storage/connect/tabutil.cpp index 4069cdbed2a..762c61bd1a1 100644 --- a/storage/connect/tabutil.cpp +++ b/storage/connect/tabutil.cpp @@ -1,7 +1,7 @@ /************* Tabutil cpp Declares Source Code File (.CPP) ************/ -/* Name: TABUTIL.CPP Version 1.1 */ +/* Name: TABUTIL.CPP Version 1.2 */ /* */ -/* (C) Copyright to the author Olivier BERTRAND 2013 - 2016 */ +/* (C) Copyright to the author Olivier BERTRAND 2013 - 2017 */ /* */ /* Utility function used by the PROXY, XCOL, OCCUR, and TBL tables. */ /***********************************************************************/ @@ -45,8 +45,9 @@ #include "myutil.h" #include "valblk.h" #include "resource.h" -#include "reldef.h" +//#include "reldef.h" #include "xtable.h" +#include "tabext.h" #include "tabmysql.h" #include "tabcol.h" #include "tabutil.h" @@ -356,7 +357,7 @@ TDBPRX::TDBPRX(PTDBPRX tdbp) : TDBASE(tdbp) } // end of TDBPRX copy constructor // Method -PTDB TDBPRX::CopyOne(PTABS t) +PTDB TDBPRX::Clone(PTABS t) { PTDB tp; PPRXCOL cp1, cp2; @@ -370,12 +371,12 @@ PTDB TDBPRX::CopyOne(PTABS t) } // endfor cp1 return tp; - } // end of CopyOne + } // end of Clone /***********************************************************************/ /* Get the PTDB of the sub-table. */ /***********************************************************************/ -PTDBASE TDBPRX::GetSubTable(PGLOBAL g, PTABLE tabp, bool b) +PTDB TDBPRX::GetSubTable(PGLOBAL g, PTABLE tabp, bool b) { const char *sp = NULL; char *db, *name; @@ -456,13 +457,13 @@ PTDBASE TDBPRX::GetSubTable(PGLOBAL g, PTABLE tabp, bool b) if (trace && tdbp) htrc("Subtable %s in %s\n", - name, SVP(((PTDBASE)tdbp)->GetDef()->GetDB())); + name, SVP(tdbp->GetDef()->GetDB())); err: if (s) free_table_share(s); - return (PTDBASE)tdbp; + return tdbp; } // end of GetSubTable /***********************************************************************/ @@ -560,9 +561,9 @@ bool TDBPRX::OpenDB(PGLOBAL g) /* its column blocks in mode write (required by XML tables). */ /*********************************************************************/ if (Mode == MODE_UPDATE) { - PTDBASE utp; + PTDB utp; - if (!(utp= (PTDBASE)Tdbp->Duplicate(g))) { + if (!(utp= Tdbp->Duplicate(g))) { sprintf(g->Message, MSG(INV_UPDT_TABLE), Tdbp->GetName()); return true; } // endif tp @@ -681,7 +682,7 @@ char *PRXCOL::Decode(PGLOBAL g, const char *cnm) /* PRXCOL initialization routine. */ /* Look for the matching column in the object table. */ /***********************************************************************/ -bool PRXCOL::Init(PGLOBAL g, PTDBASE tp) +bool PRXCOL::Init(PGLOBAL g, PTDB tp) { if (!tp) tp = ((PTDBPRX)To_Tdb)->Tdbp; diff --git a/storage/connect/tabutil.h b/storage/connect/tabutil.h index b320d169b36..8e56aecff86 100644 --- a/storage/connect/tabutil.h +++ b/storage/connect/tabutil.h @@ -67,7 +67,7 @@ class DllExport TDBPRX : public TDBASE { {return (PTDB)new(g) TDBPRX(this);} // Methods - virtual PTDB CopyOne(PTABS t); + virtual PTDB Clone(PTABS t); virtual int GetRecpos(void) {return Tdbp->GetRecpos();} virtual void ResetDB(void) {Tdbp->ResetDB();} virtual int RowNumber(PGLOBAL g, bool b = FALSE); @@ -83,12 +83,12 @@ class DllExport TDBPRX : public TDBASE { virtual int WriteDB(PGLOBAL g); virtual int DeleteDB(PGLOBAL g, int irc); virtual void CloseDB(PGLOBAL g) {if (Tdbp) Tdbp->CloseDB(g);} - PTDBASE GetSubTable(PGLOBAL g, PTABLE tabp, bool b = false); + PTDB GetSubTable(PGLOBAL g, PTABLE tabp, bool b = false); void RemoveNext(PTABLE tp); protected: // Members - PTDBASE Tdbp; // The object table + PTDB Tdbp; // The object table }; // end of class TDBPRX /***********************************************************************/ @@ -115,7 +115,7 @@ class DllExport PRXCOL : public COLBLK { {return false;} virtual void ReadColumn(PGLOBAL g); virtual void WriteColumn(PGLOBAL g); - virtual bool Init(PGLOBAL g, PTDBASE tp); + virtual bool Init(PGLOBAL g, PTDB tp); protected: char *Decode(PGLOBAL g, const char *cnm); diff --git a/storage/connect/tabvct.cpp b/storage/connect/tabvct.cpp index e788529075f..282fb55a43c 100644 --- a/storage/connect/tabvct.cpp +++ b/storage/connect/tabvct.cpp @@ -241,7 +241,7 @@ PTDB VCTDEF::GetTable(PGLOBAL g, MODE mode) /*********************************************************************/ if (mode != MODE_INSERT) if (tdbp->GetBlockValues(g)) - PushWarning(g, (PTDBASE)tdbp); + PushWarning(g, tdbp); // return NULL; // causes a crash when deleting index return tdbp; @@ -263,7 +263,7 @@ TDBVCT::TDBVCT(PGLOBAL g, PTDBVCT tdbp) : TDBFIX(g, tdbp) } // end of TDBVCT copy constructor // Method -PTDB TDBVCT::CopyOne(PTABS t) +PTDB TDBVCT::Clone(PTABS t) { PTDB tp; PVCTCOL cp1, cp2; @@ -277,7 +277,7 @@ PTDB TDBVCT::CopyOne(PTABS t) } // endfor cp1 return tp; - } // end of CopyOne + } // end of Clone /***********************************************************************/ /* Allocate VCT column description block. */ diff --git a/storage/connect/tabvct.h b/storage/connect/tabvct.h index 8ad3c8e21be..189a9ae2221 100644 --- a/storage/connect/tabvct.h +++ b/storage/connect/tabvct.h @@ -68,7 +68,7 @@ class DllExport TDBVCT : public TDBFIX { bool IsSplit(void) {return ((VCTDEF*)To_Def)->Split;} // Methods - virtual PTDB CopyOne(PTABS t); + virtual PTDB Clone(PTABS t); virtual bool IsUsingTemp(PGLOBAL g); // Database routines diff --git a/storage/connect/tabvir.cpp b/storage/connect/tabvir.cpp index 356fc981357..155c71fe268 100644 --- a/storage/connect/tabvir.cpp +++ b/storage/connect/tabvir.cpp @@ -20,7 +20,7 @@ #include "plgdbsem.h" #include "filter.h" #include "xtable.h" -#include "reldef.h" +//#include "reldef.h" #include "colblk.h" #include "mycat.h" // for FNC_COL #include "tabvir.h" diff --git a/storage/connect/tabwmi.cpp b/storage/connect/tabwmi.cpp index 98a44b9d635..4871a1d66dc 100644 --- a/storage/connect/tabwmi.cpp +++ b/storage/connect/tabwmi.cpp @@ -1,5 +1,5 @@ /***********************************************************************/ -/* TABWMI: Author Olivier Bertrand -- PlugDB -- 2012 - 2013 */ +/* TABWMI: Author Olivier Bertrand -- PlugDB -- 2012 - 2017 */ /* TABWMI: Virtual table to get WMI information. */ /***********************************************************************/ #if !defined(__WIN__) @@ -11,8 +11,9 @@ #include "global.h" #include "plgdbsem.h" #include "mycat.h" -#include "reldef.h" +//#include "reldef.h" #include "xtable.h" +#include "tabext.h" #include "colblk.h" //#include "filter.h" //#include "xindex.h" @@ -62,7 +63,7 @@ PWMIUT InitWMI(PGLOBAL g, char *nsp, char *classname) if (FAILED(res)) { sprintf(g->Message, "Failed to initialize COM library. " - "Error code = %p", res); + "Error code = %x", res); return NULL; } // endif res @@ -85,7 +86,7 @@ PWMIUT InitWMI(PGLOBAL g, char *nsp, char *classname) (void**) &loc); if (FAILED(res)) { sprintf(g->Message, "Failed to create Locator. " - "Error code = %p", res); + "Error code = %x", res); CoUninitialize(); return NULL; } // endif res @@ -94,7 +95,7 @@ PWMIUT InitWMI(PGLOBAL g, char *nsp, char *classname) NULL, NULL, NULL, 0, NULL, NULL, &wp->Svc); if (FAILED(res)) { - sprintf(g->Message, "Could not connect. Error code = %p", res); + sprintf(g->Message, "Could not connect. Error code = %x", res); loc->Release(); CoUninitialize(); return NULL; @@ -423,7 +424,7 @@ bool TDBWMI::Initialize(PGLOBAL g) if (FAILED(Res)) { sprintf(g->Message, "Failed to initialize COM library. " - "Error code = %p", Res); + "Error code = %x", Res); return true; // Program has failed. } // endif Res @@ -436,7 +437,7 @@ bool TDBWMI::Initialize(PGLOBAL g) if (FAILED(Res)) { sprintf(g->Message, "Failed to create Locator. " - "Error code = %p", Res); + "Error code = %x", Res); CoUninitialize(); return true; // Program has failed. } // endif Res @@ -448,7 +449,7 @@ bool TDBWMI::Initialize(PGLOBAL g) NULL, NULL,0, NULL, 0, 0, &Svc); if (FAILED(Res)) { - sprintf(g->Message, "Could not connect. Error code = %p", Res); + sprintf(g->Message, "Could not connect. Error code = %x", Res); loc->Release(); CoUninitialize(); return true; // Program has failed. @@ -463,7 +464,7 @@ bool TDBWMI::Initialize(PGLOBAL g) RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE); if (FAILED(Res)) { - sprintf(g->Message, "Could not set proxy. Error code = 0x", Res); + sprintf(g->Message, "Could not set proxy. Error code = %x", Res); Svc->Release(); CoUninitialize(); return true; // Program has failed. @@ -573,7 +574,7 @@ bool TDBWMI::GetWMIInfo(PGLOBAL g) NULL, &Enumerator); if (FAILED(Rc)) { - sprintf(g->Message, "Query %s failed. Error code = %p", cmd, Rc); + sprintf(g->Message, "Query %s failed. Error code = %x", cmd, Rc); Svc->Release(); CoUninitialize(); return true; // Program has failed. diff --git a/storage/connect/tabxcl.cpp b/storage/connect/tabxcl.cpp index add61431493..93a24accc3c 100644 --- a/storage/connect/tabxcl.cpp +++ b/storage/connect/tabxcl.cpp @@ -1,7 +1,7 @@ /************* TabXcl CPP Declares Source Code File (.CPP) *************/ /* Name: TABXCL.CPP Version 1.0 */ /* */ -/* (C) Copyright to the author Olivier BERTRAND 2013 */ +/* (C) Copyright to the author Olivier BERTRAND 2013-2017 */ /* */ /* XCOL: Table having one column containing several values */ /* comma separated. When creating the table, the name of the X */ @@ -45,12 +45,12 @@ #include "plgdbsem.h" #include "plgcnx.h" // For DB types #include "resource.h" -#include "reldef.h" +#include "xtable.h" +#include "tabext.h" #include "filamtxt.h" #include "tabdos.h" #include "tabcol.h" #include "tabxcl.h" -#include "xtable.h" #include "tabmysql.h" #include "ha_connect.h" @@ -246,7 +246,7 @@ XCLCOL::XCLCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i) /* XCLCOL initialization routine. */ /* Allocate Cbuf that will contain the Colp value. */ /***********************************************************************/ -bool XCLCOL::Init(PGLOBAL g, PTDBASE tp) +bool XCLCOL::Init(PGLOBAL g, PTDB tp) { if (PRXCOL::Init(g, tp)) return true; diff --git a/storage/connect/tabxcl.h b/storage/connect/tabxcl.h index 291f0b4263a..fde000ee709 100644 --- a/storage/connect/tabxcl.h +++ b/storage/connect/tabxcl.h @@ -91,7 +91,7 @@ class XCLCOL : public PRXCOL { using PRXCOL::Init; virtual void Reset(void) {} // Evaluated only by TDBXCL virtual void ReadColumn(PGLOBAL g); - virtual bool Init(PGLOBAL g, PTDBASE tp = NULL); + virtual bool Init(PGLOBAL g, PTDB tp = NULL); protected: // Default constructor not to be used diff --git a/storage/connect/tabxml.cpp b/storage/connect/tabxml.cpp index 3b8229fcf51..52cf3d3812f 100644 --- a/storage/connect/tabxml.cpp +++ b/storage/connect/tabxml.cpp @@ -42,7 +42,7 @@ /***********************************************************************/ #include "global.h" #include "plgdbsem.h" -#include "reldef.h" +//#include "reldef.h" #include "xtable.h" #include "colblk.h" #include "mycat.h" @@ -537,6 +537,11 @@ PTDB XMLDEF::GetTable(PGLOBAL g, MODE m) if (Catfunc == FNC_COL) return new(g) TDBXCT(this); + if (Zipped && !(m == MODE_READ || m == MODE_ANY)) { + strcpy(g->Message, "ZIpped XML tables are read only"); + return NULL; + } // endif Zipped + PTDBASE tdbp = new(g) TDBXML(this); if (Multiple) @@ -655,7 +660,7 @@ TDBXML::TDBXML(PTDBXML tdbp) : TDBASE(tdbp) } // end of TDBXML copy constructor // Used for update -PTDB TDBXML::CopyOne(PTABS t) +PTDB TDBXML::Clone(PTABS t) { PTDB tp; PXMLCOL cp1, cp2; @@ -669,7 +674,7 @@ PTDB TDBXML::CopyOne(PTABS t) } // endfor cp1 return tp; - } // end of CopyOne + } // end of Clone /***********************************************************************/ /* Allocate XML column description block. */ @@ -926,7 +931,7 @@ bool TDBXML::Initialize(PGLOBAL g) if (rc) sprintf(g->Message, "%s: %s", MSG(COM_ERROR), buf); else - sprintf(g->Message, "%s hr=%p", MSG(COM_ERROR), e.Error()); + sprintf(g->Message, "%s hr=%x", MSG(COM_ERROR), e.Error()); goto error; #endif // __WIN__ diff --git a/storage/connect/tabxml.h b/storage/connect/tabxml.h index 6c586d79dec..65b353072cb 100644 --- a/storage/connect/tabxml.h +++ b/storage/connect/tabxml.h @@ -71,7 +71,7 @@ class DllExport TDBXML : public TDBASE { virtual PTDB Duplicate(PGLOBAL g) {return (PTDB)new(g) TDBXML(this);} // Methods - virtual PTDB CopyOne(PTABS t); + virtual PTDB Clone(PTABS t); virtual int GetRecpos(void); virtual int GetProgCur(void) {return N;} virtual PSZ GetFile(PGLOBAL g) {return Xfile;} diff --git a/storage/connect/tabzip.cpp b/storage/connect/tabzip.cpp index 11f414ee154..b91059a3843 100644 --- a/storage/connect/tabzip.cpp +++ b/storage/connect/tabzip.cpp @@ -70,8 +70,12 @@ PCOL TDBZIP::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n) /* param: filename path and the filename of the zip file to open. */ /* return: true if open, false otherwise. */ /***********************************************************************/ -bool TDBZIP::open(PGLOBAL g, const char *filename) +bool TDBZIP::open(PGLOBAL g, const char *fn) { + char filename[_MAX_PATH]; + + PlugSetPath(filename, fn, GetPath()); + if (!zipfile && !(zipfile = unzOpen64(filename))) sprintf(g->Message, "Zipfile open error"); @@ -102,7 +106,7 @@ int TDBZIP::Cardinality(PGLOBAL g) unz_global_info64 ginfo; int err = unzGetGlobalInfo64(zipfile, &ginfo); - Cardinal = (err == UNZ_OK) ? ginfo.number_entry : 0; + Cardinal = (err == UNZ_OK) ? (int)ginfo.number_entry : 0; } else Cardinal = 0; @@ -221,6 +225,14 @@ void ZIPCOL::ReadColumn(PGLOBAL g) case 3: Value->SetValue((int)Tdbz->finfo.compression_method); break; + case 4: + Tdbz->finfo.tmu_date.tm_year -= 1900; + + if (((DTVAL*)Value)->MakeTime((tm*)&Tdbz->finfo.tmu_date)) + Value->SetNull(true); + + Tdbz->finfo.tmu_date.tm_year += 1900; + break; default: Value->SetValue_psz((PSZ)Tdbz->fn); } // endswitch flag diff --git a/storage/connect/tabzip.h b/storage/connect/tabzip.h index 6f1735258e7..dcec3475371 100644 --- a/storage/connect/tabzip.h +++ b/storage/connect/tabzip.h @@ -20,7 +20,7 @@ typedef class ZIPCOL *PZIPCOL; /***********************************************************************/ class DllExport ZIPDEF : public DOSDEF { /* Table description */ friend class TDBZIP; - friend class ZIPFAM; + friend class UNZFAM; public: // Constructor ZIPDEF(void) {} diff --git a/storage/connect/value.h b/storage/connect/value.h index a670ade4c28..14a568c3549 100644 --- a/storage/connect/value.h +++ b/storage/connect/value.h @@ -271,7 +271,7 @@ class DllExport TYPVAL<PSZ>: public VALUE { virtual void Reset(void) {*Strp = 0;} virtual int GetValLen(void) {return Len;}; virtual int GetValPrec() {return (Ci) ? 1 : 0;} - virtual int GetSize(void) {return (Strp) ? strlen(Strp) : 0;} + virtual int GetSize(void) {return (Strp) ? (int)strlen(Strp) : 0;} virtual PSZ GetCharValue(void) {return Strp;} virtual char GetTinyValue(void); virtual uchar GetUTinyValue(void); diff --git a/storage/connect/xindex.cpp b/storage/connect/xindex.cpp index a2cf4e77b80..15fb71ab88a 100755 --- a/storage/connect/xindex.cpp +++ b/storage/connect/xindex.cpp @@ -81,7 +81,7 @@ int PlgMakeIndex(PGLOBAL g, PSZ name, PIXDEF pxdf, bool add) { int rc; PTABLE tablep; - PTDBASE tdbp; + PTDB tdbp; PCATLG cat = PlgGetCatalog(g, true); /*********************************************************************/ @@ -89,12 +89,12 @@ int PlgMakeIndex(PGLOBAL g, PSZ name, PIXDEF pxdf, bool add) /*********************************************************************/ tablep = new(g) XTAB(name); - if (!(tdbp = (PTDBASE)cat->GetTable(g, tablep))) + if (!(tdbp = cat->GetTable(g, tablep))) rc = RC_NF; else if (!tdbp->GetDef()->Indexable()) { sprintf(g->Message, MSG(TABLE_NO_INDEX), name); rc = RC_NF; - } else if ((rc = tdbp->MakeIndex(g, pxdf, add)) == RC_INFO) + } else if ((rc = ((PTDBASE)tdbp)->MakeIndex(g, pxdf, add)) == RC_INFO) rc = RC_OK; // No or remote index return rc; @@ -2738,7 +2738,7 @@ bool XHUGE::Read(PGLOBAL g, void *buf, int n, int size) } // endif nbr } else { - char *buf[256]; + char buf[256]; DWORD drc = GetLastError(); FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | diff --git a/storage/connect/xindex.h b/storage/connect/xindex.h index ef2e934e5ee..2d10d72722e 100644 --- a/storage/connect/xindex.h +++ b/storage/connect/xindex.h @@ -184,7 +184,7 @@ class DllExport XXBASE : public CSORT, public BLOCK { virtual bool IsRandom(void) {return true;} virtual bool IsDynamic(void) {return Dynamic;} virtual void SetDynamic(bool dyn) {Dynamic = dyn;} - virtual bool HaveSame(void) {return false;} +//virtual bool HaveSame(void) {return false;} virtual int GetCurPos(void) {return Cur_K;} virtual void SetNval(int n) {assert(n == 1);} virtual void SetOp(OPVAL op) {Op = op;} @@ -256,7 +256,7 @@ class DllExport XINDEX : public XXBASE { // Implementation virtual IDT GetType(void) {return TYPE_IDX_INDX;} virtual bool IsMul(void) {return (Nval < Nk) ? true : Mul;} - virtual bool HaveSame(void) {return Op == OP_SAME;} +//virtual bool HaveSame(void) {return Op == OP_SAME;} virtual int GetCurPos(void) {return (Pex) ? Pex[Cur_K] : Cur_K;} virtual void SetNval(int n) {Nval = n;} int GetMaxSame(void) {return MaxSame;} diff --git a/storage/connect/xobject.h b/storage/connect/xobject.h index d78cd09f9a4..8f6c23c4aeb 100644 --- a/storage/connect/xobject.h +++ b/storage/connect/xobject.h @@ -127,7 +127,8 @@ class DllExport STRING : public BLOCK { // Implementation inline int GetLength(void) {return (int)Length;} - inline PSZ GetStr(void) {return Strp;} + inline void SetLength(uint n) {Length = n;} + inline PSZ GetStr(void) {return Strp;} inline uint32 GetSize(void) {return Size;} // Methods diff --git a/storage/connect/xtable.h b/storage/connect/xtable.h index e18a08a54b8..4aeea05946a 100644 --- a/storage/connect/xtable.h +++ b/storage/connect/xtable.h @@ -1,7 +1,7 @@ /**************** Table H Declares Source Code File (.H) ***************/ -/* Name: TABLE.H Version 2.3 */ +/* Name: TABLE.H Version 2.4 */ /* */ -/* (C) Copyright to the author Olivier BERTRAND 1999-2015 */ +/* (C) Copyright to the author Olivier BERTRAND 1999-2017 */ /* */ /* This file contains the TBX, OPJOIN and TDB class definitions. */ /***********************************************************************/ @@ -17,6 +17,7 @@ #include "block.h" #include "colblk.h" #include "m_ctype.h" +#include "reldef.h" typedef class CMD *PCMD; typedef struct st_key_range key_range; @@ -32,24 +33,30 @@ class CMD : public BLOCK { char *Cmd; }; // end of class CMD +#if 0 // Condition filter structure class CONDFIL : public BLOCK { public: // Constructor CONDFIL(const Item *cond, uint idx, AMT type) { - Cond = cond; Idx = idx; Type = type; Body = NULL; Op = OP_XX; Cmds = NULL; + Cond = cond; Idx = idx; Type = type; Op = OP_XX; + Cmds = NULL; All = true; Body = NULL, Having = NULL; } // Members const Item *Cond; AMT Type; uint Idx; - char *Body; OPVAL Op; PCMD Cmds; + bool All; + char *Body; + char *Having; }; // end of class CONDFIL +#endif // 0 +typedef class EXTCOL *PEXTCOL; typedef class CONDFIL *PCFIL; typedef class TDBCAT *PTDBCAT; typedef class CATCOL *PCATCOL; @@ -64,47 +71,61 @@ class DllExport TDB: public BLOCK { // Table Descriptor Block. TDB(PTDB tdbp); // Implementation - static void SetTnum(int n) {Tnum = n;} - inline PTDB GetOrig(void) {return To_Orig;} - inline TUSE GetUse(void) {return Use;} - inline PCFIL GetCondFil(void) {return To_CondFil;} - inline LPCSTR GetName(void) {return Name;} - inline PTABLE GetTable(void) {return To_Table;} - inline PCOL GetColumns(void) {return Columns;} - inline int GetDegree(void) {return Degree;} - inline MODE GetMode(void) {return Mode;} - inline PFIL GetFilter(void) {return To_Filter;} - inline void SetFilter(PFIL fp) {To_Filter = fp;} - inline void SetOrig(PTDB txp) {To_Orig = txp;} - inline void SetUse(TUSE n) {Use = n;} - inline void SetCondFil(PCFIL cfp) {To_CondFil = cfp;} - inline void SetNext(PTDB tdbp) {Next = tdbp;} - inline void SetName(LPCSTR name) {Name = name;} - inline void SetTable(PTABLE tablep) {To_Table = tablep;} - inline void SetColumns(PCOL colp) {Columns = colp;} - inline void SetDegree(int degree) {Degree = degree;} - inline void SetMode(MODE mode) {Mode = mode;} + static void SetTnum(int n) {Tnum = n;} + inline PTABDEF GetDef(void) {return To_Def;} + inline PTDB GetOrig(void) {return To_Orig;} + inline TUSE GetUse(void) {return Use;} + inline PCFIL GetCondFil(void) {return To_CondFil;} + inline LPCSTR GetName(void) {return Name;} + inline PTABLE GetTable(void) {return To_Table;} + inline PCOL GetColumns(void) {return Columns;} + inline int GetDegree(void) {return Degree;} + inline MODE GetMode(void) {return Mode;} + inline PFIL GetFilter(void) {return To_Filter;} + inline PCOL GetSetCols(void) {return To_SetCols;} + inline void SetSetCols(PCOL colp) {To_SetCols = colp;} + inline void SetFilter(PFIL fp) {To_Filter = fp;} + inline void SetOrig(PTDB txp) {To_Orig = txp;} + inline void SetUse(TUSE n) {Use = n;} + inline void SetCondFil(PCFIL cfp) {To_CondFil = cfp;} + inline void SetNext(PTDB tdbp) {Next = tdbp;} + inline void SetName(LPCSTR name) {Name = name;} + inline void SetTable(PTABLE tablep) {To_Table = tablep;} + inline void SetColumns(PCOL colp) {Columns = colp;} + inline void SetDegree(int degree) {Degree = degree;} + inline void SetMode(MODE mode) {Mode = mode;} // Properties - virtual AMT GetAmType(void) {return TYPE_AM_ERROR;} - virtual int GetTdb_No(void) {return Tdb_No;} - virtual PTDB GetNext(void) {return Next;} - virtual PCATLG GetCat(void) {return NULL;} - virtual void SetAbort(bool) {;} + virtual AMT GetAmType(void) {return TYPE_AM_ERROR;} + virtual bool IsRemote(void) {return false;} + virtual bool IsIndexed(void) {return false;} + virtual int GetTdb_No(void) {return Tdb_No;} + virtual PTDB GetNext(void) {return Next;} + virtual PCATLG GetCat(void) {return NULL;} + virtual void SetAbort(bool) {;} + virtual PKXBASE GetKindex(void) {return NULL;} // Methods virtual bool IsSame(PTDB tp) {return tp == this;} - virtual bool IsSpecial(PSZ name) = 0; - virtual bool GetBlockValues(PGLOBAL) {return false;} + virtual bool IsSpecial(PSZ name); + virtual bool IsReadOnly(void) {return Read_Only;} + virtual bool IsView(void) {return FALSE;} + virtual PSZ GetPath(void); + virtual RECFM GetFtype(void) {return RECFM_NAF;} + virtual bool GetBlockValues(PGLOBAL) { return false; } virtual int Cardinality(PGLOBAL) {return 0;} - virtual int GetMaxSize(PGLOBAL) = 0; + virtual int GetRecpos(void) = 0; + virtual bool SetRecpos(PGLOBAL g, int recpos); + virtual int GetMaxSize(PGLOBAL) = 0; virtual int GetProgMax(PGLOBAL) = 0; - virtual int GetProgCur(void) = 0; - virtual int RowNumber(PGLOBAL g, bool b = false); - virtual bool IsReadOnly(void) {return true;} - virtual const CHARSET_INFO *data_charset() {return NULL;} + virtual int GetProgCur(void) {return GetRecpos();} + virtual PSZ GetFile(PGLOBAL) {return "Not a file";} + virtual void SetFile(PGLOBAL, PSZ) {} + virtual void ResetDB(void) {} + virtual void ResetSize(void) {MaxSize = -1;} + virtual int RowNumber(PGLOBAL g, bool b = false); virtual PTDB Duplicate(PGLOBAL) {return NULL;} - virtual PTDB CopyOne(PTABS) {return this;} + virtual PTDB Clone(PTABS) {return this;} virtual PTDB Copy(PTABS t); virtual void PrintAM(FILE *f, char *m) {fprintf(f, "%s AM(%d)\n", m, GetAmType());} @@ -112,10 +133,15 @@ class DllExport TDB: public BLOCK { // Table Descriptor Block. virtual void Print(PGLOBAL g, char *ps, uint z); virtual PSZ GetServer(void) = 0; virtual int GetBadLines(void) {return 0;} + virtual CHARSET_INFO *data_charset(void); - // Database pure virtual routines - virtual PCOL ColDB(PGLOBAL g, PSZ name, int num) = 0; - virtual void MarkDB(PGLOBAL, PTDB) = 0; + // Database routines + virtual PCOL ColDB(PGLOBAL g, PSZ name, int num); + virtual PCOL MakeCol(PGLOBAL, PCOLDEF, PCOL, int) + {assert(false); return NULL;} + virtual PCOL InsertSpecialColumn(PCOL colp); + virtual PCOL InsertSpcBlk(PGLOBAL g, PCOLDEF cdp); + virtual void MarkDB(PGLOBAL g, PTDB tdb2); virtual bool OpenDB(PGLOBAL) = 0; virtual int ReadDB(PGLOBAL) = 0; virtual int WriteDB(PGLOBAL) = 0; @@ -126,20 +152,26 @@ class DllExport TDB: public BLOCK { // Table Descriptor Block. protected: // Members - PTDB To_Orig; // Pointer to original if it is a copy - TUSE Use; - PFIL To_Filter; - PCFIL To_CondFil; // To condition filter structure - static int Tnum; // Used to generate Tdb_no's - const int Tdb_No; // GetTdb_No() is always 0 for OPJOIN - PTDB Next; // Next in linearized queries - PTABLE To_Table; // Points to the XTAB object - LPCSTR Name; // Table name - PCOL Columns; // Points to the first column of the table - MODE Mode; // 10 Read, 30 Update, 40 Insert, 50 Delete - int Degree; // Number of columns - int Cardinal; // Table number of rows - }; // end of class TDB + PTDB To_Orig; // Pointer to original if it is a copy + PTABDEF To_Def; // Points to catalog description block + TUSE Use; + PFIL To_Filter; + PCFIL To_CondFil; // To condition filter structure + static int Tnum; // Used to generate Tdb_no's + const int Tdb_No; // GetTdb_No() is always 0 for OPJOIN + PTDB Next; // Next in linearized queries + PTABLE To_Table; // Points to the XTAB object + LPCSTR Name; // Table name + PCOL Columns; // Points to the first column of the table + PCOL To_SetCols; // Points to updated columns + MODE Mode; // 10 Read, 30 Update, 40 Insert, 50 Delete + int Degree; // Number of columns + int Cardinal; // Table number of rows + int MaxSize; // Max size in number of lines + bool Read_Only; // True for read only tables + const CHARSET_INFO *m_data_charset; + const char *csname; // Table charset name +}; // end of class TDB /***********************************************************************/ /* This is the base class for all query tables (except decode). */ @@ -155,50 +187,50 @@ class DllExport TDBASE : public TDB { // Implementation inline int GetKnum(void) {return Knum;} - inline PTABDEF GetDef(void) {return To_Def;} - inline PKXBASE GetKindex(void) {return To_Kindex;} - inline PCOL GetSetCols(void) {return To_SetCols;} - inline void SetSetCols(PCOL colp) {To_SetCols = colp;} +//inline PTABDEF GetDef(void) {return To_Def;} +//inline PCOL GetSetCols(void) {return To_SetCols;} +//inline void SetSetCols(PCOL colp) {To_SetCols = colp;} inline void SetKey_Col(PCOL *cpp) {To_Key_Col = cpp;} inline void SetXdp(PIXDEF xdp) {To_Xdp = xdp;} inline void SetKindex(PKXBASE kxp) {To_Kindex = kxp;} // Properties - void ResetKindex(PGLOBAL g, PKXBASE kxp); + virtual PKXBASE GetKindex(void) {return To_Kindex;} + void ResetKindex(PGLOBAL g, PKXBASE kxp); PCOL Key(int i) {return (To_Key_Col) ? To_Key_Col[i] : NULL;} // Methods virtual bool IsUsingTemp(PGLOBAL) {return false;} - virtual bool IsIndexed(void) {return false;} - virtual bool IsSpecial(PSZ name); +//virtual bool IsIndexed(void) {return false;} +//virtual bool IsSpecial(PSZ name); virtual PCATLG GetCat(void); - virtual PSZ GetPath(void); +//virtual PSZ GetPath(void); virtual void PrintAM(FILE *f, char *m); - virtual RECFM GetFtype(void) {return RECFM_NAF;} +//virtual RECFM GetFtype(void) {return RECFM_NAF;} //virtual int GetAffectedRows(void) {return -1;} - virtual int GetRecpos(void) = 0; - virtual bool SetRecpos(PGLOBAL g, int recpos); - virtual bool IsReadOnly(void) {return Read_Only;} - virtual bool IsView(void) {return FALSE;} - virtual CHARSET_INFO *data_charset(void); +//virtual int GetRecpos(void) = 0; +//virtual bool SetRecpos(PGLOBAL g, int recpos); +//virtual bool IsReadOnly(void) {return Read_Only;} +//virtual bool IsView(void) {return FALSE;} +//virtual CHARSET_INFO *data_charset(void); virtual int GetProgMax(PGLOBAL g) {return GetMaxSize(g);} - virtual int GetProgCur(void) {return GetRecpos();} - virtual PSZ GetFile(PGLOBAL) {return "Not a file";} - virtual int GetRemote(void) {return 0;} - virtual void SetFile(PGLOBAL, PSZ) {} - virtual void ResetDB(void) {} - virtual void ResetSize(void) {MaxSize = -1;} +//virtual int GetProgCur(void) {return GetRecpos();} +//virtual PSZ GetFile(PGLOBAL) {return "Not a file";} +//virtual int GetRemote(void) {return 0;} +//virtual void SetFile(PGLOBAL, PSZ) {} +//virtual void ResetDB(void) {} +//virtual void ResetSize(void) {MaxSize = -1;} virtual void RestoreNrec(void) {} virtual int ResetTableOpt(PGLOBAL g, bool dop, bool dox); virtual PSZ GetServer(void) {return "Current";} // Database routines - virtual PCOL ColDB(PGLOBAL g, PSZ name, int num); - virtual PCOL MakeCol(PGLOBAL, PCOLDEF, PCOL, int) - {assert(false); return NULL;} - virtual PCOL InsertSpecialColumn(PCOL colp); - virtual PCOL InsertSpcBlk(PGLOBAL g, PCOLDEF cdp); - virtual void MarkDB(PGLOBAL g, PTDB tdb2); +//virtual PCOL ColDB(PGLOBAL g, PSZ name, int num); +//virtual PCOL MakeCol(PGLOBAL, PCOLDEF, PCOL, int) +// {assert(false); return NULL;} +//virtual PCOL InsertSpecialColumn(PCOL colp); +//virtual PCOL InsertSpcBlk(PGLOBAL g, PCOLDEF cdp); +//virtual void MarkDB(PGLOBAL g, PTDB tdb2); virtual int MakeIndex(PGLOBAL g, PIXDEF, bool) {strcpy(g->Message, "Remote index"); return RC_INFO;} virtual bool ReadKey(PGLOBAL, OPVAL, const key_range *) @@ -209,19 +241,19 @@ class DllExport TDBASE : public TDB { "This function should not be called for this table"); return true;} // Members - PTABDEF To_Def; // Points to catalog description block +//PTABDEF To_Def; // Points to catalog description block PXOB *To_Link; // Points to column of previous relations PCOL *To_Key_Col; // Points to key columns in current file PKXBASE To_Kindex; // Points to table key index PIXDEF To_Xdp; // To the index definition block - PCOL To_SetCols; // Points to updated columns +//PCOL To_SetCols; // Points to updated columns RECFM Ftype; // File type: 0-var 1-fixed 2-binary (VCT) - int MaxSize; // Max size in number of lines +//int MaxSize; // Max size in number of lines int Knum; // Size of key arrays - bool Read_Only; // True for read only tables - const CHARSET_INFO *m_data_charset; - const char *csname; // Table charset name - }; // end of class TDBASE +//bool Read_Only; // True for read only tables +//const CHARSET_INFO *m_data_charset; +//const char *csname; // Table charset name +}; // end of class TDBASE /***********************************************************************/ /* The abstract base class declaration for the catalog tables. */ @@ -243,7 +275,8 @@ class DllExport TDBCAT : public TDBASE { // Database routines virtual PCOL MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n); - virtual int GetMaxSize(PGLOBAL g); + virtual int Cardinality(PGLOBAL) {return 10;} // To avoid assert + virtual int GetMaxSize(PGLOBAL g); virtual bool OpenDB(PGLOBAL g); virtual int ReadDB(PGLOBAL g); virtual int WriteDB(PGLOBAL g); @@ -275,7 +308,7 @@ class DllExport CATCOL : public COLBLK { virtual int GetAmType(void) {return TYPE_AM_ODBC;} // Methods - virtual void ReadColumn(PGLOBAL g); + virtual void ReadColumn(PGLOBAL g); protected: CATCOL(void) {} // Default constructor not to be used diff --git a/storage/connect/zip.c b/storage/connect/zip.c index ea54853e858..4bbe31ab7dd 100644 --- a/storage/connect/zip.c +++ b/storage/connect/zip.c @@ -637,7 +637,7 @@ local ZPOS64_T zip64local_SearchCentralDir64(const zlib_filefunc64_32_def* pzlib return relativeOffset; } -int LoadCentralDirectoryRecord(zip64_internal* pziinit) +static int LoadCentralDirectoryRecord(zip64_internal* pziinit) { int err=ZIP_OK; ZPOS64_T byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ @@ -846,7 +846,7 @@ int LoadCentralDirectoryRecord(zip64_internal* pziinit) /************************************************************/ -extern zipFile ZEXPORT zipOpen3 (const void *pathname, int append, zipcharpc* globalcomment, zlib_filefunc64_32_def* pzlib_filefunc64_32_def) +static zipFile zipOpen3 (const void *pathname, int append, zipcharpc* globalcomment, zlib_filefunc64_32_def* pzlib_filefunc64_32_def) { zip64_internal ziinit; zip64_internal* zi; @@ -955,7 +955,7 @@ extern zipFile ZEXPORT zipOpen64 (const void* pathname, int append) return zipOpen3(pathname,append,NULL,NULL); } -int Write_LocalFileHeader(zip64_internal* zi, const char* filename, uInt size_extrafield_local, const void* extrafield_local) +static int Write_LocalFileHeader(zip64_internal* zi, const char* filename, uInt size_extrafield_local, const void* extrafield_local) { /* write the local header */ int err; @@ -1752,7 +1752,7 @@ extern int ZEXPORT zipCloseFileInZip (zipFile file) return zipCloseFileInZipRaw (file,0,0); } -int Write_Zip64EndOfCentralDirectoryLocator(zip64_internal* zi, ZPOS64_T zip64eocd_pos_inzip) +static int Write_Zip64EndOfCentralDirectoryLocator(zip64_internal* zi, ZPOS64_T zip64eocd_pos_inzip) { int err = ZIP_OK; ZPOS64_T pos = zip64eocd_pos_inzip - zi->add_position_when_writting_offset; @@ -1774,7 +1774,7 @@ int Write_Zip64EndOfCentralDirectoryLocator(zip64_internal* zi, ZPOS64_T zip64eo return err; } -int Write_Zip64EndOfCentralDirectoryRecord(zip64_internal* zi, uLong size_centraldir, ZPOS64_T centraldir_pos_inzip) +static int Write_Zip64EndOfCentralDirectoryRecord(zip64_internal* zi, uLong size_centraldir, ZPOS64_T centraldir_pos_inzip) { int err = ZIP_OK; @@ -1813,7 +1813,7 @@ int Write_Zip64EndOfCentralDirectoryRecord(zip64_internal* zi, uLong size_centra } return err; } -int Write_EndOfCentralDirectoryRecord(zip64_internal* zi, uLong size_centraldir, ZPOS64_T centraldir_pos_inzip) +static int Write_EndOfCentralDirectoryRecord(zip64_internal* zi, uLong size_centraldir, ZPOS64_T centraldir_pos_inzip) { int err = ZIP_OK; @@ -1861,7 +1861,7 @@ int Write_EndOfCentralDirectoryRecord(zip64_internal* zi, uLong size_centraldir, return err; } -int Write_GlobalComment(zip64_internal* zi, const char* global_comment) +static int Write_GlobalComment(zip64_internal* zi, const char* global_comment) { int err = ZIP_OK; uInt size_global_comment = 0; diff --git a/storage/innobase/btr/btr0cur.cc b/storage/innobase/btr/btr0cur.cc index fca433f9648..4f24ad75e26 100644 --- a/storage/innobase/btr/btr0cur.cc +++ b/storage/innobase/btr/btr0cur.cc @@ -1880,7 +1880,6 @@ btr_cur_update_alloc_zip_func( const page_t* page = page_cur_get_page(cursor); ut_ad(page_zip == page_cur_get_page_zip(cursor)); - ut_ad(page_zip); ut_ad(!dict_index_is_ibuf(index)); ut_ad(rec_offs_validate(page_cur_get_rec(cursor), index, offsets)); @@ -2962,7 +2961,7 @@ btr_cur_del_mark_set_clust_rec( ut_ad(page_is_leaf(page_align(rec))); #ifdef UNIV_DEBUG - if (btr_cur_print_record_ops && (thr != NULL)) { + if (btr_cur_print_record_ops) { btr_cur_trx_report(thr_get_trx(thr)->id, index, "del mark "); rec_print_new(stderr, rec, offsets); } @@ -4278,7 +4277,6 @@ btr_cur_disown_inherited_fields( ut_ad(rec_offs_validate(rec, index, offsets)); ut_ad(!rec_offs_comp(offsets) || !rec_get_node_ptr_flag(rec)); ut_ad(rec_offs_any_extern(offsets)); - ut_ad(mtr); for (i = 0; i < rec_offs_n_fields(offsets); i++) { if (rec_offs_nth_extern(offsets, i) @@ -4341,9 +4339,6 @@ btr_push_update_extern_fields( ulint n; const upd_field_t* uf; - ut_ad(tuple); - ut_ad(update); - uf = update->fields; n = upd_get_n_fields(update); @@ -4515,7 +4510,6 @@ btr_store_big_rec_extern_fields( ut_ad(rec_offs_validate(rec, index, offsets)); ut_ad(rec_offs_any_extern(offsets)); - ut_ad(btr_mtr); ut_ad(mtr_memo_contains(btr_mtr, dict_index_get_lock(index), MTR_MEMO_X_LOCK)); ut_ad(mtr_memo_contains(btr_mtr, rec_block, MTR_MEMO_PAGE_X_FIX)); diff --git a/storage/innobase/buf/buf0dump.cc b/storage/innobase/buf/buf0dump.cc index d08bde763b3..d5efd87f9e1 100644 --- a/storage/innobase/buf/buf0dump.cc +++ b/storage/innobase/buf/buf0dump.cc @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 2011, 2016, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2017, MariaDB Corporation. 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 @@ -52,8 +53,8 @@ enum status_severity { /* Flags that tell the buffer pool dump/load thread which action should it take after being waked up. */ -static ibool buf_dump_should_start = FALSE; -static ibool buf_load_should_start = FALSE; +static volatile bool buf_dump_should_start; +static volatile bool buf_load_should_start; static ibool buf_load_abort_flag = FALSE; @@ -78,7 +79,7 @@ void buf_dump_start() /*============*/ { - buf_dump_should_start = TRUE; + buf_dump_should_start = true; os_event_set(srv_buf_dump_event); } @@ -92,7 +93,7 @@ void buf_load_start() /*============*/ { - buf_load_should_start = TRUE; + buf_load_should_start = true; os_event_set(srv_buf_dump_event); } @@ -695,15 +696,18 @@ DECLARE_THREAD(buf_dump_thread)( os_event_wait(srv_buf_dump_event); if (buf_dump_should_start) { - buf_dump_should_start = FALSE; + buf_dump_should_start = false; buf_dump(TRUE /* quit on shutdown */); } if (buf_load_should_start) { - buf_load_should_start = FALSE; + buf_load_should_start = false; buf_load(); } + if (buf_dump_should_start || buf_load_should_start) { + continue; + } os_event_reset(srv_buf_dump_event); } diff --git a/storage/innobase/buf/buf0flu.cc b/storage/innobase/buf/buf0flu.cc index a139166d5d3..d923514123c 100644 --- a/storage/innobase/buf/buf0flu.cc +++ b/storage/innobase/buf/buf0flu.cc @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1995, 2016, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2017, MariaDB Corporation. 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 @@ -732,8 +733,7 @@ buf_flush_write_complete( flush_type = buf_page_get_flush_type(bpage); buf_pool->n_flush[flush_type]--; - /* fprintf(stderr, "n pending flush %lu\n", - buf_pool->n_flush[flush_type]); */ + ut_ad(buf_pool_mutex_own(buf_pool)); if (buf_pool->n_flush[flush_type] == 0 && buf_pool->init_flush[flush_type] == FALSE) { diff --git a/storage/innobase/dict/dict0dict.cc b/storage/innobase/dict/dict0dict.cc index 9373f06e7de..03de0b4db7c 100644 --- a/storage/innobase/dict/dict0dict.cc +++ b/storage/innobase/dict/dict0dict.cc @@ -6178,7 +6178,6 @@ dict_set_corrupted( row_mysql_lock_data_dictionary(trx); } - ut_ad(index); ut_ad(mutex_own(&dict_sys->mutex)); ut_ad(!dict_table_is_comp(dict_sys->sys_tables)); ut_ad(!dict_table_is_comp(dict_sys->sys_indexes)); diff --git a/storage/innobase/dict/dict0stats_bg.cc b/storage/innobase/dict/dict0stats_bg.cc index 3215fe9a456..05ee6f5886f 100644 --- a/storage/innobase/dict/dict0stats_bg.cc +++ b/storage/innobase/dict/dict0stats_bg.cc @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 2012, 2016, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2017, MariaDB Corporation. 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 @@ -39,8 +40,9 @@ Created Apr 25, 2012 Vasil Dimov #define SHUTTING_DOWN() (srv_shutdown_state != SRV_SHUTDOWN_NONE) -/** Event to wake up the stats thread */ -UNIV_INTERN os_event_t dict_stats_event = NULL; +/** Event to wake up dict_stats_thread on dict_stats_recalc_pool_add() +or shutdown. Not protected by any mutex. */ +UNIV_INTERN os_event_t dict_stats_event; /** This mutex protects the "recalc_pool" variable. */ static ib_mutex_t recalc_pool_mutex; diff --git a/storage/innobase/dyn/dyn0dyn.cc b/storage/innobase/dyn/dyn0dyn.cc index 3ef5297a7c9..dd1f6863c14 100644 --- a/storage/innobase/dyn/dyn0dyn.cc +++ b/storage/innobase/dyn/dyn0dyn.cc @@ -40,7 +40,6 @@ dyn_array_add_block( mem_heap_t* heap; dyn_block_t* block; - ut_ad(arr); ut_ad(arr->magic_n == DYN_BLOCK_MAGIC_N); if (arr->heap == NULL) { diff --git a/storage/innobase/fil/fil0fil.cc b/storage/innobase/fil/fil0fil.cc index 9d471f9dbd3..72ee8d74a82 100644 --- a/storage/innobase/fil/fil0fil.cc +++ b/storage/innobase/fil/fil0fil.cc @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1995, 2016, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2014, 2017, MariaDB Corporation. 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 @@ -4953,15 +4954,12 @@ fil_extend_space_to_desired_size( byte* buf; ulint buf_size; ulint start_page_no; - ulint file_start_page_no; ulint page_size; - ulint pages_added; ibool success; ut_ad(!srv_read_only_mode); retry: - pages_added = 0; success = TRUE; fil_mutex_enter_and_prepare_for_io(space_id); @@ -5015,29 +5013,36 @@ retry: mutex_exit(&fil_system->mutex); start_page_no = space->size; - file_start_page_no = space->size - node->size; - + const ulint file_start_page_no = space->size - node->size; #ifdef HAVE_POSIX_FALLOCATE if (srv_use_posix_fallocate) { - os_offset_t start_offset = start_page_no * page_size; - os_offset_t n_pages = (size_after_extend - start_page_no); - os_offset_t len = n_pages * page_size; - - if (posix_fallocate(node->handle, start_offset, len) == -1) { - ib_logf(IB_LOG_LEVEL_ERROR, "preallocating file " - "space for file \'%s\' failed. Current size " - INT64PF ", desired size " INT64PF "\n", - node->name, start_offset, len+start_offset); - os_file_handle_error_no_exit(node->name, "posix_fallocate", FALSE); - success = FALSE; - } else { - success = TRUE; + const os_offset_t start_offset + = os_offset_t(start_page_no - file_start_page_no) + * page_size; + const ulint n_pages + = size_after_extend - start_page_no; + const os_offset_t len = os_offset_t(n_pages) * page_size; + + int err; + do { + err = posix_fallocate(node->handle, start_offset, len); + } while (err == EINTR + && srv_shutdown_state == SRV_SHUTDOWN_NONE); + + success = !err; + if (!success) { + ib_logf(IB_LOG_LEVEL_ERROR, "extending file %s" + " from " INT64PF " to " INT64PF " bytes" + " failed with error %d", + node->name, start_offset, len + start_offset, + err); } DBUG_EXECUTE_IF("ib_os_aio_func_io_failure_28", - success = FALSE; errno = 28; os_has_said_disk_full = TRUE;); + success = FALSE; os_has_said_disk_full = TRUE;); mutex_enter(&fil_system->mutex); + if (success) { node->size += n_pages; space->size += n_pages; @@ -5054,14 +5059,24 @@ retry: } #endif +#ifdef _WIN32 + /* Write 1 page of zeroes at the desired end. */ + start_page_no = size_after_extend - 1; + buf_size = page_size; +#else /* Extend at most 64 pages at a time */ buf_size = ut_min(64, size_after_extend - start_page_no) * page_size; - buf2 = static_cast<byte*>(mem_alloc(buf_size + page_size)); +#endif + buf2 = static_cast<byte*>(calloc(1, buf_size + page_size)); + if (!buf2) { + ib_logf(IB_LOG_LEVEL_ERROR, "Cannot allocate " ULINTPF + " bytes to extend file", + buf_size + page_size); + success = FALSE; + } buf = static_cast<byte*>(ut_align(buf2, page_size)); - memset(buf, 0, buf_size); - - while (start_page_no < size_after_extend) { + while (success && start_page_no < size_after_extend) { ulint n_pages = ut_min(buf_size / page_size, size_after_extend - start_page_no); @@ -5070,56 +5085,47 @@ retry: = ((os_offset_t) (start_page_no - file_start_page_no)) * page_size; - const char* name = node->name == NULL ? space->name : node->name; - #ifdef UNIV_HOTBACKUP - success = os_file_write(name, node->handle, buf, + success = os_file_write(node->name, node->handle, buf, offset, page_size * n_pages); #else success = os_aio(OS_FILE_WRITE, OS_AIO_SYNC, - name, node->handle, buf, + node->name, node->handle, buf, offset, page_size * n_pages, NULL, NULL); #endif /* UNIV_HOTBACKUP */ - DBUG_EXECUTE_IF("ib_os_aio_func_io_failure_28", - success = FALSE; errno = 28; os_has_said_disk_full = TRUE;); - - if (success) { - os_has_said_disk_full = FALSE; - } else { - /* Let us measure the size of the file to determine - how much we were able to extend it */ - os_offset_t size; + success = FALSE; os_has_said_disk_full = TRUE;); - size = os_file_get_size(node->handle); - ut_a(size != (os_offset_t) -1); + /* Let us measure the size of the file to determine + how much we were able to extend it */ + os_offset_t size = os_file_get_size(node->handle); + ut_a(size != (os_offset_t) -1); - n_pages = ((ulint) (size / page_size)) - - node->size - pages_added; - - pages_added += n_pages; - break; - } - - start_page_no += n_pages; - pages_added += n_pages; + start_page_no = (ulint) (size / page_size) + + file_start_page_no; } - mem_free(buf2); + free(buf2); mutex_enter(&fil_system->mutex); ut_a(node->being_extended); + ut_a(start_page_no - file_start_page_no >= node->size); - space->size += pages_added; - node->size += pages_added; + if (buf) { + ulint file_size = start_page_no - file_start_page_no; + space->size += file_size - node->size; + node->size = file_size; + } fil_node_complete_io(node, fil_system, OS_FILE_WRITE); /* At this point file has been extended */ +#ifdef HAVE_POSIX_FALLOCATE file_extended: +#endif /* HAVE_POSIX_FALLOCATE */ node->being_extended = FALSE; *actual_size = space->size; diff --git a/storage/innobase/fsp/fsp0fsp.cc b/storage/innobase/fsp/fsp0fsp.cc index afdd6667711..1732a02aeac 100644 --- a/storage/innobase/fsp/fsp0fsp.cc +++ b/storage/innobase/fsp/fsp0fsp.cc @@ -1064,8 +1064,6 @@ fsp_fill_free_list( ulint i; mtr_t ibuf_mtr; - ut_ad(header != NULL); - ut_ad(mtr != NULL); ut_ad(page_offset(header) == FSP_HEADER_OFFSET); /* Check if we can fill free list from above the free list limit */ @@ -1348,9 +1346,6 @@ fsp_alloc_free_page( ulint page_no; ulint space_size; - ut_ad(mtr); - ut_ad(init_mtr); - header = fsp_get_space_header(space, zip_size, mtr); /* Get the hinted descriptor */ @@ -2363,7 +2358,6 @@ fseg_alloc_free_page_low( ibool success; ulint n; - ut_ad(mtr); ut_ad((direction >= FSP_UP) && (direction <= FSP_NO_DIR)); ut_ad(mach_read_from_4(seg_inode + FSEG_MAGIC_N) == FSEG_MAGIC_N_VALUE); @@ -2801,6 +2795,7 @@ try_again: } } else { ut_a(alloc_type == FSP_CLEANING); + reserve = 0; } success = fil_space_reserve_free_extents(space, n_free, n_ext); diff --git a/storage/innobase/fts/fts0fts.cc b/storage/innobase/fts/fts0fts.cc index 6059c28eabc..18d09c8138f 100644 --- a/storage/innobase/fts/fts0fts.cc +++ b/storage/innobase/fts/fts0fts.cc @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 2011, 2016, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2016, MariaDB Corporation. 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 @@ -1934,6 +1935,8 @@ func_exit: /*************************************************************//** Wrapper function of fts_create_index_tables_low(), create auxiliary tables for an FTS index + +@see row_merge_create_fts_sort_index() @return: DB_SUCCESS or error code */ static dict_table_t* @@ -1965,13 +1968,12 @@ fts_create_one_index_table( (int)(field->col->prtype & DATA_MYSQL_TYPE_MASK), (uint) dtype_get_charset_coll(field->col->prtype)); - if (strcmp(charset->name, "latin1_swedish_ci") == 0) { - dict_mem_table_add_col(new_table, heap, "word", DATA_VARCHAR, - field->col->prtype, FTS_MAX_WORD_LEN); - } else { - dict_mem_table_add_col(new_table, heap, "word", DATA_VARMYSQL, - field->col->prtype, FTS_MAX_WORD_LEN); - } + dict_mem_table_add_col(new_table, heap, "word", + charset == &my_charset_latin1 + ? DATA_VARCHAR : DATA_VARMYSQL, + field->col->prtype, + FTS_MAX_WORD_LEN_IN_CHAR + * DATA_MBMAXLEN(field->col->mbminmaxlen)); dict_mem_table_add_col(new_table, heap, "first_doc_id", DATA_INT, DATA_NOT_NULL | DATA_UNSIGNED, diff --git a/storage/innobase/fts/fts0opt.cc b/storage/innobase/fts/fts0opt.cc index 455e2669c0d..cb30122adcb 100644 --- a/storage/innobase/fts/fts0opt.cc +++ b/storage/innobase/fts/fts0opt.cc @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 2007, 2016, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2016, MariaDB Corporation. 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 @@ -281,7 +282,7 @@ fts_zip_initialize( zip->last_big_block = 0; zip->word.f_len = 0; - memset(zip->word.f_str, 0, FTS_MAX_WORD_LEN); + *zip->word.f_str = 0; ib_vector_reset(zip->blocks); diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 6fe56892ae6..084a3090f15 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -4,7 +4,7 @@ Copyright (c) 2000, 2016, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2008, 2009 Google Inc. Copyright (c) 2009, Percona Inc. Copyright (c) 2012, Facebook Inc. -Copyright (c) 2013, 2016, MariaDB Corporation. +Copyright (c) 2013, 2017, MariaDB Corporation. Portions of this file contain modifications contributed and copyrighted by Google, Inc. Those modifications are gratefully acknowledged and are described @@ -3604,7 +3604,6 @@ innobase_change_buffering_inited_ok: and consequently we do not need to know the ordering internally in InnoDB. */ - ut_a(0 == strcmp(my_charset_latin1.name, "latin1_swedish_ci")); srv_latin1_ordering = my_charset_latin1.sort_order; innobase_commit_concurrency_init_default(); @@ -6390,18 +6389,16 @@ get_innobase_type_from_mysql_type( case MYSQL_TYPE_VARCHAR: /* new >= 5.0.3 true VARCHAR */ if (field->binary()) { return(DATA_BINARY); - } else if (strcmp(field->charset()->name, - "latin1_swedish_ci") == 0) { + } else if (field->charset() == &my_charset_latin1) { return(DATA_VARCHAR); } else { return(DATA_VARMYSQL); } case MYSQL_TYPE_BIT: - case MYSQL_TYPE_STRING: if (field->binary()) { - + case MYSQL_TYPE_STRING: + if (field->binary()) { return(DATA_FIXBINARY); - } else if (strcmp(field->charset()->name, - "latin1_swedish_ci") == 0) { + } else if (field->charset() == &my_charset_latin1) { return(DATA_CHAR); } else { return(DATA_MYSQL); @@ -18497,13 +18494,6 @@ static MYSQL_SYSVAR_ULONG(force_recovery, srv_force_recovery, "Helps to save your data in case the disk image of the database becomes corrupt.", NULL, NULL, 0, 0, 6, 0); -#ifndef DBUG_OFF -static MYSQL_SYSVAR_ULONG(force_recovery_crash, srv_force_recovery_crash, - PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY, - "Kills the server during crash recovery.", - NULL, NULL, 0, 0, 10, 0); -#endif /* !DBUG_OFF */ - static MYSQL_SYSVAR_ULONG(page_size, srv_page_size, PLUGIN_VAR_OPCMDARG | PLUGIN_VAR_READONLY, "Page size to use for all InnoDB tablespaces.", @@ -18906,9 +18896,6 @@ static struct st_mysql_sys_var* innobase_system_variables[]= { MYSQL_SYSVAR(flush_log_at_trx_commit), MYSQL_SYSVAR(flush_method), MYSQL_SYSVAR(force_recovery), -#ifndef DBUG_OFF - MYSQL_SYSVAR(force_recovery_crash), -#endif /* !DBUG_OFF */ MYSQL_SYSVAR(ft_cache_size), MYSQL_SYSVAR(ft_total_cache_size), MYSQL_SYSVAR(ft_result_cache_limit), diff --git a/storage/innobase/include/buf0buf.h b/storage/innobase/include/buf0buf.h index b80e5d505ac..78e961d91b7 100644 --- a/storage/innobase/include/buf0buf.h +++ b/storage/innobase/include/buf0buf.h @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1995, 2016, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2017, MariaDB Corporation. 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 @@ -1905,7 +1906,9 @@ struct buf_pool_t{ os_event_t no_flush[BUF_FLUSH_N_TYPES]; /*!< this is in the set state when there is no flush batch - of the given type running */ + of the given type running; + os_event_set() and os_event_reset() + are protected by buf_pool_t::mutex */ ib_rbt_t* flush_rbt; /*!< a red-black tree is used exclusively during recovery to speed up insertions in the diff --git a/storage/innobase/include/buf0dblwr.h b/storage/innobase/include/buf0dblwr.h index a62a6400d97..5582778825c 100644 --- a/storage/innobase/include/buf0dblwr.h +++ b/storage/innobase/include/buf0dblwr.h @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1995, 2014, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2017, MariaDB Corporation. 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 @@ -134,11 +135,13 @@ struct buf_dblwr_t{ ulint b_reserved;/*!< number of slots currently reserved for batch flush. */ os_event_t b_event;/*!< event where threads wait for a - batch flush to end. */ + batch flush to end; + os_event_set() and os_event_reset() + are protected by buf_dblwr_t::mutex */ ulint s_reserved;/*!< number of slots currently reserved for single page flushes. */ os_event_t s_event;/*!< event where threads wait for a - single page flush slot. */ + single page flush slot. Protected by mutex. */ bool* in_use; /*!< flag used to indicate if a slot is in use. Only used for single page flushes. */ diff --git a/storage/innobase/include/dict0dict.ic b/storage/innobase/include/dict0dict.ic index 6fc6098f3d9..1ed231aa248 100644 --- a/storage/innobase/include/dict0dict.ic +++ b/storage/innobase/include/dict0dict.ic @@ -266,7 +266,6 @@ dict_index_is_clust( /*================*/ const dict_index_t* index) /*!< in: index */ { - ut_ad(index); ut_ad(index->magic_n == DICT_INDEX_MAGIC_N); return(index->type & DICT_CLUSTERED); @@ -280,7 +279,6 @@ dict_index_is_unique( /*=================*/ const dict_index_t* index) /*!< in: index */ { - ut_ad(index); ut_ad(index->magic_n == DICT_INDEX_MAGIC_N); return(index->type & DICT_UNIQUE); @@ -295,7 +293,6 @@ dict_index_is_ibuf( /*===============*/ const dict_index_t* index) /*!< in: index */ { - ut_ad(index); ut_ad(index->magic_n == DICT_INDEX_MAGIC_N); return(index->type & DICT_IBUF); @@ -327,7 +324,6 @@ dict_index_is_sec_or_ibuf( { ulint type; - ut_ad(index); ut_ad(index->magic_n == DICT_INDEX_MAGIC_N); type = index->type; @@ -345,7 +341,6 @@ dict_table_get_n_user_cols( /*=======================*/ const dict_table_t* table) /*!< in: table */ { - ut_ad(table); ut_ad(table->magic_n == DICT_TABLE_MAGIC_N); return(table->n_cols - DATA_N_SYS_COLS); @@ -377,7 +372,6 @@ dict_table_get_n_cols( /*==================*/ const dict_table_t* table) /*!< in: table */ { - ut_ad(table); ut_ad(table->magic_n == DICT_TABLE_MAGIC_N); return(table->n_cols); @@ -1373,7 +1367,6 @@ dict_index_is_corrupted( /*====================*/ const dict_index_t* index) /*!< in: index */ { - ut_ad(index); ut_ad(index->magic_n == DICT_INDEX_MAGIC_N); return((index->type & DICT_CORRUPT) diff --git a/storage/innobase/include/dict0mem.h b/storage/innobase/include/dict0mem.h index 1c674134514..9bbb836663f 100644 --- a/storage/innobase/include/dict0mem.h +++ b/storage/innobase/include/dict0mem.h @@ -133,7 +133,7 @@ allows InnoDB to update_create_info() accordingly. */ + DICT_TF_WIDTH_DATA_DIR) /** A mask of all the known/used bits in table flags */ -#define DICT_TF_BIT_MASK (~(~0 << DICT_TF_BITS)) +#define DICT_TF_BIT_MASK (~(~0U << DICT_TF_BITS)) /** Zero relative shift position of the COMPACT field */ #define DICT_TF_POS_COMPACT 0 diff --git a/storage/innobase/include/dict0stats_bg.h b/storage/innobase/include/dict0stats_bg.h index 82cd2b468b9..1973380d197 100644 --- a/storage/innobase/include/dict0stats_bg.h +++ b/storage/innobase/include/dict0stats_bg.h @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 2012, 2016, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2017, MariaDB Corporation. 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 @@ -32,7 +33,8 @@ Created Apr 26, 2012 Vasil Dimov #include "os0sync.h" /* os_event_t */ #include "os0thread.h" /* DECLARE_THREAD */ -/** Event to wake up the stats thread */ +/** Event to wake up dict_stats_thread on dict_stats_recalc_pool_add() +or shutdown. Not protected by any mutex. */ extern os_event_t dict_stats_event; /*****************************************************************//** diff --git a/storage/innobase/include/dyn0dyn.ic b/storage/innobase/include/dyn0dyn.ic index f18f2e6dff9..13003862638 100644 --- a/storage/innobase/include/dyn0dyn.ic +++ b/storage/innobase/include/dyn0dyn.ic @@ -47,8 +47,6 @@ dyn_block_get_used( /*===============*/ const dyn_block_t* block) /*!< in: dyn array block */ { - ut_ad(block); - return((block->used) & ~DYN_BLOCK_FULL_FLAG); } @@ -76,7 +74,6 @@ dyn_array_create( dyn_array_t* arr) /*!< in/out: memory buffer of size sizeof(dyn_array_t) */ { - ut_ad(arr); #if DYN_ARRAY_DATA_SIZE >= DYN_BLOCK_FULL_FLAG # error "DYN_ARRAY_DATA_SIZE >= DYN_BLOCK_FULL_FLAG" #endif @@ -119,7 +116,6 @@ dyn_array_push( dyn_block_t* block; ulint used; - ut_ad(arr); ut_ad(arr->magic_n == DYN_BLOCK_MAGIC_N); ut_ad(size <= DYN_ARRAY_DATA_SIZE); ut_ad(size); @@ -159,7 +155,6 @@ dyn_array_open( { dyn_block_t* block; - ut_ad(arr); ut_ad(arr->magic_n == DYN_BLOCK_MAGIC_N); ut_ad(size <= DYN_ARRAY_DATA_SIZE); ut_ad(size); @@ -195,7 +190,6 @@ dyn_array_close( { dyn_block_t* block; - ut_ad(arr); ut_ad(arr->magic_n == DYN_BLOCK_MAGIC_N); block = dyn_array_get_last_block(arr); @@ -222,7 +216,6 @@ dyn_array_get_element( { const dyn_block_t* block; - ut_ad(arr); ut_ad(arr->magic_n == DYN_BLOCK_MAGIC_N); /* Get the first array block */ @@ -260,7 +253,6 @@ dyn_array_get_data_size( const dyn_block_t* block; ulint sum = 0; - ut_ad(arr); ut_ad(arr->magic_n == DYN_BLOCK_MAGIC_N); if (arr->heap == NULL) { diff --git a/storage/innobase/include/fil0fil.h b/storage/innobase/include/fil0fil.h index 35a4b2496d4..f29d1bf3408 100644 --- a/storage/innobase/include/fil0fil.h +++ b/storage/innobase/include/fil0fil.h @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1995, 2016, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2017, MariaDB Corporation. 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 @@ -206,7 +207,9 @@ struct fil_node_t { ibool open; /*!< TRUE if file open */ os_file_t handle; /*!< OS handle to the file, if file open */ os_event_t sync_event;/*!< Condition event to group and - serialize calls to fsync */ + serialize calls to fsync; + os_event_set() and os_event_reset() + are protected by fil_system_t::mutex */ ibool is_raw_disk;/*!< TRUE if the 'file' is actually a raw device or a raw disk partition */ ulint size; /*!< size of the file in database pages, 0 if diff --git a/storage/innobase/include/fsp0fsp.h b/storage/innobase/include/fsp0fsp.h index 099cb8edc14..0097537e4d6 100644 --- a/storage/innobase/include/fsp0fsp.h +++ b/storage/innobase/include/fsp0fsp.h @@ -61,7 +61,7 @@ is found in a remote location, not the default data directory. */ + FSP_FLAGS_WIDTH_DATA_DIR) /** A mask of all the known/used bits in tablespace flags */ -#define FSP_FLAGS_MASK (~(~0 << FSP_FLAGS_WIDTH)) +#define FSP_FLAGS_MASK (~(~0U << FSP_FLAGS_WIDTH)) /** Zero relative shift position of the POST_ANTELOPE field */ #define FSP_FLAGS_POS_POST_ANTELOPE 0 diff --git a/storage/innobase/include/fsp0types.h b/storage/innobase/include/fsp0types.h index 94fd908ab0c..9734f4c8abc 100644 --- a/storage/innobase/include/fsp0types.h +++ b/storage/innobase/include/fsp0types.h @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1995, 2009, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2017, MariaDB Corporation. 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 diff --git a/storage/innobase/include/fts0fts.h b/storage/innobase/include/fts0fts.h index 3e2f359bbeb..7aa7055640c 100644 --- a/storage/innobase/include/fts0fts.h +++ b/storage/innobase/include/fts0fts.h @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 2011, 2016, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2016, MariaDB Corporation. 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 @@ -366,8 +367,8 @@ extern ulong fts_min_token_size; need a sync to free some memory */ extern bool fts_need_sync; -/** Maximum possible Fulltext word length */ -#define FTS_MAX_WORD_LEN HA_FT_MAXBYTELEN +/** Maximum possible Fulltext word length in bytes (assuming mbmaxlen=4) */ +#define FTS_MAX_WORD_LEN (HA_FT_MAXCHARLEN * 4) /** Maximum possible Fulltext word length (in characters) */ #define FTS_MAX_WORD_LEN_IN_CHAR HA_FT_MAXCHARLEN diff --git a/storage/innobase/include/fts0types.h b/storage/innobase/include/fts0types.h index e495fe72a60..0dad75d8f1b 100644 --- a/storage/innobase/include/fts0types.h +++ b/storage/innobase/include/fts0types.h @@ -126,7 +126,9 @@ struct fts_sync_t { bool in_progress; /*!< flag whether sync is in progress.*/ bool unlock_cache; /*!< flag whether unlock cache when write fts node */ - os_event_t event; /*!< sync finish event */ + os_event_t event; /*!< sync finish event; + only os_event_set() and os_event_wait() + are used */ }; /** The cache for the FTS system. It is a memory-based inverted index diff --git a/storage/innobase/include/lock0lock.h b/storage/innobase/include/lock0lock.h index c68651aa03b..2547097a14b 100644 --- a/storage/innobase/include/lock0lock.h +++ b/storage/innobase/include/lock0lock.h @@ -928,7 +928,12 @@ struct lock_sys_t{ srv_slot_t* waiting_threads; /*!< Array of user threads suspended while waiting for locks within InnoDB, protected - by the lock_sys->wait_mutex */ + by the lock_sys->wait_mutex; + os_event_set() and + os_event_reset() on + waiting_threads[]->event + are protected by + trx_t::mutex */ srv_slot_t* last_slot; /*!< highest slot ever used in the waiting_threads array, protected by @@ -941,10 +946,11 @@ struct lock_sys_t{ ulint n_lock_max_wait_time; /*!< Max wait time */ - os_event_t timeout_event; /*!< Set to the event that is - created in the lock wait monitor - thread. A value of 0 means the - thread is not active */ + os_event_t timeout_event; /*!< An event waited for by + lock_wait_timeout_thread. + Not protected by a mutex, + but the waits are timed. + Signaled on shutdown only. */ bool timeout_thread_active; /*!< True if the timeout thread is running */ diff --git a/storage/innobase/include/log0log.h b/storage/innobase/include/log0log.h index ad9710b1870..44074a61952 100644 --- a/storage/innobase/include/log0log.h +++ b/storage/innobase/include/log0log.h @@ -2,6 +2,7 @@ Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved. Copyright (c) 2009, Google Inc. +Copyright (c) 2017, MariaDB Corporation. All Rights Reserved. Portions of this file contain modifications contributed and copyrighted by Google, Inc. Those modifications are gratefully acknowledged and are described @@ -875,10 +876,8 @@ struct log_t{ be 'flush_or_write'! */ os_event_t no_flush_event; /*!< this event is in the reset state when a flush or a write is running; - a thread should wait for this without - owning the log mutex, but NOTE that - to set or reset this event, the - thread MUST own the log mutex! */ + os_event_set() and os_event_reset() + are protected by log_sys_t::mutex */ ibool one_flushed; /*!< during a flush, this is first FALSE and becomes TRUE when one log group has been @@ -887,11 +886,9 @@ struct log_t{ flush or write has not yet completed for any log group; e.g., this means that a transaction has been committed - when this is set; a thread should wait - for this without owning the log mutex, - but NOTE that to set or reset this - event, the thread MUST own the log - mutex! */ + when this is set; + os_event_set() and os_event_reset() + are protected by log_sys_t::mutex */ ulint n_log_ios; /*!< number of log i/os initiated thus far */ ulint n_log_ios_old; /*!< number of log i/o's at the @@ -976,9 +973,9 @@ struct log_t{ ulint archive_buf_size;/*!< size of archive_buf */ byte* archive_buf; /*!< log segment is written to the archive from this buffer */ - os_event_t archiving_on; /*!< if archiving has been stopped, - a thread can wait for this event to - become signaled */ + os_event_t archiving_on; /*!< if archiving has been stopped; + os_event_set() and os_event_reset() + are protected by log_sys_t::mutex */ /* @} */ #endif /* UNIV_LOG_ARCHIVE */ }; diff --git a/storage/innobase/include/mach0data.ic b/storage/innobase/include/mach0data.ic index c46fcec107e..6b303979b18 100644 --- a/storage/innobase/include/mach0data.ic +++ b/storage/innobase/include/mach0data.ic @@ -52,7 +52,6 @@ mach_read_from_1( /*=============*/ const byte* b) /*!< in: pointer to byte */ { - ut_ad(b); return((ulint)(b[0])); } @@ -149,7 +148,6 @@ mach_read_from_3( /*=============*/ const byte* b) /*!< in: pointer to 3 bytes */ { - ut_ad(b); return( ((ulint)(b[0]) << 16) | ((ulint)(b[1]) << 8) | (ulint)(b[2]) @@ -186,7 +184,6 @@ mach_read_from_4( /*=============*/ const byte* b) /*!< in: pointer to four bytes */ { - ut_ad(b); return( ((ulint)(b[0]) << 24) | ((ulint)(b[1]) << 16) | ((ulint)(b[2]) << 8) @@ -265,8 +262,6 @@ mach_read_compressed( { ulint flag; - ut_ad(b); - flag = mach_read_from_1(b); if (flag < 0x80UL) { @@ -347,8 +342,6 @@ mach_read_from_7( /*=============*/ const byte* b) /*!< in: pointer to 7 bytes */ { - ut_ad(b); - return(ut_ull_create(mach_read_from_3(b), mach_read_from_4(b + 3))); } @@ -378,8 +371,6 @@ mach_read_from_6( /*=============*/ const byte* b) /*!< in: pointer to 6 bytes */ { - ut_ad(b); - return(ut_ull_create(mach_read_from_2(b), mach_read_from_4(b + 2))); } @@ -427,8 +418,6 @@ mach_ull_read_compressed( ib_uint64_t n; ulint size; - ut_ad(b); - n = (ib_uint64_t) mach_read_compressed(b); size = mach_get_compressed_size((ulint) n); @@ -494,8 +483,6 @@ mach_ull_read_much_compressed( ib_uint64_t n; ulint size; - ut_ad(b); - if (*b != (byte)0xFF) { n = 0; size = 0; diff --git a/storage/innobase/include/os0file.h b/storage/innobase/include/os0file.h index e7fa1926bc7..6cb4f54d629 100644 --- a/storage/innobase/include/os0file.h +++ b/storage/innobase/include/os0file.h @@ -2,6 +2,7 @@ Copyright (c) 1995, 2016, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2009, Percona Inc. +Copyright (c) 2017, MariaDB Corporation. All Rights Reserved. Portions of this file contain modifications contributed and copyrighted by Percona Inc.. Those modifications are @@ -1143,6 +1144,7 @@ UNIV_INTERN void os_aio_simulated_wake_handler_threads(void); /*=======================================*/ +#ifdef _WIN32 /**********************************************************************//** This function can be called if one wants to post a batch of reads and prefers an i/o-handler thread to handle them all at once later. You must @@ -1150,8 +1152,10 @@ call os_aio_simulated_wake_handler_threads later to ensure the threads are not left sleeping! */ UNIV_INTERN void -os_aio_simulated_put_read_threads_to_sleep(void); -/*============================================*/ +os_aio_simulated_put_read_threads_to_sleep(); +#else /* _WIN32 */ +# define os_aio_simulated_put_read_threads_to_sleep() +#endif /* _WIN32 */ #ifdef WIN_ASYNC_IO /**********************************************************************//** diff --git a/storage/innobase/include/page0page.ic b/storage/innobase/include/page0page.ic index 99e17001c0a..a4d3bd40426 100644 --- a/storage/innobase/include/page0page.ic +++ b/storage/innobase/include/page0page.ic @@ -160,7 +160,6 @@ page_header_get_offs( { ulint offs; - ut_ad(page); ut_ad((field == PAGE_FREE) || (field == PAGE_LAST_INSERT) || (field == PAGE_HEAP_TOP)); diff --git a/storage/innobase/include/srv0srv.h b/storage/innobase/include/srv0srv.h index 4b9b8281d04..7795cf9b309 100644 --- a/storage/innobase/include/srv0srv.h +++ b/storage/innobase/include/srv0srv.h @@ -3,7 +3,7 @@ Copyright (c) 1995, 2016, Oracle and/or its affiliates. All rights reserved. Copyright (c) 2008, 2009, Google Inc. Copyright (c) 2009, Percona Inc. -Copyright (c) 2013, 2014, SkySQL Ab. All Rights Reserved. +Copyright (c) 2013, 2017, MariaDB Corporation Ab. All Rights Reserved. Portions of this file contain modifications contributed and copyrighted by Google, Inc. Those modifications are gratefully acknowledged and are described @@ -145,13 +145,16 @@ extern const char* srv_main_thread_op_info; /** Prefix used by MySQL to indicate pre-5.1 table name encoding */ extern const char srv_mysql50_table_name_prefix[10]; -/* The monitor thread waits on this event. */ +/** Event to signal srv_monitor_thread. Not protected by a mutex. +Set after setting srv_print_innodb_monitor. */ extern os_event_t srv_monitor_event; -/* The error monitor thread waits on this event. */ +/** Event to signal the shutdown of srv_error_monitor_thread. +Not protected by a mutex. */ extern os_event_t srv_error_event; -/** The buffer pool dump/load thread waits on this event. */ +/** Event for waking up buf_dump_thread. Not protected by a mutex. +Set on shutdown or by buf_dump_start() or buf_load_start(). */ extern os_event_t srv_buf_dump_event; /** The buffer pool dump/load file name */ @@ -349,9 +352,6 @@ extern double srv_adaptive_flushing_lwm; extern ulong srv_flushing_avg_loops; extern ulong srv_force_recovery; -#ifndef DBUG_OFF -extern ulong srv_force_recovery_crash; -#endif /* !DBUG_OFF */ extern ulint srv_fast_shutdown; /*!< If this is 1, do not do a purge and index buffer merge. @@ -785,17 +785,12 @@ ulint srv_get_task_queue_length(void); /*===========================*/ -/*********************************************************************//** -Releases threads of the type given from suspension in the thread table. -NOTE! The server mutex has to be reserved by the caller! -@return number of threads released: this may be less than n if not -enough threads were suspended at the moment */ -UNIV_INTERN -ulint -srv_release_threads( -/*================*/ - enum srv_thread_type type, /*!< in: thread type */ - ulint n); /*!< in: number of threads to release */ +/** Ensure that a given number of threads of the type given are running +(or are already terminated). +@param[in] type thread type +@param[in] n number of threads that have to run */ +void +srv_release_threads(enum srv_thread_type type, ulint n); /**********************************************************************//** Check whether any background thread are active. If so print which thread @@ -806,12 +801,10 @@ const char* srv_any_background_threads_are_active(void); /*=======================================*/ -/**********************************************************************//** -Wakeup the purge threads. */ +/** Wake up the purge threads. */ UNIV_INTERN void -srv_purge_wakeup(void); -/*==================*/ +srv_purge_wakeup(); /** Status variables to be passed to MySQL */ struct export_var_t{ diff --git a/storage/innobase/include/trx0purge.h b/storage/innobase/include/trx0purge.h index 1e13c883800..f72652963c9 100644 --- a/storage/innobase/include/trx0purge.h +++ b/storage/innobase/include/trx0purge.h @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2011, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2017, MariaDB Corporation. 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 @@ -145,7 +146,10 @@ struct trx_purge_t{ log operation can prevent this by obtaining an s-latch here. It also protects state and running */ - os_event_t event; /*!< State signal event */ + os_event_t event; /*!< State signal event; + os_event_set() and os_event_reset() + are protected by trx_purge_t::latch + X-lock */ ulint n_stop; /*!< Counter to track number stops */ volatile bool running; /*!< true, if purge is active, we check this without the latch too */ diff --git a/storage/innobase/include/ut0wqueue.h b/storage/innobase/include/ut0wqueue.h index 33385ddf2d4..09b2eb717a7 100644 --- a/storage/innobase/include/ut0wqueue.h +++ b/storage/innobase/include/ut0wqueue.h @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 2006, 2009, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2017, MariaDB Corporation. 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 @@ -99,7 +100,9 @@ ib_wqueue_timedwait( struct ib_wqueue_t { ib_mutex_t mutex; /*!< mutex protecting everything */ ib_list_t* items; /*!< work item list */ - os_event_t event; /*!< event we use to signal additions to list */ + os_event_t event; /*!< event we use to signal additions to list; + os_event_set() and os_event_reset() are + protected by ib_wqueue_t::mutex */ }; #endif diff --git a/storage/innobase/log/log0log.cc b/storage/innobase/log/log0log.cc index 8f2af1b0454..57516d7c8f4 100644 --- a/storage/innobase/log/log0log.cc +++ b/storage/innobase/log/log0log.cc @@ -2,6 +2,7 @@ Copyright (c) 1995, 2016, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2009, Google Inc. +Copyright (c) 2017, MariaDB Corporation. All Rights Reserved. Portions of this file contain modifications contributed and copyrighted by Google, Inc. Those modifications are gratefully acknowledged and are described @@ -1459,17 +1460,7 @@ log_write_up_to( } loop: -#ifdef UNIV_DEBUG - loop_count++; - - ut_ad(loop_count < 5); - -# if 0 - if (loop_count > 2) { - fprintf(stderr, "Log loop count %lu\n", loop_count); - } -# endif -#endif + ut_ad(++loop_count < 100); mutex_enter(&(log_sys->mutex)); ut_ad(!recv_no_log_write); @@ -3204,7 +3195,6 @@ logs_empty_and_mark_files_at_shutdown(void) lsn_t lsn; ulint arch_log_no; ulint count = 0; - ulint total_trx; ulint pending_io; enum srv_thread_type active_thd; const char* thread_name; @@ -3251,10 +3241,9 @@ loop: shutdown, because the InnoDB layer may have committed or prepared transactions and we don't want to lose them. */ - total_trx = trx_sys_any_active_transactions(); - - if (total_trx > 0) { - + if (ulint total_trx = srv_was_started && !srv_read_only_mode + && srv_force_recovery < SRV_FORCE_NO_TRX_UNDO + ? trx_sys_any_active_transactions() : 0) { if (srv_print_verbose_log && count > 600) { ib_logf(IB_LOG_LEVEL_INFO, "Waiting for %lu active transactions to finish", diff --git a/storage/innobase/log/log0recv.cc b/storage/innobase/log/log0recv.cc index aed94d00834..6137c84b21d 100644 --- a/storage/innobase/log/log0recv.cc +++ b/storage/innobase/log/log0recv.cc @@ -174,7 +174,7 @@ UNIV_INTERN mysql_pfs_key_t recv_writer_mutex_key; # endif /* UNIV_PFS_MUTEX */ /** Flag indicating if recv_writer thread is active. */ -UNIV_INTERN bool recv_writer_thread_active = false; +static volatile bool recv_writer_thread_active; UNIV_INTERN os_thread_t recv_writer_thread_handle = 0; #endif /* !UNIV_HOTBACKUP */ @@ -344,8 +344,6 @@ DECLARE_THREAD(recv_writer_thread)( os_thread_pf(os_thread_get_curr_id())); #endif /* UNIV_DEBUG_THREAD_CREATION */ - recv_writer_thread_active = true; - while (srv_shutdown_state == SRV_SHUTDOWN_NONE) { os_thread_sleep(100000); @@ -2806,11 +2804,10 @@ recv_scan_log_recs( recv_init_crash_recovery(); } else { - - ib_logf(IB_LOG_LEVEL_WARN, - "Recovery skipped, " - "--innodb-read-only set!"); - + ib_logf(IB_LOG_LEVEL_ERROR, + "innodb_read_only prevents" + " crash recovery"); + recv_needed_recovery = TRUE; return(TRUE); } } @@ -2989,6 +2986,7 @@ recv_init_crash_recovery(void) /* Spawn the background thread to flush dirty pages from the buffer pools. */ + recv_writer_thread_active = true; recv_writer_thread_handle = os_thread_create( recv_writer_thread, 0, 0); } @@ -3227,6 +3225,11 @@ recv_recovery_from_checkpoint_start_func( /* Done with startup scan. Clear the flag. */ recv_log_scan_is_startup_type = FALSE; + + if (srv_read_only_mode && recv_needed_recovery) { + return(DB_READ_ONLY); + } + if (TYPE_CHECKPOINT) { /* NOTE: we always do a 'recovery' at startup, but only if there is something wrong we will print a message to the @@ -3377,15 +3380,6 @@ void recv_recovery_from_checkpoint_finish(void) /*======================================*/ { - /* Apply the hashed log records to the respective file pages */ - - if (srv_force_recovery < SRV_FORCE_NO_LOG_REDO) { - - recv_apply_hashed_log_recs(TRUE); - } - - DBUG_PRINT("ib_log", ("apply completed")); - if (recv_needed_recovery) { trx_sys_print_mysql_master_log_pos(); trx_sys_print_mysql_binlog_offset(); diff --git a/storage/innobase/mtr/mtr0mtr.cc b/storage/innobase/mtr/mtr0mtr.cc index f60ec648ba1..6e87671817e 100644 --- a/storage/innobase/mtr/mtr0mtr.cc +++ b/storage/innobase/mtr/mtr0mtr.cc @@ -309,7 +309,6 @@ mtr_commit( /*=======*/ mtr_t* mtr) /*!< in: mini-transaction */ { - ut_ad(mtr); ut_ad(mtr->magic_n == MTR_MAGIC_N); ut_ad(mtr->state == MTR_ACTIVE); ut_ad(!mtr->inside_ibuf); diff --git a/storage/innobase/os/os0file.cc b/storage/innobase/os/os0file.cc index a9b9cb0fa96..57503fd77f7 100644 --- a/storage/innobase/os/os0file.cc +++ b/storage/innobase/os/os0file.cc @@ -2,6 +2,7 @@ Copyright (c) 1995, 2016, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2009, Percona Inc. +Copyright (c) 2012, 2017, MariaDB Corporation. All Rights Reserved. Portions of this file contain modifications contributed and copyrighted by Percona Inc.. Those modifications are @@ -199,11 +200,15 @@ struct os_aio_array_t{ os_event_t not_full; /*!< The event which is set to the signaled state when there is space in - the aio outside the ibuf segment */ + the aio outside the ibuf segment; + os_event_set() and os_event_reset() + are protected by os_aio_array_t::mutex */ os_event_t is_empty; /*!< The event which is set to the signaled state when there are no - pending i/os in this array */ + pending i/os in this array; + os_event_set() and os_event_reset() + are protected by os_aio_array_t::mutex */ ulint n_slots;/*!< Total number of slots in the aio array. This must be divisible by n_threads. */ @@ -254,8 +259,8 @@ struct os_aio_array_t{ #define OS_AIO_IO_SETUP_RETRY_ATTEMPTS 5 #endif -/** Array of events used in simulated aio */ -static os_event_t* os_aio_segment_wait_events = NULL; +/** Array of events used in simulated aio. */ +static os_event_t* os_aio_segment_wait_events; /** The aio arrays for non-ibuf i/o and ibuf i/o, as well as sync aio. These are NULL when the module has not yet been initialized. @{ */ @@ -2135,48 +2140,52 @@ os_file_set_size( os_file_t file, /*!< in: handle to a file */ os_offset_t size) /*!< in: file size */ { - os_offset_t current_size; ibool ret; byte* buf; byte* buf2; ulint buf_size; - current_size = 0; - #ifdef HAVE_POSIX_FALLOCATE if (srv_use_posix_fallocate) { + int err; + do { + err = posix_fallocate(file, 0, size); + } while (err == EINTR + && srv_shutdown_state == SRV_SHUTDOWN_NONE); - if (posix_fallocate(file, current_size, size) == -1) { - - fprintf(stderr, "InnoDB: Error: preallocating file " - "space for file \'%s\' failed. Current size " - "%lu, desired size %lu\n", - name, (long unsigned) current_size, (long unsigned) size); - os_file_handle_error_no_exit(name, "posix_fallocate", FALSE); - return(FALSE); + if (err) { + ib_logf(IB_LOG_LEVEL_ERROR, + "preallocating " INT64PF " bytes for" + "file %s failed with error %d", + size, name, err); } - return(TRUE); + return(!err); } #endif - +#ifdef _WIN32 + /* Write 1 page of zeroes at the desired end. */ + buf_size = UNIV_PAGE_SIZE; + os_offset_t current_size = size - buf_size; +#else /* Write up to 1 megabyte at a time. */ buf_size = ut_min(64, (ulint) (size / UNIV_PAGE_SIZE)) * UNIV_PAGE_SIZE; - buf2 = static_cast<byte*>(ut_malloc(buf_size + UNIV_PAGE_SIZE)); + os_offset_t current_size = 0; +#endif + buf2 = static_cast<byte*>(calloc(1, buf_size + UNIV_PAGE_SIZE)); + + if (!buf2) { + ib_logf(IB_LOG_LEVEL_ERROR, + "Cannot allocate " ULINTPF " bytes to extend file\n", + buf_size + UNIV_PAGE_SIZE); + return(FALSE); + } /* Align the buffer for possible raw i/o */ buf = static_cast<byte*>(ut_align(buf2, UNIV_PAGE_SIZE)); - /* Write buffer full of zeros */ - memset(buf, 0, buf_size); - - if (size >= (os_offset_t) 100 << 20) { - - fprintf(stderr, "InnoDB: Progress in MB:"); - } - - while (current_size < size) { + do { ulint n_bytes; if (size - current_size < (os_offset_t) buf_size) { @@ -2187,37 +2196,15 @@ os_file_set_size( ret = os_file_write(name, file, buf, current_size, n_bytes); if (!ret) { - ut_free(buf2); - goto error_handling; - } - - /* Print about progress for each 100 MB written */ - if ((current_size + n_bytes) / (100 << 20) - != current_size / (100 << 20)) { - - fprintf(stderr, " %lu00", - (ulong) ((current_size + n_bytes) - / (100 << 20))); + break; } current_size += n_bytes; - } - - if (size >= (os_offset_t) 100 << 20) { - - fprintf(stderr, "\n"); - } - - ut_free(buf2); - - ret = os_file_flush(file); + } while (current_size < size); - if (ret) { - return(TRUE); - } + free(buf2); -error_handling: - return(FALSE); + return(ret && os_file_flush(file)); } /***********************************************************************//** @@ -4028,6 +4015,12 @@ os_aio_init( os_aio_validate(); + os_last_printout = ut_time(); + + if (srv_use_native_aio) { + return(TRUE); + } + os_aio_segment_wait_events = static_cast<os_event_t*>( ut_malloc(n_segments * sizeof *os_aio_segment_wait_events)); @@ -4035,10 +4028,7 @@ os_aio_init( os_aio_segment_wait_events[i] = os_event_create(); } - os_last_printout = ut_time(); - return(TRUE); - } /*********************************************************************** @@ -4066,8 +4056,10 @@ os_aio_free(void) os_aio_array_free(os_aio_read_array); - for (ulint i = 0; i < os_aio_n_segments; i++) { - os_event_free(os_aio_segment_wait_events[i]); + if (!srv_use_native_aio) { + for (ulint i = 0; i < os_aio_n_segments; i++) { + os_event_free(os_aio_segment_wait_events[i]); + } } ut_free(os_aio_segment_wait_events); @@ -4116,22 +4108,17 @@ os_aio_wake_all_threads_at_shutdown(void) if (os_aio_log_array != 0) { os_aio_array_wake_win_aio_at_shutdown(os_aio_log_array); } - #elif defined(LINUX_NATIVE_AIO) - /* When using native AIO interface the io helper threads wait on io_getevents with a timeout value of 500ms. At each wake up these threads check the server status. No need to do anything to wake them up. */ +#endif /* !WIN_ASYNC_AIO */ if (srv_use_native_aio) { return; } - /* Fall through to simulated AIO handler wakeup if we are - not using native AIO. */ -#endif /* !WIN_ASYNC_AIO */ - /* This loop wakes up all simulated ai/o threads */ for (ulint i = 0; i < os_aio_n_segments; i++) { @@ -4500,6 +4487,7 @@ os_aio_simulated_wake_handler_threads(void) } } +#ifdef _WIN32 /**********************************************************************//** This function can be called if one wants to post a batch of reads and prefers an i/o-handler thread to handle them all at once later. You must @@ -4507,15 +4495,14 @@ call os_aio_simulated_wake_handler_threads later to ensure the threads are not left sleeping! */ UNIV_INTERN void -os_aio_simulated_put_read_threads_to_sleep(void) -/*============================================*/ +os_aio_simulated_put_read_threads_to_sleep() { /* The idea of putting background IO threads to sleep is only for Windows when using simulated AIO. Windows XP seems to schedule background threads too eagerly to allow for coalescing during readahead requests. */ -#ifdef __WIN__ + os_aio_array_t* array; if (srv_use_native_aio) { @@ -4534,8 +4521,8 @@ readahead requests. */ os_event_reset(os_aio_segment_wait_events[i]); } } -#endif /* __WIN__ */ } +#endif /* _WIN32 */ #if defined(LINUX_NATIVE_AIO) /*******************************************************************//** @@ -5745,11 +5732,12 @@ os_aio_print( srv_io_thread_op_info[i], srv_io_thread_function[i]); -#ifndef __WIN__ - if (os_aio_segment_wait_events[i]->is_set) { +#ifndef _WIN32 + if (!srv_use_native_aio + && os_aio_segment_wait_events[i]->is_set) { fprintf(file, " ev set"); } -#endif /* __WIN__ */ +#endif /* _WIN32 */ fprintf(file, "\n"); } diff --git a/storage/innobase/page/page0page.cc b/storage/innobase/page/page0page.cc index 95b8db0ccc8..090848a4525 100644 --- a/storage/innobase/page/page0page.cc +++ b/storage/innobase/page/page0page.cc @@ -1442,7 +1442,6 @@ page_dir_split_slot( ulint i; ulint n_owned; - ut_ad(page); ut_ad(!page_zip || page_is_comp(page)); ut_ad(slot_no > 0); @@ -1504,7 +1503,6 @@ page_dir_balance_slot( rec_t* old_rec; rec_t* new_rec; - ut_ad(page); ut_ad(!page_zip || page_is_comp(page)); ut_ad(slot_no > 0); diff --git a/storage/innobase/page/page0zip.cc b/storage/innobase/page/page0zip.cc index e7d545ddb52..e8a147aab47 100644 --- a/storage/innobase/page/page0zip.cc +++ b/storage/innobase/page/page0zip.cc @@ -4807,8 +4807,6 @@ page_zip_parse_compress( ulint size; ulint trailer_size; - ut_ad(ptr != NULL); - ut_ad(end_ptr != NULL); ut_ad(!page == !page_zip); if (UNIV_UNLIKELY(ptr + (2 + 2) > end_ptr)) { diff --git a/storage/innobase/row/row0ftsort.cc b/storage/innobase/row/row0ftsort.cc index e4a88047955..4865c8e850a 100644 --- a/storage/innobase/row/row0ftsort.cc +++ b/storage/innobase/row/row0ftsort.cc @@ -57,6 +57,8 @@ tokenized doc string. The index has three "fields": integer value) 3) Word's position in original doc. +@see fts_create_one_index_table() + @return dict_index_t structure for the fts sort index */ UNIV_INTERN dict_index_t* @@ -96,16 +98,12 @@ row_merge_create_fts_sort_index( field->prefix_len = 0; field->col = static_cast<dict_col_t*>( mem_heap_alloc(new_index->heap, sizeof(dict_col_t))); - field->col->len = FTS_MAX_WORD_LEN; - - if (strcmp(charset->name, "latin1_swedish_ci") == 0) { - field->col->mtype = DATA_VARCHAR; - } else { - field->col->mtype = DATA_VARMYSQL; - } - field->col->prtype = idx_field->col->prtype | DATA_NOT_NULL; + field->col->mtype = charset == &my_charset_latin1 + ? DATA_VARCHAR : DATA_VARMYSQL; field->col->mbminmaxlen = idx_field->col->mbminmaxlen; + field->col->len = HA_FT_MAXCHARLEN * DATA_MBMAXLEN(field->col->mbminmaxlen); + field->fixed_len = 0; /* Doc ID */ @@ -359,6 +357,7 @@ row_fts_free_pll_merge_buf( /*********************************************************************//** Tokenize incoming text data and add to the sort buffer. +@see row_merge_buf_encode() @return TRUE if the record passed, FALSE if out of space */ static ibool @@ -367,8 +366,6 @@ row_merge_fts_doc_tokenize( row_merge_buf_t** sort_buf, /*!< in/out: sort buffer */ doc_id_t doc_id, /*!< in: Doc ID */ fts_doc_t* doc, /*!< in: Doc to be tokenized */ - dtype_t* word_dtype, /*!< in: data structure for - word col */ merge_file_t** merge_file, /*!< in/out: merge file */ ibool opt_doc_id_size,/*!< in: whether to use 4 bytes instead of 8 bytes integer to @@ -400,7 +397,7 @@ row_merge_fts_doc_tokenize( ulint idx = 0; ib_uint32_t position; ulint offset = 0; - ulint cur_len = 0; + ulint cur_len; doc_id_t write_doc_id; inc = innobase_mysql_fts_get_token( @@ -454,14 +451,34 @@ row_merge_fts_doc_tokenize( dfield_set_data(field, t_str.f_str, t_str.f_len); len = dfield_get_len(field); - field->type.mtype = word_dtype->mtype; - field->type.prtype = word_dtype->prtype | DATA_NOT_NULL; + dict_col_copy_type(dict_index_get_nth_col(buf->index, 0), &field->type); + field->type.prtype |= DATA_NOT_NULL; + ut_ad(len <= field->type.len); - /* Variable length field, set to max size. */ - field->type.len = FTS_MAX_WORD_LEN; - field->type.mbminmaxlen = word_dtype->mbminmaxlen; + /* For the temporary file, row_merge_buf_encode() uses + 1 byte for representing the number of extra_size bytes. + This number will always be 1, because for this 3-field index + consisting of one variable-size column, extra_size will always + be 1 or 2, which can be encoded in one byte. + + The extra_size is 1 byte if the length of the + variable-length column is less than 128 bytes or the + maximum length is less than 256 bytes. */ + + /* One variable length column, word with its lenght less than + fts_max_token_size, add one extra size and one extra byte. + + Since the max length for FTS token now is larger than 255, + so we will need to signify length byte itself, so only 1 to 128 + bytes can be used for 1 bytes, larger than that 2 bytes. */ + if (len < 128 || field->type.len < 256) { + /* Extra size is one byte. */ + cur_len = 2 + len; + } else { + /* Extra size is two bytes. */ + cur_len = 3 + len; + } - cur_len += len; dfield_dup(field, buf->heap); field++; @@ -511,20 +528,6 @@ row_merge_fts_doc_tokenize( cur_len += len; dfield_dup(field, buf->heap); - /* One variable length column, word with its lenght less than - fts_max_token_size, add one extra size and one extra byte. - - Since the max length for FTS token now is larger than 255, - so we will need to signify length byte itself, so only 1 to 128 - bytes can be used for 1 bytes, larger than that 2 bytes. */ - if (t_str.f_len < 128) { - /* Extra size is one byte. */ - cur_len += 2; - } else { - /* Extra size is two bytes. */ - cur_len += 3; - } - /* Reserve one byte for the end marker of row_merge_block_t. */ if (buf->total_size + data_size[idx] + cur_len >= srv_sort_buf_size - 1) { @@ -617,8 +620,6 @@ fts_parallel_tokenization( mem_heap_t* blob_heap = NULL; fts_doc_t doc; dict_table_t* table = psort_info->psort_common->new_table; - dtype_t word_dtype; - dict_field_t* idx_field; fts_tokenize_ctx_t t_ctx; ulint retried = 0; dberr_t error = DB_SUCCESS; @@ -640,13 +641,6 @@ fts_parallel_tokenization( doc.charset = fts_index_get_charset( psort_info->psort_common->dup->index); - idx_field = dict_index_get_nth_field( - psort_info->psort_common->dup->index, 0); - word_dtype.prtype = idx_field->col->prtype; - word_dtype.mbminmaxlen = idx_field->col->mbminmaxlen; - word_dtype.mtype = (strcmp(doc.charset->name, "latin1_swedish_ci") == 0) - ? DATA_VARCHAR : DATA_VARMYSQL; - block = psort_info->merge_block; zip_size = dict_table_zip_size(table); @@ -696,7 +690,6 @@ loop: processed = row_merge_fts_doc_tokenize( buf, doc_item->doc_id, &doc, - &word_dtype, merge_file, psort_info->psort_common->opt_doc_id_size, &t_ctx); diff --git a/storage/innobase/row/row0merge.cc b/storage/innobase/row/row0merge.cc index 9518712e160..f214633e9ce 100644 --- a/storage/innobase/row/row0merge.cc +++ b/storage/innobase/row/row0merge.cc @@ -944,14 +944,8 @@ row_merge_read_rec( ulint data_size; ulint avail_size; - ut_ad(block); - ut_ad(buf); ut_ad(b >= &block[0]); ut_ad(b < &block[srv_sort_buf_size]); - ut_ad(index); - ut_ad(foffs); - ut_ad(mrec); - ut_ad(offsets); ut_ad(*offsets == 1 + REC_OFFS_HEADER_SIZE + dict_index_get_n_fields(index)); diff --git a/storage/innobase/row/row0upd.cc b/storage/innobase/row/row0upd.cc index 8d13d436ab8..720a5c049b9 100644 --- a/storage/innobase/row/row0upd.cc +++ b/storage/innobase/row/row0upd.cc @@ -1279,8 +1279,6 @@ row_upd_index_replace_new_col_vals_index_pos( ulint n_fields; const ulint zip_size = dict_table_zip_size(index->table); - ut_ad(index); - dtuple_set_info_bits(entry, update->info_bits); if (order_only) { @@ -1465,8 +1463,6 @@ row_upd_changes_ord_field_binary_func( ulint i; const dict_index_t* clust_index; - ut_ad(index); - ut_ad(update); ut_ad(thr); ut_ad(thr->graph); ut_ad(thr->graph->trx); diff --git a/storage/innobase/srv/srv0conc.cc b/storage/innobase/srv/srv0conc.cc index cbf81f8e8f7..f236f9eb9cc 100644 --- a/storage/innobase/srv/srv0conc.cc +++ b/storage/innobase/srv/srv0conc.cc @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 2011, 2012, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2017, MariaDB Corporation. All Rights Reserved. Portions of this file contain modifications contributed and copyrighted by Google, Inc. Those modifications are gratefully acknowledged and are described @@ -81,7 +82,9 @@ typedef UT_LIST_NODE_T(struct srv_conc_slot_t) srv_conc_node_t; /** Slot for a thread waiting in the concurrency control queue. */ struct srv_conc_slot_t{ - os_event_t event; /*!< event to wait */ + os_event_t event; /*!< event to wait for; + os_event_set() and os_event_reset() + are protected by srv_conc_mutex */ ibool reserved; /*!< TRUE if slot reserved */ ibool wait_ended; /*!< TRUE when another thread has @@ -375,11 +378,11 @@ srv_conc_exit_innodb_without_atomics( } } - os_fast_mutex_unlock(&srv_conc_mutex); - if (slot != NULL) { os_event_set(slot->event); } + + os_fast_mutex_unlock(&srv_conc_mutex); } /*********************************************************************//** diff --git a/storage/innobase/srv/srv0srv.cc b/storage/innobase/srv/srv0srv.cc index ae73fe7df96..756138d9ca7 100644 --- a/storage/innobase/srv/srv0srv.cc +++ b/storage/innobase/srv/srv0srv.cc @@ -3,7 +3,7 @@ Copyright (c) 1995, 2016, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2008, 2009 Google Inc. Copyright (c) 2009, Percona Inc. -Copyright (c) 2013, 2014, SkySQL Ab. All Rights Reserved. +Copyright (c) 2013, 2017, MariaDB Corporation Ab. All Rights Reserved. Portions of this file contain modifications contributed and copyrighted by Google, Inc. Those modifications are gratefully acknowledged and are described @@ -321,11 +321,6 @@ starting from SRV_FORCE_IGNORE_CORRUPT, so that data can be recovered by SELECT or mysqldump. When this is nonzero, we do not allow any user modifications to the data. */ UNIV_INTERN ulong srv_force_recovery; -#ifndef DBUG_OFF -/** Inject a crash at different steps of the recovery process. -This is for testing and debugging only. */ -UNIV_INTERN ulong srv_force_recovery_crash; -#endif /* !DBUG_OFF */ /** Print all user-level transactions deadlocks to mysqld stderr */ @@ -604,7 +599,11 @@ struct srv_sys_t{ ulint n_sys_threads; /*!< size of the sys_threads array */ - srv_slot_t* sys_threads; /*!< server thread table */ + srv_slot_t* sys_threads; /*!< server thread table; + os_event_set() and + os_event_reset() on + sys_threads[]->event are + covered by srv_sys_t::mutex */ ulint n_threads_active[SRV_MASTER + 1]; /*!< number of threads active @@ -622,13 +621,16 @@ UNIV_INTERN ib_mutex_t server_mutex; static srv_sys_t* srv_sys = NULL; -/** Event to signal the monitor thread. */ +/** Event to signal srv_monitor_thread. Not protected by a mutex. +Set after setting srv_print_innodb_monitor. */ UNIV_INTERN os_event_t srv_monitor_event; -/** Event to signal the error thread */ +/** Event to signal the shutdown of srv_error_monitor_thread. +Not protected by a mutex. */ UNIV_INTERN os_event_t srv_error_event; -/** Event to signal the buffer pool dump/load thread */ +/** Event for waking up buf_dump_thread. Not protected by a mutex. +Set on shutdown or by buf_dump_start() or buf_load_start(). */ UNIV_INTERN os_event_t srv_buf_dump_event; /** The buffer pool dump/load file name */ @@ -789,7 +791,6 @@ srv_suspend_thread_low( /*===================*/ srv_slot_t* slot) /*!< in/out: thread slot */ { - ut_ad(!srv_read_only_mode); ut_ad(srv_sys_mutex_own()); @@ -847,34 +848,71 @@ srv_suspend_thread( return(sig_count); } -/*********************************************************************//** -Releases threads of the type given from suspension in the thread table. -NOTE! The server mutex has to be reserved by the caller! -@return number of threads released: this may be less than n if not - enough threads were suspended at the moment. */ -UNIV_INTERN -ulint -srv_release_threads( -/*================*/ - srv_thread_type type, /*!< in: thread type */ - ulint n) /*!< in: number of threads to release */ +/** Resume the calling thread. +@param[in,out] slot thread slot +@param[in] sig_count signal count (if wait) +@param[in] wait whether to wait for the event +@param[in] timeout_usec timeout in microseconds (0=infinite) +@return whether the wait timed out */ +static +bool +srv_resume_thread(srv_slot_t* slot, ib_int64_t sig_count = 0, bool wait = true, + ulint timeout_usec = 0) +{ + bool timeout; + + ut_ad(!srv_read_only_mode); + ut_ad(slot->in_use); + ut_ad(slot->suspended); + + if (!wait) { + timeout = false; + } else if (timeout_usec) { + timeout = OS_SYNC_TIME_EXCEEDED == os_event_wait_time_low( + slot->event, timeout_usec, sig_count); + } else { + timeout = false; + os_event_wait_low(slot->event, sig_count); + } + + srv_sys_mutex_enter(); + ut_ad(slot->in_use); + ut_ad(slot->suspended); + + slot->suspended = FALSE; + ++srv_sys->n_threads_active[slot->type]; + srv_sys_mutex_exit(); + return(timeout); +} + +/** Ensure that a given number of threads of the type given are running +(or are already terminated). +@param[in] type thread type +@param[in] n number of threads that have to run */ +void +srv_release_threads(enum srv_thread_type type, ulint n) { - ulint i; - ulint count = 0; + ulint running; ut_ad(srv_thread_type_validate(type)); ut_ad(n > 0); - srv_sys_mutex_enter(); + do { + running = 0; - for (i = 0; i < srv_sys->n_sys_threads; i++) { - srv_slot_t* slot; + srv_sys_mutex_enter(); - slot = &srv_sys->sys_threads[i]; + for (ulint i = 0; i < srv_sys->n_sys_threads; i++) { + srv_slot_t* slot = &srv_sys->sys_threads[i]; - if (slot->in_use - && srv_slot_get_type(slot) == type - && slot->suspended) { + if (!slot->in_use || srv_slot_get_type(slot) != type) { + continue; + } else if (!slot->suspended) { + if (++running >= n) { + break; + } + continue; + } switch (type) { case SRV_NONE: @@ -904,21 +942,11 @@ srv_release_threads( break; } - slot->suspended = FALSE; - - ++srv_sys->n_threads_active[type]; - os_event_set(slot->event); - - if (++count == n) { - break; - } } - } - - srv_sys_mutex_exit(); - return(count); + srv_sys_mutex_exit(); + } while (running && running < n); } /*********************************************************************//** @@ -931,11 +959,8 @@ srv_free_slot( { srv_sys_mutex_enter(); - if (!slot->suspended) { - /* Mark the thread as inactive. */ - srv_suspend_thread_low(slot); - } - + /* Mark the thread as inactive. */ + srv_suspend_thread_low(slot); /* Free the slot for reuse. */ ut_ad(slot->in_use); slot->in_use = FALSE; @@ -1994,15 +2019,7 @@ srv_active_wake_master_thread(void) if (slot->in_use) { ut_a(srv_slot_get_type(slot) == SRV_MASTER); - - if (slot->suspended) { - - slot->suspended = FALSE; - - ++srv_sys->n_threads_active[SRV_MASTER]; - - os_event_set(slot->event); - } + os_event_set(slot->event); } srv_sys_mutex_exit(); @@ -2468,7 +2485,7 @@ suspend_thread: manual also mentions this string in several places. */ srv_main_thread_op_info = "waiting for server activity"; - os_event_wait(slot->event); + srv_resume_thread(slot); if (srv_shutdown_state == SRV_SHUTDOWN_EXIT_THREADS) { os_thread_exit(NULL); @@ -2580,8 +2597,7 @@ DECLARE_THREAD(srv_worker_thread)( do { srv_suspend_thread(slot); - - os_event_wait(slot->event); + srv_resume_thread(slot); if (srv_task_execute()) { @@ -2717,8 +2733,6 @@ srv_purge_coordinator_suspend( ib_int64_t sig_count = srv_suspend_thread(slot); do { - ulint ret; - rw_lock_x_lock(&purge_sys->latch); purge_sys->running = false; @@ -2727,32 +2741,11 @@ srv_purge_coordinator_suspend( /* We don't wait right away on the the non-timed wait because we want to signal the thread that wants to suspend purge. */ - - if (stop) { - os_event_wait_low(slot->event, sig_count); - ret = 0; - } else if (rseg_history_len <= trx_sys->rseg_history_len) { - ret = os_event_wait_time_low( - slot->event, SRV_PURGE_MAX_TIMEOUT, sig_count); - } else { - /* We don't want to waste time waiting, if the - history list increased by the time we got here, - unless purge has been stopped. */ - ret = 0; - } - - srv_sys_mutex_enter(); - - /* The thread can be in state !suspended after the timeout - but before this check if another thread sent a wakeup signal. */ - - if (slot->suspended) { - slot->suspended = FALSE; - ++srv_sys->n_threads_active[slot->type]; - ut_a(srv_sys->n_threads_active[slot->type] == 1); - } - - srv_sys_mutex_exit(); + const bool wait = stop + || rseg_history_len <= trx_sys->rseg_history_len; + const bool timeout = srv_resume_thread( + slot, sig_count, wait, + stop ? 0 : SRV_PURGE_MAX_TIMEOUT); sig_count = srv_suspend_thread(slot); @@ -2764,6 +2757,19 @@ srv_purge_coordinator_suspend( if (!stop) { ut_a(purge_sys->n_stop == 0); purge_sys->running = true; + + if (timeout + && rseg_history_len == trx_sys->rseg_history_len + && trx_sys->rseg_history_len < 5000) { + /* No new records were added since the + wait started. Simply wait for new + records. The magic number 5000 is an + approximation for the case where we + have cached UNDO log records which + prevent truncate of the UNDO + segments. */ + stop = true; + } } else { ut_a(purge_sys->n_stop > 0); @@ -2772,33 +2778,9 @@ srv_purge_coordinator_suspend( } rw_lock_x_unlock(&purge_sys->latch); - - if (ret == OS_SYNC_TIME_EXCEEDED) { - - /* No new records added since wait started then simply - wait for new records. The magic number 5000 is an - approximation for the case where we have cached UNDO - log records which prevent truncate of the UNDO - segments. */ - - if (rseg_history_len == trx_sys->rseg_history_len - && trx_sys->rseg_history_len < 5000) { - - stop = true; - } - } - } while (stop); - srv_sys_mutex_enter(); - - if (slot->suspended) { - slot->suspended = FALSE; - ++srv_sys->n_threads_active[slot->type]; - ut_a(srv_sys->n_threads_active[slot->type] == 1); - } - - srv_sys_mutex_exit(); + srv_resume_thread(slot, 0, false); } /*********************************************************************//** @@ -2851,8 +2833,9 @@ DECLARE_THREAD(srv_purge_coordinator_thread)( srv_purge_coordinator_suspend(slot, rseg_history_len); } + ut_ad(!slot->suspended); + if (srv_purge_should_exit(n_total_purged)) { - ut_a(!slot->suspended); break; } @@ -2963,12 +2946,10 @@ srv_get_task_queue_length(void) return(n_tasks); } -/**********************************************************************//** -Wakeup the purge threads. */ +/** Wake up the purge threads. */ UNIV_INTERN void -srv_purge_wakeup(void) -/*==================*/ +srv_purge_wakeup() { ut_ad(!srv_read_only_mode); @@ -2983,4 +2964,3 @@ srv_purge_wakeup(void) } } } - diff --git a/storage/innobase/srv/srv0start.cc b/storage/innobase/srv/srv0start.cc index 044a087b53c..d1e53d0e0dd 100644 --- a/storage/innobase/srv/srv0start.cc +++ b/storage/innobase/srv/srv0start.cc @@ -3,6 +3,7 @@ Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved. Copyright (c) 2008, Google Inc. Copyright (c) 2009, Percona Inc. +Copyright (c) 2013, 2017, MariaDB Corporation Portions of this file contain modifications contributed and copyrighted by Google, Inc. Those modifications are gratefully acknowledged and are described @@ -582,19 +583,6 @@ create_log_file( /** Initial number of the first redo log file */ #define INIT_LOG_FILE0 (SRV_N_LOG_FILES_MAX + 1) -#ifdef DBUG_OFF -# define RECOVERY_CRASH(x) do {} while(0) -#else -# define RECOVERY_CRASH(x) do { \ - if (srv_force_recovery_crash == x) { \ - fprintf(stderr, "innodb_force_recovery_crash=%lu\n", \ - srv_force_recovery_crash); \ - fflush(stderr); \ - exit(3); \ - } \ -} while (0) -#endif - /*********************************************************************//** Creates all log files. @return DB_SUCCESS or error code */ @@ -635,13 +623,14 @@ create_log_files( file should be recoverable. The buffer pool was clean, and we can simply create all log files from the scratch. */ - RECOVERY_CRASH(6); + DBUG_EXECUTE_IF("innodb_log_abort_6", + return(DB_ERROR);); } } ut_ad(!buf_pool_check_no_pending_io()); - RECOVERY_CRASH(7); + DBUG_EXECUTE_IF("innodb_log_abort_7", return(DB_ERROR);); for (unsigned i = 0; i < srv_n_log_files; i++) { sprintf(logfilename + dirnamelen, @@ -654,7 +643,7 @@ create_log_files( } } - RECOVERY_CRASH(8); + DBUG_EXECUTE_IF("innodb_log_abort_8", return(DB_ERROR);); /* We did not create the first log file initially as ib_logfile0, so that crash recovery cannot find it until it @@ -699,10 +688,16 @@ create_log_files( return(DB_SUCCESS); } -/*********************************************************************//** -Renames the first log file. */ +/** Rename the first redo log file. +@param[in,out] logfilename buffer for the log file name +@param[in] dirnamelen length of the directory path +@param[in] lsn FIL_PAGE_FILE_FLUSH_LSN value +@param[in,out] logfile0 name of the first log file +@return error code +@retval DB_SUCCESS on successful operation */ +MY_ATTRIBUTE((warn_unused_result, nonnull)) static -void +dberr_t create_log_files_rename( /*====================*/ char* logfilename, /*!< in/out: buffer for log file name */ @@ -713,6 +708,9 @@ create_log_files_rename( /* If innodb_flush_method=O_DSYNC, we need to explicitly flush the log buffers. */ fil_flush(SRV_LOG_SPACE_FIRST_ID); + + DBUG_EXECUTE_IF("innodb_log_abort_9", return(DB_ERROR);); + /* Close the log files, so that we can rename the first one. */ fil_close_log_files(false); @@ -721,26 +719,28 @@ create_log_files_rename( checkpoint has been created. */ sprintf(logfilename + dirnamelen, "ib_logfile%u", 0); - RECOVERY_CRASH(9); - ib_logf(IB_LOG_LEVEL_INFO, "Renaming log file %s to %s", logfile0, logfilename); mutex_enter(&log_sys->mutex); ut_ad(strlen(logfile0) == 2 + strlen(logfilename)); - ibool success = os_file_rename( - innodb_file_log_key, logfile0, logfilename); - ut_a(success); - - RECOVERY_CRASH(10); + dberr_t err = os_file_rename( + innodb_file_log_key, logfile0, logfilename) + ? DB_SUCCESS : DB_ERROR; /* Replace the first file with ib_logfile0. */ strcpy(logfile0, logfilename); mutex_exit(&log_sys->mutex); - fil_open_log_and_system_tablespace_files(); + DBUG_EXECUTE_IF("innodb_log_abort_10", err = DB_ERROR;); - ib_logf(IB_LOG_LEVEL_WARN, "New log files created, LSN=" LSN_PF, lsn); + if (err == DB_SUCCESS) { + fil_open_log_and_system_tablespace_files(); + ib_logf(IB_LOG_LEVEL_WARN, + "New log files created, LSN=" LSN_PF, lsn); + } + + return(err); } /*********************************************************************//** @@ -1554,8 +1554,6 @@ innobase_start_or_create_for_mysql(void) ulint max_arch_log_no; #endif /* UNIV_LOG_ARCHIVE */ ulint sum_of_new_sizes; - ulint sum_of_data_file_sizes; - ulint tablespace_size_in_header; dberr_t err; unsigned i; ulint srv_n_log_files_found = srv_n_log_files; @@ -1568,6 +1566,19 @@ innobase_start_or_create_for_mysql(void) size_t dirnamelen; bool sys_datafiles_created = false; + /* Check that os_fast_mutexes work as expected */ + os_fast_mutex_init(PFS_NOT_INSTRUMENTED, &srv_os_test_mutex); + + ut_a(0 == os_fast_mutex_trylock(&srv_os_test_mutex)); + + os_fast_mutex_unlock(&srv_os_test_mutex); + + os_fast_mutex_lock(&srv_os_test_mutex); + + os_fast_mutex_unlock(&srv_os_test_mutex); + + os_fast_mutex_free(&srv_os_test_mutex); + high_level_read_only = srv_read_only_mode || srv_force_recovery > SRV_FORCE_NO_TRX_UNDO; @@ -1610,22 +1621,7 @@ innobase_start_or_create_for_mysql(void) #endif /* PAGE_ATOMIC_REF_COUNT */ ); - - if (sizeof(ulint) != sizeof(void*)) { - ut_print_timestamp(stderr); - fprintf(stderr, - " InnoDB: Error: size of InnoDB's ulint is %lu, " - "but size of void*\n", (ulong) sizeof(ulint)); - ut_print_timestamp(stderr); - fprintf(stderr, - " InnoDB: is %lu. The sizes should be the same " - "so that on a 64-bit\n", - (ulong) sizeof(void*)); - ut_print_timestamp(stderr); - fprintf(stderr, - " InnoDB: platforms you can allocate more than 4 GB " - "of memory.\n"); - } + compile_time_assert(sizeof(ulint) == sizeof(void*)); #ifdef UNIV_DEBUG ut_print_timestamp(stderr); @@ -2203,14 +2199,18 @@ innobase_start_or_create_for_mysql(void) dirnamelen, max_flushed_lsn, logfile0); + if (err == DB_SUCCESS) { + err = create_log_files_rename( + logfilename, + dirnamelen, + max_flushed_lsn, + logfile0); + } + if (err != DB_SUCCESS) { return(err); } - create_log_files_rename( - logfilename, dirnamelen, - max_flushed_lsn, logfile0); - /* Suppress the message about crash recovery. */ max_flushed_lsn = min_flushed_lsn @@ -2336,7 +2336,6 @@ files_checked: trx_sys_create(); if (create_new_db) { - ut_a(!srv_read_only_mode); mtr_start(&mtr); @@ -2379,8 +2378,12 @@ files_checked: fil_flush_file_spaces(FIL_TABLESPACE); - create_log_files_rename(logfilename, dirnamelen, - max_flushed_lsn, logfile0); + err = create_log_files_rename(logfilename, dirnamelen, + max_flushed_lsn, logfile0); + + if (err != DB_SUCCESS) { + return(err); + } #ifdef UNIV_LOG_ARCHIVE } else if (srv_archive_recovery) { @@ -2450,24 +2453,89 @@ files_checked: LOG_CHECKPOINT, LSN_MAX, min_flushed_lsn, max_flushed_lsn); + if (err == DB_SUCCESS) { + /* Initialize the change buffer. */ + err = dict_boot(); + } + if (err != DB_SUCCESS) { + return(err); + } - return(DB_ERROR); + if (!srv_read_only_mode) { + if (sum_of_new_sizes > 0) { + /* New data file(s) were added */ + mtr_start(&mtr); + fsp_header_inc_size(0, sum_of_new_sizes, &mtr); + mtr_commit(&mtr); + /* Immediately write the log record about + increased tablespace size to disk, so that it + is durable even if mysqld would crash + quickly */ + log_buffer_flush_to_disk(); + } } - /* Since the insert buffer init is in dict_boot, and the - insert buffer is needed in any disk i/o, first we call - dict_boot(). Note that trx_sys_init_at_db_start() only needs - to access space 0, and the insert buffer at this stage already - works for space 0. */ + const ulint tablespace_size_in_header + = fsp_header_get_tablespace_size(); - err = dict_boot(); +#ifdef UNIV_DEBUG + /* buf_debug_prints = TRUE; */ +#endif /* UNIV_DEBUG */ + ulint sum_of_data_file_sizes = 0; + + for (ulint d = 0; d < srv_n_data_files; d++) { + sum_of_data_file_sizes += srv_data_file_sizes[d]; + } + + /* Compare the system tablespace file size to what is + stored in FSP_SIZE. In open_or_create_data_files() + we already checked that the file sizes match the + innodb_data_file_path specification. */ + if (srv_read_only_mode + || sum_of_data_file_sizes == tablespace_size_in_header) { + /* Do not complain about the size. */ + } else if (!srv_auto_extend_last_data_file + || sum_of_data_file_sizes + < tablespace_size_in_header) { + ib_logf(IB_LOG_LEVEL_ERROR, + "Tablespace size stored in header is " ULINTPF + " pages, but the sum of data file sizes is " + ULINTPF " pages", + tablespace_size_in_header, + sum_of_data_file_sizes); + + if (srv_force_recovery == 0 + && sum_of_data_file_sizes + < tablespace_size_in_header) { + ib_logf(IB_LOG_LEVEL_ERROR, + "Cannot start InnoDB. The tail of" + " the system tablespace is" + " missing. Have you edited" + " innodb_data_file_path in my.cnf" + " in an inappropriate way, removing" + " data files from there?" + " You can set innodb_force_recovery=1" + " in my.cnf to force" + " a startup if you are trying to" + " recover a badly corrupt database."); - if (err != DB_SUCCESS) { - return(err); + return(DB_ERROR); + } } + /* This must precede recv_apply_hashed_log_recs(TRUE). */ ib_bh = trx_sys_init_at_db_start(); + + if (srv_force_recovery < SRV_FORCE_NO_LOG_REDO) { + /* Apply the hashed log records to the + respective file pages, for the last batch of + recv_group_scan_log_recs(). */ + + recv_apply_hashed_log_recs(TRUE); + DBUG_PRINT("ib_log", ("apply completed")); + } + n_recovered_trx = UT_LIST_GET_LEN(trx_sys->rw_trx_list); /* The purge system needs to create the purge view and @@ -2536,7 +2604,8 @@ files_checked: ULINT_MAX, LSN_MAX, NULL); ut_a(success); - RECOVERY_CRASH(1); + DBUG_EXECUTE_IF("innodb_log_abort_1", + return(DB_ERROR);); min_flushed_lsn = max_flushed_lsn = log_get_lsn(); @@ -2551,8 +2620,6 @@ files_checked: buf_flush_wait_batch_end(NULL, BUF_FLUSH_LIST); - RECOVERY_CRASH(2); - /* Flush the old log files. */ log_buffer_flush_to_disk(); /* If innodb_flush_method=O_DSYNC, @@ -2567,21 +2634,27 @@ files_checked: ut_d(recv_no_log_write = TRUE); ut_ad(!buf_pool_check_no_pending_io()); - RECOVERY_CRASH(3); + DBUG_EXECUTE_IF("innodb_log_abort_3", + return(DB_ERROR);); /* Stamp the LSN to the data files. */ fil_write_flushed_lsn_to_data_files( max_flushed_lsn, 0); - fil_flush_file_spaces(FIL_TABLESPACE); + DBUG_EXECUTE_IF("innodb_log_abort_4", err = DB_ERROR;); - RECOVERY_CRASH(4); + if (err != DB_SUCCESS) { + return(err); + } + + fil_flush_file_spaces(FIL_TABLESPACE); /* Close and free the redo log files, so that we can replace them. */ fil_close_log_files(true); - RECOVERY_CRASH(5); + DBUG_EXECUTE_IF("innodb_log_abort_5", + return(DB_ERROR);); /* Free the old log file space. */ log_group_close_all(); @@ -2595,12 +2668,15 @@ files_checked: dirnamelen, max_flushed_lsn, logfile0); + if (err == DB_SUCCESS) { + err = create_log_files_rename( + logfilename, dirnamelen, + max_flushed_lsn, logfile0); + } + if (err != DB_SUCCESS) { return(err); } - - create_log_files_rename(logfilename, dirnamelen, - max_flushed_lsn, logfile0); } srv_startup_is_before_trx_rollback_phase = FALSE; @@ -2614,20 +2690,8 @@ files_checked: trx_sys_file_format_tag_init(); } - if (!create_new_db && sum_of_new_sizes > 0) { - /* New data file(s) were added */ - mtr_start(&mtr); - - fsp_header_inc_size(0, sum_of_new_sizes, &mtr); - - mtr_commit(&mtr); - - /* Immediately write the log record about increased tablespace - size to disk, so that it is durable even if mysqld would crash - quickly */ - - log_buffer_flush_to_disk(); - } + ut_ad(err == DB_SUCCESS); + ut_a(sum_of_new_sizes != ULINT_UNDEFINED); #ifdef UNIV_LOG_ARCHIVE /* Archiving is always off under MySQL */ @@ -2770,125 +2834,6 @@ files_checked: buf_flush_page_cleaner_thread_started = true; } -#ifdef UNIV_DEBUG - /* buf_debug_prints = TRUE; */ -#endif /* UNIV_DEBUG */ - sum_of_data_file_sizes = 0; - - for (i = 0; i < srv_n_data_files; i++) { - sum_of_data_file_sizes += srv_data_file_sizes[i]; - } - - tablespace_size_in_header = fsp_header_get_tablespace_size(); - - if (!srv_read_only_mode - && !srv_auto_extend_last_data_file - && sum_of_data_file_sizes != tablespace_size_in_header) { - - ut_print_timestamp(stderr); - fprintf(stderr, - " InnoDB: Error: tablespace size" - " stored in header is %lu pages, but\n", - (ulong) tablespace_size_in_header); - ut_print_timestamp(stderr); - fprintf(stderr, - "InnoDB: the sum of data file sizes is %lu pages\n", - (ulong) sum_of_data_file_sizes); - - if (srv_force_recovery == 0 - && sum_of_data_file_sizes < tablespace_size_in_header) { - /* This is a fatal error, the tail of a tablespace is - missing */ - - ut_print_timestamp(stderr); - fprintf(stderr, - " InnoDB: Cannot start InnoDB." - " The tail of the system tablespace is\n"); - ut_print_timestamp(stderr); - fprintf(stderr, - " InnoDB: missing. Have you edited" - " innodb_data_file_path in my.cnf in an\n"); - ut_print_timestamp(stderr); - fprintf(stderr, - " InnoDB: inappropriate way, removing" - " ibdata files from there?\n"); - ut_print_timestamp(stderr); - fprintf(stderr, - " InnoDB: You can set innodb_force_recovery=1" - " in my.cnf to force\n"); - ut_print_timestamp(stderr); - fprintf(stderr, - " InnoDB: a startup if you are trying" - " to recover a badly corrupt database.\n"); - - return(DB_ERROR); - } - } - - if (!srv_read_only_mode - && srv_auto_extend_last_data_file - && sum_of_data_file_sizes < tablespace_size_in_header) { - - ut_print_timestamp(stderr); - fprintf(stderr, - " InnoDB: Error: tablespace size stored in header" - " is %lu pages, but\n", - (ulong) tablespace_size_in_header); - ut_print_timestamp(stderr); - fprintf(stderr, - " InnoDB: the sum of data file sizes" - " is only %lu pages\n", - (ulong) sum_of_data_file_sizes); - - if (srv_force_recovery == 0) { - - ut_print_timestamp(stderr); - fprintf(stderr, - " InnoDB: Cannot start InnoDB. The tail of" - " the system tablespace is\n"); - ut_print_timestamp(stderr); - fprintf(stderr, - " InnoDB: missing. Have you edited" - " innodb_data_file_path in my.cnf in an\n"); - ut_print_timestamp(stderr); - fprintf(stderr, - " InnoDB: inappropriate way, removing" - " ibdata files from there?\n"); - ut_print_timestamp(stderr); - fprintf(stderr, - " InnoDB: You can set innodb_force_recovery=1" - " in my.cnf to force\n"); - ut_print_timestamp(stderr); - fprintf(stderr, - " InnoDB: a startup if you are trying to" - " recover a badly corrupt database.\n"); - - return(DB_ERROR); - } - } - - /* Check that os_fast_mutexes work as expected */ - os_fast_mutex_init(PFS_NOT_INSTRUMENTED, &srv_os_test_mutex); - - if (0 != os_fast_mutex_trylock(&srv_os_test_mutex)) { - ut_print_timestamp(stderr); - fprintf(stderr, - " InnoDB: Error: pthread_mutex_trylock returns" - " an unexpected value on\n"); - ut_print_timestamp(stderr); - fprintf(stderr, - " InnoDB: success! Cannot continue.\n"); - exit(1); - } - - os_fast_mutex_unlock(&srv_os_test_mutex); - - os_fast_mutex_lock(&srv_os_test_mutex); - - os_fast_mutex_unlock(&srv_os_test_mutex); - - os_fast_mutex_free(&srv_os_test_mutex); - if (srv_print_verbose_log) { ib_logf(IB_LOG_LEVEL_INFO, "%s started; log sequence number " LSN_PF "", diff --git a/storage/innobase/sync/sync0sync.cc b/storage/innobase/sync/sync0sync.cc index 2fe40109511..df360d221da 100644 --- a/storage/innobase/sync/sync0sync.cc +++ b/storage/innobase/sync/sync0sync.cc @@ -2,6 +2,7 @@ Copyright (c) 1995, 2016, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2008, Google Inc. +Copyright (c) 2017, MariaDB Corporation. All Rights Reserved. Portions of this file contain modifications contributed and copyrighted by Google, Inc. Those modifications are gratefully acknowledged and are described @@ -1235,7 +1236,7 @@ sync_thread_add_level( case SYNC_TRX_UNDO_PAGE: /* Purge is allowed to read in as many UNDO pages as it likes, there was a bogus rule here earlier that forced the caller to - acquire the purge_sys_t::mutex. The purge mutex did not really + acquire the trx_purge_t::mutex. The purge mutex did not really protect anything because it was only ever acquired by the single purge thread. The purge thread can read the UNDO pages without any covering mutex. */ diff --git a/storage/innobase/trx/trx0purge.cc b/storage/innobase/trx/trx0purge.cc index efc600d16b1..f24c8f22277 100644 --- a/storage/innobase/trx/trx0purge.cc +++ b/storage/innobase/trx/trx0purge.cc @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2017, MariaDB Corporation. 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 @@ -171,10 +172,6 @@ trx_purge_sys_close(void) sess_close(purge_sys->sess); - purge_sys->sess = NULL; - - purge_sys->view = NULL; - rw_lock_free(&purge_sys->latch); mutex_free(&purge_sys->bh_mutex); @@ -183,9 +180,6 @@ trx_purge_sys_close(void) ib_bh_free(purge_sys->ib_bh); os_event_free(purge_sys->event); - - purge_sys->event = NULL; - mem_free(purge_sys); purge_sys = NULL; @@ -1301,20 +1295,16 @@ void trx_purge_stop(void) /*================*/ { - purge_state_t state; - ib_int64_t sig_count = os_event_reset(purge_sys->event); - ut_a(srv_n_purge_threads > 0); rw_lock_x_lock(&purge_sys->latch); - ut_a(purge_sys->state != PURGE_STATE_INIT); - ut_a(purge_sys->state != PURGE_STATE_EXIT); - ut_a(purge_sys->state != PURGE_STATE_DISABLED); + const ib_int64_t sig_count = os_event_reset(purge_sys->event); + const purge_state_t state = purge_sys->state; - ++purge_sys->n_stop; + ut_a(state == PURGE_STATE_RUN || state == PURGE_STATE_STOP); - state = purge_sys->state; + ++purge_sys->n_stop; if (state == PURGE_STATE_RUN) { ib_logf(IB_LOG_LEVEL_INFO, "Stopping purge"); @@ -1327,33 +1317,29 @@ trx_purge_stop(void) purge_sys->state = PURGE_STATE_STOP; - rw_lock_x_unlock(&purge_sys->latch); - if (state != PURGE_STATE_STOP) { - + rw_lock_x_unlock(&purge_sys->latch); /* Wait for purge coordinator to signal that it is suspended. */ os_event_wait_low(purge_sys->event, sig_count); - } else { - bool once = true; - - rw_lock_x_lock(&purge_sys->latch); + } else { + bool once = true; - /* Wait for purge to signal that it has actually stopped. */ - while (purge_sys->running) { + /* Wait for purge to signal that it has actually stopped. */ + while (purge_sys->running) { - if (once) { + if (once) { ib_logf(IB_LOG_LEVEL_INFO, "Waiting for purge to stop"); - once = false; + once = false; } rw_lock_x_unlock(&purge_sys->latch); - os_thread_sleep(10000); + os_thread_sleep(10000); rw_lock_x_lock(&purge_sys->latch); - } + } rw_lock_x_unlock(&purge_sys->latch); } diff --git a/storage/innobase/trx/trx0sys.cc b/storage/innobase/trx/trx0sys.cc index 8ff9180eaf2..67ad96425b1 100644 --- a/storage/innobase/trx/trx0sys.cc +++ b/storage/innobase/trx/trx0sys.cc @@ -1324,7 +1324,9 @@ trx_sys_close(void) ut_a(UT_LIST_GET_LEN(trx_sys->ro_trx_list) == 0); /* Only prepared transactions may be left in the system. Free them. */ - ut_a(UT_LIST_GET_LEN(trx_sys->rw_trx_list) == trx_sys->n_prepared_trx); + ut_a(UT_LIST_GET_LEN(trx_sys->rw_trx_list) == trx_sys->n_prepared_trx + || srv_read_only_mode + || srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO); while ((trx = UT_LIST_GET_FIRST(trx_sys->rw_trx_list)) != NULL) { trx_free_prepared(trx); diff --git a/storage/innobase/trx/trx0trx.cc b/storage/innobase/trx/trx0trx.cc index 3b39f685e6d..bfc1c546510 100644 --- a/storage/innobase/trx/trx0trx.cc +++ b/storage/innobase/trx/trx0trx.cc @@ -310,7 +310,11 @@ trx_free_prepared( /*==============*/ trx_t* trx) /*!< in, own: trx object */ { - ut_a(trx_state_eq(trx, TRX_STATE_PREPARED)); + ut_a(trx_state_eq(trx, TRX_STATE_PREPARED) + || (trx_state_eq(trx, TRX_STATE_ACTIVE) + && trx->is_recovered + && (srv_read_only_mode + || srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO))); ut_a(trx->magic_n == TRX_MAGIC_N); lock_trx_release_locks(trx); diff --git a/storage/innobase/trx/trx0undo.cc b/storage/innobase/trx/trx0undo.cc index cdd23726f2e..220589dd9ff 100644 --- a/storage/innobase/trx/trx0undo.cc +++ b/storage/innobase/trx/trx0undo.cc @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2014, 2017, MariaDB Corporation. 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 @@ -2011,13 +2012,37 @@ trx_undo_free_prepared( ut_ad(srv_shutdown_state == SRV_SHUTDOWN_EXIT_THREADS); if (trx->update_undo) { - ut_a(trx->update_undo->state == TRX_UNDO_PREPARED); + switch (trx->update_undo->state) { + case TRX_UNDO_PREPARED: + break; + case TRX_UNDO_ACTIVE: + /* lock_trx_release_locks() assigns + trx->is_recovered=false */ + ut_a(srv_read_only_mode + || srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO); + break; + default: + ut_error; + } + UT_LIST_REMOVE(undo_list, trx->rseg->update_undo_list, trx->update_undo); trx_undo_mem_free(trx->update_undo); } if (trx->insert_undo) { - ut_a(trx->insert_undo->state == TRX_UNDO_PREPARED); + switch (trx->insert_undo->state) { + case TRX_UNDO_PREPARED: + break; + case TRX_UNDO_ACTIVE: + /* lock_trx_release_locks() assigns + trx->is_recovered=false */ + ut_a(srv_read_only_mode + || srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO); + break; + default: + ut_error; + } + UT_LIST_REMOVE(undo_list, trx->rseg->insert_undo_list, trx->insert_undo); trx_undo_mem_free(trx->insert_undo); diff --git a/storage/maria/ha_maria.cc b/storage/maria/ha_maria.cc index 14ab6ec7599..3e0872b9a89 100644 --- a/storage/maria/ha_maria.cc +++ b/storage/maria/ha_maria.cc @@ -3609,10 +3609,6 @@ static int ha_maria_init(void *p) maria_pagecache->extra_debug= 1; maria_assert_if_crashed_table= debug_assert_if_crashed_table; -#if defined(HAVE_REALPATH) && !defined(HAVE_valgrind) && !defined(HAVE_BROKEN_REALPATH) - /* We can only test for sub paths if my_symlink.c is using realpath */ - maria_test_invalid_symlink= test_if_data_home_dir; -#endif if (res) maria_hton= 0; diff --git a/storage/maria/ma_check.c b/storage/maria/ma_check.c index a189af40362..bd5c67c0409 100644 --- a/storage/maria/ma_check.c +++ b/storage/maria/ma_check.c @@ -2839,7 +2839,7 @@ int maria_repair(HA_CHECK *param, register MARIA_HA *info, (param->testflag & T_BACKUP_DATA ? MYF(MY_REDEL_MAKE_BACKUP): MYF(0)) | sync_dir) || - _ma_open_datafile(info, share, NullS, -1)) + _ma_open_datafile(info, share)) { goto err; } @@ -3994,7 +3994,7 @@ int maria_repair_by_sort(HA_CHECK *param, register MARIA_HA *info, (param->testflag & T_BACKUP_DATA ? MYF(MY_REDEL_MAKE_BACKUP): MYF(0)) | sync_dir) || - _ma_open_datafile(info, share, NullS, -1)) + _ma_open_datafile(info, share)) { _ma_check_print_error(param, "Couldn't change to new data file"); goto err; @@ -4627,7 +4627,7 @@ err: MYF((param->testflag & T_BACKUP_DATA ? MY_REDEL_MAKE_BACKUP : 0) | sync_dir)) || - _ma_open_datafile(info,share, NullS, -1)) + _ma_open_datafile(info,share)) got_error=1; } } diff --git a/storage/maria/ma_create.c b/storage/maria/ma_create.c index 1176b2037b5..14809636616 100644 --- a/storage/maria/ma_create.c +++ b/storage/maria/ma_create.c @@ -53,7 +53,8 @@ int maria_create(const char *name, enum data_file_type datafile_type, uint max_field_lengths, extra_header_size, column_nr; uint internal_table= flags & HA_CREATE_INTERNAL_TABLE; ulong reclength, real_reclength,min_pack_length; - char filename[FN_REFLEN], linkname[FN_REFLEN], *linkname_ptr; + char kfilename[FN_REFLEN], klinkname[FN_REFLEN], *klinkname_ptr; + char dfilename[FN_REFLEN], dlinkname[FN_REFLEN], *dlinkname_ptr; ulong pack_reclength; ulonglong tot_length,max_rows, tmp; enum en_fieldtype type; @@ -808,19 +809,19 @@ int maria_create(const char *name, enum data_file_type datafile_type, /* chop off the table name, tempory tables use generated name */ if ((path= strrchr(ci->index_file_name, FN_LIBCHAR))) *path= '\0'; - fn_format(filename, name, ci->index_file_name, MARIA_NAME_IEXT, + fn_format(kfilename, name, ci->index_file_name, MARIA_NAME_IEXT, MY_REPLACE_DIR | MY_UNPACK_FILENAME | MY_RETURN_REAL_PATH | MY_APPEND_EXT); } else { - fn_format(filename, ci->index_file_name, "", MARIA_NAME_IEXT, + fn_format(kfilename, ci->index_file_name, "", MARIA_NAME_IEXT, MY_UNPACK_FILENAME | MY_RETURN_REAL_PATH | (have_iext ? MY_REPLACE_EXT : MY_APPEND_EXT)); } - fn_format(linkname, name, "", MARIA_NAME_IEXT, + fn_format(klinkname, name, "", MARIA_NAME_IEXT, MY_UNPACK_FILENAME|MY_APPEND_EXT); - linkname_ptr= linkname; + klinkname_ptr= klinkname; /* Don't create the table if the link or file exists to ensure that one doesn't accidently destroy another table. @@ -834,10 +835,10 @@ int maria_create(const char *name, enum data_file_type datafile_type, { char *iext= strrchr(name, '.'); int have_iext= iext && !strcmp(iext, MARIA_NAME_IEXT); - fn_format(filename, name, "", MARIA_NAME_IEXT, + fn_format(kfilename, name, "", MARIA_NAME_IEXT, MY_UNPACK_FILENAME | MY_RETURN_REAL_PATH | (have_iext ? MY_REPLACE_EXT : MY_APPEND_EXT)); - linkname_ptr= NullS; + klinkname_ptr= NullS; /* Replace the current file. Don't sync dir now if the data file has the same path. @@ -857,7 +858,7 @@ int maria_create(const char *name, enum data_file_type datafile_type, NOTE: The filename is compared against unique_file_name of every open table. Hence we need a real path here. */ - if (!internal_table && _ma_test_if_reopen(filename)) + if (!internal_table && _ma_test_if_reopen(kfilename)) { my_printf_error(HA_ERR_TABLE_EXIST, "Aria table '%s' is in use " "(most likely by a MERGE table). Try FLUSH TABLES.", @@ -866,8 +867,8 @@ int maria_create(const char *name, enum data_file_type datafile_type, goto err; } - if ((file= mysql_file_create_with_symlink(key_file_kfile, linkname_ptr, - filename, 0, create_mode, + if ((file= mysql_file_create_with_symlink(key_file_kfile, klinkname_ptr, + kfilename, 0, create_mode, MYF(MY_WME|create_flag))) < 0) goto err; errpos=1; @@ -1121,30 +1122,30 @@ int maria_create(const char *name, enum data_file_type datafile_type, /* chop off the table name, tempory tables use generated name */ if ((path= strrchr(ci->data_file_name, FN_LIBCHAR))) *path= '\0'; - fn_format(filename, name, ci->data_file_name, MARIA_NAME_DEXT, + fn_format(dfilename, name, ci->data_file_name, MARIA_NAME_DEXT, MY_REPLACE_DIR | MY_UNPACK_FILENAME | MY_APPEND_EXT); } else { - fn_format(filename, ci->data_file_name, "", MARIA_NAME_DEXT, + fn_format(dfilename, ci->data_file_name, "", MARIA_NAME_DEXT, MY_UNPACK_FILENAME | (have_dext ? MY_REPLACE_EXT : MY_APPEND_EXT)); } - fn_format(linkname, name, "",MARIA_NAME_DEXT, + fn_format(dlinkname, name, "",MARIA_NAME_DEXT, MY_UNPACK_FILENAME | MY_APPEND_EXT); - linkname_ptr= linkname; + dlinkname_ptr= dlinkname; create_flag=0; } else { - fn_format(filename,name,"", MARIA_NAME_DEXT, + fn_format(dfilename,name,"", MARIA_NAME_DEXT, MY_UNPACK_FILENAME | MY_APPEND_EXT); - linkname_ptr= NullS; + dlinkname_ptr= NullS; create_flag= (flags & HA_CREATE_KEEP_FILES) ? 0 : MY_DELETE_OLD; } if ((dfile= - mysql_file_create_with_symlink(key_file_dfile, linkname_ptr, - filename, 0, create_mode, + mysql_file_create_with_symlink(key_file_dfile, dlinkname_ptr, + dfilename, 0, create_mode, MYF(MY_WME | create_flag | sync_dir))) < 0) goto err; errpos=3; @@ -1194,19 +1195,21 @@ err_no_lock: mysql_file_close(dfile, MYF(0)); /* fall through */ case 2: - if (! (flags & HA_DONT_TOUCH_DATA)) - mysql_file_delete_with_symlink(key_file_dfile, - fn_format(filename,name,"",MARIA_NAME_DEXT, - MY_UNPACK_FILENAME | MY_APPEND_EXT), - sync_dir); + if (! (flags & HA_DONT_TOUCH_DATA)) + { + mysql_file_delete(key_file_dfile, dfilename, MYF(sync_dir)); + if (dlinkname_ptr) + mysql_file_delete(key_file_dfile, dlinkname_ptr, MYF(sync_dir)); + } /* fall through */ case 1: mysql_file_close(file, MYF(0)); if (! (flags & HA_DONT_TOUCH_DATA)) - mysql_file_delete_with_symlink(key_file_kfile, - fn_format(filename,name,"",MARIA_NAME_IEXT, - MY_UNPACK_FILENAME | MY_APPEND_EXT), - sync_dir); + { + mysql_file_delete(key_file_kfile, kfilename, MYF(sync_dir)); + if (klinkname_ptr) + mysql_file_delete(key_file_kfile, klinkname_ptr, MYF(sync_dir)); + } } my_free(log_data); my_free(rec_per_key_part); diff --git a/storage/maria/ma_delete_table.c b/storage/maria/ma_delete_table.c index 56a6dfc8e5f..a9ab8e596cc 100644 --- a/storage/maria/ma_delete_table.c +++ b/storage/maria/ma_delete_table.c @@ -84,22 +84,13 @@ int maria_delete_table(const char *name) int maria_delete_table_files(const char *name, myf sync_dir) { - char from[FN_REFLEN]; DBUG_ENTER("maria_delete_table_files"); - fn_format(from,name,"",MARIA_NAME_IEXT,MY_UNPACK_FILENAME|MY_APPEND_EXT); - if (mysql_file_delete_with_symlink(key_file_kfile, from, - MYF(MY_WME | sync_dir))) - DBUG_RETURN(my_errno); - fn_format(from,name,"",MARIA_NAME_DEXT,MY_UNPACK_FILENAME|MY_APPEND_EXT); - if (mysql_file_delete_with_symlink(key_file_dfile, from, - MYF(MY_WME | sync_dir))) + if (my_handler_delete_with_symlink(key_file_kfile, name, MARIA_NAME_IEXT, MYF(MY_WME | sync_dir)) || + my_handler_delete_with_symlink(key_file_dfile, name, MARIA_NAME_DEXT, MYF(MY_WME | sync_dir))) DBUG_RETURN(my_errno); - // optional files from maria_pack: - fn_format(from,name,"",".TMD",MY_UNPACK_FILENAME|MY_APPEND_EXT); - mysql_file_delete_with_symlink(key_file_dfile, from, MYF(0)); - fn_format(from,name,"",".OLD",MY_UNPACK_FILENAME|MY_APPEND_EXT); - mysql_file_delete_with_symlink(key_file_dfile, from, MYF(0)); + my_handler_delete_with_symlink(key_file_dfile, name, ".TMD", MYF(0)); + my_handler_delete_with_symlink(key_file_dfile, name, ".OLD", MYF(0)); DBUG_RETURN(0); } diff --git a/storage/maria/ma_open.c b/storage/maria/ma_open.c index 1d274d796be..1db85180fcf 100644 --- a/storage/maria/ma_open.c +++ b/storage/maria/ma_open.c @@ -86,7 +86,7 @@ MARIA_HA *_ma_test_if_reopen(const char *filename) */ -static MARIA_HA *maria_clone_internal(MARIA_SHARE *share, const char *name, +static MARIA_HA *maria_clone_internal(MARIA_SHARE *share, int mode, File data_file, uint internal_table) { @@ -106,7 +106,7 @@ static MARIA_HA *maria_clone_internal(MARIA_SHARE *share, const char *name, } if (data_file >= 0) info.dfile.file= data_file; - else if (_ma_open_datafile(&info, share, name, -1)) + else if (_ma_open_datafile(&info, share)) goto err; errpos= 5; @@ -252,7 +252,7 @@ MARIA_HA *maria_clone(MARIA_SHARE *share, int mode) { MARIA_HA *new_info; mysql_mutex_lock(&THR_LOCK_maria); - new_info= maria_clone_internal(share, NullS, mode, + new_info= maria_clone_internal(share, mode, share->data_file_type == BLOCK_RECORD ? share->bitmap.file.file : -1, 0); mysql_mutex_unlock(&THR_LOCK_maria); @@ -298,8 +298,13 @@ MARIA_HA *maria_open(const char *name, int mode, uint open_flags) realpath_err= my_realpath(name_buff, fn_format(org_name, name, "", MARIA_NAME_IEXT, MY_UNPACK_FILENAME),MYF(0)); + if (realpath_err > 0) /* File not found, no point in looking further. */ + { + DBUG_RETURN(NULL); + } + if (my_is_symlink(org_name) && - (realpath_err || (*maria_test_invalid_symlink)(name_buff))) + (realpath_err || mysys_test_invalid_symlink(name_buff))) { my_errno= HA_WRONG_CREATE_OPTION; DBUG_RETURN(0); @@ -324,13 +329,16 @@ MARIA_HA *maria_open(const char *name, int mode, uint open_flags) my_errno= HA_ERR_CRASHED; goto err; }); + DEBUG_SYNC_C("mi_open_kfile"); if ((kfile=mysql_file_open(key_file_kfile, name_buff, - (open_mode=O_RDWR) | O_SHARE,MYF(0))) < 0) + (open_mode=O_RDWR) | O_SHARE | O_NOFOLLOW, + MYF(MY_NOSYMLINKS))) < 0) { if ((errno != EROFS && errno != EACCES) || mode != O_RDONLY || (kfile=mysql_file_open(key_file_kfile, name_buff, - (open_mode=O_RDONLY) | O_SHARE,MYF(0))) < 0) + (open_mode=O_RDONLY) | O_SHARE | O_NOFOLLOW, + MYF(MY_NOSYMLINKS))) < 0) goto err; } share->mode=open_mode; @@ -375,7 +383,18 @@ MARIA_HA *maria_open(const char *name, int mode, uint open_flags) (void) strmov(index_name, org_name); *strrchr(org_name, FN_EXTCHAR)= '\0'; (void) fn_format(data_name,org_name,"",MARIA_NAME_DEXT, - MY_APPEND_EXT|MY_UNPACK_FILENAME|MY_RESOLVE_SYMLINKS); + MY_APPEND_EXT|MY_UNPACK_FILENAME); + if (my_is_symlink(data_name)) + { + if (my_realpath(data_name, data_name, MYF(0))) + goto err; + if (mysys_test_invalid_symlink(data_name)) + { + my_errno= HA_WRONG_CREATE_OPTION; + goto err; + } + share->mode|= O_NOFOLLOW; /* all symlinks are resolved by realpath() */ + } info_length=mi_uint2korr(share->state.header.header_length); base_pos= mi_uint2korr(share->state.header.base_pos); @@ -832,7 +851,7 @@ MARIA_HA *maria_open(const char *name, int mode, uint open_flags) if ((share->data_file_type == BLOCK_RECORD || share->data_file_type == COMPRESSED_RECORD)) { - if (_ma_open_datafile(&info, share, name, -1)) + if (_ma_open_datafile(&info, share)) goto err; data_file= info.dfile.file; } @@ -1004,7 +1023,7 @@ MARIA_HA *maria_open(const char *name, int mode, uint open_flags) data_file= share->bitmap.file.file; /* Only opened once */ } - if (!(m_info= maria_clone_internal(share, name, mode, data_file, + if (!(m_info= maria_clone_internal(share, mode, data_file, internal_table))) goto err; @@ -1878,35 +1897,15 @@ void _ma_set_index_pagecache_callbacks(PAGECACHE_FILE *file, Open data file We can't use dup() here as the data file descriptors need to have different active seek-positions. - - The argument file_to_dup is here for the future if there would on some OS - exist a dup()-like call that would give us two different file descriptors. *************************************************************************/ -int _ma_open_datafile(MARIA_HA *info, MARIA_SHARE *share, const char *org_name, - File file_to_dup __attribute__((unused))) +int _ma_open_datafile(MARIA_HA *info, MARIA_SHARE *share) { - char *data_name= share->data_file_name.str; - char real_data_name[FN_REFLEN]; - - if (org_name) - { - fn_format(real_data_name, org_name, "", MARIA_NAME_DEXT, 4); - if (my_is_symlink(real_data_name)) - { - if (my_realpath(real_data_name, real_data_name, MYF(0)) || - (*maria_test_invalid_symlink)(real_data_name)) - { - my_errno= HA_WRONG_CREATE_OPTION; - return 1; - } - data_name= real_data_name; - } - } - + myf flags= MY_WME | (share->mode & O_NOFOLLOW ? MY_NOSYMLINKS : 0); + DEBUG_SYNC_C("mi_open_datafile"); info->dfile.file= share->bitmap.file.file= - mysql_file_open(key_file_dfile, data_name, - share->mode | O_SHARE, MYF(MY_WME)); + mysql_file_open(key_file_dfile, share->data_file_name.str, + share->mode | O_SHARE, MYF(flags)); return info->dfile.file >= 0 ? 0 : 1; } @@ -1920,8 +1919,8 @@ int _ma_open_keyfile(MARIA_SHARE *share) mysql_mutex_lock(&share->intern_lock); share->kfile.file= mysql_file_open(key_file_kfile, share->unique_file_name.str, - share->mode | O_SHARE, - MYF(MY_WME)); + share->mode | O_SHARE | O_NOFOLLOW, + MYF(MY_WME | MY_NOSYMLINKS)); mysql_mutex_unlock(&share->intern_lock); return (share->kfile.file < 0); } diff --git a/storage/maria/ma_static.c b/storage/maria/ma_static.c index 35ad7d5a96a..3e4cf3249a8 100644 --- a/storage/maria/ma_static.c +++ b/storage/maria/ma_static.c @@ -106,12 +106,6 @@ uint32 maria_readnext_vec[]= SEARCH_BIGGER, SEARCH_SMALLER, SEARCH_SMALLER }; -static int always_valid(const char *filename __attribute__((unused))) -{ - return 0; -} - -int (*maria_test_invalid_symlink)(const char *filename)= always_valid; my_bool (*ma_killed)(MARIA_HA *)= ma_killed_standalone; #ifdef HAVE_PSI_INTERFACE diff --git a/storage/maria/maria_chk.c b/storage/maria/maria_chk.c index c9d38400bc4..0cdbe28f86b 100644 --- a/storage/maria/maria_chk.c +++ b/storage/maria/maria_chk.c @@ -1275,7 +1275,7 @@ static int maria_chk(HA_CHECK *param, char *filename) mysql_file_close(info->dfile.file, MYF(MY_WME)); /* Close new file */ error|=maria_change_to_newfile(filename,MARIA_NAME_DEXT,DATA_TMP_EXT, 0, MYF(0)); - if (_ma_open_datafile(info,info->s, NullS, -1)) + if (_ma_open_datafile(info, info->s)) error=1; param->out_flag&= ~O_NEW_DATA; /* We are using new datafile */ param->read_cache.file= info->dfile.file; diff --git a/storage/maria/maria_def.h b/storage/maria/maria_def.h index a1fff2e0e43..52d1033ab66 100644 --- a/storage/maria/maria_def.h +++ b/storage/maria/maria_def.h @@ -1312,8 +1312,7 @@ int _ma_def_scan_restore_pos(MARIA_HA *info, MARIA_RECORD_POS lastpos); extern MARIA_HA *_ma_test_if_reopen(const char *filename); my_bool _ma_check_table_is_closed(const char *name, const char *where); -int _ma_open_datafile(MARIA_HA *info, MARIA_SHARE *share, const char *org_name, - File file_to_dup); +int _ma_open_datafile(MARIA_HA *info, MARIA_SHARE *share); int _ma_open_keyfile(MARIA_SHARE *share); void _ma_setup_functions(register MARIA_SHARE *share); my_bool _ma_dynmap_file(MARIA_HA *info, my_off_t size); diff --git a/storage/mroonga/vendor/groonga/lib/CMakeLists.txt b/storage/mroonga/vendor/groonga/lib/CMakeLists.txt index 8959f883ca3..ef3e13e236d 100644 --- a/storage/mroonga/vendor/groonga/lib/CMakeLists.txt +++ b/storage/mroonga/vendor/groonga/lib/CMakeLists.txt @@ -22,8 +22,14 @@ include_directories( ${ONIGMO_INCLUDE_DIRS} ${MRUBY_INCLUDE_DIRS} ${LIBLZ4_INCLUDE_DIRS}) -link_directories( - ${LIBLZ4_LIBRARY_DIRS}) +if (LIBLZ4_LIBRARY_DIRS) + find_library(LZ4_LIBS + NAMES ${LIBLZ4_LIBRARIES} + PATHS ${LIBLZ4_LIBRARY_DIRS} + NO_DEFAULT_PATH) +else() + set(LZ4_LIBS ${LIBLZ4_LIBRARIES}) +endif() read_file_list(${CMAKE_CURRENT_SOURCE_DIR}/sources.am LIBGROONGA_SOURCES) read_file_list(${CMAKE_CURRENT_SOURCE_DIR}/dat/sources.am LIBGRNDAT_SOURCES) @@ -60,7 +66,7 @@ set(GRN_ALL_LIBRARIES ${RT_LIBS} ${PTHREAD_LIBS} ${Z_LIBS} - ${LIBLZ4_LIBRARIES} + ${LZ4_LIBS} ${DL_LIBS} ${M_LIBS} ${WS2_32_LIBS} diff --git a/storage/myisam/ha_myisam.cc b/storage/myisam/ha_myisam.cc index dac01b8af89..2b70518c8fd 100644 --- a/storage/myisam/ha_myisam.cc +++ b/storage/myisam/ha_myisam.cc @@ -1896,15 +1896,22 @@ int ha_myisam::info(uint flag) Set data_file_name and index_file_name to point at the symlink value if table is symlinked (Ie; Real name is not same as generated name) */ + char buf[FN_REFLEN]; data_file_name= index_file_name= 0; fn_format(name_buff, file->filename, "", MI_NAME_DEXT, MY_APPEND_EXT | MY_UNPACK_FILENAME); - if (strcmp(name_buff, misam_info.data_file_name)) - data_file_name=misam_info.data_file_name; + if (my_is_symlink(name_buff)) + { + my_readlink(buf, name_buff, MYF(0)); + data_file_name= ha_thd()->strdup(buf); + } fn_format(name_buff, file->filename, "", MI_NAME_IEXT, MY_APPEND_EXT | MY_UNPACK_FILENAME); - if (strcmp(name_buff, misam_info.index_file_name)) - index_file_name=misam_info.index_file_name; + if (my_is_symlink(name_buff)) + { + my_readlink(buf, name_buff, MYF(0)); + index_file_name= ha_thd()->strdup(buf); + } } if (flag & HA_STATUS_ERRKEY) { @@ -2179,6 +2186,74 @@ uint ha_myisam::checksum() const } +enum_alter_inplace_result +ha_myisam::check_if_supported_inplace_alter(TABLE *new_table, + Alter_inplace_info *alter_info) +{ + DBUG_ENTER("ha_myisam::check_if_supported_inplace_alter"); + + const uint readd_index= Alter_inplace_info::ADD_INDEX | + Alter_inplace_info::DROP_INDEX; + const uint readd_unique= Alter_inplace_info::ADD_UNIQUE_INDEX | + Alter_inplace_info::DROP_UNIQUE_INDEX; + const uint readd_pk= Alter_inplace_info::ADD_PK_INDEX | + Alter_inplace_info::DROP_PK_INDEX; + + const uint op= alter_info->handler_flags; + + /* + ha_myisam::open() updates table->key_info->block_size to be the actual + MYI index block size, overwriting user-specified value (if any). + So, the server can not reliably detect whether ALTER TABLE changes + key_block_size or not, it might think the block size was changed, + when it wasn't, and in this case the server will recreate (drop+add) + the index unnecessary. Fix it. + */ + + if (table->s->keys == new_table->s->keys && + ((op & readd_pk) == readd_pk || + (op & readd_unique) == readd_unique || + (op & readd_index) == readd_index)) + { + for (uint i=0; i < table->s->keys; i++) + { + KEY *old_key= table->key_info + i; + KEY *new_key= new_table->key_info + i; + + if (old_key->block_size == new_key->block_size) + DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED); // must differ somewhere else + + if (new_key->block_size && new_key->block_size != old_key->block_size) + DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED); // really changed + + /* any difference besides the block_size, and we give up */ + if (old_key->key_length != new_key->key_length || + old_key->flags != new_key->flags || + old_key->user_defined_key_parts != new_key->user_defined_key_parts || + old_key->algorithm != new_key->algorithm || + strcmp(old_key->name, new_key->name)) + DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED); + + for (uint j= 0; j < old_key->user_defined_key_parts; j++) + { + KEY_PART_INFO *old_kp= old_key->key_part + j; + KEY_PART_INFO *new_kp= new_key->key_part + j; + if (old_kp->offset != new_kp->offset || + old_kp->null_offset != new_kp->null_offset || + old_kp->length != new_kp->length || + old_kp->fieldnr != new_kp->fieldnr || + old_kp->key_part_flag != new_kp->key_part_flag || + old_kp->type != new_kp->type || + old_kp->null_bit != new_kp->null_bit) + DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED); + } + } + alter_info->handler_flags &= ~(readd_pk | readd_unique | readd_index); + } + DBUG_RETURN(handler::check_if_supported_inplace_alter(new_table, alter_info)); +} + + bool ha_myisam::check_if_incompatible_data(HA_CREATE_INFO *info, uint table_changes) { diff --git a/storage/myisam/ha_myisam.h b/storage/myisam/ha_myisam.h index 63fb0ea5a2a..b132c955795 100644 --- a/storage/myisam/ha_myisam.h +++ b/storage/myisam/ha_myisam.h @@ -138,6 +138,8 @@ class ha_myisam: public handler int optimize(THD* thd, HA_CHECK_OPT* check_opt); int assign_to_keycache(THD* thd, HA_CHECK_OPT* check_opt); int preload_keys(THD* thd, HA_CHECK_OPT* check_opt); + enum_alter_inplace_result check_if_supported_inplace_alter(TABLE *new_table, + Alter_inplace_info *alter_info); bool check_if_incompatible_data(HA_CREATE_INFO *info, uint table_changes); #ifdef HAVE_QUERY_CACHE my_bool register_query_cache_table(THD *thd, char *table_key, diff --git a/storage/myisam/mi_check.c b/storage/myisam/mi_check.c index ec72e0b430a..bbe45aa3c26 100644 --- a/storage/myisam/mi_check.c +++ b/storage/myisam/mi_check.c @@ -80,8 +80,7 @@ static SORT_KEY_BLOCKS *alloc_key_blocks(HA_CHECK *param, uint blocks, uint buffer_length); static ha_checksum mi_byte_checksum(const uchar *buf, uint length); static void set_data_file_type(MI_SORT_INFO *sort_info, MYISAM_SHARE *share); -static int replace_data_file(HA_CHECK *param, MI_INFO *info, - const char *name, File new_file); +static int replace_data_file(HA_CHECK *param, MI_INFO *info, File new_file); void myisamchk_init(HA_CHECK *param) { @@ -1715,7 +1714,7 @@ err: /* Replace the actual file with the temporary file */ if (new_file >= 0) { - got_error= replace_data_file(param, info, name, new_file); + got_error= replace_data_file(param, info, new_file); new_file= -1; param->retry_repair= 0; } @@ -2528,7 +2527,7 @@ err: /* Replace the actual file with the temporary file */ if (new_file >= 0) { - got_error= replace_data_file(param, info, name, new_file); + got_error= replace_data_file(param, info, new_file); new_file= -1; } } @@ -2542,7 +2541,7 @@ err: (void) mysql_file_delete(mi_key_file_datatmp, param->temp_filename, MYF(MY_WME)); if (info->dfile == new_file) /* Retry with key cache */ - if (unlikely(mi_open_datafile(info, share, name, -1))) + if (unlikely(mi_open_datafile(info, share))) param->retry_repair= 0; /* Safety */ } mi_mark_crashed_on_repair(info); @@ -3063,7 +3062,7 @@ err: /* Replace the actual file with the temporary file */ if (new_file >= 0) { - got_error= replace_data_file(param, info, name, new_file); + got_error= replace_data_file(param, info, new_file); new_file= -1; } } @@ -3077,7 +3076,7 @@ err: (void) mysql_file_delete(mi_key_file_datatmp, param->temp_filename, MYF(MY_WME)); if (info->dfile == new_file) /* Retry with key cache */ - if (unlikely(mi_open_datafile(info, share, name, -1))) + if (unlikely(mi_open_datafile(info, share))) param->retry_repair= 0; /* Safety */ } mi_mark_crashed_on_repair(info); @@ -4760,8 +4759,7 @@ int mi_make_backup_of_index(MI_INFO *info, time_t backup_time, myf flags) } -static int replace_data_file(HA_CHECK *param, MI_INFO *info, - const char *name, File new_file) +static int replace_data_file(HA_CHECK *param, MI_INFO *info, File new_file) { MYISAM_SHARE *share=info->s; @@ -4796,7 +4794,7 @@ static int replace_data_file(HA_CHECK *param, MI_INFO *info, DATA_TMP_EXT, param->backup_time, (param->testflag & T_BACKUP_DATA ? MYF(MY_REDEL_MAKE_BACKUP): MYF(0))) || - mi_open_datafile(info, share, name, -1)) + mi_open_datafile(info, share)) return 1; return 0; } diff --git a/storage/myisam/mi_create.c b/storage/myisam/mi_create.c index 88b9da6f8a9..aa85a3718de 100644 --- a/storage/myisam/mi_create.c +++ b/storage/myisam/mi_create.c @@ -46,7 +46,8 @@ int mi_create(const char *name,uint keys,MI_KEYDEF *keydefs, uint aligned_key_start, block_length, res; uint internal_table= flags & HA_CREATE_INTERNAL_TABLE; ulong reclength, real_reclength,min_pack_length; - char filename[FN_REFLEN],linkname[FN_REFLEN], *linkname_ptr; + char kfilename[FN_REFLEN],klinkname[FN_REFLEN], *klinkname_ptr; + char dfilename[FN_REFLEN],dlinkname[FN_REFLEN], *dlinkname_ptr; ulong pack_reclength; ulonglong tot_length,max_rows, tmp; enum en_fieldtype type; @@ -594,19 +595,19 @@ int mi_create(const char *name,uint keys,MI_KEYDEF *keydefs, /* chop off the table name, tempory tables use generated name */ if ((path= strrchr(ci->index_file_name, FN_LIBCHAR))) *path= '\0'; - fn_format(filename, name, ci->index_file_name, MI_NAME_IEXT, + fn_format(kfilename, name, ci->index_file_name, MI_NAME_IEXT, MY_REPLACE_DIR | MY_UNPACK_FILENAME | MY_RETURN_REAL_PATH | MY_APPEND_EXT); } else { - fn_format(filename, ci->index_file_name, "", MI_NAME_IEXT, + fn_format(kfilename, ci->index_file_name, "", MI_NAME_IEXT, MY_UNPACK_FILENAME | MY_RETURN_REAL_PATH | (have_iext ? MY_REPLACE_EXT : MY_APPEND_EXT)); } - fn_format(linkname, name, "", MI_NAME_IEXT, + fn_format(klinkname, name, "", MI_NAME_IEXT, MY_UNPACK_FILENAME|MY_APPEND_EXT); - linkname_ptr=linkname; + klinkname_ptr= klinkname; /* Don't create the table if the link or file exists to ensure that one doesn't accidently destroy another table. @@ -617,10 +618,10 @@ int mi_create(const char *name,uint keys,MI_KEYDEF *keydefs, { char *iext= strrchr(name, '.'); int have_iext= iext && !strcmp(iext, MI_NAME_IEXT); - fn_format(filename, name, "", MI_NAME_IEXT, + fn_format(kfilename, name, "", MI_NAME_IEXT, MY_UNPACK_FILENAME | MY_RETURN_REAL_PATH | (have_iext ? MY_REPLACE_EXT : MY_APPEND_EXT)); - linkname_ptr=0; + klinkname_ptr= 0; /* Replace the current file */ create_flag=(flags & HA_CREATE_KEEP_FILES) ? 0 : MY_DELETE_OLD; } @@ -635,7 +636,7 @@ int mi_create(const char *name,uint keys,MI_KEYDEF *keydefs, NOTE: The filename is compared against unique_file_name of every open table. Hence we need a real path here. */ - if (!internal_table && test_if_reopen(filename)) + if (!internal_table && test_if_reopen(kfilename)) { my_printf_error(HA_ERR_TABLE_EXIST, "MyISAM table '%s' is in use " "(most likely by a MERGE table). Try FLUSH TABLES.", @@ -645,7 +646,7 @@ int mi_create(const char *name,uint keys,MI_KEYDEF *keydefs, } if ((file= mysql_file_create_with_symlink(mi_key_file_kfile, - linkname_ptr, filename, 0, + klinkname_ptr, kfilename, 0, create_mode, MYF(MY_WME | create_flag))) < 0) goto err; @@ -665,31 +666,31 @@ int mi_create(const char *name,uint keys,MI_KEYDEF *keydefs, /* chop off the table name, tempory tables use generated name */ if ((path= strrchr(ci->data_file_name, FN_LIBCHAR))) *path= '\0'; - fn_format(filename, name, ci->data_file_name, MI_NAME_DEXT, + fn_format(dfilename, name, ci->data_file_name, MI_NAME_DEXT, MY_REPLACE_DIR | MY_UNPACK_FILENAME | MY_APPEND_EXT); } else { - fn_format(filename, ci->data_file_name, "", MI_NAME_DEXT, + fn_format(dfilename, ci->data_file_name, "", MI_NAME_DEXT, MY_UNPACK_FILENAME | (have_dext ? MY_REPLACE_EXT : MY_APPEND_EXT)); } - fn_format(linkname, name, "",MI_NAME_DEXT, + fn_format(dlinkname, name, "",MI_NAME_DEXT, MY_UNPACK_FILENAME | MY_APPEND_EXT); - linkname_ptr=linkname; + dlinkname_ptr= dlinkname; create_flag=0; } else { - fn_format(filename,name,"", MI_NAME_DEXT, + fn_format(dfilename,name,"", MI_NAME_DEXT, MY_UNPACK_FILENAME | MY_APPEND_EXT); - linkname_ptr=0; + dlinkname_ptr= 0; create_flag=(flags & HA_CREATE_KEEP_FILES) ? 0 : MY_DELETE_OLD; } if ((dfile= mysql_file_create_with_symlink(mi_key_file_dfile, - linkname_ptr, filename, 0, + dlinkname_ptr, dfilename, 0, create_mode, MYF(MY_WME | create_flag))) < 0) goto err; @@ -843,19 +844,21 @@ err_no_lock: (void) mysql_file_close(dfile, MYF(0)); /* fall through */ case 2: - if (! (flags & HA_DONT_TOUCH_DATA)) - mysql_file_delete_with_symlink(mi_key_file_dfile, - fn_format(filename, name, "", MI_NAME_DEXT, - MY_UNPACK_FILENAME | MY_APPEND_EXT), - MYF(0)); + if (! (flags & HA_DONT_TOUCH_DATA)) + { + mysql_file_delete(mi_key_file_dfile, dfilename, MYF(0)); + if (dlinkname_ptr) + mysql_file_delete(mi_key_file_dfile, dlinkname_ptr, MYF(0)); + } /* fall through */ case 1: (void) mysql_file_close(file, MYF(0)); if (! (flags & HA_DONT_TOUCH_DATA)) - mysql_file_delete_with_symlink(mi_key_file_kfile, - fn_format(filename, name, "", MI_NAME_IEXT, - MY_UNPACK_FILENAME | MY_APPEND_EXT), - MYF(0)); + { + mysql_file_delete(mi_key_file_kfile, kfilename, MYF(0)); + if (klinkname_ptr) + mysql_file_delete(mi_key_file_kfile, klinkname_ptr, MYF(0)); + } } my_free(rec_per_key_part); DBUG_RETURN(my_errno=save_errno); /* return the fatal errno */ diff --git a/storage/myisam/mi_delete_table.c b/storage/myisam/mi_delete_table.c index 7da960011ca..a72a94b06d2 100644 --- a/storage/myisam/mi_delete_table.c +++ b/storage/myisam/mi_delete_table.c @@ -26,30 +26,6 @@ #define mi_key_file_dfile 0 #endif -static int delete_one_file(const char *name, const char *ext, - PSI_file_key pskey __attribute__((unused)), - myf flags) -{ - char from[FN_REFLEN]; - DBUG_ENTER("delete_one_file"); - fn_format(from,name, "", ext, MY_UNPACK_FILENAME | MY_APPEND_EXT); - if (my_is_symlink(from) && (*myisam_test_invalid_symlink)(from)) - { - /* - Symlink is pointing to file in data directory. - Remove symlink, keep file. - */ - if (mysql_file_delete(pskey, from, flags)) - DBUG_RETURN(my_errno); - } - else - { - if (mysql_file_delete_with_symlink(pskey, from, flags)) - DBUG_RETURN(my_errno); - } - DBUG_RETURN(0); -} - int mi_delete_table(const char *name) { int res; @@ -59,14 +35,14 @@ int mi_delete_table(const char *name) check_table_is_closed(name,"delete"); #endif - if ((res= delete_one_file(name, MI_NAME_IEXT, mi_key_file_kfile, MYF(MY_WME)))) - DBUG_RETURN(res); - if ((res= delete_one_file(name, MI_NAME_DEXT, mi_key_file_dfile, MYF(MY_WME)))) - DBUG_RETURN(res); + if (my_handler_delete_with_symlink(mi_key_file_kfile, name, MI_NAME_IEXT, MYF(MY_WME)) || + my_handler_delete_with_symlink(mi_key_file_dfile, name, MI_NAME_DEXT, MYF(MY_WME))) + DBUG_RETURN(my_errno); + // optionally present: - delete_one_file(name, ".OLD", mi_key_file_dfile, MYF(0)); - delete_one_file(name, ".TMD", mi_key_file_dfile, MYF(0)); + my_handler_delete_with_symlink(mi_key_file_dfile, name, ".OLD", MYF(0)); + my_handler_delete_with_symlink(mi_key_file_dfile, name, ".TMD", MYF(0)); DBUG_RETURN(0); } diff --git a/storage/myisam/mi_open.c b/storage/myisam/mi_open.c index 060017f10ad..bdb2fdf8447 100644 --- a/storage/myisam/mi_open.c +++ b/storage/myisam/mi_open.c @@ -104,8 +104,13 @@ MI_INFO *mi_open(const char *name, int mode, uint open_flags) realpath_err= my_realpath(name_buff, fn_format(org_name,name,"",MI_NAME_IEXT,4),MYF(0)); + if (realpath_err > 0) /* File not found, no point in looking further. */ + { + DBUG_RETURN(NULL); + } + if (my_is_symlink(org_name) && - (realpath_err || (*myisam_test_invalid_symlink)(name_buff))) + (realpath_err || mysys_test_invalid_symlink(name_buff))) { my_errno= HA_WRONG_CREATE_OPTION; DBUG_RETURN (NULL); @@ -131,15 +136,17 @@ MI_INFO *mi_open(const char *name, int mode, uint open_flags) my_errno= HA_ERR_CRASHED; goto err; }); - if ((kfile= mysql_file_open(mi_key_file_kfile, - name_buff, - (open_mode= O_RDWR) | O_SHARE, MYF(0))) < 0) + + DEBUG_SYNC_C("mi_open_kfile"); + if ((kfile= mysql_file_open(mi_key_file_kfile, name_buff, + (open_mode= O_RDWR) | O_SHARE | O_NOFOLLOW, + MYF(MY_NOSYMLINKS))) < 0) { if ((errno != EROFS && errno != EACCES) || mode != O_RDONLY || - (kfile= mysql_file_open(mi_key_file_kfile, - name_buff, - (open_mode= O_RDONLY) | O_SHARE, MYF(0))) < 0) + (kfile= mysql_file_open(mi_key_file_kfile, name_buff, + (open_mode= O_RDONLY) | O_SHARE| O_NOFOLLOW, + MYF(MY_NOSYMLINKS))) < 0) goto err; } share->mode=open_mode; @@ -183,7 +190,18 @@ MI_INFO *mi_open(const char *name, int mode, uint open_flags) (void) strmov(index_name, org_name); *strrchr(org_name, '.')= '\0'; (void) fn_format(data_name,org_name,"",MI_NAME_DEXT, - MY_APPEND_EXT|MY_UNPACK_FILENAME|MY_RESOLVE_SYMLINKS); + MY_APPEND_EXT|MY_UNPACK_FILENAME); + if (my_is_symlink(data_name)) + { + if (my_realpath(data_name, data_name, MYF(0))) + goto err; + if (mysys_test_invalid_symlink(data_name)) + { + my_errno= HA_WRONG_CREATE_OPTION; + goto err; + } + share->mode|= O_NOFOLLOW; /* all symlinks are resolved by realpath() */ + } info_length=mi_uint2korr(share->state.header.header_length); base_pos=mi_uint2korr(share->state.header.base_pos); @@ -497,7 +515,7 @@ MI_INFO *mi_open(const char *name, int mode, uint open_flags) lock_error=1; /* Database unlocked */ } - if (mi_open_datafile(&info, share, name, -1)) + if (mi_open_datafile(&info, share)) goto err; errpos=5; @@ -578,7 +596,7 @@ MI_INFO *mi_open(const char *name, int mode, uint open_flags) my_errno=EACCES; /* Can't open in write mode */ goto err; } - if (mi_open_datafile(&info, share, name, old_info->dfile)) + if (mi_open_datafile(&info, share)) goto err; errpos=5; have_rtree= old_info->rtree_recursion_state != NULL; @@ -1250,33 +1268,14 @@ uchar *mi_recinfo_read(uchar *ptr, MI_COLUMNDEF *recinfo) Open data file. We can't use dup() here as the data file descriptors need to have different active seek-positions. - -The argument file_to_dup is here for the future if there would on some OS -exist a dup()-like call that would give us two different file descriptors. *************************************************************************/ -int mi_open_datafile(MI_INFO *info, MYISAM_SHARE *share, const char *org_name, - File file_to_dup __attribute__((unused))) +int mi_open_datafile(MI_INFO *info, MYISAM_SHARE *share) { - char *data_name= share->data_file_name; - char real_data_name[FN_REFLEN]; - - if (org_name) - { - fn_format(real_data_name,org_name,"",MI_NAME_DEXT,4); - if (my_is_symlink(real_data_name)) - { - if (my_realpath(real_data_name, real_data_name, MYF(0)) || - (*myisam_test_invalid_symlink)(real_data_name)) - { - my_errno= HA_WRONG_CREATE_OPTION; - return 1; - } - data_name= real_data_name; - } - } - info->dfile= mysql_file_open(mi_key_file_dfile, - data_name, share->mode | O_SHARE, MYF(MY_WME)); + myf flags= MY_WME | (share->mode & O_NOFOLLOW ? MY_NOSYMLINKS: 0); + DEBUG_SYNC_C("mi_open_datafile"); + info->dfile= mysql_file_open(mi_key_file_dfile, share->data_file_name, + share->mode | O_SHARE, MYF(flags)); return info->dfile >= 0 ? 0 : 1; } @@ -1285,8 +1284,8 @@ int mi_open_keyfile(MYISAM_SHARE *share) { if ((share->kfile= mysql_file_open(mi_key_file_kfile, share->unique_file_name, - share->mode | O_SHARE, - MYF(MY_WME))) < 0) + share->mode | O_SHARE | O_NOFOLLOW, + MYF(MY_NOSYMLINKS | MY_WME))) < 0) return 1; return 0; } diff --git a/storage/myisam/mi_static.c b/storage/myisam/mi_static.c index d77f4f6b8e2..49019fb861c 100644 --- a/storage/myisam/mi_static.c +++ b/storage/myisam/mi_static.c @@ -42,14 +42,6 @@ ulong myisam_data_pointer_size=4; ulonglong myisam_mmap_size= SIZE_T_MAX, myisam_mmap_used= 0; my_bool (*mi_killed)(MI_INFO *)= mi_killed_standalone; -static int always_valid(const char *filename __attribute__((unused))) -{ - return 0; -} - -int (*myisam_test_invalid_symlink)(const char *filename)= always_valid; - - /* read_vec[] is used for converting between P_READ_KEY.. and SEARCH_ Position is , == , >= , <= , > , < diff --git a/storage/myisam/myisamchk.c b/storage/myisam/myisamchk.c index 7835ab83531..edbe235e190 100644 --- a/storage/myisam/myisamchk.c +++ b/storage/myisam/myisamchk.c @@ -1047,7 +1047,7 @@ static int myisamchk(HA_CHECK *param, char * filename) MYF(MY_WME)); /* Close new file */ error|=change_to_newfile(filename, MI_NAME_DEXT, DATA_TMP_EXT, 0, MYF(0)); - if (mi_open_datafile(info,info->s, NULL, -1)) + if (mi_open_datafile(info, info->s)) error=1; param->out_flag&= ~O_NEW_DATA; /* We are using new datafile */ param->read_cache.file=info->dfile; diff --git a/storage/myisam/myisamdef.h b/storage/myisam/myisamdef.h index 07c4a5b473a..92fc5e45d5d 100644 --- a/storage/myisam/myisamdef.h +++ b/storage/myisam/myisamdef.h @@ -713,8 +713,7 @@ void mi_disable_indexes_for_rebuild(MI_INFO *info, ha_rows rows, my_bool all_keys); extern MI_INFO *test_if_reopen(char *filename); my_bool check_table_is_closed(const char *name, const char *where); -int mi_open_datafile(MI_INFO *info, MYISAM_SHARE *share, const char *orn_name, - File file_to_dup); +int mi_open_datafile(MI_INFO *info, MYISAM_SHARE *share); int mi_open_keyfile(MYISAM_SHARE *share); void mi_setup_functions(register MYISAM_SHARE *share); diff --git a/storage/oqgraph/mysql-test/oqgraph/regression_mdev6282.result b/storage/oqgraph/mysql-test/oqgraph/regression_mdev6282.result index 42c86405503..6b1d0a1854d 100644 --- a/storage/oqgraph/mysql-test/oqgraph/regression_mdev6282.result +++ b/storage/oqgraph/mysql-test/oqgraph/regression_mdev6282.result @@ -33,5 +33,5 @@ FROM `version_history` AS `v` INNER JOIN `db_history` AS `db` ON `db`.`nodeID` = WHERE `latch` = 'breadth_first' AND `origid` = '1' ORDER BY `weight` DESC LIMIT 1; version nodeID 0.0.3 3 -DROP TABLE db_history; DROP TABLE version_history; +DROP TABLE db_history; diff --git a/storage/oqgraph/mysql-test/oqgraph/regression_mdev6282.test b/storage/oqgraph/mysql-test/oqgraph/regression_mdev6282.test index 3a7a0e5af08..b8f0ae556c8 100644 --- a/storage/oqgraph/mysql-test/oqgraph/regression_mdev6282.test +++ b/storage/oqgraph/mysql-test/oqgraph/regression_mdev6282.test @@ -40,8 +40,8 @@ --disconnect con2 --connect (con3,localhost,root,,test) -DROP TABLE db_history; DROP TABLE version_history; +DROP TABLE db_history; --disconnect con3 diff --git a/storage/oqgraph/mysql-test/oqgraph/regression_mdev6345.result b/storage/oqgraph/mysql-test/oqgraph/regression_mdev6345.result index 8e680e15206..68002ce98a2 100644 --- a/storage/oqgraph/mysql-test/oqgraph/regression_mdev6345.result +++ b/storage/oqgraph/mysql-test/oqgraph/regression_mdev6345.result @@ -8,5 +8,5 @@ latch origid destid weight seq linkid breadth_first 1 6 NULL 0 1 breadth_first 1 6 1 1 2 breadth_first 1 6 1 2 6 -DROP TABLE IF EXISTS oq_backing; DROP TABLE IF EXISTS oq_graph; +DROP TABLE IF EXISTS oq_backing; diff --git a/storage/oqgraph/mysql-test/oqgraph/regression_mdev6345.test b/storage/oqgraph/mysql-test/oqgraph/regression_mdev6345.test index 72de926da2e..fefd03a7ed9 100644 --- a/storage/oqgraph/mysql-test/oqgraph/regression_mdev6345.test +++ b/storage/oqgraph/mysql-test/oqgraph/regression_mdev6345.test @@ -14,6 +14,6 @@ CREATE TABLE oq_graph (latch VARCHAR(32) NULL, origid BIGINT UNSIGNED NULL, dest SELECT * FROM oq_graph WHERE latch='breadth_first' AND origid=1 AND destid=6; -DROP TABLE IF EXISTS oq_backing; DROP TABLE IF EXISTS oq_graph; +DROP TABLE IF EXISTS oq_backing; diff --git a/storage/tokudb/CMakeLists.txt b/storage/tokudb/CMakeLists.txt index 3017d3cf68e..90a8f4974c4 100644 --- a/storage/tokudb/CMakeLists.txt +++ b/storage/tokudb/CMakeLists.txt @@ -1,4 +1,4 @@ -SET(TOKUDB_VERSION 5.6.34-79.1) +SET(TOKUDB_VERSION 5.6.35-80.0) # PerconaFT only supports x86-64 and cmake-2.8.9+ IF(CMAKE_VERSION VERSION_LESS "2.8.9") MESSAGE(STATUS "CMake 2.8.9 or higher is required by TokuDB") diff --git a/storage/tokudb/PerconaFT/ft/ft-ops.cc b/storage/tokudb/PerconaFT/ft/ft-ops.cc index 30a8710d7aa..ad9ecb1d074 100644 --- a/storage/tokudb/PerconaFT/ft/ft-ops.cc +++ b/storage/tokudb/PerconaFT/ft/ft-ops.cc @@ -651,10 +651,8 @@ void toku_ftnode_clone_callback(void *value_data, // set new pair attr if necessary if (node->height == 0) { *new_attr = make_ftnode_pair_attr(node); - for (int i = 0; i < node->n_children; i++) { - BLB(node, i)->logical_rows_delta = 0; - BLB(cloned_node, i)->logical_rows_delta = 0; - } + node->logical_rows_delta = 0; + cloned_node->logical_rows_delta = 0; } else { new_attr->is_valid = false; } @@ -702,6 +700,10 @@ void toku_ftnode_flush_callback(CACHEFILE UU(cachefile), if (ftnode->height == 0) { FT_STATUS_INC(FT_FULL_EVICTIONS_LEAF, 1); FT_STATUS_INC(FT_FULL_EVICTIONS_LEAF_BYTES, node_size); + if (!ftnode->dirty) { + toku_ft_adjust_logical_row_count( + ft, -ftnode->logical_rows_delta); + } } else { FT_STATUS_INC(FT_FULL_EVICTIONS_NONLEAF, 1); FT_STATUS_INC(FT_FULL_EVICTIONS_NONLEAF_BYTES, node_size); @@ -714,11 +716,12 @@ void toku_ftnode_flush_callback(CACHEFILE UU(cachefile), BASEMENTNODE bn = BLB(ftnode, i); toku_ft_decrease_stats(&ft->in_memory_stats, bn->stat64_delta); - if (!ftnode->dirty) - toku_ft_adjust_logical_row_count( - ft, -bn->logical_rows_delta); } } + if (!ftnode->dirty) { + toku_ft_adjust_logical_row_count( + ft, -ftnode->logical_rows_delta); + } } } toku_ftnode_free(&ftnode); @@ -944,8 +947,6 @@ int toku_ftnode_pe_callback(void *ftnode_pv, basements_to_destroy[num_basements_to_destroy++] = bn; toku_ft_decrease_stats(&ft->in_memory_stats, bn->stat64_delta); - toku_ft_adjust_logical_row_count(ft, - -bn->logical_rows_delta); set_BNULL(node, i); BP_STATE(node, i) = PT_ON_DISK; num_partial_evictions++; @@ -2652,7 +2653,7 @@ static std::unique_ptr<char[], decltype(&toku_free)> toku_file_get_parent_dir( return result; } -static bool toku_create_subdirs_if_needed(const char *path) { +bool toku_create_subdirs_if_needed(const char *path) { static const mode_t dir_mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IXOTH; @@ -4563,6 +4564,8 @@ int toku_ft_rename_iname(DB_TXN *txn, bs_new_name); } + if (!toku_create_subdirs_if_needed(new_iname_full.get())) + return get_error_errno(); r = toku_os_rename(old_iname_full.get(), new_iname_full.get()); if (r != 0) return r; diff --git a/storage/tokudb/PerconaFT/ft/ft-ops.h b/storage/tokudb/PerconaFT/ft/ft-ops.h index 70cf045d43c..df8ffe287df 100644 --- a/storage/tokudb/PerconaFT/ft/ft-ops.h +++ b/storage/tokudb/PerconaFT/ft/ft-ops.h @@ -288,3 +288,8 @@ void toku_ft_set_direct_io(bool direct_io_on); void toku_ft_set_compress_buffers_before_eviction(bool compress_buffers); void toku_note_deserialized_basement_node(bool fixed_key_size); + +// Creates all directories for the path if necessary, +// returns true if all dirs are created successfully or +// all dirs exist, false otherwise. +bool toku_create_subdirs_if_needed(const char* path); diff --git a/storage/tokudb/PerconaFT/ft/logger/recover.cc b/storage/tokudb/PerconaFT/ft/logger/recover.cc index a9c30c0e37a..9eaa56bdc53 100644 --- a/storage/tokudb/PerconaFT/ft/logger/recover.cc +++ b/storage/tokudb/PerconaFT/ft/logger/recover.cc @@ -987,7 +987,8 @@ static int toku_recover_frename(struct logtype_frename *l, RECOVER_ENV renv) { return 1; if (old_exist && !new_exist && - (toku_os_rename(old_iname_full.get(), new_iname_full.get()) == -1 || + (!toku_create_subdirs_if_needed(new_iname_full.get()) || + toku_os_rename(old_iname_full.get(), new_iname_full.get()) == -1 || toku_fsync_directory(old_iname_full.get()) == -1 || toku_fsync_directory(new_iname_full.get()) == -1)) return 1; diff --git a/storage/tokudb/PerconaFT/ft/node.cc b/storage/tokudb/PerconaFT/ft/node.cc index 12e5fda226e..07309ff7f94 100644 --- a/storage/tokudb/PerconaFT/ft/node.cc +++ b/storage/tokudb/PerconaFT/ft/node.cc @@ -386,7 +386,8 @@ static void bnc_apply_messages_to_basement_node( const pivot_bounds & bounds, // contains pivot key bounds of this basement node txn_gc_info *gc_info, - bool *msgs_applied) { + bool *msgs_applied, + int64_t* logical_rows_delta) { int r; NONLEAF_CHILDINFO bnc = BNC(ancestor, childnum); @@ -394,7 +395,6 @@ static void bnc_apply_messages_to_basement_node( // apply messages from this buffer STAT64INFO_S stats_delta = {0, 0}; uint64_t workdone_this_ancestor = 0; - int64_t logical_rows_delta = 0; uint32_t stale_lbi, stale_ube; if (!bn->stale_ancestor_messages_applied) { @@ -470,7 +470,7 @@ static void bnc_apply_messages_to_basement_node( gc_info, &workdone_this_ancestor, &stats_delta, - &logical_rows_delta); + logical_rows_delta); } } else if (stale_lbi == stale_ube) { // No stale messages to apply, we just apply fresh messages, and mark @@ -482,7 +482,7 @@ static void bnc_apply_messages_to_basement_node( .gc_info = gc_info, .workdone = &workdone_this_ancestor, .stats_to_update = &stats_delta, - .logical_rows_delta = &logical_rows_delta}; + .logical_rows_delta = logical_rows_delta}; if (fresh_ube - fresh_lbi > 0) *msgs_applied = true; r = bnc->fresh_message_tree @@ -503,7 +503,7 @@ static void bnc_apply_messages_to_basement_node( .gc_info = gc_info, .workdone = &workdone_this_ancestor, .stats_to_update = &stats_delta, - .logical_rows_delta = &logical_rows_delta}; + .logical_rows_delta = logical_rows_delta}; r = bnc->stale_message_tree .iterate_on_range<struct iterate_do_bn_apply_msg_extra, @@ -521,8 +521,6 @@ static void bnc_apply_messages_to_basement_node( if (stats_delta.numbytes || stats_delta.numrows) { toku_ft_update_stats(&t->ft->in_memory_stats, stats_delta); } - toku_ft_adjust_logical_row_count(t->ft, logical_rows_delta); - bn->logical_rows_delta += logical_rows_delta; } static void @@ -536,6 +534,7 @@ apply_ancestors_messages_to_bn( bool* msgs_applied ) { + int64_t logical_rows_delta = 0; BASEMENTNODE curr_bn = BLB(node, childnum); const pivot_bounds curr_bounds = bounds.next_bounds(node, childnum); for (ANCESTORS curr_ancestors = ancestors; curr_ancestors; curr_ancestors = curr_ancestors->next) { @@ -548,13 +547,16 @@ apply_ancestors_messages_to_bn( curr_ancestors->childnum, curr_bounds, gc_info, - msgs_applied + msgs_applied, + &logical_rows_delta ); // We don't want to check this ancestor node again if the // next time we query it, the msn hasn't changed. curr_bn->max_msn_applied = curr_ancestors->node->max_msn_applied_to_node_on_disk; } } + toku_ft_adjust_logical_row_count(t->ft, logical_rows_delta); + node->logical_rows_delta += logical_rows_delta; // At this point, we know all the stale messages above this // basement node have been applied, and any new messages will be // fresh, so we don't need to look at stale messages for this diff --git a/storage/tokudb/PerconaFT/ft/node.h b/storage/tokudb/PerconaFT/ft/node.h index 52eefec0936..db189e36d59 100644 --- a/storage/tokudb/PerconaFT/ft/node.h +++ b/storage/tokudb/PerconaFT/ft/node.h @@ -157,36 +157,49 @@ private: // TODO: class me up struct ftnode { - MSN max_msn_applied_to_node_on_disk; // max_msn_applied that will be written to disk + // max_msn_applied that will be written to disk + MSN max_msn_applied_to_node_on_disk; unsigned int flags; - BLOCKNUM blocknum; // Which block number is this node? - int layout_version; // What version of the data structure? - int layout_version_original; // different (<) from layout_version if upgraded from a previous version (useful for debugging) - int layout_version_read_from_disk; // transient, not serialized to disk, (useful for debugging) - uint32_t build_id; // build_id (svn rev number) of software that wrote this node to disk - int height; /* height is always >= 0. 0 for leaf, >0 for nonleaf. */ - int dirty; + // Which block number is this node? + BLOCKNUM blocknum; + // What version of the data structure? + int layout_version; + // different (<) from layout_version if upgraded from a previous version + // (useful for debugging) + int layout_version_original; + // transient, not serialized to disk, (useful for debugging) + int layout_version_read_from_disk; + // build_id (svn rev number) of software that wrote this node to disk + uint32_t build_id; + // height is always >= 0. 0 for leaf, >0 for nonleaf. + int height; + int dirty; uint32_t fullhash; + // current count of rows add or removed as a result of message application + // to this node as a basement, irrelevant for internal nodes, gets reset + // when node is undirtied. Used to back out tree scoped LRC id node is + // evicted but not persisted + int64_t logical_rows_delta; - // for internal nodes, if n_children==fanout+1 then the tree needs to be rebalanced. - // for leaf nodes, represents number of basement nodes + // for internal nodes, if n_children==fanout+1 then the tree needs to be + // rebalanced. for leaf nodes, represents number of basement nodes int n_children; ftnode_pivot_keys pivotkeys; - // What's the oldest referenced xid that this node knows about? The real oldest - // referenced xid might be younger, but this is our best estimate. We use it - // as a heuristic to transition provisional mvcc entries from provisional to - // committed (from implicity committed to really committed). + // What's the oldest referenced xid that this node knows about? The real + // oldest referenced xid might be younger, but this is our best estimate. + // We use it as a heuristic to transition provisional mvcc entries from + // provisional to committed (from implicity committed to really committed). // - // A better heuristic would be the oldest live txnid, but we use this since it - // still works well most of the time, and its readily available on the inject - // code path. + // A better heuristic would be the oldest live txnid, but we use this since + // it still works well most of the time, and its readily available on the + // inject code path. TXNID oldest_referenced_xid_known; // array of size n_children, consisting of ftnode partitions - // each one is associated with a child - // for internal nodes, the ith partition corresponds to the ith message buffer - // for leaf nodes, the ith partition corresponds to the ith basement node + // each one is associated with a child for internal nodes, the ith + // partition corresponds to the ith message buffer for leaf nodes, the ith + // partition corresponds to the ith basement node struct ftnode_partition *bp; struct ctpair *ct_pair; }; @@ -199,7 +212,6 @@ struct ftnode_leaf_basement_node { MSN max_msn_applied; // max message sequence number applied bool stale_ancestor_messages_applied; STAT64INFO_S stat64_delta; // change in stat64 counters since basement was last written to disk - int64_t logical_rows_delta; }; typedef struct ftnode_leaf_basement_node *BASEMENTNODE; diff --git a/storage/tokudb/PerconaFT/ft/serialize/ft_node-serialize.cc b/storage/tokudb/PerconaFT/ft/serialize/ft_node-serialize.cc index 5914f8a1050..56876b474d4 100644 --- a/storage/tokudb/PerconaFT/ft/serialize/ft_node-serialize.cc +++ b/storage/tokudb/PerconaFT/ft/serialize/ft_node-serialize.cc @@ -996,7 +996,6 @@ BASEMENTNODE toku_clone_bn(BASEMENTNODE orig_bn) { bn->seqinsert = orig_bn->seqinsert; bn->stale_ancestor_messages_applied = orig_bn->stale_ancestor_messages_applied; bn->stat64_delta = orig_bn->stat64_delta; - bn->logical_rows_delta = orig_bn->logical_rows_delta; bn->data_buffer.clone(&orig_bn->data_buffer); return bn; } @@ -1007,7 +1006,6 @@ BASEMENTNODE toku_create_empty_bn_no_buffer(void) { bn->seqinsert = 0; bn->stale_ancestor_messages_applied = false; bn->stat64_delta = ZEROSTATS; - bn->logical_rows_delta = 0; bn->data_buffer.init_zero(); return bn; } @@ -1432,6 +1430,7 @@ static FTNODE alloc_ftnode_for_deserialize(uint32_t fullhash, BLOCKNUM blocknum) node->fullhash = fullhash; node->blocknum = blocknum; node->dirty = 0; + node->logical_rows_delta = 0; node->bp = nullptr; node->oldest_referenced_xid_known = TXNID_NONE; return node; diff --git a/storage/tokudb/PerconaFT/ft/txn/roll.cc b/storage/tokudb/PerconaFT/ft/txn/roll.cc index 9f3977743a0..4f374d62173 100644 --- a/storage/tokudb/PerconaFT/ft/txn/roll.cc +++ b/storage/tokudb/PerconaFT/ft/txn/roll.cc @@ -227,7 +227,8 @@ int toku_rollback_frename(BYTESTRING old_iname, return 1; if (!old_exist && new_exist && - (toku_os_rename(new_iname_full.get(), old_iname_full.get()) == -1 || + (!toku_create_subdirs_if_needed(old_iname_full.get()) || + toku_os_rename(new_iname_full.get(), old_iname_full.get()) == -1 || toku_fsync_directory(new_iname_full.get()) == -1 || toku_fsync_directory(old_iname_full.get()) == -1)) return 1; diff --git a/storage/tokudb/PerconaFT/util/dmt.h b/storage/tokudb/PerconaFT/util/dmt.h index 71cde8814ab..99be296d0e9 100644 --- a/storage/tokudb/PerconaFT/util/dmt.h +++ b/storage/tokudb/PerconaFT/util/dmt.h @@ -589,7 +589,6 @@ private: void convert_from_tree_to_array(void); - __attribute__((nonnull(2,5))) void delete_internal(subtree *const subtreep, const uint32_t idx, subtree *const subtree_replace, subtree **const rebalance_subtree); template<typename iterate_extra_t, @@ -627,16 +626,12 @@ private: __attribute__((nonnull)) void rebalance(subtree *const subtree); - __attribute__((nonnull(3))) static void copyout(uint32_t *const outlen, dmtdata_t *const out, const dmt_node *const n); - __attribute__((nonnull(3))) static void copyout(uint32_t *const outlen, dmtdata_t **const out, dmt_node *const n); - __attribute__((nonnull(4))) static void copyout(uint32_t *const outlen, dmtdata_t *const out, const uint32_t len, const dmtdata_t *const stored_value_ptr); - __attribute__((nonnull(4))) static void copyout(uint32_t *const outlen, dmtdata_t **const out, const uint32_t len, dmtdata_t *const stored_value_ptr); template<typename dmtcmp_t, diff --git a/storage/tokudb/PerconaFT/util/omt.h b/storage/tokudb/PerconaFT/util/omt.h index 799ed0eae7c..c7ed2ca546f 100644 --- a/storage/tokudb/PerconaFT/util/omt.h +++ b/storage/tokudb/PerconaFT/util/omt.h @@ -284,7 +284,6 @@ public: * By taking ownership of the array, we save a malloc and memcpy, * and possibly a free (if the caller is done with the array). */ - __attribute__((nonnull)) void create_steal_sorted_array(omtdata_t **const values, const uint32_t numvalues, const uint32_t new_capacity); /** @@ -667,7 +666,6 @@ private: void set_at_internal(const subtree &subtree, const omtdata_t &value, const uint32_t idx); - __attribute__((nonnull(2,5))) void delete_internal(subtree *const subtreep, const uint32_t idx, omt_node *const copyn, subtree **const rebalance_subtree); template<typename iterate_extra_t, diff --git a/storage/tokudb/ha_tokudb.cc b/storage/tokudb/ha_tokudb.cc index 39a2af3f8eb..ab6c7b9f9bc 100644 --- a/storage/tokudb/ha_tokudb.cc +++ b/storage/tokudb/ha_tokudb.cc @@ -29,6 +29,7 @@ Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved. #include "tokudb_status.h" #include "tokudb_card.h" #include "ha_tokudb.h" +#include "sql_db.h" #if TOKU_INCLUDE_EXTENDED_KEYS @@ -6168,8 +6169,6 @@ int ha_tokudb::info(uint flag) { stats.deleted = 0; if (!(flag & HA_STATUS_NO_LOCK)) { uint64_t num_rows = 0; - TOKU_DB_FRAGMENTATION_S frag_info; - memset(&frag_info, 0, sizeof frag_info); error = txn_begin(db_env, NULL, &txn, DB_READ_UNCOMMITTED, ha_thd()); if (error) { @@ -6186,11 +6185,6 @@ int ha_tokudb::info(uint flag) { } else { goto cleanup; } - error = share->file->get_fragmentation(share->file, &frag_info); - if (error) { - goto cleanup; - } - stats.delete_length = frag_info.unused_bytes; DB_BTREE_STAT64 dict_stats; error = share->file->stat64(share->file, txn, &dict_stats); @@ -6202,6 +6196,7 @@ int ha_tokudb::info(uint flag) { stats.update_time = dict_stats.bt_modify_time_sec; stats.check_time = dict_stats.bt_verify_time_sec; stats.data_file_length = dict_stats.bt_dsize; + stats.delete_length = dict_stats.bt_fsize - dict_stats.bt_dsize; if (hidden_primary_key) { // // in this case, we have a hidden primary key, do not @@ -6237,30 +6232,21 @@ int ha_tokudb::info(uint flag) { // // this solution is much simpler than trying to maintain an // accurate number of valid keys at the handlerton layer. - uint curr_num_DBs = table->s->keys + tokudb_test(hidden_primary_key); + uint curr_num_DBs = + table->s->keys + tokudb_test(hidden_primary_key); for (uint i = 0; i < curr_num_DBs; i++) { // skip the primary key, skip dropped indexes if (i == primary_key || share->key_file[i] == NULL) { continue; } - error = - share->key_file[i]->stat64( - share->key_file[i], - txn, - &dict_stats); + error = share->key_file[i]->stat64( + share->key_file[i], txn, &dict_stats); if (error) { goto cleanup; } stats.index_file_length += dict_stats.bt_dsize; - - error = - share->file->get_fragmentation( - share->file, - &frag_info); - if (error) { - goto cleanup; - } - stats.delete_length += frag_info.unused_bytes; + stats.delete_length += + dict_stats.bt_fsize - dict_stats.bt_dsize; } } @@ -7697,6 +7683,27 @@ int ha_tokudb::delete_table(const char *name) { TOKUDB_HANDLER_DBUG_RETURN(error); } +static bool tokudb_check_db_dir_exist_from_table_name(const char *table_name) { + DBUG_ASSERT(table_name); + bool mysql_dir_exists; + char db_name[FN_REFLEN]; + const char *db_name_begin = strchr(table_name, FN_LIBCHAR); + const char *db_name_end = strrchr(table_name, FN_LIBCHAR); + DBUG_ASSERT(db_name_begin); + DBUG_ASSERT(db_name_end); + DBUG_ASSERT(db_name_begin != db_name_end); + + ++db_name_begin; + size_t db_name_size = db_name_end - db_name_begin; + + DBUG_ASSERT(db_name_size < FN_REFLEN); + + memcpy(db_name, db_name_begin, db_name_size); + db_name[db_name_size] = '\0'; + mysql_dir_exists = (check_db_dir_existence(db_name) == 0); + + return mysql_dir_exists; +} // // renames table from "from" to "to" @@ -7719,15 +7726,33 @@ int ha_tokudb::rename_table(const char *from, const char *to) { TOKUDB_SHARE::drop_share(share); } int error; - error = delete_or_rename_table(from, to, false); - if (TOKUDB_LIKELY(TOKUDB_DEBUG_FLAGS(TOKUDB_DEBUG_HIDE_DDL_LOCK_ERRORS) == 0) && - error == DB_LOCK_NOTGRANTED) { + bool to_db_dir_exist = tokudb_check_db_dir_exist_from_table_name(to); + if (!to_db_dir_exist) { sql_print_error( - "Could not rename table from %s to %s because another transaction " - "has accessed the table. To rename the table, make sure no " - "transactions touch the table.", + "Could not rename table from %s to %s because " + "destination db does not exist", from, to); +#ifndef __WIN__ + /* Small hack. tokudb_check_db_dir_exist_from_table_name calls + * my_access, which sets my_errno on Windows, but doesn't on + * unix. Set it for unix too. + */ + my_errno= errno; +#endif + error= my_errno; + } + else { + error = delete_or_rename_table(from, to, false); + if (TOKUDB_LIKELY(TOKUDB_DEBUG_FLAGS(TOKUDB_DEBUG_HIDE_DDL_LOCK_ERRORS) == 0) && + error == DB_LOCK_NOTGRANTED) { + sql_print_error( + "Could not rename table from %s to %s because another transaction " + "has accessed the table. To rename the table, make sure no " + "transactions touch the table.", + from, + to); + } } TOKUDB_HANDLER_DBUG_RETURN(error); } diff --git a/storage/tokudb/ha_tokudb.h b/storage/tokudb/ha_tokudb.h index 713f99748f1..0629c41b3f3 100644 --- a/storage/tokudb/ha_tokudb.h +++ b/storage/tokudb/ha_tokudb.h @@ -816,6 +816,8 @@ public: int index_first(uchar * buf); int index_last(uchar * buf); + bool has_gap_locks() const { return true; } + int rnd_init(bool scan); int rnd_end(); int rnd_next(uchar * buf); diff --git a/storage/tokudb/mysql-test/tokudb/r/dir_per_db_rename_to_nonexisting_schema.result b/storage/tokudb/mysql-test/tokudb/r/dir_per_db_rename_to_nonexisting_schema.result new file mode 100644 index 00000000000..74148bd4e74 --- /dev/null +++ b/storage/tokudb/mysql-test/tokudb/r/dir_per_db_rename_to_nonexisting_schema.result @@ -0,0 +1,46 @@ +SET GLOBAL tokudb_dir_per_db=true; +###### +# Tokudb and mysql data dirs are the same, rename to existent db +### +CREATE DATABASE new_db; +CREATE TABLE t1 (id INT AUTO_INCREMENT PRIMARY KEY NOT NULL) ENGINE=tokudb; +ALTER TABLE test.t1 RENAME new_db.t1; +The content of "test" directory: +The content of "new_db" directory: +db.opt +t1.frm +t1_main_id.tokudb +t1_status_id.tokudb +DROP DATABASE new_db; +###### +# Tokudb and mysql data dirs are the same, rename to nonexistent db +### +CREATE TABLE t1 (id INT AUTO_INCREMENT PRIMARY KEY NOT NULL) ENGINE=tokudb; +CALL mtr.add_suppression("because destination db does not exist"); +ALTER TABLE test.t1 RENAME foo.t1; +ERROR HY000: Error on rename of './test/t1' to './foo/t1' (errno: 2 "No such file or directory") +DROP TABLE t1; +SELECT @@tokudb_data_dir; +@@tokudb_data_dir +NULL +SELECT @@tokudb_dir_per_db; +@@tokudb_dir_per_db +0 +###### +# Tokudb and mysql data dirs are different, rename to existent db +### +CREATE DATABASE new_db; +CREATE TABLE t1 (id INT AUTO_INCREMENT PRIMARY KEY NOT NULL) ENGINE=tokudb; +ALTER TABLE test.t1 RENAME new_db.t1; +The content of "test" direcotry: +The content of "new_db" directory: +DROP DATABASE new_db; +###### +# Tokudb and mysql data dirs are different, rename to nonexistent db +### +CREATE TABLE t1 (id INT AUTO_INCREMENT PRIMARY KEY NOT NULL) ENGINE=tokudb; +CALL mtr.add_suppression("because destination db does not exist"); +ALTER TABLE test.t1 RENAME foo.t1; +ERROR HY000: Error on rename of './test/t1' to './foo/t1' (errno: 2 "No such file or directory") +DROP TABLE t1; +SET GLOBAL tokudb_dir_per_db=default; diff --git a/storage/tokudb/mysql-test/tokudb/r/gap_lock_error.result b/storage/tokudb/mysql-test/tokudb/r/gap_lock_error.result new file mode 100644 index 00000000000..41e294f7d8d --- /dev/null +++ b/storage/tokudb/mysql-test/tokudb/r/gap_lock_error.result @@ -0,0 +1,469 @@ +CREATE TABLE gap1 (id1 INT, id2 INT, id3 INT, c1 INT, value INT, +PRIMARY KEY (id1, id2, id3), +INDEX i (c1)) ENGINE=tokudb; +CREATE TABLE gap2 like gap1; +CREATE TABLE gap3 (id INT, value INT, +PRIMARY KEY (id), +UNIQUE KEY ui(value)) ENGINE=tokudb; +CREATE TABLE gap4 (id INT, value INT, +PRIMARY KEY (id)) ENGINE=tokudb +PARTITION BY HASH(id) PARTITIONS 2; +insert into gap3 values (1,1), (2,2),(3,3),(4,4),(5,5); +insert into gap4 values (1,1), (2,2),(3,3),(4,4),(5,5); +set session autocommit=0; +select * from gap1 limit 1 for update; +id1 id2 id3 c1 value +0 0 1 1 1 +select * from gap1 where value != 100 limit 1 for update; +id1 id2 id3 c1 value +0 0 1 1 1 +select * from gap1 where id1=1 for update; +id1 id2 id3 c1 value +1 0 2 2 2 +1 0 3 3 3 +select * from gap1 where id1=1 and id2= 1 for update; +id1 id2 id3 c1 value +select * from gap1 where id1=1 and id2= 1 and id3 != 1 for update; +id1 id2 id3 c1 value +select * from gap1 where id1=1 and id2= 1 and id3 +between 1 and 3 for update; +id1 id2 id3 c1 value +select * from gap1 where id1=1 and id2= 1 order by id3 asc +limit 1 for update; +id1 id2 id3 c1 value +select * from gap1 where id1=1 and id2= 1 order by id3 desc +limit 1 for update; +id1 id2 id3 c1 value +select * from gap1 order by id1 asc limit 1 for update; +id1 id2 id3 c1 value +0 0 1 1 1 +select * from gap1 order by id1 asc, id2 asc, id3 asc limit 1 for update; +id1 id2 id3 c1 value +0 0 1 1 1 +select * from gap1 order by id1 desc limit 1 for update; +id1 id2 id3 c1 value +500 100 1000 1000 1000 +select * from gap1 order by id1 desc, id2 desc, id3 desc +limit 1 for update; +id1 id2 id3 c1 value +500 100 1000 1000 1000 +select * from gap1 force index(i) where c1=1 for update; +id1 id2 id3 c1 value +0 0 1 1 1 +select * from gap3 force index(ui) where value=1 for update; +id value +1 1 +select * from gap1 where id1=1 and id2=1 and id3=1 for update; +id1 id2 id3 c1 value +select * from gap1 where id1=1 and id2=1 and id3 in (1, 2, 3) for update; +id1 id2 id3 c1 value +select * from gap1 where id1=1 and id2=1 and id3=1 and value=1 +order by c1 for update; +id1 id2 id3 c1 value +select * from gap3 where id=1 for update; +id value +1 1 +select * from gap4 where id=1 for update; +id value +1 1 +select * from gap4 where id in (1, 2, 3) for update; +id value +1 1 +2 2 +3 3 +select * from gap4 for update; +id value +2 2 +4 4 +1 1 +3 3 +5 5 +select * from gap4 where id between 3 and 7 for update; +id value +3 3 +4 4 +5 5 +set session autocommit=1; +select * from gap1 limit 1 for update; +id1 id2 id3 c1 value +0 0 1 1 1 +select * from gap1 where value != 100 limit 1 for update; +id1 id2 id3 c1 value +0 0 1 1 1 +select * from gap1 where id1=1 for update; +id1 id2 id3 c1 value +1 0 2 2 2 +1 0 3 3 3 +select * from gap1 where id1=1 and id2= 1 for update; +id1 id2 id3 c1 value +select * from gap1 where id1=1 and id2= 1 and id3 != 1 for update; +id1 id2 id3 c1 value +select * from gap1 where id1=1 and id2= 1 and id3 +between 1 and 3 for update; +id1 id2 id3 c1 value +select * from gap1 where id1=1 and id2= 1 order by id3 asc +limit 1 for update; +id1 id2 id3 c1 value +select * from gap1 where id1=1 and id2= 1 order by id3 desc +limit 1 for update; +id1 id2 id3 c1 value +select * from gap1 order by id1 asc limit 1 for update; +id1 id2 id3 c1 value +0 0 1 1 1 +select * from gap1 order by id1 asc, id2 asc, id3 asc limit 1 for update; +id1 id2 id3 c1 value +0 0 1 1 1 +select * from gap1 order by id1 desc limit 1 for update; +id1 id2 id3 c1 value +500 100 1000 1000 1000 +select * from gap1 order by id1 desc, id2 desc, id3 desc +limit 1 for update; +id1 id2 id3 c1 value +500 100 1000 1000 1000 +select * from gap1 force index(i) where c1=1 for update; +id1 id2 id3 c1 value +0 0 1 1 1 +select * from gap3 force index(ui) where value=1 for update; +id value +1 1 +select * from gap1 where id1=1 and id2=1 and id3=1 for update; +id1 id2 id3 c1 value +select * from gap1 where id1=1 and id2=1 and id3 in (1, 2, 3) for update; +id1 id2 id3 c1 value +select * from gap1 where id1=1 and id2=1 and id3=1 and value=1 +order by c1 for update; +id1 id2 id3 c1 value +select * from gap3 where id=1 for update; +id value +1 1 +select * from gap4 where id=1 for update; +id value +1 1 +select * from gap4 where id in (1, 2, 3) for update; +id value +1 1 +2 2 +3 3 +select * from gap4 for update; +id value +2 2 +4 4 +1 1 +3 3 +5 5 +select * from gap4 where id between 3 and 7 for update; +id value +3 3 +4 4 +5 5 +set session autocommit=0; +select * from gap1 limit 1 lock in share mode; +id1 id2 id3 c1 value +0 0 1 1 1 +select * from gap1 where value != 100 limit 1 lock in share mode; +id1 id2 id3 c1 value +0 0 1 1 1 +select * from gap1 where id1=1 lock in share mode; +id1 id2 id3 c1 value +1 0 2 2 2 +1 0 3 3 3 +select * from gap1 where id1=1 and id2= 1 lock in share mode; +id1 id2 id3 c1 value +select * from gap1 where id1=1 and id2= 1 and id3 != 1 lock in share mode; +id1 id2 id3 c1 value +select * from gap1 where id1=1 and id2= 1 and id3 +between 1 and 3 lock in share mode; +id1 id2 id3 c1 value +select * from gap1 where id1=1 and id2= 1 order by id3 asc +limit 1 lock in share mode; +id1 id2 id3 c1 value +select * from gap1 where id1=1 and id2= 1 order by id3 desc +limit 1 lock in share mode; +id1 id2 id3 c1 value +select * from gap1 order by id1 asc limit 1 lock in share mode; +id1 id2 id3 c1 value +0 0 1 1 1 +select * from gap1 order by id1 asc, id2 asc, id3 asc limit 1 lock in share mode; +id1 id2 id3 c1 value +0 0 1 1 1 +select * from gap1 order by id1 desc limit 1 lock in share mode; +id1 id2 id3 c1 value +500 100 1000 1000 1000 +select * from gap1 order by id1 desc, id2 desc, id3 desc +limit 1 lock in share mode; +id1 id2 id3 c1 value +500 100 1000 1000 1000 +select * from gap1 force index(i) where c1=1 lock in share mode; +id1 id2 id3 c1 value +0 0 1 1 1 +select * from gap3 force index(ui) where value=1 lock in share mode; +id value +1 1 +select * from gap1 where id1=1 and id2=1 and id3=1 lock in share mode; +id1 id2 id3 c1 value +select * from gap1 where id1=1 and id2=1 and id3 in (1, 2, 3) lock in share mode; +id1 id2 id3 c1 value +select * from gap1 where id1=1 and id2=1 and id3=1 and value=1 +order by c1 lock in share mode; +id1 id2 id3 c1 value +select * from gap3 where id=1 lock in share mode; +id value +1 1 +select * from gap4 where id=1 lock in share mode; +id value +1 1 +select * from gap4 where id in (1, 2, 3) lock in share mode; +id value +1 1 +2 2 +3 3 +select * from gap4 lock in share mode; +id value +2 2 +4 4 +1 1 +3 3 +5 5 +select * from gap4 where id between 3 and 7 lock in share mode; +id value +3 3 +4 4 +5 5 +set session autocommit=1; +select * from gap1 limit 1 lock in share mode; +id1 id2 id3 c1 value +0 0 1 1 1 +select * from gap1 where value != 100 limit 1 lock in share mode; +id1 id2 id3 c1 value +0 0 1 1 1 +select * from gap1 where id1=1 lock in share mode; +id1 id2 id3 c1 value +1 0 2 2 2 +1 0 3 3 3 +select * from gap1 where id1=1 and id2= 1 lock in share mode; +id1 id2 id3 c1 value +select * from gap1 where id1=1 and id2= 1 and id3 != 1 lock in share mode; +id1 id2 id3 c1 value +select * from gap1 where id1=1 and id2= 1 and id3 +between 1 and 3 lock in share mode; +id1 id2 id3 c1 value +select * from gap1 where id1=1 and id2= 1 order by id3 asc +limit 1 lock in share mode; +id1 id2 id3 c1 value +select * from gap1 where id1=1 and id2= 1 order by id3 desc +limit 1 lock in share mode; +id1 id2 id3 c1 value +select * from gap1 order by id1 asc limit 1 lock in share mode; +id1 id2 id3 c1 value +0 0 1 1 1 +select * from gap1 order by id1 asc, id2 asc, id3 asc limit 1 lock in share mode; +id1 id2 id3 c1 value +0 0 1 1 1 +select * from gap1 order by id1 desc limit 1 lock in share mode; +id1 id2 id3 c1 value +500 100 1000 1000 1000 +select * from gap1 order by id1 desc, id2 desc, id3 desc +limit 1 lock in share mode; +id1 id2 id3 c1 value +500 100 1000 1000 1000 +select * from gap1 force index(i) where c1=1 lock in share mode; +id1 id2 id3 c1 value +0 0 1 1 1 +select * from gap3 force index(ui) where value=1 lock in share mode; +id value +1 1 +select * from gap1 where id1=1 and id2=1 and id3=1 lock in share mode; +id1 id2 id3 c1 value +select * from gap1 where id1=1 and id2=1 and id3 in (1, 2, 3) lock in share mode; +id1 id2 id3 c1 value +select * from gap1 where id1=1 and id2=1 and id3=1 and value=1 +order by c1 lock in share mode; +id1 id2 id3 c1 value +select * from gap3 where id=1 lock in share mode; +id value +1 1 +select * from gap4 where id=1 lock in share mode; +id value +1 1 +select * from gap4 where id in (1, 2, 3) lock in share mode; +id value +1 1 +2 2 +3 3 +select * from gap4 lock in share mode; +id value +2 2 +4 4 +1 1 +3 3 +5 5 +select * from gap4 where id between 3 and 7 lock in share mode; +id value +3 3 +4 4 +5 5 +set session autocommit=0; +select * from gap1 limit 1 ; +id1 id2 id3 c1 value +0 0 1 1 1 +select * from gap1 where value != 100 limit 1 ; +id1 id2 id3 c1 value +0 0 1 1 1 +select * from gap1 where id1=1 ; +id1 id2 id3 c1 value +1 0 2 2 2 +1 0 3 3 3 +select * from gap1 where id1=1 and id2= 1 ; +id1 id2 id3 c1 value +select * from gap1 where id1=1 and id2= 1 and id3 != 1 ; +id1 id2 id3 c1 value +select * from gap1 where id1=1 and id2= 1 and id3 +between 1 and 3 ; +id1 id2 id3 c1 value +select * from gap1 where id1=1 and id2= 1 order by id3 asc +limit 1 ; +id1 id2 id3 c1 value +select * from gap1 where id1=1 and id2= 1 order by id3 desc +limit 1 ; +id1 id2 id3 c1 value +select * from gap1 order by id1 asc limit 1 ; +id1 id2 id3 c1 value +0 0 1 1 1 +select * from gap1 order by id1 asc, id2 asc, id3 asc limit 1 ; +id1 id2 id3 c1 value +0 0 1 1 1 +select * from gap1 order by id1 desc limit 1 ; +id1 id2 id3 c1 value +500 100 1000 1000 1000 +select * from gap1 order by id1 desc, id2 desc, id3 desc +limit 1 ; +id1 id2 id3 c1 value +500 100 1000 1000 1000 +select * from gap1 force index(i) where c1=1 ; +id1 id2 id3 c1 value +0 0 1 1 1 +select * from gap3 force index(ui) where value=1 ; +id value +1 1 +select * from gap1 where id1=1 and id2=1 and id3=1 ; +id1 id2 id3 c1 value +select * from gap1 where id1=1 and id2=1 and id3 in (1, 2, 3) ; +id1 id2 id3 c1 value +select * from gap1 where id1=1 and id2=1 and id3=1 and value=1 +order by c1 ; +id1 id2 id3 c1 value +select * from gap3 where id=1 ; +id value +1 1 +select * from gap4 where id=1 ; +id value +1 1 +select * from gap4 where id in (1, 2, 3) ; +id value +1 1 +2 2 +3 3 +select * from gap4 ; +id value +2 2 +4 4 +1 1 +3 3 +5 5 +select * from gap4 where id between 3 and 7 ; +id value +3 3 +4 4 +5 5 +set session autocommit=1; +select * from gap1 limit 1 ; +id1 id2 id3 c1 value +0 0 1 1 1 +select * from gap1 where value != 100 limit 1 ; +id1 id2 id3 c1 value +0 0 1 1 1 +select * from gap1 where id1=1 ; +id1 id2 id3 c1 value +1 0 2 2 2 +1 0 3 3 3 +select * from gap1 where id1=1 and id2= 1 ; +id1 id2 id3 c1 value +select * from gap1 where id1=1 and id2= 1 and id3 != 1 ; +id1 id2 id3 c1 value +select * from gap1 where id1=1 and id2= 1 and id3 +between 1 and 3 ; +id1 id2 id3 c1 value +select * from gap1 where id1=1 and id2= 1 order by id3 asc +limit 1 ; +id1 id2 id3 c1 value +select * from gap1 where id1=1 and id2= 1 order by id3 desc +limit 1 ; +id1 id2 id3 c1 value +select * from gap1 order by id1 asc limit 1 ; +id1 id2 id3 c1 value +0 0 1 1 1 +select * from gap1 order by id1 asc, id2 asc, id3 asc limit 1 ; +id1 id2 id3 c1 value +0 0 1 1 1 +select * from gap1 order by id1 desc limit 1 ; +id1 id2 id3 c1 value +500 100 1000 1000 1000 +select * from gap1 order by id1 desc, id2 desc, id3 desc +limit 1 ; +id1 id2 id3 c1 value +500 100 1000 1000 1000 +select * from gap1 force index(i) where c1=1 ; +id1 id2 id3 c1 value +0 0 1 1 1 +select * from gap3 force index(ui) where value=1 ; +id value +1 1 +select * from gap1 where id1=1 and id2=1 and id3=1 ; +id1 id2 id3 c1 value +select * from gap1 where id1=1 and id2=1 and id3 in (1, 2, 3) ; +id1 id2 id3 c1 value +select * from gap1 where id1=1 and id2=1 and id3=1 and value=1 +order by c1 ; +id1 id2 id3 c1 value +select * from gap3 where id=1 ; +id value +1 1 +select * from gap4 where id=1 ; +id value +1 1 +select * from gap4 where id in (1, 2, 3) ; +id value +1 1 +2 2 +3 3 +select * from gap4 ; +id value +2 2 +4 4 +1 1 +3 3 +5 5 +select * from gap4 where id between 3 and 7 ; +id value +3 3 +4 4 +5 5 +set session autocommit=0; +insert into gap1 (id1, id2, id3) values (-1,-1,-1); +insert into gap1 (id1, id2, id3) values (-1,-1,-1) +on duplicate key update value=100; +update gap1 set value=100 where id1=1; +update gap1 set value=100 where id1=1 and id2=1 and id3=1; +delete from gap1 where id1=2; +delete from gap1 where id1=-1 and id2=-1 and id3=-1; +commit; +set session autocommit=1; +insert into gap1 (id1, id2, id3) values (-1,-1,-1); +insert into gap1 (id1, id2, id3) values (-1,-1,-1) +on duplicate key update value=100; +update gap1 set value=100 where id1=1; +update gap1 set value=100 where id1=1 and id2=1 and id3=1; +delete from gap1 where id1=2; +delete from gap1 where id1=-1 and id2=-1 and id3=-1; +commit; +drop table gap1, gap2, gap3, gap4; diff --git a/storage/tokudb/mysql-test/tokudb/r/locks-select-update-3.result b/storage/tokudb/mysql-test/tokudb/r/locks-select-update-3.result index 8b31bf5a280..c1f05cdafd8 100644 --- a/storage/tokudb/mysql-test/tokudb/r/locks-select-update-3.result +++ b/storage/tokudb/mysql-test/tokudb/r/locks-select-update-3.result @@ -8,6 +8,7 @@ select * from t where a=1 for update; a b 1 0 update t set b=b+1 where a=1; +set session tokudb_lock_timeout= 60000; set session transaction isolation level read committed; begin; select * from t where a=1 for update; diff --git a/storage/tokudb/mysql-test/tokudb/r/percona_kill_idle_trx_tokudb.result b/storage/tokudb/mysql-test/tokudb/r/percona_kill_idle_trx_tokudb.result new file mode 100644 index 00000000000..089d1d2b136 --- /dev/null +++ b/storage/tokudb/mysql-test/tokudb/r/percona_kill_idle_trx_tokudb.result @@ -0,0 +1,43 @@ +SET default_storage_engine=TokuDB; +# +# Test kill_idle_transaction_timeout feature with TokuDB +# +CREATE TABLE t1 (a INT); +SET GLOBAL kill_idle_transaction= 1; +BEGIN; +INSERT INTO t1 VALUES (1),(2); +COMMIT; +SELECT * FROM t1; +a +1 +2 +BEGIN; +INSERT INTO t1 VALUES (3); +# Current connection idle transaction killed, reconnecting +SELECT * FROM t1; +a +1 +2 +# +# Test that row locks are released on idle transaction kill +# +SET GLOBAL kill_idle_transaction= 2; +# Take row locks in connection conn1 +BEGIN; +SELECT * FROM t1 FOR UPDATE; +a +1 +2 +# Take row locks in connection default +UPDATE t1 SET a=4; +SELECT * FROM t1; +a +4 +4 +# Show that connection conn1 has been killed +SELECT * FROM t1; +ERROR HY000: MySQL server has gone away +# connection default +# Cleanup +DROP TABLE t1; +SET GLOBAL kill_idle_transaction= saved_kill_idle_transaction; diff --git a/storage/tokudb/mysql-test/tokudb/t/dir_per_db_rename_to_nonexisting_schema.test b/storage/tokudb/mysql-test/tokudb/t/dir_per_db_rename_to_nonexisting_schema.test new file mode 100644 index 00000000000..17fe0188a6e --- /dev/null +++ b/storage/tokudb/mysql-test/tokudb/t/dir_per_db_rename_to_nonexisting_schema.test @@ -0,0 +1,64 @@ +--source include/have_tokudb.inc + +SET GLOBAL tokudb_dir_per_db=true; +--let DATADIR=`SELECT @@datadir` + +--echo ###### +--echo # Tokudb and mysql data dirs are the same, rename to existent db +--echo ### +CREATE DATABASE new_db; +CREATE TABLE t1 (id INT AUTO_INCREMENT PRIMARY KEY NOT NULL) ENGINE=tokudb; +ALTER TABLE test.t1 RENAME new_db.t1; +--echo The content of "test" directory: +--source include/table_files_replace_pattern.inc +--sorted_result +--list_files $DATADIR/test +--echo The content of "new_db" directory: +--source include/table_files_replace_pattern.inc +--sorted_result +--list_files $DATADIR/new_db +DROP DATABASE new_db; + +--echo ###### +--echo # Tokudb and mysql data dirs are the same, rename to nonexistent db +--echo ### +CREATE TABLE t1 (id INT AUTO_INCREMENT PRIMARY KEY NOT NULL) ENGINE=tokudb; +CALL mtr.add_suppression("because destination db does not exist"); +--error ER_ERROR_ON_RENAME +ALTER TABLE test.t1 RENAME foo.t1; +DROP TABLE t1; + +--let $custom_tokudb_data_dir=$MYSQL_TMP_DIR/custom_tokudb_data_dir +--mkdir $custom_tokudb_data_dir +--replace_result $custom_tokudb_data_dir CUSTOM_TOKUDB_DATA_DIR +--source include/restart_mysqld.inc + +--replace_result $custom_tokudb_data_dir CUSTOM_TOKUDB_DATA_DIR +SELECT @@tokudb_data_dir; +SELECT @@tokudb_dir_per_db; + +--echo ###### +--echo # Tokudb and mysql data dirs are different, rename to existent db +--echo ### +CREATE DATABASE new_db; +CREATE TABLE t1 (id INT AUTO_INCREMENT PRIMARY KEY NOT NULL) ENGINE=tokudb; +ALTER TABLE test.t1 RENAME new_db.t1; +--echo The content of "test" direcotry: +--source include/table_files_replace_pattern.inc +--sorted_result +--echo The content of "new_db" directory: +--source include/table_files_replace_pattern.inc +--sorted_result +DROP DATABASE new_db; + +--echo ###### +--echo # Tokudb and mysql data dirs are different, rename to nonexistent db +--echo ### +CREATE TABLE t1 (id INT AUTO_INCREMENT PRIMARY KEY NOT NULL) ENGINE=tokudb; +CALL mtr.add_suppression("because destination db does not exist"); +--error ER_ERROR_ON_RENAME +ALTER TABLE test.t1 RENAME foo.t1; +DROP TABLE t1; + +SET GLOBAL tokudb_dir_per_db=default; + diff --git a/storage/tokudb/mysql-test/tokudb/t/gap_lock_error.test b/storage/tokudb/mysql-test/tokudb/t/gap_lock_error.test new file mode 100644 index 00000000000..8c52cef9e27 --- /dev/null +++ b/storage/tokudb/mysql-test/tokudb/t/gap_lock_error.test @@ -0,0 +1,5 @@ +--source include/have_tokudb.inc + +let $engine=tokudb; +let $expect_gap_lock_errors=0; +--source include/gap_lock_error_all.inc diff --git a/storage/tokudb/mysql-test/tokudb/t/locks-select-update-3.test b/storage/tokudb/mysql-test/tokudb/t/locks-select-update-3.test index a563f061add..5b54fa7313e 100644 --- a/storage/tokudb/mysql-test/tokudb/t/locks-select-update-3.test +++ b/storage/tokudb/mysql-test/tokudb/t/locks-select-update-3.test @@ -15,6 +15,7 @@ select * from t where a=1 for update; # t2 update update t set b=b+1 where a=1; connect(conn1,localhost,root); +set session tokudb_lock_timeout= 60000; set session transaction isolation level read committed; begin; # t2 select for update, should hang until t1 commits diff --git a/storage/tokudb/mysql-test/tokudb/t/percona_kill_idle_trx_tokudb.test b/storage/tokudb/mysql-test/tokudb/t/percona_kill_idle_trx_tokudb.test new file mode 100644 index 00000000000..743fb9a55a7 --- /dev/null +++ b/storage/tokudb/mysql-test/tokudb/t/percona_kill_idle_trx_tokudb.test @@ -0,0 +1,5 @@ +--source include/have_tokudb.inc +--skip MariaDB doesn't support kill_idle_trx variable for all SE + +SET default_storage_engine=TokuDB; +--source include/percona_kill_idle_trx.inc diff --git a/storage/tokudb/mysql-test/tokudb_backup/t/suite.opt b/storage/tokudb/mysql-test/tokudb_backup/t/suite.opt index 0d80cf85a91..5d4cb245e27 100644 --- a/storage/tokudb/mysql-test/tokudb_backup/t/suite.opt +++ b/storage/tokudb/mysql-test/tokudb_backup/t/suite.opt @@ -1 +1 @@ -$TOKUDB_OPT $TOKUDB_LOAD_ADD $TOKUDB_BACKUP_OPT $TOKUDB_BACKUP_LOAD_ADD --loose-tokudb-check-jemalloc=0 --loose-tokudb-cache-size=512M --loose-tokudb-block-size=1M +$TOKUDB_OPT $TOKUDB_LOAD_ADD_PATH $TOKUDB_BACKUP_OPT $TOKUDB_BACKUP_LOAD_ADD_PATH --loose-tokudb-check-jemalloc=0 --loose-tokudb-cache-size=512M --loose-tokudb-block-size=1M diff --git a/storage/xtradb/btr/btr0btr.cc b/storage/xtradb/btr/btr0btr.cc index ecea98fccfe..6bf8823124c 100644 --- a/storage/xtradb/btr/btr0btr.cc +++ b/storage/xtradb/btr/btr0btr.cc @@ -3400,7 +3400,7 @@ Removes a page from the level list of pages. /*************************************************************//** Removes a page from the level list of pages. */ -static MY_ATTRIBUTE((nonnull)) +static void btr_level_list_remove_func( /*=======================*/ @@ -3416,8 +3416,6 @@ btr_level_list_remove_func( ulint prev_page_no; ulint next_page_no; - ut_ad(page != NULL); - ut_ad(mtr != NULL); ut_ad(mtr_memo_contains_page(mtr, page, MTR_MEMO_PAGE_X_FIX)); ut_ad(space == page_get_space_id(page)); /* Get the previous and next page numbers of page */ diff --git a/storage/xtradb/btr/btr0cur.cc b/storage/xtradb/btr/btr0cur.cc index 214d050d562..1705de7ce36 100644 --- a/storage/xtradb/btr/btr0cur.cc +++ b/storage/xtradb/btr/btr0cur.cc @@ -1782,7 +1782,7 @@ btr_cur_pessimistic_insert( /*************************************************************//** For an update, checks the locks and does the undo logging. @return DB_SUCCESS, DB_WAIT_LOCK, or error number */ -UNIV_INLINE MY_ATTRIBUTE((warn_unused_result, nonnull(2,3,6,7))) +UNIV_INLINE MY_ATTRIBUTE((warn_unused_result)) dberr_t btr_cur_upd_lock_and_undo( /*======================*/ @@ -2014,7 +2014,6 @@ btr_cur_update_alloc_zip_func( const page_t* page = page_cur_get_page(cursor); ut_ad(page_zip == page_cur_get_page_zip(cursor)); - ut_ad(page_zip); ut_ad(!dict_index_is_ibuf(index)); ut_ad(rec_offs_validate(page_cur_get_rec(cursor), index, offsets)); @@ -3141,7 +3140,7 @@ btr_cur_del_mark_set_clust_rec( ut_ad(page_is_leaf(page_align(rec))); #ifdef UNIV_DEBUG - if (btr_cur_print_record_ops && (thr != NULL)) { + if (btr_cur_print_record_ops) { btr_cur_trx_report(thr_get_trx(thr)->id, index, "del mark "); rec_print_new(stderr, rec, offsets); } @@ -3855,19 +3854,42 @@ inexact: return(n_rows); } -/*******************************************************************//** -Estimates the number of rows in a given index range. -@return estimated number of rows */ -UNIV_INTERN -ib_int64_t -btr_estimate_n_rows_in_range( -/*=========================*/ - dict_index_t* index, /*!< in: index */ - const dtuple_t* tuple1, /*!< in: range start, may also be empty tuple */ - ulint mode1, /*!< in: search mode for range start */ - const dtuple_t* tuple2, /*!< in: range end, may also be empty tuple */ - ulint mode2, /*!< in: search mode for range end */ - trx_t* trx) /*!< in: trx */ +/** If the tree gets changed too much between the two dives for the left +and right boundary then btr_estimate_n_rows_in_range_low() will retry +that many times before giving up and returning the value stored in +rows_in_range_arbitrary_ret_val. */ +static const unsigned rows_in_range_max_retries = 4; + +/** We pretend that a range has that many records if the tree keeps changing +for rows_in_range_max_retries retries while we try to estimate the records +in a given range. */ +static const int64_t rows_in_range_arbitrary_ret_val = 10; + +/** Estimates the number of rows in a given index range. +@param[in] index index +@param[in] tuple1 range start, may also be empty tuple +@param[in] mode1 search mode for range start +@param[in] tuple2 range end, may also be empty tuple +@param[in] mode2 search mode for range end +@param[in] trx trx +@param[in] nth_attempt if the tree gets modified too much while +we are trying to analyze it, then we will retry (this function will call +itself, incrementing this parameter) +@return estimated number of rows; if after rows_in_range_max_retries +retries the tree keeps changing, then we will just return +rows_in_range_arbitrary_ret_val as a result (if +nth_attempt >= rows_in_range_max_retries and the tree is modified between +the two dives). */ +static +int64_t +btr_estimate_n_rows_in_range_low( + dict_index_t* index, + const dtuple_t* tuple1, + ulint mode1, + const dtuple_t* tuple2, + ulint mode2, + trx_t* trx, + unsigned nth_attempt) { btr_path_t path1[BTR_PATH_ARRAY_N_SLOTS]; btr_path_t path2[BTR_PATH_ARRAY_N_SLOTS]; @@ -3905,6 +3927,12 @@ btr_estimate_n_rows_in_range( mtr_start_trx(&mtr, trx); +#ifdef UNIV_DEBUG + if (!strcmp(index->name, "iC")) { + DEBUG_SYNC_C("btr_estimate_n_rows_in_range_between_dives"); + } +#endif + cursor.path_arr = path2; if (dtuple_get_n_fields(tuple2) > 0) { @@ -3971,6 +3999,33 @@ btr_estimate_n_rows_in_range( if (!diverged && slot1->nth_rec != slot2->nth_rec) { + /* If both slots do not point to the same page or if + the paths have crossed and the same page on both + apparently contains a different number of records, + this means that the tree must have changed between + the dive for slot1 and the dive for slot2 at the + beginning of this function. */ + if (slot1->page_no != slot2->page_no + || slot1->page_level != slot2->page_level + || (slot1->nth_rec >= slot2->nth_rec + && slot1->n_recs != slot2->n_recs)) { + + /* If the tree keeps changing even after a + few attempts, then just return some arbitrary + number. */ + if (nth_attempt >= rows_in_range_max_retries) { + return(rows_in_range_arbitrary_ret_val); + } + + const int64_t ret = + btr_estimate_n_rows_in_range_low( + index, tuple1, mode1, + tuple2, mode2, trx, + nth_attempt + 1); + + return(ret); + } + diverged = TRUE; if (slot1->nth_rec < slot2->nth_rec) { @@ -3989,7 +4044,7 @@ btr_estimate_n_rows_in_range( in this case slot1->nth_rec will point to the supr record and slot2->nth_rec will point to 6 */ - n_rows = 0; + return(0); } } else if (diverged && !diverged_lot) { @@ -4020,6 +4075,30 @@ btr_estimate_n_rows_in_range( } } +/** Estimates the number of rows in a given index range. +@param[in] index index +@param[in] tuple1 range start, may also be empty tuple +@param[in] mode1 search mode for range start +@param[in] tuple2 range end, may also be empty tuple +@param[in] mode2 search mode for range end +@param[in] trx trx +@return estimated number of rows */ +int64_t +btr_estimate_n_rows_in_range( + dict_index_t* index, + const dtuple_t* tuple1, + ulint mode1, + const dtuple_t* tuple2, + ulint mode2, + trx_t* trx) +{ + const int64_t ret = btr_estimate_n_rows_in_range_low( + index, tuple1, mode1, tuple2, mode2, trx, + 1 /* first attempt */); + + return(ret); +} + /*******************************************************************//** Record the number of non_null key values in a given index for each n-column prefix of the index where 1 <= n <= dict_index_get_n_unique(index). @@ -4482,7 +4561,6 @@ btr_cur_disown_inherited_fields( ut_ad(rec_offs_validate(rec, index, offsets)); ut_ad(!rec_offs_comp(offsets) || !rec_get_node_ptr_flag(rec)); ut_ad(rec_offs_any_extern(offsets)); - ut_ad(mtr); for (i = 0; i < rec_offs_n_fields(offsets); i++) { if (rec_offs_nth_extern(offsets, i) @@ -4545,9 +4623,6 @@ btr_push_update_extern_fields( ulint n; const upd_field_t* uf; - ut_ad(tuple); - ut_ad(update); - uf = update->fields; n = upd_get_n_fields(update); @@ -4731,7 +4806,6 @@ btr_store_big_rec_extern_fields( ut_ad(rec_offs_validate(rec, index, offsets)); ut_ad(rec_offs_any_extern(offsets)); - ut_ad(btr_mtr); ut_ad(mtr_memo_contains(btr_mtr, dict_index_get_lock(index), MTR_MEMO_X_LOCK)); ut_ad(mtr_memo_contains(btr_mtr, rec_block, MTR_MEMO_PAGE_X_FIX)); diff --git a/storage/xtradb/buf/buf0buddy.cc b/storage/xtradb/buf/buf0buddy.cc index 8cb880c1169..2ee39c6c992 100644 --- a/storage/xtradb/buf/buf0buddy.cc +++ b/storage/xtradb/buf/buf0buddy.cc @@ -485,7 +485,6 @@ buf_buddy_alloc_low( { buf_block_t* block; - ut_ad(lru); ut_ad(mutex_own(&buf_pool->LRU_list_mutex)); ut_ad(!mutex_own(&buf_pool->zip_mutex)); ut_ad(i >= buf_buddy_get_slot(UNIV_ZIP_SIZE_MIN)); diff --git a/storage/xtradb/buf/buf0buf.cc b/storage/xtradb/buf/buf0buf.cc index 21b10196a25..520997591e8 100644 --- a/storage/xtradb/buf/buf0buf.cc +++ b/storage/xtradb/buf/buf0buf.cc @@ -3691,15 +3691,6 @@ buf_page_init( /* Set the state of the block */ buf_block_set_file_page(block, space, offset); -#ifdef UNIV_DEBUG_VALGRIND - if (!space) { - /* Silence valid Valgrind warnings about uninitialized - data being written to data files. There are some unused - bytes on some pages that InnoDB does not initialize. */ - UNIV_MEM_VALID(block->frame, UNIV_PAGE_SIZE); - } -#endif /* UNIV_DEBUG_VALGRIND */ - buf_block_init_low(block); block->lock_hash_val = lock_rec_hash(space, offset); diff --git a/storage/xtradb/buf/buf0dump.cc b/storage/xtradb/buf/buf0dump.cc index cae6099f03e..df62c945288 100644 --- a/storage/xtradb/buf/buf0dump.cc +++ b/storage/xtradb/buf/buf0dump.cc @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 2011, 2016, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2017, MariaDB Corporation. 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 @@ -52,8 +53,8 @@ enum status_severity { /* Flags that tell the buffer pool dump/load thread which action should it take after being waked up. */ -static ibool buf_dump_should_start = FALSE; -static ibool buf_load_should_start = FALSE; +static volatile bool buf_dump_should_start; +static volatile bool buf_load_should_start; static ibool buf_load_abort_flag = FALSE; @@ -78,7 +79,7 @@ void buf_dump_start() /*============*/ { - buf_dump_should_start = TRUE; + buf_dump_should_start = true; os_event_set(srv_buf_dump_event); } @@ -92,7 +93,7 @@ void buf_load_start() /*============*/ { - buf_load_should_start = TRUE; + buf_load_should_start = true; os_event_set(srv_buf_dump_event); } @@ -695,15 +696,18 @@ DECLARE_THREAD(buf_dump_thread)( os_event_wait(srv_buf_dump_event); if (buf_dump_should_start) { - buf_dump_should_start = FALSE; + buf_dump_should_start = false; buf_dump(TRUE /* quit on shutdown */); } if (buf_load_should_start) { - buf_load_should_start = FALSE; + buf_load_should_start = false; buf_load(); } + if (buf_dump_should_start || buf_load_should_start) { + continue; + } os_event_reset(srv_buf_dump_event); } diff --git a/storage/xtradb/buf/buf0flu.cc b/storage/xtradb/buf/buf0flu.cc index 601e79d8923..018b29b7d95 100644 --- a/storage/xtradb/buf/buf0flu.cc +++ b/storage/xtradb/buf/buf0flu.cc @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1995, 2016, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2017, MariaDB Corporation. 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 @@ -720,9 +721,6 @@ buf_flush_write_complete( buf_pool->n_flush[flush_type]--; - /* fprintf(stderr, "n pending flush %lu\n", - buf_pool->n_flush[flush_type]); */ - if (buf_pool->n_flush[flush_type] == 0 && buf_pool->init_flush[flush_type] == FALSE) { diff --git a/storage/xtradb/buf/buf0lru.cc b/storage/xtradb/buf/buf0lru.cc index c599653706d..1aa2e7a9eb7 100644 --- a/storage/xtradb/buf/buf0lru.cc +++ b/storage/xtradb/buf/buf0lru.cc @@ -1301,6 +1301,71 @@ buf_LRU_check_size_of_non_data_objects( } } +/** Diagnose failure to get a free page and request InnoDB monitor output in +the error log if more than two seconds have been spent already. +@param[in] n_iterations how many buf_LRU_get_free_page iterations + already completed +@param[in] started_ms timestamp in ms of when the attempt to get the + free page started +@param[in] flush_failures how many times single-page flush, if allowed, + has failed +@param[out] mon_value_was previous srv_print_innodb_monitor value +@param[out] started_monitor whether InnoDB monitor print has been requested +*/ +static +void +buf_LRU_handle_lack_of_free_blocks(ulint n_iterations, ulint started_ms, + ulint flush_failures, + ibool *mon_value_was, + ibool *started_monitor) +{ + static ulint last_printout_ms = 0; + + /* Legacy algorithm started warning after at least 2 seconds, we + emulate this. */ + const ulint current_ms = ut_time_ms(); + + if ((current_ms > started_ms + 2000) + && (current_ms > last_printout_ms + 2000)) { + + ut_print_timestamp(stderr); + fprintf(stderr, + " InnoDB: Warning: difficult to find free blocks in\n" + "InnoDB: the buffer pool (%lu search iterations)!\n" + "InnoDB: %lu failed attempts to flush a page!" + " Consider\n" + "InnoDB: increasing the buffer pool size.\n" + "InnoDB: It is also possible that" + " in your Unix version\n" + "InnoDB: fsync is very slow, or" + " completely frozen inside\n" + "InnoDB: the OS kernel. Then upgrading to" + " a newer version\n" + "InnoDB: of your operating system may help." + " Look at the\n" + "InnoDB: number of fsyncs in diagnostic info below.\n" + "InnoDB: Pending flushes (fsync) log: %lu;" + " buffer pool: %lu\n" + "InnoDB: %lu OS file reads, %lu OS file writes," + " %lu OS fsyncs\n" + "InnoDB: Starting InnoDB Monitor to print further\n" + "InnoDB: diagnostics to the standard output.\n", + (ulong) n_iterations, + (ulong) flush_failures, + (ulong) fil_n_pending_log_flushes, + (ulong) fil_n_pending_tablespace_flushes, + (ulong) os_n_file_reads, (ulong) os_n_file_writes, + (ulong) os_n_fsyncs); + + last_printout_ms = current_ms; + *mon_value_was = srv_print_innodb_monitor; + *started_monitor = TRUE; + srv_print_innodb_monitor = TRUE; + os_event_set(lock_sys->timeout_event); + } + +} + /** The maximum allowed backoff sleep time duration, microseconds */ #define MAX_FREE_LIST_BACKOFF_SLEEP 10000 @@ -1348,6 +1413,7 @@ buf_LRU_get_free_block( ulint flush_failures = 0; ibool mon_value_was = FALSE; ibool started_monitor = FALSE; + ulint started_ms = 0; ut_ad(!mutex_own(&buf_pool->LRU_list_mutex)); @@ -1356,7 +1422,24 @@ loop: buf_LRU_check_size_of_non_data_objects(buf_pool); /* If there is a block in the free list, take it */ - block = buf_LRU_get_free_only(buf_pool); + if (DBUG_EVALUATE_IF("simulate_lack_of_pages", true, false)) { + + block = NULL; + + if (srv_debug_monitor_printed) + DBUG_SET("-d,simulate_lack_of_pages"); + + } else if (DBUG_EVALUATE_IF("simulate_recovery_lack_of_pages", + recv_recovery_on, false)) { + + block = NULL; + + if (srv_debug_monitor_printed) + DBUG_SUICIDE(); + } else { + + block = buf_LRU_get_free_only(buf_pool); + } if (block) { @@ -1371,6 +1454,9 @@ loop: return(block); } + if (!started_ms) + started_ms = ut_time_ms(); + if (srv_empty_free_list_algorithm == SRV_EMPTY_FREE_LIST_BACKOFF && buf_lru_manager_is_active && (srv_shutdown_state == SRV_SHUTDOWN_NONE @@ -1408,11 +1494,17 @@ loop: : FREE_LIST_BACKOFF_LOW_PRIO_DIVIDER)); } - /* In case of backoff, do not ever attempt single page flushes - and wait for the cleaner to free some pages instead. */ + buf_LRU_handle_lack_of_free_blocks(n_iterations, started_ms, + flush_failures, + &mon_value_was, + &started_monitor); n_iterations++; + srv_stats.buf_pool_wait_free.add(n_iterations, 1); + + /* In case of backoff, do not ever attempt single page flushes + and wait for the cleaner to free some pages instead. */ goto loop; } else { @@ -1444,6 +1536,12 @@ loop: mutex_exit(&buf_pool->flush_state_mutex); + if (DBUG_EVALUATE_IF("simulate_recovery_lack_of_pages", true, false) + || DBUG_EVALUATE_IF("simulate_lack_of_pages", true, false)) { + + buf_pool->try_LRU_scan = false; + } + freed = FALSE; if (buf_pool->try_LRU_scan || n_iterations > 0) { @@ -1469,41 +1567,9 @@ loop: } - if (n_iterations > 20) { - ut_print_timestamp(stderr); - fprintf(stderr, - " InnoDB: Warning: difficult to find free blocks in\n" - "InnoDB: the buffer pool (%lu search iterations)!\n" - "InnoDB: %lu failed attempts to flush a page!" - " Consider\n" - "InnoDB: increasing the buffer pool size.\n" - "InnoDB: It is also possible that" - " in your Unix version\n" - "InnoDB: fsync is very slow, or" - " completely frozen inside\n" - "InnoDB: the OS kernel. Then upgrading to" - " a newer version\n" - "InnoDB: of your operating system may help." - " Look at the\n" - "InnoDB: number of fsyncs in diagnostic info below.\n" - "InnoDB: Pending flushes (fsync) log: %lu;" - " buffer pool: %lu\n" - "InnoDB: %lu OS file reads, %lu OS file writes," - " %lu OS fsyncs\n" - "InnoDB: Starting InnoDB Monitor to print further\n" - "InnoDB: diagnostics to the standard output.\n", - (ulong) n_iterations, - (ulong) flush_failures, - (ulong) fil_n_pending_log_flushes, - (ulong) fil_n_pending_tablespace_flushes, - (ulong) os_n_file_reads, (ulong) os_n_file_writes, - (ulong) os_n_fsyncs); - - mon_value_was = srv_print_innodb_monitor; - started_monitor = TRUE; - srv_print_innodb_monitor = TRUE; - os_event_set(srv_monitor_event); - } + buf_LRU_handle_lack_of_free_blocks(n_iterations, started_ms, + flush_failures, &mon_value_was, + &started_monitor); /* If we have scanned the whole LRU and still are unable to find a free block then we should sleep here to let the diff --git a/storage/xtradb/dict/dict0dict.cc b/storage/xtradb/dict/dict0dict.cc index 83e950b9334..13dd1d0c26c 100644 --- a/storage/xtradb/dict/dict0dict.cc +++ b/storage/xtradb/dict/dict0dict.cc @@ -6178,7 +6178,6 @@ dict_set_corrupted( row_mysql_lock_data_dictionary(trx); } - ut_ad(index); ut_ad(mutex_own(&dict_sys->mutex)); ut_ad(!dict_table_is_comp(dict_sys->sys_tables)); ut_ad(!dict_table_is_comp(dict_sys->sys_indexes)); diff --git a/storage/xtradb/dict/dict0stats.cc b/storage/xtradb/dict/dict0stats.cc index a4aa43651f8..c0a83c951a5 100644 --- a/storage/xtradb/dict/dict0stats.cc +++ b/storage/xtradb/dict/dict0stats.cc @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 2009, 2015, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2009, 2016, 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 @@ -1109,7 +1109,8 @@ dict_stats_analyze_index_level( them away) which brings non-determinism. We skip only leaf-level delete marks because delete marks on non-leaf level do not make sense. */ - if (level == 0 && + + if (level == 0 && srv_stats_include_delete_marked? 0: rec_get_deleted_flag( rec, page_is_comp(btr_pcur_get_page(&pcur)))) { @@ -1295,8 +1296,12 @@ enum page_scan_method_t { the given page and count the number of distinct ones, also ignore delete marked records */ - QUIT_ON_FIRST_NON_BORING/* quit when the first record that differs + QUIT_ON_FIRST_NON_BORING,/* quit when the first record that differs from its right neighbor is found */ + COUNT_ALL_NON_BORING_INCLUDE_DEL_MARKED/* scan all records on + the given page and count the number of + distinct ones, include delete marked + records */ }; /* @} */ @@ -1572,6 +1577,8 @@ dict_stats_analyze_index_below_cur( offsets_rec = dict_stats_scan_page( &rec, offsets1, offsets2, index, page, n_prefix, + srv_stats_include_delete_marked ? + COUNT_ALL_NON_BORING_INCLUDE_DEL_MARKED: COUNT_ALL_NON_BORING_AND_SKIP_DEL_MARKED, n_diff, n_external_pages); diff --git a/storage/xtradb/dict/dict0stats_bg.cc b/storage/xtradb/dict/dict0stats_bg.cc index 6f01c379776..b3c8ef018f7 100644 --- a/storage/xtradb/dict/dict0stats_bg.cc +++ b/storage/xtradb/dict/dict0stats_bg.cc @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 2012, 2016, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2017, MariaDB Corporation. 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 @@ -39,8 +40,9 @@ Created Apr 25, 2012 Vasil Dimov #define SHUTTING_DOWN() (srv_shutdown_state != SRV_SHUTDOWN_NONE) -/** Event to wake up the stats thread */ -UNIV_INTERN os_event_t dict_stats_event = NULL; +/** Event to wake up dict_stats_thread on dict_stats_recalc_pool_add() +or shutdown. Not protected by any mutex. */ +UNIV_INTERN os_event_t dict_stats_event; /** This mutex protects the "recalc_pool" variable. */ static ib_mutex_t recalc_pool_mutex; diff --git a/storage/xtradb/dyn/dyn0dyn.cc b/storage/xtradb/dyn/dyn0dyn.cc index 3ef5297a7c9..dd1f6863c14 100644 --- a/storage/xtradb/dyn/dyn0dyn.cc +++ b/storage/xtradb/dyn/dyn0dyn.cc @@ -40,7 +40,6 @@ dyn_array_add_block( mem_heap_t* heap; dyn_block_t* block; - ut_ad(arr); ut_ad(arr->magic_n == DYN_BLOCK_MAGIC_N); if (arr->heap == NULL) { diff --git a/storage/xtradb/fil/fil0fil.cc b/storage/xtradb/fil/fil0fil.cc index a7b0377d2a4..1a866a693ca 100644 --- a/storage/xtradb/fil/fil0fil.cc +++ b/storage/xtradb/fil/fil0fil.cc @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1995, 2016, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2014, 2017, MariaDB Corporation. 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 @@ -4993,15 +4994,12 @@ fil_extend_space_to_desired_size( byte* buf; ulint buf_size; ulint start_page_no; - ulint file_start_page_no; ulint page_size; - ulint pages_added; ibool success; ut_ad(!srv_read_only_mode); retry: - pages_added = 0; success = TRUE; fil_mutex_enter_and_prepare_for_io(space_id); @@ -5055,27 +5053,33 @@ retry: mutex_exit(&fil_system->mutex); start_page_no = space->size; - file_start_page_no = space->size - node->size; - + const ulint file_start_page_no = space->size - node->size; #ifdef HAVE_POSIX_FALLOCATE if (srv_use_posix_fallocate) { - os_offset_t start_offset = start_page_no * page_size; - os_offset_t n_pages = (size_after_extend - start_page_no); - os_offset_t len = n_pages * page_size; - - if (posix_fallocate(node->handle, start_offset, len) == -1) { - ib_logf(IB_LOG_LEVEL_ERROR, "preallocating file " - "space for file \'%s\' failed. Current size " - INT64PF ", desired size " INT64PF, - node->name, start_offset, len+start_offset); - os_file_handle_error_no_exit(node->name, "posix_fallocate", FALSE); - success = FALSE; - } else { - success = TRUE; + const os_offset_t start_offset + = os_offset_t(start_page_no - file_start_page_no) + * page_size; + const ulint n_pages + = size_after_extend - start_page_no; + const os_offset_t len = os_offset_t(n_pages) * page_size; + + int err; + do { + err = posix_fallocate(node->handle, start_offset, len); + } while (err == EINTR + && srv_shutdown_state == SRV_SHUTDOWN_NONE); + + success = !err; + if (!success) { + ib_logf(IB_LOG_LEVEL_ERROR, "extending file %s" + " from " INT64PF " to " INT64PF " bytes" + " failed with error %d", + node->name, start_offset, len + start_offset, + err); } DBUG_EXECUTE_IF("ib_os_aio_func_io_failure_28", - success = FALSE; errno = 28;os_has_said_disk_full = TRUE;); + success = FALSE; os_has_said_disk_full = TRUE;); mutex_enter(&fil_system->mutex); @@ -5095,14 +5099,24 @@ retry: } #endif +#ifdef _WIN32 + /* Write 1 page of zeroes at the desired end. */ + start_page_no = size_after_extend - 1; + buf_size = page_size; +#else /* Extend at most 64 pages at a time */ buf_size = ut_min(64, size_after_extend - start_page_no) * page_size; - buf2 = static_cast<byte*>(mem_alloc(buf_size + page_size)); +#endif + buf2 = static_cast<byte*>(calloc(1, buf_size + page_size)); + if (!buf2) { + ib_logf(IB_LOG_LEVEL_ERROR, "Cannot allocate " ULINTPF + " bytes to extend file", + buf_size + page_size); + success = FALSE; + } buf = static_cast<byte*>(ut_align(buf2, page_size)); - memset(buf, 0, buf_size); - - while (start_page_no < size_after_extend) { + while (success && start_page_no < size_after_extend) { ulint n_pages = ut_min(buf_size / page_size, size_after_extend - start_page_no); @@ -5111,55 +5125,47 @@ retry: = ((os_offset_t) (start_page_no - file_start_page_no)) * page_size; - const char* name = node->name == NULL ? space->name : node->name; - #ifdef UNIV_HOTBACKUP - success = os_file_write(name, node->handle, buf, + success = os_file_write(node->name, node->handle, buf, offset, page_size * n_pages); #else success = os_aio(OS_FILE_WRITE, OS_AIO_SYNC, - name, node->handle, buf, + node->name, node->handle, buf, offset, page_size * n_pages, NULL, NULL, space_id, NULL); #endif /* UNIV_HOTBACKUP */ DBUG_EXECUTE_IF("ib_os_aio_func_io_failure_28", - success = FALSE; errno = 28; os_has_said_disk_full = TRUE;); - - if (success) { - os_has_said_disk_full = FALSE; - } else { - /* Let us measure the size of the file to determine - how much we were able to extend it */ - os_offset_t size; - - size = os_file_get_size(node->handle); - ut_a(size != (os_offset_t) -1); + success = FALSE; os_has_said_disk_full = TRUE;); - n_pages = ((ulint) (size / page_size)) - - node->size - pages_added; + /* Let us measure the size of the file to determine + how much we were able to extend it */ + os_offset_t size = os_file_get_size(node->handle); + ut_a(size != (os_offset_t) -1); - pages_added += n_pages; - break; - } - - start_page_no += n_pages; - pages_added += n_pages; + start_page_no = (ulint) (size / page_size) + + file_start_page_no; } - mem_free(buf2); + free(buf2); mutex_enter(&fil_system->mutex); ut_a(node->being_extended); + ut_a(start_page_no - file_start_page_no >= node->size); - space->size += pages_added; - node->size += pages_added; + if (buf) { + ulint file_size = start_page_no - file_start_page_no; + space->size += file_size - node->size; + node->size = file_size; + } fil_node_complete_io(node, fil_system, OS_FILE_WRITE); /* At this point file has been extended */ +#ifdef HAVE_POSIX_FALLOCATE file_extended: +#endif /* HAVE_POSIX_FALLOCATE */ node->being_extended = FALSE; *actual_size = space->size; diff --git a/storage/xtradb/fsp/fsp0fsp.cc b/storage/xtradb/fsp/fsp0fsp.cc index 0bdb2ea0d4c..9292f84ab02 100644 --- a/storage/xtradb/fsp/fsp0fsp.cc +++ b/storage/xtradb/fsp/fsp0fsp.cc @@ -132,7 +132,7 @@ fsp_fill_free_list( ulint space, /*!< in: space */ fsp_header_t* header, /*!< in/out: space header */ mtr_t* mtr) /*!< in/out: mini-transaction */ - UNIV_COLD MY_ATTRIBUTE((nonnull)); + UNIV_COLD; /**********************************************************************//** Allocates a single free page from a segment. This function implements the intelligent allocation strategy which tries to minimize file space @@ -161,7 +161,7 @@ fseg_alloc_free_page_low( in which the page should be initialized. If init_mtr!=mtr, but the page is already latched in mtr, do not initialize the page. */ - MY_ATTRIBUTE((warn_unused_result, nonnull)); + MY_ATTRIBUTE((warn_unused_result)); #endif /* !UNIV_HOTBACKUP */ /**********************************************************************//** @@ -1067,8 +1067,6 @@ fsp_fill_free_list( ulint i; mtr_t ibuf_mtr; - ut_ad(header != NULL); - ut_ad(mtr != NULL); ut_ad(page_offset(header) == FSP_HEADER_OFFSET); /* Check if we can fill free list from above the free list limit */ @@ -1331,7 +1329,7 @@ Allocates a single free page from a space. The page is marked as used. @retval block, rw_lock_x_lock_count(&block->lock) == 1 if allocation succeeded (init_mtr == mtr, or the page was not previously freed in mtr) @retval block (not allocated or initialized) otherwise */ -static MY_ATTRIBUTE((nonnull, warn_unused_result)) +static MY_ATTRIBUTE((warn_unused_result)) buf_block_t* fsp_alloc_free_page( /*================*/ @@ -1351,9 +1349,6 @@ fsp_alloc_free_page( ulint page_no; ulint space_size; - ut_ad(mtr); - ut_ad(init_mtr); - header = fsp_get_space_header(space, zip_size, mtr); /* Get the hinted descriptor */ @@ -2372,7 +2367,6 @@ fseg_alloc_free_page_low( ibool success; ulint n; - ut_ad(mtr); ut_ad((direction >= FSP_UP) && (direction <= FSP_NO_DIR)); ut_ad(mach_read_from_4(seg_inode + FSEG_MAGIC_N) == FSEG_MAGIC_N_VALUE); @@ -2810,6 +2804,7 @@ try_again: } } else { ut_a(alloc_type == FSP_CLEANING); + reserve = 0; } success = fil_space_reserve_free_extents(space, n_free, n_ext); diff --git a/storage/xtradb/fts/fts0fts.cc b/storage/xtradb/fts/fts0fts.cc index 4c54afae8cd..c1f0b0bd5fe 100644 --- a/storage/xtradb/fts/fts0fts.cc +++ b/storage/xtradb/fts/fts0fts.cc @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 2011, 2016, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2016, MariaDB Corporation. 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 @@ -1934,6 +1935,8 @@ func_exit: /*************************************************************//** Wrapper function of fts_create_index_tables_low(), create auxiliary tables for an FTS index + +@see row_merge_create_fts_sort_index() @return: DB_SUCCESS or error code */ static dict_table_t* @@ -1965,13 +1968,12 @@ fts_create_one_index_table( (int)(field->col->prtype & DATA_MYSQL_TYPE_MASK), (uint) dtype_get_charset_coll(field->col->prtype)); - if (strcmp(charset->name, "latin1_swedish_ci") == 0) { - dict_mem_table_add_col(new_table, heap, "word", DATA_VARCHAR, - field->col->prtype, FTS_MAX_WORD_LEN); - } else { - dict_mem_table_add_col(new_table, heap, "word", DATA_VARMYSQL, - field->col->prtype, FTS_MAX_WORD_LEN); - } + dict_mem_table_add_col(new_table, heap, "word", + charset == &my_charset_latin1 + ? DATA_VARCHAR : DATA_VARMYSQL, + field->col->prtype, + FTS_MAX_WORD_LEN_IN_CHAR + * DATA_MBMAXLEN(field->col->mbminmaxlen)); dict_mem_table_add_col(new_table, heap, "first_doc_id", DATA_INT, DATA_NOT_NULL | DATA_UNSIGNED, diff --git a/storage/xtradb/fts/fts0opt.cc b/storage/xtradb/fts/fts0opt.cc index ea937c20752..cb30122adcb 100644 --- a/storage/xtradb/fts/fts0opt.cc +++ b/storage/xtradb/fts/fts0opt.cc @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 2007, 2016, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2016, MariaDB Corporation. 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 @@ -281,7 +282,7 @@ fts_zip_initialize( zip->last_big_block = 0; zip->word.f_len = 0; - memset(zip->word.f_str, 0, FTS_MAX_WORD_LEN); + *zip->word.f_str = 0; ib_vector_reset(zip->blocks); @@ -578,9 +579,6 @@ fts_zip_read_word( fts_zip_t* zip, /*!< in: Zip state + data */ fts_string_t* word) /*!< out: uncompressed word */ { -#ifdef UNIV_DEBUG - ulint i; -#endif short len = 0; void* null = NULL; byte* ptr = word->f_str; @@ -655,10 +653,9 @@ fts_zip_read_word( } } -#ifdef UNIV_DEBUG /* All blocks must be freed at end of inflate. */ if (zip->status != Z_OK) { - for (i = 0; i < ib_vector_size(zip->blocks); ++i) { + for (ulint i = 0; i < ib_vector_size(zip->blocks); ++i) { if (ib_vector_getp(zip->blocks, i)) { ut_free(ib_vector_getp(zip->blocks, i)); ib_vector_set(zip->blocks, i, &null); @@ -669,7 +666,6 @@ fts_zip_read_word( if (ptr != NULL) { ut_ad(word->f_len == strlen((char*) ptr)); } -#endif /* UNIV_DEBUG */ return(zip->status == Z_OK || zip->status == Z_STREAM_END ? ptr : NULL); } diff --git a/storage/xtradb/handler/ha_innodb.cc b/storage/xtradb/handler/ha_innodb.cc index ea94f90cbf8..9cb7d77ca84 100644 --- a/storage/xtradb/handler/ha_innodb.cc +++ b/storage/xtradb/handler/ha_innodb.cc @@ -1556,6 +1556,7 @@ static bool innobase_purge_archive_logs( } #endif + /*************************************************************//** Check for a valid value of innobase_commit_concurrency. @return 0 for valid innodb_commit_concurrency */ @@ -4069,19 +4070,13 @@ innobase_change_buffering_inited_ok: and consequently we do not need to know the ordering internally in InnoDB. */ - ut_a(0 == strcmp(my_charset_latin1.name, "latin1_swedish_ci")); srv_latin1_ordering = my_charset_latin1.sort_order; innobase_commit_concurrency_init_default(); -#ifndef EXTENDED_FOR_KILLIDLE - srv_kill_idle_transaction = 0; -#endif - #ifdef HAVE_POSIX_FALLOCATE srv_use_posix_fallocate = (ibool) innobase_use_fallocate; #endif - /* Do not enable backoff algorithm for small buffer pool. */ if (!innodb_empty_free_list_algorithm_backoff_allowed( static_cast<srv_empty_free_list_t>( @@ -7080,18 +7075,16 @@ get_innobase_type_from_mysql_type( case MYSQL_TYPE_VARCHAR: /* new >= 5.0.3 true VARCHAR */ if (field->binary()) { return(DATA_BINARY); - } else if (strcmp(field->charset()->name, - "latin1_swedish_ci") == 0) { + } else if (field->charset() == &my_charset_latin1) { return(DATA_VARCHAR); } else { return(DATA_VARMYSQL); } case MYSQL_TYPE_BIT: - case MYSQL_TYPE_STRING: if (field->binary()) { - + case MYSQL_TYPE_STRING: + if (field->binary()) { return(DATA_FIXBINARY); - } else if (strcmp(field->charset()->name, - "latin1_swedish_ci") == 0) { + } else if (field->charset() == &my_charset_latin1) { return(DATA_CHAR); } else { return(DATA_MYSQL); @@ -13543,9 +13536,13 @@ ha_innobase::info_low( /* If this table is already queued for background analyze, remove it from the queue as we are about to do the same */ - dict_mutex_enter_for_mysql(); - dict_stats_recalc_pool_del(ib_table); - dict_mutex_exit_for_mysql(); + if (!srv_read_only_mode) { + + dict_mutex_enter_for_mysql(); + dict_stats_recalc_pool_del( + ib_table); + dict_mutex_exit_for_mysql(); + } opt = DICT_STATS_RECALC_PERSISTENT; } else { @@ -16050,6 +16047,37 @@ ha_innobase::get_auto_increment( ulonglong col_max_value = innobase_get_int_col_max_value( table->next_number_field); + /** The following logic is needed to avoid duplicate key error + for autoincrement column. + + (1) InnoDB gives the current autoincrement value with respect + to increment and offset value. + + (2) Basically it does compute_next_insert_id() logic inside InnoDB + to avoid the current auto increment value changed by handler layer. + + (3) It is restricted only for insert operations. */ + + if (increment > 1 && thd_sql_command(user_thd) != SQLCOM_ALTER_TABLE + && autoinc < col_max_value) { + + ulonglong prev_auto_inc = autoinc; + + autoinc = ((autoinc - 1) + increment - offset)/ increment; + + autoinc = autoinc * increment + offset; + + /* If autoinc exceeds the col_max_value then reset + to old autoinc value. Because in case of non-strict + sql mode, boundary value is not considered as error. */ + + if (autoinc >= col_max_value) { + autoinc = prev_auto_inc; + } + + ut_ad(autoinc > 0); + } + /* Called for the first time ? */ if (trx->n_autoinc_rows == 0) { @@ -18524,32 +18552,6 @@ innobase_fts_retrieve_ranking( } /*********************************************************************** -functions for kill session of idle transaction */ -ibool -innobase_thd_is_idle( -/*=================*/ - const void* thd) /*!< in: thread handle (THD*) */ -{ -#ifdef EXTENDED_FOR_KILLIDLE - return(thd_command((const THD*) thd) == COM_SLEEP); -#else - return(FALSE); -#endif -} - -ib_int64_t -innobase_thd_get_start_time( -/*========================*/ - const void* thd) /*!< in: thread handle (THD*) */ -{ -#ifdef EXTENDED_FOR_KILLIDLE - return((ib_int64_t)thd_start_time((const THD*) thd)); -#else - return(0); /*dummy value*/ -#endif -} - -/*********************************************************************** Free the memory for the FTS handler */ UNIV_INTERN void @@ -18568,19 +18570,6 @@ innobase_fts_close_ranking( return; } -UNIV_INTERN -void -innobase_thd_kill( -/*==============*/ - ulong thd_id) -{ -#ifdef EXTENDED_FOR_KILLIDLE - thd_kill(thd_id); -#else - return; -#endif -} - /*********************************************************************** Find and Retrieve the FTS Relevance Ranking result for doc with doc_id of prebuilt->fts_doc_id @@ -18778,16 +18767,6 @@ innobase_fts_retrieve_docid( } -ulong -innobase_thd_get_thread_id( -/*=======================*/ - const void* thd) -{ - return(thd_get_thread_id((const THD*) thd)); -} - - - /*********************************************************************** Find and retrieve the size of the current result @return number of matching rows */ @@ -19389,6 +19368,12 @@ static MYSQL_SYSVAR_BOOL(doublewrite, innobase_use_doublewrite, "Disable with --skip-innodb-doublewrite.", NULL, NULL, TRUE); +static MYSQL_SYSVAR_BOOL(stats_include_delete_marked, + srv_stats_include_delete_marked, + PLUGIN_VAR_OPCMDARG, + "Scan delete marked records for persistent stat", + NULL, NULL, FALSE); + static MYSQL_SYSVAR_BOOL(use_atomic_writes, innobase_use_atomic_writes, PLUGIN_VAR_NOCMDARG | PLUGIN_VAR_READONLY, "Prevent partial page writes, via atomic writes (beta). " @@ -20031,13 +20016,6 @@ static MYSQL_SYSVAR_ULONG(force_recovery, srv_force_recovery, "Helps to save your data in case the disk image of the database becomes corrupt.", NULL, NULL, 0, 0, 6, 0); -#ifndef DBUG_OFF -static MYSQL_SYSVAR_ULONG(force_recovery_crash, srv_force_recovery_crash, - PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY, - "Kills the server during crash recovery.", - NULL, NULL, 0, 0, 10, 0); -#endif /* !DBUG_OFF */ - static MYSQL_SYSVAR_ULONG(page_size, srv_page_size, PLUGIN_VAR_OPCMDARG | PLUGIN_VAR_READONLY, "Page size to use for all InnoDB tablespaces.", @@ -20485,6 +20463,7 @@ static struct st_mysql_sys_var* innobase_system_variables[]= { MYSQL_SYSVAR(data_file_path), MYSQL_SYSVAR(data_home_dir), MYSQL_SYSVAR(doublewrite), + MYSQL_SYSVAR(stats_include_delete_marked), MYSQL_SYSVAR(api_enable_binlog), MYSQL_SYSVAR(api_enable_mdl), MYSQL_SYSVAR(api_disable_rowlock), @@ -20503,9 +20482,6 @@ static struct st_mysql_sys_var* innobase_system_variables[]= { MYSQL_SYSVAR(use_global_flush_log_at_trx_commit), MYSQL_SYSVAR(flush_method), MYSQL_SYSVAR(force_recovery), -#ifndef DBUG_OFF - MYSQL_SYSVAR(force_recovery_crash), -#endif /* !DBUG_OFF */ MYSQL_SYSVAR(ft_cache_size), MYSQL_SYSVAR(ft_total_cache_size), MYSQL_SYSVAR(ft_result_cache_limit), diff --git a/storage/xtradb/handler/ha_innodb.h b/storage/xtradb/handler/ha_innodb.h index 672894f6f51..da2f0f877f4 100644 --- a/storage/xtradb/handler/ha_innodb.h +++ b/storage/xtradb/handler/ha_innodb.h @@ -148,6 +148,8 @@ class ha_innobase: public handler int index_first(uchar * buf); int index_last(uchar * buf); + bool has_gap_locks() const { return true; } + int rnd_init(bool scan); int rnd_end(); int rnd_next(uchar *buf); diff --git a/storage/xtradb/handler/handler0alter.cc b/storage/xtradb/handler/handler0alter.cc index 2eea4aa4a1f..4db33fab1a6 100644 --- a/storage/xtradb/handler/handler0alter.cc +++ b/storage/xtradb/handler/handler0alter.cc @@ -1843,6 +1843,7 @@ innobase_fts_check_doc_id_index_in_def( return(FTS_NOT_EXIST_DOC_ID_INDEX); } + /*******************************************************************//** Create an index table where indexes are ordered as follows: @@ -1911,26 +1912,11 @@ innobase_create_key_defs( (only prefix/part of the column is indexed), MySQL will treat the index as a PRIMARY KEY unless the table already has one. */ - if (n_add > 0 && !new_primary && got_default_clust - && (key_info[*add].flags & HA_NOSAME) - && !(key_info[*add].flags & HA_KEY_HAS_PART_KEY_SEG)) { - uint key_part = key_info[*add].user_defined_key_parts; - - new_primary = true; + ut_ad(altered_table->s->primary_key == 0 + || altered_table->s->primary_key == MAX_KEY); - while (key_part--) { - const uint maybe_null - = key_info[*add].key_part[key_part].key_type - & FIELDFLAG_MAYBE_NULL; - DBUG_ASSERT(!maybe_null - == !key_info[*add].key_part[key_part]. - field->real_maybe_null()); - - if (maybe_null) { - new_primary = false; - break; - } - } + if (got_default_clust && !new_primary) { + new_primary = (altered_table->s->primary_key != MAX_KEY); } const bool rebuild = new_primary || add_fts_doc_id @@ -1949,8 +1935,14 @@ innobase_create_key_defs( ulint primary_key_number; if (new_primary) { - DBUG_ASSERT(n_add > 0); - primary_key_number = *add; + if (n_add == 0) { + DBUG_ASSERT(got_default_clust); + DBUG_ASSERT(altered_table->s->primary_key + == 0); + primary_key_number = 0; + } else { + primary_key_number = *add; + } } else if (got_default_clust) { /* Create the GEN_CLUST_INDEX */ index_def_t* index = indexdef++; @@ -3062,6 +3054,8 @@ prepare_inplace_alter_table_dict( ctx->add_cols = add_cols; } else { DBUG_ASSERT(!innobase_need_rebuild(ha_alter_info, old_table)); + DBUG_ASSERT(old_table->s->primary_key + == altered_table->s->primary_key); if (!ctx->new_table->fts && innobase_fulltext_exist(altered_table)) { @@ -4091,6 +4085,27 @@ found_col: add_fts_doc_id_idx, prebuilt)); } +/** Get the name of an erroneous key. +@param[in] error_key_num InnoDB number of the erroneus key +@param[in] ha_alter_info changes that were being performed +@param[in] table InnoDB table +@return the name of the erroneous key */ +static +const char* +get_error_key_name( + ulint error_key_num, + const Alter_inplace_info* ha_alter_info, + const dict_table_t* table) +{ + if (error_key_num == ULINT_UNDEFINED) { + return(FTS_DOC_ID_INDEX_NAME); + } else if (ha_alter_info->key_count == 0) { + return(dict_table_get_first_index(table)->name); + } else { + return(ha_alter_info->key_info_buffer[error_key_num].name); + } +} + /** Alter the table structure in-place with operations specified using Alter_inplace_info. The level of concurrency allowed during this operation depends @@ -4208,17 +4223,13 @@ oom: case DB_ONLINE_LOG_TOO_BIG: DBUG_ASSERT(ctx->online); my_error(ER_INNODB_ONLINE_LOG_TOO_BIG, MYF(0), - (prebuilt->trx->error_key_num == ULINT_UNDEFINED) - ? FTS_DOC_ID_INDEX_NAME - : ha_alter_info->key_info_buffer[ - prebuilt->trx->error_key_num].name); + get_error_key_name(prebuilt->trx->error_key_num, + ha_alter_info, prebuilt->table)); break; case DB_INDEX_CORRUPT: my_error(ER_INDEX_CORRUPT, MYF(0), - (prebuilt->trx->error_key_num == ULINT_UNDEFINED) - ? FTS_DOC_ID_INDEX_NAME - : ha_alter_info->key_info_buffer[ - prebuilt->trx->error_key_num].name); + get_error_key_name(prebuilt->trx->error_key_num, + ha_alter_info, prebuilt->table)); break; default: my_error_innodb(error, @@ -5031,7 +5042,6 @@ innobase_update_foreign_cache( "Foreign key constraints for table '%s'" " are loaded with charset check off", user_table->name); - } } @@ -5131,14 +5141,13 @@ commit_try_rebuild( DBUG_RETURN(true); case DB_ONLINE_LOG_TOO_BIG: my_error(ER_INNODB_ONLINE_LOG_TOO_BIG, MYF(0), - ha_alter_info->key_info_buffer[0].name); + get_error_key_name(err_key, ha_alter_info, + rebuilt_table)); DBUG_RETURN(true); case DB_INDEX_CORRUPT: my_error(ER_INDEX_CORRUPT, MYF(0), - (err_key == ULINT_UNDEFINED) - ? FTS_DOC_ID_INDEX_NAME - : ha_alter_info->key_info_buffer[err_key] - .name); + get_error_key_name(err_key, ha_alter_info, + rebuilt_table)); DBUG_RETURN(true); default: my_error_innodb(error, table_name, user_table->flags); diff --git a/storage/xtradb/handler/i_s.cc b/storage/xtradb/handler/i_s.cc index 59cad1c2e7a..f2686f1049b 100644 --- a/storage/xtradb/handler/i_s.cc +++ b/storage/xtradb/handler/i_s.cc @@ -8343,29 +8343,7 @@ i_s_innodb_changed_pages_fill( while(log_online_bitmap_iterator_next(&i) && (!srv_max_changed_pages || - output_rows_num < srv_max_changed_pages) && - /* - There is no need to compare both start LSN and end LSN fields - with maximum value. It's enough to compare only start LSN. - Example: - - max_lsn = 100 - \\\\\\\\\\\\\\\\\\\\\\\\\|\\\\\\\\ - Query 1 - I------I I-------I I-------------I I----I - ////////////////// | - Query 2 - 1 2 3 4 - - Query 1: - SELECT * FROM INNODB_CHANGED_PAGES WHERE start_lsn < 100 - will select 1,2,3 bitmaps - Query 2: - SELECT * FROM INNODB_CHANGED_PAGES WHERE end_lsn < 100 - will select 1,2 bitmaps - - The condition start_lsn <= 100 will be false after reading - 1,2,3 bitmaps which suits for both cases. - */ - LOG_BITMAP_ITERATOR_START_LSN(i) <= max_lsn) + output_rows_num < srv_max_changed_pages)) { if (!LOG_BITMAP_ITERATOR_PAGE_CHANGED(i)) continue; diff --git a/storage/xtradb/include/btr0cur.h b/storage/xtradb/include/btr0cur.h index f14288d0f89..19ab7d8bc3a 100644 --- a/storage/xtradb/include/btr0cur.h +++ b/storage/xtradb/include/btr0cur.h @@ -294,11 +294,7 @@ btr_cur_update_alloc_zip_func( false=update-in-place */ mtr_t* mtr, /*!< in/out: mini-transaction */ trx_t* trx) /*!< in: NULL or transaction */ -#ifdef UNIV_DEBUG - MY_ATTRIBUTE((nonnull (1, 2, 3, 4, 7), warn_unused_result)); -#else - MY_ATTRIBUTE((nonnull (1, 2, 3, 6), warn_unused_result)); -#endif + MY_ATTRIBUTE((warn_unused_result)); #ifdef UNIV_DEBUG # define btr_cur_update_alloc_zip(page_zip,cursor,index,offsets,len,cr,mtr,trx) \ @@ -428,7 +424,7 @@ btr_cur_del_mark_set_clust_rec( const ulint* offsets,/*!< in: rec_get_offsets(rec) */ que_thr_t* thr, /*!< in: query thread */ mtr_t* mtr) /*!< in/out: mini-transaction */ - MY_ATTRIBUTE((nonnull, warn_unused_result)); + MY_ATTRIBUTE((warn_unused_result)); /***********************************************************//** Sets a secondary index record delete mark to TRUE or FALSE. @return DB_SUCCESS, DB_LOCK_WAIT, or error number */ @@ -441,7 +437,7 @@ btr_cur_del_mark_set_sec_rec( ibool val, /*!< in: value to set */ que_thr_t* thr, /*!< in: query thread */ mtr_t* mtr) /*!< in/out: mini-transaction */ - MY_ATTRIBUTE((nonnull, warn_unused_result)); + MY_ATTRIBUTE((warn_unused_result)); /*************************************************************//** Tries to compress a page of the tree if it seems useful. It is assumed that mtr holds an x-latch on the tree and on the cursor page. To avoid @@ -609,8 +605,7 @@ btr_cur_disown_inherited_fields( dict_index_t* index, /*!< in: index of the page */ const ulint* offsets,/*!< in: array returned by rec_get_offsets() */ const upd_t* update, /*!< in: update vector */ - mtr_t* mtr) /*!< in/out: mini-transaction */ - MY_ATTRIBUTE((nonnull(2,3,4,5,6))); + mtr_t* mtr); /*!< in/out: mini-transaction */ /** Operation code for btr_store_big_rec_extern_fields(). */ enum blob_op { @@ -655,7 +650,7 @@ btr_store_big_rec_extern_fields( mtr_t* btr_mtr, /*!< in: mtr containing the latches to the clustered index */ enum blob_op op) /*! in: operation code */ - MY_ATTRIBUTE((nonnull, warn_unused_result)); + MY_ATTRIBUTE((warn_unused_result)); /*******************************************************************//** Frees the space in an externally stored field to the file space @@ -751,8 +746,7 @@ btr_push_update_extern_fields( /*==========================*/ dtuple_t* tuple, /*!< in/out: data tuple */ const upd_t* update, /*!< in: update vector */ - mem_heap_t* heap) /*!< in: memory heap */ - MY_ATTRIBUTE((nonnull)); + mem_heap_t* heap); /*!< in: memory heap */ /***********************************************************//** Sets a secondary index record's delete mark to the given value. This function is only used by the insert buffer merge mechanism. */ diff --git a/storage/xtradb/include/btr0sea.h b/storage/xtradb/include/btr0sea.h index 8f438bf640e..bfe2c43defb 100644 --- a/storage/xtradb/include/btr0sea.h +++ b/storage/xtradb/include/btr0sea.h @@ -200,7 +200,7 @@ hash_table_t* btr_search_get_hash_table( /*======================*/ const dict_index_t* index) /*!< in: index */ - MY_ATTRIBUTE((pure,warn_unused_result)); + MY_ATTRIBUTE((warn_unused_result)); /********************************************************************//** Returns the adaptive hash index latch for a given index key. @@ -210,7 +210,7 @@ prio_rw_lock_t* btr_search_get_latch( /*=================*/ const dict_index_t* index) /*!< in: index */ - MY_ATTRIBUTE((pure,warn_unused_result)); + MY_ATTRIBUTE((warn_unused_result)); /*********************************************************************//** Returns the AHI partition number corresponding to a given index ID. */ @@ -227,8 +227,7 @@ UNIV_INLINE void btr_search_index_init( /*===============*/ - dict_index_t* index) /*!< in: index */ - MY_ATTRIBUTE((nonnull)); + dict_index_t* index); /*!< in: index */ /********************************************************************//** Latches all adaptive hash index latches in exclusive mode. */ diff --git a/storage/xtradb/include/btr0sea.ic b/storage/xtradb/include/btr0sea.ic index 3cbcff75f31..e963d8a8449 100644 --- a/storage/xtradb/include/btr0sea.ic +++ b/storage/xtradb/include/btr0sea.ic @@ -90,7 +90,6 @@ btr_search_get_hash_table( /*======================*/ const dict_index_t* index) /*!< in: index */ { - ut_ad(index); ut_ad(index->search_table); return(index->search_table); @@ -105,7 +104,6 @@ btr_search_get_latch( /*=================*/ const dict_index_t* index) /*!< in: index */ { - ut_ad(index); ut_ad(index->search_latch >= btr_search_latch_arr && index->search_latch < btr_search_latch_arr + btr_search_index_num); @@ -132,8 +130,6 @@ btr_search_index_init( /*===============*/ dict_index_t* index) /*!< in: index */ { - ut_ad(index); - index->search_latch = &btr_search_latch_arr[btr_search_get_key(index->id)]; index->search_table = diff --git a/storage/xtradb/include/buf0buddy.ic b/storage/xtradb/include/buf0buddy.ic index 9bc8e9e8762..a5fb510dd19 100644 --- a/storage/xtradb/include/buf0buddy.ic +++ b/storage/xtradb/include/buf0buddy.ic @@ -50,7 +50,7 @@ buf_buddy_alloc_low( allocated from the LRU list and buf_pool->LRU_list_mutex was temporarily released */ - MY_ATTRIBUTE((malloc, nonnull)); + MY_ATTRIBUTE((malloc)); /**********************************************************************//** Deallocate a block. */ diff --git a/storage/xtradb/include/buf0buf.h b/storage/xtradb/include/buf0buf.h index 8110fbc4808..18c1cb4ff9d 100644 --- a/storage/xtradb/include/buf0buf.h +++ b/storage/xtradb/include/buf0buf.h @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1995, 2016, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2017, MariaDB Corporation. 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 @@ -242,8 +243,7 @@ buf_relocate( buf_page_t* bpage, /*!< in/out: control block being relocated; buf_page_get_state(bpage) must be BUF_BLOCK_ZIP_DIRTY or BUF_BLOCK_ZIP_PAGE */ - buf_page_t* dpage) /*!< in/out: destination control block */ - MY_ATTRIBUTE((nonnull)); + buf_page_t* dpage); /*!< in/out: destination control block */ /*********************************************************************//** Gets the current size of buffer buf_pool in bytes. @return size in bytes */ @@ -737,7 +737,7 @@ buf_page_print( ulint flags) /*!< in: 0 or BUF_PAGE_PRINT_NO_CRASH or BUF_PAGE_PRINT_NO_FULL */ - UNIV_COLD MY_ATTRIBUTE((nonnull)); + UNIV_COLD; /********************************************************************//** Decompress a block. @return TRUE if successful */ @@ -1949,7 +1949,10 @@ struct buf_pool_t{ os_event_t no_flush[BUF_FLUSH_N_TYPES]; /*!< this is in the set state when there is no flush batch - of the given type running */ + of the given type running; + os_event_set() and os_event_reset() + are protected by + buf_pool_t::flush_state_mutex */ ib_rbt_t* flush_rbt; /*!< a red-black tree is used exclusively during recovery to speed up insertions in the diff --git a/storage/xtradb/include/buf0dblwr.h b/storage/xtradb/include/buf0dblwr.h index a62a6400d97..5582778825c 100644 --- a/storage/xtradb/include/buf0dblwr.h +++ b/storage/xtradb/include/buf0dblwr.h @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1995, 2014, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2017, MariaDB Corporation. 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 @@ -134,11 +135,13 @@ struct buf_dblwr_t{ ulint b_reserved;/*!< number of slots currently reserved for batch flush. */ os_event_t b_event;/*!< event where threads wait for a - batch flush to end. */ + batch flush to end; + os_event_set() and os_event_reset() + are protected by buf_dblwr_t::mutex */ ulint s_reserved;/*!< number of slots currently reserved for single page flushes. */ os_event_t s_event;/*!< event where threads wait for a - single page flush slot. */ + single page flush slot. Protected by mutex. */ bool* in_use; /*!< flag used to indicate if a slot is in use. Only used for single page flushes. */ diff --git a/storage/xtradb/include/dict0dict.h b/storage/xtradb/include/dict0dict.h index a85f6f1eef9..a2481fbad6f 100644 --- a/storage/xtradb/include/dict0dict.h +++ b/storage/xtradb/include/dict0dict.h @@ -747,7 +747,7 @@ ulint dict_index_is_clust( /*================*/ const dict_index_t* index) /*!< in: index */ - MY_ATTRIBUTE((nonnull, pure, warn_unused_result)); + MY_ATTRIBUTE((warn_unused_result)); /********************************************************************//** Check whether the index is unique. @return nonzero for unique index, zero for other indexes */ @@ -756,7 +756,7 @@ ulint dict_index_is_unique( /*=================*/ const dict_index_t* index) /*!< in: index */ - MY_ATTRIBUTE((nonnull, pure, warn_unused_result)); + MY_ATTRIBUTE((warn_unused_result)); /********************************************************************//** Check whether the index is the insert buffer tree. @return nonzero for insert buffer, zero for other indexes */ @@ -765,7 +765,7 @@ ulint dict_index_is_ibuf( /*===============*/ const dict_index_t* index) /*!< in: index */ - MY_ATTRIBUTE((nonnull, pure, warn_unused_result)); + MY_ATTRIBUTE((warn_unused_result)); /********************************************************************//** Check whether the index is a secondary index or the insert buffer tree. @return nonzero for insert buffer, zero for other indexes */ @@ -774,7 +774,7 @@ ulint dict_index_is_sec_or_ibuf( /*======================*/ const dict_index_t* index) /*!< in: index */ - MY_ATTRIBUTE((nonnull, pure, warn_unused_result)); + MY_ATTRIBUTE((warn_unused_result)); /************************************************************************ Gets the all the FTS indexes for the table. NOTE: must not be called for @@ -796,7 +796,7 @@ ulint dict_table_get_n_user_cols( /*=======================*/ const dict_table_t* table) /*!< in: table */ - MY_ATTRIBUTE((nonnull, pure, warn_unused_result)); + MY_ATTRIBUTE((warn_unused_result)); /********************************************************************//** Gets the number of system columns in a table in the dictionary cache. @return number of system (e.g., ROW_ID) columns of a table */ @@ -815,7 +815,7 @@ ulint dict_table_get_n_cols( /*==================*/ const dict_table_t* table) /*!< in: table */ - MY_ATTRIBUTE((nonnull, pure, warn_unused_result)); + MY_ATTRIBUTE((warn_unused_result)); /********************************************************************//** Gets the approximately estimated number of rows in the table. @return estimated number of rows */ @@ -1745,7 +1745,7 @@ ulint dict_index_is_corrupted( /*====================*/ const dict_index_t* index) /*!< in: index */ - MY_ATTRIBUTE((nonnull, warn_unused_result)); + MY_ATTRIBUTE((warn_unused_result)); #endif /* !UNIV_HOTBACKUP */ /**********************************************************************//** @@ -1758,7 +1758,7 @@ dict_set_corrupted( dict_index_t* index, /*!< in/out: index */ trx_t* trx, /*!< in/out: transaction */ const char* ctx) /*!< in: context */ - UNIV_COLD MY_ATTRIBUTE((nonnull)); + UNIV_COLD; /**********************************************************************//** Flags an index corrupted in the data dictionary cache only. This @@ -1769,8 +1769,7 @@ void dict_set_corrupted_index_cache_only( /*================================*/ dict_index_t* index, /*!< in/out: index */ - dict_table_t* table) /*!< in/out: table */ - MY_ATTRIBUTE((nonnull)); + dict_table_t* table); /*!< in/out: table */ /**********************************************************************//** Flags a table with specified space_id corrupted in the table dictionary diff --git a/storage/xtradb/include/dict0dict.ic b/storage/xtradb/include/dict0dict.ic index 58a9ef4d65d..efc2c3f0e68 100644 --- a/storage/xtradb/include/dict0dict.ic +++ b/storage/xtradb/include/dict0dict.ic @@ -266,7 +266,6 @@ dict_index_is_clust( /*================*/ const dict_index_t* index) /*!< in: index */ { - ut_ad(index); ut_ad(index->magic_n == DICT_INDEX_MAGIC_N); return(index->type & DICT_CLUSTERED); @@ -280,7 +279,6 @@ dict_index_is_unique( /*=================*/ const dict_index_t* index) /*!< in: index */ { - ut_ad(index); ut_ad(index->magic_n == DICT_INDEX_MAGIC_N); return(index->type & DICT_UNIQUE); @@ -295,7 +293,6 @@ dict_index_is_ibuf( /*===============*/ const dict_index_t* index) /*!< in: index */ { - ut_ad(index); ut_ad(index->magic_n == DICT_INDEX_MAGIC_N); return(index->type & DICT_IBUF); @@ -327,7 +324,6 @@ dict_index_is_sec_or_ibuf( { ulint type; - ut_ad(index); ut_ad(index->magic_n == DICT_INDEX_MAGIC_N); type = index->type; @@ -345,7 +341,6 @@ dict_table_get_n_user_cols( /*=======================*/ const dict_table_t* table) /*!< in: table */ { - ut_ad(table); ut_ad(table->magic_n == DICT_TABLE_MAGIC_N); return(table->n_cols - DATA_N_SYS_COLS); @@ -377,7 +372,6 @@ dict_table_get_n_cols( /*==================*/ const dict_table_t* table) /*!< in: table */ { - ut_ad(table); ut_ad(table->magic_n == DICT_TABLE_MAGIC_N); return(table->n_cols); @@ -1373,7 +1367,6 @@ dict_index_is_corrupted( /*====================*/ const dict_index_t* index) /*!< in: index */ { - ut_ad(index); ut_ad(index->magic_n == DICT_INDEX_MAGIC_N); return((index->type & DICT_CORRUPT) diff --git a/storage/xtradb/include/dict0mem.h b/storage/xtradb/include/dict0mem.h index bbd05dae551..771864c89ba 100644 --- a/storage/xtradb/include/dict0mem.h +++ b/storage/xtradb/include/dict0mem.h @@ -138,7 +138,7 @@ allows InnoDB to update_create_info() accordingly. */ + DICT_TF_WIDTH_DATA_DIR) /** A mask of all the known/used bits in table flags */ -#define DICT_TF_BIT_MASK (~(~0 << DICT_TF_BITS)) +#define DICT_TF_BIT_MASK (~(~0U << DICT_TF_BITS)) /** Zero relative shift position of the COMPACT field */ #define DICT_TF_POS_COMPACT 0 diff --git a/storage/xtradb/include/dict0stats_bg.h b/storage/xtradb/include/dict0stats_bg.h index 82cd2b468b9..1973380d197 100644 --- a/storage/xtradb/include/dict0stats_bg.h +++ b/storage/xtradb/include/dict0stats_bg.h @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 2012, 2016, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2017, MariaDB Corporation. 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 @@ -32,7 +33,8 @@ Created Apr 26, 2012 Vasil Dimov #include "os0sync.h" /* os_event_t */ #include "os0thread.h" /* DECLARE_THREAD */ -/** Event to wake up the stats thread */ +/** Event to wake up dict_stats_thread on dict_stats_recalc_pool_add() +or shutdown. Not protected by any mutex. */ extern os_event_t dict_stats_event; /*****************************************************************//** diff --git a/storage/xtradb/include/dyn0dyn.h b/storage/xtradb/include/dyn0dyn.h index 1bd10b6bf58..20963a1472b 100644 --- a/storage/xtradb/include/dyn0dyn.h +++ b/storage/xtradb/include/dyn0dyn.h @@ -46,9 +46,8 @@ UNIV_INLINE dyn_array_t* dyn_array_create( /*=============*/ - dyn_array_t* arr) /*!< in/out memory buffer of + dyn_array_t* arr); /*!< in/out memory buffer of size sizeof(dyn_array_t) */ - MY_ATTRIBUTE((nonnull)); /************************************************************//** Frees a dynamic array. */ UNIV_INLINE @@ -69,7 +68,7 @@ dyn_array_open( dyn_array_t* arr, /*!< in: dynamic array */ ulint size) /*!< in: size in bytes of the buffer; MUST be smaller than DYN_ARRAY_DATA_SIZE! */ - MY_ATTRIBUTE((nonnull, warn_unused_result)); + MY_ATTRIBUTE((warn_unused_result)); /*********************************************************************//** Closes the buffer returned by dyn_array_open. */ UNIV_INLINE @@ -77,8 +76,7 @@ void dyn_array_close( /*============*/ dyn_array_t* arr, /*!< in: dynamic array */ - const byte* ptr) /*!< in: end of used space */ - MY_ATTRIBUTE((nonnull)); + const byte* ptr); /*!< in: end of used space */ /*********************************************************************//** Makes room on top of a dyn array and returns a pointer to the added element. The caller must copy the element to @@ -90,7 +88,7 @@ dyn_array_push( /*===========*/ dyn_array_t* arr, /*!< in/out: dynamic array */ ulint size) /*!< in: size in bytes of the element */ - MY_ATTRIBUTE((nonnull, warn_unused_result)); + MY_ATTRIBUTE((warn_unused_result)); /************************************************************//** Returns pointer to an element in dyn array. @return pointer to element */ @@ -101,7 +99,7 @@ dyn_array_get_element( const dyn_array_t* arr, /*!< in: dyn array */ ulint pos) /*!< in: position of element in bytes from array start */ - MY_ATTRIBUTE((nonnull, warn_unused_result)); + MY_ATTRIBUTE((warn_unused_result)); /************************************************************//** Returns the size of stored data in a dyn array. @return data size in bytes */ @@ -110,7 +108,7 @@ ulint dyn_array_get_data_size( /*====================*/ const dyn_array_t* arr) /*!< in: dyn array */ - MY_ATTRIBUTE((nonnull, warn_unused_result, pure)); + MY_ATTRIBUTE((warn_unused_result)); /************************************************************//** Gets the first block in a dyn array. @param arr dyn array @@ -144,7 +142,7 @@ ulint dyn_block_get_used( /*===============*/ const dyn_block_t* block) /*!< in: dyn array block */ - MY_ATTRIBUTE((nonnull, warn_unused_result, pure)); + MY_ATTRIBUTE((warn_unused_result)); /********************************************************************//** Gets pointer to the start of data in a dyn array block. @return pointer to data */ @@ -153,7 +151,7 @@ byte* dyn_block_get_data( /*===============*/ const dyn_block_t* block) /*!< in: dyn array block */ - MY_ATTRIBUTE((nonnull, warn_unused_result, pure)); + MY_ATTRIBUTE((warn_unused_result)); /********************************************************//** Pushes n bytes to a dyn array. */ UNIV_INLINE diff --git a/storage/xtradb/include/dyn0dyn.ic b/storage/xtradb/include/dyn0dyn.ic index f18f2e6dff9..6e97649245e 100644 --- a/storage/xtradb/include/dyn0dyn.ic +++ b/storage/xtradb/include/dyn0dyn.ic @@ -36,7 +36,7 @@ dyn_block_t* dyn_array_add_block( /*================*/ dyn_array_t* arr) /*!< in/out: dyn array */ - MY_ATTRIBUTE((nonnull, warn_unused_result)); + MY_ATTRIBUTE((warn_unused_result)); /********************************************************************//** Gets the number of used bytes in a dyn array block. @@ -47,8 +47,6 @@ dyn_block_get_used( /*===============*/ const dyn_block_t* block) /*!< in: dyn array block */ { - ut_ad(block); - return((block->used) & ~DYN_BLOCK_FULL_FLAG); } @@ -76,7 +74,6 @@ dyn_array_create( dyn_array_t* arr) /*!< in/out: memory buffer of size sizeof(dyn_array_t) */ { - ut_ad(arr); #if DYN_ARRAY_DATA_SIZE >= DYN_BLOCK_FULL_FLAG # error "DYN_ARRAY_DATA_SIZE >= DYN_BLOCK_FULL_FLAG" #endif @@ -119,7 +116,6 @@ dyn_array_push( dyn_block_t* block; ulint used; - ut_ad(arr); ut_ad(arr->magic_n == DYN_BLOCK_MAGIC_N); ut_ad(size <= DYN_ARRAY_DATA_SIZE); ut_ad(size); @@ -159,7 +155,6 @@ dyn_array_open( { dyn_block_t* block; - ut_ad(arr); ut_ad(arr->magic_n == DYN_BLOCK_MAGIC_N); ut_ad(size <= DYN_ARRAY_DATA_SIZE); ut_ad(size); @@ -195,7 +190,6 @@ dyn_array_close( { dyn_block_t* block; - ut_ad(arr); ut_ad(arr->magic_n == DYN_BLOCK_MAGIC_N); block = dyn_array_get_last_block(arr); @@ -222,7 +216,6 @@ dyn_array_get_element( { const dyn_block_t* block; - ut_ad(arr); ut_ad(arr->magic_n == DYN_BLOCK_MAGIC_N); /* Get the first array block */ @@ -260,7 +253,6 @@ dyn_array_get_data_size( const dyn_block_t* block; ulint sum = 0; - ut_ad(arr); ut_ad(arr->magic_n == DYN_BLOCK_MAGIC_N); if (arr->heap == NULL) { diff --git a/storage/xtradb/include/fil0fil.h b/storage/xtradb/include/fil0fil.h index 2581384ff22..3023b29b793 100644 --- a/storage/xtradb/include/fil0fil.h +++ b/storage/xtradb/include/fil0fil.h @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1995, 2016, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2017, MariaDB Corporation. 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 @@ -199,7 +200,9 @@ struct fil_node_t { ibool open; /*!< TRUE if file open */ os_file_t handle; /*!< OS handle to the file, if file open */ os_event_t sync_event;/*!< Condition event to group and - serialize calls to fsync */ + serialize calls to fsync; + os_event_set() and os_event_reset() + are protected by fil_system_t::mutex */ ibool is_raw_disk;/*!< TRUE if the 'file' is actually a raw device or a raw disk partition */ ulint size; /*!< size of the file in database pages, 0 if diff --git a/storage/xtradb/include/fsp0fsp.h b/storage/xtradb/include/fsp0fsp.h index 099cb8edc14..0097537e4d6 100644 --- a/storage/xtradb/include/fsp0fsp.h +++ b/storage/xtradb/include/fsp0fsp.h @@ -61,7 +61,7 @@ is found in a remote location, not the default data directory. */ + FSP_FLAGS_WIDTH_DATA_DIR) /** A mask of all the known/used bits in tablespace flags */ -#define FSP_FLAGS_MASK (~(~0 << FSP_FLAGS_WIDTH)) +#define FSP_FLAGS_MASK (~(~0U << FSP_FLAGS_WIDTH)) /** Zero relative shift position of the POST_ANTELOPE field */ #define FSP_FLAGS_POS_POST_ANTELOPE 0 diff --git a/storage/xtradb/include/fsp0types.h b/storage/xtradb/include/fsp0types.h index 94fd908ab0c..9734f4c8abc 100644 --- a/storage/xtradb/include/fsp0types.h +++ b/storage/xtradb/include/fsp0types.h @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1995, 2009, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2017, MariaDB Corporation. 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 diff --git a/storage/xtradb/include/fts0fts.h b/storage/xtradb/include/fts0fts.h index 3e2f359bbeb..7aa7055640c 100644 --- a/storage/xtradb/include/fts0fts.h +++ b/storage/xtradb/include/fts0fts.h @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 2011, 2016, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2016, MariaDB Corporation. 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 @@ -366,8 +367,8 @@ extern ulong fts_min_token_size; need a sync to free some memory */ extern bool fts_need_sync; -/** Maximum possible Fulltext word length */ -#define FTS_MAX_WORD_LEN HA_FT_MAXBYTELEN +/** Maximum possible Fulltext word length in bytes (assuming mbmaxlen=4) */ +#define FTS_MAX_WORD_LEN (HA_FT_MAXCHARLEN * 4) /** Maximum possible Fulltext word length (in characters) */ #define FTS_MAX_WORD_LEN_IN_CHAR HA_FT_MAXCHARLEN diff --git a/storage/xtradb/include/fts0types.h b/storage/xtradb/include/fts0types.h index e495fe72a60..0dad75d8f1b 100644 --- a/storage/xtradb/include/fts0types.h +++ b/storage/xtradb/include/fts0types.h @@ -126,7 +126,9 @@ struct fts_sync_t { bool in_progress; /*!< flag whether sync is in progress.*/ bool unlock_cache; /*!< flag whether unlock cache when write fts node */ - os_event_t event; /*!< sync finish event */ + os_event_t event; /*!< sync finish event; + only os_event_set() and os_event_wait() + are used */ }; /** The cache for the FTS system. It is a memory-based inverted index diff --git a/storage/xtradb/include/lock0lock.h b/storage/xtradb/include/lock0lock.h index 45955f3a7ea..698780a9a9e 100644 --- a/storage/xtradb/include/lock0lock.h +++ b/storage/xtradb/include/lock0lock.h @@ -943,7 +943,12 @@ struct lock_sys_t{ srv_slot_t* waiting_threads; /*!< Array of user threads suspended while waiting for locks within InnoDB, protected - by the lock_sys->wait_mutex */ + by the lock_sys->wait_mutex; + os_event_set() and + os_event_reset() on + waiting_threads[]->event + are protected by + trx_t::mutex */ srv_slot_t* last_slot; /*!< highest slot ever used in the waiting_threads array, protected by @@ -956,10 +961,11 @@ struct lock_sys_t{ ulint n_lock_max_wait_time; /*!< Max wait time */ - os_event_t timeout_event; /*!< Set to the event that is - created in the lock wait monitor - thread. A value of 0 means the - thread is not active */ + os_event_t timeout_event; /*!< An event waited for by + lock_wait_timeout_thread. + Not protected by a mutex, + but the waits are timed. + Signaled on shutdown only. */ bool timeout_thread_active; /*!< True if the timeout thread is running */ diff --git a/storage/xtradb/include/log0log.h b/storage/xtradb/include/log0log.h index f716e534b01..259c4301de9 100644 --- a/storage/xtradb/include/log0log.h +++ b/storage/xtradb/include/log0log.h @@ -2,6 +2,7 @@ Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved. Copyright (c) 2009, Google Inc. +Copyright (c) 2017, MariaDB Corporation. All Rights Reserved. Portions of this file contain modifications contributed and copyrighted by Google, Inc. Those modifications are gratefully acknowledged and are described @@ -924,10 +925,8 @@ struct log_t{ be 'flush_or_write'! */ os_event_t no_flush_event; /*!< this event is in the reset state when a flush or a write is running; - a thread should wait for this without - owning the log mutex, but NOTE that - to set or reset this event, the - thread MUST own the log mutex! */ + os_event_set() and os_event_reset() + are protected by log_sys_t::mutex */ ibool one_flushed; /*!< during a flush, this is first FALSE and becomes TRUE when one log group has been @@ -936,11 +935,9 @@ struct log_t{ flush or write has not yet completed for any log group; e.g., this means that a transaction has been committed - when this is set; a thread should wait - for this without owning the log mutex, - but NOTE that to set or reset this - event, the thread MUST own the log - mutex! */ + when this is set; + os_event_set() and os_event_reset() + are protected by log_sys_t::mutex */ ulint n_log_ios; /*!< number of log i/os initiated thus far */ ulint n_log_ios_old; /*!< number of log i/o's at the @@ -1026,9 +1023,9 @@ struct log_t{ byte* archive_buf_ptr;/*!< unaligned archived_buf */ byte* archive_buf; /*!< log segment is written to the archive from this buffer */ - os_event_t archiving_on; /*!< if archiving has been stopped, - a thread can wait for this event to - become signaled */ + os_event_t archiving_on; /*!< if archiving has been stopped; + os_event_set() and os_event_reset() + are protected by log_sys_t::mutex */ /* @} */ #endif /* UNIV_LOG_ARCHIVE */ lsn_t tracked_lsn; /*!< log tracking has advanced to this diff --git a/storage/xtradb/include/log0online.h b/storage/xtradb/include/log0online.h index 5706f3af4b0..722336dd6b4 100644 --- a/storage/xtradb/include/log0online.h +++ b/storage/xtradb/include/log0online.h @@ -38,19 +38,25 @@ log_online_bitmap_file_range_t; /** An iterator over changed page info */ typedef struct log_bitmap_iterator_struct log_bitmap_iterator_t; -/*********************************************************************//** -Initializes the online log following subsytem. */ +/** Initialize the constant part of the log tracking subsystem */ +UNIV_INTERN +void +log_online_init(void); + +/** Initialize the dynamic part of the log tracking subsystem */ UNIV_INTERN void log_online_read_init(void); -/*=======================*/ -/*********************************************************************//** -Shuts down the online log following subsystem. */ +/** Shut down the dynamic part of the log tracking subsystem */ UNIV_INTERN void log_online_read_shutdown(void); -/*===========================*/ + +/** Shut down the constant part of the log tracking subsystem */ +UNIV_INTERN +void +log_online_shutdown(void); /*********************************************************************//** Reads and parses the redo log up to last checkpoint LSN to build the changed @@ -147,6 +153,8 @@ struct log_online_bitmap_file_range_struct { /** Struct for an iterator through all bits of changed pages bitmap blocks */ struct log_bitmap_iterator_struct { + lsn_t max_lsn; /*!< End LSN of the + range */ ibool failed; /*!< Has the iteration stopped prematurely */ log_online_bitmap_file_range_t in_files; /*!< The bitmap files diff --git a/storage/xtradb/include/mach0data.h b/storage/xtradb/include/mach0data.h index 9859def0adc..2e16634a6c2 100644 --- a/storage/xtradb/include/mach0data.h +++ b/storage/xtradb/include/mach0data.h @@ -53,7 +53,7 @@ ulint mach_read_from_1( /*=============*/ const byte* b) /*!< in: pointer to byte */ - MY_ATTRIBUTE((nonnull, pure)); + MY_ATTRIBUTE((warn_unused_result)); /*******************************************************//** The following function is used to store data in two consecutive bytes. We store the most significant byte to the lower address. */ @@ -114,7 +114,7 @@ ulint mach_read_from_3( /*=============*/ const byte* b) /*!< in: pointer to 3 bytes */ - MY_ATTRIBUTE((nonnull, pure)); + MY_ATTRIBUTE((warn_unused_result)); /*******************************************************//** The following function is used to store data in four consecutive bytes. We store the most significant byte to the lowest address. */ @@ -133,7 +133,7 @@ ulint mach_read_from_4( /*=============*/ const byte* b) /*!< in: pointer to four bytes */ - MY_ATTRIBUTE((nonnull, pure)); + MY_ATTRIBUTE((warn_unused_result)); /*********************************************************//** Writes a ulint in a compressed form (1..5 bytes). @return stored size in bytes */ @@ -160,7 +160,7 @@ ulint mach_read_compressed( /*=================*/ const byte* b) /*!< in: pointer to memory from where to read */ - MY_ATTRIBUTE((nonnull, pure)); + MY_ATTRIBUTE((warn_unused_result)); /*******************************************************//** The following function is used to store data in 6 consecutive bytes. We store the most significant byte to the lowest address. */ @@ -179,7 +179,7 @@ ib_uint64_t mach_read_from_6( /*=============*/ const byte* b) /*!< in: pointer to 6 bytes */ - MY_ATTRIBUTE((nonnull, pure)); + MY_ATTRIBUTE((warn_unused_result)); /*******************************************************//** The following function is used to store data in 7 consecutive bytes. We store the most significant byte to the lowest address. */ @@ -198,7 +198,7 @@ ib_uint64_t mach_read_from_7( /*=============*/ const byte* b) /*!< in: pointer to 7 bytes */ - MY_ATTRIBUTE((nonnull, pure)); + MY_ATTRIBUTE((warn_unused_result)); /*******************************************************//** The following function is used to store data in 8 consecutive bytes. We store the most significant byte to the lowest address. */ @@ -243,7 +243,7 @@ ib_uint64_t mach_ull_read_compressed( /*=====================*/ const byte* b) /*!< in: pointer to memory from where to read */ - MY_ATTRIBUTE((nonnull, pure)); + MY_ATTRIBUTE((warn_unused_result)); /*********************************************************//** Writes a 64-bit integer in a compressed form (1..11 bytes). @return size in bytes */ @@ -270,7 +270,7 @@ ib_uint64_t mach_ull_read_much_compressed( /*==========================*/ const byte* b) /*!< in: pointer to memory from where to read */ - MY_ATTRIBUTE((nonnull, pure)); + MY_ATTRIBUTE((warn_unused_result)); /*********************************************************//** Reads a ulint in a compressed form if the log record fully contains it. @return pointer to end of the stored field, NULL if not complete */ diff --git a/storage/xtradb/include/mach0data.ic b/storage/xtradb/include/mach0data.ic index 27b9f62b552..4b9275cc12c 100644 --- a/storage/xtradb/include/mach0data.ic +++ b/storage/xtradb/include/mach0data.ic @@ -52,7 +52,6 @@ mach_read_from_1( /*=============*/ const byte* b) /*!< in: pointer to byte */ { - ut_ad(b); return((ulint)(b[0])); } @@ -132,7 +131,6 @@ mach_read_from_3( /*=============*/ const byte* b) /*!< in: pointer to 3 bytes */ { - ut_ad(b); return( ((ulint)(b[0]) << 16) | ((ulint)(b[1]) << 8) | (ulint)(b[2]) @@ -182,7 +180,6 @@ mach_read_from_4( /*=============*/ const byte* b) /*!< in: pointer to four bytes */ { - ut_ad(b); return( ((ulint)(b[0]) << 24) | ((ulint)(b[1]) << 16) | ((ulint)(b[2]) << 8) @@ -261,8 +258,6 @@ mach_read_compressed( { ulint flag; - ut_ad(b); - flag = mach_read_from_1(b); if (flag < 0x80UL) { @@ -339,8 +334,6 @@ mach_read_from_7( /*=============*/ const byte* b) /*!< in: pointer to 7 bytes */ { - ut_ad(b); - return(ut_ull_create(mach_read_from_3(b), mach_read_from_4(b + 3))); } @@ -370,8 +363,6 @@ mach_read_from_6( /*=============*/ const byte* b) /*!< in: pointer to 6 bytes */ { - ut_ad(b); - return(ut_ull_create(mach_read_from_2(b), mach_read_from_4(b + 2))); } @@ -419,8 +410,6 @@ mach_ull_read_compressed( ib_uint64_t n; ulint size; - ut_ad(b); - n = (ib_uint64_t) mach_read_compressed(b); size = mach_get_compressed_size((ulint) n); @@ -486,8 +475,6 @@ mach_ull_read_much_compressed( ib_uint64_t n; ulint size; - ut_ad(b); - if (*b != (byte)0xFF) { n = 0; size = 0; diff --git a/storage/xtradb/include/mtr0mtr.h b/storage/xtradb/include/mtr0mtr.h index c00472117dc..f939918a90d 100644 --- a/storage/xtradb/include/mtr0mtr.h +++ b/storage/xtradb/include/mtr0mtr.h @@ -227,8 +227,7 @@ UNIV_INTERN void mtr_commit( /*=======*/ - mtr_t* mtr) /*!< in/out: mini-transaction */ - MY_ATTRIBUTE((nonnull)); + mtr_t* mtr); /*!< in/out: mini-transaction */ /**********************************************************//** Sets and returns a savepoint in mtr. @return savepoint */ @@ -334,7 +333,7 @@ mtr_memo_contains( mtr_t* mtr, /*!< in: mtr */ const void* object, /*!< in: object to search */ ulint type) /*!< in: type of object */ - MY_ATTRIBUTE((warn_unused_result, nonnull)); + MY_ATTRIBUTE((warn_unused_result)); /**********************************************************//** Checks if memo contains the given page. diff --git a/storage/xtradb/include/os0file.h b/storage/xtradb/include/os0file.h index d2d2be7662f..60a9c3475e7 100644 --- a/storage/xtradb/include/os0file.h +++ b/storage/xtradb/include/os0file.h @@ -2,6 +2,7 @@ Copyright (c) 1995, 2016, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2009, Percona Inc. +Copyright (c) 2017, MariaDB Corporation. All Rights Reserved. Portions of this file contain modifications contributed and copyrighted by Percona Inc.. Those modifications are @@ -552,9 +553,10 @@ os_file_create_simple_no_error_handling_func( ibool* success)/*!< out: TRUE if succeed, FALSE if error */ MY_ATTRIBUTE((nonnull, warn_unused_result)); /****************************************************************//** -Tries to disable OS caching on an opened file descriptor. */ +Tries to disable OS caching on an opened file descriptor. +@return true if operation is success and false otherwise */ UNIV_INTERN -void +bool os_file_set_nocache( /*================*/ os_file_t fd, /*!< in: file descriptor to alter */ @@ -1175,6 +1177,7 @@ UNIV_INTERN void os_aio_simulated_wake_handler_threads(void); /*=======================================*/ +#ifdef _WIN32 /**********************************************************************//** This function can be called if one wants to post a batch of reads and prefers an i/o-handler thread to handle them all at once later. You must @@ -1182,8 +1185,10 @@ call os_aio_simulated_wake_handler_threads later to ensure the threads are not left sleeping! */ UNIV_INTERN void -os_aio_simulated_put_read_threads_to_sleep(void); -/*============================================*/ +os_aio_simulated_put_read_threads_to_sleep(); +#else /* _WIN32 */ +# define os_aio_simulated_put_read_threads_to_sleep() +#endif /* _WIN32 */ #ifdef WIN_ASYNC_IO /**********************************************************************//** diff --git a/storage/xtradb/include/os0thread.h b/storage/xtradb/include/os0thread.h index 671b9b7dc3f..7865358b0f7 100644 --- a/storage/xtradb/include/os0thread.h +++ b/storage/xtradb/include/os0thread.h @@ -131,11 +131,9 @@ os_thread_create_func( os_thread_id_t* thread_id); /*!< out: id of the created thread, or NULL */ -/** -Waits until the specified thread completes and joins it. Its return value is -ignored. - -@param thread thread to join */ +/** Waits until the specified thread completes and joins it. +Its return value is ignored. +@param[in,out] thread thread to join */ UNIV_INTERN void os_thread_join( diff --git a/storage/xtradb/include/page0page.h b/storage/xtradb/include/page0page.h index cb43c937757..eefa0fa4c5b 100644 --- a/storage/xtradb/include/page0page.h +++ b/storage/xtradb/include/page0page.h @@ -235,8 +235,7 @@ ulint page_header_get_offs( /*=================*/ const page_t* page, /*!< in: page */ - ulint field) /*!< in: PAGE_FREE, ... */ - MY_ATTRIBUTE((nonnull, pure)); + ulint field); /*!< in: PAGE_FREE, ... */ /*************************************************************//** Returns the pointer stored in the given header field, or NULL. */ @@ -528,7 +527,7 @@ bool page_is_leaf( /*=========*/ const page_t* page) /*!< in: page */ - MY_ATTRIBUTE((nonnull, pure)); + MY_ATTRIBUTE((warn_unused_result)); /************************************************************//** Determine whether the page is empty. @return true if the page is empty (PAGE_N_RECS = 0) */ @@ -849,8 +848,7 @@ page_copy_rec_list_end( buf_block_t* block, /*!< in: index page containing rec */ rec_t* rec, /*!< in: record on page */ dict_index_t* index, /*!< in: record descriptor */ - mtr_t* mtr) /*!< in: mtr */ - MY_ATTRIBUTE((nonnull)); + mtr_t* mtr); /*!< in: mtr */ /*************************************************************//** Copies records from page to new_page, up to the given record, NOT including that record. Infimum and supremum records are not copied. @@ -871,8 +869,7 @@ page_copy_rec_list_start( buf_block_t* block, /*!< in: index page containing rec */ rec_t* rec, /*!< in: record on page */ dict_index_t* index, /*!< in: record descriptor */ - mtr_t* mtr) /*!< in: mtr */ - MY_ATTRIBUTE((nonnull)); + mtr_t* mtr); /*!< in: mtr */ /*************************************************************//** Deletes records from a page from a given record onward, including that record. The infimum and supremum records are not deleted. */ @@ -921,8 +918,7 @@ page_move_rec_list_end( buf_block_t* block, /*!< in: index page from where to move */ rec_t* split_rec, /*!< in: first record to move */ dict_index_t* index, /*!< in: record descriptor */ - mtr_t* mtr) /*!< in: mtr */ - MY_ATTRIBUTE((nonnull(1, 2, 4, 5))); + mtr_t* mtr); /*!< in: mtr */ /*************************************************************//** Moves record list start to another page. Moved records do not include split_rec. @@ -952,8 +948,7 @@ page_dir_split_slot( page_t* page, /*!< in: index page */ page_zip_des_t* page_zip,/*!< in/out: compressed page whose uncompressed part will be written, or NULL */ - ulint slot_no)/*!< in: the directory slot */ - MY_ATTRIBUTE((nonnull(1))); + ulint slot_no);/*!< in: the directory slot */ /*************************************************************//** Tries to balance the given directory slot with too few records with the upper neighbor, so that there are at least the minimum number @@ -965,8 +960,7 @@ page_dir_balance_slot( /*==================*/ page_t* page, /*!< in/out: index page */ page_zip_des_t* page_zip,/*!< in/out: compressed page, or NULL */ - ulint slot_no)/*!< in: the directory slot */ - MY_ATTRIBUTE((nonnull(1))); + ulint slot_no);/*!< in: the directory slot */ /**********************************************************//** Parses a log record of a record list end or start deletion. @return end of log record or NULL */ diff --git a/storage/xtradb/include/page0page.ic b/storage/xtradb/include/page0page.ic index 4a22a32112f..80939bbb828 100644 --- a/storage/xtradb/include/page0page.ic +++ b/storage/xtradb/include/page0page.ic @@ -154,7 +154,6 @@ page_header_get_offs( { ulint offs; - ut_ad(page); ut_ad((field == PAGE_FREE) || (field == PAGE_LAST_INSERT) || (field == PAGE_HEAP_TOP)); diff --git a/storage/xtradb/include/page0zip.h b/storage/xtradb/include/page0zip.h index 81068e7bd29..adafaa6d8b6 100644 --- a/storage/xtradb/include/page0zip.h +++ b/storage/xtradb/include/page0zip.h @@ -132,7 +132,7 @@ page_zip_compress( dict_index_t* index, /*!< in: index of the B-tree node */ ulint level, /*!< in: compression level */ mtr_t* mtr) /*!< in: mini-transaction, or NULL */ - MY_ATTRIBUTE((nonnull(1,2,3))); + MY_ATTRIBUTE((warn_unused_result)); /**********************************************************************//** Decompress a page. This function should tolerate errors on the compressed @@ -424,8 +424,7 @@ page_zip_reorganize( out: data, n_blobs, m_start, m_end, m_nonempty */ dict_index_t* index, /*!< in: index of the B-tree node */ - mtr_t* mtr) /*!< in: mini-transaction */ - MY_ATTRIBUTE((nonnull)); + mtr_t* mtr); /*!< in: mini-transaction */ #ifndef UNIV_HOTBACKUP /**********************************************************************//** Copy the records of a page byte for byte. Do not copy the page header @@ -458,7 +457,7 @@ page_zip_parse_compress( byte* end_ptr,/*!< in: buffer end */ page_t* page, /*!< out: uncompressed page */ page_zip_des_t* page_zip)/*!< out: compressed page */ - MY_ATTRIBUTE((nonnull(1,2))); + MY_ATTRIBUTE((warn_unused_result)); #endif /* !UNIV_INNOCHECKSUM */ diff --git a/storage/xtradb/include/rem0rec.h b/storage/xtradb/include/rem0rec.h index a6d47e80049..3d147e3d5fb 100644 --- a/storage/xtradb/include/rem0rec.h +++ b/storage/xtradb/include/rem0rec.h @@ -747,8 +747,7 @@ rec_copy( /*=====*/ void* buf, /*!< in: buffer */ const rec_t* rec, /*!< in: physical record */ - const ulint* offsets)/*!< in: array returned by rec_get_offsets() */ - MY_ATTRIBUTE((nonnull)); + const ulint* offsets);/*!< in: array returned by rec_get_offsets() */ #ifndef UNIV_HOTBACKUP /**********************************************************//** Determines the size of a data tuple prefix in a temporary file. diff --git a/storage/xtradb/include/row0upd.h b/storage/xtradb/include/row0upd.h index e59ec58b63c..4312fcf7339 100644 --- a/storage/xtradb/include/row0upd.h +++ b/storage/xtradb/include/row0upd.h @@ -248,9 +248,8 @@ row_upd_index_replace_new_col_vals_index_pos( /*!< in: if TRUE, limit the replacement to ordering fields of index; note that this does not work for non-clustered indexes. */ - mem_heap_t* heap) /*!< in: memory heap for allocating and + mem_heap_t* heap); /*!< in: memory heap for allocating and copying the new values */ - MY_ATTRIBUTE((nonnull)); /***********************************************************//** Replaces the new column values stored in the update vector to the index entry given. */ @@ -311,7 +310,7 @@ row_upd_changes_ord_field_binary_func( compile time */ const row_ext_t*ext) /*!< NULL, or prefixes of the externally stored columns in the old row */ - MY_ATTRIBUTE((nonnull(1,2), warn_unused_result)); + MY_ATTRIBUTE((warn_unused_result)); #ifdef UNIV_DEBUG # define row_upd_changes_ord_field_binary(index,update,thr,row,ext) \ row_upd_changes_ord_field_binary_func(index,update,thr,row,ext) diff --git a/storage/xtradb/include/srv0srv.h b/storage/xtradb/include/srv0srv.h index 8ea92f69368..53098d7e426 100644 --- a/storage/xtradb/include/srv0srv.h +++ b/storage/xtradb/include/srv0srv.h @@ -3,7 +3,7 @@ Copyright (c) 1995, 2016, Oracle and/or its affiliates. All rights reserved. Copyright (c) 2008, 2009, Google Inc. Copyright (c) 2009, Percona Inc. -Copyright (c) 2013, 2014, SkySQL Ab. All Rights Reserved. +Copyright (c) 2013, 2017, MariaDB Corporation Ab. All Rights Reserved. Portions of this file contain modifications contributed and copyrighted by Google, Inc. Those modifications are gratefully acknowledged and are described @@ -152,13 +152,16 @@ extern const char* srv_main_thread_op_info; /** Prefix used by MySQL to indicate pre-5.1 table name encoding */ extern const char srv_mysql50_table_name_prefix[10]; -/* The monitor thread waits on this event. */ +/** Event to signal srv_monitor_thread. Not protected by a mutex. +Set after setting srv_print_innodb_monitor. */ extern os_event_t srv_monitor_event; -/* The error monitor thread waits on this event. */ +/** Event to signal the shutdown of srv_error_monitor_thread. +Not protected by a mutex. */ extern os_event_t srv_error_event; -/** The buffer pool dump/load thread waits on this event. */ +/** Event for waking up buf_dump_thread. Not protected by a mutex. +Set on shutdown or by buf_dump_start() or buf_load_start(). */ extern os_event_t srv_buf_dump_event; /** The buffer pool dump/load file name */ @@ -423,9 +426,6 @@ extern double srv_adaptive_flushing_lwm; extern ulong srv_flushing_avg_loops; extern ulong srv_force_recovery; -#ifndef DBUG_OFF -extern ulong srv_force_recovery_crash; -#endif /* !DBUG_OFF */ extern ulint srv_fast_shutdown; /*!< If this is 1, do not do a purge and index buffer merge. @@ -440,6 +440,7 @@ extern unsigned long long srv_stats_transient_sample_pages; extern my_bool srv_stats_persistent; extern unsigned long long srv_stats_persistent_sample_pages; extern my_bool srv_stats_auto_recalc; +extern my_bool srv_stats_include_delete_marked; extern unsigned long long srv_stats_modified_counter; extern my_bool srv_stats_sample_traditional; @@ -972,17 +973,12 @@ ulint srv_get_task_queue_length(void); /*===========================*/ -/*********************************************************************//** -Releases threads of the type given from suspension in the thread table. -NOTE! The server mutex has to be reserved by the caller! -@return number of threads released: this may be less than n if not -enough threads were suspended at the moment */ -UNIV_INTERN -ulint -srv_release_threads( -/*================*/ - enum srv_thread_type type, /*!< in: thread type */ - ulint n); /*!< in: number of threads to release */ +/** Ensure that a given number of threads of the type given are running +(or are already terminated). +@param[in] type thread type +@param[in] n number of threads that have to run */ +void +srv_release_threads(enum srv_thread_type type, ulint n); /**********************************************************************//** Check whether any background thread are active. If so print which thread @@ -993,12 +989,10 @@ const char* srv_any_background_threads_are_active(void); /*=======================================*/ -/**********************************************************************//** -Wakeup the purge threads. */ +/** Wake up the purge threads. */ UNIV_INTERN void -srv_purge_wakeup(void); -/*==================*/ +srv_purge_wakeup(); /** Status variables to be passed to MySQL */ struct export_var_t{ @@ -1166,4 +1160,12 @@ wsrep_srv_conc_cancel_wait( thread */ #endif /* WITH_WSREP */ +#ifndef DBUG_OFF +/** false before InnoDB monitor has been printed at least once, true +afterwards */ +extern bool srv_debug_monitor_printed; +#else +#define srv_debug_monitor_printed false +#endif + #endif diff --git a/storage/xtradb/include/trx0purge.h b/storage/xtradb/include/trx0purge.h index a862523c092..7b9b5dc49cd 100644 --- a/storage/xtradb/include/trx0purge.h +++ b/storage/xtradb/include/trx0purge.h @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2011, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2017, MariaDB Corporation. 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 @@ -145,7 +146,10 @@ struct trx_purge_t{ log operation can prevent this by obtaining an s-latch here. It also protects state and running */ - os_event_t event; /*!< State signal event */ + os_event_t event; /*!< State signal event; + os_event_set() and os_event_reset() + are protected by trx_purge_t::latch + X-lock */ ulint n_stop; /*!< Counter to track number stops */ volatile bool running; /*!< true, if purge is active, we check this without the latch too */ diff --git a/storage/xtradb/include/trx0trx.h b/storage/xtradb/include/trx0trx.h index ec810405b5a..f9917c8448d 100644 --- a/storage/xtradb/include/trx0trx.h +++ b/storage/xtradb/include/trx0trx.h @@ -106,7 +106,7 @@ void trx_free_prepared( /*==============*/ trx_t* trx) /*!< in, own: trx object */ - UNIV_COLD MY_ATTRIBUTE((nonnull)); + UNIV_COLD; /********************************************************************//** Frees a transaction object for MySQL. */ UNIV_INTERN diff --git a/storage/xtradb/include/univ.i b/storage/xtradb/include/univ.i index 3444cbb1e7f..6acb0eec319 100644 --- a/storage/xtradb/include/univ.i +++ b/storage/xtradb/include/univ.i @@ -45,10 +45,10 @@ Created 1/20/1994 Heikki Tuuri #define INNODB_VERSION_MAJOR 5 #define INNODB_VERSION_MINOR 6 -#define INNODB_VERSION_BUGFIX 34 +#define INNODB_VERSION_BUGFIX 35 #ifndef PERCONA_INNODB_VERSION -#define PERCONA_INNODB_VERSION 79.1 +#define PERCONA_INNODB_VERSION 80.0 #endif /* Enable UNIV_LOG_ARCHIVE in XtraDB */ diff --git a/storage/xtradb/include/ut0wqueue.h b/storage/xtradb/include/ut0wqueue.h index 33385ddf2d4..09b2eb717a7 100644 --- a/storage/xtradb/include/ut0wqueue.h +++ b/storage/xtradb/include/ut0wqueue.h @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 2006, 2009, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2017, MariaDB Corporation. 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 @@ -99,7 +100,9 @@ ib_wqueue_timedwait( struct ib_wqueue_t { ib_mutex_t mutex; /*!< mutex protecting everything */ ib_list_t* items; /*!< work item list */ - os_event_t event; /*!< event we use to signal additions to list */ + os_event_t event; /*!< event we use to signal additions to list; + os_event_set() and os_event_reset() are + protected by ib_wqueue_t::mutex */ }; #endif diff --git a/storage/xtradb/log/log0log.cc b/storage/xtradb/log/log0log.cc index 15183b9c1cb..b39a8ed1829 100644 --- a/storage/xtradb/log/log0log.cc +++ b/storage/xtradb/log/log0log.cc @@ -2,6 +2,7 @@ Copyright (c) 1995, 2016, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2009, Google Inc. +Copyright (c) 2017, MariaDB Corporation. All Rights Reserved. Portions of this file contain modifications contributed and copyrighted by Google, Inc. Those modifications are gratefully acknowledged and are described @@ -1569,17 +1570,7 @@ log_write_up_to( } loop: -#ifdef UNIV_DEBUG - loop_count++; - - ut_ad(loop_count < 5); - -# if 0 - if (loop_count > 2) { - fprintf(stderr, "Log loop count %lu\n", loop_count); - } -# endif -#endif + ut_ad(++loop_count < 100); mutex_enter(&(log_sys->mutex)); ut_ad(!recv_no_log_write); @@ -3505,7 +3496,6 @@ logs_empty_and_mark_files_at_shutdown(void) lsn_t lsn; lsn_t tracked_lsn; ulint count = 0; - ulint total_trx; ulint pending_io; enum srv_thread_type active_thd; const char* thread_name; @@ -3556,10 +3546,9 @@ loop: shutdown, because the InnoDB layer may have committed or prepared transactions and we don't want to lose them. */ - total_trx = trx_sys_any_active_transactions(); - - if (total_trx > 0) { - + if (ulint total_trx = srv_was_started && !srv_read_only_mode + && srv_force_recovery < SRV_FORCE_NO_TRX_UNDO + ? trx_sys_any_active_transactions() : 0) { if (srv_print_verbose_log && count > 600) { ib_logf(IB_LOG_LEVEL_INFO, "Waiting for %lu active transactions to finish", diff --git a/storage/xtradb/log/log0online.cc b/storage/xtradb/log/log0online.cc index 209ca743628..764c9e0c52f 100644 --- a/storage/xtradb/log/log0online.cc +++ b/storage/xtradb/log/log0online.cc @@ -76,12 +76,14 @@ struct log_bitmap_struct { both the correct type and the tree does not mind its overwrite during rbt_next() tree traversal. */ - ib_mutex_t mutex; /*!< mutex protecting all the fields.*/ }; /* The log parsing and bitmap output struct instance */ static struct log_bitmap_struct* log_bmp_sys; +/* Mutex protecting log_bmp_sys */ +static ib_mutex_t log_bmp_sys_mutex; + /** File name stem for bitmap files. */ static const char* bmp_file_name_stem = "ib_modified_log_"; @@ -173,28 +175,24 @@ log_online_set_page_bit( ulint space, /*!<in: log record space id */ ulint page_no)/*!<in: log record page id */ { - ulint block_start_page; - ulint block_pos; - uint bit_pos; - ib_rbt_bound_t tree_search_pos; - byte search_page[MODIFIED_PAGE_BLOCK_SIZE]; - byte *page_ptr; - - ut_ad(mutex_own(&log_bmp_sys->mutex)); + ut_ad(mutex_own(&log_bmp_sys_mutex)); ut_a(space != ULINT_UNDEFINED); ut_a(page_no != ULINT_UNDEFINED); - block_start_page = page_no / MODIFIED_PAGE_BLOCK_ID_COUNT + ulint block_start_page = page_no / MODIFIED_PAGE_BLOCK_ID_COUNT * MODIFIED_PAGE_BLOCK_ID_COUNT; - block_pos = block_start_page ? (page_no % block_start_page / 8) + ulint block_pos = block_start_page ? (page_no % block_start_page / 8) : (page_no / 8); - bit_pos = page_no % 8; + uint bit_pos = page_no % 8; + byte search_page[MODIFIED_PAGE_BLOCK_SIZE]; mach_write_to_4(search_page + MODIFIED_PAGE_SPACE_ID, space); mach_write_to_4(search_page + MODIFIED_PAGE_1ST_PAGE_ID, block_start_page); + byte *page_ptr; + ib_rbt_bound_t tree_search_pos; if (!rbt_search(log_bmp_sys->modified_pages, &tree_search_pos, search_page)) { page_ptr = rbt_value(byte, tree_search_pos.last); @@ -596,12 +594,19 @@ log_online_is_bitmap_file( && (!strcmp(stem, bmp_file_name_stem))); } -/*********************************************************************//** -Initialize the online log following subsytem. */ +/** Initialize the constant part of the log tracking subsystem */ +UNIV_INTERN +void +log_online_init(void) +{ + mutex_create(log_bmp_sys_mutex_key, &log_bmp_sys_mutex, + SYNC_LOG_ONLINE); +} + +/** Initialize the dynamic part of the log tracking subsystem */ UNIV_INTERN void log_online_read_init(void) -/*======================*/ { ibool success; lsn_t tracking_start_lsn @@ -625,9 +630,6 @@ log_online_read_init(void) log_bmp_sys->read_buf = static_cast<byte *> (ut_align(log_bmp_sys->read_buf_ptr, OS_FILE_LOG_BLOCK_SIZE)); - mutex_create(log_bmp_sys_mutex_key, &log_bmp_sys->mutex, - SYNC_LOG_ONLINE); - /* Initialize bitmap file directory from srv_data_home and add a path separator if needed. */ srv_data_home_len = strlen(srv_data_home); @@ -767,13 +769,15 @@ log_online_read_init(void) log_set_tracked_lsn(tracking_start_lsn); } -/*********************************************************************//** -Shut down the online log following subsystem. */ +/** Shut down the dynamic part of the log tracking subsystem */ UNIV_INTERN void log_online_read_shutdown(void) -/*==========================*/ { + mutex_enter(&log_bmp_sys_mutex); + + srv_track_changed_pages = FALSE; + ib_rbt_node_t *free_list_node = log_bmp_sys->page_free_list; if (log_bmp_sys->out.file != os_file_invalid) { @@ -789,10 +793,21 @@ log_online_read_shutdown(void) free_list_node = next; } - mutex_free(&log_bmp_sys->mutex); - ut_free(log_bmp_sys->read_buf_ptr); ut_free(log_bmp_sys); + log_bmp_sys = NULL; + + srv_redo_log_thread_started = false; + + mutex_exit(&log_bmp_sys_mutex); +} + +/** Shut down the constant part of the log tracking subsystem */ +UNIV_INTERN +void +log_online_shutdown(void) +{ + mutex_free(&log_bmp_sys_mutex); } /*********************************************************************//** @@ -838,13 +853,12 @@ void log_online_parse_redo_log(void) /*===========================*/ { + ut_ad(mutex_own(&log_bmp_sys_mutex)); + byte *ptr = log_bmp_sys->parse_buf; byte *end = log_bmp_sys->parse_buf_end; - ulint len = 0; - ut_ad(mutex_own(&log_bmp_sys->mutex)); - while (ptr != end && log_bmp_sys->next_parse_lsn < log_bmp_sys->end_lsn) { @@ -926,6 +940,8 @@ log_online_add_to_parse_buf( ulint skip_len) /*!< in: how much of log data to skip */ { + ut_ad(mutex_own(&log_bmp_sys_mutex)); + ulint start_offset = skip_len ? skip_len : LOG_BLOCK_HDR_SIZE; ulint end_offset = (data_len == OS_FILE_LOG_BLOCK_SIZE) @@ -934,8 +950,6 @@ log_online_add_to_parse_buf( ulint actual_data_len = (end_offset >= start_offset) ? end_offset - start_offset : 0; - ut_ad(mutex_own(&log_bmp_sys->mutex)); - ut_memcpy(log_bmp_sys->parse_buf_end, log_block + start_offset, actual_data_len); @@ -958,11 +972,9 @@ log_online_parse_redo_log_block( log data should be skipped as they were parsed before */ { - ulint block_data_len; + ut_ad(mutex_own(&log_bmp_sys_mutex)); - ut_ad(mutex_own(&log_bmp_sys->mutex)); - - block_data_len = log_block_get_data_len(log_block); + ulint block_data_len = log_block_get_data_len(log_block); ut_ad(block_data_len % OS_FILE_LOG_BLOCK_SIZE == 0 || block_data_len < OS_FILE_LOG_BLOCK_SIZE); @@ -982,14 +994,14 @@ log_online_follow_log_seg( lsn_t block_start_lsn, /*!< in: the LSN to read from */ lsn_t block_end_lsn) /*!< in: the LSN to read to */ { + ut_ad(mutex_own(&log_bmp_sys_mutex)); + /* Pointer to the current OS_FILE_LOG_BLOCK-sized chunk of the read log data to parse */ byte* log_block = log_bmp_sys->read_buf; byte* log_block_end = log_bmp_sys->read_buf + (block_end_lsn - block_start_lsn); - ut_ad(mutex_own(&log_bmp_sys->mutex)); - mutex_enter(&log_sys->mutex); log_group_read_log_seg(LOG_RECOVER, log_bmp_sys->read_buf, group, block_start_lsn, block_end_lsn, TRUE); @@ -1049,11 +1061,11 @@ log_online_follow_log_group( lsn_t contiguous_lsn) /*!< in: the LSN of log block start containing the log_parse_start_lsn */ { + ut_ad(mutex_own(&log_bmp_sys_mutex)); + lsn_t block_start_lsn = contiguous_lsn; lsn_t block_end_lsn; - ut_ad(mutex_own(&log_bmp_sys->mutex)); - log_bmp_sys->next_parse_lsn = log_bmp_sys->start_lsn; log_bmp_sys->parse_buf_end = log_bmp_sys->parse_buf; @@ -1090,20 +1102,28 @@ log_online_write_bitmap_page( /*=========================*/ const byte *block) /*!< in: block to write */ { - ibool success; - - ut_ad(srv_track_changed_pages); - ut_ad(mutex_own(&log_bmp_sys->mutex)); + ut_ad(mutex_own(&log_bmp_sys_mutex)); /* Simulate a write error */ DBUG_EXECUTE_IF("bitmap_page_write_error", - ib_logf(IB_LOG_LEVEL_ERROR, - "simulating bitmap write error in " - "log_online_write_bitmap_page"); - return FALSE;); - - success = os_file_write(log_bmp_sys->out.name, log_bmp_sys->out.file, - block, log_bmp_sys->out.offset, + { + ulint space_id + = mach_read_from_4(block + + MODIFIED_PAGE_SPACE_ID); + if (space_id > 0) { + ib_logf(IB_LOG_LEVEL_ERROR, + "simulating bitmap write " + "error in " + "log_online_write_bitmap_page " + "for space ID %lu", + space_id); + return FALSE; + } + }); + + ibool success = os_file_write(log_bmp_sys->out.name, + log_bmp_sys->out.file, block, + log_bmp_sys->out.offset, MODIFIED_PAGE_BLOCK_SIZE); if (UNIV_UNLIKELY(!success)) { @@ -1143,11 +1163,7 @@ ibool log_online_write_bitmap(void) /*=========================*/ { - ib_rbt_node_t *bmp_tree_node; - const ib_rbt_node_t *last_bmp_tree_node; - ibool success = TRUE; - - ut_ad(mutex_own(&log_bmp_sys->mutex)); + ut_ad(mutex_own(&log_bmp_sys_mutex)); if (log_bmp_sys->out.offset >= srv_max_bitmap_file_size) { if (!log_online_rotate_bitmap_file(log_bmp_sys->start_lsn)) { @@ -1155,9 +1171,12 @@ log_online_write_bitmap(void) } } - bmp_tree_node = (ib_rbt_node_t *) - rbt_first(log_bmp_sys->modified_pages); - last_bmp_tree_node = rbt_last(log_bmp_sys->modified_pages); + ib_rbt_node_t *bmp_tree_node + = (ib_rbt_node_t *)rbt_first(log_bmp_sys->modified_pages); + const ib_rbt_node_t * const last_bmp_tree_node + = rbt_last(log_bmp_sys->modified_pages); + + ibool success = TRUE; while (bmp_tree_node) { @@ -1190,9 +1209,11 @@ log_online_write_bitmap(void) rbt_next(log_bmp_sys->modified_pages, bmp_tree_node); DBUG_EXECUTE_IF("bitmap_page_2_write_error", - ut_ad(bmp_tree_node); /* 2nd page must exist */ - DBUG_SET("+d,bitmap_page_write_error"); - DBUG_SET("-d,bitmap_page_2_write_error");); + if (bmp_tree_node) + { + DBUG_SET("+d,bitmap_page_write_error"); + DBUG_SET("-d,bitmap_page_2_write_error"); + }); } rbt_reset(log_bmp_sys->modified_pages); @@ -1213,10 +1234,19 @@ log_online_follow_redo_log(void) log_group_t* group; ibool result; - ut_ad(srv_track_changed_pages); ut_ad(!srv_read_only_mode); - mutex_enter(&log_bmp_sys->mutex); + if (!srv_track_changed_pages) + return TRUE; + + DEBUG_SYNC_C("log_online_follow_redo_log"); + + mutex_enter(&log_bmp_sys_mutex); + + if (!srv_track_changed_pages) { + mutex_exit(&log_bmp_sys_mutex); + return TRUE; + } /* Grab the LSN of the last checkpoint, we will parse up to it */ mutex_enter(&(log_sys->mutex)); @@ -1224,7 +1254,7 @@ log_online_follow_redo_log(void) mutex_exit(&(log_sys->mutex)); if (log_bmp_sys->end_lsn == log_bmp_sys->start_lsn) { - mutex_exit(&log_bmp_sys->mutex); + mutex_exit(&log_bmp_sys_mutex); return TRUE; } @@ -1247,7 +1277,7 @@ log_online_follow_redo_log(void) log_bmp_sys->start_lsn = log_bmp_sys->end_lsn; log_set_tracked_lsn(log_bmp_sys->start_lsn); - mutex_exit(&log_bmp_sys->mutex); + mutex_exit(&log_bmp_sys_mutex); return result; } @@ -1594,6 +1624,8 @@ log_online_bitmap_iterator_init( { ut_a(i); + i->max_lsn = max_lsn; + if (UNIV_UNLIKELY(min_lsn > max_lsn)) { /* Empty range */ @@ -1702,6 +1734,9 @@ log_online_bitmap_iterator_next( return TRUE; } + if (i->end_lsn >= i->max_lsn && i->last_page_in_run) + return FALSE; + while (!checksum_ok) { while (i->in.size < MODIFIED_PAGE_BLOCK_SIZE @@ -1797,15 +1832,21 @@ log_online_purge_changed_page_bitmaps( lsn = LSN_MAX; } + bool log_bmp_sys_inited = false; if (srv_redo_log_thread_started) { /* User requests might happen with both enabled and disabled tracking */ - mutex_enter(&log_bmp_sys->mutex); + log_bmp_sys_inited = true; + mutex_enter(&log_bmp_sys_mutex); + if (!srv_redo_log_thread_started) { + log_bmp_sys_inited = false; + mutex_exit(&log_bmp_sys_mutex); + } } if (!log_online_setup_bitmap_file_range(&bitmap_files, 0, LSN_MAX)) { - if (srv_redo_log_thread_started) { - mutex_exit(&log_bmp_sys->mutex); + if (log_bmp_sys_inited) { + mutex_exit(&log_bmp_sys_mutex); } return TRUE; } @@ -1843,7 +1884,7 @@ log_online_purge_changed_page_bitmaps( } } - if (srv_redo_log_thread_started) { + if (log_bmp_sys_inited) { if (lsn > log_bmp_sys->end_lsn) { lsn_t new_file_lsn; if (lsn == LSN_MAX) { @@ -1859,7 +1900,7 @@ log_online_purge_changed_page_bitmaps( } } - mutex_exit(&log_bmp_sys->mutex); + mutex_exit(&log_bmp_sys_mutex); } free(bitmap_files.files); diff --git a/storage/xtradb/log/log0recv.cc b/storage/xtradb/log/log0recv.cc index 9e42fb5cc1c..01975712d99 100644 --- a/storage/xtradb/log/log0recv.cc +++ b/storage/xtradb/log/log0recv.cc @@ -2,6 +2,7 @@ Copyright (c) 1997, 2016, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2012, Facebook Inc. +Copyright (c) 2017, MariaDB Corporation. 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 @@ -176,7 +177,7 @@ UNIV_INTERN mysql_pfs_key_t recv_writer_mutex_key; # endif /* UNIV_PFS_MUTEX */ /** Flag indicating if recv_writer thread is active. */ -UNIV_INTERN bool recv_writer_thread_active = false; +static volatile bool recv_writer_thread_active; UNIV_INTERN os_thread_t recv_writer_thread_handle = 0; #endif /* !UNIV_HOTBACKUP */ @@ -342,8 +343,6 @@ DECLARE_THREAD(recv_writer_thread)( os_thread_pf(os_thread_get_curr_id())); #endif /* UNIV_DEBUG_THREAD_CREATION */ - recv_writer_thread_active = true; - while (srv_shutdown_state == SRV_SHUTDOWN_NONE) { os_thread_sleep(100000); @@ -2895,11 +2894,10 @@ recv_scan_log_recs( recv_init_crash_recovery(); } else { - - ib_logf(IB_LOG_LEVEL_WARN, - "Recovery skipped, " - "--innodb-read-only set!"); - + ib_logf(IB_LOG_LEVEL_ERROR, + "innodb_read_only prevents" + " crash recovery"); + recv_needed_recovery = TRUE; return(TRUE); } } @@ -3078,6 +3076,7 @@ recv_init_crash_recovery(void) /* Spawn the background thread to flush dirty pages from the buffer pools. */ + recv_writer_thread_active = true; recv_writer_thread_handle = os_thread_create( recv_writer_thread, 0, 0); } @@ -3323,6 +3322,11 @@ recv_recovery_from_checkpoint_start_func( /* Done with startup scan. Clear the flag. */ recv_log_scan_is_startup_type = FALSE; + + if (srv_read_only_mode && recv_needed_recovery) { + return(DB_READ_ONLY); + } + if (TYPE_CHECKPOINT) { /* NOTE: we always do a 'recovery' at startup, but only if there is something wrong we will print a message to the @@ -3473,15 +3477,6 @@ void recv_recovery_from_checkpoint_finish(void) /*======================================*/ { - /* Apply the hashed log records to the respective file pages */ - - if (srv_force_recovery < SRV_FORCE_NO_LOG_REDO) { - - recv_apply_hashed_log_recs(TRUE); - } - - DBUG_PRINT("ib_log", ("apply completed")); - if (recv_needed_recovery) { trx_sys_print_mysql_master_log_pos(); trx_sys_print_mysql_binlog_offset(); diff --git a/storage/xtradb/mach/mach0data.cc b/storage/xtradb/mach/mach0data.cc index 206434dc5ab..feeedb01609 100644 --- a/storage/xtradb/mach/mach0data.cc +++ b/storage/xtradb/mach/mach0data.cc @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 1995, 2009, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 1995, 2016, 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 @@ -55,7 +55,6 @@ mach_parse_compressed( if (flag < 0x80UL) { *val = flag; return(ptr + 1); - } /* Workaround GCC bug @@ -64,7 +63,11 @@ mach_parse_compressed( function, causing and out-of-bounds read if we are reading a short integer close to the end of buffer. */ #if defined(__GNUC__) && (__GNUC__ >= 5) && !defined(__clang__) - asm volatile("": : :"memory"); +#define DEPLOY_FENCE +#endif + +#ifdef DEPLOY_FENCE + __atomic_thread_fence(__ATOMIC_ACQUIRE); #endif if (flag < 0xC0UL) { @@ -75,8 +78,13 @@ mach_parse_compressed( *val = mach_read_from_2(ptr) & 0x7FFFUL; return(ptr + 2); + } - } else if (flag < 0xE0UL) { +#ifdef DEPLOY_FENCE + __atomic_thread_fence(__ATOMIC_ACQUIRE); +#endif + + if (flag < 0xE0UL) { if (end_ptr < ptr + 3) { return(NULL); } @@ -84,7 +92,13 @@ mach_parse_compressed( *val = mach_read_from_3(ptr) & 0x3FFFFFUL; return(ptr + 3); - } else if (flag < 0xF0UL) { + } + +#ifdef DEPLOY_FENCE + __atomic_thread_fence(__ATOMIC_ACQUIRE); +#endif + + if (flag < 0xF0UL) { if (end_ptr < ptr + 4) { return(NULL); } @@ -92,14 +106,20 @@ mach_parse_compressed( *val = mach_read_from_4(ptr) & 0x1FFFFFFFUL; return(ptr + 4); - } else { - ut_ad(flag == 0xF0UL); + } - if (end_ptr < ptr + 5) { - return(NULL); - } +#ifdef DEPLOY_FENCE + __atomic_thread_fence(__ATOMIC_ACQUIRE); +#endif - *val = mach_read_from_4(ptr + 1); - return(ptr + 5); +#undef DEPLOY_FENCE + + ut_ad(flag == 0xF0UL); + + if (end_ptr < ptr + 5) { + return(NULL); } + + *val = mach_read_from_4(ptr + 1); + return(ptr + 5); } diff --git a/storage/xtradb/mtr/mtr0mtr.cc b/storage/xtradb/mtr/mtr0mtr.cc index 5ed0803727f..ed44fba9762 100644 --- a/storage/xtradb/mtr/mtr0mtr.cc +++ b/storage/xtradb/mtr/mtr0mtr.cc @@ -312,7 +312,6 @@ mtr_commit( /*=======*/ mtr_t* mtr) /*!< in: mini-transaction */ { - ut_ad(mtr); ut_ad(mtr->magic_n == MTR_MAGIC_N); ut_ad(mtr->state == MTR_ACTIVE); ut_ad(!mtr->inside_ibuf); diff --git a/storage/xtradb/os/os0file.cc b/storage/xtradb/os/os0file.cc index 817289352f2..2a651a84a1a 100644 --- a/storage/xtradb/os/os0file.cc +++ b/storage/xtradb/os/os0file.cc @@ -2,6 +2,7 @@ Copyright (c) 1995, 2016, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2009, Percona Inc. +Copyright (c) 2013, 2017, MariaDB Corporation. All Rights Reserved. Portions of this file contain modifications contributed and copyrighted by Percona Inc.. Those modifications are @@ -215,11 +216,15 @@ struct os_aio_array_t{ os_event_t not_full; /*!< The event which is set to the signaled state when there is space in - the aio outside the ibuf segment */ + the aio outside the ibuf segment; + os_event_set() and os_event_reset() + are protected by os_aio_array_t::mutex */ os_event_t is_empty; /*!< The event which is set to the signaled state when there are no - pending i/os in this array */ + pending i/os in this array; + os_event_set() and os_event_reset() + are protected by os_aio_array_t::mutex */ ulint n_slots;/*!< Total number of slots in the aio array. This must be divisible by n_threads. */ @@ -261,8 +266,8 @@ struct os_aio_array_t{ #define OS_AIO_IO_SETUP_RETRY_ATTEMPTS 5 #endif -/** Array of events used in simulated aio */ -static os_event_t* os_aio_segment_wait_events = NULL; +/** Array of events used in simulated aio. */ +static os_event_t* os_aio_segment_wait_events; /** The aio arrays for non-ibuf i/o and ibuf i/o, as well as sync aio. These are NULL when the module has not yet been initialized. @{ */ @@ -1103,50 +1108,15 @@ next_file: char* full_path; int ret; struct stat statinfo; -#ifdef HAVE_READDIR_R - char dirent_buf[sizeof(struct dirent) - + _POSIX_PATH_MAX + 100]; - /* In /mysys/my_lib.c, _POSIX_PATH_MAX + 1 is used as - the max file name len; but in most standards, the - length is NAME_MAX; we add 100 to be even safer */ -#endif next_file: -#ifdef HAVE_READDIR_R - ret = readdir_r(dir, (struct dirent*) dirent_buf, &ent); - - if (ret != 0 -#ifdef UNIV_AIX - /* On AIX, only if we got non-NULL 'ent' (result) value and - a non-zero 'ret' (return) value, it indicates a failed - readdir_r() call. An NULL 'ent' with an non-zero 'ret' - would indicate the "end of the directory" is reached. */ - && ent != NULL -#endif - ) { - fprintf(stderr, - "InnoDB: cannot read directory %s, error %lu\n", - dirname, (ulong) ret); - - return(-1); - } - - if (ent == NULL) { - /* End of directory */ - - return(1); - } - - ut_a(strlen(ent->d_name) < _POSIX_PATH_MAX + 100 - 1); -#else ent = readdir(dir); if (ent == NULL) { return(1); } -#endif ut_a(strlen(ent->d_name) < OS_FILE_MAX_PATH); if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) { @@ -1473,7 +1443,11 @@ os_file_set_nocache_if_needed(os_file_t file, const char* name, && (srv_unix_file_flush_method == SRV_UNIX_O_DIRECT || (srv_unix_file_flush_method == SRV_UNIX_O_DIRECT_NO_FSYNC)))) - os_file_set_nocache(file, name, mode_str); + /* Do fsync() on log files when setting O_DIRECT fails. + See log_io_complete() */ + if (!os_file_set_nocache(file, name, mode_str) + && srv_unix_file_flush_method == SRV_UNIX_ALL_O_DIRECT) + srv_unix_file_flush_method = SRV_UNIX_O_DIRECT; } /****************************************************************//** @@ -1644,9 +1618,10 @@ os_file_create_simple_no_error_handling_func( } /****************************************************************//** -Tries to disable OS caching on an opened file descriptor. */ +Tries to disable OS caching on an opened file descriptor. +@return TRUE if operation is success and FALSE otherwise */ UNIV_INTERN -void +bool os_file_set_nocache( /*================*/ os_file_t fd /*!< in: file descriptor to alter */ @@ -1667,6 +1642,7 @@ os_file_set_nocache( "Failed to set DIRECTIO_ON on file %s: %s: %s, " "continuing anyway.", file_name, operation_name, strerror(errno_save)); + return false; } #elif defined(O_DIRECT) if (fcntl(fd, F_SETFL, O_DIRECT) == -1) { @@ -1697,8 +1673,10 @@ short_warning: "continuing anyway.", file_name, operation_name, strerror(errno_save)); } + return false; } #endif /* defined(UNIV_SOLARIS) && defined(DIRECTIO_ON) */ + return true; } @@ -2356,48 +2334,52 @@ os_file_set_size( os_file_t file, /*!< in: handle to a file */ os_offset_t size) /*!< in: file size */ { - os_offset_t current_size; ibool ret; byte* buf; byte* buf2; ulint buf_size; - current_size = 0; - #ifdef HAVE_POSIX_FALLOCATE if (srv_use_posix_fallocate) { + int err; + do { + err = posix_fallocate(file, 0, size); + } while (err == EINTR + && srv_shutdown_state == SRV_SHUTDOWN_NONE); - if (posix_fallocate(file, current_size, size) == -1) { - - ib_logf(IB_LOG_LEVEL_ERROR, "preallocating file " - "space for file \'%s\' failed. Current size " - INT64PF ", desired size " INT64PF, - name, current_size, size); - os_file_handle_error_no_exit (name, "posix_fallocate", - FALSE); - return(FALSE); + if (err) { + ib_logf(IB_LOG_LEVEL_ERROR, + "preallocating " INT64PF " bytes for" + "file %s failed with error %d", + size, name, err); } - return(TRUE); + return(!err); } #endif +#ifdef _WIN32 + /* Write 1 page of zeroes at the desired end. */ + buf_size = UNIV_PAGE_SIZE; + os_offset_t current_size = size - buf_size; +#else /* Write up to 1 megabyte at a time. */ buf_size = ut_min(64, (ulint) (size / UNIV_PAGE_SIZE)) * UNIV_PAGE_SIZE; - buf2 = static_cast<byte*>(ut_malloc(buf_size + UNIV_PAGE_SIZE)); + os_offset_t current_size = 0; +#endif + buf2 = static_cast<byte*>(calloc(1, buf_size + UNIV_PAGE_SIZE)); + + if (!buf2) { + ib_logf(IB_LOG_LEVEL_ERROR, + "Cannot allocate " ULINTPF " bytes to extend file\n", + buf_size + UNIV_PAGE_SIZE); + return(FALSE); + } /* Align the buffer for possible raw i/o */ buf = static_cast<byte*>(ut_align(buf2, UNIV_PAGE_SIZE)); - /* Write buffer full of zeros */ - memset(buf, 0, buf_size); - - if (size >= (os_offset_t) 100 << 20) { - - fprintf(stderr, "InnoDB: Progress in MB:"); - } - - while (current_size < size) { + do { ulint n_bytes; if (size - current_size < (os_offset_t) buf_size) { @@ -2408,37 +2390,15 @@ os_file_set_size( ret = os_file_write(name, file, buf, current_size, n_bytes); if (!ret) { - ut_free(buf2); - goto error_handling; - } - - /* Print about progress for each 100 MB written */ - if ((current_size + n_bytes) / (100 << 20) - != current_size / (100 << 20)) { - - fprintf(stderr, " %lu00", - (ulong) ((current_size + n_bytes) - / (100 << 20))); + break; } current_size += n_bytes; - } + } while (current_size < size); - if (size >= (os_offset_t) 100 << 20) { - - fprintf(stderr, "\n"); - } + free(buf2); - ut_free(buf2); - - ret = os_file_flush(file); - - if (ret) { - return(TRUE); - } - -error_handling: - return(FALSE); + return(ret && os_file_flush(file)); } /***********************************************************************//** @@ -4240,13 +4200,6 @@ os_aio_init( os_aio_validate(); - os_aio_segment_wait_events = static_cast<os_event_t*>( - ut_malloc(n_segments * sizeof *os_aio_segment_wait_events)); - - for (ulint i = 0; i < n_segments; ++i) { - os_aio_segment_wait_events[i] = os_event_create(); - } - os_last_printout = ut_time(); #ifdef _WIN32 @@ -4256,8 +4209,18 @@ os_aio_init( ut_a(completion_port && read_completion_port); #endif - return(TRUE); + if (srv_use_native_aio) { + return(TRUE); + } + + os_aio_segment_wait_events = static_cast<os_event_t*>( + ut_malloc(n_segments * sizeof *os_aio_segment_wait_events)); + + for (ulint i = 0; i < n_segments; ++i) { + os_aio_segment_wait_events[i] = os_event_create(); + } + return(TRUE); } /*********************************************************************** @@ -4285,8 +4248,10 @@ os_aio_free(void) os_aio_array_free(os_aio_read_array); - for (ulint i = 0; i < os_aio_n_segments; i++) { - os_event_free(os_aio_segment_wait_events[i]); + if (!srv_use_native_aio) { + for (ulint i = 0; i < os_aio_n_segments; i++) { + os_event_free(os_aio_segment_wait_events[i]); + } } #if !defined(HAVE_ATOMIC_BUILTINS) || UNIV_WORD_SIZE < 8 @@ -4346,22 +4311,17 @@ os_aio_wake_all_threads_at_shutdown(void) if (os_aio_log_array != 0) { os_aio_array_wake_win_aio_at_shutdown(os_aio_log_array); } - #elif defined(LINUX_NATIVE_AIO) - /* When using native AIO interface the io helper threads wait on io_getevents with a timeout value of 500ms. At each wake up these threads check the server status. No need to do anything to wake them up. */ +#endif /* !WIN_ASYNC_AIO */ if (srv_use_native_aio) { return; } - /* Fall through to simulated AIO handler wakeup if we are - not using native AIO. */ -#endif /* !WIN_ASYNC_AIO */ - /* This loop wakes up all simulated ai/o threads */ for (ulint i = 0; i < os_aio_n_segments; i++) { @@ -4729,6 +4689,7 @@ os_aio_simulated_wake_handler_threads(void) } } +#ifdef _WIN32 /**********************************************************************//** This function can be called if one wants to post a batch of reads and prefers an i/o-handler thread to handle them all at once later. You must @@ -4736,15 +4697,14 @@ call os_aio_simulated_wake_handler_threads later to ensure the threads are not left sleeping! */ UNIV_INTERN void -os_aio_simulated_put_read_threads_to_sleep(void) -/*============================================*/ +os_aio_simulated_put_read_threads_to_sleep() { /* The idea of putting background IO threads to sleep is only for Windows when using simulated AIO. Windows XP seems to schedule background threads too eagerly to allow for coalescing during readahead requests. */ -#ifdef __WIN__ + os_aio_array_t* array; if (srv_use_native_aio) { @@ -4763,8 +4723,8 @@ readahead requests. */ os_event_reset(os_aio_segment_wait_events[i]); } } -#endif /* __WIN__ */ } +#endif /* _WIN32 */ #if defined(LINUX_NATIVE_AIO) /*******************************************************************//** @@ -5934,11 +5894,12 @@ os_aio_print( srv_io_thread_op_info[i], srv_io_thread_function[i]); -#ifndef __WIN__ - if (os_aio_segment_wait_events[i]->is_set()) { +#ifndef _WIN32 + if (!srv_use_native_aio + && os_aio_segment_wait_events[i]->is_set()) { fprintf(file, " ev set"); } -#endif /* __WIN__ */ +#endif /* _WIN32 */ fprintf(file, "\n"); } diff --git a/storage/xtradb/os/os0thread.cc b/storage/xtradb/os/os0thread.cc index af826027efc..cb45929cab6 100644 --- a/storage/xtradb/os/os0thread.cc +++ b/storage/xtradb/os/os0thread.cc @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 1995, 2013, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 1995, 2016, 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 @@ -210,29 +210,32 @@ os_thread_create_func( #endif } -/** -Waits until the specified thread completes and joins it. Its return value is -ignored. - -@param thread thread to join */ +/** Waits until the specified thread completes and joins it. +Its return value is ignored. +@param[in,out] thread thread to join */ UNIV_INTERN void os_thread_join( os_thread_t thread) { - /*This function is currently only used to workaround glibc bug + /* This function is currently only used to workaround glibc bug described in http://bugs.mysql.com/bug.php?id=82886 On Windows, no workarounds are necessary, all threads are "detached" upon thread exit (handle is closed), so we do nothing. */ -#ifndef _WIN32 - int ret MY_ATTRIBUTE((unused)) = pthread_join(thread, NULL); +#ifdef __WIN__ + /* Do nothing. */ +#else +#ifdef UNIV_DEBUG + const int ret MY_ATTRIBUTE((unused)) = +#endif /* UNIV_DEBUG */ + pthread_join(thread, NULL); - /* Waiting on already-quit threads is allowed */ + /* Waiting on already-quit threads is allowed. */ ut_ad(ret == 0 || ret == ESRCH); -#endif +#endif /* __WIN__ */ } /*****************************************************************//** @@ -261,8 +264,9 @@ os_thread_exit( #ifdef __WIN__ ExitThread((DWORD) exit_value); #else - if (detach) + if (detach) { pthread_detach(pthread_self()); + } pthread_exit(exit_value); #endif } diff --git a/storage/xtradb/page/page0page.cc b/storage/xtradb/page/page0page.cc index 48c4b53aaa4..1a8271de1e4 100644 --- a/storage/xtradb/page/page0page.cc +++ b/storage/xtradb/page/page0page.cc @@ -1447,7 +1447,6 @@ page_dir_split_slot( ulint i; ulint n_owned; - ut_ad(page); ut_ad(!page_zip || page_is_comp(page)); ut_ad(slot_no > 0); @@ -1509,7 +1508,6 @@ page_dir_balance_slot( rec_t* old_rec; rec_t* new_rec; - ut_ad(page); ut_ad(!page_zip || page_is_comp(page)); ut_ad(slot_no > 0); diff --git a/storage/xtradb/page/page0zip.cc b/storage/xtradb/page/page0zip.cc index 21008ff0ffe..0d2bb7fb986 100644 --- a/storage/xtradb/page/page0zip.cc +++ b/storage/xtradb/page/page0zip.cc @@ -4814,8 +4814,6 @@ page_zip_parse_compress( ulint size; ulint trailer_size; - ut_ad(ptr != NULL); - ut_ad(end_ptr != NULL); ut_ad(!page == !page_zip); if (UNIV_UNLIKELY(ptr + (2 + 2) > end_ptr)) { diff --git a/storage/xtradb/rem/rem0rec.cc b/storage/xtradb/rem/rem0rec.cc index f3de279e182..8daece64b68 100644 --- a/storage/xtradb/rem/rem0rec.cc +++ b/storage/xtradb/rem/rem0rec.cc @@ -789,7 +789,7 @@ rec_get_nth_field_offs_old( /**********************************************************//** Determines the size of a data tuple prefix in ROW_FORMAT=COMPACT. @return total size */ -UNIV_INLINE MY_ATTRIBUTE((warn_unused_result, nonnull(1,2))) +UNIV_INLINE MY_ATTRIBUTE((warn_unused_result)) ulint rec_get_converted_size_comp_prefix_low( /*===================================*/ diff --git a/storage/xtradb/row/row0ftsort.cc b/storage/xtradb/row/row0ftsort.cc index cb47d605623..134127c6adf 100644 --- a/storage/xtradb/row/row0ftsort.cc +++ b/storage/xtradb/row/row0ftsort.cc @@ -58,6 +58,8 @@ tokenized doc string. The index has three "fields": integer value) 3) Word's position in original doc. +@see fts_create_one_index_table() + @return dict_index_t structure for the fts sort index */ UNIV_INTERN dict_index_t* @@ -99,16 +101,12 @@ row_merge_create_fts_sort_index( field->prefix_len = 0; field->col = static_cast<dict_col_t*>( mem_heap_alloc(new_index->heap, sizeof(dict_col_t))); - field->col->len = FTS_MAX_WORD_LEN; - - if (strcmp(charset->name, "latin1_swedish_ci") == 0) { - field->col->mtype = DATA_VARCHAR; - } else { - field->col->mtype = DATA_VARMYSQL; - } - field->col->prtype = idx_field->col->prtype | DATA_NOT_NULL; + field->col->mtype = charset == &my_charset_latin1 + ? DATA_VARCHAR : DATA_VARMYSQL; field->col->mbminmaxlen = idx_field->col->mbminmaxlen; + field->col->len = HA_FT_MAXCHARLEN * DATA_MBMAXLEN(field->col->mbminmaxlen); + field->fixed_len = 0; /* Doc ID */ @@ -362,6 +360,7 @@ row_fts_free_pll_merge_buf( /*********************************************************************//** Tokenize incoming text data and add to the sort buffer. +@see row_merge_buf_encode() @return TRUE if the record passed, FALSE if out of space */ static ibool @@ -370,8 +369,6 @@ row_merge_fts_doc_tokenize( row_merge_buf_t** sort_buf, /*!< in/out: sort buffer */ doc_id_t doc_id, /*!< in: Doc ID */ fts_doc_t* doc, /*!< in: Doc to be tokenized */ - dtype_t* word_dtype, /*!< in: data structure for - word col */ merge_file_t** merge_file, /*!< in/out: merge file */ ibool opt_doc_id_size,/*!< in: whether to use 4 bytes instead of 8 bytes integer to @@ -403,7 +400,7 @@ row_merge_fts_doc_tokenize( ulint idx = 0; ib_uint32_t position; ulint offset = 0; - ulint cur_len = 0; + ulint cur_len; doc_id_t write_doc_id; inc = innobase_mysql_fts_get_token( @@ -457,14 +454,34 @@ row_merge_fts_doc_tokenize( dfield_set_data(field, t_str.f_str, t_str.f_len); len = dfield_get_len(field); - field->type.mtype = word_dtype->mtype; - field->type.prtype = word_dtype->prtype | DATA_NOT_NULL; + dict_col_copy_type(dict_index_get_nth_col(buf->index, 0), &field->type); + field->type.prtype |= DATA_NOT_NULL; + ut_ad(len <= field->type.len); - /* Variable length field, set to max size. */ - field->type.len = FTS_MAX_WORD_LEN; - field->type.mbminmaxlen = word_dtype->mbminmaxlen; + /* For the temporary file, row_merge_buf_encode() uses + 1 byte for representing the number of extra_size bytes. + This number will always be 1, because for this 3-field index + consisting of one variable-size column, extra_size will always + be 1 or 2, which can be encoded in one byte. + + The extra_size is 1 byte if the length of the + variable-length column is less than 128 bytes or the + maximum length is less than 256 bytes. */ + + /* One variable length column, word with its lenght less than + fts_max_token_size, add one extra size and one extra byte. + + Since the max length for FTS token now is larger than 255, + so we will need to signify length byte itself, so only 1 to 128 + bytes can be used for 1 bytes, larger than that 2 bytes. */ + if (len < 128 || field->type.len < 256) { + /* Extra size is one byte. */ + cur_len = 2 + len; + } else { + /* Extra size is two bytes. */ + cur_len = 3 + len; + } - cur_len += len; dfield_dup(field, buf->heap); field++; @@ -514,20 +531,6 @@ row_merge_fts_doc_tokenize( cur_len += len; dfield_dup(field, buf->heap); - /* One variable length column, word with its lenght less than - fts_max_token_size, add one extra size and one extra byte. - - Since the max length for FTS token now is larger than 255, - so we will need to signify length byte itself, so only 1 to 128 - bytes can be used for 1 bytes, larger than that 2 bytes. */ - if (t_str.f_len < 128) { - /* Extra size is one byte. */ - cur_len += 2; - } else { - /* Extra size is two bytes. */ - cur_len += 3; - } - /* Reserve one byte for the end marker of row_merge_block_t. */ if (buf->total_size + data_size[idx] + cur_len >= srv_sort_buf_size - 1) { @@ -620,8 +623,6 @@ fts_parallel_tokenization( mem_heap_t* blob_heap = NULL; fts_doc_t doc; dict_table_t* table = psort_info->psort_common->new_table; - dtype_t word_dtype; - dict_field_t* idx_field; fts_tokenize_ctx_t t_ctx; ulint retried = 0; dberr_t error = DB_SUCCESS; @@ -643,13 +644,6 @@ fts_parallel_tokenization( doc.charset = fts_index_get_charset( psort_info->psort_common->dup->index); - idx_field = dict_index_get_nth_field( - psort_info->psort_common->dup->index, 0); - word_dtype.prtype = idx_field->col->prtype; - word_dtype.mbminmaxlen = idx_field->col->mbminmaxlen; - word_dtype.mtype = (strcmp(doc.charset->name, "latin1_swedish_ci") == 0) - ? DATA_VARCHAR : DATA_VARMYSQL; - block = psort_info->merge_block; zip_size = dict_table_zip_size(table); @@ -699,7 +693,6 @@ loop: processed = row_merge_fts_doc_tokenize( buf, doc_item->doc_id, &doc, - &word_dtype, merge_file, psort_info->psort_common->opt_doc_id_size, &t_ctx); diff --git a/storage/xtradb/row/row0merge.cc b/storage/xtradb/row/row0merge.cc index c1436f9e1b7..bcc37fb530c 100644 --- a/storage/xtradb/row/row0merge.cc +++ b/storage/xtradb/row/row0merge.cc @@ -952,14 +952,8 @@ row_merge_read_rec( ulint data_size; ulint avail_size; - ut_ad(block); - ut_ad(buf); ut_ad(b >= &block[0]); ut_ad(b < &block[srv_sort_buf_size]); - ut_ad(index); - ut_ad(foffs); - ut_ad(mrec); - ut_ad(offsets); ut_ad(*offsets == 1 + REC_OFFS_HEADER_SIZE + dict_index_get_n_fields(index)); @@ -3833,8 +3827,8 @@ wait_again: for (j = 0; j < FTS_NUM_AUX_INDEX; j++) { - os_thread_join(merge_info[j] - .thread_hdl); + os_thread_join(merge_info[j] + .thread_hdl); } } } else { diff --git a/storage/xtradb/row/row0mysql.cc b/storage/xtradb/row/row0mysql.cc index b366a55b2d9..68b4e1ecea5 100644 --- a/storage/xtradb/row/row0mysql.cc +++ b/storage/xtradb/row/row0mysql.cc @@ -1365,6 +1365,8 @@ run_again: row_ins_step(thr); + DEBUG_SYNC_C("ib_after_row_insert_step"); + err = trx->error_state; if (err != DB_SUCCESS) { diff --git a/storage/xtradb/row/row0purge.cc b/storage/xtradb/row/row0purge.cc index bc2e0b0e1cb..35b3520749b 100644 --- a/storage/xtradb/row/row0purge.cc +++ b/storage/xtradb/row/row0purge.cc @@ -897,7 +897,7 @@ row_purge_record_func( Fetches an undo log record and does the purge for the recorded operation. If none left, or the current purge completed, returns the control to the parent node, which is always a query thread node. */ -static MY_ATTRIBUTE((nonnull)) +static void row_purge( /*======*/ diff --git a/storage/xtradb/row/row0upd.cc b/storage/xtradb/row/row0upd.cc index e43a18a43af..3881fe410ec 100644 --- a/storage/xtradb/row/row0upd.cc +++ b/storage/xtradb/row/row0upd.cc @@ -1281,8 +1281,6 @@ row_upd_index_replace_new_col_vals_index_pos( ulint n_fields; const ulint zip_size = dict_table_zip_size(index->table); - ut_ad(index); - dtuple_set_info_bits(entry, update->info_bits); if (order_only) { @@ -1467,8 +1465,6 @@ row_upd_changes_ord_field_binary_func( ulint i; const dict_index_t* clust_index; - ut_ad(index); - ut_ad(update); ut_ad(thr); ut_ad(thr->graph); ut_ad(thr->graph->trx); diff --git a/storage/xtradb/srv/srv0conc.cc b/storage/xtradb/srv/srv0conc.cc index 0580d7d0054..dadb1f19a8a 100644 --- a/storage/xtradb/srv/srv0conc.cc +++ b/storage/xtradb/srv/srv0conc.cc @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 2011, 2012, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2017, MariaDB Corporation. All Rights Reserved. Portions of this file contain modifications contributed and copyrighted by Google, Inc. Those modifications are gratefully acknowledged and are described @@ -82,7 +83,9 @@ typedef UT_LIST_NODE_T(struct srv_conc_slot_t) srv_conc_node_t; /** Slot for a thread waiting in the concurrency control queue. */ struct srv_conc_slot_t{ - os_event_t event; /*!< event to wait */ + os_event_t event; /*!< event to wait for; + os_event_set() and os_event_reset() + are protected by srv_conc_mutex */ ibool reserved; /*!< TRUE if slot reserved */ ibool wait_ended; /*!< TRUE when another thread has @@ -381,11 +384,11 @@ srv_conc_exit_innodb_without_atomics( } } - os_fast_mutex_unlock(&srv_conc_mutex); - if (slot != NULL) { os_event_set(slot->event); } + + os_fast_mutex_unlock(&srv_conc_mutex); } /*********************************************************************//** diff --git a/storage/xtradb/srv/srv0srv.cc b/storage/xtradb/srv/srv0srv.cc index c04b446f480..a76c6aaf62b 100644 --- a/storage/xtradb/srv/srv0srv.cc +++ b/storage/xtradb/srv/srv0srv.cc @@ -3,7 +3,7 @@ Copyright (c) 1995, 2016, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2008, 2009 Google Inc. Copyright (c) 2009, Percona Inc. -Copyright (c) 2013, 2014, SkySQL Ab. All Rights Reserved. +Copyright (c) 2013, 2017, MariaDB Corporation Ab. All Rights Reserved. Portions of this file contain modifications contributed and copyrighted by Google, Inc. Those modifications are gratefully acknowledged and are described @@ -74,12 +74,6 @@ Created 10/8/1995 Heikki Tuuri #include "mysql/plugin.h" #include "mysql/service_thd_wait.h" -/* prototypes of new functions added to ha_innodb.cc for kill_idle_transaction */ -ibool innobase_thd_is_idle(const void* thd); -ib_int64_t innobase_thd_get_start_time(const void* thd); -void innobase_thd_kill(ulong thd_id); -ulong innobase_thd_get_thread_id(const void* thd); - /* prototypes for new functions added to ha_innodb.cc */ ibool innobase_get_slow_log(); @@ -440,11 +434,6 @@ starting from SRV_FORCE_IGNORE_CORRUPT, so that data can be recovered by SELECT or mysqldump. When this is nonzero, we do not allow any user modifications to the data. */ UNIV_INTERN ulong srv_force_recovery; -#ifndef DBUG_OFF -/** Inject a crash at different steps of the recovery process. -This is for testing and debugging only. */ -UNIV_INTERN ulong srv_force_recovery_crash; -#endif /* !DBUG_OFF */ /** Print all user-level transactions deadlocks to mysqld stderr */ @@ -473,6 +462,7 @@ this many index pages, there are 2 ways to calculate statistics: table/index are not found in the innodb database */ UNIV_INTERN unsigned long long srv_stats_transient_sample_pages = 8; UNIV_INTERN my_bool srv_stats_persistent = TRUE; +UNIV_INTERN my_bool srv_stats_include_delete_marked = FALSE; UNIV_INTERN unsigned long long srv_stats_persistent_sample_pages = 20; UNIV_INTERN my_bool srv_stats_auto_recalc = TRUE; @@ -747,7 +737,11 @@ struct srv_sys_t{ ulint n_sys_threads; /*!< size of the sys_threads array */ - srv_slot_t* sys_threads; /*!< server thread table */ + srv_slot_t* sys_threads; /*!< server thread table; + os_event_set() and + os_event_reset() on + sys_threads[]->event are + covered by srv_sys_t::mutex */ ulint n_threads_active[SRV_MASTER + 1]; /*!< number of threads active @@ -769,13 +763,16 @@ UNIV_INTERN ib_mutex_t server_mutex; static srv_sys_t* srv_sys = NULL; -/** Event to signal the monitor thread. */ +/** Event to signal srv_monitor_thread. Not protected by a mutex. +Set after setting srv_print_innodb_monitor. */ UNIV_INTERN os_event_t srv_monitor_event; -/** Event to signal the error thread */ +/** Event to signal the shutdown of srv_error_monitor_thread. +Not protected by a mutex. */ UNIV_INTERN os_event_t srv_error_event; -/** Event to signal the buffer pool dump/load thread */ +/** Event for waking up buf_dump_thread. Not protected by a mutex. +Set on shutdown or by buf_dump_start() or buf_load_start(). */ UNIV_INTERN os_event_t srv_buf_dump_event; /** The buffer pool dump/load file name */ @@ -945,7 +942,6 @@ srv_suspend_thread_low( /*===================*/ srv_slot_t* slot) /*!< in/out: thread slot */ { - ut_ad(!srv_read_only_mode); ut_ad(srv_sys_mutex_own()); @@ -1003,34 +999,71 @@ srv_suspend_thread( return(sig_count); } -/*********************************************************************//** -Releases threads of the type given from suspension in the thread table. -NOTE! The server mutex has to be reserved by the caller! -@return number of threads released: this may be less than n if not - enough threads were suspended at the moment. */ -UNIV_INTERN -ulint -srv_release_threads( -/*================*/ - srv_thread_type type, /*!< in: thread type */ - ulint n) /*!< in: number of threads to release */ +/** Resume the calling thread. +@param[in,out] slot thread slot +@param[in] sig_count signal count (if wait) +@param[in] wait whether to wait for the event +@param[in] timeout_usec timeout in microseconds (0=infinite) +@return whether the wait timed out */ +static +bool +srv_resume_thread(srv_slot_t* slot, ib_int64_t sig_count = 0, bool wait = true, + ulint timeout_usec = 0) +{ + bool timeout; + + ut_ad(!srv_read_only_mode); + ut_ad(slot->in_use); + ut_ad(slot->suspended); + + if (!wait) { + timeout = false; + } else if (timeout_usec) { + timeout = OS_SYNC_TIME_EXCEEDED == os_event_wait_time_low( + slot->event, timeout_usec, sig_count); + } else { + timeout = false; + os_event_wait_low(slot->event, sig_count); + } + + srv_sys_mutex_enter(); + ut_ad(slot->in_use); + ut_ad(slot->suspended); + + slot->suspended = FALSE; + ++srv_sys->n_threads_active[slot->type]; + srv_sys_mutex_exit(); + return(timeout); +} + +/** Ensure that a given number of threads of the type given are running +(or are already terminated). +@param[in] type thread type +@param[in] n number of threads that have to run */ +void +srv_release_threads(enum srv_thread_type type, ulint n) { - ulint i; - ulint count = 0; + ulint running; ut_ad(srv_thread_type_validate(type)); ut_ad(n > 0); - srv_sys_mutex_enter(); + do { + running = 0; - for (i = 0; i < srv_sys->n_sys_threads; i++) { - srv_slot_t* slot; + srv_sys_mutex_enter(); - slot = &srv_sys->sys_threads[i]; + for (ulint i = 0; i < srv_sys->n_sys_threads; i++) { + srv_slot_t* slot = &srv_sys->sys_threads[i]; - if (slot->in_use - && srv_slot_get_type(slot) == type - && slot->suspended) { + if (!slot->in_use || srv_slot_get_type(slot) != type) { + continue; + } else if (!slot->suspended) { + if (++running >= n) { + break; + } + continue; + } switch (type) { case SRV_NONE: @@ -1060,21 +1093,11 @@ srv_release_threads( break; } - slot->suspended = FALSE; - - ++srv_sys->n_threads_active[type]; - os_event_set(slot->event); - - if (++count == n) { - break; - } } - } - srv_sys_mutex_exit(); - - return(count); + srv_sys_mutex_exit(); + } while (running && running < n); } /*********************************************************************//** @@ -1087,11 +1110,8 @@ srv_free_slot( { srv_sys_mutex_enter(); - if (!slot->suspended) { - /* Mark the thread as inactive. */ - srv_suspend_thread_low(slot); - } - + /* Mark the thread as inactive. */ + srv_suspend_thread_low(slot); /* Free the slot for reuse. */ ut_ad(slot->in_use); slot->in_use = FALSE; @@ -1402,22 +1422,26 @@ srv_printf_innodb_monitor( low level 135. Therefore we can reserve the latter mutex here without a danger of a deadlock of threads. */ - mutex_enter(&dict_foreign_err_mutex); + if (!recv_recovery_on) { - if (!srv_read_only_mode && ftell(dict_foreign_err_file) != 0L) { - fputs("------------------------\n" - "LATEST FOREIGN KEY ERROR\n" - "------------------------\n", file); - ut_copy_file(file, dict_foreign_err_file); - } + mutex_enter(&dict_foreign_err_mutex); - mutex_exit(&dict_foreign_err_mutex); + if (!srv_read_only_mode + && ftell(dict_foreign_err_file) != 0L) { + fputs("------------------------\n" + "LATEST FOREIGN KEY ERROR\n" + "------------------------\n", file); + ut_copy_file(file, dict_foreign_err_file); + } + + mutex_exit(&dict_foreign_err_mutex); + } /* Only if lock_print_info_summary proceeds correctly, before we call the lock_print_info_all_transactions to print all the lock information. IMPORTANT NOTE: This function acquires the lock mutex on success. */ - ret = lock_print_info_summary(file, nowait); + ret = recv_recovery_on ? FALSE : lock_print_info_summary(file, nowait); if (ret) { if (trx_start_pos) { @@ -1450,10 +1474,13 @@ srv_printf_innodb_monitor( "--------\n", file); os_aio_print(file); - fputs("-------------------------------------\n" - "INSERT BUFFER AND ADAPTIVE HASH INDEX\n" - "-------------------------------------\n", file); - ibuf_print(file); + if (!recv_recovery_on) { + + fputs("-------------------------------------\n" + "INSERT BUFFER AND ADAPTIVE HASH INDEX\n" + "-------------------------------------\n", file); + ibuf_print(file); + } fprintf(file, @@ -1465,10 +1492,13 @@ srv_printf_innodb_monitor( btr_cur_n_sea_old = btr_cur_n_sea; btr_cur_n_non_sea_old = btr_cur_n_non_sea; - fputs("---\n" - "LOG\n" - "---\n", file); - log_print(file); + if (!recv_recovery_on) { + + fputs("---\n" + "LOG\n" + "---\n", file); + log_print(file); + } fputs("----------------------\n" "BUFFER POOL AND MEMORY\n" @@ -1563,8 +1593,9 @@ srv_printf_innodb_monitor( ? (recv_sys->addr_hash->n_cells * sizeof(hash_cell_t)) : 0), recv_sys_subtotal); + fprintf(file, "Dictionary memory allocated " ULINTPF "\n", - dict_sys->size); + dict_sys ? dict_sys->size : 0); buf_print_io(file); @@ -1672,6 +1703,10 @@ srv_printf_innodb_monitor( mutex_exit(&srv_innodb_monitor_mutex); fflush(file); +#ifndef DBUG_OFF + srv_debug_monitor_printed = true; +#endif + return(ret); } @@ -1982,6 +2017,12 @@ srv_export_innodb_status(void) mutex_exit(&srv_innodb_monitor_mutex); } +#ifndef DBUG_OFF +/** false before InnoDB monitor has been printed at least once, true +afterwards */ +bool srv_debug_monitor_printed = false; +#endif + /*********************************************************************//** A thread which prints the info output by various InnoDB monitors. @return a dummy parameter */ @@ -2266,36 +2307,6 @@ loop: old_sema = sema; } - if (srv_kill_idle_transaction && trx_sys) { - trx_t* trx; - time_t now; -rescan_idle: - now = time(NULL); - mutex_enter(&trx_sys->mutex); - trx = UT_LIST_GET_FIRST(trx_sys->mysql_trx_list); - while (trx) { - if (trx->state == TRX_STATE_ACTIVE - && trx->mysql_thd - && innobase_thd_is_idle(trx->mysql_thd)) { - ib_int64_t start_time = innobase_thd_get_start_time(trx->mysql_thd); - ulong thd_id = innobase_thd_get_thread_id(trx->mysql_thd); - - if (trx->last_stmt_start != start_time) { - trx->idle_start = now; - trx->last_stmt_start = start_time; - } else if (difftime(now, trx->idle_start) - > srv_kill_idle_transaction) { - /* kill the session */ - mutex_exit(&trx_sys->mutex); - innobase_thd_kill(thd_id); - goto rescan_idle; - } - } - trx = UT_LIST_GET_NEXT(mysql_trx_list, trx); - } - mutex_exit(&trx_sys->mutex); - } - /* Flush stderr so that a database user gets the output to possible MySQL error file */ @@ -2451,10 +2462,8 @@ DECLARE_THREAD(srv_redo_log_follow_thread)( } while (srv_shutdown_state < SRV_SHUTDOWN_LAST_PHASE); - srv_track_changed_pages = FALSE; log_online_read_shutdown(); os_event_set(srv_redo_log_tracked_event); - srv_redo_log_thread_started = false; /* Defensive, not required */ my_thread_end(); os_thread_exit(NULL); @@ -2620,15 +2629,7 @@ srv_active_wake_master_thread(void) if (slot->in_use) { ut_a(srv_slot_get_type(slot) == SRV_MASTER); - - if (slot->suspended) { - - slot->suspended = FALSE; - - ++srv_sys->n_threads_active[SRV_MASTER]; - - os_event_set(slot->event); - } + os_event_set(slot->event); } srv_sys_mutex_exit(); @@ -3149,7 +3150,7 @@ suspend_thread: manual also mentions this string in several places. */ srv_main_thread_op_info = "waiting for server activity"; - os_event_wait(slot->event); + srv_resume_thread(slot); if (srv_shutdown_state == SRV_SHUTDOWN_EXIT_THREADS) { os_thread_exit(NULL); @@ -3271,8 +3272,7 @@ DECLARE_THREAD(srv_worker_thread)( do { srv_suspend_thread(slot); - - os_event_wait(slot->event); + srv_resume_thread(slot); srv_current_thread_priority = srv_purge_thread_priority; @@ -3412,8 +3412,6 @@ srv_purge_coordinator_suspend( ib_int64_t sig_count = srv_suspend_thread(slot); do { - ulint ret; - rw_lock_x_lock(&purge_sys->latch); purge_sys->running = false; @@ -3422,32 +3420,11 @@ srv_purge_coordinator_suspend( /* We don't wait right away on the the non-timed wait because we want to signal the thread that wants to suspend purge. */ - - if (stop) { - os_event_wait_low(slot->event, sig_count); - ret = 0; - } else if (rseg_history_len <= trx_sys->rseg_history_len) { - ret = os_event_wait_time_low( - slot->event, SRV_PURGE_MAX_TIMEOUT, sig_count); - } else { - /* We don't want to waste time waiting, if the - history list increased by the time we got here, - unless purge has been stopped. */ - ret = 0; - } - - srv_sys_mutex_enter(); - - /* The thread can be in state !suspended after the timeout - but before this check if another thread sent a wakeup signal. */ - - if (slot->suspended) { - slot->suspended = FALSE; - ++srv_sys->n_threads_active[slot->type]; - ut_a(srv_sys->n_threads_active[slot->type] == 1); - } - - srv_sys_mutex_exit(); + const bool wait = stop + || rseg_history_len <= trx_sys->rseg_history_len; + const bool timeout = srv_resume_thread( + slot, sig_count, wait, + stop ? 0 : SRV_PURGE_MAX_TIMEOUT); sig_count = srv_suspend_thread(slot); @@ -3459,6 +3436,19 @@ srv_purge_coordinator_suspend( if (!stop) { ut_a(purge_sys->n_stop == 0); purge_sys->running = true; + + if (timeout + && rseg_history_len == trx_sys->rseg_history_len + && trx_sys->rseg_history_len < 5000) { + /* No new records were added since the + wait started. Simply wait for new + records. The magic number 5000 is an + approximation for the case where we + have cached UNDO log records which + prevent truncate of the UNDO + segments. */ + stop = true; + } } else { ut_a(purge_sys->n_stop > 0); @@ -3467,33 +3457,9 @@ srv_purge_coordinator_suspend( } rw_lock_x_unlock(&purge_sys->latch); - - if (ret == OS_SYNC_TIME_EXCEEDED) { - - /* No new records added since wait started then simply - wait for new records. The magic number 5000 is an - approximation for the case where we have cached UNDO - log records which prevent truncate of the UNDO - segments. */ - - if (rseg_history_len == trx_sys->rseg_history_len - && trx_sys->rseg_history_len < 5000) { - - stop = true; - } - } - } while (stop); - srv_sys_mutex_enter(); - - if (slot->suspended) { - slot->suspended = FALSE; - ++srv_sys->n_threads_active[slot->type]; - ut_a(srv_sys->n_threads_active[slot->type] == 1); - } - - srv_sys_mutex_exit(); + srv_resume_thread(slot, 0, false); } /*********************************************************************//** @@ -3549,8 +3515,9 @@ DECLARE_THREAD(srv_purge_coordinator_thread)( srv_purge_coordinator_suspend(slot, rseg_history_len); } + ut_ad(!slot->suspended); + if (srv_purge_should_exit(n_total_purged)) { - ut_a(!slot->suspended); break; } @@ -3665,12 +3632,10 @@ srv_get_task_queue_length(void) return(n_tasks); } -/**********************************************************************//** -Wakeup the purge threads. */ +/** Wake up the purge threads. */ UNIV_INTERN void -srv_purge_wakeup(void) -/*==================*/ +srv_purge_wakeup() { ut_ad(!srv_read_only_mode); @@ -3685,4 +3650,3 @@ srv_purge_wakeup(void) } } } - diff --git a/storage/xtradb/srv/srv0start.cc b/storage/xtradb/srv/srv0start.cc index 270489a1973..35b378ec831 100644 --- a/storage/xtradb/srv/srv0start.cc +++ b/storage/xtradb/srv/srv0start.cc @@ -613,19 +613,6 @@ create_log_file( /** Initial number of the first redo log file */ #define INIT_LOG_FILE0 (SRV_N_LOG_FILES_MAX + 1) -#ifdef DBUG_OFF -# define RECOVERY_CRASH(x) do {} while(0) -#else -# define RECOVERY_CRASH(x) do { \ - if (srv_force_recovery_crash == x) { \ - fprintf(stderr, "innodb_force_recovery_crash=%lu\n", \ - srv_force_recovery_crash); \ - fflush(stderr); \ - exit(3); \ - } \ -} while (0) -#endif - /*********************************************************************//** Creates all log files. @return DB_SUCCESS or error code */ @@ -666,13 +653,14 @@ create_log_files( file should be recoverable. The buffer pool was clean, and we can simply create all log files from the scratch. */ - RECOVERY_CRASH(6); + DBUG_EXECUTE_IF("innodb_log_abort_6", + return(DB_ERROR);); } } ut_ad(!buf_pool_check_no_pending_io()); - RECOVERY_CRASH(7); + DBUG_EXECUTE_IF("innodb_log_abort_7", return(DB_ERROR);); for (unsigned i = 0; i < srv_n_log_files; i++) { sprintf(logfilename + dirnamelen, @@ -685,7 +673,7 @@ create_log_files( } } - RECOVERY_CRASH(8); + DBUG_EXECUTE_IF("innodb_log_abort_8", return(DB_ERROR);); /* We did not create the first log file initially as ib_logfile0, so that crash recovery cannot find it until it @@ -740,10 +728,16 @@ create_log_files( return(DB_SUCCESS); } -/*********************************************************************//** -Renames the first log file. */ +/** Rename the first redo log file. +@param[in,out] logfilename buffer for the log file name +@param[in] dirnamelen length of the directory path +@param[in] lsn FIL_PAGE_FILE_FLUSH_LSN value +@param[in,out] logfile0 name of the first log file +@return error code +@retval DB_SUCCESS on successful operation */ +MY_ATTRIBUTE((warn_unused_result, nonnull)) static -void +dberr_t create_log_files_rename( /*====================*/ char* logfilename, /*!< in/out: buffer for log file name */ @@ -754,6 +748,9 @@ create_log_files_rename( /* If innodb_flush_method=O_DSYNC, we need to explicitly flush the log buffers. */ fil_flush(SRV_LOG_SPACE_FIRST_ID); + + DBUG_EXECUTE_IF("innodb_log_abort_9", return(DB_ERROR);); + /* Close the log files, so that we can rename the first one. */ fil_close_log_files(false); @@ -762,26 +759,28 @@ create_log_files_rename( checkpoint has been created. */ sprintf(logfilename + dirnamelen, "ib_logfile%u", 0); - RECOVERY_CRASH(9); - ib_logf(IB_LOG_LEVEL_INFO, "Renaming log file %s to %s", logfile0, logfilename); mutex_enter(&log_sys->mutex); ut_ad(strlen(logfile0) == 2 + strlen(logfilename)); - ibool success = os_file_rename( - innodb_file_log_key, logfile0, logfilename); - ut_a(success); - - RECOVERY_CRASH(10); + dberr_t err = os_file_rename( + innodb_file_log_key, logfile0, logfilename) + ? DB_SUCCESS : DB_ERROR; /* Replace the first file with ib_logfile0. */ strcpy(logfile0, logfilename); mutex_exit(&log_sys->mutex); - fil_open_log_and_system_tablespace_files(); + DBUG_EXECUTE_IF("innodb_log_abort_10", err = DB_ERROR;); - ib_logf(IB_LOG_LEVEL_WARN, "New log files created, LSN=" LSN_PF, lsn); + if (err == DB_SUCCESS) { + fil_open_log_and_system_tablespace_files(); + ib_logf(IB_LOG_LEVEL_WARN, + "New log files created, LSN=" LSN_PF, lsn); + } + + return(err); } /*********************************************************************//** @@ -1613,8 +1612,6 @@ innobase_start_or_create_for_mysql(void) lsn_t max_arch_log_no = LSN_MAX; #endif /* UNIV_LOG_ARCHIVE */ ulint sum_of_new_sizes; - ulint sum_of_data_file_sizes; - ulint tablespace_size_in_header; dberr_t err; unsigned i; ulint srv_n_log_files_found = srv_n_log_files; @@ -1627,6 +1624,19 @@ innobase_start_or_create_for_mysql(void) size_t dirnamelen; bool sys_datafiles_created = false; + /* Check that os_fast_mutexes work as expected */ + os_fast_mutex_init(PFS_NOT_INSTRUMENTED, &srv_os_test_mutex); + + ut_a(0 == os_fast_mutex_trylock(&srv_os_test_mutex)); + + os_fast_mutex_unlock(&srv_os_test_mutex); + + os_fast_mutex_lock(&srv_os_test_mutex); + + os_fast_mutex_unlock(&srv_os_test_mutex); + + os_fast_mutex_free(&srv_os_test_mutex); + high_level_read_only = srv_read_only_mode || srv_force_recovery > SRV_FORCE_NO_TRX_UNDO; @@ -1669,22 +1679,7 @@ innobase_start_or_create_for_mysql(void) #endif /* PAGE_ATOMIC_REF_COUNT */ ); - - if (sizeof(ulint) != sizeof(void*)) { - ut_print_timestamp(stderr); - fprintf(stderr, - " InnoDB: Error: size of InnoDB's ulint is %lu, " - "but size of void*\n", (ulong) sizeof(ulint)); - ut_print_timestamp(stderr); - fprintf(stderr, - " InnoDB: is %lu. The sizes should be the same " - "so that on a 64-bit\n", - (ulong) sizeof(void*)); - ut_print_timestamp(stderr); - fprintf(stderr, - " InnoDB: platforms you can allocate more than 4 GB " - "of memory.\n"); - } + compile_time_assert(sizeof(ulint) == sizeof(void*)); /* If stacktrace is used we set up signal handler for SIGUSR2 signal here. If signal handler set fails we report that and disable @@ -2108,6 +2103,7 @@ innobase_start_or_create_for_mysql(void) fsp_init(); log_init(); + log_online_init(); lock_sys_create(srv_lock_table_size); @@ -2282,14 +2278,18 @@ innobase_start_or_create_for_mysql(void) dirnamelen, max_flushed_lsn, logfile0); + if (err == DB_SUCCESS) { + err = create_log_files_rename( + logfilename, + dirnamelen, + max_flushed_lsn, + logfile0); + } + if (err != DB_SUCCESS) { return(err); } - create_log_files_rename( - logfilename, dirnamelen, - max_flushed_lsn, logfile0); - /* Suppress the message about crash recovery. */ max_flushed_lsn = min_flushed_lsn @@ -2414,8 +2414,9 @@ files_checked: trx_sys_create(); - if (create_new_db) { + bool srv_monitor_thread_started = false; + if (create_new_db) { ut_a(!srv_read_only_mode); init_log_online(); @@ -2459,8 +2460,12 @@ files_checked: fil_flush_file_spaces(FIL_TABLESPACE); - create_log_files_rename(logfilename, dirnamelen, - max_flushed_lsn, logfile0); + err = create_log_files_rename(logfilename, dirnamelen, + max_flushed_lsn, logfile0); + + if (err != DB_SUCCESS) { + return(err); + } } else { /* Check if we support the max format that is stamped @@ -2489,6 +2494,22 @@ files_checked: and there must be no page in the buf_flush list. */ buf_pool_invalidate(); + /* Start monitor thread early enough so that e.g. crash + recovery failing to find free pages in the buffer pool is + diagnosed. */ + if (!srv_read_only_mode) + { + /* Create the thread which prints InnoDB monitor + info */ + thread_handles[4 + SRV_MAX_N_IO_THREADS] = + os_thread_create( + srv_monitor_thread, + NULL, + thread_ids + 4 + SRV_MAX_N_IO_THREADS); + + thread_started[4 + SRV_MAX_N_IO_THREADS] = true; + } + /* We always try to do a recovery, even if the database had been shut down normally: this is the normal startup path */ @@ -2497,25 +2518,92 @@ files_checked: min_flushed_lsn, max_flushed_lsn); if (err != DB_SUCCESS) { - - return(DB_ERROR); + return(err); } init_log_online(); - /* Since the insert buffer init is in dict_boot, and the - insert buffer is needed in any disk i/o, first we call - dict_boot(). Note that trx_sys_init_at_db_start() only needs - to access space 0, and the insert buffer at this stage already - works for space 0. */ - + /* Initialize the change buffer. */ err = dict_boot(); if (err != DB_SUCCESS) { return(err); } + if (!srv_read_only_mode) { + if (sum_of_new_sizes > 0) { + /* New data file(s) were added */ + mtr_start(&mtr); + fsp_header_inc_size(0, sum_of_new_sizes, &mtr); + mtr_commit(&mtr); + /* Immediately write the log record about + increased tablespace size to disk, so that it + is durable even if mysqld would crash + quickly */ + log_buffer_flush_to_disk(); + } + } + + const ulint tablespace_size_in_header + = fsp_header_get_tablespace_size(); + +#ifdef UNIV_DEBUG + /* buf_debug_prints = TRUE; */ +#endif /* UNIV_DEBUG */ + ulint sum_of_data_file_sizes = 0; + + for (ulint d = 0; d < srv_n_data_files; d++) { + sum_of_data_file_sizes += srv_data_file_sizes[d]; + } + + /* Compare the system tablespace file size to what is + stored in FSP_SIZE. In open_or_create_data_files() + we already checked that the file sizes match the + innodb_data_file_path specification. */ + if (srv_read_only_mode + || sum_of_data_file_sizes == tablespace_size_in_header) { + /* Do not complain about the size. */ + } else if (!srv_auto_extend_last_data_file + || sum_of_data_file_sizes + < tablespace_size_in_header) { + ib_logf(IB_LOG_LEVEL_ERROR, + "Tablespace size stored in header is " ULINTPF + " pages, but the sum of data file sizes is " + ULINTPF " pages", + tablespace_size_in_header, + sum_of_data_file_sizes); + + if (srv_force_recovery == 0 + && sum_of_data_file_sizes + < tablespace_size_in_header) { + ib_logf(IB_LOG_LEVEL_ERROR, + "Cannot start InnoDB. The tail of" + " the system tablespace is" + " missing. Have you edited" + " innodb_data_file_path in my.cnf" + " in an inappropriate way, removing" + " data files from there?" + " You can set innodb_force_recovery=1" + " in my.cnf to force" + " a startup if you are trying to" + " recover a badly corrupt database."); + + return(DB_ERROR); + } + } + + /* This must precede recv_apply_hashed_log_recs(TRUE). */ ib_bh = trx_sys_init_at_db_start(); + + if (srv_force_recovery < SRV_FORCE_NO_LOG_REDO) { + /* Apply the hashed log records to the + respective file pages, for the last batch of + recv_group_scan_log_recs(). */ + + recv_apply_hashed_log_recs(TRUE); + DBUG_PRINT("ib_log", ("apply completed")); + } + n_recovered_trx = UT_LIST_GET_LEN(trx_sys->rw_trx_list); /* The purge system needs to create the purge view and @@ -2584,7 +2672,8 @@ files_checked: ULINT_MAX, LSN_MAX, NULL); ut_a(success); - RECOVERY_CRASH(1); + DBUG_EXECUTE_IF("innodb_log_abort_1", + return(DB_ERROR);); min_flushed_lsn = max_flushed_lsn = log_get_lsn(); @@ -2599,8 +2688,6 @@ files_checked: buf_flush_wait_batch_end(NULL, BUF_FLUSH_LIST); - RECOVERY_CRASH(2); - /* Flush the old log files. */ log_buffer_flush_to_disk(); /* If innodb_flush_method=O_DSYNC, @@ -2615,21 +2702,27 @@ files_checked: ut_d(recv_no_log_write = TRUE); ut_ad(!buf_pool_check_no_pending_io()); - RECOVERY_CRASH(3); + DBUG_EXECUTE_IF("innodb_log_abort_3", + return(DB_ERROR);); /* Stamp the LSN to the data files. */ fil_write_flushed_lsn_to_data_files( max_flushed_lsn, 0); - fil_flush_file_spaces(FIL_TABLESPACE); + DBUG_EXECUTE_IF("innodb_log_abort_4", err = DB_ERROR;); - RECOVERY_CRASH(4); + if (err != DB_SUCCESS) { + return(err); + } + + fil_flush_file_spaces(FIL_TABLESPACE); /* Close and free the redo log files, so that we can replace them. */ fil_close_log_files(true); - RECOVERY_CRASH(5); + DBUG_EXECUTE_IF("innodb_log_abort_5", + return(DB_ERROR);); /* Free the old log file space. */ log_group_close_all(); @@ -2653,8 +2746,11 @@ files_checked: fil_write_flushed_lsn_to_data_files(min_flushed_lsn, 0); fil_flush_file_spaces(FIL_TABLESPACE); - create_log_files_rename(logfilename, dirnamelen, - log_get_lsn(), logfile0); + err = create_log_files_rename(logfilename, dirnamelen, + log_get_lsn(), logfile0); + if (err != DB_SUCCESS) { + return(err); + } } srv_startup_is_before_trx_rollback_phase = FALSE; @@ -2668,20 +2764,8 @@ files_checked: trx_sys_file_format_tag_init(); } - if (!create_new_db && sum_of_new_sizes > 0) { - /* New data file(s) were added */ - mtr_start(&mtr); - - fsp_header_inc_size(0, sum_of_new_sizes, &mtr); - - mtr_commit(&mtr); - - /* Immediately write the log record about increased tablespace - size to disk, so that it is durable even if mysqld would crash - quickly */ - - log_buffer_flush_to_disk(); - } + ut_ad(err == DB_SUCCESS); + ut_a(sum_of_new_sizes != ULINT_UNDEFINED); #ifdef UNIV_LOG_ARCHIVE if (!srv_read_only_mode) { @@ -2760,10 +2844,13 @@ files_checked: thread_started[3 + SRV_MAX_N_IO_THREADS] = true; /* Create the thread which prints InnoDB monitor info */ - thread_handles[4 + SRV_MAX_N_IO_THREADS] = os_thread_create( - srv_monitor_thread, - NULL, thread_ids + 4 + SRV_MAX_N_IO_THREADS); - thread_started[4 + SRV_MAX_N_IO_THREADS] = true; + if (!thread_started[4 + SRV_MAX_N_IO_THREADS]) { + /* srv_monitor_thread not yet started */ + thread_handles[4 + SRV_MAX_N_IO_THREADS] = os_thread_create( + srv_monitor_thread, + NULL, thread_ids + 4 + SRV_MAX_N_IO_THREADS); + thread_started[4 + SRV_MAX_N_IO_THREADS] = true; + } } /* Create the SYS_FOREIGN and SYS_FOREIGN_COLS system tables */ @@ -2830,125 +2917,6 @@ files_checked: buf_flush_lru_manager_thread_handle = os_thread_create(buf_flush_lru_manager_thread, NULL, NULL); buf_flush_lru_manager_thread_started = true; -#ifdef UNIV_DEBUG - /* buf_debug_prints = TRUE; */ -#endif /* UNIV_DEBUG */ - sum_of_data_file_sizes = 0; - - for (i = 0; i < srv_n_data_files; i++) { - sum_of_data_file_sizes += srv_data_file_sizes[i]; - } - - tablespace_size_in_header = fsp_header_get_tablespace_size(); - - if (!srv_read_only_mode - && !srv_auto_extend_last_data_file - && sum_of_data_file_sizes != tablespace_size_in_header) { - - ut_print_timestamp(stderr); - fprintf(stderr, - " InnoDB: Error: tablespace size" - " stored in header is %lu pages, but\n", - (ulong) tablespace_size_in_header); - ut_print_timestamp(stderr); - fprintf(stderr, - "InnoDB: the sum of data file sizes is %lu pages\n", - (ulong) sum_of_data_file_sizes); - - if (srv_force_recovery == 0 - && sum_of_data_file_sizes < tablespace_size_in_header) { - /* This is a fatal error, the tail of a tablespace is - missing */ - - ut_print_timestamp(stderr); - fprintf(stderr, - " InnoDB: Cannot start InnoDB." - " The tail of the system tablespace is\n"); - ut_print_timestamp(stderr); - fprintf(stderr, - " InnoDB: missing. Have you edited" - " innodb_data_file_path in my.cnf in an\n"); - ut_print_timestamp(stderr); - fprintf(stderr, - " InnoDB: inappropriate way, removing" - " ibdata files from there?\n"); - ut_print_timestamp(stderr); - fprintf(stderr, - " InnoDB: You can set innodb_force_recovery=1" - " in my.cnf to force\n"); - ut_print_timestamp(stderr); - fprintf(stderr, - " InnoDB: a startup if you are trying" - " to recover a badly corrupt database.\n"); - - return(DB_ERROR); - } - } - - if (!srv_read_only_mode - && srv_auto_extend_last_data_file - && sum_of_data_file_sizes < tablespace_size_in_header) { - - ut_print_timestamp(stderr); - fprintf(stderr, - " InnoDB: Error: tablespace size stored in header" - " is %lu pages, but\n", - (ulong) tablespace_size_in_header); - ut_print_timestamp(stderr); - fprintf(stderr, - " InnoDB: the sum of data file sizes" - " is only %lu pages\n", - (ulong) sum_of_data_file_sizes); - - if (srv_force_recovery == 0) { - - ut_print_timestamp(stderr); - fprintf(stderr, - " InnoDB: Cannot start InnoDB. The tail of" - " the system tablespace is\n"); - ut_print_timestamp(stderr); - fprintf(stderr, - " InnoDB: missing. Have you edited" - " innodb_data_file_path in my.cnf in an\n"); - ut_print_timestamp(stderr); - fprintf(stderr, - " InnoDB: inappropriate way, removing" - " ibdata files from there?\n"); - ut_print_timestamp(stderr); - fprintf(stderr, - " InnoDB: You can set innodb_force_recovery=1" - " in my.cnf to force\n"); - ut_print_timestamp(stderr); - fprintf(stderr, - " InnoDB: a startup if you are trying to" - " recover a badly corrupt database.\n"); - - return(DB_ERROR); - } - } - - /* Check that os_fast_mutexes work as expected */ - os_fast_mutex_init(PFS_NOT_INSTRUMENTED, &srv_os_test_mutex); - - if (0 != os_fast_mutex_trylock(&srv_os_test_mutex)) { - ut_print_timestamp(stderr); - fprintf(stderr, - " InnoDB: Error: pthread_mutex_trylock returns" - " an unexpected value on\n"); - ut_print_timestamp(stderr); - fprintf(stderr, - " InnoDB: success! Cannot continue.\n"); - exit(1); - } - - os_fast_mutex_unlock(&srv_os_test_mutex); - - os_fast_mutex_lock(&srv_os_test_mutex); - - os_fast_mutex_unlock(&srv_os_test_mutex); - - os_fast_mutex_free(&srv_os_test_mutex); - if (!srv_file_per_table && srv_pass_corrupt_table) { fprintf(stderr, "InnoDB: Warning:" " The option innodb_file_per_table is disabled," @@ -3200,6 +3168,7 @@ innobase_shutdown_for_mysql(void) btr_search_disable(); ibuf_close(); + log_online_shutdown(); log_shutdown(); trx_sys_file_format_close(); trx_sys_close(); diff --git a/storage/xtradb/sync/sync0sync.cc b/storage/xtradb/sync/sync0sync.cc index e705ed4f587..ca937df4475 100644 --- a/storage/xtradb/sync/sync0sync.cc +++ b/storage/xtradb/sync/sync0sync.cc @@ -2,6 +2,7 @@ Copyright (c) 1995, 2016, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2008, Google Inc. +Copyright (c) 2017, MariaDB Corporation. All Rights Reserved. Portions of this file contain modifications contributed and copyrighted by Google, Inc. Those modifications are gratefully acknowledged and are described @@ -1349,7 +1350,7 @@ sync_thread_add_level( case SYNC_TRX_UNDO_PAGE: /* Purge is allowed to read in as many UNDO pages as it likes, there was a bogus rule here earlier that forced the caller to - acquire the purge_sys_t::mutex. The purge mutex did not really + acquire the trx_purge_t::mutex. The purge mutex did not really protect anything because it was only ever acquired by the single purge thread. The purge thread can read the UNDO pages without any covering mutex. */ diff --git a/storage/xtradb/trx/trx0purge.cc b/storage/xtradb/trx/trx0purge.cc index d9e40c5d6f5..6b7b7df4cd8 100644 --- a/storage/xtradb/trx/trx0purge.cc +++ b/storage/xtradb/trx/trx0purge.cc @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2017, MariaDB Corporation. 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 @@ -172,13 +173,9 @@ trx_purge_sys_close(void) sess_close(purge_sys->sess); - purge_sys->sess = NULL; - read_view_free(purge_sys->prebuilt_view); read_view_free(purge_sys->prebuilt_clone); - purge_sys->view = NULL; - rw_lock_free(&purge_sys->latch); mutex_free(&purge_sys->bh_mutex); @@ -187,9 +184,6 @@ trx_purge_sys_close(void) ib_bh_free(purge_sys->ib_bh); os_event_free(purge_sys->event); - - purge_sys->event = NULL; - mem_free(purge_sys); purge_sys = NULL; @@ -1306,20 +1300,16 @@ void trx_purge_stop(void) /*================*/ { - purge_state_t state; - ib_int64_t sig_count = os_event_reset(purge_sys->event); - ut_a(srv_n_purge_threads > 0); rw_lock_x_lock(&purge_sys->latch); - ut_a(purge_sys->state != PURGE_STATE_INIT); - ut_a(purge_sys->state != PURGE_STATE_EXIT); - ut_a(purge_sys->state != PURGE_STATE_DISABLED); + const ib_int64_t sig_count = os_event_reset(purge_sys->event); + const purge_state_t state = purge_sys->state; - ++purge_sys->n_stop; + ut_a(state == PURGE_STATE_RUN || state == PURGE_STATE_STOP); - state = purge_sys->state; + ++purge_sys->n_stop; if (state == PURGE_STATE_RUN) { ib_logf(IB_LOG_LEVEL_INFO, "Stopping purge"); diff --git a/storage/xtradb/trx/trx0sys.cc b/storage/xtradb/trx/trx0sys.cc index 6f524718052..5126711fb82 100644 --- a/storage/xtradb/trx/trx0sys.cc +++ b/storage/xtradb/trx/trx0sys.cc @@ -1333,7 +1333,9 @@ trx_sys_close(void) ut_a(UT_LIST_GET_LEN(trx_sys->ro_trx_list) == 0); /* Only prepared transactions may be left in the system. Free them. */ - ut_a(UT_LIST_GET_LEN(trx_sys->rw_trx_list) == trx_sys->n_prepared_trx); + ut_a(UT_LIST_GET_LEN(trx_sys->rw_trx_list) == trx_sys->n_prepared_trx + || srv_read_only_mode + || srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO); while ((trx = UT_LIST_GET_FIRST(trx_sys->rw_trx_list)) != NULL) { trx_free_prepared(trx); @@ -1379,6 +1381,33 @@ trx_sys_close(void) trx_sys = NULL; } +/** @brief Convert an undo log to TRX_UNDO_PREPARED state on shutdown. + +If any prepared ACTIVE transactions exist, and their rollback was +prevented by innodb_force_recovery, we convert these transactions to +XA PREPARE state in the main-memory data structures, so that shutdown +will proceed normally. These transactions will again recover as ACTIVE +on the next restart, and they will be rolled back unless +innodb_force_recovery prevents it again. + +@param[in] trx transaction +@param[in,out] undo undo log to convert to TRX_UNDO_PREPARED */ +static +void +trx_undo_fake_prepared( + const trx_t* trx, + trx_undo_t* undo) +{ + ut_ad(srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO); + ut_ad(trx_state_eq(trx, TRX_STATE_ACTIVE)); + ut_ad(trx->is_recovered); + + if (undo != NULL) { + ut_ad(undo->state == TRX_UNDO_ACTIVE); + undo->state = TRX_UNDO_PREPARED; + } +} + /********************************************************************* Check if there are any active (non-prepared) transactions. @return total number of active transactions or 0 if none */ @@ -1387,15 +1416,42 @@ ulint trx_sys_any_active_transactions(void) /*=================================*/ { - ulint total_trx = 0; - mutex_enter(&trx_sys->mutex); - total_trx = UT_LIST_GET_LEN(trx_sys->rw_trx_list) - + UT_LIST_GET_LEN(trx_sys->mysql_trx_list); + ulint total_trx = UT_LIST_GET_LEN(trx_sys->mysql_trx_list); + + if (total_trx == 0) { + total_trx = UT_LIST_GET_LEN(trx_sys->rw_trx_list); + ut_a(total_trx >= trx_sys->n_prepared_trx); + + if (total_trx > trx_sys->n_prepared_trx + && srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO) { + for (trx_t* trx = UT_LIST_GET_FIRST( + trx_sys->rw_trx_list); + trx != NULL; + trx = UT_LIST_GET_NEXT(trx_list, trx)) { + if (!trx_state_eq(trx, TRX_STATE_ACTIVE) + || !trx->is_recovered) { + continue; + } + /* This was a recovered transaction + whose rollback was disabled by + the innodb_force_recovery setting. + Pretend that it is in XA PREPARE + state so that shutdown will work. */ + trx_undo_fake_prepared( + trx, trx->insert_undo); + trx_undo_fake_prepared( + trx, trx->update_undo); + trx->state = TRX_STATE_PREPARED; + trx_sys->n_prepared_trx++; + trx_sys->n_prepared_recovered_trx++; + } + } - ut_a(total_trx >= trx_sys->n_prepared_trx); - total_trx -= trx_sys->n_prepared_trx; + ut_a(total_trx >= trx_sys->n_prepared_trx); + total_trx -= trx_sys->n_prepared_trx; + } mutex_exit(&trx_sys->mutex); diff --git a/storage/xtradb/trx/trx0trx.cc b/storage/xtradb/trx/trx0trx.cc index 97d1f264235..07e122e54ee 100644 --- a/storage/xtradb/trx/trx0trx.cc +++ b/storage/xtradb/trx/trx0trx.cc @@ -476,7 +476,11 @@ trx_free_prepared( /*==============*/ trx_t* trx) /*!< in, own: trx object */ { - ut_a(trx_state_eq(trx, TRX_STATE_PREPARED)); + ut_a(trx_state_eq(trx, TRX_STATE_PREPARED) + || (trx_state_eq(trx, TRX_STATE_ACTIVE) + && trx->is_recovered + && (srv_read_only_mode + || srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO))); ut_a(trx->magic_n == TRX_MAGIC_N); lock_trx_release_locks(trx); diff --git a/storage/xtradb/trx/trx0undo.cc b/storage/xtradb/trx/trx0undo.cc index cdd23726f2e..220589dd9ff 100644 --- a/storage/xtradb/trx/trx0undo.cc +++ b/storage/xtradb/trx/trx0undo.cc @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2014, 2017, MariaDB Corporation. 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 @@ -2011,13 +2012,37 @@ trx_undo_free_prepared( ut_ad(srv_shutdown_state == SRV_SHUTDOWN_EXIT_THREADS); if (trx->update_undo) { - ut_a(trx->update_undo->state == TRX_UNDO_PREPARED); + switch (trx->update_undo->state) { + case TRX_UNDO_PREPARED: + break; + case TRX_UNDO_ACTIVE: + /* lock_trx_release_locks() assigns + trx->is_recovered=false */ + ut_a(srv_read_only_mode + || srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO); + break; + default: + ut_error; + } + UT_LIST_REMOVE(undo_list, trx->rseg->update_undo_list, trx->update_undo); trx_undo_mem_free(trx->update_undo); } if (trx->insert_undo) { - ut_a(trx->insert_undo->state == TRX_UNDO_PREPARED); + switch (trx->insert_undo->state) { + case TRX_UNDO_PREPARED: + break; + case TRX_UNDO_ACTIVE: + /* lock_trx_release_locks() assigns + trx->is_recovered=false */ + ut_a(srv_read_only_mode + || srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO); + break; + default: + ut_error; + } + UT_LIST_REMOVE(undo_list, trx->rseg->insert_undo_list, trx->insert_undo); trx_undo_mem_free(trx->insert_undo); diff --git a/support-files/SELinux/CMakeLists.txt b/support-files/SELinux/CMakeLists.txt index e3cdb26ca8f..996d56d6c05 100644 --- a/support-files/SELinux/CMakeLists.txt +++ b/support-files/SELinux/CMakeLists.txt @@ -20,7 +20,7 @@ MARK_AS_ADVANCED(CHECKMODULE SEMODULE_PACKAGE) SET(params DESTINATION ${INSTALL_SUPPORTFILESDIR}/SELinux COMPONENT SupportFiles) IF(CHECKMODULE AND SEMODULE_PACKAGE) - FOREACH(pol centos6-mariadb) + FOREACH(pol mariadb) SET(src ${CMAKE_CURRENT_SOURCE_DIR}/${pol}.te) SET(mod ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/${pol}-pp.dir/${pol}.mod) SET(out ${CMAKE_CURRENT_BINARY_DIR}/${pol}.pp) @@ -32,4 +32,4 @@ IF(CHECKMODULE AND SEMODULE_PACKAGE) INSTALL(FILES ${out} ${params}) ENDFOREACH() ENDIF() -INSTALL(FILES centos6-mariadb.te rhel4-mysql.fc rhel4-mysql.te ${params}) +INSTALL(FILES mariadb.te rhel4-mysql.fc rhel4-mysql.te ${params}) diff --git a/support-files/SELinux/centos6-mariadb.te b/support-files/SELinux/mariadb.te index 1d3de52c700..1d3de52c700 100644 --- a/support-files/SELinux/centos6-mariadb.te +++ b/support-files/SELinux/mariadb.te diff --git a/support-files/build-tags b/support-files/build-tags index 4b76e01907a..03b243ee8cc 100755 --- a/support-files/build-tags +++ b/support-files/build-tags @@ -1,9 +1,16 @@ #! /bin/sh rm -f TAGS -filter='\.cpp$\|\.cc$\|\.c$\|\.h$\|sql_yacc\.yy$\|\.hpp$\|\.ic$\|errmsg-utf8\.txt$' -list="find . -type f" -git rev-parse >/dev/null 2>/dev/null && list="git ls-files" - -$list |grep $filter | xargs etags -o TAGS --append +if git rev-parse HEAD >/dev/null 2>&1 +then + cd `git rev-parse --show-toplevel` + echo client storage dbug libmysql sql-common \ + sql extra mysys mysys_ssl strings regex pcre vio include \ + tools unittest plugin libmysqld | \ + xargs -n1 git ls-files | grep -v '\.jar$' | \ + xargs etags -o TAGS --append +else + find . -type f ! -name "*.jar" | + xargs etags -o TAGS --append +fi diff --git a/support-files/mysql-log-rotate.sh b/support-files/mysql-log-rotate.sh index 0ee78b0f7ca..5d1b30b208e 100644 --- a/support-files/mysql-log-rotate.sh +++ b/support-files/mysql-log-rotate.sh @@ -30,7 +30,8 @@ if test -x @bindir@/mysqladmin && \ @bindir@/mysqladmin ping &>/dev/null then - @bindir@/mysqladmin flush-logs + @bindir@/mysqladmin --local flush-error-log \ + flush-engine-log flush-general-log flush-slow-log fi endscript } diff --git a/support-files/rpm/server-postin.sh b/support-files/rpm/server-postin.sh index 377a752824d..daf68f00c0c 100644 --- a/support-files/rpm/server-postin.sh +++ b/support-files/rpm/server-postin.sh @@ -31,6 +31,11 @@ if [ $1 = 1 ] ; then # The user may already exist, make sure it has the proper group nevertheless (BUG#12823) usermod --gid %{mysqld_group} %{mysqld_user} 2> /dev/null || true + # Temporary Workaround for MDEV-11386 - will be corrected in Advance Toolchain 10.0-3 and 8.0-8 + for ldconfig in /opt/at*/sbin/ldconfig; do + test -x $ldconfig && $ldconfig + done + # Change permissions so that the user that will run the MySQL daemon # owns all database files. chown -R %{mysqld_user}:%{mysqld_group} $datadir @@ -55,36 +60,9 @@ fi SETARGETDIR=/etc/selinux/targeted/src/policy SEDOMPROG=$SETARGETDIR/domains/program SECONPROG=$SETARGETDIR/file_contexts/program -if [ -f /etc/redhat-release ] ; then - if grep '\(Red Hat Enterprise Linux ..\|CentOS\) release 4' \ - /etc/redhat-release >/dev/null 2>&1; then - echo - echo - echo 'Notes regarding SELinux on this platform:' - echo '=========================================' - echo - echo 'The default policy might cause server startup to fail because it is ' - echo 'not allowed to access critical files. In this case, please update ' - echo 'your installation. ' - echo - echo 'The default policy might also cause inavailability of SSL related ' - echo 'features because the server is not allowed to access /dev/random ' - echo 'and /dev/urandom. If this is a problem, please do the following: ' - echo - echo ' 1) install selinux-policy-targeted-sources from your OS vendor' - echo ' 2) add the following two lines to '$SEDOMPROG/mysqld.te':' - echo ' allow mysqld_t random_device_t:chr_file read;' - echo ' allow mysqld_t urandom_device_t:chr_file read;' - echo ' 3) cd to '$SETARGETDIR' and issue the following command:' - echo ' make load' - echo - echo - fi - if grep 'CentOS release 6' /etc/redhat-release >/dev/null 2>&1; then - if [ -x /usr/sbin/semodule ] ; then - /usr/sbin/semodule -i /usr/share/mysql/SELinux/centos6-mariadb.pp - fi - fi + +if [ -x /usr/sbin/semodule ] ; then + /usr/sbin/semodule -i /usr/share/mysql/SELinux/mariadb.pp fi if [ -x sbin/restorecon ] ; then diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index 2acec60166e..4e4768559d4 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -1,5 +1,5 @@ -/* Copyright (c) 2002, 2012, Oracle and/or its affiliates. - Copyright (c) 2008, 2013, Monty Program Ab +/* Copyright (c) 2002, 2014, Oracle and/or its affiliates. + Copyright (c) 2008, 2017, MariaDB 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 @@ -19274,6 +19274,49 @@ static void test_mdev4326() } +/** + BUG#17512527: LIST HANDLING INCORRECT IN MYSQL_PRUNE_STMT_LIST() +*/ +static void test_bug17512527() +{ + MYSQL *conn; + MYSQL_STMT *stmt1, *stmt2; + unsigned long thread_id; + char query[MAX_TEST_QUERY_LENGTH]; + int rc; + + conn= client_connect(0, MYSQL_PROTOCOL_SOCKET, 1); + + stmt1 = mysql_stmt_init(conn); + check_stmt(stmt1); + rc= mysql_stmt_prepare(stmt1, STRING_WITH_LEN("SELECT 1")); + check_execute(stmt1, rc); + + stmt2 = mysql_stmt_init(conn); + check_stmt(stmt2); + + thread_id= mysql_thread_id(conn); + sprintf(query, "KILL %lu", thread_id); + if (thread_query(query)) + exit(1); + + rc= mysql_stmt_prepare(stmt2, STRING_WITH_LEN("SELECT 2")); + check_execute(stmt2, rc); + + rc= mysql_stmt_execute(stmt1); + check_execute_r(stmt1, rc); + + rc= mysql_stmt_execute(stmt2); + check_execute(stmt2, rc); + + mysql_close(conn); + + mysql_stmt_close(stmt2); + mysql_stmt_close(stmt1); +} + + + /* Check compressed protocol */ @@ -19645,6 +19688,9 @@ static struct my_tests_st my_tests[]= { { "test_bug13001491", test_bug13001491 }, { "test_mdev4326", test_mdev4326 }, { "test_ps_sp_out_params", test_ps_sp_out_params }, +#ifndef _WIN32 + { "test_bug17512527", test_bug17512527}, +#endif { "test_compressed_protocol", test_compressed_protocol }, { "test_big_packet", test_big_packet }, { 0, 0 } |