Merge from mysql-5.5-runtime to mysql-5.5-bugteam
No conflicts
This commit is contained in:
commit
e486d21dfc
@ -26,7 +26,6 @@ rpl.rpl_heartbeat_2slaves # BUG#43828 2009-10-22 luis fails spora
|
||||
rpl.rpl_innodb_bug28430* # Bug#46029
|
||||
rpl.rpl_innodb_bug30888* @solaris # Bug#47646 2009-09-25 alik rpl.rpl_innodb_bug30888 fails sporadically on Solaris
|
||||
rpl.rpl_killed_ddl @windows # Bug#47638 2010-01-20 alik The rpl_killed_ddl test fails on Windows
|
||||
rpl.rpl_row_sp011* @solaris # Bug#47791 2010-01-20 alik Several test cases fail on Solaris with error Thread stack overrun
|
||||
|
||||
sys_vars.max_sp_recursion_depth_func @solaris # Bug#47791 2010-01-20 alik Several test cases fail on Solaris with error Thread stack overrun
|
||||
sys_vars.slow_query_log_func @solaris # Bug#54819 2010-06-26 alik sys_vars.slow_query_log_func fails sporadically on Solaris 10
|
||||
|
@ -220,3 +220,14 @@ Variable_name Value
|
||||
Qcache_hits 1
|
||||
set GLOBAL query_cache_size=1048576;
|
||||
drop table t2;
|
||||
CREATE TABLE t1 (a INT) ENGINE=InnoDB;
|
||||
BEGIN;
|
||||
INSERT INTO t1 VALUES(1);
|
||||
ROLLBACK WORK AND CHAIN NO RELEASE;
|
||||
SELECT a FROM t1;
|
||||
a
|
||||
ROLLBACK WORK AND CHAIN NO RELEASE;
|
||||
SELECT a FROM t1;
|
||||
a
|
||||
ROLLBACK;
|
||||
DROP TABLE t1;
|
||||
|
@ -509,3 +509,18 @@ CREATE TABLE t3 LIKE t1;
|
||||
DELETE FROM t1.*, test.t2.*, a.* USING t1, t2, t3 AS a;
|
||||
DROP TABLE t1, t2, t3;
|
||||
End of 5.1 tests
|
||||
#
|
||||
# Bug#51099 Assertion in mysql_multi_delete_prepare()
|
||||
#
|
||||
DROP TABLE IF EXISTS t1, t2;
|
||||
DROP VIEW IF EXISTS v1, v2;
|
||||
CREATE TABLE t1(a INT);
|
||||
CREATE TABLE t2(b INT);
|
||||
CREATE VIEW v1 AS SELECT a, b FROM t1, t2;
|
||||
CREATE VIEW v2 AS SELECT a FROM v1;
|
||||
DELETE FROM v2;
|
||||
ERROR HY000: Can not delete from join view 'test.v2'
|
||||
DELETE v2 FROM v2;
|
||||
ERROR HY000: Can not delete from join view 'test.v2'
|
||||
DROP VIEW v2, v1;
|
||||
DROP TABLE t1, t2;
|
||||
|
@ -2632,7 +2632,8 @@ DROP TABLE IF EXISTS t1;
|
||||
CREATE TABLE t1 (a INT) ENGINE=InnoDB;
|
||||
INSERT INTO t1 VALUES (1),(2),(3);
|
||||
# Connection: con1
|
||||
SET debug_sync='lock_table_for_truncate SIGNAL parked_truncate WAIT_FOR go_truncate';
|
||||
LOCK TABLES t1 WRITE;
|
||||
SET debug_sync='upgrade_lock_for_truncate SIGNAL parked_truncate WAIT_FOR go_truncate';
|
||||
TRUNCATE TABLE t1;
|
||||
# Connection: default
|
||||
SET debug_sync='now WAIT_FOR parked_truncate';
|
||||
@ -2647,10 +2648,11 @@ FLUSH TABLES t1;
|
||||
# Connection: default
|
||||
SET debug_sync='now WAIT_FOR parked_flush';
|
||||
SET debug_sync='now SIGNAL go_truncate';
|
||||
# Connection: con1
|
||||
# Reaping...
|
||||
# Connection: default
|
||||
# Ensure that truncate waits for a exclusive lock
|
||||
SET debug_sync= 'now SIGNAL go_show';
|
||||
# Connection: con1 (TRUNCATE)
|
||||
# Reaping...
|
||||
UNLOCK TABLES;
|
||||
# Connection: con2 (SHOW FIELDS FROM t1)
|
||||
# Reaping...
|
||||
Field Type Null Key Default Extra
|
||||
|
@ -3486,12 +3486,13 @@ ALTER TABLE m1 ADD INDEX (c1);
|
||||
UNLOCK TABLES;
|
||||
DROP TABLE m1, t1;
|
||||
#
|
||||
# Locking the merge table will implicitly lock children.
|
||||
# Locking the merge table won't implicitly lock children.
|
||||
#
|
||||
CREATE TABLE t1 (c1 INT);
|
||||
CREATE TABLE m1 (c1 INT) ENGINE=MRG_MyISAM UNION=(t1);
|
||||
LOCK TABLE m1 WRITE;
|
||||
ALTER TABLE t1 ADD INDEX (c1);
|
||||
ERROR HY000: Table 't1' was locked with a READ lock and can't be updated
|
||||
LOCK TABLE m1 WRITE, t1 WRITE;
|
||||
ALTER TABLE t1 ADD INDEX (c1);
|
||||
UNLOCK TABLES;
|
||||
@ -3661,4 +3662,16 @@ REPAIR TABLE t2 USE_FRM;
|
||||
Table Op Msg_type Msg_text
|
||||
test.t2 repair note The storage engine for the table doesn't support repair
|
||||
DROP TABLE t1, t2;
|
||||
#
|
||||
# Bug#57002 Assert in upgrade_shared_lock_to_exclusive()
|
||||
# for ALTER TABLE + MERGE tables
|
||||
#
|
||||
DROP TABLE IF EXISTS t1, m1;
|
||||
CREATE TABLE t1(a INT) engine=myisam;
|
||||
CREATE TABLE m1(a INT) engine=merge UNION(t1);
|
||||
LOCK TABLES t1 READ, m1 WRITE;
|
||||
ALTER TABLE t1 engine=myisam;
|
||||
ERROR HY000: Table 't1' was locked with a READ lock and can't be updated
|
||||
UNLOCK TABLES;
|
||||
DROP TABLE m1, t1;
|
||||
End of 6.0 tests
|
||||
|
@ -585,3 +585,20 @@ UPDATE t1_aux SET f2 = 2 WHERE f1 = f1_two_inserts()|
|
||||
ERROR 23000: Column 'f2' cannot be null
|
||||
DROP TABLE t1_aux, t1_not_null|
|
||||
DROP FUNCTION f1_two_inserts|
|
||||
#
|
||||
# Bug#49938: Failing assertion: inode or deadlock in fsp/fsp0fsp.c
|
||||
#
|
||||
DROP PROCEDURE IF EXISTS p1|
|
||||
DROP TABLE IF EXISTS t1|
|
||||
CREATE TABLE t1 (a INT) ENGINE=INNODB|
|
||||
CREATE PROCEDURE p1()
|
||||
BEGIN
|
||||
TRUNCATE TABLE t1;
|
||||
END|
|
||||
LOCK TABLES t1 WRITE|
|
||||
CALL p1()|
|
||||
FLUSH TABLES;
|
||||
UNLOCK TABLES|
|
||||
CALL p1()|
|
||||
DROP PROCEDURE p1|
|
||||
DROP TABLE t1|
|
||||
|
@ -151,9 +151,14 @@ CREATE TRIGGER t1_ad AFTER DELETE ON t1 FOR EACH ROW SET @b = 1;
|
||||
SET @a = 0;
|
||||
SET @b = 0;
|
||||
TRUNCATE t1;
|
||||
ERROR 42000: Cannot truncate a table referenced in a foreign key constraint (`test`.`t2`, CONSTRAINT `t2_ibfk_1` FOREIGN KEY (`b`) REFERENCES `test`.`t1` (`a`))
|
||||
SELECT @a, @b;
|
||||
@a @b
|
||||
0 0
|
||||
DELETE FROM t1;
|
||||
SELECT @a, @b;
|
||||
@a @b
|
||||
1 1
|
||||
INSERT INTO t1 VALUES (1);
|
||||
DELETE FROM t1;
|
||||
SELECT @a, @b;
|
||||
|
68
mysql-test/suite/innodb/r/innodb-truncate.result
Normal file
68
mysql-test/suite/innodb/r/innodb-truncate.result
Normal file
@ -0,0 +1,68 @@
|
||||
#
|
||||
# TRUNCATE TABLE
|
||||
#
|
||||
# Truncating is disallowed for parent tables unless such table
|
||||
# participates in self-referencing foreign keys only.
|
||||
#
|
||||
CREATE TABLE t1 (pk INT PRIMARY KEY) ENGINE=INNODB;
|
||||
CREATE TABLE t2 (fk INT NOT NULL, FOREIGN KEY (fk) REFERENCES t1 (pk)) ENGINE=INNODB;
|
||||
TRUNCATE TABLE t1;
|
||||
ERROR 42000: Cannot truncate a table referenced in a foreign key constraint (`test`.`t2`, CONSTRAINT `t2_ibfk_1` FOREIGN KEY (`fk`) REFERENCES `test`.`t1` (`pk`))
|
||||
# Truncation of child should succeed.
|
||||
TRUNCATE TABLE t2;
|
||||
DROP TABLE t2;
|
||||
DROP TABLE t1;
|
||||
CREATE TABLE t1 (pk INT PRIMARY KEY, fk INT,
|
||||
FOREIGN KEY (fk) REFERENCES t1 (pk)) ENGINE=INNODB;
|
||||
# Truncation of self-referencing table should succeed.
|
||||
TRUNCATE TABLE t1;
|
||||
DROP TABLE t1;
|
||||
#
|
||||
# Also, truncating such tables is allowed if foreign key
|
||||
# checks are disabled.
|
||||
#
|
||||
SET @old_foreign_key_checks = @@SESSION.foreign_key_checks;
|
||||
CREATE TABLE t1 (pk INT PRIMARY KEY) ENGINE=INNODB;
|
||||
CREATE TABLE t2 (fk INT NOT NULL, FOREIGN KEY (fk) REFERENCES t1 (pk)) ENGINE=INNODB;
|
||||
CREATE TABLE t3 (pk INT PRIMARY KEY, fk INT,
|
||||
FOREIGN KEY (fk) REFERENCES t1 (pk)) ENGINE=INNODB;
|
||||
SET @@SESSION.foreign_key_checks = 0;
|
||||
TRUNCATE TABLE t1;
|
||||
TRUNCATE TABLE t2;
|
||||
TRUNCATE TABLE t3;
|
||||
SET @@SESSION.foreign_key_checks = 1;
|
||||
TRUNCATE TABLE t1;
|
||||
ERROR 42000: Cannot truncate a table referenced in a foreign key constraint (`test`.`t2`, CONSTRAINT `t2_ibfk_1` FOREIGN KEY (`fk`) REFERENCES `test`.`t1` (`pk`))
|
||||
TRUNCATE TABLE t2;
|
||||
TRUNCATE TABLE t3;
|
||||
LOCK TABLES t1 WRITE;
|
||||
SET @@SESSION.foreign_key_checks = 0;
|
||||
TRUNCATE TABLE t1;
|
||||
SET @@SESSION.foreign_key_checks = 1;
|
||||
TRUNCATE TABLE t1;
|
||||
ERROR 42000: Cannot truncate a table referenced in a foreign key constraint (`test`.`t2`, CONSTRAINT `t2_ibfk_1` FOREIGN KEY (`fk`) REFERENCES `test`.`t1` (`pk`))
|
||||
UNLOCK TABLES;
|
||||
DROP TABLE t3,t2,t1;
|
||||
SET @@SESSION.foreign_key_checks = @old_foreign_key_checks;
|
||||
#
|
||||
# Test that TRUNCATE resets auto-increment.
|
||||
#
|
||||
CREATE TABLE t1 (a INT PRIMARY KEY NOT NULL AUTO_INCREMENT);
|
||||
INSERT INTO t1 VALUES (NULL), (NULL);
|
||||
SELECT AUTO_INCREMENT FROM INFORMATION_SCHEMA.TABLES WHERE table_name = 't1';
|
||||
AUTO_INCREMENT
|
||||
3
|
||||
SELECT * FROM t1 ORDER BY a;
|
||||
a
|
||||
1
|
||||
2
|
||||
TRUNCATE TABLE t1;
|
||||
SELECT AUTO_INCREMENT FROM INFORMATION_SCHEMA.TABLES WHERE table_name = 't1';
|
||||
AUTO_INCREMENT
|
||||
1
|
||||
INSERT INTO t1 VALUES (NULL), (NULL);
|
||||
SELECT * FROM t1 ORDER BY a;
|
||||
a
|
||||
1
|
||||
2
|
||||
DROP TABLE t1;
|
@ -2424,10 +2424,6 @@ drop table t1,t2;
|
||||
CREATE TABLE t1 (
|
||||
id INTEGER NOT NULL AUTO_INCREMENT, PRIMARY KEY (id)
|
||||
) ENGINE=InnoDB;
|
||||
CREATE TABLE t2 (
|
||||
id INTEGER NOT NULL,
|
||||
FOREIGN KEY (id) REFERENCES t1 (id)
|
||||
) ENGINE=InnoDB;
|
||||
INSERT INTO t1 (id) VALUES (NULL);
|
||||
SELECT * FROM t1;
|
||||
id
|
||||
@ -2443,7 +2439,7 @@ INSERT INTO t1 (id) VALUES (NULL);
|
||||
SELECT * FROM t1;
|
||||
id
|
||||
1
|
||||
DROP TABLE t2, t1;
|
||||
DROP TABLE t1;
|
||||
CREATE TABLE t1
|
||||
(
|
||||
id INT PRIMARY KEY
|
||||
@ -2621,13 +2617,15 @@ ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fail
|
||||
update t4 set a=2;
|
||||
ERROR 23000: Cannot add or update a child row: a foreign key constraint fails (`test`.`t4`, CONSTRAINT `t4_ibfk_1` FOREIGN KEY (`a`) REFERENCES `t3` (`a`))
|
||||
truncate t1;
|
||||
ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails (`test`.`t2`, CONSTRAINT `t2_ibfk_1` FOREIGN KEY (`a`) REFERENCES `t1` (`a`))
|
||||
ERROR 42000: Cannot truncate a table referenced in a foreign key constraint (`test`.`t2`, CONSTRAINT `t2_ibfk_1` FOREIGN KEY (`a`) REFERENCES `test`.`t1` (`a`))
|
||||
truncate t3;
|
||||
ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails (`test`.`t4`, CONSTRAINT `t4_ibfk_1` FOREIGN KEY (`a`) REFERENCES `t3` (`a`))
|
||||
ERROR 42000: Cannot truncate a table referenced in a foreign key constraint (`test`.`t4`, CONSTRAINT `t4_ibfk_1` FOREIGN KEY (`a`) REFERENCES `test`.`t3` (`a`))
|
||||
truncate t2;
|
||||
truncate t4;
|
||||
truncate t1;
|
||||
ERROR 42000: Cannot truncate a table referenced in a foreign key constraint (`test`.`t2`, CONSTRAINT `t2_ibfk_1` FOREIGN KEY (`a`) REFERENCES `test`.`t1` (`a`))
|
||||
truncate t3;
|
||||
ERROR 42000: Cannot truncate a table referenced in a foreign key constraint (`test`.`t4`, CONSTRAINT `t4_ibfk_1` FOREIGN KEY (`a`) REFERENCES `test`.`t3` (`a`))
|
||||
drop table t4,t3,t2,t1;
|
||||
create table t1 (a varchar(255) character set utf8,
|
||||
b varchar(255) character set utf8,
|
||||
|
@ -1941,7 +1941,7 @@ INSERT INTO t2 VALUES (3,2);
|
||||
SET AUTOCOMMIT = 0;
|
||||
START TRANSACTION;
|
||||
TRUNCATE TABLE t1;
|
||||
ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails (`test`.`t2`, CONSTRAINT `t2_ibfk_1` FOREIGN KEY (`t1_id`) REFERENCES `t1` (`id`))
|
||||
ERROR 42000: Cannot truncate a table referenced in a foreign key constraint (`test`.`t2`, CONSTRAINT `t2_ibfk_1` FOREIGN KEY (`t1_id`) REFERENCES `test`.`t1` (`id`))
|
||||
SELECT * FROM t1;
|
||||
id
|
||||
1
|
||||
@ -1953,7 +1953,7 @@ id
|
||||
2
|
||||
START TRANSACTION;
|
||||
TRUNCATE TABLE t1;
|
||||
ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails (`test`.`t2`, CONSTRAINT `t2_ibfk_1` FOREIGN KEY (`t1_id`) REFERENCES `t1` (`id`))
|
||||
ERROR 42000: Cannot truncate a table referenced in a foreign key constraint (`test`.`t2`, CONSTRAINT `t2_ibfk_1` FOREIGN KEY (`t1_id`) REFERENCES `test`.`t1` (`id`))
|
||||
SELECT * FROM t1;
|
||||
id
|
||||
1
|
||||
@ -1971,7 +1971,7 @@ id
|
||||
2
|
||||
COMMIT;
|
||||
TRUNCATE TABLE t1;
|
||||
ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails (`test`.`t2`, CONSTRAINT `t2_ibfk_1` FOREIGN KEY (`t1_id`) REFERENCES `t1` (`id`))
|
||||
ERROR 42000: Cannot truncate a table referenced in a foreign key constraint (`test`.`t2`, CONSTRAINT `t2_ibfk_1` FOREIGN KEY (`t1_id`) REFERENCES `test`.`t1` (`id`))
|
||||
SELECT * FROM t1;
|
||||
id
|
||||
1
|
||||
@ -1983,9 +1983,12 @@ id
|
||||
1
|
||||
2
|
||||
TRUNCATE TABLE t1;
|
||||
ERROR 42000: Cannot truncate a table referenced in a foreign key constraint (`test`.`t2`, CONSTRAINT `t2_ibfk_1` FOREIGN KEY (`t1_id`) REFERENCES `test`.`t1` (`id`))
|
||||
ROLLBACK;
|
||||
SELECT * FROM t1;
|
||||
id
|
||||
1
|
||||
2
|
||||
TRUNCATE TABLE t2;
|
||||
DROP TABLE t2;
|
||||
DROP TABLE t1;
|
||||
@ -2077,9 +2080,9 @@ i i
|
||||
** error handling inside a row iteration.
|
||||
**
|
||||
DROP TRIGGER trg;
|
||||
TRUNCATE TABLE t1;
|
||||
TRUNCATE TABLE t2;
|
||||
TRUNCATE TABLE t3;
|
||||
DELETE FROM t1;
|
||||
DELETE FROM t2;
|
||||
DELETE FROM t3;
|
||||
INSERT INTO t1 VALUES (1),(2),(3),(4);
|
||||
INSERT INTO t3 VALUES (1),(2),(3),(4);
|
||||
INSERT INTO t4 VALUES (3,3),(4,4);
|
||||
@ -2105,9 +2108,9 @@ DROP TRIGGER trg;
|
||||
**
|
||||
** Induce an error midway through an AFTER-trigger
|
||||
**
|
||||
TRUNCATE TABLE t4;
|
||||
TRUNCATE TABLE t1;
|
||||
TRUNCATE TABLE t3;
|
||||
DELETE FROM t4;
|
||||
DELETE FROM t1;
|
||||
DELETE FROM t3;
|
||||
INSERT INTO t1 VALUES (1),(2),(3),(4);
|
||||
INSERT INTO t3 VALUES (1),(2),(3),(4);
|
||||
CREATE TRIGGER trg AFTER DELETE ON t1 FOR EACH ROW
|
||||
|
65
mysql-test/suite/innodb/t/innodb-truncate.test
Normal file
65
mysql-test/suite/innodb/t/innodb-truncate.test
Normal file
@ -0,0 +1,65 @@
|
||||
--source include/have_innodb.inc
|
||||
|
||||
--echo #
|
||||
--echo # TRUNCATE TABLE
|
||||
--echo #
|
||||
--echo # Truncating is disallowed for parent tables unless such table
|
||||
--echo # participates in self-referencing foreign keys only.
|
||||
--echo #
|
||||
CREATE TABLE t1 (pk INT PRIMARY KEY) ENGINE=INNODB;
|
||||
CREATE TABLE t2 (fk INT NOT NULL, FOREIGN KEY (fk) REFERENCES t1 (pk)) ENGINE=INNODB;
|
||||
--error ER_TRUNCATE_ILLEGAL_FK
|
||||
TRUNCATE TABLE t1;
|
||||
--echo # Truncation of child should succeed.
|
||||
TRUNCATE TABLE t2;
|
||||
DROP TABLE t2;
|
||||
DROP TABLE t1;
|
||||
CREATE TABLE t1 (pk INT PRIMARY KEY, fk INT,
|
||||
FOREIGN KEY (fk) REFERENCES t1 (pk)) ENGINE=INNODB;
|
||||
--echo # Truncation of self-referencing table should succeed.
|
||||
TRUNCATE TABLE t1;
|
||||
DROP TABLE t1;
|
||||
|
||||
--echo #
|
||||
--echo # Also, truncating such tables is allowed if foreign key
|
||||
--echo # checks are disabled.
|
||||
--echo #
|
||||
|
||||
SET @old_foreign_key_checks = @@SESSION.foreign_key_checks;
|
||||
CREATE TABLE t1 (pk INT PRIMARY KEY) ENGINE=INNODB;
|
||||
CREATE TABLE t2 (fk INT NOT NULL, FOREIGN KEY (fk) REFERENCES t1 (pk)) ENGINE=INNODB;
|
||||
CREATE TABLE t3 (pk INT PRIMARY KEY, fk INT,
|
||||
FOREIGN KEY (fk) REFERENCES t1 (pk)) ENGINE=INNODB;
|
||||
SET @@SESSION.foreign_key_checks = 0;
|
||||
TRUNCATE TABLE t1;
|
||||
TRUNCATE TABLE t2;
|
||||
TRUNCATE TABLE t3;
|
||||
SET @@SESSION.foreign_key_checks = 1;
|
||||
--error ER_TRUNCATE_ILLEGAL_FK
|
||||
TRUNCATE TABLE t1;
|
||||
TRUNCATE TABLE t2;
|
||||
TRUNCATE TABLE t3;
|
||||
LOCK TABLES t1 WRITE;
|
||||
SET @@SESSION.foreign_key_checks = 0;
|
||||
TRUNCATE TABLE t1;
|
||||
SET @@SESSION.foreign_key_checks = 1;
|
||||
--error ER_TRUNCATE_ILLEGAL_FK
|
||||
TRUNCATE TABLE t1;
|
||||
UNLOCK TABLES;
|
||||
DROP TABLE t3,t2,t1;
|
||||
SET @@SESSION.foreign_key_checks = @old_foreign_key_checks;
|
||||
|
||||
--echo #
|
||||
--echo # Test that TRUNCATE resets auto-increment.
|
||||
--echo #
|
||||
|
||||
CREATE TABLE t1 (a INT PRIMARY KEY NOT NULL AUTO_INCREMENT);
|
||||
INSERT INTO t1 VALUES (NULL), (NULL);
|
||||
SELECT AUTO_INCREMENT FROM INFORMATION_SCHEMA.TABLES WHERE table_name = 't1';
|
||||
SELECT * FROM t1 ORDER BY a;
|
||||
TRUNCATE TABLE t1;
|
||||
SELECT AUTO_INCREMENT FROM INFORMATION_SCHEMA.TABLES WHERE table_name = 't1';
|
||||
INSERT INTO t1 VALUES (NULL), (NULL);
|
||||
SELECT * FROM t1 ORDER BY a;
|
||||
DROP TABLE t1;
|
||||
|
@ -1471,11 +1471,6 @@ CREATE TABLE t1 (
|
||||
id INTEGER NOT NULL AUTO_INCREMENT, PRIMARY KEY (id)
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
CREATE TABLE t2 (
|
||||
id INTEGER NOT NULL,
|
||||
FOREIGN KEY (id) REFERENCES t1 (id)
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
INSERT INTO t1 (id) VALUES (NULL);
|
||||
SELECT * FROM t1;
|
||||
TRUNCATE t1;
|
||||
@ -1488,7 +1483,7 @@ DELETE FROM t1;
|
||||
TRUNCATE t1;
|
||||
INSERT INTO t1 (id) VALUES (NULL);
|
||||
SELECT * FROM t1;
|
||||
DROP TABLE t2, t1;
|
||||
DROP TABLE t1;
|
||||
|
||||
# Test that foreign keys in temporary tables are not accepted (bug #12084)
|
||||
CREATE TABLE t1
|
||||
@ -1723,13 +1718,15 @@ update t2 set a=2;
|
||||
update t3 set a=2;
|
||||
-- error 1452
|
||||
update t4 set a=2;
|
||||
-- error 1451
|
||||
-- error ER_TRUNCATE_ILLEGAL_FK
|
||||
truncate t1;
|
||||
-- error 1451
|
||||
-- error ER_TRUNCATE_ILLEGAL_FK
|
||||
truncate t3;
|
||||
truncate t2;
|
||||
truncate t4;
|
||||
-- error ER_TRUNCATE_ILLEGAL_FK
|
||||
truncate t1;
|
||||
-- error ER_TRUNCATE_ILLEGAL_FK
|
||||
truncate t3;
|
||||
|
||||
drop table t4,t3,t2,t1;
|
||||
|
@ -151,14 +151,14 @@ INSERT INTO t2 VALUES (3,2);
|
||||
SET AUTOCOMMIT = 0;
|
||||
|
||||
START TRANSACTION;
|
||||
--error ER_ROW_IS_REFERENCED_2
|
||||
--error ER_TRUNCATE_ILLEGAL_FK
|
||||
TRUNCATE TABLE t1;
|
||||
SELECT * FROM t1;
|
||||
COMMIT;
|
||||
SELECT * FROM t1;
|
||||
|
||||
START TRANSACTION;
|
||||
--error ER_ROW_IS_REFERENCED_2
|
||||
--error ER_TRUNCATE_ILLEGAL_FK
|
||||
TRUNCATE TABLE t1;
|
||||
SELECT * FROM t1;
|
||||
ROLLBACK;
|
||||
@ -170,13 +170,14 @@ START TRANSACTION;
|
||||
SELECT * FROM t1;
|
||||
COMMIT;
|
||||
|
||||
--error ER_ROW_IS_REFERENCED_2
|
||||
--error ER_TRUNCATE_ILLEGAL_FK
|
||||
TRUNCATE TABLE t1;
|
||||
SELECT * FROM t1;
|
||||
DELETE FROM t2 WHERE id = 3;
|
||||
|
||||
START TRANSACTION;
|
||||
SELECT * FROM t1;
|
||||
--error ER_TRUNCATE_ILLEGAL_FK
|
||||
TRUNCATE TABLE t1;
|
||||
ROLLBACK;
|
||||
SELECT * FROM t1;
|
||||
@ -275,9 +276,9 @@ SELECT * FROM t1 LEFT JOIN t3 ON t1.i=t3.i;
|
||||
--echo ** error handling inside a row iteration.
|
||||
--echo **
|
||||
DROP TRIGGER trg;
|
||||
TRUNCATE TABLE t1;
|
||||
TRUNCATE TABLE t2;
|
||||
TRUNCATE TABLE t3;
|
||||
DELETE FROM t1;
|
||||
DELETE FROM t2;
|
||||
DELETE FROM t3;
|
||||
|
||||
INSERT INTO t1 VALUES (1),(2),(3),(4);
|
||||
INSERT INTO t3 VALUES (1),(2),(3),(4);
|
||||
@ -304,9 +305,9 @@ DROP TRIGGER trg;
|
||||
--echo **
|
||||
--echo ** Induce an error midway through an AFTER-trigger
|
||||
--echo **
|
||||
TRUNCATE TABLE t4;
|
||||
TRUNCATE TABLE t1;
|
||||
TRUNCATE TABLE t3;
|
||||
DELETE FROM t4;
|
||||
DELETE FROM t1;
|
||||
DELETE FROM t3;
|
||||
INSERT INTO t1 VALUES (1),(2),(3),(4);
|
||||
INSERT INTO t3 VALUES (1),(2),(3),(4);
|
||||
delimiter ||;
|
||||
|
@ -177,7 +177,7 @@ let $any_unique= `SELECT @my_errno IN ($ER_DUP_KEY,$ER_DUP_ENTRY)`;
|
||||
# @my_errno AS sql_errno;
|
||||
if (`SELECT @my_errno NOT IN (0,$ER_DUP_KEY,$ER_DUP_ENTRY)`)
|
||||
{
|
||||
--echo # The last command got an unexepected error response.
|
||||
--echo # The last command got an unexpected error response.
|
||||
--echo # Expected/handled SQL codes are 0,$ER_DUP_KEY,$ER_DUP_ENTRY
|
||||
SELECT '# SQL code we got was: ' AS "", @my_errno AS "";
|
||||
--echo # Sorry, have to abort.
|
||||
@ -219,7 +219,7 @@ if ($any_unique)
|
||||
# @my_errno AS sql_errno;
|
||||
if (`SELECT @my_errno NOT IN (0,$ER_DUP_KEY,$ER_DUP_ENTRY)`)
|
||||
{
|
||||
--echo # The last command got an unexepected error response.
|
||||
--echo # The last command got an unexpected error response.
|
||||
--echo # Expected/handled SQL codes are 0,$ER_DUP_KEY,$ER_DUP_ENTRY
|
||||
SELECT '# SQL code we got was: ' AS "", @my_errno AS "";
|
||||
--echo # Sorry, have to abort.
|
||||
@ -255,7 +255,7 @@ if ($any_unique)
|
||||
# @my_errno AS sql_errno;
|
||||
if (`SELECT @my_errno NOT IN (0,$ER_DUP_KEY,$ER_DUP_ENTRY)`)
|
||||
{
|
||||
--echo # The last command got an unexepected error response.
|
||||
--echo # The last command got an unexpected error response.
|
||||
--echo # Expected/handled SQL codes are 0,$ER_DUP_KEY,$ER_DUP_ENTRY
|
||||
SELECT '# SQL code we got was: ' AS "", @my_errno AS "";
|
||||
--echo # Sorry, have to abort.
|
||||
@ -503,7 +503,7 @@ if ($no_debug)
|
||||
eval SET @my_errno = $mysql_errno;
|
||||
if (`SELECT @my_errno NOT IN (0,$ER_SAME_NAME_PARTITION,$ER_NO_PARTITION_FOR_GIVEN_VALUE)`)
|
||||
{
|
||||
--echo # The last command got an unexepected error response.
|
||||
--echo # The last command got an unexpected error response.
|
||||
--echo # Expected/handled SQL codes are 0,$ER_SAME_NAME_PARTITION,$ER_NO_PARTITION_FOR_GIVEN_VALUE
|
||||
SELECT '# SQL code we got was: ' AS "", @my_errno AS "";
|
||||
--echo # Sorry, have to abort.
|
||||
@ -566,7 +566,7 @@ eval SET @my_errno = $mysql_errno;
|
||||
let $run= `SELECT @my_errno = 0`;
|
||||
if (`SELECT @my_errno NOT IN (0,$ER_BAD_NULL_ERROR)`)
|
||||
{
|
||||
--echo # The last command got an unexepected error response.
|
||||
--echo # The last command got an unexpected error response.
|
||||
--echo # Expected/handled SQL codes are 0,$ER_BAD_NULL_ERROR
|
||||
SELECT '# SQL code we got was: ' AS "", @my_errno AS "";
|
||||
--echo # Sorry, have to abort.
|
||||
|
@ -26,7 +26,7 @@ INSERT INTO t2 values (20,22);
|
||||
ERROR 23000: Cannot add or update a child row: a foreign key constraint fails (`test`.`t2`, CONSTRAINT `fk` FOREIGN KEY (`b`) REFERENCES `t1` (`a`))
|
||||
'---Check when foreign_key_checks is disabled---'
|
||||
TRUNCATE t1;
|
||||
ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails (`test`.`t2`, CONSTRAINT `fk` FOREIGN KEY (`b`) REFERENCES `t1` (`a`))
|
||||
ERROR 42000: Cannot truncate a table referenced in a foreign key constraint (`test`.`t2`, CONSTRAINT `fk` FOREIGN KEY (`b`) REFERENCES `test`.`t1` (`a`))
|
||||
SET @@session.foreign_key_checks = 0;
|
||||
TRUNCATE t1;
|
||||
TRUNCATE t2;
|
||||
|
@ -76,7 +76,7 @@ INSERT INTO t2 values (20,22);
|
||||
--echo '---Check when foreign_key_checks is disabled---'
|
||||
#===========================================================
|
||||
|
||||
--Error ER_ROW_IS_REFERENCED_2
|
||||
--Error ER_TRUNCATE_ILLEGAL_FK
|
||||
TRUNCATE t1;
|
||||
|
||||
SET @@session.foreign_key_checks = 0;
|
||||
|
@ -14,3 +14,18 @@ let $engine_type= InnoDB;
|
||||
let $test_foreign_keys= 1;
|
||||
|
||||
--source include/query_cache.inc
|
||||
|
||||
#
|
||||
# Bug#56452 Assertion failed: thd->transaction.stmt.is_empty() ||
|
||||
# thd->in_sub_stmt
|
||||
#
|
||||
CREATE TABLE t1 (a INT) ENGINE=InnoDB;
|
||||
BEGIN;
|
||||
INSERT INTO t1 VALUES(1);
|
||||
ROLLBACK WORK AND CHAIN NO RELEASE;
|
||||
SELECT a FROM t1;
|
||||
ROLLBACK WORK AND CHAIN NO RELEASE;
|
||||
SELECT a FROM t1;
|
||||
ROLLBACK;
|
||||
DROP TABLE t1;
|
||||
|
||||
|
@ -554,3 +554,29 @@ DELETE FROM t1.*, test.t2.*, a.* USING t1, t2, t3 AS a;
|
||||
DROP TABLE t1, t2, t3;
|
||||
|
||||
--echo End of 5.1 tests
|
||||
|
||||
|
||||
--echo #
|
||||
--echo # Bug#51099 Assertion in mysql_multi_delete_prepare()
|
||||
--echo #
|
||||
|
||||
--disable_warnings
|
||||
DROP TABLE IF EXISTS t1, t2;
|
||||
DROP VIEW IF EXISTS v1, v2;
|
||||
--enable_warnings
|
||||
|
||||
CREATE TABLE t1(a INT);
|
||||
CREATE TABLE t2(b INT);
|
||||
CREATE VIEW v1 AS SELECT a, b FROM t1, t2;
|
||||
CREATE VIEW v2 AS SELECT a FROM v1;
|
||||
|
||||
# This is a normal delete
|
||||
--error ER_VIEW_DELETE_MERGE_VIEW
|
||||
DELETE FROM v2;
|
||||
# This is a multi table delete, check that we get the same error
|
||||
# This caused the assertion.
|
||||
--error ER_VIEW_DELETE_MERGE_VIEW
|
||||
DELETE v2 FROM v2;
|
||||
|
||||
DROP VIEW v2, v1;
|
||||
DROP TABLE t1, t2;
|
||||
|
@ -3969,7 +3969,8 @@ INSERT INTO t1 VALUES (1),(2),(3);
|
||||
|
||||
--echo # Connection: con1
|
||||
connection con1;
|
||||
SET debug_sync='lock_table_for_truncate SIGNAL parked_truncate WAIT_FOR go_truncate';
|
||||
LOCK TABLES t1 WRITE;
|
||||
SET debug_sync='upgrade_lock_for_truncate SIGNAL parked_truncate WAIT_FOR go_truncate';
|
||||
send TRUNCATE TABLE t1;
|
||||
|
||||
connection default;
|
||||
@ -3994,15 +3995,17 @@ connection default;
|
||||
--echo # Connection: default
|
||||
SET debug_sync='now WAIT_FOR parked_flush';
|
||||
SET debug_sync='now SIGNAL go_truncate';
|
||||
--echo # Ensure that truncate waits for a exclusive lock
|
||||
let $wait_condition=SELECT COUNT(*)=1 FROM information_schema.processlist
|
||||
WHERE state='Waiting for table metadata lock' AND info='TRUNCATE TABLE t1';
|
||||
--source include/wait_condition.inc
|
||||
SET debug_sync= 'now SIGNAL go_show';
|
||||
|
||||
connection con1;
|
||||
--echo # Connection: con1
|
||||
--echo # Connection: con1 (TRUNCATE)
|
||||
--echo # Reaping...
|
||||
reap;
|
||||
|
||||
connection default;
|
||||
--echo # Connection: default
|
||||
SET debug_sync= 'now SIGNAL go_show';
|
||||
UNLOCK TABLES;
|
||||
|
||||
connection con2;
|
||||
--echo # Connection: con2 (SHOW FIELDS FROM t1)
|
||||
|
@ -2600,11 +2600,12 @@ UNLOCK TABLES;
|
||||
DROP TABLE m1, t1;
|
||||
|
||||
--echo #
|
||||
--echo # Locking the merge table will implicitly lock children.
|
||||
--echo # Locking the merge table won't implicitly lock children.
|
||||
--echo #
|
||||
CREATE TABLE t1 (c1 INT);
|
||||
CREATE TABLE m1 (c1 INT) ENGINE=MRG_MyISAM UNION=(t1);
|
||||
LOCK TABLE m1 WRITE;
|
||||
--error ER_TABLE_NOT_LOCKED_FOR_WRITE
|
||||
ALTER TABLE t1 ADD INDEX (c1);
|
||||
LOCK TABLE m1 WRITE, t1 WRITE;
|
||||
ALTER TABLE t1 ADD INDEX (c1);
|
||||
@ -2776,6 +2777,27 @@ REPAIR TABLE t2 USE_FRM;
|
||||
DROP TABLE t1, t2;
|
||||
|
||||
|
||||
--echo #
|
||||
--echo # Bug#57002 Assert in upgrade_shared_lock_to_exclusive()
|
||||
--echo # for ALTER TABLE + MERGE tables
|
||||
--echo #
|
||||
|
||||
--disable_warnings
|
||||
DROP TABLE IF EXISTS t1, m1;
|
||||
--enable_warnings
|
||||
|
||||
CREATE TABLE t1(a INT) engine=myisam;
|
||||
CREATE TABLE m1(a INT) engine=merge UNION(t1);
|
||||
LOCK TABLES t1 READ, m1 WRITE;
|
||||
|
||||
# This caused an assert
|
||||
--error ER_TABLE_NOT_LOCKED_FOR_WRITE
|
||||
ALTER TABLE t1 engine=myisam;
|
||||
|
||||
UNLOCK TABLES;
|
||||
DROP TABLE m1, t1;
|
||||
|
||||
|
||||
--echo End of 6.0 tests
|
||||
|
||||
--disable_result_log
|
||||
|
@ -636,6 +636,30 @@ UPDATE t1_aux SET f2 = 2 WHERE f1 = f1_two_inserts()|
|
||||
DROP TABLE t1_aux, t1_not_null|
|
||||
DROP FUNCTION f1_two_inserts|
|
||||
|
||||
--echo #
|
||||
--echo # Bug#49938: Failing assertion: inode or deadlock in fsp/fsp0fsp.c
|
||||
--echo #
|
||||
|
||||
--disable_warnings
|
||||
DROP PROCEDURE IF EXISTS p1|
|
||||
DROP TABLE IF EXISTS t1|
|
||||
--enable_warnings
|
||||
|
||||
CREATE TABLE t1 (a INT) ENGINE=INNODB|
|
||||
|
||||
CREATE PROCEDURE p1()
|
||||
BEGIN
|
||||
TRUNCATE TABLE t1;
|
||||
END|
|
||||
|
||||
LOCK TABLES t1 WRITE|
|
||||
CALL p1()|
|
||||
FLUSH TABLES;
|
||||
UNLOCK TABLES|
|
||||
CALL p1()|
|
||||
|
||||
DROP PROCEDURE p1|
|
||||
DROP TABLE t1|
|
||||
|
||||
#
|
||||
# BUG#NNNN: New bug synopsis
|
||||
|
@ -148,10 +148,15 @@ CREATE TRIGGER t1_ad AFTER DELETE ON t1 FOR EACH ROW SET @b = 1;
|
||||
SET @a = 0;
|
||||
SET @b = 0;
|
||||
|
||||
--error ER_TRUNCATE_ILLEGAL_FK
|
||||
TRUNCATE t1;
|
||||
|
||||
SELECT @a, @b;
|
||||
|
||||
DELETE FROM t1;
|
||||
|
||||
SELECT @a, @b;
|
||||
|
||||
INSERT INTO t1 VALUES (1);
|
||||
|
||||
DELETE FROM t1;
|
||||
|
@ -3337,113 +3337,123 @@ int ha_partition::delete_row(const uchar *buf)
|
||||
Called from sql_delete.cc by mysql_delete().
|
||||
Called from sql_select.cc by JOIN::reinit().
|
||||
Called from sql_union.cc by st_select_lex_unit::exec().
|
||||
|
||||
Also used for handle ALTER TABLE t TRUNCATE PARTITION ...
|
||||
NOTE: auto increment value will be truncated in that partition as well!
|
||||
*/
|
||||
|
||||
int ha_partition::delete_all_rows()
|
||||
{
|
||||
int error;
|
||||
bool truncate= FALSE;
|
||||
handler **file;
|
||||
THD *thd= ha_thd();
|
||||
DBUG_ENTER("ha_partition::delete_all_rows");
|
||||
|
||||
if (thd->lex->sql_command == SQLCOM_TRUNCATE)
|
||||
{
|
||||
Alter_info *alter_info= &thd->lex->alter_info;
|
||||
/* TRUNCATE also means resetting auto_increment */
|
||||
lock_auto_increment();
|
||||
table_share->ha_part_data->next_auto_inc_val= 0;
|
||||
table_share->ha_part_data->auto_inc_initialized= FALSE;
|
||||
unlock_auto_increment();
|
||||
if (alter_info->flags & ALTER_ADMIN_PARTITION)
|
||||
{
|
||||
/* ALTER TABLE t TRUNCATE PARTITION ... */
|
||||
List_iterator<partition_element> part_it(m_part_info->partitions);
|
||||
int saved_error= 0;
|
||||
uint num_parts= m_part_info->num_parts;
|
||||
uint num_subparts= m_part_info->num_subparts;
|
||||
uint i= 0;
|
||||
uint num_parts_set= alter_info->partition_names.elements;
|
||||
uint num_parts_found= set_part_state(alter_info, m_part_info,
|
||||
PART_ADMIN);
|
||||
if (num_parts_set != num_parts_found &&
|
||||
(!(alter_info->flags & ALTER_ALL_PARTITION)))
|
||||
DBUG_RETURN(HA_ERR_NO_PARTITION_FOUND);
|
||||
|
||||
/*
|
||||
Cannot return HA_ERR_WRONG_COMMAND here without correct pruning
|
||||
since that whould delete the whole table row by row in sql_delete.cc
|
||||
*/
|
||||
bitmap_clear_all(&m_part_info->used_partitions);
|
||||
do
|
||||
{
|
||||
partition_element *part_elem= part_it++;
|
||||
if (part_elem->part_state == PART_ADMIN)
|
||||
{
|
||||
if (m_is_sub_partitioned)
|
||||
{
|
||||
List_iterator<partition_element>
|
||||
subpart_it(part_elem->subpartitions);
|
||||
partition_element *sub_elem;
|
||||
uint j= 0, part;
|
||||
do
|
||||
{
|
||||
sub_elem= subpart_it++;
|
||||
part= i * num_subparts + j;
|
||||
bitmap_set_bit(&m_part_info->used_partitions, part);
|
||||
if (!saved_error)
|
||||
{
|
||||
DBUG_PRINT("info", ("truncate subpartition %u (%s)",
|
||||
part, sub_elem->partition_name));
|
||||
if ((error= m_file[part]->ha_delete_all_rows()))
|
||||
saved_error= error;
|
||||
/* If not reset_auto_increment is supported, just accept it */
|
||||
if (!saved_error &&
|
||||
(error= m_file[part]->ha_reset_auto_increment(0)) &&
|
||||
error != HA_ERR_WRONG_COMMAND)
|
||||
saved_error= error;
|
||||
}
|
||||
} while (++j < num_subparts);
|
||||
}
|
||||
else
|
||||
{
|
||||
DBUG_PRINT("info", ("truncate partition %u (%s)", i,
|
||||
part_elem->partition_name));
|
||||
bitmap_set_bit(&m_part_info->used_partitions, i);
|
||||
if (!saved_error)
|
||||
{
|
||||
if ((error= m_file[i]->ha_delete_all_rows()) && !saved_error)
|
||||
saved_error= error;
|
||||
/* If not reset_auto_increment is supported, just accept it */
|
||||
if (!saved_error &&
|
||||
(error= m_file[i]->ha_reset_auto_increment(0)) &&
|
||||
error != HA_ERR_WRONG_COMMAND)
|
||||
saved_error= error;
|
||||
}
|
||||
}
|
||||
part_elem->part_state= PART_NORMAL;
|
||||
}
|
||||
} while (++i < num_parts);
|
||||
DBUG_RETURN(saved_error);
|
||||
}
|
||||
truncate= TRUE;
|
||||
}
|
||||
file= m_file;
|
||||
do
|
||||
{
|
||||
if ((error= (*file)->ha_delete_all_rows()))
|
||||
DBUG_RETURN(error);
|
||||
/* Ignore the error */
|
||||
if (truncate)
|
||||
(void) (*file)->ha_reset_auto_increment(0);
|
||||
} while (*(++file));
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Manually truncate the table.
|
||||
|
||||
@retval 0 Success.
|
||||
@retval > 0 Error code.
|
||||
*/
|
||||
|
||||
int ha_partition::truncate()
|
||||
{
|
||||
int error;
|
||||
handler **file;
|
||||
DBUG_ENTER("ha_partition::truncate");
|
||||
|
||||
/*
|
||||
TRUNCATE also means resetting auto_increment. Hence, reset
|
||||
it so that it will be initialized again at the next use.
|
||||
*/
|
||||
lock_auto_increment();
|
||||
table_share->ha_part_data->next_auto_inc_val= 0;
|
||||
table_share->ha_part_data->auto_inc_initialized= FALSE;
|
||||
unlock_auto_increment();
|
||||
|
||||
file= m_file;
|
||||
do
|
||||
{
|
||||
if ((error= (*file)->ha_truncate()))
|
||||
DBUG_RETURN(error);
|
||||
} while (*(++file));
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Truncate a set of specific partitions.
|
||||
|
||||
@remark Auto increment value will be truncated in that partition as well!
|
||||
|
||||
ALTER TABLE t TRUNCATE PARTITION ...
|
||||
*/
|
||||
|
||||
int ha_partition::truncate_partition(Alter_info *alter_info)
|
||||
{
|
||||
int error= 0;
|
||||
List_iterator<partition_element> part_it(m_part_info->partitions);
|
||||
uint num_parts= m_part_info->num_parts;
|
||||
uint num_subparts= m_part_info->num_subparts;
|
||||
uint i= 0;
|
||||
uint num_parts_set= alter_info->partition_names.elements;
|
||||
uint num_parts_found= set_part_state(alter_info, m_part_info,
|
||||
PART_ADMIN);
|
||||
DBUG_ENTER("ha_partition::truncate_partition");
|
||||
|
||||
/*
|
||||
TRUNCATE also means resetting auto_increment. Hence, reset
|
||||
it so that it will be initialized again at the next use.
|
||||
*/
|
||||
lock_auto_increment();
|
||||
table_share->ha_part_data->next_auto_inc_val= 0;
|
||||
table_share->ha_part_data->auto_inc_initialized= FALSE;
|
||||
unlock_auto_increment();
|
||||
|
||||
if (num_parts_set != num_parts_found &&
|
||||
(!(alter_info->flags & ALTER_ALL_PARTITION)))
|
||||
DBUG_RETURN(HA_ERR_NO_PARTITION_FOUND);
|
||||
|
||||
do
|
||||
{
|
||||
partition_element *part_elem= part_it++;
|
||||
if (part_elem->part_state == PART_ADMIN)
|
||||
{
|
||||
if (m_is_sub_partitioned)
|
||||
{
|
||||
List_iterator<partition_element>
|
||||
subpart_it(part_elem->subpartitions);
|
||||
partition_element *sub_elem;
|
||||
uint j= 0, part;
|
||||
do
|
||||
{
|
||||
sub_elem= subpart_it++;
|
||||
part= i * num_subparts + j;
|
||||
DBUG_PRINT("info", ("truncate subpartition %u (%s)",
|
||||
part, sub_elem->partition_name));
|
||||
if ((error= m_file[part]->ha_truncate()))
|
||||
break;
|
||||
} while (++j < num_subparts);
|
||||
}
|
||||
else
|
||||
{
|
||||
DBUG_PRINT("info", ("truncate partition %u (%s)", i,
|
||||
part_elem->partition_name));
|
||||
error= m_file[i]->ha_truncate();
|
||||
}
|
||||
part_elem->part_state= PART_NORMAL;
|
||||
}
|
||||
} while (!error && (++i < num_parts));
|
||||
DBUG_RETURN(error);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Start a large batch of insert rows
|
||||
|
||||
@ -6327,8 +6337,8 @@ void ha_partition::print_error(int error, myf errflag)
|
||||
/* Should probably look for my own errors first */
|
||||
DBUG_PRINT("enter", ("error: %d", error));
|
||||
|
||||
if (error == HA_ERR_NO_PARTITION_FOUND &&
|
||||
thd->lex->sql_command != SQLCOM_TRUNCATE)
|
||||
if ((error == HA_ERR_NO_PARTITION_FOUND) &&
|
||||
! (thd->lex->alter_info.flags & ALTER_TRUNCATE_PARTITION))
|
||||
m_part_info->print_no_partition_found(table);
|
||||
else
|
||||
{
|
||||
|
@ -346,6 +346,7 @@ public:
|
||||
virtual int update_row(const uchar * old_data, uchar * new_data);
|
||||
virtual int delete_row(const uchar * buf);
|
||||
virtual int delete_all_rows(void);
|
||||
virtual int truncate();
|
||||
virtual void start_bulk_insert(ha_rows rows);
|
||||
virtual int end_bulk_insert();
|
||||
private:
|
||||
@ -354,6 +355,15 @@ private:
|
||||
long estimate_read_buffer_size(long original_size);
|
||||
public:
|
||||
|
||||
/*
|
||||
Method for truncating a specific partition.
|
||||
(i.e. ALTER TABLE t1 TRUNCATE PARTITION p).
|
||||
|
||||
@remark This method is a partitioning-specific hook
|
||||
and thus not a member of the general SE API.
|
||||
*/
|
||||
int truncate_partition(Alter_info *);
|
||||
|
||||
virtual bool is_fatal_error(int error, uint flags)
|
||||
{
|
||||
if (!handler::is_fatal_error(error, flags) ||
|
||||
|
@ -3208,6 +3208,21 @@ handler::ha_delete_all_rows()
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Truncate table: public interface.
|
||||
|
||||
@sa handler::truncate()
|
||||
*/
|
||||
|
||||
int
|
||||
handler::ha_truncate()
|
||||
{
|
||||
mark_trx_read_write();
|
||||
|
||||
return truncate();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Reset auto increment: public interface.
|
||||
|
||||
|
@ -1331,6 +1331,7 @@ public:
|
||||
int ha_bulk_update_row(const uchar *old_data, uchar *new_data,
|
||||
uint *dup_key_found);
|
||||
int ha_delete_all_rows();
|
||||
int ha_truncate();
|
||||
int ha_reset_auto_increment(ulonglong value);
|
||||
int ha_optimize(THD* thd, HA_CHECK_OPT* check_opt);
|
||||
int ha_analyze(THD* thd, HA_CHECK_OPT* check_opt);
|
||||
@ -1644,8 +1645,33 @@ public:
|
||||
{ return(NULL);} /* gets tablespace name from handler */
|
||||
/** used in ALTER TABLE; 1 if changing storage engine is allowed */
|
||||
virtual bool can_switch_engines() { return 1; }
|
||||
/** used in REPLACE; is > 0 if table is referred by a FOREIGN KEY */
|
||||
virtual int get_foreign_key_list(THD *thd, List<FOREIGN_KEY_INFO> *f_key_list)
|
||||
/**
|
||||
Get the list of foreign keys in this table.
|
||||
|
||||
@remark Returns the set of foreign keys where this table is the
|
||||
dependent or child table.
|
||||
|
||||
@param thd The thread handle.
|
||||
@param f_key_list[out] The list of foreign keys.
|
||||
|
||||
@return The handler error code or zero for success.
|
||||
*/
|
||||
virtual int
|
||||
get_foreign_key_list(THD *thd, List<FOREIGN_KEY_INFO> *f_key_list)
|
||||
{ return 0; }
|
||||
/**
|
||||
Get the list of foreign keys referencing this table.
|
||||
|
||||
@remark Returns the set of foreign keys where this table is the
|
||||
referenced or parent table.
|
||||
|
||||
@param thd The thread handle.
|
||||
@param f_key_list[out] The list of foreign keys.
|
||||
|
||||
@return The handler error code or zero for success.
|
||||
*/
|
||||
virtual int
|
||||
get_parent_foreign_key_list(THD *thd, List<FOREIGN_KEY_INFO> *f_key_list)
|
||||
{ return 0; }
|
||||
virtual uint referenced_by_foreign_key() { return 0;}
|
||||
virtual void init_table_handle_for_HANDLER()
|
||||
@ -2010,16 +2036,34 @@ private:
|
||||
This is called to delete all rows in a table
|
||||
If the handler don't support this, then this function will
|
||||
return HA_ERR_WRONG_COMMAND and MySQL will delete the rows one
|
||||
by one. It should reset auto_increment if
|
||||
thd->lex->sql_command == SQLCOM_TRUNCATE.
|
||||
by one.
|
||||
*/
|
||||
virtual int delete_all_rows()
|
||||
{ return (my_errno=HA_ERR_WRONG_COMMAND); }
|
||||
/**
|
||||
Quickly remove all rows from a table.
|
||||
|
||||
@remark This method is responsible for implementing MySQL's TRUNCATE
|
||||
TABLE statement, which is a DDL operation. As such, a engine
|
||||
can bypass certain integrity checks and in some cases avoid
|
||||
fine-grained locking (e.g. row locks) which would normally be
|
||||
required for a DELETE statement.
|
||||
|
||||
@remark Typically, truncate is not used if it can result in integrity
|
||||
violation. For example, truncate is not used when a foreign
|
||||
key references the table, but it might be used if foreign key
|
||||
checks are disabled.
|
||||
|
||||
@remark Engine is responsible for resetting the auto-increment counter.
|
||||
|
||||
@remark The table is locked in exclusive mode.
|
||||
*/
|
||||
virtual int truncate()
|
||||
{ return HA_ERR_WRONG_COMMAND; }
|
||||
/**
|
||||
Reset the auto-increment counter to the given value, i.e. the next row
|
||||
inserted will get the given value. This is called e.g. after TRUNCATE
|
||||
is emulated by doing a 'DELETE FROM t'. HA_ERR_WRONG_COMMAND is
|
||||
returned by storage engines that don't support this operation.
|
||||
inserted will get the given value. HA_ERR_WRONG_COMMAND is returned by
|
||||
storage engines that don't support this operation.
|
||||
*/
|
||||
virtual int reset_auto_increment(ulonglong value)
|
||||
{ return HA_ERR_WRONG_COMMAND; }
|
||||
|
@ -6379,3 +6379,6 @@ ER_SET_PASSWORD_AUTH_PLUGIN
|
||||
|
||||
ER_GRANT_PLUGIN_USER_EXISTS
|
||||
eng "GRANT with IDENTIFIED WITH is illegal because the user %-.*s already exists"
|
||||
|
||||
ER_TRUNCATE_ILLEGAL_FK 42000
|
||||
eng "Cannot truncate a table referenced in a foreign key constraint (%.192s)"
|
||||
|
@ -1213,8 +1213,27 @@ sp_head::execute(THD *thd)
|
||||
Object_creation_ctx *saved_creation_ctx;
|
||||
Warning_info *saved_warning_info, warning_info(thd->warning_info->warn_id());
|
||||
|
||||
/* Use some extra margin for possible SP recursion and functions */
|
||||
if (check_stack_overrun(thd, 8 * STACK_MIN_SIZE, (uchar*)&old_packet))
|
||||
/*
|
||||
Just reporting a stack overrun error
|
||||
(@sa check_stack_overrun()) requires stack memory for error
|
||||
message buffer. Thus, we have to put the below check
|
||||
relatively close to the beginning of the execution stack,
|
||||
where available stack margin is still big. As long as the check
|
||||
has to be fairly high up the call stack, the amount of memory
|
||||
we "book" for has to stay fairly high as well, and hence
|
||||
not very accurate. The number below has been calculated
|
||||
by trial and error, and reflects the amount of memory necessary
|
||||
to execute a single stored procedure instruction, be it either
|
||||
an SQL statement, or, heaviest of all, a CALL, which involves
|
||||
parsing and loading of another stored procedure into the cache
|
||||
(@sa db_load_routine() and Bug#10100).
|
||||
At the time of measuring, a recursive SP invocation required
|
||||
3232 bytes of stack on 32 bit Linux and 6016 bytes on 64 bit Mac.
|
||||
The same with db_load_routine() required circa 7k bytes and
|
||||
14k bytes accordingly. Hence, here we book the stack with some
|
||||
reasonable margin.
|
||||
*/
|
||||
if (check_stack_overrun(thd, 2 * STACK_MIN_SIZE, (uchar*)&old_packet))
|
||||
DBUG_RETURN(TRUE);
|
||||
|
||||
/* init per-instruction memroot */
|
||||
|
@ -341,6 +341,7 @@ TODO list:
|
||||
#include "../storage/myisammrg/ha_myisammrg.h"
|
||||
#include "../storage/myisammrg/myrg_def.h"
|
||||
#include "probes_mysql.h"
|
||||
#include "transaction.h"
|
||||
|
||||
#ifdef EMBEDDED_LIBRARY
|
||||
#include "emb_qcache.h"
|
||||
@ -1683,6 +1684,8 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d",
|
||||
}
|
||||
else
|
||||
thd->lex->safe_to_cache_query= 0; // Don't try to cache this
|
||||
/* End the statement transaction potentially started by engine. */
|
||||
trans_rollback_stmt(thd);
|
||||
goto err_unlock; // Parse query
|
||||
}
|
||||
else
|
||||
@ -1724,6 +1727,13 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d",
|
||||
|
||||
thd->limit_found_rows = query->found_rows();
|
||||
thd->status_var.last_query_cost= 0.0;
|
||||
/*
|
||||
End the statement transaction potentially started by an
|
||||
engine callback. We ignore the return value for now,
|
||||
since as long as EOF packet is part of the query cache
|
||||
response, we can't handle it anyway.
|
||||
*/
|
||||
(void) trans_commit_stmt(thd);
|
||||
if (!thd->stmt_da->is_set())
|
||||
thd->stmt_da->disable_status();
|
||||
|
||||
|
@ -525,9 +525,7 @@ int mysql_multi_delete_prepare(THD *thd)
|
||||
if (!(target_tbl->table= target_tbl->correspondent_table->table))
|
||||
{
|
||||
DBUG_ASSERT(target_tbl->correspondent_table->view &&
|
||||
target_tbl->correspondent_table->merge_underlying_list &&
|
||||
target_tbl->correspondent_table->merge_underlying_list->
|
||||
next_local);
|
||||
target_tbl->correspondent_table->multitable_view);
|
||||
my_error(ER_VIEW_DELETE_MERGE_VIEW, MYF(0),
|
||||
target_tbl->correspondent_table->view_db.str,
|
||||
target_tbl->correspondent_table->view_name.str);
|
||||
|
@ -958,6 +958,7 @@ inline bool st_select_lex_unit::is_union ()
|
||||
#define ALTER_ALL_PARTITION (1L << 21)
|
||||
#define ALTER_REMOVE_PARTITIONING (1L << 22)
|
||||
#define ALTER_FOREIGN_KEY (1L << 23)
|
||||
#define ALTER_TRUNCATE_PARTITION (1L << 24)
|
||||
|
||||
enum enum_alter_table_change_level
|
||||
{
|
||||
|
@ -34,7 +34,7 @@
|
||||
#include "sql_locale.h" // my_locale_en_US
|
||||
#include "log.h" // flush_error_log
|
||||
#include "sql_view.h" // mysql_create_view, mysql_drop_view
|
||||
#include "sql_delete.h" // mysql_truncate, mysql_delete
|
||||
#include "sql_delete.h" // mysql_delete
|
||||
#include "sql_insert.h" // mysql_insert
|
||||
#include "sql_update.h" // mysql_update, mysql_multi_update
|
||||
#include "sql_partition.h" // struct partition_info
|
||||
@ -49,7 +49,6 @@
|
||||
// mysql_recreate_table,
|
||||
// mysql_backup_table,
|
||||
// mysql_restore_table
|
||||
#include "sql_truncate.h" // mysql_truncate_table
|
||||
#include "sql_reload.h" // reload_acl_and_cache
|
||||
#include "sql_admin.h" // mysql_assign_to_keycache
|
||||
#include "sql_connect.h" // check_user,
|
||||
|
@ -16,10 +16,10 @@
|
||||
#include "sql_parse.h" // check_one_table_access
|
||||
#include "sql_table.h" // mysql_alter_table, etc.
|
||||
#include "sql_lex.h" // Sql_statement
|
||||
#include "sql_truncate.h" // mysql_truncate_table,
|
||||
// Truncate_statement
|
||||
#include "sql_admin.h" // Analyze/Check/.._table_statement
|
||||
#include "sql_partition_admin.h" // Alter_table_*_partition
|
||||
#include "ha_partition.h" // ha_partition
|
||||
#include "sql_base.h" // open_and_lock_tables
|
||||
|
||||
#ifndef WITH_PARTITION_STORAGE_ENGINE
|
||||
|
||||
@ -46,7 +46,7 @@ bool Alter_table_analyze_partition_statement::execute(THD *thd)
|
||||
m_lex->alter_info.flags|= ALTER_ADMIN_PARTITION;
|
||||
|
||||
res= Analyze_table_statement::execute(thd);
|
||||
|
||||
|
||||
DBUG_RETURN(res);
|
||||
}
|
||||
|
||||
@ -104,36 +104,85 @@ bool Alter_table_repair_partition_statement::execute(THD *thd)
|
||||
|
||||
bool Alter_table_truncate_partition_statement::execute(THD *thd)
|
||||
{
|
||||
int error;
|
||||
ha_partition *partition;
|
||||
ulong timeout= thd->variables.lock_wait_timeout;
|
||||
TABLE_LIST *first_table= thd->lex->select_lex.table_list.first;
|
||||
bool res;
|
||||
enum_sql_command original_sql_command;
|
||||
DBUG_ENTER("Alter_table_truncate_partition_statement::execute");
|
||||
|
||||
/*
|
||||
Execute TRUNCATE PARTITION just like TRUNCATE TABLE.
|
||||
Some storage engines (InnoDB, partition) checks thd_sql_command,
|
||||
so we set it to SQLCOM_TRUNCATE during the execution.
|
||||
*/
|
||||
original_sql_command= m_lex->sql_command;
|
||||
m_lex->sql_command= SQLCOM_TRUNCATE;
|
||||
|
||||
/*
|
||||
Flag that it is an ALTER command which administrates partitions, used
|
||||
by ha_partition.
|
||||
*/
|
||||
m_lex->alter_info.flags|= ALTER_ADMIN_PARTITION;
|
||||
|
||||
/*
|
||||
Fix the lock types (not the same as ordinary ALTER TABLE).
|
||||
*/
|
||||
m_lex->alter_info.flags|= ALTER_ADMIN_PARTITION |
|
||||
ALTER_TRUNCATE_PARTITION;
|
||||
|
||||
/* Fix the lock types (not the same as ordinary ALTER TABLE). */
|
||||
first_table->lock_type= TL_WRITE;
|
||||
first_table->mdl_request.set_type(MDL_SHARED_NO_READ_WRITE);
|
||||
first_table->mdl_request.set_type(MDL_EXCLUSIVE);
|
||||
|
||||
/* execute as a TRUNCATE TABLE */
|
||||
res= Truncate_statement::execute(thd);
|
||||
/*
|
||||
Check table permissions and open it with a exclusive lock.
|
||||
Ensure it is a partitioned table and finally, upcast the
|
||||
handler and invoke the partition truncate method. Lastly,
|
||||
write the statement to the binary log if necessary.
|
||||
*/
|
||||
|
||||
m_lex->sql_command= original_sql_command;
|
||||
DBUG_RETURN(res);
|
||||
if (check_one_table_access(thd, DROP_ACL, first_table))
|
||||
DBUG_RETURN(TRUE);
|
||||
|
||||
if (open_and_lock_tables(thd, first_table, FALSE, 0))
|
||||
DBUG_RETURN(TRUE);
|
||||
|
||||
/*
|
||||
TODO: Add support for TRUNCATE PARTITION for NDB and other
|
||||
engines supporting native partitioning.
|
||||
*/
|
||||
if (first_table->table->s->db_type() != partition_hton)
|
||||
{
|
||||
my_error(ER_PARTITION_MGMT_ON_NONPARTITIONED, MYF(0));
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
|
||||
/*
|
||||
Under locked table modes this might still not be an exclusive
|
||||
lock. Hence, upgrade the lock since the handler truncate method
|
||||
mandates an exclusive metadata lock.
|
||||
*/
|
||||
MDL_ticket *ticket= first_table->table->mdl_ticket;
|
||||
if (thd->mdl_context.upgrade_shared_lock_to_exclusive(ticket, timeout))
|
||||
DBUG_RETURN(TRUE);
|
||||
|
||||
tdc_remove_table(thd, TDC_RT_REMOVE_NOT_OWN, first_table->db,
|
||||
first_table->table_name, FALSE);
|
||||
|
||||
partition= (ha_partition *) first_table->table->file;
|
||||
|
||||
/* Invoke the handler method responsible for truncating the partition. */
|
||||
if ((error= partition->truncate_partition(&thd->lex->alter_info)))
|
||||
first_table->table->file->print_error(error, MYF(0));
|
||||
|
||||
/*
|
||||
All effects of a truncate operation are committed even if the
|
||||
operation fails. Thus, the query must be written to the binary
|
||||
log. The only exception is a unimplemented truncate method. Also,
|
||||
it is logged in statement format, regardless of the binlog format.
|
||||
*/
|
||||
if (error != HA_ERR_WRONG_COMMAND)
|
||||
error|= write_bin_log(thd, !error, thd->query(), thd->query_length());
|
||||
|
||||
/*
|
||||
A locked table ticket was upgraded to a exclusive lock. After the
|
||||
the query has been written to the binary log, downgrade the lock
|
||||
to a shared one.
|
||||
*/
|
||||
if (thd->locked_tables_mode)
|
||||
ticket->downgrade_exclusive_lock(MDL_SHARED_NO_READ_WRITE);
|
||||
|
||||
if (! error)
|
||||
my_ok(thd);
|
||||
|
||||
DBUG_RETURN(error);
|
||||
}
|
||||
|
||||
#endif /* WITH_PARTITION_STORAGE_ENGINE */
|
||||
|
@ -210,7 +210,7 @@ public:
|
||||
/**
|
||||
Class that represents the ALTER TABLE t1 TRUNCATE PARTITION p statement.
|
||||
*/
|
||||
class Alter_table_truncate_partition_statement : public Truncate_statement
|
||||
class Alter_table_truncate_partition_statement : public Sql_statement
|
||||
{
|
||||
public:
|
||||
/**
|
||||
@ -218,10 +218,10 @@ public:
|
||||
@param lex the LEX structure for this statement.
|
||||
*/
|
||||
Alter_table_truncate_partition_statement(LEX *lex)
|
||||
: Truncate_statement(lex)
|
||||
: Sql_statement(lex)
|
||||
{}
|
||||
|
||||
~Alter_table_truncate_partition_statement()
|
||||
virtual ~Alter_table_truncate_partition_statement()
|
||||
{}
|
||||
|
||||
/**
|
||||
|
@ -5063,8 +5063,8 @@ static int get_schema_constraints_record(THD *thd, TABLE_LIST *tables,
|
||||
while ((f_key_info=it++))
|
||||
{
|
||||
if (store_constraints(thd, table, db_name, table_name,
|
||||
f_key_info->forein_id->str,
|
||||
strlen(f_key_info->forein_id->str),
|
||||
f_key_info->foreign_id->str,
|
||||
strlen(f_key_info->foreign_id->str),
|
||||
"FOREIGN KEY", 11))
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
@ -5263,8 +5263,8 @@ static int get_schema_key_column_usage_record(THD *thd,
|
||||
f_idx++;
|
||||
restore_record(table, s->default_values);
|
||||
store_key_column_usage(table, db_name, table_name,
|
||||
f_key_info->forein_id->str,
|
||||
f_key_info->forein_id->length,
|
||||
f_key_info->foreign_id->str,
|
||||
f_key_info->foreign_id->length,
|
||||
f_info->str, f_info->length,
|
||||
(longlong) f_idx);
|
||||
table->field[8]->store((longlong) f_idx, TRUE);
|
||||
@ -6053,8 +6053,8 @@ get_referential_constraints_record(THD *thd, TABLE_LIST *tables,
|
||||
table->field[0]->store(STRING_WITH_LEN("def"), cs);
|
||||
table->field[1]->store(db_name->str, db_name->length, cs);
|
||||
table->field[9]->store(table_name->str, table_name->length, cs);
|
||||
table->field[2]->store(f_key_info->forein_id->str,
|
||||
f_key_info->forein_id->length, cs);
|
||||
table->field[2]->store(f_key_info->foreign_id->str,
|
||||
f_key_info->foreign_id->length, cs);
|
||||
table->field[3]->store(STRING_WITH_LEN("def"), cs);
|
||||
table->field[4]->store(f_key_info->referenced_db->str,
|
||||
f_key_info->referenced_db->length, cs);
|
||||
|
@ -265,8 +265,12 @@ public:
|
||||
CHARSET_INFO *csto, uint *errors);
|
||||
bool append(const String &s);
|
||||
bool append(const char *s);
|
||||
bool append(const char *s,uint32 arg_length);
|
||||
bool append(const char *s,uint32 arg_length, CHARSET_INFO *cs);
|
||||
bool append(LEX_STRING *ls)
|
||||
{
|
||||
return append(ls->str, ls->length);
|
||||
}
|
||||
bool append(const char *s, uint32 arg_length);
|
||||
bool append(const char *s, uint32 arg_length, CHARSET_INFO *cs);
|
||||
bool append_ulonglong(ulonglong val);
|
||||
bool append(IO_CACHE* file, uint32 arg_length);
|
||||
bool append_with_prefill(const char *s, uint32 arg_length,
|
||||
|
@ -13,11 +13,8 @@
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
|
||||
|
||||
#include "sql_priv.h"
|
||||
#include "transaction.h"
|
||||
#include "debug_sync.h"
|
||||
#include "records.h" // READ_RECORD
|
||||
#include "table.h" // TABLE
|
||||
#include "debug_sync.h" // DEBUG_SYNC
|
||||
#include "table.h" // TABLE, FOREIGN_KEY_INFO
|
||||
#include "sql_class.h" // THD
|
||||
#include "sql_base.h" // open_and_lock_tables
|
||||
#include "sql_table.h" // write_bin_log
|
||||
@ -29,145 +26,212 @@
|
||||
#include "sql_truncate.h"
|
||||
|
||||
|
||||
/*
|
||||
Delete all rows of a locked table.
|
||||
/**
|
||||
Append a list of field names to a string.
|
||||
|
||||
@param thd Thread context.
|
||||
@param table_list Table list element for the table.
|
||||
@param rows_deleted Whether rows might have been deleted.
|
||||
@param str The string.
|
||||
@param fields The list of field names.
|
||||
|
||||
@retval FALSE Success.
|
||||
@retval TRUE Error.
|
||||
@return TRUE on failure, FALSE otherwise.
|
||||
*/
|
||||
|
||||
static bool fk_info_append_fields(String *str, List<LEX_STRING> *fields)
|
||||
{
|
||||
bool res= FALSE;
|
||||
LEX_STRING *field;
|
||||
List_iterator_fast<LEX_STRING> it(*fields);
|
||||
|
||||
while ((field= it++))
|
||||
{
|
||||
res|= str->append("`");
|
||||
res|= str->append(field);
|
||||
res|= str->append("`, ");
|
||||
}
|
||||
|
||||
str->chop();
|
||||
str->chop();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Generate a foreign key description suitable for a error message.
|
||||
|
||||
@param thd Thread context.
|
||||
@param fk_info The foreign key information.
|
||||
|
||||
@return A human-readable string describing the foreign key.
|
||||
*/
|
||||
|
||||
static const char *fk_info_str(THD *thd, FOREIGN_KEY_INFO *fk_info)
|
||||
{
|
||||
bool res= FALSE;
|
||||
char buffer[STRING_BUFFER_USUAL_SIZE*2];
|
||||
String str(buffer, sizeof(buffer), system_charset_info);
|
||||
|
||||
str.length(0);
|
||||
|
||||
/*
|
||||
`db`.`tbl`, CONSTRAINT `id` FOREIGN KEY (`fk`) REFERENCES `db`.`tbl` (`fk`)
|
||||
*/
|
||||
|
||||
res|= str.append('`');
|
||||
res|= str.append(fk_info->foreign_db);
|
||||
res|= str.append("`.`");
|
||||
res|= str.append(fk_info->foreign_table);
|
||||
res|= str.append("`, CONSTRAINT `");
|
||||
res|= str.append(fk_info->foreign_id);
|
||||
res|= str.append("` FOREIGN KEY (");
|
||||
res|= fk_info_append_fields(&str, &fk_info->foreign_fields);
|
||||
res|= str.append(") REFERENCES `");
|
||||
res|= str.append(fk_info->referenced_db);
|
||||
res|= str.append("`.`");
|
||||
res|= str.append(fk_info->referenced_table);
|
||||
res|= str.append("` (");
|
||||
res|= fk_info_append_fields(&str, &fk_info->referenced_fields);
|
||||
res|= str.append(')');
|
||||
|
||||
return res ? NULL : thd->strmake(str.ptr(), str.length());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Check and emit a fatal error if the table which is going to be
|
||||
affected by TRUNCATE TABLE is a parent table in some non-self-
|
||||
referencing foreign key.
|
||||
|
||||
@remark The intention is to allow truncate only for tables that
|
||||
are not dependent on other tables.
|
||||
|
||||
@param thd Thread context.
|
||||
@param table Table handle.
|
||||
|
||||
@retval FALSE This table is not parent in a non-self-referencing foreign
|
||||
key. Statement can proceed.
|
||||
@retval TRUE This table is parent in a non-self-referencing foreign key,
|
||||
error was emitted.
|
||||
*/
|
||||
|
||||
static bool
|
||||
delete_all_rows(THD *thd, TABLE *table)
|
||||
fk_truncate_illegal_if_parent(THD *thd, TABLE *table)
|
||||
{
|
||||
int error;
|
||||
READ_RECORD info;
|
||||
bool is_bulk_delete;
|
||||
bool some_rows_deleted= FALSE;
|
||||
bool save_binlog_row_based= thd->is_current_stmt_binlog_format_row();
|
||||
DBUG_ENTER("delete_all_rows");
|
||||
|
||||
/* Replication of truncate table must be statement based. */
|
||||
thd->clear_current_stmt_binlog_format_row();
|
||||
FOREIGN_KEY_INFO *fk_info;
|
||||
List<FOREIGN_KEY_INFO> fk_list;
|
||||
List_iterator_fast<FOREIGN_KEY_INFO> it;
|
||||
|
||||
/*
|
||||
Update handler statistics (e.g. table->file->stats.records).
|
||||
Might be used by the storage engine to aggregate information
|
||||
necessary to allow deletion. Currently, this seems to be
|
||||
meaningful only to the archive storage engine, which uses
|
||||
the info method to set the number of records. Although
|
||||
archive does not support deletion, it becomes necessary in
|
||||
order to return a error if the table is not empty.
|
||||
Bail out early if the table is not referenced by a foreign key.
|
||||
In this case, the table could only be, if at all, a child table.
|
||||
*/
|
||||
error= table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
|
||||
if (error && error != HA_ERR_WRONG_COMMAND)
|
||||
{
|
||||
table->file->print_error(error, MYF(0));
|
||||
goto end;
|
||||
}
|
||||
if (! table->file->referenced_by_foreign_key())
|
||||
return FALSE;
|
||||
|
||||
/*
|
||||
Attempt to delete all rows in the table.
|
||||
If it is unsupported, switch to row by row deletion.
|
||||
This table _is_ referenced by a foreign key. At this point, only
|
||||
self-referencing keys are acceptable. For this reason, get the list
|
||||
of foreign keys referencing this table in order to check the name
|
||||
of the child (dependent) tables.
|
||||
*/
|
||||
if (! (error= table->file->ha_delete_all_rows()))
|
||||
goto end;
|
||||
table->file->get_parent_foreign_key_list(thd, &fk_list);
|
||||
|
||||
if (error != HA_ERR_WRONG_COMMAND)
|
||||
{
|
||||
/*
|
||||
If a transactional engine fails in the middle of deletion,
|
||||
we expect it to be able to roll it back. Some reasons
|
||||
for the engine to fail would be media failure or corrupted
|
||||
data dictionary (i.e. in case of a partitioned table). We
|
||||
have sufficiently strong metadata locks to rule out any
|
||||
potential deadlocks.
|
||||
|
||||
If a non-transactional engine fails here (that would
|
||||
not be MyISAM, since MyISAM does TRUNCATE by recreate),
|
||||
and binlog is on, replication breaks, since nothing gets
|
||||
written to the binary log. (XXX: is this a bug?)
|
||||
*/
|
||||
table->file->print_error(error, MYF(0));
|
||||
goto end;
|
||||
}
|
||||
|
||||
/*
|
||||
A workaround for Bug#53696 "Performance schema engine violates the
|
||||
PSEA API by calling my_error()".
|
||||
*/
|
||||
/* Out of memory when building list. */
|
||||
if (thd->is_error())
|
||||
goto end;
|
||||
return TRUE;
|
||||
|
||||
/* Handler didn't support fast delete. Delete rows one by one. */
|
||||
it.init(fk_list);
|
||||
|
||||
init_read_record(&info, thd, table, NULL, TRUE, TRUE, FALSE);
|
||||
|
||||
/*
|
||||
Start bulk delete. If the engine does not support it, go on,
|
||||
it's not an error.
|
||||
*/
|
||||
is_bulk_delete= ! table->file->start_bulk_delete();
|
||||
|
||||
table->mark_columns_needed_for_delete();
|
||||
|
||||
while (!(error= info.read_record(&info)) && !thd->killed)
|
||||
/* Loop over the set of foreign keys for which this table is a parent. */
|
||||
while ((fk_info= it++))
|
||||
{
|
||||
if ((error= table->file->ha_delete_row(table->record[0])))
|
||||
{
|
||||
table->file->print_error(error, MYF(0));
|
||||
DBUG_ASSERT(!my_strcasecmp(system_charset_info,
|
||||
fk_info->referenced_db->str,
|
||||
table->s->db.str));
|
||||
|
||||
DBUG_ASSERT(!my_strcasecmp(system_charset_info,
|
||||
fk_info->referenced_table->str,
|
||||
table->s->table_name.str));
|
||||
|
||||
if (my_strcasecmp(system_charset_info, fk_info->foreign_db->str,
|
||||
table->s->db.str) ||
|
||||
my_strcasecmp(system_charset_info, fk_info->foreign_table->str,
|
||||
table->s->table_name.str))
|
||||
break;
|
||||
}
|
||||
|
||||
some_rows_deleted= TRUE;
|
||||
}
|
||||
|
||||
/* HA_ERR_END_OF_FILE */
|
||||
if (error == -1)
|
||||
error= 0;
|
||||
|
||||
/* Close down the bulk delete. */
|
||||
if (is_bulk_delete)
|
||||
/* Table is parent in a non-self-referencing foreign key. */
|
||||
if (fk_info)
|
||||
{
|
||||
int bulk_delete_error= table->file->end_bulk_delete();
|
||||
if (bulk_delete_error && !error)
|
||||
{
|
||||
table->file->print_error(bulk_delete_error, MYF(0));
|
||||
error= bulk_delete_error;
|
||||
}
|
||||
my_error(ER_TRUNCATE_ILLEGAL_FK, MYF(0), fk_info_str(thd, fk_info));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
end_read_record(&info);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Open and truncate a locked table.
|
||||
|
||||
@param thd Thread context.
|
||||
@param table_ref Table list element for the table to be truncated.
|
||||
@param is_tmp_table True if element refers to a temp table.
|
||||
|
||||
@retval 0 Success.
|
||||
@retval > 0 Error code.
|
||||
*/
|
||||
|
||||
int Truncate_statement::handler_truncate(THD *thd, TABLE_LIST *table_ref,
|
||||
bool is_tmp_table)
|
||||
{
|
||||
int error= 0;
|
||||
uint flags;
|
||||
DBUG_ENTER("Truncate_statement::handler_truncate");
|
||||
|
||||
/*
|
||||
Regardless of the error status, the query must be written to the
|
||||
binary log if rows of the table is non-transactional.
|
||||
Can't recreate, the engine must mechanically delete all rows
|
||||
in the table. Use open_and_lock_tables() to open a write cursor.
|
||||
*/
|
||||
if (some_rows_deleted && !table->file->has_transactions())
|
||||
|
||||
/* If it is a temporary table, no need to take locks. */
|
||||
if (is_tmp_table)
|
||||
flags= MYSQL_OPEN_TEMPORARY_ONLY;
|
||||
else
|
||||
{
|
||||
thd->transaction.stmt.modified_non_trans_table= TRUE;
|
||||
thd->transaction.all.modified_non_trans_table= TRUE;
|
||||
/* We don't need to load triggers. */
|
||||
DBUG_ASSERT(table_ref->trg_event_map == 0);
|
||||
/*
|
||||
Our metadata lock guarantees that no transaction is reading
|
||||
or writing into the table. Yet, to open a write cursor we need
|
||||
a thr_lock lock. Allow to open base tables only.
|
||||
*/
|
||||
table_ref->required_type= FRMTYPE_TABLE;
|
||||
/*
|
||||
Ignore pending FLUSH TABLES since we don't want to release
|
||||
the MDL lock taken above and otherwise there is no way to
|
||||
wait for FLUSH TABLES in deadlock-free fashion.
|
||||
*/
|
||||
flags= MYSQL_OPEN_IGNORE_FLUSH | MYSQL_OPEN_SKIP_TEMPORARY;
|
||||
/*
|
||||
Even though we have an MDL lock on the table here, we don't
|
||||
pass MYSQL_OPEN_HAS_MDL_LOCK to open_and_lock_tables
|
||||
since to truncate a MERGE table, we must open and lock
|
||||
merge children, and on those we don't have an MDL lock.
|
||||
Thus clear the ticket to satisfy MDL asserts.
|
||||
*/
|
||||
table_ref->mdl_request.ticket= NULL;
|
||||
}
|
||||
|
||||
if (error || thd->killed)
|
||||
goto end;
|
||||
/* Open the table as it will handle some required preparations. */
|
||||
if (open_and_lock_tables(thd, table_ref, FALSE, flags))
|
||||
DBUG_RETURN(1);
|
||||
|
||||
/* Truncate resets the auto-increment counter. */
|
||||
error= table->file->ha_reset_auto_increment(0);
|
||||
if (error)
|
||||
{
|
||||
if (error != HA_ERR_WRONG_COMMAND)
|
||||
table->file->print_error(error, MYF(0));
|
||||
else
|
||||
error= 0;
|
||||
}
|
||||
/* Whether to truncate regardless of foreign keys. */
|
||||
if (! (thd->variables.option_bits & OPTION_NO_FOREIGN_KEY_CHECKS))
|
||||
error= fk_truncate_illegal_if_parent(thd, table_ref->table);
|
||||
|
||||
end:
|
||||
if (save_binlog_row_based)
|
||||
thd->set_current_stmt_binlog_format_row();
|
||||
if (!error && (error= table_ref->table->file->ha_truncate()))
|
||||
table_ref->table->file->print_error(error, MYF(0));
|
||||
|
||||
DBUG_RETURN(error);
|
||||
}
|
||||
@ -225,30 +289,29 @@ static bool recreate_temporary_table(THD *thd, TABLE *table)
|
||||
|
||||
|
||||
/*
|
||||
Handle opening and locking if a base table for truncate.
|
||||
Handle locking a base table for truncate.
|
||||
|
||||
@param[in] thd Thread context.
|
||||
@param[in] table_ref Table list element for the table to
|
||||
be truncated.
|
||||
@param[out] hton_can_recreate Set to TRUE if table can be dropped
|
||||
and recreated.
|
||||
@param[out] ticket_downgrade Set if a lock must be downgraded after
|
||||
truncate is done.
|
||||
|
||||
@retval FALSE Success.
|
||||
@retval TRUE Error.
|
||||
*/
|
||||
|
||||
static bool open_and_lock_table_for_truncate(THD *thd, TABLE_LIST *table_ref,
|
||||
bool *hton_can_recreate,
|
||||
MDL_ticket **ticket_downgrade)
|
||||
bool Truncate_statement::lock_table(THD *thd, TABLE_LIST *table_ref,
|
||||
bool *hton_can_recreate)
|
||||
{
|
||||
TABLE *table= NULL;
|
||||
handlerton *table_type;
|
||||
DBUG_ENTER("open_and_lock_table_for_truncate");
|
||||
DBUG_ENTER("Truncate_statement::lock_table");
|
||||
|
||||
/* Lock types are set in the parser. */
|
||||
DBUG_ASSERT(table_ref->lock_type == TL_WRITE);
|
||||
DBUG_ASSERT(table_ref->mdl_request.type == MDL_SHARED_NO_READ_WRITE);
|
||||
/* The handler truncate protocol dictates a exclusive lock. */
|
||||
DBUG_ASSERT(table_ref->mdl_request.type == MDL_EXCLUSIVE);
|
||||
|
||||
/*
|
||||
Before doing anything else, acquire a metadata lock on the table,
|
||||
or ensure we have one. We don't use open_and_lock_tables()
|
||||
@ -268,103 +331,45 @@ static bool open_and_lock_table_for_truncate(THD *thd, TABLE_LIST *table_ref,
|
||||
table_ref->table_name, FALSE)))
|
||||
DBUG_RETURN(TRUE);
|
||||
|
||||
table_type= table->s->db_type();
|
||||
*hton_can_recreate= ha_check_storage_engine_flag(table_type,
|
||||
*hton_can_recreate= ha_check_storage_engine_flag(table->s->db_type(),
|
||||
HTON_CAN_RECREATE);
|
||||
table_ref->mdl_request.ticket= table->mdl_ticket;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
Even though we could use the previous execution branch here just as
|
||||
well, we must not try to open the table:
|
||||
*/
|
||||
/* Acquire an exclusive lock. */
|
||||
DBUG_ASSERT(table_ref->next_global == NULL);
|
||||
if (lock_table_names(thd, table_ref, NULL,
|
||||
thd->variables.lock_wait_timeout,
|
||||
MYSQL_OPEN_SKIP_TEMPORARY))
|
||||
DBUG_RETURN(TRUE);
|
||||
|
||||
if (dd_frm_storage_engine(thd, table_ref->db, table_ref->table_name,
|
||||
&table_type))
|
||||
if (dd_check_storage_engine_flag(thd, table_ref->db, table_ref->table_name,
|
||||
HTON_CAN_RECREATE, hton_can_recreate))
|
||||
DBUG_RETURN(TRUE);
|
||||
*hton_can_recreate= ha_check_storage_engine_flag(table_type,
|
||||
HTON_CAN_RECREATE);
|
||||
}
|
||||
|
||||
#ifdef WITH_PARTITION_STORAGE_ENGINE
|
||||
/*
|
||||
TODO: Add support for TRUNCATE PARTITION for NDB and other engines
|
||||
supporting native partitioning.
|
||||
A storage engine can recreate or truncate the table only if there
|
||||
are no references to it from anywhere, i.e. no cached TABLE in the
|
||||
table cache.
|
||||
*/
|
||||
if (thd->lex->alter_info.flags & ALTER_ADMIN_PARTITION &&
|
||||
table_type != partition_hton)
|
||||
if (thd->locked_tables_mode)
|
||||
{
|
||||
my_error(ER_PARTITION_MGMT_ON_NONPARTITIONED, MYF(0));
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
#endif
|
||||
DEBUG_SYNC(thd, "lock_table_for_truncate");
|
||||
|
||||
if (*hton_can_recreate)
|
||||
{
|
||||
/*
|
||||
Acquire an exclusive lock. The storage engine can recreate the
|
||||
table only if there are no references to it from anywhere, i.e.
|
||||
no cached TABLE in the table cache. To remove the table from the
|
||||
cache we need an exclusive lock.
|
||||
*/
|
||||
if (thd->locked_tables_mode)
|
||||
{
|
||||
if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN))
|
||||
DBUG_RETURN(TRUE);
|
||||
*ticket_downgrade= table->mdl_ticket;
|
||||
DEBUG_SYNC(thd, "upgrade_lock_for_truncate");
|
||||
/* To remove the table from the cache we need an exclusive lock. */
|
||||
if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN))
|
||||
DBUG_RETURN(TRUE);
|
||||
m_ticket_downgrade= table->mdl_ticket;
|
||||
/* Close if table is going to be recreated. */
|
||||
if (*hton_can_recreate)
|
||||
close_all_tables_for_name(thd, table->s, FALSE);
|
||||
}
|
||||
else
|
||||
{
|
||||
ulong timeout= thd->variables.lock_wait_timeout;
|
||||
if (thd->mdl_context.
|
||||
upgrade_shared_lock_to_exclusive(table_ref->mdl_request.ticket,
|
||||
timeout))
|
||||
DBUG_RETURN(TRUE);
|
||||
tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table_ref->db,
|
||||
table_ref->table_name, FALSE);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
Can't recreate, we must mechanically delete all rows in
|
||||
the table. Our metadata lock guarantees that no transaction
|
||||
is reading or writing into the table. Yet, to open a write
|
||||
cursor we need a thr_lock lock. Use open_and_lock_tables()
|
||||
to do the necessary job.
|
||||
*/
|
||||
|
||||
/* Allow to open base tables only. */
|
||||
table_ref->required_type= FRMTYPE_TABLE;
|
||||
/* We don't need to load triggers. */
|
||||
DBUG_ASSERT(table_ref->trg_event_map == 0);
|
||||
/*
|
||||
Even though we have an MDL lock on the table here, we don't
|
||||
pass MYSQL_OPEN_HAS_MDL_LOCK to open_and_lock_tables
|
||||
since to truncate a MERGE table, we must open and lock
|
||||
merge children, and on those we don't have an MDL lock.
|
||||
Thus clear the ticket to satisfy MDL asserts.
|
||||
*/
|
||||
table_ref->mdl_request.ticket= NULL;
|
||||
|
||||
/*
|
||||
Open the table as it will handle some required preparations.
|
||||
Ignore pending FLUSH TABLES since we don't want to release
|
||||
the MDL lock taken above and otherwise there is no way to
|
||||
wait for FLUSH TABLES in deadlock-free fashion.
|
||||
*/
|
||||
if (open_and_lock_tables(thd, table_ref, FALSE,
|
||||
MYSQL_OPEN_IGNORE_FLUSH |
|
||||
MYSQL_OPEN_SKIP_TEMPORARY))
|
||||
DBUG_RETURN(TRUE);
|
||||
/* Table is already locked exclusively. Remove cached instances. */
|
||||
tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table_ref->db,
|
||||
table_ref->table_name, FALSE);
|
||||
}
|
||||
|
||||
DBUG_RETURN(FALSE);
|
||||
@ -385,14 +390,17 @@ static bool open_and_lock_table_for_truncate(THD *thd, TABLE_LIST *table_ref,
|
||||
@retval TRUE Error.
|
||||
*/
|
||||
|
||||
bool mysql_truncate_table(THD *thd, TABLE_LIST *table_ref)
|
||||
bool Truncate_statement::truncate_table(THD *thd, TABLE_LIST *table_ref)
|
||||
{
|
||||
int error;
|
||||
TABLE *table;
|
||||
bool error= TRUE, binlog_stmt;
|
||||
MDL_ticket *mdl_ticket= NULL;
|
||||
DBUG_ENTER("mysql_truncate_table");
|
||||
bool binlog_stmt;
|
||||
DBUG_ENTER("Truncate_statement::truncate_table");
|
||||
|
||||
/* Remove tables from the HANDLER's hash. */
|
||||
/* Initialize, or reinitialize in case of reexecution (SP). */
|
||||
m_ticket_downgrade= NULL;
|
||||
|
||||
/* Remove table from the HANDLER's hash. */
|
||||
mysql_ha_rm_tables(thd, table_ref);
|
||||
|
||||
/* If it is a temporary table, no need to take locks. */
|
||||
@ -413,14 +421,11 @@ bool mysql_truncate_table(THD *thd, TABLE_LIST *table_ref)
|
||||
{
|
||||
/*
|
||||
The engine does not support truncate-by-recreate. Open the
|
||||
table and delete all rows. In such a manner this can in fact
|
||||
open several tables if it's a temporary MyISAMMRG table.
|
||||
table and invoke the handler truncate. In such a manner this
|
||||
can in fact open several tables if it's a temporary MyISAMMRG
|
||||
table.
|
||||
*/
|
||||
if (open_and_lock_tables(thd, table_ref, FALSE,
|
||||
MYSQL_OPEN_TEMPORARY_ONLY))
|
||||
DBUG_RETURN(TRUE);
|
||||
|
||||
error= delete_all_rows(thd, table_ref->table);
|
||||
error= handler_truncate(thd, table_ref, TRUE);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -434,8 +439,7 @@ bool mysql_truncate_table(THD *thd, TABLE_LIST *table_ref)
|
||||
{
|
||||
bool hton_can_recreate;
|
||||
|
||||
if (open_and_lock_table_for_truncate(thd, table_ref,
|
||||
&hton_can_recreate, &mdl_ticket))
|
||||
if (lock_table(thd, table_ref, &hton_can_recreate))
|
||||
DBUG_RETURN(TRUE);
|
||||
|
||||
if (hton_can_recreate)
|
||||
@ -454,13 +458,18 @@ bool mysql_truncate_table(THD *thd, TABLE_LIST *table_ref)
|
||||
}
|
||||
else
|
||||
{
|
||||
error= delete_all_rows(thd, table_ref->table);
|
||||
/*
|
||||
The engine does not support truncate-by-recreate.
|
||||
Attempt to use the handler truncate method.
|
||||
*/
|
||||
error= handler_truncate(thd, table_ref, FALSE);
|
||||
|
||||
/*
|
||||
Regardless of the error status, the query must be written to the
|
||||
binary log if rows of a non-transactional table were deleted.
|
||||
All effects of a TRUNCATE TABLE operation are committed even if
|
||||
truncation fails. Thus, the query must be written to the binary
|
||||
log. The only exception is a unimplemented truncate method.
|
||||
*/
|
||||
binlog_stmt= !error || thd->transaction.stmt.modified_non_trans_table;
|
||||
binlog_stmt= !error || error != HA_ERR_WRONG_COMMAND;
|
||||
}
|
||||
|
||||
query_cache_invalidate3(thd, table_ref, FALSE);
|
||||
@ -470,50 +479,38 @@ bool mysql_truncate_table(THD *thd, TABLE_LIST *table_ref)
|
||||
if (binlog_stmt)
|
||||
error|= write_bin_log(thd, !error, thd->query(), thd->query_length());
|
||||
|
||||
/*
|
||||
All effects of a TRUNCATE TABLE operation are rolled back if a row
|
||||
by row deletion fails. Otherwise, it is automatically committed at
|
||||
the end.
|
||||
*/
|
||||
if (error)
|
||||
{
|
||||
trans_rollback_stmt(thd);
|
||||
trans_rollback(thd);
|
||||
}
|
||||
|
||||
/*
|
||||
A locked table ticket was upgraded to a exclusive lock. After the
|
||||
the query has been written to the binary log, downgrade the lock
|
||||
to a shared one.
|
||||
*/
|
||||
if (mdl_ticket)
|
||||
mdl_ticket->downgrade_exclusive_lock(MDL_SHARED_NO_READ_WRITE);
|
||||
if (m_ticket_downgrade)
|
||||
m_ticket_downgrade->downgrade_exclusive_lock(MDL_SHARED_NO_READ_WRITE);
|
||||
|
||||
DBUG_PRINT("exit", ("error: %d", error));
|
||||
DBUG_RETURN(test(error));
|
||||
DBUG_RETURN(error);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Execute a TRUNCATE statement at runtime.
|
||||
|
||||
@param thd The current thread.
|
||||
|
||||
@return FALSE on success.
|
||||
*/
|
||||
|
||||
bool Truncate_statement::execute(THD *thd)
|
||||
{
|
||||
TABLE_LIST *first_table= thd->lex->select_lex.table_list.first;
|
||||
bool res= TRUE;
|
||||
TABLE_LIST *first_table= thd->lex->select_lex.table_list.first;
|
||||
DBUG_ENTER("Truncate_statement::execute");
|
||||
|
||||
if (check_one_table_access(thd, DROP_ACL, first_table))
|
||||
goto error;
|
||||
/*
|
||||
Don't allow this within a transaction because we want to use
|
||||
re-generate table
|
||||
*/
|
||||
if (thd->in_active_multi_stmt_transaction())
|
||||
{
|
||||
my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
|
||||
ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
|
||||
goto error;
|
||||
}
|
||||
if (! (res= mysql_truncate_table(thd, first_table)))
|
||||
DBUG_RETURN(res);
|
||||
|
||||
if (! (res= truncate_table(thd, first_table)))
|
||||
my_ok(thd);
|
||||
error:
|
||||
|
||||
DBUG_RETURN(res);
|
||||
}
|
||||
|
||||
|
@ -18,13 +18,15 @@
|
||||
class THD;
|
||||
struct TABLE_LIST;
|
||||
|
||||
bool mysql_truncate_table(THD *thd, TABLE_LIST *table_ref);
|
||||
|
||||
/**
|
||||
Truncate_statement represents the TRUNCATE statement.
|
||||
*/
|
||||
class Truncate_statement : public Sql_statement
|
||||
{
|
||||
private:
|
||||
/* Set if a lock must be downgraded after truncate is done. */
|
||||
MDL_ticket *m_ticket_downgrade;
|
||||
|
||||
public:
|
||||
/**
|
||||
Constructor, used to represent a ALTER TABLE statement.
|
||||
@ -34,7 +36,7 @@ public:
|
||||
: Sql_statement(lex)
|
||||
{}
|
||||
|
||||
~Truncate_statement()
|
||||
virtual ~Truncate_statement()
|
||||
{}
|
||||
|
||||
/**
|
||||
@ -43,7 +45,20 @@ public:
|
||||
@return false on success.
|
||||
*/
|
||||
bool execute(THD *thd);
|
||||
|
||||
protected:
|
||||
/** Handle locking a base table for truncate. */
|
||||
bool lock_table(THD *, TABLE_LIST *, bool *);
|
||||
|
||||
/** Truncate table via the handler method. */
|
||||
int handler_truncate(THD *, TABLE_LIST *, bool);
|
||||
|
||||
/**
|
||||
Optimized delete of all rows by doing a full regenerate of the table.
|
||||
Depending on the storage engine, it can be accomplished through a
|
||||
drop and recreate or via the handler truncate method.
|
||||
*/
|
||||
bool truncate_table(THD *, TABLE_LIST *);
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
@ -10770,7 +10770,7 @@ truncate:
|
||||
lex->select_lex.sql_cache= SELECT_LEX::SQL_CACHE_UNSPECIFIED;
|
||||
lex->select_lex.init_order();
|
||||
YYPS->m_lock_type= TL_WRITE;
|
||||
YYPS->m_mdl_type= MDL_SHARED_NO_READ_WRITE;
|
||||
YYPS->m_mdl_type= MDL_EXCLUSIVE;
|
||||
}
|
||||
table_name
|
||||
{
|
||||
|
@ -1170,7 +1170,9 @@ enum enum_schema_table_state
|
||||
|
||||
typedef struct st_foreign_key_info
|
||||
{
|
||||
LEX_STRING *forein_id;
|
||||
LEX_STRING *foreign_id;
|
||||
LEX_STRING *foreign_db;
|
||||
LEX_STRING *foreign_table;
|
||||
LEX_STRING *referenced_db;
|
||||
LEX_STRING *referenced_table;
|
||||
LEX_STRING *update_method;
|
||||
|
@ -1650,9 +1650,9 @@ int ha_archive::end_bulk_insert()
|
||||
This is done for security reasons. In a later version we will enable this by
|
||||
allowing the user to select a different row format.
|
||||
*/
|
||||
int ha_archive::delete_all_rows()
|
||||
int ha_archive::truncate()
|
||||
{
|
||||
DBUG_ENTER("ha_archive::delete_all_rows");
|
||||
DBUG_ENTER("ha_archive::truncate");
|
||||
DBUG_RETURN(HA_ERR_WRONG_COMMAND);
|
||||
}
|
||||
|
||||
|
@ -115,7 +115,7 @@ public:
|
||||
int close(void);
|
||||
int write_row(uchar * buf);
|
||||
int real_write_row(uchar *buf, azio_stream *writer);
|
||||
int delete_all_rows();
|
||||
int truncate();
|
||||
int rnd_init(bool scan=1);
|
||||
int rnd_next(uchar *buf);
|
||||
int rnd_pos(uchar * buf, uchar *pos);
|
||||
|
@ -87,6 +87,16 @@ int ha_blackhole::create(const char *name, TABLE *table_arg,
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
/*
|
||||
Intended to support partitioning.
|
||||
Allows a particular partition to be truncated.
|
||||
*/
|
||||
int ha_blackhole::truncate()
|
||||
{
|
||||
DBUG_ENTER("ha_blackhole::truncate");
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
const char *ha_blackhole::index_type(uint key_number)
|
||||
{
|
||||
DBUG_ENTER("ha_blackhole::index_type");
|
||||
|
@ -76,6 +76,7 @@ public:
|
||||
uint max_supported_key_part_length() const { return BLACKHOLE_MAX_KEY_LENGTH; }
|
||||
int open(const char *name, int mode, uint test_if_locked);
|
||||
int close(void);
|
||||
int truncate();
|
||||
int rnd_init(bool scan);
|
||||
int rnd_next(uchar *buf);
|
||||
int rnd_pos(uchar * buf, uchar *pos);
|
||||
|
@ -356,14 +356,14 @@ int ha_example::close(void)
|
||||
is happening. buf() is a byte array of data. You can use the field
|
||||
information to extract the data from the native byte array type.
|
||||
|
||||
@details
|
||||
@details
|
||||
Example of this would be:
|
||||
@code
|
||||
@code
|
||||
for (Field **field=table->field ; *field ; field++)
|
||||
{
|
||||
...
|
||||
}
|
||||
@endcode
|
||||
@endcode
|
||||
|
||||
See ha_tina.cc for an example of extracting all of the data as strings.
|
||||
ha_berekly.cc has an example of how to store it intact by "packing" it
|
||||
@ -375,7 +375,7 @@ int ha_example::close(void)
|
||||
Called from item_sum.cc, item_sum.cc, sql_acl.cc, sql_insert.cc,
|
||||
sql_insert.cc, sql_select.cc, sql_table.cc, sql_udf.cc, and sql_update.cc.
|
||||
|
||||
@see
|
||||
@see
|
||||
item_sum.cc, item_sum.cc, sql_acl.cc, sql_insert.cc,
|
||||
sql_insert.cc, sql_select.cc, sql_table.cc, sql_udf.cc and sql_update.cc
|
||||
*/
|
||||
@ -400,19 +400,19 @@ int ha_example::write_row(uchar *buf)
|
||||
Keep in mind that the server can do updates based on ordering if an ORDER BY
|
||||
clause was used. Consecutive ordering is not guaranteed.
|
||||
|
||||
@details
|
||||
@details
|
||||
Currently new_data will not have an updated auto_increament record, or
|
||||
and updated timestamp field. You can do these for example by doing:
|
||||
@code
|
||||
@code
|
||||
if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE)
|
||||
table->timestamp_field->set_time();
|
||||
if (table->next_number_field && record == table->record[0])
|
||||
update_auto_increment();
|
||||
@endcode
|
||||
@endcode
|
||||
|
||||
Called from sql_select.cc, sql_acl.cc, sql_update.cc, and sql_insert.cc.
|
||||
|
||||
@see
|
||||
@see
|
||||
sql_select.cc, sql_acl.cc, sql_update.cc and sql_insert.cc
|
||||
*/
|
||||
int ha_example::update_row(const uchar *old_data, uchar *new_data)
|
||||
@ -507,10 +507,10 @@ int ha_example::index_prev(uchar *buf)
|
||||
@brief
|
||||
index_first() asks for the first key in the index.
|
||||
|
||||
@details
|
||||
@details
|
||||
Called from opt_range.cc, opt_sum.cc, sql_handler.cc, and sql_select.cc.
|
||||
|
||||
@see
|
||||
@see
|
||||
opt_range.cc, opt_sum.cc, sql_handler.cc and sql_select.cc
|
||||
*/
|
||||
int ha_example::index_first(uchar *buf)
|
||||
@ -528,10 +528,10 @@ int ha_example::index_first(uchar *buf)
|
||||
@brief
|
||||
index_last() asks for the last key in the index.
|
||||
|
||||
@details
|
||||
@details
|
||||
Called from opt_range.cc, opt_sum.cc, sql_handler.cc, and sql_select.cc.
|
||||
|
||||
@see
|
||||
@see
|
||||
opt_range.cc, opt_sum.cc, sql_handler.cc and sql_select.cc
|
||||
*/
|
||||
int ha_example::index_last(uchar *buf)
|
||||
@ -551,11 +551,11 @@ int ha_example::index_last(uchar *buf)
|
||||
scan. See the example in the introduction at the top of this file to see when
|
||||
rnd_init() is called.
|
||||
|
||||
@details
|
||||
@details
|
||||
Called from filesort.cc, records.cc, sql_handler.cc, sql_select.cc, sql_table.cc,
|
||||
and sql_update.cc.
|
||||
|
||||
@see
|
||||
@see
|
||||
filesort.cc, records.cc, sql_handler.cc, sql_select.cc, sql_table.cc and sql_update.cc
|
||||
*/
|
||||
int ha_example::rnd_init(bool scan)
|
||||
@ -578,11 +578,11 @@ int ha_example::rnd_end()
|
||||
The Field structure for the table is the key to getting data into buf
|
||||
in a manner that will allow the server to understand it.
|
||||
|
||||
@details
|
||||
@details
|
||||
Called from filesort.cc, records.cc, sql_handler.cc, sql_select.cc, sql_table.cc,
|
||||
and sql_update.cc.
|
||||
|
||||
@see
|
||||
@see
|
||||
filesort.cc, records.cc, sql_handler.cc, sql_select.cc, sql_table.cc and sql_update.cc
|
||||
*/
|
||||
int ha_example::rnd_next(uchar *buf)
|
||||
@ -602,11 +602,11 @@ int ha_example::rnd_next(uchar *buf)
|
||||
position() is called after each call to rnd_next() if the data needs
|
||||
to be ordered. You can do something like the following to store
|
||||
the position:
|
||||
@code
|
||||
@code
|
||||
my_store_ptr(ref, ref_length, current_position);
|
||||
@endcode
|
||||
@endcode
|
||||
|
||||
@details
|
||||
@details
|
||||
The server uses ref to store data. ref_length in the above case is
|
||||
the size needed to store current_position. ref is just a byte array
|
||||
that the server will maintain. If you are using offsets to mark rows, then
|
||||
@ -615,7 +615,7 @@ int ha_example::rnd_next(uchar *buf)
|
||||
|
||||
Called from filesort.cc, sql_select.cc, sql_delete.cc, and sql_update.cc.
|
||||
|
||||
@see
|
||||
@see
|
||||
filesort.cc, sql_select.cc, sql_delete.cc and sql_update.cc
|
||||
*/
|
||||
void ha_example::position(const uchar *record)
|
||||
@ -632,10 +632,10 @@ void ha_example::position(const uchar *record)
|
||||
ref. You can use ha_get_ptr(pos,ref_length) to retrieve whatever key
|
||||
or position you saved when position() was called.
|
||||
|
||||
@details
|
||||
@details
|
||||
Called from filesort.cc, records.cc, sql_insert.cc, sql_select.cc, and sql_update.cc.
|
||||
|
||||
@see
|
||||
@see
|
||||
filesort.cc, records.cc, sql_insert.cc, sql_select.cc and sql_update.cc
|
||||
*/
|
||||
int ha_example::rnd_pos(uchar *buf, uchar *pos)
|
||||
@ -655,15 +655,15 @@ int ha_example::rnd_pos(uchar *buf, uchar *pos)
|
||||
::info() is used to return information to the optimizer. See my_base.h for
|
||||
the complete description.
|
||||
|
||||
@details
|
||||
@details
|
||||
Currently this table handler doesn't implement most of the fields really needed.
|
||||
SHOW also makes use of this data.
|
||||
|
||||
You will probably want to have the following in your code:
|
||||
@code
|
||||
@code
|
||||
if (records < 2)
|
||||
records = 2;
|
||||
@endcode
|
||||
@endcode
|
||||
The reason is that the server will optimize for cases of only a single
|
||||
record. If, in a table scan, you don't know the number of records, it
|
||||
will probably be better to set records to two so you can return as many
|
||||
@ -682,7 +682,7 @@ int ha_example::rnd_pos(uchar *buf, uchar *pos)
|
||||
sql_select.cc, sql_select.cc, sql_show.cc, sql_show.cc, sql_show.cc, sql_show.cc,
|
||||
sql_table.cc, sql_union.cc, and sql_update.cc.
|
||||
|
||||
@see
|
||||
@see
|
||||
filesort.cc, ha_heap.cc, item_sum.cc, opt_sum.cc, sql_delete.cc, sql_delete.cc,
|
||||
sql_derived.cc, sql_select.cc, sql_select.cc, sql_select.cc, sql_select.cc,
|
||||
sql_select.cc, sql_show.cc, sql_show.cc, sql_show.cc, sql_show.cc, sql_table.cc,
|
||||
@ -716,14 +716,14 @@ int ha_example::extra(enum ha_extra_function operation)
|
||||
Used to delete all rows in a table, including cases of truncate and cases where
|
||||
the optimizer realizes that all rows will be removed as a result of an SQL statement.
|
||||
|
||||
@details
|
||||
@details
|
||||
Called from item_sum.cc by Item_func_group_concat::clear(),
|
||||
Item_sum_count_distinct::clear(), and Item_func_group_concat::clear().
|
||||
Called from sql_delete.cc by mysql_delete().
|
||||
Called from sql_select.cc by JOIN::reinit().
|
||||
Called from sql_union.cc by st_select_lex_unit::exec().
|
||||
|
||||
@see
|
||||
@see
|
||||
Item_func_group_concat::clear(), Item_sum_count_distinct::clear() and
|
||||
Item_func_group_concat::clear() in item_sum.cc;
|
||||
mysql_delete() in sql_delete.cc;
|
||||
@ -737,6 +737,29 @@ int ha_example::delete_all_rows()
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
@brief
|
||||
Used for handler specific truncate table. The table is locked in
|
||||
exclusive mode and handler is responsible for reseting the auto-
|
||||
increment counter.
|
||||
|
||||
@details
|
||||
Called from Truncate_statement::handler_truncate.
|
||||
Not used if the handlerton supports HTON_CAN_RECREATE, unless this
|
||||
engine can be used as a partition. In this case, it is invoked when
|
||||
a particular partition is to be truncated.
|
||||
|
||||
@see
|
||||
Truncate_statement in sql_truncate.cc
|
||||
Remarks in handler::truncate.
|
||||
*/
|
||||
int ha_example::truncate()
|
||||
{
|
||||
DBUG_ENTER("ha_example::truncate");
|
||||
DBUG_RETURN(HA_ERR_WRONG_COMMAND);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
@brief
|
||||
This create a lock on the table. If you are implementing a storage engine
|
||||
@ -745,11 +768,11 @@ int ha_example::delete_all_rows()
|
||||
here. Hint: Read the section "locking functions for mysql" in lock.cc to understand
|
||||
this.
|
||||
|
||||
@details
|
||||
@details
|
||||
Called from lock.cc by lock_external() and unlock_external(). Also called
|
||||
from sql_table.cc by copy_data_between_tables().
|
||||
|
||||
@see
|
||||
@see
|
||||
lock.cc by lock_external() and unlock_external() in lock.cc;
|
||||
the section "locking functions for mysql" in lock.cc;
|
||||
copy_data_between_tables() in sql_table.cc.
|
||||
@ -767,7 +790,7 @@ int ha_example::external_lock(THD *thd, int lock_type)
|
||||
should be needed for the table. For updates/deletes/inserts we get WRITE
|
||||
locks, for SELECT... we get read locks.
|
||||
|
||||
@details
|
||||
@details
|
||||
Before adding the lock into the table lock handler (see thr_lock.c),
|
||||
mysqld calls store lock with the requested locks. Store lock can now
|
||||
modify a write lock to a read lock (or some other lock), ignore the
|
||||
@ -790,12 +813,12 @@ int ha_example::external_lock(THD *thd, int lock_type)
|
||||
|
||||
Called from lock.cc by get_lock_data().
|
||||
|
||||
@note
|
||||
@note
|
||||
In this method one should NEVER rely on table->in_use, it may, in fact,
|
||||
refer to a different thread! (this happens if get_lock_data() is called
|
||||
from mysql_lock_abort_for_thread() function)
|
||||
|
||||
@see
|
||||
@see
|
||||
get_lock_data() in lock.cc
|
||||
*/
|
||||
THR_LOCK_DATA **ha_example::store_lock(THD *thd,
|
||||
@ -816,7 +839,7 @@ THR_LOCK_DATA **ha_example::store_lock(THD *thd,
|
||||
shared references released). The variable name will just be the name of
|
||||
the table. You will need to remove any files you have created at this point.
|
||||
|
||||
@details
|
||||
@details
|
||||
If you do not implement this, the default delete_table() is called from
|
||||
handler.cc and it will delete all files with the file extensions returned
|
||||
by bas_ext().
|
||||
@ -825,7 +848,7 @@ THR_LOCK_DATA **ha_example::store_lock(THD *thd,
|
||||
during create if the table_flag HA_DROP_BEFORE_CREATE was specified for
|
||||
the storage engine.
|
||||
|
||||
@see
|
||||
@see
|
||||
delete_table and ha_create_table() in handler.cc
|
||||
*/
|
||||
int ha_example::delete_table(const char *name)
|
||||
|
@ -245,6 +245,7 @@ public:
|
||||
int extra(enum ha_extra_function operation);
|
||||
int external_lock(THD *thd, int lock_type); ///< required
|
||||
int delete_all_rows(void);
|
||||
int truncate();
|
||||
ha_rows records_in_range(uint inx, key_range *min_key,
|
||||
key_range *max_key);
|
||||
int delete_table(const char *from);
|
||||
|
@ -3041,6 +3041,16 @@ int ha_federated::delete_all_rows()
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Used to manually truncate the table via a delete of all rows in a table.
|
||||
*/
|
||||
|
||||
int ha_federated::truncate()
|
||||
{
|
||||
return delete_all_rows();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
The idea with handler::store_lock() is the following:
|
||||
|
||||
|
@ -248,6 +248,7 @@ public:
|
||||
int optimize(THD* thd, HA_CHECK_OPT* check_opt);
|
||||
|
||||
int delete_all_rows(void);
|
||||
int truncate();
|
||||
int create(const char *name, TABLE *form,
|
||||
HA_CREATE_INFO *create_info); //required
|
||||
ha_rows records_in_range(uint inx, key_range *start_key,
|
||||
|
@ -455,6 +455,13 @@ int ha_heap::delete_all_rows()
|
||||
}
|
||||
|
||||
|
||||
int ha_heap::truncate()
|
||||
{
|
||||
int error= delete_all_rows();
|
||||
return error ? error : reset_auto_increment(0);
|
||||
}
|
||||
|
||||
|
||||
int ha_heap::reset_auto_increment(ulonglong value)
|
||||
{
|
||||
file->s->auto_increment= value;
|
||||
|
@ -99,6 +99,7 @@ public:
|
||||
int reset();
|
||||
int external_lock(THD *thd, int lock_type);
|
||||
int delete_all_rows(void);
|
||||
int truncate();
|
||||
int reset_auto_increment(ulonglong value);
|
||||
int disable_indexes(uint mode);
|
||||
int enable_indexes(uint mode);
|
||||
|
@ -494,10 +494,10 @@ int ha_ibmdb2i::get_foreign_key_list(THD *thd, List<FOREIGN_KEY_INFO> *f_key_lis
|
||||
|
||||
convFromEbcdic(FKCstDef->CstName.Name, convName,FKCstDef->CstName.Len);
|
||||
if (convName[0] == '"') // If quoted, exclude quotes.
|
||||
f_key_info.forein_id = thd_make_lex_string(thd, 0,
|
||||
f_key_info.foreign_id = thd_make_lex_string(thd, 0,
|
||||
convName + 1, (uint) (FKCstDef->CstName.Len - 2), 1);
|
||||
else // Not quoted
|
||||
f_key_info.forein_id = thd_make_lex_string(thd, 0,
|
||||
f_key_info.foreign_id = thd_make_lex_string(thd, 0,
|
||||
convName, (uint) FKCstDef->CstName.Len, 1);
|
||||
|
||||
/* Process the names of the foreign keys. */
|
||||
|
@ -1806,6 +1806,13 @@ int ha_ibmdb2i::delete_all_rows()
|
||||
}
|
||||
|
||||
|
||||
int ha_ibmdb2i::truncate()
|
||||
{
|
||||
int error = delete_all_rows();
|
||||
return error ? error : reset_auto_increment(0);
|
||||
}
|
||||
|
||||
|
||||
int ha_ibmdb2i::external_lock(THD *thd, int lock_type)
|
||||
{
|
||||
int rc = 0;
|
||||
|
@ -7081,33 +7081,21 @@ Deletes all rows of an InnoDB table.
|
||||
@return error number */
|
||||
UNIV_INTERN
|
||||
int
|
||||
ha_innobase::delete_all_rows(void)
|
||||
ha_innobase::truncate(void)
|
||||
/*==============================*/
|
||||
{
|
||||
int error;
|
||||
|
||||
DBUG_ENTER("ha_innobase::delete_all_rows");
|
||||
DBUG_ENTER("ha_innobase::truncate");
|
||||
|
||||
/* Get the transaction associated with the current thd, or create one
|
||||
if not yet created, and update prebuilt->trx */
|
||||
|
||||
update_thd(ha_thd());
|
||||
|
||||
if (thd_sql_command(user_thd) != SQLCOM_TRUNCATE) {
|
||||
fallback:
|
||||
/* We only handle TRUNCATE TABLE t as a special case.
|
||||
DELETE FROM t will have to use ha_innobase::delete_row(),
|
||||
because DELETE is transactional while TRUNCATE is not. */
|
||||
DBUG_RETURN(my_errno=HA_ERR_WRONG_COMMAND);
|
||||
}
|
||||
|
||||
/* Truncate the table in InnoDB */
|
||||
|
||||
error = row_truncate_table_for_mysql(prebuilt->table, prebuilt->trx);
|
||||
if (error == DB_ERROR) {
|
||||
/* Cannot truncate; resort to ha_innobase::delete_row() */
|
||||
goto fallback;
|
||||
}
|
||||
|
||||
error = convert_error_code_to_mysql(error, prebuilt->table->flags,
|
||||
NULL);
|
||||
@ -8315,136 +8303,199 @@ ha_innobase::get_foreign_key_create_info(void)
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************//**
|
||||
Maps a InnoDB foreign key constraint to a equivalent MySQL foreign key info.
|
||||
@return pointer to foreign key info */
|
||||
static
|
||||
FOREIGN_KEY_INFO*
|
||||
get_foreign_key_info(
|
||||
/*=================*/
|
||||
THD* thd, /*!< in: user thread handle */
|
||||
dict_foreign_t* foreign) /*!< in: foreign key constraint */
|
||||
{
|
||||
FOREIGN_KEY_INFO f_key_info;
|
||||
FOREIGN_KEY_INFO* pf_key_info;
|
||||
uint i = 0;
|
||||
ulint len;
|
||||
char tmp_buff[NAME_LEN+1];
|
||||
char name_buff[NAME_LEN+1];
|
||||
const char* ptr;
|
||||
LEX_STRING* referenced_key_name;
|
||||
LEX_STRING* name = NULL;
|
||||
|
||||
ptr = dict_remove_db_name(foreign->id);
|
||||
f_key_info.foreign_id = thd_make_lex_string(thd, 0, ptr,
|
||||
(uint) strlen(ptr), 1);
|
||||
|
||||
/* Name format: database name, '/', table name, '\0' */
|
||||
|
||||
/* Referenced (parent) database name */
|
||||
len = dict_get_db_name_len(foreign->referenced_table_name);
|
||||
ut_a(len < sizeof(tmp_buff));
|
||||
ut_memcpy(tmp_buff, foreign->referenced_table_name, len);
|
||||
tmp_buff[len] = 0;
|
||||
|
||||
len = filename_to_tablename(tmp_buff, name_buff, sizeof(name_buff));
|
||||
f_key_info.referenced_db = thd_make_lex_string(thd, 0, name_buff, len, 1);
|
||||
|
||||
/* Referenced (parent) table name */
|
||||
ptr = dict_remove_db_name(foreign->referenced_table_name);
|
||||
len = filename_to_tablename(ptr, name_buff, sizeof(name));
|
||||
f_key_info.referenced_table = thd_make_lex_string(thd, 0, name_buff, len, 1);
|
||||
|
||||
/* Dependent (child) database name */
|
||||
len = dict_get_db_name_len(foreign->foreign_table_name);
|
||||
ut_a(len < sizeof(tmp_buff));
|
||||
ut_memcpy(tmp_buff, foreign->foreign_table_name, len);
|
||||
tmp_buff[len] = 0;
|
||||
|
||||
len = filename_to_tablename(tmp_buff, name_buff, sizeof(name_buff));
|
||||
f_key_info.foreign_db = thd_make_lex_string(thd, 0, name_buff, len, 1);
|
||||
|
||||
/* Dependent (child) table name */
|
||||
ptr = dict_remove_db_name(foreign->foreign_table_name);
|
||||
len = filename_to_tablename(ptr, name_buff, sizeof(name_buff));
|
||||
f_key_info.foreign_table = thd_make_lex_string(thd, 0, name_buff, len, 1);
|
||||
|
||||
do {
|
||||
ptr = foreign->foreign_col_names[i];
|
||||
name = thd_make_lex_string(thd, name, ptr,
|
||||
(uint) strlen(ptr), 1);
|
||||
f_key_info.foreign_fields.push_back(name);
|
||||
ptr = foreign->referenced_col_names[i];
|
||||
name = thd_make_lex_string(thd, name, ptr,
|
||||
(uint) strlen(ptr), 1);
|
||||
f_key_info.referenced_fields.push_back(name);
|
||||
} while (++i < foreign->n_fields);
|
||||
|
||||
if (foreign->type & DICT_FOREIGN_ON_DELETE_CASCADE) {
|
||||
len = 7;
|
||||
ptr = "CASCADE";
|
||||
} else if (foreign->type & DICT_FOREIGN_ON_DELETE_SET_NULL) {
|
||||
len = 8;
|
||||
ptr = "SET NULL";
|
||||
} else if (foreign->type & DICT_FOREIGN_ON_DELETE_NO_ACTION) {
|
||||
len = 9;
|
||||
ptr = "NO ACTION";
|
||||
} else {
|
||||
len = 8;
|
||||
ptr = "RESTRICT";
|
||||
}
|
||||
|
||||
f_key_info.delete_method = thd_make_lex_string(thd,
|
||||
f_key_info.delete_method,
|
||||
ptr, len, 1);
|
||||
|
||||
if (foreign->type & DICT_FOREIGN_ON_UPDATE_CASCADE) {
|
||||
len = 7;
|
||||
ptr = "CASCADE";
|
||||
} else if (foreign->type & DICT_FOREIGN_ON_UPDATE_SET_NULL) {
|
||||
len = 8;
|
||||
ptr = "SET NULL";
|
||||
} else if (foreign->type & DICT_FOREIGN_ON_UPDATE_NO_ACTION) {
|
||||
len = 9;
|
||||
ptr = "NO ACTION";
|
||||
} else {
|
||||
len = 8;
|
||||
ptr = "RESTRICT";
|
||||
}
|
||||
|
||||
f_key_info.update_method = thd_make_lex_string(thd,
|
||||
f_key_info.update_method,
|
||||
ptr, len, 1);
|
||||
|
||||
if (foreign->referenced_index && foreign->referenced_index->name) {
|
||||
referenced_key_name = thd_make_lex_string(thd,
|
||||
f_key_info.referenced_key_name,
|
||||
foreign->referenced_index->name,
|
||||
(uint) strlen(foreign->referenced_index->name),
|
||||
1);
|
||||
} else {
|
||||
referenced_key_name = NULL;
|
||||
}
|
||||
|
||||
f_key_info.referenced_key_name = referenced_key_name;
|
||||
|
||||
pf_key_info = (FOREIGN_KEY_INFO *) thd_memdup(thd, &f_key_info,
|
||||
sizeof(FOREIGN_KEY_INFO));
|
||||
|
||||
return(pf_key_info);
|
||||
}
|
||||
|
||||
/*******************************************************************//**
|
||||
Gets the list of foreign keys in this table.
|
||||
@return always 0, that is, always succeeds */
|
||||
UNIV_INTERN
|
||||
int
|
||||
ha_innobase::get_foreign_key_list(THD *thd, List<FOREIGN_KEY_INFO> *f_key_list)
|
||||
ha_innobase::get_foreign_key_list(
|
||||
/*==============================*/
|
||||
THD* thd, /*!< in: user thread handle */
|
||||
List<FOREIGN_KEY_INFO>* f_key_list) /*!< out: foreign key list */
|
||||
{
|
||||
dict_foreign_t* foreign;
|
||||
FOREIGN_KEY_INFO* pf_key_info;
|
||||
dict_foreign_t* foreign;
|
||||
|
||||
DBUG_ENTER("get_foreign_key_list");
|
||||
ut_a(prebuilt != NULL);
|
||||
update_thd(ha_thd());
|
||||
prebuilt->trx->op_info = (char*)"getting list of foreign keys";
|
||||
trx_search_latch_release_if_reserved(prebuilt->trx);
|
||||
mutex_enter(&(dict_sys->mutex));
|
||||
foreign = UT_LIST_GET_FIRST(prebuilt->table->foreign_list);
|
||||
ut_a(prebuilt != NULL);
|
||||
update_thd(ha_thd());
|
||||
|
||||
while (foreign != NULL) {
|
||||
uint i;
|
||||
FOREIGN_KEY_INFO f_key_info;
|
||||
LEX_STRING *name= 0;
|
||||
uint ulen;
|
||||
char uname[NAME_LEN+1]; /* Unencoded name */
|
||||
char db_name[NAME_LEN+1];
|
||||
const char *tmp_buff;
|
||||
prebuilt->trx->op_info = "getting list of foreign keys";
|
||||
|
||||
tmp_buff= foreign->id;
|
||||
i= 0;
|
||||
while (tmp_buff[i] != '/')
|
||||
i++;
|
||||
tmp_buff+= i + 1;
|
||||
f_key_info.forein_id = thd_make_lex_string(thd, 0,
|
||||
tmp_buff, (uint) strlen(tmp_buff), 1);
|
||||
tmp_buff= foreign->referenced_table_name;
|
||||
trx_search_latch_release_if_reserved(prebuilt->trx);
|
||||
|
||||
/* Database name */
|
||||
i= 0;
|
||||
while (tmp_buff[i] != '/')
|
||||
{
|
||||
db_name[i]= tmp_buff[i];
|
||||
i++;
|
||||
}
|
||||
db_name[i]= 0;
|
||||
ulen= filename_to_tablename(db_name, uname, sizeof(uname));
|
||||
f_key_info.referenced_db = thd_make_lex_string(thd, 0,
|
||||
uname, ulen, 1);
|
||||
mutex_enter(&(dict_sys->mutex));
|
||||
|
||||
/* Table name */
|
||||
tmp_buff+= i + 1;
|
||||
ulen= filename_to_tablename(tmp_buff, uname, sizeof(uname));
|
||||
f_key_info.referenced_table = thd_make_lex_string(thd, 0,
|
||||
uname, ulen, 1);
|
||||
for (foreign = UT_LIST_GET_FIRST(prebuilt->table->foreign_list);
|
||||
foreign != NULL;
|
||||
foreign = UT_LIST_GET_NEXT(referenced_list, foreign)) {
|
||||
pf_key_info = get_foreign_key_info(thd, foreign);
|
||||
if (pf_key_info) {
|
||||
f_key_list->push_back(pf_key_info);
|
||||
}
|
||||
}
|
||||
|
||||
for (i= 0;;) {
|
||||
tmp_buff= foreign->foreign_col_names[i];
|
||||
name = thd_make_lex_string(thd, name,
|
||||
tmp_buff, (uint) strlen(tmp_buff), 1);
|
||||
f_key_info.foreign_fields.push_back(name);
|
||||
tmp_buff= foreign->referenced_col_names[i];
|
||||
name = thd_make_lex_string(thd, name,
|
||||
tmp_buff, (uint) strlen(tmp_buff), 1);
|
||||
f_key_info.referenced_fields.push_back(name);
|
||||
if (++i >= foreign->n_fields)
|
||||
break;
|
||||
}
|
||||
mutex_exit(&(dict_sys->mutex));
|
||||
|
||||
ulong length;
|
||||
if (foreign->type & DICT_FOREIGN_ON_DELETE_CASCADE)
|
||||
{
|
||||
length=7;
|
||||
tmp_buff= "CASCADE";
|
||||
}
|
||||
else if (foreign->type & DICT_FOREIGN_ON_DELETE_SET_NULL)
|
||||
{
|
||||
length=8;
|
||||
tmp_buff= "SET NULL";
|
||||
}
|
||||
else if (foreign->type & DICT_FOREIGN_ON_DELETE_NO_ACTION)
|
||||
{
|
||||
length=9;
|
||||
tmp_buff= "NO ACTION";
|
||||
}
|
||||
else
|
||||
{
|
||||
length=8;
|
||||
tmp_buff= "RESTRICT";
|
||||
}
|
||||
f_key_info.delete_method = thd_make_lex_string(
|
||||
thd, f_key_info.delete_method, tmp_buff, length, 1);
|
||||
prebuilt->trx->op_info = "";
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
if (foreign->type & DICT_FOREIGN_ON_UPDATE_CASCADE)
|
||||
{
|
||||
length=7;
|
||||
tmp_buff= "CASCADE";
|
||||
}
|
||||
else if (foreign->type & DICT_FOREIGN_ON_UPDATE_SET_NULL)
|
||||
{
|
||||
length=8;
|
||||
tmp_buff= "SET NULL";
|
||||
}
|
||||
else if (foreign->type & DICT_FOREIGN_ON_UPDATE_NO_ACTION)
|
||||
{
|
||||
length=9;
|
||||
tmp_buff= "NO ACTION";
|
||||
}
|
||||
else
|
||||
{
|
||||
length=8;
|
||||
tmp_buff= "RESTRICT";
|
||||
}
|
||||
f_key_info.update_method = thd_make_lex_string(
|
||||
thd, f_key_info.update_method, tmp_buff, length, 1);
|
||||
if (foreign->referenced_index &&
|
||||
foreign->referenced_index->name)
|
||||
{
|
||||
f_key_info.referenced_key_name = thd_make_lex_string(
|
||||
thd, f_key_info.referenced_key_name,
|
||||
foreign->referenced_index->name,
|
||||
(uint) strlen(foreign->referenced_index->name), 1);
|
||||
}
|
||||
else
|
||||
f_key_info.referenced_key_name= 0;
|
||||
/*******************************************************************//**
|
||||
Gets the set of foreign keys where this table is the referenced table.
|
||||
@return always 0, that is, always succeeds */
|
||||
UNIV_INTERN
|
||||
int
|
||||
ha_innobase::get_parent_foreign_key_list(
|
||||
/*=====================================*/
|
||||
THD* thd, /*!< in: user thread handle */
|
||||
List<FOREIGN_KEY_INFO>* f_key_list) /*!< out: foreign key list */
|
||||
{
|
||||
FOREIGN_KEY_INFO* pf_key_info;
|
||||
dict_foreign_t* foreign;
|
||||
|
||||
FOREIGN_KEY_INFO *pf_key_info = (FOREIGN_KEY_INFO *)
|
||||
thd_memdup(thd, &f_key_info, sizeof(FOREIGN_KEY_INFO));
|
||||
f_key_list->push_back(pf_key_info);
|
||||
foreign = UT_LIST_GET_NEXT(foreign_list, foreign);
|
||||
}
|
||||
mutex_exit(&(dict_sys->mutex));
|
||||
prebuilt->trx->op_info = (char*)"";
|
||||
ut_a(prebuilt != NULL);
|
||||
update_thd(ha_thd());
|
||||
|
||||
DBUG_RETURN(0);
|
||||
prebuilt->trx->op_info = "getting list of referencing foreign keys";
|
||||
|
||||
trx_search_latch_release_if_reserved(prebuilt->trx);
|
||||
|
||||
mutex_enter(&(dict_sys->mutex));
|
||||
|
||||
for (foreign = UT_LIST_GET_FIRST(prebuilt->table->referenced_list);
|
||||
foreign != NULL;
|
||||
foreign = UT_LIST_GET_NEXT(referenced_list, foreign)) {
|
||||
pf_key_info = get_foreign_key_info(thd, foreign);
|
||||
if (pf_key_info) {
|
||||
f_key_list->push_back(pf_key_info);
|
||||
}
|
||||
}
|
||||
|
||||
mutex_exit(&(dict_sys->mutex));
|
||||
|
||||
prebuilt->trx->op_info = "";
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
/*****************************************************************//**
|
||||
|
@ -178,13 +178,15 @@ class ha_innobase: public handler
|
||||
void update_create_info(HA_CREATE_INFO* create_info);
|
||||
int create(const char *name, register TABLE *form,
|
||||
HA_CREATE_INFO *create_info);
|
||||
int delete_all_rows();
|
||||
int truncate();
|
||||
int delete_table(const char *name);
|
||||
int rename_table(const char* from, const char* to);
|
||||
int check(THD* thd, HA_CHECK_OPT* check_opt);
|
||||
char* update_table_comment(const char* comment);
|
||||
char* get_foreign_key_create_info();
|
||||
int get_foreign_key_list(THD *thd, List<FOREIGN_KEY_INFO> *f_key_list);
|
||||
int get_parent_foreign_key_list(THD *thd,
|
||||
List<FOREIGN_KEY_INFO> *f_key_list);
|
||||
bool can_switch_engines();
|
||||
uint referenced_by_foreign_key();
|
||||
void free_foreign_key_create_info(char* str);
|
||||
|
@ -2985,8 +2985,7 @@ next_rec:
|
||||
dict_table_change_id_in_cache(table, new_id);
|
||||
}
|
||||
|
||||
/* MySQL calls ha_innobase::reset_auto_increment() which does
|
||||
the same thing. */
|
||||
/* Reset auto-increment. */
|
||||
dict_table_autoinc_lock(table);
|
||||
dict_table_autoinc_initialize(table, 1);
|
||||
dict_table_autoinc_unlock(table);
|
||||
|
@ -1788,6 +1788,18 @@ int ha_myisam::delete_all_rows()
|
||||
return mi_delete_all_rows(file);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Intended to support partitioning.
|
||||
Allows a particular partition to be truncated.
|
||||
*/
|
||||
|
||||
int ha_myisam::truncate()
|
||||
{
|
||||
int error= delete_all_rows();
|
||||
return error ? error : reset_auto_increment(0);
|
||||
}
|
||||
|
||||
int ha_myisam::reset_auto_increment(ulonglong value)
|
||||
{
|
||||
file->s->state.auto_increment= value;
|
||||
|
@ -107,6 +107,7 @@ class ha_myisam: public handler
|
||||
int reset(void);
|
||||
int external_lock(THD *thd, int lock_type);
|
||||
int delete_all_rows(void);
|
||||
int truncate();
|
||||
int reset_auto_increment(ulonglong value);
|
||||
int disable_indexes(uint mode);
|
||||
int enable_indexes(uint mode);
|
||||
|
@ -478,8 +478,31 @@ int ha_myisammrg::add_children_list(void)
|
||||
/* Set the expected table version, to not cause spurious re-prepare. */
|
||||
child_l->set_table_ref_id(mrg_child_def->get_child_table_ref_type(),
|
||||
mrg_child_def->get_child_def_version());
|
||||
/* Use the same metadata lock type for children. */
|
||||
child_l->mdl_request.set_type(parent_l->mdl_request.type);
|
||||
/*
|
||||
For statements which acquire a SNW metadata lock on a parent table and
|
||||
then later try to upgrade it to an X lock (e.g. ALTER TABLE), SNW
|
||||
locks should be also taken on the children tables.
|
||||
|
||||
Otherwise we end up in a situation where the thread trying to upgrade SNW
|
||||
to X lock on the parent also holds a SR metadata lock and a read
|
||||
thr_lock.c lock on the child. As a result, another thread might be
|
||||
blocked on the thr_lock.c lock for the child after successfully acquiring
|
||||
a SR or SW metadata lock on it. If at the same time this second thread
|
||||
has a shared metadata lock on the parent table or there is some other
|
||||
thread which has a shared metadata lock on the parent and is waiting for
|
||||
this second thread, we get a deadlock. This deadlock cannot be properly
|
||||
detected by the MDL subsystem as part of the waiting happens within
|
||||
thr_lock.c. By taking SNW locks on the child tables we ensure that any
|
||||
thread which waits for a thread doing SNW -> X upgrade, does this within
|
||||
the MDL subsystem and thus potential deadlocks are exposed to the deadlock
|
||||
detector.
|
||||
|
||||
We don't do the same thing for SNRW locks as this would allow
|
||||
DDL on implicitly locked underlying tables of a MERGE table.
|
||||
*/
|
||||
if (! thd->locked_tables_mode &&
|
||||
parent_l->mdl_request.type == MDL_SHARED_NO_WRITE)
|
||||
child_l->mdl_request.set_type(MDL_SHARED_NO_WRITE);
|
||||
/* Link TABLE_LIST object into the children list. */
|
||||
if (this->children_last_l)
|
||||
child_l->prev_global= this->children_last_l;
|
||||
@ -1203,6 +1226,22 @@ ha_rows ha_myisammrg::records_in_range(uint inx, key_range *min_key,
|
||||
}
|
||||
|
||||
|
||||
int ha_myisammrg::truncate()
|
||||
{
|
||||
int err= 0;
|
||||
MYRG_TABLE *table;
|
||||
DBUG_ENTER("ha_myisammrg::truncate");
|
||||
|
||||
for (table= file->open_tables; table != file->end_table; table++)
|
||||
{
|
||||
if ((err= mi_delete_all_rows(table->table)))
|
||||
break;
|
||||
}
|
||||
|
||||
DBUG_RETURN(err);
|
||||
}
|
||||
|
||||
|
||||
int ha_myisammrg::info(uint flag)
|
||||
{
|
||||
MYMERGE_INFO mrg_info;
|
||||
|
@ -131,6 +131,7 @@ public:
|
||||
int rnd_pos(uchar * buf, uchar *pos);
|
||||
void position(const uchar *record);
|
||||
ha_rows records_in_range(uint inx, key_range *min_key, key_range *max_key);
|
||||
int truncate();
|
||||
int info(uint);
|
||||
int reset(void);
|
||||
int extra(enum ha_extra_function operation);
|
||||
|
@ -345,6 +345,11 @@ int ha_perfschema::delete_all_rows(void)
|
||||
DBUG_RETURN(result);
|
||||
}
|
||||
|
||||
int ha_perfschema::truncate()
|
||||
{
|
||||
return delete_all_rows();
|
||||
}
|
||||
|
||||
THR_LOCK_DATA **ha_perfschema::store_lock(THD *thd,
|
||||
THR_LOCK_DATA **to,
|
||||
enum thr_lock_type lock_type)
|
||||
|
@ -127,6 +127,8 @@ public:
|
||||
|
||||
int delete_all_rows(void);
|
||||
|
||||
int truncate();
|
||||
|
||||
int delete_table(const char *from);
|
||||
|
||||
int rename_table(const char * from, const char * to);
|
||||
|
Loading…
x
Reference in New Issue
Block a user