diff options
44 files changed, 666 insertions, 245 deletions
diff --git a/client/mysql.cc b/client/mysql.cc index f9da1a8737b..dfbe394d81c 100644 --- a/client/mysql.cc +++ b/client/mysql.cc @@ -4082,7 +4082,7 @@ static int com_source(String *buffer, char *line) If we got an error during source operation, don't abort the client if ignore_errors is set */ - if (error && !batch_abort_on_error && ignore_errors) + if (error && ignore_errors) error= -1; // Ignore error return error; } diff --git a/client/mysqltest.cc b/client/mysqltest.cc index fbd76445e37..cb59c93365b 100644 --- a/client/mysqltest.cc +++ b/client/mysqltest.cc @@ -1875,7 +1875,7 @@ void check_result() if (access(reject_file, W_OK) == 0) { /* Result file directory is writable, save reject file there */ - fn_format(reject_file, result_file_name, NULL, + fn_format(reject_file, result_file_name, "", ".reject", MY_REPLACE_EXT); } else diff --git a/dbug/dbug.c b/dbug/dbug.c index 59f7f4e10c5..d3d8c604db9 100644 --- a/dbug/dbug.c +++ b/dbug/dbug.c @@ -286,7 +286,7 @@ typedef struct _db_code_state_ { #define ListDel(A,B,C) ListAddDel(A,B,C,EXCLUDE) static struct link *ListAddDel(struct link *, const char *, const char *, int); static struct link *ListCopy(struct link *); -static int InList(struct link *linkp,const char *cp); +static int InList(struct link *linkp,const char *cp, int exact_match); static uint ListFlags(struct link *linkp); static void FreeList(struct link *linkp); @@ -1581,13 +1581,13 @@ static struct link *ListCopy(struct link *orig) * */ -static int InList(struct link *linkp, const char *cp) +static int InList(struct link *linkp, const char *cp, int exact_match) { int result; for (result=MATCHED; linkp != NULL; linkp= linkp->next_link) { - if (!fnmatch(linkp->str, cp, 0)) + if (!(exact_match ? strcmp(linkp->str,cp) : fnmatch(linkp->str, cp, 0))) return linkp->flags; if (!(linkp->flags & EXCLUDE)) result=NOT_MATCHED; @@ -1752,8 +1752,8 @@ void _db_end_() static int DoTrace(CODE_STATE *cs) { if ((cs->stack->maxdepth == 0 || cs->level <= cs->stack->maxdepth) && - InList(cs->stack->processes, cs->process) & (MATCHED|INCLUDE)) - switch(InList(cs->stack->functions, cs->func)) { + InList(cs->stack->processes, cs->process, 0) & (MATCHED|INCLUDE)) + switch(InList(cs->stack->functions, cs->func, 0)) { case INCLUDE|SUBDIR: return ENABLE_TRACE; case INCLUDE: return DO_TRACE; case MATCHED|SUBDIR: @@ -1790,10 +1790,10 @@ static int DoTrace(CODE_STATE *cs) #ifndef THREAD static BOOLEAN DoProfile(CODE_STATE *cs) { - return PROFILING && - cs->level <= cs->stack->maxdepth && - InList(cs->stack->p_functions, cs->func) & (INCLUDE|MATCHED) && - InList(cs->stack->processes, cs->process) & (INCLUDE|MATCHED); + return (PROFILING && + cs->level <= cs->stack->maxdepth && + InList(cs->stack->p_functions, cs->func, 0) & (INCLUDE|MATCHED) && + InList(cs->stack->processes, cs->process, 0) & (INCLUDE|MATCHED)); } #endif @@ -1827,10 +1827,10 @@ FILE *_db_fp_(void) BOOLEAN _db_keyword_(CODE_STATE *cs, const char *keyword, int strict) { get_code_state_if_not_set_or_return FALSE; - strict=strict ? INCLUDE : INCLUDE|MATCHED; + int match= strict ? INCLUDE : INCLUDE|MATCHED; - return DEBUGGING && DoTrace(cs) & DO_TRACE && - InList(cs->stack->keywords, keyword) & strict; + return (DEBUGGING && DoTrace(cs) & DO_TRACE && + InList(cs->stack->keywords, keyword, strict) & match); } /* diff --git a/include/my_base.h b/include/my_base.h index 00a13f28f57..d8b4d8b39b3 100644 --- a/include/my_base.h +++ b/include/my_base.h @@ -50,6 +50,7 @@ #define HA_OPEN_COPY 256 /* Open copy (for repair) */ /* Internal temp table, used for temporary results */ #define HA_OPEN_INTERNAL_TABLE 512 +#define HA_OPEN_MERGE_TABLE 1024 /* The following is parameter to ha_rkey() how to use key */ @@ -196,6 +197,7 @@ enum ha_extra_function { */ HA_EXTRA_ATTACH_CHILDREN, HA_EXTRA_DETACH_CHILDREN, + HA_EXTRA_DETACH_CHILD, /* Inform handler we will force a close as part of flush */ HA_EXTRA_PREPARE_FOR_FORCED_CLOSE }; diff --git a/include/thr_lock.h b/include/thr_lock.h index 14d93a47e5a..08cc8bd5408 100644 --- a/include/thr_lock.h +++ b/include/thr_lock.h @@ -82,6 +82,12 @@ enum enum_thr_lock_result { THR_LOCK_SUCCESS= 0, THR_LOCK_ABORTED= 1, THR_LOCK_WAIT_TIMEOUT= 2, THR_LOCK_DEADLOCK= 3 }; +/* Priority for locks */ +#define THR_LOCK_LATE_PRIV 1 /* For locks to be merged with org lock */ +#define THR_LOCK_MERGE_PRIV 2 /* For merge tables */ + +#define THR_UNLOCK_UPDATE_STATUS 1 + extern ulong max_write_lock_count; extern ulong table_lock_wait_timeout; extern my_bool thr_lock_inited; @@ -116,9 +122,10 @@ typedef struct st_thr_lock_data { struct st_thr_lock_data *next,**prev; struct st_thr_lock *lock; pthread_cond_t *cond; - enum thr_lock_type type; void *status_param; /* Param to status functions */ void *debug_print_param; + enum thr_lock_type type; + uint priority; } THR_LOCK_DATA; struct st_lock_list { @@ -138,8 +145,10 @@ typedef struct st_thr_lock { void (*get_status)(void*, my_bool); /* When one gets a lock */ void (*copy_status)(void*,void*); void (*update_status)(void*); /* Before release of write */ - void (*restore_status)(void*); /* Before release of read */ + void (*restore_status)(void*); /* Before release of read */ + my_bool (*start_trans)(void*); /* When all locks are taken */ my_bool (*check_status)(void *); + void (*fix_status)(void *, void *);/* For thr_merge_locks() */ my_bool allow_multiple_concurrent_insert; } THR_LOCK; @@ -154,13 +163,11 @@ void thr_lock_init(THR_LOCK *lock); void thr_lock_delete(THR_LOCK *lock); void thr_lock_data_init(THR_LOCK *lock,THR_LOCK_DATA *data, void *status_param); -enum enum_thr_lock_result thr_lock(THR_LOCK_DATA *data, - THR_LOCK_OWNER *owner, - enum thr_lock_type lock_type); -void thr_unlock(THR_LOCK_DATA *data); +void thr_unlock(THR_LOCK_DATA *data, uint unlock_flags); enum enum_thr_lock_result thr_multi_lock(THR_LOCK_DATA **data, uint count, THR_LOCK_OWNER *owner); -void thr_multi_unlock(THR_LOCK_DATA **data,uint count); +void thr_multi_unlock(THR_LOCK_DATA **data,uint count, uint unlock_flags); +void thr_merge_locks(THR_LOCK_DATA **data, uint org_count, uint new_count); void thr_abort_locks(THR_LOCK *lock, my_bool upgrade_lock); my_bool thr_abort_locks_for_thread(THR_LOCK *lock, my_thread_id thread); void thr_print_locks(void); /* For debugging */ diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index c77f15bcb14..711185e9453 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -4284,6 +4284,7 @@ sub extract_warning_lines ($) { qr/Slave SQL thread retried transaction/, qr/Slave \(additional info\)/, qr/Incorrect information in file/, + qr/Incorrect key file for table .*crashed.*/, qr/Slave I\/O: Get master SERVER_ID failed with error:.*/, qr/Slave I\/O: Get master clock failed with error:.*/, qr/Slave I\/O: Get master COLLATION_SERVER failed with error:.*/, diff --git a/mysql-test/r/merge.result b/mysql-test/r/merge.result index 83dae077312..e4bcf775bab 100644 --- a/mysql-test/r/merge.result +++ b/mysql-test/r/merge.result @@ -1574,7 +1574,7 @@ UNLOCK TABLES; # # Trigger on parent DELETE FROM t4 WHERE c1 = 4; -CREATE TRIGGER t4_ai AFTER INSERT ON t4 FOR EACH ROW SET @a=1; +CREATE TRIGGER t4_ai1 AFTER INSERT ON t4 FOR EACH ROW SET @a=1; SET @a=0; INSERT INTO t4 VALUES (4); SELECT @a; @@ -1586,10 +1586,13 @@ c1 2 3 4 -DROP TRIGGER t4_ai; +DROP TRIGGER t4_ai1; +CHECK TABLE t3; +Table Op Msg_type Msg_text +test.t3 check status OK # Trigger on parent under LOCK TABLES LOCK TABLES t3 WRITE, t2 WRITE, t4 WRITE, t1 WRITE; -CREATE TRIGGER t4_ai AFTER INSERT ON t4 FOR EACH ROW SET @a=1; +CREATE TRIGGER t4_ai2 AFTER INSERT ON t4 FOR EACH ROW SET @a=1; SET @a=0; INSERT INTO t4 VALUES (4); SELECT @a; @@ -1602,12 +1605,15 @@ c1 3 4 4 -DROP TRIGGER t4_ai; +DROP TRIGGER t4_ai2; UNLOCK TABLES; +CHECK TABLE t3; +Table Op Msg_type Msg_text +test.t3 check status OK # # Trigger on child DELETE FROM t4 WHERE c1 = 4; -CREATE TRIGGER t3_ai AFTER INSERT ON t3 FOR EACH ROW SET @a=1; +CREATE TRIGGER t3_ai3 AFTER INSERT ON t3 FOR EACH ROW SET @a=1; SET @a=0; INSERT INTO t4 VALUES (4); SELECT @a; @@ -1624,10 +1630,13 @@ c1 3 4 33 -DROP TRIGGER t3_ai; +DROP TRIGGER t3_ai3; +CHECK TABLE t3; +Table Op Msg_type Msg_text +test.t3 check status OK # Trigger on child under LOCK TABLES LOCK TABLES t3 WRITE, t2 WRITE, t4 WRITE, t1 WRITE; -CREATE TRIGGER t3_ai AFTER INSERT ON t3 FOR EACH ROW SET @a=1; +CREATE TRIGGER t3_ai4 AFTER INSERT ON t3 FOR EACH ROW SET @a=1; SET @a=0; INSERT INTO t4 VALUES (4); SELECT @a; @@ -1647,11 +1656,17 @@ c1 33 33 DELETE FROM t4 WHERE c1 = 33; -DROP TRIGGER t3_ai; +DROP TRIGGER t3_ai4; +CHECK TABLE t3; +Table Op Msg_type Msg_text +test.t3 check status OK # # Trigger with table use on child DELETE FROM t4 WHERE c1 = 4; -CREATE TRIGGER t3_ai AFTER INSERT ON t3 FOR EACH ROW INSERT INTO t2 VALUES(22); +CREATE TRIGGER t3_ai5 AFTER INSERT ON t3 FOR EACH ROW INSERT INTO t2 VALUES(22); +SELECT COUNT(*) FROM t2; +COUNT(*) +1 INSERT INTO t4 VALUES (4); SELECT * FROM t4 ORDER BY c1; c1 @@ -1670,10 +1685,15 @@ c1 33 DELETE FROM t4 WHERE c1 = 22; DELETE FROM t4 WHERE c1 = 33; -DROP TRIGGER t3_ai; +DROP TRIGGER t3_ai5; +UNLOCK TABLES; +CHECK TABLE t2,t3; +Table Op Msg_type Msg_text +test.t2 check status OK +test.t3 check status OK # Trigger with table use on child under LOCK TABLES LOCK TABLES t3 WRITE, t2 WRITE, t4 WRITE, t1 WRITE; -CREATE TRIGGER t3_ai AFTER INSERT ON t3 FOR EACH ROW INSERT INTO t2 VALUES(22); +CREATE TRIGGER t3_ai6 AFTER INSERT ON t3 FOR EACH ROW INSERT INTO t2 VALUES(22); INSERT INTO t4 VALUES (4); SELECT * FROM t4 ORDER BY c1; c1 @@ -1692,10 +1712,44 @@ c1 4 22 33 -DROP TRIGGER t3_ai; +DROP TRIGGER t3_ai6; +UNLOCK TABLES; +check table t2,t3,t4; +Table Op Msg_type Msg_text +test.t2 check status OK +test.t3 check status OK +test.t4 check status OK DELETE FROM t4 WHERE c1 = 22; DELETE FROM t4 WHERE c1 = 33; +# Trigger with table use on child under different LOCK TABLES +DELETE FROM t4 WHERE c1 = 4; +LOCK TABLES t4 WRITE,t3 WRITE, t2 WRITE, t1 WRITE; +CREATE TRIGGER t3_ai7 AFTER INSERT ON t3 FOR EACH ROW INSERT INTO t2 VALUES(22); +INSERT INTO t4 VALUES (4); +SELECT * FROM t4 ORDER BY c1; +c1 +1 +2 +3 +4 +INSERT INTO t3 VALUES (33); +SELECT * FROM t4 ORDER BY c1; +c1 +1 +2 +3 +4 +22 +33 +DROP TRIGGER t3_ai7; UNLOCK TABLES; +check table t2,t3,t4; +Table Op Msg_type Msg_text +test.t2 check status OK +test.t3 check status OK +test.t4 check status OK +DELETE FROM t4 WHERE c1 = 22; +DELETE FROM t4 WHERE c1 = 33; # # Repair # @@ -1711,7 +1765,6 @@ c1 2 3 4 -4 LOCK TABLES t3 WRITE, t2 WRITE, t4 WRITE, t1 WRITE; REPAIR TABLE t4; Table Op Msg_type Msg_text @@ -1725,7 +1778,6 @@ c1 2 3 4 -4 UNLOCK TABLES; # # Optimize @@ -1742,7 +1794,6 @@ c1 2 3 4 -4 LOCK TABLES t3 WRITE, t2 WRITE, t4 WRITE, t1 WRITE; OPTIMIZE TABLE t4; Table Op Msg_type Msg_text @@ -1756,14 +1807,13 @@ c1 2 3 4 -4 UNLOCK TABLES; # # Checksum # CHECKSUM TABLE t4; Table Checksum -test.t4 46622073 +test.t4 149057747 CHECKSUM TABLE t2; Table Checksum test.t2 3700403066 @@ -1773,11 +1823,10 @@ c1 2 3 4 -4 LOCK TABLES t3 WRITE, t2 WRITE, t4 WRITE, t1 WRITE; CHECKSUM TABLE t4; Table Checksum -test.t4 46622073 +test.t4 149057747 CHECKSUM TABLE t2; Table Checksum test.t2 3700403066 @@ -1787,7 +1836,6 @@ c1 2 3 4 -4 UNLOCK TABLES; # # Insert delayed @@ -1801,7 +1849,6 @@ c1 2 3 4 -4 33 LOCK TABLES t3 WRITE, t2 WRITE, t4 WRITE, t1 WRITE; INSERT DELAYED INTO t4 VALUES(444); @@ -1814,7 +1861,6 @@ c1 2 3 4 -4 33 UNLOCK TABLES; DROP TABLE t1, t2, t3, t4; diff --git a/mysql-test/r/merge_debug.result b/mysql-test/r/merge_debug.result new file mode 100644 index 00000000000..b857ff597f2 --- /dev/null +++ b/mysql-test/r/merge_debug.result @@ -0,0 +1,24 @@ +set global storage_engine=myisam; +set session storage_engine=myisam; +drop table if exists crashed,t2,t3,t4; +SET @orig_debug=@@debug; +CREATE TABLE crashed (c1 INT); +CREATE TABLE t2 (c1 INT); +CREATE TABLE t3 (c1 INT); +CREATE TABLE t4 (c1 INT) ENGINE=MRG_MYISAM UNION=(crashed,t2,t3) INSERT_METHOD=LAST; +INSERT INTO crashed VALUES (10); +INSERT INTO t2 VALUES (20); +INSERT INTO t3 VALUES (30); +LOCK TABLES t3 WRITE, t2 WRITE, t4 WRITE, crashed WRITE; +SET GLOBAL debug="+d,*,myisam_pretend_crashed_table_on_open"; +CREATE TRIGGER t1_ai AFTER INSERT ON crashed FOR EACH ROW INSERT INTO t2 VALUES(29); +SET GLOBAL debug=@orig_debug; +INSERT INTO t4 VALUES (39); +ERROR HY000: Table 't4' was not locked with LOCK TABLES +INSERT INTO crashed VALUES (11); +ERROR HY000: Table 'crashed' was not locked with LOCK TABLES +INSERT INTO t2 VALUES (21); +INSERT INTO t3 VALUES (31); +UNLOCK TABLES; +DROP TRIGGER t1_ai; +DROP TABLE t4,crashed,t2,t3; diff --git a/mysql-test/r/udf_query_cache.result b/mysql-test/r/udf_query_cache.result index 01dac554142..8c25c127012 100644 --- a/mysql-test/r/udf_query_cache.result +++ b/mysql-test/r/udf_query_cache.result @@ -3,6 +3,7 @@ CREATE FUNCTION metaphon RETURNS STRING SONAME "UDF_EXAMPLE_LIB"; create table t1 (a char); set GLOBAL query_cache_size=1355776; reset query cache; +flush status; select metaphon('MySQL') from t1; metaphon('MySQL') show status like "Qcache_hits"; diff --git a/mysql-test/suite/parts/r/partition_repair_myisam.result b/mysql-test/suite/parts/r/partition_repair_myisam.result index 2d0a26b397c..4af00ddcc6d 100644 --- a/mysql-test/suite/parts/r/partition_repair_myisam.result +++ b/mysql-test/suite/parts/r/partition_repair_myisam.result @@ -408,7 +408,7 @@ ALTER TABLE t1_will_crash CHECK PARTITION p6; Table Op Msg_type Msg_text test.t1_will_crash check warning Size of datafile is: 868 Should be: 604 test.t1_will_crash check error Record-count is not ok; is 8 Should be: 7 -test.t1_will_crash check warning Found 10 key parts. Should be: 7 +test.t1_will_crash check warning Found 10 parts. Should be: 7 test.t1_will_crash check error Partition p6 returned error test.t1_will_crash check error Corrupt ALTER TABLE t1_will_crash REPAIR PARTITION p6; diff --git a/mysql-test/t/merge.test b/mysql-test/t/merge.test index df99d6150ab..4e0ca615f99 100644 --- a/mysql-test/t/merge.test +++ b/mysql-test/t/merge.test @@ -1119,35 +1119,38 @@ UNLOCK TABLES; --echo # --echo # Trigger on parent DELETE FROM t4 WHERE c1 = 4; -CREATE TRIGGER t4_ai AFTER INSERT ON t4 FOR EACH ROW SET @a=1; +CREATE TRIGGER t4_ai1 AFTER INSERT ON t4 FOR EACH ROW SET @a=1; SET @a=0; INSERT INTO t4 VALUES (4); SELECT @a; SELECT * FROM t4 ORDER BY c1; -DROP TRIGGER t4_ai; +DROP TRIGGER t4_ai1; +CHECK TABLE t3; --echo # Trigger on parent under LOCK TABLES LOCK TABLES t3 WRITE, t2 WRITE, t4 WRITE, t1 WRITE; -CREATE TRIGGER t4_ai AFTER INSERT ON t4 FOR EACH ROW SET @a=1; +CREATE TRIGGER t4_ai2 AFTER INSERT ON t4 FOR EACH ROW SET @a=1; SET @a=0; INSERT INTO t4 VALUES (4); SELECT @a; SELECT * FROM t4 ORDER BY c1; -DROP TRIGGER t4_ai; +DROP TRIGGER t4_ai2; UNLOCK TABLES; +CHECK TABLE t3; --echo # --echo # Trigger on child DELETE FROM t4 WHERE c1 = 4; -CREATE TRIGGER t3_ai AFTER INSERT ON t3 FOR EACH ROW SET @a=1; +CREATE TRIGGER t3_ai3 AFTER INSERT ON t3 FOR EACH ROW SET @a=1; SET @a=0; INSERT INTO t4 VALUES (4); SELECT @a; INSERT INTO t3 VALUES (33); SELECT @a; SELECT * FROM t4 ORDER BY c1; -DROP TRIGGER t3_ai; +DROP TRIGGER t3_ai3; +CHECK TABLE t3; --echo # Trigger on child under LOCK TABLES LOCK TABLES t3 WRITE, t2 WRITE, t4 WRITE, t1 WRITE; -CREATE TRIGGER t3_ai AFTER INSERT ON t3 FOR EACH ROW SET @a=1; +CREATE TRIGGER t3_ai4 AFTER INSERT ON t3 FOR EACH ROW SET @a=1; SET @a=0; INSERT INTO t4 VALUES (4); SELECT @a; @@ -1155,29 +1158,47 @@ INSERT INTO t3 VALUES (33); SELECT @a; SELECT * FROM t4 ORDER BY c1; DELETE FROM t4 WHERE c1 = 33; -DROP TRIGGER t3_ai; +DROP TRIGGER t3_ai4; +CHECK TABLE t3; --echo # --echo # Trigger with table use on child DELETE FROM t4 WHERE c1 = 4; -CREATE TRIGGER t3_ai AFTER INSERT ON t3 FOR EACH ROW INSERT INTO t2 VALUES(22); +CREATE TRIGGER t3_ai5 AFTER INSERT ON t3 FOR EACH ROW INSERT INTO t2 VALUES(22); +SELECT COUNT(*) FROM t2; INSERT INTO t4 VALUES (4); SELECT * FROM t4 ORDER BY c1; INSERT INTO t3 VALUES (33); SELECT * FROM t4 ORDER BY c1; DELETE FROM t4 WHERE c1 = 22; DELETE FROM t4 WHERE c1 = 33; -DROP TRIGGER t3_ai; +DROP TRIGGER t3_ai5; +UNLOCK TABLES; +CHECK TABLE t2,t3; --echo # Trigger with table use on child under LOCK TABLES LOCK TABLES t3 WRITE, t2 WRITE, t4 WRITE, t1 WRITE; -CREATE TRIGGER t3_ai AFTER INSERT ON t3 FOR EACH ROW INSERT INTO t2 VALUES(22); +CREATE TRIGGER t3_ai6 AFTER INSERT ON t3 FOR EACH ROW INSERT INTO t2 VALUES(22); INSERT INTO t4 VALUES (4); SELECT * FROM t4 ORDER BY c1; INSERT INTO t3 VALUES (33); SELECT * FROM t4 ORDER BY c1; -DROP TRIGGER t3_ai; +DROP TRIGGER t3_ai6; +UNLOCK TABLES; +check table t2,t3,t4; DELETE FROM t4 WHERE c1 = 22; DELETE FROM t4 WHERE c1 = 33; +--echo # Trigger with table use on child under different LOCK TABLES +DELETE FROM t4 WHERE c1 = 4; +LOCK TABLES t4 WRITE,t3 WRITE, t2 WRITE, t1 WRITE; +CREATE TRIGGER t3_ai7 AFTER INSERT ON t3 FOR EACH ROW INSERT INTO t2 VALUES(22); +INSERT INTO t4 VALUES (4); +SELECT * FROM t4 ORDER BY c1; +INSERT INTO t3 VALUES (33); +SELECT * FROM t4 ORDER BY c1; +DROP TRIGGER t3_ai7; UNLOCK TABLES; +check table t2,t3,t4; +DELETE FROM t4 WHERE c1 = 22; +DELETE FROM t4 WHERE c1 = 33; # --echo # --echo # Repair diff --git a/mysql-test/t/merge_debug.test b/mysql-test/t/merge_debug.test new file mode 100644 index 00000000000..2e30cf88b5d --- /dev/null +++ b/mysql-test/t/merge_debug.test @@ -0,0 +1,42 @@ +# +# Test failures with MERGE +# + +--source include/have_debug.inc + +let $default=`select @@global.storage_engine`; +set global storage_engine=myisam; +set session storage_engine=myisam; + +--disable_warnings +drop table if exists crashed,t2,t3,t4; +--enable_warnings + +SET @orig_debug=@@debug; + +# +# Check that MariaDB handles reopen that fails without crashing +# +CREATE TABLE crashed (c1 INT); +CREATE TABLE t2 (c1 INT); +CREATE TABLE t3 (c1 INT); +CREATE TABLE t4 (c1 INT) ENGINE=MRG_MYISAM UNION=(crashed,t2,t3) INSERT_METHOD=LAST; +INSERT INTO crashed VALUES (10); +INSERT INTO t2 VALUES (20); +INSERT INTO t3 VALUES (30); + +LOCK TABLES t3 WRITE, t2 WRITE, t4 WRITE, crashed WRITE; +SET GLOBAL debug="+d,*,myisam_pretend_crashed_table_on_open"; +--disable_warnings +CREATE TRIGGER t1_ai AFTER INSERT ON crashed FOR EACH ROW INSERT INTO t2 VALUES(29); +--enable_warnings +SET GLOBAL debug=@orig_debug; +--error ER_TABLE_NOT_LOCKED +INSERT INTO t4 VALUES (39); +--error ER_TABLE_NOT_LOCKED +INSERT INTO crashed VALUES (11); +INSERT INTO t2 VALUES (21); +INSERT INTO t3 VALUES (31); +UNLOCK TABLES; +DROP TRIGGER t1_ai; +DROP TABLE t4,crashed,t2,t3; diff --git a/mysql-test/t/udf_query_cache.test b/mysql-test/t/udf_query_cache.test index ce7bd43ea1f..b0037973631 100644 --- a/mysql-test/t/udf_query_cache.test +++ b/mysql-test/t/udf_query_cache.test @@ -20,6 +20,7 @@ create table t1 (a char); set GLOBAL query_cache_size=1355776; reset query cache; +flush status; select metaphon('MySQL') from t1; show status like "Qcache_hits"; diff --git a/mysys/my_getopt.c b/mysys/my_getopt.c index f9a2c17ae7c..7281f2e1420 100644 --- a/mysys/my_getopt.c +++ b/mysys/my_getopt.c @@ -614,7 +614,6 @@ static int setval(const struct my_option *opts, void *value, char *argument, my_bool set_maximum_value) { int err= 0; - int pos; if (value && argument) { diff --git a/mysys/my_symlink2.c b/mysys/my_symlink2.c index 7c3ddbb911c..bc7ac751fad 100644 --- a/mysys/my_symlink2.c +++ b/mysys/my_symlink2.c @@ -34,8 +34,8 @@ File my_create_with_symlink(const char *linkname, const char *filename, char abs_linkname[FN_REFLEN]; DBUG_ENTER("my_create_with_symlink"); DBUG_PRINT("enter", ("linkname: %s filename: %s", - linkname ? linkname : "(null)", - filename ? filename : "(null)")); + linkname ? linkname : "(NULL)", + filename ? filename : "(NULL)")); if (my_disable_symlinks) { diff --git a/mysys/thr_lock.c b/mysys/thr_lock.c index b8aa9e5fcc0..9c8236ae0e6 100644 --- a/mysys/thr_lock.c +++ b/mysys/thr_lock.c @@ -63,6 +63,11 @@ update_status: A storage engine should also call update_status internally in the ::external_lock(F_UNLCK) method. In MyISAM and CSV this functions updates the length of the datafile. + MySQL does in some exceptional cases (when doing DLL statements on + open tables calls thr_unlock() followed by thr_lock() without calling + ::external_lock() in between. In this case thr_unlock() is called with + the THR_UNLOCK_UPDATE_STATUS flag and thr_unlock() will call + update_status for write locks. get_status: When one gets a lock this functions is called. In MyISAM this stores the number of rows and size of the datafile @@ -105,8 +110,30 @@ static inline pthread_cond_t *get_cond(void) return &my_thread_var->suspend; } + +/* + Priority for locks (decides in which order locks are locked) + We want all write locks to be first, followed by read locks. + Locks from MERGE tables has a little lower priority than other + locks, to allow one to release merge tables without having + to unlock and re-lock other locks. + The lower the number, the higher the priority for the lock. + Read locks should have 4, write locks should have 0. + UNLOCK is 8, to force these last in thr_merge_locks. + For MERGE tables we add 2 (THR_LOCK_MERGE_PRIV) to the lock priority. + THR_LOCK_LATE_PRIV (1) is used when one locks other tables to be merged + with existing locks. This way we prioritize the original locks over the + new locks. +*/ + +static uint lock_priority[(uint)TL_WRITE_ONLY+1] = +{ 8, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0}; + +#define LOCK_CMP(A,B) ((uchar*) ((A)->lock) + lock_priority[(uint) (A)->type] + (A)->priority < (uchar*) ((B)->lock) + lock_priority[(uint) (B)->type] + (B)->priority) + + /* -** For the future (now the thread specific cond is alloced by my_pthread.c) + For the future (now the thread specific cond is alloced by my_pthread.c) */ my_bool init_thr_lock() @@ -530,7 +557,7 @@ wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data, } -enum enum_thr_lock_result +static enum enum_thr_lock_result thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner, enum thr_lock_type lock_type) { @@ -544,6 +571,7 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner, data->cond=0; /* safety */ data->type=lock_type; data->owner= owner; /* Must be reset ! */ + data->priority&= ~THR_LOCK_LATE_PRIV; VOID(pthread_mutex_lock(&lock->mutex)); DBUG_PRINT("lock",("data: 0x%lx thread: 0x%lx lock: 0x%lx type: %d", (long) data, data->owner->info->thread_id, @@ -808,7 +836,7 @@ static inline void free_all_read_locks(THR_LOCK *lock, /* Unlock lock and free next thread on same lock */ -void thr_unlock(THR_LOCK_DATA *data) +void thr_unlock(THR_LOCK_DATA *data, uint unlock_flags) { THR_LOCK *lock=data->lock; enum thr_lock_type lock_type=data->type; @@ -832,6 +860,21 @@ void thr_unlock(THR_LOCK_DATA *data) } else lock->write.last=data->prev; + + if (unlock_flags & THR_UNLOCK_UPDATE_STATUS) + { + /* External lock was not called; Update or restore status */ + if (lock_type >= TL_WRITE_CONCURRENT_INSERT) + { + if (lock->update_status) + (*lock->update_status)(data->status_param); + } + else + { + if (lock->restore_status) + (*lock->restore_status)(data->status_param); + } + } if (lock_type == TL_READ_NO_INSERT) lock->read_no_write_count--; data->type=TL_UNLOCK; /* Mark unlocked */ @@ -967,14 +1010,12 @@ end: /* -** Get all locks in a specific order to avoid dead-locks -** Sort acording to lock position and put write_locks before read_locks if -** lock on same lock. + Get all locks in a specific order to avoid dead-locks + Sort acording to lock position and put write_locks before read_locks if + lock on same lock. Locks on MERGE tables has lower priority than other + locks of the same type. See comment for lock_priority. */ - -#define LOCK_CMP(A,B) ((uchar*) (A->lock) - (uint) ((A)->type) < (uchar*) (B->lock)- (uint) ((B)->type)) - static void sort_locks(THR_LOCK_DATA **data,uint count) { THR_LOCK_DATA **pos,**end,**prev,*tmp; @@ -999,18 +1040,22 @@ static void sort_locks(THR_LOCK_DATA **data,uint count) enum enum_thr_lock_result thr_multi_lock(THR_LOCK_DATA **data, uint count, THR_LOCK_OWNER *owner) { - THR_LOCK_DATA **pos,**end; + THR_LOCK_DATA **pos, **end, **first_lock; DBUG_ENTER("thr_multi_lock"); DBUG_PRINT("lock",("data: 0x%lx count: %d", (long) data, count)); + if (count > 1) sort_locks(data,count); + else if (count == 0) + DBUG_RETURN(THR_LOCK_SUCCESS); + /* lock everything */ for (pos=data,end=data+count; pos < end ; pos++) { enum enum_thr_lock_result result= thr_lock(*pos, owner, (*pos)->type); if (result != THR_LOCK_SUCCESS) { /* Aborted */ - thr_multi_unlock(data,(uint) (pos-data)); + thr_multi_unlock(data,(uint) (pos-data), 0); DBUG_RETURN(result); } #ifdef MAIN @@ -1018,63 +1063,103 @@ thr_multi_lock(THR_LOCK_DATA **data, uint count, THR_LOCK_OWNER *owner) (long) pos[0]->lock, pos[0]->type); fflush(stdout); #endif } + /* - Ensure that all get_locks() have the same status + Call start_trans for all locks. If we lock the same table multiple times, we must use the same - status_param! + status_param; We ensure this by calling copy_status() for all + copies of the same tables. */ -#if !defined(DONT_USE_RW_LOCKS) - if (count > 1) + if ((*data)->lock->start_trans) + ((*data)->lock->start_trans)((*data)->status_param); + for (first_lock=data, pos= data+1 ; pos < end ; pos++) { - THR_LOCK_DATA *last_lock= end[-1]; - pos=end-1; - do + /* Get the current status (row count, checksum, trid etc) */ + if ((*pos)->lock->start_trans) + (*(*pos)->lock->start_trans)((*pos)->status_param); + /* + If same table as previous table use pointer to previous status + information to ensure that all read/write tables shares same + state. + */ + if (pos[0]->lock == pos[-1]->lock && pos[0]->lock->copy_status) + (pos[0]->lock->copy_status)((*pos)->status_param, + (*first_lock)->status_param); + else { - pos--; - if (last_lock->lock == (*pos)->lock && - last_lock->lock->copy_status) - { - if (last_lock->type <= TL_READ_NO_INSERT) - { - THR_LOCK_DATA **read_lock; - /* - If we are locking the same table with read locks we must ensure - that all tables share the status of the last write lock or - the same read lock. - */ - for (; - (*pos)->type <= TL_READ_NO_INSERT && - pos != data && - pos[-1]->lock == (*pos)->lock ; - pos--) ; - - read_lock = pos+1; - do - { - (last_lock->lock->copy_status)((*read_lock)->status_param, - (*pos)->status_param); - } while (*(read_lock++) != last_lock); - last_lock= (*pos); /* Point at last write lock */ - } - else - (*last_lock->lock->copy_status)((*pos)->status_param, - last_lock->status_param); - } - else - last_lock=(*pos); - } while (pos != data); + /* Different lock, use this as base for next lock */ + first_lock= pos; + } } -#endif DBUG_RETURN(THR_LOCK_SUCCESS); } - /* free all locks */ -void thr_multi_unlock(THR_LOCK_DATA **data,uint count) +/** + Merge two sets of locks. + + @param data All locks. First old locks, then new locks. + @param old_count Original number of locks. These are first in 'data'. + @param new_count How many new locks + + The merge is needed if the new locks contains same tables as the old + locks, in which case we have to ensure that same tables shares the + same status (as after a thr_multi_lock()). +*/ + +void thr_merge_locks(THR_LOCK_DATA **data, uint old_count, uint new_count) +{ + THR_LOCK_DATA **pos, **end, **first_lock= 0; + DBUG_ENTER("thr_merge_lock"); + + /* Remove marks on old locks to make them sort before new ones */ + for (pos=data, end= pos + old_count; pos < end ; pos++) + (*pos)->priority&= ~THR_LOCK_LATE_PRIV; + + /* Mark new locks with LATE_PRIV to make them sort after org ones */ + for (pos=data + old_count, end= pos + new_count; pos < end ; pos++) + (*pos)->priority|= THR_LOCK_LATE_PRIV; + + sort_locks(data, old_count + new_count); + + for (pos=data ; pos < end ; pos++) + { + /* Check if lock was unlocked before */ + if (pos[0]->type == TL_UNLOCK || ! pos[0]->lock->fix_status) + { + DBUG_PRINT("info", ("lock skipped. unlocked: %d fix_status: %d", + pos[0]->type == TL_UNLOCK, + pos[0]->lock->fix_status == 0)); + continue; + } + + /* + If same table as previous table use pointer to previous status + information to ensure that all read/write tables shares same + state. + */ + if (first_lock && pos[0]->lock == first_lock[0]->lock) + (pos[0]->lock->fix_status)((*first_lock)->status_param, + (*pos)->status_param); + else + { + /* Different lock, use this as base for next lock */ + first_lock= pos; + (pos[0]->lock->fix_status)((*first_lock)->status_param, 0); + } + } + DBUG_VOID_RETURN; +} + + +/* Unlock all locks */ + +void thr_multi_unlock(THR_LOCK_DATA **data,uint count, uint unlock_flags) { THR_LOCK_DATA **pos,**end; DBUG_ENTER("thr_multi_unlock"); - DBUG_PRINT("lock",("data: 0x%lx count: %d", (long) data, count)); + DBUG_PRINT("lock",("data: 0x%lx count: %d flags: %u", (long) data, count, + unlock_flags)); for (pos=data,end=data+count; pos < end ; pos++) { @@ -1084,7 +1169,7 @@ void thr_multi_unlock(THR_LOCK_DATA **data,uint count) fflush(stdout); #endif if ((*pos)->type != TL_UNLOCK) - thr_unlock(*pos); + thr_unlock(*pos, unlock_flags); else { DBUG_PRINT("lock",("Free lock: data: 0x%lx thread: 0x%lx lock: 0x%lx", @@ -1400,6 +1485,7 @@ my_bool thr_upgrade_write_delay_lock(THR_LOCK_DATA *data, enum thr_lock_type new_lock_type) { THR_LOCK *lock=data->lock; + enum enum_thr_lock_result res; DBUG_ENTER("thr_upgrade_write_delay_lock"); pthread_mutex_lock(&lock->mutex); @@ -1420,6 +1506,8 @@ my_bool thr_upgrade_write_delay_lock(THR_LOCK_DATA *data, if (lock->get_status) (*lock->get_status)(data->status_param, 0); pthread_mutex_unlock(&lock->mutex); + if (lock->start_trans) + (*lock->start_trans)(data->status_param); DBUG_RETURN(0); } @@ -1440,7 +1528,10 @@ my_bool thr_upgrade_write_delay_lock(THR_LOCK_DATA *data, { check_locks(lock,"waiting for lock",0); } - DBUG_RETURN(wait_for_lock(&lock->write_wait,data,1)); + res= wait_for_lock(&lock->write_wait,data,1); + if (res == THR_LOCK_SUCCESS && lock->start_trans) + DBUG_RETURN((*lock->start_trans)(data->status_param)); + DBUG_RETURN(0); } @@ -1684,7 +1775,7 @@ static void *test_thread(void *arg) } } pthread_mutex_unlock(&LOCK_thread_count); - thr_multi_unlock(multi_locks,lock_counts[param]); + thr_multi_unlock(multi_locks,lock_counts[param], THR_UNLOCK_UPDATE_STATUS); } printf("Thread %s (%d) ended\n",my_thread_name(),param); fflush(stdout); diff --git a/sql/handler.cc b/sql/handler.cc index a5b74e65328..c331a3bb855 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -2631,8 +2631,18 @@ void handler::print_keydup_error(uint key_nr, const char *msg) - table->s->path - table->alias */ + +#ifndef DBUG_OFF +#define SET_FATAL_ERROR fatal_error=1 +#else +#define SET_FATAL_ERROR +#endif + void handler::print_error(int error, myf errflag) { +#ifndef DBUG_OFF + bool fatal_error= 0; +#endif DBUG_ENTER("handler::print_error"); DBUG_PRINT("enter",("error: %d",error)); @@ -2650,6 +2660,13 @@ void handler::print_error(int error, myf errflag) case HA_ERR_KEY_NOT_FOUND: case HA_ERR_NO_ACTIVE_RECORD: case HA_ERR_END_OF_FILE: + /* + This errors is not not normally fatal (for example for reads). However + if you get it during an update or delete, then its fatal. + As the user is calling print_error() (which is not done on read), we + assume something when wrong with the update or delete. + */ + SET_FATAL_ERROR; textno=ER_KEY_NOT_FOUND; break; case HA_ERR_WRONG_MRG_TABLE_DEF: @@ -2701,21 +2718,26 @@ void handler::print_error(int error, myf errflag) textno=ER_DUP_UNIQUE; break; case HA_ERR_RECORD_CHANGED: + SET_FATAL_ERROR; textno=ER_CHECKREAD; break; case HA_ERR_CRASHED: + SET_FATAL_ERROR; textno=ER_NOT_KEYFILE; break; case HA_ERR_WRONG_IN_RECORD: + SET_FATAL_ERROR; textno= ER_CRASHED_ON_USAGE; break; case HA_ERR_CRASHED_ON_USAGE: + SET_FATAL_ERROR; textno=ER_CRASHED_ON_USAGE; break; case HA_ERR_NOT_A_TABLE: textno= error; break; case HA_ERR_CRASHED_ON_REPAIR: + SET_FATAL_ERROR; textno=ER_CRASHED_ON_REPAIR; break; case HA_ERR_OUT_OF_MEM: @@ -2814,7 +2836,10 @@ void handler::print_error(int error, myf errflag) if (temporary) my_error(ER_GET_TEMPORARY_ERRMSG, MYF(0), error, str.ptr(), engine); else + { + SET_FATAL_ERROR; my_error(ER_GET_ERRMSG, MYF(0), error, str.ptr(), engine); + } } else my_error(ER_GET_ERRNO,errflag,error); @@ -2822,6 +2847,7 @@ void handler::print_error(int error, myf errflag) } } my_error(textno, errflag, table_share->table_name.str, error); + DBUG_ASSERT(!fatal_error || !debug_assert_if_crashed_table); DBUG_VOID_RETURN; } diff --git a/sql/lock.cc b/sql/lock.cc index 1908e68dbd2..566275c5ea2 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -211,7 +211,8 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count, for (;;) { if (! (sql_lock= get_lock_data(thd, tables, count, GET_LOCK_STORE_LOCKS, - &write_lock_used))) + &write_lock_used)) || + ! sql_lock->table_count) break; if (global_read_lock && write_lock_used && @@ -257,8 +258,7 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count, thd_proc_info(thd, "System lock"); DBUG_PRINT("info", ("thd->proc_info %s", thd->proc_info)); - if (sql_lock->table_count && lock_external(thd, sql_lock->table, - sql_lock->table_count)) + if (lock_external(thd, sql_lock->table, sql_lock->table_count)) { /* Clear the lock type of all lock data to avoid reusage. */ reset_lock_data(sql_lock); @@ -279,8 +279,7 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count, thd->lock_id)]; if (rc > 1) /* a timeout or a deadlock */ { - if (sql_lock->table_count) - VOID(unlock_external(thd, sql_lock->table, sql_lock->table_count)); + VOID(unlock_external(thd, sql_lock->table, sql_lock->table_count)); my_error(rc, MYF(0)); my_free((uchar*) sql_lock,MYF(0)); sql_lock= 0; @@ -388,7 +387,7 @@ void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock) if (sql_lock->table_count) VOID(unlock_external(thd,sql_lock->table,sql_lock->table_count)); if (sql_lock->lock_count) - thr_multi_unlock(sql_lock->locks,sql_lock->lock_count); + thr_multi_unlock(sql_lock->locks,sql_lock->lock_count, 0); my_free((uchar*) sql_lock,MYF(0)); DBUG_VOID_RETURN; } @@ -418,25 +417,8 @@ void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock) uint i,found; DBUG_ENTER("mysql_unlock_read_tables"); - /* Move all write locks first */ - THR_LOCK_DATA **lock=sql_lock->locks; - for (i=found=0 ; i < sql_lock->lock_count ; i++) - { - if (sql_lock->locks[i]->type >= TL_WRITE_ALLOW_READ) - { - swap_variables(THR_LOCK_DATA *, *lock, sql_lock->locks[i]); - lock++; - found++; - } - } - /* unlock the read locked tables */ - if (i != found) - { - thr_multi_unlock(lock,i-found); - sql_lock->lock_count= found; - } + /* Call external lock for all tables to be unlocked */ - /* Then do the same for the external locks */ /* Move all write locked tables first */ TABLE **table=sql_lock->table; for (i=found=0 ; i < sql_lock->table_count ; i++) @@ -455,6 +437,27 @@ void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock) VOID(unlock_external(thd,table,i-found)); sql_lock->table_count=found; } + + /* Call thr_unlock() for all tables to be unlocked */ + + /* Move all write locks first */ + THR_LOCK_DATA **lock=sql_lock->locks; + for (i=found=0 ; i < sql_lock->lock_count ; i++) + { + if (sql_lock->locks[i]->type >= TL_WRITE_ALLOW_READ) + { + swap_variables(THR_LOCK_DATA *, *lock, sql_lock->locks[i]); + lock++; + found++; + } + } + /* unlock the read locked tables */ + if (i != found) + { + thr_multi_unlock(lock, i-found, 0); + sql_lock->lock_count= found; + } + /* Fix the lock positions in TABLE */ table= sql_lock->table; found= 0; @@ -582,8 +585,21 @@ void mysql_lock_abort(THD *thd, TABLE *table, bool upgrade_lock) if ((locked= get_lock_data(thd, &table, 1, GET_LOCK_UNLOCK, &write_lock_used))) { - for (uint i=0; i < locked->lock_count; i++) - thr_abort_locks(locked->locks[i]->lock, upgrade_lock); + if (table->children_attached) + { + /* + Don't abort locks for underlying tables just because merge table + is deleted. Doing would cause anyone accessing these tables to + spin in open_table/close_table forever until lock is released. + */ + thr_multi_unlock(locked->locks, locked->lock_count, + THR_UNLOCK_UPDATE_STATUS); + } + else + { + for (uint i=0; i < locked->lock_count; i++) + thr_abort_locks(locked->locks[i]->lock, upgrade_lock); + } my_free((uchar*) locked,MYF(0)); } DBUG_VOID_RETURN; @@ -624,21 +640,36 @@ bool mysql_lock_abort_for_thread(THD *thd, TABLE *table) } +/** + Merge two thr_lock:s + mysql_lock_merge() + + @param a Original locks + @param b New locks + + @retval New lock structure that contains a and b + + @note + a and b are freed with my_free() +*/ + MYSQL_LOCK *mysql_lock_merge(MYSQL_LOCK *a,MYSQL_LOCK *b) { MYSQL_LOCK *sql_lock; TABLE **table, **end_table; DBUG_ENTER("mysql_lock_merge"); + DBUG_PRINT("enter", ("a->lock_count: %u b->lock_count: %u", + a->lock_count, b->lock_count)); if (!(sql_lock= (MYSQL_LOCK*) my_malloc(sizeof(*sql_lock)+ - sizeof(THR_LOCK_DATA*)*(a->lock_count+b->lock_count)+ + sizeof(THR_LOCK_DATA*)*((a->lock_count+b->lock_count)*2) + sizeof(TABLE*)*(a->table_count+b->table_count),MYF(MY_WME)))) DBUG_RETURN(0); // Fatal error sql_lock->lock_count=a->lock_count+b->lock_count; sql_lock->table_count=a->table_count+b->table_count; sql_lock->locks=(THR_LOCK_DATA**) (sql_lock+1); - sql_lock->table=(TABLE**) (sql_lock->locks+sql_lock->lock_count); + sql_lock->table=(TABLE**) (sql_lock->locks+sql_lock->lock_count*2); memcpy(sql_lock->locks,a->locks,a->lock_count*sizeof(*a->locks)); memcpy(sql_lock->locks+a->lock_count,b->locks, b->lock_count*sizeof(*b->locks)); @@ -659,6 +690,18 @@ MYSQL_LOCK *mysql_lock_merge(MYSQL_LOCK *a,MYSQL_LOCK *b) (*table)->lock_data_start+= a->lock_count; } + /* + Ensure that locks of the same tables share same data structures if we + reopen a table that is already open. This can happen for example with + MERGE tables. + */ + + /* Copy the lock data array. thr_merge_lock() reorders its content */ + memcpy(sql_lock->locks + sql_lock->lock_count, sql_lock->locks, + sql_lock->lock_count * sizeof(*sql_lock->locks)); + thr_merge_locks(sql_lock->locks + sql_lock->lock_count, + a->lock_count, b->lock_count); + /* Delete old, not needed locks */ my_free((uchar*) a,MYF(0)); my_free((uchar*) b,MYF(0)); @@ -832,7 +875,7 @@ static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count, /* Allocating twice the number of pointers for lock data for use in - thr_mulit_lock(). This function reorders the lock data, but cannot + thr_multi_lock(). This function reorders the lock data, but cannot update the table values. So the second part of the array is copied from the first part immediately before calling thr_multi_lock(). */ @@ -1062,11 +1105,13 @@ int lock_table_name(THD *thd, TABLE_LIST *table_list, bool check_in_use) void unlock_table_name(THD *thd, TABLE_LIST *table_list) { + DBUG_ENTER("unlock_table_name"); if (table_list->table) { hash_delete(&open_cache, (uchar*) table_list->table); broadcast_refresh(); } + DBUG_VOID_RETURN; } diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index efb92108781..8b2cba4f9e6 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -1989,7 +1989,7 @@ extern my_bool opt_slave_compressed_protocol, use_temp_pool; extern ulong slave_exec_mode_options; extern my_bool opt_readonly, lower_case_file_system; extern my_bool opt_enable_named_pipe, opt_sync_frm, opt_allow_suspicious_udfs; -extern my_bool opt_secure_auth; +extern my_bool opt_secure_auth, debug_assert_if_crashed_table; extern char* opt_secure_file_priv; extern my_bool opt_log_slow_admin_statements, opt_log_slow_slave_statements; extern my_bool sp_automatic_privileges, opt_noacl; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 97b405b6354..9e7b88dc170 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -455,7 +455,7 @@ static pthread_cond_t COND_thread_cache, COND_flush_thread_cache; /* Global variables */ bool opt_update_log, opt_bin_log, opt_ignore_builtin_innodb= 0; -my_bool opt_log, opt_slow_log; +my_bool opt_log, opt_slow_log, debug_assert_if_crashed_table; ulong log_output_options; my_bool opt_log_queries_not_using_indexes= 0; bool opt_error_log= IF_WIN(1,0); @@ -5962,7 +5962,7 @@ enum options_mysqld OPT_SECURE_FILE_PRIV, OPT_MIN_EXAMINED_ROW_LIMIT, OPT_LOG_SLOW_SLAVE_STATEMENTS, - OPT_DEBUG_CRC, OPT_DEBUG_ON, OPT_OLD_MODE, + OPT_DEBUG_CRC, OPT_DEBUG_ON, OPT_DEBUG_ASSERT_IF_CRASHED_TABLE, OPT_OLD_MODE, OPT_TEST_IGNORE_WRONG_OPTIONS, OPT_TEST_RESTART, #if defined(ENABLED_DEBUG_SYNC) OPT_DEBUG_SYNC_TIMEOUT, @@ -6131,6 +6131,10 @@ struct my_option my_long_options[] = 0, GET_ULONG, REQUIRED_ARG, 0, 0, ~(ulong) 0L, 0, 0, 0}, {"debug-flush", OPT_DEBUG_FLUSH, "Default debug log with flush after write", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"debug-assert-if-crashed-table", OPT_DEBUG_ASSERT_IF_CRASHED_TABLE, + "Do an assert in handler::print_error() if we get a crashed table", + &debug_assert_if_crashed_table, &debug_assert_if_crashed_table, + 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, #endif {"default-character-set", OPT_DEFAULT_CHARACTER_SET_OLD, "Set the default character set (deprecated option, use --character-set-server instead).", diff --git a/sql/parse_file.cc b/sql/parse_file.cc index 3d65fa1de31..5bc16e55ec0 100644 --- a/sql/parse_file.cc +++ b/sql/parse_file.cc @@ -216,7 +216,7 @@ sql_create_definition_file(const LEX_STRING *dir, const LEX_STRING *file_name, File_option *param; DBUG_ENTER("sql_create_definition_file"); DBUG_PRINT("enter", ("Dir: %s, file: %s, base 0x%lx", - dir ? dir->str : "(null)", + dir ? dir->str : "", file_name->str, (ulong) base)); if (dir) diff --git a/sql/set_var.cc b/sql/set_var.cc index c4e41bbf261..9e5cf7ab7dd 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -4341,7 +4341,7 @@ bool sys_var_thd_dbug::update(THD *thd, set_var *var) uchar *sys_var_thd_dbug::value_ptr(THD *thd, enum_var_type type, LEX_STRING *b) { - char buf[256]; + char buf[1024]; if (type == OPT_GLOBAL) { DBUG_EXPLAIN_INITIAL(buf, sizeof(buf)); diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 33ea834247e..708b4d6126b 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -2032,6 +2032,8 @@ static void unlink_open_merge(THD *thd, TABLE *table, TABLE ***prev_pp) Remove parent from open_tables list and close it. This includes detaching and hence clearing parent references. */ + DBUG_PRINT("info", ("Closing parent to '%s'.'%s'", + table->s->db.str, table->s->table_name.str)); close_thread_table(thd, prv_p); } } @@ -3061,8 +3063,9 @@ bool reopen_table(TABLE *table) TABLE_LIST table_list; THD *thd= table->in_use; DBUG_ENTER("reopen_table"); - DBUG_PRINT("tcache", ("table: '%s'.'%s' 0x%lx", table->s->db.str, - table->s->table_name.str, (long) table)); + DBUG_PRINT("tcache", ("table: '%s'.'%s' table: 0x%lx share: 0x%lx", + table->s->db.str, table->s->table_name.str, + (long) table, (long) table->s)); DBUG_ASSERT(table->s->ref_count == 0); DBUG_ASSERT(!table->sort.io_cache); @@ -3349,7 +3352,8 @@ bool reopen_tables(THD *thd, bool get_locks, bool mark_share_as_old) merge_table_found= TRUE; if (!tables || (!db_stat && reopen_table(table))) { - my_error(ER_CANT_REOPEN_TABLE, MYF(0), table->alias); + my_error(ER_CANT_REOPEN_TABLE, MYF(0), + table->alias ? table->alias : table->s->table_name.str); /* If we could not allocate 'tables', we may close open tables here. If a MERGE table is affected, detach the children first. @@ -3359,9 +3363,10 @@ bool reopen_tables(THD *thd, bool get_locks, bool mark_share_as_old) that they cannot be moved into the unused_tables chain with these pointers set. */ - if (table->child_l || table->parent) - detach_merge_children(table, TRUE); - VOID(hash_delete(&open_cache,(uchar*) table)); + unlink_open_table(thd, table, 0); + /* Restart loop */ + prev= &thd->open_tables; + next= *prev; error=1; } else @@ -3396,7 +3401,7 @@ bool reopen_tables(THD *thd, bool get_locks, bool mark_share_as_old) } DBUG_PRINT("tcache", ("open tables to lock: %u", (uint) (tables_ptr - tables))); - if (tables != tables_ptr) // Should we get back old locks + if (tables != tables_ptr) // Should we get back old locks { MYSQL_LOCK *lock; /* @@ -3545,7 +3550,7 @@ bool table_is_used(TABLE *table, bool wait_for_name_lock) char *key= table->s->table_cache_key.str; uint key_length= table->s->table_cache_key.length; - DBUG_PRINT("loop", ("table_name: %s", table->alias)); + DBUG_PRINT("loop", ("table_name: %s", table->alias ? table->alias : "")); HASH_SEARCH_STATE state; for (TABLE *search= (TABLE*) hash_first(&open_cache, (uchar*) key, key_length, &state); @@ -3883,6 +3888,7 @@ static int open_unireg_entry(THD *thd, TABLE *entry, TABLE_LIST *table_list, int error; TABLE_SHARE *share; uint discover_retry_count= 0; + bool locked_table; DBUG_ENTER("open_unireg_entry"); safe_mutex_assert_owner(&LOCK_open); @@ -4003,8 +4009,10 @@ retry: } if (!entry->s || !entry->s->crashed) goto err; - // Code below is for repairing a crashed file - if ((error= lock_table_name(thd, table_list, TRUE))) + + // Code below is for repairing a crashed file + locked_table= table_list->table != 0; + if (! locked_table && (error= lock_table_name(thd, table_list, TRUE))) { if (error < 0) goto err; @@ -4038,12 +4046,13 @@ retry: else thd->clear_error(); // Clear error message pthread_mutex_lock(&LOCK_open); - unlock_table_name(thd, table_list); + if (!locked_table) + unlock_table_name(thd, table_list); if (error) goto err; break; - } + } if (Table_triggers_list::check_n_load(thd, share->db.str, share->table_name.str, entry, 0)) @@ -4289,7 +4298,7 @@ void detach_merge_children(TABLE *table, bool clear_refs) children attached yet. Also this is called for every child and the parent from close_thread_tables(). */ - if ((first_detach= parent->children_attached)) + if (parent->children_attached) { VOID(parent->file->extra(HA_EXTRA_DETACH_CHILDREN)); parent->children_attached= FALSE; @@ -4301,38 +4310,50 @@ void detach_merge_children(TABLE *table, bool clear_refs) if (clear_refs) { - /* In any case clear the own parent reference. (***) */ - table->parent= NULL; + if (table->parent) + { + /* In any case clear the own parent reference. (***) */ + table->parent= NULL; + table->file->extra(HA_EXTRA_DETACH_CHILD); + } /* - On the first detach, clear all references. If this table is the - parent, we still may need to clear the child references. The first - detach might not have done this. + Clear all references. If this table is the parent, we still may + need to clear the child references. The first detach might not + have done this. */ - if (first_detach || (table == parent)) + for (child_l= parent->child_l; ; child_l= child_l->next_global) { - /* Clear TABLE references to force new assignment at next open. */ - for (child_l= parent->child_l; ; child_l= child_l->next_global) + /* + Do not DBUG_ASSERT(child_l->table); open_tables might be + incomplete or we may have been called twice. + + Clear the parent reference of the children only on the first + detach. The children might already be closed. They will clear + it themselves when this function is called for them with + 'clear_refs' true. See above "(***)". + */ + if (child_l->table) { + if (child_l->table->parent) + { + child_l->table->parent= NULL; + if (child_l->table->db_stat) + child_l->table->file->extra(HA_EXTRA_DETACH_CHILD); + } /* - Do not DBUG_ASSERT(child_l->table); open_tables might be - incomplete. - - Clear the parent reference of the children only on the first - detach. The children might already be closed. They will clear - it themseves when this function is called for them with - 'clear_refs' true. See above "(***)". + Set alias to "" to ensure that table is not used if we are in + LOCK TABLES */ - if (first_detach && child_l->table) - child_l->table->parent= NULL; + ((char*) child_l->table->alias)[0]= 0; /* Clear the table reference to force new assignment at next open. */ child_l->table= NULL; - - /* Break when this was the last child. */ - if (&child_l->next_global == parent->child_last_l) - break; } + + /* Break when this was the last child. */ + if (&child_l->next_global == parent->child_last_l) + break; } } @@ -5129,9 +5150,11 @@ bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables, uint flags) static void mark_real_tables_as_free_for_reuse(TABLE_LIST *table) { + DBUG_ENTER("mark_real_tables_as_free_for_reuse"); for (; table; table= table->next_global) if (!table->placeholder()) table->table->query_id= 0; + DBUG_VOID_RETURN; } diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 2b102e47abe..34ef4cfa066 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -273,7 +273,7 @@ const char *set_thd_proc_info(THD *thd, const char *info, const char *old_info= thd->proc_info; DBUG_PRINT("proc_info", ("%s:%d %s", calling_file, calling_line, - (info != NULL) ? info : "(null)")); + (info != NULL) ? info : "")); #if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER) thd->profiling.status_change(info, calling_function, calling_file, calling_line); #endif diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index a031a921c60..db96b6fc9a2 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -7999,6 +7999,8 @@ bool parse_sql(THD *thd, Object_creation_ctx *creation_ctx) { bool mysql_parse_status; + DBUG_ENTER("parse_sql"); + DBUG_ASSERT(thd->m_parser_state == NULL); /* Backup creation context. */ @@ -8032,7 +8034,7 @@ bool parse_sql(THD *thd, /* That's it. */ - return mysql_parse_status || thd->is_fatal_error; + DBUG_RETURN(mysql_parse_status || thd->is_fatal_error); } /** diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index e8a382ca8f6..f6864add19f 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -498,9 +498,6 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) thd->in_lock_tables= 1; if (reopen_tables(thd, 1, 1)) { - /* To be safe remove this table from the set of LOCKED TABLES */ - unlink_open_table(thd, tables->table, FALSE); - /* Ignore reopen_tables errors for now. It's better not leave master/slave in a inconsistent state. diff --git a/storage/maria/ma_bitmap.c b/storage/maria/ma_bitmap.c index d8b7a9b9a99..c96e0bd7a86 100644 --- a/storage/maria/ma_bitmap.c +++ b/storage/maria/ma_bitmap.c @@ -1016,7 +1016,11 @@ static my_bool allocate_tail(MARIA_FILE_BITMAP *bitmap, uint size, DBUG_PRINT("enter", ("size: %u", size)); LINT_INIT(best_pos); - DBUG_ASSERT(size <= MAX_TAIL_SIZE(bitmap->block_size)); + /* + We have to add DIR_ENTRY_SIZE here as this is not part of the data size + See call to allocate_tail() in find_tail(). + */ + DBUG_ASSERT(size <= MAX_TAIL_SIZE(bitmap->block_size) + DIR_ENTRY_SIZE); for (; data < end; data += 6) { diff --git a/storage/maria/ma_blockrec.c b/storage/maria/ma_blockrec.c index 92ec916a9d1..bc2b1d5187f 100644 --- a/storage/maria/ma_blockrec.c +++ b/storage/maria/ma_blockrec.c @@ -2823,6 +2823,10 @@ static my_bool write_block_record(MARIA_HA *info, data+= diff_length; head_length= share->base.min_block_length; } + /* + If this is a redo entry (ie, undo_lsn != LSN_ERROR) then we should have + written exactly head_length bytes (same as original record). + */ DBUG_ASSERT(undo_lsn == LSN_ERROR || head_length == row_pos->length); int2store(row_pos->dir + 2, head_length); /* update empty space at start of block */ @@ -7161,6 +7165,7 @@ my_bool _ma_apply_undo_row_update(MARIA_HA *info, LSN undo_lsn, header+= HA_CHECKSUM_STORE_SIZE; } length_on_head_page= uint2korr(header); + set_if_bigger(length_on_head_page, share->base.min_block_length); header+= 2; extent_count= pagerange_korr(header); header+= PAGERANGE_STORE_SIZE; diff --git a/storage/maria/ma_blockrec.h b/storage/maria/ma_blockrec.h index c39b0af73ad..d18b20aa387 100644 --- a/storage/maria/ma_blockrec.h +++ b/storage/maria/ma_blockrec.h @@ -281,7 +281,8 @@ my_bool write_hook_for_commit(enum translog_record_type type, TRN *trn, MARIA_HA *tbl_info, LSN *lsn, void *hook_arg); void _ma_block_get_status(void *param, my_bool concurrent_insert); -void _ma_block_get_status_no_versioning(void *param, my_bool concurrent_ins); +my_bool _ma_block_start_trans(void* param); +my_bool _ma_block_start_trans_no_versioning(void *param); void _ma_block_update_status(void *param); void _ma_block_restore_status(void *param); my_bool _ma_block_check_status(void *param); diff --git a/storage/maria/ma_locking.c b/storage/maria/ma_locking.c index b355d7bc792..6bb308e5959 100644 --- a/storage/maria/ma_locking.c +++ b/storage/maria/ma_locking.c @@ -63,7 +63,7 @@ int maria_lock_database(MARIA_HA *info, int lock_type) { count= --share->w_locks; if (share->lock.update_status) - (*share->lock.update_status)(info); + _ma_update_status_with_lock(info); } --share->tot_locks; if (info->lock_type == F_WRLCK && !share->w_locks) diff --git a/storage/maria/ma_open.c b/storage/maria/ma_open.c index 30d099d939a..63e1801a39a 100644 --- a/storage/maria/ma_open.c +++ b/storage/maria/ma_open.c @@ -874,8 +874,8 @@ MARIA_HA *maria_open(const char *name, int mode, uint open_flags) share->have_versioning= 1; share->row_is_visible= _ma_row_visible_transactional_table; share->lock.get_status= _ma_block_get_status; - share->lock.update_status= _ma_block_update_status; share->lock.check_status= _ma_block_check_status; + share->lock.start_trans= _ma_block_start_trans; /* We can for the moment only allow multiple concurrent inserts only if there is no auto-increment key. To lift this restriction @@ -903,7 +903,7 @@ MARIA_HA *maria_open(const char *name, int mode, uint open_flags) else if (share->now_transactional) { DBUG_ASSERT(share->data_file_type == BLOCK_RECORD); - share->lock.get_status= _ma_block_get_status_no_versioning; + share->lock.start_trans= _ma_block_start_trans_no_versioning; } } #endif diff --git a/storage/maria/ma_state.c b/storage/maria/ma_state.c index c7eee7d511d..ca94d58264b 100644 --- a/storage/maria/ma_state.c +++ b/storage/maria/ma_state.c @@ -335,6 +335,25 @@ void _ma_update_status(void* param) } +/* + Same as ma_update_status() but take a lock in the table lock, to protect + against someone calling ma_get_status() from thr_lock() at the same time. +*/ + +void _ma_update_status_with_lock(MARIA_HA *info) +{ + my_bool locked= 0; + if (info->state == &info->state_save) + { + locked= 1; + pthread_mutex_lock(&info->s->lock.mutex); + } + (*info->s->lock.update_status)(info); + if (locked) + pthread_mutex_unlock(&info->s->lock.mutex); +} + + void _ma_restore_status(void *param) { MARIA_HA *info= (MARIA_HA*) param; @@ -585,7 +604,13 @@ void _ma_block_get_status(void* param, my_bool concurrent_insert) { DBUG_ASSERT(info->lock.type != TL_WRITE_CONCURRENT_INSERT); } + DBUG_VOID_RETURN; +} + +my_bool _ma_block_start_trans(void* param) +{ + MARIA_HA *info=(MARIA_HA*) param; if (info->s->lock_key_trees) { /* @@ -593,24 +618,22 @@ void _ma_block_get_status(void* param, my_bool concurrent_insert) out of memory conditions) TODO: Fix this by having one extra state pre-allocated */ - (void) _ma_setup_live_state(info); + return _ma_setup_live_state(info); } - else + + /* + Info->trn is set if this table is already handled and we are + called from maria_versioning() + */ + if (info->s->base.born_transactional && !info->trn) { /* - Info->trn is set if this table is already handled and we are - called from maria_versioning() + Assume for now that this doesn't fail (It can only fail in + out of memory conditions) */ - if (info->s->base.born_transactional && !info->trn) - { - /* - Assume for now that this doesn't fail (It can only fail in - out of memory conditions) - */ - (void) maria_create_trn_hook(info); - } + return maria_create_trn_hook(info) != 0; } - DBUG_VOID_RETURN; + return 0; } @@ -639,13 +662,10 @@ my_bool _ma_block_check_status(void *param __attribute__((unused))) /* Get status when transactional but not versioned */ -void _ma_block_get_status_no_versioning(void* param, - my_bool concurrent_insert - __attribute__((unused))) +my_bool _ma_block_start_trans_no_versioning(void* param) { MARIA_HA *info=(MARIA_HA*) param; DBUG_ENTER("_ma_block_get_status_no_version"); - DBUG_PRINT("enter", ("concurrent_insert %d", concurrent_insert)); DBUG_ASSERT(info->s->base.born_transactional); info->state->changed= 0; /* from _ma_reset_update_flag() */ @@ -655,9 +675,9 @@ void _ma_block_get_status_no_versioning(void* param, Assume for now that this doesn't fail (It can only fail in out of memory conditions) */ - (void) maria_create_trn_hook(info); + DBUG_RETURN(maria_create_trn_hook(info)); } - DBUG_VOID_RETURN; + DBUG_RETURN(0); } diff --git a/storage/maria/ma_state.h b/storage/maria/ma_state.h index f3317f0084a..03ce5c2ea8c 100644 --- a/storage/maria/ma_state.h +++ b/storage/maria/ma_state.h @@ -62,6 +62,7 @@ MARIA_STATE_HISTORY *_ma_remove_not_visible_states(MARIA_STATE_HISTORY void _ma_reset_state(MARIA_HA *info); void _ma_get_status(void* param, my_bool concurrent_insert); void _ma_update_status(void* param); +void _ma_update_status_with_lock(MARIA_HA *info); void _ma_restore_status(void *param); void _ma_copy_status(void* to, void *from); void _ma_reset_update_flag(void *param, my_bool concurrent_insert); diff --git a/storage/maria/ma_write.c b/storage/maria/ma_write.c index 7ea73d17b9b..02eeec754ee 100644 --- a/storage/maria/ma_write.c +++ b/storage/maria/ma_write.c @@ -678,7 +678,6 @@ static int w_search(register MARIA_HA *info, uint32 comp_flag, MARIA_KEY *key, } else /* not HA_FULLTEXT, normal HA_NOSAME key */ { - DBUG_PRINT("warning", ("Duplicate key")); /* TODO When the index will support true versioning - with multiple @@ -696,6 +695,12 @@ static int w_search(register MARIA_HA *info, uint32 comp_flag, MARIA_KEY *key, info->dup_key_trid= _ma_trid_from_key(&tmp_key); info->dup_key_pos= dup_key_pos; my_errno= HA_ERR_FOUND_DUPP_KEY; + DBUG_PRINT("warning", + ("Duplicate key. dup_key_trid: %lu pos %lu visible: %d", + (ulong) info->dup_key_trid, + (ulong) info->dup_key_pos, + info->trn ? trnman_can_read_from(info->trn, + info->dup_key_trid) : 2)); goto err; } } diff --git a/storage/myisam/mi_check.c b/storage/myisam/mi_check.c index 7096c03cf7d..8f732db234c 100644 --- a/storage/myisam/mi_check.c +++ b/storage/myisam/mi_check.c @@ -1337,7 +1337,7 @@ int chk_data_link(HA_CHECK *param, MI_INFO *info, my_bool extend) if (splits != info->s->state.split) { mi_check_print_warning(param, - "Found %10s key parts. Should be: %s", + "Found %10s parts. Should be: %s", llstr(splits,llbuff), llstr(info->s->state.split,llbuff2)); } diff --git a/storage/myisam/mi_extra.c b/storage/myisam/mi_extra.c index 3b14e5eb98e..4c5a65f900f 100644 --- a/storage/myisam/mi_extra.c +++ b/storage/myisam/mi_extra.c @@ -390,6 +390,11 @@ int mi_extra(MI_INFO *info, enum ha_extra_function function, void *extra_arg) share->is_log_table= TRUE; pthread_mutex_unlock(&share->intern_lock); break; + case HA_EXTRA_DETACH_CHILD: /* When used with MERGE tables */ + info->open_flag&= ~HA_OPEN_MERGE_TABLE; + info->lock.priority&= ~THR_LOCK_MERGE_PRIV; + break; + case HA_EXTRA_KEY_CACHE: case HA_EXTRA_NO_KEY_CACHE: default: diff --git a/storage/myisam/mi_locking.c b/storage/myisam/mi_locking.c index 40a04293731..19ec8337539 100644 --- a/storage/myisam/mi_locking.c +++ b/storage/myisam/mi_locking.c @@ -22,6 +22,8 @@ #include "ftdefs.h" +static void mi_update_status_with_lock(MI_INFO *info); + /* lock table by F_UNLCK, F_RDLCK or F_WRLCK */ int mi_lock_database(MI_INFO *info, int lock_type) @@ -62,7 +64,7 @@ int mi_lock_database(MI_INFO *info, int lock_type) else { count= --share->w_locks; - mi_update_status(info); + mi_update_status_with_lock(info); } --share->tot_locks; if (info->lock_type == F_WRLCK && !share->w_locks && @@ -244,7 +246,7 @@ int mi_lock_database(MI_INFO *info, int lock_type) a crash on windows if the table is renamed and later on referenced by the merge table. */ - if( info->owned_by_merge && (info->s)->kfile < 0 ) + if ((info->open_flags & HA_OPEN_MERGE_TABLE) && (info->s)->kfile < 0) { error = HA_ERR_NO_SUCH_TABLE; } @@ -273,9 +275,11 @@ void mi_get_status(void* param, my_bool concurrent_insert) { MI_INFO *info=(MI_INFO*) param; DBUG_ENTER("mi_get_status"); - DBUG_PRINT("info",("key_file: %ld data_file: %ld concurrent_insert: %d", - (long) info->s->state.state.key_file_length, - (long) info->s->state.state.data_file_length, + DBUG_PRINT("info",("name: %s key_file: %lu data_file: %lu rows: %lu concurrent_insert: %d", + info->s->index_file_name, + (ulong) info->s->state.state.key_file_length, + (ulong) info->s->state.state.data_file_length, + (ulong) info->s->state.state.records, concurrent_insert)); #ifndef DBUG_OFF if (info->state->key_file_length > info->s->state.state.key_file_length || @@ -306,9 +310,11 @@ void mi_update_status(void* param) if (info->state == &info->save_state) { #ifndef DBUG_OFF - DBUG_PRINT("info",("updating status: key_file: %ld data_file: %ld", - (long) info->state->key_file_length, - (long) info->state->data_file_length)); + DBUG_PRINT("info", + ("updating status: key_file: %lu data_file: %lu rows: %lu", + (ulong) info->state->key_file_length, + (ulong) info->state->data_file_length, + (ulong) info->state->records)); if (info->state->key_file_length < info->s->state.state.key_file_length || info->state->data_file_length < info->s->state.state.data_file_length) DBUG_PRINT("warning",("old info: key_file: %ld data_file: %ld", @@ -342,6 +348,24 @@ void mi_update_status(void* param) DBUG_VOID_RETURN; } +/* + Same as mi_update_status() but take a lock in the table lock, to protect + against someone calling mi_get_status() from thr_lock() at the same time. +*/ + +static void mi_update_status_with_lock(MI_INFO *info) +{ + my_bool locked= 0; + if (info->state == &info->save_state) + { + locked= 1; + pthread_mutex_lock(&info->s->lock.mutex); + } + mi_update_status(info); + if (locked) + pthread_mutex_unlock(&info->s->lock.mutex); +} + void mi_restore_status(void *param) { @@ -407,6 +431,32 @@ my_bool mi_check_status(void *param) } +/** + Fix status for thr_lock_merge() + + @param org_table + @param new_table that should point on org_lock. new_table is 0 + in case this is the first occurence of the table in the lock + structure. +*/ + +void mi_fix_status(MI_INFO *org_table, MI_INFO *new_table) +{ + DBUG_ENTER("mi_fix_status"); + if (!new_table) + { + /* First in group. Set state as in mi_get_status() */ + org_table->state= &org_table->save_state; + } + else + { + /* Set new_table to use state from org_table (first lock of this table) */ + new_table->state= org_table->state; + } + DBUG_VOID_RETURN; +} + + /**************************************************************************** ** functions to read / write the state ****************************************************************************/ diff --git a/storage/myisam/mi_open.c b/storage/myisam/mi_open.c index e4240dec90c..9fc82846e91 100644 --- a/storage/myisam/mi_open.c +++ b/storage/myisam/mi_open.c @@ -119,7 +119,7 @@ MI_INFO *mi_open(const char *name, int mode, uint open_flags) dflt_key_cache); DBUG_EXECUTE_IF("myisam_pretend_crashed_table_on_open", - if (strstr(name, "/t1")) + if (strstr(name, "/crashed")) { my_errno= HA_ERR_CRASHED; goto err; @@ -556,6 +556,7 @@ MI_INFO *mi_open(const char *name, int mode, uint open_flags) share->lock.update_status=mi_update_status; share->lock.restore_status= mi_restore_status; share->lock.check_status=mi_check_status; + share->lock.fix_status= (void (*)(void *, void *)) mi_fix_status; } } #endif @@ -606,6 +607,7 @@ MI_INFO *mi_open(const char *name, int mode, uint open_flags) info.s=share; info.lastpos= HA_OFFSET_ERROR; info.update= (short) (HA_STATE_NEXT_FOUND+HA_STATE_PREV_FOUND); + info.open_flag= open_flags; info.opt_flag=READ_CHECK_USED; info.this_unique= (ulong) info.dfile; /* Uniq number in process */ if (share->data_file_type == COMPRESSED_RECORD) diff --git a/storage/myisam/myisamdef.h b/storage/myisam/myisamdef.h index 3262808803c..e03f88b1a1f 100644 --- a/storage/myisam/myisamdef.h +++ b/storage/myisam/myisamdef.h @@ -274,7 +274,9 @@ struct st_myisam_info */ ulong packed_length, blob_length; /* Length of found, packed record */ int dfile; /* The datafile */ + uint open_flag; /* Parameters for open */ uint opt_flag; /* Optim. for space/speed */ + uint once_flags; /* For MYISAMMRG */ uint update; /* If file changed since open */ int lastinx; /* Last used index */ uint lastkey_length; /* Length of key in lastkey */ @@ -300,10 +302,6 @@ struct st_myisam_info my_bool page_changed; /* If info->buff has to be reread for rnext */ my_bool buff_used; - my_bool once_flags; /* For MYISAMMRG */ -#ifdef __WIN__ - my_bool owned_by_merge; /* This MyISAM table is part of a merge union */ -#endif #ifdef THREAD THR_LOCK_DATA lock; #endif @@ -712,6 +710,7 @@ void mi_update_status(void *param); void mi_restore_status(void *param); void mi_copy_status(void *to, void *from); my_bool mi_check_status(void *param); +void mi_fix_status(MI_INFO *org_table, MI_INFO *new_table); void mi_disable_non_unique_index(MI_INFO *info, ha_rows rows); extern MI_INFO *test_if_reopen(char *filename); diff --git a/storage/myisammrg/ha_myisammrg.cc b/storage/myisammrg/ha_myisammrg.cc index 30be1d34e2a..99513e2267f 100644 --- a/storage/myisammrg/ha_myisammrg.cc +++ b/storage/myisammrg/ha_myisammrg.cc @@ -86,8 +86,6 @@ On parent open the storage engine structures are allocated and initialized. They stay with the open table until its final close. - - */ #ifdef USE_PRAGMA_IMPLEMENTATION @@ -1070,6 +1068,8 @@ THR_LOCK_DATA **ha_myisammrg::store_lock(THD *thd, open_table != file->end_table ; open_table++) { + open_table->table->lock.priority|= THR_LOCK_MERGE_PRIV; + *(to++)= &open_table->table->lock; if (lock_type != TL_IGNORE && open_table->table->lock.type == TL_UNLOCK) open_table->table->lock.type=lock_type; diff --git a/storage/myisammrg/myrg_locking.c b/storage/myisammrg/myrg_locking.c index 4f1e3f844a1..a414cee7bb8 100644 --- a/storage/myisammrg/myrg_locking.c +++ b/storage/myisammrg/myrg_locking.c @@ -27,15 +27,8 @@ int myrg_lock_database(MYRG_INFO *info, int lock_type) error=0; for (file=info->open_tables ; file != info->end_table ; file++) { -#ifdef __WIN__ - /* - Make sure this table is marked as owned by a merge table. - The semaphore is never released as long as table remains - in memory. This should be refactored into a more generic - approach (observer pattern) - */ - (file->table)->owned_by_merge = TRUE; -#endif + DBUG_ASSERT(file->table->open_flag & HA_OPEN_MERGE_TABLE); + if ((new_error=mi_lock_database(file->table,lock_type))) { error=new_error; diff --git a/storage/myisammrg/myrg_open.c b/storage/myisammrg/myrg_open.c index 17c9b4ba4d1..ba452854808 100644 --- a/storage/myisammrg/myrg_open.c +++ b/storage/myisammrg/myrg_open.c @@ -27,8 +27,9 @@ if handle_locking is 0 then exit with error if some table is locked if handle_locking is 1 then wait if table is locked - NOTE: This function is not used in the MySQL server. It is for - MERGE use independent from MySQL. Currently there is some code + NOTE: This function is only used in the MySQL server when a + table is cloned. It is also used for usage of MERGE + independent from MySQL. Currently there is some code duplication between myrg_open() and myrg_parent_open() + myrg_attach_children(). Please duplicate changes in these functions or make common sub-functions. @@ -93,7 +94,8 @@ MYRG_INFO *myrg_open(const char *name, int mode, int handle_locking) } else fn_format(buff, buff, "", "", 0); - if (!(isam=mi_open(buff,mode,(handle_locking?HA_OPEN_WAIT_IF_LOCKED:0)))) + if (!(isam=mi_open(buff,mode,(handle_locking?HA_OPEN_WAIT_IF_LOCKED:0) | + HA_OPEN_MERGE_TABLE))) { if (handle_locking & HA_OPEN_FOR_REPAIR) { @@ -430,6 +432,8 @@ int myrg_attach_children(MYRG_INFO *m_info, int handle_locking, m_info->open_tables[child_nr].table= myisam; m_info->open_tables[child_nr].file_offset= (my_off_t) file_offset; file_offset+= myisam->state->data_file_length; + /* Mark as MERGE table */ + myisam->open_flag|= HA_OPEN_MERGE_TABLE; /* Check table definition match. */ if (m_info->reclength != myisam->s->base.reclength) diff --git a/storage/xtradb/buf/buf0buf.c b/storage/xtradb/buf/buf0buf.c index 55ff207cf11..1c08bd6d0bf 100644 --- a/storage/xtradb/buf/buf0buf.c +++ b/storage/xtradb/buf/buf0buf.c @@ -1116,7 +1116,7 @@ init_again: if (shm_info->buf_pool_backup.LRU_old) shm_info->buf_pool_backup.LRU_old = (buf_page_t*)((byte*)(shm_info->buf_pool_backup.LRU_old) - + (((void*)shm_info->buf_pool_backup.LRU_old > previous_frame_address) + + (((byte*)shm_info->buf_pool_backup.LRU_old > previous_frame_address) ? logi_offset : blocks_offset)); UT_LIST_OFFSET(unzip_LRU, buf_block_t, shm_info->buf_pool_backup.unzip_LRU, diff --git a/storage/xtradb/buf/buf0lru.c b/storage/xtradb/buf/buf0lru.c index 79c7c0d3bbe..94828940fd4 100644 --- a/storage/xtradb/buf/buf0lru.c +++ b/storage/xtradb/buf/buf0lru.c @@ -2265,7 +2265,7 @@ buf_LRU_file_restore(void) ulint req = 0; ibool terminated = FALSE; ibool ret = FALSE; - dump_record_t* records; + dump_record_t* records= 0; ulint size; ulint size_high; ulint length; |