summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--client/mysql.cc2
-rw-r--r--client/mysqltest.cc2
-rw-r--r--dbug/dbug.c24
-rw-r--r--include/my_base.h2
-rw-r--r--include/thr_lock.h21
-rwxr-xr-xmysql-test/mysql-test-run.pl1
-rw-r--r--mysql-test/r/merge.result90
-rw-r--r--mysql-test/r/merge_debug.result24
-rw-r--r--mysql-test/r/udf_query_cache.result1
-rw-r--r--mysql-test/suite/parts/r/partition_repair_myisam.result2
-rw-r--r--mysql-test/t/merge.test45
-rw-r--r--mysql-test/t/merge_debug.test42
-rw-r--r--mysql-test/t/udf_query_cache.test1
-rw-r--r--mysys/my_getopt.c1
-rw-r--r--mysys/my_symlink2.c4
-rw-r--r--mysys/thr_lock.c207
-rw-r--r--sql/handler.cc26
-rw-r--r--sql/lock.cc103
-rw-r--r--sql/mysql_priv.h2
-rw-r--r--sql/mysqld.cc8
-rw-r--r--sql/parse_file.cc2
-rw-r--r--sql/set_var.cc2
-rw-r--r--sql/sql_base.cc91
-rw-r--r--sql/sql_class.cc2
-rw-r--r--sql/sql_parse.cc4
-rw-r--r--sql/sql_trigger.cc3
-rw-r--r--storage/maria/ma_bitmap.c6
-rw-r--r--storage/maria/ma_blockrec.c5
-rw-r--r--storage/maria/ma_blockrec.h3
-rw-r--r--storage/maria/ma_locking.c2
-rw-r--r--storage/maria/ma_open.c4
-rw-r--r--storage/maria/ma_state.c58
-rw-r--r--storage/maria/ma_state.h1
-rw-r--r--storage/maria/ma_write.c7
-rw-r--r--storage/myisam/mi_check.c2
-rw-r--r--storage/myisam/mi_extra.c5
-rw-r--r--storage/myisam/mi_locking.c66
-rw-r--r--storage/myisam/mi_open.c4
-rw-r--r--storage/myisam/myisamdef.h7
-rw-r--r--storage/myisammrg/ha_myisammrg.cc4
-rw-r--r--storage/myisammrg/myrg_locking.c11
-rw-r--r--storage/myisammrg/myrg_open.c10
-rw-r--r--storage/xtradb/buf/buf0buf.c2
-rw-r--r--storage/xtradb/buf/buf0lru.c2
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;