summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--client/mysql.cc2
-rw-r--r--client/mysqltest.cc20
-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/r/warnings_debug.result10
-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--mysql-test/t/warnings_debug.test19
-rw-r--r--mysys/my_getopt.c1
-rw-r--r--mysys/my_symlink2.c4
-rw-r--r--mysys/thr_lock.c208
-rw-r--r--sql/handler.cc32
-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/protocol.cc2
-rw-r--r--sql/set_var.cc2
-rw-r--r--sql/sql_base.cc92
-rw-r--r--sql/sql_class.cc2
-rw-r--r--sql/sql_class.h7
-rw-r--r--sql/sql_error.cc2
-rw-r--r--sql/sql_parse.cc4
-rw-r--r--sql/sql_trigger.cc3
-rw-r--r--storage/maria/ma_bitmap.c91
-rw-r--r--storage/maria/ma_blockrec.c85
-rw-r--r--storage/maria/ma_blockrec.h5
-rw-r--r--storage/maria/ma_dbug.c3
-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/maria/maria_def.h4
-rw-r--r--storage/myisam/mi_check.c2
-rw-r--r--storage/myisam/mi_dbug.c3
-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
-rw-r--r--storage/xtradb/include/os0sync.h2
-rw-r--r--storage/xtradb/include/srv0srv.h3
-rw-r--r--storage/xtradb/log/log0log.c1
-rw-r--r--storage/xtradb/os/os0sync.c53
-rw-r--r--storage/xtradb/srv/srv0srv.c6
57 files changed, 905 insertions, 314 deletions
diff --git a/client/mysql.cc b/client/mysql.cc
index 99b8499faea..134f54757cb 100644
--- a/client/mysql.cc
+++ b/client/mysql.cc
@@ -4091,7 +4091,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 635d013eae8..be4a3e06fbb 100644
--- a/client/mysqltest.cc
+++ b/client/mysqltest.cc
@@ -1255,15 +1255,6 @@ void die(const char *fmt, ...)
DBUG_ENTER("die");
DBUG_PRINT("enter", ("start_lineno: %d", start_lineno));
- /*
- Protect against dying twice
- first time 'die' is called, try to write log files
- second time, just exit
- */
- if (dying)
- cleanup_and_exit(1);
- dying= 1;
-
/* Print the error message */
fprintf(stderr, "mysqltest: ");
if (cur_file && cur_file != file_stack)
@@ -1282,6 +1273,15 @@ void die(const char *fmt, ...)
fprintf(stderr, "\n");
fflush(stderr);
+ /*
+ Protect against dying twice
+ first time 'die' is called, try to write log files
+ second time, just exit
+ */
+ if (dying)
+ cleanup_and_exit(1);
+ dying= 1;
+
log_file.show_tail(opt_tail_lines);
/*
@@ -1876,7 +1876,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..608b4395c81 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
@@ -1826,11 +1826,11 @@ FILE *_db_fp_(void)
BOOLEAN _db_keyword_(CODE_STATE *cs, const char *keyword, int strict)
{
+ int match= strict ? INCLUDE : INCLUDE|MATCHED;
get_code_state_if_not_set_or_return FALSE;
- strict=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 8065441d4d7..8e6436bde70 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 2eb32c33954..41501da6088 100755
--- a/mysql-test/mysql-test-run.pl
+++ b/mysql-test/mysql-test-run.pl
@@ -4281,6 +4281,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 8cd5302345b..2a36c78fc28 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/r/warnings_debug.result b/mysql-test/r/warnings_debug.result
new file mode 100644
index 00000000000..08908bf0437
--- /dev/null
+++ b/mysql-test/r/warnings_debug.result
@@ -0,0 +1,10 @@
+drop table if exists t1;
+create table t1 (a int primary key) engine=innodb;
+SET SESSION debug="+d,warn_during_ha_commit_trans";
+INSERT INTO t1 VALUES (1);
+Warnings:
+Warning 1196 Some non-transactional changed tables couldn't be rolled back
+SHOW WARNINGS;
+Level Code Message
+Warning 1196 Some non-transactional changed tables couldn't be rolled back
+drop table t1;
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/mysql-test/t/warnings_debug.test b/mysql-test/t/warnings_debug.test
new file mode 100644
index 00000000000..99d02330960
--- /dev/null
+++ b/mysql-test/t/warnings_debug.test
@@ -0,0 +1,19 @@
+--source include/have_innodb.inc
+--source include/have_debug.inc
+
+--disable_warnings
+drop table if exists t1;
+--enable_warnings
+
+create table t1 (a int primary key) engine=innodb;
+
+# Test that warnings produced during autocommit (after calling
+# set_ok_status()) are still reported to the client.
+SET SESSION debug="+d,warn_during_ha_commit_trans";
+INSERT INTO t1 VALUES (1);
+# The warning will be shown automatically by mysqltest; there was a bug where
+# this didn't happen because the warning was not counted when sending result
+# packet. Show the warnings manually also.
+SHOW WARNINGS;
+
+drop table t1;
diff --git a/mysys/my_getopt.c b/mysys/my_getopt.c
index 21eea5517f1..db6ccd230f2 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..341b2f0058e 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()
@@ -379,6 +406,7 @@ void thr_lock_data_init(THR_LOCK *lock,THR_LOCK_DATA *data, void *param)
data->owner= 0; /* no owner yet */
data->status_param=param;
data->cond=0;
+ data->priority= 0;
}
@@ -530,7 +558,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 +572,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 +837,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 +861,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 +1011,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 +1041,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 +1064,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 +1170,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 +1486,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 +1507,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 +1529,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 +1776,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 1fcd9555221..c931b689842 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -1094,6 +1094,12 @@ int ha_commit_trans(THD *thd, bool all)
my_xid xid= thd->transaction.xid_state.xid.get_my_xid();
DBUG_ENTER("ha_commit_trans");
+ /* Just a random warning to test warnings pushed during autocommit. */
+ DBUG_EXECUTE_IF("warn_during_ha_commit_trans",
+ push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_WARNING_NOT_COMPLETE_ROLLBACK,
+ ER(ER_WARNING_NOT_COMPLETE_ROLLBACK)););
+
/*
We must not commit the normal transaction if a statement
transaction is pending. Otherwise statement transaction
@@ -2643,8 +2649,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));
@@ -2662,6 +2678,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:
@@ -2713,21 +2736,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:
@@ -2826,7 +2854,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);
@@ -2834,6 +2865,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 255e22005d3..5fc9eadeff3 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -2057,7 +2057,7 @@ extern ulong slave_exec_mode_options;
extern my_bool opt_readonly, lower_case_file_system;
extern my_bool opt_userstat_running;
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 f8da9432732..daf2e236601 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -488,7 +488,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;
my_bool opt_userstat_running;
ulong log_output_options;
my_bool opt_log_queries_not_using_indexes= 0;
@@ -6037,7 +6037,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,
@@ -6207,6 +6207,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/protocol.cc b/sql/protocol.cc
index 786d6ef8544..8a9d712077d 100644
--- a/sql/protocol.cc
+++ b/sql/protocol.cc
@@ -203,7 +203,7 @@ net_send_ok(THD *thd,
NET *net= &thd->net;
uchar buff[MYSQL_ERRMSG_SIZE+10],*pos;
bool error= FALSE;
- DBUG_ENTER("my_ok");
+ DBUG_ENTER("net_send_ok");
if (! net->vio) // hack for re-parsing queries
{
diff --git a/sql/set_var.cc b/sql/set_var.cc
index 0eaa450d09c..7e00ae5aa19 100644
--- a/sql/set_var.cc
+++ b/sql/set_var.cc
@@ -4369,7 +4369,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 e8dcc557f05..e14c8961165 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -2045,6 +2045,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);
}
}
@@ -3076,8 +3078,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);
@@ -3364,7 +3367,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.
@@ -3374,9 +3378,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
@@ -3411,7 +3416,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;
/*
@@ -3560,7 +3565,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);
@@ -3898,6 +3903,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);
@@ -4018,8 +4024,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;
@@ -4053,12 +4061,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))
@@ -4286,7 +4295,6 @@ void detach_merge_children(TABLE *table, bool clear_refs)
{
TABLE_LIST *child_l;
TABLE *parent= table->child_l ? table : table->parent;
- bool first_detach;
DBUG_ENTER("detach_merge_children");
/*
Either table->child_l or table->parent must be set. Parent must have
@@ -4304,7 +4312,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;
@@ -4316,38 +4324,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;
}
}
@@ -5144,9 +5164,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 cd408ac723f..a1f8dbab175 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -328,7 +328,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_class.h b/sql/sql_class.h
index b3d78c4c801..7e044f63d0f 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -1313,6 +1313,13 @@ public:
return m_total_warn_count;
}
+ /* Used to count any warnings pushed after calling set_ok_status(). */
+ void increment_warning()
+ {
+ if (m_status != DA_EMPTY)
+ m_total_warn_count++;
+ }
+
Diagnostics_area() { reset_diagnostics_area(); }
private:
diff --git a/sql/sql_error.cc b/sql/sql_error.cc
index 9ea7facbe41..835e60cd6ba 100644
--- a/sql/sql_error.cc
+++ b/sql/sql_error.cc
@@ -159,6 +159,8 @@ MYSQL_ERROR *push_warning(THD *thd, MYSQL_ERROR::enum_warning_level level,
}
thd->warn_count[(uint) level]++;
thd->total_warn_count++;
+ /* Make sure we also count warnings pushed after calling set_ok_status(). */
+ thd->main_da.increment_warning();
DBUG_RETURN(err);
}
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 94164a30311..90ebfb8a223 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -8014,6 +8014,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. */
@@ -8047,7 +8049,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..c0763b0612d 100644
--- a/storage/maria/ma_bitmap.c
+++ b/storage/maria/ma_bitmap.c
@@ -227,7 +227,8 @@ my_bool _ma_bitmap_init(MARIA_SHARE *share, File file)
The +1 is to add the bitmap page, as this doesn't have to be covered
*/
bitmap->pages_covered= aligned_bit_blocks * 16 + 1;
- bitmap->flush_all_requested= bitmap->non_flushable= 0;
+ bitmap->flush_all_requested= 0;
+ bitmap->non_flushable= 0;
/* Update size for bits */
/* TODO; Make this dependent of the row size */
@@ -311,8 +312,8 @@ my_bool _ma_bitmap_flush(MARIA_SHARE *share)
pthread_mutex_lock(&share->bitmap.bitmap_lock);
if (share->bitmap.changed)
{
- share->bitmap.changed= 0;
res= write_changed_bitmap(share, &share->bitmap);
+ share->bitmap.changed= 0;
}
pthread_mutex_unlock(&share->bitmap.bitmap_lock);
}
@@ -355,7 +356,7 @@ my_bool _ma_bitmap_flush_all(MARIA_SHARE *share)
pthread_mutex_lock(&bitmap->bitmap_lock);
if (bitmap->changed || bitmap->changed_not_flushed)
{
- bitmap->flush_all_requested= TRUE;
+ bitmap->flush_all_requested++;
#ifndef WRONG_BITMAP_FLUSH
while (bitmap->non_flushable > 0)
{
@@ -363,6 +364,7 @@ my_bool _ma_bitmap_flush_all(MARIA_SHARE *share)
pthread_cond_wait(&bitmap->bitmap_cond, &bitmap->bitmap_lock);
}
#endif
+ DBUG_ASSERT(bitmap->flush_all_requested == 1);
/*
Bitmap is in a flushable state: its contents in memory are reflected by
log records (complete REDO-UNDO groups) and all bitmap pages are
@@ -391,7 +393,7 @@ my_bool _ma_bitmap_flush_all(MARIA_SHARE *share)
PCFLUSH_PINNED_AND_ERROR)
res= TRUE;
bitmap->changed_not_flushed= FALSE;
- bitmap->flush_all_requested= FALSE;
+ bitmap->flush_all_requested--;
/*
Some well-behaved threads may be waiting for flush_all_requested to
become false, wake them up.
@@ -405,6 +407,70 @@ my_bool _ma_bitmap_flush_all(MARIA_SHARE *share)
/**
+ @brief Lock bitmap from being used by another thread
+
+ @fn _ma_bitmap_lock()
+ @param share Table's share
+
+ @notes
+ This is a temporary solution for allowing someone to delete an inserted
+ duplicate-key row while someone else is doing concurrent inserts.
+ This is ok for now as duplicate key errors are not that common.
+
+ In the future we will add locks for row-pages to ensure two threads doesn't
+ work at the same time on the same page.
+*/
+
+void _ma_bitmap_lock(MARIA_SHARE *share)
+{
+ MARIA_FILE_BITMAP *bitmap= &share->bitmap;
+ DBUG_ENTER("_ma_bitmap_lock");
+
+ if (!share->now_transactional)
+ DBUG_VOID_RETURN;
+
+ pthread_mutex_lock(&bitmap->bitmap_lock);
+ bitmap->flush_all_requested++;
+ while (bitmap->non_flushable)
+ {
+ DBUG_PRINT("info", ("waiting for bitmap to be flushable"));
+ pthread_cond_wait(&bitmap->bitmap_cond, &bitmap->bitmap_lock);
+ }
+ /*
+ Ensure that _ma_bitmap_flush_all() and _ma_bitmap_lock() are blocked.
+ ma_bitmap_flushable() is blocked thanks to 'flush_all_requested'.
+ */
+ bitmap->non_flushable= 1;
+ pthread_mutex_unlock(&bitmap->bitmap_lock);
+ DBUG_VOID_RETURN;
+}
+
+/**
+ @brief Unlock bitmap after _ma_bitmap_lock()
+
+ @fn _ma_bitmap_unlock()
+ @param share Table's share
+*/
+
+void _ma_bitmap_unlock(MARIA_SHARE *share)
+{
+ MARIA_FILE_BITMAP *bitmap= &share->bitmap;
+ DBUG_ENTER("_ma_bitmap_unlock");
+
+ if (!share->now_transactional)
+ DBUG_VOID_RETURN;
+ DBUG_ASSERT(bitmap->flush_all_requested > 0 && bitmap->non_flushable == 1);
+
+ pthread_mutex_lock(&bitmap->bitmap_lock);
+ bitmap->flush_all_requested--;
+ bitmap->non_flushable= 0;
+ pthread_mutex_unlock(&bitmap->bitmap_lock);
+ pthread_cond_broadcast(&bitmap->bitmap_cond);
+ DBUG_VOID_RETURN;
+}
+
+
+/**
@brief Unpin all pinned bitmap pages
@param share Table's share
@@ -633,11 +699,12 @@ static void _ma_print_bitmap_changes(MARIA_FILE_BITMAP *bitmap)
{
uchar *pos, *end, *org_pos;
ulong page;
+ DBUG_ENTER("_ma_print_bitmap_changes");
end= bitmap->map + bitmap->used_size;
DBUG_LOCK_FILE;
- fprintf(DBUG_FILE,"\nBitmap page changes at page %lu\n",
- (ulong) bitmap->page);
+ fprintf(DBUG_FILE,"\nBitmap page changes at page: %lu bitmap: 0x%lx\n",
+ (ulong) bitmap->page, (long) bitmap->map);
page= (ulong) bitmap->page+1;
for (pos= bitmap->map, org_pos= bitmap->map + bitmap->block_size ;
@@ -666,6 +733,7 @@ static void _ma_print_bitmap_changes(MARIA_FILE_BITMAP *bitmap)
fputc('\n', DBUG_FILE);
DBUG_UNLOCK_FILE;
memcpy(bitmap->map + bitmap->block_size, bitmap->map, bitmap->block_size);
+ DBUG_VOID_RETURN;
}
@@ -877,6 +945,7 @@ static void fill_block(MARIA_FILE_BITMAP *bitmap,
{
uint page, offset, tmp;
uchar *data;
+ DBUG_ENTER("fill_block");
/* For each 6 bytes we have 6*8/3= 16 patterns */
page= ((uint) (best_data - bitmap->map)) / 6 * 16 + best_pos;
@@ -902,6 +971,7 @@ static void fill_block(MARIA_FILE_BITMAP *bitmap,
int2store(data, tmp);
bitmap->changed= 1;
DBUG_EXECUTE("bitmap", _ma_print_bitmap_changes(bitmap););
+ DBUG_VOID_RETURN;
}
@@ -1016,7 +1086,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)
{
@@ -1510,6 +1584,8 @@ static void use_head(MARIA_HA *info, pgcache_page_no_t page, uint size,
MARIA_BITMAP_BLOCK *block;
uchar *data;
uint offset, tmp, offset_page;
+ DBUG_ENTER("use_head");
+
DBUG_ASSERT(page % bitmap->pages_covered);
block= dynamic_element(&info->bitmap_blocks, block_position,
@@ -1532,6 +1608,7 @@ static void use_head(MARIA_HA *info, pgcache_page_no_t page, uint size,
int2store(data, tmp);
bitmap->changed= 1;
DBUG_EXECUTE("bitmap", _ma_print_bitmap_changes(bitmap););
+ DBUG_VOID_RETURN;
}
diff --git a/storage/maria/ma_blockrec.c b/storage/maria/ma_blockrec.c
index 92ec916a9d1..fd02e2ac0ec 100644
--- a/storage/maria/ma_blockrec.c
+++ b/storage/maria/ma_blockrec.c
@@ -1993,19 +1993,6 @@ static my_bool write_tail(MARIA_HA *info,
/* Keep BLOCKUSED_USE_ORG_BITMAP */
block->used|= BLOCKUSED_USED | BLOCKUSED_TAIL;
- /* Increase data file size, if extended */
- position= (my_off_t) block->page * block_size;
- if (share->state.state.data_file_length <= position)
- {
- /*
- We are modifying a state member before writing the UNDO; this is a WAL
- violation. But for data_file_length this is ok, as long as we change
- data_file_length after writing any log record (FILE_ID/REDO/UNDO) (see
- collect_tables()).
- */
- _ma_set_share_data_file_length(share, position + block_size);
- }
-
if (block_is_read)
{
/* Current page link is last element in pinned_pages */
@@ -2021,17 +2008,33 @@ static my_bool write_tail(MARIA_HA *info,
page_link->unlock= PAGECACHE_LOCK_READ_UNLOCK;
res= 0;
}
- else if (!(res= pagecache_write(share->pagecache,
- &info->dfile, block->page, 0,
- row_pos.buff,share->page_type,
- PAGECACHE_LOCK_READ,
- PAGECACHE_PIN,
- PAGECACHE_WRITE_DELAY, &page_link.link,
- LSN_IMPOSSIBLE)))
+ else
{
- page_link.unlock= PAGECACHE_LOCK_READ_UNLOCK;
- page_link.changed= 1;
- push_dynamic(&info->pinned_pages, (void*) &page_link);
+ if (!(res= pagecache_write(share->pagecache,
+ &info->dfile, block->page, 0,
+ row_pos.buff,share->page_type,
+ PAGECACHE_LOCK_READ,
+ PAGECACHE_PIN,
+ PAGECACHE_WRITE_DELAY, &page_link.link,
+ LSN_IMPOSSIBLE)))
+ {
+ page_link.unlock= PAGECACHE_LOCK_READ_UNLOCK;
+ page_link.changed= 1;
+ push_dynamic(&info->pinned_pages, (void*) &page_link);
+ }
+
+ /* Increase data file size, if extended */
+ position= (my_off_t) block->page * block_size;
+ if (share->state.state.data_file_length <= position)
+ {
+ /*
+ We are modifying a state member before writing the UNDO; this is a WAL
+ violation. But for data_file_length this is ok, as long as we change
+ data_file_length after writing any log record (FILE_ID/REDO/UNDO) (see
+ collect_tables()).
+ */
+ _ma_set_share_data_file_length(share, position + block_size);
+ }
}
DBUG_RETURN(res);
}
@@ -2068,7 +2071,7 @@ static my_bool write_full_pages(MARIA_HA *info,
uint data_size= FULL_PAGE_SIZE(block_size);
uchar *buff= info->keyread_buff;
uint page_count, sub_blocks;
- my_off_t position;
+ my_off_t position, max_position;
DBUG_ENTER("write_full_pages");
DBUG_PRINT("enter", ("length: %lu page: %lu page_count: %lu",
(ulong) length, (ulong) block->page,
@@ -2080,9 +2083,7 @@ static my_bool write_full_pages(MARIA_HA *info,
page_count= block->page_count;
sub_blocks= block->sub_blocks;
- position= (my_off_t) (page + page_count) * block_size;
- if (share->state.state.data_file_length < position)
- _ma_set_share_data_file_length(share, position);
+ max_position= (my_off_t) (page + page_count) * block_size;
/* Increase data file size, if extended */
@@ -2105,8 +2106,7 @@ static my_bool write_full_pages(MARIA_HA *info,
(ulong) block->page, (ulong) block->page_count));
position= (page + page_count + 1) * block_size;
- if (share->state.state.data_file_length < position)
- _ma_set_share_data_file_length(share, position);
+ set_if_bigger(max_position, position);
}
lsn_store(buff, lsn);
buff[PAGE_TYPE_OFFSET]= (uchar) BLOB_PAGE;
@@ -2134,6 +2134,8 @@ static my_bool write_full_pages(MARIA_HA *info,
page++;
DBUG_ASSERT(block->used & BLOCKUSED_USED);
}
+ if (share->state.state.data_file_length < max_position)
+ _ma_set_share_data_file_length(share, max_position);
DBUG_RETURN(0);
}
@@ -2823,6 +2825,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 */
@@ -3117,11 +3123,6 @@ static my_bool write_block_record(MARIA_HA *info,
}
#endif
- /* Increase data file size, if extended */
- position= (my_off_t) head_block->page * block_size;
- if (share->state.state.data_file_length <= position)
- _ma_set_share_data_file_length(share, position + block_size);
-
if (head_block_is_read)
{
MARIA_PINNED_PAGE *page_link;
@@ -3150,6 +3151,11 @@ static my_bool write_block_record(MARIA_HA *info,
page_link.unlock= PAGECACHE_LOCK_READ_UNLOCK;
page_link.changed= 1;
push_dynamic(&info->pinned_pages, (void*) &page_link);
+
+ /* Increase data file size, if extended */
+ position= (my_off_t) head_block->page * block_size;
+ if (share->state.state.data_file_length <= position)
+ _ma_set_share_data_file_length(share, position + block_size);
}
if (share->now_transactional && (tmp_data_used || blob_full_pages_exists))
@@ -3588,7 +3594,7 @@ my_bool _ma_write_abort_block_record(MARIA_HA *info)
MARIA_SHARE *share= info->s;
DBUG_ENTER("_ma_write_abort_block_record");
- _ma_bitmap_flushable(info, 1);
+ _ma_bitmap_lock(share); /* Lock bitmap from other insert threads */
if (delete_head_or_tail(info,
ma_recordpos_to_page(info->cur_row.lastpos),
ma_recordpos_to_dir_entry(info->cur_row.lastpos), 1,
@@ -3626,7 +3632,7 @@ my_bool _ma_write_abort_block_record(MARIA_HA *info)
&lsn, (void*) 0))
res= 1;
}
- _ma_bitmap_flushable(info, -1);
+ _ma_bitmap_unlock(share);
_ma_unpin_all_pages_and_finalize_row(info, lsn);
DBUG_RETURN(res);
}
@@ -5158,6 +5164,7 @@ my_bool _ma_scan_init_block_record(MARIA_HA *info)
info->scan.number_of_rows= 0;
info->scan.bitmap_pos= info->scan.bitmap_end;
info->scan.bitmap_page= (pgcache_page_no_t) 0 - share->bitmap.pages_covered;
+ info->scan.max_page= share->state.state.data_file_length / share->block_size;
/*
We need to flush what's in memory (bitmap.map) to page cache otherwise, as
we are going to read bitmaps from page cache in table scan (see
@@ -5359,6 +5366,11 @@ restart_bitmap_scan:
page= (info->scan.bitmap_page + 1 +
(data - info->scan.bitmap_buff) / 6 * 16 + bit_pos - 1);
info->scan.row_base_page= ma_recordpos(page, 0);
+ if (page >= info->scan.max_page)
+ {
+ DBUG_PRINT("info", ("Found end of file"));
+ DBUG_RETURN((my_errno= HA_ERR_END_OF_FILE));
+ }
if (!(pagecache_read(share->pagecache,
&info->dfile,
page, 0, info->scan.page_buff,
@@ -7161,6 +7173,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..a5858880dd0 100644
--- a/storage/maria/ma_blockrec.h
+++ b/storage/maria/ma_blockrec.h
@@ -217,6 +217,8 @@ uint _ma_bitmap_get_page_bits(MARIA_HA *info, MARIA_FILE_BITMAP *bitmap,
void _ma_bitmap_delete_all(MARIA_SHARE *share);
int _ma_bitmap_create_first(MARIA_SHARE *share);
void _ma_bitmap_flushable(MARIA_HA *info, int non_flushable_inc);
+void _ma_bitmap_lock(MARIA_SHARE *share);
+void _ma_bitmap_unlock(MARIA_SHARE *share);
void _ma_bitmap_set_pagecache_callbacks(PAGECACHE_FILE *file,
MARIA_SHARE *share);
#ifndef DBUG_OFF
@@ -281,7 +283,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_dbug.c b/storage/maria/ma_dbug.c
index 0d2db0d4f4b..af90a108e2a 100644
--- a/storage/maria/ma_dbug.c
+++ b/storage/maria/ma_dbug.c
@@ -137,6 +137,7 @@ void _ma_print_keydata(FILE *stream, register HA_KEYSEG *keyseg,
key=end;
break;
}
+#endif
case HA_KEYTYPE_BIT:
{
uint i;
@@ -146,8 +147,6 @@ void _ma_print_keydata(FILE *stream, register HA_KEYSEG *keyseg,
key= end;
break;
}
-
-#endif
case HA_KEYTYPE_VARTEXT1: /* VARCHAR and TEXT */
case HA_KEYTYPE_VARTEXT2: /* VARCHAR and TEXT */
case HA_KEYTYPE_VARBINARY1: /* VARBINARY and BLOB */
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/maria/maria_def.h b/storage/maria/maria_def.h
index e58b149a051..ba97684b1aa 100644
--- a/storage/maria/maria_def.h
+++ b/storage/maria/maria_def.h
@@ -246,7 +246,7 @@ typedef struct st_maria_file_bitmap
uint used_size; /* Size of bitmap head that is not 0 */
my_bool changed; /* 1 if page needs to be written */
my_bool changed_not_flushed; /* 1 if some bitmap is not flushed */
- my_bool flush_all_requested; /**< If _ma_bitmap_flush_all waiting */
+ uint flush_all_requested; /**< If _ma_bitmap_flush_all waiting */
uint non_flushable; /**< 0 if bitmap and log are in sync */
PAGECACHE_FILE file; /* datafile where bitmap is stored */
@@ -476,7 +476,7 @@ typedef struct st_maria_block_scan
{
uchar *bitmap_buff, *bitmap_pos, *bitmap_end, *page_buff;
uchar *dir, *dir_end;
- pgcache_page_no_t bitmap_page;
+ pgcache_page_no_t bitmap_page, max_page;
ulonglong bits;
uint number_of_rows, bit_pos;
MARIA_RECORD_POS row_base_page;
diff --git a/storage/myisam/mi_check.c b/storage/myisam/mi_check.c
index 6df148857c5..d24dba3d6b3 100644
--- a/storage/myisam/mi_check.c
+++ b/storage/myisam/mi_check.c
@@ -1338,7 +1338,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_dbug.c b/storage/myisam/mi_dbug.c
index 1a61d4823b5..ab85ece07ab 100644
--- a/storage/myisam/mi_dbug.c
+++ b/storage/myisam/mi_dbug.c
@@ -131,6 +131,7 @@ void _mi_print_key(FILE *stream, register HA_KEYSEG *keyseg,
key=end;
break;
}
+#endif
case HA_KEYTYPE_BIT:
{
uint i;
@@ -140,8 +141,6 @@ void _mi_print_key(FILE *stream, register HA_KEYSEG *keyseg,
key= end;
break;
}
-
-#endif
case HA_KEYTYPE_VARTEXT1: /* VARCHAR and TEXT */
case HA_KEYTYPE_VARTEXT2: /* VARCHAR and TEXT */
case HA_KEYTYPE_VARBINARY1: /* VARBINARY and BLOB */
diff --git a/storage/myisam/mi_extra.c b/storage/myisam/mi_extra.c
index deeb5d87cc1..648e5fb0024 100644
--- a/storage/myisam/mi_extra.c
+++ b/storage/myisam/mi_extra.c
@@ -392,6 +392,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 741b9b01986..97011831af8 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 &&
@@ -246,7 +248,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_flag & HA_OPEN_MERGE_TABLE) && (info->s)->kfile < 0)
{
error = HA_ERR_NO_SUCH_TABLE;
}
@@ -275,9 +277,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 ||
@@ -308,9 +312,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",
@@ -344,6 +350,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)
{
@@ -409,6 +433,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 846be8f3b2e..20974e0b717 100644
--- a/storage/myisam/myisamdef.h
+++ b/storage/myisam/myisamdef.h
@@ -277,7 +277,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 */
@@ -303,12 +305,8 @@ 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 */
index_cond_func_t index_cond_func; /* Index condition function */
void *index_cond_func_arg; /* parameter for the func */
-#ifdef __WIN__
- my_bool owned_by_merge; /* This MyISAM table is part of a merge union */
-#endif
#ifdef THREAD
THR_LOCK_DATA lock;
#endif
@@ -717,6 +715,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 be3f0ca4813..c61d65572cb 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;
diff --git a/storage/xtradb/include/os0sync.h b/storage/xtradb/include/os0sync.h
index 0c22162b900..c230a03b6db 100644
--- a/storage/xtradb/include/os0sync.h
+++ b/storage/xtradb/include/os0sync.h
@@ -189,7 +189,7 @@ os_event_wait_low(
/**********************************************************//**
Waits for an event object until it is in the signaled state or
-a timeout is exceeded. In Unix the timeout is always infinite.
+a timeout is exceeded.
@return 0 if success, OS_SYNC_TIME_EXCEEDED if timeout was exceeded */
UNIV_INTERN
ulint
diff --git a/storage/xtradb/include/srv0srv.h b/storage/xtradb/include/srv0srv.h
index dc455581350..8c64d5cee71 100644
--- a/storage/xtradb/include/srv0srv.h
+++ b/storage/xtradb/include/srv0srv.h
@@ -57,6 +57,9 @@ extern const char srv_mysql50_table_name_prefix[9];
thread starts running */
extern os_event_t srv_lock_timeout_thread_event;
+/* This event is set to tell the purge thread to shut down */
+extern os_event_t srv_purge_thread_event;
+
/* If the last data file is auto-extended, we add this many pages to it
at a time */
#define SRV_AUTO_EXTEND_INCREMENT \
diff --git a/storage/xtradb/log/log0log.c b/storage/xtradb/log/log0log.c
index fade31037b5..b9f19aeff31 100644
--- a/storage/xtradb/log/log0log.c
+++ b/storage/xtradb/log/log0log.c
@@ -3102,6 +3102,7 @@ logs_empty_and_mark_files_at_shutdown(void)
algorithm only works if the server is idle at shutdown */
srv_shutdown_state = SRV_SHUTDOWN_CLEANUP;
+ os_event_set(srv_purge_thread_event);
loop:
os_thread_sleep(100000);
diff --git a/storage/xtradb/os/os0sync.c b/storage/xtradb/os/os0sync.c
index 60467242e14..f9ab58c2ee4 100644
--- a/storage/xtradb/os/os0sync.c
+++ b/storage/xtradb/os/os0sync.c
@@ -31,6 +31,9 @@ Created 9/6/1995 Heikki Tuuri
#ifdef __WIN__
#include <windows.h>
+#else
+#include <sys/time.h>
+#include <time.h>
#endif
#include "ut0mem.h"
@@ -407,14 +410,14 @@ os_event_wait_low(
/**********************************************************//**
Waits for an event object until it is in the signaled state or
-a timeout is exceeded. In Unix the timeout is always infinite.
+a timeout is exceeded.
@return 0 if success, OS_SYNC_TIME_EXCEEDED if timeout was exceeded */
UNIV_INTERN
ulint
os_event_wait_time(
/*===============*/
os_event_t event, /*!< in: event to wait */
- ulint time) /*!< in: timeout in microseconds, or
+ ulint wtime) /*!< in: timeout in microseconds, or
OS_SYNC_INFINITE_TIME */
{
#ifdef __WIN__
@@ -422,8 +425,8 @@ os_event_wait_time(
ut_a(event);
- if (time != OS_SYNC_INFINITE_TIME) {
- err = WaitForSingleObject(event->handle, (DWORD) time / 1000);
+ if (wtime != OS_SYNC_INFINITE_TIME) {
+ err = WaitForSingleObject(event->handle, (DWORD) wtime / 1000);
} else {
err = WaitForSingleObject(event->handle, INFINITE);
}
@@ -439,13 +442,47 @@ os_event_wait_time(
return(1000000); /* dummy value to eliminate compiler warn. */
}
#else
- UT_NOT_USED(time);
+ int err;
+ int ret = 0;
+ ulint tmp;
+ ib_int64_t old_count;
+ struct timeval tv_start;
+ struct timespec timeout;
+
+ if (wtime == OS_SYNC_INFINITE_TIME) {
+ os_event_wait(event);
+ return 0;
+ }
+
+ /* Compute the absolute point in time at which to time out. */
+ gettimeofday(&tv_start, NULL);
+ tmp = tv_start.tv_usec + wtime;
+ timeout.tv_sec = tv_start.tv_sec + (tmp / 1000000);
+ timeout.tv_nsec = (tmp % 1000000) * 1000;
+
+ os_fast_mutex_lock(&(event->os_mutex));
+ old_count = event->signal_count;
+
+ for (;;) {
+ if (event->is_set == TRUE || event->signal_count != old_count)
+ break;
+
+ err = pthread_cond_timedwait(&(event->cond_var),
+ &(event->os_mutex), &timeout);
+ if (err == ETIMEDOUT) {
+ ret = OS_SYNC_TIME_EXCEEDED;
+ break;
+ }
+ }
- /* In Posix this is just an ordinary, infinite wait */
+ os_fast_mutex_unlock(&(event->os_mutex));
- os_event_wait(event);
+ if (srv_shutdown_state == SRV_SHUTDOWN_EXIT_THREADS) {
+
+ os_thread_exit(NULL);
+ }
- return(0);
+ return ret;
#endif
}
diff --git a/storage/xtradb/srv/srv0srv.c b/storage/xtradb/srv/srv0srv.c
index c1d0f255c64..43799aab196 100644
--- a/storage/xtradb/srv/srv0srv.c
+++ b/storage/xtradb/srv/srv0srv.c
@@ -704,6 +704,8 @@ UNIV_INTERN srv_slot_t* srv_mysql_table = NULL;
UNIV_INTERN os_event_t srv_lock_timeout_thread_event;
+UNIV_INTERN os_event_t srv_purge_thread_event;
+
UNIV_INTERN srv_sys_t* srv_sys = NULL;
/* padding to prevent other memory update hotspots from residing on
@@ -1009,6 +1011,7 @@ srv_init(void)
}
srv_lock_timeout_thread_event = os_event_create(NULL);
+ srv_purge_thread_event = os_event_create(NULL);
for (i = 0; i < SRV_MASTER + 1; i++) {
srv_n_threads_active[i] = 0;
@@ -3337,9 +3340,10 @@ loop:
mutex_exit(&kernel_mutex);
sleep_ms = 10;
+ os_event_reset(srv_purge_thread_event);
}
- os_thread_sleep( sleep_ms * 1000 );
+ os_event_wait_time(srv_purge_thread_event, sleep_ms * 1000);
history_len = trx_sys->rseg_history_len;
if (history_len > 1000)