diff options
author | unknown <Sinisa@sinisa.nasamreza.org> | 2003-05-21 21:58:12 +0300 |
---|---|---|
committer | unknown <Sinisa@sinisa.nasamreza.org> | 2003-05-21 21:58:12 +0300 |
commit | 0ce9d6af4fffbadeb1824a4868889eba0ac58daa (patch) | |
tree | 5f5a744b449128b32f8a09bf6a567d24f7412b46 | |
parent | 9d97b5ff8ce69b1c9ec10d822c094b1e11351dd8 (diff) | |
parent | 87a9e90fd0042ee06bea16e46cc7353043936b99 (diff) | |
download | mariadb-git-0ce9d6af4fffbadeb1824a4868889eba0ac58daa.tar.gz |
Merge sinisa@bk-internal.mysql.com:/home/bk/mysql-4.0
into sinisa.nasamreza.org:/mnt/work/mysql-4.0
124 files changed, 1826 insertions, 835 deletions
diff --git a/BitKeeper/etc/logging_ok b/BitKeeper/etc/logging_ok index 8074f38541a..c0e329b1c8d 100644 --- a/BitKeeper/etc/logging_ok +++ b/BitKeeper/etc/logging_ok @@ -62,6 +62,7 @@ monty@work.mysql.com mwagner@cash.mwagner.org mwagner@evoq.mwagner.org mwagner@work.mysql.com +mysqldev@build.mysql2.com nick@mysql.com nick@nick.leippe.com paul@central.snake.net diff --git a/client/mysqldump.c b/client/mysqldump.c index 3a66e49a795..459cb9fda31 100644 --- a/client/mysqldump.c +++ b/client/mysqldump.c @@ -1454,7 +1454,7 @@ int main(int argc, char **argv) else { row = mysql_fetch_row(master); - if (row[0] && row[1]) + if (row && row[0] && row[1]) { fprintf(md_result_file, "\n--\n-- Position to start replication from\n--\n\n"); diff --git a/configure.in b/configure.in index a1ea566435b..df83f7e715e 100644 --- a/configure.in +++ b/configure.in @@ -4,7 +4,7 @@ dnl Process this file with autoconf to produce a configure script. AC_INIT(sql/mysqld.cc) AC_CANONICAL_SYSTEM # The Docs Makefile.am parses this line! -AM_INIT_AUTOMAKE(mysql, 4.0.13) +AM_INIT_AUTOMAKE(mysql, 4.0.14) AM_CONFIG_HEADER(config.h) PROTOCOL_VERSION=10 @@ -1007,8 +1007,9 @@ case $SYSTEM_TYPE in *darwin5*) if test "$ac_cv_prog_gcc" = "yes" then - CFLAGS="$CFLAGS -traditional-cpp -DHAVE_DARWIN_THREADS -D_P1003_1B_VISIBLE -DSIGNAL_WITH_VIO_CLOSE -DSIGNALS_DONT_BREAK_READ -DHAVE_BROKEN_REALPATH -DFN_NO_CASE_SENCE" - CXXFLAGS="$CXXFLAGS -traditional-cpp -DHAVE_DARWIN_THREADS -D_P1003_1B_VISIBLE -DSIGNAL_WITH_VIO_CLOSE -DSIGNALS_DONT_BREAK_READ -DFN_NO_CASE_SENCE" + FLAGS="-traditional-cpp -DHAVE_DARWIN_THREADS -D_P1003_1B_VISIBLE -DSIGNAL_WITH_VIO_CLOSE -DSIGNALS_DONT_BREAK_READ -DHAVE_BROKEN_REALPATH -DFN_NO_CASE_SENCE" + CFLAGS="$CFLAGS $FLAGS" + CXXFLAGS="$CXXFLAGS $FLAGS" MAX_C_OPTIMIZE="-O" with_named_curses="" fi @@ -1016,8 +1017,9 @@ case $SYSTEM_TYPE in *darwin6*) if test "$ac_cv_prog_gcc" = "yes" then - CFLAGS="$CFLAGS -traditional-cpp -DHAVE_DARWIN_THREADS -D_P1003_1B_VISIBLE -DSIGNAL_WITH_VIO_CLOSE -DSIGNALS_DONT_BREAK_READ -DHAVE_BROKEN_REALPATH -DFN_NO_CASE_SENCE" - CXXFLAGS="$CXXFLAGS -traditional-cpp -DHAVE_DARWIN_THREADS -D_P1003_1B_VISIBLE -DSIGNAL_WITH_VIO_CLOSE -DSIGNALS_DONT_BREAK_READ -DFN_NO_CASE_SENCE" + FLAGS="-traditional-cpp -DHAVE_DARWIN_THREADS -D_P1003_1B_VISIBLE -DSIGNAL_WITH_VIO_CLOSE -DSIGNALS_DONT_BREAK_READ -DHAVE_BROKEN_REALPATH -DFN_NO_CASE_SENCE" + CFLAGS="$CFLAGS $FLAGS" + CXXFLAGS="$CXXFLAGS $FLAGS" MAX_C_OPTIMIZE="-O" fi ;; @@ -1367,10 +1369,8 @@ then with_named_thread="-Kthread -lsocket -lnsl" if expr "$SYSTEM_TYPE" : ".*unixware7.0.0" > /dev/null then -# AC_DEFINE(HAVE_OpenUNIX8_THREADS) AC_DEFINE(HAVE_UNIXWARE7_THREADS) else -# AC_DEFINE(HAVE_OpenUNIX8_POSIX) AC_DEFINE(HAVE_UNIXWARE7_POSIX) fi # We must have cc @@ -1379,10 +1379,8 @@ then then { echo "configure: error: On OpenUNIX8 and UnixWare7 MySQL must be compiled with cc. See the Installation chapter in the Reference Manual." 1>&2; exit 1; }; else -# CC="$CC -Kthread -DOpenUNIX8"; -# CXX="$CXX -Kthread -DOpenUNIX8"; - CC="$CC -Kthread -DUNIXWARE_7 -DHAVE_BROKEN_RWLOCK"; - CXX="$CXX -Kthread -DUNIXWARE_7 -DHAVE_BROKEN_RWLOCK" + CC="$CC -Kthread -DUNIXWARE_7 -DHAVE_BROKEN_RWLOCK"; + CXX="$CXX -Kthread -DUNIXWARE_7 -DHAVE_BROKEN_RWLOCK"; fi AC_MSG_RESULT("yes") else diff --git a/include/my_base.h b/include/my_base.h index f91b45ee469..cd04ab971db 100644 --- a/include/my_base.h +++ b/include/my_base.h @@ -224,6 +224,7 @@ enum ha_base_keytype { /* Errorcodes given by functions */ +/* opt_sum_query() assumes these codes are > 1 */ #define HA_ERR_KEY_NOT_FOUND 120 /* Didn't find key on read or update */ #define HA_ERR_FOUND_DUPP_KEY 121 /* Dupplicate key on write */ #define HA_ERR_RECORD_CHANGED 123 /* Uppdate with is recoverable */ diff --git a/include/my_global.h b/include/my_global.h index 98034fc1cff..15495c60dd7 100644 --- a/include/my_global.h +++ b/include/my_global.h @@ -290,9 +290,7 @@ C_MODE_END /* This has to be after include limits.h */ #define HAVE_ERRNO_AS_DEFINE #define HAVE_FCNTL_LOCK -#undef HAVE_SYS_UN_H #undef HAVE_FINITE -#undef HAVE_RINT #undef LONGLONG_MIN /* These get wrongly defined in QNX 6.2 */ #undef LONGLONG_MAX /* standard system library 'limits.h' */ #endif @@ -361,7 +359,7 @@ typedef unsigned short ushort; #define set_bits(type, bit_count) (sizeof(type)*8 <= (bit_count) ? ~(type) 0 : ((((type) 1) << (bit_count)) - (type) 1)) #define array_elements(A) ((uint) (sizeof(A)/sizeof(A[0]))) #ifndef HAVE_RINT -#define rint(A) floor((A)+0.5) +#define rint(A) floor((A)+(((A) < 0)? -0.5 : 0.5)) #endif /* Define some general constants */ diff --git a/include/my_sys.h b/include/my_sys.h index c67a150f24f..603b3bad6bd 100644 --- a/include/my_sys.h +++ b/include/my_sys.h @@ -749,6 +749,7 @@ extern my_bool my_uncompress(byte *, ulong *, ulong *); extern byte *my_compress_alloc(const byte *packet, ulong *len, ulong *complen); extern ulong checksum(const byte *mem, uint count); extern uint my_bit_log2(ulong value); +uint my_count_bits(ulonglong v); extern void my_sleep(ulong m_seconds); #ifdef __WIN__ diff --git a/innobase/buf/buf0buf.c b/innobase/buf/buf0buf.c index 42799da9d7c..e000d862403 100644 --- a/innobase/buf/buf0buf.c +++ b/innobase/buf/buf0buf.c @@ -1613,7 +1613,7 @@ buf_pool_invalidate(void) freed = TRUE; while (freed) { - freed = buf_LRU_search_and_free_block(0); + freed = buf_LRU_search_and_free_block(100); } mutex_enter(&(buf_pool->mutex)); @@ -1833,6 +1833,29 @@ buf_get_n_pending_ios(void) } /************************************************************************* +Returns the ratio in percents of modified pages in the buffer pool / +database pages in the buffer pool. */ + +ulint +buf_get_modified_ratio_pct(void) +/*============================*/ +{ + ulint ratio; + + mutex_enter(&(buf_pool->mutex)); + + ratio = (100 * UT_LIST_GET_LEN(buf_pool->flush_list)) + / (1 + UT_LIST_GET_LEN(buf_pool->LRU) + + UT_LIST_GET_LEN(buf_pool->free)); + + /* 1 + is there to avoid division by zero */ + + mutex_exit(&(buf_pool->mutex)); + + return(ratio); +} + +/************************************************************************* Prints info of the buffer i/o. */ void @@ -1876,8 +1899,10 @@ buf_print_io( buf += sprintf(buf, "Pending writes: LRU %lu, flush list %lu, single page %lu\n", - buf_pool->n_flush[BUF_FLUSH_LRU], - buf_pool->n_flush[BUF_FLUSH_LIST], + buf_pool->n_flush[BUF_FLUSH_LRU] + + buf_pool->init_flush[BUF_FLUSH_LRU], + buf_pool->n_flush[BUF_FLUSH_LIST] + + buf_pool->init_flush[BUF_FLUSH_LIST], buf_pool->n_flush[BUF_FLUSH_SINGLE_PAGE]); current_time = time(NULL); diff --git a/innobase/buf/buf0flu.c b/innobase/buf/buf0flu.c index 516056b5174..735966c28c5 100644 --- a/innobase/buf/buf0flu.c +++ b/innobase/buf/buf0flu.c @@ -398,7 +398,7 @@ buf_flush_write_block_low( "Warning: cannot force log to disk in the log debug version!\n"); #else /* Force the log to the disk before writing the modified block */ - log_flush_up_to(block->newest_modification, LOG_WAIT_ALL_GROUPS); + log_write_up_to(block->newest_modification, LOG_WAIT_ALL_GROUPS, TRUE); #endif buf_flush_init_for_writing(block->frame, block->newest_modification, block->space, block->offset); @@ -531,7 +531,8 @@ buf_flush_try_page( rw_lock_s_lock_gen(&(block->lock), BUF_IO_WRITE); if (buf_debug_prints) { - printf("Flushing single page space %lu, page no %lu \n", + printf( + "Flushing single page space %lu, page no %lu \n", block->space, block->offset); } @@ -572,15 +573,7 @@ buf_flush_try_neighbors( low = offset; high = offset + 1; - } else if (flush_type == BUF_FLUSH_LIST) { - /* Since semaphore waits require us to flush the - doublewrite buffer to disk, it is best that the - search area is just the page itself, to minimize - chances for semaphore waits */ - - low = offset; - high = offset + 1; - } + } /* printf("Flush area: low %lu high %lu\n", low, high); */ @@ -597,13 +590,20 @@ buf_flush_try_neighbors( if (block && flush_type == BUF_FLUSH_LRU && i != offset && !block->old) { - /* We avoid flushing 'non-old' blocks in an LRU flush, - because the flushed blocks are soon freed */ + /* We avoid flushing 'non-old' blocks in an LRU flush, + because the flushed blocks are soon freed */ - continue; + continue; } - if (block && buf_flush_ready_for_flush(block, flush_type)) { + if (block && buf_flush_ready_for_flush(block, flush_type) + && (i == offset || block->buf_fix_count == 0)) { + /* We only try to flush those neighbors != offset + where the buf fix count is zero, as we then know that + we probably can latch the page without a semaphore + wait. Semaphore waits are expensive because we must + flush the doublewrite buffer before we start + waiting. */ mutex_exit(&(buf_pool->mutex)); @@ -722,7 +722,6 @@ buf_flush_batch( page_count += buf_flush_try_neighbors(space, offset, flush_type); - /* printf( "Flush type %lu, page no %lu, neighb %lu\n", flush_type, offset, @@ -848,11 +847,19 @@ buf_flush_free_margin(void) /*=======================*/ { ulint n_to_flush; + ulint n_flushed; n_to_flush = buf_flush_LRU_recommendation(); if (n_to_flush > 0) { - buf_flush_batch(BUF_FLUSH_LRU, n_to_flush, ut_dulint_zero); + n_flushed = buf_flush_batch(BUF_FLUSH_LRU, n_to_flush, + ut_dulint_zero); + if (n_flushed == ULINT_UNDEFINED) { + /* There was an LRU type flush batch already running; + let us wait for it to end */ + + buf_flush_wait_batch_end(BUF_FLUSH_LRU); + } } } diff --git a/innobase/buf/buf0lru.c b/innobase/buf/buf0lru.c index 2ec1506c522..eeccbf7728a 100644 --- a/innobase/buf/buf0lru.c +++ b/innobase/buf/buf0lru.c @@ -104,12 +104,15 @@ ibool buf_LRU_search_and_free_block( /*==========================*/ /* out: TRUE if freed */ - ulint n_iterations __attribute__((unused))) /* in: how many times - this has been called repeatedly without - result: a high value means that we should - search farther */ + ulint n_iterations) /* in: how many times this has been called + repeatedly without result: a high value means + that we should search farther; if value is + k < 10, then we only search k/10 * [number + of pages in the buffer pool] from the end + of the LRU list */ { buf_block_t* block; + ulint distance = 0; ibool freed; mutex_enter(&(buf_pool->mutex)); @@ -146,6 +149,18 @@ buf_LRU_search_and_free_block( } block = UT_LIST_GET_PREV(LRU, block); + distance++; + + if (!freed && n_iterations <= 10 + && distance > 100 + (n_iterations * buf_pool->curr_size) + / 10) { + + buf_pool->LRU_flush_ended = 0; + + mutex_exit(&(buf_pool->mutex)); + + return(FALSE); + } } if (buf_pool->LRU_flush_ended > 0) { @@ -180,7 +195,7 @@ buf_LRU_try_free_flushed_blocks(void) mutex_exit(&(buf_pool->mutex)); - buf_LRU_search_and_free_block(0); + buf_LRU_search_and_free_block(1); mutex_enter(&(buf_pool->mutex)); } @@ -200,7 +215,7 @@ buf_LRU_get_free_block(void) { buf_block_t* block = NULL; ibool freed; - ulint n_iterations = 0; + ulint n_iterations = 1; ibool mon_value_was = 0; /* remove bug */ ibool started_monitor = FALSE; loop: @@ -228,9 +243,12 @@ loop: fprintf(stderr, " InnoDB: WARNING: over 4 / 5 of the buffer pool is occupied by\n" "InnoDB: lock heaps or the adaptive hash index! Check that your\n" -"InnoDB: transactions do not set too many row locks. Starting InnoDB\n" -"InnoDB: Monitor to print diagnostics, including lock heap and hash index\n" -"InnoDB: sizes.\n"); +"InnoDB: transactions do not set too many row locks.\n" +"InnoDB: Your buffer pool size is %lu MB. Maybe you should make\n" +"InnoDB: the buffer pool bigger?\n" +"InnoDB: Starting the InnoDB Monitor to print diagnostics, including\n" +"InnoDB: lock heap and hash index sizes.\n", + buf_pool->curr_size / (1024 * 1024 / UNIV_PAGE_SIZE)); srv_print_innodb_monitor = TRUE; @@ -243,14 +261,6 @@ loop: srv_print_innodb_monitor = FALSE; } - - if (buf_pool->LRU_flush_ended > 0) { - mutex_exit(&(buf_pool->mutex)); - - buf_LRU_try_free_flushed_blocks(); - - mutex_enter(&(buf_pool->mutex)); - } /* If there is a block in the free list, take it */ if (UT_LIST_GET_LEN(buf_pool->free) > 0) { @@ -316,6 +326,20 @@ loop: os_aio_simulated_wake_handler_threads(); + mutex_enter(&(buf_pool->mutex)); + + if (buf_pool->LRU_flush_ended > 0) { + /* We have written pages in an LRU flush. To make the insert + buffer more efficient, we try to move these pages to the free + list. */ + + mutex_exit(&(buf_pool->mutex)); + + buf_LRU_try_free_flushed_blocks(); + } else { + mutex_exit(&(buf_pool->mutex)); + } + if (n_iterations > 10) { os_thread_sleep(500000); diff --git a/innobase/dict/dict0dict.c b/innobase/dict/dict0dict.c index 74fe5cd5b70..c11a5f76d94 100644 --- a/innobase/dict/dict0dict.c +++ b/innobase/dict/dict0dict.c @@ -185,10 +185,12 @@ dict_foreign_free( /*==============*/ dict_foreign_t* foreign); /* in, own: foreign key struct */ -/* Buffer for storing detailed information about the latest foreig key -error */ +/* Buffers for storing detailed information about the latest foreign key +and unique key errors */ char* dict_foreign_err_buf = NULL; -mutex_t dict_foreign_err_mutex; /* mutex protecting the buffer */ +char* dict_unique_err_buf = NULL; +mutex_t dict_foreign_err_mutex; /* mutex protecting the foreign + and unique error buffers */ /************************************************************************ @@ -582,6 +584,8 @@ dict_init(void) dict_foreign_err_buf = mem_alloc(DICT_FOREIGN_ERR_BUF_LEN); dict_foreign_err_buf[0] = '\0'; + dict_unique_err_buf = mem_alloc(DICT_FOREIGN_ERR_BUF_LEN); + dict_unique_err_buf[0] = '\0'; mutex_create(&dict_foreign_err_mutex); mutex_set_level(&dict_foreign_err_mutex, SYNC_ANY_LATCH); } diff --git a/innobase/include/buf0buf.h b/innobase/include/buf0buf.h index 395f88a2c7c..e4d3671586d 100644 --- a/innobase/include/buf0buf.h +++ b/innobase/include/buf0buf.h @@ -472,6 +472,13 @@ buf_print_io( /*=========*/ char* buf, /* in/out: buffer where to print */ char* buf_end);/* in: buffer end */ +/************************************************************************* +Returns the ratio in percents of modified pages in the buffer pool / +database pages in the buffer pool. */ + +ulint +buf_get_modified_ratio_pct(void); +/*============================*/ /************************************************************************** Refreshes the statistics used to print per-second averages. */ diff --git a/innobase/include/buf0lru.h b/innobase/include/buf0lru.h index 946b6c4e31d..eb9d43d3b93 100644 --- a/innobase/include/buf0lru.h +++ b/innobase/include/buf0lru.h @@ -46,6 +46,20 @@ buf_LRU_get_recent_limit(void); /*==========================*/ /* out: the limit; zero if could not determine it */ /********************************************************************** +Look for a replaceable block from the end of the LRU list and put it to +the free list if found. */ + +ibool +buf_LRU_search_and_free_block( +/*==========================*/ + /* out: TRUE if freed */ + ulint n_iterations); /* in: how many times this has been called + repeatedly without result: a high value means + that we should search farther; if value is + k < 10, then we only search k/10 * number + of pages in the buffer pool from the end + of the LRU list */ +/********************************************************************** Returns a free block from the buf_pool. The block is taken off the free list. If it is empty, blocks are moved from the end of the LRU list to the free list. */ @@ -86,17 +100,6 @@ void buf_LRU_make_block_old( /*===================*/ buf_block_t* block); /* in: control block */ -/********************************************************************** -Look for a replaceable block from the end of the LRU list and put it to -the free list if found. */ - -ibool -buf_LRU_search_and_free_block( -/*==========================*/ - /* out: TRUE if freed */ - ulint n_iterations); /* in: how many times this has been called - repeatedly without result: a high value - means that we should search farther */ /************************************************************************** Validates the LRU list. */ diff --git a/innobase/include/dict0dict.h b/innobase/include/dict0dict.h index 600965700ed..97486a7c2f6 100644 --- a/innobase/include/dict0dict.h +++ b/innobase/include/dict0dict.h @@ -839,10 +839,11 @@ dict_mutex_exit_for_mysql(void); /* The following len must be at least 10000 bytes! */ #define DICT_FOREIGN_ERR_BUF_LEN 10000 -/* Buffer for storing detailed information about the latest foreig key -error */ +/* Buffers for storing detailed information about the latest foreign key +and unique key errors */ extern char* dict_foreign_err_buf; -extern mutex_t dict_foreign_err_mutex; /* mutex protecting the buffer */ +extern char* dict_unique_err_buf; +extern mutex_t dict_foreign_err_mutex; /* mutex protecting the buffers */ extern dict_sys_t* dict_sys; /* the dictionary system */ extern rw_lock_t dict_operation_lock; diff --git a/innobase/include/log0log.h b/innobase/include/log0log.h index f200371de9d..4e1404b15fe 100644 --- a/innobase/include/log0log.h +++ b/innobase/include/log0log.h @@ -20,7 +20,7 @@ typedef struct log_group_struct log_group_t; extern ibool log_do_write; extern ibool log_debug_writes; -/* Wait modes for log_flush_up_to */ +/* Wait modes for log_write_up_to */ #define LOG_NO_WAIT 91 #define LOG_WAIT_ONE_GROUP 92 #define LOG_WAIT_ALL_GROUPS 93 @@ -157,26 +157,21 @@ log_io_complete( /*============*/ log_group_t* group); /* in: log group */ /********************************************************** -Flushes the log files to the disk, using, for example, the Unix fsync. -This function does the flush even if the user has set -srv_flush_log_at_trx_commit = FALSE. */ - -void -log_flush_to_disk(void); -/*===================*/ -/********************************************************** This function is called, e.g., when a transaction wants to commit. It checks -that the log has been flushed to disk up to the last log entry written by the -transaction. If there is a flush running, it waits and checks if the flush -flushed enough. If not, starts a new flush. */ +that the log has been written to the log file up to the last log entry written +by the transaction. If there is a flush running, it waits and checks if the +flush flushed enough. If not, starts a new flush. */ void -log_flush_up_to( +log_write_up_to( /*============*/ dulint lsn, /* in: log sequence number up to which the log should - be flushed, ut_dulint_max if not specified */ - ulint wait); /* in: LOG_NO_WAIT, LOG_WAIT_ONE_GROUP, + be written, ut_dulint_max if not specified */ + ulint wait, /* in: LOG_NO_WAIT, LOG_WAIT_ONE_GROUP, or LOG_WAIT_ALL_GROUPS */ + ibool flush_to_disk); + /* in: TRUE if we want the written log also to be + flushed to disk */ /******************************************************************** Advances the smallest lsn for which there are unflushed dirty blocks in the buffer pool and also may make a new checkpoint. NOTE: this function may only @@ -741,27 +736,37 @@ struct log_struct{ be advanced, it is enough that the write i/o has been completed for all log groups */ - dulint flush_lsn; /* end lsn for the current flush */ - ulint flush_end_offset;/* the data in buffer has been flushed + dulint write_lsn; /* end lsn for the current running + write */ + ulint write_end_offset;/* the data in buffer has been written up to this offset when the current - flush ends: this field will then + write ends: this field will then be copied to buf_next_to_write */ - ulint n_pending_writes;/* number of currently pending flush - writes */ + dulint current_flush_lsn;/* end lsn for the current running + write + flush operation */ + dulint flushed_to_disk_lsn; + /* how far we have written the log + AND flushed to disk */ + ulint n_pending_writes;/* number of currently pending flushes + or writes */ + /* NOTE on the 'flush' in names of the fields below: starting from + 4.0.14, we separate the write of the log file and the actual fsync() + or other method to flush it to disk. The names below shhould really + be 'flush_or_write'! */ os_event_t no_flush_event; /* this event is in the reset state - when a flush is running; a thread - should wait for this without owning - the log mutex, but NOTE that to set or - reset this event, the thread MUST own - the log mutex! */ + when a flush or a write is running; + a thread should wait for this without + owning the log mutex, but NOTE that + to set or reset this event, the + thread MUST own the log mutex! */ ibool one_flushed; /* during a flush, this is first FALSE and becomes TRUE when one log group - has been flushed */ + has been written or flushed */ os_event_t one_flushed_event;/* this event is reset when the - flush has not yet completed for any - log group; e.g., this means that a - transaction has been committed when - this is set; a thread should wait + flush or write has not yet completed + for any log group; e.g., this means + that a transaction has been committed + when this is set; a thread should wait for this without owning the log mutex, but NOTE that to set or reset this event, the thread MUST own the log diff --git a/innobase/include/srv0srv.h b/innobase/include/srv0srv.h index ad6f71f7a3a..8355496762c 100644 --- a/innobase/include/srv0srv.h +++ b/innobase/include/srv0srv.h @@ -74,6 +74,9 @@ extern ulint srv_lock_wait_timeout; extern char* srv_file_flush_method_str; extern ulint srv_unix_file_flush_method; extern ulint srv_win_file_flush_method; + +extern ulint srv_max_dirty_pages_pct; + extern ulint srv_force_recovery; extern ulint srv_thread_concurrency; diff --git a/innobase/include/trx0trx.h b/innobase/include/trx0trx.h index be96519c4ea..39229923375 100644 --- a/innobase/include/trx0trx.h +++ b/innobase/include/trx0trx.h @@ -157,6 +157,15 @@ trx_commit_for_mysql( /* out: 0 or error number */ trx_t* trx); /* in: trx handle */ /************************************************************************** +If required, flushes the log to disk if we called trx_commit_for_mysql() +with trx->flush_log_later == TRUE. */ + +ulint +trx_commit_complete_for_mysql( +/*==========================*/ + /* out: 0 or error number */ + trx_t* trx); /* in: trx handle */ +/************************************************************************** Marks the latest SQL statement ended. */ void @@ -343,6 +352,11 @@ struct trx_struct{ dulint no; /* transaction serialization number == max trx id when the transaction is moved to COMMITTED_IN_MEMORY state */ + ibool flush_log_later;/* when we commit the transaction + in MySQL's binlog write, we will + flush the log to disk later in + a separate call */ + dulint commit_lsn; /* lsn at the time of the commit */ ibool dict_operation; /* TRUE if the trx is used to create a table, create an index, or drop a table */ diff --git a/innobase/log/log0log.c b/innobase/log/log0log.c index 539cde337bd..25cc666e802 100644 --- a/innobase/log/log0log.c +++ b/innobase/log/log0log.c @@ -178,7 +178,7 @@ loop: /* Not enough free space, do a syncronous flush of the log buffer */ - log_flush_up_to(ut_dulint_max, LOG_WAIT_ALL_GROUPS); + log_write_up_to(ut_dulint_max, LOG_WAIT_ALL_GROUPS, TRUE); count++; @@ -675,7 +675,9 @@ log_init(void) log_sys->buf_next_to_write = 0; - log_sys->flush_lsn = ut_dulint_zero; + log_sys->write_lsn = ut_dulint_zero; + log_sys->current_flush_lsn = ut_dulint_zero; + log_sys->flushed_to_disk_lsn = ut_dulint_zero; log_sys->written_to_some_lsn = log_sys->lsn; log_sys->written_to_all_lsn = log_sys->lsn; @@ -867,7 +869,7 @@ log_group_check_flush_completion( printf("Log flushed first to group %lu\n", group->id); } - log_sys->written_to_some_lsn = log_sys->flush_lsn; + log_sys->written_to_some_lsn = log_sys->write_lsn; log_sys->one_flushed = TRUE; return(LOG_UNLOCK_NONE_FLUSHED_LOCK); @@ -896,15 +898,15 @@ log_sys_check_flush_completion(void) if (log_sys->n_pending_writes == 0) { - log_sys->written_to_all_lsn = log_sys->flush_lsn; - log_sys->buf_next_to_write = log_sys->flush_end_offset; + log_sys->written_to_all_lsn = log_sys->write_lsn; + log_sys->buf_next_to_write = log_sys->write_end_offset; - if (log_sys->flush_end_offset > log_sys->max_buf_free / 2) { + if (log_sys->write_end_offset > log_sys->max_buf_free / 2) { /* Move the log buffer content to the start of the buffer */ move_start = ut_calc_align_down( - log_sys->flush_end_offset, + log_sys->write_end_offset, OS_FILE_LOG_BLOCK_SIZE); move_end = ut_calc_align(log_sys->buf_free, OS_FILE_LOG_BLOCK_SIZE); @@ -982,57 +984,6 @@ log_io_complete( } /********************************************************** -Flushes the log files to the disk, using, for example, the Unix fsync. -This function does the flush even if the user has set -srv_flush_log_at_trx_commit = FALSE. */ - -void -log_flush_to_disk(void) -/*===================*/ -{ - log_group_t* group; -loop: - mutex_enter(&(log_sys->mutex)); - - if (log_sys->n_pending_writes > 0) { - /* A log file write is running */ - - mutex_exit(&(log_sys->mutex)); - - /* Wait for the log file write to complete and try again */ - - os_event_wait(log_sys->no_flush_event); - - goto loop; - } - - group = UT_LIST_GET_FIRST(log_sys->log_groups); - - log_sys->n_pending_writes++; - group->n_pending_writes++; - - os_event_reset(log_sys->no_flush_event); - os_event_reset(log_sys->one_flushed_event); - - mutex_exit(&(log_sys->mutex)); - - fil_flush(group->space_id); - - mutex_enter(&(log_sys->mutex)); - - ut_a(group->n_pending_writes == 1); - ut_a(log_sys->n_pending_writes == 1); - - group->n_pending_writes--; - log_sys->n_pending_writes--; - - os_event_set(log_sys->no_flush_event); - os_event_set(log_sys->one_flushed_event); - - mutex_exit(&(log_sys->mutex)); -} - -/********************************************************** Writes a log file header to a log file space. */ static void @@ -1205,12 +1156,15 @@ by the transaction. If there is a flush running, it waits and checks if the flush flushed enough. If not, starts a new flush. */ void -log_flush_up_to( +log_write_up_to( /*============*/ dulint lsn, /* in: log sequence number up to which the log should be written, ut_dulint_max if not specified */ - ulint wait) /* in: LOG_NO_WAIT, LOG_WAIT_ONE_GROUP, + ulint wait, /* in: LOG_NO_WAIT, LOG_WAIT_ONE_GROUP, or LOG_WAIT_ALL_GROUPS */ + ibool flush_to_disk) + /* in: TRUE if we want the written log also to be + flushed to disk */ { log_group_t* group; ulint start_offset; @@ -1239,9 +1193,18 @@ loop: mutex_enter(&(log_sys->mutex)); - if ((ut_dulint_cmp(log_sys->written_to_all_lsn, lsn) >= 0) - || ((ut_dulint_cmp(log_sys->written_to_some_lsn, lsn) >= 0) - && (wait != LOG_WAIT_ALL_GROUPS))) { + if (flush_to_disk + && ut_dulint_cmp(log_sys->flushed_to_disk_lsn, lsn) >= 0) { + + mutex_exit(&(log_sys->mutex)); + + return; + } + + if (!flush_to_disk + && (ut_dulint_cmp(log_sys->written_to_all_lsn, lsn) >= 0 + || (ut_dulint_cmp(log_sys->written_to_some_lsn, lsn) >= 0 + && wait != LOG_WAIT_ALL_GROUPS))) { mutex_exit(&(log_sys->mutex)); @@ -1249,10 +1212,19 @@ loop: } if (log_sys->n_pending_writes > 0) { - /* A flush is running */ + /* A write (+ possibly flush to disk) is running */ + + if (flush_to_disk + && ut_dulint_cmp(log_sys->current_flush_lsn, lsn) >= 0) { + /* The write + flush will write enough: wait for it to + complete */ + + goto do_waits; + } - if (ut_dulint_cmp(log_sys->flush_lsn, lsn) >= 0) { - /* The flush will flush enough: wait for it to + if (!flush_to_disk + && ut_dulint_cmp(log_sys->write_lsn, lsn) >= 0) { + /* The write will write enough: wait for it to complete */ goto do_waits; @@ -1260,16 +1232,17 @@ loop: mutex_exit(&(log_sys->mutex)); - /* Wait for the flush to complete and try to start a new - flush */ + /* Wait for the write to complete and try to start a new + write */ os_event_wait(log_sys->no_flush_event); goto loop; } - if (log_sys->buf_free == log_sys->buf_next_to_write) { - /* Nothing to flush */ + if (!flush_to_disk + && log_sys->buf_free == log_sys->buf_next_to_write) { + /* Nothing to write and no flush to disk requested */ mutex_exit(&(log_sys->mutex)); @@ -1277,7 +1250,7 @@ loop: } if (log_debug_writes) { - printf("Flushing log from %lu %lu up to lsn %lu %lu\n", + printf("Writing log from %lu %lu up to lsn %lu %lu\n", ut_dulint_get_high(log_sys->written_to_all_lsn), ut_dulint_get_low(log_sys->written_to_all_lsn), ut_dulint_get_high(log_sys->lsn), @@ -1301,7 +1274,12 @@ loop: ut_ad(area_end - area_start > 0); - log_sys->flush_lsn = log_sys->lsn; + log_sys->write_lsn = log_sys->lsn; + + if (flush_to_disk) { + log_sys->current_flush_lsn = log_sys->lsn; + } + log_sys->one_flushed = FALSE; log_block_set_flush_bit(log_sys->buf + area_start, TRUE); @@ -1318,10 +1296,12 @@ loop: OS_FILE_LOG_BLOCK_SIZE); log_sys->buf_free += OS_FILE_LOG_BLOCK_SIZE; - log_sys->flush_end_offset = log_sys->buf_free; + log_sys->write_end_offset = log_sys->buf_free; group = UT_LIST_GET_FIRST(log_sys->log_groups); + /* Do the write to the log files */ + while (group) { log_group_write_buf(LOG_FLUSH, group, log_sys->buf + area_start, @@ -1330,20 +1310,25 @@ loop: OS_FILE_LOG_BLOCK_SIZE), start_offset - area_start); - log_group_set_fields(group, log_sys->flush_lsn); + log_group_set_fields(group, log_sys->write_lsn); group = UT_LIST_GET_NEXT(log_groups, group); } mutex_exit(&(log_sys->mutex)); - if (srv_unix_file_flush_method != SRV_UNIX_O_DSYNC - && srv_unix_file_flush_method != SRV_UNIX_NOSYNC - && srv_flush_log_at_trx_commit != 2) { + if (srv_unix_file_flush_method == SRV_UNIX_O_DSYNC) { + /* O_DSYNC means the OS did not buffer the log file at all: + so we have also flushed to disk what we have written */ + + log_sys->flushed_to_disk_lsn = log_sys->write_lsn; + + } else if (flush_to_disk) { group = UT_LIST_GET_FIRST(log_sys->log_groups); fil_flush(group->space_id); + log_sys->flushed_to_disk_lsn = log_sys->write_lsn; } mutex_enter(&(log_sys->mutex)); @@ -1403,7 +1388,7 @@ log_flush_margin(void) mutex_exit(&(log->mutex)); if (do_flush) { - log_flush_up_to(ut_dulint_max, LOG_NO_WAIT); + log_write_up_to(ut_dulint_max, LOG_NO_WAIT, FALSE); } } @@ -1555,7 +1540,8 @@ log_group_checkpoint( buf = group->checkpoint_buf; mach_write_to_8(buf + LOG_CHECKPOINT_NO, log_sys->next_checkpoint_no); - mach_write_to_8(buf + LOG_CHECKPOINT_LSN, log_sys->next_checkpoint_lsn); + mach_write_to_8(buf + LOG_CHECKPOINT_LSN, + log_sys->next_checkpoint_lsn); mach_write_to_4(buf + LOG_CHECKPOINT_OFFSET, log_group_calc_lsn_offset( @@ -1664,8 +1650,10 @@ log_reset_first_header_and_checkpoint( lsn = ut_dulint_add(start, LOG_BLOCK_HDR_SIZE); /* Write the label of ibbackup --restore */ - sprintf((char*) hdr_buf + LOG_FILE_WAS_CREATED_BY_HOT_BACKUP, "ibbackup "); - ut_sprintf_timestamp((char*) hdr_buf + LOG_FILE_WAS_CREATED_BY_HOT_BACKUP + sprintf((char*) hdr_buf + LOG_FILE_WAS_CREATED_BY_HOT_BACKUP, + "ibbackup "); + ut_sprintf_timestamp( + (char*) hdr_buf + LOG_FILE_WAS_CREATED_BY_HOT_BACKUP + strlen("ibbackup ")); buf = hdr_buf + LOG_CHECKPOINT_1; @@ -1773,7 +1761,7 @@ log_checkpoint( write-ahead-logging algorithm ensures that the log has been flushed up to oldest_lsn. */ - log_flush_up_to(oldest_lsn, LOG_WAIT_ALL_GROUPS); + log_write_up_to(oldest_lsn, LOG_WAIT_ALL_GROUPS, TRUE); mutex_enter(&(log_sys->mutex)); @@ -2466,7 +2454,7 @@ loop: mutex_exit(&(log_sys->mutex)); - log_flush_up_to(limit_lsn, LOG_WAIT_ALL_GROUPS); + log_write_up_to(limit_lsn, LOG_WAIT_ALL_GROUPS, TRUE); calc_new_limit = FALSE; @@ -3104,8 +3092,8 @@ log_print( "Last checkpoint at %lu %lu\n", ut_dulint_get_high(log_sys->lsn), ut_dulint_get_low(log_sys->lsn), - ut_dulint_get_high(log_sys->written_to_some_lsn), - ut_dulint_get_low(log_sys->written_to_some_lsn), + ut_dulint_get_high(log_sys->flushed_to_disk_lsn), + ut_dulint_get_low(log_sys->flushed_to_disk_lsn), ut_dulint_get_high(log_sys->last_checkpoint_lsn), ut_dulint_get_low(log_sys->last_checkpoint_lsn)); diff --git a/innobase/os/os0file.c b/innobase/os/os0file.c index 1d1d84adda7..c7f95d79104 100644 --- a/innobase/os/os0file.c +++ b/innobase/os/os0file.c @@ -521,10 +521,11 @@ try_again: } #endif #ifdef UNIV_NON_BUFFERED_IO - if (type == OS_LOG_FILE && srv_flush_log_at_trx_commit == 2) { + if (type == OS_LOG_FILE) { /* Do not use unbuffered i/o to log files because - value 2 denotes that we do not flush the log at every - commit, but only once per second */ + to allow group commit to work when MySQL binlogging + is used we must separate log file write and log + file flush to disk. */ } else { if (srv_win_file_flush_method == SRV_WIN_IO_UNBUFFERED) { @@ -751,7 +752,12 @@ os_file_set_size( offset = 0; low = (ib_longlong)size + (((ib_longlong)size_high) << 32); + + if (low >= (ib_longlong)(100 * 1024 * 1024)) { + fprintf(stderr, "InnoDB: Progress in MB:"); + } + while (offset < low) { if (low - offset < UNIV_PAGE_SIZE * 512) { n_bytes = (ulint)(low - offset); @@ -767,9 +773,24 @@ os_file_set_size( ut_free(buf2); goto error_handling; } + + /* Print about progress for each 100 MB written */ + if ((offset + n_bytes) / (ib_longlong)(100 * 1024 * 1024) + != offset / (ib_longlong)(100 * 1024 * 1024)) { + + fprintf(stderr, " %lu00", + (ulint)((offset + n_bytes) + / (ib_longlong)(100 * 1024 * 1024))); + } + offset += n_bytes; } + if (low >= (ib_longlong)(100 * 1024 * 1024)) { + + fprintf(stderr, "\n"); + } + ut_free(buf2); ret = os_file_flush(file); diff --git a/innobase/os/os0sync.c b/innobase/os/os0sync.c index a9127e6310a..407b280f805 100644 --- a/innobase/os/os0sync.c +++ b/innobase/os/os0sync.c @@ -68,9 +68,10 @@ os_event_create( os_fast_mutex_init(&(event->os_mutex)); #if defined(UNIV_HOTBACKUP) && defined(UNIV_HPUX10) - pthread_cond_init(&(event->cond_var), pthread_condattr_default); + ut_a(0 == pthread_cond_init(&(event->cond_var), + pthread_condattr_default)); #else - pthread_cond_init(&(event->cond_var), NULL); + ut_a(0 == pthread_cond_init(&(event->cond_var), NULL)); #endif event->is_set = FALSE; @@ -130,7 +131,7 @@ os_event_set( /* Do nothing */ } else { event->is_set = TRUE; - pthread_cond_broadcast(&(event->cond_var)); + ut_a(0 == pthread_cond_broadcast(&(event->cond_var))); } os_fast_mutex_unlock(&(event->os_mutex)); @@ -182,7 +183,7 @@ os_event_free( ut_a(event); os_fast_mutex_free(&(event->os_mutex)); - pthread_cond_destroy(&(event->cond_var)); + ut_a(0 == pthread_cond_destroy(&(event->cond_var))); ut_free(event); #endif @@ -446,9 +447,9 @@ os_fast_mutex_init( InitializeCriticalSection((LPCRITICAL_SECTION) fast_mutex); #else #if defined(UNIV_HOTBACKUP) && defined(UNIV_HPUX10) - pthread_mutex_init(fast_mutex, pthread_mutexattr_default); + ut_a(0 == pthread_mutex_init(fast_mutex, pthread_mutexattr_default)); #else - pthread_mutex_init(fast_mutex, MY_MUTEX_INIT_FAST); + ut_a(0 == pthread_mutex_init(fast_mutex, MY_MUTEX_INIT_FAST)); #endif #endif } @@ -495,10 +496,7 @@ os_fast_mutex_free( ut_a(fast_mutex); DeleteCriticalSection((LPCRITICAL_SECTION) fast_mutex); -#elif defined(__NETWARE__) || defined(SAFE_MUTEX_DETECT_DESTROY) - pthread_mutex_destroy(fast_mutex); #else - UT_NOT_USED(fast_mutex); - + ut_a(0 == pthread_mutex_destroy(fast_mutex)); #endif } diff --git a/innobase/row/row0ins.c b/innobase/row/row0ins.c index 596273477aa..3af9e1b752b 100644 --- a/innobase/row/row0ins.c +++ b/innobase/row/row0ins.c @@ -1263,6 +1263,48 @@ row_ins_check_foreign_constraints( return(DB_SUCCESS); } +/************************************************************************* +Reports a UNIQUE key error to dict_unique_err_buf so that SHOW INNODB +STATUS can print it. */ +static +void +row_ins_unique_report_err( +/*======================*/ + que_thr_t* thr, /* in: query thread */ + rec_t* rec, /* in: a record in the index */ + dtuple_t* entry, /* in: index entry to insert in the index */ + dict_index_t* index) /* in: index */ +{ + char* buf = dict_unique_err_buf; + + /* The foreign err mutex protects also dict_unique_err_buf */ + + mutex_enter(&dict_foreign_err_mutex); + + ut_sprintf_timestamp(buf); + sprintf(buf + strlen(buf), " Transaction:\n"); + trx_print(buf + strlen(buf), thr_get_trx(thr)); + + sprintf(buf + strlen(buf), +"Unique key constraint fails for table %.500s.\n", index->table_name); + sprintf(buf + strlen(buf), +"Trying to add in index %.500s (%lu fields unique) tuple:\n", index->name, + dict_index_get_n_unique(index)); + + dtuple_sprintf(buf + strlen(buf), 1000, entry); + + sprintf(buf + strlen(buf), +"\nBut there is already a record:\n"); + + rec_sprintf(buf + strlen(buf), 1000, rec); + + sprintf(buf + strlen(buf), "\n"); + + ut_a(strlen(buf) < DICT_FOREIGN_ERR_BUF_LEN); + + mutex_exit(&dict_foreign_err_mutex); +} + /******************************************************************* Checks if a unique key violation to rec would occur at the index entry insert. */ @@ -1393,10 +1435,8 @@ row_ins_scan_sec_index_for_duplicate( if (cmp == 0) { if (row_ins_dupl_error_with_rec(rec, entry, index)) { - /* printf("Duplicate key in index %s\n", - index->name); - dtuple_print(entry); */ - + row_ins_unique_report_err(thr, rec, entry, + index); err = DB_DUPLICATE_KEY; thr_get_trx(thr)->error_info = index; @@ -1491,7 +1531,8 @@ row_ins_duplicate_error_in_clust( if (row_ins_dupl_error_with_rec(rec, entry, cursor->index)) { trx->error_info = cursor->index; - + row_ins_unique_report_err(thr, rec, entry, + cursor->index); return(DB_DUPLICATE_KEY); } } @@ -1515,6 +1556,8 @@ row_ins_duplicate_error_in_clust( cursor->index)) { trx->error_info = cursor->index; + row_ins_unique_report_err(thr, rec, entry, + cursor->index); return(DB_DUPLICATE_KEY); } } diff --git a/innobase/row/row0mysql.c b/innobase/row/row0mysql.c index db1119a2abc..428e4d568f3 100644 --- a/innobase/row/row0mysql.c +++ b/innobase/row/row0mysql.c @@ -1664,7 +1664,7 @@ row_drop_table_for_mysql_in_background( the InnoDB data dictionary get out-of-sync if the user runs with innodb_flush_log_at_trx_commit = 0 */ - log_flush_up_to(ut_dulint_max, LOG_WAIT_ONE_GROUP); + log_write_up_to(ut_dulint_max, LOG_WAIT_ONE_GROUP, TRUE); trx_commit_for_mysql(trx); diff --git a/innobase/srv/srv0srv.c b/innobase/srv/srv0srv.c index d90b818ad4b..ef50a4ca261 100644 --- a/innobase/srv/srv0srv.c +++ b/innobase/srv/srv0srv.c @@ -157,6 +157,13 @@ char* srv_file_flush_method_str = NULL; ulint srv_unix_file_flush_method = SRV_UNIX_FDATASYNC; ulint srv_win_file_flush_method = SRV_WIN_IO_UNBUFFERED; +/* The InnoDB main thread tries to keep the ratio of modified pages +in the buffer pool to all database pages in the buffer pool smaller than +the following number. But it is not guaranteed that the value stays below +that during a time of heavy update/insert activity. */ + +ulint srv_max_buf_pool_modified_pct = 90; + /* If the following is != 0 we do not allow inserts etc. This protects the user from forgetting the innodb_force_recovery keyword to my.cnf */ @@ -2359,6 +2366,19 @@ srv_sprintf_innodb_monitor( ut_a(buf < buf_end + 1500); + if (*dict_unique_err_buf != '\0') { + buf += sprintf(buf, +"---------------------------------------------------------------\n" +"LATEST UNIQUE KEY ERROR (is masked in REPLACE or INSERT IGNORE)\n" +"---------------------------------------------------------------\n"); + + if (buf_end - buf > 6000) { + buf+= sprintf(buf, "%.4000s", dict_unique_err_buf); + } + } + + ut_a(buf < buf_end + 1500); + lock_print_info(buf, buf_end); buf = buf + strlen(buf); @@ -2753,6 +2773,7 @@ srv_master_thread( ulint n_ios_old; ulint n_ios_very_old; ulint n_pend_ios; + ibool skip_sleep = FALSE; ulint i; UT_NOT_USED(arg); @@ -2770,21 +2791,42 @@ srv_master_thread( os_event_set(srv_sys->operational); loop: + /*****************************************************************/ + /* ---- When there is database activity by users, we cycle in this + loop */ + srv_main_thread_op_info = (char*) "reserving kernel mutex"; n_ios_very_old = log_sys->n_log_ios + buf_pool->n_pages_read + buf_pool->n_pages_written; mutex_enter(&kernel_mutex); + /* Store the user activity counter at the start of this loop */ old_activity_count = srv_activity_count; mutex_exit(&kernel_mutex); + if (srv_force_recovery >= SRV_FORCE_NO_BACKGROUND) { + + goto suspend_thread; + } + + /* ---- We run the following loop approximately once per second + when there is database activity */ + + skip_sleep = FALSE; + for (i = 0; i < 10; i++) { n_ios_old = log_sys->n_log_ios + buf_pool->n_pages_read + buf_pool->n_pages_written; srv_main_thread_op_info = (char*)"sleeping"; - os_thread_sleep(1000000); + + if (!skip_sleep) { + + os_thread_sleep(1000000); + } + + skip_sleep = FALSE; /* ALTER TABLE in MySQL requires on Unix that the table handler can drop tables lazily after there no longer are SELECT @@ -2797,11 +2839,6 @@ loop: srv_main_thread_op_info = (char*)""; - if (srv_force_recovery >= SRV_FORCE_NO_BACKGROUND) { - - goto suspend_thread; - } - if (srv_fast_shutdown && srv_shutdown_state > 0) { goto background_loop; @@ -2812,10 +2849,9 @@ loop: at transaction commit */ srv_main_thread_op_info = (char*)"flushing log"; - log_flush_up_to(ut_dulint_max, LOG_WAIT_ONE_GROUP); - log_flush_to_disk(); + log_write_up_to(ut_dulint_max, LOG_WAIT_ONE_GROUP, TRUE); - /* If there were less than 10 i/os during the + /* If there were less than 5 i/os during the one second sleep, we assume that there is free disk i/o capacity available, and it makes sense to do an insert buffer merge. */ @@ -2824,30 +2860,45 @@ loop: + log_sys->n_pending_writes; n_ios = log_sys->n_log_ios + buf_pool->n_pages_read + buf_pool->n_pages_written; - if (n_pend_ios < 3 && (n_ios - n_ios_old < 10)) { + if (n_pend_ios < 3 && (n_ios - n_ios_old < 5)) { srv_main_thread_op_info = (char*)"doing insert buffer merge"; ibuf_contract_for_n_pages(TRUE, 5); srv_main_thread_op_info = (char*)"flushing log"; - log_flush_up_to(ut_dulint_max, LOG_WAIT_ONE_GROUP); - log_flush_to_disk(); + log_write_up_to(ut_dulint_max, LOG_WAIT_ONE_GROUP, + TRUE); + } + + if (buf_get_modified_ratio_pct() > + srv_max_buf_pool_modified_pct) { + + /* Try to keep the number of modified pages in the + buffer pool under the limit wished by the user */ + + n_pages_flushed = buf_flush_batch(BUF_FLUSH_LIST, 100, + ut_dulint_max); + + /* If we had to do the flush, it may have taken + even more than 1 second, and also, there may be more + to flush. Do not sleep 1 second during the next + iteration of this loop. */ + + skip_sleep = TRUE; } if (srv_activity_count == old_activity_count) { - if (srv_print_thread_releases) { - printf("Master thread wakes up!\n"); - } + /* There is no user activity at the moment, go to + the background loop */ goto background_loop; } } - if (srv_print_thread_releases) { - printf("Master thread wakes up!\n"); - } + /* ---- We perform the following code approximately once per + 10 seconds when there is database activity */ #ifdef MEM_PERIODIC_CHECK /* Check magic numbers of every allocated mem block once in 10 @@ -2856,7 +2907,7 @@ loop: #endif /* If there were less than 200 i/os during the 10 second period, we assume that there is free disk i/o capacity available, and it - makes sense to do a buffer pool flush. */ + makes sense to flush 100 pages. */ n_pend_ios = buf_get_n_pending_ios() + log_sys->n_pending_writes; n_ios = log_sys->n_log_ios + buf_pool->n_pages_read @@ -2867,8 +2918,7 @@ loop: buf_flush_batch(BUF_FLUSH_LIST, 100, ut_dulint_max); srv_main_thread_op_info = (char*) "flushing log"; - log_flush_up_to(ut_dulint_max, LOG_WAIT_ONE_GROUP); - log_flush_to_disk(); + log_write_up_to(ut_dulint_max, LOG_WAIT_ONE_GROUP, TRUE); } /* We run a batch of insert buffer merge every 10 seconds, @@ -2878,8 +2928,7 @@ loop: ibuf_contract_for_n_pages(TRUE, 5); srv_main_thread_op_info = (char*)"flushing log"; - log_flush_up_to(ut_dulint_max, LOG_WAIT_ONE_GROUP); - log_flush_to_disk(); + log_write_up_to(ut_dulint_max, LOG_WAIT_ONE_GROUP, TRUE); /* We run a full purge every 10 seconds, even if the server were active */ @@ -2903,30 +2952,29 @@ loop: if (difftime(current_time, last_flush_time) > 1) { srv_main_thread_op_info = (char*) "flushing log"; - log_flush_up_to(ut_dulint_max, LOG_WAIT_ONE_GROUP); - log_flush_to_disk(); + log_write_up_to(ut_dulint_max, LOG_WAIT_ONE_GROUP, + TRUE); last_flush_time = current_time; } } - -background_loop: - /* In this loop we run background operations when the server - is quiet and we also come here about once in 10 seconds */ - - srv_main_thread_op_info = (char*)"doing background drop tables"; - - n_tables_to_drop = row_drop_tables_for_mysql_in_background(); - - srv_main_thread_op_info = (char*)""; srv_main_thread_op_info = (char*)"flushing buffer pool pages"; - /* Flush a few oldest pages to make the checkpoint younger */ + /* Flush a few oldest pages to make a new checkpoint younger */ + + if (buf_get_modified_ratio_pct() > 70) { + + /* If there are lots of modified pages in the buffer pool + (> 70 %), we assume we can afford reserving the disk(s) for + the time it requires to flush 100 pages */ - if (srv_fast_shutdown && srv_shutdown_state > 0) { n_pages_flushed = buf_flush_batch(BUF_FLUSH_LIST, 100, ut_dulint_max); } else { + /* Otherwise, we only flush a small number of pages so that + we do not unnecessarily use much disk i/o capacity from + other work */ + n_pages_flushed = buf_flush_batch(BUF_FLUSH_LIST, 10, ut_dulint_max); } @@ -2940,16 +2988,31 @@ background_loop: srv_main_thread_op_info = (char*)"reserving kernel mutex"; mutex_enter(&kernel_mutex); + + /* ---- When there is database activity, we jump from here back to + the start of loop */ + if (srv_activity_count != old_activity_count) { mutex_exit(&kernel_mutex); goto loop; } - old_activity_count = srv_activity_count; + mutex_exit(&kernel_mutex); + /* If the database is quiet, we enter the background loop */ + + /*****************************************************************/ +background_loop: + /* ---- In this loop we run background operations when the server + is quiet from user activity */ + /* The server has been quiet for a while: start running background operations */ + srv_main_thread_op_info = (char*)"doing background drop tables"; + + n_tables_to_drop = row_drop_tables_for_mysql_in_background(); + srv_main_thread_op_info = (char*)"purging"; if (srv_fast_shutdown && srv_shutdown_state > 0) { @@ -2984,6 +3047,7 @@ background_loop: } mutex_exit(&kernel_mutex); +flush_loop: srv_main_thread_op_info = (char*)"flushing buffer pool pages"; n_pages_flushed = buf_flush_batch(BUF_FLUSH_LIST, 100, ut_dulint_max); @@ -3004,6 +3068,14 @@ background_loop: log_checkpoint(TRUE, FALSE); + if (buf_get_modified_ratio_pct() > srv_max_buf_pool_modified_pct) { + + /* Try to keep the number of modified pages in the + buffer pool under the limit wished by the user */ + + goto flush_loop; + } + srv_main_thread_op_info = (char*)"reserving kernel mutex"; mutex_enter(&kernel_mutex); @@ -3018,15 +3090,24 @@ background_loop: log_archive_do(FALSE, &n_bytes_archived); + /* Keep looping in the background loop if still work to do */ + if (srv_fast_shutdown && srv_shutdown_state > 0) { if (n_tables_to_drop + n_pages_flushed + n_bytes_archived != 0) { + /* If we are doing a fast shutdown (= the default) + we do not do purge or insert buffer merge. But we + flush the buffer pool completely to disk. */ + goto background_loop; } } else if (n_tables_to_drop + - n_pages_purged + n_bytes_merged + n_pages_flushed + n_pages_purged + n_bytes_merged + n_pages_flushed + n_bytes_archived != 0) { + /* In a 'slow' shutdown we run purge and the insert buffer + merge to completion */ + goto background_loop; } @@ -3058,6 +3139,9 @@ suspend_thread: os_event_wait(event); + /* When there is user activity, InnoDB will set the event and the main + thread goes back to loop: */ + goto loop; #ifndef __WIN__ diff --git a/innobase/srv/srv0start.c b/innobase/srv/srv0start.c index ec0546f8c66..d47af68d663 100644 --- a/innobase/srv/srv0start.c +++ b/innobase/srv/srv0start.c @@ -161,13 +161,13 @@ srv_parse_data_file_paths_and_sizes( } if (strlen(str) >= ut_strlen(":autoextend") - && 0 == ut_memcmp(str, ":autoextend", + && 0 == ut_memcmp(str, (char*)":autoextend", ut_strlen(":autoextend"))) { str += ut_strlen(":autoextend"); if (strlen(str) >= ut_strlen(":max:") - && 0 == ut_memcmp(str, ":max:", + && 0 == ut_memcmp(str, (char*)":max:", ut_strlen(":max:"))) { str += ut_strlen(":max:"); @@ -265,7 +265,7 @@ srv_parse_data_file_paths_and_sizes( (*data_file_sizes)[i] = size; if (strlen(str) >= ut_strlen(":autoextend") - && 0 == ut_memcmp(str, ":autoextend", + && 0 == ut_memcmp(str, (char*)":autoextend", ut_strlen(":autoextend"))) { *is_auto_extending = TRUE; @@ -273,7 +273,7 @@ srv_parse_data_file_paths_and_sizes( str += ut_strlen(":autoextend"); if (strlen(str) >= ut_strlen(":max:") - && 0 == ut_memcmp(str, ":max:", + && 0 == ut_memcmp(str, (char*)":max:", ut_strlen(":max:"))) { str += ut_strlen(":max:"); @@ -864,6 +864,7 @@ open_or_create_data_files( return(DB_SUCCESS); } +#ifdef notdefined /********************************************************************* This thread is used to measure contention of latches. */ static @@ -935,6 +936,7 @@ test_measure_cont( return(0); } +#endif /******************************************************************** Starts InnoDB and creates a new database if database files @@ -1036,20 +1038,24 @@ innobase_start_or_create_for_mysql(void) srv_win_file_flush_method = SRV_WIN_IO_UNBUFFERED; #ifndef __WIN__ - } else if (0 == ut_strcmp(srv_file_flush_method_str, "fdatasync")) { + } else if (0 == ut_strcmp(srv_file_flush_method_str, + (char*)"fdatasync")) { srv_unix_file_flush_method = SRV_UNIX_FDATASYNC; - } else if (0 == ut_strcmp(srv_file_flush_method_str, "O_DSYNC")) { + } else if (0 == ut_strcmp(srv_file_flush_method_str, + (char*)"O_DSYNC")) { srv_unix_file_flush_method = SRV_UNIX_O_DSYNC; } else if (0 == ut_strcmp(srv_file_flush_method_str, - "littlesync")) { + (char*)"littlesync")) { srv_unix_file_flush_method = SRV_UNIX_LITTLESYNC; - } else if (0 == ut_strcmp(srv_file_flush_method_str, "nosync")) { + } else if (0 == ut_strcmp(srv_file_flush_method_str, + (char*)"nosync")) { srv_unix_file_flush_method = SRV_UNIX_NOSYNC; #else - } else if (0 == ut_strcmp(srv_file_flush_method_str, "normal")) { + } else if (0 == ut_strcmp(srv_file_flush_method_str, + (char*)"normal")) { srv_win_file_flush_method = SRV_WIN_IO_NORMAL; os_aio_use_native_aio = FALSE; @@ -1157,7 +1163,14 @@ innobase_start_or_create_for_mysql(void) &max_flushed_lsn, &max_arch_log_no, &sum_of_new_sizes); if (err != DB_SUCCESS) { - fprintf(stderr, "InnoDB: Could not open data files\n"); + fprintf(stderr, +"InnoDB: Could not open or create data files.\n" +"InnoDB: If you tried to add new data files, and it failed here,\n" +"InnoDB: you should now edit innodb_data_file_path in my.cnf back\n" +"InnoDB: to what it was, and remove the new ibdata files InnoDB created\n" +"InnoDB: in this failed attempt. InnoDB only wrote those files full of\n" +"InnoDB: zeros, but did not yet use them in any way. But be careful: do not\n" +"InnoDB: remove old data files which contain your precious data!\n"); return((int) err); } @@ -1168,7 +1181,10 @@ innobase_start_or_create_for_mysql(void) and restore them from the doublewrite buffer if possible */ - trx_sys_doublewrite_restore_corrupt_pages(); + if (srv_force_recovery < SRV_FORCE_NO_LOG_REDO) { + + trx_sys_doublewrite_restore_corrupt_pages(); + } } srv_normalize_path_for_win(srv_arch_dir); diff --git a/innobase/trx/trx0sys.c b/innobase/trx/trx0sys.c index c403cd447e3..51aad60d3e2 100644 --- a/innobase/trx/trx0sys.c +++ b/innobase/trx/trx0sys.c @@ -340,7 +340,6 @@ trx_sys_doublewrite_restore_corrupt_pages(void) /* It is an unwritten doublewrite buffer page: do nothing */ - } else { /* Read in the actual page from the data files */ @@ -357,9 +356,19 @@ trx_sys_doublewrite_restore_corrupt_pages(void) "InnoDB: Trying to recover it from the doublewrite buffer.\n"); if (buf_page_is_corrupted(page)) { + fprintf(stderr, + "InnoDB: Dump of the page:\n"); + buf_page_print(read_buf); + fprintf(stderr, + "InnoDB: Dump of corresponding page in doublewrite buffer:\n"); + buf_page_print(page); + fprintf(stderr, "InnoDB: Also the page in the doublewrite buffer is corrupt.\n" - "InnoDB: Cannot continue operation.\n"); + "InnoDB: Cannot continue operation.\n" + "InnoDB: You can try to recover the database with the my.cnf\n" + "InnoDB: option:\n" + "InnoDB: set-variable=innodb_force_recovery=6\n"); exit(1); } diff --git a/innobase/trx/trx0trx.c b/innobase/trx/trx0trx.c index 4ce2236f78a..d73d6327d76 100644 --- a/innobase/trx/trx0trx.c +++ b/innobase/trx/trx0trx.c @@ -89,6 +89,8 @@ trx_create( trx->check_foreigns = TRUE; trx->check_unique_secondary = TRUE; + trx->flush_log_later = FALSE; + trx->dict_operation = FALSE; trx->mysql_thd = NULL; @@ -780,13 +782,26 @@ trx_commit_off_kernel( /*-------------------------------------*/ - /* Most MySQL users run with srv_flush_.. set to FALSE: */ + /* Most MySQL users run with srv_flush_.. set to 0: */ - if (srv_flush_log_at_trx_commit) { - - log_flush_up_to(lsn, LOG_WAIT_ONE_GROUP); + if (srv_flush_log_at_trx_commit != 0) { + if (srv_unix_file_flush_method != SRV_UNIX_NOSYNC + && srv_flush_log_at_trx_commit != 2 + && !trx->flush_log_later) { + + /* Write the log to the log files AND flush + them to disk */ + + log_write_up_to(lsn, LOG_WAIT_ONE_GROUP, TRUE); + } else { + /* Write the log but do not flush it to disk */ + + log_write_up_to(lsn, LOG_WAIT_ONE_GROUP, FALSE); + } } + trx->commit_lsn = lsn; + /*-------------------------------------*/ mutex_enter(&kernel_mutex); @@ -1468,6 +1483,33 @@ trx_commit_for_mysql( } /************************************************************************** +If required, flushes the log to disk if we called trx_commit_for_mysql() +with trx->flush_log_later == TRUE. */ + +ulint +trx_commit_complete_for_mysql( +/*==========================*/ + /* out: 0 or error number */ + trx_t* trx) /* in: trx handle */ +{ + ut_a(trx); + + if (srv_flush_log_at_trx_commit == 1 + && srv_unix_file_flush_method != SRV_UNIX_NOSYNC) { + + trx->op_info = (char *) "flushing log"; + + /* Flush the log files to disk */ + + log_write_up_to(trx->commit_lsn, LOG_WAIT_ONE_GROUP, TRUE); + + trx->op_info = (char *) ""; + } + + return(0); +} + +/************************************************************************** Marks the latest SQL statement ended. */ void diff --git a/man/Makefile.am b/man/Makefile.am index 7019d2aa865..9f919e77b8f 100644 --- a/man/Makefile.am +++ b/man/Makefile.am @@ -19,7 +19,7 @@ man_MANS = mysql.1 isamchk.1 isamlog.1 mysql_zap.1 mysqlaccess.1 \ mysqladmin.1 mysqld.1 mysqld_multi.1 mysqldump.1 mysqlshow.1 \ - perror.1 replace.1 mysqld_safe.1 + perror.1 replace.1 mysqld_safe.1 mysql_fix_privilege_tables.1 EXTRA_DIST = $(man_MANS) diff --git a/man/isamchk.1 b/man/isamchk.1 index bfc4ccd9c08..341e968a131 100644 --- a/man/isamchk.1 +++ b/man/isamchk.1 @@ -1,4 +1,4 @@ -.TH isamchk 1 "19 December 2000" "MySQL 3.23" "MySQL database" +.TH isamchk 1 "19 December 2000" "MySQL 4.0" "MySQL database" .SH NAME .BR isamchk \- Description, check and repair of ISAM tables. diff --git a/man/isamlog.1 b/man/isamlog.1 index a386f11c010..402c3052ad8 100644 --- a/man/isamlog.1 +++ b/man/isamlog.1 @@ -1,4 +1,4 @@ -.TH isamlog 1 "19 December 2000" "MySQL 3.23" "MySQL database" +.TH isamlog 1 "19 December 2000" "MySQL 4.0" "MySQL database" .SH NAME isamlog - Write info about whats in a nisam log file. .SH USAGE diff --git a/man/mysql.1 b/man/mysql.1 index 6664581072f..c0c37716b14 100644 --- a/man/mysql.1 +++ b/man/mysql.1 @@ -1,4 +1,4 @@ -.TH mysql 1 "19 December 2000" "MySQL 3.23" "MySQL database" +.TH mysql 1 "19 December 2000" "MySQL 4.0" "MySQL database" .SH NAME mysql \- text-based client for mysqld, a SQL-based relational database daemon .SH SYNOPSIS diff --git a/man/mysql_fix_privilege_tables.1 b/man/mysql_fix_privilege_tables.1 new file mode 100644 index 00000000000..bf4e0e15dfa --- /dev/null +++ b/man/mysql_fix_privilege_tables.1 @@ -0,0 +1,23 @@ +.TH mysql 1 "17 March 2003" "MySQL 4.0" "MySQL database" +.SH NAME +mysql_fix_privilege_tables \- Fixes MySQL privilege tables. +.SH SYNOPSIS +mysql_fix_privilege_tables [options] +.SH DESCRIPTION +This scripts updates the mysql.user, mysql.db, mysql.host and the +mysql.func tables to MySQL 3.22.14 and above. + +This is needed if you want to use the new GRANT functions, +CREATE AGGREGATE FUNCTION or want to use the more secure passwords in 3.23 + +If you get 'Access denied' errors, you should run this script again +and give the MySQL root user password as an argument! + +For more information start the program with '--help'. +.SH "SEE ALSO" +mysql (1), mysqld (1) +.SH AUTHOR +This manpage was written by Christian Hammers <ch@debian.org>. + +MySQL is available at http://www.mysql.com/. +.\" end of man page diff --git a/man/mysql_zap.1 b/man/mysql_zap.1 index 144fc212372..71931c03db6 100644 --- a/man/mysql_zap.1 +++ b/man/mysql_zap.1 @@ -1,4 +1,4 @@ -.TH zap 1 "19 December 2000" "MySQL 3.23" "MySQL database" +.TH zap 1 "19 December 2000" "MySQL 4.0" "MySQL database" .SH NAME zap - a perl script used to kill processes .SH USAGE diff --git a/man/mysqlaccess.1 b/man/mysqlaccess.1 index c1c61d4a8a7..fc7c185e050 100644 --- a/man/mysqlaccess.1 +++ b/man/mysqlaccess.1 @@ -1,4 +1,4 @@ -.TH mysqlaccess 1 "19 December 2000" "MySQL 3.23" "MySQL database" +.TH mysqlaccess 1 "19 December 2000" "MySQL 4.0" "MySQL database" .SH NAME .BR mysqlaccess \- Create new users to mysql. diff --git a/man/mysqladmin.1 b/man/mysqladmin.1 index 9d7d73aad21..693e8aa5a72 100644 --- a/man/mysqladmin.1 +++ b/man/mysqladmin.1 @@ -1,4 +1,4 @@ -.TH mysqladmin 1 "19 December 2000" "MySQL 3.23" "MySQL database" +.TH mysqladmin 1 "19 December 2000" "MySQL 4.0" "MySQL database" .SH NAME mysqladmin [OPTIONS] command command.... \- A utility for performing administrative operations .SH OPTION SYNOPSIS diff --git a/man/mysqld.1 b/man/mysqld.1 index 0a6fcccbef2..98247cd9445 100644 --- a/man/mysqld.1 +++ b/man/mysqld.1 @@ -1,4 +1,4 @@ -.TH mysqld 1 "19 December 2000" "MySQL 3.23" "MySQL database" +.TH mysqld 1 "19 December 2000" "MySQL 4.0" "MySQL database" .SH NAME .BR mysqld \- Starts the MySQL server demon diff --git a/man/mysqld_multi.1 b/man/mysqld_multi.1 index 68b9d1e876f..47418835939 100644 --- a/man/mysqld_multi.1 +++ b/man/mysqld_multi.1 @@ -1,4 +1,4 @@ -.TH mysqld_multi 1 "19 December 2000" "MySQL 3.23" "MySQL database" +.TH mysqld_multi 1 "19 December 2000" "MySQL 4.0" "MySQL database" .SH NAME mysqld_multi - is meant for managing several mysqld processes running in different UNIX sockets and TCP/IP ports. .SH USAGE diff --git a/man/mysqld_safe.1 b/man/mysqld_safe.1 index b8271c848cc..3679346d7db 100644 --- a/man/mysqld_safe.1 +++ b/man/mysqld_safe.1 @@ -1,4 +1,4 @@ -.TH safe_mysqld 1 "19 December 2000" "MySQL 3.23" "MySQL database" +.TH safe_mysqld 1 "19 December 2000" "MySQL 4.0" "MySQL database" .SH NAME mysqld_safe \- start the mysqld daemon on Unix. .SH SYNOPSIS diff --git a/man/mysqldump.1 b/man/mysqldump.1 index b4aba2ade13..af50366f24c 100644 --- a/man/mysqldump.1 +++ b/man/mysqldump.1 @@ -1,4 +1,4 @@ -.TH mysqldump 1 "19 December 2000" "MySQL 3.23" "MySQL database" +.TH mysqldump 1 "19 December 2000" "MySQL 4.0" "MySQL database" .SH NAME mysqldump \- text-based client for dumping or backing up mysql databases , tables and or data. diff --git a/man/mysqlshow.1 b/man/mysqlshow.1 index b6aceec82e3..39590b54375 100644 --- a/man/mysqlshow.1 +++ b/man/mysqlshow.1 @@ -1,4 +1,4 @@ -.TH mysqlshow 1 "19 December 2000" "MySQL 3.23" "MySQL database" +.TH mysqlshow 1 "19 December 2000" "MySQL 4.0" "MySQL database" .SH NAME .BR mysqlshow \- Shows the structure of a mysql database (databases,tables and columns) diff --git a/man/perror.1 b/man/perror.1 index 2c5dd9a295f..b8eec8af318 100644 --- a/man/perror.1 +++ b/man/perror.1 @@ -1,4 +1,4 @@ -.TH perror 1 "19 December 2000" "MySQL 3.23" "MySQL database" +.TH perror 1 "19 December 2000" "MySQL 4.0" "MySQL database" .SH NAME perror \- describes a system or MySQL error code. .SH SYNOPSIS diff --git a/man/replace.1 b/man/replace.1 index 7c3b79f605b..46d542d57f4 100644 --- a/man/replace.1 +++ b/man/replace.1 @@ -1,4 +1,4 @@ -.TH replace 1 "19 December 2000" "MySQL 3.23" "MySQL database" +.TH replace 1 "19 December 2000" "MySQL 4.0" "MySQL database" .SH NAME .TP replace - A utility program that is used by msql2mysql, but that has more general applicability as well. replace changes strings in place in files or on the standard input. Uses a finite state machine to match longer strings first. Can be used to swap strings. diff --git a/myisam/ft_boolean_search.c b/myisam/ft_boolean_search.c index 97c55c1d937..ed6bf1808a9 100644 --- a/myisam/ft_boolean_search.c +++ b/myisam/ft_boolean_search.c @@ -242,10 +242,10 @@ static void _ftb_init_index_search(FT_INFO *ftb) else /* 3 */ { 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); - } + else + reset_tree(& ftb->no_dupes); } } r=_mi_search(info, keyinfo, (uchar*) ftbw->word, ftbw->len, diff --git a/myisam/mi_check.c b/myisam/mi_check.c index b23d4b2277b..92641cce13a 100644 --- a/myisam/mi_check.c +++ b/myisam/mi_check.c @@ -873,15 +873,19 @@ int chk_data_link(MI_CHECK *param, MI_INFO *info,int extend) { if (b_type & BLOCK_LAST) { - mi_check_print_error(param,"Record link to short for record at %s", - llstr(start_recpos,llbuff)); + mi_check_print_error(param, + "Wrong record length %s of %s at %s", + llstr(block_info.rec_len-left_length,llbuff), + llstr(block_info.rec_len, llbuff2), + llstr(start_recpos,llbuff3)); got_error=1; break; } if (info->state->data_file_length < block_info.next_filepos) { - mi_check_print_error(param,"Found next-recordlink that points outside datafile at %s", - llstr(block_info.filepos,llbuff)); + mi_check_print_error(param, + "Found next-recordlink that points outside datafile at %s", + llstr(block_info.filepos,llbuff)); got_error=1; break; } @@ -2246,7 +2250,17 @@ int mi_repair_parallel(MI_CHECK *param, register MI_INFO *info, else rec_length=share->base.pack_reclength; sort_info.max_records= - ((param->testflag & T_CREATE_MISSING_KEYS) ? info->state->records : + /* +1 below is required hack for parallel repair mode. + The info->state->records value, that is compared later + to sort_info.max_records and cannot exceed it, is + increased in sort_key_write. In mi_repair_by_sort, sort_key_write + is called after sort_key_read, where the comparison is performed, + but in parallel mode master thread can call sort_key_write + before some other repair thread calls sort_key_read. + Furthermore I'm not even sure +1 would be enough. + May be sort_info.max_records shold be always set to max value in + parallel mode. */ + ((param->testflag & T_CREATE_MISSING_KEYS) ? info->state->records + 1: (ha_rows) (sort_info.filelength/rec_length+1)); del=info->state->del; diff --git a/myisam/mi_dynrec.c b/myisam/mi_dynrec.c index dc51269ac35..faf86c3ffbd 100644 --- a/myisam/mi_dynrec.c +++ b/myisam/mi_dynrec.c @@ -485,7 +485,7 @@ int _mi_write_part_record(MI_INFO *info, { info->update&= ~HA_STATE_EXTEND_BLOCK; if (my_block_write(&info->rec_cache,(byte*) *record-head_length, - length+extra_length+del_length,filepos)) + length+extra_length+del_length,filepos)) goto err; } else if (my_b_write(&info->rec_cache,(byte*) *record-head_length, @@ -1412,10 +1412,7 @@ uint _mi_get_block_info(MI_BLOCK_INFO *info, File file, my_off_t filepos) VOID(my_seek(file,filepos,MY_SEEK_SET,MYF(0))); if (my_read(file,(char*) header,sizeof(info->header),MYF(0)) != sizeof(info->header)) - { - my_errno=HA_ERR_WRONG_IN_RECORD; - return BLOCK_FATAL_ERROR; - } + goto err; } DBUG_DUMP("header",(byte*) header,MI_BLOCK_INFO_HEADER_LENGTH); if (info->second_read) @@ -1435,10 +1432,7 @@ uint _mi_get_block_info(MI_BLOCK_INFO *info, File file, my_off_t filepos) if ((info->block_len=(uint) mi_uint3korr(header+1)) < MI_MIN_BLOCK_LENGTH || (info->block_len & (MI_DYN_ALIGN_SIZE -1))) - { - my_errno=HA_ERR_WRONG_IN_RECORD; - return BLOCK_ERROR; - } + goto err; info->filepos=filepos; info->next_filepos=mi_sizekorr(header+4); info->prev_filepos=mi_sizekorr(header+12); @@ -1449,7 +1443,7 @@ uint _mi_get_block_info(MI_BLOCK_INFO *info, File file, my_off_t filepos) (mi_uint4korr(header+12) != 0 && (mi_uint4korr(header+12) != (ulong) ~0 || info->prev_filepos != (ulong) ~0))) - return BLOCK_FATAL_ERROR; + goto err; #endif return return_val | BLOCK_DELETED; /* Deleted block */ @@ -1529,8 +1523,9 @@ uint _mi_get_block_info(MI_BLOCK_INFO *info, File file, my_off_t filepos) info->second_read=1; info->filepos=filepos+12; return return_val; - default: - my_errno=HA_ERR_WRONG_IN_RECORD; /* Garbage */ - return BLOCK_ERROR; } + +err: + my_errno=HA_ERR_WRONG_IN_RECORD; /* Garbage */ + return BLOCK_ERROR; } diff --git a/myisam/mi_open.c b/myisam/mi_open.c index 0ccc8a3bf40..99b97db3fbd 100644 --- a/myisam/mi_open.c +++ b/myisam/mi_open.c @@ -114,8 +114,10 @@ MI_INFO *mi_open(const char *name, int mode, uint open_flags) errpos=1; if (my_read(kfile,(char*) share->state.header.file_version,head_length, MYF(MY_NABP))) + { + my_errno= HA_ERR_NOT_A_TABLE; goto err; - + } if (memcmp((byte*) share->state.header.file_version, (byte*) myisam_file_magic, 4)) { @@ -165,7 +167,10 @@ MI_INFO *mi_open(const char *name, int mode, uint open_flags) } errpos=3; if (my_read(kfile,disk_cache,info_length,MYF(MY_NABP))) + { + my_errno=HA_ERR_CRASHED; goto err; + } len=mi_uint2korr(share->state.header.state_info_length); keys= (uint) share->state.header.keys; uniques= (uint) share->state.header.uniques; diff --git a/myisam/myisamchk.c b/myisam/myisamchk.c index ac1d0fbfc4a..a7df01e0169 100644 --- a/myisam/myisamchk.c +++ b/myisam/myisamchk.c @@ -44,6 +44,8 @@ static const char *load_default_groups[]= { "myisamchk", 0 }; static const char *set_charset_name; static CHARSET_INFO *set_charset; static long opt_myisam_block_size; +static const char *my_progname_short; +static int stopwords_inited= 0; static const char *type_names[]= { "?","char","binary", "short", "long", "float", @@ -85,6 +87,7 @@ int main(int argc, char **argv) { int error; MY_INIT(argv[0]); + my_progname_short= my_progname+dirname_length(my_progname); #ifdef __EMX__ _wildcard (&argc, &argv); @@ -330,7 +333,7 @@ static void usage(void) puts("This software comes with NO WARRANTY: see the PUBLIC for details.\n"); puts("Description, check and repair of MyISAM tables."); puts("Used without options all tables on the command will be checked for errors"); - printf("Usage: %s [OPTIONS] tables[.MYI]\n", my_progname); + printf("Usage: %s [OPTIONS] tables[.MYI]\n", my_progname_short); puts("\nGlobal options:\n\ -#, --debug=... Output debug log. Often this is 'd:t:o,filename'\n\ -?, --help Display this help and exit.\n\ @@ -679,7 +682,7 @@ static void get_options(register int *argc,register char ***argv) { VOID(fprintf(stderr, "%s: --unpack can't be used with --quick or --sort-records\n", - my_progname)); + my_progname_short)); exit(1); } if ((check_param.testflag & T_READONLY) && @@ -689,7 +692,7 @@ static void get_options(register int *argc,register char ***argv) { VOID(fprintf(stderr, "%s: Can't use --readonly when repairing or sorting\n", - my_progname)); + my_progname_short)); exit(1); } @@ -859,7 +862,7 @@ static int myisamchk(MI_CHECK *param, my_string filename) } else { - if (share->fulltext_index) + if (share->fulltext_index && !stopwords_inited++) ft_init_stopwords(); if (!(param->testflag & T_READONLY)) @@ -1655,13 +1658,13 @@ void mi_check_print_warning(MI_CHECK *param, const char *fmt,...) if (!param->warning_printed && !param->error_printed) { if (param->testflag & T_SILENT) - fprintf(stderr,"%s: MyISAM file %s\n",my_progname, + fprintf(stderr,"%s: MyISAM file %s\n",my_progname_short, param->isam_file_name); param->out_flag|= O_DATA_LOST; } param->warning_printed=1; va_start(args,fmt); - fprintf(stderr,"%s: warning: ",my_progname); + fprintf(stderr,"%s: warning: ",my_progname_short); VOID(vfprintf(stderr, fmt, args)); VOID(fputc('\n',stderr)); fflush(stderr); @@ -1681,12 +1684,12 @@ void mi_check_print_error(MI_CHECK *param, const char *fmt,...) if (!param->warning_printed && !param->error_printed) { if (param->testflag & T_SILENT) - fprintf(stderr,"%s: MyISAM file %s\n",my_progname,param->isam_file_name); + fprintf(stderr,"%s: MyISAM file %s\n",my_progname_short,param->isam_file_name); param->out_flag|= O_DATA_LOST; } param->error_printed|=1; va_start(args,fmt); - fprintf(stderr,"%s: error: ",my_progname); + fprintf(stderr,"%s: error: ",my_progname_short); VOID(vfprintf(stderr, fmt, args)); VOID(fputc('\n',stderr)); fflush(stderr); diff --git a/mysql-test/mysql-test-run.sh b/mysql-test/mysql-test-run.sh index 65c960d13fa..7ce6ffe14af 100644 --- a/mysql-test/mysql-test-run.sh +++ b/mysql-test/mysql-test-run.sh @@ -110,11 +110,11 @@ GREP=grep if test $? != 0; then exit 1; fi PRINTF=printf RM=rm -TIME=`which time` if test $? != 0; then exit 1; fi TR=tr XARGS=`which xargs` if test $? != 0; then exit 1; fi +SORT=sort # Are we using a source or a binary distribution? @@ -409,7 +409,7 @@ fi if test ${COLUMNS:-0} -lt 80 ; then COLUMNS=80 ; fi E=`$EXPR $COLUMNS - 8` -DASH72=`$ECHO '------------------------------------------------------------------------'|$CUT -c 1-$E` +DASH72=`$ECHO '------------------------------------------'|$CUT -c 1-$E` # on source dist, we pick up freshly build executables # on binary, use what is installed @@ -588,9 +588,8 @@ skip_test() { USERT=" ...." SYST=" ...." REALT=" ...." - timestr="$USERT $SYST $REALT" pname=`$ECHO "$1 "|$CUT -c 1-24` - RES="$pname $timestr" + RES="$pname" skip_inc $ECHO "$RES$RES_SPACE [ skipped ]" } @@ -1217,31 +1216,15 @@ run_testcase () $RM -f r/$tname.*reject mysql_test_args="-R r/$tname.result $EXTRA_MYSQL_TEST_OPT" if [ -z "$DO_CLIENT_GDB" ] ; then - mytime=`$TIME -p $MYSQL_TEST $mysql_test_args < $tf 2> $TIMEFILE` + `$MYSQL_TEST $mysql_test_args < $tf 2> $TIMEFILE`; else do_gdb_test "$mysql_test_args" "$tf" fi res=$? - if [ $res = 0 ]; then - mytime=`$CAT $TIMEFILE | $TAIL -3 | $TR '\n' ':'` - - USERT=`$ECHO $mytime | $CUT -d : -f 2 | $CUT -d ' ' -f 2` - USERT=`prefix_to_8 $USERT` - SYST=`$ECHO $mytime | $CUT -d : -f 3 | $CUT -d ' ' -f 2` - SYST=`prefix_to_8 $SYST` - REALT=`$ECHO $mytime | $CUT -d : -f 1 | $CUT -d ' ' -f 2` - REALT=`prefix_to_8 $REALT` - else - USERT=" ...." - SYST=" ...." - REALT=" ...." - fi - - timestr="$USERT $SYST $REALT" pname=`$ECHO "$tname "|$CUT -c 1-24` - RES="$pname $timestr" + RES="$pname" if [ x$many_slaves = x1 ] ; then stop_slave 1 @@ -1373,7 +1356,7 @@ then fi $ECHO -$ECHO " TEST USER SYSTEM ELAPSED RESULT" +$ECHO " TEST RESULT" $ECHO $DASH72 if [ -z "$1" ] ; @@ -1381,7 +1364,7 @@ then if [ x$RECORD = x1 ]; then $ECHO "Will not run in record mode without a specific test case." else - for tf in $TESTDIR/*.$TESTSUFFIX + for tf in `ls -1 $TESTDIR/*.$TESTSUFFIX | $SORT` do run_testcase $tf done diff --git a/mysql-test/r/ansi.result b/mysql-test/r/ansi.result new file mode 100644 index 00000000000..f9f96310b73 --- /dev/null +++ b/mysql-test/r/ansi.result @@ -0,0 +1,10 @@ +drop table if exists t1; +SELECT 'A' || 'B'; +'A' || 'B' +AB +CREATE TABLE t1 (id INT, id2 int); +SELECT id,NULL,1,1.1,'a' FROM t1 GROUP BY id; +id NULL 1 1.1 a +SELECT id FROM t1 GROUP BY id2; +'t1.id' isn't in GROUP BY +drop table t1; diff --git a/mysql-test/r/create.result b/mysql-test/r/create.result index a2ab0e97905..2e2aa41c671 100644 --- a/mysql-test/r/create.result +++ b/mysql-test/r/create.result @@ -25,13 +25,9 @@ create table t1 (a int not null auto_increment,primary key (a)) type=heap; The used table type doesn't support AUTO_INCREMENT columns create table t1 (a int not null,b text) type=heap; The used table type doesn't support BLOB/TEXT columns -create table t1 (a int ,primary key(a)) type=heap; -All parts of a PRIMARY KEY must be NOT NULL; If you need NULL in a key, use UNIQUE instead drop table if exists t1; create table t1 (ordid int(8) not null auto_increment, ord varchar(50) not null, primary key (ord,ordid)) type=heap; The used table type doesn't support AUTO_INCREMENT columns -create table t1 (ordid int(8), primary key (ordid)); -All parts of a PRIMARY KEY must be NOT NULL; If you need NULL in a key, use UNIQUE instead create table not_existing_database.test (a int); Got one of the listed errors create table `a/a` (a int); @@ -171,3 +167,12 @@ t1 CREATE TABLE `t1` ( ) TYPE=MyISAM SET SESSION table_type=default; drop table t1; +create table t1 ( k1 varchar(2), k2 int, primary key(k1,k2)); +insert into t1 values ("a", 1), ("b", 2); +insert into t1 values ("c", NULL); +Column 'k2' cannot be null +insert into t1 values (NULL, 3); +Column 'k1' cannot be null +insert into t1 values (NULL, NULL); +Column 'k1' cannot be null +drop table t1; diff --git a/mysql-test/r/delete.result b/mysql-test/r/delete.result index 582ab894233..abc8245e69f 100644 --- a/mysql-test/r/delete.result +++ b/mysql-test/r/delete.result @@ -38,4 +38,15 @@ NULL d 7 delete from t1 where misc > 5 and bool is null; select * from t1 where misc > 5 and bool is null; bool not_null misc +select count(*) from t1; +count(*) +2 +delete from t1 where 1 > 2; +select count(*) from t1; +count(*) +2 +delete from t1 where 3 > 2; +select count(*) from t1; +count(*) +0 drop table t1; diff --git a/mysql-test/r/func_misc.result b/mysql-test/r/func_misc.result index 4eed80c4cc9..8d05adcc1ba 100644 --- a/mysql-test/r/func_misc.result +++ b/mysql-test/r/func_misc.result @@ -10,3 +10,6 @@ inet_aton("255.255.255.255.255") inet_aton("255.255.1.255") inet_aton("0.1.255") select inet_ntoa(1099511627775),inet_ntoa(4294902271),inet_ntoa(511); inet_ntoa(1099511627775) inet_ntoa(4294902271) inet_ntoa(511) NULL 255.255.1.255 0.0.1.255 +select length(format('nan', 2)) > 0; +length(format('nan', 2)) > 0 +1 diff --git a/mysql-test/r/having.result b/mysql-test/r/having.result index f113eb6ed49..d643070f7f9 100644 --- a/mysql-test/r/having.result +++ b/mysql-test/r/having.result @@ -69,4 +69,12 @@ select id, sum(qty) as sqty from t1 group by id having sqty>2; id sqty 1 5 2 9 +select sum(qty) as sqty from t1 group by id having count(id) > 0; +sqty +5 +9 +select sum(qty) as sqty from t1 group by id having count(distinct id) > 0; +sqty +5 +9 drop table t1; diff --git a/mysql-test/r/innodb.result b/mysql-test/r/innodb.result index c98f85f93d2..e2dea324ff2 100644 --- a/mysql-test/r/innodb.result +++ b/mysql-test/r/innodb.result @@ -138,15 +138,6 @@ id parent_id level 1008 102 2 1010 102 2 1015 102 2 -explain select level from t1 where level=1; -table type possible_keys key key_len ref rows Extra -t1 ref level level 1 const 12 Using where; Using index -explain select level,id from t1 where level=1; -table type possible_keys key key_len ref rows Extra -t1 ref level level 1 const 12 Using where; Using index -explain select level,id,parent_id from t1 where level=1; -table type possible_keys key key_len ref rows Extra -t1 ref level level 1 const 12 Using where select level,id from t1 where level=1; level id 1 1002 @@ -595,9 +586,6 @@ id parent_id level 1009 102 2 1025 102 2 1016 102 2 -explain select level from t1 where level=1; -table type possible_keys key key_len ref rows Extra -t1 ref level level 1 const 6 Using where; Using index select level,id from t1 where level=1; level id 1 1004 @@ -1139,7 +1127,7 @@ a b drop table t1; CREATE TABLE t1 (a int not null primary key, b int not null, key (b)) type=innodb; CREATE TABLE t2 (a int not null primary key, b int not null, key (b)) type=innodb; -INSERT INTO t1 values (1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8),(9,9); +INSERT INTO t1 values (1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8),(9,9),(10,10),(11,11),(12,12); INSERT INTO t2 values (1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8),(9,9); update t1,t2 set t1.a=t1.a+100; select * from t1; @@ -1153,6 +1141,9 @@ a b 107 7 108 8 109 9 +110 10 +111 11 +112 12 update t1,t2 set t1.a=t1.a+100 where t1.a=101; select * from t1; a b @@ -1165,6 +1156,9 @@ a b 107 7 108 8 109 9 +110 10 +111 11 +112 12 update t1,t2 set t1.b=t1.b+10 where t1.b=2; select * from t1; a b @@ -1176,8 +1170,11 @@ a b 107 7 108 8 109 9 +110 10 +111 11 102 12 -update t1,t2 set t1.b=t1.b+2,t2.b=t1.b where t1.b between 3 and 5; +112 12 +update t1,t2 set t1.b=t1.b+2,t2.b=t1.b+10 where t1.b between 3 and 5 and t1.a=t2.a+100; select * from t1; a b 201 1 @@ -1188,18 +1185,21 @@ a b 107 7 108 8 109 9 +110 10 +111 11 102 12 +112 12 select * from t2; a b -1 5 -2 5 -3 5 -4 5 -5 5 -6 5 -7 5 -8 5 -9 5 +1 1 +2 2 +6 6 +7 7 +8 8 +9 9 +3 13 +4 14 +5 15 drop table t1,t2; CREATE TABLE t2 ( NEXT_T BIGINT NOT NULL PRIMARY KEY) TYPE=MyISAM; CREATE TABLE t1 ( B_ID INTEGER NOT NULL PRIMARY KEY) TYPE=InnoDB; diff --git a/mysql-test/r/key.result b/mysql-test/r/key.result index 1cd9c9dfe79..31d35a681aa 100644 --- a/mysql-test/r/key.result +++ b/mysql-test/r/key.result @@ -42,7 +42,6 @@ price area type transityes shopsyes schoolsyes petsyes drop table t1; CREATE TABLE t1 (program enum('signup','unique','sliding') not null, type enum('basic','sliding','signup'), sites set('mt'), PRIMARY KEY (program)); ALTER TABLE t1 modify program enum('signup','unique','sliding'); -All parts of a PRIMARY KEY must be NOT NULL; If you need NULL in a key, use UNIQUE instead drop table t1; CREATE TABLE t1 ( name varchar(50) DEFAULT '' NOT NULL, diff --git a/mysql-test/r/loaddata.result b/mysql-test/r/loaddata.result new file mode 100644 index 00000000000..d121a4e6c40 --- /dev/null +++ b/mysql-test/r/loaddata.result @@ -0,0 +1,11 @@ +drop table if exists t1; +create table t1 (a date, b date, c date not null, d date); +load data infile '../../std_data/loaddata1.dat' into table t1 fields terminated by ','; +load data infile '../../std_data/loaddata1.dat' into table t1 fields terminated by ',' IGNORE 2 LINES; +SELECT * from t1; +a b c d +0000-00-00 NULL 0000-00-00 0000-00-00 +0000-00-00 0000-00-00 0000-00-00 0000-00-00 +2003-03-03 2003-03-03 2003-03-03 NULL +2003-03-03 2003-03-03 2003-03-03 NULL +drop table t1; diff --git a/mysql-test/r/raid.result b/mysql-test/r/raid.result index 41af50851e9..fd47a9451f6 100644 --- a/mysql-test/r/raid.result +++ b/mysql-test/r/raid.result @@ -1,3 +1,6 @@ +create database test_raid; +create table test_raid.r1 (i int) raid_type=1; +drop database test_raid; DROP TABLE IF EXISTS t1,t2; CREATE TABLE t1 ( id int unsigned not null auto_increment primary key, diff --git a/mysql-test/r/repair.result b/mysql-test/r/repair.result index adc09ded0e2..6c2107b2cf3 100644 --- a/mysql-test/r/repair.result +++ b/mysql-test/r/repair.result @@ -9,3 +9,7 @@ repair table t1 use_frm; Table Op Msg_type Msg_text test.t1 repair error The handler for the table doesn't support repair drop table t1; +repair table t1 use_frm; +Table Op Msg_type Msg_text +test.t1 repair error Table 'test.t1' doesn't exist +create table t1 type=myisam SELECT 1,"table 1"; diff --git a/mysql-test/r/repair_part2.result b/mysql-test/r/repair_part2.result new file mode 100644 index 00000000000..77aa98c3da9 --- /dev/null +++ b/mysql-test/r/repair_part2.result @@ -0,0 +1,8 @@ +repair table t1; +Table Op Msg_type Msg_text +test.t1 repair error Can't open file: 't1.MYI'. (errno: 130) +repair table t1 use_frm; +Table Op Msg_type Msg_text +test.t1 repair warning Number of rows changed from 0 to 1 +test.t1 repair status OK +drop table t1; diff --git a/mysql-test/r/rpl_loaddata.result b/mysql-test/r/rpl_loaddata.result index c1518e8e29a..62071a07d0c 100644 --- a/mysql-test/r/rpl_loaddata.result +++ b/mysql-test/r/rpl_loaddata.result @@ -7,7 +7,7 @@ slave start; create table t1(a int not null auto_increment, b int, primary key(a) ); load data infile '../../std_data/rpl_loaddata.dat' into table t1; create temporary table t2 (day date,id int(9),category enum('a','b','c'),name varchar(60)); -load data infile '../../std_data/rpl_loaddata2.dat' into table t2 fields terminated by ',' optionally enclosed by '%' escaped by '@' lines terminated by '\n##\n' ignore 1 lines; +load data infile '../../std_data/rpl_loaddata2.dat' into table t2 fields terminated by ',' optionally enclosed by '%' escaped by '@' lines terminated by '\n##\n' starting by '>' ignore 1 lines; create table t3 (day date,id int(9),category enum('a','b','c'),name varchar(60)); insert into t3 select * from t2; select * from t1; diff --git a/mysql-test/r/type_decimal.result b/mysql-test/r/type_decimal.result index 4c326957c03..b74765696a2 100644 --- a/mysql-test/r/type_decimal.result +++ b/mysql-test/r/type_decimal.result @@ -357,3 +357,9 @@ select * from t1; a 99999999999 drop table t1; +CREATE TABLE t1 (a_dec DECIMAL(-1,0)); +Too big column length for column 'a_dec' (max = 255). Use BLOB instead +CREATE TABLE t1 (a_dec DECIMAL(-2,1)); +Too big column length for column 'a_dec' (max = 255). Use BLOB instead +CREATE TABLE t1 (a_dec DECIMAL(-1,1)); +Too big column length for column 'a_dec' (max = 255). Use BLOB instead diff --git a/mysql-test/r/union.result b/mysql-test/r/union.result index 2af9d5a3584..970e83c6752 100644 --- a/mysql-test/r/union.result +++ b/mysql-test/r/union.result @@ -1,4 +1,4 @@ -drop table if exists t1,t2,t3; +drop table if exists t1,t2,t3,t4,t5,t6; CREATE TABLE t1 (a int not null, b char (10) not null); insert into t1 values(1,'a'),(2,'b'),(3,'c'),(3,'c'); CREATE TABLE t2 (a int not null, b char (10) not null); @@ -202,3 +202,60 @@ a 11 set SQL_SELECT_LIMIT=DEFAULT; drop table t1,t2; +CREATE TABLE t1 ( +cid smallint(5) unsigned NOT NULL default '0', +cv varchar(250) NOT NULL default '', +PRIMARY KEY (cid), +UNIQUE KEY cv (cv) +) ; +INSERT INTO t1 VALUES (8,'dummy'); +CREATE TABLE t2 ( +cid bigint(20) unsigned NOT NULL auto_increment, +cap varchar(255) NOT NULL default '', +PRIMARY KEY (cid), +KEY cap (cap) +) ; +CREATE TABLE t3 ( +gid bigint(20) unsigned NOT NULL auto_increment, +gn varchar(255) NOT NULL default '', +must tinyint(4) default NULL, +PRIMARY KEY (gid), +KEY gn (gn) +) ; +INSERT INTO t3 VALUES (1,'V1',NULL); +CREATE TABLE t4 ( +uid bigint(20) unsigned NOT NULL default '0', +gid bigint(20) unsigned default NULL, +rid bigint(20) unsigned default NULL, +cid bigint(20) unsigned default NULL, +UNIQUE KEY m (uid,gid,rid,cid), +KEY uid (uid), +KEY rid (rid), +KEY cid (cid), +KEY container (gid,rid,cid) +) ; +INSERT INTO t4 VALUES (1,1,NULL,NULL); +CREATE TABLE t5 ( +rid bigint(20) unsigned NOT NULL auto_increment, +rl varchar(255) NOT NULL default '', +PRIMARY KEY (rid), +KEY rl (rl) +) ; +CREATE TABLE t6 ( +uid bigint(20) unsigned NOT NULL auto_increment, +un varchar(250) NOT NULL default '', +uc smallint(5) unsigned NOT NULL default '0', +PRIMARY KEY (uid), +UNIQUE KEY nc (un,uc), +KEY un (un) +) ; +INSERT INTO t6 VALUES (1,'test',8); +SELECT t4.uid, t5.rl, t3.gn as g1, t4.cid, t4.gid as gg FROM t3, t6, t1, t4 left join t5 on t5.rid = t4.rid left join t2 on t2.cid = t4.cid WHERE t3.gid=t4.gid AND t6.uid = t4.uid AND t6.uc = t1.cid AND t1.cv = "dummy" AND t6.un = "test"; +uid rl g1 cid gg +1 NULL V1 NULL 1 +SELECT t4.uid, t5.rl, t3.gn as g1, t4.cid, t4.gid as gg FROM t3, t6, t1, t4 left join t5 on t5.rid = t4.rid left join t2 on t2.cid = t4.cid WHERE t3.gid=t4.gid AND t6.uid = t4.uid AND t3.must IS NOT NULL AND t6.uc = t1.cid AND t1.cv = "dummy" AND t6.un = "test"; +uid rl g1 cid gg +(SELECT t4.uid, t5.rl, t3.gn as g1, t4.cid, t4.gid as gg FROM t3, t6, t1, t4 left join t5 on t5.rid = t4.rid left join t2 on t2.cid = t4.cid WHERE t3.gid=t4.gid AND t6.uid = t4.uid AND t3.must IS NOT NULL AND t6.uc = t1.cid AND t1.cv = "dummy" AND t6.un = "test") UNION (SELECT t4.uid, t5.rl, t3.gn as g1, t4.cid, t4.gid as gg FROM t3, t6, t1, t4 left join t5 on t5.rid = t4.rid left join t2 on t2.cid = t4.cid WHERE t3.gid=t4.gid AND t6.uid = t4.uid AND t6.uc = t1.cid AND t1.cv = "dummy" AND t6.un = "test"); +uid rl g1 cid gg +1 NULL V1 NULL 1 +drop table t1,t2,t3,t4,t5,t6; diff --git a/mysql-test/r/update.result b/mysql-test/r/update.result index 86647e845a9..11aff8fe50a 100644 --- a/mysql-test/r/update.result +++ b/mysql-test/r/update.result @@ -138,3 +138,31 @@ a b 111 100 111 100 drop table t1; +CREATE TABLE t1 ( +`id_param` smallint(3) unsigned NOT NULL default '0', +`nom_option` char(40) NOT NULL default '', +`valid` tinyint(1) NOT NULL default '0', +KEY `id_param` (`id_param`,`nom_option`) +) TYPE=MyISAM; +INSERT INTO t1 (id_param,nom_option,valid) VALUES (185,'600x1200',1); +UPDATE t1 SET nom_option='test' WHERE id_param=185 AND nom_option='600x1200' AND valid=1 LIMIT 1; +select * from t1; +id_param nom_option valid +185 test 1 +drop table t1; +create table t1 (F1 VARCHAR(30), F2 VARCHAR(30), F3 VARCHAR(30), cnt int, groupid int, KEY groupid_index (groupid)); +insert into t1 (F1,F2,F3,cnt,groupid) values ('0','0','0',1,6), +('0','1','2',1,5), ('0','2','0',1,3), ('1','0','1',1,2), +('1','2','1',1,1), ('1','2','2',1,1), ('2','0','1',2,4), +('2','2','0',1,7); +delete from t1 using t1 m1,t1 m2 where m1.groupid=m2.groupid and (m1.cnt < m2.cnt or m1.cnt=m2.cnt and m1.F3>m2.F3); +select * from t1; +F1 F2 F3 cnt groupid +0 0 0 1 6 +0 1 2 1 5 +0 2 0 1 3 +1 0 1 1 2 +1 2 1 1 1 +2 0 1 2 4 +2 2 0 1 7 +drop table t1; diff --git a/mysql-test/r/variables.result b/mysql-test/r/variables.result index 1cc48d2aeac..7c054f55acd 100644 --- a/mysql-test/r/variables.result +++ b/mysql-test/r/variables.result @@ -108,17 +108,14 @@ show global variables like 'table_type'; Variable_name Value table_type INNODB set GLOBAL query_cache_size=100000; -set myisam_max_sort_file_size=10000, GLOBAL myisam_max_sort_file_size=20000; -show variables like 'myisam_max_sort_file_size'; -Variable_name Value -myisam_max_sort_file_size 10000 +set GLOBAL myisam_max_sort_file_size=2000000; show global variables like 'myisam_max_sort_file_size'; Variable_name Value -myisam_max_sort_file_size 20000 -set myisam_max_sort_file_size=default; +myisam_max_sort_file_size 1048576 +set GLOBAL myisam_max_sort_file_size=default; show variables like 'myisam_max_sort_file_size'; Variable_name Value -myisam_max_sort_file_size 20000 +myisam_max_sort_file_size FILE_SIZE set global net_retry_count=10, session net_retry_count=10; set global net_buffer_length=1024, net_write_timeout=200, net_read_timeout=300; set session net_buffer_length=2048, net_write_timeout=500, net_read_timeout=600; @@ -211,6 +208,10 @@ set @@global.sql_auto_is_null=1; Variable 'sql_auto_is_null' is a LOCAL variable and can't be used with SET GLOBAL select @@global.sql_auto_is_null; Variable 'sql_auto_is_null' is a LOCAL variable and can't be used with SET GLOBAL +set myisam_max_sort_file_size=100; +Variable 'myisam_max_sort_file_size' is a GLOBAL variable and should be set with SET GLOBAL +set myisam_max_extra_sort_file_size=100; +Variable 'myisam_max_extra_sort_file_size' is a GLOBAL variable and should be set with SET GLOBAL set autocommit=1; set big_tables=1; select @@autocommit, @@big_tables; @@ -264,11 +265,11 @@ select @@max_user_connections; @@max_user_connections 100 set global max_write_lock_count=100; -set myisam_max_extra_sort_file_size=100; +set global myisam_max_extra_sort_file_size=100; select @@myisam_max_extra_sort_file_size; @@myisam_max_extra_sort_file_size 100 -set myisam_max_sort_file_size=100; +set global myisam_max_sort_file_size=100; set myisam_sort_buffer_size=100; set net_buffer_length=100; set net_read_timeout=100; diff --git a/mysql-test/std_data/loaddata1.dat b/mysql-test/std_data/loaddata1.dat new file mode 100644 index 00000000000..c9e8549b211 --- /dev/null +++ b/mysql-test/std_data/loaddata1.dat @@ -0,0 +1,3 @@ +,\N,NULL,, +00,0,000000,, +2003-03-03, 20030303,030303,\N diff --git a/mysql-test/std_data/rpl_loaddata2.dat b/mysql-test/std_data/rpl_loaddata2.dat index 7a3d4ea7695..b883d9dcd58 100644 --- a/mysql-test/std_data/rpl_loaddata2.dat +++ b/mysql-test/std_data/rpl_loaddata2.dat @@ -1,8 +1,8 @@ -2003-01-21,6328,%a%,%aaaaa% +>2003-01-21,6328,%a%,%aaaaa% ## -2003-02-22,2461,b,%a a a @@ @% @b ' " a% +>2003-02-22,2461,b,%a a a @@ @% @b ' " a% ## -2003-03-22,2161,%c%,%asdf% +>2003-03-22,2161,%c%,%asdf% ## -2003-04-22,2416,%a%,%bbbbb% +>2003-04-22,2416,%a%,%bbbbb% ## diff --git a/mysql-test/t/ansi-master.opt b/mysql-test/t/ansi-master.opt new file mode 100644 index 00000000000..6bf7a4f30e2 --- /dev/null +++ b/mysql-test/t/ansi-master.opt @@ -0,0 +1 @@ +--ansi diff --git a/mysql-test/t/ansi.test b/mysql-test/t/ansi.test new file mode 100644 index 00000000000..e1ac8ffd4f9 --- /dev/null +++ b/mysql-test/t/ansi.test @@ -0,0 +1,17 @@ +# +# Test of ansi mode +# + +drop table if exists t1; + +# Test some functions that works different in ansi mode + +SELECT 'A' || 'B'; + +# Test GROUP BY behaviour + +CREATE TABLE t1 (id INT, id2 int); +SELECT id,NULL,1,1.1,'a' FROM t1 GROUP BY id; +--error 1055 +SELECT id FROM t1 GROUP BY id2; +drop table t1; diff --git a/mysql-test/t/create.test b/mysql-test/t/create.test index 68d68929f07..86c3f6be0f5 100644 --- a/mysql-test/t/create.test +++ b/mysql-test/t/create.test @@ -24,11 +24,9 @@ drop table if exists t1,t2; !$1167 create table t1 (b char(0) not null, index(b)); !$1164 create table t1 (a int not null auto_increment,primary key (a)) type=heap; !$1163 create table t1 (a int not null,b text) type=heap; -!$1171 create table t1 (a int ,primary key(a)) type=heap; drop table if exists t1; !$1164 create table t1 (ordid int(8) not null auto_increment, ord varchar(50) not null, primary key (ord,ordid)) type=heap; -!$1171 create table t1 (ordid int(8), primary key (ordid)); -- error 1044,1 create table not_existing_database.test (a int); @@ -119,3 +117,13 @@ show create table t1; SET SESSION table_type=default; drop table t1; + +# +# ISO requires that primary keys are implicitly NOT NULL +# +create table t1 ( k1 varchar(2), k2 int, primary key(k1,k2)); +insert into t1 values ("a", 1), ("b", 2); +!$1048 insert into t1 values ("c", NULL); +!$1048 insert into t1 values (NULL, 3); +!$1048 insert into t1 values (NULL, NULL); +drop table t1; diff --git a/mysql-test/t/delete.test b/mysql-test/t/delete.test index 13fa617b3cf..904d959d148 100644 --- a/mysql-test/t/delete.test +++ b/mysql-test/t/delete.test @@ -53,5 +53,11 @@ select * from t1 where misc > 5 and bool is null; delete from t1 where misc > 5 and bool is null; select * from t1 where misc > 5 and bool is null; +select count(*) from t1; +delete from t1 where 1 > 2; +select count(*) from t1; +delete from t1 where 3 > 2; +select count(*) from t1; + drop table t1; diff --git a/mysql-test/t/func_misc.test b/mysql-test/t/func_misc.test index d48b17e87af..be64c170fa1 100644 --- a/mysql-test/t/func_misc.test +++ b/mysql-test/t/func_misc.test @@ -7,3 +7,8 @@ select format(1.5555,0),format(123.5555,1),format(1234.5555,2),format(12345.5555 select inet_ntoa(inet_aton("255.255.255.255.255.255.255.255")); select inet_aton("255.255.255.255.255"),inet_aton("255.255.1.255"),inet_aton("0.1.255"); select inet_ntoa(1099511627775),inet_ntoa(4294902271),inet_ntoa(511); + +# +# Test for core dump with nan +# +select length(format('nan', 2)) > 0; diff --git a/mysql-test/t/having.test b/mysql-test/t/having.test index fd972fea1ad..cb6fa85ffde 100644 --- a/mysql-test/t/having.test +++ b/mysql-test/t/having.test @@ -63,4 +63,6 @@ drop table t1; create table t1 (id int not null, qty int not null); insert into t1 values (1,2),(1,3),(2,4),(2,5); select id, sum(qty) as sqty from t1 group by id having sqty>2; +select sum(qty) as sqty from t1 group by id having count(id) > 0; +select sum(qty) as sqty from t1 group by id having count(distinct id) > 0; drop table t1; diff --git a/mysql-test/t/innodb.test b/mysql-test/t/innodb.test index d65b9b1638e..dc3c76f1a91 100644 --- a/mysql-test/t/innodb.test +++ b/mysql-test/t/innodb.test @@ -40,9 +40,9 @@ update ignore t1 set id=id+1; # This will change all rows select * from t1; update ignore t1 set id=1023 where id=1010; select * from t1 where parent_id=102; -explain select level from t1 where level=1; -explain select level,id from t1 where level=1; -explain select level,id,parent_id from t1 where level=1; +# explain select level from t1 where level=1; +# explain select level,id from t1 where level=1; +# explain select level,id,parent_id from t1 where level=1; select level,id from t1 where level=1; select level,id,parent_id from t1 where level=1; optimize table t1; @@ -339,7 +339,7 @@ update ignore t1 set id=id+1; # This will change all rows select * from t1; update ignore t1 set id=1023 where id=1010; select * from t1 where parent_id=102; -explain select level from t1 where level=1; +# explain select level from t1 where level=1; select level,id from t1 where level=1; select level,id,parent_id from t1 where level=1; select level,id from t1 where level=1 order by id; @@ -776,7 +776,7 @@ drop table t1; CREATE TABLE t1 (a int not null primary key, b int not null, key (b)) type=innodb; CREATE TABLE t2 (a int not null primary key, b int not null, key (b)) type=innodb; -INSERT INTO t1 values (1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8),(9,9); +INSERT INTO t1 values (1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8),(9,9),(10,10),(11,11),(12,12); INSERT INTO t2 values (1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8),(9,9); # Full join, without key @@ -792,7 +792,7 @@ update t1,t2 set t1.b=t1.b+10 where t1.b=2; select * from t1; # Range key (in t1) -update t1,t2 set t1.b=t1.b+2,t2.b=t1.b where t1.b between 3 and 5; +update t1,t2 set t1.b=t1.b+2,t2.b=t1.b+10 where t1.b between 3 and 5 and t1.a=t2.a+100; select * from t1; select * from t2; diff --git a/mysql-test/t/key.test b/mysql-test/t/key.test index 1996c793880..cb80d88aee8 100644 --- a/mysql-test/t/key.test +++ b/mysql-test/t/key.test @@ -54,12 +54,12 @@ INSERT INTO t1 VALUES (900,'Vancouver','Shared/Roomate','Y','Y','Y','Y'); drop table t1; # -# problem med primary key +# No longer a problem with primary key # CREATE TABLE t1 (program enum('signup','unique','sliding') not null, type enum('basic','sliding','signup'), sites set('mt'), PRIMARY KEY (program)); -# The following should give an error for wrong primary key -!$1171 ALTER TABLE t1 modify program enum('signup','unique','sliding'); +# This no longer give an error for wrong primary key +ALTER TABLE t1 modify program enum('signup','unique','sliding'); drop table t1; # diff --git a/mysql-test/t/loaddata.test b/mysql-test/t/loaddata.test new file mode 100644 index 00000000000..ceb5c47af11 --- /dev/null +++ b/mysql-test/t/loaddata.test @@ -0,0 +1,11 @@ +# +# Some simple test of load data +# + +drop table if exists t1; + +create table t1 (a date, b date, c date not null, d date); +load data infile '../../std_data/loaddata1.dat' into table t1 fields terminated by ','; +load data infile '../../std_data/loaddata1.dat' into table t1 fields terminated by ',' IGNORE 2 LINES; +SELECT * from t1; +drop table t1; diff --git a/mysql-test/t/raid.test b/mysql-test/t/raid.test index 8b608c1069f..4dbaf84a836 100644 --- a/mysql-test/t/raid.test +++ b/mysql-test/t/raid.test @@ -5,6 +5,9 @@ show variables like "have_raid"; # Test of raided tables # +create database test_raid; +create table test_raid.r1 (i int) raid_type=1; +drop database test_raid; DROP TABLE IF EXISTS t1,t2; CREATE TABLE t1 ( id int unsigned not null auto_increment primary key, diff --git a/mysql-test/t/repair.test b/mysql-test/t/repair.test index b901fb3467f..159fc090653 100644 --- a/mysql-test/t/repair.test +++ b/mysql-test/t/repair.test @@ -8,3 +8,12 @@ repair table t1 use_frm; alter table t1 TYPE=HEAP; repair table t1 use_frm; drop table t1; + +# non-existent table +repair table t1 use_frm; + +# +# Create test table for repair2 +# The following must be last in this file + +create table t1 type=myisam SELECT 1,"table 1"; diff --git a/mysql-test/t/repair_part2-master.sh b/mysql-test/t/repair_part2-master.sh new file mode 100644 index 00000000000..964bde06c18 --- /dev/null +++ b/mysql-test/t/repair_part2-master.sh @@ -0,0 +1 @@ +echo "1" > $MYSQL_TEST_DIR/var/master-data/test/t1.MYI diff --git a/mysql-test/t/repair_part2.test b/mysql-test/t/repair_part2.test new file mode 100644 index 00000000000..8c27e382dff --- /dev/null +++ b/mysql-test/t/repair_part2.test @@ -0,0 +1,7 @@ +# +# This test starts with a crashed t1.MYI file left over from repair.test +# + +repair table t1; +repair table t1 use_frm; +drop table t1; diff --git a/mysql-test/t/rpl_loaddata.test b/mysql-test/t/rpl_loaddata.test index 2acb67dfce2..1f34aa9d3f9 100644 --- a/mysql-test/t/rpl_loaddata.test +++ b/mysql-test/t/rpl_loaddata.test @@ -10,8 +10,7 @@ create table t1(a int not null auto_increment, b int, primary key(a) ); load data infile '../../std_data/rpl_loaddata.dat' into table t1; create temporary table t2 (day date,id int(9),category enum('a','b','c'),name varchar(60)); -#load data infile '../../std_data/rpl_loaddata2.dat' into table t2 fields terminated by ',' optionaly enclosed by '%' escaped by '@' lines terminated by '\n%%\n' ignore 1 lines; - load data infile '../../std_data/rpl_loaddata2.dat' into table t2 fields terminated by ',' optionally enclosed by '%' escaped by '@' lines terminated by '\n##\n' ignore 1 lines; +load data infile '../../std_data/rpl_loaddata2.dat' into table t2 fields terminated by ',' optionally enclosed by '%' escaped by '@' lines terminated by '\n##\n' starting by '>' ignore 1 lines; create table t3 (day date,id int(9),category enum('a','b','c'),name varchar(60)); insert into t3 select * from t2; diff --git a/mysql-test/t/type_decimal.test b/mysql-test/t/type_decimal.test index 7d5d719592c..7aedc051905 100644 --- a/mysql-test/t/type_decimal.test +++ b/mysql-test/t/type_decimal.test @@ -230,3 +230,13 @@ insert into t1 values("1e4294967297"); select * from t1; drop table t1; +# +# Test of wrong decimal type +# + +--error 1074 +CREATE TABLE t1 (a_dec DECIMAL(-1,0)); +--error 1074 +CREATE TABLE t1 (a_dec DECIMAL(-2,1)); +--error 1074 +CREATE TABLE t1 (a_dec DECIMAL(-1,1)); diff --git a/mysql-test/t/union.test b/mysql-test/t/union.test index 6e6b43ebe3b..7262b8f78d7 100644 --- a/mysql-test/t/union.test +++ b/mysql-test/t/union.test @@ -2,7 +2,7 @@ # Test of unions # -drop table if exists t1,t2,t3; +drop table if exists t1,t2,t3,t4,t5,t6; CREATE TABLE t1 (a int not null, b char (10) not null); insert into t1 values(1,'a'),(2,'b'),(3,'c'),(3,'c'); CREATE TABLE t2 (a int not null, b char (10) not null); @@ -98,3 +98,62 @@ set SQL_SELECT_LIMIT=2; (select * from t1 limit 1) union (select * from t2 limit 3); set SQL_SELECT_LIMIT=DEFAULT; drop table t1,t2; + +# +# Test error with left join +# + +CREATE TABLE t1 ( + cid smallint(5) unsigned NOT NULL default '0', + cv varchar(250) NOT NULL default '', + PRIMARY KEY (cid), + UNIQUE KEY cv (cv) +) ; +INSERT INTO t1 VALUES (8,'dummy'); +CREATE TABLE t2 ( + cid bigint(20) unsigned NOT NULL auto_increment, + cap varchar(255) NOT NULL default '', + PRIMARY KEY (cid), + KEY cap (cap) +) ; +CREATE TABLE t3 ( + gid bigint(20) unsigned NOT NULL auto_increment, + gn varchar(255) NOT NULL default '', + must tinyint(4) default NULL, + PRIMARY KEY (gid), + KEY gn (gn) +) ; +INSERT INTO t3 VALUES (1,'V1',NULL); +CREATE TABLE t4 ( + uid bigint(20) unsigned NOT NULL default '0', + gid bigint(20) unsigned default NULL, + rid bigint(20) unsigned default NULL, + cid bigint(20) unsigned default NULL, + UNIQUE KEY m (uid,gid,rid,cid), + KEY uid (uid), + KEY rid (rid), + KEY cid (cid), + KEY container (gid,rid,cid) +) ; +INSERT INTO t4 VALUES (1,1,NULL,NULL); +CREATE TABLE t5 ( + rid bigint(20) unsigned NOT NULL auto_increment, + rl varchar(255) NOT NULL default '', + PRIMARY KEY (rid), + KEY rl (rl) +) ; +CREATE TABLE t6 ( + uid bigint(20) unsigned NOT NULL auto_increment, + un varchar(250) NOT NULL default '', + uc smallint(5) unsigned NOT NULL default '0', + PRIMARY KEY (uid), + UNIQUE KEY nc (un,uc), + KEY un (un) +) ; +INSERT INTO t6 VALUES (1,'test',8); + +SELECT t4.uid, t5.rl, t3.gn as g1, t4.cid, t4.gid as gg FROM t3, t6, t1, t4 left join t5 on t5.rid = t4.rid left join t2 on t2.cid = t4.cid WHERE t3.gid=t4.gid AND t6.uid = t4.uid AND t6.uc = t1.cid AND t1.cv = "dummy" AND t6.un = "test"; +SELECT t4.uid, t5.rl, t3.gn as g1, t4.cid, t4.gid as gg FROM t3, t6, t1, t4 left join t5 on t5.rid = t4.rid left join t2 on t2.cid = t4.cid WHERE t3.gid=t4.gid AND t6.uid = t4.uid AND t3.must IS NOT NULL AND t6.uc = t1.cid AND t1.cv = "dummy" AND t6.un = "test"; +(SELECT t4.uid, t5.rl, t3.gn as g1, t4.cid, t4.gid as gg FROM t3, t6, t1, t4 left join t5 on t5.rid = t4.rid left join t2 on t2.cid = t4.cid WHERE t3.gid=t4.gid AND t6.uid = t4.uid AND t3.must IS NOT NULL AND t6.uc = t1.cid AND t1.cv = "dummy" AND t6.un = "test") UNION (SELECT t4.uid, t5.rl, t3.gn as g1, t4.cid, t4.gid as gg FROM t3, t6, t1, t4 left join t5 on t5.rid = t4.rid left join t2 on t2.cid = t4.cid WHERE t3.gid=t4.gid AND t6.uid = t4.uid AND t6.uc = t1.cid AND t1.cv = "dummy" AND t6.un = "test"); + +drop table t1,t2,t3,t4,t5,t6; diff --git a/mysql-test/t/update.test b/mysql-test/t/update.test index 359ae815197..a455b308158 100644 --- a/mysql-test/t/update.test +++ b/mysql-test/t/update.test @@ -91,3 +91,35 @@ update t1 set b=100 where a=1 order by b desc limit 2; update t1 set a=a+10+b where a=1 order by b; select * from t1 order by a,b; drop table t1; + +# +# Test with limit (Bug #393) +# + +CREATE TABLE t1 ( + `id_param` smallint(3) unsigned NOT NULL default '0', + `nom_option` char(40) NOT NULL default '', + `valid` tinyint(1) NOT NULL default '0', + KEY `id_param` (`id_param`,`nom_option`) + ) TYPE=MyISAM; + +INSERT INTO t1 (id_param,nom_option,valid) VALUES (185,'600x1200',1); + +UPDATE t1 SET nom_option='test' WHERE id_param=185 AND nom_option='600x1200' AND valid=1 LIMIT 1; +select * from t1; +drop table t1; + +# +# Multi table update test from bugs +# + +create table t1 (F1 VARCHAR(30), F2 VARCHAR(30), F3 VARCHAR(30), cnt int, groupid int, KEY groupid_index (groupid)); + +insert into t1 (F1,F2,F3,cnt,groupid) values ('0','0','0',1,6), +('0','1','2',1,5), ('0','2','0',1,3), ('1','0','1',1,2), +('1','2','1',1,1), ('1','2','2',1,1), ('2','0','1',2,4), +('2','2','0',1,7); + +delete from t1 using t1 m1,t1 m2 where m1.groupid=m2.groupid and (m1.cnt < m2.cnt or m1.cnt=m2.cnt and m1.F3>m2.F3); +select * from t1; +drop table t1; diff --git a/mysql-test/t/variables.test b/mysql-test/t/variables.test index 639a28edd38..fa39906fd6a 100644 --- a/mysql-test/t/variables.test +++ b/mysql-test/t/variables.test @@ -69,10 +69,10 @@ show local variables like 'table_type'; show global variables like 'table_type'; set GLOBAL query_cache_size=100000; -set myisam_max_sort_file_size=10000, GLOBAL myisam_max_sort_file_size=20000; -show variables like 'myisam_max_sort_file_size'; +set GLOBAL myisam_max_sort_file_size=2000000; show global variables like 'myisam_max_sort_file_size'; -set myisam_max_sort_file_size=default; +set GLOBAL myisam_max_sort_file_size=default; +--replace_result 2147483647 FILE_SIZE 9223372036854775807 FILE_SIZE show variables like 'myisam_max_sort_file_size'; set global net_retry_count=10, session net_retry_count=10; @@ -137,6 +137,10 @@ set @@concurrent_insert=1; set @@global.sql_auto_is_null=1; --error 1228 select @@global.sql_auto_is_null; +--error 1229 +set myisam_max_sort_file_size=100; +--error 1229 +set myisam_max_extra_sort_file_size=100; # Test setting all variables @@ -181,9 +185,9 @@ set max_tmp_tables=100; set global max_user_connections=100; select @@max_user_connections; set global max_write_lock_count=100; -set myisam_max_extra_sort_file_size=100; +set global myisam_max_extra_sort_file_size=100; select @@myisam_max_extra_sort_file_size; -set myisam_max_sort_file_size=100; +set global myisam_max_sort_file_size=100; set myisam_sort_buffer_size=100; set net_buffer_length=100; set net_read_timeout=100; diff --git a/mysys/my_bit.c b/mysys/my_bit.c index 0ff487afe03..55dd72f5f76 100644 --- a/mysys/my_bit.c +++ b/mysys/my_bit.c @@ -29,3 +29,45 @@ uint my_bit_log2(ulong value) for (bit=0 ; value > 1 ; value>>=1, bit++) ; return bit; } + +static char nbits[256] = { + 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, +}; + +uint my_count_bits(ulonglong v) +{ +#if SIZEOF_LONG_LONG > 4 + /* The following code is a bit faster on 16 bit machines than if we would + only shift v */ + ulong v2=(ulong) (v >> 32); + return (uint) (uchar) (nbits[(uchar) v] + + nbits[(uchar) (v >> 8)] + + nbits[(uchar) (v >> 16)] + + nbits[(uchar) (v >> 24)] + + nbits[(uchar) (v2)] + + nbits[(uchar) (v2 >> 8)] + + nbits[(uchar) (v2 >> 16)] + + nbits[(uchar) (v2 >> 24)]); +#else + return (uint) (uchar) (nbits[(uchar) v] + + nbits[(uchar) (v >> 8)] + + nbits[(uchar) (v >> 16)] + + nbits[(uchar) (v >> 24)]); +#endif +} + diff --git a/mysys/my_seek.c b/mysys/my_seek.c index 177a5cee953..ec24a26b3d9 100644 --- a/mysys/my_seek.c +++ b/mysys/my_seek.c @@ -15,6 +15,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "mysys_priv.h" +#include <assert.h> /* Seek to position in file */ /*ARGSUSED*/ @@ -27,6 +28,8 @@ my_off_t my_seek(File fd, my_off_t pos, int whence, DBUG_PRINT("my",("Fd: %d Hpos: %lu Pos: %lu Whence: %d MyFlags: %d", fd, (ulong) (((ulonglong) pos) >> 32), (ulong) pos, whence, MyFlags)); + DBUG_ASSERT(pos != MY_FILEPOS_ERROR); /* safety check */ + newpos=lseek(fd, pos, whence); if (newpos == (os_off_t) -1) { diff --git a/mysys/raid.cc b/mysys/raid.cc index d6359dc0f93..0b688464fb3 100644 --- a/mysys/raid.cc +++ b/mysys/raid.cc @@ -157,10 +157,10 @@ extern "C" { DBUG_PRINT("enter",("Fd: %d pos: %lu whence: %d MyFlags: %d", fd, (ulong) pos, whence, MyFlags)); - assert(pos != MY_FILEPOS_ERROR); - if (is_raid(fd)) { + assert(pos != MY_FILEPOS_ERROR); + RaidFd *raid= (*dynamic_element(&RaidFd::_raid_map,fd,RaidFd**)); DBUG_RETURN(raid->Seek(pos,whence,MyFlags)); } diff --git a/scripts/mysql_install_db.sh b/scripts/mysql_install_db.sh index 2f27f5d7c1a..64fdd0dfebb 100644 --- a/scripts/mysql_install_db.sh +++ b/scripts/mysql_install_db.sh @@ -118,7 +118,8 @@ then resolved=`$bindir/resolveip localhost 2>&1` if [ $? -eq 0 ] then - echo "Sorry, the host '$hostname' could not be looked up." + echo "Neither host '$hostname' and 'localhost' could not be looked up with" + echo "$bindir/resolveip" echo "Please configure the 'hostname' command to return a correct hostname." echo "If you want to solve this at a later stage, restart this script with" echo "the --force option" @@ -134,15 +135,12 @@ then fi # Create database directories mysql & test -if test "$IN_RPM" -eq 0 -then if test ! -d $ldata; then mkdir $ldata; chmod 700 $ldata ; fi if test ! -d $ldata/mysql; then mkdir $ldata/mysql; chmod 700 $ldata/mysql ; fi if test ! -d $ldata/test; then mkdir $ldata/test; chmod 700 $ldata/test ; fi if test -w / -a ! -z "$user"; then chown $user $ldata $ldata/mysql $ldata/test; fi -fi # Initialize variables c_d="" i_d="" diff --git a/scripts/mysqlhotcopy.sh b/scripts/mysqlhotcopy.sh index ec76aa479f3..a89a8919752 100644 --- a/scripts/mysqlhotcopy.sh +++ b/scripts/mysqlhotcopy.sh @@ -55,7 +55,8 @@ Usage: $0 db_name[./table_regex/] [new_db_name | directory] -P, --port=# port to use when connecting to local server with TCP/IP -S, --socket=# socket to use when connecting to local server - --allowold don\'t abort if target already exists (rename it _old) + --allowold don\'t abort if target dir already exists (rename it _old) + --addtodest don\'t rename target dir if it exists, just add files to it --keepold don\'t delete previous (now renamed) target when done --noindices don\'t include full index files in copy --method=# method for copy (only "cp" currently supported) @@ -98,6 +99,7 @@ GetOptions( \%opt, "socket|S=s", "allowold!", "keepold!", + "addtodest!", "noindices!", "method=s", "debug", @@ -380,14 +382,14 @@ if ($opt{method} =~ /^cp\b/) push @existing, $rdb->{target} if ( -d $rdb->{target} ); } - if ( @existing && !$opt{allowold} ) + if ( @existing && !($opt{allowold} || $opt{addtodest}) ) { $dbh->disconnect(); - die "Can't hotcopy to '", join( "','", @existing ), "' because directory\nalready exist and the --allowold option was not given.\n" + die "Can't hotcopy to '", join( "','", @existing ), "' because directory\nalready exist and the --allowold or --addtodest options were not given.\n" } } -retire_directory( @existing ) if ( @existing ); +retire_directory( @existing ) if @existing && !$opt{addtodest}; foreach my $rdb ( @db_desc ) { foreach my $td ( '', @{$rdb->{raid_dirs}} ) { @@ -403,8 +405,8 @@ foreach my $rdb ( @db_desc ) { ## ... } else { - mkdir($tgt_dirpath, 0750) - or die "Can't create '$tgt_dirpath': $!\n"; + mkdir($tgt_dirpath, 0750) or die "Can't create '$tgt_dirpath': $!\n" + unless -d $tgt_dirpath; } } } @@ -861,6 +863,22 @@ Any existing versions of the backup directory are deleted. Behaves as for the --allowold, with the additional feature of keeping the backup directory after the copy successfully completes. +=item --addtodest + +Don't rename target directory if it already exists, just add the +copied files into it. + +This is most useful when backing up a database with many large +tables and you don't want to have all the tables locked for the +whole duration. + +In this situation, I<if> you are happy for groups of tables to be +backed up separately (and thus possibly not be logically consistant +with one another) then you can run mysqlhotcopy several times on +the same database each with different db_name./table_regex/. +All but the first should use the --addtodest option so the tables +all end up in the same directory. + =item --flushlog Rotate the log files by executing "FLUSH LOGS" after all tables are @@ -869,13 +887,13 @@ locked, and before they are copied. =item --resetmaster Reset the bin-log by executing "RESET MASTER" after all tables are -locked, and before they are copied. Usefull if you are recovering a +locked, and before they are copied. Useful if you are recovering a slave in a replication setup. =item --resetslave Reset the master.info by executing "RESET SLAVE" after all tables are -locked, and before they are copied. Usefull if you are recovering a +locked, and before they are copied. Useful if you are recovering a server in a mutual replication setup. =item --regexp pattern @@ -941,7 +959,7 @@ will vary with your ability to understand how scp works. 'man scp' and 'man ssh' are your friends. The destination directory _must exist_ on the target machine using the -scp method. --keepold and --allowold are meeningless with scp. +scp method. --keepold and --allowold are meaningless with scp. Liberal use of the --debug option will help you figure out what\'s really going on when you do an scp. diff --git a/sql/field.h b/sql/field.h index 842bbb89988..e63802d8c00 100644 --- a/sql/field.h +++ b/sql/field.h @@ -122,6 +122,8 @@ public: Field *tmp= (Field*) memdup_root(root,(char*) this,size_of()); if (tmp) { + if (tmp->table->maybe_null) + tmp->flags&= ~NOT_NULL_FLAG; tmp->table=new_table; tmp->key_start=tmp->part_of_key=tmp->part_of_sortkey=0; tmp->unireg_check=Field::NONE; diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc index 73654536083..50bb4275eaa 100644 --- a/sql/ha_innodb.cc +++ b/sql/ha_innodb.cc @@ -872,8 +872,7 @@ innobase_flush_logs(void) DBUG_ENTER("innobase_flush_logs"); - log_flush_up_to(ut_dulint_max, LOG_WAIT_ONE_GROUP); - log_flush_to_disk(); + log_write_up_to(ut_dulint_max, LOG_WAIT_ONE_GROUP, TRUE); DBUG_RETURN(result); } @@ -920,7 +919,7 @@ Commits a transaction in an InnoDB database. */ int innobase_commit( /*============*/ - /* out: 0 or error number */ + /* out: 0 */ THD* thd, /* in: MySQL thread handle of the user for whom the transaction should be committed */ void* trx_handle)/* in: InnoDB trx handle or @@ -928,7 +927,6 @@ innobase_commit( that the current SQL statement ended, and we should mark the start of a new statement with a savepoint */ { - int error = 0; trx_t* trx; DBUG_ENTER("innobase_commit"); @@ -955,29 +953,27 @@ innobase_commit( innobase_release_stat_resources(trx); trx_mark_sql_stat_end(trx); -#ifndef DBUG_OFF - if (error) { - DBUG_PRINT("error", ("error: %d", error)); - } -#endif /* Tell InnoDB server that there might be work for utility threads: */ srv_active_wake_master_thread(); - DBUG_RETURN(error); + DBUG_RETURN(0); } /********************************************************************* This is called when MySQL writes the binlog entry for the current transaction. Writes to the InnoDB tablespace info which tells where the MySQL binlog entry for the current transaction ended. Also commits the -transaction inside InnoDB. */ +transaction inside InnoDB but does NOT flush InnoDB log files to disk. +To flush you have to call innobase_flush_log_to_disk. We have separated +flushing to eliminate the bottleneck of LOCK_log in log.cc which disabled +InnoDB's group commit capability. */ int innobase_report_binlog_offset_and_commit( /*=====================================*/ - /* out: 0 or error code */ + /* out: 0 */ THD* thd, /* in: user thread */ void* trx_handle, /* in: InnoDB trx handle */ char* log_file_name, /* in: latest binlog file name */ @@ -993,7 +989,39 @@ innobase_report_binlog_offset_and_commit( trx->mysql_log_file_name = log_file_name; trx->mysql_log_offset = (ib_longlong)end_offset; - return(innobase_commit(thd, trx_handle)); + trx->flush_log_later = TRUE; + + innobase_commit(thd, trx_handle); + + trx->flush_log_later = FALSE; + + return(0); +} + +/********************************************************************* +This is called after MySQL has written the binlog entry for the current +transaction. Flushes the InnoDB log files to disk if required. */ + +int +innobase_commit_complete( +/*=====================*/ + /* out: 0 */ + void* trx_handle) /* in: InnoDB trx handle */ +{ + trx_t* trx; + + if (srv_flush_log_at_trx_commit == 0) { + + return(0); + } + + trx = (trx_t*)trx_handle; + + ut_a(trx != NULL); + + trx_commit_complete_for_mysql(trx); + + return(0); } /********************************************************************* @@ -3202,7 +3230,7 @@ ha_innobase::create( the InnoDB data dictionary get out-of-sync if the user runs with innodb_flush_log_at_trx_commit = 0 */ - log_flush_up_to(ut_dulint_max, LOG_WAIT_ONE_GROUP); + log_write_up_to(ut_dulint_max, LOG_WAIT_ONE_GROUP, TRUE); innobase_table = dict_table_get(norm_name, NULL); @@ -3277,7 +3305,7 @@ ha_innobase::delete_table( the InnoDB data dictionary get out-of-sync if the user runs with innodb_flush_log_at_trx_commit = 0 */ - log_flush_up_to(ut_dulint_max, LOG_WAIT_ONE_GROUP); + log_write_up_to(ut_dulint_max, LOG_WAIT_ONE_GROUP, TRUE); /* Tell the InnoDB server that there might be work for utility threads: */ @@ -3347,7 +3375,7 @@ innobase_drop_database( the InnoDB data dictionary get out-of-sync if the user runs with innodb_flush_log_at_trx_commit = 0 */ - log_flush_up_to(ut_dulint_max, LOG_WAIT_ONE_GROUP); + log_write_up_to(ut_dulint_max, LOG_WAIT_ONE_GROUP, TRUE); /* Tell the InnoDB server that there might be work for utility threads: */ @@ -3419,7 +3447,7 @@ ha_innobase::rename_table( the InnoDB data dictionary get out-of-sync if the user runs with innodb_flush_log_at_trx_commit = 0 */ - log_flush_up_to(ut_dulint_max, LOG_WAIT_ONE_GROUP); + log_write_up_to(ut_dulint_max, LOG_WAIT_ONE_GROUP, TRUE); /* Tell the InnoDB server that there might be work for utility threads: */ @@ -3936,7 +3964,7 @@ ha_innobase::extra( case HA_EXTRA_RESET: case HA_EXTRA_RESET_STATE: prebuilt->read_just_key = 0; - break; + break; case HA_EXTRA_NO_KEYREAD: prebuilt->read_just_key = 0; break; diff --git a/sql/ha_innodb.h b/sql/ha_innodb.h index 5677d22a2ca..1a9b1b16c64 100644 --- a/sql/ha_innodb.h +++ b/sql/ha_innodb.h @@ -197,6 +197,9 @@ extern char *innobase_unix_file_flush_method; /* The following variables have to be my_bool for SHOW VARIABLES to work */ extern my_bool innobase_log_archive, innobase_use_native_aio, innobase_fast_shutdown; +extern "C" { +extern ulong srv_max_buf_pool_modified_pct; +} extern TYPELIB innobase_lock_typelib; @@ -211,6 +214,8 @@ int innobase_report_binlog_offset_and_commit( void* trx_handle, char* log_file_name, my_off_t end_offset); +int innobase_commit_complete( + void* trx_handle); int innobase_rollback(THD *thd, void* trx_handle); int innobase_close_connection(THD *thd); int innobase_drop_database(char *path); diff --git a/sql/ha_myisam.cc b/sql/ha_myisam.cc index aef9c09833a..202cd90fd88 100644 --- a/sql/ha_myisam.cc +++ b/sql/ha_myisam.cc @@ -579,10 +579,24 @@ int ha_myisam::repair(THD *thd, MI_CHECK ¶m, bool optimize) { local_testflag|= T_STATISTICS; param.testflag|= T_STATISTICS; // We get this for free - thd->proc_info="Repair by sorting"; statistics_done=1; - error = mi_repair_by_sort(¶m, file, fixed_name, - param.testflag & T_QUICK); + if (current_thd->variables.myisam_repair_threads>1) + { + char buf[40]; + /* TODO: respect myisam_repair_threads variable */ + my_snprintf(buf, 40, "Repair with %d threads", my_count_bits(key_map)); + thd->proc_info=buf; + error = mi_repair_parallel(¶m, file, fixed_name, + param.testflag & T_QUICK); + thd->proc_info="Repair done"; // to reset proc_info, as + // it was pointing to local buffer + } + else + { + thd->proc_info="Repair by sorting"; + error = mi_repair_by_sort(¶m, file, fixed_name, + param.testflag & T_QUICK); + } } else { diff --git a/sql/handler.cc b/sql/handler.cc index 6e3f8486b45..45c83355c94 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -243,6 +243,9 @@ int ha_autocommit_or_rollback(THD *thd, int error) replication. This function also calls the commit of the table handler, because the order of transactions in the log of the table handler must be the same as in the binlog. + NOTE that to eliminate the bottleneck of the group commit, we do not + flush the handler log files here, but only later in a call of + ha_commit_complete(). arguments: thd: the thread handle of the current connection @@ -269,13 +272,38 @@ int ha_report_binlog_offset_and_commit(THD *thd, my_error(ER_ERROR_DURING_COMMIT, MYF(0), error); error=1; } - trans->innodb_active_trans=0; } #endif return error; } /* + Flushes the handler log files (if my.cnf settings do not free us from it) + after we have called ha_report_binlog_offset_and_commit(). To eliminate + the bottleneck from the group commit, this should be called when + LOCK_log has been released in log.cc. + + arguments: + thd: the thread handle of the current connection + return value: always 0 +*/ + +int ha_commit_complete(THD *thd) +{ +#ifdef HAVE_INNOBASE_DB + THD_TRANS *trans; + trans = &thd->transaction.all; + if (trans->innobase_tid) + { + innobase_commit_complete(trans->innobase_tid); + + trans->innodb_active_trans=0; + } +#endif + return 0; +} + +/* This function should be called when MySQL sends rows of a SELECT result set or the EOF mark to the client. It releases a possible adaptive hash index S-latch held by thd in InnoDB and also releases a possible InnoDB query @@ -802,7 +830,8 @@ void handler::print_error(int error, myf errflag) DBUG_VOID_RETURN; } - /* Return key if error because of duplicated keys */ + +/* Return key if error because of duplicated keys */ uint handler::get_dup_key(int error) { @@ -813,6 +842,7 @@ uint handler::get_dup_key(int error) DBUG_RETURN(table->file->errkey); } + int handler::delete_table(const char *name) { int error=0; @@ -839,9 +869,10 @@ int handler::rename_table(const char * from, const char * to) DBUG_RETURN(0); } -/* Tell the handler to turn on or off logging to the handler's - recovery log +/* + Tell the handler to turn on or off logging to the handler's recovery log */ + int ha_recovery_logging(THD *thd, bool on) { int error=0; @@ -886,7 +917,6 @@ int handler::delete_all_rows() int ha_create_table(const char *name, HA_CREATE_INFO *create_info, bool update_create_info) - { int error; TABLE table; diff --git a/sql/handler.h b/sql/handler.h index 72a05d7ebee..fbad36bffdd 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -372,6 +372,7 @@ void ha_resize_key_cache(void); int ha_start_stmt(THD *thd); int ha_report_binlog_offset_and_commit(THD *thd, char *log_file_name, my_off_t end_offset); +int ha_commit_complete(THD *thd); int ha_release_temporary_latches(THD *thd); int ha_commit_trans(THD *thd, THD_TRANS *trans); int ha_rollback_trans(THD *thd, THD_TRANS *trans); diff --git a/sql/init.cc b/sql/init.cc index df06ddd41ef..7d90cc564a1 100644 --- a/sql/init.cc +++ b/sql/init.cc @@ -38,13 +38,11 @@ void unireg_init(ulong options) init_my_atof(); /* use our atof */ #endif my_abort_hook=unireg_abort; /* Abort with close of databases */ - f_fyllchar=' '; /* Input fill char */ VOID(strmov(reg_ext,".frm")); for (i=0 ; i < 6 ; i++) // YYMMDDHHMMSS dayord.pos[i]=i; specialflag=SPECIAL_SAME_DB_NAME; - blob_newline='^'; /* Convert newline in blobs to this */ /* Make a tab of powers of 10 */ for (i=0,nr=1.0; i < array_elements(log_10) ; i++) { /* It's used by filesort... */ diff --git a/sql/item_func.cc b/sql/item_func.cc index 6c23b0aa424..532a7cedec0 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -1110,47 +1110,6 @@ longlong Item_func_find_in_set::val_int() return 0; } -static char nbits[256] = { - 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, - 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, - 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, - 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, - 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, - 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, - 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, - 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, - 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, - 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, -}; - -uint count_bits(ulonglong v) -{ -#if SIZEOF_LONG_LONG > 4 - /* The following code is a bit faster on 16 bit machines than if we would - only shift v */ - ulong v2=(ulong) (v >> 32); - return (uint) (uchar) (nbits[(uchar) v] + - nbits[(uchar) (v >> 8)] + - nbits[(uchar) (v >> 16)] + - nbits[(uchar) (v >> 24)] + - nbits[(uchar) (v2)] + - nbits[(uchar) (v2 >> 8)] + - nbits[(uchar) (v2 >> 16)] + - nbits[(uchar) (v2 >> 24)]); -#else - return (uint) (uchar) (nbits[(uchar) v] + - nbits[(uchar) (v >> 8)] + - nbits[(uchar) (v >> 16)] + - nbits[(uchar) (v >> 24)]); -#endif -} - longlong Item_func_bit_count::val_int() { ulonglong value= (ulonglong) args[0]->val_int(); @@ -1159,7 +1118,7 @@ longlong Item_func_bit_count::val_int() null_value=1; /* purecov: inspected */ return 0; /* purecov: inspected */ } - return (longlong) count_bits(value); + return (longlong) my_count_bits(value); } @@ -2221,7 +2180,7 @@ bool Item_func_match::fix_fields(THD *thd,struct st_table_list *tlist) used_tables_cache|=item->used_tables(); } /* check that all columns come from the same table */ - if (count_bits(used_tables_cache) != 1) + if (my_count_bits(used_tables_cache) != 1) key=NO_SUCH_KEY; const_item_cache=0; table=((Item_field *)fields.head())->field->table; diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index 9876b77e8cb..ae8bf1dfecb 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -1473,15 +1473,17 @@ String *Item_func_format::val_str(String *str) str_length=str->length(); if (nr < 0) str_length--; // Don't count sign - length=str->length()+(diff=(str_length- dec-1)/3); - if (diff && diff < 330) // size of buff ... + + /* We need this test to handle 'nan' values */ + if (str_length >= dec+4) { char *tmp,*pos; - str=copy_if_not_alloced(&tmp_str,str,length); + length= str->length()+(diff=(str_length- dec-1)/3); + str= copy_if_not_alloced(&tmp_str,str,length); str->length(length); - tmp=(char*) str->ptr()+length - dec-1; - for (pos=(char*) str->ptr()+length ; pos != tmp; pos--) - pos[0]=pos[- (int) diff]; + tmp= (char*) str->ptr()+length - dec-1; + for (pos= (char*) str->ptr()+length ; pos != tmp; pos--) + pos[0]= pos[-(int) diff]; while (diff) { pos[0]=pos[-(int) diff]; pos--; diff --git a/sql/log.cc b/sql/log.cc index 8a5aba5cd34..79ee59eedf8 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -1033,6 +1033,8 @@ bool MYSQL_LOG::write(THD *thd,enum enum_server_command command, bool MYSQL_LOG::write(Log_event* event_info) { + THD *thd=event_info->thd; + bool called_handler_commit=0; bool error=0; DBUG_ENTER("MYSQL_LOG::write(event)"); @@ -1047,7 +1049,6 @@ bool MYSQL_LOG::write(Log_event* event_info) if (is_open()) { bool should_rotate = 0; - THD *thd=event_info->thd; const char *local_db = event_info->get_db(); #ifdef USING_TRANSACTIONS IO_CACHE *file = ((event_info->get_cache_stmt()) ? @@ -1132,23 +1133,26 @@ bool MYSQL_LOG::write(Log_event* event_info) was a MyISAM event! */ - if (file == &log_file && opt_using_transactions - && !my_b_tell(&thd->transaction.trans_log)) + if (file == &log_file) // we are writing to the real log (disk) { - /* - LOAD DATA INFILE in AUTOCOMMIT=1 mode writes to the binlog - chunks also before it is successfully completed. We only report - the binlog write and do the commit inside the transactional table - handler if the log event type is appropriate. - */ - - if (event_info->get_type_code() == QUERY_EVENT - || event_info->get_type_code() == EXEC_LOAD_EVENT) + if (opt_using_transactions && !my_b_tell(&thd->transaction.trans_log)) { - error = ha_report_binlog_offset_and_commit(thd, log_file_name, - file->pos_in_file); + /* + LOAD DATA INFILE in AUTOCOMMIT=1 mode writes to the binlog + chunks also before it is successfully completed. We only report + the binlog write and do the commit inside the transactional table + handler if the log event type is appropriate. + */ + + if (event_info->get_type_code() == QUERY_EVENT + || event_info->get_type_code() == EXEC_LOAD_EVENT) + { + error = ha_report_binlog_offset_and_commit(thd, log_file_name, + file->pos_in_file); + called_handler_commit=1; + } } - + /* we wrote to the real log, check automatic rotation */ should_rotate= (my_b_tell(file) >= (my_off_t) max_binlog_size); } @@ -1172,6 +1176,15 @@ err: } pthread_mutex_unlock(&LOCK_log); + + /* Flush the transactional handler log file now that we have released + LOCK_log; the flush is placed here to eliminate the bottleneck on the + group commit */ + + if (called_handler_commit) { + ha_commit_complete(thd); + } + DBUG_RETURN(error); } @@ -1277,6 +1290,13 @@ bool MYSQL_LOG::write(THD *thd, IO_CACHE *cache) } VOID(pthread_mutex_unlock(&LOCK_log)); + + /* Flush the transactional handler log file now that we have released + LOCK_log; the flush is placed here to eliminate the bottleneck on the + group commit */ + + ha_commit_complete(thd); + DBUG_RETURN(0); err: diff --git a/sql/log_event.cc b/sql/log_event.cc index bbea89bfd3f..300e367e1e7 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -309,15 +309,19 @@ void Load_log_event::pack_info(String* packet) pretty_print_str(&tmp, sql_ex.escaped, sql_ex.escaped_len); } + bool line_lexem_added= false; if (sql_ex.line_term_len) { tmp.append(" LINES TERMINATED BY "); pretty_print_str(&tmp, sql_ex.line_term, sql_ex.line_term_len); + line_lexem_added= true; } if (sql_ex.line_start_len) { - tmp.append(" LINES STARTING BY "); + if (!line_lexem_added) + tmp.append(" LINES"); + tmp.append(" STARTING BY "); pretty_print_str(&tmp, sql_ex.line_start, sql_ex.line_start_len); } @@ -1308,15 +1312,19 @@ void Load_log_event::print(FILE* file, bool short_form, char* last_db) pretty_print_str(file, sql_ex.escaped, sql_ex.escaped_len); } + bool line_lexem_added= false; if (sql_ex.line_term) { fprintf(file," LINES TERMINATED BY "); pretty_print_str(file, sql_ex.line_term, sql_ex.line_term_len); + line_lexem_added= true; } if (sql_ex.line_start) { - fprintf(file," LINES STARTING BY "); + if (!line_lexem_added) + fprintf(file," LINES"); + fprintf(file," STARTING BY "); pretty_print_str(file, sql_ex.line_start, sql_ex.line_start_len); } diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 9617d19caae..2c28fcf03bb 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -560,8 +560,8 @@ bool remove_table_from_cache(THD *thd, const char *db, const char *table, bool return_if_owned_by_thd=0); bool close_cached_tables(THD *thd, bool wait_for_refresh, TABLE_LIST *tables); void copy_field_from_tmp_record(Field *field,int offset); -int fill_record(List<Item> &fields,List<Item> &values); -int fill_record(Field **field,List<Item> &values); +int fill_record(List<Item> &fields,List<Item> &values, bool ignore_errors); +int fill_record(Field **field,List<Item> &values, bool ignore_errors); OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *wild); /* sql_calc.cc */ @@ -627,7 +627,6 @@ extern char language[LIBLEN],reg_ext[FN_EXTLEN]; extern char glob_hostname[FN_REFLEN], mysql_home[FN_REFLEN]; extern char pidfile_name[FN_REFLEN], time_zone[30], *opt_init_file; extern char log_error_file[FN_REFLEN]; -extern char blob_newline; extern double log_10[32]; extern ulonglong keybuff_size; extern ulong refresh_version,flush_version, thread_id,query_id,opened_tables; @@ -671,8 +670,8 @@ extern uint volatile thread_count, thread_running, global_read_lock; extern my_bool opt_sql_bin_update, opt_safe_user_create, opt_no_mix_types; extern my_bool opt_safe_show_db, opt_local_infile, lower_case_table_names; extern my_bool opt_slave_compressed_protocol, use_temp_pool; +extern my_bool opt_readonly; extern my_bool opt_enable_named_pipe; -extern char f_fyllchar; extern MYSQL_LOG mysql_log,mysql_update_log,mysql_slow_log,mysql_bin_log; extern FILE *bootstrap_file; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 33724c7100e..67fb7798ebd 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -289,7 +289,7 @@ my_bool opt_enable_named_pipe= 0; my_bool opt_local_infile, opt_external_locking, opt_slave_compressed_protocol; uint delay_key_write_options= (uint) DELAY_KEY_WRITE_ON; -static bool opt_do_pstack = 0; +static my_bool opt_do_pstack = 0; static ulong opt_specialflag=SPECIAL_ENGLISH; static ulong opt_myisam_block_size; @@ -306,6 +306,7 @@ my_bool opt_safe_user_create = 0, opt_no_mix_types = 0; my_bool lower_case_table_names, opt_old_rpl_compat; my_bool opt_show_slave_auth_info, opt_sql_bin_update = 0; my_bool opt_log_slave_updates= 0, opt_console= 0; +my_bool opt_readonly = 0; volatile bool mqh_used = 0; FILE *bootstrap_file=0; @@ -376,7 +377,7 @@ const char *localhost=LOCAL_HOST; const char *delayed_user="DELAYED"; uint master_port = MYSQL_PORT, master_connect_retry = 60; uint report_port = MYSQL_PORT; -bool master_ssl = 0; +my_bool master_ssl = 0; ulong master_retry_count=0; ulong bytes_sent = 0L, bytes_received = 0L; @@ -404,8 +405,7 @@ ulong slow_launch_threads = 0; char mysql_real_data_home[FN_REFLEN], language[LIBLEN],reg_ext[FN_EXTLEN], mysql_charsets_dir[FN_REFLEN], *charsets_list, - blob_newline,f_fyllchar,max_sort_char,*mysqld_user,*mysqld_chroot, - *opt_init_file; + max_sort_char,*mysqld_user,*mysqld_chroot, *opt_init_file; char *language_ptr= language; char mysql_data_home_buff[2], *mysql_data_home=mysql_real_data_home; #ifndef EMBEDDED_LIBRARY @@ -1607,6 +1607,7 @@ static void init_signals(void) sa.sa_handler=handle_segfault; #endif sigaction(SIGSEGV, &sa, NULL); + sigaction(SIGABRT, &sa, NULL); #ifdef SIGBUS sigaction(SIGBUS, &sa, NULL); #endif @@ -3142,10 +3143,11 @@ enum options { OPT_QUERY_CACHE_TYPE, OPT_RECORD_BUFFER, OPT_RECORD_RND_BUFFER, OPT_RELAY_LOG_SPACE_LIMIT, OPT_SLAVE_NET_TIMEOUT, OPT_SLAVE_COMPRESSED_PROTOCOL, OPT_SLOW_LAUNCH_TIME, + OPT_READONLY, OPT_SORT_BUFFER, OPT_TABLE_CACHE, OPT_THREAD_CONCURRENCY, OPT_THREAD_CACHE_SIZE, OPT_TMP_TABLE_SIZE, OPT_THREAD_STACK, - OPT_WAIT_TIMEOUT, + OPT_WAIT_TIMEOUT, OPT_MYISAM_REPAIR_THREADS, OPT_INNODB_MIRRORED_LOG_GROUPS, OPT_INNODB_LOG_FILES_IN_GROUP, OPT_INNODB_LOG_FILE_SIZE, @@ -3156,6 +3158,7 @@ enum options { OPT_INNODB_LOCK_WAIT_TIMEOUT, OPT_INNODB_THREAD_CONCURRENCY, OPT_INNODB_FORCE_RECOVERY, + OPT_INNODB_MAX_DIRTY_PAGES_PCT, OPT_BDB_CACHE_SIZE, OPT_BDB_LOG_BUFFER_SIZE, OPT_BDB_MAX_LOCK, @@ -3304,6 +3307,10 @@ struct my_option my_long_options[] = {"innodb_fast_shutdown", OPT_INNODB_FAST_SHUTDOWN, "Speeds up server shutdown process", (gptr*) &innobase_fast_shutdown, (gptr*) &innobase_fast_shutdown, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0}, + {"innodb_max_dirty_pages_pct", OPT_INNODB_MAX_DIRTY_PAGES_PCT, + "Percentage of dirty pages allowed in bufferpool", (gptr*) &srv_max_buf_pool_modified_pct, + (gptr*) &srv_max_buf_pool_modified_pct, 0, GET_ULONG, REQUIRED_ARG, 90, 0, 100, 0, 0, 0}, + #endif /* End HAVE_INNOBASE_DB */ {"help", '?', "Display this help and exit", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, @@ -3833,19 +3840,25 @@ replicating a LOAD DATA INFILE command", (gptr*) &global_system_variables.myisam_max_extra_sort_file_size, (gptr*) &max_system_variables.myisam_max_extra_sort_file_size, 0, GET_ULL, REQUIRED_ARG, (ulonglong) MI_MAX_TEMP_LENGTH, - 0, ~0L, 0, 1, 0}, + 0, (ulonglong) MAX_FILE_SIZE, 0, 1, 0}, {"myisam_max_sort_file_size", OPT_MYISAM_MAX_SORT_FILE_SIZE, "Don't use the fast sort index method to created index if the temporary file would get bigger than this!", (gptr*) &global_system_variables.myisam_max_sort_file_size, (gptr*) &max_system_variables.myisam_max_sort_file_size, 0, - GET_ULL, REQUIRED_ARG, (longlong) LONG_MAX, 0, ~0L, 0, 1024*1024, 0}, + GET_ULL, REQUIRED_ARG, (longlong) LONG_MAX, 0, (ulonglong) MAX_FILE_SIZE, + 0, 1024*1024, 0}, + {"myisam_repair_threads", OPT_MYISAM_REPAIR_THREADS, + "Number of threads to use when repairing MyISAM tables. The value of 1 disables parallel repair.", + (gptr*) &global_system_variables.myisam_repair_threads, + (gptr*) &max_system_variables.myisam_repair_threads, 0, + GET_ULONG, REQUIRED_ARG, 1, 1, ~0L, 0, 1, 0}, {"myisam_sort_buffer_size", OPT_MYISAM_SORT_BUFFER_SIZE, "The buffer that is allocated when sorting the index when doing a REPAIR or when creating indexes with CREATE INDEX or ALTER TABLE.", (gptr*) &global_system_variables.myisam_sort_buff_size, (gptr*) &max_system_variables.myisam_sort_buff_size, 0, GET_ULONG, REQUIRED_ARG, 8192*1024, 4, ~0L, 0, 1, 0}, {"net_buffer_length", OPT_NET_BUFFER_LENGTH, - "Buffer length for TCP/IP and socket communication.", + "Buffer length for TCP/IP and socket communication.", (gptr*) &global_system_variables.net_buffer_length, (gptr*) &max_system_variables.net_buffer_length, 0, GET_ULONG, REQUIRED_ARG, 16384, 1024, 1024*1024L, 0, 1024, 0}, @@ -3915,6 +3928,11 @@ replicating a LOAD DATA INFILE command", "Number of seconds to wait for more data from a master/slave connection before aborting the read.", (gptr*) &slave_net_timeout, (gptr*) &slave_net_timeout, 0, GET_ULONG, REQUIRED_ARG, SLAVE_NET_TIMEOUT, 1, LONG_TIMEOUT, 0, 1, 0}, + {"read-only", OPT_READONLY, + "Make all tables readonly, with the expections for replications (slave) threads and users with the SUPER privilege", + (gptr*) &opt_readonly, + (gptr*) &opt_readonly, + 0, GET_BOOL, NO_ARG, 0, 0, 1, 0, 1, 0}, {"slow_launch_time", OPT_SLOW_LAUNCH_TIME, "If creating the thread takes longer than this value (in seconds), the Slow_launch_threads counter will be incremented.", (gptr*) &slow_launch_time, (gptr*) &slow_launch_time, 0, GET_ULONG, @@ -4710,11 +4728,9 @@ static void get_options(int argc,char **argv) my_disable_locking= myisam_single_user= test(opt_external_locking == 0); my_default_record_cache_size=global_system_variables.read_buff_size; myisam_max_temp_length= - (my_off_t) min(global_system_variables.myisam_max_sort_file_size, - (ulonglong) MAX_FILE_SIZE); + (my_off_t) global_system_variables.myisam_max_sort_file_size; myisam_max_extra_temp_length= - (my_off_t) min(global_system_variables.myisam_max_extra_sort_file_size, - (ulonglong) MAX_FILE_SIZE); + (my_off_t) global_system_variables.myisam_max_extra_sort_file_size; /* Set global variables based on startup options */ myisam_block_size=(uint) 1 << my_bit_log2(opt_myisam_block_size); diff --git a/sql/opt_sum.cc b/sql/opt_sum.cc index aeaf8beef07..855813f1140 100644 --- a/sql/opt_sum.cc +++ b/sql/opt_sum.cc @@ -37,8 +37,10 @@ static bool find_range_key(TABLE_REF *ref, Field* field,COND *cond); RETURN VALUES 0 No errors - 1 if all items was resolved - -1 on impossible conditions + 1 if all items were resolved + -1 on impossible conditions + OR an error number from my_base.h HA_ERR_... if a deadlock or a lock + wait timeout happens, for example */ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds) @@ -50,6 +52,7 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds) table_map where_tables= 0; Item *item; COND *org_conds= conds; + int error; if (conds) where_tables= conds->used_tables(); @@ -136,7 +139,7 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds) const_result=0; break; } - bool error= table->file->index_init((uint) ref.key); + error= table->file->index_init((uint) ref.key); enum ha_rkey_function find_flag= HA_READ_KEY_OR_NEXT; uint prefix_len= ref.key_length; /* @@ -150,12 +153,17 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds) } if (!ref.key_length) - error=table->file->index_first(table->record[0]) !=0; + { + error=table->file->index_first(table->record[0]); + } else + { error=table->file->index_read(table->record[0],key_buff, ref.key_length, - find_flag) || - key_cmp(table, key_buff, ref.key, prefix_len); + find_flag); + if (!error && key_cmp(table, key_buff, ref.key, prefix_len)) + error = HA_ERR_KEY_NOT_FOUND; + } if (table->key_read) { table->key_read=0; @@ -163,7 +171,14 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds) } table->file->index_end(); if (error) - return -1; // No rows matching where + { + if (error == HA_ERR_KEY_NOT_FOUND || error == HA_ERR_END_OF_FILE) + return -1; // No rows matching WHERE + + table->file->print_error(error, MYF(0)); + return(error); // HA_ERR_LOCK_DEADLOCK or + // some other error + } removed_tables|= table->map; } else if (!expr->const_item()) // This is VERY seldom false @@ -191,27 +206,30 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds) ref.key_buff=key_buff; TABLE *table=((Item_field*) expr)->field->table; - if ((outer_tables & table->map) || - !find_range_key(&ref, ((Item_field*) expr)->field,conds)) + if ((table->file->table_flags() & HA_NOT_READ_AFTER_KEY)) { const_result=0; break; } - if ((table->file->table_flags() & HA_NOT_READ_AFTER_KEY)) + if ((outer_tables & table->map) || + !find_range_key(&ref, ((Item_field*) expr)->field,conds)) { const_result=0; break; } - bool error=table->file->index_init((uint) ref.key); + error=table->file->index_init((uint) ref.key); if (!ref.key_length) - error=table->file->index_last(table->record[0]) !=0; + { + error=table->file->index_last(table->record[0]); + } else { - error = table->file->index_read(table->record[0], key_buff, + error=table->file->index_read(table->record[0], key_buff, ref.key_length, - HA_READ_PREFIX_LAST) || - key_cmp(table,key_buff,ref.key,ref.key_length); + HA_READ_PREFIX_LAST); + if (!error && key_cmp(table,key_buff,ref.key,ref.key_length)) + error = HA_ERR_KEY_NOT_FOUND; } if (table->key_read) { @@ -220,7 +238,13 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds) } table->file->index_end(); if (error) - return -1; // Impossible query + { + if (error == HA_ERR_KEY_NOT_FOUND || error == HA_ERR_END_OF_FILE) + return -1; // Impossible query + + table->file->print_error(error, MYF(0)); + return error; // Deadlock or some other error + } removed_tables|= table->map; } else if (!expr->const_item()) // This is VERY seldom false @@ -348,7 +372,13 @@ bool part_of_cond(COND *cond,Field *field) } -/* Check if we can get value for field by using a key */ +/* + Check if we can get value for field by using a key + + NOTES + This function may set table->key_read to 1, which must be reset after + index is used! (This can only happen when function returns 1) +*/ static bool find_range_key(TABLE_REF *ref, Field* field, COND *cond) { diff --git a/sql/set_var.cc b/sql/set_var.cc index 172939ce981..2ca12cdb802 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -82,6 +82,8 @@ static void fix_net_retry_count(THD *thd, enum_var_type type); static void fix_max_join_size(THD *thd, enum_var_type type); static void fix_query_cache_size(THD *thd, enum_var_type type); static void fix_key_buffer_size(THD *thd, enum_var_type type); +static void fix_myisam_max_extra_sort_file_size(THD *thd, enum_var_type type); +static void fix_myisam_max_sort_file_size(THD *thd, enum_var_type type); /* Variable definition list @@ -165,8 +167,9 @@ sys_var_thd_ulong sys_max_tmp_tables("max_tmp_tables", &SV::max_tmp_tables); sys_var_long_ptr sys_max_write_lock_count("max_write_lock_count", &max_write_lock_count); -sys_var_thd_ulonglong sys_myisam_max_extra_sort_file_size("myisam_max_extra_sort_file_size", &SV::myisam_max_extra_sort_file_size); -sys_var_thd_ulonglong sys_myisam_max_sort_file_size("myisam_max_sort_file_size", &SV::myisam_max_sort_file_size); +sys_var_thd_ulonglong sys_myisam_max_extra_sort_file_size("myisam_max_extra_sort_file_size", &SV::myisam_max_extra_sort_file_size, fix_myisam_max_extra_sort_file_size, 1); +sys_var_thd_ulonglong sys_myisam_max_sort_file_size("myisam_max_sort_file_size", &SV::myisam_max_sort_file_size, fix_myisam_max_sort_file_size, 1); +sys_var_thd_ulong sys_myisam_repair_threads("myisam_repair_threads", &SV::myisam_repair_threads); sys_var_thd_ulong sys_myisam_sort_buffer_size("myisam_sort_buffer_size", &SV::myisam_sort_buff_size); sys_var_thd_ulong sys_net_buffer_length("net_buffer_length", &SV::net_buffer_length); @@ -201,6 +204,8 @@ sys_var_bool_ptr sys_slave_compressed_protocol("slave_compressed_protocol", &opt_slave_compressed_protocol); sys_var_long_ptr sys_slave_net_timeout("slave_net_timeout", &slave_net_timeout); +sys_var_bool_ptr sys_readonly("read_only", + &opt_readonly); sys_var_long_ptr sys_slow_launch_time("slow_launch_time", &slow_launch_time); sys_var_thd_ulong sys_sort_buffer("sort_buffer_size", @@ -219,6 +224,11 @@ sys_var_thd_ulong sys_tmp_table_size("tmp_table_size", &SV::tmp_table_size); sys_var_thd_ulong sys_net_wait_timeout("wait_timeout", &SV::net_wait_timeout); + +#ifdef HAVE_INNOBASE_DB +sys_var_long_ptr sys_innodb_max_dirty_pages_pct("innodb_max_dirty_pages_pct", + &srv_max_buf_pool_modified_pct); +#endif /* @@ -342,6 +352,7 @@ sys_var *sys_variables[]= &sys_max_write_lock_count, &sys_myisam_max_extra_sort_file_size, &sys_myisam_max_sort_file_size, + &sys_myisam_repair_threads, &sys_myisam_sort_buffer_size, &sys_net_buffer_length, &sys_net_read_timeout, @@ -366,6 +377,7 @@ sys_var *sys_variables[]= &sys_slave_compressed_protocol, &sys_slave_net_timeout, &sys_slave_skip_counter, + &sys_readonly, &sys_slow_launch_time, &sys_sort_buffer, &sys_sql_big_tables, @@ -378,6 +390,9 @@ sys_var *sys_variables[]= &sys_timestamp, &sys_tmp_table_size, &sys_tx_isolation, +#ifdef HAVE_INNOBASE_DB + &sys_innodb_max_dirty_pages_pct, +#endif &sys_unique_checks }; @@ -446,6 +461,7 @@ struct show_var_st init_vars[]= { {"innodb_log_files_in_group", (char*) &innobase_log_files_in_group, SHOW_LONG}, {"innodb_log_group_home_dir", (char*) &innobase_log_group_home_dir, SHOW_CHAR_PTR}, {"innodb_mirrored_log_groups", (char*) &innobase_mirrored_log_groups, SHOW_LONG}, + {sys_innodb_max_dirty_pages_pct.name, (char*) &sys_innodb_max_dirty_pages_pct, SHOW_SYS}, #endif {sys_interactive_timeout.name,(char*) &sys_interactive_timeout, SHOW_SYS}, {sys_join_buffer_size.name, (char*) &sys_join_buffer_size, SHOW_SYS}, @@ -475,13 +491,14 @@ struct show_var_st init_vars[]= { {sys_max_join_size.name, (char*) &sys_max_join_size, SHOW_SYS}, {sys_max_sort_length.name, (char*) &sys_max_sort_length, SHOW_SYS}, {sys_max_user_connections.name,(char*) &sys_max_user_connections, SHOW_SYS}, - {sys_max_tmp_tables.name, (char*) &sys_max_tmp_tables, SHOW_SYS}, + {sys_max_tmp_tables.name, (char*) &sys_max_tmp_tables, SHOW_SYS}, {sys_max_write_lock_count.name, (char*) &sys_max_write_lock_count,SHOW_SYS}, {sys_myisam_max_extra_sort_file_size.name, (char*) &sys_myisam_max_extra_sort_file_size, SHOW_SYS}, - {sys_myisam_max_sort_file_size.name, - (char*) &sys_myisam_max_sort_file_size, + {sys_myisam_max_sort_file_size.name, (char*) &sys_myisam_max_sort_file_size, + SHOW_SYS}, + {sys_myisam_repair_threads.name, (char*) &sys_myisam_repair_threads, SHOW_SYS}, {"myisam_recover_options", (char*) &myisam_recover_options_str, SHOW_CHAR_PTR}, {sys_myisam_sort_buffer_size.name, (char*) &sys_myisam_sort_buffer_size, SHOW_SYS}, @@ -508,6 +525,7 @@ struct show_var_st init_vars[]= { #endif /* HAVE_QUERY_CACHE */ {sys_server_id.name, (char*) &sys_server_id, SHOW_SYS}, {sys_slave_net_timeout.name,(char*) &sys_slave_net_timeout, SHOW_SYS}, + {sys_readonly.name, (char*) &sys_readonly, SHOW_SYS}, {"skip_external_locking", (char*) &my_disable_locking, SHOW_MY_BOOL}, {"skip_networking", (char*) &opt_disable_networking, SHOW_BOOL}, {"skip_show_database", (char*) &opt_skip_show_db, SHOW_BOOL}, @@ -574,6 +592,21 @@ static void fix_low_priority_updates(THD *thd, enum_var_type type) } +static void +fix_myisam_max_extra_sort_file_size(THD *thd, enum_var_type type) +{ + myisam_max_extra_temp_length= + (my_off_t) global_system_variables.myisam_max_extra_sort_file_size; +} + + +static void +fix_myisam_max_sort_file_size(THD *thd, enum_var_type type) +{ + myisam_max_temp_length= + (my_off_t) global_system_variables.myisam_max_sort_file_size; +} + /* Set the OPTION_BIG_SELECTS flag if max_join_size == HA_POS_ERROR */ @@ -803,15 +836,22 @@ byte *sys_var_thd_ha_rows::value_ptr(THD *thd, enum_var_type type) bool sys_var_thd_ulonglong::update(THD *thd, set_var *var) { + ulonglong tmp= var->value->val_int(); + + if ((ulonglong) tmp > max_system_variables.*offset) + tmp= max_system_variables.*offset; + + if (option_limits) + tmp= (ulong) getopt_ull_limit_value(tmp, option_limits); if (var->type == OPT_GLOBAL) { /* Lock is needed to make things safe on 32 bit systems */ pthread_mutex_lock(&LOCK_global_system_variables); - global_system_variables.*offset= var->value->val_int(); + global_system_variables.*offset= (ulonglong) tmp; pthread_mutex_unlock(&LOCK_global_system_variables); } else - thd->variables.*offset= var->value->val_int(); + thd->variables.*offset= (ulonglong) tmp; return 0; } diff --git a/sql/set_var.h b/sql/set_var.h index c74f1e827bd..f33f53c5acc 100644 --- a/sql/set_var.h +++ b/sql/set_var.h @@ -233,13 +233,27 @@ class sys_var_thd_ulonglong :public sys_var_thd { public: ulonglong SV::*offset; + bool only_global; sys_var_thd_ulonglong(const char *name_arg, ulonglong SV::*offset_arg) :sys_var_thd(name_arg), offset(offset_arg) {} + sys_var_thd_ulonglong(const char *name_arg, ulonglong SV::*offset_arg, + sys_after_update_func func, bool only_global_arg) + :sys_var_thd(name_arg, func), offset(offset_arg), + only_global(only_global_arg) + {} bool update(THD *thd, set_var *var); void set_default(THD *thd, enum_var_type type); SHOW_TYPE type() { return SHOW_LONGLONG; } byte *value_ptr(THD *thd, enum_var_type type); + bool check_default(enum_var_type type) + { + return type == OPT_GLOBAL && !option_limits; + } + bool check_type(enum_var_type type) + { + return (only_global && type != OPT_GLOBAL); + } }; diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 3332e596146..f01248bb682 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -2520,8 +2520,8 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, } -bool check_grant_column (THD *thd,TABLE *table, const char *name, - uint length, uint show_tables) +bool check_grant_column(THD *thd,TABLE *table, const char *name, + uint length, uint show_tables) { GRANT_TABLE *grant_table; GRANT_COLUMN *grant_column; @@ -2529,6 +2529,8 @@ bool check_grant_column (THD *thd,TABLE *table, const char *name, ulong want_access=table->grant.want_privilege; if (!want_access) return 0; // Already checked + if (!grant_option) + goto err2; pthread_mutex_lock(&LOCK_grant); @@ -2560,8 +2562,9 @@ bool check_grant_column (THD *thd,TABLE *table, const char *name, #endif /* We must use my_printf_error() here! */ - err: +err: pthread_mutex_unlock(&LOCK_grant); +err2: if (!show_tables) { char command[128]; diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 0f0c3c97ed2..3bfd5e14d43 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -482,8 +482,9 @@ bool close_thread_table(THD *thd, TABLE **table_ptr) { DBUG_ENTER("close_thread_table"); - bool found_old_table=0; - TABLE *table=*table_ptr; + bool found_old_table= 0; + TABLE *table= *table_ptr; + DBUG_ASSERT(table->key_read == 0); *table_ptr=table->next; if (table->version != refresh_version || @@ -1693,7 +1694,7 @@ Field *find_field_in_table(THD *thd,TABLE *table,const char *name,uint length, else thd->dupp_field=field; } - if (check_grants && check_grant_column(thd,table,name,length)) + if (check_grants && check_grant_column(thd,table,name,length)) return WRONG_GRANT; return field; } @@ -1718,8 +1719,8 @@ find_field_in_tables(THD *thd,Item_field *item,TABLE_LIST *tables) { found_table=1; Field *find=find_field_in_table(thd,tables->table,name,length, - grant_option && - tables->table->grant.want_privilege, + test(tables->table->grant. + want_privilege), 1); if (find) { @@ -1759,8 +1760,7 @@ find_field_in_tables(THD *thd,Item_field *item,TABLE_LIST *tables) for (; tables ; tables=tables->next) { Field *field=find_field_in_table(thd,tables->table,name,length, - grant_option && - tables->table->grant.want_privilege, + test(tables->table->grant.want_privilege), allow_rowid); if (field) { @@ -1984,10 +1984,10 @@ insert_fields(THD *thd,TABLE_LIST *tables, const char *db_name, (!db_name || !strcmp(tables->db,db_name)))) { /* Ensure that we have access right to all columns */ - if (grant_option && !(table->grant.privilege & - table->grant.want_privilege) && + if (!(table->grant.privilege & SELECT_ACL) && check_grant_all_columns(thd,SELECT_ACL,table)) DBUG_RETURN(-1); + Field **ptr=table->field,*field; thd->used_tables|=table->map; while ((field = *ptr++)) @@ -2112,7 +2112,7 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds) ******************************************************************************/ int -fill_record(List<Item> &fields,List<Item> &values) +fill_record(List<Item> &fields,List<Item> &values, bool ignore_errors) { List_iterator_fast<Item> f(fields),v(values); Item *value; @@ -2122,7 +2122,7 @@ fill_record(List<Item> &fields,List<Item> &values) while ((field=(Item_field*) f++)) { value=v++; - if (value->save_in_field(field->field, 0)) + if (value->save_in_field(field->field, 0) && !ignore_errors) DBUG_RETURN(1); } DBUG_RETURN(0); @@ -2130,7 +2130,7 @@ fill_record(List<Item> &fields,List<Item> &values) int -fill_record(Field **ptr,List<Item> &values) +fill_record(Field **ptr,List<Item> &values, bool ignore_errors) { List_iterator_fast<Item> v(values); Item *value; @@ -2140,7 +2140,7 @@ fill_record(Field **ptr,List<Item> &values) while ((field = *ptr++)) { value=v++; - if (value->save_in_field(field, 0)) + if (value->save_in_field(field, 0) && !ignore_errors) DBUG_RETURN(1); } DBUG_RETURN(0); diff --git a/sql/sql_class.h b/sql/sql_class.h index 160c06ca3c6..9857774fb84 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -294,6 +294,7 @@ struct system_variables ulong max_heap_table_size; ulong max_sort_length; ulong max_tmp_tables; + ulong myisam_repair_threads; ulong myisam_sort_buff_size; ulong net_buffer_length; ulong net_interactive_timeout; diff --git a/sql/sql_db.cc b/sql/sql_db.cc index 7e58b5d4582..6251b1ec624 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -211,6 +211,8 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db, ulong found_other_files=0; char filePath[FN_REFLEN]; TABLE_LIST *tot_list=0, **tot_list_next; + List<String> raid_dirs; + DBUG_ENTER("mysql_rm_known_files"); DBUG_PRINT("enter",("path: %s", org_path)); @@ -229,6 +231,8 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db, { char newpath[FN_REFLEN]; MY_DIR *new_dirp; + String *dir; + strxmov(newpath,org_path,"/",file->name,NullS); unpack_filename(newpath,newpath); if ((new_dirp = my_dir(newpath,MYF(MY_DONT_SORT)))) @@ -239,7 +243,11 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db, my_dirend(dirp); DBUG_RETURN(-1); } + raid_dirs.push_back(dir=new String(newpath)); + dir->copy(); + continue; } + found_other_files++; continue; } if (find_type(fn_ext(file->name),&deletable_extentions,1+2) <= 0) @@ -278,12 +286,19 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db, deleted++; } } - my_dirend(dirp); - if (thd->killed || (tot_list && mysql_rm_table_part2_with_lock(thd, tot_list, 1, 1))) + { + my_dirend(dirp); DBUG_RETURN(-1); - + } + List_iterator<String> it(raid_dirs); + String *dir; + while ((dir= it++)) + if (rmdir(dir->c_ptr()) < 0) + found_other_files++; + my_dirend(dirp); + /* If the directory is a symbolic link, remove the link first, then remove the directory the symbolic link pointed at diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 1507d49ebd6..caa1e0e0312 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -54,7 +54,7 @@ int mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ORDER *order, DBUG_RETURN(-1); /* Test if the user wants to delete all rows */ - if (!using_limit && (!conds || conds->const_item()) && + if (!using_limit && (!conds || (conds->const_item() && conds->val_int())) && !(specialflag & (SPECIAL_NO_NEW_FUNC | SPECIAL_SAFE_MODE)) && !safe_update) { deleted= table->file->records; diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index e02f457fd77..33a13fabdc6 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -219,7 +219,7 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, List<Item> &fields, if (fields.elements || !value_count) { restore_record(table,2); // Get empty record - if (fill_record(fields,*values) || check_null_fields(thd,table)) + if (fill_record(fields, *values, 0) || check_null_fields(thd,table)) { if (values_list.elements != 1) { @@ -236,7 +236,7 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, List<Item> &fields, restore_record(table,2); // Get empty record else table->record[0][0]=table->record[2][0]; // Fix delete marker - if (fill_record(table->field,*values)) + if (fill_record(table->field, *values, 0)) { if (values_list.elements != 1) { @@ -1330,9 +1330,9 @@ bool select_insert::send_data(List<Item> &values) return 0; } if (fields->elements) - fill_record(*fields,values); + fill_record(*fields, values, 1); else - fill_record(table->field,values); + fill_record(table->field, values, 1); if (write_record(table,&info)) return 1; if (table->next_number_field) // Clear for next record @@ -1444,7 +1444,7 @@ bool select_create::send_data(List<Item> &values) thd->offset_limit--; return 0; } - fill_record(field,values); + fill_record(field, values, 1); if (write_record(table,&info)) return 1; if (table->next_number_field) // Clear for next record diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 60942e47337..d9060b4b26e 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -196,9 +196,17 @@ static bool check_user(THD *thd,enum_server_command command, const char *user, thd->db=0; thd->db_length=0; USER_RESOURCES ur; + char tmp_passwd[SCRAMBLE_LENGTH]; if (passwd[0] && strlen(passwd) != SCRAMBLE_LENGTH) return 1; + /* + Move password to temporary buffer as it may be stored in communication + buffer + */ + strmov(tmp_passwd, passwd); + passwd= tmp_passwd; // Use local copy + if (!(thd->user = my_strdup(user, MYF(0)))) { send_error(net,ER_OUT_OF_RESOURCES); @@ -264,6 +272,7 @@ static bool check_user(THD *thd,enum_server_command command, const char *user, } else send_ok(net); // Ready to handle questions + thd->password= test(passwd[0]); // Remember for error messages return 0; // ok } @@ -617,7 +626,6 @@ check_connections(THD *thd) net->read_timeout=(uint) thd->variables.net_read_timeout; if (check_user(thd,COM_CONNECT, user, passwd, db, 1)) return (-1); - thd->password=test(passwd[0]); return 0; } @@ -1007,7 +1015,6 @@ bool dispatch_command(enum enum_server_command command, THD *thd, decrease_user_connections(save_uc); x_free((gptr) save_db); x_free((gptr) save_user); - thd->password=test(passwd[0]); break; } @@ -1322,6 +1329,18 @@ mysql_execute_command(void) (table_rules_on && tables && thd->slave_thread && !tables_ok(thd,tables))) DBUG_VOID_RETURN; + + /* + When option readonly is set deny operations which change tables. + Except for the replication thread and the 'super' users. + */ + if (opt_readonly && + !(thd->slave_thread || (thd->master_access & SUPER_ACL)) && + (uc_update_queries[lex->sql_command] > 0)) + { + send_error(&thd->net,ER_CANT_UPDATE_WITH_READLOCK); + DBUG_VOID_RETURN; + } statistic_increment(com_stat[lex->sql_command],&LOCK_status); switch (lex->sql_command) { @@ -1475,7 +1494,8 @@ mysql_execute_command(void) } case SQLCOM_SHOW_SLAVE_STAT: { - if (check_global_access(thd, SUPER_ACL)) + /* Accept one of two privileges */ + if (check_global_access(thd, SUPER_ACL | REPL_CLIENT_ACL)) goto error; LOCK_ACTIVE_MI; res = show_master_info(thd,active_mi); @@ -1484,7 +1504,8 @@ mysql_execute_command(void) } case SQLCOM_SHOW_MASTER_STAT: { - if (check_global_access(thd, SUPER_ACL)) + /* Accept one of two privileges */ + if (check_global_access(thd, SUPER_ACL | REPL_CLIENT_ACL)) goto error; res = show_binlog_info(thd); break; @@ -2533,7 +2554,8 @@ error: save_priv In this we store global and db level grants for the table Note that we don't store db level grants if the global grants - is enough to satisfy the request. + is enough to satisfy the request and the global grants contains + a SELECT grant. ****************************************************************************/ bool @@ -2558,7 +2580,17 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, if ((thd->master_access & want_access) == want_access) { - *save_priv=thd->master_access | thd->db_access; + /* + If we don't have a global SELECT privilege, we have to get the database + specific access rights to be able to handle queries of type + UPDATE t1 SET a=1 WHERE b > 0 + */ + db_access= thd->db_access; + if (!(thd->master_access & SELECT_ACL) && + (db && (!thd->db || strcmp(db,thd->db)))) + db_access=acl_get(thd->host, thd->ip, (char*) &thd->remote.sin_addr, + thd->priv_user, db); /* purecov: inspected */ + *save_priv=thd->master_access | db_access; DBUG_RETURN(FALSE); } if (((want_access & ~thd->master_access) & ~(DB_ACLS | EXTRA_ACL)) || @@ -2598,12 +2630,29 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, } -/* check for global access and give descriptive error message if it fails */ +/* + check for global access and give descriptive error message if it fails + + SYNOPSIS + check_global_access() + thd Thread handler + want_access Use should have any of these global rights + + WARNING + One gets access rigth if one has ANY of the rights in want_access + This is useful as one in most cases only need one global right, + but in some case we want to check if the user has SUPER or + REPL_CLIENT_ACL rights. + + RETURN + 0 ok + 1 Access denied. In this case an error is sent to the client +*/ bool check_global_access(THD *thd, ulong want_access) { char command[128]; - if ((thd->master_access & want_access) == want_access) + if ((thd->master_access & want_access)) return 0; get_privilege_desc(command, sizeof(command), want_access); net_printf(&thd->net,ER_SPECIFIC_ACCESS_DENIED_ERROR, @@ -2940,9 +2989,8 @@ bool add_field_to_list(char *field_name, enum_field_types type, new_field->change=change; new_field->interval=0; new_field->pack_length=0; - if (length) - if (!(new_field->length= (uint) atoi(length))) - length=0; /* purecov: inspected */ + if (length && !(new_field->length= (uint) atoi(length))) + length=0; /* purecov: inspected */ uint sign_len=type_modifier & UNSIGNED_FLAG ? 0 : 1; if (new_field->length && new_field->decimals && @@ -2978,10 +3026,13 @@ bool add_field_to_list(char *field_name, enum_field_types type, break; case FIELD_TYPE_DECIMAL: if (!length) - new_field->length = 10; // Default length for DECIMAL - new_field->length+=sign_len; - if (new_field->decimals) - new_field->length++; + new_field->length= 10; // Default length for DECIMAL + if (new_field->length < MAX_FIELD_WIDTH) // Skip wrong argument + { + new_field->length+=sign_len; + if (new_field->decimals) + new_field->length++; + } break; case FIELD_TYPE_BLOB: case FIELD_TYPE_TINY_BLOB: diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 79ba13a3339..ff6fde1ca0c 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -391,8 +391,17 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, if (tables && join.tmp_table_param.sum_func_count && ! group) { int res; + /* + opt_sum_query returns -1 if no rows match to the WHERE conditions, + or 1 if all items were resolved, or 0, or an error number HA_ERR_... + */ if ((res=opt_sum_query(tables, all_fields, conds))) { + if (res > 1) + { + delete procedure; + DBUG_RETURN(-1); + } if (res < 0) { error=return_zero_rows(&join, result, tables, fields, !group, @@ -6659,7 +6668,8 @@ setup_group(THD *thd,TABLE_LIST *tables,List<Item> &fields, while ((item=li++)) { - if (item->type() != Item::SUM_FUNC_ITEM && !item->marker) + if (item->type() != Item::SUM_FUNC_ITEM && !item->marker && + !item->const_item()) { my_printf_error(ER_WRONG_FIELD_WITH_GROUP, ER(ER_WRONG_FIELD_WITH_GROUP), diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 94b37e164e7..07ec1d67538 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -598,8 +598,9 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, { if (key->type == Key::PRIMARY) { - my_error(ER_PRIMARY_CANT_HAVE_NULL, MYF(0)); - DBUG_RETURN(-1); + /* Implicitly set primary key fields to NOT NULL for ISO conf. */ + sql_field->flags|= NOT_NULL_FLAG; + sql_field->pack_flag&= ~FIELDFLAG_MAYBE_NULL; } if (!(file->table_flags() & HA_NULL_KEY)) { @@ -945,6 +946,7 @@ static void safe_remove_from_cache(THD *thd,TABLE *table) DBUG_VOID_RETURN; } + bool close_cached_table(THD *thd,TABLE *table) { DBUG_ENTER("close_cached_table"); @@ -956,7 +958,8 @@ bool close_cached_table(THD *thd,TABLE *table) /* Close lock if this is not got with LOCK TABLES */ if (thd->lock) { - mysql_unlock_tables(thd, thd->lock); thd->lock=0; // Start locked threads + mysql_unlock_tables(thd, thd->lock); + thd->lock=0; // Start locked threads } /* Close all copies of 'table'. This also frees all LOCK TABLES lock */ thd->open_tables=unlink_open_table(thd,thd->open_tables,table); @@ -1044,88 +1047,105 @@ static int prepare_for_restore(THD* thd, TABLE_LIST* table, } -static int prepare_for_repair(THD* thd, TABLE_LIST* table, +static int prepare_for_repair(THD* thd, TABLE_LIST *table_list, HA_CHECK_OPT *check_opt) { + int error= 0; + TABLE tmp_table, *table; DBUG_ENTER("prepare_for_repair"); if (!(check_opt->sql_flags & TT_USEFRM)) - { DBUG_RETURN(0); - } - else + + if (!(table= table_list->table)) /* if open_ltable failed */ { - /* - User gave us USE_FRM which means that the header in the index file is - trashed. - In this case we will try to fix the table the following way: - - Rename the data file to a temporary name - - Truncate the table - - Replace the new data file with the old one - - Run a normal repair using the new index file and the old data file - */ + char name[FN_REFLEN]; + strxmov(name, mysql_data_home, "/", table_list->db, "/", + table_list->real_name, NullS); + if (openfrm(name, "", 0, 0, 0, &tmp_table)) + DBUG_RETURN(0); // Can't open frm file + table= &tmp_table; + } - char from[FN_REFLEN],tmp[FN_REFLEN+32]; - const char **ext= table->table->file->bas_ext(); - MY_STAT stat_info; + /* + User gave us USE_FRM which means that the header in the index file is + trashed. + In this case we will try to fix the table the following way: + - Rename the data file to a temporary name + - Truncate the table + - Replace the new data file with the old one + - Run a normal repair using the new index file and the old data file + */ - /* - Check if this is a table type that stores index and data separately, - like ISAM or MyISAM - */ - if (!ext[0] || !ext[1]) - DBUG_RETURN(0); // No data file + char from[FN_REFLEN],tmp[FN_REFLEN+32]; + const char **ext= table->file->bas_ext(); + MY_STAT stat_info; - strxmov(from, table->table->path, ext[1], NullS); // Name of data file - if (!my_stat(from, &stat_info, MYF(0))) - DBUG_RETURN(0); // Can't use USE_FRM flag + /* + Check if this is a table type that stores index and data separately, + like ISAM or MyISAM + */ + if (!ext[0] || !ext[1]) + goto end; // No data file - sprintf(tmp,"%s-%lx_%lx", from, current_pid, thd->thread_id); + strxmov(from, table->path, ext[1], NullS); // Name of data file + if (!my_stat(from, &stat_info, MYF(0))) + goto end; // Can't use USE_FRM flag - pthread_mutex_lock(&LOCK_open); - close_cached_table(thd,table->table); - pthread_mutex_unlock(&LOCK_open); + sprintf(tmp,"%s-%lx_%lx", from, current_pid, thd->thread_id); - if (lock_and_wait_for_table_name(thd,table)) - DBUG_RETURN(-1); + pthread_mutex_lock(&LOCK_open); + close_cached_table(thd,table_list->table); + pthread_mutex_unlock(&LOCK_open); - if (my_rename(from, tmp, MYF(MY_WME))) - { - pthread_mutex_lock(&LOCK_open); - unlock_table_name(thd, table); - pthread_mutex_unlock(&LOCK_open); - DBUG_RETURN(send_check_errmsg(thd, table, "repair", - "Failed renaming data file")); - } - if (mysql_truncate(thd, table, 1)) - { - pthread_mutex_lock(&LOCK_open); - unlock_table_name(thd, table); - pthread_mutex_unlock(&LOCK_open); - DBUG_RETURN(send_check_errmsg(thd, table, "repair", - "Failed generating table from .frm file")); - } - if (my_rename(tmp, from, MYF(MY_WME))) - { - pthread_mutex_lock(&LOCK_open); - unlock_table_name(thd, table); - pthread_mutex_unlock(&LOCK_open); - DBUG_RETURN(send_check_errmsg(thd, table, "repair", - "Failed restoring .MYD file")); - } + if (lock_and_wait_for_table_name(thd,table_list)) + { + error= -1; + goto end; + } + if (my_rename(from, tmp, MYF(MY_WME))) + { + pthread_mutex_lock(&LOCK_open); + unlock_table_name(thd, table_list); + pthread_mutex_unlock(&LOCK_open); + error= send_check_errmsg(thd, table_list, "repair", + "Failed renaming data file"); + goto end; + } + if (mysql_truncate(thd, table_list, 1)) + { + pthread_mutex_lock(&LOCK_open); + unlock_table_name(thd, table_list); + pthread_mutex_unlock(&LOCK_open); + error= send_check_errmsg(thd, table_list, "repair", + "Failed generating table from .frm file"); + goto end; + } + if (my_rename(tmp, from, MYF(MY_WME))) + { + pthread_mutex_lock(&LOCK_open); + unlock_table_name(thd, table_list); + pthread_mutex_unlock(&LOCK_open); + error= send_check_errmsg(thd, table_list, "repair", + "Failed restoring .MYD file"); + goto end; } /* Now we should be able to open the partially repaired table to finish the repair in the handler later on. */ - if (!(table->table = reopen_name_locked_table(thd, table))) + if (!(table_list->table = reopen_name_locked_table(thd, table_list))) { pthread_mutex_lock(&LOCK_open); - unlock_table_name(thd, table); + unlock_table_name(thd, table_list); pthread_mutex_unlock(&LOCK_open); } - DBUG_RETURN(0); + +end: + if (table == &tmp_table) + closefrm(table); // Free allocated memory + DBUG_RETURN(error); } diff --git a/sql/sql_union.cc b/sql/sql_union.cc index e7afa7fbd23..55b5d57d07d 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -110,7 +110,8 @@ int mysql_union(THD *thd, LEX *lex,select_result *result) while ((item= it++)) if (item_list.push_back(item)) DBUG_RETURN(-1); - if (setup_fields(thd,first_table,item_list,0,0,1)) + if (setup_tables(first_table) || + setup_fields(thd,first_table,item_list,0,0,1)) DBUG_RETURN(-1); } @@ -259,7 +260,7 @@ bool select_union::send_data(List<Item> &values) return 0; } - fill_record(table->field,values); + fill_record(table->field, values, 1); if ((write_record(table,&info))) { if (create_myisam_from_heap(thd, table, tmp_table_param, info.last_errno, diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 462206b25f3..ed4d6fd9b81 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -62,6 +62,7 @@ int mysql_update(THD *thd, int error=0; uint used_index, want_privilege; ulong query_id=thd->query_id, timestamp_query_id; + ha_rows updated, found; key_map old_used_keys; TABLE *table; SQL_SELECT *select; @@ -192,9 +193,8 @@ int mysql_update(THD *thd, limit, &examined_rows)) == HA_POS_ERROR) { - delete select; free_io_cache(table); - DBUG_RETURN(-1); + goto err; } /* Filesort has already found and selected the rows we want to update, @@ -214,10 +214,7 @@ int mysql_update(THD *thd, IO_CACHE tempfile; if (open_cached_file(&tempfile, mysql_tmpdir,TEMP_PREFIX, DISK_BUFFER_SIZE, MYF(MY_WME))) - { - delete select; /* purecov: inspected */ - DBUG_RETURN(-1); - } + goto err; init_read_record(&info,thd,table,select,0,1); thd->proc_info="Searching rows for update"; @@ -234,9 +231,13 @@ int mysql_update(THD *thd, break; /* purecov: inspected */ } if (!--limit && using_limit) + { + error= -1; break; + } } } + limit= tmp_limit; end_read_record(&info); /* Change select to use tempfile */ if (select) @@ -256,10 +257,7 @@ int mysql_update(THD *thd, error=1; /* purecov: inspected */ select->file=tempfile; // Read row ptrs from this file if (error >= 0) - { - delete select; - DBUG_RETURN(-1); - } + goto err; } if (table->key_read) { @@ -272,7 +270,7 @@ int mysql_update(THD *thd, table->file->extra(HA_EXTRA_IGNORE_DUP_KEY); init_read_record(&info,thd,table,select,0,1); - ha_rows updated=0L,found=0L; + updated= found= 0; thd->count_cuted_fields=1; /* calc cuted fields */ thd->cuted_fields=0L; thd->proc_info="Updating"; @@ -283,7 +281,7 @@ int mysql_update(THD *thd, if (!(select && select->skipp_record())) { store_record(table,1); - if (fill_record(fields,values)) + if (fill_record(fields, values, 0)) break; /* purecov: inspected */ found++; if (compare_record(table, query_id)) @@ -365,6 +363,15 @@ int mysql_update(THD *thd, thd->count_cuted_fields=0; /* calc cuted fields */ free_io_cache(table); DBUG_RETURN(0); + +err: + delete select; + if (table->key_read) + { + table->key_read=0; + table->file->extra(HA_EXTRA_NO_KEYREAD); + } + DBUG_RETURN(-1); } @@ -723,7 +730,7 @@ bool multi_update::send_data(List<Item> ¬_used_values) { table->status|= STATUS_UPDATED; store_record(table,1); - if (fill_record(*fields_for_table[offset], *values_for_table[offset])) + if (fill_record(*fields_for_table[offset], *values_for_table[offset],0 )) DBUG_RETURN(1); found++; if (compare_record(table, thd->query_id)) @@ -751,7 +758,7 @@ bool multi_update::send_data(List<Item> ¬_used_values) { int error; TABLE *tmp_table= tmp_tables[offset]; - fill_record(tmp_table->field+1, *values_for_table[offset]); + fill_record(tmp_table->field+1, *values_for_table[offset], 1); found++; /* Store pointer to row */ memcpy((char*) tmp_table->field[0]->ptr, diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index f895c809366..340fbc1b3dc 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -2017,8 +2017,12 @@ sum_expr: { $$=new Item_sum_count(new Item_int((int32) 0L,1)); } | COUNT_SYM '(' in_sum_expr ')' { $$=new Item_sum_count($3); } - | COUNT_SYM '(' DISTINCT expr_list ')' - { $$=new Item_sum_count_distinct(* $4); } + | COUNT_SYM '(' DISTINCT + { Select->in_sum_expr++; } + expr_list + { Select->in_sum_expr--; } + ')' + { $$=new Item_sum_count_distinct(* $5); } | GROUP_UNIQUE_USERS '(' text_literal ',' NUM ',' NUM ',' in_sum_expr ')' { $$= new Item_sum_unique_users($3,atoi($5.str),atoi($7.str),$9); } | MIN_SYM '(' in_sum_expr ')' diff --git a/sql/time.cc b/sql/time.cc index 4fe79966404..cc8cd4fbcf9 100644 --- a/sql/time.cc +++ b/sql/time.cc @@ -251,144 +251,6 @@ void get_date_from_daynr(long daynr,uint *ret_year,uint *ret_month, DBUG_VOID_RETURN; } -/* find date from string and put it in vektor - Input: pos = "YYMMDD" OR "YYYYMMDD" in any order or - "xxxxx YYxxxMMxxxDD xxxx" where xxx is anything exept - a number. Month or day mustn't exeed 2 digits, year may be 4 digits. -*/ - - -#ifdef NOT_NEEDED - -void find_date(string pos,uint *vek,uint flag) -{ - uint length,value; - string start; - DBUG_ENTER("find_date"); - DBUG_PRINT("enter",("pos: '%s' flag: %d",pos,flag)); - - bzero((char*) vek,sizeof(int)*4); - while (*pos && !isdigit(*pos)) - pos++; - length=(uint) strlen(pos); - for (uint i=0 ; i< 3; i++) - { - start=pos; value=0; - while (isdigit(pos[0]) && - ((pos-start) < 2 || ((pos-start) < 4 && length >= 8 && - !(flag & 3)))) - { - value=value*10 + (uint) (uchar) (*pos - '0'); - pos++; - } - vek[flag & 3]=value; flag>>=2; - while (*pos && (ispunct(*pos) || isspace(*pos))) - pos++; - } - DBUG_PRINT("exit",("year: %d month: %d day: %d",vek[0],vek[1],vek[2])); - DBUG_VOID_RETURN; -} /* find_date */ - - - /* Outputs YYMMDD if input year < 100 or YYYYMMDD else */ - -static long calc_daynr_from_week(uint year,uint week,uint day) -{ - long daynr; - int weekday; - - daynr=calc_daynr(year,1,1); - if ((weekday= calc_weekday(daynr,0)) >= 3) - daynr+= (7-weekday); - else - daynr-=weekday; - - return (daynr+week*7+day-8); -} - -void convert_week_to_date(string date,uint flag,uint *res_length) -{ - string format; - uint year,vek[4]; - - find_date(date,vek,(uint) (1*4+2*16)); /* YY-WW-DD */ - year=vek[0]; - - get_date_from_daynr(calc_daynr_from_week(vek[0],vek[1],vek[2]), - &vek[0],&vek[1],&vek[2]); - *res_length=8; - format="%04d%02d%02d"; - if (year < 100) - { - vek[0]= vek[0]%100; - *res_length=6; - format="%02d%02d%02d"; - } - sprintf(date,format,vek[flag & 3],vek[(flag >> 2) & 3], - vek[(flag >> 4) & 3]); - return; -} - - /* returns YYWWDD or YYYYWWDD according to input year */ - /* flag only reflects format of input date */ - -void convert_date_to_week(string date,uint flag,uint *res_length) -{ - uint vek[4],weekday,days,year,week,day; - long daynr,first_daynr; - char buff[256],*format; - - if (! date[0]) - { - get_date(buff,0,0L); /* Use current date */ - find_date(buff+2,vek,(uint) (1*4+2*16)); /* YY-MM-DD */ - } - else - find_date(date,vek,flag); - - year= vek[0]; - daynr= calc_daynr(year,vek[1],vek[2]); - first_daynr=calc_daynr(year,1,1); - - /* Caculate year and first daynr of year */ - if (vek[1] == 1 && (weekday=calc_weekday(first_daynr,0)) >= 3 && - vek[2] <= 7-weekday) - { - if (!year--) - year=99; - first_daynr=first_daynr-calc_days_in_year(year); - } - else if (vek[1] == 12 && - (weekday=calc_weekday(first_daynr+calc_days_in_year(year)),0) < 3 && - vek[2] > 31-weekday) - { - first_daynr=first_daynr+calc_days_in_year(year); - if (year++ == 99) - year=0; - } - - /* Calulate daynr of first day of week 1 */ - if ((weekday= calc_weekday(first_daynr,0)) >= 3) - first_daynr+= (7-weekday); - else - first_daynr-=weekday; - - days=(int) (daynr-first_daynr); - week=days/7+1 ; day=calc_weekday(daynr,0)+1; - - *res_length=8; - format="%04d%02d%02d"; - if (year < 100) - { - *res_length=6; - format="%02d%02d%02d"; - } - sprintf(date,format,year,week,day); - return; -} - -#endif - /* Functions to handle periods */ ulong convert_period_to_month(ulong period) @@ -486,14 +348,14 @@ str_to_TIME(const char *str, uint length, TIME *l_time,bool fuzzy_date) else date[6]=0; - if (year_length == 2) + if (year_length == 2 && i >=2 && (date[1] || date[2])) date[0]+= (date[0] < YY_PART_YEAR ? 2000 : 1900); number_of_fields=i; while (i < 6) date[i++]=0; if (number_of_fields < 3 || date[1] > 12 || date[2] > 31 || date[3] > 23 || date[4] > 59 || date[5] > 59 || - !fuzzy_date && (date[1] == 0 || date[2] == 0)) + (!fuzzy_date && (date[1] == 0 || date[2] == 0))) { /* Only give warning for a zero date if there is some garbage after */ if (!not_zero_date) // If zero date diff --git a/support-files/MacOSX/postinstall.sh b/support-files/MacOSX/postinstall.sh index daaf10bda8f..f46f4480e3e 100644 --- a/support-files/MacOSX/postinstall.sh +++ b/support-files/MacOSX/postinstall.sh @@ -10,7 +10,7 @@ if cd @prefix@ ; then if [ ! -f data/mysql/db.frm ] ; then - ./scripts/mysql_install_db + ./scripts/mysql_install_db -IN-RPM fi if [ -d data ] ; then diff --git a/support-files/Makefile.am b/support-files/Makefile.am index 4aadd98bd1b..79ba6eec763 100644 --- a/support-files/Makefile.am +++ b/support-files/Makefile.am @@ -25,7 +25,8 @@ EXTRA_DIST = mysql.spec.sh \ mysql-log-rotate.sh \ mysql.server.sh \ binary-configure.sh \ - magic + magic \ + MySQL-shared-compat.spec.sh SUBDIRS = MacOSX @@ -35,7 +36,8 @@ pkgdata_DATA = my-small.cnf \ my-huge.cnf \ mysql-log-rotate \ mysql-@VERSION@.spec \ - binary-configure + binary-configure \ + MySQL-shared-compat.spec pkgdata_SCRIPTS = mysql.server @@ -47,7 +49,8 @@ CLEANFILES = my-small.cnf \ mysql-@VERSION@.spec \ mysql-log-rotate \ mysql.server \ - binary-configure + binary-configure \ + MySQL-shared-compat.spec mysql-@VERSION@.spec: mysql.spec diff --git a/support-files/MySQL-shared-compat.spec.sh b/support-files/MySQL-shared-compat.spec.sh new file mode 100644 index 00000000000..f569dc20f42 --- /dev/null +++ b/support-files/MySQL-shared-compat.spec.sh @@ -0,0 +1,72 @@ +# +# MySQL-shared-compat.spec +# +# RPM build instructions to create a "meta" package that includes two +# versions of the MySQL shared libraries (for compatibility with +# distributions that ship older versions of MySQL and do not provide a +# separate "MySQL-shared" package. This spec file simply repackages two +# already existing MySQL-shared RPMs into a single package. +# +# Copyright (C) 2003 MySQL AB +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the Free +# Software Foundation; either version 2 of the License, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., 59 +# Temple Place, Suite 330, Boston, MA 02111-1307 USA + +# +# Change this to match the version of the shared libs you want to include +# +%define version4 @VERSION@ +%define version3 3.23.56 + +Name: MySQL-shared-compat +Packager: Lenz Grimmer <build@mysql.com> +Vendor: MySQL AB +License: GPL +Group: Applications/Databases +URL: http://www.mysql.com/ +Autoreqprov: on +Version: %{version4} +Release: 0 +BuildRoot: %{_tmppath}/%{name}-%{version}-build +Obsoletes: MySQL-shared, mysql-shared +Provides: MySQL-shared +Summary: MySQL shared libraries for MySQL %{version4} and %{version3} +Source0: MySQL-shared-%{version4}-0.i386.rpm +Source1: MySQL-shared-%{version3}-1.i386.rpm +# No need to include the RPMs once more - they can be downloaded seperately +# if you want to rebuild this package +NoSource: 0 +NoSource: 1 +BuildRoot: %{_tmppath}/%{name}-%{version}-build + +%description +This package includes the shared libraries for both MySQL %{version3} and +MySQL %{version4}. Install this package instead of "MySQL-shared", if you +have applications installed that are dynamically linked against MySQL +3.23.xx but you want to upgrade to MySQL 4.0.xx without breaking the library +dependencies. + +%install +[ "$RPM_BUILD_ROOT" != "/" ] && [ -d $RPM_BUILD_ROOT ] && rm -rf $RPM_BUILD_ROOT; +mkdir -p $RPM_BUILD_ROOT +cd $RPM_BUILD_ROOT +rpm2cpio %{SOURCE0} | cpio -iv --make-directories +rpm2cpio %{SOURCE1} | cpio -iv --make-directories + +%clean +[ "$RPM_BUILD_ROOT" != "/" ] && [ -d $RPM_BUILD_ROOT ] && rm -rf $RPM_BUILD_ROOT; + +%files +%defattr(-, root, root) +/usr/lib/libmysqlclient* diff --git a/support-files/mysql.spec.sh b/support-files/mysql.spec.sh index 9f8b29ab697..aab3e298e14 100644 --- a/support-files/mysql.spec.sh +++ b/support-files/mysql.spec.sh @@ -146,7 +146,7 @@ languages and applications need to dynamically load and use MySQL. %package Max Release: %{release} -Summary: MySQL - server with Berkeley BD and UDF support +Summary: MySQL - server with Berkeley BD, RAID and UDF support Group: Applications/Databases Provides: mysql-Max Obsoletes: mysql-Max @@ -154,7 +154,7 @@ Requires: MySQL >= 4.0 %description Max Optional MySQL server binary that supports additional features like -Berkeley DB and User Defined Functions (UDFs). +Berkeley DB, RAID and User Defined Functions (UDFs). To activate this binary, just install this package in addition to the standard MySQL package. @@ -257,6 +257,7 @@ export PATH BuildMySQL "--enable-shared \ --with-berkeley-db \ --with-innodb \ + --with-raid \ --with-server-suffix='-Max'" # Save everything for debug @@ -534,6 +535,9 @@ fi %attr(644, root, root) /usr/lib/mysql/libmysqld.a %changelog +* Fri May 16 2003 Lenz Grimmer <lenz@mysql.com> + +- re-enabled RAID again * Wed Apr 30 2003 Lenz Grimmer <lenz@mysql.com> diff --git a/tests/big_record.pl b/tests/big_record.pl index 08547b50823..fbe94e3540f 100755 --- a/tests/big_record.pl +++ b/tests/big_record.pl @@ -11,12 +11,13 @@ use Getopt::Long; $opt_host=""; $opt_user=$opt_password=""; $opt_db="test"; -$opt_rows=200; # Test of blobs up to ($rows-1)*100000+1 bytes +$opt_rows=20; # Test of blobs up to ($rows-1)*100000+1 bytes $opt_compress=0; $opt_table="test_big_record"; +$opt_loop_count=100000; # Change this to make test harder/easier GetOptions("host=s","db=s","user=s", "password=s", "table=s", "rows=i", - "compress") || die "Aborted"; + "compress", "loop-count=i") || die "Aborted"; print "Connection to database $test_db\n"; @@ -42,12 +43,12 @@ $|=1; # Flush output to stdout to be able to monitor process for ($i=0 ; $i < $opt_rows ; $i++) { $tmp= chr(65+($i % 16)) x ($i*100000+1); - print $i," ",length($tmp),"\n"; $tmp= $dbh->quote($tmp); $dbh->do("insert into $opt_table (test) values ($tmp)") or die $DBI::errstr; + print "."; } -print "Reading records\n"; +print "\nReading records\n"; $sth=$dbh->prepare("select * from $opt_table", { "mysql_use_result" => 1}) or die $dbh->errstr; @@ -56,14 +57,40 @@ $sth->execute() or die $sth->errstr; $i=0; while (($row = $sth->fetchrow_arrayref)) { - print $row->[0]," ",length($row->[1]),"\n"; die "Record $i had wrong data in blob" if ($row->[1] ne (chr(65+($i % 16)) x ($i*100000+1))); $i++; } die "Didn't get all rows from server" if ($i != $opt_rows); -$dbh->do("drop table $opt_table") or die $DBI::errstr; +# +# Test by insert/updating/deleting random rows for a while +# -print "Test ok\n"; +print "Testing insert/update/delete\n"; + +$max_row_id= $rows; +for ($i= 0 ; $i < $opt_loop_count ; $i++) +{ + $length= int(rand 65535); + $tmp= chr(65+($i % 16)) x $length; + $tmp= $dbh->quote($tmp); + $dbh->do("insert into $opt_table (test) values ($tmp)") or die $DBI::errstr; + $max_row_id++; + $length=int(rand 65535); + $tmp= chr(65+($i % 16)) x $length; + $tmp= $dbh->quote($tmp); + $id= int(rand $max_row_id); + $dbh->do("update $opt_table set test= $tmp where auto= $id") or die $DBI::errstr; + if (($i % 2) == 1) + { + $id= int(rand $max_row_id); + $dbh->do("delete from $opt_table where auto= $id") or die $DBI::errstr; + } + print "." if ($i % ($opt_loop_count/100) == 1); +} + +# $dbh->do("drop table $opt_table") or die $DBI::errstr; + +print "\nTest ok\n"; exit 0; diff --git a/tests/grant.pl b/tests/grant.pl index e32431ad63a..3146b7b6c25 100644 --- a/tests/grant.pl +++ b/tests/grant.pl @@ -82,6 +82,7 @@ user_query("select * from mysql.user where user = '$opt_user'"); user_query("select * from mysql.db where user = '$opt_user'"); safe_query("grant select on *.* to $user,$user"); safe_query("show grants for $user"); +user_connect(0); # The following should fail user_query("insert into mysql.user (host,user) values ('error','$opt_user')",1); @@ -95,16 +96,21 @@ safe_query("grant select on $opt_database.not_exists to $opt_user",1); safe_query("grant FILE on $opt_database.test to $opt_user",1); safe_query("grant select on *.* to wrong___________user_name",1); safe_query("grant select on $opt_database.* to wrong___________user_name",1); +user_connect(0); user_query("grant select on $opt_database.test to $opt_user with grant option",1); safe_query("set password FOR ''\@''=''",1); user_query("set password FOR root\@$opt_host = password('test')",1); # Change privileges for user safe_query("revoke select on *.* from $user"); -safe_query("grant create on *.* to $user"); +safe_query("grant create,update on *.* to $user"); user_connect(0); +safe_query("flush privileges"); user_query("create table $opt_database.test (a int,b int)"); - +user_query("update $opt_database.test set b=b+1 where a > 0",1); +safe_query("show grants for $user"); +safe_query("revoke update on *.* from $user"); +user_connect(0); safe_query("grant select(c) on $opt_database.test to $user",1); safe_query("revoke select(c) on $opt_database.test from $user",1); safe_query("grant select on $opt_database.test to wrong___________user_name",1); @@ -223,8 +229,21 @@ user_query("update $opt_database.test set b=b+1",1); safe_query("grant SELECT on *.* to $user"); user_connect(0); user_query("update $opt_database.test set b=b+1"); +user_query("update $opt_database.test set b=b+1 where a > 0"); safe_query("revoke SELECT on *.* from $user"); +safe_query("grant SELECT on $opt_database.* to $user"); user_connect(0); +user_query("update $opt_database.test set b=b+1"); +user_query("update $opt_database.test set b=b+1 where a > 0"); +safe_query("grant UPDATE on *.* to $user"); +user_connect(0); +user_query("update $opt_database.test set b=b+1"); +user_query("update $opt_database.test set b=b+1 where a > 0"); +safe_query("revoke UPDATE on *.* from $user"); +safe_query("revoke SELECT on $opt_database.* from $user"); +user_connect(0); +user_query("update $opt_database.test set b=b+1 where a > 0",1); +user_query("update $opt_database.test set b=b+1",1); # Add one privilege at a time until the user has all privileges user_query("select * from test",1); diff --git a/tests/grant.res b/tests/grant.res index a50f73740c6..f208241d989 100644 --- a/tests/grant.res +++ b/tests/grant.res @@ -28,6 +28,7 @@ grant select on *.* to grant_user@localhost,grant_user@localhost show grants for grant_user@localhost GRANT SELECT ON *.* TO 'grant_user'@'localhost' +Connecting grant_user insert into mysql.user (host,user) values ('error','grant_user') Error in execute: insert command denied to user: 'grant_user@localhost' for table 'user' update mysql.user set host='error' WHERE user='grant_user' @@ -50,6 +51,7 @@ grant select on *.* to wrong___________user_name Error in execute: The host or user argument to GRANT is too long grant select on grant_test.* to wrong___________user_name Error in execute: The host or user argument to GRANT is too long +Connecting grant_user grant select on grant_test.test to grant_user with grant option Error in execute: grant command denied to user: 'grant_user@localhost' for table 'test' set password FOR ''@''='' @@ -57,9 +59,17 @@ Error in execute: Can't find any matching row in the user table set password FOR root@localhost = password('test') Error in execute: Access denied for user: 'grant_user@localhost' to database 'mysql' revoke select on *.* from grant_user@localhost -grant create on *.* to grant_user@localhost +grant create,update on *.* to grant_user@localhost Connecting grant_user +flush privileges create table grant_test.test (a int,b int) +update grant_test.test set b=b+1 where a > 0 +Error in execute: SELECT command denied to user: 'grant_user@localhost' for column 'a' in table 'test' +show grants for grant_user@localhost +GRANT UPDATE, CREATE ON *.* TO 'grant_user'@'localhost' + +revoke update on *.* from grant_user@localhost +Connecting grant_user grant select(c) on grant_test.test to grant_user@localhost Error in execute: Unknown column 'c' in 'test' revoke select(c) on grant_test.test from grant_user@localhost @@ -200,8 +210,23 @@ Error in execute: SELECT command denied to user: 'grant_user@localhost' for colu grant SELECT on *.* to grant_user@localhost Connecting grant_user update grant_test.test set b=b+1 +update grant_test.test set b=b+1 where a > 0 revoke SELECT on *.* from grant_user@localhost +grant SELECT on grant_test.* to grant_user@localhost Connecting grant_user +update grant_test.test set b=b+1 +update grant_test.test set b=b+1 where a > 0 +grant UPDATE on *.* to grant_user@localhost +Connecting grant_user +update grant_test.test set b=b+1 +update grant_test.test set b=b+1 where a > 0 +revoke UPDATE on *.* from grant_user@localhost +revoke SELECT on grant_test.* from grant_user@localhost +Connecting grant_user +update grant_test.test set b=b+1 where a > 0 +Error in execute: SELECT command denied to user: 'grant_user@localhost' for column 'a' in table 'test' +update grant_test.test set b=b+1 +Error in execute: SELECT command denied to user: 'grant_user@localhost' for column 'b' in table 'test' select * from test Error in execute: select command denied to user: 'grant_user@localhost' for table 'test' grant select on grant_test.test to grant_user@localhost @@ -489,7 +514,7 @@ Error in execute: Access denied for user: 'grant_user@localhost' to database 'gr grant LOCK TABLES on *.* to grant_user@localhost show grants for grant_user@localhost GRANT LOCK TABLES ON *.* TO 'grant_user'@'localhost' -GRANT SELECT, INSERT ON grant_test.test3 TO 'grant_user'@'localhost' +GRANT SELECT, INSERT ON `grant_test`.`test3` TO 'grant_user'@'localhost' select * from mysql.user where user='grant_user' 127.0.0.1 grant_user 7f70e8b858ee6782 N N N N N N N N N N N N N N N N N N N N N 0 0 0 diff --git a/tests/table_types.pl b/tests/table_types.pl index 8198cd9ba86..4dbcdcb975c 100755 --- a/tests/table_types.pl +++ b/tests/table_types.pl @@ -66,13 +66,6 @@ $dbh = $server->connect(); #### $table_name="bench1"; -<<<<<<< table_types.pl -||||||| 1.2 -test("n","type=isam","char"); test("m","type=myisam pack_keys=1","char"); exit(1); - -======= - ->>>>>>> /tmp/T4a17019 test($table_name,"type=isam","char"); test($table_name,"type=myisam pack_keys=0","char"); test($table_name,"type=myisam pack_keys=0","char"); @@ -91,7 +84,7 @@ exit (0); sub test { my ($name,$options,$chartype)=@_; - + print "\nTesting with options: '$options'\n"; $dbh->do("drop table $name"); do_many($dbh,$server->create("$name", @@ -102,23 +95,23 @@ sub test { ["primary key (id,id2)", "index index_id3 (id3)"], $options)); - + if ($opt_lock_tables) { $sth = $dbh->do("LOCK TABLES $name WRITE") || die $DBI::errstr; } - + if ($opt_fast && defined($server->{vacuum})) { $server->vacuum(\$dbh,1); } - + #### #### Insert $total_rows records in order, in reverse order and random. #### - + $loop_time=new Benchmark; - + if ($opt_fast_insert) { $query="insert into $name values "; @@ -127,11 +120,11 @@ sub test { { $query="insert into $name (id,id2,id3,dummy1) values "; } - + if (($opt_fast || $opt_fast_insert) && $limits->{'multi_value_insert'}) { $query_size=$server->{'limits'}->{'query_size'}; - + print "Inserting $opt_loop_count multiple-value rows in order\n"; $res=$query; for ($i=0 ; $i < $opt_loop_count ; $i++) @@ -186,7 +179,7 @@ sub test { { $sth = $dbh->do($query . "($i,$i,$i,'ABCDEFGHIJ')") or die $DBI::errstr; } - + print "Inserting $opt_loop_count rows in reverse order\n"; for ($i=0 ; $i < $opt_loop_count ; $i++) { @@ -195,25 +188,25 @@ sub test { ($total_rows-1-$i) . ",'BCDEFGHIJK')") or die $DBI::errstr; } - + print "Inserting $opt_loop_count rows in random order\n"; - + for ($i=0 ; $i < $opt_loop_count ; $i++) { $sth = $dbh->do($query . "(". $random[$i] . "," . $random[$i] . "," . $random[$i] . ",'CDEFGHIJKL')") or die $DBI::errstr; } } - + $end_time=new Benchmark; print "Time for insert (" . ($total_rows) . "): " . timestr(timediff($end_time, $loop_time),"all") . "\n\n"; - + if ($opt_fast && defined($server->{vacuum})) { $server->vacuum(\$dbh,1); } - + $sth=$dbh->prepare("show table status like '$name'"); $sth->execute || die "Show table status returned error: $DBI::errstr\n"; while (@row = $sth->fetchrow_array) |