diff --git a/BUILD/FINISH.sh b/BUILD/FINISH.sh index 094eb8275d6..81defaa7f52 100644 --- a/BUILD/FINISH.sh +++ b/BUILD/FINISH.sh @@ -10,7 +10,7 @@ do done commands="\ -$make -k clean || true +$make -k distclean || true /bin/rm -rf */.deps/*.P config.cache innobase/config.cache bdb/build_unix/config.cache bdb/dist/autom4te.cache autom4te.cache innobase/autom4te.cache; aclocal || (echo \"Can't execute aclocal\" && exit 1) diff --git a/VC++Files/client/mysqladmin.dsp b/VC++Files/client/mysqladmin.dsp index a7e4404e253..7a0b3bec1a7 100644 --- a/VC++Files/client/mysqladmin.dsp +++ b/VC++Files/client/mysqladmin.dsp @@ -115,7 +115,7 @@ LINK32=xilink6.exe # Name "mysqladmin - Win32 classic" # Begin Source File -SOURCE=.\mysqladmin.c +SOURCE=.\mysqladmin.cpp # End Source File # End Target # End Project diff --git a/extra/perror.c b/extra/perror.c index a28626fd873..1bd4b203120 100644 --- a/extra/perror.c +++ b/extra/perror.c @@ -69,7 +69,7 @@ static HA_ERRORS ha_errlist[]= { { 120,"Didn't find key on read or update" }, { 121,"Duplicate key on write or update" }, - { 123,"Someone has changed the row since it was read; Update with is recoverable" }, + { 123,"Someone has changed the row since it was read (while the table was locked to prevent it)" }, { 124,"Wrong index given to function" }, { 126,"Index file is crashed" }, { 127,"Record-file is crashed" }, diff --git a/heap/hp_rfirst.c b/heap/hp_rfirst.c index 1668376ed1c..85548fea212 100644 --- a/heap/hp_rfirst.c +++ b/heap/hp_rfirst.c @@ -52,6 +52,7 @@ int heap_rfirst(HP_INFO *info, byte *record, int inx) my_errno=HA_ERR_END_OF_FILE; DBUG_RETURN(my_errno); } + DBUG_ASSERT(0); /* TODO fix it */ info->current_record=0; info->current_hash_ptr=0; info->update=HA_STATE_PREV_FOUND; diff --git a/include/m_ctype.h b/include/m_ctype.h index ddc21070547..26e285b9683 100644 --- a/include/m_ctype.h +++ b/include/m_ctype.h @@ -63,7 +63,7 @@ typedef struct unicase_info_st #define MY_CS_UNICODE 128 /* is a charset is full unicode */ #define MY_CS_READY 256 /* if a charset is initialized */ #define MY_CS_AVAILABLE 512 /* If either compiled-in or loaded*/ - +#define MY_CS_CSSORT 1024 /* if case sensitive sort order */ #define MY_CHARSET_UNDEFINED 0 diff --git a/innobase/buf/buf0buf.c b/innobase/buf/buf0buf.c index 376deedabec..d686b559528 100644 --- a/innobase/buf/buf0buf.c +++ b/innobase/buf/buf0buf.c @@ -2152,7 +2152,8 @@ buf_get_latched_pages_number(void) block = buf_pool_get_nth_block(buf_pool, i); - if ((block->buf_fix_count != 0) || (block->io_fix != 0)) + if (((block->buf_fix_count != 0) || (block->io_fix != 0)) && + block->magic_n == BUF_BLOCK_MAGIC_N ) fixed_pages_number++; } diff --git a/innobase/lock/lock0lock.c b/innobase/lock/lock0lock.c index 98def88fa31..479952235f0 100644 --- a/innobase/lock/lock0lock.c +++ b/innobase/lock/lock0lock.c @@ -4089,6 +4089,9 @@ lock_print_info( (ulong) ut_dulint_get_high(purge_sys->purge_undo_no), (ulong) ut_dulint_get_low(purge_sys->purge_undo_no)); + fprintf(file, + "History list length %lu\n", (ulong) trx_sys->rseg_history_len); + fprintf(file, "Total number of lock structs in row lock hash table %lu\n", (ulong) lock_get_n_rec_locks()); diff --git a/innobase/row/row0sel.c b/innobase/row/row0sel.c index 71163bc35b6..26d26ca323c 100644 --- a/innobase/row/row0sel.c +++ b/innobase/row/row0sel.c @@ -638,23 +638,24 @@ row_sel_get_clust_rec( if (!node->read_view) { /* Try to place a lock on the index record */ - /* If innodb_locks_unsafe_for_binlog option is used, - we lock only the record, i.e. next-key locking is - not used. - */ - if ( srv_locks_unsafe_for_binlog ) - { - err = lock_clust_rec_read_check_and_lock(0, clust_rec, - index,node->row_lock_mode, LOCK_REC_NOT_GAP, thr); - } - else - { - err = lock_clust_rec_read_check_and_lock(0, clust_rec, index, - node->row_lock_mode, LOCK_ORDINARY, thr); + /* If innodb_locks_unsafe_for_binlog option is used, + we lock only the record, i.e. next-key locking is + not used. + */ - } + if (srv_locks_unsafe_for_binlog) { + err = lock_clust_rec_read_check_and_lock(0, + clust_rec, + index, node->row_lock_mode, + LOCK_REC_NOT_GAP, thr); + } else { + err = lock_clust_rec_read_check_and_lock(0, + clust_rec, + index, node->row_lock_mode, + LOCK_ORDINARY, thr); + } - if (err != DB_SUCCESS) { + if (err != DB_SUCCESS) { return(err); } @@ -1205,22 +1206,24 @@ rec_loop: if (!consistent_read) { - /* If innodb_locks_unsafe_for_binlog option is used, - we lock only the record, i.e. next-key locking is - not used. - */ + /* If innodb_locks_unsafe_for_binlog option is used, + we lock only the record, i.e. next-key locking is + not used. + */ - if ( srv_locks_unsafe_for_binlog ) - { - err = sel_set_rec_lock(page_rec_get_next(rec), index, - node->row_lock_mode, LOCK_REC_NOT_GAP, thr); - } - else - { - err = sel_set_rec_lock(page_rec_get_next(rec), index, - node->row_lock_mode, LOCK_ORDINARY, thr); - } - if (err != DB_SUCCESS) { + if (srv_locks_unsafe_for_binlog) { + err = sel_set_rec_lock(page_rec_get_next(rec), + index, + node->row_lock_mode, + LOCK_REC_NOT_GAP, thr); + } else { + err = sel_set_rec_lock(page_rec_get_next(rec), + index, + node->row_lock_mode, + LOCK_ORDINARY, thr); + } + + if (err != DB_SUCCESS) { /* Note that in this case we will store in pcur the PREDECESSOR of the record we are waiting the lock for */ @@ -1245,21 +1248,18 @@ rec_loop: if (!consistent_read) { /* Try to place a lock on the index record */ - /* If innodb_locks_unsafe_for_binlog option is used, - we lock only the record, i.e. next-key locking is - not used. - */ + /* If innodb_locks_unsafe_for_binlog option is used, + we lock only the record, i.e. next-key locking is + not used. + */ - if ( srv_locks_unsafe_for_binlog ) - { - err = sel_set_rec_lock(rec, index, node->row_lock_mode, + if (srv_locks_unsafe_for_binlog) { + err = sel_set_rec_lock(rec, index, node->row_lock_mode, LOCK_REC_NOT_GAP, thr); - } - else - { - err = sel_set_rec_lock(rec, index, node->row_lock_mode, + } else { + err = sel_set_rec_lock(rec, index, node->row_lock_mode, LOCK_ORDINARY, thr); - } + } if (err != DB_SUCCESS) { @@ -3234,8 +3234,7 @@ rec_loop: we do not lock gaps. Supremum record is really a gap and therefore we do not set locks there. */ - if ( srv_locks_unsafe_for_binlog == FALSE ) - { + if (srv_locks_unsafe_for_binlog == FALSE) { err = sel_set_rec_lock(rec, index, prebuilt->select_lock_type, LOCK_ORDINARY, thr); @@ -3337,11 +3336,18 @@ rec_loop: if (prebuilt->select_lock_type != LOCK_NONE && set_also_gap_locks) { - /* Try to place a lock on the index record */ - err = sel_set_rec_lock(rec, index, + /* Try to place a gap lock on the index + record only if innodb_locks_unsafe_for_binlog + option is not set */ + + if (srv_locks_unsafe_for_binlog == FALSE) { + + err = sel_set_rec_lock(rec, index, prebuilt->select_lock_type, LOCK_GAP, thr); + } + if (err != DB_SUCCESS) { goto lock_wait_or_error; @@ -3363,11 +3369,18 @@ rec_loop: if (prebuilt->select_lock_type != LOCK_NONE && set_also_gap_locks) { - /* Try to place a lock on the index record */ - err = sel_set_rec_lock(rec, index, + /* Try to place a gap lock on the index + record only if innodb_locks_unsafe_for_binlog + option is not set */ + + if (srv_locks_unsafe_for_binlog == FALSE) { + + err = sel_set_rec_lock(rec, index, prebuilt->select_lock_type, LOCK_GAP, thr); + } + if (err != DB_SUCCESS) { goto lock_wait_or_error; @@ -3401,19 +3414,16 @@ rec_loop: prebuilt->select_lock_type, LOCK_REC_NOT_GAP, thr); } else { - /* If innodb_locks_unsafe_for_binlog option is used, - we lock only the record, i.e. next-key locking is - not used. - */ - if ( srv_locks_unsafe_for_binlog ) - { - err = sel_set_rec_lock(rec, index, + /* If innodb_locks_unsafe_for_binlog option is used, + we lock only the record, i.e. next-key locking is + not used. */ + + if (srv_locks_unsafe_for_binlog) { + err = sel_set_rec_lock(rec, index, prebuilt->select_lock_type, LOCK_REC_NOT_GAP, thr); - } - else - { - err = sel_set_rec_lock(rec, index, + } else { + err = sel_set_rec_lock(rec, index, prebuilt->select_lock_type, LOCK_ORDINARY, thr); } diff --git a/innobase/trx/trx0purge.c b/innobase/trx/trx0purge.c index 5c62640e011..3df34111281 100644 --- a/innobase/trx/trx0purge.c +++ b/innobase/trx/trx0purge.c @@ -289,7 +289,7 @@ trx_purge_add_update_undo_to_history( flst_get_len(seg_header + TRX_UNDO_PAGE_LIST, mtr)); mlog_write_ulint(rseg_header + TRX_RSEG_HISTORY_SIZE, - hist_size + undo->size, MLOG_4BYTES, mtr); + hist_size + undo->size, MLOG_4BYTES, mtr); } /* Add the log as the first in the history list */ @@ -646,6 +646,27 @@ trx_purge_rseg_get_next_history_log( mutex_exit(&(rseg->mutex)); mtr_commit(&mtr); + mutex_enter(&kernel_mutex); + + /* Add debug code to track history list corruption reported + on the MySQL mailing list on Nov 9, 2004. The fut0lst.c + file-based list was corrupt. The prev node pointer was + FIL_NULL, even though the list length was over 8 million nodes! + We assume that purge truncates the history list in moderate + size pieces, and if we here reach the head of the list, the + list cannot be longer than 20 000 undo logs now. */ + + if (trx_sys->rseg_history_len > 20000) { + ut_print_timestamp(stderr); + fprintf(stderr, +" InnoDB: Warning: purge reached the head of the history list,\n" +"InnoDB: but its length is still reported as %lu! Make a detailed bug\n" +"InnoDB: report, and post it to bugs.mysql.com\n", + (ulong)trx_sys->rseg_history_len); + } + + mutex_exit(&kernel_mutex); + return; } diff --git a/innobase/trx/trx0undo.c b/innobase/trx/trx0undo.c index c1edc223cbc..8d1518753dd 100644 --- a/innobase/trx/trx0undo.c +++ b/innobase/trx/trx0undo.c @@ -1241,7 +1241,7 @@ trx_undo_lists_init( if (page_no != FIL_NULL && srv_force_recovery < SRV_FORCE_NO_UNDO_LOG_SCAN) { - + undo = trx_undo_mem_create_at_db_start(rseg, i, page_no, &mtr); size += undo->size; diff --git a/myisam/ft_boolean_search.c b/myisam/ft_boolean_search.c index 9a51eff88e1..c432ac5a16c 100644 --- a/myisam/ft_boolean_search.c +++ b/myisam/ft_boolean_search.c @@ -348,7 +348,7 @@ static void _ftb_init_index_search(FT_INFO *ftb) FTB_EXPR *top_ftbe=ftbe->up->up; ftbw->docid[0]=HA_OFFSET_ERROR; for (ftbe=ftbw->up; ftbe != top_ftbe; ftbe=ftbe->up) - if (ftbe->flags & FTB_FLAG_YES) + if (!(ftbe->flags & FTB_FLAG_NO)) ftbe->yweaks++; ftbe=0; break; @@ -356,7 +356,7 @@ static void _ftb_init_index_search(FT_INFO *ftb) } if (!ftbe) continue; - /* 3 */ + /* 4 */ if (!is_tree_inited(& ftb->no_dupes)) init_tree(& ftb->no_dupes,0,0,sizeof(my_off_t), _ftb_no_dupes_cmp,0,0,0); diff --git a/myisam/mi_create.c b/myisam/mi_create.c index 7fc7cc4edf1..f99a2c655d2 100644 --- a/myisam/mi_create.c +++ b/myisam/mi_create.c @@ -16,7 +16,7 @@ /* Create a MyISAM table */ -#include "fulltext.h" +#include "ftdefs.h" #include "sp_defs.h" #if defined(MSDOS) || defined(__WIN__) @@ -41,7 +41,7 @@ int mi_create(const char *name,uint keys,MI_KEYDEF *keydefs, File dfile,file; int errpos,save_errno; myf create_flag; - uint fields,length,max_key_length,packed,pointer, + uint fields,length,max_key_length,packed,pointer,real_length_diff, key_length,info_length,key_segs,options,min_key_length_skip, base_pos,varchar_count,long_varchar_count,varchar_length, max_key_block_length,unique_key_parts,fulltext_keys,offset; @@ -238,7 +238,7 @@ int mi_create(const char *name,uint keys,MI_KEYDEF *keydefs, { share.state.key_root[i]= HA_OFFSET_ERROR; - min_key_length_skip=length=0; + min_key_length_skip=length=real_length_diff=0; key_length=pointer; if (keydef->flag & HA_SPATIAL) { @@ -297,6 +297,7 @@ int mi_create(const char *name,uint keys,MI_KEYDEF *keydefs, key_length+= HA_FT_MAXBYTELEN+HA_FT_WLEN; length++; /* At least one length byte */ min_key_length_skip+=HA_FT_MAXBYTELEN; + real_length_diff=HA_FT_MAXBYTELEN-FT_MAX_WORD_LEN_FOR_SORT; } else { @@ -397,7 +398,8 @@ int mi_create(const char *name,uint keys,MI_KEYDEF *keydefs, key_segs) share.state.rec_per_key_part[key_segs-1]=1L; length+=key_length; - keydef->block_length= MI_BLOCK_SIZE(length,pointer,MI_MAX_KEYPTR_SIZE); + keydef->block_length= MI_BLOCK_SIZE(length-real_length_diff, + pointer,MI_MAX_KEYPTR_SIZE); if (keydef->block_length > MI_MAX_KEY_BLOCK_LENGTH || length >= MI_MAX_KEY_BUFF) { diff --git a/mysql-test/mysql_test_run.c b/mysql-test/mysql_test_run.c deleted file mode 100644 index 6f388fc4a45..00000000000 --- a/mysql-test/mysql_test_run.c +++ /dev/null @@ -1,1728 +0,0 @@ -/* - Copyright (c) 2002, 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. - - 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 -#include -#include -#ifndef __WIN__ -#include -#endif -#include -#ifdef __NETWARE__ -#include -#include -#endif -#include -#include -#ifndef __WIN__ -#include -#endif -#include -#ifdef __NETWARE__ -#include -#endif -#ifdef __WIN__ -#include -#include -#endif - -#include "my_manage.h" - -/****************************************************************************** - - macros - -******************************************************************************/ - -#define HEADER "TEST RESULT \n" -#define DASH "-------------------------------------------------------\n" - -#define NW_TEST_SUFFIX ".nw-test" -#define NW_RESULT_SUFFIX ".nw-result" -#define TEST_SUFFIX ".test" -#define RESULT_SUFFIX ".result" -#define REJECT_SUFFIX ".reject" -#define OUT_SUFFIX ".out" -#define ERR_SUFFIX ".err" - -const char *TEST_PASS = "[ pass ]"; -const char *TEST_SKIP = "[ skip ]"; -const char *TEST_FAIL = "[ fail ]"; -const char *TEST_BAD = "[ bad ]"; -const char *TEST_IGNORE = "[ignore]"; - -/****************************************************************************** - - global variables - -******************************************************************************/ -#ifdef __NETWARE__ -static char base_dir[PATH_MAX] = "sys:/mysql"; -#else -static char base_dir[PATH_MAX] = ".."; -#endif -static char db[PATH_MAX] = "test"; -static char user[PATH_MAX] = "root"; -static char password[PATH_MAX] = ""; - -int master_port = 9306; -int slave_port = 9307; - -#if !defined(__NETWARE__) && !defined(__WIN__) -static char master_socket[PATH_MAX] = "./var/tmp/master.sock"; -static char slave_socket[PATH_MAX] = "./var/tmp/slave.sock"; -#endif - -// comma delimited list of tests to skip or empty string -#ifndef __WIN__ -static char skip_test[PATH_MAX] = " lowercase_table3 , system_mysql_db_fix "; -#else -/* - The most ignore testes contain the calls of system command -*/ -#define MAX_COUNT_TESTES 1024 -/* - lowercase_table3 is disabled by Gerg - system_mysql_db_fix is disabled by Gerg - sp contains a command system - rpl_EE_error contains a command system - rpl_loaddatalocal contains a command system - ndb_autodiscover contains a command system - rpl_rotate_logs contains a command system - repair contains a command system - rpl_trunc_binlog contains a command system - mysqldump contains a command system - rpl000001 makes non-exit loop...temporary skiped -*/ -static char skip_test[PATH_MAX] = " lowercase_table3 , system_mysql_db_fix , sp , rpl_EE_error , rpl_loaddatalocal , ndb_autodiscover , rpl_rotate_logs , repair , rpl_trunc_binlog , mysqldump , rpl000001 "; -#endif -static char ignore_test[PATH_MAX] = ""; - -static char bin_dir[PATH_MAX]; -static char mysql_test_dir[PATH_MAX]; -static char test_dir[PATH_MAX]; -static char mysql_tmp_dir[PATH_MAX]; -static char result_dir[PATH_MAX]; -static char master_dir[PATH_MAX]; -static char slave_dir[PATH_MAX]; -static char lang_dir[PATH_MAX]; -static char char_dir[PATH_MAX]; - -static char mysqladmin_file[PATH_MAX]; -static char mysqld_file[PATH_MAX]; -static char mysqltest_file[PATH_MAX]; -#ifndef __WIN__ -static char master_pid[PATH_MAX]; -static char slave_pid[PATH_MAX]; -static char sh_file[PATH_MAX] = "/bin/sh"; -#else -static HANDLE master_pid; -static HANDLE slave_pid; -#endif - -static char master_opt[PATH_MAX] = ""; -static char slave_opt[PATH_MAX] = ""; - -static char slave_master_info[PATH_MAX] = ""; - -static char master_init_script[PATH_MAX] = ""; -static char slave_init_script[PATH_MAX] = ""; - -// OpenSSL -static char ca_cert[PATH_MAX]; -static char server_cert[PATH_MAX]; -static char server_key[PATH_MAX]; -static char client_cert[PATH_MAX]; -static char client_key[PATH_MAX]; - -int total_skip = 0; -int total_pass = 0; -int total_fail = 0; -int total_test = 0; - -int total_ignore = 0; - -int use_openssl = FALSE; -int master_running = FALSE; -int slave_running = FALSE; -int skip_slave = TRUE; -int single_test = TRUE; - -int restarts = 0; - -FILE *log_fd = NULL; - -/****************************************************************************** - - functions - -******************************************************************************/ - -/****************************************************************************** - - prototypes - -******************************************************************************/ - -void report_stats(); -void install_db(char *); -void mysql_install_db(); -void start_master(); -void start_slave(); -void mysql_start(); -void stop_slave(); -void stop_master(); -void mysql_stop(); -void mysql_restart(); -int read_option(char *, char *); -void run_test(char *); -void setup(char *); -void vlog(const char *, va_list); -void mlog(const char *, ...); -void log_info(const char *, ...); -void log_error(const char *, ...); -void log_errno(const char *, ...); -void die(const char *); -char *str_tok(char *string, const char *delim); -#ifndef __WIN__ -void run_init_script(const char *script_name); -#endif -/****************************************************************************** - - report_stats() - - Report the gathered statistics. - -******************************************************************************/ -void report_stats() -{ - if (total_fail == 0) - { - mlog("\nAll %d test(s) were successful.\n", total_test); - } - else - { - double percent = ((double)total_pass / total_test) * 100; - - mlog("\nFailed %u/%u test(s), %.02f%% successful.\n", - total_fail, total_test, percent); - mlog("\nThe .out and .err files in %s may give you some\n", result_dir); - mlog("hint of what when wrong.\n"); - mlog("\nIf you want to report this error, please first read the documentation\n"); - mlog("at: http://www.mysql.com/doc/M/y/MySQL_test_suite.html\n"); - } -} - -/****************************************************************************** - - install_db() - - Install the a database. - -******************************************************************************/ -void install_db(char *datadir) -{ - arg_list_t al; - int err; - char input[PATH_MAX]; - char output[PATH_MAX]; - char error[PATH_MAX]; - - // input file -#ifdef __NETWARE__ - snprintf(input, PATH_MAX, "%s/bin/init_db.sql", base_dir); -#else - snprintf(input, PATH_MAX, "%s/mysql-test/init_db.sql", base_dir); -#endif - snprintf(output, PATH_MAX, "%s/install.out", datadir); - snprintf(error, PATH_MAX, "%s/install.err", datadir); - - // args - init_args(&al); - add_arg(&al, mysqld_file); - add_arg(&al, "--no-defaults"); - add_arg(&al, "--bootstrap"); - add_arg(&al, "--skip-grant-tables"); - add_arg(&al, "--basedir=%s", base_dir); - add_arg(&al, "--datadir=%s", datadir); - add_arg(&al, "--skip-innodb"); - add_arg(&al, "--skip-bdb"); -#ifndef __NETWARE__ - add_arg(&al, "--character-sets-dir=%s", char_dir); - add_arg(&al, "--language=%s", lang_dir); -#endif - - // spawn - if ((err = spawn(mysqld_file, &al, TRUE, input, output, error, NULL)) != 0) - { - die("Unable to create database."); - } - - // free args - free_args(&al); -} - -/****************************************************************************** - - mysql_install_db() - - Install the test databases. - -******************************************************************************/ -void mysql_install_db() -{ - char temp[PATH_MAX]; - - // var directory - snprintf(temp, PATH_MAX, "%s/var", mysql_test_dir); - - // clean up old direcotry - del_tree(temp); - - // create var directory -#ifndef __WIN__ - mkdir(temp, S_IRWXU); - // create subdirectories - mlog("Creating test-suite folders...\n"); - snprintf(temp, PATH_MAX, "%s/var/run", mysql_test_dir); - mkdir(temp, S_IRWXU); - snprintf(temp, PATH_MAX, "%s/var/tmp", mysql_test_dir); - mkdir(temp, S_IRWXU); - snprintf(temp, PATH_MAX, "%s/var/master-data", mysql_test_dir); - mkdir(temp, S_IRWXU); - snprintf(temp, PATH_MAX, "%s/var/master-data/mysql", mysql_test_dir); - mkdir(temp, S_IRWXU); - snprintf(temp, PATH_MAX, "%s/var/master-data/test", mysql_test_dir); - mkdir(temp, S_IRWXU); - snprintf(temp, PATH_MAX, "%s/var/slave-data", mysql_test_dir); - mkdir(temp, S_IRWXU); - snprintf(temp, PATH_MAX, "%s/var/slave-data/mysql", mysql_test_dir); - mkdir(temp, S_IRWXU); - snprintf(temp, PATH_MAX, "%s/var/slave-data/test", mysql_test_dir); - mkdir(temp, S_IRWXU); -#else - mkdir(temp); - // create subdirectories - mlog("Creating test-suite folders...\n"); - snprintf(temp, PATH_MAX, "%s/var/run", mysql_test_dir); - mkdir(temp); - snprintf(temp, PATH_MAX, "%s/var/tmp", mysql_test_dir); - mkdir(temp); - snprintf(temp, PATH_MAX, "%s/var/master-data", mysql_test_dir); - mkdir(temp); - snprintf(temp, PATH_MAX, "%s/var/master-data/mysql", mysql_test_dir); - mkdir(temp); - snprintf(temp, PATH_MAX, "%s/var/master-data/test", mysql_test_dir); - mkdir(temp); - snprintf(temp, PATH_MAX, "%s/var/slave-data", mysql_test_dir); - mkdir(temp); - snprintf(temp, PATH_MAX, "%s/var/slave-data/mysql", mysql_test_dir); - mkdir(temp); - snprintf(temp, PATH_MAX, "%s/var/slave-data/test", mysql_test_dir); - mkdir(temp); -#endif - - // install databases - mlog("Creating test databases for master... \n"); - install_db(master_dir); - mlog("Creating test databases for slave... \n"); - install_db(slave_dir); -} - -/****************************************************************************** - - start_master() - - Start the master server. - -******************************************************************************/ -void start_master() -{ - arg_list_t al; - int err; - char master_out[PATH_MAX]; - char master_err[PATH_MAX]; -// char temp[PATH_MAX]; - char temp2[PATH_MAX]; - - // remove old berkeley db log files that can confuse the server - removef("%s/log.*", master_dir); - - // remove stale binary logs - removef("%s/var/log/*-bin.*", mysql_test_dir); - - // remove stale binary logs - removef("%s/var/log/*.index", mysql_test_dir); - - // remove master.info file - removef("%s/master.info", master_dir); - - // remove relay files - removef("%s/var/log/*relay*", mysql_test_dir); - - // remove relay-log.info file - removef("%s/relay-log.info", master_dir); - - // init script - if (master_init_script[0] != 0) - { -#ifdef __NETWARE__ - // TODO: use the scripts - if (strinstr(master_init_script, "repair_part2-master.sh") != 0) - { - FILE *fp; - - // create an empty index file - snprintf(temp, PATH_MAX, "%s/test/t1.MYI", master_dir); - fp = fopen(temp, "wb+"); - - fputs("1", fp); - - fclose(fp); - } -#elif !defined(__WIN__) - run_init_script(master_init_script); -#endif - } - - // redirection files - snprintf(master_out, PATH_MAX, "%s/var/run/master%u.out", - mysql_test_dir, restarts); - snprintf(master_err, PATH_MAX, "%s/var/run/master%u.err", - mysql_test_dir, restarts); -#ifndef __WIN__ - snprintf(temp2,PATH_MAX,"%s/var",mysql_test_dir); - mkdir(temp2,S_IRWXU); - snprintf(temp2,PATH_MAX,"%s/var/log",mysql_test_dir); - mkdir(temp2,S_IRWXU); -#else - snprintf(temp2,PATH_MAX,"%s/var",mysql_test_dir); - mkdir(temp2); - snprintf(temp2,PATH_MAX,"%s/var/log",mysql_test_dir); - mkdir(temp2); -#endif - // args - init_args(&al); - add_arg(&al, "%s", mysqld_file); - add_arg(&al, "--no-defaults"); - add_arg(&al, "--log-bin=%s/var/log/master-bin",mysql_test_dir); - add_arg(&al, "--server-id=1"); - add_arg(&al, "--basedir=%s", base_dir); - add_arg(&al, "--port=%u", master_port); -#if !defined(__NETWARE__) && !defined(__WIN__) - add_arg(&al, "--socket=%s",master_socket); -#endif - add_arg(&al, "--local-infile"); - add_arg(&al, "--core"); - add_arg(&al, "--datadir=%s", master_dir); -#ifndef __WIN__ - add_arg(&al, "--pid-file=%s", master_pid); -#endif - add_arg(&al, "--character-sets-dir=%s", char_dir); - add_arg(&al, "--tmpdir=%s", mysql_tmp_dir); - add_arg(&al, "--language=%s", lang_dir); -#ifdef DEBUG //only for debug builds - add_arg(&al, "--debug"); -#endif - - if (use_openssl) - { - add_arg(&al, "--ssl-ca=%s", ca_cert); - add_arg(&al, "--ssl-cert=%s", server_cert); - add_arg(&al, "--ssl-key=%s", server_key); - } - - // $MASTER_40_ARGS - add_arg(&al, "--rpl-recovery-rank=1"); - add_arg(&al, "--init-rpl-role=master"); - - // $SMALL_SERVER - add_arg(&al, "-O"); - add_arg(&al, "key_buffer_size=1M"); - add_arg(&al, "-O"); - add_arg(&al, "sort_buffer=256K"); - add_arg(&al, "-O"); - add_arg(&al, "max_heap_table_size=1M"); - - // $EXTRA_MASTER_OPT - if (master_opt[0] != 0) - { - char *p; - - p = (char *)str_tok(master_opt, " \t"); - if (!strstr(master_opt, "timezone")) - { - while (p) - { - add_arg(&al, "%s", p); - p = (char *)str_tok(NULL, " \t"); - } - } - } - - // remove the pid file if it exists -#ifndef __WIN__ - remove(master_pid); -#endif - - // spawn -#ifdef __WIN__ - if ((err= spawn(mysqld_file, &al, FALSE, NULL, master_out, master_err, &master_pid)) == 0) -#else - if ((err= spawn(mysqld_file, &al, FALSE, NULL, master_out, master_err, master_pid)) == 0) -#endif - { - sleep_until_file_exists(master_pid); - - if ((err = wait_for_server_start(bin_dir, mysqladmin_file, user, password, master_port, - mysql_tmp_dir)) == 0) - { - master_running = TRUE; - } - else - { - log_error("The master server went down early."); - } - } - else - { - log_error("Unable to start master server."); - } - - // free_args - free_args(&al); -} - -/****************************************************************************** - - start_slave() - - Start the slave server. - -******************************************************************************/ -void start_slave() -{ - arg_list_t al; - int err; - char slave_out[PATH_MAX]; - char slave_err[PATH_MAX]; - - // skip? - if (skip_slave) return; - - // remove stale binary logs - removef("%s/*-bin.*", slave_dir); - - // remove stale binary logs - removef("%s/*.index", slave_dir); - - // remove master.info file - removef("%s/master.info", slave_dir); - - // remove relay files - removef("%s/var/log/*relay*", mysql_test_dir); - - // remove relay-log.info file - removef("%s/relay-log.info", slave_dir); - - // init script - if (slave_init_script[0] != 0) - { -#ifdef __NETWARE__ - // TODO: use the scripts - if (strinstr(slave_init_script, "rpl000016-slave.sh") != 0) - { - // create empty master.info file - snprintf(temp, PATH_MAX, "%s/master.info", slave_dir); - close(open(temp, O_WRONLY | O_CREAT,S_IRWXU|S_IRWXG|S_IRWXO)); - } - else if (strinstr(slave_init_script, "rpl000017-slave.sh") != 0) - { - FILE *fp; - - // create a master.info file - snprintf(temp, PATH_MAX, "%s/master.info", slave_dir); - fp = fopen(temp, "wb+"); - - fputs("master-bin.000001\n", fp); - fputs("4\n", fp); - fputs("127.0.0.1\n", fp); - fputs("replicate\n", fp); - fputs("aaaaaaaaaaaaaaab\n", fp); - fputs("9306\n", fp); - fputs("1\n", fp); - fputs("0\n", fp); - - fclose(fp); - } - else if (strinstr(slave_init_script, "rpl_rotate_logs-slave.sh") != 0) - { - // create empty master.info file - snprintf(temp, PATH_MAX, "%s/master.info", slave_dir); - close(open(temp, O_WRONLY | O_CREAT,S_IRWXU|S_IRWXG|S_IRWXO)); - } -#elif !defined(__WIN__) - run_init_script(slave_init_script); -#endif - } - - // redirection files - snprintf(slave_out, PATH_MAX, "%s/var/run/slave%u.out", - mysql_test_dir, restarts); - snprintf(slave_err, PATH_MAX, "%s/var/run/slave%u.err", - mysql_test_dir, restarts); - - // args - init_args(&al); - add_arg(&al, "%s", mysqld_file); - add_arg(&al, "--no-defaults"); - add_arg(&al, "--log-bin=slave-bin"); - add_arg(&al, "--relay_log=slave-relay-bin"); - add_arg(&al, "--basedir=%s", base_dir); - add_arg(&al, "--port=%u", slave_port); -#if !defined(__NETWARE__) && !defined(__WIN__) - add_arg(&al, "--socket=%s",slave_socket); -#endif - add_arg(&al, "--datadir=%s", slave_dir); -#ifndef __WIN__ - add_arg(&al, "--pid-file=%s", slave_pid); -#endif - add_arg(&al, "--character-sets-dir=%s", char_dir); - add_arg(&al, "--core"); - add_arg(&al, "--tmpdir=%s", mysql_tmp_dir); - add_arg(&al, "--language=%s", lang_dir); - - add_arg(&al, "--exit-info=256"); - add_arg(&al, "--log-slave-updates"); - add_arg(&al, "--init-rpl-role=slave"); - add_arg(&al, "--skip-innodb"); - add_arg(&al, "--skip-slave-start"); - add_arg(&al, "--slave-load-tmpdir=../../var/tmp"); - - add_arg(&al, "--report-user=%s", user); - add_arg(&al, "--report-host=127.0.0.1"); - add_arg(&al, "--report-port=%u", slave_port); - - add_arg(&al, "--master-retry-count=10"); - add_arg(&al, "-O"); - add_arg(&al, "slave_net_timeout=10"); -#ifdef DEBUG //only for debug builds - add_arg(&al, "--debug"); -#endif - - if (use_openssl) - { - add_arg(&al, "--ssl-ca=%s", ca_cert); - add_arg(&al, "--ssl-cert=%s", server_cert); - add_arg(&al, "--ssl-key=%s", server_key); - } - - // slave master info - if (slave_master_info[0] != 0) - { - char *p; - - p = (char *)str_tok(slave_master_info, " \t"); - - while(p) - { - add_arg(&al, "%s", p); - - p = (char *)str_tok(NULL, " \t"); - } - } - else - { - add_arg(&al, "--master-user=%s", user); - add_arg(&al, "--master-password=%s", password); - add_arg(&al, "--master-host=127.0.0.1"); - add_arg(&al, "--master-port=%u", master_port); - add_arg(&al, "--master-connect-retry=1"); - add_arg(&al, "--server-id=2"); - add_arg(&al, "--rpl-recovery-rank=2"); - } - - // small server - add_arg(&al, "-O"); - add_arg(&al, "key_buffer_size=1M"); - add_arg(&al, "-O"); - add_arg(&al, "sort_buffer=256K"); - add_arg(&al, "-O"); - add_arg(&al, "max_heap_table_size=1M"); - - - // opt args - if (slave_opt[0] != 0) - { - char *p; - - p = (char *)str_tok(slave_opt, " \t"); - - while(p) - { - add_arg(&al, "%s", p); - - p = (char *)str_tok(NULL, " \t"); - } - } - - // remove the pid file if it exists -#ifndef __WIN__ - remove(slave_pid); -#endif - // spawn -#ifdef __WIN__ - if ((err = spawn(mysqld_file, &al, FALSE, NULL, slave_out, slave_err, &slave_pid)) == 0) -#else - if ((err = spawn(mysqld_file, &al, FALSE, NULL, slave_out, slave_err, slave_pid)) == 0) -#endif - { - sleep_until_file_exists(slave_pid); - - if ((err = wait_for_server_start(bin_dir, mysqladmin_file, user, password, slave_port, - mysql_tmp_dir)) == 0) - { - slave_running = TRUE; - } - else - { - log_error("The slave server went down early."); - } - } - else - { - log_error("Unable to start slave server."); - } - - // free args - free_args(&al); -} - -/****************************************************************************** - - mysql_start() - - Start the mysql servers. - -******************************************************************************/ -void mysql_start() -{ -// log_info("Starting the MySQL server(s): %u", ++restarts); - start_master(); - - start_slave(); - - // activate the test screen -#ifdef __NETWARE__ - ActivateScreen(getscreenhandle()); -#endif -} - -/****************************************************************************** - - stop_slave() - - Stop the slave server. - -******************************************************************************/ -void stop_slave() -{ - int err; - - // running? - if (!slave_running) return; - - // stop - if ((err = stop_server(bin_dir, mysqladmin_file, user, password, slave_port, slave_pid, - mysql_tmp_dir)) == 0) - { - slave_running = FALSE; - } - else - { - log_error("Unable to stop slave server."); - } -} - -/****************************************************************************** - - stop_master() - - Stop the master server. - -******************************************************************************/ -void stop_master() -{ - int err; - - // running? - if (!master_running) return; - - if ((err = stop_server(bin_dir, mysqladmin_file, user, password, master_port, master_pid, - mysql_tmp_dir)) == 0) - { - master_running = FALSE; - } - else - { - log_error("Unable to stop master server."); - } -} - -/****************************************************************************** - - mysql_stop() - - Stop the mysql servers. - -******************************************************************************/ -void mysql_stop() -{ - - stop_master(); - - stop_slave(); - - // activate the test screen -#ifdef __NETWARE__ - ActivateScreen(getscreenhandle()); -#endif -} - -/****************************************************************************** - - mysql_restart() - - Restart the mysql servers. - -******************************************************************************/ -void mysql_restart() -{ -// log_info("Restarting the MySQL server(s): %u", ++restarts); - - mysql_stop(); - - mlog(DASH); - - mysql_start(); -} - -/****************************************************************************** - - read_option() - - Read the option file. - -******************************************************************************/ -int read_option(char *opt_file, char *opt) -{ - int fd, err; - char *p; - char buf[PATH_MAX]; - - // copy current option - strncpy(buf, opt, PATH_MAX); - - // open options file - fd = open(opt_file, O_RDONLY); - - err = read(fd, opt, PATH_MAX); - - close(fd); - - if (err > 0) - { - // terminate string - if ((p = strchr(opt, '\n')) != NULL) - { - *p = 0; - - // check for a '\r' - if ((p = strchr(opt, '\r')) != NULL) - { - *p = 0; - } - } - else - { - opt[err] = 0; - } - - // check for $MYSQL_TEST_DIR - if ((p = strstr(opt, "$MYSQL_TEST_DIR")) != NULL) - { - char temp[PATH_MAX]; - - *p = 0; - - strcpy(temp, p + strlen("$MYSQL_TEST_DIR")); - - strcat(opt, mysql_test_dir); - - strcat(opt, temp); - } - // Check for double backslash and replace it with single bakslash - if ((p = strstr(opt, "\\\\")) != NULL) - { - /* bmove is guranteed to work byte by byte */ - bmove(p, p+1, strlen(p+1)); - } - } - else - { - // clear option - *opt = 0; - } - - // compare current option with previous - return strcmp(opt, buf); -} - -/****************************************************************************** - - run_test() - - Run the given test case. - -******************************************************************************/ -void run_test(char *test) -{ - char temp[PATH_MAX]; - const char *rstr; - int skip = FALSE, ignore=FALSE; - int restart = FALSE; - int flag = FALSE; - struct stat info; - - // skip tests in the skip list - snprintf(temp, PATH_MAX, " %s ", test); - skip = (strinstr(skip_test, temp) != 0); - if (skip == FALSE) - ignore = (strinstr(ignore_test, temp) != 0); - - snprintf(master_init_script, PATH_MAX, "%s/%s-master.sh", test_dir, test); - snprintf(slave_init_script, PATH_MAX, "%s/%s-slave.sh", test_dir, test); -#ifdef __WIN__ - if (! stat(master_init_script, &info)) - skip = TRUE; - if (!stat(slave_init_script, &info)) - skip = TRUE; -#endif - if (ignore) - { - // show test - mlog("%-46s ", test); - - // ignore - rstr = TEST_IGNORE; - ++total_ignore; - } - else if (!skip) // skip test? - { - char test_file[PATH_MAX]; - char master_opt_file[PATH_MAX]; - char slave_opt_file[PATH_MAX]; - char slave_master_info_file[PATH_MAX]; - char result_file[PATH_MAX]; - char reject_file[PATH_MAX]; - char out_file[PATH_MAX]; - char err_file[PATH_MAX]; - int err; - arg_list_t al; -#ifdef __WIN__ - /* - Clean test database - */ - removef("%s/test/*.*", master_dir); - removef("%s/test/*.*", slave_dir); - removef("%s/mysqltest/*.*", master_dir); - removef("%s/mysqltest/*.*", slave_dir); - -#endif - // skip slave? - flag = skip_slave; - skip_slave = (strncmp(test, "rpl", 3) != 0); - if (flag != skip_slave) restart = TRUE; - - // create files - snprintf(master_opt_file, PATH_MAX, "%s/%s-master.opt", test_dir, test); - snprintf(slave_opt_file, PATH_MAX, "%s/%s-slave.opt", test_dir, test); - snprintf(slave_master_info_file, PATH_MAX, "%s/%s.slave-mi", test_dir, test); - snprintf(reject_file, PATH_MAX, "%s/%s%s", result_dir, test, REJECT_SUFFIX); - snprintf(out_file, PATH_MAX, "%s/%s%s", result_dir, test, OUT_SUFFIX); - snprintf(err_file, PATH_MAX, "%s/%s%s", result_dir, test, ERR_SUFFIX); - - // netware specific files - snprintf(test_file, PATH_MAX, "%s/%s%s", test_dir, test, NW_TEST_SUFFIX); - if (stat(test_file, &info)) - { - snprintf(test_file, PATH_MAX, "%s/%s%s", test_dir, test, TEST_SUFFIX); - if (access(test_file,0)) - { - printf("Invalid test name %s, %s file not found\n",test,test_file); - return; - } - } - - snprintf(result_file, PATH_MAX, "%s/%s%s", result_dir, test, NW_RESULT_SUFFIX); - if (stat(result_file, &info)) - { - snprintf(result_file, PATH_MAX, "%s/%s%s", result_dir, test, RESULT_SUFFIX); - } - - // init scripts - if (stat(master_init_script, &info)) - master_init_script[0] = 0; - else - restart = TRUE; - - if (stat(slave_init_script, &info)) - slave_init_script[0] = 0; - else - restart = TRUE; - - // read options - if (read_option(master_opt_file, master_opt)) restart = TRUE; - if (read_option(slave_opt_file, slave_opt)) restart = TRUE; - if (read_option(slave_master_info_file, slave_master_info)) restart = TRUE; - - // cleanup previous run - remove(reject_file); - remove(out_file); - remove(err_file); - - // start or restart? - if (!master_running) mysql_start(); - else if (restart) mysql_restart(); - - // let the system stabalize - sleep(1); - - // show test - mlog("%-46s ", test); - - - // args - init_args(&al); - add_arg(&al, "%s", mysqltest_file); - add_arg(&al, "--no-defaults"); - add_arg(&al, "--port=%u", master_port); -#if !defined(__NETWARE__) && !defined(__WIN__) - add_arg(&al, "--socket=%s", master_socket); - add_arg(&al, "--tmpdir=%s", mysql_tmp_dir); -#endif - add_arg(&al, "--database=%s", db); - add_arg(&al, "--user=%s", user); - add_arg(&al, "--password=%s", password); - add_arg(&al, "--silent"); - add_arg(&al, "--basedir=%s/", mysql_test_dir); - add_arg(&al, "--host=127.0.0.1"); - add_arg(&al, "-v"); - add_arg(&al, "-R"); - add_arg(&al, "%s", result_file); - - if (use_openssl) - { - add_arg(&al, "--ssl-ca=%s", ca_cert); - add_arg(&al, "--ssl-cert=%s", client_cert); - add_arg(&al, "--ssl-key=%s", client_key); - } - - // spawn - err = spawn(mysqltest_file, &al, TRUE, test_file, out_file, err_file, NULL); - - // free args - free_args(&al); - - remove_empty_file(out_file); - remove_empty_file(err_file); - - if (err == 0) - { - // pass - rstr = TEST_PASS; - ++total_pass; - - // increment total - ++total_test; - } - else if (err == 2) - { - // skip - rstr = TEST_SKIP; - ++total_skip; - } - else if (err == 1) - { - // fail - rstr = TEST_FAIL; - ++total_fail; - - // increment total - ++total_test; - } - else - { - rstr = TEST_BAD; - } - } - else // early skips - { - // show test - mlog("%-46s ", test); - - // skip - rstr = TEST_SKIP; - ++total_skip; - } - - // result - mlog("%-14s\n", rstr); -} - -/****************************************************************************** - - vlog() - - Log the message. - -******************************************************************************/ -void vlog(const char *format, va_list ap) -{ - vfprintf(stdout, format, ap); - fflush(stdout); - - if (log_fd) - { - vfprintf(log_fd, format, ap); - fflush(log_fd); - } -} - -/****************************************************************************** - - log() - - Log the message. - -******************************************************************************/ -void mlog(const char *format, ...) -{ - va_list ap; - - va_start(ap, format); - - vlog(format, ap); - - va_end(ap); -} - -/****************************************************************************** - - log_info() - - Log the given information. - -******************************************************************************/ -void log_info(const char *format, ...) -{ - va_list ap; - - va_start(ap, format); - - mlog("-- INFO : "); - vlog(format, ap); - mlog("\n"); - - va_end(ap); -} - -/****************************************************************************** - - log_error() - - Log the given error. - -******************************************************************************/ -void log_error(const char *format, ...) -{ - va_list ap; - - va_start(ap, format); - - mlog("-- ERROR: "); - vlog(format, ap); - mlog("\n"); - - va_end(ap); -} - -/****************************************************************************** - - log_errno() - - Log the given error and errno. - -******************************************************************************/ -void log_errno(const char *format, ...) -{ - va_list ap; - - va_start(ap, format); - - mlog("-- ERROR: (%003u) ", errno); - vlog(format, ap); - mlog("\n"); - - va_end(ap); -} - -/****************************************************************************** - - die() - - Exit the application. - -******************************************************************************/ -void die(const char *msg) -{ - log_error(msg); -#ifdef __NETWARE__ - pressanykey(); -#endif - exit(-1); -} - -/****************************************************************************** - - setup() - - Setup the mysql test enviornment. - -******************************************************************************/ -void setup(char *file) -{ - char temp[PATH_MAX]; - char file_path[PATH_MAX*2]; - char *p; - int position; - - // set the timezone for the timestamp test -#ifdef __WIN__ - _putenv( "TZ=GMT-3" ); -#else - setenv("TZ", "GMT-3", TRUE); -#endif - // find base dir -#ifdef __NETWARE__ - strcpy(temp, strlwr(file)); - while((p = strchr(temp, '\\')) != NULL) *p = '/'; -#else - getcwd(temp, PATH_MAX); - position = strlen(temp); - temp[position] = '/'; - temp[position+1] = 0; -#ifdef __WIN__ - while((p = strchr(temp, '\\')) != NULL) *p = '/'; -#endif -#endif - - if ((position = strinstr(temp, "/mysql-test/")) != 0) - { - p = temp + position - 1; - *p = 0; - strcpy(base_dir, temp); - } - - log_info("Currect directory: %s",base_dir); - -#ifdef __NETWARE__ - // setup paths - snprintf(bin_dir, PATH_MAX, "%s/bin", base_dir); - snprintf(mysql_test_dir, PATH_MAX, "%s/mysql-test", base_dir); - snprintf(test_dir, PATH_MAX, "%s/t", mysql_test_dir); - snprintf(mysql_tmp_dir, PATH_MAX, "%s/var/tmp", mysql_test_dir); - snprintf(result_dir, PATH_MAX, "%s/r", mysql_test_dir); - snprintf(master_dir, PATH_MAX, "%s/var/master-data", mysql_test_dir); - snprintf(slave_dir, PATH_MAX, "%s/var/slave-data", mysql_test_dir); - snprintf(lang_dir, PATH_MAX, "%s/share/english", base_dir); - snprintf(char_dir, PATH_MAX, "%s/share/charsets", base_dir); - -#ifdef HAVE_OPENSSL - use_openssl = TRUE; -#endif // HAVE_OPENSSL - - // OpenSSL paths - snprintf(ca_cert, PATH_MAX, "%s/SSL/cacert.pem", base_dir); - snprintf(server_cert, PATH_MAX, "%s/SSL/server-cert.pem", base_dir); - snprintf(server_key, PATH_MAX, "%s/SSL/server-key.pem", base_dir); - snprintf(client_cert, PATH_MAX, "%s/SSL/client-cert.pem", base_dir); - snprintf(client_key, PATH_MAX, "%s/SSL/client-key.pem", base_dir); - - // setup files - snprintf(mysqld_file, PATH_MAX, "%s/mysqld", bin_dir); - snprintf(mysqltest_file, PATH_MAX, "%s/mysqltest", bin_dir); - snprintf(mysqladmin_file, PATH_MAX, "%s/mysqladmin", bin_dir); - snprintf(master_pid, PATH_MAX, "%s/var/run/master.pid", mysql_test_dir); - snprintf(slave_pid, PATH_MAX, "%s/var/run/slave.pid", mysql_test_dir); -#elif __WIN__ - // setup paths -#ifdef _DEBUG - snprintf(bin_dir, PATH_MAX, "%s/client_debug", base_dir); -#else - snprintf(bin_dir, PATH_MAX, "%s/client_release", base_dir); -#endif - snprintf(mysql_test_dir, PATH_MAX, "%s/mysql-test", base_dir); - snprintf(test_dir, PATH_MAX, "%s/t", mysql_test_dir); - snprintf(mysql_tmp_dir, PATH_MAX, "%s/var/tmp", mysql_test_dir); - snprintf(result_dir, PATH_MAX, "%s/r", mysql_test_dir); - snprintf(master_dir, PATH_MAX, "%s/var/master-data", mysql_test_dir); - snprintf(slave_dir, PATH_MAX, "%s/var/slave-data", mysql_test_dir); - snprintf(lang_dir, PATH_MAX, "%s/share/english", base_dir); - snprintf(char_dir, PATH_MAX, "%s/share/charsets", base_dir); - -#ifdef HAVE_OPENSSL - use_openssl = TRUE; -#endif // HAVE_OPENSSL - - // OpenSSL paths - snprintf(ca_cert, PATH_MAX, "%s/SSL/cacert.pem", base_dir); - snprintf(server_cert, PATH_MAX, "%s/SSL/server-cert.pem", base_dir); - snprintf(server_key, PATH_MAX, "%s/SSL/server-key.pem", base_dir); - snprintf(client_cert, PATH_MAX, "%s/SSL/client-cert.pem", base_dir); - snprintf(client_key, PATH_MAX, "%s/SSL/client-key.pem", base_dir); - - // setup files - snprintf(mysqld_file, PATH_MAX, "%s/mysqld.exe", bin_dir); - snprintf(mysqltest_file, PATH_MAX, "%s/mysqltest.exe", bin_dir); - snprintf(mysqladmin_file, PATH_MAX, "%s/mysqladmin.exe", bin_dir); -#else - // setup paths - snprintf(bin_dir, PATH_MAX, "%s/client", base_dir); - snprintf(mysql_test_dir, PATH_MAX, "%s/mysql-test", base_dir); - snprintf(test_dir, PATH_MAX, "%s/t", mysql_test_dir); - snprintf(mysql_tmp_dir, PATH_MAX, "%s/var/tmp", mysql_test_dir); - snprintf(result_dir, PATH_MAX, "%s/r", mysql_test_dir); - snprintf(master_dir, PATH_MAX, "%s/var/master-data", mysql_test_dir); - snprintf(slave_dir, PATH_MAX, "%s/var/slave-data", mysql_test_dir); - snprintf(lang_dir, PATH_MAX, "%s/sql/share/english", base_dir); - snprintf(char_dir, PATH_MAX, "%s/sql/share/charsets", base_dir); - -#ifdef HAVE_OPENSSL - use_openssl = TRUE; -#endif // HAVE_OPENSSL - - // OpenSSL paths - snprintf(ca_cert, PATH_MAX, "%s/SSL/cacert.pem", base_dir); - snprintf(server_cert, PATH_MAX, "%s/SSL/server-cert.pem", base_dir); - snprintf(server_key, PATH_MAX, "%s/SSL/server-key.pem", base_dir); - snprintf(client_cert, PATH_MAX, "%s/SSL/client-cert.pem", base_dir); - snprintf(client_key, PATH_MAX, "%s/SSL/client-key.pem", base_dir); - - // setup files - snprintf(mysqld_file, PATH_MAX, "%s/sql/mysqld", base_dir); - snprintf(mysqltest_file, PATH_MAX, "%s/mysqltest", bin_dir); - snprintf(mysqladmin_file, PATH_MAX, "%s/mysqladmin", bin_dir); - snprintf(master_pid, PATH_MAX, "%s/var/run/master.pid", mysql_test_dir); - snprintf(slave_pid, PATH_MAX, "%s/var/run/slave.pid", mysql_test_dir); - - snprintf(master_socket,PATH_MAX, "%s/var/tmp/master.sock", mysql_test_dir); - snprintf(slave_socket,PATH_MAX, "%s/var/tmp/slave.sock", mysql_test_dir); - -#endif - // create log file - snprintf(temp, PATH_MAX, "%s/mysql-test-run.log", mysql_test_dir); - if ((log_fd = fopen(temp, "w+")) == NULL) - { - log_errno("Unable to create log file."); - } - - // prepare skip test list - while((p = strchr(skip_test, ',')) != NULL) *p = ' '; - strcpy(temp, strlwr(skip_test)); - snprintf(skip_test, PATH_MAX, " %s ", temp); - - // environment -#ifdef __NETWARE__ - setenv("MYSQL_TEST_DIR", mysql_test_dir, 1); - snprintf(file_path, PATH_MAX*2, "%s/client/mysqldump --no-defaults -u root --port=%u", bin_dir, master_port); - setenv("MYSQL_DUMP", file_path, 1); - snprintf(file_path, PATH_MAX*2, "%s/client/mysqlbinlog --no-defaults --local-load=%s", bin_dir, mysql_tmp_dir); - setenv("MYSQL_BINLOG", file_path, 1); -#elif __WIN__ - snprintf(file_path,MAX_PATH,"MYSQL_TEST_DIR=%s",mysql_test_dir); - _putenv(file_path); - snprintf(file_path, PATH_MAX*2, "MYSQL_DUMP=%s/mysqldump.exe --no-defaults -u root --port=%u", bin_dir, master_port); - _putenv(file_path); - snprintf(file_path, PATH_MAX*2, "MYSQL_BINLOG=%s/mysqlbinlog.exe --no-defaults --local-load=%s", bin_dir, mysql_tmp_dir); - _putenv(file_path); -#else - setenv("MYSQL_TEST_DIR", mysql_test_dir, 1); - snprintf(file_path, PATH_MAX*2, "%s/mysqldump --no-defaults -u root --port=%u --socket=%s", bin_dir, master_port, master_socket); - setenv("MYSQL_DUMP", file_path, 1); - snprintf(file_path, PATH_MAX*2, "%s/mysqlbinlog --no-defaults --local-load=%s", bin_dir, mysql_tmp_dir); - setenv("MYSQL_BINLOG", file_path, 1); -#endif - -#ifndef __WIN__ - setenv("MASTER_MYPORT", "9306", 1); - setenv("SLAVE_MYPORT", "9307", 1); - setenv("MYSQL_TCP_PORT", "3306", 1); -#else - _putenv("MASTER_MYPORT=9306"); - _putenv("SLAVE_MYPORT=9307"); - _putenv("MYSQL_TCP_PORT=3306"); -#endif - -} - -/****************************************************************************** - - main() - -******************************************************************************/ -int main(int argc, char **argv) -{ - int is_ignore_list = 0; - // setup - setup(argv[0]); - - /* The --ignore option is comma saperated list of test cases to skip and - should be very first command line option to the test suite. - - The usage is now: - mysql_test_run --ignore=test1,test2 test3 test4 - where test1 and test2 are test cases to ignore - and test3 and test4 are test cases to run. - */ - if (argc >= 2 && !strnicmp(argv[1], "--ignore=", sizeof("--ignore=")-1)) - { - char *temp, *token; - temp= strdup(strchr(argv[1],'=') + 1); - for (token=str_tok(temp, ","); token != NULL; token=str_tok(NULL, ",")) - { - if (strlen(ignore_test) + strlen(token) + 2 <= PATH_MAX-1) - sprintf(ignore_test+strlen(ignore_test), " %s ", token); - else - { - free(temp); - die("ignore list too long."); - } - } - free(temp); - is_ignore_list = 1; - } - // header -#ifndef __WIN__ - mlog("MySQL Server %s, for %s (%s)\n\n", VERSION, SYSTEM_TYPE, MACHINE_TYPE); -#else - mlog("MySQL Server ---, for %s (%s)\n\n", SYSTEM_TYPE, MACHINE_TYPE); -#endif - - mlog("Initializing Tests...\n"); - - // install test databases - mysql_install_db(); - - mlog("Starting Tests...\n"); - - mlog("\n"); - mlog(HEADER); - mlog(DASH); - - if ( argc > 1 + is_ignore_list ) - { - int i; - - // single test - single_test = TRUE; - - for (i = 1 + is_ignore_list; i < argc; i++) - { - // run given test - run_test(argv[i]); - } - } - else - { - // run all tests -#ifndef __WIN__ - struct dirent **namelist; - int i,n; - char test[NAME_MAX]; - char *p; - int position; - - n = scandir(test_dir, &namelist, 0, alphasort); - if (n < 0) - die("Unable to open tests directory."); - else - { - for (i = 0; i < n; i++) - { - strcpy(test, strlwr(namelist[i]->d_name)); - // find the test suffix - if ((position = strinstr(test, TEST_SUFFIX)) != 0) - { - p = test + position - 1; - // null terminate at the suffix - *p = 0; - // run test - run_test(test); - } - free(namelist[n]); - } - free(namelist); - } -#else - struct _finddata_t dir; - intptr_t handle; - char test[NAME_MAX]; - char mask[PATH_MAX]; - char *p; - int position; - char **names = 0; - char **testes = 0; - int name_index; - int index; - - // single test - single_test = FALSE; - - snprintf(mask,MAX_PATH,"%s/*.test",test_dir); - - if ((handle=_findfirst(mask,&dir)) == -1L) - { - die("Unable to open tests directory."); - } - - names = malloc(MAX_COUNT_TESTES*4); - testes = names; - name_index = 0; - - do - { - if (!(dir.attrib & _A_SUBDIR)) - { - strcpy(test, strlwr(dir.name)); - - // find the test suffix - if ((position = strinstr(test, TEST_SUFFIX)) != 0) - { - p = test + position - 1; - // null terminate at the suffix - *p = 0; - - // insert test - *names = malloc(PATH_MAX); - strcpy(*names,test); - names++; - name_index++; - } - } - }while (_findnext(handle,&dir) == 0); - - _findclose(handle); - - qsort( (void *)testes, name_index, sizeof( char * ), compare ); - - for (index = 0; index <= name_index; index++) - { - run_test(testes[index]); - free(testes[index]); - } - - free(testes); -#endif - } - - // stop server - mysql_stop(); - - mlog(DASH); - mlog("\n"); - - mlog("Ending Tests...\n"); - - // report stats - report_stats(); - - // close log - if (log_fd) fclose(log_fd); - - // keep results up -#ifdef __NETWARE__ - pressanykey(); -#endif - return 0; -} - - -/* - Synopsis: - This function breaks the string into a sequence of tokens. The difference - between this function and strtok is that it respects the quoted string i.e. - it skips any delimiter character within the quoted part of the string. - It return tokens by eliminating quote character. It modifies the input string - passed. It will work with whitespace delimeter but may not work properly with - other delimeter. If the delimeter will contain any quote character, then - function will not tokenize and will return null string. - e.g. if input string is - --init-slave="set global max_connections=500" --skip-external-locking - then the output will two string i.e. - --init-slave=set global max_connections=500 - --skip-external-locking - -Arguments: - string: input string - delim: set of delimiter character -Output: - return the null terminated token of NULL. -*/ - - -char *str_tok(char *string, const char *delim) -{ - char *token; /* current token received from strtok */ - char *qt_token; /* token delimeted by the matching pair of quote */ - /* - if there are any quote chars found in the token then this variable - will hold the concatenated string to return to the caller - */ - char *ptr_token=NULL; - /* pointer to the quote character in the token from strtok */ - char *ptr_quote=NULL; - - /* See if the delimeter contains any quote character */ - if (strchr(delim,'\'') || strchr(delim,'\"')) - return NULL; - - /* repeate till we are getting some token from strtok */ - while ((token = (char*)strtok(string, delim) ) != NULL) - { - /* - make the input string NULL so that next time onward strtok can - be called with NULL input string. - */ - string = NULL; - /* - We don't need to remove any quote character for Windows version - */ -#ifndef __WIN__ - /* check if the current token contain double quote character*/ - if ((ptr_quote = (char*)strchr(token,'\"')) != NULL) - { - /* - get the matching the matching double quote in the remaining - input string - */ - qt_token = (char*)strtok(NULL,"\""); - } - /* check if the current token contain single quote character*/ - else if ((ptr_quote = (char*)strchr(token,'\'')) != NULL) - { - /* - get the matching the matching single quote in the remaining - input string - */ - qt_token = (char*)strtok(NULL,"\'"); - } -#endif - /* - if the current token does not contains any quote character then - return to the caller. - */ - if (ptr_quote == NULL) - { - /* - if there is any earlier token i.e. ptr_token then append the - current token in it and return it else return the current - token directly - */ - return ptr_token ? strcat(ptr_token,token) : token; - } - - /* - remove the quote character i.e. make NULL so that the token will - be devided in two part and later both part can be concatenated - and hence quote will be removed - */ - *ptr_quote= 0; - - /* check if ptr_token has been initialized or not */ - if (ptr_token == NULL) - { - /* initialize the ptr_token with current token */ - ptr_token= token; - /* copy entire string between matching pair of quote*/ - sprintf(ptr_token+strlen(ptr_token),"%s %s", ptr_quote+1, qt_token); - } - else - { - /* - copy the current token and entire string between matching pair - of quote - */ - if (qt_token == NULL) - { - sprintf(ptr_token+strlen(ptr_token),"%s%s", token, ptr_quote+1); - } - else - { - sprintf(ptr_token+strlen(ptr_token),"%s%s %s", token, ptr_quote+1, - qt_token ); - } - } - } - - /* return the concatenated token */ - return ptr_token; -} - -#ifndef __WIN__ - -/* - Synopsis: - This function run scripts files on Linux and Netware - -Arguments: - script_name: name of script file - -Output: - nothing -*/ -void run_init_script(const char *script_name) -{ - arg_list_t al; - int err; - - // args - init_args(&al); - add_arg(&al, sh_file); - add_arg(&al, script_name); - - // spawn - if ((err = spawn(sh_file, &al, TRUE, NULL, NULL, NULL, NULL)) != 0) - { - die("Unable to run script."); - } - - // free args - free_args(&al); -} -#endif diff --git a/mysql-test/r/ctype_latin1.result b/mysql-test/r/ctype_latin1.result index a8182438ac4..355f53b63a5 100644 --- a/mysql-test/r/ctype_latin1.result +++ b/mysql-test/r/ctype_latin1.result @@ -296,3 +296,12 @@ FD C3BD FD 1 FE C3BE FE 1 FF C3BF FF 1 DROP TABLE t1; +select 'a' regexp 'A' collate latin1_general_ci; +'a' regexp 'A' collate latin1_general_ci +1 +select 'a' regexp 'A' collate latin1_general_cs; +'a' regexp 'A' collate latin1_general_cs +0 +select 'a' regexp 'A' collate latin1_bin; +'a' regexp 'A' collate latin1_bin +0 diff --git a/mysql-test/r/delete.result b/mysql-test/r/delete.result index 5575ee1bf98..f1fba87c70b 100644 --- a/mysql-test/r/delete.result +++ b/mysql-test/r/delete.result @@ -16,12 +16,34 @@ SET AUTOCOMMIT=0; DELETE from t1; SET AUTOCOMMIT=1; drop table t1; -create table t1 (a bigint not null, primary key (a,a,a,a,a,a,a,a,a,a)); -insert into t1 values (2),(4),(6),(8),(10),(12),(14),(16),(18),(20),(22),(24),(26),(23); +create table t1 ( +a bigint not null, +b bigint not null default 0, +c bigint not null default 0, +d bigint not null default 0, +e bigint not null default 0, +f bigint not null default 0, +g bigint not null default 0, +h bigint not null default 0, +i bigint not null default 0, +j bigint not null default 0, +primary key (a,b,c,d,e,f,g,h,i,j)); +insert into t1 (a) values (2),(4),(6),(8),(10),(12),(14),(16),(18),(20),(22),(24),(26),(23); delete from t1 where a=26; drop table t1; -create table t1 (a bigint not null, primary key (a,a,a,a,a,a,a,a,a,a)); -insert into t1 values (2),(4),(6),(8),(10),(12),(14),(16),(18),(20),(22),(24),(26),(23),(27); +create table t1 ( +a bigint not null, +b bigint not null default 0, +c bigint not null default 0, +d bigint not null default 0, +e bigint not null default 0, +f bigint not null default 0, +g bigint not null default 0, +h bigint not null default 0, +i bigint not null default 0, +j bigint not null default 0, +primary key (a,b,c,d,e,f,g,h,i,j)); +insert into t1 (a) values (2),(4),(6),(8),(10),(12),(14),(16),(18),(20),(22),(24),(26),(23),(27); delete from t1 where a=27; drop table t1; CREATE TABLE `t1` ( diff --git a/mysql-test/r/fulltext.result b/mysql-test/r/fulltext.result index 5f0b228e23d..1951f68e822 100644 --- a/mysql-test/r/fulltext.result +++ b/mysql-test/r/fulltext.result @@ -190,6 +190,14 @@ a select * from t1 where match a against ("+aaa10 +(bbb*)" in boolean mode); a aaa10 bbb20 +select * from t1 where match a against ("+(+aaa* +bbb1*)" in boolean mode); +a +aaa20 bbb15 +aaa30 bbb10 +select * from t1 where match a against ("(+aaa* +bbb1*)" in boolean mode); +a +aaa20 bbb15 +aaa30 bbb10 drop table t1; CREATE TABLE t1 ( id int(11), diff --git a/mysql-test/r/heap.result b/mysql-test/r/heap.result index 3f468a5751e..e741a6859c7 100644 --- a/mysql-test/r/heap.result +++ b/mysql-test/r/heap.result @@ -233,3 +233,10 @@ SELECT * FROM t1 WHERE B is not null; a B 1 1 DROP TABLE t1; +CREATE TABLE t1 (pseudo char(35) PRIMARY KEY, date int(10) unsigned NOT NULL) ENGINE=HEAP; +INSERT INTO t1 VALUES ('massecot',1101106491),('altec',1101106492),('stitch+',1101106304),('Seb Corgan',1101106305),('beerfilou',1101106263),('flaker',1101106529),('joce8',5),('M4vrick',1101106418),('gabay008',1101106525),('Vamp irX',1101106291),('ZoomZip',1101106546),('rip666',1101106502),('CBP ',1101106397),('guezpard',1101106496); +DELETE FROM t1 WHERE date<1101106546; +SELECT * FROM t1; +pseudo date +ZoomZip 1101106546 +DROP TABLE t1; diff --git a/mysql-test/r/information_schema.result b/mysql-test/r/information_schema.result index 9ebf33be5bd..cdbed168df2 100644 --- a/mysql-test/r/information_schema.result +++ b/mysql-test/r/information_schema.result @@ -442,6 +442,7 @@ v select sql_mode from information_schema.ROUTINES; sql_mode +drop procedure px5; create table t1 (a int not null auto_increment,b int, primary key (a)); insert into t1 values (1,1),(NULL,3),(NULL,4); select AUTO_INCREMENT from information_schema.tables where table_name = 't1'; @@ -508,3 +509,34 @@ g int(11) 11 11 11 0 h double(10,3) 10 10 10 3 i double 22 22 22 NULL drop table t1; +create table t115 as select table_name, column_name, column_type +from information_schema.columns where table_name = 'proc'; +select * from t115; +table_name column_name column_type +proc db varchar(64) +proc name varchar(64) +proc type enum('FUNCTION','PROCEDURE') +proc specific_name varchar(64) +proc language enum('SQL') +proc sql_data_access enum('CONTAINS_SQL','NO_SQL','READS_SQL_DATA','MODIFIES_SQL_DATA') +proc is_deterministic enum('YES','NO') +proc security_type enum('INVOKER','DEFINER') +proc param_list blob +proc returns varchar(64) +proc body blob +proc definer varchar(77) +proc created timestamp +proc modified timestamp +proc sql_mode set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES','IGNORE_SPACE','NOT_USED','ONLY_FULL_GROUP_BY','NO_UNSIGNED_SUBTRACTION','NO_DIR_IN_CREATE','POSTGRESQL','ORACLE','MSSQL','DB2','MAXDB','NO_KEY_OPTIONS','NO_TABLE_OPTIONS','NO_FIELD_OPTIONS','MYSQL323','MYSQL40','ANSI','NO_AUTO_VALUE_ON_ZERO') +proc comment varchar(64) +drop table t115; +create view vk as select count(*) from information_schema.tables a; +select * from vk; +count(*) +17 +drop view vk; +create procedure p108 () begin declare c cursor for select data_type +from information_schema.columns; open c; open c; end;// +call p108()// +ERROR 24000: Cursor is already open +drop procedure p108; diff --git a/mysql-test/r/innodb.result b/mysql-test/r/innodb.result index 6097dadb1fa..9f19dd479c8 100644 --- a/mysql-test/r/innodb.result +++ b/mysql-test/r/innodb.result @@ -1664,6 +1664,24 @@ select count(*) from t1 where x = 18446744073709551601; count(*) 1 drop table t1; +create table t1 (c char(10), index (c,c)) engine=innodb; +ERROR 42S21: Duplicate column name 'c' +create table t1 (c1 char(10), c2 char(10), index (c1,c2,c1)) engine=innodb; +ERROR 42S21: Duplicate column name 'c1' +create table t1 (c1 char(10), c2 char(10), index (c1,c1,c2)) engine=innodb; +ERROR 42S21: Duplicate column name 'c1' +create table t1 (c1 char(10), c2 char(10), index (c2,c1,c1)) engine=innodb; +ERROR 42S21: Duplicate column name 'c1' +create table t1 (c1 char(10), c2 char(10)) engine=innodb; +alter table t1 add key (c1,c1); +ERROR 42S21: Duplicate column name 'c1' +alter table t1 add key (c2,c1,c1); +ERROR 42S21: Duplicate column name 'c1' +alter table t1 add key (c1,c2,c1); +ERROR 42S21: Duplicate column name 'c1' +alter table t1 add key (c1,c1,c2); +ERROR 42S21: Duplicate column name 'c1' +drop table t1; show status like "Innodb_buffer_pool_pages_total"; Variable_name Value Innodb_buffer_pool_pages_total 512 diff --git a/mysql-test/r/key.result b/mysql-test/r/key.result index b11969ab6d7..98e8851bb7e 100644 --- a/mysql-test/r/key.result +++ b/mysql-test/r/key.result @@ -311,3 +311,21 @@ test.t1 check status OK drop table t1; create table t1 (c char(10), index (c(0))); ERROR HY000: Key part 'c' length cannot be 0 +create table t1 (c char(10), index (c,c)); +ERROR 42S21: Duplicate column name 'c' +create table t1 (c1 char(10), c2 char(10), index (c1,c2,c1)); +ERROR 42S21: Duplicate column name 'c1' +create table t1 (c1 char(10), c2 char(10), index (c1,c1,c2)); +ERROR 42S21: Duplicate column name 'c1' +create table t1 (c1 char(10), c2 char(10), index (c2,c1,c1)); +ERROR 42S21: Duplicate column name 'c1' +create table t1 (c1 char(10), c2 char(10)); +alter table t1 add key (c1,c1); +ERROR 42S21: Duplicate column name 'c1' +alter table t1 add key (c2,c1,c1); +ERROR 42S21: Duplicate column name 'c1' +alter table t1 add key (c1,c2,c1); +ERROR 42S21: Duplicate column name 'c1' +alter table t1 add key (c1,c1,c2); +ERROR 42S21: Duplicate column name 'c1' +drop table t1; diff --git a/mysql-test/r/ndb_index_unique.result b/mysql-test/r/ndb_index_unique.result index af9b84022ed..31b258c0a6f 100644 --- a/mysql-test/r/ndb_index_unique.result +++ b/mysql-test/r/ndb_index_unique.result @@ -44,6 +44,51 @@ a b c 7 8 3 8 2 3 drop table t1; +CREATE TABLE t1 ( +a int unsigned NOT NULL PRIMARY KEY, +b int unsigned, +c int unsigned, +UNIQUE bc(b,c) +) engine = ndb; +insert into t1 values(1,1,1),(2,NULL,2),(3,NULL,NULL),(4,4,NULL); +select * from t1 use index (bc) where b IS NULL order by a; +a b c +2 NULL 2 +3 NULL NULL +select * from t1 use index (bc)order by a; +a b c +1 1 1 +2 NULL 2 +3 NULL NULL +4 4 NULL +select * from t1 use index (bc) order by a; +a b c +1 1 1 +2 NULL 2 +3 NULL NULL +4 4 NULL +select * from t1 use index (PRIMARY) where b IS NULL order by a; +a b c +2 NULL 2 +3 NULL NULL +select * from t1 use index (bc) where b IS NULL order by a; +a b c +2 NULL 2 +3 NULL NULL +select * from t1 use index (bc) where b IS NULL and c IS NULL order by a; +a b c +select * from t1 use index (bc) where b IS NULL and c = 2 order by a; +a b c +select * from t1 use index (bc) where b < 4 order by a; +a b c +1 1 1 +select * from t1 use index (bc) where b IS NOT NULL order by a; +a b c +1 1 1 +4 4 NULL +insert into t1 values(5,1,1); +ERROR 23000: Duplicate entry '5' for key 1 +drop table t1; CREATE TABLE t2 ( a int unsigned NOT NULL PRIMARY KEY, b int unsigned not null, @@ -87,6 +132,13 @@ a b c 7 8 3 8 2 3 drop table t2; +CREATE TABLE t2 ( +a int unsigned NOT NULL PRIMARY KEY, +b int unsigned not null, +c int unsigned, +UNIQUE USING HASH (b, c) +) engine=ndbcluster; +ERROR 42000: Column 'c' is used with UNIQUE or INDEX but is not defined as NOT NULL CREATE TABLE t3 ( a int unsigned NOT NULL, b int unsigned not null, diff --git a/mysql-test/r/ps.result b/mysql-test/r/ps.result index e0f230fa579..a4c16206b11 100644 --- a/mysql-test/r/ps.result +++ b/mysql-test/r/ps.result @@ -450,6 +450,27 @@ PREPARE stmt FROM 'UPDATE t1 AS P1 INNER JOIN (SELECT N FROM t1 GROUP BY N HAVIN EXECUTE stmt; DEALLOCATE PREPARE stmt; DROP TABLE t1; +prepare stmt from "select ? is null, ? is not null, ?"; +select @no_such_var is null, @no_such_var is not null, @no_such_var; +@no_such_var is null @no_such_var is not null @no_such_var +1 0 NULL +execute stmt using @no_such_var, @no_such_var, @no_such_var; +? is null ? is not null ? +1 0 NULL +set @var='abc'; +select @var is null, @var is not null, @var; +@var is null @var is not null @var +0 1 abc +execute stmt using @var, @var, @var; +? is null ? is not null ? +0 1 abc +set @var=null; +select @var is null, @var is not null, @var; +@var is null @var is not null @var +1 0 NULL +execute stmt using @var, @var, @var; +? is null ? is not null ? +1 0 NULL create table t1 (a varchar(20)); insert into t1 values ('foo'); prepare stmt FROM 'SELECT char_length (a) FROM t1'; diff --git a/mysql-test/r/range.result b/mysql-test/r/range.result index 1374a8fd0ed..8a1d80e9f79 100644 --- a/mysql-test/r/range.result +++ b/mysql-test/r/range.result @@ -218,12 +218,14 @@ drop table t1; create table t1 (x int, y int, index(x), index(y)); insert into t1 (x) values (1),(2),(3),(4),(5),(6),(7),(8),(9); update t1 set y=x; -explain select * from t1, t1 t2 where t1.y = 2 and t2.x between 7 and t1.y+0; +explain select * from t1, t1 t2 where t1.y = 8 and t2.x between 7 and t1.y+0; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables -explain select * from t1, t1 t2 where t1.y = 2 and t2.x >= 7 and t2.x <= t1.y+0; +1 SIMPLE t1 ref y y 5 const 1 Using where +1 SIMPLE t2 range x x 5 NULL 2 Using where +explain select * from t1, t1 t2 where t1.y = 8 and t2.x >= 7 and t2.x <= t1.y+0; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables +1 SIMPLE t1 ref y y 5 const 1 Using where +1 SIMPLE t2 range x x 5 NULL 2 Using where explain select * from t1, t1 t2 where t1.y = 2 and t2.x between t1.y-1 and t1.y+1; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 ref y y 5 const 1 Using where diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index ceb8919b26a..20ab3ac8f80 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -1998,3 +1998,18 @@ ac 700 NULL drop tables t1,t2; +create table t1 (a int not null, b int not null, c int, primary key (a,b)); +insert into t1 values (1,1,1), (2,2,2), (3,3,3); +set @b:= 0; +explain select sum(a) from t1 where b > @b; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 index NULL PRIMARY 8 NULL 3 Using where; Using index +set @a:= (select sum(a) from t1 where b > @b); +explain select a from t1 where c=2; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 Using where +do @a:= (select sum(a) from t1 where b > @b); +explain select a from t1 where c=2; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 Using where +drop table t1; diff --git a/mysql-test/r/trigger.result b/mysql-test/r/trigger.result index b45aaea0cbe..4a85097cfce 100644 --- a/mysql-test/r/trigger.result +++ b/mysql-test/r/trigger.result @@ -150,6 +150,10 @@ create trigger trg before delete on t1 for each row set new.i:=1; ERROR HY000: There is no NEW row in on DELETE trigger create trigger trg after update on t1 for each row set new.i:=1; ERROR HY000: Updating of NEW row is not allowed in after trigger +create trigger trg before update on t1 for each row set new.j:=1; +ERROR 42S22: Unknown column 'j' in 'NEW' +create trigger trg before update on t1 for each row set @a:=old.j; +ERROR 42S22: Unknown column 'j' in 'OLD' create trigger trg before insert on t2 for each row set @a:=1; ERROR 42S02: Table 'test.t2' doesn't exist create trigger trg before insert on t1 for each row set @a:=1; diff --git a/mysql-test/r/type_blob.result b/mysql-test/r/type_blob.result index 2f564112e40..c9c15b4c293 100644 --- a/mysql-test/r/type_blob.result +++ b/mysql-test/r/type_blob.result @@ -682,8 +682,8 @@ id txt 3 NULL 1 Chevy drop table t1; -CREATE TABLE t1 ( i int(11) NOT NULL default '0', c text NOT NULL, PRIMARY KEY (i), KEY (c(1),c(1))); -INSERT t1 VALUES (1,''),(2,''),(3,'asdfh'),(4,''); +CREATE TABLE t1 ( i int(11) NOT NULL default '0', c text NOT NULL, d varchar(1) NOT NULL DEFAULT ' ', PRIMARY KEY (i), KEY (c(1),d)); +INSERT t1 (i, c) VALUES (1,''),(2,''),(3,'asdfh'),(4,''); select max(i) from t1 where c = ''; max(i) 4 diff --git a/mysql-test/t/ctype_latin1.test b/mysql-test/t/ctype_latin1.test index 14062437428..677acd9faa9 100644 --- a/mysql-test/t/ctype_latin1.test +++ b/mysql-test/t/ctype_latin1.test @@ -53,3 +53,10 @@ SELECT hex(@l:=convert(@u using latin1)), a=@l FROM t1; DROP TABLE t1; + +# +# Bug #6737: REGEXP gives wrong result with case sensitive collation +# +select 'a' regexp 'A' collate latin1_general_ci; +select 'a' regexp 'A' collate latin1_general_cs; +select 'a' regexp 'A' collate latin1_bin; diff --git a/mysql-test/t/delete.test b/mysql-test/t/delete.test index 5f60445d765..0bf7187865d 100644 --- a/mysql-test/t/delete.test +++ b/mysql-test/t/delete.test @@ -29,12 +29,34 @@ drop table t1; # (This assumes a block size of 1024) # -create table t1 (a bigint not null, primary key (a,a,a,a,a,a,a,a,a,a)); -insert into t1 values (2),(4),(6),(8),(10),(12),(14),(16),(18),(20),(22),(24),(26),(23); +create table t1 ( + a bigint not null, + b bigint not null default 0, + c bigint not null default 0, + d bigint not null default 0, + e bigint not null default 0, + f bigint not null default 0, + g bigint not null default 0, + h bigint not null default 0, + i bigint not null default 0, + j bigint not null default 0, + primary key (a,b,c,d,e,f,g,h,i,j)); +insert into t1 (a) values (2),(4),(6),(8),(10),(12),(14),(16),(18),(20),(22),(24),(26),(23); delete from t1 where a=26; drop table t1; -create table t1 (a bigint not null, primary key (a,a,a,a,a,a,a,a,a,a)); -insert into t1 values (2),(4),(6),(8),(10),(12),(14),(16),(18),(20),(22),(24),(26),(23),(27); +create table t1 ( + a bigint not null, + b bigint not null default 0, + c bigint not null default 0, + d bigint not null default 0, + e bigint not null default 0, + f bigint not null default 0, + g bigint not null default 0, + h bigint not null default 0, + i bigint not null default 0, + j bigint not null default 0, + primary key (a,b,c,d,e,f,g,h,i,j)); +insert into t1 (a) values (2),(4),(6),(8),(10),(12),(14),(16),(18),(20),(22),(24),(26),(23),(27); delete from t1 where a=27; drop table t1; diff --git a/mysql-test/t/fulltext.test b/mysql-test/t/fulltext.test index afbe8f8117c..41fbf3f27ac 100644 --- a/mysql-test/t/fulltext.test +++ b/mysql-test/t/fulltext.test @@ -98,6 +98,8 @@ select * from t1 where match a against ("+aaa* +bbb*" in boolean mode); select * from t1 where match a against ("+aaa* +bbb1*" in boolean mode); select * from t1 where match a against ("+aaa* +ccc*" in boolean mode); select * from t1 where match a against ("+aaa10 +(bbb*)" in boolean mode); +select * from t1 where match a against ("+(+aaa* +bbb1*)" in boolean mode); +select * from t1 where match a against ("(+aaa* +bbb1*)" in boolean mode); drop table t1; # diff --git a/mysql-test/t/heap.test b/mysql-test/t/heap.test index e1776245d9e..2eff36f3317 100644 --- a/mysql-test/t/heap.test +++ b/mysql-test/t/heap.test @@ -174,3 +174,14 @@ CREATE TABLE t1 (a INT NOT NULL, B INT, KEY(B)) ENGINE=HEAP; INSERT INTO t1 VALUES(1,1), (1,NULL); SELECT * FROM t1 WHERE B is not null; DROP TABLE t1; + +# +# Bug #6748 +# heap_rfirst() doesn't work (and never did!) +# +CREATE TABLE t1 (pseudo char(35) PRIMARY KEY, date int(10) unsigned NOT NULL) ENGINE=HEAP; +INSERT INTO t1 VALUES ('massecot',1101106491),('altec',1101106492),('stitch+',1101106304),('Seb Corgan',1101106305),('beerfilou',1101106263),('flaker',1101106529),('joce8',5),('M4vrick',1101106418),('gabay008',1101106525),('Vamp irX',1101106291),('ZoomZip',1101106546),('rip666',1101106502),('CBP ',1101106397),('guezpard',1101106496); +DELETE FROM t1 WHERE date<1101106546; +SELECT * FROM t1; +DROP TABLE t1; + diff --git a/mysql-test/t/information_schema.test b/mysql-test/t/information_schema.test index ce8dc0290e9..b52a3c2edec 100644 --- a/mysql-test/t/information_schema.test +++ b/mysql-test/t/information_schema.test @@ -218,6 +218,7 @@ call px5()// call px5()// delimiter ;// select sql_mode from information_schema.ROUTINES; +drop procedure px5; create table t1 (a int not null auto_increment,b int, primary key (a)); insert into t1 values (1,1),(NULL,3),(NULL,4); @@ -252,3 +253,20 @@ select COLUMN_NAME,COLUMN_TYPE, CHARACTER_MAXIMUM_LENGTH, CHARACTER_OCTET_LENGTH, NUMERIC_PRECISION, NUMERIC_SCALE from information_schema.columns where table_name= 't1'; drop table t1; + +create table t115 as select table_name, column_name, column_type +from information_schema.columns where table_name = 'proc'; +select * from t115; +drop table t115; + +create view vk as select count(*) from information_schema.tables a; +select * from vk; +drop view vk; + +delimiter //; +create procedure p108 () begin declare c cursor for select data_type +from information_schema.columns; open c; open c; end;// +--error 1325 +call p108()// +delimiter ;// +drop procedure p108; diff --git a/mysql-test/t/innodb.test b/mysql-test/t/innodb.test index 5ea6817bfba..47324f1b828 100644 --- a/mysql-test/t/innodb.test +++ b/mysql-test/t/innodb.test @@ -1180,6 +1180,28 @@ select count(*) from t1 where x = 18446744073709551601; drop table t1; +# +# Bug #6126: Duplicate columns in keys gives misleading error message +# +--error 1060 +create table t1 (c char(10), index (c,c)) engine=innodb; +--error 1060 +create table t1 (c1 char(10), c2 char(10), index (c1,c2,c1)) engine=innodb; +--error 1060 +create table t1 (c1 char(10), c2 char(10), index (c1,c1,c2)) engine=innodb; +--error 1060 +create table t1 (c1 char(10), c2 char(10), index (c2,c1,c1)) engine=innodb; +create table t1 (c1 char(10), c2 char(10)) engine=innodb; +--error 1060 +alter table t1 add key (c1,c1); +--error 1060 +alter table t1 add key (c2,c1,c1); +--error 1060 +alter table t1 add key (c1,c2,c1); +--error 1060 +alter table t1 add key (c1,c1,c2); +drop table t1; + # Test for testable InnoDB status variables. This test # uses previous ones(pages_created, rows_deleted, ...). show status like "Innodb_buffer_pool_pages_total"; diff --git a/mysql-test/t/key.test b/mysql-test/t/key.test index 0a86c1cd145..af3509c8454 100644 --- a/mysql-test/t/key.test +++ b/mysql-test/t/key.test @@ -301,3 +301,26 @@ drop table t1; --error 1391 create table t1 (c char(10), index (c(0))); + +# +# Bug #6126: Duplicate columns in keys should fail +# Bug #6252: (dup) +# +--error 1060 +create table t1 (c char(10), index (c,c)); +--error 1060 +create table t1 (c1 char(10), c2 char(10), index (c1,c2,c1)); +--error 1060 +create table t1 (c1 char(10), c2 char(10), index (c1,c1,c2)); +--error 1060 +create table t1 (c1 char(10), c2 char(10), index (c2,c1,c1)); +create table t1 (c1 char(10), c2 char(10)); +--error 1060 +alter table t1 add key (c1,c1); +--error 1060 +alter table t1 add key (c2,c1,c1); +--error 1060 +alter table t1 add key (c1,c2,c1); +--error 1060 +alter table t1 add key (c1,c1,c2); +drop table t1; diff --git a/mysql-test/t/ndb_autodiscover.test b/mysql-test/t/ndb_autodiscover.test index fd7fe0e60d8..6551732adba 100644 --- a/mysql-test/t/ndb_autodiscover.test +++ b/mysql-test/t/ndb_autodiscover.test @@ -199,7 +199,7 @@ insert into t4 values (1, "Automatic"); select * from t4; # Remove the table from NDB -system exec $NDB_TOOLS_DIR/ndb_drop_table -d test t4 > /dev/null ; +system exec $NDB_TOOLS_DIR/ndb_drop_table --no-defaults -d test t4 > /dev/null ; # # Test that correct error is returned @@ -230,7 +230,7 @@ select * from t4; flush tables; # Remove the table from NDB -system exec $NDB_TOOLS_DIR/ndb_drop_table -d test t4 > /dev/null ; +system exec $NDB_TOOLS_DIR/ndb_drop_table --no-defaults -d test t4 > /dev/null ; SHOW TABLES; @@ -264,8 +264,8 @@ insert into t8 values (8, "myisam table 8"); insert into t9 values (9); # Remove t3, t5 from NDB -system exec $NDB_TOOLS_DIR/ndb_drop_table -d test t3 > /dev/null ; -system exec $NDB_TOOLS_DIR/ndb_drop_table -d test t5 > /dev/null ; +system exec $NDB_TOOLS_DIR/ndb_drop_table --no-defaults -d test t3 > /dev/null ; +system exec $NDB_TOOLS_DIR/ndb_drop_table --no-defaults -d test t5 > /dev/null ; # Remove t6, t7 from disk system rm var/master-data/test/t6.frm > /dev/null ; system rm var/master-data/test/t7.frm > /dev/null ; @@ -306,8 +306,8 @@ insert into t8 values (8, "myisam table 8"); insert into t9 values (9); # Remove t3, t5 from NDB -system exec $NDB_TOOLS_DIR/ndb_drop_table -d test t3 > /dev/null ; -system exec $NDB_TOOLS_DIR/ndb_drop_table -d test t5 > /dev/null ; +system exec $NDB_TOOLS_DIR/ndb_drop_table --no-defaults -d test t3 > /dev/null ; +system exec $NDB_TOOLS_DIR/ndb_drop_table --no-defaults -d test t5 > /dev/null ; # Remove t6, t7 from disk system rm var/master-data/test/t6.frm > /dev/null ; system rm var/master-data/test/t7.frm > /dev/null ; @@ -479,4 +479,4 @@ create table t10 ( insert into t10 values (1, 'kalle'); ---exec $NDB_TOOLS_DIR/ndb_drop_table -d test `$NDB_TOOLS_DIR/ndb_show_tables | grep BLOB` > /dev/null 2>&1 || true +--exec $NDB_TOOLS_DIR/ndb_drop_table --no-defaults -d test `$NDB_TOOLS_DIR/ndb_show_tables --no-defaults | grep BLOB` > /dev/null 2>&1 || true diff --git a/mysql-test/t/ndb_index_unique.test b/mysql-test/t/ndb_index_unique.test index bdb23949763..397a2c45a9f 100644 --- a/mysql-test/t/ndb_index_unique.test +++ b/mysql-test/t/ndb_index_unique.test @@ -30,6 +30,32 @@ select * from t1 order by a; drop table t1; +# +# Indexing NULL values +# + +CREATE TABLE t1 ( + a int unsigned NOT NULL PRIMARY KEY, + b int unsigned, + c int unsigned, + UNIQUE bc(b,c) +) engine = ndb; + +insert into t1 values(1,1,1),(2,NULL,2),(3,NULL,NULL),(4,4,NULL); +select * from t1 use index (bc) where b IS NULL order by a; + +select * from t1 use index (bc)order by a; +select * from t1 use index (bc) order by a; +select * from t1 use index (PRIMARY) where b IS NULL order by a; +select * from t1 use index (bc) where b IS NULL order by a; +select * from t1 use index (bc) where b IS NULL and c IS NULL order by a; +select * from t1 use index (bc) where b IS NULL and c = 2 order by a; +select * from t1 use index (bc) where b < 4 order by a; +select * from t1 use index (bc) where b IS NOT NULL order by a; +-- error 1062 +insert into t1 values(5,1,1); +drop table t1; + # # Show use of UNIQUE USING HASH indexes @@ -58,6 +84,14 @@ select * from t2 order by a; drop table t2; +-- error 1121 +CREATE TABLE t2 ( + a int unsigned NOT NULL PRIMARY KEY, + b int unsigned not null, + c int unsigned, + UNIQUE USING HASH (b, c) +) engine=ndbcluster; + # # Show use of PRIMARY KEY USING HASH indexes # diff --git a/mysql-test/t/ps.test b/mysql-test/t/ps.test index 1d23a09a71e..157b125a13d 100644 --- a/mysql-test/t/ps.test +++ b/mysql-test/t/ps.test @@ -458,6 +458,20 @@ EXECUTE stmt; DEALLOCATE PREPARE stmt; DROP TABLE t1; +# +# Bug#6297 "prepared statement, wrong handling of IS NULL" +# Test that placeholders work with IS NULL/IS NOT NULL clauses. +# +prepare stmt from "select ? is null, ? is not null, ?"; +select @no_such_var is null, @no_such_var is not null, @no_such_var; +execute stmt using @no_such_var, @no_such_var, @no_such_var; +set @var='abc'; +select @var is null, @var is not null, @var; +execute stmt using @var, @var, @var; +set @var=null; +select @var is null, @var is not null, @var; +execute stmt using @var, @var, @var; + # # Bug#6102 "Server crash with prepared statement and blank after # function name" @@ -469,3 +483,4 @@ insert into t1 values ('foo'); --error 1305 prepare stmt FROM 'SELECT char_length (a) FROM t1'; drop table t1; + diff --git a/mysql-test/t/range.test b/mysql-test/t/range.test index 0b96f71d585..18cf614f338 100644 --- a/mysql-test/t/range.test +++ b/mysql-test/t/range.test @@ -181,8 +181,8 @@ create table t1 (x int, y int, index(x), index(y)); insert into t1 (x) values (1),(2),(3),(4),(5),(6),(7),(8),(9); update t1 set y=x; # between with only one end fixed -explain select * from t1, t1 t2 where t1.y = 2 and t2.x between 7 and t1.y+0; -explain select * from t1, t1 t2 where t1.y = 2 and t2.x >= 7 and t2.x <= t1.y+0; +explain select * from t1, t1 t2 where t1.y = 8 and t2.x between 7 and t1.y+0; +explain select * from t1, t1 t2 where t1.y = 8 and t2.x >= 7 and t2.x <= t1.y+0; # between with both expressions on both ends explain select * from t1, t1 t2 where t1.y = 2 and t2.x between t1.y-1 and t1.y+1; explain select * from t1, t1 t2 where t1.y = 2 and t2.x >= t1.y-1 and t2.x <= t1.y+1; diff --git a/mysql-test/t/rpl000001.test b/mysql-test/t/rpl000001.test index 2e0ba2fff25..835af92186f 100644 --- a/mysql-test/t/rpl000001.test +++ b/mysql-test/t/rpl000001.test @@ -98,7 +98,7 @@ wait_for_slave_to_stop; # The following test can't be done because the result of Pos will differ # on different computers -# --replace_result 9306 9999 3334 9999 3335 9999 +# --replace_result $MASTER_MYPORT MASTER_PORT # show slave status; set global sql_slave_skip_counter=1; diff --git a/mysql-test/t/subselect.test b/mysql-test/t/subselect.test index 16556c4864c..9cf9f13cfea 100644 --- a/mysql-test/t/subselect.test +++ b/mysql-test/t/subselect.test @@ -1287,3 +1287,22 @@ INSERT INTO `t2` VALUES (6,5,12,7,'a'),(12,0,0,7,'a'),(12,1,0,7,'a'),(12,5,5,7,' SELECT b.sc FROM (SELECT (SELECT a.access FROM t1 a WHERE a.map = op.map AND a.slave = op.pid AND a.master = 1) ac FROM t2 op WHERE op.id = 12 AND op.map = 0) b; SELECT b.ac FROM (SELECT (SELECT a.access FROM t1 a WHERE a.map = op.map AND a.slave = op.pid AND a.master = 1) ac FROM t2 op WHERE op.id = 12 AND op.map = 0) b; drop tables t1,t2; + +# +# Test for bug #6462. "Same request on same data returns different +# results." a.k.a. "Proper cleanup of subqueries is missing for +# SET and DO statements". +# +create table t1 (a int not null, b int not null, c int, primary key (a,b)); +insert into t1 values (1,1,1), (2,2,2), (3,3,3); +set @b:= 0; +# Let us check that subquery will use covering index +explain select sum(a) from t1 where b > @b; +# This should not crash -debug server due to failing assertion +set @a:= (select sum(a) from t1 where b > @b); +# And this should not falsely report index usage +explain select a from t1 where c=2; +# Same for DO statement +do @a:= (select sum(a) from t1 where b > @b); +explain select a from t1 where c=2; +drop table t1; diff --git a/mysql-test/t/trigger.test b/mysql-test/t/trigger.test index 7dc976cf716..d4879b22bae 100644 --- a/mysql-test/t/trigger.test +++ b/mysql-test/t/trigger.test @@ -161,8 +161,10 @@ create trigger trg before update on t1 for each row set old.i:=1; create trigger trg before delete on t1 for each row set new.i:=1; --error 1362 create trigger trg after update on t1 for each row set new.i:=1; -# TODO: We should also test wrong field names here, we don't do it now -# because proper error handling is not in place yet. +--error 1054 +create trigger trg before update on t1 for each row set new.j:=1; +--error 1054 +create trigger trg before update on t1 for each row set @a:=old.j; # diff --git a/mysql-test/t/type_blob.test b/mysql-test/t/type_blob.test index b67fa7a552d..f70193ddbe0 100644 --- a/mysql-test/t/type_blob.test +++ b/mysql-test/t/type_blob.test @@ -369,8 +369,8 @@ explain select * from t1 where txt='Chevy' or txt is NULL order by txt; select * from t1 where txt='Chevy' or txt is NULL order by txt; drop table t1; -CREATE TABLE t1 ( i int(11) NOT NULL default '0', c text NOT NULL, PRIMARY KEY (i), KEY (c(1),c(1))); -INSERT t1 VALUES (1,''),(2,''),(3,'asdfh'),(4,''); +CREATE TABLE t1 ( i int(11) NOT NULL default '0', c text NOT NULL, d varchar(1) NOT NULL DEFAULT ' ', PRIMARY KEY (i), KEY (c(1),d)); +INSERT t1 (i, c) VALUES (1,''),(2,''),(3,'asdfh'),(4,''); select max(i) from t1 where c = ''; drop table t1; diff --git a/mysys/charset.c b/mysys/charset.c index 1388fc40c6d..cb2379f8723 100644 --- a/mysys/charset.c +++ b/mysys/charset.c @@ -228,6 +228,7 @@ static int add_collation(CHARSET_INFO *cs) } else { + uchar *sort_order= all_charsets[cs->number]->sort_order; simple_cs_init_functions(all_charsets[cs->number]); new->mbminlen= 1; new->mbmaxlen= 1; @@ -236,6 +237,16 @@ static int add_collation(CHARSET_INFO *cs) all_charsets[cs->number]->state |= MY_CS_LOADED; } all_charsets[cs->number]->state|= MY_CS_AVAILABLE; + + /* + Check if case sensitive sort order: A < a < B. + We need MY_CS_FLAG for regex library, and for + case sensitivity flag for 5.0 client protocol, + to support isCaseSensitive() method in JDBC driver + */ + if (sort_order && sort_order['A'] < sort_order['a'] && + sort_order['a'] < sort_order['B']) + all_charsets[cs->number]->state|= MY_CS_CSSORT; } } else diff --git a/ndb/include/ndbapi/NdbConnection.hpp b/ndb/include/ndbapi/NdbConnection.hpp index 077b0bce53c..f0f44170ed7 100644 --- a/ndb/include/ndbapi/NdbConnection.hpp +++ b/ndb/include/ndbapi/NdbConnection.hpp @@ -646,8 +646,8 @@ private: NdbOperation* theLastExecOpInList; // Last executing operation in list. - NdbOperation* theCompletedFirstOp; // First operation in completed - // operation list. + NdbOperation* theCompletedFirstOp; // First & last operation in completed + NdbOperation* theCompletedLastOp; // operation list. Uint32 theNoOfOpSent; // How many operations have been sent Uint32 theNoOfOpCompleted; // How many operations have completed diff --git a/ndb/src/kernel/blocks/ERROR_codes.txt b/ndb/src/kernel/blocks/ERROR_codes.txt index 7ff03684cff..5193d3eae9d 100644 --- a/ndb/src/kernel/blocks/ERROR_codes.txt +++ b/ndb/src/kernel/blocks/ERROR_codes.txt @@ -1,7 +1,7 @@ Next QMGR 1 Next NDBCNTR 1000 Next NDBFS 2000 -Next DBACC 3001 +Next DBACC 3002 Next DBTUP 4013 Next DBLQH 5042 Next DBDICT 6006 @@ -393,6 +393,7 @@ Failed Create Table: -------------------- 7173: Create table failed due to not sufficient number of fragment or replica records. +3001: Fail create 1st fragment 4007 12001: Fail create 1st fragment 4008 12002: Fail create 2nd fragment 4009 12003: Fail create 1st attribute in 1st fragment diff --git a/ndb/src/kernel/blocks/dbacc/DbaccMain.cpp b/ndb/src/kernel/blocks/dbacc/DbaccMain.cpp index 7d829095be7..1a5e22ac70a 100644 --- a/ndb/src/kernel/blocks/dbacc/DbaccMain.cpp +++ b/ndb/src/kernel/blocks/dbacc/DbaccMain.cpp @@ -1069,7 +1069,21 @@ void Dbacc::execACCFRAGREQ(Signal* signal) { const AccFragReq * const req = (AccFragReq*)&signal->theData[0]; jamEntry(); + if (ERROR_INSERTED(3001)) { + jam(); + addFragRefuse(signal, 1); + CLEAR_ERROR_INSERT_VALUE; + return; + } tabptr.i = req->tableId; +#ifndef VM_TRACE + // config mismatch - do not crash if release compiled + if (tabptr.i >= ctablesize) { + jam(); + addFragRefuse(signal, 800); + return; + } +#endif ptrCheckGuard(tabptr, ctablesize, tabrec); ndbrequire((req->reqInfo & 0xF) == ZADDFRAG); ndbrequire(!getrootfragmentrec(signal, rootfragrecptr, req->fragId)); diff --git a/ndb/src/kernel/blocks/dbtup/DbtupMeta.cpp b/ndb/src/kernel/blocks/dbtup/DbtupMeta.cpp index 914dba00674..405f790954e 100644 --- a/ndb/src/kernel/blocks/dbtup/DbtupMeta.cpp +++ b/ndb/src/kernel/blocks/dbtup/DbtupMeta.cpp @@ -69,6 +69,17 @@ void Dbtup::execTUPFRAGREQ(Signal* signal) Uint32 noOfAttributeGroups = signal->theData[12]; Uint32 globalCheckpointIdIndicator = signal->theData[13]; +#ifndef VM_TRACE + // config mismatch - do not crash if release compiled + if (regTabPtr.i >= cnoOfTablerec) { + ljam(); + signal->theData[0] = userptr; + signal->theData[1] = 800; + sendSignal(userblockref, GSN_TUPFRAGREF, signal, 2, JBB); + return; + } +#endif + ptrCheckGuard(regTabPtr, cnoOfTablerec, tablerec); if (cfirstfreeFragopr == RNIL) { ljam(); diff --git a/ndb/src/ndbapi/NdbConnection.cpp b/ndb/src/ndbapi/NdbConnection.cpp index 426b2c55d9f..fec14b9c522 100644 --- a/ndb/src/ndbapi/NdbConnection.cpp +++ b/ndb/src/ndbapi/NdbConnection.cpp @@ -55,6 +55,7 @@ NdbConnection::NdbConnection( Ndb* aNdb ) : theFirstExecOpInList(NULL), theLastExecOpInList(NULL), theCompletedFirstOp(NULL), + theCompletedLastOp(NULL), theNoOfOpSent(0), theNoOfOpCompleted(0), theNoOfOpFetched(0), @@ -124,6 +125,7 @@ NdbConnection::init() theLastExecOpInList = NULL; theCompletedFirstOp = NULL; + theCompletedLastOp = NULL; theGlobalCheckpointId = 0; theCommitStatus = Started; @@ -256,6 +258,8 @@ NdbConnection::handleExecuteCompletion() if (tLastExecOp != NULL) { tLastExecOp->next(theCompletedFirstOp); theCompletedFirstOp = tFirstExecOp; + if (theCompletedLastOp == NULL) + theCompletedLastOp = tLastExecOp; theFirstExecOpInList = NULL; theLastExecOpInList = NULL; }//if @@ -292,6 +296,8 @@ NdbConnection::execute(ExecType aTypeOfExec, ExecType tExecType; NdbOperation* tPrepOp; + NdbOperation* tCompletedFirstOp = NULL; + NdbOperation* tCompletedLastOp = NULL; int ret = 0; do { @@ -314,6 +320,7 @@ NdbConnection::execute(ExecType aTypeOfExec, } tPrepOp = tPrepOp->next(); } + // save rest of prepared ops if batch NdbOperation* tRestOp= 0; NdbOperation* tLastOp= 0; @@ -323,6 +330,7 @@ NdbConnection::execute(ExecType aTypeOfExec, tLastOp = theLastOpInList; theLastOpInList = tPrepOp; } + if (tExecType == Commit) { NdbOperation* tOp = theCompletedFirstOp; while (tOp != NULL) { @@ -338,6 +346,19 @@ NdbConnection::execute(ExecType aTypeOfExec, } } + // completed ops are in unspecified order + if (theCompletedFirstOp != NULL) { + if (tCompletedFirstOp == NULL) { + tCompletedFirstOp = theCompletedFirstOp; + tCompletedLastOp = theCompletedLastOp; + } else { + tCompletedLastOp->next(theCompletedFirstOp); + tCompletedLastOp = theCompletedLastOp; + } + theCompletedFirstOp = NULL; + theCompletedLastOp = NULL; + } + if (executeNoBlobs(tExecType, abortOption, forceSend) == -1) ret = -1; #ifndef VM_TRACE @@ -362,6 +383,7 @@ NdbConnection::execute(ExecType aTypeOfExec, tOp = tOp->next(); } } + // add saved prepared ops if batch if (tPrepOp != NULL && tRestOp != NULL) { if (theFirstOpInList == NULL) @@ -373,6 +395,18 @@ NdbConnection::execute(ExecType aTypeOfExec, assert(theFirstOpInList == NULL || tExecType == NoCommit); } while (theFirstOpInList != NULL || tExecType != aTypeOfExec); + if (tCompletedFirstOp != NULL) { + tCompletedLastOp->next(theCompletedFirstOp); + theCompletedFirstOp = tCompletedFirstOp; + if (theCompletedLastOp == NULL) + theCompletedLastOp = tCompletedLastOp; + } +#if ndb_api_count_completed_ops_after_blob_execute + { NdbOperation* tOp; unsigned n = 0; + for (tOp = theCompletedFirstOp; tOp != NULL; tOp = tOp->next()) n++; + ndbout << "completed ops: " << n << endl; + } +#endif DBUG_RETURN(ret); } @@ -894,6 +928,7 @@ NdbConnection::releaseOperations() releaseOps(theFirstExecOpInList); theCompletedFirstOp = NULL; + theCompletedLastOp = NULL; theFirstOpInList = NULL; theFirstExecOpInList = NULL; theLastOpInList = NULL; @@ -909,6 +944,7 @@ NdbConnection::releaseCompletedOperations() { releaseOps(theCompletedFirstOp); theCompletedFirstOp = NULL; + theCompletedLastOp = NULL; }//NdbConnection::releaseOperations() /****************************************************************************** diff --git a/ndb/test/ndbapi/testBlobs.cpp b/ndb/test/ndbapi/testBlobs.cpp index efa0811aa39..4b532856709 100644 --- a/ndb/test/ndbapi/testBlobs.cpp +++ b/ndb/test/ndbapi/testBlobs.cpp @@ -22,6 +22,7 @@ #include #include #include +#include struct Bcol { bool m_nullable; @@ -59,6 +60,9 @@ struct Opt { bool m_oneblob; Bcol m_blob1; Bcol m_blob2; + // perf + const char* m_tnameperf; + unsigned m_rowsperf; // bugs int m_bug; int (*m_bugtest)(); @@ -84,6 +88,9 @@ struct Opt { m_oneblob(false), m_blob1(false, 7, 1137, 10), m_blob2(true, 99, 55, 1), + // perf + m_tnameperf("TBLOB2"), + m_rowsperf(10000), // bugs m_bug(0), m_bugtest(0) { @@ -107,6 +114,7 @@ printusage() << " -loop N loop N times 0=forever [" << d.m_loop << "]" << endl << " -parts N max parts in blob value [" << d.m_parts << "]" << endl << " -rows N number of rows [" << d.m_rows << "]" << endl + << " -rowsperf N rows for performace test [" << d.m_rowsperf << "]" << endl << " -seed N random seed 0=loop number [" << d.m_seed << "]" << endl << " -skip xxx skip given tests (see list) [no tests]" << endl << " -test xxx only given tests (see list) [all tests]" << endl @@ -118,6 +126,7 @@ printusage() << " i hash index ops" << endl << " s table scans" << endl << " r ordered index scans" << endl + << " p performance test" << endl << "additional flags for test/skip" << endl << " u update existing blob value" << endl << " n normal insert and update" << endl @@ -1381,6 +1390,292 @@ testmain() return 0; } +// separate performance test + +struct Tmr { // stolen from testOIBasic + Tmr() { + clr(); + } + void clr() { + m_on = m_ms = m_cnt = m_time[0] = m_text[0] = 0; + } + void on() { + assert(m_on == 0); + m_on = NdbTick_CurrentMillisecond(); + } + void off(unsigned cnt = 0) { + NDB_TICKS off = NdbTick_CurrentMillisecond(); + assert(m_on != 0 && off >= m_on); + m_ms += off - m_on; + m_cnt += cnt; + m_on = 0; + } + const char* time() { + if (m_cnt == 0) + sprintf(m_time, "%u ms", m_ms); + else + sprintf(m_time, "%u ms per %u ( %u ms per 1000 )", m_ms, m_cnt, (1000 * m_ms) / m_cnt); + return m_time; + } + const char* pct (const Tmr& t1) { + if (0 < t1.m_ms) + sprintf(m_text, "%u pct", (100 * m_ms) / t1.m_ms); + else + sprintf(m_text, "[cannot measure]"); + return m_text; + } + const char* over(const Tmr& t1) { + if (0 < t1.m_ms) { + if (t1.m_ms <= m_ms) + sprintf(m_text, "%u pct", (100 * (m_ms - t1.m_ms)) / t1.m_ms); + else + sprintf(m_text, "-%u pct", (100 * (t1.m_ms - m_ms)) / t1.m_ms); + } else + sprintf(m_text, "[cannot measure]"); + return m_text; + } + NDB_TICKS m_on; + unsigned m_ms; + unsigned m_cnt; + char m_time[100]; + char m_text[100]; +}; + +static int +testperf() +{ + if (! testcase('p')) + return 0; + DBG("=== perf test ==="); + g_ndb = new Ndb("TEST_DB"); + CHK(g_ndb->init() == 0); + CHK(g_ndb->waitUntilReady() == 0); + g_dic = g_ndb->getDictionary(); + NdbDictionary::Table tab(g_opt.m_tnameperf); + if (g_dic->getTable(tab.getName()) != 0) + CHK(g_dic->dropTable(tab) == 0); + // col A - pk + { NdbDictionary::Column col("A"); + col.setType(NdbDictionary::Column::Unsigned); + col.setPrimaryKey(true); + tab.addColumn(col); + } + // col B - char 20 + { NdbDictionary::Column col("B"); + col.setType(NdbDictionary::Column::Char); + col.setLength(20); + col.setNullable(true); + tab.addColumn(col); + } + // col C - text + { NdbDictionary::Column col("C"); + col.setType(NdbDictionary::Column::Text); + col.setInlineSize(20); + col.setPartSize(512); + col.setStripeSize(1); + col.setNullable(true); + tab.addColumn(col); + } + // create + CHK(g_dic->createTable(tab) == 0); + Uint32 cA = 0, cB = 1, cC = 2; + // timers + Tmr t1; + Tmr t2; + // insert char (one trans) + { + DBG("--- insert char ---"); + t1.on(); + CHK((g_con = g_ndb->startTransaction()) != 0); + for (Uint32 k = 0; k < g_opt.m_rowsperf; k++) { + CHK((g_opr = g_con->getNdbOperation(tab.getName())) != 0); + CHK(g_opr->insertTuple() == 0); + CHK(g_opr->equal(cA, (char*)&k) == 0); + CHK(g_opr->setValue(cB, "b") == 0); + CHK(g_con->execute(NoCommit) == 0); + } + t1.off(g_opt.m_rowsperf); + CHK(g_con->execute(Rollback) == 0); + DBG(t1.time()); + g_opr = 0; + g_con = 0; + } + // insert text (one trans) + { + DBG("--- insert text ---"); + t2.on(); + CHK((g_con = g_ndb->startTransaction()) != 0); + for (Uint32 k = 0; k < g_opt.m_rowsperf; k++) { + CHK((g_opr = g_con->getNdbOperation(tab.getName())) != 0); + CHK(g_opr->insertTuple() == 0); + CHK(g_opr->equal(cA, (char*)&k) == 0); + CHK((g_bh1 = g_opr->getBlobHandle(cC)) != 0); + CHK((g_bh1->setValue("c", 1) == 0)); + CHK(g_con->execute(NoCommit) == 0); + } + t2.off(g_opt.m_rowsperf); + CHK(g_con->execute(Rollback) == 0); + DBG(t2.time()); + g_bh1 = 0; + g_opr = 0; + g_con = 0; + } + // insert overhead + DBG("insert overhead: " << t2.over(t1)); + t1.clr(); + t2.clr(); + // insert + { + DBG("--- insert for read test ---"); + unsigned n = 0; + CHK((g_con = g_ndb->startTransaction()) != 0); + for (Uint32 k = 0; k < g_opt.m_rowsperf; k++) { + CHK((g_opr = g_con->getNdbOperation(tab.getName())) != 0); + CHK(g_opr->insertTuple() == 0); + CHK(g_opr->equal(cA, (char*)&k) == 0); + CHK(g_opr->setValue(cB, "b") == 0); + CHK((g_bh1 = g_opr->getBlobHandle(cC)) != 0); + CHK((g_bh1->setValue("c", 1) == 0)); + if (++n == g_opt.m_batch) { + CHK(g_con->execute(Commit) == 0); + g_ndb->closeTransaction(g_con); + CHK((g_con = g_ndb->startTransaction()) != 0); + n = 0; + } + } + if (n != 0) { + CHK(g_con->execute(Commit) == 0); + n = 0; + } + g_bh1 = 0; + g_opr = 0; + g_con = 0; + } + // pk read char (one trans) + { + DBG("--- pk read char ---"); + CHK((g_con = g_ndb->startTransaction()) != 0); + Uint32 a; + char b[20]; + t1.on(); + for (Uint32 k = 0; k < g_opt.m_rowsperf; k++) { + CHK((g_opr = g_con->getNdbOperation(tab.getName())) != 0); + CHK(g_opr->readTuple() == 0); + CHK(g_opr->equal(cA, (char*)&k) == 0); + CHK(g_opr->getValue(cA, (char*)&a) != 0); + CHK(g_opr->getValue(cB, b) != 0); + a = (Uint32)-1; + b[0] = 0; + CHK(g_con->execute(NoCommit) == 0); + CHK(a == k && strcmp(b, "b") == 0); + } + CHK(g_con->execute(Commit) == 0); + t1.off(g_opt.m_rowsperf); + DBG(t1.time()); + g_opr = 0; + g_con = 0; + } + // pk read text (one trans) + { + DBG("--- pk read text ---"); + CHK((g_con = g_ndb->startTransaction()) != 0); + Uint32 a; + char c[20]; + t2.on(); + for (Uint32 k = 0; k < g_opt.m_rowsperf; k++) { + CHK((g_opr = g_con->getNdbOperation(tab.getName())) != 0); + CHK(g_opr->readTuple() == 0); + CHK(g_opr->equal(cA, (char*)&k) == 0); + CHK(g_opr->getValue(cA, (char*)&a) != 0); + CHK((g_bh1 = g_opr->getBlobHandle(cC)) != 0); + a = (Uint32)-1; + c[0] = 0; + CHK(g_con->execute(NoCommit) == 0); + Uint32 m = 20; + CHK(g_bh1->readData(c, m) == 0); + CHK(a == k && m == 1 && strcmp(c, "c") == 0); + } + CHK(g_con->execute(Commit) == 0); + t2.off(g_opt.m_rowsperf); + DBG(t2.time()); + g_opr = 0; + g_con = 0; + } + // pk read overhead + DBG("pk read overhead: " << t2.over(t1)); + t1.clr(); + t2.clr(); + // scan read char + { + DBG("--- scan read char ---"); + NdbResultSet* rs; + Uint32 a; + char b[20]; + CHK((g_con = g_ndb->startTransaction()) != 0); + CHK((g_ops = g_con->getNdbScanOperation(tab.getName())) != 0); + CHK((rs = g_ops->readTuples(NdbScanOperation::LM_Read)) != 0); + CHK(g_ops->getValue(cA, (char*)&a) != 0); + CHK(g_ops->getValue(cB, b) != 0); + CHK(g_con->execute(NoCommit) == 0); + unsigned n = 0; + t1.on(); + while (1) { + a = (Uint32)-1; + b[0] = 0; + int ret; + CHK((ret = rs->nextResult(true)) == 0 || ret == 1); + if (ret == 1) + break; + CHK(a < g_opt.m_rowsperf && strcmp(b, "b") == 0); + n++; + } + CHK(n == g_opt.m_rowsperf); + t1.off(g_opt.m_rowsperf); + DBG(t1.time()); + g_ops = 0; + g_con = 0; + } + // scan read text + { + DBG("--- read text ---"); + NdbResultSet* rs; + Uint32 a; + char c[20]; + CHK((g_con = g_ndb->startTransaction()) != 0); + CHK((g_ops = g_con->getNdbScanOperation(tab.getName())) != 0); + CHK((rs = g_ops->readTuples(NdbScanOperation::LM_Read)) != 0); + CHK(g_ops->getValue(cA, (char*)&a) != 0); + CHK((g_bh1 = g_ops->getBlobHandle(cC)) != 0); + CHK(g_con->execute(NoCommit) == 0); + unsigned n = 0; + t2.on(); + while (1) { + a = (Uint32)-1; + c[0] = 0; + int ret; + CHK((ret = rs->nextResult(true)) == 0 || ret == 1); + if (ret == 1) + break; + Uint32 m = 20; + CHK(g_bh1->readData(c, m) == 0); + CHK(a < g_opt.m_rowsperf && m == 1 && strcmp(c, "c") == 0); + n++; + } + CHK(n == g_opt.m_rowsperf); + t2.off(g_opt.m_rowsperf); + DBG(t2.time()); + g_bh1 = 0; + g_ops = 0; + g_con = 0; + } + // scan read overhead + DBG("scan read overhead: " << t2.over(t1)); + t1.clr(); + t2.clr(); + delete g_ndb; + return 0; +} + // bug tests static int @@ -1498,6 +1793,12 @@ NDB_COMMAND(testOdbcDriver, "testBlobs", "testBlobs", "testBlobs", 65535) continue; } } + if (strcmp(arg, "-rowsperf") == 0) { + if (++argv, --argc > 0) { + g_opt.m_rowsperf = atoi(argv[0]); + continue; + } + } if (strcmp(arg, "-seed") == 0) { if (++argv, --argc > 0) { g_opt.m_seed = atoi(argv[0]); @@ -1558,7 +1859,7 @@ NDB_COMMAND(testOdbcDriver, "testBlobs", "testBlobs", "testBlobs", 65535) strcat(b, "r"); g_opt.m_skip = strdup(b); } - if (testmain() == -1) { + if (testmain() == -1 || testperf() == -1) { ndbout << "line " << __LINE__ << " FAIL loop=" << g_loop << endl; return NDBT_ProgramExit(NDBT_FAILED); } diff --git a/ndb/test/ndbapi/testDict.cpp b/ndb/test/ndbapi/testDict.cpp index 712ab2e4d25..0a43bb02fff 100644 --- a/ndb/test/ndbapi/testDict.cpp +++ b/ndb/test/ndbapi/testDict.cpp @@ -1480,8 +1480,10 @@ runTestDictionaryPerf(NDBT_Context* ctx, NDBT_Step* step){ } int runFailAddFragment(NDBT_Context* ctx, NDBT_Step* step){ + static int acclst[] = { 3001 }; static int tuplst[] = { 4007, 4008, 4009, 4010, 4011, 4012 }; static int tuxlst[] = { 12001, 12002, 12003, 12004, 12005, 12006 }; + static unsigned acccnt = sizeof(acclst)/sizeof(acclst[0]); static unsigned tupcnt = sizeof(tuplst)/sizeof(tuplst[0]); static unsigned tuxcnt = sizeof(tuxlst)/sizeof(tuxlst[0]); @@ -1509,6 +1511,19 @@ int runFailAddFragment(NDBT_Context* ctx, NDBT_Step* step){ (void)pDic->dropTable(tab.getName()); for (int l = 0; l < loops; l++) { + for (unsigned i0 = 0; i0 < acccnt; i0++) { + unsigned j = (l == 0 ? i0 : myRandom48(acccnt)); + int errval = acclst[j]; + g_info << "insert error node=" << nodeId << " value=" << errval << endl; + CHECK2(restarter.insertErrorInNode(nodeId, errval) == 0, + "failed to set error insert"); + CHECK2(pDic->createTable(tab) != 0, + "failed to fail after error insert " << errval); + CHECK2(pDic->createTable(tab) == 0, + pDic->getNdbError()); + CHECK2(pDic->dropTable(tab.getName()) == 0, + pDic->getNdbError()); + } for (unsigned i1 = 0; i1 < tupcnt; i1++) { unsigned j = (l == 0 ? i1 : myRandom48(tupcnt)); int errval = tuplst[j]; @@ -1638,7 +1653,7 @@ TESTCASE("DictionaryPerf", INITIALIZER(runTestDictionaryPerf); } TESTCASE("FailAddFragment", - "Fail add fragment or attribute in TUP or TUX\n"){ + "Fail add fragment or attribute in ACC or TUP or TUX\n"){ INITIALIZER(runFailAddFragment); } NDBT_TESTSUITE_END(testDict); @@ -1650,5 +1665,3 @@ int main(int argc, const char** argv){ myRandom48Init(NdbTick_CurrentMillisecond()); return testDict.execute(argc, argv); } - - diff --git a/sql/ha_myisammrg.cc b/sql/ha_myisammrg.cc index 2574892b1fe..744128faf69 100644 --- a/sql/ha_myisammrg.cc +++ b/sql/ha_myisammrg.cc @@ -35,6 +35,17 @@ const char **ha_myisammrg::bas_ext() const { static const char *ext[]= { ".MRG", NullS }; return ext; } +const char *ha_myisammrg::index_type(uint key_number) +{ + return ((table->key_info[key_number].flags & HA_FULLTEXT) ? + "FULLTEXT" : + (table->key_info[key_number].flags & HA_SPATIAL) ? + "SPATIAL" : + (table->key_info[key_number].algorithm == HA_KEY_ALG_RTREE) ? + "RTREE" : + "BTREE"); +} + int ha_myisammrg::open(const char *name, int mode, uint test_if_locked) { diff --git a/sql/ha_myisammrg.h b/sql/ha_myisammrg.h index 264c580220c..6058c32c805 100644 --- a/sql/ha_myisammrg.h +++ b/sql/ha_myisammrg.h @@ -32,6 +32,7 @@ class ha_myisammrg: public handler ~ha_myisammrg() {} const char *table_type() const { return "MRG_MyISAM"; } const char **bas_ext() const; + const char *index_type(uint key_number); ulong table_flags() const { return (HA_REC_NOT_IN_SEQ | HA_AUTO_PART_KEY | HA_READ_RND_SAME | diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index a9537db39c7..0dc1399db24 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -796,7 +796,8 @@ int ha_ndbcluster::build_index_list(TABLE *tab, enum ILBP phase) error= create_unique_index(unique_index_name, key_info); break; case UNIQUE_INDEX: - error= create_unique_index(unique_index_name, key_info); + if (!(error= check_index_fields_not_null(i))) + error= create_unique_index(unique_index_name, key_info); break; case ORDERED_INDEX: error= create_ordered_index(index_name, key_info); @@ -848,6 +849,26 @@ NDB_INDEX_TYPE ha_ndbcluster::get_index_type_from_table(uint inx) const ORDERED_INDEX); } +int ha_ndbcluster::check_index_fields_not_null(uint inx) +{ + KEY* key_info= table->key_info + inx; + KEY_PART_INFO* key_part= key_info->key_part; + KEY_PART_INFO* end= key_part+key_info->key_parts; + DBUG_ENTER("check_index_fields_not_null"); + + for (; key_part != end; key_part++) + { + Field* field= key_part->field; + if (field->maybe_null()) + { + my_printf_error(ER_NULL_COLUMN_IN_INDEX,ER(ER_NULL_COLUMN_IN_INDEX), + MYF(0),field->field_name); + DBUG_RETURN(ER_NULL_COLUMN_IN_INDEX); + } + } + + DBUG_RETURN(0); +} void ha_ndbcluster::release_metadata() { diff --git a/sql/ha_ndbcluster.h b/sql/ha_ndbcluster.h index 02d7b96db20..354a98e698c 100644 --- a/sql/ha_ndbcluster.h +++ b/sql/ha_ndbcluster.h @@ -160,7 +160,8 @@ class ha_ndbcluster: public handler void release_metadata(); NDB_INDEX_TYPE get_index_type(uint idx_no) const; NDB_INDEX_TYPE get_index_type_from_table(uint index_no) const; - + int check_index_fields_not_null(uint index_no); + int pk_read(const byte *key, uint key_len, byte *buf); int complemented_pk_read(const byte *old_data, byte *new_data); int peek_row(); diff --git a/sql/handler.cc b/sql/handler.cc index 3c5244927d4..b474e6290f2 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -970,8 +970,10 @@ int handler::read_first_row(byte * buf, uint primary_key) /* If there is very few deleted rows in the table, find the first row by scanning the table. + TODO remove the test for HA_READ_ORDER */ - if (deleted < 10 || primary_key >= MAX_KEY) + if (deleted < 10 || primary_key >= MAX_KEY || + !(index_flags(primary_key, 0, 0) & HA_READ_ORDER)) { (void) ha_rnd_init(1); while ((error= rnd_next(buf)) == HA_ERR_RECORD_DELETED) ; diff --git a/sql/item.cc b/sql/item.cc index 944d377282d..6ebe34f91dc 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -3152,16 +3152,12 @@ void Item_insert_value::print(String *str) NOTE This function does almost the same as fix_fields() for Item_field but is invoked during trigger definition parsing and takes TABLE - object as its argument. - - RETURN VALUES - 0 ok - 1 field was not found. + object as its argument. If proper field was not found in table + error will be reported at fix_fields() time. */ -bool Item_trigger_field::setup_field(THD *thd, TABLE *table, +void Item_trigger_field::setup_field(THD *thd, TABLE *table, enum trg_event_type event) { - bool result= 1; uint field_idx= (uint)-1; bool save_set_query_id= thd->set_query_id; @@ -3175,12 +3171,9 @@ bool Item_trigger_field::setup_field(THD *thd, TABLE *table, field= (row_version == OLD_ROW && event == TRG_EVENT_UPDATE) ? table->triggers->old_field[field_idx] : table->field[field_idx]; - result= 0; } thd->set_query_id= save_set_query_id; - - return result; } @@ -3204,10 +3197,18 @@ bool Item_trigger_field::fix_fields(THD *thd, FIXME may be we still should bother about permissions here. */ DBUG_ASSERT(fixed == 0); - // QQ: May be this should be moved to setup_field? - set_field(field); - fixed= 1; - return 0; + + if (field) + { + // QQ: May be this should be moved to setup_field? + set_field(field); + fixed= 1; + return 0; + } + + my_error(ER_BAD_FIELD_ERROR, MYF(0), field_name, + (row_version == NEW_ROW) ? "NEW" : "OLD"); + return 1; } diff --git a/sql/item.h b/sql/item.h index 9c036c28408..933233e38c3 100644 --- a/sql/item.h +++ b/sql/item.h @@ -266,6 +266,14 @@ public: virtual bool get_time(TIME *ltime); virtual bool get_date_result(TIME *ltime,uint fuzzydate) { return get_date(ltime,fuzzydate); } + /* + This function is used only in Item_func_isnull/Item_func_isnotnull + (implementations of IS NULL/IS NOT NULL clauses). Item_func_is{not}null + calls this method instead of one of val/result*() methods, which + normally will set null_value. This allows to determine nullness of + a complex expression without fully evaluating it. + Any new item which can be NULL must implement this call. + */ virtual bool is_null() { return 0; } /* it is "top level" item of WHERE clause and we do not need correct NULL @@ -710,6 +718,8 @@ public: void print(String *str); /* parameter never equal to other parameter of other item */ bool eq(const Item *item, bool binary_cmp) const { return 0; } + bool is_null() + { DBUG_ASSERT(state != NO_VALUE); return state == NULL_VALUE; } }; class Item_int :public Item_num @@ -1309,13 +1319,15 @@ public: /* Is this item represents row from NEW or OLD row ? */ enum row_version_type {OLD_ROW, NEW_ROW}; row_version_type row_version; + /* Next in list of all Item_trigger_field's in trigger */ + Item_trigger_field *next_trg_field; Item_trigger_field(row_version_type row_ver_par, const char *field_name_par): Item_field((const char *)NULL, (const char *)NULL, field_name_par), row_version(row_ver_par) {} - bool setup_field(THD *thd, TABLE *table, enum trg_event_type event); + void setup_field(THD *thd, TABLE *table, enum trg_event_type event); enum Type type() const { return TRIGGER_FIELD_ITEM; } bool eq(const Item *item, bool binary_cmp) const; bool fix_fields(THD *, struct st_table_list *, Item **); diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 4a4485ba2da..23bd1b503f7 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -2429,11 +2429,12 @@ Item_func_regex::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) return FALSE; } int error; - if ((error=regcomp(&preg,res->c_ptr(), - (cmp_collation.collation->state & MY_CS_BINSORT) ? - REG_EXTENDED | REG_NOSUB : - REG_EXTENDED | REG_NOSUB | REG_ICASE, - cmp_collation.collation))) + if ((error= regcomp(&preg,res->c_ptr(), + ((cmp_collation.collation->state & MY_CS_BINSORT) || + (cmp_collation.collation->state & MY_CS_CSSORT)) ? + REG_EXTENDED | REG_NOSUB : + REG_EXTENDED | REG_NOSUB | REG_ICASE, + cmp_collation.collation))) { (void) regerror(error,&preg,buff,sizeof(buff)); my_error(ER_REGEXP_ERROR, MYF(0), buff); @@ -2481,10 +2482,11 @@ longlong Item_func_regex::val_int() regex_compiled=0; } if (regcomp(&preg,res2->c_ptr(), - (cmp_collation.collation->state & MY_CS_BINSORT) ? - REG_EXTENDED | REG_NOSUB : - REG_EXTENDED | REG_NOSUB | REG_ICASE, - cmp_collation.collation)) + ((cmp_collation.collation->state & MY_CS_BINSORT) || + (cmp_collation.collation->state & MY_CS_CSSORT)) ? + REG_EXTENDED | REG_NOSUB : + REG_EXTENDED | REG_NOSUB | REG_ICASE, + cmp_collation.collation)) { null_value=1; return 0; diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index eced813f75d..f948bf13226 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -378,6 +378,15 @@ typedef struct st_sql_list { first= save->first; elements+= save->elements; } + inline void push_back(struct st_sql_list *save) + { + if (save->first) + { + *next= save->first; + next= save->next; + elements+= save->elements; + } + } } SQL_LIST; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index cd158e48b11..3001cda8b2b 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -469,6 +469,7 @@ Query_cache query_cache; #ifdef HAVE_SMEM char *shared_memory_base_name= default_shared_memory_base_name; bool opt_enable_shared_memory; +HANDLE smem_event_connect_request= 0; #endif #include "sslopt-vars.h" @@ -746,6 +747,15 @@ void kill_mysql(void) CloseHandle(hEvent); */ } +#ifdef HAVE_SMEM + /* + Send event to smem_event_connect_request for aborting + */ + if (!SetEvent(smem_event_connect_request)) + { + DBUG_PRINT("error",("Got error: %ld from SetEvent of smem_event_connect_request",GetLastError())); + } +#endif #endif #elif defined(OS2) pthread_cond_signal(&eventShutdown); // post semaphore @@ -3799,7 +3809,6 @@ pthread_handler_decl(handle_connections_shared_memory,arg) /* file-mapping object, use for create shared memory */ HANDLE handle_connect_file_map= 0; char *handle_connect_map= 0; // pointer on shared memory - HANDLE event_connect_request= 0; // for start connection actions HANDLE event_connect_answer= 0; ulong smem_buffer_length= shared_memory_buffer_length + 4; ulong connect_number= 1; @@ -3820,7 +3829,7 @@ pthread_handler_decl(handle_connections_shared_memory,arg) */ suffix_pos= strxmov(tmp,shared_memory_base_name,"_",NullS); strmov(suffix_pos, "CONNECT_REQUEST"); - if ((event_connect_request= CreateEvent(0,FALSE,FALSE,tmp)) == 0) + if ((smem_event_connect_request= CreateEvent(0,FALSE,FALSE,tmp)) == 0) { errmsg= "Could not create request event"; goto error; @@ -3851,7 +3860,13 @@ pthread_handler_decl(handle_connections_shared_memory,arg) while (!abort_loop) { /* Wait a request from client */ - WaitForSingleObject(event_connect_request,INFINITE); + WaitForSingleObject(smem_event_connect_request,INFINITE); + + /* + it can be after shutdown command + */ + if (abort_loop) + goto error; HANDLE handle_client_file_map= 0; char *handle_client_map= 0; @@ -3976,7 +3991,7 @@ error: if (handle_connect_map) UnmapViewOfFile(handle_connect_map); if (handle_connect_file_map) CloseHandle(handle_connect_file_map); if (event_connect_answer) CloseHandle(event_connect_answer); - if (event_connect_request) CloseHandle(event_connect_request); + if (smem_event_connect_request) CloseHandle(smem_event_connect_request); decrement_handler_count(); DBUG_RETURN(0); diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 3368482f28d..6392a2fee32 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -2869,10 +2869,10 @@ TRP_ROR_INTERSECT *get_best_ror_intersect(const PARAM *param, SEL_TREE *tree, trp->records= best_rows? best_rows : 1; trp->index_scan_costs= best_index_scan_costs; trp->cpk_scan= cpk_scan; + DBUG_PRINT("info", + ("Returning non-covering ROR-intersect plan: cost %g, records %lu", + trp->read_cost, (ulong) trp->records)); } - DBUG_PRINT("info", - ("Returning non-covering ROR-intersect plan: cost %g, records %lu", - trp->read_cost, (ulong) trp->records)); DBUG_RETURN(trp); } diff --git a/sql/set_var.cc b/sql/set_var.cc index 79d886fb84f..46865de9314 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -2781,13 +2781,18 @@ int sql_set_variables(THD *thd, List *var_list) while ((var=it++)) { if ((error=var->check(thd))) - DBUG_RETURN(error); + goto err; } - if (thd->net.report_error) - DBUG_RETURN(1); - it.rewind(); - while ((var=it++)) - error|= var->update(thd); // Returns 0, -1 or 1 + if (!thd->net.report_error) + { + it.rewind(); + while ((var= it++)) + error|= var->update(thd); // Returns 0, -1 or 1 + } + else + error= 1; +err: + free_underlaid_joins(thd, &thd->lex->select_lex); DBUG_RETURN(error); } diff --git a/sql/sp.cc b/sql/sp.cc index 5798eedbad9..a39380d7276 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -1153,7 +1153,6 @@ sp_change_db(THD *thd, char *name, bool no_access_check) int length, db_length; char *dbname=my_strdup((char*) name,MYF(MY_WME)); char path[FN_REFLEN]; - ulong db_access; HA_CREATE_INFO create; DBUG_ENTER("sp_change_db"); DBUG_PRINT("enter", ("db: %s, no_access_check: %d", name, no_access_check)); @@ -1174,6 +1173,8 @@ sp_change_db(THD *thd, char *name, bool no_access_check) #ifndef NO_EMBEDDED_ACCESS_CHECKS if (! no_access_check) { + ulong db_access; + if (test_all_bits(thd->master_access,DB_ACLS)) db_access=DB_ACLS; else diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 7db79128bb8..114ff0d451a 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -275,6 +275,11 @@ sp_head::init(LEX *lex) DBUG_ENTER("sp_head::init"); lex->spcont= m_pcont= new sp_pcontext(NULL); + /* + Altough trg_table_fields list is used only in triggers we init for all + types of stored procedures to simplify reset_lex()/restore_lex() code. + */ + lex->trg_table_fields.empty(); my_init_dynamic_array(&m_instr, sizeof(sp_instr *), 16, 8); m_param_begin= m_param_end= m_returns_begin= m_returns_end= m_body_begin= 0; m_qname.str= m_db.str= m_name.str= m_params.str= m_retstr.str= @@ -771,7 +776,7 @@ sp_head::reset_lex(THD *thd) sublex->spcont= oldlex->spcont; /* And trigger related stuff too */ sublex->trg_chistics= oldlex->trg_chistics; - sublex->trg_table= oldlex->trg_table; + sublex->trg_table_fields.empty(); sublex->sp_lex_in_use= FALSE; DBUG_VOID_RETURN; } @@ -790,6 +795,7 @@ sp_head::restore_lex(THD *thd) // Update some state in the old one first oldlex->ptr= sublex->ptr; oldlex->next_state= sublex->next_state; + oldlex->trg_table_fields.push_back(&sublex->trg_table_fields); // Collect some data from the sub statement lex. sp_merge_funs(oldlex, sublex); diff --git a/sql/sp_head.h b/sql/sp_head.h index 4bfe1076f65..c4d2068661c 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -439,13 +439,9 @@ public: virtual void print(String *str); - bool setup_field(THD *thd, TABLE *table, enum trg_event_type event) - { - return trigger_field.setup_field(thd, table, event); - } -private: - Item_trigger_field trigger_field; + +private: Item *value; }; // class sp_instr_trigger_field : public sp_instr diff --git a/sql/sql_do.cc b/sql/sql_do.cc index 3ca3bea743a..3f34835c2c9 100644 --- a/sql/sql_do.cc +++ b/sql/sql_do.cc @@ -29,6 +29,7 @@ bool mysql_do(THD *thd, List &values) DBUG_RETURN(TRUE); while ((value = li++)) value->val_int(); + free_underlaid_joins(thd, &thd->lex->select_lex); thd->clear_error(); // DO always is OK send_ok(thd); DBUG_RETURN(FALSE); diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 1cbe004caa0..971ba125859 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -175,7 +175,6 @@ void lex_start(THD *thd, uchar *buf,uint length) lex->duplicates= DUP_ERROR; lex->sphead= NULL; lex->spcont= NULL; - lex->trg_table= NULL; lex->proc_list.first= 0; if (lex->spfuns.records) diff --git a/sql/sql_lex.h b/sql/sql_lex.h index ce22caa13fc..3ae6f91bdd6 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -750,11 +750,13 @@ typedef struct st_lex /* Characterstics of trigger being created */ st_trg_chistics trg_chistics; /* - Points to table being opened when we are parsing trigger definition - while opening table. 0 if we are parsing user provided CREATE TRIGGER - or any other statement. Used for NEW/OLD row field lookup in trigger. + List of all items (Item_trigger_field objects) representing fields in + old/new version of row in trigger. We use this list for checking whenever + all such fields are valid at trigger creation time and for binding these + fields to TABLE object at table open (altough for latter pointer to table + being opened is probably enough). */ - TABLE *trg_table; + SQL_LIST trg_table_fields; st_lex() :result(0) { diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 7781ceac9e2..89807765421 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -3339,16 +3339,15 @@ create_error: } case SQLCOM_CREATE_FUNCTION: // UDF function { - sp_head *sph; if (check_access(thd,INSERT_ACL,"mysql",0,1,0)) break; #ifdef HAVE_DLOPEN - if ((sph= sp_find_function(thd, lex->spname))) + if (sp_find_function(thd, lex->spname)) { my_error(ER_UDF_EXISTS, MYF(0), lex->spname->m_name.str); goto error; } - if (!(res = mysql_create_function(thd,&lex->udf))) + if (!(res = mysql_create_function(thd, &lex->udf))) send_ok(thd); #else res= TRUE; @@ -3800,35 +3799,35 @@ create_error: else sp= sp_find_function(thd, lex->spname); mysql_reset_errors(thd); - if (! sp) - result= SP_KEY_NOT_FOUND; - else + if (sp) { if (check_sp_definer_access(thd, sp)) goto error; if (lex->sql_command == SQLCOM_DROP_PROCEDURE) result= sp_drop_procedure(thd, lex->spname); else - { result= sp_drop_function(thd, lex->spname); + } + else + { #ifdef HAVE_DLOPEN - if (result == SP_KEY_NOT_FOUND) - { - udf_func *udf = find_udf(lex->spname->m_name.str, - lex->spname->m_name.length); - if (udf) + if (lex->sql_command == SQLCOM_DROP_FUNCTION) + { + udf_func *udf = find_udf(lex->spname->m_name.str, + lex->spname->m_name.length); + if (udf) + { + if (check_access(thd, DELETE_ACL, "mysql", 0, 1, 0)) + goto error; + if (!(res = mysql_drop_function(thd, &lex->spname->m_name))) { - if (check_access(thd, DELETE_ACL, "mysql", 0, 1, 0)) - goto error; - if (!(res = mysql_drop_function(thd,&lex->spname->m_name))) - { - send_ok(thd); - break; - } + send_ok(thd); + break; } } -#endif } +#endif + result= SP_KEY_NOT_FOUND; } res= result; switch (result) @@ -3913,11 +3912,11 @@ create_error: } case SQLCOM_CREATE_TRIGGER: { - /* We don't care much about trigger body at that point */ + res= mysql_create_or_drop_trigger(thd, all_tables, 1); + + /* We don't care about trigger body after this point */ delete lex->sphead; lex->sphead= 0; - - res= mysql_create_or_drop_trigger(thd, all_tables, 1); break; } case SQLCOM_DROP_TRIGGER: diff --git a/sql/sql_select.cc b/sql/sql_select.cc index c63ddca14ce..4c652ee972a 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -2626,11 +2626,7 @@ add_key_field(KEY_FIELD **key_fields, uint and_level, COND *cond, bool is_const=1; for (uint i=0; iconst_item(); - */ - is_const&= (*value)->const_item(); + is_const&= value[i]->const_item(); if (is_const) stat[0].const_keys.merge(possible_keys); /* @@ -12889,8 +12885,9 @@ void st_table_list::print(THD *thd, String *str) str->append('.'); if (schema_table) { - append_identifier(thd, str, alias, strlen(alias)); - cmp_name= alias; + append_identifier(thd, str, schema_table_name, + strlen(schema_table_name)); + cmp_name= schema_table_name; } else { diff --git a/sql/sql_select.h b/sql/sql_select.h index 7fd9bf48b0b..5e42fc0ee30 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -277,8 +277,6 @@ class JOIN :public Sql_alloc hidden_group_fields= 0; /*safety*/ buffer_result= test(select_options & OPTION_BUFFER_RESULT) && !test(select_options & OPTION_FOUND_ROWS); - all_fields= fields_arg; - fields_list= fields_arg; error= 0; select= 0; return_tab= 0; @@ -288,6 +286,7 @@ class JOIN :public Sql_alloc optimized= 0; cond_equal= 0; + all_fields= fields_arg; fields_list= fields_arg; bzero((char*) &keyuse,sizeof(keyuse)); tmp_table_param.copy_field=0; diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 1642a2eaa17..f41eab1ba14 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -597,7 +597,7 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list) else { if (table_list->schema_table) - protocol->store(table_list->alias, system_charset_info); + protocol->store(table_list->schema_table_name, system_charset_info); else protocol->store(table->table_name, system_charset_info); if (store_create_info(thd, table_list, &buffer)) @@ -938,7 +938,7 @@ store_create_info(THD *thd, TABLE_LIST *table_list, String *packet) else packet->append("CREATE TABLE ", 13); if (table_list->schema_table) - alias= table_list->alias; + alias= table_list->schema_table_name; else alias= (lower_case_table_names == 2 ? table->table_name : table->real_name); @@ -2193,7 +2193,7 @@ static int get_schema_tables_record(THD *thd, struct st_table_list *tables, tmp_buff= (show_table->table_charset ? show_table-> table_charset->name : "default"); table->field[17]->store(tmp_buff, strlen(tmp_buff), cs); - if (file->table_flags() & HA_HAS_CHECKSUM) + if (file->table_flags() & (ulong) HA_HAS_CHECKSUM) { table->field[18]->store((longlong) file->checksum()); table->field[18]->set_notnull(); @@ -2282,7 +2282,7 @@ static int get_schema_column_record(THD *thd, struct st_table_list *tables, !wild_case_compare(system_charset_info, field->field_name,wild)) { uint tmp_length; - char *tmp_buff; + const char *tmp_buff; byte *pos; uint flags=field->flags; char tmp[MAX_FIELD_WIDTH]; @@ -2298,7 +2298,7 @@ static int get_schema_column_record(THD *thd, struct st_table_list *tables, table->field[4]->store((longlong) count); field->sql_type(type); table->field[14]->store(type.ptr(), type.length(), cs); - tmp_buff= strchr(type.ptr(),'('); + tmp_buff= strchr(type.ptr(), '('); table->field[7]->store(type.ptr(), (tmp_buff ? tmp_buff - type.ptr() : type.length()), cs); @@ -3137,6 +3137,7 @@ int mysql_schema_table(THD *thd, LEX *lex, TABLE_LIST *table_list) } table->tmp_table= TMP_TABLE; table->grant.privilege= SELECT_ACL; + table_list->schema_table_name= table_list->real_name; table_list->real_name= table->real_name; table_list->table= table; table->next= thd->derived_tables; @@ -3291,14 +3292,14 @@ ST_FIELD_INFO columns_fields_info[]= {"ORDINAL_POSITION", 21 , MYSQL_TYPE_LONG, 0, 0, 0}, {"COLUMN_DEFAULT", NAME_LEN, MYSQL_TYPE_STRING, 0, 1, "Default"}, {"IS_NULLABLE", 3, MYSQL_TYPE_STRING, 0, 0, "Null"}, - {"DATA_TYPE", 40, MYSQL_TYPE_STRING, 0, 0, 0}, + {"DATA_TYPE", 65535, MYSQL_TYPE_STRING, 0, 0, 0}, {"CHARACTER_MAXIMUM_LENGTH", 21 , MYSQL_TYPE_LONG, 0, 0, 0}, {"CHARACTER_OCTET_LENGTH", 21 , MYSQL_TYPE_LONG, 0, 0, 0}, {"NUMERIC_PRECISION", 21 , MYSQL_TYPE_LONG, 0, 1, 0}, {"NUMERIC_SCALE", 21 , MYSQL_TYPE_LONG, 0, 1, 0}, {"CHARACTER_SET_NAME", 40, MYSQL_TYPE_STRING, 0, 1, 0}, {"COLLATION_NAME", 40, MYSQL_TYPE_STRING, 0, 1, "Collation"}, - {"COLUMN_TYPE", 40, MYSQL_TYPE_STRING, 0, 0, "Type"}, + {"COLUMN_TYPE", 65535, MYSQL_TYPE_STRING, 0, 0, "Type"}, {"COLUMN_KEY", 3, MYSQL_TYPE_STRING, 0, 0, "Key"}, {"EXTRA", 20, MYSQL_TYPE_STRING, 0, 0, "Extra"}, {"PRIVILEGES", 80, MYSQL_TYPE_STRING, 0, 0, "Privileges"}, @@ -3356,7 +3357,7 @@ ST_FIELD_INFO proc_fields_info[]= {"SECURITY_TYPE", 7, MYSQL_TYPE_STRING, 0, 0, "Security_type"}, {"CREATED", 0, MYSQL_TYPE_TIMESTAMP, 0, 0, "Created"}, {"LAST_ALTERED", 0, MYSQL_TYPE_TIMESTAMP, 0, 0, "Modified"}, - {"SQL_MODE", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0}, + {"SQL_MODE", 65535, MYSQL_TYPE_STRING, 0, 0, 0}, {"ROUTINE_COMMENT", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Comment"}, {"DEFINER", 77, MYSQL_TYPE_STRING, 0, 0, "Definer"}, {0, 0, MYSQL_TYPE_STRING, 0, 0, 0} diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 5020b4820a0..a7c08f356a2 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -835,7 +835,7 @@ int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info, #endif } - List_iterator cols(key->columns); + List_iterator cols(key->columns), cols2(key->columns); CHARSET_INFO *ft_key_charset=0; // for FULLTEXT for (uint column_nr=0 ; (column=cols++) ; column_nr++) { @@ -851,6 +851,19 @@ int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info, my_error(ER_KEY_COLUMN_DOES_NOT_EXITS, MYF(0), column->field_name); DBUG_RETURN(-1); } + for (uint dup_nr= 0; dup_nr < column_nr; dup_nr++) + { + key_part_spec *dup_column= cols2++; + if (!my_strcasecmp(system_charset_info, + column->field_name, dup_column->field_name)) + { + my_printf_error(ER_DUP_FIELDNAME, + ER(ER_DUP_FIELDNAME),MYF(0), + column->field_name); + DBUG_RETURN(-1); + } + } + cols2.rewind(); /* for fulltext keys keyseg length is 1 for blobs (it's ignored in ft code anyway, and 0 (set to column width later) for char's. it has to be correct col width for char's, as char data are not diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index 7637679430f..a88dc0b20bf 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -136,6 +136,7 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables) char dir_buff[FN_REFLEN], file_buff[FN_REFLEN]; LEX_STRING dir, file; LEX_STRING *trg_def, *name; + Item_trigger_field *trg_field; List_iterator_fast it(names_list); /* We don't allow creation of several triggers of the same type yet */ @@ -156,6 +157,31 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables) } } + /* + Let us check if all references to fields in old/new versions of row in + this trigger are ok. + + NOTE: We do it here more from ease of use standpoint. We still have to + do some checks on each execution. E.g. we can catch privilege changes + only during execution. Also in near future, when we will allow access + to other tables from trigger we won't be able to catch changes in other + tables... + + To simplify code a bit we have to create Fields for accessing to old row + values if we have ON UPDATE trigger. + */ + if (!old_field && lex->trg_chistics.event == TRG_EVENT_UPDATE && + prepare_old_row_accessors(table)) + return 1; + + for (trg_field= (Item_trigger_field *)(lex->trg_table_fields.first); + trg_field; trg_field= trg_field->next_trg_field) + { + trg_field->setup_field(thd, table, lex->trg_chistics.event); + if (trg_field->fix_fields(thd, (TABLE_LIST *)0, (Item **)0)) + return 1; + } + /* Here we are creating file with triggers and save all triggers in it. sql_create_definition_file() files handles renaming and backup of older @@ -274,6 +300,44 @@ Table_triggers_list::~Table_triggers_list() } +/* + Prepare array of Field objects which will represent OLD.* row values in + ON UPDATE trigger (by referencing to record[1] instead of record[0]). + + SYNOPSIS + prepare_old_row_accessors() + table - pointer to TABLE object for which we are creating fields. + + RETURN VALUE + False - success + True - error +*/ +bool Table_triggers_list::prepare_old_row_accessors(TABLE *table) +{ + Field **fld, **old_fld; + + if (!(old_field= (Field **)alloc_root(&table->mem_root, + (table->fields + 1) * + sizeof(Field*)))) + return 1; + + for (fld= table->field, old_fld= old_field; *fld; fld++, old_fld++) + { + /* + QQ: it is supposed that it is ok to use this function for field + cloning... + */ + if (!(*old_fld= (*fld)->new_field(&table->mem_root, table))) + return 1; + (*old_fld)->move_field((my_ptrdiff_t)(table->record[1] - + table->record[0])); + } + *old_fld= 0; + + return 0; +} + + /* Check whenever .TRG file for table exist and load all triggers it contains. @@ -317,7 +381,6 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, if (!strncmp(triggers_file_type.str, parser->type()->str, parser->type()->length)) { - Field **fld, **old_fld; Table_triggers_list *triggers= new (&table->mem_root) Table_triggers_list(); @@ -330,31 +393,10 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, table->triggers= triggers; - /* - We have to prepare array of Field objects which will represent OLD.* - row values by referencing to record[1] instead of record[0] - - TODO: This could be avoided if there is no ON UPDATE trigger. - */ - if (!(triggers->old_field= - (Field **)alloc_root(&table->mem_root, (table->fields + 1) * - sizeof(Field*)))) + /* TODO: This could be avoided if there is no ON UPDATE trigger. */ + if (triggers->prepare_old_row_accessors(table)) DBUG_RETURN(1); - for (fld= table->field, old_fld= triggers->old_field; *fld; - fld++, old_fld++) - { - /* - QQ: it is supposed that it is ok to use this function for field - cloning... - */ - if (!(*old_fld= (*fld)->new_field(&table->mem_root, table))) - DBUG_RETURN(1); - (*old_fld)->move_field((my_ptrdiff_t)(table->record[1] - - table->record[0])); - } - *old_fld= 0; - List_iterator_fast it(triggers->definitions_list); LEX_STRING *trg_create_str, *trg_name_str; char *trg_name_buff; @@ -365,7 +407,7 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, while ((trg_create_str= it++)) { lex_start(thd, (uchar*)trg_create_str->str, trg_create_str->length); - lex.trg_table= table; + if (yyparse((void *)thd) || thd->is_fatal_error) { /* @@ -400,6 +442,21 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, if (triggers->names_list.push_back(trg_name_str, &table->mem_root)) goto err_with_lex_cleanup; + /* + Let us bind Item_trigger_field objects representing access to fields + in old/new versions of row in trigger to Field objects in table being + opened. + + We ignore errors here, because if even something is wrong we still will + be willing to open table to perform some operations (e.g. SELECT)... + Anyway some things can be checked only during trigger execution. + */ + for (Item_trigger_field *trg_field= + (Item_trigger_field *)(lex.trg_table_fields.first); + trg_field; + trg_field= trg_field->next_trg_field) + trg_field->setup_field(thd, table, lex.trg_chistics.event); + lex_end(&lex); } thd->lex= old_lex; diff --git a/sql/sql_trigger.h b/sql/sql_trigger.h index d0376f056d9..82e7c1ce023 100644 --- a/sql/sql_trigger.h +++ b/sql/sql_trigger.h @@ -65,4 +65,7 @@ public: } friend class Item_trigger_field; + +private: + bool prepare_old_row_accessors(TABLE *table); }; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 14086514ff7..6cb02fa4046 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1238,8 +1238,9 @@ create: my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), "TRIGGER"); YYABORT; } - - sp= new sp_head(); + + if (!(sp= new sp_head())) + YYABORT; sp->reset_thd_mem_root(YYTHD); sp->init(lex); @@ -6622,6 +6623,7 @@ simple_ident_q: (!my_strcasecmp(system_charset_info, $1.str, "NEW") || !my_strcasecmp(system_charset_info, $1.str, "OLD"))) { + Item_trigger_field *trg_fld; bool new_row= ($1.str[0]=='N' || $1.str[0]=='n'); if (lex->trg_chistics.event == TRG_EVENT_INSERT && @@ -6638,23 +6640,18 @@ simple_ident_q: YYABORT; } - Item_trigger_field *trg_fld= - new Item_trigger_field(new_row ? Item_trigger_field::NEW_ROW : - Item_trigger_field::OLD_ROW, - $3.str); - - if (lex->trg_table && - trg_fld->setup_field(thd, lex->trg_table, - lex->trg_chistics.event)) - { - /* - FIXME. Far from perfect solution. See comment for - "SET NEW.field_name:=..." for more info. - */ - my_error(ER_BAD_FIELD_ERROR, MYF(0), - $3.str, new_row ? "NEW": "OLD"); + if (!(trg_fld= new Item_trigger_field(new_row ? + Item_trigger_field::NEW_ROW: + Item_trigger_field::OLD_ROW, + $3.str))) YYABORT; - } + + /* + Let us add this item to list of all Item_trigger_field objects + in trigger. + */ + lex->trg_table_fields.link_in_list((byte *)trg_fld, + (byte**)&trg_fld->next_trg_field); $$= (Item *)trg_fld; } @@ -7156,28 +7153,19 @@ option_value: /* QQ: Shouldn't this be field's default value ? */ it= new Item_null(); } - i= new sp_instr_set_trigger_field(lex->sphead->instructions(), - lex->spcont, $1.base_name, it); - if (lex->trg_table && i->setup_field(YYTHD, lex->trg_table, - lex->trg_chistics.event)) - { - /* - FIXME. Now we are catching this kind of errors only - during opening tables. But this doesn't save us from most - common user error - misspelling field name, because we - will bark too late in this case... Moreover it is easy to - make table unusable with such kind of error... - - So in future we either have to parse trigger definition - second time during create trigger or gather all trigger - fields in one list and perform setup_field() for them as - separate stage. - - Error message also should be improved. - */ - my_error(ER_BAD_FIELD_ERROR, MYF(0), $1.base_name, "NEW"); + + if (!(i= new sp_instr_set_trigger_field( + lex->sphead->instructions(), lex->spcont, + $1.base_name, it))) YYABORT; - } + + /* + Let us add this item to list of all Item_trigger_field + objects in trigger. + */ + lex->trg_table_fields.link_in_list((byte *)&i->trigger_field, + (byte **)&i->trigger_field.next_trg_field); + lex->sphead->add_instr(i); } else if ($1.var) diff --git a/sql/table.h b/sql/table.h index b12e82f3c73..09c7719980b 100644 --- a/sql/table.h +++ b/sql/table.h @@ -281,7 +281,7 @@ typedef struct st_table_list struct st_table_list *next_local; /* link in a global list of all queries tables */ struct st_table_list *next_global, **prev_global; - char *db, *alias, *real_name; + char *db, *alias, *real_name, *schema_table_name; char *option; /* Used by cache index */ Item *on_expr; /* Used with outer join */ COND_EQUAL *cond_equal; /* Used with outer join */ diff --git a/strings/ctype-czech.c b/strings/ctype-czech.c index 6f9e9f74d35..2177a18504e 100644 --- a/strings/ctype-czech.c +++ b/strings/ctype-czech.c @@ -589,12 +589,12 @@ static MY_COLLATION_HANDLER my_collation_latin2_czech_ci_handler = CHARSET_INFO my_charset_latin2_czech_ci = { - 2,0,0, /* number */ - MY_CS_COMPILED|MY_CS_STRNXFRM, /* state */ - "latin2", /* cs name */ - "latin2_czech_cs", /* name */ - "", /* comment */ - NULL, /* tailoring */ + 2,0,0, /* number */ + MY_CS_COMPILED|MY_CS_STRNXFRM|MY_CS_CSSORT, /* state */ + "latin2", /* cs name */ + "latin2_czech_cs", /* name */ + "", /* comment */ + NULL, /* tailoring */ ctype_czech, to_lower_czech, to_upper_czech, diff --git a/strings/ctype-win1250ch.c b/strings/ctype-win1250ch.c index b4dbda3e8ed..4ada3d47bf5 100644 --- a/strings/ctype-win1250ch.c +++ b/strings/ctype-win1250ch.c @@ -624,12 +624,12 @@ static MY_COLLATION_HANDLER my_collation_czech_ci_handler = CHARSET_INFO my_charset_cp1250_czech_ci = { - 34,0,0, /* number */ - MY_CS_COMPILED|MY_CS_STRNXFRM, /* state */ - "cp1250", /* cs name */ - "cp1250_czech_cs", /* name */ - "", /* comment */ - NULL, /* tailoring */ + 34,0,0, /* number */ + MY_CS_COMPILED|MY_CS_STRNXFRM|MY_CS_CSSORT, /* state */ + "cp1250", /* cs name */ + "cp1250_czech_cs", /* name */ + "", /* comment */ + NULL, /* tailoring */ ctype_win1250ch, to_lower_win1250ch, to_upper_win1250ch, diff --git a/strings/uca-dump.c b/strings/uca-dump.c index 6836c321526..db5cb7e999a 100644 --- a/strings/uca-dump.c +++ b/strings/uca-dump.c @@ -23,13 +23,14 @@ struct uca_item_st #define MY_UCA_PSHIFT 8 #endif +static char *pname[]= {"", "2", "3"}; + int main(int ac, char **av) { char str[256]; char *weights[64]; struct uca_item_st uca[64*1024]; - size_t code, page, w; - int pagemaxlen[MY_UCA_NPAGES]; + size_t code, w; int pageloaded[MY_UCA_NPAGES]; bzero(uca, sizeof(uca)); @@ -155,14 +156,20 @@ int main(int ac, char **av) printf("#define MY_UCA_CMASK %d\n",MY_UCA_CMASK); printf("#define MY_UCA_PSHIFT %d\n",MY_UCA_PSHIFT); - for (w=0; w<1; w++) + for (w=0; w<3; w++) { + size_t page; + int pagemaxlen[MY_UCA_NPAGES]; + for (page=0; page < MY_UCA_NPAGES; page++) { size_t offs; size_t maxnum= 0; size_t nchars= 0; size_t mchars; + size_t ndefs= 0; + + pagemaxlen[page]= 0; /* Skip this page if no weights were loaded @@ -183,15 +190,36 @@ int main(int ac, char **av) code= page*MY_UCA_NCHARS+offs; /* Calculate only non-zero weights */ - num=0; - for (i=0; i < uca[code].num; i++) + for (num=0, i=0; i < uca[code].num; i++) if (uca[code].weight[w][i]) num++; maxnum= maxnum < num ? num : maxnum; + + /* Check if default weight */ + if (w == 1 && num == 1) + { + /* 0020 0000 ... */ + if (uca[code].weight[w][0] == 0x0020) + ndefs++; + } + else if (w == 2 && num == 1) + { + /* 0002 0000 ... */ + if (uca[code].weight[w][0] == 0x0002) + ndefs++; + } } maxnum++; + /* + If the page have only default weights + then no needs to dump it, skip. + */ + if (ndefs == MY_UCA_NCHARS) + { + continue; + } switch (maxnum) { case 0: mchars= 8; break; @@ -210,8 +238,8 @@ int main(int ac, char **av) */ - printf("uint16 page%03Xdata[]= { /* %04X (%d weights per char) */\n", - page, page*MY_UCA_NCHARS, maxnum); + printf("uint16 page%03Xdata%s[]= { /* %04X (%d weights per char) */\n", + page, pname[w], page*MY_UCA_NCHARS, maxnum); for (offs=0; offs < MY_UCA_NCHARS; offs++) { @@ -234,7 +262,17 @@ int main(int ac, char **av) for (i=0; i < maxnum; i++) { - printf("0x%04X",(int)weight[i]); + /* + Invert weights for secondary level to + sort upper case letters before their + lower case counter part. + */ + int tmp= weight[i]; + if (w == 2 && tmp) + tmp= (int)(0x100 - weight[i]); + + + printf("0x%04X", tmp); if ((offs+1 != MY_UCA_NCHARS) || (i+1!=maxnum)) printf(","); nchars++; @@ -251,25 +289,28 @@ int main(int ac, char **av) } printf("};\n\n"); } + + printf("uchar uca_length%s[%d]={\n", pname[w], MY_UCA_NPAGES); + for (page=0; page < MY_UCA_NPAGES; page++) + { + printf("%d%s%s",pagemaxlen[page],page