diff options
390 files changed, 14766 insertions, 7332 deletions
diff --git a/.bzrignore b/.bzrignore index 57fcbdd8f73..835e4b2c357 100644 --- a/.bzrignore +++ b/.bzrignore @@ -1,3 +1,4 @@ +*-t *.a *.bb *.bbg @@ -458,6 +459,7 @@ libmysqld/emb_qcache.cpp libmysqld/errmsg.c libmysqld/event.cc libmysqld/event_executor.cc +libmysqld/event_scheduler.cc libmysqld/event_timed.cc libmysqld/examples/client_test.c libmysqld/examples/client_test.cc @@ -788,6 +790,8 @@ mysys/main.cc mysys/my_new.cpp mysys/raid.cpp mysys/ste5KbMa +mysys/test_atomic +mysys/test_bitmap mysys/test_charset mysys/test_dir mysys/test_gethwaddr @@ -1770,4 +1774,3 @@ vio/viotest-sslconnect.cpp vio/viotest.cpp zlib/*.ds? zlib/*.vcproj -libmysqld/event_scheduler.cc diff --git a/BUILD/compile-pentium64 b/BUILD/compile-pentium64 new file mode 100755 index 00000000000..e45528d3b45 --- /dev/null +++ b/BUILD/compile-pentium64 @@ -0,0 +1,13 @@ +#! /bin/sh + +path=`dirname $0` +. "$path/SETUP.sh" $@ --with-debug=full + +extra_flags="$pentium64_cflags $fast_cflags" +c_warnings="$c_warnings" +cxx_warnings="$cxx_warnings" +extra_configs="$pentium_configs $static_link" + +extra_configs="$extra_configs " +CC="$CC --pipe" +. "$path/FINISH.sh" diff --git a/BUILD/compile-pentium64-debug b/BUILD/compile-pentium64-debug index 761c590b680..145d5c44f82 100755 --- a/BUILD/compile-pentium64-debug +++ b/BUILD/compile-pentium64-debug @@ -7,5 +7,5 @@ extra_flags="$pentium64_cflags $debug_cflags" extra_configs="$pentium_configs $debug_configs $static_link" extra_configs="$extra_configs " - +CC="$CC --pipe" . "$path/FINISH.sh" diff --git a/BUILD/compile-pentium64-debug-max b/BUILD/compile-pentium64-debug-max index 594fe2e3009..7b71237cb2f 100755 --- a/BUILD/compile-pentium64-debug-max +++ b/BUILD/compile-pentium64-debug-max @@ -7,5 +7,5 @@ extra_flags="$pentium64_cflags $debug_cflags" extra_configs="$pentium_configs $debug_configs $max_configs" extra_configs="$extra_configs " - +CC="$CC --pipe" . "$path/FINISH.sh" diff --git a/BitKeeper/triggers/pre-outgoing.crash-protect.pl b/BitKeeper/triggers/pre-outgoing.crash-protect.pl new file mode 100755 index 00000000000..bbaa092e335 --- /dev/null +++ b/BitKeeper/triggers/pre-outgoing.crash-protect.pl @@ -0,0 +1,85 @@ +#! /usr/bin/perl + +use strict; +use warnings; + +my $event= $ENV{BK_EVENT}; +unless($event eq 'outgoing pull' || $event eq 'outgoing push' || + $event eq 'resolve') { + exit 0; +} + +print "Checking for bad changesets from old crashed 5.1 tree...\n"; + +my @bad_csets= + ( 'monty@mysql.com|ChangeSet|20060418090255|16983', + 'monty@mysql.com|ChangeSet|20060418090458|02628', + 'monty@mysql.com|ChangeSet|20060419084236|49576', + 'monty@mysql.com|ChangeSet|20060503164655|51444', + 'monty@mysql.com|ChangeSet|20060503225814|60133', + 'monty@mysql.com|ChangeSet|20060504033006|54878', + 'monty@mysql.com|ChangeSet|20060504130520|48660', + 'monty@mysql.com|ChangeSet|20060504164102|03511', + 'monty@mysql.com|ChangeSet|20060504193112|04109', + 'monty@mysql.com|ChangeSet|20060505015314|02799', + 'monty@mysql.com|ChangeSet|20060505084007|16704', + 'monty@mysql.com|ChangeSet|20060505104008|16695', + 'monty@mysql.com|ChangeSet|20060505171041|13924', + 'monty@mysql.com|ChangeSet|20060508121933|13866', + 'monty@mysql.com|ChangeSet|20060508160902|15029', + 'monty@mysql.com|ChangeSet|20060509145448|38636', + 'monty@mysql.com|ChangeSet|20060509224111|40037', + 'monty@mysql.com|ChangeSet|20060510090758|40678', + 'monty@mysql.com|ChangeSet|20060515164104|46760', + 'monty@mysql.com|ChangeSet|20060530114549|35852', + 'monty@mysql.com|ChangeSet|20060605032828|23579', + 'monty@mysql.com|ChangeSet|20060605033011|10641', + 'monty@mysql.com|ChangeSet|20060605060652|09843', + 'msvensson@neptunus.(none)|ChangeSet|20060605094744|10838', + 'msvensson@neptunus.(none)|ChangeSet|20060605105746|11800', + 'msvensson@neptunus.(none)|ChangeSet|20060605122345|12772', + 'jmiller@mysql.com|ChangeSet|20060531210831|36442', + 'jmiller@mysql.com|ChangeSet|20060602151941|36118', + 'jmiller@mysql.com|ChangeSet|20060602152136|27762', + 'jmiller@mysql.com|ChangeSet|20060605121748|12864', + 'jmiller@mysql.com|ChangeSet|20060605160304|14798', + 'jimw@mysql.com|ChangeSet|20060605210201|14667', + 'igor@rurik.mysql.com|ChangeSet|20060605220727|15265', + 'igor@rurik.mysql.com|ChangeSet|20060605221206|15134', + 'stewart@mysql.com|ChangeSet|20060525073521|11169', + 'stewart@mysql.com|ChangeSet|20060605154220|12975', + 'stewart@mysql.com|ChangeSet|20060606040001|15337', + ); + +# Read the list of changesets. +my $csetlist = $ENV{BK_CSETLIST}; +if(!defined($csetlist) || !open(FH, '<', $csetlist)) { + die "Failed to open list of incoming changesets '$csetlist': $!.\n"; +} +my @csets = <FH>; +close FH; + +# Reject any attempt to push/pull a bad changeset. +for my $cs (@csets) { + # Do this the raw way, don't want to be bitten by different EOL conventions + # on server and client (Unix/Windows/Mac). + $cs =~ s/\x0d?\x0a?$//s; + if(grep($_ eq $cs, @bad_csets)) { + print <<END; +BAD CHANGESET DETECTED! $event REJECTED! + +The changeset with key '$cs' was detected in the attempted push or pull. +This changeset is from the corrupt part of the crashed mysql-5.1-new tree. +Pushing or pulling this changeset would result in corruption of the new tree, +and therefore the operation has been rejected. + +Contact Kristian Nielsen (knielsen\@mysql.com, IRC knielsen) if you have any +questions regarding this. +END + exit 1; + } +} + +print "No bad changesets found, proceeding.\n"; + +exit 0; diff --git a/BitKeeper/triggers/pre-resolve.crash-protect.pl b/BitKeeper/triggers/pre-resolve.crash-protect.pl new file mode 100755 index 00000000000..bbaa092e335 --- /dev/null +++ b/BitKeeper/triggers/pre-resolve.crash-protect.pl @@ -0,0 +1,85 @@ +#! /usr/bin/perl + +use strict; +use warnings; + +my $event= $ENV{BK_EVENT}; +unless($event eq 'outgoing pull' || $event eq 'outgoing push' || + $event eq 'resolve') { + exit 0; +} + +print "Checking for bad changesets from old crashed 5.1 tree...\n"; + +my @bad_csets= + ( 'monty@mysql.com|ChangeSet|20060418090255|16983', + 'monty@mysql.com|ChangeSet|20060418090458|02628', + 'monty@mysql.com|ChangeSet|20060419084236|49576', + 'monty@mysql.com|ChangeSet|20060503164655|51444', + 'monty@mysql.com|ChangeSet|20060503225814|60133', + 'monty@mysql.com|ChangeSet|20060504033006|54878', + 'monty@mysql.com|ChangeSet|20060504130520|48660', + 'monty@mysql.com|ChangeSet|20060504164102|03511', + 'monty@mysql.com|ChangeSet|20060504193112|04109', + 'monty@mysql.com|ChangeSet|20060505015314|02799', + 'monty@mysql.com|ChangeSet|20060505084007|16704', + 'monty@mysql.com|ChangeSet|20060505104008|16695', + 'monty@mysql.com|ChangeSet|20060505171041|13924', + 'monty@mysql.com|ChangeSet|20060508121933|13866', + 'monty@mysql.com|ChangeSet|20060508160902|15029', + 'monty@mysql.com|ChangeSet|20060509145448|38636', + 'monty@mysql.com|ChangeSet|20060509224111|40037', + 'monty@mysql.com|ChangeSet|20060510090758|40678', + 'monty@mysql.com|ChangeSet|20060515164104|46760', + 'monty@mysql.com|ChangeSet|20060530114549|35852', + 'monty@mysql.com|ChangeSet|20060605032828|23579', + 'monty@mysql.com|ChangeSet|20060605033011|10641', + 'monty@mysql.com|ChangeSet|20060605060652|09843', + 'msvensson@neptunus.(none)|ChangeSet|20060605094744|10838', + 'msvensson@neptunus.(none)|ChangeSet|20060605105746|11800', + 'msvensson@neptunus.(none)|ChangeSet|20060605122345|12772', + 'jmiller@mysql.com|ChangeSet|20060531210831|36442', + 'jmiller@mysql.com|ChangeSet|20060602151941|36118', + 'jmiller@mysql.com|ChangeSet|20060602152136|27762', + 'jmiller@mysql.com|ChangeSet|20060605121748|12864', + 'jmiller@mysql.com|ChangeSet|20060605160304|14798', + 'jimw@mysql.com|ChangeSet|20060605210201|14667', + 'igor@rurik.mysql.com|ChangeSet|20060605220727|15265', + 'igor@rurik.mysql.com|ChangeSet|20060605221206|15134', + 'stewart@mysql.com|ChangeSet|20060525073521|11169', + 'stewart@mysql.com|ChangeSet|20060605154220|12975', + 'stewart@mysql.com|ChangeSet|20060606040001|15337', + ); + +# Read the list of changesets. +my $csetlist = $ENV{BK_CSETLIST}; +if(!defined($csetlist) || !open(FH, '<', $csetlist)) { + die "Failed to open list of incoming changesets '$csetlist': $!.\n"; +} +my @csets = <FH>; +close FH; + +# Reject any attempt to push/pull a bad changeset. +for my $cs (@csets) { + # Do this the raw way, don't want to be bitten by different EOL conventions + # on server and client (Unix/Windows/Mac). + $cs =~ s/\x0d?\x0a?$//s; + if(grep($_ eq $cs, @bad_csets)) { + print <<END; +BAD CHANGESET DETECTED! $event REJECTED! + +The changeset with key '$cs' was detected in the attempted push or pull. +This changeset is from the corrupt part of the crashed mysql-5.1-new tree. +Pushing or pulling this changeset would result in corruption of the new tree, +and therefore the operation has been rejected. + +Contact Kristian Nielsen (knielsen\@mysql.com, IRC knielsen) if you have any +questions regarding this. +END + exit 1; + } +} + +print "No bad changesets found, proceeding.\n"; + +exit 0; diff --git a/client/Makefile.am b/client/Makefile.am index aa78f825c87..ff97243815a 100644 --- a/client/Makefile.am +++ b/client/Makefile.am @@ -32,7 +32,7 @@ endif INCLUDES = -I$(top_builddir)/include \ -I$(top_srcdir)/include \ -I$(top_srcdir)/regex \ - $(openssl_includes) $(yassl_includes) + $(openssl_includes) LIBS = @CLIENT_LIBS@ LDADD= @CLIENT_EXTRA_LDFLAGS@ $(CLIENT_THREAD_LIBS) \ $(top_builddir)/libmysql/libmysqlclient.la @@ -83,13 +83,13 @@ link_sources: for f in $(sql_src) ; do \ rm -f $$f; \ @LN_CP_F@ $(top_srcdir)/sql/$$f $$f; \ - done; \ + done; \ for f in $(strings_src) ; do \ rm -f $(srcdir)/$$f; \ @LN_CP_F@ $(top_srcdir)/strings/$$f $$f; \ - done; \ - rm -f $(srcdir)/my_user.c; \ - @LN_CP_F@ $(top_srcdir)/sql-common/my_user.c my_user.c + done; \ + rm -f $(srcdir)/my_user.c; \ + @LN_CP_F@ $(top_srcdir)/sql-common/my_user.c my_user.c; # Don't update the files from bitkeeper diff --git a/client/client_priv.h.rej b/client/client_priv.h.rej deleted file mode 100644 index ac3818bb1e1..00000000000 --- a/client/client_priv.h.rej +++ /dev/null @@ -1,15 +0,0 @@ -*************** -*** 50,55 **** - OPT_NDBCLUSTER, OPT_NDB_CONNECTSTRING, - #endif - OPT_TRIGGERS, - OPT_IGNORE_TABLE,OPT_INSERT_IGNORE,OPT_SHOW_WARNINGS,OPT_DROP_DATABASE, -! OPT_TZ_UTC, OPT_AUTO_CLOSE - }; ---- 50,55 ---- - OPT_NDBCLUSTER, OPT_NDB_CONNECTSTRING, - #endif - OPT_TRIGGERS, - OPT_IGNORE_TABLE,OPT_INSERT_IGNORE,OPT_SHOW_WARNINGS,OPT_DROP_DATABASE, -! OPT_TZ_UTC, OPT_AUTO_CLOSE, OPT_SSL_VERIFY_SERVER_CERT - }; diff --git a/client/mysqlbinlog.cc b/client/mysqlbinlog.cc index 4b2527c25ef..81ad74466eb 100644 --- a/client/mysqlbinlog.cc +++ b/client/mysqlbinlog.cc @@ -72,6 +72,7 @@ static int port= 0; static const char* sock= 0; static const char* user = 0; static char* pass = 0; +static char *charset= 0; static ulonglong start_position, stop_position; #define start_position_mot ((my_off_t)start_position) @@ -733,6 +734,9 @@ static struct my_option my_long_options[] = "Extract only binlog entries created by the server having the given id.", (gptr*) &server_id, (gptr*) &server_id, 0, GET_ULONG, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"set-charset", OPT_SET_CHARSET, + "Add 'SET NAMES character_set' to the output.", (gptr*) &charset, + (gptr*) &charset, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"short-form", 's', "Just show the queries, no extra info.", (gptr*) &short_form, (gptr*) &short_form, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, @@ -1457,6 +1461,13 @@ int main(int argc, char** argv) "/*!50003 SET @OLD_COMPLETION_TYPE=@@COMPLETION_TYPE," "COMPLETION_TYPE=0*/;\n"); + if (charset) + fprintf(result_file, + "\n/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;" + "\n/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;" + "\n/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;" + "\n/*!40101 SET NAMES %s */;\n", charset); + for (save_stop_position= stop_position, stop_position= ~(my_off_t)0 ; (--argc >= 0) && !stop_passed ; ) { @@ -1481,6 +1492,12 @@ int main(int argc, char** argv) if (disable_log_bin) fprintf(result_file, "/*!32316 SET SQL_LOG_BIN=@OLD_SQL_LOG_BIN*/;\n"); + if (charset) + fprintf(result_file, + "/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;\n" + "/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;\n" + "/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;\n"); + if (tmpdir.list) free_tmpdir(&tmpdir); if (result_file != stdout) diff --git a/client/mysqldump.c b/client/mysqldump.c index 58cd2342bd3..e257e3734a1 100644 --- a/client/mysqldump.c +++ b/client/mysqldump.c @@ -3063,14 +3063,13 @@ static my_bool dump_all_views_in_db(char *database) different case (e.g. T1 vs t1) RETURN - int - 0 if a tablename was retrieved. 1 if not + pointer to the table name + 0 if error */ -static int get_actual_table_name(const char *old_table_name, - char *new_table_name, - int buf_size) +static char *get_actual_table_name(const char *old_table_name, MEM_ROOT *root) { - int retval; + char *name= 0; MYSQL_RES *table_res; MYSQL_ROW row; char query[50 + 2*NAME_LEN]; @@ -3087,66 +3086,55 @@ static int get_actual_table_name(const char *old_table_name, safe_exit(EX_MYSQLERR); } - retval = 1; - if ((table_res= mysql_store_result(sock))) { my_ulonglong num_rows= mysql_num_rows(table_res); if (num_rows > 0) { + ulong *lengths; /* Return first row TODO: Return all matching rows */ row= mysql_fetch_row(table_res); - strmake(new_table_name, row[0], buf_size-1); - retval= 0; + lengths= mysql_fetch_lengths(table_res); + name= strmake_root(root, row[0], lengths[0]); } mysql_free_result(table_res); } - DBUG_RETURN(retval); + DBUG_PRINT("exit", ("new_table_name: %s", name)); + DBUG_RETURN(name); } static int dump_selected_tables(char *db, char **table_names, int tables) { - uint i; char table_buff[NAME_LEN*+3]; - char new_table_name[NAME_LEN]; DYNAMIC_STRING lock_tables_query; - HASH dump_tables; - char *table_name; + MEM_ROOT root; + char **dump_tables, **pos, **end; DBUG_ENTER("dump_selected_tables"); if (init_dumping(db)) DBUG_RETURN(1); - /* Init hash table for storing the actual name of tables to dump */ - if (hash_init(&dump_tables, charset_info, 16, 0, 0, - (hash_get_key) get_table_key, (hash_free_key) free_table_ent, - 0)) + init_alloc_root(&root, 8192, 0); + if (!(dump_tables= pos= (char**) alloc_root(&root, tables * sizeof(char *)))) exit(EX_EOM); init_dynamic_string(&lock_tables_query, "LOCK TABLES ", 256, 1024); for (; tables > 0 ; tables-- , table_names++) { /* the table name passed on commandline may be wrong case */ - if (!get_actual_table_name(*table_names, - new_table_name, sizeof(new_table_name))) + if ((*pos= get_actual_table_name(*table_names, &root))) { /* Add found table name to lock_tables_query */ if (lock_tables) { - dynstr_append(&lock_tables_query, - quote_name(new_table_name, table_buff, 1)); + dynstr_append(&lock_tables_query, quote_name(*pos, table_buff, 1)); dynstr_append(&lock_tables_query, " READ /*!32311 LOCAL */,"); } - - /* Add found table name to dump_tables list */ - if (my_hash_insert(&dump_tables, - (byte*)my_strdup(new_table_name, MYF(0)))) - exit(EX_EOM); - + pos++; } else { @@ -3156,6 +3144,7 @@ static int dump_selected_tables(char *db, char **table_names, int tables) /* We shall countinue here, if --force was given */ } } + end= pos; if (lock_tables) { @@ -3175,24 +3164,20 @@ static int dump_selected_tables(char *db, char **table_names, int tables) print_xml_tag1(md_result_file, "", "database name=", db, "\n"); /* Dump each selected table */ - for (i= 0; i < dump_tables.records; i++) + for (pos= dump_tables; pos < end; pos++) { - table_name= hash_element(&dump_tables, i); - DBUG_PRINT("info",("Dumping table %s", table_name)); - dump_table(table_name,db); + DBUG_PRINT("info",("Dumping table %s", *pos)); + dump_table(*pos, db); if (opt_dump_triggers && mysql_get_server_version(sock) >= 50009) - dump_triggers_for_table(table_name, db); + dump_triggers_for_table(*pos, db); } /* Dump each selected view */ if (was_views) { - for(i=0; i < dump_tables.records; i++) - { - table_name= hash_element(&dump_tables, i); - get_view_structure(table_name, db); - } + for (pos= dump_tables; pos < end; pos++) + get_view_structure(*pos, db); } if (opt_events && !opt_xml && mysql_get_server_version(sock) >= 50106) @@ -3207,7 +3192,7 @@ static int dump_selected_tables(char *db, char **table_names, int tables) DBUG_PRINT("info", ("Dumping routines for database %s", db)); dump_routines_for_db(db); } - hash_free(&dump_tables); + free_root(&root, MYF(0)); my_free(order_by, MYF(MY_ALLOW_ZERO_PTR)); order_by= 0; if (opt_xml) diff --git a/client/mysqltest.c b/client/mysqltest.c index 774cbac6e3f..11e2cc0048e 100644 --- a/client/mysqltest.c +++ b/client/mysqltest.c @@ -2757,6 +2757,9 @@ int do_connect(struct st_query *q) #ifdef HAVE_OPENSSL if (opt_use_ssl || con_ssl) { + /* Turn on ssl_verify_server_cert only if host is "localhost" */ + opt_ssl_verify_server_cert= !strcmp(con_host, "localhost"); + mysql_ssl_set(&next_con->mysql, opt_ssl_key, opt_ssl_cert, opt_ssl_ca, opt_ssl_capath, opt_ssl_cipher); mysql_options(&next_con->mysql, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, diff --git a/config/ac-macros/ha_ndbcluster.m4 b/config/ac-macros/ha_ndbcluster.m4 index 505d000c196..ee31fa9fca2 100644 --- a/config/ac-macros/ha_ndbcluster.m4 +++ b/config/ac-macros/ha_ndbcluster.m4 @@ -197,7 +197,7 @@ AC_DEFUN([MYSQL_SETUP_NDBCLUSTER], [ MAKE_BINARY_DISTRIBUTION_OPTIONS="$MAKE_BINARY_DISTRIBUTION_OPTIONS --with-ndbcluster" - # CXXFLAGS="$CXXFLAGS \$(NDB_CXXFLAGS)" + CXXFLAGS="$CXXFLAGS \$(NDB_CXXFLAGS)" if test "$have_ndb_debug" = "default" then have_ndb_debug=$with_debug diff --git a/config/ac-macros/misc.m4 b/config/ac-macros/misc.m4 index a2f70071e2d..3ed64b5625b 100644 --- a/config/ac-macros/misc.m4 +++ b/config/ac-macros/misc.m4 @@ -155,84 +155,6 @@ fi ]) -#---START: Used in for client configure -AC_DEFUN([MYSQL_CHECK_ULONG], -[AC_MSG_CHECKING(for type ulong) -AC_CACHE_VAL(ac_cv_ulong, -[AC_TRY_RUN([#include <stdio.h> -#include <sys/types.h> -main() -{ - ulong foo; - foo++; - exit(0); -}], ac_cv_ulong=yes, ac_cv_ulong=no, ac_cv_ulong=no)]) -AC_MSG_RESULT($ac_cv_ulong) -if test "$ac_cv_ulong" = "yes" -then - AC_DEFINE([HAVE_ULONG], [1], [system headers define ulong]) -fi -]) - -AC_DEFUN([MYSQL_CHECK_UCHAR], -[AC_MSG_CHECKING(for type uchar) -AC_CACHE_VAL(ac_cv_uchar, -[AC_TRY_RUN([#include <stdio.h> -#include <sys/types.h> -main() -{ - uchar foo; - foo++; - exit(0); -}], ac_cv_uchar=yes, ac_cv_uchar=no, ac_cv_uchar=no)]) -AC_MSG_RESULT($ac_cv_uchar) -if test "$ac_cv_uchar" = "yes" -then - AC_DEFINE([HAVE_UCHAR], [1], [system headers define uchar]) -fi -]) - -AC_DEFUN([MYSQL_CHECK_UINT], -[AC_MSG_CHECKING(for type uint) -AC_CACHE_VAL(ac_cv_uint, -[AC_TRY_RUN([#include <stdio.h> -#include <sys/types.h> -main() -{ - uint foo; - foo++; - exit(0); -}], ac_cv_uint=yes, ac_cv_uint=no, ac_cv_uint=no)]) -AC_MSG_RESULT($ac_cv_uint) -if test "$ac_cv_uint" = "yes" -then - AC_DEFINE([HAVE_UINT], [1], [system headers define uint]) -fi -]) - - -AC_DEFUN([MYSQL_CHECK_IN_ADDR_T], -[AC_MSG_CHECKING(for type in_addr_t) -AC_CACHE_VAL(ac_cv_in_addr_t, -[AC_TRY_RUN([#include <stdio.h> -#include <sys/types.h> -#include <sys/socket.h> -#include <netinet/in.h> -#include <arpa/inet.h> - -int main(int argc, char **argv) -{ - in_addr_t foo; - exit(0); -}], ac_cv_in_addr_t=yes, ac_cv_in_addr_t=no, ac_cv_in_addr_t=no)]) -AC_MSG_RESULT($ac_cv_in_addr_t) -if test "$ac_cv_in_addr_t" = "yes" -then - AC_DEFINE([HAVE_IN_ADDR_T], [1], [system headers define in_addr_t]) -fi -]) - - AC_DEFUN([MYSQL_PTHREAD_YIELD], [AC_CACHE_CHECK([if pthread_yield takes zero arguments], ac_cv_pthread_yield_zero_arg, [AC_TRY_LINK([#define _GNU_SOURCE @@ -272,25 +194,6 @@ fi #---END: -AC_DEFUN([MYSQL_CHECK_FP_EXCEPT], -[AC_MSG_CHECKING(for type fp_except) -AC_CACHE_VAL(ac_cv_fp_except, -[AC_TRY_RUN([#include <stdio.h> -#include <sys/types.h> -#include <ieeefp.h> -main() -{ - fp_except foo; - foo++; - exit(0); -}], ac_cv_fp_except=yes, ac_cv_fp_except=no, ac_cv_fp_except=no)]) -AC_MSG_RESULT($ac_cv_fp_except) -if test "$ac_cv_fp_except" = "yes" -then - AC_DEFINE([HAVE_FP_EXCEPT], [1], [fp_except from ieeefp.h]) -fi -]) - # From fileutils-3.14/aclocal.m4 # @defmac AC_PROG_CC_STDC diff --git a/configure.in b/configure.in index 0f0649fd3bc..be6f3c8eaec 100644 --- a/configure.in +++ b/configure.in @@ -778,48 +778,6 @@ struct request_info *req; AC_SUBST(WRAPLIBS) if test "$TARGET_LINUX" = "true"; then - AC_MSG_CHECKING([for atomic operations]) - - AC_LANG_SAVE - AC_LANG_CPLUSPLUS - - atom_ops= - AC_TRY_RUN([ -#include <asm/atomic.h> -int main() -{ - atomic_t v; - - atomic_set(&v, 23); - atomic_add(5, &v); - return atomic_read(&v) == 28 ? 0 : -1; -} - ], - [AC_DEFINE([HAVE_ATOMIC_ADD], [1], - [atomic_add() from <asm/atomic.h> (Linux only)]) - atom_ops="${atom_ops}atomic_add "], - ) - AC_TRY_RUN([ -#include <asm/atomic.h> -int main() -{ - atomic_t v; - - atomic_set(&v, 23); - atomic_sub(5, &v); - return atomic_read(&v) == 18 ? 0 : -1; -} - ], - [AC_DEFINE([HAVE_ATOMIC_SUB], [1], - [atomic_sub() from <asm/atomic.h> (Linux only)]) - atom_ops="${atom_ops}atomic_sub "], - ) - - if test -z "$atom_ops"; then atom_ops="no"; fi - AC_MSG_RESULT($atom_ops) - - AC_LANG_RESTORE - AC_ARG_WITH(pstack, [ --with-pstack Use the pstack backtrace library], [ USE_PSTACK=$withval ], @@ -871,38 +829,20 @@ fi # Later in this script LIBS will be augmented with a threads library. NON_THREADED_LIBS="$LIBS" -AC_MSG_CHECKING([for int8]) -case $SYSTEM_TYPE in - *netware) - AC_MSG_RESULT([no]) - ;; - *) -AC_TRY_RUN([ -#ifdef HAVE_STDLIB_H -#include <stdlib.h> -#endif - -#ifdef HAVE_STDDEF_H -#include <stddef.h> -#endif - -#ifdef HAVE_SYS_TYPES_H +AC_CHECK_TYPES([int8, uint8, int16, uint16, int32, uint32, int64, uint64, + uchar, uint, ulong],[],[], [ #include <sys/types.h> -#endif - -int main() -{ - int8 i; - return 0; -} -], -[AC_DEFINE([HAVE_INT_8_16_32], [1], - [whether int8, int16 and int32 types exist]) -AC_MSG_RESULT([yes])], -[AC_MSG_RESULT([no])] -) - ;; -esac +]) +AC_CHECK_TYPES([in_addr_t], [], [], [ +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +]) +AC_CHECK_TYPES([fp_except], [], [], [ +#include <sys/types.h> +#include <ieeefp.h> +]) # # Some system specific hacks @@ -1631,6 +1571,20 @@ then fi fi +AC_ARG_WITH([atomic-ops], + AC_HELP_STRING([--with-atomic-ops=rwlocks|smp|up], + [Implement atomic operations using pthread rwlocks or atomic CPU + instructions for multi-processor (default) or uniprocessor + configuration]), , [with_atomic_ops=smp]) +case "$with_atomic_ops" in + "up") AC_DEFINE([MY_ATOMIC_MODE_DUMMY], [1], + [Assume single-CPU mode, no concurrency]) ;; + "rwlocks") AC_DEFINE([MY_ATOMIC_MODE_RWLOCKS], [1], + [Use pthread rwlocks for atomic ops]) ;; + "smp") ;; + *) AC_MSG_ERROR(["$with_atomic_ops" is not a valid value for --with-atomic-ops]) ;; +esac + # Force static compilation to avoid linking problems/get more speed AC_ARG_WITH(mysqld-ldflags, [ --with-mysqld-ldflags Extra linking arguments for mysqld], @@ -1762,16 +1716,6 @@ MYSQL_FUNC_ALLOCA MYSQL_TIMESPEC_TS # Do we have the tzname variable MYSQL_TZNAME -# Do the system files define ulong -MYSQL_CHECK_ULONG -# Do the system files define uchar -MYSQL_CHECK_UCHAR -# Do the system files define uint -MYSQL_CHECK_UINT -# Check for fp_except in ieeefp.h -MYSQL_CHECK_FP_EXCEPT -# Check for IN_ADDR_T -MYSQL_CHECK_IN_ADDR_T # Do the c++ compiler have a bool type MYSQL_CXX_BOOL # Check some common bugs with gcc 2.8.# on sparc diff --git a/extra/yassl/CMakeLists.txt b/extra/yassl/CMakeLists.txt index cafa3011e94..e5429876072 100644 --- a/extra/yassl/CMakeLists.txt +++ b/extra/yassl/CMakeLists.txt @@ -1,4 +1,4 @@ -ADD_DEFINITIONS("-DWIN32 -D_LIB") +ADD_DEFINITIONS("-DWIN32 -D_LIB -DYASSL_PREFIX") INCLUDE_DIRECTORIES(include taocrypt/include mySTL) ADD_LIBRARY(yassl src/buffer.cpp src/cert_wrapper.cpp src/crypto_wrapper.cpp src/handshake.cpp src/lock.cpp diff --git a/extra/yassl/include/openssl/crypto.h b/extra/yassl/include/openssl/crypto.h index 4a0c1db0df1..288990e1318 100644 --- a/extra/yassl/include/openssl/crypto.h +++ b/extra/yassl/include/openssl/crypto.h @@ -3,6 +3,10 @@ #ifndef ysSSL_crypto_h__ #define yaSSL_crypto_h__ +#ifdef YASSL_PREFIX +#include "prefix_crypto.h" +#endif + const char* SSLeay_version(int type); #define SSLEAY_VERSION 0x0900L diff --git a/extra/yassl/include/openssl/generate_prefix_files.pl b/extra/yassl/include/openssl/generate_prefix_files.pl new file mode 100755 index 00000000000..b921ee11e9a --- /dev/null +++ b/extra/yassl/include/openssl/generate_prefix_files.pl @@ -0,0 +1,45 @@ +#!/usr/bin/perl +# +# This script generates defines for all functions +# in yassl/include/openssl/ so they are renamed to +# ya<old_function_name>. Hopefully that is unique enough. +# +# The script is to be run manually when we import +# a new version of yaSSL +# + + + +# Find all functions in "input" and add macros +# to prefix/rename them into "output +sub generate_prefix($$) +{ + my $input= shift; + my $output= shift; + open(IN, $input) + or die("Can't open input file $input: $!"); + open(OUT, ">", $output) + or mtr_error("Can't open output file $output: $!"); + + while (<IN>) + { + chomp; + + if ( /typedef/ ) + { + next; + } + + if ( /^\s*[a-zA-Z0-9*_ ]+\s+([_a-zA-Z0-9]+)\s*\(/ ) + { + print OUT "#define $1 ya$1\n"; + } + } + + close OUT; + close IN; +} + +generate_prefix("ssl.h", "prefix_ssl.h"); +generate_prefix("crypto.h", "prefix_crypto.h"); + diff --git a/extra/yassl/include/openssl/prefix_crypto.h b/extra/yassl/include/openssl/prefix_crypto.h new file mode 100644 index 00000000000..3fa5f32c627 --- /dev/null +++ b/extra/yassl/include/openssl/prefix_crypto.h @@ -0,0 +1 @@ +#define SSLeay_version yaSSLeay_version diff --git a/extra/yassl/include/openssl/prefix_ssl.h b/extra/yassl/include/openssl/prefix_ssl.h new file mode 100644 index 00000000000..7f815156f47 --- /dev/null +++ b/extra/yassl/include/openssl/prefix_ssl.h @@ -0,0 +1,152 @@ +#define Copyright yaCopyright +#define yaSSL_CleanUp yayaSSL_CleanUp +#define DH_new yaDH_new +#define DH_free yaDH_free +#define RSA_free yaRSA_free +#define RSA_generate_key yaRSA_generate_key +#define X509_free yaX509_free +#define X509_STORE_CTX_get_current_cert yaX509_STORE_CTX_get_current_cert +#define X509_STORE_CTX_get_error yaX509_STORE_CTX_get_error +#define X509_STORE_CTX_get_error_depth yaX509_STORE_CTX_get_error_depth +#define X509_NAME_oneline yaX509_NAME_oneline +#define X509_get_issuer_name yaX509_get_issuer_name +#define X509_get_subject_name yaX509_get_subject_name +#define X509_verify_cert_error_string yaX509_verify_cert_error_string +#define X509_LOOKUP_add_dir yaX509_LOOKUP_add_dir +#define X509_LOOKUP_load_file yaX509_LOOKUP_load_file +#define X509_LOOKUP_hash_dir yaX509_LOOKUP_hash_dir +#define X509_LOOKUP_file yaX509_LOOKUP_file +#define X509_STORE_add_lookup yaX509_STORE_add_lookup +#define X509_STORE_new yaX509_STORE_new +#define X509_STORE_get_by_subject yaX509_STORE_get_by_subject +#define ERR_get_error_line_data yaERR_get_error_line_data +#define ERR_print_errors_fp yaERR_print_errors_fp +#define ERR_error_string yaERR_error_string +#define ERR_remove_state yaERR_remove_state +#define ERR_get_error yaERR_get_error +#define ERR_peek_error yaERR_peek_error +#define ERR_GET_REASON yaERR_GET_REASON +#define SSL_CTX_new yaSSL_CTX_new +#define SSL_new yaSSL_new +#define SSL_set_fd yaSSL_set_fd +#define SSL_connect yaSSL_connect +#define SSL_write yaSSL_write +#define SSL_read yaSSL_read +#define SSL_accept yaSSL_accept +#define SSL_CTX_free yaSSL_CTX_free +#define SSL_free yaSSL_free +#define SSL_clear yaSSL_clear +#define SSL_shutdown yaSSL_shutdown +#define SSL_set_connect_state yaSSL_set_connect_state +#define SSL_set_accept_state yaSSL_set_accept_state +#define SSL_do_handshake yaSSL_do_handshake +#define SSL_get_cipher yaSSL_get_cipher +#define SSL_get_cipher_name yaSSL_get_cipher_name +#define SSL_get_shared_ciphers yaSSL_get_shared_ciphers +#define SSL_get_cipher_list yaSSL_get_cipher_list +#define SSL_get_version yaSSL_get_version +#define SSLeay_version yaSSLeay_version +#define SSL_get_error yaSSL_get_error +#define SSL_load_error_strings yaSSL_load_error_strings +#define SSL_set_session yaSSL_set_session +#define SSL_get_session yaSSL_get_session +#define SSL_SESSION_set_timeout yaSSL_SESSION_set_timeout +#define SSL_get_peer_certificate yaSSL_get_peer_certificate +#define SSL_get_verify_result yaSSL_get_verify_result +#define SSL_CTX_set_verify yaSSL_CTX_set_verify +#define SSL_CTX_load_verify_locations yaSSL_CTX_load_verify_locations +#define SSL_CTX_set_default_verify_paths yaSSL_CTX_set_default_verify_paths +#define SSL_CTX_check_private_key yaSSL_CTX_check_private_key +#define SSL_CTX_set_session_id_context yaSSL_CTX_set_session_id_context +#define SSL_CTX_set_tmp_rsa_callback yaSSL_CTX_set_tmp_rsa_callback +#define SSL_CTX_set_options yaSSL_CTX_set_options +#define SSL_CTX_set_session_cache_mode yaSSL_CTX_set_session_cache_mode +#define SSL_CTX_set_timeout yaSSL_CTX_set_timeout +#define SSL_CTX_use_certificate_chain_file yaSSL_CTX_use_certificate_chain_file +#define SSL_CTX_set_default_passwd_cb yaSSL_CTX_set_default_passwd_cb +#define SSL_CTX_use_RSAPrivateKey_file yaSSL_CTX_use_RSAPrivateKey_file +#define SSL_CTX_set_info_callback yaSSL_CTX_set_info_callback +#define SSL_CTX_sess_accept yaSSL_CTX_sess_accept +#define SSL_CTX_sess_connect yaSSL_CTX_sess_connect +#define SSL_CTX_sess_accept_good yaSSL_CTX_sess_accept_good +#define SSL_CTX_sess_connect_good yaSSL_CTX_sess_connect_good +#define SSL_CTX_sess_accept_renegotiate yaSSL_CTX_sess_accept_renegotiate +#define SSL_CTX_sess_connect_renegotiate yaSSL_CTX_sess_connect_renegotiate +#define SSL_CTX_sess_hits yaSSL_CTX_sess_hits +#define SSL_CTX_sess_cb_hits yaSSL_CTX_sess_cb_hits +#define SSL_CTX_sess_cache_full yaSSL_CTX_sess_cache_full +#define SSL_CTX_sess_misses yaSSL_CTX_sess_misses +#define SSL_CTX_sess_timeouts yaSSL_CTX_sess_timeouts +#define SSL_CTX_sess_number yaSSL_CTX_sess_number +#define SSL_CTX_sess_get_cache_size yaSSL_CTX_sess_get_cache_size +#define SSL_CTX_get_verify_mode yaSSL_CTX_get_verify_mode +#define SSL_get_verify_mode yaSSL_get_verify_mode +#define SSL_CTX_get_verify_depth yaSSL_CTX_get_verify_depth +#define SSL_get_verify_depth yaSSL_get_verify_depth +#define SSL_get_default_timeout yaSSL_get_default_timeout +#define SSL_CTX_get_session_cache_mode yaSSL_CTX_get_session_cache_mode +#define SSL_session_reused yaSSL_session_reused +#define SSL_set_rfd yaSSL_set_rfd +#define SSL_set_wfd yaSSL_set_wfd +#define SSL_set_shutdown yaSSL_set_shutdown +#define SSL_want_read yaSSL_want_read +#define SSL_want_write yaSSL_want_write +#define SSL_pending yaSSL_pending +#define SSL_CTX_use_certificate_file yaSSL_CTX_use_certificate_file +#define SSL_CTX_use_PrivateKey_file yaSSL_CTX_use_PrivateKey_file +#define SSL_CTX_set_cipher_list yaSSL_CTX_set_cipher_list +#define SSL_CTX_sess_set_cache_size yaSSL_CTX_sess_set_cache_size +#define SSL_CTX_set_tmp_dh yaSSL_CTX_set_tmp_dh +#define OpenSSL_add_all_algorithms yaOpenSSL_add_all_algorithms +#define SSL_library_init yaSSL_library_init +#define SSLeay_add_ssl_algorithms yaSSLeay_add_ssl_algorithms +#define SSL_get_current_cipher yaSSL_get_current_cipher +#define SSL_CIPHER_description yaSSL_CIPHER_description +#define SSL_alert_type_string_long yaSSL_alert_type_string_long +#define SSL_alert_desc_string_long yaSSL_alert_desc_string_long +#define SSL_state_string_long yaSSL_state_string_long +#define EVP_md5 yaEVP_md5 +#define EVP_des_ede3_cbc yaEVP_des_ede3_cbc +#define EVP_BytesToKey yaEVP_BytesToKey +#define DES_set_key_unchecked yaDES_set_key_unchecked +#define DES_ede3_cbc_encrypt yaDES_ede3_cbc_encrypt +#define RAND_screen yaRAND_screen +#define RAND_file_name yaRAND_file_name +#define RAND_write_file yaRAND_write_file +#define RAND_load_file yaRAND_load_file +#define RAND_status yaRAND_status +#define DES_set_key yaDES_set_key +#define DES_set_odd_parity yaDES_set_odd_parity +#define DES_ecb_encrypt yaDES_ecb_encrypt +#define SSL_CTX_set_default_passwd_cb_userdata yaSSL_CTX_set_default_passwd_cb_userdata +#define SSL_SESSION_free yaSSL_SESSION_free +#define SSL_get_certificate yaSSL_get_certificate +#define SSL_get_privatekey yaSSL_get_privatekey +#define X509_get_pubkey yaX509_get_pubkey +#define EVP_PKEY_copy_parameters yaEVP_PKEY_copy_parameters +#define EVP_PKEY_free yaEVP_PKEY_free +#define ERR_error_string_n yaERR_error_string_n +#define ERR_free_strings yaERR_free_strings +#define EVP_cleanup yaEVP_cleanup +#define X509_get_ext_d2i yaX509_get_ext_d2i +#define GENERAL_NAMES_free yaGENERAL_NAMES_free +#define sk_GENERAL_NAME_num yask_GENERAL_NAME_num +#define sk_GENERAL_NAME_value yask_GENERAL_NAME_value +#define ASN1_STRING_data yaASN1_STRING_data +#define ASN1_STRING_length yaASN1_STRING_length +#define ASN1_STRING_type yaASN1_STRING_type +#define X509_NAME_get_index_by_NID yaX509_NAME_get_index_by_NID +#define X509_NAME_ENTRY_get_data yaX509_NAME_ENTRY_get_data +#define X509_NAME_get_entry yaX509_NAME_get_entry +#define ASN1_STRING_to_UTF8 yaASN1_STRING_to_UTF8 +#define SSLv23_client_method yaSSLv23_client_method +#define SSLv2_client_method yaSSLv2_client_method +#define SSL_get1_session yaSSL_get1_session +#define X509_get_notBefore yaX509_get_notBefore +#define X509_get_notAfter yaX509_get_notAfter +#define MD4_Init yaMD4_Init +#define MD4_Update yaMD4_Update +#define MD4_Final yaMD4_Final +#define MD5_Init yaMD5_Init +#define MD5_Update yaMD5_Update +#define MD5_Final yaMD5_Final diff --git a/extra/yassl/include/openssl/ssl.h b/extra/yassl/include/openssl/ssl.h index 23e48d2011f..af801029561 100644 --- a/extra/yassl/include/openssl/ssl.h +++ b/extra/yassl/include/openssl/ssl.h @@ -28,6 +28,10 @@ #ifndef yaSSL_openssl_h__ #define yaSSL_openssl_h__ +#ifdef YASSL_PREFIX +#include "prefix_ssl.h" +#endif + #include <stdio.h> /* ERR_print fp */ #include "opensslv.h" /* for version number */ #include "rsa.h" @@ -373,11 +377,9 @@ char* SSL_state_string_long(SSL*); /* EVP stuff, des and md5, different file? */ -typedef struct Digest Digest; -typedef Digest EVP_MD; +typedef char EVP_MD; -typedef struct BulkCipher BulkCipher; -typedef BulkCipher EVP_CIPHER; +typedef char EVP_CIPHER; typedef struct EVP_PKEY EVP_PKEY; diff --git a/extra/yassl/include/yassl_error.hpp b/extra/yassl/include/yassl_error.hpp index 9c12b06e34a..2f35fecb59b 100644 --- a/extra/yassl/include/yassl_error.hpp +++ b/extra/yassl/include/yassl_error.hpp @@ -26,7 +26,6 @@ #ifndef yaSSL_ERROR_HPP #define yaSSL_ERROR_HPP -#include "stdexcept.hpp" namespace yaSSL { @@ -63,7 +62,7 @@ enum { MAX_ERROR_SZ = 80 }; void SetErrorString(YasslError, char*); - +/* remove for now, if go back to exceptions use this wrapper // Base class for all yaSSL exceptions class Error : public mySTL::runtime_error { YasslError error_; @@ -75,6 +74,7 @@ public: YasslError get_number() const; Library get_lib() const; }; +*/ } // naemspace diff --git a/extra/yassl/include/yassl_int.hpp b/extra/yassl/include/yassl_int.hpp index 97ae468d2f9..633b75d479f 100644 --- a/extra/yassl/include/yassl_int.hpp +++ b/extra/yassl/include/yassl_int.hpp @@ -127,25 +127,6 @@ private: }; -// hold add crypt references provided to callers -class CryptProvider { - mySTL::list<Digest*> digestList_; - mySTL::list<BulkCipher*> cipherList_; - CryptProvider() {} // only GetCryptProvider creates -public: - ~CryptProvider(); - - Digest* NewMd5(); - BulkCipher* NewDesEde(); - - friend CryptProvider& GetCryptProvider(); -private: - CryptProvider(const CryptProvider&); // hide copy - CryptProvider& operator=(const CryptProvider&); // and assign -}; - -CryptProvider& GetCryptProvider(); - #undef X509_NAME // wincrypt.h clash // openSSL X509 names diff --git a/extra/yassl/mySTL/stdexcept.hpp b/extra/yassl/mySTL/stdexcept.hpp index b50dd35edae..33ea43bf0e0 100644 --- a/extra/yassl/mySTL/stdexcept.hpp +++ b/extra/yassl/mySTL/stdexcept.hpp @@ -46,10 +46,8 @@ public: // for compiler generated call, never used static void operator delete(void*) { assert(0); } private: -#if defined(__hpux) // don't allow dynamic creation of exceptions static void* operator new(size_t); -#endif }; diff --git a/extra/yassl/src/Makefile.am b/extra/yassl/src/Makefile.am index a852ca4019b..2b3e1aea5f5 100644 --- a/extra/yassl/src/Makefile.am +++ b/extra/yassl/src/Makefile.am @@ -5,4 +5,4 @@ libyassl_la_SOURCES = buffer.cpp cert_wrapper.cpp crypto_wrapper.cpp \ handshake.cpp lock.cpp log.cpp socket_wrapper.cpp ssl.cpp \ template_instnt.cpp timer.cpp yassl_imp.cpp yassl_error.cpp yassl_int.cpp EXTRA_DIST = $(wildcard ../include/*.hpp) $(wildcard ../include/openssl/*.h) -AM_CXXFLAGS = -DYASSL_PURE_C +AM_CXXFLAGS = -DYASSL_PURE_C -DYASSL_PREFIX diff --git a/extra/yassl/src/ssl.cpp b/extra/yassl/src/ssl.cpp index 747305730df..07f5e9859b2 100644 --- a/extra/yassl/src/ssl.cpp +++ b/extra/yassl/src/ssl.cpp @@ -811,25 +811,34 @@ const char* X509_verify_cert_error_string(long /* error */) const EVP_MD* EVP_md5(void) { - return GetCryptProvider().NewMd5(); + static const char* type = "MD5"; + return type; } const EVP_CIPHER* EVP_des_ede3_cbc(void) { - return GetCryptProvider().NewDesEde(); + static const char* type = "DES_EDE3_CBC"; + return type; } int EVP_BytesToKey(const EVP_CIPHER* type, const EVP_MD* md, const byte* salt, const byte* data, int sz, int count, byte* key, byte* iv) { - EVP_MD* myMD = const_cast<EVP_MD*>(md); - uint digestSz = myMD->get_digestSize(); + // only support MD5 for now + if (strncmp(md, "MD5", 3)) return 0; + + // only support DES_EDE3_CBC for now + if (strncmp(type, "DES_EDE3_CBC", 12)) return 0; + + yaSSL::MD5 myMD; + uint digestSz = myMD.get_digestSize(); byte digest[SHA_LEN]; // max size - int keyLen = type->get_keySize(); - int ivLen = type->get_ivSize(); + yaSSL::DES_EDE cipher; + int keyLen = cipher.get_keySize(); + int ivLen = cipher.get_ivSize(); int keyLeft = keyLen; int ivLeft = ivLen; int keyOutput = 0; @@ -838,17 +847,17 @@ int EVP_BytesToKey(const EVP_CIPHER* type, const EVP_MD* md, const byte* salt, int digestLeft = digestSz; // D_(i - 1) if (keyOutput) // first time D_0 is empty - myMD->update(digest, digestSz); + myMD.update(digest, digestSz); // data - myMD->update(data, sz); + myMD.update(data, sz); // salt if (salt) - myMD->update(salt, EVP_SALT_SZ); - myMD->get_digest(digest); + myMD.update(salt, EVP_SALT_SZ); + myMD.get_digest(digest); // count for (int j = 1; j < count; j++) { - myMD->update(digest, digestSz); - myMD->get_digest(digest); + myMD.update(digest, digestSz); + myMD.get_digest(digest); } if (keyLeft) { diff --git a/extra/yassl/src/template_instnt.cpp b/extra/yassl/src/template_instnt.cpp index 134deb00c75..ce8972c72fe 100644 --- a/extra/yassl/src/template_instnt.cpp +++ b/extra/yassl/src/template_instnt.cpp @@ -86,7 +86,6 @@ template void ysDelete<X509>(X509*); template void ysDelete<Message>(Message*); template void ysDelete<sslFactory>(sslFactory*); template void ysDelete<Sessions>(Sessions*); -template void ysDelete<CryptProvider>(CryptProvider*); template void ysArrayDelete<unsigned char>(unsigned char*); template void ysArrayDelete<char>(char*); } diff --git a/extra/yassl/src/yassl_error.cpp b/extra/yassl/src/yassl_error.cpp index 1973c54d781..72b8e459241 100644 --- a/extra/yassl/src/yassl_error.cpp +++ b/extra/yassl/src/yassl_error.cpp @@ -27,10 +27,12 @@ #include "yassl_error.hpp" #include "error.hpp" // TaoCrypt error numbers #include "openssl/ssl.h" // SSL_ERROR_WANT_READ +#include <string.h> // strncpy namespace yaSSL { +/* may bring back in future Error::Error(const char* s, YasslError e, Library l) : mySTL::runtime_error(s), error_(e), lib_(l) { @@ -48,6 +50,7 @@ Library Error::get_lib() const return lib_; } +*/ void SetErrorString(YasslError error, char* buffer) diff --git a/extra/yassl/src/yassl_int.cpp b/extra/yassl/src/yassl_int.cpp index a715d32f282..1ff46903bfd 100644 --- a/extra/yassl/src/yassl_int.cpp +++ b/extra/yassl/src/yassl_int.cpp @@ -1382,38 +1382,6 @@ sslFactory& GetSSL_Factory() } -static CryptProvider* cryptProviderInstance = 0; - -CryptProvider& GetCryptProvider() -{ - if (!cryptProviderInstance) - cryptProviderInstance = NEW_YS CryptProvider; - return *cryptProviderInstance; -} - - -CryptProvider::~CryptProvider() -{ - mySTL::for_each(digestList_.begin(), digestList_.end(), del_ptr_zero()); - mySTL::for_each(cipherList_.begin(), cipherList_.end(), del_ptr_zero()); -} - - -Digest* CryptProvider::NewMd5() -{ - Digest* ptr = NEW_YS MD5(); - digestList_.push_back(ptr); - return ptr; -} - - -BulkCipher* CryptProvider::NewDesEde() -{ - BulkCipher* ptr = NEW_YS DES_EDE(); - cipherList_.push_back(ptr); - return ptr; -} - typedef Mutex::Lock Lock; @@ -2106,9 +2074,12 @@ ASN1_STRING* StringHolder::GetString() extern "C" void yaSSL_CleanUp() { TaoCrypt::CleanUp(); - ysDelete(yaSSL::cryptProviderInstance); - ysDelete(yaSSL::sslFactoryInstance); - ysDelete(yaSSL::sessionsInstance); + yaSSL::ysDelete(yaSSL::sslFactoryInstance); + yaSSL::ysDelete(yaSSL::sessionsInstance); + + // In case user calls more than once, prevent seg fault + yaSSL::sslFactoryInstance = 0; + yaSSL::sessionsInstance = 0; } diff --git a/extra/yassl/taocrypt/src/integer.cpp b/extra/yassl/taocrypt/src/integer.cpp index 885ddfbf630..a296e122985 100644 --- a/extra/yassl/taocrypt/src/integer.cpp +++ b/extra/yassl/taocrypt/src/integer.cpp @@ -2735,8 +2735,11 @@ void CleanUp() { tcDelete(one); tcDelete(zero); -} + // In case user calls more than once, prevent seg fault + one = 0; + zero = 0; +} Integer::Integer(RandomNumberGenerator& rng, const Integer& min, const Integer& max) diff --git a/extra/yassl/taocrypt/src/misc.cpp b/extra/yassl/taocrypt/src/misc.cpp index 4ef163a7f5d..2869df71c8a 100644 --- a/extra/yassl/taocrypt/src/misc.cpp +++ b/extra/yassl/taocrypt/src/misc.cpp @@ -81,6 +81,19 @@ extern "C" { } +#if defined(__ICC) || defined(__INTEL_COMPILER) + +extern "C" { + + int __cxa_pure_virtual() { + assert("Pure virtual method called." == "Aborted"); + return 0; + } + +} // extern "C" + +#endif + #endif // YASSL_PURE_C diff --git a/extra/yassl/taocrypt/taocrypt.vcproj b/extra/yassl/taocrypt/taocrypt.vcproj index 603fafd4090..7eef7b82db7 100755 --- a/extra/yassl/taocrypt/taocrypt.vcproj +++ b/extra/yassl/taocrypt/taocrypt.vcproj @@ -397,6 +397,27 @@ </FileConfiguration> </File> <File + RelativePath="src\md4.cpp"> + <FileConfiguration + Name="Debug|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="" + PreprocessorDefinitions="" + BasicRuntimeChecks="3" + BrowseInformation="1"/> + </FileConfiguration> + <FileConfiguration + Name="Release|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + AdditionalIncludeDirectories="" + PreprocessorDefinitions=""/> + </FileConfiguration> + </File> + <File RelativePath="src\md5.cpp"> <FileConfiguration Name="Debug|Win32"> @@ -572,6 +593,9 @@ RelativePath="include\md2.hpp"> </File> <File + RelativePath="include\md4.hpp"> + </File> + <File RelativePath="include\md5.hpp"> </File> <File diff --git a/extra/yassl/testsuite/Makefile.am b/extra/yassl/testsuite/Makefile.am index d91822f609e..2ae46a1b409 100644 --- a/extra/yassl/testsuite/Makefile.am +++ b/extra/yassl/testsuite/Makefile.am @@ -5,7 +5,7 @@ testsuite_SOURCES = testsuite.cpp ../taocrypt/test/test.cpp \ ../examples/echoclient/echoclient.cpp \ ../examples/echoserver/echoserver.cpp testsuite_LDFLAGS = -L../src/ -L../taocrypt/src -testsuite_CXXFLAGS = -DYASSL_PURE_C -DNO_MAIN_DRIVER +testsuite_CXXFLAGS = -DYASSL_PURE_C -DYASSL_PREFIX -DNO_MAIN_DRIVER testsuite_LDADD = -lyassl -ltaocrypt testsuite_DEPENDENCIES = ../src/libyassl.la ../taocrypt/src/libtaocrypt.la EXTRA_DIST = testsuite.dsp test.hpp input quit make.bat diff --git a/extra/yassl/yassl.vcproj b/extra/yassl/yassl.vcproj index e2915e3f575..a7688ac4583 100755 --- a/extra/yassl/yassl.vcproj +++ b/extra/yassl/yassl.vcproj @@ -22,7 +22,7 @@ Name="VCCLCompilerTool" Optimization="0" AdditionalIncludeDirectories="include,taocrypt\include,mySTL" - PreprocessorDefinitions="WIN32;_DEBUG;_LIB" + PreprocessorDefinitions="WIN32;_DEBUG;_LIB;YASSL_PREFIX" ExceptionHandling="FALSE" BasicRuntimeChecks="3" RuntimeLibrary="1" @@ -76,7 +76,7 @@ Optimization="2" InlineFunctionExpansion="1" AdditionalIncludeDirectories="include,taocrypt\include,mySTL" - PreprocessorDefinitions="WIN32;NDEBUG;_LIB" + PreprocessorDefinitions="WIN32;NDEBUG;_LIB;YASSL_PREFIX" StringPooling="TRUE" ExceptionHandling="FALSE" RuntimeLibrary="0" diff --git a/include/Makefile.am b/include/Makefile.am index e1ddadb933a..27b359bb6a3 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -31,7 +31,8 @@ noinst_HEADERS = config-win.h config-netware.h \ my_aes.h my_tree.h my_trie.h hash.h thr_alarm.h \ thr_lock.h t_ctype.h violite.h md5.h base64.h \ mysql_version.h.in my_handler.h my_time.h decimal.h \ - my_vle.h my_user.h + my_vle.h my_user.h my_atomic.h atomic/nolock.h \ + atomic/rwlock.h atomic/x86-gcc.h atomic/x86-msvc.h # mysql_version.h are generated CLEANFILES = mysql_version.h my_config.h readline openssl diff --git a/include/atomic/nolock.h b/include/atomic/nolock.h new file mode 100644 index 00000000000..cf21a94c7de --- /dev/null +++ b/include/atomic/nolock.h @@ -0,0 +1,169 @@ +/* Copyright (C) 2006 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#if defined(__i386__) || defined(_M_IX86) +#ifdef MY_ATOMIC_MODE_DUMMY +# define LOCK "" +#else +# define LOCK "lock " +#endif +#ifdef __GNUC__ +#include "x86-gcc.h" +#elif defined(_MSC_VER) +#include "x86-msvc.h" +#endif +#endif + +#ifdef make_atomic_add_body8 + +#ifdef HAVE_INLINE + +#define make_atomic_add(S) \ +static inline uint ## S _my_atomic_add ## S( \ + my_atomic_ ## S ## _t *a, uint ## S v) \ +{ \ + make_atomic_add_body ## S; \ + return v; \ +} + +#define make_atomic_swap(S) \ +static inline uint ## S _my_atomic_swap ## S( \ + my_atomic_ ## S ## _t *a, uint ## S v) \ +{ \ + make_atomic_swap_body ## S; \ + return v; \ +} + +#define make_atomic_cas(S) \ +static inline uint _my_atomic_cas ## S(my_atomic_ ## S ## _t *a,\ + uint ## S *cmp, uint ## S set) \ +{ \ + uint8 ret; \ + make_atomic_cas_body ## S; \ + return ret; \ +} + +#define make_atomic_load(S) \ +static inline uint ## S _my_atomic_load ## S( \ + my_atomic_ ## S ## _t *a) \ +{ \ + uint ## S ret; \ + make_atomic_load_body ## S; \ + return ret; \ +} + +#define make_atomic_store(S) \ +static inline void _my_atomic_store ## S( \ + my_atomic_ ## S ## _t *a, uint ## S v) \ +{ \ + make_atomic_store_body ## S; \ +} + +#else /* no inline functions */ + +#define make_atomic_add(S) \ +extern uint ## S _my_atomic_add ## S( \ + my_atomic_ ## S ## _t *a, uint ## S v); + +#define make_atomic_swap(S) \ +extern uint ## S _my_atomic_swap ## S( \ + my_atomic_ ## S ## _t *a, uint ## S v); + +#define make_atomic_cas(S) \ +extern uint _my_atomic_cas ## S(my_atomic_ ## S ## _t *a, \ + uint ## S *cmp, uint ## S set); + +#define make_atomic_load(S) \ +extern uint ## S _my_atomic_load ## S( \ + my_atomic_ ## S ## _t *a); + +#define make_atomic_store(S) \ +extern void _my_atomic_store ## S( \ + my_atomic_ ## S ## _t *a, uint ## S v); + +#endif + +make_atomic_add( 8) +make_atomic_add(16) +make_atomic_add(32) + +make_atomic_cas( 8) +make_atomic_cas(16) +make_atomic_cas(32) + +make_atomic_load( 8) +make_atomic_load(16) +make_atomic_load(32) + +make_atomic_store( 8) +make_atomic_store(16) +make_atomic_store(32) + +make_atomic_swap( 8) +make_atomic_swap(16) +make_atomic_swap(32) + +#undef make_atomic_add_body8 +#undef make_atomic_cas_body8 +#undef make_atomic_load_body8 +#undef make_atomic_store_body8 +#undef make_atomic_swap_body8 +#undef make_atomic_add_body16 +#undef make_atomic_cas_body16 +#undef make_atomic_load_body16 +#undef make_atomic_store_body16 +#undef make_atomic_swap_body16 +#undef make_atomic_add_body32 +#undef make_atomic_cas_body32 +#undef make_atomic_load_body32 +#undef make_atomic_store_body32 +#undef make_atomic_swap_body32 +#undef make_atomic_add +#undef make_atomic_cas +#undef make_atomic_load +#undef make_atomic_store +#undef make_atomic_swap + +#define my_atomic_add8(a,v,L) _my_atomic_add8(a,v) +#define my_atomic_add16(a,v,L) _my_atomic_add16(a,v) +#define my_atomic_add32(a,v,L) _my_atomic_add32(a,v) + +#define my_atomic_cas8(a,c,v,L) _my_atomic_cas8(a,c,v) +#define my_atomic_cas16(a,c,v,L) _my_atomic_cas16(a,c,v) +#define my_atomic_cas32(a,c,v,L) _my_atomic_cas32(a,c,v) + +#define my_atomic_load8(a,L) _my_atomic_load8(a) +#define my_atomic_load16(a,L) _my_atomic_load16(a) +#define my_atomic_load32(a,L) _my_atomic_load32(a) + +#define my_atomic_store8(a,v,L) _my_atomic_store8(a,v) +#define my_atomic_store16(a,v,L) _my_atomic_store16(a,v) +#define my_atomic_store32(a,v,L) _my_atomic_store32(a,v) + +#define my_atomic_swap8(a,v,L) _my_atomic_swap8(a,v) +#define my_atomic_swap16(a,v,L) _my_atomic_swap16(a,v) +#define my_atomic_swap32(a,v,L) _my_atomic_swap32(a,v) + +#define my_atomic_rwlock_t typedef int +#define my_atomic_rwlock_destroy(name) +#define my_atomic_rwlock_init(name) +#define my_atomic_rwlock_rdlock(name) +#define my_atomic_rwlock_wrlock(name) +#define my_atomic_rwlock_rdunlock(name) +#define my_atomic_rwlock_wrunlock(name) + +#endif + diff --git a/include/atomic/rwlock.h b/include/atomic/rwlock.h new file mode 100644 index 00000000000..ca5be29ab9b --- /dev/null +++ b/include/atomic/rwlock.h @@ -0,0 +1,161 @@ +/* Copyright (C) 2006 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +typedef struct {pthread_rwlock_t rw;} my_atomic_rwlock_t; + +#ifdef MY_ATOMIC_EXTRA_DEBUG +#define CHECK_RW if (rw) if (a->rw) assert(rw == a->rw); else a->rw=rw; +#else +#define CHECK_RW +#endif + +#ifdef MY_ATOMIC_MODE_DUMMY +/* + the following can never be enabled by ./configure, one need to put #define in + a source to trigger the following warning. The resulting code will be broken, + it only makes sense to do it to see now test_atomic detects broken + implementations (another way is to run a UP build on an SMP box). +*/ +#warning MY_ATOMIC_MODE_DUMMY and MY_ATOMIC_MODE_RWLOCKS are incompatible +#define my_atomic_rwlock_destroy(name) +#define my_atomic_rwlock_init(name) +#define my_atomic_rwlock_rdlock(name) +#define my_atomic_rwlock_wrlock(name) +#define my_atomic_rwlock_rdunlock(name) +#define my_atomic_rwlock_wrunlock(name) +#else +#define my_atomic_rwlock_destroy(name) pthread_rwlock_destroy(& (name)->rw) +#define my_atomic_rwlock_init(name) pthread_rwlock_init(& (name)->rw, 0) +#define my_atomic_rwlock_rdlock(name) pthread_rwlock_rdlock(& (name)->rw) +#define my_atomic_rwlock_wrlock(name) pthread_rwlock_wrlock(& (name)->rw) +#define my_atomic_rwlock_rdunlock(name) pthread_rwlock_unlock(& (name)->rw) +#define my_atomic_rwlock_wrunlock(name) pthread_rwlock_unlock(& (name)->rw) +#endif + +#ifdef HAVE_INLINE + +#define make_atomic_add(S) \ +static inline uint ## S my_atomic_add ## S( \ + my_atomic_ ## S ## _t *a, uint ## S v, my_atomic_rwlock_t *rw) \ +{ \ + uint ## S ret; \ + CHECK_RW; \ + if (rw) my_atomic_rwlock_wrlock(rw); \ + ret= a->val; \ + a->val+= v; \ + if (rw) my_atomic_rwlock_wrunlock(rw); \ + return ret; \ +} + +#define make_atomic_swap(S) \ +static inline uint ## S my_atomic_swap ## S( \ + my_atomic_ ## S ## _t *a, uint ## S v, my_atomic_rwlock_t *rw) \ +{ \ + uint ## S ret; \ + CHECK_RW; \ + if (rw) my_atomic_rwlock_wrlock(rw); \ + ret= a->val; \ + a->val= v; \ + if (rw) my_atomic_rwlock_wrunlock(rw); \ + return ret; \ +} + +#define make_atomic_cas(S) \ +static inline uint my_atomic_cas ## S(my_atomic_ ## S ## _t *a, \ + uint ## S *cmp, uint ## S set, my_atomic_rwlock_t *rw) \ +{ \ + uint ret; \ + CHECK_RW; \ + if (rw) my_atomic_rwlock_wrlock(rw); \ + if (ret= (a->val == *cmp)) a->val= set; else *cmp=a->val; \ + if (rw) my_atomic_rwlock_wrunlock(rw); \ + return ret; \ +} + +#define make_atomic_load(S) \ +static inline uint ## S my_atomic_load ## S( \ + my_atomic_ ## S ## _t *a, my_atomic_rwlock_t *rw) \ +{ \ + uint ## S ret; \ + CHECK_RW; \ + if (rw) my_atomic_rwlock_wrlock(rw); \ + ret= a->val; \ + if (rw) my_atomic_rwlock_wrunlock(rw); \ + return ret; \ +} + +#define make_atomic_store(S) \ +static inline void my_atomic_store ## S( \ + my_atomic_ ## S ## _t *a, uint ## S v, my_atomic_rwlock_t *rw) \ +{ \ + CHECK_RW; \ + if (rw) my_atomic_rwlock_rdlock(rw); \ + (a)->val= (v); \ + if (rw) my_atomic_rwlock_rdunlock(rw); \ +} + +#else /* no inline functions */ + +#define make_atomic_add(S) \ +extern uint ## S my_atomic_add ## S( \ + my_atomic_ ## S ## _t *a, uint ## S v, my_atomic_rwlock_t *rw); + +#define make_atomic_swap(S) \ +extern uint ## S my_atomic_swap ## S( \ + my_atomic_ ## S ## _t *a, uint ## S v, my_atomic_rwlock_t *rw); + +#define make_atomic_cas(S) \ +extern uint my_atomic_cas ## S(my_atomic_ ## S ## _t *a, \ + uint ## S *cmp, uint ## S set, my_atomic_rwlock_t *rw); + +#define make_atomic_load(S) \ +extern uint ## S my_atomic_load ## S( \ + my_atomic_ ## S ## _t *a, my_atomic_rwlock_t *rw); + +#define make_atomic_store(S) \ +extern void my_atomic_store ## S( \ + my_atomic_ ## S ## _t *a, uint ## S v, my_atomic_rwlock_t *rw); + +#endif + +make_atomic_add( 8) +make_atomic_add(16) +make_atomic_add(32) +make_atomic_add(64) +make_atomic_cas( 8) +make_atomic_cas(16) +make_atomic_cas(32) +make_atomic_cas(64) +make_atomic_load( 8) +make_atomic_load(16) +make_atomic_load(32) +make_atomic_load(64) +make_atomic_store( 8) +make_atomic_store(16) +make_atomic_store(32) +make_atomic_store(64) +make_atomic_swap( 8) +make_atomic_swap(16) +make_atomic_swap(32) +make_atomic_swap(64) +#undef make_atomic_add +#undef make_atomic_cas +#undef make_atomic_load +#undef make_atomic_store +#undef make_atomic_swap +#undef CHECK_RW + + diff --git a/include/atomic/x86-gcc.h b/include/atomic/x86-gcc.h new file mode 100644 index 00000000000..7576db54d69 --- /dev/null +++ b/include/atomic/x86-gcc.h @@ -0,0 +1,59 @@ +/* Copyright (C) 2006 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* + XXX 64-bit atomic operations can be implemented using + cmpxchg8b, if necessary +*/ + +/* fix -ansi errors while maintaining readability */ +#define asm __asm__ + +#define make_atomic_add_body8 \ + asm volatile (LOCK "xadd %0, %1;" : "+r" (v) , "+m" (a->val)) +#define make_atomic_swap_body8 \ + asm volatile ("xchg %0, %1;" : "+r" (v) , "+m" (a->val)) +#define make_atomic_cas_body8 \ + asm volatile (LOCK "cmpxchg %3, %0; setz %2;" \ + : "+m" (a->val), "+a" (*cmp), "=q" (ret): "r" (set)) + +#ifdef MY_ATOMIC_MODE_DUMMY +#define make_atomic_load_body8 ret=a->val +#define make_atomic_store_body8 a->val=v +#else +/* + Actually 32-bit reads/writes are always atomic on x86 + But we add LOCK here anyway to force memory barriers +*/ +#define make_atomic_load_body8 \ + ret=0; \ + asm volatile (LOCK "cmpxchg %2, %0" \ + : "+m" (a->val), "+a" (ret): "r" (ret)) +#define make_atomic_store_body8 \ + asm volatile ("xchg %0, %1;" : "+m" (a->val) : "r" (v)) +#endif + +#define make_atomic_add_body16 make_atomic_add_body8 +#define make_atomic_add_body32 make_atomic_add_body8 +#define make_atomic_cas_body16 make_atomic_cas_body8 +#define make_atomic_cas_body32 make_atomic_cas_body8 +#define make_atomic_load_body16 make_atomic_load_body8 +#define make_atomic_load_body32 make_atomic_load_body8 +#define make_atomic_store_body16 make_atomic_store_body8 +#define make_atomic_store_body32 make_atomic_store_body8 +#define make_atomic_swap_body16 make_atomic_swap_body8 +#define make_atomic_swap_body32 make_atomic_swap_body8 + diff --git a/include/atomic/x86-msvc.h b/include/atomic/x86-msvc.h new file mode 100644 index 00000000000..19645551196 --- /dev/null +++ b/include/atomic/x86-msvc.h @@ -0,0 +1,85 @@ +/* Copyright (C) 2006 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* + XXX 64-bit atomic operations can be implemented using + cmpxchg8b, if necessary +*/ + +// Would it be better to use intrinsics ? +// (InterlockedCompareExchange, InterlockedCompareExchange16 +// InterlockedExchangeAdd, InterlockedExchange) + +#define make_atomic_add_body(REG) \ + _asm { \ + _asm mov REG, v \ + _asm LOCK xadd a->val, REG \ + _asm movzx v, REG \ + } +#define make_atomic_cas_body(AREG,REG2) \ + _asm { \ + _asm mov AREG, *cmp \ + _asm mov REG2, set \ + _asm LOCK cmpxchg a->val, REG2 \ + _asm mov *cmp, AREG \ + _asm setz al \ + _asm movzx ret, al \ + } +#define make_atomic_swap_body(REG) \ + _asm { \ + _asm mov REG, v \ + _asm xchg a->val, REG \ + _asm mov v, REG \ + } + +#ifdef MY_ATOMIC_MODE_DUMMY +#define make_atomic_load_body(AREG,REG) ret=a->val +#define make_atomic_store_body(REG) a->val=v +#else +/* + Actually 32-bit reads/writes are always atomic on x86 + But we add LOCK here anyway to force memory barriers +*/ +#define make_atomic_load_body(AREG,REG2) \ + _asm { \ + _asm mov AREG, 0 \ + _asm mov REG2, AREG \ + _asm LOCK cmpxchg a->val, REG2 \ + _asm mov ret, AREG \ + } +#define make_atomic_store_body(REG) \ + _asm { \ + _asm mov REG, v \ + _asm xchg a->val, REG \ + } +#endif + +#define make_atomic_add_body8 make_atomic_add_body(al) +#define make_atomic_add_body16 make_atomic_add_body(ax) +#define make_atomic_add_body32 make_atomic_add_body(eax) +#define make_atomic_cas_body8 make_atomic_cas_body(al, bl) +#define make_atomic_cas_body16 make_atomic_cas_body(ax, bx) +#define make_atomic_cas_body32 make_atomic_cas_body(eax, ebx) +#define make_atomic_load_body8 make_atomic_load_body(al, bl) +#define make_atomic_load_body16 make_atomic_load_body(ax, bx) +#define make_atomic_load_body32 make_atomic_load_body(eax, ebx) +#define make_atomic_store_body8 make_atomic_store_body(al) +#define make_atomic_store_body16 make_atomic_store_body(ax) +#define make_atomic_store_body32 make_atomic_store_body(eax) +#define make_atomic_swap_body8 make_atomic_swap_body(al) +#define make_atomic_swap_body16 make_atomic_swap_body(ax) +#define make_atomic_swap_body32 make_atomic_swap_body(eax) + diff --git a/include/base64.h b/include/base64.h index a2b0fc0352b..4653e824a9a 100644 --- a/include/base64.h +++ b/include/base64.h @@ -21,8 +21,6 @@ extern "C" { #endif -#include <my_global.h> - /* Calculate how much memory needed for dst of base64_encode() */ diff --git a/include/heap.h b/include/heap.h index 4c003462a45..985b20f9dc9 100644 --- a/include/heap.h +++ b/include/heap.h @@ -209,6 +209,7 @@ extern int heap_create(const char *name, uint keys, HP_KEYDEF *keydef, extern int heap_delete_table(const char *name); extern void heap_drop_table(HP_INFO *info); extern int heap_extra(HP_INFO *info,enum ha_extra_function function); +extern int heap_reset(HP_INFO *info); extern int heap_rename(const char *old_name,const char *new_name); extern int heap_panic(enum ha_panic_function flag); extern int heap_rsame(HP_INFO *info,byte *record,int inx); diff --git a/include/my_atomic.h b/include/my_atomic.h new file mode 100644 index 00000000000..091edc0f57b --- /dev/null +++ b/include/my_atomic.h @@ -0,0 +1,46 @@ +/* Copyright (C) 2006 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifndef atomic_rwlock_init + +#ifdef MY_ATOMIC_EXTRA_DEBUG +#ifndef MY_ATOMIC_MODE_RWLOCKS +#error MY_ATOMIC_EXTRA_DEBUG can be only used with MY_ATOMIC_MODE_RWLOCKS +#endif +#define LOCK_PTR void *rw; +#else +#define LOCK_PTR +#endif + +typedef volatile struct {uint8 val; LOCK_PTR} my_atomic_8_t; +typedef volatile struct {uint16 val; LOCK_PTR} my_atomic_16_t; +typedef volatile struct {uint32 val; LOCK_PTR} my_atomic_32_t; +typedef volatile struct {uint64 val; LOCK_PTR} my_atomic_64_t; + +#ifndef MY_ATOMIC_MODE_RWLOCKS +#include "atomic/nolock.h" +#endif + +#ifndef my_atomic_rwlock_init +#include "atomic/rwlock.h" +#endif + +#define MY_ATOMIC_OK 0 +#define MY_ATOMIC_NOT_1CPU 1 +extern int my_atomic_initialize(); + +#endif + diff --git a/include/my_base.h b/include/my_base.h index e77bffeaf94..728b2ab2a1b 100644 --- a/include/my_base.h +++ b/include/my_base.h @@ -102,7 +102,7 @@ enum ha_key_alg { enum ha_extra_function { HA_EXTRA_NORMAL=0, /* Optimize for space (def) */ HA_EXTRA_QUICK=1, /* Optimize for speed */ - HA_EXTRA_RESET=2, /* Reset database to after open */ + HA_EXTRA_NOT_USED=2, HA_EXTRA_CACHE=3, /* Cache record in HA_rrnd() */ HA_EXTRA_NO_CACHE=4, /* End caching of records (def) */ HA_EXTRA_NO_READCHECK=5, /* No readcheck on update */ @@ -128,15 +128,6 @@ enum ha_extra_function { HA_EXTRA_RESET_STATE, /* Reset positions */ HA_EXTRA_IGNORE_DUP_KEY, /* Dup keys don't rollback everything*/ HA_EXTRA_NO_IGNORE_DUP_KEY, - /* - Instructs InnoDB to retrieve all columns (except in key read), not just - those where field->query_id is the same as the current query id - */ - HA_EXTRA_RETRIEVE_ALL_COLS, - /* - Instructs InnoDB to retrieve at least all the primary key columns - */ - HA_EXTRA_RETRIEVE_PRIMARY_KEY, HA_EXTRA_PREPARE_FOR_DELETE, HA_EXTRA_PREPARE_FOR_UPDATE, /* Remove read cache if problems */ HA_EXTRA_PRELOAD_BUFFER_SIZE, /* Set buffer size for preloading */ diff --git a/include/my_bitmap.h b/include/my_bitmap.h index 428ca7dc702..0b16a1b4832 100644 --- a/include/my_bitmap.h +++ b/include/my_bitmap.h @@ -17,19 +17,18 @@ #ifndef _my_bitmap_h_ #define _my_bitmap_h_ -#ifdef THREAD -#include <my_pthread.h> -#endif - #define MY_BIT_NONE (~(uint) 0) +#include <m_string.h> + +typedef uint32 my_bitmap_map; typedef struct st_bitmap { - uint32 *bitmap; + my_bitmap_map *bitmap; uint n_bits; /* number of bits occupied by the above */ - uint32 last_word_mask; - uint32 *last_word_ptr; + my_bitmap_map last_word_mask; + my_bitmap_map *last_word_ptr; /* mutex will be acquired for the duration of each bitmap operation if thread_safe flag in bitmap_init was set. Otherwise, we optimize by not @@ -43,12 +42,16 @@ typedef struct st_bitmap #ifdef __cplusplus extern "C" { #endif -extern my_bool bitmap_init(MY_BITMAP *map, uint32 *buf, uint n_bits, my_bool thread_safe); +extern my_bool bitmap_init(MY_BITMAP *map, my_bitmap_map *buf, uint n_bits, + my_bool thread_safe); extern my_bool bitmap_is_clear_all(const MY_BITMAP *map); extern my_bool bitmap_is_prefix(const MY_BITMAP *map, uint prefix_size); extern my_bool bitmap_is_set_all(const MY_BITMAP *map); extern my_bool bitmap_is_subset(const MY_BITMAP *map1, const MY_BITMAP *map2); +extern my_bool bitmap_is_overlapping(const MY_BITMAP *map1, + const MY_BITMAP *map2); extern my_bool bitmap_test_and_set(MY_BITMAP *map, uint bitmap_bit); +extern my_bool bitmap_test_and_clear(MY_BITMAP *map, uint bitmap_bit); extern my_bool bitmap_fast_test_and_set(MY_BITMAP *map, uint bitmap_bit); extern uint bitmap_set_next(MY_BITMAP *map); extern uint bitmap_get_first(const MY_BITMAP *map); @@ -62,6 +65,7 @@ extern void bitmap_subtract(MY_BITMAP *map, const MY_BITMAP *map2); extern void bitmap_union(MY_BITMAP *map, const MY_BITMAP *map2); extern void bitmap_xor(MY_BITMAP *map, const MY_BITMAP *map2); extern void bitmap_invert(MY_BITMAP *map); +extern void bitmap_copy(MY_BITMAP *map, const MY_BITMAP *map2); extern uint bitmap_lock_set_next(MY_BITMAP *map); extern void bitmap_lock_clear_bit(MY_BITMAP *map, uint bitmap_bit); @@ -88,7 +92,7 @@ extern void bitmap_lock_xor(MY_BITMAP *map, const MY_BITMAP *map2); extern void bitmap_lock_invert(MY_BITMAP *map); #endif /* Fast, not thread safe, bitmap functions */ -#define bitmap_buffer_size(bits) 4*(((bits)+31)/32); +#define bitmap_buffer_size(bits) (((bits)+31)/32)*4 #define no_bytes_in_map(map) (((map)->n_bits + 7)/8) #define no_words_in_map(map) (((map)->n_bits + 31)/32) #define bytes_word_aligned(bytes) (4*((bytes + 3)/4)) @@ -98,28 +102,28 @@ extern void bitmap_lock_invert(MY_BITMAP *map); ^= (1 << ((BIT) & 7))) #define _bitmap_clear_bit(MAP, BIT) (((uchar*)(MAP)->bitmap)[(BIT) / 8] \ &= ~ (1 << ((BIT) & 7))) -#define _bitmap_is_set(MAP, BIT) (((uchar*)(MAP)->bitmap)[(BIT) / 8] \ - & (1 << ((BIT) & 7))) +#define _bitmap_is_set(MAP, BIT) (uint) (((uchar*)(MAP)->bitmap)[(BIT) / 8] \ + & (1 << ((BIT) & 7))) #ifndef DBUG_OFF -static inline uint32 +static inline void bitmap_set_bit(MY_BITMAP *map,uint bit) { DBUG_ASSERT(bit < (map)->n_bits); - return _bitmap_set_bit(map,bit); + _bitmap_set_bit(map,bit); } -static inline uint32 +static inline void bitmap_flip_bit(MY_BITMAP *map,uint bit) { DBUG_ASSERT(bit < (map)->n_bits); - return _bitmap_flip_bit(map,bit); + _bitmap_flip_bit(map,bit); } -static inline uint32 +static inline void bitmap_clear_bit(MY_BITMAP *map,uint bit) { DBUG_ASSERT(bit < (map)->n_bits); - return _bitmap_clear_bit(map,bit); + _bitmap_clear_bit(map,bit); } -static inline uint32 +static inline uint bitmap_is_set(const MY_BITMAP *map,uint bit) { DBUG_ASSERT(bit < (map)->n_bits); @@ -131,11 +135,16 @@ bitmap_is_set(const MY_BITMAP *map,uint bit) #define bitmap_clear_bit(MAP, BIT) _bitmap_clear_bit(MAP, BIT) #define bitmap_is_set(MAP, BIT) _bitmap_is_set(MAP, BIT) #endif -#define bitmap_cmp(MAP1, MAP2) \ - (memcmp((MAP1)->bitmap, (MAP2)->bitmap, 4*no_words_in_map((MAP1)))==0) + +static inline my_bool bitmap_cmp(const MY_BITMAP *map1, const MY_BITMAP *map2) +{ + *(map1)->last_word_ptr|= (map1)->last_word_mask; + *(map2)->last_word_ptr|= (map2)->last_word_mask; + return memcmp((map1)->bitmap, (map2)->bitmap, 4*no_words_in_map((map1)))==0; +} + #define bitmap_clear_all(MAP) \ - { memset((MAP)->bitmap, 0, 4*no_words_in_map((MAP))); \ - *(MAP)->last_word_ptr|= (MAP)->last_word_mask; } + { memset((MAP)->bitmap, 0, 4*no_words_in_map((MAP))); } #define bitmap_set_all(MAP) \ (memset((MAP)->bitmap, 0xFF, 4*no_words_in_map((MAP)))) diff --git a/include/my_global.h b/include/my_global.h index 100e695a0de..f2ad3af0b6f 100644 --- a/include/my_global.h +++ b/include/my_global.h @@ -181,6 +181,17 @@ #define HOT_DATA #endif +/* + now let's figure out if inline functions are supported + autoconf defines 'inline' to be empty, if not +*/ +#define inline_test_1(X) X ## 1 +#define inline_test_2(X) inline_test_1(X) +#if inline_test_2(inline) != 1 +#define HAVE_INLINE +#endif +#undef inline_test_2 +#undef inline_test_1 /* The following macros are used to control inlining a bit more than @@ -853,26 +864,36 @@ typedef void *gptr; /* Generic pointer */ #else typedef char *gptr; /* Generic pointer */ #endif -#ifndef HAVE_INT_8_16_32 -typedef signed char int8; /* Signed integer >= 8 bits */ -typedef short int16; /* Signed integer >= 16 bits */ -#endif #ifndef HAVE_UCHAR typedef unsigned char uchar; /* Short for unsigned char */ #endif -typedef unsigned char uint8; /* Short for unsigned integer >= 8 bits */ -typedef unsigned short uint16; /* Short for unsigned integer >= 16 bits */ +#ifndef HAVE_INT8 +typedef signed char int8; /* Signed integer >= 8 bits */ +#endif +#ifndef HAVE_UINT8 +typedef unsigned char uint8; /* Unsigned integer >= 8 bits */ +#endif +#ifndef HAVE_INT16 +typedef short int16; +#endif +#ifndef HAVE_UINT16 +typedef unsigned short uint16; +#endif #if SIZEOF_INT == 4 -#ifndef HAVE_INT_8_16_32 -typedef int int32; +#ifndef HAVE_INT32 +typedef int int32; +#endif +#ifndef HAVE_UINT32 +typedef unsigned int uint32; #endif -typedef unsigned int uint32; /* Short for unsigned integer >= 32 bits */ #elif SIZEOF_LONG == 4 -#ifndef HAVE_INT_8_16_32 -typedef long int32; +#ifndef HAVE_INT32 +typedef long int32; +#endif +#ifndef HAVE_UINT32 +typedef unsigned long uint32; #endif -typedef unsigned long uint32; /* Short for unsigned integer >= 32 bits */ #else #error "Neither int or long is of 4 bytes width" #endif @@ -889,6 +910,12 @@ typedef unsigned long ulonglong; /* ulong or unsigned long long */ typedef long longlong; #endif #endif +#ifndef HAVE_INT64 +typedef longlong int64; +#endif +#ifndef HAVE_UINT64 +typedef ulonglong uint64; +#endif #if defined(NO_CLIENT_LONG_LONG) typedef unsigned long my_ulonglong; diff --git a/include/my_sys.h b/include/my_sys.h index 5024505a821..4ea7cecf0a1 100644 --- a/include/my_sys.h +++ b/include/my_sys.h @@ -821,8 +821,9 @@ extern ulong crc32(ulong crc, const uchar *buf, uint len); extern uint my_set_max_open_files(uint files); void my_free_open_file_info(void); -ulonglong my_getsystime(void); -my_bool my_gethwaddr(uchar *to); +extern ulonglong my_getsystime(void); +extern my_bool my_gethwaddr(uchar *to); +extern int my_getncpus(); #ifdef HAVE_SYS_MMAN_H #include <sys/mman.h> diff --git a/include/myisam.h b/include/myisam.h index db1a7bd984d..075779a6e42 100644 --- a/include/myisam.h +++ b/include/myisam.h @@ -303,6 +303,7 @@ extern int mi_rename(const char *from, const char *to); extern int mi_extra(struct st_myisam_info *file, enum ha_extra_function function, void *extra_arg); +extern int mi_reset(struct st_myisam_info *file); extern ha_rows mi_records_in_range(struct st_myisam_info *info,int inx, key_range *min_key, key_range *max_key); extern int mi_log(int activate_log); diff --git a/include/myisammrg.h b/include/myisammrg.h index de8a36c2d0a..f23759e22e1 100644 --- a/include/myisammrg.h +++ b/include/myisammrg.h @@ -99,6 +99,7 @@ extern int myrg_create(const char *name, const char **table_names, uint insert_method, my_bool fix_names); extern int myrg_extra(MYRG_INFO *file,enum ha_extra_function function, void *extra_arg); +extern int myrg_reset(MYRG_INFO *info); extern void myrg_extrafunc(MYRG_INFO *info,invalidator_by_filename inv); extern ha_rows myrg_records_in_range(MYRG_INFO *info,int inx, key_range *min_key, key_range *max_key); diff --git a/include/mysql/plugin.h b/include/mysql/plugin.h index ab5ca6e7be4..0cee54bffde 100644 --- a/include/mysql/plugin.h +++ b/include/mysql/plugin.h @@ -28,7 +28,7 @@ */ #define MYSQL_UDF_PLUGIN 0 /* User-defined function */ #define MYSQL_STORAGE_ENGINE_PLUGIN 1 /* Storage Engine */ -#define MYSQL_FTPARSER_PLUGIN 2 /* Full-text [pre]parser */ +#define MYSQL_FTPARSER_PLUGIN 2 /* Full-text parser plugin */ #define MYSQL_MAX_PLUGIN_TYPE_NUM 3 /* The number of plugin types */ /* @@ -95,12 +95,14 @@ struct st_mysql_plugin }; /************************************************************************* - API for Full-text [pre]parser plugin. (MYSQL_FTPARSER_PLUGIN) + API for Full-text parser plugin. (MYSQL_FTPARSER_PLUGIN) */ -#define MYSQL_FTPARSER_INTERFACE_VERSION 0x0000 +#define MYSQL_FTPARSER_INTERFACE_VERSION 0x0100 /* Parsing modes. Set in MYSQL_FTPARSER_PARAM::mode */ +enum enum_ftparser_mode +{ /* Fast and simple mode. This mode is used for indexing, and natural language queries. @@ -109,7 +111,7 @@ struct st_mysql_plugin index. Stopwords or too short/long words should not be returned. The 'boolean_info' argument of mysql_add_word() does not have to be set. */ -#define MYSQL_FTPARSER_SIMPLE_MODE 0 + MYSQL_FTPARSER_SIMPLE_MODE= 0, /* Parse with stopwords mode. This mode is used in boolean searches for @@ -120,7 +122,7 @@ struct st_mysql_plugin or long. The 'boolean_info' argument of mysql_add_word() does not have to be set. */ -#define MYSQL_FTPARSER_WITH_STOPWORDS 1 + MYSQL_FTPARSER_WITH_STOPWORDS= 1, /* Parse in boolean mode. This mode is used to parse a boolean query string. @@ -133,7 +135,8 @@ struct st_mysql_plugin MYSQL_FTPARSER_WITH_STOPWORDS mode, no word should be ignored. Instead, use FT_TOKEN_STOPWORD for the token type of such a word. */ -#define MYSQL_FTPARSER_FULL_BOOLEAN_INFO 2 + MYSQL_FTPARSER_FULL_BOOLEAN_INFO= 2 +}; /* Token types for boolean mode searching (used for the type member of @@ -198,6 +201,17 @@ typedef struct st_mysql_ftparser_boolean_info char *quot; } MYSQL_FTPARSER_BOOLEAN_INFO; +/* + The following flag means that buffer with a string (document, word) + may be overwritten by the caller before the end of the parsing (that is + before st_mysql_ftparser::deinit() call). If one needs the string + to survive between two successive calls of the parsing function, she + needs to save a copy of it. The flag may be set by MySQL before calling + st_mysql_ftparser::parse(), or it may be set by a plugin before calling + st_mysql_ftparser_param::mysql_parse() or + st_mysql_ftparser_param::mysql_add_word(). +*/ +#define MYSQL_FTFLAGS_NEED_COPY 1 /* An argument of the full-text parser plugin. This structure is @@ -209,22 +223,20 @@ typedef struct st_mysql_ftparser_boolean_info to invoke the MySQL default parser. If plugin's role is to extract textual data from .doc, .pdf or .xml content, it might extract plaintext from the content, and then pass the text to the default - MySQL parser to be parsed. When mysql_parser is called, its param - argument should be given as the mysql_ftparam value. + MySQL parser to be parsed. mysql_add_word: A server callback to add a new word. When parsing a document, the server sets this to point at a function that adds the word to MySQL full-text index. When parsing a search query, this function will add the new word to the list of words to search - for. When mysql_add_word is called, its param argument should be - given as the mysql_ftparam value. boolean_info can be NULL for all - cases except when mode is MYSQL_FTPARSER_FULL_BOOLEAN_INFO. + for. The boolean_info argument can be NULL for all cases except + when mode is MYSQL_FTPARSER_FULL_BOOLEAN_INFO. ftparser_state: A generic pointer. The plugin can set it to point to information to be used internally for its own purposes. - mysql_ftparam: This is set by the server. It is passed as the first - argument to the mysql_parse or mysql_add_word callback. The plugin + mysql_ftparam: This is set by the server. It is used by MySQL functions + called via mysql_parse() and mysql_add_word() callback. The plugin should not modify it. cs: Information about the character set of the document or query string. @@ -233,21 +245,26 @@ typedef struct st_mysql_ftparser_boolean_info length: Length of the document or query string, in bytes. + flags: See MYSQL_FTFLAGS_* constants above. + mode: The parsing mode. With boolean operators, with stopwords, or - nothing. See MYSQL_FTPARSER_* constants above. + nothing. See enum_ftparser_mode above. */ typedef struct st_mysql_ftparser_param { - int (*mysql_parse)(void *param, char *doc, int doc_len); - int (*mysql_add_word)(void *param, char *word, int word_len, + int (*mysql_parse)(struct st_mysql_ftparser_param *, + char *doc, int doc_len); + int (*mysql_add_word)(struct st_mysql_ftparser_param *, + char *word, int word_len, MYSQL_FTPARSER_BOOLEAN_INFO *boolean_info); void *ftparser_state; void *mysql_ftparam; struct charset_info_st *cs; char *doc; int length; - int mode; + int flags; + enum enum_ftparser_mode mode; } MYSQL_FTPARSER_PARAM; /* @@ -265,5 +282,25 @@ struct st_mysql_ftparser int (*init)(MYSQL_FTPARSER_PARAM *param); int (*deinit)(MYSQL_FTPARSER_PARAM *param); }; + +/************************************************************************* + API for Storage Engine plugin. (MYSQL_STORAGE_ENGINE_PLUGIN) +*/ + +/* handlertons of different MySQL releases are incompatible */ +#define MYSQL_HANDLERTON_INTERFACE_VERSION (MYSQL_VERSION_ID << 8) + +/* + The real API is in the sql/handler.h + Here we define only the descriptor structure, that is referred from + st_mysql_plugin. +*/ + +struct st_mysql_storage_engine +{ + int interface_version; + struct handlerton *handlerton; +}; + #endif diff --git a/include/mysql_com.h b/include/mysql_com.h index bff5fcc47d2..623aaf43783 100644 --- a/include/mysql_com.h +++ b/include/mysql_com.h @@ -98,6 +98,7 @@ enum enum_server_command #define BINCMP_FLAG 131072 /* Intern: Used by sql_yacc */ #define GET_FIXED_FIELDS_FLAG (1 << 18) /* Used to get fields in item tree */ #define FIELD_IN_PART_FUNC_FLAG (1 << 19)/* Field part of partition func */ +#define FIELD_IN_ADD_INDEX (1<< 20) /* Intern: Field used in ADD INDEX */ #define REFRESH_GRANT 1 /* Refresh grant tables */ #define REFRESH_LOG 2 /* Start on new log file */ diff --git a/include/violite.h b/include/violite.h index 4837ade64b4..323e72e82d3 100644 --- a/include/violite.h +++ b/include/violite.h @@ -102,6 +102,7 @@ void vio_timeout(Vio *vio,uint which, uint timeout); #define HEADER_DES_LOCL_H dummy_something #define YASSL_MYSQL_COMPATIBLE +#define YASSL_PREFIX #include <openssl/ssl.h> #include <openssl/err.h> diff --git a/libmysql/Makefile.am b/libmysql/Makefile.am index 787ffc51de7..5e6c60a007b 100644 --- a/libmysql/Makefile.am +++ b/libmysql/Makefile.am @@ -24,7 +24,7 @@ target = libmysqlclient.la target_defs = -DUNDEF_THREADS_HACK -DDONT_USE_RAID @LIB_EXTRA_CCFLAGS@ LIBS = @CLIENT_LIBS@ INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include \ - $(openssl_includes) $(yassl_includes) @ZLIB_INCLUDES@ + $(openssl_includes) @ZLIB_INCLUDES@ include $(srcdir)/Makefile.shared diff --git a/libmysql_r/Makefile.am b/libmysql_r/Makefile.am index a1ccbca48e5..93f5bffdec7 100644 --- a/libmysql_r/Makefile.am +++ b/libmysql_r/Makefile.am @@ -25,7 +25,7 @@ target_defs = -DDONT_USE_RAID -DMYSQL_CLIENT @LIB_EXTRA_CCFLAGS@ LIBS = @LIBS@ @ZLIB_LIBS@ @openssl_libs@ INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include \ - $(openssl_includes) $(yassl_includes) @ZLIB_INCLUDES@ + $(openssl_includes) @ZLIB_INCLUDES@ ## automake barfs if you don't use $(srcdir) or $(top_srcdir) in include include $(top_srcdir)/libmysql/Makefile.shared diff --git a/libmysqld/Makefile.am b/libmysqld/Makefile.am index a3af2d43bd5..50f364babe1 100644 --- a/libmysqld/Makefile.am +++ b/libmysqld/Makefile.am @@ -32,7 +32,7 @@ INCLUDES= -I$(top_builddir)/include -I$(top_srcdir)/include \ -I$(top_builddir)/include -I$(top_srcdir)/include \ -I$(top_srcdir)/sql -I$(top_srcdir)/sql/examples \ -I$(top_srcdir)/regex \ - $(openssl_includes) $(yassl_includes) @ZLIB_INCLUDES@ + $(openssl_includes) @ZLIB_INCLUDES@ noinst_LIBRARIES = libmysqld_int.a pkglib_LIBRARIES = libmysqld.a diff --git a/libmysqld/examples/Makefile.am b/libmysqld/examples/Makefile.am index 7ee8adfef80..c705fb1e68d 100644 --- a/libmysqld/examples/Makefile.am +++ b/libmysqld/examples/Makefile.am @@ -34,7 +34,7 @@ link_sources: DEFS = -DEMBEDDED_LIBRARY INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include -I$(srcdir) \ -I$(top_srcdir) -I$(top_srcdir)/client -I$(top_srcdir)/regex \ - $(openssl_includes) $(yassl_includes) + $(openssl_includes) LIBS = @LIBS@ @WRAPLIBS@ @CLIENT_LIBS@ $(yassl_libs) LDADD = @CLIENT_EXTRA_LDFLAGS@ ../libmysqld.a @LIBDL@ $(CXXLDFLAGS) diff --git a/mysql-test/extra/binlog_tests/binlog.test b/mysql-test/extra/binlog_tests/binlog.test index ff43debf967..6f7990893f0 100644 --- a/mysql-test/extra/binlog_tests/binlog.test +++ b/mysql-test/extra/binlog_tests/binlog.test @@ -22,7 +22,7 @@ insert t2 values (5); commit; # first COMMIT must be Query_log_event, second - Xid_log_event --replace_column 2 # 5 # ---replace_regex /table_id: [0-9]+/table_id: #/ +--replace_regex /table_id: [0-9]+/table_id: #/ /\/\* xid=.* \*\//\/* xid= *\// show binlog events from 102; drop table t1,t2; @@ -44,8 +44,8 @@ while ($1) commit; drop table t1; --replace_column 2 # 5 # ---replace_regex /table_id: [0-9]+/table_id: #/ +--replace_regex /table_id: [0-9]+/table_id: #/ /\/\* xid=.* \*\//\/* xid= *\// show binlog events in 'master-bin.000001' from 102; --replace_column 2 # 5 # ---replace_regex /table_id: [0-9]+/table_id: #/ +--replace_regex /table_id: [0-9]+/table_id: #/ /\/\* xid=.* \*\//\/* xid= *\// show binlog events in 'master-bin.000002' from 102; diff --git a/mysql-test/extra/binlog_tests/innodb_stat.test b/mysql-test/extra/binlog_tests/innodb_stat.test index 131c244d8fd..4638dfcd2f7 100644 --- a/mysql-test/extra/binlog_tests/innodb_stat.test +++ b/mysql-test/extra/binlog_tests/innodb_stat.test @@ -1,3 +1,5 @@ +# Embedded server doesn't support binlog +-- source include/not_embedded.inc -- source include/have_innodb.inc # diff --git a/mysql-test/extra/binlog_tests/mix_innodb_myisam_binlog.test b/mysql-test/extra/binlog_tests/mix_innodb_myisam_binlog.test index b75a326d5a8..b5052620f91 100644 --- a/mysql-test/extra/binlog_tests/mix_innodb_myisam_binlog.test +++ b/mysql-test/extra/binlog_tests/mix_innodb_myisam_binlog.test @@ -30,7 +30,7 @@ insert into t2 select * from t1; commit; --replace_column 5 # ---replace_regex /table_id: [0-9]+/table_id: #/ +--replace_regex /table_id: [0-9]+/table_id: #/ /\/\* xid=.* \*\//\/* xid= *\// show binlog events from 102; delete from t1; @@ -44,7 +44,7 @@ insert into t2 select * from t1; rollback; --replace_column 5 # ---replace_regex /table_id: [0-9]+/table_id: #/ +--replace_regex /table_id: [0-9]+/table_id: #/ /\/\* xid=.* \*\//\/* xid= *\// show binlog events from 102; delete from t1; @@ -60,7 +60,7 @@ rollback to savepoint my_savepoint; commit; --replace_column 5 # ---replace_regex /table_id: [0-9]+/table_id: #/ +--replace_regex /table_id: [0-9]+/table_id: #/ /\/\* xid=.* \*\//\/* xid= *\// show binlog events from 102; delete from t1; @@ -78,7 +78,7 @@ commit; select a from t1 order by a; # check that savepoints work :) --replace_column 5 # ---replace_regex /table_id: [0-9]+/table_id: #/ +--replace_regex /table_id: [0-9]+/table_id: #/ /\/\* xid=.* \*\//\/* xid= *\// show binlog events from 102; # and when ROLLBACK is not explicit? @@ -100,7 +100,7 @@ connection con2; # logging has been done, we use a user lock. select get_lock("a",10); --replace_column 5 # ---replace_regex /table_id: [0-9]+/table_id: #/ +--replace_regex /table_id: [0-9]+/table_id: #/ /\/\* xid=.* \*\//\/* xid= *\// show binlog events from 102; # and when not in a transact1on? @@ -112,7 +112,7 @@ insert into t1 values(9); insert into t2 select * from t1; --replace_column 5 # ---replace_regex /table_id: [0-9]+/table_id: #/ +--replace_regex /table_id: [0-9]+/table_id: #/ /\/\* xid=.* \*\//\/* xid= *\// show binlog events from 102; # Check that when the query updat1ng the MyISAM table is the first in the @@ -125,13 +125,13 @@ insert into t1 values(10); # first make t1 non-empty begin; insert into t2 select * from t1; --replace_column 5 # ---replace_regex /table_id: [0-9]+/table_id: #/ +--replace_regex /table_id: [0-9]+/table_id: #/ /\/\* xid=.* \*\//\/* xid= *\// show binlog events from 102; insert into t1 values(11); commit; --replace_column 5 # ---replace_regex /table_id: [0-9]+/table_id: #/ +--replace_regex /table_id: [0-9]+/table_id: #/ /\/\* xid=.* \*\//\/* xid= *\// show binlog events from 102; @@ -150,7 +150,7 @@ insert into t2 select * from t1; commit; --replace_column 5 # ---replace_regex /table_id: [0-9]+/table_id: #/ +--replace_regex /table_id: [0-9]+/table_id: #/ /\/\* xid=.* \*\//\/* xid= *\// show binlog events from 102; delete from t1; @@ -163,7 +163,7 @@ insert into t2 select * from t1; rollback; --replace_column 5 # ---replace_regex /table_id: [0-9]+/table_id: #/ +--replace_regex /table_id: [0-9]+/table_id: #/ /\/\* xid=.* \*\//\/* xid= *\// show binlog events from 102; delete from t1; @@ -179,7 +179,7 @@ rollback to savepoint my_savepoint; commit; --replace_column 5 # ---replace_regex /table_id: [0-9]+/table_id: #/ +--replace_regex /table_id: [0-9]+/table_id: #/ /\/\* xid=.* \*\//\/* xid= *\// show binlog events from 102; delete from t1; @@ -197,7 +197,7 @@ commit; select a from t1 order by a; # check that savepoints work :) --replace_column 5 # ---replace_regex /table_id: [0-9]+/table_id: #/ +--replace_regex /table_id: [0-9]+/table_id: #/ /\/\* xid=.* \*\//\/* xid= *\// show binlog events from 102; # Test for BUG#5714, where a MyISAM update in the transaction used to @@ -234,8 +234,8 @@ select (@after-@before) >= 2; drop table t1,t2; commit; -# test for BUG#7947 - DO RELEASE_LOCK() not written to binlog on rollback in the middle -# of a transaction +# test for BUG#7947 - DO RELEASE_LOCK() not written to binlog on rollback in +# the middle of a transaction connection con2; begin; @@ -258,13 +258,75 @@ disconnect con2; connection con3; select get_lock("lock1",60); --replace_column 5 # ---replace_regex /table_id: [0-9]+/table_id: #/ +--replace_regex /table_id: [0-9]+/table_id: #/ /\/\* xid=.* \*\//\/* xid= *\// show binlog events from 102; do release_lock("lock1"); drop table t0,t2; # End of 4.1 tests +# +# Test behaviour of CREATE ... SELECT when mixing MyISAM and InnoDB tables +# + +set autocommit=0; +CREATE TABLE t1 (a int, b int) engine=myisam; +reset master; +INSERT INTO t1 values (1,1),(1,2); +--error 1062 +CREATE TABLE t2 (primary key (a)) engine=innodb select * from t1; +# This should give warning +DROP TABLE if exists t2; +INSERT INTO t1 values (3,3); +--error 1062 +CREATE TEMPORARY TABLE t2 (primary key (a)) engine=innodb select * from t1; +ROLLBACK; +# This should give warning +DROP TABLE IF EXISTS t2; + +CREATE TABLE t2 (a int, b int, primary key (a)) engine=innodb; +INSERT INTO t1 VALUES (4,4); +--error 1062 +CREATE TABLE IF NOT EXISTS t2 (primary key (a)) engine=innodb select * from t1; +SELECT * from t2; +TRUNCATE table t2; +INSERT INTO t1 VALUES (5,5); +--error 1062 +INSERT INTO t2 select * from t1; +SELECT * FROM t2; +DROP TABLE t2; + +INSERT INTO t1 values (6,6); +CREATE TEMPORARY TABLE t2 (a int, b int, primary key (a)) engine=innodb ; +INSERT INTO t1 values (7,7); +ROLLBACK; +INSERT INTO t1 values (8,8); +--error 1062 +CREATE TEMPORARY TABLE IF NOT EXISTS t2 (primary key (a)) engine=innodb select * from t1; +COMMIT; +INSERT INTO t1 values (9,9); +--error 1062 +CREATE TEMPORARY TABLE IF NOT EXISTS t2 (primary key (a)) engine=innodb select * from t1; +ROLLBACK; +SELECT * from t2; +TRUNCATE table t2; +INSERT INTO t1 values (10,10); +--error 1062 +INSERT INTO t2 select * from t1; +SELECT * from t1; +INSERT INTO t2 values (100,100); +--error 1062 +CREATE TEMPORARY TABLE IF NOT EXISTS t2 (primary key (a)) engine=innodb select * from t1; +COMMIT; +INSERT INTO t2 values (101,101); +--error 1062 +CREATE TEMPORARY TABLE IF NOT EXISTS t2 (primary key (a)) engine=innodb select * from t1; +ROLLBACK; +SELECT * from t2; +DROP TABLE t1,t2; +--replace_regex /table_id: [0-9]+/table_id: #/ /\/\* xid=.* \*\//\/* xid= *\// +show binlog events from 102; + # Test for BUG#16559 (ROLLBACK should always have a zero error code in # binlog). Has to be here and not earlier, as the SELECTs influence # XIDs differently between normal and ps-protocol (and SHOW BINLOG @@ -283,3 +345,4 @@ disconnect con3; connection con4; select get_lock("a",10); # wait for rollback to finish + diff --git a/mysql-test/extra/rpl_tests/rpl_insert_id.test b/mysql-test/extra/rpl_tests/rpl_insert_id.test index 304fade7e1d..68e39c54381 100644 --- a/mysql-test/extra/rpl_tests/rpl_insert_id.test +++ b/mysql-test/extra/rpl_tests/rpl_insert_id.test @@ -156,3 +156,5 @@ drop function bug15728_insert; drop table t1, t2; # End of 5.0 tests + +sync_slave_with_master; diff --git a/mysql-test/include/common-tests.inc b/mysql-test/include/common-tests.inc index 46d0182d17f..882ac689498 100644 --- a/mysql-test/include/common-tests.inc +++ b/mysql-test/include/common-tests.inc @@ -1296,9 +1296,9 @@ explain select fld3 from t2 use index (fld1,fld3) where fld3 = 'honeysuckle'; # The next should give an error # --- error 1072 +-- error 1176 explain select fld3 from t2 ignore index (fld3,not_used); --- error 1072 +-- error 1176 explain select fld3 from t2 use index (not_used); # diff --git a/mysql-test/install_test_db.sh b/mysql-test/install_test_db.sh index 4554b92857e..9006957019a 100644 --- a/mysql-test/install_test_db.sh +++ b/mysql-test/install_test_db.sh @@ -34,7 +34,6 @@ if [ x$1 = x"-slave" ] then shift 1 data=var/slave-data - ldata=$fix_bin/var/slave-data else if [ x$1 = x"-1" ] then @@ -42,8 +41,8 @@ else else data=var/master-data fi - ldata=$fix_bin/$data fi +ldata=$fix_bin/$data mdata=$data/mysql EXTRA_ARG="" @@ -81,9 +80,7 @@ basedir=. EXTRA_ARG="--language=../sql/share/english/ --character-sets-dir=../sql/share/charsets/" fi -mysqld_boot=" $execdir/mysqld --no-defaults --bootstrap --skip-grant-tables \ - --basedir=$basedir --datadir=$ldata --skip-innodb --skip-ndbcluster --skip-bdb \ - $EXTRA_ARG" +mysqld_boot=" $execdir/mysqld --no-defaults --bootstrap --skip-grant-tables --basedir=$basedir --datadir=$ldata --skip-innodb --skip-ndbcluster --skip-bdb --tmpdir=. $EXTRA_ARG" echo "running $mysqld_boot" if $scriptdir/mysql_create_system_tables test $mdata $hostname | $mysqld_boot diff --git a/mysql-test/lib/init_db.sql b/mysql-test/lib/init_db.sql index a7079b0ac33..a5736ed4b9b 100644 --- a/mysql-test/lib/init_db.sql +++ b/mysql-test/lib/init_db.sql @@ -631,7 +631,7 @@ CREATE TABLE event ( 'HIGH_NOT_PRECEDENCE' ) DEFAULT '' NOT NULL, comment char(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '', - PRIMARY KEY (definer, db, name) + PRIMARY KEY (db, name) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT 'Events'; CREATE DATABASE IF NOT EXISTS cluster; diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 8923c032dff..ec105b29e3f 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -694,6 +694,12 @@ sub command_line_setup () { { push(@opt_extra_mysqld_opt, $arg); } + elsif ( $arg =~ /^--$/ ) + { + # It is an effect of setting 'pass_through' in option processing + # that the lone '--' separating options from arguments survives, + # simply ignore it. + } elsif ( $arg =~ /^-/ ) { usage("Invalid option \"$arg\""); @@ -1172,6 +1178,8 @@ sub executable_setup () { sub environment_setup () { + umask(022); + # -------------------------------------------------------------------------- # We might not use a standard installation directory, like /usr/lib. # Set LD_LIBRARY_PATH to make sure we find our installed libraries. @@ -1941,6 +1949,7 @@ sub install_db ($$) { mtr_add_arg($args, "--skip-innodb"); mtr_add_arg($args, "--skip-ndbcluster"); mtr_add_arg($args, "--skip-bdb"); + mtr_add_arg($args, "--tmpdir=."); if ( ! $opt_netware ) { @@ -2603,7 +2612,6 @@ sub mysqld_arguments ($$$$$$) { mtr_add_arg($args, "%s--character-sets-dir=%s", $prefix, $path_charsetsdir); mtr_add_arg($args, "%s--core", $prefix); mtr_add_arg($args, "%s--log-bin-trust-function-creators", $prefix); - mtr_add_arg($args, "%s--loose-binlog-show-xid=0", $prefix); mtr_add_arg($args, "%s--default-character-set=latin1", $prefix); mtr_add_arg($args, "%s--language=%s", $prefix, $path_language); mtr_add_arg($args, "%s--tmpdir=$opt_tmpdir", $prefix); @@ -2749,7 +2757,6 @@ sub mysqld_arguments ($$$$$$) { mtr_add_arg($args, "%s--sort_buffer=256K", $prefix); mtr_add_arg($args, "%s--max_heap_table_size=1M", $prefix); mtr_add_arg($args, "%s--log-bin-trust-function-creators", $prefix); - mtr_add_arg($args, "%s--loose-binlog-show-xid=0", $prefix); if ( $opt_ssl_supported ) { @@ -3281,7 +3288,7 @@ sub run_mysqltest ($) { } my $cmdline_mysql= - "$exe_mysql --host=localhost --user=root --password= " . + "$exe_mysql --no-defaults --host=localhost --user=root --password= " . "--port=$master->[0]->{'path_myport'} " . "--socket=$master->[0]->{'path_mysock'}"; @@ -3712,6 +3719,13 @@ sub valgrind_arguments { ############################################################################## sub usage ($) { + my $message= shift; + + if ( $message ) + { + print STDERR "$message \n"; + } + print STDERR <<HERE; mysql-test-run [ OPTIONS ] [ TESTCASE ] diff --git a/mysql-test/mysql-test-run.sh b/mysql-test/mysql-test-run.sh index f56099f3a49..4cbb1bece9c 100644 --- a/mysql-test/mysql-test-run.sh +++ b/mysql-test/mysql-test-run.sh @@ -1121,7 +1121,10 @@ mysql_install_db () { if [ ! -z "$USE_NDBCLUSTER" ] then $ECHO "Installing Master Databases 1" - $INSTALL_DB -1 +# $INSTALL_DB -1 + $RM -rf var/master-data1 + mkdir var/master-data1 + cp -r var/master-data/* var/master-data1 if [ $? != 0 ]; then error "Could not install master test DBs 1" exit 1 @@ -1129,7 +1132,9 @@ mysql_install_db () { fi $ECHO "Installing Slave Databases" $RM -rf $SLAVE_MYDDIR $MY_LOG_DIR/* - $INSTALL_DB -slave +# $INSTALL_DB -slave + mkdir var/slave-data + cp -r var/master-data/* var/slave-data if [ $? != 0 ]; then error "Could not install slave test DBs" exit 1 @@ -1345,7 +1350,6 @@ start_master() --innodb_data_file_path=ibdata1:128M:autoextend \ --open-files-limit=1024 \ --log-bin-trust-function-creators \ - --loose-binlog-show-xid=0 \ $MASTER_40_ARGS \ $SMALL_SERVER \ $MASTER_MYSQLD_BINLOG_OPT \ @@ -1369,7 +1373,6 @@ start_master() --language=$LANGUAGE \ --innodb_data_file_path=ibdata1:128M:autoextend \ --log-bin-trust-function-creators \ - --loose-binlog-show-xid=0 \ $MASTER_40_ARGS \ $SMALL_SERVER \ $MASTER_MYSQLD_BINLOG_OPT \ @@ -1542,7 +1545,6 @@ start_slave() --master-retry-count=10 \ -O slave_net_timeout=10 \ --log-bin-trust-function-creators \ - --loose-binlog-show-xid=0 \ $SMALL_SERVER \ $SLAVE_MYSQLD_BINLOG_OPT \ $EXTRA_SLAVE_MYSQLD_OPT $EXTRA_SLAVE_OPT \ @@ -2158,6 +2160,7 @@ then # Remove files that can cause problems $RM -rf $MYSQL_TEST_DIR/var/ndbcluster + $RM -rf $MYSQL_TEST_DIR/var/tmp/snapshot* $RM -f $MYSQL_TEST_DIR/var/run/* $MYSQL_TEST_DIR/var/tmp/* # Remove old berkeley db log files that can confuse the server diff --git a/mysql-test/r/analyze.result b/mysql-test/r/analyze.result index fc267cb598d..7b476c3cca2 100644 --- a/mysql-test/r/analyze.result +++ b/mysql-test/r/analyze.result @@ -46,6 +46,7 @@ Field_name Min_value Max_value Min_length Max_length Empties_or_zeros Nulls Avg_ execute stmt1; Field_name Min_value Max_value Min_length Max_length Empties_or_zeros Nulls Avg_value_or_avg_length Std Optimal_fieldtype deallocate prepare stmt1; +drop table t1; create temporary table t1(a int, index(a)); insert into t1 values('1'),('2'),('3'),('4'),('5'); analyze table t1; diff --git a/mysql-test/r/binlog_row_mix_innodb_myisam.result b/mysql-test/r/binlog_row_mix_innodb_myisam.result index 078a95d5abd..84959684c42 100644 --- a/mysql-test/r/binlog_row_mix_innodb_myisam.result +++ b/mysql-test/r/binlog_row_mix_innodb_myisam.result @@ -234,8 +234,6 @@ commit; begin; create temporary table ti (a int) engine=innodb; rollback; -Warnings: -Warning 1196 Some non-transactional changed tables couldn't be rolled back insert into ti values(1); set autocommit=0; create temporary table t1 (a int) engine=myisam; @@ -285,6 +283,162 @@ master-bin.000001 1260 Write_rows 1 # table_id: # flags: STMT_END_F master-bin.000001 1294 Query 1 # use `test`; create table t2 (n int) engine=innodb do release_lock("lock1"); drop table t0,t2; +set autocommit=0; +CREATE TABLE t1 (a int, b int) engine=myisam; +reset master; +INSERT INTO t1 values (1,1),(1,2); +CREATE TABLE t2 (primary key (a)) engine=innodb select * from t1; +ERROR 23000: Duplicate entry '1' for key 'PRIMARY' +DROP TABLE if exists t2; +Warnings: +Note 1051 Unknown table 't2' +INSERT INTO t1 values (3,3); +CREATE TEMPORARY TABLE t2 (primary key (a)) engine=innodb select * from t1; +ERROR 23000: Duplicate entry '1' for key 'PRIMARY' +ROLLBACK; +Warnings: +Warning 1196 Some non-transactional changed tables couldn't be rolled back +DROP TABLE IF EXISTS t2; +Warnings: +Note 1051 Unknown table 't2' +CREATE TABLE t2 (a int, b int, primary key (a)) engine=innodb; +INSERT INTO t1 VALUES (4,4); +CREATE TABLE IF NOT EXISTS t2 (primary key (a)) engine=innodb select * from t1; +ERROR 23000: Duplicate entry '1' for key 'PRIMARY' +SELECT * from t2; +a b +TRUNCATE table t2; +INSERT INTO t1 VALUES (5,5); +INSERT INTO t2 select * from t1; +ERROR 23000: Duplicate entry '1' for key 'PRIMARY' +SELECT * FROM t2; +a b +DROP TABLE t2; +INSERT INTO t1 values (6,6); +CREATE TEMPORARY TABLE t2 (a int, b int, primary key (a)) engine=innodb ; +INSERT INTO t1 values (7,7); +ROLLBACK; +Warnings: +Warning 1196 Some non-transactional changed tables couldn't be rolled back +INSERT INTO t1 values (8,8); +CREATE TEMPORARY TABLE IF NOT EXISTS t2 (primary key (a)) engine=innodb select * from t1; +ERROR 23000: Duplicate entry '1' for key 'PRIMARY' +COMMIT; +INSERT INTO t1 values (9,9); +CREATE TEMPORARY TABLE IF NOT EXISTS t2 (primary key (a)) engine=innodb select * from t1; +ERROR 23000: Duplicate entry '1' for key 'PRIMARY' +ROLLBACK; +Warnings: +Warning 1196 Some non-transactional changed tables couldn't be rolled back +SELECT * from t2; +a b +TRUNCATE table t2; +INSERT INTO t1 values (10,10); +INSERT INTO t2 select * from t1; +ERROR 23000: Duplicate entry '1' for key 'PRIMARY' +SELECT * from t1; +a b +1 1 +1 2 +3 3 +4 4 +5 5 +6 6 +7 7 +8 8 +9 9 +10 10 +INSERT INTO t2 values (100,100); +CREATE TEMPORARY TABLE IF NOT EXISTS t2 (primary key (a)) engine=innodb select * from t1; +ERROR 23000: Duplicate entry '1' for key 'PRIMARY' +COMMIT; +INSERT INTO t2 values (101,101); +CREATE TEMPORARY TABLE IF NOT EXISTS t2 (primary key (a)) engine=innodb select * from t1; +ERROR 23000: Duplicate entry '1' for key 'PRIMARY' +ROLLBACK; +SELECT * from t2; +a b +100 100 +DROP TABLE t1,t2; +show binlog events from 102; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 102 Table_map 1 142 table_id: # (test.t1) +master-bin.000001 142 Write_rows 1 189 table_id: # flags: STMT_END_F +master-bin.000001 189 Query 1 257 use `test`; BEGIN +master-bin.000001 257 Query 1 182 use `test`; CREATE TABLE `t2` ( + `a` int(11) NOT NULL DEFAULT '0', + `b` int(11) DEFAULT NULL, + PRIMARY KEY (`a`) +) ENGINE=InnoDB +master-bin.000001 439 Table_map 1 222 table_id: # (test.t2) +master-bin.000001 479 Write_rows 1 260 table_id: # flags: STMT_END_F +master-bin.000001 517 Xid 1 544 COMMIT /* xid= */ +master-bin.000001 544 Query 1 630 use `test`; DROP TABLE if exists t2 +master-bin.000001 630 Table_map 1 670 table_id: # (test.t1) +master-bin.000001 670 Write_rows 1 708 table_id: # flags: STMT_END_F +master-bin.000001 708 Query 1 776 use `test`; BEGIN +master-bin.000001 776 Query 1 192 use `test`; CREATE TEMPORARY TABLE `t2` ( + `a` int(11) NOT NULL DEFAULT '0', + `b` int(11) DEFAULT NULL, + PRIMARY KEY (`a`) +) ENGINE=InnoDB +master-bin.000001 968 Query 1 1039 use `test`; ROLLBACK +master-bin.000001 1039 Query 1 1125 use `test`; DROP TABLE IF EXISTS t2 +master-bin.000001 1125 Query 1 1249 use `test`; CREATE TABLE t2 (a int, b int, primary key (a)) engine=innodb +master-bin.000001 1249 Table_map 1 1289 table_id: # (test.t1) +master-bin.000001 1289 Write_rows 1 1327 table_id: # flags: STMT_END_F +master-bin.000001 1327 Query 1 1395 use `test`; BEGIN +master-bin.000001 1395 Query 1 182 use `test`; CREATE TABLE `t2` ( + `a` int(11) NOT NULL DEFAULT '0', + `b` int(11) DEFAULT NULL, + PRIMARY KEY (`a`) +) ENGINE=InnoDB +master-bin.000001 1577 Table_map 1 222 table_id: # (test.t2) +master-bin.000001 1617 Write_rows 1 260 table_id: # flags: STMT_END_F +master-bin.000001 1655 Xid 1 1682 COMMIT /* xid= */ +master-bin.000001 1682 Query 1 80 use `test`; TRUNCATE table t2 +master-bin.000001 1762 Xid 1 1789 COMMIT /* xid= */ +master-bin.000001 1789 Table_map 1 1829 table_id: # (test.t1) +master-bin.000001 1829 Write_rows 1 1867 table_id: # flags: STMT_END_F +master-bin.000001 1867 Query 1 1935 use `test`; BEGIN +master-bin.000001 1935 Table_map 1 40 table_id: # (test.t2) +master-bin.000001 1975 Write_rows 1 78 table_id: # flags: STMT_END_F +master-bin.000001 2013 Xid 1 2040 COMMIT /* xid= */ +master-bin.000001 2040 Query 1 2116 use `test`; DROP TABLE t2 +master-bin.000001 2116 Table_map 1 2156 table_id: # (test.t1) +master-bin.000001 2156 Write_rows 1 2194 table_id: # flags: STMT_END_F +master-bin.000001 2194 Table_map 1 2234 table_id: # (test.t1) +master-bin.000001 2234 Write_rows 1 2272 table_id: # flags: STMT_END_F +master-bin.000001 2272 Table_map 1 2312 table_id: # (test.t1) +master-bin.000001 2312 Write_rows 1 2350 table_id: # flags: STMT_END_F +master-bin.000001 2350 Query 1 2418 use `test`; BEGIN +master-bin.000001 2418 Query 1 192 use `test`; CREATE TEMPORARY TABLE `t2` ( + `a` int(11) NOT NULL DEFAULT '0', + `b` int(11) DEFAULT NULL, + PRIMARY KEY (`a`) +) ENGINE=InnoDB +master-bin.000001 2610 Xid 1 2637 COMMIT /* xid= */ +master-bin.000001 2637 Table_map 1 2677 table_id: # (test.t1) +master-bin.000001 2677 Write_rows 1 2715 table_id: # flags: STMT_END_F +master-bin.000001 2715 Query 1 2783 use `test`; BEGIN +master-bin.000001 2783 Query 1 192 use `test`; CREATE TEMPORARY TABLE `t2` ( + `a` int(11) NOT NULL DEFAULT '0', + `b` int(11) DEFAULT NULL, + PRIMARY KEY (`a`) +) ENGINE=InnoDB +master-bin.000001 2975 Query 1 3046 use `test`; ROLLBACK +master-bin.000001 3046 Query 1 80 use `test`; TRUNCATE table t2 +master-bin.000001 3126 Xid 1 3153 COMMIT /* xid= */ +master-bin.000001 3153 Table_map 1 3193 table_id: # (test.t1) +master-bin.000001 3193 Write_rows 1 3231 table_id: # flags: STMT_END_F +master-bin.000001 3231 Query 1 3299 use `test`; BEGIN +master-bin.000001 3299 Query 1 192 use `test`; CREATE TEMPORARY TABLE `t2` ( + `a` int(11) NOT NULL DEFAULT '0', + `b` int(11) DEFAULT NULL, + PRIMARY KEY (`a`) +) ENGINE=InnoDB +master-bin.000001 3491 Xid 1 3518 COMMIT /* xid= */ +master-bin.000001 3518 Query 1 3622 use `test`; DROP TABLE `t1` /* generated by server */ reset master; create table t1 (a int) engine=innodb; create table t2 (a int) engine=myisam; diff --git a/mysql-test/r/binlog_stm_mix_innodb_myisam.result b/mysql-test/r/binlog_stm_mix_innodb_myisam.result index c5abcff4246..e836cae0b15 100644 --- a/mysql-test/r/binlog_stm_mix_innodb_myisam.result +++ b/mysql-test/r/binlog_stm_mix_innodb_myisam.result @@ -209,8 +209,6 @@ commit; begin; create temporary table ti (a int) engine=innodb; rollback; -Warnings: -Warning 1196 Some non-transactional changed tables couldn't be rolled back insert into ti values(1); set autocommit=0; create temporary table t1 (a int) engine=myisam; @@ -256,6 +254,107 @@ master-bin.000001 1654 Query 1 # use `test`; create table t2 (n int) engine=inno master-bin.000001 1754 Query 1 # use `test`; DROP /*!40005 TEMPORARY */ TABLE IF EXISTS `test`.`t1`,`test`.`ti` do release_lock("lock1"); drop table t0,t2; +set autocommit=0; +CREATE TABLE t1 (a int, b int) engine=myisam; +reset master; +INSERT INTO t1 values (1,1),(1,2); +CREATE TABLE t2 (primary key (a)) engine=innodb select * from t1; +ERROR 23000: Duplicate entry '1' for key 'PRIMARY' +DROP TABLE if exists t2; +Warnings: +Note 1051 Unknown table 't2' +INSERT INTO t1 values (3,3); +CREATE TEMPORARY TABLE t2 (primary key (a)) engine=innodb select * from t1; +ERROR 23000: Duplicate entry '1' for key 'PRIMARY' +ROLLBACK; +Warnings: +Warning 1196 Some non-transactional changed tables couldn't be rolled back +DROP TABLE IF EXISTS t2; +Warnings: +Note 1051 Unknown table 't2' +CREATE TABLE t2 (a int, b int, primary key (a)) engine=innodb; +INSERT INTO t1 VALUES (4,4); +CREATE TABLE IF NOT EXISTS t2 (primary key (a)) engine=innodb select * from t1; +ERROR 23000: Duplicate entry '1' for key 'PRIMARY' +SELECT * from t2; +a b +TRUNCATE table t2; +INSERT INTO t1 VALUES (5,5); +INSERT INTO t2 select * from t1; +ERROR 23000: Duplicate entry '1' for key 'PRIMARY' +SELECT * FROM t2; +a b +DROP TABLE t2; +INSERT INTO t1 values (6,6); +CREATE TEMPORARY TABLE t2 (a int, b int, primary key (a)) engine=innodb ; +INSERT INTO t1 values (7,7); +ROLLBACK; +Warnings: +Warning 1196 Some non-transactional changed tables couldn't be rolled back +INSERT INTO t1 values (8,8); +CREATE TEMPORARY TABLE IF NOT EXISTS t2 (primary key (a)) engine=innodb select * from t1; +ERROR 23000: Duplicate entry '1' for key 'PRIMARY' +COMMIT; +INSERT INTO t1 values (9,9); +CREATE TEMPORARY TABLE IF NOT EXISTS t2 (primary key (a)) engine=innodb select * from t1; +ERROR 23000: Duplicate entry '1' for key 'PRIMARY' +ROLLBACK; +Warnings: +Warning 1196 Some non-transactional changed tables couldn't be rolled back +SELECT * from t2; +a b +TRUNCATE table t2; +INSERT INTO t1 values (10,10); +INSERT INTO t2 select * from t1; +ERROR 23000: Duplicate entry '1' for key 'PRIMARY' +SELECT * from t1; +a b +1 1 +1 2 +3 3 +4 4 +5 5 +6 6 +7 7 +8 8 +9 9 +10 10 +INSERT INTO t2 values (100,100); +CREATE TEMPORARY TABLE IF NOT EXISTS t2 (primary key (a)) engine=innodb select * from t1; +ERROR 23000: Duplicate entry '1' for key 'PRIMARY' +COMMIT; +INSERT INTO t2 values (101,101); +CREATE TEMPORARY TABLE IF NOT EXISTS t2 (primary key (a)) engine=innodb select * from t1; +ERROR 23000: Duplicate entry '1' for key 'PRIMARY' +ROLLBACK; +SELECT * from t2; +a b +100 100 +DROP TABLE t1,t2; +show binlog events from 102; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 102 Query 1 198 use `test`; INSERT INTO t1 values (1,1),(1,2) +master-bin.000001 198 Query 1 284 use `test`; DROP TABLE if exists t2 +master-bin.000001 284 Query 1 374 use `test`; INSERT INTO t1 values (3,3) +master-bin.000001 374 Query 1 460 use `test`; DROP TABLE IF EXISTS t2 +master-bin.000001 460 Query 1 584 use `test`; CREATE TABLE t2 (a int, b int, primary key (a)) engine=innodb +master-bin.000001 584 Query 1 674 use `test`; INSERT INTO t1 VALUES (4,4) +master-bin.000001 674 Query 1 80 use `test`; TRUNCATE table t2 +master-bin.000001 754 Xid 1 781 COMMIT /* xid= */ +master-bin.000001 781 Query 1 871 use `test`; INSERT INTO t1 VALUES (5,5) +master-bin.000001 871 Query 1 947 use `test`; DROP TABLE t2 +master-bin.000001 947 Query 1 1037 use `test`; INSERT INTO t1 values (6,6) +master-bin.000001 1037 Query 1 1171 use `test`; CREATE TEMPORARY TABLE t2 (a int, b int, primary key (a)) engine=innodb +master-bin.000001 1171 Query 1 1261 use `test`; INSERT INTO t1 values (7,7) +master-bin.000001 1261 Query 1 1351 use `test`; INSERT INTO t1 values (8,8) +master-bin.000001 1351 Query 1 1441 use `test`; INSERT INTO t1 values (9,9) +master-bin.000001 1441 Query 1 80 use `test`; TRUNCATE table t2 +master-bin.000001 1521 Xid 1 1548 COMMIT /* xid= */ +master-bin.000001 1548 Query 1 1640 use `test`; INSERT INTO t1 values (10,10) +master-bin.000001 1640 Query 1 1708 use `test`; BEGIN +master-bin.000001 1708 Query 1 94 use `test`; INSERT INTO t2 values (100,100) +master-bin.000001 1802 Xid 1 1829 COMMIT /* xid= */ +master-bin.000001 1829 Query 1 1908 use `test`; DROP TABLE t1,t2 reset master; create table t1 (a int) engine=innodb; create table t2 (a int) engine=myisam; diff --git a/mysql-test/r/compress.result b/mysql-test/r/compress.result index efcafbbe736..691bd56474b 100644 --- a/mysql-test/r/compress.result +++ b/mysql-test/r/compress.result @@ -145,9 +145,9 @@ explain select fld3 from t2 use index (fld1,fld3) where fld3 = 'honeysuckle'; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t2 ref fld3 fld3 30 const 1 Using where; Using index explain select fld3 from t2 ignore index (fld3,not_used); -ERROR 42000: Key column 'not_used' doesn't exist in table +ERROR 42000: Key 'not_used' doesn't exist in table 't2' explain select fld3 from t2 use index (not_used); -ERROR 42000: Key column 'not_used' doesn't exist in table +ERROR 42000: Key 'not_used' doesn't exist in table 't2' select t2.fld3 from t2 where fld3 >= 'honeysuckle' and fld3 <= 'honoring' order by fld3; fld3 honeysuckle diff --git a/mysql-test/r/create.result b/mysql-test/r/create.result index d45960cf787..6f8c319eb6f 100644 --- a/mysql-test/r/create.result +++ b/mysql-test/r/create.result @@ -266,6 +266,7 @@ select * from t1; 0 1 2 0 0 1 drop table t1; +flush status; create table t1 (a int not null, b int, primary key (a)); insert into t1 values (1,1); create table if not exists t1 select 2; @@ -281,6 +282,13 @@ Warnings: Note 1050 Table 't1' already exists create table if not exists t1 select 3 as 'a',3 as 'b'; ERROR 23000: Duplicate entry '3' for key 'PRIMARY' +show warnings; +Level Code Message +Note 1050 Table 't1' already exists +Error 1062 Duplicate entry '3' for key 'PRIMARY' +show status like "Opened_tables"; +Variable_name Value +Opened_tables 2 select * from t1; a b 1 1 @@ -778,3 +786,41 @@ Warnings: Warning 1071 Specified key was too long; max key length is 765 bytes insert into t1 values('aaa'); drop table t1; +CREATE TABLE t1 (a int, b int); +insert into t1 values (1,1),(1,2); +CREATE TABLE t2 (primary key (a)) select * from t1; +ERROR 23000: Duplicate entry '1' for key 'PRIMARY' +drop table if exists t2; +Warnings: +Note 1051 Unknown table 't2' +CREATE TEMPORARY TABLE t2 (primary key (a)) select * from t1; +ERROR 23000: Duplicate entry '1' for key 'PRIMARY' +drop table if exists t2; +Warnings: +Note 1051 Unknown table 't2' +CREATE TABLE t2 (a int, b int, primary key (a)); +CREATE TABLE IF NOT EXISTS t2 (primary key (a)) select * from t1; +ERROR 23000: Duplicate entry '1' for key 'PRIMARY' +SELECT * from t2; +a b +1 1 +TRUNCATE table t2; +INSERT INTO t2 select * from t1; +ERROR 23000: Duplicate entry '1' for key 'PRIMARY' +SELECT * from t2; +a b +1 1 +drop table t2; +CREATE TEMPORARY TABLE t2 (a int, b int, primary key (a)); +CREATE TEMPORARY TABLE IF NOT EXISTS t2 (primary key (a)) select * from t1; +ERROR 23000: Duplicate entry '1' for key 'PRIMARY' +SELECT * from t2; +a b +1 1 +TRUNCATE table t2; +INSERT INTO t2 select * from t1; +ERROR 23000: Duplicate entry '1' for key 'PRIMARY' +SELECT * from t2; +a b +1 1 +drop table t1,t2; diff --git a/mysql-test/r/events.result b/mysql-test/r/events.result index d02a2af3c9f..77280f99b7c 100644 --- a/mysql-test/r/events.result +++ b/mysql-test/r/events.result @@ -254,7 +254,7 @@ event CREATE TABLE `event` ( `on_completion` enum('DROP','PRESERVE') NOT NULL DEFAULT 'DROP', `sql_mode` set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES','IGNORE_SPACE','NOT_USED','ONLY_FULL_GROUP_BY','NO_UNSIGNED_SUBTRACTION','NO_DIR_IN_CREATE','POSTGRESQL','ORACLE','MSSQL','DB2','MAXDB','NO_KEY_OPTIONS','NO_TABLE_OPTIONS','NO_FIELD_OPTIONS','MYSQL323','MYSQL40','ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES','STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','INVALID_DATES','ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NO_AUTO_CREATE_USER','HIGH_NOT_PRECEDENCE') NOT NULL DEFAULT '', `comment` char(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '', - PRIMARY KEY (`definer`,`db`,`name`) + PRIMARY KEY (`db`,`name`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='Events' SELECT event_name FROM INFORMATION_SCHEMA.EVENTS; ERROR HY000: Cannot load from mysql.event. Table probably corrupted. See error log. @@ -280,84 +280,6 @@ SHOW EVENTS; Db Name Definer Type Execute at Interval value Interval field Starts Ends Status events_test intact_check root@localhost RECURRING NULL 10 HOUR # # ENABLED DROP EVENT intact_check; -create event one_event on schedule every 10 second do select 123; -SHOW EVENTS; -Db Name Definer Type Execute at Interval value Interval field Starts Ends Status -events_test one_event root@localhost RECURRING NULL 10 SECOND # # ENABLED -SELECT EVENT_CATALOG, EVENT_SCHEMA, EVENT_NAME, DEFINER, EVENT_BODY, EVENT_TYPE, EXECUTE_AT, INTERVAL_VALUE, INTERVAL_FIELD, STATUS,ON_COMPLETION, EVENT_COMMENT from information_schema.events; -EVENT_CATALOG EVENT_SCHEMA EVENT_NAME DEFINER EVENT_BODY EVENT_TYPE EXECUTE_AT INTERVAL_VALUE INTERVAL_FIELD STATUS ON_COMPLETION EVENT_COMMENT -NULL events_test one_event root@localhost select 123 RECURRING NULL 10 SECOND ENABLED NOT PRESERVE -CREATE DATABASE events_test2; -CREATE USER ev_test@localhost; -GRANT ALL ON events_test.* to ev_test@localhost; -GRANT ALL on events_test2.* to ev_test@localhost; -REVOKE EVENT ON events_test2.* FROM ev_test@localhost; -REVOKE PROCESS on *.* from ev_test@localhost; -select "NEW CONNECTION"; -NEW CONNECTION -NEW CONNECTION -SELECT USER(), DATABASE(); -USER() DATABASE() -ev_test@localhost events_test2 -SHOW GRANTS; -Grants for ev_test@localhost -GRANT USAGE ON *.* TO 'ev_test'@'localhost' -GRANT ALL PRIVILEGES ON `events_test`.* TO 'ev_test'@'localhost' -GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, REFERENCES, INDEX, ALTER, CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, TRIGGER ON `events_test2`.* TO 'ev_test'@'localhost' -"Here comes an error:"; -SHOW EVENTS; -ERROR 42000: Access denied for user 'ev_test'@'localhost' to database 'events_test2' -USE events_test; -"Now the list should be empty:"; -SHOW EVENTS; -Db Name Definer Type Execute at Interval value Interval field Starts Ends Status -select concat("Let's create some new events from the name of ",user()); -concat("Let's create some new events from the name of ",user()) -Let's create some new events from the name of ev_test@localhost -create event one_event on schedule every 20 second do select 123; -create event two_event on schedule every 20 second on completion not preserve comment "two event" do select 123; -create event three_event on schedule every 20 second on completion preserve comment "three event" do select 123; -"Now we should see 3 events:"; -SHOW EVENTS; -Db Name Definer Type Execute at Interval value Interval field Starts Ends Status -events_test one_event ev_test@localhost RECURRING NULL 20 SECOND # # ENABLED -events_test three_event ev_test@localhost RECURRING NULL 20 SECOND # # ENABLED -events_test two_event ev_test@localhost RECURRING NULL 20 SECOND # # ENABLED -"This should show us only 3 events:"; -SHOW EVENTS; -Db Name Definer Type Execute at Interval value Interval field Starts Ends Status -events_test one_event ev_test@localhost RECURRING NULL 20 SECOND # # ENABLED -events_test three_event ev_test@localhost RECURRING NULL 20 SECOND # # ENABLED -events_test two_event ev_test@localhost RECURRING NULL 20 SECOND # # ENABLED -"This should show us only 2 events:"; -SHOW EVENTS LIKE 't%event'; -Db Name Definer Type Execute at Interval value Interval field Starts Ends Status -events_test three_event ev_test@localhost RECURRING NULL 20 SECOND # # ENABLED -events_test two_event ev_test@localhost RECURRING NULL 20 SECOND # # ENABLED -"This should show us no events:"; -SHOW EVENTS FROM test LIKE '%'; -Db Name Definer Type Execute at Interval value Interval field Starts Ends Status -DROP DATABASE events_test2; -"should see 1 event:"; -SHOW EVENTS; -Db Name Definer Type Execute at Interval value Interval field Starts Ends Status -events_test one_event root@localhost RECURRING NULL 10 SECOND # # ENABLED -"we should see 4 events now:"; -SHOW EVENTS; -Db Name Definer Type Execute at Interval value Interval field Starts Ends Status -events_test one_event root@localhost RECURRING NULL 10 SECOND # # ENABLED -SELECT EVENT_CATALOG, EVENT_SCHEMA, EVENT_NAME, DEFINER, EVENT_BODY, EVENT_TYPE, EXECUTE_AT, INTERVAL_VALUE, INTERVAL_FIELD, STATUS,ON_COMPLETION, EVENT_COMMENT from information_schema.events; -EVENT_CATALOG EVENT_SCHEMA EVENT_NAME DEFINER EVENT_BODY EVENT_TYPE EXECUTE_AT INTERVAL_VALUE INTERVAL_FIELD STATUS ON_COMPLETION EVENT_COMMENT -NULL events_test one_event ev_test@localhost select 123 RECURRING NULL 20 SECOND ENABLED NOT PRESERVE -NULL events_test three_event ev_test@localhost select 123 RECURRING NULL 20 SECOND ENABLED PRESERVE three event -NULL events_test two_event ev_test@localhost select 123 RECURRING NULL 20 SECOND ENABLED NOT PRESERVE two event -NULL events_test one_event root@localhost select 123 RECURRING NULL 10 SECOND ENABLED NOT PRESERVE -drop event one_event; -drop event two_event; -drop event three_event; -drop user ev_test@localhost; -drop event one_event; -"Sleep a bit so the server closes the second connection" create event e_26 on schedule at '2017-01-01 00:00:00' disable do set @a = 5; select db, name, body, definer, convert_tz(execute_at, 'UTC', 'SYSTEM'), on_completion from mysql.event; db name body definer convert_tz(execute_at, 'UTC', 'SYSTEM') on_completion @@ -452,9 +374,6 @@ select 2; select event_schema, event_name, definer, event_body from information_schema.events where event_name='white_space'; event_schema event_name definer event_body events_test white_space root@localhost select 2 -select event_schema, event_name, definer, event_body from information_schema.events where event_name='white_space'; -event_schema event_name definer event_body -events_test white_space root@localhost select 2 drop event white_space; create event white_space on schedule every 10 hour disable do select 3; select event_schema, event_name, definer, event_body from information_schema.events where event_name='white_space'; diff --git a/mysql-test/r/events_grant.result b/mysql-test/r/events_grant.result new file mode 100644 index 00000000000..6c140f91eaa --- /dev/null +++ b/mysql-test/r/events_grant.result @@ -0,0 +1,121 @@ +CREATE DATABASE IF NOT EXISTS events_test; +use events_test; +CREATE EVENT one_event ON SCHEDULE EVERY 10 SECOND DO SELECT 123; +SHOW EVENTS; +Db Name Definer Type Execute at Interval value Interval field Starts Ends Status +events_test one_event root@localhost RECURRING NULL 10 SECOND # # ENABLED +SELECT EVENT_CATALOG, EVENT_SCHEMA, EVENT_NAME, DEFINER, EVENT_BODY, EVENT_TYPE, EXECUTE_AT, INTERVAL_VALUE, INTERVAL_FIELD, STATUS,ON_COMPLETION, EVENT_COMMENT from information_schema.events; +EVENT_CATALOG EVENT_SCHEMA EVENT_NAME DEFINER EVENT_BODY EVENT_TYPE EXECUTE_AT INTERVAL_VALUE INTERVAL_FIELD STATUS ON_COMPLETION EVENT_COMMENT +NULL events_test one_event root@localhost SELECT 123 RECURRING NULL 10 SECOND ENABLED NOT PRESERVE +CREATE DATABASE events_test2; +CREATE USER ev_test@localhost; +GRANT ALL ON events_test.* to ev_test@localhost; +GRANT ALL ON events_test2.* to ev_test@localhost; +REVOKE EVENT ON events_test2.* FROM ev_test@localhost; +select "NEW CONNECTION"; +NEW CONNECTION +NEW CONNECTION +SELECT USER(), DATABASE(); +USER() DATABASE() +ev_test@localhost events_test2 +SHOW GRANTS; +Grants for ev_test@localhost +GRANT USAGE ON *.* TO 'ev_test'@'localhost' +GRANT ALL PRIVILEGES ON `events_test`.* TO 'ev_test'@'localhost' +GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, REFERENCES, INDEX, ALTER, CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, TRIGGER ON `events_test2`.* TO 'ev_test'@'localhost' +"Here comes an error:"; +SHOW EVENTS; +ERROR 42000: Access denied for user 'ev_test'@'localhost' to database 'events_test2' +USE events_test; +"We should see one event"; +SHOW EVENTS; +Db Name Definer Type Execute at Interval value Interval field Starts Ends Status +events_test one_event root@localhost RECURRING NULL 10 SECOND # # ENABLED +SELECT CONCAT("Let's create some new events from the name of ", USER()); +CONCAT("Let's create some new events from the name of ", USER()) +Let's create some new events from the name of ev_test@localhost +CREATE EVENT one_event ON SCHEDULE EVERY 20 SECOND DO SELECT 123; +ERROR HY000: Event 'one_event' already exists +CREATE EVENT two_event ON SCHEDULE EVERY 20 SECOND ON COMPLETION NOT PRESERVE COMMENT "two event" DO SELECT 123; +CREATE EVENT three_event ON SCHEDULE EVERY 20 SECOND ON COMPLETION PRESERVE COMMENT "three event" DO SELECT 123; +"Now we should see 3 events:"; +SHOW EVENTS; +Db Name Definer Type Execute at Interval value Interval field Starts Ends Status +events_test one_event root@localhost RECURRING NULL 10 SECOND # # ENABLED +events_test three_event ev_test@localhost RECURRING NULL 20 SECOND # # ENABLED +events_test two_event ev_test@localhost RECURRING NULL 20 SECOND # # ENABLED +"This should show us only 2 events:"; +SHOW EVENTS LIKE 't%event'; +Db Name Definer Type Execute at Interval value Interval field Starts Ends Status +events_test three_event ev_test@localhost RECURRING NULL 20 SECOND # # ENABLED +events_test two_event ev_test@localhost RECURRING NULL 20 SECOND # # ENABLED +"This should show us no events:"; +SHOW EVENTS FROM test LIKE '%'; +Db Name Definer Type Execute at Interval value Interval field Starts Ends Status +GRANT EVENT ON events_test2.* TO ev_test@localhost; +USE events_test2; +CREATE EVENT four_event ON SCHEDULE EVERY 20 SECOND DO SELECT 42; +USE events_test; +"We should see 4 events : one_event, two_event, three_event & four_event" +SELECT EVENT_CATALOG, EVENT_SCHEMA, EVENT_NAME, DEFINER, EVENT_BODY, EVENT_TYPE, EXECUTE_AT, INTERVAL_VALUE, INTERVAL_FIELD, STATUS,ON_COMPLETION, EVENT_COMMENT FROM INFORMATION_SCHEMA.EVENTS; +EVENT_CATALOG EVENT_SCHEMA EVENT_NAME DEFINER EVENT_BODY EVENT_TYPE EXECUTE_AT INTERVAL_VALUE INTERVAL_FIELD STATUS ON_COMPLETION EVENT_COMMENT +NULL events_test one_event root@localhost SELECT 123 RECURRING NULL 10 SECOND ENABLED NOT PRESERVE +NULL events_test two_event ev_test@localhost SELECT 123 RECURRING NULL 20 SECOND ENABLED NOT PRESERVE two event +NULL events_test three_event ev_test@localhost SELECT 123 RECURRING NULL 20 SECOND ENABLED PRESERVE three event +NULL events_test2 four_event ev_test@localhost SELECT 42 RECURRING NULL 20 SECOND ENABLED NOT PRESERVE +DROP DATABASE events_test2; +"We should see 3 events : one_event, two_event, three_event" +SELECT EVENT_CATALOG, EVENT_SCHEMA, EVENT_NAME, DEFINER, EVENT_BODY, EVENT_TYPE, EXECUTE_AT, INTERVAL_VALUE, INTERVAL_FIELD, STATUS,ON_COMPLETION, EVENT_COMMENT FROM INFORMATION_SCHEMA.EVENTS; +EVENT_CATALOG EVENT_SCHEMA EVENT_NAME DEFINER EVENT_BODY EVENT_TYPE EXECUTE_AT INTERVAL_VALUE INTERVAL_FIELD STATUS ON_COMPLETION EVENT_COMMENT +NULL events_test one_event root@localhost SELECT 123 RECURRING NULL 10 SECOND ENABLED NOT PRESERVE +NULL events_test two_event ev_test@localhost SELECT 123 RECURRING NULL 20 SECOND ENABLED NOT PRESERVE two event +NULL events_test three_event ev_test@localhost SELECT 123 RECURRING NULL 20 SECOND ENABLED PRESERVE three event +CREATE DATABASE events_test2; +USE events_test2; +CREATE EVENT five_event ON SCHEDULE EVERY 20 SECOND DO SELECT 42; +"Should see 4 events - one, two, three & five" +SELECT EVENT_CATALOG, EVENT_SCHEMA, EVENT_NAME, DEFINER, EVENT_BODY, EVENT_TYPE, EXECUTE_AT, INTERVAL_VALUE, INTERVAL_FIELD, STATUS,ON_COMPLETION, EVENT_COMMENT FROM INFORMATION_SCHEMA.EVENTS; +EVENT_CATALOG EVENT_SCHEMA EVENT_NAME DEFINER EVENT_BODY EVENT_TYPE EXECUTE_AT INTERVAL_VALUE INTERVAL_FIELD STATUS ON_COMPLETION EVENT_COMMENT +NULL events_test one_event root@localhost SELECT 123 RECURRING NULL 10 SECOND ENABLED NOT PRESERVE +NULL events_test two_event ev_test@localhost SELECT 123 RECURRING NULL 20 SECOND ENABLED NOT PRESERVE two event +NULL events_test three_event ev_test@localhost SELECT 123 RECURRING NULL 20 SECOND ENABLED PRESERVE three event +NULL events_test2 five_event root@localhost SELECT 42 RECURRING NULL 20 SECOND ENABLED NOT PRESERVE +REVOKE EVENT ON events_test2.* FROM ev_test@localhost; +USE test; +"Should see 3 events - one, two & three" +SELECT EVENT_CATALOG, EVENT_SCHEMA, EVENT_NAME, DEFINER, EVENT_BODY, EVENT_TYPE, EXECUTE_AT, INTERVAL_VALUE, INTERVAL_FIELD, STATUS,ON_COMPLETION, EVENT_COMMENT FROM INFORMATION_SCHEMA.EVENTS; +EVENT_CATALOG EVENT_SCHEMA EVENT_NAME DEFINER EVENT_BODY EVENT_TYPE EXECUTE_AT INTERVAL_VALUE INTERVAL_FIELD STATUS ON_COMPLETION EVENT_COMMENT +NULL events_test one_event root@localhost SELECT 123 RECURRING NULL 10 SECOND ENABLED NOT PRESERVE +NULL events_test two_event ev_test@localhost SELECT 123 RECURRING NULL 20 SECOND ENABLED NOT PRESERVE two event +NULL events_test three_event ev_test@localhost SELECT 123 RECURRING NULL 20 SECOND ENABLED PRESERVE three event +"Let's test ALTER EVENT which changes the definer" +USE events_test; +ALTER EVENT one_event ON SCHEDULE EVERY 10 SECOND; +"The definer should be ev_test@localhost" +SELECT EVENT_CATALOG, EVENT_SCHEMA, EVENT_NAME, DEFINER, EVENT_BODY, EVENT_TYPE, EXECUTE_AT, INTERVAL_VALUE, INTERVAL_FIELD, STATUS,ON_COMPLETION, EVENT_COMMENT FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_NAME='one_event'; +EVENT_CATALOG EVENT_SCHEMA EVENT_NAME DEFINER EVENT_BODY EVENT_TYPE EXECUTE_AT INTERVAL_VALUE INTERVAL_FIELD STATUS ON_COMPLETION EVENT_COMMENT +NULL events_test one_event ev_test@localhost SELECT 123 RECURRING NULL 10 SECOND ENABLED NOT PRESERVE +USE events_test; +ALTER EVENT one_event COMMENT "comment"; +"The definer should be root@localhost" +SELECT EVENT_CATALOG, EVENT_SCHEMA, EVENT_NAME, DEFINER, EVENT_BODY, EVENT_TYPE, EXECUTE_AT, INTERVAL_VALUE, INTERVAL_FIELD, STATUS,ON_COMPLETION, EVENT_COMMENT FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_NAME='one_event'; +EVENT_CATALOG EVENT_SCHEMA EVENT_NAME DEFINER EVENT_BODY EVENT_TYPE EXECUTE_AT INTERVAL_VALUE INTERVAL_FIELD STATUS ON_COMPLETION EVENT_COMMENT +NULL events_test one_event root@localhost SELECT 123 RECURRING NULL 10 SECOND ENABLED NOT PRESERVE comment +ALTER EVENT one_event DO SELECT 12; +"The definer should be ev_test@localhost" +SELECT EVENT_CATALOG, EVENT_SCHEMA, EVENT_NAME, DEFINER, EVENT_BODY, EVENT_TYPE, EXECUTE_AT, INTERVAL_VALUE, INTERVAL_FIELD, STATUS,ON_COMPLETION, EVENT_COMMENT FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_NAME='one_event'; +EVENT_CATALOG EVENT_SCHEMA EVENT_NAME DEFINER EVENT_BODY EVENT_TYPE EXECUTE_AT INTERVAL_VALUE INTERVAL_FIELD STATUS ON_COMPLETION EVENT_COMMENT +NULL events_test one_event ev_test@localhost SELECT 12 RECURRING NULL 10 SECOND ENABLED NOT PRESERVE comment +"make the definer again root@localhost" +ALTER EVENT one_event COMMENT "new comment"; +"test DROP by another user" +DROP EVENT one_event; +"One event should not be there" +SELECT EVENT_CATALOG, EVENT_SCHEMA, EVENT_NAME, DEFINER, EVENT_BODY, EVENT_TYPE, EXECUTE_AT, INTERVAL_VALUE, INTERVAL_FIELD, STATUS,ON_COMPLETION, EVENT_COMMENT FROM INFORMATION_SCHEMA.EVENTS; +EVENT_CATALOG EVENT_SCHEMA EVENT_NAME DEFINER EVENT_BODY EVENT_TYPE EXECUTE_AT INTERVAL_VALUE INTERVAL_FIELD STATUS ON_COMPLETION EVENT_COMMENT +NULL events_test two_event ev_test@localhost SELECT 123 RECURRING NULL 20 SECOND ENABLED NOT PRESERVE two event +NULL events_test three_event ev_test@localhost SELECT 123 RECURRING NULL 20 SECOND ENABLED PRESERVE three event +NULL events_test2 five_event root@localhost SELECT 42 RECURRING NULL 20 SECOND ENABLED NOT PRESERVE +DROP USER ev_test@localhost; +DROP DATABASE events_test2; +DROP DATABASE events_test; diff --git a/mysql-test/r/events_scheduling.result b/mysql-test/r/events_scheduling.result index aec2053f0e7..eb44751c176 100644 --- a/mysql-test/r/events_scheduling.result +++ b/mysql-test/r/events_scheduling.result @@ -6,7 +6,7 @@ CREATE TABLE table_3(a int); CREATE TABLE table_4(a int); CREATE TABLE T19170(s1 TIMESTAMP); SET GLOBAL event_scheduler=1; -CREATE EVENT E19170 ON SCHEDULE EVERY 1 SECOND DO INSERT INTO T19170 VALUES(CURRENT_TIMESTAMP); +CREATE EVENT E19170 ON SCHEDULE EVERY 2 SECOND DO INSERT INTO T19170 VALUES(CURRENT_TIMESTAMP); CREATE EVENT two_sec ON SCHEDULE EVERY 2 SECOND DO INSERT INTO table_1 VALUES(1); CREATE EVENT start_n_end ON SCHEDULE EVERY 1 SECOND diff --git a/mysql-test/r/explain.result b/mysql-test/r/explain.result index 75e1548cdee..e0d9f5131b4 100644 --- a/mysql-test/r/explain.result +++ b/mysql-test/r/explain.result @@ -24,9 +24,9 @@ explain select * from t1 use key (str,str) where str="foo"; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 const str str 11 const 1 explain select * from t1 use key (str,str,foo) where str="foo"; -ERROR 42000: Key column 'foo' doesn't exist in table +ERROR 42000: Key 'foo' doesn't exist in table 't1' explain select * from t1 ignore key (str,str,foo) where str="foo"; -ERROR 42000: Key column 'foo' doesn't exist in table +ERROR 42000: Key 'foo' doesn't exist in table 't1' drop table t1; explain select 1; id select_type table type possible_keys key key_len ref rows Extra diff --git a/mysql-test/r/federated.result b/mysql-test/r/federated.result index 5f735ebe926..b3d6e10448b 100644 --- a/mysql-test/r/federated.result +++ b/mysql-test/r/federated.result @@ -1601,6 +1601,7 @@ fld_cid fld_name fld_parentid fld_delt 5 Torkel 0 0 DROP TABLE federated.t1; DROP TABLE federated.bug_17377_table; +DROP TABLE federated.t1; DROP TABLE IF EXISTS federated.t1; DROP DATABASE IF EXISTS federated; DROP TABLE IF EXISTS federated.t1; diff --git a/mysql-test/r/flush.result b/mysql-test/r/flush.result index 16c308e3450..a7f5e5e8fec 100644 --- a/mysql-test/r/flush.result +++ b/mysql-test/r/flush.result @@ -48,3 +48,10 @@ lock table t1 read, t2 read, t3 read; flush tables with read lock; unlock tables; drop table t1, t2, t3; +create table t1 (c1 int); +create table t2 (c1 int); +lock table t1 write; + flush tables with read lock; + insert into t2 values(1); +unlock tables; +drop table t1, t2; diff --git a/mysql-test/r/func_gconcat.result b/mysql-test/r/func_gconcat.result index 62b4ddf868a..773efe50749 100644 --- a/mysql-test/r/func_gconcat.result +++ b/mysql-test/r/func_gconcat.result @@ -309,6 +309,12 @@ a grp 1 2 2 4,3 3 5 +select group_concat(c order by (select concat(5-t1.c,group_concat(c order by a)) from t2 where t2.a=t1.a)) as grp from t1; +grp +5,4,3,2 +select group_concat(c order by (select concat(t1.c,group_concat(c)) from t2 where a=t1.a)) as grp from t1; +grp +2,3,4,5 select a,c,(select group_concat(c order by a) from t2 where a=t1.a) as grp from t1 order by grp; a c grp 3 5 3,3 diff --git a/mysql-test/r/func_time.result b/mysql-test/r/func_time.result index 516809b8906..283dd11ca1e 100644 --- a/mysql-test/r/func_time.result +++ b/mysql-test/r/func_time.result @@ -763,6 +763,7 @@ time_format('100:00:00', '%H %k %h %I %l') 100 100 04 04 4 create table t1 (a timestamp default '2005-05-05 01:01:01', b timestamp default '2005-05-05 01:01:01'); +drop function if exists t_slow_sysdate; create function t_slow_sysdate() returns timestamp begin do sleep(2); diff --git a/mysql-test/r/grant.result b/mysql-test/r/grant.result index 4815798d807..82775f0735b 100644 --- a/mysql-test/r/grant.result +++ b/mysql-test/r/grant.result @@ -357,12 +357,12 @@ show grants for grant_user@localhost; Grants for grant_user@localhost GRANT USAGE ON *.* TO 'grant_user'@'localhost' GRANT INSERT (a, d, c, b) ON `test`.`t1` TO 'grant_user'@'localhost' -select Host,Db,User,Table_name,Column_name,Column_priv from mysql.columns_priv; +select Host,Db,User,Table_name,Column_name,Column_priv from mysql.columns_priv order by Column_name; Host Db User Table_name Column_name Column_priv -localhost test grant_user t1 b Insert -localhost test grant_user t1 d Insert localhost test grant_user t1 a Insert +localhost test grant_user t1 b Insert localhost test grant_user t1 c Insert +localhost test grant_user t1 d Insert revoke ALL PRIVILEGES on t1 from grant_user@localhost; show grants for grant_user@localhost; Grants for grant_user@localhost @@ -381,13 +381,27 @@ grant update (a) on mysqltest_1.t1 to mysqltest_3@localhost; grant select (b) on mysqltest_1.t2 to mysqltest_3@localhost; grant select (c) on mysqltest_2.t1 to mysqltest_3@localhost; grant update (d) on mysqltest_2.t2 to mysqltest_3@localhost; -show grants for mysqltest_3@localhost; -Grants for mysqltest_3@localhost -GRANT USAGE ON *.* TO 'mysqltest_3'@'localhost' -GRANT SELECT (b) ON `mysqltest_1`.`t2` TO 'mysqltest_3'@'localhost' -GRANT UPDATE (a) ON `mysqltest_1`.`t1` TO 'mysqltest_3'@'localhost' -GRANT UPDATE (d) ON `mysqltest_2`.`t2` TO 'mysqltest_3'@'localhost' -GRANT SELECT (c) ON `mysqltest_2`.`t1` TO 'mysqltest_3'@'localhost' +SELECT * FROM INFORMATION_SCHEMA.COLUMN_PRIVILEGES +WHERE GRANTEE = '''mysqltest_3''@''localhost''' +ORDER BY TABLE_NAME,COLUMN_NAME,PRIVILEGE_TYPE; +GRANTEE TABLE_CATALOG TABLE_SCHEMA TABLE_NAME COLUMN_NAME PRIVILEGE_TYPE IS_GRANTABLE +'mysqltest_3'@'localhost' NULL mysqltest_1 t1 a UPDATE NO +'mysqltest_3'@'localhost' NULL mysqltest_2 t1 c SELECT NO +'mysqltest_3'@'localhost' NULL mysqltest_1 t2 b SELECT NO +'mysqltest_3'@'localhost' NULL mysqltest_2 t2 d UPDATE NO +SELECT * FROM INFORMATION_SCHEMA.TABLE_PRIVILEGES +WHERE GRANTEE = '''mysqltest_3''@''localhost''' +ORDER BY TABLE_NAME,PRIVILEGE_TYPE; +GRANTEE TABLE_CATALOG TABLE_SCHEMA TABLE_NAME PRIVILEGE_TYPE IS_GRANTABLE +SELECT * from INFORMATION_SCHEMA.SCHEMA_PRIVILEGES +WHERE GRANTEE = '''mysqltest_3''@''localhost''' +ORDER BY TABLE_SCHEMA,PRIVILEGE_TYPE; +GRANTEE TABLE_CATALOG TABLE_SCHEMA PRIVILEGE_TYPE IS_GRANTABLE +SELECT * from INFORMATION_SCHEMA.USER_PRIVILEGES +WHERE GRANTEE = '''mysqltest_3''@''localhost''' + ORDER BY TABLE_CATALOG,PRIVILEGE_TYPE; +GRANTEE TABLE_CATALOG PRIVILEGE_TYPE IS_GRANTABLE +'mysqltest_3'@'localhost' NULL USAGE NO update mysqltest_1.t1, mysqltest_1.t2 set q=10 where b=1; ERROR 42000: UPDATE command denied to user 'mysqltest_3'@'localhost' for column 'q' in table 't1' update mysqltest_1.t2, mysqltest_2.t2 set d=20 where d=1; @@ -593,6 +607,7 @@ insert into tables_priv values ('','test_db','mysqltest_1','test_table','test_gr flush privileges; delete from tables_priv where host = '' and user = 'mysqltest_1'; flush privileges; +use test; set @user123="non-existent"; select * from mysql.db where user=@user123; Host Db User Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Grant_priv References_priv Index_priv Alter_priv Create_tmp_table_priv Lock_tables_priv Create_view_priv Show_view_priv Create_routine_priv Alter_routine_priv Execute_priv Event_priv Trigger_priv @@ -623,7 +638,6 @@ show grants for mysqltest_7@; Grants for mysqltest_7@ GRANT USAGE ON *.* TO 'mysqltest_7'@'' IDENTIFIED BY PASSWORD '*2FB071A056F9BB745219D9C876814231DAF46517' drop user mysqltest_7@; -flush privileges; show grants for mysqltest_7@; ERROR 42000: There is no such grant defined for user 'mysqltest_7' on host '' create database mysqltest; @@ -644,3 +658,214 @@ delete from mysql.db where user='mysqltest1'; delete from mysql.tables_priv where user='mysqltest1'; flush privileges; drop database mysqltest; +use test; +create table t1 (a int); +create table t2 as select * from mysql.user where user=''; +delete from mysql.user where user=''; +flush privileges; +create user mysqltest_8@''; +create user mysqltest_8; +create user mysqltest_8@host8; +create user mysqltest_8@''; +ERROR HY000: Operation CREATE USER failed for 'mysqltest_8'@'' +create user mysqltest_8; +ERROR HY000: Operation CREATE USER failed for 'mysqltest_8'@'%' +create user mysqltest_8@host8; +ERROR HY000: Operation CREATE USER failed for 'mysqltest_8'@'host8' +select user, QUOTE(host) from mysql.user where user="mysqltest_8"; +user QUOTE(host) +mysqltest_8 '' +mysqltest_8 '%' +mysqltest_8 'host8' +Schema privileges +grant select on mysqltest.* to mysqltest_8@''; +show grants for mysqltest_8@''; +Grants for mysqltest_8@ +GRANT USAGE ON *.* TO 'mysqltest_8'@'' +GRANT SELECT ON `mysqltest`.* TO 'mysqltest_8'@'' +grant select on mysqltest.* to mysqltest_8@; +show grants for mysqltest_8@; +Grants for mysqltest_8@ +GRANT USAGE ON *.* TO 'mysqltest_8'@'' +GRANT SELECT ON `mysqltest`.* TO 'mysqltest_8'@'' +grant select on mysqltest.* to mysqltest_8; +show grants for mysqltest_8; +Grants for mysqltest_8@% +GRANT USAGE ON *.* TO 'mysqltest_8'@'%' +GRANT SELECT ON `mysqltest`.* TO 'mysqltest_8'@'%' +select * from information_schema.schema_privileges +where grantee like "'mysqltest_8'%"; +GRANTEE TABLE_CATALOG TABLE_SCHEMA PRIVILEGE_TYPE IS_GRANTABLE +'mysqltest_8'@'%' NULL mysqltest SELECT NO +'mysqltest_8'@'' NULL mysqltest SELECT NO +select * from t1; +a +revoke select on mysqltest.* from mysqltest_8@''; +revoke select on mysqltest.* from mysqltest_8; +show grants for mysqltest_8@''; +Grants for mysqltest_8@ +GRANT USAGE ON *.* TO 'mysqltest_8'@'' +show grants for mysqltest_8; +Grants for mysqltest_8@% +GRANT USAGE ON *.* TO 'mysqltest_8'@'%' +select * from information_schema.schema_privileges +where grantee like "'mysqltest_8'%"; +GRANTEE TABLE_CATALOG TABLE_SCHEMA PRIVILEGE_TYPE IS_GRANTABLE +flush privileges; +show grants for mysqltest_8@''; +Grants for mysqltest_8@ +GRANT USAGE ON *.* TO 'mysqltest_8'@'' +show grants for mysqltest_8@; +Grants for mysqltest_8@ +GRANT USAGE ON *.* TO 'mysqltest_8'@'' +grant select on mysqltest.* to mysqltest_8@''; +flush privileges; +show grants for mysqltest_8@; +Grants for mysqltest_8@ +GRANT USAGE ON *.* TO 'mysqltest_8'@'' +GRANT SELECT ON `mysqltest`.* TO 'mysqltest_8'@'' +revoke select on mysqltest.* from mysqltest_8@''; +flush privileges; +Column privileges +grant update (a) on t1 to mysqltest_8@''; +grant update (a) on t1 to mysqltest_8; +show grants for mysqltest_8@''; +Grants for mysqltest_8@ +GRANT USAGE ON *.* TO 'mysqltest_8'@'' +GRANT UPDATE (a) ON `test`.`t1` TO 'mysqltest_8'@'' +show grants for mysqltest_8; +Grants for mysqltest_8@% +GRANT USAGE ON *.* TO 'mysqltest_8'@'%' +GRANT UPDATE (a) ON `test`.`t1` TO 'mysqltest_8'@'%' +flush privileges; +show grants for mysqltest_8@''; +Grants for mysqltest_8@ +GRANT USAGE ON *.* TO 'mysqltest_8'@'' +GRANT UPDATE (a) ON `test`.`t1` TO 'mysqltest_8'@'' +show grants for mysqltest_8; +Grants for mysqltest_8@% +GRANT USAGE ON *.* TO 'mysqltest_8'@'%' +GRANT UPDATE (a) ON `test`.`t1` TO 'mysqltest_8'@'%' +select * from information_schema.column_privileges; +GRANTEE TABLE_CATALOG TABLE_SCHEMA TABLE_NAME COLUMN_NAME PRIVILEGE_TYPE IS_GRANTABLE +'mysqltest_8'@'%' NULL test t1 a UPDATE NO +'mysqltest_8'@'' NULL test t1 a UPDATE NO +select * from t1; +a +revoke update (a) on t1 from mysqltest_8@''; +revoke update (a) on t1 from mysqltest_8; +show grants for mysqltest_8@''; +Grants for mysqltest_8@ +GRANT USAGE ON *.* TO 'mysqltest_8'@'' +show grants for mysqltest_8; +Grants for mysqltest_8@% +GRANT USAGE ON *.* TO 'mysqltest_8'@'%' +select * from information_schema.column_privileges; +GRANTEE TABLE_CATALOG TABLE_SCHEMA TABLE_NAME COLUMN_NAME PRIVILEGE_TYPE IS_GRANTABLE +flush privileges; +show grants for mysqltest_8@''; +Grants for mysqltest_8@ +GRANT USAGE ON *.* TO 'mysqltest_8'@'' +show grants for mysqltest_8; +Grants for mysqltest_8@% +GRANT USAGE ON *.* TO 'mysqltest_8'@'%' +Table privileges +grant update on t1 to mysqltest_8@''; +grant update on t1 to mysqltest_8; +show grants for mysqltest_8@''; +Grants for mysqltest_8@ +GRANT USAGE ON *.* TO 'mysqltest_8'@'' +GRANT UPDATE ON `test`.`t1` TO 'mysqltest_8'@'' +show grants for mysqltest_8; +Grants for mysqltest_8@% +GRANT USAGE ON *.* TO 'mysqltest_8'@'%' +GRANT UPDATE ON `test`.`t1` TO 'mysqltest_8'@'%' +flush privileges; +show grants for mysqltest_8@''; +Grants for mysqltest_8@ +GRANT USAGE ON *.* TO 'mysqltest_8'@'' +GRANT UPDATE ON `test`.`t1` TO 'mysqltest_8'@'' +show grants for mysqltest_8; +Grants for mysqltest_8@% +GRANT USAGE ON *.* TO 'mysqltest_8'@'%' +GRANT UPDATE ON `test`.`t1` TO 'mysqltest_8'@'%' +select * from information_schema.table_privileges; +GRANTEE TABLE_CATALOG TABLE_SCHEMA TABLE_NAME PRIVILEGE_TYPE IS_GRANTABLE +'mysqltest_8'@'%' NULL test t1 UPDATE NO +'mysqltest_8'@'' NULL test t1 UPDATE NO +select * from t1; +a +revoke update on t1 from mysqltest_8@''; +revoke update on t1 from mysqltest_8; +show grants for mysqltest_8@''; +Grants for mysqltest_8@ +GRANT USAGE ON *.* TO 'mysqltest_8'@'' +show grants for mysqltest_8; +Grants for mysqltest_8@% +GRANT USAGE ON *.* TO 'mysqltest_8'@'%' +select * from information_schema.table_privileges; +GRANTEE TABLE_CATALOG TABLE_SCHEMA TABLE_NAME PRIVILEGE_TYPE IS_GRANTABLE +flush privileges; +show grants for mysqltest_8@''; +Grants for mysqltest_8@ +GRANT USAGE ON *.* TO 'mysqltest_8'@'' +show grants for mysqltest_8; +Grants for mysqltest_8@% +GRANT USAGE ON *.* TO 'mysqltest_8'@'%' +"DROP USER" should clear privileges +grant all privileges on mysqltest.* to mysqltest_8@''; +grant select on mysqltest.* to mysqltest_8@''; +grant update on t1 to mysqltest_8@''; +grant update (a) on t1 to mysqltest_8@''; +grant all privileges on mysqltest.* to mysqltest_8; +show grants for mysqltest_8@''; +Grants for mysqltest_8@ +GRANT USAGE ON *.* TO 'mysqltest_8'@'' +GRANT ALL PRIVILEGES ON `mysqltest`.* TO 'mysqltest_8'@'' +GRANT UPDATE, UPDATE (a) ON `test`.`t1` TO 'mysqltest_8'@'' +show grants for mysqltest_8; +Grants for mysqltest_8@% +GRANT USAGE ON *.* TO 'mysqltest_8'@'%' +GRANT ALL PRIVILEGES ON `mysqltest`.* TO 'mysqltest_8'@'%' +select * from information_schema.user_privileges +where grantee like "'mysqltest_8'%"; +GRANTEE TABLE_CATALOG PRIVILEGE_TYPE IS_GRANTABLE +'mysqltest_8'@'host8' NULL USAGE NO +'mysqltest_8'@'%' NULL USAGE NO +'mysqltest_8'@'' NULL USAGE NO +select * from t1; +a +flush privileges; +show grants for mysqltest_8@''; +Grants for mysqltest_8@ +GRANT USAGE ON *.* TO 'mysqltest_8'@'' +GRANT ALL PRIVILEGES ON `mysqltest`.* TO 'mysqltest_8'@'' +GRANT UPDATE, UPDATE (a) ON `test`.`t1` TO 'mysqltest_8'@'' +show grants for mysqltest_8; +Grants for mysqltest_8@% +GRANT USAGE ON *.* TO 'mysqltest_8'@'%' +GRANT ALL PRIVILEGES ON `mysqltest`.* TO 'mysqltest_8'@'%' +drop user mysqltest_8@''; +show grants for mysqltest_8@''; +ERROR 42000: There is no such grant defined for user 'mysqltest_8' on host '' +show grants for mysqltest_8; +Grants for mysqltest_8@% +GRANT USAGE ON *.* TO 'mysqltest_8'@'%' +GRANT ALL PRIVILEGES ON `mysqltest`.* TO 'mysqltest_8'@'%' +select * from information_schema.user_privileges +where grantee like "'mysqltest_8'%"; +GRANTEE TABLE_CATALOG PRIVILEGE_TYPE IS_GRANTABLE +'mysqltest_8'@'host8' NULL USAGE NO +'mysqltest_8'@'%' NULL USAGE NO +drop user mysqltest_8; +connect(localhost,mysqltest_8,,test,MASTER_PORT,MASTER_SOCKET); +ERROR 28000: Access denied for user 'mysqltest_8'@'localhost' (using password: NO) +show grants for mysqltest_8; +ERROR 42000: There is no such grant defined for user 'mysqltest_8' on host '%' +drop user mysqltest_8@host8; +show grants for mysqltest_8@host8; +ERROR 42000: There is no such grant defined for user 'mysqltest_8' on host 'host8' +insert into mysql.user select * from t2; +flush privileges; +drop table t2; +drop table t1; diff --git a/mysql-test/r/group_min_max.result b/mysql-test/r/group_min_max.result index f9b55cc6a7b..d62586dba85 100644 --- a/mysql-test/r/group_min_max.result +++ b/mysql-test/r/group_min_max.result @@ -1981,46 +1981,7 @@ a b c d -create table t4 ( -pk_col int auto_increment primary key, a1 char(64), a2 char(64), b char(16), c char(16) not null, d char(16), dummy char(64) default ' ' -) engine=innodb; -insert into t4 (a1, a2, b, c, d, dummy) select * from t1; -create index idx12672_0 on t4 (a1); -create index idx12672_1 on t4 (a1,a2,b,c); -create index idx12672_2 on t4 (a1,a2,b); -analyze table t1; -Table Op Msg_type Msg_text -test.t1 analyze status Table is already up to date -select distinct a1 from t4 where pk_col not in (1,2,3,4); -a1 -a -b -c -d -drop table t1,t2,t3,t4; -create table t1 ( -a varchar(30), b varchar(30), primary key(a), key(b) -) engine=innodb; -select distinct a from t1; -a -drop table t1; -create table t1(a int, key(a)) engine=innodb; -insert into t1 values(1); -select a, count(a) from t1 group by a with rollup; -a count(a) -1 1 -NULL 1 -drop table t1; -create table t1 (f1 int, f2 char(1), primary key(f1,f2)) engine=innodb; -insert into t1 values ( 1,"e"),(2,"a"),( 3,"c"),(4,"d"); -alter table t1 drop primary key, add primary key (f2, f1); -explain select distinct f1 a, f1 b from t1; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 index NULL PRIMARY 5 NULL 4 Using index; Using temporary -explain select distinct f1, f2 from t1; -id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 range NULL PRIMARY 5 NULL 3 Using index for group-by; Using temporary -drop table t1; +drop table t1,t2,t3; create table t1 (c1 int not null,c2 int not null, primary key(c1,c2)); insert into t1 (c1,c2) values (10,1),(10,2),(10,3),(20,4),(20,5),(20,6),(30,7),(30,8),(30,9); diff --git a/mysql-test/r/heap_btree.result b/mysql-test/r/heap_btree.result index 7b944558a62..4c7ea0eae7b 100644 --- a/mysql-test/r/heap_btree.result +++ b/mysql-test/r/heap_btree.result @@ -256,3 +256,6 @@ SELECT INDEX_LENGTH FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=DATABASE() INDEX_LENGTH 21 DROP TABLE t1; +CREATE TABLE t1 (a INT, UNIQUE USING BTREE(a)) ENGINE=MEMORY; +INSERT INTO t1 VALUES(NULL),(NULL); +DROP TABLE t1; diff --git a/mysql-test/r/information_schema_db.result b/mysql-test/r/information_schema_db.result index 37537e257da..5919e0f071d 100644 --- a/mysql-test/r/information_schema_db.result +++ b/mysql-test/r/information_schema_db.result @@ -1,3 +1,7 @@ +drop table if exists t1,t2; +drop view if exists v1,v2; +drop function if exists f1; +drop function if exists f2; use INFORMATION_SCHEMA; show tables; Tables_in_information_schema @@ -31,10 +35,12 @@ TABLE_CONSTRAINTS TABLE_PRIVILEGES TRIGGERS create database `inf%`; +create database mbase; use `inf%`; show tables; Tables_in_inf% grant all privileges on `inf%`.* to 'mysqltest_1'@'localhost'; +grant all privileges on `mbase`.* to 'mysqltest_1'@'localhost'; create table t1 (f1 int); create function func1(curr_int int) returns int begin @@ -43,9 +49,58 @@ select max(f1) from t1 into ret_val; return ret_val; end| create view v1 as select f1 from t1 where f1 = func1(f1); +create function func2() returns int return 1; +use mbase; +create procedure p1 () +begin +select table_name from information_schema.key_column_usage +order by table_name; +end| +create table t1 +(f1 int(10) unsigned not null, +f2 varchar(100) not null, +primary key (f1), unique key (f2)); select * from information_schema.tables; +call mbase.p1(); +call mbase.p1(); +call mbase.p1(); +use `inf%`; drop user mysqltest_1@localhost; +drop table t1; +select table_name, table_type, table_comment from information_schema.tables +where table_schema='inf%' and func2(); +table_name table_type table_comment +v1 VIEW View 'inf%.v1' references invalid table(s) or column(s) or function(s) or define +select table_name, table_type, table_comment from information_schema.tables +where table_schema='inf%' and func2(); +table_name table_type table_comment +v1 VIEW View 'inf%.v1' references invalid table(s) or column(s) or function(s) or define drop view v1; drop function func1; -drop table t1; +drop function func2; drop database `inf%`; +drop procedure mbase.p1; +drop database mbase; +use test; +create table t1 (i int); +create function f1 () returns int return (select max(i) from t1); +create view v1 as select f1(); +create table t2 (id int); +create function f2 () returns int return (select max(i) from t2); +create view v2 as select f2(); +drop table t2; +select table_name, table_type, table_comment from information_schema.tables +where table_schema='test'; +table_name table_type table_comment +t1 BASE TABLE +v1 VIEW VIEW +v2 VIEW View 'test.v2' references invalid table(s) or column(s) or function(s) or define +drop table t1; +select table_name, table_type, table_comment from information_schema.tables +where table_schema='test'; +table_name table_type table_comment +v1 VIEW View 'test.v1' references invalid table(s) or column(s) or function(s) or define +v2 VIEW View 'test.v2' references invalid table(s) or column(s) or function(s) or define +drop function f1; +drop function f2; +drop view v1, v2; diff --git a/mysql-test/r/information_schema_part.result b/mysql-test/r/information_schema_part.result index cf49abf888a..ef3ee19656b 100644 --- a/mysql-test/r/information_schema_part.result +++ b/mysql-test/r/information_schema_part.result @@ -111,3 +111,32 @@ NULL test t1 p0 NULL 1 NULL LINEAR HASH NULL month(f1) NULL NULL 0 0 0 # 1024 0 NULL test t1 p1 NULL 2 NULL LINEAR HASH NULL month(f1) NULL NULL 0 0 0 # 1024 0 # # NULL NULL default 0 default NULL test t1 p2 NULL 3 NULL LINEAR HASH NULL month(f1) NULL NULL 0 0 0 # 1024 0 # # NULL NULL default 0 default drop table t1; +create table t1 (a int) +PARTITION BY RANGE (a) +SUBPARTITION BY LINEAR HASH (a) +(PARTITION p0 VALUES LESS THAN (10)); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` int(11) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY RANGE (a) SUBPARTITION BY LINEAR HASH (a) (PARTITION p0 VALUES LESS THAN (10) ENGINE = MyISAM) +select SUBPARTITION_METHOD FROM information_schema.partitions WHERE +table_schema="test" AND table_name="t1"; +SUBPARTITION_METHOD +LINEAR HASH +drop table t1; +create table t1 (a int) +PARTITION BY LIST (a) +(PARTITION p0 VALUES IN +(10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, +32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53)); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` int(11) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY LIST (a) (PARTITION p0 VALUES IN (10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53) ENGINE = MyISAM) +SELECT PARTITION_DESCRIPTION FROM information_schema.partitions WHERE +table_schema = "test" AND table_name = "t1"; +PARTITION_DESCRIPTION +10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53 +drop table t1; diff --git a/mysql-test/r/innodb.result b/mysql-test/r/innodb.result index 7ae87e42b8c..eca6d148567 100644 --- a/mysql-test/r/innodb.result +++ b/mysql-test/r/innodb.result @@ -1822,7 +1822,7 @@ Variable_name Value innodb_sync_spin_loops 20 show variables like "innodb_thread_concurrency"; Variable_name Value -innodb_thread_concurrency 20 +innodb_thread_concurrency 8 set global innodb_thread_concurrency=1001; show variables like "innodb_thread_concurrency"; Variable_name Value diff --git a/mysql-test/r/innodb_mysql.result b/mysql-test/r/innodb_mysql.result index bbd9550196f..918f1c2c7c5 100644 --- a/mysql-test/r/innodb_mysql.result +++ b/mysql-test/r/innodb_mysql.result @@ -1,4 +1,4 @@ -drop table if exists t1,t2; +drop table if exists t1,t2,t1m,t1i,t2m,t2i,t4; create table t1 ( c_id int(11) not null default '0', org_id int(11) default null, @@ -192,3 +192,137 @@ select count(*), min(7), max(7) from t2m, t1i; count(*) min(7) max(7) 0 NULL NULL drop table t1m, t1i, t2m, t2i; +create table t1 ( +a1 char(64), a2 char(64), b char(16), c char(16) not null, d char(16), dummy char(64) default ' ' +); +insert into t1 (a1, a2, b, c, d) values +('a','a','a','a111','xy1'),('a','a','a','b111','xy2'),('a','a','a','c111','xy3'),('a','a','a','d111','xy4'), +('a','a','b','e112','xy1'),('a','a','b','f112','xy2'),('a','a','b','g112','xy3'),('a','a','b','h112','xy4'), +('a','b','a','i121','xy1'),('a','b','a','j121','xy2'),('a','b','a','k121','xy3'),('a','b','a','l121','xy4'), +('a','b','b','m122','xy1'),('a','b','b','n122','xy2'),('a','b','b','o122','xy3'),('a','b','b','p122','xy4'), +('b','a','a','a211','xy1'),('b','a','a','b211','xy2'),('b','a','a','c211','xy3'),('b','a','a','d211','xy4'), +('b','a','b','e212','xy1'),('b','a','b','f212','xy2'),('b','a','b','g212','xy3'),('b','a','b','h212','xy4'), +('b','b','a','i221','xy1'),('b','b','a','j221','xy2'),('b','b','a','k221','xy3'),('b','b','a','l221','xy4'), +('b','b','b','m222','xy1'),('b','b','b','n222','xy2'),('b','b','b','o222','xy3'),('b','b','b','p222','xy4'), +('c','a','a','a311','xy1'),('c','a','a','b311','xy2'),('c','a','a','c311','xy3'),('c','a','a','d311','xy4'), +('c','a','b','e312','xy1'),('c','a','b','f312','xy2'),('c','a','b','g312','xy3'),('c','a','b','h312','xy4'), +('c','b','a','i321','xy1'),('c','b','a','j321','xy2'),('c','b','a','k321','xy3'),('c','b','a','l321','xy4'), +('c','b','b','m322','xy1'),('c','b','b','n322','xy2'),('c','b','b','o322','xy3'),('c','b','b','p322','xy4'), +('d','a','a','a411','xy1'),('d','a','a','b411','xy2'),('d','a','a','c411','xy3'),('d','a','a','d411','xy4'), +('d','a','b','e412','xy1'),('d','a','b','f412','xy2'),('d','a','b','g412','xy3'),('d','a','b','h412','xy4'), +('d','b','a','i421','xy1'),('d','b','a','j421','xy2'),('d','b','a','k421','xy3'),('d','b','a','l421','xy4'), +('d','b','b','m422','xy1'),('d','b','b','n422','xy2'),('d','b','b','o422','xy3'),('d','b','b','p422','xy4'), +('a','a','a','a111','xy1'),('a','a','a','b111','xy2'),('a','a','a','c111','xy3'),('a','a','a','d111','xy4'), +('a','a','b','e112','xy1'),('a','a','b','f112','xy2'),('a','a','b','g112','xy3'),('a','a','b','h112','xy4'), +('a','b','a','i121','xy1'),('a','b','a','j121','xy2'),('a','b','a','k121','xy3'),('a','b','a','l121','xy4'), +('a','b','b','m122','xy1'),('a','b','b','n122','xy2'),('a','b','b','o122','xy3'),('a','b','b','p122','xy4'), +('b','a','a','a211','xy1'),('b','a','a','b211','xy2'),('b','a','a','c211','xy3'),('b','a','a','d211','xy4'), +('b','a','b','e212','xy1'),('b','a','b','f212','xy2'),('b','a','b','g212','xy3'),('b','a','b','h212','xy4'), +('b','b','a','i221','xy1'),('b','b','a','j221','xy2'),('b','b','a','k221','xy3'),('b','b','a','l221','xy4'), +('b','b','b','m222','xy1'),('b','b','b','n222','xy2'),('b','b','b','o222','xy3'),('b','b','b','p222','xy4'), +('c','a','a','a311','xy1'),('c','a','a','b311','xy2'),('c','a','a','c311','xy3'),('c','a','a','d311','xy4'), +('c','a','b','e312','xy1'),('c','a','b','f312','xy2'),('c','a','b','g312','xy3'),('c','a','b','h312','xy4'), +('c','b','a','i321','xy1'),('c','b','a','j321','xy2'),('c','b','a','k321','xy3'),('c','b','a','l321','xy4'), +('c','b','b','m322','xy1'),('c','b','b','n322','xy2'),('c','b','b','o322','xy3'),('c','b','b','p322','xy4'), +('d','a','a','a411','xy1'),('d','a','a','b411','xy2'),('d','a','a','c411','xy3'),('d','a','a','d411','xy4'), +('d','a','b','e412','xy1'),('d','a','b','f412','xy2'),('d','a','b','g412','xy3'),('d','a','b','h412','xy4'), +('d','b','a','i421','xy1'),('d','b','a','j421','xy2'),('d','b','a','k421','xy3'),('d','b','a','l421','xy4'), +('d','b','b','m422','xy1'),('d','b','b','n422','xy2'),('d','b','b','o422','xy3'),('d','b','b','p422','xy4'); +create table t4 ( +pk_col int auto_increment primary key, a1 char(64), a2 char(64), b char(16), c char(16) not null, d char(16), dummy char(64) default ' ' +) engine=innodb; +insert into t4 (a1, a2, b, c, d, dummy) select * from t1; +create index idx12672_0 on t4 (a1); +create index idx12672_1 on t4 (a1,a2,b,c); +create index idx12672_2 on t4 (a1,a2,b); +analyze table t1; +Table Op Msg_type Msg_text +test.t1 analyze status OK +select distinct a1 from t4 where pk_col not in (1,2,3,4); +a1 +a +b +c +d +drop table t1,t4; +create table t1 ( +a varchar(30), b varchar(30), primary key(a), key(b) +) engine=innodb; +select distinct a from t1; +a +drop table t1; +create table t1(a int, key(a)) engine=innodb; +insert into t1 values(1); +select a, count(a) from t1 group by a with rollup; +a count(a) +1 1 +NULL 1 +drop table t1; +create table t1 (f1 int, f2 char(1), primary key(f1,f2)) engine=innodb; +insert into t1 values ( 1,"e"),(2,"a"),( 3,"c"),(4,"d"); +alter table t1 drop primary key, add primary key (f2, f1); +explain select distinct f1 a, f1 b from t1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 index NULL PRIMARY 5 NULL 4 Using index; Using temporary +explain select distinct f1, f2 from t1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range NULL PRIMARY 5 NULL 3 Using index for group-by; Using temporary +drop table t1; +set storage_engine=innodb; +CREATE TABLE t1 (a int, b int); +insert into t1 values (1,1),(1,2); +CREATE TABLE t2 (primary key (a)) select * from t1; +ERROR 23000: Duplicate entry '1' for key 'PRIMARY' +drop table if exists t2; +Warnings: +Note 1051 Unknown table 't2' +CREATE TEMPORARY TABLE t2 (primary key (a)) select * from t1; +ERROR 23000: Duplicate entry '1' for key 'PRIMARY' +drop table if exists t2; +Warnings: +Note 1051 Unknown table 't2' +CREATE TABLE t2 (a int, b int, primary key (a)); +BEGIN; +INSERT INTO t2 values(100,100); +CREATE TABLE IF NOT EXISTS t2 (primary key (a)) select * from t1; +ERROR 23000: Duplicate entry '1' for key 'PRIMARY' +SELECT * from t2; +a b +100 100 +ROLLBACK; +SELECT * from t2; +a b +100 100 +TRUNCATE table t2; +INSERT INTO t2 select * from t1; +ERROR 23000: Duplicate entry '1' for key 'PRIMARY' +SELECT * from t2; +a b +drop table t2; +CREATE TEMPORARY TABLE t2 (a int, b int, primary key (a)); +BEGIN; +INSERT INTO t2 values(100,100); +CREATE TEMPORARY TABLE IF NOT EXISTS t2 (primary key (a)) select * from t1; +ERROR 23000: Duplicate entry '1' for key 'PRIMARY' +SELECT * from t2; +a b +100 100 +COMMIT; +BEGIN; +INSERT INTO t2 values(101,101); +CREATE TEMPORARY TABLE IF NOT EXISTS t2 (primary key (a)) select * from t1; +ERROR 23000: Duplicate entry '1' for key 'PRIMARY' +SELECT * from t2; +a b +100 100 +101 101 +ROLLBACK; +SELECT * from t2; +a b +100 100 +TRUNCATE table t2; +INSERT INTO t2 select * from t1; +ERROR 23000: Duplicate entry '1' for key 'PRIMARY' +SELECT * from t2; +a b +drop table t1,t2; diff --git a/mysql-test/r/insert.result b/mysql-test/r/insert.result index 5e9ab480558..18bd6d7e796 100644 --- a/mysql-test/r/insert.result +++ b/mysql-test/r/insert.result @@ -2,8 +2,8 @@ drop table if exists t1,t2,t3; create table t1 (a int not null); insert into t1 values (1); insert into t1 values (a+2); -insert into t1 values (a+3); -insert into t1 values (4),(a+5); +insert into t1 values (a+3),(a+4); +insert into t1 values (5),(a+6); select * from t1; a 1 @@ -11,6 +11,7 @@ a 3 4 5 +6 drop table t1; create table t1 (id int not null auto_increment primary key, username varchar(32) not null, unique (username)); insert into t1 values (0,"mysql"); @@ -299,3 +300,24 @@ select count(*) from t2; count(*) 25500 drop table t1,t2,t3; +create table t1 (a int, b int); +insert into t1 (a,b) values (a,b); +insert into t1 SET a=1, b=a+1; +insert into t1 (a,b) select 1,2; +INSERT INTO t1 ( a ) SELECT 0 ON DUPLICATE KEY UPDATE a = a + VALUES (a); +prepare stmt1 from ' replace into t1 (a,a) select 100, ''hundred'' '; +execute stmt1; +ERROR 42000: Column 'a' specified twice +insert into t1 (a,b,b) values (1,1,1); +ERROR 42000: Column 'b' specified twice +insert into t1 (a,a) values (1,1,1); +ERROR 21S01: Column count doesn't match value count at row 1 +insert into t1 (a,a) values (1,1); +ERROR 42000: Column 'a' specified twice +insert into t1 SET a=1,b=2,a=1; +ERROR 42000: Column 'a' specified twice +insert into t1 (b,b) select 1,2; +ERROR 42000: Column 'b' specified twice +INSERT INTO t1 (b,b) SELECT 0,0 ON DUPLICATE KEY UPDATE a = a + VALUES (a); +ERROR 42000: Column 'b' specified twice +drop table t1; diff --git a/mysql-test/r/key_cache.result b/mysql-test/r/key_cache.result index 99f5277f817..406a92b9a08 100644 --- a/mysql-test/r/key_cache.result +++ b/mysql-test/r/key_cache.result @@ -191,10 +191,10 @@ cache index t1 in unknown_key_cache; ERROR HY000: Unknown key cache 'unknown_key_cache' cache index t1 key (unknown_key) in keycache1; Table Op Msg_type Msg_text -test.t1 assign_to_keycache error Key column 'unknown_key' doesn't exist in table +test.t1 assign_to_keycache error Key 'unknown_key' doesn't exist in table 't1' test.t1 assign_to_keycache status Operation failed Warnings: -Error 1072 Key column 'unknown_key' doesn't exist in table +Error 1176 Key 'unknown_key' doesn't exist in table 't1' select @@keycache2.key_buffer_size; @@keycache2.key_buffer_size 4194304 diff --git a/mysql-test/r/loaddata.result b/mysql-test/r/loaddata.result index 0b314defece..72beee4b2e3 100644 --- a/mysql-test/r/loaddata.result +++ b/mysql-test/r/loaddata.result @@ -115,6 +115,15 @@ select @a, @b; @a @b NULL 15 truncate table t1; +load data infile '../std_data_ln/rpl_loaddata.dat' into table t1 set c=b; +Warnings: +Warning 1261 Row 1 doesn't contain data for all columns +Warning 1261 Row 2 doesn't contain data for all columns +select * from t1; +a b c +NULL 10 10 +NULL 15 15 +truncate table t1; load data infile '../std_data_ln/loaddata5.dat' into table t1 fields terminated by '' enclosed by '' (a, b) set c="Wow"; select * from t1; a b c diff --git a/mysql-test/r/lock_multi.result b/mysql-test/r/lock_multi.result index 1ab51e9fad5..6d31d01d154 100644 --- a/mysql-test/r/lock_multi.result +++ b/mysql-test/r/lock_multi.result @@ -50,3 +50,27 @@ Field Type Null Key Default Extra a int(11) YES NULL unlock tables; drop table t1; +CREATE DATABASE mysqltest_1; +FLUSH TABLES WITH READ LOCK; + DROP DATABASE mysqltest_1; +DROP DATABASE mysqltest_1; +ERROR HY000: Can't execute the query because you have a conflicting read lock +UNLOCK TABLES; +DROP DATABASE mysqltest_1; +ERROR HY000: Can't drop database 'mysqltest_1'; database doesn't exist +use mysql; +LOCK TABLES columns_priv WRITE, db WRITE, host WRITE, user WRITE; +FLUSH TABLES; +use mysql; + SELECT user.Select_priv FROM user, db WHERE user.user = db.user LIMIT 1; +OPTIMIZE TABLES columns_priv, db, host, user; +Table Op Msg_type Msg_text +mysql.columns_priv optimize status OK +mysql.db optimize status OK +mysql.host optimize status OK +mysql.user optimize status OK +UNLOCK TABLES; +Select_priv +N +use test; +use test; diff --git a/mysql-test/r/multi_update.result b/mysql-test/r/multi_update.result index ea02a703c65..6fdd105fd6c 100644 --- a/mysql-test/r/multi_update.result +++ b/mysql-test/r/multi_update.result @@ -519,3 +519,83 @@ a 30 drop view v1; drop table t1, t2; +create table t1 (i1 int, i2 int, i3 int); +create table t2 (id int, c1 varchar(20), c2 varchar(20)); +insert into t1 values (1,5,10),(3,7,12),(4,5,2),(9,10,15),(2,2,2); +insert into t2 values (9,"abc","def"),(5,"opq","lmn"),(2,"test t","t test"); +select * from t1 order by i1; +i1 i2 i3 +1 5 10 +2 2 2 +3 7 12 +4 5 2 +9 10 15 +select * from t2; +id c1 c2 +9 abc def +5 opq lmn +2 test t t test +update t1,t2 set t1.i2=15, t2.c2="ppc" where t1.i1=t2.id; +select * from t1 order by i1; +i1 i2 i3 +1 5 10 +2 15 2 +3 7 12 +4 5 2 +9 15 15 +select * from t2 order by id; +id c1 c2 +2 test t ppc +5 opq lmn +9 abc ppc +delete t1.*,t2.* from t1,t2 where t1.i2=t2.id; +select * from t1 order by i1; +i1 i2 i3 +2 15 2 +3 7 12 +9 15 15 +select * from t2 order by id; +id c1 c2 +2 test t ppc +9 abc ppc +drop table t1, t2; +create table t1 (i1 int auto_increment not null, i2 int, i3 int, primary key (i1)); +create table t2 (id int auto_increment not null, c1 varchar(20), c2 varchar(20), primary key(id)); +insert into t1 values (1,5,10),(3,7,12),(4,5,2),(9,10,15),(2,2,2); +insert into t2 values (9,"abc","def"),(5,"opq","lmn"),(2,"test t","t test"); +select * from t1 order by i1; +i1 i2 i3 +1 5 10 +2 2 2 +3 7 12 +4 5 2 +9 10 15 +select * from t2 order by id; +id c1 c2 +2 test t t test +5 opq lmn +9 abc def +update t1,t2 set t1.i2=15, t2.c2="ppc" where t1.i1=t2.id; +select * from t1 order by i1; +i1 i2 i3 +1 5 10 +2 15 2 +3 7 12 +4 5 2 +9 15 15 +select * from t2 order by id; +id c1 c2 +2 test t ppc +5 opq lmn +9 abc ppc +delete t1.*,t2.* from t1,t2 where t1.i2=t2.id; +select * from t1 order by i1; +i1 i2 i3 +2 15 2 +3 7 12 +9 15 15 +select * from t2 order by id; +id c1 c2 +2 test t ppc +9 abc ppc +drop table t1, t2; diff --git a/mysql-test/r/mysqlbinlog.result b/mysql-test/r/mysqlbinlog.result index 664833fab2a..c3be791b523 100644 --- a/mysql-test/r/mysqlbinlog.result +++ b/mysql-test/r/mysqlbinlog.result @@ -190,4 +190,8 @@ select HEX(f) from t4; HEX(f) 835C flush logs; -drop table t1, t2, t03, t04, t3, t4; +select * from t5 /* must be (1),(1) */; +a +1 +1 +drop table t1, t2, t03, t04, t3, t4, t5; diff --git a/mysql-test/r/mysqlcheck.result b/mysql-test/r/mysqlcheck.result index acdbd868361..a3c14b13bde 100644 --- a/mysql-test/r/mysqlcheck.result +++ b/mysql-test/r/mysqlcheck.result @@ -1,3 +1,4 @@ +drop database if exists client_test_db; DROP SCHEMA test; CREATE SCHEMA test; cluster.binlog_index OK diff --git a/mysql-test/r/mysqldump.result b/mysql-test/r/mysqldump.result index 493e79d4136..7075e7dec6d 100644 --- a/mysql-test/r/mysqldump.result +++ b/mysql-test/r/mysqldump.result @@ -1,4 +1,4 @@ -DROP TABLE IF EXISTS t1, `"t"1`, t1aa, t2, t2aa; +DROP TABLE IF EXISTS t1, `"t"1`, t1aa, t2, t2aa, t3; drop database if exists mysqldump_test_db; drop database if exists db1; drop database if exists db2; @@ -1509,6 +1509,7 @@ a b 12 meg drop table t1, t2; drop database db1; +--fields-optionally-enclosed-by=" CREATE DATABASE mysqldump_test_db; USE mysqldump_test_db; CREATE TABLE t1 ( a INT ); @@ -1738,6 +1739,43 @@ t1 CREATE TABLE `t1` ( KEY `t1_name` (`t1_name`) ) ENGINE=MyISAM AUTO_INCREMENT=1003 DEFAULT CHARSET=latin1 drop table `t1`; +create table t1(a int); +create table t2(a int); +create table t3(a int); + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; +DROP TABLE IF EXISTS `t3`; +CREATE TABLE `t3` ( + `a` int(11) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1; +DROP TABLE IF EXISTS `t1`; +CREATE TABLE `t1` ( + `a` int(11) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1; +DROP TABLE IF EXISTS `t2`; +CREATE TABLE `t2` ( + `a` int(11) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +drop table t1, t2, t3; End of 4.1 tests create table t1 (a binary(1), b blob); insert into t1 values ('',''); diff --git a/mysql-test/r/ndb_basic.result b/mysql-test/r/ndb_basic.result index b8bafd398fe..631165d9fc8 100644 --- a/mysql-test/r/ndb_basic.result +++ b/mysql-test/r/ndb_basic.result @@ -748,3 +748,11 @@ f1 f2 f3 111111 aaaaaa 1 222222 bbbbbb 2 drop table t1; +CREATE TABLE t1 (a VARCHAR(255) NOT NULL, +CONSTRAINT pk_a PRIMARY KEY (a))engine=ndb; +CREATE TABLE t2(a VARCHAR(255) NOT NULL, +b VARCHAR(255) NOT NULL, +c VARCHAR(255) NOT NULL, +CONSTRAINT pk_b_c_id PRIMARY KEY (b,c), +CONSTRAINT fk_a FOREIGN KEY(a) REFERENCES t1(a))engine=ndb; +drop table t1, t2; diff --git a/mysql-test/r/ndb_dd_backuprestore.result b/mysql-test/r/ndb_dd_backuprestore.result index e7568e4ce49..33edf6783e6 100644 --- a/mysql-test/r/ndb_dd_backuprestore.result +++ b/mysql-test/r/ndb_dd_backuprestore.result @@ -155,333 +155,10 @@ DROP TABLE test.t1; DROP TABLE test.t2; DROP TABLE test.t3; DROP TABLE test.t4; -**** Test 3 Adding partition Test backup and restore **** -CREATE TABLESPACE table_space2 -ADD DATAFILE './table_space2/datafile.dat' -USE LOGFILE GROUP log_group1 -INITIAL_SIZE 12M -ENGINE NDB; -CREATE TABLE test.t1 (pk1 MEDIUMINT NOT NULL AUTO_INCREMENT, c2 VARCHAR(150) NOT NULL, c3 INT NOT NULL, c4 BIT NOT NULL, PRIMARY KEY(pk1,c3))TABLESPACE table_space1 STORAGE DISK ENGINE=NDB PARTITION BY HASH(c3) PARTITIONS 4; -CREATE TABLE test.t4 (pk1 MEDIUMINT NOT NULL AUTO_INCREMENT, c2 VARCHAR(180) NOT NULL, c3 INT NOT NULL, c4 BIT NOT NULL, PRIMARY KEY(pk1,c3))ENGINE=NDB PARTITION BY HASH(c3) PARTITIONS 2; -CREATE TABLE test.t2 (pk1 MEDIUMINT NOT NULL AUTO_INCREMENT, c2 TEXT NOT NULL, c3 INT NOT NULL, c4 BIT NOT NULL, PRIMARY KEY(pk1,c3))TABLESPACE table_space2 STORAGE DISK ENGINE=NDB PARTITION BY KEY(c3) (PARTITION p0 ENGINE = NDB, PARTITION p1 ENGINE = NDB); -CREATE TABLE test.t5 (pk1 MEDIUMINT NOT NULL AUTO_INCREMENT, c2 TEXT NOT NULL, c3 INT NOT NULL, c4 BIT NOT NULL, PRIMARY KEY(pk1,c3))ENGINE=NDB PARTITION BY KEY(pk1) (PARTITION p0 ENGINE = NDB, PARTITION p1 ENGINE = NDB); -CREATE TABLE test.t3 (pk1 MEDIUMINT NOT NULL AUTO_INCREMENT, c2 VARCHAR(202) NOT NULL, c3 INT NOT NULL, c4 BIT NOT NULL, PRIMARY KEY(pk1,c3))TABLESPACE table_space2 STORAGE DISK ENGINE=NDB PARTITION BY RANGE (c3) PARTITIONS 3 (PARTITION x1 VALUES LESS THAN (105), PARTITION x2 VALUES LESS THAN (333), PARTITION x3 VALUES LESS THAN (720)); -CREATE TABLE test.t6 (pk1 MEDIUMINT NOT NULL AUTO_INCREMENT, c2 VARCHAR(220) NOT NULL, c3 INT NOT NULL, c4 BIT NOT NULL, PRIMARY KEY(pk1,c3))ENGINE=NDB PARTITION BY RANGE (pk1) PARTITIONS 2 (PARTITION x1 VALUES LESS THAN (333), PARTITION x2 VALUES LESS THAN (720)); -SHOW CREATE TABLE test.t1; -Table Create Table -t1 CREATE TABLE `t1` ( - `pk1` mediumint(9) NOT NULL AUTO_INCREMENT, - `c2` varchar(150) NOT NULL, - `c3` int(11) NOT NULL, - `c4` bit(1) NOT NULL, - PRIMARY KEY (`pk1`,`c3`) -) TABLESPACE table_space1 STORAGE DISK ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY HASH (c3) PARTITIONS 4 -SHOW CREATE TABLE test.t2; -Table Create Table -t2 CREATE TABLE `t2` ( - `pk1` mediumint(9) NOT NULL AUTO_INCREMENT, - `c2` text NOT NULL, - `c3` int(11) NOT NULL, - `c4` bit(1) NOT NULL, - PRIMARY KEY (`pk1`,`c3`) -) TABLESPACE table_space2 STORAGE DISK ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY KEY (c3) (PARTITION p0 ENGINE = ndbcluster, PARTITION p1 ENGINE = ndbcluster) -SHOW CREATE TABLE test.t3; -Table Create Table -t3 CREATE TABLE `t3` ( - `pk1` mediumint(9) NOT NULL AUTO_INCREMENT, - `c2` varchar(202) NOT NULL, - `c3` int(11) NOT NULL, - `c4` bit(1) NOT NULL, - PRIMARY KEY (`pk1`,`c3`) -) TABLESPACE table_space2 STORAGE DISK ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY RANGE (c3) (PARTITION x1 VALUES LESS THAN (105) ENGINE = ndbcluster, PARTITION x2 VALUES LESS THAN (333) ENGINE = ndbcluster, PARTITION x3 VALUES LESS THAN (720) ENGINE = ndbcluster) -SHOW CREATE TABLE test.t4; -Table Create Table -t4 CREATE TABLE `t4` ( - `pk1` mediumint(9) NOT NULL AUTO_INCREMENT, - `c2` varchar(180) NOT NULL, - `c3` int(11) NOT NULL, - `c4` bit(1) NOT NULL, - PRIMARY KEY (`pk1`,`c3`) -) ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY HASH (c3) PARTITIONS 2 -SHOW CREATE TABLE test.t5; -Table Create Table -t5 CREATE TABLE `t5` ( - `pk1` mediumint(9) NOT NULL AUTO_INCREMENT, - `c2` text NOT NULL, - `c3` int(11) NOT NULL, - `c4` bit(1) NOT NULL, - PRIMARY KEY (`pk1`,`c3`) -) ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY KEY (pk1) (PARTITION p0 ENGINE = ndbcluster, PARTITION p1 ENGINE = ndbcluster) -SHOW CREATE TABLE test.t6; -Table Create Table -t6 CREATE TABLE `t6` ( - `pk1` mediumint(9) NOT NULL AUTO_INCREMENT, - `c2` varchar(220) NOT NULL, - `c3` int(11) NOT NULL, - `c4` bit(1) NOT NULL, - PRIMARY KEY (`pk1`,`c3`) -) ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY RANGE (pk1) (PARTITION x1 VALUES LESS THAN (333) ENGINE = ndbcluster, PARTITION x2 VALUES LESS THAN (720) ENGINE = ndbcluster) -SELECT * FROM information_schema.partitions WHERE table_name= 't1'; -TABLE_CATALOG TABLE_SCHEMA TABLE_NAME PARTITION_NAME SUBPARTITION_NAME PARTITION_ORDINAL_POSITION SUBPARTITION_ORDINAL_POSITION PARTITION_METHOD SUBPARTITION_METHOD PARTITION_EXPRESSION SUBPARTITION_EXPRESSION PARTITION_DESCRIPTION TABLE_ROWS AVG_ROW_LENGTH DATA_LENGTH MAX_DATA_LENGTH INDEX_LENGTH DATA_FREE CREATE_TIME UPDATE_TIME CHECK_TIME CHECKSUM PARTITION_COMMENT NODEGROUP TABLESPACE_NAME -NULL test t1 p0 NULL 1 NULL HASH NULL c3 NULL NULL 0 0 0 NULL 0 0 NULL NULL NULL NULL default 0 default -NULL test t1 p1 NULL 2 NULL HASH NULL c3 NULL NULL 0 0 0 NULL 0 0 NULL NULL NULL NULL default 0 default -NULL test t1 p2 NULL 3 NULL HASH NULL c3 NULL NULL 0 0 0 NULL 0 0 NULL NULL NULL NULL default 0 default -NULL test t1 p3 NULL 4 NULL HASH NULL c3 NULL NULL 0 0 0 NULL 0 0 NULL NULL NULL NULL default 0 default -SELECT * FROM information_schema.partitions WHERE table_name= 't2'; -TABLE_CATALOG TABLE_SCHEMA TABLE_NAME PARTITION_NAME SUBPARTITION_NAME PARTITION_ORDINAL_POSITION SUBPARTITION_ORDINAL_POSITION PARTITION_METHOD SUBPARTITION_METHOD PARTITION_EXPRESSION SUBPARTITION_EXPRESSION PARTITION_DESCRIPTION TABLE_ROWS AVG_ROW_LENGTH DATA_LENGTH MAX_DATA_LENGTH INDEX_LENGTH DATA_FREE CREATE_TIME UPDATE_TIME CHECK_TIME CHECKSUM PARTITION_COMMENT NODEGROUP TABLESPACE_NAME -NULL test t2 p0 NULL 1 NULL KEY NULL c3 NULL NULL 0 0 0 NULL 0 0 NULL NULL NULL NULL default 0 default -NULL test t2 p1 NULL 2 NULL KEY NULL c3 NULL NULL 0 0 0 NULL 0 0 NULL NULL NULL NULL default 0 default -SELECT * FROM information_schema.partitions WHERE table_name= 't3'; -TABLE_CATALOG TABLE_SCHEMA TABLE_NAME PARTITION_NAME SUBPARTITION_NAME PARTITION_ORDINAL_POSITION SUBPARTITION_ORDINAL_POSITION PARTITION_METHOD SUBPARTITION_METHOD PARTITION_EXPRESSION SUBPARTITION_EXPRESSION PARTITION_DESCRIPTION TABLE_ROWS AVG_ROW_LENGTH DATA_LENGTH MAX_DATA_LENGTH INDEX_LENGTH DATA_FREE CREATE_TIME UPDATE_TIME CHECK_TIME CHECKSUM PARTITION_COMMENT NODEGROUP TABLESPACE_NAME -NULL test t3 x1 NULL 1 NULL RANGE NULL c3 NULL 105 0 0 0 NULL 0 0 NULL NULL NULL NULL default 0 default -NULL test t3 x2 NULL 2 NULL RANGE NULL c3 NULL 333 0 0 0 NULL 0 0 NULL NULL NULL NULL default 0 default -NULL test t3 x3 NULL 3 NULL RANGE NULL c3 NULL 720 0 0 0 NULL 0 0 NULL NULL NULL NULL default 0 default -SELECT * FROM information_schema.partitions WHERE table_name= 't4'; -TABLE_CATALOG TABLE_SCHEMA TABLE_NAME PARTITION_NAME SUBPARTITION_NAME PARTITION_ORDINAL_POSITION SUBPARTITION_ORDINAL_POSITION PARTITION_METHOD SUBPARTITION_METHOD PARTITION_EXPRESSION SUBPARTITION_EXPRESSION PARTITION_DESCRIPTION TABLE_ROWS AVG_ROW_LENGTH DATA_LENGTH MAX_DATA_LENGTH INDEX_LENGTH DATA_FREE CREATE_TIME UPDATE_TIME CHECK_TIME CHECKSUM PARTITION_COMMENT NODEGROUP TABLESPACE_NAME -NULL test t4 p0 NULL 1 NULL HASH NULL c3 NULL NULL 0 0 0 NULL 0 0 NULL NULL NULL NULL default 0 default -NULL test t4 p1 NULL 2 NULL HASH NULL c3 NULL NULL 0 0 0 NULL 0 0 NULL NULL NULL NULL default 0 default -SELECT * FROM information_schema.partitions WHERE table_name= 't5'; -TABLE_CATALOG TABLE_SCHEMA TABLE_NAME PARTITION_NAME SUBPARTITION_NAME PARTITION_ORDINAL_POSITION SUBPARTITION_ORDINAL_POSITION PARTITION_METHOD SUBPARTITION_METHOD PARTITION_EXPRESSION SUBPARTITION_EXPRESSION PARTITION_DESCRIPTION TABLE_ROWS AVG_ROW_LENGTH DATA_LENGTH MAX_DATA_LENGTH INDEX_LENGTH DATA_FREE CREATE_TIME UPDATE_TIME CHECK_TIME CHECKSUM PARTITION_COMMENT NODEGROUP TABLESPACE_NAME -NULL test t5 p0 NULL 1 NULL KEY NULL pk1 NULL NULL 0 0 0 NULL 0 0 NULL NULL NULL NULL default 0 default -NULL test t5 p1 NULL 2 NULL KEY NULL pk1 NULL NULL 0 0 0 NULL 0 0 NULL NULL NULL NULL default 0 default -SELECT * FROM information_schema.partitions WHERE table_name= 't6'; -TABLE_CATALOG TABLE_SCHEMA TABLE_NAME PARTITION_NAME SUBPARTITION_NAME PARTITION_ORDINAL_POSITION SUBPARTITION_ORDINAL_POSITION PARTITION_METHOD SUBPARTITION_METHOD PARTITION_EXPRESSION SUBPARTITION_EXPRESSION PARTITION_DESCRIPTION TABLE_ROWS AVG_ROW_LENGTH DATA_LENGTH MAX_DATA_LENGTH INDEX_LENGTH DATA_FREE CREATE_TIME UPDATE_TIME CHECK_TIME CHECKSUM PARTITION_COMMENT NODEGROUP TABLESPACE_NAME -NULL test t6 x1 NULL 1 NULL RANGE NULL pk1 NULL 333 0 0 0 NULL 0 0 NULL NULL NULL NULL default 0 default -NULL test t6 x2 NULL 2 NULL RANGE NULL pk1 NULL 720 0 0 0 NULL 0 0 NULL NULL NULL NULL default 0 default -SELECT COUNT(*) FROM test.t1; -COUNT(*) -250 -SELECT pk1, c2, c3, hex(c4) FROM test.t1 ORDER BY c3 LIMIT 5; -pk1 c2 c3 hex(c4) -250 Sweden, Texas 2 0 -249 Sweden, Texas 4 0 -248 Sweden, Texas 6 0 -247 Sweden, Texas 8 0 -246 Sweden, Texas 10 0 -SELECT COUNT(*) FROM test.t2; -COUNT(*) -250 -SELECT pk1, c2, c3, hex(c4) FROM test.t2 ORDER BY c3 LIMIT 5; -pk1 c2 c3 hex(c4) -250 Sweden, Texas, ITALY, Kyle, JO, JBM,TU 1 1 -249 Sweden, Texas, ITALY, Kyle, JO, JBM,TU 3 1 -248 Sweden, Texas, ITALY, Kyle, JO, JBM,TU 5 1 -247 Sweden, Texas, ITALY, Kyle, JO, JBM,TU 7 1 -246 Sweden, Texas, ITALY, Kyle, JO, JBM,TU 9 1 -SELECT COUNT(*) FROM test.t3; -COUNT(*) -250 -SELECT pk1, c2, c3, hex(c4) FROM test.t3 ORDER BY c3 LIMIT 5; -pk1 c2 c3 hex(c4) -250 TEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXAS, ITALY, Kyle, JO, JBM,TU 0 1 -249 TEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXAS, ITALY, Kyle, JO, JBM,TU 2 1 -248 TEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXAS, ITALY, Kyle, JO, JBM,TU 4 1 -247 TEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXAS, ITALY, Kyle, JO, JBM,TU 6 1 -246 TEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXAS, ITALY, Kyle, JO, JBM,TU 8 1 -SELECT COUNT(*) FROM test.t4; -COUNT(*) -250 -SELECT pk1, c2, c3, hex(c4) FROM test.t4 ORDER BY c3 LIMIT 5; -pk1 c2 c3 hex(c4) -250 Sweden, Texas 2 0 -249 Sweden, Texas 4 0 -248 Sweden, Texas 6 0 -247 Sweden, Texas 8 0 -246 Sweden, Texas 10 0 -SELECT COUNT(*) FROM test.t5; -COUNT(*) -250 -SELECT pk1, c2, c3, hex(c4) FROM test.t5 ORDER BY c3 LIMIT 5; -pk1 c2 c3 hex(c4) -250 Sweden, Texas, ITALY, Kyle, JO, JBM,TU 1 1 -249 Sweden, Texas, ITALY, Kyle, JO, JBM,TU 3 1 -248 Sweden, Texas, ITALY, Kyle, JO, JBM,TU 5 1 -247 Sweden, Texas, ITALY, Kyle, JO, JBM,TU 7 1 -246 Sweden, Texas, ITALY, Kyle, JO, JBM,TU 9 1 -SELECT COUNT(*) FROM test.t6; -COUNT(*) -250 -SELECT pk1, c2, c3, hex(c4) FROM test.t6 ORDER BY c3 LIMIT 5; -pk1 c2 c3 hex(c4) -250 TEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXAS, ITALY, Kyle, JO, JBM,TU 0 1 -249 TEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXAS, ITALY, Kyle, JO, JBM,TU 2 1 -248 TEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXAS, ITALY, Kyle, JO, JBM,TU 4 1 -247 TEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXAS, ITALY, Kyle, JO, JBM,TU 6 1 -246 TEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXAS, ITALY, Kyle, JO, JBM,TU 8 1 -CREATE TEMPORARY TABLE IF NOT EXISTS test.backup_info (id INT, backup_id INT) ENGINE = HEAP; -DELETE FROM test.backup_info; -LOAD DATA INFILE '../tmp.dat' INTO TABLE test.backup_info FIELDS TERMINATED BY ','; -SELECT @the_backup_id:=backup_id FROM test.backup_info; -@the_backup_id:=backup_id -<the_backup_id> -DROP TABLE test.backup_info; -DROP TABLE test.t1; -DROP TABLE test.t2; -DROP TABLE test.t3; -DROP TABLE test.t4; -DROP TABLE test.t5; -DROP TABLE test.t6; ALTER TABLESPACE table_space1 DROP DATAFILE './table_space1/datafile.dat' ENGINE = NDB; -ALTER TABLESPACE table_space2 -DROP DATAFILE './table_space2/datafile.dat' -ENGINE = NDB; DROP TABLESPACE table_space1 ENGINE = NDB; -DROP TABLESPACE table_space2 -ENGINE = NDB; DROP LOGFILE GROUP log_group1 ENGINE =NDB; -SHOW CREATE TABLE test.t1; -Table Create Table -t1 CREATE TABLE `t1` ( - `pk1` mediumint(9) NOT NULL AUTO_INCREMENT, - `c2` varchar(150) NOT NULL, - `c3` int(11) NOT NULL, - `c4` bit(1) NOT NULL, - PRIMARY KEY (`pk1`,`c3`) -) TABLESPACE table_space1 STORAGE DISK ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY HASH (c3) PARTITIONS 4 -SHOW CREATE TABLE test.t2; -Table Create Table -t2 CREATE TABLE `t2` ( - `pk1` mediumint(9) NOT NULL AUTO_INCREMENT, - `c2` text NOT NULL, - `c3` int(11) NOT NULL, - `c4` bit(1) NOT NULL, - PRIMARY KEY (`pk1`,`c3`) -) TABLESPACE table_space2 STORAGE DISK ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY KEY (c3) (PARTITION p0 ENGINE = ndbcluster, PARTITION p1 ENGINE = ndbcluster) -SHOW CREATE TABLE test.t3; -Table Create Table -t3 CREATE TABLE `t3` ( - `pk1` mediumint(9) NOT NULL AUTO_INCREMENT, - `c2` varchar(202) NOT NULL, - `c3` int(11) NOT NULL, - `c4` bit(1) NOT NULL, - PRIMARY KEY (`pk1`,`c3`) -) TABLESPACE table_space2 STORAGE DISK ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY RANGE (c3) (PARTITION x1 VALUES LESS THAN (105) ENGINE = ndbcluster, PARTITION x2 VALUES LESS THAN (333) ENGINE = ndbcluster, PARTITION x3 VALUES LESS THAN (720) ENGINE = ndbcluster) -SHOW CREATE TABLE test.t4; -Table Create Table -t4 CREATE TABLE `t4` ( - `pk1` mediumint(9) NOT NULL AUTO_INCREMENT, - `c2` varchar(180) NOT NULL, - `c3` int(11) NOT NULL, - `c4` bit(1) NOT NULL, - PRIMARY KEY (`pk1`,`c3`) -) ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY HASH (c3) PARTITIONS 2 -SHOW CREATE TABLE test.t5; -Table Create Table -t5 CREATE TABLE `t5` ( - `pk1` mediumint(9) NOT NULL AUTO_INCREMENT, - `c2` text NOT NULL, - `c3` int(11) NOT NULL, - `c4` bit(1) NOT NULL, - PRIMARY KEY (`pk1`,`c3`) -) ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY KEY (pk1) (PARTITION p0 ENGINE = ndbcluster, PARTITION p1 ENGINE = ndbcluster) -SHOW CREATE TABLE test.t6; -Table Create Table -t6 CREATE TABLE `t6` ( - `pk1` mediumint(9) NOT NULL AUTO_INCREMENT, - `c2` varchar(220) NOT NULL, - `c3` int(11) NOT NULL, - `c4` bit(1) NOT NULL, - PRIMARY KEY (`pk1`,`c3`) -) ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY RANGE (pk1) (PARTITION x1 VALUES LESS THAN (333) ENGINE = ndbcluster, PARTITION x2 VALUES LESS THAN (720) ENGINE = ndbcluster) -SELECT * FROM information_schema.partitions WHERE table_name= 't1'; -TABLE_CATALOG TABLE_SCHEMA TABLE_NAME PARTITION_NAME SUBPARTITION_NAME PARTITION_ORDINAL_POSITION SUBPARTITION_ORDINAL_POSITION PARTITION_METHOD SUBPARTITION_METHOD PARTITION_EXPRESSION SUBPARTITION_EXPRESSION PARTITION_DESCRIPTION TABLE_ROWS AVG_ROW_LENGTH DATA_LENGTH MAX_DATA_LENGTH INDEX_LENGTH DATA_FREE CREATE_TIME UPDATE_TIME CHECK_TIME CHECKSUM PARTITION_COMMENT NODEGROUP TABLESPACE_NAME -NULL test t1 p0 NULL 1 NULL HASH NULL c3 NULL NULL 0 0 0 NULL 0 0 NULL NULL NULL NULL default 0 default -NULL test t1 p1 NULL 2 NULL HASH NULL c3 NULL NULL 0 0 0 NULL 0 0 NULL NULL NULL NULL default 0 default -NULL test t1 p2 NULL 3 NULL HASH NULL c3 NULL NULL 0 0 0 NULL 0 0 NULL NULL NULL NULL default 0 default -NULL test t1 p3 NULL 4 NULL HASH NULL c3 NULL NULL 0 0 0 NULL 0 0 NULL NULL NULL NULL default 0 default -SELECT * FROM information_schema.partitions WHERE table_name= 't2'; -TABLE_CATALOG TABLE_SCHEMA TABLE_NAME PARTITION_NAME SUBPARTITION_NAME PARTITION_ORDINAL_POSITION SUBPARTITION_ORDINAL_POSITION PARTITION_METHOD SUBPARTITION_METHOD PARTITION_EXPRESSION SUBPARTITION_EXPRESSION PARTITION_DESCRIPTION TABLE_ROWS AVG_ROW_LENGTH DATA_LENGTH MAX_DATA_LENGTH INDEX_LENGTH DATA_FREE CREATE_TIME UPDATE_TIME CHECK_TIME CHECKSUM PARTITION_COMMENT NODEGROUP TABLESPACE_NAME -NULL test t2 p0 NULL 1 NULL KEY NULL c3 NULL NULL 0 0 0 NULL 0 0 NULL NULL NULL NULL default 0 default -NULL test t2 p1 NULL 2 NULL KEY NULL c3 NULL NULL 0 0 0 NULL 0 0 NULL NULL NULL NULL default 0 default -SELECT * FROM information_schema.partitions WHERE table_name= 't3'; -TABLE_CATALOG TABLE_SCHEMA TABLE_NAME PARTITION_NAME SUBPARTITION_NAME PARTITION_ORDINAL_POSITION SUBPARTITION_ORDINAL_POSITION PARTITION_METHOD SUBPARTITION_METHOD PARTITION_EXPRESSION SUBPARTITION_EXPRESSION PARTITION_DESCRIPTION TABLE_ROWS AVG_ROW_LENGTH DATA_LENGTH MAX_DATA_LENGTH INDEX_LENGTH DATA_FREE CREATE_TIME UPDATE_TIME CHECK_TIME CHECKSUM PARTITION_COMMENT NODEGROUP TABLESPACE_NAME -NULL test t3 x1 NULL 1 NULL RANGE NULL c3 NULL 105 0 0 0 NULL 0 0 NULL NULL NULL NULL default 0 default -NULL test t3 x2 NULL 2 NULL RANGE NULL c3 NULL 333 0 0 0 NULL 0 0 NULL NULL NULL NULL default 0 default -NULL test t3 x3 NULL 3 NULL RANGE NULL c3 NULL 720 0 0 0 NULL 0 0 NULL NULL NULL NULL default 0 default -SELECT * FROM information_schema.partitions WHERE table_name= 't4'; -TABLE_CATALOG TABLE_SCHEMA TABLE_NAME PARTITION_NAME SUBPARTITION_NAME PARTITION_ORDINAL_POSITION SUBPARTITION_ORDINAL_POSITION PARTITION_METHOD SUBPARTITION_METHOD PARTITION_EXPRESSION SUBPARTITION_EXPRESSION PARTITION_DESCRIPTION TABLE_ROWS AVG_ROW_LENGTH DATA_LENGTH MAX_DATA_LENGTH INDEX_LENGTH DATA_FREE CREATE_TIME UPDATE_TIME CHECK_TIME CHECKSUM PARTITION_COMMENT NODEGROUP TABLESPACE_NAME -NULL test t4 p0 NULL 1 NULL HASH NULL c3 NULL NULL 0 0 0 NULL 0 0 NULL NULL NULL NULL default 0 default -NULL test t4 p1 NULL 2 NULL HASH NULL c3 NULL NULL 0 0 0 NULL 0 0 NULL NULL NULL NULL default 0 default -SELECT * FROM information_schema.partitions WHERE table_name= 't5'; -TABLE_CATALOG TABLE_SCHEMA TABLE_NAME PARTITION_NAME SUBPARTITION_NAME PARTITION_ORDINAL_POSITION SUBPARTITION_ORDINAL_POSITION PARTITION_METHOD SUBPARTITION_METHOD PARTITION_EXPRESSION SUBPARTITION_EXPRESSION PARTITION_DESCRIPTION TABLE_ROWS AVG_ROW_LENGTH DATA_LENGTH MAX_DATA_LENGTH INDEX_LENGTH DATA_FREE CREATE_TIME UPDATE_TIME CHECK_TIME CHECKSUM PARTITION_COMMENT NODEGROUP TABLESPACE_NAME -NULL test t5 p0 NULL 1 NULL KEY NULL pk1 NULL NULL 0 0 0 NULL 0 0 NULL NULL NULL NULL default 0 default -NULL test t5 p1 NULL 2 NULL KEY NULL pk1 NULL NULL 0 0 0 NULL 0 0 NULL NULL NULL NULL default 0 default -SELECT * FROM information_schema.partitions WHERE table_name= 't6'; -TABLE_CATALOG TABLE_SCHEMA TABLE_NAME PARTITION_NAME SUBPARTITION_NAME PARTITION_ORDINAL_POSITION SUBPARTITION_ORDINAL_POSITION PARTITION_METHOD SUBPARTITION_METHOD PARTITION_EXPRESSION SUBPARTITION_EXPRESSION PARTITION_DESCRIPTION TABLE_ROWS AVG_ROW_LENGTH DATA_LENGTH MAX_DATA_LENGTH INDEX_LENGTH DATA_FREE CREATE_TIME UPDATE_TIME CHECK_TIME CHECKSUM PARTITION_COMMENT NODEGROUP TABLESPACE_NAME -NULL test t6 x1 NULL 1 NULL RANGE NULL pk1 NULL 333 0 0 0 NULL 0 0 NULL NULL NULL NULL default 0 default -NULL test t6 x2 NULL 2 NULL RANGE NULL pk1 NULL 720 0 0 0 NULL 0 0 NULL NULL NULL NULL default 0 default -SELECT COUNT(*) FROM test.t1; -COUNT(*) -250 -SELECT pk1, c2, c3, hex(c4) FROM test.t1 ORDER BY c3 LIMIT 5; -pk1 c2 c3 hex(c4) -250 Sweden, Texas 2 0 -249 Sweden, Texas 4 0 -248 Sweden, Texas 6 0 -247 Sweden, Texas 8 0 -246 Sweden, Texas 10 0 -SELECT COUNT(*) FROM test.t2; -COUNT(*) -250 -SELECT pk1, c2, c3, hex(c4) FROM test.t2 ORDER BY c3 LIMIT 5; -pk1 c2 c3 hex(c4) -250 Sweden, Texas, ITALY, Kyle, JO, JBM,TU 1 1 -249 Sweden, Texas, ITALY, Kyle, JO, JBM,TU 3 1 -248 Sweden, Texas, ITALY, Kyle, JO, JBM,TU 5 1 -247 Sweden, Texas, ITALY, Kyle, JO, JBM,TU 7 1 -246 Sweden, Texas, ITALY, Kyle, JO, JBM,TU 9 1 -SELECT COUNT(*) FROM test.t3; -COUNT(*) -250 -SELECT pk1, c2, c3, hex(c4) FROM test.t3 ORDER BY c3 LIMIT 5; -pk1 c2 c3 hex(c4) -250 TEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXAS, ITALY, Kyle, JO, JBM,TU 0 1 -249 TEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXAS, ITALY, Kyle, JO, JBM,TU 2 1 -248 TEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXAS, ITALY, Kyle, JO, JBM,TU 4 1 -247 TEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXAS, ITALY, Kyle, JO, JBM,TU 6 1 -246 TEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXAS, ITALY, Kyle, JO, JBM,TU 8 1 -SELECT COUNT(*) FROM test.t4; -COUNT(*) -250 -SELECT pk1, c2, c3, hex(c4) FROM test.t4 ORDER BY c3 LIMIT 5; -pk1 c2 c3 hex(c4) -250 Sweden, Texas 2 0 -249 Sweden, Texas 4 0 -248 Sweden, Texas 6 0 -247 Sweden, Texas 8 0 -246 Sweden, Texas 10 0 -SELECT COUNT(*) FROM test.t5; -COUNT(*) -250 -SELECT pk1, c2, c3, hex(c4) FROM test.t5 ORDER BY c3 LIMIT 5; -pk1 c2 c3 hex(c4) -250 Sweden, Texas, ITALY, Kyle, JO, JBM,TU 1 1 -249 Sweden, Texas, ITALY, Kyle, JO, JBM,TU 3 1 -248 Sweden, Texas, ITALY, Kyle, JO, JBM,TU 5 1 -247 Sweden, Texas, ITALY, Kyle, JO, JBM,TU 7 1 -246 Sweden, Texas, ITALY, Kyle, JO, JBM,TU 9 1 -SELECT COUNT(*) FROM test.t6; -COUNT(*) -250 -SELECT pk1, c2, c3, hex(c4) FROM test.t6 ORDER BY c3 LIMIT 5; -pk1 c2 c3 hex(c4) -250 TEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXAS, ITALY, Kyle, JO, JBM,TU 0 1 -249 TEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXAS, ITALY, Kyle, JO, JBM,TU 2 1 -248 TEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXAS, ITALY, Kyle, JO, JBM,TU 4 1 -247 TEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXAS, ITALY, Kyle, JO, JBM,TU 6 1 -246 TEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXAS, ITALY, Kyle, JO, JBM,TU 8 1 -DROP TABLE test.t1; -DROP TABLE test.t2; -DROP TABLE test.t3; -DROP TABLE test.t4; -DROP TABLE test.t5; -DROP TABLE test.t6; -ALTER TABLESPACE table_space1 DROP DATAFILE './table_space1/datafile.dat' ENGINE=NDB; -ALTER TABLESPACE table_space2 DROP DATAFILE './table_space2/datafile.dat' ENGINE=NDB; -DROP TABLESPACE table_space1 ENGINE = NDB; -DROP TABLESPACE table_space2 ENGINE = NDB; -DROP LOGFILE GROUP log_group1 ENGINE = NDB; diff --git a/mysql-test/r/ndb_dd_basic.result b/mysql-test/r/ndb_dd_basic.result index 008be3aa79f..6c10fbe63b3 100644 --- a/mysql-test/r/ndb_dd_basic.result +++ b/mysql-test/r/ndb_dd_basic.result @@ -11,7 +11,7 @@ ADD UNDOFILE 'undofile02.dat' INITIAL_SIZE = 4M ENGINE=XYZ; Warnings: -Error 1266 Using storage engine MyISAM for table 'lg1' +Error 1286 Unknown table engine 'XYZ' Error 1465 Table storage engine 'MyISAM' does not support the create option 'TABLESPACE or LOGFILE GROUP' CREATE TABLESPACE ts1 ADD DATAFILE 'datafile.dat' diff --git a/mysql-test/r/ndb_index_unique.result b/mysql-test/r/ndb_index_unique.result index bd42595f060..88c6e00e215 100644 --- a/mysql-test/r/ndb_index_unique.result +++ b/mysql-test/r/ndb_index_unique.result @@ -144,7 +144,7 @@ b int unsigned not null, c int unsigned, UNIQUE (b, c) USING HASH ) engine=ndbcluster; -ERROR 42000: Column 'c' is used with UNIQUE or INDEX but is not defined as NOT NULL +ERROR 42000: Table handler doesn't support NULL in given index. Please change column 'c' to be NOT NULL or use another handler CREATE TABLE t3 ( a int unsigned NOT NULL, b int unsigned not null, diff --git a/mysql-test/r/ndb_lock.result b/mysql-test/r/ndb_lock.result index ac93f15dac3..197995505a1 100644 --- a/mysql-test/r/ndb_lock.result +++ b/mysql-test/r/ndb_lock.result @@ -63,6 +63,86 @@ pk u o 5 5 5 insert into t1 values (1,1,1); drop table t1; +create table t1 (x integer not null primary key, y varchar(32), z integer, key(z)) engine = ndb; +insert into t1 values (1,'one',1), (2,'two',2),(3,"three",3); +begin; +select * from t1 where x = 1 for update; +x y z +1 one 1 +begin; +select * from t1 where x = 2 for update; +x y z +2 two 2 +select * from t1 where x = 1 for update; +ERROR HY000: Lock wait timeout exceeded; try restarting transaction +rollback; +commit; +begin; +select * from t1 where y = 'one' or y = 'three' order by x for update; +x y z +1 one 1 +3 three 3 +begin; +select * from t1 where x = 1 for update; +ERROR HY000: Lock wait timeout exceeded; try restarting transaction +rollback; +commit; +begin; +select * from t1 where z > 1 and z < 3 for update; +x y z +2 two 2 +begin; +select * from t1 where x = 1 for update; +x y z +1 one 1 +select * from t1 where x = 2 for update; +ERROR HY000: Lock wait timeout exceeded; try restarting transaction +rollback; +commit; +begin; +select * from t1 where x = 1 lock in share mode; +x y z +1 one 1 +begin; +select * from t1 where x = 1 lock in share mode; +x y z +1 one 1 +select * from t1 where x = 2 for update; +x y z +2 two 2 +select * from t1 where x = 1 for update; +ERROR HY000: Lock wait timeout exceeded; try restarting transaction +rollback; +commit; +begin; +select * from t1 where y = 'one' or y = 'three' order by x lock in share mode; +x y z +1 one 1 +3 three 3 +begin; +select * from t1 where y = 'one' lock in share mode; +x y z +1 one 1 +select * from t1 where x = 1 for update; +ERROR HY000: Lock wait timeout exceeded; try restarting transaction +rollback; +commit; +begin; +select * from t1 where z > 1 and z < 3 lock in share mode; +x y z +2 two 2 +begin; +select * from t1 where z = 1 lock in share mode; +x y z +1 one 1 +select * from t1 where x = 1 for update; +x y z +1 one 1 +select * from t1 where x = 2 for update; +ERROR HY000: Lock wait timeout exceeded; try restarting transaction +rollback; +commit; +drop table t1; create table t3 (id2 int) engine=ndb; lock tables t3 write; unlock tables; diff --git a/mysql-test/r/ndb_rename.result b/mysql-test/r/ndb_rename.result new file mode 100644 index 00000000000..2cc2dfb3ff1 --- /dev/null +++ b/mysql-test/r/ndb_rename.result @@ -0,0 +1,24 @@ +DROP TABLE IF EXISTS t1,t2; +drop database if exists mysqltest; +CREATE TABLE t1 ( +pk1 INT NOT NULL PRIMARY KEY, +attr1 INT NOT NULL, +attr2 INT, +attr3 VARCHAR(10), +INDEX i1(attr1) +) ENGINE=ndbcluster; +INSERT INTO t1 VALUES (0,0,0,"zero"),(1,1,1,"one"),(2,2,2,"two"); +SELECT * FROM t1 WHERE attr1 = 1; +pk1 attr1 attr2 attr3 +1 1 1 one +alter table t1 rename t2; +SELECT * FROM t2 WHERE attr1 = 1; +pk1 attr1 attr2 attr3 +1 1 1 one +create database ndbtest; +alter table t2 rename ndbtest.t2; +SELECT * FROM ndbtest.t2 WHERE attr1 = 1; +pk1 attr1 attr2 attr3 +1 1 1 one +drop table ndbtest.t2; +drop database ndbtest; diff --git a/mysql-test/r/ndb_replace.result b/mysql-test/r/ndb_replace.result index 5e49968ca64..cdcd935bfcc 100644 --- a/mysql-test/r/ndb_replace.result +++ b/mysql-test/r/ndb_replace.result @@ -30,4 +30,4 @@ REPLACE INTO t1 (i,j) VALUES (17,2); SELECT * from t1 ORDER BY i; i j k 3 1 42 -17 2 24 +17 2 NULL diff --git a/mysql-test/r/ndb_truncate.result b/mysql-test/r/ndb_truncate.result index 38f3a78029c..811e5e3afeb 100644 --- a/mysql-test/r/ndb_truncate.result +++ b/mysql-test/r/ndb_truncate.result @@ -1,14 +1,23 @@ -DROP TABLE IF EXISTS t2; -CREATE TABLE t2 ( -a bigint unsigned NOT NULL PRIMARY KEY, +DROP TABLE IF EXISTS t1, t2; +CREATE TABLE t1 ( +a bigint unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY, b int unsigned not null, c int unsigned ) engine=ndbcluster; -select count(*) from t2; +select count(*) from t1; count(*) 5000 -truncate table t2; -select count(*) from t2; +select * from t1 order by a limit 2; +a b c +1 509 2500 +2 510 7 +truncate table t1; +select count(*) from t1; count(*) 0 -drop table t2; +insert into t1 values(NULL,1,1),(NULL,2,2); +select * from t1 order by a; +a b c +1 1 1 +2 2 2 +drop table t1; diff --git a/mysql-test/r/partition.result b/mysql-test/r/partition.result index 2aacbd23b1e..025d9f46412 100644 --- a/mysql-test/r/partition.result +++ b/mysql-test/r/partition.result @@ -8,6 +8,23 @@ create table t1 (a int) partition by key(a) (partition p0 engine = MEMORY); drop table t1; +create table t1 (a int) +partition by range (a) +subpartition by key (a) +(partition p0 values less than (1)); +alter table t1 add partition (partition p1 values less than (2)); +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` int(11) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY RANGE (a) SUBPARTITION BY KEY (a) (PARTITION p0 VALUES LESS THAN (1) ENGINE = MyISAM, PARTITION p1 VALUES LESS THAN (2) ENGINE = MyISAM) +alter table t1 reorganize partition p1 into (partition p1 values less than (3)); +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` int(11) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY RANGE (a) SUBPARTITION BY KEY (a) (PARTITION p0 VALUES LESS THAN (1) ENGINE = MyISAM, PARTITION p1 VALUES LESS THAN (3) ENGINE = MyISAM) +drop table t1; CREATE TABLE t1 ( a int not null, b int not null, @@ -332,25 +349,25 @@ show create table t1; Table Create Table t1 CREATE TABLE `t1` ( `a` int(11) DEFAULT NULL -) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY KEY (a) (PARTITION p0 ENGINE = MyISAM, PARTITION p1 ENGINE = MyISAM) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY KEY (a) (PARTITION p0 ENGINE = MyISAM, PARTITION p1 ENGINE = MyISAM) alter table t1; show create table t1; Table Create Table t1 CREATE TABLE `t1` ( `a` int(11) DEFAULT NULL -) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY KEY (a) (PARTITION p0 ENGINE = MyISAM, PARTITION p1 ENGINE = MyISAM) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY KEY (a) (PARTITION p0 ENGINE = MyISAM, PARTITION p1 ENGINE = MyISAM) alter table t1 engine=myisam; show create table t1; Table Create Table t1 CREATE TABLE `t1` ( `a` int(11) DEFAULT NULL -) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY KEY (a) (PARTITION p0 ENGINE = MyISAM, PARTITION p1 ENGINE = MyISAM) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY KEY (a) (PARTITION p0 ENGINE = MyISAM, PARTITION p1 ENGINE = MyISAM) alter table t1 engine=heap; show create table t1; Table Create Table t1 CREATE TABLE `t1` ( `a` int(11) DEFAULT NULL -) ENGINE=MEMORY DEFAULT CHARSET=latin1 PARTITION BY KEY (a) (PARTITION p0 ENGINE = MEMORY, PARTITION p1 ENGINE = MEMORY) +) ENGINE=MEMORY DEFAULT CHARSET=latin1 PARTITION BY KEY (a) (PARTITION p0 ENGINE = MEMORY, PARTITION p1 ENGINE = MEMORY) alter table t1 remove partitioning; show create table t1; Table Create Table @@ -367,7 +384,7 @@ show create table t1; Table Create Table t1 CREATE TABLE `t1` ( `a` int(11) DEFAULT NULL -) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY KEY (a) (PARTITION p0 ENGINE = MyISAM, PARTITION p1 ENGINE = MyISAM) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY KEY (a) (PARTITION p0 ENGINE = MyISAM, PARTITION p1 ENGINE = MyISAM) alter table t1 add column b int remove partitioning; show create table t1; Table Create Table @@ -384,7 +401,7 @@ Table Create Table t1 CREATE TABLE `t1` ( `a` int(11) DEFAULT NULL, `b` int(11) DEFAULT NULL -) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY KEY (a) (PARTITION p0 ENGINE = MyISAM, PARTITION p1 ENGINE = MyISAM) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY KEY (a) (PARTITION p0 ENGINE = MyISAM, PARTITION p1 ENGINE = MyISAM) alter table t1 engine=heap partition by key(a) @@ -394,7 +411,7 @@ Table Create Table t1 CREATE TABLE `t1` ( `a` int(11) DEFAULT NULL, `b` int(11) DEFAULT NULL -) ENGINE=MEMORY DEFAULT CHARSET=latin1 PARTITION BY KEY (a) (PARTITION p0 ENGINE = MEMORY, PARTITION p1 ENGINE = MEMORY) +) ENGINE=MEMORY DEFAULT CHARSET=latin1 PARTITION BY KEY (a) (PARTITION p0 ENGINE = MEMORY, PARTITION p1 ENGINE = MEMORY) alter table t1 engine=myisam, add column c int remove partitioning; show create table t1; Table Create Table @@ -413,7 +430,7 @@ t1 CREATE TABLE `t1` ( `a` int(11) DEFAULT NULL, `b` int(11) DEFAULT NULL, `c` int(11) DEFAULT NULL -) ENGINE=MEMORY DEFAULT CHARSET=latin1 PARTITION BY KEY (a) (PARTITION p0 ENGINE = MEMORY, PARTITION p1 ENGINE = MEMORY) +) ENGINE=MEMORY DEFAULT CHARSET=latin1 PARTITION BY KEY (a) (PARTITION p0 ENGINE = MEMORY, PARTITION p1 ENGINE = MEMORY) alter table t1 partition by key (a) (partition p0, partition p1); @@ -423,7 +440,7 @@ t1 CREATE TABLE `t1` ( `a` int(11) DEFAULT NULL, `b` int(11) DEFAULT NULL, `c` int(11) DEFAULT NULL -) ENGINE=MEMORY DEFAULT CHARSET=latin1 PARTITION BY KEY (a) (PARTITION p0 ENGINE = MEMORY, PARTITION p1 ENGINE = MEMORY) +) ENGINE=MEMORY DEFAULT CHARSET=latin1 PARTITION BY KEY (a) (PARTITION p0 ENGINE = MEMORY, PARTITION p1 ENGINE = MEMORY) alter table t1 engine=heap partition by key (a) @@ -434,7 +451,7 @@ t1 CREATE TABLE `t1` ( `a` int(11) DEFAULT NULL, `b` int(11) DEFAULT NULL, `c` int(11) DEFAULT NULL -) ENGINE=MEMORY DEFAULT CHARSET=latin1 PARTITION BY KEY (a) (PARTITION p0 ENGINE = MEMORY, PARTITION p1 ENGINE = MEMORY) +) ENGINE=MEMORY DEFAULT CHARSET=latin1 PARTITION BY KEY (a) (PARTITION p0 ENGINE = MEMORY, PARTITION p1 ENGINE = MEMORY) alter table t1 partition by key(a) (partition p0, partition p1 engine=heap); @@ -579,14 +596,14 @@ show create table t1; Table Create Table t1 CREATE TABLE `t1` ( `a` int(11) DEFAULT NULL -) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY RANGE (a) SUBPARTITION BY HASH (a) (PARTITION p0 VALUES LESS THAN (100) ) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY RANGE (a) SUBPARTITION BY HASH (a) (PARTITION p0 VALUES LESS THAN (100) ENGINE = MyISAM) alter table t1 add partition (partition p1 values less than (200) (subpartition subpart21)); show create table t1; Table Create Table t1 CREATE TABLE `t1` ( `a` int(11) DEFAULT NULL -) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY RANGE (a) SUBPARTITION BY HASH (a) (PARTITION p0 VALUES LESS THAN (100) (SUBPARTITION p0sp0 ENGINE = MyISAM), PARTITION p1 VALUES LESS THAN (200) (SUBPARTITION subpart21 ENGINE = MyISAM)) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY RANGE (a) SUBPARTITION BY HASH (a) (PARTITION p0 VALUES LESS THAN (100) (SUBPARTITION p0sp0 ENGINE = MyISAM), PARTITION p1 VALUES LESS THAN (200) (SUBPARTITION subpart21 ENGINE = MyISAM)) drop table t1; create table t1 (a int) partition by key (a); @@ -600,7 +617,7 @@ show create table t1; Table Create Table t1 CREATE TABLE `t1` ( `a` int(11) DEFAULT NULL -) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY KEY (a) (PARTITION p0 ENGINE = MyISAM, PARTITION p1 ENGINE = MyISAM) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY KEY (a) (PARTITION p0 ENGINE = MyISAM, PARTITION p1 ENGINE = MyISAM) drop table t1; create table t1 (a int, b int) partition by range (a) @@ -901,6 +918,28 @@ insert into t1 values (1); create index inx1 on t1(a); drop table t1; create table t1 (a int) +PARTITION BY KEY (a) +(PARTITION p0); +set session sql_mode='no_table_options'; +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` int(11) DEFAULT NULL +) PARTITION BY KEY (a) (PARTITION p0) +set session sql_mode=''; +drop table t1; +create table t1 (a int) +partition by key (a) +(partition p1 engine = innodb); +alter table t1 rebuild partition p1; +alter table t1 rebuild partition p1; +alter table t1 rebuild partition p1; +alter table t1 rebuild partition p1; +alter table t1 rebuild partition p1; +alter table t1 rebuild partition p1; +alter table t1 rebuild partition p1; +drop table t1; +create table t1 (a int) partition by key (a) (partition p0 engine = MERGE); ERROR HY000: MyISAM Merge handler cannot be used in partitioned tables @@ -941,4 +980,16 @@ OPTIMIZE TABLE t1; Table Op Msg_type Msg_text test.t1 optimize note The storage engine for the table doesn't support optimize drop table t1; +create database db99; +use db99; +create table t1 (a int not null) +engine=archive +partition by list (a) +(partition p0 values in (1), partition p1 values in (2)); +insert into t1 values (1), (2); +create index inx on t1 (a); +alter table t1 add partition (partition p2 values in (3)); +alter table t1 drop partition p2; +use test; +drop database db99; End of 5.1 tests diff --git a/mysql-test/r/partition_02myisam.result b/mysql-test/r/partition_02myisam.result index a7786bfcfbd..35ee0fe22ee 100644 --- a/mysql-test/r/partition_02myisam.result +++ b/mysql-test/r/partition_02myisam.result @@ -147,7 +147,7 @@ Table Create Table t1 CREATE TABLE `t1` ( `f1` int(11) DEFAULT NULL, `f2` char(20) DEFAULT NULL -) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY HASH (f1) (PARTITION part1 ENGINE = MyISAM, PARTITION part2 ENGINE = MyISAM) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY HASH (f1) (PARTITION part1 ENGINE = MyISAM, PARTITION part2 ENGINE = MyISAM) SELECT COUNT(*) = 0 AS my_value FROM t1; my_value 1 @@ -205,7 +205,7 @@ Table Create Table t1 CREATE TABLE `t1` ( `f1` int(11) DEFAULT NULL, `f2` char(20) DEFAULT NULL -) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY RANGE (f1) SUBPARTITION BY HASH (f1) (PARTITION part1 VALUES LESS THAN (100) (SUBPARTITION subpart11 ENGINE = MyISAM, SUBPARTITION subpart12 ENGINE = MyISAM), PARTITION part2 VALUES LESS THAN (2147483647) (SUBPARTITION subpart21 ENGINE = MyISAM, SUBPARTITION subpart22 ENGINE = MyISAM)) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY RANGE (f1) SUBPARTITION BY HASH (f1) (PARTITION part1 VALUES LESS THAN (100) (SUBPARTITION subpart11 ENGINE = MyISAM, SUBPARTITION subpart12 ENGINE = MyISAM), PARTITION part2 VALUES LESS THAN (2147483647) (SUBPARTITION subpart21 ENGINE = MyISAM, SUBPARTITION subpart22 ENGINE = MyISAM)) SELECT COUNT(*) = 0 AS my_value FROM t1; my_value 1 @@ -260,7 +260,7 @@ Table Create Table t1 CREATE TABLE `t1` ( `f1` int(11) DEFAULT NULL, `f2` char(20) DEFAULT NULL -) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY HASH (f1) (PARTITION part1 ENGINE = MyISAM, PARTITION part2 ENGINE = MyISAM) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY HASH (f1) (PARTITION part1 ENGINE = MyISAM, PARTITION part2 ENGINE = MyISAM) SELECT COUNT(*) = 0 AS my_value FROM t1; my_value 1 @@ -313,7 +313,7 @@ Table Create Table t1 CREATE TABLE `t1` ( `f1` int(11) DEFAULT NULL, `f2` char(20) DEFAULT NULL -) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY HASH (f1) (PARTITION part1 ENGINE = MyISAM, PARTITION part2 ENGINE = MyISAM) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY HASH (f1) (PARTITION part1 ENGINE = MyISAM, PARTITION part2 ENGINE = MyISAM) SELECT COUNT(*) = 0 AS my_value FROM t1; my_value 1 @@ -371,7 +371,7 @@ Table Create Table t1 CREATE TABLE `t1` ( `f1` int(11) DEFAULT NULL, `f2` char(20) DEFAULT NULL -) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY RANGE (f1) SUBPARTITION BY HASH (f1) (PARTITION part1 VALUES LESS THAN (100) (SUBPARTITION subpart11 ENGINE = MyISAM, SUBPARTITION subpart12 ENGINE = MyISAM), PARTITION part2 VALUES LESS THAN (2147483647) (SUBPARTITION subpart21 ENGINE = MyISAM, SUBPARTITION subpart22 ENGINE = MyISAM)) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY RANGE (f1) SUBPARTITION BY HASH (f1) (PARTITION part1 VALUES LESS THAN (100) (SUBPARTITION subpart11 ENGINE = MyISAM, SUBPARTITION subpart12 ENGINE = MyISAM), PARTITION part2 VALUES LESS THAN (2147483647) (SUBPARTITION subpart21 ENGINE = MyISAM, SUBPARTITION subpart22 ENGINE = MyISAM)) SELECT COUNT(*) = 0 AS my_value FROM t1; my_value 1 @@ -429,7 +429,7 @@ Table Create Table t1 CREATE TABLE `t1` ( `f1` int(11) DEFAULT NULL, `f2` char(20) DEFAULT NULL -) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY RANGE (f1) SUBPARTITION BY HASH (f1) (PARTITION part1 VALUES LESS THAN (100) (SUBPARTITION subpart11 ENGINE = MyISAM, SUBPARTITION subpart12 ENGINE = MyISAM), PARTITION part2 VALUES LESS THAN (2147483647) (SUBPARTITION subpart21 ENGINE = MyISAM, SUBPARTITION subpart22 ENGINE = MyISAM)) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY RANGE (f1) SUBPARTITION BY HASH (f1) (PARTITION part1 VALUES LESS THAN (100) (SUBPARTITION subpart11 ENGINE = MyISAM, SUBPARTITION subpart12 ENGINE = MyISAM), PARTITION part2 VALUES LESS THAN (2147483647) (SUBPARTITION subpart21 ENGINE = MyISAM, SUBPARTITION subpart22 ENGINE = MyISAM)) SELECT COUNT(*) = 0 AS my_value FROM t1; my_value 1 @@ -489,7 +489,7 @@ Table Create Table t1 CREATE TABLE `t1` ( `f1` int(11) DEFAULT NULL, `f2` char(20) DEFAULT NULL -) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY RANGE (f1) SUBPARTITION BY HASH (f1) (PARTITION part1 VALUES LESS THAN (100) (SUBPARTITION subpart11 ENGINE = MyISAM, SUBPARTITION subpart12 ENGINE = MyISAM), PARTITION part2 VALUES LESS THAN (2147483647) (SUBPARTITION subpart21 ENGINE = MyISAM, SUBPARTITION subpart22 ENGINE = MyISAM)) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY RANGE (f1) SUBPARTITION BY HASH (f1) (PARTITION part1 VALUES LESS THAN (100) (SUBPARTITION subpart11 ENGINE = MyISAM, SUBPARTITION subpart12 ENGINE = MyISAM), PARTITION part2 VALUES LESS THAN (2147483647) (SUBPARTITION subpart21 ENGINE = MyISAM, SUBPARTITION subpart22 ENGINE = MyISAM)) SELECT COUNT(*) = 0 AS my_value FROM t1; my_value 1 @@ -547,7 +547,7 @@ Table Create Table t1 CREATE TABLE `t1` ( `f1` int(11) DEFAULT NULL, `f2` char(20) DEFAULT NULL -) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY RANGE (f1) SUBPARTITION BY HASH (f1) (PARTITION part1 VALUES LESS THAN (100) (SUBPARTITION subpart11 ENGINE = MyISAM, SUBPARTITION subpart12 ENGINE = MyISAM), PARTITION part2 VALUES LESS THAN (2147483647) (SUBPARTITION subpart21 ENGINE = MyISAM, SUBPARTITION subpart22 ENGINE = MyISAM)) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY RANGE (f1) SUBPARTITION BY HASH (f1) (PARTITION part1 VALUES LESS THAN (100) (SUBPARTITION subpart11 ENGINE = MyISAM, SUBPARTITION subpart12 ENGINE = MyISAM), PARTITION part2 VALUES LESS THAN (2147483647) (SUBPARTITION subpart21 ENGINE = MyISAM, SUBPARTITION subpart22 ENGINE = MyISAM)) SELECT COUNT(*) = 0 AS my_value FROM t1; my_value 1 @@ -603,7 +603,7 @@ Table Create Table t1 CREATE TABLE `t1` ( `f1` int(11) DEFAULT NULL, `f2` char(20) DEFAULT NULL -) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY HASH (f1) (PARTITION part1 ENGINE = MyISAM, PARTITION part2 ENGINE = MyISAM) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY HASH (f1) (PARTITION part1 ENGINE = MyISAM, PARTITION part2 ENGINE = MyISAM) SELECT COUNT(*) = 0 AS my_value FROM t1; my_value 1 @@ -661,7 +661,7 @@ Table Create Table t1 CREATE TABLE `t1` ( `f1` int(11) DEFAULT NULL, `f2` char(20) DEFAULT NULL -) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY RANGE (f1) SUBPARTITION BY HASH (f1) (PARTITION part1 VALUES LESS THAN (100) (SUBPARTITION subpart11 ENGINE = MyISAM, SUBPARTITION subpart12 ENGINE = MyISAM), PARTITION part2 VALUES LESS THAN (2147483647) (SUBPARTITION subpart21 ENGINE = MyISAM, SUBPARTITION subpart22 ENGINE = MyISAM)) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY RANGE (f1) SUBPARTITION BY HASH (f1) (PARTITION part1 VALUES LESS THAN (100) (SUBPARTITION subpart11 ENGINE = MyISAM, SUBPARTITION subpart12 ENGINE = MyISAM), PARTITION part2 VALUES LESS THAN (2147483647) (SUBPARTITION subpart21 ENGINE = MyISAM, SUBPARTITION subpart22 ENGINE = MyISAM)) SELECT COUNT(*) = 0 AS my_value FROM t1; my_value 1 @@ -721,7 +721,7 @@ Table Create Table t1 CREATE TABLE `t1` ( `f1` int(11) DEFAULT NULL, `f2` char(20) DEFAULT NULL -) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY RANGE (f1) SUBPARTITION BY HASH (f1) (PARTITION part1 VALUES LESS THAN (100) (SUBPARTITION subpart11 ENGINE = MyISAM, SUBPARTITION subpart12 ENGINE = MyISAM), PARTITION part2 VALUES LESS THAN (2147483647) (SUBPARTITION subpart21 ENGINE = MyISAM, SUBPARTITION subpart22 ENGINE = MyISAM)) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY RANGE (f1) SUBPARTITION BY HASH (f1) (PARTITION part1 VALUES LESS THAN (100) (SUBPARTITION subpart11 ENGINE = MyISAM, SUBPARTITION subpart12 ENGINE = MyISAM), PARTITION part2 VALUES LESS THAN (2147483647) (SUBPARTITION subpart21 ENGINE = MyISAM, SUBPARTITION subpart22 ENGINE = MyISAM)) SELECT COUNT(*) = 0 AS my_value FROM t1; my_value 1 @@ -833,7 +833,7 @@ Table Create Table t1 CREATE TABLE `t1` ( `f1` int(11) DEFAULT NULL, `f2` char(20) DEFAULT NULL -) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY HASH (f1) (PARTITION part1 ENGINE = MyISAM, PARTITION part2 ENGINE = MyISAM) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY HASH (f1) (PARTITION part1 ENGINE = MyISAM, PARTITION part2 ENGINE = MyISAM) SELECT COUNT(*) = 0 AS my_value FROM t1; my_value 1 @@ -891,7 +891,7 @@ Table Create Table t1 CREATE TABLE `t1` ( `f1` int(11) DEFAULT NULL, `f2` char(20) DEFAULT NULL -) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY RANGE (f1) SUBPARTITION BY HASH (f1) (PARTITION part1 VALUES LESS THAN (100) (SUBPARTITION subpart11 ENGINE = MyISAM, SUBPARTITION subpart12 ENGINE = MyISAM), PARTITION part2 VALUES LESS THAN (200) (SUBPARTITION subpart21 ENGINE = MyISAM, SUBPARTITION subpart22 ENGINE = MyISAM), PARTITION part3 VALUES LESS THAN (2147483647) (SUBPARTITION subpart31 ENGINE = MyISAM, SUBPARTITION subpart32 ENGINE = MyISAM)) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY RANGE (f1) SUBPARTITION BY HASH (f1) (PARTITION part1 VALUES LESS THAN (100) (SUBPARTITION subpart11 ENGINE = MyISAM, SUBPARTITION subpart12 ENGINE = MyISAM), PARTITION part2 VALUES LESS THAN (200) (SUBPARTITION subpart21 ENGINE = MyISAM, SUBPARTITION subpart22 ENGINE = MyISAM), PARTITION part3 VALUES LESS THAN (2147483647) (SUBPARTITION subpart31 ENGINE = MyISAM, SUBPARTITION subpart32 ENGINE = MyISAM)) SELECT COUNT(*) = 0 AS my_value FROM t1; my_value 1 @@ -996,7 +996,7 @@ Table Create Table t1 CREATE TABLE `t1` ( `f1` int(11) DEFAULT NULL, `f2` char(20) DEFAULT NULL -) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY RANGE (f1) SUBPARTITION BY HASH (f1) SUBPARTITIONS 2 (PARTITION part1 VALUES LESS THAN (100) , PARTITION part2 VALUES LESS THAN (2147483647) ) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY RANGE (f1) SUBPARTITION BY HASH (f1) SUBPARTITIONS 2 (PARTITION part1 VALUES LESS THAN (100) ENGINE = MyISAM, PARTITION part2 VALUES LESS THAN (2147483647) ENGINE = MyISAM) SELECT COUNT(*) = 0 AS my_value FROM t1; my_value 1 @@ -1098,7 +1098,7 @@ Table Create Table t1 CREATE TABLE `t1` ( `f1` int(11) DEFAULT NULL, `f2` char(20) DEFAULT NULL -) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY RANGE (f1) SUBPARTITION BY HASH (f1) SUBPARTITIONS 1 (PARTITION part1 VALUES LESS THAN (100) , PARTITION part2 VALUES LESS THAN (2147483647) ) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY RANGE (f1) SUBPARTITION BY HASH (f1) SUBPARTITIONS 1 (PARTITION part1 VALUES LESS THAN (100) ENGINE = MyISAM, PARTITION part2 VALUES LESS THAN (2147483647) ENGINE = MyISAM) SELECT COUNT(*) = 0 AS my_value FROM t1; my_value 1 @@ -1304,7 +1304,7 @@ Table Create Table t1 CREATE TABLE `t1` ( `f1` int(11) DEFAULT NULL, `f2` char(20) DEFAULT NULL -) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY HASH (f1) (PARTITION part1 ENGINE = MyISAM, PARTITION part2 ENGINE = MyISAM) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY HASH (f1) (PARTITION part1 ENGINE = MyISAM, PARTITION part2 ENGINE = MyISAM) DROP TABLE t1; CREATE TABLE t1 ( f1 INTEGER, f2 char(20)) PARTITION BY RANGE(f1) PARTITIONS 2 @@ -1319,7 +1319,7 @@ Table Create Table t1 CREATE TABLE `t1` ( `f1` int(11) DEFAULT NULL, `f2` char(20) DEFAULT NULL -) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY RANGE (f1) SUBPARTITION BY HASH (f1) (PARTITION part1 VALUES LESS THAN (1000) (SUBPARTITION subpart11 ENGINE = MyISAM, SUBPARTITION subpart12 ENGINE = MyISAM), PARTITION part2 VALUES LESS THAN (2147483647) (SUBPARTITION subpart21 ENGINE = MyISAM, SUBPARTITION subpart22 ENGINE = MyISAM)) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY RANGE (f1) SUBPARTITION BY HASH (f1) (PARTITION part1 VALUES LESS THAN (1000) (SUBPARTITION subpart11 ENGINE = MyISAM, SUBPARTITION subpart12 ENGINE = MyISAM), PARTITION part2 VALUES LESS THAN (2147483647) (SUBPARTITION subpart21 ENGINE = MyISAM, SUBPARTITION subpart22 ENGINE = MyISAM)) DROP TABLE t1; # 3.3.2 (positive) number of partition/subpartition , # 0 (= no) named partition/subpartition @@ -1454,7 +1454,7 @@ Table Create Table t1 CREATE TABLE `t1` ( `f1` int(11) DEFAULT NULL, `f2` char(20) DEFAULT NULL -) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY HASH (f1) (PARTITION p0 ENGINE = MyISAM, PARTITION part1 ENGINE = MyISAM) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY HASH (f1) (PARTITION p0 ENGINE = MyISAM, PARTITION part1 ENGINE = MyISAM) INSERT INTO t1 SELECT * FROM t0_template WHERE f1 BETWEEN 100 AND 200; SELECT (COUNT(*) = 200) AND (MIN(f1) = 1) AND (MAX(f1) = 200) AS my_value FROM t1; @@ -1502,7 +1502,7 @@ Table Create Table t1 CREATE TABLE `t1` ( `f1` int(11) DEFAULT NULL, `f2` char(20) DEFAULT NULL -) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY HASH (f1) (PARTITION part1 ENGINE = MyISAM, PARTITION part3 ENGINE = MyISAM) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY HASH (f1) (PARTITION part1 ENGINE = MyISAM, PARTITION part3 ENGINE = MyISAM) INSERT INTO t1 SELECT * FROM t0_template WHERE f1 BETWEEN 1 AND 100 - 1; ALTER TABLE t1 ADD PARTITION (PARTITION part0); SHOW CREATE TABLE t1; @@ -1510,7 +1510,7 @@ Table Create Table t1 CREATE TABLE `t1` ( `f1` int(11) DEFAULT NULL, `f2` char(20) DEFAULT NULL -) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY HASH (f1) (PARTITION part1 ENGINE = MyISAM, PARTITION part3 ENGINE = MyISAM, PARTITION part0 ENGINE = MyISAM) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY HASH (f1) (PARTITION part1 ENGINE = MyISAM, PARTITION part3 ENGINE = MyISAM, PARTITION part0 ENGINE = MyISAM) INSERT INTO t1 SELECT * FROM t0_template WHERE f1 BETWEEN 100 AND 200; SELECT (COUNT(*) = 200) AND (MIN(f1) = 1) AND (MAX(f1) = 200) AS my_value FROM t1; @@ -1557,7 +1557,7 @@ Table Create Table t1 CREATE TABLE `t1` ( `f1` int(11) DEFAULT NULL, `f2` char(20) DEFAULT NULL -) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY HASH (f1) (PARTITION part1 ENGINE = MyISAM, PARTITION part3 ENGINE = MyISAM, PARTITION part0 ENGINE = MyISAM, PARTITION part2 ENGINE = MyISAM) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY HASH (f1) (PARTITION part1 ENGINE = MyISAM, PARTITION part3 ENGINE = MyISAM, PARTITION part0 ENGINE = MyISAM, PARTITION part2 ENGINE = MyISAM) INSERT INTO t1 SELECT * FROM t0_template WHERE f1 BETWEEN 100 AND 200; SELECT (COUNT(*) = 200) AND (MIN(f1) = 1) AND (MAX(f1) = 200) AS my_value FROM t1; @@ -1603,7 +1603,7 @@ Table Create Table t1 CREATE TABLE `t1` ( `f1` int(11) DEFAULT NULL, `f2` char(20) DEFAULT NULL -) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY HASH (f1) (PARTITION part1 ENGINE = MyISAM, PARTITION part3 ENGINE = MyISAM, PARTITION part0 ENGINE = MyISAM, PARTITION part2 ENGINE = MyISAM) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY HASH (f1) (PARTITION part1 ENGINE = MyISAM, PARTITION part3 ENGINE = MyISAM, PARTITION part0 ENGINE = MyISAM, PARTITION part2 ENGINE = MyISAM) INSERT INTO t1 SELECT * FROM t0_template WHERE f1 BETWEEN 100 AND 200; SELECT (COUNT(*) = 200) AND (MIN(f1) = 1) AND (MAX(f1) = 200) AS my_value FROM t1; @@ -1651,14 +1651,14 @@ Table Create Table t1 CREATE TABLE `t1` ( `f1` int(11) DEFAULT NULL, `f2` char(20) DEFAULT NULL -) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY HASH (f1) (PARTITION part1 ENGINE = MyISAM, PARTITION part3 ENGINE = MyISAM) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY HASH (f1) (PARTITION part1 ENGINE = MyISAM, PARTITION part3 ENGINE = MyISAM) INSERT INTO t1 SELECT * FROM t0_template WHERE f1 BETWEEN 1 AND 100 - 1; SHOW CREATE TABLE t1; Table Create Table t1 CREATE TABLE `t1` ( `f1` int(11) DEFAULT NULL, `f2` char(20) DEFAULT NULL -) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY HASH (f1) (PARTITION part1 ENGINE = MyISAM, PARTITION part3 ENGINE = MyISAM) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY HASH (f1) (PARTITION part1 ENGINE = MyISAM, PARTITION part3 ENGINE = MyISAM) INSERT INTO t1 SELECT * FROM t0_template WHERE f1 BETWEEN 100 AND 200; SELECT (COUNT(*) = 200) AND (MIN(f1) = 1) AND (MAX(f1) = 200) AS my_value FROM t1; diff --git a/mysql-test/r/partition_error.result b/mysql-test/r/partition_error.result index 1a0b1dd9b3a..a7ca3d9b2fa 100644 --- a/mysql-test/r/partition_error.result +++ b/mysql-test/r/partition_error.result @@ -554,3 +554,26 @@ PARTITION BY RANGE (a) (PARTITION p1 VALUES LESS THAN(5)); insert into t1 values (10); ERROR HY000: Table has no partition for value 10 drop table t1; +create table t1 (v varchar(12)) +partition by range (ascii(v)) +(partition p0 values less than (10)); +drop table t1; +create table t1 (a int) +partition by hash (rand(a)); +ERROR 42000: Constant/Random expression in (sub)partitioning function is not allowed near ')' at line 2 +create table t1 (a int) +partition by hash(CURTIME() + a); +ERROR 42000: Constant/Random expression in (sub)partitioning function is not allowed near ')' at line 2 +create table t1 (a int) +partition by hash (NOW()+a); +ERROR 42000: Constant/Random expression in (sub)partitioning function is not allowed near ')' at line 2 +create table t1 (a int) +partition by hash (extract(hour from convert_tz(a, '+00:00', '+00:00'))); +ERROR HY000: This partition function is not allowed +create table t1 (a int) +partition by range (a + (select count(*) from t1)) +(partition p1 values less than (1)); +ERROR HY000: This partition function is not allowed +create table t1 (a char(10)) +partition by hash (extractvalue(a,'a')); +ERROR HY000: The PARTITION function returns the wrong type diff --git a/mysql-test/r/partition_innodb.result b/mysql-test/r/partition_innodb.result new file mode 100644 index 00000000000..5b1221dd64c --- /dev/null +++ b/mysql-test/r/partition_innodb.result @@ -0,0 +1,94 @@ +SET @max_row = 20; +DROP TABLE IF EXISTS t0_template; +CREATE TABLE t0_template ( +f_int1 INTEGER, +f_int2 INTEGER, +f_char1 CHAR(20), +f_char2 CHAR(20), +f_charbig VARCHAR(1000) , +PRIMARY KEY(f_int1)) +ENGINE = MEMORY; +INSERT INTO t0_template +SET f_int1 = 20, f_int2 = 20, f_char1 = '20', f_char2 = '20', +f_charbig = '===20==='; +INSERT INTO t0_template +SET f_int1 = 19, f_int2 = 19, f_char1 = '19', f_char2 = '19', +f_charbig = '===19==='; +INSERT INTO t0_template +SET f_int1 = 18, f_int2 = 18, f_char1 = '18', f_char2 = '18', +f_charbig = '===18==='; +INSERT INTO t0_template +SET f_int1 = 17, f_int2 = 17, f_char1 = '17', f_char2 = '17', +f_charbig = '===17==='; +INSERT INTO t0_template +SET f_int1 = 16, f_int2 = 16, f_char1 = '16', f_char2 = '16', +f_charbig = '===16==='; +INSERT INTO t0_template +SET f_int1 = 15, f_int2 = 15, f_char1 = '15', f_char2 = '15', +f_charbig = '===15==='; +INSERT INTO t0_template +SET f_int1 = 14, f_int2 = 14, f_char1 = '14', f_char2 = '14', +f_charbig = '===14==='; +INSERT INTO t0_template +SET f_int1 = 13, f_int2 = 13, f_char1 = '13', f_char2 = '13', +f_charbig = '===13==='; +INSERT INTO t0_template +SET f_int1 = 12, f_int2 = 12, f_char1 = '12', f_char2 = '12', +f_charbig = '===12==='; +INSERT INTO t0_template +SET f_int1 = 11, f_int2 = 11, f_char1 = '11', f_char2 = '11', +f_charbig = '===11==='; +INSERT INTO t0_template +SET f_int1 = 10, f_int2 = 10, f_char1 = '10', f_char2 = '10', +f_charbig = '===10==='; +INSERT INTO t0_template +SET f_int1 = 9, f_int2 = 9, f_char1 = '9', f_char2 = '9', +f_charbig = '===9==='; +INSERT INTO t0_template +SET f_int1 = 8, f_int2 = 8, f_char1 = '8', f_char2 = '8', +f_charbig = '===8==='; +INSERT INTO t0_template +SET f_int1 = 7, f_int2 = 7, f_char1 = '7', f_char2 = '7', +f_charbig = '===7==='; +INSERT INTO t0_template +SET f_int1 = 6, f_int2 = 6, f_char1 = '6', f_char2 = '6', +f_charbig = '===6==='; +INSERT INTO t0_template +SET f_int1 = 5, f_int2 = 5, f_char1 = '5', f_char2 = '5', +f_charbig = '===5==='; +INSERT INTO t0_template +SET f_int1 = 4, f_int2 = 4, f_char1 = '4', f_char2 = '4', +f_charbig = '===4==='; +INSERT INTO t0_template +SET f_int1 = 3, f_int2 = 3, f_char1 = '3', f_char2 = '3', +f_charbig = '===3==='; +INSERT INTO t0_template +SET f_int1 = 2, f_int2 = 2, f_char1 = '2', f_char2 = '2', +f_charbig = '===2==='; +INSERT INTO t0_template +SET f_int1 = 1, f_int2 = 1, f_char1 = '1', f_char2 = '1', +f_charbig = '===1==='; +DROP TABLE IF EXISTS t1; +CREATE TABLE t1 (f_date DATE, f_varchar VARCHAR(30)) engine='InnoDB'; +INSERT INTO t1 (f_date, f_varchar) +SELECT CONCAT(CAST((f_int1 + 999) AS CHAR),'-02-10'), CAST(f_char1 AS CHAR) +FROM t0_template +WHERE f_int1 + 999 BETWEEN 1000 AND 9999; +SELECT IF(9999 - 1000 + 1 > @max_row, @max_row , 9999 - 1000 + 1) +INTO @exp_row_count; +ALTER TABLE t1 PARTITION BY HASH(CAST(YEAR(f_date) AS SIGNED INTEGER)); +# 1.1.5 Add two named partitions + test +ALTER TABLE t1 ADD PARTITION (PARTITION part1, PARTITION part7); +drop table t1; +CREATE TABLE t1 (f_date DATE, f_varchar VARCHAR(30)) +ENGINE=InnoDB +PARTITION BY HASH(CAST(YEAR(f_date) AS SIGNED INTEGER)); +# This statement crashes the server. +# CREATE partitioned table with three partitions in one step +# would be harmless. +ALTER TABLE t1 ADD PARTITION PARTITIONS 1; +DROP VIEW IF EXISTS v1; +DROP TABLE IF EXISTS t1; +DROP TABLE IF EXISTS t0_aux; +DROP TABLE IF EXISTS t0_definition; +DROP TABLE IF EXISTS t0_template; diff --git a/mysql-test/r/partition_mgm.result b/mysql-test/r/partition_mgm.result new file mode 100644 index 00000000000..7b7b5729112 --- /dev/null +++ b/mysql-test/r/partition_mgm.result @@ -0,0 +1,26 @@ +DROP TABLE IF EXISTS t1; +CREATE TABLE t1 (f_date DATE, f_varchar VARCHAR(30)) +PARTITION BY HASH(CAST(YEAR(f_date) AS SIGNED INTEGER)) PARTITIONS 2; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `f_date` date DEFAULT NULL, + `f_varchar` varchar(30) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY HASH (CAST(YEAR(f_date) AS SIGNED INTEGER)) PARTITIONS 2 +/home/pappa/bug19305/mysql-test/var/master-data/test/t1#P#p0.MYD +/home/pappa/bug19305/mysql-test/var/master-data/test/t1#P#p0.MYI +/home/pappa/bug19305/mysql-test/var/master-data/test/t1#P#p1.MYD +/home/pappa/bug19305/mysql-test/var/master-data/test/t1#P#p1.MYI +/home/pappa/bug19305/mysql-test/var/master-data/test/t1.frm +/home/pappa/bug19305/mysql-test/var/master-data/test/t1.par +ALTER TABLE t1 COALESCE PARTITION 1; +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `f_date` date DEFAULT NULL, + `f_varchar` varchar(30) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY HASH (CAST(YEAR(f_date) AS SIGNED INTEGER)) PARTITIONS 1 +/home/pappa/bug19305/mysql-test/var/master-data/test/t1#P#p0.MYD +/home/pappa/bug19305/mysql-test/var/master-data/test/t1#P#p0.MYI +/home/pappa/bug19305/mysql-test/var/master-data/test/t1.frm +/home/pappa/bug19305/mysql-test/var/master-data/test/t1.par diff --git a/mysql-test/r/partition_pruning.result b/mysql-test/r/partition_pruning.result index 405cc3e6e25..950a83c6d4f 100644 --- a/mysql-test/r/partition_pruning.result +++ b/mysql-test/r/partition_pruning.result @@ -670,3 +670,30 @@ select * from t1 where a like 'n%'; a na drop table t1; +create table t1 (s1 varchar(15)) partition by key (s1); +select * from t1 where s1 = 0 or s1 is null; +s1 +insert into t1 values ('aa'),('bb'),('0'); +explain partitions select * from t1 where s1 = 0 or s1 is null; +id select_type table partitions type possible_keys key key_len ref rows Extra +1 SIMPLE t1 p0 ALL NULL NULL NULL NULL 3 Using where +drop table t1; +create table t2 (a int, b int) +partition by LIST(a) +subpartition by HASH(b) subpartitions 40 +( partition p_0_long_partition_name values in(1), +partition p_1_long_partition_name values in(2)); +insert into t2 values (1,1),(2,2); +explain partitions select * from t2; +id 1 +select_type SIMPLE +table t2 +partitions p_0_long_partition_name_p_0_long_partition_namesp0,p_0_long_partition_name_p_0_long_partition_namesp1,p_0_long_partition_name_p_0_long_partition_namesp2,p_0_long_partition_name_p_0_long_partition_namesp3,p_0_long_partition_name_p_0_long_partition_namesp4,p_0_long_partition_name_p_0_long_partition_namesp5,p_0_long_partition_name_p_0_long_partition_namesp6,p_0_long_partition_name_p_0_long_partition_namesp7,p_0_long_partition_name_p_0_long_partition_namesp8,p_0_long_partition_name_p_0_long_partition_namesp9,p_0_long_partition_name_p_0_long_partition_namesp10,p_0_long_partition_name_p_0_long_partition_namesp11,p_0_long_partition_name_p_0_long_partition_namesp12,p_0_long_partition_name_p_0_long_partition_namesp13,p_0_long_partition_name_p_0_long_partition_namesp14,p_0_long_partition_name_p_0_long_partition_namesp15,p_0_long_partition_name_p_0_long_partition_namesp16,p_0_long_partition_name_p_0_long_partition_namesp17,p_0_long_partition_name_p_0_long_partition_namesp18,p_0_long_partition_name_p_0_long_partition_namesp19,p_0_long_partition_name_p_0_long_partition_namesp20,p_0_long_partition_name_p_0_long_partition_namesp21,p_0_long_partition_name_p_0_long_partition_namesp22,p_0_long_partition_name_p_0_long_partition_namesp23,p_0_long_partition_name_p_0_long_partition_namesp24,p_0_long_partition_name_p_0_long_partition_namesp25,p_0_long_partition_name_p_0_long_partition_namesp26,p_0_long_partition_name_p_0_long_partition_namesp27,p_0_long_partition_name_p_0_long_partition_namesp28,p_0_long_partition_name_p_0_long_partition_namesp29,p_0_long_partition_name_p_0_long_partition_namesp30,p_0_long_partition_name_p_0_long_partition_namesp31,p_0_long_partition_name_p_0_long_partition_namesp32,p_0_long_partition_name_p_0_long_partition_namesp33,p_0_long_partition_name_p_0_long_partition_namesp34,p_0_long_partition_name_p_0_long_partition_namesp35,p_0_long_partition_name_p_0_long_partition_namesp36,p_0_long_partition_name_p_0_long_partition_namesp37,p_0_long_partition_name_p_0_long_partition_namesp38,p_0_long_partition_name_p_0_long_partition_namesp39,p_1_long_partition_name_p_1_long_partition_namesp0,p_1_long_partition_name_p_1_long_partition_namesp1,p_1_long_partition_name_p_1_long_partition_namesp2,p_1_long_partition_name_p_1_long_partition_namesp3,p_1_long_partition_name_p_1_long_partition_namesp4,p_1_long_partition_name_p_1_long_partition_namesp5,p_1_long_partition_name_p_1_long_partition_namesp6,p_1_long_partition_name_p_1_long_partition_namesp7,p_1_long_partition_name_p_1_long_partition_namesp8,p_1_long_partition_name_p_1_long_partition_namesp9,p_1_long_partition_name_p_1_long_partition_namesp10,p_1_long_partition_name_p_1_long_partition_namesp11,p_1_long_partition_name_p_1_long_partition_namesp12,p_1_long_partition_name_p_1_long_partition_namesp13,p_1_long_partition_name_p_1_long_partition_namesp14,p_1_long_partition_name_p_1_long_partition_namesp15,p_1_long_partition_name_p_1_long_partition_namesp16,p_1_long_partition_name_p_1_long_partition_namesp17,p_1_long_partition_name_p_1_long_partition_namesp18,p_1_long_partition_name_p_1_long_partition_namesp19,p_1_long_partition_name_p_1_long_partition_namesp20,p_1_long_partition_name_p_1_long_partition_namesp21,p_1_long_partition_name_p_1_long_partition_namesp22,p_1_long_partition_name_p_1_long_partition_namesp23,p_1_long_partition_name_p_1_long_partition_namesp24,p_1_long_partition_name_p_1_long_partition_namesp25,p_1_long_partition_name_p_1_long_partition_namesp26,p_1_long_partition_name_p_1_long_partition_namesp27,p_1_long_partition_name_p_1_long_partition_namesp28,p_1_long_partition_name_p_1_long_partition_namesp29,p_1_long_partition_name_p_1_long_partition_namesp30,p_1_long_partition_name_p_1_long_partition_namesp31,p_1_long_partition_name_p_1_long_partition_namesp32,p_1_long_partition_name_p_1_long_partition_namesp33,p_1_long_partition_name_p_1_long_partition_namesp34,p_1_long_partition_name_p_1_long_partition_namesp35,p_1_long_partition_name_p_1_long_partition_namesp36,p_1_long_partition_name_p_1_long_partition_namesp37,p_1_long_partition_name_p_1_long_partition_namesp38,p_1_long_partition_name_p_1_long_partition_namesp39 +type ALL +possible_keys NULL +key NULL +key_len NULL +ref NULL +rows 2 +Extra +drop table t2; diff --git a/mysql-test/r/partition_range.result b/mysql-test/r/partition_range.result index c7257db4910..518d3a8e1d4 100644 --- a/mysql-test/r/partition_range.result +++ b/mysql-test/r/partition_range.result @@ -143,7 +143,7 @@ t1 CREATE TABLE `t1` ( `b` int(11) NOT NULL, `c` int(11) NOT NULL, PRIMARY KEY (`a`,`b`) -) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY RANGE (a) SUBPARTITION BY HASH (a+b) (PARTITION x1 VALUES LESS THAN (1) (SUBPARTITION x11 ENGINE = MyISAM, SUBPARTITION x12 ENGINE = MyISAM), PARTITION x2 VALUES LESS THAN (5) (SUBPARTITION x21 ENGINE = MyISAM, SUBPARTITION x22 ENGINE = MyISAM)) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY RANGE (a) SUBPARTITION BY HASH (a+b) (PARTITION x1 VALUES LESS THAN (1) (SUBPARTITION x11 ENGINE = MyISAM, SUBPARTITION x12 ENGINE = MyISAM), PARTITION x2 VALUES LESS THAN (5) (SUBPARTITION x21 ENGINE = MyISAM, SUBPARTITION x22 ENGINE = MyISAM)) ALTER TABLE t1 ADD COLUMN d int; show create table t1; Table Create Table @@ -153,7 +153,7 @@ t1 CREATE TABLE `t1` ( `c` int(11) NOT NULL, `d` int(11) DEFAULT NULL, PRIMARY KEY (`a`,`b`) -) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY RANGE (a) SUBPARTITION BY HASH (a+b) (PARTITION x1 VALUES LESS THAN (1) (SUBPARTITION x11 ENGINE = MyISAM, SUBPARTITION x12 ENGINE = MyISAM), PARTITION x2 VALUES LESS THAN (5) (SUBPARTITION x21 ENGINE = MyISAM, SUBPARTITION x22 ENGINE = MyISAM)) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY RANGE (a) SUBPARTITION BY HASH (a+b) (PARTITION x1 VALUES LESS THAN (1) (SUBPARTITION x11 ENGINE = MyISAM, SUBPARTITION x12 ENGINE = MyISAM), PARTITION x2 VALUES LESS THAN (5) (SUBPARTITION x21 ENGINE = MyISAM, SUBPARTITION x22 ENGINE = MyISAM)) drop table t1; CREATE TABLE t1 ( a int not null, @@ -387,3 +387,108 @@ ALTER TABLE t1 DROP PARTITION p0; ALTER TABLE t1 DROP PARTITION p1; ALTER TABLE t1 DROP PARTITION p2; drop table t1; +create table t1 (a int DEFAULT NULL, +b varchar(30) DEFAULT NULL, +c date DEFAULT NULL) +ENGINE=MYISAM DEFAULT CHARSET=latin1; +insert into t1 values (1, 'abc', '1995-01-01'); +insert into t1 values (1, 'abc', '1995-01-02'); +insert into t1 values (1, 'abc', '1995-01-03'); +insert into t1 values (1, 'abc', '1995-01-04'); +insert into t1 values (1, 'abc', '1995-01-05'); +insert into t1 values (1, 'abc', '1995-01-06'); +insert into t1 values (1, 'abc', '1995-01-07'); +insert into t1 values (1, 'abc', '1995-01-08'); +insert into t1 values (1, 'abc', '1995-01-09'); +insert into t1 values (1, 'abc', '1995-01-10'); +insert into t1 values (1, 'abc', '1995-01-11'); +insert into t1 values (1, 'abc', '1995-01-12'); +insert into t1 values (1, 'abc', '1995-01-13'); +insert into t1 values (1, 'abc', '1995-01-14'); +insert into t1 values (1, 'abc', '1995-01-15'); +insert into t1 values (1, 'abc', '1997-01-01'); +insert into t1 values (1, 'abc', '1997-01-02'); +insert into t1 values (1, 'abc', '1997-01-03'); +insert into t1 values (1, 'abc', '1997-01-04'); +insert into t1 values (1, 'abc', '1997-01-05'); +insert into t1 values (1, 'abc', '1997-01-06'); +insert into t1 values (1, 'abc', '1997-01-07'); +insert into t1 values (1, 'abc', '1997-01-08'); +insert into t1 values (1, 'abc', '1997-01-09'); +insert into t1 values (1, 'abc', '1997-01-10'); +insert into t1 values (1, 'abc', '1997-01-11'); +insert into t1 values (1, 'abc', '1997-01-12'); +insert into t1 values (1, 'abc', '1997-01-13'); +insert into t1 values (1, 'abc', '1997-01-14'); +insert into t1 values (1, 'abc', '1997-01-15'); +insert into t1 values (1, 'abc', '1998-01-01'); +insert into t1 values (1, 'abc', '1998-01-02'); +insert into t1 values (1, 'abc', '1998-01-03'); +insert into t1 values (1, 'abc', '1998-01-04'); +insert into t1 values (1, 'abc', '1998-01-05'); +insert into t1 values (1, 'abc', '1998-01-06'); +insert into t1 values (1, 'abc', '1998-01-07'); +insert into t1 values (1, 'abc', '1998-01-08'); +insert into t1 values (1, 'abc', '1998-01-09'); +insert into t1 values (1, 'abc', '1998-01-10'); +insert into t1 values (1, 'abc', '1998-01-11'); +insert into t1 values (1, 'abc', '1998-01-12'); +insert into t1 values (1, 'abc', '1998-01-13'); +insert into t1 values (1, 'abc', '1998-01-14'); +insert into t1 values (1, 'abc', '1998-01-15'); +insert into t1 values (1, 'abc', '1999-01-01'); +insert into t1 values (1, 'abc', '1999-01-02'); +insert into t1 values (1, 'abc', '1999-01-03'); +insert into t1 values (1, 'abc', '1999-01-04'); +insert into t1 values (1, 'abc', '1999-01-05'); +insert into t1 values (1, 'abc', '1999-01-06'); +insert into t1 values (1, 'abc', '1999-01-07'); +insert into t1 values (1, 'abc', '1999-01-08'); +insert into t1 values (1, 'abc', '1999-01-09'); +insert into t1 values (1, 'abc', '1999-01-10'); +insert into t1 values (1, 'abc', '1999-01-11'); +insert into t1 values (1, 'abc', '1999-01-12'); +insert into t1 values (1, 'abc', '1999-01-13'); +insert into t1 values (1, 'abc', '1999-01-14'); +insert into t1 values (1, 'abc', '1999-01-15'); +insert into t1 values (1, 'abc', '2000-01-01'); +insert into t1 values (1, 'abc', '2000-01-02'); +insert into t1 values (1, 'abc', '2000-01-03'); +insert into t1 values (1, 'abc', '2000-01-04'); +insert into t1 values (1, 'abc', '2000-01-05'); +insert into t1 values (1, 'abc', '2000-01-06'); +insert into t1 values (1, 'abc', '2000-01-07'); +insert into t1 values (1, 'abc', '2000-01-08'); +insert into t1 values (1, 'abc', '2000-01-09'); +insert into t1 values (1, 'abc', '2000-01-15'); +insert into t1 values (1, 'abc', '2000-01-11'); +insert into t1 values (1, 'abc', '2000-01-12'); +insert into t1 values (1, 'abc', '2000-01-13'); +insert into t1 values (1, 'abc', '2000-01-14'); +insert into t1 values (1, 'abc', '2000-01-15'); +insert into t1 values (1, 'abc', '2001-01-01'); +insert into t1 values (1, 'abc', '2001-01-02'); +insert into t1 values (1, 'abc', '2001-01-03'); +insert into t1 values (1, 'abc', '2001-01-04'); +insert into t1 values (1, 'abc', '2001-01-05'); +insert into t1 values (1, 'abc', '2001-01-06'); +insert into t1 values (1, 'abc', '2001-01-07'); +insert into t1 values (1, 'abc', '2001-01-08'); +insert into t1 values (1, 'abc', '2001-01-09'); +insert into t1 values (1, 'abc', '2001-01-15'); +insert into t1 values (1, 'abc', '2001-01-11'); +insert into t1 values (1, 'abc', '2001-01-12'); +insert into t1 values (1, 'abc', '2001-01-13'); +insert into t1 values (1, 'abc', '2001-01-14'); +insert into t1 values (1, 'abc', '2001-01-15'); +alter table t1 +partition by range (year(c)) +(partition p5 values less than (2000), partition p10 values less than (2010)); +alter table t1 +reorganize partition p5 into +(partition p1 values less than (1996), +partition p2 values less than (1997), +partition p3 values less than (1998), +partition p4 values less than (1999), +partition p5 values less than (2000)); +drop table t1; diff --git a/mysql-test/r/preload.result b/mysql-test/r/preload.result index b668b07b398..145fd22ffb6 100644 --- a/mysql-test/r/preload.result +++ b/mysql-test/r/preload.result @@ -160,11 +160,11 @@ Key_reads 0 load index into cache t3 key (b), t2 key (c) ; Table Op Msg_type Msg_text test.t3 preload_keys error Table 'test.t3' doesn't exist -test.t2 preload_keys error Key column 'c' doesn't exist in table +test.t2 preload_keys error Key 'c' doesn't exist in table 't2' test.t2 preload_keys status Operation failed Warnings: Error 1146 Table 'test.t3' doesn't exist -Error 1072 Key column 'c' doesn't exist in table +Error 1176 Key 'c' doesn't exist in table 't2' show status like "key_read%"; Variable_name Value Key_read_requests 0 diff --git a/mysql-test/r/ps.result b/mysql-test/r/ps.result index 06443cde01d..3e3981c0050 100644 --- a/mysql-test/r/ps.result +++ b/mysql-test/r/ps.result @@ -1157,3 +1157,4 @@ test.t1 analyze status Table is already up to date Warnings: Error 1146 Table 'test.t4' doesn't exist deallocate prepare stmt; +drop table t1, t2, t3; diff --git a/mysql-test/r/rpl_ddl.result b/mysql-test/r/rpl_ddl.result index e828f2c1e31..cfa2b4dc76d 100644 --- a/mysql-test/r/rpl_ddl.result +++ b/mysql-test/r/rpl_ddl.result @@ -359,8 +359,6 @@ MAX(f1) -------- switch to master ------- ROLLBACK; -Warnings: -Warning 1196 Some non-transactional changed tables couldn't be rolled back SELECT MAX(f1) FROM t1; MAX(f1) 5 @@ -579,8 +577,6 @@ MAX(f1) -------- switch to master ------- ROLLBACK; -Warnings: -Warning 1196 Some non-transactional changed tables couldn't be rolled back SELECT MAX(f1) FROM t1; MAX(f1) 8 diff --git a/mysql-test/r/rpl_ndb_2myisam.result b/mysql-test/r/rpl_ndb_2myisam.result index 00fb2f5455f..8611d83f3f3 100644 --- a/mysql-test/r/rpl_ndb_2myisam.result +++ b/mysql-test/r/rpl_ndb_2myisam.result @@ -27,7 +27,7 @@ t1 CREATE TABLE `t1` ( `y` year(4) DEFAULT NULL, `t` date DEFAULT NULL, PRIMARY KEY (`id`) -) ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY KEY () +) ENGINE=ndbcluster DEFAULT CHARSET=latin1 --- Show table on slave --- SHOW CREATE TABLE t1; Table Create Table @@ -100,7 +100,7 @@ t1 CREATE TABLE `t1` ( `y` year(4) DEFAULT NULL, `t` date DEFAULT NULL, PRIMARY KEY (`id`,`total`) -) ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY KEY () +) ENGINE=ndbcluster DEFAULT CHARSET=latin1 --- Make sure that our tables on slave are still same engine --- --- and that the alter statements replicated correctly --- SHOW CREATE TABLE t1; diff --git a/mysql-test/r/rpl_ndb_basic.result b/mysql-test/r/rpl_ndb_basic.result index b23e5f03f27..32a1c790c99 100644 --- a/mysql-test/r/rpl_ndb_basic.result +++ b/mysql-test/r/rpl_ndb_basic.result @@ -146,4 +146,19 @@ c1 c2 3 NULL 4 NULL 5 NULL +TRUNCATE t1; +SELECT count(*) FROM t1; +count(*) +0 +INSERT INTO t1 VALUES (101,NULL),(102,NULL),(103,NULL),(104,NULL),(105,NULL),(106,NULL),(107,NULL),(108,NULL),(109,NULL),(1010,NULL); +SELECT count(*) FROM t1; +count(*) +10 +SELECT c1 FROM t1 ORDER BY c1 LIMIT 5; +c1 +101 +102 +103 +104 +105 DROP TABLE t1; diff --git a/mysql-test/r/rpl_ndb_log.result b/mysql-test/r/rpl_ndb_log.result index 5f6f040b715..c435fb37531 100644 --- a/mysql-test/r/rpl_ndb_log.result +++ b/mysql-test/r/rpl_ndb_log.result @@ -47,6 +47,10 @@ master-bin.000001 # Table_map 1 # table_id: # (test.t1) flush logs; create table t3 (a int)ENGINE=NDB; start slave; + +let $result_pattern= '%127.0.0.1%root%master-bin.000002%slave-relay-bin.000005%Yes%Yes%0%0%None%' ; + +--source include/wait_slave_status.inc flush logs; stop slave; create table t2 (n int)ENGINE=NDB; diff --git a/mysql-test/r/select.result b/mysql-test/r/select.result index f45e16f66c1..d47d49b5298 100644 --- a/mysql-test/r/select.result +++ b/mysql-test/r/select.result @@ -144,9 +144,9 @@ explain select fld3 from t2 use index (fld1,fld3) where fld3 = 'honeysuckle'; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t2 ref fld3 fld3 30 const 1 Using where; Using index explain select fld3 from t2 ignore index (fld3,not_used); -ERROR 42000: Key column 'not_used' doesn't exist in table +ERROR 42000: Key 'not_used' doesn't exist in table 't2' explain select fld3 from t2 use index (not_used); -ERROR 42000: Key column 'not_used' doesn't exist in table +ERROR 42000: Key 'not_used' doesn't exist in table 't2' select t2.fld3 from t2 where fld3 >= 'honeysuckle' and fld3 <= 'honoring' order by fld3; fld3 honeysuckle @@ -2716,6 +2716,16 @@ select * from t1 where f1 in (select f3 from t2 where (f3,f4)= (select f3,f4 fro f1 f2 1 1 drop table t1,t2; +CREATE TABLE t1 (a int, INDEX idx(a)); +INSERT INTO t1 VALUES (2), (3), (1); +EXPLAIN SELECT * FROM t1 IGNORE INDEX (idx); +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 +EXPLAIN SELECT * FROM t1 IGNORE INDEX (a); +ERROR 42000: Key 'a' doesn't exist in table 't1' +EXPLAIN SELECT * FROM t1 FORCE INDEX (a); +ERROR 42000: Key 'a' doesn't exist in table 't1' +DROP TABLE t1; CREATE TABLE t1 ( city char(30) ); INSERT INTO t1 VALUES ('London'); INSERT INTO t1 VALUES ('Paris'); diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index 8330c391715..9bdd1d5645f 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -4970,4 +4970,34 @@ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa DROP FUNCTION bug18589_f1| DROP PROCEDURE bug18589_p1| DROP PROCEDURE bug18589_p2| +DROP FUNCTION IF EXISTS bug18037_f1| +DROP PROCEDURE IF EXISTS bug18037_p1| +DROP PROCEDURE IF EXISTS bug18037_p2| +CREATE FUNCTION bug18037_f1() RETURNS INT +BEGIN +RETURN @@server_id; +END| +CREATE PROCEDURE bug18037_p1() +BEGIN +DECLARE v INT DEFAULT @@server_id; +END| +CREATE PROCEDURE bug18037_p2() +BEGIN +CASE @@server_id +WHEN -1 THEN +SELECT 0; +ELSE +SELECT 1; +END CASE; +END| +SELECT bug18037_f1()| +bug18037_f1() +1 +CALL bug18037_p1()| +CALL bug18037_p2()| +1 +1 +DROP FUNCTION bug18037_f1| +DROP PROCEDURE bug18037_p1| +DROP PROCEDURE bug18037_p2| drop table t1,t2; diff --git a/mysql-test/r/ssl.result b/mysql-test/r/ssl.result index bb7297d6807..c5df9e99de6 100644 --- a/mysql-test/r/ssl.result +++ b/mysql-test/r/ssl.result @@ -145,9 +145,9 @@ explain select fld3 from t2 use index (fld1,fld3) where fld3 = 'honeysuckle'; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t2 ref fld3 fld3 30 const 1 Using where; Using index explain select fld3 from t2 ignore index (fld3,not_used); -ERROR 42000: Key column 'not_used' doesn't exist in table +ERROR 42000: Key 'not_used' doesn't exist in table 't2' explain select fld3 from t2 use index (not_used); -ERROR 42000: Key column 'not_used' doesn't exist in table +ERROR 42000: Key 'not_used' doesn't exist in table 't2' select t2.fld3 from t2 where fld3 >= 'honeysuckle' and fld3 <= 'honoring' order by fld3; fld3 honeysuckle diff --git a/mysql-test/r/ssl_compress.result b/mysql-test/r/ssl_compress.result index 9c1cf4b0ec3..c7919c7e424 100644 --- a/mysql-test/r/ssl_compress.result +++ b/mysql-test/r/ssl_compress.result @@ -148,9 +148,9 @@ explain select fld3 from t2 use index (fld1,fld3) where fld3 = 'honeysuckle'; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t2 ref fld3 fld3 30 const 1 Using where; Using index explain select fld3 from t2 ignore index (fld3,not_used); -ERROR 42000: Key column 'not_used' doesn't exist in table +ERROR 42000: Key 'not_used' doesn't exist in table 't2' explain select fld3 from t2 use index (not_used); -ERROR 42000: Key column 'not_used' doesn't exist in table +ERROR 42000: Key 'not_used' doesn't exist in table 't2' select t2.fld3 from t2 where fld3 >= 'honeysuckle' and fld3 <= 'honoring' order by fld3; fld3 honeysuckle diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index 71a71a14ad2..e307bb9eafe 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -3183,3 +3183,24 @@ select * from (select min(i) from t1 where j=(select * from (select min(j) from min(i) 1 drop table t1; +CREATE TABLE t1 (i BIGINT UNSIGNED); +INSERT INTO t1 VALUES (10000000000000000000); +INSERT INTO t1 VALUES (1); +CREATE TABLE t2 (i BIGINT UNSIGNED); +INSERT INTO t2 VALUES (10000000000000000000); +INSERT INTO t2 VALUES (1); +/* simple test */ +SELECT t1.i FROM t1 JOIN t2 ON t1.i = t2.i; +i +10000000000000000000 +1 +/* subquery test */ +SELECT t1.i FROM t1 WHERE t1.i = (SELECT MAX(i) FROM t2); +i +10000000000000000000 +/* subquery test with cast*/ +SELECT t1.i FROM t1 WHERE t1.i = CAST((SELECT MAX(i) FROM t2) AS UNSIGNED); +i +10000000000000000000 +DROP TABLE t1; +DROP TABLE t2; diff --git a/mysql-test/r/system_mysql_db.result b/mysql-test/r/system_mysql_db.result index 30cde39531d..b9d3504993c 100644 --- a/mysql-test/r/system_mysql_db.result +++ b/mysql-test/r/system_mysql_db.result @@ -208,7 +208,7 @@ event CREATE TABLE `event` ( `on_completion` enum('DROP','PRESERVE') NOT NULL DEFAULT 'DROP', `sql_mode` set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES','IGNORE_SPACE','NOT_USED','ONLY_FULL_GROUP_BY','NO_UNSIGNED_SUBTRACTION','NO_DIR_IN_CREATE','POSTGRESQL','ORACLE','MSSQL','DB2','MAXDB','NO_KEY_OPTIONS','NO_TABLE_OPTIONS','NO_FIELD_OPTIONS','MYSQL323','MYSQL40','ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES','STRICT_ALL_TABLES','NO_ZERO_IN_DATE','NO_ZERO_DATE','INVALID_DATES','ERROR_FOR_DIVISION_BY_ZERO','TRADITIONAL','NO_AUTO_CREATE_USER','HIGH_NOT_PRECEDENCE') NOT NULL DEFAULT '', `comment` char(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '', - PRIMARY KEY (`definer`,`db`,`name`) + PRIMARY KEY (`db`,`name`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='Events' show create table general_log; Table Create Table diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result index 10a9ac87748..9fabb0ad2d8 100644 --- a/mysql-test/r/view.result +++ b/mysql-test/r/view.result @@ -614,7 +614,7 @@ drop table t1; create table t1 (a int, b int); create view v1 as select a, sum(b) from t1 group by a; select b from v1 use index (some_index) where b=1; -ERROR 42000: Key column 'some_index' doesn't exist in table +ERROR 42000: Key 'some_index' doesn't exist in table 'v1' drop view v1; drop table t1; create table t1 (col1 char(5),col2 char(5)); diff --git a/mysql-test/r/view_grant.result b/mysql-test/r/view_grant.result index 928e3635ec6..db82016f398 100644 --- a/mysql-test/r/view_grant.result +++ b/mysql-test/r/view_grant.result @@ -1,5 +1,5 @@ drop database if exists mysqltest; -drop view if exists v1; +drop view if exists v1,v2,v3; grant create view on test.* to test@localhost; show grants for test@localhost; Grants for test@localhost @@ -535,3 +535,88 @@ View Create View v2 CREATE ALGORITHM=UNDEFINED DEFINER=`some_user`@`localhost` SQL SECURITY INVOKER VIEW `v2` AS select 1 AS `1` drop view v1; drop view v2; +CREATE DATABASE mysqltest1; +CREATE USER readonly@localhost; +CREATE TABLE mysqltest1.t1 (x INT); +INSERT INTO mysqltest1.t1 VALUES (1), (2); +CREATE SQL SECURITY INVOKER VIEW mysqltest1.v_t1 AS SELECT * FROM mysqltest1.t1; +CREATE SQL SECURITY DEFINER VIEW mysqltest1.v_ts AS SELECT * FROM mysqltest1.t1; +CREATE SQL SECURITY DEFINER VIEW mysqltest1.v_ti AS SELECT * FROM mysqltest1.t1; +CREATE SQL SECURITY DEFINER VIEW mysqltest1.v_tu AS SELECT * FROM mysqltest1.t1; +CREATE SQL SECURITY DEFINER VIEW mysqltest1.v_tus AS SELECT * FROM mysqltest1.t1; +CREATE SQL SECURITY DEFINER VIEW mysqltest1.v_td AS SELECT * FROM mysqltest1.t1; +CREATE SQL SECURITY DEFINER VIEW mysqltest1.v_tds AS SELECT * FROM mysqltest1.t1; +GRANT SELECT, INSERT, UPDATE, DELETE ON mysqltest1.v_t1 TO readonly; +GRANT SELECT ON mysqltest1.v_ts TO readonly; +GRANT INSERT ON mysqltest1.v_ti TO readonly; +GRANT UPDATE ON mysqltest1.v_tu TO readonly; +GRANT UPDATE,SELECT ON mysqltest1.v_tus TO readonly; +GRANT DELETE ON mysqltest1.v_td TO readonly; +GRANT DELETE,SELECT ON mysqltest1.v_tds TO readonly; +SELECT * FROM mysqltest1.v_t1; +ERROR HY000: View 'mysqltest1.v_t1' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them +INSERT INTO mysqltest1.v_t1 VALUES(4); +ERROR HY000: View 'mysqltest1.v_t1' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them +DELETE FROM mysqltest1.v_t1 WHERE x = 1; +ERROR HY000: View 'mysqltest1.v_t1' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them +UPDATE mysqltest1.v_t1 SET x = 3 WHERE x = 2; +ERROR HY000: View 'mysqltest1.v_t1' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them +UPDATE mysqltest1.v_t1 SET x = 3; +ERROR HY000: View 'mysqltest1.v_t1' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them +DELETE FROM mysqltest1.v_t1; +ERROR HY000: View 'mysqltest1.v_t1' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them +SELECT 1 FROM mysqltest1.v_t1; +ERROR HY000: View 'mysqltest1.v_t1' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them +SELECT * FROM mysqltest1.t1; +ERROR 42000: SELECT command denied to user 'readonly'@'localhost' for table 't1' +SELECT * FROM mysqltest1.v_ts; +x +1 +2 +SELECT * FROM mysqltest1.v_ts, mysqltest1.t1 WHERE mysqltest1.t1.x = mysqltest1.v_ts.x; +ERROR 42000: SELECT command denied to user 'readonly'@'localhost' for table 't1' +SELECT * FROM mysqltest1.v_ti; +ERROR 42000: SELECT command denied to user 'readonly'@'localhost' for table 'v_ti' +INSERT INTO mysqltest1.v_ts VALUES (100); +ERROR 42000: INSERT command denied to user 'readonly'@'localhost' for table 'v_ts' +INSERT INTO mysqltest1.v_ti VALUES (100); +UPDATE mysqltest1.v_ts SET x= 200 WHERE x = 100; +ERROR 42000: UPDATE command denied to user 'readonly'@'localhost' for table 'v_ts' +UPDATE mysqltest1.v_ts SET x= 200; +ERROR 42000: UPDATE command denied to user 'readonly'@'localhost' for table 'v_ts' +UPDATE mysqltest1.v_tu SET x= 200 WHERE x = 100; +UPDATE mysqltest1.v_tus SET x= 200 WHERE x = 100; +UPDATE mysqltest1.v_tu SET x= 200; +DELETE FROM mysqltest1.v_ts WHERE x= 200; +ERROR 42000: DELETE command denied to user 'readonly'@'localhost' for table 'v_ts' +DELETE FROM mysqltest1.v_ts; +ERROR 42000: DELETE command denied to user 'readonly'@'localhost' for table 'v_ts' +DELETE FROM mysqltest1.v_td WHERE x= 200; +ERROR 42000: SELECT command denied to user 'readonly'@'localhost' for column 'x' in table 'v_td' +DELETE FROM mysqltest1.v_tds WHERE x= 200; +DELETE FROM mysqltest1.v_td; +DROP VIEW mysqltest1.v_tds; +DROP VIEW mysqltest1.v_td; +DROP VIEW mysqltest1.v_tus; +DROP VIEW mysqltest1.v_tu; +DROP VIEW mysqltest1.v_ti; +DROP VIEW mysqltest1.v_ts; +DROP VIEW mysqltest1.v_t1; +DROP TABLE mysqltest1.t1; +DROP USER readonly@localhost; +DROP DATABASE mysqltest1; +CREATE TABLE t1 (a INT PRIMARY KEY); +INSERT INTO t1 VALUES (1), (2), (3); +CREATE DEFINER = 'no-such-user'@localhost VIEW v AS SELECT a from t1; +Warnings: +Note 1449 There is no 'no-such-user'@'localhost' registered +SHOW CREATE VIEW v; +View Create View +v CREATE ALGORITHM=UNDEFINED DEFINER=`no-such-user`@`localhost` SQL SECURITY DEFINER VIEW `v` AS select `t1`.`a` AS `a` from `t1` +Warnings: +Note 1449 There is no 'no-such-user'@'localhost' registered +SELECT * FROM v; +ERROR HY000: There is no 'no-such-user'@'localhost' registered +DROP VIEW v; +DROP TABLE t1; +USE test; diff --git a/mysql-test/std_data/bug15328.cnf b/mysql-test/std_data/bug15328.cnf new file mode 100644 index 00000000000..e23d33bfa54 --- /dev/null +++ b/mysql-test/std_data/bug15328.cnf @@ -0,0 +1,2 @@ +[mysqldump] +fields-optionally-enclosed-by=" diff --git a/mysql-test/t/analyze.test b/mysql-test/t/analyze.test index 1801a4a440f..7c9830bb468 100644 --- a/mysql-test/t/analyze.test +++ b/mysql-test/t/analyze.test @@ -61,6 +61,7 @@ prepare stmt1 from "SELECT * FROM t1 PROCEDURE ANALYSE()"; execute stmt1; execute stmt1; deallocate prepare stmt1; +drop table t1; # # bug#15225 (ANALYZE temporary has no effect) diff --git a/mysql-test/t/create.test b/mysql-test/t/create.test index fe8cfe70c4e..e4a7d1cd9af 100644 --- a/mysql-test/t/create.test +++ b/mysql-test/t/create.test @@ -226,6 +226,7 @@ drop table t1; # Test create table if not exists with duplicate key error # +flush status; create table t1 (a int not null, b int, primary key (a)); insert into t1 values (1,1); create table if not exists t1 select 2; @@ -233,6 +234,8 @@ select * from t1; create table if not exists t1 select 3 as 'a',4 as 'b'; --error 1062 create table if not exists t1 select 3 as 'a',3 as 'b'; +show warnings; +show status like "Opened_tables"; select * from t1; drop table t1; @@ -676,3 +679,37 @@ insert into t1 values('aaa'); drop table t1; # End of 5.0 tests + +# +# Test of behaviour with CREATE ... SELECT +# + +CREATE TABLE t1 (a int, b int); +insert into t1 values (1,1),(1,2); +--error 1062 +CREATE TABLE t2 (primary key (a)) select * from t1; +# This should give warning +drop table if exists t2; +--error 1062 +CREATE TEMPORARY TABLE t2 (primary key (a)) select * from t1; +# This should give warning +drop table if exists t2; +CREATE TABLE t2 (a int, b int, primary key (a)); +--error 1062 +CREATE TABLE IF NOT EXISTS t2 (primary key (a)) select * from t1; +SELECT * from t2; +TRUNCATE table t2; +--error 1062 +INSERT INTO t2 select * from t1; +SELECT * from t2; +drop table t2; + +CREATE TEMPORARY TABLE t2 (a int, b int, primary key (a)); +--error 1062 +CREATE TEMPORARY TABLE IF NOT EXISTS t2 (primary key (a)) select * from t1; +SELECT * from t2; +TRUNCATE table t2; +--error 1062 +INSERT INTO t2 select * from t1; +SELECT * from t2; +drop table t1,t2; diff --git a/mysql-test/t/disabled.def b/mysql-test/t/disabled.def index d6083ab8bfe..96f31133e65 100644 --- a/mysql-test/t/disabled.def +++ b/mysql-test/t/disabled.def @@ -13,6 +13,9 @@ #events_stress : BUG#17619 2006-02-21 andrey Race conditions #events : BUG#17619 2006-02-21 andrey Race conditions #events_scheduling : BUG#19170 2006-04-26 andrey Test case of 19170 fails on some platforms. Has to be checked. +im_instance_conf : Bug#20294 2006-06-06 monty Instance manager test im_instance_conf fails randomly +im_options : Bug#20294 2006-06-06 monty Instance manager test im_instance_conf fails randomly +im_life_cycle : Bug#20368 2006-06-10 alik im_life_cycle test fails ndb_autodiscover : BUG#18952 2006-02-16 jmiller Needs to be fixed w.r.t binlog ndb_autodiscover2 : BUG#18952 2006-02-16 jmiller Needs to be fixed w.r.t binlog #ndb_binlog_discover : BUG#19395 2006-04-28 tomas/knielsen mysqld does not always detect cluster shutdown @@ -22,17 +25,17 @@ ndb_load : BUG#17233 2006-05-04 tomas failed load data from infi partition_03ndb : BUG#16385 2006-03-24 mikael Partitions: crash when updating a range partitioned NDB table ps_7ndb : BUG#18950 2006-02-16 jmiller create table like does not obtain LOCK_open rpl_ndb_2innodb : BUG#19227 2006-04-20 pekka pk delete apparently not replicated -rpl_ndb_2myisam : BUG#19227 2006-04-20 pekka pk delete apparently not replicated +rpl_ndb_2myisam : BUG#19227 Seems to pass currently rpl_ndb_auto_inc : BUG#17086 2006-02-16 jmiller CR: auto_increment_increment and auto_increment_offset produce duplicate key er -rpl_ndb_commit_afterflush : BUG#19328 2006-05-04 tomas Slave timeout with COM_REGISTER_SLAVE error causing stop +#rpl_ndb_commit_afterflush : BUG#19328 2006-05-04 tomas Slave timeout with COM_REGISTER_SLAVE error causing stop rpl_ndb_dd_partitions : BUG#19259 2006-04-21 rpl_ndb_dd_partitions fails on s/AMD rpl_ndb_ddl : BUG#18946 result file needs update + test needs to checked -rpl_ndb_innodb2ndb : Bug #19710 Cluster replication to partition table fails on DELETE FROM statement -rpl_ndb_log : BUG#18947 2006-03-21 tomas CRBR: order in binlog of create table and insert (on different table) not determ -rpl_ndb_myisam2ndb : Bug #19710 Cluster replication to partition table fails on DELETE FROM statement +rpl_ndb_innodb2ndb : Bug #19710 Cluster replication to partition table fails on DELETE FROM statement +#rpl_ndb_log : BUG#18947 2006-03-21 tomas CRBR: order in binlog of create table and insert (on different table) not determ +rpl_ndb_myisam2ndb : Bug #19710 Cluster replication to partition table fails on DELETE FROM statement rpl_switch_stm_row_mixed : BUG#18590 2006-03-28 brian rpl_row_blob_innodb : BUG#18980 2006-04-10 kent Test fails randomly -rpl_row_func003 : BUG#19074 2006-13-04 andrei test failed +rpl_row_func003 : BUG#19074 2006-13-04 andrei test failed rpl_row_inexist_tbl : BUG#18948 2006-03-09 mats Disabled since patch makes this test wait forever rpl_sp : BUG#16456 2006-02-16 jmiller diff --git a/mysql-test/t/events.test b/mysql-test/t/events.test index 819d64ccf14..a3e2bbc0998 100644 --- a/mysql-test/t/events.test +++ b/mysql-test/t/events.test @@ -148,9 +148,9 @@ set names cp1251; create event ðóóò21 on schedule every '50:23:59:95' day_second COMMENT 'òîâà å 1251 êîìåíòàð' do select 1; SHOW CREATE EVENT ðóóò21; insert into mysql.event (db, name, body, definer, interval_value, interval_field) values (database(), "root22", "select 1", user(), 100, "SECOND_MICROSECOND"); ---error 1235 +--error ER_NOT_SUPPORTED_YET show create event root22; ---error 1235 +--error ER_NOT_SUPPORTED_YET SHOW EVENTS; drop event root22; drop event root6; @@ -239,82 +239,6 @@ DROP EVENT intact_check; # mysql.event intact checking end # -# -#INFORMATION_SCHEMA.EVENTS test begin -# -create event one_event on schedule every 10 second do select 123; ---replace_column 8 # 9 # -SHOW EVENTS; -SELECT EVENT_CATALOG, EVENT_SCHEMA, EVENT_NAME, DEFINER, EVENT_BODY, EVENT_TYPE, EXECUTE_AT, INTERVAL_VALUE, INTERVAL_FIELD, STATUS,ON_COMPLETION, EVENT_COMMENT from information_schema.events; -CREATE DATABASE events_test2; -CREATE USER ev_test@localhost; -GRANT ALL ON events_test.* to ev_test@localhost; -GRANT ALL on events_test2.* to ev_test@localhost; -REVOKE EVENT ON events_test2.* FROM ev_test@localhost; -REVOKE PROCESS on *.* from ev_test@localhost; -#now we are on con1 -connect (ev_con1,localhost,ev_test,,events_test2); -select "NEW CONNECTION"; -SELECT USER(), DATABASE(); -SHOW GRANTS; - ---echo "Here comes an error:"; -#NO EVENT_ACL on events_test2 ---error 1044 -SHOW EVENTS; -USE events_test; - ---echo "Now the list should be empty:"; ---replace_column 8 # 9 # -SHOW EVENTS; -#now create an event with the same name but we are different user -select concat("Let's create some new events from the name of ",user()); -create event one_event on schedule every 20 second do select 123; -create event two_event on schedule every 20 second on completion not preserve comment "two event" do select 123; -create event three_event on schedule every 20 second on completion preserve comment "three event" do select 123; - ---echo "Now we should see 3 events:"; ---replace_column 8 # 9 # -SHOW EVENTS; - ---echo "This should show us only 3 events:"; ---replace_column 8 # 9 # -SHOW EVENTS; - ---echo "This should show us only 2 events:"; ---replace_column 8 # 9 # -SHOW EVENTS LIKE 't%event'; - ---echo "This should show us no events:"; ---replace_column 8 # 9 # -SHOW EVENTS FROM test LIKE '%'; -#ok, we are back -connection default; -DROP DATABASE events_test2; - ---echo "should see 1 event:"; ---replace_column 8 # 9 # -SHOW EVENTS; - ---echo "we should see 4 events now:"; ---replace_column 8 # 9 # -SHOW EVENTS; -SELECT EVENT_CATALOG, EVENT_SCHEMA, EVENT_NAME, DEFINER, EVENT_BODY, EVENT_TYPE, EXECUTE_AT, INTERVAL_VALUE, INTERVAL_FIELD, STATUS,ON_COMPLETION, EVENT_COMMENT from information_schema.events; - -connection ev_con1; -drop event one_event; -drop event two_event; -drop event three_event; -disconnect ev_con1; -connection default; -drop user ev_test@localhost; -drop event one_event; -# -##INFORMATION_SCHEMA.EVENTS test end -# - ---echo "Sleep a bit so the server closes the second connection" ---sleep 2 create event e_26 on schedule at '2017-01-01 00:00:00' disable do set @a = 5; select db, name, body, definer, convert_tz(execute_at, 'UTC', 'SYSTEM'), on_completion from mysql.event; drop event e_26; @@ -358,7 +282,7 @@ select get_lock("test_lock2", 20); --echo "Create an event which tries to acquire a mutex. The event locks on the mutex" create event закачка on schedule every 10 hour do select get_lock("test_lock2", 20); --echo "Let some time pass to the event starts" ---sleep 2 +--sleep 1 --echo "Should have only 2 processes: the scheduler and the locked event" select /*2*/ user, host, db, command, state, info from information_schema.processlist where info is null or info not like '%processlist%' order by info;--echo "Release the mutex, the event worker should finish." --echo "Release the mutex, the event worker should finish." @@ -409,7 +333,6 @@ create event white_space on schedule every 10 hour disable do select 2; select event_schema, event_name, definer, event_body from information_schema.events where event_name='white_space'; -select event_schema, event_name, definer, event_body from information_schema.events where event_name='white_space'; drop event white_space; create event white_space on schedule every 10 hour disable do select 3; select event_schema, event_name, definer, event_body from information_schema.events where event_name='white_space'; diff --git a/mysql-test/t/events_grant.test b/mysql-test/t/events_grant.test new file mode 100644 index 00000000000..ba94944a3cf --- /dev/null +++ b/mysql-test/t/events_grant.test @@ -0,0 +1,105 @@ +CREATE DATABASE IF NOT EXISTS events_test; +use events_test; +# +# Events grants test begin +# +CREATE EVENT one_event ON SCHEDULE EVERY 10 SECOND DO SELECT 123; +--replace_column 8 # 9 # +SHOW EVENTS; +SELECT EVENT_CATALOG, EVENT_SCHEMA, EVENT_NAME, DEFINER, EVENT_BODY, EVENT_TYPE, EXECUTE_AT, INTERVAL_VALUE, INTERVAL_FIELD, STATUS,ON_COMPLETION, EVENT_COMMENT from information_schema.events; +CREATE DATABASE events_test2; +CREATE USER ev_test@localhost; +GRANT ALL ON events_test.* to ev_test@localhost; +GRANT ALL ON events_test2.* to ev_test@localhost; +REVOKE EVENT ON events_test2.* FROM ev_test@localhost; +#now we are on con1 +connect (ev_con1,localhost,ev_test,,events_test2); +select "NEW CONNECTION"; +SELECT USER(), DATABASE(); +SHOW GRANTS; +--echo "Here comes an error:"; +#NO EVENT_ACL on events_test2 +--error 1044 +SHOW EVENTS; +USE events_test; +--echo "We should see one event"; +--replace_column 8 # 9 # +SHOW EVENTS; +#now create an event with the same name but we are different user +SELECT CONCAT("Let's create some new events from the name of ", USER()); +--error ER_EVENT_ALREADY_EXISTS +CREATE EVENT one_event ON SCHEDULE EVERY 20 SECOND DO SELECT 123; +CREATE EVENT two_event ON SCHEDULE EVERY 20 SECOND ON COMPLETION NOT PRESERVE COMMENT "two event" DO SELECT 123; +CREATE EVENT three_event ON SCHEDULE EVERY 20 SECOND ON COMPLETION PRESERVE COMMENT "three event" DO SELECT 123; + +--echo "Now we should see 3 events:"; +--replace_column 8 # 9 # +SHOW EVENTS; + +--echo "This should show us only 2 events:"; +--replace_column 8 # 9 # +SHOW EVENTS LIKE 't%event'; + +--echo "This should show us no events:"; +--replace_column 8 # 9 # +SHOW EVENTS FROM test LIKE '%'; +#ok, we are back +connection default; +GRANT EVENT ON events_test2.* TO ev_test@localhost; +connection ev_con1; +USE events_test2; +CREATE EVENT four_event ON SCHEDULE EVERY 20 SECOND DO SELECT 42; +connection default; +USE events_test; +--echo "We should see 4 events : one_event, two_event, three_event & four_event" +SELECT EVENT_CATALOG, EVENT_SCHEMA, EVENT_NAME, DEFINER, EVENT_BODY, EVENT_TYPE, EXECUTE_AT, INTERVAL_VALUE, INTERVAL_FIELD, STATUS,ON_COMPLETION, EVENT_COMMENT FROM INFORMATION_SCHEMA.EVENTS; +DROP DATABASE events_test2; +--echo "We should see 3 events : one_event, two_event, three_event" +SELECT EVENT_CATALOG, EVENT_SCHEMA, EVENT_NAME, DEFINER, EVENT_BODY, EVENT_TYPE, EXECUTE_AT, INTERVAL_VALUE, INTERVAL_FIELD, STATUS,ON_COMPLETION, EVENT_COMMENT FROM INFORMATION_SCHEMA.EVENTS; + +connection default; +CREATE DATABASE events_test2; +USE events_test2; +CREATE EVENT five_event ON SCHEDULE EVERY 20 SECOND DO SELECT 42; + +connection ev_con1; +--echo "Should see 4 events - one, two, three & five" +SELECT EVENT_CATALOG, EVENT_SCHEMA, EVENT_NAME, DEFINER, EVENT_BODY, EVENT_TYPE, EXECUTE_AT, INTERVAL_VALUE, INTERVAL_FIELD, STATUS,ON_COMPLETION, EVENT_COMMENT FROM INFORMATION_SCHEMA.EVENTS; +connection default; +REVOKE EVENT ON events_test2.* FROM ev_test@localhost; +connection ev_con1; +USE test; +--echo "Should see 3 events - one, two & three" +SELECT EVENT_CATALOG, EVENT_SCHEMA, EVENT_NAME, DEFINER, EVENT_BODY, EVENT_TYPE, EXECUTE_AT, INTERVAL_VALUE, INTERVAL_FIELD, STATUS,ON_COMPLETION, EVENT_COMMENT FROM INFORMATION_SCHEMA.EVENTS; +--echo "Let's test ALTER EVENT which changes the definer" +USE events_test; +ALTER EVENT one_event ON SCHEDULE EVERY 10 SECOND; +--echo "The definer should be ev_test@localhost" +SELECT EVENT_CATALOG, EVENT_SCHEMA, EVENT_NAME, DEFINER, EVENT_BODY, EVENT_TYPE, EXECUTE_AT, INTERVAL_VALUE, INTERVAL_FIELD, STATUS,ON_COMPLETION, EVENT_COMMENT FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_NAME='one_event'; +connection default; +USE events_test; +ALTER EVENT one_event COMMENT "comment"; +connection ev_con1; +--echo "The definer should be root@localhost" +SELECT EVENT_CATALOG, EVENT_SCHEMA, EVENT_NAME, DEFINER, EVENT_BODY, EVENT_TYPE, EXECUTE_AT, INTERVAL_VALUE, INTERVAL_FIELD, STATUS,ON_COMPLETION, EVENT_COMMENT FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_NAME='one_event'; +ALTER EVENT one_event DO SELECT 12; +--echo "The definer should be ev_test@localhost" +SELECT EVENT_CATALOG, EVENT_SCHEMA, EVENT_NAME, DEFINER, EVENT_BODY, EVENT_TYPE, EXECUTE_AT, INTERVAL_VALUE, INTERVAL_FIELD, STATUS,ON_COMPLETION, EVENT_COMMENT FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_NAME='one_event'; +connection default; +--echo "make the definer again root@localhost" +ALTER EVENT one_event COMMENT "new comment"; +connection ev_con1; +--echo "test DROP by another user" +DROP EVENT one_event; +connection default; +--echo "One event should not be there" +SELECT EVENT_CATALOG, EVENT_SCHEMA, EVENT_NAME, DEFINER, EVENT_BODY, EVENT_TYPE, EXECUTE_AT, INTERVAL_VALUE, INTERVAL_FIELD, STATUS,ON_COMPLETION, EVENT_COMMENT FROM INFORMATION_SCHEMA.EVENTS; +disconnect ev_con1; +connection default; +DROP USER ev_test@localhost; +DROP DATABASE events_test2; +# +## EVENTS grants test end +# + +DROP DATABASE events_test; diff --git a/mysql-test/t/events_scheduling.test b/mysql-test/t/events_scheduling.test index a73d25cd8ee..c688864a1e6 100644 --- a/mysql-test/t/events_scheduling.test +++ b/mysql-test/t/events_scheduling.test @@ -6,7 +6,9 @@ CREATE TABLE table_3(a int); CREATE TABLE table_4(a int); CREATE TABLE T19170(s1 TIMESTAMP); SET GLOBAL event_scheduler=1; -CREATE EVENT E19170 ON SCHEDULE EVERY 1 SECOND DO INSERT INTO T19170 VALUES(CURRENT_TIMESTAMP); +# We need to have 2 to make it safe with valgrind. This is probably because +# of when we calculate the timestamp value +CREATE EVENT E19170 ON SCHEDULE EVERY 2 SECOND DO INSERT INTO T19170 VALUES(CURRENT_TIMESTAMP); CREATE EVENT two_sec ON SCHEDULE EVERY 2 SECOND DO INSERT INTO table_1 VALUES(1); CREATE EVENT start_n_end ON SCHEDULE EVERY 1 SECOND diff --git a/mysql-test/t/explain.test b/mysql-test/t/explain.test index a38771db233..efce0cdf3b5 100644 --- a/mysql-test/t/explain.test +++ b/mysql-test/t/explain.test @@ -15,9 +15,9 @@ explain select * from t1 ignore key (str) where str="foo"; explain select * from t1 use key (str,str) where str="foo"; #The following should give errors ---error 1072 +--error 1176 explain select * from t1 use key (str,str,foo) where str="foo"; ---error 1072 +--error 1176 explain select * from t1 ignore key (str,str,foo) where str="foo"; drop table t1; diff --git a/mysql-test/t/federated.test b/mysql-test/t/federated.test index 3c43fb1d1f9..f6799f3630a 100644 --- a/mysql-test/t/federated.test +++ b/mysql-test/t/federated.test @@ -1,6 +1,6 @@ # should work with embedded server after mysqltest is fixed --- source include/not_embedded.inc -source include/federated.inc; +--source include/not_embedded.inc +--source include/federated.inc connection slave; DROP TABLE IF EXISTS federated.t1; @@ -1310,6 +1310,57 @@ select * from federated.t1 where fld_parentid=0 and fld_delt=0; DROP TABLE federated.t1; connection slave; DROP TABLE federated.bug_17377_table; +DROP TABLE federated.t1; + +# +# Test multi updates and deletes without keys +# + +# The following can be enabled when bug #19773 has been fixed +--disable_parsing +connection slave; +create table federated.t1 (i1 int, i2 int, i3 int); +create table federated.t2 (id int, c1 varchar(20), c2 varchar(20)); +connection master; +eval create table federated.t1 (i1 int, i2 int, i3 int) ENGINE=FEDERATED CONNECTION='mysql://root@127.0.0.1:$SLAVE_MYPORT/federated/t1'; +eval create table federated.t2 (id int, c1 varchar(20), c2 varchar(20)) ENGINE=FEDERATED CONNECTION='mysql://root@127.0.0.1:$SLAVE_MYPORT/federated/t2'; +insert into federated.t1 values (1,5,10),(3,7,12),(4,5,2),(9,10,15),(2,2,2); +insert into federated.t2 values (9,"abc","def"),(5,"opq","lmn"),(2,"test t","t test"); +select * from federated.t1 order by i1; +select * from federated.t2; +update federated.t1,federated.t2 set t1.i2=15, t2.c2="ppc" where t1.i1=t2.id; +select * from federated.t1 order by i1; +select * from federated.t2 order by id; +delete t1.*,t2.* from federated.t1,federated.t2 where t1.i2=t2.id; +select * from federated.t1 order by i1; +select * from federated.t2 order by id; +drop table federated.t1, federated.t2; +connection slave; +drop table federated.t1, federated.t2; +connection master; +# Test multi updates and deletes with keys + +connection slave; +create table federated.t1 (i1 int, i2 int, i3 int, primary key (i1)); +create table federated.t2 (id int, c1 varchar(20), c2 varchar(20), primary key (id)); +connection master; +eval create table federated.t1 (i1 int auto_increment not null, i2 int, i3 int, primary key (i1)) ENGINE=FEDERATED ENGINE=FEDERATED CONNECTION='mysql://root@127.0.0.1:$SLAVE_MYPORT/federated/t1'; +eval create table federated.t2 (id int auto_increment not null, c1 varchar(20), c2 varchar(20), primary key(id)) ENGINE=FEDERATED ENGINE=FEDERATED CONNECTION='mysql://root@127.0.0.1:$SLAVE_MYPORT/federated/t2'; +insert into federated.t1 values (1,5,10),(3,7,12),(4,5,2),(9,10,15),(2,2,2); +insert into federated.t2 values (9,"abc","def"),(5,"opq","lmn"),(2,"test t","t test"); +select * from federated.t1 order by i1; +select * from federated.t2 order by id; +update federated.t1,federated.t2 set t1.i2=15, t2.c2="ppc" where t1.i1=t2.id; +select * from federated.t1 order by i1; +select * from federated.t2 order by id; +delete t1.*,t2.* from federated.t1,federated.t2 where t1.i2=t2.id; +select * from federated.t1 order by i1; +select * from federated.t2 order by id; +drop table federated.t1, federated.t2; +connection slave; +drop table federated.t1, federated.t2; +connection master; +--enable_parsing -source include/federated_cleanup.inc; +--source include/federated_cleanup.inc diff --git a/mysql-test/t/flush.test b/mysql-test/t/flush.test index f5fd9fcadf2..95ba633fefd 100644 --- a/mysql-test/t/flush.test +++ b/mysql-test/t/flush.test @@ -102,3 +102,43 @@ unlock tables; drop table t1, t2, t3; # End of 4.1 tests + +# +# Test of deadlock problem when doing FLUSH TABLE with read lock +# (Bug was in NTPL threads in Linux when using different mutex while +# waiting for a condtion variable) + +create table t1 (c1 int); +create table t2 (c1 int); + +connect (con1,localhost,root,,); +connect (con3,localhost,root,,); + +connection con1; +lock table t1 write; + +connection con2; +send flush tables with read lock; +--sleep 1 + +connection con3; +send insert into t2 values(1); +--sleep 1 + +connection con1; +unlock tables; +disconnect con1; + +connection con2; +reap; +disconnect con2; + +connection con3; +# It hangs here (insert into t2 does not end). +reap; +disconnect con3; + +connection default; +drop table t1, t2; + +# End of 5.0 tests diff --git a/mysql-test/t/func_gconcat.test b/mysql-test/t/func_gconcat.test index fbfdfa3b5d0..7fd7edddf28 100644 --- a/mysql-test/t/func_gconcat.test +++ b/mysql-test/t/func_gconcat.test @@ -176,8 +176,8 @@ select t1.a, group_concat(c order by (select mid(group_concat(c order by a),1,5) select t1.a, group_concat(c order by (select mid(group_concat(c order by a),1,5) from t2 where t2.a=t1.a) desc) as grp from t1 group by 1; # The following returns random results as we are sorting on blob addresses -# select group_concat(c order by (select group_concat(c order by a) from t2 where t2.a=t1.a)) as grp from t1; -# select group_concat(c order by (select group_concat(c) from t2 where a=t1.a)) as grp from t1; +select group_concat(c order by (select concat(5-t1.c,group_concat(c order by a)) from t2 where t2.a=t1.a)) as grp from t1; +select group_concat(c order by (select concat(t1.c,group_concat(c)) from t2 where a=t1.a)) as grp from t1; select a,c,(select group_concat(c order by a) from t2 where a=t1.a) as grp from t1 order by grp; drop table t1,t2; diff --git a/mysql-test/t/func_time.test b/mysql-test/t/func_time.test index 5c993028cb4..e6c1ddfcf1f 100644 --- a/mysql-test/t/func_time.test +++ b/mysql-test/t/func_time.test @@ -385,6 +385,7 @@ select time_format('100:00:00', '%H %k %h %I %l'); create table t1 (a timestamp default '2005-05-05 01:01:01', b timestamp default '2005-05-05 01:01:01'); delimiter //; +drop function if exists t_slow_sysdate; create function t_slow_sysdate() returns timestamp begin do sleep(2); diff --git a/mysql-test/t/grant.test b/mysql-test/t/grant.test index 97f13381557..a9d52f559ca 100644 --- a/mysql-test/t/grant.test +++ b/mysql-test/t/grant.test @@ -302,7 +302,7 @@ DROP DATABASE testdb10; create table t1(a int, b int, c int, d int); grant insert(b), insert(c), insert(d), insert(a) on t1 to grant_user@localhost; show grants for grant_user@localhost; -select Host,Db,User,Table_name,Column_name,Column_priv from mysql.columns_priv; +select Host,Db,User,Table_name,Column_name,Column_priv from mysql.columns_priv order by Column_name; revoke ALL PRIVILEGES on t1 from grant_user@localhost; show grants for grant_user@localhost; select Host,Db,User,Table_name,Column_name,Column_priv from mysql.columns_priv; @@ -326,7 +326,18 @@ grant select (c) on mysqltest_2.t1 to mysqltest_3@localhost; grant update (d) on mysqltest_2.t2 to mysqltest_3@localhost; connect (conn1,localhost,mysqltest_3,,); connection conn1; -show grants for mysqltest_3@localhost; +SELECT * FROM INFORMATION_SCHEMA.COLUMN_PRIVILEGES + WHERE GRANTEE = '''mysqltest_3''@''localhost''' + ORDER BY TABLE_NAME,COLUMN_NAME,PRIVILEGE_TYPE; +SELECT * FROM INFORMATION_SCHEMA.TABLE_PRIVILEGES + WHERE GRANTEE = '''mysqltest_3''@''localhost''' + ORDER BY TABLE_NAME,PRIVILEGE_TYPE; +SELECT * from INFORMATION_SCHEMA.SCHEMA_PRIVILEGES + WHERE GRANTEE = '''mysqltest_3''@''localhost''' + ORDER BY TABLE_SCHEMA,PRIVILEGE_TYPE; +SELECT * from INFORMATION_SCHEMA.USER_PRIVILEGES + WHERE GRANTEE = '''mysqltest_3''@''localhost''' + ORDER BY TABLE_CATALOG,PRIVILEGE_TYPE; --error 1143 update mysqltest_1.t1, mysqltest_1.t2 set q=10 where b=1; --error 1143 @@ -479,6 +490,7 @@ insert into tables_priv values ('','test_db','mysqltest_1','test_table','test_gr flush privileges; delete from tables_priv where host = '' and user = 'mysqltest_1'; flush privileges; +use test; # # Bug #10892 user variables not auto cast for comparisons @@ -507,7 +519,6 @@ create user mysqltest_7@; set password for mysqltest_7@ = password('systpass'); show grants for mysqltest_7@; drop user mysqltest_7@; -flush privileges; # BUG#16297(flush should be removed when that bug is fixed) --error 1141 show grants for mysqltest_7@; @@ -528,3 +539,145 @@ flush privileges; drop database mysqltest; # End of 4.1 tests + +# +# Bug #16297 In memory grant tables not flushed when users's hostname is "" +# +use test; +create table t1 (a int); + +# Backup anonymous users and remove them. (They get in the way of +# the one we test with here otherwise.) +create table t2 as select * from mysql.user where user=''; +delete from mysql.user where user=''; +flush privileges; + +# Create some users with different hostnames +create user mysqltest_8@''; +create user mysqltest_8; +create user mysqltest_8@host8; + +# Try to create them again +--error 1396 +create user mysqltest_8@''; +--error 1396 +create user mysqltest_8; +--error 1396 +create user mysqltest_8@host8; + +select user, QUOTE(host) from mysql.user where user="mysqltest_8"; + +--echo Schema privileges +grant select on mysqltest.* to mysqltest_8@''; +show grants for mysqltest_8@''; +grant select on mysqltest.* to mysqltest_8@; +show grants for mysqltest_8@; +grant select on mysqltest.* to mysqltest_8; +show grants for mysqltest_8; +select * from information_schema.schema_privileges +where grantee like "'mysqltest_8'%"; +connect (conn3,localhost,mysqltest_8,,); +select * from t1; +disconnect conn3; +connection master; +revoke select on mysqltest.* from mysqltest_8@''; +revoke select on mysqltest.* from mysqltest_8; +show grants for mysqltest_8@''; +show grants for mysqltest_8; +select * from information_schema.schema_privileges +where grantee like "'mysqltest_8'%"; +flush privileges; +show grants for mysqltest_8@''; +show grants for mysqltest_8@; +grant select on mysqltest.* to mysqltest_8@''; +flush privileges; +show grants for mysqltest_8@; +revoke select on mysqltest.* from mysqltest_8@''; +flush privileges; + +--echo Column privileges +grant update (a) on t1 to mysqltest_8@''; +grant update (a) on t1 to mysqltest_8; +show grants for mysqltest_8@''; +show grants for mysqltest_8; +flush privileges; +show grants for mysqltest_8@''; +show grants for mysqltest_8; +select * from information_schema.column_privileges; +connect (conn4,localhost,mysqltest_8,,); +select * from t1; +disconnect conn4; +connection master; +revoke update (a) on t1 from mysqltest_8@''; +revoke update (a) on t1 from mysqltest_8; +show grants for mysqltest_8@''; +show grants for mysqltest_8; +select * from information_schema.column_privileges; +flush privileges; +show grants for mysqltest_8@''; +show grants for mysqltest_8; + +--echo Table privileges +grant update on t1 to mysqltest_8@''; +grant update on t1 to mysqltest_8; +show grants for mysqltest_8@''; +show grants for mysqltest_8; +flush privileges; +show grants for mysqltest_8@''; +show grants for mysqltest_8; +select * from information_schema.table_privileges; +connect (conn5,localhost,mysqltest_8,,); +select * from t1; +disconnect conn5; +connection master; +revoke update on t1 from mysqltest_8@''; +revoke update on t1 from mysqltest_8; +show grants for mysqltest_8@''; +show grants for mysqltest_8; +select * from information_schema.table_privileges; +flush privileges; +show grants for mysqltest_8@''; +show grants for mysqltest_8; + +--echo "DROP USER" should clear privileges +grant all privileges on mysqltest.* to mysqltest_8@''; +grant select on mysqltest.* to mysqltest_8@''; +grant update on t1 to mysqltest_8@''; +grant update (a) on t1 to mysqltest_8@''; +grant all privileges on mysqltest.* to mysqltest_8; +show grants for mysqltest_8@''; +show grants for mysqltest_8; +select * from information_schema.user_privileges +where grantee like "'mysqltest_8'%"; +connect (conn5,localhost,mysqltest_8,,); +select * from t1; +disconnect conn5; +connection master; +flush privileges; +show grants for mysqltest_8@''; +show grants for mysqltest_8; +drop user mysqltest_8@''; +--error 1141 +show grants for mysqltest_8@''; +show grants for mysqltest_8; +select * from information_schema.user_privileges +where grantee like "'mysqltest_8'%"; +drop user mysqltest_8; +--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT +--error 1045 +connect (conn6,localhost,mysqltest_8,,); +connection master; +--error 1141 +show grants for mysqltest_8; +drop user mysqltest_8@host8; +--error 1141 +show grants for mysqltest_8@host8; + +# Restore the anonymous users. +insert into mysql.user select * from t2; +flush privileges; +drop table t2; + +drop table t1; + + diff --git a/mysql-test/t/group_min_max.test b/mysql-test/t/group_min_max.test index d9836ccc8df..874f3cd1a80 100644 --- a/mysql-test/t/group_min_max.test +++ b/mysql-test/t/group_min_max.test @@ -659,59 +659,7 @@ select a1 from t1 where a2 = 'b' group by a1; explain select distinct a1 from t1 where a2 = 'b'; select distinct a1 from t1 where a2 = 'b'; -# -# Bug #12672: primary key implcitly included in every innodb index -# - ---disable_warnings -create table t4 ( - pk_col int auto_increment primary key, a1 char(64), a2 char(64), b char(16), c char(16) not null, d char(16), dummy char(64) default ' ' -) engine=innodb; ---enable_warnings -insert into t4 (a1, a2, b, c, d, dummy) select * from t1; - -create index idx12672_0 on t4 (a1); -create index idx12672_1 on t4 (a1,a2,b,c); -create index idx12672_2 on t4 (a1,a2,b); -analyze table t1; - -select distinct a1 from t4 where pk_col not in (1,2,3,4); - -drop table t1,t2,t3,t4; - -# -# Bug #6142: a problem with the empty innodb table -# - ---disable_warnings -create table t1 ( - a varchar(30), b varchar(30), primary key(a), key(b) -) engine=innodb; ---enable_warnings -select distinct a from t1; -drop table t1; - -# -# Bug #9798: group by with rollup -# - ---disable_warnings -create table t1(a int, key(a)) engine=innodb; ---enable_warnings -insert into t1 values(1); -select a, count(a) from t1 group by a with rollup; -drop table t1; - -# -# Bug #13293 Wrongly used index results in endless loop. -# -create table t1 (f1 int, f2 char(1), primary key(f1,f2)) engine=innodb; -insert into t1 values ( 1,"e"),(2,"a"),( 3,"c"),(4,"d"); -alter table t1 drop primary key, add primary key (f2, f1); -explain select distinct f1 a, f1 b from t1; -explain select distinct f1, f2 from t1; -drop table t1; - +drop table t1,t2,t3; # # Bug #14920 Ordering aggregated result sets with composite primary keys # corrupts resultset diff --git a/mysql-test/t/heap_btree.test b/mysql-test/t/heap_btree.test index f1b9d290885..fb715fccefe 100644 --- a/mysql-test/t/heap_btree.test +++ b/mysql-test/t/heap_btree.test @@ -176,4 +176,12 @@ UPDATE t1 SET val=1; SELECT INDEX_LENGTH FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='t1'; DROP TABLE t1; +# +# BUG#12873 - BTREE index on MEMORY table with multiple NULL values doesn't +# work properly +# +CREATE TABLE t1 (a INT, UNIQUE USING BTREE(a)) ENGINE=MEMORY; +INSERT INTO t1 VALUES(NULL),(NULL); +DROP TABLE t1; + # End of 4.1 tests diff --git a/mysql-test/t/information_schema.test b/mysql-test/t/information_schema.test index 4db108e1b40..acb877e1e74 100644 --- a/mysql-test/t/information_schema.test +++ b/mysql-test/t/information_schema.test @@ -5,9 +5,6 @@ # on the presence of the log tables (which are CSV-based). --source include/have_csv.inc -# This test uses chmod, can't be run with root permissions --- source include/not_as_root.inc - # Test for information_schema.schemata & # show databases diff --git a/mysql-test/t/information_schema_chmod.test b/mysql-test/t/information_schema_chmod.test index fb850b8e38d..c7ea2b03890 100644 --- a/mysql-test/t/information_schema_chmod.test +++ b/mysql-test/t/information_schema_chmod.test @@ -8,6 +8,9 @@ # --source include/not_windows.inc +# This test uses chmod, can't be run with root permissions +-- source include/not_as_root.inc + # # Bug #15851 Unlistable directories yield no info from information_schema diff --git a/mysql-test/t/information_schema_db.test b/mysql-test/t/information_schema_db.test index b65135a621d..2cfa766d799 100644 --- a/mysql-test/t/information_schema_db.test +++ b/mysql-test/t/information_schema_db.test @@ -1,16 +1,25 @@ -- source include/testdb_only.inc +--disable_warnings +drop table if exists t1,t2; +drop view if exists v1,v2; +drop function if exists f1; +drop function if exists f2; +--enable_warnings + use INFORMATION_SCHEMA; --replace_result Tables_in_INFORMATION_SCHEMA Tables_in_information_schema show tables; --replace_result 'Tables_in_INFORMATION_SCHEMA (T%)' 'Tables_in_information_schema (T%)' show tables from INFORMATION_SCHEMA like 'T%'; create database `inf%`; +create database mbase; use `inf%`; show tables; # # Bug#18113 SELECT * FROM information_schema.xxx crashes server +# Bug#17204 second CALL to procedure crashes Server # Crash happened when one selected data from one of INFORMATION_SCHEMA # tables and in order to build its contents server had to open view which # used stored function and table or view on which one had not global or @@ -18,6 +27,7 @@ show tables; # privileges at all). # grant all privileges on `inf%`.* to 'mysqltest_1'@'localhost'; +grant all privileges on `mbase`.* to 'mysqltest_1'@'localhost'; create table t1 (f1 int); delimiter |; create function func1(curr_int int) returns int @@ -28,15 +38,63 @@ begin end| delimiter ;| create view v1 as select f1 from t1 where f1 = func1(f1); +create function func2() returns int return 1; + +use mbase; +delimiter |; +create procedure p1 () +begin +select table_name from information_schema.key_column_usage +order by table_name; +end| +delimiter ;| + +create table t1 +(f1 int(10) unsigned not null, + f2 varchar(100) not null, + primary key (f1), unique key (f2)); + connect (user1,localhost,mysqltest_1,,); connection user1; --disable_result_log select * from information_schema.tables; +call mbase.p1(); +call mbase.p1(); +call mbase.p1(); --enable_result_log + connection default; +use `inf%`; drop user mysqltest_1@localhost; +drop table t1; +select table_name, table_type, table_comment from information_schema.tables +where table_schema='inf%' and func2(); +select table_name, table_type, table_comment from information_schema.tables +where table_schema='inf%' and func2(); drop view v1; drop function func1; -drop table t1; +drop function func2; drop database `inf%`; +drop procedure mbase.p1; +drop database mbase; + +# +# Bug#18282 INFORMATION_SCHEMA.TABLES provides inconsistent info about invalid views +# +use test; +create table t1 (i int); +create function f1 () returns int return (select max(i) from t1); +create view v1 as select f1(); +create table t2 (id int); +create function f2 () returns int return (select max(i) from t2); +create view v2 as select f2(); +drop table t2; +select table_name, table_type, table_comment from information_schema.tables +where table_schema='test'; +drop table t1; +select table_name, table_type, table_comment from information_schema.tables +where table_schema='test'; +drop function f1; +drop function f2; +drop view v1, v2; diff --git a/mysql-test/t/information_schema_part.test b/mysql-test/t/information_schema_part.test index 163b04248b8..4cbf21ca1d3 100644 --- a/mysql-test/t/information_schema_part.test +++ b/mysql-test/t/information_schema_part.test @@ -99,3 +99,25 @@ select * from information_schema.partitions where table_schema="test" and table_name="t1"; drop table t1; +# +# Bug 20161 Partitions: SUBPARTITION METHOD doesn't show LINEAR keyword +# +create table t1 (a int) +PARTITION BY RANGE (a) +SUBPARTITION BY LINEAR HASH (a) +(PARTITION p0 VALUES LESS THAN (10)); + +SHOW CREATE TABLE t1; +select SUBPARTITION_METHOD FROM information_schema.partitions WHERE +table_schema="test" AND table_name="t1"; +drop table t1; + +create table t1 (a int) +PARTITION BY LIST (a) +(PARTITION p0 VALUES IN +(10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, + 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53)); +SHOW CREATE TABLE t1; +SELECT PARTITION_DESCRIPTION FROM information_schema.partitions WHERE +table_schema = "test" AND table_name = "t1"; +drop table t1; diff --git a/mysql-test/t/innodb_mysql.test b/mysql-test/t/innodb_mysql.test index 3de55e1c403..0a9eeb049b0 100644 --- a/mysql-test/t/innodb_mysql.test +++ b/mysql-test/t/innodb_mysql.test @@ -1,7 +1,7 @@ -- source include/have_innodb.inc --disable_warnings -drop table if exists t1,t2; +drop table if exists t1,t2,t1m,t1i,t2m,t2i,t4; --enable_warnings # BUG#16798: Uninitialized row buffer reads in ref-or-null optimizer @@ -129,3 +129,146 @@ select count(*), min(7), max(7) from t2m, t1i; drop table t1m, t1i, t2m, t2i; +# +# Bug #12672: primary key implcitly included in every innodb index +# (was part of group_min_max.test) +# + +create table t1 ( + a1 char(64), a2 char(64), b char(16), c char(16) not null, d char(16), dummy char(64) default ' ' +); + +insert into t1 (a1, a2, b, c, d) values +('a','a','a','a111','xy1'),('a','a','a','b111','xy2'),('a','a','a','c111','xy3'),('a','a','a','d111','xy4'), +('a','a','b','e112','xy1'),('a','a','b','f112','xy2'),('a','a','b','g112','xy3'),('a','a','b','h112','xy4'), +('a','b','a','i121','xy1'),('a','b','a','j121','xy2'),('a','b','a','k121','xy3'),('a','b','a','l121','xy4'), +('a','b','b','m122','xy1'),('a','b','b','n122','xy2'),('a','b','b','o122','xy3'),('a','b','b','p122','xy4'), +('b','a','a','a211','xy1'),('b','a','a','b211','xy2'),('b','a','a','c211','xy3'),('b','a','a','d211','xy4'), +('b','a','b','e212','xy1'),('b','a','b','f212','xy2'),('b','a','b','g212','xy3'),('b','a','b','h212','xy4'), +('b','b','a','i221','xy1'),('b','b','a','j221','xy2'),('b','b','a','k221','xy3'),('b','b','a','l221','xy4'), +('b','b','b','m222','xy1'),('b','b','b','n222','xy2'),('b','b','b','o222','xy3'),('b','b','b','p222','xy4'), +('c','a','a','a311','xy1'),('c','a','a','b311','xy2'),('c','a','a','c311','xy3'),('c','a','a','d311','xy4'), +('c','a','b','e312','xy1'),('c','a','b','f312','xy2'),('c','a','b','g312','xy3'),('c','a','b','h312','xy4'), +('c','b','a','i321','xy1'),('c','b','a','j321','xy2'),('c','b','a','k321','xy3'),('c','b','a','l321','xy4'), +('c','b','b','m322','xy1'),('c','b','b','n322','xy2'),('c','b','b','o322','xy3'),('c','b','b','p322','xy4'), +('d','a','a','a411','xy1'),('d','a','a','b411','xy2'),('d','a','a','c411','xy3'),('d','a','a','d411','xy4'), +('d','a','b','e412','xy1'),('d','a','b','f412','xy2'),('d','a','b','g412','xy3'),('d','a','b','h412','xy4'), +('d','b','a','i421','xy1'),('d','b','a','j421','xy2'),('d','b','a','k421','xy3'),('d','b','a','l421','xy4'), +('d','b','b','m422','xy1'),('d','b','b','n422','xy2'),('d','b','b','o422','xy3'),('d','b','b','p422','xy4'), +('a','a','a','a111','xy1'),('a','a','a','b111','xy2'),('a','a','a','c111','xy3'),('a','a','a','d111','xy4'), +('a','a','b','e112','xy1'),('a','a','b','f112','xy2'),('a','a','b','g112','xy3'),('a','a','b','h112','xy4'), +('a','b','a','i121','xy1'),('a','b','a','j121','xy2'),('a','b','a','k121','xy3'),('a','b','a','l121','xy4'), +('a','b','b','m122','xy1'),('a','b','b','n122','xy2'),('a','b','b','o122','xy3'),('a','b','b','p122','xy4'), +('b','a','a','a211','xy1'),('b','a','a','b211','xy2'),('b','a','a','c211','xy3'),('b','a','a','d211','xy4'), +('b','a','b','e212','xy1'),('b','a','b','f212','xy2'),('b','a','b','g212','xy3'),('b','a','b','h212','xy4'), +('b','b','a','i221','xy1'),('b','b','a','j221','xy2'),('b','b','a','k221','xy3'),('b','b','a','l221','xy4'), +('b','b','b','m222','xy1'),('b','b','b','n222','xy2'),('b','b','b','o222','xy3'),('b','b','b','p222','xy4'), +('c','a','a','a311','xy1'),('c','a','a','b311','xy2'),('c','a','a','c311','xy3'),('c','a','a','d311','xy4'), +('c','a','b','e312','xy1'),('c','a','b','f312','xy2'),('c','a','b','g312','xy3'),('c','a','b','h312','xy4'), +('c','b','a','i321','xy1'),('c','b','a','j321','xy2'),('c','b','a','k321','xy3'),('c','b','a','l321','xy4'), +('c','b','b','m322','xy1'),('c','b','b','n322','xy2'),('c','b','b','o322','xy3'),('c','b','b','p322','xy4'), +('d','a','a','a411','xy1'),('d','a','a','b411','xy2'),('d','a','a','c411','xy3'),('d','a','a','d411','xy4'), +('d','a','b','e412','xy1'),('d','a','b','f412','xy2'),('d','a','b','g412','xy3'),('d','a','b','h412','xy4'), +('d','b','a','i421','xy1'),('d','b','a','j421','xy2'),('d','b','a','k421','xy3'),('d','b','a','l421','xy4'), +('d','b','b','m422','xy1'),('d','b','b','n422','xy2'),('d','b','b','o422','xy3'),('d','b','b','p422','xy4'); +--disable_warnings +create table t4 ( + pk_col int auto_increment primary key, a1 char(64), a2 char(64), b char(16), c char(16) not null, d char(16), dummy char(64) default ' ' +) engine=innodb; +--enable_warnings +insert into t4 (a1, a2, b, c, d, dummy) select * from t1; + +create index idx12672_0 on t4 (a1); +create index idx12672_1 on t4 (a1,a2,b,c); +create index idx12672_2 on t4 (a1,a2,b); +analyze table t1; + +select distinct a1 from t4 where pk_col not in (1,2,3,4); + +drop table t1,t4; + +# +# Bug #6142: a problem with the empty innodb table +# (was part of group_min_max.test) +# + +--disable_warnings +create table t1 ( + a varchar(30), b varchar(30), primary key(a), key(b) +) engine=innodb; +--enable_warnings +select distinct a from t1; +drop table t1; + +# +# Bug #9798: group by with rollup +# (was part of group_min_max.test) +# + +--disable_warnings +create table t1(a int, key(a)) engine=innodb; +--enable_warnings +insert into t1 values(1); +select a, count(a) from t1 group by a with rollup; +drop table t1; + +# +# Bug #13293 Wrongly used index results in endless loop. +# (was part of group_min_max.test) +# +create table t1 (f1 int, f2 char(1), primary key(f1,f2)) engine=innodb; +insert into t1 values ( 1,"e"),(2,"a"),( 3,"c"),(4,"d"); +alter table t1 drop primary key, add primary key (f2, f1); +explain select distinct f1 a, f1 b from t1; +explain select distinct f1, f2 from t1; +drop table t1; + + +# +# Test of behaviour with CREATE ... SELECT +# + +set storage_engine=innodb; +CREATE TABLE t1 (a int, b int); +insert into t1 values (1,1),(1,2); +--error 1062 +CREATE TABLE t2 (primary key (a)) select * from t1; +# This should give warning +drop table if exists t2; +--error 1062 +CREATE TEMPORARY TABLE t2 (primary key (a)) select * from t1; +# This should give warning +drop table if exists t2; +CREATE TABLE t2 (a int, b int, primary key (a)); +BEGIN; +INSERT INTO t2 values(100,100); +--error 1062 +CREATE TABLE IF NOT EXISTS t2 (primary key (a)) select * from t1; +SELECT * from t2; +ROLLBACK; +SELECT * from t2; +TRUNCATE table t2; +--error 1062 +INSERT INTO t2 select * from t1; +SELECT * from t2; +drop table t2; + +CREATE TEMPORARY TABLE t2 (a int, b int, primary key (a)); +BEGIN; +INSERT INTO t2 values(100,100); +--error 1062 +CREATE TEMPORARY TABLE IF NOT EXISTS t2 (primary key (a)) select * from t1; +SELECT * from t2; +COMMIT; +BEGIN; +INSERT INTO t2 values(101,101); +--error 1062 +CREATE TEMPORARY TABLE IF NOT EXISTS t2 (primary key (a)) select * from t1; +SELECT * from t2; +ROLLBACK; +SELECT * from t2; +TRUNCATE table t2; +--error 1062 +INSERT INTO t2 select * from t1; +SELECT * from t2; +drop table t1,t2; diff --git a/mysql-test/t/insert.test b/mysql-test/t/insert.test index f3dd8e7e199..e2514083ea7 100644 --- a/mysql-test/t/insert.test +++ b/mysql-test/t/insert.test @@ -9,8 +9,8 @@ drop table if exists t1,t2,t3; create table t1 (a int not null); insert into t1 values (1); insert into t1 values (a+2); -insert into t1 values (a+3); -insert into t1 values (4),(a+5); +insert into t1 values (a+3),(a+4); +insert into t1 values (5),(a+6); select * from t1; drop table t1; @@ -176,3 +176,28 @@ insert into t2 select t1.* from t1, t2 t, t3 where t1.id1 = t.id2 and t.id2 = select count(*) from t2; drop table t1,t2,t3; +# +# Test different cases of duplicate fields +# + +create table t1 (a int, b int); +insert into t1 (a,b) values (a,b); +insert into t1 SET a=1, b=a+1; +insert into t1 (a,b) select 1,2; +INSERT INTO t1 ( a ) SELECT 0 ON DUPLICATE KEY UPDATE a = a + VALUES (a); +prepare stmt1 from ' replace into t1 (a,a) select 100, ''hundred'' '; +--error 1110 +execute stmt1; +--error 1110 +insert into t1 (a,b,b) values (1,1,1); +--error 1136 +insert into t1 (a,a) values (1,1,1); +--error 1110 +insert into t1 (a,a) values (1,1); +--error 1110 +insert into t1 SET a=1,b=2,a=1; +--error 1110 +insert into t1 (b,b) select 1,2; +--error 1110 +INSERT INTO t1 (b,b) SELECT 0,0 ON DUPLICATE KEY UPDATE a = a + VALUES (a); +drop table t1; diff --git a/mysql-test/t/loaddata.test b/mysql-test/t/loaddata.test index fcd127e3e98..aff5efa51cb 100644 --- a/mysql-test/t/loaddata.test +++ b/mysql-test/t/loaddata.test @@ -92,6 +92,10 @@ load data infile '../std_data_ln/rpl_loaddata.dat' into table t1 (@a, @b); select * from t1; select @a, @b; truncate table t1; +# Reading of all columns with set +load data infile '../std_data_ln/rpl_loaddata.dat' into table t1 set c=b; +select * from t1; +truncate table t1; # now going to test fixed field-row file format load data infile '../std_data_ln/loaddata5.dat' into table t1 fields terminated by '' enclosed by '' (a, b) set c="Wow"; select * from t1; diff --git a/mysql-test/t/lock_multi.test b/mysql-test/t/lock_multi.test index 3d07ec98d69..7fc65f52214 100644 --- a/mysql-test/t/lock_multi.test +++ b/mysql-test/t/lock_multi.test @@ -126,3 +126,68 @@ show columns from t1; connection locker; unlock tables; drop table t1; + +# +# Bug#19815 - CREATE/RENAME/DROP DATABASE can deadlock on a global read lock +# +connect (con1,localhost,root,,); +connect (con2,localhost,root,,); +# +connection con1; +CREATE DATABASE mysqltest_1; +FLUSH TABLES WITH READ LOCK; +# +# With bug in place: acquire LOCK_mysql_create_table and +# wait in wait_if_global_read_lock(). +connection con2; +send DROP DATABASE mysqltest_1; +--sleep 1 +# +# With bug in place: try to acquire LOCK_mysql_create_table... +# When fixed: Reject dropping db because of the read lock. +connection con1; +--error ER_CANT_UPDATE_WITH_READLOCK +DROP DATABASE mysqltest_1; +UNLOCK TABLES; +# +connection con2; +reap; +# +connection default; +disconnect con1; +disconnect con2; +# This must have been dropped by connection 2 already, +# which waited until the global read lock was released. +--error ER_DB_DROP_EXISTS +DROP DATABASE mysqltest_1; + +# Bug#16986 - Deadlock condition with MyISAM tables +# +connection locker; +use mysql; +LOCK TABLES columns_priv WRITE, db WRITE, host WRITE, user WRITE; +FLUSH TABLES; +--sleep 1 +# +connection reader; +use mysql; +#NOTE: This must be a multi-table select, otherwise the deadlock will not occur +send SELECT user.Select_priv FROM user, db WHERE user.user = db.user LIMIT 1; +--sleep 1 +# +connection locker; +# Make test case independent from earlier grants. +--replace_result "Table is already up to date" "OK" +OPTIMIZE TABLES columns_priv, db, host, user; +UNLOCK TABLES; +# +connection reader; +reap; +use test; +# +connection locker; +use test; +# +connection default; + +# End of 5.0 tests diff --git a/mysql-test/t/multi_update.test b/mysql-test/t/multi_update.test index acc816ae921..04c33e9d709 100644 --- a/mysql-test/t/multi_update.test +++ b/mysql-test/t/multi_update.test @@ -532,3 +532,33 @@ select * from t1; select * from t2; drop view v1; drop table t1, t2; + +# +# Test multi updates and deletes using primary key and without. +# +create table t1 (i1 int, i2 int, i3 int); +create table t2 (id int, c1 varchar(20), c2 varchar(20)); +insert into t1 values (1,5,10),(3,7,12),(4,5,2),(9,10,15),(2,2,2); +insert into t2 values (9,"abc","def"),(5,"opq","lmn"),(2,"test t","t test"); +select * from t1 order by i1; +select * from t2; +update t1,t2 set t1.i2=15, t2.c2="ppc" where t1.i1=t2.id; +select * from t1 order by i1; +select * from t2 order by id; +delete t1.*,t2.* from t1,t2 where t1.i2=t2.id; +select * from t1 order by i1; +select * from t2 order by id; +drop table t1, t2; +create table t1 (i1 int auto_increment not null, i2 int, i3 int, primary key (i1)); +create table t2 (id int auto_increment not null, c1 varchar(20), c2 varchar(20), primary key(id)); +insert into t1 values (1,5,10),(3,7,12),(4,5,2),(9,10,15),(2,2,2); +insert into t2 values (9,"abc","def"),(5,"opq","lmn"),(2,"test t","t test"); +select * from t1 order by i1; +select * from t2 order by id; +update t1,t2 set t1.i2=15, t2.c2="ppc" where t1.i1=t2.id; +select * from t1 order by i1; +select * from t2 order by id; +delete t1.*,t2.* from t1,t2 where t1.i2=t2.id; +select * from t1 order by i1; +select * from t2 order by id; +drop table t1, t2; diff --git a/mysql-test/t/mysqlbinlog.test b/mysql-test/t/mysqlbinlog.test index b76a5103120..ceba78bf762 100644 --- a/mysql-test/t/mysqlbinlog.test +++ b/mysql-test/t/mysqlbinlog.test @@ -125,21 +125,18 @@ select HEX(f) from t04; select HEX(f) from t4; # -# BUG#14157: utf8 encoding in binlog without set character_set_client -# -# BUG: -# This test only works on the MySQL-internal rpl machines. -# Needs to be fixed. Problem is that koi8r is not installed -# on many machines. +#BUG#14157: utf8 encoding in binlog without set character_set_client # flush logs; -# --exec $MYSQL --character-sets-dir=../sql/share/charsets/ --default-character-set=koi8r test -e 'create table if not exists t5 (a int); set names koi8r; create temporary table `ÑÝÉË` (a int); insert into `ÑÝÉË` values (1); insert into t5 select * from `ÑÝÉË`' +--exec $MYSQL test -e 'create table if not exists t5 (a int); set names latin1; create temporary table `äöüÄÖÜ` (a int); insert into `äöüÄÖÜ` values (1); insert into t5 select * from `äöüÄÖÜ`' -# resulted log is client charset insensitive (latin1 not koi8r) as it must be -# --exec $MYSQL_BINLOG --short-form $MYSQL_TEST_DIR/var/log/master-bin.000006 | $MYSQL --default-character-set=latin1 -#select * from t5 /* must be (1),(1) */; +# resulted binlog, parly consisting of multi-byte utf8 chars, +# must be digestable for both client and server. In 4.1 the client +# should use default-character-set same as the server. +--exec $MYSQL_BINLOG --short-form $MYSQLTEST_VARDIR/log/master-bin.000006 | $MYSQL +select * from t5 /* must be (1),(1) */; # clean up -drop table t1, t2, t03, t04, t3, t4; +drop table t1, t2, t03, t04, t3, t4, t5; # End of 5.0 tests diff --git a/mysql-test/t/mysqlcheck.test b/mysql-test/t/mysqlcheck.test index 61d64c733d7..f5eddff8816 100644 --- a/mysql-test/t/mysqlcheck.test +++ b/mysql-test/t/mysqlcheck.test @@ -5,6 +5,10 @@ # depends on the presence of the log tables (which are CSV-based). --source include/have_csv.inc +--disable_warnings +drop database if exists client_test_db; +--enable_warnings + DROP SCHEMA test; CREATE SCHEMA test; # diff --git a/mysql-test/t/mysqldump.test b/mysql-test/t/mysqldump.test index 732a3ce89da..98e881b67d5 100644 --- a/mysql-test/t/mysqldump.test +++ b/mysql-test/t/mysqldump.test @@ -2,7 +2,7 @@ --source include/not_embedded.inc --disable_warnings -DROP TABLE IF EXISTS t1, `"t"1`, t1aa, t2, t2aa; +DROP TABLE IF EXISTS t1, `"t"1`, t1aa, t2, t2aa, t3; drop database if exists mysqldump_test_db; drop database if exists db1; drop database if exists db2; @@ -607,6 +607,13 @@ drop table t1, t2; drop database db1; # +# BUG#15328 Segmentation fault occured if my.cnf is invalid for escape sequence +# + +--exec $MYSQL_MY_PRINT_DEFAULTS --defaults-file=$MYSQL_TEST_DIR/std_data/bug15328.cnf mysqldump + + +# # Bug #9558 mysqldump --no-data db t1 t2 format still dumps data # @@ -741,6 +748,17 @@ show create table `t1`; drop table `t1`; +# +# Bug #18536: wrong table order +# + +create table t1(a int); +create table t2(a int); +create table t3(a int); +--error 6 +--exec $MYSQL_DUMP --skip-comments --force --no-data test t3 t1 non_existing t2 +drop table t1, t2, t3; + --echo End of 4.1 tests # Bug #13318: Bad result with empty field and --hex-blob # diff --git a/mysql-test/t/ndb_basic.test b/mysql-test/t/ndb_basic.test index d11e30ae97d..c7fa5aeee1e 100644 --- a/mysql-test/t/ndb_basic.test +++ b/mysql-test/t/ndb_basic.test @@ -713,3 +713,17 @@ select * from t1 order by f1; select * from t1 order by f2; select * from t1 order by f3; drop table t1; + +# +# Bug #18483 Cannot create table with FK constraint +# ndb does not support foreign key constraint, it is silently ignored +# in line with other storage engines +# +CREATE TABLE t1 (a VARCHAR(255) NOT NULL, + CONSTRAINT pk_a PRIMARY KEY (a))engine=ndb; +CREATE TABLE t2(a VARCHAR(255) NOT NULL, + b VARCHAR(255) NOT NULL, + c VARCHAR(255) NOT NULL, + CONSTRAINT pk_b_c_id PRIMARY KEY (b,c), + CONSTRAINT fk_a FOREIGN KEY(a) REFERENCES t1(a))engine=ndb; +drop table t1, t2; diff --git a/mysql-test/t/ndb_dd_backuprestore.test b/mysql-test/t/ndb_dd_backuprestore.test index be6d73e27b4..1508cccb46d 100644 --- a/mysql-test/t/ndb_dd_backuprestore.test +++ b/mysql-test/t/ndb_dd_backuprestore.test @@ -159,185 +159,15 @@ DROP TABLE test.t1; DROP TABLE test.t2; DROP TABLE test.t3; DROP TABLE test.t4; -###################### Adding partition ################################# --- echo **** Test 3 Adding partition Test backup and restore **** - -CREATE TABLESPACE table_space2 -ADD DATAFILE './table_space2/datafile.dat' -USE LOGFILE GROUP log_group1 -INITIAL_SIZE 12M -ENGINE NDB; - -CREATE TABLE test.t1 (pk1 MEDIUMINT NOT NULL AUTO_INCREMENT, c2 VARCHAR(150) NOT NULL, c3 INT NOT NULL, c4 BIT NOT NULL, PRIMARY KEY(pk1,c3))TABLESPACE table_space1 STORAGE DISK ENGINE=NDB PARTITION BY HASH(c3) PARTITIONS 4; - -CREATE TABLE test.t4 (pk1 MEDIUMINT NOT NULL AUTO_INCREMENT, c2 VARCHAR(180) NOT NULL, c3 INT NOT NULL, c4 BIT NOT NULL, PRIMARY KEY(pk1,c3))ENGINE=NDB PARTITION BY HASH(c3) PARTITIONS 2; - -CREATE TABLE test.t2 (pk1 MEDIUMINT NOT NULL AUTO_INCREMENT, c2 TEXT NOT NULL, c3 INT NOT NULL, c4 BIT NOT NULL, PRIMARY KEY(pk1,c3))TABLESPACE table_space2 STORAGE DISK ENGINE=NDB PARTITION BY KEY(c3) (PARTITION p0 ENGINE = NDB, PARTITION p1 ENGINE = NDB); - -CREATE TABLE test.t5 (pk1 MEDIUMINT NOT NULL AUTO_INCREMENT, c2 TEXT NOT NULL, c3 INT NOT NULL, c4 BIT NOT NULL, PRIMARY KEY(pk1,c3))ENGINE=NDB PARTITION BY KEY(pk1) (PARTITION p0 ENGINE = NDB, PARTITION p1 ENGINE = NDB); - -CREATE TABLE test.t3 (pk1 MEDIUMINT NOT NULL AUTO_INCREMENT, c2 VARCHAR(202) NOT NULL, c3 INT NOT NULL, c4 BIT NOT NULL, PRIMARY KEY(pk1,c3))TABLESPACE table_space2 STORAGE DISK ENGINE=NDB PARTITION BY RANGE (c3) PARTITIONS 3 (PARTITION x1 VALUES LESS THAN (105), PARTITION x2 VALUES LESS THAN (333), PARTITION x3 VALUES LESS THAN (720)); - -CREATE TABLE test.t6 (pk1 MEDIUMINT NOT NULL AUTO_INCREMENT, c2 VARCHAR(220) NOT NULL, c3 INT NOT NULL, c4 BIT NOT NULL, PRIMARY KEY(pk1,c3))ENGINE=NDB PARTITION BY RANGE (pk1) PARTITIONS 2 (PARTITION x1 VALUES LESS THAN (333), PARTITION x2 VALUES LESS THAN (720)); - -SHOW CREATE TABLE test.t1; - -SHOW CREATE TABLE test.t2; - -SHOW CREATE TABLE test.t3; - -SHOW CREATE TABLE test.t4; - -SHOW CREATE TABLE test.t5; - -SHOW CREATE TABLE test.t6; - -SELECT * FROM information_schema.partitions WHERE table_name= 't1'; - -SELECT * FROM information_schema.partitions WHERE table_name= 't2'; - -SELECT * FROM information_schema.partitions WHERE table_name= 't3'; - -SELECT * FROM information_schema.partitions WHERE table_name= 't4'; - -SELECT * FROM information_schema.partitions WHERE table_name= 't5'; - -SELECT * FROM information_schema.partitions WHERE table_name= 't6'; - - -let $j= 500; ---disable_query_log -while ($j) -{ - eval INSERT INTO test.t1 VALUES (NULL, "Sweden, Texas", $j, b'0'); - eval INSERT INTO test.t4 VALUES (NULL, "Sweden, Texas", $j, b'0'); - dec $j; - eval INSERT INTO test.t2 VALUES (NULL, "Sweden, Texas, ITALY, Kyle, JO, JBM,TU", $j, b'1'); - eval INSERT INTO test.t5 VALUES (NULL, "Sweden, Texas, ITALY, Kyle, JO, JBM,TU", $j, b'1'); - dec $j; - eval INSERT INTO test.t3 VALUES (NULL, "TEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXAS, ITALY, Kyle, JO, JBM,TU", $j, b'1'); - eval INSERT INTO test.t6 VALUES (NULL, "TEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXASTEXAS, ITALY, Kyle, JO, JBM,TU", $j, b'1'); } --enable_query_log - -SELECT COUNT(*) FROM test.t1; - -SELECT pk1, c2, c3, hex(c4) FROM test.t1 ORDER BY c3 LIMIT 5; - -SELECT COUNT(*) FROM test.t2; - -SELECT pk1, c2, c3, hex(c4) FROM test.t2 ORDER BY c3 LIMIT 5; - -SELECT COUNT(*) FROM test.t3; - -SELECT pk1, c2, c3, hex(c4) FROM test.t3 ORDER BY c3 LIMIT 5; - -SELECT COUNT(*) FROM test.t4; - -SELECT pk1, c2, c3, hex(c4) FROM test.t4 ORDER BY c3 LIMIT 5; - -SELECT COUNT(*) FROM test.t5; - -SELECT pk1, c2, c3, hex(c4) FROM test.t5 ORDER BY c3 LIMIT 5; - -SELECT COUNT(*) FROM test.t6; - -SELECT pk1, c2, c3, hex(c4) FROM test.t6 ORDER BY c3 LIMIT 5; - --- source include/ndb_backup.inc - -DROP TABLE test.t1; -DROP TABLE test.t2; -DROP TABLE test.t3; -DROP TABLE test.t4; -DROP TABLE test.t5; -DROP TABLE test.t6; ALTER TABLESPACE table_space1 DROP DATAFILE './table_space1/datafile.dat' ENGINE = NDB; -ALTER TABLESPACE table_space2 -DROP DATAFILE './table_space2/datafile.dat' -ENGINE = NDB; - DROP TABLESPACE table_space1 ENGINE = NDB; -DROP TABLESPACE table_space2 -ENGINE = NDB; - DROP LOGFILE GROUP log_group1 ENGINE =NDB; --- source include/ndb_restore_master.inc - - -SHOW CREATE TABLE test.t1; - -SHOW CREATE TABLE test.t2; - -SHOW CREATE TABLE test.t3; - -SHOW CREATE TABLE test.t4; - -SHOW CREATE TABLE test.t5; - -SHOW CREATE TABLE test.t6; - -SELECT * FROM information_schema.partitions WHERE table_name= 't1'; - -SELECT * FROM information_schema.partitions WHERE table_name= 't2'; - -SELECT * FROM information_schema.partitions WHERE table_name= 't3'; - -SELECT * FROM information_schema.partitions WHERE table_name= 't4'; - -SELECT * FROM information_schema.partitions WHERE table_name= 't5'; - -SELECT * FROM information_schema.partitions WHERE table_name= 't6'; - -SELECT COUNT(*) FROM test.t1; - -SELECT pk1, c2, c3, hex(c4) FROM test.t1 ORDER BY c3 LIMIT 5; - -SELECT COUNT(*) FROM test.t2; - -SELECT pk1, c2, c3, hex(c4) FROM test.t2 ORDER BY c3 LIMIT 5; - -SELECT COUNT(*) FROM test.t3; - -SELECT pk1, c2, c3, hex(c4) FROM test.t3 ORDER BY c3 LIMIT 5; - -SELECT COUNT(*) FROM test.t4; - -SELECT pk1, c2, c3, hex(c4) FROM test.t4 ORDER BY c3 LIMIT 5; - -SELECT COUNT(*) FROM test.t5; - -SELECT pk1, c2, c3, hex(c4) FROM test.t5 ORDER BY c3 LIMIT 5; - -SELECT COUNT(*) FROM test.t6; - -SELECT pk1, c2, c3, hex(c4) FROM test.t6 ORDER BY c3 LIMIT 5; - -# Cleanup - -DROP TABLE test.t1; -DROP TABLE test.t2; -DROP TABLE test.t3; -DROP TABLE test.t4; -DROP TABLE test.t5; -DROP TABLE test.t6; - -ALTER TABLESPACE table_space1 DROP DATAFILE './table_space1/datafile.dat' ENGINE=NDB; - -ALTER TABLESPACE table_space2 DROP DATAFILE './table_space2/datafile.dat' ENGINE=NDB; - -DROP TABLESPACE table_space1 ENGINE = NDB; - -DROP TABLESPACE table_space2 ENGINE = NDB; - -DROP LOGFILE GROUP log_group1 ENGINE = NDB; - #End 5.1 test case - - diff --git a/mysql-test/t/ndb_lock.test b/mysql-test/t/ndb_lock.test index 3d8597dcc45..b022ea550cc 100644 --- a/mysql-test/t/ndb_lock.test +++ b/mysql-test/t/ndb_lock.test @@ -69,6 +69,119 @@ insert into t1 values (1,1,1); drop table t1; +# Lock for update + +create table t1 (x integer not null primary key, y varchar(32), z integer, key(z)) engine = ndb; + +insert into t1 values (1,'one',1), (2,'two',2),(3,"three",3); + +# PK access +connection con1; +begin; +select * from t1 where x = 1 for update; + +connection con2; +begin; +select * from t1 where x = 2 for update; +--error 1205 +select * from t1 where x = 1 for update; +rollback; + +connection con1; +commit; + +# table scan +connection con1; +begin; +select * from t1 where y = 'one' or y = 'three' order by x for update; + +connection con2; +begin; +# Have to check with pk access here since scans take locks on +# all rows and then release them in chunks +# Bug #20390 SELECT FOR UPDATE does not release locks of untouched rows in full table scans +#select * from t1 where x = 2 for update; +--error 1205 +select * from t1 where x = 1 for update; +rollback; + +connection con1; +commit; + +# index scan +connection con1; +begin; +select * from t1 where z > 1 and z < 3 for update; + +connection con2; +begin; +# Have to check with pk access here since scans take locks on +# all rows and then release them in chunks +select * from t1 where x = 1 for update; +--error 1205 +select * from t1 where x = 2 for update; +rollback; + +connection con1; +commit; + +# share locking + +# PK access +connection con1; +begin; +select * from t1 where x = 1 lock in share mode; + +connection con2; +begin; +select * from t1 where x = 1 lock in share mode; +select * from t1 where x = 2 for update; +--error 1205 +select * from t1 where x = 1 for update; +rollback; + +connection con1; +commit; + +# table scan +connection con1; +begin; +select * from t1 where y = 'one' or y = 'three' order by x lock in share mode; + +connection con2; +begin; +select * from t1 where y = 'one' lock in share mode; +# Have to check with pk access here since scans take locks on +# all rows and then release them in chunks +# Bug #20390 SELECT FOR UPDATE does not release locks of untouched rows in full table scans +#select * from t1 where x = 2 for update; +--error 1205 +select * from t1 where x = 1 for update; +rollback; + +connection con1; +commit; + +# index scan +connection con1; +begin; +select * from t1 where z > 1 and z < 3 lock in share mode; + +connection con2; +begin; +select * from t1 where z = 1 lock in share mode; +# Have to check with pk access here since scans take locks on +# all rows and then release them in chunks +select * from t1 where x = 1 for update; +--error 1205 +select * from t1 where x = 2 for update; +rollback; + +connection con1; +commit; + +drop table t1; + # End of 4.1 tests # diff --git a/mysql-test/t/ndb_rename.test b/mysql-test/t/ndb_rename.test new file mode 100644 index 00000000000..7f9fd0e6984 --- /dev/null +++ b/mysql-test/t/ndb_rename.test @@ -0,0 +1,36 @@ +-- source include/have_ndb.inc +-- source include/not_embedded.inc + +--disable_warnings +DROP TABLE IF EXISTS t1,t2; +drop database if exists mysqltest; +--enable_warnings + +# +# Table rename tests +# + +# +# Create a normal table with primary key +# +CREATE TABLE t1 ( + pk1 INT NOT NULL PRIMARY KEY, + attr1 INT NOT NULL, + attr2 INT, + attr3 VARCHAR(10), + INDEX i1(attr1) +) ENGINE=ndbcluster; + +INSERT INTO t1 VALUES (0,0,0,"zero"),(1,1,1,"one"),(2,2,2,"two"); +SELECT * FROM t1 WHERE attr1 = 1; +alter table t1 rename t2; +SELECT * FROM t2 WHERE attr1 = 1; + +create database ndbtest; +alter table t2 rename ndbtest.t2; +SELECT * FROM ndbtest.t2 WHERE attr1 = 1; + +drop table ndbtest.t2; +drop database ndbtest; + +# End of 4.1 tests diff --git a/mysql-test/t/ndb_truncate.test b/mysql-test/t/ndb_truncate.test index 73af70d0d0f..a1ef4be0d48 100644 --- a/mysql-test/t/ndb_truncate.test +++ b/mysql-test/t/ndb_truncate.test @@ -2,12 +2,11 @@ -- source include/not_embedded.inc --disable_warnings -DROP TABLE IF EXISTS t2; +DROP TABLE IF EXISTS t1, t2; --enable_warnings - -CREATE TABLE t2 ( - a bigint unsigned NOT NULL PRIMARY KEY, +CREATE TABLE t1 ( + a bigint unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY, b int unsigned not null, c int unsigned ) engine=ndbcluster; @@ -20,17 +19,23 @@ let $1=500; disable_query_log; while ($1) { - eval insert into t2 values($1*10, $1+9, 5*$1), ($1*10+1, $1+10, 7),($1*10+2, $1+10, 7*$1), ($1*10+3, $1+10, 10+$1), ($1*10+4, $1+10, 70*$1), ($1*10+5, $1+10, 7), ($1*10+6, $1+10, 9), ($1*10+7, $1+299, 899), ($1*10+8, $1+10, 12), ($1*10+9, $1+10, 14*$1); + eval insert into t1 values(NULL, $1+9, 5*$1), (NULL, $1+10, 7),(NULL, $1+10, 7*$1), (NULL, $1+10, 10+$1), (NULL, $1+10, 70*$1), (NULL, $1+10, 7), (NULL, $1+10, 9), (NULL, $1+299, 899), (NULL, $1+10, 12), (NULL, $1+10, 14*$1); dec $1; } enable_query_log; -select count(*) from t2; +select count(*) from t1; + +select * from t1 order by a limit 2; + +truncate table t1; + +select count(*) from t1; -truncate table t2; +insert into t1 values(NULL,1,1),(NULL,2,2); -select count(*) from t2; +select * from t1 order by a; -drop table t2; +drop table t1; # End of 4.1 tests diff --git a/mysql-test/t/partition.test b/mysql-test/t/partition.test index 8773eb2f2e3..a7f2e1c0b3e 100644 --- a/mysql-test/t/partition.test +++ b/mysql-test/t/partition.test @@ -26,6 +26,19 @@ partition by key(a) drop table t1; # +# BUG 19067 ALTER TABLE .. ADD PARTITION for subpartitioned table crashes +# +create table t1 (a int) +partition by range (a) +subpartition by key (a) +(partition p0 values less than (1)); +alter table t1 add partition (partition p1 values less than (2)); +show create table t1; +alter table t1 reorganize partition p1 into (partition p1 values less than (3)); +show create table t1; +drop table t1; + +# # Partition by key no partition defined => OK # CREATE TABLE t1 ( @@ -1035,6 +1048,34 @@ create index inx1 on t1(a); drop table t1; # +# Bug 19695 Partitions: SHOW CREATE TABLE shows table options even when it +# shouldn't +# +create table t1 (a int) +PARTITION BY KEY (a) +(PARTITION p0); +set session sql_mode='no_table_options'; +show create table t1; +set session sql_mode=''; +drop table t1; + +# +# BUG 19122 Crash after ALTER TABLE t1 REBUILD PARTITION p1 +# +create table t1 (a int) +partition by key (a) +(partition p1 engine = innodb); + +alter table t1 rebuild partition p1; +alter table t1 rebuild partition p1; +alter table t1 rebuild partition p1; +alter table t1 rebuild partition p1; +alter table t1 rebuild partition p1; +alter table t1 rebuild partition p1; +alter table t1 rebuild partition p1; +drop table t1; + +# # BUG 19304 Partitions: MERGE handler not allowed in partitioned tables # --error ER_PARTITION_MERGE_ERROR @@ -1088,5 +1129,21 @@ OPTIMIZE TABLE t1; drop table t1; +# +# Bug 17310 Partitions: Bugs with archived partitioned tables +# +create database db99; +use db99; +create table t1 (a int not null) +engine=archive +partition by list (a) +(partition p0 values in (1), partition p1 values in (2)); +insert into t1 values (1), (2); +--error 0, 1005 +create index inx on t1 (a); +alter table t1 add partition (partition p2 values in (3)); +alter table t1 drop partition p2; +use test; +drop database db99; --echo End of 5.1 tests diff --git a/mysql-test/t/partition_error.test b/mysql-test/t/partition_error.test index 03a2ab41807..659f0b8cef4 100644 --- a/mysql-test/t/partition_error.test +++ b/mysql-test/t/partition_error.test @@ -747,3 +747,31 @@ CREATE TABLE t1(a int) --error ER_NO_PARTITION_FOR_GIVEN_VALUE insert into t1 values (10); drop table t1; + +# +# Bug 18198 Partitions: Verify that erroneus partition functions doesn't work +# +create table t1 (v varchar(12)) +partition by range (ascii(v)) +(partition p0 values less than (10)); +drop table t1; + +-- error 1064 +create table t1 (a int) +partition by hash (rand(a)); +-- error 1064 +create table t1 (a int) +partition by hash(CURTIME() + a); +-- error 1064 +create table t1 (a int) +partition by hash (NOW()+a); +-- error ER_PARTITION_FUNCTION_IS_NOT_ALLOWED +create table t1 (a int) +partition by hash (extract(hour from convert_tz(a, '+00:00', '+00:00'))); +-- error ER_PARTITION_FUNCTION_IS_NOT_ALLOWED +create table t1 (a int) +partition by range (a + (select count(*) from t1)) +(partition p1 values less than (1)); +-- error ER_PARTITION_FUNC_NOT_ALLOWED_ERROR +create table t1 (a char(10)) +partition by hash (extractvalue(a,'a')); diff --git a/mysql-test/t/partition_innodb.test b/mysql-test/t/partition_innodb.test new file mode 100644 index 00000000000..6a95dd7c8b0 --- /dev/null +++ b/mysql-test/t/partition_innodb.test @@ -0,0 +1,68 @@ +-- source include/have_innodb.inc + +SET @max_row = 20; +let $engine= 'InnoDB'; +let $MAX_VALUE= (2147483646); + +let $max_row= `SELECT @max_row`; + +# Column list with definition for all tables to be checked +let $column_list= f_int1 INTEGER, +f_int2 INTEGER, +f_char1 CHAR(20), +f_char2 CHAR(20), +f_charbig VARCHAR(1000); + +let $sub_part_no= 3; +--disable_warnings +DROP TABLE IF EXISTS t0_template; +--enable_warnings +eval CREATE TABLE t0_template ( +$column_list , +PRIMARY KEY(f_int1)) +ENGINE = MEMORY; +let $num= `SELECT @max_row`; +while ($num) +{ + eval INSERT INTO t0_template +SET f_int1 = $num, f_int2 = $num, f_char1 = '$num', f_char2 = '$num', +f_charbig = '===$num==='; + dec $num; +} +# 1. Create the table +--disable_warnings +DROP TABLE IF EXISTS t1; +--enable_warnings +eval CREATE TABLE t1 (f_date DATE, f_varchar VARCHAR(30)) engine=$engine; +# 2. Fill the table t1 with records +INSERT INTO t1 (f_date, f_varchar) +SELECT CONCAT(CAST((f_int1 + 999) AS CHAR),'-02-10'), CAST(f_char1 AS CHAR) +FROM t0_template +WHERE f_int1 + 999 BETWEEN 1000 AND 9999; +# 3. Calculate the number of inserted records. +SELECT IF(9999 - 1000 + 1 > @max_row, @max_row , 9999 - 1000 + 1) + INTO @exp_row_count; +# DEBUG SELECT @exp_row_count; +# 4. Print the layout, check Readability +ALTER TABLE t1 PARTITION BY HASH(CAST(YEAR(f_date) AS SIGNED INTEGER)); +--echo # 1.1.5 Add two named partitions + test +ALTER TABLE t1 ADD PARTITION (PARTITION part1, PARTITION part7); +drop table t1; + +CREATE TABLE t1 (f_date DATE, f_varchar VARCHAR(30)) +ENGINE=InnoDB +PARTITION BY HASH(CAST(YEAR(f_date) AS SIGNED INTEGER)); + +--echo # This statement crashes the server. +--echo # CREATE partitioned table with three partitions in one step +--echo # would be harmless. +ALTER TABLE t1 ADD PARTITION PARTITIONS 1; + +--disable_warnings +DROP VIEW IF EXISTS v1; +DROP TABLE IF EXISTS t1; +DROP TABLE IF EXISTS t0_aux; +DROP TABLE IF EXISTS t0_definition; +DROP TABLE IF EXISTS t0_template; +--enable_warnings + diff --git a/mysql-test/t/partition_mgm.test b/mysql-test/t/partition_mgm.test new file mode 100644 index 00000000000..aa9a6459a1a --- /dev/null +++ b/mysql-test/t/partition_mgm.test @@ -0,0 +1,15 @@ +--disable_warnings +DROP TABLE IF EXISTS t1; +--enable_warnings +CREATE TABLE t1 (f_date DATE, f_varchar VARCHAR(30)) +PARTITION BY HASH(CAST(YEAR(f_date) AS SIGNED INTEGER)) PARTITIONS 2; +SHOW CREATE TABLE t1; + +--exec ls $MYSQLTEST_VARDIR/master-data/test/t1* +ALTER TABLE t1 COALESCE PARTITION 1; +SHOW CREATE TABLE t1; +--exec ls $MYSQLTEST_VARDIR/master-data/test/t1* + + + + diff --git a/mysql-test/t/partition_mgm_err2.test b/mysql-test/t/partition_mgm_err2.test index dd96731ccdd..7d15bd7b19f 100644 --- a/mysql-test/t/partition_mgm_err2.test +++ b/mysql-test/t/partition_mgm_err2.test @@ -15,7 +15,7 @@ enable_query_log; --system mkdir $MYSQLTEST_VARDIR/tmp/bug14354 disable_query_log; eval CREATE TABLE t1 (id int) PARTITION BY RANGE(id) ( -PARTITION p1 VALUES LESS THAN (20) ENGINE=myiasm +PARTITION p1 VALUES LESS THAN (20) ENGINE=myisam DATA DIRECTORY="$MYSQLTEST_VARDIR/tmp/bug14354" INDEX DIRECTORY="$MYSQLTEST_VARDIR/tmp/bug14354"); enable_query_log; diff --git a/mysql-test/t/partition_pruning.test b/mysql-test/t/partition_pruning.test index 0c922392d32..976466e1578 100644 --- a/mysql-test/t/partition_pruning.test +++ b/mysql-test/t/partition_pruning.test @@ -556,7 +556,7 @@ drop table t1; # being fixed. # -#BUG 17946 Like searches fail with partitioning +# BUG#17946 Like searches fail with partitioning # create table t1 (a char(32) primary key) partition by key() @@ -566,3 +566,27 @@ select * from t1; select * from t1 where a like 'n%'; drop table t1; + +# BUG#19055 Crashes for varchar_col=NUMBER or varchar_col IS NULL +create table t1 (s1 varchar(15)) partition by key (s1); +select * from t1 where s1 = 0 or s1 is null; +insert into t1 values ('aa'),('bb'),('0'); +explain partitions select * from t1 where s1 = 0 or s1 is null; +drop table t1; + +# +# BUG#19684: EXPLAIN PARTITIONS produces garbage in 'partitions' column when +# the length of string to be displayed exceeds some limit. +create table t2 (a int, b int) + partition by LIST(a) + subpartition by HASH(b) subpartitions 40 +( partition p_0_long_partition_name values in(1), + partition p_1_long_partition_name values in(2)); + +insert into t2 values (1,1),(2,2); + +--vertical_results +explain partitions select * from t2; +--horizontal_results +drop table t2; + diff --git a/mysql-test/t/partition_range.test b/mysql-test/t/partition_range.test index ef539e2001f..42ce4e0d879 100644 --- a/mysql-test/t/partition_range.test +++ b/mysql-test/t/partition_range.test @@ -416,3 +416,117 @@ ALTER TABLE t1 DROP PARTITION p0; ALTER TABLE t1 DROP PARTITION p1; ALTER TABLE t1 DROP PARTITION p2; drop table t1; + +# +# Bug 19830: ALTER TABLE t1 REORGANIZE PARTITION crashes +# +create table t1 (a int DEFAULT NULL, + b varchar(30) DEFAULT NULL, + c date DEFAULT NULL) +ENGINE=MYISAM DEFAULT CHARSET=latin1; + +insert into t1 values (1, 'abc', '1995-01-01'); +insert into t1 values (1, 'abc', '1995-01-02'); +insert into t1 values (1, 'abc', '1995-01-03'); +insert into t1 values (1, 'abc', '1995-01-04'); +insert into t1 values (1, 'abc', '1995-01-05'); +insert into t1 values (1, 'abc', '1995-01-06'); +insert into t1 values (1, 'abc', '1995-01-07'); +insert into t1 values (1, 'abc', '1995-01-08'); +insert into t1 values (1, 'abc', '1995-01-09'); +insert into t1 values (1, 'abc', '1995-01-10'); +insert into t1 values (1, 'abc', '1995-01-11'); +insert into t1 values (1, 'abc', '1995-01-12'); +insert into t1 values (1, 'abc', '1995-01-13'); +insert into t1 values (1, 'abc', '1995-01-14'); +insert into t1 values (1, 'abc', '1995-01-15'); +insert into t1 values (1, 'abc', '1997-01-01'); +insert into t1 values (1, 'abc', '1997-01-02'); +insert into t1 values (1, 'abc', '1997-01-03'); +insert into t1 values (1, 'abc', '1997-01-04'); +insert into t1 values (1, 'abc', '1997-01-05'); +insert into t1 values (1, 'abc', '1997-01-06'); +insert into t1 values (1, 'abc', '1997-01-07'); +insert into t1 values (1, 'abc', '1997-01-08'); +insert into t1 values (1, 'abc', '1997-01-09'); +insert into t1 values (1, 'abc', '1997-01-10'); +insert into t1 values (1, 'abc', '1997-01-11'); +insert into t1 values (1, 'abc', '1997-01-12'); +insert into t1 values (1, 'abc', '1997-01-13'); +insert into t1 values (1, 'abc', '1997-01-14'); +insert into t1 values (1, 'abc', '1997-01-15'); +insert into t1 values (1, 'abc', '1998-01-01'); +insert into t1 values (1, 'abc', '1998-01-02'); +insert into t1 values (1, 'abc', '1998-01-03'); +insert into t1 values (1, 'abc', '1998-01-04'); +insert into t1 values (1, 'abc', '1998-01-05'); +insert into t1 values (1, 'abc', '1998-01-06'); +insert into t1 values (1, 'abc', '1998-01-07'); +insert into t1 values (1, 'abc', '1998-01-08'); +insert into t1 values (1, 'abc', '1998-01-09'); +insert into t1 values (1, 'abc', '1998-01-10'); +insert into t1 values (1, 'abc', '1998-01-11'); +insert into t1 values (1, 'abc', '1998-01-12'); +insert into t1 values (1, 'abc', '1998-01-13'); +insert into t1 values (1, 'abc', '1998-01-14'); +insert into t1 values (1, 'abc', '1998-01-15'); +insert into t1 values (1, 'abc', '1999-01-01'); +insert into t1 values (1, 'abc', '1999-01-02'); +insert into t1 values (1, 'abc', '1999-01-03'); +insert into t1 values (1, 'abc', '1999-01-04'); +insert into t1 values (1, 'abc', '1999-01-05'); +insert into t1 values (1, 'abc', '1999-01-06'); +insert into t1 values (1, 'abc', '1999-01-07'); +insert into t1 values (1, 'abc', '1999-01-08'); +insert into t1 values (1, 'abc', '1999-01-09'); +insert into t1 values (1, 'abc', '1999-01-10'); +insert into t1 values (1, 'abc', '1999-01-11'); +insert into t1 values (1, 'abc', '1999-01-12'); +insert into t1 values (1, 'abc', '1999-01-13'); +insert into t1 values (1, 'abc', '1999-01-14'); +insert into t1 values (1, 'abc', '1999-01-15'); +insert into t1 values (1, 'abc', '2000-01-01'); +insert into t1 values (1, 'abc', '2000-01-02'); +insert into t1 values (1, 'abc', '2000-01-03'); +insert into t1 values (1, 'abc', '2000-01-04'); +insert into t1 values (1, 'abc', '2000-01-05'); +insert into t1 values (1, 'abc', '2000-01-06'); +insert into t1 values (1, 'abc', '2000-01-07'); +insert into t1 values (1, 'abc', '2000-01-08'); +insert into t1 values (1, 'abc', '2000-01-09'); +insert into t1 values (1, 'abc', '2000-01-15'); +insert into t1 values (1, 'abc', '2000-01-11'); +insert into t1 values (1, 'abc', '2000-01-12'); +insert into t1 values (1, 'abc', '2000-01-13'); +insert into t1 values (1, 'abc', '2000-01-14'); +insert into t1 values (1, 'abc', '2000-01-15'); +insert into t1 values (1, 'abc', '2001-01-01'); +insert into t1 values (1, 'abc', '2001-01-02'); +insert into t1 values (1, 'abc', '2001-01-03'); +insert into t1 values (1, 'abc', '2001-01-04'); +insert into t1 values (1, 'abc', '2001-01-05'); +insert into t1 values (1, 'abc', '2001-01-06'); +insert into t1 values (1, 'abc', '2001-01-07'); +insert into t1 values (1, 'abc', '2001-01-08'); +insert into t1 values (1, 'abc', '2001-01-09'); +insert into t1 values (1, 'abc', '2001-01-15'); +insert into t1 values (1, 'abc', '2001-01-11'); +insert into t1 values (1, 'abc', '2001-01-12'); +insert into t1 values (1, 'abc', '2001-01-13'); +insert into t1 values (1, 'abc', '2001-01-14'); +insert into t1 values (1, 'abc', '2001-01-15'); + +alter table t1 +partition by range (year(c)) +(partition p5 values less than (2000), partition p10 values less than (2010)); + +alter table t1 +reorganize partition p5 into +(partition p1 values less than (1996), + partition p2 values less than (1997), + partition p3 values less than (1998), + partition p4 values less than (1999), + partition p5 values less than (2000)); + +drop table t1; + diff --git a/mysql-test/t/ps.test b/mysql-test/t/ps.test index 639a576fb35..4a336962293 100644 --- a/mysql-test/t/ps.test +++ b/mysql-test/t/ps.test @@ -1145,5 +1145,5 @@ prepare stmt from "analyze table t4, t1"; execute stmt; execute stmt; deallocate prepare stmt; - +drop table t1, t2, t3; # End of 5.0 tests diff --git a/mysql-test/t/ps_1general.test b/mysql-test/t/ps_1general.test index f61a7820afe..0b9a46ad4d3 100644 --- a/mysql-test/t/ps_1general.test +++ b/mysql-test/t/ps_1general.test @@ -321,13 +321,15 @@ execute stmt4; prepare stmt4 from ' show variables like ''sql_mode'' '; execute stmt4; prepare stmt4 from ' show engine bdb logs '; -# The output depends on the history (actions of the bdb engine). +# The output depends on the bdb being enabled and on the history +# history (actions of the bdb engine). # That is the reason why, we switch the output here off. # (The real output will be tested in ps_6bdb.test) -# --replace_result $MYSQL_TEST_DIR TEST_DIR +--disable_warnings --disable_result_log execute stmt4; --enable_result_log +--enable_warnings prepare stmt4 from ' show grants for user '; --error 1295 prepare stmt4 from ' show create table t2 '; diff --git a/mysql-test/t/rpl_ndb_2innodb-master.opt b/mysql-test/t/rpl_ndb_2innodb-master.opt index f31d53c3cb1..701dddb075b 100644 --- a/mysql-test/t/rpl_ndb_2innodb-master.opt +++ b/mysql-test/t/rpl_ndb_2innodb-master.opt @@ -1 +1 @@ ---default-storage-engine=ndb --binlog-format=row +--default-storage-engine=ndbcluster diff --git a/mysql-test/t/rpl_ndb_2innodb-slave.opt b/mysql-test/t/rpl_ndb_2innodb-slave.opt index ee81186c513..d8857e54be2 100644 --- a/mysql-test/t/rpl_ndb_2innodb-slave.opt +++ b/mysql-test/t/rpl_ndb_2innodb-slave.opt @@ -1 +1 @@ ---innodb --default-storage-engine=innodb --binlog-format=row +--innodb --default-storage-engine=innodb diff --git a/mysql-test/t/rpl_ndb_2myisam-master.opt b/mysql-test/t/rpl_ndb_2myisam-master.opt index f31d53c3cb1..701dddb075b 100644 --- a/mysql-test/t/rpl_ndb_2myisam-master.opt +++ b/mysql-test/t/rpl_ndb_2myisam-master.opt @@ -1 +1 @@ ---default-storage-engine=ndb --binlog-format=row +--default-storage-engine=ndbcluster diff --git a/mysql-test/t/rpl_ndb_2myisam-slave.opt b/mysql-test/t/rpl_ndb_2myisam-slave.opt index 59de63c0da2..6035ce27c46 100644 --- a/mysql-test/t/rpl_ndb_2myisam-slave.opt +++ b/mysql-test/t/rpl_ndb_2myisam-slave.opt @@ -1 +1 @@ ---default-storage-engine=myisam --binlog-format=row +--default-storage-engine=myisam diff --git a/mysql-test/t/rpl_ndb_basic.test b/mysql-test/t/rpl_ndb_basic.test index c702908ed68..5290dc377c2 100644 --- a/mysql-test/t/rpl_ndb_basic.test +++ b/mysql-test/t/rpl_ndb_basic.test @@ -172,7 +172,14 @@ connection slave; # here we would get error 1412 prior to bug SELECT * FROM t1 ORDER BY c1 LIMIT 5; - +--connection master +TRUNCATE t1; +SELECT count(*) FROM t1; +INSERT INTO t1 VALUES (101,NULL),(102,NULL),(103,NULL),(104,NULL),(105,NULL),(106,NULL),(107,NULL),(108,NULL),(109,NULL),(1010,NULL); +--sync_slave_with_master +connection slave; +SELECT count(*) FROM t1; +SELECT c1 FROM t1 ORDER BY c1 LIMIT 5; # cleanup --connection master diff --git a/mysql-test/t/rpl_ndb_innodb2ndb-master.opt b/mysql-test/t/rpl_ndb_innodb2ndb-master.opt index 248f9e60566..627becdbfb5 100644 --- a/mysql-test/t/rpl_ndb_innodb2ndb-master.opt +++ b/mysql-test/t/rpl_ndb_innodb2ndb-master.opt @@ -1 +1 @@ ---innodb --binlog-format=row +--innodb diff --git a/mysql-test/t/rpl_ndb_innodb2ndb-slave.opt b/mysql-test/t/rpl_ndb_innodb2ndb-slave.opt index d6f11dcd7bc..7f9eb96dff1 100644 --- a/mysql-test/t/rpl_ndb_innodb2ndb-slave.opt +++ b/mysql-test/t/rpl_ndb_innodb2ndb-slave.opt @@ -1 +1 @@ ---binlog-format=row --default-storage-engine=ndbcluster +--default-storage-engine=ndbcluster diff --git a/mysql-test/t/rpl_ndb_myisam2ndb-slave.opt b/mysql-test/t/rpl_ndb_myisam2ndb-slave.opt index b7990823676..701dddb075b 100644 --- a/mysql-test/t/rpl_ndb_myisam2ndb-slave.opt +++ b/mysql-test/t/rpl_ndb_myisam2ndb-slave.opt @@ -1 +1 @@ ---default-storage-engine=ndbcluster --binlog-format=row +--default-storage-engine=ndbcluster diff --git a/mysql-test/t/select.test b/mysql-test/t/select.test index 4b6ae921b9b..8cd15463c62 100644 --- a/mysql-test/t/select.test +++ b/mysql-test/t/select.test @@ -1296,9 +1296,9 @@ explain select fld3 from t2 use index (fld1,fld3) where fld3 = 'honeysuckle'; # The next should give an error # --- error 1072 +-- error 1176 explain select fld3 from t2 ignore index (fld3,not_used); --- error 1072 +-- error 1176 explain select fld3 from t2 use index (not_used); # @@ -2264,6 +2264,21 @@ insert into t2 values(1,1); select * from t1 where f1 in (select f3 from t2 where (f3,f4)= (select f3,f4 from t2)); drop table t1,t2; +# +# Bug #17873: confusing error message when IGNORE INDEX refers a column name +# + +CREATE TABLE t1 (a int, INDEX idx(a)); +INSERT INTO t1 VALUES (2), (3), (1); + +EXPLAIN SELECT * FROM t1 IGNORE INDEX (idx); +--error 1176 +EXPLAIN SELECT * FROM t1 IGNORE INDEX (a); +--error 1176 +EXPLAIN SELECT * FROM t1 FORCE INDEX (a); + +DROP TABLE t1; + # End of 4.1 tests # diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test index 90d43bf7cc0..8f5fcdc9d52 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -5850,6 +5850,52 @@ DROP PROCEDURE bug18589_p2| # +# BUG#18037: Server crash when returning system variable in stored procedures +# BUG#19633: Stack corruption in fix_fields()/THD::rollback_item_tree_changes() +# + +# Prepare. + +--disable_warnings +DROP FUNCTION IF EXISTS bug18037_f1| +DROP PROCEDURE IF EXISTS bug18037_p1| +DROP PROCEDURE IF EXISTS bug18037_p2| +--enable_warnings + +# Test case. + +CREATE FUNCTION bug18037_f1() RETURNS INT +BEGIN + RETURN @@server_id; +END| + +CREATE PROCEDURE bug18037_p1() +BEGIN + DECLARE v INT DEFAULT @@server_id; +END| + +CREATE PROCEDURE bug18037_p2() +BEGIN + CASE @@server_id + WHEN -1 THEN + SELECT 0; + ELSE + SELECT 1; + END CASE; +END| + +SELECT bug18037_f1()| +CALL bug18037_p1()| +CALL bug18037_p2()| + +# Cleanup. + +DROP FUNCTION bug18037_f1| +DROP PROCEDURE bug18037_p1| +DROP PROCEDURE bug18037_p2| + + +# # BUG#NNNN: New bug synopsis # #--disable_warnings diff --git a/mysql-test/t/subselect.test b/mysql-test/t/subselect.test index 3f48b648f40..8916a5cec6d 100644 --- a/mysql-test/t/subselect.test +++ b/mysql-test/t/subselect.test @@ -2109,3 +2109,25 @@ insert into t1 values (1, 2), (2, 2), (3, 2); select * from (select min(i) from t1 where j=(select * from (select min(j) from t1) t2)) t3; drop table t1; +# +# Bug#19700: subselect returning BIGINT always returned it as SIGNED +# +CREATE TABLE t1 (i BIGINT UNSIGNED); +INSERT INTO t1 VALUES (10000000000000000000); -- > MAX SIGNED BIGINT 9323372036854775807 +INSERT INTO t1 VALUES (1); + +CREATE TABLE t2 (i BIGINT UNSIGNED); +INSERT INTO t2 VALUES (10000000000000000000); -- same as first table +INSERT INTO t2 VALUES (1); + +/* simple test */ +SELECT t1.i FROM t1 JOIN t2 ON t1.i = t2.i; + +/* subquery test */ +SELECT t1.i FROM t1 WHERE t1.i = (SELECT MAX(i) FROM t2); + +/* subquery test with cast*/ +SELECT t1.i FROM t1 WHERE t1.i = CAST((SELECT MAX(i) FROM t2) AS UNSIGNED); + +DROP TABLE t1; +DROP TABLE t2; diff --git a/mysql-test/t/view.test b/mysql-test/t/view.test index 535ba3da7fa..0db97f6d4af 100644 --- a/mysql-test/t/view.test +++ b/mysql-test/t/view.test @@ -516,7 +516,7 @@ drop table t1; # create table t1 (a int, b int); create view v1 as select a, sum(b) from t1 group by a; --- error 1072 +-- error 1176 select b from v1 use index (some_index) where b=1; drop view v1; drop table t1; diff --git a/mysql-test/t/view_grant.test b/mysql-test/t/view_grant.test index 8deff474587..aa420689149 100644 --- a/mysql-test/t/view_grant.test +++ b/mysql-test/t/view_grant.test @@ -3,7 +3,7 @@ --disable_warnings drop database if exists mysqltest; -drop view if exists v1; +drop view if exists v1,v2,v3; --enable_warnings @@ -718,3 +718,98 @@ show create view v1; show create view v2; drop view v1; drop view v2; + +# +# Bug#18681: View privileges are broken +# +CREATE DATABASE mysqltest1; +CREATE USER readonly@localhost; +CREATE TABLE mysqltest1.t1 (x INT); +INSERT INTO mysqltest1.t1 VALUES (1), (2); +CREATE SQL SECURITY INVOKER VIEW mysqltest1.v_t1 AS SELECT * FROM mysqltest1.t1; +CREATE SQL SECURITY DEFINER VIEW mysqltest1.v_ts AS SELECT * FROM mysqltest1.t1; +CREATE SQL SECURITY DEFINER VIEW mysqltest1.v_ti AS SELECT * FROM mysqltest1.t1; +CREATE SQL SECURITY DEFINER VIEW mysqltest1.v_tu AS SELECT * FROM mysqltest1.t1; +CREATE SQL SECURITY DEFINER VIEW mysqltest1.v_tus AS SELECT * FROM mysqltest1.t1; +CREATE SQL SECURITY DEFINER VIEW mysqltest1.v_td AS SELECT * FROM mysqltest1.t1; +CREATE SQL SECURITY DEFINER VIEW mysqltest1.v_tds AS SELECT * FROM mysqltest1.t1; +GRANT SELECT, INSERT, UPDATE, DELETE ON mysqltest1.v_t1 TO readonly; +GRANT SELECT ON mysqltest1.v_ts TO readonly; +GRANT INSERT ON mysqltest1.v_ti TO readonly; +GRANT UPDATE ON mysqltest1.v_tu TO readonly; +GRANT UPDATE,SELECT ON mysqltest1.v_tus TO readonly; +GRANT DELETE ON mysqltest1.v_td TO readonly; +GRANT DELETE,SELECT ON mysqltest1.v_tds TO readonly; + +CONNECT (n1,localhost,readonly,,); +CONNECTION n1; + +--error 1356 +SELECT * FROM mysqltest1.v_t1; +--error 1356 +INSERT INTO mysqltest1.v_t1 VALUES(4); +--error 1356 +DELETE FROM mysqltest1.v_t1 WHERE x = 1; +--error 1356 +UPDATE mysqltest1.v_t1 SET x = 3 WHERE x = 2; +--error 1356 +UPDATE mysqltest1.v_t1 SET x = 3; +--error 1356 +DELETE FROM mysqltest1.v_t1; +--error 1356 +SELECT 1 FROM mysqltest1.v_t1; +--error 1142 +SELECT * FROM mysqltest1.t1; + +SELECT * FROM mysqltest1.v_ts; +--error 1142 +SELECT * FROM mysqltest1.v_ts, mysqltest1.t1 WHERE mysqltest1.t1.x = mysqltest1.v_ts.x; +--error 1142 +SELECT * FROM mysqltest1.v_ti; + +--error 1142 +INSERT INTO mysqltest1.v_ts VALUES (100); +INSERT INTO mysqltest1.v_ti VALUES (100); + +--error 1142 +UPDATE mysqltest1.v_ts SET x= 200 WHERE x = 100; +--error 1142 +UPDATE mysqltest1.v_ts SET x= 200; +UPDATE mysqltest1.v_tu SET x= 200 WHERE x = 100; +UPDATE mysqltest1.v_tus SET x= 200 WHERE x = 100; +UPDATE mysqltest1.v_tu SET x= 200; + +--error 1142 +DELETE FROM mysqltest1.v_ts WHERE x= 200; +--error 1142 +DELETE FROM mysqltest1.v_ts; +--error 1143 +DELETE FROM mysqltest1.v_td WHERE x= 200; +DELETE FROM mysqltest1.v_tds WHERE x= 200; +DELETE FROM mysqltest1.v_td; + +CONNECTION default; +DROP VIEW mysqltest1.v_tds; +DROP VIEW mysqltest1.v_td; +DROP VIEW mysqltest1.v_tus; +DROP VIEW mysqltest1.v_tu; +DROP VIEW mysqltest1.v_ti; +DROP VIEW mysqltest1.v_ts; +DROP VIEW mysqltest1.v_t1; +DROP TABLE mysqltest1.t1; +DROP USER readonly@localhost; +DROP DATABASE mysqltest1; + +# +# BUG#14875: Bad view DEFINER makes SHOW CREATE VIEW fail +# +CREATE TABLE t1 (a INT PRIMARY KEY); +INSERT INTO t1 VALUES (1), (2), (3); +CREATE DEFINER = 'no-such-user'@localhost VIEW v AS SELECT a from t1; +--warning 1448 +SHOW CREATE VIEW v; +--error 1449 +SELECT * FROM v; +DROP VIEW v; +DROP TABLE t1; +USE test; diff --git a/mysql-test/t/wait_timeout.test b/mysql-test/t/wait_timeout.test index 1fef3deea3c..9310c3502b9 100644 --- a/mysql-test/t/wait_timeout.test +++ b/mysql-test/t/wait_timeout.test @@ -4,10 +4,41 @@ # # Bug #8731: wait_timeout does not work on Mac OS X # + + +# Connect with another connection and reset counters +--disable_query_log +connect (wait_con,localhost,root,,test,,); +flush status; # Reset counters +connection wait_con; +let $retries=300; +let $aborted_clients = `SHOW STATUS LIKE 'aborted_clients'`; +set @aborted_clients= 0; +--enable_query_log + +# Disable reconnect and do the query +connection default; --disable_reconnect select 1; -# wait_timeout is 1, so we should get disconnected now ---sleep 2 + +# Switch to wait_con and wait until server has aborted the connection +--disable_query_log +connection wait_con; +while (!`select @aborted_clients`) +{ + sleep 0.1; + let $aborted_clients = `SHOW STATUS LIKE 'aborted_clients'`; + eval set @aborted_clients= SUBSTRING('$aborted_clients', 16)+0; + + dec $retries; + if (!$retries) + { + Failed to detect that client has been aborted; + } +} +--enable_query_log + +connection default; # When the connection is closed in this way, the error code should # be consistent see bug#2845 for an explanation --error 2006 @@ -15,12 +46,41 @@ select 2; --enable_reconnect select 3; +# # Do the same test as above on a TCP connection +# (which we get by specifying a ip adress) + +# Connect with another connection and reset counters +--disable_query_log +connection wait_con; +flush status; # Reset counters +let $retries=300; +let $aborted_clients = `SHOW STATUS LIKE 'aborted_clients'`; +set @aborted_clients= 0; +--enable_query_log + connect (con1,127.0.0.1,root,,test,$MASTER_MYPORT,); --disable_reconnect select 1; -# wait_timeout is 1, so we should get disconnected now ---sleep 2 + +# Switch to wait_con and wait until server has aborted the connection +--disable_query_log +connection wait_con; +while (!`select @aborted_clients`) +{ + sleep 0.1; + let $aborted_clients = `SHOW STATUS LIKE 'aborted_clients'`; + eval set @aborted_clients= SUBSTRING('$aborted_clients', 16)+0; + + dec $retries; + if (!$retries) + { + Failed to detect that client has been aborted; + } +} +--enable_query_log + +connection con1; # When the connection is closed in this way, the error code should # be consistent see bug#2845 for an explanation --error 2006 diff --git a/mysql-test/valgrind.supp b/mysql-test/valgrind.supp index 6939dd489cd..9061a71364d 100644 --- a/mysql-test/valgrind.supp +++ b/mysql-test/valgrind.supp @@ -240,6 +240,7 @@ fun:kill_server_thread } + # Red Hat AS 4 32 bit { dl_relocate_object @@ -405,3 +406,30 @@ futex(utime) fun:__lll_mutex_unlock_wake } + +# +# Warning when printing stack trace (to suppress some not needed warnings) +# + +{ + vprintf on stacktrace + Memcheck:Cond + fun:vfprintf + fun:uffered_vfprintf + fun:vfprintf + fun:fprintf + fun:print_stacktrace +} + +# +# Safe warnings, that may happen because of thread scheduling +# + +{ + dbug initialization + Memcheck:Leak + fun:malloc + fun:DbugMalloc + fun:ListAdd + fun:_db_set_ +} diff --git a/mysql-test/valgrind.supp.orig b/mysql-test/valgrind.supp.orig new file mode 100644 index 00000000000..1a08abcf953 --- /dev/null +++ b/mysql-test/valgrind.supp.orig @@ -0,0 +1,189 @@ +# +# Suppress some common (not fatal) errors in system libraries found by valgrind +# + +# +# Pthread doesn't free all thread specific memory before program exists +# +{ + pthread allocate_tls memory loss + Memcheck:Leak + fun:calloc + fun:_dl_allocate_tls + fun:allocate_stack + fun:pthread_create@@GLIBC_2.1 +} + +{ + pthread allocate_dtv memory loss + Memcheck:Leak + fun:calloc + fun:allocate_dtv + fun:_dl_allocate_tls_storage + fun:__GI__dl_allocate_tls + fun:pthread_create +} + +{ + pthread allocate_dtv memory loss second + Memcheck:Leak + fun:calloc + fun:allocate_dtv + fun:_dl_allocate_tls + fun:pthread_create* +} + +{ + pthread allocate_dtv memory loss second + Memcheck:Leak + fun:calloc + fun:allocate_dtv + fun:_dl_allocate_tls + fun:pthread_create* +} + +{ + pthread memalign memory loss + Memcheck:Leak + fun:memalign + fun:_dl_allocate_tls_storage + fun:__GI__dl_allocate_tls + fun:pthread_create +} + +{ + pthread pthread_key_create + Memcheck:Leak + fun:malloc + fun:* + fun:* + fun:pthread_key_create + fun:my_thread_global_init +} + +{ + pthread strstr uninit + Memcheck:Cond + fun:strstr + obj:/lib/tls/libpthread.so.* + obj:/lib/tls/libpthread.so.* + fun:call_init + fun:_dl_init + obj:/lib/ld-*.so +} + +{ + pthread strstr uninit + Memcheck:Cond + fun:strstr + obj:/lib/tls/libpthread.so.* + obj:/lib/tls/libpthread.so.* + fun:call_init + fun:_dl_init + obj:/lib/ld-*.so +} + +{ + pthread errno + Memcheck:Leak + fun:calloc + fun:_dlerror_run + fun:dlsym + fun:__errno_location +} + + +# +# Warnings in libz becasue it works with aligned memory(?) +# + +{ + libz tr_flush_block + Memcheck:Cond + fun:_tr_flush_block + fun:deflate_slow + fun:deflate + fun:do_flush + fun:gzclose +} + +{ + libz tr_flush_block2 + Memcheck:Cond + fun:_tr_flush_block + fun:deflate_slow + fun:deflate + fun:compress2 +} + +{ + libz longest_match + Memcheck:Cond + fun:longest_match + fun:deflate_slow + fun:deflate + fun:do_flush +} + +{ + libz longest_match2 + Memcheck:Cond + fun:longest_match + fun:deflate_slow + fun:deflate + fun:compress2 +} + +{ + libz deflate + Memcheck:Cond + obj:*/libz.so.* + obj:*/libz.so.* + fun:deflate + fun:compress2 +} + +{ + libz deflate2 + Memcheck:Cond + obj:*/libz.so.* + obj:*/libz.so.* + fun:deflate + obj:*/libz.so.* + fun:gzflush +} + +{ + libz deflate3 + Memcheck:Cond + obj:*/libz.so.* + obj:*/libz.so.* + fun:deflate + fun:do_flush +} + +# +# Warning from my_thread_init becasue mysqld dies before kill thread exists +# + +{ + my_thread_init kill thread memory loss second + Memcheck:Leak + fun:calloc + fun:my_thread_init + fun:kill_server_thread +} + +# +# Warning when printing stack trace (to suppress some not needed warnings) +# + +{ + vprintf on stacktrace + Memcheck:Cond + fun:vfprintf + fun:uffered_vfprintf + fun:vfprintf + fun:fprintf + fun:print_stacktrace +} diff --git a/mysys/Makefile.am b/mysys/Makefile.am index 1241e8cdded..2838427e9e0 100644 --- a/mysys/Makefile.am +++ b/mysys/Makefile.am @@ -20,8 +20,7 @@ MYSQLBASEdir= $(prefix) INCLUDES = @ZLIB_INCLUDES@ -I$(top_builddir)/include \ -I$(top_srcdir)/include -I$(srcdir) pkglib_LIBRARIES = libmysys.a -LDADD = libmysys.a ../dbug/libdbug.a \ - ../strings/libmystrings.a +LDADD = libmysys.a $(top_builddir)/strings/libmystrings.a noinst_HEADERS = mysys_priv.h my_static.h libmysys_a_SOURCES = my_init.c my_getwd.c mf_getdate.c my_mmap.c \ mf_path.c mf_loadpath.c my_file.c \ @@ -32,7 +31,7 @@ libmysys_a_SOURCES = my_init.c my_getwd.c mf_getdate.c my_mmap.c \ mf_tempdir.c my_lock.c mf_brkhant.c my_alarm.c \ my_malloc.c my_realloc.c my_once.c mulalloc.c \ my_alloc.c safemalloc.c my_new.cc \ - my_vle.c \ + my_vle.c my_atomic.c \ my_fopen.c my_fstream.c my_getsystime.c \ my_error.c errors.c my_div.c my_messnc.c \ mf_format.c mf_same.c mf_dirname.c mf_fn_ext.c \ @@ -40,7 +39,7 @@ libmysys_a_SOURCES = my_init.c my_getwd.c mf_getdate.c my_mmap.c \ mf_pack.c mf_unixpath.c mf_strip.c \ mf_wcomp.c mf_wfile.c my_gethwaddr.c \ mf_qsort.c mf_qsort2.c mf_sort.c \ - ptr_cmp.c mf_radix.c queues.c \ + ptr_cmp.c mf_radix.c queues.c my_getncpus.c \ tree.c trie.c list.c hash.c array.c string.c typelib.c \ my_copy.c my_append.c my_lib.c \ my_delete.c my_rename.c my_redel.c \ @@ -64,7 +63,6 @@ libmysys_a_LIBADD = @THREAD_LOBJECTS@ # testhash_DEPENDENCIES= $(LIBRARIES) # test_charset_DEPENDENCIES= $(LIBRARIES) # charset2html_DEPENDENCIES= $(LIBRARIES) -EXTRA_PROGRAMS = DEFS = -DDEFAULT_BASEDIR=\"$(prefix)\" \ -DDATADIR="\"$(MYSQLDATAdir)\"" \ -DDEFAULT_CHARSET_HOME="\"$(MYSQLBASEdir)\"" \ diff --git a/mysys/base64.c b/mysys/base64.c index 60218993c42..610797dd2ce 100644 --- a/mysys/base64.c +++ b/mysys/base64.c @@ -14,9 +14,10 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <base64.h> +#include <my_global.h> #include <m_string.h> /* strchr() */ #include <m_ctype.h> /* my_isspace() */ +#include <base64.h> #ifndef MAIN diff --git a/mysys/default.c b/mysys/default.c index 3a80d7b37b9..863ebc37dbc 100644 --- a/mysys/default.c +++ b/mysys/default.c @@ -763,7 +763,9 @@ static int search_default_file_with_ext(Process_option_func opt_handler, value_end=value; /* remove quotes around argument */ - if ((*value == '\"' || *value == '\'') && *value == value_end[-1]) + if ((*value == '\"' || *value == '\'') && /* First char is quote */ + (value + 1 < value_end ) && /* String is longer than 1 */ + *value == value_end[-1] ) /* First char is equal to last char */ { value++; value_end--; diff --git a/mysys/my_atomic.c b/mysys/my_atomic.c new file mode 100644 index 00000000000..21b80f0f6a1 --- /dev/null +++ b/mysys/my_atomic.c @@ -0,0 +1,46 @@ +/* Copyright (C) 2006 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include <my_global.h> +#include <my_pthread.h> + +#ifndef HAVE_INLINE +/* + the following will cause all inline functions to be instantiated +*/ +#define HAVE_INLINE +#define static extern +#endif + +#include <my_atomic.h> + +/* + checks that the current build of atomic ops + can run on this machine + + RETURN + ATOMIC_xxx values, see my_atomic.h +*/ +int my_atomic_initialize() +{ + /* currently the only thing worth checking is SMP/UP issue */ +#ifdef MY_ATOMIC_MODE_DUMMY + return my_getncpus() == 1 ? MY_ATOMIC_OK : MY_ATOMIC_NOT_1CPU; +#else + return MY_ATOMIC_OK; +#endif +} + diff --git a/mysys/my_bitmap.c b/mysys/my_bitmap.c index 70edb507b5f..92b0cbb1371 100644 --- a/mysys/my_bitmap.c +++ b/mysys/my_bitmap.c @@ -46,8 +46,8 @@ void create_last_word_mask(MY_BITMAP *map) unsigned int const used= 1U + ((map->n_bits-1U) & 0x7U); /* - * Create a mask with the upper 'unused' bits set and the lower 'used' - * bits clear. The bits within each byte is stored in big-endian order. + Create a mask with the upper 'unused' bits set and the lower 'used' + bits clear. The bits within each byte is stored in big-endian order. */ unsigned char const mask= (~((1 << used) - 1)) & 255; @@ -60,13 +60,11 @@ void create_last_word_mask(MY_BITMAP *map) unsigned char *ptr= (unsigned char*)&map->last_word_mask; map->last_word_ptr= map->bitmap + no_words_in_map(map)-1; - switch (no_bytes_in_map(map)&3) - { + switch (no_bytes_in_map(map) & 3) { case 1: map->last_word_mask= ~0U; ptr[0]= mask; return; - case 2: map->last_word_mask= ~0U; ptr[0]= 0; @@ -84,6 +82,7 @@ void create_last_word_mask(MY_BITMAP *map) } } + static inline void bitmap_lock(MY_BITMAP *map __attribute__((unused))) { #ifdef THREAD @@ -101,37 +100,41 @@ static inline void bitmap_unlock(MY_BITMAP *map __attribute__((unused))) } -my_bool bitmap_init(MY_BITMAP *map, uint32 *buf, uint n_bits, +my_bool bitmap_init(MY_BITMAP *map, my_bitmap_map *buf, uint n_bits, my_bool thread_safe) { DBUG_ENTER("bitmap_init"); - DBUG_ASSERT(n_bits > 0); if (!buf) { - uint size_in_bytes= ((n_bits+31)/32)*4 + uint size_in_bytes= bitmap_buffer_size(n_bits); + uint extra= 0; #ifdef THREAD - +(thread_safe ? sizeof(pthread_mutex_t) : 0) + if (thread_safe) + { + size_in_bytes= ALIGN_SIZE(size_in_bytes); + extra= sizeof(pthread_mutex_t); + } + map->mutex= 0; #endif - ; - if (!(buf= (uint32*) my_malloc(size_in_bytes, MYF(MY_WME)))) + if (!(buf= (my_bitmap_map*) my_malloc(size_in_bytes+extra, MYF(MY_WME)))) DBUG_RETURN(1); - } #ifdef THREAD - else - DBUG_ASSERT(thread_safe == 0); + if (thread_safe) + { + map->mutex= (pthread_mutex_t *) ((char*) buf + size_in_bytes); + pthread_mutex_init(map->mutex, MY_MUTEX_INIT_FAST); + } #endif + } #ifdef THREAD - if (thread_safe) + else { - map->mutex=(pthread_mutex_t *)buf; - pthread_mutex_init(map->mutex, MY_MUTEX_INIT_FAST); - buf+= sizeof(pthread_mutex_t)/4; + DBUG_ASSERT(thread_safe == 0); } - else - map->mutex=0; #endif + map->bitmap= buf; - map->n_bits=n_bits; + map->n_bits= n_bits; create_last_word_mask(map); bitmap_clear_all(map); DBUG_RETURN(0); @@ -144,15 +147,10 @@ void bitmap_free(MY_BITMAP *map) if (map->bitmap) { #ifdef THREAD - char *buf= (char *)map->mutex; - if (buf) + if (map->mutex) pthread_mutex_destroy(map->mutex); - else - buf=(char*) map->bitmap; - my_free(buf, MYF(0)); -#else - my_free((char*) map->bitmap, MYF(0)); #endif + my_free((char*) map->bitmap, MYF(0)); map->bitmap=0; } DBUG_VOID_RETURN; @@ -205,6 +203,40 @@ my_bool bitmap_test_and_set(MY_BITMAP *map, uint bitmap_bit) return res; } +/* + test if bit already set and clear it if it was set(thread unsafe method) + + SYNOPSIS + bitmap_fast_test_and_set() + MAP bit map struct + BIT bit number + + RETURN + 0 bit was not set + !=0 bit was set +*/ + +my_bool bitmap_fast_test_and_clear(MY_BITMAP *map, uint bitmap_bit) +{ + uchar *byte= (uchar*) map->bitmap + (bitmap_bit / 8); + uchar bit= 1 << ((bitmap_bit) & 7); + uchar res= (*byte) & bit; + *byte&= ~bit; + return res; +} + + +my_bool bitmap_test_and_clear(MY_BITMAP *map, uint bitmap_bit) +{ + my_bool res; + DBUG_ASSERT(map->bitmap && bitmap_bit < map->n_bits); + bitmap_lock(map); + res= bitmap_fast_test_and_clear(map, bitmap_bit); + bitmap_unlock(map); + return res; +} + + uint bitmap_set_next(MY_BITMAP *map) { uint bit_found; @@ -230,7 +262,6 @@ void bitmap_set_prefix(MY_BITMAP *map, uint prefix_size) *m++= (1 << prefix_bits)-1; if ((d= no_bytes_in_map(map)-prefix_bytes)) bzero(m, d); - *map->last_word_ptr|= map->last_word_mask; /*Set last bits*/ } @@ -247,7 +278,7 @@ my_bool bitmap_is_prefix(const MY_BITMAP *map, uint prefix_size) if (*m++ != 0xff) return 0; - *map->last_word_ptr^= map->last_word_mask; /*Clear bits*/ + *map->last_word_ptr&= ~map->last_word_mask; /*Clear bits*/ res= 0; if (prefix_bits && *m++ != (1 << prefix_bits)-1) goto ret; @@ -257,15 +288,15 @@ my_bool bitmap_is_prefix(const MY_BITMAP *map, uint prefix_size) goto ret; res= 1; ret: - *map->last_word_ptr|= map->last_word_mask; /*Set bits again*/ return res; } my_bool bitmap_is_set_all(const MY_BITMAP *map) { - uint32 *data_ptr= map->bitmap; - uint32 *end= map->last_word_ptr; + my_bitmap_map *data_ptr= map->bitmap; + my_bitmap_map *end= map->last_word_ptr; + *map->last_word_ptr |= map->last_word_mask; for (; data_ptr <= end; data_ptr++) if (*data_ptr != 0xFFFFFFFF) return FALSE; @@ -275,9 +306,9 @@ my_bool bitmap_is_set_all(const MY_BITMAP *map) my_bool bitmap_is_clear_all(const MY_BITMAP *map) { - uint32 *data_ptr= map->bitmap; - uint32 *end; - if (*map->last_word_ptr != map->last_word_mask) + my_bitmap_map *data_ptr= map->bitmap; + my_bitmap_map *end; + if (*map->last_word_ptr & ~map->last_word_mask) return FALSE; end= map->last_word_ptr; for (; data_ptr < end; data_ptr++) @@ -286,16 +317,18 @@ my_bool bitmap_is_clear_all(const MY_BITMAP *map) return TRUE; } +/* Return TRUE if map1 is a subset of map2 */ my_bool bitmap_is_subset(const MY_BITMAP *map1, const MY_BITMAP *map2) { - uint32 *m1= map1->bitmap, *m2= map2->bitmap, *end; + my_bitmap_map *m1= map1->bitmap, *m2= map2->bitmap, *end; DBUG_ASSERT(map1->bitmap && map2->bitmap && map1->n_bits==map2->n_bits); end= map1->last_word_ptr; - + *map1->last_word_ptr &= ~map1->last_word_mask; + *map2->last_word_ptr &= ~map2->last_word_mask; while (m1 <= end) { if ((*m1++) & ~(*m2++)) @@ -304,16 +337,36 @@ my_bool bitmap_is_subset(const MY_BITMAP *map1, const MY_BITMAP *map2) return 1; } +/* True if bitmaps has any common bits */ + +my_bool bitmap_is_overlapping(const MY_BITMAP *map1, const MY_BITMAP *map2) +{ + my_bitmap_map *m1= map1->bitmap, *m2= map2->bitmap, *end; + + DBUG_ASSERT(map1->bitmap && map2->bitmap && + map1->n_bits==map2->n_bits); + + end= map1->last_word_ptr; + *map1->last_word_ptr &= ~map1->last_word_mask; + *map2->last_word_ptr &= ~map2->last_word_mask; + while (m1 <= end) + { + if ((*m1++) & (*m2++)) + return 1; + } + return 0; +} + void bitmap_intersect(MY_BITMAP *map, const MY_BITMAP *map2) { - uint32 *to= map->bitmap, *from= map2->bitmap, *end; + my_bitmap_map *to= map->bitmap, *from= map2->bitmap, *end; uint len= no_words_in_map(map), len2 = no_words_in_map(map2); DBUG_ASSERT(map->bitmap && map2->bitmap); end= to+min(len,len2); - *map2->last_word_ptr^= map2->last_word_mask; /*Clear last bits in map2*/ + *map2->last_word_ptr&= ~map2->last_word_mask; /*Clear last bits in map2*/ while (to < end) *to++ &= *from++; @@ -323,8 +376,6 @@ void bitmap_intersect(MY_BITMAP *map, const MY_BITMAP *map2) while (to < end) *to++=0; } - *map2->last_word_ptr|= map2->last_word_mask; /*Set last bits in map*/ - *map->last_word_ptr|= map->last_word_mask; /*Set last bits in map2*/ } @@ -356,13 +407,12 @@ void bitmap_set_above(MY_BITMAP *map, uint from_byte, uint use_bit) while (to < end) *to++= use_byte; - *map->last_word_ptr|= map->last_word_mask; /*Set last bits again*/ } void bitmap_subtract(MY_BITMAP *map, const MY_BITMAP *map2) { - uint32 *to= map->bitmap, *from= map2->bitmap, *end; + my_bitmap_map *to= map->bitmap, *from= map2->bitmap, *end; DBUG_ASSERT(map->bitmap && map2->bitmap && map->n_bits==map2->n_bits); @@ -370,13 +420,12 @@ void bitmap_subtract(MY_BITMAP *map, const MY_BITMAP *map2) while (to <= end) *to++ &= ~(*from++); - *map->last_word_ptr|= map->last_word_mask; /*Set last bits again*/ } void bitmap_union(MY_BITMAP *map, const MY_BITMAP *map2) { - uint32 *to= map->bitmap, *from= map2->bitmap, *end; + my_bitmap_map *to= map->bitmap, *from= map2->bitmap, *end; DBUG_ASSERT(map->bitmap && map2->bitmap && map->n_bits==map2->n_bits); @@ -389,25 +438,23 @@ void bitmap_union(MY_BITMAP *map, const MY_BITMAP *map2) void bitmap_xor(MY_BITMAP *map, const MY_BITMAP *map2) { - uint32 *to= map->bitmap, *from= map2->bitmap, *end= map->last_word_ptr; + my_bitmap_map *to= map->bitmap, *from= map2->bitmap, *end= map->last_word_ptr; DBUG_ASSERT(map->bitmap && map2->bitmap && map->n_bits==map2->n_bits); while (to <= end) *to++ ^= *from++; - *map->last_word_ptr|= map->last_word_mask; /*Set last bits again*/ } void bitmap_invert(MY_BITMAP *map) { - uint32 *to= map->bitmap, *end; + my_bitmap_map *to= map->bitmap, *end; DBUG_ASSERT(map->bitmap); end= map->last_word_ptr; while (to <= end) *to++ ^= 0xFFFFFFFF; - *map->last_word_ptr|= map->last_word_mask; /*Set last bits again*/ } @@ -418,21 +465,35 @@ uint bitmap_bits_set(const MY_BITMAP *map) uint res= 0; DBUG_ASSERT(map->bitmap); - *map->last_word_ptr^=map->last_word_mask; /*Reset last bits to zero*/ + *map->last_word_ptr&= ~map->last_word_mask; /*Reset last bits to zero*/ while (m < end) res+= my_count_bits_ushort(*m++); - *map->last_word_ptr^=map->last_word_mask; /*Set last bits to one again*/ return res; } + +void bitmap_copy(MY_BITMAP *map, const MY_BITMAP *map2) +{ + my_bitmap_map *to= map->bitmap, *from= map2->bitmap, *end; + + DBUG_ASSERT(map->bitmap && map2->bitmap && + map->n_bits==map2->n_bits); + end= map->last_word_ptr; + while (to <= end) + *to++ = *from++; +} + + uint bitmap_get_first_set(const MY_BITMAP *map) { uchar *byte_ptr; - uint bit_found,i,j,k; - uint32 *data_ptr, *end= map->last_word_ptr; + uint i,j,k; + my_bitmap_map *data_ptr, *end= map->last_word_ptr; DBUG_ASSERT(map->bitmap); data_ptr= map->bitmap; + *map->last_word_ptr &= ~map->last_word_mask; + for (i=0; data_ptr <= end; data_ptr++, i++) { if (*data_ptr) @@ -445,12 +506,7 @@ uint bitmap_get_first_set(const MY_BITMAP *map) for (k=0; ; k++) { if (*byte_ptr & (1 << k)) - { - bit_found= (i*32) + (j*8) + k; - if (bit_found == map->n_bits) - return MY_BIT_NONE; - return bit_found; - } + return (i*32) + (j*8) + k; } DBUG_ASSERT(0); } @@ -465,11 +521,13 @@ uint bitmap_get_first_set(const MY_BITMAP *map) uint bitmap_get_first(const MY_BITMAP *map) { uchar *byte_ptr; - uint bit_found= MY_BIT_NONE, i,j,k; - uint32 *data_ptr, *end= map->last_word_ptr; + uint i,j,k; + my_bitmap_map *data_ptr, *end= map->last_word_ptr; DBUG_ASSERT(map->bitmap); data_ptr= map->bitmap; + *map->last_word_ptr|= map->last_word_mask; + for (i=0; data_ptr <= end; data_ptr++, i++) { if (*data_ptr != 0xFFFFFFFF) @@ -482,12 +540,7 @@ uint bitmap_get_first(const MY_BITMAP *map) for (k=0; ; k++) { if (!(*byte_ptr & (1 << k))) - { - bit_found= (i*32) + (j*8) + k; - if (bit_found == map->n_bits) - return MY_BIT_NONE; - return bit_found; - } + return (i*32) + (j*8) + k; } DBUG_ASSERT(0); } @@ -705,16 +758,6 @@ void bitmap_lock_flip_bit(MY_BITMAP *map, uint bitmap_bit) #endif #ifdef MAIN -static void bitmap_print(MY_BITMAP *map) -{ - uint32 *to= map->bitmap, *end= map->last_word_ptr; - while (to <= end) - { - fprintf(stderr,"0x%x ", *to++); - } - fprintf(stderr,"\n"); -} - uint get_rand_bit(uint bitsize) { return (rand() % bitsize); @@ -766,7 +809,8 @@ error2: return TRUE; } -bool test_operators(MY_BITMAP *map, uint bitsize) +bool test_operators(MY_BITMAP *map __attribute__((unused)), + uint bitsize __attribute__((unused))) { return FALSE; } @@ -819,8 +863,8 @@ bool test_compare_operators(MY_BITMAP *map, uint bitsize) uint no_loops= bitsize > 128 ? 128 : bitsize; MY_BITMAP map2_obj, map3_obj; MY_BITMAP *map2= &map2_obj, *map3= &map3_obj; - uint32 map2buf[1024]; - uint32 map3buf[1024]; + my_bitmap_map map2buf[1024]; + my_bitmap_map map3buf[1024]; bitmap_init(&map2_obj, map2buf, bitsize, FALSE); bitmap_init(&map3_obj, map3buf, bitsize, FALSE); bitmap_clear_all(map2); @@ -947,7 +991,7 @@ error2: bool test_get_first_bit(MY_BITMAP *map, uint bitsize) { - uint i, j, test_bit; + uint i, test_bit; uint no_loops= bitsize > 128 ? 128 : bitsize; for (i=0; i < no_loops; i++) { @@ -1027,7 +1071,7 @@ error3: bool do_test(uint bitsize) { MY_BITMAP map; - uint32 buf[1024]; + my_bitmap_map buf[1024]; if (bitmap_init(&map, buf, bitsize, FALSE)) { printf("init error for bitsize %d", bitsize); diff --git a/mysys/my_getncpus.c b/mysys/my_getncpus.c new file mode 100644 index 00000000000..6c45617e496 --- /dev/null +++ b/mysys/my_getncpus.c @@ -0,0 +1,40 @@ +/* Copyright (C) 2006 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* get the number of (online) CPUs */ + +#include "mysys_priv.h" +#include <unistd.h> + +static int ncpus=0; + +#ifdef _SC_NPROCESSORS_ONLN +int my_getncpus() +{ + if (!ncpus) + ncpus= sysconf(_SC_NPROCESSORS_ONLN); + return ncpus; +} + +#else +/* unknown */ +int my_getncpus() +{ + return 2; +} + +#endif + diff --git a/mysys/thr_lock.c b/mysys/thr_lock.c index 4b3e03750c8..b4de57229bf 100644 --- a/mysys/thr_lock.c +++ b/mysys/thr_lock.c @@ -480,7 +480,7 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner, data->type=lock_type; data->owner= owner; /* Must be reset ! */ VOID(pthread_mutex_lock(&lock->mutex)); - DBUG_PRINT("lock",("data: 0x%lx thread: %ld lock: 0x%lx type: %d", + DBUG_PRINT("lock",("data: 0x%lx thread: 0x%lx lock: 0x%lx type: %d", data, data->owner->info->thread_id, lock, (int) lock_type)); check_locks(lock,(uint) lock_type <= (uint) TL_READ_NO_INSERT ? @@ -499,7 +499,7 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner, and the read lock is not TL_READ_NO_INSERT */ - DBUG_PRINT("lock",("write locked by thread: %ld", + DBUG_PRINT("lock",("write locked by thread: 0x%lx", lock->write.data->owner->info->thread_id)); if (thr_lock_owner_equal(data->owner, lock->write.data->owner) || (lock->write.data->type <= TL_WRITE_DELAYED && @@ -621,7 +621,7 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner, statistic_increment(locks_immediate,&THR_LOCK_lock); goto end; } - DBUG_PRINT("lock",("write locked by thread: %ld", + DBUG_PRINT("lock",("write locked by thread: 0x%lx", lock->write.data->owner->info->thread_id)); } else @@ -657,7 +657,7 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner, goto end; } } - DBUG_PRINT("lock",("write locked by thread: %ld, type: %ld", + DBUG_PRINT("lock",("write locked by thread: 0x%lx, type: %ld", lock->read.data->owner->info->thread_id, data->type)); } wait_queue= &lock->write_wait; @@ -719,7 +719,7 @@ static inline void free_all_read_locks(THR_LOCK *lock, } lock->read_no_write_count++; } - DBUG_PRINT("lock",("giving read lock to thread: %ld", + DBUG_PRINT("lock",("giving read lock to thread: 0x%lx", data->owner->info->thread_id)); data->cond=0; /* Mark thread free */ VOID(pthread_cond_signal(cond)); @@ -737,7 +737,7 @@ void thr_unlock(THR_LOCK_DATA *data) THR_LOCK *lock=data->lock; enum thr_lock_type lock_type=data->type; DBUG_ENTER("thr_unlock"); - DBUG_PRINT("lock",("data: 0x%lx thread: %ld lock: 0x%lx", + DBUG_PRINT("lock",("data: 0x%lx thread: 0x%lx lock: 0x%lx", data, data->owner->info->thread_id, lock)); pthread_mutex_lock(&lock->mutex); check_locks(lock,"start of release lock",0); @@ -797,7 +797,7 @@ void thr_unlock(THR_LOCK_DATA *data) if (data->type == TL_WRITE_CONCURRENT_INSERT && (*lock->check_status)(data->status_param)) data->type=TL_WRITE; /* Upgrade lock */ - DBUG_PRINT("lock",("giving write lock of type %d to thread: %ld", + DBUG_PRINT("lock",("giving write lock of type %d to thread: 0x%lx", data->type, data->owner->info->thread_id)); { pthread_cond_t *cond=data->cond; diff --git a/plugin/fulltext/plugin_example.c b/plugin/fulltext/plugin_example.c index 9b937453ce4..eefb10bd26b 100644 --- a/plugin/fulltext/plugin_example.c +++ b/plugin/fulltext/plugin_example.c @@ -144,10 +144,7 @@ static void add_word(MYSQL_FTPARSER_PARAM *param, char *word, size_t len) MYSQL_FTPARSER_BOOLEAN_INFO bool_info= { FT_TOKEN_WORD, 0, 0, 0, 0, ' ', 0 }; - if (param->mode == MYSQL_FTPARSER_FULL_BOOLEAN_INFO) - param->mysql_add_word(param->mysql_ftparam, word, len, &bool_info); - else - param->mysql_add_word(param->mysql_ftparam, word, len, 0); + param->mysql_add_word(param, word, len, &bool_info); } /* diff --git a/scripts/make_sharedlib_distribution.sh b/scripts/make_sharedlib_distribution.sh index fbc945e445a..c475d0e14a4 100644 --- a/scripts/make_sharedlib_distribution.sh +++ b/scripts/make_sharedlib_distribution.sh @@ -45,9 +45,11 @@ fi mkdir -p $BASE/lib for i in \ - libmysql/.libs/libmysqlclient.s{l,o}* \ + libmysql/.libs/libmysqlclient.so* \ + libmysql/.libs/libmysqlclient.sl* \ libmysql/.libs/libmysqlclient*.dylib \ - libmysql_r/.libs/libmysqlclient_r.s{l,o}* \ + libmysql_r/.libs/libmysqlclient_r.so* \ + libmysql_r/.libs/libmysqlclient_r.sl* \ libmysql_r/.libs/libmysqlclient_r*.dylib do if [ -f $i ] diff --git a/scripts/make_win_src_distribution.sh b/scripts/make_win_src_distribution.sh index b070e30c5c7..6883719cbe4 100644 --- a/scripts/make_win_src_distribution.sh +++ b/scripts/make_win_src_distribution.sh @@ -343,6 +343,7 @@ mv $BASE/sql/sql_yacc.cpp-new $BASE/sql/sql_yacc.cpp find $BASE \( -name "*.cnf" -o -name "*.ini" \ -o -name COPYING -o -name ChangeLog -o -name EXCEPTIONS-CLIENT \ -o -name "INSTALL*" -o -name LICENSE -o -name "README*" \ + -o -name "*.dsp" -o -name "*.dsw" \ -o -name "*.vcproj" -o -name "*.sln" \) -type f -print \ | while read v do diff --git a/scripts/mysql_create_system_tables.sh b/scripts/mysql_create_system_tables.sh index a401e1d0159..69ea8e3d004 100644 --- a/scripts/mysql_create_system_tables.sh +++ b/scripts/mysql_create_system_tables.sh @@ -827,7 +827,7 @@ then c_ev="$c_ev 'HIGH_NOT_PRECEDENCE'" c_ev="$c_ev ) DEFAULT '' NOT NULL," c_ev="$c_ev comment char(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default ''," - c_ev="$c_ev PRIMARY KEY (definer, db, name)" + c_ev="$c_ev PRIMARY KEY (db, name)" c_ev="$c_ev ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT 'Events';" fi diff --git a/scripts/mysql_fix_privilege_tables.sql b/scripts/mysql_fix_privilege_tables.sql index 5b3cab16db2..f3c0c7f13be 100644 --- a/scripts/mysql_fix_privilege_tables.sql +++ b/scripts/mysql_fix_privilege_tables.sql @@ -570,7 +570,7 @@ DROP PROCEDURE create_log_tables; CREATE TABLE event ( db char(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '', - name char(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '', + name char(64) CHARACTER SET utf8 NOT NULL default '', body longblob NOT NULL, definer char(77) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL default '', execute_at DATETIME default NULL, @@ -636,7 +636,7 @@ SELECT @hadEventPriv :=1 FROM user WHERE Event_priv LIKE '%'; ALTER TABLE user add Event_priv enum('N','Y') character set utf8 DEFAULT 'N' NOT NULL AFTER Create_user_priv; ALTER TABLE db add Event_priv enum('N','Y') character set utf8 DEFAULT 'N' NOT NULL; ALTER TABLE event DROP PRIMARY KEY; -ALTER TABLE event ADD PRIMARY KEY(definer, db, name); +ALTER TABLE event ADD PRIMARY KEY(db, name); ALTER TABLE event ADD sql_mode set('REAL_AS_FLOAT', 'PIPES_AS_CONCAT', diff --git a/server-tools/instance-manager/Makefile.am b/server-tools/instance-manager/Makefile.am index 4139bf2eb10..4719828081a 100644 --- a/server-tools/instance-manager/Makefile.am +++ b/server-tools/instance-manager/Makefile.am @@ -15,7 +15,7 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA INCLUDES= @ZLIB_INCLUDES@ -I$(top_srcdir)/include \ - @openssl_includes@ @yassl_includes@ -I$(top_builddir)/include + @openssl_includes@ -I$(top_builddir)/include DEFS= -DMYSQL_INSTANCE_MANAGER -DMYSQL_SERVER diff --git a/server-tools/instance-manager/guardian.cc b/server-tools/instance-manager/guardian.cc index 7a9ff3e8367..b58241272f9 100644 --- a/server-tools/instance-manager/guardian.cc +++ b/server-tools/instance-manager/guardian.cc @@ -175,9 +175,12 @@ void Guardian_thread::process_instance(Instance *instance, case JUST_CRASHED: if (current_time - current_node->crash_moment <= 2) { - instance->start(); - log_info("guardian: starting instance %s", - instance->options.instance_name); + if (instance->is_crashed()) + { + instance->start(); + log_info("guardian: starting instance %s", + instance->options.instance_name); + } } else current_node->state= CRASHED; @@ -188,11 +191,14 @@ void Guardian_thread::process_instance(Instance *instance, { if ((current_node->restart_counter < restart_retry)) { - instance->start(); - current_node->last_checked= current_time; - current_node->restart_counter++; - log_info("guardian: restarting instance %s", - instance->options.instance_name); + if (instance->is_crashed()) + { + instance->start(); + current_node->last_checked= current_time; + current_node->restart_counter++; + log_info("guardian: restarting instance %s", + instance->options.instance_name); + } } else { diff --git a/server-tools/instance-manager/manager.cc b/server-tools/instance-manager/manager.cc index e2bb09cb9fa..599131089ed 100644 --- a/server-tools/instance-manager/manager.cc +++ b/server-tools/instance-manager/manager.cc @@ -198,28 +198,6 @@ void manager() if (create_pid_file(Options::Main::pid_file_name, manager_pid)) return; /* necessary logging has been already done. */ - sigset_t mask; - set_signals(&mask); - - /* create the listener */ - { - pthread_t listener_thd_id; - pthread_attr_t listener_thd_attr; - int rc; - - pthread_attr_init(&listener_thd_attr); - pthread_attr_setdetachstate(&listener_thd_attr, PTHREAD_CREATE_DETACHED); - rc= set_stacksize_n_create_thread(&listener_thd_id, &listener_thd_attr, - listener, &listener_args); - pthread_attr_destroy(&listener_thd_attr); - if (rc) - { - log_error("manager(): set_stacksize_n_create_thread(listener) failed"); - goto err; - } - - } - /* create guardian thread */ { pthread_t guardian_thd_id; @@ -268,6 +246,29 @@ void manager() } } + /* Initialize signals and alarm-infrastructure. */ + + sigset_t mask; + set_signals(&mask); + + /* create the listener */ + { + pthread_t listener_thd_id; + pthread_attr_t listener_thd_attr; + int rc; + + pthread_attr_init(&listener_thd_attr); + pthread_attr_setdetachstate(&listener_thd_attr, PTHREAD_CREATE_DETACHED); + rc= set_stacksize_n_create_thread(&listener_thd_id, &listener_thd_attr, + listener, &listener_args); + pthread_attr_destroy(&listener_thd_attr); + if (rc) + { + log_error("manager(): set_stacksize_n_create_thread(listener) failed"); + goto err; + } + } + /* After the list of guarded instances have been initialized, Guardian should start them. diff --git a/sql/Makefile.am b/sql/Makefile.am index 7f711a1e212..ffc5b2c2573 100644 --- a/sql/Makefile.am +++ b/sql/Makefile.am @@ -22,7 +22,7 @@ MYSQLBASEdir= $(prefix) MYSQLLIBdir= $(pkglibdir) INCLUDES = @ZLIB_INCLUDES@ \ -I$(top_builddir)/include -I$(top_srcdir)/include \ - -I$(top_srcdir)/regex -I$(srcdir) $(yassl_includes) \ + -I$(top_srcdir)/regex -I$(srcdir) \ $(openssl_includes) WRAPLIBS= @WRAPLIBS@ SUBDIRS = share @@ -35,7 +35,7 @@ LDADD = $(top_builddir)/vio/libvio.a \ $(top_builddir)/dbug/libdbug.a \ $(top_builddir)/regex/libregex.a \ $(top_builddir)/strings/libmystrings.a @ZLIB_LIBS@ - +mysqld_DEPENDENCIES= @mysql_plugin_libs@ $(LDADD) mysqld_LDADD = @MYSQLD_EXTRA_LDFLAGS@ \ @pstack_libs@ \ @mysql_plugin_libs@ \ diff --git a/sql/authors.h b/sql/authors.h index 595d14c6e0b..618c6305f6b 100644 --- a/sql/authors.h +++ b/sql/authors.h @@ -57,7 +57,8 @@ struct show_table_authors_st show_table_authors[]= { { "Yves Carlier", "", "mysqlaccess" }, { "Joshua Chamas", "Cupertino, CA, USA", "Concurrent insert, extended date syntax" }, - { "Petr Chardin", "Moscow, Russia", "Instance Manager (5.0)" }, + { "Petr Chardin", "Moscow, Russia", + "Instance Manager (5.0), Server log tables (5.1)" }, { "Wei-Jou Chen", "", "Chinese (Big5) character set" }, { "Albert Chin-A-Young", "", "Tru64 port, large file support, better TCP wrappers support" }, @@ -73,6 +74,7 @@ struct show_table_authors_st show_table_authors[]= { { "Nikolay Grishakin", "Austin, TX, USA", "Testing - Server" }, { "Wei He", "", "Chinese (GBK) character set" }, { "Eric Herman", "Amsterdam, Netherlands", "Bug fixing - federated" }, + { "Andrey Hristov", "Walldorf, Germany", "Event scheduler (5.1)" }, { "Alexander (Alexi) Ivanov", "St. Petersburg, Russia", "Replication" }, { "Alexander (Salle) Keremidarski", "Sofia, Bulgaria", "Bug fixing" }, diff --git a/sql/discover.cc b/sql/discover.cc index 2a3da55f154..938a05ff4a7 100644 --- a/sql/discover.cc +++ b/sql/discover.cc @@ -119,8 +119,8 @@ int writefrm(const char *name, const void *frmdata, uint len) { if (my_write(file,(byte*)frmdata,len,MYF(MY_WME | MY_NABP))) error= 2; + VOID(my_close(file,MYF(0))); } - VOID(my_close(file,MYF(0))); DBUG_RETURN(error); } /* writefrm */ diff --git a/sql/event.cc b/sql/event.cc index 7c3f17304aa..9ca5c62fc1c 100644 --- a/sql/event.cc +++ b/sql/event.cc @@ -31,8 +31,8 @@ should be replicated as disabled. If an event is ALTERed as DISABLED the query should go untouched into the binary log, when ALTERed as enable then it should go as SLAVESIDE_DISABLED. This is regarding the SQL interface. - TT routines however modify mysql.event internally and this does not go the log - so in this case queries has to be injected into the log...somehow... or + TT routines however modify mysql.event internally and this does not go the + log so in this case queries has to be injected into the log...somehow... or maybe a solution is RBR for this case, because the event may go only from ENABLED to DISABLED status change and this is safe for replicating. As well an event may be deleted which is also safe for RBR. @@ -40,9 +40,9 @@ - Add logging to file Warning: - - For now parallel execution is not possible because the same sp_head cannot be - executed few times!!! There is still no lock attached to particular event. - + - For now parallel execution is not possible because the same sp_head cannot + be executed few times!!! There is still no lock attached to particular + event. */ @@ -411,9 +411,9 @@ common_1_lev_code: SYNOPSIS Events::open_event_table() - thd Thread context - lock_type How to lock the table - table The table pointer + thd Thread context + lock_type How to lock the table + table We will store the open table here RETURN VALUE 1 Cannot lock table @@ -426,7 +426,7 @@ Events::open_event_table(THD *thd, enum thr_lock_type lock_type, TABLE **table) { TABLE_LIST tables; - DBUG_ENTER("open_proc_table"); + DBUG_ENTER("open_events_table"); bzero((char*) &tables, sizeof(tables)); tables.db= (char*) "mysql"; @@ -445,7 +445,7 @@ Events::open_event_table(THD *thd, enum thr_lock_type lock_type, DBUG_RETURN(2); } *table= tables.table; - + tables.table->use_all_columns(); DBUG_RETURN(0); } @@ -456,7 +456,7 @@ Events::open_event_table(THD *thd, enum thr_lock_type lock_type, SYNOPSIS evex_db_find_event_aux() thd Thread context - et evet_timed object containing dbname, name & definer + et event_timed object containing dbname & name table TABLE object for open mysql.event table. RETURN VALUE @@ -467,8 +467,7 @@ Events::open_event_table(THD *thd, enum thr_lock_type lock_type, inline int evex_db_find_event_aux(THD *thd, Event_timed *et, TABLE *table) { - return evex_db_find_event_by_name(thd, et->dbname, et->name, - et->definer, table); + return evex_db_find_event_by_name(thd, et->dbname, et->name, table); } @@ -490,7 +489,6 @@ evex_db_find_event_aux(THD *thd, Event_timed *et, TABLE *table) int evex_db_find_event_by_name(THD *thd, const LEX_STRING dbname, const LEX_STRING ev_name, - const LEX_STRING user_name, TABLE *table) { byte key[MAX_KEY_LENGTH]; @@ -505,25 +503,23 @@ evex_db_find_event_by_name(THD *thd, const LEX_STRING dbname, same fields. */ if (dbname.length > table->field[Events::FIELD_DB]->field_length || - ev_name.length > table->field[Events::FIELD_NAME]->field_length || - user_name.length > table->field[Events::FIELD_DEFINER]->field_length) + ev_name.length > table->field[Events::FIELD_NAME]->field_length) DBUG_RETURN(EVEX_KEY_NOT_FOUND); table->field[Events::FIELD_DB]->store(dbname.str, dbname.length, - &my_charset_bin); + &my_charset_bin); table->field[Events::FIELD_NAME]->store(ev_name.str, ev_name.length, - &my_charset_bin); - table->field[Events::FIELD_DEFINER]->store(user_name.str, - user_name.length, - &my_charset_bin); + &my_charset_bin); - key_copy(key, table->record[0], table->key_info, table->key_info->key_length); + key_copy(key, table->record[0], table->key_info, + table->key_info->key_length); if (table->file->index_read_idx(table->record[0], 0, key, - table->key_info->key_length,HA_READ_KEY_EXACT)) + table->key_info->key_length, + HA_READ_KEY_EXACT)) { - DBUG_PRINT("info", ("Row not fonud")); + DBUG_PRINT("info", ("Row not found")); DBUG_RETURN(EVEX_KEY_NOT_FOUND); } @@ -553,6 +549,7 @@ evex_db_find_event_by_name(THD *thd, const LEX_STRING dbname, static int evex_fill_row(THD *thd, TABLE *table, Event_timed *et, my_bool is_update) { + CHARSET_INFO *scs= system_charset_info; enum Events::enum_table_field field_num; DBUG_ENTER("evex_fill_row"); @@ -561,20 +558,23 @@ evex_fill_row(THD *thd, TABLE *table, Event_timed *et, my_bool is_update) DBUG_PRINT("info", ("name =[%s]", et->name.str)); DBUG_PRINT("info", ("body =[%s]", et->body.str)); + if (table->field[field_num= Events::FIELD_DEFINER]-> + store(et->definer.str, et->definer.length, scs)) + goto err_truncate; + if (table->field[field_num= Events::FIELD_DB]-> - store(et->dbname.str, et->dbname.length, system_charset_info)) - goto trunc_err; + store(et->dbname.str, et->dbname.length, scs)) + goto err_truncate; if (table->field[field_num= Events::FIELD_NAME]-> - store(et->name.str, et->name.length, system_charset_info)) - goto trunc_err; + store(et->name.str, et->name.length, scs)) + goto err_truncate; /* both ON_COMPLETION and STATUS are NOT NULL thus not calling set_notnull()*/ table->field[Events::FIELD_ON_COMPLETION]-> store((longlong)et->on_completion, true); - table->field[Events::FIELD_STATUS]-> - store((longlong)et->status, true); + table->field[Events::FIELD_STATUS]->store((longlong)et->status, true); /* Change the SQL_MODE only if body was present in an ALTER EVENT and of course @@ -586,8 +586,8 @@ evex_fill_row(THD *thd, TABLE *table, Event_timed *et, my_bool is_update) store((longlong)thd->variables.sql_mode, true); if (table->field[field_num= Events::FIELD_BODY]-> - store(et->body.str, et->body.length, system_charset_info)) - goto trunc_err; + store(et->body.str, et->body.length, scs)) + goto err_truncate; } if (et->expression) @@ -645,12 +645,12 @@ evex_fill_row(THD *thd, TABLE *table, Event_timed *et, my_bool is_update) if (et->comment.str) { if (table->field[field_num= Events::FIELD_COMMENT]-> - store(et->comment.str, et->comment.length, system_charset_info)) - goto trunc_err; + store(et->comment.str, et->comment.length, scs)) + goto err_truncate; } DBUG_RETURN(0); -trunc_err: +err_truncate: my_error(ER_EVENT_DATA_TOO_LONG, MYF(0), table->field[field_num]->field_name); DBUG_RETURN(EVEX_GENERAL_ERROR); } @@ -710,7 +710,8 @@ db_create_event(THD *thd, Event_timed *et, my_bool create_if_not, } DBUG_PRINT("info", ("non-existant, go forward")); - if ((ret= sp_use_new_db(thd, et->dbname.str,olddb, sizeof(olddb),0, &dbchanged))) + if ((ret= sp_use_new_db(thd, et->dbname.str,olddb, sizeof(olddb),0, + &dbchanged))) { my_error(ER_BAD_DB_ERROR, MYF(0)); goto err; @@ -746,13 +747,6 @@ db_create_event(THD *thd, Event_timed *et, my_bool create_if_not, goto err; } - if ((ret=table->field[Events::FIELD_DEFINER]-> - store(et->definer.str, et->definer.length, scs))) - { - my_error(ER_EVENT_STORE_FAILED, MYF(0), et->name.str, ret); - goto err; - } - ((Field_timestamp *)table->field[Events::FIELD_CREATED])->set_time(); /* @@ -816,6 +810,7 @@ err: static int db_update_event(THD *thd, Event_timed *et, sp_name *new_name) { + CHARSET_INFO *scs= system_charset_info; TABLE *table; int ret= EVEX_OPEN_TABLE_FAILED; DBUG_ENTER("db_update_event"); @@ -835,22 +830,21 @@ db_update_event(THD *thd, Event_timed *et, sp_name *new_name) /* first look whether we overwrite */ if (new_name) { - if (!sortcmp_lex_string(et->name, new_name->m_name, system_charset_info) && - !sortcmp_lex_string(et->dbname, new_name->m_db, system_charset_info)) + if (!sortcmp_lex_string(et->name, new_name->m_name, scs) && + !sortcmp_lex_string(et->dbname, new_name->m_db, scs)) { my_error(ER_EVENT_SAME_NAME, MYF(0), et->name.str); goto err; } - if (!evex_db_find_event_by_name(thd, new_name->m_db, new_name->m_name, - et->definer, table)) + if (!evex_db_find_event_by_name(thd,new_name->m_db,new_name->m_name,table)) { my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), new_name->m_name.str); goto err; } } /* - ...and then whether there is such an event. don't exchange the blocks + ...and then if there is such an event. Don't exchange the blocks because you will get error 120 from table handler because new_name will overwrite the key and SE will tell us that it cannot find the already found row (copied into record[1] later @@ -866,16 +860,19 @@ db_update_event(THD *thd, Event_timed *et, sp_name *new_name) /* Don't update create on row update. */ table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; - /* evex_fill_row() calls my_error() in case of error so no need to handle it here */ + /* + evex_fill_row() calls my_error() in case of error so no need to + handle it here + */ if ((ret= evex_fill_row(thd, table, et, true))) goto err; if (new_name) { table->field[Events::FIELD_DB]-> - store(new_name->m_db.str, new_name->m_db.length, system_charset_info); + store(new_name->m_db.str, new_name->m_db.length, scs); table->field[Events::FIELD_NAME]-> - store(new_name->m_name.str, new_name->m_name.length, system_charset_info); + store(new_name->m_name.str, new_name->m_name.length, scs); } if ((ret= table->file->ha_update_row(table->record[1], table->record[0]))) @@ -903,7 +900,6 @@ err: db_find_event() thd THD name the name of the event to find - definer who owns the event ett event's data if event is found tbl TABLE object to use when not NULL @@ -917,12 +913,12 @@ err: */ int -db_find_event(THD *thd, sp_name *name, LEX_STRING *definer, Event_timed **ett, - TABLE *tbl, MEM_ROOT *root) +db_find_event(THD *thd, sp_name *name, Event_timed **ett, TABLE *tbl, + MEM_ROOT *root) { TABLE *table; int ret; - Event_timed *et=NULL; + Event_timed *et= NULL; DBUG_ENTER("db_find_event"); DBUG_PRINT("enter", ("name: %*s", name->m_name.length, name->m_name.str)); @@ -938,8 +934,7 @@ db_find_event(THD *thd, sp_name *name, LEX_STRING *definer, Event_timed **ett, goto done; } - if ((ret= evex_db_find_event_by_name(thd, name->m_db, name->m_name, *definer, - table))) + if ((ret= evex_db_find_event_by_name(thd, name->m_db, name->m_name, table))) { my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name->m_name.str); goto done; @@ -959,7 +954,7 @@ db_find_event(THD *thd, sp_name *name, LEX_STRING *definer, Event_timed **ett, } done: - if (ret && et) + if (ret) { delete et; et= 0; @@ -994,7 +989,7 @@ done: int Events::create_event(THD *thd, Event_timed *et, uint create_options, - uint *rows_affected) + uint *rows_affected) { int ret; @@ -1166,7 +1161,6 @@ Events::drop_event(THD *thd, Event_timed *et, bool drop_if_exists, Events::show_create_event() thd THD spn the name of the event (db, name) - definer the definer of the event RETURN VALUE 0 OK @@ -1174,7 +1168,7 @@ Events::drop_event(THD *thd, Event_timed *et, bool drop_if_exists, */ int -Events::show_create_event(THD *thd, sp_name *spn, LEX_STRING definer) +Events::show_create_event(THD *thd, sp_name *spn) { int ret; Event_timed *et= NULL; @@ -1184,7 +1178,7 @@ Events::show_create_event(THD *thd, sp_name *spn, LEX_STRING definer) DBUG_PRINT("enter", ("name: %*s", spn->m_name.length, spn->m_name.str)); thd->reset_n_backup_open_tables_state(&backup); - ret= db_find_event(thd, spn, &definer, &et, NULL, thd->mem_root); + ret= db_find_event(thd, spn, &et, NULL, thd->mem_root); thd->restore_backup_open_tables_state(&backup); if (!ret) @@ -1289,7 +1283,9 @@ db_drop_events_from_table(THD *thd, LEX_STRING *db) if ((ret= Events::open_event_table(thd, TL_WRITE, &table))) { - sql_print_error("Table mysql.event is damaged."); + if (my_errno != ENOENT) + sql_print_error("Table mysql.event is damaged. Got error %d on open", + my_errno); DBUG_RETURN(ret); } /* only enabled events are in memory, so we go now and delete the rest */ diff --git a/sql/event.h b/sql/event.h index 40ede7b0c5f..02c5fa78150 100644 --- a/sql/event.h +++ b/sql/event.h @@ -85,7 +85,7 @@ public: open_event_table(THD *thd, enum thr_lock_type lock_type, TABLE **table); static int - show_create_event(THD *thd, sp_name *spn, LEX_STRING definer); + show_create_event(THD *thd, sp_name *spn); static int reconstruct_interval_expression(String *buf, interval_type interval, diff --git a/sql/event_priv.h b/sql/event_priv.h index b0a18377ace..43ef30a659f 100644 --- a/sql/event_priv.h +++ b/sql/event_priv.h @@ -33,7 +33,6 @@ my_time_compare(TIME *a, TIME *b); int evex_db_find_event_by_name(THD *thd, const LEX_STRING dbname, const LEX_STRING ev_name, - const LEX_STRING user_name, TABLE *table); int @@ -43,8 +42,8 @@ int db_drop_event(THD *thd, Event_timed *et, bool drop_if_exists, uint *rows_affected); int -db_find_event(THD *thd, sp_name *name, LEX_STRING *definer, Event_timed **ett, - TABLE *tbl, MEM_ROOT *root); +db_find_event(THD *thd, sp_name *name, Event_timed **ett, TABLE *tbl, + MEM_ROOT *root); int db_create_event(THD *thd, Event_timed *et, my_bool create_if_not, diff --git a/sql/event_scheduler.cc b/sql/event_scheduler.cc index 0644f8ec534..db855e9135b 100644 --- a/sql/event_scheduler.cc +++ b/sql/event_scheduler.cc @@ -873,13 +873,19 @@ Event_scheduler::drop_event(THD *thd, Event_timed *et) */ enum Event_scheduler::enum_error_code -Event_scheduler::replace_event(THD *thd, Event_timed *et, LEX_STRING *new_schema, +Event_scheduler::replace_event(THD *thd, Event_timed *et, + LEX_STRING *new_schema, LEX_STRING *new_name) { enum enum_error_code res; Event_timed *et_old, *et_new= NULL; LEX_STRING old_schema, old_name; + LINT_INIT(old_schema.str); + LINT_INIT(old_schema.length); + LINT_INIT(old_name.str); + LINT_INIT(old_name.length); + DBUG_ENTER("Event_scheduler::replace_event"); DBUG_PRINT("enter", ("thd=%p et=%p et=[%s.%s] lock=%p", thd, et, et->dbname.str, et->name.str, &LOCK_scheduler_data)); @@ -1299,11 +1305,17 @@ Event_scheduler::run(THD *thd) /* Skip disabled events */ if (et->status != Event_timed::ENABLED) { - sql_print_error("SCHEDULER: Found a disabled event %*s.%*s in the queue", - et->dbname.length, et->dbname.str, et->name.length, - et->name.str); + /* + It could be a one-timer scheduled for a time, already in the past when the + scheduler was suspended. + */ + sql_print_information("SCHEDULER: Found a disabled event %*s.%*s in the queue", + et->dbname.length, et->dbname.str, et->name.length, + et->name.str); queue_remove(&queue, 0); /* ToDo: check this again */ + if (et->dropped) + et->drop(thd); delete et; UNLOCK_SCHEDULER_DATA(); continue; @@ -1812,7 +1824,10 @@ Event_scheduler::check_n_suspend_if_needed(THD *thd) DBUG_PRINT("info", ("We have to recompute the execution times")); for (i= 0; i < queue.elements; i++) + { ((Event_timed*)queue_element(&queue, i))->compute_next_execution_time(); + ((Event_timed*)queue_element(&queue, i))->update_fields(thd); + } queue_fix(&queue); } /* This will implicitly unlock LOCK_scheduler_data */ @@ -2057,8 +2072,7 @@ Event_scheduler::load_named_event(THD *thd, Event_timed *etn, Event_timed **etn_ /* No need to use my_error() here because db_find_event() has done it */ { sp_name spn(etn->dbname, etn->name); - ret= db_find_event(thd, &spn, &etn->definer, &et_loaded, NULL, - &scheduler_root); + ret= db_find_event(thd, &spn, &et_loaded, NULL, &scheduler_root); } thd->restore_backup_open_tables_state(&backup); /* In this case no memory was allocated so we don't need to clean */ diff --git a/sql/event_timed.cc b/sql/event_timed.cc index fd85f5ebecc..d6d6dddf971 100644 --- a/sql/event_timed.cc +++ b/sql/event_timed.cc @@ -324,7 +324,7 @@ int Event_timed::init_interval(THD *thd, Item *expr, interval_type new_interval) { String value; - INTERVAL interval; + INTERVAL interval_tmp; DBUG_ENTER("Event_timed::init_interval"); @@ -332,71 +332,74 @@ Event_timed::init_interval(THD *thd, Item *expr, interval_type new_interval) DBUG_RETURN(EVEX_PARSE_ERROR); value.alloc(MAX_DATETIME_FULL_WIDTH*MY_CHARSET_BIN_MB_MAXLEN); - if (get_interval_value(expr, new_interval, &value, &interval)) + if (get_interval_value(expr, new_interval, &value, &interval_tmp)) DBUG_RETURN(EVEX_PARSE_ERROR); expression= 0; switch (new_interval) { case INTERVAL_YEAR: - expression= interval.year; + expression= interval_tmp.year; break; case INTERVAL_QUARTER: case INTERVAL_MONTH: - expression= interval.month; + expression= interval_tmp.month; break; case INTERVAL_WEEK: case INTERVAL_DAY: - expression= interval.day; + expression= interval_tmp.day; break; case INTERVAL_HOUR: - expression= interval.hour; + expression= interval_tmp.hour; break; case INTERVAL_MINUTE: - expression= interval.minute; + expression= interval_tmp.minute; break; case INTERVAL_SECOND: - expression= interval.second; + expression= interval_tmp.second; break; case INTERVAL_YEAR_MONTH: // Allow YEAR-MONTH YYYYYMM - expression= interval.year* 12 + interval.month; + expression= interval_tmp.year* 12 + interval_tmp.month; break; case INTERVAL_DAY_HOUR: - expression= interval.day* 24 + interval.hour; + expression= interval_tmp.day* 24 + interval_tmp.hour; break; case INTERVAL_DAY_MINUTE: - expression= (interval.day* 24 + interval.hour) * 60 + interval.minute; + expression= (interval_tmp.day* 24 + interval_tmp.hour) * 60 + + interval_tmp.minute; break; case INTERVAL_HOUR_SECOND: /* day is anyway 0 */ case INTERVAL_DAY_SECOND: /* DAY_SECOND having problems because of leap seconds? */ - expression= ((interval.day* 24 + interval.hour) * 60 + interval.minute)*60 - + interval.second; + expression= ((interval_tmp.day* 24 + interval_tmp.hour) * 60 + + interval_tmp.minute)*60 + + interval_tmp.second; break; case INTERVAL_MINUTE_MICROSECOND: /* day and hour are 0 */ case INTERVAL_HOUR_MICROSECOND: /* day is anyway 0 */ case INTERVAL_DAY_MICROSECOND: DBUG_RETURN(EVEX_MICROSECOND_UNSUP); - expression= ((((interval.day*24) + interval.hour)*60+interval.minute)*60 + - interval.second) * 1000000L + interval.second_part; + expression= ((((interval_tmp.day*24) + interval_tmp.hour)*60+ + interval_tmp.minute)*60 + + interval_tmp.second) * 1000000L + interval_tmp.second_part; break; case INTERVAL_HOUR_MINUTE: - expression= interval.hour * 60 + interval.minute; + expression= interval_tmp.hour * 60 + interval_tmp.minute; break; case INTERVAL_MINUTE_SECOND: - expression= interval.minute * 60 + interval.second; + expression= interval_tmp.minute * 60 + interval_tmp.second; break; case INTERVAL_SECOND_MICROSECOND: DBUG_RETURN(EVEX_MICROSECOND_UNSUP); - expression= interval.second * 1000000L + interval.second_part; + expression= interval_tmp.second * 1000000L + interval_tmp.second_part; break; case INTERVAL_MICROSECOND: DBUG_RETURN(EVEX_MICROSECOND_UNSUP); } - if (interval.neg || expression > EVEX_MAX_INTERVAL_VALUE) + if (interval_tmp.neg || expression > EVEX_MAX_INTERVAL_VALUE) DBUG_RETURN(EVEX_BAD_PARAMS); - this->interval= new_interval; + interval= new_interval; DBUG_RETURN(0); } @@ -961,7 +964,7 @@ Event_timed::compute_next_execution_time() } goto ret; } - current_thd->end_time(); + my_tz_UTC->gmt_sec_to_TIME(&time_now, current_thd->query_start()); DBUG_PRINT("info",("NOW=[%llu]", TIME_to_ulonglong_datetime(&time_now))); @@ -975,6 +978,7 @@ Event_timed::compute_next_execution_time() execute_at_null= TRUE; if (on_completion == Event_timed::ON_COMPLETION_DROP) dropped= true; + DBUG_PRINT("info", ("Dropped=%d", dropped)); status= Event_timed::DISABLED; status_changed= true; @@ -1225,7 +1229,7 @@ Event_timed::update_fields(THD *thd) { TABLE *table; Open_tables_state backup; - int ret= 0; + int ret; DBUG_ENTER("Event_timed::update_time_fields"); @@ -1233,7 +1237,7 @@ Event_timed::update_fields(THD *thd) /* No need to update if nothing has changed */ if (!(status_changed || last_executed_changed)) - goto done; + DBUG_RETURN(0); thd->reset_n_backup_open_tables_state(&backup); @@ -1244,7 +1248,7 @@ Event_timed::update_fields(THD *thd) } - if ((ret= evex_db_find_event_by_name(thd, dbname, name, definer, table))) + if ((ret= evex_db_find_event_by_name(thd, dbname, name, table))) goto done; store_record(table,record[1]); diff --git a/sql/field.cc b/sql/field.cc index e1d7aaf6343..1f65adca2d5 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -50,6 +50,9 @@ const char field_separator=','; #define BLOB_PACK_LENGTH_TO_MAX_LENGH(arg) \ ((ulong) ((LL(1) << min(arg, 4) * 8) - LL(1))) +#define ASSERT_COLUMN_MARKED_FOR_READ DBUG_ASSERT(!table->read_set || bitmap_is_set(table->read_set, field_index)) +#define ASSERT_COLUMN_MARKED_FOR_WRITE DBUG_ASSERT(!table->write_set || bitmap_is_set(table->write_set, field_index)) + /* Rules for merging different types of fields in UNION @@ -1201,9 +1204,11 @@ static bool test_if_real(const char *str,int length, CHARSET_INFO *cs) String *Field::val_int_as_str(String *val_buffer, my_bool unsigned_val) { + ASSERT_COLUMN_MARKED_FOR_READ; CHARSET_INFO *cs= &my_charset_bin; uint length= 21; longlong value= val_int(); + if (val_buffer->alloc(length)) return 0; length= (uint) (*cs->cset->longlong10_to_str)(cs, (char*) val_buffer->ptr(), @@ -1218,19 +1223,20 @@ String *Field::val_int_as_str(String *val_buffer, my_bool unsigned_val) Field::Field(char *ptr_arg,uint32 length_arg,uchar *null_ptr_arg, uchar null_bit_arg, utype unireg_check_arg, const char *field_name_arg) - :ptr(ptr_arg),null_ptr(null_ptr_arg), + :ptr(ptr_arg), null_ptr(null_ptr_arg), table(0), orig_table(0), table_name(0), field_name(field_name_arg), - query_id(0), key_start(0), part_of_key(0), part_of_sortkey(0), - unireg_check(unireg_check_arg), - field_length(length_arg), null_bit(null_bit_arg), dflt_field(0) + key_start(0), part_of_key(0), part_of_key_not_clustered(0), + part_of_sortkey(0), unireg_check(unireg_check_arg), + field_length(length_arg), null_bit(null_bit_arg) { flags=null_ptr ? 0: NOT_NULL_FLAG; comment.str= (char*) ""; comment.length=0; - fieldnr= 0; + field_index= 0; } + uint Field::offset() { return (uint) (ptr - (char*) table->record[0]); @@ -1354,6 +1360,7 @@ longlong Field::convert_decimal2longlong(const my_decimal *val, int Field_num::store_decimal(const my_decimal *val) { + ASSERT_COLUMN_MARKED_FOR_WRITE; int err= 0; longlong i= convert_decimal2longlong(val, unsigned_flag, &err); return test(err | store(i, unsigned_flag)); @@ -1378,6 +1385,7 @@ int Field_num::store_decimal(const my_decimal *val) my_decimal* Field_num::val_decimal(my_decimal *decimal_value) { + ASSERT_COLUMN_MARKED_FOR_READ; DBUG_ASSERT(result_type() == INT_RESULT); longlong nr= val_int(); int2my_decimal(E_DEC_FATAL_ERROR, nr, unsigned_flag, decimal_value); @@ -1423,6 +1431,7 @@ void Field_num::make_field(Send_field *field) int Field_str::store_decimal(const my_decimal *d) { + ASSERT_COLUMN_MARKED_FOR_WRITE; double val; /* TODO: use decimal2string? */ int err= warn_if_overflow(my_decimal2double(E_DEC_FATAL_ERROR & @@ -1433,6 +1442,7 @@ int Field_str::store_decimal(const my_decimal *d) my_decimal *Field_str::val_decimal(my_decimal *decimal_value) { + ASSERT_COLUMN_MARKED_FOR_READ; longlong nr= val_int(); int2my_decimal(E_DEC_FATAL_ERROR, nr, 0, decimal_value); return decimal_value; @@ -1498,6 +1508,7 @@ bool Field::get_time(TIME *ltime) int Field::store_time(TIME *ltime, timestamp_type type) { + ASSERT_COLUMN_MARKED_FOR_WRITE; char buff[MAX_DATE_STRING_REP_LENGTH]; uint length= (uint) my_TIME_to_str(ltime, buff); return store(buff, length, &my_charset_bin); @@ -1720,6 +1731,7 @@ void Field_decimal::overflow(bool negative) int Field_decimal::store(const char *from, uint len, CHARSET_INFO *cs) { + ASSERT_COLUMN_MARKED_FOR_WRITE; char buff[STRING_BUFFER_USUAL_SIZE]; String tmp(buff,sizeof(buff), &my_charset_bin); @@ -2089,6 +2101,7 @@ int Field_decimal::store(const char *from, uint len, CHARSET_INFO *cs) int Field_decimal::store(double nr) { + ASSERT_COLUMN_MARKED_FOR_WRITE; if (unsigned_flag && nr < 0) { overflow(1); @@ -2134,6 +2147,7 @@ int Field_decimal::store(double nr) int Field_decimal::store(longlong nr, bool unsigned_val) { + ASSERT_COLUMN_MARKED_FOR_WRITE; char buff[22]; uint length, int_part; char fyllchar, *to; @@ -2168,6 +2182,7 @@ int Field_decimal::store(longlong nr, bool unsigned_val) double Field_decimal::val_real(void) { + ASSERT_COLUMN_MARKED_FOR_READ; int not_used; char *end_not_used; return my_strntod(&my_charset_bin, ptr, field_length, &end_not_used, @@ -2176,6 +2191,7 @@ double Field_decimal::val_real(void) longlong Field_decimal::val_int(void) { + ASSERT_COLUMN_MARKED_FOR_READ; int not_used; if (unsigned_flag) return my_strntoull(&my_charset_bin, ptr, field_length, 10, NULL, @@ -2189,6 +2205,7 @@ longlong Field_decimal::val_int(void) String *Field_decimal::val_str(String *val_buffer __attribute__((unused)), String *val_ptr) { + ASSERT_COLUMN_MARKED_FOR_READ; char *str; for (str=ptr ; *str == ' ' ; str++) ; uint tmp_length=(uint) (str-ptr); @@ -2365,6 +2382,7 @@ void Field_new_decimal::set_value_on_overflow(my_decimal *decimal_value, bool Field_new_decimal::store_value(const my_decimal *decimal_value) { + ASSERT_COLUMN_MARKED_FOR_WRITE; int error= 0; DBUG_ENTER("Field_new_decimal::store_value"); #ifndef DBUG_OFF @@ -2409,6 +2427,7 @@ bool Field_new_decimal::store_value(const my_decimal *decimal_value) int Field_new_decimal::store(const char *from, uint length, CHARSET_INFO *charset) { + ASSERT_COLUMN_MARKED_FOR_WRITE; int err; my_decimal decimal_value; DBUG_ENTER("Field_new_decimal::store(char*)"); @@ -2456,6 +2475,7 @@ int Field_new_decimal::store(const char *from, uint length, int Field_new_decimal::store(double nr) { + ASSERT_COLUMN_MARKED_FOR_WRITE; my_decimal decimal_value; int err; DBUG_ENTER("Field_new_decimal::store(double)"); @@ -2490,6 +2510,7 @@ int Field_new_decimal::store(double nr) int Field_new_decimal::store(longlong nr, bool unsigned_val) { + ASSERT_COLUMN_MARKED_FOR_WRITE; my_decimal decimal_value; int err; @@ -2511,12 +2532,14 @@ int Field_new_decimal::store(longlong nr, bool unsigned_val) int Field_new_decimal::store_decimal(const my_decimal *decimal_value) { + ASSERT_COLUMN_MARKED_FOR_WRITE; return store_value(decimal_value); } double Field_new_decimal::val_real(void) { + ASSERT_COLUMN_MARKED_FOR_READ; double dbl; my_decimal decimal_value; my_decimal2double(E_DEC_FATAL_ERROR, val_decimal(&decimal_value), &dbl); @@ -2526,6 +2549,7 @@ double Field_new_decimal::val_real(void) longlong Field_new_decimal::val_int(void) { + ASSERT_COLUMN_MARKED_FOR_READ; longlong i; my_decimal decimal_value; my_decimal2int(E_DEC_FATAL_ERROR, val_decimal(&decimal_value), @@ -2536,6 +2560,7 @@ longlong Field_new_decimal::val_int(void) my_decimal* Field_new_decimal::val_decimal(my_decimal *decimal_value) { + ASSERT_COLUMN_MARKED_FOR_READ; DBUG_ENTER("Field_new_decimal::val_decimal"); binary2my_decimal(E_DEC_FATAL_ERROR, ptr, decimal_value, precision, dec); @@ -2548,6 +2573,7 @@ my_decimal* Field_new_decimal::val_decimal(my_decimal *decimal_value) String *Field_new_decimal::val_str(String *val_buffer, String *val_ptr __attribute__((unused))) { + ASSERT_COLUMN_MARKED_FOR_READ; my_decimal decimal_value; uint fixed_precision= zerofill ? precision : 0; my_decimal2string(E_DEC_FATAL_ERROR, val_decimal(&decimal_value), @@ -2584,6 +2610,7 @@ void Field_new_decimal::sql_type(String &str) const int Field_tiny::store(const char *from,uint len,CHARSET_INFO *cs) { + ASSERT_COLUMN_MARKED_FOR_WRITE; int not_used; // We can ignore result from str2int char *end; long tmp= my_strntol(cs, from, len, 10, &end, ¬_used); @@ -2630,6 +2657,7 @@ int Field_tiny::store(const char *from,uint len,CHARSET_INFO *cs) int Field_tiny::store(double nr) { + ASSERT_COLUMN_MARKED_FOR_WRITE; int error= 0; nr=rint(nr); if (unsigned_flag) @@ -2672,6 +2700,7 @@ int Field_tiny::store(double nr) int Field_tiny::store(longlong nr, bool unsigned_val) { + ASSERT_COLUMN_MARKED_FOR_WRITE; int error= 0; if (unsigned_flag) @@ -2716,6 +2745,7 @@ int Field_tiny::store(longlong nr, bool unsigned_val) double Field_tiny::val_real(void) { + ASSERT_COLUMN_MARKED_FOR_READ; int tmp= unsigned_flag ? (int) ((uchar*) ptr)[0] : (int) ((signed char*) ptr)[0]; return (double) tmp; @@ -2724,6 +2754,7 @@ double Field_tiny::val_real(void) longlong Field_tiny::val_int(void) { + ASSERT_COLUMN_MARKED_FOR_READ; int tmp= unsigned_flag ? (int) ((uchar*) ptr)[0] : (int) ((signed char*) ptr)[0]; return (longlong) tmp; @@ -2733,6 +2764,7 @@ longlong Field_tiny::val_int(void) String *Field_tiny::val_str(String *val_buffer, String *val_ptr __attribute__((unused))) { + ASSERT_COLUMN_MARKED_FOR_READ; CHARSET_INFO *cs= &my_charset_bin; uint length; uint mlength=max(field_length+1,5*cs->mbmaxlen); @@ -2788,6 +2820,7 @@ void Field_tiny::sql_type(String &res) const int Field_short::store(const char *from,uint len,CHARSET_INFO *cs) { + ASSERT_COLUMN_MARKED_FOR_WRITE; int not_used; // We can ignore result from str2int char *end; long tmp= my_strntol(cs, from, len, 10, &end, ¬_used); @@ -2841,6 +2874,7 @@ int Field_short::store(const char *from,uint len,CHARSET_INFO *cs) int Field_short::store(double nr) { + ASSERT_COLUMN_MARKED_FOR_WRITE; int error= 0; int16 res; nr=rint(nr); @@ -2892,6 +2926,7 @@ int Field_short::store(double nr) int Field_short::store(longlong nr, bool unsigned_val) { + ASSERT_COLUMN_MARKED_FOR_WRITE; int error= 0; int16 res; @@ -2946,6 +2981,7 @@ int Field_short::store(longlong nr, bool unsigned_val) double Field_short::val_real(void) { + ASSERT_COLUMN_MARKED_FOR_READ; short j; #ifdef WORDS_BIGENDIAN if (table->s->db_low_byte_first) @@ -2958,6 +2994,7 @@ double Field_short::val_real(void) longlong Field_short::val_int(void) { + ASSERT_COLUMN_MARKED_FOR_READ; short j; #ifdef WORDS_BIGENDIAN if (table->s->db_low_byte_first) @@ -2972,6 +3009,7 @@ longlong Field_short::val_int(void) String *Field_short::val_str(String *val_buffer, String *val_ptr __attribute__((unused))) { + ASSERT_COLUMN_MARKED_FOR_READ; CHARSET_INFO *cs= &my_charset_bin; uint length; uint mlength=max(field_length+1,7*cs->mbmaxlen); @@ -3062,6 +3100,7 @@ void Field_short::sql_type(String &res) const int Field_medium::store(const char *from,uint len,CHARSET_INFO *cs) { + ASSERT_COLUMN_MARKED_FOR_WRITE; int not_used; // We can ignore result from str2int char *end; long tmp= my_strntol(cs, from, len, 10, &end, ¬_used); @@ -3109,6 +3148,7 @@ int Field_medium::store(const char *from,uint len,CHARSET_INFO *cs) int Field_medium::store(double nr) { + ASSERT_COLUMN_MARKED_FOR_WRITE; int error= 0; nr=rint(nr); if (unsigned_flag) @@ -3154,6 +3194,7 @@ int Field_medium::store(double nr) int Field_medium::store(longlong nr, bool unsigned_val) { + ASSERT_COLUMN_MARKED_FOR_WRITE; int error= 0; if (unsigned_flag) @@ -3202,6 +3243,7 @@ int Field_medium::store(longlong nr, bool unsigned_val) double Field_medium::val_real(void) { + ASSERT_COLUMN_MARKED_FOR_READ; long j= unsigned_flag ? (long) uint3korr(ptr) : sint3korr(ptr); return (double) j; } @@ -3209,6 +3251,7 @@ double Field_medium::val_real(void) longlong Field_medium::val_int(void) { + ASSERT_COLUMN_MARKED_FOR_READ; long j= unsigned_flag ? (long) uint3korr(ptr) : sint3korr(ptr); return (longlong) j; } @@ -3217,6 +3260,7 @@ longlong Field_medium::val_int(void) String *Field_medium::val_str(String *val_buffer, String *val_ptr __attribute__((unused))) { + ASSERT_COLUMN_MARKED_FOR_READ; CHARSET_INFO *cs= &my_charset_bin; uint length; uint mlength=max(field_length+1,10*cs->mbmaxlen); @@ -3234,6 +3278,7 @@ String *Field_medium::val_str(String *val_buffer, bool Field_medium::send_binary(Protocol *protocol) { + ASSERT_COLUMN_MARKED_FOR_READ; return protocol->store_long(Field_medium::val_int()); } @@ -3298,6 +3343,7 @@ static bool test_if_minus(CHARSET_INFO *cs, int Field_long::store(const char *from,uint len,CHARSET_INFO *cs) { + ASSERT_COLUMN_MARKED_FOR_WRITE; ulong tmp_scan; longlong tmp; long store_tmp; @@ -3370,6 +3416,7 @@ int Field_long::store(const char *from,uint len,CHARSET_INFO *cs) int Field_long::store(double nr) { + ASSERT_COLUMN_MARKED_FOR_WRITE; int error= 0; int32 res; nr=rint(nr); @@ -3421,6 +3468,7 @@ int Field_long::store(double nr) int Field_long::store(longlong nr, bool unsigned_val) { + ASSERT_COLUMN_MARKED_FOR_WRITE; int error= 0; int32 res; DBUG_ASSERT(table->in_use == current_thd); // General safety @@ -3474,6 +3522,7 @@ int Field_long::store(longlong nr, bool unsigned_val) double Field_long::val_real(void) { + ASSERT_COLUMN_MARKED_FOR_READ; int32 j; #ifdef WORDS_BIGENDIAN if (table->s->db_low_byte_first) @@ -3486,6 +3535,7 @@ double Field_long::val_real(void) longlong Field_long::val_int(void) { + ASSERT_COLUMN_MARKED_FOR_READ; int32 j; /* See the comment in Field_long::store(long long) */ DBUG_ASSERT(table->in_use == current_thd); @@ -3501,6 +3551,7 @@ longlong Field_long::val_int(void) String *Field_long::val_str(String *val_buffer, String *val_ptr __attribute__((unused))) { + ASSERT_COLUMN_MARKED_FOR_READ; CHARSET_INFO *cs= &my_charset_bin; uint length; uint mlength=max(field_length+1,12*cs->mbmaxlen); @@ -3527,6 +3578,7 @@ String *Field_long::val_str(String *val_buffer, bool Field_long::send_binary(Protocol *protocol) { + ASSERT_COLUMN_MARKED_FOR_READ; return protocol->store_long(Field_long::val_int()); } @@ -3591,6 +3643,7 @@ void Field_long::sql_type(String &res) const int Field_longlong::store(const char *from,uint len,CHARSET_INFO *cs) { + ASSERT_COLUMN_MARKED_FOR_WRITE; longlong tmp; int error= 0; char *end; @@ -3632,6 +3685,7 @@ int Field_longlong::store(const char *from,uint len,CHARSET_INFO *cs) int Field_longlong::store(double nr) { + ASSERT_COLUMN_MARKED_FOR_WRITE; int error= 0; longlong res; @@ -3683,6 +3737,7 @@ int Field_longlong::store(double nr) int Field_longlong::store(longlong nr, bool unsigned_val) { + ASSERT_COLUMN_MARKED_FOR_WRITE; int error= 0; if (nr < 0) // Only possible error @@ -3713,6 +3768,7 @@ int Field_longlong::store(longlong nr, bool unsigned_val) double Field_longlong::val_real(void) { + ASSERT_COLUMN_MARKED_FOR_READ; longlong j; #ifdef WORDS_BIGENDIAN if (table->s->db_low_byte_first) @@ -3734,6 +3790,7 @@ double Field_longlong::val_real(void) longlong Field_longlong::val_int(void) { + ASSERT_COLUMN_MARKED_FOR_READ; longlong j; #ifdef WORDS_BIGENDIAN if (table->s->db_low_byte_first) @@ -3772,6 +3829,7 @@ String *Field_longlong::val_str(String *val_buffer, bool Field_longlong::send_binary(Protocol *protocol) { + ASSERT_COLUMN_MARKED_FOR_READ; return protocol->store_longlong(Field_longlong::val_int(), unsigned_flag); } @@ -3864,6 +3922,7 @@ int Field_float::store(const char *from,uint len,CHARSET_INFO *cs) int Field_float::store(double nr) { + ASSERT_COLUMN_MARKED_FOR_WRITE; float j; int error= 0; @@ -3928,12 +3987,14 @@ int Field_float::store(double nr) int Field_float::store(longlong nr, bool unsigned_val) { - return store(unsigned_val ? ulonglong2double((ulonglong) nr) : (double) nr); + return Field_float::store(unsigned_val ? ulonglong2double((ulonglong) nr) : + (double) nr); } double Field_float::val_real(void) { + ASSERT_COLUMN_MARKED_FOR_READ; float j; #ifdef WORDS_BIGENDIAN if (table->s->db_low_byte_first) @@ -3964,6 +4025,7 @@ longlong Field_float::val_int(void) String *Field_float::val_str(String *val_buffer, String *val_ptr __attribute__((unused))) { + ASSERT_COLUMN_MARKED_FOR_READ; float nr; #ifdef WORDS_BIGENDIAN if (table->s->db_low_byte_first) @@ -4109,6 +4171,7 @@ void Field_float::sort_string(char *to,uint length __attribute__((unused))) bool Field_float::send_binary(Protocol *protocol) { + ASSERT_COLUMN_MARKED_FOR_READ; return protocol->store((float) Field_float::val_real(), dec, (String*) 0); } @@ -4152,6 +4215,7 @@ int Field_double::store(const char *from,uint len,CHARSET_INFO *cs) int Field_double::store(double nr) { + ASSERT_COLUMN_MARKED_FOR_WRITE; int error= 0; if (isnan(nr)) @@ -4209,7 +4273,8 @@ int Field_double::store(double nr) int Field_double::store(longlong nr, bool unsigned_val) { - return store(unsigned_val ? ulonglong2double((ulonglong) nr) : (double) nr); + return Field_double::store(unsigned_val ? ulonglong2double((ulonglong) nr) : + (double) nr); } @@ -4222,6 +4287,7 @@ int Field_real::store_decimal(const my_decimal *dm) double Field_double::val_real(void) { + ASSERT_COLUMN_MARKED_FOR_READ; double j; #ifdef WORDS_BIGENDIAN if (table->s->db_low_byte_first) @@ -4236,6 +4302,7 @@ double Field_double::val_real(void) longlong Field_double::val_int(void) { + ASSERT_COLUMN_MARKED_FOR_READ; double j; longlong res; #ifdef WORDS_BIGENDIAN @@ -4275,6 +4342,7 @@ warn: my_decimal *Field_real::val_decimal(my_decimal *decimal_value) { + ASSERT_COLUMN_MARKED_FOR_READ; double2my_decimal(E_DEC_FATAL_ERROR, val_real(), decimal_value); return decimal_value; } @@ -4283,6 +4351,7 @@ my_decimal *Field_real::val_decimal(my_decimal *decimal_value) String *Field_double::val_str(String *val_buffer, String *val_ptr __attribute__((unused))) { + ASSERT_COLUMN_MARKED_FOR_READ; double nr; #ifdef WORDS_BIGENDIAN if (table->s->db_low_byte_first) @@ -4371,6 +4440,7 @@ bool Field_double::send_binary(Protocol *protocol) int Field_double::cmp(const char *a_ptr, const char *b_ptr) { + ASSERT_COLUMN_MARKED_FOR_READ; double a,b; #ifdef WORDS_BIGENDIAN if (table->s->db_low_byte_first) @@ -4529,6 +4599,7 @@ timestamp_auto_set_type Field_timestamp::get_auto_set_type() const int Field_timestamp::store(const char *from,uint len,CHARSET_INFO *cs) { + ASSERT_COLUMN_MARKED_FOR_WRITE; TIME l_time; my_time_t tmp= 0; int error; @@ -4599,6 +4670,7 @@ int Field_timestamp::store(double nr) int Field_timestamp::store(longlong nr, bool unsigned_val) { + ASSERT_COLUMN_MARKED_FOR_WRITE; TIME l_time; my_time_t timestamp= 0; int error; @@ -4650,11 +4722,13 @@ int Field_timestamp::store(longlong nr, bool unsigned_val) double Field_timestamp::val_real(void) { + ASSERT_COLUMN_MARKED_FOR_READ; return (double) Field_timestamp::val_int(); } longlong Field_timestamp::val_int(void) { + ASSERT_COLUMN_MARKED_FOR_READ; uint32 temp; TIME time_tmp; THD *thd= table->in_use; @@ -4680,6 +4754,7 @@ longlong Field_timestamp::val_int(void) String *Field_timestamp::val_str(String *val_buffer, String *val_ptr) { + ASSERT_COLUMN_MARKED_FOR_READ; uint32 temp, temp2; TIME time_tmp; THD *thd= table->in_use; @@ -4909,6 +4984,7 @@ int Field_time::store_time(TIME *ltime, timestamp_type type) int Field_time::store(double nr) { + ASSERT_COLUMN_MARKED_FOR_WRITE; long tmp; int error= 0; if (nr > 8385959.0) @@ -4946,6 +5022,7 @@ int Field_time::store(double nr) int Field_time::store(longlong nr, bool unsigned_val) { + ASSERT_COLUMN_MARKED_FOR_WRITE; long tmp; int error= 0; if (nr < (longlong) -8385959L && !unsigned_val) @@ -4983,12 +5060,14 @@ int Field_time::store(longlong nr, bool unsigned_val) double Field_time::val_real(void) { + ASSERT_COLUMN_MARKED_FOR_READ; uint32 j= (uint32) uint3korr(ptr); return (double) j; } longlong Field_time::val_int(void) { + ASSERT_COLUMN_MARKED_FOR_READ; return (longlong) sint3korr(ptr); } @@ -5001,6 +5080,7 @@ longlong Field_time::val_int(void) String *Field_time::val_str(String *val_buffer, String *val_ptr __attribute__((unused))) { + ASSERT_COLUMN_MARKED_FOR_READ; TIME ltime; val_buffer->alloc(19); long tmp=(long) sint3korr(ptr); @@ -5110,6 +5190,7 @@ void Field_time::sql_type(String &res) const int Field_year::store(const char *from, uint len,CHARSET_INFO *cs) { + ASSERT_COLUMN_MARKED_FOR_WRITE; char *end; int error; long nr= my_strntol(cs, from, len, 10, &end, &error); @@ -5148,6 +5229,7 @@ int Field_year::store(double nr) int Field_year::store(longlong nr, bool unsigned_val) { + ASSERT_COLUMN_MARKED_FOR_WRITE; if (nr < 0 || nr >= 100 && nr <= 1900 || nr > 2155) { *ptr= 0; @@ -5168,6 +5250,7 @@ int Field_year::store(longlong nr, bool unsigned_val) bool Field_year::send_binary(Protocol *protocol) { + ASSERT_COLUMN_MARKED_FOR_READ; ulonglong tmp= Field_year::val_int(); return protocol->store_short(tmp); } @@ -5181,6 +5264,7 @@ double Field_year::val_real(void) longlong Field_year::val_int(void) { + ASSERT_COLUMN_MARKED_FOR_READ; int tmp= (int) ((uchar*) ptr)[0]; if (field_length != 4) tmp%=100; // Return last 2 char @@ -5218,6 +5302,7 @@ void Field_year::sql_type(String &res) const int Field_date::store(const char *from, uint len,CHARSET_INFO *cs) { + ASSERT_COLUMN_MARKED_FOR_WRITE; TIME l_time; uint32 tmp; int error; @@ -5273,6 +5358,7 @@ int Field_date::store(double nr) int Field_date::store(longlong nr, bool unsigned_val) { + ASSERT_COLUMN_MARKED_FOR_WRITE; TIME not_used; int error; longlong initial_nr= nr; @@ -5323,6 +5409,7 @@ bool Field_date::send_binary(Protocol *protocol) double Field_date::val_real(void) { + ASSERT_COLUMN_MARKED_FOR_READ; int32 j; #ifdef WORDS_BIGENDIAN if (table->s->db_low_byte_first) @@ -5336,6 +5423,7 @@ double Field_date::val_real(void) longlong Field_date::val_int(void) { + ASSERT_COLUMN_MARKED_FOR_READ; int32 j; #ifdef WORDS_BIGENDIAN if (table->s->db_low_byte_first) @@ -5350,6 +5438,7 @@ longlong Field_date::val_int(void) String *Field_date::val_str(String *val_buffer, String *val_ptr __attribute__((unused))) { + ASSERT_COLUMN_MARKED_FOR_READ; TIME ltime; val_buffer->alloc(field_length); int32 tmp; @@ -5421,6 +5510,7 @@ void Field_date::sql_type(String &res) const int Field_newdate::store(const char *from,uint len,CHARSET_INFO *cs) { + ASSERT_COLUMN_MARKED_FOR_WRITE; TIME l_time; long tmp; int error; @@ -5461,6 +5551,7 @@ int Field_newdate::store(double nr) int Field_newdate::store(longlong nr, bool unsigned_val) { + ASSERT_COLUMN_MARKED_FOR_WRITE; TIME l_time; longlong tmp; int error; @@ -5489,6 +5580,7 @@ int Field_newdate::store(longlong nr, bool unsigned_val) int Field_newdate::store_time(TIME *ltime,timestamp_type type) { + ASSERT_COLUMN_MARKED_FOR_WRITE; long tmp; int error= 0; if (type == MYSQL_TIMESTAMP_DATE || type == MYSQL_TIMESTAMP_DATETIME) @@ -5514,12 +5606,14 @@ bool Field_newdate::send_binary(Protocol *protocol) double Field_newdate::val_real(void) { + ASSERT_COLUMN_MARKED_FOR_READ; return (double) Field_newdate::val_int(); } longlong Field_newdate::val_int(void) { + ASSERT_COLUMN_MARKED_FOR_READ; ulong j= uint3korr(ptr); j= (j % 32L)+(j / 32L % 16L)*100L + (j/(16L*32L))*10000L; return (longlong) j; @@ -5529,6 +5623,7 @@ longlong Field_newdate::val_int(void) String *Field_newdate::val_str(String *val_buffer, String *val_ptr __attribute__((unused))) { + ASSERT_COLUMN_MARKED_FOR_READ; val_buffer->alloc(field_length); val_buffer->length(field_length); uint32 tmp=(uint32) uint3korr(ptr); @@ -5605,6 +5700,7 @@ void Field_newdate::sql_type(String &res) const int Field_datetime::store(const char *from,uint len,CHARSET_INFO *cs) { + ASSERT_COLUMN_MARKED_FOR_WRITE; TIME time_tmp; int error; ulonglong tmp= 0; @@ -5656,6 +5752,7 @@ int Field_datetime::store(double nr) int Field_datetime::store(longlong nr, bool unsigned_val) { + ASSERT_COLUMN_MARKED_FOR_WRITE; TIME not_used; int error; longlong initial_nr= nr; @@ -5692,6 +5789,7 @@ int Field_datetime::store(longlong nr, bool unsigned_val) int Field_datetime::store_time(TIME *ltime,timestamp_type type) { + ASSERT_COLUMN_MARKED_FOR_WRITE; longlong tmp; int error= 0; /* @@ -5733,6 +5831,7 @@ double Field_datetime::val_real(void) longlong Field_datetime::val_int(void) { + ASSERT_COLUMN_MARKED_FOR_READ; longlong j; #ifdef WORDS_BIGENDIAN if (table->s->db_low_byte_first) @@ -5747,6 +5846,7 @@ longlong Field_datetime::val_int(void) String *Field_datetime::val_str(String *val_buffer, String *val_ptr __attribute__((unused))) { + ASSERT_COLUMN_MARKED_FOR_READ; val_buffer->alloc(field_length); val_buffer->length(field_length); ulonglong tmp; @@ -5878,6 +5978,7 @@ void Field_datetime::sql_type(String &res) const int Field_string::store(const char *from,uint length,CHARSET_INFO *cs) { + ASSERT_COLUMN_MARKED_FOR_WRITE; int error= 0, well_formed_error; uint32 not_used; char buff[STRING_BUFFER_USUAL_SIZE]; @@ -5954,6 +6055,7 @@ int Field_string::store(const char *from,uint length,CHARSET_INFO *cs) int Field_str::store(double nr) { + ASSERT_COLUMN_MARKED_FOR_WRITE; char buff[DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE]; uint length; bool use_scientific_notation= TRUE; @@ -6029,6 +6131,7 @@ int Field_longstr::store_decimal(const my_decimal *d) double Field_string::val_real(void) { + ASSERT_COLUMN_MARKED_FOR_READ; int not_used; char *end_not_used; CHARSET_INFO *cs= charset(); @@ -6038,6 +6141,7 @@ double Field_string::val_real(void) longlong Field_string::val_int(void) { + ASSERT_COLUMN_MARKED_FOR_READ; int not_used; char *end_not_used; CHARSET_INFO *cs=charset(); @@ -6048,6 +6152,7 @@ longlong Field_string::val_int(void) String *Field_string::val_str(String *val_buffer __attribute__((unused)), String *val_ptr) { + ASSERT_COLUMN_MARKED_FOR_READ; uint length= field_charset->cset->lengthsp(field_charset, ptr, field_length); /* See the comment for Field_long::store(long long) */ DBUG_ASSERT(table->in_use == current_thd); @@ -6058,6 +6163,7 @@ String *Field_string::val_str(String *val_buffer __attribute__((unused)), my_decimal *Field_string::val_decimal(my_decimal *decimal_value) { + ASSERT_COLUMN_MARKED_FOR_READ; str2my_decimal(E_DEC_FATAL_ERROR, ptr, field_length, charset(), decimal_value); return decimal_value; @@ -6295,6 +6401,7 @@ Field *Field_string::new_field(MEM_ROOT *root, struct st_table *new_table) int Field_varstring::store(const char *from,uint length,CHARSET_INFO *cs) { + ASSERT_COLUMN_MARKED_FOR_WRITE; uint32 not_used, copy_length; char buff[STRING_BUFFER_USUAL_SIZE]; String tmpstr(buff,sizeof(buff), &my_charset_bin); @@ -6369,6 +6476,7 @@ int Field_varstring::store(longlong nr, bool unsigned_val) double Field_varstring::val_real(void) { + ASSERT_COLUMN_MARKED_FOR_READ; int not_used; char *end_not_used; uint length= length_bytes == 1 ? (uint) (uchar) *ptr : uint2korr(ptr); @@ -6379,6 +6487,7 @@ double Field_varstring::val_real(void) longlong Field_varstring::val_int(void) { + ASSERT_COLUMN_MARKED_FOR_READ; int not_used; char *end_not_used; uint length= length_bytes == 1 ? (uint) (uchar) *ptr : uint2korr(ptr); @@ -6389,6 +6498,7 @@ longlong Field_varstring::val_int(void) String *Field_varstring::val_str(String *val_buffer __attribute__((unused)), String *val_ptr) { + ASSERT_COLUMN_MARKED_FOR_READ; uint length= length_bytes == 1 ? (uint) (uchar) *ptr : uint2korr(ptr); val_ptr->set((const char*) ptr+length_bytes, length, field_charset); return val_ptr; @@ -6397,6 +6507,7 @@ String *Field_varstring::val_str(String *val_buffer __attribute__((unused)), my_decimal *Field_varstring::val_decimal(my_decimal *decimal_value) { + ASSERT_COLUMN_MARKED_FOR_READ; uint length= length_bytes == 1 ? (uint) (uchar) *ptr : uint2korr(ptr); str2my_decimal(E_DEC_FATAL_ERROR, ptr+length_bytes, length, charset(), decimal_value); @@ -6937,6 +7048,7 @@ void Field_blob::put_length(char *pos, uint32 length) int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs) { + ASSERT_COLUMN_MARKED_FOR_WRITE; int error= 0, well_formed_error; if (!length) { @@ -7023,6 +7135,7 @@ int Field_blob::store(longlong nr, bool unsigned_val) double Field_blob::val_real(void) { + ASSERT_COLUMN_MARKED_FOR_READ; int not_used; char *end_not_used, *blob; uint32 length; @@ -7039,6 +7152,7 @@ double Field_blob::val_real(void) longlong Field_blob::val_int(void) { + ASSERT_COLUMN_MARKED_FOR_READ; int not_used; char *blob; memcpy_fixed(&blob,ptr+packlength,sizeof(char*)); @@ -7051,6 +7165,7 @@ longlong Field_blob::val_int(void) String *Field_blob::val_str(String *val_buffer __attribute__((unused)), String *val_ptr) { + ASSERT_COLUMN_MARKED_FOR_READ; char *blob; memcpy_fixed(&blob,ptr+packlength,sizeof(char*)); if (!blob) @@ -7063,6 +7178,7 @@ String *Field_blob::val_str(String *val_buffer __attribute__((unused)), my_decimal *Field_blob::val_decimal(my_decimal *decimal_value) { + ASSERT_COLUMN_MARKED_FOR_READ; const char *blob; memcpy_fixed(&blob, ptr+packlength, sizeof(const char*)); if (!blob) @@ -7631,6 +7747,7 @@ void Field_enum::store_type(ulonglong value) int Field_enum::store(const char *from,uint length,CHARSET_INFO *cs) { + ASSERT_COLUMN_MARKED_FOR_WRITE; int err= 0; uint32 not_used; char buff[STRING_BUFFER_USUAL_SIZE]; @@ -7677,6 +7794,7 @@ int Field_enum::store(double nr) int Field_enum::store(longlong nr, bool unsigned_val) { + ASSERT_COLUMN_MARKED_FOR_WRITE; int error= 0; if ((ulonglong) nr > typelib->count || nr == 0) { @@ -7697,44 +7815,45 @@ double Field_enum::val_real(void) longlong Field_enum::val_int(void) { + ASSERT_COLUMN_MARKED_FOR_READ; switch (packlength) { case 1: return (longlong) (uchar) ptr[0]; case 2: - { - uint16 tmp; + { + uint16 tmp; #ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) - tmp=sint2korr(ptr); - else + if (table->s->db_low_byte_first) + tmp=sint2korr(ptr); + else #endif - shortget(tmp,ptr); - return (longlong) tmp; - } + shortget(tmp,ptr); + return (longlong) tmp; + } case 3: return (longlong) uint3korr(ptr); case 4: - { - uint32 tmp; + { + uint32 tmp; #ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) - tmp=uint4korr(ptr); - else + if (table->s->db_low_byte_first) + tmp=uint4korr(ptr); + else #endif - longget(tmp,ptr); - return (longlong) tmp; - } + longget(tmp,ptr); + return (longlong) tmp; + } case 8: - { - longlong tmp; + { + longlong tmp; #ifdef WORDS_BIGENDIAN - if (table->s->db_low_byte_first) - tmp=sint8korr(ptr); - else + if (table->s->db_low_byte_first) + tmp=sint8korr(ptr); + else #endif - longlongget(tmp,ptr); - return tmp; - } + longlongget(tmp,ptr); + return tmp; + } } return 0; // impossible } @@ -7812,6 +7931,7 @@ void Field_enum::sql_type(String &res) const int Field_set::store(const char *from,uint length,CHARSET_INFO *cs) { + ASSERT_COLUMN_MARKED_FOR_WRITE; bool got_warning= 0; int err= 0; char *not_used; @@ -7851,6 +7971,7 @@ int Field_set::store(const char *from,uint length,CHARSET_INFO *cs) int Field_set::store(longlong nr, bool unsigned_val) { + ASSERT_COLUMN_MARKED_FOR_WRITE; int error= 0; if ((ulonglong) nr > (ulonglong) (((longlong) 1 << typelib->count) - (longlong) 1)) @@ -8034,6 +8155,7 @@ Field *Field_bit::new_key_field(MEM_ROOT *root, int Field_bit::store(const char *from, uint length, CHARSET_INFO *cs) { + ASSERT_COLUMN_MARKED_FOR_WRITE; int delta; for (; length && !*from; from++, length--); // skip left 0's @@ -8080,7 +8202,7 @@ int Field_bit::store(const char *from, uint length, CHARSET_INFO *cs) int Field_bit::store(double nr) { - return store((longlong) nr, FALSE); + return Field_bit::store((longlong) nr, FALSE); } @@ -8109,6 +8231,7 @@ double Field_bit::val_real(void) longlong Field_bit::val_int(void) { + ASSERT_COLUMN_MARKED_FOR_READ; ulonglong bits= 0; if (bit_len) { @@ -8133,6 +8256,7 @@ longlong Field_bit::val_int(void) String *Field_bit::val_str(String *val_buffer, String *val_ptr __attribute__((unused))) { + ASSERT_COLUMN_MARKED_FOR_READ; char buff[sizeof(longlong)]; uint length= min(pack_length(), sizeof(longlong)); ulonglong bits= val_int(); @@ -8148,6 +8272,7 @@ String *Field_bit::val_str(String *val_buffer, my_decimal *Field_bit::val_decimal(my_decimal *deciaml_value) { + ASSERT_COLUMN_MARKED_FOR_READ; int2my_decimal(E_DEC_FATAL_ERROR, val_int(), 1, deciaml_value); return deciaml_value; } @@ -8277,6 +8402,7 @@ Field_bit_as_char::Field_bit_as_char(char *ptr_arg, uint32 len_arg, int Field_bit_as_char::store(const char *from, uint length, CHARSET_INFO *cs) { + ASSERT_COLUMN_MARKED_FOR_WRITE; int delta; uchar bits= field_length & 7; @@ -8839,15 +8965,14 @@ Field *make_field(TABLE_SHARE *share, char *ptr, uint32 field_length, null_bit= ((uchar) 1) << null_bit; } - switch (field_type) - { - case FIELD_TYPE_DATE: - case FIELD_TYPE_NEWDATE: - case FIELD_TYPE_TIME: - case FIELD_TYPE_DATETIME: - case FIELD_TYPE_TIMESTAMP: - field_charset= &my_charset_bin; - default: break; + switch (field_type) { + case FIELD_TYPE_DATE: + case FIELD_TYPE_NEWDATE: + case FIELD_TYPE_TIME: + case FIELD_TYPE_DATETIME: + case FIELD_TYPE_TIMESTAMP: + field_charset= &my_charset_bin; + default: break; } if (f_is_alpha(pack_flag)) @@ -9059,14 +9184,15 @@ create_field::create_field(Field *old_field,Field *orig_field) diff= (my_ptrdiff_t) (orig_field->table->s->default_values- orig_field->table->record[0]); orig_field->move_field_offset(diff); // Points now at default_values - is_null= orig_field->is_real_null(); - res= orig_field->val_str(&tmp); - orig_field->move_field_offset(-diff); // Back to record[0] - if (!is_null) + if (!orig_field->is_real_null()) { + char buff[MAX_FIELD_WIDTH], *pos; + String tmp(buff, sizeof(buff), charset), *res; + res= orig_field->val_str(&tmp); pos= (char*) sql_strmake(res->ptr(), res->length()); def= new Item_string(pos, res->length(), charset); } + orig_field->move_field_offset(-diff); // Back to record[0] } } diff --git a/sql/field.h b/sql/field.h index 602b4ea779d..2ac7ec2c69d 100644 --- a/sql/field.h +++ b/sql/field.h @@ -55,12 +55,6 @@ public: char *ptr; // Position to field in record uchar *null_ptr; // Byte where null_bit is /* - dflt_field is used only for the fields of temporary tables. - It points to the default value of the field in another table - from which this field has been created. - */ - Field *dflt_field; // Field to copy default value from - /* Note that you can use table->in_use as replacement for current_thd member only inside of val_*() and store() members (e.g. you can't use it in cons) */ @@ -68,10 +62,9 @@ public: struct st_table *orig_table; // Pointer to original table const char **table_name, *field_name; LEX_STRING comment; - query_id_t query_id; // For quick test of used fields - bool add_index; // For check if field will be indexed /* Field is part of the following keys */ - key_map key_start,part_of_key,part_of_sortkey; + key_map key_start, part_of_key, part_of_key_not_clustered; + key_map part_of_sortkey; /* We use three additional unireg types for TIMESTAMP to overcome limitation of current binary format of .frm file. We'd like to be able to support @@ -94,12 +87,8 @@ public: utype unireg_check; uint32 field_length; // Length of field - uint field_index; // field number in fields array uint32 flags; - /* fieldnr is the id of the field (first field = 1) as is also - used in key_part. - */ - uint16 fieldnr; + uint16 field_index; // field number in fields array uchar null_bit; // Bit used to test null bit Field(char *ptr_arg,uint32 length_arg,uchar *null_ptr_arg,uchar null_bit_arg, diff --git a/sql/filesort.cc b/sql/filesort.cc index 42d25dbbaee..4e48df5db9f 100644 --- a/sql/filesort.cc +++ b/sql/filesort.cc @@ -44,6 +44,7 @@ static ha_rows find_all_keys(SORTPARAM *param,SQL_SELECT *select, static int write_keys(SORTPARAM *param,uchar * *sort_keys, uint count, IO_CACHE *buffer_file, IO_CACHE *tempfile); static void make_sortkey(SORTPARAM *param,uchar *to, byte *ref_pos); +static void register_used_fields(SORTPARAM *param); static int merge_index(SORTPARAM *param,uchar *sort_buffer, BUFFPEK *buffpek, uint maxbuffer,IO_CACHE *tempfile, @@ -66,11 +67,11 @@ static void unpack_addon_fields(struct st_sort_addon_field *addon_field, table Table to sort sortorder How to sort the table s_length Number of elements in sortorder - select condition to apply to the rows - special Not used. - (This could be used to sort the rows pointed on by - select->file) - examined_rows Store number of examined rows here + select Condition to apply to the rows + ha_maxrows Return only this many rows + sort_positions Set to 1 if we want to force sorting by position + (Needed by UPDATE/INSERT or ALTER TABLE) + examined_rows Store number of examined rows here IMPLEMENTATION Creates a set of pointers that can be used to read the rows @@ -81,6 +82,10 @@ static void unpack_addon_fields(struct st_sort_addon_field *addon_field, Before calling filesort, one must have done table->file->info(HA_STATUS_VARIABLE) + NOTES + If we sort by position (like if sort_positions is 1) filesort() will + call table->prepare_for_position(). + RETURN HA_POS_ERROR Error # Number of rows @@ -92,7 +97,8 @@ static void unpack_addon_fields(struct st_sort_addon_field *addon_field, */ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length, - SQL_SELECT *select, ha_rows max_rows, ha_rows *examined_rows) + SQL_SELECT *select, ha_rows max_rows, + bool sort_positions, ha_rows *examined_rows) { int error; ulong memavl, min_sort_memory; @@ -128,8 +134,8 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length, param.ref_length= table->file->ref_length; param.addon_field= 0; param.addon_length= 0; - if (!(table->file->table_flags() & HA_FAST_KEY_READ) && - !table->fulltext_searched) + if (!(table->file->ha_table_flags() & HA_FAST_KEY_READ) && + !table->fulltext_searched && !sort_positions) { /* Get the descriptors of all fields whose values are appended @@ -175,7 +181,7 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length, if (select && select->quick && select->quick->records > 0L) { records=min((ha_rows) (select->quick->records*2+EXTRA_RECORDS*2), - table->file->records)+EXTRA_RECORDS; + table->file->stats.records)+EXTRA_RECORDS; selected_records_file=0; } else @@ -404,8 +410,11 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select, TABLE *sort_form; volatile THD::killed_state *killed= ¤t_thd->killed; handler *file; + MY_BITMAP *save_read_set, *save_write_set; DBUG_ENTER("find_all_keys"); - DBUG_PRINT("info",("using: %s",(select?select->quick?"ranges":"where":"every row"))); + DBUG_PRINT("info",("using: %s", + (select ? select->quick ? "ranges" : "where": + "every row"))); idx=indexpos=0; error=quick_select=0; @@ -415,7 +424,7 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select, ref_pos= ref_buff; quick_select=select && select->quick; record=0; - flag= ((!indexfile && file->table_flags() & HA_REC_NOT_IN_SEQ) + flag= ((!indexfile && file->ha_table_flags() & HA_REC_NOT_IN_SEQ) || quick_select); if (indexfile || flag) ref_pos= &file->ref[0]; @@ -437,6 +446,19 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select, select, 1, 1); } + /* Remember original bitmaps */ + save_read_set= sort_form->read_set; + save_write_set= sort_form->write_set; + /* Set up temporary column read map for columns used by sort */ + bitmap_clear_all(&sort_form->tmp_set); + /* Temporary set for register_used_fields and register_field_in_read_map */ + sort_form->read_set= &sort_form->tmp_set; + register_used_fields(param); + if (select && select->cond) + select->cond->walk(&Item::register_field_in_read_map, 1, + (byte*) sort_form); + sort_form->column_bitmaps_set(&sort_form->tmp_set, &sort_form->tmp_set); + for (;;) { if (quick_select) @@ -515,6 +537,9 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select, file->ha_rnd_end(); } + /* Signal we should use orignal column read and write maps */ + sort_form->column_bitmaps_set(save_read_set, save_write_set); + DBUG_PRINT("test",("error: %d indexpos: %d",error,indexpos)); if (error != HA_ERR_END_OF_FILE) { @@ -845,6 +870,50 @@ static void make_sortkey(register SORTPARAM *param, return; } + +/* + Register fields used by sorting in the sorted table's read set +*/ + +static void register_used_fields(SORTPARAM *param) +{ + reg1 SORT_FIELD *sort_field; + reg5 uint length; + TABLE *table=param->sort_form; + MY_BITMAP *bitmap= table->read_set; + + for (sort_field= param->local_sortorder ; + sort_field != param->end ; + sort_field++) + { + Field *field; + if ((field= sort_field->field)) + { + if (field->table == table) + bitmap_set_bit(bitmap, field->field_index); + } + else + { // Item + sort_field->item->walk(&Item::register_field_in_read_map, 1, + (byte *) table); + } + } + + if (param->addon_field) + { + SORT_ADDON_FIELD *addonf= param->addon_field; + Field *field; + for ( ; (field= addonf->field) ; addonf++) + bitmap_set_bit(bitmap, field->field_index); + } + else + { + /* Save filepos last */ + table->prepare_for_position(); + } +} + + static bool save_index(SORTPARAM *param, uchar **sort_keys, uint count, FILESORT_INFO *table_sort) { @@ -1353,7 +1422,8 @@ get_addon_fields(THD *thd, Field **ptabfield, uint sortlength, uint *plength) uint length= 0; uint fields= 0; uint null_fields= 0; - query_id_t query_id= thd->query_id; + MY_BITMAP *read_set= (*ptabfield)->table->read_set; + /* If there is a reference to a field in the query add it to the the set of appended fields. @@ -1365,17 +1435,9 @@ get_addon_fields(THD *thd, Field **ptabfield, uint sortlength, uint *plength) */ *plength= 0; - /* - The following statement is added to avoid sorting in alter_table. - The fact is the filter 'field->query_id != thd->query_id' - doesn't work for alter table - */ - if (thd->lex->sql_command != SQLCOM_SELECT && - thd->lex->sql_command != SQLCOM_INSERT_SELECT) - return 0; for (pfield= ptabfield; (field= *pfield) ; pfield++) { - if (field->query_id != query_id) + if (!bitmap_is_set(read_set, field->field_index)) continue; if (field->flags & BLOB_FLAG) return 0; @@ -1398,7 +1460,7 @@ get_addon_fields(THD *thd, Field **ptabfield, uint sortlength, uint *plength) null_fields= 0; for (pfield= ptabfield; (field= *pfield) ; pfield++) { - if (field->query_id != thd->query_id) + if (!bitmap_is_set(read_set, field->field_index)) continue; addonf->field= field; addonf->offset= length; diff --git a/sql/ha_berkeley.cc b/sql/ha_berkeley.cc index 473fb149871..d8159a81f90 100644 --- a/sql/ha_berkeley.cc +++ b/sql/ha_berkeley.cc @@ -25,7 +25,8 @@ We will need an updated Berkeley DB version for this. - Killing threads that has got a 'deadlock' - SHOW TABLE STATUS should give more information about the table. - - Get a more accurate count of the number of rows (estimate_rows_upper_bound()). + - Get a more accurate count of the number of rows + (estimate_rows_upper_bound()). We could store the found number of rows when the table is scanned and then increment the counter for each attempted write. - We will need to extend the manager thread to makes checkpoints at @@ -97,6 +98,9 @@ pthread_mutex_t bdb_mutex; static DB_ENV *db_env; static HASH bdb_open_tables; +static const char berkeley_hton_name[]= "BerkeleyDB"; +static const int berkeley_hton_name_length=sizeof(berkeley_hton_name)-1; + const char *berkeley_lock_names[] = { "DEFAULT", "OLDEST", "RANDOM", "YOUNGEST", "EXPIRE", "MAXLOCKS", "MAXWRITE", "MINLOCKS", "MINWRITE", 0 }; @@ -123,53 +127,14 @@ static int berkeley_rollback(THD *thd, bool all); static int berkeley_rollback_to_savepoint(THD* thd, void *savepoint); static int berkeley_savepoint(THD* thd, void *savepoint); static int berkeley_release_savepoint(THD* thd, void *savepoint); -static handler *berkeley_create_handler(TABLE_SHARE *table); - -static const char berkeley_hton_name[]= "BerkeleyDB"; -static const char berkeley_hton_comment[]= - "Supports transactions and page-level locking"; +static handler *berkeley_create_handler(TABLE_SHARE *table, + MEM_ROOT *mem_root); -handlerton berkeley_hton = { - MYSQL_HANDLERTON_INTERFACE_VERSION, - berkeley_hton_name, - SHOW_OPTION_YES, - berkeley_hton_comment, - DB_TYPE_BERKELEY_DB, - berkeley_init, - 0, /* slot */ - sizeof(DB_TXN *), /* savepoint size */ - berkeley_close_connection, - berkeley_savepoint, /* savepoint_set */ - berkeley_rollback_to_savepoint, /* savepoint_rollback */ - berkeley_release_savepoint, /* savepoint_release */ - berkeley_commit, - berkeley_rollback, - NULL, /* prepare */ - NULL, /* recover */ - NULL, /* commit_by_xid */ - NULL, /* rollback_by_xid */ - NULL, /* create_cursor_read_view */ - NULL, /* set_cursor_read_view */ - NULL, /* close_cursor_read_view */ - berkeley_create_handler, /* Create a new handler */ - NULL, /* Drop a database */ - berkeley_end, /* Panic call */ - NULL, /* Start Consistent Snapshot */ - berkeley_flush_logs, /* Flush logs */ - berkeley_show_status, /* Show status */ - NULL, /* Partition flags */ - NULL, /* Alter table flags */ - NULL, /* Alter Tablespace */ - NULL, /* Fill Files Table */ - HTON_CLOSE_CURSORS_AT_COMMIT | HTON_FLUSH_AFTER_RENAME, - NULL, /* binlog_func */ - NULL, /* binlog_log_query */ - NULL /* release_temporary_latches */ -}; +handlerton berkeley_hton; -handler *berkeley_create_handler(TABLE_SHARE *table) +static handler *berkeley_create_handler(TABLE_SHARE *table, MEM_ROOT *mem_root) { - return new ha_berkeley(table); + return new (mem_root) ha_berkeley(table); } typedef struct st_berkeley_trx_data { @@ -181,12 +146,27 @@ typedef struct st_berkeley_trx_data { /* General functions */ -bool berkeley_init(void) +int berkeley_init(void) { DBUG_ENTER("berkeley_init"); + berkeley_hton.state=SHOW_OPTION_YES; + berkeley_hton.db_type=DB_TYPE_BERKELEY_DB; + berkeley_hton.savepoint_offset=sizeof(DB_TXN *); + berkeley_hton.close_connection=berkeley_close_connection; + berkeley_hton.savepoint_set=berkeley_savepoint; + berkeley_hton.savepoint_rollback=berkeley_rollback_to_savepoint; + berkeley_hton.savepoint_release=berkeley_release_savepoint; + berkeley_hton.commit=berkeley_commit; + berkeley_hton.rollback=berkeley_rollback; + berkeley_hton.create=berkeley_create_handler; + berkeley_hton.panic=berkeley_end; + berkeley_hton.flush_logs=berkeley_flush_logs; + berkeley_hton.show_status=berkeley_show_status; + berkeley_hton.flags=HTON_CLOSE_CURSORS_AT_COMMIT | HTON_FLUSH_AFTER_RENAME; + if (have_berkeley_db != SHOW_OPTION_YES) - goto error; + return 0; // nothing else to do if (!berkeley_tmpdir) berkeley_tmpdir=mysql_tmpdir; @@ -373,7 +353,6 @@ static int berkeley_release_savepoint(THD* thd, void *savepoint) static bool berkeley_show_logs(THD *thd, stat_print_fn *stat_print) { char **all_logs, **free_logs, **a, **f; - uint hton_name_len= strlen(berkeley_hton.name); int error=1; MEM_ROOT **root_ptr= my_pthread_getspecific_ptr(MEM_ROOT**,THR_MALLOC); MEM_ROOT show_logs_root, *old_mem_root= *root_ptr; @@ -382,6 +361,7 @@ static bool berkeley_show_logs(THD *thd, stat_print_fn *stat_print) init_sql_alloc(&show_logs_root, BDB_LOG_ALLOC_BLOCK_SIZE, BDB_LOG_ALLOC_BLOCK_SIZE); *root_ptr= &show_logs_root; + all_logs= free_logs= 0; if ((error= db_env->log_archive(db_env, &all_logs, DB_ARCH_ABS | DB_ARCH_LOG)) || @@ -401,21 +381,25 @@ static bool berkeley_show_logs(THD *thd, stat_print_fn *stat_print) if (f && *f && strcmp(*a, *f) == 0) { f++; - if ((error= stat_print(thd, berkeley_hton.name, hton_name_len, - *a, strlen(*a), + if ((error= stat_print(thd, berkeley_hton_name, + berkeley_hton_name_length, *a, strlen(*a), STRING_WITH_LEN(SHOW_LOG_STATUS_FREE)))) break; } else { - if ((error= stat_print(thd, berkeley_hton.name, hton_name_len, - *a, strlen(*a), + if ((error= stat_print(thd, berkeley_hton_name, + berkeley_hton_name_length, *a, strlen(*a), STRING_WITH_LEN(SHOW_LOG_STATUS_INUSE)))) break; } } } err: + if (all_logs) + free(all_logs); + if (free_logs) + free(free_logs); free_root(&show_logs_root,MYF(0)); *root_ptr= old_mem_root; DBUG_RETURN(error); @@ -479,7 +463,7 @@ void berkeley_cleanup_log_files(void) ha_berkeley::ha_berkeley(TABLE_SHARE *table_arg) :handler(&berkeley_hton, table_arg), alloc_ptr(0), rec_buff(0), file(0), int_table_flags(HA_REC_NOT_IN_SEQ | HA_FAST_KEY_READ | - HA_NULL_IN_KEY | HA_CAN_INDEX_BLOBS | HA_NOT_EXACT_COUNT | + HA_NULL_IN_KEY | HA_CAN_INDEX_BLOBS | HA_PRIMARY_KEY_IN_READ_INDEX | HA_FILE_BASED | HA_CAN_GEOMETRY | HA_AUTO_PART_KEY | HA_TABLE_SCAN_ON_INDEX), @@ -783,7 +767,7 @@ int ha_berkeley::open(const char *name, int mode, uint test_if_locked) transaction=0; cursor=0; key_read=0; - block_size=8192; // Berkeley DB block size + stats.block_size=8192; // Berkeley DB block size share->fixed_length_row= !(table_share->db_create_options & HA_OPTION_PACK_RECORD); @@ -799,7 +783,7 @@ int ha_berkeley::close(void) my_free((char*) rec_buff,MYF(MY_ALLOW_ZERO_PTR)); my_free(alloc_ptr,MYF(MY_ALLOW_ZERO_PTR)); - ha_berkeley::extra(HA_EXTRA_RESET); // current_row buffer + ha_berkeley::reset(); // current_row buffer DBUG_RETURN(free_share(share,table, hidden_primary_key,0)); } @@ -900,11 +884,13 @@ void ha_berkeley::unpack_row(char *record, DBT *row) else { /* Copy null bits */ + my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set); const char *ptr= (const char*) row->data; memcpy(record, ptr, table_share->null_bytes); ptr+= table_share->null_bytes; for (Field **field=table->field ; *field ; field++) ptr= (*field)->unpack(record + (*field)->offset(), ptr); + dbug_tmp_restore_column_map(table->write_set, old_map); } } @@ -962,6 +948,7 @@ DBT *ha_berkeley::create_key(DBT *key, uint keynr, char *buff, KEY *key_info=table->key_info+keynr; KEY_PART_INFO *key_part=key_info->key_part; KEY_PART_INFO *end=key_part+key_info->key_parts; + my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set); DBUG_ENTER("create_key"); key->data=buff; @@ -985,6 +972,7 @@ DBT *ha_berkeley::create_key(DBT *key, uint keynr, char *buff, } key->size= (buff - (char*) key->data); DBUG_DUMP("key",(char*) key->data, key->size); + dbug_tmp_restore_column_map(table->write_set, old_map); DBUG_RETURN(key); } @@ -1002,6 +990,7 @@ DBT *ha_berkeley::pack_key(DBT *key, uint keynr, char *buff, KEY *key_info=table->key_info+keynr; KEY_PART_INFO *key_part=key_info->key_part; KEY_PART_INFO *end=key_part+key_info->key_parts; + my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set); DBUG_ENTER("bdb:pack_key"); bzero((char*) key,sizeof(*key)); @@ -1029,6 +1018,7 @@ DBT *ha_berkeley::pack_key(DBT *key, uint keynr, char *buff, } key->size= (buff - (char*) key->data); DBUG_DUMP("key",(char*) key->data, key->size); + dbug_tmp_restore_column_map(table->write_set, old_map); DBUG_RETURN(key); } @@ -1267,8 +1257,8 @@ int ha_berkeley::update_row(const byte * old_row, byte * new_row) DB_TXN *sub_trans; bool primary_key_changed; DBUG_ENTER("update_row"); - LINT_INIT(error); + LINT_INIT(error); statistic_increment(table->in_use->status_var.ha_update_count,&LOCK_status); if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE) table->timestamp_field->set_time(); @@ -1849,8 +1839,9 @@ void ha_berkeley::info(uint flag) DBUG_ENTER("ha_berkeley::info"); if (flag & HA_STATUS_VARIABLE) { - records = share->rows + changed_rows; // Just to get optimisations right - deleted = 0; + // Just to get optimizations right + stats.records = share->rows + changed_rows; + stats.deleted = 0; } if ((flag & HA_STATUS_CONST) || version != share->version) { @@ -1871,19 +1862,8 @@ void ha_berkeley::info(uint flag) int ha_berkeley::extra(enum ha_extra_function operation) { switch (operation) { - case HA_EXTRA_RESET: case HA_EXTRA_RESET_STATE: - key_read=0; - using_ignore=0; - if (current_row.flags & (DB_DBT_MALLOC | DB_DBT_REALLOC)) - { - current_row.flags=0; - if (current_row.data) - { - free(current_row.data); - current_row.data=0; - } - } + reset(); break; case HA_EXTRA_KEYREAD: key_read=1; // Query satisfied with key @@ -1906,8 +1886,17 @@ int ha_berkeley::extra(enum ha_extra_function operation) int ha_berkeley::reset(void) { - ha_berkeley::extra(HA_EXTRA_RESET); - key_read=0; // Reset to state after open + key_read= 0; + using_ignore= 0; + if (current_row.flags & (DB_DBT_MALLOC | DB_DBT_REALLOC)) + { + current_row.flags= 0; + if (current_row.data) + { + free(current_row.data); + current_row.data= 0; + } + } return 0; } @@ -2196,7 +2185,7 @@ int ha_berkeley::rename_table(const char * from, const char * to) double ha_berkeley::scan_time() { - return rows2double(records/3); + return rows2double(stats.records/3); } ha_rows ha_berkeley::records_in_range(uint keynr, key_range *start_key, @@ -2249,14 +2238,18 @@ ha_rows ha_berkeley::records_in_range(uint keynr, key_range *start_key, end_pos=end_range.less; else end_pos=end_range.less+end_range.equal; - rows=(end_pos-start_pos)*records; + rows=(end_pos-start_pos)*stats.records; DBUG_PRINT("exit",("rows: %g",rows)); DBUG_RETURN((ha_rows)(rows <= 1.0 ? 1 : rows)); } -ulonglong ha_berkeley::get_auto_increment() +void ha_berkeley::get_auto_increment(ulonglong offset, ulonglong increment, + ulonglong nb_desired_values, + ulonglong *first_value, + ulonglong *nb_reserved_values) { + /* Ideally in case of real error (not "empty table") nr should be ~ULL(0) */ ulonglong nr=1; // Default if error or new key int error; (void) ha_berkeley::extra(HA_EXTRA_KEYREAD); @@ -2267,9 +2260,18 @@ ulonglong ha_berkeley::get_auto_increment() if (!table_share->next_number_key_offset) { // Autoincrement at key-start error=ha_berkeley::index_last(table->record[1]); + /* has taken read lock on page of max key so reserves to infinite */ + *nb_reserved_values= ULONGLONG_MAX; } else { + /* + MySQL needs to call us for next row: assume we are inserting ("a",null) + here, we return 3, and next this statement will want to insert ("b",null): + there is no reason why ("b",3+1) would be the good row to insert: maybe it + already exists, maybe 3+1 is too large... + */ + *nb_reserved_values= 1; DBT row,old_key; bzero((char*) &row,sizeof(row)); KEY *key_info= &table->key_info[active_index]; @@ -2310,7 +2312,7 @@ ulonglong ha_berkeley::get_auto_increment() table->next_number_field->val_int_offset(table_share->rec_buff_length)+1; ha_berkeley::index_end(); (void) ha_berkeley::extra(HA_EXTRA_NO_KEYREAD); - return nr; + *first_value= nr; } void ha_berkeley::print_error(int error, myf errflag) @@ -2732,15 +2734,17 @@ bool ha_berkeley::check_if_incompatible_data(HA_CREATE_INFO *info, return COMPATIBLE_DATA_YES; } +struct st_mysql_storage_engine berkeley_storage_engine= +{ MYSQL_HANDLERTON_INTERFACE_VERSION, &berkeley_hton }; mysql_declare_plugin(berkeley) { MYSQL_STORAGE_ENGINE_PLUGIN, - &berkeley_hton, + &berkeley_storage_engine, berkeley_hton_name, "Sleepycat Software", - berkeley_hton_comment, - NULL, /* Plugin Init */ + "Supports transactions and page-level locking", + berkeley_init, /* Plugin Init */ NULL, /* Plugin Deinit */ 0x0100, /* 1.0 */ 0 diff --git a/sql/ha_berkeley.h b/sql/ha_berkeley.h index 21b618b8d6d..47aab1fbb68 100644 --- a/sql/ha_berkeley.h +++ b/sql/ha_berkeley.h @@ -90,7 +90,7 @@ class ha_berkeley: public handler ulong index_flags(uint idx, uint part, bool all_parts) const; const char *index_type(uint key_number) { return "BTREE"; } const char **bas_ext() const; - ulong table_flags(void) const { return int_table_flags; } + ulonglong table_flags(void) const { return int_table_flags; } uint max_supported_keys() const { return MAX_KEY-1; } uint extra_rec_buf_length() const { return BDB_HIDDEN_PRIMARY_KEY_LENGTH; } ha_rows estimate_rows_upper_bound(); @@ -98,7 +98,6 @@ class ha_berkeley: public handler uint max_supported_key_part_length() const { return UINT_MAX32; } const key_map *keys_to_use_for_scanning() { return &key_map_full; } - bool has_transactions() { return 1;} int open(const char *name, int mode, uint test_if_locked); int close(void); @@ -149,7 +148,10 @@ class ha_berkeley: public handler int5store(to,share->auto_ident); pthread_mutex_unlock(&share->mutex); } - ulonglong get_auto_increment(); + virtual void get_auto_increment(ulonglong offset, ulonglong increment, + ulonglong nb_desired_values, + ulonglong *first_value, + ulonglong *nb_reserved_values); void print_error(int error, myf errflag); uint8 table_cache_type() { return HA_CACHE_TBL_TRANSACT; } bool primary_key_is_clustered() { return true; } @@ -172,7 +174,7 @@ extern char *berkeley_home, *berkeley_tmpdir, *berkeley_logdir; extern long berkeley_lock_scan_time; extern TYPELIB berkeley_lock_typelib; -bool berkeley_init(void); +int berkeley_init(void); int berkeley_end(ha_panic_function type); bool berkeley_flush_logs(void); bool berkeley_show_status(THD *thd, stat_print_fn *print, enum ha_stat_type); diff --git a/sql/ha_federated.cc b/sql/ha_federated.cc index f4fc5f47193..91111a433dc 100644 --- a/sql/ha_federated.cc +++ b/sql/ha_federated.cc @@ -364,57 +364,19 @@ pthread_mutex_t federated_mutex; // To init the hash static int federated_init= FALSE; // Checking the state of hash /* Static declaration for handerton */ -static handler *federated_create_handler(TABLE_SHARE *table); +static handler *federated_create_handler(TABLE_SHARE *table, + MEM_ROOT *mem_root); static int federated_commit(THD *thd, bool all); static int federated_rollback(THD *thd, bool all); /* Federated storage engine handlerton */ -static const char federated_hton_name[]= "FEDERATED"; -static const char federated_hton_comment[]= "Federated MySQL storage engine"; - -handlerton federated_hton= { - MYSQL_HANDLERTON_INTERFACE_VERSION, - federated_hton_name, - SHOW_OPTION_YES, - federated_hton_comment, - DB_TYPE_FEDERATED_DB, - federated_db_init, - 0, /* slot */ - 0, /* savepoint size. */ - NULL, /* close_connection */ - NULL, /* savepoint */ - NULL, /* rollback to savepoint */ - NULL, /* release savepoint */ - federated_commit, /* commit */ - federated_rollback, /* rollback */ - NULL, /* prepare */ - NULL, /* recover */ - NULL, /* commit_by_xid */ - NULL, /* rollback_by_xid */ - NULL, /* create_cursor_read_view */ - NULL, /* set_cursor_read_view */ - NULL, /* close_cursor_read_view */ - federated_create_handler, /* Create a new handler */ - NULL, /* Drop a database */ - federated_db_end, /* Panic call */ - NULL, /* Start Consistent Snapshot */ - NULL, /* Flush logs */ - NULL, /* Show status */ - NULL, /* Partition flags */ - NULL, /* Alter table flags */ - NULL, /* Alter Tablespace */ - NULL, /* Fill FILES table */ - HTON_ALTER_NOT_SUPPORTED, - NULL, /* binlog_func */ - NULL, /* binlog_log_query */ - NULL /* release_temporary_latches */ -}; - - -static handler *federated_create_handler(TABLE_SHARE *table) +handlerton federated_hton; + +static handler *federated_create_handler(TABLE_SHARE *table, + MEM_ROOT *mem_root) { - return new ha_federated(table); + return new (mem_root) ha_federated(table); } @@ -439,9 +401,18 @@ static byte *federated_get_key(FEDERATED_SHARE *share, uint *length, TRUE Error */ -bool federated_db_init() +int federated_db_init() { DBUG_ENTER("federated_db_init"); + + federated_hton.state= SHOW_OPTION_YES; + federated_hton.db_type= DB_TYPE_FEDERATED_DB; + federated_hton.commit= federated_commit; + federated_hton.rollback= federated_rollback; + federated_hton.create= federated_create_handler; + federated_hton.panic= federated_db_end; + federated_hton.flags= HTON_ALTER_NOT_SUPPORTED; + if (pthread_mutex_init(&federated_mutex, MY_MUTEX_INIT_FAST)) goto error; if (!hash_init(&federated_open_tables, system_charset_info, 32, 0, 0, @@ -796,6 +767,7 @@ uint ha_federated::convert_row_to_internal_format(byte *record, MYSQL_ROW row) { ulong *lengths; Field **field; + my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set); DBUG_ENTER("ha_federated::convert_row_to_internal_format"); lengths= mysql_fetch_lengths(stored_result); @@ -814,12 +786,15 @@ uint ha_federated::convert_row_to_internal_format(byte *record, MYSQL_ROW row) (*field)->set_null(); else { - (*field)->set_notnull(); - (*field)->store(*row, *lengths, &my_charset_bin); + if (bitmap_is_set(table->read_set, (*field)->field_index)) + { + (*field)->set_notnull(); + (*field)->store(*row, *lengths, &my_charset_bin); + } } (*field)->move_field_offset(-old_ptr); } - + dbug_tmp_restore_column_map(table->write_set, old_map); DBUG_RETURN(0); } @@ -1138,22 +1113,25 @@ bool ha_federated::create_where_from_key(String *to, KEY *key_info, const key_range *start_key, const key_range *end_key, - bool records_in_range) + bool records_in_range, + bool eq_range) { - bool both_not_null= + bool both_not_null= (start_key != NULL && end_key != NULL) ? TRUE : FALSE; const byte *ptr; uint remainder, length; char tmpbuff[FEDERATED_QUERY_BUFFER_SIZE]; String tmp(tmpbuff, sizeof(tmpbuff), system_charset_info); const key_range *ranges[2]= { start_key, end_key }; + my_bitmap_map *old_map; DBUG_ENTER("ha_federated::create_where_from_key"); tmp.length(0); if (start_key == NULL && end_key == NULL) DBUG_RETURN(1); - for (int i= 0; i <= 1; i++) + old_map= dbug_tmp_use_all_columns(table, table->write_set); + for (uint i= 0; i <= 1; i++) { bool needs_quotes; KEY_PART_INFO *key_part; @@ -1187,16 +1165,16 @@ bool ha_federated::create_where_from_key(String *to, { if (emit_key_part_name(&tmp, key_part) || tmp.append(FEDERATED_ISNULL)) - DBUG_RETURN(1); + goto err; continue; } } if (tmp.append(FEDERATED_OPENPAREN)) - DBUG_RETURN(1); + goto err; - switch(ranges[i]->flag) { - case(HA_READ_KEY_EXACT): + switch (ranges[i]->flag) { + case HA_READ_KEY_EXACT: DBUG_PRINT("info", ("federated HA_READ_KEY_EXACT %d", i)); if (store_length >= length || !needs_quotes || @@ -1204,22 +1182,22 @@ bool ha_federated::create_where_from_key(String *to, field->result_type() != STRING_RESULT) { if (emit_key_part_name(&tmp, key_part)) - DBUG_RETURN(1); + goto err; if (records_in_range) { if (tmp.append(FEDERATED_GE)) - DBUG_RETURN(1); + goto err; } else { if (tmp.append(FEDERATED_EQ)) - DBUG_RETURN(1); + goto err; } if (emit_key_part_element(&tmp, key_part, needs_quotes, 0, ptr, part_length)) - DBUG_RETURN(1); + goto err; } else { @@ -1228,43 +1206,49 @@ bool ha_federated::create_where_from_key(String *to, tmp.append(FEDERATED_LIKE) || emit_key_part_element(&tmp, key_part, needs_quotes, 1, ptr, part_length)) - DBUG_RETURN(1); + goto err; } break; - case(HA_READ_AFTER_KEY): + case HA_READ_AFTER_KEY: + if (eq_range) + { + if (tmp.append("1=1")) // Dummy + goto err; + break; + } DBUG_PRINT("info", ("federated HA_READ_AFTER_KEY %d", i)); if (store_length >= length) /* end key */ { if (emit_key_part_name(&tmp, key_part)) - DBUG_RETURN(1); + goto err; if (i > 0) /* end key */ { if (tmp.append(FEDERATED_LE)) - DBUG_RETURN(1); + goto err; } else /* start key */ { if (tmp.append(FEDERATED_GT)) - DBUG_RETURN(1); + goto err; } if (emit_key_part_element(&tmp, key_part, needs_quotes, 0, ptr, part_length)) { - DBUG_RETURN(1); + goto err; } break; } - case(HA_READ_KEY_OR_NEXT): + case HA_READ_KEY_OR_NEXT: DBUG_PRINT("info", ("federated HA_READ_KEY_OR_NEXT %d", i)); if (emit_key_part_name(&tmp, key_part) || tmp.append(FEDERATED_GE) || emit_key_part_element(&tmp, key_part, needs_quotes, 0, ptr, part_length)) - DBUG_RETURN(1); + goto err; break; - case(HA_READ_BEFORE_KEY): + case HA_READ_BEFORE_KEY: DBUG_PRINT("info", ("federated HA_READ_BEFORE_KEY %d", i)); if (store_length >= length) { @@ -1272,23 +1256,23 @@ bool ha_federated::create_where_from_key(String *to, tmp.append(FEDERATED_LT) || emit_key_part_element(&tmp, key_part, needs_quotes, 0, ptr, part_length)) - DBUG_RETURN(1); + goto err; break; } - case(HA_READ_KEY_OR_PREV): + case HA_READ_KEY_OR_PREV: DBUG_PRINT("info", ("federated HA_READ_KEY_OR_PREV %d", i)); if (emit_key_part_name(&tmp, key_part) || tmp.append(FEDERATED_LE) || emit_key_part_element(&tmp, key_part, needs_quotes, 0, ptr, part_length)) - DBUG_RETURN(1); + goto err; break; default: DBUG_PRINT("info",("cannot handle flag %d", ranges[i]->flag)); - DBUG_RETURN(1); + goto err; } if (tmp.append(FEDERATED_CLOSEPAREN)) - DBUG_RETURN(1); + goto err; next_loop: if (store_length >= length) @@ -1298,13 +1282,15 @@ next_loop: length-= store_length; ptr+= store_length; if (tmp.append(FEDERATED_AND)) - DBUG_RETURN(1); + goto err; DBUG_PRINT("info", ("create_where_from_key WHERE clause: %s", tmp.c_ptr_quick())); } } + dbug_tmp_restore_column_map(table->write_set, old_map); + if (both_not_null) if (tmp.append(FEDERATED_CLOSEPAREN)) DBUG_RETURN(1); @@ -1316,6 +1302,10 @@ next_loop: DBUG_RETURN(1); DBUG_RETURN(0); + +err: + dbug_tmp_restore_column_map(table->write_set, old_map); + DBUG_RETURN(1); } /* @@ -1355,7 +1345,7 @@ static FEDERATED_SHARE *get_share(const char *table_name, TABLE *table) query.append(FEDERATED_BTICK); query.append(FEDERATED_COMMA); } - query.length(query.length()- (FEDERATED_COMMA_LEN - 1)); + query.length(query.length()- FEDERATED_COMMA_LEN); query.append(FEDERATED_FROM); query.append(FEDERATED_BTICK); @@ -1606,15 +1596,16 @@ int ha_federated::write_row(byte *buf) String insert_field_value_string(insert_field_value_buffer, sizeof(insert_field_value_buffer), &my_charset_bin); - values_string.length(0); - insert_string.length(0); - insert_field_value_string.length(0); + my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set); DBUG_ENTER("ha_federated::write_row"); DBUG_PRINT("info", ("table charset name %s csname %s", table->s->table_charset->name, table->s->table_charset->csname)); + values_string.length(0); + insert_string.length(0); + insert_field_value_string.length(0); statistic_increment(table->in_use->status_var.ha_write_count, &LOCK_status); if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT) table->timestamp_field->set_time(); @@ -1641,7 +1632,7 @@ int ha_federated::write_row(byte *buf) for (field= table->field; *field; field++) { /* if there is a query id and if it's equal to the current query id */ - if (ha_get_bit_in_write_set((*field)->fieldnr)) + if (bitmap_is_set(table->write_set, (*field)->field_index)) { /* There are some fields. This will be used later to determine @@ -1666,22 +1657,17 @@ int ha_federated::write_row(byte *buf) /* append commas between both fields and fieldnames */ /* - unfortunately, we can't use the logic - if *(fields + 1) to make the following - appends conditional because we may not append - if the next field doesn't match the condition: - (((*field)->query_id && (*field)->query_id == current_query_id) + unfortunately, we can't use the logic if *(fields + 1) to + make the following appends conditional as we don't know if the + next field is in the write set */ insert_string.append(FEDERATED_COMMA); values_string.append(FEDERATED_COMMA); } } + dbug_tmp_restore_column_map(table->read_set, old_map); /* - remove trailing comma - */ - insert_string.length(insert_string.length() - strlen(FEDERATED_COMMA)); - /* if there were no fields, we don't want to add a closing paren AND, we don't want to chop off the last char '(' insert will be "INSERT INTO t1 VALUES ();" @@ -1689,9 +1675,13 @@ int ha_federated::write_row(byte *buf) if (has_fields) { /* chops off leading commas */ - values_string.length(values_string.length() - strlen(FEDERATED_COMMA)); + insert_string.length(insert_string.length() - FEDERATED_COMMA_LEN); + values_string.length(values_string.length() - FEDERATED_COMMA_LEN); insert_string.append(FEDERATED_CLOSEPAREN); } + else + insert_string.length(insert_string.length() - FEDERATED_CLOSEPAREN_LEN); + /* we always want to append this, even if there aren't any fields */ values_string.append(FEDERATED_CLOSEPAREN); @@ -1705,8 +1695,8 @@ int ha_federated::write_row(byte *buf) DBUG_RETURN(stash_remote_error()); } /* - If the table we've just written a record to contains an auto_increment field, - then store the last_insert_id() value from the foreign server + If the table we've just written a record to contains an auto_increment + field, then store the last_insert_id() value from the foreign server */ if (table->next_number_field) update_auto_increment(); @@ -1728,7 +1718,7 @@ void ha_federated::update_auto_increment(void) DBUG_ENTER("ha_federated::update_auto_increment"); thd->insert_id(mysql->last_used_con->insert_id); - DBUG_PRINT("info",("last_insert_id %d", auto_increment_value)); + DBUG_PRINT("info",("last_insert_id %d", stats.auto_increment_value)); DBUG_VOID_RETURN; } @@ -1816,7 +1806,7 @@ int ha_federated::update_row(const byte *old_data, byte *new_data) this? Because we only are updating one record, and LIMIT enforces this. */ - bool has_a_primary_key= (table->s->primary_key == 0 ? TRUE : FALSE); + bool has_a_primary_key= test(table->s->primary_key != MAX_KEY); /* buffers for following strings */ @@ -1868,48 +1858,52 @@ int ha_federated::update_row(const byte *old_data, byte *new_data) for (Field **field= table->field; *field; field++) { - where_string.append((*field)->field_name); - update_string.append((*field)->field_name); - update_string.append(FEDERATED_EQ); - - if ((*field)->is_null()) - new_field_value.append(FEDERATED_NULL); - else - { - /* otherwise = */ - (*field)->val_str(&new_field_value); - (*field)->quote_data(&new_field_value); - - if (!field_in_record_is_null(table, *field, (char*) old_data)) - where_string.append(FEDERATED_EQ); - } - - if (field_in_record_is_null(table, *field, (char*) old_data)) - where_string.append(FEDERATED_ISNULL); - else + if (bitmap_is_set(table->write_set, (*field)->field_index)) { - (*field)->val_str(&old_field_value, - (char*) (old_data + (*field)->offset())); - (*field)->quote_data(&old_field_value); - where_string.append(old_field_value); + if ((*field)->is_null()) + new_field_value.append(FEDERATED_NULL); + else + { + my_bitmap_map *old_map= tmp_use_all_columns(table, table->read_set); + /* otherwise = */ + (*field)->val_str(&new_field_value); + (*field)->quote_data(&new_field_value); + tmp_restore_column_map(table->read_set, old_map); + } + update_string.append((*field)->field_name); + update_string.append(FEDERATED_EQ); + update_string.append(new_field_value); + update_string.append(FEDERATED_COMMA); + new_field_value.length(0); } - update_string.append(new_field_value); - new_field_value.length(0); - - /* - Only append conjunctions if we have another field in which - to iterate - */ - if (*(field + 1)) + if (bitmap_is_set(table->read_set, (*field)->field_index)) { - update_string.append(FEDERATED_COMMA); + where_string.append((*field)->field_name); + if (field_in_record_is_null(table, *field, (char*) old_data)) + where_string.append(FEDERATED_ISNULL); + else + { + where_string.append(FEDERATED_EQ); + (*field)->val_str(&old_field_value, + (char*) (old_data + (*field)->offset())); + (*field)->quote_data(&old_field_value); + where_string.append(old_field_value); + old_field_value.length(0); + } where_string.append(FEDERATED_AND); } - old_field_value.length(0); } - update_string.append(FEDERATED_WHERE); - update_string.append(where_string); + + /* Remove last ', '. This works as there must be at least on updated field */ + update_string.length(update_string.length() - FEDERATED_COMMA_LEN); + if (where_string.length()) + { + where_string.length(where_string.length() - FEDERATED_AND_LEN); + update_string.append(FEDERATED_WHERE); + update_string.append(where_string); + } + /* If this table has not a primary key, then we could possibly update multiple rows. We want to make sure to only update one! @@ -1943,9 +1937,9 @@ int ha_federated::delete_row(const byte *buf) { char delete_buffer[FEDERATED_QUERY_BUFFER_SIZE]; char data_buffer[FEDERATED_QUERY_BUFFER_SIZE]; - String delete_string(delete_buffer, sizeof(delete_buffer), &my_charset_bin); String data_string(data_buffer, sizeof(data_buffer), &my_charset_bin); + uint found= 0; DBUG_ENTER("ha_federated::delete_row"); delete_string.length(0); @@ -1959,25 +1953,31 @@ int ha_federated::delete_row(const byte *buf) for (Field **field= table->field; *field; field++) { Field *cur_field= *field; - data_string.length(0); - delete_string.append(cur_field->field_name); - - if (cur_field->is_null()) - { - delete_string.append(FEDERATED_IS); - data_string.append(FEDERATED_NULL); - } - else + found++; + if (bitmap_is_set(table->read_set, cur_field->field_index)) { - delete_string.append(FEDERATED_EQ); - cur_field->val_str(&data_string); - cur_field->quote_data(&data_string); + data_string.length(0); + delete_string.append(cur_field->field_name); + if (cur_field->is_null()) + { + delete_string.append(FEDERATED_IS); + delete_string.append(FEDERATED_NULL); + } + else + { + delete_string.append(FEDERATED_EQ); + cur_field->val_str(&data_string); + cur_field->quote_data(&data_string); + delete_string.append(data_string); + } + delete_string.append(FEDERATED_AND); } - - delete_string.append(data_string); - delete_string.append(FEDERATED_AND); } - delete_string.length(delete_string.length()-5); // Remove trailing AND + + // Remove trailing AND + delete_string.length(delete_string.length() - FEDERATED_AND_LEN); + if (!found) + delete_string.length(delete_string.length() - FEDERATED_WHERE_LEN); delete_string.append(FEDERATED_LIMIT1); DBUG_PRINT("info", @@ -1986,10 +1986,10 @@ int ha_federated::delete_row(const byte *buf) { DBUG_RETURN(stash_remote_error()); } - deleted+= mysql->affected_rows; + stats.deleted+= mysql->affected_rows; DBUG_PRINT("info", ("rows deleted %d rows deleted for all time %d", - int(mysql->affected_rows), deleted)); + int(mysql->affected_rows), stats.deleted)); DBUG_RETURN(0); } @@ -2050,7 +2050,7 @@ int ha_federated::index_read_idx(byte *buf, uint index, const byte *key, create_where_from_key(&index_string, &table->key_info[index], &range, - NULL, 0); + NULL, 0, 0); sql_query.append(index_string); DBUG_PRINT("info", @@ -2112,15 +2112,10 @@ int ha_federated::index_init(uint keynr, bool sorted) DBUG_RETURN(0); } -/* - int read_range_first(const key_range *start_key, - const key_range *end_key, - bool eq_range, bool sorted); -*/ int ha_federated::read_range_first(const key_range *start_key, - const key_range *end_key, - bool eq_range, bool sorted) + const key_range *end_key, + bool eq_range, bool sorted) { char sql_query_buffer[FEDERATED_QUERY_BUFFER_SIZE]; int retval; @@ -2136,7 +2131,7 @@ int ha_federated::read_range_first(const key_range *start_key, sql_query.append(share->select_query); create_where_from_key(&sql_query, &table->key_info[active_index], - start_key, end_key, 0); + start_key, end_key, 0, eq_range); if (mysql_real_query(mysql, sql_query.ptr(), sql_query.length())) { @@ -2481,18 +2476,20 @@ void ha_federated::info(uint flag) delete_length = ? */ if (row[4] != NULL) - records= (ha_rows) my_strtoll10(row[4], (char**) 0, &error); + stats.records= (ha_rows) my_strtoll10(row[4], (char**) 0, + &error); if (row[5] != NULL) - mean_rec_length= (ha_rows) my_strtoll10(row[5], (char**) 0, &error); + stats.mean_rec_length= (ha_rows) my_strtoll10(row[5], (char**) 0, + &error); if (row[12] != NULL) - update_time= (ha_rows) my_strtoll10(row[12], (char**) 0, &error); + stats.update_time= (ha_rows) my_strtoll10(row[12], (char**) 0, + &error); if (row[13] != NULL) - check_time= (ha_rows) my_strtoll10(row[13], (char**) 0, &error); + stats.check_time= (ha_rows) my_strtoll10(row[13], (char**) 0, + &error); } if (flag & HA_STATUS_CONST) - { - block_size= 4096; - } + stats.block_size= 4096; } if (result) @@ -2543,8 +2540,8 @@ int ha_federated::delete_all_rows() { DBUG_RETURN(stash_remote_error()); } - deleted+= records; - records= 0; + stats.deleted+= stats.records; + stats.records= 0; DBUG_RETURN(0); } @@ -2797,7 +2794,7 @@ int ha_federated::connection_autocommit(bool state) DBUG_ENTER("ha_federated::connection_autocommit"); text= (state == true) ? "SET AUTOCOMMIT=1" : "SET AUTOCOMMIT=0"; DBUG_RETURN(execute_simple_query(text, 16)); -} +} int ha_federated::execute_simple_query(const char *query, int len) @@ -2811,17 +2808,20 @@ int ha_federated::execute_simple_query(const char *query, int len) DBUG_RETURN(0); } +struct st_mysql_storage_engine federated_storage_engine= +{ MYSQL_HANDLERTON_INTERFACE_VERSION, &federated_hton }; mysql_declare_plugin(federated) { MYSQL_STORAGE_ENGINE_PLUGIN, - &federated_hton, - federated_hton_name, + &federated_storage_engine, + "FEDERATED", "Patrick Galbraith and Brian Aker, MySQL AB", - federated_hton_comment, - NULL, /* Plugin Init */ + "Federated MySQL storage engine", + federated_db_init, /* Plugin Init */ NULL, /* Plugin Deinit */ 0x0100 /* 1.0 */, + 0 } mysql_declare_plugin_end; diff --git a/sql/ha_federated.h b/sql/ha_federated.h index 458ef42ebda..4a4561ba274 100644 --- a/sql/ha_federated.h +++ b/sql/ha_federated.h @@ -39,83 +39,83 @@ #define FEDERATED_RECORDS_IN_RANGE 2 #define FEDERATED_INFO " SHOW TABLE STATUS LIKE " -#define FEDERATED_INFO_LEN sizeof(FEDERATED_INFO) +#define FEDERATED_INFO_LEN (sizeof(FEDERATED_INFO) -1) #define FEDERATED_SELECT "SELECT " -#define FEDERATED_SELECT_LEN sizeof(FEDERATED_SELECT) +#define FEDERATED_SELECT_LEN (sizeof(FEDERATED_SELECT) -1) #define FEDERATED_WHERE " WHERE " -#define FEDERATED_WHERE_LEN sizeof(FEDERATED_WHERE) +#define FEDERATED_WHERE_LEN (sizeof(FEDERATED_WHERE) -1) #define FEDERATED_FROM " FROM " -#define FEDERATED_FROM_LEN sizeof(FEDERATED_FROM) +#define FEDERATED_FROM_LEN (sizeof(FEDERATED_FROM) -1) #define FEDERATED_PERCENT "%" -#define FEDERATED_PERCENT_LEN sizeof(FEDERATED_PERCENT) +#define FEDERATED_PERCENT_LEN (sizeof(FEDERATED_PERCENT) -1) #define FEDERATED_IS " IS " -#define FEDERATED_IS_LEN sizeof(FEDERATED_IS) +#define FEDERATED_IS_LEN (sizeof(FEDERATED_IS) -1) #define FEDERATED_NULL " NULL " -#define FEDERATED_NULL_LEN sizeof(FEDERATED_NULL) +#define FEDERATED_NULL_LEN (sizeof(FEDERATED_NULL) -1) #define FEDERATED_ISNULL " IS NULL " -#define FEDERATED_ISNULL_LEN sizeof(FEDERATED_ISNULL) +#define FEDERATED_ISNULL_LEN (sizeof(FEDERATED_ISNULL) -1) #define FEDERATED_LIKE " LIKE " -#define FEDERATED_LIKE_LEN sizeof(FEDERATED_LIKE) +#define FEDERATED_LIKE_LEN (sizeof(FEDERATED_LIKE) -1) #define FEDERATED_TRUNCATE "TRUNCATE " -#define FEDERATED_TRUNCATE_LEN sizeof(FEDERATED_TRUNCATE) +#define FEDERATED_TRUNCATE_LEN (sizeof(FEDERATED_TRUNCATE) -1) #define FEDERATED_DELETE "DELETE " -#define FEDERATED_DELETE_LEN sizeof(FEDERATED_DELETE) +#define FEDERATED_DELETE_LEN (sizeof(FEDERATED_DELETE) -1) #define FEDERATED_INSERT "INSERT INTO " -#define FEDERATED_INSERT_LEN sizeof(FEDERATED_INSERT) +#define FEDERATED_INSERT_LEN (sizeof(FEDERATED_INSERT) -1) #define FEDERATED_OPTIMIZE "OPTIMIZE TABLE " -#define FEDERATED_OPTIMIZE_LEN sizeof(FEDERATED_OPTIMIZE) +#define FEDERATED_OPTIMIZE_LEN (sizeof(FEDERATED_OPTIMIZE) -1) #define FEDERATED_REPAIR "REPAIR TABLE " -#define FEDERATED_REPAIR_LEN sizeof(FEDERATED_REPAIR) +#define FEDERATED_REPAIR_LEN (sizeof(FEDERATED_REPAIR) -1) #define FEDERATED_QUICK " QUICK" -#define FEDERATED_QUICK_LEN sizeof(FEDERATED_QUICK) +#define FEDERATED_QUICK_LEN (sizeof(FEDERATED_QUICK) -1) #define FEDERATED_EXTENDED " EXTENDED" -#define FEDERATED_EXTENDED_LEN sizeof(FEDERATED_EXTENDED) +#define FEDERATED_EXTENDED_LEN (sizeof(FEDERATED_EXTENDED) -1) #define FEDERATED_USE_FRM " USE_FRM" -#define FEDERATED_USE_FRM_LEN sizeof(FEDERATED_USE_FRM) +#define FEDERATED_USE_FRM_LEN (sizeof(FEDERATED_USE_FRM) -1) #define FEDERATED_LIMIT1 " LIMIT 1" -#define FEDERATED_LIMIT1_LEN sizeof(FEDERATED_LIMIT1) +#define FEDERATED_LIMIT1_LEN (sizeof(FEDERATED_LIMIT1) -1) #define FEDERATED_VALUES "VALUES " -#define FEDERATED_VALUES_LEN sizeof(FEDERATED_VALUES) +#define FEDERATED_VALUES_LEN (sizeof(FEDERATED_VALUES) -1) #define FEDERATED_UPDATE "UPDATE " -#define FEDERATED_UPDATE_LEN sizeof(FEDERATED_UPDATE) -#define FEDERATED_SET "SET " -#define FEDERATED_SET_LEN sizeof(FEDERATED_SET) +#define FEDERATED_UPDATE_LEN (sizeof(FEDERATED_UPDATE) -1) +#define FEDERATED_SET " SET " +#define FEDERATED_SET_LEN (sizeof(FEDERATED_SET) -1) #define FEDERATED_AND " AND " -#define FEDERATED_AND_LEN sizeof(FEDERATED_AND) +#define FEDERATED_AND_LEN (sizeof(FEDERATED_AND) -1) #define FEDERATED_CONJUNCTION ") AND (" -#define FEDERATED_CONJUNCTION_LEN sizeof(FEDERATED_CONJUNCTION) +#define FEDERATED_CONJUNCTION_LEN (sizeof(FEDERATED_CONJUNCTION) -1) #define FEDERATED_OR " OR " -#define FEDERATED_OR_LEN sizeof(FEDERATED_OR) +#define FEDERATED_OR_LEN (sizeof(FEDERATED_OR) -1) #define FEDERATED_NOT " NOT " -#define FEDERATED_NOT_LEN sizeof(FEDERATED_NOT) +#define FEDERATED_NOT_LEN (sizeof(FEDERATED_NOT) -1) #define FEDERATED_STAR "* " -#define FEDERATED_STAR_LEN sizeof(FEDERATED_STAR) +#define FEDERATED_STAR_LEN (sizeof(FEDERATED_STAR) -1) #define FEDERATED_SPACE " " -#define FEDERATED_SPACE_LEN sizeof(FEDERATED_SPACE) +#define FEDERATED_SPACE_LEN (sizeof(FEDERATED_SPACE) -1) #define FEDERATED_SQUOTE "'" -#define FEDERATED_SQUOTE_LEN sizeof(FEDERATED_SQUOTE) +#define FEDERATED_SQUOTE_LEN (sizeof(FEDERATED_SQUOTE) -1) #define FEDERATED_COMMA ", " -#define FEDERATED_COMMA_LEN sizeof(FEDERATED_COMMA) +#define FEDERATED_COMMA_LEN (sizeof(FEDERATED_COMMA) -1) #define FEDERATED_BTICK "`" -#define FEDERATED_BTICK_LEN sizeof(FEDERATED_BTICK) +#define FEDERATED_BTICK_LEN (sizeof(FEDERATED_BTICK) -1) #define FEDERATED_OPENPAREN " (" -#define FEDERATED_OPENPAREN_LEN sizeof(FEDERATED_OPENPAREN) +#define FEDERATED_OPENPAREN_LEN (sizeof(FEDERATED_OPENPAREN) -1) #define FEDERATED_CLOSEPAREN ") " -#define FEDERATED_CLOSEPAREN_LEN sizeof(FEDERATED_CLOSEPAREN) +#define FEDERATED_CLOSEPAREN_LEN (sizeof(FEDERATED_CLOSEPAREN) -1) #define FEDERATED_NE " != " -#define FEDERATED_NE_LEN sizeof(FEDERATED_NE) +#define FEDERATED_NE_LEN (sizeof(FEDERATED_NE) -1) #define FEDERATED_GT " > " -#define FEDERATED_GT_LEN sizeof(FEDERATED_GT) +#define FEDERATED_GT_LEN (sizeof(FEDERATED_GT) -1) #define FEDERATED_LT " < " -#define FEDERATED_LT_LEN sizeof(FEDERATED_LT) +#define FEDERATED_LT_LEN (sizeof(FEDERATED_LT) -1) #define FEDERATED_LE " <= " -#define FEDERATED_LE_LEN sizeof(FEDERATED_LE) +#define FEDERATED_LE_LEN (sizeof(FEDERATED_LE) -1) #define FEDERATED_GE " >= " -#define FEDERATED_GE_LEN sizeof(FEDERATED_GE) +#define FEDERATED_GE_LEN (sizeof(FEDERATED_GE) -1) #define FEDERATED_EQ " = " -#define FEDERATED_EQ_LEN sizeof(FEDERATED_EQ) +#define FEDERATED_EQ_LEN (sizeof(FEDERATED_EQ) -1) #define FEDERATED_FALSE " 1=0" -#define FEDERATED_FALSE_LEN sizeof(FEDERATED_FALSE) +#define FEDERATED_FALSE_LEN (sizeof(FEDERATED_FALSE) -1) /* FEDERATED_SHARE is a structure that will be shared amoung all open handlers @@ -168,7 +168,7 @@ private: bool create_where_from_key(String *to, KEY *key_info, const key_range *start_key, const key_range *end_key, - bool records_in_range); + bool records_in_range, bool eq_range); int stash_remote_error(); public: @@ -192,12 +192,12 @@ public: implements. The current table flags are documented in handler.h */ - ulong table_flags() const + ulonglong table_flags() const { /* fix server to be able to get remote server table flags */ - return (HA_NOT_EXACT_COUNT | - HA_PRIMARY_KEY_IN_READ_INDEX | HA_FILE_BASED | HA_REC_NOT_IN_SEQ | - HA_AUTO_PART_KEY | HA_CAN_INDEX_BLOBS| HA_NO_PREFIX_CHAR_KEYS); + return (HA_PRIMARY_KEY_IN_READ_INDEX | HA_FILE_BASED | HA_REC_NOT_IN_SEQ | + HA_AUTO_PART_KEY | HA_CAN_INDEX_BLOBS| HA_NO_PREFIX_CHAR_KEYS | + HA_PRIMARY_KEY_REQUIRED_FOR_DELETE | HA_PARTIAL_COLUMN_READ); } /* This is a bitmap of flags that says how the storage engine @@ -231,8 +231,8 @@ public: */ double scan_time() { - DBUG_PRINT("info", ("records %lu", (ulong) records)); - return (double)(records*1000); + DBUG_PRINT("info", ("records %lu", (ulong) stats.records)); + return (double)(stats.records*1000); } /* The next method will never be called if you do not implement indexes. @@ -302,11 +302,10 @@ public: int external_lock(THD *thd, int lock_type); int connection_commit(); int connection_rollback(); - bool has_transactions() { return 1; } int connection_autocommit(bool state); int execute_simple_query(const char *query, int len); }; -bool federated_db_init(void); +int federated_db_init(void); int federated_db_end(ha_panic_function type); diff --git a/sql/ha_heap.cc b/sql/ha_heap.cc index 1a7efb42748..3a2027afaba 100644 --- a/sql/ha_heap.cc +++ b/sql/ha_heap.cc @@ -24,53 +24,23 @@ #include "ha_heap.h" -static handler *heap_create_handler(TABLE_SHARE *table); - -static const char heap_hton_name[]= "MEMORY"; -static const char heap_hton_comment[]= - "Hash based, stored in memory, useful for temporary tables"; - -handlerton heap_hton= { - MYSQL_HANDLERTON_INTERFACE_VERSION, - heap_hton_name, - SHOW_OPTION_YES, - heap_hton_comment, - DB_TYPE_HEAP, - NULL, - 0, /* slot */ - 0, /* savepoint size. */ - NULL, /* close_connection */ - NULL, /* savepoint */ - NULL, /* rollback to savepoint */ - NULL, /* release savepoint */ - NULL, /* commit */ - NULL, /* rollback */ - NULL, /* prepare */ - NULL, /* recover */ - NULL, /* commit_by_xid */ - NULL, /* rollback_by_xid */ - NULL, /* create_cursor_read_view */ - NULL, /* set_cursor_read_view */ - NULL, /* close_cursor_read_view */ - heap_create_handler, /* Create a new handler */ - NULL, /* Drop a database */ - heap_panic, /* Panic call */ - NULL, /* Start Consistent Snapshot */ - NULL, /* Flush logs */ - NULL, /* Show status */ - NULL, /* Partition flags */ - NULL, /* Alter table flags */ - NULL, /* Alter Tablespace */ - NULL, /* Fill Files Table */ - HTON_CAN_RECREATE, - NULL, /* binlog_func */ - NULL, /* binlog_log_query */ - NULL /* release_temporary_latches */ -}; +static handler *heap_create_handler(TABLE_SHARE *table, MEM_ROOT *mem_root); + +handlerton heap_hton; + +int heap_init() +{ + heap_hton.state= SHOW_OPTION_YES; + heap_hton.db_type= DB_TYPE_HEAP; + heap_hton.create= heap_create_handler; + heap_hton.panic= heap_panic; + heap_hton.flags= HTON_CAN_RECREATE; + return 0; +} -static handler *heap_create_handler(TABLE_SHARE *table) +static handler *heap_create_handler(TABLE_SHARE *table, MEM_ROOT *mem_root) { - return new ha_heap(table); + return new (mem_root) ha_heap(table); } @@ -361,16 +331,16 @@ void ha_heap::info(uint flag) HEAPINFO info; (void) heap_info(file,&info,flag); - records = info.records; - deleted = info.deleted; - errkey = info.errkey; - mean_rec_length=info.reclength; - data_file_length=info.data_length; - index_file_length=info.index_length; - max_data_file_length= info.max_records* info.reclength; - delete_length= info.deleted * info.reclength; + errkey= info.errkey; + stats.records = info.records; + stats.deleted = info.deleted; + stats.mean_rec_length=info.reclength; + stats.data_file_length=info.data_length; + stats.index_file_length=info.index_length; + stats.max_data_file_length= info.max_records* info.reclength; + stats.delete_length= info.deleted * info.reclength; if (flag & HA_STATUS_AUTO) - auto_increment_value= info.auto_increment; + stats.auto_increment_value= info.auto_increment; /* If info() is called for the first time after open(), we will still have to update the key statistics. Hoping that a table lock is now @@ -380,11 +350,19 @@ void ha_heap::info(uint flag) update_key_stats(); } + int ha_heap::extra(enum ha_extra_function operation) { return heap_extra(file,operation); } + +int ha_heap::reset() +{ + return heap_reset(file); +} + + int ha_heap::delete_all_rows() { heap_clear(file); @@ -561,8 +539,8 @@ ha_rows ha_heap::records_in_range(uint inx, key_range *min_key, max_key->flag != HA_READ_AFTER_KEY) return HA_POS_ERROR; // Can only use exact keys - if (records <= 1) - return records; + if (stats.records <= 1) + return stats.records; /* Assert that info() did run. We need current statistics here. */ DBUG_ASSERT(key_stat_version == file->s->key_stat_version); @@ -690,13 +668,18 @@ void ha_heap::update_create_info(HA_CREATE_INFO *create_info) { table->file->info(HA_STATUS_AUTO); if (!(create_info->used_fields & HA_CREATE_USED_AUTO)) - create_info->auto_increment_value= auto_increment_value; + create_info->auto_increment_value= stats.auto_increment_value; } -ulonglong ha_heap::get_auto_increment() +void ha_heap::get_auto_increment(ulonglong offset, ulonglong increment, + ulonglong nb_desired_values, + ulonglong *first_value, + ulonglong *nb_reserved_values) { ha_heap::info(HA_STATUS_AUTO); - return auto_increment_value; + *first_value= stats.auto_increment_value; + /* such table has only table-level locking so reserves up to +inf */ + *nb_reserved_values= ULONGLONG_MAX; } @@ -711,14 +694,17 @@ bool ha_heap::check_if_incompatible_data(HA_CREATE_INFO *info, return COMPATIBLE_DATA_YES; } +struct st_mysql_storage_engine heap_storage_engine= +{ MYSQL_HANDLERTON_INTERFACE_VERSION, &heap_hton}; + mysql_declare_plugin(heap) { MYSQL_STORAGE_ENGINE_PLUGIN, - &heap_hton, - heap_hton_name, + &heap_storage_engine, + "MEMORY", "MySQL AB", - heap_hton_comment, - NULL, + "Hash based, stored in memory, useful for temporary tables", + heap_init, NULL, 0x0100, /* 1.0 */ 0 diff --git a/sql/ha_heap.h b/sql/ha_heap.h index 9b9b7f90d90..00e59856f26 100644 --- a/sql/ha_heap.h +++ b/sql/ha_heap.h @@ -46,11 +46,11 @@ public: /* Rows also use a fixed-size format */ enum row_type get_row_type() const { return ROW_TYPE_FIXED; } const char **bas_ext() const; - ulong table_flags() const + ulonglong table_flags() const { return (HA_FAST_KEY_READ | HA_NO_BLOBS | HA_NULL_IN_KEY | - HA_REC_NOT_IN_SEQ | HA_READ_RND_SAME | - HA_CAN_INSERT_DELAYED); + HA_REC_NOT_IN_SEQ | HA_CAN_INSERT_DELAYED | HA_NO_TRANSACTIONS | + HA_HAS_RECORDS | HA_STATS_RECORDS_IS_EXACT); } ulong index_flags(uint inx, uint part, bool all_parts) const { @@ -61,7 +61,8 @@ public: const key_map *keys_to_use_for_scanning() { return &btree_keys; } uint max_supported_keys() const { return MAX_KEY; } uint max_supported_key_part_length() const { return MAX_KEY_LENGTH; } - double scan_time() { return (double) (records+deleted) / 20.0+10; } + double scan_time() + { return (double) (stats.records+stats.deleted) / 20.0+10; } double read_time(uint index, uint ranges, ha_rows rows) { return (double) rows / 20.0+1; } @@ -71,7 +72,10 @@ public: int write_row(byte * buf); int update_row(const byte * old_data, byte * new_data); int delete_row(const byte * buf); - ulonglong get_auto_increment(); + virtual void get_auto_increment(ulonglong offset, ulonglong increment, + ulonglong nb_desired_values, + ulonglong *first_value, + ulonglong *nb_reserved_values); int index_read(byte * buf, const byte * key, uint key_len, enum ha_rkey_function find_flag); int index_read_idx(byte * buf, uint idx, const byte * key, @@ -87,6 +91,7 @@ public: void position(const byte *record); void info(uint); int extra(enum ha_extra_function operation); + int reset(); int external_lock(THD *thd, int lock_type); int delete_all_rows(void); int disable_indexes(uint mode); diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc index 0b2f561e8c9..742f9ce7631 100644 --- a/sql/ha_innodb.cc +++ b/sql/ha_innodb.cc @@ -43,6 +43,7 @@ have disables the InnoDB inlining in this file. */ #define MAX_ULONG_BIT ((ulong) 1 << (sizeof(ulong)*8-1)) #ifdef WITH_INNOBASE_STORAGE_ENGINE + #include "ha_innodb.h" pthread_mutex_t innobase_share_mutex, /* to protect innobase_open_files */ @@ -203,54 +204,15 @@ static int innobase_rollback(THD* thd, bool all); static int innobase_rollback_to_savepoint(THD* thd, void *savepoint); static int innobase_savepoint(THD* thd, void *savepoint); static int innobase_release_savepoint(THD* thd, void *savepoint); -static handler *innobase_create_handler(TABLE_SHARE *table); +static handler *innobase_create_handler(TABLE_SHARE *table, + MEM_ROOT *mem_root); static const char innobase_hton_name[]= "InnoDB"; -static const char innobase_hton_comment[]= - "Supports transactions, row-level locking, and foreign keys"; - -handlerton innobase_hton = { - MYSQL_HANDLERTON_INTERFACE_VERSION, - innobase_hton_name, - SHOW_OPTION_YES, - innobase_hton_comment, - DB_TYPE_INNODB, - innobase_init, - 0, /* slot */ - sizeof(trx_named_savept_t), /* savepoint size. TODO: use it */ - innobase_close_connection, - innobase_savepoint, - innobase_rollback_to_savepoint, - innobase_release_savepoint, - innobase_commit, /* commit */ - innobase_rollback, /* rollback */ - innobase_xa_prepare, /* prepare */ - innobase_xa_recover, /* recover */ - innobase_commit_by_xid, /* commit_by_xid */ - innobase_rollback_by_xid, /* rollback_by_xid */ - innobase_create_cursor_view, - innobase_set_cursor_view, - innobase_close_cursor_view, - innobase_create_handler, /* Create a new handler */ - innobase_drop_database, /* Drop a database */ - innobase_end, /* Panic call */ - innobase_start_trx_and_assign_read_view, /* Start Consistent Snapshot */ - innobase_flush_logs, /* Flush logs */ - innobase_show_status, /* Show status */ - NULL, /* Partition flags */ - NULL, /* Alter table flags */ - NULL, /* alter_tablespace */ - NULL, /* Fill FILES table */ - HTON_NO_FLAGS, - NULL, /* binlog_func */ - NULL, /* binlog_log_query */ - innobase_release_temporary_latches -}; +handlerton innobase_hton; - -static handler *innobase_create_handler(TABLE_SHARE *table) +static handler *innobase_create_handler(TABLE_SHARE *table, MEM_ROOT *mem_root) { - return new ha_innobase(table); + return new (mem_root) ha_innobase(table); } @@ -843,10 +805,9 @@ ha_innobase::ha_innobase(TABLE_SHARE *table_arg) HA_NULL_IN_KEY | HA_CAN_INDEX_BLOBS | HA_CAN_SQL_HANDLER | - HA_NOT_EXACT_COUNT | - HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS | + HA_PRIMARY_KEY_REQUIRED_FOR_POSITION | HA_PRIMARY_KEY_IN_READ_INDEX | - HA_CAN_GEOMETRY | + HA_CAN_GEOMETRY | HA_PARTIAL_COLUMN_READ | HA_TABLE_SCAN_ON_INDEX), start_of_scan(0), num_write_row(0) @@ -1230,10 +1191,9 @@ ha_innobase::init_table_handle_for_HANDLER(void) /************************************************************************* Opens an InnoDB database. */ -bool +int innobase_init(void) /*===============*/ - /* out: &innobase_hton, or NULL on error */ { static char current_dir[3]; /* Set if using current lib */ int err; @@ -1242,8 +1202,33 @@ innobase_init(void) DBUG_ENTER("innobase_init"); + innobase_hton.state=have_innodb; + innobase_hton.db_type= DB_TYPE_INNODB; + innobase_hton.savepoint_offset=sizeof(trx_named_savept_t); + innobase_hton.close_connection=innobase_close_connection; + innobase_hton.savepoint_set=innobase_savepoint; + innobase_hton.savepoint_rollback=innobase_rollback_to_savepoint; + innobase_hton.savepoint_release=innobase_release_savepoint; + innobase_hton.commit=innobase_commit; + innobase_hton.rollback=innobase_rollback; + innobase_hton.prepare=innobase_xa_prepare; + innobase_hton.recover=innobase_xa_recover; + innobase_hton.commit_by_xid=innobase_commit_by_xid; + innobase_hton.rollback_by_xid=innobase_rollback_by_xid; + innobase_hton.create_cursor_read_view=innobase_create_cursor_view; + innobase_hton.set_cursor_read_view=innobase_set_cursor_view; + innobase_hton.close_cursor_read_view=innobase_close_cursor_view; + innobase_hton.create=innobase_create_handler; + innobase_hton.drop_database=innobase_drop_database; + innobase_hton.panic=innobase_end; + innobase_hton.start_consistent_snapshot=innobase_start_trx_and_assign_read_view; + innobase_hton.flush_logs=innobase_flush_logs; + innobase_hton.show_status=innobase_show_status; + innobase_hton.flags=HTON_NO_FLAGS; + innobase_hton.release_temporary_latches=innobase_release_temporary_latches; + if (have_innodb != SHOW_OPTION_YES) - goto error; + DBUG_RETURN(0); // nothing else to do ut_a(DATA_MYSQL_TRUE_VARCHAR == (ulint)MYSQL_TYPE_VARCHAR); @@ -2322,7 +2307,7 @@ ha_innobase::open( } } - block_size = 16 * 1024; /* Index block size in InnoDB: used by MySQL + stats.block_size = 16 * 1024; /* Index block size in InnoDB: used by MySQL in query optimization */ /* Init table lock structure */ @@ -2917,16 +2902,15 @@ ha_innobase::store_key_val_for_row( /****************************************************************** Builds a 'template' to the prebuilt struct. The template is used in fast retrieval of just those column values MySQL needs in its processing. */ -static void -build_template( +ha_innobase::build_template( /*===========*/ row_prebuilt_t* prebuilt, /* in: prebuilt struct */ THD* thd, /* in: current user thread, used only if templ_type is ROW_MYSQL_REC_FIELDS */ TABLE* table, /* in: MySQL table */ - ulint templ_type) /* in: ROW_MYSQL_WHOLE_ROW or + uint templ_type) /* in: ROW_MYSQL_WHOLE_ROW or ROW_MYSQL_REC_FIELDS */ { dict_index_t* index; @@ -3035,8 +3019,8 @@ build_template( goto include_field; } - if (table->file->ha_get_bit_in_read_set(i+1) || - table->file->ha_get_bit_in_write_set(i+1)) { + if (bitmap_is_set(table->read_set, i) || + bitmap_is_set(table->write_set, i)) { /* This field is needed in the query */ goto include_field; @@ -5420,7 +5404,7 @@ ha_innobase::info( nor the CHECK TABLE time, nor the UPDATE or INSERT time. */ if (os_file_get_status(path,&stat_info)) { - create_time = stat_info.ctime; + stats.create_time = stat_info.ctime; } } @@ -5448,21 +5432,21 @@ ha_innobase::info( n_rows++; } - records = (ha_rows)n_rows; - deleted = 0; - data_file_length = ((ulonglong) + stats.records = (ha_rows)n_rows; + stats.deleted = 0; + stats.data_file_length = ((ulonglong) ib_table->stat_clustered_index_size) * UNIV_PAGE_SIZE; - index_file_length = ((ulonglong) + stats.index_file_length = ((ulonglong) ib_table->stat_sum_of_other_index_sizes) * UNIV_PAGE_SIZE; - delete_length = 0; - check_time = 0; + stats.delete_length = 0; + stats.check_time = 0; - if (records == 0) { - mean_rec_length = 0; + if (stats.records == 0) { + stats.mean_rec_length = 0; } else { - mean_rec_length = (ulong) (data_file_length / records); + stats.mean_rec_length = (ulong) (stats.data_file_length / stats.records); } } @@ -5511,9 +5495,9 @@ ha_innobase::info( if (index->stat_n_diff_key_vals[j + 1] == 0) { - rec_per_key = records; + rec_per_key = stats.records; } else { - rec_per_key = (ha_rows)(records / + rec_per_key = (ha_rows)(stats.records / index->stat_n_diff_key_vals[j + 1]); } @@ -5568,7 +5552,7 @@ ha_innobase::info( } } - auto_increment_value = auto_inc; + stats.auto_increment_value = auto_inc; } prebuilt->trx->op_info = (char*)""; @@ -5963,8 +5947,7 @@ ha_innobase::extra( /*===============*/ /* out: 0 or error number */ enum ha_extra_function operation) - /* in: HA_EXTRA_RETRIEVE_ALL_COLS or some - other flag */ + /* in: HA_EXTRA_FLUSH or some other flag */ { row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt; @@ -5978,13 +5961,6 @@ ha_innobase::extra( row_mysql_prebuilt_free_blob_heap(prebuilt); } break; - case HA_EXTRA_RESET: - if (prebuilt->blob_heap) { - row_mysql_prebuilt_free_blob_heap(prebuilt); - } - prebuilt->keep_other_fields_on_keyread = 0; - prebuilt->read_just_key = 0; - break; case HA_EXTRA_RESET_STATE: prebuilt->keep_other_fields_on_keyread = 0; prebuilt->read_just_key = 0; @@ -5992,16 +5968,6 @@ ha_innobase::extra( case HA_EXTRA_NO_KEYREAD: prebuilt->read_just_key = 0; break; - case HA_EXTRA_RETRIEVE_ALL_COLS: - prebuilt->hint_need_to_fetch_extra_cols - = ROW_RETRIEVE_ALL_COLS; - break; - case HA_EXTRA_RETRIEVE_PRIMARY_KEY: - if (prebuilt->hint_need_to_fetch_extra_cols == 0) { - prebuilt->hint_need_to_fetch_extra_cols - = ROW_RETRIEVE_PRIMARY_KEY; - } - break; case HA_EXTRA_KEYREAD: prebuilt->read_just_key = 1; break; @@ -6015,6 +5981,18 @@ ha_innobase::extra( return(0); } +int ha_innobase::reset() +{ + row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt; + if (prebuilt->blob_heap) { + row_mysql_prebuilt_free_blob_heap(prebuilt); + } + prebuilt->keep_other_fields_on_keyread = 0; + prebuilt->read_just_key = 0; + return 0; +} + + /********************************************************************** MySQL calls this function at the start of each SQL statement inside LOCK TABLES. Inside LOCK TABLES the ::external_lock method does not work to @@ -6473,7 +6451,7 @@ innodb_show_status( bool result = FALSE; - if (stat_print(thd, innobase_hton.name, strlen(innobase_hton.name), + if (stat_print(thd, innobase_hton_name, strlen(innobase_hton_name), STRING_WITH_LEN(""), str, flen)) { result= TRUE; } @@ -6500,7 +6478,7 @@ innodb_mutex_show_status( ulint rw_lock_count_os_wait= 0; ulint rw_lock_count_os_yield= 0; ulonglong rw_lock_wait_time= 0; - uint hton_name_len= strlen(innobase_hton.name), buf1len, buf2len; + uint hton_name_len= strlen(innobase_hton_name), buf1len, buf2len; DBUG_ENTER("innodb_mutex_show_status"); #ifdef MUTEX_PROTECT_TO_BE_ADDED_LATER @@ -6527,7 +6505,7 @@ innodb_mutex_show_status( mutex->count_os_yield, mutex->lspent_time/1000); - if (stat_print(thd, innobase_hton.name, + if (stat_print(thd, innobase_hton_name, hton_name_len, buf1, buf1len, buf2, buf2len)) { #ifdef MUTEX_PROTECT_TO_BE_ADDED_LATER @@ -6557,7 +6535,7 @@ innodb_mutex_show_status( rw_lock_count_os_wait, rw_lock_count_os_yield, rw_lock_wait_time/1000); - if (stat_print(thd, innobase_hton.name, hton_name_len, + if (stat_print(thd, innobase_hton_name, hton_name_len, STRING_WITH_LEN("rw_lock_mutexes"), buf2, buf2len)) { DBUG_RETURN(1); } @@ -6963,17 +6941,21 @@ func_exit_early: return(error); } -/*********************************************************************** +/******************************************************************************* This function initializes the auto-inc counter if it has not been initialized yet. This function does not change the value of the auto-inc counter if it already has been initialized. Returns the value of the -auto-inc counter. */ +auto-inc counter in *first_value, and ULONGLONG_MAX in *nb_reserved_values (as +we have a table-level lock). offset, increment, nb_desired_values are ignored. +*first_value is set to -1 if error (deadlock or lock wait timeout) */ -ulonglong -ha_innobase::get_auto_increment() -/*=============================*/ - /* out: auto-increment column value, -1 if error - (deadlock or lock wait timeout) */ +void ha_innobase::get_auto_increment( +/*=================================*/ + ulonglong offset, /* in */ + ulonglong increment, /* in */ + ulonglong nb_desired_values, /* in */ + ulonglong *first_value, /* out */ + ulonglong *nb_reserved_values) /* out */ { longlong nr; int error; @@ -6988,10 +6970,13 @@ ha_innobase::get_auto_increment() ut_print_timestamp(stderr); sql_print_error("Error %lu in ::get_auto_increment()", (ulong) error); - return(~(ulonglong) 0); + *first_value= (~(ulonglong) 0); + return; } - return((ulonglong) nr); + *first_value= (ulonglong) nr; + /* table-level autoinc lock reserves up to +inf */ + *nb_reserved_values= ULONGLONG_MAX; } /* See comment in handler.h */ @@ -7459,15 +7444,17 @@ bool ha_innobase::check_if_incompatible_data( return COMPATIBLE_DATA_YES; } +struct st_mysql_storage_engine innobase_storage_engine= +{ MYSQL_HANDLERTON_INTERFACE_VERSION, &innobase_hton}; mysql_declare_plugin(innobase) { MYSQL_STORAGE_ENGINE_PLUGIN, - &innobase_hton, + &innobase_storage_engine, innobase_hton_name, "Innobase OY", - innobase_hton_comment, - NULL, /* Plugin Init */ + "Supports transactions, row-level locking, and foreign keys", + innobase_init, /* Plugin Init */ NULL, /* Plugin Deinit */ 0x0100 /* 1.0 */, 0 @@ -7475,3 +7462,4 @@ mysql_declare_plugin(innobase) mysql_declare_plugin_end; #endif + diff --git a/sql/ha_innodb.h b/sql/ha_innodb.h index 4f0c9eb151b..c7d698cbcf4 100644 --- a/sql/ha_innodb.h +++ b/sql/ha_innodb.h @@ -33,6 +33,8 @@ typedef struct st_innobase_share { } INNOBASE_SHARE; +struct row_prebuilt_struct; + my_bool innobase_query_caching_of_table_permitted(THD* thd, char* full_name, uint full_name_len, ulonglong *unused); @@ -89,7 +91,7 @@ class ha_innobase: public handler const char* table_type() const { return("InnoDB");} const char *index_type(uint key_number) { return "BTREE"; } const char** bas_ext() const; - ulong table_flags() const { return int_table_flags; } + ulonglong table_flags() const { return int_table_flags; } ulong index_flags(uint idx, uint part, bool all_parts) const { return (HA_READ_NEXT | @@ -109,7 +111,6 @@ class ha_innobase: public handler uint max_supported_key_length() const { return 3500; } uint max_supported_key_part_length() const; const key_map *keys_to_use_for_scanning() { return &key_map_full; } - bool has_transactions() { return 1;} int open(const char *name, int mode, uint test_if_locked); int close(void); @@ -147,20 +148,10 @@ class ha_innobase: public handler int optimize(THD* thd,HA_CHECK_OPT* check_opt); int discard_or_import_tablespace(my_bool discard); int extra(enum ha_extra_function operation); + int reset(); int external_lock(THD *thd, int lock_type); int transactional_table_lock(THD *thd, int lock_type); int start_stmt(THD *thd, thr_lock_type lock_type); - - int ha_retrieve_all_cols() - { - ha_set_all_bits_in_read_set(); - return extra(HA_EXTRA_RETRIEVE_ALL_COLS); - } - int ha_retrieve_all_pk() - { - ha_set_primary_key_in_read_set(); - return extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY); - } void position(byte *record); ha_rows records_in_range(uint inx, key_range *min_key, key_range *max_key); @@ -181,7 +172,10 @@ class ha_innobase: public handler THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to, enum thr_lock_type lock_type); void init_table_handle_for_HANDLER(); - ulonglong get_auto_increment(); + virtual void get_auto_increment(ulonglong offset, ulonglong increment, + ulonglong nb_desired_values, + ulonglong *first_value, + ulonglong *nb_reserved_values); int reset_auto_increment(ulonglong value); virtual bool get_error_message(int error, String *buf); @@ -207,6 +201,8 @@ class ha_innobase: public handler int cmp_ref(const byte *ref1, const byte *ref2); bool check_if_incompatible_data(HA_CREATE_INFO *info, uint table_changes); + void build_template(struct row_prebuilt_struct *prebuilt, THD *thd, + TABLE *table, uint templ_type); }; extern SHOW_VAR innodb_status_variables[]; @@ -249,7 +245,7 @@ extern ulong srv_thread_concurrency; extern ulong srv_commit_concurrency; } -bool innobase_init(void); +int innobase_init(void); int innobase_end(ha_panic_function type); bool innobase_flush_logs(void); uint innobase_get_free_space(void); diff --git a/sql/ha_myisam.cc b/sql/ha_myisam.cc index 40081c975c8..2d097c34f97 100644 --- a/sql/ha_myisam.cc +++ b/sql/ha_myisam.cc @@ -52,63 +52,11 @@ TYPELIB myisam_stats_method_typelib= { ** MyISAM tables *****************************************************************************/ -static handler *myisam_create_handler(TABLE_SHARE *table); - -/* MyISAM handlerton */ - -static const char myisam_hton_name[]= "MyISAM"; -static const char myisam_hton_comment[]= - "Default engine as of MySQL 3.23 with great performance"; - -handlerton myisam_hton= { - MYSQL_HANDLERTON_INTERFACE_VERSION, - myisam_hton_name, - SHOW_OPTION_YES, - myisam_hton_comment, - DB_TYPE_MYISAM, - NULL, - 0, /* slot */ - 0, /* savepoint size. */ - NULL, /* close_connection */ - NULL, /* savepoint */ - NULL, /* rollback to savepoint */ - NULL, /* release savepoint */ - NULL, /* commit */ - NULL, /* rollback */ - NULL, /* prepare */ - NULL, /* recover */ - NULL, /* commit_by_xid */ - NULL, /* rollback_by_xid */ - NULL, /* create_cursor_read_view */ - NULL, /* set_cursor_read_view */ - NULL, /* close_cursor_read_view */ - /* - MyISAM doesn't support transactions and doesn't have - transaction-dependent context: cursors can survive a commit. - */ - myisam_create_handler, /* Create a new handler */ - NULL, /* Drop a database */ - mi_panic,/* Panic call */ - NULL, /* Start Consistent Snapshot */ - NULL, /* Flush logs */ - NULL, /* Show status */ - NULL, /* Partition flags */ - NULL, /* Alter table flags */ - NULL, /* Alter Tablespace */ - NULL, /* Fill Files Table */ - HTON_CAN_RECREATE, - NULL, /* binlog_func */ - NULL, /* binlog_log_query */ - NULL /* release_temporary_latches */ -}; - - -static handler *myisam_create_handler(TABLE_SHARE *table) +static handler *myisam_create_handler(TABLE_SHARE *table, MEM_ROOT *mem_root) { - return new ha_myisam(table); + return new (mem_root) ha_myisam(table); } - // collect errors printed by mi_check routines static void mi_check_print_msg(MI_CHECK *param, const char* msg_type, @@ -192,10 +140,11 @@ void mi_check_print_warning(MI_CHECK *param, const char *fmt,...) ha_myisam::ha_myisam(TABLE_SHARE *table_arg) :handler(&myisam_hton, table_arg), file(0), int_table_flags(HA_NULL_IN_KEY | HA_CAN_FULLTEXT | HA_CAN_SQL_HANDLER | - HA_DUPP_POS | HA_CAN_INDEX_BLOBS | HA_AUTO_PART_KEY | - HA_FILE_BASED | HA_CAN_GEOMETRY | HA_READ_RND_SAME | - HA_CAN_INSERT_DELAYED | HA_CAN_BIT_FIELD | HA_CAN_RTREEKEYS), - can_enable_indexes(1) + HA_DUPLICATE_POS | HA_CAN_INDEX_BLOBS | HA_AUTO_PART_KEY | + HA_FILE_BASED | HA_CAN_GEOMETRY | HA_NO_TRANSACTIONS | + HA_CAN_INSERT_DELAYED | HA_CAN_BIT_FIELD | HA_CAN_RTREEKEYS | + HA_HAS_RECORDS | HA_STATS_RECORDS_IS_EXACT), + can_enable_indexes(1) {} @@ -1318,7 +1267,7 @@ int ha_myisam::rnd_init(bool scan) { if (scan) return mi_scan_init(file); - return mi_extra(file, HA_EXTRA_RESET, 0); + return mi_reset(file); // Free buffers } int ha_myisam::rnd_next(byte *buf) @@ -1358,24 +1307,23 @@ void ha_myisam::info(uint flag) (void) mi_status(file,&info,flag); if (flag & HA_STATUS_VARIABLE) { - records = info.records; - deleted = info.deleted; - data_file_length=info.data_file_length; - index_file_length=info.index_file_length; - delete_length = info.delete_length; - check_time = info.check_time; - mean_rec_length=info.mean_reclength; + stats.records = info.records; + stats.deleted = info.deleted; + stats.data_file_length=info.data_file_length; + stats.index_file_length=info.index_file_length; + stats.delete_length = info.delete_length; + stats.check_time = info.check_time; + stats. mean_rec_length=info.mean_reclength; } if (flag & HA_STATUS_CONST) { TABLE_SHARE *share= table->s; - max_data_file_length= info.max_data_file_length; - max_index_file_length= info.max_index_file_length; - create_time= info.create_time; - sortkey= info.sortkey; + stats.max_data_file_length= info.max_data_file_length; + stats.max_index_file_length= info.max_index_file_length; + stats.create_time= info.create_time; ref_length= info.reflength; share->db_options_in_use= info.options; - block_size= myisam_block_size; /* record block size */ + stats.block_size= myisam_block_size; /* record block size */ /* Update share */ if (share->tmp_table == NO_TMP_TABLE) @@ -1406,12 +1354,12 @@ void ha_myisam::info(uint flag) if (flag & HA_STATUS_ERRKEY) { errkey = info.errkey; - my_store_ptr(dupp_ref, ref_length, info.dupp_key_pos); + my_store_ptr(dup_ref, ref_length, info.dupp_key_pos); } if (flag & HA_STATUS_TIME) - update_time = info.update_time; + stats.update_time = info.update_time; if (flag & HA_STATUS_AUTO) - auto_increment_value= info.auto_increment; + stats.auto_increment_value= info.auto_increment; } @@ -1422,6 +1370,10 @@ int ha_myisam::extra(enum ha_extra_function operation) return mi_extra(file, operation, 0); } +int ha_myisam::reset(void) +{ + return mi_reset(file); +} /* To be used with WRITE_CACHE and EXTRA_CACHE */ @@ -1465,7 +1417,7 @@ void ha_myisam::update_create_info(HA_CREATE_INFO *create_info) ha_myisam::info(HA_STATUS_AUTO | HA_STATUS_CONST); if (!(create_info->used_fields & HA_CREATE_USED_AUTO)) { - create_info->auto_increment_value=auto_increment_value; + create_info->auto_increment_value= stats.auto_increment_value; } create_info->data_file_name=data_file_name; create_info->index_file_name=index_file_name; @@ -1690,7 +1642,10 @@ int ha_myisam::rename_table(const char * from, const char * to) } -ulonglong ha_myisam::get_auto_increment() +void ha_myisam::get_auto_increment(ulonglong offset, ulonglong increment, + ulonglong nb_desired_values, + ulonglong *first_value, + ulonglong *nb_reserved_values) { ulonglong nr; int error; @@ -1699,7 +1654,10 @@ ulonglong ha_myisam::get_auto_increment() if (!table->s->next_number_key_offset) { // Autoincrement at key-start ha_myisam::info(HA_STATUS_AUTO); - return auto_increment_value; + *first_value= stats.auto_increment_value; + /* MyISAM has only table-level lock, so reserves to +inf */ + *nb_reserved_values= ULONGLONG_MAX; + return; } /* it's safe to call the following if bulk_insert isn't on */ @@ -1720,7 +1678,14 @@ ulonglong ha_myisam::get_auto_increment() val_int_offset(table->s->rec_buff_length)+1); } extra(HA_EXTRA_NO_KEYREAD); - return nr; + *first_value= nr; + /* + MySQL needs to call us for next row: assume we are inserting ("a",null) + here, we return 3, and next this statement will want to insert ("b",null): + there is no reason why ("b",3+1) would be the good row to insert: maybe it + already exists, maybe 3+1 is too large... + */ + *nb_reserved_values= 1; } @@ -1783,7 +1748,7 @@ bool ha_myisam::check_if_incompatible_data(HA_CREATE_INFO *info, { uint options= table->s->db_options_in_use; - if (info->auto_increment_value != auto_increment_value || + if (info->auto_increment_value != stats.auto_increment_value || info->data_file_name != data_file_name || info->index_file_name != index_file_name || table_changes == IS_EQUAL_NO || @@ -1798,17 +1763,32 @@ bool ha_myisam::check_if_incompatible_data(HA_CREATE_INFO *info, return COMPATIBLE_DATA_YES; } +handlerton myisam_hton; + +static int myisam_init() +{ + myisam_hton.state=SHOW_OPTION_YES; + myisam_hton.db_type=DB_TYPE_MYISAM; + myisam_hton.create=myisam_create_handler; + myisam_hton.panic=mi_panic; + myisam_hton.flags=HTON_CAN_RECREATE; + return 0; +} + +struct st_mysql_storage_engine myisam_storage_engine= +{ MYSQL_HANDLERTON_INTERFACE_VERSION, &myisam_hton }; mysql_declare_plugin(myisam) { MYSQL_STORAGE_ENGINE_PLUGIN, - &myisam_hton, - myisam_hton_name, + &myisam_storage_engine, + "MyISAM", "MySQL AB", - myisam_hton_comment, - NULL, /* Plugin Init */ + "Default engine as of MySQL 3.23 with great performance", + myisam_init, /* Plugin Init */ NULL, /* Plugin Deinit */ 0x0100, /* 1.0 */ 0 } mysql_declare_plugin_end; + diff --git a/sql/ha_myisam.h b/sql/ha_myisam.h index 86efed27478..5544e5040b3 100644 --- a/sql/ha_myisam.h +++ b/sql/ha_myisam.h @@ -48,7 +48,7 @@ class ha_myisam: public handler const char *table_type() const { return "MyISAM"; } const char *index_type(uint key_number); const char **bas_ext() const; - ulong table_flags() const { return int_table_flags; } + ulonglong table_flags() const { return int_table_flags; } ulong index_flags(uint inx, uint part, bool all_parts) const { return ((table_share->key_info[inx].algorithm == HA_KEY_ALG_FULLTEXT) ? @@ -101,6 +101,7 @@ class ha_myisam: public handler void info(uint); int extra(enum ha_extra_function operation); int extra_opt(enum ha_extra_function operation, ulong cache_size); + int reset(void); int external_lock(THD *thd, int lock_type); int delete_all_rows(void); int disable_indexes(uint mode); @@ -113,7 +114,10 @@ class ha_myisam: public handler int create(const char *name, TABLE *form, HA_CREATE_INFO *create_info); THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to, enum thr_lock_type lock_type); - ulonglong get_auto_increment(); + virtual void get_auto_increment(ulonglong offset, ulonglong increment, + ulonglong nb_desired_values, + ulonglong *first_value, + ulonglong *nb_reserved_values); int rename_table(const char * from, const char * to); int delete_table(const char *name); int check(THD* thd, HA_CHECK_OPT* check_opt); diff --git a/sql/ha_myisammrg.cc b/sql/ha_myisammrg.cc index f4a052cea8a..afeed5f79df 100644 --- a/sql/ha_myisammrg.cc +++ b/sql/ha_myisammrg.cc @@ -34,55 +34,17 @@ ** MyISAM MERGE tables *****************************************************************************/ -static handler *myisammrg_create_handler(TABLE_SHARE *table); +static handler *myisammrg_create_handler(TABLE_SHARE *table, + MEM_ROOT *mem_root); /* MyISAM MERGE handlerton */ -static const char myisammrg_hton_name[]= "MRG_MYISAM"; -static const char myisammrg_hton_comment[]= - "Collection of identical MyISAM tables"; - -handlerton myisammrg_hton= { - MYSQL_HANDLERTON_INTERFACE_VERSION, - myisammrg_hton_name, - SHOW_OPTION_YES, - myisammrg_hton_comment, - DB_TYPE_MRG_MYISAM, - NULL, - 0, /* slot */ - 0, /* savepoint size. */ - NULL, /* close_connection */ - NULL, /* savepoint */ - NULL, /* rollback to savepoint */ - NULL, /* release savepoint */ - NULL, /* commit */ - NULL, /* rollback */ - NULL, /* prepare */ - NULL, /* recover */ - NULL, /* commit_by_xid */ - NULL, /* rollback_by_xid */ - NULL, /* create_cursor_read_view */ - NULL, /* set_cursor_read_view */ - NULL, /* close_cursor_read_view */ - myisammrg_create_handler, /* Create a new handler */ - NULL, /* Drop a database */ - myrg_panic, /* Panic call */ - NULL, /* Start Consistent Snapshot */ - NULL, /* Flush logs */ - NULL, /* Show status */ - NULL, /* Partition flags */ - NULL, /* Alter table flags */ - NULL, /* Alter Tablespace */ - NULL, /* Fill Files Table */ - HTON_CAN_RECREATE | HTON_ALTER_CANNOT_CREATE, - NULL, /* binlog_func */ - NULL, /* binlog_log_query */ - NULL /* release_temporary_latches */ -}; +handlerton myisammrg_hton; -static handler *myisammrg_create_handler(TABLE_SHARE *table) +static handler *myisammrg_create_handler(TABLE_SHARE *table, + MEM_ROOT *mem_root) { - return new ha_myisammrg(table); + return new (mem_root) ha_myisammrg(table); } @@ -134,10 +96,10 @@ int ha_myisammrg::open(const char *name, int mode, uint test_if_locked) if (!(test_if_locked & HA_OPEN_WAIT_IF_LOCKED)) myrg_extra(file,HA_EXTRA_WAIT_LOCK,0); - if (table->s->reclength != mean_rec_length && mean_rec_length) + if (table->s->reclength != stats.mean_rec_length && stats.mean_rec_length) { DBUG_PRINT("error",("reclength: %d mean_rec_length: %d", - table->s->reclength, mean_rec_length)); + table->s->reclength, stats.mean_rec_length)); goto err; } #if !defined(BIG_TABLES) || SIZEOF_OFF_T == 4 @@ -258,11 +220,13 @@ int ha_myisammrg::index_next_same(byte * buf, return error; } + int ha_myisammrg::rnd_init(bool scan) { - return myrg_extra(file,HA_EXTRA_RESET,0); + return myrg_reset(file); } + int ha_myisammrg::rnd_next(byte *buf) { statistic_increment(table->in_use->status_var.ha_read_rnd_next_count, @@ -272,6 +236,7 @@ int ha_myisammrg::rnd_next(byte *buf) return error; } + int ha_myisammrg::rnd_pos(byte * buf, byte *pos) { statistic_increment(table->in_use->status_var.ha_read_rnd_count, @@ -303,18 +268,18 @@ void ha_myisammrg::info(uint flag) The following fails if one has not compiled MySQL with -DBIG_TABLES and one has more than 2^32 rows in the merge tables. */ - records = (ha_rows) info.records; - deleted = (ha_rows) info.deleted; + stats.records = (ha_rows) info.records; + stats.deleted = (ha_rows) info.deleted; #if !defined(BIG_TABLES) || SIZEOF_OFF_T == 4 if ((info.records >= (ulonglong) 1 << 32) || (info.deleted >= (ulonglong) 1 << 32)) table->s->crashed= 1; #endif - data_file_length=info.data_file_length; + stats.data_file_length=info.data_file_length; errkey = info.errkey; table->s->keys_in_use.set_prefix(table->s->keys); table->s->db_options_in_use= info.options; - mean_rec_length= info.reclength; + stats.mean_rec_length= info.reclength; /* The handler::block_size is used all over the code in index scan cost @@ -332,11 +297,11 @@ void ha_myisammrg::info(uint flag) TODO: In 5.2 index scan cost calculation will be factored out into a virtual function in class handler and we'll be able to remove this hack. */ - block_size= 0; + stats.block_size= 0; if (file->tables) - block_size= myisam_block_size / file->tables; + stats.block_size= myisam_block_size / file->tables; - update_time=0; + stats.update_time= 0; #if SIZEOF_OFF_T > 4 ref_length=6; // Should be big enough #else @@ -362,6 +327,10 @@ int ha_myisammrg::extra(enum ha_extra_function operation) return myrg_extra(file,operation,0); } +int ha_myisammrg::reset(void) +{ + return myrg_reset(file); +} /* To be used with WRITE_CACHE, EXTRA_CACHE and BULK_INSERT_BEGIN */ @@ -580,14 +549,27 @@ bool ha_myisammrg::check_if_incompatible_data(HA_CREATE_INFO *info, return COMPATIBLE_DATA_NO; } +static int myisammrg_init() +{ + myisammrg_hton.state=SHOW_OPTION_YES; + myisammrg_hton.db_type=DB_TYPE_MRG_MYISAM; + myisammrg_hton.create=myisammrg_create_handler; + myisammrg_hton.panic=myrg_panic; + myisammrg_hton.flags= HTON_CAN_RECREATE | HTON_ALTER_CANNOT_CREATE; + return 0; +} + +struct st_mysql_storage_engine myisammrg_storage_engine= +{ MYSQL_HANDLERTON_INTERFACE_VERSION, &myisammrg_hton }; + mysql_declare_plugin(myisammrg) { MYSQL_STORAGE_ENGINE_PLUGIN, - &myisammrg_hton, - myisammrg_hton_name, + &myisammrg_storage_engine, + "MRG_MYISAM", "MySQL AB", - myisammrg_hton_comment, - NULL, /* Plugin Init */ + "Collection of identical MyISAM tables", + myisammrg_init, /* Plugin Init */ NULL, /* Plugin Deinit */ 0x0100, /* 1.0 */ 0 diff --git a/sql/ha_myisammrg.h b/sql/ha_myisammrg.h index 4327b1c17b9..b67ec3dc204 100644 --- a/sql/ha_myisammrg.h +++ b/sql/ha_myisammrg.h @@ -33,9 +33,9 @@ class ha_myisammrg: public handler const char *table_type() const { return "MRG_MyISAM"; } const char **bas_ext() const; const char *index_type(uint key_number); - ulong table_flags() const + ulonglong table_flags() const { - return (HA_REC_NOT_IN_SEQ | HA_AUTO_PART_KEY | HA_READ_RND_SAME | + return (HA_REC_NOT_IN_SEQ | HA_AUTO_PART_KEY | HA_NO_TRANSACTIONS | HA_NULL_IN_KEY | HA_CAN_INDEX_BLOBS | HA_FILE_BASED | HA_CAN_INSERT_DELAYED | HA_ANY_INDEX_MAY_BE_UNIQUE | HA_NO_COPY_ON_ALTER); @@ -50,7 +50,7 @@ class ha_myisammrg: public handler uint max_supported_key_length() const { return MI_MAX_KEY_LENGTH; } uint max_supported_key_part_length() const { return MI_MAX_KEY_LENGTH; } double scan_time() - { return ulonglong2double(data_file_length) / IO_SIZE + file->tables; } + { return ulonglong2double(stats.data_file_length) / IO_SIZE + file->tables; } int open(const char *name, int mode, uint test_if_locked); int close(void); @@ -73,6 +73,7 @@ class ha_myisammrg: public handler void position(const byte *record); ha_rows records_in_range(uint inx, key_range *min_key, key_range *max_key); void info(uint); + int reset(void); int extra(enum ha_extra_function operation); int extra_opt(enum ha_extra_function operation, ulong cache_size); int external_lock(THD *thd, int lock_type); diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index 3425c638005..94fb7e6eb5c 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -63,30 +63,18 @@ static const int max_transactions= 3; // should really be 2 but there is a trans static uint ndbcluster_partition_flags(); static uint ndbcluster_alter_table_flags(uint flags); -static bool ndbcluster_init(void); +static int ndbcluster_init(void); static int ndbcluster_end(ha_panic_function flag); static bool ndbcluster_show_status(THD*,stat_print_fn *,enum ha_stat_type); static int ndbcluster_alter_tablespace(THD* thd, st_alter_tablespace *info); static int ndbcluster_fill_files_table(THD *thd, TABLE_LIST *tables, COND *cond); -static const char ndbcluster_hton_name[]= "ndbcluster"; -static const char ndbcluster_hton_comment[]= "Clustered, fault-tolerant tables"; - -handlerton ndbcluster_hton = { - MYSQL_HANDLERTON_INTERFACE_VERSION, - "ndbcluster", - SHOW_OPTION_YES, - "Clustered, fault-tolerant tables", - DB_TYPE_NDBCLUSTER, - ndbcluster_init, - ~(uint)0, /* slot */ - /* below are initialized by name in ndbcluster_init() */ - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 -}; +handlerton ndbcluster_hton; -static handler *ndbcluster_create_handler(TABLE_SHARE *table) +static handler *ndbcluster_create_handler(TABLE_SHARE *table, + MEM_ROOT *mem_root) { - return new ha_ndbcluster(table); + return new (mem_root) ha_ndbcluster(table); } static uint ndbcluster_partition_flags() @@ -440,9 +428,10 @@ void ha_ndbcluster::records_update() Ndb *ndb= get_ndb(); ndb->setDatabaseName(m_dbname); struct Ndb_statistics stat; - if (ndb_get_table_statistics(ndb, m_table, &stat) == 0){ - mean_rec_length= stat.row_size; - data_file_length= stat.fragment_memory; + if (ndb_get_table_statistics(ndb, m_table, &stat) == 0) + { + stats.mean_rec_length= stat.row_size; + stats.data_file_length= stat.fragment_memory; info->records= stat.row_count; } } @@ -451,7 +440,7 @@ void ha_ndbcluster::records_update() if (get_thd_ndb(thd)->error) info->no_uncommitted_rows_count= 0; } - records= info->records+ info->no_uncommitted_rows_count; + stats.records= info->records+ info->no_uncommitted_rows_count; DBUG_VOID_RETURN; } @@ -899,23 +888,24 @@ int ha_ndbcluster::get_ndb_partition_id(NdbOperation *ndb_op) /* Check if any set or get of blob value in current query. */ + bool ha_ndbcluster::uses_blob_value() { + uint blob_fields; + MY_BITMAP *bitmap; + uint *blob_index, *blob_index_end; if (table_share->blob_fields == 0) return FALSE; + + bitmap= m_write_op ? table->write_set : table->read_set; + blob_index= table_share->blob_field; + blob_index_end= blob_index + table_share->blob_fields; + do { - uint no_fields= table_share->fields; - int i; - // They always put blobs at the end.. - for (i= no_fields - 1; i >= 0; i--) - { - if ((m_write_op && ha_get_bit_in_write_set(i+1)) || - (!m_write_op && ha_get_bit_in_read_set(i+1))) - { - return TRUE; - } - } - } + if (bitmap_is_set(table->write_set, + table->field[*blob_index]->field_index)) + return TRUE; + } while (++blob_index != blob_index_end); return FALSE; } @@ -1412,10 +1402,10 @@ int ha_ndbcluster::get_ndb_lock_type(enum thr_lock_type type) { if (type >= TL_WRITE_ALLOW_WRITE) return NdbOperation::LM_Exclusive; - else if (uses_blob_value()) + if (type == TL_READ_WITH_SHARED_LOCKS || + uses_blob_value()) return NdbOperation::LM_Read; - else - return NdbOperation::LM_CommittedRead; + return NdbOperation::LM_CommittedRead; } static const ulong index_type_flags[]= @@ -1590,13 +1580,13 @@ int ha_ndbcluster::define_read_attrs(byte* buf, NdbOperation* op) for (i= 0; i < table_share->fields; i++) { Field *field= table->field[i]; - if (ha_get_bit_in_read_set(i+1) || + if (bitmap_is_set(table->read_set, i) || ((field->flags & PRI_KEY_FLAG))) { if (get_ndb_value(op, field, i, buf)) ERR_RETURN(op->getNdbError()); } - else + else { m_value[i].ptr= NULL; } @@ -1700,7 +1690,7 @@ int ha_ndbcluster::complemented_read(const byte *old_data, byte *new_data, DBUG_ENTER("complemented_read"); m_write_op= FALSE; - if (ha_get_all_bit_in_read_set()) + if (bitmap_is_set_all(table->read_set)) { // We have allready retrieved all fields, nothing to complement DBUG_RETURN(0); @@ -1731,7 +1721,8 @@ int ha_ndbcluster::complemented_read(const byte *old_data, byte *new_data, { Field *field= table->field[i]; if (!((field->flags & PRI_KEY_FLAG) || - (ha_get_bit_in_read_set(i+1)))) + bitmap_is_set(table->read_set, i)) && + !bitmap_is_set(table->write_set, i)) { if (get_ndb_value(op, field, i, new_data)) ERR_RETURN(trans->getNdbError()); @@ -1755,7 +1746,7 @@ int ha_ndbcluster::complemented_read(const byte *old_data, byte *new_data, { Field *field= table->field[i]; if (!((field->flags & PRI_KEY_FLAG) || - (ha_get_bit_in_read_set(i+1)))) + bitmap_is_set(table->read_set, i))) { m_value[i].ptr= NULL; } @@ -1856,11 +1847,11 @@ int ha_ndbcluster::peek_indexed_rows(const byte *record) uint32 part_id; int error; longlong func_value; - if ((error= m_part_info->get_partition_id(m_part_info, &part_id, - &func_value))) - { + my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set); + error= m_part_info->get_partition_id(m_part_info, &part_id, &func_value); + dbug_tmp_restore_column_map(table->read_set, old_map); + if (error) DBUG_RETURN(error); - } op->setPartitionId(part_id); } } @@ -1957,7 +1948,30 @@ inline int ha_ndbcluster::fetch_next(NdbScanOperation* cursor) int check; NdbTransaction *trans= m_active_trans; - bool contact_ndb= m_lock.type < TL_WRITE_ALLOW_WRITE; + if (m_lock_tuple) + { + /* + Lock level m_lock.type either TL_WRITE_ALLOW_WRITE + (SELECT FOR UPDATE) or TL_READ_WITH_SHARED_LOCKS (SELECT + LOCK WITH SHARE MODE) and row was not explictly unlocked + with unlock_row() call + */ + NdbConnection *trans= m_active_trans; + NdbOperation *op; + // Lock row + DBUG_PRINT("info", ("Keeping lock on scanned row")); + + if (!(op= m_active_cursor->lockCurrentTuple())) + { + m_lock_tuple= false; + ERR_RETURN(trans->getNdbError()); + } + m_ops_pending++; + } + m_lock_tuple= false; + + bool contact_ndb= m_lock.type < TL_WRITE_ALLOW_WRITE && + m_lock.type != TL_READ_WITH_SHARED_LOCKS;; do { DBUG_PRINT("info", ("Call nextResult, contact_ndb: %d", contact_ndb)); /* @@ -1973,6 +1987,13 @@ inline int ha_ndbcluster::fetch_next(NdbScanOperation* cursor) if ((check= cursor->nextResult(contact_ndb, m_force_send)) == 0) { + /* + Explicitly lock tuple if "select for update" or + "select lock in share mode" + */ + m_lock_tuple= (m_lock.type == TL_WRITE_ALLOW_WRITE + || + m_lock.type == TL_READ_WITH_SHARED_LOCKS); DBUG_RETURN(0); } else if (check == 1 || check == 2) @@ -2268,9 +2289,10 @@ int ha_ndbcluster::ordered_index_scan(const key_range *start_key, restart= FALSE; NdbOperation::LockMode lm= (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type); + bool need_pk = (lm == NdbOperation::LM_Read); if (!(op= trans->getNdbIndexScanOperation(m_index[active_index].index, m_table)) || - op->readTuples(lm, 0, parallelism, sorted, descending)) + op->readTuples(lm, 0, parallelism, sorted, descending, false, need_pk)) ERR_RETURN(trans->getNdbError()); if (m_use_partition_function && part_spec != NULL && part_spec->start_part == part_spec->end_part) @@ -2339,8 +2361,11 @@ int ha_ndbcluster::full_table_scan(byte *buf) NdbOperation::LockMode lm= (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type); + bool need_pk = (lm == NdbOperation::LM_Read); if (!(op=trans->getNdbScanOperation(m_table)) || - op->readTuples(lm, 0, parallelism)) + op->readTuples(lm, + (need_pk)?NdbScanOperation::SF_KeyInfo:0, + parallelism)) ERR_RETURN(trans->getNdbError()); m_active_cursor= op; @@ -2452,11 +2477,11 @@ int ha_ndbcluster::write_row(byte *record) { uint32 part_id; int error; - if ((error= m_part_info->get_partition_id(m_part_info, &part_id, - &func_value))) - { + my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set); + error= m_part_info->get_partition_id(m_part_info, &part_id, &func_value); + dbug_tmp_restore_column_map(table->read_set, old_map); + if (error) DBUG_RETURN(error); - } op->setPartitionId(part_id); } @@ -2480,25 +2505,27 @@ int ha_ndbcluster::write_row(byte *record) } else { - int res; - - if ((res= set_primary_key_from_record(op, record))) - return res; + int error; + if ((error= set_primary_key_from_record(op, record))) + DBUG_RETURN(error); } // Set non-key attribute(s) bool set_blob_value= FALSE; + my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set); for (i= 0; i < table_share->fields; i++) { Field *field= table->field[i]; if (!(field->flags & PRI_KEY_FLAG) && - (ha_get_bit_in_write_set(i + 1) || !m_use_write) && + (bitmap_is_set(table->write_set, i) || !m_use_write) && set_ndb_value(op, field, i, record-table->record[0], &set_blob_value)) { m_skip_auto_increment= TRUE; + dbug_tmp_restore_column_map(table->read_set, old_map); ERR_RETURN(op->getNdbError()); } } + dbug_tmp_restore_column_map(table->read_set, old_map); if (m_use_partition_function) { @@ -2577,6 +2604,7 @@ int ha_ndbcluster::write_row(byte *record) } m_skip_auto_increment= TRUE; + DBUG_PRINT("exit",("ok")); DBUG_RETURN(0); } @@ -2636,7 +2664,7 @@ int ha_ndbcluster::update_row(const byte *old_data, byte *new_data) if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE) { table->timestamp_field->set_time(); - ha_set_bit_in_write_set(table->timestamp_field->fieldnr); + bitmap_set_bit(table->write_set, table->timestamp_field->field_index); } if (m_use_partition_function && @@ -2713,6 +2741,7 @@ int ha_ndbcluster::update_row(const byte *old_data, byte *new_data) DBUG_PRINT("info", ("Calling updateTuple on cursor")); if (!(op= cursor->updateCurrentTuple())) ERR_RETURN(trans->getNdbError()); + m_lock_tuple= false; m_ops_pending++; if (uses_blob_value()) m_blobs_pending= TRUE; @@ -2750,14 +2779,19 @@ int ha_ndbcluster::update_row(const byte *old_data, byte *new_data) m_rows_changed++; // Set non-key attribute(s) + my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set); for (i= 0; i < table_share->fields; i++) { Field *field= table->field[i]; - if (ha_get_bit_in_write_set(i+1) && + if (bitmap_is_set(table->write_set, i) && (!(field->flags & PRI_KEY_FLAG)) && set_ndb_value(op, field, i, new_data - table->record[0])) + { + dbug_tmp_restore_column_map(table->read_set, old_map); ERR_RETURN(op->getNdbError()); + } } + dbug_tmp_restore_column_map(table->read_set, old_map); if (m_use_partition_function) { @@ -2816,6 +2850,7 @@ int ha_ndbcluster::delete_row(const byte *record) DBUG_PRINT("info", ("Calling deleteTuple on cursor")); if (cursor->deleteCurrentTuple() != 0) ERR_RETURN(trans->getNdbError()); + m_lock_tuple= false; m_ops_pending++; if (m_use_partition_function) @@ -2849,9 +2884,8 @@ int ha_ndbcluster::delete_row(const byte *record) } else { - int res; - if ((res= set_primary_key_from_record(op, record))) - return res; + if ((error= set_primary_key_from_record(op, record))) + DBUG_RETURN(error); } } @@ -2881,7 +2915,8 @@ void ndb_unpack_record(TABLE *table, NdbValue *value, MY_BITMAP *defined, byte *buf) { Field **p_field= table->field, *field= *p_field; - my_ptrdiff_t row_offset= buf - table->record[0]; + my_ptrdiff_t row_offset= (my_ptrdiff_t) (buf - table->record[0]); + my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set); DBUG_ENTER("ndb_unpack_record"); // Set null flag(s) @@ -2942,13 +2977,13 @@ void ndb_unpack_record(TABLE *table, NdbValue *value, field_bit->Field_bit::move_field_offset(-row_offset); DBUG_PRINT("info",("[%u] SET", (*value).rec->getColumn()->getColumnNo())); - DBUG_DUMP("info", (const char*) field->ptr, field->field_length); + DBUG_DUMP("info", (const char*) field->ptr, field->pack_length()); } else { DBUG_PRINT("info",("[%u] SET", (*value).rec->getColumn()->getColumnNo())); - DBUG_DUMP("info", (const char*) field->ptr, field->field_length); + DBUG_DUMP("info", (const char*) field->ptr, field->pack_length()); } } else @@ -2981,6 +3016,7 @@ void ndb_unpack_record(TABLE *table, NdbValue *value, } } } + dbug_tmp_restore_column_map(table->write_set, old_map); DBUG_VOID_RETURN; } @@ -3074,6 +3110,13 @@ int ha_ndbcluster::index_init(uint index, bool sorted) DBUG_PRINT("enter", ("index: %u sorted: %d", index, sorted)); active_index= index; m_sorted= sorted; + /* + Locks are are explicitly released in scan + unless m_lock.type == TL_READ_HIGH_PRIORITY + and no sub-sequent call to unlock_row() + */ + m_lock_tuple= false; + m_lock_tuple= false; DBUG_RETURN(0); } @@ -3503,7 +3546,7 @@ void ha_ndbcluster::info(uint flag) if (m_table_info) { if (m_ha_not_exact_count) - records= 100; + stats.records= 100; else records_update(); } @@ -3517,14 +3560,14 @@ void ha_ndbcluster::info(uint flag) if (current_thd->variables.ndb_use_exact_count && ndb_get_table_statistics(ndb, m_table, &stat) == 0) { - mean_rec_length= stat.row_size; - data_file_length= stat.fragment_memory; - records= stat.row_count; + stats.mean_rec_length= stat.row_size; + stats.data_file_length= stat.fragment_memory; + stats.records= stat.row_count; } else { - mean_rec_length= 0; - records= 100; + stats.mean_rec_length= 0; + stats.records= 100; } } } @@ -3553,10 +3596,10 @@ void ha_ndbcluster::info(uint flag) const NdbError err= ndb->getNdbError(); sql_print_error("Error %lu in readAutoIncrementValue(): %s", (ulong) err.code, err.message); - auto_increment_value= ~(Uint64)0; + stats.auto_increment_value= ~(ulonglong)0; } else - auto_increment_value= (ulonglong)auto_increment_value64; + stats.auto_increment_value= (ulonglong)auto_increment_value64; } } DBUG_VOID_RETURN; @@ -3580,18 +3623,6 @@ int ha_ndbcluster::extra(enum ha_extra_function operation) { DBUG_ENTER("extra"); switch (operation) { - case HA_EXTRA_RESET: /* Reset database to after open */ - DBUG_PRINT("info", ("HA_EXTRA_RESET")); - DBUG_PRINT("info", ("Clearing condition stack")); - cond_clear(); - /* - * Regular partition pruning will set the bitmap appropriately. - * Some queries like ALTER TABLE doesn't use partition pruning and - * thus the 'used_partitions' bitmap needs to be initialized - */ - if (m_part_info) - bitmap_set_all(&m_part_info->used_partitions); - break; case HA_EXTRA_IGNORE_DUP_KEY: /* Dup keys don't rollback everything*/ DBUG_PRINT("info", ("HA_EXTRA_IGNORE_DUP_KEY")); if (current_thd->lex->sql_command == SQLCOM_REPLACE && !m_has_unique_index) @@ -3627,6 +3658,22 @@ int ha_ndbcluster::extra(enum ha_extra_function operation) DBUG_RETURN(0); } + +int ha_ndbcluster::reset() +{ + DBUG_ENTER("ha_ndbcluster::reset"); + cond_clear(); + /* + Regular partition pruning will set the bitmap appropriately. + Some queries like ALTER TABLE doesn't use partition pruning and + thus the 'used_partitions' bitmap needs to be initialized + */ + if (m_part_info) + bitmap_set_all(&m_part_info->used_partitions); + DBUG_RETURN(0); +} + + /* Start of an insert, remember number of rows to be inserted, it will be used in write_row and get_autoincrement to send an optimal number @@ -3743,7 +3790,7 @@ const char** ha_ndbcluster::bas_ext() const double ha_ndbcluster::scan_time() { DBUG_ENTER("ha_ndbcluster::scan_time()"); - double res= rows2double(records*1000); + double res= rows2double(stats.records*1000); DBUG_PRINT("exit", ("table: %s value: %f", m_tabname, res)); DBUG_RETURN(res); @@ -3827,8 +3874,8 @@ int ha_ndbcluster::external_lock(THD *thd, int lock_type) { int error=0; NdbTransaction* trans= NULL; - DBUG_ENTER("external_lock"); + /* Check that this handler instance has a connection set up to the Ndb object of thd @@ -3839,9 +3886,10 @@ int ha_ndbcluster::external_lock(THD *thd, int lock_type) Thd_ndb *thd_ndb= get_thd_ndb(thd); Ndb *ndb= thd_ndb->ndb; - DBUG_PRINT("enter", ("this: %x thd: %lx thd_ndb: %lx " + DBUG_PRINT("enter", ("this: 0x%lx thd: 0x%lx thd_ndb: %lx " "thd_ndb->lock_count: %d", - this, thd, thd_ndb, thd_ndb->lock_count)); + (long) this, (long) thd, (long) thd_ndb, + thd_ndb->lock_count)); if (lock_type != F_UNLCK) { @@ -3993,6 +4041,22 @@ int ha_ndbcluster::external_lock(THD *thd, int lock_type) } /* + Unlock the last row read in an open scan. + Rows are unlocked by default in ndb, but + for SELECT FOR UPDATE and SELECT LOCK WIT SHARE MODE + locks are kept if unlock_row() is not called. +*/ + +void ha_ndbcluster::unlock_row() +{ + DBUG_ENTER("unlock_row"); + + DBUG_PRINT("info", ("Unlocking row")); + m_lock_tuple= false; + DBUG_VOID_RETURN; +} + +/* Start a transaction for running a statement if one is not already running in a transaction. This will be the case in a BEGIN; COMMIT; block @@ -4409,6 +4473,7 @@ int ha_ndbcluster::create(const char *name, uint pack_length, length, i, pk_length= 0; const void *data, *pack_data; bool create_from_engine= (info->table_options & HA_OPTION_CREATE_FROM_ENGINE); + bool is_truncate= (current_thd->lex->sql_command == SQLCOM_TRUNCATE); DBUG_ENTER("ha_ndbcluster::create"); DBUG_PRINT("enter", ("name: %s", name)); @@ -4417,6 +4482,12 @@ int ha_ndbcluster::create(const char *name, set_dbname(name); set_tabname(name); + if (is_truncate) + { + DBUG_PRINT("info", ("Dropping and re-creating table for TRUNCATE")); + if ((my_errno= delete_table(name))) + DBUG_RETURN(my_errno); + } table= form; if (create_from_engine) { @@ -4661,7 +4732,9 @@ int ha_ndbcluster::create(const char *name, share->db, share->table_name, m_table->getObjectId(), m_table->getObjectVersion(), - SOT_CREATE_TABLE); + (is_truncate) ? + SOT_TRUNCATE_TABLE : SOT_CREATE_TABLE, + 0, 0, 1); break; } } @@ -4934,13 +5007,17 @@ int ha_ndbcluster::rename_table(const char *from, const char *to) { NDBDICT *dict; char old_dbname[FN_HEADLEN]; + char new_dbname[FN_HEADLEN]; char new_tabname[FN_HEADLEN]; const NDBTAB *orig_tab; int result; + bool recreate_indexes= FALSE; + NDBDICT::List index_list; DBUG_ENTER("ha_ndbcluster::rename_table"); DBUG_PRINT("info", ("Renaming %s to %s", from, to)); set_dbname(from, old_dbname); + set_dbname(to, new_dbname); set_tabname(from); set_tabname(to, new_tabname); @@ -4965,6 +5042,11 @@ int ha_ndbcluster::rename_table(const char *from, const char *to) DBUG_ASSERT(r == 0); } #endif + if (my_strcasecmp(system_charset_info, new_dbname, old_dbname)) + { + dict->listIndexes(index_list, *orig_tab); + recreate_indexes= TRUE; + } // Change current database to that of target table set_dbname(to); ndb->setDatabaseName(m_dbname); @@ -5043,7 +5125,33 @@ int ha_ndbcluster::rename_table(const char *from, const char *to) old_dbname, m_tabname, ndb_table_id, ndb_table_version, SOT_RENAME_TABLE, - m_dbname, new_tabname); + m_dbname, new_tabname, 1); + } + + // If we are moving tables between databases, we need to recreate + // indexes + if (recreate_indexes) + { + for (unsigned i = 0; i < index_list.count; i++) + { + NDBDICT::List::Element& index_el = index_list.elements[i]; + // Recreate any indexes not stored in the system database + if (my_strcasecmp(system_charset_info, + index_el.database, NDB_SYSTEM_DATABASE)) + { + set_dbname(from); + ndb->setDatabaseName(m_dbname); + const NDBINDEX * index= dict->getIndexGlobal(index_el.name, new_tab); + DBUG_PRINT("info", ("Creating index %s/%s", + index_el.database, index->getName())); + dict->createIndex(*index, new_tab); + DBUG_PRINT("info", ("Dropping index %s/%s", + index_el.database, index->getName())); + set_dbname(from); + ndb->setDatabaseName(m_dbname); + dict->dropIndexGlobal(*index); + } + } } if (share) free_share(&share); @@ -5066,6 +5174,7 @@ ha_ndbcluster::delete_table(ha_ndbcluster *h, Ndb *ndb, const char *db, const char *table_name) { + THD *thd= current_thd; DBUG_ENTER("ha_ndbcluster::ndbcluster_delete_table"); NDBDICT *dict= ndb->getDictionary(); #ifdef HAVE_NDB_BINLOG @@ -5085,7 +5194,7 @@ ha_ndbcluster::delete_table(ha_ndbcluster *h, Ndb *ndb, /* Drop the table from NDB */ - int res; + int res= 0; if (h && h->m_table) { if (dict->dropTableGlobal(*h->m_table)) @@ -5097,7 +5206,7 @@ ha_ndbcluster::delete_table(ha_ndbcluster *h, Ndb *ndb, ndb_table_version= h->m_table->getObjectVersion(); } #endif - h->release_metadata(current_thd, ndb); + h->release_metadata(thd, ndb); } else { @@ -5113,7 +5222,6 @@ ha_ndbcluster::delete_table(ha_ndbcluster *h, Ndb *ndb, ndb_table_id= ndbtab_g.get_table()->getObjectId(); ndb_table_version= ndbtab_g.get_table()->getObjectVersion(); #endif - res= 0; } else if (dict->getNdbError().code == NDB_INVALID_SCHEMA_OBJECT) { @@ -5161,13 +5269,14 @@ ha_ndbcluster::delete_table(ha_ndbcluster *h, Ndb *ndb, */ int table_dropped= dict->getNdbError().code != 709; - if (!IS_TMP_PREFIX(table_name) && share) + if (!IS_TMP_PREFIX(table_name) && share && + current_thd->lex->sql_command != SQLCOM_TRUNCATE) { - ndbcluster_log_schema_op(current_thd, share, - current_thd->query, current_thd->query_length, + ndbcluster_log_schema_op(thd, share, + thd->query, thd->query_length, share->db, share->table_name, ndb_table_id, ndb_table_version, - SOT_DROP_TABLE); + SOT_DROP_TABLE, 0, 0, 1); } else if (table_dropped && share && share->op) /* ndbcluster_log_schema_op will do a force GCP */ @@ -5230,7 +5339,10 @@ int ha_ndbcluster::delete_table(const char *name) } -ulonglong ha_ndbcluster::get_auto_increment() +void ha_ndbcluster::get_auto_increment(ulonglong offset, ulonglong increment, + ulonglong nb_desired_values, + ulonglong *first_value, + ulonglong *nb_reserved_values) { int cache_size; Uint64 auto_value; @@ -5264,9 +5376,13 @@ ulonglong ha_ndbcluster::get_auto_increment() const NdbError err= ndb->getNdbError(); sql_print_error("Error %lu in ::get_auto_increment(): %s", (ulong) err.code, err.message); - DBUG_RETURN(~(ulonglong) 0); + *first_value= ~(ulonglong) 0; + DBUG_VOID_RETURN; } - DBUG_RETURN((longlong)auto_value); + *first_value= (longlong)auto_value; + /* From the point of view of MySQL, NDB reserves one row at a time */ + *nb_reserved_values= 1; + DBUG_VOID_RETURN; } @@ -5282,7 +5398,9 @@ ulonglong ha_ndbcluster::get_auto_increment() HA_NEED_READ_RANGE_BUFFER | \ HA_CAN_GEOMETRY | \ HA_CAN_BIT_FIELD | \ - HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS + HA_PRIMARY_KEY_REQUIRED_FOR_POSITION | \ + HA_PRIMARY_KEY_REQUIRED_FOR_DELETE | \ + HA_PARTIAL_COLUMN_READ ha_ndbcluster::ha_ndbcluster(TABLE_SHARE *table_arg): handler(&ndbcluster_hton, table_arg), @@ -5325,8 +5443,8 @@ ha_ndbcluster::ha_ndbcluster(TABLE_SHARE *table_arg): m_tabname[0]= '\0'; m_dbname[0]= '\0'; - records= ~(ha_rows)0; // uninitialized - block_size= 1024; + stats.records= ~(ha_rows)0; // uninitialized + stats.block_size= 1024; for (i= 0; i < MAX_KEY; i++) ndb_init_index(m_index[i]); @@ -5650,7 +5768,7 @@ int ndbcluster_table_exists_in_engine(THD* thd, const char *db, { Ndb* ndb; DBUG_ENTER("ndbcluster_table_exists_in_engine"); - DBUG_PRINT("enter", ("db: %s, name: %s", db, name)); + DBUG_PRINT("enter", ("db: %s name: %s", db, name)); if (!(ndb= check_ndb_in_thd(thd))) DBUG_RETURN(HA_ERR_NO_CONNECTION); @@ -5659,14 +5777,13 @@ int ndbcluster_table_exists_in_engine(THD* thd, const char *db, NdbDictionary::Dictionary::List list; if (dict->listObjects(list, NdbDictionary::Object::UserTable) != 0) ERR_RETURN(dict->getNdbError()); - for (int i= 0 ; i < list.count ; i++) + for (uint i= 0 ; i < list.count ; i++) { NdbDictionary::Dictionary::List::Element& elmt= list.elements[i]; if (my_strcasecmp(system_charset_info, elmt.database, db)) continue; if (my_strcasecmp(system_charset_info, elmt.name, name)) continue; - // table found DBUG_PRINT("info", ("Found table")); DBUG_RETURN(1); } @@ -5746,6 +5863,7 @@ int ndbcluster_drop_database_impl(const char *path) static void ndbcluster_drop_database(char *path) { + THD *thd= current_thd; DBUG_ENTER("ndbcluster_drop_database"); #ifdef HAVE_NDB_BINLOG /* @@ -5763,9 +5881,9 @@ static void ndbcluster_drop_database(char *path) #ifdef HAVE_NDB_BINLOG char db[FN_REFLEN]; ha_ndbcluster::set_dbname(path, db); - ndbcluster_log_schema_op(current_thd, 0, - current_thd->query, current_thd->query_length, - db, "", 0, 0, SOT_DROP_DB); + ndbcluster_log_schema_op(thd, 0, + thd->query, thd->query_length, + db, "", 0, 0, SOT_DROP_DB, 0, 0, 0); #endif DBUG_VOID_RETURN; } @@ -5796,6 +5914,8 @@ int ndbcluster_find_all_files(THD *thd) NDBDICT *dict= ndb->getDictionary(); int unhandled, retries= 5, skipped; + LINT_INIT(unhandled); + LINT_INIT(skipped); do { NdbDictionary::Dictionary::List list; @@ -6107,17 +6227,18 @@ static int connect_callback() } extern int ndb_dictionary_is_mysqld; -static bool ndbcluster_init() + +static int ndbcluster_init() { int res; DBUG_ENTER("ndbcluster_init"); ndb_dictionary_is_mysqld= 1; - if (have_ndbcluster != SHOW_OPTION_YES) - goto ndbcluster_init_error; { handlerton &h= ndbcluster_hton; + h.state= have_ndbcluster; + h.db_type= DB_TYPE_NDBCLUSTER; h.close_connection= ndbcluster_close_connection; h.commit= ndbcluster_commit; h.rollback= ndbcluster_rollback; @@ -6132,9 +6253,12 @@ static bool ndbcluster_init() #ifdef HAVE_NDB_BINLOG ndbcluster_binlog_init_handlerton(); #endif - h.flags= HTON_TEMPORARY_NOT_SUPPORTED; + h.flags= HTON_CAN_RECREATE | HTON_TEMPORARY_NOT_SUPPORTED; } + if (have_ndbcluster != SHOW_OPTION_YES) + DBUG_RETURN(0); // nothing else to do + // Set connectstring if specified if (opt_ndbcluster_connectstring != 0) DBUG_PRINT("connectstring", ("%s", opt_ndbcluster_connectstring)); @@ -6206,7 +6330,7 @@ static bool ndbcluster_init() if (ndbcluster_binlog_start()) goto ndbcluster_init_error; #endif /* HAVE_NDB_BINLOG */ - + pthread_mutex_init(&LOCK_ndb_util_thread, MY_MUTEX_INIT_FAST); pthread_cond_init(&COND_ndb_util_thread, NULL); @@ -6222,7 +6346,7 @@ static bool ndbcluster_init() pthread_cond_destroy(&COND_ndb_util_thread); goto ndbcluster_init_error; } - + ndbcluster_inited= 1; DBUG_RETURN(FALSE); @@ -6298,9 +6422,11 @@ void ha_ndbcluster::print_error(int error, myf errflag) if (error == HA_ERR_NO_PARTITION_FOUND) { char buf[100]; + my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set); my_error(ER_NO_PARTITION_FOR_GIVEN_VALUE, MYF(0), m_part_info->part_expr->null_value ? "NULL" : llstr(m_part_info->part_expr->val_int(), buf)); + dbug_tmp_restore_column_map(table->read_set, old_map); } else handler::print_error(error, errflag); @@ -6518,12 +6644,11 @@ ha_ndbcluster::records_in_range(uint inx, key_range *min_key, DBUG_RETURN(10); /* Good guess when you don't know anything */ } -ulong ha_ndbcluster::table_flags(void) const +ulonglong ha_ndbcluster::table_flags(void) const { if (m_ha_not_exact_count) - return m_table_flags | HA_NOT_EXACT_COUNT; - else - return m_table_flags; + return m_table_flags & ~HA_STATS_RECORDS_IS_EXACT; + return m_table_flags; } const char * ha_ndbcluster::table_type() const { @@ -6557,10 +6682,6 @@ bool ha_ndbcluster::low_byte_first() const return TRUE; #endif } -bool ha_ndbcluster::has_transactions() -{ - return TRUE; -} const char* ha_ndbcluster::index_type(uint key_number) { switch (get_index_type(key_number)) { @@ -6840,6 +6961,7 @@ static void dbug_print_open_tables() */ int handle_trailing_share(NDB_SHARE *share) { + THD *thd= current_thd; static ulong trailing_share_id= 0; DBUG_ENTER("handle_trailing_share"); @@ -6850,7 +6972,7 @@ int handle_trailing_share(NDB_SHARE *share) bzero((char*) &table_list,sizeof(table_list)); table_list.db= share->db; table_list.alias= table_list.table_name= share->table_name; - close_cached_tables(current_thd, 0, &table_list, TRUE); + close_cached_tables(thd, 0, &table_list, TRUE); pthread_mutex_lock(&ndbcluster_mutex); if (!--share->use_count) @@ -7378,6 +7500,7 @@ ha_ndbcluster::read_multi_range_first(KEY_MULTI_RANGE **found_range_p, byte *end_of_buffer= (byte*)buffer->buffer_end; NdbOperation::LockMode lm= (NdbOperation::LockMode)get_ndb_lock_type(m_lock.type); + bool need_pk = (lm == NdbOperation::LM_Read); const NDBTAB *tab= m_table; const NDBINDEX *unique_idx= m_index[active_index].unique_index; const NDBINDEX *idx= m_index[active_index].index; @@ -7468,7 +7591,8 @@ ha_ndbcluster::read_multi_range_first(KEY_MULTI_RANGE **found_range_p, end_of_buffer -= reclength; } else if ((scanOp= m_active_trans->getNdbIndexScanOperation(idx, tab)) - &&!scanOp->readTuples(lm, 0, parallelism, sorted, FALSE, TRUE) + &&!scanOp->readTuples(lm, 0, parallelism, sorted, + FALSE, TRUE, need_pk) &&!generate_scan_filter(m_cond_stack, scanOp) &&!define_read_attrs(end_of_buffer-reclength, scanOp)) { @@ -9383,9 +9507,8 @@ ndbcluster_show_status(THD* thd, stat_print_fn *stat_print, ndb_number_of_storage_nodes, ndb_number_of_ready_storage_nodes, ndb_connect_count); - if (stat_print(thd, ndbcluster_hton.name, strlen(ndbcluster_hton.name), - "connection", strlen("connection"), - buf, buflen)) + if (stat_print(thd, ndbcluster_hton_name, ndbcluster_hton_name_length, + STRING_WITH_LEN("connection"), buf, buflen)) DBUG_RETURN(TRUE); if (get_thd_ndb(thd) && get_thd_ndb(thd)->ndb) @@ -9399,7 +9522,7 @@ ndbcluster_show_status(THD* thd, stat_print_fn *stat_print, my_snprintf(buf, sizeof(buf), "created=%u, free=%u, sizeof=%u", tmp.m_created, tmp.m_free, tmp.m_sizeof); - if (stat_print(thd, ndbcluster_hton.name, strlen(ndbcluster_hton.name), + if (stat_print(thd, ndbcluster_hton_name, ndbcluster_hton_name_length, tmp.m_name, strlen(tmp.m_name), buf, buflen)) DBUG_RETURN(TRUE); } @@ -9615,7 +9738,7 @@ uint ha_ndbcluster::set_up_partition_info(partition_info *part_info, for (i= 0; i < part_info->part_field_list.elements; i++) { - NDBCOL *col= tab->getColumn(fields[i]->fieldnr - 1); + NDBCOL *col= tab->getColumn(fields[i]->field_index); DBUG_PRINT("info",("setting dist key on %s", col->getName())); col->setPartitionKey(TRUE); } @@ -9705,7 +9828,7 @@ bool ha_ndbcluster::check_if_incompatible_data(HA_CREATE_INFO *info, { Field *field= table->field[i]; const NDBCOL *col= tab->getColumn(field->field_name); - if (field->add_index && + if ((field->flags & FIELD_IN_ADD_INDEX) && col->getStorageType() == NdbDictionary::Column::StorageTypeDisk) { DBUG_PRINT("info", ("add/drop index not supported for disk stored column")); @@ -9954,13 +10077,13 @@ int ndbcluster_alter_tablespace(THD* thd, st_alter_tablespace *info) thd->query, thd->query_length, "", info->tablespace_name, 0, 0, - SOT_TABLESPACE); + SOT_TABLESPACE, 0, 0, 0); else ndbcluster_log_schema_op(thd, 0, thd->query, thd->query_length, "", info->logfile_group_name, 0, 0, - SOT_LOGFILE_GROUP); + SOT_LOGFILE_GROUP, 0, 0, 0); #endif DBUG_RETURN(FALSE); @@ -9981,10 +10104,11 @@ bool ha_ndbcluster::get_no_parts(const char *name, uint *no_parts) const NDBTAB *tab; int err; DBUG_ENTER("ha_ndbcluster::get_no_parts"); + LINT_INIT(err); set_dbname(name); set_tabname(name); - do + for (;;) { if (check_ndb_connection()) { @@ -9998,22 +10122,21 @@ bool ha_ndbcluster::get_no_parts(const char *name, uint *no_parts) ERR_BREAK(dict->getNdbError(), err); *no_parts= ndbtab_g.get_table()->getFragmentCount(); DBUG_RETURN(FALSE); - } while (1); + } -end: print_error(err, MYF(0)); DBUG_RETURN(TRUE); } -static int ndbcluster_fill_files_table(THD *thd, TABLE_LIST *tables, COND *cond) +static int ndbcluster_fill_files_table(THD *thd, TABLE_LIST *tables, + COND *cond) { TABLE* table= tables->table; Ndb *ndb= check_ndb_in_thd(thd); NdbDictionary::Dictionary* dict= ndb->getDictionary(); NdbDictionary::Dictionary::List dflist; NdbError ndberr; - unsigned i; - + uint i; DBUG_ENTER("ndbcluster_fill_files_table"); dict->listObjects(dflist, NdbDictionary::Object::Datafile); @@ -10025,12 +10148,13 @@ static int ndbcluster_fill_files_table(THD *thd, TABLE_LIST *tables, COND *cond) { NdbDictionary::Dictionary::List::Element& elt = dflist.elements[i]; Ndb_cluster_connection_node_iter iter; - unsigned id; - + uint id; + g_ndb_cluster_connection->init_get_next_node(iter); while ((id= g_ndb_cluster_connection->get_next_node(iter))) { + uint c= 0; NdbDictionary::Datafile df= dict->getDatafile(id, elt.name); ndberr= dict->getNdbError(); if(ndberr.classification != NdbError::NoError) @@ -10048,7 +10172,6 @@ static int ndbcluster_fill_files_table(THD *thd, TABLE_LIST *tables, COND *cond) ERR_RETURN(ndberr); } - int c= 0; table->field[c++]->set_null(); // FILE_ID table->field[c++]->store(elt.name, strlen(elt.name), system_charset_info); @@ -10064,8 +10187,8 @@ static int ndbcluster_fill_files_table(THD *thd, TABLE_LIST *tables, COND *cond) strlen(ts.getDefaultLogfileGroup()), system_charset_info); table->field[c++]->set_null(); // LOGFILE_GROUP_NUMBER - table->field[c++]->store(ndbcluster_hton.name, - strlen(ndbcluster_hton.name), + table->field[c++]->store(ndbcluster_hton_name, + ndbcluster_hton_name_length, system_charset_info); // ENGINE table->field[c++]->set_null(); // FULLTEXT_KEYS @@ -10158,8 +10281,8 @@ static int ndbcluster_fill_files_table(THD *thd, TABLE_LIST *tables, COND *cond) strlen(uf.getLogfileGroup()), system_charset_info); table->field[c++]->store(uf.getLogfileGroupId()); // LOGFILE_GROUP_NUMBER - table->field[c++]->store(ndbcluster_hton.name, - strlen(ndbcluster_hton.name), + table->field[c++]->store(ndbcluster_hton_name, + ndbcluster_hton_name_length, system_charset_info); // ENGINE table->field[c++]->set_null(); // FULLTEXT_KEYS @@ -10205,15 +10328,17 @@ static int ndbcluster_fill_files_table(THD *thd, TABLE_LIST *tables, COND *cond) DBUG_RETURN(0); } +struct st_mysql_storage_engine ndbcluster_storage_engine= +{ MYSQL_HANDLERTON_INTERFACE_VERSION, &ndbcluster_hton }; mysql_declare_plugin(ndbcluster) { MYSQL_STORAGE_ENGINE_PLUGIN, - &ndbcluster_hton, + &ndbcluster_storage_engine, ndbcluster_hton_name, "MySQL AB", - ndbcluster_hton_comment, - NULL, /* Plugin Init */ + "Clustered, fault-tolerant tables", + ndbcluster_init, /* Plugin Init */ NULL, /* Plugin Deinit */ 0x0100 /* 1.0 */, 0 diff --git a/sql/ha_ndbcluster.h b/sql/ha_ndbcluster.h index badca69941a..9bcc549cb60 100644 --- a/sql/ha_ndbcluster.h +++ b/sql/ha_ndbcluster.h @@ -350,7 +350,12 @@ class Ndb_item { const Item *item= value.item; if (item && field) - ((Item *)item)->save_in_field(field, false); + { + my_bitmap_map *old_map= + dbug_tmp_use_all_columns(field->table, field->table->write_set); + ((Item *)item)->save_in_field(field, FALSE); + dbug_tmp_restore_column_map(field->table->write_set, old_map); + } }; static NDB_FUNC_TYPE item_func_to_ndb_func(Item_func::Functype fun) @@ -621,12 +626,14 @@ class ha_ndbcluster: public handler void get_dynamic_partition_info(PARTITION_INFO *stat_info, uint part_id); int extra(enum ha_extra_function operation); int extra_opt(enum ha_extra_function operation, ulong cache_size); + int reset(); int external_lock(THD *thd, int lock_type); + void unlock_row(); int start_stmt(THD *thd, thr_lock_type lock_type); void print_error(int error, myf errflag); const char * table_type() const; const char ** bas_ext() const; - ulong table_flags(void) const; + ulonglong table_flags(void) const; void prepare_for_alter(); int add_index(TABLE *table_arg, KEY *key_info, uint num_of_keys); int prepare_drop_index(TABLE *table_arg, uint *key_num, uint num_of_keys); @@ -653,7 +660,6 @@ class ha_ndbcluster: public handler enum thr_lock_type lock_type); bool low_byte_first() const; - bool has_transactions(); virtual bool is_injective() const { return true; } @@ -691,7 +697,7 @@ static void set_tabname(const char *pathname, char *tabname); AND ... AND pushed_condN) or less restrictive condition, depending on handler's capabilities. - handler->extra(HA_EXTRA_RESET) call empties the condition stack. + handler->reset() call empties the condition stack. Calls to rnd_init/rnd_end, index_init/index_end etc do not affect the condition stack. The current implementation supports arbitrary AND/OR nested conditions @@ -803,7 +809,10 @@ private: int set_index_key(NdbOperation *, const KEY *key_info, const byte *key_ptr); void print_results(); - ulonglong get_auto_increment(); + virtual void get_auto_increment(ulonglong offset, ulonglong increment, + ulonglong nb_desired_values, + ulonglong *first_value, + ulonglong *nb_reserved_values); int ndb_err(NdbTransaction*); bool uses_blob_value(); @@ -847,6 +856,7 @@ private: char m_tabname[FN_HEADLEN]; ulong m_table_flags; THR_LOCK_DATA m_lock; + bool m_lock_tuple; NDB_SHARE *m_share; NDB_INDEX_DATA m_index[MAX_KEY]; THD_NDB_SHARE *m_thd_ndb_share; @@ -865,7 +875,7 @@ private: bool m_primary_key_update; bool m_write_op; bool m_ignore_no_key; - ha_rows m_rows_to_insert; + ha_rows m_rows_to_insert; // TODO: merge it with handler::estimation_rows_to_insert? ha_rows m_rows_inserted; ha_rows m_bulk_insert_rows; ha_rows m_rows_changed; @@ -903,3 +913,7 @@ int ndbcluster_find_files(THD *thd,const char *db,const char *path, int ndbcluster_table_exists_in_engine(THD* thd, const char *db, const char *name); void ndbcluster_print_error(int error, const NdbOperation *error_op); + +static const char ndbcluster_hton_name[]= "ndbcluster"; +static const int ndbcluster_hton_name_length=sizeof(ndbcluster_hton_name)-1; + diff --git a/sql/ha_ndbcluster_binlog.cc b/sql/ha_ndbcluster_binlog.cc index 144c073d565..af6eb74a236 100644 --- a/sql/ha_ndbcluster_binlog.cc +++ b/sql/ha_ndbcluster_binlog.cc @@ -40,6 +40,12 @@ #define NDB_SCHEMA_TABLE_FILE "./" NDB_REP_DB "/" NDB_SCHEMA_TABLE /* + Timeout for syncing schema events between + mysql servers, and between mysql server and the binlog +*/ +const int opt_ndb_sync_timeout= 120; + +/* Flag showing if the ndb injector thread is running, if so == 1 -1 if it was started but later stopped for some reason 0 if never started @@ -264,6 +270,14 @@ ndbcluster_binlog_close_table(THD *thd, NDB_SHARE *share) DBUG_VOID_RETURN; } + +/* + Creates a TABLE object for the ndb cluster table + + NOTES + This does not open the underlying table +*/ + static int ndbcluster_binlog_open_table(THD *thd, NDB_SHARE *share, TABLE_SHARE *table_share, TABLE *table, @@ -319,6 +333,8 @@ ndbcluster_binlog_open_table(THD *thd, NDB_SHARE *share, share->table_share= table_share; DBUG_ASSERT(share->table == 0); share->table= table; + /* We can't use 'use_all_columns()' as the file object is not setup yet */ + table->column_bitmaps_set_no_signal(&table->s->all_set, &table->s->all_set); #ifndef DBUG_OFF dbug_print_table("table", table); #endif @@ -352,7 +368,7 @@ void ndbcluster_binlog_init_share(NDB_SHARE *share, TABLE *_table) { bitmap_init(&share->subscriber_bitmap[i], (Uint32*)alloc_root(mem_root, max_ndb_nodes/8), - max_ndb_nodes, false); + max_ndb_nodes, FALSE); bitmap_clear_all(&share->subscriber_bitmap[i]); } } @@ -498,6 +514,7 @@ ndbcluster_binlog_log_query(THD *thd, enum_binlog_command binlog_command, { case LOGCOM_CREATE_TABLE: type= SOT_CREATE_TABLE; + DBUG_ASSERT(FALSE); break; case LOGCOM_ALTER_TABLE: type= SOT_ALTER_TABLE; @@ -505,9 +522,11 @@ ndbcluster_binlog_log_query(THD *thd, enum_binlog_command binlog_command, break; case LOGCOM_RENAME_TABLE: type= SOT_RENAME_TABLE; + DBUG_ASSERT(FALSE); break; case LOGCOM_DROP_TABLE: type= SOT_DROP_TABLE; + DBUG_ASSERT(FALSE); break; case LOGCOM_CREATE_DB: type= SOT_CREATE_DB; @@ -519,12 +538,14 @@ ndbcluster_binlog_log_query(THD *thd, enum_binlog_command binlog_command, break; case LOGCOM_DROP_DB: type= SOT_DROP_DB; + DBUG_ASSERT(FALSE); break; } if (log) { ndbcluster_log_schema_op(thd, 0, query, query_length, - db, table_name, 0, 0, type); + db, table_name, 0, 0, type, + 0, 0, 0); } DBUG_VOID_RETURN; } @@ -875,6 +896,7 @@ static void ndbcluster_get_schema(NDB_SHARE *share, /* unpack blob values */ byte* blobs_buffer= 0; uint blobs_buffer_size= 0; + my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set); { ptrdiff_t ptrdiff= 0; int ret= get_ndb_blobs_value(table, share->ndb_value[0], @@ -884,7 +906,7 @@ static void ndbcluster_get_schema(NDB_SHARE *share, { my_free(blobs_buffer, MYF(MY_ALLOW_ZERO_PTR)); DBUG_PRINT("info", ("blob read error")); - DBUG_ASSERT(false); + DBUG_ASSERT(FALSE); } } /* db varchar 1 length byte */ @@ -936,6 +958,7 @@ static void ndbcluster_get_schema(NDB_SHARE *share, s->type= ((Field_long *)*field)->val_int(); /* free blobs buffer */ my_free(blobs_buffer, MYF(MY_ALLOW_ZERO_PTR)); + dbug_tmp_restore_column_map(table->read_set, old_map); } /* @@ -962,6 +985,154 @@ static char *ndb_pack_varchar(const NDBCOL *col, char *buf, } /* + acknowledge handling of schema operation +*/ +static int +ndbcluster_update_slock(THD *thd, + const char *db, + const char *table_name) +{ + DBUG_ENTER("ndbcluster_update_slock"); + if (!schema_share) + { + DBUG_RETURN(0); + } + + const NdbError *ndb_error= 0; + uint32 node_id= g_ndb_cluster_connection->node_id(); + Ndb *ndb= check_ndb_in_thd(thd); + char save_db[FN_HEADLEN]; + strcpy(save_db, ndb->getDatabaseName()); + + char tmp_buf[FN_REFLEN]; + NDBDICT *dict= ndb->getDictionary(); + ndb->setDatabaseName(NDB_REP_DB); + Ndb_table_guard ndbtab_g(dict, NDB_SCHEMA_TABLE); + const NDBTAB *ndbtab= ndbtab_g.get_table(); + NdbTransaction *trans= 0; + int retries= 100; + const NDBCOL *col[SCHEMA_SIZE]; + unsigned sz[SCHEMA_SIZE]; + + MY_BITMAP slock; + uint32 bitbuf[SCHEMA_SLOCK_SIZE/4]; + bitmap_init(&slock, bitbuf, sizeof(bitbuf)*8, false); + + if (ndbtab == 0) + { + abort(); + DBUG_RETURN(0); + } + + { + uint i; + for (i= 0; i < SCHEMA_SIZE; i++) + { + col[i]= ndbtab->getColumn(i); + if (i != SCHEMA_QUERY_I) + { + sz[i]= col[i]->getLength(); + DBUG_ASSERT(sz[i] <= sizeof(tmp_buf)); + } + } + } + + while (1) + { + if ((trans= ndb->startTransaction()) == 0) + goto err; + { + NdbOperation *op= 0; + int r= 0; + + /* read the bitmap exlusive */ + r|= (op= trans->getNdbOperation(ndbtab)) == 0; + DBUG_ASSERT(r == 0); + r|= op->readTupleExclusive(); + DBUG_ASSERT(r == 0); + + /* db */ + ndb_pack_varchar(col[SCHEMA_DB_I], tmp_buf, db, strlen(db)); + r|= op->equal(SCHEMA_DB_I, tmp_buf); + DBUG_ASSERT(r == 0); + /* name */ + ndb_pack_varchar(col[SCHEMA_NAME_I], tmp_buf, table_name, + strlen(table_name)); + r|= op->equal(SCHEMA_NAME_I, tmp_buf); + DBUG_ASSERT(r == 0); + /* slock */ + r|= op->getValue(SCHEMA_SLOCK_I, (char*)slock.bitmap) == 0; + DBUG_ASSERT(r == 0); + } + if (trans->execute(NdbTransaction::NoCommit)) + goto err; + bitmap_clear_bit(&slock, node_id); + { + NdbOperation *op= 0; + int r= 0; + + /* now update the tuple */ + r|= (op= trans->getNdbOperation(ndbtab)) == 0; + DBUG_ASSERT(r == 0); + r|= op->updateTuple(); + DBUG_ASSERT(r == 0); + + /* db */ + ndb_pack_varchar(col[SCHEMA_DB_I], tmp_buf, db, strlen(db)); + r|= op->equal(SCHEMA_DB_I, tmp_buf); + DBUG_ASSERT(r == 0); + /* name */ + ndb_pack_varchar(col[SCHEMA_NAME_I], tmp_buf, table_name, + strlen(table_name)); + r|= op->equal(SCHEMA_NAME_I, tmp_buf); + DBUG_ASSERT(r == 0); + /* slock */ + r|= op->setValue(SCHEMA_SLOCK_I, (char*)slock.bitmap); + DBUG_ASSERT(r == 0); + /* node_id */ + r|= op->setValue(SCHEMA_NODE_ID_I, node_id); + DBUG_ASSERT(r == 0); + /* type */ + r|= op->setValue(SCHEMA_TYPE_I, (uint32)SOT_CLEAR_SLOCK); + DBUG_ASSERT(r == 0); + } + if (trans->execute(NdbTransaction::Commit) == 0) + { + dict->forceGCPWait(); + DBUG_PRINT("info", ("node %d cleared lock on '%s.%s'", + node_id, db, table_name)); + break; + } + err: + const NdbError *this_error= trans ? + &trans->getNdbError() : &ndb->getNdbError(); + if (this_error->status == NdbError::TemporaryError) + { + if (retries--) + { + if (trans) + ndb->closeTransaction(trans); + continue; // retry + } + } + ndb_error= this_error; + break; + } +end: + if (ndb_error) + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR, + ER_GET_ERRMSG, ER(ER_GET_ERRMSG), + ndb_error->code, + ndb_error->message, + "Could not release lock on '%s.%s'", + db, table_name); + if (trans) + ndb->closeTransaction(trans); + ndb->setDatabaseName(save_db); + DBUG_RETURN(0); +} + +/* log query in schema table */ static void ndb_report_waiting(const char *key, @@ -995,7 +1166,8 @@ int ndbcluster_log_schema_op(THD *thd, NDB_SHARE *share, uint32 ndb_table_id, uint32 ndb_table_version, enum SCHEMA_OP_TYPE type, - const char *new_db, const char *new_table_name) + const char *new_db, const char *new_table_name, + int have_lock_open) { DBUG_ENTER("ndbcluster_log_schema_op"); Thd_ndb *thd_ndb= get_thd_ndb(thd); @@ -1060,6 +1232,9 @@ int ndbcluster_log_schema_op(THD *thd, NDB_SHARE *share, case SOT_LOGFILE_GROUP: type_str= "logfile group"; break; + case SOT_TRUNCATE_TABLE: + type_str= "truncate table"; + break; default: abort(); /* should not happen, programming error */ } @@ -1076,12 +1251,12 @@ int ndbcluster_log_schema_op(THD *thd, NDB_SHARE *share, Uint64 epoch= 0; MY_BITMAP schema_subscribers; uint32 bitbuf[sizeof(ndb_schema_object->slock)/4]; - uint32 bitbuf_e[sizeof(bitbuf)]; - bzero((char *)bitbuf_e, sizeof(bitbuf_e)); + char bitbuf_e[sizeof(bitbuf)]; + bzero(bitbuf_e, sizeof(bitbuf_e)); { int i, updated= 0; int no_storage_nodes= g_ndb_cluster_connection->no_db_nodes(); - bitmap_init(&schema_subscribers, bitbuf, sizeof(bitbuf)*8, false); + bitmap_init(&schema_subscribers, bitbuf, sizeof(bitbuf)*8, FALSE); bitmap_set_all(&schema_subscribers); (void) pthread_mutex_lock(&schema_share->mutex); for (i= 0; i < no_storage_nodes; i++) @@ -1096,7 +1271,17 @@ int ndbcluster_log_schema_op(THD *thd, NDB_SHARE *share, } (void) pthread_mutex_unlock(&schema_share->mutex); if (updated) + { bitmap_clear_bit(&schema_subscribers, node_id); + /* + if setting own acknowledge bit it is important that + no other mysqld's are registred, as subsequent code + will cause the original event to be hidden (by blob + merge event code) + */ + if (bitmap_is_clear_all(&schema_subscribers)) + bitmap_set_bit(&schema_subscribers, node_id); + } else bitmap_clear_all(&schema_subscribers); @@ -1209,7 +1394,7 @@ int ndbcluster_log_schema_op(THD *thd, NDB_SHARE *share, { log_db= new_db; log_tab= new_table_name; - log_subscribers= (const char *)bitbuf_e; // no ack expected on this + log_subscribers= bitbuf_e; // no ack expected on this log_type= (uint32)SOT_RENAME_TABLE_NEW; continue; } @@ -1217,7 +1402,6 @@ int ndbcluster_log_schema_op(THD *thd, NDB_SHARE *share, } if (trans->execute(NdbTransaction::Commit) == 0) { - dict->forceGCPWait(); DBUG_PRINT("info", ("logged: %s", query)); break; } @@ -1238,7 +1422,7 @@ err: } end: if (ndb_error) - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR, + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR, ER_GET_ERRMSG, ER(ER_GET_ERRMSG), ndb_error->code, ndb_error->message, @@ -1254,8 +1438,22 @@ end: if (ndb_error == 0 && !bitmap_is_clear_all(&schema_subscribers)) { - int max_timeout= 10; + /* + if own nodeid is set we are a single mysqld registred + as an optimization we update the slock directly + */ + if (bitmap_is_set(&schema_subscribers, node_id)) + ndbcluster_update_slock(thd, db, table_name); + else + dict->forceGCPWait(); + + int max_timeout= opt_ndb_sync_timeout; (void) pthread_mutex_lock(&ndb_schema_object->mutex); + if (have_lock_open) + { + safe_mutex_assert_owner(&LOCK_open); + (void) pthread_mutex_unlock(&LOCK_open); + } while (1) { struct timespec abstime; @@ -1265,7 +1463,8 @@ end: int ret= pthread_cond_timedwait(&injector_cond, &ndb_schema_object->mutex, &abstime); - + if (thd->killed) + break; (void) pthread_mutex_lock(&schema_share->mutex); for (i= 0; i < no_storage_nodes; i++) { @@ -1300,6 +1499,10 @@ end: "distributing", ndb_schema_object->key); } } + if (have_lock_open) + { + (void) pthread_mutex_lock(&LOCK_open); + } (void) pthread_mutex_unlock(&ndb_schema_object->mutex); } @@ -1310,154 +1513,6 @@ end: } /* - acknowledge handling of schema operation -*/ -static int -ndbcluster_update_slock(THD *thd, - const char *db, - const char *table_name) -{ - DBUG_ENTER("ndbcluster_update_slock"); - if (!schema_share) - { - DBUG_RETURN(0); - } - - const NdbError *ndb_error= 0; - uint32 node_id= g_ndb_cluster_connection->node_id(); - Ndb *ndb= check_ndb_in_thd(thd); - char save_db[FN_HEADLEN]; - strcpy(save_db, ndb->getDatabaseName()); - - char tmp_buf[FN_REFLEN]; - NDBDICT *dict= ndb->getDictionary(); - ndb->setDatabaseName(NDB_REP_DB); - Ndb_table_guard ndbtab_g(dict, NDB_SCHEMA_TABLE); - const NDBTAB *ndbtab= ndbtab_g.get_table(); - NdbTransaction *trans= 0; - int retries= 100; - const NDBCOL *col[SCHEMA_SIZE]; - unsigned sz[SCHEMA_SIZE]; - - MY_BITMAP slock; - uint32 bitbuf[SCHEMA_SLOCK_SIZE/4]; - bitmap_init(&slock, bitbuf, sizeof(bitbuf)*8, false); - - if (ndbtab == 0) - { - abort(); - DBUG_RETURN(0); - } - - { - uint i; - for (i= 0; i < SCHEMA_SIZE; i++) - { - col[i]= ndbtab->getColumn(i); - if (i != SCHEMA_QUERY_I) - { - sz[i]= col[i]->getLength(); - DBUG_ASSERT(sz[i] <= sizeof(tmp_buf)); - } - } - } - - while (1) - { - if ((trans= ndb->startTransaction()) == 0) - goto err; - { - NdbOperation *op= 0; - int r= 0; - - /* read the bitmap exlusive */ - r|= (op= trans->getNdbOperation(ndbtab)) == 0; - DBUG_ASSERT(r == 0); - r|= op->readTupleExclusive(); - DBUG_ASSERT(r == 0); - - /* db */ - ndb_pack_varchar(col[SCHEMA_DB_I], tmp_buf, db, strlen(db)); - r|= op->equal(SCHEMA_DB_I, tmp_buf); - DBUG_ASSERT(r == 0); - /* name */ - ndb_pack_varchar(col[SCHEMA_NAME_I], tmp_buf, table_name, - strlen(table_name)); - r|= op->equal(SCHEMA_NAME_I, tmp_buf); - DBUG_ASSERT(r == 0); - /* slock */ - r|= op->getValue(SCHEMA_SLOCK_I, (char*)slock.bitmap) == 0; - DBUG_ASSERT(r == 0); - } - if (trans->execute(NdbTransaction::NoCommit)) - goto err; - bitmap_clear_bit(&slock, node_id); - { - NdbOperation *op= 0; - int r= 0; - - /* now update the tuple */ - r|= (op= trans->getNdbOperation(ndbtab)) == 0; - DBUG_ASSERT(r == 0); - r|= op->updateTuple(); - DBUG_ASSERT(r == 0); - - /* db */ - ndb_pack_varchar(col[SCHEMA_DB_I], tmp_buf, db, strlen(db)); - r|= op->equal(SCHEMA_DB_I, tmp_buf); - DBUG_ASSERT(r == 0); - /* name */ - ndb_pack_varchar(col[SCHEMA_NAME_I], tmp_buf, table_name, - strlen(table_name)); - r|= op->equal(SCHEMA_NAME_I, tmp_buf); - DBUG_ASSERT(r == 0); - /* slock */ - r|= op->setValue(SCHEMA_SLOCK_I, (char*)slock.bitmap); - DBUG_ASSERT(r == 0); - /* node_id */ - r|= op->setValue(SCHEMA_NODE_ID_I, node_id); - DBUG_ASSERT(r == 0); - /* type */ - r|= op->setValue(SCHEMA_TYPE_I, (uint32)SOT_CLEAR_SLOCK); - DBUG_ASSERT(r == 0); - } - if (trans->execute(NdbTransaction::Commit) == 0) - { - dict->forceGCPWait(); - DBUG_PRINT("info", ("node %d cleared lock on '%s.%s'", - node_id, db, table_name)); - break; - } - err: - const NdbError *this_error= trans ? - &trans->getNdbError() : &ndb->getNdbError(); - if (this_error->status == NdbError::TemporaryError) - { - if (retries--) - { - if (trans) - ndb->closeTransaction(trans); - continue; // retry - } - } - ndb_error= this_error; - break; - } -end: - if (ndb_error) - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR, - ER_GET_ERRMSG, ER(ER_GET_ERRMSG), - ndb_error->code, - ndb_error->message, - "Could not release lock on '%s.%s'", - db, table_name); - if (trans) - ndb->closeTransaction(trans); - ndb->setDatabaseName(save_db); - DBUG_RETURN(0); -} - -/* Handle _non_ data events from the storage nodes */ int @@ -1677,20 +1732,29 @@ ndb_binlog_thread_handle_schema_event(THD *thd, Ndb *ndb, Cluster_schema *schema= (Cluster_schema *) sql_alloc(sizeof(Cluster_schema)); MY_BITMAP slock; - bitmap_init(&slock, schema->slock, 8*SCHEMA_SLOCK_SIZE, false); + bitmap_init(&slock, schema->slock, 8*SCHEMA_SLOCK_SIZE, FALSE); uint node_id= g_ndb_cluster_connection->node_id(); ndbcluster_get_schema(tmp_share, schema); + enum SCHEMA_OP_TYPE schema_type= (enum SCHEMA_OP_TYPE)schema->type; + DBUG_PRINT("info", + ("%s.%s: log query_length: %d query: '%s' type: %d", + schema->db, schema->name, + schema->query_length, schema->query, + schema_type)); + if (schema_type == SOT_CLEAR_SLOCK) + { + /* + handle slock after epoch is completed to ensure that + schema events get inserted in the binlog after any data + events + */ + post_epoch_log_list->push_back(schema, mem_root); + DBUG_RETURN(0); + } if (schema->node_id != node_id) { int log_query= 0, post_epoch_unlock= 0; - DBUG_PRINT("info", - ("%s.%s: log query_length: %d query: '%s' type: %d", - schema->db, schema->name, - schema->query_length, schema->query, - schema->type)); - char key[FN_REFLEN]; - build_table_filename(key, sizeof(key), schema->db, schema->name, ""); - switch ((enum SCHEMA_OP_TYPE)schema->type) + switch (schema_type) { case SOT_DROP_TABLE: // fall through @@ -1704,6 +1768,7 @@ ndb_binlog_thread_handle_schema_event(THD *thd, Ndb *ndb, post_epoch_unlock= 1; break; case SOT_CREATE_TABLE: + case SOT_TRUNCATE_TABLE: pthread_mutex_lock(&LOCK_open); if (ndb_create_table_from_engine(thd, schema->db, schema->name)) { @@ -1738,30 +1803,12 @@ ndb_binlog_thread_handle_schema_event(THD *thd, Ndb *ndb, TRUE, /* print error */ FALSE); /* binlog the query */ break; - case SOT_CLEAR_SLOCK: - { - pthread_mutex_lock(&ndbcluster_mutex); - NDB_SCHEMA_OBJECT *ndb_schema_object= - (NDB_SCHEMA_OBJECT*) hash_search(&ndb_schema_objects, - (byte*) key, strlen(key)); - if (ndb_schema_object) - { - pthread_mutex_lock(&ndb_schema_object->mutex); - memcpy(ndb_schema_object->slock, schema->slock, - sizeof(ndb_schema_object->slock)); - DBUG_DUMP("ndb_schema_object->slock_bitmap.bitmap", - (char*)ndb_schema_object->slock_bitmap.bitmap, - no_bytes_in_map(&ndb_schema_object->slock_bitmap)); - pthread_mutex_unlock(&ndb_schema_object->mutex); - pthread_cond_signal(&injector_cond); - } - pthread_mutex_unlock(&ndbcluster_mutex); - DBUG_RETURN(0); - } case SOT_TABLESPACE: case SOT_LOGFILE_GROUP: log_query= 1; break; + case SOT_CLEAR_SLOCK: + abort(); } if (log_query && ndb_binlog_running) { @@ -1902,10 +1949,30 @@ ndb_binlog_thread_handle_schema_event_post_epoch(THD *thd, schema->type)); int log_query= 0; { + enum SCHEMA_OP_TYPE schema_type= (enum SCHEMA_OP_TYPE)schema->type; char key[FN_REFLEN]; build_table_filename(key, sizeof(key), schema->db, schema->name, ""); - NDB_SHARE *share= get_share(key, 0, false, false); - enum SCHEMA_OP_TYPE schema_type= (enum SCHEMA_OP_TYPE)schema->type; + if (schema_type == SOT_CLEAR_SLOCK) + { + pthread_mutex_lock(&ndbcluster_mutex); + NDB_SCHEMA_OBJECT *ndb_schema_object= + (NDB_SCHEMA_OBJECT*) hash_search(&ndb_schema_objects, + (byte*) key, strlen(key)); + if (ndb_schema_object) + { + pthread_mutex_lock(&ndb_schema_object->mutex); + memcpy(ndb_schema_object->slock, schema->slock, + sizeof(ndb_schema_object->slock)); + DBUG_DUMP("ndb_schema_object->slock_bitmap.bitmap", + (char*)ndb_schema_object->slock_bitmap.bitmap, + no_bytes_in_map(&ndb_schema_object->slock_bitmap)); + pthread_mutex_unlock(&ndb_schema_object->mutex); + pthread_cond_signal(&injector_cond); + } + pthread_mutex_unlock(&ndbcluster_mutex); + continue; + } + NDB_SHARE *share= get_share(key, 0, FALSE, FALSE); switch (schema_type) { case SOT_DROP_DB: @@ -1969,7 +2036,7 @@ ndb_binlog_thread_handle_schema_event_post_epoch(THD *thd, } break; default: - DBUG_ASSERT(false); + DBUG_ASSERT(FALSE); } if (share) { @@ -2046,18 +2113,20 @@ static int open_binlog_index(THD *thd, TABLE_LIST *tables, } *binlog_index= tables->table; thd->proc_info= save_proc_info; + (*binlog_index)->use_all_columns(); return 0; } + /* Insert one row in the binlog_index */ + int ndb_add_binlog_index(THD *thd, void *_row) { Binlog_index_row &row= *(Binlog_index_row *) _row; int error= 0; bool need_reopen; - /* Turn of binlogging to prevent the table changes to be written to the binary log. @@ -2099,10 +2168,9 @@ int ndb_add_binlog_index(THD *thd, void *_row) binlog_index->field[5]->store(row.n_deletes); binlog_index->field[6]->store(row.n_schemaops); - int r; - if ((r= binlog_index->file->ha_write_row(binlog_index->record[0]))) + if ((error= binlog_index->file->ha_write_row(binlog_index->record[0]))) { - sql_print_error("NDB Binlog: Writing row to binlog_index: %d", r); + sql_print_error("NDB Binlog: Writing row to binlog_index: %d", error); error= -1; goto add_binlog_index_err; } @@ -2236,7 +2304,7 @@ int ndbcluster_create_binlog_setup(Ndb *ndb, const char *key, } /* Create share which is needed to hold replication information */ - if (!(share= get_share(key, 0, true, true))) + if (!(share= get_share(key, 0, TRUE, TRUE))) { sql_print_error("NDB Binlog: " "allocating table share for %s failed", key); @@ -2328,6 +2396,7 @@ ndbcluster_create_event(Ndb *ndb, const NDBTAB *ndbtab, const char *event_name, NDB_SHARE *share, int push_warning) { + THD *thd= current_thd; DBUG_ENTER("ndbcluster_create_event"); DBUG_PRINT("info", ("table=%s version=%d event=%s share=%s", ndbtab->getName(), ndbtab->getObjectVersion(), @@ -2357,12 +2426,12 @@ ndbcluster_create_event(Ndb *ndb, const NDBTAB *ndbtab, "with BLOB attribute and no PK is not supported", share->key); if (push_warning) - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR, + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR, ER_ILLEGAL_HA_CREATE_OPTION, ER(ER_ILLEGAL_HA_CREATE_OPTION), - ndbcluster_hton.name, + ndbcluster_hton_name, "Binlog of table with BLOB attribute and no PK"); - + share->flags|= NSF_NO_BINLOG; DBUG_RETURN(-1); } @@ -2386,7 +2455,7 @@ ndbcluster_create_event(Ndb *ndb, const NDBTAB *ndbtab, } } if (share->flags & NSF_BLOB_FLAG) - my_event.mergeEvents(true); + my_event.mergeEvents(TRUE); /* add all columns to the event */ int n_cols= ndbtab->getNoOfColumns(); @@ -2401,7 +2470,7 @@ ndbcluster_create_event(Ndb *ndb, const NDBTAB *ndbtab, failed, print a warning */ if (push_warning) - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR, + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR, ER_GET_ERRMSG, ER(ER_GET_ERRMSG), dict->getNdbError().code, dict->getNdbError().message, "NDB"); @@ -2429,7 +2498,7 @@ ndbcluster_create_event(Ndb *ndb, const NDBTAB *ndbtab, dict->dropEvent(my_event.getName())) { if (push_warning) - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR, + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR, ER_GET_ERRMSG, ER(ER_GET_ERRMSG), dict->getNdbError().code, dict->getNdbError().message, "NDB"); @@ -2448,7 +2517,7 @@ ndbcluster_create_event(Ndb *ndb, const NDBTAB *ndbtab, if (dict->createEvent(my_event)) { if (push_warning) - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR, + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR, ER_GET_ERRMSG, ER(ER_GET_ERRMSG), dict->getNdbError().code, dict->getNdbError().message, "NDB"); @@ -2461,7 +2530,7 @@ ndbcluster_create_event(Ndb *ndb, const NDBTAB *ndbtab, DBUG_RETURN(-1); } #ifdef NDB_BINLOG_EXTRA_WARNINGS - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR, + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR, ER_GET_ERRMSG, ER(ER_GET_ERRMSG), 0, "NDB Binlog: Removed trailing event", "NDB"); @@ -2490,6 +2559,7 @@ int ndbcluster_create_event_ops(NDB_SHARE *share, const NDBTAB *ndbtab, const char *event_name) { + THD *thd= current_thd; /* we are in either create table or rename table so table should be locked, hence we can work with the share without locks @@ -2563,7 +2633,7 @@ ndbcluster_create_event_ops(NDB_SHARE *share, const NDBTAB *ndbtab, { sql_print_error("NDB Binlog: Creating NdbEventOperation failed for" " %s",event_name); - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR, + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR, ER_GET_ERRMSG, ER(ER_GET_ERRMSG), ndb->getNdbError().code, ndb->getNdbError().message, @@ -2573,7 +2643,7 @@ ndbcluster_create_event_ops(NDB_SHARE *share, const NDBTAB *ndbtab, } if (share->flags & NSF_BLOB_FLAG) - op->mergeEvents(true); // currently not inherited from event + op->mergeEvents(TRUE); // currently not inherited from event DBUG_PRINT("info", ("share->ndb_value[0]: 0x%x", share->ndb_value[0])); @@ -2613,7 +2683,7 @@ ndbcluster_create_event_ops(NDB_SHARE *share, const NDBTAB *ndbtab, sql_print_error("NDB Binlog: Creating NdbEventOperation" " blob field %u handles failed (code=%d) for %s", j, op->getNdbError().code, event_name); - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR, + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR, ER_GET_ERRMSG, ER(ER_GET_ERRMSG), op->getNdbError().code, op->getNdbError().message, @@ -2650,7 +2720,7 @@ ndbcluster_create_event_ops(NDB_SHARE *share, const NDBTAB *ndbtab, retries= 0; if (retries == 0) { - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR, + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR, ER_GET_ERRMSG, ER(ER_GET_ERRMSG), op->getNdbError().code, op->getNdbError().message, "NDB"); @@ -2698,6 +2768,7 @@ ndbcluster_handle_drop_table(Ndb *ndb, const char *event_name, NDB_SHARE *share, const char *type_str) { DBUG_ENTER("ndbcluster_handle_drop_table"); + THD *thd= current_thd; NDBDICT *dict= ndb->getDictionary(); if (event_name && dict->dropEvent(event_name)) @@ -2705,7 +2776,7 @@ ndbcluster_handle_drop_table(Ndb *ndb, const char *event_name, if (dict->getNdbError().code != 4710) { /* drop event failed for some reason, issue a warning */ - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR, + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR, ER_GET_ERRMSG, ER(ER_GET_ERRMSG), dict->getNdbError().code, dict->getNdbError().message, "NDB"); @@ -2720,7 +2791,7 @@ ndbcluster_handle_drop_table(Ndb *ndb, const char *event_name, share->op->getState() == NdbEventOperation::EO_EXECUTING && dict->getNdbError().code != 4009) { - DBUG_ASSERT(false); + DBUG_ASSERT(FALSE); DBUG_RETURN(-1); } } @@ -2743,10 +2814,14 @@ ndbcluster_handle_drop_table(Ndb *ndb, const char *event_name, these out of order, thus we are keeping the SYNC_DROP_ defined for now. */ + const char *save_proc_info= thd->proc_info; #define SYNC_DROP_ #ifdef SYNC_DROP_ + thd->proc_info= "Syncing ndb table schema operation and binlog"; (void) pthread_mutex_lock(&share->mutex); - int max_timeout= 10; + safe_mutex_assert_owner(&LOCK_open); + (void) pthread_mutex_unlock(&LOCK_open); + int max_timeout= opt_ndb_sync_timeout; while (share->op) { struct timespec abstime; @@ -2754,7 +2829,8 @@ ndbcluster_handle_drop_table(Ndb *ndb, const char *event_name, int ret= pthread_cond_timedwait(&injector_cond, &share->mutex, &abstime); - if (share->op == 0) + if (thd->killed || + share->op == 0) break; if (ret) { @@ -2770,6 +2846,7 @@ ndbcluster_handle_drop_table(Ndb *ndb, const char *event_name, type_str, share->key); } } + (void) pthread_mutex_lock(&LOCK_open); (void) pthread_mutex_unlock(&share->mutex); #else (void) pthread_mutex_lock(&share->mutex); @@ -2777,6 +2854,7 @@ ndbcluster_handle_drop_table(Ndb *ndb, const char *event_name, share->op= 0; (void) pthread_mutex_unlock(&share->mutex); #endif + thd->proc_info= save_proc_info; DBUG_RETURN(0); } @@ -2841,7 +2919,7 @@ ndb_binlog_thread_handle_non_data_event(THD *thd, Ndb *ndb, /* make sure to flush any pending events as they can be dependent on one of the tables being changed below */ - thd->binlog_flush_pending_rows_event(true); + thd->binlog_flush_pending_rows_event(TRUE); switch (type) { @@ -2925,7 +3003,7 @@ ndb_binlog_thread_handle_data_event(Ndb *ndb, NdbEventOperation *pOp, /* Potential buffer for the bitmap */ uint32 bitbuf[128 / (sizeof(uint32) * 8)]; bitmap_init(&b, n_fields <= sizeof(bitbuf) * 8 ? bitbuf : NULL, - n_fields, false); + n_fields, FALSE); bitmap_set_all(&b); /* @@ -2958,7 +3036,7 @@ ndb_binlog_thread_handle_data_event(Ndb *ndb, NdbEventOperation *pOp, } ndb_unpack_record(table, share->ndb_value[0], &b, table->record[0]); int ret= trans.write_row(::server_id, - injector::transaction::table(table, true), + injector::transaction::table(table, TRUE), &b, n_fields, table->record[0]); DBUG_ASSERT(ret == 0); } @@ -2996,7 +3074,7 @@ ndb_binlog_thread_handle_data_event(Ndb *ndb, NdbEventOperation *pOp, ndb_unpack_record(table, share->ndb_value[n], &b, table->record[n]); DBUG_EXECUTE("info", print_records(table, table->record[n]);); int ret= trans.delete_row(::server_id, - injector::transaction::table(table, true), + injector::transaction::table(table, TRUE), &b, n_fields, table->record[n]); DBUG_ASSERT(ret == 0); } @@ -3023,7 +3101,7 @@ ndb_binlog_thread_handle_data_event(Ndb *ndb, NdbEventOperation *pOp, since table has a primary key, we can do a write using only after values */ - trans.write_row(::server_id, injector::transaction::table(table, true), + trans.write_row(::server_id, injector::transaction::table(table, TRUE), &b, n_fields, table->record[0]);// after values } else @@ -3043,7 +3121,7 @@ ndb_binlog_thread_handle_data_event(Ndb *ndb, NdbEventOperation *pOp, ndb_unpack_record(table, share->ndb_value[1], &b, table->record[1]); DBUG_EXECUTE("info", print_records(table, table->record[1]);); int ret= trans.update_row(::server_id, - injector::transaction::table(table, true), + injector::transaction::table(table, TRUE), &b, n_fields, table->record[1], // before values table->record[0]);// after values @@ -3135,7 +3213,7 @@ static NDB_SCHEMA_OBJECT *ndb_get_schema_object(const char *key, } pthread_mutex_init(&ndb_schema_object->mutex, MY_MUTEX_INIT_FAST); bitmap_init(&ndb_schema_object->slock_bitmap, ndb_schema_object->slock, - sizeof(ndb_schema_object->slock)*8, false); + sizeof(ndb_schema_object->slock)*8, FALSE); bitmap_clear_all(&ndb_schema_object->slock_bitmap); break; } @@ -3546,7 +3624,7 @@ restart: inj->new_trans(thd, &trans); } DBUG_PRINT("info", ("use_table: %.*s", name.length, name.str)); - injector::transaction::table tbl(table, true); + injector::transaction::table tbl(table, TRUE); int ret= trans.use_table(::server_id, tbl); DBUG_ASSERT(ret == 0); } @@ -3559,20 +3637,14 @@ restart: const LEX_STRING& name=table->s->table_name; DBUG_PRINT("info", ("use_table: %.*s", name.length, name.str)); - injector::transaction::table tbl(table, true); + injector::transaction::table tbl(table, TRUE); int ret= trans.use_table(::server_id, tbl); DBUG_ASSERT(ret == 0); - - MY_BITMAP b; - uint32 bitbuf; - DBUG_ASSERT(table->s->fields <= sizeof(bitbuf) * 8); - bitmap_init(&b, &bitbuf, table->s->fields, false); - bitmap_set_all(&b); table->field[0]->store((longlong)::server_id); table->field[1]->store((longlong)gci); trans.write_row(::server_id, - injector::transaction::table(table, true), - &b, table->s->fields, + injector::transaction::table(table, TRUE), + &table->s->all_set, table->s->fields, table->record[0]); } else @@ -3822,7 +3894,7 @@ ndbcluster_show_status_binlog(THD* thd, stat_print_fn *stat_print, ndb_latest_received_binlog_epoch, ndb_latest_handled_binlog_epoch, ndb_latest_applied_binlog_epoch); - if (stat_print(thd, ndbcluster_hton.name, strlen(ndbcluster_hton.name), + if (stat_print(thd, ndbcluster_hton_name, ndbcluster_hton_name_length, "binlog", strlen("binlog"), buf, buflen)) DBUG_RETURN(TRUE); diff --git a/sql/ha_ndbcluster_binlog.h b/sql/ha_ndbcluster_binlog.h index d82cdccb1b9..58bf7517df5 100644 --- a/sql/ha_ndbcluster_binlog.h +++ b/sql/ha_ndbcluster_binlog.h @@ -49,7 +49,8 @@ enum SCHEMA_OP_TYPE SOT_CLEAR_SLOCK= 7, SOT_TABLESPACE= 8, SOT_LOGFILE_GROUP= 9, - SOT_RENAME_TABLE= 10 + SOT_RENAME_TABLE= 10, + SOT_TRUNCATE_TABLE= 11 }; const uint max_ndb_nodes= 64; /* multiple of 32 */ @@ -138,8 +139,9 @@ int ndbcluster_log_schema_op(THD *thd, NDB_SHARE *share, uint32 ndb_table_id, uint32 ndb_table_version, enum SCHEMA_OP_TYPE type, - const char *new_db= 0, - const char *new_table_name= 0); + const char *new_db, + const char *new_table_name, + int have_lock_open); int ndbcluster_handle_drop_table(Ndb *ndb, const char *event_name, NDB_SHARE *share, const char *type_str); diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index af0556f1e6f..f151d89bb1a 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -69,50 +69,23 @@ static PARTITION_SHARE *get_share(const char *table_name, TABLE * table); MODULE create/delete handler object ****************************************************************************/ -static handler *partition_create_handler(TABLE_SHARE *share); +static handler *partition_create_handler(TABLE_SHARE *share, + MEM_ROOT *mem_root); static uint partition_flags(); static uint alter_table_flags(uint flags); -static const char partition_hton_name[]= "partition"; -static const char partition_hton_comment[]= "Partition Storage Engine Helper"; - -handlerton partition_hton = { - MYSQL_HANDLERTON_INTERFACE_VERSION, - partition_hton_name, - SHOW_OPTION_YES, - partition_hton_comment, /* A comment used by SHOW to describe an engine */ - DB_TYPE_PARTITION_DB, - 0, /* Method that initializes a storage engine */ - 0, /* slot */ - 0, /* savepoint size */ - NULL /*ndbcluster_close_connection*/, - NULL, /* savepoint_set */ - NULL, /* savepoint_rollback */ - NULL, /* savepoint_release */ - NULL /*ndbcluster_commit*/, - NULL /*ndbcluster_rollback*/, - NULL, /* prepare */ - NULL, /* recover */ - NULL, /* commit_by_xid */ - NULL, /* rollback_by_xid */ - NULL, - NULL, - NULL, - partition_create_handler, /* Create a new handler */ - NULL, /* Drop a database */ - NULL, /* Panic call */ - NULL, /* Start Consistent Snapshot */ - NULL, /* Flush logs */ - NULL, /* Show status */ - partition_flags, /* Partition flags */ - alter_table_flags, /* Partition flags */ - NULL, /* Alter Tablespace */ - NULL, /* Fill FILES table */ - HTON_NOT_USER_SELECTABLE | HTON_HIDDEN, - NULL, /* binlog_func */ - NULL, /* binlog_log_query */ - NULL /* release_temporary_latches */ -}; +handlerton partition_hton; + +static int partition_initialize() +{ + partition_hton.state= SHOW_OPTION_YES; + partition_hton.db_type= DB_TYPE_PARTITION_DB; + partition_hton.create= partition_create_handler; + partition_hton.partition_flags= partition_flags; + partition_hton.alter_table_flags= alter_table_flags; + partition_hton.flags= HTON_NOT_USER_SELECTABLE | HTON_HIDDEN; + return 0; +} /* Create new partition handler @@ -125,9 +98,16 @@ handlerton partition_hton = { New partition object */ -static handler *partition_create_handler(TABLE_SHARE *share) +static handler *partition_create_handler(TABLE_SHARE *share, + MEM_ROOT *mem_root) { - return new ha_partition(share); + ha_partition *file= new (mem_root) ha_partition(share); + if (file && file->initialise_partition(mem_root)) + { + delete file; + file= 0; + } + return file; } /* @@ -229,7 +209,6 @@ void ha_partition::init_handler_variables() m_reorged_parts= 0; m_added_file= NULL; m_tot_parts= 0; - m_has_transactions= 0; m_pkey_is_clustered= 0; m_lock_type= F_UNLCK; m_part_spec.start_part= NO_CURRENT_PART_ID; @@ -301,7 +280,8 @@ ha_partition::~ha_partition() Initialise partition handler object SYNOPSIS - ha_initialise() + initialise_partition() + mem_root Allocate memory through this RETURN VALUE 1 Error @@ -341,16 +321,16 @@ ha_partition::~ha_partition() */ -int ha_partition::ha_initialise() +bool ha_partition::initialise_partition(MEM_ROOT *mem_root) { handler **file_array, *file; - DBUG_ENTER("ha_partition::ha_initialise"); + DBUG_ENTER("ha_partition::initialise_partition"); if (m_create_handler) { m_tot_parts= m_part_info->get_tot_partitions(); DBUG_ASSERT(m_tot_parts > 0); - if (new_handlers_from_part_info()) + if (new_handlers_from_part_info(mem_root)) DBUG_RETURN(1); } else if (!table_share || !table_share->normalized_path.str) @@ -363,7 +343,7 @@ int ha_partition::ha_initialise() m_table_flags|= HA_FILE_BASED | HA_REC_NOT_IN_SEQ; DBUG_RETURN(0); } - else if (get_from_handler_file(table_share->normalized_path.str)) + else if (get_from_handler_file(table_share->normalized_path.str, mem_root)) { mem_alloc_error(2); DBUG_RETURN(1); @@ -377,12 +357,11 @@ int ha_partition::ha_initialise() other parameters are calculated on demand. HA_FILE_BASED is always set for partition handler since we use a special file for handling names of partitions, engine types. - HA_CAN_GEOMETRY, HA_CAN_FULLTEXT, HA_CAN_SQL_HANDLER, HA_DUPP_POS, + HA_CAN_GEOMETRY, HA_CAN_FULLTEXT, HA_CAN_SQL_HANDLER, HA_DUPLICATE_POS, HA_CAN_INSERT_DELAYED is disabled until further investigated. */ m_table_flags= m_file[0]->table_flags(); m_low_byte_first= m_file[0]->low_byte_first(); - m_has_transactions= TRUE; m_pkey_is_clustered= TRUE; file_array= m_file; do @@ -394,13 +373,11 @@ int ha_partition::ha_initialise() my_error(ER_MIX_HANDLER_ERROR, MYF(0)); DBUG_RETURN(1); } - if (!file->has_transactions()) - m_has_transactions= FALSE; if (!file->primary_key_is_clustered()) m_pkey_is_clustered= FALSE; m_table_flags&= file->table_flags(); } while (*(++file_array)); - m_table_flags&= ~(HA_CAN_GEOMETRY | HA_CAN_FULLTEXT | HA_DUPP_POS | + m_table_flags&= ~(HA_CAN_GEOMETRY | HA_CAN_FULLTEXT | HA_DUPLICATE_POS | HA_CAN_SQL_HANDLER | HA_CAN_INSERT_DELAYED); m_table_flags|= HA_FILE_BASED | HA_REC_NOT_IN_SEQ; DBUG_RETURN(0); @@ -634,6 +611,8 @@ int ha_partition::drop_partitions(const char *path) DBUG_PRINT("info", ("Drop subpartition %s", part_name_buff)); if ((ret_error= file->delete_table((const char *) part_name_buff))) error= ret_error; + if (deactivate_ddl_log_entry(sub_elem->log_entry->entry_pos)) + error= 1; } while (++j < no_subparts); } else @@ -645,6 +624,8 @@ int ha_partition::drop_partitions(const char *path) DBUG_PRINT("info", ("Drop partition %s", part_name_buff)); if ((ret_error= file->delete_table((const char *) part_name_buff))) error= ret_error; + if (deactivate_ddl_log_entry(part_elem->log_entry->entry_pos)) + error= 1; } if (part_elem->part_state == PART_IS_CHANGED) part_elem->part_state= PART_NORMAL; @@ -652,6 +633,7 @@ int ha_partition::drop_partitions(const char *path) part_elem->part_state= PART_IS_DROPPED; } } while (++i < no_parts); + VOID(sync_ddl_log()); DBUG_RETURN(error); } @@ -768,6 +750,7 @@ int ha_partition::rename_partitions(const char *path) */ part_elem= part_it++; if (part_elem->part_state == PART_IS_CHANGED || + part_elem->part_state == PART_TO_BE_DROPPED || (part_elem->part_state == PART_IS_ADDED && temp_partitions)) { if (m_is_sub_partitioned) @@ -1386,9 +1369,10 @@ int ha_partition::change_partitions(HA_CREATE_INFO *create_info, uint j= 0; do { - if (!(new_file_array[part_count++]= get_new_handler(table->s, - thd->mem_root, - part_elem->engine_type))) + if (!(new_file_array[part_count++]= + get_new_handler(table->s, + thd->mem_root, + part_elem->engine_type))) { mem_alloc_error(sizeof(handler)); DBUG_RETURN(ER_OUTOFMEMORY); @@ -1642,7 +1626,7 @@ uint ha_partition::del_ren_cre_table(const char *from, handler **file; DBUG_ENTER("del_ren_cre_table()"); - if (get_from_handler_file(from)) + if (get_from_handler_file(from, current_thd->mem_root)) DBUG_RETURN(TRUE); DBUG_ASSERT(m_file_buffer); name_buffer_ptr= m_name_buffer_ptr; @@ -1951,7 +1935,6 @@ void ha_partition::clear_handler_file() my_free((char*) m_file_buffer, MYF(MY_ALLOW_ZERO_PTR)); my_free((char*) m_engine_array, MYF(MY_ALLOW_ZERO_PTR)); m_file_buffer= NULL; - m_name_buffer_ptr= NULL; m_engine_array= NULL; } @@ -1960,29 +1943,29 @@ void ha_partition::clear_handler_file() SYNOPSIS create_handlers() + mem_root Allocate memory through this RETURN VALUE TRUE Error FALSE Success */ -bool ha_partition::create_handlers() +bool ha_partition::create_handlers(MEM_ROOT *mem_root) { uint i; uint alloc_len= (m_tot_parts + 1) * sizeof(handler*); DBUG_ENTER("create_handlers"); - if (!(m_file= (handler **) sql_alloc(alloc_len))) + if (!(m_file= (handler **) alloc_root(mem_root, alloc_len))) DBUG_RETURN(TRUE); - bzero(m_file, alloc_len); + bzero((char*) m_file, alloc_len); for (i= 0; i < m_tot_parts; i++) { - if (!(m_file[i]= get_new_handler(table_share, current_thd->mem_root, + if (!(m_file[i]= get_new_handler(table_share, mem_root, m_engine_array[i]))) DBUG_RETURN(TRUE); DBUG_PRINT("info", ("engine_type: %u", m_engine_array[i])); } - m_file[m_tot_parts]= 0; /* For the moment we only support partition over the same table engine */ if (m_engine_array[0] == &myisam_hton) { @@ -2003,13 +1986,14 @@ bool ha_partition::create_handlers() SYNOPSIS new_handlers_from_part_info() + mem_root Allocate memory through this RETURN VALUE TRUE Error FALSE Success */ -bool ha_partition::new_handlers_from_part_info() +bool ha_partition::new_handlers_from_part_info(MEM_ROOT *mem_root) { uint i, j, part_count; partition_element *part_elem; @@ -2018,12 +2002,12 @@ bool ha_partition::new_handlers_from_part_info() THD *thd= current_thd; DBUG_ENTER("ha_partition::new_handlers_from_part_info"); - if (!(m_file= (handler **) sql_alloc(alloc_len))) + if (!(m_file= (handler **) alloc_root(mem_root, alloc_len))) { mem_alloc_error(alloc_len); goto error_end; } - bzero(m_file, alloc_len); + bzero((char*) m_file, alloc_len); DBUG_ASSERT(m_part_info->no_parts > 0); i= 0; @@ -2039,8 +2023,8 @@ bool ha_partition::new_handlers_from_part_info() { for (j= 0; j < m_part_info->no_subparts; j++) { - if (!(m_file[i]= get_new_handler(table_share, thd->mem_root, - part_elem->engine_type))) + if (!(m_file[part_count++]= get_new_handler(table_share, mem_root, + part_elem->engine_type))) goto error; DBUG_PRINT("info", ("engine_type: %u", (uint) ha_legacy_type(part_elem->engine_type))); @@ -2048,7 +2032,7 @@ bool ha_partition::new_handlers_from_part_info() } else { - if (!(m_file[part_count++]= get_new_handler(table_share, thd->mem_root, + if (!(m_file[part_count++]= get_new_handler(table_share, mem_root, part_elem->engine_type))) goto error; DBUG_PRINT("info", ("engine_type: %u", @@ -2074,6 +2058,7 @@ error_end: SYNOPSIS get_from_handler_file() name Full path of table name + mem_root Allocate memory through this RETURN VALUE TRUE Error @@ -2084,7 +2069,7 @@ error_end: partitions. */ -bool ha_partition::get_from_handler_file(const char *name) +bool ha_partition::get_from_handler_file(const char *name, MEM_ROOT *mem_root) { char buff[FN_REFLEN], *address_tot_name_len; File file; @@ -2123,7 +2108,8 @@ bool ha_partition::get_from_handler_file(const char *name) goto err2; for (i= 0; i < m_tot_parts; i++) engine_array[i]= ha_resolve_by_legacy_type(current_thd, - (enum legacy_db_type) *(uchar *) ((file_buffer) + 12 + i)); + (enum legacy_db_type) + *(uchar *) ((file_buffer) + 12 + i)); address_tot_name_len= file_buffer + 12 + 4 * tot_partition_words; tot_name_words= (uint4korr(address_tot_name_len) + 3) / 4; if (len_words != (tot_partition_words + tot_name_words + 4)) @@ -2133,7 +2119,7 @@ bool ha_partition::get_from_handler_file(const char *name) m_file_buffer= file_buffer; // Will be freed in clear_handler_file() m_name_buffer_ptr= name_buffer_ptr; m_engine_array= engine_array; - if (!m_file && create_handlers()) + if (!m_file && create_handlers(mem_root)) { clear_handler_file(); DBUG_RETURN(TRUE); @@ -2187,7 +2173,7 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked) m_mode= mode; m_open_test_lock= test_if_locked; m_part_field_array= m_part_info->full_part_field_array; - if (get_from_handler_file(name)) + if (get_from_handler_file(name, &table->mem_root)) DBUG_RETURN(1); m_start_key.length= 0; m_rec0= table->record[0]; @@ -2224,6 +2210,8 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked) DBUG_RETURN(1); bitmap_set_all(&(m_part_info->used_partitions)); + /* Recalculate table flags as they may change after open */ + m_table_flags= m_file[0]->table_flags(); file= m_file; do { @@ -2235,7 +2223,11 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked) m_no_locks+= (*file)->lock_count(); name_buffer_ptr+= strlen(name_buffer_ptr) + 1; set_if_bigger(ref_length, ((*file)->ref_length)); + m_table_flags&= (*file)->table_flags(); } while (*(++file)); + m_table_flags&= ~(HA_CAN_GEOMETRY | HA_CAN_FULLTEXT | HA_DUPLICATE_POS | + HA_CAN_SQL_HANDLER | HA_CAN_INSERT_DELAYED); + m_table_flags|= HA_FILE_BASED | HA_REC_NOT_IN_SEQ; /* Add 2 bytes for partition id in position ref length. @@ -2598,6 +2590,7 @@ int ha_partition::write_row(byte * buf) if (table->next_number_field && buf == table->record[0]) update_auto_increment(); + my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set); #ifdef NOT_NEEDED if (likely(buf == rec0)) #endif @@ -2612,6 +2605,7 @@ int ha_partition::write_row(byte * buf) set_field_ptr(m_part_field_array, rec0, buf); } #endif + dbug_tmp_restore_column_map(table->read_set, old_map); if (unlikely(error)) DBUG_RETURN(error); m_last_part= part_id; @@ -2803,7 +2797,7 @@ void ha_partition::start_bulk_insert(ha_rows rows) file= m_file; do { - (*file)->start_bulk_insert(rows); + (*file)->ha_start_bulk_insert(rows); } while (*(++file)); DBUG_VOID_RETURN; } @@ -2830,7 +2824,7 @@ int ha_partition::end_bulk_insert() do { int tmp; - if ((tmp= (*file)->end_bulk_insert())) + if ((tmp= (*file)->ha_end_bulk_insert())) error= tmp; } while (*(++file)); DBUG_RETURN(error); @@ -4068,7 +4062,7 @@ void ha_partition::include_partition_fields_in_used_fields() do { - ha_set_bit_in_read_set((*ptr)->fieldnr); + bitmap_set_bit(table->read_set, (*ptr)->field_index); } while (*(++ptr)); DBUG_VOID_RETURN; } @@ -4155,8 +4149,12 @@ void ha_partition::info(uint flag) if (flag & HA_STATUS_AUTO) { + ulonglong nb_reserved_values; DBUG_PRINT("info", ("HA_STATUS_AUTO")); - auto_increment_value= get_auto_increment(); + /* we don't want to reserve any values, it's pure information */ + get_auto_increment(0, 0, 0, &stats.auto_increment_value, + &nb_reserved_values); + release_auto_increment(); } if (flag & HA_STATUS_VARIABLE) { @@ -4180,12 +4178,12 @@ void ha_partition::info(uint flag) check_time: Time of last check (only applicable to MyISAM) We report last time of all underlying handlers */ - records= 0; - deleted= 0; - data_file_length= 0; - index_file_length= 0; - delete_length= 0; - check_time= 0; + stats.records= 0; + stats.deleted= 0; + stats.data_file_length= 0; + stats.index_file_length= 0; + stats.check_time= 0; + stats.delete_length= 0; file_array= m_file; do { @@ -4193,22 +4191,22 @@ void ha_partition::info(uint flag) { file= *file_array; file->info(HA_STATUS_VARIABLE); - records+= file->records; - deleted+= file->deleted; - data_file_length+= file->data_file_length; - index_file_length+= file->index_file_length; - delete_length+= file->delete_length; - if (file->check_time > check_time) - check_time= file->check_time; + stats.records+= file->stats.records; + stats.deleted+= file->stats.deleted; + stats.data_file_length+= file->stats.data_file_length; + stats.index_file_length+= file->stats.index_file_length; + stats.delete_length+= file->stats.delete_length; + if (file->stats.check_time > stats.check_time) + stats.check_time= file->stats.check_time; } } while (*(++file_array)); - if (records < 2 && - m_table_flags & HA_NOT_EXACT_COUNT) - records= 2; - if (records > 0) - mean_rec_length= (ulong) (data_file_length / records); + if (stats.records < 2 && + !(m_table_flags & HA_STATS_RECORDS_IS_EXACT)) + stats.records= 2; + if (stats.records > 0) + stats.mean_rec_length= (ulong) (stats.data_file_length / stats.records); else - mean_rec_length= 1; //? What should we set here + stats.mean_rec_length= 1; //? What should we set here } if (flag & HA_STATUS_CONST) { @@ -4253,7 +4251,6 @@ void ha_partition::info(uint flag) We ignore it since it is never used block_size: Block size used We set it to the value of the first handler - sortkey: Never used at any place so ignored ref_length: We set this to the value calculated and stored in local object create_time: Creation time of table @@ -4265,7 +4262,7 @@ void ha_partition::info(uint flag) file= m_file[0]; file->info(HA_STATUS_CONST); - create_time= file->create_time; + stats.create_time= file->stats.create_time; ref_length= m_ref_length; } if (flag & HA_STATUS_ERRKEY) @@ -4289,14 +4286,14 @@ void ha_partition::info(uint flag) Used by SHOW commands We will report the maximum of these times */ - update_time= 0; + stats.update_time= 0; file_array= m_file; do { file= *file_array; file->info(HA_STATUS_TIME); - if (file->update_time > update_time) - update_time= file->update_time; + if (file->stats.update_time > stats.update_time) + stats.update_time= file->stats.update_time; } while (*(++file_array)); } DBUG_VOID_RETURN; @@ -4310,17 +4307,17 @@ void ha_partition::get_dynamic_partition_info(PARTITION_INFO *stat_info, file->info(HA_STATUS_CONST | HA_STATUS_TIME | HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK); - stat_info->records= file->records; - stat_info->mean_rec_length= file->mean_rec_length; - stat_info->data_file_length= file->data_file_length; - stat_info->max_data_file_length= file->max_data_file_length; - stat_info->index_file_length= file->index_file_length; - stat_info->delete_length= file->delete_length; - stat_info->create_time= file->create_time; - stat_info->update_time= file->update_time; - stat_info->check_time= file->check_time; + stat_info->records= file->stats.records; + stat_info->mean_rec_length= file->stats.mean_rec_length; + stat_info->data_file_length= file->stats.data_file_length; + stat_info->max_data_file_length= file->stats.max_data_file_length; + stat_info->index_file_length= file->stats.index_file_length; + stat_info->delete_length= file->stats.delete_length; + stat_info->create_time= file->stats.create_time; + stat_info->update_time= file->stats.update_time; + stat_info->check_time= file->stats.check_time; stat_info->check_sum= 0; - if (file->table_flags() & (ulong) HA_HAS_CHECKSUM) + if (file->ha_table_flags() & HA_HAS_CHECKSUM) stat_info->check_sum= file->checksum(); return; } @@ -4391,22 +4388,6 @@ void ha_partition::get_dynamic_partition_info(PARTITION_INFO *stat_info, 2) Parameters used by some non-MyISAM handlers ---------------------------------------------- - HA_EXTRA_RETRIEVE_ALL_COLS: - Many handlers have implemented optimisations to avoid fetching all - fields when retrieving data. In certain situations all fields need - to be retrieved even though the query_id is not set on all field - objects. - - It is called from copy_data_between_tables where all fields are - copied without setting query_id before calling the handlers. - It is called from UPDATE statements when the fields of the index - used is updated or ORDER BY is used with UPDATE. - And finally when calculating checksum of a table using the CHECKSUM - command. - HA_EXTRA_RETRIEVE_PRIMARY_KEY: - In some situations it is mandatory to retrieve primary key fields - independent of the query id's. This extra flag specifies that fetch - of primary key fields is mandatory. HA_EXTRA_KEYREAD_PRESERVE_FIELDS: This is a strictly InnoDB feature that is more or less undocumented. When it is activated InnoDB copies field by field from its fetch @@ -4555,7 +4536,7 @@ void ha_partition::get_dynamic_partition_info(PARTITION_INFO *stat_info, 4) Parameters only used by temporary tables for query processing ---------------------------------------------------------------- HA_EXTRA_RESET_STATE: - Same as HA_EXTRA_RESET except that buffers are not released. If there is + Same as reset() except that buffers are not released. If there is a READ CACHE it is reinit'ed. A cache is reinit'ed to restart reading or to change type of cache between READ CACHE and WRITE CACHE. @@ -4594,8 +4575,9 @@ void ha_partition::get_dynamic_partition_info(PARTITION_INFO *stat_info, HA_EXTRA_FLUSH_CACHE: Flush WRITE CACHE in MyISAM. It is only from one place in the code. This is in sql_insert.cc where it is called if the table_flags doesn't - contain HA_DUPP_POS. The only handler having the HA_DUPP_POS set is the - MyISAM handler and so the only handler not receiving this call is MyISAM. + contain HA_DUPLICATE_POS. The only handler having the HA_DUPLICATE_POS + set is the MyISAM handler and so the only handler not receiving this + call is MyISAM. Thus in effect this call is called but never used. Could be removed from sql_insert.cc HA_EXTRA_NO_USER_CHANGE: @@ -4639,8 +4621,6 @@ int ha_partition::extra(enum ha_extra_function operation) /* Category 2), used by non-MyISAM handlers */ case HA_EXTRA_IGNORE_DUP_KEY: case HA_EXTRA_NO_IGNORE_DUP_KEY: - case HA_EXTRA_RETRIEVE_ALL_COLS: - case HA_EXTRA_RETRIEVE_PRIMARY_KEY: case HA_EXTRA_KEYREAD_PRESERVE_FIELDS: { if (!m_myisam) @@ -4706,8 +4686,7 @@ int ha_partition::extra(enum ha_extra_function operation) 0 Success DESCRIPTION - This will in the future be called instead of extra(HA_EXTRA_RESET) as this - is such a common call + Called at end of each statement to reste buffers */ int ha_partition::reset(void) @@ -5115,14 +5094,16 @@ void ha_partition::print_error(int error, myf errflag) /* Should probably look for my own errors first */ /* monty: needs to be called for the last used partition ! */ - DBUG_PRINT("enter", ("error = %d", error)); + DBUG_PRINT("enter", ("error: %d", error)); if (error == HA_ERR_NO_PARTITION_FOUND) { char buf[100]; + my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set); my_error(ER_NO_PARTITION_FOR_GIVEN_VALUE, MYF(0), m_part_info->part_expr->null_value ? "NULL" : llstr(m_part_info->part_expr->val_int(), buf)); + dbug_tmp_restore_column_map(table->read_set, old_map); } else m_file[0]->print_error(error, errflag); @@ -5302,19 +5283,55 @@ void ha_partition::restore_auto_increment() partitions. */ -ulonglong ha_partition::get_auto_increment() +void ha_partition::get_auto_increment(ulonglong offset, ulonglong increment, + ulonglong nb_desired_values, + ulonglong *first_value, + ulonglong *nb_reserved_values) { - ulonglong auto_inc, max_auto_inc= 0; + ulonglong first_value_part, last_value_part, nb_reserved_values_part, + last_value= ~ (ulonglong) 0; + handler **pos, **end; DBUG_ENTER("ha_partition::get_auto_increment"); - for (uint i= 0; i < m_tot_parts; i++) + for (pos=m_file, end= m_file+ m_tot_parts; pos != end ; pos++) { - auto_inc= m_file[i]->get_auto_increment(); - set_if_bigger(max_auto_inc, auto_inc); + (*pos)->get_auto_increment(offset, increment, nb_desired_values, + &first_value_part, &nb_reserved_values_part); + if (first_value_part == ~(ulonglong)(0)) // error in one partition + { + *first_value= first_value_part; + break; + } + /* + Partition has reserved an interval. Intersect it with the intervals + already reserved for the previous partitions. + */ + last_value_part= (nb_reserved_values_part == ULONGLONG_MAX) ? + ULONGLONG_MAX : (first_value_part + nb_reserved_values_part * increment); + set_if_bigger(*first_value, first_value_part); + set_if_smaller(last_value, last_value_part); } - DBUG_RETURN(max_auto_inc); + if (last_value < *first_value) /* empty intersection, error */ + { + *first_value= ~(ulonglong)(0); + } + if (increment) // If not check for values + *nb_reserved_values= (last_value == ULONGLONG_MAX) ? + ULONGLONG_MAX : ((last_value - *first_value) / increment); + + DBUG_VOID_RETURN; } +void ha_partition::release_auto_increment() +{ + DBUG_ENTER("ha_partition::release_auto_increment"); + + for (uint i= 0; i < m_tot_parts; i++) + { + m_file[i]->release_auto_increment(); + } + DBUG_VOID_RETURN; +} /**************************************************************************** MODULE initialise handler for HANDLER call @@ -5523,15 +5540,17 @@ static int free_share(PARTITION_SHARE *share) } #endif /* NOT_USED */ +struct st_mysql_storage_engine partition_storage_engine= +{ MYSQL_HANDLERTON_INTERFACE_VERSION, &partition_hton }; mysql_declare_plugin(partition) { MYSQL_STORAGE_ENGINE_PLUGIN, - &partition_hton, - partition_hton_name, + &partition_storage_engine, + "partition", "Mikael Ronstrom, MySQL AB", - partition_hton_comment, - NULL, /* Plugin Init */ + "Partition Storage Engine Helper", + partition_initialize, /* Plugin Init */ NULL, /* Plugin Deinit */ 0x0100, /* 1.0 */ 0 diff --git a/sql/ha_partition.h b/sql/ha_partition.h index 1443a20133c..b52c8d92164 100644 --- a/sql/ha_partition.h +++ b/sql/ha_partition.h @@ -95,7 +95,6 @@ private: uint m_rec_length; // Local copy of record length bool m_ordered; // Ordered/Unordered index scan - bool m_has_transactions; // Can we support transactions bool m_pkey_is_clustered; // Is primary key clustered bool m_create_handler; // Handler used to create table bool m_is_sub_partitioned; // Is subpartitioned @@ -157,7 +156,7 @@ public: enable later calls of the methods to retrieve constants from the under- lying handlers. Returns false if not successful. */ - int ha_initialise(); + bool initialise_partition(MEM_ROOT *mem_root); /* ------------------------------------------------------------------------- @@ -208,25 +207,24 @@ private: delete_table, rename_table and create uses very similar logic which is packed into this routine. */ - uint del_ren_cre_table(const char *from, - const char *to= NULL, - TABLE *table_arg= NULL, - HA_CREATE_INFO *create_info= NULL); + uint del_ren_cre_table(const char *from, const char *to, + TABLE *table_arg, HA_CREATE_INFO *create_info); /* One method to create the table_name.par file containing the names of the underlying partitions, their engine and the number of partitions. And one method to read it in. */ bool create_handler_file(const char *name); - bool get_from_handler_file(const char *name); - bool new_handlers_from_part_info(); - bool create_handlers(); + bool get_from_handler_file(const char *name, MEM_ROOT *mem_root); + bool new_handlers_from_part_info(MEM_ROOT *mem_root); + bool create_handlers(MEM_ROOT *mem_root); void clear_handler_file(); void set_up_table_before_create(TABLE *table_arg, const char *partition_name_with_path, HA_CREATE_INFO *info, uint part_id); partition_element *find_partition_element(uint part_id); + public: /* @@ -588,7 +586,7 @@ public: NULLable field. (BDB, HEAP, MyISAM, NDB, InnoDB) - HA_DUPP_POS: + HA_DUPLICATE_POS: Tells that we can the position for the conflicting duplicate key record is stored in table->file->dupp_ref. (insert uses rnd_pos() on this to find the duplicated row) @@ -609,11 +607,10 @@ public: with hidden primary key) (No handler has this limitation currently) - HA_NOT_EXACT_COUNT: + HA_STATS_RECORDS_IS_EXACT: Does the counter of records after the info call specify an exact - value or not. If it doesn't this flag is set. + value or not. If it does this flag is set. Only MyISAM and HEAP uses exact count. - (MyISAM, HEAP, BDB, InnoDB, NDB, Federated) HA_CAN_INSERT_DELAYED: Can the storage engine support delayed inserts. @@ -676,7 +673,7 @@ public: index scan module. (NDB) */ - virtual ulong table_flags() const + virtual ulonglong table_flags() const { return m_table_flags; } /* @@ -771,13 +768,6 @@ public: virtual uint min_record_length(uint options) const; /* - Transactions on the table is supported if all handlers below support - transactions. - */ - virtual bool has_transactions() - { return m_has_transactions; } - - /* Primary key is clustered can only be true if all underlying handlers have this feature. */ @@ -815,7 +805,11 @@ public: ------------------------------------------------------------------------- */ virtual void restore_auto_increment(); - virtual ulonglong get_auto_increment(); + virtual void get_auto_increment(ulonglong offset, ulonglong increment, + ulonglong nb_desired_values, + ulonglong *first_value, + ulonglong *nb_reserved_values); + virtual void release_auto_increment(); /* ------------------------------------------------------------------------- diff --git a/sql/handler.cc b/sql/handler.cc index f51e91f1882..81c42f9da15 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -44,48 +44,29 @@ #include "ha_innodb.h" #endif -/* While we have legacy_db_type, we have this array to - check for dups and to find handlerton from legacy_db_type. - Remove when legacy_db_type is finally gone */ -static handlerton *installed_htons[128]; +/* + While we have legacy_db_type, we have this array to + check for dups and to find handlerton from legacy_db_type. + Remove when legacy_db_type is finally gone +*/ +st_plugin_int *hton2plugin[MAX_HA]; -#define BITMAP_STACKBUF_SIZE (128/8) +static handlerton *installed_htons[128]; KEY_CREATE_INFO default_key_create_info= { HA_KEY_ALG_UNDEF, 0, {NullS,0} }; /* static functions defined in this file */ -static handler *create_default(TABLE_SHARE *table); - -const handlerton default_hton = -{ - MYSQL_HANDLERTON_INTERFACE_VERSION, - "DEFAULT", - SHOW_OPTION_YES, - NULL, - DB_TYPE_DEFAULT, - NULL, - 0, 0, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, - create_default, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, /* alter_tablespace */ - NULL, /* fill_files_table */ - HTON_NO_FLAGS, /* flags */ - NULL, /* binlog_func */ - NULL, /* binlog_log_query */ - NULL /* release_temporary_latches */ -}; +static handler *create_default(TABLE_SHARE *table, MEM_ROOT *mem_root); static SHOW_COMP_OPTION have_yes= SHOW_OPTION_YES; /* number of entries in handlertons[] */ -ulong total_ha; +ulong total_ha= 0; /* number of storage engines (from handlertons[]) that support 2pc */ -ulong total_ha_2pc; +ulong total_ha_2pc= 0; /* size of savepoint storage area (see ha_init) */ -ulong savepoint_alloc_size; +ulong savepoint_alloc_size= 0; struct show_table_alias_st sys_table_aliases[]= { @@ -122,7 +103,7 @@ handlerton *ha_resolve_by_name(THD *thd, LEX_STRING *name) if ((plugin= plugin_lock(name, MYSQL_STORAGE_ENGINE_PLUGIN))) { - handlerton *hton= (handlerton *) plugin->plugin->info; + handlerton *hton= (handlerton *)plugin->data; if (!(hton->flags & HTON_NOT_USER_SELECTABLE)) return hton; plugin_unlock(plugin); @@ -146,8 +127,7 @@ handlerton *ha_resolve_by_name(THD *thd, LEX_STRING *name) const char *ha_get_storage_engine(enum legacy_db_type db_type) { - switch (db_type) - { + switch (db_type) { case DB_TYPE_DEFAULT: return "DEFAULT"; case DB_TYPE_UNKNOWN: @@ -155,24 +135,22 @@ const char *ha_get_storage_engine(enum legacy_db_type db_type) default: if (db_type > DB_TYPE_UNKNOWN && db_type < DB_TYPE_DEFAULT && installed_htons[db_type]) - return installed_htons[db_type]->name; + return hton2plugin[installed_htons[db_type]->slot]->name.str; return "*NONE*"; } } -static handler *create_default(TABLE_SHARE *table) +static handler *create_default(TABLE_SHARE *table, MEM_ROOT *mem_root) { handlerton *hton=ha_resolve_by_legacy_type(current_thd, DB_TYPE_DEFAULT); - return (hton && hton != &default_hton && hton->create) ? - hton->create(table) : NULL; + return (hton && hton->create) ? hton->create(table, mem_root) : NULL; } handlerton *ha_resolve_by_legacy_type(THD *thd, enum legacy_db_type db_type) { - switch (db_type) - { + switch (db_type) { case DB_TYPE_DEFAULT: return (thd->variables.table_type != NULL) ? thd->variables.table_type : @@ -183,7 +161,7 @@ handlerton *ha_resolve_by_legacy_type(THD *thd, enum legacy_db_type db_type) default: if (db_type > DB_TYPE_UNKNOWN && db_type < DB_TYPE_DEFAULT) return installed_htons[db_type]; - return NULL; + return NULL; } } @@ -225,36 +203,23 @@ handlerton *ha_checktype(THD *thd, enum legacy_db_type database_type, handler *get_new_handler(TABLE_SHARE *share, MEM_ROOT *alloc, handlerton *db_type) { - handler *file= NULL; - /* - handlers are allocated with new in the handlerton create() function - we need to set the thd mem_root for these to be allocated correctly - */ - THD *thd= current_thd; - MEM_ROOT *thd_save_mem_root= thd->mem_root; - thd->mem_root= alloc; - - if (db_type != NULL && db_type->state == SHOW_OPTION_YES && db_type->create) - file= db_type->create(share); - - thd->mem_root= thd_save_mem_root; + handler *file; + DBUG_ENTER("get_new_handler"); + DBUG_PRINT("enter", ("alloc: 0x%lx", (long) alloc)); - if (!file) - { - handlerton *def= current_thd->variables.table_type; - /* Try first with 'default table type' */ - if (db_type != def) - return get_new_handler(share, alloc, def); - } - if (file) + if (db_type && db_type->state == SHOW_OPTION_YES && db_type->create) { - if (file->ha_initialise()) - { - delete file; - file=0; - } + if ((file= db_type->create(share, alloc))) + file->init(); + DBUG_RETURN(file); } - return file; + /* + Try the default table type + Here the call to current_thd() is ok as we call this function a lot of + times but we enter this branch very seldom. + */ + DBUG_RETURN(get_new_handler(share, alloc, + current_thd->variables.table_type)); } @@ -265,11 +230,13 @@ handler *get_ha_partition(partition_info *part_info) DBUG_ENTER("get_ha_partition"); if ((partition= new ha_partition(part_info))) { - if (partition->ha_initialise()) + if (partition->initialise_partition(current_thd->mem_root)) { delete partition; partition= 0; } + else + partition->init(); } else { @@ -372,22 +339,19 @@ static int ha_finish_errors(void) int ha_finalize_handlerton(st_plugin_int *plugin) { - handlerton *hton; + handlerton *hton= (handlerton *)plugin->data; DBUG_ENTER("ha_finalize_handlerton"); - if (!(hton= (handlerton *) plugin->plugin->info)) - DBUG_RETURN(1); - switch (hton->state) { case SHOW_OPTION_NO: case SHOW_OPTION_DISABLED: break; case SHOW_OPTION_YES: - if (hton->panic && hton->panic(HA_PANIC_CLOSE)) - DBUG_RETURN(1); if (installed_htons[hton->db_type] == hton) installed_htons[hton->db_type]= NULL; + if (hton->panic && hton->panic(HA_PANIC_CLOSE)) + DBUG_RETURN(1); break; }; DBUG_RETURN(0); @@ -396,37 +360,28 @@ int ha_finalize_handlerton(st_plugin_int *plugin) int ha_initialize_handlerton(st_plugin_int *plugin) { - handlerton *hton; + handlerton *hton= ((st_mysql_storage_engine *)plugin->plugin->info)->handlerton; DBUG_ENTER("ha_initialize_handlerton"); - if (!(hton= (handlerton *) plugin->plugin->info)) - DBUG_RETURN(1); - - /* for the sake of sanity, we set the handlerton name to be the - same as the plugin name */ - hton->name= plugin->name.str; - + plugin->data= hton; // shortcut for the future + /* + the switch below and hton->state should be removed when + command-line options for plugins will be implemented + */ switch (hton->state) { case SHOW_OPTION_NO: break; case SHOW_OPTION_YES: - if (!hton->init || !hton->init()) { - uint tmp= hton->savepoint_offset; - hton->savepoint_offset= savepoint_alloc_size; - savepoint_alloc_size+= tmp; - hton->slot= total_ha++; - if (hton->prepare) - total_ha_2pc++; - + uint tmp; /* now check the db_type for conflict */ - if (hton->db_type <= DB_TYPE_UNKNOWN || + if (hton->db_type <= DB_TYPE_UNKNOWN || hton->db_type >= DB_TYPE_DEFAULT || installed_htons[hton->db_type]) { int idx= (int) DB_TYPE_FIRST_DYNAMIC; - + while (idx < (int) DB_TYPE_DEFAULT && installed_htons[idx]) idx++; @@ -437,10 +392,17 @@ int ha_initialize_handlerton(st_plugin_int *plugin) } if (hton->db_type != DB_TYPE_UNKNOWN) sql_print_warning("Storage engine '%s' has conflicting typecode. " - "Assigning value %d.", hton->name, idx); + "Assigning value %d.", plugin->plugin->name, idx); hton->db_type= (enum legacy_db_type) idx; } installed_htons[hton->db_type]= hton; + tmp= hton->savepoint_offset; + hton->savepoint_offset= savepoint_alloc_size; + savepoint_alloc_size+= tmp; + hton->slot= total_ha++; + hton2plugin[hton->slot]=plugin; + if (hton->prepare) + total_ha_2pc++; break; } /* fall through */ @@ -451,33 +413,14 @@ int ha_initialize_handlerton(st_plugin_int *plugin) DBUG_RETURN(0); } - -static my_bool init_handlerton(THD *unused1, st_plugin_int *plugin, - void *unused2) -{ - if (plugin->state == PLUGIN_IS_UNINITIALIZED) - { - ha_initialize_handlerton(plugin); - plugin->state= PLUGIN_IS_READY; - } - return FALSE; -} - - int ha_init() { int error= 0; - total_ha= savepoint_alloc_size= 0; DBUG_ENTER("ha_init"); - bzero(installed_htons, sizeof(installed_htons)); - if (ha_init_errors()) DBUG_RETURN(1); - if (plugin_foreach(NULL, init_handlerton, MYSQL_STORAGE_ENGINE_PLUGIN, 0)) - DBUG_RETURN(1); - DBUG_ASSERT(total_ha < MAX_HA); /* Check if there is a transaction-capable storage engine besides the @@ -489,16 +432,14 @@ int ha_init() DBUG_RETURN(error); } +/* + close, flush or restart databases + Ignore this for other databases than ours +*/ - - - /* close, flush or restart databases */ - /* Ignore this for other databases than ours */ - -static my_bool panic_handlerton(THD *unused1, st_plugin_int *plugin, - void *arg) +static my_bool panic_handlerton(THD *unused1, st_plugin_int *plugin, void *arg) { - handlerton *hton= (handlerton *) plugin->plugin->info; + handlerton *hton= (handlerton *)plugin->data; if (hton->state == SHOW_OPTION_YES && hton->panic) ((int*)arg)[0]|= hton->panic((enum ha_panic_function)((int*)arg)[1]); return FALSE; @@ -508,10 +449,10 @@ static my_bool panic_handlerton(THD *unused1, st_plugin_int *plugin, int ha_panic(enum ha_panic_function flag) { int error[2]; - + error[0]= 0; error[1]= (int)flag; plugin_foreach(NULL, panic_handlerton, MYSQL_STORAGE_ENGINE_PLUGIN, error); - + if (flag == HA_PANIC_CLOSE && ha_finish_errors()) error[0]= 1; return error[0]; @@ -520,7 +461,7 @@ int ha_panic(enum ha_panic_function flag) static my_bool dropdb_handlerton(THD *unused1, st_plugin_int *plugin, void *path) { - handlerton *hton= (handlerton *) plugin->plugin->info; + handlerton *hton= (handlerton *)plugin->data; if (hton->state == SHOW_OPTION_YES && hton->drop_database) hton->drop_database((char *)path); return FALSE; @@ -536,9 +477,11 @@ void ha_drop_database(char* path) static my_bool closecon_handlerton(THD *thd, st_plugin_int *plugin, void *unused) { - handlerton *hton= (handlerton *) plugin->plugin->info; - /* there's no need to rollback here as all transactions must - be rolled back already */ + handlerton *hton= (handlerton *)plugin->data; + /* + there's no need to rollback here as all transactions must + be rolled back already + */ if (hton->state == SHOW_OPTION_YES && hton->close_connection && thd->ha_data[hton->slot]) hton->close_connection(thd); @@ -628,7 +571,8 @@ int ha_prepare(THD *thd) else { push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, - ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA), (*ht)->name); + ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA), + hton2plugin[(*ht)->slot]->name.str); } } } @@ -867,7 +811,7 @@ struct xahton_st { static my_bool xacommit_handlerton(THD *unused1, st_plugin_int *plugin, void *arg) { - handlerton *hton= (handlerton *) plugin->plugin->info; + handlerton *hton= (handlerton *)plugin->data; if (hton->state == SHOW_OPTION_YES && hton->recover) { hton->commit_by_xid(((struct xahton_st *)arg)->xid); @@ -879,7 +823,7 @@ static my_bool xacommit_handlerton(THD *unused1, st_plugin_int *plugin, static my_bool xarollback_handlerton(THD *unused1, st_plugin_int *plugin, void *arg) { - handlerton *hton= (handlerton *) plugin->plugin->info; + handlerton *hton= (handlerton *)plugin->data; if (hton->state == SHOW_OPTION_YES && hton->recover) { hton->rollback_by_xid(((struct xahton_st *)arg)->xid); @@ -894,7 +838,7 @@ int ha_commit_or_rollback_by_xid(XID *xid, bool commit) struct xahton_st xaop; xaop.xid= xid; xaop.result= 1; - + plugin_foreach(NULL, commit ? xacommit_handlerton : xarollback_handlerton, MYSQL_STORAGE_ENGINE_PLUGIN, &xaop); @@ -986,16 +930,16 @@ struct xarecover_st static my_bool xarecover_handlerton(THD *unused, st_plugin_int *plugin, void *arg) { - handlerton *hton= (handlerton *) plugin->plugin->info; + handlerton *hton= (handlerton *)plugin->data; struct xarecover_st *info= (struct xarecover_st *) arg; int got; - + if (hton->state == SHOW_OPTION_YES && hton->recover) { while ((got= hton->recover(info->list, info->len)) > 0 ) { sql_print_information("Found %d prepared transaction(s) in %s", - got, hton->name); + got, hton2plugin[hton->slot]->name.str); for (int i=0; i < got; i ++) { my_xid x=info->list[i].get_my_xid(); @@ -1175,7 +1119,7 @@ bool mysql_xa_recover(THD *thd) static my_bool release_temporary_latches(THD *thd, st_plugin_int *plugin, void *unused) { - handlerton *hton= (handlerton *) plugin->plugin->info; + handlerton *hton= (handlerton *)plugin->data; if (hton->state == SHOW_OPTION_YES && hton->release_temporary_latches) hton->release_temporary_latches(thd); @@ -1300,7 +1244,7 @@ int ha_release_savepoint(THD *thd, SAVEPOINT *sv) static my_bool snapshot_handlerton(THD *thd, st_plugin_int *plugin, void *arg) { - handlerton *hton= (handlerton *) plugin->plugin->info; + handlerton *hton= (handlerton *)plugin->data; if (hton->state == SHOW_OPTION_YES && hton->start_consistent_snapshot) { @@ -1331,7 +1275,7 @@ int ha_start_consistent_snapshot(THD *thd) static my_bool flush_handlerton(THD *thd, st_plugin_int *plugin, void *arg) { - handlerton *hton= (handlerton *) plugin->plugin->info; + handlerton *hton= (handlerton *)plugin->data; if (hton->state == SHOW_OPTION_YES && hton->flush_logs && hton->flush_logs()) return TRUE; return FALSE; @@ -1379,7 +1323,7 @@ int ha_delete_table(THD *thd, handlerton *table_type, const char *path, ! (file=get_new_handler(&dummy_share, thd->mem_root, table_type))) DBUG_RETURN(ENOENT); - if (lower_case_table_names == 2 && !(file->table_flags() & HA_FILE_BASED)) + if (lower_case_table_names == 2 && !(file->ha_table_flags() & HA_FILE_BASED)) { /* Ensure that table handler get path in lower case */ strmov(tmp_path, path); @@ -1462,6 +1406,7 @@ int handler::ha_open(TABLE *table_arg, const char *name, int mode, table= table_arg; DBUG_ASSERT(table->s == table_share); + DBUG_ASSERT(alloc_root_inited(&table->mem_root)); if ((error=open(name,mode,test_if_locked))) { @@ -1483,106 +1428,23 @@ int handler::ha_open(TABLE *table_arg, const char *name, int mode, table->db_stat|=HA_READ_ONLY; (void) extra(HA_EXTRA_NO_READCHECK); // Not needed in SQL - DBUG_ASSERT(alloc_root_inited(&table->mem_root)); - if (!(ref= (byte*) alloc_root(&table->mem_root, ALIGN_SIZE(ref_length)*2))) { close(); error=HA_ERR_OUT_OF_MEM; } else - dupp_ref=ref+ALIGN_SIZE(ref_length); - - if (ha_allocate_read_write_set(table->s->fields)) - error= 1; + dup_ref=ref+ALIGN_SIZE(ref_length); + cached_table_flags= table_flags(); } DBUG_RETURN(error); } -int handler::ha_initialise() -{ - DBUG_ENTER("ha_initialise"); - DBUG_RETURN(FALSE); -} - - -/* - Initalize bit maps for used fields - - Called from open_table_from_share() -*/ - -int handler::ha_allocate_read_write_set(ulong no_fields) -{ - uint bitmap_size= bitmap_buffer_size(no_fields+1); - uint32 *read_buf, *write_buf; - DBUG_ENTER("ha_allocate_read_write_set"); - DBUG_PRINT("enter", ("no_fields = %d", no_fields)); - - if (!multi_alloc_root(&table->mem_root, - &read_set, sizeof(MY_BITMAP), - &write_set, sizeof(MY_BITMAP), - &read_buf, bitmap_size, - &write_buf, bitmap_size, - NullS)) - { - DBUG_RETURN(TRUE); - } - bitmap_init(read_set, read_buf, no_fields+1, FALSE); - bitmap_init(write_set, write_buf, no_fields+1, FALSE); - table->read_set= read_set; - table->write_set= write_set; - ha_clear_all_set(); - DBUG_RETURN(FALSE); -} - -void handler::ha_clear_all_set() -{ - DBUG_ENTER("ha_clear_all_set"); - bitmap_clear_all(read_set); - bitmap_clear_all(write_set); - bitmap_set_bit(read_set, 0); - bitmap_set_bit(write_set, 0); - DBUG_VOID_RETURN; -} - -int handler::ha_retrieve_all_cols() -{ - DBUG_ENTER("handler::ha_retrieve_all_cols"); - bitmap_set_all(read_set); - DBUG_RETURN(0); -} - -int handler::ha_retrieve_all_pk() -{ - DBUG_ENTER("ha_retrieve_all_pk"); - ha_set_primary_key_in_read_set(); - DBUG_RETURN(0); -} - -void handler::ha_set_primary_key_in_read_set() -{ - ulong prim_key= table->s->primary_key; - DBUG_ENTER("handler::ha_set_primary_key_in_read_set"); - DBUG_PRINT("info", ("Primary key = %d", prim_key)); - if (prim_key != MAX_KEY) - { - KEY_PART_INFO *key_part= table->key_info[prim_key].key_part; - KEY_PART_INFO *key_part_end= key_part + - table->key_info[prim_key].key_parts; - for (;key_part != key_part_end; ++key_part) - ha_set_bit_in_read_set(key_part->fieldnr); - } - DBUG_VOID_RETURN; -} - - - /* Read first row (only) from a table This is never called for InnoDB or BDB tables, as these table types - has the HA_NOT_EXACT_COUNT set. + has the HA_STATS_RECORDS_IS_EXACT set. */ int handler::read_first_row(byte * buf, uint primary_key) @@ -1598,7 +1460,7 @@ int handler::read_first_row(byte * buf, uint primary_key) scanning the table. TODO remove the test for HA_READ_ORDER */ - if (deleted < 10 || primary_key >= MAX_KEY || + if (stats.deleted < 10 || primary_key >= MAX_KEY || !(index_flags(primary_key, 0, 0) & HA_READ_ORDER)) { (void) ha_rnd_init(1); @@ -1639,7 +1501,7 @@ next_insert_id(ulonglong nr,struct system_variables *variables) Update the auto_increment field if necessary SYNOPSIS - update_auto_increment() + update_auto_increment() RETURN 0 ok @@ -1668,8 +1530,9 @@ next_insert_id(ulonglong nr,struct system_variables *variables) statement. For the following rows we generate new numbers based on the last used number. - - thd->next_insert_id != 0. This happens when we have read a statement - from the binary log or when one has used SET LAST_INSERT_ID=#. + - thd->next_insert_id != 0. This happens when we have read an Intvar event + of type INSERT_ID_EVENT from the binary log or when one has used SET + INSERT_ID=#. In this case we will set the column to the value of next_insert_id. The next row will be given the id @@ -1685,8 +1548,20 @@ next_insert_id(ulonglong nr,struct system_variables *variables) start counting from the inserted value. thd->next_insert_id is cleared after it's been used for a statement. + + TODO + + Replace all references to "next number" or NEXT_NUMBER to + "auto_increment", everywhere (see below: there is + table->auto_increment_field_not_null, and there also exists + table->next_number_field, it's not consistent). + */ +#define AUTO_INC_DEFAULT_NB_ROWS 1 // Some prefer 1024 here +#define AUTO_INC_DEFAULT_NB_MAX_BITS 16 +#define AUTO_INC_DEFAULT_NB_MAX ((1 << AUTO_INC_DEFAULT_NB_MAX_BITS) - 1) + bool handler::update_auto_increment() { ulonglong nr; @@ -1702,17 +1577,24 @@ bool handler::update_auto_increment() */ thd->prev_insert_id= thd->next_insert_id; auto_increment_field_not_null= table->auto_increment_field_not_null; - table->auto_increment_field_not_null= FALSE; + table->auto_increment_field_not_null= FALSE; // to reset for next row if ((nr= table->next_number_field->val_int()) != 0 || auto_increment_field_not_null && thd->variables.sql_mode & MODE_NO_AUTO_VALUE_ON_ZERO) { - /* Clear flag for next row */ - /* Mark that we didn't generate a new value **/ + /* + The user did specify a value for the auto_inc column, we don't generate + a new value, write it down. + */ auto_increment_column_changed=0; - /* Update next_insert_id if we have already generated a value */ + /* + Update next_insert_id if we had already generated a value in this + statement (case of INSERT VALUES(null),(3763),(null): + the last NULL needs to insert 3764, not the value of the first NULL plus + 1). + */ if (thd->clear_next_insert_id && nr >= thd->next_insert_id) { if (variables->auto_increment_increment != 1) @@ -1726,9 +1608,53 @@ bool handler::update_auto_increment() } if (!(nr= thd->next_insert_id)) { - if ((nr= get_auto_increment()) == ~(ulonglong) 0) + ulonglong nb_desired_values= 1, nb_reserved_values; +#ifdef TO_BE_ENABLED_SOON + /* + Reserved intervals will be stored in "THD::auto_inc_intervals". + handler::estimation_rows_to_insert will be the argument passed by + handler::ha_start_bulk_insert(). + */ + uint estimation_known= test(estimation_rows_to_insert > 0); + uint nb_already_reserved_intervals= thd->auto_inc_intervals.nb_elements(); + /* + If an estimation was given to the engine: + - use it. + - if we already reserved numbers, it means the estimation was + not accurate, then we'll reserve 2*AUTO_INC_DEFAULT_NB_VALUES the 2nd + time, twice that the 3rd time etc. + If no estimation was given, use those increasing defaults from the + start, starting from AUTO_INC_DEFAULT_NB_VALUES. + Don't go beyond a max to not reserve "way too much" (because reservation + means potentially losing unused values). + */ + if (nb_already_reserved_intervals == 0 && estimation_known) + nb_desired_values= estimation_rows_to_insert; + else /* go with the increasing defaults */ + { + /* avoid overflow in formula, with this if() */ + if (nb_already_reserved_intervals <= AUTO_INC_DEFAULT_NB_MAX_BITS) + { + nb_desired_values= AUTO_INC_DEFAULT_NB_VALUES * + (1 << nb_already_reserved_intervals); + set_if_smaller(nb_desired_values, AUTO_INC_DEFAULT_NB_MAX); + } + else + nb_desired_values= AUTO_INC_DEFAULT_NB_MAX; + } +#endif + /* This call ignores all its parameters but nr, currently */ + get_auto_increment(variables->auto_increment_offset, + variables->auto_increment_increment, + nb_desired_values, &nr, + &nb_reserved_values); + if (nr == ~(ulonglong) 0) result= 1; // Mark failure + /* + That should not be needed when engines actually use offset and increment + above. + */ if (variables->auto_increment_increment != 1) nr= next_insert_id(nr-1, variables); /* @@ -1786,16 +1712,69 @@ void handler::restore_auto_increment() } -ulonglong handler::get_auto_increment() +/* + MySQL signal that it changed the column bitmap + + USAGE + This is for handlers that needs to setup their own column bitmaps. + Normally the handler should set up their own column bitmaps in + index_init() or rnd_init() and in any column_bitmaps_signal() call after + this. + + The handler is allowd to do changes to the bitmap after a index_init or + rnd_init() call is made as after this, MySQL will not use the bitmap + for any program logic checking. +*/ + +void handler::column_bitmaps_signal() +{ + DBUG_ENTER("column_bitmaps_signal"); + DBUG_PRINT("info", ("read_set: 0x%lx write_set: 0x%lx", table->read_set, + table->write_set)); + DBUG_VOID_RETURN; +} + + +/* + Reserves an interval of auto_increment values from the handler. + + SYNOPSIS + get_auto_increment() + offset + increment + nb_desired_values how many values we want + first_value (OUT) the first value reserved by the handler + nb_reserved_values (OUT) how many values the handler reserved + + offset and increment means that we want values to be of the form + offset + N * increment, where N>=0 is integer. + If the function sets *first_value to ~(ulonglong)0 it means an error. + If the function sets *nb_reserved_values to ULONGLONG_MAX it means it has + reserved to "positive infinite". +*/ + +void handler::get_auto_increment(ulonglong offset, ulonglong increment, + ulonglong nb_desired_values, + ulonglong *first_value, + ulonglong *nb_reserved_values) { ulonglong nr; int error; (void) extra(HA_EXTRA_KEYREAD); + table->mark_columns_used_by_index_no_reset(table->s->next_number_index, + table->read_set); + column_bitmaps_signal(); index_init(table->s->next_number_index, 1); if (!table->s->next_number_key_offset) { // Autoincrement at key-start error=index_last(table->record[1]); + /* + MySQL implicitely assumes such method does locking (as MySQL decides to + use nr+increment without checking again with the handler, in + handler::update_auto_increment()), so reserves to infinite. + */ + *nb_reserved_values= ULONGLONG_MAX; } else { @@ -1805,6 +1784,13 @@ ulonglong handler::get_auto_increment() table->s->next_number_key_offset); error= index_read(table->record[1], key, table->s->next_number_key_offset, HA_READ_PREFIX_LAST); + /* + MySQL needs to call us for next row: assume we are inserting ("a",null) + here, we return 3, and next this statement will want to insert + ("b",null): there is no reason why ("b",3+1) would be the good row to + insert: maybe it already exists, maybe 3+1 is too large... + */ + *nb_reserved_values= 1; } if (error) @@ -1814,11 +1800,11 @@ ulonglong handler::get_auto_increment() val_int_offset(table->s->rec_buff_length)+1); index_end(); (void) extra(HA_EXTRA_NO_KEYREAD); - return nr; + *first_value= nr; } -void handler::print_keydupp_error(uint key_nr, const char *msg) +void handler::print_keydup_error(uint key_nr, const char *msg) { /* Write the duplicated key in the error message */ char key[MAX_KEY_LENGTH]; @@ -1875,7 +1861,7 @@ void handler::print_error(int error, myf errflag) uint key_nr=get_dup_key(error); if ((int) key_nr >= 0) { - print_keydupp_error(key_nr, ER(ER_DUP_ENTRY)); + print_keydup_error(key_nr, ER(ER_DUP_ENTRY)); DBUG_VOID_RETURN; } textno=ER_DUP_KEY; @@ -1886,12 +1872,14 @@ void handler::print_error(int error, myf errflag) uint key_nr= get_dup_key(error); if ((int) key_nr >= 0) { + uint max_length; /* Write the key in the error message */ char key[MAX_KEY_LENGTH]; String str(key,sizeof(key),system_charset_info); /* Table is opened and defined at this point */ key_unpack(&str,table,(uint) key_nr); - uint max_length= MYSQL_ERRMSG_SIZE-(uint) strlen(ER(ER_FOREIGN_DUPLICATE_KEY)); + max_length= (MYSQL_ERRMSG_SIZE- + (uint) strlen(ER(ER_FOREIGN_DUPLICATE_KEY))); if (str.length() >= max_length) { str.length(max_length-4); @@ -2300,22 +2288,23 @@ int handler::index_next_same(byte *buf, const byte *key, uint keylen) } -void handler::get_dynamic_partition_info(PARTITION_INFO *stat_info, uint part_id) +void handler::get_dynamic_partition_info(PARTITION_INFO *stat_info, + uint part_id) { info(HA_STATUS_CONST | HA_STATUS_TIME | HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK); - stat_info->records= records; - stat_info->mean_rec_length= mean_rec_length; - stat_info->data_file_length= data_file_length; - stat_info->max_data_file_length= max_data_file_length; - stat_info->index_file_length= index_file_length; - stat_info->delete_length= delete_length; - stat_info->create_time= create_time; - stat_info->update_time= update_time; - stat_info->check_time= check_time; - stat_info->check_sum= 0; + stat_info->records= stats.records; + stat_info->mean_rec_length= stats.mean_rec_length; + stat_info->data_file_length= stats.data_file_length; + stat_info->max_data_file_length= stats.max_data_file_length; + stat_info->index_file_length= stats.index_file_length; + stat_info->delete_length= stats.delete_length; + stat_info->create_time= stats.create_time; + stat_info->update_time= stats.update_time; + stat_info->check_time= stats.check_time; + stat_info->check_sum= 0; if (table_flags() & (ulong) HA_HAS_CHECKSUM) - stat_info->check_sum= checksum(); + stat_info->check_sum= checksum(); return; } @@ -2325,11 +2314,11 @@ void handler::get_dynamic_partition_info(PARTITION_INFO *stat_info, uint part_id ****************************************************************************/ /* - Initiates table-file and calls apropriate database-creator + Initiates table-file and calls appropriate database-creator NOTES We must have a write lock on LOCK_open to be sure no other thread - interfers with table + interferes with table RETURN 0 ok @@ -2359,7 +2348,7 @@ int ha_create_table(THD *thd, const char *path, name= share.path.str; if (lower_case_table_names == 2 && - !(table.file->table_flags() & HA_FILE_BASED)) + !(table.file->ha_table_flags() & HA_FILE_BASED)) { /* Ensure that handler gets name in lower case */ strmov(name_buff, name); @@ -2438,7 +2427,7 @@ int ha_create_table_from_engine(THD* thd, const char *db, const char *name) create_info.table_options|= HA_OPTION_CREATE_FROM_ENGINE; if (lower_case_table_names == 2 && - !(table.file->table_flags() & HA_FILE_BASED)) + !(table.file->ha_table_flags() & HA_FILE_BASED)) { /* Ensure that handler gets name in lower case */ my_casedn_str(files_charset_info, path); @@ -2573,7 +2562,7 @@ int ha_discover(THD *thd, const char *db, const char *name, /* - Call this function in order to give the handler the possiblity + Call this function in order to give the handler the possibility to ask engine if there are any new tables that should be written to disk or any dropped tables that need to be removed from disk */ @@ -2642,7 +2631,7 @@ struct binlog_func_st static my_bool binlog_func_list(THD *thd, st_plugin_int *plugin, void *arg) { hton_list_st *hton_list= (hton_list_st *)arg; - handlerton *hton= (handlerton *) plugin->plugin->info; + handlerton *hton= (handlerton *)plugin->data; if (hton->state == SHOW_OPTION_YES && hton->binlog_func) { uint sz= hton_list->sz; @@ -2732,7 +2721,7 @@ static my_bool binlog_log_query_handlerton(THD *thd, st_plugin_int *plugin, void *args) { - return binlog_log_query_handlerton2(thd, (const handlerton *) plugin->plugin->info, args); + return binlog_log_query_handlerton2(thd, (const handlerton *)plugin->data, args); } void ha_binlog_log_query(THD *thd, const handlerton *hton, @@ -2788,6 +2777,9 @@ int handler::read_multi_range_first(KEY_MULTI_RANGE **found_range_p, multi_range_sorted= sorted; multi_range_buffer= buffer; + table->mark_columns_used_by_index_no_reset(active_index, table->read_set); + table->column_bitmaps_set(table->read_set, table->write_set); + for (multi_range_curr= ranges, multi_range_end= ranges + range_count; multi_range_curr < multi_range_end; multi_range_curr++) @@ -3013,7 +3005,7 @@ int handler::index_read_idx(byte * buf, uint index, const byte * key, SYNOPSIS ha_known_exts() - + NOTES No mutexes, worst case race is a minor surplus memory allocation We have to recreate the extension map if mysqld is restarted (for example @@ -3027,14 +3019,14 @@ static my_bool exts_handlerton(THD *unused, st_plugin_int *plugin, void *arg) { List<char> *found_exts= (List<char> *) arg; - handlerton *hton= (handlerton *) plugin->plugin->info; + handlerton *hton= (handlerton *)plugin->data; handler *file; if (hton->state == SHOW_OPTION_YES && hton->create && - (file= hton->create((TABLE_SHARE*) 0))) + (file= hton->create((TABLE_SHARE*) 0, current_thd->mem_root))) { List_iterator_fast<char> it(*found_exts); const char **ext, *old_ext; - + for (ext= file->bas_ext(); *ext; ext++) { while ((old_ext= it++)) @@ -3063,14 +3055,14 @@ TYPELIB *ha_known_exts(void) known_extensions_id= mysys_usage_id; found_exts.push_back((char*) triggers_file_ext); found_exts.push_back((char*) trigname_file_ext); - - plugin_foreach(NULL, exts_handlerton, + + plugin_foreach(NULL, exts_handlerton, MYSQL_STORAGE_ENGINE_PLUGIN, &found_exts); ext= (const char **) my_once_alloc(sizeof(char *)* (found_exts.elements+1), MYF(MY_WME | MY_FAE)); - + DBUG_ASSERT(ext != 0); known_extensions.count= found_exts.elements; known_extensions.type_names= ext; @@ -3103,7 +3095,7 @@ static my_bool showstat_handlerton(THD *thd, st_plugin_int *plugin, void *arg) { enum ha_stat_type stat= *(enum ha_stat_type *) arg; - handlerton *hton= (handlerton *) plugin->plugin->info; + handlerton *hton= (handlerton *)plugin->data; if (hton->state == SHOW_OPTION_YES && hton->show_status && hton->show_status(thd, stat_print, stat)) return TRUE; @@ -3126,16 +3118,19 @@ bool ha_show_status(THD *thd, handlerton *db_type, enum ha_stat_type stat) if (db_type == NULL) { - result= plugin_foreach(thd, showstat_handlerton, + result= plugin_foreach(thd, showstat_handlerton, MYSQL_STORAGE_ENGINE_PLUGIN, &stat); } else { if (db_type->state != SHOW_OPTION_YES) - result= stat_print(thd, db_type->name, strlen(db_type->name), + { + const LEX_STRING *name=&hton2plugin[db_type->slot]->name; + result= stat_print(thd, name->str, name->length, "", 0, "DISABLED", 8) ? 1 : 0; + } else - result= db_type->show_status && + result= db_type->show_status && db_type->show_status(thd, stat_print, stat) ? 1 : 0; } @@ -3167,7 +3162,7 @@ namespace { char const *name; }; - int table_name_compare(void const *a, void const *b) + static int table_name_compare(void const *a, void const *b) { st_table_data const *x = (st_table_data const*) a; st_table_data const *y = (st_table_data const*) b; @@ -3198,44 +3193,29 @@ namespace { DBUG_ASSERT(table->s->cached_row_logging_check == 0 || table->s->cached_row_logging_check == 1); - return - thd->current_stmt_binlog_row_based && - thd && (thd->options & OPTION_BIN_LOG) && - mysql_bin_log.is_open() && - table->s->cached_row_logging_check; + return (thd->current_stmt_binlog_row_based && + (thd->options & OPTION_BIN_LOG) && + mysql_bin_log.is_open() && + table->s->cached_row_logging_check); } } -template<class RowsEventT> int binlog_log_row(TABLE* table, +template<class RowsEventT> int binlog_log_row(TABLE *table, const byte *before_record, const byte *after_record) { if (table->file->is_injective()) return 0; bool error= 0; - THD *const thd= current_thd; - - if (check_table_binlog_row_based(thd, table)) - { - MY_BITMAP cols; - /* Potential buffer on the stack for the bitmap */ - uint32 bitbuf[BITMAP_STACKBUF_SIZE/sizeof(uint32)]; - uint n_fields= table->s->fields; - my_bool use_bitbuf= n_fields <= sizeof(bitbuf)*8; - if (likely(!(error= bitmap_init(&cols, - use_bitbuf ? bitbuf : NULL, - (n_fields + 7) & ~7UL, - false)))) - { - bitmap_set_all(&cols); - error= - RowsEventT::binlog_row_logging_function(thd, table, - table->file->has_transactions(), - &cols, table->s->fields, - before_record, after_record); - if (!use_bitbuf) - bitmap_free(&cols); - } + + if (check_table_binlog_row_based(table->in_use, table)) + { + error= + RowsEventT::binlog_row_logging_function(table->in_use, table, + table->file->has_transactions(), + &table->s->all_set, + table->s->fields, + before_record, after_record); } return error ? HA_ERR_RBR_LOGGING_FAILED : 0; } @@ -3296,6 +3276,28 @@ int handler::ha_external_lock(THD *thd, int lock_type) DBUG_RETURN(0); } + +/* + Check handler usage and reset state of file to after 'open' +*/ + +int handler::ha_reset() +{ + DBUG_ENTER("ha_reset"); + /* Check that we have called all proper delallocation functions */ + DBUG_ASSERT((byte*) table->def_read_set.bitmap + + table->s->column_bitmap_size == + (char*) table->def_write_set.bitmap); + DBUG_ASSERT(bitmap_is_set_all(&table->s->all_set)); + DBUG_ASSERT(table->key_read == 0); + /* ensure that ha_index_end / ha_rnd_end has been called */ + DBUG_ASSERT(inited == NONE); + /* Free cache used by filesort */ + free_io_cache(table); + DBUG_RETURN(reset()); +} + + int handler::ha_write_row(byte *buf) { int error; @@ -3338,3 +3340,200 @@ int handler::ha_delete_row(const byte *buf) #endif return 0; } + + + +/* + use_hidden_primary_key() is called in case of an update/delete when + (table_flags() and HA_PRIMARY_KEY_REQUIRED_FOR_DELETE) is defined + but we don't have a primary key +*/ + +void handler::use_hidden_primary_key() +{ + /* fallback to use all columns in the table to identify row */ + table->use_all_columns(); +} + + +/* + Dummy function which accept information about log files which is not need + by handlers +*/ + +void signal_log_not_needed(struct handlerton, char *log_file) +{ + DBUG_ENTER("signal_log_not_needed"); + DBUG_PRINT("enter", ("logfile '%s'", log_file)); + DBUG_VOID_RETURN; +} + + +#ifdef TRANS_LOG_MGM_EXAMPLE_CODE +/* + Example of transaction log management functions based on assumption that logs + placed into a directory +*/ +#include <my_dir.h> +#include <my_sys.h> +int example_of_iterator_using_for_logs_cleanup(handlerton *hton) +{ + void *buffer; + int res= 1; + struct handler_iterator iterator; + struct handler_log_file_data data; + + if (!hton->create_iterator) + return 1; /* iterator creator is not supported */ + + if ((*hton->create_iterator)(HA_TRANSACTLOG_ITERATOR, &iterator) != + HA_ITERATOR_OK) + { + /* error during creation of log iterator or iterator is not supported */ + return 1; + } + while((*iterator.next)(&iterator, (void*)&data) == 0) + { + printf("%s\n", data.filename.str); + if (data.status == HA_LOG_STATUS_FREE && + my_delete(data.filename.str, MYF(MY_WME))) + goto err; + } + res= 0; +err: + (*iterator.destroy)(&iterator); + return res; +} + + +/* + Here we should get info from handler where it save logs but here is + just example, so we use constant. + IMHO FN_ROOTDIR ("/") is safe enough for example, because nobody has + rights on it except root and it consist of directories only at lest for + *nix (sorry, can't find windows-safe solution here, but it is only example). +*/ +#define fl_dir FN_ROOTDIR + + +/* + Dummy function to return log status should be replaced by function which + really detect the log status and check that the file is a log of this + handler. +*/ + +enum log_status fl_get_log_status(char *log) +{ + MY_STAT stat_buff; + if (my_stat(log, &stat_buff, MYF(0))) + return HA_LOG_STATUS_INUSE; + return HA_LOG_STATUS_NOSUCHLOG; +} + + +struct fl_buff +{ + LEX_STRING *names; + enum log_status *statuses; + uint32 entries; + uint32 current; +}; + + +int fl_log_iterator_next(struct handler_iterator *iterator, + void *iterator_object) +{ + struct fl_buff *buff= (struct fl_buff *)iterator->buffer; + struct handler_log_file_data *data= + (struct handler_log_file_data *) iterator_object; + if (buff->current >= buff->entries) + return 1; + data->filename= buff->names[buff->current]; + data->status= buff->statuses[buff->current]; + buff->current++; + return 0; +} + + +void fl_log_iterator_destroy(struct handler_iterator *iterator) +{ + my_free((gptr)iterator->buffer, MYF(MY_ALLOW_ZERO_PTR)); +} + + +/* + returns buffer, to be assigned in handler_iterator struct +*/ +enum handler_create_iterator_result +fl_log_iterator_buffer_init(struct handler_iterator *iterator) +{ + MY_DIR *dirp; + struct fl_buff *buff; + char *name_ptr; + byte *ptr; + FILEINFO *file; + uint32 i; + + /* to be able to make my_free without crash in case of error */ + iterator->buffer= 0; + + if (!(dirp = my_dir(fl_dir, MYF(0)))) + { + return HA_ITERATOR_ERROR; + } + if ((ptr= (byte*)my_malloc(ALIGN_SIZE(sizeof(fl_buff)) + + ((ALIGN_SIZE(sizeof(LEX_STRING)) + + sizeof(enum log_status) + + + FN_REFLEN) * + (uint) dirp->number_off_files), + MYF(0))) == 0) + { + return HA_ITERATOR_ERROR; + } + buff= (struct fl_buff *)ptr; + buff->entries= buff->current= 0; + ptr= ptr + (ALIGN_SIZE(sizeof(fl_buff))); + buff->names= (LEX_STRING*) (ptr); + ptr= ptr + ((ALIGN_SIZE(sizeof(LEX_STRING)) * + (uint) dirp->number_off_files)); + buff->statuses= (enum log_status *)(ptr); + name_ptr= (char *)(ptr + (sizeof(enum log_status) * + (uint) dirp->number_off_files)); + for (i=0 ; i < (uint) dirp->number_off_files ; i++) + { + enum log_status st; + file= dirp->dir_entry + i; + if ((file->name[0] == '.' && + ((file->name[1] == '.' && file->name[2] == '\0') || + file->name[1] == '\0'))) + continue; + if ((st= fl_get_log_status(file->name)) == HA_LOG_STATUS_NOSUCHLOG) + continue; + name_ptr= strxnmov(buff->names[buff->entries].str= name_ptr, + FN_REFLEN, fl_dir, file->name, NullS); + buff->names[buff->entries].length= (name_ptr - + buff->names[buff->entries].str) - 1; + buff->statuses[buff->entries]= st; + buff->entries++; + } + + iterator->buffer= buff; + iterator->next= &fl_log_iterator_next; + iterator->destroy= &fl_log_iterator_destroy; + return HA_ITERATOR_OK; +} + + +/* An example of a iterator creator */ +enum handler_create_iterator_result +fl_create_iterator(enum handler_iterator_type type, + struct handler_iterator *iterator) +{ + switch(type) { + case HA_TRANSACTLOG_ITERATOR: + return fl_log_iterator_buffer_init(iterator); + default: + return HA_ITERATOR_UNSUPPORTED; + } +} +#endif /*TRANS_LOG_MGM_EXAMPLE_CODE*/ diff --git a/sql/handler.h b/sql/handler.h index 9a5a3b04823..f459d52fdeb 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -49,14 +49,18 @@ /* Bits in table_flags() to show what database can do */ +#define HA_NO_TRANSACTIONS (1 << 0) /* Doesn't support transactions */ +#define HA_PARTIAL_COLUMN_READ (1 << 1) /* read may not return all columns */ +#define HA_TABLE_SCAN_ON_INDEX (1 << 2) /* No separate data/index file */ /* - Can switch index during the scan with ::rnd_same() - not used yet. - see mi_rsame/heap_rsame/myrg_rsame + The following should be set if the following is not true when scanning + a table with rnd_next() + - We will see all rows (including deleted ones) + - Row positions are 'table->s->db_record_offset' apart + If this flag is not set, filesort will do a postion() call for each matched + row to be able to find the row later. */ -#define HA_READ_RND_SAME (1 << 0) -#define HA_TABLE_SCAN_ON_INDEX (1 << 2) /* No separate data/index file */ -#define HA_REC_NOT_IN_SEQ (1 << 3) /* ha_info don't return recnumber; - It returns a position to ha_r_rnd */ +#define HA_REC_NOT_IN_SEQ (1 << 3) #define HA_CAN_GEOMETRY (1 << 4) /* Reading keys in random order is as fast as reading keys in sort order @@ -64,28 +68,41 @@ filesort to decide if we should sort key + data or key + pointer-to-row */ #define HA_FAST_KEY_READ (1 << 5) +/* + Set the following flag if we on delete should force all key to be read + and on update read all keys that changes +*/ +#define HA_REQUIRES_KEY_COLUMNS_FOR_DELETE (1 << 6) #define HA_NULL_IN_KEY (1 << 7) /* One can have keys with NULL */ -#define HA_DUPP_POS (1 << 8) /* ha_position() gives dup row */ +#define HA_DUPLICATE_POS (1 << 8) /* ha_position() gives dup row */ #define HA_NO_BLOBS (1 << 9) /* Doesn't support blobs */ #define HA_CAN_INDEX_BLOBS (1 << 10) #define HA_AUTO_PART_KEY (1 << 11) /* auto-increment in multi-part key */ #define HA_REQUIRE_PRIMARY_KEY (1 << 12) /* .. and can't create a hidden one */ -#define HA_NOT_EXACT_COUNT (1 << 13) +#define HA_STATS_RECORDS_IS_EXACT (1 << 13) /* stats.records is exact */ /* INSERT_DELAYED only works with handlers that uses MySQL internal table level locks */ #define HA_CAN_INSERT_DELAYED (1 << 14) +/* + If we get the primary key columns for free when we do an index read + It also implies that we have to retrive the primary key when using + position() and rnd_pos(). +*/ #define HA_PRIMARY_KEY_IN_READ_INDEX (1 << 15) /* - If HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS is set, it means that the engine can - do this: the position of an arbitrary record can be retrieved using - position() when the table has a primary key, effectively allowing random - access on the table based on a given record. + If HA_PRIMARY_KEY_REQUIRED_FOR_POSITION is set, it means that to position() + uses a primary key. Without primary key, we can't call position(). */ -#define HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS (1 << 16) +#define HA_PRIMARY_KEY_REQUIRED_FOR_POSITION (1 << 16) #define HA_CAN_RTREEKEYS (1 << 17) #define HA_NOT_DELETE_WITH_CACHE (1 << 18) +/* + The following is we need to a primary key to delete (and update) a row. + If there is no primary key, all columns needs to be read on update and delete +*/ +#define HA_PRIMARY_KEY_REQUIRED_FOR_DELETE (1 << 19) #define HA_NO_PREFIX_CHAR_KEYS (1 << 20) #define HA_CAN_FULLTEXT (1 << 21) #define HA_CAN_SQL_HANDLER (1 << 22) @@ -97,7 +114,8 @@ #define HA_CAN_BIT_FIELD (1 << 28) /* supports bit fields */ #define HA_NEED_READ_RANGE_BUFFER (1 << 29) /* for read_multi_range */ #define HA_ANY_INDEX_MAY_BE_UNIQUE (1 << 30) -#define HA_NO_COPY_ON_ALTER (1 << 31) +#define HA_NO_COPY_ON_ALTER (LL(1) << 31) +#define HA_HAS_RECORDS (LL(1) << 32) /* records() gives exact count*/ /* bits in index_flags(index_number) for what you can do with index */ #define HA_READ_NEXT 1 /* TODO really use this flag */ @@ -177,6 +195,7 @@ so: innodb + bdb + ndb + binlog + myisam + myisammrg + archive + example + csv + heap + blackhole + federated + 0 (yes, the sum is deliberately inaccurate) + TODO remove the limit, use dynarrays */ #define MAX_HA 15 @@ -240,7 +259,7 @@ enum legacy_db_type enum row_type { ROW_TYPE_NOT_USED=-1, ROW_TYPE_DEFAULT, ROW_TYPE_FIXED, ROW_TYPE_DYNAMIC, ROW_TYPE_COMPRESSED, - ROW_TYPE_REDUNDANT, ROW_TYPE_COMPACT }; + ROW_TYPE_REDUNDANT, ROW_TYPE_COMPACT, ROW_TYPE_PAGES }; enum enum_binlog_func { BFN_RESET_LOGS= 1, @@ -460,6 +479,73 @@ typedef bool (stat_print_fn)(THD *thd, const char *type, uint type_len, const char *file, uint file_len, const char *status, uint status_len); enum ha_stat_type { HA_ENGINE_STATUS, HA_ENGINE_LOGS, HA_ENGINE_MUTEX }; +extern st_plugin_int *hton2plugin[MAX_HA]; + +/* Transaction log maintains type definitions */ +enum log_status +{ + HA_LOG_STATUS_FREE= 0, /* log is free and can be deleted */ + HA_LOG_STATUS_INUSE= 1, /* log can't be deleted because it is in use */ + HA_LOG_STATUS_NOSUCHLOG= 2 /* no such log (can't be returned by + the log iterator status) */ +}; +/* + Function for signaling that the log file changed its state from + LOG_STATUS_INUSE to LOG_STATUS_FREE + + Now it do nothing, will be implemented as part of new transaction + log management for engines. + TODO: implement the function. +*/ +void signal_log_not_needed(struct handlerton, char *log_file); +/* + Data of transaction log iterator. +*/ +struct handler_log_file_data { + LEX_STRING filename; + enum log_status status; +}; + + +enum handler_iterator_type +{ + /* request of transaction log iterator */ + HA_TRANSACTLOG_ITERATOR= 1 +}; +enum handler_create_iterator_result +{ + HA_ITERATOR_OK, /* iterator created */ + HA_ITERATOR_UNSUPPORTED, /* such type of iterator is not supported */ + HA_ITERATOR_ERROR /* error during iterator creation */ +}; + +/* + Iterator structure. Can be used by handler/handlerton for different purposes. + + Iterator should be created in the way to point "before" the first object + it iterate, so next() call move it to the first object or return !=0 if + there is nothing to iterate through. +*/ +struct handler_iterator { + /* + Moves iterator to next record and return 0 or return !=0 + if there is no records. + iterator_object will be filled by this function if next() returns 0. + Content of the iterator_object depend on iterator type. + */ + int (*next)(struct handler_iterator *, void *iterator_object); + /* + Free resources allocated by iterator, after this call iterator + is not usable. + */ + void (*destroy)(struct handler_iterator *); + /* + Pointer to buffer for the iterator to use. + Should be allocated by function which created the iterator and + destroied by freed by above "destroy" call + */ + void *buffer; +}; /* handlerton is a singleton structure - one instance per storage engine - @@ -475,38 +561,15 @@ enum ha_stat_type { HA_ENGINE_STATUS, HA_ENGINE_LOGS, HA_ENGINE_MUTEX }; struct handlerton { /* - handlerton structure version - */ - const int interface_version; -/* last version change: 0x0001 in 5.1.6 */ -#define MYSQL_HANDLERTON_INTERFACE_VERSION 0x0001 - - - /* - storage engine name as it should be printed to a user - */ - const char *name; - - /* - Historical marker for if the engine is available of not + Historical marker for if the engine is available of not */ SHOW_COMP_OPTION state; /* - A comment used by SHOW to describe an engine. - */ - const char *comment; - - /* Historical number used for frm file to determine the correct storage engine. This is going away and new engines will just use "name" for this. */ enum legacy_db_type db_type; - /* - Method that initizlizes a storage engine - */ - bool (*init)(); - /* each storage engine has it's own memory area (actually a pointer) in the thd, for storing per-connection information. @@ -563,7 +626,7 @@ struct handlerton void *(*create_cursor_read_view)(); void (*set_cursor_read_view)(void *); void (*close_cursor_read_view)(void *); - handler *(*create)(TABLE_SHARE *table); + handler *(*create)(TABLE_SHARE *table, MEM_ROOT *mem_root); void (*drop_database)(char* path); int (*panic)(enum ha_panic_function flag); int (*start_consistent_snapshot)(THD *thd); @@ -585,9 +648,24 @@ struct handlerton const char *query, uint query_length, const char *db, const char *table_name); int (*release_temporary_latches)(THD *thd); -}; -extern const handlerton default_hton; + /* + Get log status. + If log_status is null then the handler do not support transaction + log information (i.e. log iterator can't be created). + (see example of implementation in handler.cc, TRANS_LOG_MGM_EXAMPLE_CODE) + + */ + enum log_status (*get_log_status)(char *log); + + /* + Iterators creator. + Presence of the pointer should be checked before using + */ + enum handler_create_iterator_result + (*create_iterator)(enum handler_iterator_type type, + struct handler_iterator *fill_this_in); +}; struct show_table_alias_st { const char *alias; @@ -747,11 +825,37 @@ typedef struct st_handler_buffer typedef struct system_status_var SSV; +class ha_statistics +{ +public: + ulonglong data_file_length; /* Length off data file */ + ulonglong max_data_file_length; /* Length off data file */ + ulonglong index_file_length; + ulonglong max_index_file_length; + ulonglong delete_length; /* Free bytes */ + ulonglong auto_increment_value; + ha_rows records; /* Estimated records in table */ + ha_rows deleted; /* Deleted records */ + ulong mean_rec_length; /* physical reclength */ + time_t create_time; /* When table was created */ + time_t check_time; + time_t update_time; + uint block_size; /* index block size */ + + ha_statistics(): + data_file_length(0), max_data_file_length(0), + index_file_length(0), delete_length(0), auto_increment_value(0), + records(0), deleted(0), mean_rec_length(0), create_time(0), + check_time(0), update_time(0), block_size(0) + {} +}; + /* The handler class is the interface for dynamically loadable storage engines. Do not add ifdefs and take care when adding or changing virtual functions to avoid vtable confusion */ + class handler :public Sql_alloc { friend class ha_partition; @@ -759,6 +863,7 @@ class handler :public Sql_alloc protected: struct st_table_share *table_share; /* The table definition */ struct st_table *table; /* The current open table */ + ulonglong cached_table_flags; /* Set on init() and open() */ virtual int index_init(uint idx, bool sorted) { active_index=idx; return 0; } virtual int index_end() { active_index=MAX_KEY; return 0; } @@ -771,28 +876,18 @@ class handler :public Sql_alloc */ virtual int rnd_init(bool scan) =0; virtual int rnd_end() { return 0; } - + virtual ulonglong table_flags(void) const =0; void ha_statistic_increment(ulong SSV::*offset) const; - -private: - virtual int reset() { return extra(HA_EXTRA_RESET); } + ha_rows estimation_rows_to_insert; + virtual void start_bulk_insert(ha_rows rows) {} + virtual int end_bulk_insert() {return 0; } public: const handlerton *ht; /* storage engine of this handler */ byte *ref; /* Pointer to current row */ - byte *dupp_ref; /* Pointer to dupp row */ - ulonglong data_file_length; /* Length off data file */ - ulonglong max_data_file_length; /* Length off data file */ - ulonglong index_file_length; - ulonglong max_index_file_length; - ulonglong delete_length; /* Free bytes */ - ulonglong auto_increment_value; - ha_rows records; /* Records in table */ - ha_rows deleted; /* Deleted records */ - ulong mean_rec_length; /* physical reclength */ - time_t create_time; /* When table was created */ - time_t check_time; - time_t update_time; + byte *dup_ref; /* Pointer to duplicate row */ + + ha_statistics stats; /* The following are for read_multi_range */ bool multi_range_sorted; @@ -807,27 +902,20 @@ public: bool eq_range; uint errkey; /* Last dup key */ - uint sortkey, key_used_on_scan; + uint key_used_on_scan; uint active_index; /* Length of ref (1-8 or the clustered key length) */ uint ref_length; - uint block_size; /* index block size */ FT_INFO *ft_handler; enum {NONE=0, INDEX, RND} inited; bool auto_increment_column_changed; bool implicit_emptied; /* Can be !=0 only if HEAP */ const COND *pushed_cond; - MY_BITMAP *read_set; - MY_BITMAP *write_set; handler(const handlerton *ht_arg, TABLE_SHARE *share_arg) - :table_share(share_arg), ht(ht_arg), - ref(0), data_file_length(0), max_data_file_length(0), index_file_length(0), - delete_length(0), auto_increment_value(0), - records(0), deleted(0), mean_rec_length(0), - create_time(0), check_time(0), update_time(0), - key_used_on_scan(MAX_KEY), active_index(MAX_KEY), - ref_length(sizeof(my_off_t)), block_size(0), + :table_share(share_arg), estimation_rows_to_insert(0), ht(ht_arg), + ref(0), key_used_on_scan(MAX_KEY), active_index(MAX_KEY), + ref_length(sizeof(my_off_t)), ft_handler(0), inited(NONE), implicit_emptied(0), pushed_cond(NULL) {} @@ -835,6 +923,11 @@ public: { /* TODO: DBUG_ASSERT(inited == NONE); */ } + /* This is called after create to allow us to set up cached variables */ + void init() + { + cached_table_flags= table_flags(); + } /* Check whether a handler allows to lock the table. @@ -862,10 +955,9 @@ public: { return TRUE; } - virtual int ha_initialise(); int ha_open(TABLE *table, const char *name, int mode, int test_if_locked); bool update_auto_increment(); - void print_keydupp_error(uint key_nr, const char *msg); + void print_keydup_error(uint key_nr, const char *msg); virtual void print_error(int error, myf errflag); virtual bool get_error_message(int error, String *buf); uint get_dup_key(int error); @@ -875,21 +967,27 @@ public: table_share= share; } virtual double scan_time() - { return ulonglong2double(data_file_length) / IO_SIZE + 2; } + { return ulonglong2double(stats.data_file_length) / IO_SIZE + 2; } virtual double read_time(uint index, uint ranges, ha_rows rows) - { return rows2double(ranges+rows); } + { return rows2double(ranges+rows); } virtual const key_map *keys_to_use_for_scanning() { return &key_map_empty; } - virtual bool has_transactions(){ return 0;} + bool has_transactions() + { return (ha_table_flags() & HA_NO_TRANSACTIONS) == 0; } virtual uint extra_rec_buf_length() const { return 0; } /* + Number of rows in table. It will only be called if + (table_flags() & (HA_HAS_RECORDS | HA_STATS_RECORDS_IS_EXACT)) != 0 + */ + virtual ha_rows records() { return stats.records; } + /* Return upper bound of current number of records in the table (max. of how many records one will retrieve when doing a full table scan) If upper bound is not known, HA_POS_ERROR should be returned as a max possible upper bound. */ virtual ha_rows estimate_rows_upper_bound() - { return records+EXTRA_RECORDS; } + { return stats.records+EXTRA_RECORDS; } /* Get the row type from the storage engine. If this method returns @@ -927,139 +1025,23 @@ public: inited=NONE; DBUG_RETURN(rnd_end()); } - int ha_reset() - { - DBUG_ENTER("ha_reset"); - ha_clear_all_set(); - DBUG_RETURN(reset()); - } + int ha_reset(); /* this is necessary in many places, e.g. in HANDLER command */ int ha_index_or_rnd_end() { return inited == INDEX ? ha_index_end() : inited == RND ? ha_rnd_end() : 0; } + longlong ha_table_flags() { return cached_table_flags; } + /* - These are a set of routines used to enable handlers to only read/write - partial lists of the fields in the table. The bit vector is maintained - by the server part and is used by the handler at calls to read/write - data in the table. - It replaces the use of query id's for this purpose. The benefit is that - the handler can also set bits in the read/write set if it has special - needs and it is also easy for other parts of the server to interact - with the handler (e.g. the replication part for row-level logging). - The routines are all part of the general handler and are not possible - to override by a handler. A handler can however set/reset bits by - calling these routines. - - The methods ha_retrieve_all_cols and ha_retrieve_all_pk are made - virtual to handle InnoDB specifics. If InnoDB doesn't need the - extra parameters HA_EXTRA_RETRIEVE_ALL_COLS and - HA_EXTRA_RETRIEVE_PRIMARY_KEY anymore then these methods need not be - virtual anymore. + Signal that the table->read_set and table->write_set table maps changed + The handler is allowed to set additional bits in the above map in this + call. Normally the handler should ignore all calls until we have done + a ha_rnd_init() or ha_index_init(), write_row(), update_row or delete_row() + as there may be several calls to this routine. */ - virtual int ha_retrieve_all_cols(); - virtual int ha_retrieve_all_pk(); - void ha_set_all_bits_in_read_set() - { - DBUG_ENTER("ha_set_all_bits_in_read_set"); - bitmap_set_all(read_set); - DBUG_VOID_RETURN; - } - void ha_set_all_bits_in_write_set() - { - DBUG_ENTER("ha_set_all_bits_in_write_set"); - bitmap_set_all(write_set); - DBUG_VOID_RETURN; - } - void ha_set_bit_in_read_set(uint fieldnr) - { - DBUG_ENTER("ha_set_bit_in_read_set"); - DBUG_PRINT("info", ("fieldnr = %d", fieldnr)); - bitmap_set_bit(read_set, fieldnr); - DBUG_VOID_RETURN; - } - void ha_clear_bit_in_read_set(uint fieldnr) - { - DBUG_ENTER("ha_clear_bit_in_read_set"); - DBUG_PRINT("info", ("fieldnr = %d", fieldnr)); - bitmap_clear_bit(read_set, fieldnr); - DBUG_VOID_RETURN; - } - void ha_set_bit_in_write_set(uint fieldnr) - { - DBUG_ENTER("ha_set_bit_in_write_set"); - DBUG_PRINT("info", ("fieldnr = %d", fieldnr)); - bitmap_set_bit(write_set, fieldnr); - DBUG_VOID_RETURN; - } - void ha_clear_bit_in_write_set(uint fieldnr) - { - DBUG_ENTER("ha_clear_bit_in_write_set"); - DBUG_PRINT("info", ("fieldnr = %d", fieldnr)); - bitmap_clear_bit(write_set, fieldnr); - DBUG_VOID_RETURN; - } - void ha_set_bit_in_rw_set(uint fieldnr, bool write_op) - { - DBUG_ENTER("ha_set_bit_in_rw_set"); - DBUG_PRINT("info", ("Set bit %u in read set", fieldnr)); - bitmap_set_bit(read_set, fieldnr); - if (!write_op) { - DBUG_VOID_RETURN; - } - else - { - DBUG_PRINT("info", ("Set bit %u in read and write set", fieldnr)); - bitmap_set_bit(write_set, fieldnr); - } - DBUG_VOID_RETURN; - } - bool ha_get_bit_in_read_set(uint fieldnr) - { - bool bit_set=bitmap_is_set(read_set,fieldnr); - DBUG_ENTER("ha_get_bit_in_read_set"); - DBUG_PRINT("info", ("bit %u = %u", fieldnr, bit_set)); - DBUG_RETURN(bit_set); - } - bool ha_get_bit_in_write_set(uint fieldnr) - { - bool bit_set=bitmap_is_set(write_set,fieldnr); - DBUG_ENTER("ha_get_bit_in_write_set"); - DBUG_PRINT("info", ("bit %u = %u", fieldnr, bit_set)); - DBUG_RETURN(bit_set); - } - bool ha_get_all_bit_in_read_set() - { - bool all_bits_set= bitmap_is_set_all(read_set); - DBUG_ENTER("ha_get_all_bit_in_read_set"); - DBUG_PRINT("info", ("all bits set = %u", all_bits_set)); - DBUG_RETURN(all_bits_set); - } - bool ha_get_all_bit_in_read_clear() - { - bool all_bits_set= bitmap_is_clear_all(read_set); - DBUG_ENTER("ha_get_all_bit_in_read_clear"); - DBUG_PRINT("info", ("all bits clear = %u", all_bits_set)); - DBUG_RETURN(all_bits_set); - } - bool ha_get_all_bit_in_write_set() - { - bool all_bits_set= bitmap_is_set_all(write_set); - DBUG_ENTER("ha_get_all_bit_in_write_set"); - DBUG_PRINT("info", ("all bits set = %u", all_bits_set)); - DBUG_RETURN(all_bits_set); - } - bool ha_get_all_bit_in_write_clear() - { - bool all_bits_set= bitmap_is_clear_all(write_set); - DBUG_ENTER("ha_get_all_bit_in_write_clear"); - DBUG_PRINT("info", ("all bits clear = %u", all_bits_set)); - DBUG_RETURN(all_bits_set); - } - void ha_set_primary_key_in_read_set(); - int ha_allocate_read_write_set(ulong no_fields); - void ha_clear_all_set(); + virtual void column_bitmaps_signal(); uint get_index(void) const { return active_index; } virtual int open(const char *name, int mode, uint test_if_locked)=0; virtual int close(void)=0; @@ -1212,6 +1194,13 @@ public: { return 0; } virtual int extra_opt(enum ha_extra_function operation, ulong cache_size) { return extra(operation); } + + /* + Reset state of file to after 'open' + This function is called after every statement for all tables used + by that statement. + */ + virtual int reset() { return 0; } /* In an UPDATE or DELETE, if the row under the cursor was locked by another transaction, and the engine used an optimistic read of the last @@ -1242,7 +1231,11 @@ public: */ virtual int delete_all_rows() { return (my_errno=HA_ERR_WRONG_COMMAND); } - virtual ulonglong get_auto_increment(); + virtual void get_auto_increment(ulonglong offset, ulonglong increment, + ulonglong nb_desired_values, + ulonglong *first_value, + ulonglong *nb_reserved_values); + virtual void release_auto_increment() { return; }; virtual void restore_auto_increment(); /* @@ -1303,8 +1296,16 @@ public: virtual int disable_indexes(uint mode) { return HA_ERR_WRONG_COMMAND; } virtual int enable_indexes(uint mode) { return HA_ERR_WRONG_COMMAND; } virtual int indexes_are_disabled(void) {return 0;} - virtual void start_bulk_insert(ha_rows rows) {} - virtual int end_bulk_insert() {return 0; } + void ha_start_bulk_insert(ha_rows rows) + { + estimation_rows_to_insert= rows; + start_bulk_insert(rows); + } + int ha_end_bulk_insert() + { + estimation_rows_to_insert= 0; + return end_bulk_insert(); + } virtual int discard_or_import_tablespace(my_bool discard) {return HA_ERR_WRONG_COMMAND;} virtual int net_read_dump(NET* net) { return HA_ERR_WRONG_COMMAND; } @@ -1341,7 +1342,6 @@ public: /* The following can be called without an open handler */ virtual const char *table_type() const =0; virtual const char **bas_ext() const =0; - virtual ulong table_flags(void) const =0; virtual int get_default_no_partitions(ulonglong max_rows) { return 1;} virtual void set_auto_partitions(partition_info *part_info) { return; } @@ -1450,7 +1450,6 @@ public: false otherwise */ virtual bool primary_key_is_clustered() { return FALSE; } - virtual int cmp_ref(const byte *ref1, const byte *ref2) { return memcmp(ref1, ref2, ref_length); @@ -1466,10 +1465,12 @@ public: cond_push() cond Condition to be pushed. The condition tree must not be modified by the by the caller. + RETURN The 'remainder' condition that caller must use to filter out records. NULL means the handler will not return rows that do not match the passed condition. + NOTES The pushed conditions form a stack (from which one can remove the last pushed condition using cond_pop). @@ -1477,7 +1478,7 @@ public: AND ... AND pushed_condN) or less restrictive condition, depending on handler's capabilities. - handler->extra(HA_EXTRA_RESET) call empties the condition stack. + handler->ha_reset() call empties the condition stack. Calls to rnd_init/rnd_end, index_init/index_end etc do not affect the condition stack. */ @@ -1493,18 +1494,7 @@ public: uint table_changes) { return COMPATIBLE_DATA_NO; } -private: - /* - Row-level primitives for storage engines. These should be - overridden by the storage engine class. To call these methods, use - the corresponding 'ha_*' method above. - */ - virtual int external_lock(THD *thd __attribute__((unused)), - int lock_type __attribute__((unused))) - { - return 0; - } - + /* These are only called from sql_select for internal temporary tables */ virtual int write_row(byte *buf __attribute__((unused))) { return HA_ERR_WRONG_COMMAND; @@ -1520,6 +1510,24 @@ private: { return HA_ERR_WRONG_COMMAND; } + /* + use_hidden_primary_key() is called in case of an update/delete when + (table_flags() and HA_PRIMARY_KEY_REQUIRED_FOR_DELETE) is defined + but we don't have a primary key + */ + virtual void use_hidden_primary_key(); + +private: + /* + Row-level primitives for storage engines. These should be + overridden by the storage engine class. To call these methods, use + the corresponding 'ha_*' method above. + */ + virtual int external_lock(THD *thd __attribute__((unused)), + int lock_type __attribute__((unused))) + { + return 0; + } }; /* Some extern variables used with handlers */ @@ -1553,7 +1561,7 @@ static inline enum legacy_db_type ha_legacy_type(const handlerton *db_type) static inline const char *ha_resolve_storage_engine_name(const handlerton *db_type) { - return db_type == NULL ? "UNKNOWN" : db_type->name; + return db_type == NULL ? "UNKNOWN" : hton2plugin[db_type->slot]->name.str; } static inline bool ha_check_storage_engine_flag(const handlerton *db_type, uint32 flag) diff --git a/sql/item.cc b/sql/item.cc index d596699dd30..5fa3ad61c15 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -551,6 +551,23 @@ bool Item_field::find_item_in_field_list_processor(byte *arg) } +/* + Mark field in read_map + + NOTES + This is used by filesort to register used fields in a a temporary + column read set or to register used fields in a view +*/ + +bool Item_field::register_field_in_read_map(byte *arg) +{ + TABLE *table= (TABLE *) arg; + if (field->table == table || !table) + bitmap_set_bit(field->table->read_set, field->field_index); + return 0; +} + + bool Item::check_cols(uint c) { if (c != 1) @@ -790,14 +807,25 @@ CHARSET_INFO *Item::default_charset() } +/* + Save value in field, but don't give any warnings + + NOTES + This is used to temporary store and retrieve a value in a column, + for example in opt_range to adjust the key value to fit the column. +*/ + int Item::save_in_field_no_warnings(Field *field, bool no_conversions) { int res; - THD *thd= field->table->in_use; + TABLE *table= field->table; + THD *thd= table->in_use; enum_check_fields tmp= thd->count_cuted_fields; + my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set); thd->count_cuted_fields= CHECK_FIELD_IGNORE; res= save_in_field(field, no_conversions); thd->count_cuted_fields= tmp; + dbug_tmp_restore_column_map(table->write_set, old_map); return res; } @@ -2366,7 +2394,8 @@ bool Item_param::set_from_user_var(THD *thd, const user_var_entry *entry) CHARSET_INFO *tocs= thd->variables.collation_connection; uint32 dummy_offset; - value.cs_info.character_set_of_placeholder= fromcs; + value.cs_info.character_set_of_placeholder= + value.cs_info.character_set_client= fromcs; /* Setup source and destination character sets so that they are different only if conversion is necessary: this will @@ -3569,7 +3598,8 @@ bool Item_field::fix_fields(THD *thd, Item **reference) Item** res= find_item_in_list(this, thd->lex->current_select->item_list, &counter, REPORT_EXCEPT_NOT_FOUND, ¬_used); - if (res != (Item **)not_found_item && (*res)->type() == Item::FIELD_ITEM) + if (res != (Item **)not_found_item && + (*res)->type() == Item::FIELD_ITEM) { set_field((*((Item_field**)res))->field); return 0; @@ -3588,7 +3618,7 @@ bool Item_field::fix_fields(THD *thd, Item **reference) if it is not expression from merged VIEW we will set this field. We can leave expression substituted from view for next PS/SP rexecution - (i.e. do not register this substitution for reverting on cleupup() + (i.e. do not register this substitution for reverting on cleanup() (register_item_tree_changing())), because this subtree will be fix_field'ed during setup_tables()->setup_underlying() (i.e. before all other expressions of query, and references on tables which do @@ -3600,13 +3630,13 @@ bool Item_field::fix_fields(THD *thd, Item **reference) return FALSE; if (!outer_fixed && cached_table && cached_table->select_lex && - context->select_lex && - cached_table->select_lex != context->select_lex) + context->select_lex && + cached_table->select_lex != context->select_lex) { int ret; if ((ret= fix_outer_field(thd, &from_field, reference)) < 0) goto error; - else if (!ret) + if (!ret) return FALSE; } @@ -3617,17 +3647,28 @@ bool Item_field::fix_fields(THD *thd, Item **reference) set_if_bigger(thd->lex->in_sum_func->max_arg_level, thd->lex->current_select->nest_level); } - else if (thd->set_query_id) + else if (thd->mark_used_columns != MARK_COLUMNS_NONE) { TABLE *table= field->table; - table->file->ha_set_bit_in_rw_set(field->fieldnr, - (bool)(thd->set_query_id-1)); - if (field->query_id != thd->query_id) + MY_BITMAP *current_bitmap, *other_bitmap; + if (thd->mark_used_columns == MARK_COLUMNS_READ) { - /* We only come here in unions */ - field->query_id=thd->query_id; - table->used_fields++; - table->used_keys.intersect(field->part_of_key); + current_bitmap= table->read_set; + other_bitmap= table->write_set; + } + else + { + current_bitmap= table->write_set; + other_bitmap= table->read_set; + } + if (!bitmap_fast_test_and_set(current_bitmap, field->field_index)) + { + if (!bitmap_is_set(other_bitmap, field->field_index)) + { + /* First usage of column */ + table->used_fields++; // Used to optimize loops + table->used_keys.intersect(field->part_of_key); + } } } #ifndef NO_EMBEDDED_ACCESS_CHECKS @@ -5398,17 +5439,17 @@ void Item_insert_value::print(String *str) void Item_trigger_field::setup_field(THD *thd, TABLE *table, GRANT_INFO *table_grant_info) { - bool save_set_query_id= thd->set_query_id; + enum_mark_columns save_mark_used_columns= thd->mark_used_columns; /* TODO: Think more about consequences of this step. */ - thd->set_query_id= 0; + thd->mark_used_columns= MARK_COLUMNS_NONE; /* Try to find field by its name and if it will be found set field_idx properly. */ (void)find_field_in_table(thd, table, field_name, (uint) strlen(field_name), 0, &field_idx); - thd->set_query_id= save_set_query_id; + thd->mark_used_columns= save_mark_used_columns; triggers= table->triggers; table_grants= table_grant_info; } diff --git a/sql/item.h b/sql/item.h index 2f99034130a..54cab34eb78 100644 --- a/sql/item.h +++ b/sql/item.h @@ -417,6 +417,8 @@ public: required, otherwise we only reading it and SELECT privilege might be required. */ + Settable_routine_parameter() {} + virtual ~Settable_routine_parameter() {} virtual void set_required_privilege(bool rw) {}; /* @@ -760,7 +762,7 @@ public: static CHARSET_INFO *default_charset(); virtual CHARSET_INFO *compare_collation() { return NULL; } - virtual bool walk(Item_processor processor, byte *arg) + virtual bool walk(Item_processor processor, bool walk_subquery, byte *arg) { return (this->*processor)(arg); } @@ -782,7 +784,30 @@ public: virtual bool collect_item_field_processor(byte * arg) { return 0; } virtual bool find_item_in_field_list_processor(byte *arg) { return 0; } virtual bool change_context_processor(byte *context) { return 0; } - virtual bool reset_query_id_processor(byte *query_id) { return 0; } + virtual bool register_field_in_read_map(byte *arg) { return 0; } + /* + Check if a partition function is allowed + SYNOPSIS + check_partition_func_processor() + bool_arg Return argument + RETURN VALUE + 0 + DESCRIPTION + check_partition_func_processor is used to check if a partition function + uses an allowed function. The default is that an item is not allowed + in a partition function. However all mathematical functions, string + manipulation functions, date functions are allowed. Allowed functions + can never depend on server version, they cannot depend on anything + related to the environment. They can also only depend on a set of + fields in the table itself. They cannot depend on other tables and + cannot contain any queries and cannot contain udf's or similar. + If a new Item class is defined and it inherits from a class that is + allowed in a partition function then it is very important to consider + whether this should be inherited to the new class. If not the function + below should be defined in the new Item class. + */ + virtual bool check_partition_func_processor(byte *bool_arg) + { *(bool *)bool_arg= FALSE; return 0; } virtual Item *equal_fields_propagator(byte * arg) { return this; } virtual Item *set_no_const_sub(byte *arg) { return this; } @@ -1073,6 +1098,7 @@ public: Item::maybe_null= TRUE; } + bool check_partition_func_processor(byte *bool_arg) { return 0; } bool fix_fields(THD *, Item **); enum Type type() const; @@ -1119,6 +1145,7 @@ public: Item_num() {} /* Remove gcc warning */ virtual Item_num *neg()= 0; Item *safe_charset_converter(CHARSET_INFO *tocs); + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; #define NO_CACHED_FIELD_INDEX ((uint)(-1)) @@ -1253,13 +1280,8 @@ public: Item *get_tmp_table_item(THD *thd); bool collect_item_field_processor(byte * arg); bool find_item_in_field_list_processor(byte *arg); - bool reset_query_id_processor(byte *arg) - { - field->query_id= *((query_id_t *) arg); - if (result_field) - result_field->query_id= field->query_id; - return 0; - } + bool register_field_in_read_map(byte *arg); + bool check_partition_func_processor(byte *bool_arg) { return 0; } void cleanup(); Item_equal *find_item_equal(COND_EQUAL *cond_equal); Item *equal_fields_propagator(byte *arg); @@ -1303,6 +1325,7 @@ public: bool is_null() { return 1; } void print(String *str) { str->append(STRING_WITH_LEN("NULL")); } Item *safe_charset_converter(CHARSET_INFO *tocs); + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; class Item_null_result :public Item_null @@ -1315,6 +1338,8 @@ public: { save_in_field(result_field, no_conversions); } + bool check_partition_func_processor(byte *bool_arg) + { *(bool *)bool_arg= FALSE; return 0; } }; /* Item represents one placeholder ('?') of prepared statement */ @@ -1605,6 +1630,8 @@ public: {} void print(String *str) { str->append(func_name); } Item *safe_charset_converter(CHARSET_INFO *tocs); + bool check_partition_func_processor(byte *bool_arg) + { *(bool *)bool_arg= FALSE; return 0; } }; @@ -1682,6 +1709,7 @@ public: void print(String *str); // to prevent drop fixed flag (no need parent cleanup call) void cleanup() {} + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; @@ -1696,6 +1724,8 @@ public: {} Item *safe_charset_converter(CHARSET_INFO *tocs); void print(String *str) { str->append(func_name); } + bool check_partition_func_processor(byte *bool_arg) + { *(bool *)bool_arg= FALSE; return 0; } }; @@ -1708,6 +1738,8 @@ public: &my_charset_bin) { max_length=19;} enum_field_types field_type() const { return MYSQL_TYPE_DATETIME; } + bool check_partition_func_processor(byte *bool_arg) + { *(bool *)bool_arg= FALSE; return 0; } }; class Item_empty_string :public Item_string @@ -1730,6 +1762,8 @@ public: unsigned_flag=1; } enum_field_types field_type() const { return int_field_type; } + bool check_partition_func_processor(byte *bool_arg) + { *(bool *)bool_arg= FALSE; return 0; } }; @@ -1753,6 +1787,7 @@ public: void cleanup() {} bool eq(const Item *item, bool binary_cmp) const; virtual Item *safe_charset_converter(CHARSET_INFO *tocs); + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; @@ -1866,8 +1901,8 @@ public: { return ref ? (*ref)->real_item() : this; } - bool walk(Item_processor processor, byte *arg) - { return (*ref)->walk(processor, arg); } + bool walk(Item_processor processor, bool walk_subquery, byte *arg) + { return (*ref)->walk(processor, walk_subquery, arg); } void print(String *str); void cleanup(); Item_field *filed_for_view_update() @@ -1975,6 +2010,8 @@ public: } Item *new_item(); virtual Item *real_item() { return ref; } + bool check_partition_func_processor(byte *bool_arg) + { *(bool *)bool_arg= FALSE; return 0; } }; #ifdef MYSQL_SERVER @@ -2116,9 +2153,9 @@ public: int save_in_field(Field *field_arg, bool no_conversions); table_map used_tables() const { return (table_map)0L; } - bool walk(Item_processor processor, byte *args) + bool walk(Item_processor processor, bool walk_subquery, byte *args) { - return arg->walk(processor, args) || + return arg->walk(processor, walk_subquery, args) || (this->*processor)(args); } @@ -2163,9 +2200,9 @@ public: } table_map used_tables() const { return (table_map)0L; } - bool walk(Item_processor processor, byte *args) + bool walk(Item_processor processor, bool walk_subquery, byte *args) { - return arg->walk(processor, args) || + return arg->walk(processor, walk_subquery, args) || (this->*processor)(args); } }; @@ -2289,6 +2326,7 @@ public: max_length= item->max_length; decimals= item->decimals; collation.set(item->collation); + unsigned_flag= item->unsigned_flag; return 0; }; virtual void store(Item *)= 0; diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 0552e8b6336..3a1f4b50458 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -234,23 +234,31 @@ longlong Item_func_nop_all::val_int() static bool convert_constant_item(THD *thd, Field *field, Item **item) { + int result= 0; if (!(*item)->with_subselect && (*item)->const_item()) { /* For comparison purposes allow invalid dates like 2000-01-32 */ - ulong orig_sql_mode= field->table->in_use->variables.sql_mode; - field->table->in_use->variables.sql_mode|= MODE_INVALID_DATES; + TABLE *table= field->table; + ulong orig_sql_mode= table->in_use->variables.sql_mode; + my_bitmap_map *old_write_map= + dbug_tmp_use_all_columns(table, table->write_set); + my_bitmap_map *old_read_map= + dbug_tmp_use_all_columns(table, table->read_set); + + table->in_use->variables.sql_mode|= MODE_INVALID_DATES; if (!(*item)->save_in_field(field, 1) && !((*item)->null_value)) { - Item *tmp=new Item_int_with_ref(field->val_int(), *item, - test(field->flags & UNSIGNED_FLAG)); - field->table->in_use->variables.sql_mode= orig_sql_mode; + Item *tmp= new Item_int_with_ref(field->val_int(), *item, + test(field->flags & UNSIGNED_FLAG)); if (tmp) thd->change_item_tree(item, tmp); - return 1; // Item was replaced + result= 1; // Item was replaced } - field->table->in_use->variables.sql_mode= orig_sql_mode; + table->in_use->variables.sql_mode= orig_sql_mode; + dbug_tmp_restore_column_map(table->write_set, old_write_map); + dbug_tmp_restore_column_map(table->read_set, old_read_map); } - return 0; + return result; } @@ -2608,14 +2616,14 @@ Item_cond::fix_fields(THD *thd, Item **ref) return FALSE; } -bool Item_cond::walk(Item_processor processor, byte *arg) +bool Item_cond::walk(Item_processor processor, bool walk_subquery, byte *arg) { List_iterator_fast<Item> li(list); Item *item; while ((item= li++)) - if (item->walk(processor, arg)) + if (item->walk(processor, walk_subquery, arg)) return 1; - return Item_func::walk(processor, arg); + return Item_func::walk(processor, walk_subquery, arg); } @@ -3861,14 +3869,16 @@ void Item_equal::fix_length_and_dec() eval_item->cmp_charset= cmp_collation.collation; } -bool Item_equal::walk(Item_processor processor, byte *arg) +bool Item_equal::walk(Item_processor processor, bool walk_subquery, byte *arg) { List_iterator_fast<Item_field> it(fields); Item *item; while ((item= it++)) - if (item->walk(processor, arg)) + { + if (item->walk(processor, walk_subquery, arg)) return 1; - return Item_func::walk(processor, arg); + } + return Item_func::walk(processor, walk_subquery, arg); } Item *Item_equal::transform(Item_transformer transformer, byte *arg) diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 1cfdcef02d0..8bd1b53e226 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -239,6 +239,7 @@ public: } Item *neg_transformer(THD *thd); virtual Item *negated_item(); + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; class Item_func_not :public Item_bool_func @@ -249,6 +250,7 @@ public: enum Functype functype() const { return NOT_FUNC; } const char *func_name() const { return "not"; } Item *neg_transformer(THD *thd); + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; class Item_maxmin_subselect; @@ -463,6 +465,7 @@ public: bool is_bool_func() { return 1; } CHARSET_INFO *compare_collation() { return cmp_collation.collation; } uint decimal_precision() const { return 1; } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; @@ -474,6 +477,7 @@ public: optimize_type select_optimize() const { return OPTIMIZE_NONE; } const char *func_name() const { return "strcmp"; } void print(String *str) { Item_func::print(str); } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; @@ -536,6 +540,7 @@ public: const char *func_name() const { return "ifnull"; } Field *tmp_table_field(TABLE *table); uint decimal_precision() const; + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; @@ -576,6 +581,7 @@ public: void print(String *str) { Item_func::print(str); } table_map not_null_tables() const { return 0; } bool is_null(); + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; @@ -618,6 +624,7 @@ public: void print(String *str); Item *find_item(String *str); CHARSET_INFO *compare_collation() { return cmp_collation.collation; } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; @@ -968,6 +975,7 @@ public: bool nulls_in_row(); bool is_bool_func() { return 1; } CHARSET_INFO *compare_collation() { return cmp_collation.collation; } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; /* Functions used by where clause */ @@ -1009,6 +1017,7 @@ public: optimize_type select_optimize() const { return OPTIMIZE_NULL; } Item *neg_transformer(THD *thd); CHARSET_INFO *compare_collation() { return args[0]->collation.collation; } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; /* Functions used by HAVING for rewriting IN subquery */ @@ -1030,6 +1039,8 @@ public: */ table_map used_tables() const { return used_tables_cache | RAND_TABLE_BIT; } + bool check_partition_func_processor(byte *bool_arg) + { *(bool *)bool_arg= FALSE; return 0; } }; @@ -1052,6 +1063,7 @@ public: void print(String *str); CHARSET_INFO *compare_collation() { return args[0]->collation.collation; } void top_level_item() { abort_on_null=1; } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; @@ -1090,6 +1102,7 @@ public: const char *func_name() const { return "like"; } bool fix_fields(THD *thd, Item **ref); void cleanup(); + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; #ifdef USE_REGEX @@ -1112,6 +1125,7 @@ public: const char *func_name() const { return "regexp"; } void print(String *str) { print_op(str); } CHARSET_INFO *compare_collation() { return cmp_collation.collation; } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; #else @@ -1164,10 +1178,11 @@ public: COND **conds); void top_level_item() { abort_on_null=1; } void copy_andor_arguments(THD *thd, Item_cond *item); - bool walk(Item_processor processor, byte *arg); + bool walk(Item_processor processor, bool walk_subquery, byte *arg); Item *transform(Item_transformer transformer, byte *arg); void traverse_cond(Cond_traverser, void *arg, traverse_order order); void neg_arguments(THD *thd); + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; @@ -1277,7 +1292,7 @@ public: void fix_length_and_dec(); bool fix_fields(THD *thd, Item **ref); void update_used_tables(); - bool walk(Item_processor processor, byte *arg); + bool walk(Item_processor processor, bool walk_subquery, byte *arg); Item *transform(Item_transformer transformer, byte *arg); void print(String *str); CHARSET_INFO *compare_collation() diff --git a/sql/item_func.cc b/sql/item_func.cc index acdaa4b246d..3b633295f20 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -194,14 +194,16 @@ Item_func::fix_fields(THD *thd, Item **ref) return FALSE; } -bool Item_func::walk (Item_processor processor, byte *argument) + +bool Item_func::walk(Item_processor processor, bool walk_subquery, + byte *argument) { if (arg_count) { Item **arg,**arg_end; for (arg= args, arg_end= args+arg_count; arg != arg_end; arg++) { - if ((*arg)->walk(processor, argument)) + if ((*arg)->walk(processor, walk_subquery, argument)) return 1; } } @@ -4397,7 +4399,7 @@ bool Item_func_match::fix_fields(THD *thd, Item **ref) return TRUE; } table=((Item_field *)item)->field->table; - if (!(table->file->table_flags() & HA_CAN_FULLTEXT)) + if (!(table->file->ha_table_flags() & HA_CAN_FULLTEXT)) { my_error(ER_TABLE_CANT_HANDLE_FT, MYF(0)); return 1; diff --git a/sql/item_func.h b/sql/item_func.h index 1d8a1bd5e22..0aedae73bdc 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -182,7 +182,7 @@ public: { return agg_item_charsets(c, func_name(), items, nitems, flags); } - bool walk(Item_processor processor, byte *arg); + bool walk(Item_processor processor, bool walk_subquery, byte *arg); Item *transform(Item_transformer transformer, byte *arg); void traverse_cond(Cond_traverser traverser, void * arg, traverse_order order); @@ -247,6 +247,7 @@ public: void fix_num_length_and_dec(); void find_num_type(); String *str_op(String *str) { DBUG_ASSERT(0); return 0; } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; @@ -259,6 +260,7 @@ class Item_num_op :public Item_func_numhybrid void print(String *str) { print_op(str); } void find_num_type(); String *str_op(String *str) { DBUG_ASSERT(0); return 0; } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; @@ -309,7 +311,7 @@ public: { max_length=args[0]->max_length; unsigned_flag=0; } void print(String *str); uint decimal_precision() const { return args[0]->decimal_precision(); } - + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; @@ -343,6 +345,7 @@ public: void fix_length_and_dec() {}; const char *func_name() const { return "decimal_typecast"; } void print(String *); + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; @@ -411,6 +414,7 @@ public: const char *func_name() const { return "DIV"; } void fix_length_and_dec(); void print(String *str) { print_op(str); } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; @@ -483,6 +487,7 @@ public: Item_func_exp(Item *a) :Item_dec_func(a) {} double val_real(); const char *func_name() const { return "exp"; } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; @@ -492,6 +497,7 @@ public: Item_func_ln(Item *a) :Item_dec_func(a) {} double val_real(); const char *func_name() const { return "ln"; } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; @@ -502,6 +508,7 @@ public: Item_func_log(Item *a,Item *b) :Item_dec_func(a,b) {} double val_real(); const char *func_name() const { return "log"; } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; @@ -511,6 +518,7 @@ public: Item_func_log2(Item *a) :Item_dec_func(a) {} double val_real(); const char *func_name() const { return "log2"; } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; @@ -520,6 +528,7 @@ public: Item_func_log10(Item *a) :Item_dec_func(a) {} double val_real(); const char *func_name() const { return "log10"; } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; @@ -529,6 +538,7 @@ public: Item_func_sqrt(Item *a) :Item_dec_func(a) {} double val_real(); const char *func_name() const { return "sqrt"; } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; @@ -538,6 +548,7 @@ public: Item_func_pow(Item *a,Item *b) :Item_dec_func(a,b) {} double val_real(); const char *func_name() const { return "pow"; } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; @@ -547,6 +558,7 @@ public: Item_func_acos(Item *a) :Item_dec_func(a) {} double val_real(); const char *func_name() const { return "acos"; } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; class Item_func_asin :public Item_dec_func @@ -555,6 +567,7 @@ public: Item_func_asin(Item *a) :Item_dec_func(a) {} double val_real(); const char *func_name() const { return "asin"; } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; class Item_func_atan :public Item_dec_func @@ -564,6 +577,7 @@ public: Item_func_atan(Item *a,Item *b) :Item_dec_func(a,b) {} double val_real(); const char *func_name() const { return "atan"; } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; class Item_func_cos :public Item_dec_func @@ -572,6 +586,7 @@ public: Item_func_cos(Item *a) :Item_dec_func(a) {} double val_real(); const char *func_name() const { return "cos"; } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; class Item_func_sin :public Item_dec_func @@ -580,6 +595,7 @@ public: Item_func_sin(Item *a) :Item_dec_func(a) {} double val_real(); const char *func_name() const { return "sin"; } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; class Item_func_tan :public Item_dec_func @@ -588,6 +604,7 @@ public: Item_func_tan(Item *a) :Item_dec_func(a) {} double val_real(); const char *func_name() const { return "tan"; } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; class Item_func_integer :public Item_int_func @@ -664,6 +681,7 @@ public: Item_func_sign(Item *a) :Item_int_func(a) {} const char *func_name() const { return "sign"; } longlong val_int(); + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; @@ -678,6 +696,7 @@ public: const char *func_name() const { return name; } void fix_length_and_dec() { decimals= NOT_FIXED_DEC; max_length= float_length(decimals); } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; @@ -695,6 +714,7 @@ public: my_decimal *val_decimal(my_decimal *); void fix_length_and_dec(); enum Item_result result_type () const { return cmp_type; } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; class Item_func_min :public Item_func_min_max @@ -720,6 +740,7 @@ public: longlong val_int(); const char *func_name() const { return "length"; } void fix_length_and_dec() { max_length=10; } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; class Item_func_bit_length :public Item_func_length @@ -739,6 +760,7 @@ public: longlong val_int(); const char *func_name() const { return "char_length"; } void fix_length_and_dec() { max_length=10; } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; class Item_func_coercibility :public Item_int_func @@ -749,6 +771,7 @@ public: const char *func_name() const { return "coercibility"; } void fix_length_and_dec() { max_length=10; maybe_null= 0; } table_map not_null_tables() const { return 0; } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; class Item_func_locate :public Item_int_func @@ -762,6 +785,7 @@ public: longlong val_int(); void fix_length_and_dec(); void print(String *str); + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; @@ -786,6 +810,7 @@ public: longlong val_int(); const char *func_name() const { return "ascii"; } void fix_length_and_dec() { max_length=3; } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; class Item_func_ord :public Item_int_func @@ -795,6 +820,7 @@ public: Item_func_ord(Item *a) :Item_int_func(a) {} longlong val_int(); const char *func_name() const { return "ord"; } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; class Item_func_find_in_set :public Item_int_func @@ -808,6 +834,7 @@ public: longlong val_int(); const char *func_name() const { return "find_in_set"; } void fix_length_and_dec(); + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; /* Base class for all bit functions: '~', '|', '^', '&', '>>', '<<' */ @@ -819,6 +846,7 @@ public: Item_func_bit(Item *a) :Item_int_func(a) {} void fix_length_and_dec() { unsigned_flag= 1; } void print(String *str) { print_op(str); } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; class Item_func_bit_or :public Item_func_bit @@ -844,6 +872,7 @@ public: longlong val_int(); const char *func_name() const { return "bit_count"; } void fix_length_and_dec() { max_length=2; } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; class Item_func_shift_left :public Item_func_bit @@ -1280,6 +1309,7 @@ public: longlong val_int(); const char *func_name() const { return "inet_aton"; } void fix_length_and_dec() { decimals = 0; max_length = 21; maybe_null=1;} + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; diff --git a/sql/item_row.cc b/sql/item_row.cc index f5c8d511025..c7b678323a8 100644 --- a/sql/item_row.cc +++ b/sql/item_row.cc @@ -142,16 +142,18 @@ void Item_row::print(String *str) str->append(')'); } -bool Item_row::walk(Item_processor processor, byte *arg) + +bool Item_row::walk(Item_processor processor, bool walk_subquery, byte *arg) { for (uint i= 0; i < arg_count; i++) { - if (items[i]->walk(processor, arg)) + if (items[i]->walk(processor, walk_subquery, arg)) return 1; } return (this->*processor)(arg); } + Item *Item_row::transform(Item_transformer transformer, byte *arg) { for (uint i= 0; i < arg_count; i++) diff --git a/sql/item_row.h b/sql/item_row.h index d6dd4371372..39913086e8d 100644 --- a/sql/item_row.h +++ b/sql/item_row.h @@ -68,7 +68,7 @@ public: void update_used_tables(); void print(String *str); - bool walk(Item_processor processor, byte *arg); + bool walk(Item_processor processor, bool walk_subquery, byte *arg); Item *transform(Item_transformer transformer, byte *arg); uint cols() { return arg_count; } diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index 90d421a2c68..35d8ac1873b 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -47,6 +47,7 @@ public: String *val_str(String *); void fix_length_and_dec(); const char *func_name() const { return "md5"; } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; @@ -57,6 +58,7 @@ public: String *val_str(String *); void fix_length_and_dec(); const char *func_name() const { return "sha"; } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; class Item_func_aes_encrypt :public Item_str_func @@ -87,6 +89,7 @@ public: String *val_str(String *); void fix_length_and_dec(); const char *func_name() const { return "concat"; } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; class Item_func_concat_ws :public Item_str_func @@ -107,6 +110,7 @@ public: String *val_str(String *); void fix_length_and_dec(); const char *func_name() const { return "reverse"; } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; @@ -144,6 +148,7 @@ protected: public: Item_str_conv(Item *item) :Item_str_func(item) {} String *val_str(String *); + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; @@ -207,6 +212,7 @@ public: String *val_str(String *); void fix_length_and_dec(); const char *func_name() const { return "substr"; } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; @@ -218,6 +224,7 @@ public: String *val_str(String *); void fix_length_and_dec(); const char *func_name() const { return "substring_index"; } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; @@ -232,6 +239,7 @@ public: String *val_str(String *); void fix_length_and_dec(); const char *func_name() const { return "trim"; } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; @@ -411,6 +419,7 @@ public: String *val_str(String *); void fix_length_and_dec(); const char *func_name() const { return "soundex"; } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; @@ -446,10 +455,10 @@ public: void update_used_tables(); const char *func_name() const { return "make_set"; } - bool walk(Item_processor processor, byte *arg) + bool walk(Item_processor processor, bool walk_subquery, byte *arg) { - return item->walk(processor, arg) || - Item_str_func::walk(processor, arg); + return item->walk(processor, walk_subquery, arg) || + Item_str_func::walk(processor, walk_subquery, arg); } Item *transform(Item_transformer transformer, byte *arg) { @@ -518,6 +527,7 @@ public: String *val_str(String *); void fix_length_and_dec(); const char *func_name() const { return "rpad"; } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; @@ -530,6 +540,7 @@ public: String *val_str(String *); void fix_length_and_dec(); const char *func_name() const { return "lpad"; } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; @@ -544,6 +555,7 @@ public: collation.set(default_charset()); max_length= 64; } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; @@ -560,6 +572,7 @@ public: decimals=0; max_length=args[0]->max_length*2*collation.collation->mbmaxlen; } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; class Item_func_unhex :public Item_str_func @@ -575,6 +588,7 @@ public: decimals=0; max_length=(1+args[0]->max_length)/2; } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; @@ -598,6 +612,7 @@ public: } void print(String *str); const char *func_name() const { return "cast_as_binary"; } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; @@ -637,6 +652,7 @@ public: String* val_str(String* str); const char *func_name() const { return "inet_ntoa"; } void fix_length_and_dec() { decimals = 0; max_length=3*8+7; } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; class Item_func_quote :public Item_str_func @@ -651,6 +667,7 @@ public: collation.set(args[0]->collation); max_length= args[0]->max_length * 2 + 2; } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; class Item_func_conv_charset :public Item_str_func @@ -693,6 +710,7 @@ public: void fix_length_and_dec(); const char *func_name() const { return "convert"; } void print(String *str); + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; class Item_func_set_collation :public Item_str_func @@ -725,6 +743,7 @@ public: maybe_null= 0; }; table_map not_null_tables() const { return 0; } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; class Item_func_collation :public Item_str_func @@ -740,6 +759,7 @@ public: maybe_null= 0; }; table_map not_null_tables() const { return 0; } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; class Item_func_crc32 :public Item_int_func @@ -750,6 +770,7 @@ public: const char *func_name() const { return "crc32"; } void fix_length_and_dec() { max_length=10; } longlong val_int(); + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; class Item_func_uncompressed_length : public Item_int_func @@ -760,6 +781,7 @@ public: const char *func_name() const{return "uncompressed_length";} void fix_length_and_dec() { max_length=10; } longlong val_int(); + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; #ifdef HAVE_COMPRESS @@ -776,6 +798,7 @@ public: void fix_length_and_dec(){max_length= (args[0]->max_length*120)/100+12;} const char *func_name() const{return "compress";} String *val_str(String *) ZLIB_DEPENDED_FUNCTION + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; class Item_func_uncompress: public Item_str_func @@ -786,6 +809,7 @@ public: void fix_length_and_dec(){max_length= MAX_BLOB_WIDTH;} const char *func_name() const{return "uncompress";} String *val_str(String *) ZLIB_DEPENDED_FUNCTION + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; #define UUID_LENGTH (8+1+4+1+4+1+4+1+12) diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 5280dbf6813..a08ac5d5f6a 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -195,6 +195,46 @@ err: return res; } + +bool Item_subselect::walk(Item_processor processor, bool walk_subquery, + byte *argument) +{ + + if (walk_subquery) + { + for (SELECT_LEX *lex= unit->first_select(); lex; lex= lex->next_select()) + { + List_iterator<Item> li(lex->item_list); + Item *item; + ORDER *order; + + if (lex->where && (lex->where)->walk(processor, walk_subquery, argument)) + return 1; + if (lex->having && (lex->having)->walk(processor, walk_subquery, + argument)) + return 1; + + while ((item=li++)) + { + if (item->walk(processor, walk_subquery, argument)) + return 1; + } + for (order= (ORDER*) lex->order_list.first ; order; order= order->next) + { + if ((*order->item)->walk(processor, walk_subquery, argument)) + return 1; + } + for (order= (ORDER*) lex->group_list.first ; order; order= order->next) + { + if ((*order->item)->walk(processor, walk_subquery, argument)) + return 1; + } + } + } + return (this->*processor)(argument); +} + + bool Item_subselect::exec() { int res; @@ -374,7 +414,7 @@ Item_singlerow_subselect::select_transformer(JOIN *join) as far as we moved content to upper level, field which depend of 'upper' select is not really dependent => we remove this dependence */ - substitution->walk(&Item::remove_dependence_processor, + substitution->walk(&Item::remove_dependence_processor, 0, (byte *) select_lex->outer_select()); /* SELECT without FROM clause can't have WHERE or HAVING clause */ DBUG_ASSERT(join->conds == 0 && join->having == 0); @@ -407,6 +447,7 @@ void Item_singlerow_subselect::fix_length_and_dec() engine->fix_length_and_dec(row); value= *row; } + unsigned_flag= value->unsigned_flag; /* If there are not tables in subquery then ability to have NULL value depends on SELECT list (if single row subquery have tables then it diff --git a/sql/item_subselect.h b/sql/item_subselect.h index 293408dc09e..85bd7a1139d 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -121,6 +121,7 @@ public: */ virtual void reset_value_registration() {} enum_parsing_place place() { return parsing_place; } + bool walk(Item_processor processor, bool walk_subquery, byte *arg); friend class select_subselect; friend class Item_in_optimizer; diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 50c22495463..1285e842769 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -353,14 +353,15 @@ Item *Item_sum::get_tmp_table_item(THD *thd) } -bool Item_sum::walk (Item_processor processor, byte *argument) +bool Item_sum::walk (Item_processor processor, bool walk_subquery, + byte *argument) { if (arg_count) { Item **arg,**arg_end; for (arg= args, arg_end= args+arg_count; arg != arg_end; arg++) { - if ((*arg)->walk(processor, argument)) + if ((*arg)->walk(processor, walk_subquery, argument)) return 1; } } @@ -734,7 +735,7 @@ static int simple_raw_key_cmp(void* arg, const void* key1, const void* key2) static int item_sum_distinct_walk(void *element, element_count num_of_dups, void *item) { - return ((Item_sum_distinct*) (item))->unique_walk_function(element); + return ((Item_sum_distinct*) (item))->unique_walk_function(element); } C_MODE_END @@ -2688,7 +2689,7 @@ longlong Item_sum_count_distinct::val_int() return (longlong) count; } table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK); - return table->file->records; + return table->file->stats.records; } diff --git a/sql/item_sum.h b/sql/item_sum.h index f4ff257aa4e..a0cd08dcb11 100644 --- a/sql/item_sum.h +++ b/sql/item_sum.h @@ -312,7 +312,7 @@ public: Item *get_tmp_table_item(THD *thd); virtual Field *create_tmp_field(bool group, TABLE *table, uint convert_blob_length); - bool walk (Item_processor processor, byte *argument); + bool walk(Item_processor processor, bool walk_subquery, byte *argument); bool init_sum_func_check(THD *thd); bool check_sum_func(THD *thd, Item **ref); bool register_sum_func(THD *thd, Item **ref); diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index 9d98e446c84..a2c7af79ef3 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -2010,39 +2010,11 @@ longlong Item_date_add_interval::val_int() bool Item_date_add_interval::eq(const Item *item, bool binary_cmp) const { - INTERVAL interval, other_interval; - String val= value; // Because of const - - if (this == item) - return TRUE; - - if ((item->type() != FUNC_ITEM) || - (arg_count != ((Item_func*) item)->arg_count) || - (func_name() != ((Item_func*) item)->func_name())) - return FALSE; - Item_date_add_interval *other= (Item_date_add_interval*) item; - - if ((int_type != other->int_type) || - (!args[0]->eq(other->args[0], binary_cmp))) - return FALSE; - - if (!args[1]->const_item() || !other->args[1]->const_item()) - return (args[1]->eq(other->args[1], binary_cmp)); - - if (get_interval_value(args[1], int_type, &val, &interval)) - return FALSE; - - val= other->value; - - if ((get_interval_value(other->args[1], other->int_type, &val, - &other_interval)) || - ((date_sub_interval ^ interval.neg) ^ - (other->date_sub_interval ^ other_interval.neg))) - return FALSE; - - // Assume comparing same types here due to earlier check - return memcmp(&interval, &other_interval, sizeof(INTERVAL)) == 0; + if (!Item_func::eq(item, binary_cmp)) + return 0; + return ((int_type == other->int_type) && + (date_sub_interval == other->date_sub_interval)); } diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h index ae0ca1a0445..69c8ec5959a 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -39,6 +39,7 @@ public: { max_length=6*MY_CHARSET_BIN_MB_MAXLEN; } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; @@ -53,6 +54,7 @@ public: decimals=0; max_length=6*MY_CHARSET_BIN_MB_MAXLEN; } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; @@ -69,6 +71,7 @@ public: maybe_null=1; } enum_monotonicity_info get_monotonicity_info() const; + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; @@ -84,6 +87,7 @@ public: max_length=2*MY_CHARSET_BIN_MB_MAXLEN; maybe_null=1; } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; @@ -108,6 +112,7 @@ public: max_length=2*MY_CHARSET_BIN_MB_MAXLEN; maybe_null=1; } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; @@ -140,6 +145,7 @@ public: max_length=3*MY_CHARSET_BIN_MB_MAXLEN; maybe_null=1; } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; @@ -155,6 +161,7 @@ public: max_length=2*MY_CHARSET_BIN_MB_MAXLEN; maybe_null=1; } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; @@ -170,6 +177,7 @@ public: max_length=2*MY_CHARSET_BIN_MB_MAXLEN; maybe_null=1; } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; @@ -185,6 +193,7 @@ public: max_length=1*MY_CHARSET_BIN_MB_MAXLEN; maybe_null=1; } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; @@ -200,6 +209,7 @@ public: max_length=2*MY_CHARSET_BIN_MB_MAXLEN; maybe_null=1; } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; @@ -215,6 +225,7 @@ public: max_length=2*MY_CHARSET_BIN_MB_MAXLEN; maybe_null=1; } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; class Item_func_yearweek :public Item_int_func @@ -229,6 +240,7 @@ public: max_length=6*MY_CHARSET_BIN_MB_MAXLEN; maybe_null=1; } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; @@ -245,6 +257,7 @@ public: max_length=4*MY_CHARSET_BIN_MB_MAXLEN; maybe_null=1; } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; @@ -274,6 +287,7 @@ public: max_length=1*MY_CHARSET_BIN_MB_MAXLEN; maybe_null=1; } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; class Item_func_dayname :public Item_func_weekday @@ -306,6 +320,7 @@ public: decimals=0; max_length=10*MY_CHARSET_BIN_MB_MAXLEN; } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; @@ -320,6 +335,7 @@ public: decimals=0; max_length=10*MY_CHARSET_BIN_MB_MAXLEN; } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; @@ -526,6 +542,7 @@ public: Item_func_from_days(Item *a) :Item_date(a) {} const char *func_name() const { return "from_days"; } bool get_date(TIME *res, uint fuzzy_date); + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; @@ -543,6 +560,7 @@ public: void fix_length_and_dec(); uint format_length(const String *format); bool eq(const Item *item, bool binary_cmp) const; + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; @@ -561,6 +579,7 @@ class Item_func_from_unixtime :public Item_date_func const char *func_name() const { return "from_unixtime"; } void fix_length_and_dec(); bool get_date(TIME *res, uint fuzzy_date); + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; @@ -627,6 +646,7 @@ public: { return tmp_table_field_from_field_type(table, 0); } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; @@ -649,6 +669,7 @@ public: bool get_date(TIME *res, uint fuzzy_date); bool eq(const Item *item, bool binary_cmp) const; void print(String *str); + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; @@ -666,6 +687,7 @@ class Item_extract :public Item_int_func void fix_length_and_dec(); bool eq(const Item *item, bool binary_cmp) const; void print(String *str); + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; @@ -702,6 +724,7 @@ public: max_length=args[0]->max_length; maybe_null= 1; } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; @@ -721,6 +744,7 @@ public: String *val_str(String *a); void fix_length_and_dec(); void print(String *str); + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; @@ -792,6 +816,7 @@ public: { return tmp_table_field_from_field_type(table, 0); } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; @@ -814,6 +839,7 @@ public: } void print(String *str); const char *func_name() const { return "add_time"; } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; class Item_func_timediff :public Item_str_func @@ -853,6 +879,7 @@ public: { return tmp_table_field_from_field_type(table, 0); } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; class Item_func_microsecond :public Item_int_func @@ -866,6 +893,7 @@ public: decimals=0; maybe_null=1; } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; @@ -883,6 +911,7 @@ public: maybe_null=1; } void print(String *str); + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; @@ -929,6 +958,7 @@ public: { return tmp_table_field_from_field_type(table, 1); } + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; diff --git a/sql/item_xmlfunc.h b/sql/item_xmlfunc.h index bc47e9c5bb1..e11b4eac1e2 100644 --- a/sql/item_xmlfunc.h +++ b/sql/item_xmlfunc.h @@ -42,6 +42,7 @@ public: Item_func_xml_extractvalue(Item *a,Item *b) :Item_xml_str_func(a,b) {} const char *func_name() const { return "extractvalue"; } String *val_str(String *); + bool check_partition_func_processor(byte *bool_arg) { return 0;} }; diff --git a/sql/key.cc b/sql/key.cc index a407fff4840..11dd267875f 100644 --- a/sql/key.cc +++ b/sql/key.cc @@ -301,14 +301,26 @@ bool key_cmp_if_same(TABLE *table,const byte *key,uint idx,uint key_length) return 0; } - /* unpack key-fields from record to some buffer */ - /* This is used to get a good error message */ +/* + unpack key-fields from record to some buffer + + SYNOPSIS + key_unpack() + to Store value here in an easy to read form + table Table to use + idx Key number + + NOTES + This is used mainly to get a good error message + We temporary change the column bitmap so that all columns are readable. +*/ void key_unpack(String *to,TABLE *table,uint idx) { KEY_PART_INFO *key_part,*key_part_end; Field *field; String tmp; + my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set); DBUG_ENTER("key_unpack"); to->length(0); @@ -337,6 +349,7 @@ void key_unpack(String *to,TABLE *table,uint idx) else to->append(STRING_WITH_LEN("???")); } + dbug_tmp_restore_column_map(table->read_set, old_map); DBUG_VOID_RETURN; } @@ -373,7 +386,7 @@ bool check_if_key_used(TABLE *table, uint idx, List<Item> &fields) key is not updated */ if (idx != table->s->primary_key && table->s->primary_key < MAX_KEY && - (table->file->table_flags() & HA_PRIMARY_KEY_IN_READ_INDEX)) + (table->file->ha_table_flags() & HA_PRIMARY_KEY_IN_READ_INDEX)) return check_if_key_used(table, table->s->primary_key, fields); return 0; } diff --git a/sql/lock.cc b/sql/lock.cc index 5a6cd58dd56..e5003325df6 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -828,7 +828,7 @@ int lock_and_wait_for_table_name(THD *thd, TABLE_LIST *table_list) if (wait_if_global_read_lock(thd, 0, 1)) DBUG_RETURN(1); VOID(pthread_mutex_lock(&LOCK_open)); - if ((lock_retcode = lock_table_name(thd, table_list)) < 0) + if ((lock_retcode = lock_table_name(thd, table_list, TRUE)) < 0) goto end; if (lock_retcode && wait_for_locked_table_names(thd, table_list)) { @@ -851,6 +851,7 @@ end: lock_table_name() thd Thread handler table_list Lock first table in this list + check_in_use Do we need to check if table already in use by us WARNING If you are going to update the table, you should use @@ -870,7 +871,7 @@ end: > 0 table locked, but someone is using it */ -int lock_table_name(THD *thd, TABLE_LIST *table_list) +int lock_table_name(THD *thd, TABLE_LIST *table_list, bool check_in_use) { TABLE *table; char key[MAX_DBKEY_LENGTH]; @@ -882,17 +883,22 @@ int lock_table_name(THD *thd, TABLE_LIST *table_list) key_length= create_table_def_key(thd, key, table_list, 0); - /* Only insert the table if we haven't insert it already */ - for (table=(TABLE*) hash_first(&open_cache, (byte*)key, key_length, &state); - table ; - table = (TABLE*) hash_next(&open_cache,(byte*) key,key_length, &state)) + if (check_in_use) { - if (table->in_use == thd) + /* Only insert the table if we haven't insert it already */ + for (table=(TABLE*) hash_first(&open_cache, (byte*)key, + key_length, &state); + table ; + table = (TABLE*) hash_next(&open_cache,(byte*) key, + key_length, &state)) { - DBUG_PRINT("info", ("Table is in use")); - table->s->version= 0; // Ensure no one can use this - table->locked_by_name= 1; - DBUG_RETURN(0); + if (table->in_use == thd) + { + DBUG_PRINT("info", ("Table is in use")); + table->s->version= 0; // Ensure no one can use this + table->locked_by_name= 1; + DBUG_RETURN(0); + } } } /* @@ -917,10 +923,10 @@ int lock_table_name(THD *thd, TABLE_LIST *table_list) my_free((gptr) table,MYF(0)); DBUG_RETURN(-1); } - + /* Return 1 if table is in use */ DBUG_RETURN(test(remove_table_from_cache(thd, db, table_list->table_name, - RTFC_NO_FLAG))); + check_in_use ? RTFC_NO_FLAG : RTFC_WAIT_OTHER_THREAD_FLAG))); } @@ -1003,7 +1009,7 @@ bool lock_table_names(THD *thd, TABLE_LIST *table_list) for (lock_table= table_list; lock_table; lock_table= lock_table->next_local) { int got_lock; - if ((got_lock=lock_table_name(thd,lock_table)) < 0) + if ((got_lock=lock_table_name(thd,lock_table, TRUE)) < 0) goto end; // Fatal error if (got_lock) got_all_locks=0; // Someone is using table @@ -1174,16 +1180,17 @@ bool lock_global_read_lock(THD *thd) if (!thd->global_read_lock) { + const char *old_message; (void) pthread_mutex_lock(&LOCK_global_read_lock); - const char *old_message=thd->enter_cond(&COND_refresh, &LOCK_global_read_lock, - "Waiting to get readlock"); + old_message=thd->enter_cond(&COND_global_read_lock, &LOCK_global_read_lock, + "Waiting to get readlock"); DBUG_PRINT("info", ("waiting_for: %d protect_against: %d", waiting_for_read_lock, protect_against_global_read_lock)); waiting_for_read_lock++; while (protect_against_global_read_lock && !thd->killed) - pthread_cond_wait(&COND_refresh, &LOCK_global_read_lock); + pthread_cond_wait(&COND_global_read_lock, &LOCK_global_read_lock); waiting_for_read_lock--; if (thd->killed) { @@ -1205,9 +1212,15 @@ bool lock_global_read_lock(THD *thd) DBUG_RETURN(0); } + void unlock_global_read_lock(THD *thd) { uint tmp; + DBUG_ENTER("unlock_global_read_lock"); + DBUG_PRINT("info", + ("global_read_lock: %u global_read_lock_blocks_commit: %u", + global_read_lock, global_read_lock_blocks_commit)); + pthread_mutex_lock(&LOCK_global_read_lock); tmp= --global_read_lock; if (thd->global_read_lock == MADE_GLOBAL_READ_LOCK_BLOCK_COMMIT) @@ -1215,8 +1228,13 @@ void unlock_global_read_lock(THD *thd) pthread_mutex_unlock(&LOCK_global_read_lock); /* Send the signal outside the mutex to avoid a context switch */ if (!tmp) - pthread_cond_broadcast(&COND_refresh); + { + DBUG_PRINT("signal", ("Broadcasting COND_global_read_lock")); + pthread_cond_broadcast(&COND_global_read_lock); + } thd->global_read_lock= 0; + + DBUG_VOID_RETURN; } #define must_wait (global_read_lock && \ @@ -1254,11 +1272,15 @@ bool wait_if_global_read_lock(THD *thd, bool abort_on_refresh, */ DBUG_RETURN(is_not_commit); } - old_message=thd->enter_cond(&COND_refresh, &LOCK_global_read_lock, + old_message=thd->enter_cond(&COND_global_read_lock, &LOCK_global_read_lock, "Waiting for release of readlock"); while (must_wait && ! thd->killed && (!abort_on_refresh || thd->version == refresh_version)) - (void) pthread_cond_wait(&COND_refresh,&LOCK_global_read_lock); + { + DBUG_PRINT("signal", ("Waiting for COND_global_read_lock")); + (void) pthread_cond_wait(&COND_global_read_lock, &LOCK_global_read_lock); + DBUG_PRINT("signal", ("Got COND_global_read_lock")); + } if (thd->killed) result=1; } @@ -1287,7 +1309,7 @@ void start_waiting_global_read_lock(THD *thd) (waiting_for_read_lock || global_read_lock_blocks_commit)); (void) pthread_mutex_unlock(&LOCK_global_read_lock); if (tmp) - pthread_cond_broadcast(&COND_refresh); + pthread_cond_broadcast(&COND_global_read_lock); DBUG_VOID_RETURN; } @@ -1309,10 +1331,10 @@ bool make_global_read_lock_block_commit(THD *thd) /* For testing we set up some blocking, to see if we can be killed */ DBUG_EXECUTE_IF("make_global_read_lock_block_commit_loop", protect_against_global_read_lock++;); - old_message= thd->enter_cond(&COND_refresh, &LOCK_global_read_lock, + old_message= thd->enter_cond(&COND_global_read_lock, &LOCK_global_read_lock, "Waiting for all running commits to finish"); while (protect_against_global_read_lock && !thd->killed) - pthread_cond_wait(&COND_refresh, &LOCK_global_read_lock); + pthread_cond_wait(&COND_global_read_lock, &LOCK_global_read_lock); DBUG_EXECUTE_IF("make_global_read_lock_block_commit_loop", protect_against_global_read_lock--;); if ((error= test(thd->killed))) diff --git a/sql/log.cc b/sql/log.cc index 31133a71757..5a17ef817d0 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -47,7 +47,7 @@ ulong sync_binlog_counter= 0; static bool test_if_number(const char *str, long *res, bool allow_wildcards); -static bool binlog_init(); +static int binlog_init(); static int binlog_close_connection(THD *thd); static int binlog_savepoint_set(THD *thd, void *sv); static int binlog_savepoint_rollback(THD *thd, void *sv); @@ -78,49 +78,7 @@ struct binlog_trx_data { Rows_log_event *pending; // The pending binrows event }; -static const char binlog_hton_name[]= "binlog"; -static const char binlog_hton_comment[]= - "This is a meta storage engine to represent the binlog in a transaction"; - -handlerton binlog_hton = { - MYSQL_HANDLERTON_INTERFACE_VERSION, - binlog_hton_name, - SHOW_OPTION_YES, - binlog_hton_comment, - DB_TYPE_BINLOG, /* IGNORE for now */ - binlog_init, - 0, - sizeof(my_off_t), /* savepoint size = binlog offset */ - binlog_close_connection, - binlog_savepoint_set, - binlog_savepoint_rollback, - NULL, /* savepoint_release */ - binlog_commit, - binlog_rollback, - binlog_prepare, - NULL, /* recover */ - NULL, /* commit_by_xid */ - NULL, /* rollback_by_xid */ - NULL, /* create_cursor_read_view */ - NULL, /* set_cursor_read_view */ - NULL, /* close_cursor_read_view */ - NULL, /* Create a new handler */ - NULL, /* Drop a database */ - NULL, /* Panic call */ - NULL, /* Start Consistent Snapshot */ - NULL, /* Flush logs */ - NULL, /* Show status */ - NULL, /* Partition flags */ - NULL, /* Alter table flags */ - NULL, /* Alter Tablespace */ - NULL, /* Fill FILES table */ - HTON_NOT_USER_SELECTABLE | HTON_HIDDEN, - NULL, /* binlog_func */ - NULL, /* binlog_log_query */ - NULL /* release_temporary_latches */ -}; - - +handlerton binlog_hton; /* Open log table of a given type (general or slow log) @@ -209,8 +167,10 @@ bool Log_to_csv_event_handler::open_log_table(uint log_type) table->table->file->ha_rnd_init(0)) error= TRUE; else + { + table->table->use_all_columns(); table->table->locked_by_logger= TRUE; - + } /* restore thread settings */ if (curr) curr->store_globals(); @@ -1066,9 +1026,20 @@ void Log_to_csv_event_handler:: should be moved here. */ -bool binlog_init() +int binlog_init() { - return !opt_bin_log; + + binlog_hton.state=opt_bin_log ? SHOW_OPTION_YES : SHOW_OPTION_NO; + binlog_hton.db_type=DB_TYPE_BINLOG; + binlog_hton.savepoint_offset= sizeof(my_off_t); + binlog_hton.close_connection= binlog_close_connection; + binlog_hton.savepoint_set= binlog_savepoint_set; + binlog_hton.savepoint_rollback= binlog_savepoint_rollback; + binlog_hton.commit= binlog_commit; + binlog_hton.rollback= binlog_rollback; + binlog_hton.prepare= binlog_prepare; + binlog_hton.flags= HTON_NOT_USER_SELECTABLE | HTON_HIDDEN; + return 0; } static int binlog_close_connection(THD *thd) @@ -1184,7 +1155,8 @@ static int binlog_rollback(THD *thd, bool all) table. Such cases should be rare (updating a non-transactional table inside a transaction...) */ - if (unlikely(thd->options & OPTION_STATUS_NO_TRANS_UPDATE)) + if (unlikely(thd->options & (OPTION_STATUS_NO_TRANS_UPDATE | + OPTION_KEEP_LOG))) { Query_log_event qev(thd, STRING_WITH_LEN("ROLLBACK"), TRUE, FALSE); qev.error_code= 0; // see comment in MYSQL_LOG::write(THD, IO_CACHE) @@ -1245,7 +1217,8 @@ static int binlog_savepoint_rollback(THD *thd, void *sv) non-transactional table. Otherwise, truncate the binlog cache starting from the SAVEPOINT command. */ - if (unlikely(thd->options & OPTION_STATUS_NO_TRANS_UPDATE)) + if (unlikely(thd->options & + (OPTION_STATUS_NO_TRANS_UPDATE | OPTION_KEEP_LOG))) { int const error= thd->binlog_query(THD::STMT_QUERY_TYPE, @@ -2672,8 +2645,9 @@ int THD::binlog_write_table_map(TABLE *table, bool is_trans) { int error; DBUG_ENTER("THD::binlog_write_table_map"); - DBUG_PRINT("enter", ("table: %p (%s: #%u)", - table, table->s->table_name, table->s->table_map_id)); + DBUG_PRINT("enter", ("table: %0xlx (%s: #%u)", + (long) table, table->s->table_name, + table->s->table_map_id)); /* Pre-conditions */ DBUG_ASSERT(current_stmt_binlog_row_based && mysql_bin_log.is_open()); @@ -2686,7 +2660,8 @@ int THD::binlog_write_table_map(TABLE *table, bool is_trans) the_event(this, table, table->s->table_map_id, is_trans, flags); if (is_trans) - trans_register_ha(this, options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN), + trans_register_ha(this, + (options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) != 0, &binlog_hton); if ((error= mysql_bin_log.write(&the_event))) @@ -2877,7 +2852,7 @@ bool MYSQL_LOG::write(Log_event *event_info) } #endif /* HAVE_REPLICATION */ -#ifdef USING_TRANSACTIONS +#if defined(USING_TRANSACTIONS) && defined(HAVE_ROW_BASED_REPLICATION) /* Should we write to the binlog cache or to the binlog on disk? Write to the binlog cache if: @@ -2889,10 +2864,8 @@ bool MYSQL_LOG::write(Log_event *event_info) */ if (opt_using_transactions && thd) { -#ifdef HAVE_ROW_BASED_REPLICATION if (thd->binlog_setup_trx_data()) goto err; -#endif /*HAVE_ROW_BASED_REPLICATION*/ binlog_trx_data *const trx_data= (binlog_trx_data*) thd->ha_data[binlog_hton.slot]; @@ -2902,7 +2875,7 @@ bool MYSQL_LOG::write(Log_event *event_info) if (event_info->get_cache_stmt() && !trans_log_in_use) trans_register_ha(thd, (thd->options & - (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)), + (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) != 0, &binlog_hton); if (event_info->get_cache_stmt() || trans_log_in_use) { @@ -2916,7 +2889,7 @@ bool MYSQL_LOG::write(Log_event *event_info) LOCK_log. */ } -#endif +#endif /* USING_TRANSACTIONS && HAVE_ROW_BASED_REPLICATION */ DBUG_PRINT("info",("event type: %d",event_info->get_type_code())); /* @@ -2946,6 +2919,11 @@ bool MYSQL_LOG::write(Log_event *event_info) } if (thd->insert_id_used) { + /* + If the auto_increment was second in a table's index (possible with + MyISAM or BDB) (table->next_number_key_offset != 0), such event is + in fact not necessary. We could avoid logging it. + */ Intvar_log_event e(thd,(uchar) INSERT_ID_EVENT,thd->last_insert_id); if (e.write(file)) goto err; @@ -4372,16 +4350,19 @@ err1: return 1; } +struct st_mysql_storage_engine binlog_storage_engine= +{ MYSQL_HANDLERTON_INTERFACE_VERSION, &binlog_hton }; mysql_declare_plugin(binlog) { MYSQL_STORAGE_ENGINE_PLUGIN, - &binlog_hton, - binlog_hton_name, + &binlog_storage_engine, + "binlog", "MySQL AB", - binlog_hton_comment, - NULL, /* Plugin Init */ + "This is a pseudo storage engine to represent the binlog in a transaction", + binlog_init, /* Plugin Init */ NULL, /* Plugin Deinit */ 0x0100 /* 1.0 */, + 0 } mysql_declare_plugin_end; diff --git a/sql/log_event.cc b/sql/log_event.cc index ab9fa2975a1..78ab54aeb79 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -1744,21 +1744,22 @@ int Query_log_event::exec_event(struct st_relay_log_info* rli, const char *query { if (flags2_inited) /* - all bits of thd->options which are 1 in OPTIONS_WRITTEN_TO_BIN_LOG must - take their value from flags2. + all bits of thd->options which are 1 in OPTIONS_WRITTEN_TO_BIN_LOG + must take their value from flags2. */ - thd->options= flags2|(thd->options & ~(ulong)OPTIONS_WRITTEN_TO_BIN_LOG); + thd->options= flags2|(thd->options & ~OPTIONS_WRITTEN_TO_BIN_LOG); /* else, we are in a 3.23/4.0 binlog; we previously received a - Rotate_log_event which reset thd->options and sql_mode etc, so nothing to do. + Rotate_log_event which reset thd->options and sql_mode etc, so + nothing to do. */ /* We do not replicate IGNORE_DIR_IN_CREATE. That is, if the master is a slave which runs with SQL_MODE=IGNORE_DIR_IN_CREATE, this should not force us to ignore the dir too. Imagine you are a ring of machines, and - one has a disk problem so that you temporarily need IGNORE_DIR_IN_CREATE - on this machine; you don't want it to propagate elsewhere (you don't want - all slaves to start ignoring the dirs). + one has a disk problem so that you temporarily need + IGNORE_DIR_IN_CREATE on this machine; you don't want it to propagate + elsewhere (you don't want all slaves to start ignoring the dirs). */ if (sql_mode_inited) thd->variables.sql_mode= @@ -3264,8 +3265,8 @@ int Rotate_log_event::exec_event(struct st_relay_log_info* rli) rli->notify_group_master_log_name_update(); rli->group_master_log_pos= pos; rli->group_relay_log_pos= rli->event_relay_log_pos; - DBUG_PRINT("info", ("group_master_log_name: '%s' group_master_log_pos:\ -%lu", + DBUG_PRINT("info", ("group_master_log_name: '%s' " + "group_master_log_pos: %lu", rli->group_master_log_name, (ulong) rli->group_master_log_pos)); /* @@ -3481,24 +3482,12 @@ int Rand_log_event::exec_event(struct st_relay_log_info* rli) Xid_log_event methods **************************************************************************/ -#if !defined(DBUG_OFF) && !defined(MYSQL_CLIENT) -/* - This static class member could be removed when mysqltest is made to support - a --replace-regex command: then tests which have XIDs in their output can - use this command to suppress non-deterministic XID values. -*/ -my_bool Xid_log_event::show_xid; -#endif - #if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) void Xid_log_event::pack_info(Protocol *protocol) { char buf[128], *pos; pos= strmov(buf, "COMMIT /* xid="); -#if !defined(DBUG_OFF) && !defined(MYSQL_CLIENT) - if (show_xid) -#endif - pos= longlong10_to_str(xid, pos, 10); + pos= longlong10_to_str(xid, pos, 10); pos= strmov(pos, " */"); protocol->store(buf, (uint) (pos-buf), &my_charset_bin); } @@ -5212,8 +5201,9 @@ int Rows_log_event::do_add_row_data(byte *const row_data, log only the primary key value instead of the entire "before image". This would save binlog space. TODO */ - DBUG_ENTER("Rows_log_event::do_add_row_data(byte *data, my_size_t length)"); - DBUG_PRINT("enter", ("row_data= %p, length= %lu", row_data, length)); + DBUG_ENTER("Rows_log_event::do_add_row_data"); + DBUG_PRINT("enter", ("row_data: 0x%lx length: %lu", (ulong) row_data, + length)); DBUG_DUMP("row_data", (const char*)row_data, min(length, 32)); DBUG_ASSERT(m_rows_buf <= m_rows_cur); @@ -5268,7 +5258,7 @@ static char const *unpack_row(TABLE *table, { DBUG_ASSERT(record && row); - MY_BITMAP *write_set= table->file->write_set; + MY_BITMAP *write_set= table->write_set; my_size_t const n_null_bytes= table->s->null_bytes; my_ptrdiff_t const offset= record - (byte*) table->record[0]; @@ -5281,13 +5271,13 @@ static char const *unpack_row(TABLE *table, { Field *const f= *field_ptr; - if (bitmap_is_set(cols, field_ptr - begin_ptr)) + if (bitmap_is_set(cols, (uint) (field_ptr - begin_ptr))) { /* Field...::unpack() cannot return 0 */ ptr= f->unpack(f->ptr + offset, ptr); } else - bitmap_clear_bit(write_set, (field_ptr - begin_ptr) + 1); + bitmap_clear_bit(write_set, (uint) (field_ptr - begin_ptr)); } return ptr; } @@ -5443,7 +5433,8 @@ int Rows_log_event::exec_event(st_relay_log_info *rli) DBUG_ASSERT(sizeof(thd->options) == sizeof(OPTION_RELAXED_UNIQUE_CHECKS)); error= do_before_row_operations(table); - while (error == 0 && row_start < (const char*)m_rows_end) { + while (error == 0 && row_start < (const char*) m_rows_end) + { char const *row_end= do_prepare_row(thd, table, row_start); DBUG_ASSERT(row_end != NULL); // cannot happen DBUG_ASSERT(row_end <= (const char*)m_rows_end); @@ -5478,8 +5469,10 @@ int Rows_log_event::exec_event(st_relay_log_info *rli) rli->abort_slave=1;); error= do_after_row_operations(table, error); if (!cache_stmt) - thd->options|= OPTION_STATUS_NO_TRANS_UPDATE; - + { + DBUG_PRINT("info", ("Marked that we need to keep log")); + thd->options|= OPTION_KEEP_LOG; + } } if (error) @@ -6142,7 +6135,7 @@ int Write_rows_log_event::do_before_row_operations(TABLE *table) how many rows are going to be inserted, then it can allocate needed memory from the start. */ - table->file->start_bulk_insert(0); + table->file->ha_start_bulk_insert(0); /* We need TIMESTAMP_NO_AUTO_SET otherwise ha_write_row() will not use fill any TIMESTAMP column with data from the row but instead will use @@ -6165,7 +6158,7 @@ int Write_rows_log_event::do_before_row_operations(TABLE *table) int Write_rows_log_event::do_after_row_operations(TABLE *table, int error) { if (error == 0) - error= table->file->end_bulk_insert(); + error= table->file->ha_end_bulk_insert(); return error; } @@ -6267,9 +6260,9 @@ replace_record(THD *thd, TABLE *table) - use index_read_idx() with the key that is duplicated, to retrieve the offending row. */ - if (table->file->table_flags() & HA_DUPP_POS) + if (table->file->ha_table_flags() & HA_DUPLICATE_POS) { - error= table->file->rnd_pos(table->record[1], table->file->dupp_ref); + error= table->file->rnd_pos(table->record[1], table->file->dup_ref); if (error) return error; } @@ -6386,16 +6379,17 @@ static bool record_compare(TABLE *table) to find (and fetch) the row. If the engine allows random access of the records, a combination of position() and rnd_pos() will be used. */ + static int find_and_fetch_row(TABLE *table, byte *key) { DBUG_ENTER("find_and_fetch_row(TABLE *table, byte *key, byte *record)"); - DBUG_PRINT("enter", ("table=%p, key=%p, record=%p", - table, key, table->record[1])); + DBUG_PRINT("enter", ("table: 0x%lx, key: 0x%lx record: 0x%lx", + (long) table, (long) key, (long) table->record[1])); DBUG_ASSERT(table->in_use != NULL); - if ((table->file->table_flags() & HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS) - && table->s->primary_key < MAX_KEY) + if ((table->file->ha_table_flags() & HA_PRIMARY_KEY_REQUIRED_FOR_POSITION) && + table->s->primary_key < MAX_KEY) { /* Use a more efficient method to fetch the record given by @@ -6411,15 +6405,15 @@ static int find_and_fetch_row(TABLE *table, byte *key) DBUG_ASSERT(table->record[1]); /* We need to retrieve all fields */ - table->file->ha_set_all_bits_in_read_set(); + /* TODO: Move this out from this function to main loop */ + table->use_all_columns(); if (table->s->keys > 0) { int error; /* We have a key: search the table using the index */ - if (!table->file->inited) - if ((error= table->file->ha_index_init(0, FALSE))) - return error; + if (!table->file->inited && (error= table->file->ha_index_init(0, FALSE))) + return error; /* We need to set the null bytes to ensure that the filler bit are @@ -6452,7 +6446,7 @@ static int find_and_fetch_row(TABLE *table, byte *key) comparison of non-PK columns to decide if the correct record is found. I can see no scenario where it would be incorrect to chose the row to change only using a PK or an UNNI. - */ + */ if (table->key_info->flags & HA_NOSAME) { table->file->ha_index_end(); @@ -6576,7 +6570,7 @@ int Delete_rows_log_event::do_before_row_operations(TABLE *table) { DBUG_ASSERT(m_memory == NULL); - if ((table->file->table_flags() & HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS) && + if ((table->file->ha_table_flags() & HA_PRIMARY_KEY_REQUIRED_FOR_POSITION) && table->s->primary_key < MAX_KEY) { /* @@ -6650,19 +6644,18 @@ char const *Delete_rows_log_event::do_prepare_row(THD *thd, TABLE *table, int Delete_rows_log_event::do_exec_row(TABLE *table) { + int error; DBUG_ASSERT(table != NULL); - int error= find_and_fetch_row(table, m_key); - if (error) - return error; - - /* - Now we should have the right row to delete. We are using - record[0] since it is guaranteed to point to a record with the - correct value. - */ - error= table->file->ha_delete_row(table->record[0]); - + if (!(error= find_and_fetch_row(table, m_key))) + { + /* + Now we should have the right row to delete. We are using + record[0] since it is guaranteed to point to a record with the + correct value. + */ + error= table->file->ha_delete_row(table->record[0]); + } return error; } diff --git a/sql/log_event.h b/sql/log_event.h index b24686514e3..36933f4a7dd 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -1221,9 +1221,6 @@ class Xid_log_event: public Log_event bool write(IO_CACHE* file); #endif bool is_valid() const { return 1; } -#if !defined(DBUG_OFF) && !defined(MYSQL_CLIENT) - static my_bool show_xid; -#endif }; /***************************************************************************** @@ -1635,6 +1632,8 @@ public: #endif char *str_to_hex(char *to, const char *from, uint len); +#ifdef HAVE_ROW_BASED_REPLICATION + /***************************************************************************** Table map log event class @@ -1643,7 +1642,6 @@ char *str_to_hex(char *to, const char *from, uint len); identifier (an integer number). ****************************************************************************/ - class Table_map_log_event : public Log_event { public: @@ -1750,6 +1748,7 @@ private: ****************************************************************************/ + class Rows_log_event : public Log_event { public: @@ -2121,5 +2120,6 @@ private: #endif }; +#endif /* HAVE_ROW_BASED_REPLICATION */ #endif /* _log_event_h */ diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index e3db96be2e7..3d499b67519 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -279,9 +279,9 @@ extern CHARSET_INFO *national_charset_info, *table_alias_charset; #define OPTION_BEGIN (LL(1) << 20) // THD, intern #define OPTION_TABLE_LOCK (LL(1) << 21) // THD, intern #define OPTION_QUICK (LL(1) << 22) // SELECT (for DELETE) +#define OPTION_KEEP_LOG (LL(1) << 23) // Keep binlog on rollback -/* Thr following is used to detect a conflict with DISTINCT - in the user query has requested */ +/* The following is used to detect a conflict with DISTINCT */ #define SELECT_ALL (LL(1) << 24) // SELECT, user, parser /* Set if we are updating a non-transaction safe table */ @@ -504,13 +504,13 @@ typedef my_bool (*qc_engine_callback)(THD *thd, char *table_key, #include "sql_list.h" #include "sql_map.h" #include "my_decimal.h" +#include "sql_plugin.h" #include "handler.h" #include "parse_file.h" #include "table.h" #include "sql_error.h" #include "field.h" /* Field definitions */ #include "protocol.h" -#include "sql_plugin.h" #include "sql_udf.h" #include "sql_partition.h" @@ -849,6 +849,7 @@ bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *t); bool mysql_derived_filling(THD *thd, LEX *lex, TABLE_LIST *t); Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, Item ***copy_func, Field **from_field, + Field **def_field, bool group, bool modify_item, bool table_cant_handle_bit_fields, bool make_copy_field, @@ -1105,20 +1106,28 @@ bool insert_fields(THD *thd, Name_resolution_context *context, List_iterator<Item> *it, bool any_privileges); bool setup_tables(THD *thd, Name_resolution_context *context, List<TABLE_LIST> *from_clause, TABLE_LIST *tables, - Item **conds, TABLE_LIST **leaves, bool select_insert); + TABLE_LIST **leaves, bool select_insert); +bool setup_tables_and_check_access(THD *thd, + Name_resolution_context *context, + List<TABLE_LIST> *from_clause, + TABLE_LIST *tables, + TABLE_LIST **leaves, + bool select_insert, + ulong want_access); int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields, List<Item> *sum_func_list, uint wild_num); bool setup_fields(THD *thd, Item** ref_pointer_array, - List<Item> &item, ulong set_query_id, + List<Item> &item, enum_mark_columns mark_used_columns, List<Item> *sum_func_list, bool allow_sum_func); inline bool setup_fields_with_no_wrap(THD *thd, Item **ref_pointer_array, - List<Item> &item, ulong set_query_id, - List<Item> *sum_func_list, - bool allow_sum_func) + List<Item> &item, + enum_mark_columns mark_used_columns, + List<Item> *sum_func_list, + bool allow_sum_func) { bool res; thd->lex->select_lex.no_wrap_view_item= TRUE; - res= setup_fields(thd, ref_pointer_array, item, set_query_id, sum_func_list, + res= setup_fields(thd, ref_pointer_array, item, mark_used_columns, sum_func_list, allow_sum_func); thd->lex->select_lex.no_wrap_view_item= FALSE; return res; @@ -1165,7 +1174,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, HA_CREATE_INFO *create_info, TABLE_LIST *table_list, List<create_field> *create_list, - List<Key> *key_list, const char *db, + List<Key> *key_list, char *db, const char *table_name, uint fast_alter_partition); uint prep_alter_part_table(THD *thd, TABLE *table, ALTER_INFO *alter_info, @@ -1195,10 +1204,12 @@ void create_subpartition_name(char *out, const char *in1, typedef struct st_lock_param_type { + TABLE_LIST table_list; ulonglong copied; ulonglong deleted; THD *thd; HA_CREATE_INFO *create_info; + ALTER_INFO *alter_info; List<create_field> *create_list; List<create_field> new_create_list; List<Key> *key_list; @@ -1549,6 +1560,7 @@ extern pthread_cond_t COND_server_started; extern int mysqld_server_started; extern rw_lock_t LOCK_grant, LOCK_sys_init_connect, LOCK_sys_init_slave; extern pthread_cond_t COND_refresh, COND_thread_count, COND_manager; +extern pthread_cond_t COND_global_read_lock; extern pthread_attr_t connection_attrib; extern I_List<THD> threads; extern I_List<NAMED_LIST> key_caches; @@ -1677,7 +1689,7 @@ void unset_protect_against_global_read_lock(void); /* Lock based on name */ int lock_and_wait_for_table_name(THD *thd, TABLE_LIST *table_list); -int lock_table_name(THD *thd, TABLE_LIST *table_list); +int lock_table_name(THD *thd, TABLE_LIST *table_list, bool check_in_use); void unlock_table_name(THD *thd, TABLE_LIST *table_list); bool wait_for_locked_table_names(THD *thd, TABLE_LIST *table_list); bool lock_table_names(THD *thd, TABLE_LIST *table_list); @@ -1765,7 +1777,8 @@ void init_read_record_idx(READ_RECORD *info, THD *thd, TABLE *table, void end_read_record(READ_RECORD *info); ha_rows filesort(THD *thd, TABLE *form,struct st_sort_field *sortorder, uint s_length, SQL_SELECT *select, - ha_rows max_rows, ha_rows *examined_rows); + ha_rows max_rows, bool sort_positions, + ha_rows *examined_rows); void filesort_free_buffers(TABLE *table); void change_double_for_sort(double nr,byte *to); double my_double_round(double value, int dec, bool truncate); diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 2a8fe21f656..56c3b1857a8 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -329,7 +329,7 @@ static char *opt_init_slave, *language_ptr, *opt_init_connect; static char *default_character_set_name; static char *character_set_filesystem_name; static char *my_bind_addr_str; -static char *default_collation_name; +static char *default_collation_name, *default_storage_engine_str; static char mysql_data_home_buff[2]; static struct passwd *user_info; static I_List<THD> thread_cache; @@ -605,7 +605,7 @@ pthread_mutex_t LOCK_prepared_stmt_count; pthread_mutex_t LOCK_des_key_file; #endif rw_lock_t LOCK_grant, LOCK_sys_init_connect, LOCK_sys_init_slave; -pthread_cond_t COND_refresh,COND_thread_count; +pthread_cond_t COND_refresh, COND_thread_count, COND_global_read_lock; pthread_t signal_thread; pthread_attr_t connection_attrib; pthread_mutex_t LOCK_server_started; @@ -1024,7 +1024,20 @@ void kill_mysql(void) DBUG_VOID_RETURN; } - /* Force server down. kill all connections and threads and exit */ +/* + Force server down. Kill all connections and threads and exit + + SYNOPSIS + kill_server + + sig_ptr Signal number that caused kill_server to be called. + + NOTE! + A signal number of 0 mean that the function was not called + from a signal handler and there is thus no signal to block + or stop, we just want to kill the server. + +*/ #if defined(__NETWARE__) extern "C" void kill_server(int sig_ptr) @@ -1068,7 +1081,11 @@ static void __cdecl kill_server(int sig_ptr) my_thread_init(); // If this is a new thread #endif close_connections(); - if (sig != MYSQL_KILL_SIGNAL && sig != 0) + if (sig != MYSQL_KILL_SIGNAL && +#ifdef __WIN__ + sig != SIGINT && /* Bug#18235 */ +#endif + sig != 0) unireg_abort(1); /* purecov: inspected */ else unireg_end(); @@ -1322,6 +1339,7 @@ static void clean_up_mutexes() (void) pthread_mutex_destroy(&LOCK_prepared_stmt_count); (void) pthread_cond_destroy(&COND_thread_count); (void) pthread_cond_destroy(&COND_refresh); + (void) pthread_cond_destroy(&COND_global_read_lock); (void) pthread_cond_destroy(&COND_thread_cache); (void) pthread_cond_destroy(&COND_flush_thread_cache); (void) pthread_cond_destroy(&COND_manager); @@ -1744,13 +1762,11 @@ void end_thread(THD *thd, bool put_in_cache) } } - DBUG_PRINT("info", ("sending a broadcast")); - /* Tell main we are ready */ (void) pthread_mutex_unlock(&LOCK_thread_count); /* It's safe to broadcast outside a lock (COND... is not deleted here) */ + DBUG_PRINT("signal", ("Broadcasting COND_thread_count")); (void) pthread_cond_broadcast(&COND_thread_count); - DBUG_PRINT("info", ("unlocked thread_count mutex")); #ifdef ONE_THREAD if (!(test_flags & TEST_NO_THREADS)) // For debugging under Linux #endif @@ -2456,10 +2472,12 @@ static int my_message_sql(uint error, const char *str, myf MyFlags) if (thd->lex->current_select && thd->lex->current_select->no_error && !thd->is_fatal_error) { - DBUG_PRINT("error", ("Error converted to warning: current_select: no_error %d fatal_error: %d", - (thd->lex->current_select ? - thd->lex->current_select->no_error : 0), - (int) thd->is_fatal_error)); + DBUG_PRINT("error", + ("Error converted to warning: current_select: no_error %d " + "fatal_error: %d", + (thd->lex->current_select ? + thd->lex->current_select->no_error : 0), + (int) thd->is_fatal_error)); } else { @@ -2638,12 +2656,6 @@ static int init_common_variables(const char *conf_file_name, int argc, if (add_status_vars(status_vars)) return 1; // an error was already reported - if (plugin_init()) - { - sql_print_error("Failed to init plugins."); - return 1; - } - load_defaults(conf_file_name, groups, &argc, &argv); defaults_argv=argv; get_options(argc,argv); @@ -2846,6 +2858,7 @@ static int init_thread_environment() (void) my_rwlock_init(&LOCK_grant, NULL); (void) pthread_cond_init(&COND_thread_count,NULL); (void) pthread_cond_init(&COND_refresh,NULL); + (void) pthread_cond_init(&COND_global_read_lock,NULL); (void) pthread_cond_init(&COND_thread_cache,NULL); (void) pthread_cond_init(&COND_flush_thread_cache,NULL); (void) pthread_cond_init(&COND_manager,NULL); @@ -3159,6 +3172,12 @@ server."); using_update_log=1; } + if (plugin_init()) + { + sql_print_error("Failed to init plugins."); + return 1; + } + /* We have to initialize the storage engines before CSV logging */ if (ha_init()) { @@ -3205,15 +3224,27 @@ server."); /* Check that the default storage engine is actually available. */ - if (!ha_storage_engine_is_enabled(global_system_variables.table_type)) { - if (!opt_bootstrap) + LEX_STRING name= { default_storage_engine_str, + strlen(default_storage_engine_str) }; + handlerton *hton= ha_resolve_by_name(0, &name); + if (hton == NULL) { - sql_print_error("Default storage engine (%s) is not available", - global_system_variables.table_type->name); + sql_print_error("Unknown/unsupported table type: %s", + default_storage_engine_str); unireg_abort(1); } - global_system_variables.table_type= &myisam_hton; + if (!ha_storage_engine_is_enabled(hton)) + { + if (!opt_bootstrap) + { + sql_print_error("Default storage engine (%s) is not available", + default_storage_engine_str); + unireg_abort(1); + } + hton= &myisam_hton; + } + global_system_variables.table_type= hton; } tc_log= (total_ha_2pc > 1 ? (opt_bin_log ? @@ -4727,6 +4758,7 @@ enum options_mysqld OPT_INNODB_FILE_IO_THREADS, OPT_INNODB_LOCK_WAIT_TIMEOUT, OPT_INNODB_THREAD_CONCURRENCY, + OPT_INNODB_COMMIT_CONCURRENCY, OPT_INNODB_FORCE_RECOVERY, OPT_INNODB_STATUS_FILE, OPT_INNODB_MAX_DIRTY_PAGES_PCT, @@ -4888,14 +4920,6 @@ Disable with --skip-bdb (will save memory).", {"binlog-ignore-db", OPT_BINLOG_IGNORE_DB, "Tells the master that updates to the given database should not be logged tothe binary log.", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, -#if !defined(DBUG_OFF) && !defined(MYSQL_CLIENT) - {"binlog-show-xid", OPT_BINLOG_SHOW_XID, - "Option used by mysql-test for debugging and testing: " - "do not display the XID in SHOW BINLOG EVENTS; " - "may be removed in future versions", - (gptr*) &Xid_log_event::show_xid, (gptr*) &Xid_log_event::show_xid, - 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0}, -#endif #ifdef HAVE_ROW_BASED_REPLICATION {"binlog-row-event-max-size", OPT_BINLOG_ROWS_EVENT_MAX_SIZE, "The maximum size of a row-based binary log event in bytes. Rows will be " @@ -4962,7 +4986,8 @@ Disable with --skip-bdb (will save memory).", "Set the default storage engine (table type) for tables.", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"default-table-type", OPT_STORAGE_ENGINE, - "(deprecated) Use --default-storage-engine.", 0, 0, + "(deprecated) Use --default-storage-engine.", + (gptr*)default_storage_engine_str, (gptr*)default_storage_engine_str, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"default-time-zone", OPT_DEFAULT_TIME_ZONE, "Set the default time zone.", (gptr*) &default_tz_name, (gptr*) &default_tz_name, @@ -5008,7 +5033,7 @@ Disable with --skip-bdb (will save memory).", REQUIRED_ARG, 2/*default*/, 0/*min-value*/, 2/*max-value*/, 0, 0, 0}, {"exit-info", 'T', "Used for debugging; Use at your own risk!", 0, 0, 0, GET_LONG, OPT_ARG, 0, 0, 0, 0, 0, 0}, - {"external-locking", OPT_USE_LOCKING, "Use system (external) locking. With this option enabled you can run myisamchk to test (not repair) tables while the MySQL server is running.", + {"external-locking", OPT_USE_LOCKING, "Use system (external) locking (disabled by default). With this option enabled you can run myisamchk to test (not repair) tables while the MySQL server is running. Disable with --skip-external-locking.", (gptr*) &opt_external_locking, (gptr*) &opt_external_locking, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"flush", OPT_FLUSH, "Flush tables to disk between SQL commands.", 0, 0, 0, @@ -5673,7 +5698,6 @@ log and this option does nothing anymore.", "The buffer that is allocated to cache index and rows for BDB tables.", (gptr*) &berkeley_cache_size, (gptr*) &berkeley_cache_size, 0, GET_ULL, REQUIRED_ARG, KEY_CACHE_SIZE, 20*1024, (ulonglong) ~0, 0, IO_SIZE, 0}, - /* QQ: The following should be removed soon! (bdb_max_lock preferred) */ {"bdb_lock_max", OPT_BDB_MAX_LOCK, "Synonym for bdb_max_lock.", (gptr*) &berkeley_max_lock, (gptr*) &berkeley_max_lock, 0, GET_ULONG, REQUIRED_ARG, 10000, 0, (long) ~0, 0, 1, 0}, @@ -5790,7 +5814,7 @@ log and this option does nothing anymore.", (gptr*) &innobase_buffer_pool_size, (gptr*) &innobase_buffer_pool_size, 0, GET_LL, REQUIRED_ARG, 8*1024*1024L, 1024*1024L, LONGLONG_MAX, 0, 1024*1024L, 0}, - {"innodb_commit_concurrency", OPT_INNODB_THREAD_CONCURRENCY, + {"innodb_commit_concurrency", OPT_INNODB_COMMIT_CONCURRENCY, "Helps in performance tuning in heavily concurrent environments.", (gptr*) &srv_commit_concurrency, (gptr*) &srv_commit_concurrency, 0, GET_LONG, REQUIRED_ARG, 0, 0, 1000, 0, 1, 0}, @@ -5842,7 +5866,7 @@ log and this option does nothing anymore.", {"innodb_thread_concurrency", OPT_INNODB_THREAD_CONCURRENCY, "Helps in performance tuning in heavily concurrent environments.", (gptr*) &srv_thread_concurrency, (gptr*) &srv_thread_concurrency, - 0, GET_LONG, REQUIRED_ARG, 20, 1, 1000, 0, 1, 0}, + 0, GET_LONG, REQUIRED_ARG, 8, 1, 1000, 0, 1, 0}, {"innodb_thread_sleep_delay", OPT_INNODB_THREAD_SLEEP_DELAY, "Time of innodb thread sleeping before joining InnoDB queue (usec). Value 0" " disable a sleep", @@ -6991,8 +7015,8 @@ static void mysql_init_variables(void) sys_charset_system.value= (char*) system_charset_info->csname; character_set_filesystem_name= (char*) "binary"; - /* Set default values for some option variables */ + default_storage_engine_str= (char*) "MyISAM"; global_system_variables.table_type= &myisam_hton; global_system_variables.tx_isolation= ISO_REPEATABLE_READ; global_system_variables.select_limit= (ulonglong) HA_POS_ERROR; @@ -7452,17 +7476,6 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), case OPT_BOOTSTRAP: opt_noacl=opt_bootstrap=1; break; - case OPT_STORAGE_ENGINE: - { - LEX_STRING name= { argument, strlen(argument) }; - if ((global_system_variables.table_type= - ha_resolve_by_name(current_thd, &name)) == NULL) - { - fprintf(stderr,"Unknown/unsupported table type: %s\n",argument); - exit(1); - } - break; - } case OPT_SERVER_ID: server_id_supplied = 1; break; diff --git a/sql/opt_range.cc b/sql/opt_range.cc index ffaf3fad6c8..7fa47b0b005 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -374,6 +374,12 @@ public: keys_map.clear_all(); bzero((char*) keys,sizeof(keys)); } + /* + Note: there may exist SEL_TREE objects with sel_tree->type=KEY and + keys[i]=0 for all i. (SergeyP: it is not clear whether there is any + merit in range analyzer functions (e.g. get_mm_parts) returning a + pointer to such SEL_TREE instead of NULL) + */ SEL_ARG *keys[MAX_KEY]; key_map keys_map; /* bitmask of non-NULL elements in keys */ @@ -822,6 +828,10 @@ QUICK_RANGE_SELECT::QUICK_RANGE_SELECT(THD *thd, TABLE *table, uint key_nr, bool no_alloc, MEM_ROOT *parent_alloc) :dont_free(0),error(0),free_file(0),in_range(0),cur_range(NULL),range(0) { + my_bitmap_map *bitmap; + DBUG_ENTER("QUICK_RANGE_SELECT::QUICK_RANGE_SELECT"); + + in_ror_merged_scan= 0; sorted= 0; index= key_nr; head= table; @@ -845,6 +855,19 @@ QUICK_RANGE_SELECT::QUICK_RANGE_SELECT(THD *thd, TABLE *table, uint key_nr, bzero((char*) &alloc,sizeof(alloc)); file= head->file; record= head->record[0]; + save_read_set= head->read_set; + save_write_set= head->write_set; + + /* Allocate a bitmap for used columns */ + if (!(bitmap= (my_bitmap_map*) my_malloc(head->s->column_bitmap_size, + MYF(MY_WME)))) + { + column_bitmap.bitmap= 0; + error= 1; + } + else + bitmap_init(&column_bitmap, bitmap, head->s->fields, FALSE); + DBUG_VOID_RETURN; } @@ -874,24 +897,26 @@ QUICK_RANGE_SELECT::~QUICK_RANGE_SELECT() if (file) { range_end(); - file->extra(HA_EXTRA_NO_KEYREAD); if (free_file) { DBUG_PRINT("info", ("Freeing separate handler %p (free=%d)", file, free_file)); - file->ha_reset(); file->ha_external_lock(current_thd, F_UNLCK); file->close(); delete file; } + else + { + file->extra(HA_EXTRA_NO_KEYREAD); + } } delete_dynamic(&ranges); /* ranges are allocated in alloc */ free_root(&alloc,MYF(0)); + my_free((char*) column_bitmap.bitmap, MYF(MY_ALLOW_ZERO_PTR)); } - if (multi_range) - my_free((char*) multi_range, MYF(0)); - if (multi_range_buff) - my_free((char*) multi_range_buff, MYF(0)); + head->column_bitmaps_set(save_read_set, save_write_set); + x_free(multi_range); + x_free(multi_range_buff); DBUG_VOID_RETURN; } @@ -1011,20 +1036,21 @@ int QUICK_ROR_INTERSECT_SELECT::init() int QUICK_RANGE_SELECT::init_ror_merged_scan(bool reuse_handler) { - handler *save_file= file; + handler *save_file= file, *org_file; THD *thd; + MY_BITMAP *bitmap; DBUG_ENTER("QUICK_RANGE_SELECT::init_ror_merged_scan"); + in_ror_merged_scan= 1; if (reuse_handler) { - DBUG_PRINT("info", ("Reusing handler %p", file)); - if (file->extra(HA_EXTRA_KEYREAD) || - file->ha_retrieve_all_pk() || - init() || reset()) + DBUG_PRINT("info", ("Reusing handler 0x%lx", (long) file)); + if (init() || reset()) { DBUG_RETURN(1); } - DBUG_RETURN(0); + head->column_bitmaps_set(&column_bitmap, &column_bitmap); + goto end; } /* Create a separate handler object for this quick select */ @@ -1037,19 +1063,20 @@ int QUICK_RANGE_SELECT::init_ror_merged_scan(bool reuse_handler) thd= head->in_use; if (!(file= get_new_handler(head->s, thd->mem_root, head->s->db_type))) goto failure; - DBUG_PRINT("info", ("Allocated new handler %p", file)); + DBUG_PRINT("info", ("Allocated new handler 0x%lx", (long) file)); if (file->ha_open(head, head->s->normalized_path.str, head->db_stat, HA_OPEN_IGNORE_IF_LOCKED)) { /* Caller will free the memory */ goto failure; } + + head->column_bitmaps_set(&column_bitmap, &column_bitmap); + if (file->ha_external_lock(thd, F_RDLCK)) goto failure; - if (file->extra(HA_EXTRA_KEYREAD) || - file->ha_retrieve_all_pk() || - init() || reset()) + if (init() || reset()) { file->ha_external_lock(thd, F_UNLCK); file->close(); @@ -1057,11 +1084,28 @@ int QUICK_RANGE_SELECT::init_ror_merged_scan(bool reuse_handler) } free_file= TRUE; last_rowid= file->ref; + +end: + /* + We are only going to read key fields and call position() on 'file' + The following sets head->tmp_set to only use this key and then updates + head->read_set and head->write_set to use this bitmap. + The now bitmap is stored in 'column_bitmap' which is used in ::get_next() + */ + org_file= head->file; + head->file= file; + /* We don't have to set 'head->keyread' here as the 'file' is unique */ + head->mark_columns_used_by_index(index); + head->prepare_for_position(); + head->file= org_file; + bitmap_copy(&column_bitmap, head->read_set); + head->column_bitmaps_set(&column_bitmap, &column_bitmap); + DBUG_RETURN(0); failure: - if (file) - delete file; + head->column_bitmaps_set(save_read_set, save_write_set); + delete file; file= save_file; DBUG_RETURN(1); } @@ -1766,32 +1810,26 @@ public: static int fill_used_fields_bitmap(PARAM *param) { TABLE *table= param->table; - param->fields_bitmap_size= bitmap_buffer_size(table->s->fields+1); - uint32 *tmp; + my_bitmap_map *tmp; uint pk; - if (!(tmp= (uint32*) alloc_root(param->mem_root,param->fields_bitmap_size)) || - bitmap_init(¶m->needed_fields, tmp, param->fields_bitmap_size*8, - FALSE)) + param->fields_bitmap_size= table->s->column_bitmap_size; + if (!(tmp= (my_bitmap_map*) alloc_root(param->mem_root, + param->fields_bitmap_size)) || + bitmap_init(¶m->needed_fields, tmp, table->s->fields, FALSE)) return 1; - bitmap_clear_all(¶m->needed_fields); - for (uint i= 0; i < table->s->fields; i++) - { - if (param->thd->query_id == table->field[i]->query_id) - bitmap_set_bit(¶m->needed_fields, i+1); - } + bitmap_copy(¶m->needed_fields, table->read_set); + bitmap_union(¶m->needed_fields, table->write_set); pk= param->table->s->primary_key; - if (param->table->file->primary_key_is_clustered() && pk != MAX_KEY) + if (pk != MAX_KEY && param->table->file->primary_key_is_clustered()) { /* The table uses clustered PK and it is not internally generated */ KEY_PART_INFO *key_part= param->table->key_info[pk].key_part; KEY_PART_INFO *key_part_end= key_part + param->table->key_info[pk].key_parts; for (;key_part != key_part_end; ++key_part) - { - bitmap_clear_bit(¶m->needed_fields, key_part->fieldnr); - } + bitmap_clear_bit(¶m->needed_fields, key_part->fieldnr-1); } return 0; } @@ -1843,7 +1881,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use, DBUG_PRINT("enter",("keys_to_use: %lu prev_tables: %lu const_tables: %lu", keys_to_use.to_ulonglong(), (ulong) prev_tables, (ulong) const_tables)); - DBUG_PRINT("info", ("records=%lu", (ulong)head->file->records)); + DBUG_PRINT("info", ("records: %lu", (ulong) head->file->stats.records)); delete quick; quick=0; needed_reg.clear_all(); @@ -1853,7 +1891,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use, DBUG_RETURN(0); /* purecov: inspected */ if (keys_to_use.is_clear_all()) DBUG_RETURN(0); - records= head->file->records; + records= head->file->stats.records; if (!records) records++; /* purecov: inspected */ scan_time= (double) records / TIME_FOR_COMPARE + 1; @@ -1878,7 +1916,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use, /* set up parameter that is passed to all functions */ param.thd= thd; - param.baseflag=head->file->table_flags(); + param.baseflag=head->file->ha_table_flags(); param.prev_tables=prev_tables | const_tables; param.read_tables=read_tables; param.current_table= head->map; @@ -2296,6 +2334,7 @@ bool prune_partitions(THD *thd, TABLE *table, Item *pprune_cond) PART_PRUNE_PARAM prune_param; MEM_ROOT alloc; RANGE_OPT_PARAM *range_par= &prune_param.range_param; + my_bitmap_map *old_read_set, *old_write_set; prune_param.part_info= part_info; init_sql_alloc(&alloc, thd->variables.range_alloc_block_size, 0); @@ -2309,6 +2348,8 @@ bool prune_partitions(THD *thd, TABLE *table, Item *pprune_cond) DBUG_RETURN(FALSE); } + old_write_set= dbug_tmp_use_all_columns(table, table->write_set); + old_read_set= dbug_tmp_use_all_columns(table, table->read_set); range_par->thd= thd; range_par->table= table; /* range_par->cond doesn't need initialization */ @@ -2398,6 +2439,8 @@ all_used: retval= FALSE; // some partitions are used mark_all_partitions_as_used(prune_param.part_info); end: + dbug_tmp_restore_column_map(table->write_set, old_write_set); + dbug_tmp_restore_column_map(table->read_set, old_read_set); thd->no_errors=0; thd->mem_root= range_par->old_root; free_root(&alloc,MYF(0)); // Return memory & allocator @@ -2424,6 +2467,8 @@ end: void store_key_image_to_rec(Field *field, char *ptr, uint len) { /* Do the same as print_key() does */ + my_bitmap_map *old_map; + if (field->real_maybe_null()) { if (*ptr) @@ -2434,7 +2479,10 @@ void store_key_image_to_rec(Field *field, char *ptr, uint len) field->set_notnull(); ptr++; } + old_map= dbug_tmp_use_all_columns(field->table, + field->table->write_set); field->set_key_image(ptr, len); + dbug_tmp_restore_column_map(field->table->write_set, old_map); } @@ -2514,11 +2562,11 @@ static int find_used_partitions_imerge_list(PART_PRUNE_PARAM *ppar, { MY_BITMAP all_merges; uint bitmap_bytes; - uint32 *bitmap_buf; + my_bitmap_map *bitmap_buf; uint n_bits= ppar->part_info->used_partitions.n_bits; bitmap_bytes= bitmap_buffer_size(n_bits); - if (!(bitmap_buf= (uint32*)alloc_root(ppar->range_param.mem_root, - bitmap_bytes))) + if (!(bitmap_buf= (my_bitmap_map*) alloc_root(ppar->range_param.mem_root, + bitmap_bytes))) { /* Fallback, process just the first SEL_IMERGE. This can leave us with more @@ -2580,7 +2628,8 @@ int find_used_partitions_imerge(PART_PRUNE_PARAM *ppar, SEL_IMERGE *imerge) ppar->cur_part_fields= 0; ppar->cur_subpart_fields= 0; init_all_partitions_iterator(ppar->part_info, &ppar->part_iter); - if (-1 == (res |= find_used_partitions(ppar, (*ptree)->keys[0]))) + SEL_ARG *key_tree= (*ptree)->keys[0]; + if (!key_tree || (-1 == (res |= find_used_partitions(ppar, key_tree)))) return -1; } return res; @@ -2764,7 +2813,8 @@ int find_used_partitions(PART_PRUNE_PARAM *ppar, SEL_ARG *key_tree) uint32 subpart_id; bitmap_clear_all(&ppar->subparts_bitmap); - while ((subpart_id= subpart_iter.get_next(&subpart_iter)) != NOT_A_PARTITION_ID) + while ((subpart_id= subpart_iter.get_next(&subpart_iter)) != + NOT_A_PARTITION_ID) bitmap_set_bit(&ppar->subparts_bitmap, subpart_id); /* Mark each partition as used in each subpartition. */ @@ -2870,7 +2920,8 @@ process_next_key_part: /* Got "full range" for subpartitioning fields */ uint32 part_id; bool found= FALSE; - while ((part_id= ppar->part_iter.get_next(&ppar->part_iter)) != NOT_A_PARTITION_ID) + while ((part_id= ppar->part_iter.get_next(&ppar->part_iter)) != + NOT_A_PARTITION_ID) { ppar->mark_full_partition_used(ppar->part_info, part_id); found= TRUE; @@ -3017,11 +3068,12 @@ static bool create_partition_index_description(PART_PRUNE_PARAM *ppar) if (ppar->subpart_fields) { - uint32 *buf; + my_bitmap_map *buf; uint32 bufsize= bitmap_buffer_size(ppar->part_info->no_subparts); - if (!(buf= (uint32*)alloc_root(alloc, bufsize))) + if (!(buf= (my_bitmap_map*) alloc_root(alloc, bufsize))) return TRUE; - bitmap_init(&ppar->subparts_bitmap, buf, ppar->part_info->no_subparts, FALSE); + bitmap_init(&ppar->subparts_bitmap, buf, ppar->part_info->no_subparts, + FALSE); } range_par->key_parts= key_part; Field **field= (ppar->part_fields)? part_info->part_field_array : @@ -3188,7 +3240,8 @@ double get_sweep_read_cost(const PARAM *param, ha_rows records) else { double n_blocks= - ceil(ulonglong2double(param->table->file->data_file_length) / IO_SIZE); + ceil(ulonglong2double(param->table->file->stats.data_file_length) / + IO_SIZE); double busy_blocks= n_blocks * (1.0 - pow(1.0 - 1.0/n_blocks, rows2double(records))); if (busy_blocks < 1.0) @@ -3357,7 +3410,7 @@ TABLE_READ_PLAN *get_best_disjunct_quick(PARAM *param, SEL_IMERGE *imerge, DBUG_PRINT("info", ("index_merge scans cost=%g", imerge_cost)); if (imerge_too_expensive || (imerge_cost > read_time) || - (non_cpk_scan_records+cpk_scan_records >= param->table->file->records) && + (non_cpk_scan_records+cpk_scan_records >= param->table->file->stats.records) && read_time != DBL_MAX) { /* @@ -3415,7 +3468,7 @@ TABLE_READ_PLAN *get_best_disjunct_quick(PARAM *param, SEL_IMERGE *imerge, imerge_trp->read_cost= imerge_cost; imerge_trp->records= non_cpk_scan_records + cpk_scan_records; imerge_trp->records= min(imerge_trp->records, - param->table->file->records); + param->table->file->stats.records); imerge_trp->range_scans= range_scans; imerge_trp->range_scans_end= range_scans + n_child_scans; read_time= imerge_cost; @@ -3476,7 +3529,7 @@ skip_to_ror_scan: ((TRP_ROR_INTERSECT*)(*cur_roru_plan))->index_scan_costs; roru_total_records += (*cur_roru_plan)->records; roru_intersect_part *= (*cur_roru_plan)->records / - param->table->file->records; + param->table->file->stats.records; } /* @@ -3486,7 +3539,7 @@ skip_to_ror_scan: in disjunction do not share key parts. */ roru_total_records -= (ha_rows)(roru_intersect_part* - param->table->file->records); + param->table->file->stats.records); /* ok, got a ROR read plan for each of the disjuncts Calculate cost: cost(index_union_scan(scan_1, ... scan_n)) = @@ -3547,7 +3600,7 @@ static double get_index_only_read_time(const PARAM* param, ha_rows records, int keynr) { double read_time; - uint keys_per_block= (param->table->file->block_size/2/ + uint keys_per_block= (param->table->file->stats.block_size/2/ (param->table->key_info[keynr].key_length+ param->table->file->ref_length) + 1); read_time=((double) (records+keys_per_block-1)/ @@ -3599,7 +3652,7 @@ static ROR_SCAN_INFO *make_ror_scan(const PARAM *param, int idx, SEL_ARG *sel_arg) { ROR_SCAN_INFO *ror_scan; - uint32 *bitmap_buf; + my_bitmap_map *bitmap_buf; uint keynr; DBUG_ENTER("make_ror_scan"); @@ -3614,12 +3667,12 @@ ROR_SCAN_INFO *make_ror_scan(const PARAM *param, int idx, SEL_ARG *sel_arg) ror_scan->sel_arg= sel_arg; ror_scan->records= param->table->quick_rows[keynr]; - if (!(bitmap_buf= (uint32*)alloc_root(param->mem_root, - param->fields_bitmap_size))) + if (!(bitmap_buf= (my_bitmap_map*) alloc_root(param->mem_root, + param->fields_bitmap_size))) DBUG_RETURN(NULL); if (bitmap_init(&ror_scan->covered_fields, bitmap_buf, - param->fields_bitmap_size*8, FALSE)) + param->table->s->fields, FALSE)) DBUG_RETURN(NULL); bitmap_clear_all(&ror_scan->covered_fields); @@ -3628,8 +3681,8 @@ ROR_SCAN_INFO *make_ror_scan(const PARAM *param, int idx, SEL_ARG *sel_arg) param->table->key_info[keynr].key_parts; for (;key_part != key_part_end; ++key_part) { - if (bitmap_is_set(¶m->needed_fields, key_part->fieldnr)) - bitmap_set_bit(&ror_scan->covered_fields, key_part->fieldnr); + if (bitmap_is_set(¶m->needed_fields, key_part->fieldnr-1)) + bitmap_set_bit(&ror_scan->covered_fields, key_part->fieldnr-1); } ror_scan->index_read_cost= get_index_only_read_time(param, param->table->quick_rows[ror_scan->keynr], @@ -3729,21 +3782,21 @@ static ROR_INTERSECT_INFO* ror_intersect_init(const PARAM *param) { ROR_INTERSECT_INFO *info; - uint32* buf; + my_bitmap_map* buf; if (!(info= (ROR_INTERSECT_INFO*)alloc_root(param->mem_root, sizeof(ROR_INTERSECT_INFO)))) return NULL; info->param= param; - if (!(buf= (uint32*)alloc_root(param->mem_root, - param->fields_bitmap_size))) + if (!(buf= (my_bitmap_map*) alloc_root(param->mem_root, + param->fields_bitmap_size))) return NULL; - if (bitmap_init(&info->covered_fields, buf, param->fields_bitmap_size*8, + if (bitmap_init(&info->covered_fields, buf, param->table->s->fields, FALSE)) return NULL; info->is_covering= FALSE; info->index_scan_costs= 0.0; info->index_records= 0; - info->out_rows= param->table->file->records; + info->out_rows= param->table->file->stats.records; bitmap_clear_all(&info->covered_fields); return info; } @@ -3862,14 +3915,14 @@ static double ror_scan_selectivity(const ROR_INTERSECT_INFO *info, SEL_ARG *sel_arg, *tuple_arg= NULL; bool cur_covered; bool prev_covered= test(bitmap_is_set(&info->covered_fields, - key_part->fieldnr)); + key_part->fieldnr-1)); key_range min_range; key_range max_range; min_range.key= (byte*) key_val; min_range.flag= HA_READ_KEY_EXACT; max_range.key= (byte*) key_val; max_range.flag= HA_READ_AFTER_KEY; - ha_rows prev_records= info->param->table->file->records; + ha_rows prev_records= info->param->table->file->stats.records; DBUG_ENTER("ror_intersect_selectivity"); for (sel_arg= scan->sel_arg; sel_arg; @@ -3877,7 +3930,7 @@ static double ror_scan_selectivity(const ROR_INTERSECT_INFO *info, { DBUG_PRINT("info",("sel_arg step")); cur_covered= test(bitmap_is_set(&info->covered_fields, - key_part[sel_arg->part].fieldnr)); + key_part[sel_arg->part].fieldnr-1)); if (cur_covered != prev_covered) { /* create (part1val, ..., part{n-1}val) tuple. */ @@ -4006,15 +4059,15 @@ static bool ror_intersect_add(ROR_INTERSECT_INFO *info, } info->total_cost= info->index_scan_costs; - DBUG_PRINT("info", ("info->total_cost= %g", info->total_cost)); + DBUG_PRINT("info", ("info->total_cost: %g", info->total_cost)); if (!info->is_covering) { info->total_cost += get_sweep_read_cost(info->param, double2rows(info->out_rows)); DBUG_PRINT("info", ("info->total_cost= %g", info->total_cost)); } - DBUG_PRINT("info", ("New out_rows= %g", info->out_rows)); - DBUG_PRINT("info", ("New cost= %g, %scovering", info->total_cost, + DBUG_PRINT("info", ("New out_rows: %g", info->out_rows)); + DBUG_PRINT("info", ("New cost: %g, %scovering", info->total_cost, info->is_covering?"" : "non-")); DBUG_RETURN(TRUE); } @@ -4093,7 +4146,7 @@ TRP_ROR_INTERSECT *get_best_ror_intersect(const PARAM *param, SEL_TREE *tree, double min_cost= DBL_MAX; DBUG_ENTER("get_best_ror_intersect"); - if ((tree->n_ror_scans < 2) || !param->table->file->records) + if ((tree->n_ror_scans < 2) || !param->table->file->stats.records) DBUG_RETURN(NULL); /* @@ -4262,7 +4315,8 @@ TRP_ROR_INTERSECT *get_best_ror_intersect(const PARAM *param, SEL_TREE *tree, F=set of all fields to cover S={} - do { + do + { Order I by (#covered fields in F desc, #components asc, number of first not covered component asc); @@ -4280,7 +4334,6 @@ TRP_ROR_INTERSECT *get_best_covering_ror_intersect(PARAM *param, ROR_SCAN_INFO **ror_scan_mark; ROR_SCAN_INFO **ror_scans_end= tree->ror_scans_end; DBUG_ENTER("get_best_covering_ror_intersect"); - uint nbits= param->fields_bitmap_size*8; for (ROR_SCAN_INFO **scan= tree->ror_scans; scan != ror_scans_end; ++scan) (*scan)->key_components= @@ -4294,9 +4347,9 @@ TRP_ROR_INTERSECT *get_best_covering_ror_intersect(PARAM *param, /*I=set of all covering indexes */ ror_scan_mark= tree->ror_scans; - uint32 int_buf[MAX_KEY/32+1]; + my_bitmap_map int_buf[MAX_KEY/(sizeof(my_bitmap_map)*8)+1]; MY_BITMAP covered_fields; - if (bitmap_init(&covered_fields, int_buf, nbits, FALSE)) + if (bitmap_init(&covered_fields, int_buf, param->table->s->fields, FALSE)) DBUG_RETURN(0); bitmap_clear_all(&covered_fields); @@ -4545,7 +4598,8 @@ QUICK_SELECT_I *TRP_ROR_INTERSECT::make_quick(PARAM *param, if ((quick_intrsect= new QUICK_ROR_INTERSECT_SELECT(param->thd, param->table, - retrieve_full_rows? (!is_covering):FALSE, + (retrieve_full_rows? (!is_covering) : + FALSE), parent_alloc))) { DBUG_EXECUTE("info", print_ror_scans_arr(param->table, @@ -5104,7 +5158,7 @@ get_mm_parts(RANGE_OPT_PARAM *param, COND *cond_func, Field *field, tree->keys_map.set_bit(key_part->key); } } - + DBUG_RETURN(tree); } @@ -7218,7 +7272,7 @@ QUICK_RANGE_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table, goto err; quick->records= records; - if (cp_buffer_from_ref(thd,ref) && thd->is_fatal_error || + if (cp_buffer_from_ref(thd, table, ref) && thd->is_fatal_error || !(range= new(alloc) QUICK_RANGE())) goto err; // out of memory @@ -7281,10 +7335,9 @@ err: rowids into Unique, get the sorted sequence and destroy the Unique. If table has a clustered primary key that covers all rows (TRUE for bdb - and innodb currently) and one of the index_merge scans is a scan on PK, - then - rows that will be retrieved by PK scan are not put into Unique and - primary key scan is not performed here, it is performed later separately. + and innodb currently) and one of the index_merge scans is a scan on PK, + then rows that will be retrieved by PK scan are not put into Unique and + primary key scan is not performed here, it is performed later separately. RETURN 0 OK @@ -7297,21 +7350,17 @@ int QUICK_INDEX_MERGE_SELECT::read_keys_and_merge() QUICK_RANGE_SELECT* cur_quick; int result; Unique *unique; - DBUG_ENTER("QUICK_INDEX_MERGE_SELECT::prepare_unique"); + MY_BITMAP *save_read_set, *save_write_set; + handler *file= head->file; + DBUG_ENTER("QUICK_INDEX_MERGE_SELECT::read_keys_and_merge"); /* We're going to just read rowids. */ - if (head->file->extra(HA_EXTRA_KEYREAD)) - DBUG_RETURN(1); - - /* - Make innodb retrieve all PK member fields, so - * ha_innobase::position (which uses them) call works. - * We can filter out rows that will be retrieved by clustered PK. - (This also creates a deficiency - it is possible that we will retrieve - parts of key that are not used by current query at all.) - */ - if (head->file->ha_retrieve_all_pk()) - DBUG_RETURN(1); + save_read_set= head->read_set; + save_write_set= head->write_set; + file->extra(HA_EXTRA_KEYREAD); + bitmap_clear_all(&head->tmp_set); + head->column_bitmaps_set(&head->tmp_set, &head->tmp_set); + head->prepare_for_position(); cur_quick_it.rewind(); cur_quick= cur_quick_it++; @@ -7324,8 +7373,8 @@ int QUICK_INDEX_MERGE_SELECT::read_keys_and_merge() if (cur_quick->init() || cur_quick->reset()) DBUG_RETURN(1); - unique= new Unique(refpos_order_cmp, (void *)head->file, - head->file->ref_length, + unique= new Unique(refpos_order_cmp, (void *)file, + file->ref_length, thd->variables.sortbuff_size); if (!unique) DBUG_RETURN(1); @@ -7368,15 +7417,16 @@ int QUICK_INDEX_MERGE_SELECT::read_keys_and_merge() } + DBUG_PRINT("info", ("ok")); /* ok, all row ids are in Unique */ result= unique->get(head); delete unique; doing_pk_scan= FALSE; + /* index_merge currently doesn't support "using index" at all */ + file->extra(HA_EXTRA_NO_KEYREAD); + head->column_bitmaps_set(save_read_set, save_write_set); /* start table scan */ init_read_record(&read_record, thd, head, (SQL_SELECT*) 0, 1, 1); - /* index_merge currently doesn't support "using index" at all */ - head->file->extra(HA_EXTRA_NO_KEYREAD); - DBUG_RETURN(result); } @@ -7398,9 +7448,7 @@ int QUICK_INDEX_MERGE_SELECT::get_next() if (doing_pk_scan) DBUG_RETURN(pk_quick_select->get_next()); - result= read_record.read_record(&read_record); - - if (result == -1) + if ((result= read_record.read_record(&read_record)) == -1) { result= HA_ERR_END_OF_FILE; end_read_record(&read_record); @@ -7408,7 +7456,8 @@ int QUICK_INDEX_MERGE_SELECT::get_next() if (pk_quick_select) { doing_pk_scan= TRUE; - if ((result= pk_quick_select->init()) || (result= pk_quick_select->reset())) + if ((result= pk_quick_select->init()) || + (result= pk_quick_select->reset())) DBUG_RETURN(result); DBUG_RETURN(pk_quick_select->get_next()); } @@ -7450,16 +7499,12 @@ int QUICK_ROR_INTERSECT_SELECT::get_next() { /* Get a rowid for first quick and save it as a 'candidate' */ quick= quick_it++; + error= quick->get_next(); if (cpk_quick) { - do - { + while (!error && !cpk_quick->row_in_ranges()) error= quick->get_next(); - }while (!error && !cpk_quick->row_in_ranges()); } - else - error= quick->get_next(); - if (error) DBUG_RETURN(error); @@ -7505,7 +7550,7 @@ int QUICK_ROR_INTERSECT_SELECT::get_next() } } - /* We get here iff we got the same row ref in all scans. */ + /* We get here if we got the same row ref in all scans. */ if (need_to_fetch_row) error= head->file->rnd_pos(head->record[0], last_rowid); } while (error == HA_ERR_RECORD_DELETED); @@ -7578,6 +7623,7 @@ int QUICK_ROR_UNION_SELECT::get_next() DBUG_RETURN(error); } + int QUICK_RANGE_SELECT::reset() { uint mrange_bufsiz; @@ -7617,7 +7663,7 @@ int QUICK_RANGE_SELECT::reset() } /* Allocate the handler buffer if necessary. */ - if (file->table_flags() & HA_NEED_READ_RANGE_BUFFER) + if (file->ha_table_flags() & HA_NEED_READ_RANGE_BUFFER) { mrange_bufsiz= min(multi_range_bufsiz, (QUICK_SELECT_I::records + 1)* head->s->reclength); @@ -7682,6 +7728,15 @@ int QUICK_RANGE_SELECT::get_next() (cur_range >= (QUICK_RANGE**) ranges.buffer) && (cur_range <= (QUICK_RANGE**) ranges.buffer + ranges.elements)); + if (in_ror_merged_scan) + { + /* + We don't need to signal the bitmap change as the bitmap is always the + same for this head->file + */ + head->column_bitmaps_set_no_signal(&column_bitmap, &column_bitmap); + } + for (;;) { if (in_range) @@ -7689,10 +7744,7 @@ int QUICK_RANGE_SELECT::get_next() /* We did already start to read this key. */ result= file->read_multi_range_next(&mrange); if (result != HA_ERR_END_OF_FILE) - { - in_range= ! result; - DBUG_RETURN(result); - } + goto end; } uint count= min(multi_range_length, ranges.elements - @@ -7701,6 +7753,8 @@ int QUICK_RANGE_SELECT::get_next() { /* Ranges have already been used up before. None is left for read. */ in_range= FALSE; + if (in_ror_merged_scan) + head->column_bitmaps_set_no_signal(save_read_set, save_write_set); DBUG_RETURN(HA_ERR_END_OF_FILE); } KEY_MULTI_RANGE *mrange_slot, *mrange_end; @@ -7732,12 +7786,18 @@ int QUICK_RANGE_SELECT::get_next() result= file->read_multi_range_first(&mrange, multi_range, count, sorted, multi_range_buff); if (result != HA_ERR_END_OF_FILE) - { - in_range= ! result; - DBUG_RETURN(result); - } + goto end; in_range= FALSE; /* No matching rows; go to next set of ranges. */ } + +end: + in_range= ! result; + if (in_ror_merged_scan) + { + /* Restore bitmaps set on entry */ + head->column_bitmaps_set_no_signal(save_read_set, save_write_set); + } + DBUG_RETURN(result); } @@ -7914,7 +7974,7 @@ bool QUICK_RANGE_SELECT::row_in_ranges() QUICK_SELECT_DESC::QUICK_SELECT_DESC(QUICK_RANGE_SELECT *q, uint used_key_parts) - : QUICK_RANGE_SELECT(*q), rev_it(rev_ranges) + :QUICK_RANGE_SELECT(*q), rev_it(rev_ranges) { QUICK_RANGE *r; @@ -8390,9 +8450,10 @@ cost_group_min_max(TABLE* table, KEY *index_info, uint used_key_parts, groups, and thus can be applied after the grouping. GA4. There are no expressions among G_i, just direct column references. NGA1.If in the index I there is a gap between the last GROUP attribute G_k, - and the MIN/MAX attribute C, then NGA must consist of exactly the index - attributes that constitute the gap. As a result there is a permutation - of NGA that coincides with the gap in the index <B_1, ..., B_m>. + and the MIN/MAX attribute C, then NGA must consist of exactly the + index attributes that constitute the gap. As a result there is a + permutation of NGA that coincides with the gap in the index + <B_1, ..., B_m>. NGA2.If BA <> {}, then the WHERE clause must contain a conjunction EQ of equality conditions for all NG_i of the form (NG_i = const) or (const = NG_i), such that each NG_i is referenced in exactly one @@ -8400,9 +8461,10 @@ cost_group_min_max(TABLE* table, KEY *index_info, uint used_key_parts, gap in the index. WA1. There are no other attributes in the WHERE clause except the ones referenced in predicates RNG, PA, PC, EQ defined above. Therefore - WA is subset of (GA union NGA union C) for GA,NGA,C that pass the above - tests. By transitivity then it also follows that each WA_i participates - in the index I (if this was already tested for GA, NGA and C). + WA is subset of (GA union NGA union C) for GA,NGA,C that pass the + above tests. By transitivity then it also follows that each WA_i + participates in the index I (if this was already tested for GA, NGA + and C). C) Overall query form: SELECT EXPR([A_1,...,A_k], [B_1,...,B_m], [MIN(C)], [MAX(C)]) @@ -8464,12 +8526,12 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree) TABLE *table= param->table; bool have_min= FALSE; /* TRUE if there is a MIN function. */ bool have_max= FALSE; /* TRUE if there is a MAX function. */ - Item_field *min_max_arg_item= NULL;/* The argument of all MIN/MAX functions.*/ + Item_field *min_max_arg_item= NULL; // The argument of all MIN/MAX functions KEY_PART_INFO *min_max_arg_part= NULL; /* The corresponding keypart. */ uint group_prefix_len= 0; /* Length (in bytes) of the key prefix. */ KEY *index_info= NULL; /* The index chosen for data access. */ uint index= 0; /* The id of the chosen index. */ - uint group_key_parts= 0; /* Number of index key parts in the group prefix. */ + uint group_key_parts= 0; // Number of index key parts in the group prefix. uint used_key_parts= 0; /* Number of index key parts used for access. */ byte key_infix[MAX_KEY_LENGTH]; /* Constants from equality predicates.*/ uint key_infix_len= 0; /* Length of key_infix. */ @@ -8587,28 +8649,19 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree) we check that all query fields are indeed covered by 'cur_index'. */ if (pk < MAX_KEY && cur_index != pk && - (table->file->table_flags() & HA_PRIMARY_KEY_IN_READ_INDEX)) + (table->file->ha_table_flags() & HA_PRIMARY_KEY_IN_READ_INDEX)) { /* For each table field */ for (uint i= 0; i < table->s->fields; i++) { Field *cur_field= table->field[i]; /* - If the field is used in the current query, check that the - field is covered by some keypart of the current index. + If the field is used in the current query ensure that it's + part of 'cur_index' */ - if (thd->query_id == cur_field->query_id) - { - KEY_PART_INFO *key_part= cur_index_info->key_part; - KEY_PART_INFO *key_part_end= key_part + cur_index_info->key_parts; - for (;;) - { - if (key_part->field == cur_field) - break; - if (++key_part == key_part_end) - goto next_index; // Field was not part of key - } - } + if (bitmap_is_set(table->read_set, cur_field->field_index) && + !cur_field->part_of_key_not_clustered.is_set(cur_index)) + goto next_index; // Field was not part of key } } @@ -8762,7 +8815,7 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree) key_part_range[1]= last_part; /* Check if cur_part is referenced in the WHERE clause. */ - if (join->conds->walk(&Item::find_item_in_field_list_processor, + if (join->conds->walk(&Item::find_item_in_field_list_processor, 0, (byte*) key_part_range)) goto next_index; } @@ -8776,7 +8829,7 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree) { for (cur_part= first_non_infix_part; cur_part != last_part; cur_part++) { - if (cur_part->field->query_id == thd->query_id) + if (bitmap_is_set(table->read_set, cur_part->field->field_index)) goto next_index; } } @@ -9240,8 +9293,8 @@ void cost_group_min_max(TABLE* table, KEY *index_info, uint used_key_parts, double cpu_cost= 0; /* TODO: CPU cost of index_read calls? */ DBUG_ENTER("cost_group_min_max"); - table_records= table->file->records; - keys_per_block= (table->file->block_size / 2 / + table_records= table->file->stats.records; + keys_per_block= (table->file->stats.block_size / 2 / (index_info->key_length + table->file->ref_length) + 1); num_blocks= (table_records / keys_per_block) + 1; @@ -10414,6 +10467,10 @@ print_key(KEY_PART *key_part,const char *key,uint used_length) const char *key_end= key+used_length; String tmp(buff,sizeof(buff),&my_charset_bin); uint store_length; + TABLE *table= key_part->field->table; + my_bitmap_map *old_write_set, *old_read_set; + old_write_set= dbug_tmp_use_all_columns(table, table->write_set); + old_read_set= dbug_tmp_use_all_columns(table, table->read_set); for (; key < key_end; key+=store_length, key_part++) { @@ -10439,18 +10496,28 @@ print_key(KEY_PART *key_part,const char *key,uint used_length) if (key+store_length < key_end) fputc('/',DBUG_FILE); } + dbug_tmp_restore_column_map(table->write_set, old_write_set); + dbug_tmp_restore_column_map(table->read_set, old_read_set); } static void print_quick(QUICK_SELECT_I *quick, const key_map *needed_reg) { char buf[MAX_KEY/8+1]; + TABLE *table; + my_bitmap_map *old_read_map, *old_write_map; DBUG_ENTER("print_quick"); if (!quick) DBUG_VOID_RETURN; DBUG_LOCK_FILE; + table= quick->head; + old_read_map= dbug_tmp_use_all_columns(table, table->read_set); + old_write_map= dbug_tmp_use_all_columns(table, table->write_set); quick->dbug_dump(0, TRUE); + dbug_tmp_restore_column_map(table->read_set, old_read_map); + dbug_tmp_restore_column_map(table->write_set, old_write_map); + fprintf(DBUG_FILE,"other_keys: 0x%s:\n", needed_reg->print(buf)); DBUG_UNLOCK_FILE; diff --git a/sql/opt_range.h b/sql/opt_range.h index bc2496b0769..85cedf663cd 100644 --- a/sql/opt_range.h +++ b/sql/opt_range.h @@ -192,8 +192,9 @@ public: function is called. SYNOPSIS init_ror_merged_scan() - reuse_handler If true, the quick select may use table->handler, otherwise - it must create and use a separate handler object. + reuse_handler If true, the quick select may use table->handler, + otherwise it must create and use a separate handler + object. RETURN 0 Ok other Error @@ -259,7 +260,7 @@ class SEL_ARG; class QUICK_RANGE_SELECT : public QUICK_SELECT_I { protected: - bool next,dont_free; + bool next,dont_free,in_ror_merged_scan; public: int error; protected: @@ -277,8 +278,8 @@ protected: freed by QUICK_RANGE_SELECT) */ HANDLER_BUFFER *multi_range_buff; /* the handler buffer (allocated and freed by QUICK_RANGE_SELECT) */ + MY_BITMAP column_bitmap, *save_read_set, *save_write_set; -protected: friend class TRP_ROR_INTERSECT; friend QUICK_RANGE_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table, diff --git a/sql/opt_sum.cc b/sql/opt_sum.cc index d4e7745551a..ce3f5c5f108 100644 --- a/sql/opt_sum.cc +++ b/sql/opt_sum.cc @@ -55,6 +55,36 @@ static int maxmin_in_range(bool max_fl, Field* field, COND *cond); /* + Get exact count of rows in all tables + + SYNOPSIS + get_exact_records() + tables List of tables + + NOTES + When this is called, we know all table handlers supports HA_HAS_RECORDS + or HA_STATS_RECORDS_IS_EXACT + + RETURN + ULONGLONG_MAX Error: Could not calculate number of rows + # Multiplication of number of rows in all tables +*/ + +static ulonglong get_exact_record_count(TABLE_LIST *tables) +{ + ulonglong count= 1; + for (TABLE_LIST *tl= tables; tl; tl= tl->next_leaf) + { + ha_rows tmp= tl->table->file->records(); + if ((tmp == HA_POS_ERROR)) + return ULONGLONG_MAX; + count*= tmp; + } + return count; +} + + +/* Substitutes constants for some COUNT(), MIN() and MAX() functions. SYNOPSIS @@ -80,8 +110,8 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds) List_iterator_fast<Item> it(all_fields); int const_result= 1; bool recalc_const_item= 0; - longlong count= 1; - bool is_exact_count= TRUE; + ulonglong count= 1; + bool is_exact_count= TRUE, maybe_exact_count= TRUE; table_map removed_tables= 0, outer_tables= 0, used_tables= 0; table_map where_tables= 0; Item *item; @@ -120,22 +150,25 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds) used_tables|= tl->table->map; /* - If the storage manager of 'tl' gives exact row count, compute the total - number of rows. If there are no outer table dependencies, this count - may be used as the real count. + If the storage manager of 'tl' gives exact row count as part of + statistics (cheap), compute the total number of rows. If there are + no outer table dependencies, this count may be used as the real count. Schema tables are filled after this function is invoked, so we can't get row count */ - if ((tl->table->file->table_flags() & HA_NOT_EXACT_COUNT) || + if (!(tl->table->file->ha_table_flags() & HA_STATS_RECORDS_IS_EXACT) || tl->schema_table) { + maybe_exact_count&= test(!tl->schema_table && + (tl->table->file->ha_table_flags() & + HA_HAS_RECORDS)); is_exact_count= FALSE; count= 1; // ensure count != 0 } else { tl->table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK); - count*= tl->table->file->records; + count*= tl->table->file->stats.records; } } @@ -157,9 +190,19 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds) there are no outer joins. */ if (!conds && !((Item_sum_count*) item)->args[0]->maybe_null && - !outer_tables && is_exact_count) + !outer_tables && maybe_exact_count) { - ((Item_sum_count*) item)->make_const(count); + if (!is_exact_count) + { + if ((count= get_exact_record_count(tables)) == ULONGLONG_MAX) + { + /* Error from handler in counting rows. Don't optimize count() */ + const_result= 0; + continue; + } + is_exact_count= 1; // count is now exact + } + ((Item_sum_count*) item)->make_const((longlong) count); recalc_const_item= 1; } else diff --git a/sql/partition_element.h b/sql/partition_element.h index 13693934c0f..90d0bda87c1 100644 --- a/sql/partition_element.h +++ b/sql/partition_element.h @@ -64,8 +64,20 @@ public: engine_type(NULL),part_state(PART_NORMAL), nodegroup_id(UNDEF_NODEGROUP), has_null_value(FALSE) { - subpartitions.empty(); - list_val_list.empty(); + } + partition_element(partition_element *part_elem) + : part_max_rows(part_elem->part_max_rows), + part_min_rows(part_elem->part_min_rows), + partition_name(NULL), + tablespace_name(part_elem->tablespace_name), + range_value(0), part_comment(part_elem->part_comment), + data_file_name(part_elem->data_file_name), + index_file_name(part_elem->index_file_name), + engine_type(part_elem->engine_type), + part_state(part_elem->part_state), + nodegroup_id(part_elem->nodegroup_id), + has_null_value(FALSE) + { } ~partition_element() {} }; diff --git a/sql/partition_info.cc b/sql/partition_info.cc index 0af200dc4d2..59cedc884b6 100644 --- a/sql/partition_info.cc +++ b/sql/partition_info.cc @@ -267,7 +267,7 @@ bool partition_info::set_up_default_subpartitions(handler *file, j= 0; do { - partition_element *subpart_elem= new partition_element(); + partition_element *subpart_elem= new partition_element(part_elem); if (likely(subpart_elem != 0 && (!part_elem->subpartitions.push_back(subpart_elem)))) { @@ -637,8 +637,8 @@ bool partition_info::check_list_constants() &list_part_cmp); not_first= FALSE; - i= prev_value= 0; //prev_value initialised to quiet compiler - do + prev_value= 0; // prev_value initialised to quiet compiler + for (i= 0; i < no_list_values ; i++) { curr_value= list_array[i].list_value; if (likely(!not_first || prev_value != curr_value)) @@ -651,7 +651,7 @@ bool partition_info::check_list_constants() my_error(ER_MULTIPLE_DEF_CONST_IN_LIST_PART_ERROR, MYF(0)); goto end; } - } while (++i < no_list_values); + } result= FALSE; end: DBUG_RETURN(result); @@ -687,8 +687,20 @@ bool partition_info::check_partition_info(handlerton **eng_type, uint i, tot_partitions; bool result= TRUE; char *same_name; + bool part_expression_ok= TRUE; DBUG_ENTER("partition_info::check_partition_info"); + if (part_type != HASH_PARTITION || !list_of_part_fields) + part_expr->walk(&Item::check_partition_func_processor, 0, + (byte*)(&part_expression_ok)); + if (is_sub_partitioned() && !list_of_subpart_fields) + subpart_expr->walk(&Item::check_partition_func_processor, 0, + (byte*)(&part_expression_ok)); + if (!part_expression_ok) + { + my_error(ER_PARTITION_FUNCTION_IS_NOT_ALLOWED, MYF(0)); + goto end; + } if (unlikely(!is_sub_partitioned() && !(use_default_subpartitions && use_default_no_subpartitions))) { @@ -726,6 +738,8 @@ bool partition_info::check_partition_info(handlerton **eng_type, do { partition_element *part_elem= part_it++; + if (part_elem->engine_type == NULL) + part_elem->engine_type= default_engine_type; if (!is_sub_partitioned()) { if (check_table_name(part_elem->partition_name, @@ -734,8 +748,6 @@ bool partition_info::check_partition_info(handlerton **eng_type, my_error(ER_WRONG_PARTITION_NAME, MYF(0)); goto end; } - if (part_elem->engine_type == NULL) - part_elem->engine_type= default_engine_type; DBUG_PRINT("info", ("engine = %d", ha_legacy_type(part_elem->engine_type))); engine_array[part_count++]= part_elem->engine_type; @@ -746,18 +758,18 @@ bool partition_info::check_partition_info(handlerton **eng_type, List_iterator<partition_element> sub_it(part_elem->subpartitions); do { - part_elem= sub_it++; - if (check_table_name(part_elem->partition_name, - strlen(part_elem->partition_name))) + partition_element *sub_elem= sub_it++; + if (check_table_name(sub_elem->partition_name, + strlen(sub_elem->partition_name))) { my_error(ER_WRONG_PARTITION_NAME, MYF(0)); goto end; } - if (part_elem->engine_type == NULL) - part_elem->engine_type= default_engine_type; + if (sub_elem->engine_type == NULL) + sub_elem->engine_type= default_engine_type; DBUG_PRINT("info", ("engine = %u", - ha_legacy_type(part_elem->engine_type))); - engine_array[part_count++]= part_elem->engine_type; + ha_legacy_type(sub_elem->engine_type))); + engine_array[part_count++]= sub_elem->engine_type; } while (++j < no_subparts); } } while (++i < no_parts); diff --git a/sql/protocol.cc b/sql/protocol.cc index 650bd8fc58f..bb0891cdbbe 100644 --- a/sql/protocol.cc +++ b/sql/protocol.cc @@ -924,8 +924,19 @@ bool Protocol_simple::store(Field *field) char buff[MAX_FIELD_WIDTH]; String str(buff,sizeof(buff), &my_charset_bin); CHARSET_INFO *tocs= this->thd->variables.character_set_results; + TABLE *table= field->table; +#ifdef DBUG_OFF + my_bitmap_map *old_map= 0; + if (table->file) + old_map= dbug_tmp_use_all_columns(table, table->read_set); +#endif field->val_str(&str); +#ifdef DBUG_OFF + if (old_map) + dbug_tmp_restore_column_map(table->read_set, old_map); +#endif + return store_string_aux(str.ptr(), str.length(), str.charset(), tocs); } diff --git a/sql/records.cc b/sql/records.cc index 5cb9b1e5c47..b2505600b22 100644 --- a/sql/records.cc +++ b/sql/records.cc @@ -64,10 +64,7 @@ void init_read_record_idx(READ_RECORD *info, THD *thd, TABLE *table, table->status=0; /* And it's always found */ if (!table->file->inited) - { table->file->ha_index_init(idx, 1); - table->file->extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY); - } /* read_record will be changed to rr_index in rr_index_first */ info->read_record= rr_index_first; } @@ -195,11 +192,11 @@ void init_read_record(READ_RECORD *info,THD *thd, TABLE *table, if (!table->sort.addon_field && ! (specialflag & SPECIAL_SAFE_MODE) && thd->variables.read_rnd_buff_size && - !(table->file->table_flags() & HA_FAST_KEY_READ) && + !(table->file->ha_table_flags() & HA_FAST_KEY_READ) && (table->db_stat & HA_READ_ONLY || table->reginfo.lock_type <= TL_READ_NO_INSERT) && - (ulonglong) table->s->reclength* (table->file->records+ - table->file->deleted) > + (ulonglong) table->s->reclength* (table->file->stats.records+ + table->file->stats.deleted) > (ulonglong) MIN_FILE_LENGTH_TO_USE_ROW_CACHE && info->io_cache->end_of_file/info->ref_length * table->s->reclength > (my_off_t) MIN_ROWS_TO_USE_TABLE_CACHE && @@ -239,7 +236,7 @@ void init_read_record(READ_RECORD *info,THD *thd, TABLE *table, (int) table->reginfo.lock_type <= (int) TL_READ_HIGH_PRIORITY || !(table->s->db_options_in_use & HA_OPTION_PACK_RECORD) || (use_record_cache < 0 && - !(table->file->table_flags() & HA_NOT_DELETE_WITH_CACHE)))) + !(table->file->ha_table_flags() & HA_NOT_DELETE_WITH_CACHE)))) VOID(table->file->extra_opt(HA_EXTRA_CACHE, thd->variables.read_buff_size)); } diff --git a/sql/set_var.cc b/sql/set_var.cc index 242fe0f6068..53b4b395c37 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -2728,7 +2728,7 @@ bool sys_var_max_user_conn::check(THD *thd, set_var *var) { /* Per-session values of max_user_connections can't be set directly. - QQ: May be we should have a separate error message for this? + May be we should have a separate error message for this? */ my_error(ER_GLOBAL_VARIABLE, MYF(0), name); return TRUE; @@ -2795,7 +2795,8 @@ static bool set_option_autocommit(THD *thd, set_var *var) if ((org_options & OPTION_NOT_AUTOCOMMIT)) { /* We changed to auto_commit mode */ - thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE); + thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE | + OPTION_KEEP_LOG); thd->server_status|= SERVER_STATUS_AUTOCOMMIT; if (ha_commit(thd)) return 1; @@ -3307,7 +3308,7 @@ byte *sys_var_thd_storage_engine::value_ptr(THD *thd, enum_var_type type, handlerton *val; val= (type == OPT_GLOBAL) ? global_system_variables.*offset : thd->variables.*offset; - return (byte *) val->name; + return (byte *) hton2plugin[val->slot]->name.str; } diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index 9a0319a88b5..1de2e893555 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -2865,30 +2865,8 @@ ER_WRONG_OUTER_JOIN 42000 swe "Felaktigt referens i OUTER JOIN. Kontrollera ON-uttrycket" ukr "ðÅÒÅÈÒÅÓÎÁ ÚÁÌÅÖΦÓÔØ Õ OUTER JOIN. ðÅÒÅצÒÔÅ ÕÍÏ×Õ ON" ER_NULL_COLUMN_IN_INDEX 42000 - cze "Sloupec '%-.32s' je pou-B¾it s UNIQUE nebo INDEX, ale není definován jako NOT NULL" - dan "Kolonne '%-.32s' bruges som UNIQUE eller INDEX men er ikke defineret som NOT NULL" - nla "Kolom '%-.64s' wordt gebruikt met UNIQUE of INDEX maar is niet gedefinieerd als NOT NULL" - eng "Column '%-.64s' is used with UNIQUE or INDEX but is not defined as NOT NULL" - jps "Column '%-.64s' ‚ª UNIQUE ‚© INDEX ‚ÅŽg—p‚³‚ê‚Ü‚µ‚½. ‚±‚̃Jƒ‰ƒ€‚Í NOT NULL ‚Æ’è‹`‚³‚ê‚Ä‚¢‚Ü‚¹‚ñ.", - est "Tulp '%-.64s' on kasutusel indeksina, kuid ei ole määratletud kui NOT NULL" - fre "La colonne '%-.32s' fait partie d'un index UNIQUE ou INDEX mais n'est pas définie comme NOT NULL" - ger "Spalte '%-.64s' wurde mit UNIQUE oder INDEX benutzt, ist aber nicht als NOT NULL definiert" - greek "Ôï ðåäßï '%-.64s' ÷ñçóéìïðïéåßôáé óáí UNIQUE Þ INDEX áëëÜ äåí Ý÷åé ïñéóèåß óáí NOT NULL" - hun "A(z) '%-.64s' oszlop INDEX vagy UNIQUE (egyedi), de a definicioja szerint nem NOT NULL" - ita "La colonna '%-.64s' e` usata con UNIQUE o INDEX ma non e` definita come NOT NULL" - jpn "Column '%-.64s' ¤¬ UNIQUE ¤« INDEX ¤Ç»ÈÍѤµ¤ì¤Þ¤·¤¿. ¤³¤Î¥«¥é¥à¤Ï NOT NULL ¤ÈÄêµÁ¤µ¤ì¤Æ¤¤¤Þ¤»¤ó." - kor "'%-.64s' Ä®·³ÀÌ UNIQUE³ª INDEX¸¦ »ç¿ëÇÏ¿´Áö¸¸ NOT NULLÀÌ Á¤ÀǵÇÁö ¾Ê¾Ò±º¿ä..." - nor "Column '%-.32s' is used with UNIQUE or INDEX but is not defined as NOT NULL" - norwegian-ny "Column '%-.32s' is used with UNIQUE or INDEX but is not defined as NOT NULL" - pol "Column '%-.32s' is used with UNIQUE or INDEX but is not defined as NOT NULL" - por "Coluna '%-.64s' é usada com única (UNIQUE) ou índice (INDEX), mas não está definida como não-nula (NOT NULL)" - rum "Coloana '%-.64s' e folosita cu UNIQUE sau INDEX dar fara sa fie definita ca NOT NULL" - rus "óÔÏÌÂÅà '%-.64s' ÉÓÐÏÌØÚÕÅÔÓÑ × UNIQUE ÉÌÉ × INDEX, ÎÏ ÎÅ ÏÐÒÅÄÅÌÅÎ ËÁË NOT NULL" - serbian "Kolona '%-.64s' je upotrebljena kao 'UNIQUE' ili 'INDEX' ali nije definisana kao 'NOT NULL'" - slo "Pole '%-.64s' je pou¾ité s UNIQUE alebo INDEX, ale nie je zadefinované ako NOT NULL" - spa "Columna '%-.32s' es usada con UNIQUE o INDEX pero no está definida como NOT NULL" - swe "Kolumn '%-.32s' är använd med UNIQUE eller INDEX men är inte definerad med NOT NULL" - ukr "óÔÏ×ÂÅÃØ '%-.64s' ×ÉËÏÒÉÓÔÏ×Õ¤ÔØÓÑ Ú UNIQUE ÁÂÏ INDEX, ÁÌÅ ÎÅ ×ÉÚÎÁÞÅÎÉÊ ÑË NOT NULL" + eng "Table handler doesn't support NULL in given index. Please change column '%-.64s' to be NOT NULL or use another handler" + swe "Tabell hanteraren kan inte indexera NULL kolumner för den givna index typen. Ändra '%-.64s' till NOT NULL eller använd en annan hanterare" ER_CANT_FIND_UDF cze "Nemohu na-Bèíst funkci '%-.64s'" dan "Kan ikke læse funktionen '%-.64s'" @@ -3957,7 +3935,7 @@ ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE spa "Tu estás usando modo de actualización segura y tentado actualizar una tabla sin un WHERE que usa una KEY columna" swe "Du använder 'säker uppdateringsmod' och försökte uppdatera en tabell utan en WHERE-sats som använder sig av en nyckel" ukr "÷É Õ ÒÅÖÉͦ ÂÅÚÐÅÞÎÏÇÏ ÏÎÏ×ÌÅÎÎÑ ÔÁ ÎÁÍÁÇÁ¤ÔÅÓØ ÏÎÏ×ÉÔÉ ÔÁÂÌÉÃÀ ÂÅÚ ÏÐÅÒÁÔÏÒÁ WHERE, ÝÏ ×ÉËÏÒÉÓÔÏ×Õ¤ KEY ÓÔÏ×ÂÅÃØ" -ER_KEY_DOES_NOT_EXITS +ER_KEY_DOES_NOT_EXITS 42000 S1009 cze "Kl-Bíè '%-.64s' v tabulce '%-.64s' neexistuje" dan "Nøglen '%-.64s' eksisterer ikke i tabellen '%-.64s'" nla "Zoeksleutel '%-.64s' bestaat niet in tabel '%-.64s'" @@ -5832,6 +5810,9 @@ ER_NDB_CANT_SWITCH_BINLOG_FORMAT eng "The NDB cluster engine does not support changing the binlog format on the fly yet" ER_PARTITION_NO_TEMPORARY eng "Cannot create temporary table with partitions" +ER_PARTITION_FUNCTION_IS_NOT_ALLOWED + eng "This partition function is not allowed" + swe "Denna partitioneringsfunktion är inte tillåten" ER_DDL_LOG_ERROR eng "Error in DDL log" ER_NULL_IN_VALUES_LESS_THAN diff --git a/sql/slave.cc b/sql/slave.cc index 4ab9e951813..5ef91b1f0d4 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -95,6 +95,8 @@ void init_thread_mask(int* mask,MASTER_INFO* mi,bool inverse) { bool set_io = mi->slave_running, set_sql = mi->rli.slave_running; register int tmp_mask=0; + DBUG_ENTER("init_thread_mask"); + if (set_io) tmp_mask |= SLAVE_IO; if (set_sql) @@ -102,6 +104,7 @@ void init_thread_mask(int* mask,MASTER_INFO* mi,bool inverse) if (inverse) tmp_mask^= (SLAVE_IO | SLAVE_SQL); *mask = tmp_mask; + DBUG_VOID_RETURN; } @@ -111,9 +114,12 @@ void init_thread_mask(int* mask,MASTER_INFO* mi,bool inverse) void lock_slave_threads(MASTER_INFO* mi) { + DBUG_ENTER("lock_slave_threads"); + //TODO: see if we can do this without dual mutex pthread_mutex_lock(&mi->run_lock); pthread_mutex_lock(&mi->rli.run_lock); + DBUG_VOID_RETURN; } @@ -123,9 +129,12 @@ void lock_slave_threads(MASTER_INFO* mi) void unlock_slave_threads(MASTER_INFO* mi) { + DBUG_ENTER("unlock_slave_threads"); + //TODO: see if we can do this without dual mutex pthread_mutex_unlock(&mi->rli.run_lock); pthread_mutex_unlock(&mi->run_lock); + DBUG_VOID_RETURN; } @@ -423,6 +432,8 @@ err: void init_slave_skip_errors(const char* arg) { const char *p; + DBUG_ENTER("init_slave_skip_errors"); + if (bitmap_init(&slave_error_mask,0,MAX_SLAVE_ERROR,0)) { fprintf(stderr, "Badly out of memory, please check your system status\n"); @@ -434,7 +445,7 @@ void init_slave_skip_errors(const char* arg) if (!my_strnncoll(system_charset_info,(uchar*)arg,4,(const uchar*)"all",4)) { bitmap_set_all(&slave_error_mask); - return; + DBUG_VOID_RETURN; } for (p= arg ; *p; ) { @@ -446,12 +457,15 @@ void init_slave_skip_errors(const char* arg) while (!my_isdigit(system_charset_info,*p) && *p) p++; } + DBUG_VOID_RETURN; } void st_relay_log_info::inc_group_relay_log_pos(ulonglong log_pos, bool skip_lock) { + DBUG_ENTER("st_relay_log_info::inc_group_relay_log_pos"); + if (!skip_lock) pthread_mutex_lock(&data_lock); inc_event_relay_log_pos(); @@ -500,12 +514,14 @@ void st_relay_log_info::inc_group_relay_log_pos(ulonglong log_pos, pthread_cond_broadcast(&data_cond); if (!skip_lock) pthread_mutex_unlock(&data_lock); + DBUG_VOID_RETURN; } void st_relay_log_info::close_temporary_tables() { TABLE *table,*next; + DBUG_ENTER("st_relay_log_info::close_temporary_tables"); for (table=save_temporary_tables ; table ; table=next) { @@ -514,10 +530,12 @@ void st_relay_log_info::close_temporary_tables() Don't ask for disk deletion. For now, anyway they will be deleted when slave restarts, but it is a better intention to not delete them. */ + DBUG_PRINT("info", ("table: %p", table)); close_temporary(table, 1, 0); } save_temporary_tables= 0; slave_open_temp_tables= 0; + DBUG_VOID_RETURN; } /* @@ -613,12 +631,13 @@ err: int terminate_slave_threads(MASTER_INFO* mi,int thread_mask,bool skip_lock) { + DBUG_ENTER("terminate_slave_threads"); + if (!mi->inited) - return 0; /* successfully do nothing */ + DBUG_RETURN(0); /* successfully do nothing */ int error,force_all = (thread_mask & SLAVE_FORCE_ALL); pthread_mutex_t *sql_lock = &mi->rli.run_lock, *io_lock = &mi->run_lock; pthread_mutex_t *sql_cond_lock,*io_cond_lock; - DBUG_ENTER("terminate_slave_threads"); sql_cond_lock=sql_lock; io_cond_lock=io_lock; @@ -704,9 +723,10 @@ int start_slave_thread(pthread_handler h_func, pthread_mutex_t *start_lock, { pthread_t th; ulong start_id; - DBUG_ASSERT(mi->inited); DBUG_ENTER("start_slave_thread"); + DBUG_ASSERT(mi->inited); + if (start_lock) pthread_mutex_lock(start_lock); if (!server_id) @@ -810,8 +830,10 @@ int start_slave_threads(bool need_slave_mutex, bool wait_for_start, #ifdef NOT_USED_YET static int end_slave_on_walk(MASTER_INFO* mi, gptr /*unused*/) { + DBUG_ENTER("end_slave_on_walk"); + end_master_info(mi); - return 0; + DBUG_RETURN(0); } #endif @@ -825,6 +847,8 @@ static int end_slave_on_walk(MASTER_INFO* mi, gptr /*unused*/) void end_slave() { + DBUG_ENTER("end_slave"); + /* This is called when the server terminates, in close_connections(). It terminates slave threads. However, some CHANGE MASTER etc may still be @@ -846,19 +870,24 @@ void end_slave() active_mi= 0; } pthread_mutex_unlock(&LOCK_active_mi); + DBUG_VOID_RETURN; } static bool io_slave_killed(THD* thd, MASTER_INFO* mi) { + DBUG_ENTER("io_slave_killed"); + DBUG_ASSERT(mi->io_thd == thd); DBUG_ASSERT(mi->slave_running); // tracking buffer overrun - return mi->abort_slave || abort_loop || thd->killed; + DBUG_RETURN(mi->abort_slave || abort_loop || thd->killed); } static bool sql_slave_killed(THD* thd, RELAY_LOG_INFO* rli) { + DBUG_ENTER("sql_slave_killed"); + DBUG_ASSERT(rli->sql_thd == thd); DBUG_ASSERT(rli->slave_running == 1);// tracking buffer overrun if (abort_loop || thd->killed || rli->abort_slave) @@ -873,7 +902,7 @@ static bool sql_slave_killed(THD* thd, RELAY_LOG_INFO* rli) is actively working. */ if (!rli->unsafe_to_stop_at) - return 1; + DBUG_RETURN(1); DBUG_PRINT("info", ("Slave SQL thread is in an unsafe situation, giving " "it some grace period")); if (difftime(time(0), rli->unsafe_to_stop_at) > 60) @@ -885,10 +914,10 @@ static bool sql_slave_killed(THD* thd, RELAY_LOG_INFO* rli) "There is a risk of duplicate updates when the slave " "SQL thread is restarted. Please check your tables' " "contents after restart."); - return 1; + DBUG_RETURN(1); } } - return 0; + DBUG_RETURN(0); } @@ -917,6 +946,8 @@ void slave_print_msg(enum loglevel level, RELAY_LOG_INFO* rli, char buff[MAX_SLAVE_ERRMSG], *pbuff= buff; uint pbuffsize= sizeof(buff); va_list args; + DBUG_ENTER("slave_print_msg"); + va_start(args,msg); switch (level) { @@ -943,7 +974,7 @@ void slave_print_msg(enum loglevel level, RELAY_LOG_INFO* rli, break; default: DBUG_ASSERT(0); // should not come here - return; // don't crash production builds, just do nothing + DBUG_VOID_RETURN; // don't crash production builds, just do nothing } my_vsnprintf(pbuff, pbuffsize, msg, args); /* If the msg string ends with '.', do not add a ',' it would be ugly */ @@ -951,6 +982,7 @@ void slave_print_msg(enum loglevel level, RELAY_LOG_INFO* rli, (*report_function)("Slave: %s Error_code: %d", pbuff, err_code); else (*report_function)("Slave: %s, Error_code: %d", pbuff, err_code); + DBUG_VOID_RETURN; } /* @@ -962,9 +994,12 @@ void slave_print_msg(enum loglevel level, RELAY_LOG_INFO* rli, void skip_load_data_infile(NET *net) { + DBUG_ENTER("skip_load_data_infile"); + (void)net_request_file(net, "/dev/null"); (void)my_net_read(net); // discard response (void)net_write_command(net, 0, "", 0, "", 0); // Send ok + DBUG_VOID_RETURN; } @@ -983,13 +1018,17 @@ bool net_request_file(NET* net, const char* fname) const char *print_slave_db_safe(const char* db) { - return (db ? db : ""); + DBUG_ENTER("*print_slave_db_safe"); + + DBUG_RETURN((db ? db : "")); } static int init_strvar_from_file(char *var, int max_size, IO_CACHE *f, const char *default_val) { uint length; + DBUG_ENTER("init_strvar_from_file"); + if ((length=my_b_gets(f,var, max_size))) { char* last_p = var + length -1; @@ -1004,32 +1043,34 @@ static int init_strvar_from_file(char *var, int max_size, IO_CACHE *f, int c; while (((c=my_b_get(f)) != '\n' && c != my_b_EOF)); } - return 0; + DBUG_RETURN(0); } else if (default_val) { strmake(var, default_val, max_size-1); - return 0; + DBUG_RETURN(0); } - return 1; + DBUG_RETURN(1); } static int init_intvar_from_file(int* var, IO_CACHE* f, int default_val) { char buf[32]; + DBUG_ENTER("init_intvar_from_file"); + if (my_b_gets(f, buf, sizeof(buf))) { *var = atoi(buf); - return 0; + DBUG_RETURN(0); } else if (default_val) { *var = default_val; - return 0; + DBUG_RETURN(0); } - return 1; + DBUG_RETURN(1); } /* @@ -1049,6 +1090,7 @@ static int init_intvar_from_file(int* var, IO_CACHE* f, int default_val) static int get_master_version_and_clock(MYSQL* mysql, MASTER_INFO* mi) { const char* errmsg= 0; + DBUG_ENTER("get_master_version_and_clock"); /* Free old description_event_for_queue (that is needed if we are in @@ -1104,14 +1146,14 @@ static int get_master_version_and_clock(MYSQL* mysql, MASTER_INFO* mi) if (errmsg) { sql_print_error(errmsg); - return 1; + DBUG_RETURN(1); } /* as we are here, we tried to allocate the event */ if (!mi->rli.relay_log.description_event_for_queue) { sql_print_error("Slave I/O thread failed to create a default Format_description_log_event"); - return 1; + DBUG_RETURN(1); } /* @@ -1227,10 +1269,10 @@ err: if (errmsg) { sql_print_error(errmsg); - return 1; + DBUG_RETURN(1); } - return 0; + DBUG_RETURN(0); } /* @@ -1257,7 +1299,7 @@ static int create_table_from_dump(THD* thd, MYSQL *mysql, const char* db, handler *file; ulong save_options; NET *net= &mysql->net; - DBUG_ENTER("create_table_from_dump"); + DBUG_ENTER("create_table_from_dump"); packet_len= my_net_read(net); // read create table statement if (packet_len == packet_error) @@ -1666,7 +1708,6 @@ static bool wait_for_relay_log_space(RELAY_LOG_INFO* rli) MASTER_INFO* mi = rli->mi; const char *save_proc_info; THD* thd = mi->io_thd; - DBUG_ENTER("wait_for_relay_log_space"); pthread_mutex_lock(&rli->log_space_lock); @@ -1725,6 +1766,8 @@ static void write_ignored_events_info_to_relay_log(THD *thd, MASTER_INFO *mi) { RELAY_LOG_INFO *rli= &mi->rli; pthread_mutex_t *log_lock= rli->relay_log.get_log_lock(); + DBUG_ENTER("write_ignored_events_info_to_relay_log"); + DBUG_ASSERT(thd == mi->io_thd); pthread_mutex_lock(log_lock); if (rli->ign_master_log_name_end[0]) @@ -1755,11 +1798,14 @@ static void write_ignored_events_info_to_relay_log(THD *thd, MASTER_INFO *mi) } else pthread_mutex_unlock(log_lock); + DBUG_VOID_RETURN; } void init_master_info_with_options(MASTER_INFO* mi) { + DBUG_ENTER("init_master_info_with_options"); + mi->master_log_name[0] = 0; mi->master_log_pos = BIN_LOG_HEADER_SIZE; // skip magic number @@ -1783,13 +1829,17 @@ void init_master_info_with_options(MASTER_INFO* mi) strmake(mi->ssl_cipher, master_ssl_cipher, sizeof(mi->ssl_cipher)-1); if (master_ssl_key) strmake(mi->ssl_key, master_ssl_key, sizeof(mi->ssl_key)-1); + DBUG_VOID_RETURN; } void clear_slave_error(RELAY_LOG_INFO* rli) { + DBUG_ENTER("clear_slave_error"); + /* Clear the errors displayed by SHOW SLAVE STATUS */ rli->last_slave_error[0]= 0; rli->last_slave_errno= 0; + DBUG_VOID_RETURN; } /* @@ -1800,9 +1850,12 @@ void clear_slave_error(RELAY_LOG_INFO* rli) */ void clear_until_condition(RELAY_LOG_INFO* rli) { + DBUG_ENTER("clear_until_condition"); + rli->until_condition= RELAY_LOG_INFO::UNTIL_NONE; rli->until_log_name[0]= 0; rli->until_log_pos= 0; + DBUG_VOID_RETURN; } @@ -2031,9 +2084,10 @@ int register_slave_on_master(MYSQL* mysql) { char buf[1024], *pos= buf; uint report_host_len, report_user_len=0, report_password_len=0; + DBUG_ENTER("register_slave_on_master"); if (!report_host) - return 0; + DBUG_RETURN(0); report_host_len= strlen(report_host); if (report_user) report_user_len= strlen(report_user); @@ -2042,7 +2096,7 @@ int register_slave_on_master(MYSQL* mysql) /* 30 is a good safety margin */ if (report_host_len + report_user_len + report_password_len + 30 > sizeof(buf)) - return 0; // safety + DBUG_RETURN(0); // safety int4store(pos, server_id); pos+= 4; pos= net_store_data(pos, report_host, report_host_len); @@ -2059,9 +2113,9 @@ int register_slave_on_master(MYSQL* mysql) sql_print_error("Error on COM_REGISTER_SLAVE: %d '%s'", mysql_errno(mysql), mysql_error(mysql)); - return 1; + DBUG_RETURN(1); } - return 0; + DBUG_RETURN(0); } @@ -2313,6 +2367,8 @@ st_relay_log_info::st_relay_log_info() m_reload_flags(RELOAD_NONE_F), unsafe_to_stop_at(0) { + DBUG_ENTER("st_relay_log_info::st_relay_log_info"); + group_relay_log_name[0]= event_relay_log_name[0]= group_master_log_name[0]= 0; last_slave_error[0]= until_log_name[0]= ign_master_log_name_end[0]= 0; @@ -2327,11 +2383,14 @@ st_relay_log_info::st_relay_log_info() pthread_cond_init(&stop_cond, NULL); pthread_cond_init(&log_space_cond, NULL); relay_log.init_pthread_objects(); + DBUG_VOID_RETURN; } st_relay_log_info::~st_relay_log_info() { + DBUG_ENTER("st_relay_log_info::~st_relay_log_info"); + pthread_mutex_destroy(&run_lock); pthread_mutex_destroy(&data_lock); pthread_mutex_destroy(&log_space_lock); @@ -2340,6 +2399,7 @@ st_relay_log_info::~st_relay_log_info() pthread_cond_destroy(&stop_cond); pthread_cond_destroy(&log_space_cond); relay_log.cleanup(); + DBUG_VOID_RETURN; } /* @@ -2371,14 +2431,16 @@ int st_relay_log_info::wait_for_pos(THD* thd, String* log_name, longlong log_pos, longlong timeout) { - if (!inited) - return -1; int event_count = 0; ulong init_abort_pos_wait; int error=0; struct timespec abstime; // for timeout checking const char *msg; - DBUG_ENTER("wait_for_pos"); + DBUG_ENTER("st_relay_log_info::wait_for_pos"); + + if (!inited) + DBUG_RETURN(-1); + DBUG_PRINT("enter",("log_name: '%s' log_pos: %lu timeout: %lu", log_name->c_ptr(), (ulong) log_pos, (ulong) timeout)); @@ -2546,13 +2608,18 @@ improper_arguments: %d timed_out: %d", void set_slave_thread_options(THD* thd) { + DBUG_ENTER("set_slave_thread_options"); + thd->options = ((opt_log_slave_updates) ? OPTION_BIN_LOG:0) | OPTION_AUTO_IS_NULL; thd->variables.completion_type= 0; + DBUG_VOID_RETURN; } void set_slave_thread_default_charset(THD* thd, RELAY_LOG_INFO *rli) { + DBUG_ENTER("set_slave_thread_default_charset"); + thd->variables.character_set_client= global_system_variables.character_set_client; thd->variables.collation_connection= @@ -2561,6 +2628,7 @@ void set_slave_thread_default_charset(THD* thd, RELAY_LOG_INFO *rli) global_system_variables.collation_server; thd->update_charset(); rli->cached_charset_invalidate(); + DBUG_VOID_RETURN; } /* @@ -2622,6 +2690,8 @@ static int safe_sleep(THD* thd, int sec, CHECK_KILLED_FUNC thread_killed, { int nap_time; thr_alarm_t alarmed; + DBUG_ENTER("safe_sleep"); + thr_alarm_init(&alarmed); time_t start_time= time((time_t*) 0); time_t end_time= start_time+sec; @@ -2639,10 +2709,10 @@ static int safe_sleep(THD* thd, int sec, CHECK_KILLED_FUNC thread_killed, thr_end_alarm(&alarmed); if ((*thread_killed)(thd,thread_killed_arg)) - return 1; + DBUG_RETURN(1); start_time=time((time_t*) 0); } - return 0; + DBUG_RETURN(0); } @@ -2684,13 +2754,15 @@ static int request_dump(MYSQL* mysql, MASTER_INFO* mi, static int request_table_dump(MYSQL* mysql, const char* db, const char* table) { char buf[1024]; + DBUG_ENTER("request_table_dump"); + char * p = buf; uint table_len = (uint) strlen(table); uint db_len = (uint) strlen(db); if (table_len + db_len > sizeof(buf) - 2) { sql_print_error("request_table_dump: Buffer overrun"); - return 1; + DBUG_RETURN(1); } *p++ = db_len; @@ -2703,10 +2775,10 @@ static int request_table_dump(MYSQL* mysql, const char* db, const char* table) { sql_print_error("request_table_dump: Error sending the table dump \ command"); - return 1; + DBUG_RETURN(1); } - return 0; + DBUG_RETURN(0); } @@ -2730,6 +2802,7 @@ command"); static ulong read_event(MYSQL* mysql, MASTER_INFO *mi, bool* suppress_warnings) { ulong len; + DBUG_ENTER("read_event"); *suppress_warnings= 0; /* @@ -2738,7 +2811,7 @@ static ulong read_event(MYSQL* mysql, MASTER_INFO *mi, bool* suppress_warnings) */ #ifndef DBUG_OFF if (disconnect_slave_event_count && !(mi->events_till_disconnect--)) - return packet_error; + DBUG_RETURN(packet_error); #endif len = net_safe_read(mysql); @@ -2756,7 +2829,7 @@ static ulong read_event(MYSQL* mysql, MASTER_INFO *mi, bool* suppress_warnings) else sql_print_error("Error reading packet from server: %s ( server_errno=%d)", mysql_error(mysql), mysql_errno(mysql)); - return packet_error; + DBUG_RETURN(packet_error); } /* Check if eof packet */ @@ -2765,25 +2838,27 @@ static ulong read_event(MYSQL* mysql, MASTER_INFO *mi, bool* suppress_warnings) sql_print_information("Slave: received end packet from server, apparent " "master shutdown: %s", mysql_error(mysql)); - return packet_error; + DBUG_RETURN(packet_error); } DBUG_PRINT("info",( "len=%u, net->read_pos[4] = %d\n", len, mysql->net.read_pos[4])); - return len - 1; + DBUG_RETURN(len - 1); } int check_expected_error(THD* thd, RELAY_LOG_INFO* rli, int expected_error) { + DBUG_ENTER("check_expected_error"); + switch (expected_error) { case ER_NET_READ_ERROR: case ER_NET_ERROR_ON_WRITE: case ER_SERVER_SHUTDOWN: case ER_NEW_ABORTING_CONNECTION: - return 1; + DBUG_RETURN(1); default: - return 0; + DBUG_RETURN(0); } } @@ -2819,6 +2894,7 @@ bool st_relay_log_info::is_until_satisfied() { const char *log_name; ulonglong log_pos; + DBUG_ENTER("st_relay_log_info::is_until_satisfied"); DBUG_ASSERT(until_condition != UNTIL_NONE); @@ -2865,34 +2941,39 @@ bool st_relay_log_info::is_until_satisfied() /* Probably error so we aborting */ sql_print_error("Slave SQL thread is stopped because UNTIL " "condition is bad."); - return TRUE; + DBUG_RETURN(TRUE); } } else - return until_log_pos == 0; + DBUG_RETURN(until_log_pos == 0); } - return ((until_log_names_cmp_result == UNTIL_LOG_NAMES_CMP_EQUAL && + DBUG_RETURN(((until_log_names_cmp_result == UNTIL_LOG_NAMES_CMP_EQUAL && log_pos >= until_log_pos) || - until_log_names_cmp_result == UNTIL_LOG_NAMES_CMP_GREATER); + until_log_names_cmp_result == UNTIL_LOG_NAMES_CMP_GREATER)); } void st_relay_log_info::cached_charset_invalidate() { + DBUG_ENTER("st_relay_log_info::cached_charset_invalidate"); + /* Full of zeroes means uninitialized. */ bzero(cached_charset, sizeof(cached_charset)); + DBUG_VOID_RETURN; } bool st_relay_log_info::cached_charset_compare(char *charset) { + DBUG_ENTER("st_relay_log_info::cached_charset_compare"); + if (bcmp(cached_charset, charset, sizeof(cached_charset))) { memcpy(cached_charset, charset, sizeof(cached_charset)); - return 1; + DBUG_RETURN(1); } - return 0; + DBUG_RETURN(0); } /* @@ -2904,8 +2985,10 @@ bool st_relay_log_info::cached_charset_compare(char *charset) */ static int has_temporary_error(THD *thd) { + DBUG_ENTER("has_temporary_error"); + if (thd->is_fatal_error) - return 0; + DBUG_RETURN(0); /* Temporary error codes: @@ -2914,7 +2997,7 @@ static int has_temporary_error(THD *thd) */ if (thd->net.last_errno == ER_LOCK_DEADLOCK || thd->net.last_errno == ER_LOCK_WAIT_TIMEOUT) - return 1; + DBUG_RETURN(1); #ifdef HAVE_NDB_BINLOG /* @@ -2928,17 +3011,19 @@ static int has_temporary_error(THD *thd) switch (err->code) { case ER_GET_TEMPORARY_ERRMSG: - return 1; + DBUG_RETURN(1); default: break; } } #endif - return 0; + DBUG_RETURN(0); } static int exec_relay_log_event(THD* thd, RELAY_LOG_INFO* rli) { + DBUG_ENTER("exec_relay_log_event"); + /* We acquire this mutex since we need it for all operations except event execution. But we will release it in places where we will @@ -2965,7 +3050,7 @@ static int exec_relay_log_event(THD* thd, RELAY_LOG_INFO* rli) */ rli->abort_slave= 1; pthread_mutex_unlock(&rli->data_lock); - return 1; + DBUG_RETURN(1); } Log_event * ev = next_event(rli); @@ -2976,7 +3061,7 @@ static int exec_relay_log_event(THD* thd, RELAY_LOG_INFO* rli) { pthread_mutex_unlock(&rli->data_lock); delete ev; - return 1; + DBUG_RETURN(1); } if (ev) { @@ -3044,7 +3129,7 @@ static int exec_relay_log_event(THD* thd, RELAY_LOG_INFO* rli) --rli->slave_skip_counter; pthread_mutex_unlock(&rli->data_lock); delete ev; - return 0; // avoid infinite update loops + DBUG_RETURN(0); // avoid infinite update loops } pthread_mutex_unlock(&rli->data_lock); @@ -3125,13 +3210,11 @@ static int exec_relay_log_event(THD* thd, RELAY_LOG_INFO* rli) */ rli->trans_retries= 0; // restart from fresh } - } - return exec_res; + } + DBUG_RETURN(exec_res); } - else - { - pthread_mutex_unlock(&rli->data_lock); - slave_print_msg(ERROR_LEVEL, rli, 0, "\ + pthread_mutex_unlock(&rli->data_lock); + slave_print_msg(ERROR_LEVEL, rli, 0, "\ Could not parse relay log event entry. The possible reasons are: the master's \ binary log is corrupted (you can check this by running 'mysqlbinlog' on the \ binary log), the slave's relay log is corrupted (you can check this by running \ @@ -3140,8 +3223,7 @@ or slave's MySQL code. If you want to check the master's binary log or slave's \ relay log, you will be able to know their names by issuing 'SHOW SLAVE STATUS' \ on this slave.\ "); - return 1; - } + DBUG_RETURN(1); } @@ -3461,6 +3543,7 @@ pthread_handler_t handle_slave_sql(void *arg) { THD *thd; /* needs to be first for thread_stack */ char llbuff[22],llbuff1[22]; + RELAY_LOG_INFO* rli = &((MASTER_INFO*)arg)->rli; const char *errmsg; @@ -4060,16 +4143,18 @@ err: static int queue_old_event(MASTER_INFO *mi, const char *buf, ulong event_len) { + DBUG_ENTER("queue_old_event"); + switch (mi->rli.relay_log.description_event_for_queue->binlog_version) { case 1: - return queue_binlog_ver_1_event(mi,buf,event_len); + DBUG_RETURN(queue_binlog_ver_1_event(mi,buf,event_len)); case 3: - return queue_binlog_ver_3_event(mi,buf,event_len); + DBUG_RETURN(queue_binlog_ver_3_event(mi,buf,event_len)); default: /* unsupported format; eg version 2 */ DBUG_PRINT("info",("unsupported binlog format %d in queue_old_event()", mi->rli.relay_log.description_event_for_queue->binlog_version)); - return 1; + DBUG_RETURN(1); } } @@ -4282,7 +4367,9 @@ void end_relay_log_info(RELAY_LOG_INFO* rli) static int safe_connect(THD* thd, MYSQL* mysql, MASTER_INFO* mi) { - return connect_to_master(thd, mysql, mi, 0, 0); + DBUG_ENTER("safe_connect"); + + DBUG_RETURN(connect_to_master(thd, mysql, mi, 0, 0)); } @@ -4439,9 +4526,10 @@ static int safe_reconnect(THD* thd, MYSQL* mysql, MASTER_INFO* mi, bool flush_relay_log_info(RELAY_LOG_INFO* rli) { bool error=0; + DBUG_ENTER("flush_relay_log_info"); if (unlikely(rli->no_storage)) - return 0; + DBUG_RETURN(0); IO_CACHE *file = &rli->info_file; char buff[FN_REFLEN*2+22*2+4], *pos; @@ -4461,7 +4549,7 @@ bool flush_relay_log_info(RELAY_LOG_INFO* rli) error=1; /* Flushing the relay log is done by the slave I/O thread */ - return error; + DBUG_RETURN(error); } @@ -4471,9 +4559,9 @@ bool flush_relay_log_info(RELAY_LOG_INFO* rli) static IO_CACHE *reopen_relay_log(RELAY_LOG_INFO *rli, const char **errmsg) { + DBUG_ENTER("reopen_relay_log"); DBUG_ASSERT(rli->cur_log != &rli->cache_buf); DBUG_ASSERT(rli->cur_log_fd == -1); - DBUG_ENTER("reopen_relay_log"); IO_CACHE *cur_log = rli->cur_log=&rli->cache_buf; if ((rli->cur_log_fd=open_binlog(cur_log,rli->event_relay_log_name, @@ -4494,11 +4582,11 @@ static Log_event* next_event(RELAY_LOG_INFO* rli) { Log_event* ev; IO_CACHE* cur_log = rli->cur_log; - pthread_mutex_t *log_lock = rli->relay_log.get_log_lock(); + pthread_mutex_t *log_lock = rli->relay_log.get_log_lock(); const char* errmsg=0; THD* thd = rli->sql_thd; - DBUG_ENTER("next_event"); + DBUG_ASSERT(thd != 0); #ifndef DBUG_OFF @@ -4909,12 +4997,16 @@ static int reload_entry_compare(const void *lhs, const void *rhs) { const char *lstr = static_cast<const char *>(lhs); const char *rstr = static_cast<const st_reload_entry*>(rhs)->table; - return strcmp(lstr, rstr); + DBUG_ENTER("reload_entry_compare"); + + DBUG_RETURN(strcmp(lstr, rstr)); } void st_relay_log_info::touching_table(char const* db, char const* table, ulong table_id) { + DBUG_ENTER("st_relay_log_info::touching_table"); + if (strcmp(db,"mysql") == 0) { #if defined(HAVE_BSEARCH) && defined(HAVE_SIZE_T) @@ -4935,10 +5027,13 @@ void st_relay_log_info::touching_table(char const* db, char const* table, if (entry) m_reload_flags|= entry->flag; } + DBUG_VOID_RETURN; } void st_relay_log_info::transaction_end(THD* thd) { + DBUG_ENTER("st_relay_log_info::transaction_end"); + if (m_reload_flags != RELOAD_NONE_F) { if (m_reload_flags & RELOAD_ACCESS_F) @@ -4949,11 +5044,14 @@ void st_relay_log_info::transaction_end(THD* thd) m_reload_flags= RELOAD_NONE_F; } + DBUG_VOID_RETURN; } #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) void st_relay_log_info::cleanup_context(THD *thd, bool error) { + DBUG_ENTER("st_relay_log_info::cleanup_context"); + DBUG_ASSERT(sql_thd == thd); /* 1) Instances of Table_map_log_event, if ::exec_event() was called on them, @@ -4976,6 +5074,7 @@ void st_relay_log_info::cleanup_context(THD *thd, bool error) close_thread_tables(thd); clear_tables_to_lock(); unsafe_to_stop_at= 0; + DBUG_VOID_RETURN; } #endif diff --git a/sql/sp.cc b/sql/sp.cc index 0e81e627f71..653c04ee11a 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -137,6 +137,7 @@ TABLE *open_proc_table_for_read(THD *thd, Open_tables_state *backup) mysql_proc_table_exists= 0; DBUG_RETURN(0); } + table->use_all_columns(); DBUG_ASSERT(table->s->system_table); @@ -182,6 +183,8 @@ static TABLE *open_proc_table_for_update(THD *thd) tables.lock_type= TL_WRITE; table= open_ltable(thd, &tables, TL_WRITE); + if (table) + table->use_all_columns(); /* Under explicit LOCK TABLES or in prelocked mode we should not @@ -801,6 +804,7 @@ db_show_routine_status(THD *thd, int type, const char *wild) TABLE_LIST *leaves= 0; st_used_field used_fields[array_elements(init_fields)]; + table->use_all_columns(); memcpy((char*) used_fields, (char*) init_fields, sizeof(used_fields)); /* Init header */ for (used_field= &used_fields[0]; @@ -834,7 +838,7 @@ db_show_routine_status(THD *thd, int type, const char *wild) thd->lex->select_lex.context.resolve_in_table_list_only(&tables); setup_tables(thd, &thd->lex->select_lex.context, &thd->lex->select_lex.top_join_list, - &tables, 0, &leaves, FALSE); + &tables, &leaves, FALSE); for (used_field= &used_fields[0]; used_field->field_name; used_field++) diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 172cfbff532..b3c35ad5022 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -250,6 +250,7 @@ sp_get_flags_for_command(LEX *lex) case SQLCOM_TRUNCATE: case SQLCOM_COMMIT: case SQLCOM_ROLLBACK: + case SQLCOM_LOAD: case SQLCOM_LOAD_MASTER_DATA: case SQLCOM_LOCK_TABLES: case SQLCOM_CREATE_PROCEDURE: @@ -3441,7 +3442,7 @@ sp_add_to_query_tables(THD *thd, LEX *lex, table->table_name= thd->strmake(name, table->table_name_length); table->alias= thd->strdup(name); table->lock_type= locktype; - table->select_lex= lex->current_select; // QQ? + table->select_lex= lex->current_select; table->cacheable_table= 1; lex->add_to_query_tables(table); diff --git a/sql/spatial.cc b/sql/spatial.cc index 9f1f05aa18f..02f1525f2ad 100644 --- a/sql/spatial.cc +++ b/sql/spatial.cc @@ -829,7 +829,6 @@ int Gis_polygon::area(double *ar, const char **end_of_data) const double x, y; get_point(&x, &y, data); data+= (SIZEOF_STORED_DOUBLE*2); - /* QQ: Is the following prev_x+x right ? */ lr_area+= (prev_x + x)* (prev_y - y); prev_x= x; prev_y= y; @@ -952,7 +951,6 @@ int Gis_polygon::centroid_xy(double *x, double *y) const double x, y; get_point(&x, &y, data); data+= (SIZEOF_STORED_DOUBLE*2); - /* QQ: Is the following prev_x+x right ? */ cur_area+= (prev_x + x) * (prev_y - y); cur_cx+= x; cur_cy+= y; diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index d2501d08c4d..4c2dfac6b8b 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -323,6 +323,7 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) init_sql_alloc(&mem, ACL_ALLOC_BLOCK_SIZE, 0); init_read_record(&read_record_info,thd,table= tables[0].table,NULL,1,0); + table->use_all_columns(); VOID(my_init_dynamic_array(&acl_hosts,sizeof(ACL_HOST),20,50)); while (!(read_record_info.read_record(&read_record_info))) { @@ -342,7 +343,8 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) "case that has been forced to lowercase because " "lower_case_table_names is set. It will not be " "possible to remove this privilege using REVOKE.", - host.host.hostname, host.db); + host.host.hostname ? host.host.hostname : "", + host.db ? host.db : ""); } host.access= get_access(table,2); host.access= fix_rights_for_db(host.access); @@ -351,7 +353,8 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) { sql_print_warning("'host' entry '%s|%s' " "ignored in --skip-name-resolve mode.", - host.host.hostname, host.db?host.db:""); + host.host.hostname ? host.host.hostname : "", + host.db ? host.db : ""); continue; } #ifndef TO_BE_REMOVED @@ -369,6 +372,7 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) freeze_size(&acl_hosts); init_read_record(&read_record_info,thd,table=tables[1].table,NULL,1,0); + table->use_all_columns(); VOID(my_init_dynamic_array(&acl_users,sizeof(ACL_USER),50,100)); password_length= table->field[2]->field_length / table->field[2]->charset()->mbmaxlen; @@ -421,7 +425,8 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) { sql_print_warning("'user' entry '%s@%s' " "ignored in --skip-name-resolve mode.", - user.user, user.host.hostname); + user.user ? user.user : "", + user.host.hostname ? user.host.hostname : ""); continue; } @@ -544,8 +549,8 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) #endif } VOID(push_dynamic(&acl_users,(gptr) &user)); - if (!user.host.hostname || user.host.hostname[0] == wild_many && - !user.host.hostname[1]) + if (!user.host.hostname || + (user.host.hostname[0] == wild_many && !user.host.hostname[1])) allow_all_hosts=1; // Anyone can connect } } @@ -555,6 +560,7 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) freeze_size(&acl_users); init_read_record(&read_record_info,thd,table=tables[2].table,NULL,1,0); + table->use_all_columns(); VOID(my_init_dynamic_array(&acl_dbs,sizeof(ACL_DB),50,100)); while (!(read_record_info.read_record(&read_record_info))) { @@ -571,7 +577,9 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) { sql_print_warning("'db' entry '%s %s@%s' " "ignored in --skip-name-resolve mode.", - db.db, db.user, db.host.hostname); + db.db, + db.user ? db.user : "", + db.host.hostname ? db.host.hostname : ""); continue; } db.access=get_access(table,3); @@ -590,7 +598,9 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) "case that has been forced to lowercase because " "lower_case_table_names is set. It will not be " "possible to remove this privilege using REVOKE.", - db.db, db.user, db.host.hostname, db.host.hostname); + db.db, + db.user ? db.user : "", + db.host.hostname ? db.host.hostname : ""); } } db.sort=get_sort(3,db.host.hostname,db.db,db.user); @@ -1088,6 +1098,8 @@ bool acl_getroot_no_password(Security_context *sctx, char *user, char *host, sctx->master_access= 0; sctx->db_access= 0; + sctx->priv_user= (char *) ""; + *sctx->priv_host= 0; /* Find acl entry in user database. @@ -1162,8 +1174,7 @@ static void acl_update_user(const char *user, const char *host, { ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*); if (!acl_user->user && !user[0] || - acl_user->user && - !strcmp(user,acl_user->user)) + acl_user->user && !strcmp(user,acl_user->user)) { if (!acl_user->host.hostname && !host[0] || acl_user->host.hostname && @@ -1226,8 +1237,8 @@ static void acl_insert_user(const char *user, const char *host, set_user_salt(&acl_user, password, password_len); VOID(push_dynamic(&acl_users,(gptr) &acl_user)); - if (!acl_user.host.hostname || acl_user.host.hostname[0] == wild_many - && !acl_user.host.hostname[1]) + if (!acl_user.host.hostname || + (acl_user.host.hostname[0] == wild_many && !acl_user.host.hostname[1])) allow_all_hosts=1; // Anyone can connect /* purecov: tested */ qsort((gptr) dynamic_element(&acl_users,0,ACL_USER*),acl_users.elements, sizeof(ACL_USER),(qsort_cmp) acl_compare); @@ -1287,7 +1298,7 @@ static void acl_insert_db(const char *user, const char *host, const char *db, ACL_DB acl_db; safe_mutex_assert_owner(&acl_cache->lock); acl_db.user=strdup_root(&mem,user); - update_hostname(&acl_db.host,strdup_root(&mem,host)); + update_hostname(&acl_db.host, *host ? strdup_root(&mem,host) : 0); acl_db.db=strdup_root(&mem,db); acl_db.access=privileges; acl_db.sort=get_sort(3,acl_db.host.hostname,acl_db.db,acl_db.user); @@ -1674,11 +1685,10 @@ find_acl_user(const char *host, const char *user, my_bool exact) { ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*); DBUG_PRINT("info",("strcmp('%s','%s'), compare_hostname('%s','%s'),", - user, - acl_user->user ? acl_user->user : "", - host, - acl_user->host.hostname ? acl_user->host.hostname : - "")); + user, acl_user->user ? acl_user->user : "", + host, + acl_user->host.hostname ? acl_user->host.hostname : + "")); if (!acl_user->user && !user[0] || acl_user->user && !strcmp(user,acl_user->user)) { @@ -1728,7 +1738,7 @@ static const char *calc_ip(const char *ip, long *val, char end) static void update_hostname(acl_host_and_ip *host, const char *hostname) { - host->hostname=(char*) hostname; // This will not be modified! + host->hostname=(char*) hostname; // This will not be modified! if (!hostname || (!(hostname=calc_ip(hostname,&host->ip,'/')) || !(hostname=calc_ip(hostname+1,&host->ip_mask,'\0')))) @@ -1748,8 +1758,8 @@ static bool compare_hostname(const acl_host_and_ip *host, const char *hostname, } return (!host->hostname || (hostname && !wild_case_compare(system_charset_info, - hostname,host->hostname)) || - (ip && !wild_compare(ip,host->hostname,0))); + hostname, host->hostname)) || + (ip && !wild_compare(ip, host->hostname, 0))); } bool hostname_requires_resolving(const char *hostname) @@ -1795,14 +1805,15 @@ static bool update_user_table(THD *thd, TABLE *table, DBUG_ENTER("update_user_table"); DBUG_PRINT("enter",("user: %s host: %s",user,host)); + table->use_all_columns(); table->field[0]->store(host,(uint) strlen(host), system_charset_info); table->field[1]->store(user,(uint) strlen(user), system_charset_info); key_copy((byte *) user_key, table->record[0], table->key_info, table->key_info->key_length); - table->file->ha_retrieve_all_cols(); if (table->file->index_read_idx(table->record[0], 0, - (byte *) user_key, table->key_info->key_length, + (byte *) user_key, + table->key_info->key_length, HA_READ_KEY_EXACT)) { my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH), @@ -1885,12 +1896,14 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo, password=combo.password.str; } - table->field[0]->store(combo.host.str,combo.host.length, system_charset_info); - table->field[1]->store(combo.user.str,combo.user.length, system_charset_info); + table->use_all_columns(); + table->field[0]->store(combo.host.str,combo.host.length, + system_charset_info); + table->field[1]->store(combo.user.str,combo.user.length, + system_charset_info); key_copy(user_key, table->record[0], table->key_info, table->key_info->key_length); - table->file->ha_retrieve_all_cols(); if (table->file->index_read_idx(table->record[0], 0, user_key, table->key_info->key_length, HA_READ_KEY_EXACT)) @@ -2026,7 +2039,6 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo, We should NEVER delete from the user table, as a uses can still use mysqld even if he doesn't have any privileges in the user table! */ - table->file->ha_retrieve_all_cols(); if (cmp_record(table,record[1]) && (error=table->file->ha_update_row(table->record[1],table->record[0]))) { // This should never happen @@ -2102,13 +2114,15 @@ static int replace_db_table(TABLE *table, const char *db, DBUG_RETURN(-1); } - table->field[0]->store(combo.host.str,combo.host.length, system_charset_info); + table->use_all_columns(); + table->field[0]->store(combo.host.str,combo.host.length, + system_charset_info); table->field[1]->store(db,(uint) strlen(db), system_charset_info); - table->field[2]->store(combo.user.str,combo.user.length, system_charset_info); + table->field[2]->store(combo.user.str,combo.user.length, + system_charset_info); key_copy(user_key, table->record[0], table->key_info, table->key_info->key_length); - table->file->ha_retrieve_all_cols(); if (table->file->index_read_idx(table->record[0],0, user_key, table->key_info->key_length, HA_READ_KEY_EXACT)) @@ -2120,9 +2134,11 @@ static int replace_db_table(TABLE *table, const char *db, } old_row_exists = 0; restore_record(table, s->default_values); - table->field[0]->store(combo.host.str,combo.host.length, system_charset_info); + table->field[0]->store(combo.host.str,combo.host.length, + system_charset_info); table->field[1]->store(db,(uint) strlen(db), system_charset_info); - table->field[2]->store(combo.user.str,combo.user.length, system_charset_info); + table->field[2]->store(combo.user.str,combo.user.length, + system_charset_info); } else { @@ -2144,18 +2160,17 @@ static int replace_db_table(TABLE *table, const char *db, /* update old existing row */ if (rights) { - table->file->ha_retrieve_all_cols(); - if ((error=table->file->ha_update_row(table->record[1], - table->record[0]))) + if ((error= table->file->ha_update_row(table->record[1], + table->record[0]))) goto table_error; /* purecov: deadcode */ } else /* must have been a revoke of all privileges */ { - if ((error = table->file->ha_delete_row(table->record[1]))) + if ((error= table->file->ha_delete_row(table->record[1]))) goto table_error; /* purecov: deadcode */ } } - else if (rights && (error=table->file->ha_write_row(table->record[0]))) + else if (rights && (error= table->file->ha_write_row(table->record[0]))) { if (error && error != HA_ERR_FOUND_DUPP_KEY) /* purecov: inspected */ goto table_error; /* purecov: deadcode */ @@ -2311,7 +2326,8 @@ GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs) uint key_prefix_len; KEY_PART_INFO *key_part= col_privs->key_info->key_part; col_privs->field[0]->store(host.hostname, - host.hostname ? (uint) strlen(host.hostname) : 0, + host.hostname ? (uint) strlen(host.hostname) : + 0, system_charset_info); col_privs->field[1]->store(db,(uint) strlen(db), system_charset_info); col_privs->field[2]->store(user,(uint) strlen(user), system_charset_info); @@ -2397,7 +2413,8 @@ static GRANT_NAME *name_hash_search(HASH *name_hash, { if (exact) { - if ((host && + if (!grant_name->host.hostname || + (host && !my_strcasecmp(system_charset_info, host, grant_name->host.hostname)) || (ip && !strcmp(ip, grant_name->host.hostname))) @@ -2452,6 +2469,7 @@ static int replace_column_table(GRANT_TABLE *g_t, KEY_PART_INFO *key_part= table->key_info->key_part; DBUG_ENTER("replace_column_table"); + table->use_all_columns(); table->field[0]->store(combo.host.str,combo.host.length, system_charset_info); table->field[1]->store(db,(uint) strlen(db), @@ -2487,7 +2505,6 @@ static int replace_column_table(GRANT_TABLE *g_t, key_copy(user_key, table->record[0], table->key_info, table->key_info->key_length); - table->file->ha_retrieve_all_cols(); if (table->file->index_read(table->record[0], user_key, table->key_info->key_length, HA_READ_KEY_EXACT)) @@ -2565,7 +2582,6 @@ static int replace_column_table(GRANT_TABLE *g_t, key_copy(user_key, table->record[0], table->key_info, key_prefix_length); - table->file->ha_retrieve_all_cols(); if (table->file->index_read(table->record[0], user_key, key_prefix_length, HA_READ_KEY_EXACT)) @@ -2655,16 +2671,19 @@ static int replace_table_table(THD *thd, GRANT_TABLE *grant_table, DBUG_RETURN(-1); /* purecov: deadcode */ } + table->use_all_columns(); restore_record(table, s->default_values); // Get empty record - table->field[0]->store(combo.host.str,combo.host.length, system_charset_info); + table->field[0]->store(combo.host.str,combo.host.length, + system_charset_info); table->field[1]->store(db,(uint) strlen(db), system_charset_info); - table->field[2]->store(combo.user.str,combo.user.length, system_charset_info); - table->field[3]->store(table_name,(uint) strlen(table_name), system_charset_info); + table->field[2]->store(combo.user.str,combo.user.length, + system_charset_info); + table->field[3]->store(table_name,(uint) strlen(table_name), + system_charset_info); store_record(table,record[1]); // store at pos 1 key_copy(user_key, table->record[0], table->key_info, table->key_info->key_length); - table->file->ha_retrieve_all_cols(); if (table->file->index_read_idx(table->record[0], 0, user_key, table->key_info->key_length, HA_READ_KEY_EXACT)) @@ -2777,6 +2796,7 @@ static int replace_routine_table(THD *thd, GRANT_NAME *grant_name, DBUG_RETURN(-1); } + table->use_all_columns(); restore_record(table, s->default_values); // Get empty record table->field[0]->store(combo.host.str,combo.host.length, &my_charset_latin1); table->field[1]->store(db,(uint) strlen(db), &my_charset_latin1); @@ -3473,10 +3493,14 @@ static my_bool grant_load(TABLE_LIST *tables) 0,0); init_sql_alloc(&memex, ACL_ALLOC_BLOCK_SIZE, 0); - t_table = tables[0].table; c_table = tables[1].table; + t_table = tables[0].table; + c_table = tables[1].table; p_table= tables[2].table; t_table->file->ha_index_init(0, 1); p_table->file->ha_index_init(0, 1); + t_table->use_all_columns(); + c_table->use_all_columns(); + p_table->use_all_columns(); if (!t_table->file->index_first(t_table->record[0])) { memex_ptr= &memex; @@ -3484,7 +3508,7 @@ static my_bool grant_load(TABLE_LIST *tables) do { GRANT_TABLE *mem_check; - if (!(mem_check=new GRANT_TABLE(t_table,c_table))) + if (!(mem_check=new (memex_ptr) GRANT_TABLE(t_table,c_table))) { /* This could only happen if we are out memory */ grant_option= FALSE; @@ -3497,8 +3521,10 @@ static my_bool grant_load(TABLE_LIST *tables) { sql_print_warning("'tables_priv' entry '%s %s@%s' " "ignored in --skip-name-resolve mode.", - mem_check->tname, mem_check->user, - mem_check->host, mem_check->host); + mem_check->tname, + mem_check->user ? mem_check->user : "", + mem_check->host.hostname ? + mem_check->host.hostname : ""); continue; } } @@ -3522,7 +3548,7 @@ static my_bool grant_load(TABLE_LIST *tables) { GRANT_NAME *mem_check; HASH *hash; - if (!(mem_check=new GRANT_NAME(p_table))) + if (!(mem_check=new (&memex) GRANT_NAME(p_table))) { /* This could only happen if we are out memory */ grant_option= FALSE; @@ -3536,7 +3562,8 @@ static my_bool grant_load(TABLE_LIST *tables) sql_print_warning("'procs_priv' entry '%s %s@%s' " "ignored in --skip-name-resolve mode.", mem_check->tname, mem_check->user, - mem_check->host); + mem_check->host.hostname ? + mem_check->host.hostname : ""); continue; } } @@ -3692,6 +3719,7 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, TABLE_LIST *table, *first_not_own_table= thd->lex->first_not_own_table(); Security_context *sctx= thd->security_ctx; uint i; + ulong orig_want_access= want_access; DBUG_ENTER("check_grant"); DBUG_ASSERT(number > 0); @@ -3713,18 +3741,22 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, table->grant.orig_want_privilege= (want_access & ~SHOW_VIEW_ACL); } - want_access&= ~sctx->master_access; - if (!want_access) - DBUG_RETURN(0); // ok - rw_rdlock(&LOCK_grant); for (table= tables; table && number-- && table != first_not_own_table; table= table->next_global) { GRANT_TABLE *grant_table; + sctx = test(table->security_ctx) ? + table->security_ctx : thd->security_ctx; + + want_access= orig_want_access; + want_access&= ~sctx->master_access; + if (!want_access) + continue; // ok + if (!(~table->grant.privilege & want_access) || - table->derived || table->schema_table || table->belong_to_view) + table->derived || table->schema_table) { /* It is subquery in the FROM clause. VIEW set table->derived after @@ -4243,11 +4275,6 @@ bool mysql_show_grants(THD *thd,LEX_USER *lex_user) DBUG_RETURN(TRUE); } - if (!lex_user->host.str) - { - lex_user->host.str= (char*) "%"; - lex_user->host.length=1; - } if (lex_user->host.length > HOSTNAME_LENGTH || lex_user->user.length > USERNAME_LENGTH) { @@ -4457,16 +4484,17 @@ bool mysql_show_grants(THD *thd,LEX_USER *lex_user) /* Add table & column access */ for (index=0 ; index < column_priv_hash.records ; index++) { - const char *user; + const char *user, *host; GRANT_TABLE *grant_table= (GRANT_TABLE*) hash_element(&column_priv_hash, index); if (!(user=grant_table->user)) user= ""; + if (!(host= grant_table->host.hostname)) + host= ""; if (!strcmp(lex_user->user.str,user) && - !my_strcasecmp(system_charset_info, lex_user->host.str, - grant_table->host.hostname)) + !my_strcasecmp(system_charset_info, lex_user->host.str, host)) { ulong table_access= grant_table->privs; if ((table_access | grant_table->cols) != 0) @@ -4593,15 +4621,16 @@ static int show_routine_grants(THD* thd, LEX_USER *lex_user, HASH *hash, /* Add routine access */ for (index=0 ; index < hash->records ; index++) { - const char *user; + const char *user, *host; GRANT_NAME *grant_proc= (GRANT_NAME*) hash_element(hash, index); if (!(user=grant_proc->user)) user= ""; + if (!(host= grant_proc->host.hostname)) + host= ""; if (!strcmp(lex_user->user.str,user) && - !my_strcasecmp(system_charset_info, lex_user->host.str, - grant_proc->host.hostname)) + !my_strcasecmp(system_charset_info, lex_user->host.str, host)) { ulong proc_access= grant_proc->privs; if (proc_access != 0) @@ -4877,6 +4906,7 @@ static int handle_grant_table(TABLE_LIST *tables, uint table_no, bool drop, uint key_prefix_length; DBUG_ENTER("handle_grant_table"); + table->use_all_columns(); if (! table_no) // mysql.user table { /* @@ -5055,39 +5085,37 @@ static int handle_grant_struct(uint struct_no, bool drop, { /* Get a pointer to the element. - Unfortunaltely, the host default differs for the structures. */ switch (struct_no) { case 0: acl_user= dynamic_element(&acl_users, idx, ACL_USER*); user= acl_user->user; - if (!(host= acl_user->host.hostname)) - host= "%"; - break; + host= acl_user->host.hostname; + break; case 1: acl_db= dynamic_element(&acl_dbs, idx, ACL_DB*); user= acl_db->user; - if (!(host= acl_db->host.hostname)) - host= "%"; + host= acl_db->host.hostname; break; case 2: grant_name= (GRANT_NAME*) hash_element(&column_priv_hash, idx); user= grant_name->user; - if (!(host= grant_name->host.hostname)) - host= "%"; + host= grant_name->host.hostname; break; case 3: grant_name= (GRANT_NAME*) hash_element(&proc_priv_hash, idx); user= grant_name->user; - if (!(host= grant_name->host.hostname)) - host= "%"; + host= grant_name->host.hostname; break; } if (! user) user= ""; + if (! host) + host= ""; + #ifdef EXTRA_DEBUG DBUG_PRINT("loop",("scan struct: %u index: %u user: '%s' host: '%s'", struct_no, idx, user, host)); @@ -5526,7 +5554,8 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list) if (!strcmp(lex_user->user.str,user) && !my_strcasecmp(system_charset_info, lex_user->host.str, host)) { - if (!replace_db_table(tables[1].table, acl_db->db, *lex_user, ~(ulong)0, 1)) + if (!replace_db_table(tables[1].table, acl_db->db, *lex_user, + ~(ulong)0, 1)) { /* Don't increment counter as replace_db_table deleted the @@ -5671,8 +5700,10 @@ bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name, LEX_USER lex_user; lex_user.user.str= grant_proc->user; lex_user.user.length= strlen(grant_proc->user); - lex_user.host.str= grant_proc->host.hostname; - lex_user.host.length= strlen(grant_proc->host.hostname); + lex_user.host.str= grant_proc->host.hostname ? + grant_proc->host.hostname : (char*)""; + lex_user.host.length= grant_proc->host.hostname ? + strlen(grant_proc->host.hostname) : 0; if (!replace_routine_table(thd,grant_proc,tables[4].table,lex_user, grant_proc->db, grant_proc->tname, is_proc, ~(ulong)0, 1)) @@ -5979,16 +6010,17 @@ int fill_schema_table_privileges(THD *thd, TABLE_LIST *tables, COND *cond) for (index=0 ; index < column_priv_hash.records ; index++) { - const char *user, *is_grantable= "YES"; + const char *user, *host, *is_grantable= "YES"; GRANT_TABLE *grant_table= (GRANT_TABLE*) hash_element(&column_priv_hash, index); if (!(user=grant_table->user)) user= ""; + if (!(host= grant_table->host.hostname)) + host= ""; if (no_global_access && (strcmp(thd->security_ctx->priv_user, user) || - my_strcasecmp(system_charset_info, curr_host, - grant_table->host.hostname))) + my_strcasecmp(system_charset_info, curr_host, host))) continue; ulong table_access= grant_table->privs; @@ -6004,7 +6036,7 @@ int fill_schema_table_privileges(THD *thd, TABLE_LIST *tables, COND *cond) if (!(table_access & GRANT_ACL)) is_grantable= "NO"; - strxmov(buff,"'",user,"'@'",grant_table->host.hostname,"'",NullS); + strxmov(buff, "'", user, "'@'", host, "'", NullS); if (!test_access) update_schema_privilege(table, buff, grant_table->db, grant_table->tname, 0, 0, STRING_WITH_LEN("USAGE"), is_grantable); @@ -6046,16 +6078,17 @@ int fill_schema_column_privileges(THD *thd, TABLE_LIST *tables, COND *cond) for (index=0 ; index < column_priv_hash.records ; index++) { - const char *user, *is_grantable= "YES"; + const char *user, *host, *is_grantable= "YES"; GRANT_TABLE *grant_table= (GRANT_TABLE*) hash_element(&column_priv_hash, index); if (!(user=grant_table->user)) user= ""; + if (!(host= grant_table->host.hostname)) + host= ""; if (no_global_access && (strcmp(thd->security_ctx->priv_user, user) || - my_strcasecmp(system_charset_info, curr_host, - grant_table->host.hostname))) + my_strcasecmp(system_charset_info, curr_host, host))) continue; ulong table_access= grant_table->cols; @@ -6065,7 +6098,7 @@ int fill_schema_column_privileges(THD *thd, TABLE_LIST *tables, COND *cond) is_grantable= "NO"; ulong test_access= table_access & ~GRANT_ACL; - strxmov(buff,"'",user,"'@'",grant_table->host.hostname,"'",NullS); + strxmov(buff, "'", user, "'@'", host, "'", NullS); if (!test_access) continue; else diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 58c04224ac9..9b7d46119b7 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -25,7 +25,7 @@ #include <m_ctype.h> #include <my_dir.h> #include <hash.h> -#ifdef __WIN__ +#ifdef __WIN__ #include <io.h> #endif @@ -879,7 +879,7 @@ bool close_cached_tables(THD *thd, bool if_wait_for_refresh, bool found=1; /* Wait until all threads has closed all the tables we had locked */ DBUG_PRINT("info", - ("Waiting for others threads to close their open tables")); + ("Waiting for other threads to close their open tables")); while (found && ! thd->killed) { found=0; @@ -890,6 +890,7 @@ bool close_cached_tables(THD *thd, bool if_wait_for_refresh, ((table->s->version) < refresh_version && table->db_stat)) { found=1; + DBUG_PRINT("signal", ("Waiting for COND_refresh")); pthread_cond_wait(&COND_refresh,&LOCK_open); break; } @@ -947,8 +948,13 @@ bool close_cached_tables(THD *thd, bool if_wait_for_refresh, static void mark_used_tables_as_free_for_reuse(THD *thd, TABLE *table) { for (; table ; table= table->next) + { if (table->query_id == thd->query_id) + { table->query_id= 0; + table->file->ha_reset(); + } + } } @@ -1028,21 +1034,13 @@ void close_thread_tables(THD *thd, bool lock_in_use, bool skip_derived) */ ha_commit_stmt(thd); - /* We are under simple LOCK TABLES so should not do anything else. */ - if (!prelocked_mode) - DBUG_VOID_RETURN; + /* Ensure we are calling ha_reset() for all used tables */ + mark_used_tables_as_free_for_reuse(thd, thd->open_tables); - if (!thd->lex->requires_prelocking()) - { - /* - If we are executing one of substatements we have to mark - all tables which it used as free for reuse. - */ - mark_used_tables_as_free_for_reuse(thd, thd->open_tables); + /* We are under simple LOCK TABLES so should not do anything else. */ + if (!prelocked_mode || !thd->lex->requires_prelocking()) DBUG_VOID_RETURN; - } - DBUG_ASSERT(prelocked_mode); /* We are in prelocked mode, so we have to leave it now with doing implicit UNLOCK TABLES if need. @@ -1096,7 +1094,7 @@ void close_thread_tables(THD *thd, bool lock_in_use, bool skip_derived) found_old_table= 0; while (thd->open_tables) - found_old_table|=close_thread_table(thd, &thd->open_tables); + found_old_table|= close_thread_table(thd, &thd->open_tables); thd->some_tables_deleted=0; /* Free tables to hold down open files */ @@ -1125,6 +1123,7 @@ void close_thread_tables(THD *thd, bool lock_in_use, bool skip_derived) DBUG_VOID_RETURN; } + /* move one table to free list */ bool close_thread_table(THD *thd, TABLE **table_ptr) @@ -1149,11 +1148,8 @@ bool close_thread_table(THD *thd, TABLE **table_ptr) table->s->flush_version= flush_version; table->file->extra(HA_EXTRA_FLUSH); } - else - { - // Free memory and reset for next loop - table->file->ha_reset(); - } + // Free memory and reset for next loop + table->file->ha_reset(); table->in_use=0; if (unused_tables) { @@ -1183,10 +1179,8 @@ static inline uint tmpkeyval(THD *thd, TABLE *table) void close_temporary_tables(THD *thd) { - TABLE *next, - *prev_table /* prev link is not maintained in TABLE's double-linked list */, - *table; - char *query= (gptr) 0, *end; + TABLE *next, *prev_table, *table; + char *query= 0, *end; uint query_buf_size, max_names_len; bool found_user_tables; @@ -1684,6 +1678,7 @@ void wait_for_condition(THD *thd, pthread_mutex_t *mutex, pthread_cond_t *cond) thd->mysys_var->current_cond= cond; proc_info=thd->proc_info; thd->proc_info="Waiting for table"; + DBUG_ENTER("wait_for_condition"); if (!thd->killed) (void) pthread_cond_wait(cond, mutex); @@ -1704,6 +1699,7 @@ void wait_for_condition(THD *thd, pthread_mutex_t *mutex, pthread_cond_t *cond) thd->mysys_var->current_cond= 0; thd->proc_info= proc_info; pthread_mutex_unlock(&thd->mysys_var->mutex); + DBUG_VOID_RETURN; } @@ -1982,6 +1978,10 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, { if (table->s->version != refresh_version) { + DBUG_PRINT("note", + ("Found table '%s.%s' with different refresh version", + table_list->db, table_list->table_name)); + /* Don't close tables if we are working with a log table or were asked not to close the table explicitly @@ -2089,6 +2089,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, if (table->timestamp_field) table->timestamp_field_type= table->timestamp_field->get_auto_set_type(); table_list->updatable= 1; // It is not derived table nor non-updatable VIEW + table->clear_column_bitmaps(); DBUG_ASSERT(table->key_read == 0); DBUG_RETURN(table); } @@ -2186,6 +2187,7 @@ static bool reopen_table(TABLE *table) VOID(closefrm(table, 1)); // close file, free everything *table= tmp; + table->default_column_bitmaps(); table->file->change_table_ptr(table, table->s); DBUG_ASSERT(table->alias != 0); @@ -2684,7 +2686,7 @@ retry: goto err; // Code below is for repairing a crashed file - if ((error= lock_table_name(thd, table_list))) + if ((error= lock_table_name(thd, table_list, TRUE))) { if (error < 0) goto err; @@ -3553,22 +3555,50 @@ Field *view_ref_found= (Field*) 0x2; static void update_field_dependencies(THD *thd, Field *field, TABLE *table) { - if (thd->set_query_id) + DBUG_ENTER("update_field_dependencies"); + if (thd->mark_used_columns != MARK_COLUMNS_NONE) { - table->file->ha_set_bit_in_rw_set(field->fieldnr, - (bool)(thd->set_query_id-1)); - if (field->query_id != thd->query_id) + MY_BITMAP *current_bitmap, *other_bitmap; + + /* + We always want to register the used keys, as the column bitmap may have + been set for all fields (for example for view). + */ + + table->used_keys.intersect(field->part_of_key); + table->merge_keys.merge(field->part_of_key); + + if (thd->mark_used_columns == MARK_COLUMNS_READ) { - if (table->get_fields_in_item_tree) - field->flags|= GET_FIXED_FIELDS_FLAG; - field->query_id= thd->query_id; - table->used_fields++; - table->used_keys.intersect(field->part_of_key); + current_bitmap= table->read_set; + other_bitmap= table->write_set; } else - thd->dupp_field= field; - } else if (table->get_fields_in_item_tree) + { + current_bitmap= table->write_set; + other_bitmap= table->read_set; + } + + if (bitmap_fast_test_and_set(current_bitmap, field->field_index)) + { + if (thd->mark_used_columns == MARK_COLUMNS_WRITE) + { + DBUG_PRINT("warning", ("Found duplicated field")); + thd->dup_field= field; + } + else + { + DBUG_PRINT("note", ("Field found before")); + } + DBUG_VOID_RETURN; + } + if (table->get_fields_in_item_tree) + field->flags|= GET_FIXED_FIELDS_FLAG; + table->used_fields++; + } + else if (table->get_fields_in_item_tree) field->flags|= GET_FIXED_FIELDS_FLAG; + DBUG_VOID_RETURN; } @@ -3977,12 +4007,12 @@ find_field_in_table_ref(THD *thd, TABLE_LIST *table_list, fld= WRONG_GRANT; else #endif - if (thd->set_query_id) + if (thd->mark_used_columns != MARK_COLUMNS_NONE) { /* - * get rw_set correct for this field so that the handler - * knows that this field is involved in the query and gets - * retrieved/updated + Get rw_set correct for this field so that the handler + knows that this field is involved in the query and gets + retrieved/updated */ Field *field_to_set= NULL; if (fld == view_ref_found) @@ -3990,13 +4020,22 @@ find_field_in_table_ref(THD *thd, TABLE_LIST *table_list, Item *it= (*ref)->real_item(); if (it->type() == Item::FIELD_ITEM) field_to_set= ((Item_field*)it)->field; + else + { + if (thd->mark_used_columns == MARK_COLUMNS_READ) + it->walk(&Item::register_field_in_read_map, 1, (byte *) 0); + } } else field_to_set= fld; if (field_to_set) - field_to_set->table->file-> - ha_set_bit_in_rw_set(field_to_set->fieldnr, - (bool)(thd->set_query_id-1)); + { + TABLE *table= field_to_set->table; + if (thd->mark_used_columns == MARK_COLUMNS_READ) + bitmap_set_bit(table->read_set, field_to_set->field_index); + else + bitmap_set_bit(table->write_set, field_to_set->field_index); + } } } DBUG_RETURN(fld); @@ -4689,17 +4728,17 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2, { TABLE *table_1= nj_col_1->table_ref->table; /* Mark field_1 used for table cache. */ - field_1->query_id= thd->query_id; - table_1->file->ha_set_bit_in_read_set(field_1->fieldnr); + bitmap_set_bit(table_1->read_set, field_1->field_index); table_1->used_keys.intersect(field_1->part_of_key); + table_1->merge_keys.merge(field_1->part_of_key); } if (field_2) { TABLE *table_2= nj_col_2->table_ref->table; /* Mark field_2 used for table cache. */ - field_2->query_id= thd->query_id; - table_2->file->ha_set_bit_in_read_set(field_2->fieldnr); + bitmap_set_bit(table_2->read_set, field_2->field_index); table_2->used_keys.intersect(field_2->part_of_key); + table_2->merge_keys.merge(field_2->part_of_key); } if (using_fields != NULL) @@ -5167,17 +5206,17 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields, ****************************************************************************/ bool setup_fields(THD *thd, Item **ref_pointer_array, - List<Item> &fields, ulong set_query_id, + List<Item> &fields, enum_mark_columns mark_used_columns, List<Item> *sum_func_list, bool allow_sum_func) { reg2 Item *item; - ulong save_set_query_id= thd->set_query_id; + enum_mark_columns save_mark_used_columns= thd->mark_used_columns; nesting_map save_allow_sum_func= thd->lex->allow_sum_func; List_iterator<Item> it(fields); DBUG_ENTER("setup_fields"); - thd->set_query_id=set_query_id; - DBUG_PRINT("info", ("thd->set_query_id: %d", thd->set_query_id)); + thd->mark_used_columns= mark_used_columns; + DBUG_PRINT("info", ("thd->mark_used_columns: %d", thd->mark_used_columns)); if (allow_sum_func) thd->lex->allow_sum_func|= 1 << thd->lex->current_select->nest_level; thd->where= THD::DEFAULT_WHERE; @@ -5203,8 +5242,8 @@ bool setup_fields(THD *thd, Item **ref_pointer_array, (item= *(it.ref()))->check_cols(1)) { thd->lex->allow_sum_func= save_allow_sum_func; - thd->set_query_id= save_set_query_id; - DBUG_PRINT("info", ("thd->set_query_id: %d", thd->set_query_id)); + thd->mark_used_columns= save_mark_used_columns; + DBUG_PRINT("info", ("thd->mark_used_columns: %d", thd->mark_used_columns)); DBUG_RETURN(TRUE); /* purecov: inspected */ } if (ref) @@ -5215,8 +5254,8 @@ bool setup_fields(THD *thd, Item **ref_pointer_array, thd->used_tables|= item->used_tables(); } thd->lex->allow_sum_func= save_allow_sum_func; - thd->set_query_id= save_set_query_id; - DBUG_PRINT("info", ("thd->set_query_id: %d", thd->set_query_id)); + thd->mark_used_columns= save_mark_used_columns; + DBUG_PRINT("info", ("thd->mark_used_columns: %d", thd->mark_used_columns)); DBUG_RETURN(test(thd->net.report_error)); } @@ -5260,7 +5299,6 @@ TABLE_LIST **make_leaves_list(TABLE_LIST **list, TABLE_LIST *tables) context name resolution contest to setup table list there from_clause Top-level list of table references in the FROM clause tables Table list (select_lex->table_list) - conds Condition of current SELECT (can be changed by VIEW) leaves List of join table leaves list (select_lex->leaf_tables) refresh It is onle refresh for subquery select_insert It is SELECT ... INSERT command @@ -5282,7 +5320,7 @@ TABLE_LIST **make_leaves_list(TABLE_LIST **list, TABLE_LIST *tables) bool setup_tables(THD *thd, Name_resolution_context *context, List<TABLE_LIST> *from_clause, TABLE_LIST *tables, - Item **conds, TABLE_LIST **leaves, bool select_insert) + TABLE_LIST **leaves, bool select_insert) { uint tablenr= 0; DBUG_ENTER("setup_tables"); @@ -5305,6 +5343,7 @@ bool setup_tables(THD *thd, Name_resolution_context *context, table_list= table_list->next_leaf, tablenr++) { TABLE *table= table_list->table; + table->pos_in_table_list= table_list; if (first_select_table && table_list->top_table() == first_select_table) { @@ -5314,6 +5353,7 @@ bool setup_tables(THD *thd, Name_resolution_context *context, } setup_table_map(table, table_list, tablenr); table->used_keys= table->s->keys_for_keyread; + table->merge_keys.clear_all(); if (table_list->use_index) { key_map map; @@ -5368,6 +5408,58 @@ bool setup_tables(THD *thd, Name_resolution_context *context, /* + prepare tables and check access for the view tables + + SYNOPSIS + setup_tables_and_check_view_access() + thd Thread handler + context name resolution contest to setup table list there + from_clause Top-level list of table references in the FROM clause + tables Table list (select_lex->table_list) + conds Condition of current SELECT (can be changed by VIEW) + leaves List of join table leaves list (select_lex->leaf_tables) + refresh It is onle refresh for subquery + select_insert It is SELECT ... INSERT command + want_access what access is needed + + NOTE + a wrapper for check_tables that will also check the resulting + table leaves list for access to all the tables that belong to a view + + RETURN + FALSE ok; In this case *map will include the chosen index + TRUE error +*/ +bool setup_tables_and_check_access(THD *thd, + Name_resolution_context *context, + List<TABLE_LIST> *from_clause, + TABLE_LIST *tables, + TABLE_LIST **leaves, + bool select_insert, + ulong want_access) +{ + TABLE_LIST *leaves_tmp= NULL; + + if (setup_tables(thd, context, from_clause, tables, + &leaves_tmp, select_insert)) + return TRUE; + + *leaves= leaves_tmp; + + for (; leaves_tmp; leaves_tmp= leaves_tmp->next_leaf) + { + if (leaves_tmp->belong_to_view && + check_one_table_access(thd, want_access, leaves_tmp)) + { + tables->hide_view_error(thd); + return TRUE; + } + } + return FALSE; +} + + +/* Create a key_map from a list of index names SYNOPSIS @@ -5396,8 +5488,8 @@ bool get_key_map_from_key_list(key_map *map, TABLE *table, name->length(), 1)) <= 0) { - my_error(ER_KEY_COLUMN_DOES_NOT_EXITS, MYF(0), name->c_ptr(), - table->s->table_name.str); + my_error(ER_KEY_DOES_NOT_EXITS, MYF(0), name->c_ptr(), + table->pos_in_table_list->alias); map->set_all(); return 1; } @@ -5487,7 +5579,6 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name, } #endif - /* Update the tables used in the query based on the referenced fields. For views and natural joins this update is performed inside the loop below. @@ -5553,18 +5644,13 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name, if ((field= field_iterator.field())) { - /* - Mark if field used before in this select. - Used by 'insert' to verify if a field name is used twice. - */ - if (field->query_id == thd->query_id) - thd->dupp_field= field; - field->query_id= thd->query_id; - field->table->file->ha_set_bit_in_read_set(field->fieldnr); - + /* Mark fields as used to allow storage engine to optimze access */ + bitmap_set_bit(field->table->read_set, field->field_index); if (table) + { table->used_keys.intersect(field->part_of_key); - + table->merge_keys.merge(field->part_of_key); + } if (tables->is_natural_join) { TABLE *field_table; @@ -5581,16 +5667,13 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name, { thd->used_tables|= field_table->map; field_table->used_keys.intersect(field->part_of_key); + field_table->merge_keys.merge(field->part_of_key); field_table->used_fields++; } } } else - { thd->used_tables|= item->used_tables(); - item->walk(&Item::reset_query_id_processor, - (byte *)(&thd->query_id)); - } } /* In case of stored tables, all fields are considered as used, @@ -5599,10 +5682,7 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name, For NATURAL joins, used_tables is updated in the IF above. */ if (table) - { table->used_fields= table->s->fields; - table->file->ha_set_all_bits_in_read_set(); - } } if (found) DBUG_RETURN(FALSE); @@ -5661,8 +5741,8 @@ int setup_conds(THD *thd, TABLE_LIST *tables, TABLE_LIST *leaves, arena->is_conventional()) arena= 0; // For easier test - thd->set_query_id=1; - DBUG_PRINT("info", ("thd->set_query_id: %d", thd->set_query_id)); + thd->mark_used_columns= MARK_COLUMNS_READ; + DBUG_PRINT("info", ("thd->mark_used_columns: %d", thd->mark_used_columns)); select_lex->cond_count= 0; for (table= tables; table; table= table->next_local) @@ -5917,7 +5997,7 @@ static void mysql_rm_tmp_tables(void) if (!bcmp(file->name,tmp_file_prefix,tmp_file_prefix_length)) { - sprintf(filePath,"%s%s",tmpdir,file->name); + sprintf(filePath,"%s%c%s",tmpdir,FN_LIBCHAR,file->name); VOID(my_delete(filePath,MYF(MY_WME))); } } @@ -6003,6 +6083,7 @@ bool remove_table_from_cache(THD *thd, const char *db, const char *table_name, TABLE_SHARE *share; bool result= 0, signalled= 0; DBUG_ENTER("remove_table_from_cache"); + DBUG_PRINT("enter", ("Table: '%s.%s' flags: %u", db, table_name, flags)); key_length=(uint) (strmov(strmov(key,db)+1,table_name)-key)+1; for (;;) @@ -6029,7 +6110,10 @@ bool remove_table_from_cache(THD *thd, const char *db, const char *table_name, DBUG_PRINT("info", ("Table was in use by other thread")); in_use->some_tables_deleted=1; if (table->db_stat) + { + DBUG_PRINT("info", ("Found another active instance of the table")); result=1; + } /* Kill delayed insert threads */ if ((in_use->system_thread & SYSTEM_THREAD_DELAYED_INSERT) && ! in_use->killed) @@ -6084,6 +6168,12 @@ bool remove_table_from_cache(THD *thd, const char *db, const char *table_name, if (result && (flags & RTFC_WAIT_OTHER_THREAD_FLAG)) { + /* + Signal any thread waiting for tables to be freed to + reopen their tables + */ + (void) pthread_cond_broadcast(&COND_refresh); + DBUG_PRINT("info", ("Waiting for refresh signal")); if (!(flags & RTFC_CHECK_KILLED_FLAG) || !thd->killed) { dropping_tables++; @@ -6163,7 +6253,7 @@ int init_ftfuncs(THD *thd, SELECT_LEX *select_lex, bool no_order) alias alias for table db database table_name name of table - db_stat open flags (for example HA_OPEN_KEYFILE|HA_OPEN_RNDFILE..) + db_stat open flags (for example ->OPEN_KEYFILE|HA_OPEN_RNDFILE..) can be 0 (example in ha_example_table) prgflag READ_ALL etc.. ha_open_flags HA_OPEN_ABORT_IF_LOCKED etc.. diff --git a/sql/sql_bitmap.h b/sql/sql_bitmap.h index 35c501ede56..3a7fa4b661a 100644 --- a/sql/sql_bitmap.h +++ b/sql/sql_bitmap.h @@ -66,6 +66,7 @@ public: my_bool is_clear_all() const { return bitmap_is_clear_all(&map); } my_bool is_set_all() const { return bitmap_is_set_all(&map); } my_bool is_subset(const Bitmap& map2) const { return bitmap_is_subset(&map, &map2.map); } + my_bool is_overlapping(const Bitmap& map2) const { return bitmap_is_overlapping(&map, map2.map); } my_bool operator==(const Bitmap& map2) const { return bitmap_cmp(&map, &map2.map); } char *print(char *buf) const { @@ -132,6 +133,7 @@ public: my_bool is_clear_all() const { return map == (ulonglong)0; } my_bool is_set_all() const { return map == ~(ulonglong)0; } my_bool is_subset(const Bitmap<64>& map2) const { return !(map & ~map2.map); } + my_bool is_overlapping(const Bitmap<64>& map2) const { return (map & map2.map)!= 0; } my_bool operator==(const Bitmap<64>& map2) const { return map == map2.map; } char *print(char *buf) const { longlong2str(map,buf,16); return buf; } ulonglong to_ulonglong() const { return map; } diff --git a/sql/sql_class.cc b/sql/sql_class.cc index b4a7f9abc1c..0ede042da17 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -228,7 +228,7 @@ THD::THD() hash_clear(&handler_tables_hash); tmp_table=0; used_tables=0; - cuted_fields= sent_row_count= 0L; + cuted_fields= sent_row_count= row_count= 0L; limit_found_rows= 0; statement_id_counter= 0UL; #ifdef ERROR_INJECT_SUPPORT @@ -248,6 +248,7 @@ THD::THD() bzero(ha_data, sizeof(ha_data)); mysys_var=0; binlog_evt_union.do_union= FALSE; + enable_slow_log= 0; #ifndef DBUG_OFF dbug_sentry=THD_SENTRY_MAGIC; #endif @@ -1646,7 +1647,7 @@ Statement::Statement(enum enum_state state_arg, ulong id_arg, ulong alloc_block_size, ulong prealloc_size) :Query_arena(&main_mem_root, state_arg), id(id_arg), - set_query_id(1), + mark_used_columns(MARK_COLUMNS_READ), lex(&main_lex), query(0), query_length(0), @@ -1666,7 +1667,7 @@ Query_arena::Type Statement::type() const void Statement::set_statement(Statement *stmt) { id= stmt->id; - set_query_id= stmt->set_query_id; + mark_used_columns= stmt->mark_used_columns; lex= stmt->lex; query= stmt->query; query_length= stmt->query_length; @@ -2449,6 +2450,7 @@ field_type_name(enum_field_types type) return "Unknown"; } + my_size_t THD::max_row_length_blob(TABLE *table, const byte *data) const { my_size_t length= 0; @@ -2465,53 +2467,52 @@ my_size_t THD::max_row_length_blob(TABLE *table, const byte *data) const return length; } + my_size_t THD::pack_row(TABLE *table, MY_BITMAP const* cols, byte *row_data, const byte *record) const { - Field **p_field= table->field, *field= *p_field; + Field **p_field= table->field, *field; int n_null_bytes= table->s->null_bytes; - my_ptrdiff_t const offset= record - (byte*) table->record[0]; - + byte *ptr; + uint i; + my_ptrdiff_t const offset= (my_ptrdiff_t) (record - (byte*) + table->record[0]); memcpy(row_data, record, n_null_bytes); - byte *ptr= row_data+n_null_bytes; + ptr= row_data+n_null_bytes; - for (int i= 0 ; field ; i++, p_field++, field= *p_field) + for (i= 0 ; (field= *p_field) ; i++, p_field++) { if (bitmap_is_set(cols,i)) ptr= (byte*)field->pack((char *) ptr, field->ptr + offset); } - - /* - my_ptrdiff_t is signed, size_t is unsigned. Assert that the - conversion will work correctly. - */ - DBUG_ASSERT(ptr - row_data >= 0); - return (static_cast<size_t>(ptr - row_data)); + return (static_cast<my_size_t>(ptr - row_data)); } + int THD::binlog_write_row(TABLE* table, bool is_trans, MY_BITMAP const* cols, my_size_t colcnt, byte const *record) { DBUG_ASSERT(current_stmt_binlog_row_based && mysql_bin_log.is_open()); - /* - Pack records into format for transfer. We are allocating more - memory than needed, but that doesn't matter. + /* + Pack records into format for transfer. We are allocating more + memory than needed, but that doesn't matter. */ bool error= 0; byte *row_data= table->write_row_record; my_size_t const max_len= max_row_length(table, record); - - /* - * Allocate room for a row (if needed) - */ + my_size_t len; + Rows_log_event *ev; + + /* Allocate room for a row (if needed) */ if (!row_data) { if (!table->s->blob_fields) { /* multiply max_len by 2 so it can be used for update_row as well */ - table->write_row_record= (byte *) alloc_root(&table->mem_root, 2*max_len); + table->write_row_record= (byte *) alloc_root(&table->mem_root, + 2*max_len); if (!table->write_row_record) return HA_ERR_OUT_OF_MEM; row_data= table->write_row_record; @@ -2519,12 +2520,11 @@ int THD::binlog_write_row(TABLE* table, bool is_trans, else if (unlikely(!(row_data= (byte *) my_malloc(max_len, MYF(MY_WME))))) return HA_ERR_OUT_OF_MEM; } - my_size_t const len= pack_row(table, cols, row_data, record); + len= pack_row(table, cols, row_data, record); - Rows_log_event* const ev= - binlog_prepare_pending_rows_event(table, server_id, cols, colcnt, - len, is_trans, - static_cast<Write_rows_log_event*>(0)); + ev= binlog_prepare_pending_rows_event(table, server_id, cols, colcnt, + len, is_trans, + static_cast<Write_rows_log_event*>(0)); /* add_row_data copies row_data to internal buffer */ error= likely(ev != 0) ? ev->add_row_data(row_data,len) : HA_ERR_OUT_OF_MEM ; diff --git a/sql/sql_class.h b/sql/sql_class.h index a0971b22d3d..03a8439db58 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -37,9 +37,10 @@ enum enum_ha_read_modes { RFIRST, RNEXT, RPREV, RLAST, RKEY, RNEXT_SAME }; enum enum_duplicates { DUP_ERROR, DUP_REPLACE, DUP_UPDATE }; enum enum_delay_key_write { DELAY_KEY_WRITE_NONE, DELAY_KEY_WRITE_ON, DELAY_KEY_WRITE_ALL }; - -enum enum_check_fields { CHECK_FIELD_IGNORE, CHECK_FIELD_WARN, - CHECK_FIELD_ERROR_FOR_NULL }; +enum enum_check_fields +{ CHECK_FIELD_IGNORE, CHECK_FIELD_WARN, CHECK_FIELD_ERROR_FOR_NULL }; +enum enum_mark_columns +{ MARK_COLUMNS_NONE, MARK_COLUMNS_READ, MARK_COLUMNS_WRITE}; extern char internal_table_name[2]; extern const char **errmesg; @@ -465,17 +466,17 @@ public: ulong id; /* - - if set_query_id=1, we set field->query_id for all fields. In that case - field list can not contain duplicates. - 0: Means query_id is not set and no indicator to handler of fields used - is set - 1: Means query_id is set for fields in list and bit in read set is set - to inform handler of that field is to be read - 2: Means query is set for fields in list and bit is set in update set - to inform handler that it needs to update this field in write_row - and update_row + MARK_COLUMNS_NONE: Means mark_used_colums is not set and no indicator to + handler of fields used is set + MARK_COLUMNS_READ: Means a bit in read set is set to inform handler + that the field is to be read. If field list contains + duplicates, then thd->dup_field is set to point + to the last found duplicate. + MARK_COLUMNS_WRITE: Means a bit is set in write set to inform handler + that it needs to update this field in write_row + and update_row. */ - ulong set_query_id; + enum enum_mark_columns mark_used_columns; LEX_STRING name; /* name for named prepared statements */ LEX *lex; // parse tree descriptor @@ -1027,7 +1028,7 @@ public: #endif } } transaction; - Field *dupp_field; + Field *dup_field; #ifndef __WIN__ sigset_t signals,block_signals; #endif @@ -1408,7 +1409,8 @@ public: } inline void reset_current_stmt_binlog_row_based() { - current_stmt_binlog_row_based= test(variables.binlog_format == BINLOG_FORMAT_ROW); + current_stmt_binlog_row_based= test(variables.binlog_format == + BINLOG_FORMAT_ROW); } #endif /*HAVE_ROW_BASED_REPLICATION*/ }; @@ -1570,6 +1572,7 @@ class select_insert :public select_result_interceptor { int prepare2(void); bool send_data(List<Item> &items); virtual void store_values(List<Item> &values); + virtual bool can_rollback_data() { return 0; } void send_error(uint errcode,const char *err); bool send_eof(); /* not implemented: select_insert is never re-used in prepared statements */ @@ -1591,17 +1594,19 @@ public: List<create_field> &fields_par, List<Key> &keys_par, List<Item> &select_fields,enum_duplicates duplic, bool ignore) - :select_insert (NULL, NULL, &select_fields, 0, 0, duplic, ignore), create_table(table), - extra_fields(&fields_par),keys(&keys_par), create_info(create_info_par), + :select_insert (NULL, NULL, &select_fields, 0, 0, duplic, ignore), + create_table(table), extra_fields(&fields_par),keys(&keys_par), + create_info(create_info_par), lock(0) {} int prepare(List<Item> &list, SELECT_LEX_UNIT *u); - void binlog_show_create_table(TABLE **tables, uint count); void store_values(List<Item> &values); void send_error(uint errcode,const char *err); bool send_eof(); void abort(); + virtual bool can_rollback_data() { return 1; } + // Needed for access from local class MY_HOOKS in prepare(), since thd is proteted. THD *get_thd(void) { return thd; } }; diff --git a/sql/sql_db.cc b/sql/sql_db.cc index 6d5362c2554..11c892fee44 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -538,16 +538,27 @@ bool mysql_create_db(THD *thd, char *db, HA_CREATE_INFO *create_info, my_error(ER_DB_CREATE_EXISTS, MYF(0), db); DBUG_RETURN(-1); } - - VOID(pthread_mutex_lock(&LOCK_mysql_create_db)); - /* do not create database if another thread is holding read lock */ + /* + Do not create database if another thread is holding read lock. + Wait for global read lock before acquiring LOCK_mysql_create_db. + After wait_if_global_read_lock() we have protection against another + global read lock. If we would acquire LOCK_mysql_create_db first, + another thread could step in and get the global read lock before we + reach wait_if_global_read_lock(). If this thread tries the same as we + (admin a db), it would then go and wait on LOCK_mysql_create_db... + Furthermore wait_if_global_read_lock() checks if the current thread + has the global read lock and refuses the operation with + ER_CANT_UPDATE_WITH_READLOCK if applicable. + */ if (wait_if_global_read_lock(thd, 0, 1)) { error= -1; goto exit2; } + VOID(pthread_mutex_lock(&LOCK_mysql_create_db)); + /* Check directory */ path_len= build_table_filename(path, sizeof(path), db, "", ""); path[path_len-1]= 0; // Remove last '/' from path @@ -655,9 +666,9 @@ bool mysql_create_db(THD *thd, char *db, HA_CREATE_INFO *create_info, } exit: + VOID(pthread_mutex_unlock(&LOCK_mysql_create_db)); start_waiting_global_read_lock(thd); exit2: - VOID(pthread_mutex_unlock(&LOCK_mysql_create_db)); DBUG_RETURN(error); } @@ -671,12 +682,23 @@ bool mysql_alter_db(THD *thd, const char *db, HA_CREATE_INFO *create_info) int error= 0; DBUG_ENTER("mysql_alter_db"); - VOID(pthread_mutex_lock(&LOCK_mysql_create_db)); - - /* do not alter database if another thread is holding read lock */ + /* + Do not alter database if another thread is holding read lock. + Wait for global read lock before acquiring LOCK_mysql_create_db. + After wait_if_global_read_lock() we have protection against another + global read lock. If we would acquire LOCK_mysql_create_db first, + another thread could step in and get the global read lock before we + reach wait_if_global_read_lock(). If this thread tries the same as we + (admin a db), it would then go and wait on LOCK_mysql_create_db... + Furthermore wait_if_global_read_lock() checks if the current thread + has the global read lock and refuses the operation with + ER_CANT_UPDATE_WITH_READLOCK if applicable. + */ if ((error=wait_if_global_read_lock(thd,0,1))) goto exit2; + VOID(pthread_mutex_lock(&LOCK_mysql_create_db)); + /* Recreate db options file: /dbpath/.db.opt We pass MY_DB_OPT_FILE as "extension" to avoid @@ -721,9 +743,9 @@ bool mysql_alter_db(THD *thd, const char *db, HA_CREATE_INFO *create_info) send_ok(thd, result); exit: + VOID(pthread_mutex_unlock(&LOCK_mysql_create_db)); start_waiting_global_read_lock(thd); exit2: - VOID(pthread_mutex_unlock(&LOCK_mysql_create_db)); DBUG_RETURN(error); } @@ -755,15 +777,26 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent) TABLE_LIST* dropped_tables= 0; DBUG_ENTER("mysql_rm_db"); - VOID(pthread_mutex_lock(&LOCK_mysql_create_db)); - - /* do not drop database if another thread is holding read lock */ + /* + Do not drop database if another thread is holding read lock. + Wait for global read lock before acquiring LOCK_mysql_create_db. + After wait_if_global_read_lock() we have protection against another + global read lock. If we would acquire LOCK_mysql_create_db first, + another thread could step in and get the global read lock before we + reach wait_if_global_read_lock(). If this thread tries the same as we + (admin a db), it would then go and wait on LOCK_mysql_create_db... + Furthermore wait_if_global_read_lock() checks if the current thread + has the global read lock and refuses the operation with + ER_CANT_UPDATE_WITH_READLOCK if applicable. + */ if (wait_if_global_read_lock(thd, 0, 1)) { error= -1; goto exit2; } + VOID(pthread_mutex_lock(&LOCK_mysql_create_db)); + length= build_table_filename(path, sizeof(path), db, "", ""); strmov(path+length, MY_DB_OPT_FILE); // Append db option file name del_dbopt(path); // Remove dboption hash entry @@ -872,7 +905,6 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent) exit: (void)sp_drop_db_routines(thd, db); /* QQ Ignore errors for now */ error= Events::drop_schema_events(thd, db); - start_waiting_global_read_lock(thd); /* If this database was the client's selected database, we silently change the client's selected database to nothing (to have an empty SELECT DATABASE() @@ -901,9 +933,9 @@ exit: thd->db= 0; thd->db_length= 0; } -exit2: VOID(pthread_mutex_unlock(&LOCK_mysql_create_db)); - + start_waiting_global_read_lock(thd); +exit2: DBUG_RETURN(error); } diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 44b0fe1a2f1..dd4748bc15c 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -80,9 +80,9 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, !(specialflag & (SPECIAL_NO_NEW_FUNC | SPECIAL_SAFE_MODE)) && !(table->triggers && table->triggers->has_delete_triggers())) { - /* Update the table->file->records number */ + /* Update the table->file->stats.records number */ table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK); - ha_rows const maybe_deleted= table->file->records; + ha_rows const maybe_deleted= table->file->stats.records; /* If all rows shall be deleted, we (almost) always log this statement-based (see [binlog], below), so we set this flag and @@ -113,7 +113,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, DBUG_RETURN(0); } #endif - /* Update the table->file->records number */ + /* Update the table->file->stats.records number */ table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK); table->used_keys.clear_all(); @@ -184,7 +184,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, if (!(sortorder= make_unireg_sortorder((ORDER*) order->first, &length)) || (table->sort.found_records = filesort(thd, table, sortorder, length, - select, HA_POS_ERROR, + select, HA_POS_ERROR, 1, &examined_rows)) == HA_POS_ERROR) { @@ -226,6 +226,8 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, if (ha_delete_all_rows) thd->options&= ~static_cast<ulonglong>(OPTION_BIN_LOG); + table->mark_columns_needed_for_delete(); + while (!(error=info.read_record(&info)) && !thd->killed && !thd->net.report_error) { @@ -285,7 +287,6 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, } thd->proc_info= "end"; end_read_record(&info); - free_io_cache(table); // Will not do any harm if (options & OPTION_QUICK) (void) table->file->extra(HA_EXTRA_NORMAL); @@ -394,10 +395,11 @@ bool mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds) DBUG_ENTER("mysql_prepare_delete"); thd->lex->allow_sum_func= 0; - if (setup_tables(thd, &thd->lex->select_lex.context, - &thd->lex->select_lex.top_join_list, - table_list, conds, &select_lex->leaf_tables, - FALSE) || + if (setup_tables_and_check_access(thd, &thd->lex->select_lex.context, + &thd->lex->select_lex.top_join_list, + table_list, + &select_lex->leaf_tables, FALSE, + DELETE_ACL) || setup_conds(thd, table_list, select_lex->leaf_tables, conds) || setup_ftfuncs(select_lex)) DBUG_RETURN(TRUE); @@ -456,10 +458,11 @@ bool mysql_multi_delete_prepare(THD *thd) lex->query_tables also point on local list of DELETE SELECT_LEX */ - if (setup_tables(thd, &thd->lex->select_lex.context, - &thd->lex->select_lex.top_join_list, - lex->query_tables, &lex->select_lex.where, - &lex->select_lex.leaf_tables, FALSE)) + if (setup_tables_and_check_access(thd, &thd->lex->select_lex.context, + &thd->lex->select_lex.top_join_list, + lex->query_tables, + &lex->select_lex.leaf_tables, FALSE, + DELETE_ACL)) DBUG_RETURN(TRUE); @@ -565,6 +568,8 @@ multi_delete::initialize_tables(JOIN *join) transactional_tables= 1; else normal_tables= 1; + tbl->prepare_for_position(); + tbl->mark_columns_needed_for_delete(); } else if ((tab->type != JT_SYSTEM && tab->type != JT_CONST) && walk == delete_tables) @@ -604,7 +609,6 @@ multi_delete::~multi_delete() table_being_deleted= table_being_deleted->next_local) { TABLE *table= table_being_deleted->table; - free_io_cache(table); // Alloced by unique table->no_keyread=0; } @@ -950,8 +954,10 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) // crashes, replacement works. *(path + path_length - reg_ext_length)= // '\0'; path[path_length - reg_ext_length] = 0; + VOID(pthread_mutex_lock(&LOCK_open)); error= ha_create_table(thd, path, table_list->db, table_list->table_name, &create_info, 1); + VOID(pthread_mutex_unlock(&LOCK_open)); query_cache_invalidate3(thd, table_list, 0); end: diff --git a/sql/sql_do.cc b/sql/sql_do.cc index 08388dee516..98483ce2de6 100644 --- a/sql/sql_do.cc +++ b/sql/sql_do.cc @@ -24,7 +24,7 @@ bool mysql_do(THD *thd, List<Item> &values) List_iterator<Item> li(values); Item *value; DBUG_ENTER("mysql_do"); - if (setup_fields(thd, 0, values, 0, 0, 0)) + if (setup_fields(thd, 0, values, MARK_COLUMNS_NONE, 0, 0)) DBUG_RETURN(TRUE); while ((value = li++)) value->val_int(); diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc index 18b63ba49a3..bf035401bea 100644 --- a/sql/sql_handler.cc +++ b/sql/sql_handler.cc @@ -188,13 +188,13 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen) /* for now HANDLER can be used only for real TABLES */ tables->required_type= FRMTYPE_TABLE; error= open_tables(thd, &tables, &counter, 0); - HANDLER_TABLES_HACK(thd); + if (error) goto err; /* There can be only one table in '*tables'. */ - if (! (tables->table->file->table_flags() & HA_CAN_SQL_HANDLER)) + if (! (tables->table->file->ha_table_flags() & HA_CAN_SQL_HANDLER)) { if (! reopen) my_error(ER_ILLEGAL_HA, MYF(0), tables->alias); @@ -421,6 +421,9 @@ bool mysql_ha_read(THD *thd, TABLE_LIST *tables, if (!lock) goto err0; // mysql_lock_tables() printed error message already + // Always read all columns + tables->table->read_set= &tables->table->s->all_set; + if (cond) { if (table->query_id != thd->query_id) @@ -514,6 +517,7 @@ bool mysql_ha_read(THD *thd, TABLE_LIST *tables, Item *item; for (key_len=0 ; (item=it_ke++) ; key_part++) { + my_bitmap_map *old_map; // 'item' can be changed by fix_fields() call if ((!item->fixed && item->fix_fields(thd, it_ke.ref())) || @@ -524,16 +528,19 @@ bool mysql_ha_read(THD *thd, TABLE_LIST *tables, my_error(ER_WRONG_ARGUMENTS,MYF(0),"HANDLER ... READ"); goto err; } + old_map= dbug_tmp_use_all_columns(table, table->write_set); (void) item->save_in_field(key_part->field, 1); + dbug_tmp_restore_column_map(table->write_set, old_map); key_len+=key_part->store_length; } + if (!(key= (byte*) thd->calloc(ALIGN_SIZE(key_len)))) goto err; table->file->ha_index_or_rnd_end(); table->file->ha_index_init(keyno, 1); key_copy(key, table->record[0], table->key_info + keyno, key_len); error= table->file->index_read(table->record[0], - key,key_len,ha_rkey_mode); + key,key_len,ha_rkey_mode); mode=rkey_to_rnext[(int)ha_rkey_mode]; break; } diff --git a/sql/sql_help.cc b/sql/sql_help.cc index ea9bca57cc6..69d21f8b7bb 100644 --- a/sql/sql_help.cc +++ b/sql/sql_help.cc @@ -94,6 +94,11 @@ static bool init_fields(THD *thd, TABLE_LIST *tables, 0, REPORT_ALL_ERRORS, 1, TRUE))) DBUG_RETURN(1); + bitmap_set_bit(find_fields->field->table->read_set, + find_fields->field->field_index); + /* To make life easier when setting values in keys */ + bitmap_set_bit(find_fields->field->table->write_set, + find_fields->field->field_index); } DBUG_RETURN(0); } @@ -272,7 +277,6 @@ int get_topics_for_keyword(THD *thd, TABLE *topics, TABLE *relations, int count= 0; int iindex_topic, iindex_relations; Field *rtopic_id, *rkey_id; - DBUG_ENTER("get_topics_for_keyword"); if ((iindex_topic= find_type((char*) primary_key_name, @@ -292,8 +296,9 @@ int get_topics_for_keyword(THD *thd, TABLE *topics, TABLE *relations, rkey_id->store((longlong) key_id, TRUE); rkey_id->get_key_image(buff, rkey_id->pack_length(), Field::itRAW); int key_res= relations->file->index_read(relations->record[0], - (byte *)buff, rkey_id->pack_length(), - HA_READ_KEY_EXACT); + (byte *) buff, + rkey_id->pack_length(), + HA_READ_KEY_EXACT); for ( ; !key_res && key_id == (int16) rkey_id->val_int() ; @@ -653,13 +658,15 @@ bool mysqld_help(THD *thd, const char *mask) if (open_and_lock_tables(thd, tables)) goto error; + /* Init tables and fields to be usable from items tables do not contain VIEWs => we can pass 0 as conds */ - setup_tables(thd, &thd->lex->select_lex.context, - &thd->lex->select_lex.top_join_list, - tables, 0, &leaves, FALSE); + if (setup_tables(thd, &thd->lex->select_lex.context, + &thd->lex->select_lex.top_join_list, + tables, &leaves, FALSE)) + goto error; memcpy((char*) used_fields, (char*) init_used_fields, sizeof(used_fields)); if (init_fields(thd, tables, used_fields, array_elements(used_fields))) goto error; @@ -681,10 +688,12 @@ bool mysqld_help(THD *thd, const char *mask) int key_id; if (!(select= prepare_select_for_name(thd,mask,mlen,tables,tables[3].table, - used_fields[help_keyword_name].field,&error))) + used_fields[help_keyword_name].field, + &error))) goto error; - count_topics=search_keyword(thd,tables[3].table,used_fields,select,&key_id); + count_topics= search_keyword(thd,tables[3].table, used_fields, select, + &key_id); delete select; count_topics= (count_topics != 1) ? 0 : get_topics_for_keyword(thd,tables[0].table,tables[2].table, @@ -698,7 +707,8 @@ bool mysqld_help(THD *thd, const char *mask) Field *cat_cat_id= used_fields[help_category_parent_category_id].field; if (!(select= prepare_select_for_name(thd,mask,mlen,tables,tables[1].table, - used_fields[help_category_name].field,&error))) + used_fields[help_category_name].field, + &error))) goto error; count_categories= search_categories(thd, tables[1].table, used_fields, diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 84760e93d8e..0dae2b8f37b 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -108,7 +108,7 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list, No fields are provided so all fields must be provided in the values. Thus we set all bits in the write set. */ - table->file->ha_set_all_bits_in_write_set(); + bitmap_set_all(table->write_set); } else { // Part field list @@ -123,7 +123,7 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list, return -1; } - thd->dupp_field=0; + thd->dup_field= 0; select_lex->no_wrap_view_item= TRUE; /* Save the state of the current name resolution context. */ @@ -135,11 +135,7 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list, */ table_list->next_local= 0; context->resolve_in_table_list_only(table_list); - /* - Indicate fields in list is to be updated by setting set_query_id - parameter to 2. This sets the bit in the write_set for each field. - */ - res= setup_fields(thd, 0, fields, 2, 0, 0); + res= setup_fields(thd, 0, fields, MARK_COLUMNS_WRITE, 0, 0); /* Restore the current context. */ ctx_state.restore_state(context, table_list); @@ -167,16 +163,27 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list, table_list->table= table= tbl->table; } - if (check_unique && thd->dupp_field) + if (check_unique && thd->dup_field) { - my_error(ER_FIELD_SPECIFIED_TWICE, MYF(0), thd->dupp_field->field_name); + my_error(ER_FIELD_SPECIFIED_TWICE, MYF(0), thd->dup_field->field_name); return -1; } - if (table->timestamp_field && // Don't set timestamp if used - table->timestamp_field->query_id == thd->query_id) - clear_timestamp_auto_bits(table->timestamp_field_type, - TIMESTAMP_AUTO_SET_ON_INSERT); + if (table->timestamp_field) // Don't automaticly set timestamp if used + { + if (bitmap_is_set(table->write_set, + table->timestamp_field->field_index)) + clear_timestamp_auto_bits(table->timestamp_field_type, + TIMESTAMP_AUTO_SET_ON_INSERT); + else + { + bitmap_set_bit(table->write_set, + table->timestamp_field->field_index); + } + } } + if (table->found_next_number_field) + table->mark_auto_increment_column(); + table->mark_columns_needed_for_insert(); // For the values we need select_priv #ifndef NO_EMBEDDED_ACCESS_CHECKS table->grant.want_privilege= (SELECT_ACL & ~table->grant.privilege); @@ -217,40 +224,33 @@ static int check_update_fields(THD *thd, TABLE_LIST *insert_table_list, List<Item> &update_fields) { TABLE *table= insert_table_list->table; - query_id_t timestamp_query_id; - LINT_INIT(timestamp_query_id); + my_bool timestamp_mark; - /* - Change the query_id for the timestamp column so that we can - check if this is modified directly. - */ if (table->timestamp_field) { - timestamp_query_id= table->timestamp_field->query_id; - table->timestamp_field->query_id= thd->query_id - 1; + /* + Unmark the timestamp field so that we can check if this is modified + by update_fields + */ + timestamp_mark= bitmap_test_and_clear(table->write_set, + table->timestamp_field->field_index); } - /* - Check the fields we are going to modify. This will set the query_id - of all used fields to the threads query_id. It will also set all - fields into the write set of this table. - */ - if (setup_fields(thd, 0, update_fields, 2, 0, 0)) + /* Check the fields we are going to modify */ + if (setup_fields(thd, 0, update_fields, MARK_COLUMNS_WRITE, 0, 0)) return -1; if (table->timestamp_field) { /* Don't set timestamp column if this is modified. */ - if (table->timestamp_field->query_id == thd->query_id) + if (bitmap_is_set(table->write_set, + table->timestamp_field->field_index)) clear_timestamp_auto_bits(table->timestamp_field_type, TIMESTAMP_AUTO_SET_ON_UPDATE); - else - { - table->timestamp_field->query_id= timestamp_query_id; - table->file->ha_set_bit_in_write_set(table->timestamp_field->fieldnr); - } + if (timestamp_mark) + bitmap_set_bit(table->write_set, + table->timestamp_field->field_index); } - return 0; } @@ -269,8 +269,8 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, By default, both logs are enabled (this won't cause problems if the server runs without --log-update or --log-bin). */ - bool log_on= (thd->options & OPTION_BIN_LOG) || - (!(thd->security_ctx->master_access & SUPER_ACL)); + bool log_on= ((thd->options & OPTION_BIN_LOG) || + (!(thd->security_ctx->master_access & SUPER_ACL))); bool transactional_table, joins_freed= FALSE; bool changed; uint value_count; @@ -380,7 +380,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, my_error(ER_WRONG_VALUE_COUNT_ON_ROW, MYF(0), counter); goto abort; } - if (setup_fields(thd, 0, *values, 0, 0, 0)) + if (setup_fields(thd, 0, *values, MARK_COLUMNS_READ, 0, 0)) goto abort; } its.rewind (); @@ -428,7 +428,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, the code to make the call of end_bulk_insert() below safe. */ if (lock_type != TL_WRITE_DELAYED && !thd->prelocked_mode) - table->file->start_bulk_insert(values_list.elements); + table->file->ha_start_bulk_insert(values_list.elements); thd->no_trans_update= 0; thd->abort_on_warning= (!ignore && @@ -553,7 +553,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, else #endif { - if (!thd->prelocked_mode && table->file->end_bulk_insert() && !error) + if (!thd->prelocked_mode && table->file->ha_end_bulk_insert() && !error) { table->file->print_error(my_errno,MYF(0)); error=1; @@ -644,6 +644,8 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, thd->row_count_func= info.copied+info.deleted+info.updated; ::send_ok(thd, (ulong) thd->row_count_func, id, buff); } + if (table != NULL) + table->file->release_auto_increment(); thd->abort_on_warning= 0; DBUG_RETURN(FALSE); @@ -652,6 +654,8 @@ abort: if (lock_type == TL_WRITE_DELAYED) end_delayed_insert(thd); #endif + if (table != NULL) + table->file->release_auto_increment(); if (!joins_freed) free_underlaid_joins(thd, &thd->lex->select_lex); thd->abort_on_warning= 0; @@ -753,16 +757,17 @@ static bool check_view_insertability(THD * thd, TABLE_LIST *view) */ static bool mysql_prepare_insert_check_table(THD *thd, TABLE_LIST *table_list, - List<Item> &fields, COND **where, + List<Item> &fields, bool select_insert) { bool insert_into_view= (table_list->view != 0); DBUG_ENTER("mysql_prepare_insert_check_table"); - if (setup_tables(thd, &thd->lex->select_lex.context, - &thd->lex->select_lex.top_join_list, - table_list, where, &thd->lex->select_lex.leaf_tables, - select_insert)) + if (setup_tables_and_check_access(thd, &thd->lex->select_lex.context, + &thd->lex->select_lex.top_join_list, + table_list, + &thd->lex->select_lex.leaf_tables, + select_insert, INSERT_ACL)) DBUG_RETURN(TRUE); if (insert_into_view && !fields.elements) @@ -851,8 +856,7 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, DBUG_RETURN(TRUE); } - if (mysql_prepare_insert_check_table(thd, table_list, fields, where, - select_insert)) + if (mysql_prepare_insert_check_table(thd, table_list, fields, select_insert)) DBUG_RETURN(TRUE); /* Save the state of the current name resolution context. */ @@ -869,7 +873,7 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, if (values && !(res= check_insert_fields(thd, context->table_list, fields, *values, !insert_into_view) || - setup_fields(thd, 0, *values, 0, 0, 0)) && + setup_fields(thd, 0, *values, MARK_COLUMNS_READ, 0, 0)) && duplic == DUP_UPDATE) { select_lex->no_wrap_view_item= TRUE; @@ -887,7 +891,7 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, next_name_resolution_table= ctx_state.save_next_local; } if (!res) - res= setup_fields(thd, 0, update_values, 1, 0, 0); + res= setup_fields(thd, 0, update_values, MARK_COLUMNS_READ, 0, 0); } /* Restore the current context. */ @@ -912,7 +916,7 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, select_lex->first_execution= 0; } if (duplic == DUP_UPDATE || duplic == DUP_REPLACE) - table->file->ha_retrieve_all_pk(); + table->prepare_for_position(); DBUG_RETURN(FALSE); } @@ -959,9 +963,12 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) { int error, trg_error= 0; char *key=0; + MY_BITMAP *save_read_set, *save_write_set; DBUG_ENTER("write_record"); info->records++; + save_read_set= table->read_set; + save_write_set= table->write_set; if (info->handle_duplicates == DUP_REPLACE || info->handle_duplicates == DUP_UPDATE) @@ -971,12 +978,14 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) uint key_nr; if (error != HA_WRITE_SKIP) goto err; - table->file->restore_auto_increment(); + table->file->restore_auto_increment(); // it's too early here! BUG#20188 if ((int) (key_nr = table->file->get_dup_key(error)) < 0) { error=HA_WRITE_SKIP; /* Database can't find key */ goto err; } + /* Read all columns for the row we are going to replace */ + table->use_all_columns(); /* Don't allow REPLACE to replace a row when a auto_increment column was used. This ensures that we don't get a problem when the @@ -987,9 +996,9 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) key_nr == table->s->next_number_index && table->file->auto_increment_column_changed) goto err; - if (table->file->table_flags() & HA_DUPP_POS) + if (table->file->ha_table_flags() & HA_DUPLICATE_POS) { - if (table->file->rnd_pos(table->record[1],table->file->dupp_ref)) + if (table->file->rnd_pos(table->record[1],table->file->dup_ref)) goto err; } else @@ -1050,7 +1059,8 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) thd->clear_next_insert_id= 0; thd->next_insert_id= 0; } - if ((error=table->file->ha_update_row(table->record[1],table->record[0]))) + if ((error=table->file->ha_update_row(table->record[1], + table->record[0]))) { if ((error == HA_ERR_FOUND_DUPP_KEY) && info->ignore) goto ok_or_after_trg_err; @@ -1127,6 +1137,13 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) trg_error= (table->triggers && table->triggers->process_triggers(thd, TRG_EVENT_INSERT, TRG_ACTION_AFTER, TRUE)); + /* + Restore column maps if they where replaced during an duplicate key + problem. + */ + if (table->read_set != save_read_set || + table->write_set != save_write_set) + table->column_bitmaps_set(save_read_set, save_write_set); } else if ((error=table->file->ha_write_row(table->record[0]))) { @@ -1160,6 +1177,7 @@ err: before_trg_err: if (key) my_safe_afree(key, table->s->max_unique_length, MAX_KEY_LENGTH); + table->column_bitmaps_set(save_read_set, save_write_set); DBUG_RETURN(1); } @@ -1172,9 +1190,11 @@ int check_that_all_fields_are_given_values(THD *thd, TABLE *entry, TABLE_LIST *table_list) { int err= 0; + MY_BITMAP *write_set= entry->write_set; + for (Field **field=entry->field ; *field ; field++) { - if ((*field)->query_id != thd->query_id && + if (!bitmap_is_set(write_set, (*field)->field_index) && ((*field)->flags & NO_DEFAULT_VALUE_FLAG) && ((*field)->real_type() != FIELD_TYPE_ENUM)) { @@ -1506,6 +1526,7 @@ TABLE *delayed_insert::get_local_table(THD* client_thd) Field **field,**org_field, *found_next_number_field; TABLE *copy; TABLE_SHARE *share= table->s; + byte *bitmap; /* First request insert thread to get a lock */ status=1; @@ -1532,14 +1553,16 @@ TABLE *delayed_insert::get_local_table(THD* client_thd) client_thd->proc_info="allocating local table"; copy= (TABLE*) client_thd->alloc(sizeof(*copy)+ (share->fields+1)*sizeof(Field**)+ - share->reclength); + share->reclength + + share->column_bitmap_size*2); if (!copy) goto error; *copy= *table; /* We don't need to change the file handler here */ - field=copy->field=(Field**) (copy+1); - copy->record[0]=(byte*) (field+share->fields+1); + field= copy->field= (Field**) (copy+1); + bitmap= (byte*) (field+share->fields+1); + copy->record[0]= (bitmap+ share->column_bitmap_size*2); memcpy((char*) copy->record[0],(char*) table->record[0],share->reclength); /* Make a copy of all fields */ @@ -1568,13 +1591,21 @@ TABLE *delayed_insert::get_local_table(THD* client_thd) copy->timestamp_field_type= copy->timestamp_field->get_auto_set_type(); } - /* Adjust in_use for pointing to client thread */ copy->in_use= client_thd; /* Adjust lock_count. This table object is not part of a lock. */ copy->lock_count= 0; + /* Adjust bitmaps */ + copy->def_read_set.bitmap= (my_bitmap_map*) bitmap; + copy->def_write_set.bitmap= ((my_bitmap_map*) + (bitmap + share->column_bitmap_size)); + copy->tmp_set.bitmap= 0; // To catch errors + bzero((char*) bitmap, share->column_bitmap_size*2); + copy->read_set= ©->def_read_set; + copy->write_set= ©->def_write_set; + return copy; /* Got fatal error */ @@ -1742,7 +1773,7 @@ pthread_handler_t handle_delayed_insert(void *arg) thd->fatal_error(); // Abort waiting inserts goto err; } - if (!(di->table->file->table_flags() & HA_CAN_INSERT_DELAYED)) + if (!(di->table->file->ha_table_flags() & HA_CAN_INSERT_DELAYED)) { thd->fatal_error(); my_error(ER_ILLEGAL_HA, MYF(0), di->table_list.table_name); @@ -1952,6 +1983,7 @@ bool delayed_insert::handle_inserts(void) pthread_mutex_unlock(&mutex); table->next_number_field=table->found_next_number_field; + table->use_all_columns(); thd.proc_info="upgrading lock"; if (thr_upgrade_write_delay_lock(*thd.lock->locks)) @@ -2058,7 +2090,6 @@ bool delayed_insert::handle_inserts(void) } thd.proc_info=0; - table->next_number_field=0; pthread_mutex_unlock(&mutex); /* After releasing the mutex, to prevent deadlocks. */ @@ -2181,7 +2212,7 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u) lex->current_select= &lex->select_lex; res= check_insert_fields(thd, table_list, *fields, values, !insert_into_view) || - setup_fields(thd, 0, values, 0, 0, 0); + setup_fields(thd, 0, values, MARK_COLUMNS_READ, 0, 0); if (info.handle_duplicates == DUP_UPDATE) { @@ -2211,7 +2242,8 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u) context->first_name_resolution_table-> next_name_resolution_table= ctx_state.save_next_local; } - res= res || setup_fields(thd, 0, *info.update_values, 1, 0, 0); + res= res || setup_fields(thd, 0, *info.update_values, MARK_COLUMNS_READ, + 0, 0); /* Restore the current context. */ ctx_state.restore_state(context, table_list); @@ -2248,7 +2280,7 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u) We won't start bulk inserts at all if this statement uses functions or should invoke triggers since they may access to the same table too. */ - table->file->start_bulk_insert((ha_rows) 0); + table->file->ha_start_bulk_insert((ha_rows) 0); } restore_record(table,s->default_values); // Get empty record table->next_number_field=table->found_next_number_field; @@ -2264,16 +2296,6 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u) check_that_all_fields_are_given_values(thd, table, table_list)) || table_list->prepare_where(thd, 0, TRUE) || table_list->prepare_check_option(thd)); - - /* - For non-transactional non-temporary tables, we set the - OPTION_STATUS_NO_TRANS_UPDATE flag here. The send_eof() function - is used by both the select_insert and the select_create classes, - so setting it there would clash. - */ - if (!(table->file->has_transactions() || table->s->tmp_table)) - thd->options|=OPTION_STATUS_NO_TRANS_UPDATE; - DBUG_RETURN(res); } @@ -2299,7 +2321,7 @@ int select_insert::prepare2(void) DBUG_ENTER("select_insert::prepare2"); if (thd->lex->current_select->options & OPTION_BUFFER_RESULT && !thd->prelocked_mode) - table->file->start_bulk_insert((ha_rows) 0); + table->file->ha_start_bulk_insert((ha_rows) 0); DBUG_RETURN(0); } @@ -2373,6 +2395,7 @@ bool select_insert::send_data(List<Item> &values) last_insert_id= thd->insert_id(); } } + table->file->release_auto_increment(); DBUG_RETURN(error); } @@ -2391,7 +2414,9 @@ void select_insert::send_error(uint errcode,const char *err) { DBUG_ENTER("select_insert::send_error"); - my_message(errcode, err, MYF(0)); + /* Avoid an extra 'unknown error' message if we already reported an error */ + if (errcode != ER_UNKNOWN_ERROR && !thd->net.report_error) + my_message(errcode, err, MYF(0)); if (!table) { @@ -2402,7 +2427,7 @@ void select_insert::send_error(uint errcode,const char *err) DBUG_VOID_RETURN; } if (!thd->prelocked_mode) - table->file->end_bulk_insert(); + table->file->ha_end_bulk_insert(); /* If at least one row has been inserted/modified and will stay in the table (the table doesn't have transactions) we must write to the binlog (and @@ -2419,11 +2444,8 @@ void select_insert::send_error(uint errcode,const char *err) INSERT-SELECT. When replicating a CREATE-SELECT statement, we shall not write the - events to the binary log. To prevent the ha_rollback_stmt() below - from writing to the binary log, we have to pretend that the table - is transactional, even if it actually is not. Therefore, the - OPTION_STATUS_NO_TRANS_UPDATE is cleared in - select_create::prepare() and will remain cleared here. + events to the binary log and should thus not set + OPTION_STATUS_NO_TRANS_UPDATE. When replicating INSERT-SELECT, we shall not write the events to the binary log for transactional table, but shall write all events @@ -2431,22 +2453,22 @@ void select_insert::send_error(uint errcode,const char *err) this case, the OPTION_STATUS_NO_TRANS_UPDATE is set if there is a write to a non-transactional table, otherwise it is cleared. */ - if ((info.copied || info.deleted || info.updated) && - !table->file->has_transactions()) + if (info.copied || info.deleted || info.updated) { - if (last_insert_id) - thd->insert_id(last_insert_id); // For binary log - if (mysql_bin_log.is_open()) + if (!table->file->has_transactions()) { - thd->binlog_query(THD::ROW_QUERY_TYPE, thd->query, thd->query_length, - table->file->has_transactions(), FALSE); + if (last_insert_id) + thd->insert_id(last_insert_id); // For binary log + if (mysql_bin_log.is_open()) + { + thd->binlog_query(THD::ROW_QUERY_TYPE, thd->query, thd->query_length, + table->file->has_transactions(), FALSE); + } + if (!thd->current_stmt_binlog_row_based && !table->s->tmp_table && + !can_rollback_data()) + thd->options|= OPTION_STATUS_NO_TRANS_UPDATE; + query_cache_invalidate3(thd, table, 1); } - if (!thd->current_stmt_binlog_row_based && !table->s->tmp_table) - thd->options|=OPTION_STATUS_NO_TRANS_UPDATE; - } - if (info.copied || info.deleted || info.updated) - { - query_cache_invalidate3(thd, table, 1); } ha_rollback_stmt(thd); DBUG_VOID_RETURN; @@ -2458,25 +2480,28 @@ bool select_insert::send_eof() int error,error2; DBUG_ENTER("select_insert::send_eof"); - error= (!thd->prelocked_mode) ? table->file->end_bulk_insert():0; + error= (!thd->prelocked_mode) ? table->file->ha_end_bulk_insert():0; table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY); - /* - We must invalidate the table in the query cache before binlog writing - and ha_autocommit_or_rollback. - - If nothing was inserted in the table, there is no need to emit a - ROLLBACK statement to the binary log, so in that case we clear - OPTION_STATUS_NO_TRANS_UPDATE. - - Observe that select_insert::send_eof() is used by both - select_insert and select_create and that they set the flag in - different manners. See Note 1 below for more info. - */ if (info.copied || info.deleted || info.updated) + { + /* + We must invalidate the table in the query cache before binlog writing + and ha_autocommit_or_rollback. + */ query_cache_invalidate3(thd, table, 1); - else - thd->options&= ~OPTION_STATUS_NO_TRANS_UPDATE; + /* + Mark that we have done permanent changes if all of the below is true + - Table doesn't support transactions + - It's a normal (not temporary) table. (Changes to temporary tables + are not logged in RBR) + - We are using statement based replication + */ + if (!table->file->has_transactions() && + (!table->s->tmp_table || + !thd->current_stmt_binlog_row_based)) + thd->options|= OPTION_STATUS_NO_TRANS_UPDATE; + } if (last_insert_id) thd->insert_id(last_insert_id); // For binary log @@ -2588,12 +2613,13 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, while ((item=it++)) { create_field *cr_field; - Field *field; + Field *field, *def_field; if (item->type() == Item::FUNC_ITEM) - field=item->tmp_table_field(&tmp_table); + field= item->tmp_table_field(&tmp_table); else - field=create_tmp_field(thd, &tmp_table, item, item->type(), - (Item ***) 0, &tmp_field, 0, 0, 0, 0, 0); + field= create_tmp_field(thd, &tmp_table, item, item->type(), + (Item ***) 0, &tmp_field, &def_field, 0, 0, 0, 0, + 0); if (!field || !(cr_field=new create_field(field,(item->type() == Item::FIELD_ITEM ? ((Item_field *)item)->field : @@ -2614,9 +2640,9 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, don't want to delete from it) 2) it would be written before the CREATE TABLE, which is a wrong order. So we keep binary logging disabled when we open_table(). - NOTE: By locking table which we just have created (or for which we just have - have found that it already exists) separately from other tables used by the - statement we create potential window for deadlock. + NOTE: By locking table which we just have created (or for which we just + have have found that it already exists) separately from other tables used + by the statement we create potential window for deadlock. TODO: create and open should be done atomic ! */ { @@ -2674,7 +2700,8 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, } -class MY_HOOKS : public TABLEOP_HOOKS { +class MY_HOOKS : public TABLEOP_HOOKS +{ public: MY_HOOKS(select_create *x) : ptr(x) { } virtual void do_prelock(TABLE **tables, uint count) @@ -2687,18 +2714,16 @@ private: select_create *ptr; }; -int -select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u) -{ - DBUG_ENTER("select_create::prepare"); +int select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u) +{ MY_HOOKS hooks(this); + DBUG_ENTER("select_create::prepare"); unit= u; - table= create_table_from_items(thd, create_info, create_table, - extra_fields, keys, &values, &lock, - &hooks); - if (!table) + if (!(table= create_table_from_items(thd, create_info, create_table, + extra_fields, keys, &values, &lock, + &hooks))) DBUG_RETURN(-1); // abort() deletes table if (table->s->fields < values.elements) @@ -2707,16 +2732,15 @@ select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u) DBUG_RETURN(-1); } - /* First field to copy */ + /* First field to copy */ field= table->field+table->s->fields - values.elements; /* Mark all fields that are given values */ for (Field **f= field ; *f ; f++) - (*f)->query_id= thd->query_id; + bitmap_set_bit(table->write_set, (*f)->field_index); /* Don't set timestamp if used */ table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; - table->next_number_field=table->found_next_number_field; restore_record(table,s->default_values); // Get empty record @@ -2724,7 +2748,7 @@ select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u) if (info.ignore || info.handle_duplicates != DUP_ERROR) table->file->extra(HA_EXTRA_IGNORE_DUP_KEY); if (!thd->prelocked_mode) - table->file->start_bulk_insert((ha_rows) 0); + table->file->ha_start_bulk_insert((ha_rows) 0); thd->no_trans_update= 0; thd->abort_on_warning= (!info.ignore && (thd->variables.sql_mode & @@ -2735,8 +2759,7 @@ select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u) } -void -select_create::binlog_show_create_table(TABLE **tables, uint count) +void select_create::binlog_show_create_table(TABLE **tables, uint count) { /* Note 1: In RBR mode, we generate a CREATE TABLE statement for the @@ -2754,25 +2777,24 @@ select_create::binlog_show_create_table(TABLE **tables, uint count) since there potentially are sub-selects or accesses to information schema that will do a close_thread_tables(), destroying the statement transaction cache. - - To ensure that the event kaboodle is not written to the binary log - on rollback, we clear the OPTION_STATUS_NO_TRANS_UPDATE bit of - thd->options. - */ + */ +#ifdef HAVE_ROW_BASED_REPLICATION DBUG_ASSERT(thd->current_stmt_binlog_row_based); +#endif DBUG_ASSERT(tables && *tables && count > 0); - thd->options&= ~OPTION_STATUS_NO_TRANS_UPDATE; char buf[2048]; String query(buf, sizeof(buf), system_charset_info); - query.length(0); // Have to zero it since constructor doesn't - + int result; TABLE_LIST table_list; + memset(&table_list, 0, sizeof(table_list)); table_list.table = *tables; + query.length(0); // Have to zero it since constructor doesn't - int result= store_create_info(thd, &table_list, &query, create_info); + result= store_create_info(thd, &table_list, &query, create_info); DBUG_ASSERT(result == 0); /* store_create_info() always return 0 */ + thd->binlog_query(THD::STMT_QUERY_TYPE, query.ptr(), query.length(), /* is_trans */ TRUE, @@ -2809,17 +2831,9 @@ bool select_create::send_eof() table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY); VOID(pthread_mutex_lock(&LOCK_open)); mysql_unlock_tables(thd, lock); - /* - TODO: - Check if we can remove the following two rows. - We should be able to just keep the table in the table cache. - */ if (!table->s->tmp_table) { - ulong version= table->s->version; - hash_delete(&open_cache,(byte*) table); - /* Tell threads waiting for refresh that something has happened */ - if (version != refresh_version) + if (close_thread_table(thd, &table)) VOID(pthread_cond_broadcast(&COND_refresh)); } lock=0; diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 02711ed8f48..d45f4369095 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -152,8 +152,7 @@ void lex_start(THD *thd, const uchar *buf, uint length) lex->found_semicolon= 0; lex->safe_to_cache_query= 1; lex->time_zone_tables_used= 0; - lex->leaf_tables_insert= lex->query_tables= 0; - lex->query_tables_last= &lex->query_tables; + lex->leaf_tables_insert= 0; lex->variables_used= 0; lex->empty_field_list_on_rset= 0; lex->select_lex.select_number= 1; @@ -175,17 +174,12 @@ void lex_start(THD *thd, const uchar *buf, uint length) lex->sphead= NULL; lex->spcont= NULL; lex->proc_list.first= 0; - lex->query_tables_own_last= 0; lex->escape_used= lex->et_compile_phase= FALSE; + lex->reset_query_tables_list(FALSE); lex->name= 0; lex->et= NULL; - if (lex->sroutines.records) - my_hash_reset(&lex->sroutines); - lex->sroutines_list.empty(); - lex->sroutines_list_own_last= lex->sroutines_list.next; - lex->sroutines_list_own_elements= 0; lex->nest_level=0 ; lex->allow_sum_func= 0; lex->in_sum_func= NULL; @@ -1604,6 +1598,52 @@ void st_select_lex::print_limit(THD *thd, String *str) /* + Initialize (or reset) Query_tables_list object. + + SYNOPSIS + reset_query_tables_list() + init TRUE - we should perform full initialization of object with + allocating needed memory + FALSE - object is already initialized so we should only reset + its state so it can be used for parsing/processing + of new statement + + DESCRIPTION + This method initializes Query_tables_list so it can be used as part + of LEX object for parsing/processing of statement. One can also use + this method to reset state of already initialized Query_tables_list + so it can be used for processing of new statement. +*/ + +void Query_tables_list::reset_query_tables_list(bool init) +{ + query_tables= 0; + query_tables_last= &query_tables; + query_tables_own_last= 0; + if (init) + hash_init(&sroutines, system_charset_info, 0, 0, 0, sp_sroutine_key, 0, 0); + else if (sroutines.records) + my_hash_reset(&sroutines); + sroutines_list.empty(); + sroutines_list_own_last= sroutines_list.next; + sroutines_list_own_elements= 0; +} + + +/* + Destroy Query_tables_list object with freeing all resources used by it. + + SYNOPSIS + destroy_query_tables_list() +*/ + +void Query_tables_list::destroy_query_tables_list() +{ + hash_free(&sroutines); +} + + +/* Initialize LEX object. SYNOPSIS @@ -1619,12 +1659,9 @@ void st_select_lex::print_limit(THD *thd, String *str) st_lex::st_lex() :result(0), yacc_yyss(0), yacc_yyvs(0), - sql_command(SQLCOM_END), query_tables_own_last(0) + sql_command(SQLCOM_END) { - hash_init(&sroutines, system_charset_info, 0, 0, 0, sp_sroutine_key, 0, 0); - sroutines_list.empty(); - sroutines_list_own_last= sroutines_list.next; - sroutines_list_own_elements= 0; + reset_query_tables_list(TRUE); } @@ -2008,6 +2045,11 @@ void st_lex::link_first_table_back(TABLE_LIST *first, SYNOPSIS st_lex::cleanup_after_one_table_open() + + NOTE + This method is mostly responsible for cleaning up of selects lists and + derived tables state. To rollback changes in Query_tables_list one has + to call Query_tables_list::reset_query_tables_list(FALSE). */ void st_lex::cleanup_after_one_table_open() @@ -2034,11 +2076,41 @@ void st_lex::cleanup_after_one_table_open() select_lex.cut_subtree(); } time_zone_tables_used= 0; - if (sroutines.records) - my_hash_reset(&sroutines); - sroutines_list.empty(); - sroutines_list_own_last= sroutines_list.next; - sroutines_list_own_elements= 0; +} + + +/* + Save current state of Query_tables_list for this LEX, and prepare it + for processing of new statemnt. + + SYNOPSIS + reset_n_backup_query_tables_list() + backup Pointer to Query_tables_list instance to be used for backup +*/ + +void st_lex::reset_n_backup_query_tables_list(Query_tables_list *backup) +{ + backup->set_query_tables_list(this); + /* + We have to perform full initialization here since otherwise we + will damage backed up state. + */ + this->reset_query_tables_list(TRUE); +} + + +/* + Restore state of Query_tables_list for this LEX from backup. + + SYNOPSIS + restore_backup_query_tables_list() + backup Pointer to Query_tables_list instance used for backup +*/ + +void st_lex::restore_backup_query_tables_list(Query_tables_list *backup) +{ + this->destroy_query_tables_list(); + this->set_query_tables_list(backup); } diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 12c7b106c26..77f79ad61f4 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -752,9 +752,95 @@ extern sys_var *trg_new_row_fake_var; enum xa_option_words {XA_NONE, XA_JOIN, XA_RESUME, XA_ONE_PHASE, XA_SUSPEND, XA_FOR_MIGRATE}; + +/* + Class representing list of all tables used by statement. + It also contains information about stored functions used by statement + since during its execution we may have to add all tables used by its + stored functions/triggers to this list in order to pre-open and lock + them. + + Also used by st_lex::reset_n_backup/restore_backup_query_tables_list() + methods to save and restore this information. +*/ + +class Query_tables_list +{ +public: + /* Global list of all tables used by this statement */ + TABLE_LIST *query_tables; + /* Pointer to next_global member of last element in the previous list. */ + TABLE_LIST **query_tables_last; + /* + If non-0 then indicates that query requires prelocking and points to + next_global member of last own element in query table list (i.e. last + table which was not added to it as part of preparation to prelocking). + 0 - indicates that this query does not need prelocking. + */ + TABLE_LIST **query_tables_own_last; + /* Set of stored routines called by statement. */ + HASH sroutines; + /* + List linking elements of 'sroutines' set. Allows you to add new elements + to this set as you iterate through the list of existing elements. + 'sroutines_list_own_last' is pointer to ::next member of last element of + this list which represents routine which is explicitly used by query. + 'sroutines_list_own_elements' number of explicitly used routines. + We use these two members for restoring of 'sroutines_list' to the state + in which it was right after query parsing. + */ + SQL_LIST sroutines_list; + byte **sroutines_list_own_last; + uint sroutines_list_own_elements; + + /* + These constructor and destructor serve for creation/destruction + of Query_tables_list instances which are used as backup storage. + */ + Query_tables_list() {} + ~Query_tables_list() {} + + /* Initializes (or resets) Query_tables_list object for "real" use. */ + void reset_query_tables_list(bool init); + void destroy_query_tables_list(); + void set_query_tables_list(Query_tables_list *state) + { + *this= *state; + } + + void add_to_query_tables(TABLE_LIST *table) + { + *(table->prev_global= query_tables_last)= table; + query_tables_last= &table->next_global; + } + bool requires_prelocking() + { + return test(query_tables_own_last); + } + void mark_as_requiring_prelocking(TABLE_LIST **tables_own_last) + { + query_tables_own_last= tables_own_last; + } + /* Return pointer to first not-own table in query-tables or 0 */ + TABLE_LIST* first_not_own_table() + { + return ( query_tables_own_last ? *query_tables_own_last : 0); + } + void chop_off_not_own_tables() + { + if (query_tables_own_last) + { + *query_tables_own_last= 0; + query_tables_last= query_tables_own_last; + query_tables_own_last= 0; + } + } +}; + + /* The state of the lex parsing. This is saved in the THD struct */ -typedef struct st_lex +typedef struct st_lex : public Query_tables_list { uint yylineno,yytoklen; /* Simulate lex */ LEX_YYSTYPE yylval; @@ -787,14 +873,6 @@ typedef struct st_lex gptr yacc_yyss,yacc_yyvs; THD *thd; CHARSET_INFO *charset; - TABLE_LIST *query_tables; /* global list of all tables in this query */ - /* - last element next_global of previous list (used only for list building - during parsing and VIEW processing. This pointer could be invalid during - processing of information schema tables(see get_schema_tables_result - function) - */ - TABLE_LIST **query_tables_last; /* store original leaf_tables for INSERT SELECT and PS/SP */ TABLE_LIST *leaf_tables_insert; /* Position (first character index) of SELECT of CREATE VIEW statement */ @@ -935,20 +1013,6 @@ typedef struct st_lex bool sp_lex_in_use; /* Keep track on lex usage in SPs for error handling */ bool all_privileges; sp_pcontext *spcont; - /* Set of stored routines called by statement. */ - HASH sroutines; - /* - List linking elements of 'sroutines' set. Allows you to add new elements - to this set as you iterate through the list of existing elements. - 'sroutines_list_own_last' is pointer to ::next member of last element of - this list which represents routine which is explicitly used by query. - 'sroutines_list_own_elements' number of explicitly used routines. - We use these two members for restoring of 'sroutines_list' to the state - in which it was right after query parsing. - */ - SQL_LIST sroutines_list; - byte **sroutines_list_own_last; - uint sroutines_list_own_elements; st_sp_chistics sp_chistics; @@ -989,14 +1053,6 @@ typedef struct st_lex const char *stmt_definition_begin; /* - If non-0 then indicates that query requires prelocking and points to - next_global member of last own element in query table list (i.e. last - table which was not added to it as part of preparation to prelocking). - 0 - indicates that this query does not need prelocking. - */ - TABLE_LIST **query_tables_own_last; - - /* Pointers to part of LOAD DATA statement that should be rewritten during replication ("LOCAL 'filename' REPLACE INTO" part). */ @@ -1014,7 +1070,7 @@ typedef struct st_lex virtual ~st_lex() { - hash_free(&sroutines); + destroy_query_tables_list(); } inline void uncacheable(uint8 cause) @@ -1039,11 +1095,6 @@ typedef struct st_lex TABLE_LIST *unlink_first_table(bool *link_to_local); void link_first_table_back(TABLE_LIST *first, bool link_to_local); void first_lists_tables_same(); - inline void add_to_query_tables(TABLE_LIST *table) - { - *(table->prev_global= query_tables_last)= table; - query_tables_last= &table->next_global; - } bool add_time_zone_tables_to_query_tables(THD *thd); bool can_be_merged(); @@ -1075,28 +1126,7 @@ typedef struct st_lex return FALSE; } } - inline bool requires_prelocking() - { - return test(query_tables_own_last); - } - inline void mark_as_requiring_prelocking(TABLE_LIST **tables_own_last) - { - query_tables_own_last= tables_own_last; - } - /* Return pointer to first not-own table in query-tables or 0 */ - TABLE_LIST* first_not_own_table() - { - return ( query_tables_own_last ? *query_tables_own_last : 0); - } - void chop_off_not_own_tables() - { - if (query_tables_own_last) - { - *query_tables_own_last= 0; - query_tables_last= query_tables_own_last; - query_tables_own_last= 0; - } - } + void cleanup_after_one_table_open(); bool push_context(Name_resolution_context *context) @@ -1113,6 +1143,9 @@ typedef struct st_lex { return context_stack.head(); } + + void reset_n_backup_query_tables_list(Query_tables_list *backup); + void restore_backup_query_tables_list(Query_tables_list *backup); } LEX; struct st_lex_local: public st_lex diff --git a/sql/sql_load.cc b/sql/sql_load.cc index bf8a6b8cfbe..f8debbedc62 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -117,11 +117,10 @@ bool mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, { char name[FN_REFLEN]; File file; - TABLE *table; + TABLE *table= NULL; int error; String *field_term=ex->field_term,*escaped=ex->escaped; String *enclosed=ex->enclosed; - Item *unused_conds= 0; bool is_fifo=0; #ifndef EMBEDDED_LIBRARY LOAD_FILE_INFO lf_info; @@ -153,10 +152,11 @@ bool mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, ha_enable_transaction(thd, FALSE); if (open_and_lock_tables(thd, table_list)) DBUG_RETURN(TRUE); - if (setup_tables(thd, &thd->lex->select_lex.context, - &thd->lex->select_lex.top_join_list, - table_list, &unused_conds, - &thd->lex->select_lex.leaf_tables, FALSE)) + if (setup_tables_and_check_access(thd, &thd->lex->select_lex.context, + &thd->lex->select_lex.top_join_list, + table_list, + &thd->lex->select_lex.leaf_tables, FALSE, + INSERT_ACL | UPDATE_ACL)) DBUG_RETURN(-1); if (!table_list->table || // do not suport join view !table_list->updatable || // and derived tables @@ -187,51 +187,48 @@ bool mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, table= table_list->table; transactional_table= table->file->has_transactions(); + if (table->found_next_number_field) + table->mark_auto_increment_column(); + if (!fields_vars.elements) { Field **field; for (field=table->field; *field ; field++) fields_vars.push_back(new Item_field(*field)); - /* - Since all fields are set we set all bits in the write set - */ - table->file->ha_set_all_bits_in_write_set(); + bitmap_set_all(table->write_set); table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; /* Let us also prepare SET clause, altough it is probably empty in this case. */ - if (setup_fields(thd, 0, set_fields, 1, 0, 0) || - setup_fields(thd, 0, set_values, 1, 0, 0)) + if (setup_fields(thd, 0, set_fields, MARK_COLUMNS_WRITE, 0, 0) || + setup_fields(thd, 0, set_values, MARK_COLUMNS_READ, 0, 0)) DBUG_RETURN(TRUE); } else { // Part field list /* TODO: use this conds for 'WITH CHECK OPTIONS' */ - /* - Indicate that both variables in field list and fields in update_list - is to be included in write set of table. We do however set all bits - in write set anyways since it is not allowed to specify NULLs in - LOAD DATA - */ - table->file->ha_set_all_bits_in_write_set(); - if (setup_fields(thd, 0, fields_vars, 2, 0, 0) || - setup_fields(thd, 0, set_fields, 2, 0, 0) || + if (setup_fields(thd, 0, fields_vars, MARK_COLUMNS_WRITE, 0, 0) || + setup_fields(thd, 0, set_fields, MARK_COLUMNS_WRITE, 0, 0) || check_that_all_fields_are_given_values(thd, table, table_list)) DBUG_RETURN(TRUE); /* Check whenever TIMESTAMP field with auto-set feature specified explicitly. */ - if (table->timestamp_field && - table->timestamp_field->query_id == thd->query_id) - table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; - /* - Fix the expressions in SET clause. This should be done after - check_that_all_fields_are_given_values() and setting use_timestamp - since it may update query_id for some fields. - */ - if (setup_fields(thd, 0, set_values, 1, 0, 0)) + if (table->timestamp_field) + { + if (bitmap_is_set(table->write_set, + table->timestamp_field->field_index)) + table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; + else + { + bitmap_set_bit(table->write_set, + table->timestamp_field->field_index); + } + } + /* Fix the expressions in SET clause */ + if (setup_fields(thd, 0, set_values, MARK_COLUMNS_READ, 0, 0)) DBUG_RETURN(TRUE); } @@ -366,7 +363,7 @@ bool mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, handle_duplicates == DUP_REPLACE) table->file->extra(HA_EXTRA_IGNORE_DUP_KEY); if (!thd->prelocked_mode) - table->file->start_bulk_insert((ha_rows) 0); + table->file->ha_start_bulk_insert((ha_rows) 0); table->copy_blobs=1; thd->no_trans_update= 0; @@ -383,7 +380,7 @@ bool mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, error= read_sep_field(thd, info, table_list, fields_vars, set_fields, set_values, read_info, *enclosed, skip_lines, ignore); - if (!thd->prelocked_mode && table->file->end_bulk_insert() && !error) + if (!thd->prelocked_mode && table->file->ha_end_bulk_insert() && !error) { table->file->print_error(my_errno, MYF(0)); error= 1; @@ -505,6 +502,8 @@ err: mysql_unlock_tables(thd, thd->lock); thd->lock=0; } + if (table != NULL) + table->file->release_auto_increment(); thd->abort_on_warning= 0; DBUG_RETURN(error); } diff --git a/sql/sql_olap.cc b/sql/sql_olap.cc index b457ff5a6d6..3d06030536f 100644 --- a/sql/sql_olap.cc +++ b/sql/sql_olap.cc @@ -155,9 +155,11 @@ int handle_olaps(LEX *lex, SELECT_LEX *select_lex) if (setup_tables(lex->thd, &select_lex->context, &select_lex->top_join_list, (TABLE_LIST *)select_lex->table_list.first - &select_lex->where, &select_lex->leaf_tables, FALSE) || - setup_fields(lex->thd, 0, select_lex->item_list, 1, &all_fields,1) || - setup_fields(lex->thd, 0, item_list_copy, 1, &all_fields, 1)) + &select_lex->leaf_tables, FALSE) || + setup_fields(lex->thd, 0, select_lex->item_list, MARK_COLUMNS_READ, + &all_fields,1) || + setup_fields(lex->thd, 0, item_list_copy, MARK_COLUMNS_READ, + &all_fields, 1)) return -1; if (select_lex->olap == CUBE_TYPE) diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 95f3f455351..14847a9906d 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -162,8 +162,9 @@ bool end_active_trans(THD *thd) thd->server_status&= ~SERVER_STATUS_IN_TRANS; if (ha_commit(thd)) error=1; - thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE); } + thd->options&= ~(OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE | + OPTION_KEEP_LOG); DBUG_RETURN(error); } @@ -186,8 +187,7 @@ bool begin_trans(THD *thd) else { LEX *lex= thd->lex; - thd->options= ((thd->options & (ulong) ~(OPTION_STATUS_NO_TRANS_UPDATE)) | - OPTION_BEGIN); + thd->options|= OPTION_BEGIN; thd->server_status|= SERVER_STATUS_IN_TRANS; if (lex->start_transaction_opt & MYSQL_START_TRANS_OPT_WITH_CONS_SNAPSHOT) error= ha_start_consistent_snapshot(thd); @@ -398,7 +398,7 @@ int check_user(THD *thd, enum enum_server_command command, NO_ACCESS)) // authentication is OK { DBUG_PRINT("info", - ("Capabilities: %d packet_length: %ld Host: '%s' " + ("Capabilities: %lx packet_length: %ld Host: '%s' " "Login user: '%s' Priv_user: '%s' Using password: %s " "Access: %u db: '%s'", thd->client_capabilities, @@ -1439,7 +1439,8 @@ int end_trans(THD *thd, enum enum_mysql_completiontype completion) */ thd->server_status&= ~SERVER_STATUS_IN_TRANS; res= ha_commit(thd); - thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE); + thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE | + OPTION_KEEP_LOG); break; case COMMIT_RELEASE: do_release= 1; /* fall through */ @@ -1456,7 +1457,8 @@ int end_trans(THD *thd, enum enum_mysql_completiontype completion) thd->server_status&= ~SERVER_STATUS_IN_TRANS; if (ha_rollback(thd)) res= -1; - thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE); + thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE | + OPTION_KEEP_LOG); if (!res && (completion == ROLLBACK_AND_CHAIN)) res= begin_trans(thd); break; @@ -2008,13 +2010,17 @@ bool dispatch_command(enum enum_server_command command, THD *thd, #else char *buff= thd->net.last_error; #endif + + STATUS_VAR current_global_status_var; + calc_sum_of_all_status(¤t_global_status_var); + ulong uptime = (ulong) (thd->start_time - start_time); sprintf((char*) buff, "Uptime: %lu Threads: %d Questions: %lu Slow queries: %lu Opens: %lu Flush tables: %lu Open tables: %u Queries per second avg: %.3f", uptime, (int) thread_count, (ulong) thd->query_id, - (ulong) thd->status_var.long_query_count, - thd->status_var.opened_tables, refresh_version, + current_global_status_var.long_query_count, + current_global_status_var.opened_tables, refresh_version, cached_open_tables(), (uptime ? (ulonglong2double(thd->query_id) / (double) uptime) : (double) 0)); @@ -2737,8 +2743,7 @@ mysql_execute_command(THD *thd) goto error; if (end_active_trans(thd)) goto error; - else - res = load_master_data(thd); + res = load_master_data(thd); break; #endif /* HAVE_REPLICATION */ case SQLCOM_SHOW_ENGINE_STATUS: @@ -2802,11 +2807,6 @@ mysql_execute_command(THD *thd) break; } } - else - { - /* So that CREATE TEMPORARY TABLE gets to binlog at commit/rollback */ - thd->options|= OPTION_STATUS_NO_TRANS_UPDATE; - } DBUG_ASSERT(first_table == all_tables && first_table != 0); bool link_to_local; // Skip first table, which is the table we are creating @@ -2931,6 +2931,9 @@ mysql_execute_command(THD *thd) } else { + /* So that CREATE TEMPORARY TABLE gets to binlog at commit/rollback */ + if (lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) + thd->options|= OPTION_KEEP_LOG; /* regular create */ if (lex->like_name) res= mysql_create_like_table(thd, create_table, &lex->create_info, @@ -3501,7 +3504,7 @@ end_with_restore_list: lex->drop_if_exists= 1; /* So that DROP TEMPORARY TABLE gets to binlog at commit/rollback */ - thd->options|= OPTION_STATUS_NO_TRANS_UPDATE; + thd->options|= OPTION_KEEP_LOG; } res= mysql_rm_table(thd, first_table, lex->drop_if_exists, lex->drop_temporary); @@ -3840,16 +3843,16 @@ end_with_restore_list: switch (lex->sql_command) { case SQLCOM_CREATE_EVENT: res= Events::create_event(thd, lex->et, - (uint) lex->create_info.options, - &rows_affected); + (uint) lex->create_info.options, + &rows_affected); break; case SQLCOM_ALTER_EVENT: res= Events::update_event(thd, lex->et, lex->spname, - &rows_affected); + &rows_affected); break; case SQLCOM_DROP_EVENT: res= Events::drop_event(thd, lex->et, lex->drop_if_exists, - &rows_affected); + &rows_affected); default:; } DBUG_PRINT("info", ("CREATE/ALTER/DROP returned error code=%d af_rows=%d", @@ -3887,7 +3890,7 @@ end_with_restore_list: my_error(ER_TOO_LONG_IDENT, MYF(0), lex->spname->m_name.str); goto error; } - res= Events::show_create_event(thd, lex->spname, lex->et->definer); + res= Events::show_create_event(thd, lex->spname); break; } #ifndef DBUG_OFF @@ -4238,7 +4241,8 @@ end_with_restore_list: res= TRUE; // cannot happen else { - if ((thd->options & OPTION_STATUS_NO_TRANS_UPDATE) && + if ((thd->options & + (OPTION_STATUS_NO_TRANS_UPDATE | OPTION_KEEP_LOG)) && !thd->slave_thread) push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARNING_NOT_COMPLETE_ROLLBACK, @@ -4969,7 +4973,8 @@ end_with_restore_list: thd->transaction.xid_state.xa_state=XA_ACTIVE; thd->transaction.xid_state.xid.set(thd->lex->xid); xid_cache_insert(&thd->transaction.xid_state); - thd->options= ((thd->options & (ulong) ~(OPTION_STATUS_NO_TRANS_UPDATE)) | + thd->options= ((thd->options & ~(OPTION_STATUS_NO_TRANS_UPDATE | + OPTION_KEEP_LOG)) | OPTION_BEGIN); thd->server_status|= SERVER_STATUS_IN_TRANS; send_ok(thd); @@ -5063,7 +5068,8 @@ end_with_restore_list: xa_state_names[thd->transaction.xid_state.xa_state]); break; } - thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE); + thd->options&= ~(OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE | + OPTION_KEEP_LOG); thd->server_status&= ~SERVER_STATUS_IN_TRANS; xid_cache_delete(&thd->transaction.xid_state); thd->transaction.xid_state.xa_state=XA_NOTR; @@ -5093,7 +5099,8 @@ end_with_restore_list: my_error(ER_XAER_RMERR, MYF(0)); else send_ok(thd); - thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE); + thd->options&= ~(OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE | + OPTION_KEEP_LOG); thd->server_status&= ~SERVER_STATUS_IN_TRANS; xid_cache_delete(&thd->transaction.xid_state); thd->transaction.xid_state.xa_state=XA_NOTR; @@ -5188,23 +5195,35 @@ error: bool check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *all_tables) { + Security_context * backup_ctx= thd->security_ctx; + + /* we need to switch to the saved context (if any) */ + if (all_tables->security_ctx) + thd->security_ctx= all_tables->security_ctx; + if (check_access(thd, privilege, all_tables->db, &all_tables->grant.privilege, 0, 0, test(all_tables->schema_table))) - return 1; + goto deny; /* Show only 1 table for check_grant */ if (grant_option && check_grant(thd, privilege, all_tables, 0, 1, 0)) - return 1; + goto deny; + + thd->security_ctx= backup_ctx; /* Check rights on tables of subselects and implictly opened tables */ TABLE_LIST *subselects_tables; if ((subselects_tables= all_tables->next_global)) { if ((check_table_access(thd, SELECT_ACL, subselects_tables, 0))) - return 1; + goto deny; } return 0; + +deny: + thd->security_ctx= backup_ctx; + return 1; } @@ -5385,6 +5404,7 @@ check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables, ulong found_access=0; TABLE_LIST *org_tables= tables; TABLE_LIST *first_not_own_table= thd->lex->first_not_own_table(); + Security_context *sctx= thd->security_ctx, *backup_ctx= thd->security_ctx; /* The check that first_not_own_table is not reached is for the case when the given table list refers to the list for prelocking (contains tables @@ -5392,12 +5412,17 @@ check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables, */ for (; tables != first_not_own_table; tables= tables->next_global) { + if (tables->security_ctx) + sctx= tables->security_ctx; + else + sctx= backup_ctx; + if (tables->schema_table && (want_access & ~(SELECT_ACL | EXTRA_ACL | FILE_ACL))) { if (!no_errors) my_error(ER_DBACCESS_DENIED_ERROR, MYF(0), - thd->security_ctx->priv_user, thd->security_ctx->priv_host, + sctx->priv_user, sctx->priv_host, information_schema_name.str); return TRUE; } @@ -5406,12 +5431,13 @@ check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables, Remove SHOW_VIEW_ACL, because it will be checked during making view */ tables->grant.orig_want_privilege= (want_access & ~SHOW_VIEW_ACL); - if (tables->derived || tables->schema_table || tables->belong_to_view || + if (tables->derived || tables->schema_table || (tables->table && (int)tables->table->s->tmp_table) || my_tz_check_n_skip_implicit_tables(&tables, thd->lex->time_zone_tables_used)) continue; - if ((thd->security_ctx->master_access & want_access) == + thd->security_ctx= sctx; + if ((sctx->master_access & want_access) == (want_access & ~EXTRA_ACL) && thd->db) tables->grant.privilege= want_access; @@ -5423,19 +5449,23 @@ check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables, { if (check_access(thd,want_access,tables->db,&tables->grant.privilege, 0, no_errors, test(tables->schema_table))) - return TRUE; // Access denied + goto deny; // Access denied found_access=tables->grant.privilege; found=1; } } else if (check_access(thd,want_access,tables->db,&tables->grant.privilege, 0, no_errors, test(tables->schema_table))) - return TRUE; + goto deny; } + thd->security_ctx= backup_ctx; if (grant_option) return check_grant(thd,want_access & ~EXTRA_ACL,org_tables, test(want_access & EXTRA_ACL), UINT_MAX, no_errors); return FALSE; +deny: + thd->security_ctx= backup_ctx; + return TRUE; } @@ -5686,6 +5716,14 @@ void mysql_reset_thd_for_next_command(THD *thd) thd->server_status&= ~ (SERVER_MORE_RESULTS_EXISTS | SERVER_QUERY_NO_INDEX_USED | SERVER_QUERY_NO_GOOD_INDEX_USED); + /* + If in autocommit mode and not in a transaction, reset + OPTION_STATUS_NO_TRANS_UPDATE | OPTION_KEEP_LOG to not get warnings + in ha_rollback_trans() about some tables couldn't be rolled back. + */ + if (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))) + thd->options&= ~(OPTION_STATUS_NO_TRANS_UPDATE | OPTION_KEEP_LOG); + DBUG_ASSERT(thd->security_ctx== &thd->main_security_ctx); thd->tmp_table_used= 0; if (!thd->in_sub_stmt) @@ -7143,7 +7181,7 @@ bool mysql_create_index(THD *thd, TABLE_LIST *table_list, List<Key> &keys) HA_CREATE_INFO create_info; DBUG_ENTER("mysql_create_index"); bzero((char*) &create_info,sizeof(create_info)); - create_info.db_type= (handlerton*) &default_hton; + create_info.db_type= 0; create_info.default_table_charset= thd->variables.collation_database; DBUG_RETURN(mysql_alter_table(thd,table_list->db,table_list->table_name, &create_info, table_list, @@ -7159,7 +7197,7 @@ bool mysql_drop_index(THD *thd, TABLE_LIST *table_list, ALTER_INFO *alter_info) HA_CREATE_INFO create_info; DBUG_ENTER("mysql_drop_index"); bzero((char*) &create_info,sizeof(create_info)); - create_info.db_type= (handlerton*) &default_hton; + create_info.db_type= 0; create_info.default_table_charset= thd->variables.collation_database; alter_info->clear(); alter_info->flags= ALTER_DROP_INDEX; diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index 73fc699989e..db69fd3daef 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -849,7 +849,7 @@ static bool fix_fields_part_func(THD *thd, TABLE_LIST *tables, context->table_list= tables; context->first_name_resolution_table= tables; context->last_name_resolution_table= NULL; - func_expr->walk(&Item::change_context_processor, (byte*) context); + func_expr->walk(&Item::change_context_processor, 0, (byte*) context); save_where= thd->where; thd->where= "partition function"; error= func_expr->fix_fields(thd, (Item**)0); @@ -1335,7 +1335,7 @@ bool fix_partition_func(THD *thd, const char* name, TABLE *table, char db_name_string[FN_REFLEN]; char* db_name; partition_info *part_info= table->part_info; - ulong save_set_query_id= thd->set_query_id; + enum_mark_columns save_mark_used_columns= thd->mark_used_columns; Item *thd_free_list= thd->free_list; DBUG_ENTER("fix_partition_func"); @@ -1343,8 +1343,8 @@ bool fix_partition_func(THD *thd, const char* name, TABLE *table, { DBUG_RETURN(FALSE); } - thd->set_query_id= 0; - DBUG_PRINT("info", ("thd->set_query_id: %d", thd->set_query_id)); + thd->mark_used_columns= MARK_COLUMNS_NONE; + DBUG_PRINT("info", ("thd->mark_used_columns: %d", thd->mark_used_columns)); /* Set-up the TABLE_LIST object to be a list with a single table Set the object to zero to create NULL pointers and set alias @@ -1484,8 +1484,8 @@ bool fix_partition_func(THD *thd, const char* name, TABLE *table, result= FALSE; end: thd->free_list= thd_free_list; - thd->set_query_id= save_set_query_id; - DBUG_PRINT("info", ("thd->set_query_id: %d", thd->set_query_id)); + thd->mark_used_columns= save_mark_used_columns; + DBUG_PRINT("info", ("thd->mark_used_columns: %d", thd->mark_used_columns)); DBUG_RETURN(result); } @@ -1665,8 +1665,8 @@ static int add_keyword_int(File fptr, const char *keyword, longlong num) static int add_engine(File fptr, handlerton *engine_type) { - const char *engine_str= engine_type->name; - DBUG_PRINT("info", ("ENGINE = %s", engine_str)); + const char *engine_str= hton2plugin[engine_type->slot]->name.str; + DBUG_PRINT("info", ("ENGINE: %s", engine_str)); int err= add_string(fptr, "ENGINE = "); return err + add_string(fptr, engine_str); } @@ -1675,8 +1675,9 @@ static int add_partition_options(File fptr, partition_element *p_elem) { int err= 0; + err+= add_space(fptr); if (p_elem->tablespace_name) - err+= add_keyword_string(fptr,"TABLESPACE", FALSE, + err+= add_keyword_string(fptr,"TABLESPACE", FALSE, p_elem->tablespace_name); if (p_elem->nodegroup_id != UNDEF_NODEGROUP) err+= add_keyword_int(fptr,"NODEGROUP",(longlong)p_elem->nodegroup_id); @@ -1702,7 +1703,7 @@ static int add_partition_values(File fptr, partition_info *part_info, if (part_info->part_type == RANGE_PARTITION) { - err+= add_string(fptr, "VALUES LESS THAN "); + err+= add_string(fptr, " VALUES LESS THAN "); if (p_elem->range_value != LONGLONG_MAX) { err+= add_begin_parenthesis(fptr); @@ -1716,7 +1717,7 @@ static int add_partition_values(File fptr, partition_info *part_info, { uint i; List_iterator<longlong> list_val_it(p_elem->list_val_list); - err+= add_string(fptr, "VALUES IN "); + err+= add_string(fptr, " VALUES IN "); uint no_items= p_elem->list_val_list.elements; err+= add_begin_parenthesis(fptr); if (p_elem->has_null_value) @@ -1740,7 +1741,7 @@ static int add_partition_values(File fptr, partition_info *part_info, err+= add_end_parenthesis(fptr); } end: - return err + add_space(fptr); + return err; } /* @@ -1754,7 +1755,7 @@ end: buf_length A pointer to the returned buffer length use_sql_alloc Allocate buffer from sql_alloc if true otherwise use my_malloc - write_all Write everything, also default values + show_partition_options Should we display partition options RETURN VALUES NULL error @@ -1783,7 +1784,7 @@ end: char *generate_partition_syntax(partition_info *part_info, uint *buf_length, bool use_sql_alloc, - bool write_all) + bool show_partition_options) { uint i,j, tot_no_parts, no_subparts, no_parts; partition_element *part_elem; @@ -1843,6 +1844,8 @@ char *generate_partition_syntax(partition_info *part_info, { err+= add_subpartition_by(fptr); /* Must be hash partitioning for subpartitioning */ + if (part_info->linear_hash_ind) + err+= add_string(fptr, partition_keywords[PKW_LINEAR].str); if (part_info->list_of_subpart_fields) err+= add_key_partition(fptr, part_info->subpart_field_list); else @@ -1863,7 +1866,7 @@ char *generate_partition_syntax(partition_info *part_info, tot_no_parts= part_info->partitions.elements; no_subparts= part_info->no_subparts; - if (write_all || (!part_info->use_default_partitions)) + if (!part_info->use_default_partitions) { bool first= TRUE; err+= add_begin_parenthesis(fptr); @@ -1882,12 +1885,14 @@ char *generate_partition_syntax(partition_info *part_info, first= FALSE; err+= add_partition(fptr); err+= add_name_string(fptr, part_elem->partition_name); - err+= add_space(fptr); err+= add_partition_values(fptr, part_info, part_elem); - if (!part_info->is_sub_partitioned()) - err+= add_partition_options(fptr, part_elem); - if (part_info->is_sub_partitioned() && - (write_all || (!part_info->use_default_subpartitions))) + if (!part_info->is_sub_partitioned() || + part_info->use_default_subpartitions) + { + if (show_partition_options) + err+= add_partition_options(fptr, part_elem); + } + else { err+= add_space(fptr); err+= add_begin_parenthesis(fptr); @@ -1898,8 +1903,8 @@ char *generate_partition_syntax(partition_info *part_info, part_elem= sub_it++; err+= add_subpartition(fptr); err+= add_name_string(fptr, part_elem->partition_name); - err+= add_space(fptr); - err+= add_partition_options(fptr, part_elem); + if (show_partition_options) + err+= add_partition_options(fptr, part_elem); if (j != (no_subparts-1)) { err+= add_comma(fptr); @@ -1990,10 +1995,7 @@ bool partition_key_modified(TABLE *table, List<Item> &fields) in function */ -static -inline -longlong -part_val_int(Item *item_expr) +static inline longlong part_val_int(Item *item_expr) { longlong value= item_expr->val_int(); if (item_expr->null_value) @@ -2197,9 +2199,11 @@ static uint32 get_part_id_linear_key(partition_info *part_info, out:part_id The partition id is returned through this pointer RETURN VALUE - part_id - return TRUE means that the fields of the partition function didn't fit - into any partition and thus the values of the PF-fields are not allowed. + part_id Partition id of partition that would contain + row with given values of PF-fields + HA_ERR_NO_PARTITION_FOUND The fields of the partition function didn't + fit into any partition and thus the values of + the PF-fields are not allowed. DESCRIPTION A routine used from write_row, update_row and delete_row from any @@ -2238,9 +2242,11 @@ static uint32 get_part_id_linear_key(partition_info *part_info, out:part_id The partition id is returned through this pointer RETURN VALUE - part_id - return TRUE means that the fields of the partition function didn't fit - into any partition and thus the values of the PF-fields are not allowed. + part_id Partition id of partition that would contain + row with given values of PF-fields + HA_ERR_NO_PARTITION_FOUND The fields of the partition function didn't + fit into any partition and thus the values of + the PF-fields are not allowed. DESCRIPTION @@ -2406,10 +2412,12 @@ int get_partition_id_range(partition_info *part_info, loc_part_id++; *part_id= (uint32)loc_part_id; *func_value= part_func_value; - if (loc_part_id == max_partition) - if (range_array[loc_part_id] != LONGLONG_MAX) - if (part_func_value >= range_array[loc_part_id]) - DBUG_RETURN(HA_ERR_NO_PARTITION_FOUND); + if (loc_part_id == max_partition && + range_array[loc_part_id] != LONGLONG_MAX && + part_func_value >= range_array[loc_part_id]) + DBUG_RETURN(HA_ERR_NO_PARTITION_FOUND); + + DBUG_PRINT("exit",("partition: %d", *part_id)); DBUG_RETURN(0); } @@ -3559,17 +3567,9 @@ static bool check_engine_condition(partition_element *p_elem, DBUG_ENTER("check_engine_condition"); DBUG_PRINT("enter", ("def_eng = %u, first = %u", default_engine, *first)); - if (*engine_type) - DBUG_PRINT("info", ("engine_type = %s", (*engine_type)->name)); - else - DBUG_PRINT("info", ("engine_type = NULL")); if (*first && default_engine) { *engine_type= p_elem->engine_type; - if (*engine_type) - DBUG_PRINT("info", ("engine_type changed to = %s", (*engine_type)->name)); - else - DBUG_PRINT("info", ("engine_type changed to = NULL")); } *first= FALSE; if ((!default_engine && @@ -3879,6 +3879,7 @@ uint prep_alter_part_table(THD *thd, TABLE *table, ALTER_INFO *alter_info, DBUG_RETURN(TRUE); } alt_part_info->part_type= tab_part_info->part_type; + alt_part_info->subpart_type= tab_part_info->subpart_type; if (alt_part_info->set_up_defaults_for_partitioning(table->file, ULL(0), tab_part_info->no_parts)) @@ -4321,6 +4322,15 @@ state of p1. my_error(ER_TOO_MANY_PARTITIONS_ERROR, MYF(0)); DBUG_RETURN(TRUE); } + alt_part_info->part_type= tab_part_info->part_type; + alt_part_info->subpart_type= tab_part_info->subpart_type; + DBUG_ASSERT(!alt_part_info->use_default_partitions); + if (alt_part_info->set_up_defaults_for_partitioning(table->file, + ULL(0), + 0)) + { + DBUG_RETURN(TRUE); + } /* Online handling: REORGANIZE PARTITION: @@ -4457,7 +4467,7 @@ the generated partition syntax in a correct manner. tab_part_info->use_default_no_subpartitions= FALSE; } if (tab_part_info->check_partition_info((handlerton**)NULL, - table->file, ULL(0))) + table->file, ULL(0))) { DBUG_RETURN(TRUE); } @@ -4523,8 +4533,8 @@ the generated partition syntax in a correct manner. DBUG_PRINT("info", ("No explicit engine used")); create_info->db_type= table->part_info->default_engine_type; } - DBUG_PRINT("info", ("New engine type = %s", - create_info->db_type->name)); + DBUG_PRINT("info", ("New engine type: %s", + hton2plugin[create_info->db_type->slot]->name.str)); thd->work_part_info= NULL; *partition_changed= TRUE; } @@ -4586,11 +4596,9 @@ the generated partition syntax in a correct manner. } if (!is_native_partitioned) { - DBUG_ASSERT(create_info->db_type != &default_hton); + DBUG_ASSERT(create_info->db_type); create_info->db_type= &partition_hton; } - DBUG_PRINT("info", ("default_engine_type = %s", - thd->work_part_info->default_engine_type->name)); } } DBUG_RETURN(FALSE); @@ -4991,8 +4999,7 @@ static bool write_log_dropped_partitions(ALTER_PARTITION_PARAM_TYPE *lpt, DBUG_RETURN(TRUE); } *next_entry= log_entry->entry_pos; - if (temp_list) - sub_elem->log_entry= log_entry; + sub_elem->log_entry= log_entry; insert_part_info_log_entry_list(part_info, log_entry); } while (++j < no_subparts); } @@ -5010,8 +5017,7 @@ static bool write_log_dropped_partitions(ALTER_PARTITION_PARAM_TYPE *lpt, DBUG_RETURN(TRUE); } *next_entry= log_entry->entry_pos; - if (temp_list) - part_elem->log_entry= log_entry; + part_elem->log_entry= log_entry; insert_part_info_log_entry_list(part_info, log_entry); } } @@ -5285,7 +5291,7 @@ static bool write_log_final_change_partition(ALTER_PARTITION_PARAM_TYPE *lpt) lpt->table_name, "#"); pthread_mutex_lock(&LOCK_gdl); if (write_log_dropped_partitions(lpt, &next_entry, (const char*)path, - TRUE)) + lpt->alter_info->flags & ALTER_REORGANIZE_PARTITION)) goto error; if (write_log_changed_partitions(lpt, &next_entry, (const char*)path)) goto error; @@ -5373,6 +5379,79 @@ static void release_log_entries(partition_info *part_info) /* + Get a lock on table name to avoid that anyone can open the table in + a critical part of the ALTER TABLE. + SYNOPSIS + get_name_lock() + lpt Struct carrying parameters + RETURN VALUES + FALSE Success + TRUE Failure +*/ + +static int get_name_lock(ALTER_PARTITION_PARAM_TYPE *lpt) +{ + int error= 0; + DBUG_ENTER("get_name_lock"); + + bzero(&lpt->table_list, sizeof(lpt->table_list)); + lpt->table_list.db= (char*)lpt->db; + lpt->table_list.table= lpt->table; + lpt->table_list.table_name= (char*)lpt->table_name; + pthread_mutex_lock(&LOCK_open); + error= lock_table_name(lpt->thd, &lpt->table_list, FALSE); + pthread_mutex_unlock(&LOCK_open); + DBUG_RETURN(error); +} + + +/* + Unlock and close table before renaming and dropping partitions + SYNOPSIS + alter_close_tables() + lpt Struct carrying parameters + RETURN VALUES + 0 +*/ + +static int alter_close_tables(ALTER_PARTITION_PARAM_TYPE *lpt) +{ + THD *thd= lpt->thd; + TABLE *table= lpt->table; + DBUG_ENTER("alter_close_tables"); + /* + We need to also unlock tables and close all handlers. + We set lock to zero to ensure we don't do this twice + and we set db_stat to zero to ensure we don't close twice. + */ + mysql_unlock_tables(thd, thd->lock); + thd->lock= 0; + table->file->close(); + table->db_stat= 0; + DBUG_RETURN(0); +} + + +/* + Release a lock name + SYNOPSIS + release_name_lock() + lpt + RETURN VALUES + 0 +*/ + +static int release_name_lock(ALTER_PARTITION_PARAM_TYPE *lpt) +{ + DBUG_ENTER("release_name_lock"); + pthread_mutex_lock(&LOCK_open); + unlock_table_name(lpt->thd, &lpt->table_list); + pthread_mutex_unlock(&LOCK_open); + DBUG_RETURN(0); +} + + +/* Handle errors for ALTER TABLE for partitioning SYNOPSIS handle_alter_part_error() @@ -5522,7 +5601,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, HA_CREATE_INFO *create_info, TABLE_LIST *table_list, List<create_field> *create_list, - List<Key> *key_list, const char *db, + List<Key> *key_list, char *db, const char *table_name, uint fast_alter_partition) { @@ -5539,6 +5618,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, lpt->thd= thd; lpt->part_info= part_info; + lpt->alter_info= alter_info; lpt->create_info= create_info; lpt->create_list= create_list; lpt->key_list= key_list; @@ -5662,8 +5742,17 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, 2) Write the ddl log to ensure that the operation is completed even in the presence of a MySQL Server crash 3) Lock the table in TL_WRITE_ONLY to ensure all other accesses to - the table have completed - 4) Write the bin log + the table have completed. This ensures that other threads can not + execute on the table in parallel. + 4) Get a name lock on the table. This ensures that we can release all + locks on the table and since no one can open the table, there can + be no new threads accessing the table. They will be hanging on the + name lock. + 5) Close all tables that have already been opened but didn't stumble on + the abort locked previously. This is done as part of the + get_name_lock call. + 6) We are now ready to release all locks we got in this thread. + 7) Write the bin log Unfortunately the writing of the binlog is not synchronised with other logging activities. So no matter in which order the binlog is written compared to other activities there will always be cases @@ -5674,14 +5763,13 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, require writing the statement first in the ddl log and then when recovering from the crash read the binlog and insert it into the binlog if not written already. - 5) Install the previously written shadow frm file - 6) Ensure that any users that has opened the table but not yet - reached the abort lock do that before downgrading the lock. - 7) Prepare MyISAM handlers for drop of partitions - 8) Drop the partitions - 9) Remove entries from ddl log - 10) Wait until all accesses using the old frm file has completed - 11) Complete query + 8) Install the previously written shadow frm file + 9) Prepare handlers for drop of partitions + 10) Drop the partitions + 11) Remove entries from ddl log + 12) Release name lock so that all other threads can access the table + again. + 13) Complete query We insert Error injections at all places where it could be interesting to test if recovery is properly done. @@ -5694,22 +5782,24 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, ERROR_INJECT_CRASH("crash_drop_partition_3") || (not_completed= FALSE) || abort_and_upgrade_lock(lpt) || /* Always returns 0 */ + ERROR_INJECT_CRASH("crash_drop_partition_4") || + get_name_lock(lpt) || + ERROR_INJECT_CRASH("crash_drop_partition_5") || + alter_close_tables(lpt) || + ERROR_INJECT_CRASH("crash_drop_partition_6") || ((!thd->lex->no_write_to_binlog) && (write_bin_log(thd, FALSE, thd->query, thd->query_length), FALSE)) || - ERROR_INJECT_CRASH("crash_drop_partition_4") || - (table->file->extra(HA_EXTRA_PREPARE_FOR_DELETE), FALSE) || - ERROR_INJECT_CRASH("crash_drop_partition_5") || + ERROR_INJECT_CRASH("crash_drop_partition_7") || ((frm_install= TRUE), FALSE) || mysql_write_frm(lpt, WFRM_INSTALL_SHADOW) || ((frm_install= FALSE), FALSE) || - (close_open_tables_and_downgrade(lpt), FALSE) || - ERROR_INJECT_CRASH("crash_drop_partition_6") || + ERROR_INJECT_CRASH("crash_drop_partition_8") || mysql_drop_partitions(lpt) || - ERROR_INJECT_CRASH("crash_drop_partition_7") || + ERROR_INJECT_CRASH("crash_drop_partition_9") || (write_log_completed(lpt, FALSE), FALSE) || - ERROR_INJECT_CRASH("crash_drop_partition_8") || - (mysql_wait_completed_table(lpt, table), FALSE)) + ERROR_INJECT_CRASH("crash_drop_partition_10") || + (release_name_lock(lpt), FALSE)) { handle_alter_part_error(lpt, not_completed, TRUE, frm_install); DBUG_RETURN(TRUE); @@ -5735,15 +5825,24 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, 3) Lock all partitions in TL_WRITE_ONLY to ensure that no users are still using the old partitioning scheme. Wait until all ongoing users have completed before progressing. - 4) Write binlog - 5) Now the change is completed except for the installation of the + 4) Get a name lock on the table. This ensures that we can release all + locks on the table and since no one can open the table, there can + be no new threads accessing the table. They will be hanging on the + name lock. + 5) Close all tables that have already been opened but didn't stumble on + the abort locked previously. This is done as part of the + get_name_lock call. + 6) Close all table handlers and unlock all handlers but retain name lock + 7) Write binlog + 8) Now the change is completed except for the installation of the new frm file. We thus write an action in the log to change to the shadow frm file - 6) Install the new frm file of the table where the partitions are + 9) Install the new frm file of the table where the partitions are added to the table. - 7) Wait until all accesses using the old frm file has completed - 8) Remove entries from ddl log - 9) Complete query + 10)Wait until all accesses using the old frm file has completed + 11)Remove entries from ddl log + 12)Release name lock + 13)Complete query */ if (write_log_add_change_partition(lpt) || ERROR_INJECT_CRASH("crash_add_partition_1") || @@ -5752,19 +5851,24 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, mysql_change_partitions(lpt) || ERROR_INJECT_CRASH("crash_add_partition_3") || abort_and_upgrade_lock(lpt) || /* Always returns 0 */ + ERROR_INJECT_CRASH("crash_add_partition_3") || + get_name_lock(lpt) || + ERROR_INJECT_CRASH("crash_add_partition_4") || + alter_close_tables(lpt) || + ERROR_INJECT_CRASH("crash_add_partition_5") || ((!thd->lex->no_write_to_binlog) && (write_bin_log(thd, FALSE, thd->query, thd->query_length), FALSE)) || - ERROR_INJECT_CRASH("crash_add_partition_4") || + ERROR_INJECT_CRASH("crash_add_partition_6") || write_log_rename_frm(lpt) || (not_completed= FALSE) || - ERROR_INJECT_CRASH("crash_add_partition_5") || + ERROR_INJECT_CRASH("crash_add_partition_7") || ((frm_install= TRUE), FALSE) || mysql_write_frm(lpt, WFRM_INSTALL_SHADOW) || - ERROR_INJECT_CRASH("crash_add_partition_6") || - (close_open_tables_and_downgrade(lpt), FALSE) || + ERROR_INJECT_CRASH("crash_add_partition_8") || (write_log_completed(lpt, FALSE), FALSE) || - ERROR_INJECT_CRASH("crash_add_partition_7")) + ERROR_INJECT_CRASH("crash_add_partition_9") || + (release_name_lock(lpt), FALSE)) { handle_alter_part_error(lpt, not_completed, FALSE, frm_install); DBUG_RETURN(TRUE); @@ -5814,16 +5918,20 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, 5) Lock all partitions in TL_WRITE_ONLY to ensure that no users are still using the old partitioning scheme. Wait until all ongoing users have completed before progressing. - 6) Prepare MyISAM handlers for rename and delete of partitions - 7) Rename the reorged partitions such that they are no longer - used and rename those added to their real new names. - 8) Write bin log - 9) Install the shadow frm file - 10) Wait until all accesses using the old frm file has completed - 11) Drop the reorganised partitions - 12) Remove log entry - 13)Wait until all accesses using the old frm file has completed - 14)Complete query + 6) Get a name lock of the table + 7) Close all tables opened but not yet locked, after this call we are + certain that no other thread is in the lock wait queue or has + opened the table. The name lock will ensure that they are blocked + on the open call. This is achieved also by get_name_lock call. + 8) Close all partitions opened by this thread, but retain name lock. + 9) Write bin log + 10) Prepare handlers for rename and delete of partitions + 11) Rename and drop the reorged partitions such that they are no + longer used and rename those added to their real new names. + 12) Install the shadow frm file + 13) Release the name lock to enable other threads to start using the + table again. + 14) Complete query */ if (write_log_add_change_partition(lpt) || ERROR_INJECT_CRASH("crash_change_partition_1") || @@ -5835,22 +5943,25 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, ERROR_INJECT_CRASH("crash_change_partition_4") || (not_completed= FALSE) || abort_and_upgrade_lock(lpt) || /* Always returns 0 */ - ((!thd->lex->no_write_to_binlog) && - (write_bin_log(thd, FALSE, - thd->query, thd->query_length), FALSE)) || ERROR_INJECT_CRASH("crash_change_partition_5") || - (table->file->extra(HA_EXTRA_PREPARE_FOR_DELETE), FALSE) || + get_name_lock(lpt) || ERROR_INJECT_CRASH("crash_change_partition_6") || - mysql_rename_partitions(lpt) || - ((frm_install= TRUE), FALSE) || + alter_close_tables(lpt) || ERROR_INJECT_CRASH("crash_change_partition_7") || - mysql_write_frm(lpt, WFRM_INSTALL_SHADOW) || + ((!thd->lex->no_write_to_binlog) && + (write_bin_log(thd, FALSE, + thd->query, thd->query_length), FALSE)) || ERROR_INJECT_CRASH("crash_change_partition_8") || - (close_open_tables_and_downgrade(lpt), FALSE) || + mysql_write_frm(lpt, WFRM_INSTALL_SHADOW) || ERROR_INJECT_CRASH("crash_change_partition_9") || - (write_log_completed(lpt, FALSE), FALSE) || + mysql_drop_partitions(lpt) || ERROR_INJECT_CRASH("crash_change_partition_10") || - (mysql_wait_completed_table(lpt, table), FALSE)) + mysql_rename_partitions(lpt) || + ((frm_install= TRUE), FALSE) || + ERROR_INJECT_CRASH("crash_change_partition_11") || + (write_log_completed(lpt, FALSE), FALSE) || + ERROR_INJECT_CRASH("crash_change_partition_12") || + (release_name_lock(lpt), FALSE)) { handle_alter_part_error(lpt, not_completed, FALSE, frm_install); DBUG_RETURN(TRUE); diff --git a/sql/sql_partition.h b/sql/sql_partition.h index fd2c474236f..7b07fd2eb53 100644 --- a/sql/sql_partition.h +++ b/sql/sql_partition.h @@ -70,7 +70,7 @@ bool fix_partition_func(THD *thd, const char *name, TABLE *table, bool create_table_ind); char *generate_partition_syntax(partition_info *part_info, uint *buf_length, bool use_sql_alloc, - bool write_all); + bool show_partition_options); bool partition_key_modified(TABLE *table, List<Item> &fields); void get_partition_set(const TABLE *table, byte *buf, const uint index, const key_range *key_spec, diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc index 01faae22c57..c15b484f448 100644 --- a/sql/sql_plugin.cc +++ b/sql/sql_plugin.cc @@ -23,12 +23,18 @@ extern struct st_mysql_plugin *mysqld_builtins[]; char *opt_plugin_dir_ptr; char opt_plugin_dir[FN_REFLEN]; -LEX_STRING plugin_type_names[]= +LEX_STRING plugin_type_names[MYSQL_MAX_PLUGIN_TYPE_NUM]= { { (char *)STRING_WITH_LEN("UDF") }, { (char *)STRING_WITH_LEN("STORAGE ENGINE") }, { (char *)STRING_WITH_LEN("FTPARSER") } }; + +plugin_type_init plugin_type_initialize[MYSQL_MAX_PLUGIN_TYPE_NUM]= +{ + 0,ha_initialize_handlerton,0 +}; + static const char *plugin_interface_version_sym= "_mysql_plugin_interface_version_"; static const char *sizeof_st_plugin_sym= @@ -41,8 +47,8 @@ static int min_plugin_interface_version= 0x0000; static int min_plugin_info_interface_version[MYSQL_MAX_PLUGIN_TYPE_NUM]= { 0x0000, - 0x0000, - 0x0000 + MYSQL_HANDLERTON_INTERFACE_VERSION, + MYSQL_FTPARSER_INTERFACE_VERSION }; static int cur_plugin_info_interface_version[MYSQL_MAX_PLUGIN_TYPE_NUM]= { @@ -50,6 +56,7 @@ static int cur_plugin_info_interface_version[MYSQL_MAX_PLUGIN_TYPE_NUM]= MYSQL_HANDLERTON_INTERFACE_VERSION, MYSQL_FTPARSER_INTERFACE_VERSION }; + static DYNAMIC_ARRAY plugin_dl_array; static DYNAMIC_ARRAY plugin_array; static HASH plugin_hash[MYSQL_MAX_PLUGIN_TYPE_NUM]; @@ -522,27 +529,15 @@ static int plugin_initialize(struct st_plugin_int *plugin) { sql_print_error("Plugin '%s' init function returned error.", plugin->name.str); - DBUG_PRINT("warning", ("Plugin '%s' init function returned error.", - plugin->name.str)); goto err; } } - - switch (plugin->plugin->type) + if (plugin_type_initialize[plugin->plugin->type] && + (*plugin_type_initialize[plugin->plugin->type])(plugin)) { - case MYSQL_STORAGE_ENGINE_PLUGIN: - if (ha_initialize_handlerton(plugin)) - { - sql_print_error("Plugin '%s' handlerton init returned error.", - plugin->name.str); - DBUG_PRINT("warning", ("Plugin '%s' handlerton init returned error.", - plugin->name.str)); - goto err; - } - break; - - default: - break; + sql_print_error("Plugin '%s' registration as a %s failed.", + plugin->name.str, plugin_type_names[plugin->plugin->type]); + goto err; } DBUG_RETURN(0); @@ -554,14 +549,14 @@ static int plugin_finalize(THD *thd, struct st_plugin_int *plugin) { int rc; DBUG_ENTER("plugin_finalize"); - + if (plugin->ref_count) { push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 0, "Plugin is busy and will be uninstalled on shutdown"); goto err; } - + switch (plugin->plugin->type) { case MYSQL_STORAGE_ENGINE_PLUGIN: @@ -591,7 +586,7 @@ static int plugin_finalize(THD *thd, struct st_plugin_int *plugin) goto err; } } - + DBUG_RETURN(0); err: DBUG_RETURN(1); @@ -676,7 +671,7 @@ int plugin_init(void) get_hash_key, NULL, 0)) goto err; } - + /* Register all the built-in plugins */ for (builtins= mysqld_builtins; *builtins; builtins++) { @@ -684,6 +679,12 @@ int plugin_init(void) { if (plugin_register_builtin(plugin)) goto err; + struct st_plugin_int *tmp=dynamic_element(&plugin_array, + plugin_array.elements-1, + struct st_plugin_int *); + if (plugin_initialize(tmp)) + goto err; + tmp->state= PLUGIN_IS_READY; } } @@ -758,6 +759,7 @@ void plugin_load(void) } table= tables.table; init_read_record(&read_record_info, new_thd, table, NULL, 1, 0); + table->use_all_columns(); while (!(error= read_record_info.read_record(&read_record_info))) { DBUG_PRINT("info", ("init plugin record")); @@ -841,6 +843,7 @@ my_bool mysql_install_plugin(THD *thd, LEX_STRING *name, LEX_STRING *dl) tmp->state= PLUGIN_IS_READY; + table->use_all_columns(); restore_record(table, s->default_values); table->field[0]->store(name->str, name->length, system_charset_info); table->field[1]->store(dl->str, dl->length, files_charset_info); @@ -897,8 +900,8 @@ my_bool mysql_uninstall_plugin(THD *thd, LEX_STRING *name) else plugin->state= PLUGIN_IS_DELETED; + table->use_all_columns(); table->field[0]->store(name->str, name->length, system_charset_info); - table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS); if (! table->file->index_read_idx(table->record[0], 0, (byte *)table->field[0]->ptr, table->key_info[0].key_length, diff --git a/sql/sql_plugin.h b/sql/sql_plugin.h index 672db105cd1..b013beaba1f 100644 --- a/sql/sql_plugin.h +++ b/sql/sql_plugin.h @@ -58,9 +58,12 @@ struct st_plugin_int struct st_mysql_plugin *plugin; struct st_plugin_dl *plugin_dl; enum enum_plugin_state state; - uint ref_count; /* number of threads using the plugin */ + uint ref_count; /* number of threads using the plugin */ + void *data; /* plugin type specific, e.g. handlerton */ }; +typedef int (*plugin_type_init)(struct st_plugin_int *); + extern char *opt_plugin_dir_ptr; extern char opt_plugin_dir[FN_REFLEN]; extern LEX_STRING plugin_type_names[]; diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 14bcb437337..078c7e01d5a 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -621,6 +621,7 @@ static void setup_one_conversion_function(THD *thd, Item_param *param, param->value.cs_info.character_set_of_placeholder= &my_charset_bin; param->value.cs_info.character_set_client= thd->variables.character_set_client; + DBUG_ASSERT(thd->variables.character_set_client); param->value.cs_info.final_character_set_of_str_value= &my_charset_bin; param->item_type= Item::STRING_ITEM; param->item_result_type= STRING_RESULT; @@ -1066,7 +1067,7 @@ static bool mysql_test_insert(Prepared_statement *stmt, its.rewind(); if (table_list->lock_type == TL_WRITE_DELAYED && - !(table_list->table->file->table_flags() & HA_CAN_INSERT_DELAYED)) + !(table_list->table->file->ha_table_flags() & HA_CAN_INSERT_DELAYED)) { my_error(ER_ILLEGAL_HA, MYF(0), (table_list->view ? table_list->view_name.str : @@ -1081,7 +1082,7 @@ static bool mysql_test_insert(Prepared_statement *stmt, my_error(ER_WRONG_VALUE_COUNT_ON_ROW, MYF(0), counter); goto error; } - if (setup_fields(thd, 0, *values, 0, 0, 0)) + if (setup_fields(thd, 0, *values, MARK_COLUMNS_NONE, 0, 0)) goto error; } } @@ -1168,7 +1169,7 @@ static int mysql_test_update(Prepared_statement *stmt, table_list->register_want_access(want_privilege); #endif thd->lex->select_lex.no_wrap_view_item= TRUE; - res= setup_fields(thd, 0, select->item_list, 1, 0, 0); + res= setup_fields(thd, 0, select->item_list, MARK_COLUMNS_READ, 0, 0); thd->lex->select_lex.no_wrap_view_item= FALSE; if (res) goto error; @@ -1179,7 +1180,7 @@ static int mysql_test_update(Prepared_statement *stmt, (SELECT_ACL & ~table_list->table->grant.privilege); table_list->register_want_access(SELECT_ACL); #endif - if (setup_fields(thd, 0, stmt->lex->value_list, 0, 0, 0)) + if (setup_fields(thd, 0, stmt->lex->value_list, MARK_COLUMNS_NONE, 0, 0)) goto error; /* TODO: here we should send types of placeholders to the client. */ DBUG_RETURN(0); @@ -1333,7 +1334,7 @@ static bool mysql_test_do_fields(Prepared_statement *stmt, if (open_and_lock_tables(thd, tables)) DBUG_RETURN(TRUE); - DBUG_RETURN(setup_fields(thd, 0, *values, 0, 0, 0)); + DBUG_RETURN(setup_fields(thd, 0, *values, MARK_COLUMNS_NONE, 0, 0)); } diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index 918c9f507e2..baea34f2d0b 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -1295,12 +1295,12 @@ int cmp_master_pos(const char* log_file_name1, ulonglong log_pos1, bool mysql_show_binlog_events(THD* thd) { Protocol *protocol= thd->protocol; - DBUG_ENTER("mysql_show_binlog_events"); List<Item> field_list; const char *errmsg = 0; bool ret = TRUE; IO_CACHE log; File file = -1; + DBUG_ENTER("mysql_show_binlog_events"); Log_event::init_show_field_list(&field_list); if (protocol->send_fields(&field_list, @@ -1354,12 +1354,12 @@ bool mysql_show_binlog_events(THD* thd) pthread_mutex_lock(log_lock); /* - open_binlog() sought to position 4. - Read the first event in case it's a Format_description_log_event, to - know the format. If there's no such event, we are 3.23 or 4.x. This - code, like before, can't read 3.23 binlogs. - This code will fail on a mixed relay log (one which has Format_desc then - Rotate then Format_desc). + open_binlog() sought to position 4. + Read the first event in case it's a Format_description_log_event, to + know the format. If there's no such event, we are 3.23 or 4.x. This + code, like before, can't read 3.23 binlogs. + This code will fail on a mixed relay log (one which has Format_desc then + Rotate then Format_desc). */ ev = Log_event::read_log_event(&log,(pthread_mutex_t*)0,description_event); @@ -1383,7 +1383,8 @@ bool mysql_show_binlog_events(THD* thd) } for (event_count = 0; - (ev = Log_event::read_log_event(&log,(pthread_mutex_t*)0,description_event)); ) + (ev = Log_event::read_log_event(&log,(pthread_mutex_t*) 0, + description_event)); ) { if (event_count >= limit_start && ev->net_send(protocol, linfo.log_file_name, pos)) diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 2212371db92..2cd14c3f5e6 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -337,12 +337,13 @@ JOIN::prepare(Item ***rref_pointer_array, /* Check that all tables, fields, conds and order are ok */ if ((!(select_options & OPTION_SETUP_TABLES_DONE) && - setup_tables(thd, &select_lex->context, join_list, - tables_list, &conds, &select_lex->leaf_tables, - FALSE)) || + setup_tables_and_check_access(thd, &select_lex->context, join_list, + tables_list, + &select_lex->leaf_tables, FALSE, + SELECT_ACL)) || setup_wild(thd, tables_list, fields_list, &all_fields, wild_num) || select_lex->setup_ref_array(thd, og_num) || - setup_fields(thd, (*rref_pointer_array), fields_list, 1, + setup_fields(thd, (*rref_pointer_array), fields_list, MARK_COLUMNS_READ, &all_fields, 1) || setup_without_group(thd, (*rref_pointer_array), tables_list, select_lex->leaf_tables, fields_list, @@ -458,13 +459,6 @@ JOIN::prepare(Item ***rref_pointer_array, goto err; /* purecov: inspected */ } } -#ifdef NOT_NEEDED - else if (!group_list && procedure->flags & PROC_GROUP) - { - my_message(ER_NO_GROUP_FOR_PROC, MYF(0)); - goto err; - } -#endif if (order && (procedure->flags & PROC_NO_SORT)) { /* purecov: inspected */ my_message(ER_ORDER_WITH_PROC, ER(ER_ORDER_WITH_PROC), @@ -1004,11 +998,8 @@ JOIN::optimize() */ if (need_tmp || select_distinct || group_list || order) { - for (uint i_h = const_tables; i_h < tables; i_h++) - { - TABLE* table_h = join_tab[i_h].table; - table_h->file->ha_retrieve_all_pk(); - } + for (uint i = const_tables; i < tables; i++) + join_tab[i].table->prepare_for_position(); } DBUG_EXECUTE("info",TEST_join(this);); @@ -1075,7 +1066,7 @@ JOIN::optimize() tmp_table_param.hidden_field_count= (all_fields.elements - fields_list.elements); - if (!(exec_tmp_table1 = + if (!(exec_tmp_table1= create_tmp_table(thd, &tmp_table_param, all_fields, ((!simple_group && !procedure && !(test_flags & TEST_NO_KEY_GROUP)) ? @@ -1791,9 +1782,7 @@ JOIN::destroy() { JOIN_TAB *tab, *end; for (tab= join_tab, end= tab+tables ; tab != end ; tab++) - { tab->cleanup(); - } } tmp_join->tmp_join= 0; tmp_table_param.copy_field=0; @@ -2048,16 +2037,16 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables, COND *conds, s->dependent= tables->dep_tables; s->key_dependent= 0; if (tables->schema_table) - table->file->records= 2; + table->file->stats.records= 2; s->on_expr_ref= &tables->on_expr; if (*s->on_expr_ref) { /* s is the only inner table of an outer join */ #ifdef WITH_PARTITION_STORAGE_ENGINE - if ((!table->file->records || table->no_partitions_used) && !embedding) + if ((!table->file->stats.records || table->no_partitions_used) && !embedding) #else - if (!table->file->records && !embedding) + if (!table->file->stats.records && !embedding) #endif { // Empty table s->dependent= 0; // Ignore LEFT JOIN depend. @@ -2090,10 +2079,10 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables, COND *conds, #else const bool no_partitions_used= FALSE; #endif - if ((table->s->system || table->file->records <= 1 || + if ((table->s->system || table->file->stats.records <= 1 || no_partitions_used) && !s->dependent && - !(table->file->table_flags() & HA_NOT_EXACT_COUNT) && + (table->file->ha_table_flags() & HA_STATS_RECORDS_IS_EXACT) && !table->fulltext_searched) { set_position(join,const_count++,s,(KEYUSE*) 0); @@ -2182,8 +2171,8 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables, COND *conds, // All dep. must be constants if (s->dependent & ~(found_const_table_map)) continue; - if (table->file->records <= 1L && - !(table->file->table_flags() & HA_NOT_EXACT_COUNT) && + if (table->file->stats.records <= 1L && + (table->file->ha_table_flags() & HA_STATS_RECORDS_IS_EXACT) && !table->pos_in_table_list->embedding) { // system table int tmp= 0; @@ -2271,7 +2260,7 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables, COND *conds, continue; } /* Approximate found rows and time to read them */ - s->found_records=s->records=s->table->file->records; + s->found_records=s->records=s->table->file->stats.records; s->read_time=(ha_rows) s->table->file->scan_time(); /* @@ -2581,7 +2570,10 @@ add_key_field(KEY_FIELD **key_fields,uint and_level, Item_func *cond, bool is_const=1; for (uint i=0; i<num_values; i++) - is_const&= value[i]->const_item(); + { + if (!(is_const&= value[i]->const_item())) + break; + } if (is_const) stat[0].const_keys.merge(possible_keys); /* @@ -3211,7 +3203,7 @@ static void optimize_keyuse(JOIN *join, DYNAMIC_ARRAY *keyuse_array) if (map == 1) // Only one table { TABLE *tmp_table=join->all_tables[tablenr]; - keyuse->ref_table_rows= max(tmp_table->file->records, 100); + keyuse->ref_table_rows= max(tmp_table->file->stats.records, 100); } } /* @@ -3256,7 +3248,7 @@ add_group_and_distinct_keys(JOIN *join, JOIN_TAB *join_tab) if (join->group_list) { /* Collect all query fields referenced in the GROUP clause. */ for (cur_group= join->group_list; cur_group; cur_group= cur_group->next) - (*cur_group->item)->walk(&Item::collect_item_field_processor, + (*cur_group->item)->walk(&Item::collect_item_field_processor, 0, (byte*) &indexed_fields); } else if (join->select_distinct) @@ -3265,7 +3257,8 @@ add_group_and_distinct_keys(JOIN *join, JOIN_TAB *join_tab) List_iterator<Item> select_items_it(select_items); Item *item; while ((item= select_items_it++)) - item->walk(&Item::collect_item_field_processor, (byte*) &indexed_fields); + item->walk(&Item::collect_item_field_processor, 0, + (byte*) &indexed_fields); } else return; @@ -3521,7 +3514,7 @@ best_access_path(JOIN *join, if (table->used_keys.is_set(key)) { /* we can use only index tree */ - uint keys_per_block= table->file->block_size/2/ + uint keys_per_block= table->file->stats.block_size/2/ (keyinfo->key_length+table->file->ref_length)+1; tmp= record_count*(tmp+keys_per_block-1)/keys_per_block; } @@ -3665,7 +3658,7 @@ best_access_path(JOIN *join, if (table->used_keys.is_set(key)) { /* we can use only index tree */ - uint keys_per_block= table->file->block_size/2/ + uint keys_per_block= table->file->stats.block_size/2/ (keyinfo->key_length+table->file->ref_length)+1; tmp= record_count*(tmp+keys_per_block-1)/keys_per_block; } @@ -3719,7 +3712,7 @@ best_access_path(JOIN *join, if ((records >= s->found_records || best > s->read_time) && // (1) !(s->quick && best_key && s->quick->index == best_key->key && // (2) best_max_key_part >= s->table->quick_key_parts[best_key->key]) &&// (2) - !((s->table->file->table_flags() & HA_TABLE_SCAN_ON_INDEX) && // (3) + !((s->table->file->ha_table_flags() & HA_TABLE_SCAN_ON_INDEX) && // (3) ! s->table->used_keys.is_clear_all() && best_key) && // (3) !(s->table->force_index && best_key && !s->quick)) // (4) { // Check full join @@ -4519,12 +4512,13 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count, static void calc_used_field_length(THD *thd, JOIN_TAB *join_tab) { uint null_fields,blobs,fields,rec_length; - null_fields=blobs=fields=rec_length=0; - Field **f_ptr,*field; + MY_BITMAP *read_set= join_tab->table->read_set;; + + null_fields= blobs= fields= rec_length=0; for (f_ptr=join_tab->table->field ; (field= *f_ptr) ; f_ptr++) { - if (field->query_id == thd->query_id) + if (bitmap_is_set(read_set, field->field_index)) { uint flags=field->flags; fields++; @@ -4541,7 +4535,7 @@ static void calc_used_field_length(THD *thd, JOIN_TAB *join_tab) rec_length+=sizeof(my_bool); if (blobs) { - uint blob_length=(uint) (join_tab->table->file->mean_rec_length- + uint blob_length=(uint) (join_tab->table->file->stats.mean_rec_length- (join_tab->table->s->reclength- rec_length)); rec_length+=(uint) max(4,blob_length); } @@ -4832,8 +4826,12 @@ bool store_val_in_field(Field *field,Item *item) { bool error; - THD *thd= field->table->in_use; + TABLE *table= field->table; + THD *thd= table->in_use; ha_rows cuted_fields=thd->cuted_fields; + my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, + table->write_set); + /* we should restore old value of count_cuted_fields because store_val_in_field can be called from mysql_insert @@ -4843,6 +4841,7 @@ store_val_in_field(Field *field,Item *item) thd->count_cuted_fields= CHECK_FIELD_WARN; error= item->save_in_field(field, 1); thd->count_cuted_fields= old_count_cuted_fields; + dbug_tmp_restore_column_map(table->write_set, old_map); return error || cuted_fields != thd->cuted_fields; } @@ -5180,7 +5179,8 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) DBUG_RETURN(1); tmp->quick_fix_field(); cond_tab->select_cond= !cond_tab->select_cond ? tmp : - new Item_cond_and(cond_tab->select_cond,tmp); + new Item_cond_and(cond_tab->select_cond, + tmp); if (!cond_tab->select_cond) DBUG_RETURN(1); cond_tab->select_cond->quick_fix_field(); @@ -5485,7 +5485,6 @@ static void make_join_readinfo(JOIN *join, uint options) { uint i; - bool statistics= test(!(join->select_options & SELECT_DESCRIBE)); bool sorted= 1; DBUG_ENTER("make_join_readinfo"); @@ -8175,6 +8174,7 @@ Field *create_tmp_field_for_schema(THD *thd, Item *item, TABLE *table) in this array from_field if field will be created using other field as example, pointer example field will be written here + default_field If field has a default value field, store it here group 1 if we are going to do a relative group by on result modify_item 1 if item->result_field should point to new item. This is relevent for how fill_record() is going to @@ -8193,6 +8193,7 @@ Field *create_tmp_field_for_schema(THD *thd, Item *item, TABLE *table) Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, Item ***copy_func, Field **from_field, + Field **default_field, bool group, bool modify_item, bool table_cant_handle_bit_fields, bool make_copy_field, @@ -8260,7 +8261,7 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, if (orig_type == Item::REF_ITEM && orig_modify) ((Item_ref*)orig_item)->set_result_field(result); if (field->field->eq_def(result)) - result->dflt_field= field->field; + *default_field= field->field; return result; } /* Fall through */ @@ -8293,6 +8294,29 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, } } +/* + Set up column usage bitmaps for a temporary table + + IMPLEMENTATION + For temporary tables, we need one bitmap with all columns set and + a tmp_set bitmap to be used by things like filesort. +*/ + +void setup_tmp_table_column_bitmaps(TABLE *table, byte *bitmaps) +{ + uint field_count= table->s->fields; + bitmap_init(&table->def_read_set, (my_bitmap_map*) bitmaps, field_count, + FALSE); + bitmap_init(&table->tmp_set, + (my_bitmap_map*) (bitmaps+ bitmap_buffer_size(field_count)), + field_count, FALSE); + /* write_set and all_set are copies of read_set */ + table->def_write_set= table->def_read_set; + table->s->all_set= table->def_read_set; + bitmap_set_all(&table->s->all_set); + table->default_column_bitmaps(); +} + /* Create a temp table according to a field list. @@ -8347,9 +8371,9 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, bool use_packed_rows= 0; bool not_all_columns= !(select_options & TMP_TABLE_ALL_COLUMNS); char *tmpname,path[FN_REFLEN]; - byte *pos,*group_buff; + byte *pos, *group_buff, *bitmaps; uchar *null_flags; - Field **reg_field, **from_field; + Field **reg_field, **from_field, **default_field; uint *blob_field; Copy_field *copy=0; KEY *keyinfo; @@ -8359,9 +8383,10 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, uint total_uneven_bit_length= 0; bool force_copy_fields= param->force_copy_fields; DBUG_ENTER("create_tmp_table"); - DBUG_PRINT("enter",("distinct: %d save_sum_fields: %d rows_limit: %lu group: %d", - (int) distinct, (int) save_sum_fields, - (ulong) rows_limit,test(group))); + DBUG_PRINT("enter", + ("distinct: %d save_sum_fields: %d rows_limit: %lu group: %d", + (int) distinct, (int) save_sum_fields, + (ulong) rows_limit,test(group))); statistic_increment(thd->status_var.created_tmp_tables, &LOCK_status); @@ -8419,6 +8444,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, &table, sizeof(*table), &share, sizeof(*share), ®_field, sizeof(Field*) * (field_count+1), + &default_field, sizeof(Field*) * (field_count), &blob_field, sizeof(uint)*(field_count+1), &from_field, sizeof(Field*)*field_count, ©_func, sizeof(*copy_func)*(copy_func_count+1), @@ -8428,8 +8454,9 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, ¶m->start_recinfo, sizeof(*param->recinfo)*(field_count*2+4), &tmpname, (uint) strlen(path)+1, - &group_buff, group && ! using_unique_constraint ? - param->group_length : 0, + &group_buff, (group && ! using_unique_constraint ? + param->group_length : 0), + &bitmaps, bitmap_buffer_size(field_count)*2, NullS)) { bitmap_lock_clear_bit(&temp_pool, temp_pool_slot); @@ -8448,6 +8475,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, bzero((char*) table,sizeof(*table)); bzero((char*) reg_field,sizeof(Field*)*(field_count+1)); + bzero((char*) default_field, sizeof(Field*) * (field_count)); bzero((char*) from_field,sizeof(Field*)*field_count); table->mem_root= own_root; @@ -8516,7 +8544,8 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, { Field *new_field= create_tmp_field(thd, table, arg, arg->type(), ©_func, - tmp_from_field, group != 0,not_all_columns, + tmp_from_field, &default_field[fieldnr], + group != 0,not_all_columns, distinct, 0, param->convert_blob_length); if (!new_field) @@ -8525,12 +8554,11 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, reclength+=new_field->pack_length(); if (new_field->flags & BLOB_FLAG) { - *blob_field++= (uint) (reg_field - table->field); + *blob_field++= fieldnr; blob_count++; } if (new_field->type() == FIELD_TYPE_BIT) total_uneven_bit_length+= new_field->field_length & 7; - new_field->field_index= (uint) (reg_field - table->field); *(reg_field++)= new_field; if (new_field->real_type() == MYSQL_TYPE_STRING || new_field->real_type() == MYSQL_TYPE_VARCHAR) @@ -8550,8 +8578,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, */ (*argp)->maybe_null=1; } - new_field->query_id= thd->query_id; - new_field->fieldnr= ++fieldnr; + new_field->field_index= fieldnr++; } } } @@ -8573,7 +8600,8 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, Field *new_field= (param->schema_table) ? create_tmp_field_for_schema(thd, item, table) : create_tmp_field(thd, table, item, type, ©_func, - tmp_from_field, group != 0, + tmp_from_field, &default_field[fieldnr], + group != 0, !force_copy_fields && (not_all_columns || group !=0), item->marker == 4, force_copy_fields, @@ -8595,7 +8623,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, total_uneven_bit_length+= new_field->field_length & 7; if (new_field->flags & BLOB_FLAG) { - *blob_field++= (uint) (reg_field - table->field); + *blob_field++= fieldnr; blob_count++; } if (item->marker == 4 && item->maybe_null) @@ -8603,10 +8631,8 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, group_null_items++; new_field->flags|= GROUP_FLAG; } - new_field->query_id= thd->query_id; - new_field->fieldnr= ++fieldnr; - new_field->field_index= (uint) (reg_field - table->field); - *(reg_field++) =new_field; + new_field->field_index= fieldnr++; + *(reg_field++)= new_field; } if (!--hidden_field_count) { @@ -8619,12 +8645,14 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, We need to update hidden_field_count as we may have stored group functions with constant arguments */ - param->hidden_field_count= (uint) (reg_field - table->field); + param->hidden_field_count= fieldnr; null_count= 0; } } + DBUG_ASSERT(fieldnr == (uint) (reg_field - table->field)); DBUG_ASSERT(field_count >= (uint) (reg_field - table->field)); - field_count= (uint) (reg_field - table->field); + field_count= fieldnr; + *reg_field= 0; *blob_field= 0; // End marker share->fields= field_count; @@ -8648,6 +8676,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, if (!table->file) goto err; + if (!using_unique_constraint) reclength+= group_null_items; // null flag is stored separately @@ -8685,6 +8714,8 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, } copy_func[0]=0; // End marker + setup_tmp_table_column_bitmaps(table, bitmaps); + recinfo=param->start_recinfo; null_flags=(uchar*) table->record[0]; pos=table->record[0]+ null_pack_length; @@ -8742,30 +8773,31 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, } field->reset(); - if (field->dflt_field && field->dflt_field->ptr) + /* + Test if there is a default field value. The test for ->ptr is to skip + 'offset' fields generated by initalize_tables + */ + if (default_field[i] && default_field[i]->ptr) { /* - field->dflt_field is set only in the cases when 'field' can - inherit the default value that is defined for the field referred - by the Item_field object from which 'field' has been created. - For a field created not from a Item_field item dflt_field == 0. + default_field[i] is set only in the cases when 'field' can + inherit the default value that is defined for the field referred + by the Item_field object from which 'field' has been created. */ my_ptrdiff_t diff; - Field *orig_field= field->dflt_field; + Field *orig_field= default_field[i]; /* Get the value from default_values */ diff= (my_ptrdiff_t) (orig_field->table->s->default_values- orig_field->table->record[0]); orig_field->move_field_offset(diff); // Points now at default_values - bool is_null= orig_field->is_real_null(); - char *from= orig_field->ptr; - orig_field->move_field_offset(-diff); // Back to record[0] - if (is_null) + if (orig_field->is_real_null()) field->set_null(); else { field->set_notnull(); - memcpy(field->ptr, from, field->pack_length()); + memcpy(field->ptr, orig_field->ptr, field->pack_length()); } + orig_field->move_field_offset(-diff); // Back to record[0] } if (from_field[i]) @@ -8948,8 +8980,6 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, if (open_tmp_table(table)) goto err; - table->file->ha_set_all_bits_in_read_set(); - table->file->ha_set_all_bits_in_write_set(); thd->mem_root= mem_root_save; DBUG_RETURN(table); @@ -8995,22 +9025,28 @@ TABLE *create_virtual_tmp_table(THD *thd, List<create_field> &field_list) uint record_length= 0; uint null_count= 0; /* number of columns which may be null */ uint null_pack_length; /* NULL representation array length */ + uint *blob_field; + byte *bitmaps; + TABLE *table; TABLE_SHARE *share; - /* Create the table and list of all fields */ - TABLE *table= (TABLE*) thd->calloc(sizeof(*table)+sizeof(*share)); - field= (Field**) thd->alloc((field_count + 1) * sizeof(Field*)); - if (!table || !field) + + if (!multi_alloc_root(thd->mem_root, + &table, sizeof(*table), + &share, sizeof(*share), + &field, (field_count + 1) * sizeof(Field*), + &blob_field, (field_count+1) *sizeof(uint), + &bitmaps, bitmap_buffer_size(field_count)*2, + NullS)) return 0; + bzero(table, sizeof(*table)); + bzero(share, sizeof(*share)); table->field= field; - table->s= share= (TABLE_SHARE*) (table+1); + table->s= share; + share->blob_field= blob_field; share->fields= field_count; - - if (!(share->blob_field= (uint*)thd->alloc((field_list.elements + 1) * - sizeof(uint)))) - return 0; - share->blob_ptr_size= mi_portable_sizeof_char_ptr; + setup_tmp_table_column_bitmaps(table, bitmaps); /* Create all fields and calculate the total length of record */ List_iterator_fast<create_field> it(field_list); @@ -9297,8 +9333,8 @@ bool create_myisam_from_heap(THD *thd, TABLE *table, TMP_TABLE_PARAM *param, To use start_bulk_insert() (which is new in 4.1) we need to find all places where a corresponding end_bulk_insert() should be put. */ - table->file->info(HA_STATUS_VARIABLE); /* update table->file->records */ - new_table.file->start_bulk_insert(table->file->records); + table->file->info(HA_STATUS_VARIABLE); /* update table->file->stats.records */ + new_table.file->ha_start_bulk_insert(table->file->stats.records); #else /* HA_EXTRA_WRITE_CACHE can stay until close, no need to disable it */ new_table.file->extra(HA_EXTRA_WRITE_CACHE); @@ -9312,11 +9348,11 @@ bool create_myisam_from_heap(THD *thd, TABLE *table, TMP_TABLE_PARAM *param, */ while (!table->file->rnd_next(new_table.record[1])) { - if ((write_err=new_table.file->ha_write_row(new_table.record[1]))) + if ((write_err= new_table.file->write_row(new_table.record[1]))) goto err; } /* copy row that filled HEAP table */ - if ((write_err=new_table.file->ha_write_row(table->record[0]))) + if ((write_err=new_table.file->write_row(table->record[0]))) { if (write_err != HA_ERR_FOUND_DUPP_KEY && write_err != HA_ERR_FOUND_DUPP_UNIQUE || !ignore_last_dupp_key_error) @@ -9333,6 +9369,7 @@ bool create_myisam_from_heap(THD *thd, TABLE *table, TMP_TABLE_PARAM *param, *table= new_table; *table->s= share; table->file->change_table_ptr(table, table->s); + table->use_all_columns(); if (save_proc_info) thd->proc_info= (!strcmp(save_proc_info,"Copying to tmp table") ? "Copying to tmp table on disk" : save_proc_info); @@ -10163,7 +10200,7 @@ join_read_const(JOIN_TAB *tab) if (table->status & STATUS_GARBAGE) // If first read { table->status= 0; - if (cp_buffer_from_ref(tab->join->thd, &tab->ref)) + if (cp_buffer_from_ref(tab->join->thd, table, &tab->ref)) error=HA_ERR_KEY_NOT_FOUND; else { @@ -10236,7 +10273,7 @@ join_read_always_key(JOIN_TAB *tab) { table->file->ha_index_init(tab->ref.key, tab->sorted); } - if (cp_buffer_from_ref(tab->join->thd, &tab->ref)) + if (cp_buffer_from_ref(tab->join->thd, table, &tab->ref)) return -1; if ((error=table->file->index_read(table->record[0], tab->ref.key_buff, @@ -10263,7 +10300,7 @@ join_read_last_key(JOIN_TAB *tab) if (!table->file->inited) table->file->ha_index_init(tab->ref.key, tab->sorted); - if (cp_buffer_from_ref(tab->join->thd, &tab->ref)) + if (cp_buffer_from_ref(tab->join->thd, table, &tab->ref)) return -1; if ((error=table->file->index_read_last(table->record[0], tab->ref.key_buff, @@ -10437,8 +10474,9 @@ join_ft_read_first(JOIN_TAB *tab) if (!table->file->inited) table->file->ha_index_init(tab->ref.key, 1); #if NOT_USED_YET - if (cp_buffer_from_ref(tab->join->thd, &tab->ref)) // as ft-key doesn't use store_key's - return -1; // see also FT_SELECT::init() + /* as ft-key doesn't use store_key's, see also FT_SELECT::init() */ + if (cp_buffer_from_ref(tab->join->thd, table, &tab->ref)) + return -1; #endif table->file->ft_init(); @@ -10545,7 +10583,7 @@ end_send(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), if ((join->tables == 1) && !join->tmp_table && !join->sort_and_group && !join->send_group_parts && !join->having && !jt->select_cond && !(jt->select && jt->select->quick) && - !(jt->table->file->table_flags() & HA_NOT_EXACT_COUNT) && + (jt->table->file->ha_table_flags() & HA_STATS_RECORDS_IS_EXACT) && (jt->ref.key < 0)) { /* Join over all rows in table; Return number of found rows */ @@ -10561,7 +10599,7 @@ end_send(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), else { table->file->info(HA_STATUS_VARIABLE); - join->send_records = table->file->records; + join->send_records= table->file->stats.records; } } else @@ -10737,7 +10775,7 @@ end_write(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), { int error; join->found_records++; - if ((error=table->file->ha_write_row(table->record[0]))) + if ((error=table->file->write_row(table->record[0]))) { if (error == HA_ERR_FOUND_DUPP_KEY || error == HA_ERR_FOUND_DUPP_UNIQUE) @@ -10799,8 +10837,8 @@ end_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), { /* Update old record */ restore_record(table,record[1]); update_tmptable_sum_func(join->sum_funcs,table); - if ((error=table->file->ha_update_row(table->record[1], - table->record[0]))) + if ((error=table->file->update_row(table->record[1], + table->record[0]))) { table->file->print_error(error,MYF(0)); /* purecov: inspected */ DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */ @@ -10823,7 +10861,7 @@ end_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), } init_tmptable_sum_functions(join->sum_funcs); copy_funcs(join->tmp_table_param.items_to_copy); - if ((error=table->file->ha_write_row(table->record[0]))) + if ((error=table->file->write_row(table->record[0]))) { if (create_myisam_from_heap(join->thd, table, &join->tmp_table_param, error, 0)) @@ -10859,7 +10897,7 @@ end_unique_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), copy_fields(&join->tmp_table_param); // Groups are copied twice. copy_funcs(join->tmp_table_param.items_to_copy); - if (!(error=table->file->ha_write_row(table->record[0]))) + if (!(error=table->file->write_row(table->record[0]))) join->send_records++; // New group else { @@ -10868,15 +10906,15 @@ end_unique_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), table->file->print_error(error,MYF(0)); /* purecov: inspected */ DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */ } - if (table->file->rnd_pos(table->record[1],table->file->dupp_ref)) + if (table->file->rnd_pos(table->record[1],table->file->dup_ref)) { table->file->print_error(error,MYF(0)); /* purecov: inspected */ DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */ } restore_record(table,record[1]); update_tmptable_sum_func(join->sum_funcs,table); - if ((error=table->file->ha_update_row(table->record[1], - table->record[0]))) + if ((error=table->file->update_row(table->record[1], + table->record[0]))) { table->file->print_error(error,MYF(0)); /* purecov: inspected */ DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */ @@ -10919,7 +10957,7 @@ end_write_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), join->sum_funcs_end[send_group_parts]); if (!join->having || join->having->val_int()) { - int error= table->file->ha_write_row(table->record[0]); + int error= table->file->write_row(table->record[0]); if (error && create_myisam_from_heap(join->thd, table, &join->tmp_table_param, error, 0)) @@ -11416,6 +11454,7 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit, */ if (!select->quick->reverse_sorted()) { + QUICK_SELECT_DESC *tmp; int quick_type= select->quick->get_type(); if (quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_MERGE || quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT || @@ -11424,8 +11463,8 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit, DBUG_RETURN(0); // Use filesort /* ORDER BY range_key DESC */ - QUICK_SELECT_DESC *tmp=new QUICK_SELECT_DESC((QUICK_RANGE_SELECT*)(select->quick), - used_key_parts); + tmp= new QUICK_SELECT_DESC((QUICK_RANGE_SELECT*)(select->quick), + used_key_parts); if (!tmp || tmp->error) { delete tmp; @@ -11465,7 +11504,7 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit, resolved with a key; This is because filesort() is usually faster than retrieving all rows through an index. */ - if (select_limit >= table->file->records) + if (select_limit >= table->file->stats.records) { keys= *table->file->keys_to_use_for_scanning(); keys.merge(table->used_keys); @@ -11606,7 +11645,8 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order, if (table->s->tmp_table) table->file->info(HA_STATUS_VARIABLE); // Get record count table->sort.found_records=filesort(thd, table,sortorder, length, - select, filesort_limit, &examined_rows); + select, filesort_limit, 0, + &examined_rows); tab->records= table->sort.found_records; // For SQL_CALC_ROWS if (select) { @@ -11740,7 +11780,7 @@ remove_duplicates(JOIN *join, TABLE *entry,List<Item> &fields, Item *having) entry->file->info(HA_STATUS_VARIABLE); if (entry->s->db_type == &heap_hton || (!entry->s->blob_fields && - ((ALIGN_SIZE(reclength) + HASH_OVERHEAD) * entry->file->records < + ((ALIGN_SIZE(reclength) + HASH_OVERHEAD) * entry->file->stats.records < thd->variables.sortbuff_size))) error=remove_dup_with_hash_index(join->thd, entry, field_count, first_field, @@ -11787,7 +11827,7 @@ static int remove_dup_with_compare(THD *thd, TABLE *table, Field **first_field, } if (having && !having->val_int()) { - if ((error=file->ha_delete_row(record))) + if ((error=file->delete_row(record))) goto err; error=file->rnd_next(record); continue; @@ -11814,7 +11854,7 @@ static int remove_dup_with_compare(THD *thd, TABLE *table, Field **first_field, } if (compare_record(table, first_field) == 0) { - if ((error=file->ha_delete_row(record))) + if ((error=file->delete_row(record))) goto err; } else if (!found) @@ -11861,7 +11901,7 @@ static int remove_dup_with_hash_index(THD *thd, TABLE *table, if (!my_multi_malloc(MYF(MY_WME), &key_buffer, (uint) ((key_length + extra_length) * - (long) file->records), + (long) file->stats.records), &field_lengths, (uint) (field_count*sizeof(*field_lengths)), NullS)) @@ -11883,7 +11923,7 @@ static int remove_dup_with_hash_index(THD *thd, TABLE *table, extra_length= ALIGN_SIZE(key_length)-key_length; } - if (hash_init(&hash, &my_charset_bin, (uint) file->records, 0, + if (hash_init(&hash, &my_charset_bin, (uint) file->stats.records, 0, key_length, (hash_get_key) 0, 0, 0)) { my_free((char*) key_buffer,MYF(0)); @@ -11911,7 +11951,7 @@ static int remove_dup_with_hash_index(THD *thd, TABLE *table, } if (having && !having->val_int()) { - if ((error=file->ha_delete_row(record))) + if ((error=file->delete_row(record))) goto err; continue; } @@ -11928,7 +11968,7 @@ static int remove_dup_with_hash_index(THD *thd, TABLE *table, if (hash_search(&hash, org_key_pos, key_length)) { /* Duplicated found ; Remove the row */ - if ((error=file->ha_delete_row(record))) + if ((error=file->delete_row(record))) goto err; } else @@ -12031,14 +12071,14 @@ join_init_cache(THD *thd,JOIN_TAB *tables,uint table_count) for (i=0 ; i < table_count ; i++) { uint null_fields=0,used_fields; - Field **f_ptr,*field; + MY_BITMAP *read_set= tables[i].table->read_set; for (f_ptr=tables[i].table->field,used_fields=tables[i].used_fields ; used_fields ; f_ptr++) { field= *f_ptr; - if (field->query_id == thd->query_id) + if (bitmap_is_set(read_set, field->field_index)) { used_fields--; length+=field->fill_cache_field(copy); @@ -12237,7 +12277,8 @@ cmp_buffer_with_ref(JOIN_TAB *tab) { memcpy(tab->ref.key_buff2, tab->ref.key_buff, tab->ref.key_length); } - if ((tab->ref.key_err= cp_buffer_from_ref(tab->join->thd, &tab->ref)) || + if ((tab->ref.key_err= cp_buffer_from_ref(tab->join->thd, tab->table, + &tab->ref)) || diff) return 1; return memcmp(tab->ref.key_buff2, tab->ref.key_buff, tab->ref.key_length) @@ -12246,20 +12287,24 @@ cmp_buffer_with_ref(JOIN_TAB *tab) bool -cp_buffer_from_ref(THD *thd, TABLE_REF *ref) +cp_buffer_from_ref(THD *thd, TABLE *table, TABLE_REF *ref) { enum enum_check_fields save_count_cuted_fields= thd->count_cuted_fields; thd->count_cuted_fields= CHECK_FIELD_IGNORE; + my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set); + bool result= 0; + for (store_key **copy=ref->key_copy ; *copy ; copy++) { if ((*copy)->copy() & 1) { - thd->count_cuted_fields= save_count_cuted_fields; - return 1; // Something went wrong + result= 1; + break; } } thd->count_cuted_fields= save_count_cuted_fields; - return 0; + dbug_tmp_restore_column_map(table->write_set, old_map); + return result; } @@ -12309,6 +12354,8 @@ find_order_in_list(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables, Item::Type order_item_type; Item **select_item; /* The corresponding item from the SELECT clause. */ Field *from_field; /* The corresponding field from the FROM clause. */ + uint counter; + bool unaliased; /* Local SP variables may be int but are expressions, not positions. @@ -12330,8 +12377,6 @@ find_order_in_list(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables, return FALSE; } /* Lookup the current GROUP/ORDER field in the SELECT clause. */ - uint counter; - bool unaliased; select_item= find_item_in_list(order_item, fields, &counter, REPORT_EXCEPT_NOT_FOUND, &unaliased); if (!select_item) @@ -12543,11 +12588,11 @@ setup_new_fields(THD *thd, List<Item> &fields, List<Item> &all_fields, ORDER *new_field) { Item **item; - DBUG_ENTER("setup_new_fields"); - - thd->set_query_id=1; // Not really needed, but... uint counter; bool not_used; + DBUG_ENTER("setup_new_fields"); + + thd->mark_used_columns= MARK_COLUMNS_READ; // Not really needed, but... for (; new_field ; new_field= new_field->next) { if ((item= find_item_in_list(*new_field->item, fields, &counter, @@ -13775,7 +13820,7 @@ int JOIN::rollup_write_data(uint idx, TABLE *table) item->save_in_result_field(1); } copy_sum_funcs(sum_funcs_end[i+1], sum_funcs_end[i]); - if ((error= table->file->ha_write_row(table->record[0]))) + if ((error= table->file->write_row(table->record[0]))) { if (create_myisam_from_heap(thd, table, &tmp_table_param, error, 0)) @@ -13833,6 +13878,10 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, thd->server_status&= ~(SERVER_QUERY_NO_INDEX_USED | SERVER_QUERY_NO_GOOD_INDEX_USED); join->unit->offset_limit_cnt= 0; + /* + NOTE: the number/types of items pushed into item_list must be in sync with + EXPLAIN column types as they're "defined" in THD::send_explain_fields() + */ if (message) { item_list.push_back(new Item_int((int32) @@ -13972,11 +14021,9 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, if (!table->derived_select_number && (part_info= table->part_info)) { - char parts_buff[128]; - String parts_str(parts_buff,sizeof(parts_buff),cs); - make_used_partitions_str(part_info, &parts_str); - item_list.push_back(new Item_string(parts_str.ptr(), - parts_str.length(), cs)); + Item_string *item_str= new Item_string(cs); + make_used_partitions_str(part_info, &item_str->str_value); + item_list.push_back(item_str); } else item_list.push_back(item_null); diff --git a/sql/sql_select.h b/sql/sql_select.h index 459d2ff89a8..6292977c209 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -56,16 +56,16 @@ typedef struct st_table_ref */ key_part_map null_rejecting; table_map depend_map; // Table depends on these tables. - byte *null_ref_key; // null byte position in the key_buf. - // used for REF_OR_NULL optimization. + /* null byte position in the key_buf. Used for REF_OR_NULL optimization */ + byte *null_ref_key; } TABLE_REF; + /* -** CACHE_FIELD and JOIN_CACHE is used on full join to cache records in outer -** table + CACHE_FIELD and JOIN_CACHE is used on full join to cache records in outer + table */ - typedef struct st_cache_field { char *str; uint length,blob_length; @@ -83,7 +83,7 @@ typedef struct st_join_cache { /* -** The structs which holds the join connections and join states + The structs which holds the join connections and join states */ enum join_type { JT_UNKNOWN,JT_SYSTEM,JT_CONST,JT_EQ_REF,JT_REF,JT_MAYBE_REF, JT_ALL, JT_RANGE, JT_NEXT, JT_FT, JT_REF_OR_NULL, @@ -103,6 +103,7 @@ typedef enum_nested_loop_state typedef int (*Read_record_func)(struct st_join_table *tab); Next_select_func setup_end_select_func(JOIN *join); + typedef struct st_join_table { st_join_table() {} /* Remove gcc warning */ TABLE *table; @@ -482,7 +483,11 @@ class store_key_field: public store_key } enum store_key_result copy() { + TABLE *table= copy_field.to_field->table; + my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, + table->write_set); copy_field.do_copy(©_field); + dbug_tmp_restore_column_map(table->write_set, old_map); return err != 0 ? STORE_KEY_FATAL : STORE_KEY_OK; } const char *name() const { return field_name; } @@ -502,7 +507,11 @@ public: {} enum store_key_result copy() { + TABLE *table= to_field->table; + my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, + table->write_set); int res= item->save_in_field(to_field, 1); + dbug_tmp_restore_column_map(table->write_set, old_map); return (err != 0 || res > 2 ? STORE_KEY_FATAL : (store_key_result) res); } @@ -539,7 +548,7 @@ public: const char *name() const { return "const"; } }; -bool cp_buffer_from_ref(THD *thd, TABLE_REF *ref); +bool cp_buffer_from_ref(THD *thd, TABLE *table, TABLE_REF *ref); bool error_if_full_join(JOIN *join); int report_error(TABLE *table, int error); int safe_index_read(JOIN_TAB *tab); diff --git a/sql/sql_show.cc b/sql/sql_show.cc index ac1825d7c84..f500e3bf481 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -47,7 +47,7 @@ static void store_key_options(THD *thd, String *packet, TABLE *table, KEY *key_info); /*************************************************************************** -** List all table types supported +** List all table types supported ***************************************************************************/ static my_bool show_handlerton(THD *thd, st_plugin_int *plugin, @@ -55,25 +55,26 @@ static my_bool show_handlerton(THD *thd, st_plugin_int *plugin, { handlerton *default_type= (handlerton *) arg; Protocol *protocol= thd->protocol; - handlerton *hton= (handlerton *) plugin->plugin->info; + handlerton *hton= (handlerton *)plugin->data; if (!(hton->flags & HTON_HIDDEN)) { protocol->prepare_for_resend(); - protocol->store(hton->name, system_charset_info); + protocol->store(plugin->name.str, plugin->name.length, + system_charset_info); const char *option_name= show_comp_option_name[(int) hton->state]; if (hton->state == SHOW_OPTION_YES && default_type == hton) option_name= "DEFAULT"; protocol->store(option_name, system_charset_info); - protocol->store(hton->comment, system_charset_info); + protocol->store(plugin->plugin->descr, system_charset_info); protocol->store(hton->commit ? "YES" : "NO", system_charset_info); protocol->store(hton->prepare ? "YES" : "NO", system_charset_info); protocol->store(hton->savepoint_set ? "YES" : "NO", system_charset_info); - + return protocol->write() ? 1 : 0; } - return 0; + return 0; } bool mysqld_show_storage_engines(THD *thd) @@ -93,7 +94,7 @@ bool mysqld_show_storage_engines(THD *thd) Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(TRUE); - if (plugin_foreach(thd, show_handlerton, + if (plugin_foreach(thd, show_handlerton, MYSQL_STORAGE_ENGINE_PLUGIN, thd->variables.table_type)) DBUG_RETURN(TRUE); @@ -740,6 +741,7 @@ mysqld_list_fields(THD *thd, TABLE_LIST *table_list, const char *wild) field_list.push_back(new Item_field(field)); } restore_record(table, s->default_values); // Get empty record + table->use_all_columns(); if (thd->protocol->send_fields(&field_list, Protocol::SEND_DEFAULTS | Protocol::SEND_EOF)) DBUG_VOID_RETURN; @@ -953,9 +955,9 @@ static void append_directory(THD *thd, String *packet, const char *dir_type, RETURN 0 OK */ -int -store_create_info(THD *thd, TABLE_LIST *table_list, String *packet, - HA_CREATE_INFO *create_info_arg) + +int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet, + HA_CREATE_INFO *create_info_arg) { List<Item> field_list; char tmp[MAX_FIELD_WIDTH], *for_str, buff[128], *end, uname[NAME_LEN*3+1]; @@ -968,6 +970,7 @@ store_create_info(THD *thd, TABLE_LIST *table_list, String *packet, handler *file= table->file; TABLE_SHARE *share= table->s; HA_CREATE_INFO create_info; + bool show_table_options= FALSE; bool foreign_db_mode= (thd->variables.sql_mode & (MODE_POSTGRESQL | MODE_ORACLE | MODE_MSSQL | @@ -977,6 +980,7 @@ store_create_info(THD *thd, TABLE_LIST *table_list, String *packet, bool limited_mysql_mode= (thd->variables.sql_mode & (MODE_NO_FIELD_OPTIONS | MODE_MYSQL323 | MODE_MYSQL40)) != 0; + my_bitmap_map *old_map; DBUG_ENTER("store_create_info"); DBUG_PRINT("enter",("table: %s", table->s->table_name.str)); @@ -999,6 +1003,12 @@ store_create_info(THD *thd, TABLE_LIST *table_list, String *packet, } append_identifier(thd, packet, alias, strlen(alias)); packet->append(STRING_WITH_LEN(" (\n")); + /* + We need this to get default values from the table + We have to restore the read_set if we are called from insert in case + of row based replication. + */ + old_map= tmp_use_all_columns(table, table->read_set); for (ptr=table->field ; (field= *ptr); ptr++) { @@ -1153,10 +1163,11 @@ store_create_info(THD *thd, TABLE_LIST *table_list, String *packet, table->field[key_part->fieldnr-1]->key_length() && !(key_info->flags & HA_FULLTEXT))) { + char *end; buff[0] = '('; - char* end=int10_to_str((long) key_part->length / - key_part->field->charset()->mbmaxlen, - buff + 1,10); + end= int10_to_str((long) key_part->length / + key_part->field->charset()->mbmaxlen, + buff + 1,10); *end++ = ')'; packet->append(buff,(uint) (end-buff)); } @@ -1185,6 +1196,7 @@ store_create_info(THD *thd, TABLE_LIST *table_list, String *packet, packet->append(STRING_WITH_LEN("\n)")); if (!(thd->variables.sql_mode & MODE_NO_TABLE_OPTIONS) && !foreign_db_mode) { + show_table_options= TRUE; /* Get possible table space definitions and append them to the CREATE TABLE statement @@ -1325,13 +1337,15 @@ store_create_info(THD *thd, TABLE_LIST *table_list, String *packet, (!table->part_info->is_auto_partitioned) && ((part_syntax= generate_partition_syntax(table->part_info, &part_syntax_len, - FALSE,FALSE)))) + FALSE, + show_table_options)))) { packet->append(part_syntax, part_syntax_len); my_free(part_syntax, MYF(0)); } } #endif + tmp_restore_column_map(table->read_set, old_map); DBUG_RETURN(0); } @@ -2375,7 +2389,6 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) TABLE *table= tables->table; SELECT_LEX *select_lex= &lex->select_lex; SELECT_LEX *old_all_select_lex= lex->all_selects_list; - TABLE_LIST **save_query_tables_last= lex->query_tables_last; enum_sql_command save_sql_command= lex->sql_command; SELECT_LEX *lsel= tables->schema_select_lex; ST_SCHEMA_TABLE *schema_table= tables->schema_table; @@ -2394,6 +2407,7 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) enum legacy_db_type not_used; Open_tables_state open_tables_state_backup; bool save_view_prepare_mode= lex->view_prepare_mode; + Query_tables_list query_tables_list_backup; lex->view_prepare_mode= TRUE; DBUG_ENTER("get_all_tables"); @@ -2406,6 +2420,8 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) */ lex->sql_command= SQLCOM_SHOW_FIELDS; + lex->reset_n_backup_query_tables_list(&query_tables_list_backup); + /* We should not introduce deadlocks even if we already have some tables open and locked, since we won't lock tables which we will @@ -2446,8 +2462,7 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) show_table_list->db), show_table_list->alias)); thd->temporary_tables= 0; - close_thread_tables(thd); - show_table_list->table= 0; + close_tables_for_reopen(thd, &show_table_list); goto err; } @@ -2539,8 +2554,8 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) { int res; /* - Set the parent lex of 'sel' because it is needed by sel.init_query() - which is called inside make_table_list. + Set the parent lex of 'sel' because it is needed by + sel.init_query() which is called inside make_table_list. */ sel.parent_lex= lex; if (make_table_list(thd, &sel, base_name, file_name)) @@ -2558,9 +2573,10 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) in this case. */ res= schema_table->process_table(thd, show_table_list, table, - res, base_name, - show_table_list->alias); - close_thread_tables(thd); + res, base_name, + show_table_list->alias); + close_tables_for_reopen(thd, &show_table_list); + DBUG_ASSERT(!lex->query_tables_own_last); if (res) goto err; } @@ -2577,11 +2593,10 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) error= 0; err: thd->restore_backup_open_tables_state(&open_tables_state_backup); + lex->restore_backup_query_tables_list(&query_tables_list_backup); lex->derived_tables= derived_tables; lex->all_selects_list= old_all_select_lex; - lex->query_tables_last= save_query_tables_last; lex->view_prepare_mode= save_view_prepare_mode; - *save_query_tables_last= 0; lex->sql_command= save_sql_command; DBUG_RETURN(error); } @@ -2735,50 +2750,55 @@ static int get_schema_tables_record(THD *thd, struct st_table_list *tables, case ROW_TYPE_COMPACT: tmp_buff= "Compact"; break; + case ROW_TYPE_PAGES: + tmp_buff= "Paged"; + break; } table->field[6]->store(tmp_buff, strlen(tmp_buff), cs); if (!tables->schema_table) { - table->field[7]->store((longlong) file->records, TRUE); + table->field[7]->store((longlong) file->stats.records, TRUE); table->field[7]->set_notnull(); } - table->field[8]->store((longlong) file->mean_rec_length, TRUE); - table->field[9]->store((longlong) file->data_file_length, TRUE); - if (file->max_data_file_length) + table->field[8]->store((longlong) file->stats.mean_rec_length, TRUE); + table->field[9]->store((longlong) file->stats.data_file_length, TRUE); + if (file->stats.max_data_file_length) { - table->field[10]->store((longlong) file->max_data_file_length, TRUE); + table->field[10]->store((longlong) file->stats.max_data_file_length, + TRUE); } - table->field[11]->store((longlong) file->index_file_length, TRUE); - table->field[12]->store((longlong) file->delete_length, TRUE); + table->field[11]->store((longlong) file->stats.index_file_length, TRUE); + table->field[12]->store((longlong) file->stats.delete_length, TRUE); if (show_table->found_next_number_field) { - table->field[13]->store((longlong) file->auto_increment_value, TRUE); + table->field[13]->store((longlong) file->stats.auto_increment_value, + TRUE); table->field[13]->set_notnull(); } - if (file->create_time) + if (file->stats.create_time) { thd->variables.time_zone->gmt_sec_to_TIME(&time, - file->create_time); + file->stats.create_time); table->field[14]->store_time(&time, MYSQL_TIMESTAMP_DATETIME); table->field[14]->set_notnull(); } - if (file->update_time) + if (file->stats.update_time) { thd->variables.time_zone->gmt_sec_to_TIME(&time, - file->update_time); + file->stats.update_time); table->field[15]->store_time(&time, MYSQL_TIMESTAMP_DATETIME); table->field[15]->set_notnull(); } - if (file->check_time) + if (file->stats.check_time) { - thd->variables.time_zone->gmt_sec_to_TIME(&time, file->check_time); + thd->variables.time_zone->gmt_sec_to_TIME(&time, file->stats.check_time); table->field[16]->store_time(&time, MYSQL_TIMESTAMP_DATETIME); table->field[16]->set_notnull(); } tmp_buff= (share->table_charset ? share->table_charset->name : "default"); table->field[17]->store(tmp_buff, strlen(tmp_buff), cs); - if (file->table_flags() & (ulong) HA_HAS_CHECKSUM) + if (file->ha_table_flags() & (ulong) HA_HAS_CHECKSUM) { table->field[18]->store((longlong) file->checksum(), TRUE); table->field[18]->set_notnull(); @@ -2875,6 +2895,7 @@ static int get_schema_column_record(THD *thd, struct st_table_list *tables, restore_record(show_table, s->default_values); base_name_length= strlen(base_name); file_name_length= strlen(file_name); + show_table->use_all_columns(); // Required for default for (ptr=show_table->field; (field= *ptr) ; ptr++) { @@ -3091,7 +3112,7 @@ static my_bool iter_schema_engines(THD *thd, st_plugin_int *plugin, void *ptable) { TABLE *table= (TABLE *) ptable; - handlerton *hton= (handlerton *) plugin->plugin->info; + handlerton *hton= (handlerton *)plugin->data; const char *wild= thd->lex->wild ? thd->lex->wild->ptr() : NullS; CHARSET_INFO *scs= system_charset_info; DBUG_ENTER("iter_schema_engines"); @@ -3099,21 +3120,26 @@ static my_bool iter_schema_engines(THD *thd, st_plugin_int *plugin, if (!(hton->flags & HTON_HIDDEN)) { if (!(wild && wild[0] && - wild_case_compare(scs, hton->name,wild))) + wild_case_compare(scs, plugin->name.str,wild))) { - const char *tmp; + LEX_STRING state[2]= {{(char*) STRING_WITH_LEN("ENABLED")}, + {(char*) STRING_WITH_LEN("DISABLED")}}; + LEX_STRING yesno[2]= {{(char*) STRING_WITH_LEN("NO")}, + {(char*) STRING_WITH_LEN("YES")}}; + LEX_STRING *tmp; restore_record(table, s->default_values); - table->field[0]->store(hton->name, strlen(hton->name), scs); - tmp= hton->state ? "DISABLED" : "ENABLED"; - table->field[1]->store( tmp, strlen(tmp), scs); - table->field[2]->store(hton->comment, strlen(hton->comment), scs); - tmp= hton->commit ? "YES" : "NO"; - table->field[3]->store( tmp, strlen(tmp), scs); - tmp= hton->prepare ? "YES" : "NO"; - table->field[4]->store( tmp, strlen(tmp), scs); - tmp= hton->savepoint_set ? "YES" : "NO"; - table->field[5]->store( tmp, strlen(tmp), scs); + table->field[0]->store(plugin->name.str, plugin->name.length, scs); + tmp= &state[test(hton->state)]; + table->field[1]->store(tmp->str, tmp->length, scs); + table->field[2]->store(plugin->plugin->descr, + strlen(plugin->plugin->descr), scs); + tmp= &yesno[test(hton->commit)]; + table->field[3]->store(tmp->str, tmp->length, scs); + tmp= &yesno[test(hton->prepare)]; + table->field[4]->store(tmp->str, tmp->length, scs); + tmp= &yesno[test(hton->savepoint_set)]; + table->field[5]->store(tmp->str, tmp->length, scs); if (schema_table_store_record(thd, table)) DBUG_RETURN(1); @@ -3125,7 +3151,7 @@ static my_bool iter_schema_engines(THD *thd, st_plugin_int *plugin, int fill_schema_engines(THD *thd, TABLE_LIST *tables, COND *cond) { - return plugin_foreach(thd, iter_schema_engines, + return plugin_foreach(thd, iter_schema_engines, MYSQL_STORAGE_ENGINE_PLUGIN, tables->table); } @@ -3140,7 +3166,7 @@ int fill_schema_collation(THD *thd, TABLE_LIST *tables, COND *cond) { CHARSET_INFO **cl; CHARSET_INFO *tmp_cs= cs[0]; - if (!tmp_cs || !(tmp_cs->state & MY_CS_AVAILABLE) || + if (!tmp_cs || !(tmp_cs->state & MY_CS_AVAILABLE) || (tmp_cs->state & MY_CS_HIDDEN) || !(tmp_cs->state & MY_CS_PRIMARY)) continue; @@ -3382,7 +3408,7 @@ static int get_schema_stat_record(THD *thd, struct st_table_list *tables, KEY *key=show_table->key_info+i; if (key->rec_per_key[j]) { - ha_rows records=(show_table->file->records / + ha_rows records=(show_table->file->stats.records / key->rec_per_key[j]); table->field[9]->store((longlong) records, TRUE); table->field[9]->set_notnull(); @@ -3809,7 +3835,7 @@ static void store_schema_partitions_record(THD *thd, TABLE *table, table->field[20]->store_time(&time, MYSQL_TIMESTAMP_DATETIME); table->field[20]->set_notnull(); } - if (file->table_flags() & (ulong) HA_HAS_CHECKSUM) + if (file->ha_table_flags() & (ulong) HA_HAS_CHECKSUM) { table->field[21]->store((longlong) stat_info.check_sum, TRUE); table->field[21]->set_notnull(); @@ -3911,24 +3937,28 @@ static int get_schema_partitions_record(THD *thd, struct st_table_list *tables, { table->field[9]->store(part_info->part_func_string, part_info->part_func_len, cs); - table->field[9]->set_notnull(); } else if (part_info->list_of_part_fields) { collect_partition_expr(part_info->part_field_list, &tmp_str); table->field[9]->store(tmp_str.ptr(), tmp_str.length(), cs); - table->field[9]->set_notnull(); } + table->field[9]->set_notnull(); if (part_info->is_sub_partitioned()) { /* Subpartition method */ + tmp_res.length(0); + if (part_info->linear_hash_ind) + tmp_res.append(partition_keywords[PKW_LINEAR].str, + partition_keywords[PKW_LINEAR].length); if (part_info->list_of_subpart_fields) - table->field[8]->store(partition_keywords[PKW_KEY].str, - partition_keywords[PKW_KEY].length, cs); + tmp_res.append(partition_keywords[PKW_KEY].str, + partition_keywords[PKW_KEY].length); else - table->field[8]->store(partition_keywords[PKW_HASH].str, - partition_keywords[PKW_HASH].length, cs); + tmp_res.append(partition_keywords[PKW_HASH].str, + partition_keywords[PKW_HASH].length); + table->field[8]->store(tmp_res.ptr(), tmp_res.length(), cs); table->field[8]->set_notnull(); /* Subpartition expression */ @@ -3936,14 +3966,13 @@ static int get_schema_partitions_record(THD *thd, struct st_table_list *tables, { table->field[10]->store(part_info->subpart_func_string, part_info->subpart_func_len, cs); - table->field[10]->set_notnull(); } else if (part_info->list_of_subpart_fields) { collect_partition_expr(part_info->subpart_field_list, &tmp_str); table->field[10]->store(tmp_str.ptr(), tmp_str.length(), cs); - table->field[10]->set_notnull(); } + table->field[10]->set_notnull(); } while ((part_elem= part_it++)) @@ -4078,8 +4107,24 @@ static interval_type get_real_interval_type(interval_type i_type) extern LEX_STRING interval_type_to_name[]; + +/* + Loads an event from mysql.event and copies it's data to a row of + I_S.EVENTS + + Synopsis + copy_event_to_schema_table() + thd Thread + sch_table The schema table (information_schema.event) + event_table The event table to use for loading (mysql.event). + + Returns + 0 OK + 1 Error +*/ + static int -fill_events_copy_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table) +copy_event_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table) { const char *wild= thd->lex->wild ? thd->lex->wild->ptr() : NullS; CHARSET_INFO *scs= system_charset_info; @@ -4097,9 +4142,19 @@ fill_events_copy_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table) if (!(!wild || !wild[0] || !wild_compare(et.name.str, wild, 0))) DBUG_RETURN(0); - + + /* + Skip events in schemas one does not have access to. The check is + optimized. It's guaranteed in case of SHOW EVENTS that the user + has access. + */ + if (thd->lex->orig_sql_command != SQLCOM_SHOW_EVENTS && + check_access(thd, EVENT_ACL, et.dbname.str, 0, 0, 1, + is_schema_db(et.dbname.str))) + DBUG_RETURN(0); + /* ->field[0] is EVENT_CATALOG and is by default NULL */ - + sch_table->field[1]->store(et.dbname.str, et.dbname.length, scs); sch_table->field[2]->store(et.name.str, et.name.length, scs); sch_table->field[3]->store(et.definer.str, et.definer.length, scs); @@ -4111,29 +4166,28 @@ fill_events_copy_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table) ulong sql_mode_len=0; sql_mode_str= sys_var_thd_sql_mode::symbolic_mode_representation(thd, et.sql_mode, - &sql_mode_len); + &sql_mode_len); sch_table->field[9]->store((const char*)sql_mode_str, sql_mode_len, scs); } - + if (et.expression) { String show_str; /* type */ sch_table->field[5]->store(STRING_WITH_LEN("RECURRING"), scs); - if (Events::reconstruct_interval_expression(&show_str, - et.interval, - et.expression)) + if (Events::reconstruct_interval_expression(&show_str, et.interval, + et.expression)) DBUG_RETURN(1); sch_table->field[7]->set_notnull(); - sch_table->field[7]->store(show_str.c_ptr(), show_str.length(), scs); + sch_table->field[7]->store(show_str.ptr(), show_str.length(), scs); LEX_STRING *ival= &interval_type_to_name[et.interval]; sch_table->field[8]->set_notnull(); sch_table->field[8]->store(ival->str, ival->length, scs); - //starts & ends + /* starts & ends */ sch_table->field[10]->set_notnull(); sch_table->field[10]->store_time(&et.starts, MYSQL_TIMESTAMP_DATETIME); @@ -4152,13 +4206,13 @@ fill_events_copy_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table) sch_table->field[6]->store_time(&et.execute_at, MYSQL_TIMESTAMP_DATETIME); } - //status + /* status */ if (et.status == Event_timed::ENABLED) sch_table->field[12]->store(STRING_WITH_LEN("ENABLED"), scs); else sch_table->field[12]->store(STRING_WITH_LEN("DISABLED"), scs); - //on_completion + /* on_completion */ if (et.on_completion == Event_timed::ON_COMPLETION_DROP) sch_table->field[13]->store(STRING_WITH_LEN("NOT PRESERVE"), scs); else @@ -4188,98 +4242,179 @@ fill_events_copy_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table) } -int fill_schema_events(THD *thd, TABLE_LIST *tables, COND *cond) +/* + Performs an index scan of event_table (mysql.event) and fills schema_table. + + Synopsis + events_table_index_read_for_db() + thd Thread + schema_table The I_S.EVENTS table + event_table The event table to use for loading (mysql.event) + + Returns + 0 OK + 1 Error +*/ + +static +int events_table_index_read_for_db(THD *thd, TABLE *schema_table, + TABLE *event_table) { - TABLE *table= tables->table; - CHARSET_INFO *scs= system_charset_info; - TABLE *event_table= NULL; - Open_tables_state backup; int ret=0; - bool verbose= false; - char definer[HOSTNAME_LENGTH+USERNAME_LENGTH+2]; - bool use_prefix_scanning= true; - uint key_len= 0; + CHARSET_INFO *scs= system_charset_info; + KEY *key_info; + uint key_len; byte *key_buf= NULL; LINT_INIT(key_buf); - DBUG_ENTER("fill_schema_events"); - - strxmov(definer, thd->security_ctx->priv_user,"@",thd->security_ctx->priv_host, - NullS); - - DBUG_PRINT("info",("db=%s current_user=%s", thd->lex->select_lex.db, definer)); + DBUG_ENTER("schema_events_do_index_scan"); - thd->reset_n_backup_open_tables_state(&backup); + DBUG_PRINT("info", ("Using prefix scanning on PK")); + event_table->file->ha_index_init(0, 1); + event_table->field[Events::FIELD_DB]-> + store(thd->lex->select_lex.db, strlen(thd->lex->select_lex.db), scs); + key_info= event_table->key_info; + key_len= key_info->key_part[0].store_length; - if ((ret= Events::open_event_table(thd, TL_READ, &event_table))) + if (!(key_buf= (byte *)alloc_root(thd->mem_root, key_len))) { - sql_print_error("Table mysql.event is damaged."); ret= 1; - goto err; - } - - event_table->file->ha_index_init(0, 1); - - /* see others' events only if you have PROCESS_ACL !! */ - verbose= ((thd->lex->verbose || - thd->lex->orig_sql_command != SQLCOM_SHOW_EVENTS) && - (thd->security_ctx->master_access & PROCESS_ACL)); - - if (verbose && thd->security_ctx->user) - { - ret= event_table->file->index_first(event_table->record[0]); - use_prefix_scanning= false; + /* don't send error, it would be done by sql_alloc_error_handler() */ } else { - event_table->field[Events::FIELD_DEFINER]-> - store(definer, strlen(definer), scs); - key_len= event_table->key_info->key_part[0].store_length; - - if (thd->lex->select_lex.db) + key_copy(key_buf, event_table->record[0], key_info, key_len); + if (!(ret= event_table->file->index_read(event_table->record[0], key_buf, + key_len, HA_READ_PREFIX))) { - event_table->field[Events::FIELD_DB]-> - store(thd->lex->select_lex.db, strlen(thd->lex->select_lex.db), scs); - key_len+= event_table->key_info->key_part[1].store_length; - } - if (!(key_buf= (byte *)alloc_root(thd->mem_root, key_len))) - { - ret= 1; - goto err; + DBUG_PRINT("info",("Found rows. Let's retrieve them. ret=%d", ret)); + do + { + ret= copy_event_to_schema_table(thd, schema_table, event_table); + if (ret == 0) + ret= event_table->file->index_next_same(event_table->record[0], + key_buf, key_len); + } while (ret == 0); } - - key_copy(key_buf, event_table->record[0], event_table->key_info, key_len); - ret= event_table->file->index_read(event_table->record[0], key_buf, key_len, - HA_READ_PREFIX); + DBUG_PRINT("info", ("Scan finished. ret=%d", ret)); } + event_table->file->ha_index_end(); + /* ret is guaranteed to be != 0 */ + if (ret == HA_ERR_END_OF_FILE || ret == HA_ERR_KEY_NOT_FOUND) + DBUG_RETURN(0); + DBUG_RETURN(1); +} - if (ret) + +/* + Performs a table scan of event_table (mysql.event) and fills schema_table. + + Synopsis + events_table_scan_all() + thd Thread + schema_table The I_S.EVENTS in memory table + event_table The event table to use for loading. + + Returns + 0 OK + 1 Error +*/ + +static +int events_table_scan_all(THD *thd, TABLE *schema_table, + TABLE *event_table) +{ + int ret; + READ_RECORD read_record_info; + + DBUG_ENTER("schema_events_do_table_scan"); + init_read_record(&read_record_info, thd, event_table, NULL, 1, 0); + + /* + rr_sequential, in read_record(), returns 137==HA_ERR_END_OF_FILE, + but rr_handle_error returns -1 for that reason. Thus, read_record() + returns -1 eventually. + */ + do { - ret= (ret == HA_ERR_END_OF_FILE || ret == HA_ERR_KEY_NOT_FOUND) ? 0 : 1; - goto err; + ret= read_record_info.read_record(&read_record_info); + if (ret == 0) + ret= copy_event_to_schema_table(thd, schema_table, event_table); } + while (ret == 0); - while (!ret) - { - if ((ret= fill_events_copy_to_schema_table(thd, table, event_table))) - goto err; + DBUG_PRINT("info", ("Scan finished. ret=%d", ret)); + end_read_record(&read_record_info); - if (use_prefix_scanning) - ret= event_table->file-> - index_next_same(event_table->record[0], key_buf, key_len); - else - ret= event_table->file->index_next(event_table->record[0]); + /* ret is guaranteed to be != 0 */ + DBUG_RETURN(ret == -1? 0:1); +} + + +/* + Fills I_S.EVENTS with data loaded from mysql.event. Also used by + SHOW EVENTS + + Synopsis + fill_schema_events() + thd Thread + tables The schema table + cond Unused + + Returns + 0 OK + 1 Error +*/ + +int fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */) +{ + TABLE *schema_table= tables->table; + TABLE *event_table= NULL; + Open_tables_state backup; + int ret= 0; + + DBUG_ENTER("fill_schema_events"); + /* + If it's SHOW EVENTS then thd->lex->select_lex.db is guaranteed not to + be NULL. Let's do an assert anyway. + */ + if (thd->lex->orig_sql_command == SQLCOM_SHOW_EVENTS) + { + DBUG_ASSERT(thd->lex->select_lex.db); + if (check_access(thd, EVENT_ACL, thd->lex->select_lex.db, 0, 0, 0, + is_schema_db(thd->lex->select_lex.db))) + DBUG_RETURN(1); } - // ret is guaranteed to be != 0 - ret= (ret != HA_ERR_END_OF_FILE); -err: - if (event_table) + + DBUG_PRINT("info",("db=%s", thd->lex->select_lex.db? + thd->lex->select_lex.db:"(null)")); + + thd->reset_n_backup_open_tables_state(&backup); + if (Events::open_event_table(thd, TL_READ, &event_table)) { - event_table->file->ha_index_end(); - close_thread_tables(thd); + sql_print_error("Table mysql.event is damaged."); + thd->restore_backup_open_tables_state(&backup); + DBUG_RETURN(1); } + /* + 1. SELECT I_S => use table scan. I_S.EVENTS does not guarantee order + thus we won't order it. OTOH, SHOW EVENTS will be + ordered. + 2. SHOW EVENTS => PRIMARY KEY with prefix scanning on (db) + Reasoning: Events are per schema, therefore a scan over an index + will save use from doing a table scan and comparing + every single row's `db` with the schema which we show. + */ + if (thd->lex->orig_sql_command == SQLCOM_SHOW_EVENTS) + ret= events_table_index_read_for_db(thd, schema_table, event_table); + else + ret= events_table_scan_all(thd, schema_table, event_table); + + close_thread_tables(thd); thd->restore_backup_open_tables_state(&backup); + + DBUG_PRINT("info", ("Return code=%d", ret)); DBUG_RETURN(ret); } @@ -4502,7 +4637,7 @@ TABLE *create_schema_table(THD *thd, TABLE_LIST *table_list) field_count++; } TMP_TABLE_PARAM *tmp_table_param = - (TMP_TABLE_PARAM*) (thd->calloc(sizeof(TMP_TABLE_PARAM))); + (TMP_TABLE_PARAM*) (thd->alloc(sizeof(TMP_TABLE_PARAM))); tmp_table_param->init(); tmp_table_param->table_charset= cs; tmp_table_param->field_count= field_count; @@ -4874,7 +5009,7 @@ bool get_schema_tables_result(JOIN *join) filesort_free_buffers(table_list->table); } else - table_list->table->file->records= 0; + table_list->table->file->stats.records= 0; if (table_list->schema_table->fill_table(thd, table_list, tab->select_cond)) @@ -4897,7 +5032,7 @@ static my_bool run_hton_fill_schema_files(THD *thd, st_plugin_int *plugin, { struct run_hton_fill_schema_files_args *args= (run_hton_fill_schema_files_args *) arg; - handlerton *hton= (handlerton *) plugin->plugin->info; + handlerton *hton= (handlerton *)plugin->data; if(hton->fill_files_table) hton->fill_files_table(thd, args->tables, args->cond); return false; @@ -5241,7 +5376,7 @@ ST_FIELD_INFO partitions_fields_info[]= {"PARTITION_ORDINAL_POSITION", 21 , MYSQL_TYPE_LONG, 0, 1, 0}, {"SUBPARTITION_ORDINAL_POSITION", 21 , MYSQL_TYPE_LONG, 0, 1, 0}, {"PARTITION_METHOD", 12, MYSQL_TYPE_STRING, 0, 1, 0}, - {"SUBPARTITION_METHOD", 5, MYSQL_TYPE_STRING, 0, 1, 0}, + {"SUBPARTITION_METHOD", 12, MYSQL_TYPE_STRING, 0, 1, 0}, {"PARTITION_EXPRESSION", 65535, MYSQL_TYPE_STRING, 0, 1, 0}, {"SUBPARTITION_EXPRESSION", 65535, MYSQL_TYPE_STRING, 0, 1, 0}, {"PARTITION_DESCRIPTION", 65535, MYSQL_TYPE_STRING, 0, 1, 0}, @@ -5316,9 +5451,9 @@ ST_FIELD_INFO files_fields_info[]= {"FREE_EXTENTS", 4, MYSQL_TYPE_LONG, 0, 0, 0}, {"TOTAL_EXTENTS", 4, MYSQL_TYPE_LONG, 0, 0, 0}, {"EXTENT_SIZE", 4, MYSQL_TYPE_LONG, 0, 0, 0}, - {"INITIAL_SIZE", 8, MYSQL_TYPE_LONGLONG, 0, 0, 0}, - {"MAXIMUM_SIZE", 8, MYSQL_TYPE_LONGLONG, 0, 0, 0}, - {"AUTOEXTEND_SIZE", 8, MYSQL_TYPE_LONGLONG, 0, 0, 0}, + {"INITIAL_SIZE", 21, MYSQL_TYPE_LONG, 0, 0, 0}, + {"MAXIMUM_SIZE", 21, MYSQL_TYPE_LONG, 0, 0, 0}, + {"AUTOEXTEND_SIZE", 21, MYSQL_TYPE_LONG, 0, 0, 0}, {"CREATION_TIME", 0, MYSQL_TYPE_TIMESTAMP, 0, 0, 0}, {"LAST_UPDATE_TIME", 0, MYSQL_TYPE_TIMESTAMP, 0, 0, 0}, {"LAST_ACCESS_TIME", 0, MYSQL_TYPE_TIMESTAMP, 0, 0, 0}, diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 289051c2dd1..a49b7a2cc42 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -75,7 +75,9 @@ uint tablename_to_filename(const char *from, char *to, uint to_length) uint errors, length; if (from[0] == '#' && !strncmp(from, MYSQL50_TABLE_NAME_PREFIX, MYSQL50_TABLE_NAME_PREFIX_LENGTH)) - return my_snprintf(to, to_length, "%s", from + 9); + return (uint) (strmake(to, from+MYSQL50_TABLE_NAME_PREFIX_LENGTH, + to_length-1) - + (from + MYSQL50_TABLE_NAME_PREFIX_LENGTH)); length= strconvert(system_charset_info, from, &my_charset_filename, to, to_length, &errors); if (check_if_legal_tablename(to) && @@ -1231,7 +1233,7 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags) { if (!(part_syntax_buf= generate_partition_syntax(part_info, &syntax_len, - TRUE, FALSE))) + TRUE, TRUE))) { DBUG_RETURN(TRUE); } @@ -2277,7 +2279,7 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info, if (sql_field->sql_type == FIELD_TYPE_BIT) { sql_field->pack_flag= FIELDFLAG_NUMBER; - if (file->table_flags() & HA_CAN_BIT_FIELD) + if (file->ha_table_flags() & HA_CAN_BIT_FIELD) total_uneven_bit_length+= sql_field->length & 7; else sql_field->pack_flag|= FIELDFLAG_TREAT_BIT_AS_CHAR; @@ -2360,7 +2362,7 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info, if (prepare_create_field(sql_field, &blob_columns, ×tamps, ×tamps_with_niladic, - file->table_flags())) + file->ha_table_flags())) DBUG_RETURN(-1); if (sql_field->sql_type == MYSQL_TYPE_VARCHAR) create_info->varchar= 1; @@ -2381,14 +2383,14 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info, DBUG_RETURN(-1); } if (auto_increment && - (file->table_flags() & HA_NO_AUTO_INCREMENT)) + (file->ha_table_flags() & HA_NO_AUTO_INCREMENT)) { my_message(ER_TABLE_CANT_HANDLE_AUTO_INCREMENT, ER(ER_TABLE_CANT_HANDLE_AUTO_INCREMENT), MYF(0)); DBUG_RETURN(-1); } - if (blob_columns && (file->table_flags() & HA_NO_BLOBS)) + if (blob_columns && (file->ha_table_flags() & HA_NO_BLOBS)) { my_message(ER_TABLE_CANT_HANDLE_BLOB, ER(ER_TABLE_CANT_HANDLE_BLOB), MYF(0)); @@ -2545,7 +2547,7 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info, if (key->type == Key::FULLTEXT) { - if (!(file->table_flags() & HA_CAN_FULLTEXT)) + if (!(file->ha_table_flags() & HA_CAN_FULLTEXT)) { my_message(ER_TABLE_CANT_HANDLE_FT, ER(ER_TABLE_CANT_HANDLE_FT), MYF(0)); @@ -2563,7 +2565,7 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info, /* TODO: Add proper checks if handler supports key_type and algorithm */ if (key_info->flags & HA_SPATIAL) { - if (!(file->table_flags() & HA_CAN_RTREEKEYS)) + if (!(file->ha_table_flags() & HA_CAN_RTREEKEYS)) { my_message(ER_TABLE_CANT_HANDLE_SPKEYS, ER(ER_TABLE_CANT_HANDLE_SPKEYS), MYF(0)); @@ -2665,7 +2667,7 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info, if (f_is_blob(sql_field->pack_flag) || (f_is_geom(sql_field->pack_flag) && key->type != Key::SPATIAL)) { - if (!(file->table_flags() & HA_CAN_INDEX_BLOBS)) + if (!(file->ha_table_flags() & HA_CAN_INDEX_BLOBS)) { my_error(ER_BLOB_USED_AS_KEY, MYF(0), column->field_name); DBUG_RETURN(-1); @@ -2702,22 +2704,24 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info, null_fields--; } else - key_info->flags|= HA_NULL_PART_KEY; - if (!(file->table_flags() & HA_NULL_IN_KEY)) - { - my_error(ER_NULL_COLUMN_IN_INDEX, MYF(0), column->field_name); - DBUG_RETURN(-1); - } - if (key->type == Key::SPATIAL) - { - my_message(ER_SPATIAL_CANT_HAVE_NULL, - ER(ER_SPATIAL_CANT_HAVE_NULL), MYF(0)); - DBUG_RETURN(-1); - } + { + key_info->flags|= HA_NULL_PART_KEY; + if (!(file->ha_table_flags() & HA_NULL_IN_KEY)) + { + my_error(ER_NULL_COLUMN_IN_INDEX, MYF(0), column->field_name); + DBUG_RETURN(-1); + } + if (key->type == Key::SPATIAL) + { + my_message(ER_SPATIAL_CANT_HAVE_NULL, + ER(ER_SPATIAL_CANT_HAVE_NULL), MYF(0)); + DBUG_RETURN(-1); + } + } } if (MTYP_TYPENR(sql_field->unireg_check) == Field::NEXT_NUMBER) { - if (column_nr == 0 || (file->table_flags() & HA_AUTO_PART_KEY)) + if (column_nr == 0 || (file->ha_table_flags() & HA_AUTO_PART_KEY)) auto_increment--; // Field is used } } @@ -2754,14 +2758,14 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info, else if (!f_is_geom(sql_field->pack_flag) && (column->length > length || ((f_is_packed(sql_field->pack_flag) || - ((file->table_flags() & HA_NO_PREFIX_CHAR_KEYS) && + ((file->ha_table_flags() & HA_NO_PREFIX_CHAR_KEYS) && (key_info->flags & HA_NOSAME))) && column->length != length))) { my_message(ER_WRONG_SUB_KEY, ER(ER_WRONG_SUB_KEY), MYF(0)); DBUG_RETURN(-1); } - else if (!(file->table_flags() & HA_NO_PREFIX_CHAR_KEYS)) + else if (!(file->ha_table_flags() & HA_NO_PREFIX_CHAR_KEYS)) length=column->length; } else if (length == 0) @@ -2847,7 +2851,7 @@ static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info, key_info++; } if (!unique_key && !primary_key && - (file->table_flags() & HA_REQUIRE_PRIMARY_KEY)) + (file->ha_table_flags() & HA_REQUIRE_PRIMARY_KEY)) { my_message(ER_REQUIRES_PRIMARY_KEY, ER(ER_REQUIRES_PRIMARY_KEY), MYF(0)); DBUG_RETURN(-1); @@ -3053,8 +3057,8 @@ bool mysql_create_table_internal(THD *thd, if (create_info->row_type == ROW_TYPE_DYNAMIC) db_options|=HA_OPTION_PACK_RECORD; alias= table_case_name(create_info, table_name); - if (!(file=get_new_handler((TABLE_SHARE*) 0, thd->mem_root, - create_info->db_type))) + if (!(file= get_new_handler((TABLE_SHARE*) 0, thd->mem_root, + create_info->db_type))) { mem_alloc_error(sizeof(handler)); DBUG_RETURN(TRUE); @@ -3106,7 +3110,8 @@ bool mysql_create_table_internal(THD *thd, } while ((key= key_iterator++)) { - if (key->type == Key::FOREIGN_KEY) + if (key->type == Key::FOREIGN_KEY && + !part_info->is_auto_partitioned) { my_error(ER_CANNOT_ADD_FOREIGN, MYF(0)); goto err; @@ -3150,7 +3155,7 @@ bool mysql_create_table_internal(THD *thd, */ if (!(part_syntax_buf= generate_partition_syntax(part_info, &syntax_len, - TRUE, FALSE))) + TRUE, TRUE))) goto err; part_info->part_info_string= part_syntax_buf; part_info->part_info_len= syntax_len; @@ -3206,7 +3211,8 @@ bool mysql_create_table_internal(THD *thd, engines in partition clauses. */ delete file; - if (!(file= get_new_handler((TABLE_SHARE*) 0, thd->mem_root, engine_type))) + if (!(file= get_new_handler((TABLE_SHARE*) 0, thd->mem_root, + engine_type))) { mem_alloc_error(sizeof(handler)); DBUG_RETURN(TRUE); @@ -3456,7 +3462,7 @@ mysql_rename_table(handlerton *base, a lowercase file name, but we leave the .frm in mixed case. */ if (lower_case_table_names == 2 && file && - !(file->table_flags() & HA_FILE_BASED)) + !(file->ha_table_flags() & HA_FILE_BASED)) { strmov(tmp_name, old_name); my_casedn_str(files_charset_info, tmp_name); @@ -3896,6 +3902,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, */ ha_autocommit_or_rollback(thd, 1); close_thread_tables(thd); + lex->reset_query_tables_list(FALSE); if (protocol->write()) goto err; continue; @@ -3921,6 +3928,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, protocol->store(buff, length, system_charset_info); ha_autocommit_or_rollback(thd, 0); close_thread_tables(thd); + lex->reset_query_tables_list(FALSE); table->table=0; // For query cache if (protocol->write()) goto err; @@ -4673,7 +4681,8 @@ static uint compare_tables(TABLE *table, List<create_field> *create_list, HA_CREATE_INFO *create_info, ALTER_INFO *alter_info, uint order_num, uint *index_drop_buffer, uint *index_drop_count, - uint *index_add_buffer, uint *index_add_count) + uint *index_add_buffer, uint *index_add_count, + bool varchar) { Field **f_ptr, *field; uint changes= 0, tmp; @@ -4708,7 +4717,8 @@ static uint compare_tables(TABLE *table, List<create_field> *create_list, create_info->used_fields & HA_CREATE_USED_CHARSET || create_info->used_fields & HA_CREATE_USED_DEFAULT_CHARSET || (alter_info->flags & (ALTER_RECREATE | ALTER_FOREIGN_KEY)) || - order_num) + order_num || + (table->s->frm_version < FRM_VER_TRUE_VARCHAR && varchar)) DBUG_RETURN(ALTER_TABLE_DATA_CHANGED); /* @@ -4738,7 +4748,7 @@ static uint compare_tables(TABLE *table, List<create_field> *create_list, if (!(tmp= field->is_equal(new_field))) DBUG_RETURN(ALTER_TABLE_DATA_CHANGED); // Clear indexed marker - field->add_index= 0; + field->flags&= ~FIELD_IN_ADD_INDEX; changes|= tmp; } @@ -4814,7 +4824,7 @@ static uint compare_tables(TABLE *table, List<create_field> *create_list, { // Mark field to be part of new key field= table->field[key_part->fieldnr]; - field->add_index= 1; + field->flags|= FIELD_IN_ADD_INDEX; } DBUG_PRINT("info", ("index changed: '%s'", table_key->name)); } @@ -4841,7 +4851,7 @@ static uint compare_tables(TABLE *table, List<create_field> *create_list, { // Mark field to be part of new key field= table->field[key_part->fieldnr]; - field->add_index= 1; + field->flags|= FIELD_IN_ADD_INDEX; } DBUG_PRINT("info", ("index added: '%s'", new_key->name)); } @@ -4882,7 +4892,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, uint db_create_options, used_fields; handlerton *old_db_type, *new_db_type; uint need_copy_table= 0; - bool no_table_reopen= FALSE; + bool no_table_reopen= FALSE, varchar= FALSE; #ifdef WITH_PARTITION_STORAGE_ENGINE uint fast_alter_partition= 0; bool partition_changed= FALSE; @@ -4924,6 +4934,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, alter_info->tablespace_op)); if (!(table=open_ltable(thd,table_list,TL_WRITE_ALLOW_READ))) DBUG_RETURN(TRUE); + table->use_all_columns(); /* Check that we are not trying to rename to an existing table */ if (new_name) @@ -4980,7 +4991,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, } old_db_type= table->s->db_type; - if (create_info->db_type == (handlerton*) &default_hton) + if (!create_info->db_type) create_info->db_type= old_db_type; #ifdef WITH_PARTITION_STORAGE_ENGINE @@ -5117,6 +5128,8 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, Field **f_ptr,*field; for (f_ptr=table->field ; (field= *f_ptr) ; f_ptr++) { + if (field->type() == MYSQL_TYPE_STRING) + varchar= TRUE; /* Check if field should be dropped */ Alter_drop *drop; drop_it.rewind(); @@ -5157,8 +5170,12 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, def_it.remove(); } } - else // This field was not dropped and not changed, add it to the list - { // for the new table. + else + { + /* + This field was not dropped and not changed, add it to the list + for the new table. + */ create_list.push_back(def=new create_field(field,field)); alter_it.rewind(); // Change default if ALTER Alter_column *alter; @@ -5452,7 +5469,8 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, key_info_buffer, key_count, create_info, alter_info, order_num, index_drop_buffer, &index_drop_count, - index_add_buffer, &index_add_count); + index_add_buffer, &index_add_count, + varchar); } /* @@ -5670,7 +5688,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, thd->proc_info="copy to tmp table"; next_insert_id=thd->next_insert_id; // Remember for logging copied=deleted=0; - if (new_table && !(new_table->file->table_flags() & HA_NO_COPY_ON_ALTER)) + if (new_table && !(new_table->file->ha_table_flags() & HA_NO_COPY_ON_ALTER)) { /* We don't want update TIMESTAMP fields during ALTER TABLE. */ new_table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; @@ -6134,7 +6152,7 @@ copy_data_between_tables(TABLE *from,TABLE *to, MODE_STRICT_ALL_TABLES)); from->file->info(HA_STATUS_VARIABLE); - to->file->start_bulk_insert(from->file->records); + to->file->ha_start_bulk_insert(from->file->stats.records); save_sql_mode= thd->variables.sql_mode; @@ -6180,19 +6198,14 @@ copy_data_between_tables(TABLE *from,TABLE *to, &tables, fields, all_fields, order) || !(sortorder=make_unireg_sortorder(order, &length)) || (from->sort.found_records = filesort(thd, from, sortorder, length, - (SQL_SELECT *) 0, HA_POS_ERROR, + (SQL_SELECT *) 0, HA_POS_ERROR, 1, &examined_rows)) == HA_POS_ERROR) goto err; }; - /* - Handler must be told explicitly to retrieve all columns, because - this function does not set field->query_id in the columns to the - current query id - */ - to->file->ha_set_all_bits_in_write_set(); - from->file->ha_retrieve_all_cols(); + /* Tell handler that we have values for all columns in the to table */ + to->use_all_columns(); init_read_record(&info, thd, from, (SQL_SELECT *) 0, 1,1); if (ignore || handle_duplicates == DUP_REPLACE) @@ -6234,9 +6247,10 @@ copy_data_between_tables(TABLE *from,TABLE *to, { const char *err_msg= ER(ER_DUP_ENTRY); if (key_nr == 0 && - (to->key_info[0].key_part[0].field->flags & AUTO_INCREMENT_FLAG)) + (to->key_info[0].key_part[0].field->flags & + AUTO_INCREMENT_FLAG)) err_msg= ER(ER_DUP_ENTRY_AUTOINCREMENT_CASE); - to->file->print_keydupp_error(key_nr, err_msg); + to->file->print_keydup_error(key_nr, err_msg); break; } } @@ -6254,7 +6268,7 @@ copy_data_between_tables(TABLE *from,TABLE *to, free_io_cache(from); delete [] copy; // This is never 0 - if (to->file->end_bulk_insert() && error <= 0) + if (to->file->ha_end_bulk_insert() && error <= 0) { to->file->print_error(my_errno,MYF(0)); error=1; @@ -6307,7 +6321,7 @@ bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list, lex->col_list.empty(); lex->alter_info.reset(); bzero((char*) &create_info,sizeof(create_info)); - create_info.db_type= (handlerton*) &default_hton; + create_info.db_type= 0; create_info.row_type=ROW_TYPE_NOT_USED; create_info.default_table_charset=default_charset_info; /* Force alter table to recreate table */ @@ -6359,10 +6373,10 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables, { t->pos_in_table_list= table; - if (t->file->table_flags() & HA_HAS_CHECKSUM && + if (t->file->ha_table_flags() & HA_HAS_CHECKSUM && !(check_opt->flags & T_EXTEND)) protocol->store((ulonglong)t->file->checksum()); - else if (!(t->file->table_flags() & HA_HAS_CHECKSUM) && + else if (!(t->file->ha_table_flags() & HA_HAS_CHECKSUM) && (check_opt->flags & T_QUICK)) protocol->store_null(); else @@ -6371,11 +6385,7 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables, ha_checksum crc= 0; uchar null_mask=256 - (1 << t->s->last_null_bit_pos); - /* - Set all bits in read set and inform InnoDB that we are reading all - fields - */ - t->file->ha_retrieve_all_cols(); + t->use_all_columns(); if (t->file->ha_rnd_init(1)) protocol->store_null(); @@ -6451,7 +6461,7 @@ static bool check_engine(THD *thd, const char *table_name, no_substitution, 1))) return TRUE; - if (req_engine != (handlerton*) &default_hton && req_engine != *new_engine) + if (req_engine && req_engine != *new_engine) { push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_USING_OTHER_HANDLER, @@ -6464,7 +6474,8 @@ static bool check_engine(THD *thd, const char *table_name, { if (create_info->used_fields & HA_CREATE_USED_ENGINE) { - my_error(ER_ILLEGAL_HA_CREATE_OPTION, MYF(0), (*new_engine)->name, "TEMPORARY"); + my_error(ER_ILLEGAL_HA_CREATE_OPTION, MYF(0), + hton2plugin[(*new_engine)->slot]->name.str, "TEMPORARY"); *new_engine= 0; return TRUE; } diff --git a/sql/sql_tablespace.cc b/sql/sql_tablespace.cc index 8bc39ec82c6..94318a67575 100644 --- a/sql/sql_tablespace.cc +++ b/sql/sql_tablespace.cc @@ -28,16 +28,16 @@ int mysql_alter_tablespace(THD *thd, st_alter_tablespace *ts_info) If the user haven't defined an engine, this will fallback to using the default storage engine. */ - if (hton == NULL || hton == &default_hton || hton->state != SHOW_OPTION_YES) + if (hton == NULL || hton->state != SHOW_OPTION_YES) { hton= ha_resolve_by_legacy_type(thd, DB_TYPE_DEFAULT); if (ts_info->storage_engine != 0) push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR, ER_WARN_USING_OTHER_HANDLER, ER(ER_WARN_USING_OTHER_HANDLER), - hton->name, - ts_info->tablespace_name - ? ts_info->tablespace_name : ts_info->logfile_group_name); + hton2plugin[hton->slot]->name.str, + ts_info->tablespace_name ? ts_info->tablespace_name + : ts_info->logfile_group_name); } if (hton->alter_tablespace) @@ -64,7 +64,7 @@ int mysql_alter_tablespace(THD *thd, st_alter_tablespace *ts_info) push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR, ER_ILLEGAL_HA_CREATE_OPTION, ER(ER_ILLEGAL_HA_CREATE_OPTION), - hton->name, + hton2plugin[hton->slot]->name.str, "TABLESPACE or LOGFILE GROUP"); } if (mysql_bin_log.is_open()) diff --git a/sql/sql_trigger.h b/sql/sql_trigger.h index b67c22e0588..bddfd8c1f0c 100644 --- a/sql/sql_trigger.h +++ b/sql/sql_trigger.h @@ -24,7 +24,7 @@ class Table_triggers_list: public Sql_alloc { /* Triggers as SPs grouped by event, action_time */ - sp_head *bodies[TRG_EVENT_MAX][TRG_ACTION_MAX]; + sp_head *bodies[TRG_EVENT_MAX][TRG_ACTION_MAX]; /* Copy of TABLE::Field array with field pointers set to TABLE::record[1] buffer instead of TABLE::record[0] (used for OLD values in on UPDATE @@ -132,6 +132,10 @@ private: const char *db_name, LEX_STRING *old_table_name, LEX_STRING *new_table_name); + friend void st_table::mark_columns_needed_for_insert(void); + friend void st_table::mark_columns_needed_for_update(void); + friend void st_table::mark_columns_needed_for_delete(void); + }; extern const LEX_STRING trg_action_time_type_names[]; diff --git a/sql/sql_udf.cc b/sql/sql_udf.cc index 495ffe9f5d5..4b9de6905fe 100644 --- a/sql/sql_udf.cc +++ b/sql/sql_udf.cc @@ -152,7 +152,8 @@ void udf_init() table= tables.table; init_read_record(&read_record_info, new_thd, table, NULL,1,0); - while (!(error = read_record_info.read_record(&read_record_info))) + table->use_all_columns(); + while (!(error= read_record_info.read_record(&read_record_info))) { DBUG_PRINT("info",("init udf record")); LEX_STRING name; @@ -449,7 +450,7 @@ int mysql_create_function(THD *thd,udf_func *udf) /* Allow creation of functions even if we can't open func table */ if (!(table = open_ltable(thd,&tables,TL_WRITE))) goto err; - + table->use_all_columns(); restore_record(table, s->default_values); // Default values for fields table->field[0]->store(u_d->name.str, u_d->name.length, system_charset_info); table->field[1]->store((longlong) u_d->returns, TRUE); @@ -507,8 +508,8 @@ int mysql_drop_function(THD *thd,const LEX_STRING *udf_name) tables.table_name= tables.alias= (char*) "func"; if (!(table = open_ltable(thd,&tables,TL_WRITE))) goto err; + table->use_all_columns(); table->field[0]->store(udf_name->str, udf_name->length, system_charset_info); - table->file->ha_retrieve_all_cols(); if (!table->file->index_read_idx(table->record[0], 0, (byte*) table->field[0]->ptr, table->key_info[0].key_length, diff --git a/sql/sql_union.cc b/sql/sql_union.cc index 3c156acfc14..bf93f0d3bea 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -152,8 +152,8 @@ st_select_lex_unit::init_prepare_fake_select_lex(THD *thd) order; order=order->next) { - (*order->item)->walk(&Item::change_context_processor, - (byte *) &fake_select_lex->context); + (*order->item)->walk(&Item::change_context_processor, 0, + (byte*) &fake_select_lex->context); } } @@ -468,7 +468,7 @@ bool st_select_lex_unit::exec() } if (!res) { - records_at_start= table->file->records; + records_at_start= table->file->stats.records; sl->join->exec(); if (sl == union_distinct) { @@ -507,7 +507,7 @@ bool st_select_lex_unit::exec() rows and actual rows added to the temporary table. */ add_rows+= (ulonglong) (thd->limit_found_rows - (ulonglong) - ((table->file->records - records_at_start))); + ((table->file->stats.records - records_at_start))); } } } @@ -567,7 +567,7 @@ bool st_select_lex_unit::exec() fake_select_lex->table_list.empty(); if (!res) { - thd->limit_found_rows = (ulonglong)table->file->records + add_rows; + thd->limit_found_rows = (ulonglong)table->file->stats.records + add_rows; thd->examined_row_count+= examined_rows; } /* diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 75e8db6621f..e917c1358ef 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -29,7 +29,7 @@ static bool safe_update_on_fly(JOIN_TAB *join_tab, List<Item> *fields); /* Return 0 if row hasn't changed */ -static bool compare_record(TABLE *table, query_id_t query_id) +static bool compare_record(TABLE *table) { if (table->s->blob_fields + table->s->varchar_fields == 0) return cmp_record(table,record[1]); @@ -39,9 +39,9 @@ static bool compare_record(TABLE *table, query_id_t query_id) table->s->null_bytes)) return TRUE; // Diff in NULL value /* Compare updated fields */ - for (Field **ptr=table->field ; *ptr ; ptr++) + for (Field **ptr= table->field ; *ptr ; ptr++) { - if ((*ptr)->query_id == query_id && + if (bitmap_is_set(table->write_set, (*ptr)->field_index) && (*ptr)->cmp_binary_offset(table->s->rec_buff_length)) return TRUE; } @@ -120,6 +120,7 @@ int mysql_update(THD *thd, bool using_limit= limit != HA_POS_ERROR; bool safe_update= thd->options & OPTION_SAFE_UPDATES; bool used_key_is_modified, transactional_table, will_batch; + bool can_compare_record; int res; int error, loc_error; uint used_index= MAX_KEY, dup_key_found; @@ -128,7 +129,6 @@ int mysql_update(THD *thd, uint want_privilege; #endif uint table_count= 0; - query_id_t query_id=thd->query_id, timestamp_query_id; ha_rows updated, found; key_map old_used_keys; TABLE *table; @@ -138,8 +138,6 @@ int mysql_update(THD *thd, bool need_reopen; DBUG_ENTER("mysql_update"); - LINT_INIT(timestamp_query_id); - for ( ; ; ) { if (open_tables(thd, &table_list, &table_count, 0)) @@ -181,26 +179,12 @@ int mysql_update(THD *thd, DBUG_RETURN(1); old_used_keys= table->used_keys; // Keys used in WHERE - /* - Change the query_id for the timestamp column so that we can - check if this is modified directly - */ - if (table->timestamp_field) - { - timestamp_query_id=table->timestamp_field->query_id; - table->timestamp_field->query_id=thd->query_id-1; - } - /* Check the fields we are going to modify */ #ifndef NO_EMBEDDED_ACCESS_CHECKS table_list->grant.want_privilege= table->grant.want_privilege= want_privilege; table_list->register_want_access(want_privilege); #endif - /* - Indicate that the set of fields is to be updated by passing 2 for - set_query_id. - */ - if (setup_fields_with_no_wrap(thd, 0, fields, 2, 0, 0)) + if (setup_fields_with_no_wrap(thd, 0, fields, MARK_COLUMNS_WRITE, 0, 0)) DBUG_RETURN(1); /* purecov: inspected */ if (table_list->view && check_fields(thd, fields)) { @@ -214,12 +198,13 @@ int mysql_update(THD *thd, if (table->timestamp_field) { // Don't set timestamp column if this is modified - if (table->timestamp_field->query_id == thd->query_id) + if (bitmap_is_set(table->write_set, + table->timestamp_field->field_index)) table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; else { - table->timestamp_field->query_id=timestamp_query_id; - table->file->ha_set_bit_in_write_set(table->timestamp_field->fieldnr); + bitmap_set_bit(table->write_set, + table->timestamp_field->field_index); } } @@ -228,7 +213,7 @@ int mysql_update(THD *thd, table_list->grant.want_privilege= table->grant.want_privilege= (SELECT_ACL & ~table->grant.privilege); #endif - if (setup_fields(thd, 0, values, 1, 0, 0)) + if (setup_fields(thd, 0, values, MARK_COLUMNS_READ, 0, 0)) { free_underlaid_joins(thd, select_lex); DBUG_RETURN(1); /* purecov: inspected */ @@ -252,7 +237,7 @@ int mysql_update(THD *thd, DBUG_RETURN(0); } #endif - /* Update the table->file->records number */ + /* Update the table->file->stats.records number */ table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK); select= make_select(table, 0, 0, conds, 0, &error); @@ -313,19 +298,23 @@ int mysql_update(THD *thd, We can't update table directly; We must first search after all matching rows before updating the table! */ - table->file->ha_retrieve_all_cols(); if (used_index < MAX_KEY && old_used_keys.is_set(used_index)) { table->key_read=1; - table->file->extra(HA_EXTRA_KEYREAD); + table->mark_columns_used_by_index(used_index); + } + else + { + table->use_all_columns(); } - /* note: can actually avoid sorting below.. */ + /* note: We avoid sorting avoid if we sort on the used index */ if (order && (need_sort || used_key_is_modified)) { /* Doing an ORDER BY; Let filesort find and sort the rows we are going to update + NOTE: filesort will call table->prepare_for_position() */ uint length; SORT_FIELD *sortorder; @@ -334,12 +323,11 @@ int mysql_update(THD *thd, table->sort.io_cache = (IO_CACHE *) my_malloc(sizeof(IO_CACHE), MYF(MY_FAE | MY_ZEROFILL)); if (!(sortorder=make_unireg_sortorder(order, &length)) || - (table->sort.found_records = filesort(thd, table, sortorder, length, - select, limit, - &examined_rows)) + (table->sort.found_records= filesort(thd, table, sortorder, length, + select, limit, 1, + &examined_rows)) == HA_POS_ERROR) { - free_io_cache(table); goto err; } /* @@ -365,6 +353,7 @@ int mysql_update(THD *thd, /* If quick select is used, initialize it before retrieving rows. */ if (select && select->quick && select->quick->reset()) goto err; + table->file->try_semi_consistent_read(1); /* When we get here, we have one of the following options: @@ -376,11 +365,6 @@ int mysql_update(THD *thd, B.2 quick select is not used, this is full index scan (with LIMIT) Full index scan must be started with init_read_record_idx */ - /* If quick select is used, initialize it before retrieving rows. */ - if (select && select->quick && select->quick->reset()) - goto err; - - table->file->try_semi_consistent_read(1); if (used_index == MAX_KEY || (select && select->quick)) init_read_record(&info,thd,table,select,0,1); @@ -440,17 +424,14 @@ int mysql_update(THD *thd, goto err; } if (table->key_read) - { - table->key_read=0; - table->file->extra(HA_EXTRA_NO_KEYREAD); - } + table->restore_column_maps_after_mark_index(); } if (ignore) table->file->extra(HA_EXTRA_IGNORE_DUP_KEY); if (select && select->quick && select->quick->reset()) - goto err; + goto err; table->file->try_semi_consistent_read(1); init_read_record(&info,thd,table,select,0,1); @@ -458,7 +439,6 @@ int mysql_update(THD *thd, thd->count_cuted_fields= CHECK_FIELD_WARN; /* calc cuted fields */ thd->cuted_fields=0L; thd->proc_info="Updating"; - query_id=thd->query_id; transactional_table= table->file->has_transactions(); thd->no_trans_update= 0; @@ -468,12 +448,23 @@ int mysql_update(THD *thd, MODE_STRICT_ALL_TABLES))); will_batch= !table->file->start_bulk_update(); + table->mark_columns_needed_for_update(); + + /* + We can use compare_record() to optimize away updates if + the table handler is returning all columns OR if + if all updated columns are read + */ + can_compare_record= (!(table->file->ha_table_flags() & + HA_PARTIAL_COLUMN_READ) || + bitmap_is_subset(table->write_set, table->read_set)); + while (!(error=info.read_record(&info)) && !thd->killed) { if (!(select && select->skip_record())) { if (table->file->was_semi_consistent_read()) - continue; /* repeat the read of the same row if it still exists */ + continue; /* repeat the read of the same row if it still exists */ store_record(table,record[1]); if (fill_record_n_invoke_before_triggers(thd, fields, values, 0, @@ -483,7 +474,7 @@ int mysql_update(THD *thd, found++; - if (compare_record(table, query_id)) + if (!can_compare_record || compare_record(table)) { if ((res= table_list->view_check_option(thd, ignore)) != VIEW_CHECK_OK) @@ -632,7 +623,6 @@ int mysql_update(THD *thd, table->file->end_bulk_update(); table->file->try_semi_consistent_read(0); end_read_record(&info); - free_io_cache(table); // If ORDER BY delete select; thd->proc_info= "end"; VOID(table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY)); @@ -698,7 +688,6 @@ int mysql_update(THD *thd, } thd->count_cuted_fields= CHECK_FIELD_IGNORE; /* calc cuted fields */ thd->abort_on_warning= 0; - free_io_cache(table); DBUG_RETURN((error >= 0 || thd->net.report_error) ? 1 : 0); err: @@ -748,9 +737,11 @@ bool mysql_prepare_update(THD *thd, TABLE_LIST *table_list, tables.alias= table_list->alias; thd->lex->allow_sum_func= 0; - if (setup_tables(thd, &select_lex->context, &select_lex->top_join_list, - table_list, conds, &select_lex->leaf_tables, - FALSE) || + if (setup_tables_and_check_access(thd, &select_lex->context, + &select_lex->top_join_list, + table_list, + &select_lex->leaf_tables, + FALSE, UPDATE_ACL) || setup_conds(thd, table_list, select_lex->leaf_tables, conds) || select_lex->setup_ref_array(thd, order_num) || setup_order(thd, select_lex->ref_pointer_array, @@ -841,13 +832,14 @@ reopen_tables: call in setup_tables()). */ - if (setup_tables(thd, &lex->select_lex.context, - &lex->select_lex.top_join_list, - table_list, &lex->select_lex.where, - &lex->select_lex.leaf_tables, FALSE)) + if (setup_tables_and_check_access(thd, &lex->select_lex.context, + &lex->select_lex.top_join_list, + table_list, + &lex->select_lex.leaf_tables, FALSE, + UPDATE_ACL)) DBUG_RETURN(TRUE); - if (setup_fields_with_no_wrap(thd, 0, *fields, 2, 0, 0)) + if (setup_fields_with_no_wrap(thd, 0, *fields, MARK_COLUMNS_WRITE, 0, 0)) DBUG_RETURN(TRUE); for (tl= table_list; tl ; tl= tl->next_local) @@ -875,7 +867,8 @@ reopen_tables: TABLE *table= tl->table; /* Only set timestamp column if this is not modified */ if (table->timestamp_field && - table->timestamp_field->query_id == thd->query_id) + bitmap_is_set(table->write_set, + table->timestamp_field->field_index)) table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; /* if table will be updated then check that it is unique */ @@ -887,6 +880,7 @@ reopen_tables: DBUG_RETURN(TRUE); } + table->mark_columns_needed_for_update(); DBUG_PRINT("info",("setting table `%s` for update", tl->alias)); /* If table will be updated we should not downgrade lock for it and @@ -1088,7 +1082,7 @@ int multi_update::prepare(List<Item> ¬_used_values, reference tables */ - if (setup_fields(thd, 0, *values, 1, 0, 0)) + if (setup_fields(thd, 0, *values, MARK_COLUMNS_READ, 0, 0)) DBUG_RETURN(1); /* @@ -1207,7 +1201,9 @@ multi_update::initialize_tables(JOIN *join) Item_field *ifield; List<Item> temp_fields= *fields_for_table[cnt]; ORDER group; + TMP_TABLE_PARAM *tmp_param; + table->mark_columns_needed_for_update(); if (table == main_table) // First table in join { if (safe_update_on_fly(join->join_tab, &temp_fields)) @@ -1216,9 +1212,9 @@ multi_update::initialize_tables(JOIN *join) continue; } } + table->prepare_for_position(); - TMP_TABLE_PARAM *tmp_param= tmp_table_param+cnt; - + tmp_param= tmp_table_param+cnt; /* Create a temporary table to store all fields that are changed for this table. The first field in the temporary table is a pointer to the @@ -1313,7 +1309,7 @@ static bool safe_update_on_fly(JOIN_TAB *join_tab, List<Item> *fields) !(table->triggers && table->triggers->has_before_update_triggers()); /* If scanning in clustered key */ - if ((table->file->table_flags() & HA_PRIMARY_KEY_IN_READ_INDEX) && + if ((table->file->ha_table_flags() & HA_PRIMARY_KEY_IN_READ_INDEX) && table->s->primary_key < MAX_KEY) return !check_if_key_used(table, table->s->primary_key, *fields) && !(table->triggers && @@ -1359,6 +1355,7 @@ bool multi_update::send_data(List<Item> ¬_used_values) for (cur_table= update_tables; cur_table; cur_table= cur_table->next_local) { TABLE *table= cur_table->table; + uint offset= cur_table->shared; /* Check if we are using outer join and we didn't find the row or if we have already updated this row in the previous call to this @@ -1374,10 +1371,18 @@ bool multi_update::send_data(List<Item> ¬_used_values) if (table->status & (STATUS_NULL_ROW | STATUS_UPDATED)) continue; - uint offset= cur_table->shared; - table->file->position(table->record[0]); + /* + We can use compare_record() to optimize away updates if + the table handler is returning all columns OR if + if all updated columns are read + */ if (table == table_to_update) { + bool can_compare_record; + can_compare_record= (!(table->file->ha_table_flags() & + HA_PARTIAL_COLUMN_READ) || + bitmap_is_subset(table->write_set, + table->read_set)); table->status|= STATUS_UPDATED; store_record(table,record[1]); if (fill_record_n_invoke_before_triggers(thd, *fields_for_table[offset], @@ -1387,7 +1392,7 @@ bool multi_update::send_data(List<Item> ¬_used_values) DBUG_RETURN(1); found++; - if (compare_record(table, thd->query_id)) + if (!can_compare_record || compare_record(table)) { int error; if ((error= cur_table->view_check_option(thd, ignore)) != @@ -1439,6 +1444,7 @@ bool multi_update::send_data(List<Item> ¬_used_values) { int error; TABLE *tmp_table= tmp_tables[offset]; + table->file->position(table->record[0]); fill_record(thd, tmp_table->field+1, *values_for_table[offset], 1); /* Store pointer to row */ memcpy((char*) tmp_table->field[0]->ptr, @@ -1504,6 +1510,7 @@ int multi_update::do_updates(bool from_send_error) for (cur_table= update_tables; cur_table; cur_table= cur_table->next_local) { byte *ref_pos; + bool can_compare_record; table = cur_table->table; if (table == table_to_update) @@ -1530,6 +1537,11 @@ int multi_update::do_updates(bool from_send_error) if ((local_error = tmp_table->file->ha_rnd_init(1))) goto err; + can_compare_record= (!(table->file->ha_table_flags() & + HA_PARTIAL_COLUMN_READ) || + bitmap_is_subset(table->write_set, + table->read_set)); + ref_pos= (byte*) tmp_table->field[0]->ptr; for (;;) { @@ -1559,7 +1571,7 @@ int multi_update::do_updates(bool from_send_error) TRG_ACTION_BEFORE, TRUE)) goto err2; - if (compare_record(table, thd->query_id)) + if (!can_compare_record || compare_record(table)) { if ((local_error=table->file->ha_update_row(table->record[1], table->record[0]))) diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 8e5a776950d..d1e7ba80ecf 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -1191,9 +1191,11 @@ ok: ok2: if (!old_lex->time_zone_tables_used && thd->lex->time_zone_tables_used) old_lex->time_zone_tables_used= thd->lex->time_zone_tables_used; + DBUG_ASSERT(lex == thd->lex); + thd->lex= old_lex; // Needed for prepare_security result= !table->prelocking_placeholder && table->prepare_security(thd); - lex_end(thd->lex); + lex_end(lex); end: if (arena) thd->restore_active_arena(arena, &backup); @@ -1343,8 +1345,8 @@ frm_type_enum mysql_frm_type(THD *thd, char *path, enum legacy_db_type *dbt) view view for check with opened table DESCRIPTION - If it is VIEW and query have LIMIT clause then check that undertlying - table of viey contain one of following: + If it is VIEW and query have LIMIT clause then check that underlying + table of view contain one of following: 1) primary key of underlying table 2) unique key underlying table with fields for which NULL value is impossible @@ -1385,19 +1387,19 @@ bool check_key_in_view(THD *thd, TABLE_LIST *view) this operation should not have influence on Field::query_id, to avoid marking as used fields which are not used */ - bool save_set_query_id= thd->set_query_id; - thd->set_query_id= 0; - DBUG_PRINT("info", ("thd->set_query_id: %d", thd->set_query_id)); + enum_mark_columns save_mark_used_columns= thd->mark_used_columns; + thd->mark_used_columns= MARK_COLUMNS_NONE; + DBUG_PRINT("info", ("thd->mark_used_columns: %d", thd->mark_used_columns)); for (Field_translator *fld= trans; fld < end_of_trans; fld++) { if (!fld->item->fixed && fld->item->fix_fields(thd, &fld->item)) { - thd->set_query_id= save_set_query_id; + thd->mark_used_columns= save_mark_used_columns; return TRUE; } } - thd->set_query_id= save_set_query_id; - DBUG_PRINT("info", ("thd->set_query_id: %d", thd->set_query_id)); + thd->mark_used_columns= save_mark_used_columns; + DBUG_PRINT("info", ("thd->mark_used_columns: %d", thd->mark_used_columns)); } /* Loop over all keys to see if a unique-not-null key is used */ for (;key_info != key_info_end ; key_info++) @@ -1550,7 +1552,6 @@ mysql_rename_view(THD *thd, File_parser *parser; char view_path[FN_REFLEN]; bool error= TRUE; - DBUG_ENTER("mysql_rename_view"); strxnmov(view_path, FN_REFLEN-1, mysql_data_home, "/", view->db, "/", diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 82dcc758ff0..a8c93b683b3 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1385,7 +1385,7 @@ ev_schedule_time: EVERY_SYM expr interval String str(buff,(uint32) sizeof(buff), system_charset_info); String *str2= $2->val_str(&str); my_error(ER_WRONG_VALUE, MYF(0), "AT", - str2? str2->c_ptr():"NULL"); + str2? str2->c_ptr_safe():"NULL"); YYABORT; break; } @@ -1436,8 +1436,8 @@ ev_starts: /* empty */ char buff[20]; String str(buff,(uint32) sizeof(buff), system_charset_info); String *str2= $2->val_str(&str); - my_error(ER_WRONG_VALUE, MYF(0), "STARTS", str2? str2->c_ptr(): - NULL); + my_error(ER_WRONG_VALUE, MYF(0), "STARTS", + str2 ? str2->c_ptr_safe() : NULL); YYABORT; break; } @@ -3102,11 +3102,11 @@ opt_ts_engine: "STORAGE ENGINE"); YYABORT; } - lex->alter_tablespace_info->storage_engine= $4 ? $4 : &default_hton; + lex->alter_tablespace_info->storage_engine= $4; }; opt_ts_wait: - /* empty */ + /* empty */ | ts_wait ; @@ -3942,12 +3942,18 @@ storage_engines: ident_or_text { $$ = ha_resolve_by_name(YYTHD, &$1); - if ($$ == NULL && - test(YYTHD->variables.sql_mode & MODE_NO_ENGINE_SUBSTITUTION)) + if ($$ == NULL) + if (YYTHD->variables.sql_mode & MODE_NO_ENGINE_SUBSTITUTION) { my_error(ER_UNKNOWN_STORAGE_ENGINE, MYF(0), $1.str); YYABORT; } + else + { + push_warning_printf(YYTHD, MYSQL_ERROR::WARN_LEVEL_ERROR, + ER_UNKNOWN_STORAGE_ENGINE, + ER(ER_UNKNOWN_STORAGE_ENGINE), $1.str); + } }; row_types: @@ -4624,7 +4630,7 @@ alter: lex->select_lex.db=lex->name= 0; lex->like_name= 0; bzero((char*) &lex->create_info,sizeof(lex->create_info)); - lex->create_info.db_type= (handlerton*) &default_hton; + lex->create_info.db_type= 0; lex->create_info.default_table_charset= NULL; lex->create_info.row_type= ROW_TYPE_NOT_USED; lex->alter_info.reset(); diff --git a/sql/table.cc b/sql/table.cc index d0caba7fe9e..45db45b1ffd 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -18,7 +18,7 @@ /* Some general useful functions */ #include "mysql_priv.h" -#include <errno.h> +#include "sql_trigger.h" #include <m_ctype.h> #include "md5.h" @@ -93,21 +93,16 @@ TABLE_SHARE *alloc_table_share(TABLE_LIST *table_list, char *key, { MEM_ROOT mem_root; TABLE_SHARE *share; - char path[FN_REFLEN], normalized_path[FN_REFLEN]; - uint path_length, normalized_length; + char path[FN_REFLEN]; + uint path_length; path_length= build_table_filename(path, sizeof(path) - 1, table_list->db, table_list->table_name, ""); - normalized_length= build_table_filename(normalized_path, - sizeof(normalized_path) - 1, - table_list->db, - table_list->table_name, ""); - init_sql_alloc(&mem_root, TABLE_ALLOC_BLOCK_SIZE, 0); if ((share= (TABLE_SHARE*) alloc_root(&mem_root, sizeof(*share) + key_length + - path_length + normalized_length +2))) + path_length +1))) { bzero((char*) share, sizeof(*share)); share->table_cache_key.str= (char*) (share+1); @@ -123,9 +118,8 @@ TABLE_SHARE *alloc_table_share(TABLE_LIST *table_list, char *key, share->path.str= share->table_cache_key.str+ key_length; share->path.length= path_length; strmov(share->path.str, path); - share->normalized_path.str= share->path.str+ path_length+1; - share->normalized_path.length= normalized_length; - strmov(share->normalized_path.str, normalized_path); + share->normalized_path.str= share->path.str; + share->normalized_path.length= path_length; share->version= refresh_version; share->flush_version= flush_version; @@ -434,6 +428,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, Field **field_ptr, *reg_field; const char **interval_array; enum legacy_db_type legacy_db_type; + my_bitmap_map *bitmaps; DBUG_ENTER("open_binary_frm"); new_field_pack_flag= head[27]; @@ -984,7 +979,6 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, goto err; /* purecov: inspected */ } - reg_field->fieldnr= i+1; //Set field number reg_field->field_index= i; reg_field->comment=comment; if (field_type == FIELD_TYPE_BIT && !f_bit_as_char(pack_flag)) @@ -1019,7 +1013,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, { uint primary_key=(uint) (find_type((char*) primary_key_name, &share->keynames, 3) - 1); - uint ha_option= handler_file->table_flags(); + uint ha_option= handler_file->ha_table_flags(); keyinfo= share->key_info; key_part= keyinfo->key_part; @@ -1108,6 +1102,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, { share->keys_for_keyread.set_bit(key); field->part_of_key.set_bit(key); + field->part_of_key_not_clustered.set_bit(key); } if (handler_file->index_flags(key, i, 1) & HA_READ_ORDER) field->part_of_sortkey.set_bit(key); @@ -1265,6 +1260,14 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, share->last_null_bit_pos= null_bit_pos; share->db_low_byte_first= handler_file->low_byte_first(); + share->column_bitmap_size= bitmap_buffer_size(share->fields); + + if (!(bitmaps= (my_bitmap_map*) alloc_root(&share->mem_root, + share->column_bitmap_size))) + goto err; + bitmap_init(&share->all_set, bitmaps, share->fields, FALSE); + bitmap_set_all(&share->all_set); + delete handler_file; #ifndef DBUG_OFF if (use_hash) @@ -1316,18 +1319,15 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias, TABLE *outparam, bool is_create_table) { int error; - uint records, i; + uint records, i, bitmap_size; bool error_reported= FALSE; - byte *record; + byte *record, *bitmaps; Field **field_ptr; - MEM_ROOT **root_ptr, *old_root; DBUG_ENTER("open_table_from_share"); DBUG_PRINT("enter",("name: '%s.%s' form: 0x%lx", share->db.str, share->table_name.str, outparam)); error= 1; - root_ptr= my_pthread_getspecific_ptr(MEM_ROOT**, THR_MALLOC); - old_root= *root_ptr; bzero((char*) outparam, sizeof(*outparam)); outparam->in_use= thd; outparam->s= share; @@ -1335,7 +1335,6 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias, outparam->write_row_record= NULL; init_sql_alloc(&outparam->mem_root, TABLE_ALLOC_BLOCK_SIZE, 0); - *root_ptr= &outparam->mem_root; if (!(outparam->alias= my_strdup(alias, MYF(MY_WME)))) goto err; @@ -1467,25 +1466,42 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias, #ifdef WITH_PARTITION_STORAGE_ENGINE if (share->partition_info_len) { - if (mysql_unpack_partition(thd, share->partition_info, - share->partition_info_len, - (uchar*)share->part_state, - share->part_state_len, - outparam, is_create_table, - share->default_part_db_type)) - goto err; - /* - Fix the partition functions and ensure they are not constant - functions - */ + MEM_ROOT **root_ptr, *old_root; + bool tmp; + root_ptr= my_pthread_getspecific_ptr(MEM_ROOT**, THR_MALLOC); + old_root= *root_ptr; + *root_ptr= &outparam->mem_root; + + tmp= mysql_unpack_partition(thd, share->partition_info, + share->partition_info_len, + (uchar*)share->part_state, + share->part_state_len, + outparam, is_create_table, + share->default_part_db_type); outparam->part_info->is_auto_partitioned= share->auto_partitioned; - DBUG_PRINT("info", ("autopartitioned = %u", share->auto_partitioned)); - if (fix_partition_func(thd, share->normalized_path.str, outparam, - is_create_table)) + DBUG_PRINT("info", ("autopartitioned: %u", share->auto_partitioned)); + if (!tmp) + tmp= fix_partition_func(thd, share->normalized_path.str, outparam, + is_create_table); + *root_ptr= old_root; + if (tmp) goto err; } #endif + /* Allocate bitmaps */ + + bitmap_size= share->column_bitmap_size; + if (!(bitmaps= (byte*) alloc_root(&outparam->mem_root, bitmap_size*3))) + goto err; + bitmap_init(&outparam->def_read_set, + (my_bitmap_map*) bitmaps, share->fields, FALSE); + bitmap_init(&outparam->def_write_set, + (my_bitmap_map*) (bitmaps+bitmap_size), share->fields, FALSE); + bitmap_init(&outparam->tmp_set, + (my_bitmap_map*) (bitmaps+bitmap_size*2), share->fields, FALSE); + outparam->default_column_bitmaps(); + /* The table struct is now initialized; Open the table */ error= 2; if (db_stat) @@ -1527,13 +1543,15 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias, } } - *root_ptr= old_root; +#if defined(HAVE_purify) && !defined(DBUG_OFF) + bzero((char*) bitmaps, bitmap_size*3); +#endif + thd->status_var.opened_tables++; DBUG_RETURN (0); err: - *root_ptr= old_root; if (! error_reported) open_table_error(share, error, my_errno, 0); delete outparam->file; @@ -1564,6 +1582,7 @@ int closefrm(register TABLE *table, bool free_share) uint idx; KEY *key_info; DBUG_ENTER("closefrm"); + DBUG_PRINT("enter", ("table: 0x%lx", (long) table)); if (table->db_stat) error=table->file->close(); @@ -2347,7 +2366,7 @@ table_check_intact(TABLE *table, uint table_f_count, DBUG_PRINT("info",("last_create_time=%d", *last_create_time)); if ((fields_diff_count= (table->s->fields != table_f_count)) || - (*last_create_time != table->file->create_time)) + (*last_create_time != table->file->stats.create_time)) { DBUG_PRINT("info", ("I am suspecting, checking table")); if (fields_diff_count) @@ -2355,33 +2374,46 @@ table_check_intact(TABLE *table, uint table_f_count, // previous MySQL version error= TRUE; if (MYSQL_VERSION_ID > table->s->mysql_version) + { my_error(ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE, MYF(0), table->alias, table_f_count, table->s->fields, table->s->mysql_version, MYSQL_VERSION_ID); + sql_print_error(ER(ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE), + table->alias, table_f_count, table->s->fields, + table->s->mysql_version, MYSQL_VERSION_ID); + DBUG_RETURN(error); + + } else if (MYSQL_VERSION_ID == table->s->mysql_version) + { my_error(ER_COL_COUNT_DOESNT_MATCH_CORRUPTED,MYF(0), table->alias, table_f_count, table->s->fields); + sql_print_error(ER(ER_COL_COUNT_DOESNT_MATCH_CORRUPTED), table->alias, + table_f_count, table->s->fields); + } else + { /* moving from newer mysql to older one -> let's say not an error but - will check the definition afterwards. If a column was added at the end - then we don't care much since it's not in the middle. + will check the definition afterwards. If a column was added at the + end then we don't care much since it's not in the middle. */ error= FALSE; + } } //definitely something has changed char buffer[255]; - for (i=0 ;i < table_f_count; ++i, ++table_def) + for (i=0 ; i < table_f_count; i++, table_def++) { - Field *field= table->field[i]; String sql_type(buffer, sizeof(buffer), system_charset_info); sql_type.length(0); /* name changes are not fatal, we use sequence numbers => no prob for us but this can show tampered table or broken table. */ - if (!fields_diff_count || i < table->s->fields) + if (i < table->s->fields) { + Field *field= table->field[i]; if (strncmp(field->field_name, table_def->name.str, table_def->name.length)) { @@ -2429,20 +2461,20 @@ table_check_intact(TABLE *table, uint table_f_count, else { sql_print_error("(%s) Expected field %s at position %d to have type %s " - " but no field found.", table_def->name.str, + " but no field found.", table->alias, table_def->name.str, i, table_def->type.str); error= TRUE; } } if (!error) - *last_create_time= table->file->create_time; + *last_create_time= table->file->stats.create_time; else if (!fields_diff_count && error_num) my_error(error_num,MYF(0), table->alias, table_f_count, table->s->fields); } else { DBUG_PRINT("info", ("Table seems ok without thorough checking.")); - *last_create_time= table->file->create_time; + *last_create_time= table->file->stats.create_time; } DBUG_RETURN(error); @@ -2821,7 +2853,8 @@ void st_table_list::hide_view_error(THD *thd) if (thd->net.last_errno == ER_BAD_FIELD_ERROR || thd->net.last_errno == ER_SP_DOES_NOT_EXIST || thd->net.last_errno == ER_PROCACCESS_DENIED_ERROR || - thd->net.last_errno == ER_COLUMNACCESS_DENIED_ERROR) + thd->net.last_errno == ER_COLUMNACCESS_DENIED_ERROR || + thd->net.last_errno == ER_TABLEACCESS_DENIED_ERROR) { TABLE_LIST *top= top_table(); thd->clear_error(); @@ -2881,7 +2914,7 @@ void st_table_list::cleanup_items() for (Field_translator *transl= field_translation; transl < field_translation_end; transl++) - transl->item->walk(&Item::cleanup_processor, 0); + transl->item->walk(&Item::cleanup_processor, 0, 0); } @@ -3181,8 +3214,18 @@ bool st_table_list::prepare_view_securety_context(THD *thd) definer.host.str, thd->db)) { - my_error(ER_NO_SUCH_USER, MYF(0), definer.user.str, definer.host.str); - DBUG_RETURN(TRUE); + if (thd->lex->sql_command == SQLCOM_SHOW_CREATE) + { + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + ER_NO_SUCH_USER, + ER(ER_NO_SUCH_USER), + definer.user.str, definer.host.str); + } + else + { + my_error(ER_NO_SUCH_USER, MYF(0), definer.user.str, definer.host.str); + DBUG_RETURN(TRUE); + } } } DBUG_RETURN(FALSE); @@ -3431,7 +3474,7 @@ Item *create_view_field(THD *thd, TABLE_LIST *view, Item **field_ref, field= *field_ref; } thd->lex->current_select->no_wrap_view_item= save_wrapper; - if (thd->lex->current_select->no_wrap_view_item) + if (save_wrapper) { DBUG_RETURN(field); } @@ -3725,6 +3768,276 @@ Field_iterator_table_ref::get_natural_column_ref() return nj_col; } +/***************************************************************************** + Functions to handle column usage bitmaps (read_set, write_set etc...) +*****************************************************************************/ + +/* Reset all columns bitmaps */ + +void st_table::clear_column_bitmaps() +{ + /* + Reset column read/write usage. It's identical to: + bitmap_clear_all(&table->def_read_set); + bitmap_clear_all(&table->def_write_set); + */ + bzero((char*) def_read_set.bitmap, s->column_bitmap_size*2); + column_bitmaps_set(&def_read_set, &def_write_set); +} + + +/* + Tell handler we are going to call position() and rnd_pos() later. + + NOTES: + This is needed for handlers that uses the primary key to find the + row. In this case we have to extend the read bitmap with the primary + key fields. +*/ + +void st_table::prepare_for_position() +{ + DBUG_ENTER("st_table::prepare_for_position"); + + if ((file->ha_table_flags() & HA_PRIMARY_KEY_IN_READ_INDEX) && + s->primary_key < MAX_KEY) + { + mark_columns_used_by_index_no_reset(s->primary_key, read_set); + /* signal change */ + file->column_bitmaps_signal(); + } + DBUG_VOID_RETURN; +} + + +/* + Mark that only fields from one key is used + + NOTE: + This changes the bitmap to use the tmp bitmap + After this, you can't access any other columns in the table until + bitmaps are reset, for example with st_table::clear_column_bitmaps() + or st_table::restore_column_maps_after_mark_index() +*/ + +void st_table::mark_columns_used_by_index(uint index) +{ + MY_BITMAP *bitmap= &tmp_set; + DBUG_ENTER("st_table::mark_columns_used_by_index"); + + (void) file->extra(HA_EXTRA_KEYREAD); + bitmap_clear_all(bitmap); + mark_columns_used_by_index_no_reset(index, bitmap); + column_bitmaps_set(bitmap, bitmap); + DBUG_VOID_RETURN; +} + + +/* + Restore to use normal column maps after key read + + NOTES + This reverse the change done by mark_columns_used_by_index + + WARNING + For this to work, one must have the normal table maps in place + when calling mark_columns_used_by_index +*/ + +void st_table::restore_column_maps_after_mark_index() +{ + DBUG_ENTER("st_table::restore_column_maps_after_mark_index"); + + key_read= 0; + (void) file->extra(HA_EXTRA_NO_KEYREAD); + default_column_bitmaps(); + file->column_bitmaps_signal(); + DBUG_VOID_RETURN; +} + + +/* + mark columns used by key, but don't reset other fields +*/ + +void st_table::mark_columns_used_by_index_no_reset(uint index, + MY_BITMAP *bitmap) +{ + KEY_PART_INFO *key_part= key_info[index].key_part; + KEY_PART_INFO *key_part_end= (key_part + + key_info[index].key_parts); + for (;key_part != key_part_end; key_part++) + bitmap_set_bit(bitmap, key_part->fieldnr-1); +} + + +/* + Mark auto-increment fields as used fields in both read and write maps + + NOTES + This is needed in insert & update as the auto-increment field is + always set and sometimes read. +*/ + +void st_table::mark_auto_increment_column() +{ + DBUG_ASSERT(found_next_number_field); + /* + We must set bit in read set as update_auto_increment() is using the + store() to check overflow of auto_increment values + */ + bitmap_set_bit(read_set, found_next_number_field->field_index); + bitmap_set_bit(write_set, found_next_number_field->field_index); + if (s->next_number_key_offset) + mark_columns_used_by_index_no_reset(s->next_number_index, read_set); + file->column_bitmaps_signal(); +} + + +/* + Mark columns needed for doing an delete of a row + + DESCRIPTON + Some table engines don't have a cursor on the retrieve rows + so they need either to use the primary key or all columns to + be able to delete a row. + + If the engine needs this, the function works as follows: + - If primary key exits, mark the primary key columns to be read. + - If not, mark all columns to be read + + If the engine has HA_REQUIRES_KEY_COLUMNS_FOR_DELETE, we will + mark all key columns as 'to-be-read'. This allows the engine to + loop over the given record to find all keys and doesn't have to + retrieve the row again. +*/ + +void st_table::mark_columns_needed_for_delete() +{ + if (triggers) + { + if (triggers->bodies[TRG_EVENT_DELETE][TRG_ACTION_BEFORE] || + triggers->bodies[TRG_EVENT_DELETE][TRG_ACTION_AFTER]) + { + /* TODO: optimize to only add columns used by trigger */ + use_all_columns(); + return; + } + } + + if (file->ha_table_flags() & HA_REQUIRES_KEY_COLUMNS_FOR_DELETE) + { + Field **reg_field; + for (reg_field= field ; *reg_field ; reg_field++) + { + if ((*reg_field)->flags & PART_KEY_FLAG) + bitmap_set_bit(read_set, (*reg_field)->field_index); + } + file->column_bitmaps_signal(); + } + if (file->ha_table_flags() & HA_PRIMARY_KEY_REQUIRED_FOR_DELETE) + { + /* + If the handler has no cursor capabilites, we have to read either + the primary key, the hidden primary key or all columns to be + able to do an delete + */ + if (s->primary_key == MAX_KEY) + file->use_hidden_primary_key(); + else + { + mark_columns_used_by_index_no_reset(s->primary_key, read_set); + file->column_bitmaps_signal(); + } + } +} + + +/* + Mark columns needed for doing an update of a row + + DESCRIPTON + Some engines needs to have all columns in an update (to be able to + build a complete row). If this is the case, we mark all not + updated columns to be read. + + If this is no the case, we do like in the delete case and mark + if neeed, either the primary key column or all columns to be read. + (see mark_columns_needed_for_delete() for details) + + If the engine has HA_REQUIRES_KEY_COLUMNS_FOR_DELETE, we will + mark all USED key columns as 'to-be-read'. This allows the engine to + loop over the given record to find all changed keys and doesn't have to + retrieve the row again. +*/ + +void st_table::mark_columns_needed_for_update() +{ + DBUG_ENTER("mark_columns_needed_for_update"); + if (triggers) + { + if (triggers->bodies[TRG_EVENT_UPDATE][TRG_ACTION_BEFORE] || + triggers->bodies[TRG_EVENT_UPDATE][TRG_ACTION_AFTER]) + { + /* TODO: optimize to only add columns used by trigger */ + use_all_columns(); + DBUG_VOID_RETURN; + } + } + if (file->ha_table_flags() & HA_REQUIRES_KEY_COLUMNS_FOR_DELETE) + { + /* Mark all used key columns for read */ + Field **reg_field; + for (reg_field= field ; *reg_field ; reg_field++) + { + /* Merge keys is all keys that had a column refered to in the query */ + if (merge_keys.is_overlapping((*reg_field)->part_of_key)) + bitmap_set_bit(read_set, (*reg_field)->field_index); + } + file->column_bitmaps_signal(); + } + if (file->ha_table_flags() & HA_PRIMARY_KEY_REQUIRED_FOR_DELETE) + { + /* + If the handler has no cursor capabilites, we have to read either + the primary key, the hidden primary key or all columns to be + able to do an update + */ + if (s->primary_key == MAX_KEY) + file->use_hidden_primary_key(); + else + { + mark_columns_used_by_index_no_reset(s->primary_key, read_set); + file->column_bitmaps_signal(); + } + } + DBUG_VOID_RETURN; +} + + +/* + Mark columns the handler needs for doing an insert + + For now, this is used to mark fields used by the trigger + as changed. +*/ + +void st_table::mark_columns_needed_for_insert() +{ + if (triggers) + { + if (triggers->bodies[TRG_EVENT_INSERT][TRG_ACTION_BEFORE] || + triggers->bodies[TRG_EVENT_INSERT][TRG_ACTION_AFTER]) + { + /* TODO: optimize to only add columns used by trigger */ + use_all_columns(); + return; + } + } + if (found_next_number_field) + mark_auto_increment_column(); +} + /***************************************************************************** ** Instansiate templates diff --git a/sql/table.h b/sql/table.h index ffecc60b19c..71a199d946e 100644 --- a/sql/table.h +++ b/sql/table.h @@ -137,6 +137,7 @@ typedef struct st_table_share char *comment; /* Comment about table */ CHARSET_INFO *table_charset; /* Default charset of string fields */ + MY_BITMAP all_set; /* A pair "database_name\0table_name\0", widely used as simply a db name */ LEX_STRING table_cache_key; LEX_STRING db; /* Pointer to db */ @@ -181,6 +182,7 @@ typedef struct st_table_share uint next_number_index; uint next_number_key_offset; uint error, open_errno, errarg; /* error from open_table_def() */ + uint column_bitmap_size; uchar frm_version; bool null_field_first; bool system; /* Set if system table (one record) */ @@ -244,7 +246,7 @@ struct st_table { byte *write_row_record; /* Used as optimisation in THD::write_row */ byte *insert_values; /* used by INSERT ... UPDATE */ - key_map quick_keys, used_keys, keys_in_use_for_query; + key_map quick_keys, used_keys, keys_in_use_for_query, merge_keys; KEY *key_info; /* data of keys in database */ Field *next_number_field; /* Set if next_number is activated */ @@ -257,8 +259,9 @@ struct st_table { ORDER *group; const char *alias; /* alias or table name */ uchar *null_flags; - MY_BITMAP *read_set; - MY_BITMAP *write_set; + my_bitmap_map *bitmap_init_value; + MY_BITMAP def_read_set, def_write_set, tmp_set; /* containers */ + MY_BITMAP *read_set, *write_set; /* Active column sets */ query_id_t query_id; ha_rows quick_rows[MAX_KEY]; @@ -330,6 +333,39 @@ struct st_table { bool fill_item_list(List<Item> *item_list) const; void reset_item_list(List<Item> *item_list) const; + void clear_column_bitmaps(void); + void prepare_for_position(void); + void mark_columns_used_by_index_no_reset(uint index, MY_BITMAP *map); + void mark_columns_used_by_index(uint index); + void restore_column_maps_after_mark_index(); + void mark_auto_increment_column(void); + void mark_columns_needed_for_update(void); + void mark_columns_needed_for_delete(void); + void mark_columns_needed_for_insert(void); + inline void column_bitmaps_set(MY_BITMAP *read_set_arg, + MY_BITMAP *write_set_arg) + { + read_set= read_set_arg; + write_set= write_set_arg; + if (file) + file->column_bitmaps_signal(); + } + inline void column_bitmaps_set_no_signal(MY_BITMAP *read_set_arg, + MY_BITMAP *write_set_arg) + { + read_set= read_set_arg; + write_set= write_set_arg; + } + inline void use_all_columns() + { + column_bitmaps_set(&s->all_set, &s->all_set); + } + inline void default_column_bitmaps() + { + read_set= &def_read_set; + write_set= &def_write_set; + } + }; @@ -901,3 +937,38 @@ my_bool table_check_intact(TABLE *table, uint table_f_count, TABLE_FIELD_W_TYPE *table_def, time_t *last_create_time, int error_num); + +static inline my_bitmap_map *tmp_use_all_columns(TABLE *table, + MY_BITMAP *bitmap) +{ + my_bitmap_map *old= bitmap->bitmap; + bitmap->bitmap= table->s->all_set.bitmap; + return old; +} + + +static inline void tmp_restore_column_map(MY_BITMAP *bitmap, + my_bitmap_map *old) +{ + bitmap->bitmap= old; +} + +/* The following is only needed for debugging */ + +static inline my_bitmap_map *dbug_tmp_use_all_columns(TABLE *table, + MY_BITMAP *bitmap) +{ +#ifndef DBUG_OFF + return tmp_use_all_columns(table, bitmap); +#else + return 0; +#endif +} + +static inline void dbug_tmp_restore_column_map(MY_BITMAP *bitmap, + my_bitmap_map *old) +{ +#ifndef DBUG_OFF + tmp_restore_column_map(bitmap, old); +#endif +} diff --git a/sql/time.cc b/sql/time.cc index 3c654de23bb..be1e4f10825 100644 --- a/sql/time.cc +++ b/sql/time.cc @@ -692,6 +692,7 @@ void make_truncated_value_warning(THD *thd, const char *str_val, char buff[128]; String str(buff,(uint32) sizeof(buff), system_charset_info); str.copy(str_val, str_length, system_charset_info); + str[str_length]= 0; // Ensure we have end 0 for snprintf switch (time_type) { case MYSQL_TIMESTAMP_DATE: diff --git a/sql/tztime.cc b/sql/tztime.cc index 228a8cd9b92..77d7efdcf7c 100644 --- a/sql/tztime.cc +++ b/sql/tztime.cc @@ -1661,6 +1661,8 @@ my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap) for MyISAM. */ (void)table->file->ha_index_init(0, 1); + table->use_all_columns(); + tz_leapcnt= 0; res= table->file->index_first(table->record[0]); @@ -1804,10 +1806,8 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables) #ifdef ABBR_ARE_USED char chars[max(TZ_MAX_CHARS + 1, (2 * (MY_TZNAME_MAX + 1)))]; #endif - DBUG_ENTER("tz_load_from_open_tables"); - /* Prepare tz_info for loading also let us make copy of time zone name */ if (!(alloc_buff= alloc_root(&tz_storage, sizeof(TIME_ZONE_INFO) + tz_name->length() + 1))) @@ -1830,6 +1830,7 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables) */ table= tz_tables->table; tz_tables= tz_tables->next_local; + table->use_all_columns(); table->field[0]->store(tz_name->ptr(), tz_name->length(), &my_charset_latin1); /* @@ -1862,6 +1863,7 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables) using the only index in this table). */ table= tz_tables->table; + table->use_all_columns(); tz_tables= tz_tables->next_local; table->field[0]->store((longlong) tzid, TRUE); (void)table->file->ha_index_init(0, 1); @@ -1889,6 +1891,7 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables) Right - using special index. */ table= tz_tables->table; + table->use_all_columns(); tz_tables= tz_tables->next_local; table->field[0]->store((longlong) tzid, TRUE); (void)table->file->ha_index_init(0, 1); @@ -1962,6 +1965,7 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables) in ascending order by index scan also satisfies us. */ table= tz_tables->table; + table->use_all_columns(); table->field[0]->store((longlong) tzid, TRUE); (void)table->file->ha_index_init(0, 1); @@ -2280,14 +2284,15 @@ my_tz_find(const String * name, TABLE_LIST *tz_tables) RETURN VALUE Pointer to corresponding Time_zone object. 0 - in case of bad time zone specification or other error. - */ + Time_zone *my_tz_find_with_opening_tz_tables(THD *thd, const String *name) { Time_zone *tz; DBUG_ENTER("my_tz_find_with_opening_tables"); DBUG_ASSERT(thd); DBUG_ASSERT(thd->slave_thread); // intended for use with slave thread only + if (!(tz= my_tz_find(name, 0)) && time_zone_tables_exist) { /* diff --git a/storage/archive/ha_archive.cc b/storage/archive/ha_archive.cc index f3f20f6b103..e6c9c5dccd7 100644 --- a/storage/archive/ha_archive.cc +++ b/storage/archive/ha_archive.cc @@ -139,59 +139,17 @@ static HASH archive_open_tables; #define ARCHIVE_CHECK_HEADER 254 // The number we use to determine corruption /* Static declarations for handerton */ -static handler *archive_create_handler(TABLE_SHARE *table); +static handler *archive_create_handler(TABLE_SHARE *table, MEM_ROOT *mem_root); /* Number of rows that will force a bulk insert. */ #define ARCHIVE_MIN_ROWS_TO_USE_BULK_INSERT 2 +handlerton archive_hton; -static const char archive_hton_name[]= "ARCHIVE"; -static const char archive_hton_comment[]= "Archive storage engine"; - -/* dummy handlerton - only to have something to return from archive_db_init */ -handlerton archive_hton = { - MYSQL_HANDLERTON_INTERFACE_VERSION, - archive_hton_name, - SHOW_OPTION_YES, - archive_hton_comment, - DB_TYPE_ARCHIVE_DB, - archive_db_init, - 0, /* slot */ - 0, /* savepoint size. */ - NULL, /* close_connection */ - NULL, /* savepoint */ - NULL, /* rollback to savepoint */ - NULL, /* releas savepoint */ - NULL, /* commit */ - NULL, /* rollback */ - NULL, /* prepare */ - NULL, /* recover */ - NULL, /* commit_by_xid */ - NULL, /* rollback_by_xid */ - NULL, /* create_cursor_read_view */ - NULL, /* set_cursor_read_view */ - NULL, /* close_cursor_read_view */ - archive_create_handler, /* Create a new handler */ - NULL, /* Drop a database */ - archive_db_end, /* Panic call */ - NULL, /* Start Consistent Snapshot */ - NULL, /* Flush logs */ - NULL, /* Show status */ - NULL, /* Partition flags */ - NULL, /* Alter table flags */ - NULL, /* Alter interface */ - NULL, /* fill_files_table */ - HTON_NO_FLAGS, - NULL, /* binlog_func */ - NULL, /* binlog_log_query */ - NULL /* release_temporary_latches */ - -}; - -static handler *archive_create_handler(TABLE_SHARE *table) +static handler *archive_create_handler(TABLE_SHARE *table, MEM_ROOT *mem_root) { - return new ha_archive(table); + return new (mem_root) ha_archive(table); } /* @@ -217,11 +175,18 @@ static byte* archive_get_key(ARCHIVE_SHARE *share,uint *length, TRUE Error */ -bool archive_db_init() +int archive_db_init() { DBUG_ENTER("archive_db_init"); if (archive_inited) DBUG_RETURN(FALSE); + + archive_hton.state=SHOW_OPTION_YES; + archive_hton.db_type=DB_TYPE_ARCHIVE_DB; + archive_hton.create=archive_create_handler; + archive_hton.panic=archive_db_end; + archive_hton.flags=HTON_NO_FLAGS; + if (pthread_mutex_init(&archive_mutex, MY_MUTEX_INIT_FAST)) goto error; if (hash_init(&archive_open_tables, system_charset_info, 32, 0, 0, @@ -697,18 +662,10 @@ int ha_archive::create(const char *name, TABLE *table_arg, int error; DBUG_ENTER("ha_archive::create"); - auto_increment_value= (create_info->auto_increment_value ? - create_info->auto_increment_value -1 : + stats.auto_increment_value= (create_info->auto_increment_value ? + create_info->auto_increment_value -1 : (ulonglong) 0); - if ((create_file= my_create(fn_format(name_buff,name,"",ARM, - MY_REPLACE_EXT|MY_UNPACK_FILENAME),0, - O_RDWR | O_TRUNC,MYF(MY_WME))) < 0) - { - error= my_errno; - goto error; - } - for (uint key= 0; key < table_arg->s->keys; key++) { KEY *pos= table_arg->key_info+key; @@ -722,12 +679,21 @@ int ha_archive::create(const char *name, TABLE *table_arg, if (!(field->flags & AUTO_INCREMENT_FLAG)) { error= -1; + DBUG_PRINT("info", ("Index error in creating archive table")); goto error; } } } - write_meta_file(create_file, 0, auto_increment_value, 0, + if ((create_file= my_create(fn_format(name_buff,name,"",ARM, + MY_REPLACE_EXT|MY_UNPACK_FILENAME),0, + O_RDWR | O_TRUNC,MYF(MY_WME))) < 0) + { + error= my_errno; + goto error; + } + + write_meta_file(create_file, 0, stats.auto_increment_value, 0, (char *)create_info->data_file_name, FALSE); my_close(create_file,MYF(0)); @@ -923,7 +889,7 @@ int ha_archive::write_row(byte *buf) else { if (temp_auto > share->auto_increment_value) - auto_increment_value= share->auto_increment_value= temp_auto; + stats.auto_increment_value= share->auto_increment_value= temp_auto; } } @@ -946,9 +912,13 @@ error: } -ulonglong ha_archive::get_auto_increment() +void ha_archive::get_auto_increment(ulonglong offset, ulonglong increment, + ulonglong nb_desired_values, + ulonglong *first_value, + ulonglong *nb_reserved_values) { - return share->auto_increment_value + 1; + *nb_reserved_values= 1; + *first_value= share->auto_increment_value + 1; } /* Initialized at each key walk (called multiple times unlike rnd_init()) */ @@ -1058,7 +1028,7 @@ int ha_archive::rnd_init(bool scan) { scan_rows= share->rows_recorded; DBUG_PRINT("info", ("archive will retrieve %llu rows", scan_rows)); - records= 0; + stats.records= 0; /* If dirty, we lock, and then reset/flush the data. @@ -1095,6 +1065,7 @@ int ha_archive::get_row(azio_stream *file_to_read, byte *buf) uint *ptr, *end; char *last; size_t total_blob_length= 0; + MY_BITMAP *read_set= table->read_set; DBUG_ENTER("ha_archive::get_row"); read= azread(file_to_read, buf, table->s->reclength); @@ -1120,8 +1091,9 @@ int ha_archive::get_row(azio_stream *file_to_read, byte *buf) ptr != end ; ptr++) { - if (ha_get_bit_in_read_set(((Field_blob*) table->field[*ptr])->fieldnr)) - total_blob_length += ((Field_blob*) table->field[*ptr])->get_length(); + if (bitmap_is_set(read_set, + (((Field_blob*) table->field[*ptr])->field_index))) + total_blob_length += ((Field_blob*) table->field[*ptr])->get_length(); } /* Adjust our row buffer if we need be */ @@ -1136,7 +1108,8 @@ int ha_archive::get_row(azio_stream *file_to_read, byte *buf) size_t size= ((Field_blob*) table->field[*ptr])->get_length(); if (size) { - if (ha_get_bit_in_read_set(((Field_blob*) table->field[*ptr])->fieldnr)) + if (bitmap_is_set(read_set, + ((Field_blob*) table->field[*ptr])->field_index)) { read= azread(file_to_read, last, size); if ((size_t) read != size) @@ -1177,7 +1150,7 @@ int ha_archive::rnd_next(byte *buf) if (rc != HA_ERR_END_OF_FILE) - records++; + stats.records++; DBUG_RETURN(rc); } @@ -1298,7 +1271,7 @@ int ha_archive::optimize(THD* thd, HA_CHECK_OPT* check_opt) if (!rc) { share->rows_recorded= 0; - auto_increment_value= share->auto_increment_value= 0; + stats.auto_increment_value= share->auto_increment_value= 0; while (!(rc= get_row(&archive, buf))) { real_write_row(buf, &writer); @@ -1308,7 +1281,7 @@ int ha_archive::optimize(THD* thd, HA_CHECK_OPT* check_opt) ulonglong auto_value= (ulonglong) field->val_int((char*)(buf + field->offset())); if (share->auto_increment_value < auto_value) - auto_increment_value= share->auto_increment_value= + stats.auto_increment_value= share->auto_increment_value= auto_value; } share->rows_recorded++; @@ -1355,25 +1328,18 @@ int ha_archive::optimize(THD* thd, HA_CHECK_OPT* check_opt) we don't check rc here because we want to open the file back up even if the optimize failed but we will return rc below so that we will know it failed. + We also need to reopen our read descriptor since it has changed. */ DBUG_PRINT("info", ("Reopening archive data file")); - if (!(azopen(&(share->archive_write), share->data_file_name, - O_WRONLY|O_APPEND|O_BINARY))) + if (!azopen(&(share->archive_write), share->data_file_name, + O_WRONLY|O_APPEND|O_BINARY) || + !azopen(&archive, share->data_file_name, O_RDONLY|O_BINARY)) { DBUG_PRINT("info", ("Could not open archive write file")); rc= HA_ERR_CRASHED_ON_USAGE; - goto error; - } - - /* - Now we need to reopen our read descriptor since it has changed. - */ - if (!(azopen(&archive, share->data_file_name, O_RDONLY|O_BINARY))) - { - rc= HA_ERR_CRASHED_ON_USAGE; - goto error; } + DBUG_RETURN(rc); error: azclose(&writer); @@ -1430,7 +1396,7 @@ void ha_archive::update_create_info(HA_CREATE_INFO *create_info) ha_archive::info(HA_STATUS_AUTO | HA_STATUS_CONST); if (!(create_info->used_fields & HA_CREATE_USED_AUTO)) { - create_info->auto_increment_value= auto_increment_value; + create_info->auto_increment_value= stats.auto_increment_value; } if (*share->real_path) create_info->data_file_name= share->real_path; @@ -1447,8 +1413,8 @@ void ha_archive::info(uint flag) This should be an accurate number now, though bulk and delayed inserts can cause the number to be inaccurate. */ - records= share->rows_recorded; - deleted= 0; + stats.records= share->rows_recorded; + stats.deleted= 0; /* Costs quite a bit more to get all information */ if (flag & HA_STATUS_TIME) { @@ -1456,17 +1422,17 @@ void ha_archive::info(uint flag) VOID(my_stat(share->data_file_name, &file_stat, MYF(MY_WME))); - mean_rec_length= table->s->reclength + buffer.alloced_length(); - data_file_length= file_stat.st_size; - create_time= file_stat.st_ctime; - update_time= file_stat.st_mtime; - max_data_file_length= share->rows_recorded * mean_rec_length; + stats.mean_rec_length= table->s->reclength + buffer.alloced_length(); + stats.data_file_length= file_stat.st_size; + stats.create_time= file_stat.st_ctime; + stats.update_time= file_stat.st_mtime; + stats.max_data_file_length= share->rows_recorded * stats.mean_rec_length; } - delete_length= 0; - index_file_length=0; + stats.delete_length= 0; + stats.index_file_length=0; if (flag & HA_STATUS_AUTO) - auto_increment_value= share->auto_increment_value; + stats.auto_increment_value= share->auto_increment_value; DBUG_VOID_RETURN; } @@ -1582,16 +1548,20 @@ bool ha_archive::check_and_repair(THD *thd) DBUG_RETURN(repair(thd, &check_opt)); } +struct st_mysql_storage_engine archive_storage_engine= +{ MYSQL_HANDLERTON_INTERFACE_VERSION, &archive_hton }; mysql_declare_plugin(archive) { MYSQL_STORAGE_ENGINE_PLUGIN, - &archive_hton, - archive_hton_name, + &archive_storage_engine, + "ARCHIVE", "Brian Aker, MySQL AB", - archive_hton_comment, - NULL, /* Plugin Init */ + "Archive storage engine", + archive_db_init, /* Plugin Init */ archive_db_done, /* Plugin Deinit */ 0x0100 /* 1.0 */, + 0 } mysql_declare_plugin_end; + diff --git a/storage/archive/ha_archive.h b/storage/archive/ha_archive.h index 4663531b674..0485b7e9f9c 100644 --- a/storage/archive/ha_archive.h +++ b/storage/archive/ha_archive.h @@ -74,16 +74,19 @@ public: const char *table_type() const { return "ARCHIVE"; } const char *index_type(uint inx) { return "NONE"; } const char **bas_ext() const; - ulong table_flags() const + ulonglong table_flags() const { - return (HA_REC_NOT_IN_SEQ | HA_NOT_EXACT_COUNT | HA_CAN_BIT_FIELD | + return (HA_NO_TRANSACTIONS | HA_REC_NOT_IN_SEQ | HA_CAN_BIT_FIELD | HA_FILE_BASED | HA_CAN_INSERT_DELAYED | HA_CAN_GEOMETRY); } ulong index_flags(uint idx, uint part, bool all_parts) const { return HA_ONLY_WHOLE_INDEX; } - ulonglong get_auto_increment(); + virtual void get_auto_increment(ulonglong offset, ulonglong increment, + ulonglong nb_desired_values, + ulonglong *first_value, + ulonglong *nb_reserved_values); uint max_supported_keys() const { return 1; } uint max_supported_key_length() const { return sizeof(ulonglong); } uint max_supported_key_part_length() const { return sizeof(ulonglong); } @@ -136,6 +139,6 @@ public: bool check_and_repair(THD *thd); }; -bool archive_db_init(void); +int archive_db_init(void); int archive_db_end(ha_panic_function type); diff --git a/storage/blackhole/ha_blackhole.cc b/storage/blackhole/ha_blackhole.cc index 098e44f39ee..1a2bb264ef9 100644 --- a/storage/blackhole/ha_blackhole.cc +++ b/storage/blackhole/ha_blackhole.cc @@ -22,61 +22,13 @@ #include "mysql_priv.h" #include "ha_blackhole.h" -#include <mysql/plugin.h> - /* Static declarations for handlerton */ -static handler *blackhole_create_handler(TABLE_SHARE *table); - - -static const char blackhole_hton_name[]= "BLACKHOLE"; -static const char blackhole_hton_comment[]= - "/dev/null storage engine (anything you write to it disappears)"; - -/* Blackhole storage engine handlerton */ - -handlerton blackhole_hton= { - MYSQL_HANDLERTON_INTERFACE_VERSION, - blackhole_hton_name, - SHOW_OPTION_YES, - blackhole_hton_comment, - DB_TYPE_BLACKHOLE_DB, - NULL, - 0, /* slot */ - 0, /* savepoint size. */ - NULL, /* close_connection */ - NULL, /* savepoint */ - NULL, /* rollback to savepoint */ - NULL, /* release savepoint */ - NULL, /* commit */ - NULL, /* rollback */ - NULL, /* prepare */ - NULL, /* recover */ - NULL, /* commit_by_xid */ - NULL, /* rollback_by_xid */ - NULL, /* create_cursor_read_view */ - NULL, /* set_cursor_read_view */ - NULL, /* close_cursor_read_view */ - blackhole_create_handler, /* Create a new handler */ - NULL, /* Drop a database */ - NULL, /* Panic call */ - NULL, /* Start Consistent Snapshot */ - NULL, /* Flush logs */ - NULL, /* Show status */ - NULL, /* Partition flags */ - NULL, /* Alter table flags */ - NULL, /* Alter Tablespace */ - NULL, /* Fill FILES table */ - HTON_CAN_RECREATE | HTON_ALTER_CANNOT_CREATE, - NULL, /* binlog_func */ - NULL, /* binlog_log_query */ - NULL /* release_temporary_latches */ -}; - - -static handler *blackhole_create_handler(TABLE_SHARE *table) +handlerton blackhole_hton; +static handler *blackhole_create_handler(TABLE_SHARE *table, + MEM_ROOT *mem_root) { - return new ha_blackhole(table); + return new (mem_root) ha_blackhole(table); } @@ -171,16 +123,9 @@ void ha_blackhole::info(uint flag) { DBUG_ENTER("ha_blackhole::info"); - records= 0; - deleted= 0; - errkey= 0; - mean_rec_length= 0; - data_file_length= 0; - index_file_length= 0; - max_data_file_length= 0; - delete_length= 0; + bzero((char*) &stats, sizeof(stats)); if (flag & HA_STATUS_AUTO) - auto_increment_value= 1; + stats.auto_increment_value= 1; DBUG_VOID_RETURN; } @@ -256,15 +201,28 @@ int ha_blackhole::index_last(byte * buf) DBUG_RETURN(HA_ERR_END_OF_FILE); } +static int blackhole_init() +{ + blackhole_hton.state= SHOW_OPTION_YES; + blackhole_hton.db_type= DB_TYPE_BLACKHOLE_DB; + blackhole_hton.create= blackhole_create_handler; + blackhole_hton.flags= HTON_CAN_RECREATE | HTON_ALTER_CANNOT_CREATE; + return 0; +} + +struct st_mysql_storage_engine blackhole_storage_engine= +{ MYSQL_HANDLERTON_INTERFACE_VERSION, &blackhole_hton }; + mysql_declare_plugin(blackhole) { MYSQL_STORAGE_ENGINE_PLUGIN, - &blackhole_hton, - blackhole_hton_name, + &blackhole_storage_engine, + "BLACKHOLE", "MySQL AB", - blackhole_hton_comment, - NULL, /* Plugin Init */ + "/dev/null storage engine (anything you write to it disappears)", + blackhole_init, /* Plugin Init */ NULL, /* Plugin Deinit */ 0x0100 /* 1.0 */, + 0 } mysql_declare_plugin_end; diff --git a/storage/blackhole/ha_blackhole.h b/storage/blackhole/ha_blackhole.h index 15e12659aa0..55c26f6f02e 100644 --- a/storage/blackhole/ha_blackhole.h +++ b/storage/blackhole/ha_blackhole.h @@ -40,12 +40,11 @@ public: */ const char *index_type(uint key_number); const char **bas_ext() const; - ulong table_flags() const + ulonglong table_flags() const { return(HA_NULL_IN_KEY | HA_CAN_FULLTEXT | HA_CAN_SQL_HANDLER | - HA_DUPP_POS | HA_CAN_INDEX_BLOBS | HA_AUTO_PART_KEY | - HA_FILE_BASED | HA_CAN_GEOMETRY | HA_READ_RND_SAME | - HA_CAN_INSERT_DELAYED); + HA_CAN_INDEX_BLOBS | HA_AUTO_PART_KEY | + HA_FILE_BASED | HA_CAN_GEOMETRY | HA_CAN_INSERT_DELAYED); } ulong index_flags(uint inx, uint part, bool all_parts) const { @@ -84,5 +83,4 @@ public: THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to, enum thr_lock_type lock_type); - bool has_transactions() { return 1; } }; diff --git a/storage/csv/ha_tina.cc b/storage/csv/ha_tina.cc index f15ce7cccb5..e5ddba7540d 100644 --- a/storage/csv/ha_tina.cc +++ b/storage/csv/ha_tina.cc @@ -74,48 +74,10 @@ static int write_meta_file(File meta_file, ha_rows rows, bool dirty); pthread_mutex_t tina_mutex; static HASH tina_open_tables; static int tina_init= 0; -static handler *tina_create_handler(TABLE_SHARE *table); +static handler *tina_create_handler(TABLE_SHARE *table, MEM_ROOT *mem_root); static int tina_init_func(); -static const char tina_hton_name[]= "CSV"; -static const char tina_hton_comment[]= "CSV storage engine"; - -handlerton tina_hton= { - MYSQL_HANDLERTON_INTERFACE_VERSION, - tina_hton_name, - SHOW_OPTION_YES, - tina_hton_comment, - DB_TYPE_CSV_DB, - (bool (*)()) tina_init_func, - 0, /* slot */ - 0, /* savepoint size. */ - NULL, /* close_connection */ - NULL, /* savepoint */ - NULL, /* rollback to savepoint */ - NULL, /* release savepoint */ - NULL, /* commit */ - NULL, /* rollback */ - NULL, /* prepare */ - NULL, /* recover */ - NULL, /* commit_by_xid */ - NULL, /* rollback_by_xid */ - NULL, /* create_cursor_read_view */ - NULL, /* set_cursor_read_view */ - NULL, /* close_cursor_read_view */ - tina_create_handler, /* Create a new handler */ - NULL, /* Drop a database */ - tina_end, /* Panic call */ - NULL, /* Start Consistent Snapshot */ - NULL, /* Flush logs */ - NULL, /* Show status */ - NULL, /* Partition flags */ - NULL, /* Alter table flags */ - NULL, /* Alter Tablespace */ - NULL, /* Fill FILES Table */ - HTON_CAN_RECREATE, - NULL, /* binlog_func */ - NULL /* binlog_log_query */ -}; +handlerton tina_hton; /***************************************************************************** ** TINA tables @@ -197,6 +159,11 @@ static int tina_init_func() VOID(pthread_mutex_init(&tina_mutex,MY_MUTEX_INIT_FAST)); (void) hash_init(&tina_open_tables,system_charset_info,32,0,0, (hash_get_key) tina_get_key,0,0); + tina_hton.state= SHOW_OPTION_YES; + tina_hton.db_type= DB_TYPE_CSV_DB; + tina_hton.create= tina_create_handler; + tina_hton.panic= tina_end; + tina_hton.flags= HTON_CAN_RECREATE; } return 0; } @@ -498,9 +465,9 @@ byte * find_eoln(byte *data, off_t begin, off_t end, int *eoln_len) } -static handler *tina_create_handler(TABLE_SHARE *table) +static handler *tina_create_handler(TABLE_SHARE *table, MEM_ROOT *mem_root) { - return new ha_tina(table); + return new (mem_root) ha_tina(table); } @@ -527,8 +494,10 @@ ha_tina::ha_tina(TABLE_SHARE *table_arg) int ha_tina::encode_quote(byte *buf) { char attribute_buffer[1024]; - String attribute(attribute_buffer, sizeof(attribute_buffer), &my_charset_bin); + String attribute(attribute_buffer, sizeof(attribute_buffer), + &my_charset_bin); + my_bitmap_map *org_bitmap= dbug_tmp_use_all_columns(table, table->read_set); buffer.length(0); for (Field **field=table->field ; *field ; field++) { @@ -589,6 +558,7 @@ int ha_tina::encode_quote(byte *buf) buffer.append('\n'); //buffer.replace(buffer.length(), 0, "\n", 1); + dbug_tmp_restore_column_map(table->read_set, org_bitmap); return (buffer.length()); } @@ -642,6 +612,7 @@ int ha_tina::find_current_row(byte *buf) byte *mapped_ptr; byte *end_ptr; int eoln_len; + my_bitmap_map *org_bitmap; DBUG_ENTER("ha_tina::find_current_row"); mapped_ptr= (byte *)share->mapped_file + current_position; @@ -654,13 +625,19 @@ int ha_tina::find_current_row(byte *buf) local_saved_data_file_length, &eoln_len)) == 0) DBUG_RETURN(HA_ERR_END_OF_FILE); + /* Avoid asserts in ::store() for columns that are not going to be updated */ + org_bitmap= dbug_tmp_use_all_columns(table, table->write_set); + for (Field **field=table->field ; *field ; field++) { buffer.length(0); if (*mapped_ptr == '"') mapped_ptr++; // Increment past the first quote else + { + dbug_tmp_restore_column_map(table->write_set, org_bitmap); DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE); + } for(;mapped_ptr != end_ptr; mapped_ptr++) { // Need to convert line feeds! @@ -693,15 +670,20 @@ int ha_tina::find_current_row(byte *buf) we are working with a damaged file. */ if (mapped_ptr == end_ptr -1) + { + dbug_tmp_restore_column_map(table->write_set, org_bitmap); DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE); + } buffer.append(*mapped_ptr); } } - (*field)->store(buffer.ptr(), buffer.length(), system_charset_info); + if (bitmap_is_set(table->read_set, (*field)->field_index)) + (*field)->store(buffer.ptr(), buffer.length(), system_charset_info); } next_position= (end_ptr - share->mapped_file)+eoln_len; /* Maybe use \N for null? */ memset(buf, 0, table->s->null_bytes); /* We do not implement nulls! */ + dbug_tmp_restore_column_map(table->write_set, org_bitmap); DBUG_RETURN(0); } @@ -919,7 +901,7 @@ int ha_tina::write_row(byte * buf) update_status(); pthread_mutex_unlock(&share->mutex); - records++; + stats.records++; DBUG_RETURN(0); } @@ -978,7 +960,7 @@ int ha_tina::delete_row(const byte * buf) if (chain_append()) DBUG_RETURN(-1); - --records; + stats.records--; /* DELETE should never happen on the log table */ DBUG_ASSERT(!share->is_log_table); @@ -1026,7 +1008,7 @@ int ha_tina::rnd_init(bool scan) DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE); current_position= next_position= 0; - records= 0; + stats.records= 0; records_is_known= 0; chain_ptr= chain; #ifdef HAVE_MADVISE @@ -1068,7 +1050,7 @@ int ha_tina::rnd_next(byte *buf) if ((rc= find_current_row(buf))) DBUG_RETURN(rc); - records++; + stats.records++; DBUG_RETURN(0); } @@ -1111,8 +1093,8 @@ void ha_tina::info(uint flag) { DBUG_ENTER("ha_tina::info"); /* This is a lie, but you don't want the optimizer to see zero or 1 */ - if (!records_is_known && records < 2) - records= 2; + if (!records_is_known && stats.records < 2) + stats.records= 2; DBUG_VOID_RETURN; } @@ -1225,6 +1207,8 @@ int ha_tina::repair(THD* thd, HA_CHECK_OPT* check_opt) goto end; } + /* Don't assert in field::val() functions */ + table->use_all_columns(); if (!(buf= (byte*) my_malloc(table->s->reclength, MYF(MY_WME)))) DBUG_RETURN(HA_ERR_OUT_OF_MEM); @@ -1328,7 +1312,7 @@ int ha_tina::delete_all_rows() if (get_mmap(share, 0) > 0) DBUG_RETURN(-1); - records=0; + stats.records=0; DBUG_RETURN(rc); } @@ -1422,17 +1406,20 @@ bool ha_tina::check_if_incompatible_data(HA_CREATE_INFO *info, return COMPATIBLE_DATA_YES; } +struct st_mysql_storage_engine csv_storage_engine= +{ MYSQL_HANDLERTON_INTERFACE_VERSION, &tina_hton }; mysql_declare_plugin(csv) { MYSQL_STORAGE_ENGINE_PLUGIN, - &tina_hton, - tina_hton_name, + &csv_storage_engine, + "CSV", "Brian Aker, MySQL AB", - tina_hton_comment, + "CSV storage engine", tina_init_func, /* Plugin Init */ tina_done_func, /* Plugin Deinit */ 0x0100 /* 1.0 */, + 0 } mysql_declare_plugin_end; diff --git a/storage/csv/ha_tina.h b/storage/csv/ha_tina.h index d155a614780..d3a8c5092b6 100644 --- a/storage/csv/ha_tina.h +++ b/storage/csv/ha_tina.h @@ -87,14 +87,16 @@ public: const char *table_type() const { return "CSV"; } const char *index_type(uint inx) { return "NONE"; } const char **bas_ext() const; - ulong table_flags() const + ulonglong table_flags() const { - return (HA_REC_NOT_IN_SEQ | HA_NOT_EXACT_COUNT | - HA_NO_AUTO_INCREMENT ); + return (HA_NO_TRANSACTIONS | HA_REC_NOT_IN_SEQ | HA_NO_AUTO_INCREMENT); } ulong index_flags(uint idx, uint part, bool all_parts) const { - /* We will never have indexes so this will never be called(AKA we return zero) */ + /* + We will never have indexes so this will never be called(AKA we return + zero) + */ return 0; } uint max_record_length() const { return HA_MAX_REC_LENGTH; } @@ -104,7 +106,7 @@ public: /* Called in test_quick_select to determine if indexes should be used. */ - virtual double scan_time() { return (double) (records+deleted) / 20.0+10; } + virtual double scan_time() { return (double) (stats.records+stats.deleted) / 20.0+10; } /* The next method will never be called */ virtual bool fast_key_read() { return 1;} /* diff --git a/storage/example/ha_example.cc b/storage/example/ha_example.cc index 2ce543dfbb0..feabad2e356 100644 --- a/storage/example/ha_example.cc +++ b/storage/example/ha_example.cc @@ -72,51 +72,12 @@ #include <mysql/plugin.h> -static handler* example_create_handler(TABLE_SHARE *table); +static handler *example_create_handler(TABLE_SHARE *table, MEM_ROOT *mem_root); static int example_init_func(); static bool example_init_func_for_handlerton(); static int example_panic(enum ha_panic_function flag); -static const char example_hton_name[]= "EXAMPLE"; -static const char example_hton_comment[]= "Example storage engine"; - -handlerton example_hton= { - MYSQL_HANDLERTON_INTERFACE_VERSION, - example_hton_name, - SHOW_OPTION_YES, - example_hton_comment, - DB_TYPE_EXAMPLE_DB, - example_init_func_for_handlerton, - 0, /* slot */ - 0, /* savepoint size. */ - NULL, /* close_connection */ - NULL, /* savepoint */ - NULL, /* rollback to savepoint */ - NULL, /* release savepoint */ - NULL, /* commit */ - NULL, /* rollback */ - NULL, /* prepare */ - NULL, /* recover */ - NULL, /* commit_by_xid */ - NULL, /* rollback_by_xid */ - NULL, /* create_cursor_read_view */ - NULL, /* set_cursor_read_view */ - NULL, /* close_cursor_read_view */ - example_create_handler, /* Create a new handler */ - NULL, /* Drop a database */ - example_panic, /* Panic call */ - NULL, /* Start Consistent Snapshot */ - NULL, /* Flush logs */ - NULL, /* Show status */ - NULL, /* Partition flags */ - NULL, /* Alter table flags */ - NULL, /* Alter tablespace */ - NULL, /* Fill Files table */ - HTON_CAN_RECREATE, - NULL, - NULL, - NULL, -}; +handlerton example_hton; /* Variables for example share methods */ static HASH example_open_tables; // Hash used to track open tables @@ -143,6 +104,11 @@ static int example_init_func() VOID(pthread_mutex_init(&example_mutex,MY_MUTEX_INIT_FAST)); (void) hash_init(&example_open_tables,system_charset_info,32,0,0, (hash_get_key) example_get_key,0,0); + + example_hton.state= SHOW_OPTION_YES; + example_hton.db_type= DB_TYPE_EXAMPLE_DB; + example_hton.create= example_create_handler; + example_hton.flags= HTON_CAN_RECREATE; } DBUG_RETURN(0); } @@ -163,17 +129,6 @@ static int example_done_func() DBUG_RETURN(0); } -static bool example_init_func_for_handlerton() -{ - return example_init_func(); -} - -static int example_panic(enum ha_panic_function flag) -{ - return example_done_func(); -} - - /* Example of simple lock controls. The "share" it creates is structure we will pass to each example handler. Do you have to have one of these? Well, you have @@ -244,9 +199,9 @@ static int free_share(EXAMPLE_SHARE *share) } -static handler* example_create_handler(TABLE_SHARE *table) +static handler* example_create_handler(TABLE_SHARE *table, MEM_ROOT *mem_root) { - return new ha_example(table); + return new (mem_root) ha_example(table); } @@ -741,21 +696,25 @@ int ha_example::create(const char *name, TABLE *table_arg, HA_CREATE_INFO *create_info) { DBUG_ENTER("ha_example::create"); - /* This is not implemented but we want someone to be able that it works. */ + /* This is not implemented but we want someone to be able to see that it works. */ DBUG_RETURN(0); } +struct st_mysql_storage_engine example_storage_engine= +{ MYSQL_HANDLERTON_INTERFACE_VERSION, &example_hton }; + mysql_declare_plugin(example) { MYSQL_STORAGE_ENGINE_PLUGIN, - &example_hton, - example_hton_name, + &example_storage_engine, + "EXAMPLE", "Brian Aker, MySQL AB", - example_hton_comment, + "Example storage engine", example_init_func, /* Plugin Init */ example_done_func, /* Plugin Deinit */ 0x0001 /* 0.1 */, + 0 } mysql_declare_plugin_end; diff --git a/storage/example/ha_example.h b/storage/example/ha_example.h index 139a50a3281..956dc62311c 100644 --- a/storage/example/ha_example.h +++ b/storage/example/ha_example.h @@ -62,7 +62,7 @@ public: implements. The current table flags are documented in handler.h */ - ulong table_flags() const + ulonglong table_flags() const { return 0; } @@ -97,7 +97,7 @@ public: /* Called in test_quick_select to determine if indexes should be used. */ - virtual double scan_time() { return (double) (records+deleted) / 20.0+10; } + virtual double scan_time() { return (double) (stats.records+stats.deleted) / 20.0+10; } /* The next method will never be called if you do not implement indexes. */ diff --git a/storage/heap/hp_extra.c b/storage/heap/hp_extra.c index dd41d6c5f19..abb632707f2 100644 --- a/storage/heap/hp_extra.c +++ b/storage/heap/hp_extra.c @@ -32,13 +32,8 @@ int heap_extra(register HP_INFO *info, enum ha_extra_function function) DBUG_ENTER("heap_extra"); switch (function) { - case HA_EXTRA_RESET: case HA_EXTRA_RESET_STATE: - info->lastinx= -1; - info->current_record= (ulong) ~0L; - info->current_hash_ptr=0; - info->update=0; - break; + heap_reset(info); case HA_EXTRA_NO_READCHECK: info->opt_flag&= ~READ_CHECK_USED; /* No readcheck */ break; @@ -56,6 +51,16 @@ int heap_extra(register HP_INFO *info, enum ha_extra_function function) } /* heap_extra */ +int heap_reset(HP_INFO *info) +{ + info->lastinx= -1; + info->current_record= (ulong) ~0L; + info->current_hash_ptr=0; + info->update=0; + return 0; +} + + /* Start/Stop Inserting Duplicates Into a Table, WL#1648. */ diff --git a/storage/heap/hp_test2.c b/storage/heap/hp_test2.c index a74872dbd11..8d2a8bc3da2 100644 --- a/storage/heap/hp_test2.c +++ b/storage/heap/hp_test2.c @@ -469,7 +469,7 @@ int main(int argc, char *argv[]) #endif printf("- Read through all records with scan\n"); - if (heap_extra(file,HA_EXTRA_RESET) || heap_extra(file,HA_EXTRA_CACHE)) + if (heap_reset(file) || heap_extra(file,HA_EXTRA_CACHE)) { puts("got error from heap_extra"); goto end; diff --git a/storage/heap/hp_write.c b/storage/heap/hp_write.c index a60d32eecb6..bc94e3bfae4 100644 --- a/storage/heap/hp_write.c +++ b/storage/heap/hp_write.c @@ -105,7 +105,7 @@ int hp_rb_write_key(HP_INFO *info, HP_KEYDEF *keyinfo, const byte *record, custom_arg.key_length= hp_rb_make_key(keyinfo, info->recbuf, record, recpos); if (keyinfo->flag & HA_NOSAME) { - custom_arg.search_flag= SEARCH_FIND | SEARCH_SAME | SEARCH_UPDATE; + custom_arg.search_flag= SEARCH_FIND | SEARCH_UPDATE; keyinfo->rb_tree.flag= TREE_NO_DUPS; } else diff --git a/storage/myisam/ft_boolean_search.c b/storage/myisam/ft_boolean_search.c index 8d48f533203..cb4005d5c84 100644 --- a/storage/myisam/ft_boolean_search.c +++ b/storage/myisam/ft_boolean_search.c @@ -160,7 +160,6 @@ static int FTB_WORD_cmp_list(CHARSET_INFO *cs, FTB_WORD **a, FTB_WORD **b) typedef struct st_my_ftb_param { - MYSQL_FTPARSER_PARAM *up; FTB *ftb; FTB_EXPR *ftbe; byte *up_quot; @@ -168,10 +167,11 @@ typedef struct st_my_ftb_param } MY_FTB_PARAM; -static int ftb_query_add_word(void *param, char *word, int word_len, +static int ftb_query_add_word(MYSQL_FTPARSER_PARAM *param, + char *word, int word_len, MYSQL_FTPARSER_BOOLEAN_INFO *info) { - MY_FTB_PARAM *ftb_param= (MY_FTB_PARAM *)param; + MY_FTB_PARAM *ftb_param= param->mysql_ftparam; FTB_WORD *ftbw; FTB_EXPR *ftbe, *tmp_expr; FT_WORD *phrase_word; @@ -269,9 +269,10 @@ static int ftb_query_add_word(void *param, char *word, int word_len, } -static int ftb_parse_query_internal(void *param, char *query, int len) +static int ftb_parse_query_internal(MYSQL_FTPARSER_PARAM *param, + char *query, int len) { - MY_FTB_PARAM *ftb_param= (MY_FTB_PARAM *)param; + MY_FTB_PARAM *ftb_param= param->mysql_ftparam; MYSQL_FTPARSER_BOOLEAN_INFO info; CHARSET_INFO *cs= ftb_param->ftb->charset; char **start= &query; @@ -281,7 +282,7 @@ static int ftb_parse_query_internal(void *param, char *query, int len) info.prev= ' '; info.quot= 0; while (ft_get_word(cs, start, end, &w, &info)) - ftb_param->up->mysql_add_word(param, w.pos, w.len, &info); + param->mysql_add_word(param, w.pos, w.len, &info); return(0); } @@ -299,7 +300,6 @@ static void _ftb_parse_query(FTB *ftb, byte *query, uint len, if (! (param= ftparser_call_initializer(ftb->info, ftb->keynr, 0))) DBUG_VOID_RETURN; - ftb_param.up= param; ftb_param.ftb= ftb; ftb_param.depth= 0; ftb_param.ftbe= ftb->root; @@ -311,6 +311,7 @@ static void _ftb_parse_query(FTB *ftb, byte *query, uint len, param->cs= ftb->charset; param->doc= query; param->length= len; + param->flags= 0; param->mode= MYSQL_FTPARSER_FULL_BOOLEAN_INFO; parser->parse(param); DBUG_VOID_RETURN; @@ -571,7 +572,6 @@ err: typedef struct st_my_ftb_phrase_param { - MYSQL_FTPARSER_PARAM *up; LIST *phrase; LIST *document; CHARSET_INFO *cs; @@ -581,10 +581,11 @@ typedef struct st_my_ftb_phrase_param } MY_FTB_PHRASE_PARAM; -static int ftb_phrase_add_word(void *param, char *word, int word_len, +static int ftb_phrase_add_word(MYSQL_FTPARSER_PARAM *param, + char *word, int word_len, MYSQL_FTPARSER_BOOLEAN_INFO *boolean_info __attribute__((unused))) { - MY_FTB_PHRASE_PARAM *phrase_param= (MY_FTB_PHRASE_PARAM *)param; + MY_FTB_PHRASE_PARAM *phrase_param= param->mysql_ftparam; FT_WORD *w= (FT_WORD *)phrase_param->document->data; LIST *phrase, *document; w->pos= word; @@ -602,8 +603,9 @@ static int ftb_phrase_add_word(void *param, char *word, int word_len, { FT_WORD *phrase_word= (FT_WORD *)phrase->data; FT_WORD *document_word= (FT_WORD *)document->data; - if (my_strnncoll(phrase_param->cs, phrase_word->pos, phrase_word->len, - document_word->pos, document_word->len)) + if (my_strnncoll(phrase_param->cs, (uchar*) phrase_word->pos, + phrase_word->len, + (uchar*) document_word->pos, document_word->len)) return 0; } phrase_param->match++; @@ -611,14 +613,15 @@ static int ftb_phrase_add_word(void *param, char *word, int word_len, } -static int ftb_check_phrase_internal(void *param, char *document, int len) +static int ftb_check_phrase_internal(MYSQL_FTPARSER_PARAM *param, + char *document, int len) { FT_WORD word; - MY_FTB_PHRASE_PARAM *phrase_param= (MY_FTB_PHRASE_PARAM *)param; + MY_FTB_PHRASE_PARAM *phrase_param= param->mysql_ftparam; const char *docend= document + len; while (ft_simple_get_word(phrase_param->cs, &document, docend, &word, FALSE)) { - phrase_param->up->mysql_add_word(param, word.pos, word.len, 0); + param->mysql_add_word(param, word.pos, word.len, 0); if (phrase_param->match) return 1; } @@ -651,7 +654,6 @@ static int _ftb_check_phrase(FTB *ftb, const byte *document, uint len, if (! (param= ftparser_call_initializer(ftb->info, ftb->keynr, 1))) DBUG_RETURN(0); - ftb_param.up= param; ftb_param.phrase= ftbe->phrase; ftb_param.document= ftbe->document; ftb_param.cs= ftb->charset; @@ -665,6 +667,7 @@ static int _ftb_check_phrase(FTB *ftb, const byte *document, uint len, param->cs= ftb->charset; param->doc= (byte *)document; param->length= len; + param->flags= 0; param->mode= MYSQL_FTPARSER_WITH_STOPWORDS; parser->parse(param); DBUG_RETURN(ftb_param.match ? 1 : 0); @@ -820,16 +823,16 @@ err: typedef struct st_my_ftb_find_param { - MYSQL_FTPARSER_PARAM *up; FT_INFO *ftb; FT_SEG_ITERATOR *ftsi; } MY_FTB_FIND_PARAM; -static int ftb_find_relevance_add_word(void *param, char *word, int len, +static int ftb_find_relevance_add_word(MYSQL_FTPARSER_PARAM *param, + char *word, int len, MYSQL_FTPARSER_BOOLEAN_INFO *boolean_info __attribute__((unused))) { - MY_FTB_FIND_PARAM *ftb_param= (MY_FTB_FIND_PARAM *)param; + MY_FTB_FIND_PARAM *ftb_param= param->mysql_ftparam; FT_INFO *ftb= ftb_param->ftb; FTB_WORD *ftbw; int a, b, c; @@ -859,14 +862,15 @@ static int ftb_find_relevance_add_word(void *param, char *word, int len, } -static int ftb_find_relevance_parse(void *param, char *doc, int len) +static int ftb_find_relevance_parse(MYSQL_FTPARSER_PARAM *param, + char *doc, int len) { - MY_FTB_FIND_PARAM *ftb_param=(MY_FTB_FIND_PARAM *)param; + MY_FTB_FIND_PARAM *ftb_param= param->mysql_ftparam; FT_INFO *ftb= ftb_param->ftb; char *end= doc + len; FT_WORD w; while (ft_simple_get_word(ftb->charset, &doc, end, &w, TRUE)) - ftb_param->up->mysql_add_word(param, w.pos, w.len, 0); + param->mysql_add_word(param, w.pos, w.len, 0); return(0); } @@ -910,12 +914,12 @@ float ft_boolean_find_relevance(FT_INFO *ftb, byte *record, uint length) _mi_ft_segiterator_init(ftb->info, ftb->keynr, record, &ftsi); memcpy(&ftsi2, &ftsi, sizeof(ftsi)); - ftb_param.up= param; ftb_param.ftb= ftb; ftb_param.ftsi= &ftsi2; param->mysql_parse= ftb_find_relevance_parse; param->mysql_add_word= ftb_find_relevance_add_word; param->mysql_ftparam= (void *)&ftb_param; + param->flags= 0; param->cs= ftb->charset; param->mode= MYSQL_FTPARSER_SIMPLE_MODE; while (_mi_ft_segiterator(&ftsi)) diff --git a/storage/myisam/ft_nlq_search.c b/storage/myisam/ft_nlq_search.c index b8207c1c3d0..53a01003dcd 100644 --- a/storage/myisam/ft_nlq_search.c +++ b/storage/myisam/ft_nlq_search.c @@ -235,7 +235,9 @@ FT_INFO *ft_init_nlq_search(MI_INFO *info, uint keynr, byte *query, NULL, NULL); ft_parse_init(&wtree, aio.charset); - if (ft_parse(&wtree, query, query_len, 0, parser, ftparser_param)) + ftparser_param->flags= 0; + if (ft_parse(&wtree, query, query_len, parser, ftparser_param, + &wtree.mem_root)) goto err; if (tree_walk(&wtree, (tree_walk_action)&walk_and_match, &aio, @@ -255,7 +257,9 @@ FT_INFO *ft_init_nlq_search(MI_INFO *info, uint keynr, byte *query, if (!(*info->read_record)(info,docid,record)) { info->update|= HA_STATE_AKTIV; - _mi_ft_parse(&wtree, info, keynr, record, 1, ftparser_param); + ftparser_param->flags= MYSQL_FTFLAGS_NEED_COPY; + _mi_ft_parse(&wtree, info, keynr, record, ftparser_param, + &wtree.mem_root); } } delete_queue(&best); diff --git a/storage/myisam/ft_parser.c b/storage/myisam/ft_parser.c index 89ede813a2b..38ac744d4a8 100644 --- a/storage/myisam/ft_parser.c +++ b/storage/myisam/ft_parser.c @@ -24,15 +24,12 @@ typedef struct st_ft_docstat { double sum; } FT_DOCSTAT; - typedef struct st_my_ft_parser_param { - MYSQL_FTPARSER_PARAM *up; - TREE *wtree; - my_bool with_alloc; + TREE *wtree; + MEM_ROOT *mem_root; } MY_FT_PARSER_PARAM; - static int FT_WORD_cmp(CHARSET_INFO* cs, FT_WORD *w1, FT_WORD *w2) { return mi_compare_text(cs, (uchar*) w1->pos, w1->len, @@ -49,14 +46,14 @@ static int walk_and_copy(FT_WORD *word,uint32 count,FT_DOCSTAT *docstat) /* transforms tree of words into the array, applying normalization */ -FT_WORD * ft_linearize(TREE *wtree) +FT_WORD * ft_linearize(TREE *wtree, MEM_ROOT *mem_root) { FT_WORD *wlist,*p; FT_DOCSTAT docstat; DBUG_ENTER("ft_linearize"); - if ((wlist=(FT_WORD *) my_malloc(sizeof(FT_WORD)* - (1+wtree->elements_in_tree),MYF(0)))) + if ((wlist=(FT_WORD *) alloc_root(mem_root, sizeof(FT_WORD)* + (1+wtree->elements_in_tree)))) { docstat.list=wlist; docstat.uniq=wtree->elements_in_tree; @@ -241,19 +238,20 @@ void ft_parse_init(TREE *wtree, CHARSET_INFO *cs) } -static int ft_add_word(void *param, byte *word, uint word_len, +static int ft_add_word(MYSQL_FTPARSER_PARAM *param, + char *word, int word_len, MYSQL_FTPARSER_BOOLEAN_INFO *boolean_info __attribute__((unused))) { TREE *wtree; FT_WORD w; + MY_FT_PARSER_PARAM *ft_param=param->mysql_ftparam; DBUG_ENTER("ft_add_word"); - wtree= ((MY_FT_PARSER_PARAM *)param)->wtree; - if (((MY_FT_PARSER_PARAM *)param)->with_alloc) + wtree= ft_param->wtree; + if (param->flags & MYSQL_FTFLAGS_NEED_COPY) { byte *ptr; - /* allocating the data in the tree - to avoid mallocs and frees */ DBUG_ASSERT(wtree->with_delete == 0); - ptr= (byte *)alloc_root(&wtree->mem_root, word_len); + ptr= (byte *)alloc_root(ft_param->mem_root, word_len); memcpy(ptr, word, word_len); w.pos= ptr; } @@ -269,32 +267,32 @@ static int ft_add_word(void *param, byte *word, uint word_len, } -static int ft_parse_internal(void *param, byte *doc, int doc_len) +static int ft_parse_internal(MYSQL_FTPARSER_PARAM *param, + byte *doc, int doc_len) { byte *end=doc+doc_len; - MY_FT_PARSER_PARAM *ft_param=(MY_FT_PARSER_PARAM *)param; + MY_FT_PARSER_PARAM *ft_param=param->mysql_ftparam; TREE *wtree= ft_param->wtree; FT_WORD w; DBUG_ENTER("ft_parse_internal"); while (ft_simple_get_word(wtree->custom_arg, &doc, end, &w, TRUE)) - if (ft_param->up->mysql_add_word(param, w.pos, w.len, 0)) + if (param->mysql_add_word(param, w.pos, w.len, 0)) DBUG_RETURN(1); DBUG_RETURN(0); } -int ft_parse(TREE *wtree, byte *doc, int doclen, my_bool with_alloc, +int ft_parse(TREE *wtree, byte *doc, int doclen, struct st_mysql_ftparser *parser, - MYSQL_FTPARSER_PARAM *param) + MYSQL_FTPARSER_PARAM *param, MEM_ROOT *mem_root) { MY_FT_PARSER_PARAM my_param; DBUG_ENTER("ft_parse"); DBUG_ASSERT(parser); - my_param.up= param; my_param.wtree= wtree; - my_param.with_alloc= with_alloc; + my_param.mem_root= mem_root; param->mysql_parse= ft_parse_internal; param->mysql_add_word= ft_add_word; @@ -356,6 +354,7 @@ MYSQL_FTPARSER_PARAM *ftparser_call_initializer(MI_INFO *info, info->ftparser_param= (MYSQL_FTPARSER_PARAM *) my_malloc(MAX_PARAM_NR * sizeof(MYSQL_FTPARSER_PARAM) * info->s->ftparsers, MYF(MY_WME|MY_ZEROFILL)); + init_alloc_root(&info->ft_memroot, FTPARSER_MEMROOT_ALLOC_SIZE, 0); if (! info->ftparser_param) return 0; } @@ -387,6 +386,7 @@ MYSQL_FTPARSER_PARAM *ftparser_call_initializer(MI_INFO *info, void ftparser_call_deinitializer(MI_INFO *info) { uint i, j, keys= info->s->state.header.keys; + free_root(&info->ft_memroot, MYF(0)); if (! info->ftparser_param) return; for (i= 0; i < keys; i++) diff --git a/storage/myisam/ft_static.c b/storage/myisam/ft_static.c index 6cfb0d59e62..61bb7b3c1f7 100644 --- a/storage/myisam/ft_static.c +++ b/storage/myisam/ft_static.c @@ -629,7 +629,7 @@ const char *ft_precompiled_stopwords[] = { static int ft_default_parser_parse(MYSQL_FTPARSER_PARAM *param) { - return param->mysql_parse(param->mysql_ftparam, param->doc, param->length); + return param->mysql_parse(param, param->doc, param->length); } struct st_mysql_ftparser ft_default_parser= diff --git a/storage/myisam/ft_update.c b/storage/myisam/ft_update.c index f5501792dcd..08d605dbcc2 100644 --- a/storage/myisam/ft_update.c +++ b/storage/myisam/ft_update.c @@ -77,7 +77,7 @@ uint _mi_ft_segiterator(register FT_SEG_ITERATOR *ftsi) { uint pack_length= (ftsi->seg->bit_start); ftsi->len= (pack_length == 1 ? (uint) *(uchar*) ftsi->pos : - uint2korr(ftsi->pos)); + uint2korr(ftsi->pos)); ftsi->pos+= pack_length; /* Skip VARCHAR length */ DBUG_RETURN(1); } @@ -95,9 +95,8 @@ uint _mi_ft_segiterator(register FT_SEG_ITERATOR *ftsi) /* parses a document i.e. calls ft_parse for every keyseg */ -uint _mi_ft_parse(TREE *parsed, MI_INFO *info, uint keynr, - const byte *record, my_bool with_alloc, - MYSQL_FTPARSER_PARAM *param) +uint _mi_ft_parse(TREE *parsed, MI_INFO *info, uint keynr, const byte *record, + MYSQL_FTPARSER_PARAM *param, MEM_ROOT *mem_root) { FT_SEG_ITERATOR ftsi; struct st_mysql_ftparser *parser; @@ -110,14 +109,14 @@ uint _mi_ft_parse(TREE *parsed, MI_INFO *info, uint keynr, while (_mi_ft_segiterator(&ftsi)) { if (ftsi.pos) - if (ft_parse(parsed, (byte *)ftsi.pos, ftsi.len, with_alloc, parser, - param)) + if (ft_parse(parsed, (byte *)ftsi.pos, ftsi.len, parser, param, mem_root)) DBUG_RETURN(1); } DBUG_RETURN(0); } -FT_WORD * _mi_ft_parserecord(MI_INFO *info, uint keynr, const byte *record) +FT_WORD *_mi_ft_parserecord(MI_INFO *info, uint keynr, const byte *record, + MEM_ROOT *mem_root) { TREE ptree; MYSQL_FTPARSER_PARAM *param; @@ -125,10 +124,11 @@ FT_WORD * _mi_ft_parserecord(MI_INFO *info, uint keynr, const byte *record) if (! (param= ftparser_call_initializer(info, keynr, 0))) DBUG_RETURN(NULL); bzero((char*) &ptree, sizeof(ptree)); - if (_mi_ft_parse(&ptree, info, keynr, record, 0, param)) + param->flags= 0; + if (_mi_ft_parse(&ptree, info, keynr, record, param, mem_root)) DBUG_RETURN(NULL); - DBUG_RETURN(ft_linearize(&ptree)); + DBUG_RETURN(ft_linearize(&ptree, mem_root)); } static int _mi_ft_store(MI_INFO *info, uint keynr, byte *keybuf, @@ -206,10 +206,11 @@ int _mi_ft_update(MI_INFO *info, uint keynr, byte *keybuf, int cmp, cmp2; DBUG_ENTER("_mi_ft_update"); - if (!(old_word=oldlist=_mi_ft_parserecord(info, keynr, oldrec))) - goto err0; - if (!(new_word=newlist=_mi_ft_parserecord(info, keynr, newrec))) - goto err1; + if (!(old_word=oldlist=_mi_ft_parserecord(info, keynr, oldrec, + &info->ft_memroot)) || + !(new_word=newlist=_mi_ft_parserecord(info, keynr, newrec, + &info->ft_memroot))) + goto err; error=0; while(old_word->pos && new_word->pos) @@ -222,13 +223,13 @@ int _mi_ft_update(MI_INFO *info, uint keynr, byte *keybuf, { key_length=_ft_make_key(info,keynr,keybuf,old_word,pos); if ((error=_mi_ck_delete(info,keynr,(uchar*) keybuf,key_length))) - goto err2; + goto err; } if (cmp > 0 || cmp2) { key_length=_ft_make_key(info,keynr,keybuf,new_word,pos); if ((error=_mi_ck_write(info,keynr,(uchar*) keybuf,key_length))) - goto err2; + goto err; } if (cmp<=0) old_word++; if (cmp>=0) new_word++; @@ -238,11 +239,8 @@ int _mi_ft_update(MI_INFO *info, uint keynr, byte *keybuf, else if (new_word->pos) error=_mi_ft_store(info,keynr,keybuf,new_word,pos); -err2: - my_free((char*) newlist,MYF(0)); -err1: - my_free((char*) oldlist,MYF(0)); -err0: +err: + free_root(&info->ft_memroot, MYF(MY_MARK_BLOCKS_FREE)); DBUG_RETURN(error); } @@ -255,12 +253,13 @@ int _mi_ft_add(MI_INFO *info, uint keynr, byte *keybuf, const byte *record, int error= -1; FT_WORD *wlist; DBUG_ENTER("_mi_ft_add"); + DBUG_PRINT("enter",("keynr: %d",keynr)); - if ((wlist=_mi_ft_parserecord(info, keynr, record))) - { + if ((wlist=_mi_ft_parserecord(info, keynr, record, &info->ft_memroot))) error=_mi_ft_store(info,keynr,keybuf,wlist,pos); - my_free((char*) wlist,MYF(0)); - } + + free_root(&info->ft_memroot, MYF(MY_MARK_BLOCKS_FREE)); + DBUG_PRINT("exit",("Return: %d",error)); DBUG_RETURN(error); } @@ -275,11 +274,10 @@ int _mi_ft_del(MI_INFO *info, uint keynr, byte *keybuf, const byte *record, DBUG_ENTER("_mi_ft_del"); DBUG_PRINT("enter",("keynr: %d",keynr)); - if ((wlist=_mi_ft_parserecord(info, keynr, record))) - { + if ((wlist=_mi_ft_parserecord(info, keynr, record, &info->ft_memroot))) error=_mi_ft_erase(info,keynr,keybuf,wlist,pos); - my_free((char*) wlist,MYF(0)); - } + + free_root(&info->ft_memroot, MYF(MY_MARK_BLOCKS_FREE)); DBUG_PRINT("exit",("Return: %d",error)); DBUG_RETURN(error); } diff --git a/storage/myisam/ftdefs.h b/storage/myisam/ftdefs.h index 2c4cfa1ffd6..108faf4f1a3 100644 --- a/storage/myisam/ftdefs.h +++ b/storage/myisam/ftdefs.h @@ -30,6 +30,8 @@ #define FT_MAX_WORD_LEN_FOR_SORT 31 +#define FTPARSER_MEMROOT_ALLOC_SIZE 65536 + #define COMPILE_STOPWORDS_IN /* Interested readers may consult SMART @@ -119,12 +121,12 @@ void _mi_ft_segiterator_dummy_init(const byte *, uint, FT_SEG_ITERATOR *); uint _mi_ft_segiterator(FT_SEG_ITERATOR *); void ft_parse_init(TREE *, CHARSET_INFO *); -int ft_parse(TREE *, byte *, int, my_bool, struct st_mysql_ftparser *parser, - MYSQL_FTPARSER_PARAM *param); -FT_WORD * ft_linearize(TREE *); -FT_WORD * _mi_ft_parserecord(MI_INFO *, uint, const byte *); -uint _mi_ft_parse(TREE *, MI_INFO *, uint, const byte *, my_bool, - MYSQL_FTPARSER_PARAM *param); +int ft_parse(TREE *, byte *, int, struct st_mysql_ftparser *parser, + MYSQL_FTPARSER_PARAM *, MEM_ROOT *); +FT_WORD * ft_linearize(TREE *, MEM_ROOT *); +FT_WORD * _mi_ft_parserecord(MI_INFO *, uint, const byte *, MEM_ROOT *); +uint _mi_ft_parse(TREE *, MI_INFO *, uint, const byte *, + MYSQL_FTPARSER_PARAM *, MEM_ROOT *); FT_INFO *ft_init_nlq_search(MI_INFO *, uint, byte *, uint, uint, byte *); FT_INFO *ft_init_boolean_search(MI_INFO *, uint, byte *, uint, CHARSET_INFO *); diff --git a/storage/myisam/mi_check.c b/storage/myisam/mi_check.c index 0fa095a21db..d91597e9138 100644 --- a/storage/myisam/mi_check.c +++ b/storage/myisam/mi_check.c @@ -453,25 +453,24 @@ int chk_key(MI_CHECK *param, register MI_INFO *info) if ((uint) share->base.auto_key -1 == key) { /* Check that auto_increment key is bigger than max key value */ - ulonglong save_auto_value=info->s->state.auto_increment; - info->s->state.auto_increment=0; + ulonglong auto_increment; info->lastinx=key; _mi_read_key_record(info, 0L, info->rec_buff); - update_auto_increment(info, info->rec_buff); - if (info->s->state.auto_increment > save_auto_value) + auto_increment= retrieve_auto_increment(info, info->rec_buff); + if (auto_increment > info->s->state.auto_increment) { - mi_check_print_warning(param, - "Auto-increment value: %s is smaller than max used value: %s", - llstr(save_auto_value,buff2), - llstr(info->s->state.auto_increment, buff)); + mi_check_print_warning(param, "Auto-increment value: %s is smaller " + "than max used value: %s", + llstr(info->s->state.auto_increment,buff2), + llstr(auto_increment, buff)); } if (param->testflag & T_AUTO_INC) { - set_if_bigger(info->s->state.auto_increment, - param->auto_increment_value); + set_if_bigger(info->s->state.auto_increment, + auto_increment); + set_if_bigger(info->s->state.auto_increment, + param->auto_increment_value); } - else - info->s->state.auto_increment=save_auto_value; /* Check that there isn't a row with auto_increment = 0 in the table */ mi_extra(info,HA_EXTRA_KEYREAD,0); @@ -481,8 +480,8 @@ int chk_key(MI_CHECK *param, register MI_INFO *info) { /* Don't count this as a real warning, as myisamchk can't correct it */ uint save=param->warning_printed; - mi_check_print_warning(param, - "Found row where the auto_increment column has the value 0"); + mi_check_print_warning(param, "Found row where the auto_increment " + "column has the value 0"); param->warning_printed=save; } mi_extra(info,HA_EXTRA_NO_KEYREAD,0); @@ -2117,6 +2116,7 @@ int mi_repair_by_sort(MI_CHECK *param, register MI_INFO *info, my_seek(param->read_cache.file,0L,MY_SEEK_END,MYF(0)); sort_param.wordlist=NULL; + init_alloc_root(&sort_param.wordroot, FTPARSER_MEMROOT_ALLOC_SIZE, 0); if (share->data_file_type == DYNAMIC_RECORD) length=max(share->base.min_pack_length+1,share->base.min_block_length); @@ -2179,12 +2179,36 @@ int mi_repair_by_sort(MI_CHECK *param, register MI_INFO *info, { uint ft_max_word_len_for_sort=FT_MAX_WORD_LEN_FOR_SORT* sort_param.keyinfo->seg->charset->mbmaxlen; - sort_info.max_records= - (ha_rows) (sort_info.filelength/ft_min_word_len+1); + sort_param.key_length+=ft_max_word_len_for_sort-HA_FT_MAXBYTELEN; + /* + fulltext indexes may have much more entries than the + number of rows in the table. We estimate the number here. + + Note, built-in parser is always nr. 0 - see ftparser_call_initializer() + */ + if (sort_param.keyinfo->ftparser_nr == 0) + { + /* + for built-in parser the number of generated index entries + cannot be larger than the size of the data file divided + by the minimal word's length + */ + sort_info.max_records= + (ha_rows) (sort_info.filelength/ft_min_word_len+1); + } + else + { + /* + for external plugin parser we cannot tell anything at all :( + so, we'll use all the sort memory and start from ~10 buffpeks. + (see _create_index_by_sort) + */ + sort_info.max_records= + 10*param->sort_buffer_length/sort_param.key_length; + } sort_param.key_read=sort_ft_key_read; sort_param.key_write=sort_ft_key_write; - sort_param.key_length+=ft_max_word_len_for_sort-HA_FT_MAXBYTELEN; } else { @@ -2200,6 +2224,7 @@ int mi_repair_by_sort(MI_CHECK *param, register MI_INFO *info, goto err; } param->calc_checksum=0; /* No need to calc glob_crc */ + free_root(&sort_param.wordroot, MYF(0)); /* Set for next loop */ sort_info.max_records= (ha_rows) info->state->records; @@ -2589,6 +2614,7 @@ int mi_repair_parallel(MI_CHECK *param, register MI_INFO *info, uint ft_max_word_len_for_sort=FT_MAX_WORD_LEN_FOR_SORT* sort_param[i].keyinfo->seg->charset->mbmaxlen; sort_param[i].key_length+=ft_max_word_len_for_sort-HA_FT_MAXBYTELEN; + init_alloc_root(&sort_param[i].wordroot, FTPARSER_MEMROOT_ALLOC_SIZE, 0); } } sort_info.total_keys=i; @@ -2810,10 +2836,11 @@ static int sort_ft_key_read(MI_SORT_PARAM *sort_param, void *key) { for (;;) { - my_free((char*) wptr, MYF(MY_ALLOW_ZERO_PTR)); + free_root(&sort_param->wordroot, MYF(MY_MARK_BLOCKS_FREE)); if ((error=sort_get_next_record(sort_param))) DBUG_RETURN(error); - if (!(wptr=_mi_ft_parserecord(info,sort_param->key,sort_param->record))) + if (!(wptr=_mi_ft_parserecord(info,sort_param->key,sort_param->record, + &sort_param->wordroot))) DBUG_RETURN(1); if (wptr->pos) break; @@ -2837,7 +2864,7 @@ static int sort_ft_key_read(MI_SORT_PARAM *sort_param, void *key) #endif if (!wptr->pos) { - my_free((char*) sort_param->wordlist, MYF(0)); + free_root(&sort_param->wordroot, MYF(MY_MARK_BLOCKS_FREE)); sort_param->wordlist=0; error=sort_write_record(sort_param); } @@ -4096,11 +4123,10 @@ void update_auto_increment_key(MI_CHECK *param, MI_INFO *info, } else { - ulonglong auto_increment= (repair_only ? info->s->state.auto_increment : - param->auto_increment_value); - info->s->state.auto_increment=0; - update_auto_increment(info, record); + ulonglong auto_increment= retrieve_auto_increment(info, record); set_if_bigger(info->s->state.auto_increment,auto_increment); + if (!repair_only) + set_if_bigger(info->s->state.auto_increment, param->auto_increment_value); } mi_extra(info,HA_EXTRA_NO_KEYREAD,0); my_free((char*) record, MYF(0)); diff --git a/storage/myisam/mi_extra.c b/storage/myisam/mi_extra.c index 04beb36bb47..c1ed29c4734 100644 --- a/storage/myisam/mi_extra.c +++ b/storage/myisam/mi_extra.c @@ -47,29 +47,6 @@ int mi_extra(MI_INFO *info, enum ha_extra_function function, void *extra_arg) DBUG_PRINT("enter",("function: %d",(int) function)); switch (function) { - case HA_EXTRA_RESET: - /* - Free buffers and reset the following flags: - EXTRA_CACHE, EXTRA_WRITE_CACHE, EXTRA_KEYREAD, EXTRA_QUICK - - If the row buffer cache is large (for dynamic tables), reduce it - to save memory. - */ - if (info->opt_flag & (READ_CACHE_USED | WRITE_CACHE_USED)) - { - info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED); - error=end_io_cache(&info->rec_cache); - } - if (share->base.blobs) - mi_alloc_rec_buff(info, -1, &info->rec_buff); -#if defined(HAVE_MMAP) && defined(HAVE_MADVISE) - if (info->opt_flag & MEMMAP_USED) - madvise(share->file_map,share->state.state.data_file_length,MADV_RANDOM); -#endif - info->opt_flag&= ~(KEY_READ_USED | REMEMBER_OLD_POS); - info->quick_mode=0; - /* Fall through */ - case HA_EXTRA_RESET_STATE: /* Reset state (don't free buffers) */ info->lastinx= 0; /* Use first index as def */ info->last_search_keypage=info->lastpos= HA_OFFSET_ERROR; @@ -425,3 +402,36 @@ static void mi_extra_keyflag(MI_INFO *info, enum ha_extra_function function) } } + +int mi_reset(MI_INFO *info) +{ + int error= 0; + MYISAM_SHARE *share=info->s; + DBUG_ENTER("mi_reset"); + /* + Free buffers and reset the following flags: + EXTRA_CACHE, EXTRA_WRITE_CACHE, EXTRA_KEYREAD, EXTRA_QUICK + + If the row buffer cache is large (for dynamic tables), reduce it + to save memory. + */ + if (info->opt_flag & (READ_CACHE_USED | WRITE_CACHE_USED)) + { + info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED); + error= end_io_cache(&info->rec_cache); + } + if (share->base.blobs) + mi_alloc_rec_buff(info, -1, &info->rec_buff); +#if defined(HAVE_MMAP) && defined(HAVE_MADVISE) + if (info->opt_flag & MEMMAP_USED) + madvise(share->file_map,share->state.state.data_file_length,MADV_RANDOM); +#endif + info->opt_flag&= ~(KEY_READ_USED | REMEMBER_OLD_POS); + info->quick_mode=0; + info->lastinx= 0; /* Use first index as def */ + info->last_search_keypage= info->lastpos= HA_OFFSET_ERROR; + info->page_changed= 1; + info->update= ((info->update & HA_STATE_CHANGED) | HA_STATE_NEXT_FOUND | + HA_STATE_PREV_FOUND); + DBUG_RETURN(error); +} diff --git a/storage/myisam/mi_key.c b/storage/myisam/mi_key.c index 526a733e724..01bd0c43119 100644 --- a/storage/myisam/mi_key.c +++ b/storage/myisam/mi_key.c @@ -507,22 +507,21 @@ int _mi_read_key_record(MI_INFO *info, my_off_t filepos, byte *buf) return(-1); /* Wrong data to read */ } - + /* - Update auto_increment info + Retrieve auto_increment info SYNOPSIS - update_auto_increment() + retrieve_auto_increment() info MyISAM handler record Row to update IMPLEMENTATION - Only replace the auto_increment value if it is higher than the previous - one. For signed columns we don't update the auto increment value if it's + For signed columns we don't retrieve the auto increment value if it's less than zero. */ -void update_auto_increment(MI_INFO *info,const byte *record) +ulonglong retrieve_auto_increment(MI_INFO *info,const byte *record) { ulonglong value= 0; /* Store unsigned values here */ longlong s_value= 0; /* Store signed values here */ @@ -587,6 +586,5 @@ void update_auto_increment(MI_INFO *info,const byte *record) and if s_value == 0 then value will contain either s_value or the correct value. */ - set_if_bigger(info->s->state.auto_increment, - (s_value > 0) ? (ulonglong) s_value : value); + return (s_value > 0) ? (ulonglong) s_value : value; } diff --git a/storage/myisam/mi_search.c b/storage/myisam/mi_search.c index 05f8459a4b4..2117e9fdf15 100644 --- a/storage/myisam/mi_search.c +++ b/storage/myisam/mi_search.c @@ -259,15 +259,16 @@ int _mi_seq_search(MI_INFO *info, register MI_KEYDEF *keyinfo, uchar *page, { mi_print_error(info->s, HA_ERR_CRASHED); my_errno=HA_ERR_CRASHED; - DBUG_PRINT("error",("Found wrong key: length: %u page: %lx end: %lx", - length, (long) page, (long) end)); + DBUG_PRINT("error", + ("Found wrong key: length: %u page: 0x%lx end: 0x%lx", + length, (long) page, (long) end)); DBUG_RETURN(MI_FOUND_WRONG_KEY); } if ((flag=ha_key_cmp(keyinfo->seg,t_buff,key,key_len,comp_flag, not_used)) >= 0) break; #ifdef EXTRA_DEBUG - DBUG_PRINT("loop",("page: %lx key: '%s' flag: %d", (long) page, t_buff, + DBUG_PRINT("loop",("page: 0x%lx key: '%s' flag: %d", (long) page, t_buff, flag)); #endif memcpy(buff,t_buff,length); @@ -276,7 +277,7 @@ int _mi_seq_search(MI_INFO *info, register MI_KEYDEF *keyinfo, uchar *page, if (flag == 0) memcpy(buff,t_buff,length); /* Result is first key */ *last_key= page == end; - DBUG_PRINT("exit",("flag: %d ret_pos: %lx", flag, (long) *ret_pos)); + DBUG_PRINT("exit",("flag: %d ret_pos: 0x%lx", flag, (long) *ret_pos)); DBUG_RETURN(flag); } /* _mi_seq_search */ @@ -416,8 +417,9 @@ int _mi_prefix_search(MI_INFO *info, register MI_KEYDEF *keyinfo, uchar *page, { mi_print_error(info->s, HA_ERR_CRASHED); my_errno=HA_ERR_CRASHED; - DBUG_PRINT("error",("Found wrong key: length: %u page: %lx end: %lx", - length, (long) page, (long) end)); + DBUG_PRINT("error", + ("Found wrong key: length: %u page: 0x%lx end: %lx", + length, (long) page, (long) end)); DBUG_RETURN(MI_FOUND_WRONG_KEY); } @@ -551,7 +553,7 @@ int _mi_prefix_search(MI_INFO *info, register MI_KEYDEF *keyinfo, uchar *page, *last_key= page == end; - DBUG_PRINT("exit",("flag: %d ret_pos: %lx", flag, (long) *ret_pos)); + DBUG_PRINT("exit",("flag: %d ret_pos: 0x%lx", flag, (long) *ret_pos)); DBUG_RETURN(flag); } /* _mi_prefix_search */ @@ -813,7 +815,7 @@ uint _mi_get_pack_key(register MI_KEYDEF *keyinfo, uint nod_flag, if (length > keyseg->length) { DBUG_PRINT("error", - ("Found too long null packed key: %u of %u at %lx", + ("Found too long null packed key: %u of %u at 0x%lx", length, keyseg->length, (long) *page_pos)); DBUG_DUMP("key",(char*) *page_pos,16); mi_print_error(keyinfo->share, HA_ERR_CRASHED); @@ -870,7 +872,7 @@ uint _mi_get_pack_key(register MI_KEYDEF *keyinfo, uint nod_flag, } if (length > (uint) keyseg->length) { - DBUG_PRINT("error",("Found too long packed key: %u of %u at %lx", + DBUG_PRINT("error",("Found too long packed key: %u of %u at 0x%lx", length, keyseg->length, (long) *page_pos)); DBUG_DUMP("key",(char*) *page_pos,16); mi_print_error(keyinfo->share, HA_ERR_CRASHED); @@ -936,8 +938,9 @@ uint _mi_get_binary_pack_key(register MI_KEYDEF *keyinfo, uint nod_flag, { if (length > keyinfo->maxlength) { - DBUG_PRINT("error",("Found too long binary packed key: %u of %u at %lx", - length, keyinfo->maxlength, (long) *page_pos)); + DBUG_PRINT("error", + ("Found too long binary packed key: %u of %u at 0x%lx", + length, keyinfo->maxlength, (long) *page_pos)); DBUG_DUMP("key",(char*) *page_pos,16); mi_print_error(keyinfo->share, HA_ERR_CRASHED); my_errno=HA_ERR_CRASHED; @@ -984,7 +987,7 @@ uint _mi_get_binary_pack_key(register MI_KEYDEF *keyinfo, uint nod_flag, length-=tmp; from=page; from_end=page_end; } - DBUG_PRINT("info",("key: %lx from: %lx length: %u", + DBUG_PRINT("info",("key: 0x%lx from: 0x%lx length: %u", (long) key, (long) from, length)); memmove((byte*) key, (byte*) from, (size_t) length); key+=length; @@ -1042,7 +1045,7 @@ uchar *_mi_get_key(MI_INFO *info, MI_KEYDEF *keyinfo, uchar *page, } } } - DBUG_PRINT("exit",("page: %lx length: %u", (long) page, + DBUG_PRINT("exit",("page: 0x%lx length: %u", (long) page, *return_key_length)); DBUG_RETURN(page); } /* _mi_get_key */ @@ -1095,7 +1098,8 @@ uchar *_mi_get_last_key(MI_INFO *info, MI_KEYDEF *keyinfo, uchar *page, uint nod_flag; uchar *lastpos; DBUG_ENTER("_mi_get_last_key"); - DBUG_PRINT("enter",("page: %lx endpos: %lx", (long) page, (long) endpos)); + DBUG_PRINT("enter",("page: 0x%lx endpos: 0x%lx", (long) page, + (long) endpos)); nod_flag=mi_test_if_nod(page); if (! (keyinfo->flag & (HA_VAR_LENGTH_KEY | HA_BINARY_PACK_KEY))) @@ -1115,7 +1119,7 @@ uchar *_mi_get_last_key(MI_INFO *info, MI_KEYDEF *keyinfo, uchar *page, *return_key_length=(*keyinfo->get_key)(keyinfo,nod_flag,&page,lastkey); if (*return_key_length == 0) { - DBUG_PRINT("error",("Couldn't find last key: page: %lx", + DBUG_PRINT("error",("Couldn't find last key: page: 0x%lx", (long) page)); mi_print_error(info->s, HA_ERR_CRASHED); my_errno=HA_ERR_CRASHED; @@ -1123,7 +1127,7 @@ uchar *_mi_get_last_key(MI_INFO *info, MI_KEYDEF *keyinfo, uchar *page, } } } - DBUG_PRINT("exit",("lastpos: %lx length: %u", (long) lastpos, + DBUG_PRINT("exit",("lastpos: 0x%lx length: %u", (long) lastpos, *return_key_length)); DBUG_RETURN(lastpos); } /* _mi_get_last_key */ @@ -1660,7 +1664,7 @@ _mi_calc_var_pack_key_length(MI_KEYDEF *keyinfo,uint nod_flag,uchar *next_key, ref_length=0; next_length_pack=0; } - DBUG_PRINT("test",("length: %d next_key: %lx", length, + DBUG_PRINT("test",("length: %d next_key: 0x%lx", length, (long) next_key)); { diff --git a/storage/myisam/mi_test2.c b/storage/myisam/mi_test2.c index 357ebb1b9bc..357128b7a40 100644 --- a/storage/myisam/mi_test2.c +++ b/storage/myisam/mi_test2.c @@ -708,7 +708,7 @@ int main(int argc, char *argv[]) if (!silent) printf("- mi_extra(CACHE) + mi_rrnd.... + mi_extra(NO_CACHE)\n"); - if (mi_extra(file,HA_EXTRA_RESET,0) || mi_extra(file,HA_EXTRA_CACHE,0)) + if (mi_reset(file) || mi_extra(file,HA_EXTRA_CACHE,0)) { if (locking || (!use_blob && !pack_fields)) { @@ -751,7 +751,7 @@ int main(int argc, char *argv[]) DBUG_PRINT("progpos",("Removing keys")); lastpos = HA_OFFSET_ERROR; /* DBUG_POP(); */ - mi_extra(file,HA_EXTRA_RESET,0); + mi_reset(file); found_parts=0; while ((error=mi_rrnd(file,read_record,HA_OFFSET_ERROR)) != HA_ERR_END_OF_FILE) diff --git a/storage/myisam/mi_update.c b/storage/myisam/mi_update.c index 937c9983b45..f8b5cf55406 100644 --- a/storage/myisam/mi_update.c +++ b/storage/myisam/mi_update.c @@ -164,7 +164,8 @@ int mi_update(register MI_INFO *info, const byte *oldrec, byte *newrec) key_changed|= HA_STATE_CHANGED; /* Must update index file */ } if (auto_key_changed) - update_auto_increment(info,newrec); + set_if_bigger(info->s->state.auto_increment, + retrieve_auto_increment(info, newrec)); if (share->calc_checksum) info->state->checksum+=(info->checksum - old_checksum); diff --git a/storage/myisam/mi_write.c b/storage/myisam/mi_write.c index 5e79b2937cc..9ab8753f6d7 100644 --- a/storage/myisam/mi_write.c +++ b/storage/myisam/mi_write.c @@ -149,7 +149,8 @@ int mi_write(MI_INFO *info, byte *record) info->state->checksum+=info->checksum; } if (share->base.auto_key) - update_auto_increment(info,record); + set_if_bigger(info->s->state.auto_increment, + retrieve_auto_increment(info, record)); info->update= (HA_STATE_CHANGED | HA_STATE_AKTIV | HA_STATE_WRITTEN | HA_STATE_ROW_CHANGED); info->state->records++; diff --git a/storage/myisam/myisamdef.h b/storage/myisam/myisamdef.h index 0b450de9c03..baab34b4b67 100644 --- a/storage/myisam/myisamdef.h +++ b/storage/myisam/myisamdef.h @@ -235,13 +235,14 @@ struct st_myisam_info { /* accumulate indexfile changes between write's */ TREE *bulk_insert; DYNAMIC_ARRAY *ft1_to_ft2; /* used only in ft1->ft2 conversion */ - MYSQL_FTPARSER_PARAM *ftparser_param; /* share info between init/deinit */ - char *filename; /* parameter to open filename */ - uchar *buff, /* Temp area for key */ - *lastkey,*lastkey2; /* Last used search key */ - uchar *first_mbr_key; /* Searhed spatial key */ - byte *rec_buff; /* Tempbuff for recordpack */ - uchar *int_keypos, /* Save position for next/previous */ + MEM_ROOT ft_memroot; /* used by the parser */ + MYSQL_FTPARSER_PARAM *ftparser_param; /* share info between init/deinit */ + char *filename; /* parameter to open filename */ + uchar *buff, /* Temp area for key */ + *lastkey,*lastkey2; /* Last used search key */ + uchar *first_mbr_key; /* Searhed spatial key */ + byte *rec_buff; /* Tempbuff for recordpack */ + uchar *int_keypos, /* Save position for next/previous */ *int_maxpos; /* -""- */ uint int_nod_flag; /* -""- */ uint32 int_keytree_version; /* -""- */ @@ -325,6 +326,7 @@ typedef struct st_mi_sort_param uchar **sort_keys; byte *rec_buff; void *wordlist, *wordptr; + MEM_ROOT wordroot; char *record; MY_TMPDIR *tmpdir; int (*key_cmp)(struct st_mi_sort_param *, const void *, const void *); @@ -591,7 +593,7 @@ extern uint _mi_pack_key(MI_INFO *info,uint keynr,uchar *key,uchar *old, extern int _mi_read_key_record(MI_INFO *info,my_off_t filepos,byte *buf); extern int _mi_read_cache(IO_CACHE *info,byte *buff,my_off_t pos, uint length,int re_read_if_possibly); -extern void update_auto_increment(MI_INFO *info,const byte *record); +extern ulonglong retrieve_auto_increment(MI_INFO *info,const byte *record); extern byte *mi_alloc_rec_buff(MI_INFO *,ulong, byte**); #define mi_get_rec_buff_ptr(info,buf) \ diff --git a/storage/myisam/myisampack.c b/storage/myisam/myisampack.c index 5b3067cb115..556d0f46145 100644 --- a/storage/myisam/myisampack.c +++ b/storage/myisam/myisampack.c @@ -3033,7 +3033,7 @@ static int mrg_rrnd(PACK_MRG_INFO *info,byte *buf) { isam_info= *(info->current=info->file); info->end=info->current+info->count; - mi_extra(isam_info, HA_EXTRA_RESET, 0); + mi_reset(isam_info); mi_extra(isam_info, HA_EXTRA_CACHE, 0); filepos=isam_info->s->pack.header_length; } @@ -3056,7 +3056,7 @@ static int mrg_rrnd(PACK_MRG_INFO *info,byte *buf) info->current++; isam_info= *info->current; filepos=isam_info->s->pack.header_length; - mi_extra(isam_info,HA_EXTRA_RESET, 0); + mi_reset(isam_info); mi_extra(isam_info,HA_EXTRA_CACHE, 0); } } diff --git a/storage/myisam/sort.c b/storage/myisam/sort.c index c9562461f56..44e7e8b464e 100644 --- a/storage/myisam/sort.c +++ b/storage/myisam/sort.c @@ -447,6 +447,7 @@ err: close_cached_file(&info->tempfile_for_exceptions); ok: + free_root(&info->wordroot, MYF(0)); remove_io_thread(&info->read_cache); pthread_mutex_lock(&info->sort_info->mutex); info->sort_info->threads_running--; diff --git a/storage/myisammrg/myrg_extra.c b/storage/myisammrg/myrg_extra.c index 62cf5f01aba..ef7eeb9d4d9 100644 --- a/storage/myisammrg/myrg_extra.c +++ b/storage/myisammrg/myrg_extra.c @@ -38,10 +38,10 @@ int myrg_extra(MYRG_INFO *info,enum ha_extra_function function, } else { - if (function == HA_EXTRA_NO_CACHE || function == HA_EXTRA_RESET || - function == HA_EXTRA_PREPARE_FOR_UPDATE) + if (function == HA_EXTRA_NO_CACHE || + function == HA_EXTRA_PREPARE_FOR_UPDATE) info->cache_in_use=0; - if (function == HA_EXTRA_RESET || function == HA_EXTRA_RESET_STATE) + if (function == HA_EXTRA_RESET_STATE) { info->current_table=0; info->last_used_table=info->open_tables; @@ -66,3 +66,23 @@ void myrg_extrafunc(MYRG_INFO *info, invalidator_by_filename inv) DBUG_VOID_RETURN; } + + +int myrg_reset(MYRG_INFO *info) +{ + int save_error= 0; + MYRG_TABLE *file; + DBUG_ENTER("myrg_reset"); + + info->cache_in_use=0; + info->current_table=0; + info->last_used_table= info->open_tables; + + for (file=info->open_tables ; file != info->end_table ; file++) + { + int error; + if ((error= mi_reset(file->table))) + save_error=error; + } + DBUG_RETURN(save_error); +} diff --git a/storage/ndb/include/kernel/signaldata/AccScan.hpp b/storage/ndb/include/kernel/signaldata/AccScan.hpp index fd1982c77af..3f6bc7f7d4d 100644 --- a/storage/ndb/include/kernel/signaldata/AccScan.hpp +++ b/storage/ndb/include/kernel/signaldata/AccScan.hpp @@ -67,6 +67,9 @@ private: static Uint32 getNRScanFlag(const Uint32 & requestInfo); static void setNRScanFlag(Uint32 & requestInfo, Uint32 nr); + + static Uint32 getLcpScanFlag(const Uint32 & requestInfo); + static void setLcpScanFlag(Uint32 & requestInfo, Uint32 nr); }; /** @@ -77,6 +80,7 @@ private: * z = Descending (TUX) - 1 Bit 6 * d = No disk scan - 1 Bit 7 * n = Node recovery scan - 1 Bit 8 + * c = LCP scan - 1 Bit 9 * * 1111111111222222222233 * 01234567890123456789012345678901 @@ -88,6 +92,7 @@ private: #define AS_DESCENDING_SHIFT (6) #define AS_NO_DISK_SCAN (7) #define AS_NR_SCAN (8) +#define AS_LCP_SCAN (9) inline Uint32 @@ -154,6 +159,19 @@ AccScanReq::setNRScanFlag(UintR & requestInfo, UintR val){ requestInfo |= (val << AS_NR_SCAN); } +inline +Uint32 +AccScanReq::getLcpScanFlag(const Uint32 & requestInfo){ + return (requestInfo >> AS_LCP_SCAN) & 1; +} + +inline +void +AccScanReq::setLcpScanFlag(UintR & requestInfo, UintR val){ + ASSERT_BOOL(val, "AccScanReq::setNoDiskScanFlag"); + requestInfo |= (val << AS_LCP_SCAN); +} + class AccScanConf { /** * Sender(s) diff --git a/storage/ndb/include/kernel/signaldata/AllocNodeId.hpp b/storage/ndb/include/kernel/signaldata/AllocNodeId.hpp index 40b30a573e1..a204bb63059 100644 --- a/storage/ndb/include/kernel/signaldata/AllocNodeId.hpp +++ b/storage/ndb/include/kernel/signaldata/AllocNodeId.hpp @@ -25,11 +25,12 @@ */ class AllocNodeIdReq { public: - STATIC_CONST( SignalLength = 3 ); + STATIC_CONST( SignalLength = 4 ); Uint32 senderRef; Uint32 senderData; Uint32 nodeId; + Uint32 nodeType; }; class AllocNodeIdConf { @@ -53,7 +54,8 @@ public: NotMaster = 702, NodeReserved = 1701, NodeConnected = 1702, - NodeFailureHandlingNotCompleted = 1703 + NodeFailureHandlingNotCompleted = 1703, + NodeTypeMismatch = 1704 }; Uint32 senderRef; diff --git a/storage/ndb/include/kernel/signaldata/DumpStateOrd.hpp b/storage/ndb/include/kernel/signaldata/DumpStateOrd.hpp index 3e3d926a999..8172a034985 100644 --- a/storage/ndb/include/kernel/signaldata/DumpStateOrd.hpp +++ b/storage/ndb/include/kernel/signaldata/DumpStateOrd.hpp @@ -141,7 +141,7 @@ public: TuxSetLogFlags = 12002, TuxMetaDataJunk = 12009, - DumpTsman = 9000, + DumpTsman = 9002, DumpLgman = 10000, DumpPgman = 11000 }; diff --git a/storage/ndb/include/kernel/signaldata/ScanFrag.hpp b/storage/ndb/include/kernel/signaldata/ScanFrag.hpp index 3c767c7f69c..b5700addb15 100644 --- a/storage/ndb/include/kernel/signaldata/ScanFrag.hpp +++ b/storage/ndb/include/kernel/signaldata/ScanFrag.hpp @@ -61,7 +61,8 @@ public: static Uint32 getAttrLen(const Uint32 & requestInfo); static Uint32 getScanPrio(const Uint32 & requestInfo); static Uint32 getNoDiskFlag(const Uint32 & requestInfo); - + static Uint32 getLcpScanFlag(const Uint32 & requestInfo); + static void setLockMode(Uint32 & requestInfo, Uint32 lockMode); static void setHoldLockFlag(Uint32 & requestInfo, Uint32 holdLock); static void setKeyinfoFlag(Uint32 & requestInfo, Uint32 keyinfo); @@ -72,6 +73,7 @@ public: static void setAttrLen(Uint32 & requestInfo, Uint32 attrLen); static void setScanPrio(Uint32& requestInfo, Uint32 prio); static void setNoDiskFlag(Uint32& requestInfo, Uint32 val); + static void setLcpScanFlag(Uint32 & requestInfo, Uint32 val); }; class KeyInfo20 { @@ -198,6 +200,7 @@ public: * Request Info * * a = Length of attrinfo - 16 Bits (16-31) + * c = LCP scan - 1 Bit 3 * d = No disk - 1 Bit 4 * l = Lock Mode - 1 Bit 5 * h = Hold lock - 1 Bit 7 @@ -205,7 +208,7 @@ public: * r = read committed - 1 Bit 9 * x = range scan - 1 Bit 6 * z = descending - 1 Bit 10 - * t = tup scan -1 Bit 11 (implies x=z=0) + * t = tup scan - 1 Bit 11 (implies x=z=0) * p = Scan prio - 4 Bits (12-15) -> max 15 * * 1111111111222222222233 @@ -222,6 +225,7 @@ public: #define SF_RANGE_SCAN_SHIFT (6) #define SF_DESCENDING_SHIFT (10) #define SF_TUP_SCAN_SHIFT (11) +#define SF_LCP_SCAN_SHIFT (3) #define SF_ATTR_LEN_SHIFT (16) #define SF_ATTR_LEN_MASK (65535) @@ -361,6 +365,19 @@ ScanFragReq::setNoDiskFlag(UintR & requestInfo, UintR val){ inline Uint32 +ScanFragReq::getLcpScanFlag(const Uint32 & requestInfo){ + return (requestInfo >> SF_LCP_SCAN_SHIFT) & 1; +} + +inline +void +ScanFragReq::setLcpScanFlag(UintR & requestInfo, UintR val){ + ASSERT_BOOL(val, "ScanFragReq::setLcpScanFlag"); + requestInfo |= (val << SF_LCP_SCAN_SHIFT); +} + +inline +Uint32 KeyInfo20::setScanInfo(Uint32 opNo, Uint32 scanNo){ ASSERT_MAX(opNo, 1023, "KeyInfo20::setScanInfo"); ASSERT_MAX(scanNo, 255, "KeyInfo20::setScanInfo"); diff --git a/storage/ndb/include/ndbapi/Ndb.hpp b/storage/ndb/include/ndbapi/Ndb.hpp index 42043fbb93a..dcd03cdc467 100644 --- a/storage/ndb/include/ndbapi/Ndb.hpp +++ b/storage/ndb/include/ndbapi/Ndb.hpp @@ -1002,6 +1002,9 @@ typedef void (* NdbEventCallback)(NdbEventOperation*, Ndb*, void*); #define WAITFOR_RESPONSE_TIMEOUT 120000 // Milliseconds #endif +#define NDB_SYSTEM_DATABASE "sys" +#define NDB_SYSTEM_SCHEMA "def" + /** * @class Ndb * @brief Represents the NDB kernel and is the main class of the NDB API. @@ -1475,6 +1478,7 @@ public: * @return 0 or -1 on error, and tupleId in out parameter */ struct TupleIdRange { + TupleIdRange() {} Uint64 m_first_tuple_id; Uint64 m_last_tuple_id; void reset() { @@ -1672,6 +1676,8 @@ private: const char * externalizeIndexName(const char * internalIndexName, bool fullyQualifiedNames); const char * externalizeIndexName(const char * internalIndexName); + const BaseString old_internalize_index_name(const NdbTableImpl * table, + const char * external_name) const; const BaseString internalize_index_name(const NdbTableImpl * table, const char * external_name) const; diff --git a/storage/ndb/include/ndbapi/NdbDictionary.hpp b/storage/ndb/include/ndbapi/NdbDictionary.hpp index 865fb506f05..27e0aede36d 100644 --- a/storage/ndb/include/ndbapi/NdbDictionary.hpp +++ b/storage/ndb/include/ndbapi/NdbDictionary.hpp @@ -1635,6 +1635,16 @@ public: int listIndexes(List & list, const char * tableName); int listIndexes(List & list, const char * tableName) const; +#ifndef DOXYGEN_SHOULD_SKIP_INTERNAL + /** + * Fetch list of indexes of given table. + * @param list Reference to list where to store the listed indexes + * @param table Reference to table that index belongs to. + * @return 0 if successful, otherwise -1 + */ + int listIndexes(List & list, const Table &table) const; +#endif + /** @} *******************************************************************/ /** * @name Events diff --git a/storage/ndb/include/ndbapi/NdbIndexScanOperation.hpp b/storage/ndb/include/ndbapi/NdbIndexScanOperation.hpp index f7ab16b1b46..15b07f5d598 100644 --- a/storage/ndb/include/ndbapi/NdbIndexScanOperation.hpp +++ b/storage/ndb/include/ndbapi/NdbIndexScanOperation.hpp @@ -62,11 +62,14 @@ public: Uint32 parallel, bool order_by, bool order_desc = false, - bool read_range_no = false) { + bool read_range_no = false, + bool keyinfo = false) { Uint32 scan_flags = (SF_OrderBy & -(Int32)order_by) | (SF_Descending & -(Int32)order_desc) | - (SF_ReadRangeNo & -(Int32)read_range_no); + (SF_ReadRangeNo & -(Int32)read_range_no) | + (SF_KeyInfo & -(Int32)keyinfo); + return readTuples(lock_mode, scan_flags, parallel); } #endif diff --git a/storage/ndb/include/ndbapi/NdbScanOperation.hpp b/storage/ndb/include/ndbapi/NdbScanOperation.hpp index 6f80a1627fe..fde232e706c 100644 --- a/storage/ndb/include/ndbapi/NdbScanOperation.hpp +++ b/storage/ndb/include/ndbapi/NdbScanOperation.hpp @@ -45,7 +45,8 @@ public: SF_TupScan = (1 << 16), // scan TUP - only LM_CommittedRead SF_OrderBy = (1 << 24), // index scan in order SF_Descending = (2 << 24), // index scan in descending order - SF_ReadRangeNo = (4 << 24) // enable @ref get_range_no + SF_ReadRangeNo = (4 << 24), // enable @ref get_range_no + SF_KeyInfo = 1 // request KeyInfo to be sent back }; /** @@ -62,15 +63,14 @@ public: #ifndef DOXYGEN_SHOULD_SKIP_DEPRECATED /** * readTuples - * * @param lock_mode Lock mode * @param batch No of rows to fetch from each fragment at a time * @param parallel No of fragments to scan in parallell - * @note specifying 0 for batch and parallall means max performance + * @note specifying 0 for batch and parallell means max performance */ #ifdef ndb_readtuples_impossible_overload int readTuples(LockMode lock_mode = LM_Read, - Uint32 batch = 0, Uint32 parallel = 0); + Uint32 batch = 0, Uint32 parallel = 0, bool keyinfo = false); #endif inline int readTuples(int parallell){ @@ -143,6 +143,20 @@ public: void close(bool forceSend = false, bool releaseOp = false); /** + * Lock current tuple + * + * @return an NdbOperation or NULL. + */ + NdbOperation* lockCurrentTuple(); + /** + * Lock current tuple + * + * @param lockTrans Transaction that should perform the lock + * + * @return an NdbOperation or NULL. + */ + NdbOperation* lockCurrentTuple(NdbTransaction* lockTrans); + /** * Update current tuple * * @return an NdbOperation or NULL. @@ -253,6 +267,19 @@ protected: inline NdbOperation* +NdbScanOperation::lockCurrentTuple(){ + return lockCurrentTuple(m_transConnection); +} + +inline +NdbOperation* +NdbScanOperation::lockCurrentTuple(NdbTransaction* takeOverTrans){ + return takeOverScanOp(NdbOperation::ReadRequest, + takeOverTrans); +} + +inline +NdbOperation* NdbScanOperation::updateCurrentTuple(){ return updateCurrentTuple(m_transConnection); } diff --git a/storage/ndb/src/kernel/blocks/ERROR_codes.txt b/storage/ndb/src/kernel/blocks/ERROR_codes.txt index 059c80a960d..d69faa90f72 100644 --- a/storage/ndb/src/kernel/blocks/ERROR_codes.txt +++ b/storage/ndb/src/kernel/blocks/ERROR_codes.txt @@ -63,6 +63,9 @@ Delay GCP_SAVEREQ by 10 secs 7165: Delay INCL_NODE_REQ in starting node yeilding error in GCP_PREPARE +7030: Delay in GCP_PREPARE until node has completed a node failure +7031: Delay in GCP_PREPARE and die 3s later + ERROR CODES FOR TESTING NODE FAILURE, LOCAL CHECKPOINT HANDLING: ----------------------------------------------------------------- diff --git a/storage/ndb/src/kernel/blocks/backup/Backup.cpp b/storage/ndb/src/kernel/blocks/backup/Backup.cpp index 620fac9eb05..07df1db862b 100644 --- a/storage/ndb/src/kernel/blocks/backup/Backup.cpp +++ b/storage/ndb/src/kernel/blocks/backup/Backup.cpp @@ -2985,22 +2985,25 @@ Backup::parseTableDescription(Signal* signal, if (disk) { /** - * Remove all disk attributes, but add DISK_REF (8 bytes) + * Remove all disk attributes */ - tabPtr.p->noOfAttributes -= (disk - 1); + tabPtr.p->noOfAttributes -= disk; - AttributePtr attrPtr; - ndbrequire(tabPtr.p->attributes.seize(attrPtr)); - - Uint32 sz32 = 2; - attrPtr.p->data.attrId = AttributeHeader::DISK_REF; - attrPtr.p->data.m_flags = Attribute::COL_FIXED; - attrPtr.p->data.sz32 = 2; - - attrPtr.p->data.offset = tabPtr.p->sz_FixedAttributes; - tabPtr.p->sz_FixedAttributes += sz32; + { + AttributePtr attrPtr; + ndbrequire(tabPtr.p->attributes.seize(attrPtr)); + + Uint32 sz32 = 2; + attrPtr.p->data.attrId = AttributeHeader::DISK_REF; + attrPtr.p->data.m_flags = Attribute::COL_FIXED; + attrPtr.p->data.sz32 = 2; + + attrPtr.p->data.offset = tabPtr.p->sz_FixedAttributes; + tabPtr.p->sz_FixedAttributes += sz32; + tabPtr.p->noOfAttributes ++; + } } - + { AttributePtr attrPtr; ndbrequire(tabPtr.p->attributes.seize(attrPtr)); @@ -3309,6 +3312,7 @@ Backup::execBACKUP_FRAGMENT_REQ(Signal* signal) ScanFragReq::setScanPrio(req->requestInfo, 1); ScanFragReq::setTupScanFlag(req->requestInfo, 1); ScanFragReq::setNoDiskFlag(req->requestInfo, 1); + ScanFragReq::setLcpScanFlag(req->requestInfo, 1); } req->transId1 = 0; req->transId2 = (BACKUP << 20) + (getOwnNodeId() << 8); diff --git a/storage/ndb/src/kernel/blocks/cmvmi/Cmvmi.cpp b/storage/ndb/src/kernel/blocks/cmvmi/Cmvmi.cpp index c3c35ac4ab2..957248bcf56 100644 --- a/storage/ndb/src/kernel/blocks/cmvmi/Cmvmi.cpp +++ b/storage/ndb/src/kernel/blocks/cmvmi/Cmvmi.cpp @@ -234,6 +234,7 @@ void Cmvmi::execEVENT_REP(Signal* signal) void Cmvmi::execEVENT_SUBSCRIBE_REQ(Signal * signal){ EventSubscribeReq * subReq = (EventSubscribeReq *)&signal->theData[0]; + Uint32 senderRef = signal->getSendersBlockRef(); SubscriberPtr ptr; jamEntry(); DBUG_ENTER("Cmvmi::execEVENT_SUBSCRIBE_REQ"); @@ -251,7 +252,7 @@ Cmvmi::execEVENT_SUBSCRIBE_REQ(Signal * signal){ * Create a new one */ if(subscribers.seize(ptr) == false){ - sendSignal(subReq->blockRef, GSN_EVENT_SUBSCRIBE_REF, signal, 1, JBB); + sendSignal(senderRef, GSN_EVENT_SUBSCRIBE_REF, signal, 1, JBB); return; } ptr.p->logLevel.clear(); @@ -278,7 +279,7 @@ Cmvmi::execEVENT_SUBSCRIBE_REQ(Signal * signal){ } signal->theData[0] = ptr.i; - sendSignal(ptr.p->blockRef, GSN_EVENT_SUBSCRIBE_CONF, signal, 1, JBB); + sendSignal(senderRef, GSN_EVENT_SUBSCRIBE_CONF, signal, 1, JBB); DBUG_VOID_RETURN; } diff --git a/storage/ndb/src/kernel/blocks/dbacc/Dbacc.hpp b/storage/ndb/src/kernel/blocks/dbacc/Dbacc.hpp index 7000d9dc8c4..f0c3dbc2866 100644 --- a/storage/ndb/src/kernel/blocks/dbacc/Dbacc.hpp +++ b/storage/ndb/src/kernel/blocks/dbacc/Dbacc.hpp @@ -17,14 +17,13 @@ #ifndef DBACC_H #define DBACC_H - +#ifdef VM_TRACE +#define ACC_SAFE_QUEUE +#endif #include <pc.hpp> #include <SimulatedBlock.hpp> -// primary key is stored in TUP -#include "../dbtup/Dbtup.hpp" - #ifdef DBACC_C // Debug Macros #define dbgWord32(ptr, ind, val) @@ -135,7 +134,10 @@ ndbout << "Ptr: " << ptr.p->word32 << " \tIndex: " << tmp_string << " \tValue: " #define ZRIGHT 2 #define ZROOTFRAGMENTSIZE 32 #define ZSCAN_LOCK_ALL 3 -#define ZSCAN_OP 5 +/** + * Check kernel_types for other operation types + */ +#define ZSCAN_OP 6 #define ZSCAN_REC_SIZE 256 #define ZSTAND_BY 2 #define ZTABLESIZE 16 @@ -358,7 +360,6 @@ struct Fragmentrec { // List of lock owners and list of lock waiters to support LCP handling //----------------------------------------------------------------------------- Uint32 lockOwnersList; - Uint32 m_current_sequence_no; //----------------------------------------------------------------------------- // References to Directory Ranges (which in turn references directories, which @@ -478,7 +479,7 @@ struct Fragmentrec { /* OPERATIONREC */ /* --------------------------------------------------------------------------------- */ struct Operationrec { - Uint32 keydata[8]; + Uint32 m_op_bits; Uint32 localdata[2]; Uint32 elementIsforward; Uint32 elementPage; @@ -487,42 +488,61 @@ struct Operationrec { Uint32 fragptr; Uint32 hashvaluePart; Uint32 hashValue; - Uint32 insertDeleteLen; - Uint32 keyinfoPage; Uint32 nextLockOwnerOp; Uint32 nextOp; Uint32 nextParallelQue; - Uint32 nextQueOp; - Uint32 nextSerialQue; + union { + Uint32 nextSerialQue; + Uint32 m_lock_owner_ptr_i; // if nextParallelQue = RNIL, else undefined + }; Uint32 prevOp; Uint32 prevLockOwnerOp; - Uint32 prevParallelQue; - Uint32 prevQueOp; - Uint32 prevSerialQue; + union { + Uint32 prevParallelQue; + Uint32 m_lo_last_parallel_op_ptr_i; + }; + union { + Uint32 prevSerialQue; + Uint32 m_lo_last_serial_op_ptr_i; + }; Uint32 scanRecPtr; Uint32 transId1; Uint32 transId2; - Uint32 longPagePtr; - Uint32 longKeyPageIndex; - Uint32 m_sequence_no; - State opState; Uint32 userptr; - State transactionstate; Uint16 elementContainer; Uint16 tupkeylen; Uint32 xfrmtupkeylen; Uint32 userblockref; Uint32 scanBits; - Uint8 elementIsDisappeared; - Uint8 insertIsDone; - Uint8 lockMode; - Uint8 lockOwner; - Uint8 nodeType; - Uint8 operation; - Uint8 opSimple; - Uint8 dirtyRead; - Uint8 commitDeleteCheckFlag; - Uint8 isAccLockReq; + + enum OpBits { + OP_MASK = 0x0000F // 4 bits for operation type + ,OP_LOCK_MODE = 0x00010 // 0 - shared lock, 1 = exclusive lock + ,OP_ACC_LOCK_MODE = 0x00020 // Or:de lock mode of all operation + // before me + ,OP_LOCK_OWNER = 0x00040 + ,OP_RUN_QUEUE = 0x00080 // In parallell queue of lock owner + ,OP_DIRTY_READ = 0x00100 + ,OP_LOCK_REQ = 0x00200 // isAccLockReq + ,OP_COMMIT_DELETE_CHECK = 0x00400 + ,OP_INSERT_IS_DONE = 0x00800 + ,OP_ELEMENT_DISAPPEARED = 0x01000 + + ,OP_STATE_MASK = 0xF0000 + ,OP_STATE_IDLE = 0xF0000 + ,OP_STATE_WAITING = 0x00000 + ,OP_STATE_RUNNING = 0x10000 + ,OP_STATE_EXECUTED = 0x30000 + + ,OP_EXECUTED_DIRTY_READ = 0x3050F + ,OP_INITIAL = ~(Uint32)0 + }; + + bool is_same_trans(const Operationrec* op) const { + return + transId1 == op->transId1 && transId2 == op->transId2; + } + }; /* p2c: size = 168 bytes */ typedef Ptr<Operationrec> OperationrecPtr; @@ -585,7 +605,6 @@ struct ScanRec { Uint32 scanUserblockref; Uint32 scanMask; Uint8 scanLockMode; - Uint8 scanKeyinfoFlag; Uint8 scanTimer; Uint8 scanContinuebCounter; Uint8 scanReadCommittedFlag; @@ -610,7 +629,8 @@ public: virtual ~Dbacc(); // pointer to TUP instance in this thread - Dbtup* c_tup; + class Dbtup* c_tup; + class Dblqh* c_lqh; void execACCMINUPDATE(Signal* signal); @@ -648,7 +668,8 @@ private: void ACCKEY_error(Uint32 fromWhere); void commitDeleteCheck(); - + void report_dealloc(Signal* signal, const Operationrec* opPtrP); + typedef void * RootfragmentrecPtr; void initRootFragPageZero(FragmentrecPtr, Page8Ptr); void initFragAdd(Signal*, FragmentrecPtr); @@ -687,14 +708,30 @@ private: bool addfragtotab(Signal* signal, Uint32 rootIndex, Uint32 fragId); void initOpRec(Signal* signal); void sendAcckeyconf(Signal* signal); - Uint32 placeReadInLockQueue(Signal* signal); - void placeSerialQueueRead(Signal* signal); - void checkOnlyReadEntry(Signal* signal); Uint32 getNoParallelTransaction(const Operationrec*); - void moveLastParallelQueue(Signal* signal); - void moveLastParallelQueueWrite(Signal* signal); - Uint32 placeWriteInLockQueue(Signal* signal); - void placeSerialQueueWrite(Signal* signal); + +#ifdef VM_TRACE + Uint32 getNoParallelTransactionFull(const Operationrec*); +#endif +#ifdef ACC_SAFE_QUEUE + bool validate_lock_queue(OperationrecPtr opPtr); + Uint32 get_parallel_head(OperationrecPtr opPtr); + void dump_lock_queue(OperationrecPtr loPtr); +#else + bool validate_lock_queue(OperationrecPtr) { return true;} +#endif + +public: + void execACCKEY_ORD(Signal* signal, Uint32 opPtrI); + void startNext(Signal* signal, OperationrecPtr lastOp); + +private: + Uint32 placeReadInLockQueue(OperationrecPtr lockOwnerPtr); + Uint32 placeWriteInLockQueue(OperationrecPtr lockOwnerPtr); + void placeSerialQueue(OperationrecPtr lockOwner, OperationrecPtr op); + void abortSerieQueueOperation(Signal* signal, OperationrecPtr op); + void abortParallelQueueOperation(Signal* signal, OperationrecPtr op); + void expandcontainer(Signal* signal); void shrinkcontainer(Signal* signal); void nextcontainerinfoExp(Signal* signal); @@ -724,8 +761,8 @@ private: void increaselistcont(Signal* signal); void seizeLeftlist(Signal* signal); void seizeRightlist(Signal* signal); - Uint32 readTablePk(Uint32 localkey1); - void getElement(Signal* signal); + Uint32 readTablePk(Uint32 localkey1, Uint32 eh, const Operationrec*); + Uint32 getElement(Signal* signal, OperationrecPtr& lockOwner); void getdirindex(Signal* signal); void commitdelete(Signal* signal); void deleteElement(Signal* signal); @@ -734,12 +771,17 @@ private: void releaseRightlist(Signal* signal); void checkoverfreelist(Signal* signal); void abortOperation(Signal* signal); - void accAbortReqLab(Signal* signal); void commitOperation(Signal* signal); - void copyOpInfo(Signal* signal); + void copyOpInfo(OperationrecPtr dst, OperationrecPtr src); Uint32 executeNextOperation(Signal* signal); void releaselock(Signal* signal); + void release_lockowner(Signal* signal, OperationrecPtr, bool commit); + void startNew(Signal* signal, OperationrecPtr newOwner); + void abortWaitingOperation(Signal*, OperationrecPtr); + void abortExecutedOperation(Signal*, OperationrecPtr); + void takeOutFragWaitQue(Signal* signal); + void check_lock_upgrade(Signal* signal, OperationrecPtr release_op, bool lo); void check_lock_upgrade(Signal* signal, OperationrecPtr lock_owner, OperationrecPtr release_op); void allocOverflowPage(Signal* signal); @@ -788,8 +830,8 @@ private: void senddatapagesLab(Signal* signal); void sttorrysignalLab(Signal* signal); void sendholdconfsignalLab(Signal* signal); - void accIsLockedLab(Signal* signal); - void insertExistElemLab(Signal* signal); + void accIsLockedLab(Signal* signal, OperationrecPtr lockOwnerPtr); + void insertExistElemLab(Signal* signal, OperationrecPtr lockOwnerPtr); void refaccConnectLab(Signal* signal); void releaseScanLab(Signal* signal); void ndbrestart1Lab(Signal* signal); @@ -848,8 +890,6 @@ private: Operationrec *operationrec; OperationrecPtr operationRecPtr; OperationrecPtr idrOperationRecPtr; - OperationrecPtr copyInOperPtr; - OperationrecPtr copyOperPtr; OperationrecPtr mlpqOperPtr; OperationrecPtr queOperPtr; OperationrecPtr readWriteOpPtr; @@ -893,8 +933,6 @@ private: Page8Ptr lcnPageptr; Page8Ptr lcnCopyPageptr; Page8Ptr lupPageptr; - Page8Ptr priPageptr; - Page8Ptr pwiPageptr; Page8Ptr ciPageidptr; Page8Ptr gsePageidptr; Page8Ptr isoPageptr; @@ -934,8 +972,6 @@ private: Tabrec *tabrec; TabrecPtr tabptr; Uint32 ctablesize; - Uint32 tpwiElementptr; - Uint32 tpriElementptr; Uint32 tgseElementptr; Uint32 tgseContainerptr; Uint32 trlHead; @@ -969,8 +1005,6 @@ private: Uint32 tdelForward; Uint32 tiopPageId; Uint32 tipPageId; - Uint32 tgeLocked; - Uint32 tgeResult; Uint32 tgeContainerptr; Uint32 tgeElementptr; Uint32 tgeForward; diff --git a/storage/ndb/src/kernel/blocks/dbacc/DbaccInit.cpp b/storage/ndb/src/kernel/blocks/dbacc/DbaccInit.cpp index 80fe82ed657..27355299a9c 100644 --- a/storage/ndb/src/kernel/blocks/dbacc/DbaccInit.cpp +++ b/storage/ndb/src/kernel/blocks/dbacc/DbaccInit.cpp @@ -130,8 +130,6 @@ Dbacc::Dbacc(Block_context& ctx): &fragrecptr, &operationRecPtr, &idrOperationRecPtr, - ©InOperPtr, - ©OperPtr, &mlpqOperPtr, &queOperPtr, &readWriteOpPtr, @@ -161,8 +159,6 @@ Dbacc::Dbacc(Block_context& ctx): &lcnPageptr, &lcnCopyPageptr, &lupPageptr, - &priPageptr, - &pwiPageptr, &ciPageidptr, &gsePageidptr, &isoPageptr, diff --git a/storage/ndb/src/kernel/blocks/dbacc/DbaccMain.cpp b/storage/ndb/src/kernel/blocks/dbacc/DbaccMain.cpp index 2d6f579302c..dc8b123f10f 100644 --- a/storage/ndb/src/kernel/blocks/dbacc/DbaccMain.cpp +++ b/storage/ndb/src/kernel/blocks/dbacc/DbaccMain.cpp @@ -41,6 +41,19 @@ #define DEBUG(x) #endif +#ifdef ACC_SAFE_QUEUE +#define vlqrequire(x) do { if (unlikely(!(x))) {\ + dump_lock_queue(loPtr); \ + ndbrequire(false); } } while(0) +#else +#define vlqrequire(x) ndbrequire(x) +#endif + + +// primary key is stored in TUP +#include "../dbtup/Dbtup.hpp" +#include "../dblqh/Dblqh.hpp" + // Signal entries and statement blocks /* --------------------------------------------------------------------------------- */ @@ -208,8 +221,8 @@ void Dbacc::execSTTOR(Signal* signal) switch (tstartphase) { case 1: jam(); - c_tup = (Dbtup*)globalData.getBlock(DBTUP); - ndbrequire(c_tup != 0); + ndbrequire((c_tup = (Dbtup*)globalData.getBlock(DBTUP)) != 0); + ndbrequire((c_lqh = (Dblqh*)globalData.getBlock(DBLQH)) != 0); break; } tuserblockref = signal->theData[3]; @@ -435,9 +448,7 @@ void Dbacc::initialiseOperationRec(Signal* signal) for (operationRecPtr.i = 0; operationRecPtr.i < coprecsize; operationRecPtr.i++) { refresh_watch_dog(); ptrAss(operationRecPtr, operationrec); - operationRecPtr.p->transactionstate = IDLE; - operationRecPtr.p->operation = ZUNDEFINED_OP; - operationRecPtr.p->opState = FREE_OP; + operationRecPtr.p->m_op_bits = Operationrec::OP_INITIAL; operationRecPtr.p->nextOp = operationRecPtr.i + 1; }//for operationRecPtr.i = coprecsize - 1; @@ -899,8 +910,7 @@ void Dbacc::execACCSEIZEREQ(Signal* signal) ptrGuard(operationRecPtr); operationRecPtr.p->userptr = tuserptr; operationRecPtr.p->userblockref = tuserblockref; - operationRecPtr.p->operation = ZUNDEFINED_OP; - operationRecPtr.p->transactionstate = IDLE; + operationRecPtr.p->m_op_bits = Operationrec::OP_INITIAL; /* ******************************< */ /* ACCSEIZECONF */ /* ******************************< */ @@ -954,51 +964,32 @@ void Dbacc::initOpRec(Signal* signal) operationRecPtr.p->xfrmtupkeylen = signal->theData[4]; operationRecPtr.p->transId1 = signal->theData[5]; operationRecPtr.p->transId2 = signal->theData[6]; - operationRecPtr.p->transactionstate = ACTIVE; - operationRecPtr.p->commitDeleteCheckFlag = ZFALSE; - operationRecPtr.p->operation = Treqinfo & 0x7; - /* --------------------------------------------------------------------------------- */ - // opSimple is not used in this version. Is needed for deadlock handling later on. - /* --------------------------------------------------------------------------------- */ - // operationRecPtr.p->opSimple = (Treqinfo >> 3) & 0x1; - - operationRecPtr.p->lockMode = (Treqinfo >> 4) & 0x3; Uint32 readFlag = (((Treqinfo >> 4) & 0x3) == 0); // Only 1 if Read Uint32 dirtyFlag = (((Treqinfo >> 6) & 0x1) == 1); // Only 1 if Dirty Uint32 dirtyReadFlag = readFlag & dirtyFlag; - operationRecPtr.p->dirtyRead = dirtyReadFlag; - operationRecPtr.p->nodeType = (Treqinfo >> 7) & 0x3; + Uint32 opbits = 0; + opbits |= Treqinfo & 0x7; + opbits |= ((Treqinfo >> 4) & 0x3) ? Operationrec::OP_LOCK_MODE : 0; + opbits |= ((Treqinfo >> 4) & 0x3) ? Operationrec::OP_ACC_LOCK_MODE : 0; + opbits |= (dirtyReadFlag) ? Operationrec::OP_DIRTY_READ : 0; + opbits |= ((Treqinfo >> 31) & 0x1) ? Operationrec::OP_LOCK_REQ : 0; + + //operationRecPtr.p->nodeType = (Treqinfo >> 7) & 0x3; operationRecPtr.p->fid = fragrecptr.p->myfid; operationRecPtr.p->fragptr = fragrecptr.i; operationRecPtr.p->nextParallelQue = RNIL; operationRecPtr.p->prevParallelQue = RNIL; - operationRecPtr.p->prevQueOp = RNIL; - operationRecPtr.p->nextQueOp = RNIL; operationRecPtr.p->nextSerialQue = RNIL; operationRecPtr.p->prevSerialQue = RNIL; operationRecPtr.p->elementPage = RNIL; - operationRecPtr.p->keyinfoPage = RNIL; - operationRecPtr.p->lockOwner = ZFALSE; - operationRecPtr.p->insertIsDone = ZFALSE; - operationRecPtr.p->elementIsDisappeared = ZFALSE; - operationRecPtr.p->insertDeleteLen = fragrecptr.p->elementLength; - operationRecPtr.p->longPagePtr = RNIL; - operationRecPtr.p->longKeyPageIndex = RNIL; operationRecPtr.p->scanRecPtr = RNIL; + operationRecPtr.p->m_op_bits = opbits; // bit to mark lock operation - operationRecPtr.p->isAccLockReq = (Treqinfo >> 31) & 0x1; // undo log is not run via ACCKEYREQ - if(ERROR_INSERTED(5900) || ERROR_INSERTED(5901)) - { - for(unsigned i = 0; i<8 && i<signal->theData[4]; i++){ - operationRecPtr.p->keydata[i] = signal->theData[i+7]; - } - } - }//Dbacc::initOpRec() /* --------------------------------------------------------------------------------- */ @@ -1007,7 +998,7 @@ void Dbacc::initOpRec(Signal* signal) void Dbacc::sendAcckeyconf(Signal* signal) { signal->theData[0] = operationRecPtr.p->userptr; - signal->theData[1] = operationRecPtr.p->operation; + signal->theData[1] = operationRecPtr.p->m_op_bits & Operationrec::OP_MASK; signal->theData[2] = operationRecPtr.p->fid; signal->theData[3] = operationRecPtr.p->localdata[0]; signal->theData[4] = operationRecPtr.p->localdata[1]; @@ -1015,7 +1006,8 @@ void Dbacc::sendAcckeyconf(Signal* signal) }//Dbacc::sendAcckeyconf() -void Dbacc::ACCKEY_error(Uint32 fromWhere) +void +Dbacc::ACCKEY_error(Uint32 fromWhere) { switch(fromWhere) { case 0: @@ -1069,7 +1061,8 @@ void Dbacc::execACCKEYREQ(Signal* signal) }//if ptrAss(operationRecPtr, operationrec); ptrAss(fragrecptr, fragmentrec); - ndbrequire(operationRecPtr.p->transactionstate == IDLE); + + ndbrequire(operationRecPtr.p->m_op_bits == Operationrec::OP_INITIAL); initOpRec(signal); // normalize key if any char attr @@ -1083,23 +1076,32 @@ void Dbacc::execACCKEYREQ(Signal* signal) /* WE REMEMBER THESE ADDRESS IF WE LATER NEED TO INSERT */ /* THE ITEM AFTER NOT FINDING THE ITEM. */ /*---------------------------------------------------------------*/ - getElement(signal); - - if (tgeResult == ZTRUE) { - switch (operationRecPtr.p->operation) { + OperationrecPtr lockOwnerPtr; + const Uint32 found = getElement(signal, lockOwnerPtr); + + Uint32 opbits = operationRecPtr.p->m_op_bits; + Uint32 op = opbits & Operationrec::OP_MASK; + if (found == ZTRUE) + { + switch (op) { case ZREAD: case ZUPDATE: case ZDELETE: case ZWRITE: case ZSCAN_OP: - if (!tgeLocked){ - if(operationRecPtr.p->operation == ZWRITE) + if (!lockOwnerPtr.p) + { + if(op == ZWRITE) { jam(); - operationRecPtr.p->operation = ZUPDATE; + opbits &= ~(Uint32)Operationrec::OP_MASK; + opbits |= (op = ZUPDATE); + operationRecPtr.p->m_op_bits = opbits; // store to get correct ACCKEYCONF } + opbits |= Operationrec::OP_STATE_RUNNING; + opbits |= Operationrec::OP_RUN_QUEUE; sendAcckeyconf(signal); - if (operationRecPtr.p->dirtyRead == ZFALSE) { + if (! (opbits & Operationrec::OP_DIRTY_READ)) { /*---------------------------------------------------------------*/ // It is not a dirty read. We proceed by locking and continue with // the operation. @@ -1116,42 +1118,44 @@ void Dbacc::execACCKEYREQ(Signal* signal) dbgWord32(gePageptr, tgeElementptr, eh); gePageptr.p->word32[tgeElementptr] = eh; - insertLockOwnersList(signal , operationRecPtr); - return; + opbits |= Operationrec::OP_LOCK_OWNER; + insertLockOwnersList(signal, operationRecPtr); } else { jam(); /*---------------------------------------------------------------*/ // It is a dirty read. We do not lock anything. Set state to // IDLE since no COMMIT call will come. /*---------------------------------------------------------------*/ - operationRecPtr.p->transactionstate = IDLE; - operationRecPtr.p->operation = ZUNDEFINED_OP; - return; + opbits = Operationrec::OP_EXECUTED_DIRTY_READ; }//if + operationRecPtr.p->m_op_bits = opbits; + return; } else { jam(); - accIsLockedLab(signal); + accIsLockedLab(signal, lockOwnerPtr); return; }//if break; case ZINSERT: jam(); - insertExistElemLab(signal); + insertExistElemLab(signal, lockOwnerPtr); return; break; default: ndbrequire(false); break; }//switch - } else if (tgeResult == ZFALSE) { - switch (operationRecPtr.p->operation) { - case ZINSERT: + } else if (found == ZFALSE) { + switch (op){ case ZWRITE: + opbits &= ~(Uint32)Operationrec::OP_MASK; + opbits |= (op = ZINSERT); + case ZINSERT: jam(); - // If a write operation makes an insert we switch operation to ZINSERT so - // that the commit-method knows an insert has been made and updates noOfElements. - operationRecPtr.p->operation = ZINSERT; - operationRecPtr.p->insertIsDone = ZTRUE; + opbits |= Operationrec::OP_INSERT_IS_DONE; + opbits |= Operationrec::OP_STATE_RUNNING; + opbits |= Operationrec::OP_RUN_QUEUE; + operationRecPtr.p->m_op_bits = opbits; insertelementLab(signal); return; break; @@ -1169,13 +1173,287 @@ void Dbacc::execACCKEYREQ(Signal* signal) }//switch } else { jam(); - acckeyref1Lab(signal, tgeResult); + acckeyref1Lab(signal, found); return; }//if return; }//Dbacc::execACCKEYREQ() void +Dbacc::execACCKEY_ORD(Signal* signal, Uint32 opPtrI) +{ + jamEntry(); + OperationrecPtr lastOp; + lastOp.i = opPtrI; + ptrCheckGuard(lastOp, coprecsize, operationrec); + Uint32 opbits = lastOp.p->m_op_bits; + Uint32 opstate = opbits & Operationrec::OP_STATE_MASK; + + if (likely(opbits == Operationrec::OP_EXECUTED_DIRTY_READ)) + { + jam(); + lastOp.p->m_op_bits = Operationrec::OP_INITIAL; + return; + } + else if (likely(opstate == Operationrec::OP_STATE_RUNNING)) + { + opbits |= Operationrec::OP_STATE_EXECUTED; + lastOp.p->m_op_bits = opbits; + startNext(signal, lastOp); + return; + } + else + { + } + + ndbout_c("bits: %.8x state: %.8x", opbits, opstate); + ndbrequire(false); +} + +void +Dbacc::startNext(Signal* signal, OperationrecPtr lastOp) +{ + jam(); + OperationrecPtr nextOp; + OperationrecPtr loPtr; + nextOp.i = lastOp.p->nextParallelQue; + loPtr.i = lastOp.p->m_lock_owner_ptr_i; + Uint32 opbits = lastOp.p->m_op_bits; + + if ((opbits & Operationrec::OP_STATE_MASK)!= Operationrec::OP_STATE_EXECUTED) + { + jam(); + return; + } + + Uint32 nextbits; + if (nextOp.i != RNIL) + { + jam(); + ptrCheckGuard(nextOp, coprecsize, operationrec); + nextbits = nextOp.p->m_op_bits; + goto checkop; + } + + if ((opbits & Operationrec::OP_LOCK_OWNER) == 0) + { + jam(); + ptrCheckGuard(loPtr, coprecsize, operationrec); + } + else + { + jam(); + loPtr = lastOp; + } + + nextOp.i = loPtr.p->nextSerialQue; + ndbassert(loPtr.p->m_op_bits & Operationrec::OP_LOCK_OWNER); + + if (nextOp.i == RNIL) + { + jam(); + return; + } + + /** + * There is an op in serie queue... + * Check if it can run + */ + ptrCheckGuard(nextOp, coprecsize, operationrec); + nextbits = nextOp.p->m_op_bits; + + { + const bool same = nextOp.p->is_same_trans(lastOp.p); + + if (!same && ((opbits & Operationrec::OP_ACC_LOCK_MODE) || + (nextbits & Operationrec::OP_LOCK_MODE))) + { + jam(); + /** + * Not same transaction + * and either last had exclusive lock + * or next had exclusive lock + */ + return; + } + + /** + * same trans and X-lock + */ + if (same && (opbits & Operationrec::OP_ACC_LOCK_MODE)) + { + jam(); + goto upgrade; + } + } + + /** + * all shared lock... + */ + if ((opbits & Operationrec::OP_ACC_LOCK_MODE) == 0 && + (nextbits & Operationrec::OP_LOCK_MODE) == 0) + { + jam(); + goto upgrade; + } + + /** + * There is a shared parallell queue & and exclusive op is first in queue + */ + ndbassert((opbits & Operationrec::OP_ACC_LOCK_MODE) == 0 && + (nextbits & Operationrec::OP_LOCK_MODE)); + + /** + * We must check if there are many transactions in parallel queue... + */ + OperationrecPtr tmp; + tmp.i = loPtr.p->nextParallelQue; + while (tmp.i != RNIL) + { + ptrCheckGuard(tmp, coprecsize, operationrec); + if (!nextOp.p->is_same_trans(tmp.p)) + { + jam(); + /** + * parallel queue contained another transaction, dont let it run + */ + return; + } + } + +upgrade: + /** + * Move first op in serie queue to end of parallell queue + */ + + tmp.i = loPtr.p->nextSerialQue = nextOp.p->nextSerialQue; + loPtr.p->m_lo_last_parallel_op_ptr_i = nextOp.i; + nextOp.p->nextSerialQue = RNIL; + nextOp.p->prevSerialQue = RNIL; + nextOp.p->m_lock_owner_ptr_i = loPtr.i; + nextOp.p->prevParallelQue = lastOp.i; + lastOp.p->nextParallelQue = nextOp.i; + + if (tmp.i != RNIL) + { + jam(); + ptrCheckGuard(tmp, coprecsize, operationrec); + tmp.p->prevSerialQue = loPtr.i; + } + else + { + jam(); + loPtr.p->m_lo_last_serial_op_ptr_i = RNIL; + } + + nextbits |= Operationrec::OP_RUN_QUEUE; + + /** + * Currently no grouping of ops in serie queue + */ + ndbrequire(nextOp.p->nextParallelQue == RNIL); + +checkop: + Uint32 errCode = 0; + OperationrecPtr save = operationRecPtr; + operationRecPtr = nextOp; + + Uint32 lastop = opbits & Operationrec::OP_MASK; + Uint32 nextop = nextbits & Operationrec::OP_MASK; + + nextbits &= nextbits & ~(Uint32)Operationrec::OP_STATE_MASK; + nextbits |= Operationrec::OP_STATE_RUNNING; + + if (lastop == ZDELETE) + { + jam(); + if (nextop != ZINSERT && nextop != ZWRITE) + { + errCode = ZREAD_ERROR; + goto ref; + } + + nextbits &= ~(Uint32)Operationrec::OP_MASK; + nextbits &= ~(Uint32)Operationrec::OP_ELEMENT_DISAPPEARED; + nextbits |= (nextop = ZINSERT); + nextbits |= Operationrec::OP_INSERT_IS_DONE; + goto conf; + } + else if (nextop == ZINSERT) + { + jam(); + errCode = ZWRITE_ERROR; + goto ref; + } + else if (nextop == ZWRITE) + { + jam(); + nextbits &= ~(Uint32)Operationrec::OP_MASK; + nextbits |= (nextop = ZUPDATE); + goto conf; + } + else + { + jam(); + } + +conf: + nextOp.p->m_op_bits = nextbits; + nextOp.p->localdata[0] = lastOp.p->localdata[0]; + nextOp.p->localdata[1] = lastOp.p->localdata[1]; + + if (nextop == ZSCAN_OP && (nextbits & Operationrec::OP_LOCK_REQ) == 0) + { + jam(); + takeOutScanLockQueue(nextOp.p->scanRecPtr); + putReadyScanQueue(signal, nextOp.p->scanRecPtr); + } + else + { + jam(); + fragrecptr.i = nextOp.p->fragptr; + ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec); + + sendAcckeyconf(signal); + sendSignal(nextOp.p->userblockref, GSN_ACCKEYCONF, + signal, 6, JBB); + } + + operationRecPtr = save; + return; + +ref: + nextOp.p->m_op_bits = nextbits; + + if (nextop == ZSCAN_OP && (nextbits & Operationrec::OP_LOCK_REQ) == 0) + { + jam(); + nextOp.p->m_op_bits |= Operationrec::OP_ELEMENT_DISAPPEARED; + takeOutScanLockQueue(nextOp.p->scanRecPtr); + putReadyScanQueue(signal, nextOp.p->scanRecPtr); + } + else + { + jam(); + signal->theData[0] = nextOp.p->userptr; + signal->theData[1] = errCode; + sendSignal(nextOp.p->userblockref, GSN_ACCKEYREF, signal, + 2, JBB); + } + + operationRecPtr = save; + return; +} + + +#if 0 +void +Dbacc::execACCKEY_REP_REF(Signal* signal, Uint32 opPtrI) +{ +} +#endif + +void Dbacc::xfrmKeyData(Signal* signal) { Uint32 table = fragrecptr.p->myTableId; @@ -1188,23 +1466,22 @@ Dbacc::xfrmKeyData(Signal* signal) operationRecPtr.p->xfrmtupkeylen = len; } -void Dbacc::accIsLockedLab(Signal* signal) +void +Dbacc::accIsLockedLab(Signal* signal, OperationrecPtr lockOwnerPtr) { ndbrequire(csystemRestart == ZFALSE); - queOperPtr.i = ElementHeader::getOpPtrI(gePageptr.p->word32[tgeElementptr]); - ptrCheckGuard(queOperPtr, coprecsize, operationrec); - if (operationRecPtr.p->dirtyRead == ZFALSE) { + + Uint32 bits = operationRecPtr.p->m_op_bits; + validate_lock_queue(lockOwnerPtr); + + if ((bits & Operationrec::OP_DIRTY_READ) == 0){ Uint32 return_result; - if (operationRecPtr.p->lockMode == ZREADLOCK) { + if ((bits & Operationrec::OP_LOCK_MODE) == ZREADLOCK) { jam(); - priPageptr = gePageptr; - tpriElementptr = tgeElementptr; - return_result = placeReadInLockQueue(signal); + return_result = placeReadInLockQueue(lockOwnerPtr); } else { jam(); - pwiPageptr = gePageptr; - tpwiElementptr = tgeElementptr; - return_result = placeWriteInLockQueue(signal); + return_result = placeWriteInLockQueue(lockOwnerPtr); }//if if (return_result == ZPARALLEL_QUEUE) { jam(); @@ -1214,24 +1491,29 @@ void Dbacc::accIsLockedLab(Signal* signal) jam(); signal->theData[0] = RNIL; return; - } else if (return_result == ZWRITE_ERROR) { + } else { jam(); acckeyref1Lab(signal, return_result); return; }//if ndbrequire(false); - } else { - if (queOperPtr.p->elementIsDisappeared == ZFALSE) { + } + else + { + if (!(lockOwnerPtr.p->m_op_bits & Operationrec::OP_ELEMENT_DISAPPEARED) && + lockOwnerPtr.p->localdata[0] != ~(Uint32)0) + { jam(); - /*---------------------------------------------------------------*/ - // It is a dirty read. We do not lock anything. Set state to - // IDLE since no COMMIT call will arrive. - /*---------------------------------------------------------------*/ + /* --------------------------------------------------------------- + * It is a dirty read. We do not lock anything. Set state to + *IDLE since no COMMIT call will arrive. + * ---------------------------------------------------------------*/ sendAcckeyconf(signal); - operationRecPtr.p->transactionstate = IDLE; - operationRecPtr.p->operation = ZUNDEFINED_OP; + operationRecPtr.p->m_op_bits = Operationrec::OP_EXECUTED_DIRTY_READ; return; - } else { + } + else + { jam(); /*---------------------------------------------------------------*/ // The tuple does not exist in the committed world currently. @@ -1243,17 +1525,18 @@ void Dbacc::accIsLockedLab(Signal* signal) }//if }//Dbacc::accIsLockedLab() -/* --------------------------------------------------------------------------------- */ -/* I N S E R T E X I S T E L E M E N T */ -/* --------------------------------------------------------------------------------- */ -void Dbacc::insertExistElemLab(Signal* signal) +/* ------------------------------------------------------------------------ */ +/* I N S E R T E X I S T E L E M E N T */ +/* ------------------------------------------------------------------------ */ +void Dbacc::insertExistElemLab(Signal* signal, OperationrecPtr lockOwnerPtr) { - if (!tgeLocked){ + if (!lockOwnerPtr.p) + { jam(); acckeyref1Lab(signal, ZWRITE_ERROR);/* THE ELEMENT ALREADY EXIST */ return; }//if - accIsLockedLab(signal); + accIsLockedLab(signal, lockOwnerPtr); }//Dbacc::insertExistElemLab() /* --------------------------------------------------------------------------------- */ @@ -1271,26 +1554,8 @@ void Dbacc::insertelementLab(Signal* signal) }//if }//if ndbrequire(operationRecPtr.p->tupkeylen <= fragrecptr.p->keyLength); - - Uint32 localKey; - if(!operationRecPtr.p->isAccLockReq) - { - signal->theData[0] = operationRecPtr.p->userptr; - Uint32 blockNo = refToBlock(operationRecPtr.p->userblockref); - EXECUTE_DIRECT(blockNo, GSN_LQH_ALLOCREQ, signal, 1); - jamEntry(); - if (signal->theData[0] != 0) { - jam(); - Uint32 result_code = signal->theData[0]; - acckeyref1Lab(signal, result_code); - return; - }//if - localKey = (signal->theData[1] << MAX_TUPLES_BITS) + signal->theData[2]; - } - else - { - localKey = signal->theData[7]; - } + ndbassert(!(operationRecPtr.p->m_op_bits & Operationrec::OP_LOCK_REQ)); + Uint32 localKey = ~(Uint32)0; insertLockOwnersList(signal, operationRecPtr); @@ -1305,196 +1570,18 @@ void Dbacc::insertelementLab(Signal* signal) idrOperationRecPtr = operationRecPtr; clocalkey[0] = localKey; operationRecPtr.p->localdata[0] = localKey; - /* --------------------------------------------------------------------------------- */ - /* WE SET THE LOCAL KEY TO MINUS ONE TO INDICATE IT IS NOT YET VALID. */ - /* --------------------------------------------------------------------------------- */ + /* ----------------------------------------------------------------------- */ + /* WE SET THE LOCAL KEY TO MINUS ONE TO INDICATE IT IS NOT YET VALID. */ + /* ----------------------------------------------------------------------- */ insertElement(signal); sendAcckeyconf(signal); return; }//Dbacc::insertelementLab() -/* --------------------------------------------------------------------------------- */ -/* PLACE_READ_IN_LOCK_QUEUE */ -/* INPUT: OPERATION_REC_PTR OUR OPERATION POINTER */ -/* QUE_OPER_PTR LOCK QUEUE OWNER OPERATION POINTER */ -/* PRI_PAGEPTR PAGE POINTER OF ELEMENT */ -/* TPRI_ELEMENTPTR ELEMENT POINTER OF ELEMENT */ -/* OUTPUT TRESULT = */ -/* ZPARALLEL_QUEUE OPERATION PLACED IN PARALLEL QUEUE */ -/* OPERATION CAN PROCEED NOW. */ -/* ZSERIAL_QUEUE OPERATION PLACED IN SERIAL QUEUE */ -/* ERROR CODE OPERATION NEEDS ABORTING */ -/* THE ELEMENT WAS LOCKED AND WE WANT TO READ THE TUPLE. WE WILL CHECK THE LOCK */ -/* QUEUES TO PERFORM THE PROPER ACTION. */ -/* */ -/* IN SOME PLACES IN THE CODE BELOW THAT HANDLES WHAT TO DO WHEN THE TUPLE IS LOCKED */ -/* WE DO ASSUME THAT NEXT_PARALLEL_QUEUE AND NEXT_SERIAL_QUEUE ON OPERATION_REC_PTR */ -/* HAVE BEEN INITIALISED TO RNIL. THUS WE DO NOT PERFORM THIS ONCE MORE EVEN IF IT */ -/* COULD BE NICE FOR READABILITY. */ -/* --------------------------------------------------------------------------------- */ -Uint32 Dbacc::placeReadInLockQueue(Signal* signal) -{ - if (getNoParallelTransaction(queOperPtr.p) == 1) { - if ((queOperPtr.p->transId1 == operationRecPtr.p->transId1) && - (queOperPtr.p->transId2 == operationRecPtr.p->transId2)) { - /* --------------------------------------------------------------------------------- */ - /* WE ARE PERFORMING A READ OPERATION AND THIS TRANSACTION ALREADY OWNS THE LOCK */ - /* ALONE. PUT THE OPERATION LAST IN THE PARALLEL QUEUE. */ - /* --------------------------------------------------------------------------------- */ - jam(); - mlpqOperPtr = queOperPtr; - moveLastParallelQueue(signal); - operationRecPtr.p->localdata[0] = queOperPtr.p->localdata[0]; - operationRecPtr.p->localdata[1] = queOperPtr.p->localdata[1]; - operationRecPtr.p->prevParallelQue = mlpqOperPtr.i; - mlpqOperPtr.p->nextParallelQue = operationRecPtr.i; - switch (queOperPtr.p->lockMode) { - case ZREADLOCK: - jam(); - /*empty*/; - break; - default: - jam(); - /* --------------------------------------------------------------------------------- */ - /* IF THE TRANSACTION PREVIOUSLY SET A WRITE LOCK WE MUST ENSURE THAT ALL */ - /* OPERATIONS IN THE PARALLEL QUEUE HAVE WRITE LOCK MODE TO AVOID STRANGE BUGS.*/ - /* --------------------------------------------------------------------------------- */ - operationRecPtr.p->lockMode = queOperPtr.p->lockMode; - break; - }//switch - return ZPARALLEL_QUEUE; - }//if - }//if - if (queOperPtr.p->nextSerialQue == RNIL) { - /* --------------------------------------------------------------------------------- */ - /* WE ARE PERFORMING A READ OPERATION AND THERE IS NO SERIAL QUEUE. IF THERE IS NO */ - /* WRITE OPERATION THAT OWNS THE LOCK OR ANY WRITE OPERATION IN THE PARALLEL QUEUE */ - /* IT IS ENOUGH TO CHECK THE LOCK MODE OF THE LEADER IN THE PARALLEL QUEUE. IF IT IS */ - /* A READ LOCK THEN WE PLACE OURSELVES IN THE PARALLEL QUEUE OTHERWISE WE GO ON TO */ - /* PLACE OURSELVES IN THE SERIAL QUEUE. */ - /* --------------------------------------------------------------------------------- */ - switch (queOperPtr.p->lockMode) { - case ZREADLOCK: - jam(); - mlpqOperPtr = queOperPtr; - moveLastParallelQueue(signal); - operationRecPtr.p->prevParallelQue = mlpqOperPtr.i; - mlpqOperPtr.p->nextParallelQue = operationRecPtr.i; - operationRecPtr.p->localdata[0] = queOperPtr.p->localdata[0]; - operationRecPtr.p->localdata[1] = queOperPtr.p->localdata[1]; - return ZPARALLEL_QUEUE; - default: - jam(); - queOperPtr.p->nextSerialQue = operationRecPtr.i; - operationRecPtr.p->prevSerialQue = queOperPtr.i; - break; - }//switch - } else { - jam(); - placeSerialQueueRead(signal); - }//if - return ZSERIAL_QUEUE; -}//Dbacc::placeReadInLockQueue() - -/* --------------------------------------------------------------------------------- */ -/* WE WILL CHECK IF THIS TRANSACTION IS ALREADY PLACED AT SOME SPOT IN THE PARALLEL */ -/* SERIAL QUEUE WITHOUT ANY NEIGHBORS FROM OTHER TRANSACTION. IF SO WE WILL INSERT */ -/* IT IN THAT PARALLEL QUEUE. */ -/* --------------------------------------------------------------------------------- */ -void Dbacc::placeSerialQueueRead(Signal* signal) -{ - readWriteOpPtr.i = queOperPtr.p->nextSerialQue; - ptrCheckGuard(readWriteOpPtr, coprecsize, operationrec); - PSQR_LOOP: - jam(); - if (readWriteOpPtr.p->nextSerialQue == RNIL) { - jam(); - /* --------------------------------------------------------------------------------- */ - /* THERE WAS NO PREVIOUS OPERATION IN THIS TRANSACTION WHICH WE COULD PUT IT */ - /* IN THE PARALLEL QUEUE TOGETHER WITH. */ - /* --------------------------------------------------------------------------------- */ - checkOnlyReadEntry(signal); - return; - }//if - if (getNoParallelTransaction(readWriteOpPtr.p) == 1) { - jam(); - /* --------------------------------------------------------------------------------- */ - /* THERE WAS ONLY ONE TRANSACTION INVOLVED IN THE PARALLEL QUEUE. IF THIS IS OUR */ - /* TRANSACTION WE CAN STILL GET HOLD OF THE LOCK. */ - /* --------------------------------------------------------------------------------- */ - if ((readWriteOpPtr.p->transId1 == operationRecPtr.p->transId1) && - (readWriteOpPtr.p->transId2 == operationRecPtr.p->transId2)) { - jam(); - /* --------------------------------------------------------------------------------- */ - /* WE ARE PERFORMING A READ IN THE SAME TRANSACTION WHERE WE ALREADY */ - /* PREVIOUSLY HAVE EXECUTED AN OPERATION. INSERT-DELETE, READ-UPDATE, READ-READ, */ - /* UPDATE-UPDATE, UPDATE-DELETE, READ-DELETE, INSERT-READ, INSERT-UPDATE ARE ALLOWED */ - /* COMBINATIONS. A NEW INSERT AFTER A DELETE IS NOT ALLOWED AND SUCH AN INSERT WILL */ - /* GO TO THE SERIAL LOCK QUEUE WHICH IT WILL NOT LEAVE UNTIL A TIME-OUT AND THE */ - /* TRANSACTION IS ABORTED. READS AND UPDATES AFTER DELETES IS ALSO NOT ALLOWED. */ - /* --------------------------------------------------------------------------------- */ - mlpqOperPtr = readWriteOpPtr; - moveLastParallelQueue(signal); - readWriteOpPtr = mlpqOperPtr; - operationRecPtr.p->prevParallelQue = readWriteOpPtr.i; - readWriteOpPtr.p->nextParallelQue = operationRecPtr.i; - operationRecPtr.p->localdata[0] = readWriteOpPtr.p->localdata[0]; - operationRecPtr.p->localdata[1] = readWriteOpPtr.p->localdata[1]; - switch (readWriteOpPtr.p->lockMode) { - case ZREADLOCK: - jam(); - /*empty*/; - break; - default: - jam(); - /* --------------------------------------------------------------------------------- */ - /* IF THE TRANSACTION PREVIOUSLY SET A WRITE LOCK WE MUST ENSURE THAT ALL */ - /* OPERATIONS IN THE PARALLEL QUEUE HAVE WRITE LOCK MODE TO AVOID STRANGE BUGS.*/ - /* --------------------------------------------------------------------------------- */ - operationRecPtr.p->lockMode = readWriteOpPtr.p->lockMode; - break; - }//switch - return; - }//if - }//if - readWriteOpPtr.i = readWriteOpPtr.p->nextSerialQue; - ptrCheckGuard(readWriteOpPtr, coprecsize, operationrec); - goto PSQR_LOOP; -}//Dbacc::placeSerialQueueRead() - -/* --------------------------------------------------------------------------------- */ -/* WE WILL CHECK IF THE LAST ENTRY IN THE SERIAL QUEUE CONTAINS ONLY READ */ -/* OPERATIONS. IF SO WE WILL INSERT IT IN THAT PARALLEL QUEUE. OTHERWISE WE */ -/* WILL PLACE IT AT THE END OF THE SERIAL QUEUE. */ -/* --------------------------------------------------------------------------------- */ -void Dbacc::checkOnlyReadEntry(Signal* signal) -{ - switch (readWriteOpPtr.p->lockMode) { - case ZREADLOCK: - jam(); - /* --------------------------------------------------------------------------------- */ - /* SINCE THIS LAST QUEUE ONLY CONTAINS READ LOCKS WE CAN JOIN THE PARALLEL QUEUE AT */ - /* THE END. */ - /* --------------------------------------------------------------------------------- */ - mlpqOperPtr = readWriteOpPtr; - moveLastParallelQueue(signal); - readWriteOpPtr = mlpqOperPtr; - operationRecPtr.p->prevParallelQue = readWriteOpPtr.i; - readWriteOpPtr.p->nextParallelQue = operationRecPtr.i; - operationRecPtr.p->localdata[0] = readWriteOpPtr.p->localdata[0]; - operationRecPtr.p->localdata[1] = readWriteOpPtr.p->localdata[1]; - break; - default: - jam(); /* PUT THE OPERATION RECORD IN THE SERIAL QUEUE */ - readWriteOpPtr.p->nextSerialQue = operationRecPtr.i; - operationRecPtr.p->prevSerialQue = readWriteOpPtr.i; - break; - }//switch -}//Dbacc::checkOnlyReadEntry() -/* --------------------------------------------------------------------------------- */ -/* GET_NO_PARALLEL_TRANSACTION */ -/* --------------------------------------------------------------------------------- */ +/* ------------------------------------------------------------------------ */ +/* GET_NO_PARALLEL_TRANSACTION */ +/* ------------------------------------------------------------------------ */ Uint32 Dbacc::getNoParallelTransaction(const Operationrec * op) { @@ -1514,142 +1601,636 @@ Dbacc::getNoParallelTransaction(const Operationrec * op) return 1; }//Dbacc::getNoParallelTransaction() -void Dbacc::moveLastParallelQueue(Signal* signal) +#ifdef VM_TRACE +Uint32 +Dbacc::getNoParallelTransactionFull(const Operationrec * op) { - while (mlpqOperPtr.p->nextParallelQue != RNIL) { - jam(); - mlpqOperPtr.i = mlpqOperPtr.p->nextParallelQue; - ptrCheckGuard(mlpqOperPtr, coprecsize, operationrec); - }//if -}//Dbacc::moveLastParallelQueue() + ConstPtr<Operationrec> tmp; + + tmp.p = op; + while ((tmp.p->m_op_bits & Operationrec::OP_LOCK_OWNER) == 0) + { + tmp.i = tmp.p->prevParallelQue; + if (tmp.i != RNIL) + { + ptrCheckGuard(tmp, coprecsize, operationrec); + } + else + { + break; + } + } + + return getNoParallelTransaction(tmp.p); +} +#endif + +#ifdef ACC_SAFE_QUEUE -void Dbacc::moveLastParallelQueueWrite(Signal* signal) +Uint32 +Dbacc::get_parallel_head(OperationrecPtr opPtr) { - /* --------------------------------------------------------------------------------- */ - /* ENSURE THAT ALL OPERATIONS HAVE LOCK MODE SET TO WRITE SINCE WE INSERT A */ - /* WRITE LOCK INTO THE PARALLEL QUEUE. */ - /* --------------------------------------------------------------------------------- */ - while (mlpqOperPtr.p->nextParallelQue != RNIL) { - jam(); - mlpqOperPtr.p->lockMode = operationRecPtr.p->lockMode; - mlpqOperPtr.i = mlpqOperPtr.p->nextParallelQue; - ptrCheckGuard(mlpqOperPtr, coprecsize, operationrec); - }//if - mlpqOperPtr.p->lockMode = operationRecPtr.p->lockMode; -}//Dbacc::moveLastParallelQueueWrite() + while ((opPtr.p->m_op_bits & Operationrec::OP_LOCK_OWNER) == 0 && + opPtr.p->prevParallelQue != RNIL) + { + opPtr.i = opPtr.p->prevParallelQue; + ptrCheckGuard(opPtr, coprecsize, operationrec); + } + + return opPtr.i; +} -/* --------------------------------------------------------------------------------- */ -/* PLACE_WRITE_IN_LOCK_QUEUE */ -/* INPUT: OPERATION_REC_PTR OUR OPERATION POINTER */ -/* QUE_OPER_PTR LOCK QUEUE OWNER OPERATION POINTER */ -/* PWI_PAGEPTR PAGE POINTER OF ELEMENT */ -/* TPWI_ELEMENTPTR ELEMENT POINTER OF ELEMENT */ -/* OUTPUT TRESULT = */ -/* ZPARALLEL_QUEUE OPERATION PLACED IN PARALLEL QUEUE */ -/* OPERATION CAN PROCEED NOW. */ -/* ZSERIAL_QUEUE OPERATION PLACED IN SERIAL QUEUE */ -/* ERROR CODE OPERATION NEEDS ABORTING */ -/* --------------------------------------------------------------------------------- */ -Uint32 Dbacc::placeWriteInLockQueue(Signal* signal) +bool +Dbacc::validate_lock_queue(OperationrecPtr opPtr) +{ + OperationrecPtr loPtr; + loPtr.i = get_parallel_head(opPtr); + ptrCheckGuard(loPtr, coprecsize, operationrec); + + while((loPtr.p->m_op_bits & Operationrec::OP_LOCK_OWNER) == 0 && + loPtr.p->prevSerialQue != RNIL) + { + loPtr.i = loPtr.p->prevSerialQue; + ptrCheckGuard(loPtr, coprecsize, operationrec); + } + + // Now we have lock owner... + vlqrequire(loPtr.p->m_op_bits & Operationrec::OP_LOCK_OWNER); + vlqrequire(loPtr.p->m_op_bits & Operationrec::OP_RUN_QUEUE); + + // 1 Validate page pointer + { + Page8Ptr pagePtr; + pagePtr.i = loPtr.p->elementPage; + ptrCheckGuard(pagePtr, cpagesize, page8); + arrGuard(loPtr.p->elementPointer, 2048); + Uint32 eh = pagePtr.p->word32[loPtr.p->elementPointer]; + vlqrequire(ElementHeader::getLocked(eh)); + vlqrequire(ElementHeader::getOpPtrI(eh) == loPtr.i); + } + + // 2 Lock owner should always have same LOCK_MODE and ACC_LOCK_MODE + if (loPtr.p->m_op_bits & Operationrec::OP_LOCK_MODE) + { + vlqrequire(loPtr.p->m_op_bits & Operationrec::OP_ACC_LOCK_MODE); + } + else + { + vlqrequire((loPtr.p->m_op_bits & Operationrec::OP_ACC_LOCK_MODE) == 0); + } + + // 3 Lock owner should never be waiting... + bool running = false; + { + Uint32 opstate = loPtr.p->m_op_bits & Operationrec::OP_STATE_MASK; + if (opstate == Operationrec::OP_STATE_RUNNING) + running = true; + else + { + vlqrequire(opstate == Operationrec::OP_STATE_EXECUTED); + } + } + + // Validate parallel queue + { + bool many = false; + bool orlockmode = loPtr.p->m_op_bits & Operationrec::OP_LOCK_MODE; + OperationrecPtr lastP = loPtr; + + while (lastP.p->nextParallelQue != RNIL) + { + Uint32 prev = lastP.i; + lastP.i = lastP.p->nextParallelQue; + ptrCheckGuard(lastP, coprecsize, operationrec); + + vlqrequire(lastP.p->prevParallelQue == prev); + + Uint32 opbits = lastP.p->m_op_bits; + many |= loPtr.p->is_same_trans(lastP.p) ? 0 : 1; + orlockmode |= !!(opbits & Operationrec::OP_LOCK_MODE); + + vlqrequire(opbits & Operationrec::OP_RUN_QUEUE); + vlqrequire((opbits & Operationrec::OP_LOCK_OWNER) == 0); + + Uint32 opstate = opbits & Operationrec::OP_STATE_MASK; + if (running) + { + // If I found a running operation, + // all following should be waiting + vlqrequire(opstate == Operationrec::OP_STATE_WAITING); + } + else + { + if (opstate == Operationrec::OP_STATE_RUNNING) + running = true; + else + vlqrequire(opstate == Operationrec::OP_STATE_EXECUTED); + } + + if (lastP.p->m_op_bits & Operationrec::OP_LOCK_MODE) + { + vlqrequire(lastP.p->m_op_bits & Operationrec::OP_ACC_LOCK_MODE); + } + else + { + vlqrequire((lastP.p->m_op_bits && orlockmode) == orlockmode); + vlqrequire((lastP.p->m_op_bits & Operationrec::OP_MASK) == ZREAD || + (lastP.p->m_op_bits & Operationrec::OP_MASK) == ZSCAN_OP); + } + + if (many) + { + vlqrequire(orlockmode == 0); + } + } + + if (lastP.i != loPtr.i) + { + vlqrequire(loPtr.p->m_lo_last_parallel_op_ptr_i == lastP.i); + vlqrequire(lastP.p->m_lock_owner_ptr_i == loPtr.i); + } + else + { + vlqrequire(loPtr.p->m_lo_last_parallel_op_ptr_i == RNIL); + } + } + + // Validate serie queue + if (loPtr.p->nextSerialQue != RNIL) + { + Uint32 prev = loPtr.i; + OperationrecPtr lastS; + lastS.i = loPtr.p->nextSerialQue; + while (true) + { + ptrCheckGuard(lastS, coprecsize, operationrec); + vlqrequire(lastS.p->prevSerialQue == prev); + vlqrequire(getNoParallelTransaction(lastS.p) == 1); + vlqrequire((lastS.p->m_op_bits & Operationrec::OP_LOCK_OWNER) == 0); + vlqrequire((lastS.p->m_op_bits & Operationrec::OP_RUN_QUEUE) == 0); + vlqrequire((lastS.p->m_op_bits & Operationrec::OP_STATE_MASK) == Operationrec::OP_STATE_WAITING); + if (lastS.p->nextSerialQue == RNIL) + break; + prev = lastS.i; + lastS.i = lastS.p->nextSerialQue; + } + + vlqrequire(loPtr.p->m_lo_last_serial_op_ptr_i == lastS.i); + } + else + { + vlqrequire(loPtr.p->m_lo_last_serial_op_ptr_i == RNIL); + } + return true; +} + +NdbOut& +operator<<(NdbOut & out, Dbacc::OperationrecPtr ptr) { - if (!((getNoParallelTransaction(queOperPtr.p) == 1) && - (queOperPtr.p->transId1 == operationRecPtr.p->transId1) && - (queOperPtr.p->transId2 == operationRecPtr.p->transId2))) { + Uint32 opbits = ptr.p->m_op_bits; + out << "[ " << dec << ptr.i + << " [ " << hex << ptr.p->transId1 + << " " << hex << ptr.p->transId2 << "] " + << " bits: H'" << hex << opbits << " "; + + bool read = false; + switch(opbits & Dbacc::Operationrec::OP_MASK){ + case ZREAD: out << "READ "; read = true; break; + case ZINSERT: out << "INSERT "; break; + case ZUPDATE: out << "UPDATE "; break; + case ZDELETE: out << "DELETE "; break; + case ZWRITE: out << "WRITE "; break; + case ZSCAN_OP: out << "SCAN "; read = true; break; + default: + out << "<Unknown: H'" + << hex << (opbits & Dbacc::Operationrec::OP_MASK) + << "> "; + } + + if (read) + { + if (opbits & Dbacc::Operationrec::OP_LOCK_MODE) + out << "(X)"; + else + out << "(S)"; + if (opbits & Dbacc::Operationrec::OP_ACC_LOCK_MODE) + out << "(X)"; + else + out << "(S)"; + } + + if (opbits) + { + out << "(RQ)"; + } + + switch(opbits & Dbacc::Operationrec::OP_STATE_MASK){ + case Dbacc::Operationrec::OP_STATE_WAITING: + out << " WAITING "; break; + case Dbacc::Operationrec::OP_STATE_RUNNING: + out << " RUNNING "; break; + case Dbacc::Operationrec::OP_STATE_EXECUTED: + out << " EXECUTED "; break; + case Dbacc::Operationrec::OP_STATE_IDLE: + out << " IDLE "; break; + default: + out << " <Unknown: H'" + << hex << (opbits & Dbacc::Operationrec::OP_STATE_MASK) + << "> "; + } + +/* + OP_MASK = 0x000F // 4 bits for operation type + ,OP_LOCK_MODE = 0x0010 // 0 - shared lock, 1 = exclusive lock + ,OP_ACC_LOCK_MODE = 0x0020 // Or:de lock mode of all operation + // before me + ,OP_LOCK_OWNER = 0x0040 + ,OP_DIRTY_READ = 0x0080 + ,OP_LOCK_REQ = 0x0100 // isAccLockReq + ,OP_COMMIT_DELETE_CHECK = 0x0200 + ,OP_INSERT_IS_DONE = 0x0400 + ,OP_ELEMENT_DISAPPEARED = 0x0800 + + ,OP_STATE_MASK = 0xF000 + ,OP_STATE_IDLE = 0xF000 + ,OP_STATE_WAITING = 0x0000 + ,OP_STATE_RUNNING = 0x1000 + ,OP_STATE_EXECUTED = 0x3000 + }; +*/ + if (opbits & Dbacc::Operationrec::OP_LOCK_OWNER) + out << "LO "; + + if (opbits & Dbacc::Operationrec::OP_DIRTY_READ) + out << "DR "; + + if (opbits & Dbacc::Operationrec::OP_LOCK_REQ) + out << "LOCK_REQ "; + + if (opbits & Dbacc::Operationrec::OP_COMMIT_DELETE_CHECK) + out << "COMMIT_DELETE_CHECK "; + + if (opbits & Dbacc::Operationrec::OP_INSERT_IS_DONE) + out << "INSERT_IS_DONE "; + + if (opbits & Dbacc::Operationrec::OP_ELEMENT_DISAPPEARED) + out << "ELEMENT_DISAPPEARED "; + + if (opbits & Dbacc::Operationrec::OP_LOCK_OWNER) + { + out << "last_parallel: " << dec << ptr.p->m_lo_last_parallel_op_ptr_i << " "; + out << "last_serial: " << dec << ptr.p->m_lo_last_serial_op_ptr_i << " "; + } + + out << "]"; + return out; +} + +void +Dbacc::dump_lock_queue(OperationrecPtr loPtr) +{ + if ((loPtr.p->m_op_bits & Operationrec::OP_LOCK_OWNER) == 0) + { + while ((loPtr.p->m_op_bits & Operationrec::OP_LOCK_OWNER) == 0 && + loPtr.p->prevParallelQue != RNIL) + { + loPtr.i = loPtr.p->prevParallelQue; + ptrCheckGuard(loPtr, coprecsize, operationrec); + } + + while ((loPtr.p->m_op_bits & Operationrec::OP_LOCK_OWNER) == 0 && + loPtr.p->prevSerialQue != RNIL) + { + loPtr.i = loPtr.p->prevSerialQue; + ptrCheckGuard(loPtr, coprecsize, operationrec); + } + + ndbassert(loPtr.p->m_op_bits & Operationrec::OP_LOCK_OWNER); + } + + ndbout << "-- HEAD --" << endl; + OperationrecPtr tmp = loPtr; + while (tmp.i != RNIL) + { + ptrCheckGuard(tmp, coprecsize, operationrec); + ndbout << tmp << " "; + tmp.i = tmp.p->nextParallelQue; + + if (tmp.i == loPtr.i) + { + ndbout << " <LOOP>"; + break; + } + } + ndbout << endl; + + tmp.i = loPtr.p->nextSerialQue; + while (tmp.i != RNIL) + { + ptrCheckGuard(tmp, coprecsize, operationrec); + OperationrecPtr tmp2 = tmp; + + if (tmp.i == loPtr.i) + { + ndbout << "<LOOP S>" << endl; + break; + } + + while (tmp2.i != RNIL) + { + ptrCheckGuard(tmp2, coprecsize, operationrec); + ndbout << tmp2 << " "; + tmp2.i = tmp2.p->nextParallelQue; + + if (tmp2.i == tmp.i) + { + ndbout << "<LOOP 3>"; + break; + } + } + ndbout << endl; + tmp.i = tmp.p->nextSerialQue; + } +} +#endif + +/* ------------------------------------------------------------------------- + * PLACE_WRITE_IN_LOCK_QUEUE + * INPUT: OPERATION_REC_PTR OUR OPERATION POINTER + * QUE_OPER_PTR LOCK QUEUE OWNER OPERATION POINTER + * PWI_PAGEPTR PAGE POINTER OF ELEMENT + * TPWI_ELEMENTPTR ELEMENT POINTER OF ELEMENT + * OUTPUT TRESULT = + * ZPARALLEL_QUEUE OPERATION PLACED IN PARALLEL QUEUE + * OPERATION CAN PROCEED NOW. + * ZSERIAL_QUEUE OPERATION PLACED IN SERIAL QUEUE + * ERROR CODE OPERATION NEEDS ABORTING + * ------------------------------------------------------------------------- */ +Uint32 +Dbacc::placeWriteInLockQueue(OperationrecPtr lockOwnerPtr) +{ + OperationrecPtr lastOpPtr; + lastOpPtr.i = lockOwnerPtr.p->m_lo_last_parallel_op_ptr_i; + Uint32 opbits = operationRecPtr.p->m_op_bits; + + if (lastOpPtr.i == RNIL) + { + lastOpPtr = lockOwnerPtr; + } + else + { + ptrCheckGuard(lastOpPtr, coprecsize, operationrec); + } + + ndbassert(get_parallel_head(lastOpPtr) == lockOwnerPtr.i); + + Uint32 lastbits = lastOpPtr.p->m_op_bits; + if (lastbits & Operationrec::OP_ACC_LOCK_MODE) + { + if(operationRecPtr.p->is_same_trans(lastOpPtr.p)) + { + goto checkop; + } + } + else + { + /** + * We dont have an exclusive lock on operation and + * + */ jam(); - placeSerialQueueWrite(signal); - return ZSERIAL_QUEUE; - }//if + + /** + * Scan parallell queue to see if we are the only one + */ + OperationrecPtr loopPtr = lockOwnerPtr; + do + { + ptrCheckGuard(loopPtr, coprecsize, operationrec); + if (!loopPtr.p->is_same_trans(operationRecPtr.p)) + { + goto serial; + } + loopPtr.i = loopPtr.p->nextParallelQue; + } while (loopPtr.i != RNIL); + + goto checkop; + } + +serial: + jam(); + placeSerialQueue(lockOwnerPtr, operationRecPtr); + + validate_lock_queue(lockOwnerPtr); + return ZSERIAL_QUEUE; + +checkop: /* - WE ARE PERFORMING AN READ EXCLUSIVE, INSERT, UPDATE OR DELETE IN THE SAME - TRANSACTION WHERE WE PREVIOUSLY HAVE EXECUTED AN OPERATION. - Read-All, Update-All, Insert-All and Delete-Insert are allowed - combinations. - Delete-Read, Delete-Update and Delete-Delete are not an allowed - combination and will result in tuple not found error. + WE ARE PERFORMING AN READ EXCLUSIVE, INSERT, UPDATE OR DELETE IN THE SAME + TRANSACTION WHERE WE PREVIOUSLY HAVE EXECUTED AN OPERATION. + Read-All, Update-All, Insert-All and Delete-Insert are allowed + combinations. + Delete-Read, Delete-Update and Delete-Delete are not an allowed + combination and will result in tuple not found error. */ - mlpqOperPtr = queOperPtr; - moveLastParallelQueueWrite(signal); + Uint32 lstate = lastbits & Operationrec::OP_STATE_MASK; - if (operationRecPtr.p->operation == ZINSERT && - mlpqOperPtr.p->operation != ZDELETE){ + Uint32 retValue = ZSERIAL_QUEUE; // So that it gets blocked... + if (lstate == Operationrec::OP_STATE_EXECUTED) + { jam(); - return ZWRITE_ERROR; - }//if - if(operationRecPtr.p->operation == ZWRITE) - { - operationRecPtr.p->operation = - (mlpqOperPtr.p->operation == ZDELETE) ? ZINSERT : ZUPDATE; + /** + * Since last operation has executed...we can now check operation types + * if not, we have to wait until it has executed + */ + Uint32 op = opbits & Operationrec::OP_MASK; + Uint32 lop = lastbits & Operationrec::OP_MASK; + if (op == ZINSERT && lop != ZDELETE) + { + jam(); + return ZWRITE_ERROR; + }//if + + /** + * NOTE. No checking op operation types, as one can read different save + * points... + */ +#if 0 + if (lop == ZDELETE && (op != ZINSERT && op != ZWRITE)) + { + jam(); + return ZREAD_ERROR; + } +#else + if (lop == ZDELETE && (op == ZUPDATE && op == ZDELETE)) + { + jam(); + return ZREAD_ERROR; + } +#endif + + if(op == ZWRITE) + { + opbits &= ~(Uint32)Operationrec::OP_MASK; + opbits |= (lop == ZDELETE) ? ZINSERT : ZUPDATE; + } + + opbits |= Operationrec::OP_STATE_RUNNING; + operationRecPtr.p->localdata[0] = lastOpPtr.p->localdata[0]; + operationRecPtr.p->localdata[1] = lastOpPtr.p->localdata[1]; + retValue = ZPARALLEL_QUEUE; } - operationRecPtr.p->localdata[0] = queOperPtr.p->localdata[0]; - operationRecPtr.p->localdata[1] = queOperPtr.p->localdata[1]; - operationRecPtr.p->prevParallelQue = mlpqOperPtr.i; - mlpqOperPtr.p->nextParallelQue = operationRecPtr.i; - return ZPARALLEL_QUEUE; + opbits |= Operationrec::OP_RUN_QUEUE; + operationRecPtr.p->m_op_bits = opbits; + operationRecPtr.p->prevParallelQue = lastOpPtr.i; + operationRecPtr.p->m_lock_owner_ptr_i = lockOwnerPtr.i; + lastOpPtr.p->nextParallelQue = operationRecPtr.i; + lockOwnerPtr.p->m_lo_last_parallel_op_ptr_i = operationRecPtr.i; + + validate_lock_queue(lockOwnerPtr); + + return retValue; }//Dbacc::placeWriteInLockQueue() -/* --------------------------------------------------------------------------------- */ -/* WE HAVE TO PLACE IT SOMEWHERE IN THE SERIAL QUEUE INSTEAD. */ -/* --------------------------------------------------------------------------------- */ -void Dbacc::placeSerialQueueWrite(Signal* signal) +Uint32 +Dbacc::placeReadInLockQueue(OperationrecPtr lockOwnerPtr) { - readWriteOpPtr = queOperPtr; - PSQW_LOOP: - if (readWriteOpPtr.p->nextSerialQue == RNIL) { + OperationrecPtr lastOpPtr; + OperationrecPtr loopPtr = lockOwnerPtr; + lastOpPtr.i = lockOwnerPtr.p->m_lo_last_parallel_op_ptr_i; + Uint32 opbits = operationRecPtr.p->m_op_bits; + + if (lastOpPtr.i == RNIL) + { + lastOpPtr = lockOwnerPtr; + } + else + { + ptrCheckGuard(lastOpPtr, coprecsize, operationrec); + } + + ndbassert(get_parallel_head(lastOpPtr) == lockOwnerPtr.i); + + /** + * Last operation in parallell queue of lock owner is same trans + * and ACC_LOCK_MODE is exlusive, then we can proceed + */ + Uint32 lastbits = lastOpPtr.p->m_op_bits; + bool same = operationRecPtr.p->is_same_trans(lastOpPtr.p); + if (same && (lastbits & Operationrec::OP_ACC_LOCK_MODE)) + { jam(); - /* --------------------------------------------------------------------------------- */ - /* WE COULD NOT PUT IN ANY PARALLEL QUEUE. WE MUST PUT IT LAST IN THE SERIAL QUEUE. */ - /* --------------------------------------------------------------------------------- */ - readWriteOpPtr.p->nextSerialQue = operationRecPtr.i; - operationRecPtr.p->prevSerialQue = readWriteOpPtr.i; - return; - }//if - readWriteOpPtr.i = readWriteOpPtr.p->nextSerialQue; - ptrCheckGuard(readWriteOpPtr, coprecsize, operationrec); - if (getNoParallelTransaction(readWriteOpPtr.p) == 1) { - /* --------------------------------------------------------------------------------- */ - /* THERE WAS ONLY ONE TRANSACTION INVOLVED IN THE PARALLEL QUEUE. IF THIS IS OUR */ - /* TRANSACTION WE CAN STILL GET HOLD OF THE LOCK. */ - /* --------------------------------------------------------------------------------- */ - if ((readWriteOpPtr.p->transId1 == operationRecPtr.p->transId1) && - (readWriteOpPtr.p->transId2 == operationRecPtr.p->transId2)) { + goto checkop; + } + + if ((lastbits & Operationrec::OP_ACC_LOCK_MODE) && !same) + { + jam(); + /** + * Last op in serial queue had X-lock and was not our transaction... + */ + goto serial; + } + + if (lockOwnerPtr.p->nextSerialQue == RNIL) + { + jam(); + goto checkop; + } + + /** + * Scan parallell queue to see if we are already there... + */ + do + { + ptrCheckGuard(loopPtr, coprecsize, operationrec); + if (loopPtr.p->is_same_trans(operationRecPtr.p)) + goto checkop; + loopPtr.i = loopPtr.p->nextParallelQue; + } while (loopPtr.i != RNIL); + +serial: + placeSerialQueue(lockOwnerPtr, operationRecPtr); + + validate_lock_queue(lockOwnerPtr); + + return ZSERIAL_QUEUE; + +checkop: + Uint32 lstate = lastbits & Operationrec::OP_STATE_MASK; + + Uint32 retValue = ZSERIAL_QUEUE; // So that it gets blocked... + if (lstate == Operationrec::OP_STATE_EXECUTED) + { + jam(); + + /** + * NOTE. No checking op operation types, as one can read different save + * points... + */ + +#if 0 + /** + * Since last operation has executed...we can now check operation types + * if not, we have to wait until it has executed + */ + if (lop == ZDELETE) + { jam(); - /* --------------------------------------------------------------------------------- */ - /* WE ARE PERFORMING AN UPDATE OR DELETE IN THE SAME TRANSACTION WHERE WE ALREADY */ - /* PREVIOUSLY HAVE EXECUTED AN OPERATION. INSERT-DELETE, READ-UPDATE, READ-READ, */ - /* UPDATE-UPDATE, UPDATE-DELETE, READ-DELETE, INSERT-READ, INSERT-UPDATE ARE ALLOWED */ - /* COMBINATIONS. A NEW INSERT AFTER A DELETE IS NOT ALLOWED AND SUCH AN INSERT WILL */ - /* GO TO THE SERIAL LOCK QUEUE WHICH IT WILL NOT LEAVE UNTIL A TIME-OUT AND THE */ - /* TRANSACTION IS ABORTED. READS AND UPDATES AFTER DELETES IS ALSO NOT ALLOWED. */ - /* --------------------------------------------------------------------------------- */ - mlpqOperPtr = readWriteOpPtr; - moveLastParallelQueueWrite(signal); - readWriteOpPtr = mlpqOperPtr; - operationRecPtr.p->prevParallelQue = readWriteOpPtr.i; - readWriteOpPtr.p->nextParallelQue = operationRecPtr.i; - operationRecPtr.p->localdata[0] = readWriteOpPtr.p->localdata[0]; - operationRecPtr.p->localdata[1] = readWriteOpPtr.p->localdata[1]; - return; - }//if - }//if - goto PSQW_LOOP; -}//Dbacc::placeSerialQueueWrite() + return ZREAD_ERROR; + } +#endif + + opbits |= Operationrec::OP_STATE_RUNNING; + operationRecPtr.p->localdata[0] = lastOpPtr.p->localdata[0]; + operationRecPtr.p->localdata[1] = lastOpPtr.p->localdata[1]; + retValue = ZPARALLEL_QUEUE; + } + opbits |= (lastbits & Operationrec::OP_ACC_LOCK_MODE); + opbits |= Operationrec::OP_RUN_QUEUE; + operationRecPtr.p->m_op_bits = opbits; + + operationRecPtr.p->prevParallelQue = lastOpPtr.i; + operationRecPtr.p->m_lock_owner_ptr_i = lockOwnerPtr.i; + lastOpPtr.p->nextParallelQue = operationRecPtr.i; + lockOwnerPtr.p->m_lo_last_parallel_op_ptr_i = operationRecPtr.i; + + validate_lock_queue(lockOwnerPtr); + + return retValue; +}//Dbacc::placeReadInLockQueue + +void Dbacc::placeSerialQueue(OperationrecPtr lockOwnerPtr, + OperationrecPtr opPtr) +{ + OperationrecPtr lastOpPtr; + lastOpPtr.i = lockOwnerPtr.p->m_lo_last_serial_op_ptr_i; + + if (lastOpPtr.i == RNIL) + { + // Lock owner is last... + ndbrequire(lockOwnerPtr.p->nextSerialQue == RNIL); + lastOpPtr = lockOwnerPtr; + } + else + { + ptrCheckGuard(lastOpPtr, coprecsize, operationrec); + } + + operationRecPtr.p->prevSerialQue = lastOpPtr.i; + lastOpPtr.p->nextSerialQue = opPtr.i; + lockOwnerPtr.p->m_lo_last_serial_op_ptr_i = opPtr.i; +} /* ------------------------------------------------------------------------- */ /* ACC KEYREQ END */ /* ------------------------------------------------------------------------- */ void Dbacc::acckeyref1Lab(Signal* signal, Uint32 result_code) { - if (operationRecPtr.p->keyinfoPage != RNIL) { - jam(); - rpPageptr.i = operationRecPtr.p->keyinfoPage; - ptrCheckGuard(rpPageptr, cpagesize, page8); - releasePage(signal); - operationRecPtr.p->keyinfoPage = RNIL; - }//if - operationRecPtr.p->transactionstate = WAIT_COMMIT_ABORT; + operationRecPtr.p->m_op_bits = Operationrec::OP_INITIAL; /* ************************<< */ /* ACCKEYREF */ /* ************************<< */ @@ -1658,15 +2239,15 @@ void Dbacc::acckeyref1Lab(Signal* signal, Uint32 result_code) return; }//Dbacc::acckeyref1Lab() -/* ******************--------------------------------------------------------------- */ -/* ACCMINUPDATE UPDATE LOCAL KEY REQ */ -/* DESCRIPTION: UPDATES LOCAL KEY OF AN ELEMENTS IN THE HASH TABLE */ -/* THIS SIGNAL IS WAITED AFTER ANY INSERT REQ */ -/* ENTER ACCMINUPDATE WITH SENDER: LQH, LEVEL B */ -/* OPERATION_REC_PTR, OPERATION RECORD PTR */ -/* CLOCALKEY(0), LOCAL KEY 1 */ -/* CLOCALKEY(1) LOCAL KEY 2 */ -/* ******************--------------------------------------------------------------- */ +/* ******************----------------------------------------------------- */ +/* ACCMINUPDATE UPDATE LOCAL KEY REQ */ +/* DESCRIPTION: UPDATES LOCAL KEY OF AN ELEMENTS IN THE HASH TABLE */ +/* THIS SIGNAL IS WAITED AFTER ANY INSERT REQ */ +/* ENTER ACCMINUPDATE WITH SENDER: LQH, LEVEL B */ +/* OPERATION_REC_PTR, OPERATION RECORD PTR */ +/* CLOCALKEY(0), LOCAL KEY 1 */ +/* CLOCALKEY(1) LOCAL KEY 2 */ +/* ******************----------------------------------------------------- */ void Dbacc::execACCMINUPDATE(Signal* signal) { Page8Ptr ulkPageidptr; @@ -1679,19 +2260,26 @@ void Dbacc::execACCMINUPDATE(Signal* signal) tlocalkey1 = signal->theData[1]; tlocalkey2 = signal->theData[2]; ptrCheckGuard(operationRecPtr, coprecsize, operationrec); - if (operationRecPtr.p->transactionstate == ACTIVE) { - fragrecptr.i = operationRecPtr.p->fragptr; - ulkPageidptr.i = operationRecPtr.p->elementPage; - tulkLocalPtr = operationRecPtr.p->elementPointer + operationRecPtr.p->elementIsforward; + Uint32 opbits = operationRecPtr.p->m_op_bits; + fragrecptr.i = operationRecPtr.p->fragptr; + ulkPageidptr.i = operationRecPtr.p->elementPage; + tulkLocalPtr = operationRecPtr.p->elementPointer + + operationRecPtr.p->elementIsforward; + + if ((opbits & Operationrec::OP_STATE_MASK) == Operationrec::OP_STATE_RUNNING) + { ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec); ptrCheckGuard(ulkPageidptr, cpagesize, page8); dbgWord32(ulkPageidptr, tulkLocalPtr, tlocalkey1); arrGuard(tulkLocalPtr, 2048); ulkPageidptr.p->word32[tulkLocalPtr] = tlocalkey1; operationRecPtr.p->localdata[0] = tlocalkey1; - if (fragrecptr.p->localkeylen == 1) { + if (likely(fragrecptr.p->localkeylen == 1)) + { return; - } else if (fragrecptr.p->localkeylen == 2) { + } + else if (fragrecptr.p->localkeylen == 2) + { jam(); tulkLocalPtr = tulkLocalPtr + operationRecPtr.p->elementIsforward; operationRecPtr.p->localdata[1] = tlocalkey2; @@ -1715,15 +2303,17 @@ void Dbacc::execACC_COMMITREQ(Signal* signal) { Uint8 Toperation; jamEntry(); - operationRecPtr.i = signal->theData[0]; + Uint32 tmp = operationRecPtr.i = signal->theData[0]; ptrCheckGuard(operationRecPtr, coprecsize, operationrec); - ndbrequire(operationRecPtr.p->transactionstate == ACTIVE); + void* ptr = operationRecPtr.p; + Uint32 opbits = operationRecPtr.p->m_op_bits; fragrecptr.i = operationRecPtr.p->fragptr; ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec); + Toperation = opbits & Operationrec::OP_MASK; commitOperation(signal); - Toperation = operationRecPtr.p->operation; - operationRecPtr.p->transactionstate = IDLE; - operationRecPtr.p->operation = ZUNDEFINED_OP; + ndbassert(operationRecPtr.i == tmp); + ndbassert(operationRecPtr.p == ptr); + operationRecPtr.p->m_op_bits = Operationrec::OP_INITIAL; if(Toperation != ZREAD){ fragrecptr.p->m_commit_count++; if (Toperation != ZINSERT) { @@ -1732,7 +2322,7 @@ void Dbacc::execACC_COMMITREQ(Signal* signal) } else { jam(); fragrecptr.p->noOfElements--; - fragrecptr.p->slack += operationRecPtr.p->insertDeleteLen; + fragrecptr.p->slack += fragrecptr.p->elementLength; if (fragrecptr.p->slack > fragrecptr.p->slackCheck) { /* TIME FOR JOIN BUCKETS PROCESS */ if (fragrecptr.p->expandCounter > 0) { @@ -1751,7 +2341,7 @@ void Dbacc::execACC_COMMITREQ(Signal* signal) } else { jam(); /* EXPAND PROCESS HANDLING */ fragrecptr.p->noOfElements++; - fragrecptr.p->slack -= operationRecPtr.p->insertDeleteLen; + fragrecptr.p->slack -= fragrecptr.p->elementLength; if (fragrecptr.p->slack >= (1u << 31)) { /* IT MEANS THAT IF SLACK < ZERO */ if (fragrecptr.p->expandFlag == 0) { @@ -1768,45 +2358,57 @@ void Dbacc::execACC_COMMITREQ(Signal* signal) return; }//Dbacc::execACC_COMMITREQ() -/* ******************--------------------------------------------------------------- */ -/* ACC ABORT REQ ABORT ALL OPERATION OF THE TRANSACTION */ -/* ******************------------------------------+ */ -/* SENDER: LQH, LEVEL B */ -/* ******************--------------------------------------------------------------- */ -/* ACC ABORT REQ ABORT TRANSACTION */ -/* ******************------------------------------+ */ +/* ******************------------------------------------------------------- */ +/* ACC ABORT REQ ABORT ALL OPERATION OF THE TRANSACTION */ +/* ******************------------------------------+ */ +/* SENDER: LQH, LEVEL B */ +/* ******************------------------------------------------------------- */ +/* ACC ABORT REQ ABORT TRANSACTION */ +/* ******************------------------------------+ */ /* SENDER: LQH, LEVEL B */ void Dbacc::execACC_ABORTREQ(Signal* signal) { jamEntry(); - accAbortReqLab(signal); -}//Dbacc::execACC_ABORTREQ() - -void Dbacc::accAbortReqLab(Signal* signal) -{ operationRecPtr.i = signal->theData[0]; - bool sendConf = signal->theData[1]; + Uint32 sendConf = signal->theData[1]; ptrCheckGuard(operationRecPtr, coprecsize, operationrec); + fragrecptr.i = operationRecPtr.p->fragptr; + Uint32 opbits = operationRecPtr.p->m_op_bits; + Uint32 opstate = opbits & Operationrec::OP_STATE_MASK; tresult = 0; /* ZFALSE */ - if ((operationRecPtr.p->transactionstate == ACTIVE) || - (operationRecPtr.p->transactionstate == WAIT_COMMIT_ABORT)) { + + if (opbits == Operationrec::OP_EXECUTED_DIRTY_READ) + { + jam(); + } + else if (opstate == Operationrec::OP_STATE_EXECUTED || + opstate == Operationrec::OP_STATE_WAITING || + opstate == Operationrec::OP_STATE_RUNNING) + { jam(); - fragrecptr.i = operationRecPtr.p->fragptr; ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec); - operationRecPtr.p->transactionstate = ABORT; abortOperation(signal); - } else { - ndbrequire(operationRecPtr.p->transactionstate == IDLE); - jam(); - }//if - operationRecPtr.p->transactionstate = IDLE; - operationRecPtr.p->operation = ZUNDEFINED_OP; - if (! sendConf) - return; + } + + operationRecPtr.p->m_op_bits = Operationrec::OP_INITIAL; + signal->theData[0] = operationRecPtr.p->userptr; - sendSignal(operationRecPtr.p->userblockref, GSN_ACC_ABORTCONF, signal, 1, JBB); - return; -}//Dbacc::accAbortReqLab() + signal->theData[1] = 0; + switch(sendConf){ + case 0: + return; + case 2: + if (opstate != Operationrec::OP_STATE_RUNNING) + { + return; + } + case 1: + sendSignal(operationRecPtr.p->userblockref, GSN_ACC_ABORTCONF, + signal, 1, JBB); + } + + signal->theData[1] = RNIL; +} /* * Lock or unlock tuple. @@ -1847,8 +2449,7 @@ void Dbacc::execACC_LOCKREQ(Signal* signal) // init as in ACCSEIZEREQ operationRecPtr.p->userptr = req->userPtr; operationRecPtr.p->userblockref = req->userRef; - operationRecPtr.p->operation = ZUNDEFINED_OP; - operationRecPtr.p->transactionstate = IDLE; + operationRecPtr.p->m_op_bits = Operationrec::OP_INITIAL; operationRecPtr.p->scanRecPtr = RNIL; // do read with lock via ACCKEYREQ Uint32 lockMode = (lockOp == AccLockReq::LockShared) ? 0 : 1; @@ -1899,8 +2500,8 @@ void Dbacc::execACC_LOCKREQ(Signal* signal) jam(); // do abort via ACC_ABORTREQ (immediate) signal->theData[0] = req->accOpPtr; - signal->theData[1] = false; // Dont send abort - accAbortReqLab(signal); + signal->theData[1] = 0; // Dont send abort + execACC_ABORTREQ(signal); releaseOpRec(signal); req->returnCode = AccLockReq::Success; *sig = *req; @@ -1910,8 +2511,8 @@ void Dbacc::execACC_LOCKREQ(Signal* signal) jam(); // do abort via ACC_ABORTREQ (with conf signal) signal->theData[0] = req->accOpPtr; - signal->theData[1] = true; // send abort - accAbortReqLab(signal); + signal->theData[1] = 1; // send abort + execACC_ABORTREQ(signal); releaseOpRec(signal); req->returnCode = AccLockReq::Success; *sig = *req; @@ -2570,16 +3171,30 @@ void Dbacc::getdirindex(Signal* signal) }//Dbacc::getdirindex() Uint32 -Dbacc::readTablePk(Uint32 localkey1) +Dbacc::readTablePk(Uint32 localkey1, Uint32 eh, const Operationrec* op) { + int ret; Uint32 tableId = fragrecptr.p->myTableId; Uint32 fragId = fragrecptr.p->myfid; - Uint32 fragPageId = localkey1 >> MAX_TUPLES_BITS; - Uint32 pageIndex = localkey1 & ((1 << MAX_TUPLES_BITS ) - 1); + bool xfrm = fragrecptr.p->hasCharAttr; + #ifdef VM_TRACE memset(ckeys, 0x1f, (fragrecptr.p->keyLength * MAX_XFRM_MULTIPLY) << 2); #endif - int ret = c_tup->accReadPk(tableId, fragId, fragPageId, pageIndex, ckeys, true); + + if (likely(localkey1 != ~(Uint32)0)) + { + Uint32 fragPageId = localkey1 >> MAX_TUPLES_BITS; + Uint32 pageIndex = localkey1 & ((1 << MAX_TUPLES_BITS ) - 1); + ret = c_tup->accReadPk(tableId, fragId, fragPageId, pageIndex, + ckeys, true); + } + else + { + ndbrequire(ElementHeader::getLocked(eh)); + ndbrequire((op->m_op_bits & Operationrec::OP_MASK) != ZSCAN_OP); + ret = c_lqh->readPrimaryKeys(op->userptr, ckeys, xfrm); + } jamEntry(); ndbrequire(ret >= 0); return ret; @@ -2613,11 +3228,12 @@ void ndb_acc_ia64_icc810_dummy_func() #endif #endif -void Dbacc::getElement(Signal* signal) +Uint32 +Dbacc::getElement(Signal* signal, OperationrecPtr& lockOwnerPtr) { + Uint32 errcode; DirRangePtr geOverflowrangeptr; DirectoryarrayPtr geOverflowDirptr; - OperationrecPtr geTmpOperationRecPtr; Uint32 tgeElementHeader; Uint32 tgeElemStep; Uint32 tgeContainerhead; @@ -2632,7 +3248,6 @@ void Dbacc::getElement(Signal* signal) getdirindex(signal); tgePageindex = tgdiPageindex; gePageptr = gdiPageptr; - tgeResult = ZFALSE; /* * The value seached is * - table key for ACCKEYREQ, stored in TUP @@ -2642,7 +3257,6 @@ void Dbacc::getElement(Signal* signal) ndbrequire(TelemLen == ZELEM_HEAD_SIZE + fragrecptr.p->localkeylen); tgeNextptrtype = ZLEFT; - tgeLocked = 0; const Uint32 tmp = fragrecptr.p->k + fragrecptr.p->lhfragbits; const Uint32 opHashValuePart = (operationRecPtr.p->hashValue >> tmp) &0xFFFF; @@ -2655,9 +3269,17 @@ void Dbacc::getElement(Signal* signal) tgeKeyptr = (tgeElementptr + ZELEM_HEAD_SIZE) + fragrecptr.p->localkeylen; tgeElemStep = TelemLen; tgeForward = 1; - if (tgeContainerptr >= 2048) { ACCKEY_error(4); return;} + if (unlikely(tgeContainerptr >= 2048)) + { + errcode = 4; + goto error; + } tgeRemLen = gePageptr.p->word32[tgeContainerptr] >> 26; - if ((tgeContainerptr + tgeRemLen - 1) >= 2048) { ACCKEY_error(5); return;} + if (unlikely(((tgeContainerptr + tgeRemLen - 1) >= 2048))) + { + errcode = 5; + goto error; + } } else if (tgeNextptrtype == ZRIGHT) { jam(); tgeContainerptr = tgeContainerptr + ((ZHEAD_SIZE + ZBUF_SIZE) - ZCON_HEAD_SIZE); @@ -2665,53 +3287,69 @@ void Dbacc::getElement(Signal* signal) tgeKeyptr = (tgeElementptr - ZELEM_HEAD_SIZE) - fragrecptr.p->localkeylen; tgeElemStep = 0 - TelemLen; tgeForward = (Uint32)-1; - if (tgeContainerptr >= 2048) { ACCKEY_error(4); return;} + if (unlikely(tgeContainerptr >= 2048)) + { + errcode = 4; + goto error; + } tgeRemLen = gePageptr.p->word32[tgeContainerptr] >> 26; - if ((tgeContainerptr - tgeRemLen) >= 2048) { ACCKEY_error(5); return;} + if (unlikely((tgeContainerptr - tgeRemLen) >= 2048)) + { + errcode = 5; + goto error; + } } else { - ACCKEY_error(6); return; + errcode = 6; + goto error; }//if if (tgeRemLen >= ZCON_HEAD_SIZE + TelemLen) { - if (tgeRemLen > ZBUF_SIZE) { - ACCKEY_error(7); return; + if (unlikely(tgeRemLen > ZBUF_SIZE)) + { + errcode = 7; + goto error; }//if - /* --------------------------------------------------------------------------------- */ - // There is at least one element in this container. Check if it is the element - // searched for. - /* --------------------------------------------------------------------------------- */ + /* ------------------------------------------------------------------- */ + // There is at least one element in this container. + // Check if it is the element searched for. + /* ------------------------------------------------------------------- */ do { tgeElementHeader = gePageptr.p->word32[tgeElementptr]; tgeRemLen = tgeRemLen - TelemLen; Uint32 hashValuePart; + Uint32 localkey1, localkey2; + lockOwnerPtr.i = RNIL; + lockOwnerPtr.p = NULL; if (ElementHeader::getLocked(tgeElementHeader)) { jam(); - geTmpOperationRecPtr.i = ElementHeader::getOpPtrI(tgeElementHeader); - ptrCheckGuard(geTmpOperationRecPtr, coprecsize, operationrec); - hashValuePart = geTmpOperationRecPtr.p->hashvaluePart; + lockOwnerPtr.i = ElementHeader::getOpPtrI(tgeElementHeader); + ptrCheckGuard(lockOwnerPtr, coprecsize, operationrec); + hashValuePart = lockOwnerPtr.p->hashvaluePart; + localkey1 = lockOwnerPtr.p->localdata[0]; + localkey2 = lockOwnerPtr.p->localdata[1]; } else { jam(); hashValuePart = ElementHeader::getHashValuePart(tgeElementHeader); + localkey1 = gePageptr.p->word32[tgeElementptr + tgeForward]; + localkey2 = 0; } if (hashValuePart == opHashValuePart) { jam(); - Uint32 localkey1 = gePageptr.p->word32[tgeElementptr + tgeForward]; - Uint32 localkey2 = 0; bool found; - if (! searchLocalKey) { - Uint32 len = readTablePk(localkey1); + if (! searchLocalKey) + { + Uint32 len = readTablePk(localkey1, tgeElementptr, lockOwnerPtr.p); found = (len == operationRecPtr.p->xfrmtupkeylen) && (memcmp(Tkeydata, ckeys, len << 2) == 0); } else { jam(); found = (localkey1 == Tkeydata[0]); } - if (found) { + if (found) + { jam(); - tgeLocked = ElementHeader::getLocked(tgeElementHeader); - tgeResult = ZTRUE; operationRecPtr.p->localdata[0] = localkey1; operationRecPtr.p->localdata[1] = localkey2; - return; + return ZTRUE; } } if (tgeRemLen <= ZCON_HEAD_SIZE) { @@ -2720,18 +3358,22 @@ void Dbacc::getElement(Signal* signal) tgeElementptr = tgeElementptr + tgeElemStep; } while (true); }//if - if (tgeRemLen != ZCON_HEAD_SIZE) { - ACCKEY_error(8); return; + if (unlikely(tgeRemLen != ZCON_HEAD_SIZE)) + { + errcode = 8; + goto error; }//if tgeContainerhead = gePageptr.p->word32[tgeContainerptr]; tgeNextptrtype = (tgeContainerhead >> 7) & 0x3; if (tgeNextptrtype == 0) { jam(); - return; /* NO MORE CONTAINER */ + return ZFALSE; /* NO MORE CONTAINER */ }//if tgePageindex = tgeContainerhead & 0x7f; /* NEXT CONTAINER PAGE INDEX 7 BITS */ - if (tgePageindex > ZEMPTYLIST) { - ACCKEY_error(9); return; + if (unlikely(tgePageindex > ZEMPTYLIST)) + { + errcode = 9; + goto error; }//if if (((tgeContainerhead >> 9) & 1) == ZFALSE) { jam(); @@ -2745,55 +3387,72 @@ void Dbacc::getElement(Signal* signal) ptrCheckGuard(gePageptr, cpagesize, page8); }//if } while (1); - return; + + return ZFALSE; + +error: + ACCKEY_error(errcode); + return ~0; }//Dbacc::getElement() -/* --------------------------------------------------------------------------------- */ -/* --------------------------------------------------------------------------------- */ -/* --------------------------------------------------------------------------------- */ -/* */ -/* END OF GET_ELEMENT MODULE */ -/* */ -/* --------------------------------------------------------------------------------- */ -/* --------------------------------------------------------------------------------- */ -/* --------------------------------------------------------------------------------- */ -/* --------------------------------------------------------------------------------- */ -/* --------------------------------------------------------------------------------- */ -/* */ -/* MODULE: DELETE */ -/* */ -/* --------------------------------------------------------------------------------- */ -/* --------------------------------------------------------------------------------- */ -/* --------------------------------------------------------------------------------- */ -/* COMMITDELETE */ -/* INPUT: OPERATION_REC_PTR, PTR TO AN OPERATION RECORD. */ -/* FRAGRECPTR, PTR TO A FRAGMENT RECORD */ -/* */ -/* OUTPUT: */ -/* NONE */ -/* DESCRIPTION: DELETE OPERATIONS WILL BE COMPLETED AT THE COMMIT OF TRANSA- */ -/* CTION. THIS SUBROUTINE SEARCHS FOR ELEMENT AND DELETES IT. IT DOES SO BY */ -/* REPLACING IT WITH THE LAST ELEMENT IN THE BUCKET. IF THE DELETED ELEMENT */ -/* IS ALSO THE LAST ELEMENT THEN IT IS ONLY NECESSARY TO REMOVE THE ELEMENT. */ -/* --------------------------------------------------------------------------------- */ +/* ------------------------------------------------------------------------- */ +/* ------------------------------------------------------------------------- */ +/* ------------------------------------------------------------------------- */ +/* */ +/* END OF GET_ELEMENT MODULE */ +/* */ +/* ------------------------------------------------------------------------- */ +/* ------------------------------------------------------------------------- */ +/* ------------------------------------------------------------------------- */ +/* ------------------------------------------------------------------------- */ +/* ------------------------------------------------------------------------- */ +/* */ +/* MODULE: DELETE */ +/* */ +/* ------------------------------------------------------------------------- */ +/* ------------------------------------------------------------------------- */ +/* ------------------------------------------------------------------------- */ +/* COMMITDELETE */ +/* INPUT: OPERATION_REC_PTR, PTR TO AN OPERATION RECORD. */ +/* FRAGRECPTR, PTR TO A FRAGMENT RECORD */ +/* */ +/* OUTPUT: */ +/* NONE */ +/* DESCRIPTION: DELETE OPERATIONS WILL BE COMPLETED AT THE + * COMMIT OF TRANSACTION. THIS SUBROUTINE SEARCHS FOR ELEMENT AND + * DELETES IT. IT DOES SO BY REPLACING IT WITH THE LAST + * ELEMENT IN THE BUCKET. IF THE DELETED ELEMENT IS ALSO THE LAST + * ELEMENT THEN IT IS ONLY NECESSARY TO REMOVE THE ELEMENT + * ------------------------------------------------------------------------- */ +void +Dbacc::report_dealloc(Signal* signal, const Operationrec* opPtrP) +{ + Uint32 localKey = opPtrP->localdata[0]; + Uint32 opbits = opPtrP->m_op_bits; + Uint32 userptr= opPtrP->userptr; + Uint32 scanInd = + ((opbits & Operationrec::OP_MASK) == ZSCAN_OP) || + (opbits & Operationrec::OP_LOCK_REQ); + + if (localKey != ~(Uint32)0) + { + signal->theData[0] = fragrecptr.p->myfid; + signal->theData[1] = fragrecptr.p->myTableId; + Uint32 pageId = localKey >> MAX_TUPLES_BITS; + Uint32 pageIndex = localKey & ((1 << MAX_TUPLES_BITS) - 1); + signal->theData[2] = pageId; + signal->theData[3] = pageIndex; + signal->theData[4] = userptr; + signal->theData[5] = scanInd; + EXECUTE_DIRECT(DBLQH, GSN_TUP_DEALLOCREQ, signal, 6); + jamEntry(); + } +} + void Dbacc::commitdelete(Signal* signal) { jam(); - Uint32 localKey = operationRecPtr.p->localdata[0]; - Uint32 userptr= operationRecPtr.p->userptr; - Uint32 scanInd = operationRecPtr.p->operation == ZSCAN_OP - || operationRecPtr.p->isAccLockReq; - - signal->theData[0] = fragrecptr.p->myfid; - signal->theData[1] = fragrecptr.p->myTableId; - Uint32 pageId = localKey >> MAX_TUPLES_BITS; - Uint32 pageIndex = localKey & ((1 << MAX_TUPLES_BITS) - 1); - signal->theData[2] = pageId; - signal->theData[3] = pageIndex; - signal->theData[4] = userptr; - signal->theData[5] = scanInd; - EXECUTE_DIRECT(DBLQH, GSN_TUP_DEALLOCREQ, signal, 6); - jamEntry(); + report_dealloc(signal, operationRecPtr.p); getdirindex(signal); tlastPageindex = tgdiPageindex; @@ -3282,31 +3941,337 @@ void Dbacc::checkoverfreelist(Signal* signal) /*BE CHECKED. THE OPERATION RECORD WILL BE REMOVED FROM THE QUEUE IF IT */ /*BELONGED TO ANY ONE, OTHERWISE THE ELEMENT HEAD WILL BE UPDATED. */ /* ------------------------------------------------------------------------- */ + +/** + * + * P0 - P1 - P2 - P3 + * S0 + * S1 + * S2 + */ +void +Dbacc::abortParallelQueueOperation(Signal* signal, OperationrecPtr opPtr) +{ + jam(); + OperationrecPtr nextP; + OperationrecPtr prevP; + OperationrecPtr loPtr; + + Uint32 opbits = opPtr.p->m_op_bits; + Uint32 opstate = opbits & Operationrec::OP_STATE_MASK; + nextP.i = opPtr.p->nextParallelQue; + prevP.i = opPtr.p->prevParallelQue; + loPtr.i = opPtr.p->m_lock_owner_ptr_i; + + ndbassert(! (opbits & Operationrec::OP_LOCK_OWNER)); + ndbassert(opbits & Operationrec::OP_RUN_QUEUE); + + ptrCheckGuard(prevP, coprecsize, operationrec); + ndbassert(prevP.p->nextParallelQue == opPtr.i); + prevP.p->nextParallelQue = nextP.i; + + if (nextP.i != RNIL) + { + ptrCheckGuard(nextP, coprecsize, operationrec); + ndbassert(nextP.p->prevParallelQue == opPtr.i); + nextP.p->prevParallelQue = prevP.i; + } + else if (prevP.i != loPtr.i) + { + jam(); + ptrCheckGuard(loPtr, coprecsize, operationrec); + ndbassert(loPtr.p->m_op_bits & Operationrec::OP_LOCK_OWNER); + ndbassert(loPtr.p->m_lo_last_parallel_op_ptr_i == opPtr.i); + loPtr.p->m_lo_last_parallel_op_ptr_i = prevP.i; + prevP.p->m_lock_owner_ptr_i = loPtr.i; + + /** + * Abort P3...check start next + */ + startNext(signal, prevP); + validate_lock_queue(prevP); + return; + } + else + { + jam(); + /** + * P0 - P1 + * + * Abort P1, check start next + */ + ndbassert(prevP.p->m_op_bits & Operationrec::OP_LOCK_OWNER); + prevP.p->m_lo_last_parallel_op_ptr_i = RNIL; + startNext(signal, prevP); + validate_lock_queue(prevP); + return; + } + + /** + * Abort P1/P2 + */ + if (opbits & Operationrec::OP_LOCK_MODE) + { + Uint32 nextbits = nextP.p->m_op_bits; + while ((nextbits & Operationrec::OP_LOCK_MODE) == 0) + { + ndbassert(nextbits & Operationrec::OP_ACC_LOCK_MODE); + nextbits &= ~(Uint32)Operationrec::OP_ACC_LOCK_MODE; + nextP.p->m_op_bits = nextbits; + + if (nextP.p->nextParallelQue != RNIL) + { + nextP.i = nextP.p->nextParallelQue; + ptrCheckGuard(nextP, coprecsize, operationrec); + nextbits = nextP.p->m_op_bits; + } + else + { + break; + } + } + } + + /** + * Abort P1, P2 + */ + if (opstate == Operationrec::OP_STATE_RUNNING) + { + jam(); + startNext(signal, prevP); + validate_lock_queue(prevP); + return; + } + + ndbassert(opstate == Operationrec::OP_STATE_EXECUTED || + opstate == Operationrec::OP_STATE_WAITING); + + /** + * Scan to last of run queue + */ + while (nextP.p->nextParallelQue != RNIL) + { + jam(); + nextP.i = nextP.p->nextParallelQue; + ptrCheckGuard(nextP, coprecsize, operationrec); + } + +#ifdef VM_TRACE + loPtr.i = nextP.p->m_lock_owner_ptr_i; + ptrCheckGuard(loPtr, coprecsize, operationrec); + ndbassert(loPtr.p->m_op_bits & Operationrec::OP_LOCK_OWNER); + ndbassert(loPtr.p->m_lo_last_parallel_op_ptr_i == nextP.i); +#endif + startNext(signal, nextP); + validate_lock_queue(nextP); + + return; +} + +void +Dbacc::abortSerieQueueOperation(Signal* signal, OperationrecPtr opPtr) +{ + jam(); + OperationrecPtr prevS, nextS; + OperationrecPtr prevP, nextP; + OperationrecPtr loPtr; + + Uint32 opbits = opPtr.p->m_op_bits; + + prevS.i = opPtr.p->prevSerialQue; + nextS.i = opPtr.p->nextSerialQue; + + prevP.i = opPtr.p->prevParallelQue; + nextP.i = opPtr.p->nextParallelQue; + + ndbassert((opbits & Operationrec::OP_LOCK_OWNER) == 0); + ndbassert((opbits & Operationrec::OP_RUN_QUEUE) == 0); + + if (prevP.i != RNIL) + { + /** + * We're not list head... + */ + ptrCheckGuard(prevP, coprecsize, operationrec); + ndbassert(prevP.p->nextParallelQue == opPtr.i); + prevP.p->nextParallelQue = nextP.i; + + if (nextP.i != RNIL) + { + ptrCheckGuard(nextP, coprecsize, operationrec); + ndbassert(nextP.p->prevParallelQue == opPtr.i); + ndbassert((nextP.p->m_op_bits & Operationrec::OP_STATE_MASK) == + Operationrec::OP_STATE_WAITING); + nextP.p->prevParallelQue = prevP.i; + + if ((prevP.p->m_op_bits & Operationrec::OP_ACC_LOCK_MODE) == 0 && + opbits & Operationrec::OP_LOCK_MODE) + { + /** + * Scan right in parallel queue to fix OP_ACC_LOCK_MODE + */ + while ((nextP.p->m_op_bits & Operationrec::OP_LOCK_MODE) == 0) + { + ndbassert(nextP.p->m_op_bits & Operationrec::OP_ACC_LOCK_MODE); + nextP.p->m_op_bits &= ~(Uint32)Operationrec::OP_ACC_LOCK_MODE; + nextP.i = nextP.p->nextParallelQue; + if (nextP.i == RNIL) + break; + ptrCheckGuard(nextP, coprecsize, operationrec); + } + } + } + validate_lock_queue(prevP); + return; + } + else + { + /** + * We're a list head + */ + ptrCheckGuard(prevS, coprecsize, operationrec); + ndbassert(prevS.p->nextSerialQue == opPtr.i); + + if (nextP.i != RNIL) + { + /** + * Promote nextP to list head + */ + ptrCheckGuard(nextP, coprecsize, operationrec); + ndbassert(nextP.p->prevParallelQue == opPtr.i); + prevS.p->nextSerialQue = nextP.i; + nextP.p->prevParallelQue = RNIL; + nextP.p->nextSerialQue = nextS.i; + if (nextS.i != RNIL) + { + jam(); + ptrCheckGuard(nextS, coprecsize, operationrec); + ndbassert(nextS.p->prevSerialQue == opPtr.i); + nextS.p->prevSerialQue = nextP.i; + validate_lock_queue(prevS); + return; + } + else + { + // nextS is RNIL, i.e we're last in serie queue... + // we must update lockOwner.m_lo_last_serial_op_ptr_i + loPtr = prevS; + while ((loPtr.p->m_op_bits & Operationrec::OP_LOCK_OWNER) == 0) + { + loPtr.i = loPtr.p->prevSerialQue; + ptrCheckGuard(loPtr, coprecsize, operationrec); + } + ndbassert(loPtr.p->m_lo_last_serial_op_ptr_i == opPtr.i); + loPtr.p->m_lo_last_serial_op_ptr_i = nextP.i; + validate_lock_queue(loPtr); + return; + } + } + + if (nextS.i == RNIL) + { + /** + * Abort S2 + */ + + // nextS is RNIL, i.e we're last in serie queue... + // and we have no parallel queue, + // we must update lockOwner.m_lo_last_serial_op_ptr_i + prevS.p->nextSerialQue = RNIL; + + loPtr = prevS; + while ((loPtr.p->m_op_bits & Operationrec::OP_LOCK_OWNER) == 0) + { + loPtr.i = loPtr.p->prevSerialQue; + ptrCheckGuard(loPtr, coprecsize, operationrec); + } + ndbassert(loPtr.p->m_lo_last_serial_op_ptr_i == opPtr.i); + if (prevS.i != loPtr.i) + { + jam(); + loPtr.p->m_lo_last_serial_op_ptr_i = prevS.i; + } + else + { + loPtr.p->m_lo_last_serial_op_ptr_i = RNIL; + } + validate_lock_queue(loPtr); + } + else if (nextP.i == RNIL) + { + ptrCheckGuard(nextS, coprecsize, operationrec); + ndbassert(nextS.p->prevSerialQue == opPtr.i); + prevS.p->nextSerialQue = nextS.i; + nextS.p->prevSerialQue = prevS.i; + + if (prevS.p->m_op_bits & Operationrec::OP_LOCK_OWNER) + { + /** + * Abort S0 + */ + OperationrecPtr lastOp; + lastOp.i = prevS.p->m_lo_last_parallel_op_ptr_i; + if (lastOp.i != RNIL) + { + jam(); + ptrCheckGuard(lastOp, coprecsize, operationrec); + ndbassert(lastOp.p->m_lock_owner_ptr_i = prevS.i); + } + else + { + jam(); + lastOp = prevS; + } + startNext(signal, lastOp); + validate_lock_queue(lastOp); + } + else + { + validate_lock_queue(prevS); + } + } + } +} + + void Dbacc::abortOperation(Signal* signal) { - OperationrecPtr aboOperRecPtr; - OperationrecPtr TaboOperRecPtr; - Page8Ptr aboPageidptr; - Uint32 taboElementptr; - Uint32 tmp2Olq; + Uint32 opbits = operationRecPtr.p->m_op_bits; + + validate_lock_queue(operationRecPtr); - if (operationRecPtr.p->lockOwner == ZTRUE) { + if (opbits & Operationrec::OP_LOCK_OWNER) + { takeOutLockOwnersList(signal, operationRecPtr); - if (operationRecPtr.p->insertIsDone == ZTRUE) { + opbits &= ~(Uint32)Operationrec::OP_LOCK_OWNER; + if (opbits & Operationrec::OP_INSERT_IS_DONE) + { jam(); - operationRecPtr.p->elementIsDisappeared = ZTRUE; + opbits |= Operationrec::OP_ELEMENT_DISAPPEARED; }//if - if ((operationRecPtr.p->nextParallelQue != RNIL) || - (operationRecPtr.p->nextSerialQue != RNIL)) { + operationRecPtr.p->m_op_bits = opbits; + const bool queue = (operationRecPtr.p->nextParallelQue != RNIL || + operationRecPtr.p->nextSerialQue != RNIL); + + if (queue) + { jam(); - releaselock(signal); - } else { - /* --------------------------------------------------------------------------------- */ - /* WE ARE OWNER OF THE LOCK AND NO OTHER OPERATIONS ARE QUEUED. IF INSERT OR STANDBY */ - /* WE DELETE THE ELEMENT OTHERWISE WE REMOVE THE LOCK FROM THE ELEMENT. */ - /* --------------------------------------------------------------------------------- */ - if (operationRecPtr.p->elementIsDisappeared == ZFALSE) { + release_lockowner(signal, operationRecPtr, false); + } + else + { + /* ------------------------------------------------------------------- + * WE ARE OWNER OF THE LOCK AND NO OTHER OPERATIONS ARE QUEUED. + * IF INSERT OR STANDBY WE DELETE THE ELEMENT OTHERWISE WE REMOVE + * THE LOCK FROM THE ELEMENT. + * ------------------------------------------------------------------ */ + if ((opbits & Operationrec::OP_ELEMENT_DISAPPEARED) == 0) + { jam(); + Page8Ptr aboPageidptr; + Uint32 taboElementptr; + Uint32 tmp2Olq; + taboElementptr = operationRecPtr.p->elementPointer; aboPageidptr.i = operationRecPtr.p->elementPage; tmp2Olq = ElementHeader::setUnlocked(operationRecPtr.p->hashvaluePart, @@ -3316,87 +4281,31 @@ void Dbacc::abortOperation(Signal* signal) arrGuard(taboElementptr, 2048); aboPageidptr.p->word32[taboElementptr] = tmp2Olq; return; - } else { + } + else + { jam(); commitdelete(signal); }//if }//if - } else { - /* --------------------------------------------------------------- */ - // We are not the lock owner. - /* --------------------------------------------------------------- */ - jam(); - if (operationRecPtr.p->prevParallelQue != RNIL) { - jam(); - /* ---------------------------------------------------------------------------------- */ - /* SINCE WE ARE NOT QUEUE LEADER WE NEED NOT CONSIDER IF THE ELEMENT IS TO BE DELETED.*/ - /* We will simply remove it from the parallel list without any other rearrangements. */ - /* ---------------------------------------------------------------------------------- */ - aboOperRecPtr.i = operationRecPtr.p->prevParallelQue; - ptrCheckGuard(aboOperRecPtr, coprecsize, operationrec); - aboOperRecPtr.p->nextParallelQue = operationRecPtr.p->nextParallelQue; - if (operationRecPtr.p->nextParallelQue != RNIL) { - jam(); - aboOperRecPtr.i = operationRecPtr.p->nextParallelQue; - ptrCheckGuard(aboOperRecPtr, coprecsize, operationrec); - aboOperRecPtr.p->prevParallelQue = operationRecPtr.p->prevParallelQue; - }//if - } else if (operationRecPtr.p->prevSerialQue != RNIL) { - /* ------------------------------------------------------------------------- */ - // We are not in the parallel queue owning the lock. Thus we are in another parallel - // queue longer down in the serial queue. We are however first since prevParallelQue - // == RNIL. - /* ------------------------------------------------------------------------- */ - if (operationRecPtr.p->nextParallelQue != RNIL) { - jam(); - /* ------------------------------------------------------------------------- */ - // We have an operation in the queue after us. We simply rearrange this parallel queue. - // The new leader of this parallel queue will be operation in the serial queue. - /* ------------------------------------------------------------------------- */ - aboOperRecPtr.i = operationRecPtr.p->nextParallelQue; - ptrCheckGuard(aboOperRecPtr, coprecsize, operationrec); - aboOperRecPtr.p->nextSerialQue = operationRecPtr.p->nextSerialQue; - aboOperRecPtr.p->prevSerialQue = operationRecPtr.p->prevSerialQue; - aboOperRecPtr.p->prevParallelQue = RNIL; // Queue Leader - if (operationRecPtr.p->nextSerialQue != RNIL) { - jam(); - TaboOperRecPtr.i = operationRecPtr.p->nextSerialQue; - ptrCheckGuard(TaboOperRecPtr, coprecsize, operationrec); - TaboOperRecPtr.p->prevSerialQue = aboOperRecPtr.i; - }//if - TaboOperRecPtr.i = operationRecPtr.p->prevSerialQue; - ptrCheckGuard(TaboOperRecPtr, coprecsize, operationrec); - TaboOperRecPtr.p->nextSerialQue = aboOperRecPtr.i; - } else { - jam(); - /* ------------------------------------------------------------------------- */ - // We are the only operation in this parallel queue. We will thus shrink the serial - // queue. - /* ------------------------------------------------------------------------- */ - aboOperRecPtr.i = operationRecPtr.p->prevSerialQue; - ptrCheckGuard(aboOperRecPtr, coprecsize, operationrec); - aboOperRecPtr.p->nextSerialQue = operationRecPtr.p->nextSerialQue; - if (operationRecPtr.p->nextSerialQue != RNIL) { - jam(); - aboOperRecPtr.i = operationRecPtr.p->nextSerialQue; - ptrCheckGuard(aboOperRecPtr, coprecsize, operationrec); - aboOperRecPtr.p->prevSerialQue = operationRecPtr.p->prevSerialQue; - }//if - }//if - }//if - }//if - /* ------------------------------------------------------------------------- */ - // If prevParallelQue = RNIL and prevSerialQue = RNIL and we are not owner of the - // lock then we cannot be in any lock queue at all. - /* ------------------------------------------------------------------------- */ -}//Dbacc::abortOperation() + } + else if (opbits & Operationrec::OP_RUN_QUEUE) + { + abortParallelQueueOperation(signal, operationRecPtr); + } + else + { + abortSerieQueueOperation(signal, operationRecPtr); + } +} -void Dbacc::commitDeleteCheck() +void +Dbacc::commitDeleteCheck() { OperationrecPtr opPtr; OperationrecPtr lastOpPtr; OperationrecPtr deleteOpPtr; - bool elementDeleted = false; + Uint32 elementDeleted = 0; bool deleteCheckOngoing = true; Uint32 hashValue = 0; lastOpPtr = operationRecPtr; @@ -3409,7 +4318,9 @@ void Dbacc::commitDeleteCheck() }//while deleteOpPtr = lastOpPtr; do { - if (deleteOpPtr.p->operation == ZDELETE) { + Uint32 opbits = deleteOpPtr.p->m_op_bits; + Uint32 op = opbits & Operationrec::OP_MASK; + if (op == ZDELETE) { jam(); /* ------------------------------------------------------------------- * IF THE CURRENT OPERATION TO BE COMMITTED IS A DELETE OPERATION DUE TO @@ -3423,10 +4334,9 @@ void Dbacc::commitDeleteCheck() * DELETE-OPERATION THAT HAS A HASH VALUE. * ----------------------------------------------------------------- */ hashValue = deleteOpPtr.p->hashValue; - elementDeleted = true; + elementDeleted = Operationrec::OP_ELEMENT_DISAPPEARED; deleteCheckOngoing = false; - } else if ((deleteOpPtr.p->operation == ZREAD) || - (deleteOpPtr.p->operation == ZSCAN_OP)) { + } else if (op == ZREAD || op == ZSCAN_OP) { /* ------------------------------------------------------------------- * We are trying to find out whether the commit will in the end delete * the tuple. Normally the delete will be the last operation in the @@ -3437,7 +4347,7 @@ void Dbacc::commitDeleteCheck() * we have to continue scanning the list looking for a delete operation. */ deleteOpPtr.i = deleteOpPtr.p->prevParallelQue; - if (deleteOpPtr.i == RNIL) { + if (opbits & Operationrec::OP_LOCK_OWNER) { jam(); deleteCheckOngoing = false; } else { @@ -3456,14 +4366,14 @@ void Dbacc::commitDeleteCheck() opPtr = lastOpPtr; do { jam(); - opPtr.p->commitDeleteCheckFlag = ZTRUE; + opPtr.p->m_op_bits |= Operationrec::OP_COMMIT_DELETE_CHECK; if (elementDeleted) { jam(); - opPtr.p->elementIsDisappeared = ZTRUE; + opPtr.p->m_op_bits |= elementDeleted; opPtr.p->hashValue = hashValue; }//if opPtr.i = opPtr.p->prevParallelQue; - if (opPtr.i == RNIL) { + if (opPtr.p->m_op_bits & Operationrec::OP_LOCK_OWNER) { jam(); break; }//if @@ -3479,14 +4389,13 @@ void Dbacc::commitDeleteCheck() /* ------------------------------------------------------------------------- */ void Dbacc::commitOperation(Signal* signal) { - OperationrecPtr tolqTmpPtr; - Page8Ptr coPageidptr; - Uint32 tcoElementptr; - Uint32 tmp2Olq; + validate_lock_queue(operationRecPtr); - if ((operationRecPtr.p->commitDeleteCheckFlag == ZFALSE) && - (operationRecPtr.p->operation != ZSCAN_OP) && - (operationRecPtr.p->operation != ZREAD)) { + Uint32 opbits = operationRecPtr.p->m_op_bits; + Uint32 op = opbits & Operationrec::OP_MASK; + ndbrequire((opbits & Operationrec::OP_STATE_MASK) == Operationrec::OP_STATE_EXECUTED); + if ((opbits & Operationrec::OP_COMMIT_DELETE_CHECK) == 0 && (op != ZREAD)) + { jam(); /* This method is used to check whether the end result of the transaction will be to delete the tuple. In this case all operation will be marked @@ -3498,16 +4407,30 @@ void Dbacc::commitOperation(Signal* signal) lock is released. */ commitDeleteCheck(); + opbits = operationRecPtr.p->m_op_bits; }//if - if (operationRecPtr.p->lockOwner == ZTRUE) { + + ndbassert(opbits & Operationrec::OP_RUN_QUEUE); + + if (opbits & Operationrec::OP_LOCK_OWNER) + { takeOutLockOwnersList(signal, operationRecPtr); - if ((operationRecPtr.p->nextParallelQue == RNIL) && - (operationRecPtr.p->nextSerialQue == RNIL) && - (operationRecPtr.p->elementIsDisappeared == ZFALSE)) { + opbits &= ~(Uint32)Operationrec::OP_LOCK_OWNER; + operationRecPtr.p->m_op_bits = opbits; + + const bool queue = (operationRecPtr.p->nextParallelQue != RNIL || + operationRecPtr.p->nextSerialQue != RNIL); + + if (!queue && (opbits & Operationrec::OP_ELEMENT_DISAPPEARED) == 0) + { /* - This is the normal path through the commit for operations owning the - lock without any queues and not a delete operation. - */ + * This is the normal path through the commit for operations owning the + * lock without any queues and not a delete operation. + */ + Page8Ptr coPageidptr; + Uint32 tcoElementptr; + Uint32 tmp2Olq; + coPageidptr.i = operationRecPtr.p->elementPage; tcoElementptr = operationRecPtr.p->elementPointer; tmp2Olq = ElementHeader::setUnlocked(operationRecPtr.p->hashvaluePart, @@ -3517,445 +4440,439 @@ void Dbacc::commitOperation(Signal* signal) arrGuard(tcoElementptr, 2048); coPageidptr.p->word32[tcoElementptr] = tmp2Olq; return; - } else if ((operationRecPtr.p->nextParallelQue != RNIL) || - (operationRecPtr.p->nextSerialQue != RNIL)) { + } + else if (queue) + { jam(); /* - The case when there is a queue lined up. - Release the lock and pass it to the next operation lined up. - */ - releaselock(signal); + * The case when there is a queue lined up. + * Release the lock and pass it to the next operation lined up. + */ + release_lockowner(signal, operationRecPtr, true); return; - } else { + } + else + { jam(); /* - No queue and elementIsDisappeared is true. We perform the actual delete - operation. - */ + * No queue and elementIsDisappeared is true. + * We perform the actual delete operation. + */ commitdelete(signal); return; }//if - } else { - /* - THE OPERATION DOES NOT OWN THE LOCK. IT MUST BE IN A LOCK QUEUE OF THE - ELEMENT. - */ - ndbrequire(operationRecPtr.p->prevParallelQue != RNIL); + } + else + { + /** + * THE OPERATION DOES NOT OWN THE LOCK. IT MUST BE IN A LOCK QUEUE OF THE + * ELEMENT. + */ jam(); - tolqTmpPtr.i = operationRecPtr.p->prevParallelQue; - ptrCheckGuard(tolqTmpPtr, coprecsize, operationrec); - tolqTmpPtr.p->nextParallelQue = operationRecPtr.p->nextParallelQue; - if (operationRecPtr.p->nextParallelQue != RNIL) { + OperationrecPtr prev, next, lockOwner; + prev.i = operationRecPtr.p->prevParallelQue; + next.i = operationRecPtr.p->nextParallelQue; + lockOwner.i = operationRecPtr.p->m_lock_owner_ptr_i; + ptrCheckGuard(prev, coprecsize, operationrec); + + prev.p->nextParallelQue = next.i; + if (next.i != RNIL) + { jam(); - tolqTmpPtr.i = operationRecPtr.p->nextParallelQue; - ptrCheckGuard(tolqTmpPtr, coprecsize, operationrec); - tolqTmpPtr.p->prevParallelQue = operationRecPtr.p->prevParallelQue; - }//if - + ptrCheckGuard(next, coprecsize, operationrec); + next.p->prevParallelQue = prev.i; + } + else if (prev.p->m_op_bits & Operationrec::OP_LOCK_OWNER) + { + jam(); + ndbassert(lockOwner.i == prev.i); + prev.p->m_lo_last_parallel_op_ptr_i = RNIL; + next = prev; + } + else + { + jam(); + /** + * Last operation in parallell queue + */ + ndbassert(prev.i != lockOwner.i); + ptrCheckGuard(lockOwner, coprecsize, operationrec); + ndbassert(lockOwner.p->m_op_bits & Operationrec::OP_LOCK_OWNER); + lockOwner.p->m_lo_last_parallel_op_ptr_i = prev.i; + prev.p->m_lock_owner_ptr_i = lockOwner.i; + next = prev; + } + /** * Check possible lock upgrade - * 1) Find lock owner - * 2) Count transactions in parallel que - * 3) If count == 1 and TRANSID(next serial) == TRANSID(lock owner) - * upgrade next serial */ - if(operationRecPtr.p->lockMode) + if(opbits & Operationrec::OP_ACC_LOCK_MODE) { jam(); + /** - * Committing a non shared operation can't lead to lock upgrade + * Not lock owner...committing a exclusive operation... + * + * e.g + * T1(R) T1(X) + * T2(R/X) + * + * If T1(X) commits T2(R/X) is not supposed to run + * as T1(R) should also commit + * + * e.g + * T1(R) T1(X) T1*(R) + * T2(R/X) + * + * If T1*(R) commits T2(R/X) is not supposed to run + * as T1(R),T2(x) should also commit */ + validate_lock_queue(prev); return; } - - OperationrecPtr lock_owner; - lock_owner.i = operationRecPtr.p->prevParallelQue; - ptrCheckGuard(lock_owner, coprecsize, operationrec); - Uint32 transid[2] = { lock_owner.p->transId1, - lock_owner.p->transId2 }; - - - while(lock_owner.p->prevParallelQue != RNIL) + + /** + * We committed a shared lock + * Check if we can start next... + */ + while(next.p->nextParallelQue != RNIL) { - lock_owner.i = lock_owner.p->prevParallelQue; - ptrCheckGuard(lock_owner, coprecsize, operationrec); + jam(); + next.i = next.p->nextParallelQue; + ptrCheckGuard(next, coprecsize, operationrec); - if(lock_owner.p->transId1 != transid[0] || - lock_owner.p->transId2 != transid[1]) + if ((next.p->m_op_bits & Operationrec::OP_STATE_MASK) != + Operationrec::OP_STATE_EXECUTED) { jam(); - /** - * If more than 1 trans in lock queue -> no lock upgrade - */ return; } } - check_lock_upgrade(signal, lock_owner, operationRecPtr); + startNext(signal, next); + + validate_lock_queue(prev); } }//Dbacc::commitOperation() -void -Dbacc::check_lock_upgrade(Signal* signal, - OperationrecPtr lock_owner, - OperationrecPtr release_op) -{ - if((lock_owner.p->transId1 == release_op.p->transId1 && - lock_owner.p->transId2 == release_op.p->transId2) || - release_op.p->lockMode || - lock_owner.p->nextSerialQue == RNIL) - { - jam(); - /** - * No lock upgrade if same trans or lock owner has no serial queue - * or releasing non shared op - */ - return; - } - - OperationrecPtr next; - next.i = lock_owner.p->nextSerialQue; - ptrCheckGuard(next, coprecsize, operationrec); +void +Dbacc::release_lockowner(Signal* signal, OperationrecPtr opPtr, bool commit) +{ + OperationrecPtr nextP; + OperationrecPtr nextS; + OperationrecPtr newOwner; + OperationrecPtr lastP; - if(lock_owner.p->transId1 != next.p->transId1 || - lock_owner.p->transId2 != next.p->transId2) + Uint32 opbits = opPtr.p->m_op_bits; + nextP.i = opPtr.p->nextParallelQue; + nextS.i = opPtr.p->nextSerialQue; + lastP.i = opPtr.p->m_lo_last_parallel_op_ptr_i; + Uint32 lastS = opPtr.p->m_lo_last_serial_op_ptr_i; + + ndbassert(lastP.i != RNIL || lastS != RNIL); + ndbassert(nextP.i != RNIL || nextS.i != RNIL); + + enum { + NOTHING, + CHECK_LOCK_UPGRADE, + START_NEW + } action = NOTHING; + + if (nextP.i != RNIL) { jam(); - /** - * No lock upgrad if !same trans in serial queue - */ - return; + ptrCheckGuard(nextP, coprecsize, operationrec); + newOwner = nextP; + + if (lastP.i == newOwner.i) + { + newOwner.p->m_lo_last_parallel_op_ptr_i = RNIL; + lastP = nextP; + } + else + { + ptrCheckGuard(lastP, coprecsize, operationrec); + newOwner.p->m_lo_last_parallel_op_ptr_i = lastP.i; + lastP.p->m_lock_owner_ptr_i = newOwner.i; + } + + newOwner.p->m_lo_last_serial_op_ptr_i = lastS; + newOwner.p->nextSerialQue = nextS.i; + + if (nextS.i != RNIL) + { + jam(); + ptrCheckGuard(nextS, coprecsize, operationrec); + ndbassert(nextS.p->prevSerialQue == opPtr.i); + nextS.p->prevSerialQue = newOwner.i; + } + + if (commit) + { + if ((opbits & Operationrec::OP_ACC_LOCK_MODE) == ZREADLOCK) + { + jam(); + /** + * Lock owner...committing a shared operation... + * this can be a lock upgrade + * + * e.g + * T1(R) T2(R) + * T2(X) + * + * If T1(R) commits T2(X) is supposed to run + * + * e.g + * T1(X) T1(R) + * T2(R) + * + * If T1(X) commits, then T1(R) _should_ commit before T2(R) is + * allowed to proceed + */ + action = CHECK_LOCK_UPGRADE; + } + else + { + jam(); + newOwner.p->m_op_bits |= Operationrec::OP_LOCK_MODE; + } + } + else + { + /** + * Aborting an operation can *always* lead to lock upgrade + */ + action = CHECK_LOCK_UPGRADE; + Uint32 opstate = opbits & Operationrec::OP_STATE_MASK; + if (opstate != Operationrec::OP_STATE_EXECUTED) + { + ndbassert(opstate == Operationrec::OP_STATE_RUNNING); + if (opbits & Operationrec::OP_ELEMENT_DISAPPEARED) + { + jam(); + report_dealloc(signal, opPtr.p); + newOwner.p->localdata[0] = ~(Uint32)0; + } + else + { + jam(); + newOwner.p->localdata[0] = opPtr.p->localdata[0]; + newOwner.p->localdata[1] = opPtr.p->localdata[1]; + } + action = START_NEW; + } + + /** + * Update ACC_LOCK_MODE + */ + if (opbits & Operationrec::OP_LOCK_MODE) + { + Uint32 nextbits = nextP.p->m_op_bits; + while ((nextbits & Operationrec::OP_LOCK_MODE) == 0) + { + ndbassert(nextbits & Operationrec::OP_ACC_LOCK_MODE); + nextbits &= ~(Uint32)Operationrec::OP_ACC_LOCK_MODE; + nextP.p->m_op_bits = nextbits; + + if (nextP.p->nextParallelQue != RNIL) + { + nextP.i = nextP.p->nextParallelQue; + ptrCheckGuard(nextP, coprecsize, operationrec); + nextbits = nextP.p->m_op_bits; + } + else + { + break; + } + } + } + } } - - if (getNoParallelTransaction(lock_owner.p) > 1) + else { jam(); - /** - * No lock upgrade if more than 1 transaction in parallell queue - */ - return; - } + ptrCheckGuard(nextS, coprecsize, operationrec); + newOwner = nextS; + + newOwner.p->m_op_bits |= Operationrec::OP_RUN_QUEUE; + + if (opbits & Operationrec::OP_ELEMENT_DISAPPEARED) + { + report_dealloc(signal, opPtr.p); + newOwner.p->localdata[0] = ~(Uint32)0; + } + else + { + jam(); + newOwner.p->localdata[0] = opPtr.p->localdata[0]; + newOwner.p->localdata[1] = opPtr.p->localdata[1]; + } + + lastP = newOwner; + while (lastP.p->nextParallelQue != RNIL) + { + lastP.i = lastP.p->nextParallelQue; + ptrCheckGuard(lastP, coprecsize, operationrec); + lastP.p->m_op_bits |= Operationrec::OP_RUN_QUEUE; + } + + if (newOwner.i != lastP.i) + { + jam(); + newOwner.p->m_lo_last_parallel_op_ptr_i = lastP.i; + } + else + { + jam(); + newOwner.p->m_lo_last_parallel_op_ptr_i = RNIL; + } - if (getNoParallelTransaction(next.p) > 1) - { - jam(); - /** - * No lock upgrade if more than 1 transaction in next's parallell queue - */ - return; + if (newOwner.i != lastS) + { + jam(); + newOwner.p->m_lo_last_serial_op_ptr_i = lastS; + } + else + { + jam(); + newOwner.p->m_lo_last_serial_op_ptr_i = RNIL; + } + + action = START_NEW; } - OperationrecPtr tmp; - tmp.i = lock_owner.p->nextSerialQue = next.p->nextSerialQue; - if(tmp.i != RNIL) - { - ptrCheckGuard(tmp, coprecsize, operationrec); - ndbassert(tmp.p->prevSerialQue == next.i); - tmp.p->prevSerialQue = lock_owner.i; - } - next.p->nextSerialQue = next.p->prevSerialQue = RNIL; + insertLockOwnersList(signal, newOwner); - // Find end of parallell que - tmp = lock_owner; - Uint32 lockMode = next.p->lockMode > lock_owner.p->lockMode ? - next.p->lockMode : lock_owner.p->lockMode; - while(tmp.p->nextParallelQue != RNIL) + /** + * Copy op info, and store op in element + * + */ { - jam(); - tmp.i = tmp.p->nextParallelQue; - tmp.p->lockMode = lockMode; - ptrCheckGuard(tmp, coprecsize, operationrec); - } - tmp.p->lockMode = lockMode; - - next.p->prevParallelQue = tmp.i; - tmp.p->nextParallelQue = next.i; - - OperationrecPtr save = operationRecPtr; - - Uint32 localdata[2]; - localdata[0] = lock_owner.p->localdata[0]; - localdata[1] = lock_owner.p->localdata[1]; - do { - next.p->localdata[0] = localdata[0]; - next.p->localdata[1] = localdata[1]; - next.p->lockMode = lockMode; - - operationRecPtr = next; - executeNextOperation(signal); - if (next.p->nextParallelQue != RNIL) + newOwner.p->elementPage = opPtr.p->elementPage; + newOwner.p->elementIsforward = opPtr.p->elementIsforward; + newOwner.p->elementPointer = opPtr.p->elementPointer; + newOwner.p->elementContainer = opPtr.p->elementContainer; + newOwner.p->scanBits = opPtr.p->scanBits; + newOwner.p->hashvaluePart = opPtr.p->hashvaluePart; + newOwner.p->m_op_bits |= (opbits & Operationrec::OP_ELEMENT_DISAPPEARED); + if (opbits & Operationrec::OP_ELEMENT_DISAPPEARED) { + /* ------------------------------------------------------------------- */ + // If the elementIsDisappeared is set then we know that the + // hashValue is also set since it always originates from a + // committing abort or a aborting insert. + // Scans do not initialise the hashValue and must have this + // value initialised if they are + // to successfully commit the delete. + /* ------------------------------------------------------------------- */ jam(); - next.i = next.p->nextParallelQue; - ptrCheckGuard(next, coprecsize, operationrec); - } else { - jam(); - break; + newOwner.p->hashValue = opPtr.p->hashValue; }//if - } while (1); + + Page8Ptr pagePtr; + pagePtr.i = newOwner.p->elementPage; + ptrCheckGuard(pagePtr, cpagesize, page8); + const Uint32 tmp = ElementHeader::setLocked(newOwner.i); + arrGuard(newOwner.p->elementPointer, 2048); + pagePtr.p->word32[newOwner.p->elementPointer] = tmp; + } - operationRecPtr = save; + switch(action){ + case NOTHING: + validate_lock_queue(newOwner); + return; + case START_NEW: + startNew(signal, newOwner); + validate_lock_queue(newOwner); + return; + case CHECK_LOCK_UPGRADE: + startNext(signal, lastP); + validate_lock_queue(lastP); + break; + } } -/* ------------------------------------------------------------------------- */ -/* RELEASELOCK */ -/* RESETS LOCK OF AN ELEMENT. */ -/* INFORMATION ABOUT THE ELEMENT IS SAVED IN THE OPERATION RECORD */ -/* THESE INFORMATION IS USED TO UPDATE HEADER OF THE ELEMENT */ -/* ------------------------------------------------------------------------- */ -void Dbacc::releaselock(Signal* signal) -{ - OperationrecPtr rloOperPtr; - OperationrecPtr trlOperPtr; - OperationrecPtr trlTmpOperPtr; - Uint32 TelementIsDisappeared; - - trlOperPtr.i = RNIL; - if (operationRecPtr.p->nextParallelQue != RNIL) { - jam(); - /** --------------------------------------------------------------------- - * NEXT OPERATION TAKES OVER THE LOCK. - * We will simply move the info from the leader - * to the new queue leader. - * -------------------------------------------------------------------- */ - trlOperPtr.i = operationRecPtr.p->nextParallelQue; - ptrCheckGuard(trlOperPtr, coprecsize, operationrec); - copyInOperPtr = trlOperPtr; - copyOperPtr = operationRecPtr; - copyOpInfo(signal); - trlOperPtr.p->prevParallelQue = RNIL; - if (operationRecPtr.p->nextSerialQue != RNIL) { - jam(); - /* ----------------------------------------------------------------- - * THERE IS A SERIAL QUEUE. MOVE IT FROM RELEASED OP REC TO THE - * NEW LOCK OWNER. - * ------------------------------------------------------------------ */ - trlOperPtr.p->nextSerialQue = operationRecPtr.p->nextSerialQue; - trlTmpOperPtr.i = trlOperPtr.p->nextSerialQue; - ptrCheckGuard(trlTmpOperPtr, coprecsize, operationrec); - trlTmpOperPtr.p->prevSerialQue = trlOperPtr.i; - }//if - - check_lock_upgrade(signal, copyInOperPtr, operationRecPtr); - } else { - ndbrequire(operationRecPtr.p->nextSerialQue != RNIL); - jam(); - /** --------------------------------------------------------------------- - * THE PARALLEL QUEUE IS EMPTY AND THE SERIAL QUEUE IS NOT EMPTY. - * WE NEED TO REARRANGE LISTS AND START A NUMBER OF OPERATIONS. - * -------------------------------------------------------------------- */ - trlOperPtr.i = operationRecPtr.p->nextSerialQue; - ptrCheckGuard(trlOperPtr, coprecsize, operationrec); - copyOperPtr = operationRecPtr; - copyInOperPtr = trlOperPtr; - copyOpInfo(signal); - trlOperPtr.p->prevSerialQue = RNIL; - ndbrequire(trlOperPtr.p->prevParallelQue == RNIL); - /* --------------------------------------------------------------------- */ - /* WE HAVE MOVED TO THE NEXT PARALLEL QUEUE. WE MUST START ALL OF THOSE */ - /* OPERATIONS WHICH UP TILL NOW HAVE BEEN QUEUED WAITING FOR THE LOCK. */ - /* --------------------------------------------------------------------- */ - rloOperPtr = operationRecPtr; - trlTmpOperPtr = trlOperPtr; - TelementIsDisappeared = trlOperPtr.p->elementIsDisappeared; - Uint32 ThashValue = trlOperPtr.p->hashValue; - do { - /* ------------------------------------------------------------------ */ - // Ensure that all operations in the queue are assigned with the - // elementIsDisappeared to ensure that the element is removed after - // a previous delete. An insert does however revert this decision - // since the element is put back again. - // Local checkpoints complicate life here since they do not - // execute the next operation but simply change - // the state on the operation. - // We need to set-up the variable elementIsDisappeared - // properly even when local checkpoints and inserts/writes after - // deletes occur. - /* ------------------------------------------------------------------- */ - trlTmpOperPtr.p->elementIsDisappeared = TelementIsDisappeared; - if (TelementIsDisappeared == ZTRUE) { - /* ----------------------------------------------------------------- */ - // If the elementIsDisappeared is set then we know that the - // hashValue is also set since it always originates from a - // committing abort or a aborting insert. - // Scans do not initialise the hashValue and must have this - // value initialised if they are to successfully commit the delete. - /* ----------------------------------------------------------------- */ - jam(); - trlTmpOperPtr.p->hashValue = ThashValue; - }//if - trlTmpOperPtr.p->localdata[0] = trlOperPtr.p->localdata[0]; - trlTmpOperPtr.p->localdata[1] = trlOperPtr.p->localdata[1]; - /* ------------------------------------------------------------------- */ - // Restart the queued operation. - /* ------------------------------------------------------------------- */ - operationRecPtr = trlTmpOperPtr; - TelementIsDisappeared = executeNextOperation(signal); - ThashValue = operationRecPtr.p->hashValue; - if (trlTmpOperPtr.p->nextParallelQue != RNIL) { - jam(); - /* ----------------------------------------------------------------- */ - // We will continue with the next operation in the parallel - // queue and start this as well. - /* ----------------------------------------------------------------- */ - trlTmpOperPtr.i = trlTmpOperPtr.p->nextParallelQue; - ptrCheckGuard(trlTmpOperPtr, coprecsize, operationrec); - } else { - jam(); - break; - }//if - } while (1); - operationRecPtr = rloOperPtr; - }//if - - // Insert the next op into the lock owner list - insertLockOwnersList(signal, trlOperPtr); - return; -}//Dbacc::releaselock() - -/* --------------------------------------------------------------------------------- */ -/* COPY_OP_INFO */ -/* INPUT: COPY_IN_OPER_PTR AND COPY_OPER_PTR. */ -/* DESCRIPTION:INFORMATION ABOUT THE ELEMENT WILL BE MOVED FROM OPERATION */ -/* REC TO QUEUE OP REC. QUE OP REC TAKES OVER THE LOCK. */ -/* --------------------------------------------------------------------------------- */ -void Dbacc::copyOpInfo(Signal* signal) +void +Dbacc::startNew(Signal* signal, OperationrecPtr newOwner) { - Page8Ptr coiPageidptr; - - copyInOperPtr.p->elementPage = copyOperPtr.p->elementPage; - copyInOperPtr.p->elementIsforward = copyOperPtr.p->elementIsforward; - copyInOperPtr.p->elementContainer = copyOperPtr.p->elementContainer; - copyInOperPtr.p->elementPointer = copyOperPtr.p->elementPointer; - copyInOperPtr.p->scanBits = copyOperPtr.p->scanBits; - copyInOperPtr.p->hashvaluePart = copyOperPtr.p->hashvaluePart; - copyInOperPtr.p->elementIsDisappeared = copyOperPtr.p->elementIsDisappeared; - if (copyInOperPtr.p->elementIsDisappeared == ZTRUE) { - /* --------------------------------------------------------------------------------- */ - // If the elementIsDisappeared is set then we know that the hashValue is also set - // since it always originates from a committing abort or a aborting insert. Scans - // do not initialise the hashValue and must have this value initialised if they are - // to successfully commit the delete. - /* --------------------------------------------------------------------------------- */ - jam(); - copyInOperPtr.p->hashValue = copyOperPtr.p->hashValue; - }//if - coiPageidptr.i = copyOperPtr.p->elementPage; - ptrCheckGuard(coiPageidptr, cpagesize, page8); - const Uint32 tmp = ElementHeader::setLocked(copyInOperPtr.i); - dbgWord32(coiPageidptr, copyOperPtr.p->elementPointer, tmp); - arrGuard(copyOperPtr.p->elementPointer, 2048); - coiPageidptr.p->word32[copyOperPtr.p->elementPointer] = tmp; - copyInOperPtr.p->localdata[0] = copyOperPtr.p->localdata[0]; - copyInOperPtr.p->localdata[1] = copyOperPtr.p->localdata[1]; -}//Dbacc::copyOpInfo() + OperationrecPtr save = operationRecPtr; + operationRecPtr = newOwner; + + Uint32 opbits = newOwner.p->m_op_bits; + Uint32 op = opbits & Operationrec::OP_MASK; + Uint32 opstate = (opbits & Operationrec::OP_STATE_MASK); + ndbassert(opstate == Operationrec::OP_STATE_WAITING); + ndbassert(opbits & Operationrec::OP_LOCK_OWNER); + const bool deleted = opbits & Operationrec::OP_ELEMENT_DISAPPEARED; + Uint32 errCode = 0; + + opbits &= opbits & ~(Uint32)Operationrec::OP_STATE_MASK; + opbits |= Operationrec::OP_STATE_RUNNING; + + if (op == ZSCAN_OP && (opbits & Operationrec::OP_LOCK_REQ) == 0) + goto scan; -/* ******************--------------------------------------------------------------- */ -/* EXECUTE NEXT OPERATION */ -/* NEXT OPERATION IN A LOCK QUEUE WILL BE EXECUTED. */ -/* --------------------------------------------------------------------------------- */ -Uint32 Dbacc::executeNextOperation(Signal* signal) -{ - ndbrequire(operationRecPtr.p->transactionstate == ACTIVE); - if (operationRecPtr.p->elementIsDisappeared == ZTRUE) { - /* --------------------------------------------------------------------- */ - /* PREVIOUS OPERATION WAS DELETE OPERATION AND THE ELEMENT IS DELETED. */ - /* --------------------------------------------------------------------- */ - if (((operationRecPtr.p->operation != ZINSERT) && - (operationRecPtr.p->operation != ZWRITE)) || - (operationRecPtr.p->prevParallelQue != RNIL)) { - if (operationRecPtr.p->operation != ZSCAN_OP || - operationRecPtr.p->isAccLockReq) { - jam(); - /* ----------------------------------------------------------------- */ - // Updates and reads with a previous delete simply aborts with read - // error indicating that tuple did not exist. - // Also inserts and writes not being the first operation. - /* ----------------------------------------------------------------- */ - operationRecPtr.p->transactionstate = WAIT_COMMIT_ABORT; - signal->theData[0] = operationRecPtr.p->userptr; - signal->theData[1] = ZREAD_ERROR; - sendSignal(operationRecPtr.p->userblockref, GSN_ACCKEYREF, signal, - 2, JBB); - return operationRecPtr.p->elementIsDisappeared; - } else { - /* ----------------------------------------------------------------- */ - /* ABORT OF OPERATION NEEDED BUT THE OPERATION IS A - * SCAN => SPECIAL TREATMENT. - * IF THE SCAN WAITS IN QUEUE THEN WE MUST REMOVE THE OPERATION - * FROM THE SCAN LOCK QUEUE AND IF NO MORE OPERATIONS ARE QUEUED - * THEN WE SHOULD RESTART THE SCAN PROCESS. OTHERWISE WE SIMPLY - * RELEASE THE OPERATION AND DECREASE THE NUMBER OF LOCKS HELD. - * ----------------------------------------------------------------- */ - takeOutScanLockQueue(operationRecPtr.p->scanRecPtr); - putReadyScanQueue(signal, operationRecPtr.p->scanRecPtr); - return operationRecPtr.p->elementIsDisappeared; - }//if - }//if - /* --------------------------------------------------------------------- */ - // Insert and writes can continue but need to be converted to inserts. - /* --------------------------------------------------------------------- */ - jam(); - operationRecPtr.p->elementIsDisappeared = ZFALSE; - operationRecPtr.p->operation = ZINSERT; - operationRecPtr.p->insertIsDone = ZTRUE; - } else if (operationRecPtr.p->operation == ZINSERT) { - bool abortFlag = true; - if (operationRecPtr.p->prevParallelQue != RNIL) { - OperationrecPtr prevOpPtr; - jam(); - prevOpPtr.i = operationRecPtr.p->prevParallelQue; - ptrCheckGuard(prevOpPtr, coprecsize, operationrec); - if (prevOpPtr.p->operation == ZDELETE) { - jam(); - abortFlag = false; - }//if - }//if - if (abortFlag) { - jam(); - /* ------------------------------------------------------------------- */ - /* ELEMENT STILL REMAINS AND WE ARE TRYING TO INSERT IT AGAIN. */ - /* THIS IS CLEARLY NOT A GOOD IDEA. */ - /* ------------------------------------------------------------------- */ - operationRecPtr.p->transactionstate = WAIT_COMMIT_ABORT; - signal->theData[0] = operationRecPtr.p->userptr; - signal->theData[1] = ZWRITE_ERROR; - sendSignal(operationRecPtr.p->userblockref, GSN_ACCKEYREF, signal, - 2, JBB); - return operationRecPtr.p->elementIsDisappeared; - }//if - } - else if(operationRecPtr.p->operation == ZWRITE) + if (deleted) { jam(); - operationRecPtr.p->operation = ZUPDATE; - if (operationRecPtr.p->prevParallelQue != RNIL) { - OperationrecPtr prevOpPtr; - jam(); - prevOpPtr.i = operationRecPtr.p->prevParallelQue; - ptrCheckGuard(prevOpPtr, coprecsize, operationrec); - if (prevOpPtr.p->operation == ZDELETE) - { - jam(); - operationRecPtr.p->operation = ZINSERT; - } + if (op != ZINSERT && op != ZWRITE) + { + errCode = ZREAD_ERROR; + goto ref; } + + opbits &= ~(Uint32)Operationrec::OP_MASK; + opbits &= ~(Uint32)Operationrec::OP_ELEMENT_DISAPPEARED; + opbits |= (op = ZINSERT); + opbits |= Operationrec::OP_INSERT_IS_DONE; + goto conf; } - - if (operationRecPtr.p->operation == ZSCAN_OP && - ! operationRecPtr.p->isAccLockReq) { + else if (op == ZINSERT) + { jam(); - takeOutScanLockQueue(operationRecPtr.p->scanRecPtr); - putReadyScanQueue(signal, operationRecPtr.p->scanRecPtr); - } else { + errCode = ZWRITE_ERROR; + goto ref; + } + else if (op == ZWRITE) + { jam(); - sendAcckeyconf(signal); - sendSignal(operationRecPtr.p->userblockref, GSN_ACCKEYCONF, - signal, 6, JBB); - }//if - return operationRecPtr.p->elementIsDisappeared; -}//Dbacc::executeNextOperation() + opbits &= ~(Uint32)Operationrec::OP_MASK; + opbits |= (op = ZUPDATE); + goto conf; + } + +conf: + newOwner.p->m_op_bits = opbits; + + sendAcckeyconf(signal); + sendSignal(newOwner.p->userblockref, GSN_ACCKEYCONF, + signal, 6, JBB); + + operationRecPtr = save; + return; + +scan: + jam(); + newOwner.p->m_op_bits = opbits; + + takeOutScanLockQueue(newOwner.p->scanRecPtr); + putReadyScanQueue(signal, newOwner.p->scanRecPtr); + + operationRecPtr = save; + return; + +ref: + newOwner.p->m_op_bits = opbits; + + signal->theData[0] = newOwner.p->userptr; + signal->theData[1] = errCode; + sendSignal(newOwner.p->userblockref, GSN_ACCKEYREF, signal, + 2, JBB); + + operationRecPtr = save; + return; +} /** * takeOutLockOwnersList @@ -3969,7 +4886,6 @@ void Dbacc::takeOutLockOwnersList(Signal* signal, { const Uint32 Tprev = outOperPtr.p->prevLockOwnerOp; const Uint32 Tnext = outOperPtr.p->nextLockOwnerOp; - #ifdef VM_TRACE // Check that operation is already in the list OperationrecPtr tmpOperPtr; @@ -3984,8 +4900,7 @@ void Dbacc::takeOutLockOwnersList(Signal* signal, ndbrequire(inList == true); #endif - ndbrequire(outOperPtr.p->lockOwner == ZTRUE); - outOperPtr.p->lockOwner = ZFALSE; + ndbassert(outOperPtr.p->m_op_bits & Operationrec::OP_LOCK_OWNER); // Fast path through the code for the common case. if ((Tprev == RNIL) && (Tnext == RNIL)) { @@ -4026,7 +4941,6 @@ void Dbacc::insertLockOwnersList(Signal* signal, const OperationrecPtr& insOperPtr) { OperationrecPtr tmpOperPtr; - #ifdef VM_TRACE // Check that operation is not already in list tmpOperPtr.i = fragrecptr.p->lockOwnersList; @@ -4036,18 +4950,15 @@ void Dbacc::insertLockOwnersList(Signal* signal, tmpOperPtr.i = tmpOperPtr.p->nextLockOwnerOp; } #endif + tmpOperPtr.i = fragrecptr.p->lockOwnersList; + + ndbrequire(! (insOperPtr.p->m_op_bits & Operationrec::OP_LOCK_OWNER)); - ndbrequire(insOperPtr.p->lockOwner == ZFALSE); - - insOperPtr.p->lockOwner = ZTRUE; + insOperPtr.p->m_op_bits |= Operationrec::OP_LOCK_OWNER; insOperPtr.p->prevLockOwnerOp = RNIL; - tmpOperPtr.i = fragrecptr.p->lockOwnersList; - const Uint32 seq = fragrecptr.p->m_current_sequence_no; insOperPtr.p->nextLockOwnerOp = tmpOperPtr.i; - insOperPtr.p->m_sequence_no = seq; fragrecptr.p->lockOwnersList = insOperPtr.i; - fragrecptr.p->m_current_sequence_no = seq+1; if (tmpOperPtr.i == RNIL) { return; } else { @@ -5407,6 +6318,7 @@ void Dbacc::execNEXT_SCANREQ(Signal* signal) if (!scanPtr.p->scanReadCommittedFlag) { commitOperation(signal); }//if + operationRecPtr.p->m_op_bits = Operationrec::OP_INITIAL; takeOutActiveScanOp(signal); releaseOpRec(signal); scanPtr.p->scanOpsAllocated--; @@ -5422,9 +6334,10 @@ void Dbacc::execNEXT_SCANREQ(Signal* signal) jam(); fragrecptr.i = scanPtr.p->activeLocalFrag; ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec); - /* --------------------------------------------------------------------------------- */ - /* THE SCAN PROCESS IS FINISHED. RELOCK ALL LOCKED EL. RELESE ALL INVOLVED REC. */ - /* --------------------------------------------------------------------------------- */ + /* --------------------------------------------------------------------- + * THE SCAN PROCESS IS FINISHED. RELOCK ALL LOCKED EL. + * RELESE ALL INVOLVED REC. + * ------------------------------------------------------------------- */ releaseScanLab(signal); return; break; @@ -5474,24 +6387,26 @@ void Dbacc::checkNextBucketLab(Signal* signal) scanPtr.p->nextBucketIndex++; if (scanPtr.p->scanBucketState == ScanRec::SECOND_LAP) { if (scanPtr.p->nextBucketIndex > scanPtr.p->maxBucketIndexToRescan) { - /* --------------------------------------------------------------------------------- */ - // We have finished the rescan phase. We are ready to proceed with the next fragment part. - /* --------------------------------------------------------------------------------- */ + /* ---------------------------------------------------------------- */ + // We have finished the rescan phase. + // We are ready to proceed with the next fragment part. + /* ---------------------------------------------------------------- */ jam(); checkNextFragmentLab(signal); return; }//if } else if (scanPtr.p->scanBucketState == ScanRec::FIRST_LAP) { if ((fragrecptr.p->p + fragrecptr.p->maxp) < scanPtr.p->nextBucketIndex) { - /* --------------------------------------------------------------------------------- */ + /* ---------------------------------------------------------------- */ // All buckets have been scanned a first time. - /* --------------------------------------------------------------------------------- */ + /* ---------------------------------------------------------------- */ if (scanPtr.p->minBucketIndexToRescan == 0xFFFFFFFF) { jam(); - /* --------------------------------------------------------------------------------- */ - // We have not had any merges behind the scan. Thus it is not necessary to perform - // any rescan any buckets and we can proceed immediately with the next fragment part. - /* --------------------------------------------------------------------------------- */ + /* -------------------------------------------------------------- */ + // We have not had any merges behind the scan. + // Thus it is not necessary to perform any rescan any buckets + // and we can proceed immediately with the next fragment part. + /* --------------------------------------------------------------- */ checkNextFragmentLab(signal); return; } else { @@ -5583,18 +6498,23 @@ void Dbacc::checkNextBucketLab(Signal* signal) tslElementptr = tnsElementptr; setlock(signal); insertLockOwnersList(signal, operationRecPtr); + operationRecPtr.p->m_op_bits |= + Operationrec::OP_STATE_RUNNING | Operationrec::OP_RUN_QUEUE; }//if } else { arrGuard(tnsElementptr, 2048); queOperPtr.i = ElementHeader::getOpPtrI(nsPageptr.p->word32[tnsElementptr]); ptrCheckGuard(queOperPtr, coprecsize, operationrec); - if (queOperPtr.p->elementIsDisappeared == ZTRUE) { + if (queOperPtr.p->m_op_bits & Operationrec::OP_ELEMENT_DISAPPEARED || + queOperPtr.p->localdata[0] == ~(Uint32)0) + { jam(); - /* --------------------------------------------------------------------------------- */ - // If the lock owner indicates the element is disappeared then we will not report this - // tuple. We will continue with the next tuple. - /* --------------------------------------------------------------------------------- */ + /* ------------------------------------------------------------------ */ + // If the lock owner indicates the element is disappeared then + // we will not report this tuple. We will continue with the next tuple. + /* ------------------------------------------------------------------ */ + operationRecPtr.p->m_op_bits = Operationrec::OP_INITIAL; releaseOpRec(signal); scanPtr.p->scanOpsAllocated--; signal->theData[0] = scanPtr.i; @@ -5606,31 +6526,29 @@ void Dbacc::checkNextBucketLab(Signal* signal) Uint32 return_result; if (scanPtr.p->scanLockMode == ZREADLOCK) { jam(); - priPageptr = nsPageptr; - tpriElementptr = tnsElementptr; - return_result = placeReadInLockQueue(signal); + return_result = placeReadInLockQueue(queOperPtr); } else { jam(); - pwiPageptr = nsPageptr; - tpwiElementptr = tnsElementptr; - return_result = placeWriteInLockQueue(signal); + return_result = placeWriteInLockQueue(queOperPtr); }//if if (return_result == ZSERIAL_QUEUE) { - /* --------------------------------------------------------------------------------- */ - /* WE PLACED THE OPERATION INTO A SERIAL QUEUE AND THUS WE HAVE TO WAIT FOR */ - /* THE LOCK TO BE RELEASED. WE CONTINUE WITH THE NEXT ELEMENT. */ - /* --------------------------------------------------------------------------------- */ + /* ----------------------------------------------------------------- + * WE PLACED THE OPERATION INTO A SERIAL QUEUE AND THUS WE HAVE TO + * WAIT FOR THE LOCK TO BE RELEASED. WE CONTINUE WITH THE NEXT ELEMENT + * ----------------------------------------------------------------- */ putOpScanLockQue(); /* PUT THE OP IN A QUE IN THE SCAN REC */ signal->theData[0] = scanPtr.i; signal->theData[1] = AccCheckScan::ZCHECK_LCP_STOP; sendSignal(cownBlockref, GSN_ACC_CHECK_SCAN, signal, 2, JBB); return; - } else if (return_result == ZWRITE_ERROR) { + } else if (return_result != ZPARALLEL_QUEUE) { jam(); - /* --------------------------------------------------------------------------------- */ - // The tuple is either not committed yet or a delete in the same transaction (not - // possible here since we are a scan). Thus we simply continue with the next tuple. - /* --------------------------------------------------------------------------------- */ + /* ----------------------------------------------------------------- */ + // The tuple is either not committed yet or a delete in + // the same transaction (not possible here since we are a scan). + // Thus we simply continue with the next tuple. + /* ----------------------------------------------------------------- */ + operationRecPtr.p->m_op_bits = Operationrec::OP_INITIAL; releaseOpRec(signal); scanPtr.p->scanOpsAllocated--; signal->theData[0] = scanPtr.i; @@ -5641,10 +6559,11 @@ void Dbacc::checkNextBucketLab(Signal* signal) ndbassert(return_result == ZPARALLEL_QUEUE); }//if }//if - /* --------------------------------------------------------------------------------- */ - // Committed read proceed without caring for locks immediately down here except when - // the tuple was deleted permanently and no new operation has inserted it again. - /* --------------------------------------------------------------------------------- */ + /* ----------------------------------------------------------------------- */ + // Committed read proceed without caring for locks immediately + // down here except when the tuple was deleted permanently + // and no new operation has inserted it again. + /* ----------------------------------------------------------------------- */ putActiveScanOp(signal); sendNextScanConf(signal); return; @@ -5668,14 +6587,15 @@ void Dbacc::initScanFragmentPart(Signal* signal) DirRangePtr cnfDirRangePtr; DirectoryarrayPtr cnfDirptr; Page8Ptr cnfPageidptr; - /* --------------------------------------------------------------------------------- */ + /* ----------------------------------------------------------------------- */ // Set the active fragment part. // Set the current bucket scanned to the first. // Start with the first lap. // Remember the number of buckets at start of the scan. - // Set the minimum and maximum to values that will always be smaller and larger than. + // Set the minimum and maximum to values that will always be smaller and + // larger than. // Reset the scan indicator on the first bucket. - /* --------------------------------------------------------------------------------- */ + /* ----------------------------------------------------------------------- */ scanPtr.p->activeLocalFrag = fragrecptr.i; scanPtr.p->nextBucketIndex = 0; /* INDEX OF SCAN BUCKET */ scanPtr.p->scanBucketState = ScanRec::FIRST_LAP; @@ -5694,11 +6614,11 @@ void Dbacc::initScanFragmentPart(Signal* signal) releaseScanBucket(signal); }//Dbacc::initScanFragmentPart() -/* --------------------------------------------------------------------------------- */ -/* FLAG = 6 = ZCOPY_CLOSE THE SCAN PROCESS IS READY OR ABORTED. ALL OPERATION IN THE */ -/* ACTIVE OR WAIT QUEUE ARE RELEASED, SCAN FLAG OF ROOT FRAG IS RESET AND THE SCAN */ -/* RECORD IS RELEASED. */ -/* --------------------------------------------------------------------------------- */ +/* ------------------------------------------------------------------------- + * FLAG = 6 = ZCOPY_CLOSE THE SCAN PROCESS IS READY OR ABORTED. + * ALL OPERATION IN THE ACTIVE OR WAIT QUEUE ARE RELEASED, + * SCAN FLAG OF ROOT FRAG IS RESET AND THE SCAN RECORD IS RELEASED. + * ------------------------------------------------------------------------ */ void Dbacc::releaseScanLab(Signal* signal) { releaseAndCommitActiveOps(signal); @@ -5737,8 +6657,17 @@ void Dbacc::releaseAndCommitActiveOps(Signal* signal) ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec); if (!scanPtr.p->scanReadCommittedFlag) { jam(); - commitOperation(signal); + if ((operationRecPtr.p->m_op_bits & Operationrec::OP_STATE_MASK) == + Operationrec::OP_STATE_EXECUTED) + { + commitOperation(signal); + } + else + { + abortOperation(signal); + } }//if + operationRecPtr.p->m_op_bits = Operationrec::OP_INITIAL; takeOutActiveScanOp(signal); releaseOpRec(signal); scanPtr.p->scanOpsAllocated--; @@ -5759,8 +6688,17 @@ void Dbacc::releaseAndCommitQueuedOps(Signal* signal) ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec); if (!scanPtr.p->scanReadCommittedFlag) { jam(); - commitOperation(signal); + if ((operationRecPtr.p->m_op_bits & Operationrec::OP_STATE_MASK) == + Operationrec::OP_STATE_EXECUTED) + { + commitOperation(signal); + } + else + { + abortOperation(signal); + } }//if + operationRecPtr.p->m_op_bits = Operationrec::OP_INITIAL; takeOutReadyScanQueue(signal); releaseOpRec(signal); scanPtr.p->scanOpsAllocated--; @@ -5783,6 +6721,7 @@ void Dbacc::releaseAndAbortLockedOps(Signal* signal) { abortOperation(signal); }//if takeOutScanLockQueue(scanPtr.i); + operationRecPtr.p->m_op_bits = Operationrec::OP_INITIAL; releaseOpRec(signal); scanPtr.p->scanOpsAllocated--; operationRecPtr.i = trsoOperPtr.i; @@ -5817,9 +6756,11 @@ void Dbacc::execACC_CHECK_SCAN(Signal* signal) takeOutReadyScanQueue(signal); fragrecptr.i = operationRecPtr.p->fragptr; ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec); - if (operationRecPtr.p->elementIsDisappeared == ZTRUE) { + if (operationRecPtr.p->m_op_bits & Operationrec::OP_ELEMENT_DISAPPEARED) + { jam(); abortOperation(signal); + operationRecPtr.p->m_op_bits = Operationrec::OP_INITIAL; releaseOpRec(signal); scanPtr.p->scanOpsAllocated--; continue; @@ -5906,7 +6847,8 @@ void Dbacc::execACC_TO_REQ(Signal* signal) jamEntry(); tatrOpPtr.i = signal->theData[1]; /* OPER PTR OF ACC */ ptrCheckGuard(tatrOpPtr, coprecsize, operationrec); - if (tatrOpPtr.p->operation == ZSCAN_OP) { + if ((tatrOpPtr.p->m_op_bits & Operationrec::OP_MASK) == ZSCAN_OP) + { tatrOpPtr.p->transId1 = signal->theData[2]; tatrOpPtr.p->transId2 = signal->theData[3]; } else { @@ -6003,32 +6945,28 @@ void Dbacc::initScanOpRec(Signal* signal) scanPtr.p->scanOpsAllocated++; + Uint32 opbits = 0; + opbits |= ZSCAN_OP; + opbits |= scanPtr.p->scanLockMode ? Operationrec::OP_LOCK_MODE : 0; + opbits |= scanPtr.p->scanLockMode ? Operationrec::OP_ACC_LOCK_MODE : 0; + opbits |= scanPtr.p->scanReadCommittedFlag ? + Operationrec::OP_EXECUTED_DIRTY_READ : 0; + opbits |= Operationrec::OP_COMMIT_DELETE_CHECK; operationRecPtr.p->userptr = RNIL; operationRecPtr.p->scanRecPtr = scanPtr.i; - operationRecPtr.p->operation = ZSCAN_OP; - operationRecPtr.p->transactionstate = ACTIVE; - operationRecPtr.p->commitDeleteCheckFlag = ZFALSE; - operationRecPtr.p->lockMode = scanPtr.p->scanLockMode; operationRecPtr.p->fid = fragrecptr.p->myfid; operationRecPtr.p->fragptr = fragrecptr.i; - operationRecPtr.p->elementIsDisappeared = ZFALSE; operationRecPtr.p->nextParallelQue = RNIL; operationRecPtr.p->prevParallelQue = RNIL; operationRecPtr.p->nextSerialQue = RNIL; operationRecPtr.p->prevSerialQue = RNIL; - operationRecPtr.p->prevQueOp = RNIL; - operationRecPtr.p->nextQueOp = RNIL; - operationRecPtr.p->keyinfoPage = RNIL; // Safety precaution operationRecPtr.p->transId1 = scanPtr.p->scanTrid1; operationRecPtr.p->transId2 = scanPtr.p->scanTrid2; - operationRecPtr.p->lockOwner = ZFALSE; - operationRecPtr.p->dirtyRead = 0; - operationRecPtr.p->nodeType = 0; // Not a stand-by node operationRecPtr.p->elementIsforward = tisoIsforward; operationRecPtr.p->elementContainer = tisoContainerptr; operationRecPtr.p->elementPointer = tisoElementptr; operationRecPtr.p->elementPage = isoPageptr.i; - operationRecPtr.p->isAccLockReq = ZFALSE; + operationRecPtr.p->m_op_bits = opbits; tisoLocalPtr = tisoElementptr + tisoIsforward; guard24 = fragrecptr.p->localkeylen - 1; for (tisoTmp = 0; tisoTmp <= guard24; tisoTmp++) { @@ -6038,7 +6976,6 @@ void Dbacc::initScanOpRec(Signal* signal) tisoLocalPtr = tisoLocalPtr + tisoIsforward; }//for arrGuard(tisoLocalPtr, 2048); - operationRecPtr.p->keydata[0] = isoPageptr.p->word32[tisoLocalPtr]; operationRecPtr.p->tupkeylen = fragrecptr.p->keyLength; operationRecPtr.p->xfrmtupkeylen = 0; // not used }//Dbacc::initScanOpRec() @@ -6894,14 +7831,12 @@ void Dbacc::releaseOpRec(Signal* signal) } ndbrequire(opInList == false); #endif - ndbrequire(operationRecPtr.p->lockOwner == ZFALSE); + ndbrequire(operationRecPtr.p->m_op_bits == Operationrec::OP_INITIAL); operationRecPtr.p->nextOp = cfreeopRec; cfreeopRec = operationRecPtr.i; /* UPDATE FREE LIST OF OP RECORDS */ operationRecPtr.p->prevOp = RNIL; - operationRecPtr.p->opState = FREE_OP; - operationRecPtr.p->transactionstate = IDLE; - operationRecPtr.p->operation = ZUNDEFINED_OP; + operationRecPtr.p->m_op_bits = Operationrec::OP_INITIAL; }//Dbacc::releaseOpRec() /* --------------------------------------------------------------------------------- */ @@ -7403,8 +8338,8 @@ Dbacc::execDUMP_STATE_ORD(Signal* signal) OperationrecPtr tmpOpPtr; tmpOpPtr.i = recordNo; ptrAss(tmpOpPtr, operationrec); - infoEvent("Dbacc::operationrec[%d]: opState=%d, transid(0x%x, 0x%x)", - tmpOpPtr.i, tmpOpPtr.p->opState, tmpOpPtr.p->transId1, + infoEvent("Dbacc::operationrec[%d]: transid(0x%x, 0x%x)", + tmpOpPtr.i, tmpOpPtr.p->transId1, tmpOpPtr.p->transId2); infoEvent("elementIsforward=%d, elementPage=%d, elementPointer=%d ", tmpOpPtr.p->elementIsforward, tmpOpPtr.p->elementPage, @@ -7412,30 +8347,19 @@ Dbacc::execDUMP_STATE_ORD(Signal* signal) infoEvent("fid=%d, fragptr=%d, hashvaluePart=%d ", tmpOpPtr.p->fid, tmpOpPtr.p->fragptr, tmpOpPtr.p->hashvaluePart); - infoEvent("hashValue=%d, insertDeleteLen=%d, keyinfoPage=%d ", - tmpOpPtr.p->hashValue, tmpOpPtr.p->insertDeleteLen, - tmpOpPtr.p->keyinfoPage); + infoEvent("hashValue=%d", tmpOpPtr.p->hashValue); infoEvent("nextLockOwnerOp=%d, nextOp=%d, nextParallelQue=%d ", tmpOpPtr.p->nextLockOwnerOp, tmpOpPtr.p->nextOp, tmpOpPtr.p->nextParallelQue); - infoEvent("nextQueOp=%d, nextSerialQue=%d, prevOp=%d ", - tmpOpPtr.p->nextQueOp, tmpOpPtr.p->nextSerialQue, + infoEvent("nextSerialQue=%d, prevOp=%d ", + tmpOpPtr.p->nextSerialQue, tmpOpPtr.p->prevOp); - infoEvent("prevLockOwnerOp=%d, prevParallelQue=%d, prevQueOp=%d ", - tmpOpPtr.p->prevLockOwnerOp, tmpOpPtr.p->nextParallelQue, - tmpOpPtr.p->prevQueOp); - infoEvent("prevSerialQue=%d, scanRecPtr=%d, longPagePtr=%d ", - tmpOpPtr.p->prevSerialQue, tmpOpPtr.p->scanRecPtr, - tmpOpPtr.p->longPagePtr); - infoEvent("transactionstate=%d, elementIsDisappeared=%d, insertIsDone=%d ", - tmpOpPtr.p->transactionstate, tmpOpPtr.p->elementIsDisappeared, - tmpOpPtr.p->insertIsDone); - infoEvent("lockMode=%d, lockOwner=%d, nodeType=%d ", - tmpOpPtr.p->lockMode, tmpOpPtr.p->lockOwner, - tmpOpPtr.p->nodeType); - infoEvent("operation=%d, opSimple=%d, dirtyRead=%d,scanBits=%d ", - tmpOpPtr.p->operation, tmpOpPtr.p->opSimple, - tmpOpPtr.p->dirtyRead, tmpOpPtr.p->scanBits); + infoEvent("prevLockOwnerOp=%d, prevParallelQue=%d", + tmpOpPtr.p->prevLockOwnerOp, tmpOpPtr.p->nextParallelQue); + infoEvent("prevSerialQue=%d, scanRecPtr=%d", + tmpOpPtr.p->prevSerialQue, tmpOpPtr.p->scanRecPtr); + infoEvent("m_op_bits=0x%x, scanBits=%d ", + tmpOpPtr.p->m_op_bits, tmpOpPtr.p->scanBits); return; } diff --git a/storage/ndb/src/kernel/blocks/dbdih/DbdihMain.cpp b/storage/ndb/src/kernel/blocks/dbdih/DbdihMain.cpp index 9936217bbf3..bb4c2ed197e 100644 --- a/storage/ndb/src/kernel/blocks/dbdih/DbdihMain.cpp +++ b/storage/ndb/src/kernel/blocks/dbdih/DbdihMain.cpp @@ -5601,6 +5601,12 @@ Dbdih::checkLocalNodefailComplete(Signal* signal, Uint32 failedNodeId, return; } + if (ERROR_INSERTED(7030)) + { + ndbout_c("Reenable GCP_PREPARE"); + CLEAR_ERROR_INSERT_VALUE; + } + NFCompleteRep * const nf = (NFCompleteRep *)&signal->theData[0]; nf->blockNo = DBDIH; nf->nodeId = cownNodeId; @@ -7733,6 +7739,16 @@ void Dbdih::execGCP_PREPARE(Signal* signal) { jamEntry(); CRASH_INSERTION(7005); + + if (ERROR_INSERTED(7030)) + { + cgckptflag = true; + ndbout_c("Delayed GCP_PREPARE 5s"); + sendSignalWithDelay(reference(), GSN_GCP_PREPARE, signal, 5000, + signal->getLength()); + return; + } + Uint32 masterNodeId = signal->theData[0]; Uint32 gci = signal->theData[1]; BlockReference retRef = calcDihBlockRef(masterNodeId); @@ -7745,6 +7761,14 @@ void Dbdih::execGCP_PREPARE(Signal* signal) cgcpParticipantState = GCP_PARTICIPANT_PREPARE_RECEIVED; cnewgcp = gci; + if (ERROR_INSERTED(7031)) + { + ndbout_c("Crashing delayed in GCP_PREPARE 3s"); + signal->theData[0] = 9999; + sendSignalWithDelay(CMVMI_REF, GSN_NDB_TAMPER, signal, 3000, 1); + return; + } + signal->theData[0] = cownNodeId; signal->theData[1] = gci; sendSignal(retRef, GSN_GCP_PREPARECONF, signal, 2, JBA); diff --git a/storage/ndb/src/kernel/blocks/dblqh/Dblqh.hpp b/storage/ndb/src/kernel/blocks/dblqh/Dblqh.hpp index 407cd8074ab..f1d1fdbf000 100644 --- a/storage/ndb/src/kernel/blocks/dblqh/Dblqh.hpp +++ b/storage/ndb/src/kernel/blocks/dblqh/Dblqh.hpp @@ -33,7 +33,8 @@ // primary key is stored in TUP #include "../dbtup/Dbtup.hpp" -#include "../dbacc/Dbacc.hpp" +class Dbacc; +class Dbtup; #ifdef DBLQH_C // Constants @@ -571,6 +572,7 @@ public: Uint8 rangeScan; Uint8 descending; Uint8 tupScan; + Uint8 lcpScan; Uint8 scanTcWaiting; Uint8 scanKeyinfoFlag; Uint8 m_last_row; @@ -2556,8 +2558,19 @@ private: Dbtup* c_tup; Dbacc* c_acc; + + /** + * Read primary key from tup + */ Uint32 readPrimaryKeys(ScanRecord*, TcConnectionrec*, Uint32 * dst); + /** + * Read primary key from operation + */ +public: + Uint32 readPrimaryKeys(Uint32 opPtrI, Uint32 * dst, bool xfrm); +private: + void acckeyconf_tupkeyreq(Signal*, TcConnectionrec*, Fragrecord*, Uint32, Uint32); void acckeyconf_load_diskpage(Signal*,TcConnectionrecPtr,Fragrecord*,Uint32); @@ -2924,6 +2937,11 @@ public: } DLHashTable<ScanRecord> c_scanTakeOverHash; + + inline bool TRACE_OP_CHECK(const TcConnectionrec* regTcPtr); +#ifdef ERROR_INSERT + void TRACE_OP_DUMP(const TcConnectionrec* regTcPtr, const char * pos); +#endif }; inline @@ -2991,10 +3009,19 @@ Dblqh::accminupdate(Signal* signal, Uint32 opId, const Local_key* key) signal->theData[1] = key->m_page_no << MAX_TUPLES_BITS | key->m_page_idx; c_acc->execACCMINUPDATE(signal); - if (ERROR_INSERTED(5712)) + if (ERROR_INSERTED(5712) || ERROR_INSERTED(5713)) ndbout << " LK: " << *key; regTcPtr.p->m_row_id = *key; } +inline +bool +Dblqh::TRACE_OP_CHECK(const TcConnectionrec* regTcPtr) +{ + return (ERROR_INSERTED(5712) && + (regTcPtr->operation == ZINSERT || + regTcPtr->operation == ZDELETE)) || + ERROR_INSERTED(5713); +} #endif diff --git a/storage/ndb/src/kernel/blocks/dblqh/DblqhMain.cpp b/storage/ndb/src/kernel/blocks/dblqh/DblqhMain.cpp index d031f9a00bf..53cf5c06fe4 100644 --- a/storage/ndb/src/kernel/blocks/dblqh/DblqhMain.cpp +++ b/storage/ndb/src/kernel/blocks/dblqh/DblqhMain.cpp @@ -67,48 +67,70 @@ // seen only when we debug the product #ifdef VM_TRACE #define DEBUG(x) ndbout << "DBLQH: "<< x << endl; +static NdbOut & operator<<(NdbOut& out, Dblqh::TcConnectionrec::TransactionState state){ out << (int)state; return out; } +static NdbOut & operator<<(NdbOut& out, Dblqh::TcConnectionrec::LogWriteState state){ out << (int)state; return out; } +static NdbOut & operator<<(NdbOut& out, Dblqh::TcConnectionrec::ListState state){ out << (int)state; return out; } +static NdbOut & operator<<(NdbOut& out, Dblqh::TcConnectionrec::AbortState state){ out << (int)state; return out; } +static NdbOut & operator<<(NdbOut& out, Dblqh::ScanRecord::ScanState state){ out << (int)state; return out; } +static NdbOut & operator<<(NdbOut& out, Dblqh::LogFileOperationRecord::LfoState state){ out << (int)state; return out; } +static NdbOut & operator<<(NdbOut& out, Dblqh::ScanRecord::ScanType state){ out << (int)state; return out; } +static +NdbOut & +operator<<(NdbOut& out, Operation_t op) +{ + switch(op){ + case ZREAD: out << "READ"; break; + case ZREAD_EX: out << "READ-EX"; break; + case ZINSERT: out << "INSERT"; break; + case ZUPDATE: out << "UPDATE"; break; + case ZDELETE: out << "DELETE"; break; + case ZWRITE: out << "WRITE"; break; + } + return out; +} + #else #define DEBUG(x) #endif @@ -120,7 +142,7 @@ const Uint32 NR_ScanNo = 0; #if defined VM_TRACE || defined ERROR_INSERT || defined NDBD_TRACENR #include <NdbConfig.h> -NdbOut * tracenrout = 0; +static NdbOut * tracenrout = 0; static int TRACENR_FLAG = 0; #define TRACENR(x) (* tracenrout) << x #define SET_TRACENR_FLAG TRACENR_FLAG = 1 @@ -132,6 +154,13 @@ static int TRACENR_FLAG = 0; #define CLEAR_TRACENR_FLAG #endif +#ifdef ERROR_INSERT +static NdbOut * traceopout = 0; +#define TRACE_OP(regTcPtr, place) do { if (TRACE_OP_CHECK(regTcPtr)) TRACE_OP_DUMP(regTcPtr, place); } while(0) +#else +#define TRACE_OP(x, y) {} +#endif + /* ------------------------------------------------------------------------- */ /* ------- SEND SYSTEM ERROR ------- */ /* */ @@ -454,6 +483,10 @@ void Dblqh::execSTTOR(Signal* signal) name = NdbConfig_SignalLogFileName(getOwnNodeId()); tracenrout = new NdbOut(* new FileOutputStream(fopen(name, "w+"))); #endif + +#ifdef ERROR_INSERT + traceopout = &ndbout; +#endif return; break; @@ -2531,14 +2564,15 @@ void Dblqh::execTUPKEYCONF(Signal* signal) case TcConnectionrec::WAIT_ACC_ABORT: case TcConnectionrec::ABORT_QUEUED: jam(); -/* -------------------------------------------------------------------------- */ -/* IGNORE SINCE ABORT OF THIS OPERATION IS ONGOING ALREADY. */ -/* -------------------------------------------------------------------------- */ +/* ------------------------------------------------------------------------- */ +/* IGNORE SINCE ABORT OF THIS OPERATION IS ONGOING ALREADY. */ +/* ------------------------------------------------------------------------- */ break; default: ndbrequire(false); break; }//switch + }//Dblqh::execTUPKEYCONF() /* ************> */ @@ -2560,6 +2594,8 @@ void Dblqh::execTUPKEYREF(Signal* signal) c_fragment_pool.getPtr(regFragptr); fragptr = regFragptr; + TRACE_OP(regTcPtr, "TUPKEYREF"); + if (unlikely(activeCreat == Fragrecord::AC_NR_COPY)) { jam(); @@ -2568,7 +2604,7 @@ void Dblqh::execTUPKEYREF(Signal* signal) ndbassert(regTcPtr->transactionState == TcConnectionrec::WAIT_TUP || regTcPtr->transactionState ==TcConnectionrec::WAIT_TUP_TO_ABORT); } - + switch (tcConnectptr.p->transactionState) { case TcConnectionrec::WAIT_TUP: jam(); @@ -3606,57 +3642,7 @@ void Dblqh::endgettupkeyLab(Signal* signal) regTcPtr->transactionState = TcConnectionrec::WAIT_ATTR; return; }//if -//#define TRACE_LQHKEYREQ -#ifdef TRACE_LQHKEYREQ - { - ndbout << (regTcPtr->operation == ZREAD ? "READ" : - regTcPtr->operation == ZUPDATE ? "UPDATE" : - regTcPtr->operation == ZINSERT ? "INSERT" : - regTcPtr->operation == ZDELETE ? "DELETE" : "<Other>") - << "(" << (int)regTcPtr->operation << ")" - << " from=(" << getBlockName(refToBlock(regTcPtr->clientBlockref)) - << ", " << refToNode(regTcPtr->clientBlockref) << ")" - << " table=" << regTcPtr->tableref << " "; - - ndbout << "hash: " << hex << regTcPtr->hashValue << endl; - - ndbout << "key=[" << hex; - Uint32 i; - for(i = 0; i<regTcPtr->primKeyLen && i < 4; i++){ - ndbout << hex << regTcPtr->tupkeyData[i] << " "; - } - - DatabufPtr regDatabufptr; - regDatabufptr.i = regTcPtr->firstTupkeybuf; - while(i < regTcPtr->primKeyLen) - { - ptrCheckGuard(regDatabufptr, cdatabufFileSize, databuf); - for(Uint32 j = 0; j<4 && i<regTcPtr->primKeyLen; j++, i++) - ndbout << hex << regDatabufptr.p->data[j] << " "; - } - ndbout << "]" << endl; - - ndbout << "attr=[" << hex; - for(i = 0; i<regTcPtr->reclenAiLqhkey && i < 5; i++) - ndbout << hex << regTcPtr->firstAttrinfo[i] << " "; - - AttrbufPtr regAttrinbufptr; - regAttrinbufptr.i= regTcPtr->firstAttrinbuf; - while(i < regTcPtr->totReclenAi) - { - ptrCheckGuard(regAttrinbufptr, cattrinbufFileSize, attrbuf); - Uint32 dataLen = regAttrinbufptr.p->attrbuf[ZINBUF_DATA_LEN]; - ndbrequire(dataLen != 0); - ndbrequire(i + dataLen <= regTcPtr->totReclenAi); - for(Uint32 j= 0; j<dataLen; j++, i++) - ndbout << hex << regAttrinbufptr.p->attrbuf[j] << " "; - - regAttrinbufptr.i = regAttrinbufptr.p->attrbuf[ZINBUF_NEXT]; - } - - ndbout << "]" << endl; - } -#endif + /* ---------------------------------------------------------------------- */ /* NOW RECEPTION OF LQHKEYREQ IS COMPLETED THE NEXT STEP IS TO START*/ /* PROCESSING THE MESSAGE. IF THE MESSAGE IS TO A STAND-BY NODE */ @@ -3763,6 +3749,7 @@ void Dblqh::prepareContinueAfterBlockedLab(Signal* signal) /* ----------------------------------------------------------------- */ if (TRACENR_FLAG) { + TRACE_OP(regTcPtr, "RECEIVED"); switch (regTcPtr->operation) { case ZREAD: TRACENR("READ"); break; case ZUPDATE: TRACENR("UPDATE"); break; @@ -3847,6 +3834,9 @@ Dblqh::exec_acckeyreq(Signal* signal, TcConnectionrecPtr regTcPtr) signal->theData[8] = sig2; signal->theData[9] = sig3; signal->theData[10] = sig4; + + TRACE_OP(regTcPtr.p, "ACC"); + if (regTcPtr.p->primKeyLen > 4) { sendKeyinfoAcc(signal, 11); }//if @@ -4133,7 +4123,7 @@ Dblqh::nr_copy_delete_row(Signal* signal, jam(); ndbrequire(rowid == 0); signal->theData[0] = accPtr; - signal->theData[1] = false; + signal->theData[1] = 0; EXECUTE_DIRECT(ref, GSN_ACC_ABORTREQ, signal, 2); jamEntry(); return; @@ -4144,16 +4134,18 @@ Dblqh::nr_copy_delete_row(Signal* signal, */ ndbrequire(regTcPtr.p->m_dealloc == 0); Local_key save = regTcPtr.p->m_row_id; - signal->theData[0] = regTcPtr.p->accConnectrec; + + c_acc->execACCKEY_ORD(signal, accPtr); + signal->theData[0] = accPtr; EXECUTE_DIRECT(ref, GSN_ACC_COMMITREQ, signal, 1); jamEntry(); - + ndbrequire(regTcPtr.p->m_dealloc == 1); int ret = c_tup->nr_delete(signal, regTcPtr.i, fragPtr.p->tupFragptr, ®TcPtr.p->m_row_id, regTcPtr.p->gci); jamEntry(); - + if (ret) { ndbassert(ret == 1); @@ -4167,7 +4159,7 @@ Dblqh::nr_copy_delete_row(Signal* signal, } TRACENR("DELETED: " << regTcPtr.p->m_row_id << endl); - + regTcPtr.p->m_dealloc = 0; regTcPtr.p->m_row_id = save; fragptr = fragPtr; @@ -4274,6 +4266,45 @@ Dblqh::nr_delete_complete(Signal* signal, Nr_op_info* op) } } +Uint32 +Dblqh::readPrimaryKeys(Uint32 opPtrI, Uint32 * dst, bool xfrm) +{ + TcConnectionrecPtr regTcPtr; + DatabufPtr regDatabufptr; + Uint64 Tmp[MAX_KEY_SIZE_IN_WORDS >> 1]; + + jamEntry(); + regTcPtr.i = opPtrI; + ptrCheckGuard(regTcPtr, ctcConnectrecFileSize, tcConnectionrec); + + Uint32 tableId = regTcPtr.p->tableref; + Uint32 keyLen = regTcPtr.p->primKeyLen; + regDatabufptr.i = regTcPtr.p->firstTupkeybuf; + Uint32 * tmp = xfrm ? (Uint32*)Tmp : dst; + + memcpy(tmp, regTcPtr.p->tupkeyData, sizeof(regTcPtr.p->tupkeyData)); + if (keyLen > 4) + { + tmp += 4; + Uint32 pos = 4; + do { + ptrCheckGuard(regDatabufptr, cdatabufFileSize, databuf); + memcpy(tmp, regDatabufptr.p->data, sizeof(regDatabufptr.p->data)); + regDatabufptr.i = regDatabufptr.p->nextDatabuf; + tmp += sizeof(regDatabufptr.p->data) >> 2; + pos += sizeof(regDatabufptr.p->data) >> 2; + } while(pos < keyLen); + } + + if (xfrm) + { + jam(); + Uint32 keyPartLen[MAX_ATTRIBUTES_IN_INDEX]; + return xfrm_key(tableId, (Uint32*)Tmp, dst, ~0, keyPartLen); + } + + return keyLen; +} /* =*======================================================================= */ /* ======= SEND KEYINFO TO ACC ======= */ @@ -4447,10 +4478,6 @@ Dblqh::acckeyconf_tupkeyreq(Signal* signal, TcConnectionrec* regTcPtr, * ----------------------------------------------------------------------- */ Uint32 page_idx = local_key & MAX_TUPLES_PER_PAGE; Uint32 page_no = local_key >> MAX_TUPLES_BITS; -#ifdef TRACE_LQHKEYREQ - ndbout << "localkey: [ " << hex << page_no << " " << page_idx << "]" - << endl; -#endif Uint32 Ttupreq = regTcPtr->dirtyOp; Ttupreq = Ttupreq + (regTcPtr->opSimple << 1); Ttupreq = Ttupreq + (op << 6); @@ -4506,70 +4533,13 @@ Dblqh::acckeyconf_tupkeyreq(Signal* signal, TcConnectionrec* regTcPtr, tupKeyReq->m_row_id_page_no = sig0; tupKeyReq->m_row_id_page_idx = sig1; - if (ERROR_INSERTED(5712) && regTcPtr->operation == ZINSERT) - { - ndbout << "INSERT " << regFragptrP->tabRef - << "(" << regFragptrP->fragId << ")"; - - { - ndbout << "key=[" << hex; - Uint32 i; - for(i = 0; i<regTcPtr->primKeyLen && i < 4; i++){ - ndbout << hex << regTcPtr->tupkeyData[i] << " "; - } - - DatabufPtr regDatabufptr; - regDatabufptr.i = regTcPtr->firstTupkeybuf; - while(i < regTcPtr->primKeyLen) - { - ptrCheckGuard(regDatabufptr, cdatabufFileSize, databuf); - for(Uint32 j = 0; j<4 && i<regTcPtr->primKeyLen; j++, i++) - ndbout << hex << regDatabufptr.p->data[j] << " "; - } - ndbout << "] "; - } - - if(regTcPtr->m_use_rowid) - ndbout << " " << regTcPtr->m_row_id; - } - - if (ERROR_INSERTED(5712) && regTcPtr->operation == ZDELETE) - { - Local_key lk; lk.assref(local_key); - - ndbout << "DELETE " << regFragptrP->tabRef - << "(" << regFragptrP->fragId << ") " << lk; - - { - ndbout << "key=[" << hex; - Uint32 i; - for(i = 0; i<regTcPtr->primKeyLen && i < 4; i++){ - ndbout << hex << regTcPtr->tupkeyData[i] << " "; - } - - DatabufPtr regDatabufptr; - regDatabufptr.i = regTcPtr->firstTupkeybuf; - while(i < regTcPtr->primKeyLen) - { - ptrCheckGuard(regDatabufptr, cdatabufFileSize, databuf); - for(Uint32 j = 0; j<4 && i<regTcPtr->primKeyLen; j++, i++) - ndbout << hex << regDatabufptr.p->data[j] << " "; - } - ndbout << "]" << endl; - } - - } - + TRACE_OP(regTcPtr, "TUPKEYREQ"); + regTcPtr->m_use_rowid |= (op == ZINSERT); regTcPtr->m_row_id.m_page_no = page_no; regTcPtr->m_row_id.m_page_idx = page_idx; EXECUTE_DIRECT(tup, GSN_TUPKEYREQ, signal, TupKeyReq::SignalLength); - - if (ERROR_INSERTED(5712) && regTcPtr->operation == ZINSERT) - { - ndbout << endl; - } }//Dblqh::execACCKEYCONF() void @@ -4654,27 +4624,37 @@ void Dblqh::tupkeyConfLab(Signal* signal) const TupKeyConf * const tupKeyConf = (TupKeyConf *)&signal->theData[0]; TcConnectionrec * const regTcPtr = tcConnectptr.p; Uint32 activeCreat = regTcPtr->activeCreat; + Uint32 readLen = tupKeyConf->readLength; + Uint32 writeLen = tupKeyConf->writeLength; + Uint32 accOp = regTcPtr->accConnectrec; + c_acc->execACCKEY_ORD(signal, accOp); + + TRACE_OP(regTcPtr, "TUPKEYCONF"); + if (regTcPtr->simpleRead) { jam(); /* ---------------------------------------------------------------------- - * THE OPERATION IS A SIMPLE READ. WE WILL IMMEDIATELY COMMIT THE OPERATION. - * SINCE WE HAVE NOT RELEASED THE FRAGMENT LOCK (FOR LOCAL CHECKPOINTS) YET + * THE OPERATION IS A SIMPLE READ. + * WE WILL IMMEDIATELY COMMIT THE OPERATION. + * SINCE WE HAVE NOT RELEASED THE FRAGMENT LOCK + * (FOR LOCAL CHECKPOINTS) YET * WE CAN GO IMMEDIATELY TO COMMIT_CONTINUE_AFTER_BLOCKED. - * WE HAVE ALREADY SENT THE RESPONSE SO WE ARE NOT INTERESTED IN READ LENGTH - * ---------------------------------------------------------------------- */ + * WE HAVE ALREADY SENT THE RESPONSE SO WE ARE NOT INTERESTED IN + * READ LENGTH + * --------------------------------------------------------------------- */ commitContinueAfterBlockedLab(signal); return; }//if - if (tupKeyConf->readLength != 0) { + if (readLen != 0) + { jam(); /* SET BIT 15 IN REQINFO */ LqhKeyReq::setApplicationAddressFlag(regTcPtr->reqinfo, 1); - - regTcPtr->readlenAi = tupKeyConf->readLength; + regTcPtr->readlenAi = readLen; }//if - regTcPtr->totSendlenAi = tupKeyConf->writeLength; + regTcPtr->totSendlenAi = writeLen; ndbrequire(regTcPtr->totSendlenAi == regTcPtr->currTupAiLen); if (unlikely(activeCreat == Fragrecord::AC_NR_COPY)) @@ -5597,6 +5577,8 @@ void Dblqh::releaseOprec(Signal* signal) if (TRACENR_FLAG) TRACENR("DELETED: " << regTcPtr->m_row_id << endl); + + TRACE_OP(regTcPtr, "DEALLOC"); signal->theData[0] = regTcPtr->fragmentid; signal->theData[1] = regTcPtr->tableref; @@ -5818,6 +5800,10 @@ void Dblqh::execCOMMIT(Signal* signal) ptrAss(tcConnectptr, regTcConnectionrec); if ((tcConnectptr.p->transid[0] == transid1) && (tcConnectptr.p->transid[1] == transid2)) { + + TcConnectionrec * const regTcPtr = tcConnectptr.p; + TRACE_OP(regTcPtr, "COMMIT"); + commitReqLab(signal, gci); return; }//if @@ -5937,6 +5923,10 @@ void Dblqh::execCOMPLETE(Signal* signal) if ((tcConnectptr.p->transactionState == TcConnectionrec::COMMITTED) && (tcConnectptr.p->transid[0] == transid1) && (tcConnectptr.p->transid[1] == transid2)) { + + TcConnectionrec * const regTcPtr = tcConnectptr.p; + TRACE_OP(regTcPtr, "COMPLETE"); + if (tcConnectptr.p->seqNoReplica != 0 && tcConnectptr.p->activeCreat == Fragrecord::AC_NORMAL) { jam(); @@ -6313,12 +6303,16 @@ void Dblqh::commitContinueAfterBlockedLab(Signal* signal) TRACENR(endl); } + TRACE_OP(regTcPtr.p, "ACC_COMMITREQ"); + Uint32 acc = refToBlock(regTcPtr.p->tcAccBlockref); signal->theData[0] = regTcPtr.p->accConnectrec; EXECUTE_DIRECT(acc, GSN_ACC_COMMITREQ, signal, 1); } else { if(!dirtyOp){ + TRACE_OP(regTcPtr.p, "ACC_COMMITREQ"); + Uint32 acc = refToBlock(regTcPtr.p->tcAccBlockref); signal->theData[0] = regTcPtr.p->accConnectrec; EXECUTE_DIRECT(acc, GSN_ACC_COMMITREQ, signal, 1); @@ -6362,6 +6356,8 @@ Dblqh::tupcommit_conf_callback(Signal* signal, Uint32 tcPtrI) c_fragment_pool.getPtr(regFragptr); fragptr = regFragptr; + TRACE_OP(tcPtr, "ACC_COMMITREQ"); + Uint32 acc = refToBlock(tcPtr->tcAccBlockref); signal->theData[0] = tcPtr->accConnectrec; EXECUTE_DIRECT(acc, GSN_ACC_COMMITREQ, signal, 1); @@ -6670,6 +6666,8 @@ void Dblqh::execABORT(Signal* signal) regTcPtr->commitAckMarker = RNIL; } + TRACE_OP(regTcPtr, "ABORT"); + abortStateHandlerLab(signal); return; @@ -7087,23 +7085,30 @@ void Dblqh::abortContinueAfterBlockedLab(Signal* signal, bool canBlock) * ALSO AS PART OF A NORMAL ABORT WITHOUT BLOCKING. * WE MUST ABORT TUP BEFORE ACC TO ENSURE THAT NO ONE RACES IN * AND SEES A STATE IN TUP. - * ------------------------------------------------------------------------ */ + * ----------------------------------------------------------------------- */ TcConnectionrec * const regTcPtr = tcConnectptr.p; - fragptr.i = regTcPtr->fragmentptr; - c_fragment_pool.getPtr(fragptr); - signal->theData[0] = regTcPtr->tupConnectrec; - EXECUTE_DIRECT(DBTUP, GSN_TUP_ABORTREQ, signal, 1); + + TRACE_OP(regTcPtr, "ACC ABORT"); + regTcPtr->transactionState = TcConnectionrec::WAIT_ACC_ABORT; signal->theData[0] = regTcPtr->accConnectrec; - signal->theData[1] = true; + signal->theData[1] = 2; // JOB BUFFER IF NEEDED EXECUTE_DIRECT(DBACC, GSN_ACC_ABORTREQ, signal, 2); - /* ------------------------------------------------------------------------ - * We need to insert a real-time break by sending ACC_ABORTCONF through the - * job buffer to ensure that we catch any ACCKEYCONF or TUPKEYCONF or - * TUPKEYREF that are in the job buffer but not yet processed. Doing - * everything without that would race and create a state error when they - * are executed. - * ----------------------------------------------------------------------- */ + + if (signal->theData[1] == RNIL) + { + jam(); + /* ------------------------------------------------------------------------ + * We need to insert a real-time break by sending ACC_ABORTCONF through the + * job buffer to ensure that we catch any ACCKEYCONF or TUPKEYCONF or + * TUPKEYREF that are in the job buffer but not yet processed. Doing + * everything without that would race and create a state error when they + * are executed. + * --------------------------------------------------------------------- */ + return; + } + + execACC_ABORTCONF(signal); return; }//Dblqh::abortContinueAfterBlockedLab() @@ -7117,6 +7122,11 @@ void Dblqh::execACC_ABORTCONF(Signal* signal) ptrCheckGuard(tcConnectptr, ctcConnectrecFileSize, tcConnectionrec); TcConnectionrec * const regTcPtr = tcConnectptr.p; ndbrequire(regTcPtr->transactionState == TcConnectionrec::WAIT_ACC_ABORT); + + TRACE_OP(regTcPtr, "ACC_ABORTCONF"); + signal->theData[0] = regTcPtr->tupConnectrec; + EXECUTE_DIRECT(DBTUP, GSN_TUP_ABORTREQ, signal, 1); + continueAbortLab(signal); return; }//Dblqh::execACC_ABORTCONF() @@ -7623,6 +7633,7 @@ void Dblqh::execNEXT_SCANCONF(Signal* signal) scanLockReleasedLab(signal); break; default: + ndbout_c("%d", scanptr.p->scanState); ndbrequire(false); }//switch }//Dblqh::execNEXT_SCANCONF() @@ -8351,6 +8362,8 @@ void Dblqh::continueAfterReceivingAllAiLab(Signal* signal) AccScanReq::setDescendingFlag(req->requestInfo, scanptr.p->descending); AccScanReq::setNoDiskScanFlag(req->requestInfo, !tcConnectptr.p->m_disk_table); + AccScanReq::setLcpScanFlag(req->requestInfo, scanptr.p->lcpScan); + req->transId1 = tcConnectptr.p->transid[0]; req->transId2 = tcConnectptr.p->transid[1]; req->savePointId = tcConnectptr.p->savePointId; @@ -8837,7 +8850,9 @@ void Dblqh::nextScanConfScanLab(Signal* signal) }//if // If accOperationPtr == RNIL no record was returned by ACC - if (nextScanConf->accOperationPtr == RNIL) { + Uint32 accOpPtr = nextScanConf->accOperationPtr; + if (accOpPtr == RNIL) + { jam(); /************************************************************* * STOP THE SCAN PROCESS IF THIS HAS BEEN REQUESTED. @@ -8871,7 +8886,8 @@ void Dblqh::nextScanConfScanLab(Signal* signal) jam(); set_acc_ptr_in_scan_record(scanptr.p, scanptr.p->m_curr_batch_size_rows, - nextScanConf->accOperationPtr); + accOpPtr); + jam(); nextScanConfLoopLab(signal); }//Dblqh::nextScanConfScanLab() @@ -9049,6 +9065,7 @@ Dblqh::readPrimaryKeys(ScanRecord *scanP, TcConnectionrec *tcConP, Uint32 *dst) } int ret = c_tup->accReadPk(tableId, fragId, fragPageId, pageIndex, dst, false); + jamEntry(); if(0) ndbout_c("readPrimaryKeys(table: %d fragment: %d [ %d %d ] -> %d", tableId, fragId, fragPageId, pageIndex, ret); @@ -9071,12 +9088,25 @@ void Dblqh::scanTupkeyConfLab(Signal* signal) tcConnectptr.p->transactionState = TcConnectionrec::SCAN_STATE_USED; scanptr.i = tcConnectptr.p->tcScanRec; c_scanRecordPool.getPtr(scanptr); + + Uint32 rows = scanptr.p->m_curr_batch_size_rows; + Uint32 accOpPtr= get_acc_ptr_from_scan_record(scanptr.p, rows, false); + if (accOpPtr != (Uint32)-1) + { + c_acc->execACCKEY_ORD(signal, accOpPtr); + jamEntry(); + } + else + { + ndbassert(refToBlock(scanptr.p->scanBlockref) != DBACC); + } + if (scanptr.p->scanCompletedStatus == ZTRUE) { /* --------------------------------------------------------------------- * STOP THE SCAN PROCESS IF THIS HAS BEEN REQUESTED. * --------------------------------------------------------------------- */ - if ((scanptr.p->scanLockHold == ZTRUE) && - (scanptr.p->m_curr_batch_size_rows > 0)) { + if ((scanptr.p->scanLockHold == ZTRUE) && rows) + { jam(); scanptr.p->scanReleaseCounter = 1; scanReleaseLocksLab(signal); @@ -9093,7 +9123,7 @@ void Dblqh::scanTupkeyConfLab(Signal* signal) }//if ndbrequire(scanptr.p->m_curr_batch_size_rows < MAX_PARALLEL_OP_PER_SCAN); scanptr.p->m_curr_batch_size_bytes+= tdata4; - scanptr.p->m_curr_batch_size_rows++; + scanptr.p->m_curr_batch_size_rows = rows + 1; scanptr.p->m_last_row = tdata5; if (scanptr.p->check_scan_batch_completed() | tdata5){ if (scanptr.p->scanLockHold == ZTRUE) { @@ -9103,7 +9133,7 @@ void Dblqh::scanTupkeyConfLab(Signal* signal) return; } else { jam(); - scanptr.p->scanReleaseCounter = scanptr.p->m_curr_batch_size_rows; + scanptr.p->scanReleaseCounter = rows + 1; scanReleaseLocksLab(signal); return; } @@ -9187,12 +9217,24 @@ void Dblqh::scanTupkeyRefLab(Signal* signal) tcConnectptr.p->transactionState = TcConnectionrec::SCAN_STATE_USED; scanptr.i = tcConnectptr.p->tcScanRec; c_scanRecordPool.getPtr(scanptr); + + Uint32 rows = scanptr.p->m_curr_batch_size_rows; + Uint32 accOpPtr= get_acc_ptr_from_scan_record(scanptr.p, rows, false); + if (accOpPtr != (Uint32)-1) + { + c_acc->execACCKEY_ORD(signal, accOpPtr); + } + else + { + ndbassert(refToBlock(scanptr.p->scanBlockref) != DBACC); + } + if (scanptr.p->scanCompletedStatus == ZTRUE) { /* --------------------------------------------------------------------- * STOP THE SCAN PROCESS IF THIS HAS BEEN REQUESTED. * --------------------------------------------------------------------- */ - if ((scanptr.p->scanLockHold == ZTRUE) && - (scanptr.p->m_curr_batch_size_rows > 0)) { + if ((scanptr.p->scanLockHold == ZTRUE) && rows) + { jam(); scanptr.p->scanReleaseCounter = 1; scanReleaseLocksLab(signal); @@ -9213,8 +9255,8 @@ void Dblqh::scanTupkeyRefLab(Signal* signal) scanptr.p->scanReleaseCounter = 1; } else { jam(); - scanptr.p->m_curr_batch_size_rows++; - scanptr.p->scanReleaseCounter = scanptr.p->m_curr_batch_size_rows; + scanptr.p->m_curr_batch_size_rows = rows + 1; + scanptr.p->scanReleaseCounter = rows + 1; }//if /* -------------------------------------------------------------------- * WE NEED TO RELEASE ALL LOCKS CURRENTLY @@ -9224,7 +9266,7 @@ void Dblqh::scanTupkeyRefLab(Signal* signal) return; }//if Uint32 time_passed= tcConnectptr.p->tcTimer - cLqhTimeOutCount; - if (scanptr.p->m_curr_batch_size_rows > 0) { + if (rows) { if (time_passed > 1) { /* ----------------------------------------------------------------------- * WE NEED TO ENSURE THAT WE DO NOT SEARCH FOR THE NEXT TUPLE FOR A @@ -9232,7 +9274,7 @@ void Dblqh::scanTupkeyRefLab(Signal* signal) * THE FOUND TUPLE IF FOUND TUPLES ARE RARE. If more than 10 ms passed we * send the found tuples to the API. * ----------------------------------------------------------------------- */ - scanptr.p->scanReleaseCounter = scanptr.p->m_curr_batch_size_rows + 1; + scanptr.p->scanReleaseCounter = rows + 1; scanReleaseLocksLab(signal); return; } @@ -9377,7 +9419,7 @@ Uint32 Dblqh::initScanrec(const ScanFragReq* scanFragReq) const Uint32 readCommitted = ScanFragReq::getReadCommittedFlag(reqinfo); const Uint32 rangeScan = ScanFragReq::getRangeScanFlag(reqinfo); const Uint32 descending = ScanFragReq::getDescendingFlag(reqinfo); - const Uint32 tupScan = ScanFragReq::getTupScanFlag(reqinfo); + Uint32 tupScan = ScanFragReq::getTupScanFlag(reqinfo); const Uint32 attrLen = ScanFragReq::getAttrLen(reqinfo); const Uint32 scanPrio = ScanFragReq::getScanPrio(reqinfo); @@ -9395,6 +9437,11 @@ Uint32 Dblqh::initScanrec(const ScanFragReq* scanFragReq) scanptr.p->m_max_batch_size_rows = max_rows; scanptr.p->m_max_batch_size_bytes = max_bytes; +#if 0 + if (! rangeScan) + tupScan = 1; +#endif + if (! rangeScan && ! tupScan) scanptr.p->scanBlockref = tcConnectptr.p->tcAccBlockref; else if (! tupScan) @@ -9408,6 +9455,7 @@ Uint32 Dblqh::initScanrec(const ScanFragReq* scanFragReq) scanptr.p->rangeScan = rangeScan; scanptr.p->descending = descending; scanptr.p->tupScan = tupScan; + scanptr.p->lcpScan = ScanFragReq::getLcpScanFlag(reqinfo); scanptr.p->scanState = ScanRecord::SCAN_FREE; scanptr.p->scanFlag = ZFALSE; scanptr.p->m_row_id.setNull(); @@ -9416,7 +9464,7 @@ Uint32 Dblqh::initScanrec(const ScanFragReq* scanFragReq) scanptr.p->scanApiOpPtr = scanFragReq->clientOpPtr; scanptr.p->m_last_row = 0; scanptr.p->scanStoredProcId = RNIL; - + scanptr.p->copyPtr = RNIL; if (max_rows == 0 || (max_bytes > 0 && max_rows > max_bytes)){ jam(); return ScanFragRef::ZWRONG_BATCH_SIZE; @@ -9437,8 +9485,10 @@ Uint32 Dblqh::initScanrec(const ScanFragReq* scanFragReq) * !idx uses 1 - (MAX_PARALLEL_SCANS_PER_FRAG - 1) = 1-11 * idx uses from MAX_PARALLEL_SCANS_PER_FRAG - MAX = 12-42) */ - Uint32 start = (rangeScan || tupScan ? MAX_PARALLEL_SCANS_PER_FRAG : 1 ); - Uint32 stop = (rangeScan || tupScan ? MAX_PARALLEL_INDEX_SCANS_PER_FRAG : MAX_PARALLEL_SCANS_PER_FRAG - 1); + tupScan = 0; // Make sure that close tup scan does not start acc scan incorrectly + Uint32 start = (rangeScan || tupScan) ? MAX_PARALLEL_SCANS_PER_FRAG : 1 ; + Uint32 stop = (rangeScan || tupScan) ? MAX_PARALLEL_INDEX_SCANS_PER_FRAG : + MAX_PARALLEL_SCANS_PER_FRAG - 1; stop += start; Uint32 free = tFragPtr.p->m_scanNumberMask.find(start); @@ -9872,6 +9922,8 @@ void Dblqh::execCOPY_FRAGREQ(Signal* signal) fragptr.p->m_scanNumberMask.clear(NR_ScanNo); scanptr.p->scanBlockref = DBTUP_REF; scanptr.p->scanLockHold = ZFALSE; + scanptr.p->m_curr_batch_size_rows = 0; + scanptr.p->m_curr_batch_size_bytes= 0; initScanTc(0, 0, @@ -10074,7 +10126,7 @@ void Dblqh::nextScanConfCopyLab(Signal* signal) initCopyTc(signal, ZDELETE); set_acc_ptr_in_scan_record(scanptr.p, 0, RNIL); tcConP->gci = nextScanConf->gci; - + tcConP->primKeyLen = 0; tcConP->totSendlenAi = 0; tcConP->connectState = TcConnectionrec::COPY_CONNECTED; @@ -10197,6 +10249,12 @@ void Dblqh::copyTupkeyConfLab(Signal* signal) scanptr.i = tcConnectptr.p->tcScanRec; c_scanRecordPool.getPtr(scanptr); ScanRecord* scanP = scanptr.p; + + Uint32 rows = scanP->m_curr_batch_size_rows; + Uint32 accOpPtr= get_acc_ptr_from_scan_record(scanP, rows, false); + ndbassert(accOpPtr != (Uint32)-1); + c_acc->execACCKEY_ORD(signal, accOpPtr); + if (tcConnectptr.p->errorCode != 0) { jam(); closeCopyLab(signal); @@ -18538,6 +18596,21 @@ Dblqh::execDUMP_STATE_ORD(Signal* signal) } ndbrequire(arg != 2308); } + +#ifdef ERROR_INSERT + if (arg == 5712 || arg == 5713) + { + if (arg == 5712) + { + traceopout = &ndbout; + } + else if (arg == 5713) + { + traceopout = tracenrout; + } + SET_ERROR_INSERT_VALUE(arg); + } +#endif }//Dblqh::execDUMP_STATE_ORD() @@ -18702,3 +18775,39 @@ void Dblqh::writeDbgInfoPageHeader(LogPageRecordPtr logP, Uint32 place, logP.p->logPageWord[ZPOS_IN_WRITING]= 1; } +#if defined ERROR_INSERT +void +Dblqh::TRACE_OP_DUMP(const Dblqh::TcConnectionrec* regTcPtr, const char * pos) +{ + (* traceopout) + << "[ " << hex << regTcPtr->transid[0] + << " " << hex << regTcPtr->transid[1] << " ] " << dec + << pos + << " " << (Operation_t)regTcPtr->operation + << " " << regTcPtr->tableref + << "(" << regTcPtr->fragmentid << ")" + << "(" << (regTcPtr->seqNoReplica == 0 ? "P" : "B") << ")" ; + + { + (* traceopout) << "key=[" << hex; + Uint32 i; + for(i = 0; i<regTcPtr->primKeyLen && i < 4; i++){ + (* traceopout) << hex << regTcPtr->tupkeyData[i] << " "; + } + + DatabufPtr regDatabufptr; + regDatabufptr.i = regTcPtr->firstTupkeybuf; + while(i < regTcPtr->primKeyLen) + { + ptrCheckGuard(regDatabufptr, cdatabufFileSize, databuf); + for(Uint32 j = 0; j<4 && i<regTcPtr->primKeyLen; j++, i++) + (* traceopout) << hex << regDatabufptr.p->data[j] << " "; + } + (* traceopout) << "] "; + } + + if (regTcPtr->m_use_rowid) + (* traceopout) << " " << regTcPtr->m_row_id; + (* traceopout) << endl; +} +#endif diff --git a/storage/ndb/src/kernel/blocks/dbtc/DbtcMain.cpp b/storage/ndb/src/kernel/blocks/dbtc/DbtcMain.cpp index 0989807db4a..28ff20e74ef 100644 --- a/storage/ndb/src/kernel/blocks/dbtc/DbtcMain.cpp +++ b/storage/ndb/src/kernel/blocks/dbtc/DbtcMain.cpp @@ -7052,6 +7052,18 @@ void Dbtc::checkScanActiveInFailedLqh(Signal* signal, found = true; } } + + ScanFragList deliv(c_scan_frag_pool, scanptr.p->m_delivered_scan_frags); + for(deliv.first(ptr); !ptr.isNull(); deliv.next(ptr)) + { + jam(); + if (refToNode(ptr.p->lqhBlockref) == failedNodeId) + { + jam(); + found = true; + break; + } + } } if(found){ jam(); @@ -7076,18 +7088,25 @@ Dbtc::nodeFailCheckTransactions(Signal* signal, { jam(); Ptr<ApiConnectRecord> transPtr; + Uint32 TtcTimer = ctcTimer; + Uint32 TapplTimeout = c_appl_timeout_value; for (transPtr.i = transPtrI; transPtr.i < capiConnectFilesize; transPtr.i++) { ptrCheckGuard(transPtr, capiConnectFilesize, apiConnectRecord); + Uint32 state = transPtr.p->apiConnectstate; if (transPtr.p->m_transaction_nodes.get(failedNodeId)) { jam(); - // Force timeout regardless of state - Uint32 save = c_appl_timeout_value; - c_appl_timeout_value = 1; - setApiConTimer(transPtr.i, 0, __LINE__); - timeOutFoundLab(signal, transPtr.i, ZNODEFAIL_BEFORE_COMMIT); - c_appl_timeout_value = save; + + // avoid assertion in timeoutfoundlab + if (state != CS_PREPARE_TO_COMMIT) + { + // Force timeout regardless of state + c_appl_timeout_value = 1; + setApiConTimer(transPtr.i, TtcTimer - 2, __LINE__); + timeOutFoundLab(signal, transPtr.i, ZNODEFAIL_BEFORE_COMMIT); + c_appl_timeout_value = TapplTimeout; + } } // Send CONTINUEB to continue later diff --git a/storage/ndb/src/kernel/blocks/dbtup/Dbtup.hpp b/storage/ndb/src/kernel/blocks/dbtup/Dbtup.hpp index b319932aec7..4ff6e069963 100644 --- a/storage/ndb/src/kernel/blocks/dbtup/Dbtup.hpp +++ b/storage/ndb/src/kernel/blocks/dbtup/Dbtup.hpp @@ -297,7 +297,6 @@ enum TransState { }; enum TupleState { - TUPLE_INITIAL_INSERT = 0, TUPLE_PREPARED = 1, TUPLE_ALREADY_ABORTED = 2, TUPLE_TO_BE_COMMITTED = 3 @@ -305,7 +304,6 @@ enum TupleState { enum State { NOT_INITIALIZED = 0, - COMMON_AREA_PAGES = 1, IDLE = 17, ACTIVE = 18, SYSTEM_RESTART = 19, @@ -1441,7 +1439,6 @@ private: void execSET_VAR_REQ(Signal* signal); void execDROP_TAB_REQ(Signal* signal); void execALTER_TAB_REQ(Signal* signal); - void execTUP_ALLOCREQ(Signal* signal); void execTUP_DEALLOCREQ(Signal* signal); void execTUP_WRITELOG_REQ(Signal* signal); diff --git a/storage/ndb/src/kernel/blocks/dbtup/DbtupExecQuery.cpp b/storage/ndb/src/kernel/blocks/dbtup/DbtupExecQuery.cpp index 1b6fef9de37..33492027ccf 100644 --- a/storage/ndb/src/kernel/blocks/dbtup/DbtupExecQuery.cpp +++ b/storage/ndb/src/kernel/blocks/dbtup/DbtupExecQuery.cpp @@ -202,29 +202,6 @@ Dbtup::receive_attrinfo(Signal* signal, Uint32 op, } } -void Dbtup::execTUP_ALLOCREQ(Signal* signal) -{ - OperationrecPtr regOperPtr; - - jamEntry(); - - regOperPtr.i= signal->theData[0]; - c_operation_pool.getPtr(regOperPtr); - - regOperPtr.p->op_struct.tuple_state= TUPLE_INITIAL_INSERT; - //ndbout_c("execTUP_ALLOCREQ"); - - signal->theData[0]= 0; - signal->theData[1]= ~0 >> MAX_TUPLES_BITS; - signal->theData[2]= (1 << MAX_TUPLES_BITS) - 1; - return; - -mem_error: - jam(); - signal->theData[0]= ZMEM_NOMEM_ERROR; - return; -} - void Dbtup::setChecksum(Tuple_header* tuple_ptr, Tablerec* regTabPtr) @@ -455,13 +432,13 @@ Dbtup::load_diskpage(Signal* signal, ptrCheckGuard(tabptr, cnoOfTablerec, tablerec); Tablerec* regTabPtr = tabptr.p; - if(regOperPtr->op_struct.tuple_state == TUPLE_INITIAL_INSERT) + if(local_key == ~(Uint32)0) { jam(); regOperPtr->op_struct.m_wait_log_buffer= 1; regOperPtr->op_struct.m_load_diskpage_on_commit= 1; return 1; - } + } jam(); Uint32 page_idx= local_key & MAX_TUPLES_PER_PAGE; @@ -663,7 +640,7 @@ void Dbtup::execTUPKEYREQ(Signal* signal) regOperPtr->savepointId= sig1; regOperPtr->op_struct.primary_replica= sig2; - regOperPtr->m_tuple_location.m_page_idx= sig3; + Uint32 pageidx = regOperPtr->m_tuple_location.m_page_idx= sig3; sig1= tupKeyReq->opRef; sig2= tupKeyReq->tcOpIndex; @@ -673,7 +650,7 @@ void Dbtup::execTUPKEYREQ(Signal* signal) req_struct.tc_operation_ptr= sig1; req_struct.TC_index= sig2; req_struct.TC_ref= sig3; - req_struct.frag_page_id= sig4; + Uint32 pageid = req_struct.frag_page_id= sig4; req_struct.m_use_rowid = (TrequestInfo >> 11) & 1; sig1= tupKeyReq->attrBufLen; @@ -706,7 +683,8 @@ void Dbtup::execTUPKEYREQ(Signal* signal) copyAttrinfo(regOperPtr, &cinBuffer[0]); - if(Roptype == ZINSERT && get_tuple_state(regOperPtr)== TUPLE_INITIAL_INSERT) + Uint32 localkey = (pageid << MAX_TUPLES_BITS) + pageidx; + if(Roptype == ZINSERT && localkey == ~0) { // No tuple allocatated yet goto do_insert; @@ -1159,49 +1137,6 @@ Dbtup::prepare_initial_insert(KeyReqStruct *req_struct, disk_undo ? (Tuple_header::DISK_ALLOC | Tuple_header::DISK_INLINE) : 0; } -void -Dbtup::fix_disk_insert_no_mem_insert(KeyReqStruct *req_struct, - Operationrec* regOperPtr, - Tablerec* regTabPtr) -{ - regOperPtr->m_undo_buffer_space= sizeof(Dbtup::Disk_undo::Alloc); - req_struct->check_offset[DD]= regTabPtr->get_check_offset(DD); - - const Uint32 cnt1= regTabPtr->m_attributes[MM].m_no_of_varsize; - const Uint32 cnt2= regTabPtr->m_attributes[DD].m_no_of_varsize; - Uint32 *ptr= req_struct->m_tuple_ptr->get_var_part_ptr(regTabPtr); - - if(cnt1) - { - // Disk part is 32-bit aligned - char *varptr = req_struct->m_var_data[MM].m_data_ptr; - ptr= ALIGN_WORD(varptr + regTabPtr->m_offsets[MM].m_max_var_offset); - } - else - { - ptr -= Tuple_header::HeaderSize; - } - - req_struct->m_disk_ptr= (Tuple_header*)ptr; - - if(cnt2) - { - KeyReqStruct::Var_data *dst= &req_struct->m_var_data[DD]; - ptr=((Tuple_header*)ptr)->m_data+regTabPtr->m_offsets[DD].m_varpart_offset; - dst->m_data_ptr= (char*)(((Uint16*)ptr)+cnt2+1); - dst->m_offset_array_ptr= req_struct->var_pos_array + (cnt1 << 1); - dst->m_var_len_offset= cnt2; - dst->m_max_var_offset= regTabPtr->m_offsets[DD].m_max_var_offset; - } - - // Set all null bits - memset(req_struct->m_disk_ptr->m_null_bits+ - regTabPtr->m_offsets[DD].m_null_offset, 0xFF, - 4*regTabPtr->m_offsets[DD].m_null_words); - req_struct->m_tuple_ptr->m_header_bits = - (Tuple_header::DISK_ALLOC | Tuple_header::DISK_INLINE); -} - int Dbtup::handleInsertReq(Signal* signal, Ptr<Operationrec> regOperPtr, Ptr<Fragrecord> fragPtr, @@ -1215,8 +1150,8 @@ int Dbtup::handleInsertReq(Signal* signal, Tuple_header *tuple_ptr; bool disk = regTabPtr->m_no_of_disk_attributes > 0; - bool mem_insert = get_tuple_state(regOperPtr.p) == TUPLE_INITIAL_INSERT; - bool disk_insert = regOperPtr.p->is_first_operation() && disk; + bool mem_insert = regOperPtr.p->is_first_operation(); + bool disk_insert = mem_insert && disk; bool varsize = regTabPtr->m_attributes[MM].m_no_of_varsize; bool rowid = req_struct->m_use_rowid; Uint32 real_page_id = regOperPtr.p->m_tuple_location.m_page_no; @@ -1244,21 +1179,16 @@ int Dbtup::handleInsertReq(Signal* signal, if(mem_insert) { jam(); - ndbassert(regOperPtr.p->is_first_operation()); // disk insert prepare_initial_insert(req_struct, regOperPtr.p, regTabPtr); } else { - if (!regOperPtr.p->is_first_operation()) - { - Operationrec* prevOp= req_struct->prevOpPtr.p; - ndbassert(prevOp->op_struct.op_type == ZDELETE); - tup_version= prevOp->tupVersion + 1; - - if(!prevOp->is_first_operation()) - org= (Tuple_header*)c_undo_buffer.get_ptr(&prevOp->m_copy_tuple_location); - } - + Operationrec* prevOp= req_struct->prevOpPtr.p; + ndbassert(prevOp->op_struct.op_type == ZDELETE); + tup_version= prevOp->tupVersion + 1; + + if(!prevOp->is_first_operation()) + org= (Tuple_header*)c_undo_buffer.get_ptr(&prevOp->m_copy_tuple_location); if (regTabPtr->need_expand()) expand_tuple(req_struct, sizes, org, regTabPtr, !disk_insert); else @@ -1268,11 +1198,6 @@ int Dbtup::handleInsertReq(Signal* signal, if (disk_insert) { int res; - if (unlikely(!mem_insert)) - { - sizes[DD] = sizes[DD+2] = regTabPtr->m_offsets[DD].m_fix_header_size; - fix_disk_insert_no_mem_insert(req_struct, regOperPtr.p, regTabPtr); - } if (ERROR_INSERTED(4015)) { @@ -1381,6 +1306,7 @@ int Dbtup::handleInsertReq(Signal* signal, } if (unlikely(ptr == 0)) { + jam(); goto alloc_rowid_error; } } @@ -1396,7 +1322,7 @@ int Dbtup::handleInsertReq(Signal* signal, (varsize ? Tuple_header::CHAINED_ROW : 0); regOperPtr.p->m_tuple_location.m_page_no = real_page_id; } - else if(!rowid || !regOperPtr.p->is_first_operation()) + else { int ret; if (ERROR_INSERTED(4020)) @@ -1417,20 +1343,6 @@ int Dbtup::handleInsertReq(Signal* signal, req_struct->m_use_rowid = false; base->m_header_bits &= ~(Uint32)Tuple_header::FREE; } - else - { - if ((req_struct->m_row_id.m_page_no == frag_page_id && - req_struct->m_row_id.m_page_idx == regOperPtr.p->m_tuple_location.m_page_idx)) - { - ndbout_c("no mem insert but rowid (same)"); - base->m_header_bits &= ~(Uint32)Tuple_header::FREE; - } - else - { - // no mem insert, but rowid - ndbrequire(false); - } - } base->m_header_bits |= Tuple_header::ALLOC & (regOperPtr.p->is_first_operation() ? ~0 : 1); diff --git a/storage/ndb/src/kernel/blocks/dbtup/DbtupGen.cpp b/storage/ndb/src/kernel/blocks/dbtup/DbtupGen.cpp index d1580f8da54..8a68905cef9 100644 --- a/storage/ndb/src/kernel/blocks/dbtup/DbtupGen.cpp +++ b/storage/ndb/src/kernel/blocks/dbtup/DbtupGen.cpp @@ -89,7 +89,6 @@ Dbtup::Dbtup(Block_context& ctx, Pgman* pgman) addRecSignal(GSN_DROP_TAB_REQ, &Dbtup::execDROP_TAB_REQ); - addRecSignal(GSN_TUP_ALLOCREQ, &Dbtup::execTUP_ALLOCREQ); addRecSignal(GSN_TUP_DEALLOCREQ, &Dbtup::execTUP_DEALLOCREQ); addRecSignal(GSN_TUP_WRITELOG_REQ, &Dbtup::execTUP_WRITELOG_REQ); diff --git a/storage/ndb/src/kernel/blocks/dbtup/DbtupScan.cpp b/storage/ndb/src/kernel/blocks/dbtup/DbtupScan.cpp index 43875911f7f..3ebfbd4aaa9 100644 --- a/storage/ndb/src/kernel/blocks/dbtup/DbtupScan.cpp +++ b/storage/ndb/src/kernel/blocks/dbtup/DbtupScan.cpp @@ -53,7 +53,10 @@ Dbtup::execACC_SCANREQ(Signal* signal) Fragrecord& frag = *fragPtr.p; // flags Uint32 bits = 0; - if (frag.m_lcp_scan_op == RNIL) { + + if (!AccScanReq::getLcpScanFlag(req->requestInfo) || + tablePtr.p->m_no_of_disk_attributes == 0) + { // seize from pool and link to per-fragment list LocalDLList<ScanOp> list(c_scanOpPool, frag.m_scanList); if (! list.seize(scanPtr)) { @@ -63,23 +66,25 @@ Dbtup::execACC_SCANREQ(Signal* signal) if (!AccScanReq::getNoDiskScanFlag(req->requestInfo) && tablePtr.p->m_no_of_disk_attributes) { - bits |= ScanOp::SCAN_DD; + bits |= ScanOp::SCAN_DD; } bool mm = (bits & ScanOp::SCAN_DD); if (tablePtr.p->m_attributes[mm].m_no_of_varsize > 0) { bits |= ScanOp::SCAN_VS; - // disk pages have fixed page format - ndbrequire(! (bits & ScanOp::SCAN_DD)); + // disk pages have fixed page format + ndbrequire(! (bits & ScanOp::SCAN_DD)); } if (! AccScanReq::getReadCommittedFlag(req->requestInfo)) { - if (AccScanReq::getLockMode(req->requestInfo) == 0) - bits |= ScanOp::SCAN_LOCK_SH; - else - bits |= ScanOp::SCAN_LOCK_EX; + if (AccScanReq::getLockMode(req->requestInfo) == 0) + bits |= ScanOp::SCAN_LOCK_SH; + else + bits |= ScanOp::SCAN_LOCK_EX; } } else { jam(); + // LCP scan and disk + ndbrequire(frag.m_lcp_scan_op == c_lcp_scan_op); c_scanOpPool.getPtr(scanPtr, frag.m_lcp_scan_op); bits |= ScanOp::SCAN_LCP; @@ -156,7 +161,7 @@ Dbtup::execNEXT_SCANREQ(Signal* signal) conf->scanPtr = scan.m_userPtr; unsigned signalLength = 1; sendSignal(scanPtr.p->m_userRef, GSN_NEXT_SCANCONF, - signal, signalLength, JBB); + signal, signalLength, JBB); return; } break; @@ -171,7 +176,7 @@ Dbtup::execNEXT_SCANREQ(Signal* signal) lockReq->requestInfo = AccLockReq::AbortWithConf; lockReq->accOpPtr = scan.m_accLockOp; EXECUTE_DIRECT(DBACC, GSN_ACC_LOCKREQ, - signal, AccLockReq::UndoSignalLength); + signal, AccLockReq::UndoSignalLength); jamEntry(); ndbrequire(lockReq->returnCode == AccLockReq::Success); scan.m_state = ScanOp::Aborting; @@ -182,10 +187,10 @@ Dbtup::execNEXT_SCANREQ(Signal* signal) ndbrequire(scan.m_accLockOp != RNIL); AccLockReq* const lockReq = (AccLockReq*)signal->getDataPtrSend(); lockReq->returnCode = RNIL; - lockReq->requestInfo = AccLockReq::Unlock; + lockReq->requestInfo = AccLockReq::Abort; lockReq->accOpPtr = scan.m_accLockOp; EXECUTE_DIRECT(DBACC, GSN_ACC_LOCKREQ, - signal, AccLockReq::UndoSignalLength); + signal, AccLockReq::UndoSignalLength); jamEntry(); ndbrequire(lockReq->returnCode == AccLockReq::Success); scan.m_accLockOp = RNIL; @@ -433,7 +438,7 @@ Dbtup::execACCKEYCONF(Signal* signal) jam(); AccLockReq* const lockReq = (AccLockReq*)signal->getDataPtrSend(); lockReq->returnCode = RNIL; - lockReq->requestInfo = AccLockReq::Unlock; + lockReq->requestInfo = AccLockReq::Abort; lockReq->accOpPtr = scan.m_accLockOp; EXECUTE_DIRECT(DBACC, GSN_ACC_LOCKREQ, signal, AccLockReq::UndoSignalLength); jamEntry(); @@ -582,12 +587,15 @@ Dbtup::scanNext(Signal* signal, ScanOpPtr scanPtr) Fragrecord& frag = *fragPtr.p; // tuple found Tuple_header* th = 0; + Uint32 thbits = 0; Uint32 loop_count = 0; Uint32 scanGCI = scanPtr.p->m_scanGCI; Uint32 foundGCI; - bool mm = (bits & ScanOp::SCAN_DD); - bool lcp = (bits & ScanOp::SCAN_LCP); + const bool mm = (bits & ScanOp::SCAN_DD); + const bool lcp = (bits & ScanOp::SCAN_LCP); + const bool dirty = (bits & ScanOp::SCAN_LOCK) == 0; + Uint32 lcp_list = fragPtr.p->m_lcp_keep_list; Uint32 size = table.m_offsets[mm].m_fix_header_size + (bits & ScanOp::SCAN_VS ? Tuple_header::HeaderSize + 1: 0); @@ -750,22 +758,22 @@ Dbtup::scanNext(Signal* signal, ScanOpPtr scanPtr) { pos.m_get = ScanPos::Get_next_tuple_fs; th = (Tuple_header*)&page->m_data[key.m_page_idx]; + thbits = th->m_header_bits; + if (likely(! (bits & ScanOp::SCAN_NR))) { - if (! (th->m_header_bits & Tuple_header::FREE)) { - goto found_tuple; - } - else + jam(); + if (! (thbits & Tuple_header::FREE)) { - jam(); - // skip free tuple - } + if (! ((thbits & Tuple_header::ALLOC) && dirty)) + goto found_tuple; + } } else { if ((foundGCI = *th->get_mm_gci(tablePtr.p)) > scanGCI) { - if (! (th->m_header_bits & Tuple_header::FREE)) + if (! (thbits & Tuple_header::FREE)) { jam(); goto found_tuple; @@ -775,9 +783,11 @@ Dbtup::scanNext(Signal* signal, ScanOpPtr scanPtr) goto found_deleted_rowid; } } - else + else if (thbits != Fix_page::FREE_RECORD && + th->m_operation_ptr_i != RNIL) { jam(); + goto found_tuple; // Locked tuple... // skip free tuple } } @@ -793,8 +803,7 @@ Dbtup::scanNext(Signal* signal, ScanOpPtr scanPtr) jam(); { // caller has already set pos.m_get to next tuple - if (! (bits & ScanOp::SCAN_LCP && - th->m_header_bits & Tuple_header::LCP_SKIP)) { + if (! (bits & ScanOp::SCAN_LCP && thbits & Tuple_header::LCP_SKIP)) { Local_key& key_mm = pos.m_key_mm; if (! (bits & ScanOp::SCAN_DD)) { key_mm = pos.m_key; @@ -810,7 +819,11 @@ Dbtup::scanNext(Signal* signal, ScanOpPtr scanPtr) } else { jam(); // clear it so that it will show up in next LCP - th->m_header_bits &= ~(Uint32)Tuple_header::LCP_SKIP; + th->m_header_bits = thbits & ~(Uint32)Tuple_header::LCP_SKIP; + if (tablePtr.p->m_bits & Tablerec::TR_Checksum) { + jam(); + setChecksum(th, tablePtr.p); + } } } break; @@ -833,7 +846,7 @@ Dbtup::scanNext(Signal* signal, ScanOpPtr scanPtr) th = (Tuple_header*)(mmpage->m_data + key_mm.m_page_idx); if ((foundGCI = *th->get_mm_gci(tablePtr.p)) > scanGCI) { - if (! (th->m_header_bits & Tuple_header::FREE)) + if (! (thbits & Tuple_header::FREE)) break; } } @@ -893,7 +906,7 @@ found_lcp_keep: NextScanConf* const conf = (NextScanConf*)signal->getDataPtrSend(); conf->scanPtr = scan.m_userPtr; - conf->accOperationPtr = RNIL + 1; + conf->accOperationPtr = (Uint32)-1; conf->fragId = frag.fragmentId; conf->localKey[0] = lcp_list; conf->localKey[1] = 0; diff --git a/storage/ndb/src/kernel/blocks/dbtux/DbtuxScan.cpp b/storage/ndb/src/kernel/blocks/dbtux/DbtuxScan.cpp index 3c0b2c4ed3f..55315806635 100644 --- a/storage/ndb/src/kernel/blocks/dbtux/DbtuxScan.cpp +++ b/storage/ndb/src/kernel/blocks/dbtux/DbtuxScan.cpp @@ -321,7 +321,7 @@ Dbtux::execNEXT_SCANREQ(Signal* signal) conf->scanPtr = scan.m_userPtr; unsigned signalLength = 1; sendSignal(scanPtr.p->m_userRef, GSN_NEXT_SCANCONF, - signal, signalLength, JBB); + signal, signalLength, JBB); return; } break; @@ -344,7 +344,8 @@ Dbtux::execNEXT_SCANREQ(Signal* signal) lockReq->returnCode = RNIL; lockReq->requestInfo = AccLockReq::AbortWithConf; lockReq->accOpPtr = scan.m_accLockOp; - EXECUTE_DIRECT(DBACC, GSN_ACC_LOCKREQ, signal, AccLockReq::UndoSignalLength); + EXECUTE_DIRECT(DBACC, GSN_ACC_LOCKREQ, signal, + AccLockReq::UndoSignalLength); jamEntry(); ndbrequire(lockReq->returnCode == AccLockReq::Success); scan.m_state = ScanOp::Aborting; @@ -355,9 +356,10 @@ Dbtux::execNEXT_SCANREQ(Signal* signal) ndbrequire(scan.m_accLockOp != RNIL); AccLockReq* const lockReq = (AccLockReq*)signal->getDataPtrSend(); lockReq->returnCode = RNIL; - lockReq->requestInfo = AccLockReq::Unlock; + lockReq->requestInfo = AccLockReq::Abort; lockReq->accOpPtr = scan.m_accLockOp; - EXECUTE_DIRECT(DBACC, GSN_ACC_LOCKREQ, signal, AccLockReq::UndoSignalLength); + EXECUTE_DIRECT(DBACC, GSN_ACC_LOCKREQ, signal, + AccLockReq::UndoSignalLength); jamEntry(); ndbrequire(lockReq->returnCode == AccLockReq::Success); scan.m_accLockOp = RNIL; @@ -612,7 +614,7 @@ Dbtux::execACCKEYCONF(Signal* signal) jam(); AccLockReq* const lockReq = (AccLockReq*)signal->getDataPtrSend(); lockReq->returnCode = RNIL; - lockReq->requestInfo = AccLockReq::Unlock; + lockReq->requestInfo = AccLockReq::Abort; lockReq->accOpPtr = scan.m_accLockOp; EXECUTE_DIRECT(DBACC, GSN_ACC_LOCKREQ, signal, AccLockReq::UndoSignalLength); jamEntry(); diff --git a/storage/ndb/src/kernel/blocks/qmgr/QmgrMain.cpp b/storage/ndb/src/kernel/blocks/qmgr/QmgrMain.cpp index 4d5ac377a5a..ebc40cb385d 100644 --- a/storage/ndb/src/kernel/blocks/qmgr/QmgrMain.cpp +++ b/storage/ndb/src/kernel/blocks/qmgr/QmgrMain.cpp @@ -4736,6 +4736,7 @@ Qmgr::execALLOC_NODEID_REQ(Signal * signal) const AllocNodeIdReq * req = (AllocNodeIdReq*)signal->getDataPtr(); Uint32 senderRef = req->senderRef; Uint32 nodeId = req->nodeId; + Uint32 nodeType = req->nodeType; Uint32 error = 0; if (refToBlock(senderRef) != QMGR) // request from management server @@ -4786,7 +4787,9 @@ Qmgr::execALLOC_NODEID_REQ(Signal * signal) NodeRecPtr nodePtr; nodePtr.i = nodeId; ptrAss(nodePtr, nodeRec); - if (nodePtr.p->failState != NORMAL) + if (nodeType != getNodeInfo(nodeId).m_type) + error = AllocNodeIdRef::NodeTypeMismatch; + else if (nodePtr.p->failState != NORMAL) error = AllocNodeIdRef::NodeFailureHandlingNotCompleted; } diff --git a/storage/ndb/src/mgmsrv/MgmtSrvr.cpp b/storage/ndb/src/mgmsrv/MgmtSrvr.cpp index 1e655c29517..3e92a892ddc 100644 --- a/storage/ndb/src/mgmsrv/MgmtSrvr.cpp +++ b/storage/ndb/src/mgmsrv/MgmtSrvr.cpp @@ -122,41 +122,50 @@ MgmtSrvr::logLevelThreadRun() /** * Handle started nodes */ - EventSubscribeReq req; - req = m_event_listner[0].m_logLevel; - req.blockRef = _ownReference; - - SetLogLevelOrd ord; - m_started_nodes.lock(); - while(m_started_nodes.size() > 0){ - Uint32 node = m_started_nodes[0]; - m_started_nodes.erase(0, false); - m_started_nodes.unlock(); + if (m_started_nodes.size() > 0) + { + // calculate max log level + EventSubscribeReq req; + { + LogLevel tmp; + m_event_listner.lock(); + for(int i = m_event_listner.m_clients.size() - 1; i >= 0; i--) + tmp.set_max(m_event_listner[i].m_logLevel); + m_event_listner.unlock(); + req = tmp; + } + req.blockRef = _ownReference; + while (m_started_nodes.size() > 0) + { + Uint32 node = m_started_nodes[0]; + m_started_nodes.erase(0, false); + m_started_nodes.unlock(); - setEventReportingLevelImpl(node, req); - - ord = m_nodeLogLevel[node]; - setNodeLogLevelImpl(node, ord); - - m_started_nodes.lock(); - } + setEventReportingLevelImpl(node, req); + + SetLogLevelOrd ord; + ord = m_nodeLogLevel[node]; + setNodeLogLevelImpl(node, ord); + + m_started_nodes.lock(); + } + } m_started_nodes.unlock(); m_log_level_requests.lock(); - while(m_log_level_requests.size() > 0){ - req = m_log_level_requests[0]; + while (m_log_level_requests.size() > 0) + { + EventSubscribeReq req = m_log_level_requests[0]; m_log_level_requests.erase(0, false); m_log_level_requests.unlock(); - - LogLevel tmp; - tmp = req; - + if(req.blockRef == 0){ req.blockRef = _ownReference; setEventReportingLevelImpl(0, req); } else { - ord = req; + SetLogLevelOrd ord; + ord = req; setNodeLogLevelImpl(req.blockRef, ord); } m_log_level_requests.lock(); @@ -1499,7 +1508,8 @@ int MgmtSrvr::setEventReportingLevelImpl(int nodeId, const EventSubscribeReq& ll) { - INIT_SIGNAL_SENDER(ss,nodeId); + SignalSender ss(theFacade); + ss.lock(); SimpleSignal ssig; EventSubscribeReq * dst = @@ -1508,41 +1518,54 @@ MgmtSrvr::setEventReportingLevelImpl(int nodeId, EventSubscribeReq::SignalLength); *dst = ll; - send(ss,ssig,nodeId,NODE_TYPE_DB); + NodeBitmask nodes; + nodes.clear(); + Uint32 max = (nodeId == 0) ? (nodeId = 1, MAX_NDB_NODES) : nodeId; + for(; nodeId <= max; nodeId++) + { + if (nodeTypes[nodeId] != NODE_TYPE_DB) + continue; + if (okToSendTo(nodeId, true)) + continue; + if (ss.sendSignal(nodeId, &ssig) == SEND_OK) + { + nodes.set(nodeId); + } + } -#if 0 - while (1) + int error = 0; + while (!nodes.isclear()) { SimpleSignal *signal = ss.waitFor(); int gsn = signal->readSignalNumber(); - switch (gsn) { + nodeId = refToNode(signal->header.theSendersBlockRef); + switch (gsn) { case GSN_EVENT_SUBSCRIBE_CONF:{ + nodes.clear(nodeId); break; } case GSN_EVENT_SUBSCRIBE_REF:{ - return SEND_OR_RECEIVE_FAILED; + nodes.clear(nodeId); + error = 1; + break; } case GSN_NF_COMPLETEREP:{ const NFCompleteRep * const rep = CAST_CONSTPTR(NFCompleteRep, signal->getDataPtr()); - if (rep->failedNodeId == nodeId) - return SEND_OR_RECEIVE_FAILED; + nodes.clear(rep->failedNodeId); break; } case GSN_NODE_FAILREP:{ - const NodeFailRep * const rep = - CAST_CONSTPTR(NodeFailRep, signal->getDataPtr()); - if (NodeBitmask::get(rep->theNodes,nodeId)) - return SEND_OR_RECEIVE_FAILED; + // ignore, NF_COMPLETEREP will arrive later break; } default: report_unknown_signal(signal); return SEND_OR_RECEIVE_FAILED; } - } -#endif + if (error) + return SEND_OR_RECEIVE_FAILED; return 0; } @@ -1562,19 +1585,6 @@ MgmtSrvr::setNodeLogLevelImpl(int nodeId, const SetLogLevelOrd & ll) return ss.sendSignal(nodeId, &ssig) == SEND_OK ? 0 : SEND_OR_RECEIVE_FAILED; } -int -MgmtSrvr::send(SignalSender &ss, SimpleSignal &ssig, Uint32 node, Uint32 node_type){ - Uint32 max = (node == 0) ? MAX_NODES : node + 1; - - for(; node < max; node++){ - while(nodeTypes[node] != (int)node_type && node < max) node++; - if(nodeTypes[node] != (int)node_type) - break; - ss.sendSignal(node, &ssig); - } - return 0; -} - //**************************************************************************** //**************************************************************************** @@ -1927,7 +1937,7 @@ MgmtSrvr::get_connected_nodes(NodeBitmask &connected_nodes) const } int -MgmtSrvr::alloc_node_id_req(Uint32 free_node_id) +MgmtSrvr::alloc_node_id_req(NodeId free_node_id, enum ndb_mgm_node_type type) { SignalSender ss(theFacade); ss.lock(); // lock will be released on exit @@ -1940,6 +1950,7 @@ MgmtSrvr::alloc_node_id_req(Uint32 free_node_id) req->senderRef = ss.getOwnRef(); req->senderData = 19; req->nodeId = free_node_id; + req->nodeType = type; int do_send = 1; NodeId nodeId = 0; @@ -2140,7 +2151,7 @@ MgmtSrvr::alloc_node_id(NodeId * nodeId, if (id_found && client_addr != 0) { - int res = alloc_node_id_req(id_found); + int res = alloc_node_id_req(id_found, type); unsigned save_id_found = id_found; switch (res) { diff --git a/storage/ndb/src/mgmsrv/MgmtSrvr.hpp b/storage/ndb/src/mgmsrv/MgmtSrvr.hpp index 63134991ffe..80eef3b1af2 100644 --- a/storage/ndb/src/mgmsrv/MgmtSrvr.hpp +++ b/storage/ndb/src/mgmsrv/MgmtSrvr.hpp @@ -486,8 +486,6 @@ public: private: //************************************************************************** - int send(SignalSender &ss, SimpleSignal &ssig, Uint32 node, Uint32 node_type); - int sendStopMgmd(NodeId nodeId, bool abort, bool stop, @@ -520,7 +518,7 @@ private: */ int getBlockNumber(const BaseString &blockName); - int alloc_node_id_req(Uint32 free_node_id); + int alloc_node_id_req(NodeId free_node_id, enum ndb_mgm_node_type type); //************************************************************************** int _blockNumber; diff --git a/storage/ndb/src/mgmsrv/Services.cpp b/storage/ndb/src/mgmsrv/Services.cpp index 2731bfd422b..beec5a819b7 100644 --- a/storage/ndb/src/mgmsrv/Services.cpp +++ b/storage/ndb/src/mgmsrv/Services.cpp @@ -813,9 +813,8 @@ MgmApiSession::setClusterLogLevel(Parser<MgmApiSession>::Context &, m_mgmsrv.m_event_listner.unlock(); { - LogLevel ll; - ll.setLogLevel(category,level); - m_mgmsrv.m_event_listner.update_max_log_level(ll); + LogLevel tmp; + m_mgmsrv.m_event_listner.update_max_log_level(tmp); } m_output->println(reply); @@ -1302,21 +1301,23 @@ Ndb_mgmd_event_service::log(int eventType, const Uint32* theData, NodeId nodeId) void Ndb_mgmd_event_service::update_max_log_level(const LogLevel &log_level) { - LogLevel tmp= m_logLevel; - tmp.set_max(log_level); + LogLevel tmp = log_level; + m_clients.lock(); + for(int i = m_clients.size() - 1; i >= 0; i--) + tmp.set_max(m_clients[i].m_logLevel); + m_clients.unlock(); update_log_level(tmp); } void Ndb_mgmd_event_service::update_log_level(const LogLevel &tmp) { - if(!(tmp == m_logLevel)){ - m_logLevel = tmp; - EventSubscribeReq req; - req = tmp; - req.blockRef = 0; - m_mgmsrv->m_log_level_requests.push_back(req); - } + m_logLevel = tmp; + EventSubscribeReq req; + req = tmp; + // send update to all nodes + req.blockRef = 0; + m_mgmsrv->m_log_level_requests.push_back(req); } void diff --git a/storage/ndb/src/ndbapi/ClusterMgr.cpp b/storage/ndb/src/ndbapi/ClusterMgr.cpp index b108ed3fd41..63fdb73c49f 100644 --- a/storage/ndb/src/ndbapi/ClusterMgr.cpp +++ b/storage/ndb/src/ndbapi/ClusterMgr.cpp @@ -38,6 +38,7 @@ #include <mgmapi_config_parameters.h> int global_flag_send_heartbeat_now= 0; +int global_flag_skip_invalidate_cache = 0; // Just a C wrapper for threadMain extern "C" @@ -458,11 +459,14 @@ ClusterMgr::reportNodeFailed(NodeId nodeId){ theNode.nfCompleteRep = false; if(noOfAliveNodes == 0) { - theFacade.m_globalDictCache.lock(); - theFacade.m_globalDictCache.invalidate_all(); - theFacade.m_globalDictCache.unlock(); - m_connect_count ++; - m_cluster_state = CS_waiting_for_clean_cache; + if (!global_flag_skip_invalidate_cache) + { + theFacade.m_globalDictCache.lock(); + theFacade.m_globalDictCache.invalidate_all(); + theFacade.m_globalDictCache.unlock(); + m_connect_count ++; + m_cluster_state = CS_waiting_for_clean_cache; + } NFCompleteRep rep; for(Uint32 i = 1; i<MAX_NODES; i++){ if(theNodes[i].defined && theNodes[i].nfCompleteRep == false){ diff --git a/storage/ndb/src/ndbapi/Ndb.cpp b/storage/ndb/src/ndbapi/Ndb.cpp index 60fdef8111e..5b0a9e9d330 100644 --- a/storage/ndb/src/ndbapi/Ndb.cpp +++ b/storage/ndb/src/ndbapi/Ndb.cpp @@ -1311,6 +1311,35 @@ Ndb::internalize_table_name(const char *external_name) const DBUG_RETURN(ret); } +const BaseString +Ndb::old_internalize_index_name(const NdbTableImpl * table, + const char * external_name) const +{ + BaseString ret; + DBUG_ENTER("old_internalize_index_name"); + DBUG_PRINT("enter", ("external_name: %s, table_id: %d", + external_name, table ? table->m_id : ~0)); + if (!table) + { + DBUG_PRINT("error", ("!table")); + DBUG_RETURN(ret); + } + + if (fullyQualifiedNames) + { + /* Internal index name format <db>/<schema>/<tabid>/<table> */ + ret.assfmt("%s%d%c%s", + theImpl->m_prefix.c_str(), + table->m_id, + table_name_separator, + external_name); + } + else + ret.assign(external_name); + + DBUG_PRINT("exit", ("internal_name: %s", ret.c_str())); + DBUG_RETURN(ret); +} const BaseString Ndb::internalize_index_name(const NdbTableImpl * table, @@ -1328,9 +1357,9 @@ Ndb::internalize_index_name(const NdbTableImpl * table, if (fullyQualifiedNames) { - /* Internal index name format <db>/<schema>/<tabid>/<table> */ + /* Internal index name format sys/def/<tabid>/<table> */ ret.assfmt("%s%d%c%s", - theImpl->m_prefix.c_str(), + theImpl->m_systemPrefix.c_str(), table->m_id, table_name_separator, external_name); diff --git a/storage/ndb/src/ndbapi/NdbDictionary.cpp b/storage/ndb/src/ndbapi/NdbDictionary.cpp index e844dc3369e..c71689d2e81 100644 --- a/storage/ndb/src/ndbapi/NdbDictionary.cpp +++ b/storage/ndb/src/ndbapi/NdbDictionary.cpp @@ -1618,6 +1618,14 @@ NdbDictionary::Dictionary::listIndexes(List& list, return m_impl.listIndexes(list, tab->getTableId()); } +int +NdbDictionary::Dictionary::listIndexes(List& list, + const NdbDictionary::Table &table) const +{ + return m_impl.listIndexes(list, table.getTableId()); +} + + const struct NdbError & NdbDictionary::Dictionary::getNdbError() const { return m_impl.getNdbError(); diff --git a/storage/ndb/src/ndbapi/NdbDictionaryImpl.cpp b/storage/ndb/src/ndbapi/NdbDictionaryImpl.cpp index 42428c49e26..22a5d2f20a5 100644 --- a/storage/ndb/src/ndbapi/NdbDictionaryImpl.cpp +++ b/storage/ndb/src/ndbapi/NdbDictionaryImpl.cpp @@ -56,7 +56,6 @@ DBUG_RETURN(b);\ } -extern Uint64 g_latest_trans_gci; int ndb_dictionary_is_mysqld = 0; bool @@ -1509,9 +1508,21 @@ NdbTableImpl * NdbDictionaryImpl::getIndexTable(NdbIndexImpl * index, NdbTableImpl * table) { + const char *current_db= m_ndb.getDatabaseName(); + NdbTableImpl *index_table; const BaseString internalName( m_ndb.internalize_index_name(table, index->getName())); - return getTable(m_ndb.externalizeTableName(internalName.c_str())); + // Get index table in system database + m_ndb.setDatabaseName(NDB_SYSTEM_DATABASE); + index_table= getTable(m_ndb.externalizeTableName(internalName.c_str())); + m_ndb.setDatabaseName(current_db); + if (!index_table) + { + // Index table not found + // Try geting index table in current database (old format) + index_table= getTable(m_ndb.externalizeTableName(internalName.c_str())); + } + return index_table; } #if 0 @@ -4223,7 +4234,6 @@ NdbDictInterface::execWAIT_GCP_CONF(NdbApiSignal* signal, { const WaitGCPConf * const conf= CAST_CONSTPTR(WaitGCPConf, signal->getDataPtr()); - g_latest_trans_gci= conf->gcp; m_waiter.signal(NO_WAIT); } diff --git a/storage/ndb/src/ndbapi/NdbDictionaryImpl.hpp b/storage/ndb/src/ndbapi/NdbDictionaryImpl.hpp index 9b63acf6d36..b6961edd019 100644 --- a/storage/ndb/src/ndbapi/NdbDictionaryImpl.hpp +++ b/storage/ndb/src/ndbapi/NdbDictionaryImpl.hpp @@ -1020,6 +1020,33 @@ NdbDictionaryImpl::getIndexGlobal(const char * index_name, } break; } + { + // Index not found, try old format + const BaseString + old_internal_indexname(m_ndb.old_internalize_index_name(&ndbtab, + index_name)); + retry= 2; + while (retry) + { + NdbTableImpl *tab= + fetchGlobalTableImplRef(InitIndex(old_internal_indexname, + index_name, ndbtab)); + if (tab) + { + // tab->m_index sould be set. otherwise tab == 0 + NdbIndexImpl *idx= tab->m_index; + if (idx->m_table_id != (unsigned)ndbtab.getObjectId() || + idx->m_table_version != (unsigned)ndbtab.getObjectVersion()) + { + releaseIndexGlobal(*idx, 1); + retry--; + continue; + } + DBUG_RETURN(idx); + } + break; + } + } m_error.code= 4243; DBUG_RETURN(0); } @@ -1086,17 +1113,41 @@ NdbDictionaryImpl::getIndex(const char* index_name, index_name, prim)); if (!tab) - goto err; + goto retry; info= Ndb_local_table_info::create(tab, 0); if (!info) - goto err; + goto retry; m_localHash.put(internal_indexname.c_str(), info); } else tab= info->m_table_impl; return tab->m_index; + +retry: + // Index not found, try fetching it from current database + const BaseString + old_internal_indexname(m_ndb.old_internalize_index_name(&prim, index_name)); + + info= m_localHash.get(old_internal_indexname.c_str()); + if (info == 0) + { + tab= fetchGlobalTableImplRef(InitIndex(old_internal_indexname, + index_name, + prim)); + if (!tab) + goto err; + + info= Ndb_local_table_info::create(tab, 0); + if (!info) + goto err; + m_localHash.put(old_internal_indexname.c_str(), info); + } + else + tab= info->m_table_impl; + + return tab->m_index; err: m_error.code= 4243; diff --git a/storage/ndb/src/ndbapi/NdbEventOperationImpl.cpp b/storage/ndb/src/ndbapi/NdbEventOperationImpl.cpp index a5fbd84e5b0..c8f05f3a96c 100644 --- a/storage/ndb/src/ndbapi/NdbEventOperationImpl.cpp +++ b/storage/ndb/src/ndbapi/NdbEventOperationImpl.cpp @@ -1933,15 +1933,16 @@ static struct Ev_t { enum_DEL = NdbDictionary::Event::_TE_DELETE, enum_UPD = NdbDictionary::Event::_TE_UPDATE, enum_NUL = NdbDictionary::Event::_TE_NUL, - enum_ERR = 255 + enum_IDM = 254, // idempotent op possibly allowed on NF + enum_ERR = 255 // always impossible }; int t1, t2, t3; } ev_t[] = { - { Ev_t::enum_INS, Ev_t::enum_INS, Ev_t::enum_ERR }, + { Ev_t::enum_INS, Ev_t::enum_INS, Ev_t::enum_IDM }, { Ev_t::enum_INS, Ev_t::enum_DEL, Ev_t::enum_NUL }, //ok { Ev_t::enum_INS, Ev_t::enum_UPD, Ev_t::enum_INS }, //ok { Ev_t::enum_DEL, Ev_t::enum_INS, Ev_t::enum_UPD }, //ok - { Ev_t::enum_DEL, Ev_t::enum_DEL, Ev_t::enum_ERR }, + { Ev_t::enum_DEL, Ev_t::enum_DEL, Ev_t::enum_IDM }, { Ev_t::enum_DEL, Ev_t::enum_UPD, Ev_t::enum_ERR }, { Ev_t::enum_UPD, Ev_t::enum_INS, Ev_t::enum_ERR }, { Ev_t::enum_UPD, Ev_t::enum_DEL, Ev_t::enum_DEL }, //ok @@ -2010,6 +2011,34 @@ NdbEventBuffer::merge_data(const SubTableData * const sdata, } assert(tp != 0 && tp->t3 != Ev_t::enum_ERR); + if (tp->t3 == Ev_t::enum_IDM) { + LinearSectionPtr (&ptr1)[3] = data->ptr; + + /* + * TODO + * - can get data in INS ptr2[2] which is supposed to be empty + * - can get extra data in DEL ptr2[2] + * - why does DBUG_PRINT not work in this file ??? + * + * replication + bug#19872 can ignore this since merge is on + * only for tables with explicit PK and before data is not used + */ + const int maxsec = 1; // ignore section 2 + + int i; + for (i = 0; i <= maxsec; i++) { + if (ptr1[i].sz != ptr2[i].sz || + memcmp(ptr1[i].p, ptr2[i].p, ptr1[i].sz << 2) != 0) { + DBUG_PRINT("info", ("idempotent op %d*%d data differs in sec %d", + tp->t1, tp->t2, i)); + assert(false); + DBUG_RETURN_EVENT(-1); + } + } + DBUG_PRINT("info", ("idempotent op %d*%d data ok", tp->t1, tp->t2)); + DBUG_RETURN_EVENT(0); + } + // save old data EventBufData olddata = *data; data->memory = 0; diff --git a/storage/ndb/src/ndbapi/NdbImpl.hpp b/storage/ndb/src/ndbapi/NdbImpl.hpp index 82795550381..3b7b8cf44fb 100644 --- a/storage/ndb/src/ndbapi/NdbImpl.hpp +++ b/storage/ndb/src/ndbapi/NdbImpl.hpp @@ -93,6 +93,8 @@ public: m_schemaname.c_str(), table_name_separator); } + BaseString m_systemPrefix; // Buffer for preformatted for <sys>/<def>/ + /** * NOTE free lists must be _after_ theNdbObjectIdMap take * assure that destructors are run in correct order diff --git a/storage/ndb/src/ndbapi/NdbScanOperation.cpp b/storage/ndb/src/ndbapi/NdbScanOperation.cpp index 6a4e657d172..7379433e1d5 100644 --- a/storage/ndb/src/ndbapi/NdbScanOperation.cpp +++ b/storage/ndb/src/ndbapi/NdbScanOperation.cpp @@ -162,7 +162,7 @@ NdbScanOperation::readTuples(NdbScanOperation::LockMode lm, return -1; } - m_keyInfo = lockExcl ? 1 : 0; + m_keyInfo = ((scan_flags & SF_KeyInfo) || lockExcl) ? 1 : 0; bool tupScan = (scan_flags & SF_TupScan); #if 1 // XXX temp for testing @@ -478,10 +478,14 @@ int NdbScanOperation::nextResultImpl(bool fetchAllowed, bool forceSend) */ PollGuard poll_guard(tp, &theNdb->theImpl->theWaiter, theNdb->theNdbBlockNumber); - if(theError.code) - return -1; - Uint32 seq = theNdbCon->theNodeSequence; + const Uint32 seq = theNdbCon->theNodeSequence; + + if(theError.code) + { + goto err4; + } + if(seq == tp->getNodeSequence(nodeId) && send_next_scan(idx, false) == 0) { @@ -564,6 +568,10 @@ int NdbScanOperation::nextResultImpl(bool fetchAllowed, bool forceSend) if(theError.code == 0) setErrorCode(4028); // seq changed = Node fail break; + case -4: +err4: + setErrorCode(theError.code); + break; } theNdbCon->theTransactionIsStarted = false; @@ -942,6 +950,12 @@ NdbScanOperation::takeOverScanOp(OperationType opType, NdbTransaction* pTrans) if (newOp == NULL){ return NULL; } + if (!m_keyInfo) + { + // Cannot take over lock if no keyinfo was requested + setErrorCodeAbort(4604); + return NULL; + } pTrans->theSimpleState = 0; assert(tRecAttr->get_size_in_bytes() > 0); @@ -950,12 +964,16 @@ NdbScanOperation::takeOverScanOp(OperationType opType, NdbTransaction* pTrans) newOp->theTupKeyLen = len; newOp->theOperationType = opType; - if (opType == DeleteRequest) { - newOp->theStatus = GetValue; - } else { - newOp->theStatus = SetValue; + switch (opType) { + case (ReadRequest): + newOp->theLockMode = theLockMode; + // Fall through + case (DeleteRequest): + newOp->theStatus = GetValue; + break; + default: + newOp->theStatus = SetValue; } - const Uint32 * src = (Uint32*)tRecAttr->aRef(); const Uint32 tScanInfo = src[len] & 0x3FFFF; const Uint32 tTakeOverFragment = src[len] >> 20; diff --git a/storage/ndb/src/ndbapi/NdbTransaction.cpp b/storage/ndb/src/ndbapi/NdbTransaction.cpp index 3158dca5c40..916135b12d5 100644 --- a/storage/ndb/src/ndbapi/NdbTransaction.cpp +++ b/storage/ndb/src/ndbapi/NdbTransaction.cpp @@ -32,6 +32,8 @@ #include <signaldata/TcKeyFailConf.hpp> #include <signaldata/TcHbRep.hpp> +Uint64 g_latest_trans_gci = 0; + /***************************************************************************** NdbTransaction( Ndb* aNdb ); @@ -1568,6 +1570,9 @@ NdbTransaction::receiveTC_COMMITCONF(const TcCommitConf * commitConf) theCommitStatus = Committed; theCompletionStatus = CompletedSuccess; theGlobalCheckpointId = commitConf->gci; + // theGlobalCheckpointId == 0 if NoOp transaction + if (theGlobalCheckpointId) + g_latest_trans_gci = theGlobalCheckpointId; return 0; } else { #ifdef NDB_NO_DROPPED_SIGNAL @@ -1746,6 +1751,8 @@ from other transactions. if (tCommitFlag == 1) { theCommitStatus = Committed; theGlobalCheckpointId = tGCI; + assert(tGCI); + g_latest_trans_gci = tGCI; } else if ((tNoComp >= tNoSent) && (theLastExecOpInList->theCommitIndicator == 1)){ @@ -1922,6 +1929,8 @@ NdbTransaction::receiveTCINDXCONF(const TcIndxConf * indxConf, if (tCommitFlag == 1) { theCommitStatus = Committed; theGlobalCheckpointId = tGCI; + assert(tGCI); + g_latest_trans_gci = tGCI; } else if ((tNoComp >= tNoSent) && (theLastExecOpInList->theCommitIndicator == 1)){ /**********************************************************************/ diff --git a/storage/ndb/src/ndbapi/Ndbif.cpp b/storage/ndb/src/ndbapi/Ndbif.cpp index 7799a71749e..ecaf6a3f435 100644 --- a/storage/ndb/src/ndbapi/Ndbif.cpp +++ b/storage/ndb/src/ndbapi/Ndbif.cpp @@ -46,7 +46,6 @@ #include <EventLogger.hpp> extern EventLogger g_eventLogger; -Uint64 g_latest_trans_gci= 0; /****************************************************************************** * int init( int aNrOfCon, int aNrOfOp ); @@ -367,7 +366,6 @@ Ndb::handleReceivedSignal(NdbApiSignal* aSignal, LinearSectionPtr ptr[3]) tCon = void2con(tFirstDataPtr); if ((tCon->checkMagicNumber() == 0) && (tCon->theSendStatus == NdbTransaction::sendTC_OP)) { - g_latest_trans_gci= keyConf->gci; tReturnCode = tCon->receiveTCKEYCONF(keyConf, tLen); if (tReturnCode != -1) { completedTransaction(tCon); @@ -520,7 +518,6 @@ Ndb::handleReceivedSignal(NdbApiSignal* aSignal, LinearSectionPtr ptr[3]) tCon = void2con(tFirstDataPtr); if ((tCon->checkMagicNumber() == 0) && (tCon->theSendStatus == NdbTransaction::sendTC_COMMIT)) { - g_latest_trans_gci= commitConf->gci; tReturnCode = tCon->receiveTC_COMMITCONF(commitConf); if (tReturnCode != -1) { completedTransaction(tCon); @@ -855,7 +852,6 @@ Ndb::handleReceivedSignal(NdbApiSignal* aSignal, LinearSectionPtr ptr[3]) tCon = void2con(tFirstDataPtr); if ((tCon->checkMagicNumber() == 0) && (tCon->theSendStatus == NdbTransaction::sendTC_OP)) { - g_latest_trans_gci= indxConf->gci; tReturnCode = tCon->receiveTCINDXCONF(indxConf, tLen); if (tReturnCode != -1) { completedTransaction(tCon); diff --git a/storage/ndb/src/ndbapi/Ndbinit.cpp b/storage/ndb/src/ndbapi/Ndbinit.cpp index 91567e99c01..5c0fb521c36 100644 --- a/storage/ndb/src/ndbapi/Ndbinit.cpp +++ b/storage/ndb/src/ndbapi/Ndbinit.cpp @@ -219,6 +219,9 @@ NdbImpl::NdbImpl(Ndb_cluster_connection *ndb_cluster_connection, } m_optimized_node_selection= m_ndb_cluster_connection.m_optimized_node_selection; + + m_systemPrefix.assfmt("%s%c%s%c", NDB_SYSTEM_DATABASE, table_name_separator, + NDB_SYSTEM_SCHEMA, table_name_separator); } NdbImpl::~NdbImpl() diff --git a/storage/ndb/src/ndbapi/ndberror.c b/storage/ndb/src/ndbapi/ndberror.c index c05924dacf8..22252960e21 100644 --- a/storage/ndb/src/ndbapi/ndberror.c +++ b/storage/ndb/src/ndbapi/ndberror.c @@ -308,7 +308,7 @@ ErrorBundle ErrorCodes[] = { { 4601, DMEC, AE, "Transaction is not started"}, { 4602, DMEC, AE, "You must call getNdbOperation before executeScan" }, { 4603, DMEC, AE, "There can only be ONE operation in a scan transaction" }, - { 4604, DMEC, AE, "takeOverScanOp, opType must be UpdateRequest or DeleteRequest" }, + { 4604, DMEC, AE, "takeOverScanOp, to take over a scanned row one must explicitly request keyinfo on readTuples call" }, { 4605, DMEC, AE, "You may only call openScanRead or openScanExclusive once for each operation"}, { 4607, DMEC, AE, "There may only be one operation in a scan transaction"}, { 4608, DMEC, AE, "You can not takeOverScan unless you have used openScanExclusive"}, @@ -462,6 +462,7 @@ ErrorBundle ErrorCodes[] = { { 1701, DMEC, AE, "Node already reserved" }, { 1702, DMEC, AE, "Node already connected" }, { 1703, DMEC, AE, "Node failure handling not completed" }, + { 1704, DMEC, AE, "Node type mismatch" }, /** * Still uncategorized diff --git a/storage/ndb/test/include/HugoOperations.hpp b/storage/ndb/test/include/HugoOperations.hpp index c6ecb4c574e..995463e5056 100644 --- a/storage/ndb/test/include/HugoOperations.hpp +++ b/storage/ndb/test/include/HugoOperations.hpp @@ -108,6 +108,8 @@ public: NDBT_ResultRow& get_row(Uint32 idx) { return *rows[idx];} int execute_async(Ndb*, NdbTransaction::ExecType, NdbTransaction::AbortOption = NdbTransaction::AbortOnError); + int execute_async_prepare(Ndb*, NdbTransaction::ExecType, NdbTransaction::AbortOption = NdbTransaction::AbortOnError); + int wait_async(Ndb*, int timeout = -1); protected: diff --git a/storage/ndb/test/ndbapi/testBasic.cpp b/storage/ndb/test/ndbapi/testBasic.cpp index d45a8ecb7b1..bcbadbc627b 100644 --- a/storage/ndb/test/ndbapi/testBasic.cpp +++ b/storage/ndb/test/ndbapi/testBasic.cpp @@ -1090,11 +1090,6 @@ runMassiveRollback4(NDBT_Context* ctx, NDBT_Step* step){ ok = false; break; } - if (hugoOps.execute_NoCommit(pNdb) != 0) - { - ok = false; - break; - } } hugoOps.execute_Rollback(pNdb); CHECK(hugoOps.closeTransaction(pNdb) == 0); @@ -1199,6 +1194,62 @@ runTupErrors(NDBT_Context* ctx, NDBT_Step* step){ return NDBT_OK; } +int +runInsertError(NDBT_Context* ctx, NDBT_Step* step){ + + int result = NDBT_OK; + HugoOperations hugoOp1(*ctx->getTab()); + HugoOperations hugoOp2(*ctx->getTab()); + Ndb* pNdb = GETNDB(step); + + NdbRestarter restarter; + restarter.insertErrorInAllNodes(4017); + const Uint32 LOOPS = 10; + for (Uint32 i = 0; i<LOOPS; i++) + { + CHECK(hugoOp1.startTransaction(pNdb) == 0); + CHECK(hugoOp1.pkInsertRecord(pNdb, 1) == 0); + + CHECK(hugoOp2.startTransaction(pNdb) == 0); + CHECK(hugoOp2.pkReadRecord(pNdb, 1, 1) == 0); + + CHECK(hugoOp1.execute_async_prepare(pNdb, NdbTransaction::Commit) == 0); + CHECK(hugoOp2.execute_async_prepare(pNdb, NdbTransaction::Commit) == 0); + hugoOp1.wait_async(pNdb); + hugoOp2.wait_async(pNdb); + CHECK(hugoOp1.closeTransaction(pNdb) == 0); + CHECK(hugoOp2.closeTransaction(pNdb) == 0); + } + + restarter.insertErrorInAllNodes(0); + + return result; +} + +int +runInsertError2(NDBT_Context* ctx, NDBT_Step* step){ + int result = NDBT_OK; + HugoOperations hugoOp1(*ctx->getTab()); + Ndb* pNdb = GETNDB(step); + + NdbRestarter restarter; + restarter.insertErrorInAllNodes(4017); + + const Uint32 LOOPS = 1; + for (Uint32 i = 0; i<LOOPS; i++) + { + CHECK(hugoOp1.startTransaction(pNdb) == 0); + CHECK(hugoOp1.pkInsertRecord(pNdb, 1) == 0); + CHECK(hugoOp1.pkDeleteRecord(pNdb, 1) == 0); + + hugoOp1.execute_NoCommit(pNdb); + CHECK(hugoOp1.closeTransaction(pNdb) == 0); + } + + restarter.insertErrorInAllNodes(0); + return NDBT_OK; +} + NDBT_TESTSUITE(testBasic); TESTCASE("PkInsert", "Verify that we can insert and delete from this table using PK" @@ -1449,16 +1500,16 @@ TESTCASE("MassiveTransaction", INITIALIZER(runLoadTable2); FINALIZER(runClearTable2); } -TESTCASE("Fill", - "Verify what happens when we fill the db" ){ - INITIALIZER(runFillTable); - INITIALIZER(runPkRead); - FINALIZER(runClearTable2); -} TESTCASE("TupError", "Verify what happens when we fill the db" ){ INITIALIZER(runTupErrors); } +TESTCASE("InsertError", "" ){ + INITIALIZER(runInsertError); +} +TESTCASE("InsertError2", "" ){ + INITIALIZER(runInsertError2); +} NDBT_TESTSUITE_END(testBasic); #if 0 @@ -1469,6 +1520,12 @@ TESTCASE("ReadConsistency", STEP(runReadOne); FINALIZER(runClearTable2); } +TESTCASE("Fill", + "Verify what happens when we fill the db" ){ + INITIALIZER(runFillTable); + INITIALIZER(runPkRead); + FINALIZER(runClearTable2); +} #endif int main(int argc, const char** argv){ diff --git a/storage/ndb/test/ndbapi/testNodeRestart.cpp b/storage/ndb/test/ndbapi/testNodeRestart.cpp index aed0b39f196..5474837228a 100644 --- a/storage/ndb/test/ndbapi/testNodeRestart.cpp +++ b/storage/ndb/test/ndbapi/testNodeRestart.cpp @@ -696,7 +696,10 @@ runBug18612(NDBT_Context* ctx, NDBT_Step* step){ do { int tmp = restarter.getRandomNodeOtherNodeGroup(node1, rand()); if (tmp == -1) - break; + { + ctx->stopTest(); + return NDBT_OK; + } node1 = tmp; } while(nodesmask.get(node1)); @@ -868,6 +871,65 @@ runBug18612SR(NDBT_Context* ctx, NDBT_Step* step){ return NDBT_OK; } +int runBug20185(NDBT_Context* ctx, NDBT_Step* step){ + int result = NDBT_OK; + int loops = ctx->getNumLoops(); + int records = ctx->getNumRecords(); + NdbRestarter restarter; + HugoOperations hugoOps(*ctx->getTab()); + Ndb* pNdb = GETNDB(step); + + const int masterNode = restarter.getMasterNodeId(); + + int dump[] = { 7090, 20 } ; + if (restarter.dumpStateAllNodes(dump, 2)) + return NDBT_FAILED; + + NdbSleep_MilliSleep(3000); + +retry: + if(hugoOps.startTransaction(pNdb) != 0) + return NDBT_FAILED; + + if(hugoOps.pkUpdateRecord(pNdb, 1, 1) != 0) + return NDBT_FAILED; + + if (hugoOps.execute_NoCommit(pNdb) != 0) + return NDBT_FAILED; + + const int node = hugoOps.getTransaction()->getConnectedNodeId(); + if (node != masterNode) + { + hugoOps.closeTransaction(pNdb); + goto retry; + } + + int nodeId; + do { + nodeId = restarter.getDbNodeId(rand() % restarter.getNumDbNodes()); + } while (nodeId == node); + + if (restarter.insertErrorInAllNodes(7030)) + return NDBT_FAILED; + + if (restarter.insertErrorInNode(nodeId, 7031)) + return NDBT_FAILED; + + NdbSleep_MilliSleep(500); + + if (hugoOps.execute_Commit(pNdb) == 0) + return NDBT_FAILED; + + NdbSleep_MilliSleep(3000); + + restarter.waitClusterStarted(); + + if (restarter.dumpStateAllNodes(dump, 1)) + return NDBT_FAILED; + + return NDBT_OK; +} + NDBT_TESTSUITE(testNodeRestart); TESTCASE("NoLoad", @@ -1175,6 +1237,12 @@ TESTCASE("Bug18612SR", STEP(runBug18612SR); FINALIZER(runClearTable); } +TESTCASE("Bug20185", + ""){ + INITIALIZER(runLoadTable); + STEP(runBug20185); + FINALIZER(runClearTable); +} NDBT_TESTSUITE_END(testNodeRestart); int main(int argc, const char** argv){ diff --git a/storage/ndb/test/ndbapi/testOperations.cpp b/storage/ndb/test/ndbapi/testOperations.cpp index 65b406f155d..e116545f7e3 100644 --- a/storage/ndb/test/ndbapi/testOperations.cpp +++ b/storage/ndb/test/ndbapi/testOperations.cpp @@ -665,9 +665,9 @@ main(int argc, const char** argv){ for(Uint32 i = 0; i < 12; i++) { - if(i == 6 || i == 8 || i == 10) + if(false && (i == 6 || i == 8 || i == 10)) continue; - + BaseString name("bug_9749"); name.appfmt("_%d", i); NDBT_TestCaseImpl1 *pt = new NDBT_TestCaseImpl1(&ts, diff --git a/storage/ndb/test/ndbapi/testScan.cpp b/storage/ndb/test/ndbapi/testScan.cpp index ee2dd56c571..d8c45985630 100644 --- a/storage/ndb/test/ndbapi/testScan.cpp +++ b/storage/ndb/test/ndbapi/testScan.cpp @@ -286,15 +286,26 @@ int runRandScanRead(NDBT_Context* ctx, NDBT_Step* step){ int records = ctx->getNumRecords(); int parallelism = ctx->getProperty("Parallelism", 240); int abort = ctx->getProperty("AbortProb", 5); + int tupscan = ctx->getProperty("TupScan", (Uint32)0); int i = 0; HugoTransactions hugoTrans(*ctx->getTab()); while (i<loops && !ctx->isTestStopped()) { g_info << i << ": "; NdbOperation::LockMode lm = (NdbOperation::LockMode)(rand() % 3); + int scan_flags = 0; + + if (tupscan == 1) + scan_flags |= NdbScanOperation::SF_TupScan; + else if (tupscan == 2 && ((rand() & 0x800))) + { + scan_flags |= NdbScanOperation::SF_TupScan; + } + if (hugoTrans.scanReadRecords(GETNDB(step), records, abort, parallelism, - lm) != 0){ + lm, + scan_flags) != 0){ return NDBT_FAILED; } i++; @@ -1320,6 +1331,16 @@ TESTCASE("ScanRead488", STEPS(runRandScanRead, 70); FINALIZER(runClearTable); } +TESTCASE("ScanRead488T", + "Verify scan requirement: It's only possible to have 11 concurrent "\ + "scans per fragment running in Ndb kernel at the same time. "\ + "When this limit is exceeded the scan will be aborted with errorcode "\ + "488."){ + TC_PROPERTY("TupScan", 1); + INITIALIZER(runLoadTable); + STEPS(runRandScanRead, 70); + FINALIZER(runClearTable); +} TESTCASE("ScanRead488O", "Verify scan requirement: It's only possible to have 11 concurrent "\ "scans per fragment running in Ndb kernel at the same time. "\ @@ -1336,6 +1357,7 @@ TESTCASE("ScanRead488_Mixed", "scans per fragment running in Ndb kernel at the same time. "\ "When this limit is exceeded the scan will be aborted with errorcode "\ "488."){ + TC_PROPERTY("TupScan", 2); INITIALIZER(createOrderedPkIndex); INITIALIZER(runLoadTable); STEPS(runRandScanRead, 50); diff --git a/storage/ndb/test/ndbapi/test_event.cpp b/storage/ndb/test/ndbapi/test_event.cpp index 2f2a28fbff2..d8939f06b14 100644 --- a/storage/ndb/test/ndbapi/test_event.cpp +++ b/storage/ndb/test/ndbapi/test_event.cpp @@ -25,7 +25,8 @@ #define GETNDB(ps) ((NDBT_NdbApiStep*)ps)->getNdb() -static int createEvent(Ndb *pNdb, const NdbDictionary::Table &tab) +static int createEvent(Ndb *pNdb, const NdbDictionary::Table &tab, + bool merge_events = false) { char eventName[1024]; sprintf(eventName,"%s_EVENT",tab.getName()); @@ -45,6 +46,7 @@ static int createEvent(Ndb *pNdb, const NdbDictionary::Table &tab) for(int a = 0; a < tab.getNoOfColumns(); a++){ myEvent.addEventColumn(a); } + myEvent.mergeEvents(merge_events); int res = myDict->createEvent(myEvent); // Add event to database @@ -137,7 +139,8 @@ NdbEventOperation *createEventOperation(Ndb *ndb, static int runCreateEvent(NDBT_Context* ctx, NDBT_Step* step) { - if (createEvent(GETNDB(step),* ctx->getTab()) != 0){ + bool merge_events = ctx->getProperty("MergeEvents"); + if (createEvent(GETNDB(step),* ctx->getTab(), merge_events) != 0){ return NDBT_FAILED; } return NDBT_OK; @@ -584,6 +587,8 @@ int runEventApplier(NDBT_Context* ctx, NDBT_Step* step) g_err << "Event operation creation failed on %s" << buf << endl; DBUG_RETURN(NDBT_FAILED); } + bool merge_events = ctx->getProperty("MergeEvents"); + pOp->mergeEvents(merge_events); int i; int n_columns= table->getNoOfColumns(); @@ -616,6 +621,11 @@ int runEventApplier(NDBT_Context* ctx, NDBT_Step* step) while ((pOp= ndb->nextEvent()) != 0) { assert(pOp == pCreate); + + if (pOp->getEventType() >= + NdbDictionary::Event::TE_FIRST_NON_DATA_EVENT) + continue; + int noRetries= 0; do { @@ -640,7 +650,7 @@ int runEventApplier(NDBT_Context* ctx, NDBT_Step* step) goto end; } - + switch (pOp->getEventType()) { case NdbDictionary::Event::TE_INSERT: if (op->writeTuple()) @@ -1607,6 +1617,33 @@ TESTCASE("EventOperationApplier_NR", FINALIZER(runVerify); FINALIZER(runDropShadowTable); } +TESTCASE("MergeEventOperationApplier", + "Verify that if we apply the data we get from merged event " + "operation is the same as the original table" + "NOTE! No errors are allowed!" ){ + TC_PROPERTY("MergeEvents", 1); + INITIALIZER(runCreateEvent); + INITIALIZER(runCreateShadowTable); + STEP(runEventApplier); + STEP(runEventMixedLoad); + FINALIZER(runDropEvent); + FINALIZER(runVerify); + FINALIZER(runDropShadowTable); +} +TESTCASE("MergeEventOperationApplier_NR", + "Verify that if we apply the data we get from merged event " + "operation is the same as the original table" + "NOTE! No errors are allowed!" ){ + TC_PROPERTY("MergeEvents", 1); + INITIALIZER(runCreateEvent); + INITIALIZER(runCreateShadowTable); + STEP(runEventApplier); + STEP(runEventMixedLoad); + STEP(runRestarter); + FINALIZER(runDropEvent); + FINALIZER(runVerify); + FINALIZER(runDropShadowTable); +} TESTCASE("Multi", "Verify that we can work with all tables in parallell" "NOTE! HugoOperations::startTransaction, pTrans != NULL errors, " diff --git a/storage/ndb/test/run-test/daily-basic-tests.txt b/storage/ndb/test/run-test/daily-basic-tests.txt index 3fead45533f..8d893f11288 100644 --- a/storage/ndb/test/run-test/daily-basic-tests.txt +++ b/storage/ndb/test/run-test/daily-basic-tests.txt @@ -199,7 +199,7 @@ max-time: 500 cmd: testBasicAsynch args: -n PkDeleteAsynch -max-time: 500 +max-time: 1000 cmd: testBasic args: -n MassiveRollback T1 T7 D1 D2 @@ -220,6 +220,14 @@ cmd: testBasic args: -n TupError max-time: 500 +cmd: testBasic +args: -n InsertError T1 + +max-time: 500 +cmd: testBasic +args: -n InsertError2 T1 + +max-time: 500 cmd: testTimeout args: T1 @@ -275,6 +283,10 @@ args: -n ScanRead488O -l 10 T6 D1 D2 max-time: 1000 cmd: testScan +args: -n ScanRead488T -l 10 T6 D1 D2 + +max-time: 1000 +cmd: testScan args: -n ScanRead488_Mixed -l 10 T6 D1 D2 max-time: 500 @@ -473,6 +485,10 @@ max-time: 1000 cmd: testNodeRestart args: -n Bug18612SR T1 +max-time: 1000 +cmd: testNodeRestart +args: -n Bug20185 T1 + # # DICT TESTS max-time: 1500 diff --git a/storage/ndb/test/run-test/daily-devel-tests.txt b/storage/ndb/test/run-test/daily-devel-tests.txt index 1a3a87fbc6e..75b85ebb1d2 100644 --- a/storage/ndb/test/run-test/daily-devel-tests.txt +++ b/storage/ndb/test/run-test/daily-devel-tests.txt @@ -216,6 +216,11 @@ args: -n EventOperationApplier_NR -l 2 # max-time: 2500 cmd: test_event +args: -n MergeEventOperationApplier_NR -l 2 + +# +max-time: 2500 +cmd: test_event args: -n Multi # diff --git a/storage/ndb/test/src/HugoOperations.cpp b/storage/ndb/test/src/HugoOperations.cpp index 84ea88388dc..2903cb8810e 100644 --- a/storage/ndb/test/src/HugoOperations.cpp +++ b/storage/ndb/test/src/HugoOperations.cpp @@ -471,16 +471,33 @@ HugoOperations::execute_async(Ndb* pNdb, NdbTransaction::ExecType et, return NDBT_OK; } +int +HugoOperations::execute_async_prepare(Ndb* pNdb, NdbTransaction::ExecType et, + NdbTransaction::AbortOption eao){ + + m_async_reply= 0; + pTrans->executeAsynchPrepare(et, + HugoOperations_async_callback, + this, + eao); + + return NDBT_OK; +} + int HugoOperations::wait_async(Ndb* pNdb, int timeout) { - pNdb->pollNdb(1000); - - if(m_async_reply) + volatile int * wait = &m_async_reply; + while (!* wait) { - if(m_async_return) - ndbout << "ERROR: " << pNdb->getNdbError(m_async_return) << endl; - return m_async_return; + pNdb->sendPollNdb(1000); + + if(* wait) + { + if(m_async_return) + ndbout << "ERROR: " << pNdb->getNdbError(m_async_return) << endl; + return m_async_return; + } } ndbout_c("wait returned nothing..."); return -1; diff --git a/storage/ndb/test/src/NDBT_Test.cpp b/storage/ndb/test/src/NDBT_Test.cpp index 701c9912373..5bec0607255 100644 --- a/storage/ndb/test/src/NDBT_Test.cpp +++ b/storage/ndb/test/src/NDBT_Test.cpp @@ -1110,6 +1110,7 @@ static int opt_timer; static char * opt_remote_mgm = NULL; static char * opt_testname = NULL; static int opt_verbose; +static int opt_seed = 0; static struct my_option my_long_options[] = { @@ -1129,6 +1130,9 @@ static struct my_option my_long_options[] = { "loops", 'l', "Number of loops", (gptr*) &opt_loops, (gptr*) &opt_loops, 0, GET_INT, REQUIRED_ARG, 5, 0, 0, 0, 0, 0 }, + { "seed", 1024, "Random seed", + (gptr*) &opt_seed, (gptr*) &opt_seed, 0, + GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, { "testname", 'n', "Name of test to run", (gptr*) &opt_testname, (gptr*) &opt_testname, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, @@ -1145,6 +1149,8 @@ static struct my_option my_long_options[] = { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0} }; +extern int global_flag_skip_invalidate_cache; + static void usage() { ndb_std_print_version(); @@ -1224,6 +1230,16 @@ int NDBT_TestSuite::execute(int argc, const char** argv){ { return NDBT_ProgramExit(NDBT_FAILED); } + + if (opt_seed == 0) + { + opt_seed = NdbTick_CurrentMillisecond(); + } + ndbout_c("random seed: %u", opt_seed); + srand(opt_seed); + srandom(opt_seed); + + global_flag_skip_invalidate_cache = 1; { Ndb ndb(&con, "TEST_DB"); diff --git a/unittest/Makefile.am b/unittest/Makefile.am index fd7530ee262..ca3291efde0 100644 --- a/unittest/Makefile.am +++ b/unittest/Makefile.am @@ -1,27 +1,15 @@ SUBDIRS = mytap . mysys examples noinst_SCRIPTS = unit +EXTRA_DIST = unit.pl +CLEANFILES = unit -EXTRA_DIST = unit.pl +unittests = mytap mysys -DISTCLEANFILES = unit - -unittests = mysys examples - -.PHONY: all mytap mysys examples test - -test: unit all - @./unit run $(unittests) - -mytap: - cd mytap && $(MAKE) - -mysys: - cd mysys && $(MAKE) - -examples: - cd examples && $(MAKE) +test: unit + ./unit run $(unittests) unit: $(srcdir)/unit.pl - cp $< $@ - chmod +x $@ + cp $(srcdir)/unit.pl $@ + chmod 700 $@ + diff --git a/unittest/README.txt b/unittest/README.txt index fd9641665c7..0d8bb9025d8 100644 --- a/unittest/README.txt +++ b/unittest/README.txt @@ -7,14 +7,14 @@ will be added over time. mytap Source for the MyTAP library mysys Tests for mysys components - bitmap.t.c Unit test for MY_BITMAP - base64.t.c Unit test for base64 encoding functions + bitmap-t.c Unit test for MY_BITMAP + base64-t.c Unit test for base64 encoding functions examples Example unit tests - simple.t.c Example of a standard TAP unit test - skip.t.c Example where some test points are skipped - skip_all.t.c Example of a test where the entire test is skipped - todo.t.c Example where test contain test points that are TODO - no_plan.t.c Example of a test with no plan (avoid this) + simple-t.c Example of a standard TAP unit test + skip-t.c Example where some test points are skipped + skip_all-t.c Example of a test where the entire test is skipped + todo-t.c Example where test contain test points that are TODO + no_plan-t.c Example of a test with no plan (avoid this) Executing unit tests @@ -28,9 +28,12 @@ To make and execute all unit tests in the directory: Adding unit tests ----------------- -Add a file with a name of the format "foo.t.c" to the appropriate +Add a file with a name of the format "foo-t.c" to the appropriate directory and add the following to the Makefile.am in that directory (where ... denotes stuff already there): - noinst_PROGRAMS = ... foo.t - foo_t_c_SOURCES = foo.t.c + noinst_PROGRAMS = ... foo-t + +Note, it's important to have "-t" at the end of the filename, otherwise the +test won't be executed by 'make test' ! + diff --git a/unittest/examples/Makefile.am b/unittest/examples/Makefile.am index 94a67927d12..f3c70b654a1 100644 --- a/unittest/examples/Makefile.am +++ b/unittest/examples/Makefile.am @@ -5,14 +5,5 @@ AM_LDFLAGS = -L$(top_builddir)/unittest/mytap LDADD = -lmytap -noinst_PROGRAMS = simple.t skip.t todo.t skip_all.t no_plan.t +noinst_PROGRAMS = simple-t skip-t todo-t skip_all-t no_plan-t -simple_t_SOURCES = simple.t.c - -skip_t_SOURCES = skip.t.c - -todo_t_SOURCES = todo.t.c - -skip_all_t_SOURCES = skip_all.t.c - -no_plan_t_SOURCES = no_plan.t.c diff --git a/unittest/examples/no_plan.t.c b/unittest/examples/no_plan-t.c index 98e4d06def6..98e4d06def6 100644 --- a/unittest/examples/no_plan.t.c +++ b/unittest/examples/no_plan-t.c diff --git a/unittest/examples/simple.t.c b/unittest/examples/simple-t.c index 866af865327..866af865327 100644 --- a/unittest/examples/simple.t.c +++ b/unittest/examples/simple-t.c diff --git a/unittest/examples/skip.t.c b/unittest/examples/skip-t.c index ef717691700..ef717691700 100644 --- a/unittest/examples/skip.t.c +++ b/unittest/examples/skip-t.c diff --git a/unittest/examples/skip_all.t.c b/unittest/examples/skip_all-t.c index 19b8c1fddaf..19b8c1fddaf 100644 --- a/unittest/examples/skip_all.t.c +++ b/unittest/examples/skip_all-t.c diff --git a/unittest/examples/todo.t.c b/unittest/examples/todo-t.c index 82601bee41c..82601bee41c 100644 --- a/unittest/examples/todo.t.c +++ b/unittest/examples/todo-t.c diff --git a/unittest/mysys/Makefile.am b/unittest/mysys/Makefile.am index 010a7845339..b1e0356bac6 100644 --- a/unittest/mysys/Makefile.am +++ b/unittest/mysys/Makefile.am @@ -7,9 +7,5 @@ AM_LDFLAGS += -L$(top_builddir)/strings -L$(top_builddir)/dbug LDADD = -lmytap -lmysys -ldbug -lmystrings -noinst_PROGRAMS = bitmap.t base64.t - -bitmap_t_SOURCES = bitmap.t.c - -base64_t_SOURCES = base64.t.c +noinst_PROGRAMS = bitmap-t base64-t my_atomic-t diff --git a/unittest/mysys/base64.t.c b/unittest/mysys/base64-t.c index 1b4f2eb2356..6d85964b20d 100644 --- a/unittest/mysys/base64.t.c +++ b/unittest/mysys/base64-t.c @@ -15,9 +15,9 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include <my_global.h> #include <base64.h> #include <tap.h> -#include <stdlib.h> #include <string.h> int diff --git a/unittest/mysys/bitmap.t.c b/unittest/mysys/bitmap-t.c index d5683baff66..d5683baff66 100644 --- a/unittest/mysys/bitmap.t.c +++ b/unittest/mysys/bitmap-t.c diff --git a/unittest/mysys/my_atomic-t.c b/unittest/mysys/my_atomic-t.c new file mode 100644 index 00000000000..8a3fe129b07 --- /dev/null +++ b/unittest/mysys/my_atomic-t.c @@ -0,0 +1,149 @@ +/* Copyright (C) 2006 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include <tap.h> + +#include <my_global.h> +#include <my_sys.h> +#include <my_atomic.h> + +my_atomic_32_t a32,b32,c32; +my_atomic_rwlock_t rwl; + +pthread_attr_t thr_attr; +pthread_mutex_t mutex; +pthread_cond_t cond; +int N; + +/* add and sub a random number in a loop. Must get 0 at the end */ +pthread_handler_t test_atomic_add_handler(void *arg) +{ + int m=*(int *)arg; + int32 x; + for (x=((int)(&m)); m ; m--) + { + x=x*m+0x87654321; + my_atomic_add32(&a32, x, &rwl); + my_atomic_add32(&a32, -x, &rwl); + } + pthread_mutex_lock(&mutex); + N--; + if (!N) pthread_cond_signal(&cond); + pthread_mutex_unlock(&mutex); + return 0; +} + +/* + 1. generate thread number 0..N-1 from b32 + 2. add it to a32 + 3. swap thread numbers in c32 + 4. (optionally) one more swap to avoid 0 as a result + 5. subtract result from a32 + must get 0 in a32 at the end +*/ +pthread_handler_t test_atomic_swap_handler(void *arg) +{ + int m=*(int *)arg; + uint32 x=my_atomic_add32(&b32, 1, &rwl); + + my_atomic_add32(&a32, x, &rwl); + + for (; m ; m--) + x=my_atomic_swap32(&c32, x,&rwl); + + if (!x) + x=my_atomic_swap32(&c32, x,&rwl); + + my_atomic_add32(&a32, -x, &rwl); + + pthread_mutex_lock(&mutex); + N--; + if (!N) pthread_cond_signal(&cond); + pthread_mutex_unlock(&mutex); + return 0; +} + +/* + same as test_atomic_add_handler, but my_atomic_add32 is emulated with + (slower) my_atomic_cas32 +*/ +pthread_handler_t test_atomic_cas_handler(void *arg) +{ + int m=*(int *)arg; + int32 x; + for (x=((int)(&m)); m ; m--) + { + uint32 y=my_atomic_load32(&a32, &rwl); + x=x*m+0x87654321; + while (!my_atomic_cas32(&a32, &y, y+x, &rwl)) ; + while (!my_atomic_cas32(&a32, &y, y-x, &rwl)) ; + } + pthread_mutex_lock(&mutex); + N--; + if (!N) pthread_cond_signal(&cond); + pthread_mutex_unlock(&mutex); + return 0; +} + +void test_atomic(const char *test, pthread_handler handler, int n, int m) +{ + pthread_t t; + ulonglong now=my_getsystime(); + + my_atomic_store32(&a32, 0, &rwl); + my_atomic_store32(&b32, 0, &rwl); + my_atomic_store32(&c32, 0, &rwl); + + diag("Testing %s with %d threads, %d iterations... ", test, n, m); + for (N=n ; n ; n--) + pthread_create(&t, &thr_attr, handler, &m); + + pthread_mutex_lock(&mutex); + while (N) + pthread_cond_wait(&cond, &mutex); + pthread_mutex_unlock(&mutex); + now=my_getsystime()-now; + ok(my_atomic_load32(&a32, &rwl) == 0, + "tested %s in %g secs", test, ((double)now)/1e7); +} + +int main() +{ + int err; + + diag("N CPUs: %d", my_getncpus()); + err= my_atomic_initialize(); + + plan(4); + ok(err == 0, "my_atomic_initialize() returned %d", err); + + pthread_attr_init(&thr_attr); + pthread_attr_setdetachstate(&thr_attr,PTHREAD_CREATE_DETACHED); + pthread_mutex_init(&mutex, 0); + pthread_cond_init(&cond, 0); + my_atomic_rwlock_init(&rwl); + + test_atomic("my_atomic_add32", test_atomic_add_handler, 100,1000000); + test_atomic("my_atomic_swap32", test_atomic_swap_handler, 100,1000000); + test_atomic("my_atomic_cas32", test_atomic_cas_handler, 100,1000000); + + pthread_mutex_destroy(&mutex); + pthread_cond_destroy(&cond); + pthread_attr_destroy(&thr_attr); + my_atomic_rwlock_destroy(&rwl); + return exit_status(); +} + diff --git a/unittest/mytap/t/Makefile.am b/unittest/mytap/t/Makefile.am index 88c31cfeb7f..b685ae7dc1c 100644 --- a/unittest/mytap/t/Makefile.am +++ b/unittest/mytap/t/Makefile.am @@ -5,8 +5,5 @@ AM_LDFLAGS = -L$(top_builddir)/unittest/mytap LDADD = -lmytap -noinst_PROGRAMS = basic.t +noinst_PROGRAMS = basic-t -basic_t_SOURCES = basic.t.c - -all: $(noinst_PROGRAMS) diff --git a/unittest/mytap/t/basic.t.c b/unittest/mytap/t/basic-t.c index bf4c1a9a664..bf4c1a9a664 100644 --- a/unittest/mytap/t/basic.t.c +++ b/unittest/mytap/t/basic-t.c diff --git a/unittest/unit.pl b/unittest/unit.pl index cae394cf9b6..3092a874192 100644 --- a/unittest/unit.pl +++ b/unittest/unit.pl @@ -59,7 +59,7 @@ sub _find_test_files (@) { my @files; find sub { $File::Find::prune = 1 if /^SCCS$/; - push(@files, $File::Find::name) if -x _ && /\.t\z/; + push(@files, $File::Find::name) if -x _ && /-t\z/; }, @dirs; return @files; } diff --git a/vio/Makefile.am b/vio/Makefile.am index e1830fdc636..b57f2453f41 100644 --- a/vio/Makefile.am +++ b/vio/Makefile.am @@ -20,7 +20,7 @@ else yassl_dummy_link_fix= endif INCLUDES= -I$(top_builddir)/include -I$(top_srcdir)/include \ - $(openssl_includes) $(yassl_includes) + $(openssl_includes) LDADD= @CLIENT_EXTRA_LDFLAGS@ $(openssl_libs) $(yassl_libs) pkglib_LIBRARIES= libvio.a noinst_PROGRAMS = test-ssl test-sslserver test-sslclient diff --git a/vio/viosocket.c b/vio/viosocket.c index 0ddceb72e91..78901fedb69 100644 --- a/vio/viosocket.c +++ b/vio/viosocket.c @@ -33,7 +33,8 @@ int vio_read(Vio * vio, gptr buf, int size) { int r; DBUG_ENTER("vio_read"); - DBUG_PRINT("enter", ("sd: %d, buf: 0x%p, size: %d", vio->sd, buf, size)); + DBUG_PRINT("enter", ("sd: %d buf: 0x%lx size: %d", vio->sd, (long) buf, + size)); /* Ensure nobody uses vio_read_buff and vio_read simultaneously */ DBUG_ASSERT(vio->read_end == vio->read_pos); @@ -64,7 +65,8 @@ int vio_read_buff(Vio *vio, gptr buf, int size) int rc; #define VIO_UNBUFFERED_READ_MIN_SIZE 2048 DBUG_ENTER("vio_read_buff"); - DBUG_PRINT("enter", ("sd: %d, buf: 0x%p, size: %d", vio->sd, buf, size)); + DBUG_PRINT("enter", ("sd: %d buf: 0x%lx size: %d", vio->sd, (long) buf, + size)); if (vio->read_pos < vio->read_end) { @@ -102,7 +104,8 @@ int vio_write(Vio * vio, const gptr buf, int size) { int r; DBUG_ENTER("vio_write"); - DBUG_PRINT("enter", ("sd: %d, buf: 0x%p, size: %d", vio->sd, buf, size)); + DBUG_PRINT("enter", ("sd: %d buf: 0x%lx size: %d", vio->sd, (long) buf, + size)); #ifdef __WIN__ r = send(vio->sd, buf, size,0); #else @@ -223,7 +226,7 @@ int vio_keepalive(Vio* vio, my_bool set_keep_alive) int r=0; uint opt = 0; DBUG_ENTER("vio_keepalive"); - DBUG_PRINT("enter", ("sd: %d, set_keep_alive: %d", vio->sd, (int) + DBUG_PRINT("enter", ("sd: %d set_keep_alive: %d", vio->sd, (int) set_keep_alive)); if (vio->type != VIO_TYPE_NAMEDPIPE) { @@ -393,7 +396,8 @@ int vio_read_pipe(Vio * vio, gptr buf, int size) { DWORD length; DBUG_ENTER("vio_read_pipe"); - DBUG_PRINT("enter", ("sd: %d, buf: 0x%p, size: %d", vio->sd, buf, size)); + DBUG_PRINT("enter", ("sd: %d buf: 0x%lx size: %d", vio->sd, (long) buf, + size)); if (!ReadFile(vio->hPipe, buf, size, &length, NULL)) DBUG_RETURN(-1); @@ -407,7 +411,8 @@ int vio_write_pipe(Vio * vio, const gptr buf, int size) { DWORD length; DBUG_ENTER("vio_write_pipe"); - DBUG_PRINT("enter", ("sd: %d, buf: 0x%p, size: %d", vio->sd, buf, size)); + DBUG_PRINT("enter", ("sd: %d buf: 0x%lx size: %d", vio->sd, (long) buf, + size)); if (!WriteFile(vio->hPipe, (char*) buf, size, &length, NULL)) DBUG_RETURN(-1); @@ -452,7 +457,8 @@ int vio_read_shared_memory(Vio * vio, gptr buf, int size) char *current_postion; DBUG_ENTER("vio_read_shared_memory"); - DBUG_PRINT("enter", ("sd: %d, buf: 0x%p, size: %d", vio->sd, buf, size)); + DBUG_PRINT("enter", ("sd: %d buf: 0x%lx size: %d", vio->sd, (long) buf, + size)); remain_local = size; current_postion=buf; @@ -513,7 +519,8 @@ int vio_write_shared_memory(Vio * vio, const gptr buf, int size) char *current_postion; DBUG_ENTER("vio_write_shared_memory"); - DBUG_PRINT("enter", ("sd: %d, buf: 0x%p, size: %d", vio->sd, buf, size)); + DBUG_PRINT("enter", ("sd: %d buf: 0x%lx size: %d", vio->sd, (long) buf, + size)); remain = size; current_postion = buf; |