mirror of
https://github.com/MariaDB/server.git
synced 2025-01-27 17:33:44 +01:00
Merge stella.local:/home2/mydev/mysql-5.1-ateam
into stella.local:/home2/mydev/mysql-5.1-axmrg sql/ha_ndbcluster_binlog.cc: Auto merged sql/handler.cc: Auto merged sql/slave.cc: Auto merged sql/sql_base.cc: Auto merged sql/sql_insert.cc: Auto merged
This commit is contained in:
commit
411d9925fb
37 changed files with 4434 additions and 329 deletions
|
@ -187,7 +187,13 @@ enum ha_extra_function {
|
|||
Inform handler that an "INSERT...ON DUPLICATE KEY UPDATE" will be
|
||||
executed. This condition is unset by HA_EXTRA_NO_IGNORE_DUP_KEY.
|
||||
*/
|
||||
HA_EXTRA_INSERT_WITH_UPDATE
|
||||
HA_EXTRA_INSERT_WITH_UPDATE,
|
||||
/*
|
||||
Orders MERGE handler to attach or detach its child tables. Used at
|
||||
begin and end of a statement.
|
||||
*/
|
||||
HA_EXTRA_ATTACH_CHILDREN,
|
||||
HA_EXTRA_DETACH_CHILDREN
|
||||
};
|
||||
|
||||
/* The following is parameter to ha_panic() */
|
||||
|
|
|
@ -69,6 +69,8 @@ typedef struct st_myrg_info
|
|||
uint merge_insert_method;
|
||||
uint tables,options,reclength,keys;
|
||||
my_bool cache_in_use;
|
||||
/* If MERGE children attached to parent. See top comment in ha_myisammrg.cc */
|
||||
my_bool children_attached;
|
||||
LIST open_list;
|
||||
QUEUE by_key;
|
||||
ulong *rec_per_key_part; /* for sql optimizing */
|
||||
|
@ -80,6 +82,13 @@ typedef struct st_myrg_info
|
|||
extern int myrg_close(MYRG_INFO *file);
|
||||
extern int myrg_delete(MYRG_INFO *file,const uchar *buff);
|
||||
extern MYRG_INFO *myrg_open(const char *name,int mode,int wait_if_locked);
|
||||
extern MYRG_INFO *myrg_parent_open(const char *parent_name,
|
||||
int (*callback)(void*, const char*),
|
||||
void *callback_param);
|
||||
extern int myrg_attach_children(MYRG_INFO *m_info, int handle_locking,
|
||||
MI_INFO *(*callback)(void*),
|
||||
void *callback_param);
|
||||
extern int myrg_detach_children(MYRG_INFO *m_info);
|
||||
extern int myrg_panic(enum ha_panic_function function);
|
||||
extern int myrg_rfirst(MYRG_INFO *file,uchar *buf,int inx);
|
||||
extern int myrg_rlast(MYRG_INFO *file,uchar *buf,int inx);
|
||||
|
|
|
@ -134,6 +134,15 @@ drop table t1,t2,t3;
|
|||
# table
|
||||
#
|
||||
CREATE TABLE t1(a INT) ENGINE=BLACKHOLE;
|
||||
# NOTE: After exchanging open_ltable() by open_and_lock_tables() in
|
||||
# handle_delayed_insert() to fix problems with MERGE tables (Bug#26379),
|
||||
# problems with INSERT DELAYED and BLACKHOLE popped up. open_ltable()
|
||||
# does not check if the binlogging capabilities of the statement and the
|
||||
# table match. So the below used to succeed. But since INSERT DELAYED
|
||||
# switches to row-based logging in mixed-mode and BLACKHOLE cannot do
|
||||
# row-based logging, it could not really work. Until this problem is
|
||||
# correctly fixed, we have that error here.
|
||||
--error ER_BINLOG_LOGGING_IMPOSSIBLE
|
||||
INSERT DELAYED INTO t1 VALUES(1);
|
||||
DROP TABLE t1;
|
||||
|
||||
|
|
|
@ -594,7 +594,7 @@ create table t1 (a int);
|
|||
create table t1 select * from t1;
|
||||
ERROR HY000: You can't specify target table 't1' for update in FROM clause
|
||||
create table t2 union = (t1) select * from t1;
|
||||
ERROR HY000: You can't specify target table 't1' for update in FROM clause
|
||||
ERROR HY000: 'test.t2' is not BASE TABLE
|
||||
flush tables with read lock;
|
||||
unlock tables;
|
||||
drop table t1;
|
||||
|
|
|
@ -250,11 +250,6 @@ SELECT HEX(a) FROM t1;
|
|||
HEX(a)
|
||||
1
|
||||
DROP TABLE t1;
|
||||
CREATE TABLE t1(c1 INT) ENGINE=MyISAM;
|
||||
CREATE TABLE t2(c1 INT) ENGINE=MERGE UNION=(t1);
|
||||
INSERT DELAYED INTO t2 VALUES(1);
|
||||
ERROR HY000: Table storage engine for 't2' doesn't have this option
|
||||
DROP TABLE t1, t2;
|
||||
DROP TABLE IF EXISTS t1,t2;
|
||||
SET SQL_MODE='NO_AUTO_VALUE_ON_ZERO';
|
||||
CREATE TABLE `t1` (
|
||||
|
|
77
mysql-test/r/merge-big.result
Normal file
77
mysql-test/r/merge-big.result
Normal file
|
@ -0,0 +1,77 @@
|
|||
drop table if exists t1,t2,t3,t4,t5,t6;
|
||||
#
|
||||
# Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE
|
||||
# corrupts a MERGE table
|
||||
# Problem #3
|
||||
#
|
||||
CREATE TABLE t1 (c1 INT) ENGINE= MyISAM;
|
||||
LOCK TABLE t1 WRITE;
|
||||
# connection con1
|
||||
SET SESSION debug="+d,sleep_open_and_lock_after_open";
|
||||
INSERT INTO t1 VALUES (1);
|
||||
# connection default
|
||||
# Let INSERT go into thr_multi_lock().
|
||||
# Kick INSERT out of thr_multi_lock().
|
||||
FLUSH TABLES;
|
||||
# Let INSERT go through open_tables() where it sleeps.
|
||||
# Unlock and close table and wait for con1 to close too.
|
||||
FLUSH TABLES;
|
||||
# This should give no result.
|
||||
SELECT * FROM t1;
|
||||
c1
|
||||
UNLOCK TABLES;
|
||||
# connection con1
|
||||
SET SESSION debug="-d,sleep_open_and_lock_after_open";
|
||||
# connection default
|
||||
DROP TABLE t1;
|
||||
#
|
||||
# Extra tests for Bug#26379 - Combination of FLUSH TABLE and
|
||||
# REPAIR TABLE corrupts a MERGE table
|
||||
#
|
||||
CREATE TABLE t1 (c1 INT);
|
||||
CREATE TABLE t2 (c1 INT);
|
||||
CREATE TABLE t3 (c1 INT);
|
||||
INSERT INTO t1 VALUES (1);
|
||||
INSERT INTO t2 VALUES (2);
|
||||
INSERT INTO t3 VALUES (3);
|
||||
#
|
||||
# CREATE ... SELECT
|
||||
# try to access parent from another thread.
|
||||
#
|
||||
# connection con1
|
||||
SET SESSION debug="+d,sleep_create_select_before_lock";
|
||||
CREATE TABLE t4 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1,t2)
|
||||
INSERT_METHOD=FIRST SELECT * FROM t3;
|
||||
# connection default
|
||||
# Now try to access the parent.
|
||||
# If 3 is in table, SELECT had to wait.
|
||||
SELECT * FROM t4 ORDER BY c1;
|
||||
c1
|
||||
1
|
||||
2
|
||||
3
|
||||
# connection con1
|
||||
SET SESSION debug="-d,sleep_create_select_before_lock";
|
||||
# connection default
|
||||
# Cleanup for next test.
|
||||
DROP TABLE t4;
|
||||
DELETE FROM t1 WHERE c1 != 1;
|
||||
#
|
||||
# CREATE ... SELECT
|
||||
# try to access child from another thread.
|
||||
#
|
||||
# connection con1
|
||||
SET SESSION debug="+d,sleep_create_select_before_lock";
|
||||
CREATE TABLE t4 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1,t2)
|
||||
INSERT_METHOD=FIRST SELECT * FROM t3;
|
||||
# connection default
|
||||
# Now try to access a child.
|
||||
# If 3 is in table, SELECT had to wait.
|
||||
SELECT * FROM t1 ORDER BY c1;
|
||||
c1
|
||||
1
|
||||
3
|
||||
# connection con1
|
||||
SET SESSION debug="-d,sleep_create_select_before_lock";
|
||||
# connection default
|
||||
DROP TABLE t1, t2, t3, t4;
|
File diff suppressed because it is too large
Load diff
|
@ -606,24 +606,6 @@ select count(*) from t1 where a is null;
|
|||
count(*)
|
||||
2
|
||||
drop table t1;
|
||||
create table t1 (c1 int, index(c1));
|
||||
create table t2 (c1 int, index(c1)) engine=merge union=(t1);
|
||||
insert into t1 values (1);
|
||||
flush tables;
|
||||
select * from t2;
|
||||
c1
|
||||
1
|
||||
flush tables;
|
||||
truncate table t1;
|
||||
insert into t1 values (1);
|
||||
flush tables;
|
||||
select * from t2;
|
||||
c1
|
||||
1
|
||||
truncate table t1;
|
||||
ERROR HY000: MyISAM table 't1' is in use (most likely by a MERGE table). Try FLUSH TABLES.
|
||||
insert into t1 values (1);
|
||||
drop table t1,t2;
|
||||
create table t1 (c1 int, c2 varchar(4) not null default '',
|
||||
key(c2(3))) default charset=utf8;
|
||||
insert into t1 values (1,'A'), (2, 'B'), (3, 'A');
|
||||
|
|
|
@ -289,6 +289,13 @@ select * from t1 where a = 4;
|
|||
a b
|
||||
4 4
|
||||
drop table t1;
|
||||
CREATE TABLE t1 (c1 INT, c2 INT, PRIMARY KEY USING BTREE (c1,c2)) ENGINE=MEMORY
|
||||
PARTITION BY KEY(c2,c1) PARTITIONS 4;
|
||||
INSERT INTO t1 VALUES (0,0),(1,1),(2,2),(3,3),(4,4),(5,5),(6,6);
|
||||
SELECT * FROM t1 WHERE c1 = 4;
|
||||
c1 c2
|
||||
4 4
|
||||
DROP TABLE t1;
|
||||
CREATE TABLE t1 (a int)
|
||||
PARTITION BY LIST (a)
|
||||
PARTITIONS 1
|
||||
|
|
|
@ -124,6 +124,7 @@ master-bin.000001 # Query # # use `test`; replace into t1 select * from t3
|
|||
drop table t1,t2,t3;
|
||||
CREATE TABLE t1(a INT) ENGINE=BLACKHOLE;
|
||||
INSERT DELAYED INTO t1 VALUES(1);
|
||||
ERROR HY000: Binary logging not possible. Message: Row-based format required for this statement, but not allowed by this combination of engines
|
||||
DROP TABLE t1;
|
||||
CREATE TABLE t1(a INT, b INT) ENGINE=BLACKHOLE;
|
||||
DELETE FROM t1 WHERE a=10;
|
||||
|
|
|
@ -483,7 +483,7 @@ drop table t1,t2;
|
|||
create table t1 (a int);
|
||||
--error 1093
|
||||
create table t1 select * from t1;
|
||||
--error 1093
|
||||
--error ER_WRONG_OBJECT
|
||||
create table t2 union = (t1) select * from t1;
|
||||
flush tables with read lock;
|
||||
unlock tables;
|
||||
|
|
|
@ -243,14 +243,6 @@ FLUSH TABLE t1;
|
|||
SELECT HEX(a) FROM t1;
|
||||
DROP TABLE t1;
|
||||
|
||||
#
|
||||
# Bug#26464 - insert delayed + update + merge = corruption
|
||||
#
|
||||
CREATE TABLE t1(c1 INT) ENGINE=MyISAM;
|
||||
CREATE TABLE t2(c1 INT) ENGINE=MERGE UNION=(t1);
|
||||
--error 1031
|
||||
INSERT DELAYED INTO t2 VALUES(1);
|
||||
DROP TABLE t1, t2;
|
||||
#
|
||||
# Bug#27358 INSERT DELAYED does not honour SQL_MODE of the client
|
||||
#
|
||||
|
|
150
mysql-test/t/merge-big.test
Normal file
150
mysql-test/t/merge-big.test
Normal file
|
@ -0,0 +1,150 @@
|
|||
#
|
||||
# Test of MERGE tables with multisession and many waits.
|
||||
#
|
||||
# This test takes rather long time so let us run it only in --big-test mode
|
||||
--source include/big_test.inc
|
||||
# We are using some debug-only features in this test
|
||||
--source include/have_debug.inc
|
||||
|
||||
--disable_warnings
|
||||
drop table if exists t1,t2,t3,t4,t5,t6;
|
||||
--enable_warnings
|
||||
|
||||
--echo #
|
||||
--echo # Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE
|
||||
--echo # corrupts a MERGE table
|
||||
--echo # Problem #3
|
||||
--echo #
|
||||
# Two FLUSH TABLES within a LOCK TABLES segment could invalidate the lock.
|
||||
# This did *not* require a MERGE table.
|
||||
#
|
||||
# To increase reproducibility it was necessary to enter a sleep of 2
|
||||
# seconds at the end of wait_for_tables() after unlock of LOCK_open. In
|
||||
# 5.0 and 5.1 the sleep must be inserted in open_and_lock_tables() after
|
||||
# open_tables() instead. wait_for_tables() is not used in this case. The
|
||||
# problem was that FLUSH TABLES releases LOCK_open while having unlocked
|
||||
# and closed all tables. When this happened while a thread was in the
|
||||
# loop in mysql_lock_tables() right after wait_for_tables()
|
||||
# (open_tables()) and before retrying to lock, the thread got the lock.
|
||||
# And it did not notice that the table needed a refresh after the
|
||||
# [re-]open. So it executed its statement on the table.
|
||||
#
|
||||
# The first FLUSH TABLES kicked the INSERT out of thr_multi_lock() and
|
||||
# let it wait in wait_for_tables() (open_table()). The second FLUSH
|
||||
# TABLES must happen while the INSERT was on its way from
|
||||
# wait_for_tables() (open_table()) to the next call of thr_multi_lock().
|
||||
# This needed to be supported by a sleep to make it repeatable.
|
||||
#
|
||||
CREATE TABLE t1 (c1 INT) ENGINE= MyISAM;
|
||||
LOCK TABLE t1 WRITE;
|
||||
#SELECT NOW();
|
||||
--echo # connection con1
|
||||
connect (con1,localhost,root,,);
|
||||
let $con1_id= `SELECT CONNECTION_ID()`;
|
||||
SET SESSION debug="+d,sleep_open_and_lock_after_open";
|
||||
send INSERT INTO t1 VALUES (1);
|
||||
--echo # connection default
|
||||
connection default;
|
||||
--echo # Let INSERT go into thr_multi_lock().
|
||||
let $wait_condition= SELECT 1 FROM INFORMATION_SCHEMA.PROCESSLIST
|
||||
WHERE ID = $con1_id AND STATE = 'Locked';
|
||||
--source include/wait_condition.inc
|
||||
#SELECT NOW();
|
||||
--echo # Kick INSERT out of thr_multi_lock().
|
||||
FLUSH TABLES;
|
||||
#SELECT NOW();
|
||||
--echo # Let INSERT go through open_tables() where it sleeps.
|
||||
let $wait_condition= SELECT 1 FROM INFORMATION_SCHEMA.PROCESSLIST
|
||||
WHERE ID = $con1_id AND STATE = 'DBUG sleep';
|
||||
--source include/wait_condition.inc
|
||||
#SELECT NOW();
|
||||
--echo # Unlock and close table and wait for con1 to close too.
|
||||
FLUSH TABLES;
|
||||
#SELECT NOW();
|
||||
--echo # This should give no result.
|
||||
SELECT * FROM t1;
|
||||
#SELECT NOW();
|
||||
UNLOCK TABLES;
|
||||
--echo # connection con1
|
||||
connection con1;
|
||||
reap;
|
||||
SET SESSION debug="-d,sleep_open_and_lock_after_open";
|
||||
disconnect con1;
|
||||
--echo # connection default
|
||||
connection default;
|
||||
DROP TABLE t1;
|
||||
|
||||
--echo #
|
||||
--echo # Extra tests for Bug#26379 - Combination of FLUSH TABLE and
|
||||
--echo # REPAIR TABLE corrupts a MERGE table
|
||||
--echo #
|
||||
CREATE TABLE t1 (c1 INT);
|
||||
CREATE TABLE t2 (c1 INT);
|
||||
CREATE TABLE t3 (c1 INT);
|
||||
INSERT INTO t1 VALUES (1);
|
||||
INSERT INTO t2 VALUES (2);
|
||||
INSERT INTO t3 VALUES (3);
|
||||
--echo #
|
||||
--echo # CREATE ... SELECT
|
||||
--echo # try to access parent from another thread.
|
||||
--echo #
|
||||
#SELECT NOW();
|
||||
--echo # connection con1
|
||||
connect (con1,localhost,root,,);
|
||||
let $con1_id= `SELECT CONNECTION_ID()`;
|
||||
SET SESSION debug="+d,sleep_create_select_before_lock";
|
||||
send CREATE TABLE t4 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1,t2)
|
||||
INSERT_METHOD=FIRST SELECT * FROM t3;
|
||||
--echo # connection default
|
||||
connection default;
|
||||
# wait for the other query to start executing
|
||||
let $wait_condition= SELECT 1 FROM INFORMATION_SCHEMA.PROCESSLIST
|
||||
WHERE ID = $con1_id AND STATE = 'DBUG sleep';
|
||||
--source include/wait_condition.inc
|
||||
#SELECT NOW();
|
||||
--echo # Now try to access the parent.
|
||||
--echo # If 3 is in table, SELECT had to wait.
|
||||
SELECT * FROM t4 ORDER BY c1;
|
||||
#SELECT NOW();
|
||||
--echo # connection con1
|
||||
connection con1;
|
||||
reap;
|
||||
#SELECT NOW();
|
||||
SET SESSION debug="-d,sleep_create_select_before_lock";
|
||||
disconnect con1;
|
||||
--echo # connection default
|
||||
connection default;
|
||||
--echo # Cleanup for next test.
|
||||
DROP TABLE t4;
|
||||
DELETE FROM t1 WHERE c1 != 1;
|
||||
--echo #
|
||||
--echo # CREATE ... SELECT
|
||||
--echo # try to access child from another thread.
|
||||
--echo #
|
||||
#SELECT NOW();
|
||||
--echo # connection con1
|
||||
connect (con1,localhost,root,,);
|
||||
let $con1_id= `SELECT CONNECTION_ID()`;
|
||||
SET SESSION debug="+d,sleep_create_select_before_lock";
|
||||
send CREATE TABLE t4 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1,t2)
|
||||
INSERT_METHOD=FIRST SELECT * FROM t3;
|
||||
--echo # connection default
|
||||
connection default;
|
||||
# wait for the other query to start executing
|
||||
let $wait_condition= SELECT 1 FROM INFORMATION_SCHEMA.PROCESSLIST
|
||||
WHERE ID = $con1_id AND STATE = 'DBUG sleep';
|
||||
--source include/wait_condition.inc
|
||||
#SELECT NOW();
|
||||
--echo # Now try to access a child.
|
||||
--echo # If 3 is in table, SELECT had to wait.
|
||||
SELECT * FROM t1 ORDER BY c1;
|
||||
#SELECT NOW();
|
||||
--echo # connection con1
|
||||
connection con1;
|
||||
reap;
|
||||
#SELECT NOW();
|
||||
SET SESSION debug="-d,sleep_create_select_before_lock";
|
||||
disconnect con1;
|
||||
--echo # connection default
|
||||
connection default;
|
||||
DROP TABLE t1, t2, t3, t4;
|
|
@ -221,6 +221,7 @@ create table t2 (a int not null);
|
|||
insert into t1 values (1);
|
||||
insert into t2 values (2);
|
||||
create temporary table t3 (a int not null) ENGINE=MERGE UNION=(t1,t2);
|
||||
--error ER_WRONG_MRG_TABLE
|
||||
select * from t3;
|
||||
create temporary table t4 (a int not null);
|
||||
create temporary table t5 (a int not null);
|
||||
|
@ -229,6 +230,58 @@ insert into t5 values (2);
|
|||
create temporary table t6 (a int not null) ENGINE=MERGE UNION=(t4,t5);
|
||||
select * from t6;
|
||||
drop table t6, t3, t1, t2, t4, t5;
|
||||
#
|
||||
# Bug#19627 - temporary merge table locking
|
||||
# MERGE table and its children must match in temporary type.
|
||||
# Forbid temporary merge on non-temporary children: shown above.
|
||||
# Forbid non-temporary merge on temporary children:
|
||||
create temporary table t1 (a int not null);
|
||||
create temporary table t2 (a int not null);
|
||||
insert into t1 values (1);
|
||||
insert into t2 values (2);
|
||||
create table t3 (a int not null) ENGINE=MERGE UNION=(t1,t2);
|
||||
--error ER_WRONG_MRG_TABLE
|
||||
select * from t3;
|
||||
drop table t3, t2, t1;
|
||||
# Forbid children mismatch in temporary:
|
||||
create table t1 (a int not null);
|
||||
create temporary table t2 (a int not null);
|
||||
insert into t1 values (1);
|
||||
insert into t2 values (2);
|
||||
create table t3 (a int not null) ENGINE=MERGE UNION=(t1,t2);
|
||||
--error ER_WRONG_MRG_TABLE
|
||||
select * from t3;
|
||||
drop table t3;
|
||||
create temporary table t3 (a int not null) ENGINE=MERGE UNION=(t1,t2);
|
||||
--error ER_WRONG_MRG_TABLE
|
||||
select * from t3;
|
||||
drop table t3, t2, t1;
|
||||
--echo # CREATE...SELECT is not implemented for MERGE tables.
|
||||
CREATE TEMPORARY TABLE t1 (c1 INT NOT NULL);
|
||||
CREATE TEMPORARY TABLE t2 (c1 INT NOT NULL);
|
||||
CREATE TABLE t3 (c1 INT NOT NULL);
|
||||
INSERT INTO t3 VALUES (3), (33);
|
||||
LOCK TABLES t3 READ;
|
||||
--error ER_WRONG_OBJECT
|
||||
CREATE TEMPORARY TABLE t4 (c1 INT NOT NULL) ENGINE=MERGE UNION=(t1,t2)
|
||||
INSERT_METHOD=LAST SELECT * FROM t3;
|
||||
--error ER_TABLE_NOT_LOCKED
|
||||
SELECT * FROM t4;
|
||||
UNLOCK TABLES;
|
||||
CREATE TEMPORARY TABLE t4 (c1 INT NOT NULL) ENGINE=MERGE UNION=(t1,t2)
|
||||
INSERT_METHOD=LAST;
|
||||
INSERT INTO t4 SELECT * FROM t3;
|
||||
--echo # Alter temporary MERGE table.
|
||||
ALTER TABLE t4 UNION=(t1);
|
||||
LOCK TABLES t4 WRITE;
|
||||
--echo # Alter temporary MERGE table under LOCk tables.
|
||||
ALTER TABLE t4 UNION=(t1,t2);
|
||||
UNLOCK TABLES;
|
||||
--echo # MERGE table and function.
|
||||
CREATE FUNCTION f1 () RETURNS INT RETURN (SELECT max(c1) FROM t3);
|
||||
SELECT * FROM t4 WHERE c1 < f1();
|
||||
DROP FUNCTION f1;
|
||||
DROP TABLE t4, t3, t2, t1;
|
||||
|
||||
#
|
||||
# testing merge::records_in_range and optimizer
|
||||
|
@ -284,11 +337,11 @@ create table t1 (a int);
|
|||
create table t2 (a int);
|
||||
insert into t1 values (0);
|
||||
insert into t2 values (1);
|
||||
--error 1093
|
||||
--error ER_WRONG_OBJECT
|
||||
create table t3 engine=merge union=(t1, t2) select * from t1;
|
||||
--error 1093
|
||||
--error ER_WRONG_OBJECT
|
||||
create table t3 engine=merge union=(t1, t2) select * from t2;
|
||||
--error 1093
|
||||
--error ER_WRONG_OBJECT
|
||||
create table t3 engine=merge union=(t1, t2) select (select max(a) from t2);
|
||||
drop table t1, t2;
|
||||
|
||||
|
@ -403,7 +456,7 @@ CREATE TABLE t2(a INT) ENGINE=MERGE UNION=(t1);
|
|||
SELECT * FROM t2;
|
||||
DROP TABLE t1, t2;
|
||||
CREATE TABLE t2(a INT) ENGINE=MERGE UNION=(t3);
|
||||
--error 1168
|
||||
--error ER_NO_SUCH_TABLE
|
||||
SELECT * FROM t2;
|
||||
DROP TABLE t2;
|
||||
|
||||
|
@ -495,11 +548,11 @@ drop table t1;
|
|||
# CREATE TABLE fails
|
||||
#
|
||||
CREATE TABLE tm1(a INT) ENGINE=MERGE UNION=(t1, t2);
|
||||
--error 1168
|
||||
--error ER_NO_SUCH_TABLE
|
||||
SELECT * FROM tm1;
|
||||
CHECK TABLE tm1;
|
||||
CREATE TABLE t1(a INT);
|
||||
--error 1168
|
||||
--error ER_NO_SUCH_TABLE
|
||||
SELECT * FROM tm1;
|
||||
CHECK TABLE tm1;
|
||||
CREATE TABLE t2(a BLOB);
|
||||
|
@ -526,3 +579,785 @@ CREATE TABLE IF NOT EXISTS t1 SELECT * FROM t2;
|
|||
DROP TABLE t1, t2;
|
||||
|
||||
--echo End of 5.0 tests
|
||||
|
||||
#
|
||||
# Bug #8306: TRUNCATE leads to index corruption
|
||||
#
|
||||
create table t1 (c1 int, index(c1));
|
||||
create table t2 (c1 int, index(c1)) engine=merge union=(t1);
|
||||
insert into t1 values (1);
|
||||
# Close all tables.
|
||||
flush tables;
|
||||
# Open t2 and (implicitly) t1.
|
||||
select * from t2;
|
||||
# Truncate after flush works (unless another threads reopens t2 in between).
|
||||
flush tables;
|
||||
truncate table t1;
|
||||
insert into t1 values (1);
|
||||
# Close all tables.
|
||||
flush tables;
|
||||
# Open t2 and (implicitly) t1.
|
||||
select * from t2;
|
||||
# Truncate t1, wich was not recognized as open without the bugfix.
|
||||
# After fix for Bug#8306 and before fix for Bug#26379,
|
||||
# it should fail with a table-in-use error message, otherwise succeed.
|
||||
truncate table t1;
|
||||
# The insert used to fail on the crashed table.
|
||||
insert into t1 values (1);
|
||||
drop table t1,t2;
|
||||
--echo #
|
||||
--echo # Extra tests for TRUNCATE.
|
||||
--echo #
|
||||
--echo # Truncate MERGE table.
|
||||
CREATE TABLE t1 (c1 INT, INDEX(c1));
|
||||
CREATE TABLE t2 (c1 INT, INDEX(c1));
|
||||
CREATE TABLE t3 (c1 INT, INDEX(c1)) ENGINE=MRG_MYISAM UNION=(t1,t2);
|
||||
INSERT INTO t1 VALUES (1);
|
||||
INSERT INTO t2 VALUES (2);
|
||||
SELECT * FROM t3;
|
||||
TRUNCATE TABLE t3;
|
||||
SELECT * FROM t3;
|
||||
--echo #
|
||||
--echo # Truncate child table.
|
||||
INSERT INTO t1 VALUES (1);
|
||||
INSERT INTO t2 VALUES (2);
|
||||
TRUNCATE TABLE t1;
|
||||
SELECT * FROM t3;
|
||||
--echo #
|
||||
--echo # Truncate MERGE table under locked tables.
|
||||
LOCK TABLE t1 WRITE, t2 WRITE, t3 WRITE;
|
||||
INSERT INTO t1 VALUES (1);
|
||||
--error ER_LOCK_OR_ACTIVE_TRANSACTION
|
||||
TRUNCATE TABLE t3;
|
||||
SELECT * FROM t3;
|
||||
--echo #
|
||||
--echo # Truncate child table under locked tables.
|
||||
--error ER_LOCK_OR_ACTIVE_TRANSACTION
|
||||
TRUNCATE TABLE t1;
|
||||
SELECT * FROM t3;
|
||||
UNLOCK TABLES;
|
||||
DROP TABLE t1, t2, t3;
|
||||
--echo #
|
||||
--echo # Truncate temporary MERGE table.
|
||||
CREATE TEMPORARY TABLE t1 (c1 INT, INDEX(c1));
|
||||
CREATE TEMPORARY TABLE t2 (c1 INT, INDEX(c1));
|
||||
CREATE TEMPORARY TABLE t3 (c1 INT, INDEX(c1)) ENGINE=MRG_MYISAM UNION=(t1,t2);
|
||||
INSERT INTO t1 VALUES (1);
|
||||
INSERT INTO t2 VALUES (2);
|
||||
SELECT * FROM t3;
|
||||
TRUNCATE TABLE t3;
|
||||
SELECT * FROM t3;
|
||||
--echo #
|
||||
--echo # Truncate temporary child table.
|
||||
INSERT INTO t1 VALUES (1);
|
||||
INSERT INTO t2 VALUES (2);
|
||||
TRUNCATE TABLE t1;
|
||||
SELECT * FROM t3;
|
||||
--echo #
|
||||
--echo # Truncate temporary MERGE table under locked tables.
|
||||
INSERT INTO t1 VALUES (1);
|
||||
CREATE TABLE t4 (c1 INT, INDEX(c1));
|
||||
LOCK TABLE t4 WRITE;
|
||||
--error ER_LOCK_OR_ACTIVE_TRANSACTION
|
||||
TRUNCATE TABLE t3;
|
||||
SELECT * FROM t3;
|
||||
--echo #
|
||||
--echo # Truncate temporary child table under locked tables.
|
||||
--error ER_LOCK_OR_ACTIVE_TRANSACTION
|
||||
TRUNCATE TABLE t1;
|
||||
SELECT * FROM t3;
|
||||
UNLOCK TABLES;
|
||||
DROP TABLE t1, t2, t3, t4;
|
||||
|
||||
#
|
||||
# Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts a MERGE table
|
||||
# Preparation
|
||||
connect (con1,localhost,root,,);
|
||||
connect (con2,localhost,root,,);
|
||||
connection default;
|
||||
#
|
||||
# Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts a MERGE table
|
||||
# Problem #1
|
||||
# A thread trying to lock a MERGE table performed busy waiting while
|
||||
# REPAIR TABLE or a similar table administration task was ongoing on one or
|
||||
# more of its MyISAM tables.
|
||||
# To allow for observability it was necessary to enter a multi-second sleep
|
||||
# in mysql_admin_table() after remove_table_from_cache(), which comes after
|
||||
# mysql_abort_lock(). The sleep faked a long running operation. One could
|
||||
# watch a high CPU load during the sleep time.
|
||||
# The problem was that mysql_abort_lock() upgrades the write lock to
|
||||
# TL_WRITE_ONLY. This lock type persisted until the final unlock at the end
|
||||
# of the administration task. The effect of TL_WRITE_ONLY is to reject any
|
||||
# attempt to lock the table. The trying thread must close the table and wait
|
||||
# until it is no longer used. Unfortunately there is no way to detect that
|
||||
# one of the MyISAM tables of a MERGE table is in use. When trying to lock
|
||||
# the MERGE table, all MyISAM tables are locked. If one fails on
|
||||
# TL_WRITE_ONLY, all locks are aborted and wait_for_tables() is entered.
|
||||
# But this doesn't see the MERGE table as used, so it seems appropriate to
|
||||
# retry a lock...
|
||||
#
|
||||
CREATE TABLE t1 (c1 INT) ENGINE= MyISAM;
|
||||
CREATE TABLE t2 (c1 INT) ENGINE= MRG_MYISAM UNION= (t1) INSERT_METHOD= LAST;
|
||||
send REPAIR TABLE t1;
|
||||
connection con1;
|
||||
sleep 1; # let repair run into its sleep
|
||||
INSERT INTO t2 VALUES (1);
|
||||
connection default;
|
||||
reap;
|
||||
DROP TABLE t1, t2;
|
||||
#
|
||||
# Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts a MERGE table
|
||||
# Problem #2
|
||||
# A thread trying to lock a MERGE table performed busy waiting until all
|
||||
# threads that did REPAIR TABLE or similar table administration tasks on
|
||||
# one or more of its MyISAM tables in LOCK TABLES segments did
|
||||
# UNLOCK TABLES.
|
||||
# The difference against problem #1 is that the busy waiting took place
|
||||
# *after* the administration task. It was terminated by UNLOCK TABLES only.
|
||||
#
|
||||
# This is the same test case as for
|
||||
# Bug#26867 - LOCK TABLES + REPAIR + merge table result in memory/cpu hogging
|
||||
#
|
||||
#
|
||||
CREATE TABLE t1 (c1 INT) ENGINE= MyISAM;
|
||||
CREATE TABLE t2 (c1 INT) ENGINE= MRG_MYISAM UNION= (t1) INSERT_METHOD= LAST;
|
||||
LOCK TABLE t1 WRITE;
|
||||
connection con1;
|
||||
send INSERT INTO t2 VALUES (1);
|
||||
connection default;
|
||||
sleep 1; # Let INSERT go into thr_multi_lock().
|
||||
REPAIR TABLE t1;
|
||||
sleep 2; # con1 performs busy waiting during this sleep.
|
||||
UNLOCK TABLES;
|
||||
connection con1;
|
||||
reap;
|
||||
connection default;
|
||||
DROP TABLE t1, t2;
|
||||
#
|
||||
# Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts a MERGE table
|
||||
# Problem #3
|
||||
# Two FLUSH TABLES within a LOCK TABLES segment could invalidate the lock.
|
||||
# This did *not* require a MERGE table.
|
||||
# To increase reproducibility it was necessary to enter a sleep of 2 seconds
|
||||
# at the end of wait_for_tables() after unlock of LOCK_open. In 5.0 and 5.1
|
||||
# the sleep must be inserted in open_and_lock_tables() after open_tables()
|
||||
# instead. wait_for_tables() is not used in this case.
|
||||
# The problem was that FLUSH TABLES releases LOCK_open while having unlocked
|
||||
# and closed all tables. When this happened while a thread was in the loop in
|
||||
# mysql_lock_tables() right after wait_for_tables() and before retrying to
|
||||
# lock, the thread got the lock. (Translate to similar code places in 5.0
|
||||
# and 5.1). And it did not notice that the table needed a refresh. So it
|
||||
# executed its statement on the table.
|
||||
# The first FLUSH TABLES kicked the INSERT out of thr_multi_lock() and let
|
||||
# it wait in wait_for_tables(). (open_table() in 5.0 and 5.1). The second
|
||||
# FLUSH TABLES must happen while the INSERT was on its way from
|
||||
# wait_for_tables() to the next call of thr_multi_lock(). This needed to be
|
||||
# supported by a sleep to make it repeatable.
|
||||
#
|
||||
CREATE TABLE t1 (c1 INT) ENGINE= MyISAM;
|
||||
LOCK TABLE t1 WRITE;
|
||||
connection con1;
|
||||
send INSERT INTO t1 VALUES (1);
|
||||
connection default;
|
||||
sleep 1; # Let INSERT go into thr_multi_lock().
|
||||
FLUSH TABLES;
|
||||
sleep 1; # Let INSERT go through wait_for_tables() where it sleeps.
|
||||
FLUSH TABLES;
|
||||
# This should give no result. But it will with sleep(2) at the right place.
|
||||
SELECT * FROM t1;
|
||||
UNLOCK TABLES;
|
||||
connection con1;
|
||||
reap;
|
||||
connection default;
|
||||
DROP TABLE t1;
|
||||
#
|
||||
# Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts a MERGE table
|
||||
# Cleanup
|
||||
disconnect con1;
|
||||
disconnect con2;
|
||||
#
|
||||
--echo #
|
||||
--echo # Extra tests for Bug#26379 - Combination of FLUSH TABLE and
|
||||
--echo # REPAIR TABLE corrupts a MERGE table
|
||||
#
|
||||
--echo #
|
||||
--echo # CREATE ... SELECT is disabled for MERGE tables.
|
||||
--echo #
|
||||
CREATE TABLE t1(c1 INT);
|
||||
INSERT INTO t1 VALUES (1);
|
||||
CREATE TABLE t2 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1) INSERT_METHOD=LAST;
|
||||
--error ER_OPEN_AS_READONLY
|
||||
CREATE TABLE t3 ENGINE=MRG_MYISAM INSERT_METHOD=LAST SELECT * FROM t2;
|
||||
--error ER_NO_SUCH_TABLE
|
||||
SHOW CREATE TABLE t3;
|
||||
--error ER_WRONG_OBJECT
|
||||
CREATE TABLE t3 ENGINE=MRG_MYISAM UNION=(t1) INSERT_METHOD=LAST
|
||||
SELECT * FROM t2;
|
||||
--error ER_NO_SUCH_TABLE
|
||||
SHOW CREATE TABLE t3;
|
||||
DROP TABLE t1, t2;
|
||||
#
|
||||
--echo #
|
||||
--echo # CREATE ... LIKE
|
||||
--echo #
|
||||
--echo # 1. Create like.
|
||||
CREATE TABLE t1 (c1 INT);
|
||||
CREATE TABLE t2 (c1 INT);
|
||||
CREATE TABLE t3 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1,t2)
|
||||
INSERT_METHOD=LAST;
|
||||
INSERT INTO t1 VALUES (1);
|
||||
INSERT INTO t2 VALUES (2);
|
||||
INSERT INTO t3 VALUES (3);
|
||||
CREATE TABLE t4 LIKE t3;
|
||||
SHOW CREATE TABLE t4;
|
||||
--error ER_OPEN_AS_READONLY
|
||||
INSERT INTO t4 VALUES (4);
|
||||
DROP TABLE t4;
|
||||
--echo #
|
||||
--echo # 1. Create like with locked tables.
|
||||
LOCK TABLES t3 WRITE, t2 WRITE, t1 WRITE;
|
||||
CREATE TABLE t4 LIKE t3;
|
||||
--error ER_TABLE_NOT_LOCKED
|
||||
SHOW CREATE TABLE t4;
|
||||
--error ER_TABLE_NOT_LOCKED
|
||||
INSERT INTO t4 VALUES (4);
|
||||
UNLOCK TABLES;
|
||||
SHOW CREATE TABLE t4;
|
||||
--error ER_OPEN_AS_READONLY
|
||||
INSERT INTO t4 VALUES (4);
|
||||
DROP TABLE t4;
|
||||
#
|
||||
--echo #
|
||||
--echo # Rename child.
|
||||
--echo #
|
||||
--echo # 1. Normal rename of non-MERGE table.
|
||||
CREATE TABLE t4 (c1 INT);
|
||||
INSERT INTO t4 VALUES (4);
|
||||
SELECT * FROM t4 ORDER BY c1;
|
||||
RENAME TABLE t4 TO t5;
|
||||
SELECT * FROM t5 ORDER BY c1;
|
||||
RENAME TABLE t5 TO t4;
|
||||
SELECT * FROM t4 ORDER BY c1;
|
||||
DROP TABLE t4;
|
||||
--echo #
|
||||
--echo # 2. Normal rename.
|
||||
SELECT * FROM t3 ORDER BY c1;
|
||||
RENAME TABLE t2 TO t5;
|
||||
--error ER_NO_SUCH_TABLE
|
||||
SELECT * FROM t3 ORDER BY c1;
|
||||
RENAME TABLE t5 TO t2;
|
||||
SELECT * FROM t3 ORDER BY c1;
|
||||
--echo #
|
||||
--echo # 3. Normal rename with locked tables.
|
||||
LOCK TABLES t1 WRITE, t2 WRITE, t3 WRITE;
|
||||
SELECT * FROM t3 ORDER BY c1;
|
||||
--error ER_LOCK_OR_ACTIVE_TRANSACTION
|
||||
RENAME TABLE t2 TO t5;
|
||||
SELECT * FROM t3 ORDER BY c1;
|
||||
--error ER_LOCK_OR_ACTIVE_TRANSACTION
|
||||
RENAME TABLE t5 TO t2;
|
||||
SELECT * FROM t3 ORDER BY c1;
|
||||
UNLOCK TABLES;
|
||||
--echo #
|
||||
--echo # 4. Alter table rename.
|
||||
ALTER TABLE t2 RENAME TO t5;
|
||||
--error ER_NO_SUCH_TABLE
|
||||
SELECT * FROM t3 ORDER BY c1;
|
||||
ALTER TABLE t5 RENAME TO t2;
|
||||
SELECT * FROM t3 ORDER BY c1;
|
||||
--echo #
|
||||
--echo # 5. Alter table rename with locked tables.
|
||||
LOCK TABLES t1 WRITE, t2 WRITE, t3 WRITE;
|
||||
ALTER TABLE t2 RENAME TO t5;
|
||||
--error ER_TABLE_NOT_LOCKED
|
||||
SELECT * FROM t3 ORDER BY c1;
|
||||
--error ER_TABLE_NOT_LOCKED
|
||||
ALTER TABLE t5 RENAME TO t2;
|
||||
UNLOCK TABLES;
|
||||
ALTER TABLE t5 RENAME TO t2;
|
||||
SELECT * FROM t3 ORDER BY c1;
|
||||
#
|
||||
--echo #
|
||||
--echo # Rename parent.
|
||||
--echo #
|
||||
--echo # 1. Normal rename with locked tables.
|
||||
LOCK TABLES t1 WRITE, t2 WRITE, t3 WRITE;
|
||||
SELECT * FROM t3 ORDER BY c1;
|
||||
--error ER_LOCK_OR_ACTIVE_TRANSACTION
|
||||
RENAME TABLE t3 TO t5;
|
||||
SELECT * FROM t3 ORDER BY c1;
|
||||
--error ER_LOCK_OR_ACTIVE_TRANSACTION
|
||||
RENAME TABLE t5 TO t3;
|
||||
SELECT * FROM t3 ORDER BY c1;
|
||||
--echo #
|
||||
--echo # 5. Alter table rename with locked tables.
|
||||
ALTER TABLE t3 RENAME TO t5;
|
||||
--error ER_TABLE_NOT_LOCKED
|
||||
SELECT * FROM t5 ORDER BY c1;
|
||||
--error ER_TABLE_NOT_LOCKED
|
||||
ALTER TABLE t5 RENAME TO t3;
|
||||
UNLOCK TABLES;
|
||||
ALTER TABLE t5 RENAME TO t3;
|
||||
SELECT * FROM t3 ORDER BY c1;
|
||||
DROP TABLE t1, t2, t3;
|
||||
#
|
||||
--echo #
|
||||
--echo # Drop locked tables.
|
||||
--echo #
|
||||
--echo # 1. Drop parent.
|
||||
CREATE TABLE t1 (c1 INT, INDEX(c1));
|
||||
CREATE TABLE t2 (c1 INT, INDEX(c1)) ENGINE=MRG_MYISAM UNION=(t1)
|
||||
INSERT_METHOD=LAST;
|
||||
LOCK TABLES t1 WRITE, t2 WRITE;
|
||||
INSERT INTO t1 VALUES (1);
|
||||
DROP TABLE t2;
|
||||
--error ER_TABLE_NOT_LOCKED
|
||||
SELECT * FROM t2;
|
||||
SELECT * FROM t1;
|
||||
UNLOCK TABLES;
|
||||
--echo # 2. Drop child.
|
||||
CREATE TABLE t2 (c1 INT, INDEX(c1)) ENGINE=MRG_MYISAM UNION=(t1)
|
||||
INSERT_METHOD=LAST;
|
||||
LOCK TABLES t1 WRITE, t2 WRITE;
|
||||
INSERT INTO t1 VALUES (1);
|
||||
DROP TABLE t1;
|
||||
--error ER_NO_SUCH_TABLE
|
||||
SELECT * FROM t2;
|
||||
--error ER_NO_SUCH_TABLE
|
||||
SELECT * FROM t1;
|
||||
UNLOCK TABLES;
|
||||
DROP TABLE t2;
|
||||
#
|
||||
--echo #
|
||||
--echo # ALTER TABLE. Change child list.
|
||||
--echo #
|
||||
CREATE TABLE t1 (c1 INT, INDEX(c1));
|
||||
CREATE TABLE t2 (c1 INT, INDEX(c1));
|
||||
CREATE TABLE t3 (c1 INT, INDEX(c1));
|
||||
INSERT INTO t1 VALUES (1);
|
||||
INSERT INTO t2 VALUES (2);
|
||||
INSERT INTO t3 VALUES (3);
|
||||
CREATE TABLE t4 (c1 INT, INDEX(c1)) ENGINE=MRG_MYISAM UNION=(t3,t2)
|
||||
INSERT_METHOD=LAST;
|
||||
--echo # Shrink child list.
|
||||
ALTER TABLE t4 UNION=(t3);
|
||||
SHOW CREATE TABLE t4;
|
||||
SELECT * FROM t4 ORDER BY c1;
|
||||
--echo # Extend child list.
|
||||
ALTER TABLE t4 UNION=(t3,t2);
|
||||
SHOW CREATE TABLE t4;
|
||||
SELECT * FROM t4 ORDER BY c1;
|
||||
#
|
||||
--echo #
|
||||
--echo # ALTER TABLE under LOCK TABLES. Change child list.
|
||||
--echo #
|
||||
LOCK TABLES t4 WRITE, t3 WRITE, t2 WRITE;
|
||||
--echo # Shrink child list.
|
||||
--error ER_LOCK_OR_ACTIVE_TRANSACTION
|
||||
ALTER TABLE t4 UNION=(t3);
|
||||
--echo # Extend child list within locked tables.
|
||||
--error ER_LOCK_OR_ACTIVE_TRANSACTION
|
||||
ALTER TABLE t4 UNION=(t3,t2);
|
||||
--echo # Extend child list beyond locked tables.
|
||||
--error ER_LOCK_OR_ACTIVE_TRANSACTION
|
||||
ALTER TABLE t4 UNION=(t3,t2,t1);
|
||||
SHOW CREATE TABLE t4;
|
||||
SELECT * FROM t4 ORDER BY c1;
|
||||
UNLOCK TABLES;
|
||||
DROP TABLE t4;
|
||||
#
|
||||
--echo #
|
||||
--echo # ALTER TABLE under LOCK TABLES. Grave change, table re-creation.
|
||||
--echo #
|
||||
CREATE TABLE t4 (c1 INT, INDEX(c1)) ENGINE=MRG_MYISAM UNION=(t1,t2,t3)
|
||||
INSERT_METHOD=LAST;
|
||||
--echo # Lock parent first and then children.
|
||||
LOCK TABLES t4 WRITE, t3 WRITE, t2 WRITE, t1 WRITE;
|
||||
ALTER TABLE t4 DROP INDEX c1, ADD UNIQUE INDEX (c1);
|
||||
SELECT * FROM t4 ORDER BY c1;
|
||||
ALTER TABLE t2 DROP INDEX c1, ADD UNIQUE INDEX (c1);
|
||||
SELECT * FROM t4 ORDER BY c1;
|
||||
UNLOCK TABLES;
|
||||
--echo # Lock children first and then parent.
|
||||
LOCK TABLES t1 WRITE, t2 WRITE, t3 WRITE, t4 WRITE;
|
||||
ALTER TABLE t4 DROP INDEX c1, ADD UNIQUE INDEX (c1);
|
||||
SELECT * FROM t4 ORDER BY c1;
|
||||
ALTER TABLE t2 DROP INDEX c1, ADD UNIQUE INDEX (c1);
|
||||
SELECT * FROM t4 ORDER BY c1;
|
||||
UNLOCK TABLES;
|
||||
--echo # Lock parent between children.
|
||||
LOCK TABLES t3 WRITE, t2 WRITE, t4 WRITE, t1 WRITE;
|
||||
ALTER TABLE t4 DROP INDEX c1, ADD UNIQUE INDEX (c1);
|
||||
SELECT * FROM t4 ORDER BY c1;
|
||||
ALTER TABLE t2 DROP INDEX c1, ADD UNIQUE INDEX (c1);
|
||||
SELECT * FROM t4 ORDER BY c1;
|
||||
UNLOCK TABLES;
|
||||
DROP TABLE t1, t2, t3, t4;
|
||||
#
|
||||
--echo #
|
||||
--echo # ALTER TABLE under LOCK TABLES. Simple change, no re-creation.
|
||||
--echo #
|
||||
CREATE TABLE t1 (c1 INT);
|
||||
CREATE TABLE t2 (c1 INT);
|
||||
CREATE TABLE t3 (c1 INT);
|
||||
CREATE TABLE t4 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1,t2,t3)
|
||||
INSERT_METHOD=LAST;
|
||||
INSERT INTO t1 VALUES (1);
|
||||
INSERT INTO t2 VALUES (2);
|
||||
INSERT INTO t3 VALUES (3);
|
||||
--echo # Lock parent first and then children.
|
||||
LOCK TABLES t4 WRITE, t3 WRITE, t2 WRITE, t1 WRITE;
|
||||
ALTER TABLE t4 ALTER COLUMN c1 SET DEFAULT 44;
|
||||
SELECT * FROM t4 ORDER BY c1;
|
||||
ALTER TABLE t2 ALTER COLUMN c1 SET DEFAULT 22;
|
||||
SELECT * FROM t4 ORDER BY c1;
|
||||
UNLOCK TABLES;
|
||||
--echo # Lock children first and then parent.
|
||||
LOCK TABLES t1 WRITE, t2 WRITE, t3 WRITE, t4 WRITE;
|
||||
ALTER TABLE t4 ALTER COLUMN c1 SET DEFAULT 44;
|
||||
SELECT * FROM t4 ORDER BY c1;
|
||||
ALTER TABLE t2 ALTER COLUMN c1 SET DEFAULT 22;
|
||||
SELECT * FROM t4 ORDER BY c1;
|
||||
UNLOCK TABLES;
|
||||
--echo # Lock parent between children.
|
||||
LOCK TABLES t3 WRITE, t2 WRITE, t4 WRITE, t1 WRITE;
|
||||
ALTER TABLE t4 ALTER COLUMN c1 SET DEFAULT 44;
|
||||
SELECT * FROM t4 ORDER BY c1;
|
||||
ALTER TABLE t2 ALTER COLUMN c1 SET DEFAULT 22;
|
||||
SELECT * FROM t4 ORDER BY c1;
|
||||
UNLOCK TABLES;
|
||||
#
|
||||
--echo #
|
||||
--echo # FLUSH TABLE under LOCK TABLES.
|
||||
--echo #
|
||||
--echo # Lock parent first and then children.
|
||||
LOCK TABLES t4 WRITE, t3 WRITE, t2 WRITE, t1 WRITE;
|
||||
FLUSH TABLE t4;
|
||||
SELECT * FROM t4 ORDER BY c1;
|
||||
FLUSH TABLE t2;
|
||||
SELECT * FROM t4 ORDER BY c1;
|
||||
FLUSH TABLES;
|
||||
SELECT * FROM t4 ORDER BY c1;
|
||||
UNLOCK TABLES;
|
||||
--echo # Lock children first and then parent.
|
||||
LOCK TABLES t1 WRITE, t2 WRITE, t3 WRITE, t4 WRITE;
|
||||
FLUSH TABLE t4;
|
||||
SELECT * FROM t4 ORDER BY c1;
|
||||
FLUSH TABLE t2;
|
||||
SELECT * FROM t4 ORDER BY c1;
|
||||
FLUSH TABLES;
|
||||
SELECT * FROM t4 ORDER BY c1;
|
||||
UNLOCK TABLES;
|
||||
--echo # Lock parent between children.
|
||||
LOCK TABLES t3 WRITE, t2 WRITE, t4 WRITE, t1 WRITE;
|
||||
FLUSH TABLE t4;
|
||||
SELECT * FROM t4 ORDER BY c1;
|
||||
FLUSH TABLE t2;
|
||||
SELECT * FROM t4 ORDER BY c1;
|
||||
FLUSH TABLES;
|
||||
SELECT * FROM t4 ORDER BY c1;
|
||||
UNLOCK TABLES;
|
||||
#
|
||||
--echo #
|
||||
--echo # Triggers
|
||||
--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;
|
||||
SET @a=0;
|
||||
INSERT INTO t4 VALUES (4);
|
||||
SELECT @a;
|
||||
SELECT * FROM t4 ORDER BY c1;
|
||||
DROP TRIGGER t4_ai;
|
||||
--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;
|
||||
SET @a=0;
|
||||
INSERT INTO t4 VALUES (4);
|
||||
SELECT @a;
|
||||
SELECT * FROM t4 ORDER BY c1;
|
||||
DROP TRIGGER t4_ai;
|
||||
UNLOCK TABLES;
|
||||
--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;
|
||||
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;
|
||||
--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;
|
||||
SET @a=0;
|
||||
INSERT INTO t4 VALUES (4);
|
||||
SELECT @a;
|
||||
INSERT INTO t3 VALUES (33);
|
||||
SELECT @a;
|
||||
SELECT * FROM t4 ORDER BY c1;
|
||||
DELETE FROM t4 WHERE c1 = 33;
|
||||
DROP TRIGGER t3_ai;
|
||||
--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);
|
||||
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;
|
||||
--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);
|
||||
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;
|
||||
DELETE FROM t4 WHERE c1 = 22;
|
||||
DELETE FROM t4 WHERE c1 = 33;
|
||||
UNLOCK TABLES;
|
||||
#
|
||||
--echo #
|
||||
--echo # Repair
|
||||
--echo #
|
||||
REPAIR TABLE t4;
|
||||
REPAIR TABLE t2;
|
||||
SELECT * FROM t4 ORDER BY c1;
|
||||
LOCK TABLES t3 WRITE, t2 WRITE, t4 WRITE, t1 WRITE;
|
||||
REPAIR TABLE t4;
|
||||
REPAIR TABLE t2;
|
||||
SELECT * FROM t4 ORDER BY c1;
|
||||
UNLOCK TABLES;
|
||||
#
|
||||
--echo #
|
||||
--echo # Optimize
|
||||
--echo #
|
||||
OPTIMIZE TABLE t4;
|
||||
OPTIMIZE TABLE t2;
|
||||
SELECT * FROM t4 ORDER BY c1;
|
||||
LOCK TABLES t3 WRITE, t2 WRITE, t4 WRITE, t1 WRITE;
|
||||
OPTIMIZE TABLE t4;
|
||||
OPTIMIZE TABLE t2;
|
||||
SELECT * FROM t4 ORDER BY c1;
|
||||
UNLOCK TABLES;
|
||||
#
|
||||
--echo #
|
||||
--echo # Checksum
|
||||
--echo #
|
||||
CHECKSUM TABLE t4;
|
||||
CHECKSUM TABLE t2;
|
||||
SELECT * FROM t4 ORDER BY c1;
|
||||
LOCK TABLES t3 WRITE, t2 WRITE, t4 WRITE, t1 WRITE;
|
||||
CHECKSUM TABLE t4;
|
||||
CHECKSUM TABLE t2;
|
||||
SELECT * FROM t4 ORDER BY c1;
|
||||
UNLOCK TABLES;
|
||||
#
|
||||
--echo #
|
||||
--echo # Insert delayed
|
||||
--echo #
|
||||
# See also Bug#26464 - insert delayed + update + merge = corruption
|
||||
# Succeeds in embedded server - is converted to normal insert
|
||||
# Fails in normal server, ps-protocol - not supported by engine
|
||||
# Fails in normal server, normal protocol - not a base table
|
||||
--error 0, ER_ILLEGAL_HA, ER_WRONG_OBJECT
|
||||
INSERT DELAYED INTO t4 VALUES(44);
|
||||
# Get rid of row in embedded server
|
||||
DELETE FROM t4 WHERE c1 = 44;
|
||||
INSERT DELAYED INTO t3 VALUES(33);
|
||||
let $wait_cmd= SHOW STATUS LIKE 'Not_flushed_delayed_rows';
|
||||
let $run= query_get_value($wait_cmd, Value, 1);
|
||||
while ($run)
|
||||
{
|
||||
let $run= query_get_value($wait_cmd, Value, 1);
|
||||
}
|
||||
SELECT * FROM t4 ORDER BY c1;
|
||||
LOCK TABLES t3 WRITE, t2 WRITE, t4 WRITE, t1 WRITE;
|
||||
--error ER_DELAYED_INSERT_TABLE_LOCKED, ER_ILLEGAL_HA
|
||||
INSERT DELAYED INTO t4 VALUES(444);
|
||||
--error ER_DELAYED_INSERT_TABLE_LOCKED, ER_ILLEGAL_HA
|
||||
INSERT DELAYED INTO t3 VALUES(333);
|
||||
SELECT * FROM t4 ORDER BY c1;
|
||||
UNLOCK TABLES;
|
||||
DROP TABLE t1, t2, t3, t4;
|
||||
#
|
||||
--echo #
|
||||
--echo # Recursive inclusion of merge tables in their union clauses.
|
||||
--echo #
|
||||
CREATE TABLE t1 (c1 INT, INDEX(c1));
|
||||
CREATE TABLE t2 (c1 INT, INDEX(c1)) ENGINE=MRG_MYISAM UNION=(t1)
|
||||
INSERT_METHOD=LAST;
|
||||
CREATE TABLE t3 (c1 INT, INDEX(c1)) ENGINE=MRG_MYISAM UNION=(t2,t1)
|
||||
INSERT_METHOD=LAST;
|
||||
ALTER TABLE t2 UNION=(t3,t1);
|
||||
--error ER_ADMIN_WRONG_MRG_TABLE
|
||||
SELECT * FROM t2;
|
||||
DROP TABLE t1, t2, t3;
|
||||
|
||||
|
||||
#
|
||||
# Bug#25038 - Waiting TRUNCATE
|
||||
#
|
||||
# Show that truncate of child table after use of parent table works.
|
||||
CREATE TABLE t1 (c1 INT) ENGINE= MyISAM;
|
||||
CREATE TABLE t2 (c1 INT) ENGINE= MyISAM;
|
||||
CREATE TABLE t3 (c1 INT) ENGINE= MRG_MYISAM UNION= (t1, t2);
|
||||
INSERT INTO t1 VALUES (1);
|
||||
INSERT INTO t2 VALUES (2);
|
||||
SELECT * FROM t3;
|
||||
TRUNCATE TABLE t1;
|
||||
SELECT * FROM t3;
|
||||
DROP TABLE t1, t2, t3;
|
||||
#
|
||||
# Show that truncate of child table waits while parent table is used.
|
||||
# (test partly borrowed from count_distinct3.)
|
||||
CREATE TABLE t1 (id INTEGER, grp TINYINT, id_rev INTEGER);
|
||||
SET @rnd_max= 2147483647;
|
||||
let $1 = 10;
|
||||
while ($1)
|
||||
{
|
||||
SET @rnd= RAND();
|
||||
SET @id = CAST(@rnd * @rnd_max AS UNSIGNED);
|
||||
SET @id_rev= @rnd_max - @id;
|
||||
SET @grp= CAST(127.0 * @rnd AS UNSIGNED);
|
||||
INSERT INTO t1 (id, grp, id_rev) VALUES (@id, @grp, @id_rev);
|
||||
dec $1;
|
||||
}
|
||||
set @@read_buffer_size=2*1024*1024;
|
||||
CREATE TABLE t2 SELECT * FROM t1;
|
||||
INSERT INTO t1 (id, grp, id_rev) SELECT id, grp, id_rev FROM t2;
|
||||
INSERT INTO t2 (id, grp, id_rev) SELECT id, grp, id_rev FROM t1;
|
||||
INSERT INTO t1 (id, grp, id_rev) SELECT id, grp, id_rev FROM t2;
|
||||
INSERT INTO t2 (id, grp, id_rev) SELECT id, grp, id_rev FROM t1;
|
||||
INSERT INTO t1 (id, grp, id_rev) SELECT id, grp, id_rev FROM t2;
|
||||
CREATE TABLE t3 (id INTEGER, grp TINYINT, id_rev INTEGER)
|
||||
ENGINE= MRG_MYISAM UNION= (t1, t2);
|
||||
SELECT COUNT(*) FROM t1;
|
||||
SELECT COUNT(*) FROM t2;
|
||||
SELECT COUNT(*) FROM t3;
|
||||
connect (con1,localhost,root,,);
|
||||
# As t3 contains random numbers, results are different from test to test.
|
||||
# That's okay, because we test only that select doesn't yield an
|
||||
# error. Note, that --disable_result_log doesn't suppress error output.
|
||||
--disable_result_log
|
||||
send SELECT COUNT(DISTINCT a1.id) FROM t3 AS a1, t3 AS a2
|
||||
WHERE a1.id = a2.id GROUP BY a2.grp;
|
||||
connection default;
|
||||
sleep 1;
|
||||
TRUNCATE TABLE t1;
|
||||
connection con1;
|
||||
reap;
|
||||
--enable_result_log
|
||||
disconnect con1;
|
||||
connection default;
|
||||
SELECT COUNT(*) FROM t1;
|
||||
SELECT COUNT(*) FROM t2;
|
||||
SELECT COUNT(*) FROM t3;
|
||||
DROP TABLE t1, t2, t3;
|
||||
|
||||
#
|
||||
# Bug#25700 - merge base tables get corrupted by optimize/analyze/repair table
|
||||
#
|
||||
# Using FLUSH TABLES before REPAIR.
|
||||
CREATE TABLE t1 (c1 INT) ENGINE=MyISAM;
|
||||
CREATE TABLE t2 (c1 INT) ENGINE=MRG_MYISAM UNION=(t1) INSERT_METHOD=LAST;
|
||||
INSERT INTO t2 VALUES (1);
|
||||
SELECT * FROM t2;
|
||||
LOCK TABLES t2 WRITE, t1 WRITE;
|
||||
FLUSH TABLES;
|
||||
REPAIR TABLE t1;
|
||||
CHECK TABLE t1;
|
||||
REPAIR TABLE t1;
|
||||
UNLOCK TABLES;
|
||||
CHECK TABLE t1 EXTENDED;
|
||||
#
|
||||
# Not using FLUSH TABLES before REPAIR.
|
||||
LOCK TABLES t2 WRITE, t1 WRITE;
|
||||
SELECT * FROM t2;
|
||||
LOCK TABLES t2 WRITE, t1 WRITE;
|
||||
REPAIR TABLE t1;
|
||||
CHECK TABLE t1;
|
||||
REPAIR TABLE t1;
|
||||
UNLOCK TABLES;
|
||||
CHECK TABLE t1 EXTENDED;
|
||||
DROP TABLE t1, t2;
|
||||
|
||||
#
|
||||
# Bug#26377 - Deadlock with MERGE and FLUSH TABLE
|
||||
#
|
||||
CREATE TABLE t1 ( a INT ) ENGINE=MyISAM;
|
||||
CREATE TABLE m1 ( a INT ) ENGINE=MRG_MYISAM UNION=(t1);
|
||||
# Lock t1 first. This did always work.
|
||||
LOCK TABLES t1 WRITE, m1 WRITE;
|
||||
FLUSH TABLE t1;
|
||||
UNLOCK TABLES;
|
||||
DROP TABLE m1, t1;
|
||||
#
|
||||
CREATE TABLE t1 ( a INT ) ENGINE=MyISAM;
|
||||
CREATE TABLE m1 ( a INT ) ENGINE=MRG_MYISAM UNION=(t1);
|
||||
# Lock m1 first. This did deadlock.
|
||||
LOCK TABLES m1 WRITE, t1 WRITE;
|
||||
FLUSH TABLE t1;
|
||||
UNLOCK TABLES;
|
||||
DROP TABLE m1, t1;
|
||||
|
||||
#
|
||||
# Bug#27660 - Falcon: merge table possible
|
||||
#
|
||||
# Normal MyISAM MERGE operation.
|
||||
CREATE TABLE t1 (c1 INT, c2 INT) ENGINE= MyISAM;
|
||||
CREATE TABLE t2 (c1 INT, c2 INT) ENGINE= MyISAM;
|
||||
CREATE TABLE t3 (c1 INT, c2 INT) ENGINE= MRG_MYISAM UNION(t1, t2);
|
||||
INSERT INTO t1 VALUES (1, 1);
|
||||
INSERT INTO t2 VALUES (2, 2);
|
||||
SELECT * FROM t3;
|
||||
# Try an unsupported engine.
|
||||
ALTER TABLE t1 ENGINE= MEMORY;
|
||||
INSERT INTO t1 VALUES (0, 0);
|
||||
# Before fixing, this succeeded, but (0, 0) was missing.
|
||||
--error 1168
|
||||
SELECT * FROM t3;
|
||||
DROP TABLE t1, t2, t3;
|
||||
|
||||
#
|
||||
# Bug#30275 - Merge tables: flush tables or unlock tables causes server to crash
|
||||
#
|
||||
CREATE TABLE t1 (c1 INT, KEY(c1));
|
||||
CREATE TABLE t2 (c1 INT, KEY(c1)) ENGINE=MRG_MYISAM UNION=(t1)
|
||||
INSERT_METHOD=FIRST;
|
||||
LOCK TABLE t1 WRITE, t2 WRITE;
|
||||
FLUSH TABLES t2, t1;
|
||||
OPTIMIZE TABLE t1;
|
||||
FLUSH TABLES t1;
|
||||
UNLOCK TABLES;
|
||||
#
|
||||
FLUSH TABLES;
|
||||
INSERT INTO t1 VALUES (1);
|
||||
LOCK TABLE t1 WRITE, t2 WRITE;
|
||||
FLUSH TABLES t2, t1;
|
||||
OPTIMIZE TABLE t1;
|
||||
FLUSH TABLES t1;
|
||||
UNLOCK TABLES;
|
||||
DROP TABLE t1, t2;
|
||||
|
||||
#
|
||||
# Test derived from test program for
|
||||
# Bug#30273 - merge tables: Can't lock file (errno: 155)
|
||||
#
|
||||
CREATE TABLE t1 (ID INT) ENGINE=MYISAM;
|
||||
CREATE TABLE m1 (ID INT) ENGINE=MRG_MYISAM UNION=(t1) INSERT_METHOD=FIRST;
|
||||
INSERT INTO t1 VALUES ();
|
||||
INSERT INTO m1 VALUES ();
|
||||
LOCK TABLE t1 WRITE, m1 WRITE;
|
||||
FLUSH TABLES m1, t1;
|
||||
OPTIMIZE TABLE t1;
|
||||
FLUSH TABLES m1, t1;
|
||||
UNLOCK TABLES;
|
||||
DROP TABLE t1, m1;
|
||||
|
||||
|
|
|
@ -575,32 +575,6 @@ explain select count(*) from t1 where a is null;
|
|||
select count(*) from t1 where a is null;
|
||||
drop table t1;
|
||||
|
||||
#
|
||||
# Bug #8306: TRUNCATE leads to index corruption
|
||||
#
|
||||
create table t1 (c1 int, index(c1));
|
||||
create table t2 (c1 int, index(c1)) engine=merge union=(t1);
|
||||
insert into t1 values (1);
|
||||
# Close all tables.
|
||||
flush tables;
|
||||
# Open t2 and (implicitly) t1.
|
||||
select * from t2;
|
||||
# Truncate after flush works (unless another threads reopens t2 in between).
|
||||
flush tables;
|
||||
truncate table t1;
|
||||
insert into t1 values (1);
|
||||
# Close all tables.
|
||||
flush tables;
|
||||
# Open t2 and (implicitly) t1.
|
||||
select * from t2;
|
||||
# Truncate t1, wich was not recognized as open without the bugfix.
|
||||
# Now, it should fail with a table-in-use error message.
|
||||
--error 1105
|
||||
truncate table t1;
|
||||
# The insert used to fail on the crashed table.
|
||||
insert into t1 values (1);
|
||||
drop table t1,t2;
|
||||
|
||||
#
|
||||
# bug9188 - Corruption Can't open file: 'table.MYI' (errno: 145)
|
||||
#
|
||||
|
|
|
@ -372,6 +372,16 @@ select * from t1 where a = 4;
|
|||
|
||||
drop table t1;
|
||||
|
||||
#
|
||||
# Bug#22351 - handler::index_next_same() call to key_cmp_if_same()
|
||||
# uses the wrong buffer
|
||||
#
|
||||
CREATE TABLE t1 (c1 INT, c2 INT, PRIMARY KEY USING BTREE (c1,c2)) ENGINE=MEMORY
|
||||
PARTITION BY KEY(c2,c1) PARTITIONS 4;
|
||||
INSERT INTO t1 VALUES (0,0),(1,1),(2,2),(3,3),(4,4),(5,5),(6,6);
|
||||
SELECT * FROM t1 WHERE c1 = 4;
|
||||
DROP TABLE t1;
|
||||
|
||||
#
|
||||
# Bug #13438: Engine clause in PARTITION clause causes crash
|
||||
#
|
||||
|
|
|
@ -396,6 +396,7 @@ wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data,
|
|||
struct timespec wait_timeout;
|
||||
enum enum_thr_lock_result result= THR_LOCK_ABORTED;
|
||||
my_bool can_deadlock= test(data->owner->info->n_cursors);
|
||||
DBUG_ENTER("wait_for_lock");
|
||||
|
||||
if (!in_wait_list)
|
||||
{
|
||||
|
@ -431,13 +432,21 @@ wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data,
|
|||
if the predicate is true.
|
||||
*/
|
||||
if (data->cond == 0)
|
||||
break;
|
||||
if (rc == ETIMEDOUT || rc == ETIME)
|
||||
{
|
||||
result= THR_LOCK_WAIT_TIMEOUT;
|
||||
DBUG_PRINT("thr_lock", ("lock granted/aborted"));
|
||||
break;
|
||||
}
|
||||
if (rc == ETIMEDOUT || rc == ETIME)
|
||||
{
|
||||
/* purecov: begin inspected */
|
||||
DBUG_PRINT("thr_lock", ("lock timed out"));
|
||||
result= THR_LOCK_WAIT_TIMEOUT;
|
||||
break;
|
||||
/* purecov: end */
|
||||
}
|
||||
}
|
||||
DBUG_PRINT("thr_lock", ("aborted: %d in_wait_list: %d",
|
||||
thread_var->abort, in_wait_list));
|
||||
|
||||
if (data->cond || data->type == TL_UNLOCK)
|
||||
{
|
||||
|
@ -453,6 +462,7 @@ wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data,
|
|||
}
|
||||
else
|
||||
{
|
||||
DBUG_PRINT("thr_lock", ("lock aborted"));
|
||||
check_locks(data->lock, "aborted wait_for_lock", 0);
|
||||
}
|
||||
}
|
||||
|
@ -471,7 +481,7 @@ wait_for_lock(struct st_lock_list *wait, THR_LOCK_DATA *data,
|
|||
thread_var->current_mutex= 0;
|
||||
thread_var->current_cond= 0;
|
||||
pthread_mutex_unlock(&thread_var->mutex);
|
||||
return result;
|
||||
DBUG_RETURN(result);
|
||||
}
|
||||
|
||||
|
||||
|
@ -509,7 +519,7 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner,
|
|||
and the read lock is not TL_READ_NO_INSERT
|
||||
*/
|
||||
|
||||
DBUG_PRINT("lock",("write locked by thread: 0x%lx",
|
||||
DBUG_PRINT("lock",("write locked 1 by thread: 0x%lx",
|
||||
lock->write.data->owner->info->thread_id));
|
||||
if (thr_lock_owner_equal(data->owner, lock->write.data->owner) ||
|
||||
(lock->write.data->type <= TL_WRITE_DELAYED &&
|
||||
|
@ -598,10 +608,14 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner,
|
|||
{
|
||||
if (lock->write.data->type == TL_WRITE_ONLY)
|
||||
{
|
||||
/* We are not allowed to get a lock in this case */
|
||||
data->type=TL_UNLOCK;
|
||||
result= THR_LOCK_ABORTED; /* Can't wait for this one */
|
||||
goto end;
|
||||
/* Allow lock owner to bypass TL_WRITE_ONLY. */
|
||||
if (!thr_lock_owner_equal(data->owner, lock->write.data->owner))
|
||||
{
|
||||
/* We are not allowed to get a lock in this case */
|
||||
data->type=TL_UNLOCK;
|
||||
result= THR_LOCK_ABORTED; /* Can't wait for this one */
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -631,10 +645,8 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner,
|
|||
statistic_increment(locks_immediate,&THR_LOCK_lock);
|
||||
goto end;
|
||||
}
|
||||
/* purecov: begin inspected */
|
||||
DBUG_PRINT("lock",("write locked by thread: 0x%lx",
|
||||
DBUG_PRINT("lock",("write locked 2 by thread: 0x%lx",
|
||||
lock->write.data->owner->info->thread_id));
|
||||
/* purecov: end */
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -669,7 +681,7 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner,
|
|||
goto end;
|
||||
}
|
||||
}
|
||||
DBUG_PRINT("lock",("write locked by thread: 0x%lx type: %d",
|
||||
DBUG_PRINT("lock",("write locked 3 by thread: 0x%lx type: %d",
|
||||
lock->read.data->owner->info->thread_id, data->type));
|
||||
}
|
||||
wait_queue= &lock->write_wait;
|
||||
|
@ -683,6 +695,7 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner,
|
|||
lock_owner= lock->read.data ? lock->read.data : lock->write.data;
|
||||
if (lock_owner && lock_owner->owner->info == owner->info)
|
||||
{
|
||||
DBUG_PRINT("lock",("deadlock"));
|
||||
result= THR_LOCK_DEADLOCK;
|
||||
goto end;
|
||||
}
|
||||
|
|
|
@ -321,7 +321,7 @@ ndbcluster_binlog_open_table(THD *thd, NDB_SHARE *share,
|
|||
DBUG_ENTER("ndbcluster_binlog_open_table");
|
||||
|
||||
safe_mutex_assert_owner(&LOCK_open);
|
||||
init_tmp_table_share(table_share, share->db, 0, share->table_name,
|
||||
init_tmp_table_share(thd, table_share, share->db, 0, share->table_name,
|
||||
share->key);
|
||||
if ((error= open_table_def(thd, table_share, 0)))
|
||||
{
|
||||
|
|
|
@ -2643,7 +2643,7 @@ int ha_create_table(THD *thd, const char *path,
|
|||
TABLE_SHARE share;
|
||||
DBUG_ENTER("ha_create_table");
|
||||
|
||||
init_tmp_table_share(&share, db, 0, table_name, path);
|
||||
init_tmp_table_share(thd, &share, db, 0, table_name, path);
|
||||
if (open_table_def(thd, &share, 0) ||
|
||||
open_table_from_share(thd, &share, "", 0, (uint) READ_ALL, 0, &table,
|
||||
TRUE))
|
||||
|
@ -2709,7 +2709,7 @@ int ha_create_table_from_engine(THD* thd, const char *db, const char *name)
|
|||
if (error)
|
||||
DBUG_RETURN(2);
|
||||
|
||||
init_tmp_table_share(&share, db, 0, name, path);
|
||||
init_tmp_table_share(thd, &share, db, 0, name, path);
|
||||
if (open_table_def(thd, &share, 0))
|
||||
{
|
||||
DBUG_RETURN(3);
|
||||
|
@ -3717,11 +3717,12 @@ int handler::ha_reset()
|
|||
int handler::ha_write_row(uchar *buf)
|
||||
{
|
||||
int error;
|
||||
DBUG_ENTER("handler::ha_write_row");
|
||||
if (unlikely(error= write_row(buf)))
|
||||
return error;
|
||||
DBUG_RETURN(error);
|
||||
if (unlikely(error= binlog_log_row<Write_rows_log_event>(table, 0, buf)))
|
||||
return error;
|
||||
return 0;
|
||||
DBUG_RETURN(error); /* purecov: inspected */
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -686,7 +686,6 @@ bool check_single_table_access(THD *thd, ulong privilege,
|
|||
bool check_routine_access(THD *thd,ulong want_access,char *db,char *name,
|
||||
bool is_proc, bool no_errors);
|
||||
bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table);
|
||||
bool check_merge_table_access(THD *thd, char *db, TABLE_LIST *table_list);
|
||||
bool check_some_routine_access(THD *thd, const char *db, const char *name, bool is_proc);
|
||||
bool multi_update_precheck(THD *thd, TABLE_LIST *tables);
|
||||
bool multi_delete_precheck(THD *thd, TABLE_LIST *tables);
|
||||
|
@ -1148,6 +1147,9 @@ TABLE *table_cache_insert_placeholder(THD *thd, const char *key,
|
|||
bool lock_table_name_if_not_cached(THD *thd, const char *db,
|
||||
const char *table_name, TABLE **table);
|
||||
TABLE *find_locked_table(THD *thd, const char *db,const char *table_name);
|
||||
void detach_merge_children(TABLE *table, bool clear_refs);
|
||||
bool fix_merge_after_open(TABLE_LIST *old_child_list, TABLE_LIST **old_last,
|
||||
TABLE_LIST *new_child_list, TABLE_LIST **new_last);
|
||||
bool reopen_table(TABLE *table);
|
||||
bool reopen_tables(THD *thd,bool get_locks,bool in_refresh);
|
||||
void close_data_files_and_morph_locks(THD *thd, const char *db,
|
||||
|
@ -1398,8 +1400,21 @@ int init_ftfuncs(THD *thd, SELECT_LEX* select, bool no_order);
|
|||
void wait_for_condition(THD *thd, pthread_mutex_t *mutex,
|
||||
pthread_cond_t *cond);
|
||||
int open_tables(THD *thd, TABLE_LIST **tables, uint *counter, uint flags);
|
||||
int simple_open_n_lock_tables(THD *thd,TABLE_LIST *tables);
|
||||
bool open_and_lock_tables(THD *thd,TABLE_LIST *tables);
|
||||
/* open_and_lock_tables with optional derived handling */
|
||||
bool open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, bool derived);
|
||||
/* simple open_and_lock_tables without derived handling */
|
||||
inline bool simple_open_n_lock_tables(THD *thd, TABLE_LIST *tables)
|
||||
{
|
||||
return open_and_lock_tables_derived(thd, tables, FALSE);
|
||||
}
|
||||
/* open_and_lock_tables with derived handling */
|
||||
inline bool open_and_lock_tables(THD *thd, TABLE_LIST *tables)
|
||||
{
|
||||
return open_and_lock_tables_derived(thd, tables, TRUE);
|
||||
}
|
||||
/* simple open_and_lock_tables without derived handling for single table */
|
||||
TABLE *open_n_lock_single_table(THD *thd, TABLE_LIST *table_l,
|
||||
thr_lock_type lock_type);
|
||||
bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables, uint flags);
|
||||
int lock_tables(THD *thd, TABLE_LIST *tables, uint counter, bool *need_reopen);
|
||||
int decide_logging_format(THD *thd, TABLE_LIST *tables);
|
||||
|
@ -1976,7 +1991,8 @@ int format_number(uint inputflag,uint max_length,char * pos,uint length,
|
|||
/* table.cc */
|
||||
TABLE_SHARE *alloc_table_share(TABLE_LIST *table_list, char *key,
|
||||
uint key_length);
|
||||
void init_tmp_table_share(TABLE_SHARE *share, const char *key, uint key_length,
|
||||
void init_tmp_table_share(THD *thd, TABLE_SHARE *share, const char *key,
|
||||
uint key_length,
|
||||
const char *table_name, const char *path);
|
||||
void free_table_share(TABLE_SHARE *share);
|
||||
int open_table_def(THD *thd, TABLE_SHARE *share, uint db_flags);
|
||||
|
|
|
@ -1013,6 +1013,12 @@ static int create_table_from_dump(THD* thd, MYSQL *mysql, const char* db,
|
|||
goto err; // mysql_parse took care of the error send
|
||||
|
||||
thd->proc_info = "Opening master dump table";
|
||||
/*
|
||||
Note: If this function starts to fail for MERGE tables,
|
||||
change the next two lines to these:
|
||||
tables.table= NULL; // was set by mysql_rm_table()
|
||||
if (!open_n_lock_single_table(thd, &tables, TL_WRITE))
|
||||
*/
|
||||
tables.lock_type = TL_WRITE;
|
||||
if (!open_ltable(thd, &tables, TL_WRITE, 0))
|
||||
{
|
||||
|
|
1072
sql/sql_base.cc
1072
sql/sql_base.cc
File diff suppressed because it is too large
Load diff
|
@ -2273,7 +2273,17 @@ pthread_handler_t handle_delayed_insert(void *arg)
|
|||
parsed using a lex, that depends on initialized thd->lex.
|
||||
*/
|
||||
lex_start(thd);
|
||||
if (!(di->table=open_ltable(thd, &di->table_list, TL_WRITE_DELAYED, 0)))
|
||||
thd->lex->sql_command= SQLCOM_INSERT; // For innodb::store_lock()
|
||||
/*
|
||||
Statement-based replication of INSERT DELAYED has problems with RAND()
|
||||
and user vars, so in mixed mode we go to row-based.
|
||||
*/
|
||||
thd->lex->set_stmt_unsafe();
|
||||
thd->set_current_stmt_binlog_row_based_if_mixed();
|
||||
|
||||
/* Open table */
|
||||
if (!(di->table= open_n_lock_single_table(thd, &di->table_list,
|
||||
TL_WRITE_DELAYED)))
|
||||
{
|
||||
thd->fatal_error(); // Abort waiting inserts
|
||||
goto err;
|
||||
|
@ -3309,7 +3319,7 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
|
|||
tmp_table.alias= 0;
|
||||
tmp_table.timestamp_field= 0;
|
||||
tmp_table.s= &share;
|
||||
init_tmp_table_share(&share, "", 0, "", "");
|
||||
init_tmp_table_share(thd, &share, "", 0, "", "");
|
||||
|
||||
tmp_table.s->db_create_options=0;
|
||||
tmp_table.s->blob_ptr_size= portable_sizeof_char_ptr;
|
||||
|
|
|
@ -470,6 +470,46 @@ end:
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
@brief Check access privs for a MERGE table and fix children lock types.
|
||||
|
||||
@param[in] thd thread handle
|
||||
@param[in] db database name
|
||||
@param[in,out] table_list list of child tables (merge_list)
|
||||
lock_type and optionally db set per table
|
||||
|
||||
@return status
|
||||
@retval 0 OK
|
||||
@retval != 0 Error
|
||||
|
||||
@detail
|
||||
This function is used for write access to MERGE tables only
|
||||
(CREATE TABLE, ALTER TABLE ... UNION=(...)). Set TL_WRITE for
|
||||
every child. Set 'db' for every child if not present.
|
||||
*/
|
||||
|
||||
static bool check_merge_table_access(THD *thd, char *db,
|
||||
TABLE_LIST *table_list)
|
||||
{
|
||||
int error= 0;
|
||||
|
||||
if (table_list)
|
||||
{
|
||||
/* Check that all tables use the current database */
|
||||
TABLE_LIST *tlist;
|
||||
|
||||
for (tlist= table_list; tlist; tlist= tlist->next_local)
|
||||
{
|
||||
if (!tlist->db || !tlist->db[0])
|
||||
tlist->db= db; /* purecov: inspected */
|
||||
}
|
||||
error= check_table_access(thd, SELECT_ACL | UPDATE_ACL | DELETE_ACL,
|
||||
table_list,0);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
/* This works because items are allocated with sql_alloc() */
|
||||
|
||||
void free_items(Item *item)
|
||||
|
@ -2254,6 +2294,19 @@ mysql_execute_command(THD *thd)
|
|||
select_lex->options|= SELECT_NO_UNLOCK;
|
||||
unit->set_limit(select_lex);
|
||||
|
||||
/*
|
||||
Disable non-empty MERGE tables with CREATE...SELECT. Too
|
||||
complicated. See Bug #26379. Empty MERGE tables are read-only
|
||||
and don't allow CREATE...SELECT anyway.
|
||||
*/
|
||||
if (create_info.used_fields & HA_CREATE_USED_UNION)
|
||||
{
|
||||
my_error(ER_WRONG_OBJECT, MYF(0), create_table->db,
|
||||
create_table->table_name, "BASE TABLE");
|
||||
res= 1;
|
||||
goto end_with_restore_list;
|
||||
}
|
||||
|
||||
if (!(create_info.options & HA_LEX_CREATE_TMP_TABLE))
|
||||
{
|
||||
lex->link_first_table_back(create_table, link_to_local);
|
||||
|
@ -5094,26 +5147,6 @@ bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table)
|
|||
}
|
||||
|
||||
|
||||
bool check_merge_table_access(THD *thd, char *db,
|
||||
TABLE_LIST *table_list)
|
||||
{
|
||||
int error=0;
|
||||
if (table_list)
|
||||
{
|
||||
/* Check that all tables use the current database */
|
||||
TABLE_LIST *tmp;
|
||||
for (tmp= table_list; tmp; tmp= tmp->next_local)
|
||||
{
|
||||
if (!tmp->db || !tmp->db[0])
|
||||
tmp->db=db;
|
||||
}
|
||||
error=check_table_access(thd, SELECT_ACL | UPDATE_ACL | DELETE_ACL,
|
||||
table_list,0);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
Check stack size; Send error if there isn't enough stack to continue
|
||||
****************************************************************************/
|
||||
|
|
|
@ -4951,7 +4951,7 @@ the generated partition syntax in a correct manner.
|
|||
|
||||
We use the old partitioning also for the new table. We do this
|
||||
by assigning the partition_info from the table loaded in
|
||||
open_ltable to the partition_info struct used by mysql_create_table
|
||||
open_table to the partition_info struct used by mysql_create_table
|
||||
later in this method.
|
||||
|
||||
Case IIb:
|
||||
|
|
|
@ -9608,7 +9608,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
|
|||
table->keys_in_use_for_query.init();
|
||||
|
||||
table->s= share;
|
||||
init_tmp_table_share(share, "", 0, tmpname, tmpname);
|
||||
init_tmp_table_share(thd, share, "", 0, tmpname, tmpname);
|
||||
share->blob_field= blob_field;
|
||||
share->blob_ptr_size= mi_portable_sizeof_char_ptr;
|
||||
share->db_low_byte_first=1; // True for HEAP and MyISAM
|
||||
|
|
|
@ -1556,13 +1556,18 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
|
|||
/* Don't give warnings for not found errors, as we already generate notes */
|
||||
thd->no_warnings_for_error= 1;
|
||||
|
||||
/* Remove the tables from the HANDLER list, if they are in it. */
|
||||
mysql_ha_flush(thd, tables, MYSQL_HA_CLOSE_FINAL, 1);
|
||||
|
||||
for (table= tables; table; table= table->next_local)
|
||||
{
|
||||
char *db=table->db;
|
||||
handlerton *table_type;
|
||||
enum legacy_db_type frm_db_type;
|
||||
|
||||
mysql_ha_flush(thd, table, MYSQL_HA_CLOSE_FINAL, 1);
|
||||
DBUG_PRINT("table", ("table_l: '%s'.'%s' table: 0x%lx s: 0x%lx",
|
||||
table->db, table->table_name, (long) table->table,
|
||||
table->table ? (long) table->table->s : (long) -1));
|
||||
|
||||
error= drop_temporary_table(thd, table);
|
||||
|
||||
|
@ -1690,6 +1695,8 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
|
|||
wrong_tables.append(',');
|
||||
wrong_tables.append(String(table->table_name,system_charset_info));
|
||||
}
|
||||
DBUG_PRINT("table", ("table: 0x%lx s: 0x%lx", (long) table->table,
|
||||
table->table ? (long) table->table->s : (long) -1));
|
||||
}
|
||||
/*
|
||||
It's safe to unlock LOCK_open: we have an exclusive lock
|
||||
|
@ -3836,6 +3843,8 @@ static int prepare_for_restore(THD* thd, TABLE_LIST* table,
|
|||
DBUG_RETURN(send_check_errmsg(thd, table, "restore",
|
||||
"Failed to open partially restored table"));
|
||||
}
|
||||
/* A MERGE table must not come here. */
|
||||
DBUG_ASSERT(!table->table || !table->table->child_l);
|
||||
pthread_mutex_unlock(&LOCK_open);
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
@ -3878,6 +3887,10 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list,
|
|||
table= &tmp_table;
|
||||
pthread_mutex_unlock(&LOCK_open);
|
||||
}
|
||||
|
||||
/* A MERGE table must not come here. */
|
||||
DBUG_ASSERT(!table->child_l);
|
||||
|
||||
/*
|
||||
REPAIR TABLE ... USE_FRM for temporary tables makes little sense.
|
||||
*/
|
||||
|
@ -4032,6 +4045,8 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
|
|||
char* db = table->db;
|
||||
bool fatal_error=0;
|
||||
|
||||
DBUG_PRINT("admin", ("table: '%s'.'%s'", table->db, table->table_name));
|
||||
DBUG_PRINT("admin", ("extra_open_options: %u", extra_open_options));
|
||||
strxmov(table_name, db, ".", table->table_name, NullS);
|
||||
thd->open_options|= extra_open_options;
|
||||
table->lock_type= lock_type;
|
||||
|
@ -4062,16 +4077,24 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
|
|||
table->next_local= save_next_local;
|
||||
thd->open_options&= ~extra_open_options;
|
||||
}
|
||||
DBUG_PRINT("admin", ("table: 0x%lx", (long) table->table));
|
||||
|
||||
if (prepare_func)
|
||||
{
|
||||
DBUG_PRINT("admin", ("calling prepare_func"));
|
||||
switch ((*prepare_func)(thd, table, check_opt)) {
|
||||
case 1: // error, message written to net
|
||||
ha_autocommit_or_rollback(thd, 1);
|
||||
close_thread_tables(thd);
|
||||
DBUG_PRINT("admin", ("simple error, admin next table"));
|
||||
continue;
|
||||
case -1: // error, message could be written to net
|
||||
/* purecov: begin inspected */
|
||||
DBUG_PRINT("admin", ("severe error, stop"));
|
||||
goto err;
|
||||
/* purecov: end */
|
||||
default: // should be 0 otherwise
|
||||
DBUG_PRINT("admin", ("prepare_func succeeded"));
|
||||
;
|
||||
}
|
||||
}
|
||||
|
@ -4086,6 +4109,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
|
|||
*/
|
||||
if (!table->table)
|
||||
{
|
||||
DBUG_PRINT("admin", ("open table failed"));
|
||||
if (!thd->warn_list.elements)
|
||||
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
|
||||
ER_CHECK_NO_SUCH_TABLE, ER(ER_CHECK_NO_SUCH_TABLE));
|
||||
|
@ -4100,14 +4124,17 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
|
|||
|
||||
if (table->view)
|
||||
{
|
||||
DBUG_PRINT("admin", ("calling view_operator_func"));
|
||||
result_code= (*view_operator_func)(thd, table);
|
||||
goto send_result;
|
||||
}
|
||||
|
||||
if ((table->table->db_stat & HA_READ_ONLY) && open_for_modify)
|
||||
{
|
||||
/* purecov: begin inspected */
|
||||
char buff[FN_REFLEN + MYSQL_ERRMSG_SIZE];
|
||||
uint length;
|
||||
DBUG_PRINT("admin", ("sending error message"));
|
||||
protocol->prepare_for_resend();
|
||||
protocol->store(table_name, system_charset_info);
|
||||
protocol->store(operator_name, system_charset_info);
|
||||
|
@ -4122,11 +4149,13 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
|
|||
if (protocol->write())
|
||||
goto err;
|
||||
continue;
|
||||
/* purecov: end */
|
||||
}
|
||||
|
||||
/* Close all instances of the table to allow repair to rename files */
|
||||
if (lock_type == TL_WRITE && table->table->s->version)
|
||||
{
|
||||
DBUG_PRINT("admin", ("removing table from cache"));
|
||||
pthread_mutex_lock(&LOCK_open);
|
||||
const char *old_message=thd->enter_cond(&COND_refresh, &LOCK_open,
|
||||
"Waiting to get writelock");
|
||||
|
@ -4146,6 +4175,8 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
|
|||
|
||||
if (table->table->s->crashed && operator_func == &handler::ha_check)
|
||||
{
|
||||
/* purecov: begin inspected */
|
||||
DBUG_PRINT("admin", ("sending crashed warning"));
|
||||
protocol->prepare_for_resend();
|
||||
protocol->store(table_name, system_charset_info);
|
||||
protocol->store(operator_name, system_charset_info);
|
||||
|
@ -4154,6 +4185,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
|
|||
system_charset_info);
|
||||
if (protocol->write())
|
||||
goto err;
|
||||
/* purecov: end */
|
||||
}
|
||||
|
||||
if (operator_func == &handler::ha_repair &&
|
||||
|
@ -4164,6 +4196,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
|
|||
HA_ADMIN_NEEDS_ALTER))
|
||||
{
|
||||
my_bool save_no_send_ok= thd->net.no_send_ok;
|
||||
DBUG_PRINT("admin", ("recreating table"));
|
||||
ha_autocommit_or_rollback(thd, 1);
|
||||
close_thread_tables(thd);
|
||||
tmp_disable_binlog(thd); // binlogging is done by caller if wanted
|
||||
|
@ -4176,7 +4209,9 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
|
|||
|
||||
}
|
||||
|
||||
DBUG_PRINT("admin", ("calling operator_func '%s'", operator_name));
|
||||
result_code = (table->table->file->*operator_func)(thd, check_opt);
|
||||
DBUG_PRINT("admin", ("operator_func returned: %d", result_code));
|
||||
|
||||
send_result:
|
||||
|
||||
|
@ -5834,10 +5869,25 @@ view_err:
|
|||
start_waiting_global_read_lock(thd);
|
||||
DBUG_RETURN(error);
|
||||
}
|
||||
if (!(table=open_ltable(thd, table_list, TL_WRITE_ALLOW_READ, 0)))
|
||||
|
||||
if (!(table= open_n_lock_single_table(thd, table_list, TL_WRITE_ALLOW_READ)))
|
||||
DBUG_RETURN(TRUE);
|
||||
table->use_all_columns();
|
||||
|
||||
/*
|
||||
Prohibit changing of the UNION list of a non-temporary MERGE table
|
||||
under LOCK tables. It would be quite difficult to reuse a shrinked
|
||||
set of tables from the old table or to open a new TABLE object for
|
||||
an extended list and verify that they belong to locked tables.
|
||||
*/
|
||||
if (thd->locked_tables &&
|
||||
(create_info->used_fields & HA_CREATE_USED_UNION) &&
|
||||
(table->s->tmp_table == NO_TMP_TABLE))
|
||||
{
|
||||
my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0));
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
|
||||
/* Check that we are not trying to rename to an existing table */
|
||||
if (new_name)
|
||||
{
|
||||
|
@ -6305,6 +6355,7 @@ view_err:
|
|||
goto err;
|
||||
|
||||
/* Open the table if we need to copy the data. */
|
||||
DBUG_PRINT("info", ("need_copy_table: %u", need_copy_table));
|
||||
if (need_copy_table != ALTER_TABLE_METADATA_ONLY)
|
||||
{
|
||||
if (table->s->tmp_table)
|
||||
|
@ -6328,6 +6379,10 @@ view_err:
|
|||
}
|
||||
if (!new_table)
|
||||
goto err1;
|
||||
/*
|
||||
Note: In case of MERGE table, we do not attach children. We do not
|
||||
copy data for MERGE tables. Only the children have data.
|
||||
*/
|
||||
}
|
||||
|
||||
/* Copy the data if necessary. */
|
||||
|
@ -6335,6 +6390,10 @@ view_err:
|
|||
thd->cuted_fields=0L;
|
||||
thd->proc_info="copy to tmp table";
|
||||
copied=deleted=0;
|
||||
/*
|
||||
We do not copy data for MERGE tables. Only the children have data.
|
||||
MERGE tables have HA_NO_COPY_ON_ALTER set.
|
||||
*/
|
||||
if (new_table && !(new_table->file->ha_table_flags() & HA_NO_COPY_ON_ALTER))
|
||||
{
|
||||
/* We don't want update TIMESTAMP fields during ALTER TABLE. */
|
||||
|
@ -6472,7 +6531,10 @@ view_err:
|
|||
|
||||
if (new_table)
|
||||
{
|
||||
/* Close the intermediate table that will be the new table */
|
||||
/*
|
||||
Close the intermediate table that will be the new table.
|
||||
Note that MERGE tables do not have their children attached here.
|
||||
*/
|
||||
intern_close_table(new_table);
|
||||
my_free(new_table,MYF(0));
|
||||
}
|
||||
|
@ -6565,6 +6627,7 @@ view_err:
|
|||
/*
|
||||
Now we have to inform handler that new .FRM file is in place.
|
||||
To do this we need to obtain a handler object for it.
|
||||
NO need to tamper with MERGE tables. The real open is done later.
|
||||
*/
|
||||
TABLE *t_table;
|
||||
if (new_name != table_name || new_db != db)
|
||||
|
@ -6632,7 +6695,7 @@ view_err:
|
|||
/*
|
||||
For the alter table to be properly flushed to the logs, we
|
||||
have to open the new table. If not, we get a problem on server
|
||||
shutdown.
|
||||
shutdown. But we do not need to attach MERGE children.
|
||||
*/
|
||||
char path[FN_REFLEN];
|
||||
TABLE *t_table;
|
||||
|
@ -6962,6 +7025,12 @@ bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list)
|
|||
Alter_info alter_info;
|
||||
|
||||
DBUG_ENTER("mysql_recreate_table");
|
||||
DBUG_ASSERT(!table_list->next_global);
|
||||
/*
|
||||
table_list->table has been closed and freed. Do not reference
|
||||
uninitialized data. open_tables() could fail.
|
||||
*/
|
||||
table_list->table= NULL;
|
||||
|
||||
bzero((char*) &create_info, sizeof(create_info));
|
||||
create_info.db_type= 0;
|
||||
|
@ -6993,6 +7062,7 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables,
|
|||
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
|
||||
DBUG_RETURN(TRUE);
|
||||
|
||||
/* Open one table after the other to keep lock time as short as possible. */
|
||||
for (table= tables; table; table= table->next_local)
|
||||
{
|
||||
char table_name[NAME_LEN*2+2];
|
||||
|
@ -7000,7 +7070,7 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables,
|
|||
|
||||
strxmov(table_name, table->db ,".", table->table_name, NullS);
|
||||
|
||||
t= table->table= open_ltable(thd, table, TL_READ, 0);
|
||||
t= table->table= open_n_lock_single_table(thd, table, TL_READ);
|
||||
thd->clear_error(); // these errors shouldn't get client
|
||||
|
||||
protocol->prepare_for_resend();
|
||||
|
|
|
@ -436,13 +436,37 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
|
|||
if (lock_table_names(thd, tables))
|
||||
goto end;
|
||||
|
||||
/* We also don't allow creation of triggers on views. */
|
||||
tables->required_type= FRMTYPE_TABLE;
|
||||
|
||||
if (reopen_name_locked_table(thd, tables, TRUE))
|
||||
/*
|
||||
If the table is under LOCK TABLES, lock_table_names() does not set
|
||||
tables->table. Find the table in open_tables.
|
||||
*/
|
||||
if (!tables->table && thd->locked_tables)
|
||||
{
|
||||
unlock_table_name(thd, tables);
|
||||
for (table= thd->open_tables;
|
||||
table && (strcmp(table->s->table_name.str, tables->table_name) ||
|
||||
strcmp(table->s->db.str, tables->db));
|
||||
table= table->next) {}
|
||||
tables->table= table;
|
||||
}
|
||||
if (!tables->table)
|
||||
{
|
||||
/* purecov: begin inspected */
|
||||
my_error(ER_TABLE_NOT_LOCKED, MYF(0), tables->alias);
|
||||
goto end;
|
||||
/* purecov: end */
|
||||
}
|
||||
|
||||
/* No need to reopen the table if it is locked with LOCK TABLES. */
|
||||
if (!thd->locked_tables || (tables->table->in_use != thd))
|
||||
{
|
||||
/* We also don't allow creation of triggers on views. */
|
||||
tables->required_type= FRMTYPE_TABLE;
|
||||
|
||||
if (reopen_name_locked_table(thd, tables, TRUE))
|
||||
{
|
||||
unlock_table_name(thd, tables);
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
table= tables->table;
|
||||
|
||||
|
@ -462,6 +486,16 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
|
|||
table->triggers->create_trigger(thd, tables, &stmt_query):
|
||||
table->triggers->drop_trigger(thd, tables, &stmt_query));
|
||||
|
||||
/* Under LOCK TABLES we must reopen the table to activate the trigger. */
|
||||
if (!result && thd->locked_tables)
|
||||
{
|
||||
close_data_files_and_morph_locks(thd, table->s->db.str,
|
||||
table->s->table_name.str);
|
||||
thd->in_lock_tables= 1;
|
||||
result= reopen_tables(thd, 1, 0);
|
||||
thd->in_lock_tables= 0;
|
||||
}
|
||||
|
||||
end:
|
||||
|
||||
if (!result)
|
||||
|
|
29
sql/table.cc
29
sql/table.cc
|
@ -329,6 +329,7 @@ TABLE_SHARE *alloc_table_share(TABLE_LIST *table_list, char *key,
|
|||
|
||||
SYNOPSIS
|
||||
init_tmp_table_share()
|
||||
thd thread handle
|
||||
share Share to fill
|
||||
key Table_cache_key, as generated from create_table_def_key.
|
||||
must start with db name.
|
||||
|
@ -346,7 +347,7 @@ TABLE_SHARE *alloc_table_share(TABLE_LIST *table_list, char *key,
|
|||
use key_length= 0 as neither table_cache_key or key_length will be used).
|
||||
*/
|
||||
|
||||
void init_tmp_table_share(TABLE_SHARE *share, const char *key,
|
||||
void init_tmp_table_share(THD *thd, TABLE_SHARE *share, const char *key,
|
||||
uint key_length, const char *table_name,
|
||||
const char *path)
|
||||
{
|
||||
|
@ -373,9 +374,14 @@ void init_tmp_table_share(TABLE_SHARE *share, const char *key,
|
|||
anyway to be able to catch errors.
|
||||
*/
|
||||
share->table_map_version= ~(ulonglong)0;
|
||||
share->table_map_id= ~0UL;
|
||||
share->cached_row_logging_check= -1;
|
||||
|
||||
/*
|
||||
table_map_id is also used for MERGE tables to suppress repeated
|
||||
compatibility checks.
|
||||
*/
|
||||
share->table_map_id= (ulong) thd->query_id;
|
||||
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
|
@ -4477,6 +4483,25 @@ void st_table::mark_columns_needed_for_insert()
|
|||
mark_auto_increment_column();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
@brief Check if this is part of a MERGE table with attached children.
|
||||
|
||||
@return status
|
||||
@retval TRUE children are attached
|
||||
@retval FALSE no MERGE part or children not attached
|
||||
|
||||
@detail
|
||||
A MERGE table consists of a parent TABLE and zero or more child
|
||||
TABLEs. Each of these TABLEs is called a part of a MERGE table.
|
||||
*/
|
||||
|
||||
bool st_table::is_children_attached(void)
|
||||
{
|
||||
return((child_l && children_attached) ||
|
||||
(parent && parent->children_attached));
|
||||
}
|
||||
|
||||
/*
|
||||
Cleanup this table for re-execution.
|
||||
|
||||
|
|
33
sql/table.h
33
sql/table.h
|
@ -431,6 +431,12 @@ typedef struct st_table_share
|
|||
{
|
||||
return (table_category == TABLE_CATEGORY_PERFORMANCE);
|
||||
}
|
||||
|
||||
inline ulong get_table_def_version()
|
||||
{
|
||||
return table_map_id;
|
||||
}
|
||||
|
||||
} TABLE_SHARE;
|
||||
|
||||
|
||||
|
@ -455,6 +461,11 @@ struct st_table {
|
|||
#endif
|
||||
struct st_table *next, *prev;
|
||||
|
||||
/* For the below MERGE related members see top comment in ha_myisammrg.cc */
|
||||
struct st_table *parent; /* Set in MERGE child. Ptr to parent */
|
||||
TABLE_LIST *child_l; /* Set in MERGE parent. List of children */
|
||||
TABLE_LIST **child_last_l; /* Set in MERGE parent. End of list */
|
||||
|
||||
THD *in_use; /* Which thread uses this */
|
||||
Field **field; /* Pointer to fields */
|
||||
|
||||
|
@ -622,6 +633,8 @@ struct st_table {
|
|||
my_bool insert_or_update; /* Can be used by the handler */
|
||||
my_bool alias_name_used; /* true if table_name is alias */
|
||||
my_bool get_fields_in_item_tree; /* Signal to fix_field */
|
||||
/* If MERGE children attached to parent. See top comment in ha_myisammrg.cc */
|
||||
my_bool children_attached;
|
||||
|
||||
REGINFO reginfo; /* field connections */
|
||||
MEM_ROOT mem_root;
|
||||
|
@ -673,6 +686,7 @@ struct st_table {
|
|||
*/
|
||||
inline bool needs_reopen_or_name_lock()
|
||||
{ return s->version != refresh_version; }
|
||||
bool is_children_attached(void);
|
||||
};
|
||||
|
||||
enum enum_schema_table_state
|
||||
|
@ -996,6 +1010,8 @@ struct TABLE_LIST
|
|||
(non-zero only for merged underlying tables of a view).
|
||||
*/
|
||||
TABLE_LIST *referencing_view;
|
||||
/* Ptr to parent MERGE table list item. See top comment in ha_myisammrg.cc */
|
||||
TABLE_LIST *parent_l;
|
||||
/*
|
||||
Security context (non-zero only for tables which belong
|
||||
to view with SQL SECURITY DEFINER)
|
||||
|
@ -1176,6 +1192,20 @@ struct TABLE_LIST
|
|||
*/
|
||||
bool process_index_hints(TABLE *table);
|
||||
|
||||
/* Access MERGE child def version. See top comment in ha_myisammrg.cc */
|
||||
inline ulong get_child_def_version()
|
||||
{
|
||||
return child_def_version;
|
||||
}
|
||||
inline void set_child_def_version(ulong version)
|
||||
{
|
||||
child_def_version= version;
|
||||
}
|
||||
inline void init_child_def_version()
|
||||
{
|
||||
child_def_version= ~0UL;
|
||||
}
|
||||
|
||||
private:
|
||||
bool prep_check_option(THD *thd, uint8 check_opt_type);
|
||||
bool prep_where(THD *thd, Item **conds, bool no_where_clause);
|
||||
|
@ -1183,6 +1213,9 @@ private:
|
|||
Cleanup for re-execution in a prepared statement or a stored
|
||||
procedure.
|
||||
*/
|
||||
|
||||
/* Remembered MERGE child def version. See top comment in ha_myisammrg.cc */
|
||||
ulong child_def_version;
|
||||
};
|
||||
|
||||
class Item;
|
||||
|
|
|
@ -119,6 +119,10 @@ static void mi_check_print_msg(MI_CHECK *param, const char* msg_type,
|
|||
definition for further use in mi_create or for a check for underlying
|
||||
table conformance in merge engine.
|
||||
|
||||
The caller needs to free *recinfo_out after use. Since *recinfo_out
|
||||
and *keydef_out are allocated with a my_multi_malloc, *keydef_out
|
||||
is freed automatically when *recinfo_out is freed.
|
||||
|
||||
RETURN VALUE
|
||||
0 OK
|
||||
!0 error code
|
||||
|
|
|
@ -140,4 +140,8 @@ class ha_myisam: public handler
|
|||
*engine_callback,
|
||||
ulonglong *engine_data);
|
||||
#endif
|
||||
MI_INFO *file_ptr(void)
|
||||
{
|
||||
return file;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -14,6 +14,82 @@
|
|||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
|
||||
|
||||
/*
|
||||
MyISAM MERGE tables
|
||||
|
||||
A MyISAM MERGE table is kind of a union of zero or more MyISAM tables.
|
||||
|
||||
Besides the normal form file (.frm) a MERGE table has a meta file
|
||||
(.MRG) with a list of tables. These are paths to the MyISAM table
|
||||
files. The last two components of the path contain the database name
|
||||
and the table name respectively.
|
||||
|
||||
When a MERGE table is open, there exists an TABLE object for the MERGE
|
||||
table itself and a TABLE object for each of the MyISAM tables. For
|
||||
abbreviated writing, I call the MERGE table object "parent" and the
|
||||
MyISAM table objects "children".
|
||||
|
||||
A MERGE table is almost always opened through open_and_lock_tables()
|
||||
and hence through open_tables(). When the parent appears in the list
|
||||
of tables to open, the initial open of the handler does nothing but
|
||||
read the meta file and collect a list of TABLE_LIST objects for the
|
||||
children. This list is attached to the parent TABLE object as
|
||||
TABLE::child_l. The end of the children list is saved in
|
||||
TABLE::child_last_l.
|
||||
|
||||
Back in open_tables(), add_merge_table_list() is called. It updates
|
||||
each list member with the lock type and a back pointer to the parent
|
||||
TABLE_LIST object TABLE_LIST::parent_l. The list is then inserted in
|
||||
the list of tables to open, right behind the parent. Consequently,
|
||||
open_tables() opens the children, one after the other. The TABLE
|
||||
references of the TABLE_LIST objects are implicitly set to the open
|
||||
tables. The children are opened as independent MyISAM tables, right as
|
||||
if they are used by the SQL statement.
|
||||
|
||||
TABLE_LIST::parent_l is required to find the parent 1. when the last
|
||||
child has been opened and children are to be attached, and 2. when an
|
||||
error happens during child open and the child list must be removed
|
||||
from the queuery list. In these cases the current child does not have
|
||||
TABLE::parent set or does not have a TABLE at all respectively.
|
||||
|
||||
When the last child is open, attach_merge_children() is called. It
|
||||
removes the list of children from the open list. Then the children are
|
||||
"attached" to the parent. All required references between parent and
|
||||
children are set up.
|
||||
|
||||
The MERGE storage engine sets up an array with references to the
|
||||
low-level MyISAM table objects (MI_INFO). It remembers the state of
|
||||
the table in MYRG_INFO::children_attached.
|
||||
|
||||
Every child TABLE::parent references the parent TABLE object. That way
|
||||
TABLE objects belonging to a MERGE table can be identified.
|
||||
TABLE::parent is required because the parent and child TABLE objects
|
||||
can live longer than the parent TABLE_LIST object. So the path
|
||||
child->pos_in_table_list->parent_l->table can be broken.
|
||||
|
||||
If necessary, the compatibility of parent and children is checked.
|
||||
This check is necessary when any of the objects are reopend. This is
|
||||
detected by comparing the current table def version against the
|
||||
remembered child def version. On parent open, the list members are
|
||||
initialized to an "impossible"/"undefined" version value. So the check
|
||||
is always executed on the first attach.
|
||||
|
||||
The version check is done in myisammrg_attach_children_callback(),
|
||||
which is called for every child. ha_myisammrg::attach_children()
|
||||
initializes 'need_compat_check' to FALSE and
|
||||
myisammrg_attach_children_callback() sets it ot TRUE if a table
|
||||
def version mismatches the remembered child def version.
|
||||
|
||||
Finally the parent TABLE::children_attached is set.
|
||||
|
||||
---
|
||||
|
||||
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
|
||||
#pragma implementation // gcc: Class implementation
|
||||
#endif
|
||||
|
@ -22,14 +98,11 @@
|
|||
#include "mysql_priv.h"
|
||||
#include <mysql/plugin.h>
|
||||
#include <m_ctype.h>
|
||||
#include "../myisam/ha_myisam.h"
|
||||
#include "ha_myisammrg.h"
|
||||
#include "myrg_def.h"
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
** MyISAM MERGE tables
|
||||
*****************************************************************************/
|
||||
|
||||
static handler *myisammrg_create_handler(handlerton *hton,
|
||||
TABLE_SHARE *table,
|
||||
MEM_ROOT *mem_root)
|
||||
|
@ -38,10 +111,23 @@ static handler *myisammrg_create_handler(handlerton *hton,
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
@brief Constructor
|
||||
*/
|
||||
|
||||
ha_myisammrg::ha_myisammrg(handlerton *hton, TABLE_SHARE *table_arg)
|
||||
:handler(hton, table_arg), file(0)
|
||||
{}
|
||||
|
||||
|
||||
/**
|
||||
@brief Destructor
|
||||
*/
|
||||
|
||||
ha_myisammrg::~ha_myisammrg(void)
|
||||
{}
|
||||
|
||||
|
||||
static const char *ha_myisammrg_exts[] = {
|
||||
".MRG",
|
||||
NullS
|
||||
|
@ -89,25 +175,311 @@ const char *ha_myisammrg::index_type(uint key_number)
|
|||
}
|
||||
|
||||
|
||||
int ha_myisammrg::open(const char *name, int mode, uint test_if_locked)
|
||||
{
|
||||
MI_KEYDEF *keyinfo;
|
||||
MI_COLUMNDEF *recinfo;
|
||||
MYRG_TABLE *u_table;
|
||||
uint recs;
|
||||
uint keys= table->s->keys;
|
||||
int error;
|
||||
char name_buff[FN_REFLEN];
|
||||
/**
|
||||
@brief Callback function for open of a MERGE parent table.
|
||||
|
||||
DBUG_PRINT("info", ("ha_myisammrg::open"));
|
||||
if (!(file=myrg_open(fn_format(name_buff,name,"","",
|
||||
MY_UNPACK_FILENAME|MY_APPEND_EXT),
|
||||
mode, test_if_locked)))
|
||||
@detail This function adds a TABLE_LIST object for a MERGE child table
|
||||
to a list of tables of the parent TABLE object. It is called for
|
||||
each child table.
|
||||
|
||||
The list of child TABLE_LIST objects is kept in the TABLE object of
|
||||
the parent for the whole life time of the MERGE table. It is
|
||||
inserted in the statement list behind the MERGE parent TABLE_LIST
|
||||
object when the MERGE table is opened. It is removed from the
|
||||
statement list after the last child is opened.
|
||||
|
||||
All memeory used for the child TABLE_LIST objects and the strings
|
||||
referred by it are taken from the parent TABLE::mem_root. Thus they
|
||||
are all freed implicitly at the final close of the table.
|
||||
|
||||
TABLE::child_l -> TABLE_LIST::next_global -> TABLE_LIST::next_global
|
||||
# # ^ # ^
|
||||
# # | # |
|
||||
# # +--------- TABLE_LIST::prev_global
|
||||
# # |
|
||||
# |<--- TABLE_LIST::prev_global |
|
||||
# |
|
||||
TABLE::child_last_l -----------------------------------------+
|
||||
|
||||
@param[in] callback_param data pointer as given to myrg_parent_open()
|
||||
@param[in] filename file name of MyISAM table
|
||||
without extension.
|
||||
|
||||
@return status
|
||||
@retval 0 OK
|
||||
@retval != 0 Error
|
||||
*/
|
||||
|
||||
static int myisammrg_parent_open_callback(void *callback_param,
|
||||
const char *filename)
|
||||
{
|
||||
ha_myisammrg *ha_myrg;
|
||||
TABLE *parent;
|
||||
TABLE_LIST *child_l;
|
||||
const char *db;
|
||||
const char *table_name;
|
||||
uint dirlen;
|
||||
char dir_path[FN_REFLEN];
|
||||
DBUG_ENTER("myisammrg_parent_open_callback");
|
||||
|
||||
/* Extract child table name and database name from filename. */
|
||||
dirlen= dirname_length(filename);
|
||||
if (dirlen >= FN_REFLEN)
|
||||
{
|
||||
DBUG_PRINT("info", ("ha_myisammrg::open exit %d", my_errno));
|
||||
return (my_errno ? my_errno : -1);
|
||||
/* purecov: begin inspected */
|
||||
DBUG_PRINT("error", ("name too long: '%.64s'", filename));
|
||||
my_errno= ENAMETOOLONG;
|
||||
DBUG_RETURN(1);
|
||||
/* purecov: end */
|
||||
}
|
||||
DBUG_PRINT("info", ("ha_myisammrg::open myrg_extrafunc..."));
|
||||
table_name= filename + dirlen;
|
||||
dirlen--; /* Strip off trailing '/'. */
|
||||
memcpy(dir_path, filename, dirlen);
|
||||
dir_path[dirlen]= '\0';
|
||||
db= base_name(dir_path);
|
||||
dirlen-= db - dir_path; /* This is now the length of 'db'. */
|
||||
DBUG_PRINT("myrg", ("open: '%s'.'%s'", db, table_name));
|
||||
|
||||
ha_myrg= (ha_myisammrg*) callback_param;
|
||||
parent= ha_myrg->table_ptr();
|
||||
|
||||
/* Get a TABLE_LIST object. */
|
||||
if (!(child_l= (TABLE_LIST*) alloc_root(&parent->mem_root,
|
||||
sizeof(TABLE_LIST))))
|
||||
{
|
||||
/* purecov: begin inspected */
|
||||
DBUG_PRINT("error", ("my_malloc error: %d", my_errno));
|
||||
DBUG_RETURN(1);
|
||||
/* purecov: end */
|
||||
}
|
||||
bzero((char*) child_l, sizeof(TABLE_LIST));
|
||||
|
||||
/* Set database (schema) name. */
|
||||
child_l->db_length= dirlen;
|
||||
child_l->db= strmake_root(&parent->mem_root, db, dirlen);
|
||||
/* Set table name. */
|
||||
child_l->table_name_length= strlen(table_name);
|
||||
child_l->table_name= strmake_root(&parent->mem_root, table_name,
|
||||
child_l->table_name_length);
|
||||
/* Convert to lowercase if required. */
|
||||
if (lower_case_table_names && child_l->table_name_length)
|
||||
child_l->table_name_length= my_casedn_str(files_charset_info,
|
||||
child_l->table_name);
|
||||
/* Set alias. */
|
||||
child_l->alias= child_l->table_name;
|
||||
|
||||
/* Initialize table map to 'undefined'. */
|
||||
child_l->init_child_def_version();
|
||||
|
||||
/* Link TABLE_LIST object into the parent list. */
|
||||
if (!parent->child_last_l)
|
||||
{
|
||||
/* Initialize parent->child_last_l when handling first child. */
|
||||
parent->child_last_l= &parent->child_l;
|
||||
}
|
||||
*parent->child_last_l= child_l;
|
||||
child_l->prev_global= parent->child_last_l;
|
||||
parent->child_last_l= &child_l->next_global;
|
||||
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
@brief Callback function for attaching a MERGE child table.
|
||||
|
||||
@detail This function retrieves the MyISAM table handle from the
|
||||
next child table. It is called for each child table.
|
||||
|
||||
@param[in] callback_param data pointer as given to
|
||||
myrg_attach_children()
|
||||
|
||||
@return pointer to open MyISAM table structure
|
||||
@retval !=NULL OK, returning pointer
|
||||
@retval NULL, my_errno == 0 Ok, no more child tables
|
||||
@retval NULL, my_errno != 0 error
|
||||
*/
|
||||
|
||||
static MI_INFO *myisammrg_attach_children_callback(void *callback_param)
|
||||
{
|
||||
ha_myisammrg *ha_myrg;
|
||||
TABLE *parent;
|
||||
TABLE *child;
|
||||
TABLE_LIST *child_l;
|
||||
MI_INFO *myisam;
|
||||
DBUG_ENTER("myisammrg_attach_children_callback");
|
||||
|
||||
my_errno= 0;
|
||||
ha_myrg= (ha_myisammrg*) callback_param;
|
||||
parent= ha_myrg->table_ptr();
|
||||
|
||||
/* Get child list item. */
|
||||
child_l= ha_myrg->next_child_attach;
|
||||
if (!child_l)
|
||||
{
|
||||
DBUG_PRINT("myrg", ("No more children to attach"));
|
||||
DBUG_RETURN(NULL);
|
||||
}
|
||||
child= child_l->table;
|
||||
DBUG_PRINT("myrg", ("child table: '%s'.'%s' 0x%lx", child->s->db.str,
|
||||
child->s->table_name.str, (long) child));
|
||||
/*
|
||||
Prepare for next child. Used as child_l in next call to this function.
|
||||
We cannot rely on a NULL-terminated chain.
|
||||
*/
|
||||
if (&child_l->next_global == parent->child_last_l)
|
||||
{
|
||||
DBUG_PRINT("myrg", ("attaching last child"));
|
||||
ha_myrg->next_child_attach= NULL;
|
||||
}
|
||||
else
|
||||
ha_myrg->next_child_attach= child_l->next_global;
|
||||
|
||||
/* Set parent reference. */
|
||||
child->parent= parent;
|
||||
|
||||
/*
|
||||
Do a quick compatibility check. The table def version is set when
|
||||
the table share is created. The child def version is copied
|
||||
from the table def version after a sucessful compatibility check.
|
||||
We need to repeat the compatibility check only if a child is opened
|
||||
from a different share than last time it was used with this MERGE
|
||||
table.
|
||||
*/
|
||||
DBUG_PRINT("myrg", ("table_def_version last: %lu current: %lu",
|
||||
(ulong) child_l->get_child_def_version(),
|
||||
(ulong) child->s->get_table_def_version()));
|
||||
if (child_l->get_child_def_version() != child->s->get_table_def_version())
|
||||
ha_myrg->need_compat_check= TRUE;
|
||||
|
||||
/*
|
||||
If parent is temporary, children must be temporary too and vice
|
||||
versa. This check must be done for every child on every open because
|
||||
the table def version can overlap between temporary and
|
||||
non-temporary tables. We need to detect the case where a
|
||||
non-temporary table has been replaced with a temporary table of the
|
||||
same version. Or vice versa. A very unlikely case, but it could
|
||||
happen.
|
||||
*/
|
||||
if (child->s->tmp_table != parent->s->tmp_table)
|
||||
{
|
||||
DBUG_PRINT("error", ("temporary table mismatch parent: %d child: %d",
|
||||
parent->s->tmp_table, child->s->tmp_table));
|
||||
my_errno= HA_ERR_WRONG_MRG_TABLE_DEF;
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Extract the MyISAM table structure pointer from the handler object. */
|
||||
if ((child->file->ht->db_type != DB_TYPE_MYISAM) ||
|
||||
!(myisam= ((ha_myisam*) child->file)->file_ptr()))
|
||||
{
|
||||
DBUG_PRINT("error", ("no MyISAM handle for child table: '%s'.'%s' 0x%lx",
|
||||
child->s->db.str, child->s->table_name.str,
|
||||
(long) child));
|
||||
my_errno= HA_ERR_WRONG_MRG_TABLE_DEF;
|
||||
}
|
||||
DBUG_PRINT("myrg", ("MyISAM handle: 0x%lx my_errno: %d",
|
||||
(long) myisam, my_errno));
|
||||
|
||||
err:
|
||||
DBUG_RETURN(my_errno ? NULL : myisam);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
@brief Open a MERGE parent table, not its children.
|
||||
|
||||
@detail This function initializes the MERGE storage engine structures
|
||||
and adds a child list of TABLE_LIST to the parent TABLE.
|
||||
|
||||
@param[in] name MERGE table path name
|
||||
@param[in] mode read/write mode, unused
|
||||
@param[in] test_if_locked open flags
|
||||
|
||||
@return status
|
||||
@retval 0 OK
|
||||
@retval -1 Error, my_errno gives reason
|
||||
*/
|
||||
|
||||
int ha_myisammrg::open(const char *name, int mode __attribute__((unused)),
|
||||
uint test_if_locked)
|
||||
{
|
||||
DBUG_ENTER("ha_myisammrg::open");
|
||||
DBUG_PRINT("myrg", ("name: '%s' table: 0x%lx", name, (long) table));
|
||||
DBUG_PRINT("myrg", ("test_if_locked: %u", test_if_locked));
|
||||
|
||||
/* Save for later use. */
|
||||
this->test_if_locked= test_if_locked;
|
||||
|
||||
/* retrieve children table list. */
|
||||
my_errno= 0;
|
||||
if (!(file= myrg_parent_open(name, myisammrg_parent_open_callback, this)))
|
||||
{
|
||||
DBUG_PRINT("error", ("my_errno %d", my_errno));
|
||||
DBUG_RETURN(my_errno ? my_errno : -1);
|
||||
}
|
||||
DBUG_PRINT("myrg", ("MYRG_INFO: 0x%lx", (long) file));
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
@brief Attach children to a MERGE table.
|
||||
|
||||
@detail Let the storage engine attach its children through a callback
|
||||
function. Check table definitions for consistency.
|
||||
|
||||
@note Special thd->open_options may be in effect. We can make use of
|
||||
them in attach. I.e. we use HA_OPEN_FOR_REPAIR to report the names
|
||||
of mismatching child tables. We cannot transport these options in
|
||||
ha_myisammrg::test_if_locked because they may change after the
|
||||
parent is opened. The parent is kept open in the table cache over
|
||||
multiple statements and can be used by other threads. Open options
|
||||
can change over time.
|
||||
|
||||
@return status
|
||||
@retval 0 OK
|
||||
@retval != 0 Error, my_errno gives reason
|
||||
*/
|
||||
|
||||
int ha_myisammrg::attach_children(void)
|
||||
{
|
||||
MYRG_TABLE *u_table;
|
||||
MI_COLUMNDEF *recinfo;
|
||||
MI_KEYDEF *keyinfo;
|
||||
uint recs;
|
||||
uint keys= table->s->keys;
|
||||
int error;
|
||||
DBUG_ENTER("ha_myisammrg::attach_children");
|
||||
DBUG_PRINT("myrg", ("table: '%s'.'%s' 0x%lx", table->s->db.str,
|
||||
table->s->table_name.str, (long) table));
|
||||
DBUG_PRINT("myrg", ("test_if_locked: %u", this->test_if_locked));
|
||||
DBUG_ASSERT(!this->file->children_attached);
|
||||
|
||||
/*
|
||||
Initialize variables that are used, modified, and/or set by
|
||||
myisammrg_attach_children_callback().
|
||||
'next_child_attach' traverses the chain of TABLE_LIST objects
|
||||
that has been compiled during myrg_parent_open(). Every call
|
||||
to myisammrg_attach_children_callback() moves the pointer to
|
||||
the next object.
|
||||
'need_compat_check' is set by myisammrg_attach_children_callback()
|
||||
if a child fails the table def version check.
|
||||
'my_errno' is set by myisammrg_attach_children_callback() in
|
||||
case of an error.
|
||||
*/
|
||||
next_child_attach= table->child_l;
|
||||
need_compat_check= FALSE;
|
||||
my_errno= 0;
|
||||
|
||||
if (myrg_attach_children(this->file, this->test_if_locked |
|
||||
current_thd->open_options,
|
||||
myisammrg_attach_children_callback, this))
|
||||
{
|
||||
DBUG_PRINT("error", ("my_errno %d", my_errno));
|
||||
DBUG_RETURN(my_errno ? my_errno : -1);
|
||||
}
|
||||
DBUG_PRINT("myrg", ("calling myrg_extrafunc"));
|
||||
myrg_extrafunc(file, query_cache_invalidate_by_MyISAM_filename_ref);
|
||||
if (!(test_if_locked == HA_OPEN_WAIT_IF_LOCKED ||
|
||||
test_if_locked == HA_OPEN_ABORT_IF_LOCKED))
|
||||
|
@ -116,69 +488,147 @@ int ha_myisammrg::open(const char *name, int mode, uint test_if_locked)
|
|||
if (!(test_if_locked & HA_OPEN_WAIT_IF_LOCKED))
|
||||
myrg_extra(file,HA_EXTRA_WAIT_LOCK,0);
|
||||
|
||||
if (table->s->reclength != stats.mean_rec_length && stats.mean_rec_length)
|
||||
/*
|
||||
The compatibility check is required only if one or more children do
|
||||
not match their table def version from the last check. This will
|
||||
always happen at the first attach because the reference child def
|
||||
version is initialized to 'undefined' at open.
|
||||
*/
|
||||
DBUG_PRINT("myrg", ("need_compat_check: %d", need_compat_check));
|
||||
if (need_compat_check)
|
||||
{
|
||||
DBUG_PRINT("error",("reclength: %lu mean_rec_length: %lu",
|
||||
table->s->reclength, stats.mean_rec_length));
|
||||
if (test_if_locked & HA_OPEN_FOR_REPAIR)
|
||||
myrg_print_wrong_table(file->open_tables->table->filename);
|
||||
error= HA_ERR_WRONG_MRG_TABLE_DEF;
|
||||
goto err;
|
||||
}
|
||||
if ((error= table2myisam(table, &keyinfo, &recinfo, &recs)))
|
||||
{
|
||||
/* purecov: begin inspected */
|
||||
DBUG_PRINT("error", ("Failed to convert TABLE object to MyISAM "
|
||||
"key and column definition"));
|
||||
goto err;
|
||||
/* purecov: end */
|
||||
}
|
||||
for (u_table= file->open_tables; u_table < file->end_table; u_table++)
|
||||
{
|
||||
if (check_definition(keyinfo, recinfo, keys, recs,
|
||||
u_table->table->s->keyinfo, u_table->table->s->rec,
|
||||
u_table->table->s->base.keys,
|
||||
u_table->table->s->base.fields, false))
|
||||
TABLE_LIST *child_l;
|
||||
|
||||
if (table->s->reclength != stats.mean_rec_length && stats.mean_rec_length)
|
||||
{
|
||||
error= HA_ERR_WRONG_MRG_TABLE_DEF;
|
||||
DBUG_PRINT("error",("reclength: %lu mean_rec_length: %lu",
|
||||
table->s->reclength, stats.mean_rec_length));
|
||||
if (test_if_locked & HA_OPEN_FOR_REPAIR)
|
||||
myrg_print_wrong_table(u_table->table->filename);
|
||||
else
|
||||
myrg_print_wrong_table(file->open_tables->table->filename);
|
||||
error= HA_ERR_WRONG_MRG_TABLE_DEF;
|
||||
goto err;
|
||||
}
|
||||
/*
|
||||
Both recinfo and keyinfo are allocated by my_multi_malloc(), thus
|
||||
only recinfo must be freed.
|
||||
*/
|
||||
if ((error= table2myisam(table, &keyinfo, &recinfo, &recs)))
|
||||
{
|
||||
/* purecov: begin inspected */
|
||||
DBUG_PRINT("error", ("failed to convert TABLE object to MyISAM "
|
||||
"key and column definition"));
|
||||
goto err;
|
||||
/* purecov: end */
|
||||
}
|
||||
for (u_table= file->open_tables; u_table < file->end_table; u_table++)
|
||||
{
|
||||
if (check_definition(keyinfo, recinfo, keys, recs,
|
||||
u_table->table->s->keyinfo, u_table->table->s->rec,
|
||||
u_table->table->s->base.keys,
|
||||
u_table->table->s->base.fields, false))
|
||||
{
|
||||
my_free((uchar*) recinfo, MYF(0));
|
||||
goto err;
|
||||
DBUG_PRINT("error", ("table definition mismatch: '%s'",
|
||||
u_table->table->filename));
|
||||
error= HA_ERR_WRONG_MRG_TABLE_DEF;
|
||||
if (!(this->test_if_locked & HA_OPEN_FOR_REPAIR))
|
||||
{
|
||||
my_free((uchar*) recinfo, MYF(0));
|
||||
goto err;
|
||||
}
|
||||
myrg_print_wrong_table(u_table->table->filename);
|
||||
}
|
||||
}
|
||||
my_free((uchar*) recinfo, MYF(0));
|
||||
if (error == HA_ERR_WRONG_MRG_TABLE_DEF)
|
||||
goto err;
|
||||
|
||||
/* All checks passed so far. Now update child def version. */
|
||||
for (child_l= table->child_l; ; child_l= child_l->next_global)
|
||||
{
|
||||
child_l->set_child_def_version(
|
||||
child_l->table->s->get_table_def_version());
|
||||
|
||||
if (&child_l->next_global == table->child_last_l)
|
||||
break;
|
||||
}
|
||||
}
|
||||
my_free((uchar*) recinfo, MYF(0));
|
||||
if (error == HA_ERR_WRONG_MRG_TABLE_DEF)
|
||||
goto err;
|
||||
#if !defined(BIG_TABLES) || SIZEOF_OFF_T == 4
|
||||
/* Merge table has more than 2G rows */
|
||||
if (table->s->crashed)
|
||||
{
|
||||
DBUG_PRINT("error", ("MERGE table marked crashed"));
|
||||
error= HA_ERR_WRONG_MRG_TABLE_DEF;
|
||||
goto err;
|
||||
}
|
||||
#endif
|
||||
return (0);
|
||||
DBUG_RETURN(0);
|
||||
|
||||
err:
|
||||
myrg_close(file);
|
||||
file=0;
|
||||
return (my_errno= error);
|
||||
myrg_detach_children(file);
|
||||
DBUG_RETURN(my_errno= error);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
@brief Detach all children from a MERGE table.
|
||||
|
||||
@note Detach must not touch the children in any way.
|
||||
They may have been closed at ths point already.
|
||||
All references to the children should be removed.
|
||||
|
||||
@return status
|
||||
@retval 0 OK
|
||||
@retval != 0 Error, my_errno gives reason
|
||||
*/
|
||||
|
||||
int ha_myisammrg::detach_children(void)
|
||||
{
|
||||
DBUG_ENTER("ha_myisammrg::detach_children");
|
||||
DBUG_ASSERT(this->file && this->file->children_attached);
|
||||
|
||||
if (myrg_detach_children(this->file))
|
||||
{
|
||||
/* purecov: begin inspected */
|
||||
DBUG_PRINT("error", ("my_errno %d", my_errno));
|
||||
DBUG_RETURN(my_errno ? my_errno : -1);
|
||||
/* purecov: end */
|
||||
}
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
@brief Close a MERGE parent table, not its children.
|
||||
|
||||
@note The children are expected to be closed separately by the caller.
|
||||
|
||||
@return status
|
||||
@retval 0 OK
|
||||
@retval != 0 Error, my_errno gives reason
|
||||
*/
|
||||
|
||||
int ha_myisammrg::close(void)
|
||||
{
|
||||
return myrg_close(file);
|
||||
int rc;
|
||||
DBUG_ENTER("ha_myisammrg::close");
|
||||
/*
|
||||
Children must not be attached here. Unless the MERGE table has no
|
||||
children. In this case children_attached is always true.
|
||||
*/
|
||||
DBUG_ASSERT(!this->file->children_attached || !this->file->tables);
|
||||
rc= myrg_close(file);
|
||||
file= 0;
|
||||
DBUG_RETURN(rc);
|
||||
}
|
||||
|
||||
int ha_myisammrg::write_row(uchar * buf)
|
||||
{
|
||||
DBUG_ENTER("ha_myisammrg::write_row");
|
||||
DBUG_ASSERT(this->file->children_attached);
|
||||
ha_statistic_increment(&SSV::ha_write_count);
|
||||
|
||||
if (file->merge_insert_method == MERGE_INSERT_DISABLED || !file->tables)
|
||||
return (HA_ERR_TABLE_READONLY);
|
||||
DBUG_RETURN(HA_ERR_TABLE_READONLY);
|
||||
|
||||
if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT)
|
||||
table->timestamp_field->set_time();
|
||||
|
@ -186,13 +636,14 @@ int ha_myisammrg::write_row(uchar * buf)
|
|||
{
|
||||
int error;
|
||||
if ((error= update_auto_increment()))
|
||||
return error;
|
||||
DBUG_RETURN(error); /* purecov: inspected */
|
||||
}
|
||||
return myrg_write(file,buf);
|
||||
DBUG_RETURN(myrg_write(file,buf));
|
||||
}
|
||||
|
||||
int ha_myisammrg::update_row(const uchar * old_data, uchar * new_data)
|
||||
{
|
||||
DBUG_ASSERT(this->file->children_attached);
|
||||
ha_statistic_increment(&SSV::ha_update_count);
|
||||
if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE)
|
||||
table->timestamp_field->set_time();
|
||||
|
@ -201,6 +652,7 @@ int ha_myisammrg::update_row(const uchar * old_data, uchar * new_data)
|
|||
|
||||
int ha_myisammrg::delete_row(const uchar * buf)
|
||||
{
|
||||
DBUG_ASSERT(this->file->children_attached);
|
||||
ha_statistic_increment(&SSV::ha_delete_count);
|
||||
return myrg_delete(file,buf);
|
||||
}
|
||||
|
@ -209,6 +661,7 @@ int ha_myisammrg::index_read_map(uchar * buf, const uchar * key,
|
|||
key_part_map keypart_map,
|
||||
enum ha_rkey_function find_flag)
|
||||
{
|
||||
DBUG_ASSERT(this->file->children_attached);
|
||||
ha_statistic_increment(&SSV::ha_read_key_count);
|
||||
int error=myrg_rkey(file,buf,active_index, key, keypart_map, find_flag);
|
||||
table->status=error ? STATUS_NOT_FOUND: 0;
|
||||
|
@ -219,6 +672,7 @@ int ha_myisammrg::index_read_idx_map(uchar * buf, uint index, const uchar * key,
|
|||
key_part_map keypart_map,
|
||||
enum ha_rkey_function find_flag)
|
||||
{
|
||||
DBUG_ASSERT(this->file->children_attached);
|
||||
ha_statistic_increment(&SSV::ha_read_key_count);
|
||||
int error=myrg_rkey(file,buf,index, key, keypart_map, find_flag);
|
||||
table->status=error ? STATUS_NOT_FOUND: 0;
|
||||
|
@ -228,6 +682,7 @@ int ha_myisammrg::index_read_idx_map(uchar * buf, uint index, const uchar * key,
|
|||
int ha_myisammrg::index_read_last_map(uchar *buf, const uchar *key,
|
||||
key_part_map keypart_map)
|
||||
{
|
||||
DBUG_ASSERT(this->file->children_attached);
|
||||
ha_statistic_increment(&SSV::ha_read_key_count);
|
||||
int error=myrg_rkey(file,buf,active_index, key, keypart_map,
|
||||
HA_READ_PREFIX_LAST);
|
||||
|
@ -237,6 +692,7 @@ int ha_myisammrg::index_read_last_map(uchar *buf, const uchar *key,
|
|||
|
||||
int ha_myisammrg::index_next(uchar * buf)
|
||||
{
|
||||
DBUG_ASSERT(this->file->children_attached);
|
||||
ha_statistic_increment(&SSV::ha_read_next_count);
|
||||
int error=myrg_rnext(file,buf,active_index);
|
||||
table->status=error ? STATUS_NOT_FOUND: 0;
|
||||
|
@ -245,6 +701,7 @@ int ha_myisammrg::index_next(uchar * buf)
|
|||
|
||||
int ha_myisammrg::index_prev(uchar * buf)
|
||||
{
|
||||
DBUG_ASSERT(this->file->children_attached);
|
||||
ha_statistic_increment(&SSV::ha_read_prev_count);
|
||||
int error=myrg_rprev(file,buf, active_index);
|
||||
table->status=error ? STATUS_NOT_FOUND: 0;
|
||||
|
@ -253,6 +710,7 @@ int ha_myisammrg::index_prev(uchar * buf)
|
|||
|
||||
int ha_myisammrg::index_first(uchar * buf)
|
||||
{
|
||||
DBUG_ASSERT(this->file->children_attached);
|
||||
ha_statistic_increment(&SSV::ha_read_first_count);
|
||||
int error=myrg_rfirst(file, buf, active_index);
|
||||
table->status=error ? STATUS_NOT_FOUND: 0;
|
||||
|
@ -261,6 +719,7 @@ int ha_myisammrg::index_first(uchar * buf)
|
|||
|
||||
int ha_myisammrg::index_last(uchar * buf)
|
||||
{
|
||||
DBUG_ASSERT(this->file->children_attached);
|
||||
ha_statistic_increment(&SSV::ha_read_last_count);
|
||||
int error=myrg_rlast(file, buf, active_index);
|
||||
table->status=error ? STATUS_NOT_FOUND: 0;
|
||||
|
@ -271,6 +730,7 @@ int ha_myisammrg::index_next_same(uchar * buf,
|
|||
const uchar *key __attribute__((unused)),
|
||||
uint length __attribute__((unused)))
|
||||
{
|
||||
DBUG_ASSERT(this->file->children_attached);
|
||||
ha_statistic_increment(&SSV::ha_read_next_count);
|
||||
int error=myrg_rnext_same(file,buf);
|
||||
table->status=error ? STATUS_NOT_FOUND: 0;
|
||||
|
@ -280,12 +740,14 @@ int ha_myisammrg::index_next_same(uchar * buf,
|
|||
|
||||
int ha_myisammrg::rnd_init(bool scan)
|
||||
{
|
||||
DBUG_ASSERT(this->file->children_attached);
|
||||
return myrg_reset(file);
|
||||
}
|
||||
|
||||
|
||||
int ha_myisammrg::rnd_next(uchar *buf)
|
||||
{
|
||||
DBUG_ASSERT(this->file->children_attached);
|
||||
ha_statistic_increment(&SSV::ha_read_rnd_next_count);
|
||||
int error=myrg_rrnd(file, buf, HA_OFFSET_ERROR);
|
||||
table->status=error ? STATUS_NOT_FOUND: 0;
|
||||
|
@ -295,6 +757,7 @@ int ha_myisammrg::rnd_next(uchar *buf)
|
|||
|
||||
int ha_myisammrg::rnd_pos(uchar * buf, uchar *pos)
|
||||
{
|
||||
DBUG_ASSERT(this->file->children_attached);
|
||||
ha_statistic_increment(&SSV::ha_read_rnd_count);
|
||||
int error=myrg_rrnd(file, buf, my_get_ptr(pos,ref_length));
|
||||
table->status=error ? STATUS_NOT_FOUND: 0;
|
||||
|
@ -303,6 +766,7 @@ int ha_myisammrg::rnd_pos(uchar * buf, uchar *pos)
|
|||
|
||||
void ha_myisammrg::position(const uchar *record)
|
||||
{
|
||||
DBUG_ASSERT(this->file->children_attached);
|
||||
ulonglong row_position= myrg_position(file);
|
||||
my_store_ptr(ref, ref_length, (my_off_t) row_position);
|
||||
}
|
||||
|
@ -311,6 +775,7 @@ void ha_myisammrg::position(const uchar *record)
|
|||
ha_rows ha_myisammrg::records_in_range(uint inx, key_range *min_key,
|
||||
key_range *max_key)
|
||||
{
|
||||
DBUG_ASSERT(this->file->children_attached);
|
||||
return (ha_rows) myrg_records_in_range(file, (int) inx, min_key, max_key);
|
||||
}
|
||||
|
||||
|
@ -318,6 +783,7 @@ ha_rows ha_myisammrg::records_in_range(uint inx, key_range *min_key,
|
|||
int ha_myisammrg::info(uint flag)
|
||||
{
|
||||
MYMERGE_INFO mrg_info;
|
||||
DBUG_ASSERT(this->file->children_attached);
|
||||
(void) myrg_status(file,&mrg_info,flag);
|
||||
/*
|
||||
The following fails if one has not compiled MySQL with -DBIG_TABLES
|
||||
|
@ -387,6 +853,23 @@ int ha_myisammrg::info(uint flag)
|
|||
|
||||
int ha_myisammrg::extra(enum ha_extra_function operation)
|
||||
{
|
||||
if (operation == HA_EXTRA_ATTACH_CHILDREN)
|
||||
{
|
||||
int rc= attach_children();
|
||||
if (!rc)
|
||||
(void) extra(HA_EXTRA_NO_READCHECK); // Not needed in SQL
|
||||
return(rc);
|
||||
}
|
||||
else if (operation == HA_EXTRA_DETACH_CHILDREN)
|
||||
{
|
||||
/*
|
||||
Note that detach must not touch the children in any way.
|
||||
They may have been closed at ths point already.
|
||||
*/
|
||||
int rc= detach_children();
|
||||
return(rc);
|
||||
}
|
||||
|
||||
/* As this is just a mapping, we don't have to force the underlying
|
||||
tables to be closed */
|
||||
if (operation == HA_EXTRA_FORCE_REOPEN ||
|
||||
|
@ -404,6 +887,7 @@ int ha_myisammrg::reset(void)
|
|||
|
||||
int ha_myisammrg::extra_opt(enum ha_extra_function operation, ulong cache_size)
|
||||
{
|
||||
DBUG_ASSERT(this->file->children_attached);
|
||||
if ((specialflag & SPECIAL_SAFE_MODE) && operation == HA_EXTRA_WRITE_CACHE)
|
||||
return 0;
|
||||
return myrg_extra(file, operation, (void*) &cache_size);
|
||||
|
@ -411,11 +895,13 @@ int ha_myisammrg::extra_opt(enum ha_extra_function operation, ulong cache_size)
|
|||
|
||||
int ha_myisammrg::external_lock(THD *thd, int lock_type)
|
||||
{
|
||||
DBUG_ASSERT(this->file->children_attached);
|
||||
return myrg_lock_database(file,lock_type);
|
||||
}
|
||||
|
||||
uint ha_myisammrg::lock_count(void) const
|
||||
{
|
||||
DBUG_ASSERT(this->file->children_attached);
|
||||
return file->tables;
|
||||
}
|
||||
|
||||
|
@ -425,6 +911,7 @@ THR_LOCK_DATA **ha_myisammrg::store_lock(THD *thd,
|
|||
enum thr_lock_type lock_type)
|
||||
{
|
||||
MYRG_TABLE *open_table;
|
||||
DBUG_ASSERT(this->file->children_attached);
|
||||
|
||||
for (open_table=file->open_tables ;
|
||||
open_table != file->end_table ;
|
||||
|
@ -519,47 +1006,50 @@ int ha_myisammrg::create(const char *name, register TABLE *form,
|
|||
uint dirlgt= dirname_length(name);
|
||||
DBUG_ENTER("ha_myisammrg::create");
|
||||
|
||||
/* Allocate a table_names array in thread mem_root. */
|
||||
if (!(table_names= (const char**)
|
||||
thd->alloc((create_info->merge_list.elements+1) * sizeof(char*))))
|
||||
DBUG_RETURN(HA_ERR_OUT_OF_MEM);
|
||||
|
||||
/* Create child path names. */
|
||||
for (pos= table_names; tables; tables= tables->next_local)
|
||||
{
|
||||
const char *table_name;
|
||||
TABLE *tbl= 0;
|
||||
if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
|
||||
tbl= find_temporary_table(thd, tables);
|
||||
if (!tbl)
|
||||
{
|
||||
/*
|
||||
Construct the path to the MyISAM table. Try to meet two conditions:
|
||||
1.) Allow to include MyISAM tables from different databases, and
|
||||
2.) allow for moving DATADIR around in the file system.
|
||||
The first means that we need paths in the .MRG file. The second
|
||||
means that we should not have absolute paths in the .MRG file.
|
||||
The best, we can do, is to use 'mysql_data_home', which is '.'
|
||||
in mysqld and may be an absolute path in an embedded server.
|
||||
This means that it might not be possible to move the DATADIR of
|
||||
an embedded server without changing the paths in the .MRG file.
|
||||
*/
|
||||
uint length= build_table_filename(buff, sizeof(buff),
|
||||
tables->db, tables->table_name, "", 0);
|
||||
/*
|
||||
If a MyISAM table is in the same directory as the MERGE table,
|
||||
we use the table name without a path. This means that the
|
||||
DATADIR can easily be moved even for an embedded server as long
|
||||
as the MyISAM tables are from the same database as the MERGE table.
|
||||
*/
|
||||
if ((dirname_length(buff) == dirlgt) && ! memcmp(buff, name, dirlgt))
|
||||
table_name= tables->table_name;
|
||||
else
|
||||
if (! (table_name= thd->strmake(buff, length)))
|
||||
DBUG_RETURN(HA_ERR_OUT_OF_MEM);
|
||||
}
|
||||
|
||||
/*
|
||||
Construct the path to the MyISAM table. Try to meet two conditions:
|
||||
1.) Allow to include MyISAM tables from different databases, and
|
||||
2.) allow for moving DATADIR around in the file system.
|
||||
The first means that we need paths in the .MRG file. The second
|
||||
means that we should not have absolute paths in the .MRG file.
|
||||
The best, we can do, is to use 'mysql_data_home', which is '.'
|
||||
in mysqld and may be an absolute path in an embedded server.
|
||||
This means that it might not be possible to move the DATADIR of
|
||||
an embedded server without changing the paths in the .MRG file.
|
||||
|
||||
Do the same even for temporary tables. MERGE children are now
|
||||
opened through the table cache. They are opened by db.table_name,
|
||||
not by their path name.
|
||||
*/
|
||||
uint length= build_table_filename(buff, sizeof(buff),
|
||||
tables->db, tables->table_name, "", 0);
|
||||
/*
|
||||
If a MyISAM table is in the same directory as the MERGE table,
|
||||
we use the table name without a path. This means that the
|
||||
DATADIR can easily be moved even for an embedded server as long
|
||||
as the MyISAM tables are from the same database as the MERGE table.
|
||||
*/
|
||||
if ((dirname_length(buff) == dirlgt) && ! memcmp(buff, name, dirlgt))
|
||||
table_name= tables->table_name;
|
||||
else
|
||||
table_name= tbl->s->path.str;
|
||||
if (! (table_name= thd->strmake(buff, length)))
|
||||
DBUG_RETURN(HA_ERR_OUT_OF_MEM); /* purecov: inspected */
|
||||
|
||||
*pos++= table_name;
|
||||
}
|
||||
*pos=0;
|
||||
|
||||
/* Create a MERGE meta file from the table_names array. */
|
||||
DBUG_RETURN(myrg_create(fn_format(buff,name,"","",
|
||||
MY_RESOLVE_SYMLINKS|
|
||||
MY_UNPACK_FILENAME|MY_APPEND_EXT),
|
||||
|
@ -642,7 +1132,7 @@ static int myisammrg_init(void *p)
|
|||
myisammrg_hton->db_type= DB_TYPE_MRG_MYISAM;
|
||||
myisammrg_hton->create= myisammrg_create_handler;
|
||||
myisammrg_hton->panic= myisammrg_panic;
|
||||
myisammrg_hton->flags= HTON_CAN_RECREATE|HTON_NO_PARTITION;
|
||||
myisammrg_hton->flags= HTON_NO_PARTITION;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -27,8 +27,12 @@ class ha_myisammrg: public handler
|
|||
MYRG_INFO *file;
|
||||
|
||||
public:
|
||||
TABLE_LIST *next_child_attach; /* next child to attach */
|
||||
uint test_if_locked; /* flags from ::open() */
|
||||
bool need_compat_check; /* if need compatibility check */
|
||||
|
||||
ha_myisammrg(handlerton *hton, TABLE_SHARE *table_arg);
|
||||
~ha_myisammrg() {}
|
||||
~ha_myisammrg();
|
||||
const char *table_type() const { return "MRG_MyISAM"; }
|
||||
const char **bas_ext() const;
|
||||
const char *index_type(uint key_number);
|
||||
|
@ -53,6 +57,8 @@ class ha_myisammrg: public handler
|
|||
{ return ulonglong2double(stats.data_file_length) / IO_SIZE + file->tables; }
|
||||
|
||||
int open(const char *name, int mode, uint test_if_locked);
|
||||
int attach_children(void);
|
||||
int detach_children(void);
|
||||
int close(void);
|
||||
int write_row(uchar * buf);
|
||||
int update_row(const uchar * old_data, uchar * new_data);
|
||||
|
@ -85,6 +91,7 @@ class ha_myisammrg: public handler
|
|||
void update_create_info(HA_CREATE_INFO *create_info);
|
||||
void append_create_info(String *packet);
|
||||
MYRG_INFO *myrg_info() { return file; }
|
||||
TABLE *table_ptr() { return table; }
|
||||
bool check_if_incompatible_data(HA_CREATE_INFO *info, uint table_changes);
|
||||
int check(THD* thd, HA_CHECK_OPT* check_opt);
|
||||
};
|
||||
|
|
|
@ -23,9 +23,37 @@ int myrg_close(MYRG_INFO *info)
|
|||
MYRG_TABLE *file;
|
||||
DBUG_ENTER("myrg_close");
|
||||
|
||||
for (file=info->open_tables ; file != info->end_table ; file++)
|
||||
if ((new_error=mi_close(file->table)))
|
||||
error=new_error;
|
||||
/*
|
||||
Assume that info->children_attached means that this is called from
|
||||
direct use of MERGE, not from a MySQL server. In this case the
|
||||
children must be closed and info->rec_per_key_part is part of the
|
||||
'info' multi_alloc.
|
||||
If info->children_attached is false, this is called from a MySQL
|
||||
server. Children are closed independently but info->rec_per_key_part
|
||||
must be freed.
|
||||
Just in case of a server panic (myrg_panic()) info->children_attached
|
||||
might be true. We would close the children though they should be
|
||||
closed independently and info->rec_per_key_part is not freed.
|
||||
This should be acceptable for a panic.
|
||||
In case of a MySQL server and no children, children_attached is
|
||||
always true. In this case no rec_per_key_part has been allocated.
|
||||
So it is correct to use the branch where an empty list of tables is
|
||||
(not) closed.
|
||||
*/
|
||||
if (info->children_attached)
|
||||
{
|
||||
for (file= info->open_tables; file != info->end_table; file++)
|
||||
{
|
||||
/* purecov: begin inspected */
|
||||
if ((new_error= mi_close(file->table)))
|
||||
error= new_error;
|
||||
else
|
||||
file->table= NULL;
|
||||
/* purecov: end */
|
||||
}
|
||||
}
|
||||
else
|
||||
my_free((uchar*) info->rec_per_key_part, MYF(MY_ALLOW_ZERO_PTR));
|
||||
delete_queue(&info->by_key);
|
||||
pthread_mutex_lock(&THR_LOCK_open);
|
||||
myrg_open_list=list_delete(myrg_open_list,&info->open_list);
|
||||
|
|
|
@ -29,6 +29,8 @@ int myrg_extra(MYRG_INFO *info,enum ha_extra_function function,
|
|||
DBUG_ENTER("myrg_extra");
|
||||
DBUG_PRINT("info",("function: %lu", (ulong) function));
|
||||
|
||||
if (!info->children_attached)
|
||||
DBUG_RETURN(1);
|
||||
if (function == HA_EXTRA_CACHE)
|
||||
{
|
||||
info->cache_in_use=1;
|
||||
|
@ -73,6 +75,8 @@ int myrg_reset(MYRG_INFO *info)
|
|||
MYRG_TABLE *file;
|
||||
DBUG_ENTER("myrg_reset");
|
||||
|
||||
if (!info->children_attached)
|
||||
DBUG_RETURN(1);
|
||||
info->cache_in_use=0;
|
||||
info->current_table=0;
|
||||
info->last_used_table= info->open_tables;
|
||||
|
|
|
@ -26,8 +26,14 @@
|
|||
open a MyISAM MERGE table
|
||||
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
|
||||
duplication between myrg_open() and myrg_parent_open() +
|
||||
myrg_attach_children(). Please duplicate changes in these
|
||||
functions or make common sub-functions.
|
||||
*/
|
||||
/* purecov: begin unused */
|
||||
|
||||
MYRG_INFO *myrg_open(const char *name, int mode, int handle_locking)
|
||||
{
|
||||
|
@ -107,13 +113,11 @@ MYRG_INFO *myrg_open(const char *name, int mode, int handle_locking)
|
|||
key_parts*sizeof(long),
|
||||
MYF(MY_WME|MY_ZEROFILL))))
|
||||
goto err;
|
||||
if (files)
|
||||
{
|
||||
m_info->open_tables=(MYRG_TABLE *) (m_info+1);
|
||||
m_info->rec_per_key_part=(ulong *) (m_info->open_tables+files);
|
||||
m_info->tables= files;
|
||||
files= 0;
|
||||
}
|
||||
DBUG_ASSERT(files);
|
||||
m_info->open_tables=(MYRG_TABLE *) (m_info+1);
|
||||
m_info->rec_per_key_part=(ulong *) (m_info->open_tables+files);
|
||||
m_info->tables= files;
|
||||
files= 0;
|
||||
m_info->reclength=isam->s->base.reclength;
|
||||
min_keys= isam->s->base.keys;
|
||||
errpos=3;
|
||||
|
@ -163,6 +167,7 @@ MYRG_INFO *myrg_open(const char *name, int mode, int handle_locking)
|
|||
/* this works ok if the table list is empty */
|
||||
m_info->end_table=m_info->open_tables+files;
|
||||
m_info->last_used_table=m_info->open_tables;
|
||||
m_info->children_attached= TRUE;
|
||||
|
||||
VOID(my_close(fd,MYF(0)));
|
||||
end_io_cache(&file);
|
||||
|
@ -189,3 +194,316 @@ err:
|
|||
my_errno=save_errno;
|
||||
DBUG_RETURN (NULL);
|
||||
}
|
||||
/* purecov: end */
|
||||
|
||||
|
||||
/**
|
||||
@brief Open parent table of a MyISAM MERGE table.
|
||||
|
||||
@detail Open MERGE meta file to get the table name paths for the child
|
||||
tables. Count the children. Allocate and initialize MYRG_INFO
|
||||
structure. Call a callback function for each child table.
|
||||
|
||||
@param[in] parent_name merge table name path as "database/table"
|
||||
@param[in] callback function to call for each child table
|
||||
@param[in] callback_param data pointer to give to the callback
|
||||
|
||||
@return MYRG_INFO pointer
|
||||
@retval != NULL OK
|
||||
@retval NULL Error
|
||||
|
||||
@note: 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.
|
||||
*/
|
||||
|
||||
MYRG_INFO *myrg_parent_open(const char *parent_name,
|
||||
int (*callback)(void*, const char*),
|
||||
void *callback_param)
|
||||
{
|
||||
MYRG_INFO *m_info;
|
||||
int rc;
|
||||
int errpos;
|
||||
int save_errno;
|
||||
int insert_method;
|
||||
uint length;
|
||||
uint dir_length;
|
||||
uint child_count;
|
||||
size_t name_buff_length;
|
||||
File fd;
|
||||
IO_CACHE file_cache;
|
||||
char parent_name_buff[FN_REFLEN * 2];
|
||||
char child_name_buff[FN_REFLEN];
|
||||
DBUG_ENTER("myrg_parent_open");
|
||||
|
||||
rc= 1;
|
||||
errpos= 0;
|
||||
bzero((char*) &file_cache, sizeof(file_cache));
|
||||
|
||||
/* Open MERGE meta file. */
|
||||
if ((fd= my_open(fn_format(parent_name_buff, parent_name, "", MYRG_NAME_EXT,
|
||||
MY_UNPACK_FILENAME|MY_APPEND_EXT),
|
||||
O_RDONLY | O_SHARE, MYF(0))) < 0)
|
||||
goto err; /* purecov: inspected */
|
||||
errpos= 1;
|
||||
|
||||
if (init_io_cache(&file_cache, fd, 4 * IO_SIZE, READ_CACHE, 0, 0,
|
||||
MYF(MY_WME | MY_NABP)))
|
||||
goto err; /* purecov: inspected */
|
||||
errpos= 2;
|
||||
|
||||
/* Count children. Determine insert method. */
|
||||
child_count= 0;
|
||||
insert_method= 0;
|
||||
while ((length= my_b_gets(&file_cache, child_name_buff, FN_REFLEN - 1)))
|
||||
{
|
||||
/* Remove line terminator. */
|
||||
if (child_name_buff[length - 1] == '\n')
|
||||
child_name_buff[--length]= '\0';
|
||||
|
||||
/* Skip empty lines. */
|
||||
if (!child_name_buff[0])
|
||||
continue; /* purecov: inspected */
|
||||
|
||||
/* Skip comments, but evaluate insert method. */
|
||||
if (child_name_buff[0] == '#')
|
||||
{
|
||||
if (!strncmp(child_name_buff + 1, "INSERT_METHOD=", 14))
|
||||
{
|
||||
/* Compare buffer with global methods list: merge_insert_method. */
|
||||
insert_method= find_type(child_name_buff + 15,
|
||||
&merge_insert_method, 2);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Count the child. */
|
||||
child_count++;
|
||||
}
|
||||
|
||||
/* Allocate MERGE parent table structure. */
|
||||
if (!(m_info= (MYRG_INFO*) my_malloc(sizeof(MYRG_INFO) +
|
||||
child_count * sizeof(MYRG_TABLE),
|
||||
MYF(MY_WME | MY_ZEROFILL))))
|
||||
goto err; /* purecov: inspected */
|
||||
errpos= 3;
|
||||
m_info->open_tables= (MYRG_TABLE*) (m_info + 1);
|
||||
m_info->tables= child_count;
|
||||
m_info->merge_insert_method= insert_method > 0 ? insert_method : 0;
|
||||
/* This works even if the table list is empty. */
|
||||
m_info->end_table= m_info->open_tables + child_count;
|
||||
if (!child_count)
|
||||
{
|
||||
/* Do not attach/detach an empty child list. */
|
||||
m_info->children_attached= TRUE;
|
||||
}
|
||||
|
||||
/* Call callback for each child. */
|
||||
dir_length= dirname_part(parent_name_buff, parent_name, &name_buff_length);
|
||||
my_b_seek(&file_cache, 0);
|
||||
while ((length= my_b_gets(&file_cache, child_name_buff, FN_REFLEN - 1)))
|
||||
{
|
||||
/* Remove line terminator. */
|
||||
if (child_name_buff[length - 1] == '\n')
|
||||
child_name_buff[--length]= '\0';
|
||||
|
||||
/* Skip empty lines and comments. */
|
||||
if (!child_name_buff[0] || (child_name_buff[0] == '#'))
|
||||
continue;
|
||||
|
||||
if (!has_path(child_name_buff))
|
||||
{
|
||||
VOID(strmake(parent_name_buff + dir_length, child_name_buff,
|
||||
sizeof(parent_name_buff) - 1 - dir_length));
|
||||
VOID(cleanup_dirname(child_name_buff, parent_name_buff));
|
||||
}
|
||||
else
|
||||
fn_format(child_name_buff, child_name_buff, "", "", 0);
|
||||
DBUG_PRINT("info", ("child: '%s'", child_name_buff));
|
||||
|
||||
/* Callback registers child with handler table. */
|
||||
if ((rc= (*callback)(callback_param, child_name_buff)))
|
||||
goto err; /* purecov: inspected */
|
||||
}
|
||||
|
||||
end_io_cache(&file_cache);
|
||||
VOID(my_close(fd, MYF(0)));
|
||||
|
||||
m_info->open_list.data= (void*) m_info;
|
||||
pthread_mutex_lock(&THR_LOCK_open);
|
||||
myrg_open_list= list_add(myrg_open_list, &m_info->open_list);
|
||||
pthread_mutex_unlock(&THR_LOCK_open);
|
||||
|
||||
DBUG_RETURN(m_info);
|
||||
|
||||
/* purecov: begin inspected */
|
||||
err:
|
||||
save_errno= my_errno;
|
||||
switch (errpos) {
|
||||
case 3:
|
||||
my_free((char*) m_info, MYF(0));
|
||||
/* Fall through */
|
||||
case 2:
|
||||
end_io_cache(&file_cache);
|
||||
/* Fall through */
|
||||
case 1:
|
||||
VOID(my_close(fd, MYF(0)));
|
||||
}
|
||||
my_errno= save_errno;
|
||||
DBUG_RETURN (NULL);
|
||||
/* purecov: end */
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
@brief Attach children to a MyISAM MERGE parent table.
|
||||
|
||||
@detail Call a callback function for each child table.
|
||||
The callback returns the MyISAM table handle of the child table.
|
||||
Check table definition match.
|
||||
|
||||
@param[in] m_info MERGE parent table structure
|
||||
@param[in] handle_locking if contains HA_OPEN_FOR_REPAIR, warn about
|
||||
incompatible child tables, but continue
|
||||
@param[in] callback function to call for each child table
|
||||
@param[in] callback_param data pointer to give to the callback
|
||||
|
||||
@return status
|
||||
@retval 0 OK
|
||||
@retval != 0 Error
|
||||
|
||||
@note: 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.
|
||||
*/
|
||||
|
||||
int myrg_attach_children(MYRG_INFO *m_info, int handle_locking,
|
||||
MI_INFO *(*callback)(void*),
|
||||
void *callback_param)
|
||||
{
|
||||
ulonglong file_offset;
|
||||
MI_INFO *myisam;
|
||||
int rc;
|
||||
int errpos;
|
||||
int save_errno;
|
||||
uint idx;
|
||||
uint child_nr;
|
||||
uint key_parts;
|
||||
uint min_keys;
|
||||
DBUG_ENTER("myrg_attach_children");
|
||||
DBUG_PRINT("myrg", ("handle_locking: %d", handle_locking));
|
||||
|
||||
rc= 1;
|
||||
errpos= 0;
|
||||
file_offset= 0;
|
||||
LINT_INIT(key_parts);
|
||||
min_keys= 0;
|
||||
child_nr= 0;
|
||||
while ((myisam= (*callback)(callback_param)))
|
||||
{
|
||||
DBUG_PRINT("myrg", ("child_nr: %u table: '%s'",
|
||||
child_nr, myisam->filename));
|
||||
DBUG_ASSERT(child_nr < m_info->tables);
|
||||
|
||||
/* Special handling when the first child is attached. */
|
||||
if (!child_nr)
|
||||
{
|
||||
m_info->reclength= myisam->s->base.reclength;
|
||||
min_keys= myisam->s->base.keys;
|
||||
key_parts= myisam->s->base.key_parts;
|
||||
if (!m_info->rec_per_key_part)
|
||||
{
|
||||
if(!(m_info->rec_per_key_part= (ulong*)
|
||||
my_malloc(key_parts * sizeof(long), MYF(MY_WME|MY_ZEROFILL))))
|
||||
goto err; /* purecov: inspected */
|
||||
errpos= 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Add MyISAM table info. */
|
||||
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;
|
||||
|
||||
/* Check table definition match. */
|
||||
if (m_info->reclength != myisam->s->base.reclength)
|
||||
{
|
||||
DBUG_PRINT("error", ("definition mismatch table: '%s' repair: %d",
|
||||
myisam->filename,
|
||||
(handle_locking & HA_OPEN_FOR_REPAIR)));
|
||||
my_errno= HA_ERR_WRONG_MRG_TABLE_DEF;
|
||||
if (handle_locking & HA_OPEN_FOR_REPAIR)
|
||||
{
|
||||
myrg_print_wrong_table(myisam->filename);
|
||||
continue;
|
||||
}
|
||||
goto err;
|
||||
}
|
||||
|
||||
m_info->options|= myisam->s->options;
|
||||
m_info->records+= myisam->state->records;
|
||||
m_info->del+= myisam->state->del;
|
||||
m_info->data_file_length+= myisam->state->data_file_length;
|
||||
if (min_keys > myisam->s->base.keys)
|
||||
min_keys= myisam->s->base.keys; /* purecov: inspected */
|
||||
for (idx= 0; idx < key_parts; idx++)
|
||||
m_info->rec_per_key_part[idx]+= (myisam->s->state.rec_per_key_part[idx] /
|
||||
m_info->tables);
|
||||
child_nr++;
|
||||
}
|
||||
|
||||
if (my_errno == HA_ERR_WRONG_MRG_TABLE_DEF)
|
||||
goto err;
|
||||
if (sizeof(my_off_t) == 4 && file_offset > (ulonglong) (ulong) ~0L)
|
||||
{
|
||||
my_errno= HA_ERR_RECORD_FILE_FULL;
|
||||
goto err;
|
||||
}
|
||||
/* Don't mark table readonly, for ALTER TABLE ... UNION=(...) to work */
|
||||
m_info->options&= ~(HA_OPTION_COMPRESS_RECORD | HA_OPTION_READ_ONLY_DATA);
|
||||
m_info->keys= min_keys;
|
||||
m_info->last_used_table= m_info->open_tables;
|
||||
m_info->children_attached= TRUE;
|
||||
DBUG_RETURN(0);
|
||||
|
||||
err:
|
||||
save_errno= my_errno;
|
||||
switch (errpos) {
|
||||
case 1:
|
||||
my_free((char*) m_info->rec_per_key_part, MYF(0));
|
||||
m_info->rec_per_key_part= NULL;
|
||||
}
|
||||
my_errno= save_errno;
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
@brief Detach children from a MyISAM MERGE parent table.
|
||||
|
||||
@param[in] m_info MERGE parent table structure
|
||||
|
||||
@note Detach must not touch the children in any way.
|
||||
They may have been closed at ths point already.
|
||||
All references to the children should be removed.
|
||||
|
||||
@return status
|
||||
@retval 0 OK
|
||||
*/
|
||||
|
||||
int myrg_detach_children(MYRG_INFO *m_info)
|
||||
{
|
||||
DBUG_ENTER("myrg_detach_children");
|
||||
if (m_info->tables)
|
||||
{
|
||||
/* Do not attach/detach an empty child list. */
|
||||
m_info->children_attached= FALSE;
|
||||
bzero((char*) m_info->open_tables, m_info->tables * sizeof(MYRG_TABLE));
|
||||
}
|
||||
m_info->records= 0;
|
||||
m_info->del= 0;
|
||||
m_info->data_file_length= 0;
|
||||
m_info->options= 0;
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue