diff options
299 files changed, 9801 insertions, 3881 deletions
diff --git a/.bzrignore b/.bzrignore index 338bc04fabc..2bc1daf45ca 100644 --- a/.bzrignore +++ b/.bzrignore @@ -2,6 +2,7 @@ *.bb *.bbg *.core +*.d *.da *.gcov *.la @@ -13,10 +14,10 @@ */.pure *~ .*.swp -.deps +.defs.mk .depend .depend.mk -.defs.mk +.deps .gdb_history .gdbinit .libs @@ -287,6 +288,7 @@ help.h include/my_config.h include/my_global.h include/mysql_version.h +include/readline include/readline/*.h include/readline/readline.h include/widec.h @@ -328,7 +330,9 @@ libmysqld/client.c libmysqld/client_settings.h libmysqld/convert.cc libmysqld/derror.cc +libmysqld/discover.cc libmysqld/errmsg.c +libmysqld/examples/client_test.c libmysqld/examples/completion_hash.cc libmysqld/examples/completion_hash.h libmysqld/examples/link_sources @@ -432,6 +436,7 @@ libmysqld/strfunc.cc libmysqld/table.cc libmysqld/thr_malloc.cc libmysqld/time.cc +libmysqld/tztime.cc libmysqld/uniques.cc libmysqld/unireg.cc libtool @@ -485,6 +490,7 @@ mysql-max-4.0.2-alpha-pc-linux-gnu-i686.tar.gz mysql-test/gmon.out mysql-test/install_test_db mysql-test/mysql-test-run +mysql-test/ndb/ndbcluster mysql-test/r/*.reject mysql-test/r/rpl000001.eval mysql-test/r/rpl000002.eval @@ -515,6 +521,130 @@ mysys/test_thr_alarm mysys/test_thr_lock mysys/test_vsnprintf mysys/testhash +ndb/bin/DbAsyncGenerator +ndb/bin/DbCreate +ndb/bin/acid +ndb/bin/async-lmc-bench-l-p10.sh +ndb/bin/async-lmc-bench-l.sh +ndb/bin/async-lmc-bench-p10.sh +ndb/bin/async-lmc-bench.sh +ndb/bin/atrt +ndb/bin/atrt-analyze-result.sh +ndb/bin/atrt-clear-result.sh +ndb/bin/atrt-gather-result.sh +ndb/bin/atrt-setup.sh +ndb/bin/bankCreator +ndb/bin/bankMakeGL +ndb/bin/bankSumAccounts +ndb/bin/bankTimer +ndb/bin/bankTransactionMaker +ndb/bin/bankValidateAllGLs +ndb/bin/basicTransporterTest +ndb/bin/benchronja +ndb/bin/bulk_copy +ndb/bin/copy_tab +ndb/bin/create_all_tabs +ndb/bin/create_index +ndb/bin/create_tab +ndb/bin/delete_all +ndb/bin/desc +ndb/bin/drop_all_tabs +ndb/bin/drop_index +ndb/bin/drop_tab +ndb/bin/flexAsynch +ndb/bin/flexBench +ndb/bin/flexHammer +ndb/bin/flexScan +ndb/bin/flexTT +ndb/bin/hugoCalculator +ndb/bin/hugoFill +ndb/bin/hugoLoad +ndb/bin/hugoLockRecords +ndb/bin/hugoPkDelete +ndb/bin/hugoPkRead +ndb/bin/hugoPkReadRecord +ndb/bin/hugoPkUpdate +ndb/bin/hugoScanRead +ndb/bin/hugoScanUpdate +ndb/bin/index +ndb/bin/index2 +ndb/bin/initronja +ndb/bin/interpreterInTup +ndb/bin/list_tables +ndb/bin/make-config.sh +ndb/bin/mgmtclient +ndb/bin/mgmtsrvr +ndb/bin/mkconfig +ndb/bin/ndb +ndb/bin/ndb_cpcc +ndb/bin/ndb_cpcd +ndb/bin/ndb_rep +ndb/bin/ndbsql +ndb/bin/newton_basic +ndb/bin/newton_br +ndb/bin/newton_pb +ndb/bin/newton_perf +ndb/bin/perfTransporterTest +ndb/bin/printConfig +ndb/bin/printSchemafile +ndb/bin/printSysfile +ndb/bin/redoLogFileReader +ndb/bin/restart +ndb/bin/restarter +ndb/bin/restarter2 +ndb/bin/restarts +ndb/bin/restore +ndb/bin/select_all +ndb/bin/select_count +ndb/bin/telco +ndb/bin/testBackup +ndb/bin/testBank +ndb/bin/testBasic +ndb/bin/testBasicAsynch +ndb/bin/testCopy +ndb/bin/testDataBuffers +ndb/bin/testDict +ndb/bin/testGrep +ndb/bin/testGrepVerify +ndb/bin/testIndex +ndb/bin/testInterpreter +ndb/bin/testKernelDataBuffer +ndb/bin/testLongSig +ndb/bin/testMgm +ndb/bin/testMgmapi +ndb/bin/testNdbApi +ndb/bin/testNodeRestart +ndb/bin/testOIBasic +ndb/bin/testOdbcDriver +ndb/bin/testOperations +ndb/bin/testRestartGci +ndb/bin/testScan +ndb/bin/testScanInterpreter +ndb/bin/testSimplePropertiesSection +ndb/bin/testSystemRestart +ndb/bin/testTimeout +ndb/bin/testTransactions +ndb/bin/test_cpcd +ndb/bin/test_event +ndb/bin/verify_index +ndb/bin/waiter +ndb/config/autom4te.cache/* +ndb/config/config.mk +ndb/examples/ndbapi_example1/ndbapi_example1 +ndb/examples/ndbapi_example2/ndbapi_example2 +ndb/examples/ndbapi_example3/ndbapi_example3 +ndb/examples/ndbapi_example5/ndbapi_example5 +ndb/examples/select_all/select_all +ndb/lib/libMGM_API.so +ndb/lib/libNDB_API.so +ndb/lib/libNDB_ODBC.so +ndb/lib/libNEWTON_API.so +ndb/lib/libNEWTON_BASICTEST_COMMON.so +ndb/lib/libREP_API.so +ndb/lib/libndbclient.so +ndb/lib/libndbclient_extra.so +ndb/src/common/mgmcommon/printConfig/*.d +ndb/src/mgmclient/test_cpcd/*.d pull.log regex/re repl-tests/test-repl-ts/repl-timestamp.master.reject @@ -585,6 +715,7 @@ sql/gen_lex_hash sql/gmon.out sql/lex_hash.h sql/mini_client_errors.c +sql/mysql_tzinfo_to_sql sql/mysqlbinlog sql/mysqld sql/mysqld-purecov @@ -604,6 +735,7 @@ sql/sql_yacc.cc sql/sql_yacc.h sql/sql_yacc.output sql/sql_yacc.yy.orig +sql/test_time sql/udf_example.so sql_error.cc sql_prepare.cc @@ -652,131 +784,6 @@ vio/test-ssl vio/test-sslclient vio/test-sslserver vio/viotest-ssl -ndb/bin/DbAsyncGenerator -ndb/bin/DbCreate -ndb/bin/acid -ndb/bin/async-lmc-bench-l-p10.sh -ndb/bin/async-lmc-bench-l.sh -ndb/bin/async-lmc-bench-p10.sh -ndb/bin/async-lmc-bench.sh -ndb/bin/atrt -ndb/bin/atrt-analyze-result.sh -ndb/bin/atrt-clear-result.sh -ndb/bin/atrt-gather-result.sh -ndb/bin/atrt-setup.sh -ndb/bin/bankCreator -ndb/bin/bankMakeGL -ndb/bin/bankSumAccounts -ndb/bin/bankTimer -ndb/bin/bankTransactionMaker -ndb/bin/bankValidateAllGLs -ndb/bin/basicTransporterTest -ndb/bin/benchronja -ndb/bin/bulk_copy -ndb/bin/copy_tab -ndb/bin/create_all_tabs -ndb/bin/create_index -ndb/bin/create_tab -ndb/bin/delete_all -ndb/bin/desc -ndb/bin/drop_all_tabs -ndb/bin/drop_index -ndb/bin/drop_tab -ndb/bin/flexAsynch -ndb/bin/flexBench -ndb/bin/flexHammer -ndb/bin/flexScan -ndb/bin/flexTT -ndb/bin/hugoCalculator -ndb/bin/hugoFill -ndb/bin/hugoLoad -ndb/bin/hugoLockRecords -ndb/bin/hugoPkDelete -ndb/bin/hugoPkRead -ndb/bin/hugoPkReadRecord -ndb/bin/hugoPkUpdate -ndb/bin/hugoScanRead -ndb/bin/hugoScanUpdate -ndb/bin/index -ndb/bin/index2 -ndb/bin/initronja -ndb/bin/interpreterInTup -ndb/bin/list_tables -ndb/bin/make-config.sh -ndb/bin/mgmtclient -ndb/bin/mgmtsrvr -ndb/bin/mkconfig -ndb/bin/ndb -ndb/bin/ndb_cpcc -ndb/bin/ndb_cpcd -ndb/bin/ndb_rep -ndb/bin/ndbsql -ndb/bin/newton_basic -ndb/bin/newton_br -ndb/bin/newton_pb -ndb/bin/newton_perf -ndb/bin/perfTransporterTest -ndb/bin/printConfig -ndb/bin/printSchemafile -ndb/bin/printSysfile -ndb/bin/redoLogFileReader -ndb/bin/restart -ndb/bin/restarter -ndb/bin/restarter2 -ndb/bin/restarts -ndb/bin/restore -ndb/bin/select_all -ndb/bin/select_count -ndb/bin/telco -ndb/bin/testBackup -ndb/bin/testBank -ndb/bin/testBasic -ndb/bin/testBasicAsynch -ndb/bin/testCopy -ndb/bin/testDataBuffers -ndb/bin/testDict -ndb/bin/testGrep -ndb/bin/testGrepVerify -ndb/bin/testIndex -ndb/bin/testInterpreter -ndb/bin/testKernelDataBuffer -ndb/bin/testLongSig -ndb/bin/testMgm -ndb/bin/testMgmapi -ndb/bin/testNdbApi -ndb/bin/testNodeRestart -ndb/bin/testOIBasic -ndb/bin/testOdbcDriver -ndb/bin/testOperations -ndb/bin/testRestartGci -ndb/bin/testScan -ndb/bin/testScanInterpreter -ndb/bin/testSimplePropertiesSection -ndb/bin/testSystemRestart -ndb/bin/testTimeout -ndb/bin/testTransactions -ndb/bin/test_cpcd -ndb/bin/test_event -ndb/bin/verify_index -ndb/bin/waiter -ndb/examples/ndbapi_example1/ndbapi_example1 -ndb/examples/ndbapi_example2/ndbapi_example2 -ndb/examples/ndbapi_example3/ndbapi_example3 -ndb/examples/ndbapi_example5/ndbapi_example5 -ndb/examples/select_all/select_all -ndb/lib/libMGM_API.so -ndb/lib/libNDB_API.so -ndb/lib/libNDB_ODBC.so -ndb/lib/libNEWTON_API.so -ndb/lib/libNEWTON_BASICTEST_COMMON.so -ndb/lib/libREP_API.so -ndb/lib/libndbclient.so -ndb/lib/libndbclient_extra.so -libmysqld/discover.cc -include/readline -ndb/config/autom4te.cache/* -ndb/config/config.mk -ndb/src/common/mgmcommon/printConfig/*.d -ndb/src/mgmclient/test_cpcd/*.d -*.d -libmysqld/examples/client_test.c +extra/tztime.cc +extra/mysql_tzinfo_to_sql +sql/mysql_tzinfo_to_sql_tztime.cc diff --git a/BitKeeper/etc/logging_ok b/BitKeeper/etc/logging_ok index 7be353a4b4f..bcc5b0130a7 100644 --- a/BitKeeper/etc/logging_ok +++ b/BitKeeper/etc/logging_ok @@ -173,6 +173,7 @@ tom@basil-firewall.home.com tomas@mc05.(none) tomas@poseidon.(none) tomas@poseidon.bredbandsbolaget.se +tomas@poseidon.elisa-laajakaista.fi tomas@poseidon.ndb.mysql.com tonu@hundin.mysql.fi tonu@volk.internalnet diff --git a/Build-tools/Do-compile b/Build-tools/Do-compile index 5d4a1bdb987..6aa36a9ccdb 100755 --- a/Build-tools/Do-compile +++ b/Build-tools/Do-compile @@ -11,7 +11,7 @@ $opt_distribution=$opt_user=$opt_config_env=$opt_config_extra_env=""; $opt_dbd_options=$opt_perl_options=$opt_config_options=$opt_make_options=$opt_suffix=""; $opt_tmp=$opt_version_suffix=""; $opt_help=$opt_delete=$opt_debug=$opt_stage=$opt_no_test=$opt_no_perl=$opt_with_low_memory=$opt_fast_benchmark=$opt_static_client=$opt_static_server=$opt_static_perl=$opt_sur=$opt_with_small_disk=$opt_local_perl=$opt_tcpip=$opt_build_thread=$opt_use_old_distribution=$opt_enable_shared=$opt_no_crash_me=$opt_no_strip=$opt_with_cluster=$opt_with_debug=$opt_no_benchmark=$opt_no_mysqltest=$opt_without_embedded=$opt_readline=0; -$opt_innodb=$opt_bdb=$opt_raid=$opt_libwrap=$opt_clearlogs=$opt_without_ndbcluster=0; +$opt_innodb=$opt_bdb=$opt_raid=$opt_libwrap=$opt_clearlogs=0; GetOptions( "bdb", @@ -57,8 +57,7 @@ GetOptions( "with-other-libc=s", "with-small-disk", "without-embedded", - "clearlogs", - "without-ndbcluster", + "clearlogs", ) || usage(); usage() if ($opt_help); @@ -250,9 +249,15 @@ if ($opt_stage <= 1) $opt_config_options.= " --with-low-memory" if ($opt_with_low_memory); $opt_config_options.= " --with-mysqld-ldflags=-all-static" if ($opt_static_server); $opt_config_options.= " --with-raid" if ($opt_raid); - $opt_config_options.= " --with-readline" if ($opt_readline); + if ($opt_readline) + { + $opt_config_options.= " --with-readline"; + } + else + { + $opt_config_options.= " --with-libedit"; + } $opt_config_options.= " --with-embedded-server" unless ($opt_without_embedded); - $opt_config_options.= " --without-ndbcluster" if ($opt_without_ndbcluster); $opt_config_options.= " --with-ndbcluster" if ($opt_with_cluster); # Only enable InnoDB when requested (required to be able to @@ -308,7 +313,7 @@ if ($opt_stage <= 3) } $flags.= " --no-strip" if ($opt_no_strip || $opt_with_debug); - $flags.= " --with-ndbcluster" if ($opt__with_ndbcluster); + $flags.= " --with-ndbcluster" if ($opt_with_cluster); check_system("scripts/make_binary_distribution --tmp=$opt_tmp --suffix=$opt_suffix $flags",".tar.gz created"); safe_system("mv mysql*.t*gz $pwd/$host"); if (-f "client/.libs/mysqladmin") @@ -347,10 +352,12 @@ $ENV{"LD_LIBRARY_PATH"}= ("$test_dir/lib" . # if ($opt_stage <= 5 && !$opt_no_test && !$opt_no_mysqltest) { + my $flags= ""; + $flags.= " --with-ndbcluster" if ($opt_with_cluster); log_timestamp(); system("mkdir $bench_tmpdir") if (! -d $bench_tmpdir); safe_cd("${test_dir}/mysql-test"); - check_system("./mysql-test-run --warnings --tmpdir=$bench_tmpdir --master_port=$mysql_tcp_port --slave_port=$slave_port --manager-port=$manager_port --no-manager --sleep=10", "tests were successful"); + check_system("./mysql-test-run $flags --warnings --tmpdir=$bench_tmpdir --master_port=$mysql_tcp_port --slave_port=$slave_port --manager-port=$manager_port --no-manager --sleep=10", "tests were successful"); } # @@ -571,7 +578,7 @@ If user is empty then no mail is sent. Set name suffix (e.g. 'com' or '-max') for a distribution --with cluster -Compile with NDB Cluster +Compile and test with NDB Cluster enabled --with-debug Build binaries with debug information (implies "--no-strip") diff --git a/Build-tools/mysql-copyright b/Build-tools/mysql-copyright index b403fdf8c0a..adc4ae34a50 100755 --- a/Build-tools/mysql-copyright +++ b/Build-tools/mysql-copyright @@ -141,13 +141,15 @@ sub main # remove temporary directory chdir($WD) or print "$! Unable to move up one dir\n"; + `cd $WD`; my $cwd = getcwd(); print "current dir is $cwd\n" if $opt_verbose ; - print "deleting temp dir $dir\n" if $opt_verbose; - if (-d $dir) { - system("rm -rf $dir") or print "$! Unable to delete $dir!\n"; + if (-e $dir) { + print "Trying to delete $dir\n" if $opt_verbose; + if ( system("rm -rf $dir")){ + print "$! Unable to delete $dir!\n"; + } } - } exit(0); } @@ -156,14 +158,17 @@ sub main #### mysqld and MySQL client programs have a usage printed with --help. #### This usage includes a copyright, which needs to be modified #### - sub fix_usage_copyright { - my @Cfiles = `find . -type f -name \"*.c*\"`; - foreach my $Cfile (@Cfiles) + my $findlist = `find . -type f -name \"*.c*\"`; + my @files = split("\n", $findlist); + my $cwd = getcwd(); + + foreach my $file (@files) { - chop $Cfile; - `replace "This is free software," "This is commercial software," "and you are welcome to modify and redistribute it under the GPL license" "please see the file MySQLEULA.txt for details" -- "$Cfile"` if -f $Cfile; + next if ! -f $file; + print "processing file $file in cwd $cwd\n" if $opt_verbose; + `replace "This is free software," "This is commercial software," "and you are welcome to modify and redistribute it under the GPL license" "please see the file MySQLEULA.txt for details" -- "$file"` ; } } @@ -173,12 +178,15 @@ sub fix_usage_copyright sub add_copyright { - my @files = `find . -type f -name "*"`; + my $findlist = `find . -type f -name "*"`; + my @files = split("\n", $findlist); + my $cwd = getcwd(); + foreach my $file (@files) { - chop $file; next if ! -f $file; next if -B $file; + print "processing file $file in cwd $cwd\n" if $opt_verbose; `$WD/Build-tools/mysql-copyright-2 "$file"`; } } diff --git a/Build-tools/mysql-copyright-2 b/Build-tools/mysql-copyright-2 index e946ed217d1..a1a870526da 100755 --- a/Build-tools/mysql-copyright-2 +++ b/Build-tools/mysql-copyright-2 @@ -89,6 +89,7 @@ sub add_copyright elsif ($ARGV =~ /\.c$/ || $ARGV =~ /\.cc$/ || $ARGV =~ /\.h$/ || + $ARGV =~ /\.cpp$/ || $ARGV =~ /\.yy$/) { $start_copyright="/* "; diff --git a/Makefile.am b/Makefile.am index aca5d4bade7..6d69ea85eb2 100644 --- a/Makefile.am +++ b/Makefile.am @@ -36,7 +36,7 @@ linked_sources = linked_client_sources linked_server_sources \ CLEANFILES = $(linked_sources) # This is just so that the linking is done early. -config.h: $(linked_sources) +all-local: $(linked_sources) linked_include_sources: cd include; $(MAKE) link_sources diff --git a/VC++Files/InstallShield/4.0.XX-classic/File Groups/Servers.fgl b/VC++Files/InstallShield/4.0.XX-classic/File Groups/Servers.fgl index 8a626c56253..b51c37f8db2 100755 --- a/VC++Files/InstallShield/4.0.XX-classic/File Groups/Servers.fgl +++ b/VC++Files/InstallShield/4.0.XX-classic/File Groups/Servers.fgl @@ -41,31 +41,55 @@ file1=C:\mysql\share\korean\errmsg.txt fulldirectory= [share\charsets] -file15=C:\mysql\share\charsets\latin1.conf -file16=C:\mysql\share\charsets\latin2.conf -file0=C:\mysql\share\charsets\win1251ukr.conf -file17=C:\mysql\share\charsets\latin5.conf +file0=C:\mysql\share\charsets\cp1250.xml +file1=C:\mysql\share\charsets\cp1251.conf +file2=C:\mysql\share\charsets\cp1251.xml +file3=C:\mysql\share\charsets\cp1256.xml file1=C:\mysql\share\charsets\cp1257.conf -file18=C:\mysql\share\charsets\Readme -file2=C:\mysql\share\charsets\croat.conf -file19=C:\mysql\share\charsets\swe7.conf -file3=C:\mysql\share\charsets\danish.conf -file4=C:\mysql\share\charsets\dec8.conf -file5=C:\mysql\share\charsets\dos.conf -file6=C:\mysql\share\charsets\estonia.conf -file7=C:\mysql\share\charsets\german1.conf -file8=C:\mysql\share\charsets\greek.conf -file9=C:\mysql\share\charsets\hebrew.conf -file20=C:\mysql\share\charsets\usa7.conf -file21=C:\mysql\share\charsets\win1250.conf -file10=C:\mysql\share\charsets\hp8.conf -fulldirectory= -file22=C:\mysql\share\charsets\win1251.conf -file11=C:\mysql\share\charsets\hungarian.conf -file23=C:\mysql\share\charsets\cp1251.conf -file12=C:\mysql\share\charsets\Index -file13=C:\mysql\share\charsets\koi8_ru.conf -file14=C:\mysql\share\charsets\koi8_ukr.conf +file4=C:\mysql\share\charsets\cp1257.xml +file5=C:\mysql\share\charsets\cp850.xml +file6=C:\mysql\share\charsets\cp852.xml +file7=C:\mysql\share\charsets\cp866.xml +file8=C:\mysql\share\charsets\croat.conf +file9=C:\mysql\share\charsets\danish.conf +file10=C:\mysql\share\charsets\dec8.conf +file10=C:\mysql\share\charsets\dec8.xml +file11=C:\mysql\share\charsets\dos.conf +file12=C:\mysql\share\charsets\estonia.conf +file13=C:\mysql\share\charsets\geostd8.xml +file14=C:\mysql\share\charsets\german1.conf +file15=C:\mysql\share\charsets\greek.xml +file16=C:\mysql\share\charsets\greek.conf +file17=C:\mysql\share\charsets\hebrew.xml +file18=C:\mysql\share\charsets\hebrew.conf +file19=C:\mysql\share\charsets\hp8.xml +file20=C:\mysql\share\charsets\hp8.conf +file21=C:\mysql\share\charsets\hungarian.conf +file22=C:\mysql\share\charsets\keybcs2.xml +file23=C:\mysql\share\charsets\koi8_ru.conf +file24=C:\mysql\share\charsets\koi8_ukr.conf +file25=C:\mysql\share\charsets\koi8r.xml +file26=C:\mysql\share\charsets\koi8u.xml +file27=C:\mysql\share\charsets\latin1.conf +file28=C:\mysql\share\charsets\latin1.xml +file29=C:\mysql\share\charsets\latin2.conf +file30=C:\mysql\share\charsets\latin2.xml +file31=C:\mysql\share\charsets\latin5.conf +file32=C:\mysql\share\charsets\latin5.xml +file33=C:\mysql\share\charsets\latin7.xml +file34=C:\mysql\share\charsets\macce.xml +file35=C:\mysql\share\charsets\macroman.xml +file36=C:\mysql\share\charsets\swe7.conf +file37=C:\mysql\share\charsets\swe7.xml +file38=C:\mysql\share\charsets\usa7.conf +file39=C:\mysql\share\charsets\win1250.conf +file40=C:\mysql\share\charsets\win1251ukr.conf +file41=C:\mysql\share\charsets\win1251.conf +file42=C:\mysql\share\charsets\Index +file43=C:\mysql\share\charsets\Index.xml +file44=C:\mysql\share\charsets\Readme +file45=C:\mysql\share\charsets\languages.html +fulldirectory= [Embedded\DLL\debug] file0=C:\mysql\embedded\DLL\debug\libmysqld.dll diff --git a/VC++Files/InstallShield/4.0.XX-gpl/File Groups/Servers.fgl b/VC++Files/InstallShield/4.0.XX-gpl/File Groups/Servers.fgl index c54ff378a55..6564512de2c 100755 --- a/VC++Files/InstallShield/4.0.XX-gpl/File Groups/Servers.fgl +++ b/VC++Files/InstallShield/4.0.XX-gpl/File Groups/Servers.fgl @@ -43,31 +43,55 @@ file1=C:\mysql\share\korean\errmsg.txt fulldirectory= [share\charsets] -file15=C:\mysql\share\charsets\latin1.conf -file16=C:\mysql\share\charsets\latin2.conf -file0=C:\mysql\share\charsets\win1251ukr.conf -file17=C:\mysql\share\charsets\latin5.conf +file0=C:\mysql\share\charsets\cp1250.xml +file1=C:\mysql\share\charsets\cp1251.conf +file2=C:\mysql\share\charsets\cp1251.xml +file3=C:\mysql\share\charsets\cp1256.xml file1=C:\mysql\share\charsets\cp1257.conf -file18=C:\mysql\share\charsets\Readme -file2=C:\mysql\share\charsets\croat.conf -file19=C:\mysql\share\charsets\swe7.conf -file3=C:\mysql\share\charsets\danish.conf -file4=C:\mysql\share\charsets\dec8.conf -file5=C:\mysql\share\charsets\dos.conf -file6=C:\mysql\share\charsets\estonia.conf -file7=C:\mysql\share\charsets\german1.conf -file8=C:\mysql\share\charsets\greek.conf -file9=C:\mysql\share\charsets\hebrew.conf -file20=C:\mysql\share\charsets\usa7.conf -file21=C:\mysql\share\charsets\win1250.conf -file10=C:\mysql\share\charsets\hp8.conf -fulldirectory= -file22=C:\mysql\share\charsets\win1251.conf -file11=C:\mysql\share\charsets\hungarian.conf -file23=C:\mysql\share\charsets\cp1251.conf -file12=C:\mysql\share\charsets\Index -file13=C:\mysql\share\charsets\koi8_ru.conf -file14=C:\mysql\share\charsets\koi8_ukr.conf +file4=C:\mysql\share\charsets\cp1257.xml +file5=C:\mysql\share\charsets\cp850.xml +file6=C:\mysql\share\charsets\cp852.xml +file7=C:\mysql\share\charsets\cp866.xml +file8=C:\mysql\share\charsets\croat.conf +file9=C:\mysql\share\charsets\danish.conf +file10=C:\mysql\share\charsets\dec8.conf +file10=C:\mysql\share\charsets\dec8.xml +file11=C:\mysql\share\charsets\dos.conf +file12=C:\mysql\share\charsets\estonia.conf +file13=C:\mysql\share\charsets\geostd8.xml +file14=C:\mysql\share\charsets\german1.conf +file15=C:\mysql\share\charsets\greek.xml +file16=C:\mysql\share\charsets\greek.conf +file17=C:\mysql\share\charsets\hebrew.xml +file18=C:\mysql\share\charsets\hebrew.conf +file19=C:\mysql\share\charsets\hp8.xml +file20=C:\mysql\share\charsets\hp8.conf +file21=C:\mysql\share\charsets\hungarian.conf +file22=C:\mysql\share\charsets\keybcs2.xml +file23=C:\mysql\share\charsets\koi8_ru.conf +file24=C:\mysql\share\charsets\koi8_ukr.conf +file25=C:\mysql\share\charsets\koi8r.xml +file26=C:\mysql\share\charsets\koi8u.xml +file27=C:\mysql\share\charsets\latin1.conf +file28=C:\mysql\share\charsets\latin1.xml +file29=C:\mysql\share\charsets\latin2.conf +file30=C:\mysql\share\charsets\latin2.xml +file31=C:\mysql\share\charsets\latin5.conf +file32=C:\mysql\share\charsets\latin5.xml +file33=C:\mysql\share\charsets\latin7.xml +file34=C:\mysql\share\charsets\macce.xml +file35=C:\mysql\share\charsets\macroman.xml +file36=C:\mysql\share\charsets\swe7.conf +file37=C:\mysql\share\charsets\swe7.xml +file38=C:\mysql\share\charsets\usa7.conf +file39=C:\mysql\share\charsets\win1250.conf +file40=C:\mysql\share\charsets\win1251ukr.conf +file41=C:\mysql\share\charsets\win1251.conf +file42=C:\mysql\share\charsets\Index +file43=C:\mysql\share\charsets\Index.xml +file44=C:\mysql\share\charsets\Readme +file45=C:\mysql\share\charsets\languages.html +fulldirectory= [Embedded\DLL\debug] file0=C:\mysql\embedded\DLL\debug\libmysqld.dll diff --git a/VC++Files/InstallShield/4.0.XX-pro/File Groups/Servers.fgl b/VC++Files/InstallShield/4.0.XX-pro/File Groups/Servers.fgl index 8a626c56253..b51c37f8db2 100755 --- a/VC++Files/InstallShield/4.0.XX-pro/File Groups/Servers.fgl +++ b/VC++Files/InstallShield/4.0.XX-pro/File Groups/Servers.fgl @@ -41,31 +41,55 @@ file1=C:\mysql\share\korean\errmsg.txt fulldirectory= [share\charsets] -file15=C:\mysql\share\charsets\latin1.conf -file16=C:\mysql\share\charsets\latin2.conf -file0=C:\mysql\share\charsets\win1251ukr.conf -file17=C:\mysql\share\charsets\latin5.conf +file0=C:\mysql\share\charsets\cp1250.xml +file1=C:\mysql\share\charsets\cp1251.conf +file2=C:\mysql\share\charsets\cp1251.xml +file3=C:\mysql\share\charsets\cp1256.xml file1=C:\mysql\share\charsets\cp1257.conf -file18=C:\mysql\share\charsets\Readme -file2=C:\mysql\share\charsets\croat.conf -file19=C:\mysql\share\charsets\swe7.conf -file3=C:\mysql\share\charsets\danish.conf -file4=C:\mysql\share\charsets\dec8.conf -file5=C:\mysql\share\charsets\dos.conf -file6=C:\mysql\share\charsets\estonia.conf -file7=C:\mysql\share\charsets\german1.conf -file8=C:\mysql\share\charsets\greek.conf -file9=C:\mysql\share\charsets\hebrew.conf -file20=C:\mysql\share\charsets\usa7.conf -file21=C:\mysql\share\charsets\win1250.conf -file10=C:\mysql\share\charsets\hp8.conf -fulldirectory= -file22=C:\mysql\share\charsets\win1251.conf -file11=C:\mysql\share\charsets\hungarian.conf -file23=C:\mysql\share\charsets\cp1251.conf -file12=C:\mysql\share\charsets\Index -file13=C:\mysql\share\charsets\koi8_ru.conf -file14=C:\mysql\share\charsets\koi8_ukr.conf +file4=C:\mysql\share\charsets\cp1257.xml +file5=C:\mysql\share\charsets\cp850.xml +file6=C:\mysql\share\charsets\cp852.xml +file7=C:\mysql\share\charsets\cp866.xml +file8=C:\mysql\share\charsets\croat.conf +file9=C:\mysql\share\charsets\danish.conf +file10=C:\mysql\share\charsets\dec8.conf +file10=C:\mysql\share\charsets\dec8.xml +file11=C:\mysql\share\charsets\dos.conf +file12=C:\mysql\share\charsets\estonia.conf +file13=C:\mysql\share\charsets\geostd8.xml +file14=C:\mysql\share\charsets\german1.conf +file15=C:\mysql\share\charsets\greek.xml +file16=C:\mysql\share\charsets\greek.conf +file17=C:\mysql\share\charsets\hebrew.xml +file18=C:\mysql\share\charsets\hebrew.conf +file19=C:\mysql\share\charsets\hp8.xml +file20=C:\mysql\share\charsets\hp8.conf +file21=C:\mysql\share\charsets\hungarian.conf +file22=C:\mysql\share\charsets\keybcs2.xml +file23=C:\mysql\share\charsets\koi8_ru.conf +file24=C:\mysql\share\charsets\koi8_ukr.conf +file25=C:\mysql\share\charsets\koi8r.xml +file26=C:\mysql\share\charsets\koi8u.xml +file27=C:\mysql\share\charsets\latin1.conf +file28=C:\mysql\share\charsets\latin1.xml +file29=C:\mysql\share\charsets\latin2.conf +file30=C:\mysql\share\charsets\latin2.xml +file31=C:\mysql\share\charsets\latin5.conf +file32=C:\mysql\share\charsets\latin5.xml +file33=C:\mysql\share\charsets\latin7.xml +file34=C:\mysql\share\charsets\macce.xml +file35=C:\mysql\share\charsets\macroman.xml +file36=C:\mysql\share\charsets\swe7.conf +file37=C:\mysql\share\charsets\swe7.xml +file38=C:\mysql\share\charsets\usa7.conf +file39=C:\mysql\share\charsets\win1250.conf +file40=C:\mysql\share\charsets\win1251ukr.conf +file41=C:\mysql\share\charsets\win1251.conf +file42=C:\mysql\share\charsets\Index +file43=C:\mysql\share\charsets\Index.xml +file44=C:\mysql\share\charsets\Readme +file45=C:\mysql\share\charsets\languages.html +fulldirectory= [Embedded\DLL\debug] file0=C:\mysql\embedded\DLL\debug\libmysqld.dll diff --git a/VC++Files/examples/udf_example/udf_example.def b/VC++Files/examples/udf_example/udf_example.def index c1cfeea63f8..9764343e5f2 100644 --- a/VC++Files/examples/udf_example/udf_example.def +++ b/VC++Files/examples/udf_example/udf_example.def @@ -2,8 +2,17 @@ LIBRARY MYUDF DESCRIPTION 'MySQL Sample for UDF' VERSION 1.0 EXPORTS - metaphon - myfunc_double - myfunc_int - sequence - avgcost
\ No newline at end of file + metaphon_init + metaphon_deinit + metaphon + myfunc_double_init + myfunc_double + myfunc_int + sequence_init + sequence_deinit + sequence + avgcost_init + avgcost_deinit + avgcost_reset + avgcost_add + avgcost diff --git a/VC++Files/winmysqladmin/main.cpp b/VC++Files/winmysqladmin/main.cpp index 6ca29659255..dfb2004a780 100644 --- a/VC++Files/winmysqladmin/main.cpp +++ b/VC++Files/winmysqladmin/main.cpp @@ -1196,7 +1196,7 @@ bool __fastcall TForm1::Shutd() if (IsConnect)
{
mysql_kill(MySQL,mysql_thread_id(MySQL));
- mysql_shutdown(MySQL);
+ mysql_shutdown(MySQL, SHUTDOWN_DEFAULT); StatusLine->SimpleText = "";
}
diff --git a/VC++Files/winmysqladmin/mysql.h b/VC++Files/winmysqladmin/mysql.h index e83babb8fa8..f01b55f5d3f 100644 --- a/VC++Files/winmysqladmin/mysql.h +++ b/VC++Files/winmysqladmin/mysql.h @@ -229,7 +229,9 @@ int STDCALL mysql_real_query(MYSQL *mysql, const char *q, unsigned int length); int STDCALL mysql_create_db(MYSQL *mysql, const char *DB); int STDCALL mysql_drop_db(MYSQL *mysql, const char *DB); -int STDCALL mysql_shutdown(MYSQL *mysql); +int STDCALL mysql_shutdown(MYSQL *mysql, + enum enum_shutdown_level + shutdown_level); int STDCALL mysql_dump_debug_info(MYSQL *mysql); int STDCALL mysql_refresh(MYSQL *mysql, unsigned int refresh_options); diff --git a/VC++Files/winmysqladmin/mysql_com.h b/VC++Files/winmysqladmin/mysql_com.h index 2a1471f735d..0870f340451 100644 --- a/VC++Files/winmysqladmin/mysql_com.h +++ b/VC++Files/winmysqladmin/mysql_com.h @@ -155,6 +155,32 @@ enum enum_field_types { FIELD_TYPE_DECIMAL, FIELD_TYPE_TINY, #define FIELD_TYPE_CHAR FIELD_TYPE_TINY /* For compability */ #define FIELD_TYPE_INTERVAL FIELD_TYPE_ENUM /* For compability */ +enum enum_shutdown_level { + /* + We want levels to be in growing order of hardness. So we leave room + for future intermediate levels. For now, escalating one level is += 10; + later if we insert new levels in between we will need a function + next_shutdown_level(level). Note that DEFAULT does not respect the + growing property. + */ + SHUTDOWN_DEFAULT= 0, /* mapped to WAIT_ALL_BUFFERS for now */ + /* + Here is the list in growing order (the next does the previous plus + something). WAIT_ALL_BUFFERS is what we have now. Others are "this MySQL + server does not support this shutdown level yet". + */ + SHUTDOWN_WAIT_CONNECTIONS= 10, /* wait for existing connections to finish */ + SHUTDOWN_WAIT_TRANSACTIONS= 20, /* wait for existing trans to finish */ + SHUTDOWN_WAIT_STATEMENTS= 30, /* wait for existing updating stmts to finish */ + SHUTDOWN_WAIT_ALL_BUFFERS= 40, /* flush InnoDB buffers */ + SHUTDOWN_WAIT_CRITICAL_BUFFERS= 50, /* flush MyISAM buffs (no corruption) */ + /* Now the 2 levels of the KILL command */ +#if MYSQL_VERSION_ID >= 50000 + KILL_QUERY= 254, +#endif + KILL_CONNECTION= 255 +}; + extern unsigned long max_allowed_packet; extern unsigned long net_buffer_length; diff --git a/acconfig.h b/acconfig.h index 964e0018d86..f9cff3010ca 100644 --- a/acconfig.h +++ b/acconfig.h @@ -84,6 +84,7 @@ #undef HAVE_CHARSET_euckr #undef HAVE_CHARSET_gb2312 #undef HAVE_CHARSET_gbk +#undef HAVE_CHARSET_geostd8 #undef HAVE_CHARSET_greek #undef HAVE_CHARSET_hebrew #undef HAVE_CHARSET_hp8 diff --git a/acinclude.m4 b/acinclude.m4 index 8ef32c8163a..30bb1549715 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -1310,7 +1310,7 @@ AC_DEFUN([MYSQL_CHECK_EXAMPLEDB], [ AC_ARG_WITH([example-storage-engine], [ --with-example-storage-engine - Enable the Example Storge Engine], + Enable the Example Storage Engine], [exampledb="$withval"], [exampledb=no]) AC_MSG_CHECKING([for example storage engine]) @@ -1340,7 +1340,7 @@ AC_DEFUN([MYSQL_CHECK_ARCHIVEDB], [ AC_ARG_WITH([archive-storage-engine], [ --with-archive-storage-engine - Enable the Archive Storge Engine], + Enable the Archive Storage Engine], [archivedb="$withval"], [archivedb=no]) AC_MSG_CHECKING([for archive storage engine]) @@ -1468,7 +1468,7 @@ AC_DEFUN([MYSQL_CHECK_NDBCLUSTER], [ ;; esac - AM_CONDITIONAL(HAVE_NDBCLUSTER_DB, test "have_ndbcluster" = "yes") + AM_CONDITIONAL([HAVE_NDBCLUSTER_DB], [ test "$have_ndbcluster" = "yes" ]) AC_SUBST(ndbcluster_includes) AC_SUBST(ndbcluster_libs) AC_SUBST(ndbcluster_system_libs) diff --git a/client/mysqladmin.c b/client/mysqladmin.c index fcbcc0d7151..aaed101a83e 100644 --- a/client/mysqladmin.c +++ b/client/mysqladmin.c @@ -509,7 +509,7 @@ static int execute_commands(MYSQL *mysql,int argc, char **argv) !stat(pidfile, &pidfile_status)) last_modified= pidfile_status.st_mtime; - if (mysql_shutdown(mysql)) + if (mysql_shutdown(mysql, SHUTDOWN_DEFAULT)) { my_printf_error(0,"shutdown failed; error: '%s'",MYF(ME_BELL), mysql_error(mysql)); diff --git a/client/mysqldump.c b/client/mysqldump.c index de39f9474d2..d8e76805f87 100644 --- a/client/mysqldump.c +++ b/client/mysqldump.c @@ -243,7 +243,7 @@ static struct my_option my_long_options[] = "Deprecated, use --set-charset or --skip-set-charset to enable/disable charset settings instead", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, {"set-charset", OPT_SET_CHARSET, - "'SET CHARACTER_SET_CLIENT=default_character_set' will be put in the output", + "Add 'SET NAMES default_character_set' to the output. Enabled by default; suppress with --skip-set-charset.", (gptr*) &opt_set_charset, (gptr*) &opt_set_charset, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0}, {"set-variable", 'O', diff --git a/client/mysqltest.c b/client/mysqltest.c index f638053b515..88b7917612e 100644 --- a/client/mysqltest.c +++ b/client/mysqltest.c @@ -59,7 +59,8 @@ #include <sys/stat.h> #include <violite.h> -#define MAX_QUERY 65536 +#define MAX_QUERY 131072 +#define MAX_VAR_NAME 256 #define MAX_COLUMNS 256 #define PAD_SIZE 128 #define MAX_CONS 128 @@ -386,6 +387,7 @@ static void do_eval(DYNAMIC_STRING* query_eval, const char* query) register char c; register int escaped = 0; VAR* v; + DBUG_ENTER("do_eval"); for (p= query; (c = *p); ++p) { @@ -417,6 +419,7 @@ static void do_eval(DYNAMIC_STRING* query_eval, const char* query) break; } } + DBUG_VOID_RETURN; } @@ -628,6 +631,7 @@ static int check_result(DYNAMIC_STRING* ds, const char* fname, return error; } + VAR* var_get(const char* var_name, const char** var_name_end, my_bool raw, my_bool ignore_not_existing) { @@ -642,25 +646,26 @@ VAR* var_get(const char* var_name, const char** var_name_end, my_bool raw, if (!(digit < 10 && digit >= 0)) { const char* save_var_name = var_name, *end; + uint length; end = (var_name_end) ? *var_name_end : 0; while (my_isvar(charset_info,*var_name) && var_name != end) - ++var_name; + var_name++; if (var_name == save_var_name) { if (ignore_not_existing) DBUG_RETURN(0); die("Empty variable"); } + length= (uint) (var_name - save_var_name); - if (!(v = (VAR*) hash_search(&var_hash, save_var_name, - var_name - save_var_name))) + if (!(v = (VAR*) hash_search(&var_hash, save_var_name, length)) && + length < MAX_VAR_NAME) { - char c=*var_name, *s=(char*)var_name;; - *s=0; - v=var_from_env(save_var_name, ""); - *s=c; + char buff[MAX_VAR_NAME+1]; + strmake(buff, save_var_name, length); + v= var_from_env(buff, ""); } - --var_name; /* Point at last character */ + var_name--; /* Point at last character */ } else v = var_reg + digit; @@ -1737,6 +1742,7 @@ int read_line(char* buf, int size) enum {R_NORMAL, R_Q1, R_ESC_Q_Q1, R_ESC_Q_Q2, R_ESC_SLASH_Q1, R_ESC_SLASH_Q2, R_Q2, R_COMMENT, R_LINE_START} state= R_LINE_START; + DBUG_ENTER("read_line"); start_lineno= *lineno; for (; p < buf_end ;) @@ -1750,7 +1756,7 @@ int read_line(char* buf, int size) cur_file--; lineno--; if (cur_file == file_stack) - return 1; + DBUG_RETURN(1); continue; } @@ -1760,7 +1766,7 @@ int read_line(char* buf, int size) if (end_of_query(c)) { *p= 0; - return 0; + DBUG_RETURN(0); } else if (c == '\'') state = R_Q1; @@ -1777,7 +1783,7 @@ int read_line(char* buf, int size) { *p= 0; (*lineno)++; - return 0; + DBUG_RETURN(0); } break; case R_LINE_START: @@ -1795,12 +1801,12 @@ int read_line(char* buf, int size) { *buf++= '}'; *buf= 0; - return 0; + DBUG_RETURN(0); } else if (end_of_query(c) || c == '{') { *p= 0; - return 0; + DBUG_RETURN(0); } else if (c == '\'') state= R_Q1; @@ -1820,7 +1826,7 @@ int read_line(char* buf, int size) if (end_of_query(c)) { *p= 0; - return 0; + DBUG_RETURN(0); } if (c != '\'') state= R_NORMAL; @@ -1841,7 +1847,7 @@ int read_line(char* buf, int size) if (end_of_query(c)) { *p= 0; - return 0; + DBUG_RETURN(0); } if (c != '"') state= R_NORMAL; @@ -1857,7 +1863,7 @@ int read_line(char* buf, int size) *p++= c; } *p= 0; /* Always end with \0 */ - return feof(*cur_file); + DBUG_RETURN(feof(*cur_file)); } @@ -1892,8 +1898,11 @@ int read_query(struct st_query** q_ptr) q->type = Q_UNKNOWN; q->query_buf= q->query= 0; if (read_line(read_query_buf, sizeof(read_query_buf))) + { + DBUG_PRINT("warning",("too long query")); DBUG_RETURN(1); - + } + DBUG_PRINT("info", ("query: %s", read_query_buf)); if (*p == '#') { q->type = Q_COMMENT; @@ -2259,6 +2268,7 @@ int run_query(MYSQL* mysql, struct st_query* q, int flags) char* query; int query_len, got_error_on_send= 0; DBUG_ENTER("run_query"); + DBUG_PRINT("enter",("flags: %d", flags)); if (q->type != Q_EVAL) { @@ -2726,7 +2736,10 @@ int main(int argc, char **argv) case Q_EVAL_RESULT: eval_result = 1; break; case Q_EVAL: if (q->query == q->query_buf) + { q->query= q->first_argument; + q->first_word_len= 0; + } /* fall through */ case Q_QUERY_VERTICAL: case Q_QUERY_HORIZONTAL: @@ -2736,13 +2749,16 @@ int main(int argc, char **argv) { /* This happens when we use 'query_..' on it's own line */ q_send_flag=1; + DBUG_PRINT("info", + ("query: '%s' first_word_len: %d send_flag=1", + q->query, q->first_word_len)); break; } /* fix up query pointer if this is * first iteration for this line */ if (q->query == q->query_buf) q->query += q->first_word_len + 1; display_result_vertically= (q->type==Q_QUERY_VERTICAL); - error |= run_query(&cur_con->mysql, q, QUERY_REAP|QUERY_SEND); + error|= run_query(&cur_con->mysql, q, QUERY_REAP|QUERY_SEND); display_result_vertically= old_display_result_vertically; break; } diff --git a/configure.in b/configure.in index b8e4135a829..45e3cbc17c4 100644 --- a/configure.in +++ b/configure.in @@ -370,12 +370,15 @@ AC_SUBST(INSTALL_SCRIPT) export CC CXX CFLAGS LD LDFLAGS AR +ndb_cxxflags_fix= if test "$GXX" = "yes" then # mysqld requires -fno-implicit-templates. # Disable exceptions as they seams to create problems with gcc and threads. # mysqld doesn't use run-time-type-checking, so we disable it. CXXFLAGS="$CXXFLAGS -fno-implicit-templates -fno-exceptions -fno-rtti" + # ndb cannot be compiled with -fno-implicit-templaces + ndb_cxxflags_fix="$ndb_cxxflags_fix -fimplicit-templates" # If you are using 'gcc' 3.0 (not g++) to compile C++ programs on Linux, # we will gets some problems when linking static programs. @@ -1029,6 +1032,7 @@ case $SYSTEM_TYPE in CXXFLAGS="$CXXFLAGS +O2" MAX_C_OPTIMIZE="" MAX_CXX_OPTIMIZE="" + ndb_cxxflags_fix="$ndb_cxxflags_fix -Aa" fi ;; *rhapsody*) @@ -2332,14 +2336,15 @@ dnl you must also create strings/ctype-$charset_name.c AC_DIVERT_PUSH(0) +define(CHARSETS_AVAILABLE0,binary) define(CHARSETS_AVAILABLE1,ascii armscii8 ascii big5 cp1250 cp1251 cp1256 cp1257) -define(CHARSETS_AVAILABLE2,cp850 cp852 cp866 dec8 euckr gb2312 gbk) +define(CHARSETS_AVAILABLE2,cp850 cp852 cp866 dec8 euckr gb2312 gbk geostd8) define(CHARSETS_AVAILABLE3,greek hebrew hp8 keybcs2 koi8r koi8u) define(CHARSETS_AVAILABLE4,latin1 latin2 latin5 latin7 macce macroman) define(CHARSETS_AVAILABLE5,sjis swe7 tis620 ucs2 ujis utf8) DEFAULT_CHARSET=latin1 -CHARSETS_AVAILABLE="CHARSETS_AVAILABLE1 CHARSETS_AVAILABLE2 CHARSETS_AVAILABLE3 CHARSETS_AVAILABLE4 CHARSETS_AVAILABLE5" +CHARSETS_AVAILABLE="CHARSETS_AVAILABLE0 CHARSETS_AVAILABLE1 CHARSETS_AVAILABLE2 CHARSETS_AVAILABLE3 CHARSETS_AVAILABLE4 CHARSETS_AVAILABLE5" CHARSETS_COMPLEX="big5 cp1250 euckr gb2312 gbk latin1 latin2 sjis tis620 ucs2 ujis utf8" AC_DIVERT_POP @@ -2347,6 +2352,7 @@ AC_DIVERT_POP AC_ARG_WITH(charset, [ --with-charset=CHARSET Default character set, use one of: + CHARSETS_AVAILABLE0 CHARSETS_AVAILABLE1 CHARSETS_AVAILABLE2 CHARSETS_AVAILABLE3 @@ -2401,6 +2407,8 @@ do AC_DEFINE(USE_MB) AC_DEFINE(USE_MB_IDENT) ;; + binary) + ;; cp1250) AC_DEFINE(HAVE_CHARSET_cp1250) ;; @@ -2440,6 +2448,9 @@ do AC_DEFINE(USE_MB) AC_DEFINE(USE_MB_IDENT) ;; + geostd8) + AC_DEFINE(HAVE_CHARSET_geostd8) + ;; greek) AC_DEFINE(HAVE_CHARSET_greek) ;; @@ -2524,6 +2535,10 @@ case $default_charset in default_charset_default_collation="big5_chinese_ci" default_charset_collations="big5_chinese_ci big5_bin" ;; + binary) + default_charset_default_collation="binary" + default_charset_collations="binary" + ;; cp1250) default_charset_default_collation="cp1250_general_ci" default_charset_collations="cp1250_general_ci cp1250_czech_ci cp1250_bin" @@ -2568,6 +2583,10 @@ case $default_charset in default_charset_default_collation="gbk_chinese_ci" default_charset_collations="gbk_chinese_ci gbk_bin" ;; + geostd8) + default_charset_default_collation="geostd8_general_ci" + default_charset_collations="geostd8_general_ci geostd8_bin" + ;; greek) default_charset_default_collation="greek_general_ci" default_charset_collations="greek_general_ci greek_bin" @@ -2630,7 +2649,17 @@ case $default_charset in ;; ucs2) default_charset_default_collation="ucs2_general_ci" - default_charset_collations="ucs2_general_ci ucs2_bin" + define(UCSC1, ucs2_general_ci ucs2_bin) + define(UCSC2, ucs2_czech_ci ucs2_danish_ci) + define(UCSC3, ucs2_estonian_ci ucs2_icelandic_ci) + define(UCSC4, ucs2_latvian_ci ucs2_lithuanian_ci) + define(UCSC5, ucs2_polish_ci ucs2_romanian_ci) + define(UCSC6, ucs2_slovak_ci ucs2_slovenian_ci) + define(UCSC7, ucs2_spanish2_ci ucs2_spanish_ci) + define(UCSC8, ucs2_swedish_ci ucs2_turkish_ci) + define(UCSC9, ucs2_unicode_ci) + UCSC="UCSC1 UCSC2 UCSC3 UCSC4 UCSC5 UCSC6 UCSC7 UCSC8 UCSC9" + default_charset_collations="$UCSC" ;; ujis) default_charset_default_collation="ujis_japanese_ci" @@ -2638,7 +2667,17 @@ case $default_charset in ;; utf8) default_charset_default_collation="utf8_general_ci" - default_charset_collations="utf8_general_ci utf8_bin" + define(UTFC1, utf8_general_ci utf8_bin) + define(UTFC2, utf8_czech_ci utf8_danish_ci) + define(UTFC3, utf8_estonian_ci utf8_icelandic_ci) + define(UTFC4, utf8_latvian_ci utf8_lithuanian_ci) + define(UTFC5, utf8_polish_ci utf8_romanian_ci) + define(UTFC6, utf8_slovak_ci utf8_slovenian_ci) + define(UTFC7, utf8_spanish2_ci utf8_spanish_ci) + define(UTFC8, utf8_swedish_ci utf8_turkish_ci) + define(UTFC9, utf8_unicode_ci) + UTFC="UTFC1 UTFC2 UTFC3 UTFC4 UTFC5 UTFC6 UTFC7 UTFC8 UTFC9" + default_charset_collations="$UTFC" ;; *) AC_MSG_ERROR([Charset $cs not available. (Available are: $CHARSETS_AVAILABLE). @@ -2892,21 +2931,20 @@ if test X"$have_ndbcluster" = Xyes then MAKE_BINARY_DISTRIBUTION_OPTIONS="$MAKE_BINARY_DISTRIBUTION_OPTIONS --with-ndbcluster" + CXXFLAGS="$CXXFLAGS \$(NDB_CXXFLAGS)" if test "$with_debug" = "yes" then # Medium debug. NDB_DEFS="-DVM_TRACE -DERROR_INSERT -DARRAY_GUARD" - CXXFLAGS="$CXXFLAGS \$(NDB_CXXFLAGS) \$(NDB_CXXFLAGS_LOC) \$(NDB_CXXFLAGS_DEBUG_LOC)" elif test "$with_debug" = "full" then NDB_DEFS="-DVM_TRACE -DERROR_INSERT -DARRAY_GUARD" - CXXFLAGS="$CXXFLAGS \$(NDB_CXXFLAGS) \$(NDB_CXXFLAGS_LOC) \$(NDB_CXXFLAGS_DEBUG_LOC)" else NDB_DEFS="-DNDEBUG" - CXXFLAGS="$CXXFLAGS \$(NDB_CXXFLAGS) \$(NDB_CXXFLAGS_LOC) \$(NDB_CXXFLAGS_RELEASE_LOC)" fi AC_SUBST([NDB_DEFS]) +AC_SUBST([ndb_cxxflags_fix]) ndb_transporter_opt_objs="" if test X"$have_ndb_shm" = Xyes diff --git a/extra/Makefile.am b/extra/Makefile.am index df29a3a6ab7..aec7ad7dda5 100644 --- a/extra/Makefile.am +++ b/extra/Makefile.am @@ -14,7 +14,8 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -INCLUDES = @MT_INCLUDES@ -I$(top_srcdir)/include @ndbcluster_includes@ +INCLUDES = @MT_INCLUDES@ -I$(top_srcdir)/include \ + @ndbcluster_includes@ -I$(top_srcdir)/sql LDADD = @CLIENT_EXTRA_LDFLAGS@ ../mysys/libmysys.a \ ../dbug/libdbug.a ../strings/libmystrings.a bin_PROGRAMS = replace comp_err perror resolveip my_print_defaults \ diff --git a/extra/perror.c b/extra/perror.c index 26ebdd5b096..f1b1a4c2005 100644 --- a/extra/perror.c +++ b/extra/perror.c @@ -256,7 +256,7 @@ int main(int argc,char *argv[]) else { if (verbose) - printf("MySql error: %3d = %s\n",code,msg); + printf("MySQL error: %3d = %s\n",code,msg); else puts(msg); } diff --git a/include/my_global.h b/include/my_global.h index 3d1a770d877..70b5b1af819 100644 --- a/include/my_global.h +++ b/include/my_global.h @@ -1102,6 +1102,14 @@ do { doubleget_union _tmp; \ #endif /* sint2korr */ /* + Macro for reading 32-bit integer from network byte order (big-endian) + from unaligned memory location. +*/ +#define int4net(A) (int32) (((uint32) ((uchar) (A)[3])) |\ + (((uint32) ((uchar) (A)[2])) << 8) |\ + (((uint32) ((uchar) (A)[1])) << 16) |\ + (((uint32) ((uchar) (A)[0])) << 24)) +/* Define-funktions for reading and storing in machine format from/to short/long to/from some place in memory V should be a (not register) variable, M is a pointer to byte @@ -1178,16 +1186,6 @@ do { doubleget_union _tmp; \ #define statistic_add(V,C,L) (V)+=(C) #endif -#ifdef HAVE_OPENSSL -#include <openssl/opensslv.h> -#if OPENSSL_VERSION_NUMBER < 0x0090700f -#define DES_cblock des_cblock -#define DES_key_schedule des_key_schedule -#define DES_set_key_unchecked(k,ks) des_set_key_unchecked((k),*(ks)) -#define DES_ede3_cbc_encrypt(i,o,l,k1,k2,k3,iv,e) des_ede3_cbc_encrypt((i),(o),(l),*(k1),*(k2),*(k3),(iv),(e)) -#endif -#endif - #ifdef HAVE_CHARSET_utf8 #define MYSQL_UNIVERSAL_CLIENT_CHARSET "utf8" #else diff --git a/include/mysql.h b/include/mysql.h index 71bff833d59..931995fb1ac 100644 --- a/include/mysql.h +++ b/include/mysql.h @@ -453,7 +453,9 @@ int STDCALL mysql_add_slave(MYSQL* mysql, const char* host, const char* user, const char* passwd); -int STDCALL mysql_shutdown(MYSQL *mysql); +int STDCALL mysql_shutdown(MYSQL *mysql, + enum enum_shutdown_level + shutdown_level); int STDCALL mysql_dump_debug_info(MYSQL *mysql); int STDCALL mysql_refresh(MYSQL *mysql, unsigned int refresh_options); @@ -558,7 +560,7 @@ typedef struct st_mysql_bind enum enum_field_types buffer_type; /* buffer type */ unsigned long buffer_length; /* buffer length, must be set for str/binary */ - /* Following are for internal use. Set by mysql_bind_param */ + /* Following are for internal use. Set by mysql_stmt_bind_param */ unsigned char *inter_buffer; /* for the current data position */ unsigned long offset; /* offset position for char/binary fetch */ unsigned long internal_length; /* Used if length is 0 */ diff --git a/include/mysql_com.h b/include/mysql_com.h index d354a979cd1..47231ef31c6 100644 --- a/include/mysql_com.h +++ b/include/mysql_com.h @@ -223,6 +223,32 @@ enum enum_field_types { MYSQL_TYPE_DECIMAL, MYSQL_TYPE_TINY, #define FIELD_TYPE_INTERVAL MYSQL_TYPE_ENUM #define FIELD_TYPE_GEOMETRY MYSQL_TYPE_GEOMETRY +enum enum_shutdown_level { + /* + We want levels to be in growing order of hardness. So we leave room + for future intermediate levels. For now, escalating one level is += 10; + later if we insert new levels in between we will need a function + next_shutdown_level(level). Note that DEFAULT does not respect the + growing property. + */ + SHUTDOWN_DEFAULT= 0, /* mapped to WAIT_ALL_BUFFERS for now */ + /* + Here is the list in growing order (the next does the previous plus + something). WAIT_ALL_BUFFERS is what we have now. Others are "this MySQL + server does not support this shutdown level yet". + */ + SHUTDOWN_WAIT_CONNECTIONS= 10, /* wait for existing connections to finish */ + SHUTDOWN_WAIT_TRANSACTIONS= 20, /* wait for existing trans to finish */ + SHUTDOWN_WAIT_STATEMENTS= 30, /* wait for existing updating stmts to finish */ + SHUTDOWN_WAIT_ALL_BUFFERS= 40, /* flush InnoDB buffers */ + SHUTDOWN_WAIT_CRITICAL_BUFFERS= 50, /* flush MyISAM buffs (no corruption) */ + /* Now the 2 levels of the KILL command */ +#if MYSQL_VERSION_ID >= 50000 + KILL_QUERY= 254, +#endif + KILL_CONNECTION= 255 +}; + /* options for mysql_set_option */ enum enum_mysql_set_option { diff --git a/include/mysqld_error.h b/include/mysqld_error.h index f341041fc75..0dcc09a173f 100644 --- a/include/mysqld_error.h +++ b/include/mysqld_error.h @@ -314,4 +314,6 @@ #define ER_UNSUPPORTED_PS 1295 #define ER_GET_ERRMSG 1296 #define ER_GET_TEMPORARY_ERRMSG 1297 -#define ER_ERROR_MESSAGES 298 +#define ER_UNKNOWN_TIME_ZONE 1298 +#define ER_WARN_INVALID_TIMESTAMP 1299 +#define ER_ERROR_MESSAGES 300 diff --git a/include/violite.h b/include/violite.h index b4b9f724d89..ba7de3ee175 100644 --- a/include/violite.h +++ b/include/violite.h @@ -83,6 +83,14 @@ my_bool vio_poll_read(Vio *vio,uint timeout); void vio_timeout(Vio *vio,uint timeout); #ifdef HAVE_OPENSSL +#include <openssl/opensslv.h> +#if OPENSSL_VERSION_NUMBER < 0x0090700f +#define DES_cblock des_cblock +#define DES_key_schedule des_key_schedule +#define DES_set_key_unchecked(k,ks) des_set_key_unchecked((k),*(ks)) +#define DES_ede3_cbc_encrypt(i,o,l,k1,k2,k3,iv,e) des_ede3_cbc_encrypt((i),(o),(l),*(k1),*(k2),*(k3),(iv),(e)) +#endif + #define HEADER_DES_LOCL_H dummy_something #include <openssl/ssl.h> #include <openssl/err.h> diff --git a/innobase/btr/btr0btr.c b/innobase/btr/btr0btr.c index 09f5c66f687..27d798f925a 100644 --- a/innobase/btr/btr0btr.c +++ b/innobase/btr/btr0btr.c @@ -2165,7 +2165,6 @@ btr_discard_page( ut_ad(btr_check_node_ptr(tree, merge_page, mtr)); } -#ifdef UNIV_DEBUG /***************************************************************** Prints size info of a B-tree. */ @@ -2287,7 +2286,6 @@ btr_print_tree( btr_validate_tree(tree); } -#endif /* UNIV_DEBUG */ /**************************************************************** Checks that the node pointer to a page is appropriate. */ diff --git a/innobase/buf/buf0buf.c b/innobase/buf/buf0buf.c index 268d6f8e16d..5ec8998473d 100644 --- a/innobase/buf/buf0buf.c +++ b/innobase/buf/buf0buf.c @@ -223,14 +223,12 @@ in the free list to the frames. buf_pool_t* buf_pool = NULL; /* The buffer buf_pool of the database */ -#ifdef UNIV_DEBUG -static ulint buf_dbg_counter = 0; /* This is used to insert validation +ulint buf_dbg_counter = 0; /* This is used to insert validation operations in excution in the debug version */ ibool buf_debug_prints = FALSE; /* If this is set TRUE, the program prints info whenever read-ahead or flush occurs */ -#endif /* UNIV_DEBUG */ /************************************************************************ Calculates a page checksum which is stored to the page when it is written @@ -1729,12 +1727,10 @@ buf_page_create( /* If we get here, the page was not in buf_pool: init it there */ -#ifdef UNIV_DEBUG if (buf_debug_prints) { fprintf(stderr, "Creating space %lu page %lu to buffer\n", (ulong) space, (ulong) offset); } -#endif /* UNIV_DEBUG */ block = free_block; @@ -1885,11 +1881,9 @@ buf_page_io_complete( rw_lock_x_unlock_gen(&(block->lock), BUF_IO_READ); -#ifdef UNIV_DEBUG if (buf_debug_prints) { fputs("Has read ", stderr); } -#endif /* UNIV_DEBUG */ } else { ut_ad(io_type == BUF_IO_WRITE); @@ -1902,21 +1896,17 @@ buf_page_io_complete( buf_pool->n_pages_written++; -#ifdef UNIV_DEBUG if (buf_debug_prints) { fputs("Has written ", stderr); } -#endif /* UNIV_DEBUG */ } mutex_exit(&(buf_pool->mutex)); -#ifdef UNIV_DEBUG if (buf_debug_prints) { fprintf(stderr, "page space %lu page no %lu\n", (ulong) block->space, (ulong) block->offset); } -#endif /* UNIV_DEBUG */ } /************************************************************************* @@ -1945,7 +1935,6 @@ buf_pool_invalidate(void) mutex_exit(&(buf_pool->mutex)); } -#ifdef UNIV_DEBUG /************************************************************************* Validates the buffer buf_pool data structure. */ @@ -2145,7 +2134,6 @@ buf_print(void) ut_a(buf_validate()); } -#endif /* UNIV_DEBUG */ /************************************************************************* Returns the number of pending buf pool ios. */ diff --git a/innobase/buf/buf0flu.c b/innobase/buf/buf0flu.c index 885bcf06456..6cefdb60956 100644 --- a/innobase/buf/buf0flu.c +++ b/innobase/buf/buf0flu.c @@ -32,7 +32,6 @@ flushed along with the original page. */ #define BUF_FLUSH_AREA ut_min(BUF_READ_AHEAD_AREA,\ buf_pool->curr_size / 16) -#ifdef UNIV_DEBUG /********************************************************************** Validates the flush list. */ static @@ -40,7 +39,6 @@ ibool buf_flush_validate_low(void); /*========================*/ /* out: TRUE if ok */ -#endif /* UNIV_DEBUG */ /************************************************************************ Inserts a modified block into the flush list. */ @@ -513,13 +511,11 @@ buf_flush_try_page( rw_lock_s_lock_gen(&(block->lock), BUF_IO_WRITE); } -#ifdef UNIV_DEBUG if (buf_debug_prints) { fprintf(stderr, "Flushing page space %lu, page no %lu \n", (ulong) block->space, (ulong) block->offset); } -#endif /* UNIV_DEBUG */ buf_flush_write_block_low(block); @@ -603,14 +599,12 @@ buf_flush_try_page( rw_lock_s_lock_gen(&(block->lock), BUF_IO_WRITE); -#ifdef UNIV_DEBUG if (buf_debug_prints) { fprintf(stderr, "Flushing single page space %lu, page no %lu \n", (ulong) block->space, (ulong) block->offset); } -#endif /* UNIV_DEBUG */ buf_flush_write_block_low(block); @@ -837,7 +831,6 @@ buf_flush_batch( buf_flush_buffered_writes(); -#ifdef UNIV_DEBUG if (buf_debug_prints && page_count > 0) { ut_a(flush_type == BUF_FLUSH_LRU || flush_type == BUF_FLUSH_LIST); @@ -846,7 +839,6 @@ buf_flush_batch( : "Flushed %lu pages in flush list flush\n", (ulong) page_count); } -#endif /* UNIV_DEBUG */ return(page_count); } @@ -938,7 +930,6 @@ buf_flush_free_margin(void) } } -#ifdef UNIV_DEBUG /********************************************************************** Validates the flush list. */ static @@ -988,4 +979,3 @@ buf_flush_validate(void) return(ret); } -#endif /* UNIV_DEBUG */ diff --git a/innobase/buf/buf0lru.c b/innobase/buf/buf0lru.c index 053161986e4..796311f0157 100644 --- a/innobase/buf/buf0lru.c +++ b/innobase/buf/buf0lru.c @@ -209,14 +209,12 @@ buf_LRU_search_and_free_block( ut_a(block->in_LRU_list); if (buf_flush_ready_for_replace(block)) { -#ifdef UNIV_DEBUG if (buf_debug_prints) { fprintf(stderr, "Putting space %lu page %lu to free list\n", (ulong) block->space, (ulong) block->offset); } -#endif /* UNIV_DEBUG */ buf_LRU_block_remove_hashed_page(block); @@ -888,7 +886,6 @@ buf_LRU_block_free_hashed_page( buf_LRU_block_free_non_file_page(block); } -#ifdef UNIV_DEBUG /************************************************************************** Validates the LRU list. */ @@ -1019,4 +1016,3 @@ buf_LRU_print(void) mutex_exit(&(buf_pool->mutex)); } -#endif /* UNIV_DEBUG */ diff --git a/innobase/buf/buf0rea.c b/innobase/buf/buf0rea.c index 63e078f3f6e..71e885ff439 100644 --- a/innobase/buf/buf0rea.c +++ b/innobase/buf/buf0rea.c @@ -284,14 +284,12 @@ buf_read_ahead_random( os_aio_simulated_wake_handler_threads(); -#ifdef UNIV_DEBUG if (buf_debug_prints && (count > 0)) { fprintf(stderr, "Random read-ahead space %lu offset %lu pages %lu\n", (ulong) space, (ulong) offset, (ulong) count); } -#endif /* UNIV_DEBUG */ return(count); } @@ -571,13 +569,11 @@ buf_read_ahead_linear( /* Flush pages from the end of the LRU list if necessary */ buf_flush_free_margin(); -#ifdef UNIV_DEBUG if (buf_debug_prints && (count > 0)) { fprintf(stderr, "LINEAR read-ahead space %lu offset %lu pages %lu\n", (ulong) space, (ulong) offset, (ulong) count); } -#endif /* UNIV_DEBUG */ return(count); } @@ -636,13 +632,11 @@ buf_read_ibuf_merge_pages( /* Flush pages from the end of the LRU list if necessary */ buf_flush_free_margin(); -#ifdef UNIV_DEBUG if (buf_debug_prints) { fprintf(stderr, "Ibuf merge read-ahead space %lu pages %lu\n", (ulong) space_ids[0], (ulong) n_stored); } -#endif /* UNIV_DEBUG */ } /************************************************************************ @@ -706,10 +700,8 @@ buf_read_recv_pages( /* Flush pages from the end of the LRU list if necessary */ buf_flush_free_margin(); -#ifdef UNIV_DEBUG if (buf_debug_prints) { fprintf(stderr, "Recovery applies read-ahead pages %lu\n", (ulong) n_stored); } -#endif /* UNIV_DEBUG */ } diff --git a/innobase/data/data0type.c b/innobase/data/data0type.c index 698b5361dfa..97d93b1b0ec 100644 --- a/innobase/data/data0type.c +++ b/innobase/data/data0type.c @@ -104,7 +104,6 @@ dtype_form_prtype( return(old_prtype + (charset_coll << 16)); } -#ifdef UNIV_DEBUG /************************************************************************* Validates a data type structure. */ @@ -123,7 +122,6 @@ dtype_validate( return(TRUE); } -#endif /* UNIV_DEBUG */ /************************************************************************* Prints a data type structure. */ diff --git a/innobase/dict/dict0dict.c b/innobase/dict/dict0dict.c index aa5787ee30e..71cf908db4e 100644 --- a/innobase/dict/dict0dict.c +++ b/innobase/dict/dict0dict.c @@ -3488,9 +3488,9 @@ dict_tree_create( tree->id = index->id; UT_LIST_INIT(tree->tree_indexes); -#ifdef UNIV_DEBUG + tree->magic_n = DICT_TREE_MAGIC_N; -#endif /* UNIV_DEBUG */ + rw_lock_create(&(tree->lock)); rw_lock_set_level(&(tree->lock), SYNC_INDEX_TREE); @@ -3929,7 +3929,6 @@ dict_foreign_print_low( fputs(" )\n", stderr); } -#ifdef UNIV_DEBUG /************************************************************************** Prints a table data. */ @@ -3962,7 +3961,6 @@ dict_table_print_by_name( dict_table_print_low(table); mutex_exit(&(dict_sys->mutex)); } -#endif /* UNIV_DEBUG */ /************************************************************************** Prints a table data. */ diff --git a/innobase/dict/dict0mem.c b/innobase/dict/dict0mem.c index 936b06b1905..8f05475df47 100644 --- a/innobase/dict/dict0mem.c +++ b/innobase/dict/dict0mem.c @@ -87,9 +87,9 @@ dict_mem_table_create( mutex_set_level(&(table->autoinc_mutex), SYNC_DICT_AUTOINC_MUTEX); table->autoinc_inited = FALSE; -#ifdef UNIV_DEBUG + table->magic_n = DICT_TABLE_MAGIC_N; -#endif /* UNIV_DEBUG */ + return(table); } @@ -205,9 +205,7 @@ dict_mem_index_create( index->stat_n_diff_key_vals = NULL; index->cached = FALSE; -#ifdef UNIV_DEBUG index->magic_n = DICT_INDEX_MAGIC_N; -#endif /* UNIV_DEBUG */ return(index); } diff --git a/innobase/fil/fil0fil.c b/innobase/fil/fil0fil.c index 8e655492aa6..59fbd6f785d 100644 --- a/innobase/fil/fil0fil.c +++ b/innobase/fil/fil0fil.c @@ -2820,12 +2820,11 @@ fil_load_single_table_tablespaces(void) if (len > dbpath_len) { dbpath_len = len; - if (!dbpath) { - dbpath = mem_alloc(dbpath_len); - } else { - dbpath = mem_realloc(dbpath, dbpath_len, - __FILE__, __LINE__); + if (dbpath) { + mem_free(dbpath); } + + dbpath = mem_alloc(dbpath_len); } sprintf(dbpath, "%s/%s", fil_path_to_mysql_datadir, dbinfo.name); diff --git a/innobase/fsp/fsp0fsp.c b/innobase/fsp/fsp0fsp.c index e7b97e88f32..34b6de76ff4 100644 --- a/innobase/fsp/fsp0fsp.c +++ b/innobase/fsp/fsp0fsp.c @@ -3476,8 +3476,7 @@ fseg_validate_low( return(TRUE); } - -#ifdef UNIV_DEBUG + /*********************************************************************** Validates a segment. */ @@ -3502,7 +3501,6 @@ fseg_validate( return(ret); } -#endif /* UNIV_DEBUG */ /*********************************************************************** Writes info of a segment. */ @@ -3554,7 +3552,6 @@ fseg_print_low( (ulong) n_used); } -#ifdef UNIV_DEBUG /*********************************************************************** Writes info of a segment. */ @@ -3575,7 +3572,6 @@ fseg_print( fseg_print_low(inode, mtr); } -#endif /* UNIV_DEBUG */ /*********************************************************************** Validates the file space system and its segments. */ diff --git a/innobase/fut/fut0lst.c b/innobase/fut/fut0lst.c index 3d92aaba1ef..8deaa8adb3f 100644 --- a/innobase/fut/fut0lst.c +++ b/innobase/fut/fut0lst.c @@ -490,7 +490,6 @@ flst_validate( return(TRUE); } -#ifdef UNIV_DEBUG /************************************************************************ Prints info of a file-based list. */ @@ -517,4 +516,3 @@ flst_print( (ulong) buf_frame_get_page_no(frame), (ulong) (base - frame), (ulong) len); } -#endif /* UNIV_DEBUG */ diff --git a/innobase/ha/hash0hash.c b/innobase/ha/hash0hash.c index 372104e54b3..facdea66198 100644 --- a/innobase/ha/hash0hash.c +++ b/innobase/ha/hash0hash.c @@ -22,7 +22,6 @@ hash_mutex_enter( hash_table_t* table, /* in: hash table */ ulint fold) /* in: fold */ { - ut_ad(table->magic_n == HASH_TABLE_MAGIC_N); mutex_enter(hash_get_mutex(table, fold)); } @@ -35,10 +34,41 @@ hash_mutex_exit( hash_table_t* table, /* in: hash table */ ulint fold) /* in: fold */ { - ut_ad(table->magic_n == HASH_TABLE_MAGIC_N); mutex_exit(hash_get_mutex(table, fold)); } +/**************************************************************** +Reserves all the mutexes of a hash table, in an ascending order. */ + +void +hash_mutex_enter_all( +/*=================*/ + hash_table_t* table) /* in: hash table */ +{ + ulint i; + + for (i = 0; i < table->n_mutexes; i++) { + + mutex_enter(table->mutexes + i); + } +} + +/**************************************************************** +Releases all the mutexes of a hash table. */ + +void +hash_mutex_exit_all( +/*================*/ + hash_table_t* table) /* in: hash table */ +{ + ulint i; + + for (i = 0; i < table->n_mutexes; i++) { + + mutex_exit(table->mutexes + i); + } +} + /***************************************************************** Creates a hash table with >= n array cells. The actual number of cells is chosen to be a prime number slightly bigger than n. */ @@ -68,9 +98,7 @@ hash_create( table->mutexes = NULL; table->heaps = NULL; table->heap = NULL; -#ifdef UNIV_DEBUG table->magic_n = HASH_TABLE_MAGIC_N; -#endif /* UNIV_DEBUG */ /* Initialize the cell array */ @@ -91,7 +119,6 @@ hash_table_free( /*============*/ hash_table_t* table) /* in, own: hash table */ { - ut_ad(table->magic_n == HASH_TABLE_MAGIC_N); ut_a(table->mutexes == NULL); ut_free(table->array); @@ -113,7 +140,6 @@ hash_create_mutexes( ulint i; ut_a(n_mutexes == ut_2_power_up(n_mutexes)); - ut_ad(table->magic_n == HASH_TABLE_MAGIC_N); table->mutexes = mem_alloc(n_mutexes * sizeof(mutex_t)); diff --git a/innobase/ibuf/ibuf0ibuf.c b/innobase/ibuf/ibuf0ibuf.c index 931fb7f4168..f05e69863a3 100644 --- a/innobase/ibuf/ibuf0ibuf.c +++ b/innobase/ibuf/ibuf0ibuf.c @@ -201,15 +201,13 @@ because ibuf merge is done to a page when it is read in, and it is still physically like the index page even if the index would have been dropped! So, there seems to be no problem. */ -#ifdef UNIV_DEBUG /********************************************************************** Validates the ibuf data structures when the caller owns ibuf_mutex. */ -static + ibool ibuf_validate_low(void); /*===================*/ /* out: TRUE if ok */ -#endif /* UNIV_DEBUG */ /********************************************************************** Sets the flag in the current OS thread local storage denoting that it is @@ -3278,10 +3276,10 @@ leave_loop: mem_heap_free(heap); } -#ifdef UNIV_DEBUG + /********************************************************************** Validates the ibuf data structures when the caller owns ibuf_mutex. */ -static + ibool ibuf_validate_low(void) /*===================*/ @@ -3308,7 +3306,6 @@ ibuf_validate_low(void) return(TRUE); } -#endif /* UNIV_DEBUG */ /********************************************************************** Looks if the insert buffer is empty. */ diff --git a/innobase/include/Makefile.am b/innobase/include/Makefile.am index 5ec70da97a2..102d25566da 100644 --- a/innobase/include/Makefile.am +++ b/innobase/include/Makefile.am @@ -43,7 +43,7 @@ noinst_HEADERS = btr0btr.h btr0btr.ic btr0cur.h btr0cur.ic \ row0purge.ic row0row.h row0row.ic row0sel.h row0sel.ic \ row0types.h row0uins.h row0uins.ic row0umod.h row0umod.ic \ row0undo.h row0undo.ic row0upd.h row0upd.ic row0vers.h \ - row0vers.ic srv0srv.h srv0srv.ic srv0start.h \ + row0vers.ic srv0que.h srv0srv.h srv0srv.ic srv0start.h \ sync0arr.h sync0arr.ic sync0rw.h \ sync0rw.ic sync0sync.h sync0sync.ic sync0types.h \ thr0loc.h thr0loc.ic trx0purge.h trx0purge.ic trx0rec.h \ diff --git a/innobase/include/btr0btr.h b/innobase/include/btr0btr.h index e904db3272f..8606fcd2a5c 100644 --- a/innobase/include/btr0btr.h +++ b/innobase/include/btr0btr.h @@ -392,7 +392,6 @@ btr_page_free_low( page_t* page, /* in: page to be freed, x-latched */ ulint level, /* in: page level */ mtr_t* mtr); /* in: mtr */ -#ifdef UNIV_DEBUG /***************************************************************** Prints size info of a B-tree. */ @@ -409,7 +408,6 @@ btr_print_tree( dict_tree_t* tree, /* in: tree */ ulint width); /* in: print this many entries from start and end */ -#endif /* UNIV_DEBUG */ /**************************************************************** Checks the size and number of fields in a record based on the definition of the index. */ diff --git a/innobase/include/buf0buf.h b/innobase/include/buf0buf.h index 9590fea1276..53599d03c73 100644 --- a/innobase/include/buf0buf.h +++ b/innobase/include/buf0buf.h @@ -54,11 +54,9 @@ Created 11/5/1995 Heikki Tuuri #define BUF_KEEP_OLD 52 extern buf_pool_t* buf_pool; /* The buffer pool of the database */ -#ifdef UNIV_DEBUG extern ibool buf_debug_prints;/* If this is set TRUE, the program prints info whenever read or flush occurs */ -#endif /* UNIV_DEBUG */ /************************************************************************ Creates the buffer pool. */ @@ -478,14 +476,12 @@ buf_pool_is_block( /*==============*/ /* out: TRUE if pointer to block */ void* ptr); /* in: pointer to memory */ -#ifdef UNIV_DEBUG /************************************************************************* Validates the buffer pool data structure. */ ibool buf_validate(void); /*==============*/ -#endif /* UNIV_DEBUG */ /************************************************************************ Prints a page to stderr. */ @@ -897,7 +893,7 @@ struct buf_pool_struct{ ulint n_pend_reads; /* number of pending read operations */ - time_t last_printout_time; /* when buf_print_io was last time + time_t last_printout_time; /* when buf_print was last time called */ ulint n_pages_read; /* number read operations */ ulint n_pages_written;/* number write operations */ @@ -912,10 +908,10 @@ struct buf_pool_struct{ ulint n_pages_awe_remapped; /* if AWE is enabled, the number of remaps of blocks to buffer frames */ - ulint n_page_gets_old;/* n_page_gets when buf_print_io was + ulint n_page_gets_old;/* n_page_gets when buf_print was last time called: used to calculate hit rate */ - ulint n_pages_read_old;/* n_pages_read when buf_print_io was + ulint n_pages_read_old;/* n_pages_read when buf_print was last time called */ ulint n_pages_written_old;/* number write operations */ ulint n_pages_created_old;/* number of pages created in diff --git a/innobase/include/buf0buf.ic b/innobase/include/buf0buf.ic index fa8421b63ad..681a0ef000a 100644 --- a/innobase/include/buf0buf.ic +++ b/innobase/include/buf0buf.ic @@ -11,6 +11,10 @@ Created 11/5/1995 Heikki Tuuri #include "buf0rea.h" #include "mtr0mtr.h" +extern ulint buf_dbg_counter; /* This is used to insert validation + operations in execution in the + debug version */ + /************************************************************************ Recommends a move of a block to the start of the LRU list if there is danger of dropping from the buffer pool. NOTE: does not reserve the buffer pool diff --git a/innobase/include/buf0flu.h b/innobase/include/buf0flu.h index 6f39eef7210..1b40acaa269 100644 --- a/innobase/include/buf0flu.h +++ b/innobase/include/buf0flu.h @@ -97,7 +97,6 @@ buf_flush_ready_for_replace( /* out: TRUE if can replace immediately */ buf_block_t* block); /* in: buffer control block, must be in state BUF_BLOCK_FILE_PAGE and in the LRU list */ -#ifdef UNIV_DEBUG /********************************************************************** Validates the flush list. */ @@ -105,7 +104,6 @@ ibool buf_flush_validate(void); /*====================*/ /* out: TRUE if ok */ -#endif /* UNIV_DEBUG */ /* When buf_flush_free_margin is called, it tries to make this many blocks available to replacement in the free list and at the end of the LRU list (to diff --git a/innobase/include/buf0lru.h b/innobase/include/buf0lru.h index 98aa0252e12..69a376f8cab 100644 --- a/innobase/include/buf0lru.h +++ b/innobase/include/buf0lru.h @@ -112,7 +112,6 @@ void buf_LRU_make_block_old( /*===================*/ buf_block_t* block); /* in: control block */ -#ifdef UNIV_DEBUG /************************************************************************** Validates the LRU list. */ @@ -125,7 +124,6 @@ Prints the LRU list. */ void buf_LRU_print(void); /*===============*/ -#endif /* UNIV_DEBUG */ #ifndef UNIV_NONINL #include "buf0lru.ic" diff --git a/innobase/include/data0data.h b/innobase/include/data0data.h index e2de13d0520..2136de0f9b3 100644 --- a/innobase/include/data0data.h +++ b/innobase/include/data0data.h @@ -395,11 +395,9 @@ struct dtuple_struct { UT_LIST_NODE_T(dtuple_t) tuple_list; /* data tuples can be linked into a list using this field */ -#ifdef UNIV_DEBUG - ulint magic_n; -#define DATA_TUPLE_MAGIC_N 65478679 -#endif /* UNIV_DEBUG */ + ulint magic_n; }; +#define DATA_TUPLE_MAGIC_N 65478679 /* A slot for a field in a big rec vector */ diff --git a/innobase/include/data0type.h b/innobase/include/data0type.h index 2138df97392..fe38a224a66 100644 --- a/innobase/include/data0type.h +++ b/innobase/include/data0type.h @@ -305,7 +305,7 @@ dtype_new_read_for_order_and_null_size( /*===================================*/ dtype_t* type, /* in: type struct */ byte* buf); /* in: buffer for stored type order info */ -#ifdef UNIV_DEBUG + /************************************************************************* Validates a data type structure. */ @@ -314,7 +314,6 @@ dtype_validate( /*===========*/ /* out: TRUE if ok */ dtype_t* type); /* in: type struct to validate */ -#endif /* UNIV_DEBUG */ /************************************************************************* Prints a data type structure. */ diff --git a/innobase/include/dict0dict.h b/innobase/include/dict0dict.h index fe04359d6f1..ebb34f7dda0 100644 --- a/innobase/include/dict0dict.h +++ b/innobase/include/dict0dict.h @@ -325,19 +325,18 @@ dict_table_get_index_noninline( dict_table_t* table, /* in: table */ const char* name); /* in: index name */ /************************************************************************** -Prints a table data. */ +Prints a table definition. */ void -dict_table_print_low( -/*=================*/ +dict_table_print( +/*=============*/ dict_table_t* table); /* in: table */ -#ifdef UNIV_DEBUG /************************************************************************** -Prints a table definition. */ +Prints a table data. */ void -dict_table_print( -/*=============*/ +dict_table_print_low( +/*=================*/ dict_table_t* table); /* in: table */ /************************************************************************** Prints a table data when we know the table name. */ @@ -346,7 +345,6 @@ void dict_table_print_by_name( /*=====================*/ const char* name); -#endif /* UNIV_DEBUG */ /************************************************************************** Outputs info on foreign keys of a table. */ diff --git a/innobase/include/dict0mem.h b/innobase/include/dict0mem.h index f141ea9da09..3fc3e850987 100644 --- a/innobase/include/dict0mem.h +++ b/innobase/include/dict0mem.h @@ -188,12 +188,11 @@ struct dict_tree_struct{ the list; if the tree is of the mixed type, the first index in the list is the index of the cluster which owns the tree */ -#ifdef UNIV_DEBUG ulint magic_n;/* magic number */ -#define DICT_TREE_MAGIC_N 7545676 -#endif /* UNIV_DEBUG */ }; +#define DICT_TREE_MAGIC_N 7545676 + /* Data structure for an index */ struct dict_index_struct{ dulint id; /* id of the index */ @@ -238,10 +237,7 @@ struct dict_index_struct{ ulint stat_n_leaf_pages; /* approximate number of leaf pages in the index tree */ -#ifdef UNIV_DEBUG ulint magic_n;/* magic number */ -#define DICT_INDEX_MAGIC_N 76789786 -#endif /* UNIV_DEBUG */ }; /* Data structure for a foreign key constraint; an example: @@ -292,6 +288,9 @@ a foreign key constraint is enforced, therefore RESTRICT just means no flag */ #define DICT_FOREIGN_ON_DELETE_NO_ACTION 16 #define DICT_FOREIGN_ON_UPDATE_NO_ACTION 32 + +#define DICT_INDEX_MAGIC_N 76789786 + /* Data structure for a database table */ struct dict_table_struct{ dulint id; /* id of the table or cluster */ @@ -410,12 +409,10 @@ struct dict_table_struct{ inited; MySQL gets the init value by executing SELECT MAX(auto inc column) */ ib_longlong autoinc;/* autoinc counter value to give to the - next inserted row */ -#ifdef UNIV_DEBUG + next inserted row */ ulint magic_n;/* magic number */ -#define DICT_TABLE_MAGIC_N 76333786 -#endif /* UNIV_DEBUG */ }; +#define DICT_TABLE_MAGIC_N 76333786 #ifndef UNIV_NONINL #include "dict0mem.ic" diff --git a/innobase/include/fsp0fsp.h b/innobase/include/fsp0fsp.h index 20f4f4c2abd..2fcde882df7 100644 --- a/innobase/include/fsp0fsp.h +++ b/innobase/include/fsp0fsp.h @@ -332,7 +332,6 @@ void fsp_print( /*======*/ ulint space); /* in: space id */ -#ifdef UNIV_DEBUG /*********************************************************************** Validates a segment. */ @@ -350,7 +349,6 @@ fseg_print( /*=======*/ fseg_header_t* header, /* in: segment header */ mtr_t* mtr); /* in: mtr */ -#endif /* UNIV_DEBUG */ /* Flags for fsp_reserve_free_extents */ #define FSP_NORMAL 1000000 diff --git a/innobase/include/fut0lst.h b/innobase/include/fut0lst.h index 3f679d61ab5..5427e2248da 100644 --- a/innobase/include/fut0lst.h +++ b/innobase/include/fut0lst.h @@ -181,7 +181,6 @@ flst_validate( /* out: TRUE if ok */ flst_base_node_t* base, /* in: pointer to base node of list */ mtr_t* mtr1); /* in: mtr */ -#ifdef UNIV_DEBUG /************************************************************************ Prints info of a file-based list. */ @@ -190,7 +189,7 @@ flst_print( /*=======*/ flst_base_node_t* base, /* in: pointer to base node of list */ mtr_t* mtr); /* in: mtr */ -#endif /* UNIV_DEBUG */ + #ifndef UNIV_NONINL #include "fut0lst.ic" diff --git a/innobase/include/hash0hash.h b/innobase/include/hash0hash.h index 79efe016324..51315e40875 100644 --- a/innobase/include/hash0hash.h +++ b/innobase/include/hash0hash.h @@ -283,6 +283,21 @@ hash_mutex_exit( /*============*/ hash_table_t* table, /* in: hash table */ ulint fold); /* in: fold */ +/**************************************************************** +Reserves all the mutexes of a hash table, in an ascending order. */ + +void +hash_mutex_enter_all( +/*=================*/ + hash_table_t* table); /* in: hash table */ +/**************************************************************** +Releases all the mutexes of a hash table. */ + +void +hash_mutex_exit_all( +/*================*/ + hash_table_t* table); /* in: hash table */ + struct hash_cell_struct{ void* node; /* hash chain node, NULL if none */ @@ -303,12 +318,11 @@ struct hash_table_struct { memory heaps; there are then n_mutexes many of these heaps */ mem_heap_t* heap; -#ifdef UNIV_DEBUG ulint magic_n; -#define HASH_TABLE_MAGIC_N 76561114 -#endif /* UNIV_DEBUG */ }; +#define HASH_TABLE_MAGIC_N 76561114 + #ifndef UNIV_NONINL #include "hash0hash.ic" #endif diff --git a/innobase/include/hash0hash.ic b/innobase/include/hash0hash.ic index 1b9acfa2f34..0d713140c13 100644 --- a/innobase/include/hash0hash.ic +++ b/innobase/include/hash0hash.ic @@ -18,7 +18,6 @@ hash_get_nth_cell( hash_table_t* table, /* in: hash table */ ulint n) /* in: cell index */ { - ut_ad(table->magic_n == HASH_TABLE_MAGIC_N); ut_ad(n < table->n_cells); return(table->array + n); @@ -33,7 +32,6 @@ hash_get_n_cells( /* out: number of cells */ hash_table_t* table) /* in: table */ { - ut_ad(table->magic_n == HASH_TABLE_MAGIC_N); return(table->n_cells); } @@ -47,7 +45,6 @@ hash_calc_hash( ulint fold, /* in: folded value */ hash_table_t* table) /* in: hash table */ { - ut_ad(table->magic_n == HASH_TABLE_MAGIC_N); return(ut_hash_ulint(fold, table->n_cells)); } @@ -61,7 +58,6 @@ hash_get_mutex_no( hash_table_t* table, /* in: hash table */ ulint fold) /* in: fold */ { - ut_ad(table->magic_n == HASH_TABLE_MAGIC_N); return(ut_2pow_remainder(fold, table->n_mutexes)); } @@ -75,7 +71,6 @@ hash_get_nth_heap( hash_table_t* table, /* in: hash table */ ulint i) /* in: index of the heap */ { - ut_ad(table->magic_n == HASH_TABLE_MAGIC_N); ut_ad(i < table->n_mutexes); return(table->heaps[i]); @@ -93,8 +88,6 @@ hash_get_heap( { ulint i; - ut_ad(table->magic_n == HASH_TABLE_MAGIC_N); - if (table->heap) { return(table->heap); } @@ -114,7 +107,6 @@ hash_get_nth_mutex( hash_table_t* table, /* in: hash table */ ulint i) /* in: index of the mutex */ { - ut_ad(table->magic_n == HASH_TABLE_MAGIC_N); ut_ad(i < table->n_mutexes); return(table->mutexes + i); diff --git a/innobase/include/lock0lock.h b/innobase/include/lock0lock.h index b1f9a10ee05..9c5f35c6674 100644 --- a/innobase/include/lock0lock.h +++ b/innobase/include/lock0lock.h @@ -19,9 +19,7 @@ Created 5/7/1996 Heikki Tuuri #include "read0types.h" #include "hash0hash.h" -#ifdef UNIV_DEBUG extern ibool lock_print_waits; -#endif /* UNIV_DEBUG */ /* Buffer for storing information about the most recent deadlock error */ extern FILE* lock_latest_err_file; @@ -475,7 +473,6 @@ lock_check_trx_id_sanity( dict_index_t* index, /* in: clustered index */ ibool has_kernel_mutex);/* in: TRUE if the caller owns the kernel mutex */ -#ifdef UNIV_DEBUG /************************************************************************* Validates the lock queue on a single record. */ @@ -485,7 +482,6 @@ lock_rec_queue_validate( /* out: TRUE if ok */ rec_t* rec, /* in: record to look at */ dict_index_t* index); /* in: index, or NULL if not known */ -#endif /* UNIV_DEBUG */ /************************************************************************* Prints info of a table lock. */ @@ -509,7 +505,6 @@ void lock_print_info( /*============*/ FILE* file); /* in: file where to print */ -#ifdef UNIV_DEBUG /************************************************************************* Validates the lock queue on a table. */ @@ -534,7 +529,6 @@ ibool lock_validate(void); /*===============*/ /* out: TRUE if ok */ -#endif /* UNIV_DEBUG */ /* The lock system */ extern lock_sys_t* lock_sys; diff --git a/innobase/include/log0log.h b/innobase/include/log0log.h index 25adc3b24b7..d14a116072d 100644 --- a/innobase/include/log0log.h +++ b/innobase/include/log0log.h @@ -18,9 +18,7 @@ typedef struct log_struct log_t; typedef struct log_group_struct log_group_t; extern ibool log_do_write; -#ifdef UNIV_LOG_DEBUG extern ibool log_debug_writes; -#endif /* UNIV_LOG_DEBUG */ /* Wait modes for log_write_up_to */ #define LOG_NO_WAIT 91 @@ -691,13 +689,11 @@ struct log_struct{ ulint max_buf_free; /* recommended maximum value of buf_free, after which the buffer is flushed */ -#ifdef UNIV_LOG_DEBUG ulint old_buf_free; /* value of buf free when log was last time opened; only in the debug version */ dulint old_lsn; /* value of lsn when log was last time opened; only in the debug version */ -#endif /* UNIV_LOG_DEBUG */ ibool check_flush_or_checkpoint; /* this is set to TRUE when there may be need to flush the log buffer, or diff --git a/innobase/include/log0log.ic b/innobase/include/log0log.ic index 3a4a3c6a5e6..ca7531783a2 100644 --- a/innobase/include/log0log.ic +++ b/innobase/include/log0log.ic @@ -10,7 +10,6 @@ Created 12/9/1995 Heikki Tuuri #include "mach0data.h" #include "mtr0mtr.h" -#ifdef UNIV_LOG_DEBUG /********************************************************** Checks by parsing that the catenated log segment for a single mtr is consistent. */ @@ -22,7 +21,6 @@ log_check_log_recs( in the log_sys->buf log buffer */ ulint len, /* in: segment length in bytes */ dulint buf_start_lsn); /* in: buffer start lsn */ -#endif /* UNIV_LOG_DEBUG */ /**************************************************************** Gets a log block flush bit. */ diff --git a/innobase/include/mem0dbg.h b/innobase/include/mem0dbg.h index 61c66cc218c..96f30842df6 100644 --- a/innobase/include/mem0dbg.h +++ b/innobase/include/mem0dbg.h @@ -31,7 +31,6 @@ check fields at the both ends of the field. */ #define MEM_SPACE_NEEDED(N) ut_calc_align((N), UNIV_MEM_ALIGNMENT) #endif -#ifdef UNIV_DEBUG /******************************************************************* Checks a memory heap for consistency and prints the contents if requested. Outputs the sum of sizes of buffers given to the user (only in @@ -61,7 +60,6 @@ mem_heap_validate_or_print( ulint* n_blocks); /* out: number of blocks in the heap, if a NULL pointer is passed as this argument, it is ignored */ -#endif /* UNIV_DEBUG */ #ifdef UNIV_MEM_DEBUG /****************************************************************** Prints the contents of a memory heap. */ @@ -71,7 +69,6 @@ mem_heap_print( /*===========*/ mem_heap_t* heap); /* in: memory heap */ #endif /* UNIV_MEM_DEBUG */ -#ifdef UNIV_DEBUG /****************************************************************** Checks that an object is a memory heap (or a block of it) */ @@ -88,7 +85,6 @@ mem_heap_validate( /*==============*/ /* out: TRUE if ok */ mem_heap_t* heap); /* in: memory heap */ -#endif /* UNIV_DEBUG */ #ifdef UNIV_MEM_DEBUG /********************************************************************* TRUE if no memory is currently allocated. */ @@ -120,4 +116,17 @@ void mem_analyze_corruption( /*===================*/ byte* ptr); /* in: pointer to place of possible corruption */ +/********************************************************************* +Prints information of dynamic memory usage and currently allocated memory +heaps or buffers. Can only be used in the debug version. */ +void +mem_print_info(void); +/*================*/ +/********************************************************************* +Prints information of dynamic memory usage and currently allocated memory +heaps or buffers since the last ..._print_info or..._print_new_info. */ + +void +mem_print_new_info(void); +/*====================*/ diff --git a/innobase/include/mem0dbg.ic b/innobase/include/mem0dbg.ic index 00db22eb518..7ce5f6f1ba5 100644 --- a/innobase/include/mem0dbg.ic +++ b/innobase/include/mem0dbg.ic @@ -56,7 +56,6 @@ mem_hash_insert( mem_heap_t* heap, /* in: the created heap */ const char* file_name, /* in: file name of creation */ ulint line); /* in: line where created */ -#ifdef UNIV_MEM_DEBUG /******************************************************************* Removes a memory heap (which is going to be freed by the caller) from the list of live memory heaps. Returns the size of the heap @@ -72,7 +71,7 @@ mem_hash_remove( mem_heap_t* heap, /* in: the heap to be freed */ const char* file_name, /* in: file name of freeing */ ulint line); /* in: line where freed */ -#endif /* UNIV_MEM_DEBUG */ + void mem_field_header_set_len(byte* field, ulint len); diff --git a/innobase/include/mem0mem.h b/innobase/include/mem0mem.h index 18bffe5732e..cd01ac77bf3 100644 --- a/innobase/include/mem0mem.h +++ b/innobase/include/mem0mem.h @@ -261,18 +261,6 @@ mem_free_func( const char* file_name, /* in: file name where created */ ulint line /* in: line where created */ ); -/******************************************************************* -Implements realloc. */ -UNIV_INLINE -void* -mem_realloc( -/*========*/ - /* out, own: free storage, - NULL if did not succeed */ - void* buf, /* in: pointer to an old buffer */ - ulint n, /* in: desired number of bytes */ - const char* file_name, /* in: file name where called */ - ulint line); /* in: line where called */ /************************************************************************** Duplicates a NUL-terminated string. */ diff --git a/innobase/include/mem0mem.ic b/innobase/include/mem0mem.ic index df3dbf9b576..d97b7d6c4dd 100644 --- a/innobase/include/mem0mem.ic +++ b/innobase/include/mem0mem.ic @@ -562,24 +562,6 @@ mem_heap_get_size( return(size); } -/******************************************************************* -Implements realloc. */ -UNIV_INLINE -void* -mem_realloc( -/*========*/ - /* out, own: free storage, - NULL if did not succeed */ - void* buf, /* in: pointer to an old buffer */ - ulint n, /* in: desired number of bytes */ - const char* file_name, /* in: file name where called */ - ulint line) /* in: line where called */ -{ - mem_free(buf); - - return(mem_alloc_func(n, file_name, line)); -} - /************************************************************************** Duplicates a NUL-terminated string. */ UNIV_INLINE diff --git a/innobase/include/mem0pool.h b/innobase/include/mem0pool.h index 95cf19676fb..43707bd5f61 100644 --- a/innobase/include/mem0pool.h +++ b/innobase/include/mem0pool.h @@ -83,7 +83,6 @@ Releases the mem pool mutex. */ void mem_pool_mutex_exit(void); /*=====================*/ -#ifdef UNIV_DEBUG /************************************************************************ Validates a memory pool. */ @@ -100,7 +99,7 @@ mem_pool_print_info( /*================*/ FILE* outfile,/* in: output file to write to */ mem_pool_t* pool); /* in: memory pool */ -#endif /* UNIV_DEBUG */ + #ifndef UNIV_NONINL #include "mem0pool.ic" diff --git a/innobase/include/mtr0mtr.h b/innobase/include/mtr0mtr.h index 55fa3d80a42..e8c68a91dad 100644 --- a/innobase/include/mtr0mtr.h +++ b/innobase/include/mtr0mtr.h @@ -246,7 +246,6 @@ mtr_memo_contains( mtr_t* mtr, /* in: mtr */ void* object, /* in: object to search */ ulint type); /* in: type of object */ -#ifdef UNIV_DEBUG /************************************************************* Prints info of an mtr handle. */ @@ -254,7 +253,6 @@ void mtr_print( /*======*/ mtr_t* mtr); /* in: mtr */ -#endif /* UNIV_DEBUG */ /*######################################################################*/ #define MTR_BUF_MEMO_SIZE 200 /* number of slots in memo */ @@ -287,12 +285,7 @@ struct mtr_memo_slot_struct{ /* Mini-transaction handle and buffer */ struct mtr_struct{ -#ifdef UNIV_DEBUG ulint state; /* MTR_ACTIVE, MTR_COMMITTING, MTR_COMMITTED */ -#define MTR_ACTIVE 12231 -#define MTR_COMMITTING 56456 -#define MTR_COMMITTED 34676 -#endif /* UNIV_DEBUG */ dyn_array_t memo; /* memo stack for locks etc. */ dyn_array_t log; /* mini-transaction log */ ibool modifications; @@ -307,12 +300,15 @@ struct mtr_struct{ this mtr */ dulint end_lsn;/* end lsn of the possible log entry for this mtr */ -#ifdef UNIV_DEBUG ulint magic_n; -#define MTR_MAGIC_N 54551 -#endif /* UNIV_DEBUG */ }; +#define MTR_MAGIC_N 54551 + +#define MTR_ACTIVE 12231 +#define MTR_COMMITTING 56456 +#define MTR_COMMITTED 34676 + #ifndef UNIV_NONINL #include "mtr0mtr.ic" #endif diff --git a/innobase/include/pars0opt.h b/innobase/include/pars0opt.h index ac0e885d05a..d091c3ee2d0 100644 --- a/innobase/include/pars0opt.h +++ b/innobase/include/pars0opt.h @@ -43,7 +43,6 @@ opt_find_all_cols( to add new found columns */ plan_t* plan, /* in: plan or NULL */ que_node_t* exp); /* in: expression or condition */ -#ifdef UNIV_SQL_DEBUG /************************************************************************ Prints info of a query plan. */ @@ -51,7 +50,6 @@ void opt_print_query_plan( /*=================*/ sel_node_t* sel_node); /* in: select node */ -#endif /* UNIV_SQL_DEBUG */ #ifndef UNIV_NONINL #include "pars0opt.ic" diff --git a/innobase/include/que0que.h b/innobase/include/que0que.h index a438116781f..e1874edcaf2 100644 --- a/innobase/include/que0que.h +++ b/innobase/include/que0que.h @@ -152,6 +152,17 @@ que_run_threads( /*============*/ que_thr_t* thr); /* in: query thread which is run initially */ /************************************************************************** +After signal handling is finished, returns control to a query graph error +handling routine. (Currently, just returns the control to the root of the +graph so that the graph can communicate an error message to the client.) */ + +void +que_fork_error_handle( +/*==================*/ + trx_t* trx, /* in: trx */ + que_t* fork); /* in: query graph which was run before signal + handling started, NULL not allowed */ +/************************************************************************** Handles an SQL error noticed during query thread execution. At the moment, does nothing! */ @@ -170,15 +181,18 @@ a single worker thread to execute it. This function should be used to end the wait state of a query thread waiting for a lock or a stored procedure completion. */ -que_thr_t* +void que_thr_end_wait( /*=============*/ - /* out: next query thread to run; - NULL if none */ - que_thr_t* thr); /* in: query thread in the + que_thr_t* thr, /* in: query thread in the QUE_THR_LOCK_WAIT, or QUE_THR_PROCEDURE_WAIT, or QUE_THR_SIG_REPLY_WAIT state */ + que_thr_t** next_thr); /* in/out: next query thread to run; + if the value which is passed in is + a pointer to a NULL pointer, then the + calling function can start running + a new query thread */ /************************************************************************** Same as que_thr_end_wait, but no parameter next_thr available. */ @@ -293,6 +307,22 @@ que_thr_peek_stop( mutex reserved is necessary before deciding the actual stopping */ que_thr_t* thr); /* in: query thread */ +/*************************************************************************** +Returns TRUE if the query graph is for a SELECT statement. */ +UNIV_INLINE +ibool +que_graph_is_select( +/*================*/ + /* out: TRUE if a select */ + que_t* graph); /* in: graph */ +/************************************************************************** +Prints info of an SQL query graph node. */ + +void +que_node_print_info( +/*================*/ + que_node_t* node); /* in: query graph node */ + /* Query graph query thread node: the fields are protected by the kernel mutex with the exceptions named below */ diff --git a/innobase/include/que0que.ic b/innobase/include/que0que.ic index a63922f8c80..ae4ed10560f 100644 --- a/innobase/include/que0que.ic +++ b/innobase/include/que0que.ic @@ -238,3 +238,21 @@ que_thr_peek_stop( return(FALSE); } + +/*************************************************************************** +Returns TRUE if the query graph is for a SELECT statement. */ +UNIV_INLINE +ibool +que_graph_is_select( +/*================*/ + /* out: TRUE if a select */ + que_t* graph) /* in: graph */ +{ + if (graph->fork_type == QUE_FORK_SELECT_SCROLL + || graph->fork_type == QUE_FORK_SELECT_NON_SCROLL) { + + return(TRUE); + } + + return(FALSE); +} diff --git a/innobase/include/row0ins.h b/innobase/include/row0ins.h index f3f0b7e8eca..a5b4b74e7fc 100644 --- a/innobase/include/row0ins.h +++ b/innobase/include/row0ins.h @@ -145,12 +145,11 @@ struct ins_node_struct{ entry_list and sys fields are stored here; if this is NULL, entry list should be created and buffers for sys fields in row allocated */ -#ifdef UNIV_DEBUG ulint magic_n; -#define INS_NODE_MAGIC_N 15849075 -#endif /* UNIV_DEBUG */ }; +#define INS_NODE_MAGIC_N 15849075 + /* Insert node types */ #define INS_SEARCHED 0 /* INSERT INTO ... SELECT ... */ #define INS_VALUES 1 /* INSERT INTO ... VALUES ... */ diff --git a/innobase/include/row0upd.h b/innobase/include/row0upd.h index 687e90deee5..f5e0a88231f 100644 --- a/innobase/include/row0upd.h +++ b/innobase/include/row0upd.h @@ -384,12 +384,11 @@ struct upd_node_struct{ sym_node_t* table_sym;/* table node in symbol table */ que_node_t* col_assign_list; /* column assignment list */ -#ifdef UNIV_DEBUG ulint magic_n; -#define UPD_NODE_MAGIC_N 1579975 -#endif /* UNIV_DEBUG */ }; +#define UPD_NODE_MAGIC_N 1579975 + /* Node execution states */ #define UPD_NODE_SET_IX_LOCK 1 /* execution came to the node from a node above and if the field diff --git a/innobase/include/srv0que.h b/innobase/include/srv0que.h new file mode 100644 index 00000000000..05c339cdd32 --- /dev/null +++ b/innobase/include/srv0que.h @@ -0,0 +1,53 @@ +/****************************************************** +Server query execution + +(c) 1996 Innobase Oy + +Created 6/5/1996 Heikki Tuuri +*******************************************************/ + + +#ifndef srv0que_h +#define srv0que_h + +#include "univ.i" +#include "que0types.h" + +/************************************************************************** +Checks if there is work to do in the server task queue. If there is, the +thread starts processing a task. Before leaving, it again checks the task +queue and picks a new task if any exists. This is called by a SRV_WORKER +thread. */ + +void +srv_que_task_queue_check(void); +/*==========================*/ +/************************************************************************** +Performs round-robin on the server tasks. This is called by a SRV_WORKER +thread every second or so. */ + +que_thr_t* +srv_que_round_robin( +/*================*/ + /* out: the new (may be == thr) query thread + to run */ + que_thr_t* thr); /* in: query thread */ +/************************************************************************** +Enqueues a task to server task queue and releases a worker thread, if +there exists one suspended. */ + +void +srv_que_task_enqueue( +/*=================*/ + que_thr_t* thr); /* in: query thread */ +/************************************************************************** +Enqueues a task to server task queue and releases a worker thread, if +there exists one suspended. */ + +void +srv_que_task_enqueue_low( +/*=====================*/ + que_thr_t* thr); /* in: query thread */ + +#endif + diff --git a/innobase/include/sync0rw.h b/innobase/include/sync0rw.h index 63b01ffac80..9a988a03e92 100644 --- a/innobase/include/sync0rw.h +++ b/innobase/include/sync0rw.h @@ -85,7 +85,6 @@ void rw_lock_free( /*=========*/ rw_lock_t* lock); /* in: rw-lock */ -#ifdef UNIV_DEBUG /********************************************************************** Checks that the rw-lock has been initialized and that there are no simultaneous shared and exclusive locks. */ @@ -94,7 +93,6 @@ ibool rw_lock_validate( /*=============*/ rw_lock_t* lock); -#endif /* UNIV_DEBUG */ /****************************************************************** NOTE! The following macros should be used in rw s-locking, not the corresponding function. */ diff --git a/innobase/include/sync0sync.h b/innobase/include/sync0sync.h index 4307ca5ba0c..8e0ec715b12 100644 --- a/innobase/include/sync0sync.h +++ b/innobase/include/sync0sync.h @@ -143,7 +143,6 @@ void sync_print( /*=======*/ FILE* file); /* in: file where to print */ -#ifdef UNIV_DEBUG /********************************************************************** Checks that the mutex has been initialized. */ @@ -151,7 +150,6 @@ ibool mutex_validate( /*===========*/ mutex_t* mutex); -#endif /* UNIV_DEBUG */ /********************************************************************** Sets the mutex latching level field. */ diff --git a/innobase/include/trx0roll.h b/innobase/include/trx0roll.h index aa88fc249fc..6004551f456 100644 --- a/innobase/include/trx0roll.h +++ b/innobase/include/trx0roll.h @@ -91,12 +91,16 @@ trx_undo_rec_release( /************************************************************************* Starts a rollback operation. */ -que_thr_t* +void trx_rollback( /*=========*/ - /* out: next query thread to run */ trx_t* trx, /* in: transaction */ - trx_sig_t* sig); /* in: signal starting the rollback */ + trx_sig_t* sig, /* in: signal starting the rollback */ + que_thr_t** next_thr);/* in/out: next query thread to run; + if the value which is passed in is + a pointer to a NULL pointer, then the + calling function can start running + a new query thread */ /*********************************************************************** Rollback or clean up transactions which have no user session. If the transaction already was committed, then we clean up a possible insert @@ -108,12 +112,17 @@ trx_rollback_or_clean_all_without_sess(void); /******************************************************************** Finishes a transaction rollback. */ -que_thr_t* +void trx_finish_rollback_off_kernel( /*===========================*/ - /* out: next query thread to run */ que_t* graph, /* in: undo graph which can now be freed */ - trx_t* trx); /* in: transaction */ + trx_t* trx, /* in: transaction */ + que_thr_t** next_thr);/* in/out: next query thread to run; + if the value which is passed in is + a pointer to a NULL pointer, then the + calling function can start running + a new query thread; if this parameter is + NULL, it is ignored */ /******************************************************************** Builds an undo 'query' graph for a transaction. The actual rollback is performed by executing this query graph like a query subprocedure call. diff --git a/innobase/include/trx0trx.h b/innobase/include/trx0trx.h index 9b7ac22e617..a8c1df534da 100644 --- a/innobase/include/trx0trx.h +++ b/innobase/include/trx0trx.h @@ -194,10 +194,9 @@ trx_end_lock_wait( /******************************************************************** Sends a signal to a trx object. */ -que_thr_t* +ibool trx_sig_send( /*=========*/ - /* out: next query thread to run */ /* out: TRUE if the signal was successfully delivered */ trx_t* trx, /* in: trx handle */ @@ -207,17 +206,27 @@ trx_sig_send( que_thr_t* receiver_thr, /* in: query thread which wants the reply, or NULL; if type is TRX_SIG_END_WAIT, this must be NULL */ - trx_savept_t* savept); /* in: possible rollback savepoint, or + trx_savept_t* savept, /* in: possible rollback savepoint, or NULL */ + que_thr_t** next_thr); /* in/out: next query thread to run; + if the value which is passed in is + a pointer to a NULL pointer, then the + calling function can start running + a new query thread; if the parameter + is NULL, it is ignored */ /******************************************************************** Send the reply message when a signal in the queue of the trx has been handled. */ -que_thr_t* +void trx_sig_reply( /*==========*/ - /* out: next query thread to run */ - trx_sig_t* sig); /* in: signal */ + trx_sig_t* sig, /* in: signal */ + que_thr_t** next_thr); /* in/out: next query thread to run; + if the value which is passed in is + a pointer to a NULL pointer, then the + calling function can start running + a new query thread */ /******************************************************************** Removes the signal object from a trx signal queue. */ @@ -229,11 +238,15 @@ trx_sig_remove( /******************************************************************** Starts handling of a trx signal. */ -que_thr_t* +void trx_sig_start_handle( /*=================*/ - /* out: next query thread to run, or NULL */ - trx_t* trx); /* in: trx handle */ + trx_t* trx, /* in: trx handle */ + que_thr_t** next_thr); /* in/out: next query thread to run; + if the value which is passed in is + a pointer to a NULL pointer, then the + calling function can start running + a new query thread */ /******************************************************************** Ends signal handling. If the session is in the error state, and trx->graph_before_signal_handling != NULL, returns control to the error diff --git a/innobase/include/usr0sess.h b/innobase/include/usr0sess.h index aeff7191e68..c7bcfb20fed 100644 --- a/innobase/include/usr0sess.h +++ b/innobase/include/usr0sess.h @@ -38,6 +38,7 @@ sess_try_close( /* The session handle. All fields are protected by the kernel mutex */ struct sess_struct{ + ulint state; /* state of the session */ trx_t* trx; /* transaction object permanently assigned for the session: the transaction instance designated by the @@ -48,6 +49,11 @@ struct sess_struct{ session */ }; +/* Session states */ +#define SESS_ACTIVE 1 +#define SESS_ERROR 2 /* session contains an error message + which has not yet been communicated + to the client */ #ifndef UNIV_NONINL #include "usr0sess.ic" #endif diff --git a/innobase/include/ut0mem.h b/innobase/include/ut0mem.h index 85b99efff68..2e02b3f0b6b 100644 --- a/innobase/include/ut0mem.h +++ b/innobase/include/ut0mem.h @@ -67,6 +67,37 @@ ut_free( /*====*/ void* ptr); /* in, own: memory block */ /************************************************************************** +Implements realloc. This is needed by /pars/lexyy.c. Otherwise, you should not +use this function because the allocation functions in mem0mem.h are the +recommended ones in InnoDB. + +man realloc in Linux, 2004: + + realloc() changes the size of the memory block pointed to + by ptr to size bytes. The contents will be unchanged to + the minimum of the old and new sizes; newly allocated mem + ory will be uninitialized. If ptr is NULL, the call is + equivalent to malloc(size); if size is equal to zero, the + call is equivalent to free(ptr). Unless ptr is NULL, it + must have been returned by an earlier call to malloc(), + calloc() or realloc(). + +RETURN VALUE + realloc() returns a pointer to the newly allocated memory, + which is suitably aligned for any kind of variable and may + be different from ptr, or NULL if the request fails. If + size was equal to 0, either NULL or a pointer suitable to + be passed to free() is returned. If realloc() fails the + original block is left untouched - it is not freed or + moved. */ + +void* +ut_realloc( +/*=======*/ + /* out, own: pointer to new mem block or NULL */ + void* ptr, /* in: pointer to old block or NULL */ + ulint size); /* in: desired size */ +/************************************************************************** Frees in shutdown all allocated memory not freed yet. */ void diff --git a/innobase/lock/lock0lock.c b/innobase/lock/lock0lock.c index 58c88f67017..be0806590a9 100644 --- a/innobase/lock/lock0lock.c +++ b/innobase/lock/lock0lock.c @@ -292,9 +292,7 @@ waiting, in its lock queue. Solution: We can copy the locks as gap type locks, so that also the waiting locks are transformed to granted gap type locks on the inserted record. */ -#ifdef UNIV_DEBUG ibool lock_print_waits = FALSE; -#endif /* UNIV_DEBUG */ /* The lock system */ lock_sys_t* lock_sys = NULL; @@ -1354,8 +1352,7 @@ lock_rec_has_expl( return(NULL); } - -#ifdef UNIV_DEBUG + /************************************************************************* Checks if some other transaction has a lock request in the queue. */ static @@ -1398,7 +1395,6 @@ lock_rec_other_has_expl_req( return(NULL); } -#endif /* UNIV_DEBUG */ /************************************************************************* Checks if some other transaction has a conflicting explicit lock request @@ -1688,13 +1684,11 @@ lock_rec_enqueue_waiting( ut_a(que_thr_stop(thr)); -#ifdef UNIV_DEBUG if (lock_print_waits) { fprintf(stderr, "Lock wait for trx %lu in index ", (ulong) ut_dulint_get_low(trx->id)); ut_print_name(stderr, index->name); } -#endif /* UNIV_DEBUG */ return(DB_LOCK_WAIT); } @@ -2034,12 +2028,10 @@ lock_grant( lock->trx->n_tables_locked++; } -#ifdef UNIV_DEBUG if (lock_print_waits) { fprintf(stderr, "Lock wait for trx %lu ends\n", (ulong) ut_dulint_get_low(lock->trx->id)); } -#endif /* UNIV_DEBUG */ /* If we are resolving a deadlock by choosing another transaction as a victim, then our original transaction may not be in the @@ -3110,11 +3102,9 @@ lock_deadlock_recursive( lock_table_print(ef, start->wait_lock); } -#ifdef UNIV_DEBUG if (lock_print_waits) { fputs("Deadlock detected\n", stderr); } -#endif /* UNIV_DEBUG */ if (ut_dulint_cmp(wait_lock->trx->undo_no, start->undo_no) >= 0) { @@ -4177,7 +4167,6 @@ loop: goto loop; } -#ifdef UNIV_DEBUG /************************************************************************* Validates the lock queue on a table. */ @@ -4488,7 +4477,6 @@ lock_validate(void) return(TRUE); } -#endif /* UNIV_DEBUG */ /*============ RECORD LOCK CHECKS FOR ROW OPERATIONS ====================*/ diff --git a/innobase/log/log0log.c b/innobase/log/log0log.c index 7d7c7dcf998..923ab448e07 100644 --- a/innobase/log/log0log.c +++ b/innobase/log/log0log.c @@ -32,9 +32,9 @@ ulint log_fsp_current_free_limit = 0; log_t* log_sys = NULL; ibool log_do_write = TRUE; -#ifdef UNIV_LOG_DEBUG + ibool log_debug_writes = FALSE; -#endif /* UNIV_LOG_DEBUG */ + /* These control how often we print warnings if the last checkpoint is too old */ @@ -945,12 +945,10 @@ log_group_check_flush_completion( #endif /* UNIV_SYNC_DEBUG */ if (!log_sys->one_flushed && group->n_pending_writes == 0) { -#ifdef UNIV_LOG_DEBUG if (log_debug_writes) { fprintf(stderr, "Log flushed first to group %lu\n", (ulong) group->id); } -#endif /* UNIV_LOG_DEBUG */ log_sys->written_to_some_lsn = log_sys->write_lsn; log_sys->one_flushed = TRUE; @@ -958,12 +956,10 @@ log_group_check_flush_completion( return(LOG_UNLOCK_NONE_FLUSHED_LOCK); } -#ifdef UNIV_LOG_DEBUG if (log_debug_writes && (group->n_pending_writes == 0)) { fprintf(stderr, "Log flushed to group %lu\n", (ulong) group->id); } -#endif /* UNIV_LOG_DEBUG */ return(0); } @@ -1041,13 +1037,11 @@ log_io_complete( fil_flush(group->space_id); } -#ifdef UNIV_LOG_DEBUG if (log_debug_writes) { fprintf(stderr, "Checkpoint info written to group %lu\n", group->id); } -#endif /* UNIV_LOG_DEBUG */ log_io_complete_checkpoint(); @@ -1110,13 +1104,11 @@ log_group_file_header_flush( dest_offset = nth_file * group->file_size; -#ifdef UNIV_LOG_DEBUG if (log_debug_writes) { fprintf(stderr, "Writing log file header to group %lu file %lu\n", (ulong) group->id, (ulong) nth_file); } -#endif /* UNIV_LOG_DEBUG */ if (log_do_write) { log_sys->n_log_ios++; @@ -1200,7 +1192,6 @@ loop: write_len = len; } -#ifdef UNIV_LOG_DEBUG if (log_debug_writes) { fprintf(stderr, @@ -1224,7 +1215,6 @@ loop: + i * OS_FILE_LOG_BLOCK_SIZE)); } } -#endif /* UNIV_LOG_DEBUG */ /* Calculate the checksums for each log block and write them to the trailer fields of the log blocks */ @@ -1352,7 +1342,6 @@ loop: return; } -#ifdef UNIV_LOG_DEBUG if (log_debug_writes) { fprintf(stderr, "Writing log from %lu %lu up to lsn %lu %lu\n", @@ -1361,7 +1350,6 @@ loop: (ulong) ut_dulint_get_high(log_sys->lsn), (ulong) ut_dulint_get_low(log_sys->lsn)); } -#endif /* UNIV_LOG_DEBUG */ log_sys->n_pending_writes++; @@ -1931,14 +1919,12 @@ log_checkpoint( log_sys->next_checkpoint_lsn = oldest_lsn; -#ifdef UNIV_LOG_DEBUG if (log_debug_writes) { fprintf(stderr, "Making checkpoint no %lu at lsn %lu %lu\n", (ulong) ut_dulint_get_low(log_sys->next_checkpoint_no), (ulong) ut_dulint_get_high(oldest_lsn), (ulong) ut_dulint_get_low(oldest_lsn)); } -#endif /* UNIV_LOG_DEBUG */ log_groups_write_checkpoint_info(); @@ -2319,11 +2305,9 @@ loop: exit(1); } -#ifdef UNIV_LOG_DEBUG if (log_debug_writes) { fprintf(stderr, "Created archive file %s\n", name); } -#endif /* UNIV_LOG_DEBUG */ ret = os_file_close(file_handle); @@ -2350,7 +2334,6 @@ loop: len = group->file_size - (next_offset % group->file_size); } -#ifdef UNIV_LOG_DEBUG if (log_debug_writes) { fprintf(stderr, "Archiving starting at lsn %lu %lu, len %lu to group %lu\n", @@ -2358,7 +2341,6 @@ loop: (ulong) ut_dulint_get_low(start_lsn), (ulong) len, (ulong) group->id); } -#endif /* UNIV_LOG_DEBUG */ log_sys->n_pending_archive_ios++; @@ -2449,13 +2431,11 @@ log_archive_write_complete_groups(void) trunc_files = n_files - 1; } -#ifdef UNIV_LOG_DEBUG if (log_debug_writes && trunc_files) { fprintf(stderr, "Complete file(s) archived to group %lu\n", (ulong) group->id); } -#endif /* UNIV_LOG_DEBUG */ /* Calculate the archive file space start lsn */ start_lsn = ut_dulint_subtract(log_sys->next_archived_lsn, @@ -2478,11 +2458,9 @@ log_archive_write_complete_groups(void) fil_space_truncate_start(group->archive_space_id, trunc_files * group->file_size); -#ifdef UNIV_LOG_DEBUG if (log_debug_writes) { fputs("Archiving writes completed\n", stderr); } -#endif /* UNIV_LOG_DEBUG */ } /********************************************************** @@ -2499,11 +2477,9 @@ log_archive_check_completion_low(void) if (log_sys->n_pending_archive_ios == 0 && log_sys->archiving_phase == LOG_ARCHIVE_READ) { -#ifdef UNIV_LOG_DEBUG if (log_debug_writes) { fputs("Archiving read completed\n", stderr); } -#endif /* UNIV_LOG_DEBUG */ /* Archive buffer has now been read in: start archive writes */ @@ -2647,7 +2623,6 @@ loop: log_sys->next_archived_lsn = limit_lsn; -#ifdef UNIV_LOG_DEBUG if (log_debug_writes) { fprintf(stderr, "Archiving from lsn %lu %lu to lsn %lu %lu\n", @@ -2656,7 +2631,6 @@ loop: (ulong) ut_dulint_get_high(limit_lsn), (ulong) ut_dulint_get_low(limit_lsn)); } -#endif /* UNIV_LOG_DEBUG */ /* Read the log segment to the archive buffer */ @@ -2759,14 +2733,12 @@ log_archive_close_groups( group->archived_file_no += 2; } -#ifdef UNIV_LOG_DEBUG if (log_debug_writes) { fprintf(stderr, "Incrementing arch file no to %lu in log group %lu\n", (ulong) group->archived_file_no + 2, (ulong) group->id); } -#endif /* UNIV_LOG_DEBUG */ } } @@ -3181,7 +3153,6 @@ loop: ut_a(0 == ut_dulint_cmp(lsn, log_sys->lsn)); } -#ifdef UNIV_LOG_DEBUG /********************************************************** Checks by parsing that the catenated log segment for a single mtr is consistent. */ @@ -3234,7 +3205,6 @@ log_check_log_recs( return(TRUE); } -#endif /* UNIV_LOG_DEBUG */ /********************************************************** Peeks the current lsn. */ diff --git a/innobase/log/log0recv.c b/innobase/log/log0recv.c index 49d537d23db..7e57efcf9e1 100644 --- a/innobase/log/log0recv.c +++ b/innobase/log/log0recv.c @@ -189,7 +189,6 @@ recv_sys_empty_hash(void) recv_sys->addr_hash = hash_create(buf_pool_get_curr_size() / 256); } -#ifndef UNIV_LOG_DEBUG /************************************************************ Frees the recovery system. */ static @@ -209,7 +208,6 @@ recv_sys_free(void) mutex_exit(&(recv_sys->mutex)); } -#endif /* !UNIV_LOG_DEBUG */ /************************************************************ Truncates possible corrupted or extra records from a log group. */ @@ -491,7 +489,6 @@ recv_find_max_checkpoint( log_group_read_checkpoint_info(group, field); if (!recv_check_cp_is_consistent(buf)) { -#ifdef UNIV_LOG_DEBUG if (log_debug_writes) { fprintf(stderr, "InnoDB: Checkpoint in group %lu at %lu invalid, %lu\n", @@ -501,7 +498,6 @@ recv_find_max_checkpoint( + LOG_CHECKPOINT_CHECKSUM_1)); } -#endif /* UNIV_LOG_DEBUG */ goto not_consistent; } @@ -515,14 +511,12 @@ recv_find_max_checkpoint( checkpoint_no = mach_read_from_8(buf + LOG_CHECKPOINT_NO); -#ifdef UNIV_LOG_DEBUG if (log_debug_writes) { fprintf(stderr, "InnoDB: Checkpoint number %lu found in group %lu\n", (ulong) ut_dulint_get_low(checkpoint_no), (ulong) group->id); } -#endif /* UNIV_LOG_DEBUG */ if (ut_dulint_cmp(checkpoint_no, max_no) >= 0) { *max_group = group; @@ -1146,7 +1140,6 @@ recv_recover_page( start_lsn = recv->start_lsn; } -#ifdef UNIV_LOG_DEBUG if (log_debug_writes) { fprintf(stderr, "InnoDB: Applying log rec type %lu len %lu to space %lu page no %lu\n", @@ -1154,7 +1147,6 @@ recv_recover_page( (ulong) recv_addr->space, (ulong) recv_addr->page_no); } -#endif /* UNIV_LOG_DEBUG */ recv_parse_or_apply_log_rec_body(recv->type, buf, buf + recv->len, page, &mtr); @@ -1397,7 +1389,6 @@ loop: mutex_exit(&(recv_sys->mutex)); } -#ifdef UNIV_HOTBACKUP /* This page is allocated from the buffer pool and used in the function below */ page_t* recv_backup_application_page = NULL; @@ -1522,9 +1513,8 @@ skip_this_recv_addr: recv_sys_empty_hash(); } -#endif /* UNIV_HOTBACKUP */ -#ifdef UNIV_LOG_REPLICATE +#ifdef notdefined /*********************************************************************** In the debug version, updates the replica of a file page, based on a log record. */ @@ -1820,11 +1810,10 @@ recv_calc_lsn_on_data_add( return(ut_dulint_add(lsn, lsn_len)); } -#ifdef UNIV_LOG_DEBUG /*********************************************************** Checks that the parser recognizes incomplete initial segments of a log record as incomplete. */ -static + void recv_check_incomplete_log_recs( /*===========================*/ @@ -1842,7 +1831,6 @@ recv_check_incomplete_log_recs( &page_no, &body)); } } -#endif /* UNIV_LOG_DEBUG */ /*********************************************************** Prints diagnostic info of corrupt log. */ @@ -1979,14 +1967,12 @@ loop: recv_sys->recovered_offset += len; recv_sys->recovered_lsn = new_recovered_lsn; -#ifdef UNIV_LOG_DEBUG if (log_debug_writes) { fprintf(stderr, "InnoDB: Parsed a single log rec type %lu len %lu space %lu page no %lu\n", (ulong) type, (ulong) len, (ulong) space, (ulong) page_no); } -#endif /* UNIV_LOG_DEBUG */ if (type == MLOG_DUMMY_RECORD) { /* Do nothing */ @@ -2070,14 +2056,12 @@ loop: #endif /* UNIV_LOG_REPLICATE */ } -#ifdef UNIV_LOG_DEBUG if (log_debug_writes) { fprintf(stderr, "InnoDB: Parsed a multi log rec type %lu len %lu space %lu page no %lu\n", (ulong) type, (ulong) len, (ulong) space, (ulong) page_no); } -#endif /* UNIV_LOG_DEBUG */ total_len += len; n_recs++; @@ -2484,7 +2468,6 @@ recv_group_scan_log_recs( start_lsn = end_lsn; } -#ifdef UNIV_LOG_DEBUG if (log_debug_writes) { fprintf(stderr, "InnoDB: Scanned group %lu up to log sequence number %lu %lu\n", @@ -2492,7 +2475,6 @@ recv_group_scan_log_recs( (ulong) ut_dulint_get_high(*group_scanned_lsn), (ulong) ut_dulint_get_low(*group_scanned_lsn)); } -#endif /* UNIV_LOG_DEBUG */ } /************************************************************ @@ -2879,12 +2861,10 @@ recv_recovery_from_checkpoint_finish(void) recv_apply_hashed_log_recs(TRUE); } -#ifdef UNIV_LOG_DEBUG if (log_debug_writes) { fprintf(stderr, "InnoDB: Log records applied to the database\n"); } -#endif /* UNIV_LOG_DEBUG */ if (recv_needed_recovery) { trx_sys_print_mysql_master_log_pos(); @@ -3214,7 +3194,6 @@ ask_again: break; } -#ifdef UNIV_LOG_DEBUG if (log_debug_writes) { fprintf(stderr, "InnoDB: Archive read starting at lsn %lu %lu, len %lu from file %s\n", @@ -3222,7 +3201,6 @@ ask_again: (ulong) ut_dulint_get_low(start_lsn), (ulong) len, name); } -#endif /* UNIV_LOG_DEBUG */ fil_io(OS_FILE_READ | OS_FILE_LOG, TRUE, group->archive_space_id, read_offset / UNIV_PAGE_SIZE, diff --git a/innobase/mem/mem0dbg.c b/innobase/mem/mem0dbg.c index d86784cb048..f46984f6bb3 100644 --- a/innobase/mem/mem0dbg.c +++ b/innobase/mem/mem0dbg.c @@ -373,7 +373,6 @@ mem_hash_remove( } #endif /* UNIV_MEM_DEBUG */ -#ifdef UNIV_DEBUG /******************************************************************* Checks a memory heap for consistency and prints the contents if requested. Outputs the sum of sizes of buffers given to the user (only in @@ -603,7 +602,6 @@ mem_heap_validate( return(TRUE); } -#endif /* UNIV_DEBUG */ #ifdef UNIV_MEM_DEBUG /********************************************************************* @@ -816,3 +814,128 @@ mem_analyze_corruption( dist++; } } + +/********************************************************************* +Prints information of dynamic memory usage and currently allocated +memory heaps or buffers. Can only be used in the debug version. */ +static +void +mem_print_info_low( +/*===============*/ + ibool print_all) /* in: if TRUE, all heaps are printed, + else only the heaps allocated after the + previous call of this function */ +{ +#ifdef UNIV_MEM_DEBUG + mem_hash_node_t* node; + ulint n_heaps = 0; + ulint allocated_mem; + ulint ph_size; + ulint total_allocated_mem = 0; + ibool error; + ulint n_blocks; +#endif + FILE* outfile; + + /* outfile = fopen("ibdebug", "a"); */ + + outfile = stdout; + + fprintf(outfile, "\n"); + fprintf(outfile, + "________________________________________________________\n"); + fprintf(outfile, "MEMORY ALLOCATION INFORMATION\n\n"); + +#ifndef UNIV_MEM_DEBUG + + UT_NOT_USED(print_all); + + mem_pool_print_info(outfile, mem_comm_pool); + + fprintf(outfile, + "Sorry, non-debug version cannot give more memory info\n"); + + /* fclose(outfile); */ + + return; +#else + mutex_enter(&mem_hash_mutex); + + fprintf(outfile, "LIST OF CREATED HEAPS AND ALLOCATED BUFFERS: \n\n"); + + if (!print_all) { + fprintf(outfile, "AFTER THE LAST PRINT INFO\n"); + } + + node = UT_LIST_GET_FIRST(mem_all_list_base); + + while (node != NULL) { + n_heaps++; + + if (!print_all && node->nth_heap < mem_last_print_info) { + + goto next_heap; + } + + mem_heap_validate_or_print(node->heap, NULL, + FALSE, &error, &allocated_mem, + &ph_size, &n_blocks); + total_allocated_mem += allocated_mem; + + fprintf(outfile, + "%lu: file %s line %lu of size %lu phys.size %lu with %lu blocks, type %lu\n", + node->nth_heap, node->file_name, node->line, + allocated_mem, ph_size, n_blocks, + (node->heap)->type); + next_heap: + node = UT_LIST_GET_NEXT(all_list, node); + } + + fprintf(outfile, "\n"); + + fprintf(outfile, "Current allocated memory : %lu\n", + mem_current_allocated_memory); + fprintf(outfile, "Current allocated heaps and buffers : %lu\n", + n_heaps); + fprintf(outfile, "Cumulative allocated memory : %lu\n", + mem_total_allocated_memory); + fprintf(outfile, "Maximum allocated memory : %lu\n", + mem_max_allocated_memory); + fprintf(outfile, "Cumulative created heaps and buffers : %lu\n", + mem_n_created_heaps); + fprintf(outfile, "Cumulative number of allocations : %lu\n", + mem_n_allocations); + + mem_last_print_info = mem_n_created_heaps; + + mutex_exit(&mem_hash_mutex); + + mem_pool_print_info(outfile, mem_comm_pool); + +/* mem_validate(); */ + +/* fclose(outfile); */ +#endif +} + +/********************************************************************* +Prints information of dynamic memory usage and currently allocated memory +heaps or buffers. Can only be used in the debug version. */ + +void +mem_print_info(void) +/*================*/ +{ + mem_print_info_low(TRUE); +} + +/********************************************************************* +Prints information of dynamic memory usage and currently allocated memory +heaps or buffers since the last ..._print_info or..._print_new_info. */ + +void +mem_print_new_info(void) +/*====================*/ +{ + mem_print_info_low(FALSE); +} diff --git a/innobase/mem/mem0pool.c b/innobase/mem/mem0pool.c index 0227c2d0969..023369e8ec5 100644 --- a/innobase/mem/mem0pool.c +++ b/innobase/mem/mem0pool.c @@ -559,7 +559,6 @@ mem_area_free( ut_ad(mem_pool_validate(pool)); } -#ifdef UNIV_DEBUG /************************************************************************ Validates a memory pool. */ @@ -637,7 +636,6 @@ mem_pool_print_info( (ulong) pool->reserved); mutex_exit(&(pool->mutex)); } -#endif /* UNIV_DEBUG */ /************************************************************************ Returns the amount of reserved memory. */ diff --git a/innobase/mtr/mtr0mtr.c b/innobase/mtr/mtr0mtr.c index deb4dabde4b..6e918806eb1 100644 --- a/innobase/mtr/mtr0mtr.c +++ b/innobase/mtr/mtr0mtr.c @@ -319,7 +319,6 @@ mtr_read_dulint( return(mach_read_from_8(ptr)); } -#ifdef UNIV_DEBUG /************************************************************* Prints info of an mtr handle. */ @@ -333,4 +332,3 @@ mtr_print( (ulong) dyn_array_get_data_size(&(mtr->memo)), (ulong) dyn_array_get_data_size(&(mtr->log))); } -#endif /* UNIV_DEBUG */ diff --git a/innobase/os/os0file.c b/innobase/os/os0file.c index 469e7c025b6..d5ca8f927c6 100644 --- a/innobase/os/os0file.c +++ b/innobase/os/os0file.c @@ -368,7 +368,7 @@ os_file_handle_error( #undef USE_FILE_LOCK #define USE_FILE_LOCK -#if defined(UNIV_HOTBACKUP) || defined(__WIN__) || defined(__FreeBSD__) +#if defined(UNIV_HOTBACKUP) || defined(__WIN__) || defined(__FreeBSD__) || defined(__NETWARE__) /* InnoDB Hot Backup does not lock the data files. * On Windows, mandatory locking is used. * On FreeBSD with LinuxThreads, advisory locking does not work properly. diff --git a/innobase/pars/lexyy.c b/innobase/pars/lexyy.c index 1b8c69ccc7a..d98d3c9b945 100644 --- a/innobase/pars/lexyy.c +++ b/innobase/pars/lexyy.c @@ -636,9 +636,9 @@ Linux. #include "mem0mem.h" #include "os0proc.h" -#define malloc(A) mem_alloc(A) -#define free(A) mem_free(A) -#define realloc(P, A) mem_realloc(P, A, __FILE__, __LINE__) +#define malloc(A) ut_malloc(A) +#define free(A) ut_free(A) +#define realloc(P, A) ut_realloc(P, A) #define exit(A) ut_error #define YY_INPUT(buf, result, max_size) pars_get_lex_chars(buf, &result, max_size) @@ -655,16 +655,16 @@ string_append( const char* str, /* in: string to be appended */ ulint len) /* in: length of the string */ { + if (stringbuf == NULL) { + stringbuf = malloc(1); + stringbuf_len_alloc = 1; + } + if (stringbuf_len + len > stringbuf_len_alloc) { - if (stringbuf_len_alloc == 0) { - stringbuf_len_alloc++; - } while (stringbuf_len + len > stringbuf_len_alloc) { stringbuf_len_alloc <<= 1; } - stringbuf = stringbuf - ? realloc(stringbuf, stringbuf_len_alloc) - : malloc(stringbuf_len_alloc); + stringbuf = realloc(stringbuf, stringbuf_len_alloc); } memcpy(stringbuf + stringbuf_len, str, len); diff --git a/innobase/pars/pars0lex.l b/innobase/pars/pars0lex.l index 0b1af554bed..4e2399613cb 100644 --- a/innobase/pars/pars0lex.l +++ b/innobase/pars/pars0lex.l @@ -58,9 +58,9 @@ Linux. #include "mem0mem.h" #include "os0proc.h" -#define malloc(A) mem_alloc(A) -#define free(A) mem_free(A) -#define realloc(P, A) mem_realloc(P, A, __FILE__, __LINE__) +#define malloc(A) ut_malloc(A) +#define free(A) ut_free(A) +#define realloc(P, A) ut_realloc(P, A) #define exit(A) ut_error #define YY_INPUT(buf, result, max_size) pars_get_lex_chars(buf, &result, max_size) @@ -77,16 +77,16 @@ string_append( const char* str, /* in: string to be appended */ ulint len) /* in: length of the string */ { + if (stringbuf == NULL) { + stringbuf = malloc(1); + stringbuf_len_alloc = 1; + } + if (stringbuf_len + len > stringbuf_len_alloc) { - if (stringbuf_len_alloc == 0) { - stringbuf_len_alloc++; - } while (stringbuf_len + len > stringbuf_len_alloc) { stringbuf_len_alloc <<= 1; } - stringbuf = stringbuf - ? realloc(stringbuf, stringbuf_len_alloc) - : malloc(stringbuf_len_alloc); + stringbuf = realloc(stringbuf, stringbuf_len_alloc); } memcpy(stringbuf + stringbuf_len, str, len); diff --git a/innobase/pars/pars0opt.c b/innobase/pars/pars0opt.c index 4fb0c937f1e..7ba4af15243 100644 --- a/innobase/pars/pars0opt.c +++ b/innobase/pars/pars0opt.c @@ -1190,7 +1190,6 @@ opt_search_plan( #endif } -#ifdef UNIV_SQL_DEBUG /************************************************************************ Prints info of a query plan. */ @@ -1237,4 +1236,3 @@ opt_print_query_plan( (unsigned long) UT_LIST_GET_LEN(plan->end_conds)); } } -#endif /* UNIV_SQL_DEBUG */ diff --git a/innobase/que/que0que.c b/innobase/que/que0que.c index 363376a72da..22878dec27f 100644 --- a/innobase/que/que0que.c +++ b/innobase/que/que0que.c @@ -12,6 +12,7 @@ Created 5/27/1996 Heikki Tuuri #include "que0que.ic" #endif +#include "srv0que.h" #include "usr0sess.h" #include "trx0trx.h" #include "trx0roll.h" @@ -174,15 +175,19 @@ a single worker thread to execute it. This function should be used to end the wait state of a query thread waiting for a lock or a stored procedure completion. */ -que_thr_t* +void que_thr_end_wait( /*=============*/ - /* out: next query thread to run; - NULL if none */ - que_thr_t* thr) /* in: query thread in the + que_thr_t* thr, /* in: query thread in the QUE_THR_LOCK_WAIT, or QUE_THR_PROCEDURE_WAIT, or QUE_THR_SIG_REPLY_WAIT state */ + que_thr_t** next_thr) /* in/out: next query thread to run; + if the value which is passed in is + a pointer to a NULL pointer, then the + calling function can start running + a new query thread; if NULL is passed + as the parameter, it is ignored */ { ibool was_active; @@ -201,8 +206,18 @@ que_thr_end_wait( que_thr_move_to_run_state(thr); - return(was_active ? NULL : thr); -} + if (was_active) { + + return; + } + + if (next_thr && *next_thr == NULL) { + *next_thr = thr; + } else { + ut_a(0); + srv_que_task_enqueue_low(thr); + } +} /************************************************************************** Same as que_thr_end_wait, but no parameter next_thr available. */ @@ -239,6 +254,8 @@ que_thr_end_wait_no_next_thr( for the lock to be released: */ srv_release_mysql_thread_if_suspended(thr); + + /* srv_que_task_enqueue_low(thr); */ } /************************************************************************** @@ -339,6 +356,49 @@ que_fork_start_command( return(NULL); } +/************************************************************************** +After signal handling is finished, returns control to a query graph error +handling routine. (Currently, just returns the control to the root of the +graph so that the graph can communicate an error message to the client.) */ + +void +que_fork_error_handle( +/*==================*/ + trx_t* trx __attribute__((unused)), /* in: trx */ + que_t* fork) /* in: query graph which was run before signal + handling started, NULL not allowed */ +{ + que_thr_t* thr; + +#ifdef UNIV_SYNC_DEBUG + ut_ad(mutex_own(&kernel_mutex)); +#endif /* UNIV_SYNC_DEBUG */ + ut_ad(trx->sess->state == SESS_ERROR); + ut_ad(UT_LIST_GET_LEN(trx->reply_signals) == 0); + ut_ad(UT_LIST_GET_LEN(trx->wait_thrs) == 0); + + thr = UT_LIST_GET_FIRST(fork->thrs); + + while (thr != NULL) { + ut_ad(!thr->is_active); + ut_ad(thr->state != QUE_THR_SIG_REPLY_WAIT); + ut_ad(thr->state != QUE_THR_LOCK_WAIT); + + thr->run_node = thr; + thr->prev_node = thr->child; + thr->state = QUE_THR_COMPLETED; + + thr = UT_LIST_GET_NEXT(thrs, thr); + } + + thr = UT_LIST_GET_FIRST(fork->thrs); + + que_thr_move_to_run_state(thr); + + ut_a(0); + srv_que_task_enqueue_low(thr); +} + /******************************************************************** Tests if all the query threads in the same fork have a given state. */ UNIV_INLINE @@ -707,18 +767,22 @@ this function may only be called from inside que_run_threads or que_thr_check_if_switch! These restrictions exist to make the rollback code easier to maintain. */ static -que_thr_t* +void que_thr_dec_refer_count( /*====================*/ - /* out: next query thread to run */ - que_thr_t* thr) /* in: query thread */ + que_thr_t* thr, /* in: query thread */ + que_thr_t** next_thr) /* in/out: next query thread to run; + if the value which is passed in is + a pointer to a NULL pointer, then the + calling function can start running + a new query thread */ { que_fork_t* fork; trx_t* trx; sess_t* sess; ulint fork_type; - que_thr_t* next_thr = NULL; - + ibool stopped; + fork = thr->common.parent; trx = thr->graph->trx; sess = trx->sess; @@ -729,7 +793,9 @@ que_thr_dec_refer_count( if (thr->state == QUE_THR_RUNNING) { - if (!que_thr_stop(thr)) { + stopped = que_thr_stop(thr); + + if (!stopped) { /* The reason for the thr suspension or wait was already canceled before we came here: continue running the thread */ @@ -737,9 +803,16 @@ que_thr_dec_refer_count( /* fputs("!!!!!!!! Wait already ended: continue thr\n", stderr); */ + if (next_thr && *next_thr == NULL) { + *next_thr = thr; + } else { + ut_a(0); + srv_que_task_enqueue_low(thr); + } + mutex_exit(&kernel_mutex); - return(thr); + return; } } @@ -755,7 +828,7 @@ que_thr_dec_refer_count( mutex_exit(&kernel_mutex); - return(next_thr); + return; } fork_type = fork->fork_type; @@ -771,7 +844,7 @@ que_thr_dec_refer_count( ut_ad(UT_LIST_GET_LEN(trx->signals) > 0); ut_ad(trx->handling_signals == TRUE); - next_thr = trx_finish_rollback_off_kernel(fork, trx); + trx_finish_rollback_off_kernel(fork, trx, next_thr); } else if (fork_type == QUE_FORK_PURGE) { @@ -793,7 +866,7 @@ que_thr_dec_refer_count( zero, then we start processing a signal; from it we may get a new query thread to run */ - next_thr = trx_sig_start_handle(trx); + trx_sig_start_handle(trx, next_thr); } if (trx->handling_signals && UT_LIST_GET_LEN(trx->signals) == 0) { @@ -802,8 +875,6 @@ que_thr_dec_refer_count( } mutex_exit(&kernel_mutex); - - return(next_thr); } /************************************************************************** @@ -968,10 +1039,9 @@ que_thr_stop_for_mysql_no_error( trx->n_active_thrs--; } -#ifdef UNIV_DEBUG /************************************************************************** Prints info of an SQL query graph node. */ -static + void que_node_print_info( /*================*/ @@ -1028,7 +1098,6 @@ que_node_print_info( fprintf(stderr, "Node type %lu: %s, address %p\n", (ulong) type, str, node); } -#endif /* UNIV_DEBUG */ /************************************************************************** Performs an execution step on a query thread. */ @@ -1177,7 +1246,6 @@ loop: /*-------------------------*/ next_thr = que_thr_step(thr); /*-------------------------*/ - ut_a(next_thr == thr || next_thr == NULL); /* Test the effect on performance of adding extra mutex reservations */ @@ -1190,7 +1258,8 @@ loop: loop_count++; if (next_thr != thr) { - next_thr = que_thr_dec_refer_count(thr); + ut_a(next_thr == NULL); + que_thr_dec_refer_count(thr, &next_thr); if (next_thr == NULL) { diff --git a/innobase/rem/rem0cmp.c b/innobase/rem/rem0cmp.c index 254ebeec8c9..6e8f3d82ef3 100644 --- a/innobase/rem/rem0cmp.c +++ b/innobase/rem/rem0cmp.c @@ -33,12 +33,13 @@ At the present, the comparison functions return 0 in the case, where two records disagree only in the way that one has more fields than the other. */ +#ifdef UNIV_DEBUG /***************************************************************** Used in debug checking of cmp_dtuple_... . This function is used to compare a data tuple to a physical record. If dtuple has n fields then rec must have either m >= n fields, or it must differ from dtuple in some of the m fields rec has. */ - +static int cmp_debug_dtuple_rec_with_match( /*============================*/ @@ -54,6 +55,7 @@ cmp_debug_dtuple_rec_with_match( completely matched fields; when function returns, contains the value for current comparison */ +#endif /* UNIV_DEBUG */ /***************************************************************** This function is used to compare two data fields for which the data type is such that we must use MySQL code to compare them. The prototype here @@ -962,13 +964,14 @@ order_resolved: return(ret); } +#ifdef UNIV_DEBUG /***************************************************************** Used in debug checking of cmp_dtuple_... . This function is used to compare a data tuple to a physical record. If dtuple has n fields then rec must have either m >= n fields, or it must differ from dtuple in some of the m fields rec has. If encounters an externally stored field, returns 0. */ - +static int cmp_debug_dtuple_rec_with_match( /*============================*/ @@ -1064,3 +1067,4 @@ order_resolved: return(ret); } +#endif /* UNIV_DEBUG */ diff --git a/innobase/row/row0ins.c b/innobase/row/row0ins.c index aa4b313d15d..7f1852c70f2 100644 --- a/innobase/row/row0ins.c +++ b/innobase/row/row0ins.c @@ -80,9 +80,9 @@ ins_node_create( node->trx_id = ut_dulint_zero; node->entry_sys_heap = mem_heap_create(128); -#ifdef UNIV_DEBUG - node->magic_n = INS_NODE_MAGIC_N; -#endif /* UNIV_DEBUG */ + + node->magic_n = INS_NODE_MAGIC_N; + return(node); } @@ -194,7 +194,6 @@ ins_node_set_new_row( ins_node_t* node, /* in: insert node */ dtuple_t* row) /* in: new row (or first row) for the node */ { - ut_ad(node->magic_n == INS_NODE_MAGIC_N); node->state = INS_NODE_SET_IX_LOCK; node->index = NULL; node->entry = NULL; @@ -2014,7 +2013,6 @@ row_ins( ulint err; ut_ad(node && thr); - ut_ad(node->magic_n == INS_NODE_MAGIC_N); if (node->state == INS_NODE_ALLOC_ROW_ID) { @@ -2079,7 +2077,7 @@ row_ins_step( trx_start_if_not_started(trx); node = thr->run_node; - ut_ad(node->magic_n == INS_NODE_MAGIC_N); + ut_ad(que_node_get_type(node) == QUE_NODE_INSERT); parent = que_node_get_parent(node); diff --git a/innobase/row/row0mysql.c b/innobase/row/row0mysql.c index 974dfa3e1c8..c4408de2a85 100644 --- a/innobase/row/row0mysql.c +++ b/innobase/row/row0mysql.c @@ -1414,7 +1414,8 @@ row_create_table_for_mysql( tab_node_t* node; mem_heap_t* heap; que_thr_t* thr; - ulint namelen; + const char* table_name; + ulint table_name_len; ulint err; ut_ad(trx->mysql_thread_id == os_thread_get_curr_id()); @@ -1466,10 +1467,17 @@ row_create_table_for_mysql( return(row_mysql_recover_tmp_table(table, trx)); } - namelen = strlen(table->name) + 1; + /* The table name is prefixed with the database name and a '/'. + Certain table names starting with 'innodb_' have their special + meaning regardless of the database name. Thus, we need to + ignore the database name prefix in the comparisons. */ + table_name = strchr(table->name, '/'); + ut_a(table_name); + table_name++; + table_name_len = strlen(table_name) + 1; - if (namelen == sizeof S_innodb_monitor - && !memcmp(table->name, S_innodb_monitor, + if (table_name_len == sizeof S_innodb_monitor + && !memcmp(table_name, S_innodb_monitor, sizeof S_innodb_monitor)) { /* Table equals "innodb_monitor": @@ -1481,27 +1489,27 @@ row_create_table_for_mysql( of InnoDB monitor prints */ os_event_set(srv_lock_timeout_thread_event); - } else if (namelen == sizeof S_innodb_lock_monitor - && !memcmp(table->name, S_innodb_lock_monitor, + } else if (table_name_len == sizeof S_innodb_lock_monitor + && !memcmp(table_name, S_innodb_lock_monitor, sizeof S_innodb_lock_monitor)) { srv_print_innodb_monitor = TRUE; srv_print_innodb_lock_monitor = TRUE; os_event_set(srv_lock_timeout_thread_event); - } else if (namelen == sizeof S_innodb_tablespace_monitor - && !memcmp(table->name, S_innodb_tablespace_monitor, + } else if (table_name_len == sizeof S_innodb_tablespace_monitor + && !memcmp(table_name, S_innodb_tablespace_monitor, sizeof S_innodb_tablespace_monitor)) { srv_print_innodb_tablespace_monitor = TRUE; os_event_set(srv_lock_timeout_thread_event); - } else if (namelen == sizeof S_innodb_table_monitor - && !memcmp(table->name, S_innodb_table_monitor, + } else if (table_name_len == sizeof S_innodb_table_monitor + && !memcmp(table_name, S_innodb_table_monitor, sizeof S_innodb_table_monitor)) { srv_print_innodb_table_monitor = TRUE; os_event_set(srv_lock_timeout_thread_event); - } else if (namelen == sizeof S_innodb_mem_validate - && !memcmp(table->name, S_innodb_mem_validate, + } else if (table_name_len == sizeof S_innodb_mem_validate + && !memcmp(table_name, S_innodb_mem_validate, sizeof S_innodb_mem_validate)) { /* We define here a debugging feature intended for developers */ @@ -2204,6 +2212,7 @@ row_drop_table_for_mysql( que_thr_t* thr; que_t* graph; ulint err; + const char* table_name; ulint namelen; ibool success; ibool locked_dictionary = FALSE; @@ -2293,10 +2302,17 @@ row_drop_table_for_mysql( trx_start_if_not_started(trx); - namelen = strlen(name) + 1; + /* The table name is prefixed with the database name and a '/'. + Certain table names starting with 'innodb_' have their special + meaning regardless of the database name. Thus, we need to + ignore the database name prefix in the comparisons. */ + table_name = strchr(name, '/'); + ut_a(table_name); + table_name++; + namelen = strlen(table_name) + 1; if (namelen == sizeof S_innodb_monitor - && !memcmp(name, S_innodb_monitor, + && !memcmp(table_name, S_innodb_monitor, sizeof S_innodb_monitor)) { /* Table name equals "innodb_monitor": @@ -2305,17 +2321,17 @@ row_drop_table_for_mysql( srv_print_innodb_monitor = FALSE; srv_print_innodb_lock_monitor = FALSE; } else if (namelen == sizeof S_innodb_lock_monitor - && !memcmp(name, S_innodb_lock_monitor, + && !memcmp(table_name, S_innodb_lock_monitor, sizeof S_innodb_lock_monitor)) { srv_print_innodb_monitor = FALSE; srv_print_innodb_lock_monitor = FALSE; } else if (namelen == sizeof S_innodb_tablespace_monitor - && !memcmp(name, S_innodb_tablespace_monitor, + && !memcmp(table_name, S_innodb_tablespace_monitor, sizeof S_innodb_tablespace_monitor)) { srv_print_innodb_tablespace_monitor = FALSE; } else if (namelen == sizeof S_innodb_table_monitor - && !memcmp(name, S_innodb_table_monitor, + && !memcmp(table_name, S_innodb_table_monitor, sizeof S_innodb_table_monitor)) { srv_print_innodb_table_monitor = FALSE; diff --git a/innobase/row/row0upd.c b/innobase/row/row0upd.c index 724e7bf91e7..d35ae0a3e38 100644 --- a/innobase/row/row0upd.c +++ b/innobase/row/row0upd.c @@ -287,9 +287,7 @@ upd_node_create( node->select = NULL; node->heap = mem_heap_create(128); -#ifdef UNIV_DEBUG - node->magic_n = UPD_NODE_MAGIC_N; -#endif /* UNIV_DEBUG */ + node->magic_n = UPD_NODE_MAGIC_N; node->cmpl_info = 0; @@ -1806,7 +1804,6 @@ row_upd_step( trx_start_if_not_started(trx); node = thr->run_node; - ut_ad(node->magic_n == UPD_NODE_MAGIC_N); sel_node = node->select; @@ -1926,7 +1923,6 @@ row_upd_in_place_in_select( node = que_node_get_parent(sel_node); - ut_ad(node->magic_n == UPD_NODE_MAGIC_N); ut_ad(que_node_get_type(node) == QUE_NODE_UPDATE); pcur = node->pcur; diff --git a/innobase/srv/Makefile.am b/innobase/srv/Makefile.am index 37fd73a4bf0..752683b82b8 100644 --- a/innobase/srv/Makefile.am +++ b/innobase/srv/Makefile.am @@ -19,6 +19,6 @@ include ../include/Makefile.i noinst_LIBRARIES = libsrv.a -libsrv_a_SOURCES = srv0srv.c srv0start.c +libsrv_a_SOURCES = srv0srv.c srv0que.c srv0start.c EXTRA_PROGRAMS = diff --git a/innobase/srv/srv0que.c b/innobase/srv/srv0que.c new file mode 100644 index 00000000000..9dc9ae453d9 --- /dev/null +++ b/innobase/srv/srv0que.c @@ -0,0 +1,113 @@ +/****************************************************** +Server query execution + +(c) 1996 Innobase Oy + +Created 6/5/1996 Heikki Tuuri +*******************************************************/ + +#include "srv0que.h" + +#include "srv0srv.h" +#include "sync0sync.h" +#include "os0thread.h" +#include "usr0sess.h" +#include "que0que.h" + +/************************************************************************** +Checks if there is work to do in the server task queue. If there is, the +thread starts processing a task. Before leaving, it again checks the task +queue and picks a new task if any exists. This is called by a SRV_WORKER +thread. */ + +void +srv_que_task_queue_check(void) +/*==========================*/ +{ + que_thr_t* thr; + + for (;;) { + mutex_enter(&kernel_mutex); + + thr = UT_LIST_GET_FIRST(srv_sys->tasks); + + if (thr == NULL) { + mutex_exit(&kernel_mutex); + + return; + } + + UT_LIST_REMOVE(queue, srv_sys->tasks, thr); + + mutex_exit(&kernel_mutex); + + que_run_threads(thr); + } +} + +/************************************************************************** +Performs round-robin on the server tasks. This is called by a SRV_WORKER +thread every second or so. */ + +que_thr_t* +srv_que_round_robin( +/*================*/ + /* out: the new (may be == thr) query thread + to run */ + que_thr_t* thr) /* in: query thread */ +{ + que_thr_t* new_thr; + + ut_ad(thr); + ut_ad(thr->state == QUE_THR_RUNNING); + + mutex_enter(&kernel_mutex); + + UT_LIST_ADD_LAST(queue, srv_sys->tasks, thr); + + new_thr = UT_LIST_GET_FIRST(srv_sys->tasks); + + mutex_exit(&kernel_mutex); + + return(new_thr); +} + +/************************************************************************** +Enqueues a task to server task queue and releases a worker thread, if there +is a suspended one. */ + +void +srv_que_task_enqueue_low( +/*=====================*/ + que_thr_t* thr) /* in: query thread */ +{ + ut_ad(thr); + +#ifdef UNIV_SYNC_DEBUG + ut_ad(mutex_own(&kernel_mutex)); +#endif /* UNIV_SYNC_DEBUG */ + + UT_LIST_ADD_LAST(queue, srv_sys->tasks, thr); + + srv_release_threads(SRV_WORKER, 1); +} + +/************************************************************************** +Enqueues a task to server task queue and releases a worker thread, if there +is a suspended one. */ + +void +srv_que_task_enqueue( +/*=================*/ + que_thr_t* thr) /* in: query thread */ +{ + ut_ad(thr); + + ut_a(0); /* Under MySQL this is never called */ + + mutex_enter(&kernel_mutex); + + srv_que_task_enqueue_low(thr); + + mutex_exit(&kernel_mutex); +} diff --git a/innobase/srv/srv0srv.c b/innobase/srv/srv0srv.c index 3817501ae70..fc46c95a8a6 100644 --- a/innobase/srv/srv0srv.c +++ b/innobase/srv/srv0srv.c @@ -34,6 +34,7 @@ Created 10/8/1995 Heikki Tuuri #include "sync0sync.h" #include "thr0loc.h" #include "que0que.h" +#include "srv0que.h" #include "log0recv.h" #include "pars0pars.h" #include "usr0sess.h" diff --git a/innobase/srv/srv0start.c b/innobase/srv/srv0start.c index 3bf39e6422b..dd38eddb20b 100644 --- a/innobase/srv/srv0start.c +++ b/innobase/srv/srv0start.c @@ -1478,9 +1478,7 @@ NetWare. */ os_thread_create(&srv_master_thread, NULL, thread_ids + 1 + SRV_MAX_N_IO_THREADS); -#ifdef UNIV_DEBUG /* buf_debug_prints = TRUE; */ -#endif /* UNIV_DEBUG */ sum_of_data_file_sizes = 0; diff --git a/innobase/sync/sync0rw.c b/innobase/sync/sync0rw.c index 2d3b48b1d89..77757685208 100644 --- a/innobase/sync/sync0rw.c +++ b/innobase/sync/sync0rw.c @@ -171,7 +171,6 @@ rw_lock_free( mutex_exit(&rw_lock_list_mutex); } -#ifdef UNIV_DEBUG /********************************************************************** Checks that the rw-lock has been initialized and that there are no simultaneous shared and exclusive locks. */ @@ -199,7 +198,6 @@ rw_lock_validate( return(TRUE); } -#endif /* UNIV_DEBUG */ /********************************************************************** Lock an rw-lock in shared mode for the current thread. If the rw-lock is diff --git a/innobase/sync/sync0sync.c b/innobase/sync/sync0sync.c index 89bfb5dd02e..86306e49cac 100644 --- a/innobase/sync/sync0sync.c +++ b/innobase/sync/sync0sync.c @@ -314,7 +314,6 @@ mutex_enter_nowait( return(1); } -#ifdef UNIV_DEBUG /********************************************************************** Checks that the mutex has been initialized. */ @@ -328,7 +327,6 @@ mutex_validate( return(TRUE); } -#endif /* UNIV_DEBUG */ /********************************************************************** Sets the waiters field in a mutex. */ @@ -1077,12 +1075,8 @@ sync_thread_add_level( } else if (level == SYNC_DICT_HEADER) { ut_a(sync_thread_levels_g(array, SYNC_DICT_HEADER)); } else if (level == SYNC_DICT) { -#ifdef UNIV_DEBUG - ut_a(buf_debug_prints || - sync_thread_levels_g(array, SYNC_DICT)); -#else /* UNIV_DEBUG */ - ut_a(sync_thread_levels_g(array, SYNC_DICT)); -#endif /* UNIV_DEBUG */ + ut_a(buf_debug_prints + || sync_thread_levels_g(array, SYNC_DICT)); } else { ut_error; } diff --git a/innobase/thr/thr0loc.c b/innobase/thr/thr0loc.c index a17d09fcca6..033bb22807f 100644 --- a/innobase/thr/thr0loc.c +++ b/innobase/thr/thr0loc.c @@ -46,12 +46,11 @@ struct thr_local_struct{ ibool in_ibuf;/* TRUE if the the thread is doing an ibuf operation */ hash_node_t hash; /* hash chain node */ -#ifdef UNIV_DEBUG ulint magic_n; -#define THR_LOCAL_MAGIC_N 1231234 -#endif /* UNIV_DEBUG */ }; +#define THR_LOCAL_MAGIC_N 1231234 + /*********************************************************************** Returns the local storage struct for a thread. */ static @@ -170,9 +169,8 @@ thr_local_create(void) local->id = os_thread_get_curr_id(); local->handle = os_thread_get_curr(); -#ifdef UNIV_DEBUG local->magic_n = THR_LOCAL_MAGIC_N; -#endif /* UNIV_DEBUG */ + local->in_ibuf = FALSE; mutex_enter(&thr_local_mutex); @@ -211,7 +209,7 @@ thr_local_free( mutex_exit(&thr_local_mutex); - ut_ad(local->magic_n == THR_LOCAL_MAGIC_N); + ut_a(local->magic_n == THR_LOCAL_MAGIC_N); mem_free(local); } diff --git a/innobase/trx/trx0purge.c b/innobase/trx/trx0purge.c index 002775eaac1..3d5f0d3f03a 100644 --- a/innobase/trx/trx0purge.c +++ b/innobase/trx/trx0purge.c @@ -23,6 +23,7 @@ Created 3/26/1996 Heikki Tuuri #include "row0purge.h" #include "row0upd.h" #include "trx0rec.h" +#include "srv0que.h" #include "os0thread.h" /* The global data structure coordinating a purge */ @@ -1059,6 +1060,8 @@ trx_purge(void) mutex_exit(&kernel_mutex); +/* srv_que_task_enqueue(thr2); */ + if (srv_print_thread_releases) { fputs("Starting purge\n", stderr); diff --git a/innobase/trx/trx0roll.c b/innobase/trx/trx0roll.c index 8b8a079bb7f..e65755a0f73 100644 --- a/innobase/trx/trx0roll.c +++ b/innobase/trx/trx0roll.c @@ -20,6 +20,7 @@ Created 3/26/1996 Heikki Tuuri #include "trx0rec.h" #include "que0que.h" #include "usr0sess.h" +#include "srv0que.h" #include "srv0start.h" #include "row0undo.h" #include "row0mysql.h" @@ -931,15 +932,21 @@ trx_undo_rec_release( /************************************************************************* Starts a rollback operation. */ -que_thr_t* +void trx_rollback( /*=========*/ - /* out: next query thread to run */ trx_t* trx, /* in: transaction */ - trx_sig_t* sig) /* in: signal starting the rollback */ + trx_sig_t* sig, /* in: signal starting the rollback */ + que_thr_t** next_thr)/* in/out: next query thread to run; + if the value which is passed in is + a pointer to a NULL pointer, then the + calling function can start running + a new query thread; if the passed value is + NULL, the parameter is ignored */ { que_t* roll_graph; que_thr_t* thr; +/* que_thr_t* thr2; */ #ifdef UNIV_SYNC_DEBUG ut_ad(mutex_own(&kernel_mutex)); @@ -981,7 +988,18 @@ trx_rollback( thr = que_fork_start_command(roll_graph); ut_ad(thr); - return(thr); + +/* thr2 = que_fork_start_command(roll_graph); + + ut_ad(thr2); */ + + if (next_thr && (*next_thr == NULL)) { + *next_thr = thr; +/* srv_que_task_enqueue_low(thr2); */ + } else { + srv_que_task_enqueue_low(thr); +/* srv_que_task_enqueue_low(thr2); */ + } } /******************************************************************** @@ -1053,14 +1071,17 @@ trx_finish_error_processing( /************************************************************************* Finishes a partial rollback operation. */ static -que_thr_t* +void trx_finish_partial_rollback_off_kernel( /*===================================*/ - /* out: next query thread to run */ - trx_t* trx) /* in: transaction */ + trx_t* trx, /* in: transaction */ + que_thr_t** next_thr)/* in/out: next query thread to run; + if the value which is passed in is a pointer + to a NULL pointer, then the calling function + can start running a new query thread; if this + parameter is NULL, it is ignored */ { trx_sig_t* sig; - que_thr_t* next_thr; #ifdef UNIV_SYNC_DEBUG ut_ad(mutex_own(&kernel_mutex)); @@ -1071,26 +1092,29 @@ trx_finish_partial_rollback_off_kernel( /* Remove the signal from the signal queue and send reply message to it */ - next_thr = trx_sig_reply(sig); + trx_sig_reply(sig, next_thr); trx_sig_remove(trx, sig); trx->que_state = TRX_QUE_RUNNING; - return(next_thr); } /******************************************************************** Finishes a transaction rollback. */ -que_thr_t* +void trx_finish_rollback_off_kernel( /*===========================*/ - /* out: next query thread to run */ que_t* graph, /* in: undo graph which can now be freed */ - trx_t* trx) /* in: transaction */ + trx_t* trx, /* in: transaction */ + que_thr_t** next_thr)/* in/out: next query thread to run; + if the value which is passed in is + a pointer to a NULL pointer, then the + calling function can start running + a new query thread; if this parameter is + NULL, it is ignored */ { trx_sig_t* sig; trx_sig_t* next_sig; - que_thr_t* next_thr; #ifdef UNIV_SYNC_DEBUG ut_ad(mutex_own(&kernel_mutex)); @@ -1105,21 +1129,21 @@ trx_finish_rollback_off_kernel( if (sig->type == TRX_SIG_ROLLBACK_TO_SAVEPT) { - return(trx_finish_partial_rollback_off_kernel(trx)); + trx_finish_partial_rollback_off_kernel(trx, next_thr); + + return; } else if (sig->type == TRX_SIG_ERROR_OCCURRED) { trx_finish_error_processing(trx); - return(NULL); + return; } -#ifdef UNIV_DEBUG if (lock_print_waits) { fprintf(stderr, "Trx %lu rollback finished\n", (ulong) ut_dulint_get_low(trx->id)); } -#endif /* UNIV_DEBUG */ trx_commit_off_kernel(trx); @@ -1127,23 +1151,19 @@ trx_finish_rollback_off_kernel( send reply messages to them */ trx->que_state = TRX_QUE_RUNNING; - - next_thr = NULL; + while (sig != NULL) { next_sig = UT_LIST_GET_NEXT(signals, sig); if (sig->type == TRX_SIG_TOTAL_ROLLBACK) { - ut_a(next_thr == NULL); - next_thr = trx_sig_reply(sig); + trx_sig_reply(sig, next_thr); trx_sig_remove(trx, sig); } sig = next_sig; } - - return(next_thr); } /************************************************************************* @@ -1176,6 +1196,7 @@ trx_rollback_step( que_thr_t* thr) /* in: query thread */ { roll_node_t* node; + ibool success; ulint sig_no; trx_savept_t* savept; @@ -1202,13 +1223,19 @@ trx_rollback_step( /* Send a rollback signal to the transaction */ - trx_sig_send(thr_get_trx(thr), sig_no, TRX_SIG_SELF, - thr, savept); + success = trx_sig_send(thr_get_trx(thr), + sig_no, TRX_SIG_SELF, + thr, savept, NULL); thr->state = QUE_THR_SIG_REPLY_WAIT; mutex_exit(&kernel_mutex); + if (!success) { + /* Error in delivering the rollback signal */ + que_thr_handle_error(thr, DB_ERROR, NULL, 0); + } + return(NULL); } diff --git a/innobase/trx/trx0sys.c b/innobase/trx/trx0sys.c index b377bdb9809..5c8ae0952ef 100644 --- a/innobase/trx/trx0sys.c +++ b/innobase/trx/trx0sys.c @@ -619,7 +619,6 @@ trx_sys_update_mysql_binlog_offset( MLOG_4BYTES, mtr); } -#ifdef UNIV_HOTBACKUP /********************************************************************* Prints to stderr the MySQL binlog info in the system header if the magic number shows it valid. */ @@ -647,7 +646,6 @@ trx_sys_print_mysql_binlog_offset_from_page( sys_header + TRX_SYS_MYSQL_LOG_INFO + TRX_SYS_MYSQL_LOG_NAME); } } -#endif /* UNIV_HOTBACKUP */ /********************************************************************* Prints to stderr the MySQL binlog offset info in the trx system header if diff --git a/innobase/trx/trx0trx.c b/innobase/trx/trx0trx.c index afef08dc100..a9aae48dae6 100644 --- a/innobase/trx/trx0trx.c +++ b/innobase/trx/trx0trx.c @@ -897,15 +897,18 @@ trx_assign_read_view( /******************************************************************** Commits a transaction. NOTE that the kernel mutex is temporarily released. */ static -que_thr_t* +void trx_handle_commit_sig_off_kernel( /*=============================*/ - /* out: next query thread to run */ - trx_t* trx) /* in: transaction */ + trx_t* trx, /* in: transaction */ + que_thr_t** next_thr) /* in/out: next query thread to run; + if the value which is passed in is + a pointer to a NULL pointer, then the + calling function can start running + a new query thread */ { trx_sig_t* sig; trx_sig_t* next_sig; - que_thr_t* next_thr = NULL; #ifdef UNIV_SYNC_DEBUG ut_ad(mutex_own(&kernel_mutex)); @@ -927,8 +930,7 @@ trx_handle_commit_sig_off_kernel( if (sig->type == TRX_SIG_COMMIT) { - ut_a(next_thr == NULL); - next_thr = trx_sig_reply(sig); + trx_sig_reply(sig, next_thr); trx_sig_remove(trx, sig); } @@ -936,8 +938,6 @@ trx_handle_commit_sig_off_kernel( } trx->que_state = TRX_QUE_RUNNING; - - return(next_thr); } /*************************************************************** @@ -999,6 +999,39 @@ trx_lock_wait_to_suspended( trx->que_state = TRX_QUE_RUNNING; } +/*************************************************************** +Moves the query threads in the sig reply wait list of trx to the SUSPENDED +state. */ +static +void +trx_sig_reply_wait_to_suspended( +/*============================*/ + trx_t* trx) /* in: transaction */ +{ + trx_sig_t* sig; + que_thr_t* thr; + +#ifdef UNIV_SYNC_DEBUG + ut_ad(mutex_own(&kernel_mutex)); +#endif /* UNIV_SYNC_DEBUG */ + + sig = UT_LIST_GET_FIRST(trx->reply_signals); + + while (sig != NULL) { + thr = sig->receiver; + + ut_ad(thr->state == QUE_THR_SIG_REPLY_WAIT); + + thr->state = QUE_THR_SUSPENDED; + + sig->receiver = NULL; + + UT_LIST_REMOVE(reply_signals, trx->reply_signals, sig); + + sig = UT_LIST_GET_FIRST(trx->reply_signals); + } +} + /********************************************************************* Checks the compatibility of a new signal with the other signals in the queue. */ @@ -1078,10 +1111,11 @@ trx_sig_is_compatible( /******************************************************************** Sends a signal to a trx object. */ -que_thr_t* +ibool trx_sig_send( /*=========*/ - /* out: next query thread to run */ + /* out: TRUE if the signal was + successfully delivered */ trx_t* trx, /* in: trx handle */ ulint type, /* in: signal type */ ulint sender, /* in: TRX_SIG_SELF or @@ -1089,8 +1123,14 @@ trx_sig_send( que_thr_t* receiver_thr, /* in: query thread which wants the reply, or NULL; if type is TRX_SIG_END_WAIT, this must be NULL */ - trx_savept_t* savept) /* in: possible rollback savepoint, or + trx_savept_t* savept, /* in: possible rollback savepoint, or NULL */ + que_thr_t** next_thr) /* in/out: next query thread to run; + if the value which is passed in is + a pointer to a NULL pointer, then the + calling function can start running + a new query thread; if the parameter + is NULL, it is ignored */ { trx_sig_t* sig; trx_t* receiver_trx; @@ -1100,7 +1140,14 @@ trx_sig_send( ut_ad(mutex_own(&kernel_mutex)); #endif /* UNIV_SYNC_DEBUG */ - ut_a(trx_sig_is_compatible(trx, type, sender)); + if (!trx_sig_is_compatible(trx, type, sender)) { + /* The signal is not compatible with the other signals in + the queue: do nothing */ + + ut_error; + + return(FALSE); + } /* Queue the signal object */ @@ -1134,6 +1181,11 @@ trx_sig_send( sig); } + if (trx->sess->state == SESS_ERROR) { + + trx_sig_reply_wait_to_suspended(trx); + } + if ((sender != TRX_SIG_SELF) || (type == TRX_SIG_BREAK_EXECUTION)) { /* The following call will add a TRX_SIG_ERROR_OCCURRED @@ -1148,10 +1200,10 @@ trx_sig_send( if (UT_LIST_GET_FIRST(trx->signals) == sig) { - return(trx_sig_start_handle(trx)); + trx_sig_start_handle(trx, next_thr); } - return(NULL); + return(TRUE); } /******************************************************************** @@ -1173,18 +1225,27 @@ trx_end_signal_handling( trx->handling_signals = FALSE; trx->graph = trx->graph_before_signal_handling; + + if (trx->graph && (trx->sess->state == SESS_ERROR)) { + + que_fork_error_handle(trx, trx->graph); + } } /******************************************************************** Starts handling of a trx signal. */ -que_thr_t* +void trx_sig_start_handle( /*=================*/ - /* out: next query thread to run, or NULL */ - trx_t* trx) /* in: trx handle */ + trx_t* trx, /* in: trx handle */ + que_thr_t** next_thr) /* in/out: next query thread to run; + if the value which is passed in is + a pointer to a NULL pointer, then the + calling function can start running + a new query thread; if the parameter + is NULL, it is ignored */ { - que_thr_t* next_thr = NULL; trx_sig_t* sig; ulint type; loop: @@ -1200,7 +1261,7 @@ loop: trx_end_signal_handling(trx); - return(next_thr); + return; } if (trx->conc_state == TRX_NOT_STARTED) { @@ -1216,13 +1277,23 @@ loop: trx_lock_wait_to_suspended(trx); } + /* If the session is in the error state and this trx has threads + waiting for reply from signals, moves these threads to the suspended + state, canceling wait reservations; note that if the transaction has + sent a commit or rollback signal to itself, and its session is not in + the error state, then nothing is done here. */ + + if (trx->sess->state == SESS_ERROR) { + trx_sig_reply_wait_to_suspended(trx); + } + /* If there are no running query threads, we can start processing of a signal, otherwise we have to wait until all query threads of this transaction are aware of the arrival of the signal. */ if (trx->n_active_thrs > 0) { - return(NULL); + return; } if (trx->handling_signals == FALSE) { @@ -1236,19 +1307,30 @@ loop: if (type == TRX_SIG_COMMIT) { - next_thr = trx_handle_commit_sig_off_kernel(trx); + trx_handle_commit_sig_off_kernel(trx, next_thr); } else if ((type == TRX_SIG_TOTAL_ROLLBACK) - || (type == TRX_SIG_ROLLBACK_TO_SAVEPT) - || (type == TRX_SIG_ERROR_OCCURRED)) { + || (type == TRX_SIG_ROLLBACK_TO_SAVEPT)) { + + trx_rollback(trx, sig, next_thr); + + /* No further signals can be handled until the rollback + completes, therefore we return */ + + return; + + } else if (type == TRX_SIG_ERROR_OCCURRED) { + + trx_rollback(trx, sig, next_thr); + /* No further signals can be handled until the rollback completes, therefore we return */ - return(trx_rollback(trx, sig)); + return; } else if (type == TRX_SIG_BREAK_EXECUTION) { - next_thr = trx_sig_reply(sig); + trx_sig_reply(sig, next_thr); trx_sig_remove(trx, sig); } else { ut_error; @@ -1261,14 +1343,17 @@ loop: Send the reply message when a signal in the queue of the trx has been handled. */ -que_thr_t* +void trx_sig_reply( /*==========*/ - /* out: next query thread to run */ - trx_sig_t* sig) /* in: signal */ + trx_sig_t* sig, /* in: signal */ + que_thr_t** next_thr) /* in/out: next query thread to run; + if the value which is passed in is + a pointer to a NULL pointer, then the + calling function can start running + a new query thread */ { - trx_t* receiver_trx; - que_thr_t* next_thr = NULL; + trx_t* receiver_trx; ut_ad(sig); #ifdef UNIV_SYNC_DEBUG @@ -1282,13 +1367,13 @@ trx_sig_reply( UT_LIST_REMOVE(reply_signals, receiver_trx->reply_signals, sig); - next_thr = que_thr_end_wait(sig->receiver); + ut_ad(receiver_trx->sess->state != SESS_ERROR); + + que_thr_end_wait(sig->receiver, next_thr); sig->receiver = NULL; } - - return(next_thr); } /******************************************************************** @@ -1344,6 +1429,7 @@ trx_commit_step( { commit_node_t* node; que_thr_t* next_thr; + ibool success; node = thr->run_node; @@ -1358,15 +1444,22 @@ trx_commit_step( node->state = COMMIT_NODE_WAIT; + next_thr = NULL; + thr->state = QUE_THR_SIG_REPLY_WAIT; /* Send the commit signal to the transaction */ - next_thr = trx_sig_send(thr_get_trx(thr), TRX_SIG_COMMIT, - TRX_SIG_SELF, thr, NULL); - + success = trx_sig_send(thr_get_trx(thr), TRX_SIG_COMMIT, + TRX_SIG_SELF, thr, NULL, &next_thr); + mutex_exit(&kernel_mutex); + if (!success) { + /* Error in delivering the commit signal */ + que_thr_handle_error(thr, DB_ERROR, NULL, 0); + } + return(next_thr); } diff --git a/innobase/usr/usr0sess.c b/innobase/usr/usr0sess.c index cc016f2b823..359c1552421 100644 --- a/innobase/usr/usr0sess.c +++ b/innobase/usr/usr0sess.c @@ -37,6 +37,8 @@ sess_open(void) #endif /* UNIV_SYNC_DEBUG */ sess = mem_alloc(sizeof(sess_t)); + sess->state = SESS_ACTIVE; + sess->trx = trx_create(sess); UT_LIST_INIT(sess->graphs); diff --git a/innobase/ut/ut0mem.c b/innobase/ut/ut0mem.c index 47b612d757e..9a591df9f77 100644 --- a/innobase/ut/ut0mem.c +++ b/innobase/ut/ut0mem.c @@ -203,6 +203,81 @@ ut_free( } /************************************************************************** +Implements realloc. This is needed by /pars/lexyy.c. Otherwise, you should not +use this function because the allocation functions in mem0mem.h are the +recommended ones in InnoDB. + +man realloc in Linux, 2004: + + realloc() changes the size of the memory block pointed to + by ptr to size bytes. The contents will be unchanged to + the minimum of the old and new sizes; newly allocated mem + ory will be uninitialized. If ptr is NULL, the call is + equivalent to malloc(size); if size is equal to zero, the + call is equivalent to free(ptr). Unless ptr is NULL, it + must have been returned by an earlier call to malloc(), + calloc() or realloc(). + +RETURN VALUE + realloc() returns a pointer to the newly allocated memory, + which is suitably aligned for any kind of variable and may + be different from ptr, or NULL if the request fails. If + size was equal to 0, either NULL or a pointer suitable to + be passed to free() is returned. If realloc() fails the + original block is left untouched - it is not freed or + moved. */ + +void* +ut_realloc( +/*=======*/ + /* out, own: pointer to new mem block or NULL */ + void* ptr, /* in: pointer to old block or NULL */ + ulint size) /* in: desired size */ +{ + ut_mem_block_t* block; + ulint old_size; + ulint min_size; + void* new_ptr; + + if (ptr == NULL) { + + return(ut_malloc(size)); + } + + if (size == 0) { + ut_free(ptr); + + return(NULL); + } + + block = (ut_mem_block_t*)((byte*)ptr - sizeof(ut_mem_block_t)); + + ut_a(block->magic_n == UT_MEM_MAGIC_N); + + old_size = block->size - sizeof(ut_mem_block_t); + + if (size < old_size) { + min_size = size; + } else { + min_size = old_size; + } + + new_ptr = ut_malloc(size); + + if (new_ptr == NULL) { + + return(NULL); + } + + /* Copy the old data from ptr */ + ut_memcpy(new_ptr, ptr, min_size); + + ut_free(ptr); + + return(new_ptr); +} + +/************************************************************************** Frees in shutdown all allocated memory not freed yet. */ void diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c index a6181cd0269..d29a7deb69e 100644 --- a/libmysql/libmysql.c +++ b/libmysql/libmysql.c @@ -84,7 +84,6 @@ my_bool net_flush(NET *net); #define MAX_LONG_DATA_LENGTH 8192 #define unsigned_field(A) ((A)->flags & UNSIGNED_FLAG) -static void stmt_update_metadata(MYSQL_STMT *stmt, MYSQL_ROWS *data); static void append_wild(char *to,char *end,const char *wild); sig_handler pipe_sig_handler(int sig); @@ -1285,10 +1284,12 @@ mysql_drop_db(MYSQL *mysql, const char *db) int STDCALL -mysql_shutdown(MYSQL *mysql) +mysql_shutdown(MYSQL *mysql, enum enum_shutdown_level shutdown_level) { + uchar level[1]; DBUG_ENTER("mysql_shutdown"); - DBUG_RETURN(simple_command(mysql,COM_SHUTDOWN,0,0,0)); + level[0]= (uchar) shutdown_level; + DBUG_RETURN(simple_command(mysql, COM_SHUTDOWN, (char *)level, 1, 0)); } @@ -1665,6 +1666,11 @@ static int stmt_read_row_unbuffered(MYSQL_STMT *stmt, unsigned char **row); static int stmt_read_row_buffered(MYSQL_STMT *stmt, unsigned char **row); static int stmt_read_row_no_data(MYSQL_STMT *stmt, unsigned char **row); +/* + This function is used in mysql_stmt_store_result if + STMT_ATTR_UPDATE_MAX_LENGTH attribute is set. +*/ +static void stmt_update_metadata(MYSQL_STMT *stmt, MYSQL_ROWS *data); /* Maximum sizes of MYSQL_TYPE_DATE, MYSQL_TYPE_TIME, MYSQL_TYPE_DATETIME @@ -1945,7 +1951,7 @@ mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query, ulong length) mysql_stmt_free_result(stmt); /* - These members must be reset for API to + These members must be reset for API to function in case of error or misuse. */ stmt->bind_param_done= stmt->bind_result_done= FALSE; @@ -1984,14 +1990,14 @@ mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query, ulong length) } /* - alloc_root will return valid address even in case param_count + alloc_root will return valid address even in case param_count and field_count are zero. Thus we should never rely on stmt->bind or stmt->params when checking for existence of placeholders or result set. */ if (!(stmt->params= (MYSQL_BIND *) alloc_root(&stmt->mem_root, sizeof(MYSQL_BIND)* - (stmt->param_count + + (stmt->param_count + stmt->field_count)))) { set_stmt_error(stmt, CR_OUT_OF_MEMORY, unknown_sqlstate); @@ -2015,22 +2021,22 @@ static unsigned int alloc_stmt_fields(MYSQL_STMT *stmt) MYSQL_FIELD *fields, *field, *end; MEM_ROOT *alloc= &stmt->mem_root; MYSQL *mysql= stmt->mysql->last_used_con; - + stmt->field_count= mysql->field_count; - + /* - Get the field information for non-select statements + Get the field information for non-select statements like SHOW and DESCRIBE commands */ - if (!(stmt->fields= (MYSQL_FIELD *) alloc_root(alloc, + if (!(stmt->fields= (MYSQL_FIELD *) alloc_root(alloc, sizeof(MYSQL_FIELD) * - stmt->field_count)) || - !(stmt->bind= (MYSQL_BIND *) alloc_root(alloc, + stmt->field_count)) || + !(stmt->bind= (MYSQL_BIND *) alloc_root(alloc, sizeof(MYSQL_BIND) * stmt->field_count))) return 0; - - for (fields= mysql->fields, end= fields+stmt->field_count, + + for (fields= mysql->fields, end= fields+stmt->field_count, field= stmt->fields; field && fields < end; fields++, field++) { @@ -2107,7 +2113,7 @@ mysql_stmt_result_metadata(MYSQL_STMT *stmt) { MYSQL_RES *result; DBUG_ENTER("mysql_stmt_result_metadata"); - + /* stmt->fields is only defined if stmt->field_count is not null; stmt->field_count is initialized in prepare. @@ -2130,41 +2136,65 @@ mysql_stmt_result_metadata(MYSQL_STMT *stmt) DBUG_RETURN(result); } + /* Returns parameter columns meta information in the form of result set. - XXX: not implemented yet. + + SYNOPSYS + mysql_stmt_param_metadata() + stmt statement handle + + DESCRIPTION + This function can be called after you prepared the statement handle + with mysql_stmt_prepare(). + XXX: not implemented yet. + + RETURN + MYSQL_RES on success, 0 if there is no metadata. + Currently this function always returns 0. */ MYSQL_RES * STDCALL mysql_stmt_param_metadata(MYSQL_STMT *stmt) { DBUG_ENTER("mysql_stmt_param_metadata"); - + if (!stmt->param_count) DBUG_RETURN(0); /* - TODO: Fix this when server sends the information. - Till then keep a dummy prototype + TODO: Fix this when server sends the information. + Till then keep a dummy prototype. */ DBUG_RETURN(0); } +/* Store type of parameter in network buffer. */ + +static void store_param_type(char **pos, MYSQL_BIND *param) +{ + uint typecode= param->buffer_type | (param->is_unsigned ? 32768 : 0); + int2store(*pos, typecode); + *pos+= 2; +} + + /* Functions to store parameter data in network packet. - All functions have the following characteristics: - SYNOPSIS store_param_xxx() net MySQL NET connection param MySQL bind param - RETURN VALUES - 0 ok - 1 Error (Can't alloc net->buffer) + DESCRIPTION + These funtions are invoked from mysql_stmt_execute by + MYSQL_BIND::store_param_func pointer. This pointer is set once per many + executions in mysql_stmt_bind_param. The caller must ensure that network + buffer have enough capacity to store parameter (MYSQL_BIND::buffer_length + contains needed number of bytes). */ static void store_param_tinyint(NET *net, MYSQL_BIND *param) @@ -2253,7 +2283,7 @@ static void net_store_datetime(NET *net, MYSQL_TIME *tm) length= 4; else length= 0; - buff[0]= (char) length++; + buff[0]= (char) length++; memcpy((char *)net->write_pos, buff, length); net->write_pos+= length; } @@ -2271,7 +2301,7 @@ static void store_param_datetime(NET *net, MYSQL_BIND *param) MYSQL_TIME *tm= (MYSQL_TIME *) param->buffer; net_store_datetime(net, tm); } - + static void store_param_str(NET *net, MYSQL_BIND *param) { /* param->length is always set in mysql_stmt_bind_param */ @@ -2292,7 +2322,8 @@ static void store_param_str(NET *net, MYSQL_BIND *param) DESCRIPTION A data package starts with a string of bits where we set a bit - if a parameter is NULL + if a parameter is NULL. Unlike bit string in result set row, here + we don't have reserved bits for OK/error packet. */ static void store_param_null(NET *net, MYSQL_BIND *param) @@ -2303,8 +2334,9 @@ static void store_param_null(NET *net, MYSQL_BIND *param) /* - Set parameter data by reading from input buffers from the - client application + Store one parameter in network packet: data is read from + client buffer and saved in network packet by means of one + of store_param_xxxx functions. */ static my_bool store_param(MYSQL_STMT *stmt, MYSQL_BIND *param) @@ -2336,14 +2368,15 @@ static my_bool store_param(MYSQL_STMT *stmt, MYSQL_BIND *param) /* - Send the prepared query to server for execution + Auxilary function to send COM_EXECUTE packet to server and read reply. + Used from cli_stmt_execute, which is in turn used by mysql_stmt_execute. */ -static my_bool execute(MYSQL_STMT * stmt, char *packet, ulong length) +static my_bool execute(MYSQL_STMT *stmt, char *packet, ulong length) { MYSQL *mysql= stmt->mysql; NET *net= &mysql->net; - char buff[4 /* size of stmt id */ + + char buff[4 /* size of stmt id */ + 5 /* execution flags */]; DBUG_ENTER("execute"); DBUG_PRINT("enter",("packet: %s, length :%d",packet ? packet :" ", length)); @@ -2363,14 +2396,6 @@ static my_bool execute(MYSQL_STMT * stmt, char *packet, ulong length) stmt->insert_id= mysql->insert_id; DBUG_RETURN(0); } - - -static void store_param_type(char **pos, MYSQL_BIND *param) -{ - uint typecode= param->buffer_type | (param->is_unsigned ? 32768 : 0); - int2store(*pos, typecode); - *pos+= 2; -} int cli_stmt_execute(MYSQL_STMT *stmt) @@ -2413,7 +2438,7 @@ int cli_stmt_execute(MYSQL_STMT *stmt) for (param= stmt->params; param < param_end; param++) { - /* check if mysql_long_data() was used */ + /* check if mysql_stmt_send_long_data() was used */ if (param->long_data_used) param->long_data_used= 0; /* Clear for next execute call */ else if (store_param(stmt, param)) @@ -2472,16 +2497,16 @@ static int stmt_read_row_buffered(MYSQL_STMT *stmt, unsigned char **row) 0 - success; *row contains valid address of a row; row data is stored in network buffer 1 - error; error code is written to - stmt->last_{errno,error}; *row is not changed + stmt->last_{errno,error}; *row is not changed MYSQL_NO_DATA - end of file was read from network; - *row is to NULL + *row is set to NULL */ static int stmt_read_row_unbuffered(MYSQL_STMT *stmt, unsigned char **row) { int rc= 1; MYSQL *mysql= stmt->mysql; - /* + /* This function won't be called if stmt->field_count is zero or execution wasn't done: this is ensured by mysql_stmt_execute. */ @@ -2555,7 +2580,7 @@ stmt_read_row_no_data(MYSQL_STMT *stmt __attribute__((unused)), 0 success !0 wrong attribute type */ - + my_bool STDCALL mysql_stmt_attr_set(MYSQL_STMT *stmt, enum enum_stmt_attr_type attr_type, const void *value) @@ -2564,14 +2589,14 @@ my_bool STDCALL mysql_stmt_attr_set(MYSQL_STMT *stmt, case STMT_ATTR_UPDATE_MAX_LENGTH: stmt->update_max_length= value ? *(const my_bool*) value : 0; break; - default: + default: return TRUE; } return FALSE; } -my_bool STDCALL mysql_stmt_attr_get(MYSQL_STMT *stmt, +my_bool STDCALL mysql_stmt_attr_get(MYSQL_STMT *stmt, enum enum_stmt_attr_type attr_type, void *value) { @@ -2579,7 +2604,7 @@ my_bool STDCALL mysql_stmt_attr_get(MYSQL_STMT *stmt, case STMT_ATTR_UPDATE_MAX_LENGTH: *(unsigned long *) value= stmt->update_max_length; break; - default: + default: return TRUE; } return FALSE; @@ -2587,7 +2612,47 @@ my_bool STDCALL mysql_stmt_attr_get(MYSQL_STMT *stmt, /* - Execute the prepared query + Send placeholders data to server (if there are placeholders) + and execute prepared statement. + + SYNOPSIS + mysql_stmt_execute() + stmt statement handle. The handle must be created + with mysql_stmt_init() and prepared with + mysql_stmt_prepare(). If there are placeholders + in the statement they must be bound to local + variables with mysql_stmt_bind_param(). + + DESCRIPTION + This function will automatically flush pending result + set (if there is one), send parameters data to the server + and read result of statement execution. + If previous result set was cached with mysql_stmt_store_result() + it will also be freed in the beginning of this call. + The server can return 3 types of responses to this command: + - error, can be retrieved with mysql_stmt_error() + - ok, no result set pending. In this case we just update + stmt->insert_id and stmt->affected_rows. + - the query returns a result set: there could be 0 .. N + rows in it. In this case the server can also send updated + result set metadata. + + Next steps you may want to make: + - find out if there is result set with mysql_stmt_field_count(). + If there is one: + - optionally, cache entire result set on client to unblock + connection with mysql_stmt_store_result() + - bind client variables to result set columns and start read rows + with mysql_stmt_fetch(). + - reset statement with mysql_stmt_reset() or close it with + mysql_stmt_close() + Otherwise: + - find out last insert id and number of affected rows with + mysql_stmt_insert_id(), mysql_stmt_affected_rows() + + RETURN + 0 success + 1 error, message can be retrieved with mysql_stmt_error(). */ int STDCALL mysql_stmt_execute(MYSQL_STMT *stmt) @@ -2681,7 +2746,19 @@ unsigned int STDCALL mysql_stmt_field_count(MYSQL_STMT *stmt) } /* - Return last inserted id for auto_increment columns + Return last inserted id for auto_increment columns. + + SYNOPSIS + mysql_stmt_insert_id() + stmt statement handle + + DESCRIPTION + Current implementation of this call has a caveat: stmt->insert_id is + unconditionally updated from mysql->insert_id in the end of each + mysql_stmt_execute(). This works OK if mysql->insert_id contains new + value (sent in reply to mysql_stmt_execute()), otherwise stmt->insert_id + value gets undefined, as it's updated from some arbitrary value saved in + connection structure during some other call. */ my_ulonglong STDCALL mysql_stmt_insert_id(MYSQL_STMT *stmt) @@ -2693,11 +2770,24 @@ my_ulonglong STDCALL mysql_stmt_insert_id(MYSQL_STMT *stmt) static my_bool int_is_null_true= 1; /* Used for MYSQL_TYPE_NULL */ static my_bool int_is_null_false= 0; + /* - Setup the parameter data buffers from application + Setup the input parameter data buffers from application + + SYNOPSIS + mysql_stmt_bind_param() + stmt statement handle + The statement must be prepared with mysql_stmt_prepare(). + bind Array of mysql_stmt_param_count() bind parameters. + + RETURN + 0 success + 1 error, can be retrieved with mysql_stmt_error. + Note, that this function doesn't check that size of MYSQL_BIND + array is >= mysql_stmt_field_count(), */ -my_bool STDCALL mysql_stmt_bind_param(MYSQL_STMT *stmt, MYSQL_BIND * bind) +my_bool STDCALL mysql_stmt_bind_param(MYSQL_STMT *stmt, MYSQL_BIND *bind) { uint count=0; MYSQL_BIND *param, *end; @@ -2785,9 +2875,8 @@ my_bool STDCALL mysql_stmt_bind_param(MYSQL_STMT *stmt, MYSQL_BIND * bind) case MYSQL_TYPE_STRING: param->store_param_func= store_param_str; /* - For variable length types we expect user to set - length or buffer_length. Otherwise mysql_stmt_execute - will just fail. + For variable length types user must set either length or + buffer_length. */ break; default: @@ -2804,7 +2893,7 @@ my_bool STDCALL mysql_stmt_bind_param(MYSQL_STMT *stmt, MYSQL_BIND * bind) if (!param->length) param->length= ¶m->buffer_length; } - /* We have to send/resendtype information to MySQL */ + /* We have to send/resend type information to MySQL */ stmt->send_types_to_server= TRUE; stmt->bind_param_done= TRUE; DBUG_RETURN(0); @@ -2825,6 +2914,30 @@ my_bool STDCALL mysql_stmt_bind_param(MYSQL_STMT *stmt, MYSQL_BIND * bind) data Data to send to server length Length of data to send (may be 0) + DESCRIPTION + This call can be used repeatedly to send long data in pieces + for any string/binary placeholder. Data supplied for + a placeholder is saved at server side till execute, and then + used instead of value from MYSQL_BIND object. More precisely, + if long data for a parameter was supplied, MYSQL_BIND object + corresponding to this parameter is not sent to server. In the + end of execution long data states of placeholders are reset, + so next time values of such placeholders will be taken again + from MYSQL_BIND array. + The server does not reply to this call: if there was an error + in data handling (which now only can happen if server run out + of memory) it would be returned in reply to + mysql_stmt_execute(). + You should choose type of long data carefully if you care + about character set conversions performed by server when the + statement is executed. No conversion is performed at all for + MYSQL_TYPE_BLOB and other binary typecodes. For + MYSQL_TYPE_STRING and the rest of text placeholders data is + converted from client character set to character set of + connection. If these character sets are different, this + conversion may require additional memory at server, equal to + total size of supplied pieces. + RETURN VALUES 0 ok 1 error @@ -2839,7 +2952,7 @@ mysql_stmt_send_long_data(MYSQL_STMT *stmt, uint param_number, DBUG_ASSERT(stmt != 0); DBUG_PRINT("enter",("param no : %d, data : %lx, length : %ld", param_number, data, length)); - + /* We only need to check for stmt->param_count, if it's not null prepare was done. @@ -2864,7 +2977,7 @@ mysql_stmt_send_long_data(MYSQL_STMT *stmt, uint param_number, DBUG_RETURN(1); } - /* + /* Send long data packet if there is data or we're sending long data for the first time. */ @@ -2872,8 +2985,8 @@ mysql_stmt_send_long_data(MYSQL_STMT *stmt, uint param_number, { MYSQL *mysql= stmt->mysql; /* Packet header: stmt id (4 bytes), param no (2 bytes) */ - char buff[MYSQL_LONG_DATA_HEADER]; - + char buff[MYSQL_LONG_DATA_HEADER]; + int4store(buff, stmt->stmt_id); int2store(buff + 4, param_number); param->long_data_used= 1; @@ -2926,7 +3039,7 @@ static uint read_binary_time(MYSQL_TIME *tm, uchar **pos) { uchar *to; uint length; - + /* net_field_length will set pos to the first byte of data */ if (!(length= net_field_length(pos))) { @@ -2983,14 +3096,14 @@ static uint read_binary_date(MYSQL_TIME *tm, uchar **pos) { uchar *to; uint length; - + if (!(length= net_field_length(pos))) { set_zero_time(tm); return 0; } - - to= *pos; + + to= *pos; tm->year = (uint) sint2korr(to); tm->month= (uint) to[2]; tm->day= (uint) to[3]; @@ -3001,10 +3114,11 @@ static uint read_binary_date(MYSQL_TIME *tm, uchar **pos) return length; } + /* Convert Numeric to buffer types */ static void send_data_long(MYSQL_BIND *param, MYSQL_FIELD *field, longlong value) -{ +{ char *buffer= param->buffer; uint field_is_unsigned= (field->flags & UNSIGNED_FLAG); @@ -3015,52 +3129,52 @@ static void send_data_long(MYSQL_BIND *param, MYSQL_FIELD *field, *param->buffer= (uchar) value; break; case MYSQL_TYPE_SHORT: - int2store(buffer, value); + shortstore(buffer, value); break; case MYSQL_TYPE_LONG: - int4store(buffer, value); + longstore(buffer, value); break; case MYSQL_TYPE_LONGLONG: - int8store(buffer, value); + longlongstore(buffer, value); break; case MYSQL_TYPE_FLOAT: { float data= (field_is_unsigned ? (float) ulonglong2double(value) : (float) value); - float4store(buffer, data); + floatstore(buffer, data); break; } case MYSQL_TYPE_DOUBLE: { double data= (field_is_unsigned ? ulonglong2double(value) : (double) value); - float8store(buffer, data); + doublestore(buffer, data); break; } default: { char tmp[22]; /* Enough for longlong */ uint length= (uint)(longlong10_to_str(value,(char *)tmp, - field_is_unsigned ? 10: -10) - - tmp); + field_is_unsigned ? 10: -10) - + tmp); ulong copy_length= min((ulong)length-param->offset, param->buffer_length); if ((long) copy_length < 0) copy_length=0; else memcpy(buffer, (char *)tmp+param->offset, copy_length); - *param->length= length; - + *param->length= length; + if (copy_length != param->buffer_length) *(buffer+copy_length)= '\0'; } - } + } } /* Convert Double to buffer types */ static void send_data_double(MYSQL_BIND *param, double value) -{ +{ char *buffer= param->buffer; switch(param->buffer_type) { @@ -3070,24 +3184,26 @@ static void send_data_double(MYSQL_BIND *param, double value) *buffer= (uchar)value; break; case MYSQL_TYPE_SHORT: - int2store(buffer, (short)value); + shortstore(buffer, (short)value); break; case MYSQL_TYPE_LONG: - int4store(buffer, (long)value); + longstore(buffer, (long)value); break; case MYSQL_TYPE_LONGLONG: - int8store(buffer, (longlong)value); + { + longlong val= (longlong) value; + longlongstore(buffer, val); break; + } case MYSQL_TYPE_FLOAT: { - float data= (float)value; - float4store(buffer, data); + float data= (float) value; + floatstore(buffer, data); break; } case MYSQL_TYPE_DOUBLE: { - double data= (double)value; - float8store(buffer, data); + doublestore(buffer, value); break; } default: @@ -3099,19 +3215,19 @@ static void send_data_double(MYSQL_BIND *param, double value) copy_length=0; else memcpy(buffer, (char *)tmp+param->offset, copy_length); - *param->length= length; - + *param->length= length; + if (copy_length != param->buffer_length) *(buffer+copy_length)= '\0'; } - } + } } /* Convert string to buffer types */ static void send_data_str(MYSQL_BIND *param, char *value, uint length) -{ +{ char *buffer= param->buffer; int err=0; @@ -3129,32 +3245,32 @@ static void send_data_str(MYSQL_BIND *param, char *value, uint length) { short data= (short)my_strntol(&my_charset_latin1,value,length,10,NULL, &err); - int2store(buffer, data); + shortstore(buffer, data); break; } case MYSQL_TYPE_LONG: { int32 data= (int32)my_strntol(&my_charset_latin1,value,length,10,NULL, &err); - int4store(buffer, data); + longstore(buffer, data); break; } case MYSQL_TYPE_LONGLONG: { longlong data= my_strntoll(&my_charset_latin1,value,length,10,NULL,&err); - int8store(buffer, data); + longlongstore(buffer, data); break; } case MYSQL_TYPE_FLOAT: { float data = (float)my_strntod(&my_charset_latin1,value,length,NULL,&err); - float4store(buffer, data); + floatstore(buffer, data); break; } case MYSQL_TYPE_DOUBLE: { double data= my_strntod(&my_charset_latin1,value,length,NULL,&err); - float8store(buffer, data); + doublestore(buffer, data); break; } case MYSQL_TYPE_TINY_BLOB: @@ -3179,7 +3295,7 @@ static void send_data_str(MYSQL_BIND *param, char *value, uint length) } -static void send_data_time(MYSQL_BIND *param, MYSQL_TIME ltime, +static void send_data_time(MYSQL_BIND *param, MYSQL_TIME ltime, uint length) { switch (param->buffer_type) { @@ -3192,7 +3308,7 @@ static void send_data_time(MYSQL_BIND *param, MYSQL_TIME ltime, case MYSQL_TYPE_TIMESTAMP: { MYSQL_TIME *tm= (MYSQL_TIME *)param->buffer; - + tm->year= ltime.year; tm->month= ltime.month; tm->day= ltime.day; @@ -3203,33 +3319,33 @@ static void send_data_time(MYSQL_BIND *param, MYSQL_TIME ltime, tm->second_part= ltime.second_part; tm->neg= ltime.neg; - break; + break; } default: { char buff[25]; - + if (!length) ltime.time_type= MYSQL_TIMESTAMP_NONE; switch (ltime.time_type) { case MYSQL_TIMESTAMP_DATE: length= my_sprintf(buff,(buff, "%04d-%02d-%02d", ltime.year, - ltime.month,ltime.day)); + ltime.month,ltime.day)); break; case MYSQL_TIMESTAMP_FULL: length= my_sprintf(buff,(buff, "%04d-%02d-%02d %02d:%02d:%02d", - ltime.year,ltime.month,ltime.day, - ltime.hour,ltime.minute,ltime.second)); + ltime.year,ltime.month,ltime.day, + ltime.hour,ltime.minute,ltime.second)); break; case MYSQL_TIMESTAMP_TIME: length= my_sprintf(buff, (buff, "%02d:%02d:%02d", - ltime.hour,ltime.minute,ltime.second)); + ltime.hour,ltime.minute,ltime.second)); break; default: length= 0; buff[0]='\0'; } - send_data_str(param, (char *)buff, length); + send_data_str(param, (char *)buff, length); } } } @@ -3261,7 +3377,7 @@ static void fetch_results(MYSQL_BIND *param, MYSQL_FIELD *field, uchar **row) longlong data= ((field_is_unsigned) ? (longlong) (unsigned short) value: (longlong) value); send_data_long(param, field, data); - length= 2; + length= 2; break; } case MYSQL_TYPE_LONG: @@ -3300,7 +3416,7 @@ static void fetch_results(MYSQL_BIND *param, MYSQL_FIELD *field, uchar **row) case MYSQL_TYPE_DATE: { MYSQL_TIME tm; - + length= read_binary_date(&tm, row); tm.time_type= MYSQL_TIMESTAMP_DATE; send_data_time(param, tm, length); @@ -3309,7 +3425,7 @@ static void fetch_results(MYSQL_BIND *param, MYSQL_FIELD *field, uchar **row) case MYSQL_TYPE_TIME: { MYSQL_TIME tm; - + length= read_binary_time(&tm, row); tm.time_type= MYSQL_TIMESTAMP_TIME; send_data_time(param, tm, length); @@ -3319,14 +3435,14 @@ static void fetch_results(MYSQL_BIND *param, MYSQL_FIELD *field, uchar **row) case MYSQL_TYPE_TIMESTAMP: { MYSQL_TIME tm; - + length= read_binary_datetime(&tm, row); tm.time_type= MYSQL_TIMESTAMP_FULL; send_data_time(param, tm, length); break; } - default: - length= net_field_length(row); + default: + length= net_field_length(row); send_data_str(param,(char*) *row,length); break; } @@ -3355,7 +3471,7 @@ static void fetch_result_int32(MYSQL_BIND *param, uchar **row) } static void fetch_result_int64(MYSQL_BIND *param, uchar **row) -{ +{ longlong value= (longlong)sint8korr(*row); longlongstore(param->buffer, value); *row+= 8; @@ -3396,11 +3512,11 @@ static void fetch_result_datetime(MYSQL_BIND *param, uchar **row) } static void fetch_result_bin(MYSQL_BIND *param, uchar **row) -{ +{ ulong length= net_field_length(row); ulong copy_length= min(length, param->buffer_length); memcpy(param->buffer, (char *)*row, copy_length); - *param->length= length; + *param->length= length; *row+= length; } @@ -3481,7 +3597,7 @@ my_bool STDCALL mysql_stmt_bind_result(MYSQL_STMT *stmt, MYSQL_BIND *bind) We only need to check that stmt->field_count - if it is not null stmt->bind was initialized in mysql_stmt_prepare */ - + memcpy((char*) stmt->bind, (char*) bind, sizeof(MYSQL_BIND) * bind_count); for (param= stmt->bind, end= param + bind_count, field= stmt->fields ; @@ -3643,16 +3759,16 @@ static int stmt_fetch_row(MYSQL_STMT *stmt, uchar *row) /* If output parameters were not bound we should just return success */ return 0; } - - null_ptr= row; + + null_ptr= row; row+= (stmt->field_count+9)/8; /* skip null bits */ bit= 4; /* first 2 bits are reserved */ - + /* Copy complete row to application buffers */ for (bind= stmt->bind, end= bind + stmt->field_count, field= stmt->fields ; bind < end ; bind++, field++) - { + { if (*null_ptr & bit) { /* @@ -3666,12 +3782,12 @@ static int stmt_fetch_row(MYSQL_STMT *stmt, uchar *row) *bind->is_null= 1; } else - { + { *bind->is_null= 0; bind->inter_buffer= row; if (field->type == bind->buffer_type) (*bind->fetch_result)(bind, &row); - else + else fetch_results(bind, field, &row); } if (!((bit<<=1) & 255)) @@ -3731,15 +3847,15 @@ int STDCALL mysql_stmt_fetch(MYSQL_STMT *stmt) column Column to fetch (first column is 0) ulong offset Offset in result data (to fetch blob in pieces) This is normally 0 - RETURN + RETURN 0 ok 1 error */ -int STDCALL mysql_stmt_fetch_column(MYSQL_STMT *stmt, MYSQL_BIND *bind, +int STDCALL mysql_stmt_fetch_column(MYSQL_STMT *stmt, MYSQL_BIND *bind, uint column, ulong offset) { - MYSQL_BIND *param= stmt->bind+column; + MYSQL_BIND *param= stmt->bind+column; DBUG_ENTER("mysql_stmt_fetch_column"); if ((int) stmt->state < (int) MYSQL_STMT_FETCH_DONE) @@ -3755,7 +3871,7 @@ int STDCALL mysql_stmt_fetch_column(MYSQL_STMT *stmt, MYSQL_BIND *bind, if (param->inter_buffer) { - MYSQL_FIELD *field= stmt->fields+column; + MYSQL_FIELD *field= stmt->fields+column; uchar *row= param->inter_buffer; bind->offset= offset; if (bind->is_null) @@ -3826,6 +3942,49 @@ err: /* + Update meta data for statement + + SYNOPSIS + stmt_update_metadata() + stmt Statement handler + row Binary data + + NOTES + Only updates MYSQL_FIELD->max_length for strings +*/ + +static void stmt_update_metadata(MYSQL_STMT *stmt, MYSQL_ROWS *data) +{ + MYSQL_BIND *bind, *end; + MYSQL_FIELD *field; + uchar *null_ptr, bit; + uchar *row= (uchar*) data->data; +#ifndef DBUG_OFF + uchar *row_end= row + data->length; +#endif + + null_ptr= row; + row+= (stmt->field_count+9)/8; /* skip null bits */ + bit= 4; /* first 2 bits are reserved */ + + /* Go throw all fields and calculate metadata */ + for (bind= stmt->bind, end= bind + stmt->field_count, field= stmt->fields ; + bind < end ; + bind++, field++) + { + if (!(*null_ptr & bit)) + (*bind->skip_result)(bind, field, &row); + DBUG_ASSERT(row <= row_end); + if (!((bit<<=1) & 255)) + { + bit= 1; /* To next byte */ + null_ptr++; + } + } +} + + +/* Store or buffer the binary results to stmt */ @@ -3880,6 +4039,7 @@ int STDCALL mysql_stmt_store_result(MYSQL_STMT *stmt) free_root(&result->alloc, MYF(MY_KEEP_PREALLOC)); result->data= NULL; result->rows= 0; + mysql->status= MYSQL_STATUS_READY; DBUG_RETURN(1); } @@ -3908,7 +4068,7 @@ mysql_stmt_row_seek(MYSQL_STMT *stmt, MYSQL_ROW_OFFSET row) { MYSQL_ROW_OFFSET offset= stmt->data_cursor; DBUG_ENTER("mysql_stmt_row_seek"); - + stmt->data_cursor= row; DBUG_RETURN(offset); } @@ -3918,11 +4078,11 @@ mysql_stmt_row_seek(MYSQL_STMT *stmt, MYSQL_ROW_OFFSET row) Return the current statement row cursor position */ -MYSQL_ROW_OFFSET STDCALL +MYSQL_ROW_OFFSET STDCALL mysql_stmt_row_tell(MYSQL_STMT *stmt) { DBUG_ENTER("mysql_stmt_row_tell"); - + DBUG_RETURN(stmt->data_cursor); } @@ -3937,7 +4097,7 @@ mysql_stmt_data_seek(MYSQL_STMT *stmt, my_ulonglong row) MYSQL_ROWS *tmp= stmt->result.data; DBUG_ENTER("mysql_stmt_data_seek"); DBUG_PRINT("enter",("row id to seek: %ld",(long) row)); - + for (; tmp && row; --row, tmp= tmp->next) ; stmt->data_cursor= tmp; @@ -3952,7 +4112,7 @@ mysql_stmt_data_seek(MYSQL_STMT *stmt, my_ulonglong row) my_ulonglong STDCALL mysql_stmt_num_rows(MYSQL_STMT *stmt) { DBUG_ENTER("mysql_stmt_num_rows"); - + DBUG_RETURN(stmt->result.rows); } @@ -3962,7 +4122,7 @@ my_bool STDCALL mysql_stmt_free_result(MYSQL_STMT *stmt) DBUG_ENTER("mysql_stmt_free_result"); DBUG_ASSERT(stmt != 0); - + if ((int) stmt->state > (int) MYSQL_STMT_INIT_DONE) { MYSQL *mysql= stmt->mysql; @@ -4030,7 +4190,7 @@ my_bool STDCALL mysql_stmt_close(MYSQL_STMT *stmt) mysql->unbuffered_fetch_owner= 0; if (mysql->status != MYSQL_STATUS_READY) { - /* + /* Flush result set of the connection. If it does not belong to this statement, set a warning. */ @@ -4068,13 +4228,13 @@ my_bool STDCALL mysql_stmt_reset(MYSQL_STMT *stmt) /* If statement hasnt been prepared there is nothing to reset */ if ((int) stmt->state < (int) MYSQL_STMT_PREPARE_DONE) DBUG_RETURN(0); - + mysql= stmt->mysql->last_used_con; int4store(buff, stmt->stmt_id); /* Send stmt id to server */ if ((*mysql->methods->advanced_command)(mysql, COM_RESET_STMT, buff, sizeof(buff), 0, 0, 0)) { - set_stmt_errmsg(stmt, mysql->net.last_error, mysql->net.last_errno, + set_stmt_errmsg(stmt, mysql->net.last_error, mysql->net.last_errno, mysql->net.sqlstate); DBUG_RETURN(1); } @@ -4115,50 +4275,6 @@ const char *STDCALL mysql_stmt_error(MYSQL_STMT * stmt) } -/* - Update meta data for statement - - SYNOPSIS - stmt_update_metadata() - stmt Statement handler - row Binary data - - NOTES - Only updates MYSQL_FIELD->max_length for strings - -*/ - -static void stmt_update_metadata(MYSQL_STMT *stmt, MYSQL_ROWS *data) -{ - MYSQL_BIND *bind, *end; - MYSQL_FIELD *field; - uchar *null_ptr, bit; - uchar *row= (uchar*) data->data; -#ifndef DBUG_OFF - uchar *row_end= row + data->length; -#endif - - null_ptr= row; - row+= (stmt->field_count+9)/8; /* skip null bits */ - bit= 4; /* first 2 bits are reserved */ - - /* Go throw all fields and calculate metadata */ - for (bind= stmt->bind, end= bind + stmt->field_count, field= stmt->fields ; - bind < end ; - bind++, field++) - { - if (!(*null_ptr & bit)) - (*bind->skip_result)(bind, field, &row); - DBUG_ASSERT(row <= row_end); - if (!((bit<<=1) & 255)) - { - bit= 1; /* To next byte */ - null_ptr++; - } - } -} - - /******************************************************************** Transactional APIs *********************************************************************/ @@ -4212,10 +4328,10 @@ my_bool STDCALL mysql_more_results(MYSQL *mysql) { my_bool res; DBUG_ENTER("mysql_more_results"); - - res= ((mysql->last_used_con->server_status & SERVER_MORE_RESULTS_EXISTS) ? + + res= ((mysql->last_used_con->server_status & SERVER_MORE_RESULTS_EXISTS) ? 1: 0); - DBUG_PRINT("exit",("More results exists ? %d", res)); + DBUG_PRINT("exit",("More results exists ? %d", res)); DBUG_RETURN(res); } @@ -4226,7 +4342,7 @@ my_bool STDCALL mysql_more_results(MYSQL *mysql) int STDCALL mysql_next_result(MYSQL *mysql) { DBUG_ENTER("mysql_next_result"); - + if (mysql->status != MYSQL_STATUS_READY) { strmov(mysql->net.sqlstate, unknown_sqlstate); diff --git a/libmysqld/Makefile.am b/libmysqld/Makefile.am index b165d7f457b..58e11e4f297 100644 --- a/libmysqld/Makefile.am +++ b/libmysqld/Makefile.am @@ -56,7 +56,7 @@ sqlsources = derror.cc field.cc field_conv.cc strfunc.cc filesort.cc \ sql_string.cc sql_table.cc sql_test.cc sql_udf.cc \ sql_update.cc sql_yacc.cc table.cc thr_malloc.cc time.cc \ unireg.cc uniques.cc stacktrace.c sql_union.cc hash_filo.cc \ - spatial.cc gstream.cc sql_help.cc + spatial.cc gstream.cc sql_help.cc tztime.cc libmysqld_int_a_SOURCES= $(libmysqld_sources) $(libmysqlsources) $(sqlsources) libmysqld_a_SOURCES= diff --git a/libmysqld/lib_sql.cc b/libmysqld/lib_sql.cc index d1a140c754a..0adf9aeb86a 100644 --- a/libmysqld/lib_sql.cc +++ b/libmysqld/lib_sql.cc @@ -356,6 +356,7 @@ int init_embedded_server(int argc, char **argv, char **groups) int fake_argc = 1; char *fake_argv[] = { (char *)"", 0 }; const char *fake_groups[] = { "server", "embedded", 0 }; + my_bool acl_error; if (argc) { argcp= &argc; @@ -397,16 +398,17 @@ int init_embedded_server(int argc, char **argv, char **groups) error_handler_hook = my_message_sql; + acl_error= 0; #ifndef NO_EMBEDDED_ACCESS_CHECKS - if (acl_init((THD *)0, opt_noacl)) + if (!(acl_error= acl_init((THD *)0, opt_noacl)) && + !opt_noacl) + (void) grant_init((THD *)0); +#endif + if (acl_error || my_tz_init((THD *)0, default_tz_name, opt_bootstrap)) { mysql_server_end(); return 1; } - if (!opt_noacl) - (void) grant_init((THD *)0); - -#endif init_max_user_conn(); init_update_queries(); diff --git a/myisam/ft_boolean_search.c b/myisam/ft_boolean_search.c index cac4d08f5d6..196cb5c21fb 100644 --- a/myisam/ft_boolean_search.c +++ b/myisam/ft_boolean_search.c @@ -53,10 +53,11 @@ static double _nwghts[11]= -3.796875000000000}; static double *nwghts=_nwghts+5; /* nwghts[i] = -0.5*1.5**i */ -#define FTB_FLAG_TRUNC 1 /* */ -#define FTB_FLAG_YES 2 /* no two from these three */ -#define FTB_FLAG_NO 4 /* YES, NO, WONLY */ -#define FTB_FLAG_WONLY 8 /* should be _ever_ set both */ +#define FTB_FLAG_TRUNC 1 +/* At most one of the following flags can be set */ +#define FTB_FLAG_YES 2 +#define FTB_FLAG_NO 4 +#define FTB_FLAG_WONLY 8 typedef struct st_ftb_expr FTB_EXPR; struct st_ftb_expr diff --git a/myisam/mi_check.c b/myisam/mi_check.c index 96a26fd90f1..052fa55a559 100644 --- a/myisam/mi_check.c +++ b/myisam/mi_check.c @@ -376,8 +376,6 @@ int chk_key(MI_CHECK *param, register MI_INFO *info) for (key= 0,keyinfo= &share->keyinfo[0]; key < share->base.keys ; rec_per_key_part+=keyinfo->keysegs, key++, keyinfo++) { - if (*killed_ptr(param)) - DBUG_RETURN(-1); param->key_crc[key]=0; if (!(((ulonglong) 1 << key) & share->state.key_map)) { diff --git a/myisam/mi_unique.c b/myisam/mi_unique.c index 06d50e6905b..ad685f4cbdc 100644 --- a/myisam/mi_unique.c +++ b/myisam/mi_unique.c @@ -69,8 +69,8 @@ my_bool mi_check_unique(MI_INFO *info, MI_UNIQUEDEF *def, byte *record, ha_checksum mi_unique_hash(MI_UNIQUEDEF *def, const byte *record) { const byte *pos, *end; - ulong crc= 0; - ulong seed= 4; + ha_checksum crc= 0; + ulong seed1=0, seed2= 4; HA_KEYSEG *keyseg; for (keyseg=def->seg ; keyseg < def->end ; keyseg++) @@ -110,7 +110,9 @@ ha_checksum mi_unique_hash(MI_UNIQUEDEF *def, const byte *record) if (type == HA_KEYTYPE_TEXT || type == HA_KEYTYPE_VARTEXT) { keyseg->charset->coll->hash_sort(keyseg->charset, - (const uchar*) pos, length, &crc, &seed); + (const uchar*) pos, length, &seed1, + &seed2); + crc^= seed1; } else while (pos != end) @@ -118,7 +120,7 @@ ha_checksum mi_unique_hash(MI_UNIQUEDEF *def, const byte *record) (((uchar) *(uchar*) pos++))) + (crc >> (8*sizeof(ha_checksum)-8)); } - return (ha_checksum)crc; + return crc; } /* diff --git a/myisam/myisamchk.c b/myisam/myisamchk.c index ef4b7a5ff87..c4b5acadc92 100644 --- a/myisam/myisamchk.c +++ b/myisam/myisamchk.c @@ -362,13 +362,13 @@ static void usage(void) this option is deprecated; you can set variables\n\ directly with '--variable-name=value'.\n\ -t, --tmpdir=path Path for temporary files. Multiple paths can be\n\ - specified, separated by " + specified, separated by "); #if defined( __WIN__) || defined(OS2) || defined(__NETWARE__) - "semicolon (;)" + puts("semicolon (;)"); #else - "colon (:)" + puts("colon (:)"); #endif - ", they will be used\n\ + puts(", they will be used\n\ in a round-robin fashion.\n\ -s, --silent Only print errors. One can use two -s to make\n\ myisamchk very silent.\n\ diff --git a/mysql-test/mysql-test-run.sh b/mysql-test/mysql-test-run.sh index 32ed205f0db..d4c5bfb2209 100644 --- a/mysql-test/mysql-test-run.sh +++ b/mysql-test/mysql-test-run.sh @@ -16,7 +16,7 @@ USE_MANAGER=0 MY_TZ=GMT-3 TZ=$MY_TZ; export TZ # for UNIX_TIMESTAMP tests to work LOCAL_SOCKET=@MYSQL_UNIX_ADDR@ -MYSQL_TCP_PORT=@MYSQL_TCP_PORT@; export MYSQL_TCP_PORT +MYSQL_TCP_PORT=@MYSQL_TCP_PORT@ # For query_cache test case `uname` in @@ -434,7 +434,7 @@ SLAVE_MYERR="$MYSQL_TEST_DIR/var/log/slave.err" CURRENT_TEST="$MYSQL_TEST_DIR/var/log/current_test" SMALL_SERVER="--key_buffer_size=1M --sort_buffer=256K --max_heap_table_size=1M" -export MASTER_MYPORT SLAVE_MYPORT +export MASTER_MYPORT SLAVE_MYPORT MYSQL_TCP_PORT if [ x$SOURCE_DIST = x1 ] ; then MY_BASEDIR=$MYSQL_TEST_DIR diff --git a/mysql-test/ndb/ndb_config_2_node.ini b/mysql-test/ndb/ndb_config_2_node.ini index c4ff0aba80a..5acb757d253 100644 --- a/mysql-test/ndb/ndb_config_2_node.ini +++ b/mysql-test/ndb/ndb_config_2_node.ini @@ -73,63 +73,5 @@ ExecuteOnComputer: 6 Id: 14 ExecuteOnComputer: 7 -# Mgmtsrvr connections - -[TCP] -NodeId1: 1 -NodeId2: 2 +[TCP DEFAULT] PortNumber: CHOOSE_PORT_BASE02 - -[TCP] -NodeId1: 1 -NodeId2: 3 -PortNumber: CHOOSE_PORT_BASE03 - -# Ndb nodes connections - -[TCP] -NodeId1: 2 -NodeId2: 3 -PortNumber: CHOOSE_PORT_BASE04 - -# Api connections - -[TCP] -NodeId1: 11 -NodeId2: 2 -PortNumber: CHOOSE_PORT_BASE05 - -[TCP] -NodeId1: 11 -NodeId2: 3 -PortNumber: CHOOSE_PORT_BASE06 - -[TCP] -NodeId1: 12 -NodeId2: 2 -PortNumber: CHOOSE_PORT_BASE07 - -[TCP] -NodeId1: 12 -NodeId2: 3 -PortNumber: CHOOSE_PORT_BASE08 - -[TCP] -NodeId1: 13 -NodeId2: 2 -PortNumber: CHOOSE_PORT_BASE09 - -[TCP] -NodeId1: 13 -NodeId2: 3 -PortNumber: CHOOSE_PORT_BASE10 - -[TCP] -NodeId1: 14 -NodeId2: 2 -PortNumber: CHOOSE_PORT_BASE11 - -[TCP] -NodeId1: 14 -NodeId2: 3 -PortNumber: CHOOSE_PORT_BASE12 diff --git a/mysql-test/r/alter_table.result b/mysql-test/r/alter_table.result index a8f540af0a2..c2ed40f3f94 100644 --- a/mysql-test/r/alter_table.result +++ b/mysql-test/r/alter_table.result @@ -468,5 +468,5 @@ t1 CREATE TABLE `t1` ( UNIQUE KEY `b` (`b`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1 ALTER TABLE t1 DROP PRIMARY KEY; -ERROR 42000: Can't DROP 'PRIMARY'. Check that column/key exists +ERROR 42000: Can't DROP 'PRIMARY'; check that column/key exists DROP TABLE t1; diff --git a/mysql-test/r/auto_increment.result b/mysql-test/r/auto_increment.result index 97bf835a9d2..6bc59d4771f 100644 --- a/mysql-test/r/auto_increment.result +++ b/mysql-test/r/auto_increment.result @@ -1,4 +1,5 @@ drop table if exists t1; +drop table if exists t2; SET SQL_WARNINGS=1; create table t1 (a int not null auto_increment,b int, primary key (a)) engine=myisam auto_increment=3; insert into t1 values (1,1),(NULL,3),(NULL,4); diff --git a/mysql-test/r/connect.result b/mysql-test/r/connect.result index c0608af0de2..6ac32232b2b 100644 --- a/mysql-test/r/connect.result +++ b/mysql-test/r/connect.result @@ -9,6 +9,11 @@ help_relation help_topic host tables_priv +time_zone +time_zone_leap_second +time_zone_name +time_zone_transition +time_zone_transition_type user show tables; Tables_in_test @@ -25,6 +30,11 @@ help_relation help_topic host tables_priv +time_zone +time_zone_leap_second +time_zone_name +time_zone_transition +time_zone_transition_type user show tables; Tables_in_test @@ -42,6 +52,11 @@ help_relation help_topic host tables_priv +time_zone +time_zone_leap_second +time_zone_name +time_zone_transition +time_zone_transition_type user show tables; Tables_in_test diff --git a/mysql-test/r/date_formats.result b/mysql-test/r/date_formats.result index 6637750913a..6a4935ef3f8 100644 --- a/mysql-test/r/date_formats.result +++ b/mysql-test/r/date_formats.result @@ -303,14 +303,14 @@ date format str_to_date 2003-01-02 10:11:12 %Y-%m-%d %h:%i:%S 2003-01-02 10:11:12 03-01-02 10:11:12 PM %Y-%m-%d %h:%i:%S %p 0003-01-02 22:11:12 Warnings: -Note 1292 Truncated incorrect string value: '10:20:10AM' +Warning 1292 Truncated incorrect datetime value: '10:20:10AM' select date,format,concat(str_to_date(date, format),'') as con from t1; date format con 10:20:10AM %h:%i:%s 0000-00-00 10:20:10 2003-01-02 10:11:12 %Y-%m-%d %h:%i:%S 2003-01-02 10:11:12 03-01-02 10:11:12 PM %Y-%m-%d %h:%i:%S %p 0003-01-02 22:11:12 Warnings: -Note 1292 Truncated incorrect string value: '10:20:10AM' +Warning 1292 Truncated incorrect datetime value: '10:20:10AM' drop table t1; select get_format(DATE, 'USA') as a; a @@ -374,7 +374,7 @@ str_to_date("02 10", "%d %f") as f6; f1 f2 f3 f4 f5 f6 2003-01-02 10:11:12.001200 2003-01-02 10:11:12 2003-01-02 58:11:12 58:11:12 48:00:00.100000 Warnings: -Note 1292 Truncated incorrect datetime value: '2003-01-02 10:11:12.0012' +Warning 1292 Truncated incorrect datetime value: '2003-01-02 10:11:12.0012' drop table t1, t2; select str_to_date("2003-01-02 10:11:12.0012ABCD", "%Y-%m-%d %H:%i:%S.%f") as f1, addtime("-01:01:01.01 GGG", "-23:59:59.1") as f2, @@ -382,13 +382,13 @@ microsecond("1997-12-31 23:59:59.01XXXX") as f3; f1 f2 f3 2003-01-02 10:11:12.001200 -25:01:00.110000 10000 Warnings: -Note 1292 Truncated incorrect datetime value: '2003-01-02 10:11:12.0012ABCD' -Note 1292 Truncated incorrect time value: '-01:01:01.01 GG' -Note 1292 Truncated incorrect datetime value: '1997-12-31 23:59:59.01XXXX' +Warning 1292 Truncated incorrect datetime value: '2003-01-02 10:11:12.0012ABCD' +Warning 1292 Truncated incorrect time value: '-01:01:01.01 GGG' +Warning 1292 Truncated incorrect time value: '1997-12-31 23:59:59.01XXXX' select str_to_date("2003-04-05 g", "%Y-%m-%d") as f1, str_to_date("2003-04-05 10:11:12.101010234567", "%Y-%m-%d %H:%i:%S.%f") as f2; f1 f2 2003-04-05 2003-04-05 10:11:12.101010 Warnings: -Note 1292 Truncated incorrect date value: '2003-04-05 g' -Note 1292 Truncated incorrect datetime value: '2003-04-05 10:11:12.101010234567' +Warning 1292 Truncated incorrect date value: '2003-04-05 g' +Warning 1292 Truncated incorrect datetime value: '2003-04-05 10:11:12.101010234567' diff --git a/mysql-test/r/delete.result b/mysql-test/r/delete.result index f1acc5d2dfa..c6b7a40214d 100644 --- a/mysql-test/r/delete.result +++ b/mysql-test/r/delete.result @@ -120,3 +120,13 @@ a b 0 10 1 11 drop table t11, t12, t2; +create table t1 (a int, b int, unique key (a), key (b)); +insert into t1 values (3, 3), (7, 7); +delete t1 from t1 where a = 3; +check table t1; +Table Op Msg_type Msg_text +test.t1 check status OK +select * from t1; +a b +7 7 +drop table t1; diff --git a/mysql-test/r/func_gconcat.result b/mysql-test/r/func_gconcat.result index 0c744d1dff6..0c8054c1f03 100644 --- a/mysql-test/r/func_gconcat.result +++ b/mysql-test/r/func_gconcat.result @@ -149,16 +149,19 @@ grp group_concat(c order by c) 3 D,D,E 4 5 NULL -set group_concat_max_len = 5; +set group_concat_max_len = 4; select grp,group_concat(c) from t1 group by grp; grp group_concat(c) 1 NULL 2 b -3 D,D,E +3 D,D, 4 5 NULL +Warnings: +Warning 1260 1 line(s) were cut by GROUP_CONCAT() show warnings; Level Code Message +Warning 1260 1 line(s) were cut by GROUP_CONCAT() set group_concat_max_len = 1024; select group_concat(sum(a)) from t1 group by grp; ERROR HY000: Invalid use of group function diff --git a/mysql-test/r/func_if.result b/mysql-test/r/func_if.result index ce26a0bf30a..4c8a0561b0a 100644 --- a/mysql-test/r/func_if.result +++ b/mysql-test/r/func_if.result @@ -77,3 +77,9 @@ select min(if(y -x > 5,y,NULL)), max(if(y - x > 5,y,NULL)) from t1; min(if(y -x > 5,y,NULL)) max(if(y - x > 5,y,NULL)) 6 56 drop table t1; +create table t1 (a int); +insert t1 values (1),(2); +select if(1>2,a,avg(a)) from t1; +if(1>2,a,avg(a)) +1.5000 +drop table t1; diff --git a/mysql-test/r/func_like.result b/mysql-test/r/func_like.result index 75692738caf..f2c11bc51f6 100644 --- a/mysql-test/r/func_like.result +++ b/mysql-test/r/func_like.result @@ -45,6 +45,12 @@ a\b select * from t1 where a like 'a\\%' escape '#' and a like 'a\\\\b'; a a\b +prepare stmt1 from 'select * from t1 where a like \'a\\%\' escape ?'; +set @esc='#'; +execute stmt1 using @esc; +a +a\b +deallocate prepare stmt1; drop table t1; create table t1 (a datetime); insert into t1 values ('2004-03-11 12:00:21'); diff --git a/mysql-test/r/func_regexp.result b/mysql-test/r/func_regexp.result index 7f977e2782b..8228d6982d3 100644 --- a/mysql-test/r/func_regexp.result +++ b/mysql-test/r/func_regexp.result @@ -81,3 +81,20 @@ _latin1'a' regexp _latin1'A' collate latin1_general_ci select _latin1'a' regexp _latin1'A' collate latin1_bin; _latin1'a' regexp _latin1'A' collate latin1_bin 0 +create table t1 (a varchar(40)); +insert into t1 values ('C1'),('C2'),('R1'),('C3'),('R2'),('R3'); +prepare stmt1 from 'select a from t1 where a rlike ? order by a'; +set @a="^C.*"; +execute stmt1 using @a; +a +C1 +C2 +C3 +set @a="^R.*"; +execute stmt1 using @a; +a +R1 +R2 +R3 +deallocate prepare stmt1; +drop table t1; diff --git a/mysql-test/r/func_sapdb.result b/mysql-test/r/func_sapdb.result index 31868261157..cf2bd687115 100644 --- a/mysql-test/r/func_sapdb.result +++ b/mysql-test/r/func_sapdb.result @@ -119,6 +119,8 @@ timestamp("2001-12-01", "01:01:01.999999") select timestamp("2001-13-01", "01:01:01.000001"); timestamp("2001-13-01", "01:01:01.000001") NULL +Warnings: +Warning 1292 Truncated incorrect datetime value: '2001-13-01' select timestamp("2001-12-01", "25:01:01"); timestamp("2001-12-01", "25:01:01") 2001-12-02 01:01:01 @@ -137,12 +139,16 @@ date("1997-12-31 23:59:59.000001") select date("1997-13-31 23:59:59.000001"); date("1997-13-31 23:59:59.000001") NULL +Warnings: +Warning 1292 Truncated incorrect datetime value: '1997-13-31 23:59:59.000001' select time("1997-12-31 23:59:59.000001"); time("1997-12-31 23:59:59.000001") 23:59:59.000001 select time("1997-12-31 25:59:59.000001"); time("1997-12-31 25:59:59.000001") NULL +Warnings: +Warning 1292 Truncated incorrect time value: '1997-12-31 25:59:59.000001' select microsecond("1997-12-31 23:59:59.000001"); microsecond("1997-12-31 23:59:59.000001") 1 diff --git a/mysql-test/r/func_time.result b/mysql-test/r/func_time.result index 6f1b4af5d3c..2dc6bffd071 100644 --- a/mysql-test/r/func_time.result +++ b/mysql-test/r/func_time.result @@ -411,9 +411,13 @@ INSERT INTO t1 VALUES (''); SELECT month(updated) from t1; month(updated) NULL +Warnings: +Warning 1292 Truncated incorrect datetime value: '' SELECT year(updated) from t1; year(updated) NULL +Warnings: +Warning 1292 Truncated incorrect datetime value: '' drop table t1; create table t1 (d date, dt datetime, t timestamp, c char(10)); insert into t1 values ("0000-00-00", "0000-00-00", "0000-00-00", "0000-00-00"); @@ -528,7 +532,9 @@ date_add(date,INTERVAL "1 1:1:1" DAY_SECOND) 2003-01-03 01:01:01 select date_add(time,INTERVAL 1 SECOND) from t1; date_add(time,INTERVAL 1 SECOND) -2006-07-08 00:00:01 +NULL +Warnings: +Warning 1264 Data truncated; out of range for column 'time' at row 1 drop table t1; select last_day('2000-02-05') as f1, last_day('2002-12-31') as f2, last_day('2003-03-32') as f3, last_day('2003-04-01') as f4, @@ -536,6 +542,8 @@ last_day('2001-01-01 01:01:01') as f5, last_day(NULL), last_day('2001-02-12'); f1 f2 f3 f4 f5 last_day(NULL) last_day('2001-02-12') 2000-02-29 2002-12-31 NULL 2003-04-30 2001-01-31 NULL 2001-02-28 +Warnings: +Warning 1292 Truncated incorrect datetime value: '2003-03-32' create table t1 select last_day('2000-02-05') as a, from_days(to_days("960101")) as b; describe t1; diff --git a/mysql-test/r/packet.result b/mysql-test/r/packet.result index 5729d7af166..29bbcf4466f 100644 --- a/mysql-test/r/packet.result +++ b/mysql-test/r/packet.result @@ -12,7 +12,7 @@ select @@net_buffer_length, @@max_allowed_packet; @@net_buffer_length @@max_allowed_packet 1024 1024 SELECT length("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") as len; -ERROR 08S01: Got a packet bigger than 'max_allowed_packet' +ERROR 08S01: Got a packet bigger than 'max_allowed_packet' bytes set global max_allowed_packet=default; set max_allowed_packet=default; set global net_buffer_length=default; diff --git a/mysql-test/r/ps.result b/mysql-test/r/ps.result index fd3a7d54b4d..7d80d08e663 100644 --- a/mysql-test/r/ps.result +++ b/mysql-test/r/ps.result @@ -113,3 +113,27 @@ execute stmt1 using @ivar; ? 1234 drop table t1,t2; +PREPARE stmt1 FROM "select _utf8 'A' collate utf8_bin = ?"; +set @var='A'; +EXECUTE stmt1 USING @var; +_utf8 'A' collate utf8_bin = ? +1 +DEALLOCATE PREPARE stmt1; +create table t1 (id int); +prepare stmt1 from "select FOUND_ROWS()"; +select SQL_CALC_FOUND_ROWS * from t1; +id +execute stmt1; +FOUND_ROWS() +0 +insert into t1 values (1); +select SQL_CALC_FOUND_ROWS * from t1; +id +1 +execute stmt1; +FOUND_ROWS() +1 +execute stmt1; +FOUND_ROWS() +0 +deallocate prepare stmt1; diff --git a/mysql-test/r/range.result b/mysql-test/r/range.result index 290e72d3b55..06a479c964c 100644 --- a/mysql-test/r/range.result +++ b/mysql-test/r/range.result @@ -299,6 +299,27 @@ a b 15 1 47 1 DROP TABLE t1; +CREATE TABLE t1 ( +id int( 11 ) unsigned NOT NULL AUTO_INCREMENT , +line int( 5 ) unsigned NOT NULL default '0', +columnid int( 3 ) unsigned NOT NULL default '0', +owner int( 3 ) unsigned NOT NULL default '0', +ordinal int( 3 ) unsigned NOT NULL default '0', +showid smallint( 6 ) unsigned NOT NULL default '1', +tableid int( 1 ) unsigned NOT NULL default '1', +content int( 5 ) unsigned NOT NULL default '188', +PRIMARY KEY ( owner, id ) , +KEY menu( owner, showid, columnid ) , +KEY `COLUMN` ( owner, columnid, line ) , +KEY `LINES` ( owner, tableid, content, id ) , +KEY recount( owner, line ) +) ENGINE = MYISAM; +INSERT into t1 (owner,id,columnid,line) values (11,15,15,1),(11,13,13,5); +SELECT id, columnid, tableid, content, showid, line, ordinal FROM t1 WHERE owner=11 AND ((columnid IN ( 15, 13, 14 ) AND line IN ( 1, 2, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 31 )) OR (columnid IN ( 13, 14 ) AND line IN ( 15 ))) LIMIT 0 , 30; +id columnid tableid content showid line ordinal +13 13 1 188 1 5 0 +15 15 1 188 1 1 0 +drop table t1; create table t1 (id int(10) primary key); insert into t1 values (1),(2),(3),(4),(5),(6),(7),(8),(9); select id from t1 where id in (2,5,9) ; diff --git a/mysql-test/r/rpl_free_items.result b/mysql-test/r/rpl_free_items.result new file mode 100644 index 00000000000..91c1e2aa6e5 --- /dev/null +++ b/mysql-test/r/rpl_free_items.result @@ -0,0 +1,10 @@ +stop slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +reset master; +reset slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +start slave; +create table t1 (a int); +create table t2 (a int); +drop table t1; +drop table t2; diff --git a/mysql-test/r/rpl_get_lock.result b/mysql-test/r/rpl_get_lock.result index 2c57069e91a..26f33bfb42c 100644 --- a/mysql-test/r/rpl_get_lock.result +++ b/mysql-test/r/rpl_get_lock.result @@ -18,9 +18,9 @@ get_lock("lock",3) select * from t1; n 1 -select is_free_lock("lock"), is_used_lock("lock"); -is_free_lock("lock") is_used_lock("lock") -0 6 +select is_free_lock("lock"), is_used_lock("lock") = connection_id(); +is_free_lock("lock") is_used_lock("lock") = connection_id() +0 1 explain extended select is_free_lock("lock"), is_used_lock("lock"); id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE NULL NULL NULL NULL NULL NULL NULL No tables used diff --git a/mysql-test/r/rpl_timezone.result b/mysql-test/r/rpl_timezone.result new file mode 100644 index 00000000000..c7be3324533 --- /dev/null +++ b/mysql-test/r/rpl_timezone.result @@ -0,0 +1,77 @@ +stop slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +reset master; +reset slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +start slave; +create table t1 (t timestamp); +create table t2 (t char(32)); +select @@time_zone; +@@time_zone +Europe/Moscow +set time_zone='UTC'; +insert into t1 values ('20040101000000'), ('20040611093902'); +select * from t1; +t +2004-01-01 00:00:00 +2004-06-11 09:39:02 +select * from t1; +t +2004-01-01 03:00:00 +2004-06-11 13:39:02 +delete from t1; +set time_zone='Europe/Moscow'; +insert into t1 values ('20040101000000'), ('20040611093902'); +select * from t1; +t +2004-01-01 00:00:00 +2004-06-11 09:39:02 +select * from t1; +t +2004-01-01 00:00:00 +2004-06-11 09:39:02 +show binlog events; +Log_name Pos Event_type Server_id Orig_log_pos Info +master-bin.000001 4 Start 1 4 Server ver: VERSION, Binlog ver: 3 +master-bin.000001 79 Query 1 79 use `test`; create table t1 (t timestamp) +master-bin.000001 143 Query 1 143 use `test`; create table t2 (t char(32)) +master-bin.000001 206 Query 1 206 use `test`; SET ONE_SHOT TIME_ZONE='UTC' +master-bin.000001 269 Query 1 269 use `test`; insert into t1 values ('20040101000000'), ('20040611093902') +master-bin.000001 364 Query 1 364 use `test`; delete from t1 +master-bin.000001 413 Query 1 413 use `test`; insert into t1 values ('20040101000000'), ('20040611093902') +set time_zone='MET'; +insert into t2 (select t from t1); +select * from t1; +t +2003-12-31 22:00:00 +2004-06-11 07:39:02 +select * from t2; +t +2003-12-31 22:00:00 +2004-06-11 07:39:02 +delete from t2; +set timestamp=1000072000; +insert into t2 values (current_timestamp), (current_date), (current_time); +set timestamp=1000072000; +select current_timestamp, current_date, current_time; +current_timestamp current_date current_time +2001-09-10 01:46:40 2001-09-10 01:46:40 +select * from t2; +t +2001-09-09 23:46:40 +2001-09-09 +23:46:40 +delete from t2; +insert into t2 values (from_unixtime(1000000000)), +(unix_timestamp('2001-09-09 03:46:40')); +select * from t2; +t +2001-09-09 03:46:40 +1000000000 +select * from t2; +t +2001-09-09 03:46:40 +1000000000 +set global time_zone='MET'; +ERROR HY000: Binary logging and replication forbid changing of the global server time zone +drop table t1, t2; diff --git a/mysql-test/r/select.result b/mysql-test/r/select.result index 8c783445127..8da1660a109 100644 --- a/mysql-test/r/select.result +++ b/mysql-test/r/select.result @@ -2071,7 +2071,14 @@ CREATE TABLE t1 (gvid int(10) unsigned default NULL, hmid int(10) unsigned defa INSERT INTO t1 VALUES (200001,2,1,1,100,1,1,1,0,0,0,1,0,1,20020425060057,'\\\\ARKIVIO-TESTPDC\\E$',''),(200002,2,2,1,101,1,1,1,0,0,0,1,0,1,20020425060057,'\\\\ARKIVIO-TESTPDC\\C$',''),(200003,1,3,2,NULL,NULL,NULL,NULL,NULL,NULL,NULL,1,0,1,20020425060427,'c:',NULL); CREATE TABLE t2 ( hmid int(10) unsigned default NULL, volid int(10) unsigned default NULL, sampletid smallint(5) unsigned default NULL, sampletime datetime default NULL, samplevalue bigint(20) unsigned default NULL, KEY idx1 (hmid,volid,sampletid,sampletime)) ENGINE=MyISAM; INSERT INTO t2 VALUES (1,3,10,'2002-06-01 08:00:00',35),(1,3,1010,'2002-06-01 12:00:01',35); -SELECT a.gvid, (SUM(CASE b.sampletid WHEN 140 THEN b.samplevalue ELSE 0 END)) as the_success,(SUM(CASE b.sampletid WHEN 141 THEN b.samplevalue ELSE 0 END)) as the_fail,(SUM(CASE b.sampletid WHEN 142 THEN b.samplevalue ELSE 0 END)) as the_size,(SUM(CASE b.sampletid WHEN 143 THEN b.samplevalue ELSE 0 END)) as the_time FROM t1 a, t2 b WHERE a.hmid = b.hmid AND a.volid = b.volid AND b.sampletime >= 'NULL' AND b.sampletime < 'NULL' AND b.sampletid IN (140, 141, 142, 143) GROUP BY a.gvid; +SELECT a.gvid, (SUM(CASE b.sampletid WHEN 140 THEN b.samplevalue ELSE 0 END)) as the_success,(SUM(CASE b.sampletid WHEN 141 THEN b.samplevalue ELSE 0 END)) as the_fail,(SUM(CASE b.sampletid WHEN 142 THEN b.samplevalue ELSE 0 END)) as the_size,(SUM(CASE b.sampletid WHEN 143 THEN b.samplevalue ELSE 0 END)) as the_time FROM t1 a, t2 b WHERE a.hmid = b.hmid AND a.volid = b.volid AND b.sampletime >= 'wrong-date-value' AND b.sampletime < 'wrong-date-value' AND b.sampletid IN (140, 141, 142, 143) GROUP BY a.gvid; +gvid the_success the_fail the_size the_time +Warnings: +Warning 1292 Truncated incorrect datetime value: 'wrong-date-value' +Warning 1292 Truncated incorrect datetime value: 'wrong-date-value' +Warning 1292 Truncated incorrect datetime value: 'wrong-date-value' +Warning 1292 Truncated incorrect datetime value: 'wrong-date-value' +SELECT a.gvid, (SUM(CASE b.sampletid WHEN 140 THEN b.samplevalue ELSE 0 END)) as the_success,(SUM(CASE b.sampletid WHEN 141 THEN b.samplevalue ELSE 0 END)) as the_fail,(SUM(CASE b.sampletid WHEN 142 THEN b.samplevalue ELSE 0 END)) as the_size,(SUM(CASE b.sampletid WHEN 143 THEN b.samplevalue ELSE 0 END)) as the_time FROM t1 a, t2 b WHERE a.hmid = b.hmid AND a.volid = b.volid AND b.sampletime >= NULL AND b.sampletime < NULL AND b.sampletid IN (140, 141, 142, 143) GROUP BY a.gvid; gvid the_success the_fail the_size the_time DROP TABLE t1,t2; create table t1 ( A_Id bigint(20) NOT NULL default '0', A_UpdateBy char(10) NOT NULL default '', A_UpdateDate bigint(20) NOT NULL default '0', A_UpdateSerial int(11) NOT NULL default '0', other_types bigint(20) NOT NULL default '0', wss_type bigint(20) NOT NULL default '0'); diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index 202219d04cf..5ab36dacaaf 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -1075,24 +1075,24 @@ CREATE TABLE t1 SELECT * FROM (SELECT 1 as a,(SELECT 1)) a; SHOW CREATE TABLE t1; Table Create Table t1 CREATE TABLE `t1` ( - `a` bigint(1) NOT NULL default '0', - `(SELECT 1)` bigint(1) NOT NULL default '0' + `a` bigint(20) NOT NULL default '0', + `(SELECT 1)` bigint(20) NOT NULL default '0' ) ENGINE=MyISAM DEFAULT CHARSET=latin1 drop table t1; CREATE TABLE t1 SELECT * FROM (SELECT 1 as a,(SELECT a)) a; SHOW CREATE TABLE t1; Table Create Table t1 CREATE TABLE `t1` ( - `a` bigint(1) NOT NULL default '0', - `(SELECT a)` bigint(1) NOT NULL default '0' + `a` bigint(20) NOT NULL default '0', + `(SELECT a)` bigint(20) NOT NULL default '0' ) ENGINE=MyISAM DEFAULT CHARSET=latin1 drop table t1; CREATE TABLE t1 SELECT * FROM (SELECT 1 as a,(SELECT a+0)) a; SHOW CREATE TABLE t1; Table Create Table t1 CREATE TABLE `t1` ( - `a` bigint(1) NOT NULL default '0', - `(SELECT a+0)` bigint(17) NOT NULL default '0' + `a` bigint(20) NOT NULL default '0', + `(SELECT a+0)` bigint(20) NOT NULL default '0' ) ENGINE=MyISAM DEFAULT CHARSET=latin1 drop table t1; CREATE TABLE t1 SELECT (SELECT 1 as a UNION SELECT 1+1 limit 1,1) as a; @@ -1102,7 +1102,7 @@ a SHOW CREATE TABLE t1; Table Create Table t1 CREATE TABLE `t1` ( - `a` bigint(17) NOT NULL default '0' + `a` bigint(20) NOT NULL default '0' ) ENGINE=MyISAM DEFAULT CHARSET=latin1 drop table t1; create table t1 (a int); @@ -1831,3 +1831,63 @@ Warnings: Note 1276 Field or reference 'up.a' of SELECT #2 was resolved in SELECT #1 Note 1003 select test.up.a AS `a`,test.up.b AS `b` from test.t1 up where exists(select 1 AS `Not_used` from test.t1 where (test.t1.a = test.up.a)) drop table t1; +CREATE TABLE t1 (t1_a int); +INSERT INTO t1 VALUES (1); +CREATE TABLE t2 (t2_a int, t2_b int, PRIMARY KEY (t2_a, t2_b)); +INSERT INTO t2 VALUES (1, 1), (1, 2); +SELECT * FROM t1, t2 table2 WHERE t1_a = 1 AND table2.t2_a = 1 +HAVING table2.t2_b = (SELECT MAX(t2_b) FROM t2 WHERE t2_a = table2.t2_a); +t1_a t2_a t2_b +1 1 2 +DROP TABLE t1, t2; +CREATE TABLE t1 (id int(11) default NULL,name varchar(10) default NULL); +INSERT INTO t1 VALUES (1,'Tim'),(2,'Rebecca'),(3,NULL); +CREATE TABLE t2 (id int(11) default NULL, pet varchar(10) default NULL); +INSERT INTO t2 VALUES (1,'Fido'),(2,'Spot'),(3,'Felix'); +SELECT a.*, b.* FROM (SELECT * FROM t1) AS a JOIN t2 as b on a.id=b.id; +id name id pet +1 Tim 1 Fido +2 Rebecca 2 Spot +3 NULL 3 Felix +drop table t1,t2; +CREATE TABLE t1 ( a int, b int ); +CREATE TABLE t2 ( c int, d int ); +INSERT INTO t1 VALUES (1,2), (2,3), (3,4); +SELECT a AS abc, b FROM t1 WHERE b = (SELECT MIN(b) FROM t1 WHERE a=abc); +abc b +1 2 +2 3 +3 4 +INSERT INTO t2 SELECT a AS abc, b FROM t1 WHERE b = (SELECT MIN(b) FROM t1 WHERE a=abc); +select * from t2; +c d +1 2 +2 3 +3 4 +CREATE TABLE t3 SELECT a AS abc, b FROM t1 WHERE b = (SELECT MIN(b) FROM t1 WHERE a=abc); +select * from t3; +abc b +1 2 +2 3 +3 4 +prepare stmt1 from "INSERT INTO t2 SELECT a AS abc, b FROM t1 WHERE b = (SELECT MIN(b) FROM t1 WHERE a=abc);"; +execute stmt1; +deallocate prepare stmt1; +select * from t2; +c d +1 2 +2 3 +3 4 +1 2 +2 3 +3 4 +drop table t3; +prepare stmt1 from "CREATE TABLE t3 SELECT a AS abc, b FROM t1 WHERE b = (SELECT MIN(b) FROM t1 WHERE a=abc);"; +execute stmt1; +select * from t3; +abc b +1 2 +2 3 +3 4 +deallocate prepare stmt1; +DROP TABLE t1, t2, t3; diff --git a/mysql-test/r/system_mysql_db.result b/mysql-test/r/system_mysql_db.result index d53ace261bf..1f09a20abc5 100644 --- a/mysql-test/r/system_mysql_db.result +++ b/mysql-test/r/system_mysql_db.result @@ -9,6 +9,11 @@ help_relation help_topic host tables_priv +time_zone +time_zone_leap_second +time_zone_name +time_zone_transition +time_zone_transition_type user show create table db; Table Create Table diff --git a/mysql-test/r/timezone.result b/mysql-test/r/timezone.result index 15f0d4121c7..10944c3706e 100644 --- a/mysql-test/r/timezone.result +++ b/mysql-test/r/timezone.result @@ -1,7 +1,7 @@ DROP TABLE IF EXISTS t1; -show variables like "timezone"; +show variables like "system_time_zone"; Variable_name Value -timezone MET +system_time_zone MET select @a:=FROM_UNIXTIME(1); @a:=FROM_UNIXTIME(1) 1970-01-01 01:00:01 @@ -32,6 +32,13 @@ ts from_unixtime(ts) 1048989599 2003-03-30 03:59:59 1048989601 2003-03-30 04:00:01 DROP TABLE t1; +CREATE TABLE t1 (ts timestamp); +INSERT INTO t1 (ts) VALUES ('2003-03-30 01:59:59'), +('2003-03-30 02:59:59'), +('2003-03-30 03:00:00'); +Warnings: +Warning 1299 Invalid TIMESTAMP value in column 'ts' at row 2 +DROP TABLE t1; select unix_timestamp('1970-01-01 01:00:00'), unix_timestamp('1970-01-01 01:00:01'), unix_timestamp('2038-01-01 00:59:59'), diff --git a/mysql-test/r/timezone2.result b/mysql-test/r/timezone2.result new file mode 100644 index 00000000000..5361ff4ffe6 --- /dev/null +++ b/mysql-test/r/timezone2.result @@ -0,0 +1,246 @@ +drop table if exists t1; +create table t1 (ts timestamp); +set time_zone='+00:00'; +select unix_timestamp(utc_timestamp())-unix_timestamp(current_timestamp()); +unix_timestamp(utc_timestamp())-unix_timestamp(current_timestamp()) +0 +insert into t1 (ts) values ('2003-03-30 02:30:00'); +set time_zone='+10:30'; +select unix_timestamp(utc_timestamp())-unix_timestamp(current_timestamp()); +unix_timestamp(utc_timestamp())-unix_timestamp(current_timestamp()) +-37800 +insert into t1 (ts) values ('2003-03-30 02:30:00'); +set time_zone='-10:00'; +select unix_timestamp(utc_timestamp())-unix_timestamp(current_timestamp()); +unix_timestamp(utc_timestamp())-unix_timestamp(current_timestamp()) +36000 +insert into t1 (ts) values ('2003-03-30 02:30:00'); +select * from t1; +ts +2003-03-29 16:30:00 +2003-03-29 06:00:00 +2003-03-30 02:30:00 +drop table t1; +select Name from mysql.time_zone_name where Name in +('UTC','Universal','MET','Europe/Moscow','leap/Europe/Moscow'); +Name +Europe/Moscow +leap/Europe/Moscow +MET +Universal +UTC +create table t1 (i int, ts timestamp); +set time_zone='MET'; +insert into t1 (i, ts) values +(unix_timestamp('2003-03-01 00:00:00'),'2003-03-01 00:00:00'); +insert into t1 (i, ts) values +(unix_timestamp('2003-03-30 01:59:59'),'2003-03-30 01:59:59'), +(unix_timestamp('2003-03-30 02:30:00'),'2003-03-30 02:30:00'), +(unix_timestamp('2003-03-30 03:00:00'),'2003-03-30 03:00:00'); +Warnings: +Warning 1299 Invalid TIMESTAMP value in column 'ts' at row 2 +insert into t1 (i, ts) values +(unix_timestamp('2003-05-01 00:00:00'),'2003-05-01 00:00:00'); +insert into t1 (i, ts) values +(unix_timestamp('2003-10-26 01:00:00'),'2003-10-26 01:00:00'), +(unix_timestamp('2003-10-26 02:00:00'),'2003-10-26 02:00:00'), +(unix_timestamp('2003-10-26 02:59:59'),'2003-10-26 02:59:59'), +(unix_timestamp('2003-10-26 04:00:00'),'2003-10-26 04:00:00'), +(unix_timestamp('2003-10-26 02:59:59'),'2003-10-26 02:59:59'); +set time_zone='UTC'; +select * from t1; +i ts +1046473200 2003-02-28 23:00:00 +1048985999 2003-03-30 00:59:59 +1048986000 2003-03-30 01:00:00 +1048986000 2003-03-30 01:00:00 +1051740000 2003-04-30 22:00:00 +1067122800 2003-10-25 23:00:00 +1067126400 2003-10-26 00:00:00 +1067129999 2003-10-26 00:59:59 +1067137200 2003-10-26 03:00:00 +1067129999 2003-10-26 00:59:59 +delete from t1; +set time_zone='Europe/Moscow'; +insert into t1 (i, ts) values +(unix_timestamp('2004-01-01 00:00:00'),'2004-01-01 00:00:00'), +(unix_timestamp('2004-03-28 02:30:00'),'2004-03-28 02:30:00'), +(unix_timestamp('2004-08-01 00:00:00'),'2003-08-01 00:00:00'), +(unix_timestamp('2004-10-31 02:30:00'),'2004-10-31 02:30:00'); +Warnings: +Warning 1299 Invalid TIMESTAMP value in column 'ts' at row 2 +select * from t1; +i ts +1072904400 2004-01-01 00:00:00 +1080428400 2004-03-28 03:00:00 +1091304000 2003-08-01 00:00:00 +1099175400 2004-10-31 02:30:00 +delete from t1; +set time_zone='leap/Europe/Moscow'; +insert into t1 (i, ts) values +(unix_timestamp('2004-01-01 00:00:00'),'2004-01-01 00:00:00'), +(unix_timestamp('2004-03-28 02:30:00'),'2004-03-28 02:30:00'), +(unix_timestamp('2004-08-01 00:00:00'),'2003-08-01 00:00:00'), +(unix_timestamp('2004-10-31 02:30:00'),'2004-10-31 02:30:00'); +Warnings: +Warning 1299 Invalid TIMESTAMP value in column 'ts' at row 2 +select * from t1; +i ts +1072904422 2004-01-01 00:00:00 +1080428422 2004-03-28 03:00:00 +1091304022 2003-08-01 00:00:00 +1099175422 2004-10-31 02:30:00 +delete from t1; +insert into t1 (i, ts) values +(unix_timestamp('1981-07-01 03:59:59'),'1981-07-01 03:59:59'), +(unix_timestamp('1981-07-01 04:00:00'),'1981-07-01 04:00:00'); +select * from t1; +i ts +362793608 1981-07-01 03:59:59 +362793610 1981-07-01 04:00:00 +select from_unixtime(362793609); +from_unixtime(362793609) +1981-07-01 03:59:60 +drop table t1; +create table t1 (ts timestamp); +set time_zone='UTC'; +insert into t1 values ('0000-00-00 00:00:00'),('1969-12-31 23:59:59'), +('1970-01-01 00:00:00'),('1970-01-01 00:00:01'), +('2037-12-31 23:59:59'),('2038-01-01 00:00:00'); +Warnings: +Warning 1264 Data truncated; out of range for column 'ts' at row 2 +Warning 1264 Data truncated; out of range for column 'ts' at row 3 +Warning 1264 Data truncated; out of range for column 'ts' at row 6 +select * from t1; +ts +0000-00-00 00:00:00 +0000-00-00 00:00:00 +0000-00-00 00:00:00 +1970-01-01 00:00:01 +2037-12-31 23:59:59 +0000-00-00 00:00:00 +delete from t1; +set time_zone='MET'; +insert into t1 values ('0000-00-00 00:00:00'),('1970-01-01 00:30:00'), +('1970-01-01 01:00:00'),('1970-01-01 01:00:01'), +('2038-01-01 00:59:59'),('2038-01-01 01:00:00'); +Warnings: +Warning 1264 Data truncated; out of range for column 'ts' at row 2 +Warning 1264 Data truncated; out of range for column 'ts' at row 3 +Warning 1264 Data truncated; out of range for column 'ts' at row 6 +select * from t1; +ts +0000-00-00 00:00:00 +0000-00-00 00:00:00 +0000-00-00 00:00:00 +1970-01-01 01:00:01 +2038-01-01 00:59:59 +0000-00-00 00:00:00 +delete from t1; +set time_zone='+01:30'; +insert into t1 values ('0000-00-00 00:00:00'),('1970-01-01 01:00:00'), +('1970-01-01 01:30:00'),('1970-01-01 01:30:01'), +('2038-01-01 01:29:59'),('2038-01-01 01:30:00'); +Warnings: +Warning 1264 Data truncated; out of range for column 'ts' at row 2 +Warning 1264 Data truncated; out of range for column 'ts' at row 3 +Warning 1264 Data truncated; out of range for column 'ts' at row 6 +select * from t1; +ts +0000-00-00 00:00:00 +0000-00-00 00:00:00 +0000-00-00 00:00:00 +1970-01-01 01:30:01 +2038-01-01 01:29:59 +0000-00-00 00:00:00 +drop table t1; +show variables like 'time_zone'; +Variable_name Value +time_zone +01:30 +set time_zone = default; +show variables like 'time_zone'; +Variable_name Value +time_zone SYSTEM +set time_zone= '0'; +ERROR HY000: Unknown or incorrect time zone: '0' +set time_zone= '0:0'; +ERROR HY000: Unknown or incorrect time zone: '0:0' +set time_zone= '-20:00'; +ERROR HY000: Unknown or incorrect time zone: '-20:00' +set time_zone= '+20:00'; +ERROR HY000: Unknown or incorrect time zone: '+20:00' +set time_zone= 'Some/Unknown/Time/Zone'; +ERROR HY000: Unknown or incorrect time zone: 'Some/Unknown/Time/Zone' +select convert_tz(now(),'UTC', 'Universal') = now(); +convert_tz(now(),'UTC', 'Universal') = now() +1 +select convert_tz(now(),'utc', 'UTC') = now(); +convert_tz(now(),'utc', 'UTC') = now() +1 +select convert_tz('1917-11-07 12:00:00', 'MET', 'UTC'); +convert_tz('1917-11-07 12:00:00', 'MET', 'UTC') +1917-11-07 12:00:00 +select convert_tz('1970-01-01 01:00:00', 'MET', 'UTC'); +convert_tz('1970-01-01 01:00:00', 'MET', 'UTC') +1970-01-01 01:00:00 +select convert_tz('1970-01-01 01:00:01', 'MET', 'UTC'); +convert_tz('1970-01-01 01:00:01', 'MET', 'UTC') +1970-01-01 00:00:01 +select convert_tz('2003-03-01 00:00:00', 'MET', 'UTC'); +convert_tz('2003-03-01 00:00:00', 'MET', 'UTC') +2003-02-28 23:00:00 +select convert_tz('2003-03-30 01:59:59', 'MET', 'UTC'); +convert_tz('2003-03-30 01:59:59', 'MET', 'UTC') +2003-03-30 00:59:59 +select convert_tz('2003-03-30 02:30:00', 'MET', 'UTC'); +convert_tz('2003-03-30 02:30:00', 'MET', 'UTC') +2003-03-30 01:00:00 +select convert_tz('2003-03-30 03:00:00', 'MET', 'UTC'); +convert_tz('2003-03-30 03:00:00', 'MET', 'UTC') +2003-03-30 01:00:00 +select convert_tz('2003-05-01 00:00:00', 'MET', 'UTC'); +convert_tz('2003-05-01 00:00:00', 'MET', 'UTC') +2003-04-30 22:00:00 +select convert_tz('2003-10-26 01:00:00', 'MET', 'UTC'); +convert_tz('2003-10-26 01:00:00', 'MET', 'UTC') +2003-10-25 23:00:00 +select convert_tz('2003-10-26 02:00:00', 'MET', 'UTC'); +convert_tz('2003-10-26 02:00:00', 'MET', 'UTC') +2003-10-26 00:00:00 +select convert_tz('2003-10-26 02:59:59', 'MET', 'UTC'); +convert_tz('2003-10-26 02:59:59', 'MET', 'UTC') +2003-10-26 00:59:59 +select convert_tz('2003-10-26 04:00:00', 'MET', 'UTC'); +convert_tz('2003-10-26 04:00:00', 'MET', 'UTC') +2003-10-26 03:00:00 +select convert_tz('2038-01-01 00:59:59', 'MET', 'UTC'); +convert_tz('2038-01-01 00:59:59', 'MET', 'UTC') +2037-12-31 23:59:59 +select convert_tz('2038-01-01 01:00:00', 'MET', 'UTC'); +convert_tz('2038-01-01 01:00:00', 'MET', 'UTC') +2038-01-01 01:00:00 +select convert_tz('2103-01-01 04:00:00', 'MET', 'UTC'); +convert_tz('2103-01-01 04:00:00', 'MET', 'UTC') +2103-01-01 04:00:00 +create table t1 (tz varchar(3)); +insert into t1 (tz) values ('MET'), ('UTC'); +select tz, convert_tz('2003-12-31 00:00:00',tz,'UTC'), convert_tz('2003-12-31 00:00:00','UTC',tz) from t1 order by tz; +tz convert_tz('2003-12-31 00:00:00',tz,'UTC') convert_tz('2003-12-31 00:00:00','UTC',tz) +MET 2003-12-30 23:00:00 2003-12-31 01:00:00 +UTC 2003-12-31 00:00:00 2003-12-31 00:00:00 +drop table t1; +select convert_tz('2003-12-31 04:00:00', NULL, 'UTC'); +convert_tz('2003-12-31 04:00:00', NULL, 'UTC') +NULL +select convert_tz('2003-12-31 04:00:00', 'SomeNotExistingTimeZone', 'UTC'); +convert_tz('2003-12-31 04:00:00', 'SomeNotExistingTimeZone', 'UTC') +NULL +select convert_tz('2003-12-31 04:00:00', 'MET', 'SomeNotExistingTimeZone'); +convert_tz('2003-12-31 04:00:00', 'MET', 'SomeNotExistingTimeZone') +NULL +select convert_tz('2003-12-31 04:00:00', 'MET', NULL); +convert_tz('2003-12-31 04:00:00', 'MET', NULL) +NULL +select convert_tz( NULL, 'MET', 'UTC'); +convert_tz( NULL, 'MET', 'UTC') +NULL diff --git a/mysql-test/r/type_date.result b/mysql-test/r/type_date.result index cebd005c2c8..e77ef9f975b 100644 --- a/mysql-test/r/type_date.result +++ b/mysql-test/r/type_date.result @@ -73,3 +73,9 @@ SELECT DATE_FORMAT("2002-03-06 10:11:12", CONCAT('%a, %d %M %Y %H:%i:%s ' , t2. DATE_FORMAT("2002-03-06 10:11:12", CONCAT('%a, %d %M %Y %H:%i:%s ' , t2.GMT)) DATE_FORMAT("2002-03-06 10:11:12", CONCAT('%a, %d %M %Y %H:%i:%s ' , t2.GMT)) Wed, 06 March 2002 10:11:12 GMT-0800 Wed, 06 March 2002 10:11:12 GMT-0800 drop table t1,t2; +CREATE TABLE t1 (f1 time default NULL, f2 time default NULL); +INSERT INTO t1 (f1, f2) VALUES ('09:00', '12:00'); +SELECT DATE_FORMAT(f1, "%l.%i %p") , DATE_FORMAT(f2, "%l.%i %p") FROM t1; +DATE_FORMAT(f1, "%l.%i %p") DATE_FORMAT(f2, "%l.%i %p") +9.00 AM 12.00 PM +DROP TABLE t1; diff --git a/mysql-test/r/type_datetime.result b/mysql-test/r/type_datetime.result index 8e86ce990b1..524bc9c50d4 100644 --- a/mysql-test/r/type_datetime.result +++ b/mysql-test/r/type_datetime.result @@ -1,12 +1,6 @@ drop table if exists t1; create table t1 (t datetime); -insert into t1 values(101),(691231),(700101),(991231),(10000101),(99991231),(101000000),(691231000000),(700101000000),(991231235959),(10000101000000),(99991231235959),(20030102030460),(20030102036301),(20030102240401),(20030132030401),(20031302030460); -Warnings: -Warning 1265 Data truncated for column 't' at row 13 -Warning 1265 Data truncated for column 't' at row 14 -Warning 1265 Data truncated for column 't' at row 15 -Warning 1265 Data truncated for column 't' at row 16 -Warning 1265 Data truncated for column 't' at row 17 +insert into t1 values (101),(691231),(700101),(991231),(10000101),(99991231),(101000000),(691231000000),(700101000000),(991231235959),(10000101000000),(99991231235959),(20030100000000),(20030000000000); select * from t1; t 2000-01-01 00:00:00 @@ -21,11 +15,8 @@ t 1999-12-31 23:59:59 1000-01-01 00:00:00 9999-12-31 23:59:59 -0000-00-00 00:00:00 -0000-00-00 00:00:00 -0000-00-00 00:00:00 -0000-00-00 00:00:00 -0000-00-00 00:00:00 +2003-01-00 00:00:00 +2003-00-00 00:00:00 delete from t1 where t > 0; optimize table t1; Table Op Msg_type Msg_text @@ -34,13 +25,7 @@ check table t1; Table Op Msg_type Msg_text test.t1 check status OK delete from t1; -insert into t1 values("000101"),("691231"),("700101"),("991231"),("00000101"),("00010101"),("99991231"),("00101000000"),("691231000000"),("700101000000"),("991231235959"),("10000101000000"),("99991231235959"),("20030102030460"),("20030102036301"),("20030102240401"),("20030132030401"),("20031302030460"); -Warnings: -Warning 1264 Data truncated; out of range for column 't' at row 14 -Warning 1264 Data truncated; out of range for column 't' at row 15 -Warning 1264 Data truncated; out of range for column 't' at row 16 -Warning 1264 Data truncated; out of range for column 't' at row 17 -Warning 1264 Data truncated; out of range for column 't' at row 18 +insert into t1 values("000101"),("691231"),("700101"),("991231"),("00000101"),("00010101"),("99991231"),("00101000000"),("691231000000"),("700101000000"),("991231235959"),("10000101000000"),("99991231235959"),("20030100000000"),("20030000000000"); select * from t1; t 2000-01-01 00:00:00 @@ -56,11 +41,8 @@ t 1999-12-31 23:59:59 1000-01-01 00:00:00 9999-12-31 23:59:59 -0000-00-00 00:00:00 -0000-00-00 00:00:00 -0000-00-00 00:00:00 -0000-00-00 00:00:00 -0000-00-00 00:00:00 +2003-01-00 00:00:00 +2003-00-00 00:00:00 drop table t1; CREATE TABLE t1 (a timestamp, b date, c time, d datetime); insert into t1 (b,c,d) values(now(),curtime(),now()); @@ -114,3 +96,43 @@ insert into t1 values (now(), now()); select * from t1 where a is null or b is null; a b drop table t1; +create table t1 (t datetime); +insert into t1 values (20030102030460),(20030102036301),(20030102240401),(20030132030401),(20031302030460); +Warnings: +Warning 1265 Data truncated for column 't' at row 1 +Warning 1265 Data truncated for column 't' at row 2 +Warning 1265 Data truncated for column 't' at row 3 +Warning 1265 Data truncated for column 't' at row 4 +Warning 1265 Data truncated for column 't' at row 5 +select * from t1; +t +0000-00-00 00:00:00 +0000-00-00 00:00:00 +0000-00-00 00:00:00 +0000-00-00 00:00:00 +0000-00-00 00:00:00 +delete from t1; +insert into t1 values ("20030102030460"),("20030102036301"),("20030102240401"),("20030132030401"),("20031302030460"); +Warnings: +Warning 1264 Data truncated; out of range for column 't' at row 1 +Warning 1264 Data truncated; out of range for column 't' at row 2 +Warning 1264 Data truncated; out of range for column 't' at row 3 +Warning 1264 Data truncated; out of range for column 't' at row 4 +Warning 1264 Data truncated; out of range for column 't' at row 5 +select * from t1; +t +0000-00-00 00:00:00 +0000-00-00 00:00:00 +0000-00-00 00:00:00 +0000-00-00 00:00:00 +0000-00-00 00:00:00 +delete from t1; +insert into t1 values ("0000-00-00 00:00:00 some trailer"),("2003-01-01 00:00:00 some trailer"); +Warnings: +Warning 1264 Data truncated; out of range for column 't' at row 1 +Warning 1264 Data truncated; out of range for column 't' at row 2 +select * from t1; +t +0000-00-00 00:00:00 +2003-01-01 00:00:00 +drop table t1; diff --git a/mysql-test/r/type_decimal.result b/mysql-test/r/type_decimal.result index e39c4c2b132..a9dcabd121e 100644 --- a/mysql-test/r/type_decimal.result +++ b/mysql-test/r/type_decimal.result @@ -444,6 +444,13 @@ CREATE TABLE t1 (a_dec DECIMAL(-2,1)); ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '-2,1))' at line 1 CREATE TABLE t1 (a_dec DECIMAL(-1,1)); ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '-1,1))' at line 1 +CREATE TABLE t1 (a_dec DECIMAL(0,11)); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a_dec` decimal(12,11) default NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t1; create table t1(a decimal(7,3)); insert into t1 values ('1'),('+1'),('-1'),('0000000001'),('+0000000001'),('-0000000001'),('10'),('+10'),('-10'),('0000000010'),('+0000000010'),('-0000000010'),('100'),('+100'),('-100'),('0000000100'),('+0000000100'),('-0000000100'),('1000'),('+1000'),('-1000'),('0000001000'),('+0000001000'),('-0000001000'),('10000'),('+10000'),('-10000'),('0000010000'),('+0000010000'),('-0000010000'),('100000'),('+100000'),('-100000'),('0000100000'),('+0000100000'),('-0000100000'),('1000000'),('+1000000'),('-1000000'),('0001000000'),('+0001000000'),('-0001000000'),('10000000'),('+10000000'),('-10000000'),('0010000000'),('+0010000000'),('-0010000000'),('100000000'),('+100000000'),('-100000000'),('0100000000'),('+0100000000'),('-0100000000'),('1000000000'),('+1000000000'),('-1000000000'),('1000000000'),('+1000000000'),('-1000000000'); select * from t1; diff --git a/mysql-test/r/type_time.result b/mysql-test/r/type_time.result index c4e1b33bb99..025cf2a57f1 100644 --- a/mysql-test/r/type_time.result +++ b/mysql-test/r/type_time.result @@ -25,11 +25,11 @@ t 36:30:31 insert into t1 values("10.22.22"),(1234567),(123456789),(123456789.10),("10 22:22"),("12.45a"); Warnings: -Note 1292 Truncated incorrect time value: '10.22.22' +Warning 1265 Data truncated for column 't' at row 1 Warning 1264 Data truncated; out of range for column 't' at row 2 Warning 1264 Data truncated; out of range for column 't' at row 3 Warning 1264 Data truncated; out of range for column 't' at row 4 -Note 1292 Truncated incorrect time value: '12.45a' +Warning 1265 Data truncated for column 't' at row 6 select * from t1; t 10:22:33 diff --git a/mysql-test/r/type_timestamp.result b/mysql-test/r/type_timestamp.result index 9a6eac683e0..aa8c0903558 100644 --- a/mysql-test/r/type_timestamp.result +++ b/mysql-test/r/type_timestamp.result @@ -43,13 +43,7 @@ date_format(a,"%Y %y") year(a) year(now()) 1970 70 1970 1970 drop table t1; create table t1 (ix timestamp); -insert into t1 values (19991101000000),(19990102030405),(19990630232922),(19990601000000),(19990930232922),(19990531232922),(19990501000000),(19991101000000),(19990501000000),(20030101010160),(20030101016001),(20030101240101),(20030132010101),(20031301010101); -Warnings: -Warning 1265 Data truncated for column 'ix' at row 10 -Warning 1265 Data truncated for column 'ix' at row 11 -Warning 1265 Data truncated for column 'ix' at row 12 -Warning 1265 Data truncated for column 'ix' at row 13 -Warning 1265 Data truncated for column 'ix' at row 14 +insert into t1 values (19991101000000),(19990102030405),(19990630232922),(19990601000000),(19990930232922),(19990531232922),(19990501000000),(19991101000000),(19990501000000); select ix+0 from t1; ix+0 19991101000000 @@ -61,24 +55,14 @@ ix+0 19990501000000 19991101000000 19990501000000 -0 -0 -0 -0 -0 delete from t1; -insert into t1 values ("19991101000000"),("19990102030405"),("19990630232922"),("19990601000000"),("20030101010160"),("20030101016001"),("20030101240101"),("20030132010101"),("20031301010101"); +insert into t1 values ("19991101000000"),("19990102030405"),("19990630232922"),("19990601000000"); select ix+0 from t1; ix+0 19991101000000 19990102030405 19990630232922 19990601000000 -0 -0 -0 -0 -0 drop table t1; CREATE TABLE t1 (date date, date_time datetime, time_stamp timestamp); INSERT INTO t1 VALUES ("1998-12-31","1998-12-31 23:59:59",19981231235959); @@ -128,6 +112,56 @@ t2 t4 t6 t8 t10 t12 t14 0000-00-00 00:00:00 0000-00-00 00:00:00 0000-00-00 00:00:00 0000-00-00 00:00:00 0000-00-00 00:00:00 0000-00-00 00:00:00 0000-00-00 00:00:00 1997-12-31 23:47:59 1997-12-31 23:47:59 1997-12-31 23:47:59 1997-12-31 23:47:59 1997-12-31 23:47:59 1997-12-31 23:47:59 1997-12-31 23:47:59 drop table t1; +create table t1 (ix timestamp); +insert into t1 values (0),(20030101010160),(20030101016001),(20030101240101),(20030132010101),(20031301010101),(20031200000000),(20030000000000); +Warnings: +Warning 1265 Data truncated for column 'ix' at row 2 +Warning 1265 Data truncated for column 'ix' at row 3 +Warning 1265 Data truncated for column 'ix' at row 4 +Warning 1265 Data truncated for column 'ix' at row 5 +Warning 1265 Data truncated for column 'ix' at row 6 +Warning 1265 Data truncated for column 'ix' at row 7 +Warning 1265 Data truncated for column 'ix' at row 8 +select ix+0 from t1; +ix+0 +0 +0 +0 +0 +0 +0 +0 +0 +delete from t1; +insert into t1 values ("00000000000000"),("20030101010160"),("20030101016001"),("20030101240101"),("20030132010101"),("20031301010101"),("20031200000000"),("20030000000000"); +Warnings: +Warning 1265 Data truncated for column 'ix' at row 2 +Warning 1265 Data truncated for column 'ix' at row 3 +Warning 1265 Data truncated for column 'ix' at row 4 +Warning 1265 Data truncated for column 'ix' at row 5 +Warning 1265 Data truncated for column 'ix' at row 6 +Warning 1265 Data truncated for column 'ix' at row 7 +Warning 1265 Data truncated for column 'ix' at row 8 +select ix+0 from t1; +ix+0 +0 +0 +0 +0 +0 +0 +0 +0 +delete from t1; +insert into t1 values ("0000-00-00 00:00:00 some trailer"),("2003-01-01 00:00:00 some trailer"); +Warnings: +Warning 1265 Data truncated for column 'ix' at row 1 +Warning 1265 Data truncated for column 'ix' at row 2 +select ix+0 from t1; +ix+0 +0 +20030101000000 +drop table t1; create table t1 (t1 timestamp, t2 timestamp default now()); ERROR HY000: Incorrect table definition; there can be only one TIMESTAMP column with CURRENT_TIMESTAMP in DEFAULT or ON UPDATE clause create table t1 (t1 timestamp, t2 timestamp on update now()); diff --git a/mysql-test/r/union.result b/mysql-test/r/union.result index da4915294b1..bed4e890c18 100644 --- a/mysql-test/r/union.result +++ b/mysql-test/r/union.result @@ -546,7 +546,7 @@ aa show create table t1; Table Create Table t1 CREATE TABLE `t1` ( - `a` char(2) NOT NULL default '' + `a` char(20) NOT NULL default '' ) ENGINE=MyISAM DEFAULT CHARSET=latin1 drop table t1; create table t1 SELECT 12 as a UNION select 12.2 as a; @@ -557,7 +557,7 @@ a show create table t1; Table Create Table t1 CREATE TABLE `t1` ( - `a` double(4,1) NOT NULL default '0.0' + `a` double(53,1) NOT NULL default '0.0' ) ENGINE=MyISAM DEFAULT CHARSET=latin1 drop table t1; create table t2 (it1 tinyint, it2 tinyint not null, i int not null, ib bigint, f float, d double, y year, da date, dt datetime, sc char(10), sv varchar(10), b blob, tx text); @@ -647,7 +647,7 @@ f show create table t1; Table Create Table t1 CREATE TABLE `t1` ( - `f` binary(12) default NULL + `f` binary(24) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 drop table t1; create table t1 SELECT y from t2 UNION select da from t2; @@ -795,7 +795,7 @@ select * from t1; show create table t1; Table Create Table t1 CREATE TABLE `t1` ( - `1` bigint(1) NOT NULL default '0' + `1` bigint(20) NOT NULL default '0' ) ENGINE=MyISAM DEFAULT CHARSET=latin1 drop table t1; create table t1 select _latin1"test" union select _latin2"testt" ; @@ -953,3 +953,10 @@ CREATE TABLE t2 (i int(11) default NULL,c char(1) default NULL,KEY i (i)); explain (select * from t1) union (select * from t2) order by not_existing_column; ERROR 42S22: Unknown column 'not_existing_column' in 'order clause' drop table t1, t2; +CREATE TABLE t1 (uid int(1)); +INSERT INTO t1 SELECT 150; +SELECT 'a' UNION SELECT uid FROM t1; +a +a +150 +drop table t1; diff --git a/mysql-test/t/auto_increment.test b/mysql-test/t/auto_increment.test index 99a75889431..73588a91aac 100644 --- a/mysql-test/t/auto_increment.test +++ b/mysql-test/t/auto_increment.test @@ -3,6 +3,7 @@ # --disable_warnings drop table if exists t1; +drop table if exists t2; --enable_warnings SET SQL_WARNINGS=1; diff --git a/mysql-test/t/delete.test b/mysql-test/t/delete.test index e370b545eff..56c8ce77627 100644 --- a/mysql-test/t/delete.test +++ b/mysql-test/t/delete.test @@ -98,3 +98,14 @@ select * from t11; delete ignore from t11 where t11.b <> (select b from t2 where t11.a < t2.a); select * from t11; drop table t11, t12, t2; + +# +# Bug #4198: deletion and KEYREAD +# + +create table t1 (a int, b int, unique key (a), key (b)); +insert into t1 values (3, 3), (7, 7); +delete t1 from t1 where a = 3; +check table t1; +select * from t1; +drop table t1; diff --git a/mysql-test/t/func_gconcat.test b/mysql-test/t/func_gconcat.test index 5ddc93e767b..62343fa2af8 100644 --- a/mysql-test/t/func_gconcat.test +++ b/mysql-test/t/func_gconcat.test @@ -61,7 +61,7 @@ select grp,group_concat(c order by c) from t1 group by grp; # Test warnings -set group_concat_max_len = 5; +set group_concat_max_len = 4; select grp,group_concat(c) from t1 group by grp; show warnings; set group_concat_max_len = 1024; diff --git a/mysql-test/t/func_if.test b/mysql-test/t/func_if.test index 36c3a38d2fe..7048d188604 100644 --- a/mysql-test/t/func_if.test +++ b/mysql-test/t/func_if.test @@ -47,3 +47,12 @@ create table t1 (x int, y int); insert into t1 values (0,6),(10,16),(20,26),(30,10),(40,46),(50,56); select min(if(y -x > 5,y,NULL)), max(if(y - x > 5,y,NULL)) from t1; drop table t1; + +# +# BUG#3987 +# +create table t1 (a int); +insert t1 values (1),(2); +select if(1>2,a,avg(a)) from t1; +drop table t1; + diff --git a/mysql-test/t/func_like.test b/mysql-test/t/func_like.test index 91f55af48cc..ad83202afa0 100644 --- a/mysql-test/t/func_like.test +++ b/mysql-test/t/func_like.test @@ -25,14 +25,23 @@ select * from t1 where a like "%abc\d%"; drop table t1; +create table t1 (a varchar(10), key(a)); + # # Bug #2231 # - -create table t1 (a varchar(10), key(a)); insert into t1 values ('a'), ('a\\b'); select * from t1 where a like 'a\\%' escape '#'; select * from t1 where a like 'a\\%' escape '#' and a like 'a\\\\b'; + +# +# Bug #4200: Prepared statement parameter as argument to ESCAPE +# +prepare stmt1 from 'select * from t1 where a like \'a\\%\' escape ?'; +set @esc='#'; +execute stmt1 using @esc; +deallocate prepare stmt1; + drop table t1; # diff --git a/mysql-test/t/func_regexp.test b/mysql-test/t/func_regexp.test index edfa9afcfa6..1a771d466fa 100644 --- a/mysql-test/t/func_regexp.test +++ b/mysql-test/t/func_regexp.test @@ -60,3 +60,16 @@ select _koi8r 0xF7 regexp _koi8r '[[:alpha:]]'; select _latin1'a' regexp _latin1'A' collate latin1_general_ci; select _latin1'a' regexp _latin1'A' collate latin1_bin; + +# +# regexp cleanup() +# +create table t1 (a varchar(40)); +insert into t1 values ('C1'),('C2'),('R1'),('C3'),('R2'),('R3'); +prepare stmt1 from 'select a from t1 where a rlike ? order by a'; +set @a="^C.*"; +execute stmt1 using @a; +set @a="^R.*"; +execute stmt1 using @a; +deallocate prepare stmt1; +drop table t1; diff --git a/mysql-test/t/func_time.test b/mysql-test/t/func_time.test index a85f9d563e9..67192c55ef9 100644 --- a/mysql-test/t/func_time.test +++ b/mysql-test/t/func_time.test @@ -255,8 +255,6 @@ select date_add(date,INTERVAL "1 1" YEAR_MONTH) from t1; select date_add(date,INTERVAL "1:1:1" HOUR_SECOND) from t1; select date_add(date,INTERVAL "1 1:1" DAY_MINUTE) from t1; select date_add(date,INTERVAL "1 1:1:1" DAY_SECOND) from t1; - -# The following is not as one would expect... select date_add(time,INTERVAL 1 SECOND) from t1; drop table t1; diff --git a/mysql-test/t/ps.test b/mysql-test/t/ps.test index d9e0f0852c5..8881d6b9eec 100644 --- a/mysql-test/t/ps.test +++ b/mysql-test/t/ps.test @@ -116,4 +116,27 @@ prepare stmt1 from @str2; execute stmt1 using @ivar; drop table t1,t2; +# +# Bug #4105: Server crash on attempt to prepare a statement with character +# set introducer +# +PREPARE stmt1 FROM "select _utf8 'A' collate utf8_bin = ?"; +set @var='A'; +EXECUTE stmt1 USING @var; +DEALLOCATE PREPARE stmt1; +# +# BUG#3486: FOUND_ROWS() fails inside stored procedure [and prepared statement] +# +create table t1 (id int); +prepare stmt1 from "select FOUND_ROWS()"; +select SQL_CALC_FOUND_ROWS * from t1; +# Expect 0 +execute stmt1; +insert into t1 values (1); +select SQL_CALC_FOUND_ROWS * from t1; +# Expect 1 +execute stmt1; +# Expect 0 +execute stmt1; +deallocate prepare stmt1; diff --git a/mysql-test/t/range.test b/mysql-test/t/range.test index 4665cd02ed8..471af8e4a5b 100644 --- a/mysql-test/t/range.test +++ b/mysql-test/t/range.test @@ -255,7 +255,34 @@ WHERE ); DROP TABLE t1; +# +# Test of problem with IN on many different keyparts. (Bug #4157) +# + +CREATE TABLE t1 ( +id int( 11 ) unsigned NOT NULL AUTO_INCREMENT , +line int( 5 ) unsigned NOT NULL default '0', +columnid int( 3 ) unsigned NOT NULL default '0', +owner int( 3 ) unsigned NOT NULL default '0', +ordinal int( 3 ) unsigned NOT NULL default '0', +showid smallint( 6 ) unsigned NOT NULL default '1', +tableid int( 1 ) unsigned NOT NULL default '1', +content int( 5 ) unsigned NOT NULL default '188', +PRIMARY KEY ( owner, id ) , +KEY menu( owner, showid, columnid ) , +KEY `COLUMN` ( owner, columnid, line ) , +KEY `LINES` ( owner, tableid, content, id ) , +KEY recount( owner, line ) +) ENGINE = MYISAM; + +INSERT into t1 (owner,id,columnid,line) values (11,15,15,1),(11,13,13,5); + +SELECT id, columnid, tableid, content, showid, line, ordinal FROM t1 WHERE owner=11 AND ((columnid IN ( 15, 13, 14 ) AND line IN ( 1, 2, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 31 )) OR (columnid IN ( 13, 14 ) AND line IN ( 15 ))) LIMIT 0 , 30; +drop table t1; + +# # test for a bug with in() and unique key +# create table t1 (id int(10) primary key); insert into t1 values (1),(2),(3),(4),(5),(6),(7),(8),(9); diff --git a/mysql-test/t/rpl_free_items-slave.opt b/mysql-test/t/rpl_free_items-slave.opt new file mode 100644 index 00000000000..b828d03fafb --- /dev/null +++ b/mysql-test/t/rpl_free_items-slave.opt @@ -0,0 +1 @@ +--replicate-wild-ignore-table=test.% diff --git a/mysql-test/t/rpl_free_items.test b/mysql-test/t/rpl_free_items.test new file mode 100644 index 00000000000..3228ffd9cde --- /dev/null +++ b/mysql-test/t/rpl_free_items.test @@ -0,0 +1,20 @@ +source include/master-slave.inc; +create table t1 (a int); +create table t2 (a int); +disable_query_log; +SET @query="INSERT INTO t2 SELECT * FROM t1 WHERE a REGEXP \"0\""; +let $1 = 2000; +while ($1) +{ + eval SET @query=concat(@query, " OR a REGEXP '$1'"); + dec $1; +} +let $1=`select @query`; +eval $1; +enable_query_log; +# I have seen the slave crash either now or at shutdown +sync_slave_with_master; +connection master; +drop table t1; +drop table t2; +sync_slave_with_master; diff --git a/mysql-test/t/rpl_get_lock.test b/mysql-test/t/rpl_get_lock.test index c3b033fb03a..5e58753e59a 100644 --- a/mysql-test/t/rpl_get_lock.test +++ b/mysql-test/t/rpl_get_lock.test @@ -22,7 +22,7 @@ connection slave; sync_with_master; select get_lock("lock",3); select * from t1; -select is_free_lock("lock"), is_used_lock("lock"); +select is_free_lock("lock"), is_used_lock("lock") = connection_id(); explain extended select is_free_lock("lock"), is_used_lock("lock"); # Check lock functions select is_free_lock("lock2"); diff --git a/mysql-test/t/rpl_timezone-master.opt b/mysql-test/t/rpl_timezone-master.opt new file mode 100644 index 00000000000..8e43bfbbb7e --- /dev/null +++ b/mysql-test/t/rpl_timezone-master.opt @@ -0,0 +1 @@ +--default-time-zone=Europe/Moscow diff --git a/mysql-test/t/rpl_timezone-slave.opt b/mysql-test/t/rpl_timezone-slave.opt new file mode 100644 index 00000000000..8e43bfbbb7e --- /dev/null +++ b/mysql-test/t/rpl_timezone-slave.opt @@ -0,0 +1 @@ +--default-time-zone=Europe/Moscow diff --git a/mysql-test/t/rpl_timezone.test b/mysql-test/t/rpl_timezone.test new file mode 100644 index 00000000000..8dff90a84cf --- /dev/null +++ b/mysql-test/t/rpl_timezone.test @@ -0,0 +1,84 @@ +# Test of replication of time zones. +source include/master-slave.inc; + +# Some preparations +let $VERSION=`select version()`; +create table t1 (t timestamp); +create table t2 (t char(32)); + +# +# Let us check how well replication works when we are saving datetime +# value in TIMESTAMP field. +# +connection master; +select @@time_zone; +set time_zone='UTC'; +insert into t1 values ('20040101000000'), ('20040611093902'); +select * from t1; +# On slave we still in 'Europe/Moscow' so we should see equivalent but +# textually different values. +sync_slave_with_master; +select * from t1; + +# Let us check also that setting of time_zone back to default also works +# well +connection master; +delete from t1; +set time_zone='Europe/Moscow'; +insert into t1 values ('20040101000000'), ('20040611093902'); +select * from t1; +sync_slave_with_master; +select * from t1; +connection master; +# We should not see SET ONE_SHOT time_zone before second insert +--replace_result $VERSION VERSION +show binlog events; + +# +# Now let us check how well we replicate statments reading TIMESTAMP fields +# (We should see the same data on master and on slave but it should differ +# from originally inserted) +# +set time_zone='MET'; +insert into t2 (select t from t1); +select * from t1; +sync_slave_with_master; +select * from t2; + +# +# Now let us check how well we replicate various CURRENT_* functions +# +connection master; +delete from t2; +set timestamp=1000072000; +insert into t2 values (current_timestamp), (current_date), (current_time); +sync_slave_with_master; +# Values in ouput of these to queries should differ because we are in +# in 'MET' on master and in 'Europe/Moscow on slave... +set timestamp=1000072000; +select current_timestamp, current_date, current_time; +select * from t2; + +# +# At last let us check replication of FROM_UNIXTIME/UNIX_TIMESTAMP functions. +# +connection master; +delete from t2; +insert into t2 values (from_unixtime(1000000000)), + (unix_timestamp('2001-09-09 03:46:40')); +select * from t2; +sync_slave_with_master; +# We should get same result on slave as on master +select * from t2; + +# +# Let us check that we are not allowing to set global time_zone with +# replication +# +connection master; +--error 1105 +set global time_zone='MET'; + +# Clean up +drop table t1, t2; +sync_slave_with_master; diff --git a/mysql-test/t/select.test b/mysql-test/t/select.test index 0a3de178456..57827f3cc7f 100644 --- a/mysql-test/t/select.test +++ b/mysql-test/t/select.test @@ -1771,7 +1771,9 @@ CREATE TABLE t1 (gvid int(10) unsigned default NULL, hmid int(10) unsigned defa INSERT INTO t1 VALUES (200001,2,1,1,100,1,1,1,0,0,0,1,0,1,20020425060057,'\\\\ARKIVIO-TESTPDC\\E$',''),(200002,2,2,1,101,1,1,1,0,0,0,1,0,1,20020425060057,'\\\\ARKIVIO-TESTPDC\\C$',''),(200003,1,3,2,NULL,NULL,NULL,NULL,NULL,NULL,NULL,1,0,1,20020425060427,'c:',NULL); CREATE TABLE t2 ( hmid int(10) unsigned default NULL, volid int(10) unsigned default NULL, sampletid smallint(5) unsigned default NULL, sampletime datetime default NULL, samplevalue bigint(20) unsigned default NULL, KEY idx1 (hmid,volid,sampletid,sampletime)) ENGINE=MyISAM; INSERT INTO t2 VALUES (1,3,10,'2002-06-01 08:00:00',35),(1,3,1010,'2002-06-01 12:00:01',35); -SELECT a.gvid, (SUM(CASE b.sampletid WHEN 140 THEN b.samplevalue ELSE 0 END)) as the_success,(SUM(CASE b.sampletid WHEN 141 THEN b.samplevalue ELSE 0 END)) as the_fail,(SUM(CASE b.sampletid WHEN 142 THEN b.samplevalue ELSE 0 END)) as the_size,(SUM(CASE b.sampletid WHEN 143 THEN b.samplevalue ELSE 0 END)) as the_time FROM t1 a, t2 b WHERE a.hmid = b.hmid AND a.volid = b.volid AND b.sampletime >= 'NULL' AND b.sampletime < 'NULL' AND b.sampletid IN (140, 141, 142, 143) GROUP BY a.gvid; +SELECT a.gvid, (SUM(CASE b.sampletid WHEN 140 THEN b.samplevalue ELSE 0 END)) as the_success,(SUM(CASE b.sampletid WHEN 141 THEN b.samplevalue ELSE 0 END)) as the_fail,(SUM(CASE b.sampletid WHEN 142 THEN b.samplevalue ELSE 0 END)) as the_size,(SUM(CASE b.sampletid WHEN 143 THEN b.samplevalue ELSE 0 END)) as the_time FROM t1 a, t2 b WHERE a.hmid = b.hmid AND a.volid = b.volid AND b.sampletime >= 'wrong-date-value' AND b.sampletime < 'wrong-date-value' AND b.sampletid IN (140, 141, 142, 143) GROUP BY a.gvid; +# Testing the same select with NULL's instead of invalid datetime values +SELECT a.gvid, (SUM(CASE b.sampletid WHEN 140 THEN b.samplevalue ELSE 0 END)) as the_success,(SUM(CASE b.sampletid WHEN 141 THEN b.samplevalue ELSE 0 END)) as the_fail,(SUM(CASE b.sampletid WHEN 142 THEN b.samplevalue ELSE 0 END)) as the_size,(SUM(CASE b.sampletid WHEN 143 THEN b.samplevalue ELSE 0 END)) as the_time FROM t1 a, t2 b WHERE a.hmid = b.hmid AND a.volid = b.volid AND b.sampletime >= NULL AND b.sampletime < NULL AND b.sampletid IN (140, 141, 142, 143) GROUP BY a.gvid; DROP TABLE t1,t2; # diff --git a/mysql-test/t/subselect.test b/mysql-test/t/subselect.test index 4b2fd33abfd..0c093c4ae3e 100644 --- a/mysql-test/t/subselect.test +++ b/mysql-test/t/subselect.test @@ -1167,3 +1167,48 @@ insert into t1 values (1,2),(3,4); select * from t1 up where exists (select * from t1 where t1.a=up.a); explain extended select * from t1 up where exists (select * from t1 where t1.a=up.a); drop table t1; + +# +# Bug #4102: subselect in HAVING +# + +CREATE TABLE t1 (t1_a int); +INSERT INTO t1 VALUES (1); +CREATE TABLE t2 (t2_a int, t2_b int, PRIMARY KEY (t2_a, t2_b)); +INSERT INTO t2 VALUES (1, 1), (1, 2); +SELECT * FROM t1, t2 table2 WHERE t1_a = 1 AND table2.t2_a = 1 + HAVING table2.t2_b = (SELECT MAX(t2_b) FROM t2 WHERE t2_a = table2.t2_a); +DROP TABLE t1, t2; + +# +# Test problem with NULL and derived tables (Bug #4097) +# + +CREATE TABLE t1 (id int(11) default NULL,name varchar(10) default NULL); +INSERT INTO t1 VALUES (1,'Tim'),(2,'Rebecca'),(3,NULL); +CREATE TABLE t2 (id int(11) default NULL, pet varchar(10) default NULL); +INSERT INTO t2 VALUES (1,'Fido'),(2,'Spot'),(3,'Felix'); +SELECT a.*, b.* FROM (SELECT * FROM t1) AS a JOIN t2 as b on a.id=b.id; +drop table t1,t2; + +# +# outer fields resolving in INSERT/REPLACE and CRETE with SELECT +# +CREATE TABLE t1 ( a int, b int ); +CREATE TABLE t2 ( c int, d int ); +INSERT INTO t1 VALUES (1,2), (2,3), (3,4); +SELECT a AS abc, b FROM t1 WHERE b = (SELECT MIN(b) FROM t1 WHERE a=abc); +INSERT INTO t2 SELECT a AS abc, b FROM t1 WHERE b = (SELECT MIN(b) FROM t1 WHERE a=abc); +select * from t2; +CREATE TABLE t3 SELECT a AS abc, b FROM t1 WHERE b = (SELECT MIN(b) FROM t1 WHERE a=abc); +select * from t3; +prepare stmt1 from "INSERT INTO t2 SELECT a AS abc, b FROM t1 WHERE b = (SELECT MIN(b) FROM t1 WHERE a=abc);"; +execute stmt1; +deallocate prepare stmt1; +select * from t2; +drop table t3; +prepare stmt1 from "CREATE TABLE t3 SELECT a AS abc, b FROM t1 WHERE b = (SELECT MIN(b) FROM t1 WHERE a=abc);"; +execute stmt1; +select * from t3; +deallocate prepare stmt1; +DROP TABLE t1, t2, t3; diff --git a/mysql-test/t/timezone.test b/mysql-test/t/timezone.test index 194602f376c..ffc2e3a3ebf 100644 --- a/mysql-test/t/timezone.test +++ b/mysql-test/t/timezone.test @@ -1,5 +1,6 @@ # -# Test of timezone handling. This script must be run with TZ=MET +# Test of SYSTEM time zone handling ( for my_system_gmt_sec()). +# This script must be run with TZ=MET -- require r/have_met_timezone.require disable_query_log; @@ -13,7 +14,7 @@ DROP TABLE IF EXISTS t1; # The following is because of daylight saving time --replace_result MEST MET -show variables like "timezone"; +show variables like "system_time_zone"; # # Test unix timestamp @@ -40,6 +41,16 @@ INSERT INTO t1 (ts) VALUES (Unix_timestamp('2003-03-30 04:00:01')); SELECT ts,from_unixtime(ts) FROM t1; DROP TABLE t1; + +# +# Test of warning for spring time-gap values for system time zone +# +CREATE TABLE t1 (ts timestamp); +INSERT INTO t1 (ts) VALUES ('2003-03-30 01:59:59'), + ('2003-03-30 02:59:59'), + ('2003-03-30 03:00:00'); +DROP TABLE t1; + # # Test for fix for Bug#2523 # diff --git a/mysql-test/t/timezone2.test b/mysql-test/t/timezone2.test new file mode 100644 index 00000000000..49579421570 --- /dev/null +++ b/mysql-test/t/timezone2.test @@ -0,0 +1,189 @@ +# This script tests our own time zone support functions + +# Preparing playground +--disable_warnings +drop table if exists t1; +--enable_warnings + + +# +# Let us first check +HH:MM style timezones +# +create table t1 (ts timestamp); + +set time_zone='+00:00'; +select unix_timestamp(utc_timestamp())-unix_timestamp(current_timestamp()); +insert into t1 (ts) values ('2003-03-30 02:30:00'); + +set time_zone='+10:30'; +select unix_timestamp(utc_timestamp())-unix_timestamp(current_timestamp()); +insert into t1 (ts) values ('2003-03-30 02:30:00'); + +set time_zone='-10:00'; +select unix_timestamp(utc_timestamp())-unix_timestamp(current_timestamp()); +insert into t1 (ts) values ('2003-03-30 02:30:00'); + +# Here we will get different results +select * from t1; + +drop table t1; + + +# +# Let us try DB specified time zones +# +select Name from mysql.time_zone_name where Name in + ('UTC','Universal','MET','Europe/Moscow','leap/Europe/Moscow'); + +create table t1 (i int, ts timestamp); + +set time_zone='MET'; + +# We check common date time value and non existent or ambiguios values +# Normal value without DST +insert into t1 (i, ts) values + (unix_timestamp('2003-03-01 00:00:00'),'2003-03-01 00:00:00'); +# Values around and in spring time-gap +insert into t1 (i, ts) values + (unix_timestamp('2003-03-30 01:59:59'),'2003-03-30 01:59:59'), + (unix_timestamp('2003-03-30 02:30:00'),'2003-03-30 02:30:00'), + (unix_timestamp('2003-03-30 03:00:00'),'2003-03-30 03:00:00'); +# Normal value with DST +insert into t1 (i, ts) values + (unix_timestamp('2003-05-01 00:00:00'),'2003-05-01 00:00:00'); +# Ambiguos values (also check for determenism) +insert into t1 (i, ts) values + (unix_timestamp('2003-10-26 01:00:00'),'2003-10-26 01:00:00'), + (unix_timestamp('2003-10-26 02:00:00'),'2003-10-26 02:00:00'), + (unix_timestamp('2003-10-26 02:59:59'),'2003-10-26 02:59:59'), + (unix_timestamp('2003-10-26 04:00:00'),'2003-10-26 04:00:00'), + (unix_timestamp('2003-10-26 02:59:59'),'2003-10-26 02:59:59'); + +set time_zone='UTC'; + +select * from t1; + +delete from t1; + +# Simple check for 'Europe/Moscow' time zone just for showing that it works +set time_zone='Europe/Moscow'; +insert into t1 (i, ts) values + (unix_timestamp('2004-01-01 00:00:00'),'2004-01-01 00:00:00'), + (unix_timestamp('2004-03-28 02:30:00'),'2004-03-28 02:30:00'), + (unix_timestamp('2004-08-01 00:00:00'),'2003-08-01 00:00:00'), + (unix_timestamp('2004-10-31 02:30:00'),'2004-10-31 02:30:00'); +select * from t1; +delete from t1; + + +# +# Check for time zone with leap seconds +# Values in ts column must be the same but values in i column should +# differ from corresponding values for Europe/Moscow a bit. +# +set time_zone='leap/Europe/Moscow'; +insert into t1 (i, ts) values + (unix_timestamp('2004-01-01 00:00:00'),'2004-01-01 00:00:00'), + (unix_timestamp('2004-03-28 02:30:00'),'2004-03-28 02:30:00'), + (unix_timestamp('2004-08-01 00:00:00'),'2003-08-01 00:00:00'), + (unix_timestamp('2004-10-31 02:30:00'),'2004-10-31 02:30:00'); +select * from t1; +delete from t1; +# Let us test leap jump +insert into t1 (i, ts) values + (unix_timestamp('1981-07-01 03:59:59'),'1981-07-01 03:59:59'), + (unix_timestamp('1981-07-01 04:00:00'),'1981-07-01 04:00:00'); +select * from t1; +# Additional 60ieth second! +select from_unixtime(362793609); + +drop table t1; + + +# +# Let us test range for TIMESTAMP +# +create table t1 (ts timestamp); +set time_zone='UTC'; +insert into t1 values ('0000-00-00 00:00:00'),('1969-12-31 23:59:59'), + ('1970-01-01 00:00:00'),('1970-01-01 00:00:01'), + ('2037-12-31 23:59:59'),('2038-01-01 00:00:00'); +select * from t1; +delete from t1; +# MET time zone has range shifted by one hour +set time_zone='MET'; +insert into t1 values ('0000-00-00 00:00:00'),('1970-01-01 00:30:00'), + ('1970-01-01 01:00:00'),('1970-01-01 01:00:01'), + ('2038-01-01 00:59:59'),('2038-01-01 01:00:00'); +select * from t1; +delete from t1; +# same for +01:30 time zone +set time_zone='+01:30'; +insert into t1 values ('0000-00-00 00:00:00'),('1970-01-01 01:00:00'), + ('1970-01-01 01:30:00'),('1970-01-01 01:30:01'), + ('2038-01-01 01:29:59'),('2038-01-01 01:30:00'); +select * from t1; + +drop table t1; + + +# +# Test of show variables +# +show variables like 'time_zone'; +set time_zone = default; +show variables like 'time_zone'; + + +# +# Let us try some invalid time zone specifications +# +--error 1298 +set time_zone= '0'; +--error 1298 +set time_zone= '0:0'; +--error 1298 +set time_zone= '-20:00'; +--error 1298 +set time_zone= '+20:00'; +--error 1298 +set time_zone= 'Some/Unknown/Time/Zone'; + + +# Let us check that aliases for time zones work and they are +# case-insensitive +select convert_tz(now(),'UTC', 'Universal') = now(); +select convert_tz(now(),'utc', 'UTC') = now(); + + +# +# Let us test CONVERT_TZ function (may be func_time.test is better place). +# +select convert_tz('1917-11-07 12:00:00', 'MET', 'UTC'); +select convert_tz('1970-01-01 01:00:00', 'MET', 'UTC'); +select convert_tz('1970-01-01 01:00:01', 'MET', 'UTC'); +select convert_tz('2003-03-01 00:00:00', 'MET', 'UTC'); +select convert_tz('2003-03-30 01:59:59', 'MET', 'UTC'); +select convert_tz('2003-03-30 02:30:00', 'MET', 'UTC'); +select convert_tz('2003-03-30 03:00:00', 'MET', 'UTC'); +select convert_tz('2003-05-01 00:00:00', 'MET', 'UTC'); +select convert_tz('2003-10-26 01:00:00', 'MET', 'UTC'); +select convert_tz('2003-10-26 02:00:00', 'MET', 'UTC'); +select convert_tz('2003-10-26 02:59:59', 'MET', 'UTC'); +select convert_tz('2003-10-26 04:00:00', 'MET', 'UTC'); +select convert_tz('2038-01-01 00:59:59', 'MET', 'UTC'); +select convert_tz('2038-01-01 01:00:00', 'MET', 'UTC'); +select convert_tz('2103-01-01 04:00:00', 'MET', 'UTC'); + +# Let us test variable time zone argument +create table t1 (tz varchar(3)); +insert into t1 (tz) values ('MET'), ('UTC'); +select tz, convert_tz('2003-12-31 00:00:00',tz,'UTC'), convert_tz('2003-12-31 00:00:00','UTC',tz) from t1 order by tz; +drop table t1; + +# Parameters to CONVERT_TZ() what should give NULL +select convert_tz('2003-12-31 04:00:00', NULL, 'UTC'); +select convert_tz('2003-12-31 04:00:00', 'SomeNotExistingTimeZone', 'UTC'); +select convert_tz('2003-12-31 04:00:00', 'MET', 'SomeNotExistingTimeZone'); +select convert_tz('2003-12-31 04:00:00', 'MET', NULL); +select convert_tz( NULL, 'MET', 'UTC'); diff --git a/mysql-test/t/type_date.test b/mysql-test/t/type_date.test index 5a53c635e6e..8d67802d42a 100644 --- a/mysql-test/t/type_date.test +++ b/mysql-test/t/type_date.test @@ -79,3 +79,12 @@ SELECT DATE_FORMAT("2002-03-06 10:11:12", CONCAT('%a, %d %M %Y %H:%i:%s ' , t2. INSERT INTO t1 VALUES(1); SELECT DATE_FORMAT("2002-03-06 10:11:12", CONCAT('%a, %d %M %Y %H:%i:%s ' , t2.GMT)), DATE_FORMAT("2002-03-06 10:11:12", CONCAT('%a, %d %M %Y %H:%i:%s ' , t2.GMT)) FROM t1,t2 GROUP BY t1.AFIELD; drop table t1,t2; + +# +# Multiple SELECT DATE_FORMAT gave incorrect results (Bug #4036) +# + +CREATE TABLE t1 (f1 time default NULL, f2 time default NULL); +INSERT INTO t1 (f1, f2) VALUES ('09:00', '12:00'); +SELECT DATE_FORMAT(f1, "%l.%i %p") , DATE_FORMAT(f2, "%l.%i %p") FROM t1; +DROP TABLE t1; diff --git a/mysql-test/t/type_datetime.test b/mysql-test/t/type_datetime.test index cec2aa3582b..47866058524 100644 --- a/mysql-test/t/type_datetime.test +++ b/mysql-test/t/type_datetime.test @@ -7,13 +7,13 @@ drop table if exists t1; --enable_warnings create table t1 (t datetime); -insert into t1 values(101),(691231),(700101),(991231),(10000101),(99991231),(101000000),(691231000000),(700101000000),(991231235959),(10000101000000),(99991231235959),(20030102030460),(20030102036301),(20030102240401),(20030132030401),(20031302030460); +insert into t1 values (101),(691231),(700101),(991231),(10000101),(99991231),(101000000),(691231000000),(700101000000),(991231235959),(10000101000000),(99991231235959),(20030100000000),(20030000000000); select * from t1; delete from t1 where t > 0; optimize table t1; check table t1; delete from t1; -insert into t1 values("000101"),("691231"),("700101"),("991231"),("00000101"),("00010101"),("99991231"),("00101000000"),("691231000000"),("700101000000"),("991231235959"),("10000101000000"),("99991231235959"),("20030102030460"),("20030102036301"),("20030102240401"),("20030132030401"),("20031302030460"); +insert into t1 values("000101"),("691231"),("700101"),("991231"),("00000101"),("00010101"),("99991231"),("00101000000"),("691231000000"),("700101000000"),("991231235959"),("10000101000000"),("99991231235959"),("20030100000000"),("20030000000000"); select * from t1; drop table t1; @@ -71,3 +71,18 @@ insert into t1 values (now(), now()); insert into t1 values (now(), now()); select * from t1 where a is null or b is null; drop table t1; + +# +# Let us check if we properly treat wrong datetimes and produce proper +# warnings (for both strings and numbers) +# +create table t1 (t datetime); +insert into t1 values (20030102030460),(20030102036301),(20030102240401),(20030132030401),(20031302030460); +select * from t1; +delete from t1; +insert into t1 values ("20030102030460"),("20030102036301"),("20030102240401"),("20030132030401"),("20031302030460"); +select * from t1; +delete from t1; +insert into t1 values ("0000-00-00 00:00:00 some trailer"),("2003-01-01 00:00:00 some trailer"); +select * from t1; +drop table t1; diff --git a/mysql-test/t/type_decimal.test b/mysql-test/t/type_decimal.test index 3257002ae2c..414a06deaa9 100644 --- a/mysql-test/t/type_decimal.test +++ b/mysql-test/t/type_decimal.test @@ -244,6 +244,9 @@ CREATE TABLE t1 (a_dec DECIMAL(-1,0)); CREATE TABLE t1 (a_dec DECIMAL(-2,1)); --error 1064 CREATE TABLE t1 (a_dec DECIMAL(-1,1)); +CREATE TABLE t1 (a_dec DECIMAL(0,11)); +SHOW CREATE TABLE t1; +DROP TABLE t1; # # Zero prepend overflow bug diff --git a/mysql-test/t/type_timestamp.test b/mysql-test/t/type_timestamp.test index 72633f9ef7d..9b3abc9f155 100644 --- a/mysql-test/t/type_timestamp.test +++ b/mysql-test/t/type_timestamp.test @@ -37,10 +37,10 @@ select date_format(a,"%Y %y"),year(a),year(now()) from t1; drop table t1; create table t1 (ix timestamp); -insert into t1 values (19991101000000),(19990102030405),(19990630232922),(19990601000000),(19990930232922),(19990531232922),(19990501000000),(19991101000000),(19990501000000),(20030101010160),(20030101016001),(20030101240101),(20030132010101),(20031301010101); +insert into t1 values (19991101000000),(19990102030405),(19990630232922),(19990601000000),(19990930232922),(19990531232922),(19990501000000),(19991101000000),(19990501000000); select ix+0 from t1; delete from t1; -insert into t1 values ("19991101000000"),("19990102030405"),("19990630232922"),("19990601000000"),("20030101010160"),("20030101016001"),("20030101240101"),("20030132010101"),("20031301010101"); +insert into t1 values ("19991101000000"),("19990102030405"),("19990630232922"),("19990601000000"); select ix+0 from t1; drop table t1; @@ -76,6 +76,21 @@ select * from t1; drop table t1; # +# Let us check if we properly treat wrong datetimes and produce proper warnings +# (for both strings and numbers) +# +create table t1 (ix timestamp); +insert into t1 values (0),(20030101010160),(20030101016001),(20030101240101),(20030132010101),(20031301010101),(20031200000000),(20030000000000); +select ix+0 from t1; +delete from t1; +insert into t1 values ("00000000000000"),("20030101010160"),("20030101016001"),("20030101240101"),("20030132010101"),("20031301010101"),("20031200000000"),("20030000000000"); +select ix+0 from t1; +delete from t1; +insert into t1 values ("0000-00-00 00:00:00 some trailer"),("2003-01-01 00:00:00 some trailer"); +select ix+0 from t1; +drop table t1; + +# # Test for TIMESTAMP column with default now() and on update now() clauses # diff --git a/mysql-test/t/union.test b/mysql-test/t/union.test index f6006a87d47..b21f635162f 100644 --- a/mysql-test/t/union.test +++ b/mysql-test/t/union.test @@ -527,3 +527,11 @@ CREATE TABLE t2 (i int(11) default NULL,c char(1) default NULL,KEY i (i)); --error 1054 explain (select * from t1) union (select * from t2) order by not_existing_column; drop table t1, t2; + +# +# length detecting +# +CREATE TABLE t1 (uid int(1)); +INSERT INTO t1 SELECT 150; +SELECT 'a' UNION SELECT uid FROM t1; +drop table t1; diff --git a/mysys/charset.c b/mysys/charset.c index 72f102a2296..1388fc40c6d 100644 --- a/mysys/charset.c +++ b/mysys/charset.c @@ -469,7 +469,6 @@ static CHARSET_INFO *get_internal_charset(uint cs_number, myf flags) } cs= (cs->state & MY_CS_AVAILABLE) ? cs : NULL; } - pthread_mutex_unlock(&THR_LOCK_charset); if (cs && !(cs->state & MY_CS_READY)) { if ((cs->cset->init && cs->cset->init(cs, cs_alloc)) || @@ -478,6 +477,7 @@ static CHARSET_INFO *get_internal_charset(uint cs_number, myf flags) else cs->state|= MY_CS_READY; } + pthread_mutex_unlock(&THR_LOCK_charset); return cs; } diff --git a/mysys/mf_tempfile.c b/mysys/mf_tempfile.c index e2ad71654dc..0b337a74c19 100644 --- a/mysys/mf_tempfile.c +++ b/mysys/mf_tempfile.c @@ -83,7 +83,7 @@ File create_temp_file(char *to, const char *dir, const char *prefix, (*free)(res); file=my_create(to, 0, mode, MyFlags); } -#elif defined(HAVE_MKSTEMP) +#elif defined(HAVE_MKSTEMP) && !defined(__NETWARE__) { char prefix_buff[30]; uint pfx_len; diff --git a/ndb/bin/mysqlcluster b/ndb/bin/mysqlcluster deleted file mode 100755 index 81fc7308942..00000000000 --- a/ndb/bin/mysqlcluster +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/sh -if [ -z "$MYSQLCLUSTER_TOP" -o ! -d "$MYSQLCLUSTER_TOP" ]; then - echo "MYSQLCLUSTER_TOP not set or directory does not exist" - exit 1 -fi -if [ -z "$MYSQLCLUSTER_TOP" -o ! -d "$MYSQLCLUSTER_TOP/ndb" ]; then - echo "$MYSQLCLUSTER_TOP/ndb directory does not exist" - exit 1 -fi - -mysql --socket=$MYSQLCLUSTER_TOP/data/mysqlcluster.sock $* diff --git a/ndb/bin/mysqlcluster_install_db b/ndb/bin/mysqlcluster_install_db deleted file mode 100755 index 6fe95ff105d..00000000000 --- a/ndb/bin/mysqlcluster_install_db +++ /dev/null @@ -1,119 +0,0 @@ -#!/bin/sh - -NDB_HOME= -export NDB_CONNECTSTRING -if [ -z "$MYSQLCLUSTER_TOP" ]; then - echo "MYSQLCLUSTER_TOP not set" - exit 1 -fi -if [ -d "$MYSQLCLUSTER_TOP" ]; then :; else - echo "$MYSQLCLUSTER_TOP directory does not exist" - exit 1 -fi -if [ -d "$MYSQLCLUSTER_TOP/ndb" ]; then :; else - echo "$MYSQLCLUSTER_TOP/ndb directory does not exist" - exit 1 -fi - -start_default_ndbcluster() { - -# configurable parameters, make sure to change in mysqlcluterd as well -MYSQLCLUSTER_FILESYSTEM=$MYSQLCLUSTER_TOP/data/mysqlclusterfs -MYSQLCLUSTER_PORT_BASE="22" # using ports MYSQLCLUSTER_PORT_BASE{"00","01", etc} -# end configurable parameters - -# do some checks - -NDB_CONNECTSTRING= - -[ -d "$MYSQLCLUSTER_FILESYSTEM" ] || mkdir "$MYSQLCLUSTER_FILESYSTEM" -if [ -d "$MYSQLCLUSTER_FILESYSTEM" ]; then :; else - echo "$MYSQLCLUSTER_FILESYSTEM filesystem directory does not exist" - exit 1 -fi - - -# set som help variables - -NDB_HOST="localhost" -NDB_PORT=$MYSQLCLUSTER_PORT_BASE"00" -NDB_CONNECTSTRING_BASE="host=$NDB_HOST:$NDB_PORT;nodeid=" - - -# Edit file system path and ports in config file - -cd $MYSQLCLUSTER_FILESYSTEM -sed \ - -e s,"WRITE_PATH_TO_FILESYSTEM_2_HERE",$MYSQLCLUSTER_FILESYSTEM,g \ - -e s,"CHOOSE_PORT_BASE",$MYSQLCLUSTER_PORT_BASE,g \ - < $MYSQLCLUSTER_TOP/ndb/demos/config-templates/config_template-install.ini \ - > config.ini - - -# Start management server as deamon - -NDB_ID="1" -NDB_CONNECTSTRING=$NDB_CONNECTSTRING_BASE$NDB_ID -#xterm -e mgmtsrvr -c $MYSQLCLUSTER_FILESYSTEM/config.ini & -if mgmtsrvr -d -c $MYSQLCLUSTER_FILESYSTEM/config.ini ; then :; else - echo "Unable to start mgmtsrvr" - exit 1 -fi - - -# Start database node - -cd $MYSQLCLUSTER_FILESYSTEM # the output from the database node gets where it starts -NDB_ID="2" -NDB_CONNECTSTRING=$NDB_CONNECTSTRING_BASE$NDB_ID -#xterm -T "NDB Cluster DB Node" -geometry 80x10 -xrm *.hold:true -e ndb -i & -ndb -d -i & - -# Start xterm for application programs - -NDB_ID="3" -NDB_CONNECTSTRING=$NDB_CONNECTSTRING_BASE$NDB_ID -#xterm -T "NDB Cluster API Node" -geometry 80x10 & -echo set before running ndbApi programs > export NDB_CONNECTSTRING=$NDB_CONNECTSTRING - -# Start management client - -#xterm -T "NDB Management Client" -geometry 80x10 -xrm *.hold:true -e mgmtclient $NDB_HOST $NDB_PORT & -echo "NDB Management Client starts with: mgmtclient $NDB_HOST $NDB_PORT" - -# test if Ndb Cluster starts properly - -NDB_ID="11" -NDB_CONNECTSTRING=$NDB_CONNECTSTRING_BASE$NDB_ID -if list_tables | grep "NDBT_ProgramExit: 0 - OK"; then :; else - echo "Ndbcluster startup failed" - exit 1 -fi -} - -start_mysql_install_db() { - # run install of regular MySQL Server - - cd $MYSQLCLUSTER_TOP - scripts/mysql_install_db --basedir=$MYSQLCLUSTER_TOP --datadir=$MYSQLCLUSTER_TOP/data --socket=$MYSQLCLUSTER_TOP/data/mysqlcluster.sock $* -} - -if test "$1" = "ndb_started" -then - shift - mgmt_host=$1 - shift - mgmt_port=$1 - shift - if [ -z "$mgmt_host" -o -z "$mgmt_port" ]; then - echo "syntax: ndb_started hostname port" - exit 1 - fi - NDB_CONNECTSTRING="host=$mgmt_host:$mgmt_port;nodeid=11" - echo using NDB_CONNECTSTRING=$NDB_CONNECTSTRING - start_mysql_install_db $* -else - start_default_ndbcluster - start_mysql_install_db -fi - diff --git a/ndb/bin/mysqlclusterd b/ndb/bin/mysqlclusterd deleted file mode 100755 index 3b4deb3ed48..00000000000 --- a/ndb/bin/mysqlclusterd +++ /dev/null @@ -1,34 +0,0 @@ -#!/bin/sh - -# configurable parameters -MYSQLCLUSTER_PORT_BASE="22" -# end configurable parameters - -if [ -z "$MYSQLCLUSTER_TOP" -o ! -d "$MYSQLCLUSTER_TOP" ]; then - echo "MYSQLCLUSTER_TOP not set or directory does not exist" - exit 1 -fi -if [ -z "$MYSQLCLUSTER_TOP" -o ! -d "$MYSQLCLUSTER_TOP/ndb" ]; then - echo "$MYSQLCLUSTER_TOP/ndb directory does not exist" - exit 1 -fi - -if test "$1" = "ndb_started" -then - shift - mgmt_host=$1 - shift - mgmt_port=$1 - shift - if [ -z "$mgmt_host" -o -z "$mgmt_port" ]; then - echo "syntax: ndb_started hostname port" - exit 1 - fi - NDB_CONNECTSTRING="host=$mgmt_host:$mgmt_port;nodeid=11" - echo using NDB_CONNECTSTRING=$NDB_CONNECTSTRING -else - NDB_CONNECTSTRING="host=localhost:"$MYSQLCLUSTER_PORT_BASE"00;nodeid=11" -fi -export NDB_CONNECTSTRING - -mysqld --default-table-type=ndbcluster --basedir=$MYSQLCLUSTER_TOP --datadir=$MYSQLCLUSTER_TOP/data --socket=$MYSQLCLUSTER_TOP/data/mysqlcluster.sock $* diff --git a/ndb/config/common.mk.am b/ndb/config/common.mk.am index 593e00d8959..869e2fae91d 100644 --- a/ndb/config/common.mk.am +++ b/ndb/config/common.mk.am @@ -9,5 +9,4 @@ mgmapiincludedir = "$(pkgincludedir)/ndb/mgmapi" INCLUDES = $(INCLUDES_LOC) LDADD = $(top_srcdir)/ndb/src/common/portlib/gcc.cpp $(LDADD_LOC) DEFS = @DEFS@ @NDB_DEFS@ $(DEFS_LOC) $(NDB_EXTRA_FLAGS) -# ndb cannot be compiled with -fno-implicit-templaces -NDB_CXXFLAGS=-fimplicit-templates +NDB_CXXFLAGS=@ndb_cxxflags_fix@ $(NDB_CXXFLAGS_LOC) diff --git a/ndb/include/Makefile.am b/ndb/include/Makefile.am index e4c82cc161f..2565a78238b 100644 --- a/ndb/include/Makefile.am +++ b/ndb/include/Makefile.am @@ -16,6 +16,7 @@ ndbapi/NdbError.hpp \ ndbapi/NdbEventOperation.hpp \ ndbapi/NdbIndexOperation.hpp \ ndbapi/NdbOperation.hpp \ +ndbapi/NdbBlob.hpp \ ndbapi/NdbPool.hpp \ ndbapi/NdbRecAttr.hpp \ ndbapi/NdbReceiver.hpp \ diff --git a/ndb/include/kernel/signaldata/TupAccess.hpp b/ndb/include/kernel/signaldata/TupAccess.hpp index 5cfb8c0d153..ab56a73322c 100644 --- a/ndb/include/kernel/signaldata/TupAccess.hpp +++ b/ndb/include/kernel/signaldata/TupAccess.hpp @@ -129,6 +129,8 @@ private: /* * Operate on entire tuple. Used by TUX where the table has a single * Uint32 array attribute representing an index tree node. + * + * XXX this signal is no longer used by TUX and can be removed */ class TupStoreTh { friend class Dbtup; diff --git a/ndb/include/kernel/signaldata/TupFrag.hpp b/ndb/include/kernel/signaldata/TupFrag.hpp index ffde2217893..fc88dacd48f 100644 --- a/ndb/include/kernel/signaldata/TupFrag.hpp +++ b/ndb/include/kernel/signaldata/TupFrag.hpp @@ -69,7 +69,7 @@ class TuxFragReq { friend class Dblqh; friend class Dbtux; public: - STATIC_CONST( SignalLength = 9 ); + STATIC_CONST( SignalLength = 14 ); private: Uint32 userPtr; Uint32 userRef; @@ -80,6 +80,9 @@ private: Uint32 fragOff; Uint32 tableType; Uint32 primaryTableId; + Uint32 tupIndexFragPtrI; + Uint32 tupTableFragPtrI[2]; + Uint32 accTableFragPtrI[2]; }; class TuxFragConf { diff --git a/ndb/include/kernel/signaldata/TuxMaint.hpp b/ndb/include/kernel/signaldata/TuxMaint.hpp index 44deb33be80..9fee031dc41 100644 --- a/ndb/include/kernel/signaldata/TuxMaint.hpp +++ b/ndb/include/kernel/signaldata/TuxMaint.hpp @@ -39,7 +39,7 @@ public: SearchError = 895, // add + found or remove + not found NoMemError = 827 }; - STATIC_CONST( SignalLength = 7 ); + STATIC_CONST( SignalLength = 8 ); private: /* * Error code set by TUX. Zero means no error. @@ -52,10 +52,11 @@ private: Uint32 indexId; Uint32 fragId; /* - * Tuple version identified by logical address of "original" tuple and - * version number. + * Tuple version identified by physical address of "original" tuple + * and version number. */ - Uint32 tupAddr; + Uint32 pageId; + Uint32 pageOffset; Uint32 tupVersion; /* * Operation code and flags. diff --git a/ndb/include/ndb_global.h b/ndb/include/ndb_global.h index bd1e4954f14..f871acbc075 100644 --- a/ndb/include/ndb_global.h +++ b/ndb/include/ndb_global.h @@ -25,7 +25,13 @@ #endif #include <sys/param.h> #ifdef HAVE_SYS_STAT_H -#include <sys/stat.h> + #if defined(__cplusplus) && defined(_APP32_64BIT_OFF_T) && defined(_INCLUDE_AES_SOURCE) + #undef _INCLUDE_AES_SOURCE + #include <sys/stat.h> + #define _INCLUDE_AES_SOURCE + #else + #include <sys/stat.h> + #endif #endif #include <sys/resource.h> #ifdef HAVE_SYS_WAIT_H diff --git a/ndb/include/ndb_types.h b/ndb/include/ndb_types.h index 5e7b952cfc5..87ebd3d6c6b 100644 --- a/ndb/include/ndb_types.h +++ b/ndb/include/ndb_types.h @@ -33,7 +33,7 @@ typedef unsigned int UintR; #ifdef __SIZE_TYPE__ typedef __SIZE_TYPE__ UintPtr; #else -#include <my_config.h> +#include <my_global.h> #ifdef HAVE_STDINT_H #include <stdint.h> #endif diff --git a/ndb/include/util/BaseString.hpp b/ndb/include/util/BaseString.hpp index 75a1c291594..8755c13e9bb 100644 --- a/ndb/include/util/BaseString.hpp +++ b/ndb/include/util/BaseString.hpp @@ -203,13 +203,13 @@ BaseString::empty() const inline void BaseString::ndb_toupper() { for(unsigned i = 0; i < length(); i++) - m_chr[i] = ::toupper(m_chr[i]); + m_chr[i] = toupper(m_chr[i]); } inline void BaseString::ndb_tolower() { for(unsigned i = 0; i < length(); i++) - m_chr[i] = ::tolower(m_chr[i]); + m_chr[i] = tolower(m_chr[i]); } inline bool diff --git a/ndb/include/util/NdbSqlUtil.hpp b/ndb/include/util/NdbSqlUtil.hpp index dae12bd11a0..841da513d4a 100644 --- a/ndb/include/util/NdbSqlUtil.hpp +++ b/ndb/include/util/NdbSqlUtil.hpp @@ -150,7 +150,6 @@ NdbSqlUtil::cmp(Uint32 typeId, const Uint32* p1, const Uint32* p2, Uint32 full, } return CmpUnknown; } - break; case Type::Tinyunsigned: { if (size >= 1) { @@ -165,7 +164,6 @@ NdbSqlUtil::cmp(Uint32 typeId, const Uint32* p1, const Uint32* p2, Uint32 full, } return CmpUnknown; } - break; case Type::Smallint: { if (size >= 1) { @@ -180,7 +178,6 @@ NdbSqlUtil::cmp(Uint32 typeId, const Uint32* p1, const Uint32* p2, Uint32 full, } return CmpUnknown; } - break; case Type::Smallunsigned: { if (size >= 1) { @@ -195,7 +192,6 @@ NdbSqlUtil::cmp(Uint32 typeId, const Uint32* p1, const Uint32* p2, Uint32 full, } return CmpUnknown; } - break; case Type::Mediumint: // XXX fix these break; case Type::Mediumunsigned: @@ -214,7 +210,6 @@ NdbSqlUtil::cmp(Uint32 typeId, const Uint32* p1, const Uint32* p2, Uint32 full, } return CmpUnknown; } - break; case Type::Unsigned: { if (size >= 1) { @@ -229,7 +224,6 @@ NdbSqlUtil::cmp(Uint32 typeId, const Uint32* p1, const Uint32* p2, Uint32 full, } return CmpUnknown; } - break; case Type::Bigint: { if (size >= 2) { @@ -246,7 +240,6 @@ NdbSqlUtil::cmp(Uint32 typeId, const Uint32* p1, const Uint32* p2, Uint32 full, } return CmpUnknown; } - break; case Type::Bigunsigned: { if (size >= 2) { @@ -263,7 +256,6 @@ NdbSqlUtil::cmp(Uint32 typeId, const Uint32* p1, const Uint32* p2, Uint32 full, } return CmpUnknown; } - break; case Type::Float: { if (size >= 1) { @@ -278,7 +270,6 @@ NdbSqlUtil::cmp(Uint32 typeId, const Uint32* p1, const Uint32* p2, Uint32 full, } return CmpUnknown; } - break; case Type::Double: { if (size >= 2) { @@ -295,7 +286,6 @@ NdbSqlUtil::cmp(Uint32 typeId, const Uint32* p1, const Uint32* p2, Uint32 full, } return CmpUnknown; } - break; case Type::Decimal: break; case Type::Char: @@ -310,7 +300,6 @@ NdbSqlUtil::cmp(Uint32 typeId, const Uint32* p1, const Uint32* p2, Uint32 full, int k = memcmp(u1.v, u2.v, size << 2); return k < 0 ? -1 : k > 0 ? +1 : full == size ? 0 : CmpUnknown; } - break; case Type::Varchar: { /* @@ -328,7 +317,6 @@ NdbSqlUtil::cmp(Uint32 typeId, const Uint32* p1, const Uint32* p2, Uint32 full, } return CmpUnknown; } - break; case Type::Binary: // XXX fix these break; case Type::Varbinary: @@ -352,7 +340,6 @@ NdbSqlUtil::cmp(Uint32 typeId, const Uint32* p1, const Uint32* p2, Uint32 full, } return CmpUnknown; } - break; case Type::Timespec: // XXX fix this break; case Type::Blob: // XXX fix @@ -371,7 +358,6 @@ NdbSqlUtil::cmp(Uint32 typeId, const Uint32* p1, const Uint32* p2, Uint32 full, } return CmpUnknown; } - break; } return CmpError; } diff --git a/ndb/src/common/debugger/signaldata/BackupImpl.cpp b/ndb/src/common/debugger/signaldata/BackupImpl.cpp index be9e43e3df1..bdc34d614cf 100644 --- a/ndb/src/common/debugger/signaldata/BackupImpl.cpp +++ b/ndb/src/common/debugger/signaldata/BackupImpl.cpp @@ -24,7 +24,7 @@ printDEFINE_BACKUP_REQ(FILE * out, const Uint32 * data, Uint32 len, Uint16 bno){ sig->backupPtr, sig->backupId, sig->clientRef, sig->clientData); fprintf(out, " backupKey: [ %08x%08x ] DataLength: %d\n", sig->backupKey[0], sig->backupKey[1], sig->backupDataLen); - char buf[sig->nodes.TextLength + 1]; + char buf[_NDB_NODE_BITMASK_SIZE * 8 + 1]; fprintf(out, " Nodes: %s\n", sig->nodes.getText(buf)); return true; } diff --git a/ndb/src/common/debugger/signaldata/CreateTrig.cpp b/ndb/src/common/debugger/signaldata/CreateTrig.cpp index d8360dec4d5..ddd45080cba 100644 --- a/ndb/src/common/debugger/signaldata/CreateTrig.cpp +++ b/ndb/src/common/debugger/signaldata/CreateTrig.cpp @@ -87,7 +87,7 @@ bool printCREATE_TRIG_REQ(FILE * output, const Uint32 * theData, Uint32 len, Uin fprintf(output, "Monitor all attributes: %s ", (sig->getMonitorAllAttributes())?"true":"false"); const AttributeMask& attributeMask = sig->getAttributeMask(); - char buf[attributeMask.TextLength + 1]; + char buf[MAXNROFATTRIBUTESINWORDS * 8 + 1]; fprintf(output, "Attribute mask: %s", attributeMask.getText(buf)); fprintf(output, "\n"); diff --git a/ndb/src/common/debugger/signaldata/FsReadWriteReq.cpp b/ndb/src/common/debugger/signaldata/FsReadWriteReq.cpp index ad9cb623c17..9b32fab87ba 100644 --- a/ndb/src/common/debugger/signaldata/FsReadWriteReq.cpp +++ b/ndb/src/common/debugger/signaldata/FsReadWriteReq.cpp @@ -59,10 +59,10 @@ printFSREADWRITEREQ(FILE * output, const Uint32 * theData, sig->numberOfPages); fprintf(output, " pageData: "); - + unsigned int i; switch(sig->getFormatFlag(sig->operationFlag)){ case FsReadWriteReq::fsFormatListOfPairs: - for (unsigned int i = 0; i < sig->numberOfPages*2; i += 2){ + for (i= 0; i < sig->numberOfPages*2; i += 2){ fprintf(output, " H\'%.8x, H\'%.8x\n", sig->data.pageData[i], sig->data.pageData[i + 1]); } @@ -72,7 +72,7 @@ printFSREADWRITEREQ(FILE * output, const Uint32 * theData, sig->data.pageData[1]); break; case FsReadWriteReq::fsFormatListOfMemPages: - for (unsigned int i = 0; i < (sig->numberOfPages + 1); i++){ + for (i= 0; i < (sig->numberOfPages + 1); i++){ fprintf(output, " H\'%.8x, ", sig->data.pageData[i]); } break; diff --git a/ndb/src/common/debugger/signaldata/TuxMaint.cpp b/ndb/src/common/debugger/signaldata/TuxMaint.cpp index 06ac475382c..ba6a299b77d 100644 --- a/ndb/src/common/debugger/signaldata/TuxMaint.cpp +++ b/ndb/src/common/debugger/signaldata/TuxMaint.cpp @@ -24,10 +24,10 @@ printTUX_MAINT_REQ(FILE* output, const Uint32* theData, Uint32 len, Uint16 rbn) //const bool inOut = rbn & (1 << 15); const TuxMaintReq* const sig = (const TuxMaintReq*)theData; fprintf(output, " errorCode=%d\n", sig->errorCode); - fprintf(output, " table: id=%d", sig->tableId); - fprintf(output, " index: id=%d", sig->indexId); - fprintf(output, " fragment: id=%d\n", sig->fragId); - fprintf(output, " tuple: addr=0x%x version=%d\n", sig->tupAddr, sig->tupVersion); + fprintf(output, " table: id=%u", sig->tableId); + fprintf(output, " index: id=%u", sig->indexId); + fprintf(output, " fragment: id=%u\n", sig->fragId); + fprintf(output, " tuple: loc=%u.%u version=%u\n", sig->pageId, sig->pageOffset, sig->tupVersion); const Uint32 opCode = sig->opInfo & 0xFF; const Uint32 opFlag = sig->opInfo >> 8; switch (opCode ) { diff --git a/ndb/src/common/mgmcommon/ConfigInfo.cpp b/ndb/src/common/mgmcommon/ConfigInfo.cpp index c3e10dd3448..c2b5fdabf01 100644 --- a/ndb/src/common/mgmcommon/ConfigInfo.cpp +++ b/ndb/src/common/mgmcommon/ConfigInfo.cpp @@ -143,6 +143,19 @@ ConfigInfo::m_SectionRules[] = { }; const int ConfigInfo::m_NoOfRules = sizeof(m_SectionRules)/sizeof(SectionRule); +/**************************************************************************** + * Config Rules declarations + ****************************************************************************/ +bool addNodeConnections(Vector<ConfigInfo::ConfigRuleSection>§ions, + struct InitConfigFileParser::Context &ctx, + const char * ruleData); + +const ConfigInfo::ConfigRule +ConfigInfo::m_ConfigRules[] = { + { addNodeConnections, 0 }, + { 0, 0 } +}; + struct DepricationTransform { const char * m_section; const char * m_oldName; @@ -1525,7 +1538,7 @@ const ConfigInfo::ParamInfo ConfigInfo::m_ParamInfo[] = { ConfigInfo::USED, false, ConfigInfo::INT, - MANDATORY, + 2202, 0, 0x7FFFFFFF }, @@ -2712,13 +2725,13 @@ checkMandatory(InitConfigFileParser::Context & ctx, const char * data){ * Transform a string "NodeidX" (e.g. "uppsala.32") * into a Uint32 "NodeIdX" (e.g. 32) and a string "SystemX" (e.g. "uppsala"). */ -bool fixNodeId(InitConfigFileParser::Context & ctx, const char * data){ - +bool fixNodeId(InitConfigFileParser::Context & ctx, const char * data) +{ char buf[] = "NodeIdX"; buf[6] = data[sizeof("NodeI")]; char sysbuf[] = "SystemX"; sysbuf[6] = data[sizeof("NodeI")]; const char* nodeId; require(ctx.m_currentSection->get(buf, &nodeId)); - + char tmpLine[MAX_LINE_LENGTH]; strncpy(tmpLine, nodeId, MAX_LINE_LENGTH); char* token1 = strtok(tmpLine, "."); @@ -2739,7 +2752,6 @@ bool fixNodeId(InitConfigFileParser::Context & ctx, const char * data){ require(ctx.m_currentSection->put(buf, id, true)); require(ctx.m_currentSection->put(sysbuf, token1)); } - return true; } @@ -2862,9 +2874,9 @@ fixPortNumber(InitConfigFileParser::Context & ctx, const char * data){ Uint32 adder = 0; ctx.m_userProperties.get("PortNumberAdder", &adder); Uint32 base = 0; - if(!ctx.m_userDefaults->get("PortNumber", &base) && + if(!(ctx.m_userDefaults && ctx.m_userDefaults->get("PortNumber", &base)) && !ctx.m_systemDefaults->get("PortNumber", &base)){ - return true; + return false; } ctx.m_currentSection->put("PortNumber", base + adder); adder++; @@ -3144,3 +3156,88 @@ saveInConfigValues(InitConfigFileParser::Context & ctx, const char * data){ } while(0); return true; } + +bool +addNodeConnections(Vector<ConfigInfo::ConfigRuleSection>§ions, + struct InitConfigFileParser::Context &ctx, + const char * ruleData) +{ + Properties * props= ctx.m_config; + Properties p_connections; + Properties p_connections2; + + for (Uint32 i = 0;; i++){ + const Properties * tmp; + Uint32 nodeId1, nodeId2; + + if(!props->get("Connection", i, &tmp)) break; + + if(!tmp->get("NodeId1", &nodeId1)) continue; + p_connections.put("", nodeId1, nodeId1); + if(!tmp->get("NodeId2", &nodeId2)) continue; + p_connections.put("", nodeId2, nodeId2); + + p_connections2.put("", nodeId1 + nodeId2<<16, nodeId1); + p_connections2.put("", nodeId2 + nodeId1<<16, nodeId2); + } + + Uint32 nNodes; + ctx.m_userProperties.get("NoOfNodes", &nNodes); + + Properties p_db_nodes; + Properties p_api_mgm_nodes; + + Uint32 i_db= 0, i_api_mgm= 0; + for (Uint32 i= 0, n= 0; n < nNodes; i++){ + const Properties * tmp; + if(!props->get("Node", i, &tmp)) continue; + n++; + + const char * type; + if(!tmp->get("Type", &type)) continue; + + if (strcmp(type,"DB") == 0) + p_db_nodes.put("", i_db++, i); + else if (strcmp(type,"API") == 0 || + strcmp(type,"MGM") == 0) + p_api_mgm_nodes.put("", i_api_mgm++, i); + } + + Uint32 nodeId1, nodeId2, dummy; + + for (Uint32 i= 0; p_db_nodes.get("", i, &nodeId1); i++){ + for (Uint32 j= i+1;; j++){ + if(!p_db_nodes.get("", j, &nodeId2)) break; + if(!p_connections2.get("", nodeId1+nodeId2<<16, &dummy)) { + ConfigInfo::ConfigRuleSection s; + s.m_sectionType= BaseString("TCP"); + s.m_sectionData= new Properties; + char buf[16]; + snprintf(buf, sizeof(buf), "%u", nodeId1); + s.m_sectionData->put("NodeId1", buf); + snprintf(buf, sizeof(buf), "%u", nodeId2); + s.m_sectionData->put("NodeId2", buf); + sections.push_back(s); + } + } + } + + for (Uint32 i= 0; p_api_mgm_nodes.get("", i, &nodeId1); i++){ + if(!p_connections.get("", nodeId1, &dummy)) { + for (Uint32 j= 0;; j++){ + if(!p_db_nodes.get("", j, &nodeId2)) break; + ConfigInfo::ConfigRuleSection s; + s.m_sectionType= BaseString("TCP"); + s.m_sectionData= new Properties; + char buf[16]; + snprintf(buf, sizeof(buf), "%u", nodeId1); + s.m_sectionData->put("NodeId1", buf); + snprintf(buf, sizeof(buf), "%u", nodeId2); + s.m_sectionData->put("NodeId2", buf); + sections.push_back(s); + } + } + } + + return true; +} diff --git a/ndb/src/common/mgmcommon/ConfigInfo.hpp b/ndb/src/common/mgmcommon/ConfigInfo.hpp index b5e011ed398..79c17b436fe 100644 --- a/ndb/src/common/mgmcommon/ConfigInfo.hpp +++ b/ndb/src/common/mgmcommon/ConfigInfo.hpp @@ -71,6 +71,21 @@ public: const char * m_ruleData; }; + /** + * Entry for config rule + */ + struct ConfigRuleSection { + BaseString m_sectionType; + Properties * m_sectionData; + }; + + struct ConfigRule { + bool (* m_configRule)(Vector<ConfigRuleSection>&, + struct InitConfigFileParser::Context &, + const char * m_ruleData); + const char * m_ruleData; + }; + ConfigInfo(); /** @@ -113,6 +128,7 @@ private: public: static const SectionRule m_SectionRules[]; + static const ConfigRule m_ConfigRules[]; static const int m_NoOfRules; }; diff --git a/ndb/src/common/mgmcommon/InitConfigFileParser.cpp b/ndb/src/common/mgmcommon/InitConfigFileParser.cpp index f4a8e1a8cd4..d52bc54db52 100644 --- a/ndb/src/common/mgmcommon/InitConfigFileParser.cpp +++ b/ndb/src/common/mgmcommon/InitConfigFileParser.cpp @@ -22,7 +22,7 @@ #include <NdbOut.hpp> #include "ConfigInfo.hpp" -const int MAX_LINE_LENGTH = 120; // Max length of line of text in config file +const int MAX_LINE_LENGTH = 1024; // Max length of line of text in config file static void trim(char *); static void require(bool v) { if(!v) abort();} @@ -163,6 +163,30 @@ InitConfigFileParser::parseConfig(FILE * file) { return 0; } + for(size_t i = 0; ConfigInfo::m_ConfigRules[i].m_configRule != 0; i++){ + ctx.type = InitConfigFileParser::Undefined; + ctx.m_currentSection = 0; + ctx.m_userDefaults = 0; + ctx.m_currentInfo = 0; + ctx.m_systemDefaults = 0; + + Vector<ConfigInfo::ConfigRuleSection> tmp; + if(!(* ConfigInfo::m_ConfigRules[i].m_configRule)(tmp, ctx, + ConfigInfo::m_ConfigRules[i].m_ruleData)) + return 0; + + for(size_t j = 0; j<tmp.size(); j++){ + snprintf(ctx.fname, sizeof(ctx.fname), tmp[j].m_sectionType.c_str()); + ctx.type = InitConfigFileParser::Section; + ctx.m_currentSection = tmp[j].m_sectionData; + ctx.m_userDefaults = getSection(ctx.fname, ctx.m_defaults); + ctx.m_currentInfo = m_info->getInfo(ctx.fname); + ctx.m_systemDefaults = m_info->getDefaults(ctx.fname); + if(!storeSection(ctx)) + return 0; + } + } + Uint32 nConnections = 0; Uint32 nComputers = 0; Uint32 nNodes = 0; @@ -499,28 +523,22 @@ bool InitConfigFileParser::storeSection(Context& ctx){ if(ctx.m_currentSection == NULL) return true; - for(int i = strlen(ctx.fname) - 1; i>=0; i--){ ctx.fname[i] = toupper(ctx.fname[i]); } - snprintf(ctx.pname, sizeof(ctx.pname), ctx.fname); - char buf[255]; if(ctx.type == InitConfigFileParser::Section) snprintf(buf, sizeof(buf), "%s", ctx.fname); if(ctx.type == InitConfigFileParser::DefaultSection) snprintf(buf, sizeof(buf), "%s DEFAULT", ctx.fname); - snprintf(ctx.fname, sizeof(ctx.fname), buf); if(ctx.type == InitConfigFileParser::Section){ for(int i = 0; i<m_info->m_NoOfRules; i++){ const ConfigInfo::SectionRule & rule = m_info->m_SectionRules[i]; - if(!strcmp(rule.m_section, "*") || !strcmp(rule.m_section, ctx.fname)){ - if(!(* rule.m_sectionRule)(ctx, rule.m_ruleData)){ + if(!strcmp(rule.m_section, "*") || !strcmp(rule.m_section, ctx.fname)) + if(!(* rule.m_sectionRule)(ctx, rule.m_ruleData)) return false; - } - } } } diff --git a/ndb/src/common/portlib/NdbThread.c b/ndb/src/common/portlib/NdbThread.c index b023e851d29..8683a37edcb 100644 --- a/ndb/src/common/portlib/NdbThread.c +++ b/ndb/src/common/portlib/NdbThread.c @@ -58,7 +58,9 @@ struct NdbThread* NdbThread_Create(NDB_THREAD_FUNC *p_thread_func, pthread_attr_setguardsize(&thread_attr, 2048); #endif +#ifdef PTHREAD_CREATE_JOINABLE /* needed on SCO */ pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_JOINABLE); +#endif result = pthread_create(&tmpThread->thread, &thread_attr, p_thread_func, diff --git a/ndb/src/kernel/blocks/dbacc/DbaccMain.cpp b/ndb/src/kernel/blocks/dbacc/DbaccMain.cpp index 02474f6bee0..933ee2cf8e1 100644 --- a/ndb/src/kernel/blocks/dbacc/DbaccMain.cpp +++ b/ndb/src/kernel/blocks/dbacc/DbaccMain.cpp @@ -2432,6 +2432,7 @@ void Dbacc::execACC_LOCKREQ(Signal* signal) } fragrecptr.i = req->fragPtrI; ptrCheckGuard(fragrecptr, cfragmentsize, fragmentrec); + ndbrequire(req->fragId == fragrecptr.p->myfid); // caller must be explicit here ndbrequire(req->accOpPtr == RNIL); // seize operation to hold the lock diff --git a/ndb/src/kernel/blocks/dblqh/DblqhMain.cpp b/ndb/src/kernel/blocks/dblqh/DblqhMain.cpp index 613f9ccea11..1abf4b3a7e9 100644 --- a/ndb/src/kernel/blocks/dblqh/DblqhMain.cpp +++ b/ndb/src/kernel/blocks/dblqh/DblqhMain.cpp @@ -1225,6 +1225,18 @@ Dblqh::sendAddFragReq(Signal* signal) tuxreq->fragOff = addfragptr.p->lh3DistrBits; tuxreq->tableType = addfragptr.p->tableType; tuxreq->primaryTableId = addfragptr.p->primaryTableId; + // pointer to index fragment in TUP + tuxreq->tupIndexFragPtrI = + addfragptr.p->addfragStatus == AddFragRecord::WAIT_TWO_TUX ? + fragptr.p->tupFragptr[0] : fragptr.p->tupFragptr[1]; + // pointers to table fragments in TUP and ACC + FragrecordPtr tFragPtr; + tFragPtr.i = fragptr.p->tableFragptr; + ptrCheckGuard(tFragPtr, cfragrecFileSize, fragrecord); + tuxreq->tupTableFragPtrI[0] = tFragPtr.p->tupFragptr[0]; + tuxreq->tupTableFragPtrI[1] = tFragPtr.p->tupFragptr[1]; + tuxreq->accTableFragPtrI[0] = tFragPtr.p->accFragptr[0]; + tuxreq->accTableFragPtrI[1] = tFragPtr.p->accFragptr[1]; sendSignal(fragptr.p->tuxBlockref, GSN_TUXFRAGREQ, signal, TuxFragReq::SignalLength, JBB); return; diff --git a/ndb/src/kernel/blocks/dbtup/Dbtup.hpp b/ndb/src/kernel/blocks/dbtup/Dbtup.hpp index c09c8984ce2..4781230a311 100644 --- a/ndb/src/kernel/blocks/dbtup/Dbtup.hpp +++ b/ndb/src/kernel/blocks/dbtup/Dbtup.hpp @@ -996,6 +996,31 @@ public: Dbtup(const class Configuration &); virtual ~Dbtup(); + /* + * TUX uses logical tuple address when talking to ACC and LQH. + */ + void tuxGetTupAddr(Uint32 fragPtrI, Uint32 pageId, Uint32 pageOffset, Uint32& tupAddr); + + /* + * TUX index in TUP has single Uint32 array attribute which stores an + * index node. TUX reads and writes the node directly via pointer. + */ + int tuxAllocNode(Signal* signal, Uint32 fragPtrI, Uint32& pageId, Uint32& pageOffset, Uint32*& node); + void tuxFreeNode(Signal* signal, Uint32 fragPtrI, Uint32 pageId, Uint32 pageOffset, Uint32* node); + void tuxGetNode(Uint32 fragPtrI, Uint32 pageId, Uint32 pageOffset, Uint32*& node); + + /* + * TUX reads primary table attributes for index keys. Input is + * attribute ids in AttributeHeader format. Output is pointers to + * attribute data within tuple or 0 for NULL value. + */ + void tuxReadAttrs(Uint32 fragPtrI, Uint32 pageId, Uint32 pageOffset, Uint32 tupVersion, Uint32 numAttrs, const Uint32* attrIds, const Uint32** attrData); + + /* + * TUX reads primary key for md5 summing and when returning keyinfo. + */ + void tuxReadKeys(); // under construction + private: BLOCK_DEFINES(Dbtup); diff --git a/ndb/src/kernel/blocks/dbtup/DbtupIndex.cpp b/ndb/src/kernel/blocks/dbtup/DbtupIndex.cpp index 3a074f7fe5b..f11de5238e2 100644 --- a/ndb/src/kernel/blocks/dbtup/DbtupIndex.cpp +++ b/ndb/src/kernel/blocks/dbtup/DbtupIndex.cpp @@ -31,6 +31,154 @@ // methods used by ordered index void +Dbtup::tuxGetTupAddr(Uint32 fragPtrI, Uint32 pageId, Uint32 pageOffset, Uint32& tupAddr) +{ + ljamEntry(); + FragrecordPtr fragPtr; + fragPtr.i = fragPtrI; + ptrCheckGuard(fragPtr, cnoOfFragrec, fragrecord); + TablerecPtr tablePtr; + tablePtr.i = fragPtr.p->fragTableId; + ptrCheckGuard(tablePtr, cnoOfTablerec, tablerec); + PagePtr pagePtr; + pagePtr.i = pageId; + ptrCheckGuard(pagePtr, cnoOfPage, page); + Uint32 fragPageId = pagePtr.p->pageWord[ZPAGE_FRAG_PAGE_ID_POS]; + Uint32 tupheadsize = tablePtr.p->tupheadsize; + ndbrequire(pageOffset >= ZPAGE_HEADER_SIZE); + Uint32 offset = pageOffset - ZPAGE_HEADER_SIZE; + ndbrequire(offset % tupheadsize == 0); + Uint32 pageIndex = (offset / tupheadsize) << 1; + tupAddr = (fragPageId << MAX_TUPLES_BITS) | pageIndex; +} + +int +Dbtup::tuxAllocNode(Signal* signal, Uint32 fragPtrI, Uint32& pageId, Uint32& pageOffset, Uint32*& node) +{ + ljamEntry(); + FragrecordPtr fragPtr; + fragPtr.i = fragPtrI; + ptrCheckGuard(fragPtr, cnoOfFragrec, fragrecord); + TablerecPtr tablePtr; + tablePtr.i = fragPtr.p->fragTableId; + ptrCheckGuard(tablePtr, cnoOfTablerec, tablerec); + PagePtr pagePtr; + terrorCode = 0; + if (! allocTh(fragPtr.p, tablePtr.p, NORMAL_PAGE, signal, pageOffset, pagePtr)) { + ljam(); + ndbrequire(terrorCode != 0); + return terrorCode; + } + pageId = pagePtr.i; + Uint32 attrDescIndex = tablePtr.p->tabDescriptor + (0 << ZAD_LOG_SIZE); + Uint32 attrDataOffset = AttributeOffset::getOffset(tableDescriptor[attrDescIndex + 1].tabDescr); + node = &pagePtr.p->pageWord[pageOffset] + attrDataOffset; + return 0; +} + +void +Dbtup::tuxFreeNode(Signal* signal, Uint32 fragPtrI, Uint32 pageId, Uint32 pageOffset, Uint32* node) +{ + ljamEntry(); + FragrecordPtr fragPtr; + fragPtr.i = fragPtrI; + ptrCheckGuard(fragPtr, cnoOfFragrec, fragrecord); + TablerecPtr tablePtr; + tablePtr.i = fragPtr.p->fragTableId; + ptrCheckGuard(tablePtr, cnoOfTablerec, tablerec); + PagePtr pagePtr; + pagePtr.i = pageId; + ptrCheckGuard(pagePtr, cnoOfPage, page); + Uint32 attrDescIndex = tablePtr.p->tabDescriptor + (0 << ZAD_LOG_SIZE); + Uint32 attrDataOffset = AttributeOffset::getOffset(tableDescriptor[attrDescIndex + 1].tabDescr); + ndbrequire(node == &pagePtr.p->pageWord[pageOffset] + attrDataOffset); + freeTh(fragPtr.p, tablePtr.p, signal, pagePtr.p, pageOffset); +} + +void +Dbtup::tuxGetNode(Uint32 fragPtrI, Uint32 pageId, Uint32 pageOffset, Uint32*& node) +{ + ljamEntry(); + FragrecordPtr fragPtr; + fragPtr.i = fragPtrI; + ptrCheckGuard(fragPtr, cnoOfFragrec, fragrecord); + TablerecPtr tablePtr; + tablePtr.i = fragPtr.p->fragTableId; + ptrCheckGuard(tablePtr, cnoOfTablerec, tablerec); + PagePtr pagePtr; + pagePtr.i = pageId; + ptrCheckGuard(pagePtr, cnoOfPage, page); + Uint32 attrDescIndex = tablePtr.p->tabDescriptor + (0 << ZAD_LOG_SIZE); + Uint32 attrDataOffset = AttributeOffset::getOffset(tableDescriptor[attrDescIndex + 1].tabDescr); + node = &pagePtr.p->pageWord[pageOffset] + attrDataOffset; +} + +void +Dbtup::tuxReadAttrs(Uint32 fragPtrI, Uint32 pageId, Uint32 pageOffset, Uint32 tupVersion, Uint32 numAttrs, const Uint32* attrIds, const Uint32** attrData) +{ + ljamEntry(); + FragrecordPtr fragPtr; + fragPtr.i = fragPtrI; + ptrCheckGuard(fragPtr, cnoOfFragrec, fragrecord); + TablerecPtr tablePtr; + tablePtr.i = fragPtr.p->fragTableId; + ptrCheckGuard(tablePtr, cnoOfTablerec, tablerec); + PagePtr pagePtr; + pagePtr.i = pageId; + ptrCheckGuard(pagePtr, cnoOfPage, page); + // search for tuple version if not original + if (pagePtr.p->pageWord[pageOffset + 1] != tupVersion) { + ljam(); + OperationrecPtr opPtr; + opPtr.i = pagePtr.p->pageWord[pageOffset]; + Uint32 loopGuard = 0; + while (true) { + ptrCheckGuard(opPtr, cnoOfOprec, operationrec); + if (opPtr.p->realPageIdC != RNIL) { + pagePtr.i = opPtr.p->realPageIdC; + pageOffset = opPtr.p->pageOffsetC; + ptrCheckGuard(pagePtr, cnoOfPage, page); + if (pagePtr.p->pageWord[pageOffset + 1] == tupVersion) { + ljam(); + break; + } + } + ljam(); + opPtr.i = opPtr.p->nextActiveOp; + ndbrequire(++loopGuard < (1 << ZTUP_VERSION_BITS)); + } + } + const Uint32 tabDescriptor = tablePtr.p->tabDescriptor; + const Uint32* tupleHeader = &pagePtr.p->pageWord[pageOffset]; + for (Uint32 i = 0; i < numAttrs; i++) { + AttributeHeader ah(attrIds[i]); + Uint32 attrId = ah.getAttributeId(); + Uint32 index = tabDescriptor + (attrId << ZAD_LOG_SIZE); + Uint32 desc1 = tableDescriptor[index].tabDescr; + Uint32 desc2 = tableDescriptor[index + 1].tabDescr; + if (AttributeDescriptor::getNullable(desc1)) { + Uint32 offset = AttributeOffset::getNullFlagOffset(desc2); + ndbrequire(offset < tablePtr.p->tupNullWords); + offset += tablePtr.p->tupNullIndex; + ndbrequire(offset < tablePtr.p->tupheadsize); + if (AttributeOffset::isNULL(tupleHeader[offset], desc2)) { + ljam(); + attrData[i] = 0; + continue; + } + } + attrData[i] = tupleHeader + AttributeOffset::getOffset(desc2); + } +} + +void // under construction +Dbtup::tuxReadKeys() +{ +} + +// deprecated signal interfaces + +void Dbtup::execTUP_READ_ATTRS(Signal* signal) { ljamEntry(); @@ -168,10 +316,10 @@ Dbtup::execTUP_QUERY_TH(Signal* signal) for this transaction and savepoint id. If its tuple version equals the requested then we have a visible tuple otherwise not. */ - jam(); + ljam(); Uint32 read_tupVersion = pagePtr.p->pageWord[tempOp.pageOffset + 1]; if (read_tupVersion == req_tupVersion) { - jam(); + ljam(); ret_result = 1; } } @@ -425,7 +573,8 @@ Dbtup::buildIndex(Signal* signal, Uint32 buildPtrI) buildPtr.p->m_tupleNo = 0; break; } - pagePtr.i = getRealpid(fragPtr.p, buildPtr.p->m_pageId); + Uint32 realPageId = getRealpid(fragPtr.p, buildPtr.p->m_pageId); + pagePtr.i = realPageId; ptrCheckGuard(pagePtr, cnoOfPage, page); const Uint32 pageState = pagePtr.p->pageWord[ZPAGE_STATE_POS]; if (pageState != ZTH_MM_FREE && @@ -439,8 +588,7 @@ Dbtup::buildIndex(Signal* signal, Uint32 buildPtrI) } // get tuple const Uint32 tupheadsize = tablePtr.p->tupheadsize; - const Uint32 pageOffset = ZPAGE_HEADER_SIZE + - buildPtr.p->m_tupleNo * tupheadsize; + Uint32 pageOffset = ZPAGE_HEADER_SIZE + buildPtr.p->m_tupleNo * tupheadsize; if (pageOffset + tupheadsize > ZWORDS_ON_PAGE) { ljam(); buildPtr.p->m_pageId++; @@ -472,15 +620,14 @@ Dbtup::buildIndex(Signal* signal, Uint32 buildPtrI) buildPtr.p->m_tupleNo++; break; } + Uint32 tupVersion = pagePtr.p->pageWord[pageOffset + 1]; OperationrecPtr pageOperPtr; pageOperPtr.i = pagePtr.p->pageWord[pageOffset]; - Uint32 pageId = buildPtr.p->m_pageId; - Uint32 pageIndex = buildPtr.p->m_tupleNo << 1; if (pageOperPtr.i != RNIL) { /* If there is an ongoing operation on the tuple then it is either a copy tuple or an original tuple with an ongoing transaction. In - both cases fragPageId and pageIndex refers to the original tuple. + both cases realPageId and pageOffset refer to the original tuple. The tuple address stored in TUX will always be the original tuple but with the tuple version of the tuple we found. @@ -490,12 +637,11 @@ Dbtup::buildIndex(Signal* signal, Uint32 buildPtrI) tuple as a copy tuple. The original tuple is stable and is thus preferrable to store in TUX. */ - jam(); + ljam(); ptrCheckGuard(pageOperPtr, cnoOfOprec, operationrec); - pageId = pageOperPtr.p->fragPageId; - pageIndex = pageOperPtr.p->pageIndex; + realPageId = pageOperPtr.p->realPageId; + pageOffset = pageOperPtr.p->pageOffset; }//if - Uint32 tup_version = pagePtr.p->pageWord[pageOffset + 1]; #ifdef TIME_MEASUREMENT NdbTick_getMicroTimer(&start); #endif @@ -505,8 +651,9 @@ Dbtup::buildIndex(Signal* signal, Uint32 buildPtrI) req->tableId = tablePtr.i; req->indexId = triggerPtr.p->indexId; req->fragId = tablePtr.p->fragid[buildPtr.p->m_fragNo]; - req->tupAddr = (pageId << MAX_TUPLES_BITS) | pageIndex; - req->tupVersion = tup_version; + req->pageId = realPageId; + req->pageOffset = pageOffset; + req->tupVersion = tupVersion; req->opInfo = TuxMaintReq::OpAdd; EXECUTE_DIRECT(DBTUX, GSN_TUX_MAINT_REQ, signal, TuxMaintReq::SignalLength); diff --git a/ndb/src/kernel/blocks/dbtup/DbtupTrigger.cpp b/ndb/src/kernel/blocks/dbtup/DbtupTrigger.cpp index 6fceea31150..a93ff4566e7 100644 --- a/ndb/src/kernel/blocks/dbtup/DbtupTrigger.cpp +++ b/ndb/src/kernel/blocks/dbtup/DbtupTrigger.cpp @@ -962,7 +962,8 @@ Dbtup::executeTuxInsertTriggers(Signal* signal, // fill in constant part req->tableId = regOperPtr->tableRef; req->fragId = regOperPtr->fragId; - req->tupAddr = (regOperPtr->fragPageId << MAX_TUPLES_BITS) | regOperPtr->pageIndex; + req->pageId = regOperPtr->realPageId; + req->pageOffset = regOperPtr->pageOffset; req->tupVersion = tupVersion; req->opInfo = TuxMaintReq::OpAdd; // loop over index list @@ -1000,7 +1001,8 @@ Dbtup::executeTuxUpdateTriggers(Signal* signal, // fill in constant part req->tableId = regOperPtr->tableRef; req->fragId = regOperPtr->fragId; - req->tupAddr = (regOperPtr->fragPageId << MAX_TUPLES_BITS) | regOperPtr->pageIndex; + req->pageId = regOperPtr->realPageId; + req->pageOffset = regOperPtr->pageOffset; req->tupVersion = tupVersion; req->opInfo = TuxMaintReq::OpAdd; // loop over index list @@ -1009,7 +1011,6 @@ Dbtup::executeTuxUpdateTriggers(Signal* signal, triggerList.first(triggerPtr); while (triggerPtr.i != RNIL) { ljam(); - req->tupAddr = (regOperPtr->fragPageId << MAX_TUPLES_BITS) | regOperPtr->pageIndex; req->indexId = triggerPtr.p->indexId; req->errorCode = RNIL; EXECUTE_DIRECT(DBTUX, GSN_TUX_MAINT_REQ, @@ -1074,7 +1075,8 @@ Dbtup::executeTuxCommitTriggers(Signal* signal, // fill in constant part req->tableId = regOperPtr->tableRef; req->fragId = regOperPtr->fragId; - req->tupAddr = (regOperPtr->fragPageId << MAX_TUPLES_BITS) | regOperPtr->pageIndex; + req->pageId = regOperPtr->realPageId; + req->pageOffset = regOperPtr->pageOffset; req->tupVersion = tupVersion; req->opInfo = TuxMaintReq::OpRemove; // loop over index list @@ -1117,7 +1119,8 @@ Dbtup::executeTuxAbortTriggers(Signal* signal, // fill in constant part req->tableId = regOperPtr->tableRef; req->fragId = regOperPtr->fragId; - req->tupAddr = (regOperPtr->fragPageId << MAX_TUPLES_BITS) | regOperPtr->pageIndex; + req->pageId = regOperPtr->realPageId; + req->pageOffset = regOperPtr->pageOffset; req->tupVersion = tupVersion; req->opInfo = TuxMaintReq::OpRemove; // loop over index list diff --git a/ndb/src/kernel/blocks/dbtux/Dbtux.hpp b/ndb/src/kernel/blocks/dbtux/Dbtux.hpp index e871fc86dea..25e85ba9f5f 100644 --- a/ndb/src/kernel/blocks/dbtux/Dbtux.hpp +++ b/ndb/src/kernel/blocks/dbtux/Dbtux.hpp @@ -20,11 +20,15 @@ #include <new> #include <ndb_limits.h> #include <SimulatedBlock.hpp> +#include <AttributeDescriptor.hpp> #include <AttributeHeader.hpp> #include <ArrayPool.hpp> #include <DataBuffer.hpp> #include <md5_hash.hpp> +// big brother +#include <Dbtup.hpp> + // signal classes #include <signaldata/DictTabInfo.hpp> #include <signaldata/TuxContinueB.hpp> @@ -81,6 +85,10 @@ #define jam() jamLine(90000 + __LINE__) #define jamEntry() jamEntryLine(90000 + __LINE__) #endif +#ifndef jam +#define jam() jamLine(__LINE__) +#define jamEntry() jamEntryLine(__LINE__) +#endif #undef max #undef min @@ -92,15 +100,13 @@ public: Dbtux(const Configuration& conf); virtual ~Dbtux(); + // pointer to TUP instance in this thread + Dbtup* c_tup; + private: // sizes are in words (Uint32) static const unsigned MaxIndexFragments = 2 * NO_OF_FRAG_PER_NODE; static const unsigned MaxIndexAttributes = MAX_ATTRIBUTES_IN_INDEX; -#ifdef VM_TRACE - static const unsigned MaxNodeHandles = 10000; // More space for printTree -#else - static const unsigned MaxNodeHandles = 128; // enough for 1 operation -#endif static const unsigned MaxAttrDataSize = 2048; public: static const unsigned DescPageSize = 256; @@ -114,7 +120,7 @@ private: struct DescEnt; /* - * Pointer to Uint32 data. Interpretation is context dependent. + * Pointer to array of Uint32. */ struct Data { private: @@ -130,7 +136,7 @@ private: friend class Data; /* - * Pointer to constant Uint32 data. + * Pointer to array of constant Uint32. */ struct ConstData; friend struct ConstData; @@ -153,8 +159,12 @@ private: static const unsigned AttributeHeaderSize = 1; /* - * Logical tuple address, "local key". Identifies both table tuples - * and index tuples. The code assumes it is one word. + * Array of pointers to TUP table attributes. Always read-on|y. + */ + typedef const Uint32** TableData; + + /* + * Logical tuple address, "local key". Identifies table tuples. */ typedef Uint32 TupAddr; static const unsigned NullTupAddr = (Uint32)-1; @@ -168,22 +178,32 @@ private: Uint32 m_pageId; // page i-value Uint16 m_pageOffset; // page offset in words TupLoc(); + TupLoc(Uint32 pageId, Uint16 pageOffset); + bool operator==(const TupLoc& loc) const; + bool operator!=(const TupLoc& loc) const; }; + /* + * There is no const member NullTupLoc since the compiler may not be + * able to optimize it to TupLoc() constants. Instead null values are + * constructed on the stack with TupLoc(). + */ +#define NullTupLoc TupLoc() + // tree definitions /* - * Tree entry. Points to a tuple in primary table via logical address - * of "original" tuple and tuple version. Uses 2 words to get correct - * aligment (one byte is wasted currently). + * Tree entry. Points to a tuple in primary table via physical + * address of "original" tuple and tuple version. + * + * ZTUP_VERSION_BITS must be 15 (or less). */ struct TreeEnt; friend struct TreeEnt; struct TreeEnt { - TupAddr m_tupAddr; // address of original tuple - Uint16 m_tupVersion; // version - Uint8 m_fragBit; // which duplicated table fragment - Uint8 unused1; + TupLoc m_tupLoc; // address of original tuple + unsigned m_tupVersion : 15; // version + unsigned m_fragBit : 1; // which duplicated table fragment TreeEnt(); // methods int cmp(const TreeEnt ent) const; @@ -196,7 +216,7 @@ private: * prefix 3) max and min entries 4) rest of entries 5) one extra entry * used as work space. * - * struct TreeNode part 1 + * struct TreeNode part 1, size 6 words * min prefix part 2, size TreeHead::m_prefSize * max prefix part 2, size TreeHead::m_prefSize * max entry part 3 @@ -204,6 +224,10 @@ private: * rest of entries part 4 * work entry part 5 * + * There are 3 links to other nodes: left child, right child, parent. + * These are in TupLoc format but the pageIds and pageOffsets are + * stored in separate arrays (saves 1 word). + * * Occupancy (number of entries) is at least 1 except temporarily when * a node is about to be removed. If occupancy is 1, only max entry * is present but both min and max prefixes are set. @@ -211,11 +235,12 @@ private: struct TreeNode; friend struct TreeNode; struct TreeNode { - TupAddr m_link[3]; // link to 0-left child 1-right child 2-parent - Uint8 m_side; // we are 0-left child 1-right child 2-root + Uint32 m_linkPI[3]; // link to 0-left child 1-right child 2-parent + Uint16 m_linkPO[3]; // page offsets for above real page ids + unsigned m_side : 2; // we are 0-left child 1-right child 2-root + int m_balance : 2; // balance -1, 0, +1 + unsigned pad1 : 4; Uint8 m_occup; // current number of entries - Int8 m_balance; // balance -1, 0, +1 - Uint8 unused1; Uint32 m_nodeScan; // list of scans at this node TreeNode(); }; @@ -243,7 +268,7 @@ private: Uint8 m_prefSize; // words in min/max prefix each Uint8 m_minOccup; // min entries in internal node Uint8 m_maxOccup; // max entries in node - TupAddr m_root; // root node + TupLoc m_root; // root node TreeHead(); // methods unsigned getSize(AccSize acc) const; @@ -261,8 +286,7 @@ private: struct TreePos; friend struct TreePos; struct TreePos { - TupAddr m_addr; // logical node address - TupLoc m_loc; // physical address + TupLoc m_loc; // physical node address Uint16 m_pos; // position 0 to m_occup Uint8 m_match; // at an existing entry Uint8 m_dir; // from link (0-2) or within node (3) @@ -304,10 +328,9 @@ private: * Attribute metadata. Size must be multiple of word size. */ struct DescAttr { - unsigned m_primaryAttrId : 16; - unsigned m_typeId : 8; - unsigned m_nullable : 1; - unsigned pad1 : 7; + Uint32 m_attrDesc; // standard AttributeDescriptor + Uint16 m_primaryAttrId; + Uint16 m_typeId; }; static const unsigned DescAttrSize = sizeof(DescAttr) >> 2; @@ -443,9 +466,11 @@ private: Uint16 m_descOff; Uint16 m_numAttrs; TreeHead m_tree; - Uint32 m_nodeList; // node cache of current operation - Uint32 m_nodeFree; // one node pre-allocated for insert + TupLoc m_freeLoc; // one node pre-allocated for insert DLList<ScanOp> m_scanList; // current scans on this fragment + Uint32 m_tupIndexFragPtrI; + Uint32 m_tupTableFragPtrI[2]; + Uint32 m_accTableFragPtrI[2]; union { Uint32 nextPool; }; @@ -476,62 +501,39 @@ private: // node handles /* - * A tree operation builds a cache of accessed nodes. This allows - * different implementations of index memory access. The cache is - * committed and released at the end of the operation. + * A node handle is a reference to a tree node in TUP. It is used to + * operate on the node. Node handles are allocated on the stack. */ struct NodeHandle; friend struct NodeHandle; struct NodeHandle { - enum Flags { - // bits 0,1 mark need for left,right prefix - DoInsert = (1 << 2), - DoDelete = (1 << 3), - DoUpdate = (1 << 4) - }; - Dbtux& m_tux; // this block Frag& m_frag; // fragment using the node - TupAddr m_addr; // logical node address TupLoc m_loc; // physical node address - AccSize m_acc; // accessed size - unsigned m_flags; // flags - union { - Uint32 m_next; // next active node under fragment - Uint32 nextPool; - }; TreeNode* m_node; // pointer to node storage - Uint32 m_cache[MaxTreeNodeSize]; - NodeHandle(Dbtux& tux, Frag& frag); + AccSize m_acc; // accessed size + NodeHandle(Frag& frag); + NodeHandle(const NodeHandle& node); + NodeHandle& operator=(const NodeHandle& node); // getters - TupAddr getLink(unsigned i); + TupLoc getLink(unsigned i); unsigned getChilds(); // cannot spell unsigned getSide(); unsigned getOccup(); int getBalance(); Uint32 getNodeScan(); - Data getPref(unsigned i); - TreeEnt getEnt(unsigned pos); - TreeEnt getMinMax(unsigned i); // setters - void setLink(unsigned i, TupAddr addr); + void setLink(unsigned i, TupLoc loc); void setSide(unsigned i); void setOccup(unsigned n); void setBalance(int b); void setNodeScan(Uint32 scanPtrI); - // operations XXX maybe these should move to Dbtux level - void pushUp(Signal* signal, unsigned pos, const TreeEnt& ent); - void popDown(Signal* signal, unsigned pos, TreeEnt& ent); - void pushDown(Signal* signal, unsigned pos, TreeEnt& ent); - void popUp(Signal* signal, unsigned pos, TreeEnt& ent); - void slide(Signal* signal, Ptr<NodeHandle> nodePtr, unsigned i); - void linkScan(Dbtux::ScanOpPtr scanPtr); - void unlinkScan(Dbtux::ScanOpPtr scanPtr); - bool islinkScan(Dbtux::ScanOpPtr scanPtr); - // for ndbrequire - void progError(int line, int cause, const char* extra); + // access other parts of the node + Data getPref(unsigned i); + TreeEnt getEnt(unsigned pos); + TreeEnt getMinMax(unsigned i); + // for ndbrequire and ndbassert + void progError(int line, int cause, const char* file); }; - typedef Ptr<NodeHandle> NodeHandlePtr; - ArrayPool<NodeHandle> c_nodeHandlePool; // parameters for methods @@ -563,42 +565,6 @@ private: }; /* - * Node storage operation. - */ - struct StorePar { - TupStoreTh::OpCode m_opCode;// operation code - unsigned m_offset; // data offset in words - unsigned m_size; // number of words - Uint32 m_errorCode; // terrorCode from TUP - StorePar(); - }; - - /* - * Tree search for entry. - */ - struct SearchPar; - friend struct SearchPar; - struct SearchPar { - ConstData m_data; // input index key values - TreeEnt m_ent; // input tuple and version - SearchPar(); - }; - - /* - * Attribute data comparison. - */ - struct CmpPar; - friend struct CmpPar; - struct CmpPar { - ConstData m_data1; // full search key - ConstData m_data2; // full or prefix data - unsigned m_len2; // words in data2 buffer - unsigned m_first; // first attribute - unsigned m_numEq; // number of initial equal attributes - CmpPar(); - }; - - /* * Scan bound comparison. */ struct BoundPar; @@ -621,7 +587,10 @@ private: void execSTTOR(Signal* signal); void execREAD_CONFIG_REQ(Signal* signal); // utils + void setKeyAttrs(const Frag& frag); + void readKeyAttrs(const Frag& frag, TreeEnt ent, unsigned start, TableData keyData); void copyAttrs(Data dst, ConstData src, CopyPar& copyPar); + void copyAttrs(const Frag& frag, TableData data1, Data data2, unsigned maxlen2 = MaxAttrDataSize); /* * DbtuxMeta.cpp @@ -633,8 +602,6 @@ private: bool allocDescEnt(IndexPtr indexPtr); void freeDescEnt(IndexPtr indexPtr); void dropIndex(Signal* signal, IndexPtr indexPtr, Uint32 senderRef, Uint32 senderData); - // helpers - DescEnt& getDescEnt(Uint32 descPage, Uint32 descOff); /* * DbtuxMaint.cpp @@ -642,29 +609,35 @@ private: void execTUX_MAINT_REQ(Signal* signal); void tupReadAttrs(Signal* signal, const Frag& frag, ReadPar& readPar); void tupReadKeys(Signal* signal, const Frag& frag, ReadPar& readPar); - void tupStoreTh(Signal* signal, const Frag& frag, NodeHandlePtr nodePtr, StorePar storePar); /* * DbtuxNode.cpp */ - void seizeNode(Signal* signal, Frag& frag, NodeHandlePtr& nodePtr); - void preallocNode(Signal* signal, Frag& frag, Uint32& errorCode); - void findNode(Signal* signal, Frag& frag, NodeHandlePtr& nodePtr, TupAddr addr); - void selectNode(Signal* signal, Frag& frag, NodeHandlePtr& nodePtr, TupAddr addr, AccSize acc); - void insertNode(Signal* signal, Frag& frag, NodeHandlePtr& nodePtr, AccSize acc); - void deleteNode(Signal* signal, Frag& frag, NodeHandlePtr& nodePtr); - void accessNode(Signal* signal, Frag& frag, NodeHandlePtr& nodePtr, AccSize acc); - void setNodePref(Signal* signal, Frag& frag, NodeHandlePtr& nodePtr, unsigned i); - void commitNodes(Signal* signal, Frag& frag, bool updateOk); + int allocNode(Signal* signal, NodeHandle& node); + void accessNode(Signal* signal, NodeHandle& node, AccSize acc); + void selectNode(Signal* signal, NodeHandle& node, TupLoc loc, AccSize acc); + void insertNode(Signal* signal, NodeHandle& node, AccSize acc); + void deleteNode(Signal* signal, NodeHandle& node); + void setNodePref(Signal* signal, NodeHandle& node, unsigned i); + // node operations + void nodePushUp(Signal* signal, NodeHandle& node, unsigned pos, const TreeEnt& ent); + void nodePopDown(Signal* signal, NodeHandle& node, unsigned pos, TreeEnt& ent); + void nodePushDown(Signal* signal, NodeHandle& node, unsigned pos, TreeEnt& ent); + void nodePopUp(Signal* signal, NodeHandle& node, unsigned pos, TreeEnt& ent); + void nodeSlide(Signal* signal, NodeHandle& dstNode, NodeHandle& srcNode, unsigned i); + // scans linked to node + void linkScan(NodeHandle& node, ScanOpPtr scanPtr); + void unlinkScan(NodeHandle& node, ScanOpPtr scanPtr); + bool islinkScan(NodeHandle& node, ScanOpPtr scanPtr); /* * DbtuxTree.cpp */ - void treeSearch(Signal* signal, Frag& frag, SearchPar searchPar, TreePos& treePos); + void treeSearch(Signal* signal, Frag& frag, TableData searchKey, TreeEnt searchEnt, TreePos& treePos); void treeAdd(Signal* signal, Frag& frag, TreePos treePos, TreeEnt ent); void treeRemove(Signal* signal, Frag& frag, TreePos treePos); - void treeRotateSingle(Signal* signal, Frag& frag, NodeHandlePtr& nodePtr, unsigned i); - void treeRotateDouble(Signal* signal, Frag& frag, NodeHandlePtr& nodePtr, unsigned i); + void treeRotateSingle(Signal* signal, Frag& frag, NodeHandle& node, unsigned i); + void treeRotateDouble(Signal* signal, Frag& frag, NodeHandle& node, unsigned i); /* * DbtuxScan.cpp @@ -687,7 +660,8 @@ private: /* * DbtuxCmp.cpp */ - int cmpTreeAttrs(const Frag& frag, CmpPar& cmpPar); + int cmpSearchKey(const Frag& frag, unsigned& start, TableData data1, ConstData data2, unsigned maxlen2 = MaxAttrDataSize); + int cmpSearchKey(const Frag& frag, unsigned& start, TableData data1, TableData data2); int cmpScanBound(const Frag& frag, const BoundPar boundPar); /* @@ -698,23 +672,24 @@ private: struct PrintPar { char m_path[100]; // LR prefix unsigned m_side; // expected side - TupAddr m_parent; // expected parent address + TupLoc m_parent; // expected parent address int m_depth; // returned depth unsigned m_occup; // returned occupancy bool m_ok; // returned status PrintPar(); }; void printTree(Signal* signal, Frag& frag, NdbOut& out); - void printNode(Signal* signal, Frag& frag, NdbOut& out, TupAddr addr, PrintPar& par); + void printNode(Signal* signal, Frag& frag, NdbOut& out, TupLoc loc, PrintPar& par); + friend class NdbOut& operator<<(NdbOut&, const TupLoc&); friend class NdbOut& operator<<(NdbOut&, const TreeEnt&); friend class NdbOut& operator<<(NdbOut&, const TreeNode&); friend class NdbOut& operator<<(NdbOut&, const TreeHead&); friend class NdbOut& operator<<(NdbOut&, const TreePos&); friend class NdbOut& operator<<(NdbOut&, const DescAttr&); + friend class NdbOut& operator<<(NdbOut&, const ScanOp&); friend class NdbOut& operator<<(NdbOut&, const Index&); friend class NdbOut& operator<<(NdbOut&, const Frag&); friend class NdbOut& operator<<(NdbOut&, const NodeHandle&); - friend class NdbOut& operator<<(NdbOut&, const ScanOp&); FILE* debugFile; NdbOut debugOut; unsigned debugFlags; @@ -730,10 +705,25 @@ private: Uint32 c_internalStartPhase; Uint32 c_typeOfStart; - // buffers - Data c_keyBuffer; // search key or scan bound + /* + * Array of index key attribute ids in AttributeHeader format. + * Includes fixed attribute sizes. This is global data set at + * operation start and is not passed as a parameter. + */ + Data c_keyAttrs; + + // buffer for search key data as pointers to TUP storage + TableData c_searchKey; + + // buffer for current entry key data as pointers to TUP storage + TableData c_entryKey; + + // buffer for scan bounds and keyinfo (primary key) + Data c_dataBuffer; - // small stuff + // inlined utils + DescEnt& getDescEnt(Uint32 descPage, Uint32 descOff); + Uint32 getTupAddr(const Frag& frag, TreeEnt ent); static unsigned min(unsigned x, unsigned y); static unsigned max(unsigned x, unsigned y); }; @@ -831,19 +821,58 @@ Dbtux::ConstData::operator=(Data data) return *this; } +// Dbtux::TupLoc + +inline +Dbtux::TupLoc::TupLoc() : + m_pageId(RNIL), + m_pageOffset(0) +{ +} + +inline +Dbtux::TupLoc::TupLoc(Uint32 pageId, Uint16 pageOffset) : + m_pageId(pageId), + m_pageOffset(pageOffset) +{ +} + +inline bool +Dbtux::TupLoc::operator==(const TupLoc& loc) const +{ + return m_pageId == loc.m_pageId && m_pageOffset == loc.m_pageOffset; +} + +inline bool +Dbtux::TupLoc::operator!=(const TupLoc& loc) const +{ + return ! (*this == loc); +} + // Dbtux::TreeEnt +inline +Dbtux::TreeEnt::TreeEnt() : + m_tupLoc(), + m_tupVersion(0), + m_fragBit(0) +{ +} + inline int Dbtux::TreeEnt::cmp(const TreeEnt ent) const { - // compare frags first (not optimal but makes easier to read logs) if (m_fragBit < ent.m_fragBit) return -1; if (m_fragBit > ent.m_fragBit) return +1; - if (m_tupAddr < ent.m_tupAddr) + if (m_tupLoc.m_pageId < ent.m_tupLoc.m_pageId) return -1; - if (m_tupAddr > ent.m_tupAddr) + if (m_tupLoc.m_pageId > ent.m_tupLoc.m_pageId) + return +1; + if (m_tupLoc.m_pageOffset < ent.m_tupLoc.m_pageOffset) + return -1; + if (m_tupLoc.m_pageOffset > ent.m_tupLoc.m_pageOffset) return +1; if (m_tupVersion < ent.m_tupVersion) return -1; @@ -852,8 +881,36 @@ Dbtux::TreeEnt::cmp(const TreeEnt ent) const return 0; } +// Dbtux::TreeNode + +inline +Dbtux::TreeNode::TreeNode() : + m_side(2), + m_balance(0), + pad1(0), + m_occup(0), + m_nodeScan(RNIL) +{ + m_linkPI[0] = NullTupLoc.m_pageId; + m_linkPO[0] = NullTupLoc.m_pageOffset; + m_linkPI[1] = NullTupLoc.m_pageId; + m_linkPO[1] = NullTupLoc.m_pageOffset; + m_linkPI[2] = NullTupLoc.m_pageId; + m_linkPO[2] = NullTupLoc.m_pageOffset; +} + // Dbtux::TreeHead +inline +Dbtux::TreeHead::TreeHead() : + m_nodeSize(0), + m_prefSize(0), + m_minOccup(0), + m_maxOccup(0), + m_root() +{ +} + inline unsigned Dbtux::TreeHead::getSize(AccSize acc) const { @@ -885,52 +942,10 @@ Dbtux::TreeHead::getEntList(TreeNode* node) const return (TreeEnt*)ptr; } -// Dbtux - -// constructors - -inline -Dbtux::TupLoc::TupLoc() : - m_pageId(RNIL), - m_pageOffset(0) -{ -} - -inline -Dbtux::TreeEnt::TreeEnt() : - m_tupAddr(NullTupAddr), - m_tupVersion(0), - m_fragBit(255), - unused1(0) -{ -} - -inline -Dbtux::TreeNode::TreeNode() : - m_side(255), - m_occup(0), - m_balance(0), - unused1(0xa1), - m_nodeScan(RNIL) -{ - m_link[0] = NullTupAddr; - m_link[1] = NullTupAddr; - m_link[2] = NullTupAddr; -} - -inline -Dbtux::TreeHead::TreeHead() : - m_nodeSize(0), - m_prefSize(0), - m_minOccup(0), - m_maxOccup(0), - m_root(0) -{ -} +// Dbtux::TreePos inline Dbtux::TreePos::TreePos() : - m_addr(NullTupAddr), m_loc(), m_pos(ZNIL), m_match(false), @@ -939,6 +954,8 @@ Dbtux::TreePos::TreePos() : { } +// Dbtux::DescPage + inline Dbtux::DescPage::DescPage() : m_nextPage(RNIL), @@ -953,6 +970,41 @@ Dbtux::DescPage::DescPage() : } } +// Dbtux::ScanOp + +inline +Dbtux::ScanOp::ScanOp(ScanBoundPool& scanBoundPool) : + m_state(Undef), + m_lockwait(false), + m_userPtr(RNIL), + m_userRef(RNIL), + m_tableId(RNIL), + m_indexId(RNIL), + m_fragPtrI(RNIL), + m_transId1(0), + m_transId2(0), + m_savePointId(0), + m_accLockOp(RNIL), + m_readCommitted(0), + m_lockMode(0), + m_keyInfo(0), + m_boundMin(scanBoundPool), + m_boundMax(scanBoundPool), + m_scanPos(), + m_lastEnt(), + m_nodeScan(RNIL) +{ + m_bound[0] = &m_boundMin; + m_bound[1] = &m_boundMax; + m_boundCnt[0] = 0; + m_boundCnt[1] = 0; + for (unsigned i = 0; i < MaxAccLockOps; i++) { + m_accLockOps[i] = RNIL; + } +} + +// Dbtux::Index + inline Dbtux::Index::Index() : m_state(NotDefined), @@ -969,6 +1021,8 @@ Dbtux::Index::Index() : }; }; +// Dbtux::Frag + inline Dbtux::Frag::Frag(ArrayPool<ScanOp>& scanOpPool) : m_tableId(RNIL), @@ -979,12 +1033,18 @@ Dbtux::Frag::Frag(ArrayPool<ScanOp>& scanOpPool) : m_descOff(0), m_numAttrs(ZNIL), m_tree(), - m_nodeList(RNIL), - m_nodeFree(RNIL), - m_scanList(scanOpPool) + m_freeLoc(), + m_scanList(scanOpPool), + m_tupIndexFragPtrI(RNIL) { + m_tupTableFragPtrI[0] = RNIL; + m_tupTableFragPtrI[1] = RNIL; + m_accTableFragPtrI[0] = RNIL; + m_accTableFragPtrI[1] = RNIL; } +// Dbtux::FragOp + inline Dbtux::FragOp::FragOp() : m_userPtr(RNIL), @@ -997,160 +1057,107 @@ Dbtux::FragOp::FragOp() : { }; +// Dbtux::NodeHandle + inline -Dbtux::NodeHandle::NodeHandle(Dbtux& tux, Frag& frag) : - m_tux(tux), +Dbtux::NodeHandle::NodeHandle(Frag& frag) : m_frag(frag), - m_addr(NullTupAddr), m_loc(), - m_acc(AccNone), - m_flags(0), - m_next(RNIL), - m_node(0) + m_node(0), + m_acc(AccNone) { } inline -Dbtux::ScanOp::ScanOp(ScanBoundPool& scanBoundPool) : - m_state(Undef), - m_lockwait(false), - m_userPtr(RNIL), - m_userRef(RNIL), - m_tableId(RNIL), - m_indexId(RNIL), - m_fragPtrI(RNIL), - m_transId1(0), - m_transId2(0), - m_savePointId(0), - m_accLockOp(RNIL), - m_readCommitted(0), - m_lockMode(0), - m_keyInfo(0), - m_boundMin(scanBoundPool), - m_boundMax(scanBoundPool), - m_scanPos(), - m_lastEnt(), - m_nodeScan(RNIL) +Dbtux::NodeHandle::NodeHandle(const NodeHandle& node) : + m_frag(node.m_frag), + m_loc(node.m_loc), + m_node(node.m_node), + m_acc(node.m_acc) { - m_bound[0] = &m_boundMin; - m_bound[1] = &m_boundMax; - m_boundCnt[0] = 0; - m_boundCnt[1] = 0; - for (unsigned i = 0; i < MaxAccLockOps; i++) { - m_accLockOps[i] = RNIL; - } } -inline -Dbtux::CopyPar::CopyPar() : - m_items(0), - m_headers(true), - m_maxwords(~0), // max unsigned - // output - m_numitems(0), - m_numwords(0) +inline Dbtux::NodeHandle& +Dbtux::NodeHandle::operator=(const NodeHandle& node) { + ndbassert(&m_frag == &node.m_frag); + m_loc = node.m_loc; + m_node = node.m_node; + m_acc = node.m_acc; + return *this; } -inline -Dbtux::ReadPar::ReadPar() : - m_first(0), - m_count(0), - m_data(0), - m_size(0) +inline Dbtux::TupLoc +Dbtux::NodeHandle::getLink(unsigned i) { + ndbrequire(i <= 2); + return TupLoc(m_node->m_linkPI[i], m_node->m_linkPO[i]); } -inline -Dbtux::StorePar::StorePar() : - m_opCode(TupStoreTh::OpUndefined), - m_offset(0), - m_size(0), - m_errorCode(0) +inline unsigned +Dbtux::NodeHandle::getChilds() { + return (getLink(0) != NullTupLoc) + (getLink(1) != NullTupLoc); } -inline -Dbtux::SearchPar::SearchPar() : - m_data(0), - m_ent() +inline unsigned +Dbtux::NodeHandle::getSide() { + return m_node->m_side; } -inline -Dbtux::CmpPar::CmpPar() : - m_data1(0), - m_data2(0), - m_len2(0), - m_first(0), - m_numEq(0) +inline unsigned +Dbtux::NodeHandle::getOccup() { + return m_node->m_occup; } -inline -Dbtux::BoundPar::BoundPar() : - m_data1(0), - m_data2(0), - m_count1(0), - m_len2(0), - m_dir(255) +inline int +Dbtux::NodeHandle::getBalance() { + return m_node->m_balance; } -#ifdef VM_TRACE -inline -Dbtux::PrintPar::PrintPar() : - // caller fills in - m_path(), - m_side(255), - m_parent(NullTupAddr), - // default return values - m_depth(0), - m_occup(0), - m_ok(true) +inline Uint32 +Dbtux::NodeHandle::getNodeScan() { + return m_node->m_nodeScan; } -#endif - -// node handles -inline Dbtux::TupAddr -Dbtux::NodeHandle::getLink(unsigned i) +inline void +Dbtux::NodeHandle::setLink(unsigned i, TupLoc loc) { ndbrequire(i <= 2); - return m_node->m_link[i]; + m_node->m_linkPI[i] = loc.m_pageId; + m_node->m_linkPO[i] = loc.m_pageOffset; } -inline unsigned -Dbtux::NodeHandle::getChilds() -{ - return - (m_node->m_link[0] != NullTupAddr) + - (m_node->m_link[1] != NullTupAddr); -} - -inline Dbtux::TupAddr -Dbtux::NodeHandle::getSide() +inline void +Dbtux::NodeHandle::setSide(unsigned i) { - return m_node->m_side; + ndbrequire(i <= 2); + m_node->m_side = i; } -inline unsigned -Dbtux::NodeHandle::getOccup() +inline void +Dbtux::NodeHandle::setOccup(unsigned n) { - return m_node->m_occup; + TreeHead& tree = m_frag.m_tree; + ndbrequire(n <= tree.m_maxOccup); + m_node->m_occup = n; } -inline int -Dbtux::NodeHandle::getBalance() +inline void +Dbtux::NodeHandle::setBalance(int b) { - return m_node->m_balance; + ndbrequire(abs(b) <= 1); + m_node->m_balance = b; } -inline Uint32 -Dbtux::NodeHandle::getNodeScan() +inline void +Dbtux::NodeHandle::setNodeScan(Uint32 scanPtrI) { - return m_node->m_nodeScan; + m_node->m_nodeScan = scanPtrI; } inline Dbtux::Data @@ -1184,47 +1191,54 @@ Dbtux::NodeHandle::getMinMax(unsigned i) return getEnt(i == 0 ? 0 : occup - 1); } -inline void -Dbtux::NodeHandle::setLink(unsigned i, TupAddr addr) -{ - ndbrequire(i <= 2); - m_node->m_link[i] = addr; - m_flags |= DoUpdate; -} +// parameters for methods -inline void -Dbtux::NodeHandle::setSide(unsigned i) +inline +Dbtux::CopyPar::CopyPar() : + m_items(0), + m_headers(true), + m_maxwords(~0), // max unsigned + // output + m_numitems(0), + m_numwords(0) { - // ndbrequire(i <= 1); - m_node->m_side = i; - m_flags |= DoUpdate; } -inline void -Dbtux::NodeHandle::setOccup(unsigned n) +inline +Dbtux::ReadPar::ReadPar() : + m_first(0), + m_count(0), + m_data(0), + m_size(0) { - TreeHead& tree = m_frag.m_tree; - ndbrequire(n <= tree.m_maxOccup); - m_node->m_occup = n; - m_flags |= DoUpdate; } -inline void -Dbtux::NodeHandle::setBalance(int b) +inline +Dbtux::BoundPar::BoundPar() : + m_data1(0), + m_data2(0), + m_count1(0), + m_len2(0), + m_dir(255) { - ndbrequire(abs(b) <= 1); - m_node->m_balance = b; - m_flags |= DoUpdate; } -inline void -Dbtux::NodeHandle::setNodeScan(Uint32 scanPtrI) +#ifdef VM_TRACE +inline +Dbtux::PrintPar::PrintPar() : + // caller fills in + m_path(), + m_side(255), + m_parent(), + // default return values + m_depth(0), + m_occup(0), + m_ok(true) { - m_node->m_nodeScan = scanPtrI; - m_flags |= DoUpdate; } +#endif -// other methods +// utils inline Dbtux::DescEnt& Dbtux::getDescEnt(Uint32 descPage, Uint32 descOff) @@ -1237,6 +1251,17 @@ Dbtux::getDescEnt(Uint32 descPage, Uint32 descOff) return *descEnt; } +inline Uint32 +Dbtux::getTupAddr(const Frag& frag, TreeEnt ent) +{ + const Uint32 tableFragPtrI = frag.m_tupTableFragPtrI[ent.m_fragBit]; + const TupLoc tupLoc = ent.m_tupLoc; + Uint32 tupAddr = NullTupAddr; + c_tup->tuxGetTupAddr(tableFragPtrI, tupLoc.m_pageId, tupLoc.m_pageOffset, tupAddr); + jamEntry(); + return tupAddr; +} + inline unsigned Dbtux::min(unsigned x, unsigned y) { diff --git a/ndb/src/kernel/blocks/dbtux/DbtuxCmp.cpp b/ndb/src/kernel/blocks/dbtux/DbtuxCmp.cpp index 6404cc66213..7601a14a242 100644 --- a/ndb/src/kernel/blocks/dbtux/DbtuxCmp.cpp +++ b/ndb/src/kernel/blocks/dbtux/DbtuxCmp.cpp @@ -18,50 +18,42 @@ #include "Dbtux.hpp" /* - * Search key vs tree entry. + * Search key vs node prefix. * - * Compare search key and index attribute data. The attribute data may - * be partial in which case CmpUnknown may be returned. Also counts how - * many (additional) initial attributes were equal. + * The comparison starts at given attribute position (in fact 0). The + * position is updated by number of equal initial attributes found. The + * prefix may be partial in which case CmpUnknown may be returned. */ int -Dbtux::cmpTreeAttrs(const Frag& frag, CmpPar& cmpPar) +Dbtux::cmpSearchKey(const Frag& frag, unsigned& start, TableData data1, ConstData data2, unsigned maxlen2) { + const unsigned numAttrs = frag.m_numAttrs; const DescEnt& descEnt = getDescEnt(frag.m_descPage, frag.m_descOff); - ConstData data1 = cmpPar.m_data1; - ConstData data2 = cmpPar.m_data2; // number of words of attribute data left - unsigned len2 = cmpPar.m_len2; - const unsigned numAttrs = frag.m_numAttrs; - unsigned index = cmpPar.m_first; - ndbrequire(index < numAttrs); - // skip to right position in search key XXX do it before the call - for (unsigned i = 0; i < index; i++) { - jam(); - data1 += AttributeHeaderSize + data1.ah().getDataSize(); - } - unsigned numEq = 0; + unsigned len2 = maxlen2; + // skip to right position in search key + data1 += start; int ret = 0; - while (index < numAttrs) { + while (start < numAttrs) { if (len2 < AttributeHeaderSize) { jam(); ret = NdbSqlUtil::CmpUnknown; break; } len2 -= AttributeHeaderSize; - if (! data1.ah().isNULL()) { + if (*data1 != 0) { if (! data2.ah().isNULL()) { jam(); // current attribute - const DescAttr& descAttr = descEnt.m_descAttr[index]; + const DescAttr& descAttr = descEnt.m_descAttr[start]; const unsigned typeId = descAttr.m_typeId; // full data size - const unsigned size1 = data1.ah().getDataSize(); + const unsigned size1 = AttributeDescriptor::getSizeInWords(descAttr.m_attrDesc); ndbrequire(size1 != 0 && size1 == data2.ah().getDataSize()); const unsigned size2 = min(size1, len2); len2 -= size2; // compare - const Uint32* const p1 = &data1[AttributeHeaderSize]; + const Uint32* const p1 = *data1; const Uint32* const p2 = &data2[AttributeHeaderSize]; ret = NdbSqlUtil::cmp(typeId, p1, p2, size1, size2); if (ret != 0) { @@ -82,18 +74,71 @@ Dbtux::cmpTreeAttrs(const Frag& frag, CmpPar& cmpPar) break; } } - data1 += AttributeHeaderSize + data1.ah().getDataSize(); + data1 += 1; data2 += AttributeHeaderSize + data2.ah().getDataSize(); - numEq++; - index++; + start++; } // XXX until data format errors are handled ndbrequire(ret != NdbSqlUtil::CmpError); - cmpPar.m_numEq += numEq; // add to previous count return ret; } /* + * Search key vs tree entry. + * + * Start position is updated as in previous routine. + */ +int +Dbtux::cmpSearchKey(const Frag& frag, unsigned& start, TableData data1, TableData data2) +{ + const unsigned numAttrs = frag.m_numAttrs; + const DescEnt& descEnt = getDescEnt(frag.m_descPage, frag.m_descOff); + // skip to right position + data1 += start; + data2 += start; + int ret = 0; + while (start < numAttrs) { + if (*data1 != 0) { + if (*data2 != 0) { + jam(); + // current attribute + const DescAttr& descAttr = descEnt.m_descAttr[start]; + const unsigned typeId = descAttr.m_typeId; + // full data size + const unsigned size1 = AttributeDescriptor::getSizeInWords(descAttr.m_attrDesc); + // compare + const Uint32* const p1 = *data1; + const Uint32* const p2 = *data2; + ret = NdbSqlUtil::cmp(typeId, p1, p2, size1, size1); + if (ret != 0) { + jam(); + break; + } + } else { + jam(); + // not NULL < NULL + ret = -1; + break; + } + } else { + if (*data2 != 0) { + jam(); + // NULL > not NULL + ret = +1; + break; + } + } + data1 += 1; + data2 += 1; + start++; + } + // XXX until data format errors are handled + ndbrequire(ret != NdbSqlUtil::CmpError); + return ret; +} + + +/* * Scan bound vs tree entry. * * Compare lower or upper bound and index attribute data. The attribute diff --git a/ndb/src/kernel/blocks/dbtux/DbtuxDebug.cpp b/ndb/src/kernel/blocks/dbtux/DbtuxDebug.cpp index c5d24205d1a..c4931685305 100644 --- a/ndb/src/kernel/blocks/dbtux/DbtuxDebug.cpp +++ b/ndb/src/kernel/blocks/dbtux/DbtuxDebug.cpp @@ -97,7 +97,7 @@ Dbtux::printTree(Signal* signal, Frag& frag, NdbOut& out) PrintPar par; strcpy(par.m_path, "."); par.m_side = 2; - par.m_parent = NullTupAddr; + par.m_parent = NullTupLoc; printNode(signal, frag, out, tree.m_root, par); out.m_out->flush(); if (! par.m_ok) { @@ -106,26 +106,24 @@ Dbtux::printTree(Signal* signal, Frag& frag, NdbOut& out) signal->theData[1] = 1; execDUMP_STATE_ORD(signal); if (debugFile != 0) { - commitNodes(signal, frag, false); printTree(signal, frag, debugOut); } } ndbrequire(false); } - commitNodes(signal, frag, false); } void -Dbtux::printNode(Signal* signal, Frag& frag, NdbOut& out, TupAddr addr, PrintPar& par) +Dbtux::printNode(Signal* signal, Frag& frag, NdbOut& out, TupLoc loc, PrintPar& par) { - if (addr == NullTupAddr) { + if (loc == NullTupLoc) { par.m_depth = 0; return; } TreeHead& tree = frag.m_tree; - NodeHandlePtr nodePtr; - selectNode(signal, frag, nodePtr, addr, AccFull); - out << par.m_path << " " << *nodePtr.p << endl; + NodeHandle node(frag); + selectNode(signal, node, loc, AccFull); + out << par.m_path << " " << node << endl; // check children PrintPar cpar[2]; ndbrequire(strlen(par.m_path) + 1 < sizeof(par.m_path)); @@ -133,57 +131,57 @@ Dbtux::printNode(Signal* signal, Frag& frag, NdbOut& out, TupAddr addr, PrintPar sprintf(cpar[i].m_path, "%s%c", par.m_path, "LR"[i]); cpar[i].m_side = i; cpar[i].m_depth = 0; - cpar[i].m_parent = addr; - printNode(signal, frag, out, nodePtr.p->getLink(i), cpar[i]); + cpar[i].m_parent = loc; + printNode(signal, frag, out, node.getLink(i), cpar[i]); if (! cpar[i].m_ok) { par.m_ok = false; } } // check child-parent links - if (nodePtr.p->getLink(2) != par.m_parent) { + if (node.getLink(2) != par.m_parent) { par.m_ok = false; out << par.m_path << " *** "; - out << "parent addr " << hex << nodePtr.p->getLink(2); + out << "parent loc " << hex << node.getLink(2); out << " should be " << hex << par.m_parent << endl; } - if (nodePtr.p->getSide() != par.m_side) { + if (node.getSide() != par.m_side) { par.m_ok = false; out << par.m_path << " *** "; - out << "side " << dec << nodePtr.p->getSide(); + out << "side " << dec << node.getSide(); out << " should be " << dec << par.m_side << endl; } // check balance const int balance = -cpar[0].m_depth + cpar[1].m_depth; - if (nodePtr.p->getBalance() != balance) { + if (node.getBalance() != balance) { par.m_ok = false; out << par.m_path << " *** "; - out << "balance " << nodePtr.p->getBalance(); + out << "balance " << node.getBalance(); out << " should be " << balance << endl; } - if (abs(nodePtr.p->getBalance()) > 1) { + if (abs(node.getBalance()) > 1) { par.m_ok = false; out << par.m_path << " *** "; - out << "balance " << nodePtr.p->getBalance() << " is invalid" << endl; + out << "balance " << node.getBalance() << " is invalid" << endl; } // check occupancy - if (nodePtr.p->getOccup() > tree.m_maxOccup) { + if (node.getOccup() > tree.m_maxOccup) { par.m_ok = false; out << par.m_path << " *** "; - out << "occupancy " << nodePtr.p->getOccup(); + out << "occupancy " << node.getOccup(); out << " greater than max " << tree.m_maxOccup << endl; } // check for occupancy of interior node - if (nodePtr.p->getChilds() == 2 && nodePtr.p->getOccup() < tree.m_minOccup) { + if (node.getChilds() == 2 && node.getOccup() < tree.m_minOccup) { par.m_ok = false; out << par.m_path << " *** "; - out << "occupancy " << nodePtr.p->getOccup() << " of interior node"; + out << "occupancy " << node.getOccup() << " of interior node"; out << " less than min " << tree.m_minOccup << endl; } // check missed half-leaf/leaf merge for (unsigned i = 0; i <= 1; i++) { - if (nodePtr.p->getLink(i) != NullTupAddr && - nodePtr.p->getLink(1 - i) == NullTupAddr && - nodePtr.p->getOccup() + cpar[i].m_occup <= tree.m_maxOccup) { + if (node.getLink(i) != NullTupLoc && + node.getLink(1 - i) == NullTupLoc && + node.getOccup() + cpar[i].m_occup <= tree.m_maxOccup) { par.m_ok = false; out << par.m_path << " *** "; out << "missed merge with child " << i << endl; @@ -191,14 +189,26 @@ Dbtux::printNode(Signal* signal, Frag& frag, NdbOut& out, TupAddr addr, PrintPar } // return values par.m_depth = 1 + max(cpar[0].m_depth, cpar[1].m_depth); - par.m_occup = nodePtr.p->getOccup(); + par.m_occup = node.getOccup(); +} + +NdbOut& +operator<<(NdbOut& out, const Dbtux::TupLoc& loc) +{ + if (loc == Dbtux::NullTupLoc) { + out << "null"; + } else { + out << dec << loc.m_pageId; + out << "." << dec << loc.m_pageOffset; + } + return out; } NdbOut& operator<<(NdbOut& out, const Dbtux::TreeEnt& ent) { out << dec << ent.m_fragBit; - out << "-" << hex << ent.m_tupAddr; + out << "-" << ent.m_tupLoc; out << "-" << dec << ent.m_tupVersion; return out; } @@ -206,10 +216,13 @@ operator<<(NdbOut& out, const Dbtux::TreeEnt& ent) NdbOut& operator<<(NdbOut& out, const Dbtux::TreeNode& node) { + Dbtux::TupLoc link0(node.m_linkPI[0], node.m_linkPO[0]); + Dbtux::TupLoc link1(node.m_linkPI[1], node.m_linkPO[1]); + Dbtux::TupLoc link2(node.m_linkPI[2], node.m_linkPO[2]); out << "[TreeNode " << hex << &node; - out << " [left " << hex << node.m_link[0] << "]"; - out << " [right " << hex << node.m_link[1] << "]"; - out << " [up " << hex << node.m_link[2] << "]"; + out << " [left " << link0 << "]"; + out << " [right " << link1 << "]"; + out << " [up " << link2 << "]"; out << " [side " << dec << node.m_side << "]"; out << " [occup " << dec << node.m_occup << "]"; out << " [balance " << dec << (int)node.m_balance << "]"; @@ -238,7 +251,7 @@ NdbOut& operator<<(NdbOut& out, const Dbtux::TreePos& pos) { out << "[TreePos " << hex << &pos; - out << " [addr " << hex << pos.m_addr << "]"; + out << " [loc " << pos.m_loc << "]"; out << " [pos " << dec << pos.m_pos << "]"; out << " [match " << dec << pos.m_match << "]"; out << " [dir " << dec << pos.m_dir << "]"; @@ -251,9 +264,9 @@ NdbOut& operator<<(NdbOut& out, const Dbtux::DescAttr& descAttr) { out << "[DescAttr " << hex << &descAttr; + out << " [attrDesc " << hex << descAttr.m_attrDesc; out << " [primaryAttrId " << dec << descAttr.m_primaryAttrId << "]"; out << " [typeId " << dec << descAttr.m_typeId << "]"; - out << " [nullable " << dec << descAttr.m_nullable << "]"; out << "]"; return out; } @@ -338,9 +351,8 @@ operator<<(NdbOut& out, const Dbtux::NodeHandle& node) const Dbtux::Frag& frag = node.m_frag; const Dbtux::TreeHead& tree = frag.m_tree; out << "[NodeHandle " << hex << &node; - out << " [addr " << hex << node.m_addr << "]"; + out << " [loc " << node.m_loc << "]"; out << " [acc " << dec << node.m_acc << "]"; - out << " [flags " << hex << node.m_flags << "]"; out << " [node " << *node.m_node << "]"; if (node.m_acc >= Dbtux::AccPref) { for (unsigned i = 0; i <= 1; i++) { diff --git a/ndb/src/kernel/blocks/dbtux/DbtuxGen.cpp b/ndb/src/kernel/blocks/dbtux/DbtuxGen.cpp index 082b243bcb1..93a5c78338c 100644 --- a/ndb/src/kernel/blocks/dbtux/DbtuxGen.cpp +++ b/ndb/src/kernel/blocks/dbtux/DbtuxGen.cpp @@ -21,6 +21,7 @@ Dbtux::Dbtux(const Configuration& conf) : SimulatedBlock(DBTUX, conf), + c_tup(0), c_descPageList(RNIL), #ifdef VM_TRACE debugFile(0), @@ -29,15 +30,15 @@ Dbtux::Dbtux(const Configuration& conf) : #endif c_internalStartPhase(0), c_typeOfStart(NodeState::ST_ILLEGAL_TYPE), - c_keyBuffer(0) + c_dataBuffer(0) { BLOCK_CONSTRUCTOR(Dbtux); // verify size assumptions (also when release-compiled) ndbrequire( - (sizeof(DescHead) & 0x3) == 0 && - (sizeof(DescAttr) & 0x3) == 0 && (sizeof(TreeEnt) & 0x3) == 0 && - (sizeof(TreeNode) & 0x3) == 0 + (sizeof(TreeNode) & 0x3) == 0 && + (sizeof(DescHead) & 0x3) == 0 && + (sizeof(DescAttr) & 0x3) == 0 ); /* * DbtuxGen.cpp @@ -123,6 +124,8 @@ Dbtux::execSTTOR(Signal* signal) case 1: jam(); CLEAR_ERROR_INSERT_VALUE; + c_tup = (Dbtup*)globalData.getBlock(DBTUP); + ndbrequire(c_tup != 0); break; case 3: jam(); @@ -175,12 +178,11 @@ Dbtux::execREAD_CONFIG_REQ(Signal* signal) c_fragPool.setSize(nFragment); c_descPagePool.setSize(nDescPage); c_fragOpPool.setSize(MaxIndexFragments); - c_nodeHandlePool.setSize(MaxNodeHandles); c_scanOpPool.setSize(nScanOp); c_scanBoundPool.setSize(nScanBoundWords); /* * Index id is physical array index. We seize and initialize all - * index records now. This assumes ArrayPool is an array. + * index records now. */ IndexPtr indexPtr; while (1) { @@ -193,7 +195,10 @@ Dbtux::execREAD_CONFIG_REQ(Signal* signal) new (indexPtr.p) Index(); } // allocate buffers - c_keyBuffer = (Uint32*)allocRecord("c_keyBuffer", sizeof(Uint64), (MaxAttrDataSize + 1) >> 1); + c_keyAttrs = (Uint32*)allocRecord("c_keyAttrs", sizeof(Uint32), MaxIndexAttributes); + c_searchKey = (TableData)allocRecord("c_searchKey", sizeof(Uint32*), MaxIndexAttributes); + c_entryKey = (TableData)allocRecord("c_entryKey", sizeof(Uint32*), MaxIndexAttributes); + c_dataBuffer = (Uint32*)allocRecord("c_dataBuffer", sizeof(Uint64), (MaxAttrDataSize + 1) >> 1); // ack ReadConfigConf * conf = (ReadConfigConf*)signal->getDataPtrSend(); conf->senderRef = reference(); @@ -205,6 +210,37 @@ Dbtux::execREAD_CONFIG_REQ(Signal* signal) // utils void +Dbtux::setKeyAttrs(const Frag& frag) +{ + Data keyAttrs = c_keyAttrs; // global + const unsigned numAttrs = frag.m_numAttrs; + const DescEnt& descEnt = getDescEnt(frag.m_descPage, frag.m_descOff); + for (unsigned i = 0; i < numAttrs; i++) { + const DescAttr& descAttr = descEnt.m_descAttr[i]; + Uint32 size = AttributeDescriptor::getSizeInWords(descAttr.m_attrDesc); + // set attr id and fixed size + keyAttrs.ah() = AttributeHeader(descAttr.m_primaryAttrId, size); + keyAttrs += 1; + } +} + +void +Dbtux::readKeyAttrs(const Frag& frag, TreeEnt ent, unsigned start, TableData keyData) +{ + ConstData keyAttrs = c_keyAttrs; // global + const Uint32 tableFragPtrI = frag.m_tupTableFragPtrI[ent.m_fragBit]; + const TupLoc tupLoc = ent.m_tupLoc; + const Uint32 tupVersion = ent.m_tupVersion; + ndbrequire(start < frag.m_numAttrs); + const unsigned numAttrs = frag.m_numAttrs - start; + // start applies to both keys and output data + keyAttrs += start; + keyData += start; + c_tup->tuxReadAttrs(tableFragPtrI, tupLoc.m_pageId, tupLoc.m_pageOffset, tupVersion, numAttrs, keyAttrs, keyData); + jamEntry(); +} + +void Dbtux::copyAttrs(Data dst, ConstData src, CopyPar& copyPar) { CopyPar c = copyPar; @@ -238,4 +274,46 @@ Dbtux::copyAttrs(Data dst, ConstData src, CopyPar& copyPar) copyPar = c; } +/* + * Input is pointers to table attributes. Output is array of attribute + * data with headers. Copies whatever fits. + */ +void +Dbtux::copyAttrs(const Frag& frag, TableData data1, Data data2, unsigned maxlen2) +{ + ConstData keyAttrs = c_keyAttrs; // global + const unsigned numAttrs = frag.m_numAttrs; + unsigned len2 = maxlen2; + for (unsigned n = 0; n < numAttrs; n++) { + jam(); + const unsigned attrId = keyAttrs.ah().getAttributeId(); + const unsigned dataSize = keyAttrs.ah().getDataSize(); + const Uint32* const p1 = *data1; + if (p1 != 0) { + if (len2 == 0) + return; + data2.ah() = AttributeHeader(attrId, dataSize); + data2 += 1; + len2 -= 1; + unsigned n = dataSize; + for (unsigned i = 0; i < dataSize; i++) { + if (len2 == 0) + return; + *data2 = p1[i]; + data2 += 1; + len2 -= 1; + } + } else { + if (len2 == 0) + return; + data2.ah() = AttributeHeader(attrId, 0); + data2.ah().setNULL(); + data2 += 1; + len2 -= 1; + } + keyAttrs += 1; + data1 += 1; + } +} + BLOCK_FUNCTIONS(Dbtux); diff --git a/ndb/src/kernel/blocks/dbtux/DbtuxMaint.cpp b/ndb/src/kernel/blocks/dbtux/DbtuxMaint.cpp index a6b2485067c..fc72611a273 100644 --- a/ndb/src/kernel/blocks/dbtux/DbtuxMaint.cpp +++ b/ndb/src/kernel/blocks/dbtux/DbtuxMaint.cpp @@ -33,11 +33,12 @@ Dbtux::execTUX_MAINT_REQ(Signal* signal) jam(); #ifdef VM_TRACE if (debugFlags & DebugMaint) { + TupLoc tupLoc(sig->pageId, sig->pageOffset); debugOut << "opInfo=" << hex << sig->opInfo; debugOut << " tableId=" << dec << sig->tableId; debugOut << " indexId=" << dec << sig->indexId; debugOut << " fragId=" << dec << sig->fragId; - debugOut << " tupAddr=" << hex << sig->tupAddr; + debugOut << " tupLoc=" << tupLoc; debugOut << " tupVersion=" << dec << sig->tupVersion; debugOut << " -- ignored at ISP=" << dec << c_internalStartPhase; debugOut << " TOS=" << dec << c_typeOfStart; @@ -72,31 +73,25 @@ Dbtux::execTUX_MAINT_REQ(Signal* signal) } ndbrequire(fragPtr.i != RNIL); Frag& frag = *fragPtr.p; - ndbrequire(frag.m_nodeList == RNIL); - // set up index entry + // set up index keys for this operation + setKeyAttrs(frag); + // set up search entry TreeEnt ent; - ent.m_tupAddr = req->tupAddr; + ent.m_tupLoc = TupLoc(req->pageId, req->pageOffset); ent.m_tupVersion = req->tupVersion; ent.m_fragBit = fragBit; // read search key - ReadPar readPar; - readPar.m_ent = ent; - readPar.m_first = 0; - readPar.m_count = frag.m_numAttrs; - // output goes here - readPar.m_data = c_keyBuffer; - tupReadAttrs(signal, frag, readPar); + readKeyAttrs(frag, ent, 0, c_searchKey); // check if all keys are null { + const unsigned numAttrs = frag.m_numAttrs; bool allNull = true; - ConstData data = readPar.m_data; - for (unsigned i = 0; i < frag.m_numAttrs; i++) { - if (! data.ah().isNULL()) { + for (unsigned i = 0; i < numAttrs; i++) { + if (c_searchKey[i] != 0) { jam(); allNull = false; break; } - data += AttributeHeaderSize + data.ah().getDataSize(); } if (allNull) { jam(); @@ -105,11 +100,6 @@ Dbtux::execTUX_MAINT_REQ(Signal* signal) return; } } - // find position in tree - SearchPar searchPar; - searchPar.m_data = c_keyBuffer; - searchPar.m_ent = ent; - TreePos treePos; #ifdef VM_TRACE if (debugFlags & DebugMaint) { debugOut << "opCode=" << dec << opCode; @@ -121,7 +111,9 @@ Dbtux::execTUX_MAINT_REQ(Signal* signal) debugOut << endl; } #endif - treeSearch(signal, frag, searchPar, treePos); + // find position in tree + TreePos treePos; + treeSearch(signal, frag, c_searchKey, ent, treePos); #ifdef VM_TRACE if (debugFlags & DebugMaint) { debugOut << treePos << endl; @@ -143,17 +135,18 @@ Dbtux::execTUX_MAINT_REQ(Signal* signal) } /* * At most one new node is inserted in the operation. We keep one - * free node pre-allocated so the operation cannot fail. This also - * gives a real TupAddr for links to the new node. + * free node pre-allocated so the operation cannot fail. */ - if (frag.m_nodeFree == RNIL) { + if (frag.m_freeLoc == NullTupLoc) { jam(); - preallocNode(signal, frag, req->errorCode); + NodeHandle node(frag); + req->errorCode = allocNode(signal, node); if (req->errorCode != 0) { jam(); break; } - ndbrequire(frag.m_nodeFree != RNIL); + frag.m_freeLoc = node.m_loc; + ndbrequire(frag.m_freeLoc != NullTupLoc); } treeAdd(signal, frag, treePos, ent); break; @@ -175,7 +168,6 @@ Dbtux::execTUX_MAINT_REQ(Signal* signal) break; } // commit and release nodes - commitNodes(signal, frag, req->errorCode == 0); #ifdef VM_TRACE if (debugFlags & DebugTree) { printTree(signal, frag, debugOut); @@ -199,11 +191,11 @@ Dbtux::tupReadAttrs(Signal* signal, const Frag& frag, ReadPar& readPar) req->requestInfo = 0; req->tableId = frag.m_tableId; req->fragId = frag.m_fragId | (ent.m_fragBit << frag.m_fragOff); - req->fragPtrI = RNIL; - req->tupAddr = ent.m_tupAddr; + req->fragPtrI = frag.m_tupTableFragPtrI[ent.m_fragBit]; + req->tupAddr = (Uint32)-1; req->tupVersion = ent.m_tupVersion; - req->pageId = RNIL; - req->pageOffset = 0; + req->pageId = ent.m_tupLoc.m_pageId; + req->pageOffset = ent.m_tupLoc.m_pageOffset; req->bufferId = 0; // add count and list of attribute ids Data data = (Uint32*)req + TupReadAttrs::SignalLength; @@ -246,11 +238,11 @@ Dbtux::tupReadKeys(Signal* signal, const Frag& frag, ReadPar& readPar) req->requestInfo = TupReadAttrs::ReadKeys; req->tableId = frag.m_tableId; req->fragId = frag.m_fragId | (ent.m_fragBit << frag.m_fragOff); - req->fragPtrI = RNIL; - req->tupAddr = ent.m_tupAddr; + req->fragPtrI = frag.m_tupTableFragPtrI[ent.m_fragBit]; + req->tupAddr = (Uint32)-1; req->tupVersion = RNIL; // not used - req->pageId = RNIL; - req->pageOffset = 0; + req->pageId = ent.m_tupLoc.m_pageId; + req->pageOffset = ent.m_tupLoc.m_pageOffset; req->bufferId = 0; // execute EXECUTE_DIRECT(DBTUP, GSN_TUP_READ_ATTRS, signal, TupReadAttrs::SignalLength); @@ -270,100 +262,3 @@ Dbtux::tupReadKeys(Signal* signal, const Frag& frag, ReadPar& readPar) readPar.m_count = numKeys; readPar.m_size = copyPar.m_numwords; } - -/* - * Operate on index node tuple in TUP. The data is copied between node - * cache and index storage via signal data. - */ -void -Dbtux::tupStoreTh(Signal* signal, const Frag& frag, NodeHandlePtr nodePtr, StorePar storePar) -{ - const TreeHead& tree = frag.m_tree; - // define the direct signal - TupStoreTh* req = (TupStoreTh*)signal->getDataPtrSend(); - req->errorCode = RNIL; - req->tableId = frag.m_indexId; - req->fragId = frag.m_fragId; - req->fragPtrI = RNIL; - req->tupAddr = nodePtr.p->m_addr; - req->tupVersion = 0; - req->pageId = nodePtr.p->m_loc.m_pageId; - req->pageOffset = nodePtr.p->m_loc.m_pageOffset; - req->bufferId = 0; - req->opCode = storePar.m_opCode; - ndbrequire(storePar.m_offset + storePar.m_size <= tree.m_nodeSize); - req->dataOffset = storePar.m_offset; - req->dataSize = storePar.m_size; - // the node cache - ndbrequire(nodePtr.p->m_node != 0); - // the buffer in signal data - Uint32* const buffer = (Uint32*)req + TupStoreTh::SignalLength; - // copy in data - switch (storePar.m_opCode) { - case TupStoreTh::OpRead: - jam(); - #ifdef VM_TRACE - { - Uint32* dst = buffer + storePar.m_offset; - memset(dst, 0xa9, storePar.m_size << 2); - } - #endif - break; - case TupStoreTh::OpInsert: - jam(); - // fallthru - case TupStoreTh::OpUpdate: - jam(); - // copy from cache to signal data - { - Uint32* dst = buffer + storePar.m_offset; - const Uint32* src = (const Uint32*)nodePtr.p->m_node + storePar.m_offset; - memcpy(dst, src, storePar.m_size << 2); - } - break; - case TupStoreTh::OpDelete: - jam(); - break; - default: - ndbrequire(false); - break; - } - // execute - EXECUTE_DIRECT(DBTUP, GSN_TUP_STORE_TH, signal, TupStoreTh::SignalLength); - jamEntry(); - if (req->errorCode != 0) { - jam(); - storePar.m_errorCode = req->errorCode; - return; - } - ndbrequire(req->errorCode == 0); - // copy out data - switch (storePar.m_opCode) { - case TupStoreTh::OpRead: - jam(); - { - Uint32* dst = (Uint32*)nodePtr.p->m_node + storePar.m_offset; - const Uint32* src = (const Uint32*)buffer + storePar.m_offset; - memcpy(dst, src, storePar.m_size << 2); - } - // fallthru - case TupStoreTh::OpInsert: - jam(); - // fallthru - case TupStoreTh::OpUpdate: - jam(); - nodePtr.p->m_addr = req->tupAddr; - nodePtr.p->m_loc.m_pageId = req->pageId; - nodePtr.p->m_loc.m_pageOffset = req->pageOffset; - break; - case TupStoreTh::OpDelete: - jam(); - nodePtr.p->m_addr = NullTupAddr; - nodePtr.p->m_loc.m_pageId = RNIL; - nodePtr.p->m_loc.m_pageOffset = 0; - break; - default: - ndbrequire(false); - break; - } -} diff --git a/ndb/src/kernel/blocks/dbtux/DbtuxMeta.cpp b/ndb/src/kernel/blocks/dbtux/DbtuxMeta.cpp index 2ffd599429c..0612f191830 100644 --- a/ndb/src/kernel/blocks/dbtux/DbtuxMeta.cpp +++ b/ndb/src/kernel/blocks/dbtux/DbtuxMeta.cpp @@ -85,6 +85,11 @@ Dbtux::execTUXFRAGREQ(Signal* signal) fragPtr.p->m_fragOff = req->fragOff; fragPtr.p->m_fragId = req->fragId; fragPtr.p->m_numAttrs = req->noOfAttr; + fragPtr.p->m_tupIndexFragPtrI = req->tupIndexFragPtrI; + fragPtr.p->m_tupTableFragPtrI[0] = req->tupTableFragPtrI[0]; + fragPtr.p->m_tupTableFragPtrI[1] = req->tupTableFragPtrI[1]; + fragPtr.p->m_accTableFragPtrI[0] = req->accTableFragPtrI[0]; + fragPtr.p->m_accTableFragPtrI[1] = req->accTableFragPtrI[1]; // add the fragment to the index indexPtr.p->m_fragId[indexPtr.p->m_numFrags] = req->fragId; indexPtr.p->m_fragPtrI[indexPtr.p->m_numFrags] = fragPtr.i; @@ -176,10 +181,9 @@ Dbtux::execTUX_ADD_ATTRREQ(Signal* signal) // define the attribute DescEnt& descEnt = getDescEnt(indexPtr.p->m_descPage, indexPtr.p->m_descOff); DescAttr& descAttr = descEnt.m_descAttr[attrId]; + descAttr.m_attrDesc = req->attrDescriptor; descAttr.m_primaryAttrId = req->primaryAttrId; descAttr.m_typeId = req->extTypeInfo & 0xFF; - descAttr.m_nullable = AttributeDescriptor::getNullable(req->attrDescriptor); - descAttr.pad1 = 0; #ifdef VM_TRACE if (debugFlags & DebugMeta) { debugOut << "Add frag " << fragPtr.i << " attr " << attrId << " " << descAttr << endl; @@ -197,6 +201,7 @@ Dbtux::execTUX_ADD_ATTRREQ(Signal* signal) jam(); // initialize tree header TreeHead& tree = fragPtr.p->m_tree; + new (&tree) TreeHead(); // make these configurable later tree.m_nodeSize = MAX_TTREE_NODE_SIZE; tree.m_prefSize = MAX_TTREE_PREF_SIZE; @@ -222,8 +227,8 @@ Dbtux::execTUX_ADD_ATTRREQ(Signal* signal) break; } tree.m_minOccup = tree.m_maxOccup - maxSlack; - // root node does not exist - tree.m_root = NullTupAddr; + // root node does not exist (also set by ctor) + tree.m_root = NullTupLoc; // fragment is defined c_fragOpPool.release(fragOpPtr); } @@ -310,12 +315,6 @@ Dbtux::dropIndex(Signal* signal, IndexPtr indexPtr, Uint32 senderRef, Uint32 sen unsigned i = --indexPtr.p->m_numFrags; FragPtr fragPtr; c_fragPool.getPtr(fragPtr, indexPtr.p->m_fragPtrI[i]); - Frag& frag = *fragPtr.p; - ndbrequire(frag.m_nodeList == RNIL); - if (frag.m_nodeFree != RNIL) { - c_nodeHandlePool.release(frag.m_nodeFree); - frag.m_nodeFree = RNIL; - } c_fragPool.release(fragPtr); // the real time break is not used for anything currently signal->theData[0] = TuxContinueB::DropIndex; diff --git a/ndb/src/kernel/blocks/dbtux/DbtuxNode.cpp b/ndb/src/kernel/blocks/dbtux/DbtuxNode.cpp index 6733a87da97..c969e35dc82 100644 --- a/ndb/src/kernel/blocks/dbtux/DbtuxNode.cpp +++ b/ndb/src/kernel/blocks/dbtux/DbtuxNode.cpp @@ -18,241 +18,113 @@ #include "Dbtux.hpp" /* - * Node handles. - * - * We use the "cache" implementation. Node operations are done on - * cached copies. Index memory is updated at the end of the operation. - * At most one node is inserted and it is always pre-allocated. - * - * An alternative "pointer" implementation which writes directly into - * index memory is planned for later. + * Allocate index node in TUP. */ - -// Dbtux - -void -Dbtux::seizeNode(Signal* signal, Frag& frag, NodeHandlePtr& nodePtr) -{ - if (! c_nodeHandlePool.seize(nodePtr)) { - jam(); - return; - } - new (nodePtr.p) NodeHandle(*this, frag); - nodePtr.p->m_next = frag.m_nodeList; - frag.m_nodeList = nodePtr.i; - // node cache used always - nodePtr.p->m_node = (TreeNode*)nodePtr.p->m_cache; - new (nodePtr.p->m_node) TreeNode(); -#ifdef VM_TRACE - TreeHead& tree = frag.m_tree; - TreeNode* node = nodePtr.p->m_node; - memset(tree.getPref(node, 0), 0xa2, tree.m_prefSize << 2); - memset(tree.getPref(node, 1), 0xa2, tree.m_prefSize << 2); - TreeEnt* entList = tree.getEntList(node); - memset(entList, 0xa4, (tree.m_maxOccup + 1) * (TreeEntSize << 2)); -#endif -} - -void -Dbtux::preallocNode(Signal* signal, Frag& frag, Uint32& errorCode) -{ - ndbrequire(frag.m_nodeFree == RNIL); - NodeHandlePtr nodePtr; - seizeNode(signal, frag, nodePtr); - ndbrequire(nodePtr.i != RNIL); - // remove from cache XXX ugly - frag.m_nodeFree = frag.m_nodeList; - frag.m_nodeList = nodePtr.p->m_next; - StorePar storePar; - storePar.m_opCode = TupStoreTh::OpInsert; - storePar.m_offset = 0; - storePar.m_size = 0; - tupStoreTh(signal, frag, nodePtr, storePar); - if (storePar.m_errorCode != 0) { - jam(); - errorCode = storePar.m_errorCode; - c_nodeHandlePool.release(nodePtr); - frag.m_nodeFree = RNIL; - } -} - -/* - * Find node in the cache. XXX too slow, use direct links instead - */ -void -Dbtux::findNode(Signal* signal, Frag& frag, NodeHandlePtr& nodePtr, TupAddr addr) -{ - NodeHandlePtr tmpPtr; - tmpPtr.i = frag.m_nodeList; - while (tmpPtr.i != RNIL) { - jam(); - c_nodeHandlePool.getPtr(tmpPtr); - if (tmpPtr.p->m_addr == addr) { - jam(); - nodePtr = tmpPtr; - return; - } - tmpPtr.i = tmpPtr.p->m_next; - } - nodePtr.i = RNIL; - nodePtr.p = 0; -} - -/* - * Get handle for existing node. - */ -void -Dbtux::selectNode(Signal* signal, Frag& frag, NodeHandlePtr& nodePtr, TupAddr addr, AccSize acc) +int +Dbtux::allocNode(Signal* signal, NodeHandle& node) { - ndbrequire(addr != NullTupAddr && acc > AccNone); - NodeHandlePtr tmpPtr; - // search in cache - findNode(signal, frag, tmpPtr, addr); - if (tmpPtr.i == RNIL) { + Frag& frag = node.m_frag; + Uint32 pageId = NullTupLoc.m_pageId; + Uint32 pageOffset = NullTupLoc.m_pageOffset; + Uint32* node32 = 0; + int errorCode = c_tup->tuxAllocNode(signal, frag.m_tupIndexFragPtrI, pageId, pageOffset, node32); + jamEntry(); + if (errorCode == 0) { jam(); - // add new node - seizeNode(signal, frag, tmpPtr); - ndbrequire(tmpPtr.i != RNIL); - tmpPtr.p->m_addr = addr; + node.m_loc = TupLoc(pageId, pageOffset); + node.m_node = reinterpret_cast<TreeNode*>(node32); + node.m_acc = AccNone; + ndbrequire(node.m_loc != NullTupLoc && node.m_node != 0); } - if (tmpPtr.p->m_acc < acc) { - jam(); - accessNode(signal, frag, tmpPtr, acc); - } - nodePtr = tmpPtr; + return errorCode; } /* - * Create new node in the cache and mark it for insert. + * Access more of the node. */ void -Dbtux::insertNode(Signal* signal, Frag& frag, NodeHandlePtr& nodePtr, AccSize acc) +Dbtux::accessNode(Signal* signal, NodeHandle& node, AccSize acc) { - ndbrequire(acc > AccNone); - NodeHandlePtr tmpPtr; - // use the pre-allocated node - tmpPtr.i = frag.m_nodeFree; - frag.m_nodeFree = RNIL; - c_nodeHandlePool.getPtr(tmpPtr); - // move it to the cache - tmpPtr.p->m_next = frag.m_nodeList; - frag.m_nodeList = tmpPtr.i; - tmpPtr.p->m_acc = acc; - tmpPtr.p->m_flags |= NodeHandle::DoInsert; - nodePtr = tmpPtr; + ndbrequire(node.m_loc != NullTupLoc && node.m_node != 0); + if (node.m_acc >= acc) + return; + // XXX could do prefetch + node.m_acc = acc; } /* - * Mark existing node for deletion. + * Set handle to point to existing node. */ void -Dbtux::deleteNode(Signal* signal, Frag& frag, NodeHandlePtr& nodePtr) +Dbtux::selectNode(Signal* signal, NodeHandle& node, TupLoc loc, AccSize acc) { - NodeHandlePtr tmpPtr = nodePtr; - ndbrequire(tmpPtr.p->getOccup() == 0); - tmpPtr.p->m_flags |= NodeHandle::DoDelete; - // scans have already been moved by popDown or popUp + Frag& frag = node.m_frag; + ndbrequire(loc != NullTupLoc); + Uint32 pageId = loc.m_pageId; + Uint32 pageOffset = loc.m_pageOffset; + Uint32* node32 = 0; + c_tup->tuxGetNode(frag.m_tupIndexFragPtrI, pageId, pageOffset, node32); + jamEntry(); + node.m_loc = loc; + node.m_node = reinterpret_cast<TreeNode*>(node32); + node.m_acc = AccNone; + ndbrequire(node.m_loc != NullTupLoc && node.m_node != 0); + accessNode(signal, node, acc); } /* - * Access more of the node. + * Set handle to point to new node. Uses the pre-allocated node. */ void -Dbtux::accessNode(Signal* signal, Frag& frag, NodeHandlePtr& nodePtr, AccSize acc) +Dbtux::insertNode(Signal* signal, NodeHandle& node, AccSize acc) { + Frag& frag = node.m_frag; + TupLoc loc = frag.m_freeLoc; + frag.m_freeLoc = NullTupLoc; + selectNode(signal, node, loc, acc); + new (node.m_node) TreeNode(); +#ifdef VM_TRACE TreeHead& tree = frag.m_tree; - NodeHandlePtr tmpPtr = nodePtr; - if (tmpPtr.p->m_acc >= acc) - return; - if (! (tmpPtr.p->m_flags & NodeHandle::DoInsert)) { - jam(); - StorePar storePar; - storePar.m_opCode = TupStoreTh::OpRead; - storePar.m_offset = tree.getSize(tmpPtr.p->m_acc); - storePar.m_size = tree.getSize(acc) - tree.getSize(tmpPtr.p->m_acc); - tmpPtr.p->m_tux.tupStoreTh(signal, frag, tmpPtr, storePar); - ndbrequire(storePar.m_errorCode == 0); - } - tmpPtr.p->m_acc = acc; + memset(node.getPref(0), 0xa2, tree.m_prefSize << 2); + memset(node.getPref(1), 0xa2, tree.m_prefSize << 2); + TreeEnt* entList = tree.getEntList(node.m_node); + memset(entList, 0xa4, (tree.m_maxOccup + 1) * (TreeEntSize << 2)); +#endif } /* - * Set prefix. + * Delete existing node. */ void -Dbtux::setNodePref(Signal* signal, Frag& frag, NodeHandlePtr& nodePtr, unsigned i) +Dbtux::deleteNode(Signal* signal, NodeHandle& node) { - TreeHead& tree = frag.m_tree; - NodeHandlePtr tmpPtr = nodePtr; - ReadPar readPar; - ndbrequire(i <= 1); - readPar.m_ent = tmpPtr.p->getMinMax(i); - readPar.m_first = 0; - readPar.m_count = frag.m_numAttrs; - // leave in signal data - readPar.m_data = 0; - // XXX implement max words to read - tupReadAttrs(signal, frag, readPar); - // copy whatever fits - CopyPar copyPar; - copyPar.m_items = readPar.m_count; - copyPar.m_headers = true; - copyPar.m_maxwords = tree.m_prefSize; - Data pref = tmpPtr.p->getPref(i); - copyAttrs(pref, readPar.m_data, copyPar); - nodePtr.p->m_flags |= NodeHandle::DoUpdate; + Frag& frag = node.m_frag; + ndbrequire(node.getOccup() == 0); + TupLoc loc = node.m_loc; + Uint32 pageId = loc.m_pageId; + Uint32 pageOffset = loc.m_pageOffset; + Uint32* node32 = reinterpret_cast<Uint32*>(node.m_node); + c_tup->tuxFreeNode(signal, frag.m_tupIndexFragPtrI, pageId, pageOffset, node32); + jamEntry(); + // invalidate handle and storage + node.m_loc = NullTupLoc; + node.m_node = 0; } /* - * Commit and release nodes at the end of an operation. Used also on - * error since no changes have been made (updateOk false). + * Set prefix. Copies the number of words that fits. Includes + * attribute headers for now. XXX use null mask instead */ void -Dbtux::commitNodes(Signal* signal, Frag& frag, bool updateOk) +Dbtux::setNodePref(Signal* signal, NodeHandle& node, unsigned i) { - TreeHead& tree = frag.m_tree; - NodeHandlePtr nodePtr; - nodePtr.i = frag.m_nodeList; - frag.m_nodeList = RNIL; - while (nodePtr.i != RNIL) { - c_nodeHandlePool.getPtr(nodePtr); - const unsigned flags = nodePtr.p->m_flags; - if (flags & NodeHandle::DoDelete) { - jam(); - ndbrequire(updateOk); - // delete - StorePar storePar; - storePar.m_opCode = TupStoreTh::OpDelete; - nodePtr.p->m_tux.tupStoreTh(signal, frag, nodePtr, storePar); - ndbrequire(storePar.m_errorCode == 0); - } else if (flags & NodeHandle::DoUpdate) { - jam(); - ndbrequire(updateOk); - // set prefixes - if (flags & (1 << 0)) { - jam(); - setNodePref(signal, frag, nodePtr, 0); - } - if (flags & (1 << 1)) { - jam(); - setNodePref(signal, frag, nodePtr, 1); - } - // update - StorePar storePar; - storePar.m_opCode = TupStoreTh::OpUpdate; - storePar.m_offset = 0; - storePar.m_size = tree.getSize(nodePtr.p->m_acc); - nodePtr.p->m_tux.tupStoreTh(signal, frag, nodePtr, storePar); - ndbrequire(storePar.m_errorCode == 0); - } - // release - NodeHandlePtr tmpPtr = nodePtr; - nodePtr.i = nodePtr.p->m_next; - c_nodeHandlePool.release(tmpPtr); - } + const Frag& frag = node.m_frag; + const TreeHead& tree = frag.m_tree; + readKeyAttrs(frag, node.getMinMax(i), 0, c_entryKey); + copyAttrs(frag, c_entryKey, node.getPref(i), tree.m_prefSize); } -// Dbtux::NodeHandle +// node operations /* * Add entry at position. Move entries greater than or equal to the old @@ -264,25 +136,26 @@ Dbtux::commitNodes(Signal* signal, Frag& frag, bool updateOk) * 0 1 2 3 4 5 6 0 1 2 3 4 5 6 */ void -Dbtux::NodeHandle::pushUp(Signal* signal, unsigned pos, const TreeEnt& ent) +Dbtux::nodePushUp(Signal* signal, NodeHandle& node, unsigned pos, const TreeEnt& ent) { - TreeHead& tree = m_frag.m_tree; - const unsigned occup = getOccup(); + Frag& frag = node.m_frag; + TreeHead& tree = frag.m_tree; + const unsigned occup = node.getOccup(); ndbrequire(occup < tree.m_maxOccup && pos <= occup); // fix scans ScanOpPtr scanPtr; - scanPtr.i = getNodeScan(); + scanPtr.i = node.getNodeScan(); while (scanPtr.i != RNIL) { jam(); - m_tux.c_scanOpPool.getPtr(scanPtr); + c_scanOpPool.getPtr(scanPtr); TreePos& scanPos = scanPtr.p->m_scanPos; - ndbrequire(scanPos.m_addr == m_addr && scanPos.m_pos < occup); + ndbrequire(scanPos.m_loc == node.m_loc && scanPos.m_pos < occup); if (scanPos.m_pos >= pos) { jam(); #ifdef VM_TRACE - if (m_tux.debugFlags & m_tux.DebugScan) { - m_tux.debugOut << "Fix scan " << scanPtr.i << " " << *scanPtr.p << endl; - m_tux.debugOut << "At pushUp pos=" << pos << " " << *this << endl; + if (debugFlags & DebugScan) { + debugOut << "Fix scan " << scanPtr.i << " " << *scanPtr.p << endl; + debugOut << "At pushUp pos=" << pos << " " << node << endl; } #endif scanPos.m_pos++; @@ -290,7 +163,7 @@ Dbtux::NodeHandle::pushUp(Signal* signal, unsigned pos, const TreeEnt& ent) scanPtr.i = scanPtr.p->m_nodeScan; } // fix node - TreeEnt* const entList = tree.getEntList(m_node); + TreeEnt* const entList = tree.getEntList(node.m_node); entList[occup] = entList[0]; TreeEnt* const tmpList = entList + 1; for (unsigned i = occup; i > pos; i--) { @@ -298,18 +171,18 @@ Dbtux::NodeHandle::pushUp(Signal* signal, unsigned pos, const TreeEnt& ent) tmpList[i] = tmpList[i - 1]; } tmpList[pos] = ent; + entList[0] = entList[occup + 1]; + node.setOccup(occup + 1); + // fix prefixes if (occup == 0 || pos == 0) - m_flags |= (1 << 0); + setNodePref(signal, node, 0); if (occup == 0 || pos == occup) - m_flags |= (1 << 1); - entList[0] = entList[occup + 1]; - setOccup(occup + 1); - m_flags |= DoUpdate; + setNodePref(signal, node, 1); } /* * Remove and return entry at position. Move entries greater than the - * removed one to the left. This is the opposite of pushUp. + * removed one to the left. This is the opposite of nodePushUp. * * D * ^ ^ @@ -317,46 +190,47 @@ Dbtux::NodeHandle::pushUp(Signal* signal, unsigned pos, const TreeEnt& ent) * 0 1 2 3 4 5 6 0 1 2 3 4 5 6 */ void -Dbtux::NodeHandle::popDown(Signal* signal, unsigned pos, TreeEnt& ent) +Dbtux::nodePopDown(Signal* signal, NodeHandle& node, unsigned pos, TreeEnt& ent) { - TreeHead& tree = m_frag.m_tree; - const unsigned occup = getOccup(); + Frag& frag = node.m_frag; + TreeHead& tree = frag.m_tree; + const unsigned occup = node.getOccup(); ndbrequire(occup <= tree.m_maxOccup && pos < occup); ScanOpPtr scanPtr; // move scans whose entry disappears - scanPtr.i = getNodeScan(); + scanPtr.i = node.getNodeScan(); while (scanPtr.i != RNIL) { jam(); - m_tux.c_scanOpPool.getPtr(scanPtr); + c_scanOpPool.getPtr(scanPtr); TreePos& scanPos = scanPtr.p->m_scanPos; - ndbrequire(scanPos.m_addr == m_addr && scanPos.m_pos < occup); + ndbrequire(scanPos.m_loc == node.m_loc && scanPos.m_pos < occup); const Uint32 nextPtrI = scanPtr.p->m_nodeScan; if (scanPos.m_pos == pos) { jam(); #ifdef VM_TRACE - if (m_tux.debugFlags & m_tux.DebugScan) { - m_tux.debugOut << "Move scan " << scanPtr.i << " " << *scanPtr.p << endl; - m_tux.debugOut << "At popDown pos=" << pos << " " << *this << endl; + if (debugFlags & DebugScan) { + debugOut << "Move scan " << scanPtr.i << " " << *scanPtr.p << endl; + debugOut << "At popDown pos=" << pos << " " << node << endl; } #endif - m_tux.scanNext(signal, scanPtr); + scanNext(signal, scanPtr); } scanPtr.i = nextPtrI; } // fix other scans - scanPtr.i = getNodeScan(); + scanPtr.i = node.getNodeScan(); while (scanPtr.i != RNIL) { jam(); - m_tux.c_scanOpPool.getPtr(scanPtr); + c_scanOpPool.getPtr(scanPtr); TreePos& scanPos = scanPtr.p->m_scanPos; - ndbrequire(scanPos.m_addr == m_addr && scanPos.m_pos < occup); + ndbrequire(scanPos.m_loc == node.m_loc && scanPos.m_pos < occup); ndbrequire(scanPos.m_pos != pos); if (scanPos.m_pos > pos) { jam(); #ifdef VM_TRACE - if (m_tux.debugFlags & m_tux.DebugScan) { - m_tux.debugOut << "Fix scan " << scanPtr.i << " " << *scanPtr.p << endl; - m_tux.debugOut << "At popDown pos=" << pos << " " << *this << endl; + if (debugFlags & DebugScan) { + debugOut << "Fix scan " << scanPtr.i << " " << *scanPtr.p << endl; + debugOut << "At popDown pos=" << pos << " " << node << endl; } #endif scanPos.m_pos--; @@ -364,7 +238,7 @@ Dbtux::NodeHandle::popDown(Signal* signal, unsigned pos, TreeEnt& ent) scanPtr.i = scanPtr.p->m_nodeScan; } // fix node - TreeEnt* const entList = tree.getEntList(m_node); + TreeEnt* const entList = tree.getEntList(node.m_node); entList[occup] = entList[0]; TreeEnt* const tmpList = entList + 1; ent = tmpList[pos]; @@ -372,13 +246,13 @@ Dbtux::NodeHandle::popDown(Signal* signal, unsigned pos, TreeEnt& ent) jam(); tmpList[i] = tmpList[i + 1]; } + entList[0] = entList[occup - 1]; + node.setOccup(occup - 1); + // fix prefixes if (occup != 1 && pos == 0) - m_flags |= (1 << 0); + setNodePref(signal, node, 0); if (occup != 1 && pos == occup - 1) - m_flags |= (1 << 1); - entList[0] = entList[occup - 1]; - setOccup(occup - 1); - m_flags |= DoUpdate; + setNodePref(signal, node, 1); } /* @@ -391,47 +265,48 @@ Dbtux::NodeHandle::popDown(Signal* signal, unsigned pos, TreeEnt& ent) * 0 1 2 3 4 5 6 0 1 2 3 4 5 6 */ void -Dbtux::NodeHandle::pushDown(Signal* signal, unsigned pos, TreeEnt& ent) +Dbtux::nodePushDown(Signal* signal, NodeHandle& node, unsigned pos, TreeEnt& ent) { - TreeHead& tree = m_frag.m_tree; - const unsigned occup = getOccup(); + Frag& frag = node.m_frag; + TreeHead& tree = frag.m_tree; + const unsigned occup = node.getOccup(); ndbrequire(occup <= tree.m_maxOccup && pos < occup); ScanOpPtr scanPtr; // move scans whose entry disappears - scanPtr.i = getNodeScan(); + scanPtr.i = node.getNodeScan(); while (scanPtr.i != RNIL) { jam(); - m_tux.c_scanOpPool.getPtr(scanPtr); + c_scanOpPool.getPtr(scanPtr); TreePos& scanPos = scanPtr.p->m_scanPos; - ndbrequire(scanPos.m_addr == m_addr && scanPos.m_pos < occup); + ndbrequire(scanPos.m_loc == node.m_loc && scanPos.m_pos < occup); const Uint32 nextPtrI = scanPtr.p->m_nodeScan; if (scanPos.m_pos == 0) { jam(); #ifdef VM_TRACE - if (m_tux.debugFlags & m_tux.DebugScan) { - m_tux.debugOut << "Move scan " << scanPtr.i << " " << *scanPtr.p << endl; - m_tux.debugOut << "At pushDown pos=" << pos << " " << *this << endl; + if (debugFlags & DebugScan) { + debugOut << "Move scan " << scanPtr.i << " " << *scanPtr.p << endl; + debugOut << "At pushDown pos=" << pos << " " << node << endl; } #endif // here we may miss a valid entry "X" XXX known bug - m_tux.scanNext(signal, scanPtr); + scanNext(signal, scanPtr); } scanPtr.i = nextPtrI; } // fix other scans - scanPtr.i = getNodeScan(); + scanPtr.i = node.getNodeScan(); while (scanPtr.i != RNIL) { jam(); - m_tux.c_scanOpPool.getPtr(scanPtr); + c_scanOpPool.getPtr(scanPtr); TreePos& scanPos = scanPtr.p->m_scanPos; - ndbrequire(scanPos.m_addr == m_addr && scanPos.m_pos < occup); + ndbrequire(scanPos.m_loc == node.m_loc && scanPos.m_pos < occup); ndbrequire(scanPos.m_pos != 0); if (scanPos.m_pos <= pos) { jam(); #ifdef VM_TRACE - if (m_tux.debugFlags & m_tux.DebugScan) { - m_tux.debugOut << "Fix scan " << scanPtr.i << " " << *scanPtr.p << endl; - m_tux.debugOut << "At pushDown pos=" << pos << " " << *this << endl; + if (debugFlags & DebugScan) { + debugOut << "Fix scan " << scanPtr.i << " " << *scanPtr.p << endl; + debugOut << "At pushDown pos=" << pos << " " << node << endl; } #endif scanPos.m_pos--; @@ -439,7 +314,7 @@ Dbtux::NodeHandle::pushDown(Signal* signal, unsigned pos, TreeEnt& ent) scanPtr.i = scanPtr.p->m_nodeScan; } // fix node - TreeEnt* const entList = tree.getEntList(m_node); + TreeEnt* const entList = tree.getEntList(node.m_node); entList[occup] = entList[0]; TreeEnt* const tmpList = entList + 1; TreeEnt oldMin = tmpList[0]; @@ -449,18 +324,18 @@ Dbtux::NodeHandle::pushDown(Signal* signal, unsigned pos, TreeEnt& ent) } tmpList[pos] = ent; ent = oldMin; + entList[0] = entList[occup]; + // fix prefixes if (true) - m_flags |= (1 << 0); + setNodePref(signal, node, 0); if (occup == 1 || pos == occup - 1) - m_flags |= (1 << 1); - entList[0] = entList[occup]; - m_flags |= DoUpdate; + setNodePref(signal, node, 1); } /* * Remove and return entry at position. Move entries less than the * removed one to the right. Replace min entry by the input entry. - * This is the opposite of pushDown. + * This is the opposite of nodePushDown. * * X D * v ^ ^ @@ -468,47 +343,48 @@ Dbtux::NodeHandle::pushDown(Signal* signal, unsigned pos, TreeEnt& ent) * 0 1 2 3 4 5 6 0 1 2 3 4 5 6 */ void -Dbtux::NodeHandle::popUp(Signal* signal, unsigned pos, TreeEnt& ent) +Dbtux::nodePopUp(Signal* signal, NodeHandle& node, unsigned pos, TreeEnt& ent) { - TreeHead& tree = m_frag.m_tree; - const unsigned occup = getOccup(); + Frag& frag = node.m_frag; + TreeHead& tree = frag.m_tree; + const unsigned occup = node.getOccup(); ndbrequire(occup <= tree.m_maxOccup && pos < occup); ScanOpPtr scanPtr; // move scans whose entry disappears - scanPtr.i = getNodeScan(); + scanPtr.i = node.getNodeScan(); while (scanPtr.i != RNIL) { jam(); - m_tux.c_scanOpPool.getPtr(scanPtr); + c_scanOpPool.getPtr(scanPtr); TreePos& scanPos = scanPtr.p->m_scanPos; - ndbrequire(scanPos.m_addr == m_addr && scanPos.m_pos < occup); + ndbrequire(scanPos.m_loc == node.m_loc && scanPos.m_pos < occup); const Uint32 nextPtrI = scanPtr.p->m_nodeScan; if (scanPos.m_pos == pos) { jam(); #ifdef VM_TRACE - if (m_tux.debugFlags & m_tux.DebugScan) { - m_tux.debugOut << "Move scan " << scanPtr.i << " " << *scanPtr.p << endl; - m_tux.debugOut << "At popUp pos=" << pos << " " << *this << endl; + if (debugFlags & DebugScan) { + debugOut << "Move scan " << scanPtr.i << " " << *scanPtr.p << endl; + debugOut << "At popUp pos=" << pos << " " << node << endl; } #endif // here we may miss a valid entry "X" XXX known bug - m_tux.scanNext(signal, scanPtr); + scanNext(signal, scanPtr); } scanPtr.i = nextPtrI; } // fix other scans - scanPtr.i = getNodeScan(); + scanPtr.i = node.getNodeScan(); while (scanPtr.i != RNIL) { jam(); - m_tux.c_scanOpPool.getPtr(scanPtr); + c_scanOpPool.getPtr(scanPtr); TreePos& scanPos = scanPtr.p->m_scanPos; - ndbrequire(scanPos.m_addr == m_addr && scanPos.m_pos < occup); + ndbrequire(scanPos.m_loc == node.m_loc && scanPos.m_pos < occup); ndbrequire(scanPos.m_pos != pos); if (scanPos.m_pos < pos) { jam(); #ifdef VM_TRACE - if (m_tux.debugFlags & m_tux.DebugScan) { - m_tux.debugOut << "Fix scan " << scanPtr.i << " " << *scanPtr.p << endl; - m_tux.debugOut << "At popUp pos=" << pos << " " << *this << endl; + if (debugFlags & DebugScan) { + debugOut << "Fix scan " << scanPtr.i << " " << *scanPtr.p << endl; + debugOut << "At popUp pos=" << pos << " " << node << endl; } #endif scanPos.m_pos++; @@ -516,7 +392,7 @@ Dbtux::NodeHandle::popUp(Signal* signal, unsigned pos, TreeEnt& ent) scanPtr.i = scanPtr.p->m_nodeScan; } // fix node - TreeEnt* const entList = tree.getEntList(m_node); + TreeEnt* const entList = tree.getEntList(node.m_node); entList[occup] = entList[0]; TreeEnt* const tmpList = entList + 1; TreeEnt newMin = ent; @@ -526,12 +402,12 @@ Dbtux::NodeHandle::popUp(Signal* signal, unsigned pos, TreeEnt& ent) tmpList[i] = tmpList[i - 1]; } tmpList[0] = newMin; + entList[0] = entList[occup]; + // fix prefixes if (true) - m_flags |= (1 << 0); + setNodePref(signal, node, 0); if (occup == 1 || pos == occup - 1) - m_flags |= (1 << 1); - entList[0] = entList[occup]; - m_flags |= DoUpdate; + setNodePref(signal, node, 1); } /* @@ -539,14 +415,15 @@ Dbtux::NodeHandle::popUp(Signal* signal, unsigned pos, TreeEnt& ent) * after the max (i=1). XXX can be optimized */ void -Dbtux::NodeHandle::slide(Signal* signal, NodeHandlePtr nodePtr, unsigned i) +Dbtux::nodeSlide(Signal* signal, NodeHandle& dstNode, NodeHandle& srcNode, unsigned i) { + Frag& frag = dstNode.m_frag; + TreeHead& tree = frag.m_tree; ndbrequire(i <= 1); - TreeHead& tree = m_frag.m_tree; - while (getOccup() < tree.m_maxOccup && nodePtr.p->getOccup() != 0) { + while (dstNode.getOccup() < tree.m_maxOccup && srcNode.getOccup() != 0) { TreeEnt ent; - nodePtr.p->popDown(signal, i == 0 ? nodePtr.p->getOccup() - 1 : 0, ent); - pushUp(signal, i == 0 ? 0 : getOccup(), ent); + nodePopDown(signal, srcNode, i == 0 ? srcNode.getOccup() - 1 : 0, ent); + nodePushUp(signal, dstNode, i == 0 ? 0 : dstNode.getOccup(), ent); } } @@ -555,50 +432,50 @@ Dbtux::NodeHandle::slide(Signal* signal, NodeHandlePtr nodePtr, unsigned i) * ordering does not matter. */ void -Dbtux::NodeHandle::linkScan(Dbtux::ScanOpPtr scanPtr) +Dbtux::linkScan(NodeHandle& node, ScanOpPtr scanPtr) { #ifdef VM_TRACE - if (m_tux.debugFlags & m_tux.DebugScan) { - m_tux.debugOut << "Link scan " << scanPtr.i << " " << *scanPtr.p << endl; - m_tux.debugOut << "To node " << *this << endl; + if (debugFlags & DebugScan) { + debugOut << "Link scan " << scanPtr.i << " " << *scanPtr.p << endl; + debugOut << "To node " << node << endl; } #endif - ndbrequire(! islinkScan(scanPtr) && scanPtr.p->m_nodeScan == RNIL); - scanPtr.p->m_nodeScan = getNodeScan(); - setNodeScan(scanPtr.i); + ndbrequire(! islinkScan(node, scanPtr) && scanPtr.p->m_nodeScan == RNIL); + scanPtr.p->m_nodeScan = node.getNodeScan(); + node.setNodeScan(scanPtr.i); } /* * Unlink a scan from the list under the node. */ void -Dbtux::NodeHandle::unlinkScan(Dbtux::ScanOpPtr scanPtr) +Dbtux::unlinkScan(NodeHandle& node, ScanOpPtr scanPtr) { #ifdef VM_TRACE - if (m_tux.debugFlags & m_tux.DebugScan) { - m_tux.debugOut << "Unlink scan " << scanPtr.i << " " << *scanPtr.p << endl; - m_tux.debugOut << "From node " << *this << endl; + if (debugFlags & DebugScan) { + debugOut << "Unlink scan " << scanPtr.i << " " << *scanPtr.p << endl; + debugOut << "From node " << node << endl; } #endif - Dbtux::ScanOpPtr currPtr; - currPtr.i = getNodeScan(); - Dbtux::ScanOpPtr prevPtr; + ScanOpPtr currPtr; + currPtr.i = node.getNodeScan(); + ScanOpPtr prevPtr; prevPtr.i = RNIL; while (true) { jam(); - m_tux.c_scanOpPool.getPtr(currPtr); + c_scanOpPool.getPtr(currPtr); Uint32 nextPtrI = currPtr.p->m_nodeScan; if (currPtr.i == scanPtr.i) { jam(); if (prevPtr.i == RNIL) { - setNodeScan(nextPtrI); + node.setNodeScan(nextPtrI); } else { jam(); prevPtr.p->m_nodeScan = nextPtrI; } scanPtr.p->m_nodeScan = RNIL; // check for duplicates - ndbrequire(! islinkScan(scanPtr)); + ndbrequire(! islinkScan(node, scanPtr)); return; } prevPtr = currPtr; @@ -610,13 +487,13 @@ Dbtux::NodeHandle::unlinkScan(Dbtux::ScanOpPtr scanPtr) * Check if a scan is linked to this node. Only for ndbrequire. */ bool -Dbtux::NodeHandle::islinkScan(Dbtux::ScanOpPtr scanPtr) +Dbtux::islinkScan(NodeHandle& node, ScanOpPtr scanPtr) { - Dbtux::ScanOpPtr currPtr; - currPtr.i = getNodeScan(); + ScanOpPtr currPtr; + currPtr.i = node.getNodeScan(); while (currPtr.i != RNIL) { jam(); - m_tux.c_scanOpPool.getPtr(currPtr); + c_scanOpPool.getPtr(currPtr); if (currPtr.i == scanPtr.i) { jam(); return true; @@ -627,7 +504,7 @@ Dbtux::NodeHandle::islinkScan(Dbtux::ScanOpPtr scanPtr) } void -Dbtux::NodeHandle::progError(int line, int cause, const char* extra) +Dbtux::NodeHandle::progError(int line, int cause, const char* file) { - m_tux.progError(line, cause, extra); + ErrorReporter::handleAssert("Dbtux::NodeHandle: assert failed", file, line); } diff --git a/ndb/src/kernel/blocks/dbtux/DbtuxScan.cpp b/ndb/src/kernel/blocks/dbtux/DbtuxScan.cpp index eaa539d9cfc..703b0abb683 100644 --- a/ndb/src/kernel/blocks/dbtux/DbtuxScan.cpp +++ b/ndb/src/kernel/blocks/dbtux/DbtuxScan.cpp @@ -42,12 +42,11 @@ Dbtux::execACC_SCANREQ(Signal* signal) } ndbrequire(fragPtr.i != RNIL); Frag& frag = *fragPtr.p; - ndbrequire(frag.m_nodeList == RNIL); // must be normal DIH/TC fragment ndbrequire(frag.m_fragId < (1 << frag.m_fragOff)); TreeHead& tree = frag.m_tree; // check for empty fragment - if (tree.m_root == NullTupAddr) { + if (tree.m_root == NullTupLoc) { jam(); AccScanConf* const conf = (AccScanConf*)signal->getDataPtrSend(); conf->scanPtr = req->senderData; @@ -241,7 +240,6 @@ Dbtux::execNEXT_SCANREQ(Signal* signal) debugOut << "NEXT_SCANREQ scan " << scanPtr.i << " " << scan << endl; } #endif - ndbrequire(frag.m_nodeList == RNIL); // handle unlock previous and close scan switch (req->scanFlag) { case NextScanReq::ZSCAN_NEXT: @@ -275,13 +273,13 @@ Dbtux::execNEXT_SCANREQ(Signal* signal) case NextScanReq::ZSCAN_CLOSE: jam(); // unlink from tree node first to avoid state changes - if (scan.m_scanPos.m_addr != NullTupAddr) { + if (scan.m_scanPos.m_loc != NullTupLoc) { jam(); - const TupAddr addr = scan.m_scanPos.m_addr; - NodeHandlePtr nodePtr; - selectNode(signal, frag, nodePtr, addr, AccHead); - nodePtr.p->unlinkScan(scanPtr); - scan.m_scanPos.m_addr = NullTupAddr; + const TupLoc loc = scan.m_scanPos.m_loc; + NodeHandle node(frag); + selectNode(signal, node, loc, AccHead); + unlinkScan(node, scanPtr); + scan.m_scanPos.m_loc = NullTupLoc; } if (scan.m_lockwait) { jam(); @@ -295,7 +293,6 @@ Dbtux::execNEXT_SCANREQ(Signal* signal) jamEntry(); ndbrequire(lockReq->returnCode == AccLockReq::Success); scan.m_state = ScanOp::Aborting; - commitNodes(signal, frag, true); return; } if (scan.m_state == ScanOp::Locked) { @@ -350,7 +347,6 @@ Dbtux::execACC_CHECK_SCAN(Signal* signal) signal->theData[1] = true; EXECUTE_DIRECT(DBLQH, GSN_CHECK_LCP_STOP, signal, 2); jamEntry(); - commitNodes(signal, frag, true); return; // stop } if (scan.m_lockwait) { @@ -365,7 +361,6 @@ Dbtux::execACC_CHECK_SCAN(Signal* signal) // if TC has ordered scan close, it will be detected here sendSignal(scan.m_userRef, GSN_NEXT_SCANCONF, signal, signalLength, JBB); - commitNodes(signal, frag, true); return; // stop } if (scan.m_state == ScanOp::First) { @@ -395,7 +390,7 @@ Dbtux::execACC_CHECK_SCAN(Signal* signal) const TreeEnt ent = scan.m_scanPos.m_ent; // read tuple key keyPar.m_ent = ent; - keyPar.m_data = c_keyBuffer; + keyPar.m_data = c_dataBuffer; tupReadKeys(signal, frag, keyPar); // get read lock or exclusive lock AccLockReq* const lockReq = (AccLockReq*)signal->getDataPtrSend(); @@ -407,12 +402,11 @@ Dbtux::execACC_CHECK_SCAN(Signal* signal) lockReq->userRef = reference(); lockReq->tableId = scan.m_tableId; lockReq->fragId = frag.m_fragId | (ent.m_fragBit << frag.m_fragOff); - // should cache this at fragment create - lockReq->fragPtrI = RNIL; + lockReq->fragPtrI = frag.m_accTableFragPtrI[ent.m_fragBit]; const Uint32* const buf32 = static_cast<Uint32*>(keyPar.m_data); const Uint64* const buf64 = reinterpret_cast<const Uint64*>(buf32); lockReq->hashValue = md5_hash(buf64, keyPar.m_size); - lockReq->tupAddr = ent.m_tupAddr; + lockReq->tupAddr = getTupAddr(frag, ent); lockReq->transId1 = scan.m_transId1; lockReq->transId2 = scan.m_transId2; // execute @@ -445,7 +439,6 @@ Dbtux::execACC_CHECK_SCAN(Signal* signal) signal->theData[1] = true; EXECUTE_DIRECT(DBLQH, GSN_CHECK_LCP_STOP, signal, 2); jamEntry(); - commitNodes(signal, frag, true); return; // stop break; case AccLockReq::Refused: @@ -458,7 +451,6 @@ Dbtux::execACC_CHECK_SCAN(Signal* signal) signal->theData[1] = true; EXECUTE_DIRECT(DBLQH, GSN_CHECK_LCP_STOP, signal, 2); jamEntry(); - commitNodes(signal, frag, true); return; // stop break; case AccLockReq::NoFreeOp: @@ -471,7 +463,6 @@ Dbtux::execACC_CHECK_SCAN(Signal* signal) signal->theData[1] = true; EXECUTE_DIRECT(DBLQH, GSN_CHECK_LCP_STOP, signal, 2); jamEntry(); - commitNodes(signal, frag, true); return; // stop break; default: @@ -492,7 +483,7 @@ Dbtux::execACC_CHECK_SCAN(Signal* signal) if (keyPar.m_data == 0) { jam(); keyPar.m_ent = ent; - keyPar.m_data = c_keyBuffer; + keyPar.m_data = c_dataBuffer; tupReadKeys(signal, frag, keyPar); } } @@ -512,7 +503,7 @@ Dbtux::execACC_CHECK_SCAN(Signal* signal) } conf->accOperationPtr = accLockOp; conf->fragId = frag.m_fragId | (ent.m_fragBit << frag.m_fragOff); - conf->localKey[0] = ent.m_tupAddr; + conf->localKey[0] = getTupAddr(frag, ent); conf->localKey[1] = 0; conf->localKeyLength = 1; unsigned signalLength = 6; @@ -555,7 +546,6 @@ Dbtux::execACC_CHECK_SCAN(Signal* signal) scan.m_lastEnt = ent; // next time look for next entry scan.m_state = ScanOp::Next; - commitNodes(signal, frag, true); return; } // XXX in ACC this is checked before req->checkLcpStop @@ -569,7 +559,6 @@ Dbtux::execACC_CHECK_SCAN(Signal* signal) unsigned signalLength = 3; sendSignal(scanPtr.p->m_userRef, GSN_NEXT_SCANCONF, signal, signalLength, JBB); - commitNodes(signal, frag, true); return; } ndbrequire(false); @@ -700,45 +689,45 @@ Dbtux::scanFirst(Signal* signal, ScanOpPtr scanPtr) ScanOp& scan = *scanPtr.p; Frag& frag = *c_fragPool.getPtr(scan.m_fragPtrI); TreeHead& tree = frag.m_tree; - if (tree.m_root == NullTupAddr) { + if (tree.m_root == NullTupLoc) { // tree may have become empty jam(); scan.m_state = ScanOp::Last; return; } TreePos pos; - pos.m_addr = tree.m_root; - NodeHandlePtr nodePtr; + pos.m_loc = tree.m_root; + NodeHandle node(frag); // unpack lower bound const ScanBound& bound = *scan.m_bound[0]; ScanBoundIterator iter; bound.first(iter); for (unsigned j = 0; j < bound.getSize(); j++) { jam(); - c_keyBuffer[j] = *iter.data; + c_dataBuffer[j] = *iter.data; bound.next(iter); } // comparison parameters BoundPar boundPar; - boundPar.m_data1 = c_keyBuffer; + boundPar.m_data1 = c_dataBuffer; boundPar.m_count1 = scan.m_boundCnt[0]; boundPar.m_dir = 0; loop: { jam(); - selectNode(signal, frag, nodePtr, pos.m_addr, AccPref); - const unsigned occup = nodePtr.p->getOccup(); + selectNode(signal, node, pos.m_loc, AccPref); + const unsigned occup = node.getOccup(); ndbrequire(occup != 0); for (unsigned i = 0; i <= 1; i++) { jam(); // compare prefix - boundPar.m_data2 = nodePtr.p->getPref(i); + boundPar.m_data2 = node.getPref(i); boundPar.m_len2 = tree.m_prefSize; int ret = cmpScanBound(frag, boundPar); if (ret == NdbSqlUtil::CmpUnknown) { jam(); // read full value ReadPar readPar; - readPar.m_ent = nodePtr.p->getMinMax(i); + readPar.m_ent = node.getMinMax(i); readPar.m_first = 0; readPar.m_count = frag.m_numAttrs; readPar.m_data = 0; // leave in signal data @@ -751,11 +740,11 @@ loop: { } if (i == 0 && ret < 0) { jam(); - const TupAddr tupAddr = nodePtr.p->getLink(i); - if (tupAddr != NullTupAddr) { + const TupLoc loc = node.getLink(i); + if (loc != NullTupLoc) { jam(); // continue to left subtree - pos.m_addr = tupAddr; + pos.m_loc = loc; goto loop; } // start scanning this node @@ -764,34 +753,34 @@ loop: { pos.m_dir = 3; scan.m_scanPos = pos; scan.m_state = ScanOp::Next; - nodePtr.p->linkScan(scanPtr); + linkScan(node, scanPtr); return; } if (i == 1 && ret > 0) { jam(); - const TupAddr tupAddr = nodePtr.p->getLink(i); - if (tupAddr != NullTupAddr) { + const TupLoc loc = node.getLink(i); + if (loc != NullTupLoc) { jam(); // continue to right subtree - pos.m_addr = tupAddr; + pos.m_loc = loc; goto loop; } // start scanning upwards pos.m_dir = 1; scan.m_scanPos = pos; scan.m_state = ScanOp::Next; - nodePtr.p->linkScan(scanPtr); + linkScan(node, scanPtr); return; } } // read rest of current node - accessNode(signal, frag, nodePtr, AccFull); + accessNode(signal, node, AccFull); // look for first entry ndbrequire(occup >= 2); for (unsigned j = 1; j < occup; j++) { jam(); ReadPar readPar; - readPar.m_ent = nodePtr.p->getEnt(j); + readPar.m_ent = node.getEnt(j); readPar.m_first = 0; readPar.m_count = frag.m_numAttrs; readPar.m_data = 0; // leave in signal data @@ -809,7 +798,7 @@ loop: { pos.m_dir = 3; scan.m_scanPos = pos; scan.m_state = ScanOp::Next; - nodePtr.p->linkScan(scanPtr); + linkScan(node, scanPtr); return; } } @@ -858,42 +847,42 @@ Dbtux::scanNext(Signal* signal, ScanOpPtr scanPtr) bound.first(iter); for (unsigned j = 0; j < bound.getSize(); j++) { jam(); - c_keyBuffer[j] = *iter.data; + c_dataBuffer[j] = *iter.data; bound.next(iter); } // comparison parameters BoundPar boundPar; - boundPar.m_data1 = c_keyBuffer; + boundPar.m_data1 = c_dataBuffer; boundPar.m_count1 = scan.m_boundCnt[1]; boundPar.m_dir = 1; // use copy of position TreePos pos = scan.m_scanPos; // get and remember original node - NodeHandlePtr origNodePtr; - selectNode(signal, frag, origNodePtr, pos.m_addr, AccHead); - ndbrequire(origNodePtr.p->islinkScan(scanPtr)); + NodeHandle origNode(frag); + selectNode(signal, origNode, pos.m_loc, AccHead); + ndbrequire(islinkScan(origNode, scanPtr)); // current node in loop - NodeHandlePtr nodePtr = origNodePtr; + NodeHandle node = origNode; while (true) { jam(); if (pos.m_dir == 2) { // coming up from root ends the scan jam(); - pos.m_addr = NullTupAddr; + pos.m_loc = NullTupLoc; scan.m_state = ScanOp::Last; break; } - if (nodePtr.p->m_addr != pos.m_addr) { + if (node.m_loc != pos.m_loc) { jam(); - selectNode(signal, frag, nodePtr, pos.m_addr, AccHead); + selectNode(signal, node, pos.m_loc, AccHead); } if (pos.m_dir == 4) { // coming down from parent proceed to left child jam(); - TupAddr addr = nodePtr.p->getLink(0); - if (addr != NullTupAddr) { + TupLoc loc = node.getLink(0); + if (loc != NullTupLoc) { jam(); - pos.m_addr = addr; + pos.m_loc = loc; pos.m_dir = 4; // unchanged continue; } @@ -910,10 +899,10 @@ Dbtux::scanNext(Signal* signal, ScanOpPtr scanPtr) if (pos.m_dir == 3) { // within node jam(); - unsigned occup = nodePtr.p->getOccup(); + unsigned occup = node.getOccup(); ndbrequire(occup >= 1); // access full node - accessNode(signal, frag, nodePtr, AccFull); + accessNode(signal, node, AccFull); // advance position if (! pos.m_match) pos.m_match = true; @@ -921,7 +910,7 @@ Dbtux::scanNext(Signal* signal, ScanOpPtr scanPtr) pos.m_pos++; if (pos.m_pos < occup) { jam(); - pos.m_ent = nodePtr.p->getEnt(pos.m_pos); + pos.m_ent = node.getEnt(pos.m_pos); pos.m_dir = 3; // unchanged // XXX implement prefix optimization ReadPar readPar; @@ -938,7 +927,7 @@ Dbtux::scanNext(Signal* signal, ScanOpPtr scanPtr) if (ret < 0) { jam(); // hit upper bound of single range scan - pos.m_addr = NullTupAddr; + pos.m_loc = NullTupLoc; scan.m_state = ScanOp::Last; break; } @@ -952,10 +941,10 @@ Dbtux::scanNext(Signal* signal, ScanOpPtr scanPtr) break; } // after node proceed to right child - TupAddr addr = nodePtr.p->getLink(1); - if (addr != NullTupAddr) { + TupLoc loc = node.getLink(1); + if (loc != NullTupLoc) { jam(); - pos.m_addr = addr; + pos.m_loc = loc; pos.m_dir = 4; continue; } @@ -965,8 +954,8 @@ Dbtux::scanNext(Signal* signal, ScanOpPtr scanPtr) if (pos.m_dir == 1) { // coming from right child proceed to parent jam(); - pos.m_addr = nodePtr.p->getLink(2); - pos.m_dir = nodePtr.p->getSide(); + pos.m_loc = node.getLink(2); + pos.m_dir = node.getSide(); continue; } ndbrequire(false); @@ -975,16 +964,16 @@ Dbtux::scanNext(Signal* signal, ScanOpPtr scanPtr) scan.m_scanPos = pos; // relink if (scan.m_state == ScanOp::Current) { - ndbrequire(pos.m_addr == nodePtr.p->m_addr); - if (origNodePtr.i != nodePtr.i) { + ndbrequire(pos.m_loc == node.m_loc); + if (origNode.m_loc != node.m_loc) { jam(); - origNodePtr.p->unlinkScan(scanPtr); - nodePtr.p->linkScan(scanPtr); + unlinkScan(origNode, scanPtr); + linkScan(node, scanPtr); } } else if (scan.m_state == ScanOp::Last) { jam(); - ndbrequire(pos.m_addr == NullTupAddr); - origNodePtr.p->unlinkScan(scanPtr); + ndbrequire(pos.m_loc == NullTupLoc); + unlinkScan(origNode, scanPtr); } else { ndbrequire(false); } @@ -1012,10 +1001,10 @@ Dbtux::scanVisible(Signal* signal, ScanOpPtr scanPtr, TreeEnt ent) Uint32 tableId = frag.m_tableId; Uint32 fragBit = ent.m_fragBit; Uint32 fragId = frag.m_fragId | (fragBit << frag.m_fragOff); - Uint32 tupAddr = ent.m_tupAddr; + Uint32 tupAddr = getTupAddr(frag, ent); Uint32 tupVersion = ent.m_tupVersion; /* Check for same tuple twice in row */ - if (scan.m_lastEnt.m_tupAddr == tupAddr && + if (scan.m_lastEnt.m_tupLoc == ent.m_tupLoc && scan.m_lastEnt.m_fragBit == fragBit) { jam(); return false; @@ -1044,7 +1033,6 @@ void Dbtux::scanClose(Signal* signal, ScanOpPtr scanPtr) { ScanOp& scan = *scanPtr.p; - Frag& frag = *c_fragPool.getPtr(scanPtr.p->m_fragPtrI); ndbrequire(! scan.m_lockwait && scan.m_accLockOp == RNIL); // unlock all not unlocked by LQH for (unsigned i = 0; i < MaxAccLockOps; i++) { @@ -1069,7 +1057,6 @@ Dbtux::scanClose(Signal* signal, ScanOpPtr scanPtr) sendSignal(scanPtr.p->m_userRef, GSN_NEXT_SCANCONF, signal, signalLength, JBB); releaseScanOp(scanPtr); - commitNodes(signal, frag, true); } void diff --git a/ndb/src/kernel/blocks/dbtux/DbtuxTree.cpp b/ndb/src/kernel/blocks/dbtux/DbtuxTree.cpp index 860aa65414f..7c3f5fa36b8 100644 --- a/ndb/src/kernel/blocks/dbtux/DbtuxTree.cpp +++ b/ndb/src/kernel/blocks/dbtux/DbtuxTree.cpp @@ -26,71 +26,55 @@ * same in min/max need not be checked. */ void -Dbtux::treeSearch(Signal* signal, Frag& frag, SearchPar searchPar, TreePos& treePos) +Dbtux::treeSearch(Signal* signal, Frag& frag, TableData searchKey, TreeEnt searchEnt, TreePos& treePos) { const TreeHead& tree = frag.m_tree; const unsigned numAttrs = frag.m_numAttrs; - treePos.m_addr = tree.m_root; - NodeHandlePtr nodePtr; - if (treePos.m_addr == NullTupAddr) { + treePos.m_loc = tree.m_root; + if (treePos.m_loc == NullTupLoc) { // empty tree jam(); treePos.m_pos = 0; treePos.m_match = false; return; } + NodeHandle node(frag); loop: { jam(); - selectNode(signal, frag, nodePtr, treePos.m_addr, AccPref); - const unsigned occup = nodePtr.p->getOccup(); + selectNode(signal, node, treePos.m_loc, AccPref); + const unsigned occup = node.getOccup(); ndbrequire(occup != 0); // number of equal initial attributes in bounding node - unsigned numEq = ZNIL; + unsigned start = ZNIL; for (unsigned i = 0; i <= 1; i++) { jam(); + unsigned start1 = 0; // compare prefix - CmpPar cmpPar; - cmpPar.m_data1 = searchPar.m_data; - cmpPar.m_data2 = nodePtr.p->getPref(i); - cmpPar.m_len2 = tree.m_prefSize; - cmpPar.m_first = 0; - cmpPar.m_numEq = 0; - int ret = cmpTreeAttrs(frag, cmpPar); + int ret = cmpSearchKey(frag, start1, searchKey, node.getPref(i), tree.m_prefSize); if (ret == NdbSqlUtil::CmpUnknown) { jam(); - // read full value - ReadPar readPar; - readPar.m_ent = nodePtr.p->getMinMax(i); - ndbrequire(cmpPar.m_numEq < numAttrs); - readPar.m_first = cmpPar.m_numEq; - readPar.m_count = numAttrs - cmpPar.m_numEq; - readPar.m_data = 0; // leave in signal data - tupReadAttrs(signal, frag, readPar); - // compare full value - cmpPar.m_data2 = readPar.m_data; - cmpPar.m_len2 = ZNIL; // big - cmpPar.m_first = readPar.m_first; - ret = cmpTreeAttrs(frag, cmpPar); + // read and compare remaining attributes + readKeyAttrs(frag, node.getMinMax(i), start1, c_entryKey); + ret = cmpSearchKey(frag, start1, searchKey, c_entryKey); ndbrequire(ret != NdbSqlUtil::CmpUnknown); } - if (numEq > cmpPar.m_numEq) - numEq = cmpPar.m_numEq; + if (start > start1) + start = start1; if (ret == 0) { jam(); // keys are equal, compare entry values - ret = searchPar.m_ent.cmp(nodePtr.p->getMinMax(i)); + ret = searchEnt.cmp(node.getMinMax(i)); } if (i == 0 ? (ret < 0) : (ret > 0)) { jam(); - const TupAddr tupAddr = nodePtr.p->getLink(i); - if (tupAddr != NullTupAddr) { + const TupLoc loc = node.getLink(i); + if (loc != NullTupLoc) { jam(); // continue to left/right subtree - treePos.m_addr = tupAddr; + treePos.m_loc = loc; goto loop; } // position is immediately before/after this node - // XXX disallow second case treePos.m_pos = (i == 0 ? 0 : occup); treePos.m_match = false; return; @@ -103,36 +87,26 @@ loop: { return; } } - // read rest of the bounding node - accessNode(signal, frag, nodePtr, AccFull); + // access rest of the bounding node + accessNode(signal, node, AccFull); // position is strictly within the node ndbrequire(occup >= 2); const unsigned numWithin = occup - 2; for (unsigned j = 1; j <= numWithin; j++) { jam(); int ret = 0; - // compare remaining attributes - if (numEq < numAttrs) { + if (start < numAttrs) { jam(); - ReadPar readPar; - readPar.m_ent = nodePtr.p->getEnt(j); - readPar.m_first = numEq; - readPar.m_count = numAttrs - numEq; - readPar.m_data = 0; // leave in signal data - tupReadAttrs(signal, frag, readPar); - // compare - CmpPar cmpPar; - cmpPar.m_data1 = searchPar.m_data; - cmpPar.m_data2 = readPar.m_data; - cmpPar.m_len2 = ZNIL; // big - cmpPar.m_first = readPar.m_first; - ret = cmpTreeAttrs(frag, cmpPar); + // read and compare remaining attributes + unsigned start1 = start; + readKeyAttrs(frag, node.getEnt(j), start1, c_entryKey); + ret = cmpSearchKey(frag, start1, searchKey, c_entryKey); ndbrequire(ret != NdbSqlUtil::CmpUnknown); } if (ret == 0) { jam(); // keys are equal, compare entry values - ret = searchPar.m_ent.cmp(nodePtr.p->getEnt(j)); + ret = searchEnt.cmp(node.getEnt(j)); } if (ret <= 0) { jam(); @@ -157,94 +131,94 @@ Dbtux::treeAdd(Signal* signal, Frag& frag, TreePos treePos, TreeEnt ent) { TreeHead& tree = frag.m_tree; unsigned pos = treePos.m_pos; - NodeHandlePtr nodePtr; + NodeHandle node(frag); // check for empty tree - if (treePos.m_addr == NullTupAddr) { + if (treePos.m_loc == NullTupLoc) { jam(); - insertNode(signal, frag, nodePtr, AccPref); - nodePtr.p->pushUp(signal, 0, ent); - nodePtr.p->setSide(2); - tree.m_root = nodePtr.p->m_addr; + insertNode(signal, node, AccPref); + nodePushUp(signal, node, 0, ent); + node.setSide(2); + tree.m_root = node.m_loc; return; } // access full node - selectNode(signal, frag, nodePtr, treePos.m_addr, AccFull); + selectNode(signal, node, treePos.m_loc, AccFull); // check if it is bounding node - if (pos != 0 && pos != nodePtr.p->getOccup()) { + if (pos != 0 && pos != node.getOccup()) { jam(); // check if room for one more - if (nodePtr.p->getOccup() < tree.m_maxOccup) { + if (node.getOccup() < tree.m_maxOccup) { jam(); - nodePtr.p->pushUp(signal, pos, ent); + nodePushUp(signal, node, pos, ent); return; } // returns min entry - nodePtr.p->pushDown(signal, pos - 1, ent); + nodePushDown(signal, node, pos - 1, ent); // find position to add the removed min entry - TupAddr childAddr = nodePtr.p->getLink(0); - if (childAddr == NullTupAddr) { + TupLoc childLoc = node.getLink(0); + if (childLoc == NullTupLoc) { jam(); // left child will be added pos = 0; } else { jam(); // find glb node - while (childAddr != NullTupAddr) { + while (childLoc != NullTupLoc) { jam(); - selectNode(signal, frag, nodePtr, childAddr, AccHead); - childAddr = nodePtr.p->getLink(1); + selectNode(signal, node, childLoc, AccHead); + childLoc = node.getLink(1); } // access full node again - accessNode(signal, frag, nodePtr, AccFull); - pos = nodePtr.p->getOccup(); + accessNode(signal, node, AccFull); + pos = node.getOccup(); } // fall thru to next case } // adding new min or max unsigned i = (pos == 0 ? 0 : 1); - ndbrequire(nodePtr.p->getLink(i) == NullTupAddr); + ndbrequire(node.getLink(i) == NullTupLoc); // check if the half-leaf/leaf has room for one more - if (nodePtr.p->getOccup() < tree.m_maxOccup) { + if (node.getOccup() < tree.m_maxOccup) { jam(); - nodePtr.p->pushUp(signal, pos, ent); + nodePushUp(signal, node, pos, ent); return; } // add a new node - NodeHandlePtr childPtr; - insertNode(signal, frag, childPtr, AccPref); - childPtr.p->pushUp(signal, 0, ent); + NodeHandle childNode(frag); + insertNode(signal, childNode, AccPref); + nodePushUp(signal, childNode, 0, ent); // connect parent and child - nodePtr.p->setLink(i, childPtr.p->m_addr); - childPtr.p->setLink(2, nodePtr.p->m_addr); - childPtr.p->setSide(i); + node.setLink(i, childNode.m_loc); + childNode.setLink(2, node.m_loc); + childNode.setSide(i); // re-balance tree at each node while (true) { // height of subtree i has increased by 1 int j = (i == 0 ? -1 : +1); - int b = nodePtr.p->getBalance(); + int b = node.getBalance(); if (b == 0) { // perfectly balanced jam(); - nodePtr.p->setBalance(j); + node.setBalance(j); // height change propagates up } else if (b == -j) { // height of shorter subtree increased jam(); - nodePtr.p->setBalance(0); + node.setBalance(0); // height of tree did not change - done break; } else if (b == j) { // height of longer subtree increased jam(); - NodeHandlePtr childPtr; - selectNode(signal, frag, childPtr, nodePtr.p->getLink(i), AccHead); - int b2 = childPtr.p->getBalance(); + NodeHandle childNode(frag); + selectNode(signal, childNode, node.getLink(i), AccHead); + int b2 = childNode.getBalance(); if (b2 == b) { jam(); - treeRotateSingle(signal, frag, nodePtr, i); + treeRotateSingle(signal, frag, node, i); } else if (b2 == -b) { jam(); - treeRotateDouble(signal, frag, nodePtr, i); + treeRotateDouble(signal, frag, node, i); } else { // height of subtree increased so it cannot be perfectly balanced ndbrequire(false); @@ -254,14 +228,14 @@ Dbtux::treeAdd(Signal* signal, Frag& frag, TreePos treePos, TreeEnt ent) } else { ndbrequire(false); } - TupAddr parentAddr = nodePtr.p->getLink(2); - if (parentAddr == NullTupAddr) { + TupLoc parentLoc = node.getLink(2); + if (parentLoc == NullTupLoc) { jam(); // root node - done break; } - i = nodePtr.p->getSide(); - selectNode(signal, frag, nodePtr, parentAddr, AccHead); + i = node.getSide(); + selectNode(signal, node, parentLoc, AccHead); } } @@ -273,101 +247,101 @@ Dbtux::treeRemove(Signal* signal, Frag& frag, TreePos treePos) { TreeHead& tree = frag.m_tree; unsigned pos = treePos.m_pos; - NodeHandlePtr nodePtr; + NodeHandle node(frag); // access full node - selectNode(signal, frag, nodePtr, treePos.m_addr, AccFull); + selectNode(signal, node, treePos.m_loc, AccFull); TreeEnt ent; // check interior node first - if (nodePtr.p->getChilds() == 2) { + if (node.getChilds() == 2) { jam(); - ndbrequire(nodePtr.p->getOccup() >= tree.m_minOccup); + ndbrequire(node.getOccup() >= tree.m_minOccup); // check if no underflow - if (nodePtr.p->getOccup() > tree.m_minOccup) { + if (node.getOccup() > tree.m_minOccup) { jam(); - nodePtr.p->popDown(signal, pos, ent); + nodePopDown(signal, node, pos, ent); return; } // save current handle - NodeHandlePtr parentPtr = nodePtr; + NodeHandle parentNode = node; // find glb node - TupAddr childAddr = nodePtr.p->getLink(0); - while (childAddr != NullTupAddr) { + TupLoc childLoc = node.getLink(0); + while (childLoc != NullTupLoc) { jam(); - selectNode(signal, frag, nodePtr, childAddr, AccHead); - childAddr = nodePtr.p->getLink(1); + selectNode(signal, node, childLoc, AccHead); + childLoc = node.getLink(1); } // access full node again - accessNode(signal, frag, nodePtr, AccFull); + accessNode(signal, node, AccFull); // use glb max as new parent min - ent = nodePtr.p->getEnt(nodePtr.p->getOccup() - 1); - parentPtr.p->popUp(signal, pos, ent); + ent = node.getEnt(node.getOccup() - 1); + nodePopUp(signal, parentNode, pos, ent); // set up to remove glb max - pos = nodePtr.p->getOccup() - 1; + pos = node.getOccup() - 1; // fall thru to next case } // remove the element - nodePtr.p->popDown(signal, pos, ent); - ndbrequire(nodePtr.p->getChilds() <= 1); + nodePopDown(signal, node, pos, ent); + ndbrequire(node.getChilds() <= 1); // handle half-leaf for (unsigned i = 0; i <= 1; i++) { jam(); - TupAddr childAddr = nodePtr.p->getLink(i); - if (childAddr != NullTupAddr) { + TupLoc childLoc = node.getLink(i); + if (childLoc != NullTupLoc) { // move to child - selectNode(signal, frag, nodePtr, childAddr, AccFull); + selectNode(signal, node, childLoc, AccFull); // balance of half-leaf parent requires child to be leaf break; } } - ndbrequire(nodePtr.p->getChilds() == 0); + ndbrequire(node.getChilds() == 0); // get parent if any - TupAddr parentAddr = nodePtr.p->getLink(2); - NodeHandlePtr parentPtr; - unsigned i = nodePtr.p->getSide(); + TupLoc parentLoc = node.getLink(2); + NodeHandle parentNode(frag); + unsigned i = node.getSide(); // move all that fits into parent - if (parentAddr != NullTupAddr) { + if (parentLoc != NullTupLoc) { jam(); - selectNode(signal, frag, parentPtr, nodePtr.p->getLink(2), AccFull); - parentPtr.p->slide(signal, nodePtr, i); + selectNode(signal, parentNode, node.getLink(2), AccFull); + nodeSlide(signal, parentNode, node, i); // fall thru to next case } // non-empty leaf - if (nodePtr.p->getOccup() >= 1) { + if (node.getOccup() >= 1) { jam(); return; } // remove empty leaf - deleteNode(signal, frag, nodePtr); - if (parentAddr == NullTupAddr) { + deleteNode(signal, node); + if (parentLoc == NullTupLoc) { jam(); // tree is now empty - tree.m_root = NullTupAddr; + tree.m_root = NullTupLoc; return; } - nodePtr = parentPtr; - nodePtr.p->setLink(i, NullTupAddr); + node = parentNode; + node.setLink(i, NullTupLoc); #ifdef dbtux_min_occup_less_max_occup // check if we created a half-leaf - if (nodePtr.p->getBalance() == 0) { + if (node.getBalance() == 0) { jam(); // move entries from the other child - TupAddr childAddr = nodePtr.p->getLink(1 - i); - NodeHandlePtr childPtr; - selectNode(signal, frag, childPtr, childAddr, AccFull); - nodePtr.p->slide(signal, childPtr, 1 - i); - if (childPtr.p->getOccup() == 0) { + TupLoc childLoc = node.getLink(1 - i); + NodeHandle childNode(frag); + selectNode(signal, childNode, childLoc, AccFull); + nodeSlide(signal, node, childNode, 1 - i); + if (childNode.getOccup() == 0) { jam(); - deleteNode(signal, frag, childPtr); - nodePtr.p->setLink(1 - i, NullTupAddr); + deleteNode(signal, childNode); + node.setLink(1 - i, NullTupLoc); // we are balanced again but our parent balance changes by -1 - parentAddr = nodePtr.p->getLink(2); - if (parentAddr == NullTupAddr) { + parentLoc = node.getLink(2); + if (parentLoc == NullTupLoc) { jam(); return; } // fix side and become parent - i = nodePtr.p->getSide(); - selectNode(signal, frag, nodePtr, parentAddr, AccHead); + i = node.getSide(); + selectNode(signal, node, parentLoc, AccHead); } } #endif @@ -375,50 +349,50 @@ Dbtux::treeRemove(Signal* signal, Frag& frag, TreePos treePos) while (true) { // height of subtree i has decreased by 1 int j = (i == 0 ? -1 : +1); - int b = nodePtr.p->getBalance(); + int b = node.getBalance(); if (b == 0) { // perfectly balanced jam(); - nodePtr.p->setBalance(-j); + node.setBalance(-j); // height of tree did not change - done return; } else if (b == j) { // height of longer subtree has decreased jam(); - nodePtr.p->setBalance(0); + node.setBalance(0); // height change propagates up } else if (b == -j) { // height of shorter subtree has decreased jam(); - NodeHandlePtr childPtr; // child on the other side - selectNode(signal, frag, childPtr, nodePtr.p->getLink(1 - i), AccHead); - int b2 = childPtr.p->getBalance(); + NodeHandle childNode(frag); + selectNode(signal, childNode, node.getLink(1 - i), AccHead); + int b2 = childNode.getBalance(); if (b2 == b) { jam(); - treeRotateSingle(signal, frag, nodePtr, 1 - i); + treeRotateSingle(signal, frag, node, 1 - i); // height of tree decreased and propagates up } else if (b2 == -b) { jam(); - treeRotateDouble(signal, frag, nodePtr, 1 - i); + treeRotateDouble(signal, frag, node, 1 - i); // height of tree decreased and propagates up } else { jam(); - treeRotateSingle(signal, frag, nodePtr, 1 - i); + treeRotateSingle(signal, frag, node, 1 - i); // height of tree did not change - done return; } } else { ndbrequire(false); } - TupAddr parentAddr = nodePtr.p->getLink(2); - if (parentAddr == NullTupAddr) { + TupLoc parentLoc = node.getLink(2); + if (parentLoc == NullTupLoc) { jam(); // root node - done return; } - i = nodePtr.p->getSide(); - selectNode(signal, frag, nodePtr, parentAddr, AccHead); + i = node.getSide(); + selectNode(signal, node, parentLoc, AccHead); } } @@ -441,55 +415,55 @@ Dbtux::treeRemove(Signal* signal, Frag& frag, TreePos treePos) void Dbtux::treeRotateSingle(Signal* signal, Frag& frag, - NodeHandlePtr& nodePtr, + NodeHandle& node, unsigned i) { ndbrequire(i <= 1); /* 5 is the old top node that have been unbalanced due to an insert or delete. The balance is still the old balance before the update. - Verify that n5Bal is 1 if RR rotate and -1 if LL rotate. + Verify that bal5 is 1 if RR rotate and -1 if LL rotate. */ - NodeHandlePtr n5Ptr = nodePtr; - const TupAddr n5Addr = n5Ptr.p->m_addr; - const int n5Bal = n5Ptr.p->getBalance(); - const int n5side = n5Ptr.p->getSide(); - ndbrequire(n5Bal + (1 - i) == i); + NodeHandle node5 = node; + const TupLoc loc5 = node5.m_loc; + const int bal5 = node5.getBalance(); + const int side5 = node5.getSide(); + ndbrequire(bal5 + (1 - i) == i); /* 3 is the new root of this part of the tree which is to swap place with node 5. For an insert to cause this it must have the same balance as 5. For deletes it can have the balance 0. */ - TupAddr n3Addr = n5Ptr.p->getLink(i); - NodeHandlePtr n3Ptr; - selectNode(signal, frag, n3Ptr, n3Addr, AccHead); - const int n3Bal = n3Ptr.p->getBalance(); + TupLoc loc3 = node5.getLink(i); + NodeHandle node3(frag); + selectNode(signal, node3, loc3, AccHead); + const int bal3 = node3.getBalance(); /* 2 must always be there but is not changed. Thus we mereley check that it exists. */ - ndbrequire(n3Ptr.p->getLink(i) != NullTupAddr); + ndbrequire(node3.getLink(i) != NullTupLoc); /* 4 is not necessarily there but if it is there it will move from one side of 3 to the other side of 5. For LL it moves from the right side to the left side and for RR it moves from the left side to the right side. This means that it also changes parent from 3 to 5. */ - TupAddr n4Addr = n3Ptr.p->getLink(1 - i); - NodeHandlePtr n4Ptr; - if (n4Addr != NullTupAddr) { + TupLoc loc4 = node3.getLink(1 - i); + NodeHandle node4(frag); + if (loc4 != NullTupLoc) { jam(); - selectNode(signal, frag, n4Ptr, n4Addr, AccHead); - ndbrequire(n4Ptr.p->getSide() == (1 - i) && - n4Ptr.p->getLink(2) == n3Addr); - n4Ptr.p->setSide(i); - n4Ptr.p->setLink(2, n5Addr); + selectNode(signal, node4, loc4, AccHead); + ndbrequire(node4.getSide() == (1 - i) && + node4.getLink(2) == loc3); + node4.setSide(i); + node4.setLink(2, loc5); }//if /* Retrieve the address of 5's parent before it is destroyed */ - TupAddr n0Addr = n5Ptr.p->getLink(2); + TupLoc loc0 = node5.getLink(2); /* The next step is to perform the rotation. 3 will inherit 5's parent @@ -503,22 +477,22 @@ Dbtux::treeRotateSingle(Signal* signal, 1. 3 must have had 5 as parent before the change. 2. 3's side is left for LL and right for RR before change. */ - ndbrequire(n3Ptr.p->getLink(2) == n5Addr); - ndbrequire(n3Ptr.p->getSide() == i); - n3Ptr.p->setLink(1 - i, n5Addr); - n3Ptr.p->setLink(2, n0Addr); - n3Ptr.p->setSide(n5side); - n5Ptr.p->setLink(i, n4Addr); - n5Ptr.p->setLink(2, n3Addr); - n5Ptr.p->setSide(1 - i); - if (n0Addr != NullTupAddr) { + ndbrequire(node3.getLink(2) == loc5); + ndbrequire(node3.getSide() == i); + node3.setLink(1 - i, loc5); + node3.setLink(2, loc0); + node3.setSide(side5); + node5.setLink(i, loc4); + node5.setLink(2, loc3); + node5.setSide(1 - i); + if (loc0 != NullTupLoc) { jam(); - NodeHandlePtr n0Ptr; - selectNode(signal, frag, n0Ptr, n0Addr, AccHead); - n0Ptr.p->setLink(n5side, n3Addr); + NodeHandle node0(frag); + selectNode(signal, node0, loc0, AccHead); + node0.setLink(side5, loc3); } else { jam(); - frag.m_tree.m_root = n3Addr; + frag.m_tree.m_root = loc3; }//if /* The final step of the change is to update the balance of 3 and 5 that changed places. There are two cases here. The first case is @@ -531,22 +505,22 @@ Dbtux::treeRotateSingle(Signal* signal, In this case 5 will change balance but still be unbalanced and 3 will be unbalanced in the opposite direction of 5. */ - if (n3Bal == n5Bal) { + if (bal3 == bal5) { jam(); - n3Ptr.p->setBalance(0); - n5Ptr.p->setBalance(0); - } else if (n3Bal == 0) { + node3.setBalance(0); + node5.setBalance(0); + } else if (bal3 == 0) { jam(); - n3Ptr.p->setBalance(-n5Bal); - n5Ptr.p->setBalance(n5Bal); + node3.setBalance(-bal5); + node5.setBalance(bal5); } else { ndbrequire(false); }//if /* - Set nodePtr to 3 as return parameter for enabling caller to continue + Set node to 3 as return parameter for enabling caller to continue traversing the tree. */ - nodePtr = n3Ptr; + node = node3; } /* @@ -651,105 +625,105 @@ Dbtux::treeRotateSingle(Signal* signal, * */ void -Dbtux::treeRotateDouble(Signal* signal, Frag& frag, NodeHandlePtr& nodePtr, unsigned i) +Dbtux::treeRotateDouble(Signal* signal, Frag& frag, NodeHandle& node, unsigned i) { // old top node - NodeHandlePtr n6Ptr = nodePtr; - const TupAddr n6Addr = n6Ptr.p->m_addr; + NodeHandle node6 = node; + const TupLoc loc6 = node6.m_loc; // the un-updated balance - const int n6Bal = n6Ptr.p->getBalance(); - const unsigned n6Side = n6Ptr.p->getSide(); + const int bal6 = node6.getBalance(); + const unsigned side6 = node6.getSide(); // level 1 - TupAddr n2Addr = n6Ptr.p->getLink(i); - NodeHandlePtr n2Ptr; - selectNode(signal, frag, n2Ptr, n2Addr, AccHead); - const int n2Bal = n2Ptr.p->getBalance(); + TupLoc loc2 = node6.getLink(i); + NodeHandle node2(frag); + selectNode(signal, node2, loc2, AccHead); + const int bal2 = node2.getBalance(); // level 2 - TupAddr n4Addr = n2Ptr.p->getLink(1 - i); - NodeHandlePtr n4Ptr; - selectNode(signal, frag, n4Ptr, n4Addr, AccHead); - const int n4Bal = n4Ptr.p->getBalance(); + TupLoc loc4 = node2.getLink(1 - i); + NodeHandle node4(frag); + selectNode(signal, node4, loc4, AccHead); + const int bal4 = node4.getBalance(); ndbrequire(i <= 1); - ndbrequire(n6Bal + (1 - i) == i); - ndbrequire(n2Bal == -n6Bal); - ndbrequire(n2Ptr.p->getLink(2) == n6Addr); - ndbrequire(n2Ptr.p->getSide() == i); - ndbrequire(n4Ptr.p->getLink(2) == n2Addr); + ndbrequire(bal6 + (1 - i) == i); + ndbrequire(bal2 == -bal6); + ndbrequire(node2.getLink(2) == loc6); + ndbrequire(node2.getSide() == i); + ndbrequire(node4.getLink(2) == loc2); // level 3 - TupAddr n3Addr = n4Ptr.p->getLink(i); - TupAddr n5Addr = n4Ptr.p->getLink(1 - i); + TupLoc loc3 = node4.getLink(i); + TupLoc loc5 = node4.getLink(1 - i); // fill up leaf before it becomes internal - if (n3Addr == NullTupAddr && n5Addr == NullTupAddr) { + if (loc3 == NullTupLoc && loc5 == NullTupLoc) { jam(); TreeHead& tree = frag.m_tree; - accessNode(signal, frag, n2Ptr, AccFull); - accessNode(signal, frag, n4Ptr, AccFull); - n4Ptr.p->slide(signal, n2Ptr, i); + accessNode(signal, node2, AccFull); + accessNode(signal, node4, AccFull); + nodeSlide(signal, node4, node2, i); // implied by rule of merging half-leaves with leaves - ndbrequire(n4Ptr.p->getOccup() >= tree.m_minOccup); - ndbrequire(n2Ptr.p->getOccup() != 0); + ndbrequire(node4.getOccup() >= tree.m_minOccup); + ndbrequire(node2.getOccup() != 0); } else { - if (n3Addr != NullTupAddr) { + if (loc3 != NullTupLoc) { jam(); - NodeHandlePtr n3Ptr; - selectNode(signal, frag, n3Ptr, n3Addr, AccHead); - n3Ptr.p->setLink(2, n2Addr); - n3Ptr.p->setSide(1 - i); + NodeHandle node3(frag); + selectNode(signal, node3, loc3, AccHead); + node3.setLink(2, loc2); + node3.setSide(1 - i); } - if (n5Addr != NullTupAddr) { + if (loc5 != NullTupLoc) { jam(); - NodeHandlePtr n5Ptr; - selectNode(signal, frag, n5Ptr, n5Addr, AccHead); - n5Ptr.p->setLink(2, n6Ptr.p->m_addr); - n5Ptr.p->setSide(i); + NodeHandle node5(frag); + selectNode(signal, node5, loc5, AccHead); + node5.setLink(2, node6.m_loc); + node5.setSide(i); } } // parent - TupAddr n0Addr = n6Ptr.p->getLink(2); - NodeHandlePtr n0Ptr; + TupLoc loc0 = node6.getLink(2); + NodeHandle node0(frag); // perform the rotation - n6Ptr.p->setLink(i, n5Addr); - n6Ptr.p->setLink(2, n4Addr); - n6Ptr.p->setSide(1 - i); + node6.setLink(i, loc5); + node6.setLink(2, loc4); + node6.setSide(1 - i); - n2Ptr.p->setLink(1 - i, n3Addr); - n2Ptr.p->setLink(2, n4Addr); + node2.setLink(1 - i, loc3); + node2.setLink(2, loc4); - n4Ptr.p->setLink(i, n2Addr); - n4Ptr.p->setLink(1 - i, n6Addr); - n4Ptr.p->setLink(2, n0Addr); - n4Ptr.p->setSide(n6Side); + node4.setLink(i, loc2); + node4.setLink(1 - i, loc6); + node4.setLink(2, loc0); + node4.setSide(side6); - if (n0Addr != NullTupAddr) { + if (loc0 != NullTupLoc) { jam(); - selectNode(signal, frag, n0Ptr, n0Addr, AccHead); - n0Ptr.p->setLink(n6Side, n4Addr); + selectNode(signal, node0, loc0, AccHead); + node0.setLink(side6, loc4); } else { jam(); - frag.m_tree.m_root = n4Addr; + frag.m_tree.m_root = loc4; } // set balance of changed nodes - n4Ptr.p->setBalance(0); - if (n4Bal == 0) { + node4.setBalance(0); + if (bal4 == 0) { jam(); - n2Ptr.p->setBalance(0); - n6Ptr.p->setBalance(0); - } else if (n4Bal == -n2Bal) { + node2.setBalance(0); + node6.setBalance(0); + } else if (bal4 == -bal2) { jam(); - n2Ptr.p->setBalance(0); - n6Ptr.p->setBalance(n2Bal); - } else if (n4Bal == n2Bal) { + node2.setBalance(0); + node6.setBalance(bal2); + } else if (bal4 == bal2) { jam(); - n2Ptr.p->setBalance(-n2Bal); - n6Ptr.p->setBalance(0); + node2.setBalance(-bal2); + node6.setBalance(0); } else { ndbrequire(false); } // new top node - nodePtr = n4Ptr; + node = node4; } diff --git a/ndb/src/kernel/blocks/dbtux/Makefile.am b/ndb/src/kernel/blocks/dbtux/Makefile.am index 5ba59e8b3b7..0b48ad5724f 100644 --- a/ndb/src/kernel/blocks/dbtux/Makefile.am +++ b/ndb/src/kernel/blocks/dbtux/Makefile.am @@ -10,6 +10,8 @@ libdbtux_a_SOURCES = \ DbtuxCmp.cpp \ DbtuxDebug.cpp +INCLUDES_LOC = -I$(top_srcdir)/ndb/src/kernel/blocks/dbtup + include $(top_srcdir)/ndb/config/common.mk.am include $(top_srcdir)/ndb/config/type_kernel.mk.am diff --git a/ndb/src/kernel/blocks/dbtux/Times.txt b/ndb/src/kernel/blocks/dbtux/Times.txt new file mode 100644 index 00000000000..16c4102249b --- /dev/null +++ b/ndb/src/kernel/blocks/dbtux/Times.txt @@ -0,0 +1,52 @@ +index maintenance overhead +========================== + +"mc02" 2x1700 MHz linux-2.4.9 gcc-2.96 -O3 one db-node + +case a: index on Unsigned +testOIBasic -case u -table 1 -index 1 -fragtype small -threads 10 -rows 100000 -subloop 1 -nologging + +case b: index on Varchar(5) + Varchar(5) + Varchar(20) + Unsigned +testOIBasic -case u -table 2 -index 4 -fragtype small -threads 10 -rows 100000 -subloop 1 -nologging + +1 million rows, pk update without index, pk update with index +shows ms / 1000 rows for each and pct overhead +the figures are based on single run on idle machine + +040616 mc02/a 40 ms 87 ms 114 pct + mc02/b 51 ms 128 ms 148 pct + +optim 1 mc02/a 38 ms 85 ms 124 pct + mc02/b 51 ms 123 ms 140 pct + +optim 2 mc02/a 41 ms 80 ms 96 pct + mc02/b 51 ms 117 ms 128 pct + +optim 3 mc02/a 43 ms 80 ms 85 pct + mc02/b 54 ms 118 ms 117 pct + +optim 4 mc02/a 42 ms 80 ms 87 pct + mc02/b 51 ms 119 ms 129 pct + +optim 5 mc02/a 43 ms 77 ms 77 pct + mc02/b 54 ms 118 ms 117 pct + +optim 6 mc02/a 42 ms 70 ms 66 pct + mc02/b 53 ms 109 ms 105 pct + +optim 7 mc02/a 42 ms 69 ms 61 pct + mc02/b 52 ms 106 ms 101 pct + +optim 8 mc02/a 42 ms 69 ms 62 pct + mc02/b 54 ms 104 ms 92 pct + +optim 9 mc02/a 43 ms 67 ms 54 pct + mc02/b 53 ms 102 ms 91 pct + +optim 10 mc02/a 44 ms 65 ms 46 pct + mc02/b 53 ms 88 ms 66 pct + +optim 11 mc02/a 43 ms 63 ms 46 pct + mc02/b 52 ms 86 ms 63 pct + +vim: set et: diff --git a/ndb/src/kernel/blocks/qmgr/QmgrMain.cpp b/ndb/src/kernel/blocks/qmgr/QmgrMain.cpp index ac29614bc70..f2d2edb615d 100644 --- a/ndb/src/kernel/blocks/qmgr/QmgrMain.cpp +++ b/ndb/src/kernel/blocks/qmgr/QmgrMain.cpp @@ -525,7 +525,7 @@ void Qmgr::execCM_REGREQ(Signal* signal) cmRegConf->dynamicId = TdynId; c_clusterNodes.copyto(NdbNodeBitmask::Size, cmRegConf->allNdbNodes); sendSignal(Tblockref, GSN_CM_REGCONF, signal, - CmRegConf::SignalLength, JBB); + CmRegConf::SignalLength, JBA); DEBUG_START(GSN_CM_REGCONF, refToNode(Tblockref), ""); /** @@ -847,7 +847,7 @@ void Qmgr::execCM_NODEINFOCONF(Signal* signal) nodePtr.i = getOwnNodeId(); ptrAss(nodePtr, nodeRec); ndbrequire(nodePtr.p->phase == ZSTARTING); - ndbrequire(c_start.m_gsn = GSN_CM_NODEINFOREQ); + ndbrequire(c_start.m_gsn == GSN_CM_NODEINFOREQ); c_start.m_nodes.clearWaitingFor(nodeId); /** @@ -1019,7 +1019,7 @@ void Qmgr::execCM_ADD(Signal* signal) ndbrequire(addNodePtr.i == nodePtr.i); switch(type){ case CmAdd::Prepare: - ndbrequire(c_start.m_gsn = GSN_CM_NODEINFOREQ); + ndbrequire(c_start.m_gsn == GSN_CM_NODEINFOREQ); /** * Wait for CM_NODEINFO_CONF */ diff --git a/ndb/src/mgmapi/mgmapi.cpp b/ndb/src/mgmapi/mgmapi.cpp index 72ba282ad13..bb4b6be8221 100644 --- a/ndb/src/mgmapi/mgmapi.cpp +++ b/ndb/src/mgmapi/mgmapi.cpp @@ -430,7 +430,7 @@ const int no_of_type_values = (sizeof(type_values) / sizeof(ndb_mgm_type_atoi)); extern "C" -enum ndb_mgm_node_type +ndb_mgm_node_type ndb_mgm_match_node_type(const char * type) { if(type == 0) @@ -474,7 +474,7 @@ const int no_of_status_values = (sizeof(status_values) / sizeof(ndb_mgm_status_atoi)); extern "C" -enum ndb_mgm_node_status +ndb_mgm_node_status ndb_mgm_match_node_status(const char * status) { if(status == 0) diff --git a/ndb/src/ndbapi/Ndb.cpp b/ndb/src/ndbapi/Ndb.cpp index f55c37709b6..3be78bbf2f8 100644 --- a/ndb/src/ndbapi/Ndb.cpp +++ b/ndb/src/ndbapi/Ndb.cpp @@ -38,6 +38,10 @@ Name: Ndb.cpp #include <NdbEnv.h> #include <BaseString.hpp> +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + /**************************************************************************** void connect(); diff --git a/ndb/test/ndbapi/bank/Makefile.am b/ndb/test/ndbapi/bank/Makefile.am index 40a23d1dbfc..886d664aefb 100644 --- a/ndb/test/ndbapi/bank/Makefile.am +++ b/ndb/test/ndbapi/bank/Makefile.am @@ -3,7 +3,7 @@ ndbtest_PROGRAMS = testBank bankSumAccounts bankValidateAllGLs bankMakeGL bankTr noinst_LIBRARIES = libbank.a -libbank_a_SOURCES = Bank.cpp BankLoad.cpp +libbank_a_SOURCES = Bank.cpp BankLoad.cpp Bank.hpp testBank_SOURCES = testBank.cpp bankSumAccounts_SOURCES = bankSumAccounts.cpp diff --git a/ndb/test/ndbapi/testOIBasic.cpp b/ndb/test/ndbapi/testOIBasic.cpp index a47d9d2099e..0ca8ce79e2e 100644 --- a/ndb/test/ndbapi/testOIBasic.cpp +++ b/ndb/test/ndbapi/testOIBasic.cpp @@ -39,6 +39,7 @@ struct Opt { NdbDictionary::Object::FragmentType m_fragtype; const char* m_index; unsigned m_loop; + bool m_nologging; unsigned m_rows; unsigned m_scanrd; unsigned m_scanex; @@ -54,6 +55,7 @@ struct Opt { m_fragtype(NdbDictionary::Object::FragUndefined), m_index(0), m_loop(1), + m_nologging(false), m_rows(1000), m_scanrd(240), m_scanex(240), @@ -82,6 +84,7 @@ printhelp() << " -fragtype T fragment type single/small/medium/large" << endl << " -index xyz only given index numbers (digits 1-9)" << endl << " -loop N loop count full suite forever=0 [" << d.m_loop << "]" << endl + << " -nologging create tables in no-logging mode" << endl << " -rows N rows per thread [" << d.m_rows << "]" << endl << " -scanrd N scan read parallelism [" << d.m_scanrd << "]" << endl << " -scanex N scan exclusive parallelism [" << d.m_scanex << "]" << endl @@ -476,7 +479,7 @@ tt1 = { "TT1", 5, tt1col, 4, tt1itab }; -// tt2 + tt2x1 tt2x2 tt2x3 +// tt2 + tt2x1 tt2x2 tt2x3 tt2x4 static const Col tt2col[] = { @@ -505,6 +508,14 @@ tt2x3col[] = { { 1, tt2col[4] } }; +static const ICol +tt2x4col[] = { + { 0, tt2col[4] }, + { 1, tt2col[3] }, + { 2, tt2col[2] }, + { 3, tt2col[1] } +}; + static const ITab tt2x1 = { "TT2X1", 2, tt2x1col @@ -521,15 +532,21 @@ tt2x3 = { }; static const ITab +tt2x4 = { + "TT2X4", 4, tt2x4col +}; + +static const ITab tt2itab[] = { tt2x1, tt2x2, - tt2x3 + tt2x3, + tt2x4 }; static const Tab tt2 = { - "TT2", 5, tt2col, 3, tt2itab + "TT2", 5, tt2col, 4, tt2itab }; // all tables @@ -823,6 +840,9 @@ createtable(Par par) if (par.m_fragtype != NdbDictionary::Object::FragUndefined) { t.setFragmentType(par.m_fragtype); } + if (par.m_nologging) { + t.setLogging(false); + } for (unsigned k = 0; k < tab.m_cols; k++) { const Col& col = tab.m_col[k]; NdbDictionary::Column c(col.m_name); @@ -2202,7 +2222,6 @@ pkupdateindexbuild(Par par) { if (par.m_no == 0) { CHK(createindex(par) == 0); - CHK(invalidateindex(par) == 0); } else { CHK(pkupdate(par) == 0); } @@ -2493,6 +2512,7 @@ tbusybuild(Par par) RUNSTEP(par, pkinsert, MT); for (unsigned i = 0; i < par.m_subloop; i++) { RUNSTEP(par, pkupdateindexbuild, MT); + RUNSTEP(par, invalidateindex, MT); RUNSTEP(par, readverify, MT); RUNSTEP(par, dropindex, ST); } @@ -2500,9 +2520,28 @@ tbusybuild(Par par) } static int -ttiming(Par par) +ttimebuild(Par par) +{ + Tmr t1; + RUNSTEP(par, droptable, ST); + RUNSTEP(par, createtable, ST); + RUNSTEP(par, invalidatetable, MT); + for (unsigned i = 0; i < par.m_subloop; i++) { + RUNSTEP(par, pkinsert, MT); + t1.on(); + RUNSTEP(par, createindex, ST); + t1.off(par.m_totrows); + RUNSTEP(par, invalidateindex, MT); + RUNSTEP(par, dropindex, ST); + } + LL1("build index - " << t1.time()); + return 0; +} + +static int +ttimemaint(Par par) { - Tmr t0, t1, t2; + Tmr t1, t2; RUNSTEP(par, droptable, ST); RUNSTEP(par, createtable, ST); RUNSTEP(par, invalidatetable, MT); @@ -2511,16 +2550,13 @@ ttiming(Par par) t1.on(); RUNSTEP(par, pkupdate, MT); t1.off(par.m_totrows); - t0.on(); RUNSTEP(par, createindex, ST); RUNSTEP(par, invalidateindex, MT); - t0.off(par.m_totrows); t2.on(); RUNSTEP(par, pkupdate, MT); t2.off(par.m_totrows); RUNSTEP(par, dropindex, ST); } - LL1("build index - " << t0.time()); LL1("update - " << t1.time()); LL1("update indexed - " << t2.time()); LL1("overhead - " << t2.over(t1)); @@ -2551,7 +2587,8 @@ tcaselist[] = { TCase("b", tpkops, "pk operations and scan reads"), TCase("c", tmixedops, "pk operations and scan operations"), TCase("d", tbusybuild, "pk operations and index build"), - TCase("t", ttiming, "time index build and maintenance"), + TCase("t", ttimebuild, "time index build"), + TCase("u", ttimemaint, "time index maintenance"), TCase("z", tdrop, "drop test tables") }; @@ -2689,6 +2726,10 @@ NDB_COMMAND(testOIBasic, "testOIBasic", "testOIBasic", "testOIBasic", 65535) continue; } } + if (strcmp(arg, "-nologging") == 0) { + g_opt.m_nologging = true; + continue; + } if (strcmp(arg, "-rows") == 0) { if (++argv, --argc > 0) { g_opt.m_rows = atoi(argv[0]); diff --git a/ndb/test/run-test/make-html-reports.sh b/ndb/test/run-test/make-html-reports.sh index 079650a729f..89f13a4b62a 100755 --- a/ndb/test/run-test/make-html-reports.sh +++ b/ndb/test/run-test/make-html-reports.sh @@ -1,106 +1,23 @@ #!/bin/sh -# NAME -# make-html-reports.sh -# -# SYNOPSIS -# make-html-reports.sh [-q] [ -R <YYYY-MM-DD> ] [ -s <src dir> ] [ -d <dst dir> ] [ -c <conf dir> ] -# -# DESCRIPTION -# -# OPTIONS -# -# EXAMPLES -# -# -# ENVIRONMENT -# NDB_PROJ_HOME Home dir for ndb -# -# FILES -# $NDB_PROJ_HOME/lib/funcs.sh general shell script functions -# -# -# SEE ALSO -# -# DIAGNOSTICTS -# -# VERSION -# 1.0 -# -# AUTHOR -# Jonas Oreland -# - -progname=`basename $0` -synopsis="make-html-reports.sh [ -R <YYYY-MM-DD> ] [ -s <src dir> ] [ -d <dst dir> ] [ -c <conf dir> ]" - -: ${NDB_PROJ_HOME:?} # If undefined, exit with error message - -: ${NDB_LOCAL_BUILD_OPTIONS:=--} # If undef, set to --. Keeps getopts happy. - # You may have to experiment a bit - # to get quoting right (if you need it). - - -. $NDB_PROJ_HOME/lib/funcs.sh # Load some good stuff - -# defaults for options related variables -# - - -src_dir=`pwd` -dst_dir=`pwd` -conf_dir=`pwd` -report_date=`date '+%Y-%m-%d'` -uniq_id=$$.$$ -verbose=yes - -# used if error when parsing the options environment variable -# -env_opterr="options environment variable: <<$options>>" - -# Option parsing, for the options variable as well as the command line. -# -# We want to be able to set options in an environment variable, -# as well as on the command line. In order not to have to repeat -# the same getopts information twice, we loop two times over the -# getopts while loop. The first time, we process options from -# the options environment variable, the second time we process -# options from the command line. -# -# The things to change are the actual options and what they do. -# -# - -for optstring in "$options" "" # 1. options variable 2. cmd line -do - - while getopts q:s:R:d:c: i $optstring # optstring empty => no arg => cmd line - do - case $i in - - q) verbose="";; # echo important things - d) dst_dir=$OPTARG;; # Destination directory - s) src_dir=$OPTARG;; # Destination directory - c) conf_dir=$OPTARG;; # - R) report_date=$OPTARG;; # - \?) syndie $env_opterr;; # print synopsis and exit - - esac - done - - [ -n "$optstring" ] && OPTIND=1 # Reset for round 2, cmdline options - env_opterr= # Round 2 should not use the value +src_dir=$1 +run=$2 +date=$3 +src_file=$src_dir/report.txt -done -shift `expr $OPTIND - 1` - -src_dir=`abspath $src_dir` -dst_dir=`abspath $dst_dir` -conf_dir=`abspath $conf_dir` +if [ ! -f $src_dir/report.txt ] +then + echo "$src_dir/report.txt is missing" + exit 1 +fi ### # # General html functions +trim(){ + echo $* +} + header(){ cat <<EOF <html><head><title>$*</title></head> @@ -166,64 +83,7 @@ hr(){ EOF } -# --- option parsing done --- - # -- Verify -trace "Verifying arguments" -summary_file=$src_dir/reports/summary.$report_date - -if [ ! -r $summary_file ] -then - syndie "Invalid src directory or report date: $summary_file not found" -fi - -if [ ! -d $conf_dir/configurations ] -then - syndie "Invalid src directory: $conf_dir/configurations not found" -fi - -if [ ! -d $conf_dir/testcases ] -then - syndie "Invalid src directory: $conf_dir/testcases not found" -fi - -if [ ! -d $dst_dir ] -then - syndie "Invalid dst dir..." -fi - -# --- option verifying done --- - -trace "src_dir: $src_dir" -trace "dst_dir: $dst_dir" -trace "conf_dir: $conf_dir" -trace "report date: $report_date" - -### -config_spec(){ - cat <<EOF -<a href=#$1>$1</a> -EOF -} - -config_spec_include(){ - # Print the $1 file to the file we are generating - cat <<EOF -<a name=$1><pre> -EOF - if [ -r $conf_dir/configurations/$1 ] - then - cat -E $conf_dir/configurations/$1 | sed 's/\$/<BR>/g' - else - cat <<EOF - Config spec $1 not found -EOF - fi -cat <<EOF -</pre></a> -EOF -} - time_spec(){ # $1 - secs _ts_tmp=$1 @@ -232,8 +92,14 @@ time_spec(){ _ts_tmp=`expr $_ts_tmp / 60` _ts_m=`expr $_ts_tmp % 60` - _ts_tmp=`expr $_ts_tmp / 60` + if [ $_ts_tmp -ge 60 ] + then + _ts_tmp=`expr $_ts_tmp / 60` + else + _ts_tmp=0 + fi + a=3 _ts_h=$_ts_tmp if [ $_ts_h -gt 0 ] @@ -247,191 +113,77 @@ time_spec(){ echo $ret } -log_spec(){ - _ff_=$src_dir/log/$report_date/$1.$2/test.$3.out - if [ -r $_ff_ ] && [ -s $_ff_ ] - then - _f2_=$dst_dir/log.$report_date.$1.$2.$3.out.gz - if [ -r $_f2_ ] - then - rm $_f2_ - fi - cp $_ff_ $dst_dir/log.$report_date.$1.$2.$3.out - gzip $dst_dir/log.$report_date.$1.$2.$3.out - rm -f $dst_dir/log.$report_date.$1.$2.$3.out - echo "<a href=log.$report_date.$1.$2.$3.out.gz>Log file</a>" - else - echo "-" - fi -} +### Main -err_spec(){ - _ff_=$src_dir/log/$report_date/$1.$2/test.$3.err.tar - if [ -r $_ff_ ] && [ -s $_ff_ ] - then - cp $_ff_ $dst_dir/err.$report_date.$1.$2.$3.err.tar - gzip $dst_dir/err.$report_date.$1.$2.$3.err.tar - rm -f $dst_dir/err.$report_date.$1.$2.$3.err.tar - echo "<a href=err.$report_date.$1.$2.$3.err.tar.gz>Error tarball</a>" - else - echo "-" - fi -} +report_file=$src_dir/report.html +summary_file=$src_dir/summary.html -command_spec(){ - echo $* | sed 's/;/<BR>/g' -} +passed=0 +failed=0 +total=0 -### Main +pass(){ + passed=`expr $passed + 1` +} -html_summary_file=$dst_dir/summary.$report_date.html +fail(){ + failed=`expr $failed + 1` +} -trace "Creating summary" ( - eval `grep "TOTAL" $summary_file | awk -F";" '{ printf("test_file=\"%s\"; elapsed=\"%s\"; started=\"%s\"; stopped=\"%s\"", $2, $3, $4, $5); }'` - - header "Autotest summary $report_date" - heading 1 "Autotest summary $report_date" - table - row ; column `bold test file: `; column $test_file ; end_row - row ; column `bold Started:` ; column "$started "; end_row - row ; column `bold Stopped:` ; column "$stopped "; end_row - row ; column `bold Elapsed:` ; column "`time_spec $elapsed secs`" ; end_row - end_table - hr - - table "border=1" - row - c_column `bold Report` - c_column `bold Tag` - c_column `bold Version` - c_column `bold Distr-Config` - c_column `bold Db-Config` - c_column `bold Type` - c_column `bold Test file` - c_column `bold Make` - c_column `bold Config` - c_column `bold Test time` - c_column `bold Passed` - c_column `bold Failed` - end_row - - grep -v "^#" $summary_file | grep -v TOTAL | sed 's/;/ /g' | \ - while read tag version config template type test_file make_res make_time conf_res conf_time test_time passed failed - do + header Report $run $date + table "border=1" row - if [ -r $src_dir/reports/report.$tag.$version.$config.$template.$type.$test_file.$report_date ] - then - column "<a href=\"report.$tag.$version.$config.$template.$type.$test_file.$report_date.html\">report</a>" - else - column "-" - fi - - column $tag - column $version - column $config - column $template - column $type - column $test_file - column "$make_res(`time_spec $make_time`)" - column "$conf_res(`time_spec $conf_time`)" - c_column "`time_spec $test_time`" - c_column `bold $passed` - c_column `bold $failed` + column `bold Test case` + column `bold Result` + column `bold Elapsed` + column `bold Log` end_row - done - end_table +) > $report_file - footer -) > $html_summary_file - -for i in $src_dir/reports/report.*.$report_date +cat $src_file | while read line do - f=`basename $i` - trace "Creating report: $f" - eval `echo $f | awk -F"." '{printf("tag=%s;version=%s;config=%s;template=%s;type=%s;test_file=%s", $2, $3, $4, $5, $6, $7);}'` - - ( - header "Autotest report $report_date" - heading 1 "Autotest report $report_date" - table #"border=1" - row ; column `bold Tag:`; column $tag ; end_row - row ; column `bold Version:` ; column $version ; end_row - row ; column `bold Configuration:` ; column `config_spec $config`; end_row - row ; column `bold Template:` ; column `config_spec $template`; end_row - row ; column `bold Type:` ; column $type ; end_row - row ; column `bold Test file:` ; column $test_file; end_row - end_table - hr - - table "border=1" - row - c_column `bold Test case` - c_column `bold Result` - c_column `bold Test time` - c_column `bold Logfile` - c_column `bold Error tarfile` - end_row - - grep -v "^#" $i | sed 's/;/ /g' | \ - while read test_no test_res test_time cmd - do - row - column "`command_spec $cmd`" - case "$test_res" in - 0) - column "PASSED";; - 1001) - column "API error";; - 1002) - column "Max time expired";; - 1003) - column "Mgm port busy";; - *) - column "Unknown: $test_res";; - esac - - column "`time_spec $test_time`" - - column "`log_spec $tag $version $test_no`" - column "`err_spec $tag $version $test_no`" - end_row - done - end_table - - # Last on page we include spec - # of used machines and template for config - # for future reference - hr - table "border=1" - row; column `bold Configuration:` $config; end_row - row; column `config_spec_include $config`; end_row - end_table - hr - table "border=1" - row; column `bold Template:` $template; end_row - row; column `config_spec_include $template`; end_row - end_table - - footer - - ) > $dst_dir/$f.html + eval `echo $line | awk -F";" '{ printf("prg=\"%s\"; no=\"%s\"; res=\"%s\"; time=\"%s\"", $1, $2, $3, $4); }'` + + prg=`trim $prg` + no=`trim $no` + res=`trim $res` + time=`trim $time` + res_dir="<a href=\"result.$no/\">log</a>" + + ts=`time_spec $time` + res_txt="" + case $res in + 0) pass; res_txt="PASSED"; res_dir=" ";; + *) fail; res_txt="FAILED";; + esac + total=`expr $total + $time` + + ( + row + column $prg + column $res_txt + column $ts + column $res_dir + end_row + ) >> $report_file + + ( + row + column $run + column $date + column $passed + column $failed + column `time_spec $total` + column "<a href=\"result-$run/$date/report.html\">report</a>" + column "<a href=\"result-$run/$date/log.txt\">log.txt</a>" + end_row + ) > $summary_file done -# Re creating index -trace "Recreating index" ( - header "Autotest super-duper index" - heading 1 "<center>Autotest super-duper index</center>" - hr - for i in `ls $dst_dir/summary.*.html | sort -r -n` - do - f=`basename $i` - cat <<EOF -<p><a href=$f>$f</a></p> -EOF - done - footer -) > $dst_dir/index.html + end_table + footer +) >> $report_file exit 0 diff --git a/netware/BUILD/mwenv b/netware/BUILD/mwenv index 7eca2711a79..22de9a4ad87 100755 --- a/netware/BUILD/mwenv +++ b/netware/BUILD/mwenv @@ -1,10 +1,10 @@ #! /bin/sh -# WINE_BUILD_DIR, BUILD_DIR, and VERSION must be correct before compiling +# F:/mydev, /home/kp/mydev, and 4.0.21 must be correct before compiling # This values are normally changed by the nwbootstrap script # the default is "F:/mydev" -export MYDEV="WINE_BUILD_DIR" +export MYDEV="F:/mydev" export MWCNWx86Includes="$MYDEV/libc/include;$MYDEV/fs64/headers;$MYDEV/zlib-1.1.4;$MYDEV" export MWNWx86Libraries="$MYDEV/libc/imports;$MYDEV/mw/lib;$MYDEV/fs64/imports;$MYDEV/zlib-1.1.4;$MYDEV/openssl;$MYDEV/mysql-VERSION/netware/BUILD" @@ -13,7 +13,7 @@ export MWNWx86LibraryFiles="libcpre.o;libc.imp;netware.imp;mwcrtl.lib;mwcpp.lib; export WINEPATH="$MYDEV/mw/bin" # the default added path is "$HOME/mydev/mysql-x.x-x/netware/BUILD" -export PATH="$PATH:BUILD_DIR/mysql-VERSION/netware/BUILD" +export PATH="$PATH:/home/kp/mydev/mysql-VERSION/netware/BUILD" export AR='mwldnlm' export AR_FLAGS='-type library -o' diff --git a/netware/BUILD/nwbootstrap b/netware/BUILD/nwbootstrap index 7737dd8898a..25e843c87e3 100755 --- a/netware/BUILD/nwbootstrap +++ b/netware/BUILD/nwbootstrap @@ -176,6 +176,11 @@ done echo "generating llibmysql.imp file..." awk 'BEGIN{x=0;} x==1 {print $1;next} /EXPORTS/{x=1}' libmysql/libmysql.def > netware/libmysql.imp +# create the libmysql.imp file in netware folder from libmysql/libmysql.def file +echo "generating llibmysql.imp file..." +awk 'BEGIN{x=0;} x==1 {print $1;next} /EXPORTS/{x=1}' libmysql/libmysql.def > netware/libmysql.imp + + # build linux tools echo "compiling linux tools..." ./netware/BUILD/compile-linux-tools diff --git a/netware/Makefile.am b/netware/Makefile.am index 324c6fd13fa..2467270f27b 100644 --- a/netware/Makefile.am +++ b/netware/Makefile.am @@ -16,6 +16,8 @@ if HAVE_NETWARE INCLUDES = -I$(srcdir)/../include -I../include -I.. +LDADD = @CLIENT_EXTRA_LDFLAGS@ ../mysys/libmysys.a \ + ../dbug/libdbug.a ../strings/libmystrings.a bin_PROGRAMS = mysqld_safe mysql_install_db mysql_test_run libmysql mysqld_safe_SOURCES= mysqld_safe.c my_manage.c mysql_install_db_SOURCES= mysql_install_db.c my_manage.c diff --git a/netware/mysql_test_run.c b/netware/mysql_test_run.c index 9c99e8a64b5..37985b9058e 100644 --- a/netware/mysql_test_run.c +++ b/netware/mysql_test_run.c @@ -16,20 +16,15 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <stdlib.h> -#include <stdio.h> -#include <errno.h> +#include <my_global.h> +#include <m_string.h> #include <dirent.h> -#include <string.h> #include <screen.h> #include <nks/vm.h> #include <ctype.h> #include <sys/stat.h> -#include <unistd.h> -#include <fcntl.h> #include <sys/mode.h> -#include "my_config.h" #include "my_manage.h" /****************************************************************************** @@ -1286,7 +1281,6 @@ int main(int argc, char **argv) return 0; } - /* Synopsis: This function breaks the string into a sequence of tokens. The difference diff --git a/netware/mysqld_safe.c b/netware/mysqld_safe.c index 8d4a5c4a296..a307b52bb7e 100644 --- a/netware/mysqld_safe.c +++ b/netware/mysqld_safe.c @@ -1,20 +1,20 @@ /*
- Copyright (c) 2003 Novell, Inc. All Rights Reserved.
+ Copyright (c) 2003 Novell, Inc. All Rights Reserved. - This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; 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.
+ 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
+ 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 <stdlib.h>
#include <stdio.h>
@@ -32,7 +32,7 @@ /******************************************************************************
global variables
-
+ ******************************************************************************/
char autoclose;
char basedir[PATH_MAX];
@@ -47,38 +47,38 @@ char mysqld[PATH_MAX]; char hostname[PATH_MAX];
char default_option[PATH_MAX];
-FILE *log_fd = NULL;
+FILE *log_fd= NULL; /******************************************************************************
prototypes
-
+ ******************************************************************************/
void usage(void); void vlog(char *, va_list);
void log(char *, ...);
-void start_defaults(int, char*[]);
+void start_defaults(int, char *[]); void finish_defaults();
void read_defaults(arg_list_t *); -void parse_args(int, char*[]);
-void get_options(int, char*[]);
+void parse_args(int, char *[]); +void get_options(int, char *[]); void check_data_vol();
void check_setup();
void check_tables();
-void mysql_start(int, char*[]);
+void mysql_start(int, char *[]); void parse_setvar(char *arg); /******************************************************************************
functions
-
+ ******************************************************************************/
/******************************************************************************
usage() - + Show usage. ******************************************************************************/ @@ -86,7 +86,7 @@ void usage(void) { // keep the screen up setscreenmode(SCR_NO_MODE); - + puts("\ \n\ usage: mysqld_safe [options]\n\ @@ -107,14 +107,14 @@ options:\n\ --mysqld=<file> Use the <file> MySQL daemon.\n\ \n\ "); - + exit(-1); } /****************************************************************************** vlog()
-
+ Log the message.
******************************************************************************/
@@ -122,7 +122,7 @@ void vlog(char *format, va_list ap) {
vfprintf(stdout, format, ap);
fflush(stdout);
-
+ if (log_fd)
{
vfprintf(log_fd, format, ap);
@@ -133,25 +133,25 @@ void vlog(char *format, va_list ap) /******************************************************************************
log()
-
+ Log the message.
******************************************************************************/
void log(char *format, ...)
{
va_list ap;
-
+ va_start(ap, format);
vlog(format, ap);
-
+ va_end(ap);
}
/******************************************************************************
start_defaults()
-
+ Start setting the defaults.
******************************************************************************/
@@ -159,30 +159,30 @@ void start_defaults(int argc, char *argv[]) {
struct stat buf;
int i;
-
+ // default options
- static char *default_options[] =
+ static char *default_options[]= {
- "--no-defaults",
- "--defaults-file=",
- "--defaults-extra-file=",
- NULL
+ "--no-defaults", + "--defaults-file=", + "--defaults-extra-file=", + NULL };
-
+ // autoclose
- autoclose = FALSE;
-
+ autoclose= FALSE; + // basedir
get_basedir(argv[0], basedir);
-
+ // check-tables - checktables = FALSE; - + checktables= FALSE; + // hostname
- if (gethostname(hostname,PATH_MAX) < 0)
+ if (gethostname(hostname, PATH_MAX) < 0) {
// default
- strcpy(hostname,"mysql");
+ strcpy(hostname, "mysql"); }
// address
@@ -190,30 +190,30 @@ void start_defaults(int argc, char *argv[]) // port
snprintf(port, PATH_MAX, "3306");
-
+ // default option
- default_option[0] = NULL;
- for (i=0; (argc > 1) && default_options[i]; i++)
- {
- if(!strnicmp(argv[1], default_options[i], strlen(default_options[i])))
- {
- strncpy(default_option, argv[1], PATH_MAX);
- break;
- }
- }
-
+ default_option[0]= NULL; + for (i= 0; (argc > 1) && default_options[i]; i++) + { + if (!strnicmp(argv[1], default_options[i], strlen(default_options[i]))) + { + strncpy(default_option, argv[1], PATH_MAX); + break; + } + } + // set after basedir is established
- datadir[0] = NULL;
- pid_file[0] = NULL;
- err_log[0] = NULL;
- safe_log[0] = NULL;
- mysqld[0] = NULL;
+ datadir[0]= NULL; + pid_file[0]= NULL; + err_log[0]= NULL; + safe_log[0]= NULL; + mysqld[0]= NULL; }
/******************************************************************************
finish_defaults()
-
+ Finish settig the defaults.
******************************************************************************/
@@ -221,22 +221,27 @@ void finish_defaults() {
struct stat buf;
int i;
-
+ // datadir
- if (!datadir[0]) snprintf(datadir, PATH_MAX, "%s/data", basedir);
-
+ if (!datadir[0]) + snprintf(datadir, PATH_MAX, "%s/data", basedir); + // pid-file
- if (!pid_file[0]) snprintf(pid_file, PATH_MAX, "%s/%s.pid", datadir, hostname);
-
+ if (!pid_file[0]) + snprintf(pid_file, PATH_MAX, "%s/%s.pid", datadir, hostname); + // err-log
- if (!err_log[0]) snprintf(err_log, PATH_MAX, "%s/%s.err", datadir, hostname);
+ if (!err_log[0]) + snprintf(err_log, PATH_MAX, "%s/%s.err", datadir, hostname); // safe-log
- if (!safe_log[0]) snprintf(safe_log, PATH_MAX, "%s/%s.safe", datadir, hostname); + if (!safe_log[0]) + snprintf(safe_log, PATH_MAX, "%s/%s.safe", datadir, hostname); // mysqld
- if (!mysqld[0]) snprintf(mysqld, PATH_MAX, "%s/bin/mysqld-max", basedir);
-
+ if (!mysqld[0]) + snprintf(mysqld, PATH_MAX, "%s/bin/mysqld-max", basedir); + if (stat(mysqld, &buf))
{
snprintf(mysqld, PATH_MAX, "%s/bin/mysqld", basedir);
@@ -246,7 +251,7 @@ void finish_defaults() /******************************************************************************
read_defaults()
-
+ Read the defaults.
******************************************************************************/
@@ -257,64 +262,66 @@ void read_defaults(arg_list_t *pal) char mydefaults[PATH_MAX];
char line[PATH_MAX];
FILE *fp;
-
- // defaults output file
- snprintf(defaults_file, PATH_MAX, "%s/bin/defaults.out", basedir);
- remove(defaults_file);
- // mysqladmin file
+ // defaults output file + snprintf(defaults_file, PATH_MAX, "%s/bin/defaults.out", basedir); + remove(defaults_file); + + // mysqladmin file snprintf(mydefaults, PATH_MAX, "%s/bin/my_print_defaults", basedir);
-
+ // args
init_args(&al); add_arg(&al, mydefaults); - if (default_option[0]) add_arg(&al, default_option); + if (default_option[0]) + add_arg(&al, default_option); add_arg(&al, "mysqld"); add_arg(&al, "server"); add_arg(&al, "mysqld_safe"); add_arg(&al, "safe_mysqld"); - spawn(mydefaults, &al, TRUE, NULL, defaults_file, NULL); + spawn(mydefaults, &al, TRUE, NULL, defaults_file, NULL); free_args(&al); - // gather defaults
- if((fp = fopen(defaults_file, "r")) != NULL)
- {
- while(fgets(line, PATH_MAX, fp))
- {
+ // gather defaults + if ((fp= fopen(defaults_file, "r")) != NULL) + { + while (fgets(line, PATH_MAX, fp)) + { char *p;
-
+ // remove end-of-line character
- if ((p = strrchr(line, '\n')) != NULL) *p = '\0';
-
+ if ((p= strrchr(line, '\n')) != NULL) + *p= '\0'; + // add the option as an argument
- add_arg(pal, line);
- }
-
- fclose(fp);
- }
-
- // remove file
- remove(defaults_file);
+ add_arg(pal, line); + } + + fclose(fp); + } + + // remove file + remove(defaults_file); }
/******************************************************************************
parse_args()
-
+ Get the options.
******************************************************************************/
void parse_args(int argc, char *argv[])
{
- int index = 0;
+ int index= 0; int c;
-
+ // parse options
enum opts
{
- OPT_BASEDIR = 0xFF,
+ OPT_BASEDIR= 0xFF, OPT_DATADIR,
OPT_PID_FILE,
OPT_BIND_ADDRESS,
@@ -325,65 +332,64 @@ void parse_args(int argc, char *argv[]) OPT_HELP, OPT_SETVAR };
-
- static struct option options[] =
+ + static struct option options[]= {
- {"autoclose", no_argument, &autoclose, TRUE},
- {"basedir", required_argument, 0, OPT_BASEDIR},
- {"check-tables", no_argument, &checktables, TRUE}, - {"datadir", required_argument, 0, OPT_DATADIR},
- {"pid-file", required_argument, 0, OPT_PID_FILE},
- {"bind-address", required_argument, 0, OPT_BIND_ADDRESS},
- {"port", required_argument, 0, OPT_PORT},
- {"err-log", required_argument, 0, OPT_ERR_LOG},
- {"safe-log", required_argument, 0, OPT_SAFE_LOG},
- {"mysqld", required_argument, 0, OPT_MYSQLD},
- {"help", no_argument, 0, OPT_HELP}, - {"set-variable", required_argument, 0, OPT_SETVAR}, - {0, 0, 0, 0}
+ {"autoclose", no_argument, &autoclose, TRUE}, + {"basedir", required_argument, 0, OPT_BASEDIR}, + {"check-tables", no_argument, &checktables, TRUE}, + {"datadir", required_argument, 0, OPT_DATADIR}, + {"pid-file", required_argument, 0, OPT_PID_FILE}, + {"bind-address", required_argument, 0, OPT_BIND_ADDRESS}, + {"port", required_argument, 0, OPT_PORT}, + {"err-log", required_argument, 0, OPT_ERR_LOG}, + {"safe-log", required_argument, 0, OPT_SAFE_LOG}, + {"mysqld", required_argument, 0, OPT_MYSQLD}, + {"help", no_argument, 0, OPT_HELP}, + {"set-variable", required_argument, 0, OPT_SETVAR}, + {0, 0, 0, 0} };
-
+ // we have to reset getopt_long because we use it multiple times
- optind = 1;
-
+ optind= 1; + // turn off error reporting
- opterr = 0;
-
- while ((c = getopt_long(argc, argv, "b:h:P:", options, &index)) >= 0)
+ opterr= 0; + + while ((c= getopt_long(argc, argv, "b:h:P:", options, &index)) >= 0) {
- switch (c)
- {
+ switch (c) { case OPT_BASEDIR:
case 'b':
strcpy(basedir, optarg);
break;
-
+ case OPT_DATADIR:
case 'h':
strcpy(datadir, optarg);
break;
-
+ case OPT_PID_FILE:
strcpy(pid_file, optarg);
break;
-
+ case OPT_BIND_ADDRESS:
strcpy(address, optarg);
break;
-
+ case OPT_PORT:
case 'P':
strcpy(port, optarg);
break;
-
+ case OPT_ERR_LOG:
strcpy(err_log, optarg);
break;
-
+ case OPT_SAFE_LOG:
strcpy(safe_log, optarg);
break;
-
+ case OPT_MYSQLD:
strcpy(mysqld, optarg);
break;
@@ -391,11 +397,11 @@ void parse_args(int argc, char *argv[]) case OPT_SETVAR: parse_setvar(optarg); break; - + case OPT_HELP: usage(); break; - + default:
// ignore
break;
@@ -410,14 +416,15 @@ void parse_args(int argc, char *argv[]) void parse_setvar(char *arg) { char *pos; - + if ((pos= strindex(arg, "port"))) { - for (; *pos && *pos != '='; pos++) ; + for (; *pos && *pos != '='; pos++); if (*pos) strcpy(port, pos + 1); } } + /****************************************************************************** @@ -425,14 +432,14 @@ void parse_setvar(char *arg) /******************************************************************************
get_options()
-
+ Get the options.
******************************************************************************/
void get_options(int argc, char *argv[])
{
arg_list_t al; -
+ // start defaults
start_defaults(argc, argv);
@@ -442,10 +449,10 @@ void get_options(int argc, char *argv[]) read_defaults(&al); parse_args(al.argc, al.argv); free_args(&al); -
+ // command-line arguments
parse_args(argc, argv);
-
+ // finish defaults
finish_defaults();
}
@@ -453,7 +460,7 @@ void get_options(int argc, char *argv[]) /******************************************************************************
check_data_vol()
-
+ Check the database volume.
******************************************************************************/
@@ -463,23 +470,23 @@ void check_data_vol() struct volume_info vol;
char buff[PATH_MAX];
char *p;
-
+ // clear struct
memset(&vol, 0, sizeof(vol));
-
+ // find volume name
strcpy(buff, datadir);
- if (p = strchr(buff, ':'))
+ if (p= strchr(buff, ':')) {
// terminate after volume name
- *p = 0;
+ *p= 0; }
else
{
// assume SYS volume
strcpy(buff, "SYS");
}
-
+ // retrieve information
netware_vol_info_from_name(&vol, buff);
@@ -493,25 +500,25 @@ void check_data_vol() /******************************************************************************
check_setup()
-
+ Check the current setup.
******************************************************************************/
void check_setup()
{
- struct stat info;
+ struct stat info; char temp[PATH_MAX];
-
+ // remove any current pid_file
- if (!stat(pid_file, &info) && (remove(pid_file) < 0))
- {
+ if (!stat(pid_file, &info) && (remove(pid_file) < 0)) + { log("ERROR: Unable to remove current pid file!\n\n");
exit(-1);
- }
-
- // check the data volume
+ } + + // check the data volume check_data_vol();
-
+ // check for a database
snprintf(temp, PATH_MAX, "%s/mysql/host.frm", datadir);
if (stat(temp, &info))
@@ -524,7 +531,7 @@ void check_setup() /******************************************************************************
check_tables()
-
+ Check the database tables.
******************************************************************************/
@@ -532,21 +539,21 @@ void check_tables() {
arg_list_t al; char mycheck[PATH_MAX];
- char table[PATH_MAX];
- char db[PATH_MAX];
- DIR *datadir_entry, *db_entry, *table_entry;
-
- // status
+ char table[PATH_MAX]; + char db[PATH_MAX]; + DIR *datadir_entry, *db_entry, *table_entry; + + // status log("checking tables...\n");
-
+ // list databases
- if ((datadir_entry = opendir(datadir)) == NULL)
- {
- return;
- }
+ if ((datadir_entry= opendir(datadir)) == NULL) + { + return; + } - while((db_entry = readdir(datadir_entry)) != NULL)
- {
+ while ((db_entry= readdir(datadir_entry)) != NULL) + { if (db_entry->d_name[0] == '.')
{
// Skip
@@ -555,71 +562,71 @@ void check_tables() {
// create long db name
snprintf(db, PATH_MAX, "%s/%s", datadir, db_entry->d_name);
-
+ // list tables
- if ((db_entry = opendir(db)) == NULL)
+ if ((db_entry= opendir(db)) == NULL) {
- continue;
+ continue; }
-
- while((table_entry = readdir(db_entry)) != NULL)
+ + while ((table_entry= readdir(db_entry)) != NULL) {
- // create long table name
- snprintf(table, PATH_MAX, "%s/%s", db, strlwr(table_entry->d_name));
-
- if (strindex(table, ".myi"))
- {
- // ** myisamchk
-
- // mysqladmin file
- snprintf(mycheck, PATH_MAX, "%s/bin/myisamchk", basedir);
-
- // args
- init_args(&al); - add_arg(&al, mycheck); - add_arg(&al, "--silent"); - add_arg(&al, "--force"); - add_arg(&al, "--fast"); - add_arg(&al, "--medium-check"); - add_arg(&al, "-O"); - add_arg(&al, "key_buffer=64M"); - add_arg(&al, "-O"); - add_arg(&al, "sort_buffer=64M"); - add_arg(&al, table); -
- spawn(mycheck, &al, TRUE, NULL, NULL, NULL); -
- free_args(&al); - }
- else if (strindex(table, ".ism"))
- {
- // ** isamchk
-
- // mysqladmin file
- snprintf(mycheck, PATH_MAX, "%s/bin/isamchk", basedir);
-
- // args
- init_args(&al); - add_arg(&al, mycheck); - add_arg(&al, "--silent"); - add_arg(&al, "--force"); - add_arg(&al, "-O"); - add_arg(&al, "sort_buffer=64M"); - add_arg(&al, table); -
- spawn(mycheck, &al, TRUE, NULL, NULL, NULL); -
- free_args(&al); - }
+ // create long table name + snprintf(table, PATH_MAX, "%s/%s", db, strlwr(table_entry->d_name)); + + if (strindex(table, ".myi")) + { + // ** myisamchk + + // mysqladmin file + snprintf(mycheck, PATH_MAX, "%s/bin/myisamchk", basedir); +
+ // args + init_args(&al); + add_arg(&al, mycheck); + add_arg(&al, "--silent"); + add_arg(&al, "--force"); + add_arg(&al, "--fast"); + add_arg(&al, "--medium-check"); + add_arg(&al, "-O"); + add_arg(&al, "key_buffer=64M"); + add_arg(&al, "-O"); + add_arg(&al, "sort_buffer=64M"); + add_arg(&al, table); +
+ spawn(mycheck, &al, TRUE, NULL, NULL, NULL); + + free_args(&al); + } + else if (strindex(table, ".ism")) + { + // ** isamchk + + // mysqladmin file + snprintf(mycheck, PATH_MAX, "%s/bin/isamchk", basedir); + + // args + init_args(&al); + add_arg(&al, mycheck); + add_arg(&al, "--silent"); + add_arg(&al, "--force"); + add_arg(&al, "-O"); + add_arg(&al, "sort_buffer=64M"); + add_arg(&al, table); + + spawn(mycheck, &al, TRUE, NULL, NULL, NULL); + + free_args(&al); + } }
}
- }
+ } }
/******************************************************************************
mysql_start()
-
+ Start the mysql server.
******************************************************************************/
@@ -632,9 +639,9 @@ void mysql_start(int argc, char *argv[]) struct tm lt; char stamp[PATH_MAX]; char skip; -
+ // private options
- static char *private_options[] =
+ static char *private_options[]= {
"--autoclose", "--check-tables", @@ -643,56 +650,57 @@ void mysql_start(int argc, char *argv[]) "--mysqld=", NULL };
-
+ // args init_args(&al); add_arg(&al, "%s", mysqld); - + // parent args - for(i = 1; i < argc; i++) + for (i= 1; i < argc; i++) { - skip = FALSE;
-
+ skip= FALSE; + // skip private arguments
- for (j=0; private_options[j]; j++)
+ for (j= 0; private_options[j]; j++) {
- if(!strnicmp(argv[i], private_options[j], strlen(private_options[j])))
+ if (!strnicmp(argv[i], private_options[j], strlen(private_options[j]))) {
- skip = TRUE;
- consoleprintf("The argument skipped is %s\n",argv[i]); - break;
+ skip= TRUE; + consoleprintf("The argument skipped is %s\n", argv[i]); + break; }
}
- + if (!skip) { add_arg(&al, "%s", argv[i]); - consoleprintf("The final argument is %s\n",argv[i]); + consoleprintf("The final argument is %s\n", argv[i]); } } // spawn
do { // check the database tables - if (checktables) check_tables(); - + if (checktables) + check_tables(); + // status time(&cal);
localtime_r(&cal, <);
strftime(stamp, PATH_MAX, "%d %b %Y %H:%M:%S", <);
log("mysql started : %s\n", stamp);
- + // spawn mysqld spawn(mysqld, &al, TRUE, NULL, NULL, err_log); } while (!stat(pid_file, &info)); - + // status time(&cal);
localtime_r(&cal, <);
strftime(stamp, PATH_MAX, "%d %b %Y %H:%M:%S", <);
log("mysql stopped : %s\n\n", stamp);
-
+ // free args free_args(&al); }
@@ -700,43 +708,45 @@ void mysql_start(int argc, char *argv[]) /******************************************************************************
main()
-
+ ******************************************************************************/
int main(int argc, char **argv)
{
- char temp[PATH_MAX];
-
+ char temp[PATH_MAX]; + // get the options
- get_options(argc, argv);
+ get_options(argc, argv); // keep the screen up
- if (!autoclose) setscreenmode(SCR_NO_MODE);
-
+ if (!autoclose) + setscreenmode(SCR_NO_MODE); + // create log file
- log_fd = fopen(safe_log, "w+");
-
+ log_fd= fopen(safe_log, "w+"); + // header
log("MySQL Server %s, for %s (%s)\n\n", VERSION, SYSTEM_TYPE, MACHINE_TYPE);
-
- // status
- log("address : %s\n", address);
- log("port : %s\n", port);
+ + // status + log("address : %s\n", address); + log("port : %s\n", port); log("daemon : %s\n", mysqld);
- log("base directory : %s\n", basedir);
- log("data directory : %s\n", datadir);
- log("pid file : %s\n", pid_file);
- log("error file : %s\n", err_log);
- log("log file : %s\n", safe_log);
+ log("base directory : %s\n", basedir); + log("data directory : %s\n", datadir); + log("pid file : %s\n", pid_file); + log("error file : %s\n", err_log); + log("log file : %s\n", safe_log); log("\n");
-
+ // check setup
check_setup();
-
+ // start the MySQL server
- mysql_start(argc, argv);
-
- // close log file
- if (log_fd) fclose(log_fd);
-
+ mysql_start(argc, argv); + + // close log file + if (log_fd) + fclose(log_fd); + return 0;
}
diff --git a/scripts/mysql_create_system_tables.sh b/scripts/mysql_create_system_tables.sh index 5129c028238..85835498bc1 100644 --- a/scripts/mysql_create_system_tables.sh +++ b/scripts/mysql_create_system_tables.sh @@ -39,6 +39,8 @@ c_hc="" c_hr="" c_hk="" i_ht="" +c_tzn="" c_tz="" c_tzt="" c_tztt="" c_tzls="" +i_tzn="" i_tz="" i_tzt="" i_tztt="" i_tzls="" # Check for old tables if test ! -f $mdata/db.frm @@ -285,6 +287,332 @@ then c_hr="$c_hr comment='keyword-topic relation';" fi +if test ! -f $mdata/time_zone_name.frm +then + if test "$1" = "verbose" ; then + echo "Preparing time_zone_name table" 1>&2; + fi + + c_tzn="$c_tzn CREATE TABLE time_zone_name (" + c_tzn="$c_tzn Name char(64) NOT NULL," + c_tzn="$c_tzn Time_zone_id int unsigned NOT NULL," + c_tzn="$c_tzn PRIMARY KEY Name (Name)" + c_tzn="$c_tzn ) DEFAULT CHARACTER SET latin1" + c_tzn="$c_tzn comment='Time zone names';" + + if test "$1" = "test" + then + i_tzn="$i_tzn INSERT INTO time_zone_name (Name, Time_Zone_id) VALUES" + i_tzn="$i_tzn ('MET', 1), ('UTC', 2), ('Universal', 2), " + i_tzn="$i_tzn ('Europe/Moscow',3), ('leap/Europe/Moscow',4);" + fi +fi + +if test ! -f $mdata/time_zone.frm +then + if test "$1" = "verbose" ; then + echo "Preparing time_zone table" 1>&2; + fi + + c_tz="$c_tz CREATE TABLE time_zone (" + c_tz="$c_tz Time_zone_id int unsigned NOT NULL auto_increment," + c_tz="$c_tz Use_leap_seconds enum('Y','N') DEFAULT 'N' NOT NULL," + c_tz="$c_tz PRIMARY KEY TzId (Time_zone_id)" + c_tz="$c_tz ) DEFAULT CHARACTER SET latin1" + c_tz="$c_tz comment='Time zones';" + + if test "$1" = "test" + then + i_tz="$i_tz INSERT INTO time_zone (Time_zone_id, Use_leap_seconds)" + i_tz="$i_tz VALUES (1,'N'), (2,'N'), (3,'N'), (4,'Y');" + fi +fi + +if test ! -f $mdata/time_zone_transition.frm +then + if test "$1" = "verbose" ; then + echo "Preparing time_zone_transition table" 1>&2; + fi + + c_tzt="$c_tzt CREATE TABLE time_zone_transition (" + c_tzt="$c_tzt Time_zone_id int unsigned NOT NULL," + c_tzt="$c_tzt Transition_time bigint signed NOT NULL," + c_tzt="$c_tzt Transition_type_id int unsigned NOT NULL," + c_tzt="$c_tzt PRIMARY KEY TzIdTranTime (Time_zone_id, Transition_time)" + c_tzt="$c_tzt ) DEFAULT CHARACTER SET latin1" + c_tzt="$c_tzt comment='Time zone transitions';" + + if test "$1" = "test" + then + i_tzt="$i_tzt INSERT INTO time_zone_transition" + i_tzt="$i_tzt (Time_zone_id, Transition_time, Transition_type_id)" + i_tzt="$i_tzt VALUES" + i_tzt="$i_tzt (1, -1693706400, 0) ,(1, -1680483600, 1)" + i_tzt="$i_tzt ,(1, -1663455600, 2) ,(1, -1650150000, 3)" + i_tzt="$i_tzt ,(1, -1632006000, 2) ,(1, -1618700400, 3)" + i_tzt="$i_tzt ,(1, -938905200, 2) ,(1, -857257200, 3)" + i_tzt="$i_tzt ,(1, -844556400, 2) ,(1, -828226800, 3)" + i_tzt="$i_tzt ,(1, -812502000, 2) ,(1, -796777200, 3)" + i_tzt="$i_tzt ,(1, 228877200, 2) ,(1, 243997200, 3)" + i_tzt="$i_tzt ,(1, 260326800, 2) ,(1, 276051600, 3)" + i_tzt="$i_tzt ,(1, 291776400, 2) ,(1, 307501200, 3)" + i_tzt="$i_tzt ,(1, 323830800, 2) ,(1, 338950800, 3)" + i_tzt="$i_tzt ,(1, 354675600, 2) ,(1, 370400400, 3)" + i_tzt="$i_tzt ,(1, 386125200, 2) ,(1, 401850000, 3)" + i_tzt="$i_tzt ,(1, 417574800, 2) ,(1, 433299600, 3)" + i_tzt="$i_tzt ,(1, 449024400, 2) ,(1, 465354000, 3)" + i_tzt="$i_tzt ,(1, 481078800, 2) ,(1, 496803600, 3)" + i_tzt="$i_tzt ,(1, 512528400, 2) ,(1, 528253200, 3)" + i_tzt="$i_tzt ,(1, 543978000, 2) ,(1, 559702800, 3)" + i_tzt="$i_tzt ,(1, 575427600, 2) ,(1, 591152400, 3)" + i_tzt="$i_tzt ,(1, 606877200, 2) ,(1, 622602000, 3)" + i_tzt="$i_tzt ,(1, 638326800, 2) ,(1, 654656400, 3)" + i_tzt="$i_tzt ,(1, 670381200, 2) ,(1, 686106000, 3)" + i_tzt="$i_tzt ,(1, 701830800, 2) ,(1, 717555600, 3)" + i_tzt="$i_tzt ,(1, 733280400, 2) ,(1, 749005200, 3)" + i_tzt="$i_tzt ,(1, 764730000, 2) ,(1, 780454800, 3)" + i_tzt="$i_tzt ,(1, 796179600, 2) ,(1, 811904400, 3)" + i_tzt="$i_tzt ,(1, 828234000, 2) ,(1, 846378000, 3)" + i_tzt="$i_tzt ,(1, 859683600, 2) ,(1, 877827600, 3)" + i_tzt="$i_tzt ,(1, 891133200, 2) ,(1, 909277200, 3)" + i_tzt="$i_tzt ,(1, 922582800, 2) ,(1, 941331600, 3)" + i_tzt="$i_tzt ,(1, 954032400, 2) ,(1, 972781200, 3)" + i_tzt="$i_tzt ,(1, 985482000, 2) ,(1, 1004230800, 3)" + i_tzt="$i_tzt ,(1, 1017536400, 2) ,(1, 1035680400, 3)" + i_tzt="$i_tzt ,(1, 1048986000, 2) ,(1, 1067130000, 3)" + i_tzt="$i_tzt ,(1, 1080435600, 2) ,(1, 1099184400, 3)" + i_tzt="$i_tzt ,(1, 1111885200, 2) ,(1, 1130634000, 3)" + i_tzt="$i_tzt ,(1, 1143334800, 2) ,(1, 1162083600, 3)" + i_tzt="$i_tzt ,(1, 1174784400, 2) ,(1, 1193533200, 3)" + i_tzt="$i_tzt ,(1, 1206838800, 2) ,(1, 1224982800, 3)" + i_tzt="$i_tzt ,(1, 1238288400, 2) ,(1, 1256432400, 3)" + i_tzt="$i_tzt ,(1, 1269738000, 2) ,(1, 1288486800, 3)" + i_tzt="$i_tzt ,(1, 1301187600, 2) ,(1, 1319936400, 3)" + i_tzt="$i_tzt ,(1, 1332637200, 2) ,(1, 1351386000, 3)" + i_tzt="$i_tzt ,(1, 1364691600, 2) ,(1, 1382835600, 3)" + i_tzt="$i_tzt ,(1, 1396141200, 2) ,(1, 1414285200, 3)" + i_tzt="$i_tzt ,(1, 1427590800, 2) ,(1, 1445734800, 3)" + i_tzt="$i_tzt ,(1, 1459040400, 2) ,(1, 1477789200, 3)" + i_tzt="$i_tzt ,(1, 1490490000, 2) ,(1, 1509238800, 3)" + i_tzt="$i_tzt ,(1, 1521939600, 2) ,(1, 1540688400, 3)" + i_tzt="$i_tzt ,(1, 1553994000, 2) ,(1, 1572138000, 3)" + i_tzt="$i_tzt ,(1, 1585443600, 2) ,(1, 1603587600, 3)" + i_tzt="$i_tzt ,(1, 1616893200, 2) ,(1, 1635642000, 3)" + i_tzt="$i_tzt ,(1, 1648342800, 2) ,(1, 1667091600, 3)" + i_tzt="$i_tzt ,(1, 1679792400, 2) ,(1, 1698541200, 3)" + i_tzt="$i_tzt ,(1, 1711846800, 2) ,(1, 1729990800, 3)" + i_tzt="$i_tzt ,(1, 1743296400, 2) ,(1, 1761440400, 3)" + i_tzt="$i_tzt ,(1, 1774746000, 2) ,(1, 1792890000, 3)" + i_tzt="$i_tzt ,(1, 1806195600, 2) ,(1, 1824944400, 3)" + i_tzt="$i_tzt ,(1, 1837645200, 2) ,(1, 1856394000, 3)" + i_tzt="$i_tzt ,(1, 1869094800, 2) ,(1, 1887843600, 3)" + i_tzt="$i_tzt ,(1, 1901149200, 2) ,(1, 1919293200, 3)" + i_tzt="$i_tzt ,(1, 1932598800, 2) ,(1, 1950742800, 3)" + i_tzt="$i_tzt ,(1, 1964048400, 2) ,(1, 1982797200, 3)" + i_tzt="$i_tzt ,(1, 1995498000, 2) ,(1, 2014246800, 3)" + i_tzt="$i_tzt ,(1, 2026947600, 2) ,(1, 2045696400, 3)" + i_tzt="$i_tzt ,(1, 2058397200, 2) ,(1, 2077146000, 3)" + i_tzt="$i_tzt ,(1, 2090451600, 2) ,(1, 2108595600, 3)" + i_tzt="$i_tzt ,(1, 2121901200, 2) ,(1, 2140045200, 3)" + i_tzt="$i_tzt ,(3, -1688265000, 2) ,(3, -1656819048, 1)" + i_tzt="$i_tzt ,(3, -1641353448, 2) ,(3, -1627965048, 3)" + i_tzt="$i_tzt ,(3, -1618716648, 1) ,(3, -1596429048, 3)" + i_tzt="$i_tzt ,(3, -1593829848, 5) ,(3, -1589860800, 4)" + i_tzt="$i_tzt ,(3, -1542427200, 5) ,(3, -1539493200, 6)" + i_tzt="$i_tzt ,(3, -1525323600, 5) ,(3, -1522728000, 4)" + i_tzt="$i_tzt ,(3, -1491188400, 7) ,(3, -1247536800, 4)" + i_tzt="$i_tzt ,(3, 354920400, 5) ,(3, 370728000, 4)" + i_tzt="$i_tzt ,(3, 386456400, 5) ,(3, 402264000, 4)" + i_tzt="$i_tzt ,(3, 417992400, 5) ,(3, 433800000, 4)" + i_tzt="$i_tzt ,(3, 449614800, 5) ,(3, 465346800, 8)" + i_tzt="$i_tzt ,(3, 481071600, 9) ,(3, 496796400, 8)" + i_tzt="$i_tzt ,(3, 512521200, 9) ,(3, 528246000, 8)" + i_tzt="$i_tzt ,(3, 543970800, 9) ,(3, 559695600, 8)" + i_tzt="$i_tzt ,(3, 575420400, 9) ,(3, 591145200, 8)" + i_tzt="$i_tzt ,(3, 606870000, 9) ,(3, 622594800, 8)" + i_tzt="$i_tzt ,(3, 638319600, 9) ,(3, 654649200, 8)" + i_tzt="$i_tzt ,(3, 670374000, 10) ,(3, 686102400, 11)" + i_tzt="$i_tzt ,(3, 695779200, 8) ,(3, 701812800, 5)" + i_tzt="$i_tzt ,(3, 717534000, 4) ,(3, 733273200, 9)" + i_tzt="$i_tzt ,(3, 748998000, 8) ,(3, 764722800, 9)" + i_tzt="$i_tzt ,(3, 780447600, 8) ,(3, 796172400, 9)" + i_tzt="$i_tzt ,(3, 811897200, 8) ,(3, 828226800, 9)" + i_tzt="$i_tzt ,(3, 846370800, 8) ,(3, 859676400, 9)" + i_tzt="$i_tzt ,(3, 877820400, 8) ,(3, 891126000, 9)" + i_tzt="$i_tzt ,(3, 909270000, 8) ,(3, 922575600, 9)" + i_tzt="$i_tzt ,(3, 941324400, 8) ,(3, 954025200, 9)" + i_tzt="$i_tzt ,(3, 972774000, 8) ,(3, 985474800, 9)" + i_tzt="$i_tzt ,(3, 1004223600, 8) ,(3, 1017529200, 9)" + i_tzt="$i_tzt ,(3, 1035673200, 8) ,(3, 1048978800, 9)" + i_tzt="$i_tzt ,(3, 1067122800, 8) ,(3, 1080428400, 9)" + i_tzt="$i_tzt ,(3, 1099177200, 8) ,(3, 1111878000, 9)" + i_tzt="$i_tzt ,(3, 1130626800, 8) ,(3, 1143327600, 9)" + i_tzt="$i_tzt ,(3, 1162076400, 8) ,(3, 1174777200, 9)" + i_tzt="$i_tzt ,(3, 1193526000, 8) ,(3, 1206831600, 9)" + i_tzt="$i_tzt ,(3, 1224975600, 8) ,(3, 1238281200, 9)" + i_tzt="$i_tzt ,(3, 1256425200, 8) ,(3, 1269730800, 9)" + i_tzt="$i_tzt ,(3, 1288479600, 8) ,(3, 1301180400, 9)" + i_tzt="$i_tzt ,(3, 1319929200, 8) ,(3, 1332630000, 9)" + i_tzt="$i_tzt ,(3, 1351378800, 8) ,(3, 1364684400, 9)" + i_tzt="$i_tzt ,(3, 1382828400, 8) ,(3, 1396134000, 9)" + i_tzt="$i_tzt ,(3, 1414278000, 8) ,(3, 1427583600, 9)" + i_tzt="$i_tzt ,(3, 1445727600, 8) ,(3, 1459033200, 9)" + i_tzt="$i_tzt ,(3, 1477782000, 8) ,(3, 1490482800, 9)" + i_tzt="$i_tzt ,(3, 1509231600, 8) ,(3, 1521932400, 9)" + i_tzt="$i_tzt ,(3, 1540681200, 8) ,(3, 1553986800, 9)" + i_tzt="$i_tzt ,(3, 1572130800, 8) ,(3, 1585436400, 9)" + i_tzt="$i_tzt ,(3, 1603580400, 8) ,(3, 1616886000, 9)" + i_tzt="$i_tzt ,(3, 1635634800, 8) ,(3, 1648335600, 9)" + i_tzt="$i_tzt ,(3, 1667084400, 8) ,(3, 1679785200, 9)" + i_tzt="$i_tzt ,(3, 1698534000, 8) ,(3, 1711839600, 9)" + i_tzt="$i_tzt ,(3, 1729983600, 8) ,(3, 1743289200, 9)" + i_tzt="$i_tzt ,(3, 1761433200, 8) ,(3, 1774738800, 9)" + i_tzt="$i_tzt ,(3, 1792882800, 8) ,(3, 1806188400, 9)" + i_tzt="$i_tzt ,(3, 1824937200, 8) ,(3, 1837638000, 9)" + i_tzt="$i_tzt ,(3, 1856386800, 8) ,(3, 1869087600, 9)" + i_tzt="$i_tzt ,(3, 1887836400, 8) ,(3, 1901142000, 9)" + i_tzt="$i_tzt ,(3, 1919286000, 8) ,(3, 1932591600, 9)" + i_tzt="$i_tzt ,(3, 1950735600, 8) ,(3, 1964041200, 9)" + i_tzt="$i_tzt ,(3, 1982790000, 8) ,(3, 1995490800, 9)" + i_tzt="$i_tzt ,(3, 2014239600, 8) ,(3, 2026940400, 9)" + i_tzt="$i_tzt ,(3, 2045689200, 8) ,(3, 2058390000, 9)" + i_tzt="$i_tzt ,(3, 2077138800, 8) ,(3, 2090444400, 9)" + i_tzt="$i_tzt ,(3, 2108588400, 8) ,(3, 2121894000, 9)" + i_tzt="$i_tzt ,(3, 2140038000, 8)" + i_tzt="$i_tzt ,(4, -1688265000, 2) ,(4, -1656819048, 1)" + i_tzt="$i_tzt ,(4, -1641353448, 2) ,(4, -1627965048, 3)" + i_tzt="$i_tzt ,(4, -1618716648, 1) ,(4, -1596429048, 3)" + i_tzt="$i_tzt ,(4, -1593829848, 5) ,(4, -1589860800, 4)" + i_tzt="$i_tzt ,(4, -1542427200, 5) ,(4, -1539493200, 6)" + i_tzt="$i_tzt ,(4, -1525323600, 5) ,(4, -1522728000, 4)" + i_tzt="$i_tzt ,(4, -1491188400, 7) ,(4, -1247536800, 4)" + i_tzt="$i_tzt ,(4, 354920409, 5) ,(4, 370728010, 4)" + i_tzt="$i_tzt ,(4, 386456410, 5) ,(4, 402264011, 4)" + i_tzt="$i_tzt ,(4, 417992411, 5) ,(4, 433800012, 4)" + i_tzt="$i_tzt ,(4, 449614812, 5) ,(4, 465346812, 8)" + i_tzt="$i_tzt ,(4, 481071612, 9) ,(4, 496796413, 8)" + i_tzt="$i_tzt ,(4, 512521213, 9) ,(4, 528246013, 8)" + i_tzt="$i_tzt ,(4, 543970813, 9) ,(4, 559695613, 8)" + i_tzt="$i_tzt ,(4, 575420414, 9) ,(4, 591145214, 8)" + i_tzt="$i_tzt ,(4, 606870014, 9) ,(4, 622594814, 8)" + i_tzt="$i_tzt ,(4, 638319615, 9) ,(4, 654649215, 8)" + i_tzt="$i_tzt ,(4, 670374016, 10) ,(4, 686102416, 11)" + i_tzt="$i_tzt ,(4, 695779216, 8) ,(4, 701812816, 5)" + i_tzt="$i_tzt ,(4, 717534017, 4) ,(4, 733273217, 9)" + i_tzt="$i_tzt ,(4, 748998018, 8) ,(4, 764722818, 9)" + i_tzt="$i_tzt ,(4, 780447619, 8) ,(4, 796172419, 9)" + i_tzt="$i_tzt ,(4, 811897219, 8) ,(4, 828226820, 9)" + i_tzt="$i_tzt ,(4, 846370820, 8) ,(4, 859676420, 9)" + i_tzt="$i_tzt ,(4, 877820421, 8) ,(4, 891126021, 9)" + i_tzt="$i_tzt ,(4, 909270021, 8) ,(4, 922575622, 9)" + i_tzt="$i_tzt ,(4, 941324422, 8) ,(4, 954025222, 9)" + i_tzt="$i_tzt ,(4, 972774022, 8) ,(4, 985474822, 9)" + i_tzt="$i_tzt ,(4, 1004223622, 8) ,(4, 1017529222, 9)" + i_tzt="$i_tzt ,(4, 1035673222, 8) ,(4, 1048978822, 9)" + i_tzt="$i_tzt ,(4, 1067122822, 8) ,(4, 1080428422, 9)" + i_tzt="$i_tzt ,(4, 1099177222, 8) ,(4, 1111878022, 9)" + i_tzt="$i_tzt ,(4, 1130626822, 8) ,(4, 1143327622, 9)" + i_tzt="$i_tzt ,(4, 1162076422, 8) ,(4, 1174777222, 9)" + i_tzt="$i_tzt ,(4, 1193526022, 8) ,(4, 1206831622, 9)" + i_tzt="$i_tzt ,(4, 1224975622, 8) ,(4, 1238281222, 9)" + i_tzt="$i_tzt ,(4, 1256425222, 8) ,(4, 1269730822, 9)" + i_tzt="$i_tzt ,(4, 1288479622, 8) ,(4, 1301180422, 9)" + i_tzt="$i_tzt ,(4, 1319929222, 8) ,(4, 1332630022, 9)" + i_tzt="$i_tzt ,(4, 1351378822, 8) ,(4, 1364684422, 9)" + i_tzt="$i_tzt ,(4, 1382828422, 8) ,(4, 1396134022, 9)" + i_tzt="$i_tzt ,(4, 1414278022, 8) ,(4, 1427583622, 9)" + i_tzt="$i_tzt ,(4, 1445727622, 8) ,(4, 1459033222, 9)" + i_tzt="$i_tzt ,(4, 1477782022, 8) ,(4, 1490482822, 9)" + i_tzt="$i_tzt ,(4, 1509231622, 8) ,(4, 1521932422, 9)" + i_tzt="$i_tzt ,(4, 1540681222, 8) ,(4, 1553986822, 9)" + i_tzt="$i_tzt ,(4, 1572130822, 8) ,(4, 1585436422, 9)" + i_tzt="$i_tzt ,(4, 1603580422, 8) ,(4, 1616886022, 9)" + i_tzt="$i_tzt ,(4, 1635634822, 8) ,(4, 1648335622, 9)" + i_tzt="$i_tzt ,(4, 1667084422, 8) ,(4, 1679785222, 9)" + i_tzt="$i_tzt ,(4, 1698534022, 8) ,(4, 1711839622, 9)" + i_tzt="$i_tzt ,(4, 1729983622, 8) ,(4, 1743289222, 9)" + i_tzt="$i_tzt ,(4, 1761433222, 8) ,(4, 1774738822, 9)" + i_tzt="$i_tzt ,(4, 1792882822, 8) ,(4, 1806188422, 9)" + i_tzt="$i_tzt ,(4, 1824937222, 8) ,(4, 1837638022, 9)" + i_tzt="$i_tzt ,(4, 1856386822, 8) ,(4, 1869087622, 9)" + i_tzt="$i_tzt ,(4, 1887836422, 8) ,(4, 1901142022, 9)" + i_tzt="$i_tzt ,(4, 1919286022, 8) ,(4, 1932591622, 9)" + i_tzt="$i_tzt ,(4, 1950735622, 8) ,(4, 1964041222, 9)" + i_tzt="$i_tzt ,(4, 1982790022, 8) ,(4, 1995490822, 9)" + i_tzt="$i_tzt ,(4, 2014239622, 8) ,(4, 2026940422, 9)" + i_tzt="$i_tzt ,(4, 2045689222, 8) ,(4, 2058390022, 9)" + i_tzt="$i_tzt ,(4, 2077138822, 8) ,(4, 2090444422, 9)" + i_tzt="$i_tzt ,(4, 2108588422, 8) ,(4, 2121894022, 9)" + i_tzt="$i_tzt ,(4, 2140038022, 8);" + fi +fi + +if test ! -f $mdata/time_zone_transition_type.frm +then + if test "$1" = "verbose" ; then + echo "Preparing time_zone_transition_type table" 1>&2; + fi + + c_tztt="$c_tztt CREATE TABLE time_zone_transition_type (" + c_tztt="$c_tztt Time_zone_id int unsigned NOT NULL," + c_tztt="$c_tztt Transition_type_id int unsigned NOT NULL," + c_tztt="$c_tztt Offset int signed DEFAULT 0 NOT NULL," + c_tztt="$c_tztt Is_DST tinyint unsigned DEFAULT 0 NOT NULL," + c_tztt="$c_tztt Abbreviation char(8) DEFAULT '' NOT NULL," + c_tztt="$c_tztt PRIMARY KEY TzIdTrTId (Time_zone_id, Transition_type_id)" + c_tztt="$c_tztt ) DEFAULT CHARACTER SET latin1" + c_tztt="$c_tztt comment='Time zone transition types';" + + if test "$1" = "test" + then + i_tztt="$i_tztt INSERT INTO time_zone_transition_type (Time_zone_id," + i_tztt="$i_tztt Transition_type_id, Offset, Is_DST, Abbreviation) VALUES" + i_tztt="$i_tztt (1, 0, 7200, 1, 'MEST') ,(1, 1, 3600, 0, 'MET')" + i_tztt="$i_tztt ,(1, 2, 7200, 1, 'MEST') ,(1, 3, 3600, 0, 'MET')" + i_tztt="$i_tztt ,(2, 0, 0, 0, 'UTC')" + i_tztt="$i_tztt ,(3, 0, 9000, 0, 'MMT') ,(3, 1, 12648, 1, 'MST')" + i_tztt="$i_tztt ,(3, 2, 9048, 0, 'MMT') ,(3, 3, 16248, 1, 'MDST')" + i_tztt="$i_tztt ,(3, 4, 10800, 0, 'MSK') ,(3, 5, 14400, 1, 'MSD')" + i_tztt="$i_tztt ,(3, 6, 18000, 1, 'MSD') ,(3, 7, 7200, 0, 'EET')" + i_tztt="$i_tztt ,(3, 8, 10800, 0, 'MSK') ,(3, 9, 14400, 1, 'MSD')" + i_tztt="$i_tztt ,(3, 10, 10800, 1, 'EEST') ,(3, 11, 7200, 0, 'EET')" + i_tztt="$i_tztt ,(4, 0, 9000, 0, 'MMT') ,(4, 1, 12648, 1, 'MST')" + i_tztt="$i_tztt ,(4, 2, 9048, 0, 'MMT') ,(4, 3, 16248, 1, 'MDST')" + i_tztt="$i_tztt ,(4, 4, 10800, 0, 'MSK') ,(4, 5, 14400, 1, 'MSD')" + i_tztt="$i_tztt ,(4, 6, 18000, 1, 'MSD') ,(4, 7, 7200, 0, 'EET')" + i_tztt="$i_tztt ,(4, 8, 10800, 0, 'MSK') ,(4, 9, 14400, 1, 'MSD')" + i_tztt="$i_tztt ,(4, 10, 10800, 1, 'EEST') ,(4, 11, 7200, 0, 'EET');" + fi +fi + +if test ! -f $mdata/time_zone_leap_second.frm +then + if test "$1" = "verbose" ; then + echo "Preparing time_zone_leap_second table" 1>&2; + fi + + c_tzls="$c_tzls CREATE TABLE time_zone_leap_second (" + c_tzls="$c_tzls Transition_time bigint signed NOT NULL," + c_tzls="$c_tzls Correction int signed NOT NULL," + c_tzls="$c_tzls PRIMARY KEY TranTime (Transition_time)" + c_tzls="$c_tzls ) DEFAULT CHARACTER SET latin1" + c_tzls="$c_tzls comment='Leap seconds information for time zones';" + + if test "$1" = "test" + then + i_tzls="$i_tzls INSERT INTO time_zone_leap_second " + i_tzls="$i_tzls (Transition_time, Correction) VALUES " + i_tzls="$i_tzls (78796800, 1) ,(94694401, 2) ,(126230402, 3)" + i_tzls="$i_tzls ,(157766403, 4) ,(189302404, 5) ,(220924805, 6)" + i_tzls="$i_tzls ,(252460806, 7) ,(283996807, 8) ,(315532808, 9)" + i_tzls="$i_tzls ,(362793609, 10) ,(394329610, 11) ,(425865611, 12)" + i_tzls="$i_tzls ,(489024012, 13) ,(567993613, 14) ,(631152014, 15)" + i_tzls="$i_tzls ,(662688015, 16) ,(709948816, 17) ,(741484817, 18)" + i_tzls="$i_tzls ,(773020818, 19) ,(820454419, 20) ,(867715220, 21)" + i_tzls="$i_tzls ,(915148821, 22);" + fi +fi + cat << END_OF_DATA use mysql; $c_d @@ -306,5 +634,16 @@ $c_ht $c_hc $c_hr $c_hk + +$c_tzn +$i_tzn +$c_tz +$i_tz +$c_tzt +$i_tzt +$c_tztt +$i_tztt +$c_tzls +$i_tzls END_OF_DATA diff --git a/scripts/mysql_fix_privilege_tables.sql b/scripts/mysql_fix_privilege_tables.sql index dabc653bcbb..41d991250c2 100644 --- a/scripts/mysql_fix_privilege_tables.sql +++ b/scripts/mysql_fix_privilege_tables.sql @@ -179,3 +179,42 @@ name varchar(64) not null, primary key (help_keyword_id), unique index (name) ) comment='help keywords'; + +# +# Create missing time zone related tables +# + +CREATE TABLE IF NOT EXISTS time_zone_name ( +Name char(64) NOT NULL, +Time_zone_id int unsigned NOT NULL, +PRIMARY KEY Name (Name) +) DEFAULT CHARACTER SET latin1 comment='Time zone names'; + +CREATE TABLE IF NOT EXISTS time_zone ( +Time_zone_id int unsigned NOT NULL auto_increment, +Use_leap_seconds enum('Y','N') DEFAULT 'N' NOT NULL, +PRIMARY KEY TzId (Time_zone_id) +) DEFAULT CHARACTER SET latin1 comment='Time zones'; + +CREATE TABLE IF NOT EXISTS time_zone_transition ( +Time_zone_id int unsigned NOT NULL, +Transition_time bigint signed NOT NULL, +Transition_type_id int unsigned NOT NULL, +PRIMARY KEY TzIdTranTime (Time_zone_id, Transition_time) +) DEFAULT CHARACTER SET latin1 comment='Time zone transitions'; + +CREATE TABLE IF NOT EXISTS time_zone_transition_type ( +Time_zone_id int unsigned NOT NULL, +Transition_type_id int unsigned NOT NULL, +Offset int signed DEFAULT 0 NOT NULL, +Is_DST tinyint unsigned DEFAULT 0 NOT NULL, +Abbreviation char(8) DEFAULT '' NOT NULL, +PRIMARY KEY TzIdTrTId (Time_zone_id, Transition_type_id) +) DEFAULT CHARACTER SET latin1 comment='Time zone transition types'; + +CREATE TABLE IF NOT EXISTS time_zone_leap_second ( +Transition_time bigint signed NOT NULL, +Correction int signed NOT NULL, +PRIMARY KEY TranTime (Transition_time) +) DEFAULT CHARACTER SET latin1 comment='Leap seconds information for time zones'; + diff --git a/scripts/mysql_install_db.sh b/scripts/mysql_install_db.sh index 1fd67b1d0ee..558b0e2da0c 100644 --- a/scripts/mysql_install_db.sh +++ b/scripts/mysql_install_db.sh @@ -10,6 +10,7 @@ in_rpm=0 windows=0 defaults="" +user="" tmp_file=/tmp/mysql_install_db.$$ case "$1" in @@ -34,7 +35,11 @@ parse_arguments() { --force) force=1 ;; --basedir=*) basedir=`echo "$arg" | sed -e 's/^[^=]*=//'` ;; --ldata=*|--datadir=*) ldata=`echo "$arg" | sed -e 's/^[^=]*=//'` ;; - --user=*) user=`echo "$arg" | sed -e 's/^[^=]*=//'` ;; + --user=*) + # Note that the user will be passed to mysqld so that it runs + # as 'user' (crucial e.g. if log-bin=/some_other_path/ + # where a chown of datadir won't help) + user=`echo "$arg" | sed -e 's/^[^=]*=//'` ;; --skip-name-resolve) ip_only=1 ;; --verbose) verbose=1 ;; --rpm) in_rpm=1 ;; @@ -198,6 +203,10 @@ else create_option="real" fi +if test -n "$user"; then + args="$args --user=$user" +fi + if test "$in_rpm" -eq 0 -a "$windows" -eq 0 then echo "Installing all prepared tables" diff --git a/scripts/mysqld_safe.sh b/scripts/mysqld_safe.sh index 779438e75c3..7b77bf449cd 100644 --- a/scripts/mysqld_safe.sh +++ b/scripts/mysqld_safe.sh @@ -43,13 +43,7 @@ parse_arguments() { --basedir=*) MY_BASEDIR_VERSION=`echo "$arg" | sed -e "s;--basedir=;;"` ;; --datadir=*) DATADIR=`echo "$arg" | sed -e "s;--datadir=;;"` ;; --pid-file=*) pid_file=`echo "$arg" | sed -e "s;--pid-file=;;"` ;; - --user=*) - if test $SET_USER -eq 0 - then - user=`echo "$arg" | sed -e "s;--[^=]*=;;"` - fi - SET_USER=1 - ;; + --user=*) user=`echo "$arg" | sed -e "s;--[^=]*=;;"` ; SET_USER=1 ;; # these two might have been set in a [mysqld_safe] section of my.cnf # they are added to mysqld command line to override settings from my.cnf diff --git a/sql-common/client.c b/sql-common/client.c index 61ffd2b52e6..5c1a718c5bb 100644 --- a/sql-common/client.c +++ b/sql-common/client.c @@ -1732,7 +1732,8 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, sprintf(net->last_error, ER(CR_UNKNOWN_HOST), host, tmp_errno); goto error; } - memcpy(&sock_addr.sin_addr,hp->h_addr, (size_t) hp->h_length); + memcpy(&sock_addr.sin_addr, hp->h_addr, + min(sizeof(sock_addr.sin_addr), (size_t) hp->h_length)); my_gethostbyname_r_free(); } sock_addr.sin_port = (ushort) htons((ushort) port); diff --git a/sql/Makefile.am b/sql/Makefile.am index cdbf78bed0e..9fecf6a0d8f 100644 --- a/sql/Makefile.am +++ b/sql/Makefile.am @@ -27,6 +27,7 @@ WRAPLIBS= @WRAPLIBS@ SUBDIRS = share libexec_PROGRAMS = mysqld noinst_PROGRAMS = gen_lex_hash +bin_PROGRAMS = mysql_tzinfo_to_sql gen_lex_hash_LDFLAGS = @NOINST_LDFLAGS@ LDADD = @isam_libs@ \ ../myisam/libmyisam.a \ @@ -57,8 +58,8 @@ noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \ lex.h lex_symbol.h sql_acl.h sql_crypt.h \ log_event.h sql_repl.h slave.h \ stacktrace.h sql_sort.h sql_cache.h set_var.h \ - spatial.h gstream.h client_settings.h \ - examples/ha_example.h examples/ha_archive.h + spatial.h gstream.h client_settings.h tzfile.h \ + tztime.h examples/ha_example.h examples/ha_archive.h mysqld_SOURCES = sql_lex.cc sql_handler.cc \ item.cc item_sum.cc item_buff.cc item_func.cc \ item_cmpfunc.cc item_strfunc.cc item_timefunc.cc \ @@ -88,10 +89,14 @@ mysqld_SOURCES = sql_lex.cc sql_handler.cc \ client.c sql_client.cc mini_client_errors.c pack.c\ stacktrace.c repl_failsafe.h repl_failsafe.cc \ gstream.cc spatial.cc sql_help.cc protocol_cursor.cc \ - examples/ha_example.cc examples/ha_archive.cc + tztime.cc examples/ha_example.cc examples/ha_archive.cc gen_lex_hash_SOURCES = gen_lex_hash.cc gen_lex_hash_LDADD = $(LDADD) $(CXXLDFLAGS) +mysql_tzinfo_to_sql_SOURCES = mysql_tzinfo_to_sql_tztime.cc +mysql_tzinfo_to_sql_CXXFLAGS = -DTZINFO2SQL $(AM_CXXFLAGS) +mysql_tzinfo_to_sql_LDADD = $(LDADD) $(CXXLDFLAGS) + DEFS = -DMYSQL_SERVER \ -DDEFAULT_MYSQL_HOME="\"$(MYSQLBASEdir)\"" \ -DDATADIR="\"$(MYSQLDATAdir)\"" \ @@ -113,6 +118,10 @@ link_sources: gen_lex_hash.o: gen_lex_hash.cc lex.h $(CXXCOMPILE) -c $(INCLUDES) $< +mysql_tzinfo_to_sql_tztime.cc: tztime.cc + rm -f $(srcdir)/mysql_tzinfo_to_sql_tztime.cc + @LN_CP_F@ $(srcdir)/tztime.cc $(srcdir)/mysql_tzinfo_to_sql_tztime.cc + # Try to get better dependencies for the grammar. Othervise really bad # things like different grammars for different pars of MySQL can # happen if you are unlucky. diff --git a/sql/field.cc b/sql/field.cc index 0660e774396..f113b98cccd 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -398,7 +398,7 @@ bool Field::get_date(TIME *ltime,uint fuzzydate) char buff[40]; String tmp(buff,sizeof(buff),&my_charset_bin),*res; if (!(res=val_str(&tmp)) || - str_to_TIME(res->ptr(),res->length(),ltime,fuzzydate) <= + str_to_TIME_with_warn(res->ptr(), res->length(), ltime, fuzzydate) <= TIMESTAMP_DATETIME_ERROR) return 1; return 0; @@ -409,7 +409,7 @@ bool Field::get_time(TIME *ltime) char buff[40]; String tmp(buff,sizeof(buff),&my_charset_bin),*res; if (!(res=val_str(&tmp)) || - str_to_time(res->ptr(),res->length(),ltime)) + str_to_time_with_warn(res->ptr(), res->length(), ltime)) return 1; return 0; } @@ -462,7 +462,7 @@ void Field_decimal::overflow(bool negative) uint len=field_length; char *to=ptr, filler= '9'; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); if (negative) { if (!unsigned_flag) @@ -546,7 +546,7 @@ int Field_decimal::store(const char *from, uint len, CHARSET_INFO *cs) char *left_wall,*right_wall; char tmp_char; /* - To remember if current_thd->cuted_fields has already been incremented, + To remember if table->in_use->cuted_fields has already been incremented, to do that only once */ bool is_cuted_fields_incr=0; @@ -572,7 +572,7 @@ int Field_decimal::store(const char *from, uint len, CHARSET_INFO *cs) from++; if (from == end) { - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, 1); is_cuted_fields_incr=1; } else if (*from == '+' || *from == '-') // Found some sign ? @@ -642,13 +642,13 @@ int Field_decimal::store(const char *from, uint len, CHARSET_INFO *cs) it makes the code easer to read. */ - if (current_thd->count_cuted_fields) + if (table->in_use->count_cuted_fields) { // Skip end spaces for (;from != end && my_isspace(&my_charset_bin, *from); from++) ; if (from != end) // If still something left, warn { - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, 1); is_cuted_fields_incr=1; } } @@ -794,7 +794,7 @@ int Field_decimal::store(const char *from, uint len, CHARSET_INFO *cs) /* Write digits of the frac_% parts ; - Depending on current_thd->count_cutted_fields, we may also want + Depending on table->in_use->count_cutted_fields, we may also want to know if some non-zero tail of these parts will be truncated (for example, 0.002->0.00 will generate a warning, while 0.000->0.00 will not) @@ -812,7 +812,7 @@ int Field_decimal::store(const char *from, uint len, CHARSET_INFO *cs) { if (pos == right_wall) { - if (current_thd->count_cuted_fields && !is_cuted_fields_incr) + if (table->in_use->count_cuted_fields && !is_cuted_fields_incr) break; // Go on below to see if we lose non zero digits return 0; } @@ -826,7 +826,8 @@ int Field_decimal::store(const char *from, uint len, CHARSET_INFO *cs) if (tmp_char != '0') // Losing a non zero digit ? { if (!is_cuted_fields_incr) - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, + ER_WARN_DATA_TRUNCATED, 1); return 0; } continue; @@ -843,7 +844,7 @@ int Field_decimal::store(const char *from, uint len, CHARSET_INFO *cs) if (tmp_char != '0') // Losing a non zero digit ? { if (!is_cuted_fields_incr) - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, 1); return 0; } continue; @@ -1061,18 +1062,18 @@ int Field_tiny::store(const char *from,uint len,CHARSET_INFO *cs) if (tmp < 0) { tmp=0; /* purecov: inspected */ - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else if (tmp > 255) { tmp= 255; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } - else if (current_thd->count_cuted_fields && !test_if_int(from,len,end,cs)) + else if (table->in_use->count_cuted_fields && !test_if_int(from,len,end,cs)) { - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, 1); error= 1; } } @@ -1081,18 +1082,18 @@ int Field_tiny::store(const char *from,uint len,CHARSET_INFO *cs) if (tmp < -128) { tmp= -128; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else if (tmp >= 128) { tmp= 127; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } - else if (current_thd->count_cuted_fields && !test_if_int(from,len,end,cs)) + else if (table->in_use->count_cuted_fields && !test_if_int(from,len,end,cs)) { - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, 1); error= 1; } } @@ -1110,13 +1111,13 @@ int Field_tiny::store(double nr) if (nr < 0.0) { *ptr=0; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else if (nr > 255.0) { *ptr=(char) 255; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else @@ -1127,13 +1128,13 @@ int Field_tiny::store(double nr) if (nr < -128.0) { *ptr= (char) -128; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else if (nr > 127.0) { *ptr=127; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else @@ -1150,13 +1151,13 @@ int Field_tiny::store(longlong nr) if (nr < 0L) { *ptr=0; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else if (nr > 255L) { *ptr= (char) 255; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else @@ -1167,13 +1168,13 @@ int Field_tiny::store(longlong nr) if (nr < -128L) { *ptr= (char) -128; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else if (nr > 127L) { *ptr=127; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else @@ -1265,18 +1266,18 @@ int Field_short::store(const char *from,uint len,CHARSET_INFO *cs) if (tmp < 0) { tmp=0; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else if (tmp > (uint16) ~0) { tmp=(uint16) ~0; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } - else if (current_thd->count_cuted_fields && !test_if_int(from,len,end,cs)) + else if (table->in_use->count_cuted_fields && !test_if_int(from,len,end,cs)) { - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, 1); error= 1; } } @@ -1285,18 +1286,18 @@ int Field_short::store(const char *from,uint len,CHARSET_INFO *cs) if (tmp < INT_MIN16) { tmp= INT_MIN16; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else if (tmp > INT_MAX16) { tmp=INT_MAX16; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } - else if (current_thd->count_cuted_fields && !test_if_int(from,len,end,cs)) + else if (table->in_use->count_cuted_fields && !test_if_int(from,len,end,cs)) { - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, 1); error= 1; } } @@ -1322,13 +1323,13 @@ int Field_short::store(double nr) if (nr < 0) { res=0; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else if (nr > (double) (uint16) ~0) { res=(int16) (uint16) ~0; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else @@ -1339,13 +1340,13 @@ int Field_short::store(double nr) if (nr < (double) INT_MIN16) { res=INT_MIN16; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else if (nr > (double) INT_MAX16) { res=INT_MAX16; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else @@ -1371,13 +1372,13 @@ int Field_short::store(longlong nr) if (nr < 0L) { res=0; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else if (nr > (longlong) (uint16) ~0) { res=(int16) (uint16) ~0; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else @@ -1388,13 +1389,13 @@ int Field_short::store(longlong nr) if (nr < INT_MIN16) { res=INT_MIN16; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else if (nr > INT_MAX16) { res=INT_MAX16; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else @@ -1540,18 +1541,18 @@ int Field_medium::store(const char *from,uint len,CHARSET_INFO *cs) if (tmp < 0) { tmp=0; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else if (tmp >= (long) (1L << 24)) { tmp=(long) (1L << 24)-1L; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } - else if (current_thd->count_cuted_fields && !test_if_int(from,len,end,cs)) + else if (table->in_use->count_cuted_fields && !test_if_int(from,len,end,cs)) { - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, 1); error= 1; } } @@ -1560,18 +1561,18 @@ int Field_medium::store(const char *from,uint len,CHARSET_INFO *cs) if (tmp < INT_MIN24) { tmp= INT_MIN24; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else if (tmp > INT_MAX24) { tmp=INT_MAX24; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } - else if (current_thd->count_cuted_fields && !test_if_int(from,len,end,cs)) + else if (table->in_use->count_cuted_fields && !test_if_int(from,len,end,cs)) { - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, 1); error= 1; } } @@ -1590,14 +1591,14 @@ int Field_medium::store(double nr) if (nr < 0) { int3store(ptr,0); - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else if (nr >= (double) (long) (1L << 24)) { uint32 tmp=(uint32) (1L << 24)-1L; int3store(ptr,tmp); - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else @@ -1609,14 +1610,14 @@ int Field_medium::store(double nr) { long tmp=(long) INT_MIN24; int3store(ptr,tmp); - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else if (nr > (double) INT_MAX24) { long tmp=(long) INT_MAX24; int3store(ptr,tmp); - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else @@ -1633,14 +1634,14 @@ int Field_medium::store(longlong nr) if (nr < 0L) { int3store(ptr,0); - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else if (nr >= (longlong) (long) (1L << 24)) { long tmp=(long) (1L << 24)-1L;; int3store(ptr,tmp); - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else @@ -1652,14 +1653,14 @@ int Field_medium::store(longlong nr) { long tmp=(long) INT_MIN24; int3store(ptr,tmp); - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else if (nr > (longlong) INT_MAX24) { long tmp=(long) INT_MAX24; int3store(ptr,tmp); - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else @@ -1771,10 +1772,10 @@ int Field_long::store(const char *from,uint len,CHARSET_INFO *cs) else tmp=my_strntol(cs,from,len,10,&end,&error); if (error || - (from+len != end && current_thd->count_cuted_fields && + (from+len != end && table->in_use->count_cuted_fields && !test_if_int(from,len,end,cs))) { - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, 1); error= 1; } #ifdef WORDS_BIGENDIAN @@ -1799,13 +1800,13 @@ int Field_long::store(double nr) if (nr < 0) { res=0; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else if (nr > (double) (ulong) ~0L) { res=(int32) (uint32) ~0L; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else @@ -1816,13 +1817,13 @@ int Field_long::store(double nr) if (nr < (double) INT_MIN32) { res=(int32) INT_MIN32; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else if (nr > (double) INT_MAX32) { res=(int32) INT_MAX32; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else @@ -1844,18 +1845,26 @@ int Field_long::store(longlong nr) { int error= 0; int32 res; + + /* + This assert has nothing to do with this method per se, it was put here + only because it is one of the best places for catching places there its + condition is broken. + */ + DBUG_ASSERT(table->in_use == current_thd); + if (unsigned_flag) { if (nr < 0) { res=0; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else if (nr >= (LL(1) << 32)) { res=(int32) (uint32) ~0L; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else @@ -1866,13 +1875,13 @@ int Field_long::store(longlong nr) if (nr < (longlong) INT_MIN32) { res=(int32) INT_MIN32; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else if (nr > (longlong) INT_MAX32) { res=(int32) INT_MAX32; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else @@ -1905,6 +1914,8 @@ double Field_long::val_real(void) longlong Field_long::val_int(void) { int32 j; + /* See the comment in Field_long::store(long long) */ + DBUG_ASSERT(table->in_use == current_thd); #ifdef WORDS_BIGENDIAN if (table->db_low_byte_first) j=sint4korr(ptr); @@ -2029,10 +2040,10 @@ int Field_longlong::store(const char *from,uint len,CHARSET_INFO *cs) else tmp=my_strntoll(cs,from,len,10,&end,&error); if (error || - (from+len != end && current_thd->count_cuted_fields && + (from+len != end && table->in_use->count_cuted_fields && !test_if_int(from,len,end,cs))) { - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, 1); error= 1; } #ifdef WORDS_BIGENDIAN @@ -2057,13 +2068,13 @@ int Field_longlong::store(double nr) if (nr < 0) { res=0; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else if (nr >= (double) ~ (ulonglong) 0) { res= ~(longlong) 0; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else @@ -2074,13 +2085,13 @@ int Field_longlong::store(double nr) if (nr <= (double) LONGLONG_MIN) { res=(longlong) LONGLONG_MIN; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else if (nr >= (double) LONGLONG_MAX) { res=(longlong) LONGLONG_MAX; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else @@ -2251,10 +2262,10 @@ int Field_float::store(const char *from,uint len,CHARSET_INFO *cs) int error; char *end; double nr= my_strntod(cs,(char*) from,len,&end,&error); - if (error || ((uint) (end-from) != len && current_thd->count_cuted_fields)) + if (error || ((uint) (end-from) != len && table->in_use->count_cuted_fields)) { error= 1; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, 1); } Field_float::store(nr); return error; @@ -2270,13 +2281,13 @@ int Field_float::store(double nr) { j= 0; set_null(); - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else if (unsigned_flag && nr < 0) { j= 0; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else @@ -2300,13 +2311,13 @@ int Field_float::store(double nr) if (nr < -max_value) { j= (float)-max_value; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else if (nr > max_value) { j= (float)max_value; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else @@ -2331,7 +2342,7 @@ int Field_float::store(longlong nr) float j= (float) nr; if (unsigned_flag && j < 0) { - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); j=0; error= 1; } @@ -2553,10 +2564,10 @@ int Field_double::store(const char *from,uint len,CHARSET_INFO *cs) int error; char *end; double nr= my_strntod(cs,(char*) from, len, &end, &error); - if (error || ((uint) (end-from) != len && current_thd->count_cuted_fields)) + if (error || ((uint) (end-from) != len && table->in_use->count_cuted_fields)) { error= 1; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, 1); } Field_double::store(nr); return error; @@ -2571,13 +2582,13 @@ int Field_double::store(double nr) { nr= 0; set_null(); - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else if (unsigned_flag && nr < 0) { nr= 0; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else @@ -2597,13 +2608,13 @@ int Field_double::store(double nr) if (nr < -max_value) { nr= -max_value; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } else if (nr > max_value) { nr= max_value; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; } } @@ -2626,7 +2637,7 @@ int Field_double::store(longlong nr) int error= 0; if (unsigned_flag && j < 0) { - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); error= 1; j=0; } @@ -2900,7 +2911,39 @@ void Field_timestamp::set_timestamp_offsets() int Field_timestamp::store(const char *from,uint len,CHARSET_INFO *cs) { - long tmp=(long) str_to_timestamp(from,len); + TIME l_time; + my_time_t tmp= 0; + int error; + bool have_smth_to_conv; + bool in_dst_time_gap; + THD *thd= table->in_use; + + have_smth_to_conv= (str_to_TIME(from, len, &l_time, 0, &error) > + TIMESTAMP_DATETIME_ERROR); + + if (error) + set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, + from, len, TIMESTAMP_DATETIME, 1); + + if (have_smth_to_conv) + { + if (!(tmp= TIME_to_timestamp(thd, &l_time, &in_dst_time_gap))) + { + set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, + ER_WARN_DATA_OUT_OF_RANGE, + from, len, TIMESTAMP_DATETIME, !error); + + error= 1; + } + else if (in_dst_time_gap) + { + set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, + ER_WARN_INVALID_TIMESTAMP, + from, len, TIMESTAMP_DATETIME, !error); + error= 1; + } + } + #ifdef WORDS_BIGENDIAN if (table->db_low_byte_first) { @@ -2909,7 +2952,7 @@ int Field_timestamp::store(const char *from,uint len,CHARSET_INFO *cs) else #endif longstore(ptr,tmp); - return 0; + return error; } int Field_timestamp::store(double nr) @@ -2917,8 +2960,10 @@ int Field_timestamp::store(double nr) int error= 0; if (nr < 0 || nr > 99991231235959.0) { + set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, + ER_WARN_DATA_OUT_OF_RANGE, + nr, TIMESTAMP_DATETIME); nr= 0; // Avoid overflow on buff - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); error= 1; } error|= Field_timestamp::store((longlong) rint(nr)); @@ -2926,96 +2971,37 @@ int Field_timestamp::store(double nr) } -/* - Convert a datetime of formats YYMMDD, YYYYMMDD or YYMMDDHHMSS to - YYYYMMDDHHMMSS. The high date '99991231235959' is checked before this - function. -*/ - -static longlong fix_datetime(longlong nr, TIME *time_res, - const char *field_name, bool *error) -{ - long part1,part2; - - *error= 0; - if (nr == LL(0) || nr >= LL(10000101000000)) - goto ok; - if (nr < 101) - goto err; - if (nr <= (YY_PART_YEAR-1)*10000L+1231L) - { - nr= (nr+20000000L)*1000000L; // YYMMDD, year: 2000-2069 - goto ok; - } - if (nr < (YY_PART_YEAR)*10000L+101L) - goto err; - if (nr <= 991231L) - { - nr= (nr+19000000L)*1000000L; // YYMMDD, year: 1970-1999 - goto ok; - } - if (nr < 10000101L) - goto err; - if (nr <= 99991231L) - { - nr= nr*1000000L; - goto ok; - } - if (nr < 101000000L) - goto err; - if (nr <= (YY_PART_YEAR-1)*LL(10000000000)+LL(1231235959)) - { - nr= nr+LL(20000000000000); // YYMMDDHHMMSS, 2000-2069 - goto ok; - } - if (nr < YY_PART_YEAR*LL(10000000000)+ LL(101000000)) - goto err; - if (nr <= LL(991231235959)) - nr= nr+LL(19000000000000); // YYMMDDHHMMSS, 1970-1999 - - ok: - part1=(long) (nr/LL(1000000)); - part2=(long) (nr - (longlong) part1*LL(1000000)); - time_res->year= (int) (part1/10000L); part1%=10000L; - time_res->month= (int) part1 / 100; - time_res->day= (int) part1 % 100; - time_res->hour= (int) (part2/10000L); part2%=10000L; - time_res->minute=(int) part2 / 100; - time_res->second=(int) part2 % 100; - - if (time_res->year <= 9999 && time_res->month <= 12 && - time_res->day <= 31 && time_res->hour <= 23 && - time_res->minute <= 59 && time_res->second <= 59) - return nr; - - err: - THD *thd= current_thd; - if (thd->count_cuted_fields) - { - thd->cuted_fields++; - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, - ER_WARN_DATA_TRUNCATED, ER(ER_WARN_DATA_TRUNCATED), - field_name, thd->row_count); - } - *error= 1; - return LL(0); -} - - int Field_timestamp::store(longlong nr) { TIME l_time; - time_t timestamp= 0; - bool error; + my_time_t timestamp= 0; + int error; + bool in_dst_time_gap; + THD *thd= table->in_use; - if ((nr= fix_datetime(nr, &l_time, field_name, &error))) + if (number_to_TIME(nr, &l_time, 0, &error)) { - long not_used; - - if (!(timestamp= my_gmt_sec(&l_time, ¬_used))) - goto err; - } + if (!(timestamp= TIME_to_timestamp(thd, &l_time, &in_dst_time_gap))) + { + set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, + ER_WARN_DATA_OUT_OF_RANGE, + nr, TIMESTAMP_DATETIME, 1); + error= 1; + } + if (in_dst_time_gap) + { + set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, + ER_WARN_INVALID_TIMESTAMP, + nr, TIMESTAMP_DATETIME, !error); + error= 1; + } + } + else if (error) + set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, + ER_WARN_DATA_TRUNCATED, + nr, TIMESTAMP_DATETIME, 1); + #ifdef WORDS_BIGENDIAN if (table->db_low_byte_first) { @@ -3024,12 +3010,8 @@ int Field_timestamp::store(longlong nr) else #endif longstore(ptr,(uint32) timestamp); + return error; - -err: - longstore(ptr,(uint32) 0); - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED); - return 1; } @@ -3040,12 +3022,9 @@ double Field_timestamp::val_real(void) longlong Field_timestamp::val_int(void) { - int part_time; uint32 temp; - time_t time_arg; - struct tm *l_time; - longlong res; - struct tm tm_tmp; + TIME time_tmp; + THD *thd= table->in_use; #ifdef WORDS_BIGENDIAN if (table->db_low_byte_first) @@ -3056,32 +3035,21 @@ longlong Field_timestamp::val_int(void) if (temp == 0L) // No time return(0); /* purecov: inspected */ - time_arg=(time_t) temp; - localtime_r(&time_arg,&tm_tmp); - l_time=&tm_tmp; - - part_time= l_time->tm_year % 100; - res= ((longlong) (part_time+ ((part_time < YY_PART_YEAR) ? 2000 : 1900))* - LL(10000000000)); - part_time= l_time->tm_mon+1; - res+= (longlong) part_time * LL(100000000); - part_time=l_time->tm_mday; - res+= (longlong) ((long) part_time * 1000000L); - part_time=l_time->tm_hour; - res+= (longlong) (part_time * 10000L); - part_time=l_time->tm_min; - res+= (longlong) (part_time * 100); - part_time=l_time->tm_sec; - return res+part_time; + + thd->variables.time_zone->gmt_sec_to_TIME(&time_tmp, (my_time_t)temp); + thd->time_zone_used= 1; + + return time_tmp.year * LL(10000000000) + time_tmp.month * LL(100000000) + + time_tmp.day * 1000000L + time_tmp.hour * 10000L + + time_tmp.minute * 100 + time_tmp.second; } String *Field_timestamp::val_str(String *val_buffer, String *val_ptr) { uint32 temp, temp2; - time_t time_arg; - struct tm *l_time; - struct tm tm_tmp; + TIME time_tmp; + THD *thd= table->in_use; char *to; val_buffer->alloc(field_length+1); @@ -3101,11 +3069,11 @@ String *Field_timestamp::val_str(String *val_buffer, String *val_ptr) return val_ptr; } val_buffer->set_charset(&my_charset_bin); // Safety - time_arg=(time_t) temp; - localtime_r(&time_arg,&tm_tmp); - l_time=&tm_tmp; + + thd->variables.time_zone->gmt_sec_to_TIME(&time_tmp,(my_time_t)temp); + thd->time_zone_used= 1; - temp= l_time->tm_year % 100; + temp= time_tmp.year % 100; if (temp < YY_PART_YEAR) { *to++= '2'; @@ -3120,27 +3088,27 @@ String *Field_timestamp::val_str(String *val_buffer, String *val_ptr) *to++= (char) ('0'+(char) (temp2)); *to++= (char) ('0'+(char) (temp)); *to++= '-'; - temp=l_time->tm_mon+1; + temp=time_tmp.month; temp2=temp/10; temp=temp-temp2*10; *to++= (char) ('0'+(char) (temp2)); *to++= (char) ('0'+(char) (temp)); *to++= '-'; - temp=l_time->tm_mday; + temp=time_tmp.day; temp2=temp/10; temp=temp-temp2*10; *to++= (char) ('0'+(char) (temp2)); *to++= (char) ('0'+(char) (temp)); *to++= ' '; - temp=l_time->tm_hour; + temp=time_tmp.hour; temp2=temp/10; temp=temp-temp2*10; *to++= (char) ('0'+(char) (temp2)); *to++= (char) ('0'+(char) (temp)); *to++= ':'; - temp=l_time->tm_min; + temp=time_tmp.minute; temp2=temp/10; temp=temp-temp2*10; *to++= (char) ('0'+(char) (temp2)); *to++= (char) ('0'+(char) (temp)); *to++= ':'; - temp=l_time->tm_sec; + temp=time_tmp.second; temp2=temp/10; temp=temp-temp2*10; *to++= (char) ('0'+(char) (temp2)); *to++= (char) ('0'+(char) (temp)); @@ -3152,6 +3120,7 @@ String *Field_timestamp::val_str(String *val_buffer, String *val_ptr) bool Field_timestamp::get_date(TIME *ltime, uint fuzzydate) { long temp; + THD *thd= table->in_use; #ifdef WORDS_BIGENDIAN if (table->db_low_byte_first) temp=uint4korr(ptr); @@ -3166,19 +3135,8 @@ bool Field_timestamp::get_date(TIME *ltime, uint fuzzydate) } else { - struct tm tm_tmp; - time_t time_arg= (time_t) temp; - localtime_r(&time_arg,&tm_tmp); - struct tm *start= &tm_tmp; - ltime->year= start->tm_year+1900; - ltime->month= start->tm_mon+1; - ltime->day= start->tm_mday; - ltime->hour= start->tm_hour; - ltime->minute= start->tm_min; - ltime->second= start->tm_sec; - ltime->second_part= 0; - ltime->neg= 0; - ltime->time_type=TIMESTAMP_DATETIME; + thd->variables.time_zone->gmt_sec_to_TIME(ltime, (my_time_t)temp); + thd->time_zone_used= 1; } return 0; } @@ -3245,7 +3203,7 @@ void Field_timestamp::sql_type(String &res) const void Field_timestamp::set_time() { - long tmp= (long) current_thd->query_start(); + long tmp= (long) table->in_use->query_start(); #ifdef WORDS_BIGENDIAN if (table->db_low_byte_first) { @@ -3267,25 +3225,35 @@ int Field_time::store(const char *from,uint len,CHARSET_INFO *cs) { TIME ltime; long tmp; - int error= 0; - if (str_to_time(from,len,<ime)) + int error; + + if (str_to_time(from, len, <ime, &error)) { tmp=0L; error= 1; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED); + set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, + from, len, TIMESTAMP_TIME, 1); } else { + if (error) + set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, + ER_WARN_DATA_TRUNCATED, + from, len, TIMESTAMP_TIME, 1); + if (ltime.month) ltime.day=0; tmp=(ltime.day*24L+ltime.hour)*10000L+(ltime.minute*100+ltime.second); if (tmp > 8385959) { tmp=8385959; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, + ER_WARN_DATA_OUT_OF_RANGE, + from, len, TIMESTAMP_TIME, !error); error= 1; } } + if (ltime.neg) tmp= -tmp; error |= Field_time::store((longlong) tmp); @@ -3300,13 +3268,15 @@ int Field_time::store(double nr) if (nr > 8385959.0) { tmp=8385959L; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, + ER_WARN_DATA_OUT_OF_RANGE, nr, TIMESTAMP_TIME); error= 1; } else if (nr < -8385959.0) { tmp= -8385959L; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, + ER_WARN_DATA_OUT_OF_RANGE, nr, TIMESTAMP_TIME); error= 1; } else @@ -3317,7 +3287,8 @@ int Field_time::store(double nr) if (tmp % 100 > 59 || tmp/100 % 100 > 59) { tmp=0; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, + ER_WARN_DATA_OUT_OF_RANGE, nr, TIMESTAMP_TIME); error= 1; } } @@ -3333,13 +3304,15 @@ int Field_time::store(longlong nr) if (nr > (longlong) 8385959L) { tmp=8385959L; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, + ER_WARN_DATA_OUT_OF_RANGE, nr, TIMESTAMP_TIME, 1); error= 1; } else if (nr < (longlong) -8385959L) { tmp= -8385959L; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, + ER_WARN_DATA_OUT_OF_RANGE, nr, TIMESTAMP_TIME, 1); error= 1; } else @@ -3348,7 +3321,8 @@ int Field_time::store(longlong nr) if (tmp % 100 > 59 || tmp/100 % 100 > 59) { tmp=0; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, + ER_WARN_DATA_OUT_OF_RANGE, nr, TIMESTAMP_TIME, 1); error= 1; } } @@ -3395,6 +3369,39 @@ String *Field_time::val_str(String *val_buffer, } +/* + Normally we would not consider 'time' as a vaild date, but we allow + get_date() here to be able to do things like + DATE_FORMAT(time, "%l.%i %p") +*/ + +bool Field_time::get_date(TIME *ltime, uint fuzzydate) +{ + long tmp; + if (!fuzzydate) + { + push_warning_printf(table->in_use, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_WARN_DATA_OUT_OF_RANGE, + ER(ER_WARN_DATA_OUT_OF_RANGE), field_name, + table->in_use->row_count); + return 1; + } + tmp=(long) sint3korr(ptr); + ltime->neg=0; + if (tmp < 0) + { + ltime->neg= 1; + tmp=-tmp; + } + ltime->hour=tmp/10000; + tmp-=ltime->hour*10000; + ltime->minute= tmp/100; + ltime->second= tmp % 100; + ltime->year= ltime->month= ltime->day= ltime->second_part= 0; + return 0; +} + + bool Field_time::get_time(TIME *ltime) { long tmp=(long) sint3korr(ptr); @@ -3460,11 +3467,11 @@ int Field_year::store(const char *from, uint len,CHARSET_INFO *cs) if (nr < 0 || nr >= 100 && nr <= 1900 || nr > 2155) { *ptr=0; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); return 1; } - if (current_thd->count_cuted_fields && !test_if_int(from,len,end,cs)) - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED); + if (table->in_use->count_cuted_fields && !test_if_int(from,len,end,cs)) + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, 1); if (nr != 0 || len != 4) { if (nr < YY_PART_YEAR) @@ -3493,7 +3500,7 @@ int Field_year::store(longlong nr) if (nr < 0 || nr >= 100 && nr <= 1900 || nr > 2155) { *ptr=0; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1); return 1; } if (nr != 0 || field_length != 4) // 0000 -> 0; 00 -> 2000 @@ -3557,15 +3564,21 @@ int Field_date::store(const char *from, uint len,CHARSET_INFO *cs) { TIME l_time; uint32 tmp; - int error= 0; - if (str_to_TIME(from,len,&l_time,1) <= TIMESTAMP_DATETIME_ERROR) + int error; + + if (str_to_TIME(from, len, &l_time, 1, &error) <= + TIMESTAMP_DATETIME_ERROR) { tmp=0; error= 1; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED); } else tmp=(uint32) l_time.year*10000L + (uint32) (l_time.month*100+l_time.day); + + if (error) + set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, + from, len, TIMESTAMP_DATE, 1); + #ifdef WORDS_BIGENDIAN if (table->db_low_byte_first) { @@ -3587,7 +3600,9 @@ int Field_date::store(double nr) if (nr < 0.0 || nr > 99991231.0) { tmp=0L; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, + ER_WARN_DATA_OUT_OF_RANGE, + nr, TIMESTAMP_DATE); error= 1; } else @@ -3613,7 +3628,9 @@ int Field_date::store(longlong nr) if (nr < 0 || nr > LL(99991231)) { tmp=0L; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, + ER_WARN_DATA_OUT_OF_RANGE, + nr, TIMESTAMP_DATE, 0); error= 1; } else @@ -3740,15 +3757,20 @@ int Field_newdate::store(const char *from,uint len,CHARSET_INFO *cs) { TIME l_time; long tmp; - int error= 0; - if (str_to_TIME(from,len,&l_time,1) <= TIMESTAMP_DATETIME_ERROR) + int error; + if (str_to_TIME(from, len, &l_time, 1, &error) <= + TIMESTAMP_DATETIME_ERROR) { tmp=0L; error= 1; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED); } else tmp= l_time.day + l_time.month*32 + l_time.year*16*32; + + if (error) + set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, + from, len, TIMESTAMP_DATE, 1); + int3store(ptr,tmp); return error; } @@ -3758,7 +3780,8 @@ int Field_newdate::store(double nr) if (nr < 0.0 || nr > 99991231235959.0) { (void) Field_newdate::store((longlong) -1); - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED); + set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, + ER_WARN_DATA_TRUNCATED, nr, TIMESTAMP_DATE); return 1; } else @@ -3775,7 +3798,8 @@ int Field_newdate::store(longlong nr) if (nr < 0L || nr > 99991231L) { tmp=0; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, + ER_WARN_DATA_OUT_OF_RANGE, nr, TIMESTAMP_DATE, 1); error= 1; } else @@ -3793,7 +3817,8 @@ int Field_newdate::store(longlong nr) if (month > 12 || day > 31) { tmp=0L; // Don't allow date to change - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); + set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, + ER_WARN_DATA_OUT_OF_RANGE, nr, TIMESTAMP_DATE, 1); error= 1; } else @@ -3811,7 +3836,7 @@ void Field_newdate::store_time(TIME *ltime,timestamp_type type) else { tmp=0; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, 1); } int3store(ptr,tmp); } @@ -3910,12 +3935,19 @@ void Field_newdate::sql_type(String &res) const int Field_datetime::store(const char *from,uint len,CHARSET_INFO *cs) { - longlong tmp=str_to_datetime(from,len,1); - if (tmp < 0) - { - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); - tmp= 0; - } + TIME time_tmp; + int error; + ulonglong tmp= 0; + + if (str_to_TIME(from, len, &time_tmp, 1, &error) > + TIMESTAMP_DATETIME_ERROR) + tmp= TIME_to_ulonglong_datetime(&time_tmp); + + if (error) + set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, + ER_WARN_DATA_OUT_OF_RANGE, + from, len, TIMESTAMP_DATETIME, 1); + #ifdef WORDS_BIGENDIAN if (table->db_low_byte_first) { @@ -3924,7 +3956,7 @@ int Field_datetime::store(const char *from,uint len,CHARSET_INFO *cs) else #endif longlongstore(ptr,tmp); - return 0; + return error; } @@ -3933,8 +3965,10 @@ int Field_datetime::store(double nr) int error= 0; if (nr < 0.0 || nr > 99991231235959.0) { + set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, + ER_WARN_DATA_OUT_OF_RANGE, + nr, TIMESTAMP_DATETIME); nr=0.0; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE); error= 1; } error |= Field_datetime::store((longlong) rint(nr)); @@ -3945,9 +3979,16 @@ int Field_datetime::store(double nr) int Field_datetime::store(longlong nr) { TIME not_used; - bool error; + int error; + longlong initial_nr= nr; - nr= fix_datetime(nr, ¬_used, field_name, &error); + nr= number_to_TIME(nr, ¬_used, 1, &error); + + if (error) + set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, + ER_WARN_DATA_TRUNCATED, initial_nr, + TIMESTAMP_DATETIME, 1); + #ifdef WORDS_BIGENDIAN if (table->db_low_byte_first) { @@ -3969,7 +4010,7 @@ void Field_datetime::store_time(TIME *ltime,timestamp_type type) else { tmp=0; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, 1); } #ifdef WORDS_BIGENDIAN if (table->db_low_byte_first) @@ -4146,7 +4187,10 @@ int Field_string::store(const char *from,uint length,CHARSET_INFO *cs) char buff[80]; String tmpstr(buff,sizeof(buff), &my_charset_bin); uint copy_length; - + + /* See the comment for Field_long::store(long long) */ + DBUG_ASSERT(table->in_use == current_thd); + /* Convert character set if nesessary */ if (String::needs_conversion(length, cs, field_charset, ¬_used)) { @@ -4168,7 +4212,7 @@ int Field_string::store(const char *from,uint length,CHARSET_INFO *cs) field_charset->cset->fill(field_charset,ptr+copy_length, field_length-copy_length,' '); - if ((copy_length < length) && current_thd->count_cuted_fields) + if ((copy_length < length) && table->in_use->count_cuted_fields) { // Check if we loosed some info const char *end=from+length; from+= copy_length; @@ -4176,7 +4220,7 @@ int Field_string::store(const char *from,uint length,CHARSET_INFO *cs) MY_SEQ_SPACES); if (from != end) { - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, 1); error=1; } } @@ -4224,6 +4268,8 @@ String *Field_string::val_str(String *val_buffer __attribute__((unused)), String *val_ptr) { 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); val_ptr->set((const char*) ptr, length, field_charset); return val_ptr; } @@ -4351,7 +4397,7 @@ int Field_varstring::store(const char *from,uint length,CHARSET_INFO *cs) if (length > field_length) { length=field_length; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, 1); error= 1; } memcpy(ptr+HA_KEY_BLOB_LENGTH,from,length); @@ -4669,7 +4715,7 @@ int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs) min(length, copy_length), copy_length); if (copy_length < length) - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, 1); Field_blob::store_length(copy_length); if (was_conversion || table->copy_blobs || copy_length <= MAX_FIELD_WIDTH) @@ -5228,11 +5274,11 @@ int Field_enum::store(const char *from,uint length,CHARSET_INFO *cs) if (err || end != from+length || tmp > typelib->count) { tmp=0; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, 1); } } else - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, 1); } store_type((ulonglong) tmp); return err; @@ -5250,7 +5296,7 @@ int Field_enum::store(longlong nr) int error= 0; if ((uint) nr > typelib->count || nr == 0) { - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, 1); nr=0; error=1; } @@ -5406,11 +5452,11 @@ int Field_set::store(const char *from,uint length,CHARSET_INFO *cs) tmp > (ulonglong) (((longlong) 1 << typelib->count) - (longlong) 1)) { tmp=0; - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, 1); } } else if (got_warning) - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, 1); store_type(tmp); return err; } @@ -5423,7 +5469,7 @@ int Field_set::store(longlong nr) (longlong) 1)) { nr&= (longlong) (((longlong) 1 << typelib->count) - (longlong) 1); - set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED); + set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, 1); error=1; } store_type((ulonglong) nr); @@ -5802,13 +5848,148 @@ create_field::create_field(Field *old_field,Field *orig_field) /* Warning handling */ -void Field::set_warning(const uint level, const uint code) + +/* + Produce warning or note about data saved into field + + SYNOPSYS + set_warning() + level - level of message (Note/Warning/Error) + code - error code of message to be produced + cuted_increment - whenever we should increase cut fields count or not + + NOTE + This function won't produce warning and increase cut fields counter + if count_cuted_fields == FIELD_CHECK_IGNORE for current thread. + + RETURN VALUE + true - if count_cuted_fields == FIELD_CHECK_IGNORE + false - otherwise +*/ +bool +Field::set_warning(const uint level, const uint code, int cuted_increment) { - THD *thd= current_thd; + THD *thd= table->in_use; if (thd->count_cuted_fields) { - thd->cuted_fields++; - push_warning_printf(thd, (MYSQL_ERROR::enum_warning_level) level, - code, ER(code), field_name, thd->row_count); + thd->cuted_fields+= cuted_increment; + push_warning_printf(thd, (MYSQL_ERROR::enum_warning_level) level, + code, ER(code), field_name, thd->row_count); + return 0; + } + return 1; +} + + +/* + Produce warning or note about datetime string data saved into field + + SYNOPSYS + set_warning() + level - level of message (Note/Warning/Error) + code - error code of message to be produced + str - string value which we tried to save + str_len - length of string which we tried to save + ts_type - type of datetime value (datetime/date/time) + cuted_increment - whenever we should increase cut fields count or not + + NOTE + This function will always produce some warning but won't increase cut + fields counter if count_cuted_fields == FIELD_CHECK_IGNORE for current + thread. +*/ +void +Field::set_datetime_warning(const uint level, const uint code, + const char *str, uint str_length, + timestamp_type ts_type, int cuted_increment) +{ + if (set_warning(level, code, cuted_increment)) + make_truncated_value_warning(table->in_use, str, str_length, ts_type); +} + + +/* + Produce warning or note about integer datetime value saved into field + + SYNOPSYS + set_warning() + level - level of message (Note/Warning/Error) + code - error code of message to be produced + nr - numeric value which we tried to save + ts_type - type of datetime value (datetime/date/time) + cuted_increment - whenever we should increase cut fields count or not + + NOTE + This function will always produce some warning but won't increase cut + fields counter if count_cuted_fields == FIELD_CHECK_IGNORE for current + thread. +*/ +void +Field::set_datetime_warning(const uint level, const uint code, + longlong nr, timestamp_type ts_type, + int cuted_increment) +{ + if (set_warning(level, code, cuted_increment)) + { + char str_nr[22]; + char *str_end= longlong10_to_str(nr, str_nr, -10); + make_truncated_value_warning(table->in_use, str_nr, str_end - str_nr, + ts_type); + } +} + + +/* + Produce warning or note about double datetime data saved into field + + SYNOPSYS + set_warning() + level - level of message (Note/Warning/Error) + code - error code of message to be produced + nr - double value which we tried to save + ts_type - type of datetime value (datetime/date/time) + + NOTE + This function will always produce some warning but won't increase cut + fields counter if count_cuted_fields == FIELD_CHECK_IGNORE for current + thread. +*/ +void +Field::set_datetime_warning(const uint level, const uint code, + double nr, timestamp_type ts_type) +{ + if (set_warning(level, code, 1)) + { + /* DBL_DIG is enough to print '-[digits].E+###' */ + char str_nr[DBL_DIG + 8]; + uint str_len= my_sprintf(str_nr, (str_nr, "%g", nr)); + make_truncated_value_warning(table->in_use, str_nr, str_len, ts_type); + } +} + +/* + maximum possible display length for blob + + SYNOPSIS + Field_blob::max_length() + + RETURN + length +*/ +uint32 Field_blob::max_length() +{ + switch (packlength) + { + case 1: + return 255; + case 2: + return 65535; + case 3: + return 16777215; + case 4: + return (uint32) 4294967295U; + default: + DBUG_ASSERT(0); // we should never go here + return 0; } } diff --git a/sql/field.h b/sql/field.h index 002a7228164..e7a30372e43 100644 --- a/sql/field.h +++ b/sql/field.h @@ -45,6 +45,10 @@ public: char *ptr; // Position to field in record uchar *null_ptr; // Byte where null_bit is + /* + 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) + */ struct st_table *table; // Pointer for table struct st_table *orig_table; // Pointer to original table const char *table_name,*field_name; @@ -264,9 +268,20 @@ public: virtual CHARSET_INFO *charset(void) const { return &my_charset_bin; } virtual bool has_charset(void) const { return FALSE; } virtual void set_charset(CHARSET_INFO *charset) { } - void set_warning(const unsigned int level, const unsigned int code); + bool set_warning(const unsigned int level, const unsigned int code, + int cuted_increment); + void set_datetime_warning(const uint level, const uint code, + const char *str, uint str_len, + timestamp_type ts_type, int cuted_increment); + void set_datetime_warning(const uint level, const uint code, + longlong nr, timestamp_type ts_type, + int cuted_increment); + void set_datetime_warning(const uint level, const uint code, + double nr, timestamp_type ts_type); virtual field_cast_enum field_cast_type()= 0; bool field_cast_compatible(field_cast_enum type); + /* maximum possible display length */ + virtual uint32 max_length()= 0; friend bool reopen_table(THD *,struct st_table *,bool); friend int cre_myisam(my_string name, register TABLE *form, uint options, ulonglong auto_increment_value); @@ -336,6 +351,7 @@ public: CHARSET_INFO *charset(void) const { return field_charset; } void set_charset(CHARSET_INFO *charset) { field_charset=charset; } bool binary() const { return field_charset->state & MY_CS_BINSORT ? 1 : 0; } + uint32 max_length() { return field_length; } friend class create_field; }; @@ -366,6 +382,7 @@ public: void overflow(bool negative); bool zero_pack() const { return 0; } void sql_type(String &str) const; + uint32 max_length() { return field_length; } field_cast_enum field_cast_type() { return FIELD_CAST_DECIMAL; } }; @@ -397,6 +414,7 @@ public: void sort_string(char *buff,uint length); uint32 pack_length() const { return 1; } void sql_type(String &str) const; + uint32 max_length() { return 4; } field_cast_enum field_cast_type() { return FIELD_CAST_TINY; } }; @@ -433,6 +451,7 @@ public: void sort_string(char *buff,uint length); uint32 pack_length() const { return 2; } void sql_type(String &str) const; + uint32 max_length() { return 6; } field_cast_enum field_cast_type() { return FIELD_CAST_SHORT; } }; @@ -464,6 +483,7 @@ public: void sort_string(char *buff,uint length); uint32 pack_length() const { return 3; } void sql_type(String &str) const; + uint32 max_length() { return 8; } field_cast_enum field_cast_type() { return FIELD_CAST_MEDIUM; } }; @@ -500,6 +520,7 @@ public: void sort_string(char *buff,uint length); uint32 pack_length() const { return 4; } void sql_type(String &str) const; + uint32 max_length() { return 11; } field_cast_enum field_cast_type() { return FIELD_CAST_LONG; } }; @@ -539,6 +560,7 @@ public: uint32 pack_length() const { return 8; } void sql_type(String &str) const; bool store_for_compare() { return 1; } + uint32 max_length() { return 20; } field_cast_enum field_cast_type() { return FIELD_CAST_LONGLONG; } }; #endif @@ -573,6 +595,7 @@ public: void sort_string(char *buff,uint length); uint32 pack_length() const { return sizeof(float); } void sql_type(String &str) const; + uint32 max_length() { return 24; } field_cast_enum field_cast_type() { return FIELD_CAST_FLOAT; } }; @@ -607,6 +630,7 @@ public: void sort_string(char *buff,uint length); uint32 pack_length() const { return sizeof(double); } void sql_type(String &str) const; + uint32 max_length() { return 53; } field_cast_enum field_cast_type() { return FIELD_CAST_DOUBLE; } }; @@ -637,6 +661,7 @@ public: uint32 pack_length() const { return 0; } void sql_type(String &str) const; uint size_of() const { return sizeof(*this); } + uint32 max_length() { return 4; } field_cast_enum field_cast_type() { return FIELD_CAST_NULL; } }; @@ -800,6 +825,7 @@ public: double val_real(void); longlong val_int(void); String *val_str(String*,String *); + bool get_date(TIME *ltime, uint fuzzydate); bool send_binary(Protocol *protocol); bool get_time(TIME *ltime); int cmp(const char *,const char*); @@ -1034,6 +1060,7 @@ public: bool has_charset(void) const { return charset() == &my_charset_bin ? FALSE : TRUE; } field_cast_enum field_cast_type() { return FIELD_CAST_BLOB; } + uint32 max_length(); }; #ifdef HAVE_SPATIAL diff --git a/sql/field_conv.cc b/sql/field_conv.cc index 0974c552364..e98068ef974 100644 --- a/sql/field_conv.cc +++ b/sql/field_conv.cc @@ -121,7 +121,8 @@ set_field_to_null(Field *field) field->reset(); if (current_thd->count_cuted_fields == CHECK_FIELD_WARN) { - field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN,ER_WARN_DATA_TRUNCATED); + field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, + ER_WARN_DATA_TRUNCATED, 1); return 0; } if (!current_thd->no_errors) @@ -178,7 +179,8 @@ set_field_to_null_with_conversions(Field *field, bool no_conversions) } if (current_thd->count_cuted_fields == CHECK_FIELD_WARN) { - field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN,ER_WARN_NULL_TO_NOTNULL); + field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, + ER_WARN_NULL_TO_NOTNULL, 1); return 0; } if (!current_thd->no_errors) @@ -229,7 +231,7 @@ static void do_copy_not_null(Copy_field *copy) if (*copy->from_null_ptr & copy->from_bit) { copy->to_field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, - ER_WARN_DATA_TRUNCATED); + ER_WARN_DATA_TRUNCATED, 1); copy->to_field->reset(); } else @@ -329,7 +331,7 @@ static void do_cut_string(Copy_field *copy) if (!my_isspace(system_charset_info, *ptr)) // QQ: ucs incompatible { copy->to_field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, - ER_WARN_DATA_TRUNCATED); + ER_WARN_DATA_TRUNCATED, 1); break; } } @@ -350,7 +352,7 @@ static void do_varstring(Copy_field *copy) length=copy->to_length-2; if (current_thd->count_cuted_fields) copy->to_field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, - ER_WARN_DATA_TRUNCATED); + ER_WARN_DATA_TRUNCATED, 1); } int2store(copy->to_ptr,length); memcpy(copy->to_ptr+2, copy->from_ptr,length); diff --git a/sql/ha_berkeley.cc b/sql/ha_berkeley.cc index 4dde893116f..a13b6147468 100644 --- a/sql/ha_berkeley.cc +++ b/sql/ha_berkeley.cc @@ -843,8 +843,8 @@ int ha_berkeley::write_row(byte * record) else { DB_TXN *sub_trans = transaction; - /* Don't use sub transactions in temporary tables (in_use == 0) */ - ulong thd_options = table->in_use ? table->in_use->options : 0; + /* Don't use sub transactions in temporary tables */ + ulong thd_options = table->tmp_table == NO_TMP_TABLE ? table->in_use->options : 0; for (uint retry=0 ; retry < berkeley_trans_retry ; retry++) { key_map changed_keys(0); @@ -1067,7 +1067,7 @@ int ha_berkeley::update_row(const byte * old_row, byte * new_row) DBT prim_key, key, old_prim_key; int error; DB_TXN *sub_trans; - ulong thd_options = table->in_use ? table->in_use->options : 0; + ulong thd_options = table->tmp_table == NO_TMP_TABLE ? table->in_use->options : 0; bool primary_key_changed; DBUG_ENTER("update_row"); LINT_INIT(error); @@ -1260,7 +1260,7 @@ int ha_berkeley::delete_row(const byte * record) int error; DBT row, prim_key; key_map keys=table->keys_in_use; - ulong thd_options = table->in_use ? table->in_use->options : 0; + ulong thd_options = table->tmp_table == NO_TMP_TABLE ? table->in_use->options : 0; DBUG_ENTER("delete_row"); statistic_increment(ha_delete_count,&LOCK_status); diff --git a/sql/ha_myisam.cc b/sql/ha_myisam.cc index 318e0fbb507..625a00d3558 100644 --- a/sql/ha_myisam.cc +++ b/sql/ha_myisam.cc @@ -1013,16 +1013,29 @@ bool ha_myisam::check_and_repair(THD *thd) if (!file->state->del && (myisam_recover_options & HA_RECOVER_QUICK)) check_opt.flags|=T_QUICK; sql_print_error("Warning: Checking table: '%s'",table->path); - if ((marked_crashed=mi_is_crashed(file)) || check(thd, &check_opt)) + if ((marked_crashed=mi_is_crashed(file))) { - sql_print_error("Warning: Recovering table: '%s'",table->path); - check_opt.flags= - ((myisam_recover_options & HA_RECOVER_BACKUP ? T_BACKUP_DATA : 0) | - (marked_crashed ? 0 : T_QUICK) | - (myisam_recover_options & HA_RECOVER_FORCE ? 0 : T_SAFE_REPAIR) | - T_AUTO_REPAIR); - if (repair(thd, &check_opt)) - error=1; + char *old_query= thd->query; + uint old_query_length= thd->query_length; + pthread_mutex_lock(&LOCK_thread_count); + thd->query= table->real_name; + thd->query_length= strlen(table->real_name); + pthread_mutex_unlock(&LOCK_thread_count); + if (check(thd, &check_opt)) + { + sql_print_error("Warning: Recovering table: '%s'",table->path); + check_opt.flags= + ((myisam_recover_options & HA_RECOVER_BACKUP ? T_BACKUP_DATA : 0) | + (marked_crashed ? 0 : T_QUICK) | + (myisam_recover_options & HA_RECOVER_FORCE ? 0 : T_SAFE_REPAIR) | + T_AUTO_REPAIR); + if (repair(thd, &check_opt)) + error=1; + } + pthread_mutex_lock(&LOCK_thread_count); + thd->query= old_query; + thd->query_length= old_query_length; + pthread_mutex_unlock(&LOCK_thread_count); } DBUG_RETURN(error); } diff --git a/sql/handler.cc b/sql/handler.cc index 717b2ee0ce8..a54aa9aff72 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -578,10 +578,11 @@ int ha_rollback_trans(THD *thd, THD_TRANS *trans) if ((trans == &thd->transaction.all) && mysql_bin_log.is_open()) { /* - Update the binary log with a BEGIN/ROLLBACK block if we have cached some - queries and we updated some non-transactional table. Such cases should - be rare (updating a non-transactional table inside a transaction...). - Count disk writes to trans_log in any case. + Update the binary log with a BEGIN/ROLLBACK block if we have + cached some queries and we updated some non-transactional + table. Such cases should be rare (updating a + non-transactional table inside a transaction...). Count disk + writes to trans_log in any case. */ if (my_b_tell(&thd->transaction.trans_log)) { @@ -626,12 +627,12 @@ int ha_rollback_trans(THD *thd, THD_TRANS *trans) simply truncate the binlog cache, we lose the part of the binlog cache where the update is. If we want to not lose it, we need to write the SAVEPOINT command and the ROLLBACK TO SAVEPOINT command to the binlog cache. The latter - is easy: it's just write at the end of the binlog cache, but the former should - be *inserted* to the place where the user called SAVEPOINT. The solution is - that when the user calls SAVEPOINT, we write it to the binlog cache (so no - need to later insert it). As transactions are never intermixed in the binary log - (i.e. they are serialized), we won't have conflicts with savepoint names when - using mysqlbinlog or in the slave SQL thread. + is easy: it's just write at the end of the binlog cache, but the former + should be *inserted* to the place where the user called SAVEPOINT. The + solution is that when the user calls SAVEPOINT, we write it to the binlog + cache (so no need to later insert it). As transactions are never intermixed + in the binary log (i.e. they are serialized), we won't have conflicts with + savepoint names when using mysqlbinlog or in the slave SQL thread. Then when ROLLBACK TO SAVEPOINT is called, if we updated some non-transactional table, we don't truncate the binlog cache but instead write ROLLBACK TO SAVEPOINT to it; otherwise we truncate the binlog cache (which diff --git a/sql/item.cc b/sql/item.cc index 8f5cd3df3fd..35e1312b540 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -228,7 +228,7 @@ bool Item::get_date(TIME *ltime,uint fuzzydate) char buff[40]; String tmp(buff,sizeof(buff), &my_charset_bin),*res; if (!(res=val_str(&tmp)) || - str_to_TIME(res->ptr(),res->length(),ltime,fuzzydate) <= + str_to_TIME_with_warn(res->ptr(),res->length(),ltime,fuzzydate) <= TIMESTAMP_DATETIME_ERROR) { bzero((char*) ltime,sizeof(*ltime)); @@ -247,7 +247,7 @@ bool Item::get_time(TIME *ltime) char buff[40]; String tmp(buff,sizeof(buff),&my_charset_bin),*res; if (!(res=val_str(&tmp)) || - str_to_time(res->ptr(),res->length(),ltime)) + str_to_time_with_warn(res->ptr(), res->length(), ltime)) { bzero((char*) ltime,sizeof(*ltime)); return 1; @@ -628,7 +628,8 @@ default_set_param_func(Item_param *param, Item_param::Item_param(unsigned pos_in_query_arg) : state(NO_VALUE), item_result_type(STRING_RESULT), - item_type(STRING_ITEM), + /* Don't pretend to be a literal unless value for this item is set. */ + item_type(PARAM_ITEM), param_type(MYSQL_TYPE_STRING), pos_in_query(pos_in_query_arg), set_param_func(default_set_param_func) @@ -755,43 +756,42 @@ bool Item_param::set_from_user_var(THD *thd, const user_var_entry *entry) if (entry && entry->value) { item_result_type= entry->type; - switch (entry->type) + switch (entry->type) { + case REAL_RESULT: + set_double(*(double*)entry->value); + break; + case INT_RESULT: + set_int(*(longlong*)entry->value, 21); + break; + case STRING_RESULT: { - case REAL_RESULT: - set_double(*(double*)entry->value); - break; - case INT_RESULT: - set_int(*(longlong*)entry->value, 21); - break; - case STRING_RESULT: - { - CHARSET_INFO *fromcs= entry->collation.collation; - CHARSET_INFO *tocs= thd->variables.collation_connection; - uint32 dummy_offset; - - 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 - make later checks easier. - */ - value.cs_info.final_character_set_of_str_value= - String::needs_conversion(0, fromcs, tocs, &dummy_offset) ? - tocs : fromcs; - /* - Exact value of max_length is not known unless data is converted to - charset of connection, so we have to set it later. - */ - item_type= Item::STRING_ITEM; - item_result_type= STRING_RESULT; - - if (set_str((const char *)entry->value, entry->length)) - DBUG_RETURN(1); - } - break; - default: - DBUG_ASSERT(0); - set_null(); + CHARSET_INFO *fromcs= entry->collation.collation; + CHARSET_INFO *tocs= thd->variables.collation_connection; + uint32 dummy_offset; + + 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 + make later checks easier. + */ + value.cs_info.final_character_set_of_str_value= + String::needs_conversion(0, fromcs, tocs, &dummy_offset) ? + tocs : fromcs; + /* + Exact value of max_length is not known unless data is converted to + charset of connection, so we have to set it later. + */ + item_type= Item::STRING_ITEM; + item_result_type= STRING_RESULT; + + if (set_str((const char *)entry->value, entry->length)) + DBUG_RETURN(1); + break; + } + default: + DBUG_ASSERT(0); + set_null(); } } else @@ -827,6 +827,15 @@ void Item_param::reset() state= NO_VALUE; maybe_null= 1; null_value= 0; + /* + Don't reset item_type to PARAM_ITEM: it's only needed to guard + us from item optimizations at prepare stage, when item doesn't yet + contain a literal of some kind. + In all other cases when this object is accessed its value is + set (this assumption is guarded by 'state' and + DBUG_ASSERTS(state != NO_VALUE) in all Item_param::get_* + methods). + */ } @@ -2419,6 +2428,7 @@ Item_type_holder::Item_type_holder(THD *thd, Item *item) field_example= ((Item_field*) item)->field; else field_example= 0; + max_length= real_length(item); collation.set(item->collation); } @@ -2438,6 +2448,7 @@ static Item_result type_convertor[4][4]= bool Item_type_holder::join_types(THD *thd, Item *item) { + uint32 new_length= real_length(item); bool change_field= 0, skip_store_field= 0; Item_result new_type= type_convertor[item_type][item->result_type()]; @@ -2463,7 +2474,7 @@ bool Item_type_holder::join_types(THD *thd, Item *item) // size/type should be changed if (change_field || (new_type != item_type) || - (max_length < item->max_length) || + (max_length < new_length) || ((new_type == INT_RESULT) && (decimals < item->decimals)) || (!maybe_null && item->maybe_null) || @@ -2472,7 +2483,7 @@ bool Item_type_holder::join_types(THD *thd, Item *item) { // new field has some parameters worse then current skip_store_field|= (change_field && - (max_length > item->max_length) || + (max_length > new_length) || ((new_type == INT_RESULT) && (decimals > item->decimals)) || (maybe_null && !item->maybe_null) || @@ -2501,7 +2512,7 @@ bool Item_type_holder::join_types(THD *thd, Item *item) return 1; } - max_length= max(max_length, item->max_length); + max_length= max(max_length, new_length); decimals= max(decimals, item->decimals); maybe_null|= item->maybe_null; item_type= new_type; @@ -2510,6 +2521,26 @@ bool Item_type_holder::join_types(THD *thd, Item *item) return 0; } +uint32 Item_type_holder::real_length(Item *item) +{ + if (item->type() == Item::FIELD_ITEM) + { + return ((Item_field *)item)->max_disp_length(); + } + switch (item->result_type()) + { + case STRING_RESULT: + return item->max_length; + case REAL_RESULT: + return 53; + case INT_RESULT: + return 20; + case ROW_RESULT: + default: + DBUG_ASSERT(0); // we should never go there + return 0; + } +} double Item_type_holder::val() { diff --git a/sql/item.h b/sql/item.h index e373eb112c0..fe8c2cb0df1 100644 --- a/sql/item.h +++ b/sql/item.h @@ -98,7 +98,8 @@ public: COPY_STR_ITEM, FIELD_AVG_ITEM, DEFAULT_VALUE_ITEM, PROC_ITEM,COND_ITEM, REF_ITEM, FIELD_STD_ITEM, FIELD_VARIANCE_ITEM, INSERT_VALUE_ITEM, - SUBSELECT_ITEM, ROW_ITEM, CACHE_ITEM, TYPE_HOLDER}; + SUBSELECT_ITEM, ROW_ITEM, CACHE_ITEM, TYPE_HOLDER, + PARAM_ITEM}; enum cond_result { COND_UNDEF,COND_OK,COND_TRUE,COND_FALSE }; @@ -382,6 +383,7 @@ public: bool is_null() { return field->is_null(); } Item *get_tmp_table_item(THD *thd); void cleanup(); + inline uint32 max_disp_length() { return field->max_length(); } friend class Item_default_value; friend class Item_insert_value; friend class st_select_lex_unit; @@ -820,7 +822,10 @@ public: void save_org_in_field(Field *field) { (*ref)->save_org_in_field(field); } enum Item_result result_type () const { return (*ref)->result_type(); } enum_field_types field_type() const { return (*ref)->field_type(); } - table_map used_tables() const { return (*ref)->used_tables(); } + table_map used_tables() const + { + return depended_from ? OUTER_REF_TABLE_BIT : (*ref)->used_tables(); + } void set_result_field(Field *field) { result_field= field; } bool is_result_field() { return 1; } void save_in_result_field(bool no_conversions) @@ -1199,6 +1204,7 @@ public: String *val_str(String*); bool join_types(THD *thd, Item *); Field *example() { return field_example; } + static uint32 real_length(Item *item); void cleanup() { DBUG_ENTER("Item_type_holder::cleanup"); diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 446d72ac143..91257c31fb9 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -922,8 +922,8 @@ Item_func_if::fix_length_and_dec() decimals=max(args[1]->decimals,args[2]->decimals); enum Item_result arg1_type=args[1]->result_type(); enum Item_result arg2_type=args[2]->result_type(); - bool null1=args[1]->null_value; - bool null2=args[2]->null_value; + bool null1=args[1]->const_item() && args[1]->null_value; + bool null2=args[2]->const_item() && args[2]->null_value; if (null1) { @@ -2151,49 +2151,62 @@ Item_func::optimize_type Item_func_like::select_optimize() const bool Item_func_like::fix_fields(THD *thd, TABLE_LIST *tlist, Item ** ref) { DBUG_ASSERT(fixed == 0); - if (Item_bool_func2::fix_fields(thd, tlist, ref)) + if (Item_bool_func2::fix_fields(thd, tlist, ref) || + escape_item->fix_fields(thd, tlist, &escape_item)) return 1; - /* - We could also do boyer-more for non-const items, but as we would have to - recompute the tables for each row it's not worth it. - */ - if (args[1]->const_item() && !use_strnxfrm(collation.collation) && - !(specialflag & SPECIAL_NO_NEW_FUNC)) + if (!escape_item->const_during_execution()) { - String* res2 = args[1]->val_str(&tmp_value2); - if (!res2) - return 0; // Null argument - - const size_t len = res2->length(); - const char* first = res2->ptr(); - const char* last = first + len - 1; + my_error(ER_WRONG_ARGUMENTS,MYF(0),"ESCAPE"); + return 1; + } + + if (escape_item->const_item()) + { + /* If we are on execution stage */ + String *escape_str= escape_item->val_str(&tmp_value1); + escape= escape_str ? *(escape_str->ptr()) : '\\'; + /* - len must be > 2 ('%pattern%') - heuristic: only do TurboBM for pattern_len > 2 + We could also do boyer-more for non-const items, but as we would have to + recompute the tables for each row it's not worth it. */ - - if (len > MIN_TURBOBM_PATTERN_LEN + 2 && - *first == wild_many && - *last == wild_many) - { - const char* tmp = first + 1; - for (; *tmp != wild_many && *tmp != wild_one && *tmp != escape; tmp++) ; - canDoTurboBM = (tmp == last) && !use_mb(args[0]->collation.collation); - } - - if (canDoTurboBM) + if (args[1]->const_item() && !use_strnxfrm(collation.collation) && + !(specialflag & SPECIAL_NO_NEW_FUNC)) { - pattern = first + 1; - pattern_len = len - 2; - DBUG_PRINT("info", ("Initializing pattern: '%s'", first)); - int *suff = (int*) thd->alloc(sizeof(int)*((pattern_len + 1)*2+ - alphabet_size)); - bmGs = suff + pattern_len + 1; - bmBc = bmGs + pattern_len + 1; - turboBM_compute_good_suffix_shifts(suff); - turboBM_compute_bad_character_shifts(); - DBUG_PRINT("info",("done")); + String* res2 = args[1]->val_str(&tmp_value2); + if (!res2) + return 0; // Null argument + + const size_t len = res2->length(); + const char* first = res2->ptr(); + const char* last = first + len - 1; + /* + len must be > 2 ('%pattern%') + heuristic: only do TurboBM for pattern_len > 2 + */ + + if (len > MIN_TURBOBM_PATTERN_LEN + 2 && + *first == wild_many && + *last == wild_many) + { + const char* tmp = first + 1; + for (; *tmp != wild_many && *tmp != wild_one && *tmp != escape; tmp++) ; + canDoTurboBM = (tmp == last) && !use_mb(args[0]->collation.collation); + } + if (canDoTurboBM) + { + pattern = first + 1; + pattern_len = len - 2; + DBUG_PRINT("info", ("Initializing pattern: '%s'", first)); + int *suff = (int*) thd->alloc(sizeof(int)*((pattern_len + 1)*2+ + alphabet_size)); + bmGs = suff + pattern_len + 1; + bmBc = bmGs + pattern_len + 1; + turboBM_compute_good_suffix_shifts(suff); + turboBM_compute_bad_character_shifts(); + DBUG_PRINT("info",("done")); + } } } return 0; @@ -2298,15 +2311,19 @@ longlong Item_func_regex::val_int() } -Item_func_regex::~Item_func_regex() +void Item_func_regex::cleanup() { + DBUG_ENTER("Item_func_regex::cleanup"); + Item_bool_func::cleanup(); if (regex_compiled) { regfree(&preg); regex_compiled=0; } + DBUG_VOID_RETURN; } + #endif /* USE_REGEX */ diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index ef80c060c03..7c96226b08a 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -841,12 +841,14 @@ class Item_func_like :public Item_bool_func2 bool turboBM_matches(const char* text, int text_len) const; enum { alphabet_size = 256 }; + Item *escape_item; + public: char escape; - Item_func_like(Item *a,Item *b, char* escape_arg) + Item_func_like(Item *a,Item *b, Item *escape_arg) :Item_bool_func2(a,b), canDoTurboBM(false), pattern(0), pattern_len(0), - bmGs(0), bmBc(0), escape(*escape_arg) {} + bmGs(0), bmBc(0), escape_item(escape_arg) {} longlong val_int(); enum Functype functype() const { return LIKE_FUNC; } optimize_type select_optimize() const; @@ -869,7 +871,7 @@ class Item_func_regex :public Item_bool_func public: Item_func_regex(Item *a,Item *b) :Item_bool_func(a,b), regex_compiled(0),regex_is_const(0) {} - ~Item_func_regex(); + void cleanup(); longlong val_int(); bool fix_fields(THD *thd, struct st_table_list *tlist, Item **ref); const char *func_name() const { return "regexp"; } diff --git a/sql/item_create.cc b/sql/item_create.cc index 53d4f14d1ee..4290a25e348 100644 --- a/sql/item_create.cc +++ b/sql/item_create.cc @@ -89,6 +89,11 @@ Item *create_func_conv(Item* a, Item *b, Item *c) return new Item_func_conv(a,b,c); } +Item *create_func_convert_tz(Item* a, Item *b, Item *c) +{ + return new Item_func_convert_tz(a,b,c); +} + Item *create_func_cos(Item* a) { return new Item_func_cos(a); @@ -149,7 +154,7 @@ Item *create_func_found_rows(void) { THD *thd=current_thd; thd->lex->safe_to_cache_query= 0; - return new Item_int(NullS,(longlong) thd->found_rows(),21); + return new Item_func_found_rows(); } Item *create_func_from_days(Item* a) diff --git a/sql/item_create.h b/sql/item_create.h index 7577627ef04..19f0c9133f2 100644 --- a/sql/item_create.h +++ b/sql/item_create.h @@ -31,6 +31,7 @@ Item *create_func_char_length(Item* a); Item *create_func_cast(Item *a, Cast_target cast_type, int len, CHARSET_INFO *cs); Item *create_func_connection_id(void); Item *create_func_conv(Item* a, Item *b, Item *c); +Item *create_func_convert_tz(Item* a, Item *b, Item *c); Item *create_func_cos(Item* a); Item *create_func_cot(Item* a); Item *create_func_crc32(Item* a); diff --git a/sql/item_func.cc b/sql/item_func.cc index 53c6884c5de..e3874d8e4fa 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -3196,7 +3196,6 @@ longlong Item_func_is_free_lock::val_int() { DBUG_ASSERT(fixed == 1); String *res=args[0]->val_str(&value); - THD *thd=current_thd; User_level_lock *ull; null_value=0; @@ -3219,7 +3218,6 @@ longlong Item_func_is_used_lock::val_int() { DBUG_ASSERT(fixed == 1); String *res=args[0]->val_str(&value); - THD *thd=current_thd; User_level_lock *ull; null_value=1; @@ -3236,3 +3234,12 @@ longlong Item_func_is_used_lock::val_int() null_value=0; return ull->thread_id; } + + +longlong Item_func_found_rows::val_int() +{ + DBUG_ASSERT(fixed == 1); + THD *thd= current_thd; + + return thd->found_rows(); +} diff --git a/sql/item_func.h b/sql/item_func.h index 39c0a47ed7c..c05c1b01259 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -1071,3 +1071,13 @@ enum Cast_target ITEM_CAST_BINARY, ITEM_CAST_SIGNED_INT, ITEM_CAST_UNSIGNED_INT, ITEM_CAST_DATE, ITEM_CAST_TIME, ITEM_CAST_DATETIME, ITEM_CAST_CHAR }; + + +class Item_func_found_rows :public Item_int_func +{ +public: + Item_func_found_rows() :Item_int_func() {} + longlong val_int(); + const char *func_name() const { return "found_rows"; } + void fix_length_and_dec() { decimals= 0; maybe_null=0; } +}; diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 4503c1b63a9..691ec5f4c7b 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -657,7 +657,6 @@ Item_in_subselect::single_value_transformer(JOIN *join, !(select_lex->next_select())) { Item *item; - subs_type type= substype(); if (func->l_op()) { /* diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index 45d66addc9f..a5ea72374c1 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -894,6 +894,9 @@ longlong Item_func_year::val_int() longlong Item_func_unix_timestamp::val_int() { + TIME ltime; + bool not_used; + DBUG_ASSERT(fixed == 1); if (arg_count == 0) return (longlong) current_thd->query_start(); @@ -903,12 +906,19 @@ longlong Item_func_unix_timestamp::val_int() if (field->type() == FIELD_TYPE_TIMESTAMP) return ((Field_timestamp*) field)->get_timestamp(); } - String *str=args[0]->val_str(&value); - if ((null_value=args[0]->null_value)) + + if (get_arg0_date(<ime, 0)) { - return 0; /* purecov: inspected */ + /* + We have to set null_value again because get_arg0_date will also set it + to true if we have wrong datetime parameter (and we should return 0 in + this case). + */ + null_value= args[0]->null_value; + return 0; } - return (longlong) str_to_timestamp(str->ptr(),str->length()); + + return (longlong) TIME_to_timestamp(current_thd, <ime, ¬_used); } @@ -1126,23 +1136,14 @@ bool Item_func_from_days::get_date(TIME *ltime, uint fuzzy_date) void Item_func_curdate::fix_length_and_dec() { - struct tm start; - collation.set(&my_charset_bin); decimals=0; max_length=MAX_DATE_WIDTH*MY_CHARSET_BIN_MB_MAXLEN; - store_now_in_tm(current_thd->query_start(),&start); + store_now_in_TIME(<ime); - /* For getdate */ - ltime.year= start.tm_year+1900; - ltime.month= start.tm_mon+1; - ltime.day= start.tm_mday; - ltime.hour= 0; - ltime.minute= 0; - ltime.second= 0; - ltime.second_part=0; - ltime.neg=0; + /* We don't need to set second_part and neg because they already 0 */ + ltime.hour= ltime.minute= ltime.second= 0; ltime.time_type=TIMESTAMP_DATE; value= (longlong) TIME_to_ulonglong_date(<ime); } @@ -1159,31 +1160,39 @@ String *Item_func_curdate::val_str(String *str) return str; } -bool Item_func_curdate::get_date(TIME *res, - uint fuzzy_date __attribute__((unused))) +/* + Converts current time in my_time_t to TIME represenatation for local + time zone. Defines time zone (local) used for whole CURDATE function. +*/ +void Item_func_curdate_local::store_now_in_TIME(TIME *now_time) { - *res=ltime; - return 0; + THD *thd= current_thd; + thd->variables.time_zone->gmt_sec_to_TIME(now_time, + (my_time_t)thd->query_start()); + thd->time_zone_used= 1; } /* - Converts time in time_t to struct tm represenatation for local timezone. - Defines timezone (local) used for whole CURDATE function + Converts current time in my_time_t to TIME represenatation for UTC + time zone. Defines time zone (UTC) used for whole UTC_DATE function. */ -void Item_func_curdate_local::store_now_in_tm(time_t now, struct tm *now_tm) +void Item_func_curdate_utc::store_now_in_TIME(TIME *now_time) { - localtime_r(&now,now_tm); + my_tz_UTC->gmt_sec_to_TIME(now_time, + (my_time_t)(current_thd->query_start())); + /* + We are not flagging this query as using time zone, since it uses fixed + UTC-SYSTEM time-zone. + */ } -/* - Converts time in time_t to struct tm represenatation for UTC - Defines timezone (UTC) used for whole UTC_DATE function -*/ -void Item_func_curdate_utc::store_now_in_tm(time_t now, struct tm *now_tm) +bool Item_func_curdate::get_date(TIME *res, + uint fuzzy_date __attribute__((unused))) { - gmtime_r(&now,now_tm); + *res=ltime; + return 0; } @@ -1197,17 +1206,12 @@ String *Item_func_curtime::val_str(String *str) void Item_func_curtime::fix_length_and_dec() { - struct tm start; - String tmp((char*) buff,sizeof(buff), &my_charset_bin); TIME ltime; + String tmp((char*) buff,sizeof(buff), &my_charset_bin); - decimals=0; - store_now_in_tm(current_thd->query_start(),&start); - ltime.hour= start.tm_hour; - ltime.minute= start.tm_min; - ltime.second= start.tm_sec; - ltime.second_part= 0; - ltime.neg= 0; + decimals=0; + collation.set(&my_charset_bin); + store_now_in_TIME(<ime); value= TIME_to_ulonglong_time(<ime); make_time((DATE_TIME_FORMAT *) 0, <ime, &tmp); max_length= buff_length= tmp.length(); @@ -1215,22 +1219,30 @@ void Item_func_curtime::fix_length_and_dec() /* - Converts time in time_t to struct tm represenatation for local timezone. - Defines timezone (local) used for whole CURTIME function + Converts current time in my_time_t to TIME represenatation for local + time zone. Defines time zone (local) used for whole CURTIME function. */ -void Item_func_curtime_local::store_now_in_tm(time_t now, struct tm *now_tm) +void Item_func_curtime_local::store_now_in_TIME(TIME *now_time) { - localtime_r(&now,now_tm); + THD *thd= current_thd; + thd->variables.time_zone->gmt_sec_to_TIME(now_time, + (my_time_t)thd->query_start()); + thd->time_zone_used= 1; } /* - Converts time in time_t to struct tm represenatation for UTC. - Defines timezone (UTC) used for whole UTC_TIME function + Converts current time in my_time_t to TIME represenatation for UTC + time zone. Defines time zone (UTC) used for whole UTC_TIME function. */ -void Item_func_curtime_utc::store_now_in_tm(time_t now, struct tm *now_tm) +void Item_func_curtime_utc::store_now_in_TIME(TIME *now_time) { - gmtime_r(&now,now_tm); + my_tz_UTC->gmt_sec_to_TIME(now_time, + (my_time_t)(current_thd->query_start())); + /* + We are not flagging this query as using time zone, since it uses fixed + UTC-SYSTEM time-zone. + */ } @@ -1244,18 +1256,12 @@ String *Item_func_now::val_str(String *str) void Item_func_now::fix_length_and_dec() { - struct tm start; String tmp((char*) buff,sizeof(buff),&my_charset_bin); decimals=0; collation.set(&my_charset_bin); - store_now_in_tm(current_thd->query_start(),&start); - - /* For getdate */ - localtime_to_TIME(<ime, &start); - ltime.time_type= TIMESTAMP_DATETIME; - + store_now_in_TIME(<ime); value= (longlong) TIME_to_ulonglong_datetime(<ime); make_datetime((DATE_TIME_FORMAT *) 0, <ime, &tmp); @@ -1263,39 +1269,47 @@ void Item_func_now::fix_length_and_dec() } -bool Item_func_now::get_date(TIME *res, - uint fuzzy_date __attribute__((unused))) +/* + Converts current time in my_time_t to TIME represenatation for local + time zone. Defines time zone (local) used for whole NOW function. +*/ +void Item_func_now_local::store_now_in_TIME(TIME *now_time) { - *res=ltime; - return 0; + THD *thd= current_thd; + thd->variables.time_zone->gmt_sec_to_TIME(now_time, + (my_time_t)thd->query_start()); + thd->time_zone_used= 1; } -int Item_func_now::save_in_field(Field *to, bool no_conversions) +/* + Converts current time in my_time_t to TIME represenatation for UTC + time zone. Defines time zone (UTC) used for whole UTC_TIMESTAMP function. +*/ +void Item_func_now_utc::store_now_in_TIME(TIME *now_time) { - to->set_notnull(); - to->store_time(<ime,TIMESTAMP_DATETIME); - return 0; + my_tz_UTC->gmt_sec_to_TIME(now_time, + (my_time_t)(current_thd->query_start())); + /* + We are not flagging this query as using time zone, since it uses fixed + UTC-SYSTEM time-zone. + */ } -/* - Converts time in time_t to struct tm represenatation for local timezone. - Defines timezone (local) used for whole CURRENT_TIMESTAMP function -*/ -void Item_func_now_local::store_now_in_tm(time_t now, struct tm *now_tm) +bool Item_func_now::get_date(TIME *res, + uint fuzzy_date __attribute__((unused))) { - localtime_r(&now,now_tm); + *res=ltime; + return 0; } -/* - Converts time in time_t to struct tm represenatation for UTC. - Defines timezone (UTC) used for whole UTC_TIMESTAMP function -*/ -void Item_func_now_utc::store_now_in_tm(time_t now, struct tm *now_tm) +int Item_func_now::save_in_field(Field *to, bool no_conversions) { - gmtime_r(&now,now_tm); + to->set_notnull(); + to->store_time(<ime,TIMESTAMP_DATETIME); + return 0; } @@ -1455,7 +1469,7 @@ String *Item_func_date_format::val_str(String *str) { String *res; if (!(res=args[0]->val_str(str)) || - (str_to_time(res->ptr(),res->length(),&l_time))) + (str_to_time_with_warn(res->ptr(), res->length(), &l_time))) goto null_date; l_time.year=l_time.month=l_time.day=0; @@ -1489,24 +1503,31 @@ null_date: } +void Item_func_from_unixtime::fix_length_and_dec() +{ + thd= current_thd; + collation.set(&my_charset_bin); + decimals=0; + max_length=MAX_DATETIME_WIDTH*MY_CHARSET_BIN_MB_MAXLEN; + thd->time_zone_used= 1; +} + + String *Item_func_from_unixtime::val_str(String *str) { - struct tm tm_tmp; - time_t tmp; - TIME ltime; + TIME time_tmp; + my_time_t tmp; DBUG_ASSERT(fixed == 1); tmp= (time_t) args[0]->val_int(); if ((null_value=args[0]->null_value)) goto null_date; - - localtime_r(&tmp,&tm_tmp); - - localtime_to_TIME(<ime, &tm_tmp); - + + thd->variables.time_zone->gmt_sec_to_TIME(&time_tmp, tmp); + if (str->alloc(20*MY_CHARSET_BIN_MB_MAXLEN)) goto null_date; - make_datetime((DATE_TIME_FORMAT *) 0, <ime, str); + make_datetime((DATE_TIME_FORMAT *) 0, &time_tmp, str); return str; null_date: @@ -1517,28 +1538,109 @@ null_date: longlong Item_func_from_unixtime::val_int() { - TIME ltime; - struct tm tm_tmp; - time_t tmp; + TIME time_tmp; + my_time_t tmp; + DBUG_ASSERT(fixed == 1); tmp= (time_t) (ulong) args[0]->val_int(); if ((null_value=args[0]->null_value)) return 0; - localtime_r(&tmp,&tm_tmp); - localtime_to_TIME(<ime, &tm_tmp); - return (longlong) TIME_to_ulonglong_datetime(<ime); + + current_thd->variables.time_zone->gmt_sec_to_TIME(&time_tmp, tmp); + + return (longlong) TIME_to_ulonglong_datetime(&time_tmp); } bool Item_func_from_unixtime::get_date(TIME *ltime, uint fuzzy_date __attribute__((unused))) { - time_t tmp=(time_t) (ulong) args[0]->val_int(); + my_time_t tmp=(my_time_t) args[0]->val_int(); if ((null_value=args[0]->null_value)) return 1; - struct tm tm_tmp; - localtime_r(&tmp,&tm_tmp); - localtime_to_TIME(ltime, &tm_tmp); + + current_thd->variables.time_zone->gmt_sec_to_TIME(ltime, tmp); + + return 0; +} + + +void Item_func_convert_tz::fix_length_and_dec() +{ + String str; + + thd= current_thd; + collation.set(&my_charset_bin); + decimals= 0; + max_length= MAX_DATETIME_WIDTH*MY_CHARSET_BIN_MB_MAXLEN; + + if (args[1]->const_item()) + from_tz= my_tz_find(thd, args[1]->val_str(&str)); + + if (args[2]->const_item()) + to_tz= my_tz_find(thd, args[2]->val_str(&str)); +} + + +String *Item_func_convert_tz::val_str(String *str) +{ + TIME time_tmp; + + if (get_date(&time_tmp, 0)) + return 0; + + if (str->alloc(20*MY_CHARSET_BIN_MB_MAXLEN)) + { + null_value= 1; + return 0; + } + + make_datetime((DATE_TIME_FORMAT *) 0, &time_tmp, str); + return str; +} + + +longlong Item_func_convert_tz::val_int() +{ + TIME time_tmp; + + if (get_date(&time_tmp, 0)) + return 0; + + return (longlong)TIME_to_ulonglong_datetime(&time_tmp); +} + + +bool Item_func_convert_tz::get_date(TIME *ltime, + uint fuzzy_date __attribute__((unused))) +{ + my_time_t my_time_tmp; + bool not_used; + String str; + + if (!args[1]->const_item()) + from_tz= my_tz_find(thd, args[1]->val_str(&str)); + + if (!args[2]->const_item()) + to_tz= my_tz_find(thd, args[2]->val_str(&str)); + + if (from_tz==0 || to_tz==0 || get_arg0_date(ltime, 0)) + { + null_value= 1; + return 1; + } + + /* Check if we in range where we treat datetime values as non-UTC */ + if (ltime->year < TIMESTAMP_MAX_YEAR && ltime->year > TIMESTAMP_MIN_YEAR || + ltime->year==TIMESTAMP_MAX_YEAR && ltime->month==1 && ltime->day==1 || + ltime->year==TIMESTAMP_MIN_YEAR && ltime->month==12 && ltime->day==31) + { + my_time_tmp= from_tz->TIME_to_gmt_sec(ltime, ¬_used); + if (my_time_tmp >= TIMESTAMP_MIN_VALUE && my_time_tmp <= TIMESTAMP_MAX_VALUE) + to_tz->gmt_sec_to_TIME(ltime, my_time_tmp); + } + + null_value= 0; return 0; } @@ -1795,7 +1897,7 @@ longlong Item_extract::val_int() else { String *res= args[0]->val_str(&value); - if (!res || str_to_time(res->ptr(),res->length(),<ime)) + if (!res || str_to_time_with_warn(res->ptr(), res->length(), <ime)) { null_value=1; return 0; diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h index f00eb93e0e5..a7ff2924786 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -348,6 +348,7 @@ public: Item_date_func() :Item_str_func() {} Item_date_func(Item *a) :Item_str_func(a) {} Item_date_func(Item *a,Item *b) :Item_str_func(a,b) {} + Item_date_func(Item *a,Item *b, Item *c) :Item_str_func(a,b,c) {} enum_field_types field_type() const { return MYSQL_TYPE_DATETIME; } Field *tmp_table_field(TABLE *t_arg) { @@ -356,7 +357,7 @@ public: }; -/* Abstract CURTIME function. Children should define what timezone is used */ +/* Abstract CURTIME function. Children should define what time zone is used */ class Item_func_curtime :public Item_func { @@ -378,10 +379,10 @@ public: } /* Abstract method that defines which time zone is used for conversion. - Converts time from time_t representation to broken down representation - in struct tm using gmtime_r or localtime_r functions. + Converts time current time in my_time_t representation to broken-down + TIME representation using UTC-SYSTEM or per-thread time zone. */ - virtual void store_now_in_tm(time_t now, struct tm *now_tm)=0; + virtual void store_now_in_TIME(TIME *now_time)=0; }; @@ -391,7 +392,7 @@ public: Item_func_curtime_local() :Item_func_curtime() {} Item_func_curtime_local(Item *a) :Item_func_curtime(a) {} const char *func_name() const { return "curtime"; } - void store_now_in_tm(time_t now, struct tm *now_tm); + virtual void store_now_in_TIME(TIME *now_time); }; @@ -401,7 +402,7 @@ public: Item_func_curtime_utc() :Item_func_curtime() {} Item_func_curtime_utc(Item *a) :Item_func_curtime(a) {} const char *func_name() const { return "utc_time"; } - void store_now_in_tm(time_t now, struct tm *now_tm); + virtual void store_now_in_TIME(TIME *now_time); }; @@ -413,12 +414,11 @@ class Item_func_curdate :public Item_date TIME ltime; public: Item_func_curdate() :Item_date() {} - void set_result_from_tm(struct tm *now); longlong val_int() { DBUG_ASSERT(fixed == 1); return (value) ; } String *val_str(String *str); void fix_length_and_dec(); bool get_date(TIME *res, uint fuzzy_date); - virtual void store_now_in_tm(time_t now, struct tm *now_tm)=0; + virtual void store_now_in_TIME(TIME *now_time)=0; }; @@ -427,7 +427,7 @@ class Item_func_curdate_local :public Item_func_curdate public: Item_func_curdate_local() :Item_func_curdate() {} const char *func_name() const { return "curdate"; } - void store_now_in_tm(time_t now, struct tm *now_tm); + void store_now_in_TIME(TIME *now_time); }; @@ -436,7 +436,7 @@ class Item_func_curdate_utc :public Item_func_curdate public: Item_func_curdate_utc() :Item_func_curdate() {} const char *func_name() const { return "utc_date"; } - void store_now_in_tm(time_t now, struct tm *now_tm); + void store_now_in_TIME(TIME *now_time); }; @@ -458,7 +458,7 @@ public: String *val_str(String *str); void fix_length_and_dec(); bool get_date(TIME *res, uint fuzzy_date); - virtual void store_now_in_tm(time_t now, struct tm *now_tm)=0; + virtual void store_now_in_TIME(TIME *now_time)=0; }; @@ -468,7 +468,7 @@ public: Item_func_now_local() :Item_func_now() {} Item_func_now_local(Item *a) :Item_func_now(a) {} const char *func_name() const { return "now"; } - void store_now_in_tm(time_t now, struct tm *now_tm); + virtual void store_now_in_TIME(TIME *now_time); virtual enum Functype functype() const { return NOW_FUNC; } }; @@ -479,7 +479,7 @@ public: Item_func_now_utc() :Item_func_now() {} Item_func_now_utc(Item *a) :Item_func_now(a) {} const char *func_name() const { return "utc_timestamp"; } - void store_now_in_tm(time_t now, struct tm *now_tm); + virtual void store_now_in_TIME(TIME *now_time); }; @@ -509,6 +509,7 @@ public: class Item_func_from_unixtime :public Item_date_func { + THD *thd; public: Item_func_from_unixtime(Item *a) :Item_date_func(a) {} double val() @@ -519,12 +520,29 @@ class Item_func_from_unixtime :public Item_date_func longlong val_int(); String *val_str(String *str); const char *func_name() const { return "from_unixtime"; } - void fix_length_and_dec() - { - collation.set(&my_charset_bin); - decimals=0; - max_length=MAX_DATETIME_WIDTH*MY_CHARSET_BIN_MB_MAXLEN; - } + void fix_length_and_dec(); + bool get_date(TIME *res, uint fuzzy_date); +}; + + +/* + We need Time_zone class declaration for storing pointers in + Item_func_convert_tz. +*/ +class Time_zone; + +class Item_func_convert_tz :public Item_date_func +{ + THD *thd; + Time_zone *from_tz, *to_tz; + public: + Item_func_convert_tz(Item *a, Item *b, Item *c): + Item_date_func(a, b, c) {} + longlong val_int(); + double val() { return (double) val_int(); } + String *val_str(String *str); + const char *func_name() const { return "convert_tz"; } + void fix_length_and_dec(); bool get_date(TIME *res, uint fuzzy_date); }; diff --git a/sql/lex.h b/sql/lex.h index fde5076a25e..b1626c75c28 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -499,6 +499,7 @@ static SYMBOL sql_functions[] = { { "CONNECTION_ID", F_SYM(FUNC_ARG0),0,CREATE_FUNC(create_func_connection_id)}, { "CONTAINS", F_SYM(FUNC_ARG2),0,CREATE_FUNC_GEOM(create_func_contains)}, { "CONV", F_SYM(FUNC_ARG3),0,CREATE_FUNC(create_func_conv)}, + { "CONVERT_TZ", F_SYM(FUNC_ARG3),0,CREATE_FUNC(create_func_convert_tz)}, { "COUNT", SYM(COUNT_SYM)}, { "COS", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_cos)}, { "COT", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_cot)}, diff --git a/sql/log.cc b/sql/log.cc index 47a6a4a9b4c..0ee1ce0ea47 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -1258,21 +1258,35 @@ bool MYSQL_LOG::write(Log_event* event_info) { char buf[200]; int written= my_snprintf(buf, sizeof(buf)-1, - "SET ONE_SHOT CHARACTER_SET_CLIENT=%lu,\ -COLLATION_CONNECTION=%lu,COLLATION_DATABASE=%lu,COLLATION_SERVER=%lu", - thd->variables.character_set_client->number, - thd->variables.collation_connection->number, - thd->variables.collation_database->number, - thd->variables.collation_server->number); + "SET ONE_SHOT CHARACTER_SET_CLIENT=%u,\ +COLLATION_CONNECTION=%u,COLLATION_DATABASE=%u,COLLATION_SERVER=%u", + (uint) thd->variables.character_set_client->number, + (uint) thd->variables.collation_connection->number, + (uint) thd->variables.collation_database->number, + (uint) thd->variables.collation_server->number); Query_log_event e(thd, buf, written, 0); e.set_log_pos(this); if (e.write(file)) goto err; } + /* + We use the same ONE_SHOT trick for making replication of time zones + working in 4.1. Again in 5.0 we have better means for doing this. + */ + if (thd->time_zone_used && + thd->variables.time_zone != global_system_variables.time_zone) + { + char buf[MAX_TIME_ZONE_NAME_LENGTH + 26]; + char *buf_end= strxmov(buf, "SET ONE_SHOT TIME_ZONE='", + thd->variables.time_zone->get_name()->ptr(), + "'", NullS); + Query_log_event e(thd, buf, buf_end - buf, 0); + e.set_log_pos(this); + if (e.write(file)) + goto err; + } #endif - /* Add logging of timezones here */ - if (thd->last_insert_id_used) { Intvar_log_event e(thd,(uchar) LAST_INSERT_ID_EVENT, diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index b2d21c3fb55..db8d534064d 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -39,6 +39,14 @@ extern const key_map key_map_empty; extern const key_map key_map_full; extern const char *primary_key_name; +/* + Portable time_t replacement. + Should be signed and hold seconds for 1902-2038 range. +*/ +typedef long my_time_t; +#define MY_TIME_T_MAX LONG_MAX +#define MY_TIME_T_MIN LONG_MIN + #include "mysql_com.h" #include <violite.h> #include "unireg.h" @@ -349,6 +357,7 @@ inline THD *_current_thd(void) #include "sql_udf.h" class user_var_entry; #include "item.h" +#include "tztime.h" typedef Comp_creator* (*chooser_compare_func_creator)(bool invert); /* sql_parse.cc */ void free_items(Item *item); @@ -378,6 +387,7 @@ struct Query_cache_query_flags uint character_set_results_num; uint collation_connection_num; ha_rows limit; + Time_zone *time_zone; }; #define QUERY_CACHE_FLAGS_SIZE sizeof(Query_cache_query_flags) #include "sql_cache.h" @@ -822,7 +832,7 @@ extern Le_creator le_creator; extern uchar *days_in_month; extern char language[LIBLEN],reg_ext[FN_EXTLEN]; extern char glob_hostname[FN_REFLEN], mysql_home[FN_REFLEN]; -extern char pidfile_name[FN_REFLEN], time_zone[30], *opt_init_file; +extern char pidfile_name[FN_REFLEN], system_time_zone[30], *opt_init_file; extern char log_error_file[FN_REFLEN]; extern double log_10[32]; extern ulonglong log_10_int[20]; @@ -878,6 +888,7 @@ extern my_bool opt_enable_named_pipe, opt_sync_frm; extern my_bool opt_secure_auth; extern char *shared_memory_base_name, *mysqld_unix_port; extern bool opt_enable_shared_memory; +extern char *default_tz_name; extern MYSQL_LOG mysql_log,mysql_update_log,mysql_slow_log,mysql_bin_log; extern FILE *bootstrap_file; @@ -988,12 +999,16 @@ uint calc_days_in_year(uint year); void get_date_from_daynr(long daynr,uint *year, uint *month, uint *day); void init_time(void); -long my_gmt_sec(TIME *, long *current_timezone); -time_t str_to_timestamp(const char *str,uint length); -bool str_to_time(const char *str,uint length,TIME *l_time); -longlong str_to_datetime(const char *str,uint length, uint fuzzy_date); +my_time_t my_system_gmt_sec(const TIME *, long *current_timezone, bool *not_exist); +my_time_t TIME_to_timestamp(THD *thd, const TIME *t, bool *not_exist); +bool str_to_time(const char *str,uint length,TIME *l_time, int *was_cut); +bool str_to_time_with_warn(const char *str,uint length,TIME *l_time); timestamp_type str_to_TIME(const char *str, uint length, TIME *l_time, - uint flags); + uint flags, int *was_cut); +timestamp_type str_to_TIME_with_warn(const char *str, uint length, + TIME *l_time, uint flags); +longlong number_to_TIME(longlong nr, TIME *time_res, bool fuzzy_date, + int *was_cut); void localtime_to_TIME(TIME *to, struct tm *from); void calc_time_from_sec(TIME *to, long seconds, long microseconds); diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 841898ac505..3c23ecd3c3d 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -47,10 +47,6 @@ #define ONE_THREAD #endif -#define SHUTDOWN_THD -#define MAIN_THD -#define SIGNAL_THD - #ifdef HAVE_purify #define IF_PURIFY(A,B) (A) #else @@ -324,7 +320,8 @@ ulonglong log_10_int[20]= time_t start_time; -char mysql_home[FN_REFLEN], pidfile_name[FN_REFLEN], time_zone[30]; +char mysql_home[FN_REFLEN], pidfile_name[FN_REFLEN], system_time_zone[30]; +char *default_tz_name; char log_error_file[FN_REFLEN], glob_hostname[FN_REFLEN]; char* log_error_file_ptr= log_error_file; char mysql_real_data_home[FN_REFLEN], @@ -827,7 +824,6 @@ static void __cdecl kill_server(int sig_ptr) #if defined(USE_ONE_SIGNAL_HAND) || (defined(__NETWARE__) && defined(SIGNALS_DONT_BREAK_READ)) extern "C" pthread_handler_decl(kill_server_thread,arg __attribute__((unused))) { - SHUTDOWN_THD; my_thread_init(); // Initialize new thread kill_server(0); my_thread_end(); // Normally never reached @@ -911,6 +907,7 @@ void clean_up(bool print_message) if (use_slave_mask) bitmap_free(&slave_error_mask); #endif + my_tz_free(); #ifndef NO_EMBEDDED_ACCESS_CHECKS acl_free(1); grant_free(); @@ -1716,7 +1713,6 @@ static void init_signals(void) signal(SIGALRM, SIG_IGN); signal(SIGBREAK,SIG_IGN); signal_thread = pthread_self(); - SIGNAL_THD; } static void start_signal_handler(void) @@ -2116,7 +2112,6 @@ int uname(struct utsname *a) extern "C" pthread_handler_decl(handle_shutdown,arg) { MSG msg; - SHUTDOWN_THD; my_thread_init(); /* this call should create the message queue for this thread */ @@ -2145,7 +2140,6 @@ int STDCALL handle_kill(ulong ctrl_type) #ifdef OS2 extern "C" pthread_handler_decl(handle_shutdown,arg) { - SHUTDOWN_THD; my_thread_init(); // wait semaphore @@ -2270,9 +2264,17 @@ static int init_common_variables(const char *conf_file_name, int argc, { struct tm tm_tmp; localtime_r(&start_time,&tm_tmp); - strmov(time_zone,tzname[tm_tmp.tm_isdst != 0 ? 1 : 0]); + strmov(system_time_zone, tzname[tm_tmp.tm_isdst != 0 ? 1 : 0]); } #endif + /* + We set SYSTEM time zone as reasonable default and + also for failure of my_tz_init() and bootstrap mode. + If user explicitly set time zone with --default-time-zone + option we will change this value in my_tz_init(). + */ + global_system_variables.time_zone= my_tz_SYSTEM; + /* Init mutexes for the global MYSQL_LOG objects. @@ -2810,7 +2812,8 @@ we force server id to 2, but this MySQL server will not act as a slave."); */ error_handler_hook = my_message_sql; start_signal_handler(); // Creates pidfile - if (acl_init((THD *)0, opt_noacl)) + if (acl_init((THD *)0, opt_noacl) || + my_tz_init((THD *)0, default_tz_name, opt_bootstrap)) { abort_loop=1; select_thread_in_use=0; @@ -3897,7 +3900,8 @@ enum options_mysqld OPT_DATE_FORMAT, OPT_TIME_FORMAT, OPT_DATETIME_FORMAT, - OPT_LOG_QUERIES_NOT_USING_INDEXES + OPT_LOG_QUERIES_NOT_USING_INDEXES, + OPT_DEFAULT_TIME_ZONE }; @@ -4010,6 +4014,9 @@ Disable with --skip-bdb (will save memory).", {"default-table-type", OPT_STORAGE_ENGINE, "(deprecated) Use default-storage-engine.", 0, 0, 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, + 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, {"delay-key-write", OPT_DELAY_KEY_WRITE, "Type of DELAY_KEY_WRITE.", 0,0,0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, {"delay-key-write-for-all-tables", OPT_DELAY_KEY_WRITE_ALL, @@ -5466,7 +5473,7 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), mysql_data_home= mysql_real_data_home; break; case 'u': - if (!mysqld_user) + if (!mysqld_user || !strcmp(mysqld_user, argument)) mysqld_user= argument; else fprintf(stderr, "Warning: Ignoring user change to '%s' because the user was set to '%s' earlier on the command line\n", argument, mysqld_user); diff --git a/sql/net_serv.cc b/sql/net_serv.cc index a0f7a779894..c2da47b480e 100644 --- a/sql/net_serv.cc +++ b/sql/net_serv.cc @@ -284,7 +284,9 @@ my_net_write(NET *net,const char *packet,ulong len) buff[3]= (uchar) net->pkt_nr++; if (net_write_buff(net,(char*) buff,NET_HEADER_SIZE)) return 1; +#ifndef DEBUG_DATA_PACKETS DBUG_DUMP("packet_header",(char*) buff,NET_HEADER_SIZE); +#endif return test(net_write_buff(net,packet,len)); } @@ -394,6 +396,9 @@ net_write_buff(NET *net,const char *packet,ulong len) else left_length= (ulong) (net->buff_end - net->write_pos); +#ifdef DEBUG_DATA_PACKETS + DBUG_DUMP("data", packet, len); +#endif if (len > left_length) { if (net->write_pos != net->buff) @@ -776,6 +781,8 @@ my_real_read(NET *net, ulong *complen) if (i == 0) { /* First parts is packet length */ ulong helping; + DBUG_DUMP("packet_header",(char*) net->buff+net->where_b, + NET_HEADER_SIZE); if (net->buff[net->where_b + 3] != (uchar) net->pkt_nr) { if (net->buff[net->where_b] != (uchar) 255) @@ -784,7 +791,6 @@ my_real_read(NET *net, ulong *complen) ("Packets out of order (Found: %d, expected %u)", (int) net->buff[net->where_b + 3], net->pkt_nr)); - DBUG_DUMP("packet_header",(char*) net->buff+net->where_b, 4); #ifdef EXTRA_DEBUG fprintf(stderr,"Packets out of order (Found: %d, expected %d)\n", (int) net->buff[net->where_b + 3], @@ -841,6 +847,10 @@ end: vio_blocking(net->vio, net_blocking, &old_mode); } net->reading_or_writing=0; +#ifdef DEBUG_DATA_PACKETS + if (len != packet_error) + DBUG_DUMP("data",(char*) net->buff+net->where_b, len); +#endif return(len); } diff --git a/sql/opt_range.cc b/sql/opt_range.cc index a5d2450e551..41ba09a3e70 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -1561,7 +1561,7 @@ key_or(SEL_ARG *key1,SEL_ARG *key2) { swap_variables(SEL_ARG *,key1,key2); } - else if (!(key1=key1->clone_tree())) + if (key1->use_count > 0 || !(key1=key1->clone_tree())) return 0; // OOM } @@ -1630,10 +1630,10 @@ key_or(SEL_ARG *key1,SEL_ARG *key2) SEL_ARG *next=key2->next; // Keys are not overlapping if (key2_shared) { - SEL_ARG *tmp= new SEL_ARG(*key2); // Must make copy - if (!tmp) + SEL_ARG *cpy= new SEL_ARG(*key2); // Must make copy + if (!cpy) return 0; // OOM - key1=key1->insert(tmp); + key1=key1->insert(cpy); key2->increment_use_count(key1->use_count+1); } else @@ -1869,8 +1869,17 @@ SEL_ARG::find_range(SEL_ARG *key) /* -** Remove a element from the tree -** This also frees all sub trees that is used by the element + Remove a element from the tree + + SYNOPSIS + tree_delete() + key Key that is to be deleted from tree (this) + + NOTE + This also frees all sub trees that is used by the element + + RETURN + root of new tree (with key deleted) */ SEL_ARG * @@ -1878,7 +1887,10 @@ SEL_ARG::tree_delete(SEL_ARG *key) { enum leaf_color remove_color; SEL_ARG *root,*nod,**par,*fix_par; - root=this; this->parent= 0; + DBUG_ENTER("tree_delete"); + + root=this; + this->parent= 0; /* Unlink from list */ if (key->prev) @@ -1925,7 +1937,7 @@ SEL_ARG::tree_delete(SEL_ARG *key) } if (root == &null_element) - return 0; // Maybe root later + DBUG_RETURN(0); // Maybe root later if (remove_color == BLACK) root=rb_delete_fixup(root,nod,fix_par); test_rb_tree(root,root->parent); @@ -1933,7 +1945,7 @@ SEL_ARG::tree_delete(SEL_ARG *key) root->use_count=this->use_count; // Fix root counters root->elements=this->elements-1; root->maybe_flag=this->maybe_flag; - return root; + DBUG_RETURN(root); } diff --git a/sql/set_var.cc b/sql/set_var.cc index 590b550ac3a..aa45afc3c30 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -441,6 +441,7 @@ static sys_var_thd_ulong sys_default_week_format("default_week_format", sys_var_thd_ulong sys_group_concat_max_len("group_concat_max_len", &SV::group_concat_max_len); +sys_var_thd_time_zone sys_time_zone("time_zone"); /* Read only variables */ @@ -586,6 +587,7 @@ sys_var *sys_variables[]= &sys_thread_cache_size, &sys_time_format, &sys_timestamp, + &sys_time_zone, &sys_tmp_table_size, &sys_trans_alloc_block_size, &sys_trans_prealloc_size, @@ -809,8 +811,9 @@ struct show_var_st init_vars[]= { {"thread_stack", (char*) &thread_stack, SHOW_LONG}, {sys_time_format.name, (char*) &sys_time_format, SHOW_SYS}, #ifdef HAVE_TZNAME - {"timezone", time_zone, SHOW_CHAR}, + {"system_time_zone", system_time_zone, SHOW_CHAR}, #endif + {"time_zone", (char*) &sys_time_zone, SHOW_SYS}, {sys_tmp_table_size.name, (char*) &sys_tmp_table_size, SHOW_SYS}, {"tmpdir", (char*) &opt_mysql_tmpdir, SHOW_CHAR_PTR}, {sys_trans_alloc_block_size.name, (char*) &sys_trans_alloc_block_size, @@ -2352,6 +2355,77 @@ bool sys_var_rand_seed2::update(THD *thd, set_var *var) } +bool sys_var_thd_time_zone::check(THD *thd, set_var *var) +{ + char buff[MAX_TIME_ZONE_NAME_LENGTH]; + String str(buff, sizeof(buff), &my_charset_latin1); + String *res= var->value->val_str(&str); + +#if defined(HAVE_REPLICATION) && (MYSQL_VERSION_ID < 50000) + if ((var->type == OPT_GLOBAL) && + (mysql_bin_log.is_open() || + active_mi->slave_running || active_mi->rli.slave_running)) + { + my_printf_error(0, "Binary logging and replication forbid changing " + "of the global server time zone", MYF(0)); + return 1; + } +#endif + + if (!(var->save_result.time_zone= my_tz_find(thd, res))) + { + my_error(ER_UNKNOWN_TIME_ZONE, MYF(0), res ? res->c_ptr() : "NULL"); + return 1; + } + return 0; +} + + +bool sys_var_thd_time_zone::update(THD *thd, set_var *var) +{ + /* We are using Time_zone object found during check() phase */ + *get_tz_ptr(thd,var->type)= var->save_result.time_zone; + return 0; +} + + +byte *sys_var_thd_time_zone::value_ptr(THD *thd, enum_var_type type, + LEX_STRING *base) +{ + /* + We can use ptr() instead of c_ptr() here because String contaning + time zone name is guaranteed to be zero ended. + */ + return (byte *)((*get_tz_ptr(thd,type))->get_name()->ptr()); +} + + +Time_zone** sys_var_thd_time_zone::get_tz_ptr(THD *thd, + enum_var_type type) +{ + if (type == OPT_GLOBAL) + return &global_system_variables.time_zone; + else + return &thd->variables.time_zone; +} + + +void sys_var_thd_time_zone::set_default(THD *thd, enum_var_type type) +{ + if (type == OPT_GLOBAL) + { + if (default_tz_name) + { + String str(default_tz_name, &my_charset_latin1); + global_system_variables.time_zone= my_tz_find(thd, &str); + } + else + global_system_variables.time_zone= my_tz_SYSTEM; + } + else + thd->variables.time_zone= global_system_variables.time_zone; +} + /* Functions to update thd->options bits */ diff --git a/sql/set_var.h b/sql/set_var.h index 9bed6f01dcc..c2b4ca34b2d 100644 --- a/sql/set_var.h +++ b/sql/set_var.h @@ -716,6 +716,29 @@ public: SHOW_TYPE type() { return show_type; } }; +class sys_var_thd_time_zone :public sys_var_thd +{ +public: + sys_var_thd_time_zone(const char *name_arg): + sys_var_thd(name_arg) + { +#if MYSQL_VERSION_ID < 50000 + no_support_one_shot= 0; +#endif + } + bool check(THD *thd, set_var *var); + SHOW_TYPE type() { return SHOW_CHAR; } + bool check_update_type(Item_result type) + { + return type != STRING_RESULT; /* Only accept strings */ + } + bool check_default(enum_var_type type) { return 0; } + bool update(THD *thd, set_var *var); + byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base); + virtual void set_default(THD *thd, enum_var_type type); + Time_zone **get_tz_ptr(THD *thd, enum_var_type type); +}; + /**************************************************************************** Classes for parsing of the SET command ****************************************************************************/ @@ -749,6 +772,7 @@ public: ulong ulong_value; ulonglong ulonglong_value; DATE_TIME_FORMAT *date_time_format; + Time_zone *time_zone; } save_result; LEX_STRING base; /* for structs */ diff --git a/sql/share/czech/errmsg.txt b/sql/share/czech/errmsg.txt index 5351cddfa51..2d377929229 100644 --- a/sql/share/czech/errmsg.txt +++ b/sql/share/czech/errmsg.txt @@ -174,7 +174,7 @@ character-set=latin2 "Zji-B¹tìn timeout pøi ètení komunikaèního packetu", "Zji-B¹tìna chyba pøi zápisu komunikaèního packetu", "Zji-B¹tìn timeout pøi zápisu komunikaèního packetu", -"V-Býsledný øetìzec je del¹í ne¾ max_allowed_packet", +"V-Býsledný øetìzec je del¹í ne¾ 'max_allowed_packet'", "Typ pou-B¾ité tabulky nepodporuje BLOB/TEXT sloupce", "Typ pou-B¾ité tabulky nepodporuje AUTO_INCREMENT sloupce", "INSERT DELAYED nen-Bí mo¾no s tabulkou '%-.64s' pou¾ít, proto¾e je zamèená pomocí LOCK TABLES", @@ -310,3 +310,5 @@ character-set=latin2 "This command is not supported in the prepared statement protocol yet", "Got error %d '%-.100s' from %s", "Got temporary error %d '%-.100s' from %s", +"Unknown or incorrect time zone: '%-.64s'", +"Invalid TIMESTAMP value in column '%s' at row %ld", diff --git a/sql/share/danish/errmsg.txt b/sql/share/danish/errmsg.txt index 016c4955e3a..af7b8263e6b 100644 --- a/sql/share/danish/errmsg.txt +++ b/sql/share/danish/errmsg.txt @@ -168,7 +168,7 @@ character-set=latin1 "Timeout-fejl ved læsning af kommunukations-pakker (communication packets)", "Fik fejlmeddelelse ved skrivning af kommunukations-pakker (communication packets)", "Timeout-fejl ved skrivning af kommunukations-pakker (communication packets)", -"Strengen med resultater er større end max_allowed_packet", +"Strengen med resultater er større end 'max_allowed_packet'", "Denne tabeltype understøtter ikke brug af BLOB og TEXT kolonner", "Denne tabeltype understøtter ikke brug af AUTO_INCREMENT kolonner", "INSERT DELAYED kan ikke bruges med tabellen '%-.64s', fordi tabellen er låst med LOCK TABLES", @@ -304,3 +304,5 @@ character-set=latin1 "This command is not supported in the prepared statement protocol yet", "Modtog fejl %d '%-.100s' fra %s", "Modtog temporary fejl %d '%-.100s' fra %s", +"Unknown or incorrect time zone: '%-.64s'", +"Invalid TIMESTAMP value in column '%s' at row %ld", diff --git a/sql/share/dutch/errmsg.txt b/sql/share/dutch/errmsg.txt index 7aabb2c9b39..aa20996680e 100644 --- a/sql/share/dutch/errmsg.txt +++ b/sql/share/dutch/errmsg.txt @@ -176,7 +176,7 @@ character-set=latin1 "Timeout bij het lezen van communicatiepakketten", "Fout bij het schrijven van communicatiepakketten", "Timeout bij het schrijven van communicatiepakketten", -"Resultaat string is langer dan max_allowed_packet", +"Resultaat string is langer dan 'max_allowed_packet'", "Het gebruikte tabel type ondersteunt geen BLOB/TEXT kolommen", "Het gebruikte tabel type ondersteunt geen AUTO_INCREMENT kolommen", "INSERT DELAYED kan niet worden gebruikt bij table '%-.64s', vanwege een 'lock met LOCK TABLES", @@ -312,3 +312,5 @@ character-set=latin1 "This command is not supported in the prepared statement protocol yet", "Got error %d '%-.100s' from %s", "Got temporary error %d '%-.100s' from %s", +"Unknown or incorrect time zone: '%-.64s'", +"Invalid TIMESTAMP value in column '%s' at row %ld", diff --git a/sql/share/english/errmsg.txt b/sql/share/english/errmsg.txt index 26118ebc164..b5a7f7962cf 100644 --- a/sql/share/english/errmsg.txt +++ b/sql/share/english/errmsg.txt @@ -82,19 +82,19 @@ character-set=latin1 "%s: ready for connections.\nVersion: '%s' socket: '%s' port: %d\n", "%s: Normal shutdown\n", "%s: Got signal %d. Aborting!\n", -"%s: Shutdown Complete\n", +"%s: Shutdown complete\n", "%s: Forcing close of thread %ld user: '%-.32s'\n", "Can't create IP socket", -"Table '%-.64s' has no index like the one used in CREATE INDEX. Recreate the table", +"Table '%-.64s' has no index like the one used in CREATE INDEX; recreate the table", "Field separator argument is not what is expected; check the manual", "You can't use fixed rowlength with BLOBs; please use 'fields terminated by'", "The file '%-.64s' must be in the database directory or be readable by all", "File '%-.80s' already exists", "Records: %ld Deleted: %ld Skipped: %ld Warnings: %ld", "Records: %ld Duplicates: %ld", -"Incorrect sub part key. The used key part isn't a string, the used length is longer than the key part or the storage engine doesn't support unique sub keys", -"You can't delete all columns with ALTER TABLE. Use DROP TABLE instead", -"Can't DROP '%-.64s'. Check that column/key exists", +"Incorrect sub part key; the used key part isn't a string, the used length is longer than the key part, or the storage engine doesn't support unique sub keys", +"You can't delete all columns with ALTER TABLE; use DROP TABLE instead", +"Can't DROP '%-.64s'; check that column/key exists", "Records: %ld Duplicates: %ld Warnings: %ld", "You can't specify target table '%-.64s' for update in FROM clause", "Unknown thread id: %lu", @@ -156,7 +156,7 @@ character-set=latin1 "Delayed insert thread couldn't get requested lock for table %-.64s", "Too many delayed threads in use", "Aborted connection %ld to db: '%-.64s' user: '%-.32s' (%-.64s)", -"Got a packet bigger than 'max_allowed_packet'", +"Got a packet bigger than 'max_allowed_packet' bytes", "Got a read error from the connection pipe", "Got an error from fcntl()", "Got packets out of order", @@ -165,7 +165,7 @@ character-set=latin1 "Got timeout reading communication packets", "Got an error writing communication packets", "Got timeout writing communication packets", -"Result string is longer than max_allowed_packet", +"Result string is longer than 'max_allowed_packet' bytes", "The used table type doesn't support BLOB/TEXT columns", "The used table type doesn't support AUTO_INCREMENT columns", "INSERT DELAYED can't be used with table '%-.64s' because it is locked with LOCK TABLES", @@ -301,3 +301,5 @@ character-set=latin1 "This command is not supported in the prepared statement protocol yet", "Got error %d '%-.100s' from %s", "Got temporary error %d '%-.100s' from %s", +"Unknown or incorrect time zone: '%-.64s'", +"Invalid TIMESTAMP value in column '%s' at row %ld", diff --git a/sql/share/estonian/errmsg.txt b/sql/share/estonian/errmsg.txt index c6fc4987fb8..0cc6e06ab26 100644 --- a/sql/share/estonian/errmsg.txt +++ b/sql/share/estonian/errmsg.txt @@ -306,3 +306,5 @@ character-set=latin7 "This command is not supported in the prepared statement protocol yet", "Got error %d '%-.100s' from %s", "Got temporary error %d '%-.100s' from %s", +"Unknown or incorrect time zone: '%-.64s'", +"Invalid TIMESTAMP value in column '%s' at row %ld", diff --git a/sql/share/french/errmsg.txt b/sql/share/french/errmsg.txt index e38ad1bd77e..2e23db62ddb 100644 --- a/sql/share/french/errmsg.txt +++ b/sql/share/french/errmsg.txt @@ -165,7 +165,7 @@ character-set=latin1 "Timeout en lecture des paquets reçus", "Erreur d'écriture des paquets envoyés", "Timeout d'écriture des paquets envoyés", -"La chaîne résultat est plus grande que max_allowed_packet", +"La chaîne résultat est plus grande que 'max_allowed_packet'", "Ce type de table ne supporte pas les colonnes BLOB/TEXT", "Ce type de table ne supporte pas les colonnes AUTO_INCREMENT", "INSERT DELAYED ne peut être utilisé avec la table '%-.64s', car elle est verrouée avec LOCK TABLES", @@ -301,3 +301,5 @@ character-set=latin1 "This command is not supported in the prepared statement protocol yet", "Got error %d '%-.100s' from %s", "Got temporary error %d '%-.100s' from %s", +"Unknown or incorrect time zone: '%-.64s'", +"Invalid TIMESTAMP value in column '%s' at row %ld", diff --git a/sql/share/german/errmsg.txt b/sql/share/german/errmsg.txt index 71cfcfff7fb..c63162c84f6 100644 --- a/sql/share/german/errmsg.txt +++ b/sql/share/german/errmsg.txt @@ -177,7 +177,7 @@ character-set=latin1 "Zeitüberschreitung beim Lesen eines Kommunikationspakets", "Fehler beim Schreiben eines Kommunikationspakets", "Zeitüberschreitung beim Schreiben eines Kommunikationspakets", -"Ergebnis ist länger als max_allowed_packet", +"Ergebnis ist länger als 'max_allowed_packet'", "Der verwendete Tabellentyp unterstützt keine BLOB- und TEXT-Spalten", "Der verwendete Tabellentyp unterstützt keine AUTO_INCREMENT-Spalten", "INSERT DELAYED kann nicht auf Tabelle '%-.64s' angewendet werden, da diese mit LOCK TABLES gesperrt ist", @@ -313,3 +313,5 @@ character-set=latin1 "This command is not supported in the prepared statement protocol yet", "Got error %d '%-.100s' from %s", "Got temporary error %d '%-.100s' from %s", +"Unknown or incorrect time zone: '%-.64s'", +"Invalid TIMESTAMP value in column '%s' at row %ld", diff --git a/sql/share/greek/errmsg.txt b/sql/share/greek/errmsg.txt index 6890a415fee..fa94b0f5107 100644 --- a/sql/share/greek/errmsg.txt +++ b/sql/share/greek/errmsg.txt @@ -156,7 +156,7 @@ character-set=greek "Delayed insert thread couldn't get requested lock for table %-.64s", "Too many delayed threads in use", "Aborted connection %ld to db: '%-.64s' user: '%-32s' (%-.64s)", -"Got a packet bigger than 'max_allowed_packet'", +"Got a packet bigger than 'max_allowed_packet' bytes", "Got a read error from the connection pipe", "Got an error from fcntl()", "Got packets out of order", @@ -165,7 +165,7 @@ character-set=greek "Got timeout reading communication packets", "Got an error writing communication packets", "Got timeout writing communication packets", -"Result string is longer than max_allowed_packet", +"Result string is longer than 'max_allowed_packet' bytes", "The used table type doesn't support BLOB/TEXT columns", "The used table type doesn't support AUTO_INCREMENT columns", "INSERT DELAYED can't be used with table '%-.64s', because it is locked with LOCK TABLES", @@ -301,3 +301,5 @@ character-set=greek "This command is not supported in the prepared statement protocol yet", "Got error %d '%-.100s' from %s", "Got temporary error %d '%-.100s' from %s", +"Unknown or incorrect time zone: '%-.64s'", +"Invalid TIMESTAMP value in column '%s' at row %ld", diff --git a/sql/share/hungarian/errmsg.txt b/sql/share/hungarian/errmsg.txt index 77589200628..56fae82c438 100644 --- a/sql/share/hungarian/errmsg.txt +++ b/sql/share/hungarian/errmsg.txt @@ -167,7 +167,7 @@ character-set=latin2 "Idotullepes a kommunikacios adatcsomagok olvasasa soran", "Hiba a kommunikacios csomagok irasa soran", "Idotullepes a kommunikacios csomagok irasa soran", -"Ez eredmeny sztring nagyobb, mint a lehetseges maximum: max_allowed_packet", +"Ez eredmeny sztring nagyobb, mint a lehetseges maximum: 'max_allowed_packet'", "A hasznalt tabla tipus nem tamogatja a BLOB/TEXT mezoket", "A hasznalt tabla tipus nem tamogatja az AUTO_INCREMENT tipusu mezoket", "Az INSERT DELAYED nem hasznalhato a '%-.64s' tablahoz, mert a tabla zarolt (LOCK TABLES)", @@ -303,3 +303,5 @@ character-set=latin2 "This command is not supported in the prepared statement protocol yet", "Got error %d '%-.100s' from %s", "Got temporary error %d '%-.100s' from %s", +"Unknown or incorrect time zone: '%-.64s'", +"Invalid TIMESTAMP value in column '%s' at row %ld", diff --git a/sql/share/italian/errmsg.txt b/sql/share/italian/errmsg.txt index 473cad94541..31768f172b4 100644 --- a/sql/share/italian/errmsg.txt +++ b/sql/share/italian/errmsg.txt @@ -165,7 +165,7 @@ character-set=latin1 "Rilevato un timeout ricevendo i pacchetti di comunicazione", "Rilevato un errore inviando i pacchetti di comunicazione", "Rilevato un timeout inviando i pacchetti di comunicazione", -"La stringa di risposta e` piu` lunga di max_allowed_packet", +"La stringa di risposta e` piu` lunga di 'max_allowed_packet'", "Il tipo di tabella usata non supporta colonne di tipo BLOB/TEXT", "Il tipo di tabella usata non supporta colonne di tipo AUTO_INCREMENT", "L'inserimento ritardato (INSERT DELAYED) non puo` essere usato con la tabella '%-.64s', perche` soggetta a lock da 'LOCK TABLES'", @@ -301,3 +301,5 @@ character-set=latin1 "This command is not supported in the prepared statement protocol yet", "Got error %d '%-.100s' from %s", "Got temporary error %d '%-.100s' from %s", +"Unknown or incorrect time zone: '%-.64s'", +"Invalid TIMESTAMP value in column '%s' at row %ld", diff --git a/sql/share/japanese/errmsg.txt b/sql/share/japanese/errmsg.txt index 35ac3594a67..4385f25c991 100644 --- a/sql/share/japanese/errmsg.txt +++ b/sql/share/japanese/errmsg.txt @@ -94,7 +94,7 @@ character-set=ujis "File '%-.64s' ¤Ï´û¤Ë¸ºß¤·¤Þ¤¹", "¥ì¥³¡¼¥É¿ô: %ld ºï½ü: %ld Skipped: %ld Warnings: %ld", "¥ì¥³¡¼¥É¿ô: %ld ½ÅÊ£: %ld", -"Incorrect sub part key. The used key part isn't a string or the used length is longer than the key part", +"Incorrect sub part key; the used key part isn't a string or the used length is longer than the key part", "ALTER TABLE ¤ÇÁ´¤Æ¤Î column ¤Ïºï½ü¤Ç¤¤Þ¤»¤ó. DROP TABLE ¤ò»ÈÍѤ·¤Æ¤¯¤À¤µ¤¤", "'%-.64s' ¤òÇË´þ¤Ç¤¤Þ¤»¤ó¤Ç¤·¤¿; check that column/key exists", "¥ì¥³¡¼¥É¿ô: %ld ½ÅÊ£¿ô: %ld Warnings: %ld", @@ -158,7 +158,7 @@ character-set=ujis "Delayed insert thread couldn't get requested lock for table %-.64s", "Too many delayed threads in use", "Aborted connection %ld to db: '%-.64s' user: '%-.64s' (%s)", -"Got a packet bigger than 'max_allowed_packet'", +"Got a packet bigger than 'max_allowed_packet' bytes", "Got a read error from the connection pipe", "Got an error from fcntl()", "Got packets out of order", @@ -167,7 +167,7 @@ character-set=ujis "Got timeout reading communication packets", "Got an error writing communication packets", "Got timeout writing communication packets", -"Result string is longer than max_allowed_packet", +"Result string is longer than 'max_allowed_packet' bytes", "The used table type doesn't support BLOB/TEXT columns", "The used table type doesn't support AUTO_INCREMENT columns", "INSERT DELAYED can't be used with table '%-.64s', because it is locked with LOCK TABLES", @@ -303,3 +303,5 @@ character-set=ujis "This command is not supported in the prepared statement protocol yet", "Got NDB error %d '%-.100s'", "Got temporary NDB error %d '%-.100s'", +"Unknown or incorrect time zone: '%-.64s'", +"Invalid TIMESTAMP value in column '%s' at row %ld", diff --git a/sql/share/korean/errmsg.txt b/sql/share/korean/errmsg.txt index 9c57ec4642f..a6e84fad01e 100644 --- a/sql/share/korean/errmsg.txt +++ b/sql/share/korean/errmsg.txt @@ -165,7 +165,7 @@ character-set=euckr "Åë½Å ÆÐŶÀ» Àд Áß timeoutÀÌ ¹ß»ýÇÏ¿´½À´Ï´Ù.", "Åë½Å ÆÐŶÀ» ±â·ÏÇÏ´Â Áß ¿À·ù°¡ ¹ß»ýÇÏ¿´½À´Ï´Ù.", "Åë½Å ÆÐÆÂÀ» ±â·ÏÇÏ´Â Áß timeoutÀÌ ¹ß»ýÇÏ¿´½À´Ï´Ù.", -"Result string is longer than max_allowed_packet", +"Result string is longer than 'max_allowed_packet' bytes", "The used table type doesn't support BLOB/TEXT columns", "The used table type doesn't support AUTO_INCREMENT columns", "INSERT DELAYED can't be used with table '%-.64s', because it is locked with LOCK TABLES", @@ -301,3 +301,5 @@ character-set=euckr "This command is not supported in the prepared statement protocol yet", "Got error %d '%-.100s' from %s", "Got temporary error %d '%-.100s' from %s", +"Unknown or incorrect time zone: '%-.64s'", +"Invalid TIMESTAMP value in column '%s' at row %ld", diff --git a/sql/share/norwegian-ny/errmsg.txt b/sql/share/norwegian-ny/errmsg.txt index 1b6e8985d6c..eaf7b3482ee 100644 --- a/sql/share/norwegian-ny/errmsg.txt +++ b/sql/share/norwegian-ny/errmsg.txt @@ -158,7 +158,7 @@ character-set=latin1 "Delayed insert thread couldn't get requested lock for table %-.64s", "Too many delayed threads in use", "Aborted connection %ld to db: '%-.64s' user: '%-.64s' (%s)", -"Got a packet bigger than 'max_allowed_packet'", +"Got a packet bigger than 'max_allowed_packet' bytes", "Got a read error from the connection pipe", "Got an error from fcntl()", "Got packets out of order", @@ -167,7 +167,7 @@ character-set=latin1 "Got timeout reading communication packets", "Got an error writing communication packets", "Got timeout writing communication packets", -"Result string is longer than max_allowed_packet", +"Result string is longer than 'max_allowed_packet' bytes", "The used table type doesn't support BLOB/TEXT columns", "The used table type doesn't support AUTO_INCREMENT columns", "INSERT DELAYED can't be used with table '%-.64s', because it is locked with LOCK TABLES", @@ -303,3 +303,5 @@ character-set=latin1 "This command is not supported in the prepared statement protocol yet", "Mottok feil %d '%-.100s' fra %s", "Mottok temporary feil %d '%-.100s' fra %s", +"Unknown or incorrect time zone: '%-.64s'", +"Invalid TIMESTAMP value in column '%s' at row %ld", diff --git a/sql/share/norwegian/errmsg.txt b/sql/share/norwegian/errmsg.txt index 35e28935b3d..692c10db58f 100644 --- a/sql/share/norwegian/errmsg.txt +++ b/sql/share/norwegian/errmsg.txt @@ -158,7 +158,7 @@ character-set=latin1 "Delayed insert thread couldn't get requested lock for table %-.64s", "Too many delayed threads in use", "Aborted connection %ld to db: '%-.64s' user: '%-.64s' (%s)", -"Got a packet bigger than 'max_allowed_packet'", +"Got a packet bigger than 'max_allowed_packet' bytes", "Got a read error from the connection pipe", "Got an error from fcntl()", "Got packets out of order", @@ -167,7 +167,7 @@ character-set=latin1 "Got timeout reading communication packets", "Got an error writing communication packets", "Got timeout writing communication packets", -"Result string is longer than max_allowed_packet", +"Result string is longer than 'max_allowed_packet' bytes", "The used table type doesn't support BLOB/TEXT columns", "The used table type doesn't support AUTO_INCREMENT columns", "INSERT DELAYED can't be used with table '%-.64s', because it is locked with LOCK TABLES", @@ -303,3 +303,5 @@ character-set=latin1 "This command is not supported in the prepared statement protocol yet", "Mottok feil %d '%-.100s' fa %s", "Mottok temporary feil %d '%-.100s' fra %s", +"Unknown or incorrect time zone: '%-.64s'", +"Invalid TIMESTAMP value in column '%s' at row %ld", diff --git a/sql/share/polish/errmsg.txt b/sql/share/polish/errmsg.txt index 15c9ec5b6f1..19f2c1c6983 100644 --- a/sql/share/polish/errmsg.txt +++ b/sql/share/polish/errmsg.txt @@ -160,7 +160,7 @@ character-set=latin2 "Delayed insert thread couldn't get requested lock for table %-.64s", "Too many delayed threads in use", "Aborted connection %ld to db: '%-.64s' user: '%-.64s' (%s)", -"Got a packet bigger than 'max_allowed_packet'", +"Got a packet bigger than 'max_allowed_packet' bytes", "Got a read error from the connection pipe", "Got an error from fcntl()", "Got packets out of order", @@ -169,7 +169,7 @@ character-set=latin2 "Got timeout reading communication packets", "Got an error writing communication packets", "Got timeout writing communication packets", -"Result string is longer than max_allowed_packet", +"Result string is longer than 'max_allowed_packet' bytes", "The used table type doesn't support BLOB/TEXT columns", "The used table type doesn't support AUTO_INCREMENT columns", "INSERT DELAYED can't be used with table '%-.64s', because it is locked with LOCK TABLES", @@ -305,3 +305,5 @@ character-set=latin2 "This command is not supported in the prepared statement protocol yet", "Got error %d '%-.100s' from %s", "Got temporary error %d '%-.100s' from %s", +"Unknown or incorrect time zone: '%-.64s'", +"Invalid TIMESTAMP value in column '%s' at row %ld", diff --git a/sql/share/portuguese/errmsg.txt b/sql/share/portuguese/errmsg.txt index be37e6758e6..c77d10d83de 100644 --- a/sql/share/portuguese/errmsg.txt +++ b/sql/share/portuguese/errmsg.txt @@ -94,7 +94,7 @@ character-set=latin1 "Registros: %ld - Deletados: %ld - Ignorados: %ld - Avisos: %ld", "Registros: %ld - Duplicados: %ld", "Sub parte da chave incorreta. A parte da chave usada não é uma 'string' ou o comprimento usado é maior que parte da chave ou o manipulador de tabelas não suporta sub chaves únicas", -"Você não pode deletar todas as colunas com ALTER TABLE. Use DROP TABLE em seu lugar", +"Você não pode deletar todas as colunas com ALTER TABLE; use DROP TABLE em seu lugar", "Não se pode fazer DROP '%-.64s'. Confira se esta coluna/chave existe", "Registros: %ld - Duplicados: %ld - Avisos: %ld", "You can't specify target table '%-.64s' for update in FROM clause", @@ -123,7 +123,7 @@ character-set=latin1 "Tabelas demais. O MySQL pode usar somente %d tabelas em uma junção (JOIN)", "Colunas demais", "Tamanho de linha grande demais. O máximo tamanho de linha, não contando BLOBs, é %d. Você tem que mudar alguns campos para BLOBs", -"Estouro da pilha do 'thread'. Usados %ld de uma pilha de %ld . Use 'mysqld -O thread_stack=#' para especificar uma pilha maior, se necessário", +"Estouro da pilha do 'thread'. Usados %ld de uma pilha de %ld. Use 'mysqld -O thread_stack=#' para especificar uma pilha maior, se necessário", "Dependência cruzada encontrada em junção externa (OUTER JOIN); examine as condições utilizadas nas cláusulas 'ON'", "Coluna '%-.64s' é usada com única (UNIQUE) ou índice (INDEX), mas não está definida como não-nula (NOT NULL)", "Não pode carregar a função '%-.64s'", @@ -302,3 +302,5 @@ character-set=latin1 "This command is not supported in the prepared statement protocol yet", "Got error %d '%-.100s' from %s", "Got temporary error %d '%-.100s' from %s", +"Unknown or incorrect time zone: '%-.64s'", +"Invalid TIMESTAMP value in column '%s' at row %ld", diff --git a/sql/share/romanian/errmsg.txt b/sql/share/romanian/errmsg.txt index de45607b37c..5ee4efd0063 100644 --- a/sql/share/romanian/errmsg.txt +++ b/sql/share/romanian/errmsg.txt @@ -169,7 +169,7 @@ character-set=latin2 "Timeout obtinut citind pachetele de comunicatie (communication packets)", "Eroare in scrierea pachetelor de comunicatie (communication packets)", "Timeout obtinut scriind pachetele de comunicatie (communication packets)", -"Sirul rezultat este mai lung decit max_allowed_packet", +"Sirul rezultat este mai lung decit 'max_allowed_packet'", "Tipul de tabela folosit nu suporta coloane de tip BLOB/TEXT", "Tipul de tabela folosit nu suporta coloane de tip AUTO_INCREMENT", "INSERT DELAYED nu poate fi folosit cu tabela '%-.64s', deoarece este locked folosing LOCK TABLES", @@ -305,3 +305,5 @@ character-set=latin2 "This command is not supported in the prepared statement protocol yet", "Got error %d '%-.100s' from %s", "Got temporary error %d '%-.100s' from %s", +"Unknown or incorrect time zone: '%-.64s'", +"Invalid TIMESTAMP value in column '%s' at row %ld", diff --git a/sql/share/russian/errmsg.txt b/sql/share/russian/errmsg.txt index 32da15409d0..20188723f6d 100644 --- a/sql/share/russian/errmsg.txt +++ b/sql/share/russian/errmsg.txt @@ -303,3 +303,5 @@ character-set=koi8r "This command is not supported in the prepared statement protocol yet", "Got error %d '%-.100s' from %s", "Got temporary error %d '%-.100s' from %s", +"Unknown or incorrect time zone: '%-.64s'", +"Invalid TIMESTAMP value in column '%s' at row %ld", diff --git a/sql/share/serbian/errmsg.txt b/sql/share/serbian/errmsg.txt index c143e65461b..cc822431464 100644 --- a/sql/share/serbian/errmsg.txt +++ b/sql/share/serbian/errmsg.txt @@ -307,3 +307,5 @@ character-set=cp1250 "This command is not supported in the prepared statement protocol yet", "Got error %d '%-.100s' from %s", "Got temporary error %d '%-.100s' from %s", +"Unknown or incorrect time zone: '%-.64s'", +"Invalid TIMESTAMP value in column '%s' at row %ld", diff --git a/sql/share/slovak/errmsg.txt b/sql/share/slovak/errmsg.txt index f80df0b659b..ee6aac5081b 100644 --- a/sql/share/slovak/errmsg.txt +++ b/sql/share/slovak/errmsg.txt @@ -100,8 +100,8 @@ character-set=latin2 "Súbor '%-.64s' u¾ existuje", "Záznamov: %ld Zmazaných: %ld Preskoèených: %ld Varovania: %ld", "Záznamov: %ld Opakovaných: %ld", -"Incorrect sub part key. The used key part isn't a string or the used length is longer than the key part", -"One nemô¾em zmaza» all fields with ALTER TABLE. Use DROP TABLE instead", +"Incorrect sub part key; the used key part isn't a string or the used length is longer than the key part", +"One nemô¾em zmaza» all fields with ALTER TABLE; use DROP TABLE instead", "Nemô¾em zru¹i» (DROP) '%-.64s'. Skontrolujte, èi neexistujú záznamy/kµúèe", "Záznamov: %ld Opakovaných: %ld Varovania: %ld", "You can't specify target table '%-.64s' for update in FROM clause", @@ -164,7 +164,7 @@ character-set=latin2 "Delayed insert thread couldn't get requested lock for table %-.64s", "Too many delayed threads in use", "Aborted connection %ld to db: '%-.64s' user: '%-.64s' (%s)", -"Got a packet bigger than 'max_allowed_packet'", +"Got a packet bigger than 'max_allowed_packet' bytes", "Got a read error from the connection pipe", "Got an error from fcntl()", "Got packets out of order", @@ -173,7 +173,7 @@ character-set=latin2 "Got timeout reading communication packets", "Got an error writing communication packets", "Got timeout writing communication packets", -"Result string is longer than max_allowed_packet", +"Result string is longer than 'max_allowed_packet' bytes", "The used table type doesn't support BLOB/TEXT columns", "The used table type doesn't support AUTO_INCREMENT columns", "INSERT DELAYED can't be used with table '%-.64s', because it is locked with LOCK TABLES", @@ -309,3 +309,5 @@ character-set=latin2 "This command is not supported in the prepared statement protocol yet", "Got error %d '%-.100s' from %s", "Got temporary error %d '%-.100s' from %s", +"Unknown or incorrect time zone: '%-.64s'", +"Invalid TIMESTAMP value in column '%s' at row %ld", diff --git a/sql/share/spanish/errmsg.txt b/sql/share/spanish/errmsg.txt index f01c7936610..483ec7068a2 100644 --- a/sql/share/spanish/errmsg.txt +++ b/sql/share/spanish/errmsg.txt @@ -167,7 +167,7 @@ character-set=latin1 "Obtenido timeout leyendo paquetes de comunicación", "Obtenido un error de escribiendo paquetes de comunicación", "Obtenido timeout escribiendo paquetes de comunicación", -"La string resultante es mayor que max_allowed_packet", +"La string resultante es mayor que 'max_allowed_packet'", "El tipo de tabla usada no permite soporte para columnas BLOB/TEXT", "El tipo de tabla usada no permite soporte para columnas AUTO_INCREMENT", "INSERT DELAYED no puede ser usado con tablas '%-.64s', porque esta bloqueada con LOCK TABLES", @@ -303,3 +303,5 @@ character-set=latin1 "This command is not supported in the prepared statement protocol yet", "Got error %d '%-.100s' from %s", "Got temporary error %d '%-.100s' from %s", +"Unknown or incorrect time zone: '%-.64s'", +"Invalid TIMESTAMP value in column '%s' at row %ld", diff --git a/sql/share/swedish/errmsg.txt b/sql/share/swedish/errmsg.txt index 4dd614ba2ea..d9f3adf92d4 100644 --- a/sql/share/swedish/errmsg.txt +++ b/sql/share/swedish/errmsg.txt @@ -165,7 +165,7 @@ character-set=latin1 "Fick 'timeout' vid läsning från klienten", "Fick ett fel vid skrivning till klienten", "Fick 'timeout' vid skrivning till klienten", -"Resultatsträngen är längre än max_allowed_packet", +"Resultatsträngen är längre än 'max_allowed_packet'", "Den använda tabelltypen kan inte hantera BLOB/TEXT-kolumner", "Den använda tabelltypen kan inte hantera AUTO_INCREMENT-kolumner", "INSERT DELAYED kan inte användas med tabell '%-.64s', emedan den är låst med LOCK TABLES", @@ -301,3 +301,5 @@ character-set=latin1 "This command is not supported in the prepared statement protocol yet", "Fick felkod %d '%-.100s' från %s", "Fick tilfällig felkod %d '%-.100s' från %s", +"Unknown or incorrect time zone: '%-.64s'", +"Invalid TIMESTAMP value in column '%s' at row %ld", diff --git a/sql/share/ukrainian/errmsg.txt b/sql/share/ukrainian/errmsg.txt index 9588d986ba3..acf6f5121e8 100644 --- a/sql/share/ukrainian/errmsg.txt +++ b/sql/share/ukrainian/errmsg.txt @@ -161,7 +161,7 @@ character-set=koi8u "ç¦ÌËÁ ÄÌÑ INSERT DELAYED ÎÅ ÍÏÖÅ ÏÔÒÉÍÁÔÉ ÂÌÏËÕ×ÁÎÎÑ ÄÌÑ ÔÁÂÌÉæ %-.64s", "úÁÂÁÇÁÔÏ ÚÁÔÒÉÍÁÎÉÈ Ç¦ÌÏË ×ÉËÏÒÉÓÔÏ×Õ¤ÔØÓÑ", "ðÅÒÅÒ×ÁÎÏ Ú'¤ÄÎÁÎÎÑ %ld ÄÏ ÂÁÚÉ ÄÁÎÎÉÈ: '%-.64s' ËÏÒÉÓÔÕ×ÁÞÁ: '%-.32s' (%-.64s)", -"ïÔÒÉÍÁÎÏ ÐÁËÅÔ Â¦ÌØÛÉÊ Î¦Ö max_allowed_packet", +"ïÔÒÉÍÁÎÏ ÐÁËÅÔ Â¦ÌØÛÉÊ Î¦Ö 'max_allowed_packet'", "ïÔÒÉÍÁÎÏ ÐÏÍÉÌËÕ ÞÉÔÁÎÎÑ Ú ËÏÍÕΦËÁæÊÎÏÇÏ ËÁÎÁÌÕ", "ïÔÒÉÍÁÎÏ ÐÏÍÉÌËËÕ ×¦Ä fcntl()", "ïÔÒÉÍÁÎÏ ÐÁËÅÔÉ Õ ÎÅÎÁÌÅÖÎÏÍÕ ÐÏÒÑÄËÕ", @@ -170,7 +170,7 @@ character-set=koi8u "ïÔÒÉÍÁÎÏ ÚÁÔÒÉÍËÕ ÞÉÔÁÎÎÑ ËÏÍÕΦËÁæÊÎÉÈ ÐÁËÅÔ¦×", "ïÔÒÉÍÁÎÏ ÐÏÍÉÌËÕ ÚÁÐÉÓÕ ËÏÍÕΦËÁæÊÎÉÈ ÐÁËÅÔ¦×", "ïÔÒÉÍÁÎÏ ÚÁÔÒÉÍËÕ ÚÁÐÉÓÕ ËÏÍÕΦËÁæÊÎÉÈ ÐÁËÅÔ¦×", -"óÔÒÏËÁ ÒÅÚÕÌØÔÁÔÕ ÄÏ×ÛÁ Î¦Ö max_allowed_packet", +"óÔÒÏËÁ ÒÅÚÕÌØÔÁÔÕ ÄÏ×ÛÁ Î¦Ö 'max_allowed_packet'", "÷ÉËÏÒÉÓÔÁÎÉÊ ÔÉÐ ÔÁÂÌÉæ ΊЦÄÔÒÉÍÕ¤ BLOB/TEXT ÓÔÏ×Âæ", "÷ÉËÏÒÉÓÔÁÎÉÊ ÔÉÐ ÔÁÂÌÉæ ΊЦÄÔÒÉÍÕ¤ AUTO_INCREMENT ÓÔÏ×Âæ", "INSERT DELAYED ÎÅ ÍÏÖÅ ÂÕÔÉ ×ÉËÏÒÉÓÔÁÎÏ Ú ÔÁÂÌÉÃÅÀ '%-.64s', ÔÏÍÕ ÝÏ §§ ÚÁÂÌÏËÏ×ÁÎÏ Ú LOCK TABLES", @@ -306,3 +306,5 @@ character-set=koi8u "This command is not supported in the prepared statement protocol yet", "Got error %d '%-.100s' from %s", "Got temporary error %d '%-.100s' from %s", +"Unknown or incorrect time zone: '%-.64s'", +"Invalid TIMESTAMP value in column '%s' at row %ld", diff --git a/sql/slave.cc b/sql/slave.cc index 1a59e5b2b5b..7e46fb81053 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -1248,7 +1248,30 @@ be equal for replication to work"; mysql_free_result(master_res); } - /* Add a timezones check here */ + /* + Perform analogous check for time zone. Theoretically we also should + perform check here to verify that SYSTEM time zones are the same on + slave and master, but we can't rely on value of @@system_time_zone + variable (it is time zone abbreviation) since it determined at start + time and so could differ for slave and master even if they are really + in the same system time zone. So we are omiting this check and just + relying on documentation. Also according to Monty there are many users + who are using replication between servers in various time zones. Hence + such check will broke everything for them. (And now everything will + work for them because by default both their master and slave will have + 'SYSTEM' time zone). + */ + if (!mysql_real_query(mysql, "SELECT @@GLOBAL.TIME_ZONE", 25) && + (master_res= mysql_store_result(mysql))) + { + if ((master_row= mysql_fetch_row(master_res)) && + strcmp(master_row[0], + global_system_variables.time_zone->get_name()->ptr())) + errmsg= "The slave I/O thread stops because master and slave have \ +different values for the TIME_ZONE global variable. The values must \ +be equal for replication to work"; + mysql_free_result(master_res); + } if (errmsg) { @@ -3539,7 +3562,6 @@ err: static int process_io_rotate(MASTER_INFO *mi, Rotate_log_event *rev) { - int return_val= 1; DBUG_ENTER("process_io_rotate"); safe_mutex_assert_owner(&mi->data_lock); diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 9d1eb7bc54d..d552429af4b 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -2830,7 +2830,7 @@ bool check_grant_all_columns(THD *thd, ulong want_access, TABLE *table) if (table->grant.version != grant_version) { table->grant.grant_table= - table_hash_search(thd->host,thd->ip,thd->db, + table_hash_search(thd->host, thd->ip, table->table_cache_key, thd->priv_user, table->real_name,0); /* purecov: inspected */ table->grant.version=grant_version; /* purecov: inspected */ @@ -2943,7 +2943,7 @@ ulong get_column_grant(THD *thd, TABLE_LIST *table, Field *field) if (table->grant.version != grant_version) { table->grant.grant_table= - table_hash_search(thd->host,thd->ip,thd->db, + table_hash_search(thd->host, thd->ip, table->db, thd->priv_user, table->real_name,0); /* purecov: inspected */ table->grant.version=grant_version; /* purecov: inspected */ diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 889c95125c5..f705b592e5a 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -1760,6 +1760,7 @@ TABLE *open_temporary_table(THD *thd, const char *path, const char *db, } tmp_table->reginfo.lock_type=TL_WRITE; // Simulate locked + tmp_table->in_use= thd; tmp_table->tmp_table = (tmp_table->file->has_transactions() ? TRANSACTIONAL_TMP_TABLE : TMP_TABLE); tmp_table->table_cache_key=(char*) (tmp_table+1); diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index b9fe61ac48a..5c6215e6fb9 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -786,6 +786,7 @@ void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used) flags.collation_connection_num= thd->variables.collation_connection->number; flags.limit= thd->variables.select_limit; + flags.time_zone= thd->variables.time_zone; STRUCT_LOCK(&structure_guard_mutex); if (query_cache_size == 0) @@ -972,6 +973,7 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length) UINT_MAX); flags.collation_connection_num= thd->variables.collation_connection->number; flags.limit= thd->variables.select_limit; + flags.time_zone= thd->variables.time_zone; memcpy((void *)(sql + (tot_length - QUERY_CACHE_FLAGS_SIZE)), &flags, QUERY_CACHE_FLAGS_SIZE); query_block = (Query_cache_block *) hash_search(&queries, (byte*) sql, @@ -3231,9 +3233,10 @@ void Query_cache::queries_dump() Query_cache_query_flags flags; memcpy(&flags, str+len, QUERY_CACHE_FLAGS_SIZE); str[len]= 0; // make zero ending DB name - DBUG_PRINT("qcache", ("F:%u C:%u L:%lu (%u) '%s' '%s'", + DBUG_PRINT("qcache", ("F:%u C:%u L:%lu T:'%s' (%u) '%s' '%s'", flags.client_long_flag, - flags.character_set_client_num, (ulong)flags.limit, + flags.character_set_client_num, + (ulong)flags.limit, flags.time_zone->get_name(), len, str, strend(str)+1)); DBUG_PRINT("qcache", ("-b- 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx", (ulong) block, (ulong) block->next, (ulong) block->prev, diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 704662fa4bf..7aa3bbbdd7b 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -157,12 +157,13 @@ bool foreign_key_prefix(Key *a, Key *b) THD::THD():user_time(0), current_statement(0), is_fatal_error(0), last_insert_id_used(0), - insert_id_used(0), rand_used(0), in_lock_tables(0), - global_read_lock(0), bootstrap(0) + insert_id_used(0), rand_used(0), time_zone_used(0), + in_lock_tables(0), global_read_lock(0), bootstrap(0) { host= user= priv_user= db= ip=0; host_or_ip= "connecting host"; - locked=killed=some_tables_deleted=no_errors=password= 0; + locked=some_tables_deleted=no_errors=password= 0; + killed=0; query_start_used= 0; count_cuted_fields= CHECK_FIELD_IGNORE; db_length= col_access= 0; diff --git a/sql/sql_class.h b/sql/sql_class.h index e9ad659a2cc..01387e28402 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -405,6 +405,8 @@ struct system_variables CHARSET_INFO *collation_database; CHARSET_INFO *collation_connection; + Time_zone *time_zone; + /* DATE, DATETIME and TIME formats */ DATE_TIME_FORMAT *date_format; DATE_TIME_FORMAT *datetime_format; @@ -559,7 +561,7 @@ public: { Statement *stmt; stmt= (Statement *) hash_search(&st_hash, (byte *) &id, sizeof(id)); - if (stmt->name.str) + if (stmt && stmt->name.str) return NULL; last_found_statement= stmt; } @@ -826,6 +828,7 @@ public: bool last_cuted_field; bool no_errors, password, is_fatal_error; bool query_start_used,last_insert_id_used,insert_id_used,rand_used; + bool time_zone_used; bool in_lock_tables,global_read_lock; bool query_error, bootstrap, cleanup_done; bool tmp_table_used; diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc index 2e2ad6786fc..01459d3fc7a 100644 --- a/sql/sql_derived.cc +++ b/sql/sql_derived.cc @@ -117,7 +117,6 @@ static int mysql_derived(THD *thd, LEX *lex, SELECT_LEX_UNIT *unit, select_union *derived_result; bool is_union= first_select->next_select() && first_select->next_select()->linkage == UNION_TYPE; - bool is_subsel= first_select->first_inner_unit() ? 1: 0; SELECT_LEX *save_current_select= lex->current_select; DBUG_ENTER("mysql_derived"); diff --git a/sql/sql_help.cc b/sql/sql_help.cc index 44293b8214f..c5a49cab3b5 100644 --- a/sql/sql_help.cc +++ b/sql/sql_help.cc @@ -626,7 +626,7 @@ SQL_SELECT *prepare_select_for_name(THD *thd, const char *mask, uint mlen, { Item *cond= new Item_func_like(new Item_field(pfname), new Item_string(mask,mlen,pfname->charset()), - (char*) "\\"); + new Item_string("\\",1,&my_charset_latin1)); if (thd->is_fatal_error) return 0; // OOM return prepare_simple_select(thd,cond,tables,table,error); diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 037dd99d3b6..f7f30b079b8 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -945,6 +945,10 @@ TABLE *delayed_insert::get_local_table(THD* client_thd) /* _rowid is not used with delayed insert */ copy->rowid_field=0; + + /* Adjust in_use for pointing to client thread */ + copy->in_use= client_thd; + return copy; /* Got fatal error */ diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 36e05b40c7d..54d9a09c1f3 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -175,8 +175,8 @@ enum tablespace_op_type e||+-------------------------+ || V| neighbor | V| unit1.1<+==================>unit1.2 unit2.1 - fake1.1 fake2.1 - select1.1.1 select 1.1.2 select1.2.1 select2.1.1 select2.1.2 + fake1.1 + select1.1.1 select 1.1.2 select1.2.1 select2.1.1 |^ || V| diff --git a/sql/sql_load.cc b/sql/sql_load.cc index f72fab9ea3a..167fb2daf8b 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -530,7 +530,7 @@ read_sep_field(THD *thd,COPY_INFO &info,TABLE *table, ((Field_timestamp*) field)->set_time(); else if (field != table->next_number_field) field->set_warning((uint) MYSQL_ERROR::WARN_LEVEL_WARN, - ER_WARN_NULL_TO_NOTNULL); + ER_WARN_NULL_TO_NOTNULL, 1); } continue; } diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 28e833b8421..2d8facfa63f 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1308,6 +1308,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, if (command != COM_STATISTICS && command != COM_PING) query_id++; thread_running++; + /* TODO: set thd->lex->sql_command to SQLCOM_END here */ VOID(pthread_mutex_unlock(&LOCK_thread_count)); thd->server_status&= @@ -1478,6 +1479,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, thd->query_length= length; thd->query= packet; thd->query_id= query_id++; + /* TODO: set thd->lex->sql_command to SQLCOM_END here */ VOID(pthread_mutex_unlock(&LOCK_thread_count)); #ifndef EMBEDDED_LIBRARY mysql_parse(thd, packet, length); @@ -1586,7 +1588,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, break; } mysql_log.write(thd,command,db); - mysql_rm_db(thd,alias,0,0); + mysql_rm_db(thd, (lower_case_table_names == 2 ? alias : db), 0, 0); break; } #ifndef EMBEDDED_LIBRARY @@ -1631,10 +1633,28 @@ bool dispatch_command(enum enum_server_command command, THD *thd, } #ifndef EMBEDDED_LIBRARY case COM_SHUTDOWN: + { statistic_increment(com_other,&LOCK_status); if (check_global_access(thd,SHUTDOWN_ACL)) break; /* purecov: inspected */ - DBUG_PRINT("quit",("Got shutdown command")); + /* + If the client is < 4.1.3, it is going to send us no argument; then + packet_length is 1, packet[0] is the end 0 of the packet. Note that + SHUTDOWN_DEFAULT is 0. If client is >= 4.1.3, the shutdown level is in + packet[0]. + */ + enum enum_shutdown_level level= + (enum enum_shutdown_level) (uchar) packet[0]; + DBUG_PRINT("quit",("Got shutdown command for level %u", level)); + if (level == SHUTDOWN_DEFAULT) + level= SHUTDOWN_WAIT_ALL_BUFFERS; // soon default will be configurable + else if (level != SHUTDOWN_WAIT_ALL_BUFFERS) + { + my_error(ER_NOT_SUPPORTED_YET, MYF(0), "this shutdown level"); + send_error(thd); + break; + } + DBUG_PRINT("quit",("Got shutdown command for level %u", level)); mysql_log.write(thd,command,NullS); send_eof(thd); #ifdef __WIN__ @@ -1650,6 +1670,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, kill_mysql(); error=TRUE; break; + } #endif case COM_STATISTICS: { @@ -1983,7 +2004,6 @@ mysql_execute_command(THD *thd) { /* This is PREPARE stmt FROM @var. */ String str; - String *pstr; CHARSET_INFO *to_cs= thd->variables.collation_connection; bool need_conversion; user_var_entry *entry; @@ -2334,7 +2354,15 @@ mysql_execute_command(THD *thd) lex->create_list, lex->key_list, select_lex->item_list,lex->duplicates))) + { + /* + CREATE from SELECT give its SELECT_LEX for SELECT, + and item_list belong to SELECT + */ + select_lex->resolve_mode= SELECT_LEX::SELECT_MODE; res=handle_select(thd, lex, result); + select_lex->resolve_mode= SELECT_LEX::NOMATTER_MODE; + } //reset for PS lex->create_list.empty(); lex->key_list.empty(); @@ -2593,7 +2621,6 @@ unsent_create_error: case SQLCOM_OPTIMIZE: { - HA_CREATE_INFO create_info; if (check_db_used(thd,tables) || check_table_access(thd,SELECT_ACL | INSERT_ACL, tables,0)) goto error; /* purecov: inspected */ @@ -2685,7 +2712,11 @@ unsent_create_error: lex->duplicates))) /* Skip first table, which is the table we are inserting in */ lex->select_lex.table_list.first= (byte*) first_local_table->next; - lex->select_lex.resolve_mode= SELECT_LEX::NOMATTER_MODE; + /* + insert/replace from SELECT give its SELECT_LEX for SELECT, + and item_list belong to SELECT + */ + lex->select_lex.resolve_mode= SELECT_LEX::SELECT_MODE; res=handle_select(thd,lex,result); /* revert changes for SP */ lex->select_lex.table_list.first= (byte*) first_local_table; @@ -3114,7 +3145,8 @@ purposes internal to the MySQL server", MYF(0)); send_error(thd,ER_LOCK_OR_ACTIVE_TRANSACTION); goto error; } - res=mysql_rm_db(thd,alias,lex->drop_if_exists,0); + res=mysql_rm_db(thd, (lower_case_table_names == 2 ? alias : lex->name), + lex->drop_if_exists, 0); break; } case SQLCOM_ALTER_DB: @@ -3489,7 +3521,8 @@ purposes internal to the MySQL server", MYF(0)); thd->variables.collation_server= global_system_variables.collation_server; thd->update_charset(); - /* Add timezone stuff here */ + thd->variables.time_zone= + global_system_variables.time_zone; thd->one_shot_set= 0; } } @@ -3847,7 +3880,7 @@ mysql_init_query(THD *thd) thd->total_warn_count=0; // Warnings for this query thd->last_insert_id_used= thd->query_start_used= thd->insert_id_used=0; thd->sent_row_count= thd->examined_row_count= 0; - thd->is_fatal_error= thd->rand_used= 0; + thd->is_fatal_error= thd->rand_used= thd->time_zone_used= 0; thd->server_status&= ~ (SERVER_MORE_RESULTS_EXISTS | SERVER_QUERY_NO_INDEX_USED | SERVER_QUERY_NO_GOOD_INDEX_USED); @@ -4185,7 +4218,12 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type, break; case FIELD_TYPE_DECIMAL: if (!length) - new_field->length= 10; // Default length for DECIMAL + { + if ((new_field->length= new_field->decimals)) + new_field->length++; + else + new_field->length= 10; // Default length for DECIMAL + } if (new_field->length < MAX_FIELD_WIDTH) // Skip wrong argument { new_field->length+=sign_len; diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 40107ebc637..506c8763d7b 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -849,7 +849,6 @@ static bool insert_params_from_vars_with_log(Prepared_statement *stmt, List_iterator<LEX_STRING> var_it(varnames); String str; - const String *res; uint32 length= 0; if (query->copy(stmt->query, stmt->query_length, default_charset_info)) DBUG_RETURN(1); @@ -1308,6 +1307,7 @@ static int mysql_test_create_table(Prepared_statement *stmt, DBUG_ENTER("mysql_test_create_table"); THD *thd= stmt->thd; LEX *lex= stmt->lex; + SELECT_LEX *select_lex= &lex->select_lex; int res= 0; /* Skip first table, which is the table we are creating */ @@ -1316,8 +1316,12 @@ static int mysql_test_create_table(Prepared_statement *stmt, &create_table_local); if (!(res= create_table_precheck(thd, tables, create_table)) && - lex->select_lex.item_list.elements) + select_lex->item_list.elements) + { + select_lex->resolve_mode= SELECT_LEX::SELECT_MODE; res= select_like_statement_test(stmt, tables); + select_lex->resolve_mode= SELECT_LEX::NOMATTER_MODE; + } /* put tables back for PS rexecuting */ tables= lex->link_first_table_back(tables, create_table, @@ -1401,7 +1405,11 @@ static int mysql_test_insert_select(Prepared_statement *stmt, (TABLE_LIST *)lex->select_lex.table_list.first; /* Skip first table, which is the table we are inserting in */ lex->select_lex.table_list.first= (byte*) first_local_table->next; - lex->select_lex.resolve_mode= SELECT_LEX::NOMATTER_MODE; + /* + insert/replace from SELECT give its SELECT_LEX for SELECT, + and item_list belong to SELECT + */ + lex->select_lex.resolve_mode= SELECT_LEX::SELECT_MODE; res= select_like_statement_test(stmt, tables); /* revert changes*/ lex->select_lex.table_list.first= (byte*) first_local_table; @@ -1602,7 +1610,7 @@ int mysql_stmt_prepare(THD *thd, char *packet, uint packet_length, if (name) { stmt->name.length= name->length; - if (!(stmt->name.str= memdup_root(&stmt->mem_root, (byte*)name->str, + if (!(stmt->name.str= memdup_root(&stmt->mem_root, (char*)name->str, name->length))) { delete stmt; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index f7a0d5259a6..f1f93343a63 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -1088,12 +1088,14 @@ JOIN::exec() DBUG_ENTER("JOIN::exec"); error= 0; - thd->limit_found_rows= thd->examined_row_count= 0; if (procedure) { if (procedure->change_columns(fields_list) || result->prepare(fields_list, unit)) + { + thd->limit_found_rows= thd->examined_row_count= 0; DBUG_VOID_RETURN; + } } if (!tables_list) @@ -1119,8 +1121,10 @@ JOIN::exec() else error=(int) result->send_eof(); } + thd->limit_found_rows= thd->examined_row_count= 0; DBUG_VOID_RETURN; } + thd->limit_found_rows= thd->examined_row_count= 0; if (zero_result_cause) { @@ -4975,6 +4979,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, table->db_low_byte_first=1; // True for HEAP and MyISAM table->temp_pool_slot = temp_pool_slot; table->copy_blobs= 1; + table->in_use= thd; table->keys_for_keyread.init(); table->keys_in_use.init(); table->read_only_keys.init(); @@ -5137,6 +5142,10 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, recinfo->length=null_pack_length; recinfo++; bfill(null_flags,null_pack_length,255); // Set null fields + + table->null_flags= (uchar*) table->record[0]; + table->null_fields= null_count+ hidden_null_count; + table->null_bytes= null_pack_length; } null_count= (blob_count == 0) ? 1 : 0; hidden_field_count=param->hidden_field_count; @@ -5200,7 +5209,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, param->copy_field_end=copy; param->recinfo=recinfo; - store_record(table,default_values); // Make empty default record + store_record(table,default_values); // Make empty default record if (thd->variables.tmp_table_size == ~(ulong) 0) // No limit table->max_rows= ~(ha_rows) 0; @@ -5971,7 +5980,8 @@ join_read_const_table(JOIN_TAB *tab, POSITION *pos) else { if (!table->key_read && table->used_keys.is_set(tab->ref.key) && - !table->no_keyread) + !table->no_keyread && + (int) table->reginfo.lock_type <= (int) TL_READ_HIGH_PRIORITY) { table->key_read=1; table->file->extra(HA_EXTRA_KEYREAD); @@ -8326,10 +8336,11 @@ calc_group_buffer(JOIN *join,ORDER *group) join->tmp_table_param.group_null_parts=null_parts; } + /* - alloc group fields or take prepared (chached) + allocate group fields or take prepared (cached) - SYNOPSYS + SYNOPSIS make_group_fields() main_join - join of current select curr_join - current join (join of current select or temporary copy of it) @@ -8342,22 +8353,21 @@ calc_group_buffer(JOIN *join,ORDER *group) static bool make_group_fields(JOIN *main_join, JOIN *curr_join) { - if (main_join->group_fields_cache.elements) - { - curr_join->group_fields= main_join->group_fields_cache; - curr_join->sort_and_group= 1; - } - else - { - if (alloc_group_fields(curr_join, curr_join->group_list)) - { - return (1); - } - main_join->group_fields_cache= curr_join->group_fields; - } - return (0); + if (main_join->group_fields_cache.elements) + { + curr_join->group_fields= main_join->group_fields_cache; + curr_join->sort_and_group= 1; + } + else + { + if (alloc_group_fields(curr_join, curr_join->group_list)) + return (1); + main_join->group_fields_cache= curr_join->group_fields; + } + return (0); } + /* Get a list of buffers for saveing last group Groups are saved in reverse order for easyer check loop @@ -8398,7 +8408,6 @@ test_if_group_changed(List<Item_buff> &list) } - /* Setup copy_fields to save fields at start of new group diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 0b565968b08..4642abfcfc4 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -529,7 +529,6 @@ int mysqld_extend_show_tables(THD *thd,const char *db,const char *wild) } else { - struct tm tm_tmp; const char *str; handler *file=table->file; file->info(HA_STATUS_VARIABLE | HA_STATUS_TIME | HA_STATUS_NO_LOCK); @@ -562,24 +561,21 @@ int mysqld_extend_show_tables(THD *thd,const char *db,const char *wild) protocol->store_null(); else { - localtime_r(&file->create_time,&tm_tmp); - localtime_to_TIME(&time, &tm_tmp); + thd->variables.time_zone->gmt_sec_to_TIME(&time, file->create_time); protocol->store(&time); } if (!file->update_time) protocol->store_null(); else { - localtime_r(&file->update_time,&tm_tmp); - localtime_to_TIME(&time, &tm_tmp); + thd->variables.time_zone->gmt_sec_to_TIME(&time, file->update_time); protocol->store(&time); } if (!file->check_time) protocol->store_null(); else { - localtime_r(&file->check_time,&tm_tmp); - localtime_to_TIME(&time, &tm_tmp); + thd->variables.time_zone->gmt_sec_to_TIME(&time, file->check_time); protocol->store(&time); } str= (table->table_charset ? table->table_charset->name : "default"); diff --git a/sql/sql_string.cc b/sql/sql_string.cc index cf4f94ba966..1ec0faafa8f 100644 --- a/sql/sql_string.cc +++ b/sql/sql_string.cc @@ -370,7 +370,7 @@ bool String::copy(const char *str, uint32 arg_length, bool String::set_ascii(const char *str, uint32 arg_length) { - if (!(str_charset->mbminlen > 1)) + if (str_charset->mbminlen == 1) { set(str, arg_length, str_charset); return 0; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 89915852b9b..49ef2f29dfc 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -606,7 +606,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %type <simple_string> remember_name remember_end opt_ident opt_db text_or_password - opt_escape opt_constraint constraint + opt_constraint constraint %type <string> text_string opt_gconcat_separator @@ -634,7 +634,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); using_list expr_or_default set_expr_or_default interval_expr param_marker singlerow_subselect singlerow_subselect_init exists_subselect exists_subselect_init geometry_function - signed_literal now_or_signed_literal + signed_literal now_or_signed_literal opt_escape %type <item_num> NUM_literal @@ -803,7 +803,7 @@ verb_clause: ; deallocate: - DEALLOCATE_SYM PREPARE_SYM ident + deallocate_or_drop PREPARE_SYM ident { THD *thd=YYTHD; LEX *lex= thd->lex; @@ -816,6 +816,12 @@ deallocate: lex->prepared_stmt_name= $3; }; +deallocate_or_drop: + DEALLOCATE_SYM | + DROP + ; + + prepare: PREPARE_SYM ident FROM prepare_src { @@ -3570,8 +3576,12 @@ having_clause: ; opt_escape: - ESCAPE_SYM TEXT_STRING_literal { $$= $2.str; } - | /* empty */ { $$= (char*) "\\"; }; + ESCAPE_SYM simple_expr { $$= $2; } + | /* empty */ + { + $$= new Item_string("\\", 1, &my_charset_latin1); + } + ; /* diff --git a/sql/table.cc b/sql/table.cc index 73f036aed87..e053eba7b6c 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -1134,7 +1134,7 @@ File create_frm(register my_string name, uint reclength, uchar *fileinfo, char fill[IO_SIZE]; #if SIZEOF_OFF_T > 4 - /* Fix this in MySQL 4.0; The current limit is 4G rows (QQ) */ + /* Fix this when we have new .frm files; Current limit is 4G rows (QQ) */ if (create_info->max_rows > ~(ulong) 0) create_info->max_rows= ~(ulong) 0; if (create_info->min_rows > ~(ulong) 0) diff --git a/sql/time.cc b/sql/time.cc index 992f1afc4af..b5550b98e8c 100644 --- a/sql/time.cc +++ b/sql/time.cc @@ -23,16 +23,26 @@ static ulong const days_at_timestart=719528; /* daynr at 1970.01.01 */ uchar *days_in_month= (uchar*) "\037\034\037\036\037\036\037\037\036\037\036\037"; - /* Init some variabels needed when using my_local_time */ - /* Currently only my_time_zone is inited */ +/* + Offset of system time zone from UTC in seconds used to speed up + work of my_system_gmt_sec() function. +*/ static long my_time_zone=0; + +/* + Prepare offset of system time zone from UTC for my_system_gmt_sec() func. + + SYNOPSIS + init_time() +*/ void init_time(void) { time_t seconds; struct tm *l_time,tm_tmp;; TIME my_time; + bool not_used; seconds= (time_t) time((time_t*) 0); localtime_r(&seconds,&tm_tmp); @@ -44,33 +54,40 @@ void init_time(void) my_time.hour= (uint) l_time->tm_hour; my_time.minute= (uint) l_time->tm_min; my_time.second= (uint) l_time->tm_sec; - my_gmt_sec(&my_time, &my_time_zone); /* Init my_time_zone */ + my_system_gmt_sec(&my_time, &my_time_zone, ¬_used); /* Init my_time_zone */ } + /* - Convert current time to sec. since 1970.01.01 - This code handles also day light saving time. - The idea is to cache the time zone (including daylight saving time) - for the next call to make things faster. + Convert time in TIME representation in system time zone to its + my_time_t form (number of seconds in UTC since begginning of Unix Epoch). -*/ + SYNOPSIS + my_system_gmt_sec() + t - time value to be converted + my_timezone - pointer to long where offset of system time zone + from UTC will be stored for caching + in_dst_time_gap - set to true if time falls into spring time-gap -long my_gmt_sec(TIME *t, long *my_timezone) + NOTES + The idea is to cache the time zone offset from UTC (including daylight + saving time) for the next call to make things faster. But currently we + just calculate this offset during startup (by calling init_time() + function) and use it all the time. + Time value provided should be legal time value (e.g. '2003-01-01 25:00:00' + is not allowed). + + RETURN VALUE + Time in UTC seconds since Unix Epoch representation. +*/ +my_time_t +my_system_gmt_sec(const TIME *t, long *my_timezone, bool *in_dst_time_gap) { uint loop; time_t tmp; struct tm *l_time,tm_tmp; long diff, current_timezone; - if (t->year > TIMESTAMP_MAX_YEAR || t->year < TIMESTAMP_MIN_YEAR) - return 0; - - if (t->hour >= 24) - { /* Fix for time-loop */ - t->day+=t->hour/24; - t->hour%=24; - } - /* Calculate the gmt time based on current time and timezone The -1 on the end is to ensure that if have a date that exists twice @@ -125,14 +142,13 @@ long my_gmt_sec(TIME *t, long *my_timezone) tmp+=3600 - t->minute*60 - t->second; // Move to next hour else if (diff == -3600) tmp-=t->minute*60 + t->second; // Move to previous hour + + *in_dst_time_gap= 1; } *my_timezone= current_timezone; - if (tmp < TIMESTAMP_MIN_VALUE || tmp > TIMESTAMP_MAX_VALUE) - tmp= 0; - - return (long) tmp; -} /* my_gmt_sec */ + return (my_time_t) tmp; +} /* my_system_gmt_sec */ /* Some functions to calculate dates */ @@ -164,6 +180,7 @@ long calc_daynr(uint year,uint month,uint day) } /* calc_daynr */ +#ifndef TESTTIME /* Calc weekday from daynr */ /* Returns 0 for monday, 1 for tuesday .... */ @@ -346,6 +363,8 @@ static char time_separator=':'; flags Bitmap of following items TIME_FUZZY_DATE Set if we should allow partial dates TIME_DATETIME_ONLY Set if we only allow full datetimes. + was_cut Set to 1 if value was cut during conversion or to 0 + otherwise. DESCRIPTION At least the following formats are recogniced (based on number of digits) @@ -383,7 +402,8 @@ static char time_separator=':'; #define MAX_DATE_PARTS 8 timestamp_type -str_to_TIME(const char *str, uint length, TIME *l_time, uint flags) +str_to_TIME(const char *str, uint length, TIME *l_time, uint flags, + int *was_cut) { uint field_length, year_length, digits, i, number_of_fields; uint date[MAX_DATE_PARTS], date_len[MAX_DATE_PARTS]; @@ -391,7 +411,6 @@ str_to_TIME(const char *str, uint length, TIME *l_time, uint flags) ulong not_zero_date, allow_space; bool is_internal_format; const char *pos, *last_field_pos; - const char *str_begin= str; const char *end=str+length; const uchar *format_position; bool found_delimitier= 0, found_space= 0; @@ -403,11 +422,16 @@ str_to_TIME(const char *str, uint length, TIME *l_time, uint flags) LINT_INIT(year_length); LINT_INIT(last_field_pos); + *was_cut= 0; + // Skip space at start for (; str != end && my_isspace(&my_charset_latin1, *str) ; str++) ; if (str == end || ! my_isdigit(&my_charset_latin1, *str)) + { + *was_cut= 1; DBUG_RETURN(TIMESTAMP_NONE); + } is_internal_format= 0; /* This has to be changed if want to activate different timestamp formats */ @@ -449,7 +473,10 @@ str_to_TIME(const char *str, uint length, TIME *l_time, uint flags) if (pos == end) { if (flags & TIME_DATETIME_ONLY) + { + *was_cut= 1; DBUG_RETURN(TIMESTAMP_NONE); // Can't be a full datetime + } /* Date field. Set hour, minutes and seconds to 0 */ date[0]= date[1]= date[2]= date[3]= date[4]= 0; start_loop= 5; // Start with first date part @@ -486,7 +513,10 @@ str_to_TIME(const char *str, uint length, TIME *l_time, uint flags) } date_len[i]= (uint) (str - start); if (tmp_value > 999999) // Impossible date part + { + *was_cut= 1; DBUG_RETURN(TIMESTAMP_NONE); + } date[i]=tmp_value; not_zero_date|= tmp_value; @@ -523,7 +553,10 @@ str_to_TIME(const char *str, uint length, TIME *l_time, uint flags) if (my_isspace(&my_charset_latin1,*str)) { if (!(allow_space & (1 << i))) + { + *was_cut= 1; DBUG_RETURN(TIMESTAMP_NONE); + } found_space= 1; } str++; @@ -551,7 +584,10 @@ str_to_TIME(const char *str, uint length, TIME *l_time, uint flags) last_field_pos= str; } if (found_delimitier && !found_space && (flags & TIME_DATETIME_ONLY)) + { + *was_cut= 1; DBUG_RETURN(TIMESTAMP_NONE); // Can't be a datetime + } str= last_field_pos; @@ -566,7 +602,10 @@ str_to_TIME(const char *str, uint length, TIME *l_time, uint flags) { year_length= date_len[(uint) format_position[0]]; if (!year_length) // Year must be specified + { + *was_cut= 1; DBUG_RETURN(TIMESTAMP_NONE); + } l_time->year= date[(uint) format_position[0]]; l_time->month= date[(uint) format_position[1]]; @@ -584,7 +623,10 @@ str_to_TIME(const char *str, uint length, TIME *l_time, uint flags) if (format_position[7] != (uchar) 255) { if (l_time->hour > 12) + { + *was_cut= 1; goto err; + } l_time->hour= l_time->hour%12 + add_hours; } } @@ -624,7 +666,7 @@ str_to_TIME(const char *str, uint length, TIME *l_time, uint flags) } } if (not_zero_date) - current_thd->cuted_fields++; + *was_cut= 1; goto err; } @@ -635,8 +677,7 @@ str_to_TIME(const char *str, uint length, TIME *l_time, uint flags) { if (!my_isspace(&my_charset_latin1,*str)) { - make_truncated_value_warning(current_thd, str_begin, length, - l_time->time_type); + *was_cut= 1; break; } } @@ -650,43 +691,59 @@ err: } -time_t str_to_timestamp(const char *str,uint length) +/* + Convert a timestamp string to a TIME value and produce a warning + if string was truncated during conversion. + + NOTE + See description of str_to_TIME() for more information. +*/ +timestamp_type +str_to_TIME_with_warn(const char *str, uint length, TIME *l_time, uint flags) { - TIME l_time; - long not_used; - time_t timestamp= 0; - - if (str_to_TIME(str,length,&l_time,0) > TIMESTAMP_DATETIME_ERROR && - !(timestamp= my_gmt_sec(&l_time, ¬_used))) - current_thd->cuted_fields++; - return timestamp; + int was_cut; + timestamp_type ts_type= str_to_TIME(str, length, l_time, flags, &was_cut); + if (was_cut) + make_truncated_value_warning(current_thd, str, length, ts_type); + return ts_type; } /* - Convert a string to datetime. + Convert a datetime from broken-down TIME representation to corresponding + TIMESTAMP value. SYNOPSIS - str_to_datetime() - str String to parse (see str_to_TIME() synopsis) - length Length of str - fuzzy_date Flags (see str_to_TIME() synopsis) - + TIME_to_timestamp() + thd - current thread + t - datetime in broken-down representation, + in_dst_time_gap - pointer to bool which is set to true if t represents + value which doesn't exists (falls into the spring + time-gap) or to false otherwise. + RETURN - -1 if error - datetime value otherwise + Number seconds in UTC since start of Unix Epoch corresponding to t. + 0 - t contains datetime value which is out of TIMESTAMP range. + */ - -longlong str_to_datetime(const char *str,uint length, uint fuzzy_date) +my_time_t TIME_to_timestamp(THD *thd, const TIME *t, bool *in_dst_time_gap) { - TIME l_time; - if (str_to_TIME(str,length,&l_time,fuzzy_date) <= TIMESTAMP_DATETIME_ERROR) - return -1; - return (longlong) (l_time.year*LL(10000000000) + - l_time.month*LL(100000000)+ - l_time.day*LL(1000000)+ - l_time.hour*LL(10000)+ - (longlong) (l_time.minute*100+l_time.second)); + my_time_t timestamp; + + *in_dst_time_gap= 0; + + if (t->year < TIMESTAMP_MAX_YEAR && t->year > TIMESTAMP_MIN_YEAR || + t->year == TIMESTAMP_MAX_YEAR && t->month == 1 && t->day == 1 || + t->year == TIMESTAMP_MIN_YEAR && t->month == 12 && t->day == 31) + { + thd->time_zone_used= 1; + timestamp= thd->variables.time_zone->TIME_to_gmt_sec(t, in_dst_time_gap); + if (timestamp >= TIMESTAMP_MIN_VALUE && timestamp <= TIMESTAMP_MAX_VALUE) + return timestamp; + } + + /* If we are here we have range error. */ + return(0); } @@ -701,6 +758,8 @@ longlong str_to_datetime(const char *str,uint length, uint fuzzy_date) There may be an optional [.second_part] after seconds length Length of str l_time Store result here + was_cut Set to 1 if value was cut during conversion or to 0 + otherwise. NOTES Because of the extra days argument, this function can only @@ -711,15 +770,15 @@ longlong str_to_datetime(const char *str,uint length, uint fuzzy_date) 1 error */ -bool str_to_time(const char *str,uint length,TIME *l_time) +bool str_to_time(const char *str, uint length, TIME *l_time, int *was_cut) { long date[5],value; const char *end=str+length, *end_of_days; - const char *str_begin= str; bool found_days,found_hours; uint state; l_time->neg=0; + *was_cut= 0; for (; str != end && my_isspace(&my_charset_latin1,*str) ; str++) length--; if (str != end && *str == '-') @@ -736,9 +795,12 @@ bool str_to_time(const char *str,uint length,TIME *l_time) { // Probably full timestamp enum timestamp_type res= str_to_TIME(str,length,l_time, (TIME_FUZZY_DATE | - TIME_DATETIME_ONLY)); + TIME_DATETIME_ONLY), + was_cut); if ((int) res >= (int) TIMESTAMP_DATETIME_ERROR) return res == TIMESTAMP_DATETIME_ERROR; + /* We need to restore was_cut flag since str_to_TIME can modify it */ + *was_cut= 0; } /* Not a timestamp. Try to get this as a DAYS_TO_SECOND string */ @@ -841,7 +903,7 @@ fractional: /* Some simple checks */ if (date[2] >= 60 || date[3] >= 60) { - current_thd->cuted_fields++; + *was_cut= 1; return 1; } l_time->year= 0; // For protocol::store_time @@ -860,8 +922,7 @@ fractional: { if (!my_isspace(&my_charset_latin1,*str)) { - make_truncated_value_warning(current_thd, str_begin, length, - TIMESTAMP_TIME); + *was_cut= 1; break; } } while (++str != end); @@ -871,6 +932,113 @@ fractional: /* + Convert a time string to a TIME struct and produce a warning + if string was cut during conversion. + + NOTE + See str_to_time() for more info. +*/ +bool +str_to_time_with_warn(const char *str, uint length, TIME *l_time) +{ + int was_cut; + bool ret_val= str_to_time(str, length, l_time, &was_cut); + if (was_cut) + make_truncated_value_warning(current_thd, str, length, TIMESTAMP_TIME); + return ret_val; +} + + +/* + Convert datetime value specified as number to broken-down TIME + representation and form value of DATETIME type as side-effect. + + SYNOPSIS + number_to_TIME() + nr - datetime value as number + time_res - pointer for structure for broken-down representation + fuzzy_date - indicates whenever we allow fuzzy dates + was_cut - set ot 1 if there was some kind of error during + conversion or to 0 if everything was OK. + + DESCRIPTION + Convert a datetime value of formats YYMMDD, YYYYMMDD, YYMMDDHHMSS, + YYYYMMDDHHMMSS to broken-down TIME representation. Return value in + YYYYMMDDHHMMSS format as side-effect. + + This function also checks if datetime value fits in DATETIME range. + + RETURN VALUE + Datetime value in YYYYMMDDHHMMSS format. + If input value is not valid datetime value then 0 is returned. +*/ + +longlong number_to_TIME(longlong nr, TIME *time_res, bool fuzzy_date, + int *was_cut) +{ + long part1,part2; + + *was_cut= 0; + + if (nr == LL(0) || nr >= LL(10000101000000)) + goto ok; + if (nr < 101) + goto err; + if (nr <= (YY_PART_YEAR-1)*10000L+1231L) + { + nr= (nr+20000000L)*1000000L; // YYMMDD, year: 2000-2069 + goto ok; + } + if (nr < (YY_PART_YEAR)*10000L+101L) + goto err; + if (nr <= 991231L) + { + nr= (nr+19000000L)*1000000L; // YYMMDD, year: 1970-1999 + goto ok; + } + if (nr < 10000101L) + goto err; + if (nr <= 99991231L) + { + nr= nr*1000000L; + goto ok; + } + if (nr < 101000000L) + goto err; + if (nr <= (YY_PART_YEAR-1)*LL(10000000000)+LL(1231235959)) + { + nr= nr+LL(20000000000000); // YYMMDDHHMMSS, 2000-2069 + goto ok; + } + if (nr < YY_PART_YEAR*LL(10000000000)+ LL(101000000)) + goto err; + if (nr <= LL(991231235959)) + nr= nr+LL(19000000000000); // YYMMDDHHMMSS, 1970-1999 + + ok: + part1=(long) (nr/LL(1000000)); + part2=(long) (nr - (longlong) part1*LL(1000000)); + time_res->year= (int) (part1/10000L); part1%=10000L; + time_res->month= (int) part1 / 100; + time_res->day= (int) part1 % 100; + time_res->hour= (int) (part2/10000L); part2%=10000L; + time_res->minute=(int) part2 / 100; + time_res->second=(int) part2 % 100; + + if (time_res->year <= 9999 && time_res->month <= 12 && + time_res->day <= 31 && time_res->hour <= 23 && + time_res->minute <= 59 && time_res->second <= 59 && + (fuzzy_date || (time_res->month != 0 && time_res->day != 0) || nr==0)) + return nr; + + err: + + *was_cut= 1; + return LL(0); +} + + +/* Convert a system time structure to TIME */ @@ -1307,6 +1475,7 @@ void make_datetime(const DATE_TIME_FORMAT *format __attribute__((unused)), str->set_charset(&my_charset_bin); } + void make_truncated_value_warning(THD *thd, const char *str_val, uint str_length, timestamp_type time_type) { @@ -1323,19 +1492,17 @@ void make_truncated_value_warning(THD *thd, const char *str_val, case TIMESTAMP_DATE: type_str= "date"; break; - case TIMESTAMP_DATETIME: - type_str= "datetime"; - break; case TIMESTAMP_TIME: type_str= "time"; break; + case TIMESTAMP_DATETIME: // FALLTHROUGH default: - type_str= "string"; + type_str= "datetime"; break; } sprintf(warn_buff, ER(ER_TRUNCATED_WRONG_VALUE), type_str, str.ptr()); - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_TRUNCATED_WRONG_VALUE, warn_buff); } @@ -1446,3 +1613,5 @@ void TIME_to_string(const TIME *time, String *str) DBUG_ASSERT(0); } } + +#endif diff --git a/sql/tzfile.h b/sql/tzfile.h new file mode 100644 index 00000000000..623cddc1f12 --- /dev/null +++ b/sql/tzfile.h @@ -0,0 +1,137 @@ +/* Copyright (C) 2004 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 */ + +/* + This file is based on public domain code from ftp://elsie.ncih.nist.gov/ + Initial source code is in the public domain, so clarified as of + 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov). +*/ + +/* + Information about time zone files. +*/ + +#ifndef TZDIR +#define TZDIR "/usr/share/zoneinfo" /* Time zone object file directory */ +#endif /* !defined TZDIR */ + +/* + Each file begins with. . . +*/ + +#define TZ_MAGIC "TZif" + +struct tzhead { + char tzh_magic[4]; /* TZ_MAGIC */ + char tzh_reserved[16]; /* reserved for future use */ + char tzh_ttisgmtcnt[4]; /* coded number of trans. time flags */ + char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */ + char tzh_leapcnt[4]; /* coded number of leap seconds */ + char tzh_timecnt[4]; /* coded number of transition times */ + char tzh_typecnt[4]; /* coded number of local time types */ + char tzh_charcnt[4]; /* coded number of abbr. chars */ +}; + +/* + . . .followed by. . . + + tzh_timecnt (char [4])s coded transition times a la time(2) + tzh_timecnt (unsigned char)s types of local time starting at above + tzh_typecnt repetitions of + one (char [4]) coded UTC offset in seconds + one (unsigned char) used to set tm_isdst + one (unsigned char) that's an abbreviation list index + tzh_charcnt (char)s '\0'-terminated zone abbreviations + tzh_leapcnt repetitions of + one (char [4]) coded leap second transition times + one (char [4]) total correction after above + tzh_ttisstdcnt (char)s indexed by type; if TRUE, transition + time is standard time, if FALSE, + transition time is wall clock time + if absent, transition times are + assumed to be wall clock time + tzh_ttisgmtcnt (char)s indexed by type; if TRUE, transition + time is UTC, if FALSE, + transition time is local time + if absent, transition times are + assumed to be local time +*/ + +/* + In the current implementation, we refuse to deal with files that + exceed any of the limits below. +*/ + +#ifndef TZ_MAX_TIMES +/* + The TZ_MAX_TIMES value below is enough to handle a bit more than a + year's worth of solar time (corrected daily to the nearest second) or + 138 years of Pacific Presidential Election time + (where there are three time zone transitions every fourth year). +*/ +#define TZ_MAX_TIMES 370 +#endif /* !defined TZ_MAX_TIMES */ + +#ifndef TZ_MAX_TYPES +#ifdef SOLAR +#define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */ +#else +/* + Must be at least 14 for Europe/Riga as of Jan 12 1995, + as noted by Earl Chew <earl@hpato.aus.hp.com>. +*/ +#define TZ_MAX_TYPES 20 /* Maximum number of local time types */ +#endif /* defined SOLAR */ +#endif /* !defined TZ_MAX_TYPES */ + +#ifndef TZ_MAX_CHARS +#define TZ_MAX_CHARS 50 /* Maximum number of abbreviation characters */ + /* (limited by what unsigned chars can hold) */ +#endif /* !defined TZ_MAX_CHARS */ + +#ifndef TZ_MAX_LEAPS +#define TZ_MAX_LEAPS 50 /* Maximum number of leap second corrections */ +#endif /* !defined TZ_MAX_LEAPS */ + +#ifndef TZ_MAX_REV_RANGES +#ifdef SOLAR +/* Solar (Asia/RiyadhXX) zones need significantly bigger TZ_MAX_REV_RANGES */ +#define TZ_MAX_REV_RANGES (TZ_MAX_TIMES*2+TZ_MAX_LEAPS*2+2) +#else +#define TZ_MAX_REV_RANGES (TZ_MAX_TIMES+TZ_MAX_LEAPS+2) +#endif +#endif + +#define SECS_PER_MIN 60 +#define MINS_PER_HOUR 60 +#define HOURS_PER_DAY 24 +#define DAYS_PER_WEEK 7 +#define DAYS_PER_NYEAR 365 +#define DAYS_PER_LYEAR 366 +#define SECS_PER_HOUR (SECS_PER_MIN * MINS_PER_HOUR) +#define SECS_PER_DAY ((long) SECS_PER_HOUR * HOURS_PER_DAY) +#define MONS_PER_YEAR 12 + +#define TM_YEAR_BASE 1900 + +#define EPOCH_YEAR 1970 + +/* + Accurate only for the past couple of centuries, + that will probably do. +*/ + +#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0)) diff --git a/sql/tztime.cc b/sql/tztime.cc new file mode 100644 index 00000000000..7bdc6fe1ac4 --- /dev/null +++ b/sql/tztime.cc @@ -0,0 +1,2564 @@ +/* Copyright (C) 2004 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 */ + +/* + Most of the following code and structures were derived from + public domain code from ftp://elsie.nci.nih.gov/pub + (We will refer to this code as to elsie-code further.) +*/ + +#ifdef __GNUC__ +#pragma implementation // gcc: Class implementation +#endif + +#include "mysql_priv.h" +#include "tzfile.h" +#include <m_string.h> +#include <my_dir.h> + +/* + Now we don't use abbreviations in server but we will do this in future. +*/ +#if defined(TZINFO2SQL) || defined(TESTTIME) +#define ABBR_ARE_USED +#else +#if !defined(DBUG_OFF) +/* Let use abbreviations for debug purposes */ +#undef ABBR_ARE_USED +#define ABBR_ARE_USED +#endif /* !defined(DBUG_OFF) */ +#endif /* defined(TZINFO2SQL) || defined(TESTTIME) */ + +/* Structure describing local time type (e.g. Moscow summer time (MSD)) */ +typedef struct ttinfo +{ + long tt_gmtoff; // Offset from UTC in seconds + uint tt_isdst; // Is daylight saving time or not. Used to set tm_isdst +#ifdef ABBR_ARE_USED + uint tt_abbrind; // Index of start of abbreviation for this time type. +#endif + /* + We don't use tt_ttisstd and tt_ttisgmt members of original elsie-code + struct since we don't support POSIX-style TZ descriptions in variables. + */ +} TRAN_TYPE_INFO; + +/* Structure describing leap-second corrections. */ +typedef struct lsinfo +{ + my_time_t ls_trans; // Transition time + long ls_corr; // Correction to apply +} LS_INFO; + +/* + Structure with information describing ranges of my_time_t shifted to local + time (my_time_t + offset). Used for local TIME -> my_time_t conversion. + See comments for TIME_to_gmt_sec() for more info. +*/ +typedef struct revtinfo +{ + long rt_offset; // Offset of local time from UTC in seconds + uint rt_type; // Type of period 0 - Normal period. 1 - Spring time-gap +} REVT_INFO; + +#ifdef TZNAME_MAX +#define MY_TZNAME_MAX TZNAME_MAX +#endif +#ifndef TZNAME_MAX +#define MY_TZNAME_MAX 255 +#endif + +/* + Structure which fully describes time zone which is + described in our db or in zoneinfo files. +*/ +typedef struct st_time_zone_info +{ + uint leapcnt; // Number of leap-second corrections + uint timecnt; // Number of transitions between time types + uint typecnt; // Number of local time types + uint charcnt; // Number of characters used for abbreviations + uint revcnt; // Number of transition descr. for TIME->my_time_t conversion + /* The following are dynamical arrays are allocated in MEM_ROOT */ + my_time_t *ats; // Times of transitions between time types + unsigned char *types; // Local time types for transitions + TRAN_TYPE_INFO *ttis; // Local time types descriptions +#ifdef ABBR_ARE_USED + /* Storage for local time types abbreviations. They are stored as ASCIIZ */ + char *chars; +#endif + /* + Leap seconds corrections descriptions, this array is shared by + all time zones who use leap seconds. + */ + LS_INFO *lsis; + /* + Starting points and descriptions of shifted my_time_t (my_time_t + offset) + ranges on which shifted my_time_t -> my_time_t mapping is linear or undefined. + Used for tm -> my_time_t conversion. + */ + my_time_t *revts; + REVT_INFO *revtis; + /* + Time type which is used for times smaller than first transition or if + there are no transitions at all. + */ + TRAN_TYPE_INFO *fallback_tti; + +} TIME_ZONE_INFO; + + +static my_bool prepare_tz_info(TIME_ZONE_INFO *sp, MEM_ROOT *storage); + + +#if defined(TZINFO2SQL) || defined(TESTTIME) + +/* + Load time zone description from zoneinfo (TZinfo) file. + + SYNOPSIS + tz_load() + name - path to zoneinfo file + sp - TIME_ZONE_INFO structure to fill + + RETURN VALUES + 0 - Ok + 1 - Error +*/ +static my_bool +tz_load(const char *name, TIME_ZONE_INFO *sp, MEM_ROOT *storage) +{ + char *p; + int read_from_file; + uint i; + FILE *file; + + if (!(file= my_fopen(name, O_RDONLY|O_BINARY, MYF(MY_WME)))) + return 1; + { + union + { + struct tzhead tzhead; + char buf[sizeof(struct tzhead) + sizeof(my_time_t) * TZ_MAX_TIMES + + TZ_MAX_TIMES + sizeof(TRAN_TYPE_INFO) * TZ_MAX_TYPES + +#ifdef ABBR_ARE_USED + max(TZ_MAX_CHARS + 1, (2 * (MY_TZNAME_MAX + 1))) + +#endif + sizeof(LS_INFO) * TZ_MAX_LEAPS]; + } u; + uint ttisstdcnt; + uint ttisgmtcnt; + char *tzinfo_buf; + + read_from_file= my_fread(file, u.buf, sizeof(u.buf), MYF(MY_WME)); + + if (my_fclose(file, MYF(MY_WME)) != 0) + return 1; + + if (read_from_file < (int)sizeof(struct tzhead)) + return 1; + + ttisstdcnt= int4net(u.tzhead.tzh_ttisgmtcnt); + ttisgmtcnt= int4net(u.tzhead.tzh_ttisstdcnt); + sp->leapcnt= int4net(u.tzhead.tzh_leapcnt); + sp->timecnt= int4net(u.tzhead.tzh_timecnt); + sp->typecnt= int4net(u.tzhead.tzh_typecnt); + sp->charcnt= int4net(u.tzhead.tzh_charcnt); + p= u.tzhead.tzh_charcnt + sizeof u.tzhead.tzh_charcnt; + if (sp->leapcnt > TZ_MAX_LEAPS || + sp->typecnt == 0 || sp->typecnt > TZ_MAX_TYPES || + sp->timecnt > TZ_MAX_TIMES || + sp->charcnt > TZ_MAX_CHARS || + (ttisstdcnt != sp->typecnt && ttisstdcnt != 0) || + (ttisgmtcnt != sp->typecnt && ttisgmtcnt != 0)) + return 1; + if ((uint)(read_from_file - (p - u.buf)) < + sp->timecnt * 4 + /* ats */ + sp->timecnt + /* types */ + sp->typecnt * (4 + 2) + /* ttinfos */ + sp->charcnt + /* chars */ + sp->leapcnt * (4 + 4) + /* lsinfos */ + ttisstdcnt + /* ttisstds */ + ttisgmtcnt) /* ttisgmts */ + return 1; + + if (!(tzinfo_buf= (char *)alloc_root(storage, + ALIGN_SIZE(sp->timecnt * + sizeof(my_time_t)) + + ALIGN_SIZE(sp->timecnt) + + ALIGN_SIZE(sp->typecnt * + sizeof(TRAN_TYPE_INFO)) + +#ifdef ABBR_ARE_USED + ALIGN_SIZE(sp->charcnt) + +#endif + sp->leapcnt * sizeof(LS_INFO)))) + return 1; + + sp->ats= (my_time_t *)tzinfo_buf; + tzinfo_buf+= ALIGN_SIZE(sp->timecnt * sizeof(my_time_t)); + sp->types= (unsigned char *)tzinfo_buf; + tzinfo_buf+= ALIGN_SIZE(sp->timecnt); + sp->ttis= (TRAN_TYPE_INFO *)tzinfo_buf; + tzinfo_buf+= ALIGN_SIZE(sp->typecnt * sizeof(TRAN_TYPE_INFO)); +#ifdef ABBR_ARE_USED + sp->chars= tzinfo_buf; + tzinfo_buf+= ALIGN_SIZE(sp->charcnt); +#endif + sp->lsis= (LS_INFO *)tzinfo_buf; + + for (i= 0; i < sp->timecnt; i++, p+= 4) + sp->ats[i]= int4net(p); + + for (i= 0; i < sp->timecnt; i++) + { + sp->types[i]= (unsigned char) *p++; + if (sp->types[i] >= sp->typecnt) + return 1; + } + for (i= 0; i < sp->typecnt; i++) + { + TRAN_TYPE_INFO * ttisp; + + ttisp= &sp->ttis[i]; + ttisp->tt_gmtoff= int4net(p); + p+= 4; + ttisp->tt_isdst= (unsigned char) *p++; + if (ttisp->tt_isdst != 0 && ttisp->tt_isdst != 1) + return 1; + ttisp->tt_abbrind= (unsigned char) *p++; + if (ttisp->tt_abbrind > sp->charcnt) + return 1; + } + for (i= 0; i < sp->charcnt; i++) + sp->chars[i]= *p++; + sp->chars[i]= '\0'; /* ensure '\0' at end */ + for (i= 0; i < sp->leapcnt; i++) + { + LS_INFO *lsisp; + + lsisp= &sp->lsis[i]; + lsisp->ls_trans= int4net(p); + p+= 4; + lsisp->ls_corr= int4net(p); + p+= 4; + } + /* + Since we don't support POSIX style TZ definitions in variables we + don't read further like glibc or elsie code. + */ + } + + return prepare_tz_info(sp, storage); +} +#endif /* defined(TZINFO2SQL) || defined(TESTTIME) */ + + +/* + Finish preparation of time zone description for use in TIME_to_gmt_sec() + and gmt_sec_to_TIME() functions. + + SYNOPSIS + prepare_tz_info() + sp - pointer to time zone description + storage - pointer to MEM_ROOT where arrays for map allocated + + DESCRIPTION + First task of this function is to find fallback time type which will + be used if there are no transitions or we have moment in time before + any transitions. + Second task is to build "shifted my_time_t" -> my_time_t map used in + TIME -> my_time_t conversion. + Note: See description of TIME_to_gmt_sec() function first. + In order to perform TIME -> my_time_t conversion we need to build table + which defines "shifted by tz offset and leap seconds my_time_t" -> + my_time_t function wich is almost the same (except ranges of ambiguity) + as reverse function to piecewise linear function used for my_time_t -> + "shifted my_time_t" conversion and which is also specified as table in + zoneinfo file or in our db (It is specified as start of time type ranges + and time type offsets). So basic idea is very simple - let us iterate + through my_time_t space from one point of discontinuity of my_time_t -> + "shifted my_time_t" function to another and build our approximation of + reverse function. (Actually we iterate through ranges on which + my_time_t -> "shifted my_time_t" is linear function). + + RETURN VALUES + 0 Ok + 1 Error +*/ +static my_bool +prepare_tz_info(TIME_ZONE_INFO *sp, MEM_ROOT *storage) +{ + my_time_t cur_t= MY_TIME_T_MIN; + my_time_t cur_l, end_t, end_l; + my_time_t cur_max_seen_l= MY_TIME_T_MIN; + long cur_offset, cur_corr, cur_off_and_corr; + uint next_trans_idx, next_leap_idx; + uint i; + /* + Temporary arrays where we will store tables. Needed because + we don't know table sizes ahead. (Well we can estimate their + upper bound but this will take extra space.) + */ + my_time_t revts[TZ_MAX_REV_RANGES]; + REVT_INFO revtis[TZ_MAX_REV_RANGES]; + + LINT_INIT(end_l); + + /* + Let us setup fallback time type which will be used if we have not any + transitions or if we have moment of time before first transition. + We will find first non-DST local time type and use it (or use first + local time type if all of them are DST types). + */ + for (i= 0; i < sp->typecnt && sp->ttis[i].tt_isdst; i++) + /* no-op */ ; + if (i == sp->typecnt) + i= 0; + sp->fallback_tti= &(sp->ttis[i]); + + + /* + Let us build shifted my_time_t -> my_time_t map. + */ + sp->revcnt= 0; + + /* Let us find initial offset */ + if (sp->timecnt == 0 || cur_t < sp->ats[0]) + { + /* + If we have not any transitions or t is before first transition we are using + already found fallback time type which index is already in i. + */ + next_trans_idx= 0; + } + else + { + /* cur_t == sp->ats[0] so we found transition */ + i= sp->types[0]; + next_trans_idx= 1; + } + + cur_offset= sp->ttis[i].tt_gmtoff; + + + /* let us find leap correction... unprobable, but... */ + for (next_leap_idx= 0; next_leap_idx < sp->leapcnt && + cur_t >= sp->lsis[next_leap_idx].ls_trans; + ++next_leap_idx) + continue; + + if (next_leap_idx > 0) + cur_corr= sp->lsis[next_leap_idx - 1].ls_corr; + else + cur_corr= 0; + + /* Iterate trough t space */ + while (sp->revcnt < TZ_MAX_REV_RANGES - 1) + { + cur_off_and_corr= cur_offset - cur_corr; + + /* + We assuming that cur_t could be only overflowed downwards, + we also assume that end_t won't be overflowed in this case. + */ + if (cur_off_and_corr < 0 && + cur_t < MY_TIME_T_MIN - cur_off_and_corr) + cur_t= MY_TIME_T_MIN - cur_off_and_corr; + + cur_l= cur_t + cur_off_and_corr; + + /* + Let us choose end_t as point before next time type change or leap + second correction. + */ + end_t= min((next_trans_idx < sp->timecnt) ? sp->ats[next_trans_idx] - 1: + MY_TIME_T_MAX, + (next_leap_idx < sp->leapcnt) ? + sp->lsis[next_leap_idx].ls_trans - 1: MY_TIME_T_MAX); + /* + again assuming that end_t can be overlowed only in positive side + we also assume that end_t won't be overflowed in this case. + */ + if (cur_off_and_corr > 0 && + end_t > MY_TIME_T_MAX - cur_off_and_corr) + end_t= MY_TIME_T_MAX - cur_off_and_corr; + + end_l= end_t + cur_off_and_corr; + + + if (end_l > cur_max_seen_l) + { + /* We want special handling in the case of first range */ + if (cur_max_seen_l == MY_TIME_T_MIN) + { + revts[sp->revcnt]= cur_l; + revtis[sp->revcnt].rt_offset= cur_off_and_corr; + revtis[sp->revcnt].rt_type= 0; + sp->revcnt++; + cur_max_seen_l= end_l; + } + else + { + if (cur_l > cur_max_seen_l + 1) + { + /* We have a spring time-gap and we are not at the first range */ + revts[sp->revcnt]= cur_max_seen_l + 1; + revtis[sp->revcnt].rt_offset= revtis[sp->revcnt-1].rt_offset; + revtis[sp->revcnt].rt_type= 1; + sp->revcnt++; + if (sp->revcnt == TZ_MAX_TIMES + TZ_MAX_LEAPS + 1) + break; /* That was too much */ + cur_max_seen_l= cur_l - 1; + } + + /* Assume here end_l > cur_max_seen_l (because end_l>=cur_l) */ + + revts[sp->revcnt]= cur_max_seen_l + 1; + revtis[sp->revcnt].rt_offset= cur_off_and_corr; + revtis[sp->revcnt].rt_type= 0; + sp->revcnt++; + cur_max_seen_l= end_l; + } + } + + if (end_t == MY_TIME_T_MAX || + (cur_off_and_corr > 0) && + (end_t >= MY_TIME_T_MAX - cur_off_and_corr)) + /* end of t space */ + break; + + cur_t= end_t + 1; + + /* + Let us find new offset and correction. Because of our choice of end_t + cur_t can only be point where new time type starts or/and leap + correction is performed. + */ + if (sp->timecnt != 0 && cur_t >= sp->ats[0]) /* else reuse old offset */ + if (next_trans_idx < sp->timecnt && + cur_t == sp->ats[next_trans_idx]) + { + /* We are at offset point */ + cur_offset= sp->ttis[sp->types[next_trans_idx]].tt_gmtoff; + ++next_trans_idx; + } + + if (next_leap_idx < sp->leapcnt && + cur_t == sp->lsis[next_leap_idx].ls_trans) + { + /* we are at leap point */ + cur_corr= sp->lsis[next_leap_idx].ls_corr; + ++next_leap_idx; + } + } + + /* check if we have had enough space */ + if (sp->revcnt == TZ_MAX_REV_RANGES - 1) + return 1; + + /* set maximum end_l as finisher */ + revts[sp->revcnt]= end_l; + + /* Allocate arrays of proper size in sp and copy result there */ + if (!(sp->revts= (my_time_t *)alloc_root(storage, + sizeof(my_time_t) * (sp->revcnt + 1))) || + !(sp->revtis= (REVT_INFO *)alloc_root(storage, + sizeof(REVT_INFO) * sp->revcnt))) + return 1; + + memcpy(sp->revts, revts, sizeof(my_time_t) * (sp->revcnt + 1)); + memcpy(sp->revtis, revtis, sizeof(REVT_INFO) * sp->revcnt); + + return 0; +} + + +static const uint mon_lengths[2][MONS_PER_YEAR]= +{ + { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, + { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } +}; + +static const uint mon_starts[2][MONS_PER_YEAR]= +{ + { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }, + { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 } +}; + +static const uint year_lengths[2]= +{ + DAYS_PER_NYEAR, DAYS_PER_LYEAR +}; + +#define LEAPS_THRU_END_OF(y) ((y) / 4 - (y) / 100 + (y) / 400) + + +/* + Converts time from my_time_t representation (seconds in UTC since Epoch) + to broken down representation using given local time zone offset. + + SYNOPSIS + sec_to_TIME() + tmp - pointer to structure for broken down representation + t - my_time_t value to be converted + offset - local time zone offset + + DESCRIPTION + Convert my_time_t with offset to TIME struct. Differs from timesub + (from elsie code) because doesn't contain any leap correction and + TM_GMTOFF and is_dst setting and contains some MySQL specific + initialization. Funny but with removing of these we almost have + glibc's offtime function. +*/ +static void +sec_to_TIME(TIME * tmp, my_time_t t, long offset) +{ + long days; + long rem; + int y; + int yleap; + const uint *ip; + + days= t / SECS_PER_DAY; + rem= t % SECS_PER_DAY; + + /* + We do this as separate step after dividing t, because this + allows us handle times near my_time_t bounds without overflows. + */ + rem+= offset; + while (rem < 0) + { + rem+= SECS_PER_DAY; + days--; + } + while (rem >= SECS_PER_DAY) + { + rem -= SECS_PER_DAY; + days++; + } + tmp->hour= (uint)(rem / SECS_PER_HOUR); + rem= rem % SECS_PER_HOUR; + tmp->minute= (uint)(rem / SECS_PER_MIN); + /* + A positive leap second requires a special + representation. This uses "... ??:59:60" et seq. + */ + tmp->second= (uint)(rem % SECS_PER_MIN); + + y= EPOCH_YEAR; + while (days < 0 || days >= (long)year_lengths[yleap= isleap(y)]) + { + int newy; + + newy= y + days / DAYS_PER_NYEAR; + if (days < 0) + newy--; + days-= (newy - y) * DAYS_PER_NYEAR + + LEAPS_THRU_END_OF(newy - 1) - + LEAPS_THRU_END_OF(y - 1); + y= newy; + } + tmp->year= y; + + ip= mon_lengths[yleap]; + for (tmp->month= 0; days >= (long) ip[tmp->month]; tmp->month++) + days= days - (long) ip[tmp->month]; + tmp->month++; + tmp->day= (uint)(days + 1); + + /* filling MySQL specific TIME members */ + tmp->neg= 0; tmp->second_part= 0; + tmp->time_type= TIMESTAMP_DATETIME; +} + + +/* + Find time range wich contains given my_time_t value + + SYNOPSIS + find_time_range() + t - my_time_t value for which we looking for range + range_boundaries - sorted array of range starts. + higher_bound - number of ranges + + DESCRIPTION + Performs binary search for range which contains given my_time_t value. + It has sense if number of ranges is greater than zero and my_time_t value + is greater or equal than beginning of first range. It also assumes that + t belongs to some range specified or end of last is MY_TIME_T_MAX. + + With this localtime_r on real data may takes less time than with linear + search (I've seen 30% speed up). + + RETURN VALUE + Index of range to which t belongs +*/ +static uint +find_time_range(my_time_t t, const my_time_t *range_boundaries, + uint higher_bound) +{ + uint i, lower_bound= 0; + + /* + Function will work without this assertion but result would be meaningless. + */ + DBUG_ASSERT(higher_bound > 0 && t >= range_boundaries[0]); + + /* + Do binary search for minimal interval which contain t. We preserve: + range_boundaries[lower_bound] <= t < range_boundaries[higher_bound] + invariant and decrease this higher_bound - lower_bound gap twice + times on each step. + */ + + while (higher_bound - lower_bound > 1) + { + i= (lower_bound + higher_bound) >> 1; + if (range_boundaries[i] <= t) + lower_bound= i; + else + higher_bound= i; + } + return lower_bound; +} + +/* + Find local time transition for given my_time_t. + + SYNOPSIS + find_transition_type() + t - my_time_t value to be converted + sp - pointer to struct with time zone description + + RETURN VALUE + Pointer to structure in time zone description describing + local time type for given my_time_t. +*/ +static +const TRAN_TYPE_INFO * +find_transition_type(my_time_t t, const TIME_ZONE_INFO *sp) +{ + if (unlikely(sp->timecnt == 0 || t < sp->ats[0])) + { + /* + If we have not any transitions or t is before first transition let + us use fallback time type. + */ + return sp->fallback_tti; + } + + /* + Do binary search for minimal interval between transitions which + contain t. With this localtime_r on real data may takes less + time than with linear search (I've seen 30% speed up). + */ + return &(sp->ttis[sp->types[find_time_range(t, sp->ats, sp->timecnt)]]); +} + + +/* + Converts time in my_time_t representation (seconds in UTC since Epoch) to + broken down TIME representation in local time zone. + + SYNOPSIS + gmt_sec_to_TIME() + tmp - pointer to structure for broken down represenatation + sec_in_utc - my_time_t value to be converted + sp - pointer to struct with time zone description + + TODO + We can improve this function by creating joined array of transitions and + leap corrections. This will require adding extra field to TRAN_TYPE_INFO + for storing number of "extra" seconds to minute occured due to correction + (60th and 61st second, look how we calculate them as "hit" in this + function). + Under realistic assumptions about frequency of transitions the same array + can be used fot TIME -> my_time_t conversion. For this we need to + implement tweaked binary search which will take into account that some + TIME has two matching my_time_t ranges and some of them have none. +*/ +static void +gmt_sec_to_TIME(TIME *tmp, my_time_t sec_in_utc, const TIME_ZONE_INFO *sp) +{ + const TRAN_TYPE_INFO *ttisp; + const LS_INFO *lp; + long corr= 0; + int hit= 0; + int i; + + /* + Find proper transition (and its local time type) for our sec_in_utc value. + Funny but again by separating this step in function we receive code + which very close to glibc's code. No wonder since they obviously use + the same base and all steps are sensible. + */ + ttisp= find_transition_type(sec_in_utc, sp); + + /* + Let us find leap correction for our sec_in_utc value and number of extra + secs to add to this minute. + This loop is rarely used because most users will use time zones without + leap seconds, and even in case when we have such time zone there won't + be many iterations (we have about 22 corrections at this moment (2004)). + */ + for ( i= sp->leapcnt; i-- > 0; ) + { + lp= &sp->lsis[i]; + if (sec_in_utc >= lp->ls_trans) + { + if (sec_in_utc == lp->ls_trans) + { + hit= ((i == 0 && lp->ls_corr > 0) || + lp->ls_corr > sp->lsis[i - 1].ls_corr); + if (hit) + { + while (i > 0 && + sp->lsis[i].ls_trans == sp->lsis[i - 1].ls_trans + 1 && + sp->lsis[i].ls_corr == sp->lsis[i - 1].ls_corr + 1) + { + hit++; + i--; + } + } + } + corr= lp->ls_corr; + break; + } + } + + sec_to_TIME(tmp, sec_in_utc, ttisp->tt_gmtoff - corr); + + tmp->second+= hit; +} + + +/* + Converts local time in broken down representation to local + time zone analog of my_time_t represenation. + + SYNOPSIS + sec_since_epoch() + year, mon, mday, hour, min, sec - broken down representation. + + DESCRIPTION + Converts time in broken down representation to my_time_t representation + ignoring time zone. Note that we cannot convert back some valid _local_ + times near ends of my_time_t range because of my_time_t overflow. But we + ignore this fact now since MySQL will never pass such argument. + + RETURN VALUE + Seconds since epoch time representation. +*/ +static my_time_t +sec_since_epoch(int year, int mon, int mday, int hour, int min ,int sec) +{ +#ifndef WE_WANT_TO_HANDLE_UNORMALIZED_DATES + /* + It turns out that only whenever month is normalized or unnormalized + plays role. + */ + DBUG_ASSERT(mon > 0 && mon < 13); + long days= year * DAYS_PER_NYEAR - EPOCH_YEAR * DAYS_PER_NYEAR + + LEAPS_THRU_END_OF(year - 1) - + LEAPS_THRU_END_OF(EPOCH_YEAR - 1); + days+= mon_starts[isleap(year)][mon - 1]; +#else + long norm_month= (mon - 1) % MONS_PER_YEAR; + long a_year= year + (mon - 1)/MONS_PER_YEAR - (int)(norm_month < 0); + long days= a_year * DAYS_PER_NYEAR - EPOCH_YEAR * DAYS_PER_NYEAR + + LEAPS_THRU_END_OF(a_year - 1) - + LEAPS_THRU_END_OF(EPOCH_YEAR - 1); + days+= mon_starts[isleap(a_year)] + [norm_month + (norm_month < 0 ? MONS_PER_YEAR : 0)]; +#endif + days+= mday - 1; + + return ((days * HOURS_PER_DAY + hour) * MINS_PER_HOUR + min) * + SECS_PER_MIN + sec; +} + + +/* + Converts local time in broken down TIME representation to my_time_t + representation. + + SYNOPSIS + TIME_to_gmt_sec() + t - pointer to structure for broken down represenatation + sp - pointer to struct with time zone description + in_dst_time_gap - pointer to bool which is set to true if datetime + value passed doesn't really exist (i.e. falls into + spring time-gap) and is not touched otherwise. + + DESCRIPTION + This is mktime analog for MySQL. It is essentially different + from mktime (or hypotetical my_mktime) because: + - It has no idea about tm_isdst member so if it + has two answers it will give the smaller one + - If we are in spring time gap then it will return + beginning of the gap + - It can give wrong results near the ends of my_time_t due to + overflows, but we are safe since in MySQL we will never + call this function for such dates (its restriction for year + between 1970 and 2038 gives us several days of reserve). + - By default it doesn't support un-normalized input. But if + sec_since_epoch() function supports un-normalized dates + then this function should handle un-normalized input right, + altough it won't normalize structure TIME. + + Traditional approach to problem of conversion from broken down + representation to time_t is iterative. Both elsie's and glibc + implementation try to guess what time_t value should correspond to + this broken-down value. They perform localtime_r function on their + guessed value and then calculate the difference and try to improve + their guess. Elsie's code guesses time_t value in bit by bit manner, + Glibc's code tries to add difference between broken-down value + corresponding to guess and target broken-down value to current guess. + It also uses caching of last found correction... So Glibc's approach + is essentially faster but introduces some undetermenism (in case if + is_dst member of broken-down representation (tm struct) is not known + and we have two possible answers). + + We use completely different approach. It is better since it is both + faster than iterative implementations and fully determenistic. If you + look at my_time_t to TIME conversion then you'll find that it consist + of two steps: + The first is calculating shifted my_time_t value and the second - TIME + calculation from shifted my_time_t value (well it is a bit simplified + picture). The part in which we are interested in is my_time_t -> shifted + my_time_t conversion. It is piecewise linear function which is defined + by combination of transition times as break points and times offset + as changing function parameter. The possible inverse function for this + converison would be ambiguos but with MySQL's restrictions we can use + some function which is the same as inverse function on unambigiuos + ranges and coincides with one of branches of inverse function in + other ranges. Thus we just need to build table which will determine + this shifted my_time_t -> my_time_t conversion similar to existing + (my_time_t -> shifted my_time_t table). We do this in + prepare_tz_info function. + + TODO + If we can even more improve this function. For doing this we will need to + build joined map of transitions and leap corrections for gmt_sec_to_TIME() + function (similar to revts/revtis). Under realistic assumptions about + frequency of transitions we can use the same array for TIME_to_gmt_sec(). + We need to implement special version of binary search for this. Such step + will be beneficial to CPU cache since we will decrease data-set used for + conversion twice. + + RETURN VALUE + Seconds in UTC since Epoch. + 0 in case of error. +*/ +static my_time_t +TIME_to_gmt_sec(const TIME *t, const TIME_ZONE_INFO *sp, bool *in_dst_time_gap) +{ + my_time_t local_t; + uint saved_seconds; + uint i; + + DBUG_ENTER("TIME_to_gmt_sec"); + + /* We need this for correct leap seconds handling */ + if (t->second < SECS_PER_MIN) + saved_seconds= 0; + else + saved_seconds= t->second; + + /* + NOTE If we want to convert full my_time_t range without MySQL + restrictions we should catch overflow here somehow. + */ + + local_t= sec_since_epoch(t->year, t->month, t->day, + t->hour, t->minute, + saved_seconds ? 0 : t->second); + + /* We have at least one range */ + DBUG_ASSERT(sp->revcnt >= 1); + + if (local_t < sp->revts[0] || local_t > sp->revts[sp->revcnt]) + { + /* + This means that source time can't be represented as my_time_t due to + limited my_time_t range. + */ + DBUG_RETURN(0); + } + + /* binary search for our range */ + i= find_time_range(local_t, sp->revts, sp->revcnt); + + if (sp->revtis[i].rt_type) + { + /* + Oops! We are in spring time gap. + May be we should return error here? + Now we are returning my_time_t value corresponding to the + beginning of the gap. + */ + *in_dst_time_gap= 1; + DBUG_RETURN(sp->revts[i] - sp->revtis[i].rt_offset + saved_seconds); + } + else + DBUG_RETURN(local_t - sp->revtis[i].rt_offset + saved_seconds); +} + + +/* + End of elsie derived code. +*/ + + +#if !defined(TESTTIME) && !defined(TZINFO2SQL) + +/* + String with names of SYSTEM time zone. +*/ +static const String tz_SYSTEM_name("SYSTEM", 6, &my_charset_latin1); + + +/* + Instance of this class represents local time zone used on this system + (specified by TZ environment variable or via any other system mechanism). + It uses system functions (localtime_r, my_system_gmt_sec) for conversion + and is always available. Because of this it is used by default - if there + were no explicit time zone specified. On the other hand because of this + conversion methods provided by this class is significantly slower and + possibly less multi-threaded-friendly than corresponding Time_zone_db + methods so the latter should be preffered there it is possible. +*/ +class Time_zone_system : public Time_zone +{ +public: + virtual my_time_t TIME_to_gmt_sec(const TIME *t, + bool *in_dst_time_gap) const; + virtual void gmt_sec_to_TIME(TIME *tmp, my_time_t t) const; + virtual const String * get_name() const; +}; + + +/* + Converts local time in system time zone in TIME representation + to its my_time_t representation. + + SYNOPSIS + TIME_to_gmt_sec() + t - pointer to TIME structure with local time in + broken-down representation. + in_dst_time_gap - pointer to bool which is set to true if datetime + value passed doesn't really exist (i.e. falls into + spring time-gap) and is not touched otherwise. + + DESCRIPTION + This method uses system function (localtime_r()) for conversion + local time in system time zone in TIME structure to its my_time_t + representation. Unlike the same function for Time_zone_db class + it it won't handle unnormalized input properly. Still it will + return lowest possible my_time_t in case of ambiguity or if we + provide time corresponding to the time-gap. + + You should call init_time() function before using this function. + + RETURN VALUE + Corresponding my_time_t value or 0 in case of error +*/ +my_time_t +Time_zone_system::TIME_to_gmt_sec(const TIME *t, bool *in_dst_time_gap) const +{ + long not_used; + return my_system_gmt_sec(t, ¬_used, in_dst_time_gap); +} + + +/* + Converts time from UTC seconds since Epoch (my_time_t) representation + to system local time zone broken-down representation. + + SYNOPSIS + gmt_sec_to_TIME() + tmp - pointer to TIME structure to fill-in + t - my_time_t value to be converted + + NOTE + We assume that value passed to this function will fit into time_t range + supported by localtime_r. This conversion is putting restriction on + TIMESTAMP range in MySQL. If we can get rid of SYSTEM time zone at least + for interaction with client then we can extend TIMESTAMP range down to + the 1902 easily. +*/ +void +Time_zone_system::gmt_sec_to_TIME(TIME *tmp, my_time_t t) const +{ + struct tm tmp_tm; + time_t tmp_t= (time_t)t; + + localtime_r(&tmp_t, &tmp_tm); + localtime_to_TIME(tmp, &tmp_tm); + tmp->time_type= TIMESTAMP_DATETIME; +} + + +/* + Get name of time zone + + SYNOPSIS + get_name() + + RETURN VALUE + Name of time zone as String +*/ +const String * +Time_zone_system::get_name() const +{ + return &tz_SYSTEM_name; +} + + +/* + Instance of this class represents UTC time zone. It uses system gmtime_r + function for conversions and is always available. It is used only for + my_time_t -> TIME conversions in various UTC_... functions, it is not + intended for TIME -> my_time_t conversions and shouldn't be exposed to user. +*/ +class Time_zone_utc : public Time_zone +{ +public: + virtual my_time_t TIME_to_gmt_sec(const TIME *t, + bool *in_dst_time_gap) const; + virtual void gmt_sec_to_TIME(TIME *tmp, my_time_t t) const; + virtual const String * get_name() const; +}; + + +/* + Convert UTC time from TIME representation to its my_time_t representation. + + SYNOPSIS + TIME_to_gmt_sec() + t - pointer to TIME structure with local time + in broken-down representation. + in_dst_time_gap - pointer to bool which is set to true if datetime + value passed doesn't really exist (i.e. falls into + spring time-gap) and is not touched otherwise. + + DESCRIPTION + Since Time_zone_utc is used only internally for my_time_t -> TIME + conversions, this function of Time_zone interface is not implemented for + this class and should not be called. + + RETURN VALUE + 0 +*/ +my_time_t +Time_zone_utc::TIME_to_gmt_sec(const TIME *t, bool *in_dst_time_gap) const +{ + /* Should be never called */ + DBUG_ASSERT(0); + return 0; +} + + +/* + Converts time from UTC seconds since Epoch (my_time_t) representation + to broken-down representation (also in UTC). + + SYNOPSIS + gmt_sec_to_TIME() + tmp - pointer to TIME structure to fill-in + t - my_time_t value to be converted + + NOTE + See note for apropriate Time_zone_system method. +*/ +void +Time_zone_utc::gmt_sec_to_TIME(TIME *tmp, my_time_t t) const +{ + struct tm tmp_tm; + time_t tmp_t= (time_t)t; + gmtime_r(&tmp_t, &tmp_tm); + localtime_to_TIME(tmp, &tmp_tm); + tmp->time_type= TIMESTAMP_DATETIME; +} + + +/* + Get name of time zone + + SYNOPSIS + get_name() + + DESCRIPTION + Since Time_zone_utc is used only internally by SQL's UTC_* functions it + is not accessible directly, and hence this function of Time_zone + interface is not implemented for this class and should not be called. + + RETURN VALUE + 0 +*/ +const String * +Time_zone_utc::get_name() const +{ + /* Should be never called */ + DBUG_ASSERT(0); + return 0; +} + + +/* + Instance of this class represents some time zone which is + described in mysql.time_zone family of tables. +*/ +class Time_zone_db : public Time_zone +{ +public: + Time_zone_db(TIME_ZONE_INFO *tz_info_arg, const String * tz_name_arg); + virtual my_time_t TIME_to_gmt_sec(const TIME *t, + bool *in_dst_time_gap) const; + virtual void gmt_sec_to_TIME(TIME *tmp, my_time_t t) const; + virtual const String * get_name() const; +private: + TIME_ZONE_INFO *tz_info; + const String *tz_name; +}; + + +/* + Initializes object representing time zone described by mysql.time_zone + tables. + + SYNOPSIS + Time_zone_db() + tz_info_arg - pointer to TIME_ZONE_INFO structure which is filled + according to db or other time zone description + (for example by my_tz_init()). + Several Time_zone_db instances can share one + TIME_ZONE_INFO structure. + tz_name_arg - name of time zone. +*/ +Time_zone_db::Time_zone_db(TIME_ZONE_INFO *tz_info_arg, + const String *tz_name_arg): + tz_info(tz_info_arg), tz_name(tz_name_arg) +{ +} + + +/* + Converts local time in time zone described from TIME + representation to its my_time_t representation. + + SYNOPSIS + TIME_to_gmt_sec() + t - pointer to TIME structure with local time + in broken-down representation. + in_dst_time_gap - pointer to bool which is set to true if datetime + value passed doesn't really exist (i.e. falls into + spring time-gap) and is not touched otherwise. + + DESCRIPTION + Please see ::TIME_to_gmt_sec for function description and + parameter restrictions. + + RETURN VALUE + Corresponding my_time_t value or 0 in case of error +*/ +my_time_t +Time_zone_db::TIME_to_gmt_sec(const TIME *t, bool *in_dst_time_gap) const +{ + return ::TIME_to_gmt_sec(t, tz_info, in_dst_time_gap); +} + + +/* + Converts time from UTC seconds since Epoch (my_time_t) representation + to local time zone described in broken-down representation. + + SYNOPSIS + gmt_sec_to_TIME() + tmp - pointer to TIME structure to fill-in + t - my_time_t value to be converted +*/ +void +Time_zone_db::gmt_sec_to_TIME(TIME *tmp, my_time_t t) const +{ + ::gmt_sec_to_TIME(tmp, t, tz_info); +} + + +/* + Get name of time zone + + SYNOPSIS + get_name() + + RETURN VALUE + Name of time zone as ASCIIZ-string +*/ +const String * +Time_zone_db::get_name() const +{ + return tz_name; +} + + +/* + Instance of this class represents time zone which + was specified as offset from UTC. +*/ +class Time_zone_offset : public Time_zone +{ +public: + Time_zone_offset(long tz_offset_arg); + virtual my_time_t TIME_to_gmt_sec(const TIME *t, + bool *in_dst_time_gap) const; + virtual void gmt_sec_to_TIME(TIME *tmp, my_time_t t) const; + virtual const String * get_name() const; + /* + This have to be public because we want to be able to access it from + my_offset_tzs_get_key() function + */ + long offset; +private: + /* Extra reserve because of snprintf */ + char name_buff[7+16]; + String name; +}; + + +/* + Initializes object representing time zone described by its offset from UTC. + + SYNOPSIS + Time_zone_offset() + tz_offset_arg - offset from UTC in seconds. + Positive for direction to east. +*/ +Time_zone_offset::Time_zone_offset(long tz_offset_arg): + offset(tz_offset_arg) +{ + uint hours= abs((int)(offset / SECS_PER_HOUR)); + uint minutes= abs((int)(offset % SECS_PER_HOUR / SECS_PER_MIN)); + ulong length= my_snprintf(name_buff, sizeof(name_buff), "%s%02d:%02d", + (offset>=0) ? "+" : "-", hours, minutes); + name.set(name_buff, length, &my_charset_latin1); +} + + +/* + Converts local time in time zone described as offset from UTC + from TIME representation to its my_time_t representation. + + SYNOPSIS + TIME_to_gmt_sec() + t - pointer to TIME structure with local time + in broken-down representation. + in_dst_time_gap - pointer to bool which should be set to true if + datetime value passed doesn't really exist + (i.e. falls into spring time-gap) and is not + touched otherwise. + It is not really used in this class. + + RETURN VALUE + Corresponding my_time_t value or 0 in case of error +*/ +my_time_t +Time_zone_offset::TIME_to_gmt_sec(const TIME *t, bool *in_dst_time_gap) const +{ + return sec_since_epoch(t->year, t->month, t->day, + t->hour, t->minute, t->second) - + offset; +} + + +/* + Converts time from UTC seconds since Epoch (my_time_t) representation + to local time zone described as offset from UTC and in broken-down + representation. + + SYNOPSIS + gmt_sec_to_TIME() + tmp - pointer to TIME structure to fill-in + t - my_time_t value to be converted +*/ +void +Time_zone_offset::gmt_sec_to_TIME(TIME *tmp, my_time_t t) const +{ + sec_to_TIME(tmp, t, offset); +} + + +/* + Get name of time zone + + SYNOPSIS + get_name() + + RETURN VALUE + Name of time zone as pointer to String object +*/ +const String * +Time_zone_offset::get_name() const +{ + return &name; +} + + +static Time_zone_utc tz_UTC; +static Time_zone_system tz_SYSTEM; + +Time_zone *my_tz_UTC= &tz_UTC; +Time_zone *my_tz_SYSTEM= &tz_SYSTEM; + +static HASH tz_names; +static HASH offset_tzs; +static MEM_ROOT tz_storage; + +/* + These mutex protects offset_tzs and tz_storage. + These protection needed only when we are trying to set + time zone which is specified as offset, and searching for existing + time zone in offset_tzs or creating if it didn't existed before in + tz_storage. So contention is low. +*/ +static pthread_mutex_t tz_LOCK; +static bool tz_inited= 0; + +/* + This two static variables are inteded for holding info about leap seconds + shared by all time zones. +*/ +static uint tz_leapcnt= 0; +static LS_INFO *tz_lsis= 0; + + +typedef struct st_tz_names_entry: public Sql_alloc +{ + String name; + Time_zone *tz; +} TZ_NAMES_ENTRY; + + +/* + We are going to call both of these functions from C code so + they should obey C calling conventions. +*/ + +extern "C" byte* my_tz_names_get_key(TZ_NAMES_ENTRY *entry, uint *length, + my_bool not_used __attribute__((unused))) +{ + *length= entry->name.length(); + return (byte*) entry->name.ptr(); +} + +extern "C" byte* my_offset_tzs_get_key(Time_zone_offset *entry, uint *length, + my_bool not_used __attribute__((unused))) +{ + *length= sizeof(long); + return (byte*) &entry->offset; +} + + +/* + Initialize time zone support infrastructure. + + SYNOPSIS + my_tz_init() + thd - current thread object + default_tzname - default time zone or 0 if none. + bootstrap - indicates whenever we are in bootstrap mode + + DESCRIPTION + This function will init memory structures needed for time zone support, + it will register mandatory SYSTEM time zone in them. It will try to open + mysql.time_zone_leap_seconds table and and load information which further + will be shared among all time zones loaded. It will also try to load + information about default time zone. If system tables with time zone + descriptions don't exist it won't fail (unless default_tzname is time zone + from tables). If bootstrap parameter is true then this routine assumes that + we are in bootstrap mode and won't load time zone descriptions unless someone + specifies default time zone which is supposedly stored in those tables. + It'll also set default time zone if it is specified. + + RETURN VALUES + 0 - ok + 1 - Error +*/ +my_bool +my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap) +{ + THD *thd; + TABLE_LIST tables; + TABLE *table; + TABLE *lock_ptr; + MYSQL_LOCK *lock; + TZ_NAMES_ENTRY *tmp_tzname; + my_bool return_val= 1; + int res; + uint not_used; + DBUG_ENTER("my_tz_init"); + + /* + To be able to run this from boot, we allocate a temporary THD + */ + if (!(thd= new THD)) + DBUG_RETURN(1); + thd->store_globals(); + + /* Init all memory structures that require explicit destruction */ + if (hash_init(&tz_names, &my_charset_latin1, 20, + 0, 0, (hash_get_key)my_tz_names_get_key, 0, 0)) + { + sql_print_error("Fatal error: OOM while initializing time zones"); + goto end; + } + if (hash_init(&offset_tzs, &my_charset_latin1, 26, 0, 0, + (hash_get_key)my_offset_tzs_get_key, 0, 0)) + { + sql_print_error("Fatal error: OOM while initializing time zones"); + hash_free(&tz_names); + goto end; + } + init_alloc_root(&tz_storage, 32 * 1024, 0); + VOID(pthread_mutex_init(&tz_LOCK, MY_MUTEX_INIT_FAST)); + tz_inited= 1; + + /* Add 'SYSTEM' time zone to tz_names hash */ + if (!(tmp_tzname= new (&tz_storage) TZ_NAMES_ENTRY())) + { + sql_print_error("Fatal error: OOM while initializing time zones"); + goto end_with_cleanup; + } + tmp_tzname->name.set("SYSTEM", 6, &my_charset_latin1); + tmp_tzname->tz= my_tz_SYSTEM; + if (my_hash_insert(&tz_names, (const byte *)tmp_tzname)) + { + sql_print_error("Fatal error: OOM while initializing time zones"); + goto end_with_cleanup; + } + + if (bootstrap) + { + /* If we are in bootstrap mode we should not load time zone tables */ + return_val= 0; + goto end_with_setting_default_tz; + } + + /* + After this point all memory structures are inited and we even can live + without time zone description tables. Now try to load information about + leap seconds shared by all time zones. + */ + + thd->db= my_strdup("mysql",MYF(0)); + thd->db_length= 5; // Safety + bzero((char*) &tables,sizeof(tables)); + tables.alias= tables.real_name= (char*)"time_zone_leap_second"; + tables.lock_type= TL_READ; + tables.db= thd->db; + + if (open_tables(thd, &tables, ¬_used)) + { + sql_print_error("Warning: Can't open time zone table: %s " + "trying to live without them", thd->net.last_error); + /* We will try emulate that everything is ok */ + return_val= 0; + goto end_with_setting_default_tz; + } + + lock_ptr= tables.table; + if (!(lock= mysql_lock_tables(thd, &lock_ptr, 1))) + { + sql_print_error("Fatal error: Can't lock time zone table: %s", + thd->net.last_error); + goto end_with_cleanup; + } + + + /* + Now we are going to load leap seconds descriptions that are shared + between all time zones that use them. We are using index for getting + records in proper order. Since we share the same MEM_ROOT between + all time zones we just allocate enough memory for it first. + */ + if (!(tz_lsis= (LS_INFO*) alloc_root(&tz_storage, + sizeof(LS_INFO) * TZ_MAX_LEAPS))) + { + sql_print_error("Fatal error: Out of memory while loading " + "mysql.time_zone_leap_second table"); + goto end_with_unlock; + } + + table= tables.table; + table->file->index_init(0); + tz_leapcnt= 0; + + res= table->file->index_first(table->record[0]); + + while (!res) + { + if (tz_leapcnt + 1 > TZ_MAX_LEAPS) + { + sql_print_error("Fatal error: While loading mysql.time_zone_leap_second" + " table: too much leaps"); + table->file->index_end(); + goto end_with_unlock; + } + + tz_lsis[tz_leapcnt].ls_trans= (my_time_t)table->field[0]->val_int(); + tz_lsis[tz_leapcnt].ls_corr= (long)table->field[1]->val_int(); + + tz_leapcnt++; + + DBUG_PRINT("info", + ("time_zone_leap_second table: tz_leapcnt=%u tt_time=%lld offset=%ld", + tz_leapcnt, (longlong)tz_lsis[tz_leapcnt-1].ls_trans, + tz_lsis[tz_leapcnt-1].ls_corr)); + + res= table->file->index_next(table->record[0]); + } + + table->file->index_end(); + + if (res != HA_ERR_END_OF_FILE) + { + sql_print_error("Fatal error: Error while loading " + "mysql.time_zone_leap_second table"); + goto end_with_unlock; + } + + /* + Loading of info about leap seconds succeeded + */ + + return_val= 0; + + +end_with_unlock: + mysql_unlock_tables(thd, lock); + thd->version--; /* Force close to free memory */ + +end_with_setting_default_tz: + /* If not an error and have default time zone try to load it */ + if (!return_val && default_tzname) + { + String tzname(default_tzname, &my_charset_latin1); + if (!(global_system_variables.time_zone= my_tz_find(thd, &tzname))) + { + sql_print_error("Fatal error: Illegal or unknown default time zone '%s'", + default_tzname); + return_val= 1; + } + } + +end_with_cleanup: + + /* if there were error free time zone describing structs */ + if (return_val) + my_tz_free(); +end: + close_thread_tables(thd); + delete thd; + if (org_thd) + org_thd->store_globals(); /* purecov: inspected */ + else + { + /* Remember that we don't have a THD */ + my_pthread_setspecific_ptr(THR_THD, 0); + my_pthread_setspecific_ptr(THR_MALLOC, 0); + } + DBUG_RETURN(return_val); +} + + +/* + Free resources used by time zone support infrastructure. + + SYNOPSIS + my_tz_free() +*/ + +void my_tz_free() +{ + if (tz_inited) + { + tz_inited= 0; + VOID(pthread_mutex_destroy(&tz_LOCK)); + hash_free(&offset_tzs); + hash_free(&tz_names); + free_root(&tz_storage, MYF(0)); + } +} + + +/* + Load time zone description from system tables. + + SYNOPSIS + tz_load_from_db() + thd - current thread object + tz_name - name of time zone that should be loaded. + + DESCRIPTION + This function will try to open system tables describing time zones + and to load information about time zone specified. It will also update + information in hash used for time zones lookup. + + RETURN VALUES + Returns pointer to newly created Time_zone object or 0 in case of error. + +*/ +static Time_zone* +tz_load_from_db(THD *thd, const String *tz_name) +{ + TABLE_LIST tables[4]; + TABLE *table= 0; + TABLE *lock_ptr[4]; + MYSQL_LOCK *lock; + char system_db_name[]= "mysql"; + char *db_save; + uint db_length_save; + TIME_ZONE_INFO *tz_info; + TZ_NAMES_ENTRY *tmp_tzname; + Time_zone *return_val= 0; + int res; + uint tzid, ttid; + my_time_t ttime; + char buff[MAX_FIELD_WIDTH]; + String abbr(buff, sizeof(buff), &my_charset_latin1); + char *alloc_buff, *tz_name_buff; + /* + Temporary arrays that are used for loading of data for filling + TIME_ZONE_INFO structure + */ + my_time_t ats[TZ_MAX_TIMES]; + unsigned char types[TZ_MAX_TIMES]; + TRAN_TYPE_INFO ttis[TZ_MAX_TYPES]; +#ifdef ABBR_ARE_USED + char chars[max(TZ_MAX_CHARS + 1, (2 * (MY_TZNAME_MAX + 1)))]; +#endif + uint not_used; + + DBUG_ENTER("tz_load_from_db"); + + + /* 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))) + { + sql_print_error("Error: Out of memory while loading time zone " + "description"); + return 0; + } + tz_info= (TIME_ZONE_INFO *)alloc_buff; + bzero(tz_info, sizeof(TIME_ZONE_INFO)); + tz_name_buff= alloc_buff + sizeof(TIME_ZONE_INFO); + /* + By writing zero to the end we guarantee that we can call ptr() + instead of c_ptr() for time zone name. + */ + strmake(tz_name_buff, tz_name->ptr(), tz_name->length()); + + /* + Open and lock time zone description tables + */ + db_save= thd->db; + db_length_save= thd->db_length; + thd->db= system_db_name; + thd->db_length= 5; + + bzero((char*) &tables,sizeof(tables)); + tables[0].alias= tables[0].real_name= (char*)"time_zone_name"; + tables[1].alias= tables[1].real_name= (char*)"time_zone"; + tables[2].alias= tables[2].real_name= (char*)"time_zone_transition"; + tables[3].alias= tables[3].real_name= (char*)"time_zone_transition_type"; + tables[0].next= tables+1; + tables[1].next= tables+2; + tables[2].next= tables+3; + tables[0].lock_type= tables[1].lock_type= tables[2].lock_type= + tables[3].lock_type= TL_READ; + tables[0].db= tables[1].db= tables[2].db= tables[3].db= thd->db; + if (open_tables(thd, tables, ¬_used)) + { + sql_print_error("Error: Can't open time zone tables: %s", + thd->net.last_error); + goto end; + } + + lock_ptr[0]= tables[0].table; + lock_ptr[1]= tables[1].table; + lock_ptr[2]= tables[2].table; + lock_ptr[3]= tables[3].table; + if (!(lock= mysql_lock_tables(thd, lock_ptr, 4))) + { + sql_print_error("Error: Can't lock time zone tables: %s", + thd->net.last_error); + goto end_with_close; + } + + /* + Let us find out time zone id by its name (there is only one index + and it is specifically for this purpose). + */ + table= tables[0].table; + + table->field[0]->store(tz_name->ptr(), tz_name->length(), &my_charset_latin1); + table->file->index_init(0); + + if (table->file->index_read(table->record[0], (byte*)table->field[0]->ptr, + 0, HA_READ_KEY_EXACT)) + { + sql_print_error("Error: Can't find description of time zone."); + goto end_with_unlock; + } + + tzid= table->field[1]->val_int(); + + table->file->index_end(); + + /* + Now we need to lookup record in mysql.time_zone table in order to + understand whenever this timezone uses leap seconds (again we are + using the only index in this table). + */ + table= tables[1].table; + table->field[0]->store((longlong)tzid); + table->file->index_init(0); + + if (table->file->index_read(table->record[0], (byte*)table->field[0]->ptr, + 0, HA_READ_KEY_EXACT)) + { + sql_print_error("Error: Can't find description of time zone."); + goto end_with_unlock; + } + + /* If Uses_leap_seconds == 'Y' */ + if (table->field[1]->val_int() == 1) + { + tz_info->leapcnt= tz_leapcnt; + tz_info->lsis= tz_lsis; + } + + table->file->index_end(); + + /* + Now we will iterate through records for out time zone in + mysql.time_zone_transition_type table. Because we want records + only for our time zone guess what are we doing? + Right - using special index. + */ + table= tables[3].table; + table->field[0]->store((longlong)tzid); + table->file->index_init(0); + + // FIXME Is there any better approach than explicitly specifying 4 ??? + res= table->file->index_read(table->record[0], (byte*)table->field[0]->ptr, + 4, HA_READ_KEY_EXACT); + while (!res) + { + ttid= table->field[1]->val_int(); + + if (ttid > TZ_MAX_TYPES) + { + sql_print_error("Error while loading time zone description from " + "mysql.time_zone_transition_type table: too big " + "transition type id"); + goto end_with_unlock; + } + + ttis[ttid].tt_gmtoff= table->field[2]->val_int(); + ttis[ttid].tt_isdst= (table->field[3]->val_int() > 0); + +#ifdef ABBR_ARE_USED + // FIXME should we do something with duplicates here ? + table->field[4]->val_str(&abbr, &abbr); + if (tz_info->charcnt + abbr.length() + 1 > sizeof(chars)) + { + sql_print_error("Error while loading time zone description from " + "mysql.time_zone_transition_type table: not enough " + "room for abbreviations"); + goto end_with_unlock; + } + ttis[ttid].tt_abbrind= tz_info->charcnt; + memcpy(chars + tz_info->charcnt, abbr.ptr(), abbr.length()); + tz_info->charcnt+= abbr.length(); + chars[tz_info->charcnt]= 0; + tz_info->charcnt++; + + DBUG_PRINT("info", + ("time_zone_transition_type table: tz_id=%u tt_id=%u tt_gmtoff=%ld " + "abbr='%s' tt_isdst=%u", tzid, ttid, ttis[ttid].tt_gmtoff, + chars + ttis[ttid].tt_abbrind, ttis[ttid].tt_isdst)); +#else + DBUG_PRINT("info", + ("time_zone_transition_type table: tz_id=%u tt_id=%u tt_gmtoff=%ld " + "tt_isdst=%u", tzid, ttid, ttis[ttid].tt_gmtoff, ttis[ttid].tt_isdst)); +#endif + + /* ttid is increasing because we are reading using index */ + DBUG_ASSERT(ttid >= tz_info->typecnt); + + tz_info->typecnt= ttid + 1; + + res= table->file->index_next_same(table->record[0], + (byte*)table->field[0]->ptr, 4); + } + + if (res != HA_ERR_END_OF_FILE) + { + sql_print_error("Error while loading time zone description from " + "mysql.time_zone_transition_type table"); + goto end_with_unlock; + } + + table->file->index_end(); + + + /* + At last we are doing the same thing for records in + mysql.time_zone_transition table. Here we additionaly need records + in ascending order by index scan also satisfies us. + */ + table= tables[2].table; + table->field[0]->store((longlong)tzid); + table->file->index_init(0); + + // FIXME Is there any better approach than explicitly specifying 4 ??? + res= table->file->index_read(table->record[0], (byte*)table->field[0]->ptr, + 4, HA_READ_KEY_EXACT); + while (!res) + { + ttime= (my_time_t)table->field[1]->val_int(); + ttid= (uint)table->field[2]->val_int(); + + if (tz_info->timecnt + 1 > TZ_MAX_TIMES) + { + sql_print_error("Error while loading time zone description from " + "mysql.time_zone_transition table: " + "too much transitions"); + goto end_with_unlock; + } + if (ttid + 1 > tz_info->typecnt) + { + sql_print_error("Error while loading time zone description from " + "mysql.time_zone_transition table: " + "bad transition type id"); + goto end_with_unlock; + } + + ats[tz_info->timecnt]= ttime; + types[tz_info->timecnt]= ttid; + tz_info->timecnt++; + + DBUG_PRINT("info", + ("time_zone_transition table: tz_id=%u tt_time=%lld tt_id=%u", + tzid, (longlong)ttime, ttid)); + + res= table->file->index_next_same(table->record[0], + (byte*)table->field[0]->ptr, 4); + } + + /* + We have to allow HA_ERR_KEY_NOT_FOUND because some time zones + for example UTC have no transitons. + */ + if (res != HA_ERR_END_OF_FILE && res != HA_ERR_KEY_NOT_FOUND) + { + sql_print_error("Error while loading time zone description from " + "mysql.time_zone_transition table"); + goto end_with_unlock; + } + + table->file->index_end(); + table= 0; + + /* + Now we will allocate memory and init TIME_ZONE_INFO structure. + */ + if (!(alloc_buff= alloc_root(&tz_storage, + ALIGN_SIZE(sizeof(my_time_t) * + tz_info->timecnt) + + ALIGN_SIZE(tz_info->timecnt) + +#ifdef ABBR_ARE_USED + ALIGN_SIZE(tz_info->charcnt) + +#endif + sizeof(TRAN_TYPE_INFO) * tz_info->typecnt))) + { + sql_print_error("Error: Out of memory while loading time zone " + "description"); + goto end_with_unlock; + } + + + tz_info->ats= (my_time_t *)alloc_buff; + memcpy(tz_info->ats, ats, tz_info->timecnt * sizeof(my_time_t)); + alloc_buff+= ALIGN_SIZE(sizeof(my_time_t) * tz_info->timecnt); + tz_info->types= (unsigned char *)alloc_buff; + memcpy(tz_info->types, types, tz_info->timecnt); + alloc_buff+= ALIGN_SIZE(tz_info->timecnt); +#ifdef ABBR_ARE_USED + tz_info->chars= alloc_buff; + memcpy(tz_info->chars, chars, tz_info->charcnt); + alloc_buff+= ALIGN_SIZE(tz_info->charcnt); +#endif + tz_info->ttis= (TRAN_TYPE_INFO *)alloc_buff; + memcpy(tz_info->ttis, ttis, tz_info->typecnt * sizeof(TRAN_TYPE_INFO)); + + /* + Let us check how correct our time zone description and build + reversed map. We don't check for tz->timecnt < 1 since it ok for GMT. + */ + if (tz_info->typecnt < 1) + { + sql_print_error("Error: loading time zone without transition types"); + goto end_with_unlock; + } + if (prepare_tz_info(tz_info, &tz_storage)) + { + sql_print_error("Error: Unable to build mktime map for time zone"); + goto end_with_unlock; + } + + + if (!(tmp_tzname= new (&tz_storage) TZ_NAMES_ENTRY()) || + !(tmp_tzname->tz= new (&tz_storage) Time_zone_db(tz_info, + &(tmp_tzname->name))) || + (tmp_tzname->name.set(tz_name_buff, tz_name->length(), + &my_charset_latin1), + my_hash_insert(&tz_names, (const byte *)tmp_tzname))) + { + sql_print_error("Error: Out of memory while loading time zone"); + goto end_with_unlock; + } + + /* + Loading of time zone succeeded + */ + return_val= tmp_tzname->tz; + +end_with_unlock: + + if (table) + table->file->index_end(); + + mysql_unlock_tables(thd, lock); + +end_with_close: + close_thread_tables(thd); + +end: + thd->db= db_save; + thd->db_length= db_length_save; + DBUG_RETURN(return_val); +} + + +/* + Parse string that specifies time zone as offset from UTC. + + SYNOPSIS + str_to_offset() + str - pointer to string which contains offset + length - length of string + offset - out parameter for storing found offset in seconds. + + DESCRIPTION + This function parses string which contains time zone offset + in form similar to '+10:00' and converts found value to + seconds from UTC form (east is positive). + + RETURN VALUE + 0 - Ok + 1 - String doesn't contain valid time zone offset +*/ +my_bool +str_to_offset(const char *str, uint length, long *offset) +{ + const char *end= str + length; + my_bool negative; + ulong number_tmp; + long offset_tmp; + + if (length < 4) + return 1; + + if (*str == '+') + negative= 0; + else if (*str == '-') + negative= 1; + else + return 1; + str++; + + number_tmp= 0; + + while (str < end && my_isdigit(&my_charset_latin1, *str)) + { + number_tmp= number_tmp*10 + *str - '0'; + str++; + } + + if (str + 1 >= end || *str != ':') + return 1; + str++; + + offset_tmp = number_tmp * MINS_PER_HOUR; number_tmp= 0; + + while (str < end && my_isdigit(&my_charset_latin1, *str)) + { + number_tmp= number_tmp * 10 + *str - '0'; + str++; + } + + if (str != end) + return 1; + + offset_tmp= (offset_tmp + number_tmp) * SECS_PER_MIN; + + if (negative) + offset_tmp= -offset_tmp; + + /* + Check if offset is in range prescribed by standard + (from -12:59 to 13:00). + */ + + if (number_tmp > 59 || offset_tmp < -13 * SECS_PER_HOUR + 1 || + offset_tmp > 13 * SECS_PER_HOUR) + return 1; + + *offset= offset_tmp; + + return 0; +} + + +/* + Get Time_zone object for specified time zone. + + SYNOPSIS + my_tz_find() + thd - current thread + name - time zone specification + + DESCRIPTION + This function checks if name is one of time zones described in db, + predefined SYSTEM time zone or valid time zone specification as + offset from UTC (In last case it will create proper Time_zone_offset + object if there were not any.). If name is ok it returns corresponding + Time_zone object. + + Clients of this function are not responsible for releasing resources + occupied by returned Time_zone object so they can just forget pointers + to Time_zone object if they are not needed longer. + + Other important property of this function: if some Time_zone found once + it will be for sure found later, so this function can also be used for + checking if proper Time_zone object exists (and if there will be error + it will be reported during first call). + + If name pointer is 0 then this function returns 0 (this allows to pass 0 + values as parameter without additional external check and this property + is used by @@time_zone variable handling code). + + It will perform lookup in system tables (mysql.time_zone*) if needed. + + RETURN VALUE + Pointer to corresponding Time_zone object. 0 - in case of bad time zone + specification or other error. + +*/ +Time_zone * +my_tz_find(THD *thd, const String * name) +{ + TZ_NAMES_ENTRY *tmp_tzname; + Time_zone *result_tz= 0; + long offset; + + DBUG_ENTER("my_tz_find"); + DBUG_PRINT("enter", ("time zone name='%s'", + name ? ((String *)name)->c_ptr() : "NULL")); + + if (!name) + DBUG_RETURN(0); + + VOID(pthread_mutex_lock(&tz_LOCK)); + + if (!str_to_offset(name->ptr(), name->length(), &offset)) + { + + if (!(result_tz= (Time_zone_offset *)hash_search(&offset_tzs, + (const byte *)&offset, + sizeof(long)))) + { + DBUG_PRINT("info", ("Creating new Time_zone_offset object")); + + if (!(result_tz= new (&tz_storage) Time_zone_offset(offset)) || + my_hash_insert(&offset_tzs, (const byte *) result_tz)) + { + sql_print_error("Fatal error: Out of memory " + "while setting new time zone"); + result_tz= 0; + } + } + } else { + if ((tmp_tzname= (TZ_NAMES_ENTRY *)hash_search(&tz_names, name->ptr(), + name->length()))) + result_tz= tmp_tzname->tz; + else + result_tz= tz_load_from_db(thd, name); + } + + VOID(pthread_mutex_unlock(&tz_LOCK)); + + DBUG_RETURN(result_tz); +} + +#endif /* !defined(TESTTIME) && !defined(TZINFO2SQL) */ + + +#ifdef TZINFO2SQL +/* + This code belongs to mysql_tzinfo_to_sql converter command line utility. + This utility should be used by db admin for populating mysql.time_zone + tables. +*/ + + +/* + Print info about time zone described by TIME_ZONE_INFO struct as + SQL statements populating mysql.time_zone* tables. + + SYNOPSIS + print_tz_as_sql() + tz_name - name of time zone + sp - structure describing time zone +*/ +void +print_tz_as_sql(const char* tz_name, const TIME_ZONE_INFO *sp) +{ + uint i; + + /* Here we assume that all time zones have same leap correction tables */ + printf("INSERT INTO time_zone (Use_leap_seconds) VALUES ('%s');\n", + sp->leapcnt ? "Y" : "N"); + printf("SET @time_zone_id= LAST_INSERT_ID();\n"); + printf("INSERT INTO time_zone_name (Name, Time_zone_id) VALUES \ +('%s', @time_zone_id);\n", tz_name); + + if (sp->timecnt) + { + printf("INSERT INTO time_zone_transition \ +(Time_zone_id, Transition_time, Transition_type_id) VALUES\n"); + for (i= 0; i < sp->timecnt; i++) + printf("%s(@time_zone_id, %ld, %u)\n", (i == 0 ? " " : ","), sp->ats[i], + (uint)sp->types[i]); + printf(";\n"); + } + + printf("INSERT INTO time_zone_transition_type \ +(Time_zone_id, Transition_type_id, Offset, Is_DST, Abbreviation) VALUES\n"); + + for (i= 0; i < sp->typecnt; i++) + printf("%s(@time_zone_id, %u, %ld, %d, '%s')\n", (i == 0 ? " " : ","), i, + sp->ttis[i].tt_gmtoff, sp->ttis[i].tt_isdst, + sp->chars + sp->ttis[i].tt_abbrind); + printf(";\n"); +} + + +/* + Print info about leap seconds in time zone as SQL statements + populating mysql.time_zone_leap_second table. + + SYNOPSIS + print_tz_leaps_as_sql() + sp - structure describing time zone +*/ +void +print_tz_leaps_as_sql(const TIME_ZONE_INFO *sp) +{ + uint i; + + /* + We are assuming that there are only one list of leap seconds + For all timezones. + */ + printf("TRUNCATE TABLE time_zone_leap_second;\n"); + + if (sp->leapcnt) + { + printf("INSERT INTO time_zone_leap_second \ +(Transition_time, Correction) VALUES\n"); + for (i= 0; i < sp->leapcnt; i++) + printf("%s(%ld, %ld)\n", (i == 0 ? " " : ","), + sp->lsis[i].ls_trans, sp->lsis[i].ls_corr); + printf(";\n"); + } + + printf("ALTER TABLE time_zone_leap_second ORDER BY Transition_time;\n"); +} + + +/* + Some variables used as temporary or as parameters + in recursive scan_tz_dir() code. +*/ +TIME_ZONE_INFO tz_info; +MEM_ROOT tz_storage; +char fullname[FN_REFLEN + 1]; +char *root_name_end; + + +/* + Recursively scan zoneinfo directory and print all found time zone + descriptions as SQL. + + SYNOPSIS + scan_tz_dir() + name_end - pointer to end of path to directory to be searched. + + DESCRIPTION + This auxiliary recursive function also uses several global + variables as in parameters and for storing temporary values. + + fullname - path to directory that should be scanned. + root_name_end - pointer to place in fullname where part with + path to initial directory ends. + current_tz_id - last used time zone id + + RETURN VALUE + 0 - Ok, 1 - Fatal error + +*/ +my_bool +scan_tz_dir(char * name_end) +{ + MY_DIR *cur_dir; + char *name_end_tmp; + uint i; + + if (!(cur_dir= my_dir(fullname, MYF(MY_WANT_STAT)))) + return 1; + + name_end= strmake(name_end, "/", FN_REFLEN - (name_end - fullname)); + + for (i= 0; i < cur_dir->number_off_files; i++) + { + if (cur_dir->dir_entry[i].name[0] != '.') + { + name_end_tmp= strmake(name_end, cur_dir->dir_entry[i].name, + FN_REFLEN - (name_end - fullname)); + + if (MY_S_ISDIR(cur_dir->dir_entry[i].mystat->st_mode)) + { + if (scan_tz_dir(name_end_tmp)) + { + my_dirend(cur_dir); + return 1; + } + } + else if (MY_S_ISREG(cur_dir->dir_entry[i].mystat->st_mode)) + { + init_alloc_root(&tz_storage, 32768, 0); + if (!tz_load(fullname, &tz_info, &tz_storage)) + print_tz_as_sql(root_name_end + 1, &tz_info); + else + fprintf(stderr, + "Warning: Unable to load '%s' as time zone. Skipping it.\n", + fullname); + free_root(&tz_storage, MYF(0)); + } + else + fprintf(stderr, "Warning: '%s' is not regular file or directory\n", + fullname); + } + } + + my_dirend(cur_dir); + + return 0; +} + + +int +main(int argc, char **argv) +{ + MY_INIT(argv[0]); + + if (argc != 2 && argc != 3) + { + fprintf(stderr, "Usage:\n"); + fprintf(stderr, " %s timezonedir\n", argv[0]); + fprintf(stderr, " %s timezonefile timezonename\n", argv[0]); + fprintf(stderr, " %s --leap timezonefile\n", argv[0]); + return 1; + } + + if (argc == 2) + { + root_name_end= strmake(fullname, argv[1], FN_REFLEN); + + printf("TRUNCATE TABLE time_zone;\n"); + printf("TRUNCATE TABLE time_zone_name;\n"); + printf("TRUNCATE TABLE time_zone_transition;\n"); + printf("TRUNCATE TABLE time_zone_transition_type;\n"); + + if (scan_tz_dir(root_name_end)) + { + fprintf(stderr, "There were fatal errors during processing " + "of zoneinfo directory\n"); + return 1; + } + + printf("ALTER TABLE time_zone_transition " + "ORDER BY Time_zone_id, Transition_time;\n"); + printf("ALTER TABLE time_zone_transition_type " + "ORDER BY Time_zone_id, Transition_type_id;\n"); + } + else + { + init_alloc_root(&tz_storage, 32768, 0); + + if (strcmp(argv[1], "--leap") == 0) + { + if (tz_load(argv[2], &tz_info, &tz_storage)) + { + fprintf(stderr, "Problems with zoneinfo file '%s'\n", argv[2]); + return 1; + } + print_tz_leaps_as_sql(&tz_info); + } + else + { + if (tz_load(argv[1], &tz_info, &tz_storage)) + { + fprintf(stderr, "Problems with zoneinfo file '%s'\n", argv[2]); + return 1; + } + print_tz_as_sql(argv[2], &tz_info); + } + + free_root(&tz_storage, MYF(0)); + } + + return 0; +} + +#endif /* defined(TZINFO2SQL) */ + + +#ifdef TESTTIME + +/* + Some simple brute-force test wich allowed to catch a pair of bugs. + Also can provide interesting facts about system's time zone support + implementation. +*/ + +#ifndef CHAR_BIT +#define CHAR_BIT 8 +#endif + +#ifndef TYPE_BIT +#define TYPE_BIT(type) (sizeof (type) * CHAR_BIT) +#endif + +#ifndef TYPE_SIGNED +#define TYPE_SIGNED(type) (((type) -1) < 0) +#endif + +my_bool +is_equal_TIME_tm(const TIME* time_arg, const struct tm * tm_arg) +{ + return (time_arg->year == (uint)tm_arg->tm_year+TM_YEAR_BASE) && + (time_arg->month == (uint)tm_arg->tm_mon+1) && + (time_arg->day == (uint)tm_arg->tm_mday) && + (time_arg->hour == (uint)tm_arg->tm_hour) && + (time_arg->minute == (uint)tm_arg->tm_min) && + (time_arg->second == (uint)tm_arg->tm_sec) && + time_arg->second_part == 0; +} + + +int +main(int argc, char **argv) +{ + my_bool localtime_negative; + TIME_ZONE_INFO tz_info; + struct tm tmp; + TIME time_tmp; + time_t t, t1, t2; + char fullname[FN_REFLEN+1]; + char *str_end; + long not_used; + bool not_used_2; + MEM_ROOT tz_storage; + + MY_INIT(argv[0]); + + init_alloc_root(&tz_storage, 32768, 0); + + /* let us set some well known timezone */ + setenv("TZ", "MET", 1); + tzset(); + + /* Some initial time zone related system info */ + printf("time_t: %s %u bit\n", TYPE_SIGNED(time_t) ? "signed" : "unsigned", + (uint)TYPE_BIT(time_t)); + if (TYPE_SIGNED(time_t)) + { + t= -100; + localtime_negative= test(localtime_r(&t, &tmp) != 0); + printf("localtime_r %s negative params \ + (time_t=%d is %d-%d-%d %d:%d:%d)\n", + (localtime_negative ? "supports" : "doesn't support"), (int)t, + TM_YEAR_BASE + tmp.tm_year, tmp.tm_mon + 1, tmp.tm_mday, + tmp.tm_hour, tmp.tm_min, tmp.tm_sec); + + printf("mktime %s negative results (%d)\n", + (t == mktime(&tmp) ? "doesn't support" : "supports"), + (int)mktime(&tmp)); + } + + tmp.tm_year= 103; tmp.tm_mon= 2; tmp.tm_mday= 30; + tmp.tm_hour= 2; tmp.tm_min= 30; tmp.tm_sec= 0; tmp.tm_isdst= -1; + t= mktime(&tmp); + printf("mktime returns %s for spring time gap (%d)\n", + (t != (time_t)-1 ? "something" : "error"), (int)t); + + tmp.tm_year= 103; tmp.tm_mon= 8; tmp.tm_mday= 1; + tmp.tm_hour= 0; tmp.tm_min= 0; tmp.tm_sec= 0; tmp.tm_isdst= 0; + t= mktime(&tmp); + printf("mktime returns %s for non existing date (%d)\n", + (t != (time_t)-1 ? "something" : "error"), (int)t); + + tmp.tm_year= 103; tmp.tm_mon= 8; tmp.tm_mday= 1; + tmp.tm_hour= 25; tmp.tm_min=0; tmp.tm_sec=0; tmp.tm_isdst=1; + t= mktime(&tmp); + printf("mktime %s unnormalized input (%d)\n", + (t != (time_t)-1 ? "handles" : "doesn't handle"), (int)t); + + tmp.tm_year= 103; tmp.tm_mon= 9; tmp.tm_mday= 26; + tmp.tm_hour= 0; tmp.tm_min= 30; tmp.tm_sec= 0; tmp.tm_isdst= 1; + mktime(&tmp); + tmp.tm_hour= 2; tmp.tm_isdst= -1; + t= mktime(&tmp); + tmp.tm_hour= 4; tmp.tm_isdst= 0; + mktime(&tmp); + tmp.tm_hour= 2; tmp.tm_isdst= -1; + t1= mktime(&tmp); + printf("mktime is %s (%d %d)\n", + (t == t1 ? "determenistic" : "is non-determenistic"), + (int)t, (int)t1); + + /* Let us load time zone description */ + str_end= strmake(fullname, TZDIR, FN_REFLEN); + strmake(str_end, "/MET", FN_REFLEN - (str_end - fullname)); + + if (tz_load(fullname, &tz_info, &tz_storage)) + { + printf("Unable to load time zone info from '%s'\n", fullname); + free_root(&tz_storage, MYF(0)); + return 1; + } + + printf("Testing our implementation\n"); + + if (TYPE_SIGNED(time_t) && localtime_negative) + { + for (t= -40000; t < 20000; t++) + { + localtime_r(&t, &tmp); + gmt_sec_to_TIME(&time_tmp, (my_time_t)t, &tz_info); + if (!is_equal_TIME_tm(&time_tmp, &tmp)) + { + printf("Problem with negative time_t = %d\n", (int)t); + free_root(&tz_storage, MYF(0)); + return 1; + } + } + printf("gmt_sec_to_TIME = localtime for time_t in [-40000,20000) range\n"); + } + + for (t= 1000000000; t < 1100000000; t+= 13) + { + localtime_r(&t,&tmp); + gmt_sec_to_TIME(&time_tmp, (my_time_t)t, &tz_info); + + if (!is_equal_TIME_tm(&time_tmp, &tmp)) + { + printf("Problem with time_t = %d\n", (int)t); + free_root(&tz_storage, MYF(0)); + return 1; + } + } + printf("gmt_sec_to_TIME = localtime for time_t in [1000000000,1100000000) range\n"); + + init_time(); + + /* + Be careful here! my_system_gmt_sec doesn't fully handle unnormalized + dates. + */ + for (time_tmp.year= 1980; time_tmp.year < 2010; time_tmp.year++) + for (time_tmp.month= 1; time_tmp.month < 13; time_tmp.month++) + for (time_tmp.day= 1; + time_tmp.day < mon_lengths[isleap(time_tmp.year)][time_tmp.month-1]; + time_tmp.day++) + for (time_tmp.hour= 0; time_tmp.hour < 24; time_tmp.hour++) + for (time_tmp.minute= 0; time_tmp.minute < 60; time_tmp.minute+= 5) + for (time_tmp.second=0; time_tmp.second<60; time_tmp.second+=25) + { + t= (time_t)my_system_gmt_sec(&time_tmp, ¬_used, ¬_used_2); + t1= (time_t)TIME_to_gmt_sec(&time_tmp, &tz_info, ¬_used_2); + if (t != t1) + { + /* + We need special handling during autumn since my_system_gmt_sec + prefers greater time_t values (in MET) for ambiguity. + And BTW that is a bug which should be fixed !!! + */ + tmp.tm_year= time_tmp.year - TM_YEAR_BASE; + tmp.tm_mon= time_tmp.month - 1; + tmp.tm_mday= time_tmp.day; + tmp.tm_hour= time_tmp.hour; + tmp.tm_min= time_tmp.minute; + tmp.tm_sec= time_tmp.second; + tmp.tm_isdst= 1; + + t2= mktime(&tmp); + + if (t1 == t2) + continue; + + printf("Problem: %u/%u/%u %u:%u:%u with times t=%d, t1=%d\n", + time_tmp.year, time_tmp.month, time_tmp.day, + time_tmp.hour, time_tmp.minute, time_tmp.second, + (int)t,(int)t1); + + free_root(&tz_storage, MYF(0)); + return 1; + } + } + + printf("TIME_to_gmt_sec = my_system_gmt_sec for test range\n"); + + free_root(&tz_storage, MYF(0)); + return 0; +} + +#endif /* defined(TESTTIME) */ diff --git a/sql/tztime.h b/sql/tztime.h new file mode 100644 index 00000000000..ed92441d790 --- /dev/null +++ b/sql/tztime.h @@ -0,0 +1,71 @@ +/* Copyright (C) 2004 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 */ + + +#ifdef __GNUC__ +#pragma interface /* gcc class interface */ +#endif + +#if !defined(TESTTIME) && !defined(TZINFO2SQL) +/* + This class represents abstract time zone and provides + basic interface for TIME <-> my_time_t conversion. + Actual time zones which are specified by DB, or via offset + or use system functions are its descendants. +*/ +class Time_zone: public Sql_alloc +{ +public: + /* + Converts local time in broken down TIME representation to + my_time_t (UTC seconds since Epoch) represenation. + Returns 0 in case of error. Sets in_dst_time_gap to true if date provided + falls into spring time-gap (or lefts it untouched otherwise). + */ + virtual my_time_t TIME_to_gmt_sec(const TIME *t, + bool *in_dst_time_gap) const = 0; + /* + Converts time in my_time_t representation to local time in + broken down TIME representation. + */ + virtual void gmt_sec_to_TIME(TIME *tmp, my_time_t t) const = 0; + /* + Because of constness of String returned by get_name() time zone name + have to be already zeroended to be able to use String::ptr() instead + of c_ptr(). + */ + virtual const String * get_name() const = 0; + + /* + We need this only for surpressing warnings, objects of this type are + allocated on MEM_ROOT and should not require destruction. + */ + virtual ~Time_zone() {}; +}; + +extern Time_zone * my_tz_UTC; +extern Time_zone * my_tz_SYSTEM; +extern Time_zone * my_tz_find(THD *thd, const String *name); +extern my_bool my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap); +extern void my_tz_free(); + +/* + Maximum length of time zone name that we support + (Time zone name is char(64) in db) +*/ +#define MAX_TIME_ZONE_NAME_LENGTH 72 + +#endif /* !defined(TESTTIME) && !defined(TZINFO2SQL) */ diff --git a/sql/unireg.cc b/sql/unireg.cc index bab021aed59..c2666be804d 100644 --- a/sql/unireg.cc +++ b/sql/unireg.cc @@ -633,6 +633,7 @@ static bool make_empty_rec(File file,enum db_type table_type, DBUG_RETURN(1); } + table.in_use= current_thd; table.db_low_byte_first= handler->low_byte_first(); table.blob_ptr_size=portable_sizeof_char_ptr; diff --git a/strings/ctype-uca.c b/strings/ctype-uca.c index 67214d31ed9..a388e0cb2a2 100644 --- a/strings/ctype-uca.c +++ b/strings/ctype-uca.c @@ -7473,55 +7473,62 @@ static int ch2x(int ch) static my_coll_lexem_num my_coll_lexem_next(MY_COLL_LEXEM *lexem) { - for ( ;lexem->beg < lexem->end ; lexem->beg++) + const char *beg; + my_coll_lexem_num rc; + + for (beg= lexem->beg ; beg < lexem->end ; beg++) { - lexem->prev= lexem->beg; - if (lexem->beg[0] == ' ' || lexem->beg[0] == '\t' || - lexem->beg[0] == '\r' || lexem->beg[0] == '\n') + if (*beg == ' ' || *beg == '\t' || *beg == '\r' || *beg == '\n') continue; - if (lexem->beg[0] == '&') + if (*beg == '&') { - lexem->beg++; - return MY_COLL_LEXEM_SHIFT; + beg++; + rc= MY_COLL_LEXEM_SHIFT; + goto ex; } - if (lexem->beg[0] == '<') + if (beg[0] == '<') { - for (lexem->beg++, lexem->diff=1; - (lexem->beg < lexem->end) && - (lexem->beg[0] == '<') && (lexem->diff<3); - lexem->beg++, lexem->diff++); - return MY_COLL_LEXEM_DIFF; + for (beg++, lexem->diff= 1; + (beg < lexem->end) && + (*beg == '<') && (lexem->diff<3); + beg++, lexem->diff++); + rc= MY_COLL_LEXEM_DIFF; + goto ex; } - if ((lexem->beg[0] >= 'a' && lexem->beg[0] <= 'z') || - (lexem->beg[0] >= 'A' && lexem->beg[0] <= 'Z')) + if ((*beg >= 'a' && *beg <= 'z') || (*beg >= 'A' && *beg <= 'Z')) { - lexem->code= lexem->beg[0]; - lexem->beg++; - return MY_COLL_LEXEM_CHAR; + lexem->code= *beg++; + rc= MY_COLL_LEXEM_CHAR; + goto ex; } - if ((lexem->beg[0] == '\\') && - (lexem->beg+2 < lexem->end) && - (lexem->beg[1] == 'u')) + if ((*beg == '\\') && (beg+2 < lexem->end) && (beg[1] == 'u')) { int ch; + beg+= 2; lexem->code= 0; - for (lexem->beg+=2; - (lexem->beg < lexem->end) && ((ch= ch2x(lexem->beg[0])) >= 0) ; - lexem->beg++) - { + while ((beg < lexem->end) && ((ch= ch2x(beg[0])) >= 0)) + { lexem->code= (lexem->code << 4) + ch; + beg++; } - return MY_COLL_LEXEM_CHAR; + rc= MY_COLL_LEXEM_CHAR; + goto ex; } - return MY_COLL_LEXEM_ERROR; + rc= MY_COLL_LEXEM_ERROR; + goto ex; } - return MY_COLL_LEXEM_EOF; + rc= MY_COLL_LEXEM_EOF; + +ex: + lexem->prev= lexem->beg; + lexem->beg= beg; + return rc; } @@ -7549,8 +7556,8 @@ typedef struct my_coll_rule_item_st USAGE RETURN VALUES - 0 - OK - 1 - ERROR, e.g. too many items. + A positive number means the number of rules loaded. + -1 means ERROR, e.g. too many items, syntax error, etc. */ static int my_coll_rule_parse(MY_COLL_RULE *rule, size_t mitems, @@ -7668,7 +7675,7 @@ static int my_coll_rule_parse(MY_COLL_RULE *rule, size_t mitems, weights applying tailorings, i.e. a set of alternative weights for some characters. - The default UCA weights are stored in my_charset_ucs2_general_uca. + The default UCA weights are stored in uca_weight/uca_length. They consist of 256 pages, 256 character each. If a page is not overwritten by tailoring rules, @@ -7685,8 +7692,8 @@ static my_bool create_tailoring(CHARSET_INFO *cs, void *(*alloc)(uint)) char errstr[128]; uchar *newlengths; uint16 **newweights; - const uchar *deflengths= my_charset_ucs2_general_uca.sort_order; - uint16 **defweights= my_charset_ucs2_general_uca.sort_order_big; + const uchar *deflengths= uca_length; + uint16 **defweights= uca_weight; int rc, i; int ncontractions= 0; @@ -7706,11 +7713,11 @@ static my_bool create_tailoring(CHARSET_INFO *cs, void *(*alloc)(uint)) return 1; } - if (!(newweights= (uint16**) alloc(256*sizeof(uint16*)))) + if (!(newweights= (uint16**) (*alloc)(256*sizeof(uint16*)))) return 1; bzero(newweights, 256*sizeof(uint16*)); - if (!(newlengths= (uchar*) alloc(256))) + if (!(newlengths= (uchar*) (*alloc)(256))) return 1; memcpy(newlengths, deflengths, 256); @@ -7747,7 +7754,7 @@ static my_bool create_tailoring(CHARSET_INFO *cs, void *(*alloc)(uint)) /* Alloc new page and copy the default UCA weights */ uint size= 256*newlengths[pagec]*sizeof(uint16); - if (!(newweights[pagec]= (uint16*) alloc(size))) + if (!(newweights[pagec]= (uint16*) (*alloc)(size))) return 1; bzero((void*) newweights[pagec], size); @@ -7774,8 +7781,10 @@ static my_bool create_tailoring(CHARSET_INFO *cs, void *(*alloc)(uint)) /* Copy non-overwritten pages from the default UCA weights */ for (i= 0; i < 256 ; i++) + { if (!newweights[i]) newweights[i]= defweights[i]; + } cs->sort_order= newlengths; cs->sort_order_big= newweights; @@ -7785,7 +7794,7 @@ static my_bool create_tailoring(CHARSET_INFO *cs, void *(*alloc)(uint)) if (ncontractions) { uint size= 0x40*0x40*sizeof(uint16); /* 8K, for basic latin letter only */ - if (!(cs->contractions= (uint16*) alloc(size))) + if (!(cs->contractions= (uint16*) (*alloc)(size))) return 1; bzero((void*)cs->contractions, size); for (i=0; i < rc; i++) diff --git a/support-files/my-huge.cnf.sh b/support-files/my-huge.cnf.sh index 5fdde0d2963..d25686f1c21 100644 --- a/support-files/my-huge.cnf.sh +++ b/support-files/my-huge.cnf.sh @@ -1,6 +1,6 @@ -# Example mysql config file for very large systems. +# Example MySQL config file for very large systems. # -# This is for large system with memory of 1G-2G where the system runs mainly +# This is for a large system with memory of 1G-2G where the system runs mainly # MySQL. # # You can copy this file to @@ -9,9 +9,9 @@ # installation this directory is @localstatedir@) or # ~/.my.cnf to set user-specific options. # -# One can in this file use all long options that the program supports. +# In this file, you can use all long options that a program supports. # If you want to know which options a program supports, run the program -# with the --help option. +# with the "--help" option. # The following options will be passed to all MySQL clients [client] diff --git a/support-files/my-innodb-heavy-4G.cnf.sh b/support-files/my-innodb-heavy-4G.cnf.sh index 54df6a8bfe6..6def311f474 100644 --- a/support-files/my-innodb-heavy-4G.cnf.sh +++ b/support-files/my-innodb-heavy-4G.cnf.sh @@ -13,8 +13,8 @@ # (@localstatedir@ for this installation) or to # ~/.my.cnf to set user-specific options. # -# In this file, you can use all long options that the program supports. -# If you want to know the options a program supports, run the program +# In this file, you can use all long options that a program supports. +# If you want to know which options a program supports, run the program # with the "--help" option. # # More detailed information about the individual options can also be diff --git a/support-files/my-large.cnf.sh b/support-files/my-large.cnf.sh index a17ec7b5227..59aca4b32f2 100644 --- a/support-files/my-large.cnf.sh +++ b/support-files/my-large.cnf.sh @@ -1,6 +1,6 @@ -# Example mysql config file for large systems. +# Example MySQL config file for large systems. # -# This is for large system with memory = 512M where the system runs mainly +# This is for a large system with memory = 512M where the system runs mainly # MySQL. # # You can copy this file to @@ -9,9 +9,9 @@ # installation this directory is @localstatedir@) or # ~/.my.cnf to set user-specific options. # -# One can in this file use all long options that the program supports. +# In this file, you can use all long options that a program supports. # If you want to know which options a program supports, run the program -# with the --help option. +# with the "--help" option. # The following options will be passed to all MySQL clients [client] diff --git a/support-files/my-medium.cnf.sh b/support-files/my-medium.cnf.sh index d50d0717c17..529740d59f0 100644 --- a/support-files/my-medium.cnf.sh +++ b/support-files/my-medium.cnf.sh @@ -1,4 +1,4 @@ -# Example mysql config file for medium systems. +# Example MySQL config file for medium systems. # # This is for a system with little memory (32M - 64M) where MySQL plays # an important part, or systems up to 128M where MySQL is used together with @@ -10,9 +10,9 @@ # installation this directory is @localstatedir@) or # ~/.my.cnf to set user-specific options. # -# One can in this file use all long options that the program supports. +# In this file, you can use all long options that a program supports. # If you want to know which options a program supports, run the program -# with the --help option. +# with the "--help" option. # The following options will be passed to all MySQL clients [client] diff --git a/support-files/my-small.cnf.sh b/support-files/my-small.cnf.sh index 2ae62fb48f1..b2ecca6127e 100644 --- a/support-files/my-small.cnf.sh +++ b/support-files/my-small.cnf.sh @@ -1,4 +1,4 @@ -# Example mysql config file for small systems. +# Example MySQL config file for small systems. # # This is for a system with little memory (<= 64M) where MySQL is only used # from time to time and it's important that the mysqld daemon @@ -10,9 +10,9 @@ # installation this directory is @localstatedir@) or # ~/.my.cnf to set user-specific options. # -# One can in this file use all long options that the program supports. +# In this file, you can use all long options that a program supports. # If you want to know which options a program supports, run the program -# with the --help option. +# with the "--help" option. # The following options will be passed to all MySQL clients [client] diff --git a/support-files/mysql.spec.sh b/support-files/mysql.spec.sh index 8583b623392..c3d74b147f3 100644 --- a/support-files/mysql.spec.sh +++ b/support-files/mysql.spec.sh @@ -397,7 +397,7 @@ useradd -M -r -d $mysql_datadir -s /bin/bash -c "MySQL server" mysql 2> /dev/nul chown -R mysql $mysql_datadir # Initiate databases -mysql_install_db --rpm +mysql_install_db -IN-RPM --user=mysql # Change permissions again to fix any new files. chown -R mysql $mysql_datadir diff --git a/tests/Makefile.am b/tests/Makefile.am index 5d6e6a68ae2..5d0e4627b69 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -36,7 +36,6 @@ LIBS = @CLIENT_LIBS@ LDADD = @CLIENT_EXTRA_LDFLAGS@ ../libmysql/libmysqlclient.la client_test_LDADD= $(LDADD) $(CXXLDFLAGS) client_test_SOURCES= client_test.c -client_test_DEPENDENCIES= $(LIBRARIES) $(pkglib_LTLIBRARIES) insert_test_DEPENDENCIES= $(LIBRARIES) $(pkglib_LTLIBRARIES) select_test_DEPENDENCIES= $(LIBRARIES) $(pkglib_LTLIBRARIES) diff --git a/tests/client_test.c b/tests/client_test.c index b4ba20dbf00..c035691fe47 100644 --- a/tests/client_test.c +++ b/tests/client_test.c @@ -177,7 +177,8 @@ static void client_connect() int rc; myheader_r("client_connect"); - fprintf(stdout, "\n Establishing a connection to '%s' ...", opt_host); + fprintf(stdout, "\n Establishing a connection to '%s' ...", + opt_host ? opt_host : ""); if (!(mysql= mysql_init(NULL))) { @@ -3226,8 +3227,11 @@ static void bind_fetch(int row_count) { MYSQL_STMT *stmt; int rc, i, count= row_count; - ulong bit; long data[10]; + int8 i8_data; + int16 i16_data; + int32 i32_data; + longlong i64_data; float f_data; double d_data; char s_data[10]; @@ -3283,9 +3287,16 @@ static void bind_fetch(int row_count) } bind[0].buffer_type= MYSQL_TYPE_TINY; + bind[0].buffer= (char *)&i8_data; + bind[1].buffer_type= MYSQL_TYPE_SHORT; + bind[1].buffer= (char *)&i16_data; + bind[2].buffer_type= MYSQL_TYPE_LONG; + bind[2].buffer= (char *)&i32_data; + bind[3].buffer_type= MYSQL_TYPE_LONGLONG; + bind[3].buffer= (char *)&i64_data; bind[4].buffer_type= MYSQL_TYPE_FLOAT; bind[4].buffer= (char *)&f_data; @@ -3312,36 +3323,47 @@ static void bind_fetch(int row_count) check_execute(stmt, rc); fprintf(stdout, "\n"); - fprintf(stdout, "\n tiny : %ld(%lu)", data[0], length[0]); - fprintf(stdout, "\n short : %ld(%lu)", data[1], length[1]); - fprintf(stdout, "\n int : %ld(%lu)", data[2], length[2]); - fprintf(stdout, "\n longlong : %ld(%lu)", data[3], length[3]); + fprintf(stdout, "\n tiny : %ld(%lu)", (ulong) i8_data, length[0]); + fprintf(stdout, "\n short : %ld(%lu)", (ulong) i16_data, length[1]); + fprintf(stdout, "\n int : %ld(%lu)", (ulong) i32_data, length[2]); + fprintf(stdout, "\n longlong : %ld(%lu)", (ulong) i64_data, length[3]); fprintf(stdout, "\n float : %f(%lu)", f_data, length[4]); fprintf(stdout, "\n double : %g(%lu)", d_data, length[5]); fprintf(stdout, "\n char : %s(%lu)", s_data, length[6]); - bit= 1; rc= 10+row_count; - for (i= 0; i < 4; i++) - { - assert(data[i] == rc+i); - assert(length[i] == bit); - bit<<= 1; - rc+= 12; - } + + /* TINY */ + assert((int) i8_data == rc); + assert(length[0] == 1); + rc+= 13; + + /* SHORT */ + assert((int) i16_data == rc); + assert(length[1] == 2); + rc+= 13; + + /* LONG */ + assert((int) i32_data == rc); + assert(length[2] == 4); + rc+= 13; + + /* LONGLONG */ + assert((int) i64_data == rc); + assert(length[3] == 8); + rc+= 13; /* FLOAT */ - rc+= i; assert((int)f_data == rc); assert(length[4] == 4); + rc+= 13; /* DOUBLE */ - rc+= 13; assert((int)d_data == rc); assert(length[5] == 8); + rc+= 13; /* CHAR */ - rc+= 13; { char buff[20]; long len= my_sprintf(buff, (buff, "%d", rc)); @@ -4523,7 +4545,8 @@ static void test_multi_stmt() { MYSQL_STMT *stmt, *stmt1, *stmt2; - int rc, id; + int rc; + ulong id; char name[50]; MYSQL_BIND bind[2]; ulong length[2]; @@ -4555,7 +4578,7 @@ static void test_multi_stmt() */ bzero((char*) bind, sizeof(bind)); - bind[0].buffer_type= MYSQL_TYPE_SHORT; + bind[0].buffer_type= MYSQL_TYPE_LONG; bind[0].buffer= (char *)&id; bind[0].is_null= &is_null[0]; bind[0].length= &length[0]; @@ -4582,7 +4605,7 @@ static void test_multi_stmt() rc= mysql_stmt_fetch(stmt); check_execute(stmt, rc); - fprintf(stdout, "\n int_data: %d(%lu)", id, length[0]); + fprintf(stdout, "\n int_data: %lu(%lu)", id, length[0]); fprintf(stdout, "\n str_data: %s(%lu)", name, length[1]); assert(id == 10); assert(strcmp(name, "mysql") == 0); @@ -4611,7 +4634,7 @@ static void test_multi_stmt() rc= mysql_stmt_fetch(stmt); check_execute(stmt, rc); - fprintf(stdout, "\n int_data: %d(%lu)", id, length[0]); + fprintf(stdout, "\n int_data: %lu(%lu)", id, length[0]); fprintf(stdout, "\n str_data: %s(%lu)", name, length[1]); assert(id == 10); assert(strcmp(name, "updated") == 0); @@ -9912,6 +9935,35 @@ static void test_bug4079() mysql_stmt_close(stmt); } + +static void test_bug4236() +{ + MYSQL_STMT *stmt; + const char *stmt_text; + int rc; + MYSQL_STMT backup; + + myheader("test_bug4296"); + + stmt= mysql_stmt_init(mysql); + + /* mysql_stmt_execute() of statement with statement id= 0 crashed server */ + stmt_text= "SELECT 1"; + /* We need to prepare statement to pass by possible check in libmysql */ + rc= mysql_stmt_prepare(stmt, stmt_text, strlen(stmt_text)); + check_execute(stmt, rc); + /* Hack to check that server works OK if statement wasn't found */ + backup.stmt_id= stmt->stmt_id; + stmt->stmt_id= 0; + rc= mysql_stmt_execute(stmt); + assert(rc); + /* Restore original statement id to be able to reprepare it */ + stmt->stmt_id= backup.stmt_id; + + mysql_stmt_close(stmt); +} + + /* Read and parse arguments and MySQL options from my.cnf */ @@ -10206,6 +10258,7 @@ int main(int argc, char **argv) test_bug3796(); /* test for select concat(?, <string>) */ test_bug4026(); /* test microseconds precision of time types */ test_bug4079(); /* erroneous subquery in prepared statement */ + test_bug4236(); /* init -> execute */ /* XXX: PLEASE RUN THIS PROGRAM UNDER VALGRIND AND VERIFY THAT YOUR TEST DOESN'T CONTAIN WARNINGS/ERRORS BEFORE YOU PUSH. diff --git a/tools/mysqlmanager.c b/tools/mysqlmanager.c index 45865e97ab7..bb0a76d6c49 100644 --- a/tools/mysqlmanager.c +++ b/tools/mysqlmanager.c @@ -687,7 +687,7 @@ HANDLE_DECL(handle_stop_exec) error="Process not running"; goto err; } - if (mysql_shutdown(&e->mysql)) + if (mysql_shutdown(&e->mysql, SHUTDOWN_DEFAULT)) { /* e->th=0; */ /* th may be a struct */ pthread_mutex_unlock(&e->lock); |