MDEV-5574 Set AUTO_INCREMENT below max value of column.

Update InnoDB to 5.6.14
Apply MySQL-5.6 hack for MySQL Bug#16434374
Move Aria-only HA_RTREE_INDEX from my_base.h to maria_def.h (breaks an assert in InnoDB)
Fix InnoDB memory leak
This commit is contained in:
Sergei Golubchik 2014-02-01 09:33:26 +01:00
commit 27d45e4696
160 changed files with 10282 additions and 5808 deletions

View file

@ -264,13 +264,11 @@ enum ha_base_keytype {
#define HA_SPATIAL 1024 /* For spatial search */
#define HA_NULL_ARE_EQUAL 2048 /* NULL in key are cmp as equal */
#define HA_GENERATED_KEY 8192 /* Automaticly generated key */
#define HA_RTREE_INDEX 16384 /* For RTREE search */
/* The combination of the above can be used for key type comparison. */
#define HA_KEYFLAG_MASK (HA_NOSAME | HA_PACK_KEY | HA_AUTO_KEY | \
HA_BINARY_PACK_KEY | HA_FULLTEXT | HA_UNIQUE_CHECK | \
HA_SPATIAL | HA_NULL_ARE_EQUAL | HA_GENERATED_KEY | \
HA_RTREE_INDEX)
HA_SPATIAL | HA_NULL_ARE_EQUAL | HA_GENERATED_KEY)
/*
Key contains partial segments.

View file

@ -0,0 +1,11 @@
create table t1(a int(10)unsigned not null auto_increment primary key,
b varchar(255) not null) engine=innodb default charset=utf8;
insert into t1 values(1,'aaa'),(2,'bbb');
alter table t1 auto_increment=1;
insert into t1 values(NULL, 'ccc');
select * from t1;
a b
1 aaa
2 bbb
3 ccc
drop table t1;

View file

@ -1597,10 +1597,6 @@ select distinct concat(a, b) from t1;
concat(a, b)
11113333
drop table t1;
CREATE TABLE t1 ( a char(10) ) ENGINE=InnoDB;
SELECT a FROM t1 WHERE MATCH (a) AGAINST ('test' IN BOOLEAN MODE);
ERROR HY000: The table does not have FULLTEXT index to support this query
DROP TABLE t1;
CREATE TABLE t1 (a_id tinyint(4) NOT NULL default '0', PRIMARY KEY (a_id)) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO t1 VALUES (1),(2),(3);
CREATE TABLE t2 (b_id tinyint(4) NOT NULL default '0',b_a tinyint(4) NOT NULL default '0', PRIMARY KEY (b_id), KEY (b_a),

View file

@ -1244,16 +1244,6 @@ insert into t1 values ('1111', '3333');
select distinct concat(a, b) from t1;
drop table t1;
#
# BUG#7709 test case - Boolean fulltext query against unsupported
# engines does not fail
#
CREATE TABLE t1 ( a char(10) ) ENGINE=InnoDB;
--error 1764
SELECT a FROM t1 WHERE MATCH (a) AGAINST ('test' IN BOOLEAN MODE);
DROP TABLE t1;
#
# check null values #1
#

View file

@ -337,9 +337,9 @@ insert into t2 values (1, 1, 'xxfoo');
insert into t2 values (2, 1, 'xxbar');
insert into t2 values (3, 1, 'xxbuz');
select * from t1 join t2 using(`t1_id`) where match (t1.name, t2.name) against('xxfoo' in boolean mode);
ERROR HY000: The table does not have FULLTEXT index to support this query
ERROR HY000: Incorrect arguments to MATCH
select * from t2 where match name against ('*a*b*c*d*e*f*' in boolean mode);
ERROR HY000: The table does not have FULLTEXT index to support this query
ERROR HY000: Can't find FULLTEXT index matching the column list
drop table t1,t2;
create table t1 (a text, fulltext key (a)) ENGINE = InnoDB;
insert into t1 select "xxxx yyyy zzzz";
@ -479,16 +479,13 @@ id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 fulltext a a 0 1 Using where
EXPLAIN SELECT * FROM t1 IGNORE INDEX(a)
WHERE MATCH(a) AGAINST('test' IN BOOLEAN MODE) AND b=1;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL b NULL NULL NULL 8 Using where
ERROR HY000: Can't find FULLTEXT index matching the column list
EXPLAIN SELECT * FROM t1 USE INDEX(b)
WHERE MATCH(a) AGAINST('test' IN BOOLEAN MODE) AND b=1;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL b NULL NULL NULL 8 Using where
ERROR HY000: Can't find FULLTEXT index matching the column list
EXPLAIN SELECT * FROM t1 FORCE INDEX(b)
WHERE MATCH(a) AGAINST('test' IN BOOLEAN MODE) AND b=1;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ref b b 5 const 5 Using where
ERROR HY000: Can't find FULLTEXT index matching the column list
DROP TABLE t1;
CREATE TABLE t1(a CHAR(10), fulltext(a)) ENGINE = InnoDB;
INSERT INTO t1 VALUES('aaa15');
@ -562,14 +559,12 @@ WHERE t3.a=t1.a AND MATCH(b2) AGAINST('scargill' IN BOOLEAN MODE)
);
count(*)
1
# should return 0
SELECT count(*) FROM t1 WHERE
not exists(
SELECT 1 FROM t2 IGNORE INDEX (b2), t3
WHERE t3.a=t1.a AND MATCH(b2) AGAINST('scargill' IN BOOLEAN MODE)
);
count(*)
0
ERROR HY000: Can't find FULLTEXT index matching the column list
DROP TABLE t1,t2,t3;
CREATE TABLE t1 (a VARCHAR(4), FULLTEXT(a)) ENGINE = InnoDB;
INSERT INTO t1 VALUES

View file

@ -66,7 +66,7 @@ create table t2 (m_id int not null, f char(200), key (m_id), fulltext (f)) engin
insert into t2 values (1, 'bword'), (3, 'aword'), (5, '');
ANALYZE TABLE t2;
select * from t1 left join t2 on m_id = id where match(d, e, f) against ('+aword +bword' in boolean mode);
id d e m_id f
ERROR HY000: Incorrect arguments to MATCH
drop table t1,t2;
CREATE TABLE t1 (
id int(10) NOT NULL auto_increment,
@ -89,9 +89,7 @@ ANALYZE TABLE t2;
SELECT t1.*, MATCH(t1.name) AGAINST('string') AS relevance
FROM t1 LEFT JOIN t2 ON t1.link = t2.id
WHERE MATCH(t1.name, t2.name) AGAINST('string' IN BOOLEAN MODE);
id link name relevance
1 1 string 0.000000001885928302414186
2 0 string 0.000000001885928302414186
ERROR HY000: Incorrect arguments to MATCH
DROP TABLE t1,t2;
CREATE TABLE t1 (a INT) ENGINE = InnoDB;
CREATE TABLE t2 (b INT, c TEXT, KEY(b), FULLTEXT(c)) ENGINE = InnoDB;

View file

@ -115,7 +115,7 @@ drop table t1, t2;
CREATE TABLE t1(a TEXT CHARSET ucs2 COLLATE ucs2_unicode_ci) ENGINE = InnoDB;
INSERT INTO t1 VALUES('abcd');
SELECT * FROM t1 WHERE MATCH(a) AGAINST ('+abcd' IN BOOLEAN MODE);
ERROR HY000: The table does not have FULLTEXT index to support this query
ERROR HY000: Can't find FULLTEXT index matching the column list
DROP TABLE t1;
create table t1 (a varchar(10), key(a), fulltext (a)) ENGINE = InnoDB;
insert into t1 values ("a"),("abc"),("abcd"),("hello"),("test");

View file

@ -710,8 +710,7 @@ CREATE FULLTEXT INDEX i ON t1 (char_column2);
Warnings:
Warning 124 InnoDB rebuilding table to add column FTS_DOC_ID
SELECT * FROM t1 WHERE MATCH(char_column) AGAINST ('abc*' IN BOOLEAN MODE);
id char_column char_column2
NULL abcde abcde
ERROR HY000: Can't find FULLTEXT index matching the column list
DROP TABLE t1;
"----------Test22---------"
CREATE TABLE t1 ( id INT , char_column VARCHAR(60) CHARACTER SET UTF8) ENGINE = InnoDB;

View file

@ -0,0 +1,29 @@
INSTALL PLUGIN simple_parser SONAME 'mypluglib';
CREATE TABLE articles (
id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
title VARCHAR(200),
body TEXT,
FULLTEXT (title) WITH PARSER simple_parser
) ENGINE=MyISAM;
ALTER TABLE articles ENGINE=InnoDB;
ERROR HY000: Cannot CREATE FULLTEXT INDEX WITH PARSER on InnoDB table
DROP TABLE articles;
CREATE TABLE articles (
id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
title VARCHAR(200),
body TEXT,
FULLTEXT (title) WITH PARSER simple_parser
) ENGINE=InnoDB;
ERROR HY000: Cannot CREATE FULLTEXT INDEX WITH PARSER on InnoDB table
CREATE TABLE articles (
id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
title VARCHAR(200),
body TEXT,
FULLTEXT (title)
) ENGINE=InnoDB;
ALTER TABLE articles ADD FULLTEXT INDEX (body) WITH PARSER simple_parser;
ERROR HY000: Cannot CREATE FULLTEXT INDEX WITH PARSER on InnoDB table
CREATE FULLTEXT INDEX ft_index ON articles(body) WITH PARSER simple_parser;
ERROR HY000: Cannot CREATE FULLTEXT INDEX WITH PARSER on InnoDB table
DROP TABLE articles;
UNINSTALL PLUGIN simple_parser;

View file

@ -0,0 +1,31 @@
CREATE TABLE t1 (
id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
a VARCHAR(200),
b TEXT
) ENGINE= InnoDB;
CREATE FULLTEXT INDEX idx on t1 (a,b);
Warnings:
Warning 124 InnoDB rebuilding table to add column FTS_DOC_ID
INSERT INTO t1 (a,b) VALUES
('MySQL from Tutorial','DBMS stands for DataBase ...') ,
('when To Use MySQL Well','After that you went through a ...'),
('where will Optimizing MySQL','what In this tutorial we will show ...'),
('MySQL from Tutorial','DBMS stands for DataBase ...') ,
('when To Use MySQL Well','After that you went through a ...'),
('where will Optimizing MySQL','what In this tutorial we will show ...'),
('MySQL from Tutorial','DBMS stands for DataBase ...') ,
('when To Use MySQL Well','After that you went through a ...'),
('where will Optimizing MySQL','what In this tutorial we will show ...');
SET SESSION debug_dbug="+d,fts_instrument_result_cache_limit";
SELECT COUNT(*) FROM t1 WHERE MATCH (a,b) AGAINST ('mysql' IN BOOLEAN MODE);
COUNT(*)
9
SELECT COUNT(*) FROM t1 WHERE MATCH (a,b) AGAINST ('mysql' WITH QUERY EXPANSION);
ERROR HY000: Table handler out of memory
SELECT COUNT(*) FROM t1 WHERE MATCH (a,b) AGAINST ('"mysql database"' IN BOOLEAN MODE);
ERROR HY000: Table handler out of memory
SELECT COUNT(*) FROM t1 WHERE MATCH (a,b) AGAINST ('"mysql database" @ 5' IN BOOLEAN MODE);
ERROR HY000: Table handler out of memory
SET SESSION debug_dbug="-d,fts_instrument_result_cache_limit";
DROP TABLE t1;
SET GLOBAL innodb_ft_result_cache_limit=default;

View file

@ -0,0 +1,321 @@
SELECT @@innodb_ft_server_stopword_table;
@@innodb_ft_server_stopword_table
NULL
SELECT @@innodb_ft_enable_stopword;
@@innodb_ft_enable_stopword
1
SELECT @@innodb_ft_user_stopword_table;
@@innodb_ft_user_stopword_table
NULL
SET NAMES utf8;
# Test 1 : default latin1_swedish_ci
CREATE TABLE articles (
id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
title VARCHAR(200)
) ENGINE=InnoDB;
INSERT INTO articles (title) VALUES
('love'),('LOVE'),('lòve'),('LÒVE'),('löve'),('LÖVE'),('løve'),('LØVE'),
('lṓve'),('LṒVE');
CREATE FULLTEXT INDEX ft_idx ON articles(title);
SELECT * FROM articles WHERE MATCH (title)
AGAINST ('lòve' IN NATURAL LANGUAGE MODE);
id title
1 love
2 LOVE
3 lòve
4 LÒVE
CREATE TABLE user_stopword(value varchar(30)) ENGINE = InnoDB;
INSERT INTO user_stopword VALUES('lòve');
SET GLOBAL innodb_ft_server_stopword_table = 'test/user_stopword';
DROP INDEX ft_idx ON articles;
CREATE FULLTEXT INDEX ft_idx ON articles(title);
SELECT * FROM articles WHERE MATCH (title)
AGAINST ('lòve' IN NATURAL LANGUAGE MODE);
id title
SELECT * FROM articles WHERE MATCH (title)
AGAINST ('love' IN NATURAL LANGUAGE MODE);
id title
DROP TABLE articles;
DROP TABLE user_stopword;
# Test 2 : latin1_general_ci
CREATE TABLE articles (
id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
title VARCHAR(200)
) ENGINE=InnoDB DEFAULT CHARACTER SET latin1 COLLATE latin1_general_ci;
INSERT INTO articles (title) VALUES
('love'),('LOVE'),('lòve'),('LÒVE'),('löve'),('LÖVE'),('løve'),('LØVE'),
('lṓve'),('LṒVE');
CREATE FULLTEXT INDEX ft_idx ON articles(title);
SELECT * FROM articles WHERE MATCH (title)
AGAINST ('lòve' IN NATURAL LANGUAGE MODE);
id title
3 lòve
4 LÒVE
CREATE TABLE user_stopword(value varchar(30)) ENGINE = InnoDB
DEFAULT CHARACTER SET latin1 COLLATE latin1_general_ci;
INSERT INTO user_stopword VALUES('lòve');
SET GLOBAL innodb_ft_server_stopword_table = 'test/user_stopword';
DROP INDEX ft_idx ON articles;
CREATE FULLTEXT INDEX ft_idx ON articles(title);
SELECT * FROM articles WHERE MATCH (title)
AGAINST ('lòve' IN NATURAL LANGUAGE MODE);
id title
SELECT * FROM articles WHERE MATCH (title)
AGAINST ('love' IN NATURAL LANGUAGE MODE);
id title
1 love
2 LOVE
DROP TABLE articles;
DROP TABLE user_stopword;
# Test 3 : latin1_spanish_ci
CREATE TABLE articles (
id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
title VARCHAR(200)
) ENGINE=InnoDB DEFAULT CHARACTER SET latin1 COLLATE latin1_spanish_ci;
INSERT INTO articles (title) VALUES
('love'),('LOVE'),('lòve'),('LÒVE'),('löve'),('LÖVE'),('løve'),('LØVE'),
('lṓve'),('LṒVE');
CREATE FULLTEXT INDEX ft_idx ON articles(title);
SELECT * FROM articles WHERE MATCH (title)
AGAINST ('lòve' IN NATURAL LANGUAGE MODE);
id title
1 love
2 LOVE
3 lòve
4 LÒVE
5 löve
6 LÖVE
7 løve
8 LØVE
CREATE TABLE user_stopword(value varchar(30)) ENGINE = InnoDB
DEFAULT CHARACTER SET latin1 COLLATE latin1_spanish_ci;
INSERT INTO user_stopword VALUES('lòve');
SET GLOBAL innodb_ft_server_stopword_table = 'test/user_stopword';
DROP INDEX ft_idx ON articles;
CREATE FULLTEXT INDEX ft_idx ON articles(title);
SELECT * FROM articles WHERE MATCH (title)
AGAINST ('lòve' IN NATURAL LANGUAGE MODE);
id title
SELECT * FROM articles WHERE MATCH (title)
AGAINST ('love' IN NATURAL LANGUAGE MODE);
id title
DROP TABLE articles;
DROP TABLE user_stopword;
# Test 4 : utf8_general_ci
CREATE TABLE articles (
id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
title VARCHAR(200)
) ENGINE=InnoDB DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
INSERT INTO articles (title) VALUES
('love'),('LOVE'),('lòve'),('LÒVE'),('löve'),('LÖVE'),('løve'),('LØVE'),
('lṓve'),('LṒVE');
CREATE FULLTEXT INDEX ft_idx ON articles(title);
SELECT * FROM articles WHERE MATCH (title)
AGAINST ('lòve' IN NATURAL LANGUAGE MODE);
id title
1 love
2 LOVE
3 lòve
4 LÒVE
5 löve
6 LÖVE
9 lṓve
10 LṒVE
CREATE TABLE user_stopword(value varchar(30)) ENGINE = InnoDB
DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
INSERT INTO user_stopword VALUES('lòve');
SET GLOBAL innodb_ft_server_stopword_table = 'test/user_stopword';
DROP INDEX ft_idx ON articles;
CREATE FULLTEXT INDEX ft_idx ON articles(title);
SELECT * FROM articles WHERE MATCH (title)
AGAINST ('lòve' IN NATURAL LANGUAGE MODE);
id title
SELECT * FROM articles WHERE MATCH (title)
AGAINST ('love' IN NATURAL LANGUAGE MODE);
id title
DROP TABLE articles;
DROP TABLE user_stopword;
# Test 5 : utf8_unicode_ci
CREATE TABLE articles (
id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
title VARCHAR(200)
) ENGINE=InnoDB DEFAULT CHARACTER SET utf8 COLLATE utf8_swedish_ci;
INSERT INTO articles (title) VALUES
('love'),('LOVE'),('lòve'),('LÒVE'),('löve'),('LÖVE'),('løve'),('LØVE'),
('lṓve'),('LṒVE');
CREATE FULLTEXT INDEX ft_idx ON articles(title);
SELECT * FROM articles WHERE MATCH (title)
AGAINST ('lòve' IN NATURAL LANGUAGE MODE);
id title
1 love
2 LOVE
3 lòve
4 LÒVE
9 lṓve
10 LṒVE
CREATE TABLE user_stopword(value varchar(30)) ENGINE = InnoDB
DEFAULT CHARACTER SET utf8 COLLATE utf8_swedish_ci;
INSERT INTO user_stopword VALUES('lòve');
SET GLOBAL innodb_ft_server_stopword_table = 'test/user_stopword';
DROP INDEX ft_idx ON articles;
CREATE FULLTEXT INDEX ft_idx ON articles(title);
SELECT * FROM articles WHERE MATCH (title)
AGAINST ('lòve' IN NATURAL LANGUAGE MODE);
id title
SELECT * FROM articles WHERE MATCH (title)
AGAINST ('love' IN NATURAL LANGUAGE MODE);
id title
DROP TABLE articles;
DROP TABLE user_stopword;
# Test 6 : utf8_unicode_ci
CREATE TABLE articles (
id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
title VARCHAR(200)
) ENGINE=InnoDB DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci;
INSERT INTO articles (title) VALUES
('love'),('LOVE'),('lòve'),('LÒVE'),('löve'),('LÖVE'),('løve'),('LØVE'),
('lṓve'),('LṒVE');
CREATE FULLTEXT INDEX ft_idx ON articles(title);
SELECT * FROM articles WHERE MATCH (title)
AGAINST ('lòve' IN NATURAL LANGUAGE MODE);
id title
1 love
2 LOVE
3 lòve
4 LÒVE
5 löve
6 LÖVE
9 lṓve
10 LṒVE
CREATE TABLE user_stopword(value varchar(30)) ENGINE = InnoDB
DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci;
INSERT INTO user_stopword VALUES('lòve');
SET GLOBAL innodb_ft_server_stopword_table = 'test/user_stopword';
DROP INDEX ft_idx ON articles;
CREATE FULLTEXT INDEX ft_idx ON articles(title);
SELECT * FROM articles WHERE MATCH (title)
AGAINST ('lòve' IN NATURAL LANGUAGE MODE);
id title
SELECT * FROM articles WHERE MATCH (title)
AGAINST ('love' IN NATURAL LANGUAGE MODE);
id title
DROP TABLE articles;
DROP TABLE user_stopword;
# Test 7 : gb2312_chinese_ci
CREATE TABLE articles (
id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
title VARCHAR(200)
) ENGINE=InnoDB DEFAULT CHARACTER SET gb2312 COLLATE gb2312_chinese_ci;
INSERT INTO articles (title) VALUES
('相亲相爱'),('怜香惜爱'),('充满可爱'),('爱恨交织');
CREATE FULLTEXT INDEX ft_idx ON articles(title);
SELECT * FROM articles WHERE MATCH (title)
AGAINST ('相亲相爱' IN NATURAL LANGUAGE MODE);
id title
1 相亲相爱
CREATE TABLE user_stopword(value varchar(30)) ENGINE = InnoDB
DEFAULT CHARACTER SET gb2312 COLLATE gb2312_chinese_ci;
INSERT INTO user_stopword VALUES('相亲相爱');
SET GLOBAL innodb_ft_server_stopword_table = 'test/user_stopword';
DROP INDEX ft_idx ON articles;
CREATE FULLTEXT INDEX ft_idx ON articles(title);
SELECT * FROM articles WHERE MATCH (title)
AGAINST ('相亲相爱' IN NATURAL LANGUAGE MODE);
id title
SELECT * FROM articles WHERE MATCH (title)
AGAINST ('怜香惜爱' IN NATURAL LANGUAGE MODE);
id title
2 怜香惜爱
DROP TABLE articles;
DROP TABLE user_stopword;
# Test 8 : test shutdown to check if stopword still works
CREATE TABLE articles (
id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
title VARCHAR(200)
) ENGINE=InnoDB;
INSERT INTO articles (title) VALUES
('love'),('LOVE'),('lòve'),('LÒVE'),('löve'),('LÖVE'),('løve'),('LØVE'),
('lṓve'),('LṒVE');
CREATE FULLTEXT INDEX ft_idx ON articles(title);
SELECT * FROM articles WHERE MATCH (title)
AGAINST ('lòve' IN NATURAL LANGUAGE MODE);
id title
1 love
2 LOVE
3 lòve
4 LÒVE
CREATE TABLE user_stopword(value varchar(30)) ENGINE = InnoDB;
INSERT INTO user_stopword VALUES('lòve');
SET GLOBAL innodb_ft_server_stopword_table = 'test/user_stopword';
DROP INDEX ft_idx ON articles;
CREATE FULLTEXT INDEX ft_idx ON articles(title);
SELECT * FROM articles WHERE MATCH (title)
AGAINST ('lòve' IN NATURAL LANGUAGE MODE);
id title
SELECT * FROM articles WHERE MATCH (title)
AGAINST ('love' IN NATURAL LANGUAGE MODE);
id title
# Shutdown and restart mysqld
SET NAMES utf8;
INSERT INTO articles (title) VALUES
('love'),('LOVE'),('lòve'),('LÒVE'),('löve'),('LÖVE'),('løve'),('LØVE'),
('lṓve'),('LṒVE');
SELECT * FROM articles WHERE MATCH (title)
AGAINST ('lòve' IN NATURAL LANGUAGE MODE);
id title
SELECT * FROM articles WHERE MATCH (title)
AGAINST ('love' IN NATURAL LANGUAGE MODE);
id title
DROP TABLE articles;
DROP TABLE user_stopword;
# Test 9 : drop user stopwrod table,test shutdown to check if it works
CREATE TABLE articles (
id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
title VARCHAR(200)
) ENGINE=InnoDB;
INSERT INTO articles (title) VALUES
('love'),('LOVE'),('lòve'),('LÒVE'),('löve'),('LÖVE'),('løve'),('LØVE'),
('lṓve'),('LṒVE');
CREATE FULLTEXT INDEX ft_idx ON articles(title);
SELECT * FROM articles WHERE MATCH (title)
AGAINST ('lòve' IN NATURAL LANGUAGE MODE);
id title
1 love
2 LOVE
3 lòve
4 LÒVE
CREATE TABLE user_stopword(value varchar(30)) ENGINE = InnoDB;
INSERT INTO user_stopword VALUES('lòve');
SET GLOBAL innodb_ft_server_stopword_table = 'test/user_stopword';
DROP INDEX ft_idx ON articles;
CREATE FULLTEXT INDEX ft_idx ON articles(title);
SELECT * FROM articles WHERE MATCH (title)
AGAINST ('lòve' IN NATURAL LANGUAGE MODE);
id title
SELECT * FROM articles WHERE MATCH (title)
AGAINST ('love' IN NATURAL LANGUAGE MODE);
id title
DROP TABLE user_stopword;
# Shutdown and restart mysqld
SET NAMES utf8;
INSERT INTO articles (title) VALUES
('love'),('LOVE'),('lòve'),('LÒVE'),('löve'),('LÖVE'),('løve'),('LØVE'),
('lṓve'),('LṒVE');
SELECT * FROM articles WHERE MATCH (title)
AGAINST ('lòve' IN NATURAL LANGUAGE MODE);
id title
11 love
12 LOVE
13 lòve
14 LÒVE
SELECT * FROM articles WHERE MATCH (title)
AGAINST ('love' IN NATURAL LANGUAGE MODE);
id title
11 love
12 LOVE
13 lòve
14 LÒVE
DROP TABLE articles;
SET SESSION innodb_ft_enable_stopword=1;
SET GLOBAL innodb_ft_server_stopword_table=default;
SET SESSION innodb_ft_user_stopword_table=default;

View file

@ -4,11 +4,6 @@
--source include/have_innodb.inc
if (`select plugin_auth_version <= "5.6.10" from information_schema.plugins where plugin_name='innodb'`)
{
--skip Not fixed in InnoDB 5.6.10 or earlier
}
--disable_warnings
drop table if exists t1,t2,t3;
--enable_warnings
@ -281,17 +276,17 @@ create table t2 (t2_id int(11) primary key, t1_id int(11), name varchar(32)) ENG
insert into t2 values (1, 1, 'xxfoo');
insert into t2 values (2, 1, 'xxbar');
insert into t2 values (3, 1, 'xxbuz');
# INNODB_FTS: Note there is no fulltext index on table. InnoDB do not support
# Fulltext search in such case, will return 1739
--error ER_TABLE_HAS_NO_FT
# INNODB_FTS: InnoDB do not support MATCH expressions with arguments from
# different tables
--error ER_WRONG_ARGUMENTS
select * from t1 join t2 using(`t1_id`) where match (t1.name, t2.name) against('xxfoo' in boolean mode);
#
# Bug #7858: bug with many short (< ft_min_word_len) words in boolean search
#
# INNODB_FTS: Note there is no fulltext index on table. InnoDB do not support
# Fulltext search in such case, will return 1739
--error ER_TABLE_HAS_NO_FT
# Fulltext search in such case
--error ER_FT_MATCHING_KEY_NOT_FOUND
select * from t2 where match name against ('*a*b*c*d*e*f*' in boolean mode);
drop table t1,t2;
@ -490,12 +485,15 @@ WHERE MATCH(a) AGAINST('test' IN BOOLEAN MODE) AND b=1;
EXPLAIN SELECT * FROM t1 FORCE INDEX(a)
WHERE MATCH(a) AGAINST('test' IN BOOLEAN MODE) AND b=1;
--error ER_FT_MATCHING_KEY_NOT_FOUND
EXPLAIN SELECT * FROM t1 IGNORE INDEX(a)
WHERE MATCH(a) AGAINST('test' IN BOOLEAN MODE) AND b=1;
--error ER_FT_MATCHING_KEY_NOT_FOUND
EXPLAIN SELECT * FROM t1 USE INDEX(b)
WHERE MATCH(a) AGAINST('test' IN BOOLEAN MODE) AND b=1;
--error ER_FT_MATCHING_KEY_NOT_FOUND
EXPLAIN SELECT * FROM t1 FORCE INDEX(b)
WHERE MATCH(a) AGAINST('test' IN BOOLEAN MODE) AND b=1;
@ -592,7 +590,7 @@ SELECT count(*) FROM t1 WHERE
WHERE t3.a=t1.a AND MATCH(b2) AGAINST('scargill' IN BOOLEAN MODE)
);
--echo # should return 0
--error ER_FT_MATCHING_KEY_NOT_FOUND
SELECT count(*) FROM t1 WHERE
not exists(
SELECT 1 FROM t2 IGNORE INDEX (b2), t3

View file

@ -77,6 +77,7 @@ insert into t2 values (1, 'bword'), (3, 'aword'), (5, '');
-- disable_result_log
ANALYZE TABLE t2;
-- enable_result_log
--error ER_WRONG_ARGUMENTS
select * from t1 left join t2 on m_id = id where match(d, e, f) against ('+aword +bword' in boolean mode);
drop table t1,t2;
@ -107,6 +108,7 @@ ANALYZE TABLE t1;
ANALYZE TABLE t2;
-- enable_result_log
--error ER_WRONG_ARGUMENTS
SELECT t1.*, MATCH(t1.name) AGAINST('string') AS relevance
FROM t1 LEFT JOIN t2 ON t1.link = t2.id
WHERE MATCH(t1.name, t2.name) AGAINST('string' IN BOOLEAN MODE);

View file

@ -139,7 +139,7 @@ CREATE TABLE t1(a TEXT CHARSET ucs2 COLLATE ucs2_unicode_ci) ENGINE = InnoDB;
INSERT INTO t1 VALUES('abcd');
# INNODB_FTS: Please Note this table do not have FTS. InnoDB return 1214 error
--error ER_TABLE_HAS_NO_FT
--error ER_FT_MATCHING_KEY_NOT_FOUND
SELECT * FROM t1 WHERE MATCH(a) AGAINST ('+abcd' IN BOOLEAN MODE);
DROP TABLE t1;

View file

@ -152,7 +152,7 @@ order by
(select b.id, b.betreff from t3 b)
order by match(betreff) against ('+abc' in boolean mode) desc;
--error 1191
--error ER_FT_MATCHING_KEY_NOT_FOUND
(select b.id, b.betreff from t3 b) union
(select b.id, b.betreff from t3 b)
order by match(betreff) against ('+abc') desc;

View file

@ -9,11 +9,6 @@ let collation=UTF8_UNICODE_CI;
drop table if exists t1;
--enable_warnings
if (`select plugin_auth_version <= "5.6.10" from information_schema.plugins where plugin_name='innodb'`)
{
--skip Not fixed in InnoDB 5.6.10 or earlier
}
# Create FTS table
CREATE TABLE t1 (
id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
@ -643,6 +638,7 @@ CREATE TABLE t1 ( id INT , char_column VARCHAR(60) CHARACTER SET UTF32, char_col
INSERT INTO t1 (char_column) VALUES ('abcde'),('fghij'),('klmno'),('qrstu');
UPDATE t1 SET char_column2 = char_column;
CREATE FULLTEXT INDEX i ON t1 (char_column2);
--error ER_FT_MATCHING_KEY_NOT_FOUND
SELECT * FROM t1 WHERE MATCH(char_column) AGAINST ('abc*' IN BOOLEAN MODE);
DROP TABLE t1;

View file

@ -0,0 +1,45 @@
--source include/have_simple_parser.inc
--source include/have_innodb.inc
# Install fts parser plugin
INSTALL PLUGIN simple_parser SONAME 'mypluglib';
# Create a myisam table and alter it to innodb table
CREATE TABLE articles (
id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
title VARCHAR(200),
body TEXT,
FULLTEXT (title) WITH PARSER simple_parser
) ENGINE=MyISAM;
--error ER_INNODB_NO_FT_USES_PARSER
ALTER TABLE articles ENGINE=InnoDB;
DROP TABLE articles;
# Create a table having a full text index with parser
--error ER_INNODB_NO_FT_USES_PARSER
CREATE TABLE articles (
id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
title VARCHAR(200),
body TEXT,
FULLTEXT (title) WITH PARSER simple_parser
) ENGINE=InnoDB;
CREATE TABLE articles (
id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
title VARCHAR(200),
body TEXT,
FULLTEXT (title)
) ENGINE=InnoDB;
# Alter table to add a full text index with parser
--error ER_INNODB_NO_FT_USES_PARSER
ALTER TABLE articles ADD FULLTEXT INDEX (body) WITH PARSER simple_parser;
# Create a full text index with parser
--error ER_INNODB_NO_FT_USES_PARSER
CREATE FULLTEXT INDEX ft_index ON articles(body) WITH PARSER simple_parser;
DROP TABLE articles;
# Uninstall plugin
UNINSTALL PLUGIN simple_parser;

View file

@ -0,0 +1,51 @@
# This is a basic test for innodb fts result cache limit.
-- source include/have_innodb.inc
# Must have debug code to use SET SESSION debug
--source include/have_debug.inc
# Create FTS table
CREATE TABLE t1 (
id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
a VARCHAR(200),
b TEXT
) ENGINE= InnoDB;
# Create the FTS index again
CREATE FULLTEXT INDEX idx on t1 (a,b);
# Insert rows
INSERT INTO t1 (a,b) VALUES
('MySQL from Tutorial','DBMS stands for DataBase ...') ,
('when To Use MySQL Well','After that you went through a ...'),
('where will Optimizing MySQL','what In this tutorial we will show ...'),
('MySQL from Tutorial','DBMS stands for DataBase ...') ,
('when To Use MySQL Well','After that you went through a ...'),
('where will Optimizing MySQL','what In this tutorial we will show ...'),
('MySQL from Tutorial','DBMS stands for DataBase ...') ,
('when To Use MySQL Well','After that you went through a ...'),
('where will Optimizing MySQL','what In this tutorial we will show ...');
SET SESSION debug_dbug="+d,fts_instrument_result_cache_limit";
# Simple term search
SELECT COUNT(*) FROM t1 WHERE MATCH (a,b) AGAINST ('mysql' IN BOOLEAN MODE);
# Query expansion
--error 128
SELECT COUNT(*) FROM t1 WHERE MATCH (a,b) AGAINST ('mysql' WITH QUERY EXPANSION);
# Simple phrase search
--error 128
SELECT COUNT(*) FROM t1 WHERE MATCH (a,b) AGAINST ('"mysql database"' IN BOOLEAN MODE);
# Simple proximity search
--error 128
SELECT COUNT(*) FROM t1 WHERE MATCH (a,b) AGAINST ('"mysql database" @ 5' IN BOOLEAN MODE);
SET SESSION debug_dbug="-d,fts_instrument_result_cache_limit";
DROP TABLE t1;
SET GLOBAL innodb_ft_result_cache_limit=default;

View file

@ -0,0 +1,421 @@
# This is the basic function tests for innodb FTS stopword charset
-- source include/have_innodb.inc
# Valgrind would complain about memory leaks when we crash on purpose.
--source include/not_valgrind.inc
# Embedded server does not support crashing
--source include/not_embedded.inc
# Avoid CrashReporter popup on Mac
--source include/not_crashrep.inc
let $innodb_ft_server_stopword_table_orig=`SELECT @@innodb_ft_server_stopword_table`;
let $innodb_ft_enable_stopword_orig=`SELECT @@innodb_ft_enable_stopword`;
let $innodb_ft_user_stopword_table_orig=`SELECT @@innodb_ft_user_stopword_table`;
SELECT @@innodb_ft_server_stopword_table;
SELECT @@innodb_ft_enable_stopword;
SELECT @@innodb_ft_user_stopword_table;
SET NAMES utf8;
-- echo # Test 1 : default latin1_swedish_ci
# Create FTS table with default charset latin1_swedish_ci
CREATE TABLE articles (
id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
title VARCHAR(200)
) ENGINE=InnoDB;
--disable_warnings
INSERT INTO articles (title) VALUES
('love'),('LOVE'),('lòve'),('LÒVE'),('löve'),('LÖVE'),('løve'),('LØVE'),
('lṓve'),('LṒVE');
# Build full text index with default stopword
CREATE FULLTEXT INDEX ft_idx ON articles(title);
--enable_warnings
# We can find 'lòve'
SELECT * FROM articles WHERE MATCH (title)
AGAINST ('lòve' IN NATURAL LANGUAGE MODE);
# Define a user stopword table and set to it
CREATE TABLE user_stopword(value varchar(30)) ENGINE = InnoDB;
INSERT INTO user_stopword VALUES('lòve');
SET GLOBAL innodb_ft_server_stopword_table = 'test/user_stopword';
# Rebuild the full text index with user stopword
DROP INDEX ft_idx ON articles;
CREATE FULLTEXT INDEX ft_idx ON articles(title);
# Now we will not find 'lòve' and check result with 'love'
SELECT * FROM articles WHERE MATCH (title)
AGAINST ('lòve' IN NATURAL LANGUAGE MODE);
SELECT * FROM articles WHERE MATCH (title)
AGAINST ('love' IN NATURAL LANGUAGE MODE);
DROP TABLE articles;
DROP TABLE user_stopword;
-- echo # Test 2 : latin1_general_ci
# Create FTS table with default charset latin1_swedish_ci
CREATE TABLE articles (
id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
title VARCHAR(200)
) ENGINE=InnoDB DEFAULT CHARACTER SET latin1 COLLATE latin1_general_ci;
--disable_warnings
INSERT INTO articles (title) VALUES
('love'),('LOVE'),('lòve'),('LÒVE'),('löve'),('LÖVE'),('løve'),('LØVE'),
('lṓve'),('LṒVE');
# Build full text index with default stopword
CREATE FULLTEXT INDEX ft_idx ON articles(title);
--enable_warnings
# We can find 'lòve'
SELECT * FROM articles WHERE MATCH (title)
AGAINST ('lòve' IN NATURAL LANGUAGE MODE);
# Define a user stopword table and set to it
CREATE TABLE user_stopword(value varchar(30)) ENGINE = InnoDB
DEFAULT CHARACTER SET latin1 COLLATE latin1_general_ci;
INSERT INTO user_stopword VALUES('lòve');
SET GLOBAL innodb_ft_server_stopword_table = 'test/user_stopword';
# Rebuild the full text index with user stopword
DROP INDEX ft_idx ON articles;
CREATE FULLTEXT INDEX ft_idx ON articles(title);
# Now we will not find 'lòve'
SELECT * FROM articles WHERE MATCH (title)
AGAINST ('lòve' IN NATURAL LANGUAGE MODE);
SELECT * FROM articles WHERE MATCH (title)
AGAINST ('love' IN NATURAL LANGUAGE MODE);
DROP TABLE articles;
DROP TABLE user_stopword;
-- echo # Test 3 : latin1_spanish_ci
# Create FTS table with default charset latin1_swedish_ci
CREATE TABLE articles (
id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
title VARCHAR(200)
) ENGINE=InnoDB DEFAULT CHARACTER SET latin1 COLLATE latin1_spanish_ci;
--disable_warnings
INSERT INTO articles (title) VALUES
('love'),('LOVE'),('lòve'),('LÒVE'),('löve'),('LÖVE'),('løve'),('LØVE'),
('lṓve'),('LṒVE');
# Build full text index with default stopword
CREATE FULLTEXT INDEX ft_idx ON articles(title);
--enable_warnings
# We can find 'lòve'
SELECT * FROM articles WHERE MATCH (title)
AGAINST ('lòve' IN NATURAL LANGUAGE MODE);
# Define a user stopword table and set to it
CREATE TABLE user_stopword(value varchar(30)) ENGINE = InnoDB
DEFAULT CHARACTER SET latin1 COLLATE latin1_spanish_ci;
INSERT INTO user_stopword VALUES('lòve');
SET GLOBAL innodb_ft_server_stopword_table = 'test/user_stopword';
# Rebuild the full text index with user stopword
DROP INDEX ft_idx ON articles;
CREATE FULLTEXT INDEX ft_idx ON articles(title);
# Now we will not find 'lòve'
SELECT * FROM articles WHERE MATCH (title)
AGAINST ('lòve' IN NATURAL LANGUAGE MODE);
SELECT * FROM articles WHERE MATCH (title)
AGAINST ('love' IN NATURAL LANGUAGE MODE);
DROP TABLE articles;
DROP TABLE user_stopword;
-- echo # Test 4 : utf8_general_ci
# Create FTS table with default charset utf8_general_ci
CREATE TABLE articles (
id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
title VARCHAR(200)
) ENGINE=InnoDB DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
--disable_warnings
INSERT INTO articles (title) VALUES
('love'),('LOVE'),('lòve'),('LÒVE'),('löve'),('LÖVE'),('løve'),('LØVE'),
('lṓve'),('LṒVE');
# Build full text index with default stopword
CREATE FULLTEXT INDEX ft_idx ON articles(title);
--enable_warnings
# We can find 'lòve'
SELECT * FROM articles WHERE MATCH (title)
AGAINST ('lòve' IN NATURAL LANGUAGE MODE);
# Define a user stopword table and set to it
CREATE TABLE user_stopword(value varchar(30)) ENGINE = InnoDB
DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
INSERT INTO user_stopword VALUES('lòve');
SET GLOBAL innodb_ft_server_stopword_table = 'test/user_stopword';
# Rebuild the full text index with user stopword
DROP INDEX ft_idx ON articles;
CREATE FULLTEXT INDEX ft_idx ON articles(title);
# Now we will not find 'lòve'
SELECT * FROM articles WHERE MATCH (title)
AGAINST ('lòve' IN NATURAL LANGUAGE MODE);
SELECT * FROM articles WHERE MATCH (title)
AGAINST ('love' IN NATURAL LANGUAGE MODE);
DROP TABLE articles;
DROP TABLE user_stopword;
-- echo # Test 5 : utf8_unicode_ci
# Create FTS table with default charset utf8_swedish_ci
CREATE TABLE articles (
id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
title VARCHAR(200)
) ENGINE=InnoDB DEFAULT CHARACTER SET utf8 COLLATE utf8_swedish_ci;
--disable_warnings
INSERT INTO articles (title) VALUES
('love'),('LOVE'),('lòve'),('LÒVE'),('löve'),('LÖVE'),('løve'),('LØVE'),
('lṓve'),('LṒVE');
# Build full text index with default stopword
CREATE FULLTEXT INDEX ft_idx ON articles(title);
--enable_warnings
# We can find 'lòve'
SELECT * FROM articles WHERE MATCH (title)
AGAINST ('lòve' IN NATURAL LANGUAGE MODE);
# Define a user stopword table and set to it
CREATE TABLE user_stopword(value varchar(30)) ENGINE = InnoDB
DEFAULT CHARACTER SET utf8 COLLATE utf8_swedish_ci;
INSERT INTO user_stopword VALUES('lòve');
SET GLOBAL innodb_ft_server_stopword_table = 'test/user_stopword';
# Rebuild the full text index with user stopword
DROP INDEX ft_idx ON articles;
CREATE FULLTEXT INDEX ft_idx ON articles(title);
# Now we will not find 'lòve'
SELECT * FROM articles WHERE MATCH (title)
AGAINST ('lòve' IN NATURAL LANGUAGE MODE);
SELECT * FROM articles WHERE MATCH (title)
AGAINST ('love' IN NATURAL LANGUAGE MODE);
DROP TABLE articles;
DROP TABLE user_stopword;
-- echo # Test 6 : utf8_unicode_ci
# Create FTS table with default charset utf8_unicode_ci
CREATE TABLE articles (
id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
title VARCHAR(200)
) ENGINE=InnoDB DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci;
--disable_warnings
INSERT INTO articles (title) VALUES
('love'),('LOVE'),('lòve'),('LÒVE'),('löve'),('LÖVE'),('løve'),('LØVE'),
('lṓve'),('LṒVE');
# Build full text index with default stopword
CREATE FULLTEXT INDEX ft_idx ON articles(title);
--enable_warnings
# We can find 'lòve'
SELECT * FROM articles WHERE MATCH (title)
AGAINST ('lòve' IN NATURAL LANGUAGE MODE);
# Define a user stopword table and set to it
CREATE TABLE user_stopword(value varchar(30)) ENGINE = InnoDB
DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci;
INSERT INTO user_stopword VALUES('lòve');
SET GLOBAL innodb_ft_server_stopword_table = 'test/user_stopword';
# Rebuild the full text index with user stopword
DROP INDEX ft_idx ON articles;
CREATE FULLTEXT INDEX ft_idx ON articles(title);
# Now we will not find 'lòve'
SELECT * FROM articles WHERE MATCH (title)
AGAINST ('lòve' IN NATURAL LANGUAGE MODE);
SELECT * FROM articles WHERE MATCH (title)
AGAINST ('love' IN NATURAL LANGUAGE MODE);
DROP TABLE articles;
DROP TABLE user_stopword;
-- echo # Test 7 : gb2312_chinese_ci
# Create FTS table with default charset gb2312_chinese_ci
CREATE TABLE articles (
id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
title VARCHAR(200)
) ENGINE=InnoDB DEFAULT CHARACTER SET gb2312 COLLATE gb2312_chinese_ci;
--disable_warnings
INSERT INTO articles (title) VALUES
('相亲相爱'),('怜香惜爱'),('充满可爱'),('爱恨交织');
# Build full text index with default stopword
CREATE FULLTEXT INDEX ft_idx ON articles(title);
--enable_warnings
# We can find '相亲相爱'
SELECT * FROM articles WHERE MATCH (title)
AGAINST ('相亲相爱' IN NATURAL LANGUAGE MODE);
# Define a user stopword table and set to it
CREATE TABLE user_stopword(value varchar(30)) ENGINE = InnoDB
DEFAULT CHARACTER SET gb2312 COLLATE gb2312_chinese_ci;
INSERT INTO user_stopword VALUES('相亲相爱');
SET GLOBAL innodb_ft_server_stopword_table = 'test/user_stopword';
# Rebuild the full text index with user stopword
DROP INDEX ft_idx ON articles;
CREATE FULLTEXT INDEX ft_idx ON articles(title);
# Now we will not find '相亲相爱'
SELECT * FROM articles WHERE MATCH (title)
AGAINST ('相亲相爱' IN NATURAL LANGUAGE MODE);
SELECT * FROM articles WHERE MATCH (title)
AGAINST ('怜香惜爱' IN NATURAL LANGUAGE MODE);
DROP TABLE articles;
DROP TABLE user_stopword;
-- echo # Test 8 : test shutdown to check if stopword still works
# Create FTS table with default charset latin1_swedish_ci
CREATE TABLE articles (
id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
title VARCHAR(200)
) ENGINE=InnoDB;
--disable_warnings
INSERT INTO articles (title) VALUES
('love'),('LOVE'),('lòve'),('LÒVE'),('löve'),('LÖVE'),('løve'),('LØVE'),
('lṓve'),('LṒVE');
# Build full text index with default stopword
CREATE FULLTEXT INDEX ft_idx ON articles(title);
--enable_warnings
# We can find 'lòve'
SELECT * FROM articles WHERE MATCH (title)
AGAINST ('lòve' IN NATURAL LANGUAGE MODE);
# Define a user stopword table and set to it
CREATE TABLE user_stopword(value varchar(30)) ENGINE = InnoDB;
INSERT INTO user_stopword VALUES('lòve');
SET GLOBAL innodb_ft_server_stopword_table = 'test/user_stopword';
# Rebuild the full text index with user stopword
DROP INDEX ft_idx ON articles;
CREATE FULLTEXT INDEX ft_idx ON articles(title);
# Now we will not find 'lòve' and check result with 'love'
SELECT * FROM articles WHERE MATCH (title)
AGAINST ('lòve' IN NATURAL LANGUAGE MODE);
SELECT * FROM articles WHERE MATCH (title)
AGAINST ('love' IN NATURAL LANGUAGE MODE);
--echo # Shutdown and restart mysqld
--source include/restart_mysqld.inc
SET NAMES utf8;
# Insert rows to check if it uses user stopword
--disable_warnings
INSERT INTO articles (title) VALUES
('love'),('LOVE'),('lòve'),('LÒVE'),('löve'),('LÖVE'),('løve'),('LØVE'),
('lṓve'),('LṒVE');
--enable_warnings
# Now we will not find 'lòve' and check result with 'love'
SELECT * FROM articles WHERE MATCH (title)
AGAINST ('lòve' IN NATURAL LANGUAGE MODE);
SELECT * FROM articles WHERE MATCH (title)
AGAINST ('love' IN NATURAL LANGUAGE MODE);
DROP TABLE articles;
DROP TABLE user_stopword;
-- echo # Test 9 : drop user stopwrod table,test shutdown to check if it works
# Create FTS table with default charset latin1_swedish_ci
CREATE TABLE articles (
id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
title VARCHAR(200)
) ENGINE=InnoDB;
--disable_warnings
INSERT INTO articles (title) VALUES
('love'),('LOVE'),('lòve'),('LÒVE'),('löve'),('LÖVE'),('løve'),('LØVE'),
('lṓve'),('LṒVE');
# Build full text index with default stopword
CREATE FULLTEXT INDEX ft_idx ON articles(title);
--enable_warnings
# We can find 'lòve'
SELECT * FROM articles WHERE MATCH (title)
AGAINST ('lòve' IN NATURAL LANGUAGE MODE);
# Define a user stopword table and set to it
CREATE TABLE user_stopword(value varchar(30)) ENGINE = InnoDB;
INSERT INTO user_stopword VALUES('lòve');
SET GLOBAL innodb_ft_server_stopword_table = 'test/user_stopword';
# Rebuild the full text index with user stopword
DROP INDEX ft_idx ON articles;
CREATE FULLTEXT INDEX ft_idx ON articles(title);
# Now we will not find 'lòve' and check result with 'love'
SELECT * FROM articles WHERE MATCH (title)
AGAINST ('lòve' IN NATURAL LANGUAGE MODE);
SELECT * FROM articles WHERE MATCH (title)
AGAINST ('love' IN NATURAL LANGUAGE MODE);
# Drop user stopword table
DROP TABLE user_stopword;
--echo # Shutdown and restart mysqld
--source include/restart_mysqld.inc
SET NAMES utf8;
# Insert rows to check if it uses user stopword
--disable_warnings
INSERT INTO articles (title) VALUES
('love'),('LOVE'),('lòve'),('LÒVE'),('löve'),('LÖVE'),('løve'),('LØVE'),
('lṓve'),('LṒVE');
--enable_warnings
# Now we will not find 'lòve' and check result with 'love'
SELECT * FROM articles WHERE MATCH (title)
AGAINST ('lòve' IN NATURAL LANGUAGE MODE);
SELECT * FROM articles WHERE MATCH (title)
AGAINST ('love' IN NATURAL LANGUAGE MODE);
DROP TABLE articles;
# Restore Values
eval SET SESSION innodb_ft_enable_stopword=$innodb_ft_enable_stopword_orig;
eval SET GLOBAL innodb_ft_server_stopword_table=default;
eval SET SESSION innodb_ft_user_stopword_table=default;

View file

@ -0,0 +1,12 @@
#
# MDEV-5574 Set AUTO_INCREMENT below max value of column
#
--source include/have_innodb.inc
create table t1(a int(10)unsigned not null auto_increment primary key,
b varchar(255) not null) engine=innodb default charset=utf8;
insert into t1 values(1,'aaa'),(2,'bbb');
alter table t1 auto_increment=1;
insert into t1 values(NULL, 'ccc');
select * from t1;
drop table t1;

View file

@ -6214,6 +6214,7 @@ bool Item_func_match::fix_fields(THD *thd, Item **ref)
return TRUE;
}
bool allows_multi_table_search= true;
const_item_cache=0;
for (uint i=1 ; i < arg_count ; i++)
{
@ -6225,7 +6226,10 @@ bool Item_func_match::fix_fields(THD *thd, Item **ref)
my_error(ER_WRONG_ARGUMENTS, MYF(0), "AGAINST");
return TRUE;
}
allows_multi_table_search &=
allows_search_on_non_indexed_columns(((Item_field *)item)->field->table);
}
/*
Check that all columns come from the same table.
We've already checked that columns in MATCH are fields so
@ -6234,7 +6238,7 @@ bool Item_func_match::fix_fields(THD *thd, Item **ref)
if ((used_tables_cache & ~PARAM_TABLE_BIT) != item->used_tables())
key=NO_SUCH_KEY;
if (key == NO_SUCH_KEY && !(flags & FT_BOOL))
if (key == NO_SUCH_KEY && !allows_multi_table_search)
{
my_error(ER_WRONG_ARGUMENTS,MYF(0),"MATCH");
return TRUE;
@ -6332,7 +6336,7 @@ bool Item_func_match::fix_index()
}
err:
if (flags & FT_BOOL)
if (allows_search_on_non_indexed_columns(table))
{
key=NO_SUCH_KEY;
return 0;

View file

@ -1909,6 +1909,41 @@ public:
/* TODO: consider adding in support for the MATCH-based virtual columns */
return trace_unsupported_by_check_vcol_func_processor(func_name());
}
private:
/**
Check whether storage engine for given table,
allows FTS Boolean search on non-indexed columns.
@todo A flag should be added to the extended fulltext API so that
it may be checked whether search on non-indexed columns are
supported. Currently, it is not possible to check for such a
flag since @c this->ft_handler is not yet set when this function is
called. The current hack is to assume that search on non-indexed
columns are supported for engines that does not support the extended
fulltext API (e.g., MyISAM), while it is not supported for other
engines (e.g., InnoDB)
@param table_arg Table for which storage engine to check
@retval true if BOOLEAN search on non-indexed columns is supported
@retval false otherwise
*/
bool allows_search_on_non_indexed_columns(TABLE* table_arg)
{
// Only Boolean search may support non_indexed columns
if (!(flags & FT_BOOL))
return false;
DBUG_ASSERT(table_arg && table_arg->file);
// Assume that if extended fulltext API is not supported,
// non-indexed columns are allowed. This will be true for MyISAM.
if ((table_arg->file->ha_table_flags() & HA_CAN_FULLTEXT_EXT) == 0)
return true;
return false;
}
};

View file

@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 2008, 2012, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2008, 2013, Oracle and/or its affiliates. All Rights Reserved.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -245,7 +245,7 @@ ib_open_table_by_id(
dict_mutex_enter_for_mysql();
}
table = dict_table_open_on_id(table_id, FALSE, FALSE);
table = dict_table_open_on_id(table_id, FALSE, DICT_TABLE_OP_NORMAL);
if (table != NULL && table->ibd_file_missing) {
table = NULL;
@ -1183,7 +1183,7 @@ ib_cursor_open_index_using_name(
/* We want to increment the ref count, so we do a redundant search. */
table = dict_table_open_on_id(cursor->prebuilt->table->id,
FALSE, FALSE);
FALSE, DICT_TABLE_OP_NORMAL);
ut_a(table != NULL);
/* The first index is always the cluster index. */
@ -1630,6 +1630,8 @@ ib_cursor_insert_row(
src_tuple->index->table, q_proc->grph.ins, node->ins);
}
srv_active_wake_master_thread();
return(err);
}
@ -1914,6 +1916,8 @@ ib_cursor_update_row(
err = ib_execute_update_query_graph(cursor, pcur);
}
srv_active_wake_master_thread();
return(err);
}
@ -2039,6 +2043,8 @@ ib_cursor_delete_row(
err = DB_RECORD_NOT_FOUND;
}
srv_active_wake_master_thread();
return(err);
}
@ -2296,12 +2302,14 @@ ib_col_set_value(
ib_tpl_t ib_tpl, /*!< in: tuple instance */
ib_ulint_t col_no, /*!< in: column index in tuple */
const void* src, /*!< in: data value */
ib_ulint_t len) /*!< in: data value len */
ib_ulint_t len, /*!< in: data value len */
ib_bool_t need_cpy) /*!< in: if need memcpy */
{
const dtype_t* dtype;
dfield_t* dfield;
void* dst = NULL;
ib_tuple_t* tuple = (ib_tuple_t*) ib_tpl;
ulint col_len;
dfield = ib_col_get_dfield(tuple, col_no);
@ -2312,6 +2320,7 @@ ib_col_set_value(
}
dtype = dfield_get_type(dfield);
col_len = dtype_get_len(dtype);
/* Not allowed to update system columns. */
if (dtype_get_mtype(dtype) == DATA_SYS) {
@ -2325,10 +2334,10 @@ ib_col_set_value(
for that. */
if (ib_col_is_capped(dtype)) {
len = ut_min(len, dtype_get_len(dtype));
len = ut_min(len, col_len);
if (dst == NULL || len > dfield_get_len(dfield)) {
dst = mem_heap_alloc(tuple->heap, dtype_get_len(dtype));
dst = mem_heap_alloc(tuple->heap, col_len);
ut_a(dst != NULL);
}
} else if (dst == NULL || len > dfield_get_len(dfield)) {
@ -2342,7 +2351,7 @@ ib_col_set_value(
switch (dtype_get_mtype(dtype)) {
case DATA_INT: {
if (dtype_get_len(dtype) == len) {
if (col_len == len) {
ibool usign;
usign = dtype_get_prtype(dtype) & DATA_UNSIGNED;
@ -2387,23 +2396,97 @@ ib_col_set_value(
memset((byte*) dst + len,
pad_char,
dtype_get_len(dtype) - len);
col_len - len);
memcpy(dst, src, len);
len = dtype_get_len(dtype);
len = col_len;
break;
}
case DATA_BLOB:
case DATA_BINARY:
case DATA_MYSQL:
case DATA_DECIMAL:
case DATA_VARCHAR:
case DATA_VARMYSQL:
case DATA_FIXBINARY:
if (need_cpy) {
memcpy(dst, src, len);
} else {
dfield_set_data(dfield, src, len);
dst = dfield_get_data(dfield);
}
break;
case DATA_MYSQL:
case DATA_VARMYSQL: {
ulint cset;
CHARSET_INFO* cs;
int error = 0;
ulint true_len = len;
/* For multi byte character sets we need to
calculate the true length of the data. */
cset = dtype_get_charset_coll(
dtype_get_prtype(dtype));
cs = all_charsets[cset];
if (cs) {
uint pos = (uint)(col_len / cs->mbmaxlen);
if (len > 0 && cs->mbmaxlen > 1) {
true_len = (ulint)
cs->cset->well_formed_len(
cs,
(const char*)src,
(const char*)src + len,
pos,
&error);
if (true_len < len) {
len = true_len;
}
}
}
/* All invalid bytes in data need be truncated.
If len == 0, means all bytes of the data is invalid.
In this case, the data will be truncated to empty.*/
memcpy(dst, src, len);
/* For DATA_MYSQL, need to pad the unused
space with spaces. */
if (dtype_get_mtype(dtype) == DATA_MYSQL) {
ulint n_chars;
if (len < col_len) {
ulint pad_len = col_len - len;
ut_a(cs != NULL);
ut_a(!(pad_len % cs->mbminlen));
cs->cset->fill(cs, (char*)dst + len,
pad_len,
0x20 /* space */);
}
/* Why we should do below? See function
row_mysql_store_col_in_innobase_format */
ut_a(!(dtype_get_len(dtype)
% dtype_get_mbmaxlen(dtype)));
n_chars = dtype_get_len(dtype)
/ dtype_get_mbmaxlen(dtype);
/* Strip space padding. */
while (col_len > n_chars
&& ((char*)dst)[col_len - 1] == 0x20) {
col_len--;
}
len = col_len;
}
break;
}
default:
ut_error;
}
@ -2476,7 +2559,9 @@ ib_col_copy_value_low(
data_len, usign);
if (usign) {
if (len == 2) {
if (len == 1) {
*(ib_i8_t*)dst = (ib_i8_t)ret;
} else if (len == 2) {
*(ib_i16_t*)dst = (ib_i16_t)ret;
} else if (len == 4) {
*(ib_i32_t*)dst = (ib_i32_t)ret;
@ -2484,7 +2569,9 @@ ib_col_copy_value_low(
*(ib_i64_t*)dst = (ib_i64_t)ret;
}
} else {
if (len == 2) {
if (len == 1) {
*(ib_u8_t*)dst = (ib_i8_t)ret;
} else if (len == 2) {
*(ib_u16_t*)dst = (ib_i16_t)ret;
} else if (len == 4) {
*(ib_u32_t*)dst = (ib_i32_t)ret;
@ -3450,7 +3537,7 @@ ib_tuple_write_int(
return(DB_DATA_MISMATCH);
}
return(ib_col_set_value(ib_tpl, col_no, value, type_len));
return(ib_col_set_value(ib_tpl, col_no, value, type_len, true));
}
/*****************************************************************//**
@ -3465,7 +3552,7 @@ ib_tuple_write_i8(
int col_no, /*!< in: column number */
ib_i8_t val) /*!< in: value to write */
{
return(ib_col_set_value(ib_tpl, col_no, &val, sizeof(val)));
return(ib_col_set_value(ib_tpl, col_no, &val, sizeof(val), true));
}
/*****************************************************************//**
@ -3480,7 +3567,7 @@ ib_tuple_write_i16(
int col_no, /*!< in: column number */
ib_i16_t val) /*!< in: value to write */
{
return(ib_col_set_value(ib_tpl, col_no, &val, sizeof(val)));
return(ib_col_set_value(ib_tpl, col_no, &val, sizeof(val), true));
}
/*****************************************************************//**
@ -3495,7 +3582,7 @@ ib_tuple_write_i32(
int col_no, /*!< in: column number */
ib_i32_t val) /*!< in: value to write */
{
return(ib_col_set_value(ib_tpl, col_no, &val, sizeof(val)));
return(ib_col_set_value(ib_tpl, col_no, &val, sizeof(val), true));
}
/*****************************************************************//**
@ -3510,7 +3597,7 @@ ib_tuple_write_i64(
int col_no, /*!< in: column number */
ib_i64_t val) /*!< in: value to write */
{
return(ib_col_set_value(ib_tpl, col_no, &val, sizeof(val)));
return(ib_col_set_value(ib_tpl, col_no, &val, sizeof(val), true));
}
/*****************************************************************//**
@ -3525,7 +3612,7 @@ ib_tuple_write_u8(
int col_no, /*!< in: column number */
ib_u8_t val) /*!< in: value to write */
{
return(ib_col_set_value(ib_tpl, col_no, &val, sizeof(val)));
return(ib_col_set_value(ib_tpl, col_no, &val, sizeof(val), true));
}
/*****************************************************************//**
@ -3540,7 +3627,7 @@ ib_tuple_write_u16(
int col_no, /*!< in: column number */
ib_u16_t val) /*!< in: value to write */
{
return(ib_col_set_value(ib_tpl, col_no, &val, sizeof(val)));
return(ib_col_set_value(ib_tpl, col_no, &val, sizeof(val), true));
}
/*****************************************************************//**
@ -3555,7 +3642,7 @@ ib_tuple_write_u32(
int col_no, /*!< in: column number */
ib_u32_t val) /*!< in: value to write */
{
return(ib_col_set_value(ib_tpl, col_no, &val, sizeof(val)));
return(ib_col_set_value(ib_tpl, col_no, &val, sizeof(val), true));
}
/*****************************************************************//**
@ -3570,7 +3657,7 @@ ib_tuple_write_u64(
int col_no, /*!< in: column number */
ib_u64_t val) /*!< in: value to write */
{
return(ib_col_set_value(ib_tpl, col_no, &val, sizeof(val)));
return(ib_col_set_value(ib_tpl, col_no, &val, sizeof(val), true));
}
/*****************************************************************//**
@ -3603,7 +3690,8 @@ ib_tuple_write_double(
dfield = ib_col_get_dfield(tuple, col_no);
if (dtype_get_mtype(dfield_get_type(dfield)) == DATA_DOUBLE) {
return(ib_col_set_value(ib_tpl, col_no, &val, sizeof(val)));
return(ib_col_set_value(ib_tpl, col_no,
&val, sizeof(val), true));
} else {
return(DB_DATA_MISMATCH);
}
@ -3653,7 +3741,8 @@ ib_tuple_write_float(
dfield = ib_col_get_dfield(tuple, col_no);
if (dtype_get_mtype(dfield_get_type(dfield)) == DATA_FLOAT) {
return(ib_col_set_value(ib_tpl, col_no, &val, sizeof(val)));
return(ib_col_set_value(ib_tpl, col_no,
&val, sizeof(val), true));
} else {
return(DB_DATA_MISMATCH);
}

View file

@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 1994, 2012, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 1994, 2013, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2012, Facebook Inc.
This program is free software; you can redistribute it and/or modify it under
@ -44,7 +44,21 @@ Created 6/2/1994 Heikki Tuuri
#include "trx0trx.h"
#include "srv0mon.h"
/**************************************************************//**
Checks if the page in the cursor can be merged with given page.
If necessary, re-organize the merge_page.
@return TRUE if possible to merge. */
UNIV_INTERN
ibool
btr_can_merge_with_page(
/*====================*/
btr_cur_t* cursor, /*!< in: cursor on the page to merge */
ulint page_no, /*!< in: a sibling page */
buf_block_t** merge_block, /*!< out: the merge block */
mtr_t* mtr); /*!< in: mini-transaction */
#endif /* UNIV_HOTBACKUP */
/**************************************************************//**
Report that an index page is corrupted. */
UNIV_INTERN
@ -1032,7 +1046,7 @@ btr_page_create(
btr_blob_dbg_assert_empty(index, buf_block_get_page_no(block));
if (page_zip) {
page_create_zip(block, index, level, mtr);
page_create_zip(block, index, level, 0, mtr);
} else {
page_create(block, mtr, dict_table_is_comp(index->table));
/* Set the level of the new index page */
@ -1602,7 +1616,7 @@ btr_create(
page_zip = buf_block_get_page_zip(block);
if (page_zip) {
page = page_create_zip(block, index, 0, mtr);
page = page_create_zip(block, index, 0, 0, mtr);
} else {
page = page_create(block, mtr,
dict_table_is_comp(index->table));
@ -1727,22 +1741,32 @@ btr_free_root(
#endif /* !UNIV_HOTBACKUP */
/*************************************************************//**
Reorganizes an index page. */
static
ibool
Reorganizes an index page.
IMPORTANT: On success, the caller will have to update IBUF_BITMAP_FREE
if this is a compressed leaf page in a secondary index. This has to
be done either within the same mini-transaction, or by invoking
ibuf_reset_free_bits() before mtr_commit(). On uncompressed pages,
IBUF_BITMAP_FREE is unaffected by reorganization.
@retval true if the operation was successful
@retval false if it is a compressed page, and recompression failed */
UNIV_INTERN
bool
btr_page_reorganize_low(
/*====================*/
ibool recovery,/*!< in: TRUE if called in recovery:
bool recovery,/*!< in: true if called in recovery:
locks should not be updated, i.e.,
there cannot exist locks on the
page, and a hash index should not be
dropped: it cannot exist */
ulint compression_level,/*!< in: compression level to be used
ulint z_level,/*!< in: compression level to be used
if dealing with compressed page */
buf_block_t* block, /*!< in: page to be reorganized */
dict_index_t* index, /*!< in: record descriptor */
mtr_t* mtr) /*!< in: mtr */
page_cur_t* cursor, /*!< in/out: page cursor */
dict_index_t* index, /*!< in: the index tree of the page */
mtr_t* mtr) /*!< in/out: mini-transaction */
{
buf_block_t* block = page_cur_get_block(cursor);
#ifndef UNIV_HOTBACKUP
buf_pool_t* buf_pool = buf_pool_from_bpage(&block->page);
#endif /* !UNIV_HOTBACKUP */
@ -1755,9 +1779,9 @@ btr_page_reorganize_low(
ulint data_size2;
ulint max_ins_size1;
ulint max_ins_size2;
ibool success = FALSE;
byte type;
byte* log_ptr;
bool success = false;
ulint pos;
bool log_compressed;
ut_ad(mtr_memo_contains(mtr, block, MTR_MEMO_PAGE_X_FIX));
btr_assert_not_corrupted(block, index);
@ -1767,27 +1791,6 @@ btr_page_reorganize_low(
data_size1 = page_get_data_size(page);
max_ins_size1 = page_get_max_insert_size_after_reorganize(page, 1);
#ifndef UNIV_HOTBACKUP
/* Write the log record */
if (page_zip) {
type = MLOG_ZIP_PAGE_REORGANIZE;
} else if (page_is_comp(page)) {
type = MLOG_COMP_PAGE_REORGANIZE;
} else {
type = MLOG_PAGE_REORGANIZE;
}
log_ptr = mlog_open_and_write_index(
mtr, page, index, type, page_zip ? 1 : 0);
/* For compressed pages write the compression level. */
if (log_ptr && page_zip) {
mach_write_to_1(log_ptr, compression_level);
mlog_close(mtr, log_ptr + 1);
}
#endif /* !UNIV_HOTBACKUP */
/* Turn logging off */
log_mode = mtr_set_log_mode(mtr, MTR_LOG_NONE);
@ -1811,6 +1814,9 @@ btr_page_reorganize_low(
#endif /* !UNIV_HOTBACKUP */
btr_blob_dbg_remove(page, index, "btr_page_reorganize");
/* Save the cursor position. */
pos = page_rec_get_n_recs_before(page_cur_get_rec(cursor));
/* Recreate the page: note that global data on page (possible
segment headers, next page-field, etc.) is preserved intact */
@ -1828,14 +1834,21 @@ btr_page_reorganize_low(
trx_id_t max_trx_id = page_get_max_trx_id(temp_page);
page_set_max_trx_id(block, NULL, max_trx_id, mtr);
/* In crash recovery, dict_index_is_sec_or_ibuf() always
returns TRUE, even for clustered indexes. max_trx_id is
holds, even for clustered indexes. max_trx_id is
unused in clustered index pages. */
ut_ad(max_trx_id != 0 || recovery);
}
/* If innodb_log_compressed_pages is ON, page reorganize should log the
compressed page image.*/
log_compressed = page_zip && page_zip_log_pages;
if (log_compressed) {
mtr_set_log_mode(mtr, log_mode);
}
if (page_zip
&& !page_zip_compress(page_zip, page, index,
compression_level, NULL)) {
&& !page_zip_compress(page_zip, page, index, z_level, mtr)) {
/* Restore the old page and exit. */
btr_blob_dbg_restore(page, temp_page, index,
@ -1890,7 +1903,14 @@ btr_page_reorganize_low(
(unsigned long) max_ins_size2);
ut_ad(0);
} else {
success = TRUE;
success = true;
}
/* Restore the cursor position. */
if (pos > 0) {
cursor->rec = page_rec_get_nth(page, pos);
} else {
ut_ad(cursor->rec == page_get_infimum_rec(page));
}
func_exit:
@ -1904,27 +1924,92 @@ func_exit:
/* Restore logging mode */
mtr_set_log_mode(mtr, log_mode);
#ifndef UNIV_HOTBACKUP
if (success) {
byte type;
byte* log_ptr;
/* Write the log record */
if (page_zip) {
ut_ad(page_is_comp(page));
type = MLOG_ZIP_PAGE_REORGANIZE;
} else if (page_is_comp(page)) {
type = MLOG_COMP_PAGE_REORGANIZE;
} else {
type = MLOG_PAGE_REORGANIZE;
}
log_ptr = log_compressed
? NULL
: mlog_open_and_write_index(
mtr, page, index, type,
page_zip ? 1 : 0);
/* For compressed pages write the compression level. */
if (log_ptr && page_zip) {
mach_write_to_1(log_ptr, z_level);
mlog_close(mtr, log_ptr + 1);
}
}
#endif /* !UNIV_HOTBACKUP */
return(success);
}
/*************************************************************//**
Reorganizes an index page.
IMPORTANT: On success, the caller will have to update IBUF_BITMAP_FREE
if this is a compressed leaf page in a secondary index. This has to
be done either within the same mini-transaction, or by invoking
ibuf_reset_free_bits() before mtr_commit(). On uncompressed pages,
IBUF_BITMAP_FREE is unaffected by reorganization.
@retval true if the operation was successful
@retval false if it is a compressed page, and recompression failed */
static __attribute__((nonnull))
bool
btr_page_reorganize_block(
/*======================*/
bool recovery,/*!< in: true if called in recovery:
locks should not be updated, i.e.,
there cannot exist locks on the
page, and a hash index should not be
dropped: it cannot exist */
ulint z_level,/*!< in: compression level to be used
if dealing with compressed page */
buf_block_t* block, /*!< in/out: B-tree page */
dict_index_t* index, /*!< in: the index tree of the page */
mtr_t* mtr) /*!< in/out: mini-transaction */
{
page_cur_t cur;
page_cur_set_before_first(block, &cur);
return(btr_page_reorganize_low(recovery, z_level, &cur, index, mtr));
}
#ifndef UNIV_HOTBACKUP
/*************************************************************//**
Reorganizes an index page.
IMPORTANT: if btr_page_reorganize() is invoked on a compressed leaf
page of a non-clustered index, the caller must update the insert
buffer free bits in the same mini-transaction in such a way that the
modification will be redo-logged.
@return TRUE on success, FALSE on failure */
IMPORTANT: On success, the caller will have to update IBUF_BITMAP_FREE
if this is a compressed leaf page in a secondary index. This has to
be done either within the same mini-transaction, or by invoking
ibuf_reset_free_bits() before mtr_commit(). On uncompressed pages,
IBUF_BITMAP_FREE is unaffected by reorganization.
@retval true if the operation was successful
@retval false if it is a compressed page, and recompression failed */
UNIV_INTERN
ibool
bool
btr_page_reorganize(
/*================*/
buf_block_t* block, /*!< in: page to be reorganized */
dict_index_t* index, /*!< in: record descriptor */
mtr_t* mtr) /*!< in: mtr */
page_cur_t* cursor, /*!< in/out: page cursor */
dict_index_t* index, /*!< in: the index tree of the page */
mtr_t* mtr) /*!< in/out: mini-transaction */
{
return(btr_page_reorganize_low(FALSE, page_compression_level,
block, index, mtr));
return(btr_page_reorganize_low(false, page_zip_level,
cursor, index, mtr));
}
#endif /* !UNIV_HOTBACKUP */
@ -1942,7 +2027,7 @@ btr_parse_page_reorganize(
buf_block_t* block, /*!< in: page to be reorganized, or NULL */
mtr_t* mtr) /*!< in: mtr or NULL */
{
ulint level = page_compression_level;
ulint level;
ut_ad(ptr && end_ptr);
@ -1954,14 +2039,16 @@ btr_parse_page_reorganize(
return(NULL);
}
level = (ulint)mach_read_from_1(ptr);
level = mach_read_from_1(ptr);
ut_a(level <= 9);
++ptr;
} else {
level = page_zip_level;
}
if (block != NULL) {
btr_page_reorganize_low(TRUE, level, block, index, mtr);
btr_page_reorganize_block(true, level, block, index, mtr);
}
return(ptr);
@ -1995,7 +2082,7 @@ btr_page_empty(
segment headers, next page-field, etc.) is preserved intact */
if (page_zip) {
page_create_zip(block, index, level, mtr);
page_create_zip(block, index, level, 0, mtr);
} else {
page_create(block, mtr, dict_table_is_comp(index->table));
btr_page_set_level(page, NULL, level, mtr);
@ -2043,7 +2130,7 @@ btr_root_raise_and_insert(
root = btr_cur_get_page(cursor);
root_block = btr_cur_get_block(cursor);
root_page_zip = buf_block_get_page_zip(root_block);
ut_ad(page_get_n_recs(root) > 0);
ut_ad(!page_is_empty(root));
index = btr_cur_get_index(cursor);
#ifdef UNIV_ZIP_DEBUG
ut_a(!root_page_zip || page_zip_validate(root_page_zip, root, index));
@ -2779,7 +2866,7 @@ func_start:
page_zip = buf_block_get_page_zip(block);
ut_ad(mtr_memo_contains(mtr, block, MTR_MEMO_PAGE_X_FIX));
ut_ad(page_get_n_recs(page) >= 1);
ut_ad(!page_is_empty(page));
page_no = buf_block_get_page_no(block);
@ -3033,15 +3120,16 @@ insert_empty:
goto func_exit;
}
/* 8. If insert did not fit, try page reorganization */
/* 8. If insert did not fit, try page reorganization.
For compressed pages, page_cur_tuple_insert() will have
attempted this already. */
if (!btr_page_reorganize(insert_block, cursor->index, mtr)) {
if (page_cur_get_page_zip(page_cursor)
|| !btr_page_reorganize(page_cursor, cursor->index, mtr)) {
goto insert_failed;
}
page_cur_search(insert_block, cursor->index, tuple,
PAGE_CUR_LE, page_cursor);
rec = page_cur_tuple_insert(page_cursor, tuple, cursor->index,
offsets, heap, n_ext, mtr);
@ -3049,9 +3137,10 @@ insert_empty:
/* The insert did not fit on the page: loop back to the
start of the function for a new split */
insert_failed:
/* We play safe and reset the free bits for new_page */
/* We play safe and reset the free bits */
if (!dict_index_is_clust(cursor->index)) {
ibuf_reset_free_bits(new_block);
ibuf_reset_free_bits(block);
}
/* fprintf(stderr, "Split second round %lu\n",
@ -3461,7 +3550,7 @@ btr_compress(
ulint left_page_no;
ulint right_page_no;
buf_block_t* merge_block;
page_t* merge_page;
page_t* merge_page = NULL;
page_zip_des_t* merge_page_zip;
ibool is_left;
buf_block_t* block;
@ -3469,11 +3558,8 @@ btr_compress(
btr_cur_t father_cursor;
mem_heap_t* heap;
ulint* offsets;
ulint data_size;
ulint n_recs;
ulint nth_rec = 0; /* remove bogus warning */
ulint max_ins_size;
ulint max_ins_size_reorg;
DBUG_ENTER("btr_compress");
block = btr_cur_get_block(cursor);
page = btr_cur_get_page(cursor);
@ -3490,10 +3576,13 @@ btr_compress(
left_page_no = btr_page_get_prev(page, mtr);
right_page_no = btr_page_get_next(page, mtr);
#if 0
fprintf(stderr, "Merge left page %lu right %lu \n",
left_page_no, right_page_no);
#endif
#ifdef UNIV_DEBUG
if (!page_is_leaf(page) && left_page_no == FIL_NULL) {
ut_a(REC_INFO_MIN_REC_FLAG & rec_get_info_bits(
page_rec_get_next(page_get_infimum_rec(page)),
page_is_comp(page)));
}
#endif /* UNIV_DEBUG */
heap = mem_heap_create(100);
offsets = btr_page_get_father_block(NULL, heap, index, block, mtr,
@ -3504,30 +3593,7 @@ btr_compress(
ut_ad(nth_rec > 0);
}
/* Decide the page to which we try to merge and which will inherit
the locks */
is_left = left_page_no != FIL_NULL;
if (is_left) {
merge_block = btr_block_get(space, zip_size, left_page_no,
RW_X_LATCH, index, mtr);
merge_page = buf_block_get_frame(merge_block);
#ifdef UNIV_BTR_DEBUG
ut_a(btr_page_get_next(merge_page, mtr)
== buf_block_get_page_no(block));
#endif /* UNIV_BTR_DEBUG */
} else if (right_page_no != FIL_NULL) {
merge_block = btr_block_get(space, zip_size, right_page_no,
RW_X_LATCH, index, mtr);
merge_page = buf_block_get_frame(merge_block);
#ifdef UNIV_BTR_DEBUG
ut_a(btr_page_get_prev(merge_page, mtr)
== buf_block_get_page_no(block));
#endif /* UNIV_BTR_DEBUG */
} else {
if (left_page_no == FIL_NULL && right_page_no == FIL_NULL) {
/* The page is the only one on the level, lift the records
to the father */
@ -3535,66 +3601,34 @@ btr_compress(
goto func_exit;
}
n_recs = page_get_n_recs(page);
data_size = page_get_data_size(page);
/* Decide the page to which we try to merge and which will inherit
the locks */
is_left = btr_can_merge_with_page(cursor, left_page_no,
&merge_block, mtr);
DBUG_EXECUTE_IF("ib_always_merge_right", is_left = FALSE;);
if(!is_left
&& !btr_can_merge_with_page(cursor, right_page_no, &merge_block,
mtr)) {
goto err_exit;
}
merge_page = buf_block_get_frame(merge_block);
#ifdef UNIV_BTR_DEBUG
ut_a(page_is_comp(merge_page) == page_is_comp(page));
if (is_left) {
ut_a(btr_page_get_next(merge_page, mtr)
== buf_block_get_page_no(block));
} else {
ut_a(btr_page_get_prev(merge_page, mtr)
== buf_block_get_page_no(block));
}
#endif /* UNIV_BTR_DEBUG */
max_ins_size_reorg = page_get_max_insert_size_after_reorganize(
merge_page, n_recs);
if (data_size > max_ins_size_reorg) {
/* No space for merge */
err_exit:
/* We play it safe and reset the free bits. */
if (zip_size
&& page_is_leaf(merge_page)
&& !dict_index_is_clust(index)) {
ibuf_reset_free_bits(merge_block);
}
mem_heap_free(heap);
return(FALSE);
}
/* If compression padding tells us that merging will result in
too packed up page i.e.: which is likely to cause compression
failure then don't merge the pages. */
if (zip_size && page_is_leaf(merge_page)
&& (page_get_data_size(merge_page) + data_size
>= dict_index_zip_pad_optimal_page_size(index))) {
goto err_exit;
}
ut_ad(page_validate(merge_page, index));
max_ins_size = page_get_max_insert_size(merge_page, n_recs);
if (data_size > max_ins_size) {
/* We have to reorganize merge_page */
if (!btr_page_reorganize(merge_block, index, mtr)) {
goto err_exit;
}
max_ins_size = page_get_max_insert_size(merge_page, n_recs);
ut_ad(page_validate(merge_page, index));
ut_ad(max_ins_size == max_ins_size_reorg);
if (data_size > max_ins_size) {
/* Add fault tolerance, though this should
never happen */
goto err_exit;
}
}
merge_page_zip = buf_block_get_page_zip(merge_block);
#ifdef UNIV_ZIP_DEBUG
if (merge_page_zip) {
@ -3629,11 +3663,19 @@ err_exit:
}
} else {
rec_t* orig_succ;
ibool compressed;
dberr_t err;
btr_cur_t cursor2;
/* father cursor pointing to node ptr
of the right sibling */
#ifdef UNIV_BTR_DEBUG
byte fil_page_prev[4];
#endif /* UNIV_BTR_DEBUG */
if (merge_page_zip) {
btr_page_get_father(index, merge_block, mtr, &cursor2);
if (merge_page_zip && left_page_no == FIL_NULL) {
/* The function page_zip_compress(), which will be
invoked by page_copy_rec_list_end() below,
requires that FIL_PAGE_PREV be FIL_NULL.
@ -3654,9 +3696,12 @@ err_exit:
if (!orig_succ) {
ut_a(merge_page_zip);
#ifdef UNIV_BTR_DEBUG
/* FIL_PAGE_PREV was restored from merge_page_zip. */
if (left_page_no == FIL_NULL) {
/* FIL_PAGE_PREV was restored from
merge_page_zip. */
ut_a(!memcmp(fil_page_prev,
merge_page + FIL_PAGE_PREV, 4));
}
#endif /* UNIV_BTR_DEBUG */
goto err_exit;
}
@ -3664,7 +3709,8 @@ err_exit:
btr_search_drop_page_hash_index(block);
#ifdef UNIV_BTR_DEBUG
if (merge_page_zip) {
if (merge_page_zip && left_page_no == FIL_NULL) {
/* Restore FIL_PAGE_PREV in order to avoid an assertion
failure in btr_level_list_remove(), which will set
the field again to FIL_NULL. Even though this makes
@ -3680,12 +3726,19 @@ err_exit:
/* Replace the address of the old child node (= page) with the
address of the merge page to the right */
btr_node_ptr_set_child_page_no(
btr_cur_get_rec(&father_cursor),
btr_cur_get_page_zip(&father_cursor),
offsets, right_page_no, mtr);
btr_node_ptr_delete(index, merge_block, mtr);
compressed = btr_cur_pessimistic_delete(&err, TRUE, &cursor2,
BTR_CREATE_FLAG,
RB_NONE, mtr);
ut_a(err == DB_SUCCESS);
if (!compressed) {
btr_cur_compress_if_useful(&cursor2, FALSE, mtr);
}
lock_update_merge_right(merge_block, orig_succ, block);
}
@ -3753,8 +3806,19 @@ func_exit:
page_rec_get_nth(merge_block->frame, nth_rec),
merge_block, cursor);
}
DBUG_RETURN(TRUE);
return(TRUE);
err_exit:
/* We play it safe and reset the free bits. */
if (zip_size
&& merge_page
&& page_is_leaf(merge_page)
&& !dict_index_is_clust(index)) {
ibuf_reset_free_bits(merge_block);
}
mem_heap_free(heap);
DBUG_RETURN(FALSE);
}
/*************************************************************//**
@ -3816,18 +3880,17 @@ btr_discard_only_page_on_level(
#endif /* UNIV_BTR_DEBUG */
btr_page_empty(block, buf_block_get_page_zip(block), index, 0, mtr);
ut_ad(page_is_leaf(buf_block_get_frame(block)));
if (!dict_index_is_clust(index)) {
/* We play it safe and reset the free bits for the root */
ibuf_reset_free_bits(block);
if (page_is_leaf(buf_block_get_frame(block))) {
ut_a(max_trx_id);
page_set_max_trx_id(block,
buf_block_get_page_zip(block),
max_trx_id, mtr);
}
}
}
/*************************************************************//**
@ -4489,9 +4552,9 @@ loop:
right_page_no = btr_page_get_next(page, &mtr);
left_page_no = btr_page_get_prev(page, &mtr);
ut_a(page_get_n_recs(page) > 0 || (level == 0
&& page_get_page_no(page)
== dict_index_get_page(index)));
ut_a(!page_is_empty(page)
|| (level == 0
&& page_get_page_no(page) == dict_index_get_page(index)));
if (right_page_no != FIL_NULL) {
const rec_t* right_rec;
@ -4799,4 +4862,97 @@ btr_validate_index(
return(ok);
}
/**************************************************************//**
Checks if the page in the cursor can be merged with given page.
If necessary, re-organize the merge_page.
@return TRUE if possible to merge. */
UNIV_INTERN
ibool
btr_can_merge_with_page(
/*====================*/
btr_cur_t* cursor, /*!< in: cursor on the page to merge */
ulint page_no, /*!< in: a sibling page */
buf_block_t** merge_block, /*!< out: the merge block */
mtr_t* mtr) /*!< in: mini-transaction */
{
dict_index_t* index;
page_t* page;
ulint space;
ulint zip_size;
ulint n_recs;
ulint data_size;
ulint max_ins_size_reorg;
ulint max_ins_size;
buf_block_t* mblock;
page_t* mpage;
DBUG_ENTER("btr_can_merge_with_page");
if (page_no == FIL_NULL) {
goto error;
}
index = btr_cur_get_index(cursor);
page = btr_cur_get_page(cursor);
space = dict_index_get_space(index);
zip_size = dict_table_zip_size(index->table);
mblock = btr_block_get(space, zip_size, page_no, RW_X_LATCH, index,
mtr);
mpage = buf_block_get_frame(mblock);
n_recs = page_get_n_recs(page);
data_size = page_get_data_size(page);
max_ins_size_reorg = page_get_max_insert_size_after_reorganize(
mpage, n_recs);
if (data_size > max_ins_size_reorg) {
goto error;
}
/* If compression padding tells us that merging will result in
too packed up page i.e.: which is likely to cause compression
failure then don't merge the pages. */
if (zip_size && page_is_leaf(mpage)
&& (page_get_data_size(mpage) + data_size
>= dict_index_zip_pad_optimal_page_size(index))) {
goto error;
}
max_ins_size = page_get_max_insert_size(mpage, n_recs);
if (data_size > max_ins_size) {
/* We have to reorganize mpage */
if (!btr_page_reorganize_block(
false, page_zip_level, mblock, index, mtr)) {
goto error;
}
max_ins_size = page_get_max_insert_size(mpage, n_recs);
ut_ad(page_validate(mpage, index));
ut_ad(max_ins_size == max_ins_size_reorg);
if (data_size > max_ins_size) {
/* Add fault tolerance, though this should
never happen */
goto error;
}
}
*merge_block = mblock;
DBUG_RETURN(TRUE);
error:
*merge_block = NULL;
DBUG_RETURN(FALSE);
}
#endif /* !UNIV_HOTBACKUP */

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 1996, 2012, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 1996, 2013, Oracle and/or its affiliates. All Rights Reserved.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -126,7 +126,7 @@ btr_pcur_store_position(
|| mtr_memo_contains(mtr, block, MTR_MEMO_PAGE_X_FIX));
ut_a(cursor->latch_mode != BTR_NO_LATCHES);
if (UNIV_UNLIKELY(page_get_n_recs(page) == 0)) {
if (page_is_empty(page)) {
/* It must be an empty index tree; NOTE that in this case
we do not store the modify_clock, but always do a search
if we restore the cursor position */

View file

@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 2006, 2011, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2006, 2013, Oracle and/or its affiliates. All Rights Reserved.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -33,12 +33,128 @@ Created December 2006 by Marko Makela
#include "buf0lru.h"
#include "buf0flu.h"
#include "page0zip.h"
#include "srv0start.h"
/** When freeing a buf we attempt to coalesce by looking at its buddy
and deciding whether it is free or not. To ascertain if the buddy is
free we look for BUF_BUDDY_STAMP_FREE at BUF_BUDDY_STAMP_OFFSET
within the buddy. The question is how we can be sure that it is
safe to look at BUF_BUDDY_STAMP_OFFSET.
The answer lies in following invariants:
* All blocks allocated by buddy allocator are used for compressed
page frame.
* A compressed table always have space_id < SRV_LOG_SPACE_FIRST_ID
* BUF_BUDDY_STAMP_OFFSET always points to the space_id field in
a frame.
-- The above is true because we look at these fields when the
corresponding buddy block is free which implies that:
* The block we are looking at must have an address aligned at
the same size that its free buddy has. For example, if we have
a free block of 8K then its buddy's address must be aligned at
8K as well.
* It is possible that the block we are looking at may have been
further divided into smaller sized blocks but its starting
address must still remain the start of a page frame i.e.: it
cannot be middle of a block. For example, if we have a free
block of size 8K then its buddy may be divided into blocks
of, say, 1K, 1K, 2K, 4K but the buddy's address will still be
the starting address of first 1K compressed page.
* What is important to note is that for any given block, the
buddy's address cannot be in the middle of a larger block i.e.:
in above example, our 8K block cannot have a buddy whose address
is aligned on 8K but it is part of a larger 16K block.
*/
/** Offset within buf_buddy_free_t where free or non_free stamps
are written.*/
#define BUF_BUDDY_STAMP_OFFSET FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID
/** Value that we stamp on all buffers that are currently on the zip_free
list. This value is stamped at BUF_BUDDY_STAMP_OFFSET offset */
#define BUF_BUDDY_STAMP_FREE (SRV_LOG_SPACE_FIRST_ID)
/** Stamp value for non-free buffers. Will be overwritten by a non-zero
value by the consumer of the block */
#define BUF_BUDDY_STAMP_NONFREE (0XFFFFFFFF)
#if BUF_BUDDY_STAMP_FREE >= BUF_BUDDY_STAMP_NONFREE
# error "BUF_BUDDY_STAMP_FREE >= BUF_BUDDY_STAMP_NONFREE"
#endif
/** Return type of buf_buddy_is_free() */
enum buf_buddy_state_t {
BUF_BUDDY_STATE_FREE, /*!< If the buddy to completely free */
BUF_BUDDY_STATE_USED, /*!< Buddy currently in used */
BUF_BUDDY_STATE_PARTIALLY_USED/*!< Some sub-blocks in the buddy
are in use */
};
#ifdef UNIV_DEBUG_VALGRIND
/**********************************************************************//**
Invalidate memory area that we won't access while page is free */
UNIV_INLINE
void
buf_buddy_mem_invalid(
/*==================*/
buf_buddy_free_t* buf, /*!< in: block to check */
ulint i) /*!< in: index of zip_free[] */
{
const size_t size = BUF_BUDDY_LOW << i;
ut_ad(i <= BUF_BUDDY_SIZES);
UNIV_MEM_ASSERT_W(buf, size);
UNIV_MEM_INVALID(buf, size);
}
#else /* UNIV_DEBUG_VALGRIND */
# define buf_buddy_mem_invalid(buf, i) ut_ad((i) <= BUF_BUDDY_SIZES)
#endif /* UNIV_DEBUG_VALGRIND */
/**********************************************************************//**
Check if a buddy is stamped free.
@return whether the buddy is free */
UNIV_INLINE __attribute__((warn_unused_result))
bool
buf_buddy_stamp_is_free(
/*====================*/
const buf_buddy_free_t* buf) /*!< in: block to check */
{
return(mach_read_from_4(buf->stamp.bytes + BUF_BUDDY_STAMP_OFFSET)
== BUF_BUDDY_STAMP_FREE);
}
/**********************************************************************//**
Stamps a buddy free. */
UNIV_INLINE
void
buf_buddy_stamp_free(
/*=================*/
buf_buddy_free_t* buf, /*!< in/out: block to stamp */
ulint i) /*!< in: block size */
{
ut_d(memset(buf, i, BUF_BUDDY_LOW << i));
buf_buddy_mem_invalid(buf, i);
mach_write_to_4(buf->stamp.bytes + BUF_BUDDY_STAMP_OFFSET,
BUF_BUDDY_STAMP_FREE);
buf->stamp.size = i;
}
/**********************************************************************//**
Stamps a buddy nonfree.
@param[in/out] buf block to stamp
@param[in] i block size */
#define buf_buddy_stamp_nonfree(buf, i) do { \
buf_buddy_mem_invalid(buf, i); \
memset(buf->stamp.bytes + BUF_BUDDY_STAMP_OFFSET, 0xff, 4); \
} while (0)
#if BUF_BUDDY_STAMP_NONFREE != 0xffffffff
# error "BUF_BUDDY_STAMP_NONFREE != 0xffffffff"
#endif
/**********************************************************************//**
Get the offset of the buddy of a compressed page frame.
@return the buddy relative of page */
UNIV_INLINE
byte*
void*
buf_buddy_get(
/*==========*/
byte* page, /*!< in: compressed page */
@ -60,14 +176,96 @@ buf_buddy_get(
/** Validate a given zip_free list. */
struct CheckZipFree {
void operator()(const buf_page_t* elem) const
ulint i;
CheckZipFree(ulint i) : i (i) {}
void operator()(const buf_buddy_free_t* elem) const
{
ut_a(buf_page_get_state(elem) == BUF_BLOCK_ZIP_FREE);
ut_a(buf_buddy_stamp_is_free(elem));
ut_a(elem->stamp.size <= i);
}
};
#define BUF_BUDDY_LIST_VALIDATE(bp, i) \
UT_LIST_VALIDATE(list, buf_page_t, bp->zip_free[i], CheckZipFree())
UT_LIST_VALIDATE(list, buf_buddy_free_t, \
bp->zip_free[i], CheckZipFree(i))
#ifdef UNIV_DEBUG
/**********************************************************************//**
Debug function to validate that a buffer is indeed free i.e.: in the
zip_free[].
@return true if free */
UNIV_INLINE
bool
buf_buddy_check_free(
/*=================*/
buf_pool_t* buf_pool,/*!< in: buffer pool instance */
const buf_buddy_free_t* buf, /*!< in: block to check */
ulint i) /*!< in: index of buf_pool->zip_free[] */
{
const ulint size = BUF_BUDDY_LOW << i;
ut_ad(buf_pool_mutex_own(buf_pool));
ut_ad(!ut_align_offset(buf, size));
ut_ad(i >= buf_buddy_get_slot(UNIV_ZIP_SIZE_MIN));
buf_buddy_free_t* itr;
for (itr = UT_LIST_GET_FIRST(buf_pool->zip_free[i]);
itr && itr != buf;
itr = UT_LIST_GET_NEXT(list, itr)) {
}
return(itr == buf);
}
#endif /* UNIV_DEBUG */
/**********************************************************************//**
Checks if a buf is free i.e.: in the zip_free[].
@retval BUF_BUDDY_STATE_FREE if fully free
@retval BUF_BUDDY_STATE_USED if currently in use
@retval BUF_BUDDY_STATE_PARTIALLY_USED if partially in use. */
static __attribute__((warn_unused_result))
buf_buddy_state_t
buf_buddy_is_free(
/*==============*/
buf_buddy_free_t* buf, /*!< in: block to check */
ulint i) /*!< in: index of
buf_pool->zip_free[] */
{
#ifdef UNIV_DEBUG
const ulint size = BUF_BUDDY_LOW << i;
ut_ad(!ut_align_offset(buf, size));
ut_ad(i >= buf_buddy_get_slot(UNIV_ZIP_SIZE_MIN));
#endif /* UNIV_DEBUG */
/* We assume that all memory from buf_buddy_alloc()
is used for compressed page frames. */
/* We look inside the allocated objects returned by
buf_buddy_alloc() and assume that each block is a compressed
page that contains one of the following in space_id.
* BUF_BUDDY_STAMP_FREE if the block is in a zip_free list or
* BUF_BUDDY_STAMP_NONFREE if the block has been allocated but
not initialized yet or
* A valid space_id of a compressed tablespace
The call below attempts to read from free memory. The memory
is "owned" by the buddy allocator (and it has been allocated
from the buffer pool), so there is nothing wrong about this. */
if (!buf_buddy_stamp_is_free(buf)) {
return(BUF_BUDDY_STATE_USED);
}
/* A block may be free but a fragment of it may still be in use.
To guard against that we write the free block size in terms of
zip_free index at start of stamped block. Note that we can
safely rely on this value only if the buf is free. */
ut_ad(buf->stamp.size <= i);
return(buf->stamp.size == i
? BUF_BUDDY_STATE_FREE
: BUF_BUDDY_STATE_PARTIALLY_USED);
}
/**********************************************************************//**
Add a block to the head of the appropriate buddy free list. */
@ -76,14 +274,16 @@ void
buf_buddy_add_to_free(
/*==================*/
buf_pool_t* buf_pool, /*!< in: buffer pool instance */
buf_page_t* bpage, /*!< in,own: block to be freed */
buf_buddy_free_t* buf, /*!< in,own: block to be freed */
ulint i) /*!< in: index of
buf_pool->zip_free[] */
{
ut_ad(buf_pool_mutex_own(buf_pool));
ut_ad(buf_page_get_state(bpage) == BUF_BLOCK_ZIP_FREE);
ut_ad(buf_pool->zip_free[i].start != bpage);
UT_LIST_ADD_FIRST(list, buf_pool->zip_free[i], bpage);
ut_ad(buf_pool->zip_free[i].start != buf);
buf_buddy_stamp_free(buf, i);
UT_LIST_ADD_FIRST(list, buf_pool->zip_free[i], buf);
ut_d(BUF_BUDDY_LIST_VALIDATE(buf_pool, i));
}
/**********************************************************************//**
@ -93,34 +293,28 @@ void
buf_buddy_remove_from_free(
/*=======================*/
buf_pool_t* buf_pool, /*!< in: buffer pool instance */
buf_page_t* bpage, /*!< in: block to be removed */
buf_buddy_free_t* buf, /*!< in,own: block to be freed */
ulint i) /*!< in: index of
buf_pool->zip_free[] */
{
#ifdef UNIV_DEBUG
buf_page_t* prev = UT_LIST_GET_PREV(list, bpage);
buf_page_t* next = UT_LIST_GET_NEXT(list, bpage);
ut_ad(!prev || buf_page_get_state(prev) == BUF_BLOCK_ZIP_FREE);
ut_ad(!next || buf_page_get_state(next) == BUF_BLOCK_ZIP_FREE);
#endif /* UNIV_DEBUG */
ut_ad(buf_pool_mutex_own(buf_pool));
ut_ad(buf_page_get_state(bpage) == BUF_BLOCK_ZIP_FREE);
UT_LIST_REMOVE(list, buf_pool->zip_free[i], bpage);
ut_ad(buf_buddy_check_free(buf_pool, buf, i));
UT_LIST_REMOVE(list, buf_pool->zip_free[i], buf);
buf_buddy_stamp_nonfree(buf, i);
}
/**********************************************************************//**
Try to allocate a block from buf_pool->zip_free[].
@return allocated block, or NULL if buf_pool->zip_free[] was empty */
static
void*
buf_buddy_free_t*
buf_buddy_alloc_zip(
/*================*/
buf_pool_t* buf_pool, /*!< in: buffer pool instance */
ulint i) /*!< in: index of buf_pool->zip_free[] */
{
buf_page_t* bpage;
buf_buddy_free_t* buf;
ut_ad(buf_pool_mutex_own(buf_pool));
ut_a(i < BUF_BUDDY_SIZES);
@ -128,33 +322,38 @@ buf_buddy_alloc_zip(
ut_d(BUF_BUDDY_LIST_VALIDATE(buf_pool, i));
bpage = UT_LIST_GET_FIRST(buf_pool->zip_free[i]);
buf = UT_LIST_GET_FIRST(buf_pool->zip_free[i]);
if (bpage) {
ut_a(buf_page_get_state(bpage) == BUF_BLOCK_ZIP_FREE);
buf_buddy_remove_from_free(buf_pool, bpage, i);
if (buf) {
buf_buddy_remove_from_free(buf_pool, buf, i);
} else if (i + 1 < BUF_BUDDY_SIZES) {
/* Attempt to split. */
bpage = (buf_page_t*) buf_buddy_alloc_zip(buf_pool, i + 1);
buf = buf_buddy_alloc_zip(buf_pool, i + 1);
if (bpage) {
buf_page_t* buddy = (buf_page_t*)
(((char*) bpage) + (BUF_BUDDY_LOW << i));
if (buf) {
buf_buddy_free_t* buddy =
reinterpret_cast<buf_buddy_free_t*>(
buf->stamp.bytes
+ (BUF_BUDDY_LOW << i));
ut_ad(!buf_pool_contains_zip(buf_pool, buddy));
ut_d(memset(buddy, i, BUF_BUDDY_LOW << i));
buddy->state = BUF_BLOCK_ZIP_FREE;
buf_buddy_add_to_free(buf_pool, buddy, i);
}
}
if (bpage) {
ut_d(memset(bpage, ~i, BUF_BUDDY_LOW << i));
UNIV_MEM_ALLOC(bpage, BUF_BUDDY_SIZES << i);
if (buf) {
/* Trash the page other than the BUF_BUDDY_STAMP_NONFREE. */
UNIV_MEM_TRASH(buf, ~i, BUF_BUDDY_STAMP_OFFSET);
UNIV_MEM_TRASH(BUF_BUDDY_STAMP_OFFSET + 4
+ buf->stamp.bytes, ~i,
(BUF_BUDDY_LOW << i)
- (BUF_BUDDY_STAMP_OFFSET + 4));
ut_ad(mach_read_from_4(buf->stamp.bytes
+ BUF_BUDDY_STAMP_OFFSET)
== BUF_BUDDY_STAMP_NONFREE);
}
return(bpage);
return(buf);
}
/**********************************************************************//**
@ -246,18 +445,17 @@ buf_buddy_alloc_from(
/* Add the unused parts of the block to the free lists. */
while (j > i) {
buf_page_t* bpage;
buf_buddy_free_t* zip_buf;
offs >>= 1;
j--;
bpage = (buf_page_t*) ((byte*) buf + offs);
ut_d(memset(bpage, j, BUF_BUDDY_LOW << j));
bpage->state = BUF_BLOCK_ZIP_FREE;
ut_d(BUF_BUDDY_LIST_VALIDATE(buf_pool, i));
buf_buddy_add_to_free(buf_pool, bpage, j);
zip_buf = reinterpret_cast<buf_buddy_free_t*>(
reinterpret_cast<byte*>(buf) + offs);
buf_buddy_add_to_free(buf_pool, zip_buf, j);
}
buf_buddy_stamp_nonfree(reinterpret_cast<buf_buddy_free_t*>(buf), i);
return(buf);
}
@ -322,9 +520,9 @@ func_exit:
/**********************************************************************//**
Try to relocate a block.
@return TRUE if relocated */
@return true if relocated */
static
ibool
bool
buf_buddy_relocate(
/*===============*/
buf_pool_t* buf_pool, /*!< in: buffer pool instance */
@ -337,7 +535,7 @@ buf_buddy_relocate(
const ulint size = BUF_BUDDY_LOW << i;
ib_mutex_t* mutex;
ulint space;
ulint page_no;
ulint offset;
ut_ad(buf_pool_mutex_own(buf_pool));
ut_ad(!mutex_own(&buf_pool->zip_mutex));
@ -346,32 +544,19 @@ buf_buddy_relocate(
ut_ad(i >= buf_buddy_get_slot(UNIV_ZIP_SIZE_MIN));
UNIV_MEM_ASSERT_W(dst, size);
/* We assume that all memory from buf_buddy_alloc()
is used for compressed page frames. */
/* We look inside the allocated objects returned by
buf_buddy_alloc() and assume that each block is a compressed
page that contains a valid space_id and page_no in the page
header. Should the fields be invalid, we will be unable to
relocate the block. */
/* The src block may be split into smaller blocks,
some of which may be free. Thus, the
mach_read_from_4() calls below may attempt to read
from free memory. The memory is "owned" by the buddy
allocator (and it has been allocated from the buffer
pool), so there is nothing wrong about this. The
mach_read_from_4() calls here will only trigger bogus
Valgrind memcheck warnings in UNIV_DEBUG_VALGRIND builds. */
space = mach_read_from_4((const byte*) src
+ FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID);
page_no = mach_read_from_4((const byte*) src
offset = mach_read_from_4((const byte*) src
+ FIL_PAGE_OFFSET);
/* Suppress Valgrind warnings about conditional jump
on uninitialized value. */
UNIV_MEM_VALID(&space, sizeof space);
UNIV_MEM_VALID(&page_no, sizeof page_no);
bpage = buf_page_hash_get(buf_pool, space, page_no);
UNIV_MEM_VALID(&offset, sizeof offset);
ut_ad(space != BUF_BUDDY_STAMP_FREE);
bpage = buf_page_hash_get(buf_pool, space, offset);
if (!bpage || bpage->zip.data != src) {
/* The block has probably been freshly
@ -379,7 +564,7 @@ buf_buddy_relocate(
added to buf_pool->page_hash yet. Obviously,
it cannot be relocated. */
return(FALSE);
return(false);
}
if (page_zip_get_size(&bpage->zip) != size) {
@ -388,7 +573,7 @@ buf_buddy_relocate(
For the sake of simplicity, give up. */
ut_ad(page_zip_get_size(&bpage->zip) < size);
return(FALSE);
return(false);
}
/* The block must have been allocated, but it may
@ -406,19 +591,17 @@ buf_buddy_relocate(
memcpy(dst, src, size);
bpage->zip.data = (page_zip_t*) dst;
mutex_exit(mutex);
UNIV_MEM_INVALID(src, size);
{
buf_buddy_stat_t* buddy_stat
= &buf_pool->buddy_stat[i];
buf_buddy_mem_invalid(
reinterpret_cast<buf_buddy_free_t*>(src), i);
buf_buddy_stat_t* buddy_stat = &buf_pool->buddy_stat[i];
buddy_stat->relocated++;
buddy_stat->relocated_usec
+= ut_time_us(NULL) - usec;
}
return(TRUE);
buddy_stat->relocated_usec += ut_time_us(NULL) - usec;
return(true);
}
mutex_exit(mutex);
return(FALSE);
return(false);
}
/**********************************************************************//**
@ -433,8 +616,7 @@ buf_buddy_free_low(
ulint i) /*!< in: index of buf_pool->zip_free[],
or BUF_BUDDY_SIZES */
{
buf_page_t* bpage;
buf_page_t* buddy;
buf_buddy_free_t* buddy;
ut_ad(buf_pool_mutex_own(buf_pool));
ut_ad(!mutex_own(&buf_pool->zip_mutex));
@ -445,7 +627,6 @@ buf_buddy_free_low(
buf_pool->buddy_stat[i].used--;
recombine:
UNIV_MEM_ASSERT_AND_ALLOC(buf, BUF_BUDDY_LOW << i);
((buf_page_t*) buf)->state = BUF_BLOCK_ZIP_FREE;
if (i == BUF_BUDDY_SIZES) {
buf_buddy_block_free(buf_pool, buf);
@ -464,73 +645,54 @@ recombine:
}
/* Try to combine adjacent blocks. */
buddy = (buf_page_t*) buf_buddy_get(((byte*) buf), BUF_BUDDY_LOW << i);
buddy = reinterpret_cast<buf_buddy_free_t*>(
buf_buddy_get(reinterpret_cast<byte*>(buf),
BUF_BUDDY_LOW << i));
#ifndef UNIV_DEBUG_VALGRIND
/* When Valgrind instrumentation is not enabled, we can read
buddy->state to quickly determine that a block is not free.
When the block is not free, buddy->state belongs to a compressed
page frame that may be flagged uninitialized in our Valgrind
instrumentation. */
if (buddy->state != BUF_BLOCK_ZIP_FREE) {
goto buddy_nonfree;
}
#endif /* !UNIV_DEBUG_VALGRIND */
for (bpage = UT_LIST_GET_FIRST(buf_pool->zip_free[i]); bpage; ) {
ut_ad(buf_page_get_state(bpage) == BUF_BLOCK_ZIP_FREE);
if (bpage == buddy) {
switch (buf_buddy_is_free(buddy, i)) {
case BUF_BUDDY_STATE_FREE:
/* The buddy is free: recombine */
buf_buddy_remove_from_free(buf_pool, bpage, i);
buf_buddy_remove_from_free(buf_pool, buddy, i);
buddy_is_free:
ut_ad(buf_page_get_state(buddy) == BUF_BLOCK_ZIP_FREE);
ut_ad(!buf_pool_contains_zip(buf_pool, buddy));
i++;
buf = ut_align_down(buf, BUF_BUDDY_LOW << i);
goto recombine;
}
ut_a(bpage != buf);
UNIV_MEM_ASSERT_W(bpage, BUF_BUDDY_LOW << i);
bpage = UT_LIST_GET_NEXT(list, bpage);
}
#ifndef UNIV_DEBUG_VALGRIND
buddy_nonfree:
#endif /* !UNIV_DEBUG_VALGRIND */
case BUF_BUDDY_STATE_USED:
ut_d(BUF_BUDDY_LIST_VALIDATE(buf_pool, i));
/* The buddy is not free. Is there a free block of this size? */
bpage = UT_LIST_GET_FIRST(buf_pool->zip_free[i]);
/* The buddy is not free. Is there a free block of
this size? */
if (buf_buddy_free_t* zip_buf =
UT_LIST_GET_FIRST(buf_pool->zip_free[i])) {
if (bpage) {
/* Remove the block from the free list, because
a successful buf_buddy_relocate() will overwrite
zip_free->list. */
buf_buddy_remove_from_free(buf_pool, zip_buf, i);
/* Remove the block from the free list, because a successful
buf_buddy_relocate() will overwrite bpage->list. */
buf_buddy_remove_from_free(buf_pool, bpage, i);
/* Try to relocate the buddy of buf to the free
block. */
if (buf_buddy_relocate(buf_pool, buddy, zip_buf, i)) {
/* Try to relocate the buddy of buf to the free block. */
if (buf_buddy_relocate(buf_pool, buddy, bpage, i)) {
buddy->state = BUF_BLOCK_ZIP_FREE;
goto buddy_is_free;
}
buf_buddy_add_to_free(buf_pool, bpage, i);
buf_buddy_add_to_free(buf_pool, zip_buf, i);
}
break;
case BUF_BUDDY_STATE_PARTIALLY_USED:
/* Some sub-blocks in the buddy are still in use.
Relocation will fail. No need to try. */
break;
}
func_exit:
/* Free the block to the buddy list. */
bpage = (buf_page_t*) buf;
/* Fill large blocks with a constant pattern. */
ut_d(memset(bpage, i, BUF_BUDDY_LOW << i));
UNIV_MEM_INVALID(bpage, BUF_BUDDY_LOW << i);
bpage->state = BUF_BLOCK_ZIP_FREE;
buf_buddy_add_to_free(buf_pool, bpage, i);
buf_buddy_add_to_free(buf_pool,
reinterpret_cast<buf_buddy_free_t*>(buf),
i);
}

View file

@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 1995, 2012, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 1995, 2013, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2008, Google Inc.
Portions of this file contain modifications contributed and copyrighted by
@ -496,14 +496,13 @@ buf_page_is_corrupted(
}
#ifndef UNIV_HOTBACKUP
if (recv_lsn_checks_on) {
if (check_lsn && recv_lsn_checks_on) {
lsn_t current_lsn;
/* Since we are going to reset the page LSN during the import
phase it makes no sense to spam the log with error messages. */
if (check_lsn
&& log_peek_lsn(&current_lsn)
if (log_peek_lsn(&current_lsn)
&& current_lsn
< mach_read_from_8(read_buf + FIL_PAGE_LSN)) {
ut_print_timestamp(stderr);
@ -1167,7 +1166,7 @@ buf_chunk_not_freed(
ibool ready;
switch (buf_block_get_state(block)) {
case BUF_BLOCK_ZIP_FREE:
case BUF_BLOCK_POOL_WATCH:
case BUF_BLOCK_ZIP_PAGE:
case BUF_BLOCK_ZIP_DIRTY:
/* The uncompressed buffer pool should never
@ -1492,7 +1491,7 @@ buf_relocate(
ut_ad(!buf_pool_watch_is_sentinel(buf_pool, bpage));
#ifdef UNIV_DEBUG
switch (buf_page_get_state(bpage)) {
case BUF_BLOCK_ZIP_FREE:
case BUF_BLOCK_POOL_WATCH:
case BUF_BLOCK_NOT_USED:
case BUF_BLOCK_READY_FOR_USE:
case BUF_BLOCK_FILE_PAGE:
@ -1964,7 +1963,7 @@ buf_block_try_discard_uncompressed(
bpage = buf_page_hash_get(buf_pool, space, offset);
if (bpage) {
buf_LRU_free_block(bpage, FALSE);
buf_LRU_free_page(bpage, false);
}
buf_pool_mutex_exit(buf_pool);
@ -2014,7 +2013,7 @@ lookup:
buf_read_page(space, zip_size, offset);
#if defined UNIV_DEBUG || defined UNIV_BUF_DEBUG
ut_a(++buf_dbg_counter % 37 || buf_validate());
ut_a(++buf_dbg_counter % 5771 || buf_validate());
#endif /* UNIV_DEBUG || UNIV_BUF_DEBUG */
}
@ -2030,11 +2029,11 @@ err_exit:
ut_ad(!buf_pool_watch_is_sentinel(buf_pool, bpage));
switch (buf_page_get_state(bpage)) {
case BUF_BLOCK_POOL_WATCH:
case BUF_BLOCK_NOT_USED:
case BUF_BLOCK_READY_FOR_USE:
case BUF_BLOCK_MEMORY:
case BUF_BLOCK_REMOVE_HASH:
case BUF_BLOCK_ZIP_FREE:
break;
case BUF_BLOCK_ZIP_PAGE:
case BUF_BLOCK_ZIP_DIRTY:
@ -2240,7 +2239,7 @@ buf_block_align_instance(
mutex_enter(&block->mutex);
switch (buf_block_get_state(block)) {
case BUF_BLOCK_ZIP_FREE:
case BUF_BLOCK_POOL_WATCH:
case BUF_BLOCK_ZIP_PAGE:
case BUF_BLOCK_ZIP_DIRTY:
/* These types should only be used in
@ -2436,7 +2435,6 @@ buf_page_get_gen(
ibool must_read;
rw_lock_t* hash_lock;
ib_mutex_t* block_mutex;
buf_page_t* hash_bpage;
ulint retries = 0;
buf_pool_t* buf_pool = buf_pool_get(space, offset);
@ -2489,7 +2487,6 @@ loop:
block = guess = NULL;
} else {
ut_ad(!block->page.in_zip_hash);
ut_ad(block->page.in_page_hash);
}
}
@ -2543,6 +2540,10 @@ loop:
retries = 0;
} else if (retries < BUF_PAGE_READ_MAX_RETRIES) {
++retries;
DBUG_EXECUTE_IF(
"innodb_page_corruption_retries",
retries = BUF_PAGE_READ_MAX_RETRIES;
);
} else {
fprintf(stderr, "InnoDB: Error: Unable"
" to read tablespace %lu page no"
@ -2564,7 +2565,7 @@ loop:
}
#if defined UNIV_DEBUG || defined UNIV_BUF_DEBUG
ut_a(++buf_dbg_counter % 37 || buf_validate());
ut_a(++buf_dbg_counter % 5771 || buf_validate());
#endif /* UNIV_DEBUG || UNIV_BUF_DEBUG */
goto loop;
}
@ -2590,6 +2591,7 @@ got_block:
/* The page is being read to buffer pool,
but we cannot wait around for the read to
complete. */
null_exit:
mutex_exit(block_mutex);
return(NULL);
@ -2603,6 +2605,14 @@ got_block:
case BUF_BLOCK_ZIP_PAGE:
case BUF_BLOCK_ZIP_DIRTY:
if (mode == BUF_PEEK_IF_IN_POOL) {
/* This mode is only used for dropping an
adaptive hash index. There cannot be an
adaptive hash index for a compressed-only
page, so do not bother decompressing the page. */
goto null_exit;
}
bpage = &block->page;
if (bpage->buf_fix_count
@ -2735,7 +2745,7 @@ wait_until_unfixed:
break;
case BUF_BLOCK_ZIP_FREE:
case BUF_BLOCK_POOL_WATCH:
case BUF_BLOCK_NOT_USED:
case BUF_BLOCK_READY_FOR_USE:
case BUF_BLOCK_MEMORY:
@ -2780,7 +2790,7 @@ wait_until_unfixed:
relocated or enter or exit the buf_pool while we
are holding the buf_pool->mutex. */
if (buf_LRU_free_block(&block->page, TRUE)) {
if (buf_LRU_free_page(&block->page, true)) {
buf_pool_mutex_exit(buf_pool);
rw_lock_x_lock(hash_lock);
@ -3728,7 +3738,7 @@ buf_page_create(
memset(frame + FIL_PAGE_FILE_FLUSH_LSN, 0, 8);
#if defined UNIV_DEBUG || defined UNIV_BUF_DEBUG
ut_a(++buf_dbg_counter % 357 || buf_validate());
ut_a(++buf_dbg_counter % 5771 || buf_validate());
#endif /* UNIV_DEBUG || UNIV_BUF_DEBUG */
#ifdef UNIV_IBUF_COUNT_DEBUG
ut_a(ibuf_count_get(buf_block_get_space(block),
@ -4196,7 +4206,7 @@ buf_pool_invalidate_instance(
pool invalidation to proceed we must ensure there is NO
write activity happening. */
if (buf_pool->n_flush[i] > 0) {
enum buf_flush type = static_cast<enum buf_flush>(i);
buf_flush_t type = static_cast<buf_flush_t>(i);
buf_pool_mutex_exit(buf_pool);
buf_flush_wait_batch_end(buf_pool, type);
@ -4285,7 +4295,7 @@ buf_pool_validate_instance(
mutex_enter(&block->mutex);
switch (buf_block_get_state(block)) {
case BUF_BLOCK_ZIP_FREE:
case BUF_BLOCK_POOL_WATCH:
case BUF_BLOCK_ZIP_PAGE:
case BUF_BLOCK_ZIP_DIRTY:
/* These should only occur on
@ -4378,7 +4388,7 @@ assert_s_latched:
/* All clean blocks should be I/O-unfixed. */
break;
case BUF_IO_READ:
/* In buf_LRU_free_block(), we temporarily set
/* In buf_LRU_free_page(), we temporarily set
b->io_fix = BUF_IO_READ for a newly allocated
control block in order to prevent
buf_page_get_gen() from decompressing the block. */
@ -4437,7 +4447,7 @@ assert_s_latched:
case BUF_BLOCK_FILE_PAGE:
/* uncompressed page */
break;
case BUF_BLOCK_ZIP_FREE:
case BUF_BLOCK_POOL_WATCH:
case BUF_BLOCK_ZIP_PAGE:
case BUF_BLOCK_NOT_USED:
case BUF_BLOCK_READY_FOR_USE:
@ -4720,7 +4730,7 @@ buf_get_latched_pages_number_instance(
case BUF_BLOCK_FILE_PAGE:
/* uncompressed page */
break;
case BUF_BLOCK_ZIP_FREE:
case BUF_BLOCK_POOL_WATCH:
case BUF_BLOCK_ZIP_PAGE:
case BUF_BLOCK_NOT_USED:
case BUF_BLOCK_READY_FOR_USE:
@ -5015,7 +5025,7 @@ buf_print_io_instance(
"Old database pages %lu\n"
"Modified db pages %lu\n"
"Pending reads %lu\n"
"Pending writes: LRU %lu, flush list %lu single page %lu\n",
"Pending writes: LRU %lu, flush list %lu, single page %lu\n",
pool_info->pool_size,
pool_info->free_list_len,
pool_info->lru_len,

View file

@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 1995, 2012, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 1995, 2013, Oracle and/or its affiliates. All Rights Reserved.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -38,11 +38,6 @@ Created 2011/12/19
#ifndef UNIV_HOTBACKUP
/** Time in milliseconds that we sleep when unable to find a slot in
the doublewrite buffer or when we have to wait for a running batch
to end. */
#define TRX_DOUBLEWRITE_BATCH_POLL_DELAY 10000
#ifdef UNIV_PFS_MUTEX
/* Key to register the mutex with performance schema */
UNIV_INTERN mysql_pfs_key_t buf_dblwr_mutex_key;
@ -104,6 +99,25 @@ buf_dblwr_get(
return(buf_block_get_frame(block) + TRX_SYS_DOUBLEWRITE);
}
/********************************************************************//**
Flush a batch of writes to the datafiles that have already been
written to the dblwr buffer on disk. */
UNIV_INLINE
void
buf_dblwr_sync_datafiles()
/*======================*/
{
/* Wake possible simulated aio thread to actually post the
writes to the operating system */
os_aio_simulated_wake_handler_threads();
/* Wait that all async writes to tablespaces have been posted to
the OS */
os_aio_wait_until_no_pending_writes();
/* Now we flush the data to disk (for example, with fsync) */
fil_flush_file_spaces(FIL_TABLESPACE);
}
/****************************************************************//**
Creates or initialializes the doublewrite buffer at a database start. */
@ -131,6 +145,8 @@ buf_dblwr_init(
mutex_create(buf_dblwr_mutex_key,
&buf_dblwr->mutex, SYNC_DOUBLEWRITE);
buf_dblwr->b_event = os_event_create();
buf_dblwr->s_event = os_event_create();
buf_dblwr->first_free = 0;
buf_dblwr->s_reserved = 0;
buf_dblwr->b_reserved = 0;
@ -140,8 +156,8 @@ buf_dblwr_init(
buf_dblwr->block2 = mach_read_from_4(
doublewrite + TRX_SYS_DOUBLEWRITE_BLOCK2);
buf_dblwr->in_use = static_cast<ibool*>(
mem_zalloc(buf_size * sizeof(ibool)));
buf_dblwr->in_use = static_cast<bool*>(
mem_zalloc(buf_size * sizeof(bool)));
buf_dblwr->write_buf_unaligned = static_cast<byte*>(
ut_malloc((1 + buf_size) * UNIV_PAGE_SIZE));
@ -365,7 +381,7 @@ buf_dblwr_init_or_restore_pages(
/* Read the trx sys header to check if we are using the doublewrite
buffer */
fil_io(OS_FILE_READ, TRUE, TRX_SYS_SPACE, 0, TRX_SYS_PAGE_NO, 0,
fil_io(OS_FILE_READ, true, TRX_SYS_SPACE, 0, TRX_SYS_PAGE_NO, 0,
UNIV_PAGE_SIZE, read_buf, NULL);
doublewrite = read_buf + TRX_SYS_DOUBLEWRITE;
@ -400,10 +416,10 @@ buf_dblwr_init_or_restore_pages(
/* Read the pages from the doublewrite buffer to memory */
fil_io(OS_FILE_READ, TRUE, TRX_SYS_SPACE, 0, block1, 0,
fil_io(OS_FILE_READ, true, TRX_SYS_SPACE, 0, block1, 0,
TRX_SYS_DOUBLEWRITE_BLOCK_SIZE * UNIV_PAGE_SIZE,
buf, NULL);
fil_io(OS_FILE_READ, TRUE, TRX_SYS_SPACE, 0, block2, 0,
fil_io(OS_FILE_READ, true, TRX_SYS_SPACE, 0, block2, 0,
TRX_SYS_DOUBLEWRITE_BLOCK_SIZE * UNIV_PAGE_SIZE,
buf + TRX_SYS_DOUBLEWRITE_BLOCK_SIZE * UNIV_PAGE_SIZE,
NULL);
@ -433,7 +449,7 @@ buf_dblwr_init_or_restore_pages(
+ i - TRX_SYS_DOUBLEWRITE_BLOCK_SIZE;
}
fil_io(OS_FILE_WRITE, TRUE, 0, 0, source_page_no, 0,
fil_io(OS_FILE_WRITE, true, 0, 0, source_page_no, 0,
UNIV_PAGE_SIZE, page, NULL);
} else {
@ -473,7 +489,7 @@ buf_dblwr_init_or_restore_pages(
ulint zip_size = fil_space_get_zip_size(space_id);
/* Read in the actual page from the file */
fil_io(OS_FILE_READ, TRUE, space_id, zip_size,
fil_io(OS_FILE_READ, true, space_id, zip_size,
page_no, 0,
zip_size ? zip_size : UNIV_PAGE_SIZE,
read_buf, NULL);
@ -525,7 +541,7 @@ buf_dblwr_init_or_restore_pages(
doublewrite buffer to the intended
position */
fil_io(OS_FILE_WRITE, TRUE, space_id,
fil_io(OS_FILE_WRITE, true, space_id,
zip_size, page_no, 0,
zip_size ? zip_size : UNIV_PAGE_SIZE,
page, NULL);
@ -557,6 +573,8 @@ buf_dblwr_free(void)
ut_ad(buf_dblwr->s_reserved == 0);
ut_ad(buf_dblwr->b_reserved == 0);
os_event_free(buf_dblwr->b_event);
os_event_free(buf_dblwr->s_event);
ut_free(buf_dblwr->write_buf_unaligned);
buf_dblwr->write_buf_unaligned = NULL;
@ -572,17 +590,21 @@ buf_dblwr_free(void)
}
/********************************************************************//**
Updates the doublewrite buffer when an IO request that is part of an
LRU or flush batch is completed. */
Updates the doublewrite buffer when an IO request is completed. */
UNIV_INTERN
void
buf_dblwr_update(void)
/*==================*/
buf_dblwr_update(
/*=============*/
const buf_page_t* bpage, /*!< in: buffer block descriptor */
buf_flush_t flush_type)/*!< in: flush type */
{
if (!srv_use_doublewrite_buf || buf_dblwr == NULL) {
return;
}
switch (flush_type) {
case BUF_FLUSH_LIST:
case BUF_FLUSH_LRU:
mutex_enter(&buf_dblwr->mutex);
ut_ad(buf_dblwr->batch_running);
@ -590,8 +612,8 @@ buf_dblwr_update(void)
ut_ad(buf_dblwr->b_reserved <= buf_dblwr->first_free);
buf_dblwr->b_reserved--;
if (buf_dblwr->b_reserved == 0) {
if (buf_dblwr->b_reserved == 0) {
mutex_exit(&buf_dblwr->mutex);
/* This will finish the batch. Sync data files
to the disk. */
@ -600,10 +622,36 @@ buf_dblwr_update(void)
/* We can now reuse the doublewrite memory buffer: */
buf_dblwr->first_free = 0;
buf_dblwr->batch_running = FALSE;
buf_dblwr->batch_running = false;
os_event_set(buf_dblwr->b_event);
}
mutex_exit(&buf_dblwr->mutex);
break;
case BUF_FLUSH_SINGLE_PAGE:
{
const ulint size = 2 * TRX_SYS_DOUBLEWRITE_BLOCK_SIZE;
ulint i;
mutex_enter(&buf_dblwr->mutex);
for (i = srv_doublewrite_batch_size; i < size; ++i) {
if (buf_dblwr->buf_block_arr[i] == bpage) {
buf_dblwr->s_reserved--;
buf_dblwr->buf_block_arr[i] = NULL;
buf_dblwr->in_use[i] = false;
break;
}
}
/* The block we are looking for must exist as a
reserved block. */
ut_a(i < size);
}
os_event_set(buf_dblwr->s_event);
mutex_exit(&buf_dblwr->mutex);
break;
case BUF_FLUSH_N_TYPES:
ut_error;
}
}
/********************************************************************//**
@ -698,18 +746,19 @@ static
void
buf_dblwr_write_block_to_datafile(
/*==============================*/
const buf_page_t* bpage) /*!< in: page to write */
const buf_page_t* bpage, /*!< in: page to write */
bool sync) /*!< in: true if sync IO
is requested */
{
ut_a(bpage);
ut_a(buf_page_in_file(bpage));
/* Increment the counter of I/O operations used
for selecting LRU policy. */
buf_LRU_stat_inc_io();
const ulint flags = sync
? OS_FILE_WRITE
: OS_FILE_WRITE | OS_AIO_SIMULATED_WAKE_LATER;
if (bpage->zip.data) {
fil_io(OS_FILE_WRITE | OS_AIO_SIMULATED_WAKE_LATER,
FALSE, buf_page_get_space(bpage),
fil_io(flags, sync, buf_page_get_space(bpage),
buf_page_get_zip_size(bpage),
buf_page_get_page_no(bpage), 0,
buf_page_get_zip_size(bpage),
@ -724,8 +773,7 @@ buf_dblwr_write_block_to_datafile(
ut_a(buf_block_get_state(block) == BUF_BLOCK_FILE_PAGE);
buf_dblwr_check_page_lsn(block->frame);
fil_io(OS_FILE_WRITE | OS_AIO_SIMULATED_WAKE_LATER,
FALSE, buf_block_get_space(block), 0,
fil_io(flags, sync, buf_block_get_space(block), 0,
buf_block_get_page_no(block), 0, UNIV_PAGE_SIZE,
(void*) block->frame, (void*) block);
}
@ -747,12 +795,12 @@ buf_dblwr_flush_buffered_writes(void)
if (!srv_use_doublewrite_buf || buf_dblwr == NULL) {
/* Sync the writes to the disk. */
buf_flush_sync_datafiles();
buf_dblwr_sync_datafiles();
return;
}
try_again:
mutex_enter(&(buf_dblwr->mutex));
mutex_enter(&buf_dblwr->mutex);
/* Write first to doublewrite buffer blocks. We use synchronous
aio and thus know that file write has been completed when the
@ -760,17 +808,18 @@ try_again:
if (buf_dblwr->first_free == 0) {
mutex_exit(&(buf_dblwr->mutex));
mutex_exit(&buf_dblwr->mutex);
return;
}
if (buf_dblwr->batch_running) {
mutex_exit(&buf_dblwr->mutex);
/* Another thread is running the batch right now. Wait
for it to finish. */
os_thread_sleep(TRX_DOUBLEWRITE_BATCH_POLL_DELAY);
ib_int64_t sig_count = os_event_reset(buf_dblwr->b_event);
mutex_exit(&buf_dblwr->mutex);
os_event_wait_low(buf_dblwr->b_event, sig_count);
goto try_again;
}
@ -779,7 +828,7 @@ try_again:
/* Disallow anyone else to post to doublewrite buffer or to
start another batch of flushing. */
buf_dblwr->batch_running = TRUE;
buf_dblwr->batch_running = true;
first_free = buf_dblwr->first_free;
/* Now safe to release the mutex. Note that though no other
@ -818,7 +867,7 @@ try_again:
len = ut_min(TRX_SYS_DOUBLEWRITE_BLOCK_SIZE,
buf_dblwr->first_free) * UNIV_PAGE_SIZE;
fil_io(OS_FILE_WRITE, TRUE, TRX_SYS_SPACE, 0,
fil_io(OS_FILE_WRITE, true, TRX_SYS_SPACE, 0,
buf_dblwr->block1, 0, len,
(void*) write_buf, NULL);
@ -834,7 +883,7 @@ try_again:
write_buf = buf_dblwr->write_buf
+ TRX_SYS_DOUBLEWRITE_BLOCK_SIZE * UNIV_PAGE_SIZE;
fil_io(OS_FILE_WRITE, TRUE, TRX_SYS_SPACE, 0,
fil_io(OS_FILE_WRITE, true, TRX_SYS_SPACE, 0,
buf_dblwr->block2, 0, len,
(void*) write_buf, NULL);
@ -864,7 +913,7 @@ flush:
ut_ad(first_free == buf_dblwr->first_free);
for (ulint i = 0; i < first_free; i++) {
buf_dblwr_write_block_to_datafile(
buf_dblwr->buf_block_arr[i]);
buf_dblwr->buf_block_arr[i], false);
}
/* Wake possible simulated aio thread to actually post the
@ -889,12 +938,11 @@ buf_dblwr_add_to_batch(
ut_a(buf_page_in_file(bpage));
try_again:
mutex_enter(&(buf_dblwr->mutex));
mutex_enter(&buf_dblwr->mutex);
ut_a(buf_dblwr->first_free <= srv_doublewrite_batch_size);
if (buf_dblwr->batch_running) {
mutex_exit(&buf_dblwr->mutex);
/* This not nearly as bad as it looks. There is only
page_cleaner thread which does background flushing
@ -902,7 +950,10 @@ try_again:
point. The only exception is when a user thread is
forced to do a flush batch because of a sync
checkpoint. */
os_thread_sleep(TRX_DOUBLEWRITE_BATCH_POLL_DELAY);
ib_int64_t sig_count = os_event_reset(buf_dblwr->b_event);
mutex_exit(&buf_dblwr->mutex);
os_event_wait_low(buf_dblwr->b_event, sig_count);
goto try_again;
}
@ -967,7 +1018,8 @@ UNIV_INTERN
void
buf_dblwr_write_single_page(
/*========================*/
buf_page_t* bpage) /*!< in: buffer block to write */
buf_page_t* bpage, /*!< in: buffer block to write */
bool sync) /*!< in: true if sync IO requested */
{
ulint n_slots;
ulint size;
@ -1004,11 +1056,12 @@ retry:
mutex_enter(&buf_dblwr->mutex);
if (buf_dblwr->s_reserved == n_slots) {
/* All slots are reserved. */
ib_int64_t sig_count =
os_event_reset(buf_dblwr->s_event);
mutex_exit(&buf_dblwr->mutex);
/* All slots are reserved. Since it involves two IOs
during the processing a sleep of 10ms should be
enough. */
os_thread_sleep(TRX_DOUBLEWRITE_BATCH_POLL_DELAY);
os_event_wait_low(buf_dblwr->s_event, sig_count);
goto retry;
}
@ -1021,9 +1074,14 @@ retry:
/* We are guaranteed to find a slot. */
ut_a(i < size);
buf_dblwr->in_use[i] = TRUE;
buf_dblwr->in_use[i] = true;
buf_dblwr->s_reserved++;
buf_dblwr->buf_block_arr[i] = bpage;
/* increment the doublewrite flushed pages counter */
srv_stats.dblwr_pages_written.inc();
srv_stats.dblwr_writes.inc();
mutex_exit(&buf_dblwr->mutex);
/* Lets see if we are going to write in the first or second
@ -1053,14 +1111,14 @@ retry:
memset(buf_dblwr->write_buf + UNIV_PAGE_SIZE * i
+ zip_size, 0, UNIV_PAGE_SIZE - zip_size);
fil_io(OS_FILE_WRITE, TRUE, TRX_SYS_SPACE, 0,
fil_io(OS_FILE_WRITE, true, TRX_SYS_SPACE, 0,
offset, 0, UNIV_PAGE_SIZE,
(void*) (buf_dblwr->write_buf
+ UNIV_PAGE_SIZE * i), NULL);
} else {
/* It is a regular page. Write it directly to the
doublewrite buffer */
fil_io(OS_FILE_WRITE, TRUE, TRX_SYS_SPACE, 0,
fil_io(OS_FILE_WRITE, true, TRX_SYS_SPACE, 0,
offset, 0, UNIV_PAGE_SIZE,
(void*) ((buf_block_t*) bpage)->frame,
NULL);
@ -1072,22 +1130,6 @@ retry:
/* We know that the write has been flushed to disk now
and during recovery we will find it in the doublewrite buffer
blocks. Next do the write to the intended position. */
buf_dblwr_write_block_to_datafile(bpage);
/* Sync the writes to the disk. */
buf_flush_sync_datafiles();
mutex_enter(&buf_dblwr->mutex);
buf_dblwr->s_reserved--;
buf_dblwr->buf_block_arr[i] = NULL;
buf_dblwr->in_use[i] = FALSE;
/* increment the doublewrite flushed pages counter */
srv_stats.dblwr_pages_written.inc();
srv_stats.dblwr_writes.inc();
mutex_exit(&(buf_dblwr->mutex));
buf_dblwr_write_block_to_datafile(bpage, sync);
}
#endif /* !UNIV_HOTBACKUP */

View file

@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 1995, 2011, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 1995, 2013, Oracle and/or its affiliates. All Rights Reserved.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -130,6 +130,60 @@ buf_flush_validate_skip(
}
#endif /* UNIV_DEBUG || UNIV_BUF_DEBUG */
/*******************************************************************//**
Sets hazard pointer during flush_list iteration. */
UNIV_INLINE
void
buf_flush_set_hp(
/*=============*/
buf_pool_t* buf_pool,/*!< in/out: buffer pool instance */
const buf_page_t* bpage) /*!< in: buffer control block */
{
ut_ad(buf_flush_list_mutex_own(buf_pool));
ut_ad(buf_pool->flush_list_hp == NULL || bpage == NULL);
ut_ad(!bpage || buf_page_in_file(bpage));
ut_ad(!bpage || bpage->in_flush_list);
ut_ad(!bpage || buf_pool_from_bpage(bpage) == buf_pool);
buf_pool->flush_list_hp = bpage;
}
/*******************************************************************//**
Checks if the given block is a hazard pointer
@return true if bpage is hazard pointer */
UNIV_INLINE
bool
buf_flush_is_hp(
/*============*/
buf_pool_t* buf_pool,/*!< in: buffer pool instance */
const buf_page_t* bpage) /*!< in: buffer control block */
{
ut_ad(buf_flush_list_mutex_own(buf_pool));
return(buf_pool->flush_list_hp == bpage);
}
/*******************************************************************//**
Whenever we move a block in flush_list (either to remove it or to
relocate it) we check the hazard pointer set by some other thread
doing the flush list scan. If the hazard pointer is the same as the
one we are about going to move then we set it to NULL to force a rescan
in the thread doing the batch. */
UNIV_INLINE
void
buf_flush_update_hp(
/*================*/
buf_pool_t* buf_pool, /*!< in: buffer pool instance */
buf_page_t* bpage) /*!< in: buffer control block */
{
ut_ad(buf_flush_list_mutex_own(buf_pool));
if (buf_flush_is_hp(buf_pool, bpage)) {
buf_flush_set_hp(buf_pool, NULL);
MONITOR_INC(MONITOR_FLUSH_HP_RESCAN);
}
}
/******************************************************************//**
Insert a block in the flush_rbt and returns a pointer to its
predecessor or NULL if no predecessor. The ordering is maintained
@ -471,34 +525,35 @@ buf_flush_ready_for_replace(
}
/********************************************************************//**
Returns TRUE if the block is modified and ready for flushing.
@return TRUE if can flush immediately */
UNIV_INLINE
ibool
Returns true if the block is modified and ready for flushing.
@return true if can flush immediately */
UNIV_INTERN
bool
buf_flush_ready_for_flush(
/*======================*/
buf_page_t* bpage, /*!< in: buffer control block, must be
buf_page_in_file(bpage) */
enum buf_flush flush_type)/*!< in: type of flush */
buf_flush_t flush_type)/*!< in: type of flush */
{
#ifdef UNIV_DEBUG
buf_pool_t* buf_pool = buf_pool_from_bpage(bpage);
ut_ad(buf_pool_mutex_own(buf_pool));
#endif
#endif /* UNIV_DEBUG */
ut_a(buf_page_in_file(bpage));
ut_ad(mutex_own(buf_page_get_mutex(bpage)));
ut_ad(flush_type < BUF_FLUSH_N_TYPES);
if (bpage->oldest_modification == 0
|| buf_page_get_io_fix(bpage) != BUF_IO_NONE) {
return(FALSE);
return(false);
}
ut_ad(bpage->in_flush_list);
switch (flush_type) {
case BUF_FLUSH_LIST:
return(TRUE);
return(true);
case BUF_FLUSH_LRU:
case BUF_FLUSH_SINGLE_PAGE:
@ -514,7 +569,7 @@ buf_flush_ready_for_flush(
}
ut_error;
return(FALSE);
return(false);
}
/********************************************************************//**
@ -535,9 +590,9 @@ buf_flush_remove(
buf_flush_list_mutex_enter(buf_pool);
switch (buf_page_get_state(bpage)) {
case BUF_BLOCK_POOL_WATCH:
case BUF_BLOCK_ZIP_PAGE:
/* Clean compressed pages should not be on the flush list */
case BUF_BLOCK_ZIP_FREE:
case BUF_BLOCK_NOT_USED:
case BUF_BLOCK_READY_FOR_USE:
case BUF_BLOCK_MEMORY:
@ -574,6 +629,7 @@ buf_flush_remove(
ut_a(buf_flush_validate_skip(buf_pool));
#endif /* UNIV_DEBUG || UNIV_BUF_DEBUG */
buf_flush_update_hp(buf_pool, bpage);
buf_flush_list_mutex_exit(buf_pool);
}
@ -652,6 +708,7 @@ buf_flush_relocate_on_flush_list(
ut_a(buf_flush_validate_low(buf_pool));
#endif /* UNIV_DEBUG || UNIV_BUF_DEBUG */
buf_flush_update_hp(buf_pool, bpage);
buf_flush_list_mutex_exit(buf_pool);
}
@ -663,7 +720,7 @@ buf_flush_write_complete(
/*=====================*/
buf_page_t* bpage) /*!< in: pointer to the block in question */
{
enum buf_flush flush_type;
buf_flush_t flush_type;
buf_pool_t* buf_pool = buf_pool_from_bpage(bpage);
ut_ad(bpage);
@ -684,18 +741,7 @@ buf_flush_write_complete(
os_event_set(buf_pool->no_flush[flush_type]);
}
switch (flush_type) {
case BUF_FLUSH_LIST:
case BUF_FLUSH_LRU:
buf_dblwr_update();
break;
case BUF_FLUSH_SINGLE_PAGE:
/* Single page flushes are synchronous. No need
to update doublewrite */
break;
case BUF_FLUSH_N_TYPES:
ut_error;
}
buf_dblwr_update(bpage, flush_type);
}
#endif /* !UNIV_HOTBACKUP */
@ -826,28 +872,6 @@ buf_flush_init_for_writing(
}
#ifndef UNIV_HOTBACKUP
/********************************************************************//**
Flush a batch of writes to the datafiles that have already been
written by the OS. */
UNIV_INTERN
void
buf_flush_sync_datafiles(void)
/*==========================*/
{
/* Wake possible simulated aio thread to actually post the
writes to the operating system */
os_aio_simulated_wake_handler_threads();
/* Wait that all async writes to tablespaces have been posted to
the OS */
os_aio_wait_until_no_pending_writes();
/* Now we flush the data to disk (for example, with fsync) */
fil_flush_file_spaces(FIL_TABLESPACE);
return;
}
/********************************************************************//**
Does an asynchronous write of a buffer page. NOTE: in simulated aio and
also when the doublewrite buffer is used, we must call
@ -858,7 +882,8 @@ void
buf_flush_write_block_low(
/*======================*/
buf_page_t* bpage, /*!< in: buffer block to write */
enum buf_flush flush_type) /*!< in: type of flush */
buf_flush_t flush_type, /*!< in: type of flush */
bool sync) /*!< in: true if sync IO request */
{
ulint zip_size = buf_page_get_zip_size(bpage);
page_t* frame = NULL;
@ -903,7 +928,7 @@ buf_flush_write_block_low(
log_write_up_to(bpage->newest_modification, LOG_WAIT_ALL_GROUPS, TRUE);
#endif
switch (buf_page_get_state(bpage)) {
case BUF_BLOCK_ZIP_FREE:
case BUF_BLOCK_POOL_WATCH:
case BUF_BLOCK_ZIP_PAGE: /* The page should be dirty. */
case BUF_BLOCK_NOT_USED:
case BUF_BLOCK_READY_FOR_USE:
@ -935,15 +960,29 @@ buf_flush_write_block_low(
if (!srv_use_doublewrite_buf || !buf_dblwr) {
fil_io(OS_FILE_WRITE | OS_AIO_SIMULATED_WAKE_LATER,
FALSE, buf_page_get_space(bpage), zip_size,
sync, buf_page_get_space(bpage), zip_size,
buf_page_get_page_no(bpage), 0,
zip_size ? zip_size : UNIV_PAGE_SIZE,
frame, bpage);
} else if (flush_type == BUF_FLUSH_SINGLE_PAGE) {
buf_dblwr_write_single_page(bpage);
buf_dblwr_write_single_page(bpage, sync);
} else {
ut_ad(!sync);
buf_dblwr_add_to_batch(bpage);
}
/* When doing single page flushing the IO is done synchronously
and we flush the changes to disk only for the tablespace we
are working on. */
if (sync) {
ut_ad(flush_type == BUF_FLUSH_SINGLE_PAGE);
fil_flush(buf_page_get_space(bpage));
buf_page_io_complete(bpage);
}
/* Increment the counter of I/O operations used
for selecting LRU policy. */
buf_LRU_stat_inc_io();
}
/********************************************************************//**
@ -959,7 +998,8 @@ buf_flush_page(
/*===========*/
buf_pool_t* buf_pool, /*!< in: buffer pool instance */
buf_page_t* bpage, /*!< in: buffer control block */
buf_flush flush_type) /*!< in: type of flush */
buf_flush_t flush_type, /*!< in: type of flush */
bool sync) /*!< in: true if sync IO request */
{
ib_mutex_t* block_mutex;
ibool is_uncompressed;
@ -967,6 +1007,7 @@ buf_flush_page(
ut_ad(flush_type < BUF_FLUSH_N_TYPES);
ut_ad(buf_pool_mutex_own(buf_pool));
ut_ad(buf_page_in_file(bpage));
ut_ad(!sync || flush_type == BUF_FLUSH_SINGLE_PAGE);
block_mutex = buf_page_get_mutex(bpage);
ut_ad(mutex_own(block_mutex));
@ -1062,7 +1103,7 @@ buf_flush_page(
flush_type, bpage->space, bpage->offset);
}
#endif /* UNIV_DEBUG */
buf_flush_write_block_low(bpage, flush_type);
buf_flush_write_block_low(bpage, flush_type, sync);
}
# if defined UNIV_DEBUG || defined UNIV_IBUF_DEBUG
@ -1089,8 +1130,7 @@ buf_flush_page_try(
/* The following call will release the buffer pool and
block mutex. */
buf_flush_page(buf_pool, &block->page, BUF_FLUSH_SINGLE_PAGE);
buf_flush_sync_datafiles();
buf_flush_page(buf_pool, &block->page, BUF_FLUSH_SINGLE_PAGE, true);
return(TRUE);
}
# endif /* UNIV_DEBUG || UNIV_IBUF_DEBUG */
@ -1103,7 +1143,7 @@ buf_flush_check_neighbor(
/*=====================*/
ulint space, /*!< in: space id */
ulint offset, /*!< in: page offset */
enum buf_flush flush_type) /*!< in: BUF_FLUSH_LRU or
buf_flush_t flush_type) /*!< in: BUF_FLUSH_LRU or
BUF_FLUSH_LIST */
{
buf_page_t* bpage;
@ -1153,7 +1193,7 @@ buf_flush_try_neighbors(
/*====================*/
ulint space, /*!< in: space id */
ulint offset, /*!< in: page offset */
enum buf_flush flush_type, /*!< in: BUF_FLUSH_LRU or
buf_flush_t flush_type, /*!< in: BUF_FLUSH_LRU or
BUF_FLUSH_LIST */
ulint n_flushed, /*!< in: number of pages
flushed so far in this batch */
@ -1274,7 +1314,7 @@ buf_flush_try_neighbors(
doublewrite buffer before we start
waiting. */
buf_flush_page(buf_pool, bpage, flush_type);
buf_flush_page(buf_pool, bpage, flush_type, false);
ut_ad(!mutex_own(block_mutex));
ut_ad(!buf_pool_mutex_own(buf_pool));
count++;
@ -1311,7 +1351,7 @@ buf_flush_page_and_try_neighbors(
buf_page_t* bpage, /*!< in: buffer control block,
must be
buf_page_in_file(bpage) */
enum buf_flush flush_type, /*!< in: BUF_FLUSH_LRU
buf_flush_t flush_type, /*!< in: BUF_FLUSH_LRU
or BUF_FLUSH_LIST */
ulint n_to_flush, /*!< in: number of pages to
flush */
@ -1396,7 +1436,7 @@ buf_free_from_unzip_LRU_list_batch(
&& lru_len > UT_LIST_GET_LEN(buf_pool->LRU) / 10) {
++scanned;
if (buf_LRU_free_block(&block->page, FALSE)) {
if (buf_LRU_free_page(&block->page, false)) {
/* Block was freed. buf_pool->mutex potentially
released and reacquired */
++count;
@ -1473,7 +1513,7 @@ buf_flush_LRU_list_batch(
of the flushed pages then the scan becomes
O(n*n). */
if (evict) {
if (buf_LRU_free_block(bpage, TRUE)) {
if (buf_LRU_free_page(bpage, true)) {
/* buf_pool->mutex was potentially
released and reacquired. */
bpage = UT_LIST_GET_LAST(buf_pool->LRU);
@ -1561,82 +1601,62 @@ buf_do_flush_list_batch(
their number does not exceed
min_n) */
{
ulint len;
buf_page_t* bpage;
ulint count = 0;
ulint scanned = 0;
ut_ad(buf_pool_mutex_own(buf_pool));
/* If we have flushed enough, leave the loop */
do {
/* Start from the end of the list looking for a suitable
block to be flushed. */
buf_flush_list_mutex_enter(buf_pool);
ulint len = UT_LIST_GET_LEN(buf_pool->flush_list);
/* We use len here because theoretically insertions can
happen in the flush_list below while we are traversing
it for a suitable candidate for flushing. We'd like to
set a limit on how farther we are willing to traverse
the list. */
len = UT_LIST_GET_LEN(buf_pool->flush_list);
bpage = UT_LIST_GET_LAST(buf_pool->flush_list);
/* In order not to degenerate this scan to O(n*n) we attempt
to preserve pointer of previous block in the flush list. To do
so we declare it a hazard pointer. Any thread working on the
flush list must check the hazard pointer and if it is removing
the same block then it must reset it. */
for (buf_page_t* bpage = UT_LIST_GET_LAST(buf_pool->flush_list);
count < min_n && bpage != NULL && len > 0
&& bpage->oldest_modification < lsn_limit;
++scanned) {
if (bpage) {
ut_a(bpage->oldest_modification > 0);
}
if (!bpage || bpage->oldest_modification >= lsn_limit) {
/* We have flushed enough */
buf_flush_list_mutex_exit(buf_pool);
break;
}
buf_page_t* prev;
ut_a(bpage->oldest_modification > 0);
ut_ad(bpage->in_flush_list);
prev = UT_LIST_GET_PREV(list, bpage);
buf_flush_set_hp(buf_pool, prev);
buf_flush_list_mutex_exit(buf_pool);
/* The list may change during the flushing and we cannot
safely preserve within this function a pointer to a
block in the list! */
while (bpage != NULL
&& len > 0
&& !buf_flush_page_and_try_neighbors(
bpage, BUF_FLUSH_LIST, min_n, &count)) {
#ifdef UNIV_DEBUG
bool flushed =
#endif /* UNIV_DEBUG */
buf_flush_page_and_try_neighbors(
bpage, BUF_FLUSH_LIST, min_n, &count);
++scanned;
buf_flush_list_mutex_enter(buf_pool);
/* If we are here that means that buf_pool->mutex
was not released in buf_flush_page_and_try_neighbors()
above and this guarantees that bpage didn't get
relocated since we released the flush_list
mutex above. There is a chance, however, that
the bpage got removed from flush_list (not
currently possible because flush_list_remove()
also obtains buf_pool mutex but that may change
in future). To avoid this scenario we check
the oldest_modification and if it is zero
we start all over again. */
if (bpage->oldest_modification == 0) {
buf_flush_list_mutex_exit(buf_pool);
break;
}
ut_ad(flushed || buf_flush_is_hp(buf_pool, prev));
bpage = UT_LIST_GET_PREV(list, bpage);
if (!buf_flush_is_hp(buf_pool, prev)) {
/* The hazard pointer was reset by some other
thread. Restart the scan. */
ut_ad(buf_flush_is_hp(buf_pool, NULL));
bpage = UT_LIST_GET_LAST(buf_pool->flush_list);
len = UT_LIST_GET_LEN(buf_pool->flush_list);
} else {
bpage = prev;
--len;
buf_flush_set_hp(buf_pool, NULL);
}
ut_ad(!bpage || bpage->in_flush_list);
buf_flush_list_mutex_exit(buf_pool);
--len;
}
} while (count < min_n && bpage != NULL && len > 0);
buf_flush_list_mutex_exit(buf_pool);
MONITOR_INC_VALUE_CUMULATIVE(MONITOR_FLUSH_BATCH_SCANNED,
MONITOR_FLUSH_BATCH_SCANNED_NUM_CALL,
@ -1660,7 +1680,7 @@ ulint
buf_flush_batch(
/*============*/
buf_pool_t* buf_pool, /*!< in: buffer pool instance */
enum buf_flush flush_type, /*!< in: BUF_FLUSH_LRU or
buf_flush_t flush_type, /*!< in: BUF_FLUSH_LRU or
BUF_FLUSH_LIST; if BUF_FLUSH_LIST,
then the caller must not own any
latches on pages */
@ -1716,7 +1736,7 @@ static
void
buf_flush_common(
/*=============*/
enum buf_flush flush_type, /*!< in: type of flush */
buf_flush_t flush_type, /*!< in: type of flush */
ulint page_count) /*!< in: number of pages flushed */
{
buf_dblwr_flush_buffered_writes();
@ -1742,7 +1762,7 @@ ibool
buf_flush_start(
/*============*/
buf_pool_t* buf_pool, /*!< buffer pool instance */
enum buf_flush flush_type) /*!< in: BUF_FLUSH_LRU
buf_flush_t flush_type) /*!< in: BUF_FLUSH_LRU
or BUF_FLUSH_LIST */
{
buf_pool_mutex_enter(buf_pool);
@ -1771,7 +1791,7 @@ void
buf_flush_end(
/*==========*/
buf_pool_t* buf_pool, /*!< buffer pool instance */
enum buf_flush flush_type) /*!< in: BUF_FLUSH_LRU
buf_flush_t flush_type) /*!< in: BUF_FLUSH_LRU
or BUF_FLUSH_LIST */
{
buf_pool_mutex_enter(buf_pool);
@ -1797,7 +1817,7 @@ void
buf_flush_wait_batch_end(
/*=====================*/
buf_pool_t* buf_pool, /*!< buffer pool instance */
enum buf_flush type) /*!< in: BUF_FLUSH_LRU
buf_flush_t type) /*!< in: BUF_FLUSH_LRU
or BUF_FLUSH_LIST */
{
ut_ad(type == BUF_FLUSH_LRU || type == BUF_FLUSH_LIST);
@ -1967,7 +1987,7 @@ buf_flush_single_page_from_LRU(
buf_page_t* bpage;
ib_mutex_t* block_mutex;
ibool freed;
ibool evict_zip;
bool evict_zip;
buf_pool_mutex_enter(buf_pool);
@ -2000,9 +2020,7 @@ buf_flush_single_page_from_LRU(
/* The following call will release the buffer pool and
block mutex. */
buf_flush_page(buf_pool, bpage, BUF_FLUSH_SINGLE_PAGE);
buf_flush_sync_datafiles();
buf_flush_page(buf_pool, bpage, BUF_FLUSH_SINGLE_PAGE, true);
/* At this point the page has been written to the disk.
As we are not holding buffer pool or block mutex therefore
@ -2037,7 +2055,7 @@ buf_flush_single_page_from_LRU(
evict_zip = !buf_LRU_evict_from_unzip_LRU(buf_pool);;
freed = buf_LRU_free_block(bpage, evict_zip);
freed = buf_LRU_free_page(bpage, evict_zip);
buf_pool_mutex_exit(buf_pool);
return(freed);
@ -2060,12 +2078,21 @@ buf_flush_LRU_tail(void)
for (ulint i = 0; i < srv_buf_pool_instances; i++) {
buf_pool_t* buf_pool = buf_pool_from_array(i);
ulint scan_depth;
/* srv_LRU_scan_depth can be arbitrarily large value.
We cap it with current LRU size. */
buf_pool_mutex_enter(buf_pool);
scan_depth = UT_LIST_GET_LEN(buf_pool->LRU);
buf_pool_mutex_exit(buf_pool);
scan_depth = ut_min(srv_LRU_scan_depth, scan_depth);
/* We divide LRU flush into smaller chunks because
there may be user threads waiting for the flush to
end in buf_LRU_get_free_block(). */
for (ulint j = 0;
j < srv_LRU_scan_depth;
j < scan_depth;
j += PAGE_CLEANER_LRU_BATCH_CHUNK_SIZE) {
ulint n_flushed = 0;
@ -2074,11 +2101,22 @@ buf_flush_LRU_tail(void)
that can trigger an LRU flush. It is possible
that a batch triggered during last iteration is
still running, */
buf_flush_LRU(buf_pool,
if (buf_flush_LRU(buf_pool,
PAGE_CLEANER_LRU_BATCH_CHUNK_SIZE,
&n_flushed);
&n_flushed)) {
/* Allowed only one batch per
buffer pool instance. */
buf_flush_wait_batch_end(
buf_pool, BUF_FLUSH_LRU);
}
if (n_flushed) {
total_flushed += n_flushed;
} else {
/* Nothing to flush */
break;
}
}
}
@ -2272,9 +2310,9 @@ page_cleaner_flush_pages_if_needed(void)
oldest_lsn = buf_pool_get_oldest_modification();
ut_ad(oldest_lsn <= cur_lsn);
ut_ad(oldest_lsn <= log_get_lsn());
age = cur_lsn - oldest_lsn;
age = cur_lsn > oldest_lsn ? cur_lsn - oldest_lsn : 0;
pct_for_dirty = af_get_pct_for_dirty();
pct_for_lsn = af_get_pct_for_lsn(age);

View file

@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 1995, 2011, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 1995, 2013, Oracle and/or its affiliates. All Rights Reserved.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -137,19 +137,19 @@ The caller must hold buf_pool->mutex, the buf_page_get_mutex() mutex
and the appropriate hash_lock. This function will release the
buf_page_get_mutex() and the hash_lock.
If a compressed page or a compressed-only block descriptor is freed,
other compressed pages or compressed-only block descriptors may be
relocated.
@return the new state of the block (BUF_BLOCK_ZIP_FREE if the state
was BUF_BLOCK_ZIP_PAGE, or BUF_BLOCK_REMOVE_HASH otherwise) */
static
enum buf_page_state
buf_LRU_block_remove_hashed_page(
/*=============================*/
If a compressed page is freed other compressed pages may be relocated.
@retval true if BUF_BLOCK_FILE_PAGE was removed from page_hash. The
caller needs to free the page to the free list
@retval false if BUF_BLOCK_ZIP_PAGE was removed from page_hash. In
this case the block is already returned to the buddy allocator. */
static __attribute__((nonnull, warn_unused_result))
bool
buf_LRU_block_remove_hashed(
/*========================*/
buf_page_t* bpage, /*!< in: block, must contain a file page and
be in a state where it can be freed; there
may or may not be a hash index to the page */
ibool zip); /*!< in: TRUE if should remove also the
bool zip); /*!< in: true if should remove also the
compressed page of an uncompressed page */
/******************************************************************//**
Puts a file page whose has no hash index to the free list. */
@ -460,14 +460,9 @@ buf_flush_or_remove_page(
don't remove else remove without
flushing to disk */
{
ib_mutex_t* block_mutex;
bool processed = false;
ut_ad(buf_pool_mutex_own(buf_pool));
ut_ad(buf_flush_list_mutex_own(buf_pool));
block_mutex = buf_page_get_mutex(bpage);
/* bpage->space and bpage->io_fix are protected by
buf_pool->mutex and block_mutex. It is safe to check
them while holding buf_pool->mutex only. */
@ -477,13 +472,18 @@ buf_flush_or_remove_page(
/* We cannot remove this page during this scan
yet; maybe the system is currently reading it
in, or flushing the modifications to the file */
return(false);
} else {
}
ib_mutex_t* block_mutex = buf_page_get_mutex(bpage);
bool processed = false;
/* We have to release the flush_list_mutex to obey the
latching order. We are however guaranteed that the page
will stay in the flush_list because buf_flush_remove()
needs buf_pool->mutex as well (for the non-flush case). */
will stay in the flush_list and won't be relocated because
buf_flush_remove() and buf_flush_relocate_on_flush_list()
need buf_pool->mutex as well. */
buf_flush_list_mutex_exit(buf_pool);
@ -491,16 +491,7 @@ buf_flush_or_remove_page(
ut_ad(bpage->oldest_modification != 0);
if (bpage->buf_fix_count > 0) {
mutex_exit(block_mutex);
/* We cannot remove this page yet;
maybe the system is currently reading
it in, or flushing the modifications
to the file */
} else if (!flush) {
if (!flush) {
buf_flush_remove(bpage);
@ -508,16 +499,13 @@ buf_flush_or_remove_page(
processed = true;
} else if (buf_page_get_io_fix(bpage) != BUF_IO_NONE) {
/* Check the status again after releasing the flush
list mutex and acquiring the block mutex. The background
flush thread may be in the process of flushing this
page when we released the flush list mutex. */
} else if (buf_flush_ready_for_flush(bpage,
BUF_FLUSH_SINGLE_PAGE)) {
/* The following call will release the buffer pool
and block mutex. */
buf_flush_page(buf_pool, bpage, BUF_FLUSH_SINGLE_PAGE);
buf_flush_page(buf_pool, bpage, BUF_FLUSH_SINGLE_PAGE, false);
ut_ad(!mutex_own(block_mutex));
/* Wake possible simulated aio thread to actually
post the writes to the operating system */
@ -527,13 +515,17 @@ buf_flush_or_remove_page(
processed = true;
} else {
/* Not ready for flush. It can't be IO fixed because we
checked for that at the start of the function. It must
be buffer fixed. */
ut_ad(bpage->buf_fix_count > 0);
mutex_exit(block_mutex);
}
buf_flush_list_mutex_enter(buf_pool);
}
ut_ad(!mutex_own(block_mutex));
ut_ad(buf_pool_mutex_own(buf_pool));
return(processed);
}
@ -562,10 +554,12 @@ buf_flush_or_remove_pages(
buf_page_t* prev;
buf_page_t* bpage;
ulint processed = 0;
bool all_freed = true;
buf_flush_list_mutex_enter(buf_pool);
rescan:
bool all_freed = true;
for (bpage = UT_LIST_GET_LAST(buf_pool->flush_list);
bpage != NULL;
bpage = prev) {
@ -585,9 +579,33 @@ buf_flush_or_remove_pages(
} else if (!buf_flush_or_remove_page(buf_pool, bpage, flush)) {
/* Remove was unsuccessful, we have to try again
by scanning the entire list from the end. */
by scanning the entire list from the end.
This also means that we never released the
buf_pool mutex. Therefore we can trust the prev
pointer.
buf_flush_or_remove_page() released the
flush list mutex but not the buf_pool mutex.
Therefore it is possible that a new page was
added to the flush list. For example, in case
where we are at the head of the flush list and
prev == NULL. That is OK because we have the
tablespace quiesced and no new pages for this
space-id should enter flush_list. This is
because the only callers of this function are
DROP TABLE and FLUSH TABLE FOR EXPORT.
We know that we'll have to do at least one more
scan but we don't break out of loop here and
try to do as much work as we can in this
iteration. */
all_freed = false;
} else if (flush) {
/* The processing was successful. And during the
processing we have released the buf_pool mutex
when calling buf_page_flush(). We cannot trust
prev pointer. */
goto rescan;
}
++processed;
@ -649,7 +667,7 @@ buf_flush_dirty_pages(
ut_ad(buf_flush_validate(buf_pool));
if (err == DB_FAIL) {
os_thread_sleep(20000);
os_thread_sleep(2000);
}
/* DB_FAIL is a soft error, it means that the task wasn't
@ -658,6 +676,9 @@ buf_flush_dirty_pages(
ut_ad(buf_flush_validate(buf_pool));
} while (err == DB_FAIL);
ut_ad(err == DB_INTERRUPTED
|| buf_pool_get_dirty_pages_count(buf_pool, id) == 0);
}
/******************************************************************//**
@ -778,22 +799,16 @@ scan_again:
/* Remove from the LRU list. */
if (buf_LRU_block_remove_hashed_page(bpage, TRUE)
!= BUF_BLOCK_ZIP_FREE) {
if (buf_LRU_block_remove_hashed(bpage, true)) {
buf_LRU_block_free_hashed_page((buf_block_t*) bpage);
} else {
/* The block_mutex should have been released
by buf_LRU_block_remove_hashed_page() when it
returns BUF_BLOCK_ZIP_FREE. */
ut_ad(block_mutex == &buf_pool->zip_mutex);
}
ut_ad(!mutex_own(block_mutex));
#ifdef UNIV_SYNC_DEBUG
/* buf_LRU_block_remove_hashed_page() releases the hash_lock */
/* buf_LRU_block_remove_hashed() releases the hash_lock */
ut_ad(!rw_lock_own(hash_lock, RW_LOCK_EX));
ut_ad(!rw_lock_own(hash_lock, RW_LOCK_SHARED));
#endif /* UNIV_SYNC_DEBUG */
@ -835,15 +850,11 @@ buf_LRU_remove_pages(
case BUF_REMOVE_FLUSH_NO_WRITE:
ut_a(trx == 0);
buf_flush_dirty_pages(buf_pool, id, false, NULL);
ut_ad(trx_is_interrupted(trx)
|| buf_pool_get_dirty_pages_count(buf_pool, id) == 0);
break;
case BUF_REMOVE_FLUSH_WRITE:
ut_a(trx != 0);
buf_flush_dirty_pages(buf_pool, id, true, trx);
ut_ad(trx_is_interrupted(trx)
|| buf_pool_get_dirty_pages_count(buf_pool, id) == 0);
/* Ensure that all asynchronous IO is completed. */
os_aio_wait_until_no_pending_writes();
fil_flush(id);
@ -880,10 +891,14 @@ buf_LRU_flush_or_remove_pages(
switch (buf_remove) {
case BUF_REMOVE_ALL_NO_WRITE:
case BUF_REMOVE_FLUSH_NO_WRITE:
buf_LRU_drop_page_hash_for_tablespace(buf_pool, id);
break;
case BUF_REMOVE_FLUSH_NO_WRITE:
/* It is a DROP TABLE for a single table
tablespace. No AHI entries exist because
we already dealt with them when freeing up
extents. */
case BUF_REMOVE_FLUSH_WRITE:
/* We allow read-only queries against the
table, there is no need to drop the AHI entries. */
@ -892,13 +907,6 @@ buf_LRU_flush_or_remove_pages(
buf_LRU_remove_pages(buf_pool, id, buf_remove, trx);
}
#ifdef UNIV_DEBUG
if (trx != 0 && id != 0) {
ut_ad(trx_is_interrupted(trx)
|| buf_flush_get_dirty_pages_count(id) == 0);
}
#endif /* UNIV_DEBUG */
}
#if defined UNIV_DEBUG || defined UNIV_BUF_DEBUG
@ -972,7 +980,7 @@ buf_LRU_free_from_unzip_LRU_list(
ut_ad(block->in_unzip_LRU_list);
ut_ad(block->page.in_LRU_list);
freed = buf_LRU_free_block(&block->page, FALSE);
freed = buf_LRU_free_page(&block->page, false);
block = prev_block;
}
@ -1017,7 +1025,7 @@ buf_LRU_free_from_common_LRU_list(
ut_ad(bpage->in_LRU_list);
accessed = buf_page_is_accessed(bpage);
freed = buf_LRU_free_block(bpage, TRUE);
freed = buf_LRU_free_page(bpage, true);
if (freed && !accessed) {
/* Keep track of pages that are evicted without
ever being accessed. This gives us a measure of
@ -1788,24 +1796,23 @@ buf_LRU_make_block_old(
Try to free a block. If bpage is a descriptor of a compressed-only
page, the descriptor object will be freed as well.
NOTE: If this function returns TRUE, it will temporarily
NOTE: If this function returns true, it will temporarily
release buf_pool->mutex. Furthermore, the page frame will no longer be
accessible via bpage.
The caller must hold buf_pool->mutex and must not hold any
buf_page_get_mutex() when calling this function.
@return TRUE if freed, FALSE otherwise. */
@return true if freed, false otherwise. */
UNIV_INTERN
ibool
buf_LRU_free_block(
bool
buf_LRU_free_page(
/*===============*/
buf_page_t* bpage, /*!< in: block to be freed */
ibool zip) /*!< in: TRUE if should remove also the
bool zip) /*!< in: true if should remove also the
compressed page of an uncompressed page */
{
buf_page_t* b = NULL;
buf_pool_t* buf_pool = buf_pool_from_bpage(bpage);
enum buf_page_state page_state;
const ulint fold = buf_page_address_fold(bpage->space,
bpage->offset);
rw_lock_t* hash_lock = buf_page_hash_lock_get(buf_pool, fold);
@ -1853,7 +1860,7 @@ buf_LRU_free_block(
func_exit:
rw_lock_x_unlock(hash_lock);
mutex_exit(block_mutex);
return(FALSE);
return(false);
} else if (buf_page_get_state(bpage) == BUF_BLOCK_FILE_PAGE) {
b = buf_page_alloc_descriptor();
@ -1885,20 +1892,16 @@ func_exit:
#endif /* UNIV_SYNC_DEBUG */
ut_ad(buf_page_can_relocate(bpage));
page_state = buf_LRU_block_remove_hashed_page(bpage, zip);
if (!buf_LRU_block_remove_hashed(bpage, zip)) {
return(true);
}
#ifdef UNIV_SYNC_DEBUG
/* buf_LRU_block_remove_hashed_page() releases the hash_lock */
/* buf_LRU_block_remove_hashed() releases the hash_lock */
ut_ad(!rw_lock_own(hash_lock, RW_LOCK_EX)
&& !rw_lock_own(hash_lock, RW_LOCK_SHARED));
#endif /* UNIV_SYNC_DEBUG */
if (page_state == BUF_BLOCK_ZIP_FREE) {
return(TRUE);
}
ut_ad(page_state == BUF_BLOCK_REMOVE_HASH);
/* We have just freed a BUF_BLOCK_FILE_PAGE. If b != NULL
then it was a compressed page with an uncompressed frame and
we are interested in freeing only the uncompressed frame.
@ -1926,7 +1929,7 @@ func_exit:
/* The fields in_page_hash and in_LRU_list of
the to-be-freed block descriptor should have
been cleared in
buf_LRU_block_remove_hashed_page(), which
buf_LRU_block_remove_hashed(), which
invokes buf_LRU_remove_block(). */
ut_ad(!bpage->in_page_hash);
ut_ad(!bpage->in_LRU_list);
@ -1935,7 +1938,7 @@ func_exit:
ut_ad(!((buf_block_t*) bpage)->in_unzip_LRU_list);
/* The fields of bpage were copied to b before
buf_LRU_block_remove_hashed_page() was invoked. */
buf_LRU_block_remove_hashed() was invoked. */
ut_ad(!b->in_zip_hash);
ut_ad(b->in_page_hash);
ut_ad(b->in_LRU_list);
@ -2037,7 +2040,7 @@ func_exit:
/* Remove possible adaptive hash index on the page.
The page was declared uninitialized by
buf_LRU_block_remove_hashed_page(). We need to flag
buf_LRU_block_remove_hashed(). We need to flag
the contents of the page valid (which it still is) in
order to avoid bogus Valgrind warnings.*/
@ -2073,7 +2076,7 @@ func_exit:
mutex_exit(block_mutex);
buf_LRU_block_free_hashed_page((buf_block_t*) bpage);
return(TRUE);
return(true);
}
/******************************************************************//**
@ -2147,19 +2150,19 @@ The caller must hold buf_pool->mutex, the buf_page_get_mutex() mutex
and the appropriate hash_lock. This function will release the
buf_page_get_mutex() and the hash_lock.
If a compressed page or a compressed-only block descriptor is freed,
other compressed pages or compressed-only block descriptors may be
relocated.
@return the new state of the block (BUF_BLOCK_ZIP_FREE if the state
was BUF_BLOCK_ZIP_PAGE, or BUF_BLOCK_REMOVE_HASH otherwise) */
If a compressed page is freed other compressed pages may be relocated.
@retval true if BUF_BLOCK_FILE_PAGE was removed from page_hash. The
caller needs to free the page to the free list
@retval false if BUF_BLOCK_ZIP_PAGE was removed from page_hash. In
this case the block is already returned to the buddy allocator. */
static
enum buf_page_state
buf_LRU_block_remove_hashed_page(
/*=============================*/
bool
buf_LRU_block_remove_hashed(
/*========================*/
buf_page_t* bpage, /*!< in: block, must contain a file page and
be in a state where it can be freed; there
may or may not be a hash index to the page */
ibool zip) /*!< in: TRUE if should remove also the
bool zip) /*!< in: true if should remove also the
compressed page of an uncompressed page */
{
ulint fold;
@ -2252,7 +2255,7 @@ buf_LRU_block_remove_hashed_page(
UNIV_MEM_ASSERT_W(bpage->zip.data,
page_zip_get_size(&bpage->zip));
break;
case BUF_BLOCK_ZIP_FREE:
case BUF_BLOCK_POOL_WATCH:
case BUF_BLOCK_ZIP_DIRTY:
case BUF_BLOCK_NOT_USED:
case BUF_BLOCK_READY_FOR_USE:
@ -2319,7 +2322,7 @@ buf_LRU_block_remove_hashed_page(
buf_pool_mutex_exit_allow(buf_pool);
buf_page_free_descriptor(bpage);
return(BUF_BLOCK_ZIP_FREE);
return(false);
case BUF_BLOCK_FILE_PAGE:
memset(((buf_block_t*) bpage)->frame
@ -2370,9 +2373,9 @@ buf_LRU_block_remove_hashed_page(
page_zip_set_size(&bpage->zip, 0);
}
return(BUF_BLOCK_REMOVE_HASH);
return(true);
case BUF_BLOCK_ZIP_FREE:
case BUF_BLOCK_POOL_WATCH:
case BUF_BLOCK_ZIP_DIRTY:
case BUF_BLOCK_NOT_USED:
case BUF_BLOCK_READY_FOR_USE:
@ -2382,7 +2385,7 @@ buf_LRU_block_remove_hashed_page(
}
ut_error;
return(BUF_BLOCK_ZIP_FREE);
return(false);
}
/******************************************************************//**
@ -2427,12 +2430,11 @@ buf_LRU_free_one_page(
rw_lock_x_lock(hash_lock);
mutex_enter(block_mutex);
if (buf_LRU_block_remove_hashed_page(bpage, TRUE)
!= BUF_BLOCK_ZIP_FREE) {
if (buf_LRU_block_remove_hashed(bpage, true)) {
buf_LRU_block_free_hashed_page((buf_block_t*) bpage);
}
/* buf_LRU_block_remove_hashed_page() releases hash_lock and block_mutex */
/* buf_LRU_block_remove_hashed() releases hash_lock and block_mutex */
#ifdef UNIV_SYNC_DEBUG
ut_ad(!rw_lock_own(hash_lock, RW_LOCK_EX)
&& !rw_lock_own(hash_lock, RW_LOCK_SHARED));
@ -2606,7 +2608,7 @@ buf_LRU_validate_instance(
bpage = UT_LIST_GET_NEXT(LRU, bpage)) {
switch (buf_page_get_state(bpage)) {
case BUF_BLOCK_ZIP_FREE:
case BUF_BLOCK_POOL_WATCH:
case BUF_BLOCK_NOT_USED:
case BUF_BLOCK_READY_FOR_USE:
case BUF_BLOCK_MEMORY:

View file

@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 1995, 2012, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 1995, 2013, Oracle and/or its affiliates. All Rights Reserved.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -107,7 +107,7 @@ buf_read_page_low(
dberr_t* err, /*!< out: DB_SUCCESS or DB_TABLESPACE_DELETED if we are
trying to read from a non-existent tablespace, or a
tablespace which is just now being dropped */
ibool sync, /*!< in: TRUE if synchronous aio is desired */
bool sync, /*!< in: true if synchronous aio is desired */
ulint mode, /*!< in: BUF_READ_IBUF_PAGES_ONLY, ...,
ORed to OS_AIO_SIMULATED_WAKE_LATER (see below
at read-ahead functions) */
@ -152,7 +152,7 @@ buf_read_page_low(
syncronous i/o, to make sure they do not get involved in
thread deadlocks. */
sync = TRUE;
sync = true;
}
/* The following call will also check if the tablespace does not exist
@ -169,15 +169,17 @@ buf_read_page_low(
#ifdef UNIV_DEBUG
if (buf_debug_prints) {
fprintf(stderr,
"Posting read request for page %lu, sync %lu\n",
(ulong) offset,
(ulong) sync);
"Posting read request for page %lu, sync %s\n",
(ulong) offset, sync ? "true" : "false");
}
#endif
ut_ad(buf_page_in_file(bpage));
if (sync) {
thd_wait_begin(NULL, THD_WAIT_DISKIO);
}
if (zip_size) {
*err = fil_io(OS_FILE_READ | wake_later
| ignore_nonexistent_pages,
@ -191,7 +193,10 @@ buf_read_page_low(
sync, space, 0, offset, 0, UNIV_PAGE_SIZE,
((buf_block_t*) bpage)->frame, bpage);
}
if (sync) {
thd_wait_end(NULL);
}
if (*err != DB_SUCCESS) {
if (ignore_nonexistent_pages || *err == DB_TABLESPACE_DELETED) {
@ -337,7 +342,7 @@ read_ahead:
if (!ibuf_bitmap_page(zip_size, i)) {
count += buf_read_page_low(
&err, FALSE,
&err, false,
ibuf_mode | OS_AIO_SIMULATED_WAKE_LATER,
space, zip_size, FALSE,
tablespace_version, i);
@ -401,7 +406,7 @@ buf_read_page(
/* We do the i/o in the synchronous aio mode to save thread
switches: hence TRUE */
count = buf_read_page_low(&err, TRUE, BUF_READ_ANY_PAGE, space,
count = buf_read_page_low(&err, true, BUF_READ_ANY_PAGE, space,
zip_size, FALSE,
tablespace_version, offset);
srv_stats.buf_pool_reads.add(count);
@ -447,7 +452,7 @@ buf_read_page_async(
tablespace_version = fil_space_get_version(space);
count = buf_read_page_low(&err, TRUE, BUF_READ_ANY_PAGE
count = buf_read_page_low(&err, true, BUF_READ_ANY_PAGE
| OS_AIO_SIMULATED_WAKE_LATER
| BUF_READ_IGNORE_NONEXISTENT_PAGES,
space, zip_size, FALSE,
@ -708,7 +713,7 @@ buf_read_ahead_linear(
if (!ibuf_bitmap_page(zip_size, i)) {
count += buf_read_page_low(
&err, FALSE,
&err, false,
ibuf_mode,
space, zip_size, FALSE, tablespace_version, i);
if (err == DB_TABLESPACE_DELETED) {
@ -754,7 +759,7 @@ UNIV_INTERN
void
buf_read_ibuf_merge_pages(
/*======================*/
ibool sync, /*!< in: TRUE if the caller
bool sync, /*!< in: true if the caller
wants this function to wait
for the highest address page
to get read in, before this
@ -893,11 +898,11 @@ buf_read_recv_pages(
os_aio_print_debug = FALSE;
if ((i + 1 == n_stored) && sync) {
buf_read_page_low(&err, TRUE, BUF_READ_ANY_PAGE, space,
buf_read_page_low(&err, true, BUF_READ_ANY_PAGE, space,
zip_size, TRUE, tablespace_version,
page_nos[i]);
} else {
buf_read_page_low(&err, FALSE, BUF_READ_ANY_PAGE
buf_read_page_low(&err, false, BUF_READ_ANY_PAGE
| OS_AIO_SIMULATED_WAKE_LATER,
space, zip_size, TRUE,
tablespace_version, page_nos[i]);

View file

@ -1,6 +1,6 @@
#!/bin/sh
#
# Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software

View file

@ -32,7 +32,6 @@ Created 4/18/1996 Heikki Tuuri
#include "dict0crea.h"
#include "btr0btr.h"
#include "dict0load.h"
#include "dict0load.h"
#include "trx0trx.h"
#include "srv0srv.h"
#include "ibuf0ibuf.h"

View file

@ -44,6 +44,7 @@ Created 1/8/1996 Heikki Tuuri
#include "ut0vec.h"
#include "dict0priv.h"
#include "fts0priv.h"
#include "ha_prototypes.h"
/*****************************************************************//**
Based on a table object, this function builds the entry to be inserted
@ -891,13 +892,17 @@ create:
for (index = UT_LIST_GET_FIRST(table->indexes);
index;
index = UT_LIST_GET_NEXT(indexes, index)) {
if (index->id == index_id && !(index->type & DICT_FTS)) {
if (index->id == index_id) {
if (index->type & DICT_FTS) {
return(FIL_NULL);
} else {
root_page_no = btr_create(type, space, zip_size,
index_id, index, mtr);
index->page = (unsigned int) root_page_no;
return(root_page_no);
}
}
}
ut_print_timestamp(stderr);
fprintf(stderr,
@ -1453,11 +1458,11 @@ static __attribute__((nonnull, warn_unused_result))
dberr_t
dict_foreign_eval_sql(
/*==================*/
pars_info_t* info, /*!< in: info struct, or NULL */
pars_info_t* info, /*!< in: info struct */
const char* sql, /*!< in: SQL string to evaluate */
dict_table_t* table, /*!< in: table */
dict_foreign_t* foreign,/*!< in: foreign */
trx_t* trx) /*!< in: transaction */
const char* name, /*!< in: table name (for diagnostics) */
const char* id, /*!< in: foreign key id */
trx_t* trx) /*!< in/out: transaction */
{
dberr_t error;
FILE* ef = dict_foreign_err_file;
@ -1470,9 +1475,9 @@ dict_foreign_eval_sql(
ut_print_timestamp(ef);
fputs(" Error in foreign key constraint creation for table ",
ef);
ut_print_name(ef, trx, TRUE, table->name);
ut_print_name(ef, trx, TRUE, name);
fputs(".\nA foreign key constraint of name ", ef);
ut_print_name(ef, trx, TRUE, foreign->id);
ut_print_name(ef, trx, TRUE, id);
fputs("\nalready exists."
" (Note that internally InnoDB adds 'databasename'\n"
"in front of the user-defined constraint name.)\n"
@ -1499,7 +1504,7 @@ dict_foreign_eval_sql(
ut_print_timestamp(ef);
fputs(" Internal error in foreign key constraint creation"
" for table ", ef);
ut_print_name(ef, trx, TRUE, table->name);
ut_print_name(ef, trx, TRUE, name);
fputs(".\n"
"See the MySQL .err log in the datadir"
" for more information.\n", ef);
@ -1519,10 +1524,10 @@ static __attribute__((nonnull, warn_unused_result))
dberr_t
dict_create_add_foreign_field_to_dictionary(
/*========================================*/
ulint field_nr, /*!< in: foreign field number */
dict_table_t* table, /*!< in: table */
dict_foreign_t* foreign, /*!< in: foreign */
trx_t* trx) /*!< in: transaction */
ulint field_nr, /*!< in: field number */
const char* table_name, /*!< in: table name */
const dict_foreign_t* foreign, /*!< in: foreign */
trx_t* trx) /*!< in/out: transaction */
{
pars_info_t* info = pars_info_create();
@ -1543,48 +1548,26 @@ dict_create_add_foreign_field_to_dictionary(
"INSERT INTO SYS_FOREIGN_COLS VALUES"
"(:id, :pos, :for_col_name, :ref_col_name);\n"
"END;\n",
table, foreign, trx));
table_name, foreign->id, trx));
}
/********************************************************************//**
Add a single foreign key definition to the data dictionary tables in the
database. We also generate names to constraints that were not named by the
user. A generated constraint has a name of the format
databasename/tablename_ibfk_NUMBER, where the numbers start from 1, and
are given locally for this table, that is, the number is not global, as in
the old format constraints < 4.0.18 it used to be.
Add a foreign key definition to the data dictionary tables.
@return error code or DB_SUCCESS */
UNIV_INTERN
dberr_t
dict_create_add_foreign_to_dictionary(
/*==================================*/
ulint* id_nr, /*!< in/out: number to use in id generation;
incremented if used */
dict_table_t* table, /*!< in: table */
dict_foreign_t* foreign,/*!< in: foreign */
const char* name, /*!< in: table name */
const dict_foreign_t* foreign,/*!< in: foreign key */
trx_t* trx) /*!< in/out: dictionary transaction */
{
dberr_t error;
ulint i;
pars_info_t* info = pars_info_create();
if (foreign->id == NULL) {
/* Generate a new constraint id */
char* id;
ulint namelen = strlen(table->name);
id = static_cast<char*>(mem_heap_alloc(
foreign->heap, namelen + 20));
/* no overflow if number < 1e13 */
sprintf(id, "%s_ibfk_%lu", table->name, (ulong) (*id_nr)++);
foreign->id = id;
}
pars_info_add_str_literal(info, "id", foreign->id);
pars_info_add_str_literal(info, "for_name", table->name);
pars_info_add_str_literal(info, "for_name", name);
pars_info_add_str_literal(info, "ref_name",
foreign->referenced_table_name);
@ -1598,16 +1581,16 @@ dict_create_add_foreign_to_dictionary(
"INSERT INTO SYS_FOREIGN VALUES"
"(:id, :for_name, :ref_name, :n_cols);\n"
"END;\n"
, table, foreign, trx);
, name, foreign->id, trx);
if (error != DB_SUCCESS) {
return(error);
}
for (i = 0; i < foreign->n_fields; i++) {
for (ulint i = 0; i < foreign->n_fields; i++) {
error = dict_create_add_foreign_field_to_dictionary(
i, table, foreign, trx);
i, name, foreign, trx);
if (error != DB_SUCCESS) {
@ -1654,7 +1637,15 @@ dict_create_add_foreigns_to_dictionary(
foreign;
foreign = UT_LIST_GET_NEXT(foreign_list, foreign)) {
error = dict_create_add_foreign_to_dictionary(&number, table,
error = dict_create_add_foreign_id(&number, table->name,
foreign);
if (error != DB_SUCCESS) {
return(error);
}
error = dict_create_add_foreign_to_dictionary(table->name,
foreign, trx);
if (error != DB_SUCCESS) {

View file

@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 1996, 2012, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 1996, 2013, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2012, Facebook Inc.
This program is free software; you can redistribute it and/or modify it under
@ -413,7 +413,8 @@ dict_table_try_drop_aborted(
trx_set_dict_operation(trx, TRX_DICT_OP_INDEX);
if (table == NULL) {
table = dict_table_open_on_id_low(table_id);
table = dict_table_open_on_id_low(
table_id, DICT_ERR_IGNORE_NONE);
} else {
ut_ad(table->id == table_id);
}
@ -786,9 +787,7 @@ dict_table_open_on_id(
/*==================*/
table_id_t table_id, /*!< in: table id */
ibool dict_locked, /*!< in: TRUE=data dictionary locked */
ibool try_drop) /*!< in: TRUE=try to drop any orphan
indexes after an aborted online
index creation */
dict_table_op_t table_op) /*!< in: operation to perform */
{
dict_table_t* table;
@ -798,7 +797,11 @@ dict_table_open_on_id(
ut_ad(mutex_own(&dict_sys->mutex));
table = dict_table_open_on_id_low(table_id);
table = dict_table_open_on_id_low(
table_id,
table_op == DICT_TABLE_OP_LOAD_TABLESPACE
? DICT_ERR_IGNORE_RECOVER_LOCK
: DICT_ERR_IGNORE_NONE);
if (table != NULL) {
@ -812,7 +815,8 @@ dict_table_open_on_id(
}
if (!dict_locked) {
dict_table_try_drop_aborted_and_mutex_exit(table, try_drop);
dict_table_try_drop_aborted_and_mutex_exit(
table, table_op == DICT_TABLE_OP_DROP_ORPHAN);
}
return(table);
@ -1459,12 +1463,13 @@ dict_table_rename_in_cache(
filepath = fil_make_ibd_name(table->name, false);
}
fil_delete_tablespace(table->space, BUF_REMOVE_FLUSH_NO_WRITE);
fil_delete_tablespace(table->space, BUF_REMOVE_ALL_NO_WRITE);
/* Delete any temp file hanging around. */
if (os_file_status(filepath, &exists, &type)
&& exists
&& !os_file_delete_if_exists(filepath)) {
&& !os_file_delete_if_exists(innodb_file_temp_key,
filepath)) {
ib_logf(IB_LOG_LEVEL_INFO,
"Delete of %s failed.", filepath);
@ -1606,22 +1611,78 @@ dict_table_rename_in_cache(
dict_mem_foreign_table_name_lookup_set(foreign, FALSE);
}
if (strchr(foreign->id, '/')) {
/* This is a >= 4.0.18 format id */
ulint db_len;
char* old_id;
char old_name_cs_filename[MAX_TABLE_NAME_LEN+20];
uint errors = 0;
/* This is a >= 4.0.18 format id */
/* All table names are internally stored in charset
my_charset_filename (except the temp tables and the
partition identifier suffix in partition tables). The
foreign key constraint names are internally stored
in UTF-8 charset. The variable fkid here is used
to store foreign key constraint name in charset
my_charset_filename for comparison further below. */
char fkid[MAX_TABLE_NAME_LEN+20];
ibool on_tmp = FALSE;
/* The old table name in my_charset_filename is stored
in old_name_cs_filename */
strncpy(old_name_cs_filename, old_name,
MAX_TABLE_NAME_LEN);
if (strstr(old_name, TEMP_TABLE_PATH_PREFIX) == NULL) {
innobase_convert_to_system_charset(
strchr(old_name_cs_filename, '/') + 1,
strchr(old_name, '/') + 1,
MAX_TABLE_NAME_LEN, &errors);
if (errors) {
/* There has been an error to convert
old table into UTF-8. This probably
means that the old table name is
actually in UTF-8. */
innobase_convert_to_filename_charset(
strchr(old_name_cs_filename,
'/') + 1,
strchr(old_name, '/') + 1,
MAX_TABLE_NAME_LEN);
} else {
/* Old name already in
my_charset_filename */
strncpy(old_name_cs_filename, old_name,
MAX_TABLE_NAME_LEN);
}
}
strncpy(fkid, foreign->id, MAX_TABLE_NAME_LEN);
if (strstr(fkid, TEMP_TABLE_PATH_PREFIX) == NULL) {
innobase_convert_to_filename_charset(
strchr(fkid, '/') + 1,
strchr(foreign->id, '/') + 1,
MAX_TABLE_NAME_LEN+20);
} else {
on_tmp = TRUE;
}
old_id = mem_strdup(foreign->id);
if (ut_strlen(foreign->id) > ut_strlen(old_name)
if (ut_strlen(fkid) > ut_strlen(old_name_cs_filename)
+ ((sizeof dict_ibfk) - 1)
&& !memcmp(foreign->id, old_name,
ut_strlen(old_name))
&& !memcmp(foreign->id + ut_strlen(old_name),
&& !memcmp(fkid, old_name_cs_filename,
ut_strlen(old_name_cs_filename))
&& !memcmp(fkid + ut_strlen(old_name_cs_filename),
dict_ibfk, (sizeof dict_ibfk) - 1)) {
/* This is a generated >= 4.0.18 format id */
char table_name[MAX_TABLE_NAME_LEN] = "";
uint errors = 0;
if (strlen(table->name) > strlen(old_name)) {
foreign->id = static_cast<char*>(
mem_heap_alloc(
@ -1630,11 +1691,36 @@ dict_table_rename_in_cache(
+ strlen(old_id) + 1));
}
/* Convert the table name to UTF-8 */
strncpy(table_name, table->name,
MAX_TABLE_NAME_LEN);
innobase_convert_to_system_charset(
strchr(table_name, '/') + 1,
strchr(table->name, '/') + 1,
MAX_TABLE_NAME_LEN, &errors);
if (errors) {
/* Table name could not be converted
from charset my_charset_filename to
UTF-8. This means that the table name
is already in UTF-8 (#mysql#50). */
strncpy(table_name, table->name,
MAX_TABLE_NAME_LEN);
}
/* Replace the prefix 'databasename/tablename'
with the new names */
strcpy(foreign->id, table->name);
strcpy(foreign->id, table_name);
if (on_tmp) {
strcat(foreign->id,
old_id + ut_strlen(old_name));
} else {
sprintf(strchr(foreign->id, '/') + 1,
"%s%s",
strchr(table_name, '/') +1,
strstr(old_id, "_ibfk_") );
}
} else {
/* This is a >= 4.0.18 format id where the user
gave the id name */
@ -2033,6 +2119,10 @@ dict_index_too_big_for_tree(
return(false);
}
DBUG_EXECUTE_IF(
"ib_force_create_table",
return(FALSE););
comp = dict_table_is_comp(table);
zip_size = dict_table_zip_size(table);
@ -2047,7 +2137,10 @@ dict_index_too_big_for_tree(
number in the page modification log. The maximum
allowed node pointer size is half that. */
page_rec_max = page_zip_empty_size(new_index->n_fields,
zip_size) - 1;
zip_size);
if (page_rec_max) {
page_rec_max--;
}
page_ptr_max = page_rec_max / 2;
/* On a compressed page, there is a two-byte entry in
the dense page directory for every record. But there
@ -3117,13 +3210,16 @@ dict_index_t*
dict_foreign_find_index(
/*====================*/
const dict_table_t* table, /*!< in: table */
const char** col_names,
/*!< in: column names, or NULL
to use table->col_names */
const char** columns,/*!< in: array of column names */
ulint n_cols, /*!< in: number of columns */
const dict_index_t* types_idx,
/*!< in: NULL or an index
whose types the column types
must match */
ibool check_charsets,
bool check_charsets,
/*!< in: whether to check
charsets. only has an effect
if types_idx != NULL */
@ -3139,20 +3235,16 @@ dict_foreign_find_index(
index = dict_table_get_first_index(table);
while (index != NULL) {
/* Ignore matches that refer to the same instance
(or the index is to be dropped) */
if (types_idx == index || index->type & DICT_FTS
|| index->to_be_dropped) {
goto next_rec;
} else if (dict_foreign_qualify_index(
table, columns, n_cols, index, types_idx,
if (types_idx != index
&& !(index->type & DICT_FTS)
&& !index->to_be_dropped
&& dict_foreign_qualify_index(
table, col_names, columns, n_cols,
index, types_idx,
check_charsets, check_null)) {
return(index);
}
next_rec:
index = dict_table_get_next_index(index);
}
@ -3211,9 +3303,16 @@ UNIV_INTERN
dberr_t
dict_foreign_add_to_cache(
/*======================*/
dict_foreign_t* foreign, /*!< in, own: foreign key constraint */
ibool check_charsets) /*!< in: TRUE=check charset
dict_foreign_t* foreign,
/*!< in, own: foreign key constraint */
const char** col_names,
/*!< in: column names, or NULL to use
foreign->foreign_table->col_names */
bool check_charsets,
/*!< in: whether to check charset
compatibility */
dict_err_ignore_t ignore_err)
/*!< in: error to be ignored */
{
dict_table_t* for_table;
dict_table_t* ref_table;
@ -3246,14 +3345,15 @@ dict_foreign_add_to_cache(
for_in_cache = foreign;
}
if (for_in_cache->referenced_table == NULL && ref_table) {
if (ref_table && !for_in_cache->referenced_table) {
index = dict_foreign_find_index(
ref_table,
ref_table, NULL,
for_in_cache->referenced_col_names,
for_in_cache->n_fields, for_in_cache->foreign_index,
check_charsets, FALSE);
check_charsets, false);
if (index == NULL) {
if (index == NULL
&& !(ignore_err & DICT_ERR_IGNORE_FK_NOKEY)) {
dict_foreign_error_report(
ef, for_in_cache,
"there is no index in referenced table"
@ -3278,9 +3378,9 @@ dict_foreign_add_to_cache(
added_to_referenced_list = TRUE;
}
if (for_in_cache->foreign_table == NULL && for_table) {
if (for_table && !for_in_cache->foreign_table) {
index = dict_foreign_find_index(
for_table,
for_table, col_names,
for_in_cache->foreign_col_names,
for_in_cache->n_fields,
for_in_cache->referenced_index, check_charsets,
@ -3288,7 +3388,8 @@ dict_foreign_add_to_cache(
& (DICT_FOREIGN_ON_DELETE_SET_NULL
| DICT_FOREIGN_ON_UPDATE_SET_NULL));
if (index == NULL) {
if (index == NULL
&& !(ignore_err & DICT_ERR_IGNORE_FK_NOKEY)) {
dict_foreign_error_report(
ef, for_in_cache,
"there is no index in the table"
@ -3350,14 +3451,27 @@ dict_scan_to(
const char* string) /*!< in: look for this */
{
char quote = '\0';
bool escape = false;
for (; *ptr; ptr++) {
if (*ptr == quote) {
/* Closing quote character: do not look for
starting quote or the keyword. */
/* If the quote character is escaped by a
backslash, ignore it. */
if (escape) {
escape = false;
} else {
quote = '\0';
}
} else if (quote) {
/* Within quotes: do nothing. */
if (escape) {
escape = false;
} else if (*ptr == '\\') {
escape = true;
}
} else if (*ptr == '`' || *ptr == '"' || *ptr == '\'') {
/* Starting quote: remember the quote character. */
quote = *ptr;
@ -3772,6 +3886,11 @@ dict_strip_comments(
char* ptr;
/* unclosed quote character (0 if none) */
char quote = 0;
bool escape = false;
DBUG_ENTER("dict_strip_comments");
DBUG_PRINT("dict_strip_comments", ("%s", sql_string));
str = static_cast<char*>(mem_alloc(sql_length + 1));
@ -3786,16 +3905,29 @@ end_of_string:
ut_a(ptr <= str + sql_length);
return(str);
DBUG_PRINT("dict_strip_comments", ("%s", str));
DBUG_RETURN(str);
}
if (*sptr == quote) {
/* Closing quote character: do not look for
starting quote or comments. */
/* If the quote character is escaped by a
backslash, ignore it. */
if (escape) {
escape = false;
} else {
quote = 0;
}
} else if (quote) {
/* Within quotes: do not look for
starting quotes or comments. */
if (escape) {
escape = false;
} else if (*sptr == '\\') {
escape = true;
}
} else if (*sptr == '"' || *sptr == '`' || *sptr == '\'') {
/* Starting quote: remember the quote character. */
quote = *sptr;
@ -4164,10 +4296,13 @@ col_loop1:
}
/* Try to find an index which contains the columns
as the first fields and in the right order */
as the first fields and in the right order. There is
no need to check column type match (on types_idx), since
the referenced table can be NULL if foreign_key_checks is
set to 0 */
index = dict_foreign_find_index(table, column_names, i,
NULL, TRUE, FALSE);
index = dict_foreign_find_index(
table, NULL, column_names, i, NULL, TRUE, FALSE);
if (!index) {
mutex_enter(&dict_foreign_err_mutex);
@ -4439,7 +4574,7 @@ try_find_index:
foreign->foreign_index */
if (referenced_table) {
index = dict_foreign_find_index(referenced_table,
index = dict_foreign_find_index(referenced_table, NULL,
column_names, i,
foreign->foreign_index,
TRUE, FALSE);
@ -5635,38 +5770,42 @@ dict_table_get_index_on_name(
/**********************************************************************//**
Replace the index passed in with another equivalent index in the
foreign key lists of the table. */
foreign key lists of the table.
@return whether all replacements were found */
UNIV_INTERN
void
bool
dict_foreign_replace_index(
/*=======================*/
dict_table_t* table, /*!< in/out: table */
const dict_index_t* index, /*!< in: index to be replaced */
const trx_t* trx) /*!< in: transaction handle */
const char** col_names,
/*!< in: column names, or NULL
to use table->col_names */
const dict_index_t* index) /*!< in: index to be replaced */
{
bool found = true;
dict_foreign_t* foreign;
ut_ad(index->to_be_dropped);
ut_ad(index->table == table);
for (foreign = UT_LIST_GET_FIRST(table->foreign_list);
foreign;
foreign = UT_LIST_GET_NEXT(foreign_list, foreign)) {
dict_index_t* new_index;
if (foreign->foreign_index == index) {
ut_ad(foreign->foreign_table == index->table);
new_index = dict_foreign_find_index(
foreign->foreign_table,
dict_index_t* new_index = dict_foreign_find_index(
foreign->foreign_table, col_names,
foreign->foreign_col_names,
foreign->n_fields, index,
/*check_charsets=*/TRUE, /*check_null=*/FALSE);
/* There must exist an alternative index,
since this must have been checked earlier. */
ut_a(new_index || !trx->check_foreigns);
ut_ad(!new_index || new_index->table == index->table);
ut_ad(!new_index || !new_index->to_be_dropped);
if (new_index) {
ut_ad(new_index->table == index->table);
ut_ad(!new_index->to_be_dropped);
} else {
found = false;
}
foreign->foreign_index = new_index;
}
@ -5676,25 +5815,28 @@ dict_foreign_replace_index(
foreign;
foreign = UT_LIST_GET_NEXT(referenced_list, foreign)) {
dict_index_t* new_index;
if (foreign->referenced_index == index) {
ut_ad(foreign->referenced_table == index->table);
new_index = dict_foreign_find_index(
foreign->referenced_table,
dict_index_t* new_index = dict_foreign_find_index(
foreign->referenced_table, NULL,
foreign->referenced_col_names,
foreign->n_fields, index,
/*check_charsets=*/TRUE, /*check_null=*/FALSE);
/* There must exist an alternative index,
since this must have been checked earlier. */
ut_a(new_index || !trx->check_foreigns);
ut_ad(!new_index || new_index->table == index->table);
ut_ad(!new_index || !new_index->to_be_dropped);
if (new_index) {
ut_ad(new_index->table == index->table);
ut_ad(!new_index->to_be_dropped);
} else {
found = false;
}
foreign->referenced_index = new_index;
}
}
return(found);
}
/**********************************************************************//**
@ -6118,7 +6260,7 @@ dict_close(void)
}
}
# ifdef UNIV_DEBUG
#ifdef UNIV_DEBUG
/**********************************************************************//**
Validate the dictionary table LRU list.
@return TRUE if valid */
@ -6203,7 +6345,7 @@ dict_non_lru_find_table(
return(FALSE);
}
# endif /* UNIV_DEBUG */
#endif /* UNIV_DEBUG */
/*********************************************************************//**
Check an index to see whether its first fields are the columns in the array,
in the same order and is not marked for deletion and is not the same
@ -6214,6 +6356,9 @@ bool
dict_foreign_qualify_index(
/*=======================*/
const dict_table_t* table, /*!< in: table */
const char** col_names,
/*!< in: column names, or NULL
to use table->col_names */
const char** columns,/*!< in: array of column names */
ulint n_cols, /*!< in: number of columns */
const dict_index_t* index, /*!< in: index to check */
@ -6221,7 +6366,7 @@ dict_foreign_qualify_index(
/*!< in: NULL or an index
whose types the column types
must match */
ibool check_charsets,
bool check_charsets,
/*!< in: whether to check
charsets. only has an effect
if types_idx != NULL */
@ -6230,50 +6375,46 @@ dict_foreign_qualify_index(
the columns must be declared
NOT NULL */
{
ulint i;
if (dict_index_get_n_fields(index) < n_cols) {
return(false);
}
for (i= 0; i < n_cols; i++) {
for (ulint i = 0; i < n_cols; i++) {
dict_field_t* field;
const char* col_name;
ulint col_no;
field = dict_index_get_nth_field(index, i);
col_name = dict_table_get_col_name(
table, dict_col_get_no(field->col));
col_no = dict_col_get_no(field->col);
if (field->prefix_len != 0) {
/* We do not accept column prefix
indexes here */
break;
}
if (0 != innobase_strcasecmp(columns[i],
col_name)) {
break;
return(false);
}
if (check_null
&& (field->col->prtype & DATA_NOT_NULL)) {
return(false);
}
break;
col_name = col_names
? col_names[col_no]
: dict_table_get_col_name(table, col_no);
if (0 != innobase_strcasecmp(columns[i], col_name)) {
return(false);
}
if (types_idx && !cmp_cols_are_equal(
dict_index_get_nth_col(index, i),
dict_index_get_nth_col(types_idx,
i),
dict_index_get_nth_col(types_idx, i),
check_charsets)) {
break;
return(false);
}
}
return((i == n_cols) ? true : false);
return(true);
}
/*********************************************************************//**

View file

@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 1996, 2012, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 1996, 2013, Oracle and/or its affiliates. All Rights Reserved.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -960,7 +960,7 @@ UNIV_INTERN
void
dict_check_tablespaces_and_store_max_id(
/*====================================*/
ibool in_crash_recovery) /*!< in: are we doing a crash recovery */
dict_check_t dict_check) /*!< in: how to check */
{
dict_table_t* sys_tables;
dict_index_t* sys_index;
@ -1039,7 +1039,7 @@ loop:
ib_logf(IB_LOG_LEVEL_ERROR,
"Table '%s' in InnoDB data dictionary"
" has unknown type %lx", table_name, flags);
mem_free(name);
goto loop;
}
@ -1085,15 +1085,36 @@ loop:
if (space_id == 0) {
/* The system tablespace always exists. */
ut_ad(!discarded);
} else if (in_crash_recovery) {
goto next_tablespace;
}
switch (dict_check) {
case DICT_CHECK_ALL_LOADED:
/* All tablespaces should have been found in
fil_load_single_table_tablespaces(). */
fil_space_for_table_exists_in_mem(
space_id, name, TRUE, !(is_temp || discarded),
false, NULL, 0);
break;
} else if (!discarded) {
case DICT_CHECK_SOME_LOADED:
/* Some tablespaces may have been opened in
trx_resurrect_table_locks(). */
if (fil_space_for_table_exists_in_mem(
space_id, name, FALSE, FALSE,
false, NULL, 0)) {
break;
}
/* fall through */
case DICT_CHECK_NONE_LOADED:
if (discarded) {
ib_logf(IB_LOG_LEVEL_INFO,
"DISCARD flag set for table '%s',"
" ignored.",
table_name);
break;
}
/* It is a normal database startup: create the
space object and check that the .ibd file exists.
@ -1127,18 +1148,16 @@ loop:
if (filepath) {
mem_free(filepath);
}
} else {
ib_logf(IB_LOG_LEVEL_INFO,
"DISCARD flag set for table '%s', ignored.",
table_name);
}
mem_free(name);
break;
}
if (space_id > max_space_id) {
max_space_id = space_id;
}
next_tablespace:
mem_free(name);
mtr_start(&mtr);
btr_pcur_restore_position(BTR_SEARCH_LEAF, &pcur, &mtr);
@ -1808,6 +1827,23 @@ dict_load_indexes(
rec = btr_pcur_get_rec(&pcur);
if ((ignore_err & DICT_ERR_IGNORE_RECOVER_LOCK)
&& rec_get_n_fields_old(rec)
== DICT_NUM_FIELDS__SYS_INDEXES) {
const byte* field;
ulint len;
field = rec_get_nth_field_old(
rec, DICT_FLD__SYS_INDEXES__NAME, &len);
if (len != UNIV_SQL_NULL
&& char(*field) == char(TEMP_INDEX_PREFIX)) {
/* Skip indexes whose name starts with
TEMP_INDEX_PREFIX, because they will
be dropped during crash recovery. */
goto next_rec;
}
}
err_msg = dict_load_index_low(buf, table->name, heap, rec,
TRUE, &index);
ut_ad((index == NULL && err_msg != NULL)
@ -2317,11 +2353,14 @@ err_exit:
table->ibd_file_missing = TRUE;
} else {
if (!(ignore_err & DICT_ERR_IGNORE_RECOVER_LOCK)) {
ib_logf(IB_LOG_LEVEL_ERROR,
"Failed to find tablespace for table '%s' "
"in the cache. Attempting to load the "
"tablespace with space id %lu.",
"Failed to find tablespace for "
"table '%s' in the cache. "
"Attempting to load the tablespace "
"with space id %lu.",
table_name, (ulong) table->space);
}
/* Use the remote filepath if needed. */
if (DICT_TF_HAS_DATA_DIR(table->flags)) {
@ -2368,13 +2407,15 @@ err_exit:
/* If there is no tablespace for the table then we only need to
load the index definitions. So that we can IMPORT the tablespace
later. */
if (table->ibd_file_missing) {
err = dict_load_indexes(
table, heap, DICT_ERR_IGNORE_ALL);
} else {
err = dict_load_indexes(table, heap, ignore_err);
}
later. When recovering table locks for resurrected incomplete
transactions, the tablespace should exist, because DDL operations
were not allowed while the table is being locked by a transaction. */
dict_err_ignore_t index_load_err =
!(ignore_err & DICT_ERR_IGNORE_RECOVER_LOCK)
&& table->ibd_file_missing
? DICT_ERR_IGNORE_ALL
: ignore_err;
err = dict_load_indexes(table, heap, index_load_err);
if (err == DB_INDEX_CORRUPT) {
/* Refuse to load the table if the table has a corrupted
@ -2411,9 +2452,16 @@ err_exit:
if (!cached || table->ibd_file_missing) {
/* Don't attempt to load the indexes from disk. */
} else if (err == DB_SUCCESS) {
err = dict_load_foreigns(table->name, TRUE, TRUE);
err = dict_load_foreigns(table->name, NULL, true, true,
ignore_err);
if (err != DB_SUCCESS) {
ib_logf(IB_LOG_LEVEL_WARN,
"Load table '%s' failed, the table has missing "
"foreign key indexes. Turn off "
"'foreign_key_checks' and try again.",
table->name);
dict_table_remove_from_cache(table);
table = NULL;
} else {
@ -2474,7 +2522,9 @@ UNIV_INTERN
dict_table_t*
dict_load_table_on_id(
/*==================*/
table_id_t table_id) /*!< in: table id */
table_id_t table_id, /*!< in: table id */
dict_err_ignore_t ignore_err) /*!< in: errors to ignore
when loading the table */
{
byte id_buf[8];
btr_pcur_t pcur;
@ -2551,7 +2601,7 @@ check_rec:
table = dict_load_table(
mem_heap_strdupl(
heap, (char*) field, len),
TRUE, DICT_ERR_IGNORE_NONE);
TRUE, ignore_err);
}
}
}
@ -2710,18 +2760,25 @@ dict_load_foreign_cols(
/***********************************************************************//**
Loads a foreign key constraint to the dictionary cache.
@return DB_SUCCESS or error code */
static __attribute__((nonnull, warn_unused_result))
static __attribute__((nonnull(1), warn_unused_result))
dberr_t
dict_load_foreign(
/*==============*/
const char* id, /*!< in: foreign constraint id, must be
const char* id,
/*!< in: foreign constraint id, must be
'\0'-terminated */
ibool check_charsets,
/*!< in: TRUE=check charset compatibility */
ibool check_recursive)
/*!< in: Whether to record the foreign table
const char** col_names,
/*!< in: column names, or NULL
to use foreign->foreign_table->col_names */
bool check_recursive,
/*!< in: whether to record the foreign table
parent count to avoid unlimited recursive
load of chained foreign tables */
bool check_charsets,
/*!< in: whether to check charset
compatibility */
dict_err_ignore_t ignore_err)
/*!< in: error to be ignored */
{
dict_foreign_t* foreign;
dict_table_t* sys_foreign;
@ -2890,7 +2947,8 @@ dict_load_foreign(
a new foreign key constraint but loading one from the data
dictionary. */
return(dict_foreign_add_to_cache(foreign, check_charsets));
return(dict_foreign_add_to_cache(foreign, col_names, check_charsets,
ignore_err));
}
/***********************************************************************//**
@ -2903,12 +2961,16 @@ already in the dictionary cache.
UNIV_INTERN
dberr_t
dict_load_foreigns(
/*===============*/
const char* table_name, /*!< in: table name */
ibool check_recursive,/*!< in: Whether to check recursive
load of tables chained by FK */
ibool check_charsets) /*!< in: TRUE=check charset
compatibility */
const char** col_names, /*!< in: column names, or NULL
to use table->col_names */
bool check_recursive,/*!< in: Whether to check
recursive load of tables
chained by FK */
bool check_charsets, /*!< in: whether to check
charset compatibility */
dict_err_ignore_t ignore_err) /*!< in: error to be ignored */
/*===============*/
{
ulint tuple_buf[(DTUPLE_EST_ALLOC(1) + sizeof(ulint) - 1)
/ sizeof(ulint)];
@ -2992,13 +3054,12 @@ loop:
may not be the same case, but the previous comparison showed that they
match with no-case. */
if ((innobase_get_lower_case_table_names() != 2)
&& (0 != ut_memcmp(field, table_name, len))) {
if (rec_get_deleted_flag(rec, 0)) {
goto next_rec;
}
if (rec_get_deleted_flag(rec, 0)) {
if ((innobase_get_lower_case_table_names() != 2)
&& (0 != ut_memcmp(field, table_name, len))) {
goto next_rec;
}
@ -3020,7 +3081,8 @@ loop:
/* Load the foreign constraint definition to the dictionary cache */
err = dict_load_foreign(fk_id, check_charsets, check_recursive);
err = dict_load_foreign(fk_id, col_names,
check_recursive, check_charsets, ignore_err);
if (err != DB_SUCCESS) {
btr_pcur_close(&pcur);

View file

@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 1996, 2012, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 1996, 2013, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2012, Facebook Inc.
This program is free software; you can redistribute it and/or modify it under
@ -53,10 +53,6 @@ Created 1/8/1996 Heikki Tuuri
UNIV_INTERN mysql_pfs_key_t autoinc_mutex_key;
#endif /* UNIV_PFS_MUTEX */
/** Prefix for tmp tables, adopted from sql/table.h */
#define tmp_file_prefix "#sql"
#define tmp_file_prefix_length 4
/**********************************************************************//**
Creates a table memory object.
@return own: table object */

View file

@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 2009, 2012, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2009, 2013, Oracle and/or its affiliates. All Rights Reserved.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -34,7 +34,6 @@ Created Jan 06, 2010 Vasil Dimov
#include "dict0stats.h"
#include "data0type.h" /* dtype_t */
#include "db0err.h" /* dberr_t */
#include "dyn0dyn.h" /* dyn_array* */
#include "page0page.h" /* page_align() */
#include "pars0pars.h" /* pars_info_create() */
#include "pars0types.h" /* pars_info_t */
@ -47,6 +46,8 @@ Created Jan 06, 2010 Vasil Dimov
#include "ut0rnd.h" /* ut_rnd_interval() */
#include "ut0ut.h" /* ut_format_name(), ut_time() */
#include <vector>
/* Sampling algorithm description @{
The algorithm is controlled by one number - N_SAMPLE_PAGES(index),
@ -135,12 +136,18 @@ descending to lower levels and fetch N_SAMPLE_PAGES(index) records
from that level */
#define N_DIFF_REQUIRED(index) (N_SAMPLE_PAGES(index) * 10)
/* A dynamic array where we store the boundaries of each distinct group
of keys. For example if a btree level is:
index: 0,1,2,3,4,5,6,7,8,9,10,11,12
data: b,b,b,b,b,b,g,g,j,j,j, x, y
then we would store 5,7,10,11,12 in the array. */
typedef std::vector<ib_uint64_t> boundaries_t;
/*********************************************************************//**
Checks whether an index should be ignored in stats manipulations:
* stats fetch
* stats recalc
* stats save
dict_stats_should_ignore_index() @{
@return true if exists and all tables are ok */
UNIV_INLINE
bool
@ -153,12 +160,10 @@ dict_stats_should_ignore_index(
|| index->to_be_dropped
|| *index->name == TEMP_INDEX_PREFIX);
}
/* @} */
/*********************************************************************//**
Checks whether the persistent statistics storage exists and that all
tables have the proper structure.
dict_stats_persistent_storage_check() @{
@return true if exists and all tables are ok */
static
bool
@ -260,7 +265,6 @@ dict_stats_persistent_storage_check(
return(true);
}
/* @} */
/*********************************************************************//**
Executes a given SQL statement using the InnoDB internal SQL parser
@ -463,8 +467,7 @@ dict_stats_table_clone_create(
/*********************************************************************//**
Free the resources occupied by an object returned by
dict_stats_table_clone_create().
dict_stats_table_clone_free() @{ */
dict_stats_table_clone_create(). */
static
void
dict_stats_table_clone_free(
@ -473,14 +476,12 @@ dict_stats_table_clone_free(
{
mem_heap_free(t->heap);
}
/* @} */
/*********************************************************************//**
Write all zeros (or 1 where it makes sense) into an index
statistics members. The resulting stats correspond to an empty index.
The caller must own index's table stats latch in X mode
(dict_table_stats_lock(table, RW_X_LATCH))
dict_stats_empty_index() @{ */
(dict_table_stats_lock(table, RW_X_LATCH)) */
static
void
dict_stats_empty_index(
@ -501,12 +502,10 @@ dict_stats_empty_index(
index->stat_index_size = 1;
index->stat_n_leaf_pages = 1;
}
/* @} */
/*********************************************************************//**
Write all zeros (or 1 where it makes sense) into a table and its indexes'
statistics members. The resulting stats correspond to an empty table.
dict_stats_empty_table() @{ */
statistics members. The resulting stats correspond to an empty table. */
static
void
dict_stats_empty_table(
@ -543,7 +542,6 @@ dict_stats_empty_table(
dict_table_stats_unlock(table, RW_X_LATCH);
}
/* @} */
/*********************************************************************//**
Check whether index's stats are initialized (assert if they are not). */
@ -573,6 +571,7 @@ dict_stats_assert_initialized_index(
&index->stat_n_leaf_pages,
sizeof(index->stat_n_leaf_pages));
}
/*********************************************************************//**
Check whether table's stats are initialized (assert if they are not). */
static
@ -760,8 +759,7 @@ dict_stats_snapshot_create(
/*********************************************************************//**
Free the resources occupied by an object returned by
dict_stats_snapshot_create().
dict_stats_snapshot_free() @{ */
dict_stats_snapshot_create(). */
static
void
dict_stats_snapshot_free(
@ -770,14 +768,12 @@ dict_stats_snapshot_free(
{
dict_stats_table_clone_free(t);
}
/* @} */
/*********************************************************************//**
Calculates new estimates for index statistics. This function is
relatively quick and is used to calculate transient statistics that
are not saved on disk. This was the only way to calculate statistics
before the Persistent Statistics feature was introduced.
dict_stats_update_transient_for_index() @{ */
before the Persistent Statistics feature was introduced. */
static
void
dict_stats_update_transient_for_index(
@ -826,15 +822,13 @@ dict_stats_update_transient_for_index(
dict_stats_empty_index(index);
}
}
/* @} */
/*********************************************************************//**
Calculates new estimates for table and index statistics. This function
is relatively quick and is used to calculate transient statistics that
are not saved on disk.
This was the only way to calculate statistics before the
Persistent Statistics feature was introduced.
dict_stats_update_transient() @{ */
Persistent Statistics feature was introduced. */
UNIV_INTERN
void
dict_stats_update_transient(
@ -900,7 +894,6 @@ dict_stats_update_transient(
table->stat_initialized = TRUE;
}
/* @} */
/* @{ Pseudo code about the relation between the following functions
@ -938,7 +931,7 @@ dict_stats_analyze_index_level(
distinct keys for all prefixes */
ib_uint64_t* total_recs, /*!< out: total number of records */
ib_uint64_t* total_pages, /*!< out: total number of pages */
dyn_array_t* n_diff_boundaries,/*!< out: boundaries of the groups
boundaries_t* n_diff_boundaries,/*!< out: boundaries of the groups
of distinct keys */
mtr_t* mtr) /*!< in/out: mini-transaction */
{
@ -983,9 +976,9 @@ dict_stats_analyze_index_level(
/* reset the dynamic arrays n_diff_boundaries[0..n_uniq-1] */
if (n_diff_boundaries != NULL) {
for (i = 0; i < n_uniq; i++) {
dyn_array_free(&n_diff_boundaries[i]);
dyn_array_create(&n_diff_boundaries[i]);
n_diff_boundaries[i].erase(
n_diff_boundaries[i].begin(),
n_diff_boundaries[i].end());
}
}
@ -1113,7 +1106,6 @@ dict_stats_analyze_index_level(
record, that is - the last one from
a group of equal keys */
void* p;
ib_uint64_t idx;
/* the index of the current record
@ -1126,11 +1118,7 @@ dict_stats_analyze_index_level(
total_recs >= 2 */
idx = *total_recs - 2;
p = dyn_array_push(
&n_diff_boundaries[i],
sizeof(ib_uint64_t));
memcpy(p, &idx, sizeof(ib_uint64_t));
n_diff_boundaries[i].push_back(idx);
}
/* increment the number of different keys
@ -1191,15 +1179,11 @@ dict_stats_analyze_index_level(
last one from the last group of equal keys; this holds for
all possible prefixes */
for (i = 0; i < n_uniq; i++) {
void* p;
ib_uint64_t idx;
idx = *total_recs - 1;
p = dyn_array_push(&n_diff_boundaries[i],
sizeof(ib_uint64_t));
memcpy(p, &idx, sizeof(ib_uint64_t));
n_diff_boundaries[i].push_back(idx);
}
}
@ -1226,9 +1210,7 @@ dict_stats_analyze_index_level(
for (j = 0; j < n_diff[i]; j++) {
ib_uint64_t idx;
idx = *(ib_uint64_t*) dyn_array_get_element(
&n_diff_boundaries[i],
j * sizeof(ib_uint64_t));
idx = n_diff_boundaries[i][j];
DEBUG_PRINTF(UINT64PF "=" UINT64PF ", ",
j, idx);
@ -1273,8 +1255,7 @@ to the right, which means that in the case of QUIT_ON_FIRST_NON_BORING the
returned n_diff can either be 0 (empty page), 1 (the whole page has all keys
equal) or 2 (the function found a non-boring record and returned).
@return offsets1 or offsets2 (the offsets of *out_rec),
or NULL if the page is empty and does not contain user records.
dict_stats_scan_page() @{ */
or NULL if the page is empty and does not contain user records. */
UNIV_INLINE __attribute__((nonnull))
ulint*
dict_stats_scan_page(
@ -1382,13 +1363,11 @@ func_exit:
*out_rec = rec;
return(offsets_rec);
}
/* @} */
/*********************************************************************//**
Dive below the current position of a cursor and calculate the number of
distinct records on the leaf page, when looking at the fist n_prefix
columns.
dict_stats_analyze_index_below_cur() @{
@return number of distinct records on the leaf page */
static
ib_uint64_t
@ -1511,7 +1490,6 @@ dict_stats_analyze_index_below_cur(
return(n_diff);
}
/* @} */
/*********************************************************************//**
For a given level in an index select N_SAMPLE_PAGES(index)
@ -1536,7 +1514,7 @@ dict_stats_analyze_index_for_n_prefix(
records on the given level,
when looking at the first
n_prefix columns */
dyn_array_t* boundaries, /*!< in: array that contains
boundaries_t* boundaries, /*!< in: array that contains
n_diff_for_this_prefix
integers each of which
represents the index (on the
@ -1602,8 +1580,7 @@ dict_stats_analyze_index_for_n_prefix(
== !(REC_INFO_MIN_REC_FLAG & rec_get_info_bits(
btr_pcur_get_rec(&pcur), page_is_comp(page))));
last_idx_on_level = *(ib_uint64_t*) dyn_array_get_element(boundaries,
(ulint) ((n_diff_for_this_prefix - 1) * sizeof(ib_uint64_t)));
last_idx_on_level = boundaries->at(n_diff_for_this_prefix - 1);
rec_idx = 0;
@ -1619,7 +1596,7 @@ dict_stats_analyze_index_for_n_prefix(
ib_uint64_t dive_below_idx;
/* there are n_diff_for_this_prefix elements
in the array boundaries[] and we divide those elements
in 'boundaries' and we divide those elements
into n_recs_to_dive_below segments, for example:
let n_diff_for_this_prefix=100, n_recs_to_dive_below=4, then:
@ -1658,9 +1635,7 @@ dict_stats_analyze_index_for_n_prefix(
ib_uint64_t could be bigger than ulint */
rnd = ut_rnd_interval(0, (ulint) (right - left));
dive_below_idx = *(ib_uint64_t*) dyn_array_get_element(
boundaries, (ulint) ((left + rnd)
* sizeof(ib_uint64_t)));
dive_below_idx = boundaries->at(left + rnd);
#if 0
DEBUG_PRINTF(" %s(): dive below record with index="
@ -1769,9 +1744,13 @@ dict_stats_analyze_index(
ib_uint64_t* n_diff_on_level;
ib_uint64_t total_recs;
ib_uint64_t total_pages;
dyn_array_t* n_diff_boundaries;
boundaries_t* n_diff_boundaries;
mtr_t mtr;
ulint size;
DBUG_ENTER("dict_stats_analyze_index");
DBUG_PRINT("info", ("index: %s, online status: %d", index->name,
dict_index_get_online_status(index)));
DEBUG_PRINTF(" %s(index=%s)\n", __func__, index->name);
@ -1794,7 +1773,7 @@ dict_stats_analyze_index(
switch (size) {
case ULINT_UNDEFINED:
dict_stats_assert_initialized_index(index);
return;
DBUG_VOID_RETURN;
case 0:
/* The root node of the tree is a leaf */
size = 1;
@ -1848,20 +1827,14 @@ dict_stats_analyze_index(
mtr_commit(&mtr);
dict_stats_assert_initialized_index(index);
return;
DBUG_VOID_RETURN;
}
/* set to zero */
n_diff_on_level = reinterpret_cast<ib_uint64_t*>
(mem_zalloc(n_uniq * sizeof(ib_uint64_t)));
n_diff_boundaries = reinterpret_cast<dyn_array_t*>
(mem_alloc(n_uniq * sizeof(dyn_array_t)));
for (ulint i = 0; i < n_uniq; i++) {
/* initialize the dynamic arrays */
dyn_array_create(&n_diff_boundaries[i]);
}
n_diff_boundaries = new boundaries_t[n_uniq];
/* total_recs is also used to estimate the number of pages on one
level below, so at the start we have 1 page (the root) */
@ -2011,15 +1984,12 @@ found_level:
mtr_commit(&mtr);
for (ulint i = 0; i < n_uniq; i++) {
dyn_array_free(&n_diff_boundaries[i]);
}
mem_free(n_diff_boundaries);
delete[] n_diff_boundaries;
mem_free(n_diff_on_level);
dict_stats_assert_initialized_index(index);
DBUG_VOID_RETURN;
}
/*********************************************************************//**
@ -2109,7 +2079,6 @@ dict_stats_update_persistent(
/*********************************************************************//**
Save an individual index's statistic into the persistent statistics
storage.
dict_stats_save_index_stat() @{
@return DB_SUCCESS or error code */
static
dberr_t
@ -2232,11 +2201,9 @@ dict_stats_save_index_stat(
return(ret);
}
/* @} */
/*********************************************************************//**
Save the table's statistics into the persistent statistics storage.
dict_stats_save() @{
@return DB_SUCCESS or error code */
static
dberr_t
@ -2401,14 +2368,12 @@ end:
return(ret);
}
/* @} */
/*********************************************************************//**
Called for the row that is selected by
SELECT ... FROM mysql.innodb_table_stats WHERE table='...'
The second argument is a pointer to the table and the fetched stats are
written to it.
dict_stats_fetch_table_stats_step() @{
@return non-NULL dummy */
static
ibool
@ -2485,7 +2450,6 @@ dict_stats_fetch_table_stats_step(
/* XXX this is not used but returning non-NULL is necessary */
return(TRUE);
}
/* @} */
/** Aux struct used to pass a table and a boolean to
dict_stats_fetch_index_stats_step(). */
@ -2511,7 +2475,6 @@ This can be improved if we sort table->indexes in a temporary area just once
and then search in that sorted list. Then the complexity will be O(N*log(N)).
We assume a table will not have more than 100 indexes, so we go with the
simpler N^2 algorithm.
dict_stats_fetch_index_stats_step() @{
@return non-NULL dummy */
static
ibool
@ -2752,11 +2715,9 @@ dict_stats_fetch_index_stats_step(
/* XXX this is not used but returning non-NULL is necessary */
return(TRUE);
}
/* @} */
/*********************************************************************//**
Read table's statistics from the persistent statistics storage.
dict_stats_fetch_from_ps() @{
@return DB_SUCCESS or error code */
static
dberr_t
@ -2877,17 +2838,17 @@ dict_stats_fetch_from_ps(
return(ret);
}
/* @} */
/*********************************************************************//**
Fetches or calculates new estimates for index statistics.
dict_stats_update_for_index() @{ */
Fetches or calculates new estimates for index statistics. */
UNIV_INTERN
void
dict_stats_update_for_index(
/*========================*/
dict_index_t* index) /*!< in/out: index */
{
DBUG_ENTER("dict_stats_update_for_index");
ut_ad(!mutex_own(&dict_sys->mutex));
if (dict_stats_is_persistent_enabled(index->table)) {
@ -2897,7 +2858,7 @@ dict_stats_update_for_index(
dict_stats_analyze_index(index);
dict_table_stats_unlock(index->table, RW_X_LATCH);
dict_stats_save(index->table);
return;
DBUG_VOID_RETURN;
}
/* else */
@ -2920,8 +2881,9 @@ dict_stats_update_for_index(
dict_table_stats_lock(index->table, RW_X_LATCH);
dict_stats_update_transient_for_index(index);
dict_table_stats_unlock(index->table, RW_X_LATCH);
DBUG_VOID_RETURN;
}
/* @} */
/*********************************************************************//**
Calculates new estimates for table and index statistics. The statistics
@ -2962,7 +2924,9 @@ dict_stats_update(
switch (stats_upd_option) {
case DICT_STATS_RECALC_PERSISTENT:
ut_ad(!srv_read_only_mode);
if (srv_read_only_mode) {
goto transient;
}
/* Persistent recalculation requested, called from
1) ANALYZE TABLE, or
@ -3063,8 +3027,6 @@ dict_stats_update(
dict_table_t* t;
ut_ad(!srv_read_only_mode);
/* Create a dummy table object with the same name and
indexes, suitable for fetching the stats into it. */
t = dict_stats_table_clone_create(table);
@ -3098,6 +3060,10 @@ dict_stats_update(
dict_stats_table_clone_free(t);
if (srv_read_only_mode) {
goto transient;
}
if (dict_stats_auto_recalc_is_enabled(table)) {
return(dict_stats_update(
table,
@ -3162,7 +3128,6 @@ rolling back dict transactions.
marko: If ibuf merges are not disabled, we need to scan the *.ibd files.
But we shouldn't open *.ibd files before we have rolled back dict
transactions and opened the SYS_* records for the *.ibd files.
dict_stats_drop_index() @{
@return DB_SUCCESS or error code */
UNIV_INTERN
dberr_t
@ -3244,14 +3209,12 @@ dict_stats_drop_index(
return(ret);
}
/* @} */
/*********************************************************************//**
Executes
DELETE FROM mysql.innodb_table_stats
WHERE database_name = '...' AND table_name = '...';
Creates its own transaction and commits it.
dict_stats_delete_from_table_stats() @{
@return DB_SUCCESS or error code */
UNIV_INLINE
dberr_t
@ -3265,7 +3228,7 @@ dict_stats_delete_from_table_stats(
#ifdef UNIV_SYNC_DEBUG
ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_EX));
#endif /* UNIV_STAT */
#endif /* UNIV_SYNC_DEBUG */
ut_ad(mutex_own(&dict_sys->mutex));
pinfo = pars_info_create();
@ -3284,14 +3247,12 @@ dict_stats_delete_from_table_stats(
return(ret);
}
/* @} */
/*********************************************************************//**
Executes
DELETE FROM mysql.innodb_index_stats
WHERE database_name = '...' AND table_name = '...';
Creates its own transaction and commits it.
dict_stats_delete_from_index_stats() @{
@return DB_SUCCESS or error code */
UNIV_INLINE
dberr_t
@ -3305,7 +3266,7 @@ dict_stats_delete_from_index_stats(
#ifdef UNIV_SYNC_DEBUG
ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_EX));
#endif /* UNIV_STAT */
#endif /* UNIV_SYNC_DEBUG */
ut_ad(mutex_own(&dict_sys->mutex));
pinfo = pars_info_create();
@ -3324,13 +3285,11 @@ dict_stats_delete_from_index_stats(
return(ret);
}
/* @} */
/*********************************************************************//**
Removes the statistics for a table and all of its indexes from the
persistent statistics storage if it exists and if there is data stored for
the table. This function creates its own transaction and commits it.
dict_stats_drop_table() @{
@return DB_SUCCESS or error code */
UNIV_INTERN
dberr_t
@ -3347,7 +3306,7 @@ dict_stats_drop_table(
#ifdef UNIV_SYNC_DEBUG
ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_EX));
#endif /* UNIV_STAT */
#endif /* UNIV_SYNC_DEBUG */
ut_ad(mutex_own(&dict_sys->mutex));
/* skip tables that do not contain a database name
@ -3403,7 +3362,6 @@ dict_stats_drop_table(
return(ret);
}
/* @} */
/*********************************************************************//**
Executes
@ -3411,7 +3369,6 @@ UPDATE mysql.innodb_table_stats SET
database_name = '...', table_name = '...'
WHERE database_name = '...' AND table_name = '...';
Creates its own transaction and commits it.
dict_stats_rename_in_table_stats() @{
@return DB_SUCCESS or error code */
UNIV_INLINE
dberr_t
@ -3427,7 +3384,7 @@ dict_stats_rename_in_table_stats(
#ifdef UNIV_SYNC_DEBUG
ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_EX));
#endif /* UNIV_STAT */
#endif /* UNIV_SYNC_DEBUG */
ut_ad(mutex_own(&dict_sys->mutex));
pinfo = pars_info_create();
@ -3451,7 +3408,6 @@ dict_stats_rename_in_table_stats(
return(ret);
}
/* @} */
/*********************************************************************//**
Executes
@ -3459,7 +3415,6 @@ UPDATE mysql.innodb_index_stats SET
database_name = '...', table_name = '...'
WHERE database_name = '...' AND table_name = '...';
Creates its own transaction and commits it.
dict_stats_rename_in_index_stats() @{
@return DB_SUCCESS or error code */
UNIV_INLINE
dberr_t
@ -3475,7 +3430,7 @@ dict_stats_rename_in_index_stats(
#ifdef UNIV_SYNC_DEBUG
ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_EX));
#endif /* UNIV_STAT */
#endif /* UNIV_SYNC_DEBUG */
ut_ad(mutex_own(&dict_sys->mutex));
pinfo = pars_info_create();
@ -3499,12 +3454,10 @@ dict_stats_rename_in_index_stats(
return(ret);
}
/* @} */
/*********************************************************************//**
Renames a table in InnoDB persistent stats storage.
This function creates its own transaction and commits it.
dict_stats_rename_table() @{
@return DB_SUCCESS or error code */
UNIV_INTERN
dberr_t
@ -3524,7 +3477,7 @@ dict_stats_rename_table(
#ifdef UNIV_SYNC_DEBUG
ut_ad(!rw_lock_own(&dict_operation_lock, RW_LOCK_EX));
#endif /* UNIV_STAT */
#endif /* UNIV_SYNC_DEBUG */
ut_ad(!mutex_own(&dict_sys->mutex));
/* skip innodb_table_stats and innodb_index_stats themselves */
@ -3658,7 +3611,6 @@ dict_stats_rename_table(
return(ret);
}
/* @} */
/* tests @{ */
#ifdef UNIV_COMPILE_TEST_FUNCS
@ -4050,5 +4002,3 @@ test_dict_stats_all()
/* @} */
#endif /* UNIV_HOTBACKUP */
/* vim: set foldmethod=marker foldmarker=@{,@}: */

View file

@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 2012, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2012, 2013, Oracle and/or its affiliates. All Rights Reserved.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -28,6 +28,10 @@ Created Apr 25, 2012 Vasil Dimov
#include "dict0stats.h"
#include "dict0stats_bg.h"
#ifdef UNIV_NONINL
# include "dict0stats_bg.ic"
#endif
#include <vector>
/** Minimum time interval between stats recalc for a given table */
@ -95,8 +99,7 @@ Add a table to the recalc pool, which is processed by the
background stats gathering thread. Only the table id is added to the
list, so the table can be closed after being enqueued and it will be
opened when needed. If the table does not exist later (has been DROPped),
then it will be removed from the pool and skipped.
dict_stats_recalc_pool_add() @{ */
then it will be removed from the pool and skipped. */
UNIV_INTERN
void
dict_stats_recalc_pool_add(
@ -124,12 +127,10 @@ dict_stats_recalc_pool_add(
os_event_set(dict_stats_event);
}
/* @} */
/*****************************************************************//**
Get a table from the auto recalc pool. The returned table id is removed
from the pool.
dict_stats_recalc_pool_get() @{
@return true if the pool was non-empty and "id" was set, false otherwise */
static
bool
@ -155,7 +156,6 @@ dict_stats_recalc_pool_get(
return(true);
}
/* @} */
/*****************************************************************//**
Delete a given table from the auto recalc pool.
@ -188,46 +188,30 @@ dict_stats_recalc_pool_del(
}
/*****************************************************************//**
Wait until background stats thread has stopped using the specified table(s).
Wait until background stats thread has stopped using the specified table.
The caller must have locked the data dictionary using
row_mysql_lock_data_dictionary() and this function may unlock it temporarily
and restore the lock before it exits.
The background stats thead is guaranteed not to start using the specified
tables after this function returns and before the caller unlocks the data
The background stats thread is guaranteed not to start using the specified
table after this function returns and before the caller unlocks the data
dictionary because it sets the BG_STAT_IN_PROGRESS bit in table->stats_bg_flag
under dict_sys->mutex.
dict_stats_wait_bg_to_stop_using_table() @{ */
under dict_sys->mutex. */
UNIV_INTERN
void
dict_stats_wait_bg_to_stop_using_tables(
/*====================================*/
dict_table_t* table1, /*!< in/out: table1 */
dict_table_t* table2, /*!< in/out: table2, could be NULL */
dict_stats_wait_bg_to_stop_using_table(
/*===================================*/
dict_table_t* table, /*!< in/out: table */
trx_t* trx) /*!< in/out: transaction to use for
unlocking/locking the data dict */
{
ut_ad(!srv_read_only_mode);
while ((table1->stats_bg_flag & BG_STAT_IN_PROGRESS)
|| (table2 != NULL
&& (table2->stats_bg_flag & BG_STAT_IN_PROGRESS))) {
table1->stats_bg_flag |= BG_STAT_SHOULD_QUIT;
if (table2 != NULL) {
table2->stats_bg_flag |= BG_STAT_SHOULD_QUIT;
}
row_mysql_unlock_data_dictionary(trx);
os_thread_sleep(250000);
row_mysql_lock_data_dictionary(trx);
while (!dict_stats_stop_bg(table)) {
DICT_STATS_BG_YIELD(trx);
}
}
/* @} */
/*****************************************************************//**
Initialize global variables needed for the operation of dict_stats_thread()
Must be called before dict_stats_thread() is started.
dict_stats_thread_init() @{ */
Must be called before dict_stats_thread() is started. */
UNIV_INTERN
void
dict_stats_thread_init()
@ -255,12 +239,10 @@ dict_stats_thread_init()
dict_stats_recalc_pool_init();
}
/* @} */
/*****************************************************************//**
Free resources allocated by dict_stats_thread_init(), must be called
after dict_stats_thread() has exited.
dict_stats_thread_deinit() @{ */
after dict_stats_thread() has exited. */
UNIV_INTERN
void
dict_stats_thread_deinit()
@ -277,12 +259,10 @@ dict_stats_thread_deinit()
os_event_free(dict_stats_event);
dict_stats_event = NULL;
}
/* @} */
/*****************************************************************//**
Get the first table that has been added for auto recalc and eventually
update its stats.
dict_stats_process_entry_from_recalc_pool() @{ */
update its stats. */
static
void
dict_stats_process_entry_from_recalc_pool()
@ -302,7 +282,7 @@ dict_stats_process_entry_from_recalc_pool()
mutex_enter(&dict_sys->mutex);
table = dict_table_open_on_id(table_id, TRUE, FALSE);
table = dict_table_open_on_id(table_id, TRUE, DICT_TABLE_OP_NORMAL);
if (table == NULL) {
/* table does not exist, must have been DROPped
@ -351,13 +331,11 @@ dict_stats_process_entry_from_recalc_pool()
mutex_exit(&dict_sys->mutex);
}
/* @} */
/*****************************************************************//**
This is the thread for background stats gathering. It pops tables, from
the auto recalc list and proceeds them, eventually recalculating their
statistics.
dict_stats_thread() @{
@return this function does not return, it calls os_thread_exit() */
extern "C" UNIV_INTERN
os_thread_ret_t
@ -397,6 +375,3 @@ DECLARE_THREAD(dict_stats_thread)(
OS_THREAD_DUMMY_RETURN;
}
/* @} */
/* vim: set foldmethod=marker foldmarker=@{,@}: */

View file

@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 1996, 2011, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 1996, 2013, Oracle and/or its affiliates. All Rights Reserved.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -35,7 +35,7 @@ UNIV_INTERN
dyn_block_t*
dyn_array_add_block(
/*================*/
dyn_array_t* arr) /*!< in: dyn array */
dyn_array_t* arr) /*!< in/out: dyn array */
{
mem_heap_t* heap;
dyn_block_t* block;

View file

@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 1995, 2012, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 1995, 2013, Oracle and/or its affiliates. All Rights Reserved.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -147,6 +147,8 @@ struct fil_node_t {
char* name; /*!< path to the file */
ibool open; /*!< TRUE if file open */
os_file_t handle; /*!< OS handle to the file, if file open */
os_event_t sync_event;/*!< Condition event to group and
serialize calls to fsync */
ibool is_raw_disk;/*!< TRUE if the 'file' is actually a raw
device or a raw disk partition */
ulint size; /*!< size of the file in database pages, 0 if
@ -374,9 +376,10 @@ NOTE: you must call fil_mutex_enter_and_prepare_for_io() first!
Prepares a file node for i/o. Opens the file if it is closed. Updates the
pending i/o's field in the node and the system appropriately. Takes the node
off the LRU list if it is in the LRU list. The caller must hold the fil_sys
mutex. */
mutex.
@return false if the file can't be opened, otherwise true */
static
void
bool
fil_node_prepare_for_io(
/*====================*/
fil_node_t* node, /*!< in: file node */
@ -416,7 +419,7 @@ UNIV_INLINE
dberr_t
fil_read(
/*=====*/
ibool sync, /*!< in: TRUE if synchronous aio is desired */
bool sync, /*!< in: true if synchronous aio is desired */
ulint space_id, /*!< in: space id */
ulint zip_size, /*!< in: compressed page size in bytes;
0 for uncompressed pages */
@ -445,7 +448,7 @@ UNIV_INLINE
dberr_t
fil_write(
/*======*/
ibool sync, /*!< in: TRUE if synchronous aio is desired */
bool sync, /*!< in: true if synchronous aio is desired */
ulint space_id, /*!< in: space id */
ulint zip_size, /*!< in: compressed page size in bytes;
0 for uncompressed pages */
@ -649,6 +652,7 @@ fil_node_create(
ut_a(!is_raw || srv_start_raw_disk_in_use);
node->sync_event = os_event_create();
node->is_raw_disk = is_raw;
node->size = size;
node->magic_n = FIL_NODE_MAGIC_N;
@ -689,9 +693,10 @@ fil_node_create(
/********************************************************************//**
Opens a file of a node of a tablespace. The caller must own the fil_system
mutex. */
mutex.
@return false if the file can't be opened, otherwise true */
static
void
bool
fil_node_open_file(
/*===============*/
fil_node_t* node, /*!< in: file node */
@ -729,12 +734,12 @@ fil_node_open_file(
ut_print_timestamp(stderr);
fprintf(stderr,
" InnoDB: Fatal error: cannot open %s\n."
"InnoDB: Have you deleted .ibd files"
" under a running mysqld server?\n",
ib_logf(IB_LOG_LEVEL_WARN, "InnoDB: Error: cannot "
"open %s\n. InnoDB: Have you deleted .ibd "
"files under a running mysqld server?\n",
node->name);
ut_a(0);
return(false);
}
size_bytes = os_file_get_size(node->handle);
@ -879,6 +884,8 @@ add_size:
/* Put the node to the LRU list */
UT_LIST_ADD_FIRST(LRU, system->LRU, node);
}
return(true);
}
/**********************************************************************//**
@ -1150,6 +1157,7 @@ fil_node_free(
there are no unflushed modifications in the file */
node->modification_counter = node->flush_counter;
os_event_set(node->sync_event);
if (fil_buffering_disabled(space)) {
@ -1173,6 +1181,7 @@ fil_node_free(
UT_LIST_REMOVE(chain, space->chain, node);
os_event_free(node->sync_event);
mem_free(node->name);
mem_free(node);
}
@ -1242,7 +1251,8 @@ fil_space_create(
if (space != 0) {
ib_logf(IB_LOG_LEVEL_WARN,
"Tablespace '%s' exists in the cache "
"with id %lu", name, (ulong) id);
"with id %lu != %lu",
name, (ulong) space->id, (ulong) id);
if (id == 0 || purpose != FIL_TABLESPACE) {
@ -1481,6 +1491,21 @@ fil_space_get_space(
if (space->size == 0 && space->purpose == FIL_TABLESPACE) {
ut_a(id != 0);
mutex_exit(&fil_system->mutex);
/* It is possible that the space gets evicted at this point
before the fil_mutex_enter_and_prepare_for_io() acquires
the fil_system->mutex. Check for this after completing the
call to fil_mutex_enter_and_prepare_for_io(). */
fil_mutex_enter_and_prepare_for_io(id);
/* We are still holding the fil_system->mutex. Check if
the space is still in memory cache. */
space = fil_space_get_by_id(id);
if (space == NULL) {
return(NULL);
}
/* The following code must change when InnoDB supports
multiple datafiles per tablespace. */
ut_a(1 == UT_LIST_GET_LEN(space->chain));
@ -1491,7 +1516,11 @@ fil_space_get_space(
the file yet; the following calls will open it and update the
size fields */
fil_node_prepare_for_io(node, fil_system, space);
if (!fil_node_prepare_for_io(node, fil_system, space)) {
/* The single-table tablespace can't be opened,
because the ibd file is missing. */
return(NULL);
}
fil_node_complete_io(node, fil_system, OS_FILE_READ);
}
@ -1552,8 +1581,7 @@ fil_space_get_size(
ulint size;
ut_ad(fil_system);
fil_mutex_enter_and_prepare_for_io(id);
mutex_enter(&fil_system->mutex);
space = fil_space_get_space(id);
@ -1583,7 +1611,7 @@ fil_space_get_flags(
return(0);
}
fil_mutex_enter_and_prepare_for_io(id);
mutex_enter(&fil_system->mutex);
space = fil_space_get_space(id);
@ -1700,7 +1728,15 @@ fil_open_log_and_system_tablespace_files(void)
node = UT_LIST_GET_NEXT(chain, node)) {
if (!node->open) {
fil_node_open_file(node, fil_system, space);
if (!fil_node_open_file(node, fil_system,
space)) {
/* This func is called during server's
startup. If some file of log or system
tablespace is missing, the server
can't start successfully. So we should
assert for it. */
ut_a(0);
}
}
if (fil_system->max_n_open < 10 + fil_system->n_open) {
@ -1929,11 +1965,64 @@ fil_write_flushed_lsn_to_data_files(
return(DB_SUCCESS);
}
/*******************************************************************//**
Checks the consistency of the first data page of a tablespace
at database startup.
@retval NULL on success, or if innodb_force_recovery is set
@return pointer to an error message string */
static __attribute__((warn_unused_result))
const char*
fil_check_first_page(
/*=================*/
const page_t* page) /*!< in: data page */
{
ulint space_id;
ulint flags;
if (srv_force_recovery >= SRV_FORCE_IGNORE_CORRUPT) {
return(NULL);
}
space_id = mach_read_from_4(FSP_HEADER_OFFSET + FSP_SPACE_ID + page);
flags = mach_read_from_4(FSP_HEADER_OFFSET + FSP_SPACE_FLAGS + page);
if (UNIV_PAGE_SIZE != fsp_flags_get_page_size(flags)) {
return("innodb-page-size mismatch");
}
if (!space_id && !flags) {
ulint nonzero_bytes = UNIV_PAGE_SIZE;
const byte* b = page;
while (!*b && --nonzero_bytes) {
b++;
}
if (!nonzero_bytes) {
return("space header page consists of zero bytes");
}
}
if (buf_page_is_corrupted(
false, page, fsp_flags_get_zip_size(flags))) {
return("checksum mismatch");
}
if (page_get_space_id(page) == space_id
&& page_get_page_no(page) == 0) {
return(NULL);
}
return("inconsistent data in space header");
}
/*******************************************************************//**
Reads the flushed lsn, arch no, and tablespace flag fields from a data
file at database startup. */
file at database startup.
@retval NULL on success, or if innodb_force_recovery is set
@return pointer to an error message string */
UNIV_INTERN
void
const char*
fil_read_first_page(
/*================*/
os_file_t data_file, /*!< in: open data file */
@ -1956,6 +2045,7 @@ fil_read_first_page(
byte* buf;
byte* page;
lsn_t flushed_lsn;
const char* check_msg = NULL;
buf = static_cast<byte*>(ut_malloc(2 * UNIV_PAGE_SIZE));
@ -1971,8 +2061,16 @@ fil_read_first_page(
flushed_lsn = mach_read_from_8(page + FIL_PAGE_FILE_FLUSH_LSN);
if (!one_read_already) {
check_msg = fil_check_first_page(page);
}
ut_free(buf);
if (check_msg) {
return(check_msg);
}
if (!one_read_already) {
*min_flushed_lsn = flushed_lsn;
*max_flushed_lsn = flushed_lsn;
@ -1980,7 +2078,7 @@ fil_read_first_page(
*min_arch_log_no = arch_log_no;
*max_arch_log_no = arch_log_no;
#endif /* UNIV_LOG_ARCHIVE */
return;
return(NULL);
}
if (*min_flushed_lsn > flushed_lsn) {
@ -1997,6 +2095,8 @@ fil_read_first_page(
*max_arch_log_no = arch_log_no;
}
#endif /* UNIV_LOG_ARCHIVE */
return(NULL);
}
/*================ SINGLE-TABLE TABLESPACES ==========================*/
@ -2567,7 +2667,7 @@ fil_close_tablespace(
char* cfg_name = fil_make_cfg_name(path);
os_file_delete_if_exists(cfg_name);
os_file_delete_if_exists(innodb_file_data_key, cfg_name);
mem_free(path);
mem_free(cfg_name);
@ -2650,7 +2750,7 @@ fil_delete_tablespace(
when we drop the database the remove directory will fail. */
{
char* cfg_name = fil_make_cfg_name(path);
os_file_delete_if_exists(cfg_name);
os_file_delete_if_exists(innodb_file_data_key, cfg_name);
mem_free(cfg_name);
}
@ -2678,7 +2778,8 @@ fil_delete_tablespace(
if (err != DB_SUCCESS) {
rw_lock_x_unlock(&space->latch);
} else if (!os_file_delete(path) && !os_file_delete_if_exists(path)) {
} else if (!os_file_delete(innodb_file_data_key, path)
&& !os_file_delete_if_exists(innodb_file_data_key, path)) {
/* Note: This is because we have removed the
tablespace instance from the cache. */
@ -3147,7 +3248,7 @@ fil_delete_link_file(
{
char* link_filepath = fil_make_isl_name(tablename);
os_file_delete_if_exists(link_filepath);
os_file_delete_if_exists(innodb_file_data_key, link_filepath);
mem_free(link_filepath);
}
@ -3458,7 +3559,7 @@ error_exit_1:
error_exit_2:
os_file_close(file);
if (err != DB_SUCCESS) {
os_file_delete(path);
os_file_delete(innodb_file_data_key, path);
}
error_exit_3:
mem_free(path);
@ -3473,12 +3574,25 @@ static
void
fil_report_bad_tablespace(
/*======================*/
char* filepath, /*!< in: filepath */
const char* filepath, /*!< in: filepath */
const char* check_msg, /*!< in: fil_check_first_page() */
ulint found_id, /*!< in: found space ID */
ulint found_flags, /*!< in: found flags */
ulint expected_id, /*!< in: expected space id */
ulint expected_flags) /*!< in: expected flags */
{
if (check_msg) {
ib_logf(IB_LOG_LEVEL_ERROR,
"Error %s in file '%s',"
"tablespace id=%lu, flags=%lu. "
"Please refer to "
REFMAN "innodb-troubleshooting-datadict.html "
"for how to resolve the issue.",
check_msg, filepath,
(ulong) expected_id, (ulong) expected_flags);
return;
}
ib_logf(IB_LOG_LEVEL_ERROR,
"In file '%s', tablespace id and flags are %lu and %lu, "
"but in the InnoDB data dictionary they are %lu and %lu. "
@ -3493,6 +3607,7 @@ fil_report_bad_tablespace(
struct fsp_open_info {
ibool success; /*!< Has the tablespace been opened? */
const char* check_msg; /*!< fil_check_first_page() message */
ibool valid; /*!< Is the tablespace valid? */
os_file_t file; /*!< File handle */
char* filepath; /*!< File path to open */
@ -3635,48 +3750,50 @@ fil_open_single_table_tablespace(
/* Read the first page of the datadir tablespace, if found. */
if (def.success) {
fil_read_first_page(
def.check_msg = fil_read_first_page(
def.file, FALSE, &def.flags, &def.id,
#ifdef UNIV_LOG_ARCHIVE
&space_arch_log_no, &space_arch_log_no,
#endif /* UNIV_LOG_ARCHIVE */
&def.lsn, &def.lsn);
def.valid = !def.check_msg;
/* Validate this single-table-tablespace with SYS_TABLES,
but do not compare the DATA_DIR flag, in case the
tablespace was relocated. */
ulint mod_def_flags = def.flags & ~FSP_FLAGS_MASK_DATA_DIR;
if (def.id == id && mod_def_flags == mod_flags) {
if (def.valid && def.id == id
&& (def.flags & ~FSP_FLAGS_MASK_DATA_DIR) == mod_flags) {
valid_tablespaces_found++;
def.valid = TRUE;
} else {
def.valid = false;
/* Do not use this tablespace. */
fil_report_bad_tablespace(
def.filepath, def.id,
def.filepath, def.check_msg, def.id,
def.flags, id, flags);
}
}
/* Read the first page of the remote tablespace */
if (remote.success) {
fil_read_first_page(
remote.check_msg = fil_read_first_page(
remote.file, FALSE, &remote.flags, &remote.id,
#ifdef UNIV_LOG_ARCHIVE
&remote.arch_log_no, &remote.arch_log_no,
#endif /* UNIV_LOG_ARCHIVE */
&remote.lsn, &remote.lsn);
remote.valid = !remote.check_msg;
/* Validate this single-table-tablespace with SYS_TABLES,
but do not compare the DATA_DIR flag, in case the
tablespace was relocated. */
ulint mod_remote_flags = remote.flags & ~FSP_FLAGS_MASK_DATA_DIR;
if (remote.id == id && mod_remote_flags == mod_flags) {
if (remote.valid && remote.id == id
&& (remote.flags & ~FSP_FLAGS_MASK_DATA_DIR) == mod_flags) {
valid_tablespaces_found++;
remote.valid = TRUE;
} else {
remote.valid = false;
/* Do not use this linked tablespace. */
fil_report_bad_tablespace(
remote.filepath, remote.id,
remote.filepath, remote.check_msg, remote.id,
remote.flags, id, flags);
link_file_is_bad = true;
}
@ -3684,24 +3801,25 @@ fil_open_single_table_tablespace(
/* Read the first page of the datadir tablespace, if found. */
if (dict.success) {
fil_read_first_page(
dict.check_msg = fil_read_first_page(
dict.file, FALSE, &dict.flags, &dict.id,
#ifdef UNIV_LOG_ARCHIVE
&dict.arch_log_no, &dict.arch_log_no,
#endif /* UNIV_LOG_ARCHIVE */
&dict.lsn, &dict.lsn);
dict.valid = !dict.check_msg;
/* Validate this single-table-tablespace with SYS_TABLES,
but do not compare the DATA_DIR flag, in case the
tablespace was relocated. */
ulint mod_dict_flags = dict.flags & ~FSP_FLAGS_MASK_DATA_DIR;
if (dict.id == id && mod_dict_flags == mod_flags) {
if (dict.valid && dict.id == id
&& (dict.flags & ~FSP_FLAGS_MASK_DATA_DIR) == mod_flags) {
valid_tablespaces_found++;
dict.valid = TRUE;
} else {
dict.valid = false;
/* Do not use this tablespace. */
fil_report_bad_tablespace(
dict.filepath, dict.id,
dict.filepath, dict.check_msg, dict.id,
dict.flags, id, flags);
}
}
@ -3914,16 +4032,22 @@ fil_validate_single_table_tablespace(
const char* tablename, /*!< in: database/tablename */
fsp_open_info* fsp) /*!< in/out: tablespace info */
{
fil_read_first_page(
if (const char* check_msg = fil_read_first_page(
fsp->file, FALSE, &fsp->flags, &fsp->id,
#ifdef UNIV_LOG_ARCHIVE
&fsp->arch_log_no, &fsp->arch_log_no,
#endif /* UNIV_LOG_ARCHIVE */
&fsp->lsn, &fsp->lsn);
&fsp->lsn, &fsp->lsn)) {
ib_logf(IB_LOG_LEVEL_ERROR,
"%s in tablespace %s (table %s)",
check_msg, fsp->filepath, tablename);
fsp->success = FALSE;
return;
}
if (fsp->id == ULINT_UNDEFINED || fsp->id == 0) {
fprintf(stderr,
" InnoDB: Error: Tablespace is not sensible;"
ib_logf(IB_LOG_LEVEL_ERROR,
"Tablespace is not sensible;"
" Table: %s Space ID: %lu Filepath: %s\n",
tablename, (ulong) fsp->id, fsp->filepath);
fsp->success = FALSE;
@ -4051,6 +4175,19 @@ fil_load_single_table_tablespace(
fprintf(stderr,
"InnoDB: Error: could not open single-table"
" tablespace file %s\n", def.filepath);
if (!strncmp(filename,
tmp_file_prefix, tmp_file_prefix_length)) {
/* Ignore errors for #sql tablespaces. */
mem_free(tablename);
if (remote.filepath) {
mem_free(remote.filepath);
}
if (def.filepath) {
mem_free(def.filepath);
}
return;
}
no_good_file:
fprintf(stderr,
"InnoDB: We do not continue the crash recovery,"
@ -4075,10 +4212,12 @@ no_good_file:
" recovery here.\n");
will_not_choose:
mem_free(tablename);
if (remote.success) {
if (remote.filepath) {
mem_free(remote.filepath);
}
if (def.filepath) {
mem_free(def.filepath);
}
if (srv_force_recovery > 0) {
ib_logf(IB_LOG_LEVEL_INFO,
@ -4089,9 +4228,6 @@ will_not_choose:
return;
}
/* If debug code, cause a core dump and call stack. For
release builds just exit and rely on the messages above. */
ut_ad(0);
exit(1);
}
@ -4167,7 +4303,7 @@ will_not_choose:
new_path = fil_make_ibbackup_old_name(fsp->filepath);
bool success = os_file_rename(
innodb_file_data_key, fsp->filepath, new_path));
innodb_file_data_key, fsp->filepath, new_path);
ut_a(success);
@ -4772,7 +4908,13 @@ retry:
goto retry;
}
fil_node_prepare_for_io(node, fil_system, space);
if (!fil_node_prepare_for_io(node, fil_system, space)) {
/* The tablespace data file, such as .ibd file, is missing */
node->being_extended = false;
mutex_exit(&fil_system->mutex);
return(false);
}
/* At this point it is safe to release fil_system mutex. No
other thread can rename, delete or close the file because
@ -5044,9 +5186,10 @@ NOTE: you must call fil_mutex_enter_and_prepare_for_io() first!
Prepares a file node for i/o. Opens the file if it is closed. Updates the
pending i/o's field in the node and the system appropriately. Takes the node
off the LRU list if it is in the LRU list. The caller must hold the fil_sys
mutex. */
mutex.
@return false if the file can't be opened, otherwise true */
static
void
bool
fil_node_prepare_for_io(
/*====================*/
fil_node_t* node, /*!< in: file node */
@ -5068,7 +5211,10 @@ fil_node_prepare_for_io(
if (node->open == FALSE) {
/* File is closed: open it */
ut_a(node->n_pending == 0);
fil_node_open_file(node, system, space);
if (!fil_node_open_file(node, system, space)) {
return(false);
}
}
if (node->n_pending == 0 && fil_space_belongs_in_lru(space)) {
@ -5080,6 +5226,8 @@ fil_node_prepare_for_io(
}
node->n_pending++;
return(true);
}
/********************************************************************//**
@ -5177,7 +5325,7 @@ fil_io(
because i/os are not actually handled until
all have been posted: use with great
caution! */
ibool sync, /*!< in: TRUE if synchronous aio is desired */
bool sync, /*!< in: true if synchronous aio is desired */
ulint space_id, /*!< in: space id */
ulint zip_size, /*!< in: compressed page size in bytes;
0 for uncompressed pages */
@ -5312,7 +5460,28 @@ fil_io(
}
/* Open file if closed */
fil_node_prepare_for_io(node, fil_system, space);
if (!fil_node_prepare_for_io(node, fil_system, space)) {
if (space->purpose == FIL_TABLESPACE
&& fil_is_user_tablespace_id(space->id)) {
mutex_exit(&fil_system->mutex);
ib_logf(IB_LOG_LEVEL_ERROR,
"Trying to do i/o to a tablespace which "
"exists without .ibd data file. "
"i/o type %lu, space id %lu, page no %lu, "
"i/o length %lu bytes",
(ulong) type, (ulong) space_id,
(ulong) block_offset, (ulong) len);
return(DB_TABLESPACE_DELETED);
}
/* The tablespace is for log. Currently, we just assert here
to prevent handling errors along the way fil_io returns.
Also, if the log files are missing, it would be hard to
promise the server can continue running. */
ut_a(0);
}
/* Check that at least the start offset is within the bounds of a
single-table tablespace, including rollback tablespaces. */
@ -5475,7 +5644,7 @@ fil_flush(
fil_space_t* space;
fil_node_t* node;
os_file_t file;
ib_int64_t old_mod_counter;
mutex_enter(&fil_system->mutex);
@ -5511,16 +5680,18 @@ fil_flush(
space->n_pending_flushes++; /*!< prevent dropping of the space while
we are flushing */
node = UT_LIST_GET_FIRST(space->chain);
for (node = UT_LIST_GET_FIRST(space->chain);
node != NULL;
node = UT_LIST_GET_NEXT(chain, node)) {
ib_int64_t old_mod_counter = node->modification_counter;;
if (old_mod_counter <= node->flush_counter) {
continue;
}
while (node) {
if (node->modification_counter > node->flush_counter) {
ut_a(node->open);
/* We want to flush the changes at least up to
old_mod_counter */
old_mod_counter = node->modification_counter;
if (space->purpose == FIL_TABLESPACE) {
fil_n_pending_tablespace_flushes++;
} else {
@ -5538,11 +5709,14 @@ retry:
/* We want to avoid calling os_file_flush() on
the file twice at the same time, because we do
not know what bugs OS's may contain in file
i/o; sleep for a while */
i/o */
ib_int64_t sig_count =
os_event_reset(node->sync_event);
mutex_exit(&fil_system->mutex);
os_thread_sleep(20000);
os_event_wait_low(node->sync_event, sig_count);
mutex_enter(&fil_system->mutex);
@ -5560,13 +5734,12 @@ retry:
mutex_exit(&fil_system->mutex);
/* fprintf(stderr, "Flushing to file %s\n",
node->name); */
os_file_flush(file);
mutex_enter(&fil_system->mutex);
os_event_set(node->sync_event);
node->n_pending_flushes--;
skip_flush:
if (node->flush_counter < old_mod_counter) {
@ -5591,9 +5764,6 @@ skip_flush:
}
}
node = UT_LIST_GET_NEXT(chain, node);
}
space->n_pending_flushes--;
mutex_exit(&fil_system->mutex);
@ -6131,11 +6301,11 @@ fil_delete_file(
ib_logf(IB_LOG_LEVEL_INFO, "Deleting %s", ibd_name);
os_file_delete_if_exists(ibd_name);
os_file_delete_if_exists(innodb_file_data_key, ibd_name);
char* cfg_name = fil_make_cfg_name(ibd_name);
os_file_delete_if_exists(cfg_name);
os_file_delete_if_exists(innodb_file_data_key, cfg_name);
mem_free(cfg_name);
}
@ -6198,15 +6368,17 @@ fil_mtr_rename_log(
ulint new_space_id, /*!< in: tablespace id of the new
table */
const char* new_name, /*!< in: new table name */
const char* tmp_name) /*!< in: temp table name used while
const char* tmp_name, /*!< in: temp table name used while
swapping */
mtr_t* mtr) /*!< in/out: mini-transaction */
{
mtr_t mtr;
mtr_start(&mtr);
if (old_space_id != TRX_SYS_SPACE) {
fil_op_write_log(MLOG_FILE_RENAME, old_space_id,
0, 0, old_name, tmp_name, &mtr);
fil_op_write_log(MLOG_FILE_RENAME, new_space_id,
0, 0, new_name, old_name, &mtr);
mtr_commit(&mtr);
}
0, 0, old_name, tmp_name, mtr);
}
if (new_space_id != TRX_SYS_SPACE) {
fil_op_write_log(MLOG_FILE_RENAME, new_space_id,
0, 0, new_name, old_name, mtr);
}
}

View file

@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 1995, 2012, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 1995, 2013, Oracle and/or its affiliates. All Rights Reserved.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -1035,6 +1035,11 @@ fsp_try_extend_data_file(
success = fil_extend_space_to_desired_size(&actual_size, space,
size + size_increase);
if (!success) {
return(false);
}
/* We ignore any fragments of a full megabyte when storing the size
to the space header */

View file

@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 2007, 2012, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2007, 2013, Oracle and/or its affiliates. All Rights Reserved.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -26,6 +26,18 @@ Created 2007/3/16 Sunny Bains.
#include "mem0mem.h"
#include "fts0ast.h"
#include "fts0pars.h"
#include "fts0fts.h"
/* The FTS ast visit pass. */
enum fts_ast_visit_pass_t {
FTS_PASS_FIRST, /*!< First visit pass,
process operators excluding
FTS_EXIST and FTS_IGNORE */
FTS_PASS_EXIST, /*!< Exist visit pass,
process operator FTS_EXIST */
FTS_PASS_IGNORE /*!< Ignore visit pass,
process operator FTS_IGNORE */
};
/******************************************************************//**
Create an empty fts_ast_node_t.
@ -66,7 +78,7 @@ fts_ast_create_node_oper(
/******************************************************************//**
This function takes ownership of the ptr and is responsible
for free'ing it
@return new node */
@return new node or a node list with tokenized words */
UNIV_INTERN
fts_ast_node_t*
fts_ast_create_node_term(
@ -74,17 +86,68 @@ fts_ast_create_node_term(
void* arg, /*!< in: ast state instance */
const char* ptr) /*!< in: ast term string */
{
fts_ast_state_t* state = static_cast<fts_ast_state_t*>(arg);
ulint len = strlen(ptr);
fts_ast_node_t* node = fts_ast_node_create();
ulint cur_pos = 0;
fts_ast_node_t* node = NULL;
fts_ast_node_t* node_list = NULL;
fts_ast_node_t* first_node = NULL;
/* Scan the incoming string and filter out any "non-word" characters */
while (cur_pos < len) {
fts_string_t str;
ulint offset;
ulint cur_len;
cur_len = innobase_mysql_fts_get_token(
state->charset,
reinterpret_cast<const byte*>(ptr) + cur_pos,
reinterpret_cast<const byte*>(ptr) + len, &str, &offset);
if (cur_len == 0) {
break;
}
cur_pos += cur_len;
if (str.f_n_char > 0) {
/* If the subsequent term (after the first one)'s size
is less than fts_min_token_size, we shall ignore
that. This is to make consistent with MyISAM behavior */
if (first_node && (str.f_n_char < fts_min_token_size)) {
continue;
}
node = fts_ast_node_create();
node->type = FTS_AST_TERM;
node->term.ptr = static_cast<byte*>(ut_malloc(len + 1));
memcpy(node->term.ptr, ptr, len + 1);
node->term.ptr = static_cast<byte*>(ut_malloc(
str.f_len + 1));
memcpy(node->term.ptr, str.f_str, str.f_len);
node->term.ptr[str.f_len] = '\0';
fts_ast_state_add_node((fts_ast_state_t*) arg, node);
fts_ast_state_add_node(
static_cast<fts_ast_state_t*>(arg), node);
return(node);
if (first_node) {
/* There is more than one word, create
a list to organize them */
if (!node_list) {
node_list = fts_ast_create_node_list(
static_cast<fts_ast_state_t*>(
arg),
first_node);
}
fts_ast_add_node(node_list, node);
} else {
first_node = node;
}
}
}
return((node_list != NULL) ? node_list : first_node);
}
/******************************************************************//**
@ -101,11 +164,19 @@ fts_ast_create_node_text(
ulint len = strlen(ptr);
fts_ast_node_t* node = NULL;
ut_ad(len >= 2);
ut_ad(len >= 1);
if (len <= 2) {
/* There is a way to directly supply null terminator
in the query string (by using 0x220022) and get here,
and certainly it would not make a valid query text */
ut_ad(ptr[0] == '\"');
if (len == 2) {
ut_ad(ptr[0] == '\"');
ut_ad(ptr[1] == '\"');
}
return(NULL);
}
@ -297,6 +368,16 @@ fts_ast_term_set_wildcard(
fts_ast_node_t* node) /*!< in/out: set attribute of
a term node */
{
if (!node) {
return;
}
/* If it's a node list, the wildcard should be set to the tail node*/
if (node->type == FTS_AST_LIST) {
ut_ad(node->list.tail != NULL);
node = node->list.tail;
}
ut_a(node->type == FTS_AST_TERM);
ut_a(!node->term.wildcard);
@ -393,9 +474,9 @@ fts_ast_node_print(
}
/******************************************************************//**
Traverse the AST - in-order traversal, except for the FTS_IGNORE
nodes, which will be ignored in the first pass of each level, and
visited in a second pass after all other nodes in the same level are visited.
Traverse the AST - in-order traversal, except for the FTX_EXIST and FTS_IGNORE
nodes, which will be ignored in the first pass of each level, and visited in a
second and third pass after all other nodes in the same level are visited.
@return DB_SUCCESS if all went well */
UNIV_INTERN
dberr_t
@ -407,85 +488,142 @@ fts_ast_visit(
void* arg, /*!< in: arg for callback */
bool* has_ignore) /*!< out: true, if the operator
was ignored during processing,
currently we only ignore
FTS_IGNORE operator */
currently we ignore FTS_EXIST
and FTS_IGNORE operators */
{
dberr_t error = DB_SUCCESS;
fts_ast_node_t* oper_node = NULL;
fts_ast_node_t* start_node;
bool revisit = false;
bool will_be_ignored = false;
fts_ast_visit_pass_t visit_pass = FTS_PASS_FIRST;
start_node = node->list.head;
ut_a(node->type == FTS_AST_LIST
|| node->type == FTS_AST_SUBEXP_LIST);
if (oper == FTS_EXIST_SKIP) {
visit_pass = FTS_PASS_EXIST;
} else if (oper == FTS_IGNORE_SKIP) {
visit_pass = FTS_PASS_IGNORE;
}
/* In the first pass of the tree, at the leaf level of the
tree, FTS_IGNORE operation will be ignored. It will be
repeated at the level above the leaf level */
tree, FTS_EXIST and FTS_IGNORE operation will be ignored.
It will be repeated at the level above the leaf level.
The basic idea here is that when we encounter FTS_EXIST or
FTS_IGNORE, we will change the operator node into FTS_EXIST_SKIP
or FTS_IGNORE_SKIP, and term node & text node with the operators
is ignored in the first pass. We have two passes during the revisit:
We process nodes with FTS_EXIST_SKIP in the exist pass, and then
process nodes with FTS_IGNORE_SKIP in the ignore pass.
The order should be restrictly followed, or we will get wrong results.
For example, we have a query 'a +b -c d +e -f'.
first pass: process 'a' and 'd' by union;
exist pass: process '+b' and '+e' by intersection;
ignore pass: process '-c' and '-f' by difference. */
for (node = node->list.head;
node && (error == DB_SUCCESS);
node = node->next) {
if (node->type == FTS_AST_LIST) {
switch(node->type) {
case FTS_AST_LIST:
if (visit_pass != FTS_PASS_FIRST) {
break;
}
error = fts_ast_visit(oper, node, visitor,
arg, &will_be_ignored);
/* If will_be_ignored is set to true, then
we encountered and ignored a FTS_IGNORE operator,
and a second pass is needed to process FTS_IGNORE
operator */
we encountered and ignored a FTS_EXIST or FTS_IGNORE
operator. */
if (will_be_ignored) {
revisit = true;
/* Remember oper for list in case '-abc&def',
ignored oper is from previous node of list.*/
node->oper = oper;
}
} else if (node->type == FTS_AST_SUBEXP_LIST) {
break;
case FTS_AST_SUBEXP_LIST:
if (visit_pass != FTS_PASS_FIRST) {
break;
}
error = fts_ast_visit_sub_exp(node, visitor, arg);
} else if (node->type == FTS_AST_OPER) {
break;
case FTS_AST_OPER:
oper = node->oper;
oper_node = node;
} else {
/* Change the operator for revisit */
if (oper == FTS_EXIST) {
oper_node->oper = FTS_EXIST_SKIP;
} else if (oper == FTS_IGNORE) {
oper_node->oper = FTS_IGNORE_SKIP;
}
break;
default:
if (node->visited) {
continue;
}
ut_a(oper == FTS_NONE || !oper_node
|| oper_node->oper == oper);
|| oper_node->oper == oper
|| oper_node->oper == FTS_EXIST_SKIP
|| oper_node->oper == FTS_IGNORE_SKIP);
if (oper == FTS_IGNORE) {
if (oper== FTS_EXIST || oper == FTS_IGNORE) {
*has_ignore = true;
/* Change the operator to FTS_IGNORE_SKIP,
so that it is processed in the second pass */
oper_node->oper = FTS_IGNORE_SKIP;
continue;
}
if (oper == FTS_IGNORE_SKIP) {
/* This must be the second pass, now we process
the FTS_IGNORE operator */
visitor(FTS_IGNORE, node, arg);
} else {
visitor(oper, node, arg);
}
/* Process leaf node accroding to its pass.*/
if (oper == FTS_EXIST_SKIP
&& visit_pass == FTS_PASS_EXIST) {
error = visitor(FTS_EXIST, node, arg);
node->visited = true;
} else if (oper == FTS_IGNORE_SKIP
&& visit_pass == FTS_PASS_IGNORE) {
error = visitor(FTS_IGNORE, node, arg);
node->visited = true;
} else if (visit_pass == FTS_PASS_FIRST) {
error = visitor(oper, node, arg);
node->visited = true;
}
}
}
/* Second pass to process the skipped FTS_IGNORE operation.
It is only performed at the level above leaf level */
if (revisit) {
/* Exist pass processes the skipped FTS_EXIST operation. */
for (node = start_node;
node && error == DB_SUCCESS;
node = node->next) {
if (node->type == FTS_AST_LIST
&& node->oper != FTS_IGNORE) {
error = fts_ast_visit(FTS_EXIST_SKIP, node,
visitor, arg, &will_be_ignored);
}
}
/* Ignore pass processes the skipped FTS_IGNORE operation. */
for (node = start_node;
node && error == DB_SUCCESS;
node = node->next) {
if (node->type == FTS_AST_LIST) {
/* In this pass, it will process all those
operators ignored in the first pass, and those
whose operators are set to FTS_IGNORE_SKIP */
error = fts_ast_visit(
oper, node, visitor, arg,
&will_be_ignored);
error = fts_ast_visit(FTS_IGNORE_SKIP, node,
visitor, arg, &will_be_ignored);
}
}
}

View file

@ -305,9 +305,9 @@ YY_BUFFER_STATE fts0b_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner
YY_BUFFER_STATE fts0b_scan_string (yyconst char *yy_str ,yyscan_t yyscanner );
YY_BUFFER_STATE fts0b_scan_bytes (yyconst char *bytes,int len ,yyscan_t yyscanner );
void *fts0balloc (yy_size_t , yyscan_t yyscanner __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) );
void *fts0brealloc (void *,yy_size_t , yyscan_t yyscanner __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) );
void fts0bfree (void * , yyscan_t yyscanner __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) );
void *fts0balloc (yy_size_t , yyscan_t yyscanner __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) );
void *fts0brealloc (void *,yy_size_t , yyscan_t yyscanner __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) );
void fts0bfree (void * , yyscan_t yyscanner __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) );
#define yy_new_buffer fts0b_create_buffer
@ -347,7 +347,7 @@ typedef int yy_state_type;
static yy_state_type yy_get_previous_state (yyscan_t yyscanner );
static yy_state_type yy_try_NUL_trans (yy_state_type current_state ,yyscan_t yyscanner);
static int yy_get_next_buffer (yyscan_t yyscanner );
static void yy_fatal_error (yyconst char msg[] , yyscan_t yyscanner __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) );
static void yy_fatal_error (yyconst char msg[] , yyscan_t yyscanner __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) );
/* Done after the current pattern has been matched and before the
* corresponding action - sets up yytext.
@ -579,11 +579,11 @@ extern int fts0bwrap (yyscan_t yyscanner );
#endif
#ifndef yytext_ptr
static void yy_flex_strncpy (char *,yyconst char *,int , yyscan_t yyscanner __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)));
static void yy_flex_strncpy (char *,yyconst char *,int , yyscan_t yyscanner __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)));
#endif
#ifdef YY_NEED_STRLEN
static int yy_flex_strlen (yyconst char * , yyscan_t yyscanner __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)));
static int yy_flex_strlen (yyconst char * , yyscan_t yyscanner __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)));
#endif
#ifndef YY_NO_INPUT
@ -1609,7 +1609,7 @@ YY_BUFFER_STATE fts0b_scan_bytes (yyconst char * yybytes, int _yybytes_len , y
#define YY_EXIT_FAILURE 2
#endif
static void yy_fatal_error (yyconst char* msg , yyscan_t yyscanner __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)))
static void yy_fatal_error (yyconst char* msg , yyscan_t yyscanner __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)))
{
(void) fprintf( stderr, "%s\n", msg );
exit( YY_EXIT_FAILURE );
@ -1910,7 +1910,7 @@ int fts0blex_destroy (yyscan_t yyscanner)
*/
#ifndef yytext_ptr
static void yy_flex_strncpy (char* s1, yyconst char * s2, int n , yyscan_t yyscanner __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)))
static void yy_flex_strncpy (char* s1, yyconst char * s2, int n , yyscan_t yyscanner __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)))
{
register int i;
for ( i = 0; i < n; ++i )
@ -1919,7 +1919,7 @@ static void yy_flex_strncpy (char* s1, yyconst char * s2, int n , yyscan_t y
#endif
#ifdef YY_NEED_STRLEN
static int yy_flex_strlen (yyconst char * s , yyscan_t yyscanner __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)))
static int yy_flex_strlen (yyconst char * s , yyscan_t yyscanner __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)))
{
register int n;
for ( n = 0; s[n]; ++n )
@ -1929,12 +1929,12 @@ static int yy_flex_strlen (yyconst char * s , yyscan_t yyscanner __attribute
}
#endif
void *fts0balloc (yy_size_t size , yyscan_t yyscanner __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)))
void *fts0balloc (yy_size_t size , yyscan_t yyscanner __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)))
{
return (void *) malloc( size );
}
void *fts0brealloc (void * ptr, yy_size_t size , yyscan_t yyscanner __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)))
void *fts0brealloc (void * ptr, yy_size_t size , yyscan_t yyscanner __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)))
{
/* The cast to (char *) in the following accommodates both
* implementations that use char* generic pointers, and those
@ -1946,7 +1946,7 @@ void *fts0brealloc (void * ptr, yy_size_t size , yyscan_t yyscanner __attr
return (void *) realloc( (char *) ptr, size );
}
void fts0bfree (void * ptr , yyscan_t yyscanner __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)))
void fts0bfree (void * ptr , yyscan_t yyscanner __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)))
{
free( (char *) ptr ); /* see fts0brealloc() for (char *) cast */
}

View file

@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 2007, 2012, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2007, 2013, Oracle and/or its affiliates. All Rights Reserved.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -221,7 +221,7 @@ fts_config_set_value(
graph = fts_parse_sql(
fts_table, info,
"BEGIN UPDATE %s SET value = :value WHERE key = :name;");
"BEGIN UPDATE \"%s\" SET value = :value WHERE key = :name;");
trx->op_info = "setting FTS config value";
@ -246,7 +246,7 @@ fts_config_set_value(
graph = fts_parse_sql(
fts_table, info,
"BEGIN\n"
"INSERT INTO %s VALUES(:name, :value);");
"INSERT INTO \"%s\" VALUES(:name, :value);");
trx->op_info = "inserting FTS config value";

View file

@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 2011, 2012, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2011, 2013, Oracle and/or its affiliates. All Rights Reserved.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -48,6 +48,17 @@ Full Text Search interface
a configurable variable */
UNIV_INTERN ulong fts_max_cache_size;
/** Whether the total memory used for FTS cache is exhausted, and we will
need a sync to free some memory */
UNIV_INTERN bool fts_need_sync = false;
/** Variable specifying the total memory allocated for FTS cache */
UNIV_INTERN ulong fts_max_total_cache_size;
/** This is FTS result cache limit for each query and would be
a configurable variable */
UNIV_INTERN ulong fts_result_cache_limit;
/** Variable specifying the maximum FTS max token size */
UNIV_INTERN ulong fts_max_token_size;
@ -146,74 +157,65 @@ struct fts_aux_table_t {
static const char* fts_create_common_tables_sql = {
"BEGIN\n"
""
"CREATE TABLE %s_ADDED (\n"
"CREATE TABLE \"%s_DELETED\" (\n"
" doc_id BIGINT UNSIGNED\n"
") COMPACT;\n"
"CREATE UNIQUE CLUSTERED INDEX IND ON %s_ADDED(doc_id);\n"
"CREATE UNIQUE CLUSTERED INDEX IND ON \"%s_DELETED\"(doc_id);\n"
""
"CREATE TABLE %s_DELETED (\n"
" doc_id BIGINT UNSIGNED\n"
") COMPACT;\n"
"CREATE UNIQUE CLUSTERED INDEX IND ON %s_DELETED(doc_id);\n"
""
"CREATE TABLE %s_DELETED_CACHE (\n"
"CREATE TABLE \"%s_DELETED_CACHE\" (\n"
" doc_id BIGINT UNSIGNED\n"
") COMPACT;\n"
"CREATE UNIQUE CLUSTERED INDEX IND "
"ON %s_DELETED_CACHE(doc_id);\n"
"ON \"%s_DELETED_CACHE\"(doc_id);\n"
""
"CREATE TABLE %s_BEING_DELETED (\n"
"CREATE TABLE \"%s_BEING_DELETED\" (\n"
" doc_id BIGINT UNSIGNED\n"
") COMPACT;\n"
"CREATE UNIQUE CLUSTERED INDEX IND "
"ON %s_BEING_DELETED(doc_id);\n"
"ON \"%s_BEING_DELETED\"(doc_id);\n"
""
"CREATE TABLE %s_BEING_DELETED_CACHE (\n"
"CREATE TABLE \"%s_BEING_DELETED_CACHE\" (\n"
" doc_id BIGINT UNSIGNED\n"
") COMPACT;\n"
"CREATE UNIQUE CLUSTERED INDEX IND "
"ON %s_BEING_DELETED_CACHE(doc_id);\n"
"ON \"%s_BEING_DELETED_CACHE\"(doc_id);\n"
""
"CREATE TABLE %s_CONFIG (\n"
"CREATE TABLE \"%s_CONFIG\" (\n"
" key CHAR(50),\n"
" value CHAR(50) NOT NULL\n"
") COMPACT;\n"
"CREATE UNIQUE CLUSTERED INDEX IND ON %s_CONFIG(key);\n"
""
"CREATE TABLE %s_STOPWORDS (\n"
" word CHAR\n"
") COMPACT;\n"
"CREATE UNIQUE CLUSTERED INDEX IND ON %s_STOPWORDS(word);\n",
"CREATE UNIQUE CLUSTERED INDEX IND ON \"%s_CONFIG\"(key);\n"
};
/** Template for creating the FTS auxiliary index specific tables. */
#ifdef FTS_DOC_STATS_DEBUG
/** Template for creating the FTS auxiliary index specific tables. This is
mainly designed for the statistics work in the future */
static const char* fts_create_index_tables_sql = {
"BEGIN\n"
""
"CREATE TABLE %s_DOC_ID (\n"
"CREATE TABLE \"%s_DOC_ID\" (\n"
" doc_id BIGINT UNSIGNED,\n"
" word_count INTEGER UNSIGNED NOT NULL\n"
") COMPACT;\n"
"CREATE UNIQUE CLUSTERED INDEX IND ON %s_DOC_ID(doc_id);\n"
"CREATE UNIQUE CLUSTERED INDEX IND ON \"%s_DOC_ID\"(doc_id);\n"
};
#endif
/** Template for creating the ancillary FTS tables word index tables. */
static const char* fts_create_index_sql = {
"BEGIN\n"
""
"CREATE UNIQUE CLUSTERED INDEX FTS_INDEX_TABLE_IND "
"ON %s(word, first_doc_id);\n"
"ON \"%s\"(word, first_doc_id);\n"
};
/** FTS auxiliary table suffixes that are common to all FT indexes. */
static const char* fts_common_tables[] = {
"ADDED",
"BEING_DELETED",
"BEING_DELETED_CACHE",
"CONFIG",
"DELETED",
"DELETED_CACHE",
"STOPWORDS",
NULL
};
@ -232,19 +234,19 @@ const fts_index_selector_t fts_index_selector[] = {
static const char* fts_config_table_insert_values_sql =
"BEGIN\n"
"\n"
"INSERT INTO %s VALUES('"
"INSERT INTO \"%s\" VALUES('"
FTS_MAX_CACHE_SIZE_IN_MB "', '256');\n"
""
"INSERT INTO %s VALUES('"
"INSERT INTO \"%s\" VALUES('"
FTS_OPTIMIZE_LIMIT_IN_SECS "', '180');\n"
""
"INSERT INTO %s VALUES ('"
"INSERT INTO \"%s\" VALUES ('"
FTS_SYNCED_DOC_ID "', '0');\n"
""
"INSERT INTO %s VALUES ('"
"INSERT INTO \"%s\" VALUES ('"
FTS_TOTAL_DELETED_COUNT "', '0');\n"
"" /* Note: 0 == FTS_TABLE_STATE_RUNNING */
"INSERT INTO %s VALUES ('"
"INSERT INTO \"%s\" VALUES ('"
FTS_TABLE_STATE "', '0');\n";
/****************************************************************//**
@ -355,6 +357,13 @@ fts_load_default_stopword(
allocator = stopword_info->heap;
heap = static_cast<mem_heap_t*>(allocator->arg);
if (!stopword_info->cached_stopword) {
/* For default stopword, we always use fts_utf8_string_cmp() */
stopword_info->cached_stopword = rbt_create(
sizeof(fts_tokenizer_word_t), fts_utf8_string_cmp);
}
stop_words = stopword_info->cached_stopword;
str.f_n_char = 0;
@ -468,9 +477,17 @@ fts_load_user_stopword(
/* Validate the user table existence and in the right
format */
if (!fts_valid_stopword_table(stopword_table_name)) {
stopword_info->charset = fts_valid_stopword_table(stopword_table_name);
if (!stopword_info->charset) {
ret = FALSE;
goto cleanup;
} else if (!stopword_info->cached_stopword) {
/* Create the stopword RB tree with the stopword column
charset. All comparison will use this charset */
stopword_info->cached_stopword = rbt_create_arg_cmp(
sizeof(fts_tokenizer_word_t), innobase_fts_text_cmp,
(void*)stopword_info->charset);
}
info = pars_info_create();
@ -638,6 +655,8 @@ fts_cache_create(
cache->sync_heap = ib_heap_allocator_create(heap);
cache->sync_heap->arg = NULL;
fts_need_sync = false;
cache->sync = static_cast<fts_sync_t*>(
mem_heap_zalloc(heap, sizeof(fts_sync_t)));
@ -649,10 +668,8 @@ fts_cache_create(
fts_cache_init(cache);
/* Create stopword RB tree. The stopword tree will
remain in cache for the duration of FTS cache's lifetime */
cache->stopword_info.cached_stopword = rbt_create(
sizeof(fts_tokenizer_word_t), fts_utf8_string_cmp);
cache->stopword_info.cached_stopword = NULL;
cache->stopword_info.charset = NULL;
cache->stopword_info.heap = cache->self_heap;
@ -922,6 +939,8 @@ fts_que_graph_free_check_lock(
mutex_enter(&dict_sys->mutex);
}
ut_ad(mutex_own(&dict_sys->mutex));
que_graph_free(graph);
if (!has_dict) {
@ -1199,7 +1218,10 @@ fts_cache_destroy(
mutex_free(&cache->optimize_lock);
mutex_free(&cache->deleted_lock);
mutex_free(&cache->doc_id_lock);
if (cache->stopword_info.cached_stopword) {
rbt_free(cache->stopword_info.cached_stopword);
}
if (cache->sync_heap->arg) {
mem_heap_free(static_cast<mem_heap_t*>(cache->sync_heap->arg));
@ -1500,6 +1522,112 @@ fts_drop_table(
return(error);
}
/****************************************************************//**
Rename a single auxiliary table due to database name change.
@return DB_SUCCESS or error code */
static __attribute__((nonnull, warn_unused_result))
dberr_t
fts_rename_one_aux_table(
/*=====================*/
const char* new_name, /*!< in: new parent tbl name */
const char* fts_table_old_name, /*!< in: old aux tbl name */
trx_t* trx) /*!< in: transaction */
{
char fts_table_new_name[MAX_TABLE_NAME_LEN];
ulint new_db_name_len = dict_get_db_name_len(new_name);
ulint old_db_name_len = dict_get_db_name_len(fts_table_old_name);
ulint table_new_name_len = strlen(fts_table_old_name)
+ new_db_name_len - old_db_name_len;
/* Check if the new and old database names are the same, if so,
nothing to do */
ut_ad((new_db_name_len != old_db_name_len)
|| strncmp(new_name, fts_table_old_name, old_db_name_len) != 0);
/* Get the database name from "new_name", and table name
from the fts_table_old_name */
strncpy(fts_table_new_name, new_name, new_db_name_len);
strncpy(fts_table_new_name + new_db_name_len,
strchr(fts_table_old_name, '/'),
table_new_name_len - new_db_name_len);
fts_table_new_name[table_new_name_len] = 0;
return(row_rename_table_for_mysql(
fts_table_old_name, fts_table_new_name, trx, false));
}
/****************************************************************//**
Rename auxiliary tables for all fts index for a table. This(rename)
is due to database name change
@return DB_SUCCESS or error code */
dberr_t
fts_rename_aux_tables(
/*==================*/
dict_table_t* table, /*!< in: user Table */
const char* new_name, /*!< in: new table name */
trx_t* trx) /*!< in: transaction */
{
ulint i;
fts_table_t fts_table;
FTS_INIT_FTS_TABLE(&fts_table, NULL, FTS_COMMON_TABLE, table);
/* Rename common auxiliary tables */
for (i = 0; fts_common_tables[i] != NULL; ++i) {
char* old_table_name;
dberr_t err = DB_SUCCESS;
fts_table.suffix = fts_common_tables[i];
old_table_name = fts_get_table_name(&fts_table);
err = fts_rename_one_aux_table(new_name, old_table_name, trx);
mem_free(old_table_name);
if (err != DB_SUCCESS) {
return(err);
}
}
fts_t* fts = table->fts;
/* Rename index specific auxiliary tables */
for (i = 0; fts->indexes != 0 && i < ib_vector_size(fts->indexes);
++i) {
dict_index_t* index;
index = static_cast<dict_index_t*>(
ib_vector_getp(fts->indexes, i));
FTS_INIT_INDEX_TABLE(&fts_table, NULL, FTS_INDEX_TABLE, index);
for (ulint j = 0; fts_index_selector[j].value; ++j) {
dberr_t err;
char* old_table_name;
fts_table.suffix = fts_get_suffix(j);
old_table_name = fts_get_table_name(&fts_table);
err = fts_rename_one_aux_table(
new_name, old_table_name, trx);
DBUG_EXECUTE_IF("fts_rename_failure",
err = DB_DEADLOCK;);
mem_free(old_table_name);
if (err != DB_SUCCESS) {
return(err);
}
}
}
return(DB_SUCCESS);
}
/****************************************************************//**
Drops the common ancillary tables needed for supporting an FTS index
on the given table. row_mysql_lock_data_dictionary must have been called
@ -1586,13 +1714,15 @@ fts_drop_index_tables(
trx_t* trx, /*!< in: transaction */
dict_index_t* index) /*!< in: Index to drop */
{
fts_table_t fts_table;
dberr_t error = DB_SUCCESS;
#ifdef FTS_DOC_STATS_DEBUG
fts_table_t fts_table;
static const char* index_tables[] = {
"DOC_ID",
NULL
};
#endif /* FTS_DOC_STATS_DEBUG */
dberr_t err = fts_drop_index_split_tables(trx, index);
@ -1601,6 +1731,7 @@ fts_drop_index_tables(
error = err;
}
#ifdef FTS_DOC_STATS_DEBUG
FTS_INIT_INDEX_TABLE(&fts_table, NULL, FTS_INDEX_TABLE, index);
for (ulint i = 0; index_tables[i] != NULL; ++i) {
@ -1619,6 +1750,7 @@ fts_drop_index_tables(
mem_free(table_name);
}
#endif /* FTS_DOC_STATS_DEBUG */
return(error);
}
@ -1884,7 +2016,6 @@ fts_create_index_tables_low(
{
ulint i;
char* sql;
que_t* graph;
fts_table_t fts_table;
dberr_t error = DB_SUCCESS;
@ -1896,6 +2027,9 @@ fts_create_index_tables_low(
fts_table.parent = table_name;
fts_table.table = NULL;
#ifdef FTS_DOC_STATS_DEBUG
char* sql;
/* Create the FTS auxiliary tables that are specific
to an FTS index. */
sql = fts_prepare_sql(&fts_table, fts_create_index_tables_sql);
@ -1905,6 +2039,7 @@ fts_create_index_tables_low(
error = fts_eval_sql(trx, graph);
que_graph_free(graph);
#endif /* FTS_DOC_STATS_DEBUG */
for (i = 0; fts_index_selector[i].value && error == DB_SUCCESS; ++i) {
dict_table_t* new_table;
@ -2501,12 +2636,14 @@ fts_get_next_doc_id(
/* Otherwise, simply increment the value in cache */
mutex_enter(&cache->doc_id_lock);
++cache->next_doc_id;
*doc_id = ++cache->next_doc_id;
mutex_exit(&cache->doc_id_lock);
} else {
mutex_enter(&cache->doc_id_lock);
*doc_id = cache->next_doc_id;
mutex_exit(&cache->doc_id_lock);
}
*doc_id = cache->next_doc_id;
return(DB_SUCCESS);
}
@ -2555,7 +2692,7 @@ retry:
graph = fts_parse_sql(
&fts_table, info,
"DECLARE FUNCTION my_func;\n"
"DECLARE CURSOR c IS SELECT value FROM %s"
"DECLARE CURSOR c IS SELECT value FROM \"%s\""
" WHERE key = 'synced_doc_id' FOR UPDATE;\n"
"BEGIN\n"
""
@ -2841,7 +2978,7 @@ fts_delete(
graph = fts_parse_sql(
&fts_table,
info,
"BEGIN INSERT INTO %s VALUES (:doc_id);");
"BEGIN INSERT INTO \"%s\" VALUES (:doc_id);");
error = fts_eval_sql(trx, graph);
@ -3404,7 +3541,13 @@ fts_add_doc_by_id(
rw_lock_x_unlock(&table->fts->cache->lock);
if (cache->total_size > fts_max_cache_size) {
DBUG_EXECUTE_IF(
"fts_instrument_sync",
fts_sync(cache->sync);
);
if (cache->total_size > fts_max_cache_size
|| fts_need_sync) {
fts_sync(cache->sync);
}
@ -3492,7 +3635,7 @@ fts_get_max_doc_id(
btr_pcur_open_at_index_side(
false, index, BTR_SEARCH_LEAF, &pcur, true, 0, &mtr);
if (page_get_n_recs(btr_pcur_get_page(&pcur)) > 0) {
if (!page_is_empty(btr_pcur_get_page(&pcur))) {
const rec_t* rec = NULL;
ulint offsets_[REC_OFFS_NORMAL_SIZE];
ulint* offsets = offsets_;
@ -3711,7 +3854,7 @@ fts_write_node(
fts_table,
info,
"BEGIN\n"
"INSERT INTO %s VALUES "
"INSERT INTO \"%s\" VALUES "
"(:token, :first_doc_id,"
" :last_doc_id, :doc_count, :ilist);");
}
@ -3756,7 +3899,7 @@ fts_sync_add_deleted_cache(
graph = fts_parse_sql(
&fts_table,
info,
"BEGIN INSERT INTO %s VALUES (:doc_id);");
"BEGIN INSERT INTO \"%s\" VALUES (:doc_id);");
for (i = 0; i < n_elems && error == DB_SUCCESS; ++i) {
fts_update_t* update;
@ -3937,7 +4080,7 @@ fts_sync_write_doc_stat(
*graph = fts_parse_sql(
&fts_table,
info,
"BEGIN INSERT INTO %s VALUES (:doc_id, :count);");
"BEGIN INSERT INTO \"%s\" VALUES (:doc_id, :count);");
}
for (;;) {
@ -4303,6 +4446,10 @@ fts_sync(
}
}
DBUG_EXECUTE_IF("fts_instrument_sync_interrupted",
sync->interrupted = true;
);
if (error == DB_SUCCESS && !sync->interrupted) {
error = fts_sync_commit(sync);
} else {
@ -4553,7 +4700,7 @@ fts_get_docs_clear(
}
/*********************************************************************//**
Get the initial Doc ID by consulting the ADDED and the CONFIG table
Get the initial Doc ID by consulting the CONFIG table
@return initial Doc ID */
UNIV_INTERN
doc_id_t
@ -4656,7 +4803,7 @@ fts_get_rows_count(
"DECLARE FUNCTION my_func;\n"
"DECLARE CURSOR c IS"
" SELECT COUNT(*) "
" FROM %s;\n"
" FROM \"%s\";\n"
"BEGIN\n"
"\n"
"OPEN c;\n"
@ -4892,20 +5039,20 @@ fts_get_doc_id_from_rec(
ulint len;
const byte* data;
ulint col_no;
ulint* offsets;
doc_id_t doc_id = 0;
dict_index_t* clust_index;
ulint offsets_[REC_OFFS_NORMAL_SIZE];
ulint* offsets = offsets_;
mem_heap_t* my_heap = heap;
ut_a(table->fts->doc_col != ULINT_UNDEFINED);
offsets = offsets_;
clust_index = dict_table_get_first_index(table);
offsets_[0] = UT_ARR_SIZE(offsets_);
rec_offs_init(offsets_);
offsets = rec_get_offsets(
rec, clust_index, offsets, ULINT_UNDEFINED, &heap);
rec, clust_index, offsets, ULINT_UNDEFINED, &my_heap);
col_no = dict_col_get_clust_pos(
&table->cols[table->fts->doc_col], clust_index);
@ -4917,6 +5064,10 @@ fts_get_doc_id_from_rec(
ut_ad(8 == sizeof(doc_id));
doc_id = static_cast<doc_id_t>(mach_read_from_8(data));
if (my_heap && !heap) {
mem_heap_free(my_heap);
}
return(doc_id);
}
@ -5794,7 +5945,7 @@ fts_check_and_drop_orphaned_tables(
ib_vector_get(tables, i));
table = dict_table_open_on_id(
aux_table->parent_id, TRUE, FALSE);
aux_table->parent_id, TRUE, DICT_TABLE_OP_NORMAL);
if (table == NULL || table->fts == NULL) {
@ -5844,7 +5995,8 @@ fts_check_and_drop_orphaned_tables(
path = fil_make_ibd_name(
aux_table->name, false);
os_file_delete_if_exists(path);
os_file_delete_if_exists(innodb_file_data_key,
path);
mem_free(path);
}
@ -5995,18 +6147,19 @@ fts_drop_orphaned_tables(void)
/**********************************************************************//**
Check whether user supplied stopword table is of the right format.
Caller is responsible to hold dictionary locks.
@return TRUE if the table qualifies */
@return the stopword column charset if qualifies */
UNIV_INTERN
ibool
CHARSET_INFO*
fts_valid_stopword_table(
/*=====================*/
const char* stopword_table_name) /*!< in: Stopword table
name */
{
dict_table_t* table;
dict_col_t* col = NULL;
if (!stopword_table_name) {
return(FALSE);
return(NULL);
}
table = dict_table_get_low(stopword_table_name);
@ -6016,9 +6169,8 @@ fts_valid_stopword_table(
"InnoDB: user stopword table %s does not exist.\n",
stopword_table_name);
return(FALSE);
return(NULL);
} else {
dict_col_t* col;
const char* col_name;
col_name = dict_table_get_col_name(table, 0);
@ -6029,22 +6181,27 @@ fts_valid_stopword_table(
"table %s. Its first column must be named as "
"'value'.\n", stopword_table_name);
return(FALSE);
return(NULL);
}
col = dict_table_get_nth_col(table, 0);
if (col->mtype != DATA_VARCHAR) {
if (col->mtype != DATA_VARCHAR
&& col->mtype != DATA_VARMYSQL) {
fprintf(stderr,
"InnoDB: invalid column type for stopword "
"table %s. Its first column must be of "
"varchar type\n", stopword_table_name);
return(FALSE);
return(NULL);
}
}
return(TRUE);
ut_ad(col);
return(innobase_get_fts_charset(
static_cast<int>(col->prtype & DATA_MYSQL_TYPE_MASK),
static_cast<ulint>(dtype_get_charset_coll(col->prtype))));
}
/**********************************************************************//**
@ -6109,7 +6266,7 @@ fts_load_stopword(
}
/* If stopword is turned off, no need to continue to load the
stopword into cache */
stopword into cache, but still need to do initialization */
if (!use_stopword) {
cache->stopword_info.status = STOPWORD_OFF;
goto cleanup;
@ -6166,6 +6323,11 @@ cleanup:
trx_free_for_background(trx);
}
if (!cache->stopword_info.cached_stopword) {
cache->stopword_info.cached_stopword = rbt_create(
sizeof(fts_tokenizer_word_t), fts_utf8_string_cmp);
}
return(error == DB_SUCCESS);
}
@ -6329,7 +6491,6 @@ fts_init_index(
dict_index_t* index;
doc_id_t start_doc;
fts_get_doc_t* get_doc = NULL;
ibool has_fts = TRUE;
fts_cache_t* cache = table->fts->cache;
bool need_init = false;
@ -6367,11 +6528,15 @@ fts_init_index(
ut_a(index);
has_fts = FALSE;
fts_doc_fetch_by_doc_id(NULL, start_doc, index,
FTS_FETCH_DOC_BY_ID_LARGE,
fts_init_get_doc_id, cache);
} else {
if (table->fts->cache->stopword_info.status
& STOPWORD_NOT_INIT) {
fts_load_stopword(table, NULL, NULL, NULL, TRUE, TRUE);
}
for (ulint i = 0; i < ib_vector_size(cache->get_docs); ++i) {
get_doc = static_cast<fts_get_doc_t*>(
ib_vector_get(cache->get_docs, i));
@ -6384,13 +6549,6 @@ fts_init_index(
}
}
if (has_fts) {
if (table->fts->cache->stopword_info.status
& STOPWORD_NOT_INIT) {
fts_load_stopword(table, NULL, NULL, NULL, TRUE, TRUE);
}
}
table->fts->fts_status |= ADDED_TABLE_SYNCED;
fts_get_docs_clear(cache->get_docs);

View file

@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 2007, 2012, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2007, 2013, Oracle and/or its affiliates. All Rights Reserved.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -54,6 +54,9 @@ static const ulint FTS_OPTIMIZE_INTERVAL_IN_SECS = 300;
/** Server is shutting down, so does we exiting the optimize thread */
static bool fts_opt_start_shutdown = false;
/** Last time we did check whether system need a sync */
static ib_time_t last_check_sync_time;
#if 0
/** Check each table in round robin to see whether they'd
need to be "optimized" */
@ -242,22 +245,22 @@ static const char* fts_init_delete_sql =
"BEGIN\n"
"\n"
"INSERT INTO %s_BEING_DELETED\n"
"SELECT doc_id FROM %s_DELETED;\n"
"SELECT doc_id FROM \"%s_DELETED\";\n"
"\n"
"INSERT INTO %s_BEING_DELETED_CACHE\n"
"SELECT doc_id FROM %s_DELETED_CACHE;\n";
"SELECT doc_id FROM \"%s_DELETED_CACHE\";\n";
static const char* fts_delete_doc_ids_sql =
"BEGIN\n"
"\n"
"DELETE FROM %s_DELETED WHERE doc_id = :doc_id1;\n"
"DELETE FROM %s_DELETED_CACHE WHERE doc_id = :doc_id2;\n";
"DELETE FROM \"%s_DELETED\" WHERE doc_id = :doc_id1;\n"
"DELETE FROM \"%s_DELETED_CACHE\" WHERE doc_id = :doc_id2;\n";
static const char* fts_end_delete_sql =
"BEGIN\n"
"\n"
"DELETE FROM %s_BEING_DELETED;\n"
"DELETE FROM %s_BEING_DELETED_CACHE;\n";
"DELETE FROM \"%s_BEING_DELETED\";\n"
"DELETE FROM \"%s_BEING_DELETED_CACHE\";\n";
/**********************************************************************//**
Initialize fts_zip_t. */
@ -500,7 +503,7 @@ fts_index_fetch_nodes(
"DECLARE CURSOR c IS"
" SELECT word, doc_count, first_doc_id, last_doc_id, "
"ilist\n"
" FROM %s\n"
" FROM \"%s\"\n"
" WHERE word LIKE :word\n"
" ORDER BY first_doc_id;\n"
"BEGIN\n"
@ -824,7 +827,7 @@ fts_index_fetch_words(
"DECLARE FUNCTION my_func;\n"
"DECLARE CURSOR c IS"
" SELECT word\n"
" FROM %s\n"
" FROM \"%s\"\n"
" WHERE word > :word\n"
" ORDER BY word;\n"
"BEGIN\n"
@ -984,7 +987,7 @@ fts_table_fetch_doc_ids(
info,
"DECLARE FUNCTION my_func;\n"
"DECLARE CURSOR c IS"
" SELECT doc_id FROM %s;\n"
" SELECT doc_id FROM \"%s\";\n"
"BEGIN\n"
"\n"
"OPEN c;\n"
@ -1457,7 +1460,7 @@ fts_optimize_write_word(
graph = fts_parse_sql(
fts_table,
info,
"BEGIN DELETE FROM %s WHERE word = :word;");
"BEGIN DELETE FROM \"%s\" WHERE word = :word;");
error = fts_eval_sql(trx, graph);
@ -2813,6 +2816,43 @@ fts_optimize_how_many(
return(n_tables);
}
/**********************************************************************//**
Check if the total memory used by all FTS table exceeds the maximum limit.
@return true if a sync is needed, false otherwise */
static
bool
fts_is_sync_needed(
/*===============*/
const ib_vector_t* tables) /*!< in: registered tables
vector*/
{
ulint total_memory = 0;
double time_diff = difftime(ut_time(), last_check_sync_time);
if (fts_need_sync || time_diff < 5) {
return(false);
}
last_check_sync_time = ut_time();
for (ulint i = 0; i < ib_vector_size(tables); ++i) {
const fts_slot_t* slot;
slot = static_cast<const fts_slot_t*>(
ib_vector_get_const(tables, i));
if (slot->table && slot->table->fts) {
total_memory += slot->table->fts->cache->total_size;
}
if (total_memory > fts_max_total_cache_size) {
return(true);
}
}
return(false);
}
#if 0
/*********************************************************************//**
Check whether a table needs to be optimized. */
@ -2933,6 +2973,10 @@ fts_optimize_thread(
/* Timeout ? */
if (msg == NULL) {
if (fts_is_sync_needed(tables)) {
fts_need_sync = true;
}
continue;
}
@ -3055,6 +3099,7 @@ fts_optimize_init(void)
fts_optimize_wq = ib_wqueue_create();
ut_a(fts_optimize_wq != NULL);
last_check_sync_time = ut_time();
os_thread_create(fts_optimize_thread, fts_optimize_wq, NULL);
}

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 2007, 2011, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2007, 2013, Oracle and/or its affiliates. All Rights Reserved.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -193,6 +193,10 @@ term : FTS_TERM {
free($1);
}
/* Ignore leading '*' */
| '*' term {
$$ = $2;
}
;
text : FTS_TEXT {

File diff suppressed because it is too large Load diff

View file

@ -305,9 +305,9 @@ YY_BUFFER_STATE fts0t_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner
YY_BUFFER_STATE fts0t_scan_string (yyconst char *yy_str ,yyscan_t yyscanner );
YY_BUFFER_STATE fts0t_scan_bytes (yyconst char *bytes,int len ,yyscan_t yyscanner );
void *fts0talloc (yy_size_t , yyscan_t yyscanner __attribute__((unused)) );
void *fts0trealloc (void *,yy_size_t , yyscan_t yyscanner __attribute__((unused)) );
void fts0tfree (void * , yyscan_t yyscanner __attribute__((unused)) );
void *fts0talloc (yy_size_t , yyscan_t yyscanner __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) );
void *fts0trealloc (void *,yy_size_t , yyscan_t yyscanner __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) );
void fts0tfree (void * , yyscan_t yyscanner __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) );
#define yy_new_buffer fts0t_create_buffer
@ -347,7 +347,7 @@ typedef int yy_state_type;
static yy_state_type yy_get_previous_state (yyscan_t yyscanner );
static yy_state_type yy_try_NUL_trans (yy_state_type current_state ,yyscan_t yyscanner);
static int yy_get_next_buffer (yyscan_t yyscanner );
static void yy_fatal_error (yyconst char msg[] , yyscan_t yyscanner __attribute__((unused)) );
static void yy_fatal_error (yyconst char msg[] , yyscan_t yyscanner __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) );
/* Done after the current pattern has been matched and before the
* corresponding action - sets up yytext.
@ -359,8 +359,8 @@ static void yy_fatal_error (yyconst char msg[] , yyscan_t yyscanner __attribute_
*yy_cp = '\0'; \
yyg->yy_c_buf_p = yy_cp;
#define YY_NUM_RULES 6
#define YY_END_OF_BUFFER 7
#define YY_NUM_RULES 7
#define YY_END_OF_BUFFER 8
/* This struct is not used in this scanner,
but its presence is necessary. */
struct yy_trans_info
@ -370,7 +370,7 @@ struct yy_trans_info
};
static yyconst flex_int16_t yy_accept[17] =
{ 0,
4, 4, 7, 4, 1, 5, 1, 6, 6, 2,
4, 4, 8, 4, 1, 6, 1, 5, 5, 2,
4, 1, 1, 0, 3, 0
} ;
@ -575,11 +575,11 @@ extern int fts0twrap (yyscan_t yyscanner );
#endif
#ifndef yytext_ptr
static void yy_flex_strncpy (char *,yyconst char *,int , yyscan_t yyscanner __attribute__((unused)));
static void yy_flex_strncpy (char *,yyconst char *,int , yyscan_t yyscanner __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)));
#endif
#ifdef YY_NEED_STRLEN
static int yy_flex_strlen (yyconst char * , yyscan_t yyscanner __attribute__((unused)));
static int yy_flex_strlen (yyconst char * , yyscan_t yyscanner __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)));
#endif
#ifndef YY_NO_INPUT
@ -816,17 +816,22 @@ YY_RULE_SETUP
}
YY_BREAK
case 5:
/* rule 5 can match eol */
YY_RULE_SETUP
#line 65 "fts0tlex.l"
;
YY_BREAK
case 6:
/* rule 6 can match eol */
YY_RULE_SETUP
#line 66 "fts0tlex.l"
YY_BREAK
case 6:
case 7:
YY_RULE_SETUP
#line 68 "fts0tlex.l"
ECHO;
YY_BREAK
#line 829 "fts0tlex.cc"
#line 834 "fts0tlex.cc"
case YY_STATE_EOF(INITIAL):
yyterminate();
@ -1596,7 +1601,7 @@ YY_BUFFER_STATE fts0t_scan_bytes (yyconst char * yybytes, int _yybytes_len , y
#define YY_EXIT_FAILURE 2
#endif
static void yy_fatal_error (yyconst char* msg , yyscan_t yyscanner __attribute__((unused)))
static void yy_fatal_error (yyconst char* msg , yyscan_t yyscanner __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)))
{
(void) fprintf( stderr, "%s\n", msg );
exit( YY_EXIT_FAILURE );
@ -1897,7 +1902,7 @@ int fts0tlex_destroy (yyscan_t yyscanner)
*/
#ifndef yytext_ptr
static void yy_flex_strncpy (char* s1, yyconst char * s2, int n , yyscan_t yyscanner __attribute__((unused)))
static void yy_flex_strncpy (char* s1, yyconst char * s2, int n , yyscan_t yyscanner __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)))
{
register int i;
for ( i = 0; i < n; ++i )
@ -1906,7 +1911,7 @@ static void yy_flex_strncpy (char* s1, yyconst char * s2, int n , yyscan_t yysc
#endif
#ifdef YY_NEED_STRLEN
static int yy_flex_strlen (yyconst char * s , yyscan_t yyscanner __attribute__((unused)))
static int yy_flex_strlen (yyconst char * s , yyscan_t yyscanner __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)))
{
register int n;
for ( n = 0; s[n]; ++n )
@ -1916,12 +1921,12 @@ static int yy_flex_strlen (yyconst char * s , yyscan_t yyscanner __attribute__(
}
#endif
void *fts0talloc (yy_size_t size , yyscan_t yyscanner __attribute__((unused)))
void *fts0talloc (yy_size_t size , yyscan_t yyscanner __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)))
{
return (void *) malloc( size );
}
void *fts0trealloc (void * ptr, yy_size_t size , yyscan_t yyscanner __attribute__((unused)))
void *fts0trealloc (void * ptr, yy_size_t size , yyscan_t yyscanner __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)))
{
/* The cast to (char *) in the following accommodates both
* implementations that use char* generic pointers, and those
@ -1933,7 +1938,7 @@ void *fts0trealloc (void * ptr, yy_size_t size , yyscan_t yyscanner __attribu
return (void *) realloc( (char *) ptr, size );
}
void fts0tfree (void * ptr , yyscan_t yyscanner __attribute__((unused)))
void fts0tfree (void * ptr , yyscan_t yyscanner __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)) __attribute__((unused)))
{
free( (char *) ptr ); /* see fts0trealloc() for (char *) cast */
}

View file

@ -62,7 +62,7 @@ this program; if not, write to the Free Software Foundation, Inc.,
return(FTS_TERM);
}
. ;
\n
%%

View file

@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
Copyright (c) 2008, 2009 Google Inc.
Copyright (c) 2009, Percona Inc.
Copyright (c) 2012, Facebook Inc.
@ -154,8 +154,6 @@ static uint innobase_old_blocks_pct;
of the buffer pool. */
static uint innobase_change_buffer_max_size = CHANGE_BUFFER_DEFAULT_SIZE;
static ulong innobase_compression_level = DEFAULT_COMPRESSION_LEVEL;
/* The default values for the following char* start-up parameters
are determined in innobase_init below: */
@ -396,7 +394,7 @@ static PSI_rwlock_info all_innodb_rwlocks[] = {
{&index_tree_rw_lock_key, "index_tree_rw_lock", 0},
{&index_online_log_key, "index_online_log", 0},
{&dict_table_stats_latch_key, "dict_table_stats", 0},
{&hash_table_rw_lock_key, "hash table locks", 0}
{&hash_table_rw_lock_key, "hash_table_locks", 0}
};
# endif /* UNIV_PFS_RWLOCK */
@ -413,7 +411,7 @@ static PSI_thread_info all_innodb_threads[] = {
{&srv_master_thread_key, "srv_master_thread", 0},
{&srv_purge_thread_key, "srv_purge_thread", 0},
{&buf_page_cleaner_thread_key, "page_cleaner_thread", 0},
{&recv_writer_thread_key, "recovery writer thread", 0}
{&recv_writer_thread_key, "recv_writer_thread", 0}
};
# endif /* UNIV_PFS_THREAD */
@ -453,10 +451,18 @@ ib_cb_t innodb_api_cb[] = {
(ib_cb_t) ib_clust_read_tuple_create,
(ib_cb_t) ib_tuple_delete,
(ib_cb_t) ib_tuple_copy,
(ib_cb_t) ib_tuple_read_u8,
(ib_cb_t) ib_tuple_write_u8,
(ib_cb_t) ib_tuple_read_u16,
(ib_cb_t) ib_tuple_write_u16,
(ib_cb_t) ib_tuple_read_u32,
(ib_cb_t) ib_tuple_write_u32,
(ib_cb_t) ib_tuple_read_u64,
(ib_cb_t) ib_tuple_write_u64,
(ib_cb_t) ib_tuple_read_i8,
(ib_cb_t) ib_tuple_write_i8,
(ib_cb_t) ib_tuple_read_i16,
(ib_cb_t) ib_tuple_write_i16,
(ib_cb_t) ib_tuple_read_i32,
(ib_cb_t) ib_tuple_write_i32,
(ib_cb_t) ib_tuple_read_i64,
@ -511,6 +517,15 @@ innodb_stopword_table_validate(
system clustered index when there is no primary key. */
const char innobase_index_reserve_name[] = "GEN_CLUST_INDEX";
/******************************************************************//**
Maps a MySQL trx isolation level code to the InnoDB isolation level code
@return InnoDB isolation level */
static inline
ulint
innobase_map_isolation_level(
/*=========================*/
enum_tx_isolation iso); /*!< in: MySQL isolation level code */
/******************************************************************//**
Maps a MySQL trx isolation level code to the InnoDB isolation level code
@return InnoDB isolation level */
@ -1506,7 +1521,8 @@ convert_error_code_to_mysql(
case DB_FTS_INVALID_DOCID:
return(HA_FTS_INVALID_DOCID);
case DB_FTS_EXCEED_RESULT_CACHE_LIMIT:
return(HA_ERR_OUT_OF_MEM);
case DB_TOO_MANY_CONCURRENT_TRXS:
return(HA_ERR_TOO_MANY_CONCURRENT_TRXS);
case DB_UNSUPPORTED:
@ -1519,6 +1535,8 @@ convert_error_code_to_mysql(
return(HA_ERR_OUT_OF_MEM);
case DB_TABLESPACE_EXISTS:
return(HA_ERR_TABLESPACE_EXISTS);
case DB_IDENTIFIER_TOO_LONG:
return(HA_ERR_INTERNAL_ERROR);
}
}
@ -1611,6 +1629,31 @@ innobase_convert_from_table_id(
strconvert(cs, from, FN_REFLEN, &my_charset_filename, to, (uint) len, &errors);
}
/**********************************************************************
Check if the length of the identifier exceeds the maximum allowed.
return true when length of identifier is too long. */
UNIV_INTERN
my_bool
innobase_check_identifier_length(
/*=============================*/
const char* id) /* in: FK identifier to check excluding the
database portion. */
{
int well_formed_error = 0;
CHARSET_INFO *cs = system_charset_info;
DBUG_ENTER("innobase_check_identifier_length");
uint res = cs->cset->well_formed_len(cs, id, id + strlen(id),
NAME_CHAR_LEN,
&well_formed_error);
if (well_formed_error || res == NAME_CHAR_LEN) {
my_error(ER_TOO_LONG_IDENT, MYF(0), id);
DBUG_RETURN(true);
}
DBUG_RETURN(false);
}
/******************************************************************//**
Converts an identifier to UTF-8. */
UNIV_INTERN
@ -1754,9 +1797,14 @@ innobase_mysql_tmpfile(void)
/*========================*/
{
int fd2 = -1;
File fd = mysql_tmpfile("ib");
File fd;
DBUG_EXECUTE_IF("innobase_tmpfile_creation_failure", return(-1););
DBUG_EXECUTE_IF(
"innobase_tmpfile_creation_failure",
return(-1);
);
fd = mysql_tmpfile("ib");
if (fd >= 0) {
/* Copy the file descriptor, so that the additional resources
@ -2125,12 +2173,12 @@ void
innobase_copy_frm_flags_from_create_info(
/*=====================================*/
dict_table_t* innodb_table, /*!< in/out: InnoDB table */
HA_CREATE_INFO* create_info) /*!< in: create info */
const HA_CREATE_INFO* create_info) /*!< in: create info */
{
ibool ps_on;
ibool ps_off;
if (dict_table_is_temporary(innodb_table) || srv_read_only_mode) {
if (dict_table_is_temporary(innodb_table)) {
/* Temp tables do not use persistent stats. */
ps_on = FALSE;
ps_off = TRUE;
@ -2161,12 +2209,12 @@ void
innobase_copy_frm_flags_from_table_share(
/*=====================================*/
dict_table_t* innodb_table, /*!< in/out: InnoDB table */
TABLE_SHARE* table_share) /*!< in: table share */
const TABLE_SHARE* table_share) /*!< in: table share */
{
ibool ps_on;
ibool ps_off;
if (dict_table_is_temporary(innodb_table) || srv_read_only_mode) {
if (dict_table_is_temporary(innodb_table)) {
/* Temp tables do not use persistent stats */
ps_on = FALSE;
ps_off = TRUE;
@ -2229,6 +2277,10 @@ ha_innobase::update_thd(
{
trx_t* trx;
DBUG_ENTER("ha_innobase::update_thd");
DBUG_PRINT("ha_innobase::update_thd", ("user_thd: %p -> %p",
user_thd, thd));
/* The table should have been opened in ha_innobase::open(). */
DBUG_ASSERT(prebuilt->table->n_ref_count > 0);
@ -2240,6 +2292,7 @@ ha_innobase::update_thd(
}
user_thd = thd;
DBUG_VOID_RETURN;
}
/*********************************************************************//**
@ -2479,21 +2532,18 @@ innobase_convert_identifier(
ibool file_id)/*!< in: TRUE=id is a table or database name;
FALSE=id is an UTF-8 string */
{
char nz[NAME_LEN + 1];
char nz2[NAME_LEN + 1 + EXPLAIN_FILENAME_MAX_EXTRA_LENGTH];
const char* s = id;
int q;
if (file_id) {
char nz[MAX_TABLE_NAME_LEN + 1];
char nz2[MAX_TABLE_NAME_LEN + 1];
/* Decode the table name. The MySQL function expects
a NUL-terminated string. The input and output strings
buffers must not be shared. */
if (UNIV_UNLIKELY(idlen > (sizeof nz) - 1)) {
idlen = (sizeof nz) - 1;
}
ut_a(idlen <= MAX_TABLE_NAME_LEN);
memcpy(nz, id, idlen);
nz[idlen] = 0;
@ -2752,6 +2802,7 @@ ha_innobase::init_table_handle_for_HANDLER(void)
Gives the file extension of an InnoDB single-table tablespace. */
static const char* ha_innobase_exts[] = {
".ibd",
".isl",
NullS
};
@ -2922,14 +2973,33 @@ mem_free_and_error:
srv_normalize_path_for_win(srv_log_group_home_dir);
if (strchr(srv_log_group_home_dir, ';')
|| innobase_mirrored_log_groups != 1) {
sql_print_error("syntax error in innodb_log_group_home_dir, "
"or a wrong number of mirrored log groups");
if (strchr(srv_log_group_home_dir, ';')) {
sql_print_error("syntax error in innodb_log_group_home_dir");
goto mem_free_and_error;
}
if (innobase_mirrored_log_groups == 1) {
sql_print_warning(
"innodb_mirrored_log_groups is an unimplemented "
"feature and the variable will be completely "
"removed in a future version.");
}
if (innobase_mirrored_log_groups > 1) {
sql_print_error(
"innodb_mirrored_log_groups is an unimplemented feature and "
"the variable will be completely removed in a future version. "
"Using values other than 1 is not supported.");
goto mem_free_and_error;
}
if (innobase_mirrored_log_groups == 0) {
/* To throw a deprecation warning message when the option is
passed, the default was changed to '0' (as a workaround). Since
the only value accepted for this option is '1', reset it to 1 */
innobase_mirrored_log_groups = 1;
}
/* Validate the file format by animal name */
if (innobase_file_format_name != NULL) {
@ -3134,8 +3204,6 @@ innobase_change_buffering_inited_ok:
srv_use_doublewrite_buf = (ibool) innobase_use_doublewrite;
page_compression_level = (ulint) innobase_compression_level;
if (!innobase_use_checksums) {
ut_print_timestamp(stderr);
fprintf(stderr,
@ -3366,6 +3434,9 @@ innobase_end(
mysql_mutex_destroy(&pending_checkpoint_mutex);
}
my_free(fts_server_stopword_table);
fts_server_stopword_table= NULL;
DBUG_RETURN(err);
}
@ -4909,10 +4980,10 @@ ha_innobase::open(
dict_table_t* ib_table;
char norm_name[FN_REFLEN];
THD* thd;
ulint retries = 0;
char* is_part = NULL;
ibool par_case_name_set = FALSE;
char par_case_name[FN_REFLEN];
dict_err_ignore_t ignore_err = DICT_ERR_IGNORE_NONE;
DBUG_ENTER("ha_innobase::open");
@ -4942,20 +5013,22 @@ ha_innobase::open(
upd_buf_size = 0;
/* We look for pattern #P# to see if the table is partitioned
MySQL table. The retry logic for partitioned tables is a
workaround for http://bugs.mysql.com/bug.php?id=33349. Look
at support issue https://support.mysql.com/view.php?id=21080
for more details. */
MySQL table. */
#ifdef __WIN__
is_part = strstr(norm_name, "#p#");
#else
is_part = strstr(norm_name, "#P#");
#endif /* __WIN__ */
retry:
/* Check whether FOREIGN_KEY_CHECKS is set to 0. If so, the table
can be opened even if some FK indexes are missing. If not, the table
can't be opened in the same situation */
if (thd_test_options(thd, OPTION_NO_FOREIGN_KEY_CHECKS)) {
ignore_err = DICT_ERR_IGNORE_FK_NOKEY;
}
/* Get pointer to a table object in InnoDB dictionary cache */
ib_table = dict_table_open_on_name(norm_name, FALSE, TRUE,
DICT_ERR_IGNORE_NONE);
ib_table = dict_table_open_on_name(norm_name, FALSE, TRUE, ignore_err);
if (ib_table
&& ((!DICT_TF2_FLAG_IS_SET(ib_table, DICT_TF2_FTS_HAS_DOC_ID)
@ -4981,7 +5054,7 @@ retry:
}
if (NULL == ib_table) {
if (is_part && retries < 10) {
if (is_part) {
/* MySQL partition engine hard codes the file name
separator as "#P#". The text case is fixed even if
lower_case_table_names is set to 1 or 2. This is true
@ -5021,14 +5094,10 @@ retry:
ib_table = dict_table_open_on_name(
par_case_name, FALSE, TRUE,
DICT_ERR_IGNORE_NONE);
ignore_err);
}
if (!ib_table) {
++retries;
os_thread_sleep(100000);
goto retry;
} else {
if (ib_table) {
#ifndef __WIN__
sql_print_warning("Partition table %s opened "
"after converting to lower "
@ -5054,9 +5123,8 @@ retry:
}
if (is_part) {
sql_print_error("Failed to open table %s after "
"%lu attempts.\n", norm_name,
retries);
sql_print_error("Failed to open table %s.\n",
norm_name);
}
ib_logf(IB_LOG_LEVEL_WARN,
@ -5299,8 +5367,6 @@ ha_innobase::clone(
mem_root));
if (new_handler) {
DBUG_ASSERT(new_handler->prebuilt != NULL);
DBUG_ASSERT(new_handler->user_thd == user_thd);
DBUG_ASSERT(new_handler->prebuilt->trx == prebuilt->trx);
new_handler->prebuilt->select_lock_type
= prebuilt->select_lock_type;
@ -5680,8 +5746,8 @@ ulint
innobase_mysql_fts_get_token(
/*=========================*/
CHARSET_INFO* cs, /*!< in: Character set */
byte* start, /*!< in: start of text */
byte* end, /*!< in: one character past end of
const byte* start, /*!< in: start of text */
const byte* end, /*!< in: one character past end of
text */
fts_string_t* token, /*!< out: token's text */
ulint* offset) /*!< out: offset to token,
@ -5689,13 +5755,12 @@ innobase_mysql_fts_get_token(
'start' */
{
int mbl;
uchar* doc = start;
const uchar* doc = start;
ut_a(cs);
token->f_n_char = token->f_len = 0;
do {
for (;;) {
if (doc >= end) {
@ -5705,7 +5770,7 @@ innobase_mysql_fts_get_token(
int ctype;
mbl = cs->cset->ctype(
cs, &ctype, (uchar*) doc, (uchar*) end);
cs, &ctype, doc, (const uchar*) end);
if (true_word_char(ctype, *doc)) {
break;
@ -5717,7 +5782,7 @@ innobase_mysql_fts_get_token(
ulint mwc = 0;
ulint length = 0;
token->f_str = doc;
token->f_str = const_cast<byte*>(doc);
while (doc < end) {
@ -5725,7 +5790,6 @@ innobase_mysql_fts_get_token(
mbl = cs->cset->ctype(
cs, &ctype, (uchar*) doc, (uchar*) end);
if (true_word_char(ctype, *doc)) {
mwc = 0;
} else if (!misc_word_char(*doc) || mwc) {
@ -5742,12 +5806,6 @@ innobase_mysql_fts_get_token(
token->f_len = (uint) (doc - token->f_str) - mwc;
token->f_n_char = length;
return(doc - start);
} while (doc < end);
token->f_str[token->f_len] = 0;
return(doc - start);
}
@ -7476,6 +7534,12 @@ ha_innobase::unlock_row(void)
DBUG_VOID_RETURN;
}
/* Ideally, this assert must be in the beginning of the function.
But there are some calls to this function from the SQL layer when the
transaction is in state TRX_STATE_NOT_STARTED. The check on
prebuilt->select_lock_type above gets around this issue. */
ut_ad(trx_state_eq(prebuilt->trx, TRX_STATE_ACTIVE));
switch (prebuilt->row_read_type) {
case ROW_READ_WITH_LOCKS:
if (!srv_locks_unsafe_for_binlog
@ -8310,7 +8374,7 @@ ha_innobase::ft_init_ext(
{
trx_t* trx;
dict_table_t* table;
ulint error;
dberr_t error;
byte* query = (byte*) key->ptr();
ulint query_len = key->length();
const CHARSET_INFO* char_set = key->charset();
@ -8387,23 +8451,24 @@ ha_innobase::ft_init_ext(
error = fts_query(trx, index, flags, query, query_len, &result);
// FIXME: Proper error handling and diagnostic
if (error != DB_SUCCESS) {
fprintf(stderr, "Error processing query\n");
} else {
/* Allocate FTS handler, and instantiate it before return */
fts_hdl = (NEW_FT_INFO*) my_malloc(sizeof(NEW_FT_INFO),
my_error(convert_error_code_to_mysql(error, 0, NULL),
MYF(0));
return(NULL);
}
fts_hdl->please = (struct _ft_vft*)(&ft_vft_result);
fts_hdl->could_you = (struct _ft_vft_ext*)(&ft_vft_ext_result);
/* Allocate FTS handler, and instantiate it before return */
fts_hdl = static_cast<NEW_FT_INFO*>(my_malloc(sizeof(NEW_FT_INFO),
MYF(0)));
fts_hdl->please = const_cast<_ft_vft*>(&ft_vft_result);
fts_hdl->could_you = const_cast<_ft_vft_ext*>(&ft_vft_ext_result);
fts_hdl->ft_prebuilt = prebuilt;
fts_hdl->ft_result = result;
/* FIXME: Re-evluate the condition when Bug 14469540
is resolved */
prebuilt->in_fts_query = true;
}
return((FT_INFO*) fts_hdl);
}
@ -8922,6 +8987,9 @@ err_col:
mem_heap_free(heap);
DBUG_EXECUTE_IF("ib_create_err_tablespace_exist",
err = DB_TABLESPACE_EXISTS;);
if (err == DB_DUPLICATE_KEY || err == DB_TABLESPACE_EXISTS) {
char display_name[FN_REFLEN];
char* buf_end = innobase_convert_identifier(
@ -9515,6 +9583,11 @@ innobase_table_flags(
DBUG_RETURN(false);
}
if (key->flags & HA_USES_PARSER) {
my_error(ER_INNODB_NO_FT_USES_PARSER, MYF(0));
DBUG_RETURN(false);
}
if (fts_doc_id_index_bad) {
goto index_bad;
}
@ -9837,7 +9910,7 @@ ha_innobase::create(
/* Check whether there already exists FTS_DOC_ID_INDEX */
ret = innobase_fts_check_doc_id_index_in_def(
form->s->keys, form->s->key_info);
form->s->keys, form->key_info);
switch (ret) {
case FTS_INCORRECT_DOC_ID_INDEX:
@ -9893,6 +9966,16 @@ ha_innobase::create(
}
}
/* Cache all the FTS indexes on this table in the FTS specific
structure. They are used for FTS indexed column update handling. */
if (flags2 & DICT_TF2_FTS) {
fts_t* fts = innobase_table->fts;
ut_a(fts != NULL);
dict_table_get_all_fts_indexes(innobase_table, fts->indexes);
}
stmt = innobase_get_stmt(thd, &stmt_len);
if (stmt) {
@ -9931,15 +10014,6 @@ ha_innobase::create(
goto cleanup;
}
}
/* Cache all the FTS indexes on this table in the FTS specific
structure. They are used for FTS indexed column update handling. */
if (flags2 & DICT_TF2_FTS) {
fts_t* fts = innobase_table->fts;
ut_a(fts != NULL);
dict_table_get_all_fts_indexes(innobase_table, fts->indexes);
}
innobase_commit_low(trx);
@ -10407,8 +10481,10 @@ innobase_rename_table(
DEBUG_SYNC_C("innodb_rename_table_ready");
trx_start_if_not_started(trx);
/* Serialize data dictionary operations with dictionary mutex:
no deadlocks can occur then in these operations */
no deadlocks can occur then in these operations. */
row_mysql_lock_data_dictionary(trx);
@ -10446,6 +10522,7 @@ innobase_rename_table(
normalize_table_name_low(
par_case_name, from, FALSE);
#endif
trx_start_if_not_started(trx);
error = row_rename_table_for_mysql(
par_case_name, norm_to, trx, TRUE);
}
@ -11054,8 +11131,6 @@ ha_innobase::info_low(
if (dict_stats_is_persistent_enabled(ib_table)) {
ut_ad(!srv_read_only_mode);
if (is_analyze) {
opt = DICT_STATS_RECALC_PERSISTENT;
} else {
@ -11602,14 +11677,15 @@ ha_innobase::check(
index_name, sizeof index_name,
index->name, TRUE);
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
push_warning_printf(
thd, Sql_condition::WARN_LEVEL_WARN,
ER_NOT_KEYFILE,
"InnoDB: The B-tree of"
" index %s is corrupted.",
index_name);
is_ok = FALSE;
dict_set_corrupted(
index, prebuilt->trx, "CHECK TABLE");
index, prebuilt->trx, "CHECK TABLE-check index");
}
if (thd_kill_level(user_thd)) {
@ -11625,15 +11701,18 @@ ha_innobase::check(
n_rows_in_table = n_rows;
} else if (!(index->type & DICT_FTS)
&& (n_rows != n_rows_in_table)) {
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
push_warning_printf(
thd, Sql_condition::WARN_LEVEL_WARN,
ER_NOT_KEYFILE,
"InnoDB: Index '%-.200s'"
" contains %lu entries,"
" should be %lu.",
"InnoDB: Index '%-.200s' contains %lu"
" entries, should be %lu.",
index->name,
(ulong) n_rows,
(ulong) n_rows_in_table);
is_ok = FALSE;
dict_set_corrupted(
index, prebuilt->trx,
"CHECK TABLE; Wrong count");
}
}
@ -12301,11 +12380,10 @@ ha_innobase::external_lock(
&& !(table_flags() & HA_BINLOG_STMT_CAPABLE)
&& thd_binlog_format(thd) == BINLOG_FORMAT_STMT
&& thd_binlog_filter_ok(thd)
&& thd_sqlcom_can_generate_row_events(thd))
{
int skip = 0;
&& thd_sqlcom_can_generate_row_events(thd)) {
bool skip = 0;
/* used by test case */
DBUG_EXECUTE_IF("no_innodb_binlog_errors", skip = 1;);
DBUG_EXECUTE_IF("no_innodb_binlog_errors", skip = true;);
if (!skip) {
my_error(ER_BINLOG_STMT_MODE_AND_ROW_ENGINE, MYF(0),
" InnoDB is limited to row-logging when "
@ -12323,14 +12401,23 @@ ha_innobase::external_lock(
|| thd_sql_command(thd) == SQLCOM_DROP_TABLE
|| thd_sql_command(thd) == SQLCOM_ALTER_TABLE
|| thd_sql_command(thd) == SQLCOM_OPTIMIZE
|| thd_sql_command(thd) == SQLCOM_CREATE_TABLE
|| (thd_sql_command(thd) == SQLCOM_CREATE_TABLE
&& lock_type == F_WRLCK)
|| thd_sql_command(thd) == SQLCOM_CREATE_INDEX
|| thd_sql_command(thd) == SQLCOM_DROP_INDEX
|| thd_sql_command(thd) == SQLCOM_DELETE)) {
ib_senderrf(thd, IB_LOG_LEVEL_WARN, ER_READ_ONLY_MODE);
if (thd_sql_command(thd) == SQLCOM_CREATE_TABLE)
{
ib_senderrf(thd, IB_LOG_LEVEL_WARN,
ER_READ_ONLY_MODE);
DBUG_RETURN(HA_ERR_TABLE_READONLY);
} else {
ib_senderrf(thd, IB_LOG_LEVEL_WARN,
ER_READ_ONLY_MODE);
DBUG_RETURN(HA_ERR_TABLE_READONLY);
}
}
trx = prebuilt->trx;
@ -13051,7 +13138,9 @@ ha_innobase::store_lock(
|| sql_command == SQLCOM_DROP_TABLE
|| sql_command == SQLCOM_ALTER_TABLE
|| sql_command == SQLCOM_OPTIMIZE
|| sql_command == SQLCOM_CREATE_TABLE
|| (sql_command == SQLCOM_CREATE_TABLE
&& (lock_type >= TL_WRITE_CONCURRENT_INSERT
&& lock_type <= TL_WRITE))
|| sql_command == SQLCOM_CREATE_INDEX
|| sql_command == SQLCOM_DROP_INDEX
|| sql_command == SQLCOM_DELETE)) {
@ -15165,6 +15254,80 @@ innodb_srv_buf_dump_filename_validate(
# define innodb_srv_buf_dump_filename_validate NULL
#endif /* __WIN__ */
#ifdef UNIV_DEBUG
static char* srv_buffer_pool_evict;
/****************************************************************//**
Evict all uncompressed pages of compressed tables from the buffer pool.
Keep the compressed pages in the buffer pool.
@return whether all uncompressed pages were evicted */
static __attribute__((warn_unused_result))
bool
innodb_buffer_pool_evict_uncompressed(void)
/*=======================================*/
{
bool all_evicted = true;
for (ulint i = 0; i < srv_buf_pool_instances; i++) {
buf_pool_t* buf_pool = &buf_pool_ptr[i];
buf_pool_mutex_enter(buf_pool);
for (buf_block_t* block = UT_LIST_GET_LAST(
buf_pool->unzip_LRU);
block != NULL; ) {
buf_block_t* prev_block = UT_LIST_GET_PREV(
unzip_LRU, block);
ut_ad(buf_block_get_state(block)
== BUF_BLOCK_FILE_PAGE);
ut_ad(block->in_unzip_LRU_list);
ut_ad(block->page.in_LRU_list);
if (!buf_LRU_free_page(&block->page, false)) {
all_evicted = false;
}
block = prev_block;
}
buf_pool_mutex_exit(buf_pool);
}
return(all_evicted);
}
/****************************************************************//**
Called on SET GLOBAL innodb_buffer_pool_evict=...
Handles some values specially, to evict pages from the buffer pool.
SET GLOBAL innodb_buffer_pool_evict='uncompressed'
evicts all uncompressed page frames of compressed tablespaces. */
static
void
innodb_buffer_pool_evict_update(
/*============================*/
THD* thd, /*!< in: thread handle */
struct st_mysql_sys_var*var, /*!< in: pointer to system variable */
void* var_ptr,/*!< out: ignored */
const void* save) /*!< in: immediate result
from check function */
{
if (const char* op = *static_cast<const char*const*>(save)) {
if (!strcmp(op, "uncompressed")) {
for (uint tries = 0; tries < 10000; tries++) {
if (innodb_buffer_pool_evict_uncompressed()) {
return;
}
os_thread_sleep(10000);
}
/* We failed to evict all uncompressed pages. */
ut_ad(0);
}
}
}
#endif /* UNIV_DEBUG */
/****************************************************************//**
Update the system variable innodb_monitor_enable and enable
specified monitor counter.
@ -15241,29 +15404,6 @@ innodb_reset_all_monitor_update(
TRUE);
}
/****************************************************************//**
Update the system variable innodb_compression_level using the "saved"
value. This function is registered as a callback with MySQL. */
static
void
innodb_compression_level_update(
/*============================*/
THD* thd, /*!< in: thread handle */
struct st_mysql_sys_var* var, /*!< in: pointer to
system variable */
void* var_ptr,/*!< out: where the
formal string goes */
const void* save) /*!< in: immediate result
from check function */
{
/* We have this call back just to avoid confusion between
ulong and ulint datatypes. */
innobase_compression_level =
(*static_cast<const ulong*>(save));
page_compression_level =
(static_cast<const ulint>(innobase_compression_level));
}
/****************************************************************//**
Parse and enable InnoDB monitor counters during server startup.
User can list the monitor counters/groups to be enable by specifying
@ -15441,6 +15581,7 @@ innobase_fts_find_ranking(
#ifdef UNIV_DEBUG
static my_bool innodb_purge_run_now = TRUE;
static my_bool innodb_purge_stop_now = TRUE;
static my_bool innodb_log_checkpoint_now = TRUE;
/****************************************************************//**
Set the purge state to RUN. If purge is disabled then it
@ -15487,6 +15628,33 @@ purge_stop_now_set(
trx_purge_stop();
}
}
/****************************************************************//**
Force innodb to checkpoint. */
static
void
checkpoint_now_set(
/*===============*/
THD* thd /*!< in: thread handle */
__attribute__((unused)),
struct st_mysql_sys_var* var /*!< in: pointer to system
variable */
__attribute__((unused)),
void* var_ptr /*!< out: where the formal
string goes */
__attribute__((unused)),
const void* save) /*!< in: immediate result from
check function */
{
if (*(my_bool*) save) {
while (log_sys->last_checkpoint_lsn < log_sys->lsn) {
log_make_checkpoint_at(LSN_MAX, TRUE);
fil_flush_file_spaces(FIL_LOG);
}
fil_write_flushed_lsn_to_data_files(log_sys->lsn, 0);
fil_flush_file_spaces(FIL_TABLESPACE);
}
}
#endif /* UNIV_DEBUG */
/***********************************************************************
@ -15725,6 +15893,11 @@ static MYSQL_SYSVAR_BOOL(purge_stop_now, innodb_purge_stop_now,
PLUGIN_VAR_OPCMDARG,
"Set purge state to STOP",
NULL, purge_stop_now_set, FALSE);
static MYSQL_SYSVAR_BOOL(log_checkpoint_now, innodb_log_checkpoint_now,
PLUGIN_VAR_OPCMDARG,
"Force checkpoint now",
NULL, checkpoint_now_set, FALSE);
#endif /* UNIV_DEBUG */
static MYSQL_SYSVAR_ULONG(purge_batch_size, srv_purge_batch_size,
@ -15744,7 +15917,7 @@ static MYSQL_SYSVAR_ULONG(purge_threads, srv_n_purge_threads,
32, 0); /* Maximum value */
static MYSQL_SYSVAR_ULONG(sync_array_size, srv_sync_array_size,
PLUGIN_VAR_OPCMDARG,
PLUGIN_VAR_OPCMDARG | PLUGIN_VAR_READONLY,
"Size of the mutex/lock wait array.",
NULL, NULL,
1, /* Default setting */
@ -15952,12 +16125,20 @@ static MYSQL_SYSVAR_ULONG(replication_delay, srv_replication_delay,
"innodb_thread_concurrency is reached (0 by default)",
NULL, NULL, 0, 0, ~0UL, 0);
static MYSQL_SYSVAR_ULONG(compression_level, innobase_compression_level,
static MYSQL_SYSVAR_UINT(compression_level, page_zip_level,
PLUGIN_VAR_RQCMDARG,
"Compression level used for compressed row format. 0 is no compression"
", 1 is fastest, 9 is best compression and default is 6.",
NULL, innodb_compression_level_update,
DEFAULT_COMPRESSION_LEVEL, 0, 9, 0);
NULL, NULL, DEFAULT_COMPRESSION_LEVEL, 0, 9, 0);
static MYSQL_SYSVAR_BOOL(log_compressed_pages, page_zip_log_pages,
PLUGIN_VAR_OPCMDARG,
"Enables/disables the logging of entire compressed page images."
" InnoDB logs the compressed pages to prevent corruption if"
" the zlib compression algorithm changes."
" When turned OFF, InnoDB will assume that the zlib"
" compression algorithm doesn't change.",
NULL, NULL, TRUE);
static MYSQL_SYSVAR_LONG(additional_mem_pool_size, innobase_additional_mem_pool_size,
PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
@ -16009,6 +16190,13 @@ static MYSQL_SYSVAR_BOOL(buffer_pool_dump_at_shutdown, srv_buffer_pool_dump_at_s
"Dump the buffer pool into a file named @@innodb_buffer_pool_filename",
NULL, NULL, FALSE);
#ifdef UNIV_DEBUG
static MYSQL_SYSVAR_STR(buffer_pool_evict, srv_buffer_pool_evict,
PLUGIN_VAR_RQCMDARG,
"Evict pages from the buffer pool",
NULL, innodb_buffer_pool_evict_update, "");
#endif /* UNIV_DEBUG */
static MYSQL_SYSVAR_BOOL(buffer_pool_load_now, innodb_buffer_pool_load_now,
PLUGIN_VAR_RQCMDARG,
"Trigger an immediate load of the buffer pool from a file named @@innodb_buffer_pool_filename",
@ -16074,6 +16262,16 @@ static MYSQL_SYSVAR_ULONG(ft_cache_size, fts_max_cache_size,
"InnoDB Fulltext search cache size in bytes",
NULL, NULL, 8000000, 1600000, 80000000, 0);
static MYSQL_SYSVAR_ULONG(ft_total_cache_size, fts_max_total_cache_size,
PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
"Total memory allocated for InnoDB Fulltext Search cache",
NULL, NULL, 640000000, 32000000, 1600000000, 0);
static MYSQL_SYSVAR_ULONG(ft_result_cache_limit, fts_result_cache_limit,
PLUGIN_VAR_RQCMDARG,
"InnoDB Fulltext search query result cache limit in bytes",
NULL, NULL, 2000000000L, 1000000L, ~0UL, 0);
static MYSQL_SYSVAR_ULONG(ft_min_token_size, fts_min_token_size,
PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
"InnoDB Fulltext search minimum token size in characters",
@ -16082,7 +16280,7 @@ static MYSQL_SYSVAR_ULONG(ft_min_token_size, fts_min_token_size,
static MYSQL_SYSVAR_ULONG(ft_max_token_size, fts_max_token_size,
PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
"InnoDB Fulltext search maximum token size in characters",
NULL, NULL, HA_FT_MAXCHARLEN, 10, FTS_MAX_WORD_LEN , 0);
NULL, NULL, FTS_MAX_WORD_LEN_IN_CHAR, 10, FTS_MAX_WORD_LEN_IN_CHAR, 0);
static MYSQL_SYSVAR_ULONG(ft_num_word_optimize, fts_num_word_optimize,
@ -16153,10 +16351,12 @@ static MYSQL_SYSVAR_ULONG(log_files_in_group, srv_n_log_files,
"Number of log files in the log group. InnoDB writes to the files in a circular fashion.",
NULL, NULL, 2, 2, SRV_N_LOG_FILES_MAX, 0);
/* Note that the default and minimum values are set to 0 to
detect if the option is passed and print deprecation message */
static MYSQL_SYSVAR_LONG(mirrored_log_groups, innobase_mirrored_log_groups,
PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
"Number of identical copies of log groups we keep for the database. Currently this should be set to 1.",
NULL, NULL, 1, 1, 10, 0);
NULL, NULL, 0, 0, 10, 0);
static MYSQL_SYSVAR_UINT(old_blocks_pct, innobase_old_blocks_pct,
PLUGIN_VAR_RQCMDARG,
@ -16432,6 +16632,9 @@ static struct st_mysql_sys_var* innobase_system_variables[]= {
MYSQL_SYSVAR(buffer_pool_filename),
MYSQL_SYSVAR(buffer_pool_dump_now),
MYSQL_SYSVAR(buffer_pool_dump_at_shutdown),
#ifdef UNIV_DEBUG
MYSQL_SYSVAR(buffer_pool_evict),
#endif /* UNIV_DEBUG */
MYSQL_SYSVAR(buffer_pool_load_now),
MYSQL_SYSVAR(buffer_pool_load_abort),
MYSQL_SYSVAR(buffer_pool_load_at_startup),
@ -16466,6 +16669,8 @@ static struct st_mysql_sys_var* innobase_system_variables[]= {
MYSQL_SYSVAR(force_recovery_crash),
#endif /* !DBUG_OFF */
MYSQL_SYSVAR(ft_cache_size),
MYSQL_SYSVAR(ft_total_cache_size),
MYSQL_SYSVAR(ft_result_cache_limit),
MYSQL_SYSVAR(ft_enable_stopword),
MYSQL_SYSVAR(ft_max_token_size),
MYSQL_SYSVAR(ft_min_token_size),
@ -16484,6 +16689,7 @@ static struct st_mysql_sys_var* innobase_system_variables[]= {
MYSQL_SYSVAR(log_file_size),
MYSQL_SYSVAR(log_files_in_group),
MYSQL_SYSVAR(log_group_home_dir),
MYSQL_SYSVAR(log_compressed_pages),
MYSQL_SYSVAR(max_dirty_pages_pct),
MYSQL_SYSVAR(max_dirty_pages_pct_lwm),
MYSQL_SYSVAR(adaptive_flushing_lwm),
@ -16548,6 +16754,7 @@ static struct st_mysql_sys_var* innobase_system_variables[]= {
#ifdef UNIV_DEBUG
MYSQL_SYSVAR(purge_run_now),
MYSQL_SYSVAR(purge_stop_now),
MYSQL_SYSVAR(log_checkpoint_now),
#endif /* UNIV_DEBUG */
#if defined UNIV_DEBUG || defined UNIV_PERF_DEBUG
MYSQL_SYSVAR(page_hash_locks),
@ -16600,7 +16807,6 @@ i_s_innodb_buffer_page_lru,
i_s_innodb_buffer_stats,
i_s_innodb_metrics,
i_s_innodb_ft_default_stopword,
i_s_innodb_ft_inserted,
i_s_innodb_ft_deleted,
i_s_innodb_ft_being_deleted,
i_s_innodb_ft_config,
@ -17056,6 +17262,23 @@ ib_logf(
}
}
/**********************************************************************
Converts an identifier from my_charset_filename to UTF-8 charset.
@return result string length, as returned by strconvert() */
uint
innobase_convert_to_filename_charset(
/*=================================*/
char* to, /* out: converted identifier */
const char* from, /* in: identifier to convert */
ulint len) /* in: length of 'to', in bytes */
{
uint errors;
CHARSET_INFO* cs_to = &my_charset_filename;
CHARSET_INFO* cs_from = system_charset_info;
return(strconvert(cs_from, from, strlen(from), cs_to, to, len, &errors));
}
/**********************************************************************
Converts an identifier from my_charset_filename to UTF-8 charset.
@return result string length, as returned by strconvert() */

View file

@ -634,7 +634,7 @@ void
innobase_copy_frm_flags_from_create_info(
/*=====================================*/
dict_table_t* innodb_table, /*!< in/out: InnoDB table */
HA_CREATE_INFO* create_info); /*!< in: create info */
const HA_CREATE_INFO* create_info); /*!< in: create info */
/*********************************************************************//**
Copy table flags from MySQL's TABLE_SHARE into an InnoDB table object.
@ -646,4 +646,4 @@ void
innobase_copy_frm_flags_from_table_share(
/*=====================================*/
dict_table_t* innodb_table, /*!< in/out: InnoDB table */
TABLE_SHARE* table_share); /*!< in: table share */
const TABLE_SHARE* table_share); /*!< in: table share */

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 2007, 2012, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2007, 2013, Oracle and/or its affiliates. All Rights Reserved.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -24,7 +24,7 @@ Created July 18, 2007 Vasil Dimov
*******************************************************/
#include <mysqld_error.h>
#include <sql_acl.h> // PROCESS_ACL
#include <sql_acl.h>
#include <m_ctype.h>
#include <hash.h>
@ -35,18 +35,19 @@ Created July 18, 2007 Vasil Dimov
#include <sql_plugin.h>
#include <innodb_priv.h>
#include "btr0pcur.h" /* for file sys_tables related info. */
#include "btr0pcur.h"
#include "btr0types.h"
#include "buf0buddy.h" /* for i_s_cmpmem */
#include "buf0buf.h" /* for buf_pool */
#include "dict0dict.h" /* for dict_table_stats_lock() */
#include "dict0load.h" /* for file sys_tables related info. */
#include "dict0dict.h"
#include "dict0load.h"
#include "buf0buddy.h"
#include "buf0buf.h"
#include "ibuf0ibuf.h"
#include "dict0mem.h"
#include "dict0types.h"
#include "ha_prototypes.h" /* for innobase_convert_name() */
#include "srv0start.h" /* for srv_was_started */
#include "ha_prototypes.h"
#include "srv0start.h"
#include "trx0i_s.h"
#include "trx0trx.h" /* for TRX_QUE_STATE_STR_MAX_LEN */
#include "trx0trx.h"
#include "srv0mon.h"
#include "fut0fut.h"
#include "pars0pars.h"
@ -64,8 +65,12 @@ struct buf_page_desc_t{
ulint type_value; /*!< Page type or page state */
};
/** Any states greater than FIL_PAGE_TYPE_LAST would be treated as unknown. */
#define I_S_PAGE_TYPE_UNKNOWN (FIL_PAGE_TYPE_LAST + 1)
/** Change buffer B-tree page */
#define I_S_PAGE_TYPE_IBUF (FIL_PAGE_TYPE_LAST + 1)
/** Any states greater than I_S_PAGE_TYPE_IBUF would be treated as
unknown. */
#define I_S_PAGE_TYPE_UNKNOWN (I_S_PAGE_TYPE_IBUF + 1)
/** We also define I_S_PAGE_TYPE_INDEX as the Index Page's position
in i_s_page_type[] array */
@ -86,6 +91,7 @@ static buf_page_desc_t i_s_page_type[] = {
{"BLOB", FIL_PAGE_TYPE_BLOB},
{"COMPRESSED_BLOB", FIL_PAGE_TYPE_ZBLOB},
{"COMPRESSED_BLOB2", FIL_PAGE_TYPE_ZBLOB2},
{"IBUF_INDEX", I_S_PAGE_TYPE_IBUF},
{"UNKNOWN", I_S_PAGE_TYPE_UNKNOWN}
};
@ -2900,8 +2906,7 @@ UNIV_INTERN struct st_maria_plugin i_s_innodb_ft_default_stopword =
};
/* Fields of the dynamic table INFORMATION_SCHEMA.INNODB_FT_DELETED
INFORMATION_SCHEMA.INNODB_FT_BEING_DELETED and
INFORMATION_SCHEMA.INNODB_FT_INSERTED */
INFORMATION_SCHEMA.INNODB_FT_BEING_DELETED */
static ST_FIELD_INFO i_s_fts_doc_fields_info[] =
{
#define I_S_FTS_DOC_ID 0
@ -3151,139 +3156,6 @@ UNIV_INTERN struct st_maria_plugin i_s_innodb_ft_being_deleted =
STRUCT_FLD(maturity, MariaDB_PLUGIN_MATURITY_STABLE),
};
/*******************************************************************//**
Fill the dynamic table INFORMATION_SCHEMA.INNODB_FT_INSERTED.
@return 0 on success, 1 on failure */
static
int
i_s_fts_inserted_fill(
/*==================*/
THD* thd, /*!< in: thread */
TABLE_LIST* tables, /*!< in/out: tables to fill */
Item* ) /*!< in: condition (ignored) */
{
Field** fields;
TABLE* table = (TABLE*) tables->table;
trx_t* trx;
fts_table_t fts_table;
fts_doc_ids_t* inserted;
dict_table_t* user_table;
DBUG_ENTER("i_s_fts_inserted_fill");
/* deny access to non-superusers */
if (check_global_access(thd, PROCESS_ACL)) {
DBUG_RETURN(0);
}
if (!fts_internal_tbl_name) {
DBUG_RETURN(0);
}
user_table = dict_table_open_on_name(
fts_internal_tbl_name, FALSE, FALSE, DICT_ERR_IGNORE_NONE);
if (!user_table) {
DBUG_RETURN(0);
}
inserted = fts_doc_ids_create();
trx = trx_allocate_for_background();
trx->op_info = "Select for FTS ADDED Table";
FTS_INIT_FTS_TABLE(&fts_table, "ADDED", FTS_COMMON_TABLE, user_table);
fts_table_fetch_doc_ids(trx, &fts_table, inserted);
fields = table->field;
for (ulint j = 0; j < ib_vector_size(inserted->doc_ids); ++j) {
doc_id_t doc_id;
doc_id = *(doc_id_t*) ib_vector_get_const(inserted->doc_ids, j);
OK(fields[I_S_FTS_DOC_ID]->store((longlong) doc_id, true));
OK(schema_table_store_record(thd, table));
}
trx_free_for_background(trx);
fts_doc_ids_free(inserted);
dict_table_close(user_table, FALSE, FALSE);
DBUG_RETURN(0);
}
/*******************************************************************//**
Bind the dynamic table INFORMATION_SCHEMA.INNODB_FT_INSERTED
@return 0 on success */
static
int
i_s_fts_inserted_init(
/*==================*/
void* p) /*!< in/out: table schema object */
{
DBUG_ENTER("i_s_fts_inserted_init");
ST_SCHEMA_TABLE* schema = (ST_SCHEMA_TABLE*) p;
schema->fields_info = i_s_fts_doc_fields_info;
schema->fill_table = i_s_fts_inserted_fill;
DBUG_RETURN(0);
}
UNIV_INTERN struct st_maria_plugin i_s_innodb_ft_inserted =
{
/* the plugin type (a MYSQL_XXX_PLUGIN value) */
/* int */
STRUCT_FLD(type, MYSQL_INFORMATION_SCHEMA_PLUGIN),
/* pointer to type-specific plugin descriptor */
/* void* */
STRUCT_FLD(info, &i_s_info),
/* plugin name */
/* const char* */
STRUCT_FLD(name, "INNODB_FT_INSERTED"),
/* plugin author (for SHOW PLUGINS) */
/* const char* */
STRUCT_FLD(author, plugin_author),
/* general descriptive text (for SHOW PLUGINS) */
/* const char* */
STRUCT_FLD(descr, "INNODB AUXILIARY FTS INSERTED TABLE"),
/* the plugin license (PLUGIN_LICENSE_XXX) */
/* int */
STRUCT_FLD(license, PLUGIN_LICENSE_GPL),
/* the function to invoke when plugin is loaded */
/* int (*)(void*); */
STRUCT_FLD(init, i_s_fts_inserted_init),
/* the function to invoke when plugin is unloaded */
/* int (*)(void*); */
STRUCT_FLD(deinit, i_s_common_deinit),
/* plugin version (for SHOW PLUGINS) */
/* unsigned int */
STRUCT_FLD(version, INNODB_VERSION_SHORT),
/* struct st_mysql_show_var* */
STRUCT_FLD(status_vars, NULL),
/* struct st_mysql_sys_var** */
STRUCT_FLD(system_vars, NULL),
/* Maria extension */
STRUCT_FLD(version_info, INNODB_VERSION_STR),
STRUCT_FLD(maturity, MariaDB_PLUGIN_MATURITY_STABLE),
};
/* Fields of the dynamic table INFORMATION_SCHEMA.INNODB_FT_INDEX_CACHED and
INFORMATION_SCHEMA.INNODB_FT_INDEX_TABLE */
static ST_FIELD_INFO i_s_fts_index_fields_info[] =
@ -3875,14 +3747,8 @@ static ST_FIELD_INFO i_s_fts_config_fields_info[] =
static const char* fts_config_key[] = {
FTS_OPTIMIZE_LIMIT_IN_SECS,
FTS_SYNCED_DOC_ID,
FTS_LAST_OPTIMIZED_WORD,
FTS_TOTAL_DELETED_COUNT,
FTS_TOTAL_WORD_COUNT,
FTS_OPTIMIZE_START_TIME,
FTS_OPTIMIZE_END_TIME,
FTS_STOPWORD_TABLE_NAME,
FTS_USE_STOPWORD,
FTS_TABLE_STATE,
NULL
};
@ -4466,6 +4332,7 @@ i_s_innodb_buffer_stats_fill_table(
buf_pool_info_t* pool_info;
DBUG_ENTER("i_s_innodb_buffer_fill_general");
RETURN_IF_INNODB_NOT_STARTED(tables->schema_table_name);
/* Only allow the PROCESS privilege holder to access the stats */
if (check_global_access(thd, PROCESS_ACL)) {
@ -4879,7 +4746,7 @@ i_s_innodb_buffer_page_fill(
/* First three states are for compression pages and
are not states we would get as we scan pages through
buffer blocks */
case BUF_BLOCK_ZIP_FREE:
case BUF_BLOCK_POOL_WATCH:
case BUF_BLOCK_ZIP_PAGE:
case BUF_BLOCK_ZIP_DIRTY:
state_str = NULL;
@ -4951,14 +4818,21 @@ i_s_innodb_set_page_type(
if (page_type == FIL_PAGE_INDEX) {
const page_t* page = (const page_t*) frame;
page_info->index_id = btr_page_get_index_id(page);
/* FIL_PAGE_INDEX is a bit special, its value
is defined as 17855, so we cannot use FIL_PAGE_INDEX
to index into i_s_page_type[] array, its array index
in the i_s_page_type[] array is I_S_PAGE_TYPE_INDEX
(1) */
(1) for index pages or I_S_PAGE_TYPE_IBUF for
change buffer index pages */
if (page_info->index_id
== static_cast<index_id_t>(DICT_IBUF_ID_MIN
+ IBUF_SPACE_ID)) {
page_info->page_type = I_S_PAGE_TYPE_IBUF;
} else {
page_info->page_type = I_S_PAGE_TYPE_INDEX;
page_info->index_id = btr_page_get_index_id(page);
}
page_info->data_size = (ulint)(page_header_get_field(
page, PAGE_HEAP_TOP) - (page_is_comp(page)
@ -4967,7 +4841,7 @@ i_s_innodb_set_page_type(
- page_header_get_field(page, PAGE_GARBAGE));
page_info->num_recs = page_get_n_recs(page);
} else if (page_type >= I_S_PAGE_TYPE_UNKNOWN) {
} else if (page_type > FIL_PAGE_TYPE_LAST) {
/* Encountered an unknown page type */
page_info->page_type = I_S_PAGE_TYPE_UNKNOWN;
} else {
@ -5039,6 +4913,16 @@ i_s_innodb_buffer_page_get_info(
page_info->freed_page_clock = bpage->freed_page_clock;
switch (buf_page_get_io_fix(bpage)) {
case BUF_IO_NONE:
case BUF_IO_WRITE:
case BUF_IO_PIN:
break;
case BUF_IO_READ:
page_info->page_type = I_S_PAGE_TYPE_UNKNOWN;
return;
}
if (page_info->page_state == BUF_BLOCK_FILE_PAGE) {
const buf_block_t*block;
@ -5075,6 +4959,7 @@ i_s_innodb_fill_buffer_pool(
mem_heap_t* heap;
DBUG_ENTER("i_s_innodb_fill_buffer_pool");
RETURN_IF_INNODB_NOT_STARTED(tables->schema_table_name);
heap = mem_heap_create(10000);
@ -5574,7 +5459,7 @@ i_s_innodb_buf_page_lru_fill(
state_str = "NO";
break;
/* We should not see following states */
case BUF_BLOCK_ZIP_FREE:
case BUF_BLOCK_POOL_WATCH:
case BUF_BLOCK_READY_FOR_USE:
case BUF_BLOCK_NOT_USED:
case BUF_BLOCK_MEMORY:
@ -5640,6 +5525,7 @@ i_s_innodb_fill_buffer_lru(
ulint lru_len;
DBUG_ENTER("i_s_innodb_fill_buffer_lru");
RETURN_IF_INNODB_NOT_STARTED(tables->schema_table_name);
/* Obtain buf_pool mutex before allocate info_buffer, since
UT_LIST_GET_LEN(buf_pool->LRU) could change */
@ -5966,6 +5852,7 @@ i_s_sys_tables_fill_table(
mtr_t mtr;
DBUG_ENTER("i_s_sys_tables_fill_table");
RETURN_IF_INNODB_NOT_STARTED(tables->schema_table_name);
/* deny access to user without PROCESS_ACL privilege */
if (check_global_access(thd, PROCESS_ACL)) {
@ -6262,6 +6149,7 @@ i_s_sys_tables_fill_table_stats(
mtr_t mtr;
DBUG_ENTER("i_s_sys_tables_fill_table_stats");
RETURN_IF_INNODB_NOT_STARTED(tables->schema_table_name);
/* deny access to user without PROCESS_ACL privilege */
if (check_global_access(thd, PROCESS_ACL)) {
@ -6511,6 +6399,7 @@ i_s_sys_indexes_fill_table(
mtr_t mtr;
DBUG_ENTER("i_s_sys_indexes_fill_table");
RETURN_IF_INNODB_NOT_STARTED(tables->schema_table_name);
/* deny access to user without PROCESS_ACL privilege */
if (check_global_access(thd, PROCESS_ACL)) {
@ -6748,6 +6637,7 @@ i_s_sys_columns_fill_table(
mtr_t mtr;
DBUG_ENTER("i_s_sys_columns_fill_table");
RETURN_IF_INNODB_NOT_STARTED(tables->schema_table_name);
/* deny access to user without PROCESS_ACL privilege */
if (check_global_access(thd, PROCESS_ACL)) {
@ -6951,6 +6841,7 @@ i_s_sys_fields_fill_table(
mtr_t mtr;
DBUG_ENTER("i_s_sys_fields_fill_table");
RETURN_IF_INNODB_NOT_STARTED(tables->schema_table_name);
/* deny access to user without PROCESS_ACL privilege */
if (check_global_access(thd, PROCESS_ACL)) {
@ -7182,6 +7073,7 @@ i_s_sys_foreign_fill_table(
mtr_t mtr;
DBUG_ENTER("i_s_sys_foreign_fill_table");
RETURN_IF_INNODB_NOT_STARTED(tables->schema_table_name);
/* deny access to user without PROCESS_ACL privilege */
if (check_global_access(thd, PROCESS_ACL)) {
@ -7396,6 +7288,7 @@ i_s_sys_foreign_cols_fill_table(
mtr_t mtr;
DBUG_ENTER("i_s_sys_foreign_cols_fill_table");
RETURN_IF_INNODB_NOT_STARTED(tables->schema_table_name);
/* deny access to user without PROCESS_ACL privilege */
if (check_global_access(thd, PROCESS_ACL)) {
@ -7660,6 +7553,7 @@ i_s_sys_tablespaces_fill_table(
mtr_t mtr;
DBUG_ENTER("i_s_sys_tablespaces_fill_table");
RETURN_IF_INNODB_NOT_STARTED(tables->schema_table_name);
/* deny access to user without PROCESS_ACL privilege */
if (check_global_access(thd, PROCESS_ACL)) {
@ -7850,6 +7744,7 @@ i_s_sys_datafiles_fill_table(
mtr_t mtr;
DBUG_ENTER("i_s_sys_datafiles_fill_table");
RETURN_IF_INNODB_NOT_STARTED(tables->schema_table_name);
/* deny access to user without PROCESS_ACL privilege */
if (check_global_access(thd, PROCESS_ACL)) {

View file

@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 2007, 2012, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2007, 2013, Oracle and/or its affiliates. All Rights Reserved.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -39,7 +39,6 @@ extern struct st_maria_plugin i_s_innodb_cmpmem;
extern struct st_maria_plugin i_s_innodb_cmpmem_reset;
extern struct st_maria_plugin i_s_innodb_metrics;
extern struct st_maria_plugin i_s_innodb_ft_default_stopword;
extern struct st_maria_plugin i_s_innodb_ft_inserted;
extern struct st_maria_plugin i_s_innodb_ft_deleted;
extern struct st_maria_plugin i_s_innodb_ft_being_deleted;
extern struct st_maria_plugin i_s_innodb_ft_index_cache;

View file

@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 1997, 2012, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 1997, 2013, Oracle and/or its affiliates. All Rights Reserved.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -61,6 +61,7 @@ UNIV_INTERN my_bool srv_ibuf_disable_background_merge;
#include "que0que.h"
#include "srv0start.h" /* srv_shutdown_state */
#include "ha_prototypes.h"
#include "rem0cmp.h"
/* STRUCTURE OF AN INSERT BUFFER RECORD
@ -416,7 +417,7 @@ ibuf_tree_root_get(
ut_ad(page_get_space_id(root) == IBUF_SPACE_ID);
ut_ad(page_get_page_no(root) == FSP_IBUF_TREE_ROOT_PAGE_NO);
ut_ad(ibuf->empty == (page_get_n_recs(root) == 0));
ut_ad(ibuf->empty == page_is_empty(root));
return(root);
}
@ -564,7 +565,7 @@ ibuf_init_at_db_start(void)
ibuf_size_update(root, &mtr);
mutex_exit(&ibuf_mutex);
ibuf->empty = (page_get_n_recs(root) == 0);
ibuf->empty = page_is_empty(root);
ibuf_mtr_commit(&mtr);
heap = mem_heap_create(450);
@ -2567,7 +2568,7 @@ ulint
ibuf_merge_pages(
/*=============*/
ulint* n_pages, /*!< out: number of pages to which merged */
bool sync) /*!< in: TRUE if the caller wants to wait for
bool sync) /*!< in: true if the caller wants to wait for
the issued read with the highest tablespace
address to complete */
{
@ -2589,7 +2590,7 @@ ibuf_merge_pages(
ut_ad(page_validate(btr_pcur_get_page(&pcur), ibuf->index));
if (page_get_n_recs(btr_pcur_get_page(&pcur)) == 0) {
if (page_is_empty(btr_pcur_get_page(&pcur))) {
/* If a B-tree page is empty, it must be the root page
and the whole B-tree must be empty. InnoDB does not
allow empty B-tree pages other than the root. */
@ -2633,7 +2634,8 @@ ibuf_get_table(
{
rw_lock_s_lock_func(&dict_operation_lock, 0, __FILE__, __LINE__);
dict_table_t* table = dict_table_open_on_id(table_id, FALSE, FALSE);
dict_table_t* table = dict_table_open_on_id(
table_id, FALSE, DICT_TABLE_OP_NORMAL);
rw_lock_s_unlock_gen(&dict_operation_lock, 0);
@ -2674,7 +2676,7 @@ ibuf_merge_space(
ulint spaces[IBUF_MAX_N_PAGES_MERGED];
ib_int64_t versions[IBUF_MAX_N_PAGES_MERGED];
if (page_get_n_recs(btr_pcur_get_page(&pcur)) == 0) {
if (page_is_empty(btr_pcur_get_page(&pcur))) {
/* If a B-tree page is empty, it must be the root page
and the whole B-tree must be empty. InnoDB does not
allow empty B-tree pages other than the root. */
@ -2712,7 +2714,7 @@ ibuf_merge_space(
#endif /* UNIV_DEBUG */
buf_read_ibuf_merge_pages(
TRUE, spaces, versions, pages, *n_pages);
true, spaces, versions, pages, *n_pages);
}
return(sum_sizes);
@ -3697,7 +3699,7 @@ fail_exit:
ut_ad(page_get_page_no(root)
== FSP_IBUF_TREE_ROOT_PAGE_NO);
ibuf->empty = (page_get_n_recs(root) == 0);
ibuf->empty = page_is_empty(root);
}
} else {
ut_ad(mode == BTR_MODIFY_TREE);
@ -3726,7 +3728,7 @@ fail_exit:
mutex_exit(&ibuf_pessimistic_insert_mutex);
ibuf_size_update(root, &mtr);
mutex_exit(&ibuf_mutex);
ibuf->empty = (page_get_n_recs(root) == 0);
ibuf->empty = page_is_empty(root);
block = btr_cur_get_block(cursor);
ut_ad(buf_block_get_space(block) == IBUF_SPACE_ID);
@ -3768,7 +3770,7 @@ func_exit:
#ifdef UNIV_IBUF_DEBUG
ut_a(n_stored <= IBUF_MAX_N_PAGES_MERGED);
#endif
buf_read_ibuf_merge_pages(FALSE, space_ids, space_versions,
buf_read_ibuf_merge_pages(false, space_ids, space_versions,
page_nos, n_stored);
}
@ -3798,6 +3800,10 @@ ibuf_insert(
/* Read the settable global variable ibuf_use only once in
this function, so that we will have a consistent view of it. */
ibuf_use_t use = ibuf_use;
DBUG_ENTER("ibuf_insert");
DBUG_PRINT("ibuf", ("op: %d, space: %ld, page_no: %ld",
op, space, page_no));
ut_ad(dtuple_check_typed(entry));
ut_ad(ut_is_2pow(zip_size));
@ -3812,7 +3818,7 @@ ibuf_insert(
case IBUF_USE_NONE:
case IBUF_USE_DELETE:
case IBUF_USE_DELETE_MARK:
return(FALSE);
DBUG_RETURN(FALSE);
case IBUF_USE_INSERT:
case IBUF_USE_INSERT_DELETE_MARK:
case IBUF_USE_ALL:
@ -3825,7 +3831,7 @@ ibuf_insert(
switch (use) {
case IBUF_USE_NONE:
case IBUF_USE_INSERT:
return(FALSE);
DBUG_RETURN(FALSE);
case IBUF_USE_DELETE_MARK:
case IBUF_USE_DELETE:
case IBUF_USE_INSERT_DELETE_MARK:
@ -3841,7 +3847,7 @@ ibuf_insert(
case IBUF_USE_NONE:
case IBUF_USE_INSERT:
case IBUF_USE_INSERT_DELETE_MARK:
return(FALSE);
DBUG_RETURN(FALSE);
case IBUF_USE_DELETE_MARK:
case IBUF_USE_DELETE:
case IBUF_USE_ALL:
@ -3883,7 +3889,7 @@ check_watch:
is being buffered, have this request executed
directly on the page in the buffer pool after the
buffered entries for this page have been merged. */
return(FALSE);
DBUG_RETURN(FALSE);
}
}
@ -3894,7 +3900,7 @@ skip_watch:
>= page_get_free_space_of_empty(dict_table_is_comp(index->table))
/ 2) {
return(FALSE);
DBUG_RETURN(FALSE);
}
err = ibuf_insert_low(BTR_MODIFY_PREV, op, no_counter,
@ -3911,20 +3917,21 @@ skip_watch:
/* fprintf(stderr, "Ibuf insert for page no %lu of index %s\n",
page_no, index->name); */
#endif
return(TRUE);
DBUG_RETURN(TRUE);
} else {
ut_a(err == DB_STRONG_FAIL || err == DB_TOO_BIG_RECORD);
return(FALSE);
DBUG_RETURN(FALSE);
}
}
/********************************************************************//**
During merge, inserts to an index page a secondary index entry extracted
from the insert buffer. */
from the insert buffer.
@return newly inserted record */
static __attribute__((nonnull))
void
rec_t*
ibuf_insert_to_index_page_low(
/*==========================*/
const dtuple_t* entry, /*!< in: buffered entry to insert */
@ -3943,22 +3950,31 @@ ibuf_insert_to_index_page_low(
ulint zip_size;
const page_t* bitmap_page;
ulint old_bits;
rec_t* rec;
DBUG_ENTER("ibuf_insert_to_index_page_low");
if (page_cur_tuple_insert(
page_cur, entry, index, offsets, &heap, 0, mtr) != NULL) {
return;
rec = page_cur_tuple_insert(page_cur, entry, index,
offsets, &heap, 0, mtr);
if (rec != NULL) {
DBUG_RETURN(rec);
}
/* Page reorganization or recompression should already have
been attempted by page_cur_tuple_insert(). Besides, per
ibuf_index_page_calc_free_zip() the page should not have been
recompressed or reorganized. */
ut_ad(!buf_block_get_page_zip(block));
/* If the record did not fit, reorganize */
btr_page_reorganize(block, index, mtr);
page_cur_search(block, index, entry, PAGE_CUR_LE, page_cur);
btr_page_reorganize(page_cur, index, mtr);
/* This time the record must fit */
if (page_cur_tuple_insert(page_cur, entry, index,
offsets, &heap, 0, mtr) != NULL) {
return;
rec = page_cur_tuple_insert(page_cur, entry, index,
offsets, &heap, 0, mtr);
if (rec != NULL) {
DBUG_RETURN(rec);
}
page = buf_block_get_frame(block);
@ -3992,6 +4008,7 @@ ibuf_insert_to_index_page_low(
fputs("InnoDB: Submit a detailed bug report"
" to http://bugs.mysql.com\n", stderr);
ut_ad(0);
DBUG_RETURN(NULL);
}
/************************************************************************
@ -4014,6 +4031,13 @@ ibuf_insert_to_index_page(
ulint* offsets;
mem_heap_t* heap;
DBUG_ENTER("ibuf_insert_to_index_page");
DBUG_PRINT("ibuf", ("page_no: %ld", buf_block_get_page_no(block)));
DBUG_PRINT("ibuf", ("index name: %s", index->name));
DBUG_PRINT("ibuf", ("online status: %d",
dict_index_get_online_status(index)));
ut_ad(ibuf_inside(mtr));
ut_ad(dtuple_check_typed(entry));
ut_ad(!buf_block_align(page)->index);
@ -4057,7 +4081,7 @@ dump:
"InnoDB: Submit a detailed bug report to"
" http://bugs.mysql.com!\n", stderr);
return;
DBUG_VOID_RETURN;
}
low_match = page_cur_search(block, index, entry,
@ -4105,15 +4129,19 @@ dump:
if (!row_upd_changes_field_size_or_external(index, offsets,
update)
&& (!page_zip || btr_cur_update_alloc_zip(
page_zip, block, index,
rec_offs_size(offsets), FALSE, mtr))) {
page_zip, &page_cur, index, offsets,
rec_offs_size(offsets), false, mtr))) {
/* This is the easy case. Do something similar
to btr_cur_update_in_place(). */
rec = page_cur_get_rec(&page_cur);
row_upd_rec_in_place(rec, index, offsets,
update, page_zip);
goto updated_in_place;
}
/* btr_cur_update_alloc_zip() may have changed this */
rec = page_cur_get_rec(&page_cur);
/* A collation may identify values that differ in
storage length.
Some examples (1 or 2 bytes):
@ -4136,10 +4164,11 @@ dump:
lock_rec_store_on_page_infimum(block, rec);
page_cur_delete_rec(&page_cur, index, offsets, mtr);
page_cur_move_to_prev(&page_cur);
ibuf_insert_to_index_page_low(entry, block, index,
rec = ibuf_insert_to_index_page_low(entry, block, index,
&offsets, heap, mtr,
&page_cur);
ut_ad(!cmp_dtuple_rec(entry, rec, offsets));
lock_rec_restore_from_page_infimum(block, rec, block);
} else {
offsets = NULL;
@ -4147,9 +4176,10 @@ dump:
&offsets, heap, mtr,
&page_cur);
}
updated_in_place:
mem_heap_free(heap);
DBUG_VOID_RETURN;
}
/****************************************************************//**
@ -4378,7 +4408,7 @@ Deletes from ibuf the record on which pcur is positioned. If we have to
resort to a pessimistic delete, this function commits mtr and closes
the cursor.
@return TRUE if mtr was committed and pcur closed in this operation */
static
static __attribute__((warn_unused_result))
ibool
ibuf_delete_rec(
/*============*/
@ -4411,7 +4441,7 @@ ibuf_delete_rec(
btr_cur_set_deleted_flag_for_ibuf(
btr_pcur_get_rec(pcur), NULL, TRUE, mtr);
ibuf_mtr_commit(mtr);
log_write_up_to(IB_ULONGLONG_MAX, LOG_WAIT_ALL_GROUPS, TRUE);
log_write_up_to(LSN_MAX, LOG_WAIT_ALL_GROUPS, TRUE);
DBUG_SUICIDE();
}
#endif /* UNIV_DEBUG || UNIV_IBUF_DEBUG */
@ -4420,7 +4450,7 @@ ibuf_delete_rec(
0, mtr);
if (success) {
if (UNIV_UNLIKELY(!page_get_n_recs(btr_pcur_get_page(pcur)))) {
if (page_is_empty(btr_pcur_get_page(pcur))) {
/* If a B-tree page is empty, it must be the root page
and the whole B-tree must be empty. InnoDB does not
allow empty B-tree pages other than the root. */
@ -4433,7 +4463,7 @@ ibuf_delete_rec(
/* ibuf->empty is protected by the root page latch.
Before the deletion, it had to be FALSE. */
ut_ad(!ibuf->empty);
ibuf->empty = TRUE;
ibuf->empty = true;
}
#ifdef UNIV_IBUF_COUNT_DEBUG
@ -4484,7 +4514,7 @@ ibuf_delete_rec(
ibuf_size_update(root, mtr);
mutex_exit(&ibuf_mutex);
ibuf->empty = (page_get_n_recs(root) == 0);
ibuf->empty = page_is_empty(root);
ibuf_btr_pcur_commit_specify_mtr(pcur, mtr);
func_exit:
@ -4677,6 +4707,12 @@ ibuf_merge_or_delete_for_page(
loop:
ibuf_mtr_start(&mtr);
/* Position pcur in the insert buffer at the first entry for this
index page */
btr_pcur_open_on_user_rec(
ibuf->index, search_tuple, PAGE_CUR_GE, BTR_MODIFY_LEAF,
&pcur, &mtr);
if (block) {
ibool success;
@ -4695,12 +4731,6 @@ loop:
buf_block_dbg_add_level(block, SYNC_IBUF_TREE_NODE);
}
/* Position pcur in the insert buffer at the first entry for this
index page */
btr_pcur_open_on_user_rec(
ibuf->index, search_tuple, PAGE_CUR_GE, BTR_MODIFY_LEAF,
&pcur, &mtr);
if (!btr_pcur_is_on_user_rec(&pcur)) {
ut_ad(btr_pcur_is_after_last_in_tree(&pcur, &mtr));
@ -4785,6 +4815,16 @@ loop:
== page_no);
ut_ad(ibuf_rec_get_space(&mtr, rec) == space);
/* Mark the change buffer record processed,
so that it will not be merged again in case
the server crashes between the following
mtr_commit() and the subsequent mtr_commit()
of deleting the change buffer record. */
btr_cur_set_deleted_flag_for_ibuf(
btr_pcur_get_rec(&pcur), NULL,
TRUE, &mtr);
btr_pcur_store_position(&pcur, &mtr);
ibuf_btr_pcur_commit_specify_mtr(&pcur, &mtr);
@ -4832,6 +4872,7 @@ loop:
/* Deletion was pessimistic and mtr was committed:
we start from the beginning again */
ut_ad(mtr.state == MTR_COMMITTED);
goto loop;
} else if (btr_pcur_is_after_last_on_page(&pcur)) {
ibuf_mtr_commit(&mtr);
@ -4962,6 +5003,7 @@ loop:
/* Deletion was pessimistic and mtr was committed:
we start from the beginning again */
ut_ad(mtr.state == MTR_COMMITTED);
goto loop;
}
@ -4991,13 +5033,13 @@ leave_loop:
/******************************************************************//**
Looks if the insert buffer is empty.
@return TRUE if empty */
@return true if empty */
UNIV_INTERN
ibool
bool
ibuf_is_empty(void)
/*===============*/
{
ibool is_empty;
bool is_empty;
const page_t* root;
mtr_t mtr;
@ -5007,7 +5049,7 @@ ibuf_is_empty(void)
root = ibuf_tree_root_get(&mtr);
mutex_exit(&ibuf_mutex);
is_empty = (page_get_n_recs(root) == 0);
is_empty = page_is_empty(root);
ut_a(is_empty == ibuf->empty);
ibuf_mtr_commit(&mtr);

View file

@ -728,7 +728,9 @@ ib_col_set_value(
ib_tpl_t ib_tpl, /*!< in: tuple instance */
ib_ulint_t col_no, /*!< in: column index in tuple */
const void* src, /*!< in: data value */
ib_ulint_t len); /*!< in: data value len */
ib_ulint_t len, /*!< in: data value len */
ib_bool_t need_cpy); /*!< in: if need memcpy */
/*****************************************************************//**
Get the size of the data available in the column the tuple.

View file

@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 1994, 2012, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 1994, 2013, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2012, Facebook Inc.
This program is free software; you can redistribute it and/or modify it under
@ -450,18 +450,48 @@ btr_root_raise_and_insert(
__attribute__((nonnull, warn_unused_result));
/*************************************************************//**
Reorganizes an index page.
IMPORTANT: if btr_page_reorganize() is invoked on a compressed leaf
page of a non-clustered index, the caller must update the insert
buffer free bits in the same mini-transaction in such a way that the
modification will be redo-logged.
@return TRUE on success, FALSE on failure */
IMPORTANT: On success, the caller will have to update IBUF_BITMAP_FREE
if this is a compressed leaf page in a secondary index. This has to
be done either within the same mini-transaction, or by invoking
ibuf_reset_free_bits() before mtr_commit(). On uncompressed pages,
IBUF_BITMAP_FREE is unaffected by reorganization.
@retval true if the operation was successful
@retval false if it is a compressed page, and recompression failed */
UNIV_INTERN
ibool
bool
btr_page_reorganize_low(
/*====================*/
bool recovery,/*!< in: true if called in recovery:
locks should not be updated, i.e.,
there cannot exist locks on the
page, and a hash index should not be
dropped: it cannot exist */
ulint z_level,/*!< in: compression level to be used
if dealing with compressed page */
page_cur_t* cursor, /*!< in/out: page cursor */
dict_index_t* index, /*!< in: the index tree of the page */
mtr_t* mtr) /*!< in/out: mini-transaction */
__attribute__((nonnull, warn_unused_result));
/*************************************************************//**
Reorganizes an index page.
IMPORTANT: On success, the caller will have to update IBUF_BITMAP_FREE
if this is a compressed leaf page in a secondary index. This has to
be done either within the same mini-transaction, or by invoking
ibuf_reset_free_bits() before mtr_commit(). On uncompressed pages,
IBUF_BITMAP_FREE is unaffected by reorganization.
@retval true if the operation was successful
@retval false if it is a compressed page, and recompression failed */
UNIV_INTERN
bool
btr_page_reorganize(
/*================*/
buf_block_t* block, /*!< in: page to be reorganized */
dict_index_t* index, /*!< in: record descriptor */
mtr_t* mtr) /*!< in: mtr */
page_cur_t* cursor, /*!< in/out: page cursor */
dict_index_t* index, /*!< in: the index tree of the page */
mtr_t* mtr) /*!< in/out: mini-transaction */
__attribute__((nonnull));
/*************************************************************//**
Decides if the page should be split at the convergence point of

View file

@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 1994, 2012, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 1994, 2013, Oracle and/or its affiliates. All Rights Reserved.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -45,7 +45,11 @@ enum {
BTR_KEEP_POS_FLAG = 8,
/** the caller is creating the index or wants to bypass the
index->info.online creation log */
BTR_CREATE_FLAG = 16
BTR_CREATE_FLAG = 16,
/** the caller of btr_cur_optimistic_update() or
btr_cur_update_in_place() will take care of
updating IBUF_BITMAP_FREE */
BTR_KEEP_IBUF_BITMAP = 32
};
#ifndef UNIV_HOTBACKUP
@ -225,10 +229,11 @@ btr_cur_optimistic_insert(
NULL */
ulint n_ext, /*!< in: number of externally stored columns */
que_thr_t* thr, /*!< in: query thread or NULL */
mtr_t* mtr) /*!< in: mtr; if this function returns
DB_SUCCESS on a leaf page of a secondary
index in a compressed tablespace, the
mtr must be committed before latching
mtr_t* mtr) /*!< in/out: mini-transaction;
if this function returns DB_SUCCESS on
a leaf page of a secondary index in a
compressed tablespace, the caller must
mtr_commit(mtr) before latching
any further pages */
__attribute__((nonnull(2,3,4,5,6,7,10), warn_unused_result));
/*************************************************************//**
@ -260,27 +265,48 @@ btr_cur_pessimistic_insert(
NULL */
ulint n_ext, /*!< in: number of externally stored columns */
que_thr_t* thr, /*!< in: query thread or NULL */
mtr_t* mtr) /*!< in: mtr */
mtr_t* mtr) /*!< in/out: mini-transaction */
__attribute__((nonnull(2,3,4,5,6,7,10), warn_unused_result));
/*************************************************************//**
See if there is enough place in the page modification log to log
an update-in-place.
@return TRUE if enough place */
@retval false if out of space; IBUF_BITMAP_FREE will be reset
outside mtr if the page was recompressed
@retval true if enough place;
IMPORTANT: The caller will have to update IBUF_BITMAP_FREE if this is
a secondary index leaf page. This has to be done either within the
same mini-transaction, or by invoking ibuf_reset_free_bits() before
mtr_commit(mtr). */
UNIV_INTERN
ibool
btr_cur_update_alloc_zip(
/*=====================*/
bool
btr_cur_update_alloc_zip_func(
/*==========================*/
page_zip_des_t* page_zip,/*!< in/out: compressed page */
buf_block_t* block, /*!< in/out: buffer page */
dict_index_t* index, /*!< in: the index corresponding to the block */
page_cur_t* cursor, /*!< in/out: B-tree page cursor */
dict_index_t* index, /*!< in: the index corresponding to cursor */
#ifdef UNIV_DEBUG
ulint* offsets,/*!< in/out: offsets of the cursor record */
#endif /* UNIV_DEBUG */
ulint length, /*!< in: size needed */
ibool create, /*!< in: TRUE=delete-and-insert,
FALSE=update-in-place */
mtr_t* mtr) /*!< in: mini-transaction */
bool create, /*!< in: true=delete-and-insert,
false=update-in-place */
mtr_t* mtr) /*!< in/out: mini-transaction */
__attribute__((nonnull, warn_unused_result));
#ifdef UNIV_DEBUG
# define btr_cur_update_alloc_zip(page_zip,cursor,index,offsets,len,cr,mtr) \
btr_cur_update_alloc_zip_func(page_zip,cursor,index,offsets,len,cr,mtr)
#else /* UNIV_DEBUG */
# define btr_cur_update_alloc_zip(page_zip,cursor,index,offsets,len,cr,mtr) \
btr_cur_update_alloc_zip_func(page_zip,cursor,index,len,cr,mtr)
#endif /* UNIV_DEBUG */
/*************************************************************//**
Updates a record when the update causes no size changes in its fields.
@return DB_SUCCESS or error number */
@return locking or undo log related error code, or
@retval DB_SUCCESS on success
@retval DB_ZIP_OVERFLOW if there is not enough space left
on the compressed page (IBUF_BITMAP_FREE was reset outside mtr) */
UNIV_INTERN
dberr_t
btr_cur_update_in_place(
@ -289,24 +315,28 @@ btr_cur_update_in_place(
btr_cur_t* cursor, /*!< in: cursor on the record to update;
cursor stays valid and positioned on the
same record */
const ulint* offsets,/*!< in: offsets on cursor->page_cur.rec */
ulint* offsets,/*!< in/out: offsets on cursor->page_cur.rec */
const upd_t* update, /*!< in: update vector */
ulint cmpl_info,/*!< in: compiler info on secondary index
updates */
que_thr_t* thr, /*!< in: query thread, or NULL if
appropriate flags are set */
que_thr_t* thr, /*!< in: query thread */
trx_id_t trx_id, /*!< in: transaction id */
mtr_t* mtr) /*!< in: mtr; must be committed before
latching any further pages */
__attribute__((warn_unused_result, nonnull(2,3,4,8)));
mtr_t* mtr) /*!< in/out: mini-transaction; if this
is a secondary index, the caller must
mtr_commit(mtr) before latching any
further pages */
__attribute__((warn_unused_result, nonnull));
/*************************************************************//**
Tries to update a record on a page in an index tree. It is assumed that mtr
holds an x-latch on the page. The operation does not succeed if there is too
little space on the page or if the update would result in too empty a page,
so that tree compression is recommended.
@return DB_SUCCESS, or DB_OVERFLOW if the updated record does not fit,
DB_UNDERFLOW if the page would become too empty, or DB_ZIP_OVERFLOW if
there is not enough space left on the compressed page */
@return error code, including
@retval DB_SUCCESS on success
@retval DB_OVERFLOW if the updated record does not fit
@retval DB_UNDERFLOW if the page would become too empty
@retval DB_ZIP_OVERFLOW if there is not enough space left
on the compressed page */
UNIV_INTERN
dberr_t
btr_cur_optimistic_update(
@ -316,17 +346,18 @@ btr_cur_optimistic_update(
cursor stays valid and positioned on the
same record */
ulint** offsets,/*!< out: offsets on cursor->page_cur.rec */
mem_heap_t** heap, /*!< in/out: pointer to memory heap, or NULL */
mem_heap_t** heap, /*!< in/out: pointer to NULL or memory heap */
const upd_t* update, /*!< in: update vector; this must also
contain trx id and roll ptr fields */
ulint cmpl_info,/*!< in: compiler info on secondary index
updates */
que_thr_t* thr, /*!< in: query thread, or NULL if
appropriate flags are set */
que_thr_t* thr, /*!< in: query thread */
trx_id_t trx_id, /*!< in: transaction id */
mtr_t* mtr) /*!< in: mtr; must be committed before
latching any further pages */
__attribute__((warn_unused_result, nonnull(2,3,4,5,9)));
mtr_t* mtr) /*!< in/out: mini-transaction; if this
is a secondary index, the caller must
mtr_commit(mtr) before latching any
further pages */
__attribute__((warn_unused_result, nonnull));
/*************************************************************//**
Performs an update of a record on a page of a tree. It is assumed
that mtr holds an x-latch on the tree and on the cursor page. If the
@ -356,12 +387,11 @@ btr_cur_pessimistic_update(
the values in update vector have no effect */
ulint cmpl_info,/*!< in: compiler info on secondary index
updates */
que_thr_t* thr, /*!< in: query thread, or NULL if
appropriate flags are set */
que_thr_t* thr, /*!< in: query thread */
trx_id_t trx_id, /*!< in: transaction id */
mtr_t* mtr) /*!< in: mtr; must be committed before
latching any further pages */
__attribute__((warn_unused_result, nonnull(2,3,4,5,6,7,11)));
mtr_t* mtr) /*!< in/out: mini-transaction; must be committed
before latching any further pages */
__attribute__((warn_unused_result, nonnull));
/***********************************************************//**
Marks a clustered index record deleted. Writes an undo log record to
undo log on this delete marking. Writes in the trx id field the id
@ -377,8 +407,8 @@ btr_cur_del_mark_set_clust_rec(
dict_index_t* index, /*!< in: clustered index of the record */
const ulint* offsets,/*!< in: rec_get_offsets(rec) */
que_thr_t* thr, /*!< in: query thread */
mtr_t* mtr) /*!< in: mtr */
__attribute__((nonnull));
mtr_t* mtr) /*!< in/out: mini-transaction */
__attribute__((nonnull, warn_unused_result));
/***********************************************************//**
Sets a secondary index record delete mark to TRUE or FALSE.
@return DB_SUCCESS, DB_LOCK_WAIT, or error number */
@ -390,7 +420,8 @@ btr_cur_del_mark_set_sec_rec(
btr_cur_t* cursor, /*!< in: cursor */
ibool val, /*!< in: value to set */
que_thr_t* thr, /*!< in: query thread */
mtr_t* mtr); /*!< in: mtr */
mtr_t* mtr) /*!< in/out: mini-transaction */
__attribute__((nonnull, warn_unused_result));
/*************************************************************//**
Tries to compress a page of the tree if it seems useful. It is assumed
that mtr holds an x-latch on the tree and on the cursor page. To avoid

View file

@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 1995, 2012, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 1995, 2013, Oracle and/or its affiliates. All Rights Reserved.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -104,9 +104,7 @@ extern buf_block_t* back_block2; /*!< second block, for page reorganize */
The enumeration values must be 0..7. */
enum buf_page_state {
BUF_BLOCK_ZIP_FREE = 0, /*!< contains a free
compressed page */
BUF_BLOCK_POOL_WATCH = 0, /*!< a sentinel for the buffer pool
BUF_BLOCK_POOL_WATCH, /*!< a sentinel for the buffer pool
watch, element of buf_pool->watch[] */
BUF_BLOCK_ZIP_PAGE, /*!< contains a clean
compressed page */
@ -897,7 +895,7 @@ buf_page_get_mutex(
Get the flush type of a page.
@return flush type */
UNIV_INLINE
enum buf_flush
buf_flush_t
buf_page_get_flush_type(
/*====================*/
const buf_page_t* bpage) /*!< in: buffer page */
@ -909,7 +907,7 @@ void
buf_page_set_flush_type(
/*====================*/
buf_page_t* bpage, /*!< in: buffer page */
enum buf_flush flush_type); /*!< in: flush type */
buf_flush_t flush_type); /*!< in: flush type */
/*********************************************************************//**
Map a block to a file page. */
UNIV_INLINE
@ -1451,7 +1449,7 @@ struct buf_page_t{
unsigned flush_type:2; /*!< if this block is currently being
flushed to disk, this tells the
flush_type.
@see enum buf_flush */
@see buf_flush_t */
unsigned io_fix:2; /*!< type of pending I/O operation;
also protected by buf_pool->mutex
@see enum buf_io_fix */
@ -1495,7 +1493,6 @@ struct buf_page_t{
- BUF_BLOCK_FILE_PAGE: flush_list
- BUF_BLOCK_ZIP_DIRTY: flush_list
- BUF_BLOCK_ZIP_PAGE: zip_clean
- BUF_BLOCK_ZIP_FREE: zip_free[]
If bpage is part of flush_list
then the node pointers are
@ -1729,6 +1726,26 @@ Compute the hash fold value for blocks in buf_pool->zip_hash. */
#define BUF_POOL_ZIP_FOLD_BPAGE(b) BUF_POOL_ZIP_FOLD((buf_block_t*) (b))
/* @} */
/** Struct that is embedded in the free zip blocks */
struct buf_buddy_free_t {
union {
ulint size; /*!< size of the block */
byte bytes[FIL_PAGE_DATA];
/*!< stamp[FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID]
== BUF_BUDDY_FREE_STAMP denotes a free
block. If the space_id field of buddy
block != BUF_BUDDY_FREE_STAMP, the block
is not in any zip_free list. If the
space_id is BUF_BUDDY_FREE_STAMP then
stamp[0] will contain the
buddy block size. */
} stamp;
buf_page_t bpage; /*!< Embedded bpage descriptor */
UT_LIST_NODE_T(buf_buddy_free_t) list;
/*!< Node of zip_free list */
};
/** @brief The buffer pool statistics structure. */
struct buf_pool_stat_t{
ulint n_page_gets; /*!< number of page gets performed;
@ -1839,7 +1856,12 @@ struct buf_pool_t{
and bpage::list pointers when
the bpage is on flush_list. It
also protects writes to
bpage::oldest_modification */
bpage::oldest_modification and
flush_list_hp */
const buf_page_t* flush_list_hp;/*!< "hazard pointer"
used during scan of flush_list
while doing flush list batch.
Protected by flush_list_mutex */
UT_LIST_BASE_NODE_T(buf_page_t) flush_list;
/*!< base node of the modified block
list */
@ -1925,7 +1947,7 @@ struct buf_pool_t{
UT_LIST_BASE_NODE_T(buf_page_t) zip_clean;
/*!< unmodified compressed pages */
#endif /* UNIV_DEBUG || UNIV_BUF_DEBUG */
UT_LIST_BASE_NODE_T(buf_page_t) zip_free[BUF_BUDDY_SIZES_MAX];
UT_LIST_BASE_NODE_T(buf_buddy_free_t) zip_free[BUF_BUDDY_SIZES_MAX];
/*!< buddy free lists */
buf_page_t* watch;

View file

@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 1995, 2011, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 1995, 2013, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2008, Google Inc.
Portions of this file contain modifications contributed and copyrighted by
@ -205,7 +205,7 @@ buf_page_get_state(
#ifdef UNIV_DEBUG
switch (state) {
case BUF_BLOCK_ZIP_FREE:
case BUF_BLOCK_POOL_WATCH:
case BUF_BLOCK_ZIP_PAGE:
case BUF_BLOCK_ZIP_DIRTY:
case BUF_BLOCK_NOT_USED:
@ -245,7 +245,7 @@ buf_page_set_state(
enum buf_page_state old_state = buf_page_get_state(bpage);
switch (old_state) {
case BUF_BLOCK_ZIP_FREE:
case BUF_BLOCK_POOL_WATCH:
ut_error;
break;
case BUF_BLOCK_ZIP_PAGE:
@ -300,9 +300,7 @@ buf_page_in_file(
const buf_page_t* bpage) /*!< in: pointer to control block */
{
switch (buf_page_get_state(bpage)) {
case BUF_BLOCK_ZIP_FREE:
/* This is a free page in buf_pool->zip_free[].
Such pages should only be accessed by the buddy allocator. */
case BUF_BLOCK_POOL_WATCH:
ut_error;
break;
case BUF_BLOCK_ZIP_PAGE:
@ -347,7 +345,7 @@ buf_page_get_mutex(
buf_pool_t* buf_pool = buf_pool_from_bpage(bpage);
switch (buf_page_get_state(bpage)) {
case BUF_BLOCK_ZIP_FREE:
case BUF_BLOCK_POOL_WATCH:
ut_error;
return(NULL);
case BUF_BLOCK_ZIP_PAGE:
@ -362,12 +360,12 @@ buf_page_get_mutex(
Get the flush type of a page.
@return flush type */
UNIV_INLINE
enum buf_flush
buf_flush_t
buf_page_get_flush_type(
/*====================*/
const buf_page_t* bpage) /*!< in: buffer page */
{
enum buf_flush flush_type = (enum buf_flush) bpage->flush_type;
buf_flush_t flush_type = (buf_flush_t) bpage->flush_type;
#ifdef UNIV_DEBUG
switch (flush_type) {
@ -389,7 +387,7 @@ void
buf_page_set_flush_type(
/*====================*/
buf_page_t* bpage, /*!< in: buffer page */
enum buf_flush flush_type) /*!< in: flush type */
buf_flush_t flush_type) /*!< in: flush type */
{
bpage->flush_type = flush_type;
ut_ad(buf_page_get_flush_type(bpage) == flush_type);
@ -666,7 +664,7 @@ buf_block_get_frame(
ut_ad(block);
switch (buf_block_get_state(block)) {
case BUF_BLOCK_ZIP_FREE:
case BUF_BLOCK_POOL_WATCH:
case BUF_BLOCK_ZIP_PAGE:
case BUF_BLOCK_ZIP_DIRTY:
case BUF_BLOCK_NOT_USED:
@ -1311,7 +1309,7 @@ buf_page_release_zip(
bpage->buf_fix_count--;
mutex_exit(&block->mutex);
return;
case BUF_BLOCK_ZIP_FREE:
case BUF_BLOCK_POOL_WATCH:
case BUF_BLOCK_NOT_USED:
case BUF_BLOCK_READY_FOR_USE:
case BUF_BLOCK_MEMORY:

View file

@ -63,12 +63,13 @@ void
buf_dblwr_free(void);
/*================*/
/********************************************************************//**
Updates the doublewrite buffer when an IO request that is part of an
LRU or flush batch is completed. */
Updates the doublewrite buffer when an IO request is completed. */
UNIV_INTERN
void
buf_dblwr_update(void);
/*==================*/
buf_dblwr_update(
/*=============*/
const buf_page_t* bpage, /*!< in: buffer block descriptor */
buf_flush_t flush_type);/*!< in: flush type */
/****************************************************************//**
Determines if a page number is located inside the doublewrite buffer.
@return TRUE if the location is inside the two blocks of the
@ -109,36 +110,41 @@ UNIV_INTERN
void
buf_dblwr_write_single_page(
/*========================*/
buf_page_t* bpage); /*!< in: buffer block to write */
buf_page_t* bpage, /*!< in: buffer block to write */
bool sync); /*!< in: true if sync IO requested */
/** Doublewrite control struct */
struct buf_dblwr_t{
ib_mutex_t mutex; /*!< mutex protecting the first_free field and
write_buf */
ib_mutex_t mutex; /*!< mutex protecting the first_free
field and write_buf */
ulint block1; /*!< the page number of the first
doublewrite block (64 pages) */
ulint block2; /*!< page number of the second block */
ulint first_free; /*!< first free position in write_buf measured
in units of UNIV_PAGE_SIZE */
ulint s_reserved; /*!< number of slots currently reserved
for single page flushes. */
ulint b_reserved; /*!< number of slots currently reserved
ulint first_free;/*!< first free position in write_buf
measured in units of UNIV_PAGE_SIZE */
ulint b_reserved;/*!< number of slots currently reserved
for batch flush. */
ibool* in_use; /*!< flag used to indicate if a slot is
os_event_t b_event;/*!< event where threads wait for a
batch flush to end. */
ulint s_reserved;/*!< number of slots currently
reserved for single page flushes. */
os_event_t s_event;/*!< event where threads wait for a
single page flush slot. */
bool* in_use; /*!< flag used to indicate if a slot is
in use. Only used for single page
flushes. */
ibool batch_running; /*!< set to TRUE if currently a batch
bool batch_running;/*!< set to TRUE if currently a batch
is being written from the doublewrite
buffer. */
byte* write_buf; /*!< write buffer used in writing to the
byte* write_buf;/*!< write buffer used in writing to the
doublewrite buffer, aligned to an
address divisible by UNIV_PAGE_SIZE
(which is required by Windows aio) */
byte* write_buf_unaligned;
/*!< pointer to write_buf, but unaligned */
buf_page_t**
buf_block_arr; /*!< array to store pointers to the buffer
blocks which have been cached to write_buf */
byte* write_buf_unaligned;/*!< pointer to write_buf,
but unaligned */
buf_page_t** buf_block_arr;/*!< array to store pointers to
the buffer blocks which have been
cached to write_buf */
};

View file

@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 1995, 2011, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 1995, 2013, Oracle and/or its affiliates. All Rights Reserved.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -87,13 +87,6 @@ buf_flush_page_try(
buf_block_t* block) /*!< in/out: buffer control block */
__attribute__((nonnull, warn_unused_result));
# endif /* UNIV_DEBUG || UNIV_IBUF_DEBUG */
/********************************************************************//**
Flush a batch of writes to the datafiles that have already been
written by the OS. */
UNIV_INTERN
void
buf_flush_sync_datafiles(void);
/*==========================*/
/*******************************************************************//**
This utility flushes dirty blocks from the end of the flush list of
all buffer pool instances.
@ -136,7 +129,7 @@ void
buf_flush_wait_batch_end(
/*=====================*/
buf_pool_t* buf_pool, /*!< in: buffer pool instance */
enum buf_flush type); /*!< in: BUF_FLUSH_LRU
buf_flush_t type); /*!< in: BUF_FLUSH_LRU
or BUF_FLUSH_LIST */
/******************************************************************//**
Waits until a flush batch of the given type ends. This is called by
@ -147,7 +140,7 @@ void
buf_flush_wait_batch_end_wait_only(
/*===============================*/
buf_pool_t* buf_pool, /*!< in: buffer pool instance */
enum buf_flush type); /*!< in: BUF_FLUSH_LRU
buf_flush_t type); /*!< in: BUF_FLUSH_LRU
or BUF_FLUSH_LIST */
/********************************************************************//**
This function should be called at a mini-transaction commit, if a page was
@ -248,8 +241,20 @@ buf_flush_page(
/*===========*/
buf_pool_t* buf_pool, /*!< in: buffer pool instance */
buf_page_t* bpage, /*!< in: buffer control block */
buf_flush flush_type) /*!< in: type of flush */
buf_flush_t flush_type, /*!< in: type of flush */
bool sync) /*!< in: true if sync IO request */
__attribute__((nonnull));
/********************************************************************//**
Returns true if the block is modified and ready for flushing.
@return true if can flush immediately */
UNIV_INTERN
bool
buf_flush_ready_for_flush(
/*======================*/
buf_page_t* bpage, /*!< in: buffer control block, must be
buf_page_in_file(bpage) */
buf_flush_t flush_type)/*!< in: type of flush */
__attribute__((warn_unused_result));
#ifdef UNIV_DEBUG
/******************************************************************//**

View file

@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 1995, 2011, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 1995, 2013, Oracle and/or its affiliates. All Rights Reserved.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -79,19 +79,19 @@ buf_LRU_insert_zip_clean(
Try to free a block. If bpage is a descriptor of a compressed-only
page, the descriptor object will be freed as well.
NOTE: If this function returns TRUE, it will temporarily
NOTE: If this function returns true, it will temporarily
release buf_pool->mutex. Furthermore, the page frame will no longer be
accessible via bpage.
The caller must hold buf_pool->mutex and must not hold any
buf_page_get_mutex() when calling this function.
@return TRUE if freed, FALSE otherwise. */
@return true if freed, false otherwise. */
UNIV_INTERN
ibool
buf_LRU_free_block(
/*===============*/
bool
buf_LRU_free_page(
/*==============*/
buf_page_t* bpage, /*!< in: block to be freed */
ibool zip) /*!< in: TRUE if should remove also the
bool zip) /*!< in: true if should remove also the
compressed page of an uncompressed page */
__attribute__((nonnull));
/******************************************************************//**

View file

@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 1995, 2009, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 1995, 2013, Oracle and/or its affiliates. All Rights Reserved.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -119,7 +119,7 @@ UNIV_INTERN
void
buf_read_ibuf_merge_pages(
/*======================*/
ibool sync, /*!< in: TRUE if the caller
bool sync, /*!< in: true if the caller
wants this function to wait
for the highest address page
to get read in, before this

View file

@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 1995, 2011, Oracle and/or its affiliates. All Rights Reserved
Copyright (c) 1995, 2013, Oracle and/or its affiliates. All Rights Reserved
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -45,7 +45,7 @@ struct buf_dblwr_t;
typedef byte buf_frame_t;
/** Flags for flush types */
enum buf_flush {
enum buf_flush_t {
BUF_FLUSH_LRU = 0, /*!< flush via the LRU list */
BUF_FLUSH_LIST, /*!< flush via the flush list
of dirty blocks */

View file

@ -124,6 +124,9 @@ enum dberr_t {
during online index creation */
DB_IO_ERROR, /*!< Generic IO error */
DB_IDENTIFIER_TOO_LONG, /*!< Identifier name too long */
DB_FTS_EXCEED_RESULT_CACHE_LIMIT, /*!< FTS query memory
exceeds result cache limit */
/* The following are partial failure codes */
DB_FAIL = 1000,

View file

@ -111,6 +111,20 @@ dberr_t
dict_create_or_check_foreign_constraint_tables(void);
/*================================================*/
/********************************************************************//**
Generate a foreign key constraint name when it was not named by the user.
A generated constraint has a name of the format dbname/tablename_ibfk_NUMBER,
where the numbers start from 1, and are given locally for this table, that is,
the number is not global, as it used to be before MySQL 4.0.18. */
UNIV_INLINE
dberr_t
dict_create_add_foreign_id(
/*=======================*/
ulint* id_nr, /*!< in/out: number to use in id generation;
incremented if used */
const char* name, /*!< in: table name */
dict_foreign_t* foreign)/*!< in/out: foreign key */
__attribute__((nonnull));
/********************************************************************//**
Adds foreign key definitions to data dictionary tables in the database. We
look at table->foreign_list, and also generate names to constraints that were
not named by the user. A generated constraint has a name of the format
@ -158,24 +172,14 @@ dict_create_add_tablespace_to_dictionary(
bool commit); /*!< in: if true then commit the
transaction */
/********************************************************************//**
Table create node structure */
/********************************************************************//**
Add a single foreign key definition to the data dictionary tables in the
database. We also generate names to constraints that were not named by the
user. A generated constraint has a name of the format
databasename/tablename_ibfk_NUMBER, where the numbers start from 1, and
are given locally for this table, that is, the number is not global, as in
the old format constraints < 4.0.18 it used to be.
Add a foreign key definition to the data dictionary tables.
@return error code or DB_SUCCESS */
UNIV_INTERN
dberr_t
dict_create_add_foreign_to_dictionary(
/*==================================*/
ulint* id_nr, /*!< in/out: number to use in id generation;
incremented if used */
dict_table_t* table, /*!< in: table */
dict_foreign_t* foreign,/*!< in: foreign */
const char* name, /*!< in: table name */
const dict_foreign_t* foreign,/*!< in: foreign key */
trx_t* trx) /*!< in/out: dictionary transaction */
__attribute__((nonnull, warn_unused_result));

View file

@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 1996, 2009, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 1996, 2012, Oracle and/or its affiliates. All Rights Reserved.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -23,3 +23,76 @@ Database object creation
Created 1/8/1996 Heikki Tuuri
*******************************************************/
#include "mem0mem.h"
/*********************************************************************//**
Checks if a table name contains the string "/#sql" which denotes temporary
tables in MySQL.
@return true if temporary table */
UNIV_INTERN
bool
row_is_mysql_tmp_table_name(
/*========================*/
const char* name) __attribute__((warn_unused_result));
/*!< in: table name in the form
'database/tablename' */
/********************************************************************//**
Generate a foreign key constraint name when it was not named by the user.
A generated constraint has a name of the format dbname/tablename_ibfk_NUMBER,
where the numbers start from 1, and are given locally for this table, that is,
the number is not global, as it used to be before MySQL 4.0.18. */
UNIV_INLINE
dberr_t
dict_create_add_foreign_id(
/*=======================*/
ulint* id_nr, /*!< in/out: number to use in id generation;
incremented if used */
const char* name, /*!< in: table name */
dict_foreign_t* foreign)/*!< in/out: foreign key */
{
if (foreign->id == NULL) {
/* Generate a new constraint id */
ulint namelen = strlen(name);
char* id = static_cast<char*>(
mem_heap_alloc(foreign->heap,
namelen + 20));
if (row_is_mysql_tmp_table_name(name)) {
/* no overflow if number < 1e13 */
sprintf(id, "%s_ibfk_%lu", name,
(ulong) (*id_nr)++);
} else {
char table_name[MAX_TABLE_NAME_LEN + 20] = "";
uint errors = 0;
strncpy(table_name, name,
MAX_TABLE_NAME_LEN + 20);
innobase_convert_to_system_charset(
strchr(table_name, '/') + 1,
strchr(name, '/') + 1,
MAX_TABLE_NAME_LEN, &errors);
if (errors) {
strncpy(table_name, name,
MAX_TABLE_NAME_LEN + 20);
}
/* no overflow if number < 1e13 */
sprintf(id, "%s_ibfk_%lu", table_name,
(ulong) (*id_nr)++);
if (innobase_check_identifier_length(
strchr(id,'/') + 1)) {
return(DB_IDENTIFIER_TOO_LONG);
}
}
foreign->id = id;
}
return(DB_SUCCESS);
}

View file

@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 1996, 2012, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 1996, 2013, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2012, Facebook Inc.
This program is free software; you can redistribute it and/or modify it under
@ -108,6 +108,18 @@ dict_remove_db_name(
const char* name) /*!< in: table name in the form
dbname '/' tablename */
__attribute__((nonnull, warn_unused_result));
/** Operation to perform when opening a table */
enum dict_table_op_t {
/** Expect the tablespace to exist. */
DICT_TABLE_OP_NORMAL = 0,
/** Drop any orphan indexes after an aborted online index creation */
DICT_TABLE_OP_DROP_ORPHAN,
/** Silently load the tablespace if it does not exist,
and do not load the definitions of incomplete indexes. */
DICT_TABLE_OP_LOAD_TABLESPACE
};
/**********************************************************************//**
Returns a table object based on table id.
@return table, NULL if does not exist */
@ -117,9 +129,7 @@ dict_table_open_on_id(
/*==================*/
table_id_t table_id, /*!< in: table id */
ibool dict_locked, /*!< in: TRUE=data dictionary locked */
ibool try_drop) /*!< in: TRUE=try to drop any orphan
indexes after an aborted online
index creation */
dict_table_op_t table_op) /*!< in: operation to perform */
__attribute__((warn_unused_result));
/********************************************************************//**
Decrements the count of open handles to a table. */
@ -408,10 +418,17 @@ UNIV_INTERN
dberr_t
dict_foreign_add_to_cache(
/*======================*/
dict_foreign_t* foreign, /*!< in, own: foreign key constraint */
ibool check_charsets) /*!< in: TRUE=check charset
dict_foreign_t* foreign,
/*!< in, own: foreign key constraint */
const char** col_names,
/*!< in: column names, or NULL to use
foreign->foreign_table->col_names */
bool check_charsets,
/*!< in: whether to check charset
compatibility */
__attribute__((nonnull, warn_unused_result));
dict_err_ignore_t ignore_err)
/*!< in: error to be ignored */
__attribute__((nonnull(1), warn_unused_result));
/*********************************************************************//**
Check if the index is referenced by a foreign key, if TRUE return the
matching instance NULL otherwise.
@ -435,15 +452,18 @@ dict_table_is_referenced_by_foreign_key(
__attribute__((nonnull, warn_unused_result));
/**********************************************************************//**
Replace the index passed in with another equivalent index in the
foreign key lists of the table. */
foreign key lists of the table.
@return whether all replacements were found */
UNIV_INTERN
void
bool
dict_foreign_replace_index(
/*=======================*/
dict_table_t* table, /*!< in/out: table */
const dict_index_t* index, /*!< in: index to be replaced */
const trx_t* trx) /*!< in: transaction handle */
__attribute__((nonnull));
const char** col_names,
/*!< in: column names, or NULL
to use table->col_names */
const dict_index_t* index) /*!< in: index to be replaced */
__attribute__((nonnull(1,3), warn_unused_result));
/**********************************************************************//**
Determines whether a string starts with the specified keyword.
@return TRUE if str starts with keyword */
@ -544,13 +564,16 @@ dict_index_t*
dict_foreign_find_index(
/*====================*/
const dict_table_t* table, /*!< in: table */
const char** col_names,
/*!< in: column names, or NULL
to use table->col_names */
const char** columns,/*!< in: array of column names */
ulint n_cols, /*!< in: number of columns */
const dict_index_t* types_idx,
/*!< in: NULL or an index
whose types the column types
must match */
ibool check_charsets,
bool check_charsets,
/*!< in: whether to check
charsets. only has an effect
if types_idx != NULL */
@ -558,7 +581,7 @@ dict_foreign_find_index(
/*!< in: nonzero if none of
the columns must be declared
NOT NULL */
__attribute__((nonnull(1,2), warn_unused_result));
__attribute__((nonnull(1,3), warn_unused_result));
/**********************************************************************//**
Returns a column's name.
@return column name. NOTE: not guaranteed to stay valid if table is
@ -624,6 +647,9 @@ bool
dict_foreign_qualify_index(
/*====================*/
const dict_table_t* table, /*!< in: table */
const char** col_names,
/*!< in: column names, or NULL
to use table->col_names */
const char** columns,/*!< in: array of column names */
ulint n_cols, /*!< in: number of columns */
const dict_index_t* index, /*!< in: index to check */
@ -631,7 +657,7 @@ dict_foreign_qualify_index(
/*!< in: NULL or an index
whose types the column types
must match */
ibool check_charsets,
bool check_charsets,
/*!< in: whether to check
charsets. only has an effect
if types_idx != NULL */
@ -639,7 +665,7 @@ dict_foreign_qualify_index(
/*!< in: nonzero if none of
the columns must be declared
NOT NULL */
__attribute__((nonnull(1,2), warn_unused_result));
__attribute__((nonnull(1,3), warn_unused_result));
#ifdef UNIV_DEBUG
/********************************************************************//**
Gets the first index on the table (the clustered index).

View file

@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 1996, 2012, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 1996, 2013, Oracle and/or its affiliates. All Rights Reserved.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -58,6 +58,18 @@ enum dict_table_info_t {
is in the cache, if so, return it */
};
/** Check type for dict_check_tablespaces_and_store_max_id() */
enum dict_check_t {
/** No user tablespaces have been opened
(no crash recovery, no transactions recovered). */
DICT_CHECK_NONE_LOADED = 0,
/** Some user tablespaces may have been opened
(no crash recovery; recovered table locks for transactions). */
DICT_CHECK_SOME_LOADED,
/** All user tablespaces have been opened (crash recovery). */
DICT_CHECK_ALL_LOADED
};
/********************************************************************//**
In a crash recovery we already have all the tablespace objects created.
This function compares the space id information in the InnoDB data dictionary
@ -70,7 +82,7 @@ UNIV_INTERN
void
dict_check_tablespaces_and_store_max_id(
/*====================================*/
ibool in_crash_recovery); /*!< in: are we doing a crash recovery */
dict_check_t dict_check); /*!< in: how to check */
/********************************************************************//**
Finds the first table name in the given database.
@return own: table name, NULL if does not exist; the caller must free
@ -199,7 +211,9 @@ UNIV_INTERN
dict_table_t*
dict_load_table_on_id(
/*==================*/
table_id_t table_id); /*!< in: table id */
table_id_t table_id, /*!< in: table id */
dict_err_ignore_t ignore_err); /*!< in: errors to ignore
when loading the table */
/********************************************************************//**
This function is called when the database is booted.
Loads system table index definitions except for the clustered index which
@ -221,11 +235,15 @@ dberr_t
dict_load_foreigns(
/*===============*/
const char* table_name, /*!< in: table name */
ibool check_recursive,/*!< in: Whether to check recursive
load of tables chained by FK */
ibool check_charsets) /*!< in: TRUE=check charsets
compatibility */
__attribute__((nonnull, warn_unused_result));
const char** col_names, /*!< in: column names, or NULL
to use table->col_names */
bool check_recursive,/*!< in: Whether to check
recursive load of tables
chained by FK */
bool check_charsets, /*!< in: whether to check
charset compatibility */
dict_err_ignore_t ignore_err) /*!< in: error to be ignored */
__attribute__((nonnull(1), warn_unused_result));
/********************************************************************//**
Prints to the standard output information on all tables found in the data
dictionary system table. */

View file

@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 1996, 2012, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 1996, 2013, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2012, Facebook Inc.
This program is free software; you can redistribute it and/or modify it under
@ -226,7 +226,7 @@ This could result in rescursive calls and out of stack error eventually.
DICT_FK_MAX_RECURSIVE_LOAD defines the maximum number of recursive loads,
when exceeded, the child table will not be loaded. It will be loaded when
the foreign constraint check needs to be run. */
#define DICT_FK_MAX_RECURSIVE_LOAD 255
#define DICT_FK_MAX_RECURSIVE_LOAD 20
/** Similarly, when tables are chained together with foreign key constraints
with on cascading delete/update clause, delete from parent table could
@ -916,7 +916,9 @@ struct dict_table_t{
the background stats thread will detect this
and will eventually quit sooner */
byte stats_bg_flag;
/*!< see BG_STAT_* above */
/*!< see BG_STAT_* above.
Writes are covered by dict_sys->mutex.
Dirty reads are possible. */
/* @} */
/*----------------------*/
/**!< The following fields are used by the

View file

@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 2010, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2010, 2013, Oracle and/or its affiliates. All Rights Reserved.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -52,7 +52,9 @@ UNIV_INLINE
dict_table_t*
dict_table_open_on_id_low(
/*=====================*/
table_id_t table_id); /*!< in: table id */
table_id_t table_id, /*!< in: table id */
dict_err_ignore_t ignore_err); /*!< in: errors to ignore
when loading the table */
#ifndef UNIV_NONINL
#include "dict0priv.ic"

View file

@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 2010, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2010, 2013, Oracle and/or its affiliates. All Rights Reserved.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -73,7 +73,9 @@ UNIV_INLINE
dict_table_t*
dict_table_open_on_id_low(
/*======================*/
table_id_t table_id) /*!< in: table id */
table_id_t table_id, /*!< in: table id */
dict_err_ignore_t ignore_err) /*!< in: errors to ignore
when loading the table */
{
dict_table_t* table;
ulint fold;
@ -87,7 +89,7 @@ dict_table_open_on_id_low(
dict_table_t*, table, ut_ad(table->cached),
table->id == table_id);
if (table == NULL) {
table = dict_load_table_on_id(table_id);
table = dict_load_table_on_id(table_id, ignore_err);
}
ut_ad(!table || table->cached);

View file

@ -31,8 +31,7 @@ Created Jan 23, 2012 Vasil Dimov
/*********************************************************************//**
Set the persistent statistics flag for a given table. This is set only
in the in-memory table object and is not saved on disk. It will be read
from the .frm file upon first open from MySQL after a server restart.
dict_stats_set_persistent() @{ */
from the .frm file upon first open from MySQL after a server restart. */
UNIV_INLINE
void
dict_stats_set_persistent(
@ -61,11 +60,9 @@ dict_stats_set_persistent(
/* we rely on this assignment to be atomic */
table->stat_persistent = stat_persistent;
}
/* @} */
/*********************************************************************//**
Check whether persistent statistics is enabled for a given table.
dict_stats_is_persistent_enabled() @{
@return TRUE if enabled, FALSE otherwise */
UNIV_INLINE
ibool
@ -100,14 +97,12 @@ dict_stats_is_persistent_enabled(
return(srv_stats_persistent);
}
}
/* @} */
/*********************************************************************//**
Set the auto recalc flag for a given table (only honored for a persistent
stats enabled table). The flag is set only in the in-memory table object
and is not saved in InnoDB files. It will be read from the .frm file upon
first open from MySQL after a server restart.
dict_stats_auto_recalc_set() @{ */
first open from MySQL after a server restart. */
UNIV_INLINE
void
dict_stats_auto_recalc_set(
@ -131,11 +126,9 @@ dict_stats_auto_recalc_set(
/* we rely on this assignment to be atomic */
table->stats_auto_recalc = stats_auto_recalc;
}
/* @} */
/*********************************************************************//**
Check whether auto recalc is enabled for a given table.
dict_stats_auto_recalc_is_enabled() @{
@return TRUE if enabled, FALSE otherwise */
UNIV_INLINE
ibool
@ -155,11 +148,9 @@ dict_stats_auto_recalc_is_enabled(
return(srv_stats_auto_recalc);
}
}
/* @} */
/*********************************************************************//**
Initialize table's stats for the first time when opening a table.
dict_stats_init() @{ */
Initialize table's stats for the first time when opening a table. */
UNIV_INLINE
void
dict_stats_init(
@ -182,12 +173,10 @@ dict_stats_init(
dict_stats_update(table, opt);
}
/* @} */
/*********************************************************************//**
Deinitialize table's stats after the last close of the table. This is
used to detect "FLUSH TABLE" and refresh the stats upon next open.
dict_stats_deinit() @{ */
used to detect "FLUSH TABLE" and refresh the stats upon next open. */
UNIV_INLINE
void
dict_stats_deinit(
@ -245,6 +234,3 @@ dict_stats_deinit(
dict_table_stats_unlock(table, RW_X_LATCH);
}
/* @} */
/* vim: set foldmethod=marker foldmarker=@{,@}: */

View file

@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 2012, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2012, 2013, Oracle and/or its affiliates. All Rights Reserved.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -40,14 +40,12 @@ Add a table to the recalc pool, which is processed by the
background stats gathering thread. Only the table id is added to the
list, so the table can be closed after being enqueued and it will be
opened when needed. If the table does not exist later (has been DROPped),
then it will be removed from the pool and skipped.
dict_stats_recalc_pool_add() @{ */
then it will be removed from the pool and skipped. */
UNIV_INTERN
void
dict_stats_recalc_pool_add(
/*=======================*/
const dict_table_t* table); /*!< in: table to add */
/* @} */
/*****************************************************************//**
Delete a given table from the auto recalc pool.
@ -57,53 +55,63 @@ void
dict_stats_recalc_pool_del(
/*=======================*/
const dict_table_t* table); /*!< in: table to remove */
/* @} */
/** Yield the data dictionary latch when waiting
for the background thread to stop accessing a table.
@param trx transaction holding the data dictionary locks */
#define DICT_STATS_BG_YIELD(trx) do { \
row_mysql_unlock_data_dictionary(trx); \
os_thread_sleep(250000); \
row_mysql_lock_data_dictionary(trx); \
} while (0)
/*****************************************************************//**
Wait until background stats thread has stopped using the specified table(s).
Request the background collection of statistics to stop for a table.
@retval true when no background process is active
@retval false when it is not safe to modify the table definition */
UNIV_INLINE
bool
dict_stats_stop_bg(
/*===============*/
dict_table_t* table) /*!< in/out: table */
__attribute__((warn_unused_result));
/*****************************************************************//**
Wait until background stats thread has stopped using the specified table.
The caller must have locked the data dictionary using
row_mysql_lock_data_dictionary() and this function may unlock it temporarily
and restore the lock before it exits.
The background stats thead is guaranteed not to start using the specified
tables after this function returns and before the caller unlocks the data
The background stats thread is guaranteed not to start using the specified
table after this function returns and before the caller unlocks the data
dictionary because it sets the BG_STAT_IN_PROGRESS bit in table->stats_bg_flag
under dict_sys->mutex.
dict_stats_wait_bg_to_stop_using_table() @{ */
under dict_sys->mutex. */
UNIV_INTERN
void
dict_stats_wait_bg_to_stop_using_tables(
/*====================================*/
dict_table_t* table1, /*!< in/out: table1 */
dict_table_t* table2, /*!< in/out: table2, could be NULL */
dict_stats_wait_bg_to_stop_using_table(
/*===================================*/
dict_table_t* table, /*!< in/out: table */
trx_t* trx); /*!< in/out: transaction to use for
unlocking/locking the data dict */
/* @} */
/*****************************************************************//**
Initialize global variables needed for the operation of dict_stats_thread().
Must be called before dict_stats_thread() is started.
dict_stats_thread_init() @{ */
Must be called before dict_stats_thread() is started. */
UNIV_INTERN
void
dict_stats_thread_init();
/*====================*/
/* @} */
/*****************************************************************//**
Free resources allocated by dict_stats_thread_init(), must be called
after dict_stats_thread() has exited.
dict_stats_thread_deinit() @{ */
after dict_stats_thread() has exited. */
UNIV_INTERN
void
dict_stats_thread_deinit();
/*======================*/
/* @} */
/*****************************************************************//**
This is the thread for background stats gathering. It pops tables, from
the auto recalc list and proceeds them, eventually recalculating their
statistics.
dict_stats_thread() @{
@return this function does not return, it calls os_thread_exit() */
extern "C" UNIV_INTERN
os_thread_ret_t
@ -111,6 +119,9 @@ DECLARE_THREAD(dict_stats_thread)(
/*==============================*/
void* arg); /*!< in: a dummy parameter
required by os_thread_create */
/* @} */
# ifndef UNIV_NONINL
# include "dict0stats_bg.ic"
# endif
#endif /* dict0stats_bg_h */

View file

@ -0,0 +1,45 @@
/*****************************************************************************
Copyright (c) 2012, 2013, Oracle and/or its affiliates. All Rights Reserved.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
*****************************************************************************/
/**************************************************//**
@file include/dict0stats_bg.ic
Code used for background table and index stats gathering.
Created Feb 8, 2013 Marko Makela
*******************************************************/
/*****************************************************************//**
Request the background collection of statistics to stop for a table.
@retval true when no background process is active
@retval false when it is not safe to modify the table definition */
UNIV_INLINE
bool
dict_stats_stop_bg(
/*===============*/
dict_table_t* table) /*!< in/out: table */
{
ut_ad(!srv_read_only_mode);
ut_ad(mutex_own(&dict_sys->mutex));
if (!(table->stats_bg_flag & BG_STAT_IN_PROGRESS)) {
return(true);
}
table->stats_bg_flag |= BG_STAT_SHOULD_QUIT;
return(false);
}

View file

@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 1996, 2011, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 1996, 2013, Oracle and/or its affiliates. All Rights Reserved.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -57,6 +57,14 @@ enum dict_err_ignore_t {
DICT_ERR_IGNORE_INDEX_ROOT = 1, /*!< ignore error if index root
page is FIL_NULL or incorrect value */
DICT_ERR_IGNORE_CORRUPT = 2, /*!< skip corrupted indexes */
DICT_ERR_IGNORE_FK_NOKEY = 4, /*!< ignore error if any foreign
key is missing */
DICT_ERR_IGNORE_RECOVER_LOCK = 8,
/*!< Used when recovering table locks
for resurrected transactions.
Silently load a missing
tablespace, and do not load
incomplete index definitions. */
DICT_ERR_IGNORE_ALL = 0xFFFF /*!< ignore all errors */
};
@ -67,4 +75,11 @@ enum ib_quiesce_t {
QUIESCE_COMPLETE /*!< All done */
};
/** Prefix for tmp tables, adopted from sql/table.h */
#define tmp_file_prefix "#sql"
#define tmp_file_prefix_length 4
#define TEMP_TABLE_PREFIX "#sql"
#define TEMP_TABLE_PATH_PREFIX "/" TEMP_TABLE_PREFIX
#endif

View file

@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 1996, 2009, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 1996, 2013, Oracle and/or its affiliates. All Rights Reserved.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -46,15 +46,17 @@ UNIV_INLINE
dyn_array_t*
dyn_array_create(
/*=============*/
dyn_array_t* arr); /*!< in: pointer to a memory buffer of
dyn_array_t* arr) /*!< in/out memory buffer of
size sizeof(dyn_array_t) */
__attribute__((nonnull));
/************************************************************//**
Frees a dynamic array. */
UNIV_INLINE
void
dyn_array_free(
/*===========*/
dyn_array_t* arr); /*!< in: dyn array */
dyn_array_t* arr) /*!< in,own: dyn array */
__attribute__((nonnull));
/*********************************************************************//**
Makes room on top of a dyn array and returns a pointer to a buffer in it.
After copying the elements, the caller must close the buffer using
@ -65,8 +67,9 @@ byte*
dyn_array_open(
/*===========*/
dyn_array_t* arr, /*!< in: dynamic array */
ulint size); /*!< in: size in bytes of the buffer; MUST be
ulint size) /*!< in: size in bytes of the buffer; MUST be
smaller than DYN_ARRAY_DATA_SIZE! */
__attribute__((nonnull, warn_unused_result));
/*********************************************************************//**
Closes the buffer returned by dyn_array_open. */
UNIV_INLINE
@ -74,7 +77,8 @@ void
dyn_array_close(
/*============*/
dyn_array_t* arr, /*!< in: dynamic array */
byte* ptr); /*!< in: buffer space from ptr up was not used */
const byte* ptr) /*!< in: end of used space */
__attribute__((nonnull));
/*********************************************************************//**
Makes room on top of a dyn array and returns a pointer to
the added element. The caller must copy the element to
@ -84,8 +88,9 @@ UNIV_INLINE
void*
dyn_array_push(
/*===========*/
dyn_array_t* arr, /*!< in: dynamic array */
ulint size); /*!< in: size in bytes of the element */
dyn_array_t* arr, /*!< in/out: dynamic array */
ulint size) /*!< in: size in bytes of the element */
__attribute__((nonnull, warn_unused_result));
/************************************************************//**
Returns pointer to an element in dyn array.
@return pointer to element */
@ -93,9 +98,10 @@ UNIV_INLINE
void*
dyn_array_get_element(
/*==================*/
dyn_array_t* arr, /*!< in: dyn array */
ulint pos); /*!< in: position of element as bytes
from array start */
const dyn_array_t* arr, /*!< in: dyn array */
ulint pos) /*!< in: position of element
in bytes from array start */
__attribute__((nonnull, warn_unused_result));
/************************************************************//**
Returns the size of stored data in a dyn array.
@return data size in bytes */
@ -103,30 +109,33 @@ UNIV_INLINE
ulint
dyn_array_get_data_size(
/*====================*/
dyn_array_t* arr); /*!< in: dyn array */
const dyn_array_t* arr) /*!< in: dyn array */
__attribute__((nonnull, warn_unused_result, pure));
/************************************************************//**
Gets the first block in a dyn array. */
UNIV_INLINE
dyn_block_t*
dyn_array_get_first_block(
/*======================*/
dyn_array_t* arr); /*!< in: dyn array */
Gets the first block in a dyn array.
@param arr dyn array
@return first block */
#define dyn_array_get_first_block(arr) (arr)
/************************************************************//**
Gets the last block in a dyn array. */
UNIV_INLINE
dyn_block_t*
dyn_array_get_last_block(
/*=====================*/
dyn_array_t* arr); /*!< in: dyn array */
Gets the last block in a dyn array.
@param arr dyn array
@return last block */
#define dyn_array_get_last_block(arr) \
((arr)->heap ? UT_LIST_GET_LAST((arr)->base) : (arr))
/********************************************************************//**
Gets the next block in a dyn array.
@param arr dyn array
@param block dyn array block
@return pointer to next, NULL if end of list */
UNIV_INLINE
dyn_block_t*
dyn_array_get_next_block(
/*=====================*/
dyn_array_t* arr, /*!< in: dyn array */
dyn_block_t* block); /*!< in: dyn array block */
#define dyn_array_get_next_block(arr, block) \
((arr)->heap ? UT_LIST_GET_NEXT(list, block) : NULL)
/********************************************************************//**
Gets the previous block in a dyn array.
@param arr dyn array
@param block dyn array block
@return pointer to previous, NULL if end of list */
#define dyn_array_get_prev_block(arr, block) \
((arr)->heap ? UT_LIST_GET_PREV(list, block) : NULL)
/********************************************************************//**
Gets the number of used bytes in a dyn array block.
@return number of bytes used */
@ -134,7 +143,8 @@ UNIV_INLINE
ulint
dyn_block_get_used(
/*===============*/
dyn_block_t* block); /*!< in: dyn array block */
const dyn_block_t* block) /*!< in: dyn array block */
__attribute__((nonnull, warn_unused_result, pure));
/********************************************************************//**
Gets pointer to the start of data in a dyn array block.
@return pointer to data */
@ -142,16 +152,18 @@ UNIV_INLINE
byte*
dyn_block_get_data(
/*===============*/
dyn_block_t* block); /*!< in: dyn array block */
const dyn_block_t* block) /*!< in: dyn array block */
__attribute__((nonnull, warn_unused_result, pure));
/********************************************************//**
Pushes n bytes to a dyn array. */
UNIV_INLINE
void
dyn_push_string(
/*============*/
dyn_array_t* arr, /*!< in: dyn array */
dyn_array_t* arr, /*!< in/out: dyn array */
const byte* str, /*!< in: string to write */
ulint len); /*!< in: string length */
ulint len) /*!< in: string length */
__attribute__((nonnull));
/*#################################################################*/

View file

@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 1996, 2009, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 1996, 2013, Oracle and/or its affiliates. All Rights Reserved.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -35,56 +35,8 @@ UNIV_INTERN
dyn_block_t*
dyn_array_add_block(
/*================*/
dyn_array_t* arr); /*!< in: dyn array */
/************************************************************//**
Gets the first block in a dyn array. */
UNIV_INLINE
dyn_block_t*
dyn_array_get_first_block(
/*======================*/
dyn_array_t* arr) /*!< in: dyn array */
{
return(arr);
}
/************************************************************//**
Gets the last block in a dyn array. */
UNIV_INLINE
dyn_block_t*
dyn_array_get_last_block(
/*=====================*/
dyn_array_t* arr) /*!< in: dyn array */
{
if (arr->heap == NULL) {
return(arr);
}
return(UT_LIST_GET_LAST(arr->base));
}
/********************************************************************//**
Gets the next block in a dyn array.
@return pointer to next, NULL if end of list */
UNIV_INLINE
dyn_block_t*
dyn_array_get_next_block(
/*=====================*/
dyn_array_t* arr, /*!< in: dyn array */
dyn_block_t* block) /*!< in: dyn array block */
{
ut_ad(arr && block);
if (arr->heap == NULL) {
ut_ad(arr == block);
return(NULL);
}
return(UT_LIST_GET_NEXT(list, block));
}
dyn_array_t* arr) /*!< in/out: dyn array */
__attribute__((nonnull, warn_unused_result));
/********************************************************************//**
Gets the number of used bytes in a dyn array block.
@ -93,7 +45,7 @@ UNIV_INLINE
ulint
dyn_block_get_used(
/*===============*/
dyn_block_t* block) /*!< in: dyn array block */
const dyn_block_t* block) /*!< in: dyn array block */
{
ut_ad(block);
@ -107,11 +59,11 @@ UNIV_INLINE
byte*
dyn_block_get_data(
/*===============*/
dyn_block_t* block) /*!< in: dyn array block */
const dyn_block_t* block) /*!< in: dyn array block */
{
ut_ad(block);
return(block->data);
return(const_cast<byte*>(block->data));
}
/*********************************************************************//**
@ -121,7 +73,7 @@ UNIV_INLINE
dyn_array_t*
dyn_array_create(
/*=============*/
dyn_array_t* arr) /*!< in: pointer to a memory buffer of
dyn_array_t* arr) /*!< in/out: memory buffer of
size sizeof(dyn_array_t) */
{
ut_ad(arr);
@ -132,10 +84,9 @@ dyn_array_create(
arr->heap = NULL;
arr->used = 0;
#ifdef UNIV_DEBUG
arr->buf_end = 0;
arr->magic_n = DYN_BLOCK_MAGIC_N;
#endif
ut_d(arr->buf_end = 0);
ut_d(arr->magic_n = DYN_BLOCK_MAGIC_N);
return(arr);
}
@ -151,9 +102,7 @@ dyn_array_free(
mem_heap_free(arr->heap);
}
#ifdef UNIV_DEBUG
arr->magic_n = 0;
#endif
ut_d(arr->magic_n = 0);
}
/*********************************************************************//**
@ -164,7 +113,7 @@ UNIV_INLINE
void*
dyn_array_push(
/*===========*/
dyn_array_t* arr, /*!< in: dynamic array */
dyn_array_t* arr, /*!< in/out: dynamic array */
ulint size) /*!< in: size in bytes of the element */
{
dyn_block_t* block;
@ -176,24 +125,23 @@ dyn_array_push(
ut_ad(size);
block = arr;
used = block->used;
if (used + size > DYN_ARRAY_DATA_SIZE) {
if (block->used + size > DYN_ARRAY_DATA_SIZE) {
/* Get the last array block */
block = dyn_array_get_last_block(arr);
used = block->used;
if (used + size > DYN_ARRAY_DATA_SIZE) {
if (block->used + size > DYN_ARRAY_DATA_SIZE) {
block = dyn_array_add_block(arr);
}
}
used = block->used;
}
}
block->used = used + size;
ut_ad(block->used <= DYN_ARRAY_DATA_SIZE);
return((block->data) + used);
return(block->data + used);
}
/*********************************************************************//**
@ -210,7 +158,6 @@ dyn_array_open(
smaller than DYN_ARRAY_DATA_SIZE! */
{
dyn_block_t* block;
ulint used;
ut_ad(arr);
ut_ad(arr->magic_n == DYN_BLOCK_MAGIC_N);
@ -218,28 +165,23 @@ dyn_array_open(
ut_ad(size);
block = arr;
used = block->used;
if (used + size > DYN_ARRAY_DATA_SIZE) {
if (block->used + size > DYN_ARRAY_DATA_SIZE) {
/* Get the last array block */
block = dyn_array_get_last_block(arr);
used = block->used;
if (used + size > DYN_ARRAY_DATA_SIZE) {
if (block->used + size > DYN_ARRAY_DATA_SIZE) {
block = dyn_array_add_block(arr);
used = block->used;
ut_a(size <= DYN_ARRAY_DATA_SIZE);
}
}
ut_ad(block->used <= DYN_ARRAY_DATA_SIZE);
#ifdef UNIV_DEBUG
ut_ad(arr->buf_end == 0);
ut_d(arr->buf_end = block->used + size);
arr->buf_end = used + size;
#endif
return((block->data) + used);
return(block->data + block->used);
}
/*********************************************************************//**
@ -248,8 +190,8 @@ UNIV_INLINE
void
dyn_array_close(
/*============*/
dyn_array_t* arr, /*!< in: dynamic array */
byte* ptr) /*!< in: buffer space from ptr up was not used */
dyn_array_t* arr, /*!< in/out: dynamic array */
const byte* ptr) /*!< in: end of used space */
{
dyn_block_t* block;
@ -264,9 +206,7 @@ dyn_array_close(
ut_ad(block->used <= DYN_ARRAY_DATA_SIZE);
#ifdef UNIV_DEBUG
arr->buf_end = 0;
#endif
ut_d(arr->buf_end = 0);
}
/************************************************************//**
@ -276,12 +216,11 @@ UNIV_INLINE
void*
dyn_array_get_element(
/*==================*/
dyn_array_t* arr, /*!< in: dyn array */
ulint pos) /*!< in: position of element as bytes
from array start */
const dyn_array_t* arr, /*!< in: dyn array */
ulint pos) /*!< in: position of element
in bytes from array start */
{
dyn_block_t* block;
ulint used;
const dyn_block_t* block;
ut_ad(arr);
ut_ad(arr->magic_n == DYN_BLOCK_MAGIC_N);
@ -290,21 +229,23 @@ dyn_array_get_element(
block = dyn_array_get_first_block(arr);
if (arr->heap != NULL) {
used = dyn_block_get_used(block);
for (;;) {
ulint used = dyn_block_get_used(block);
if (pos < used) {
break;
}
while (pos >= used) {
pos -= used;
block = UT_LIST_GET_NEXT(list, block);
ut_ad(block);
used = dyn_block_get_used(block);
}
}
ut_ad(block);
ut_ad(dyn_block_get_used(block) >= pos);
return(block->data + pos);
return(const_cast<byte*>(block->data) + pos);
}
/************************************************************//**
@ -314,9 +255,9 @@ UNIV_INLINE
ulint
dyn_array_get_data_size(
/*====================*/
dyn_array_t* arr) /*!< in: dyn array */
const dyn_array_t* arr) /*!< in: dyn array */
{
dyn_block_t* block;
const dyn_block_t* block;
ulint sum = 0;
ut_ad(arr);
@ -344,7 +285,7 @@ UNIV_INLINE
void
dyn_push_string(
/*============*/
dyn_array_t* arr, /*!< in: dyn array */
dyn_array_t* arr, /*!< in/out: dyn array */
const byte* str, /*!< in: string to write */
ulint len) /*!< in: string length */
{

View file

@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 1995, 2012, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 1995, 2013, Oracle and/or its affiliates. All Rights Reserved.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -360,9 +360,11 @@ fil_write_flushed_lsn_to_data_files(
ulint arch_log_no); /*!< in: latest archived log file number */
/*******************************************************************//**
Reads the flushed lsn, arch no, and tablespace flag fields from a data
file at database startup. */
file at database startup.
@retval NULL on success, or if innodb_force_recovery is set
@return pointer to an error message string */
UNIV_INTERN
void
const char*
fil_read_first_page(
/*================*/
os_file_t data_file, /*!< in: open data file */
@ -379,8 +381,9 @@ fil_read_first_page(
#endif /* UNIV_LOG_ARCHIVE */
lsn_t* min_flushed_lsn, /*!< out: min of flushed
lsn values in data files */
lsn_t* max_flushed_lsn); /*!< out: max of flushed
lsn_t* max_flushed_lsn) /*!< out: max of flushed
lsn values in data files */
__attribute__((warn_unused_result));
/*******************************************************************//**
Increments the count of pending operation, if space is not being deleted.
@return TRUE if being deleted, and operation should be skipped */
@ -728,7 +731,7 @@ fil_io(
because i/os are not actually handled until
all have been posted: use with great
caution! */
ibool sync, /*!< in: TRUE if synchronous aio is desired */
bool sync, /*!< in: true if synchronous aio is desired */
ulint space_id, /*!< in: space id */
ulint zip_size, /*!< in: compressed page size in bytes;
0 for uncompressed pages */
@ -977,8 +980,10 @@ fil_mtr_rename_log(
ulint new_space_id, /*!< in: tablespace id of the new
table */
const char* new_name, /*!< in: new table name */
const char* tmp_name); /*!< in: temp table name used while
const char* tmp_name, /*!< in: temp table name used while
swapping */
mtr_t* mtr) /*!< in/out: mini-transaction */
__attribute__((nonnull));
#endif /* !UNIV_INNOCHECKSUM */
#endif /* fil0fil_h */

View file

@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 2007, 2012, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2007, 2013, Oracle and/or its affiliates. All Rights Reserved.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -27,6 +27,7 @@ Created 2007/03/16/03 Sunny Bains
#define INNOBASE_FST0AST_H
#include "mem0mem.h"
#include "ha_prototypes.h"
/* The type of AST Node */
enum fts_ast_type_t {
@ -59,11 +60,16 @@ enum fts_ast_oper_t {
word*/
FTS_DISTANCE, /*!< Proximity distance */
FTS_IGNORE_SKIP /*!< Transient node operator
FTS_IGNORE_SKIP, /*!< Transient node operator
signifies that this is a
FTS_IGNORE node, and ignored in
the first pass of
fts_ast_visit() */
FTS_EXIST_SKIP /*!< Transient node operator
signifies that this ia a
FTS_EXIST node, and ignored in
the first pass of
fts_ast_visit() */
};
/* Data types used by the FTS parser */
@ -71,7 +77,7 @@ struct fts_lexer_t;
struct fts_ast_node_t;
struct fts_ast_state_t;
typedef ulint (*fts_ast_callback)(fts_ast_oper_t, fts_ast_node_t*, void*);
typedef dberr_t (*fts_ast_callback)(fts_ast_oper_t, fts_ast_node_t*, void*);
/********************************************************************
Parse the string using the lexer setup within state.*/
@ -268,6 +274,8 @@ struct fts_ast_state_t {
fts_ast_list_t list; /*!< List of nodes allocated */
fts_lexer_t* lexer; /*!< Lexer callback + arg */
CHARSET_INFO* charset; /*!< charset used for
tokenization */
};
#endif /* INNOBASE_FSTS0AST_H */

View file

@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 2011, 2012, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2011, 2013, Oracle and/or its affiliates. All Rights Reserved.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -87,6 +87,7 @@ those defined in mysql file ft_global.h */
#define FTS_EXPAND 4
#define FTS_PROXIMITY 8
#define FTS_PHRASE 16
#define FTS_OPT_RANKING 32
#define FTS_INDEX_TABLE_IND_NAME "FTS_INDEX_TABLE_IND"
@ -240,9 +241,10 @@ struct fts_ranking_t {
fts_rank_t rank; /*!< Rank is between 0 .. 1 */
ib_rbt_t* words; /*!< RB Tree of type byte*, this
contains the words that were queried
byte* words; /*!< this contains the words
that were queried
and found in this document */
ulint words_len; /*!< words len */
};
/** Query result. */
@ -345,14 +347,27 @@ extern const char* fts_default_stopword[];
/** Variable specifying the maximum FTS cache size for each table */
extern ulong fts_max_cache_size;
/** Variable specifying the total memory allocated for FTS cache */
extern ulong fts_max_total_cache_size;
/** Variable specifying the FTS result cache limit for each query */
extern ulong fts_result_cache_limit;
/** Variable specifying the maximum FTS max token size */
extern ulong fts_max_token_size;
/** Variable specifying the minimum FTS max token size */
extern ulong fts_min_token_size;
/** Whether the total memory used for FTS cache is exhausted, and we will
need a sync to free some memory */
extern bool fts_need_sync;
/** Maximum possible Fulltext word length */
#define FTS_MAX_WORD_LEN 3 * HA_FT_MAXCHARLEN
#define FTS_MAX_WORD_LEN HA_FT_MAXBYTELEN
/** Maximum possible Fulltext word length (in characters) */
#define FTS_MAX_WORD_LEN_IN_CHAR HA_FT_MAXCHARLEN
/** Variable specifying the table that has Fulltext index to display its
content through information schema table */
@ -844,7 +859,7 @@ fts_index_get_charset(
dict_index_t* index); /*!< in: FTS index */
/*********************************************************************//**
Get the initial Doc ID by consulting the ADDED and the CONFIG table
Get the initial Doc ID by consulting the CONFIG table
@return initial Doc ID */
UNIV_INTERN
doc_id_t
@ -894,8 +909,8 @@ ulint
innobase_mysql_fts_get_token(
/*=========================*/
CHARSET_INFO* charset, /*!< in: Character set */
byte* start, /*!< in: start of text */
byte* end, /*!< in: one character past
const byte* start, /*!< in: start of text */
const byte* end, /*!< in: one character past
end of text */
fts_string_t* token, /*!< out: token's text */
ulint* offset); /*!< out: offset to token,
@ -923,9 +938,9 @@ fts_get_max_doc_id(
/******************************************************************//**
Check whether user supplied stopword table exists and is of
the right format.
@return TRUE if the table qualifies */
@return the stopword column charset if qualifies */
UNIV_INTERN
ibool
CHARSET_INFO*
fts_valid_stopword_table(
/*=====================*/
const char* stopword_table_name); /*!< in: Stopword table
@ -970,9 +985,11 @@ fts_table_fetch_doc_ids(
fts_doc_ids_t* doc_ids); /*!< in: For collecting
doc ids */
/****************************************************************//**
This function loads the documents in "ADDED" table into FTS cache,
it also loads the stopword info into the FTS cache.
@return DB_SUCCESS if all OK */
This function brings FTS index in sync when FTS index is first
used. There are documents that have not yet sync-ed to auxiliary
tables from last server abnormally shutdown, we will need to bring
such document into FTS cache before any further operations
@return TRUE if all OK */
UNIV_INTERN
ibool
fts_init_index(
@ -1001,6 +1018,17 @@ fts_drop_index(
trx_t* trx) /*!< in: Transaction for the drop */
__attribute__((nonnull));
/****************************************************************//**
Rename auxiliary tables for all fts index for a table
@return DB_SUCCESS or error code */
dberr_t
fts_rename_aux_tables(
/*==================*/
dict_table_t* table, /*!< in: user Table */
const char* new_name, /*!< in: new table name */
trx_t* trx); /*!< in: transaction */
/*******************************************************************//**
Check indexes in the fts->indexes is also present in index cache and
table->indexes list

View file

@ -1,10 +1,8 @@
/* A Bison parser, made by GNU Bison 2.5. */
/* A Bison parser, made by GNU Bison 2.4.1. */
/* Bison interface for Yacc-like parsers in C
/* Skeleton interface for Bison's Yacc-like parsers in C
Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
Free Software Foundation, Inc.
Copyright (C) 1984, 1989-1990, 2000-2011 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -52,8 +50,8 @@
typedef union YYSTYPE
{
/* Line 1676 of yacc.c */
#line 36 "fts0pars.y"
/* Line 2068 of yacc.c */
#line 61 "fts0pars.y"
int oper;
char* token;
@ -61,8 +59,8 @@ typedef union YYSTYPE
/* Line 1676 of yacc.c */
#line 66 "fts0pars.h"
/* Line 2068 of yacc.c */
#line 64 "fts0pars.hh"
} YYSTYPE;
# define YYSTYPE_IS_TRIVIAL 1
# define yystype YYSTYPE /* obsolescent; will be withdrawn */

View file

@ -47,7 +47,8 @@ fts_utf8_string_dup(
const fts_string_t* src, /*!< in: src string */
mem_heap_t* heap) /*!< in: heap to use */
{
dst->f_str = (byte*) mem_heap_dup(heap, src->f_str, src->f_len + 1);
dst->f_str = (byte*)mem_heap_alloc(heap, src->f_len + 1);
memcpy(dst->f_str, src->f_str, src->f_len);
dst->f_len = src->f_len;
dst->f_str[src->f_len] = 0;

View file

@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 2006, 2012, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2006, 2013, Oracle and/or its affiliates. All Rights Reserved.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -396,8 +396,8 @@ ulint
innobase_mysql_fts_get_token(
/*=========================*/
CHARSET_INFO* charset, /*!< in: Character set */
byte* start, /*!< in: start of text */
byte* end, /*!< in: one character past end of
const byte* start, /*!< in: start of text */
const byte* end, /*!< in: one character past end of
text */
fts_string_t* token, /*!< out: token's text */
ulint* offset); /*!< out: offset to token,
@ -595,4 +595,35 @@ innobase_convert_to_system_charset(
ulint len, /* in: length of 'to', in bytes */
uint* errors); /* out: error return */
/**********************************************************************
Check if the length of the identifier exceeds the maximum allowed.
The input to this function is an identifier in charset my_charset_filename.
return true when length of identifier is too long. */
UNIV_INTERN
my_bool
innobase_check_identifier_length(
/*=============================*/
const char* id); /* in: identifier to check. it must belong
to charset my_charset_filename */
/**********************************************************************
Converts an identifier from my_charset_filename to UTF-8 charset. */
uint
innobase_convert_to_system_charset(
/*===============================*/
char* to, /* out: converted identifier */
const char* from, /* in: identifier to convert */
ulint len, /* in: length of 'to', in bytes */
uint* errors); /* out: error return */
/**********************************************************************
Converts an identifier from my_charset_filename to UTF-8 charset. */
uint
innobase_convert_to_filename_charset(
/*=================================*/
char* to, /* out: converted identifier */
const char* from, /* in: identifier to convert */
ulint len); /* in: length of 'to', in bytes */
#endif /* HA_INNODB_PROTOTYPES_H */

View file

@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 2005, 2012, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2005, 2013, Oracle and/or its affiliates. All Rights Reserved.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software

View file

@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 1997, 2009, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 1997, 2013, Oracle and/or its affiliates. All Rights Reserved.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -413,9 +413,9 @@ ibuf_count_get(
#endif
/******************************************************************//**
Looks if the insert buffer is empty.
@return TRUE if empty */
@return true if empty */
UNIV_INTERN
ibool
bool
ibuf_is_empty(void);
/*===============*/
/******************************************************************//**

View file

@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 1997, 2009, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 1997, 2013, Oracle and/or its affiliates. All Rights Reserved.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -67,10 +67,10 @@ struct ibuf_t{
ulint seg_size; /*!< allocated pages of the file
segment containing ibuf header and
tree */
ibool empty; /*!< Protected by the page
bool empty; /*!< Protected by the page
latch of the root page of the
insert buffer tree
(FSP_IBUF_TREE_ROOT_PAGE_NO). TRUE
(FSP_IBUF_TREE_ROOT_PAGE_NO). true
if and only if the insert
buffer tree is empty. */
ulint free_list_len; /*!< length of the free list */
@ -253,7 +253,15 @@ ibuf_index_page_calc_free_zip(
ut_ad(zip_size == buf_block_get_zip_size(block));
ut_ad(zip_size);
max_ins_size = page_get_max_insert_size_after_reorganize(
/* Consider the maximum insert size on the uncompressed page
without reorganizing the page. We must not assume anything
about the compression ratio. If zip_max_ins > max_ins_size and
there is 1/4 garbage on the page, recompression after the
reorganize could fail, in theory. So, let us guarantee that
merging a buffered insert to a compressed page will always
succeed without reorganizing or recompressing the page, just
by using the page modification log. */
max_ins_size = page_get_max_insert_size(
buf_block_get_frame(block), 1);
page_zip = buf_block_get_page_zip(block);
@ -331,8 +339,8 @@ ibuf_update_free_bits_if_full(
before = ibuf_index_page_calc_free_bits(0, max_ins_size);
if (max_ins_size >= increase) {
#if ULINT32_UNDEFINED <= UNIV_PAGE_SIZE
# error "ULINT32_UNDEFINED <= UNIV_PAGE_SIZE"
#if ULINT32_UNDEFINED <= UNIV_PAGE_SIZE_MAX
# error "ULINT32_UNDEFINED <= UNIV_PAGE_SIZE_MAX"
#endif
after = ibuf_index_page_calc_free_bits(0, max_ins_size
- increase);

View file

@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 1996, 2012, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 1996, 2013, Oracle and/or its affiliates. All Rights Reserved.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -466,6 +466,14 @@ lock_table(
enum lock_mode mode, /*!< in: lock mode */
que_thr_t* thr) /*!< in: query thread */
__attribute__((nonnull, warn_unused_result));
/*********************************************************************//**
Creates a table IX lock object for a resurrected transaction. */
UNIV_INTERN
void
lock_table_ix_resurrect(
/*====================*/
dict_table_t* table, /*!< in/out: table */
trx_t* trx); /*!< in/out: transaction */
/*************************************************************//**
Removes a granted record lock of a transaction from the queue and grants
locks to other transactions waiting in the queue if they now are entitled
@ -824,6 +832,19 @@ lock_trx_has_sys_table_locks(
/*=========================*/
const trx_t* trx) /*!< in: transaction to check */
__attribute__((warn_unused_result));
/*******************************************************************//**
Check if the transaction holds an exclusive lock on a record.
@return whether the locks are held */
UNIV_INTERN
bool
lock_trx_has_rec_x_lock(
/*====================*/
const trx_t* trx, /*!< in: transaction to check */
const dict_table_t* table, /*!< in: table to check */
const buf_block_t* block, /*!< in: buffer block of the record */
ulint heap_no)/*!< in: record heap number */
__attribute__((nonnull, warn_unused_result));
#endif /* UNIV_DEBUG */
/** Lock modes and types */

View file

@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 1995, 2012, Oracle and/or its affiliates. All rights reserved.
Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved.
Copyright (c) 2009, Google Inc.
Portions of this file contain modifications contributed and copyrighted by
@ -794,12 +794,14 @@ struct log_t{
ulint max_buf_free; /*!< recommended maximum value of
buf_free, after which the buffer is
flushed */
#ifdef UNIV_LOG_DEBUG
ulint old_buf_free; /*!< value of buf free when log was
last time opened; only in the debug
version */
ib_uint64_t old_lsn; /*!< value of lsn when log was
last time opened; only in the
debug version */
#endif /* UNIV_LOG_DEBUG */
ibool check_flush_or_checkpoint;
/*!< this is set to TRUE when there may
be need to flush the log buffer, or

View file

@ -873,6 +873,8 @@ mach_read_ulint(
default:
ut_error;
}
return(0);
}
#endif /* !UNIV_HOTBACKUP */

View file

@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 1995, 2012, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 1995, 2013, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2012, Facebook Inc.
This program is free software; you can redistribute it and/or modify it under
@ -299,14 +299,16 @@ mtr_x_lock_func(
#endif /* !UNIV_HOTBACKUP */
/***************************************************//**
Releases an object in the memo stack. */
Releases an object in the memo stack.
@return true if released */
UNIV_INTERN
void
bool
mtr_memo_release(
/*=============*/
mtr_t* mtr, /*!< in: mtr */
mtr_t* mtr, /*!< in/out: mini-transaction */
void* object, /*!< in: object */
ulint type); /*!< in: object type: MTR_MEMO_S_LOCK, ... */
ulint type) /*!< in: object type: MTR_MEMO_S_LOCK, ... */
__attribute__((nonnull));
#ifdef UNIV_DEBUG
# ifndef UNIV_HOTBACKUP
/**********************************************************//**
@ -318,7 +320,8 @@ mtr_memo_contains(
/*==============*/
mtr_t* mtr, /*!< in: mtr */
const void* object, /*!< in: object to search */
ulint type); /*!< in: type of object */
ulint type) /*!< in: type of object */
__attribute__((warn_unused_result, nonnull));
/**********************************************************//**
Checks if memo contains the given page.

View file

@ -213,7 +213,9 @@ various file I/O operations with performance schema.
1) register_pfs_file_open_begin() and register_pfs_file_open_end() are
used to register file creation, opening, closing and renaming.
2) register_pfs_file_io_begin() and register_pfs_file_io_end() are
used to register actual file read, write and flush */
used to register actual file read, write and flush
3) register_pfs_file_close_begin() and register_pfs_file_close_end()
are used to register file deletion operations*/
# define register_pfs_file_open_begin(state, locker, key, op, name, \
src_file, src_line) \
do { \
@ -233,6 +235,25 @@ do { \
} \
} while (0)
# define register_pfs_file_close_begin(state, locker, key, op, name, \
src_file, src_line) \
do { \
locker = PSI_FILE_CALL(get_thread_file_name_locker)( \
state, key, op, name, &locker); \
if (UNIV_LIKELY(locker != NULL)) { \
PSI_FILE_CALL(start_file_close_wait)( \
locker, src_file, src_line); \
} \
} while (0)
# define register_pfs_file_close_end(locker, result) \
do { \
if (UNIV_LIKELY(locker != NULL)) { \
PSI_FILE_CALL(end_file_close_wait)( \
locker, result); \
} \
} while (0)
# define register_pfs_file_io_begin(state, locker, file, count, op, \
src_file, src_line) \
do { \
@ -306,6 +327,12 @@ The wrapper functions have the prefix of "innodb_". */
# define os_file_rename(key, oldpath, newpath) \
pfs_os_file_rename_func(key, oldpath, newpath, __FILE__, __LINE__)
# define os_file_delete(key, name) \
pfs_os_file_delete_func(key, name, __FILE__, __LINE__)
# define os_file_delete_if_exists(key, name) \
pfs_os_file_delete_if_exists_func(key, name, __FILE__, __LINE__)
#else /* UNIV_PFS_IO */
/* If UNIV_PFS_IO is not defined, these I/O APIs point
@ -341,6 +368,11 @@ to original un-instrumented file I/O APIs */
# define os_file_rename(key, oldpath, newpath) \
os_file_rename_func(oldpath, newpath)
# define os_file_delete(key, name) os_file_delete_func(name)
# define os_file_delete_if_exists(key, name) \
os_file_delete_if_exists_func(name)
#endif /* UNIV_PFS_IO */
/* File types for directory entry data type */
@ -527,8 +559,8 @@ Deletes a file. The file has to be closed before calling this.
@return TRUE if success */
UNIV_INTERN
bool
os_file_delete(
/*===========*/
os_file_delete_func(
/*================*/
const char* name); /*!< in: file path as a null-terminated
string */
@ -537,8 +569,8 @@ Deletes a file if it exists. The file has to be closed before calling this.
@return TRUE if success */
UNIV_INTERN
bool
os_file_delete_if_exists(
/*=====================*/
os_file_delete_if_exists_func(
/*==========================*/
const char* name); /*!< in: file path as a null-terminated
string */
/***********************************************************************//**
@ -767,6 +799,38 @@ pfs_os_file_rename_func(
const char* newpath,/*!< in: new file path */
const char* src_file,/*!< in: file name where func invoked */
ulint src_line);/*!< in: line where the func invoked */
/***********************************************************************//**
NOTE! Please use the corresponding macro os_file_delete(), not directly
this function!
This is the performance schema instrumented wrapper function for
os_file_delete()
@return TRUE if success */
UNIV_INLINE
bool
pfs_os_file_delete_func(
/*====================*/
mysql_pfs_key_t key, /*!< in: Performance Schema Key */
const char* name, /*!< in: old file path as a null-terminated
string */
const char* src_file,/*!< in: file name where func invoked */
ulint src_line);/*!< in: line where the func invoked */
/***********************************************************************//**
NOTE! Please use the corresponding macro os_file_delete_if_exists(), not
directly this function!
This is the performance schema instrumented wrapper function for
os_file_delete_if_exists()
@return TRUE if success */
UNIV_INLINE
bool
pfs_os_file_delete_if_exists_func(
/*==============================*/
mysql_pfs_key_t key, /*!< in: Performance Schema Key */
const char* name, /*!< in: old file path as a null-terminated
string */
const char* src_file,/*!< in: file name where func invoked */
ulint src_line);/*!< in: line where the func invoked */
#endif /* UNIV_PFS_IO */
#ifdef UNIV_HOTBACKUP
@ -896,8 +960,8 @@ os_file_status(
The function os_file_dirname returns a directory component of a
null-terminated pathname string. In the usual case, dirname returns
the string up to, but not including, the final '/', and basename
is the component following the final '/'. Trailing '/' charac­
ters are not counted as part of the pathname.
is the component following the final '/'. Trailing '/' characters
are not counted as part of the pathname.
If path does not contain a slash, dirname returns the string ".".

View file

@ -386,4 +386,64 @@ pfs_os_file_rename_func(
return(result);
}
/***********************************************************************//**
NOTE! Please use the corresponding macro os_file_delete(), not directly
this function!
This is the performance schema instrumented wrapper function for
os_file_delete()
@return TRUE if success */
UNIV_INLINE
bool
pfs_os_file_delete_func(
/*====================*/
mysql_pfs_key_t key, /*!< in: Performance Schema Key */
const char* name, /*!< in: file path as a null-terminated
string */
const char* src_file, /*!< in: file name where func invoked */
ulint src_line) /*!< in: line where the func invoked */
{
bool result;
struct PSI_file_locker* locker = NULL;
PSI_file_locker_state state;
register_pfs_file_close_begin(&state, locker, key, PSI_FILE_DELETE,
name, src_file, src_line);
result = os_file_delete_func(name);
register_pfs_file_close_end(locker, 0);
return(result);
}
/***********************************************************************//**
NOTE! Please use the corresponding macro os_file_delete_if_exists(), not
directly this function!
This is the performance schema instrumented wrapper function for
os_file_delete_if_exists()
@return TRUE if success */
UNIV_INLINE
bool
pfs_os_file_delete_if_exists_func(
/*==============================*/
mysql_pfs_key_t key, /*!< in: Performance Schema Key */
const char* name, /*!< in: file path as a null-terminated
string */
const char* src_file, /*!< in: file name where func invoked */
ulint src_line) /*!< in: line where the func invoked */
{
bool result;
struct PSI_file_locker* locker = NULL;
PSI_file_locker_state state;
register_pfs_file_close_begin(&state, locker, key, PSI_FILE_DELETE,
name, src_file, src_line);
result = os_file_delete_if_exists_func(name);
register_pfs_file_close_end(locker, 0);
return(result);
}
#endif /* UNIV_PFS_IO */

View file

@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 1994, 2012, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 1994, 2013, Oracle and/or its affiliates. All Rights Reserved.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -162,6 +162,12 @@ Inserts a record next to page cursor. Returns pointer to inserted record if
succeed, i.e., enough space available, NULL otherwise. The cursor stays at
the same logical position, but the physical position may change if it is
pointing to a compressed page that was reorganized.
IMPORTANT: The caller will have to update IBUF_BITMAP_FREE
if this is a compressed leaf page in a secondary index.
This has to be done either within the same mini-transaction,
or by invoking ibuf_reset_free_bits() before mtr_commit().
@return pointer to record if succeed, NULL otherwise */
UNIV_INLINE
rec_t*
@ -181,6 +187,12 @@ Inserts a record next to page cursor. Returns pointer to inserted record if
succeed, i.e., enough space available, NULL otherwise. The cursor stays at
the same logical position, but the physical position may change if it is
pointing to a compressed page that was reorganized.
IMPORTANT: The caller will have to update IBUF_BITMAP_FREE
if this is a compressed leaf page in a secondary index.
This has to be done either within the same mini-transaction,
or by invoking ibuf_reset_free_bits() before mtr_commit().
@return pointer to record if succeed, NULL otherwise */
UNIV_INLINE
rec_t*
@ -205,27 +217,38 @@ page_cur_insert_rec_low(
dict_index_t* index, /*!< in: record descriptor */
const rec_t* rec, /*!< in: pointer to a physical record */
ulint* offsets,/*!< in/out: rec_get_offsets(rec, index) */
mtr_t* mtr); /*!< in: mini-transaction handle, or NULL */
mtr_t* mtr) /*!< in: mini-transaction handle, or NULL */
__attribute__((nonnull(1,2,3,4), warn_unused_result));
/***********************************************************//**
Inserts a record next to page cursor on a compressed and uncompressed
page. Returns pointer to inserted record if succeed, i.e.,
enough space available, NULL otherwise.
The cursor stays at the same position.
IMPORTANT: The caller will have to update IBUF_BITMAP_FREE
if this is a compressed leaf page in a secondary index.
This has to be done either within the same mini-transaction,
or by invoking ibuf_reset_free_bits() before mtr_commit().
@return pointer to record if succeed, NULL otherwise */
UNIV_INTERN
rec_t*
page_cur_insert_rec_zip(
/*====================*/
rec_t** current_rec,/*!< in/out: pointer to current record after
which the new record is inserted */
buf_block_t* block, /*!< in: buffer block of *current_rec */
page_cur_t* cursor, /*!< in/out: page cursor */
dict_index_t* index, /*!< in: record descriptor */
const rec_t* rec, /*!< in: pointer to a physical record */
ulint* offsets,/*!< in/out: rec_get_offsets(rec, index) */
mtr_t* mtr); /*!< in: mini-transaction handle, or NULL */
mtr_t* mtr) /*!< in: mini-transaction handle, or NULL */
__attribute__((nonnull(1,2,3,4), warn_unused_result));
/*************************************************************//**
Copies records from page to a newly created page, from a given record onward,
including that record. Infimum and supremum records are not copied. */
including that record. Infimum and supremum records are not copied.
IMPORTANT: The caller will have to update IBUF_BITMAP_FREE
if this is a compressed leaf page in a secondary index.
This has to be done either within the same mini-transaction,
or by invoking ibuf_reset_free_bits() before mtr_commit(). */
UNIV_INTERN
void
page_copy_rec_list_end_to_created_page(

View file

@ -1,6 +1,6 @@
/*****************************************************************************
Copyright (c) 1994, 2012, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 1994, 2013, Oracle and/or its affiliates. All Rights Reserved.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -237,6 +237,12 @@ Inserts a record next to page cursor. Returns pointer to inserted record if
succeed, i.e., enough space available, NULL otherwise. The cursor stays at
the same logical position, but the physical position may change if it is
pointing to a compressed page that was reorganized.
IMPORTANT: The caller will have to update IBUF_BITMAP_FREE
if this is a compressed leaf page in a secondary index.
This has to be done either within the same mini-transaction,
or by invoking ibuf_reset_free_bits() before mtr_commit().
@return pointer to record if succeed, NULL otherwise */
UNIV_INLINE
rec_t*
@ -267,8 +273,8 @@ page_cur_tuple_insert(
rec, index, *offsets, ULINT_UNDEFINED, heap);
if (buf_block_get_page_zip(cursor->block)) {
rec = page_cur_insert_rec_zip(&cursor->rec, cursor->block,
index, rec, *offsets, mtr);
rec = page_cur_insert_rec_zip(
cursor, index, rec, *offsets, mtr);
} else {
rec = page_cur_insert_rec_low(cursor->rec,
index, rec, *offsets, mtr);
@ -284,6 +290,12 @@ Inserts a record next to page cursor. Returns pointer to inserted record if
succeed, i.e., enough space available, NULL otherwise. The cursor stays at
the same logical position, but the physical position may change if it is
pointing to a compressed page that was reorganized.
IMPORTANT: The caller will have to update IBUF_BITMAP_FREE
if this is a compressed leaf page in a secondary index.
This has to be done either within the same mini-transaction,
or by invoking ibuf_reset_free_bits() before mtr_commit().
@return pointer to record if succeed, NULL otherwise */
UNIV_INLINE
rec_t*
@ -296,8 +308,8 @@ page_cur_rec_insert(
mtr_t* mtr) /*!< in: mini-transaction handle, or NULL */
{
if (buf_block_get_page_zip(cursor->block)) {
return(page_cur_insert_rec_zip(&cursor->rec, cursor->block,
index, rec, offsets, mtr));
return(page_cur_insert_rec_zip(
cursor, index, rec, offsets, mtr));
} else {
return(page_cur_insert_rec_low(cursor->rec,
index, rec, offsets, mtr));

Some files were not shown because too many files have changed in this diff Show more