Automatic merge with 5.1
This commit is contained in:
commit
00a2f36bbf
@ -25,8 +25,6 @@ typedef uint32 my_bitmap_map;
|
||||
typedef struct st_bitmap
|
||||
{
|
||||
my_bitmap_map *bitmap;
|
||||
uint n_bits; /* number of bits occupied by the above */
|
||||
my_bitmap_map last_word_mask;
|
||||
my_bitmap_map *last_word_ptr;
|
||||
/*
|
||||
mutex will be acquired for the duration of each bitmap operation if
|
||||
@ -36,6 +34,8 @@ typedef struct st_bitmap
|
||||
#ifdef THREAD
|
||||
pthread_mutex_t *mutex;
|
||||
#endif
|
||||
my_bitmap_map last_word_mask;
|
||||
uint32 n_bits; /* number of bits occupied by the above */
|
||||
} MY_BITMAP;
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
@ -32,6 +32,16 @@ files cannot. Special options are
|
||||
--timezone, --plugin-load, --result-file, --config-file-template,
|
||||
--default-time-zone, --force-restart
|
||||
|
||||
In particular, all --plugin-load instances on the command line (on the
|
||||
combined command line, assembled from different .opt and combinations
|
||||
files) are merged into one. That is, if, say, test-master.opt file contains
|
||||
--plugin-load=aaa.so and suite.opt has --plugin-load=bbb.so that mysqld
|
||||
will get --plugin-load=aaa.so:bbb.so. Also, empty --plugin-load options are
|
||||
removed from the command line. Which means that one can safely specify
|
||||
--plugin-load=$AAA_SO and if aaa.so was not built (perhaps, the plugin was
|
||||
statically linked into the server), the .opt file will not result in the
|
||||
invalid command line option that can cause the server to refuse to start.
|
||||
|
||||
==========================
|
||||
A suite can have suite.pm file in the suitedir. It must declare a
|
||||
package that inherits from My::Suite.
|
||||
|
@ -581,3 +581,58 @@ WHERE
|
||||
`RUNID`= '' AND `SUBMITNR`= '' AND `ORDERNR`='' AND `PROGRAMM`='' AND
|
||||
`TESTID`='' AND `UCCHECK`='';
|
||||
drop table t1;
|
||||
#
|
||||
# BUG#56862/640419: Wrong result with sort_union index merge when one
|
||||
# of the merged index scans is the primary key scan
|
||||
#
|
||||
CREATE TABLE t1 (
|
||||
pk int NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
a int,
|
||||
b int,
|
||||
INDEX idx(a))
|
||||
ENGINE=INNODB;
|
||||
INSERT INTO t1(a,b) VALUES
|
||||
(11, 1100), (2, 200), (1, 100), (14, 1400), (5, 500),
|
||||
(3, 300), (17, 1700), (4, 400), (12, 1200), (8, 800),
|
||||
(6, 600), (18, 1800), (9, 900), (10, 1000), (7, 700),
|
||||
(13, 1300), (15, 1500), (19, 1900), (16, 1600), (20, 2000);
|
||||
INSERT INTO t1(a,b) SELECT a+20, b+2000 FROM t1;
|
||||
INSERT INTO t1(a,b) SELECT a+40, b+4000 FROM t1;
|
||||
INSERT INTO t1(a,b) SELECT a+80, b+8000 FROM t1;
|
||||
INSERT INTO t1(a,b) SELECT a,b FROM t1;
|
||||
INSERT INTO t1(a,b) SELECT a,b FROM t1;
|
||||
INSERT INTO t1(a,b) SELECT a,b FROM t1;
|
||||
INSERT INTO t1(a,b) SELECT a,b FROM t1;
|
||||
INSERT INTO t1(a,b) SELECT a,b FROM t1;
|
||||
INSERT INTO t1(a,b) SELECT a,b FROM t1;
|
||||
INSERT INTO t1(a,b) SELECT a,b FROM t1;
|
||||
INSERT INTO t1(a,b) SELECT a,b FROM t1;
|
||||
INSERT INTO t1(a,b) SELECT a,b FROM t1;
|
||||
INSERT INTO t1(a,b) SELECT a,b FROM t1;
|
||||
INSERT INTO t1 VALUES (1000000, 0, 0);
|
||||
SET SESSION sort_buffer_size = 1024*36;
|
||||
EXPLAIN
|
||||
SELECT COUNT(*) FROM
|
||||
(SELECT * FROM t1
|
||||
WHERE a BETWEEN 2 AND 7 OR pk=1000000) AS t;
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 PRIMARY NULL NULL NULL NULL NULL NULL NULL Select tables optimized away
|
||||
2 DERIVED t1 index_merge PRIMARY,idx idx,PRIMARY 5,4 NULL 11419 Using sort_union(idx,PRIMARY); Using where
|
||||
SELECT COUNT(*) FROM
|
||||
(SELECT * FROM t1
|
||||
WHERE a BETWEEN 2 AND 7 OR pk=1000000) AS t;
|
||||
COUNT(*)
|
||||
6145
|
||||
EXPLAIN
|
||||
SELECT COUNT(*) FROM
|
||||
(SELECT * FROM t1 IGNORE INDEX(idx)
|
||||
WHERE a BETWEEN 2 AND 7 OR pk=1000000) AS t;
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 PRIMARY NULL NULL NULL NULL NULL NULL # Select tables optimized away
|
||||
2 DERIVED t1 ALL PRIMARY NULL NULL NULL # Using where
|
||||
SELECT COUNT(*) FROM
|
||||
(SELECT * FROM t1 IGNORE INDEX(idx)
|
||||
WHERE a BETWEEN 2 AND 7 OR pk=1000000) AS t;
|
||||
COUNT(*)
|
||||
6145
|
||||
DROP TABLE t1;
|
||||
|
@ -1411,4 +1411,101 @@ select * from t1 left join t2 on t1.b=t2.b where 1=1;
|
||||
a b a b
|
||||
1 NULL NULL NULL
|
||||
drop table t1,t2;
|
||||
#
|
||||
# Bug#53161: outer join in the derived table is erroneously converted
|
||||
# into an inner join for a query with a group by clause
|
||||
#
|
||||
create table t1 (pk int not null primary key, a int not null);
|
||||
create table t2 like t1;
|
||||
create table t3 like t1;
|
||||
create table t4 (pk int not null primary key);
|
||||
insert into t1 values (1000, 1), (1001, 1);
|
||||
insert into t2 values (2000, 2), (2001, 2);
|
||||
insert into t3 values (3000, 3), (3001, 2);
|
||||
insert into t4 values (4000), (4001);
|
||||
explain extended
|
||||
select t2.pk,
|
||||
(select t3.pk+if(isnull(t4.pk),0,t4.pk)
|
||||
from t3 left join t4 on t4.pk=t3.pk
|
||||
where t3.pk=t2.pk+1000 limit 1 ) as t
|
||||
from t1,t2
|
||||
where t2.pk=t1.pk+1000 and t1.pk>1000
|
||||
group by t2.pk;
|
||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||
1 PRIMARY t1 index PRIMARY PRIMARY 4 NULL 2 100.00 Using where; Using index; Using temporary; Using filesort
|
||||
1 PRIMARY t2 eq_ref PRIMARY PRIMARY 4 func 1 100.00 Using where; Using index
|
||||
2 DEPENDENT SUBQUERY t3 eq_ref PRIMARY PRIMARY 4 func 1 100.00 Using where; Using index
|
||||
2 DEPENDENT SUBQUERY t4 eq_ref PRIMARY PRIMARY 4 test.t3.pk 1 100.00 Using where; Using index
|
||||
Warnings:
|
||||
Note 1276 Field or reference 'test.t2.pk' of SELECT #2 was resolved in SELECT #1
|
||||
Note 1003 select `test`.`t2`.`pk` AS `pk`,(select (`test`.`t3`.`pk` + if(isnull(`test`.`t4`.`pk`),0,`test`.`t4`.`pk`)) from `test`.`t3` left join `test`.`t4` on((`test`.`t4`.`pk` = `test`.`t3`.`pk`)) where (`test`.`t3`.`pk` = (`test`.`t2`.`pk` + 1000)) limit 1) AS `t` from `test`.`t1` join `test`.`t2` where ((`test`.`t2`.`pk` = (`test`.`t1`.`pk` + 1000)) and (`test`.`t1`.`pk` > 1000)) group by `test`.`t2`.`pk`
|
||||
select t2.pk,
|
||||
(select t3.pk+if(isnull(t4.pk),0,t4.pk)
|
||||
from t3 left join t4 on t4.pk=t3.pk
|
||||
where t3.pk=t2.pk+1000 limit 1 ) as t
|
||||
from t1,t2
|
||||
where t2.pk=t1.pk+1000 and t1.pk>1000
|
||||
group by t2.pk;
|
||||
pk t
|
||||
2001 3001
|
||||
drop table t1,t2,t3,t4;
|
||||
#
|
||||
# Bug#57024: Poor performance when conjunctive condition over the outer
|
||||
# table is used in the on condition of an outer join
|
||||
#
|
||||
create table t1 (a int);
|
||||
insert into t1 values (NULL), (NULL), (NULL), (NULL);
|
||||
insert into t1 select * from t1;
|
||||
insert into t1 select * from t1;
|
||||
insert into t1 select * from t1;
|
||||
insert into t1 select * from t1;
|
||||
insert into t1 select * from t1;
|
||||
insert into t1 select * from t1;
|
||||
insert into t1 select * from t1;
|
||||
insert into t1 select * from t1;
|
||||
insert into t1 select * from t1;
|
||||
insert into t1 select * from t1;
|
||||
insert into t1 select * from t1;
|
||||
insert into t1 select * from t1;
|
||||
insert into t1 select * from t1;
|
||||
insert into t1 select * from t1;
|
||||
insert into t1 select * from t1;
|
||||
insert into t1 select * from t1;
|
||||
insert into t1 select * from t1;
|
||||
insert into t1 select * from t1;
|
||||
insert into t1 values (4), (2), (1), (3);
|
||||
create table t2 like t1;
|
||||
insert into t2 select if(t1.a is null, 10, t1.a) from t1;
|
||||
create table t3 (a int, b int, index idx(a));
|
||||
insert into t3 values (1, 100), (3, 301), (4, 402), (1, 102), (1, 101);
|
||||
analyze table t1,t2,t3;
|
||||
Table Op Msg_type Msg_text
|
||||
test.t1 analyze status OK
|
||||
test.t2 analyze status OK
|
||||
test.t3 analyze status OK
|
||||
flush status;
|
||||
select sum(t3.b) from t1 left join t3 on t3.a=t1.a and t1.a is not null;
|
||||
sum(t3.b)
|
||||
1006
|
||||
show status like "handler_read%";
|
||||
Variable_name Value
|
||||
Handler_read_first 0
|
||||
Handler_read_key 4
|
||||
Handler_read_next 5
|
||||
Handler_read_prev 0
|
||||
Handler_read_rnd 0
|
||||
Handler_read_rnd_next 1048581
|
||||
flush status;
|
||||
select sum(t3.b) from t2 left join t3 on t3.a=t2.a and t2.a <> 10;
|
||||
sum(t3.b)
|
||||
1006
|
||||
show status like "handler_read%";
|
||||
Variable_name Value
|
||||
Handler_read_first 0
|
||||
Handler_read_key 4
|
||||
Handler_read_next 5
|
||||
Handler_read_prev 0
|
||||
Handler_read_rnd 0
|
||||
Handler_read_rnd_next 1048581
|
||||
drop table t1,t2,t3;
|
||||
End of 5.1 tests
|
||||
|
@ -388,7 +388,7 @@ Table Op Msg_type Msg_text
|
||||
test.t1 analyze status OK
|
||||
explain extended select a, not(not(a)), not(a <= 2 and not(a)), not(a not like "1"), not (a not in (1,2)), not(a != 2) from t1 where not(not(a)) having not(not(a));
|
||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||
1 SIMPLE t1 index NULL a 5 NULL 21 100.00 Using where; Using index
|
||||
1 SIMPLE t1 index NULL a 5 NULL 5 100.00 Using where; Using index
|
||||
Warnings:
|
||||
Note 1003 select `test`.`t1`.`a` AS `a`,(`test`.`t1`.`a` <> 0) AS `not(not(a))`,((`test`.`t1`.`a` > 2) or `test`.`t1`.`a`) AS `not(a <= 2 and not(a))`,(`test`.`t1`.`a` like '1') AS `not(a not like "1")`,(`test`.`t1`.`a` in (1,2)) AS `not (a not in (1,2))`,(`test`.`t1`.`a` = 2) AS `not(a != 2)` from `test`.`t1` where `test`.`t1`.`a` having `test`.`t1`.`a`
|
||||
drop table t1;
|
||||
|
@ -1384,52 +1384,52 @@ Table Op Msg_type Msg_text
|
||||
test.t2 analyze status OK
|
||||
explain select t2.companynr,companyname from t4 left join t2 using (companynr) where t2.companynr > 0;
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t2 ALL NULL NULL NULL NULL 1200 Using where
|
||||
1 SIMPLE t2 ALL NULL NULL NULL NULL 1199 Using where
|
||||
1 SIMPLE t4 eq_ref PRIMARY PRIMARY 1 test.t2.companynr 1
|
||||
explain select t2.companynr,companyname from t4 left join t2 using (companynr) where t2.companynr > 0 or t2.companynr < 0;
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t2 ALL NULL NULL NULL NULL 1200 Using where
|
||||
1 SIMPLE t2 ALL NULL NULL NULL NULL 1199 Using where
|
||||
1 SIMPLE t4 eq_ref PRIMARY PRIMARY 1 test.t2.companynr 1
|
||||
explain select t2.companynr,companyname from t4 left join t2 using (companynr) where t2.companynr > 0 and t4.companynr > 0;
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t2 ALL NULL NULL NULL NULL 1200 Using where
|
||||
1 SIMPLE t2 ALL NULL NULL NULL NULL 1199 Using where
|
||||
1 SIMPLE t4 eq_ref PRIMARY PRIMARY 1 test.t2.companynr 1
|
||||
explain select companynr,companyname from t4 left join t2 using (companynr) where companynr > 0;
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t4 ALL PRIMARY NULL NULL NULL 12 Using where
|
||||
1 SIMPLE t2 ALL NULL NULL NULL NULL 1200
|
||||
1 SIMPLE t2 ALL NULL NULL NULL NULL 1199
|
||||
explain select companynr,companyname from t4 left join t2 using (companynr) where companynr > 0 or companynr < 0;
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t4 ALL PRIMARY NULL NULL NULL 12 Using where
|
||||
1 SIMPLE t2 ALL NULL NULL NULL NULL 1200
|
||||
1 SIMPLE t2 ALL NULL NULL NULL NULL 1199
|
||||
explain select companynr,companyname from t4 left join t2 using (companynr) where companynr > 0 and companynr > 0;
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t4 ALL PRIMARY NULL NULL NULL 12 Using where
|
||||
1 SIMPLE t2 ALL NULL NULL NULL NULL 1200
|
||||
1 SIMPLE t2 ALL NULL NULL NULL NULL 1199
|
||||
explain select t2.companynr,companyname from t4 left join t2 using (companynr) where t2.companynr > 0 or t2.companynr is null;
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t4 ALL NULL NULL NULL NULL 12
|
||||
1 SIMPLE t2 ALL NULL NULL NULL NULL 1200 Using where
|
||||
1 SIMPLE t2 ALL NULL NULL NULL NULL 1199 Using where
|
||||
explain select t2.companynr,companyname from t4 left join t2 using (companynr) where t2.companynr > 0 or t2.companynr < 0 or t4.companynr > 0;
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t4 ALL PRIMARY NULL NULL NULL 12
|
||||
1 SIMPLE t2 ALL NULL NULL NULL NULL 1200 Using where
|
||||
1 SIMPLE t2 ALL NULL NULL NULL NULL 1199 Using where
|
||||
explain select t2.companynr,companyname from t4 left join t2 using (companynr) where ifnull(t2.companynr,1)>0;
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t4 ALL NULL NULL NULL NULL 12
|
||||
1 SIMPLE t2 ALL NULL NULL NULL NULL 1200 Using where
|
||||
1 SIMPLE t2 ALL NULL NULL NULL NULL 1199 Using where
|
||||
explain select companynr,companyname from t4 left join t2 using (companynr) where companynr > 0 or companynr is null;
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t4 ALL PRIMARY NULL NULL NULL 12 Using where
|
||||
1 SIMPLE t2 ALL NULL NULL NULL NULL 1200
|
||||
1 SIMPLE t2 ALL NULL NULL NULL NULL 1199
|
||||
explain select companynr,companyname from t4 left join t2 using (companynr) where companynr > 0 or companynr < 0 or companynr > 0;
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t4 ALL PRIMARY NULL NULL NULL 12 Using where
|
||||
1 SIMPLE t2 ALL NULL NULL NULL NULL 1200
|
||||
1 SIMPLE t2 ALL NULL NULL NULL NULL 1199
|
||||
explain select companynr,companyname from t4 left join t2 using (companynr) where ifnull(companynr,1)>0;
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t4 ALL NULL NULL NULL NULL 12 Using where
|
||||
1 SIMPLE t2 ALL NULL NULL NULL NULL 1200
|
||||
1 SIMPLE t2 ALL NULL NULL NULL NULL 1199
|
||||
select distinct t2.companynr,t4.companynr from t2,t4 where t2.companynr=t4.companynr+1;
|
||||
companynr companynr
|
||||
37 36
|
||||
@ -1437,7 +1437,7 @@ companynr companynr
|
||||
explain select distinct t2.companynr,t4.companynr from t2,t4 where t2.companynr=t4.companynr+1;
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t4 index NULL PRIMARY 1 NULL 12 Using index; Using temporary
|
||||
1 SIMPLE t2 ALL NULL NULL NULL NULL 1200 Using where; Using join buffer
|
||||
1 SIMPLE t2 ALL NULL NULL NULL NULL 1199 Using where; Using join buffer
|
||||
select t2.fld1,t2.companynr,fld3,period from t3,t2 where t2.fld1 = 38208 and t2.fld1=t3.t2nr and period = 1008 or t2.fld1 = 38008 and t2.fld1 =t3.t2nr and period = 1008;
|
||||
fld1 companynr fld3 period
|
||||
038008 37 reporters 1008
|
||||
@ -1511,7 +1511,7 @@ count(*) min(fld4) max(fld4) sum(fld1) avg(fld1) std(fld1) variance(fld1)
|
||||
70 absentee vest 17788966 254128.0857 3272.5940 10709871.3069
|
||||
explain extended select count(*),min(fld4),max(fld4),sum(fld1),avg(fld1),std(fld1),variance(fld1) from t2 where companynr = 34 and fld4<>"";
|
||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||
1 SIMPLE t2 ALL NULL NULL NULL NULL 1200 100.00 Using where
|
||||
1 SIMPLE t2 ALL NULL NULL NULL NULL 1199 100.00 Using where
|
||||
Warnings:
|
||||
Note 1003 select count(0) AS `count(*)`,min(`test`.`t2`.`fld4`) AS `min(fld4)`,max(`test`.`t2`.`fld4`) AS `max(fld4)`,sum(`test`.`t2`.`fld1`) AS `sum(fld1)`,avg(`test`.`t2`.`fld1`) AS `avg(fld1)`,std(`test`.`t2`.`fld1`) AS `std(fld1)`,variance(`test`.`t2`.`fld1`) AS `variance(fld1)` from `test`.`t2` where ((`test`.`t2`.`companynr` = 34) and (`test`.`t2`.`fld4` <> ''))
|
||||
select companynr,count(*),min(fld4),max(fld4),sum(fld1),avg(fld1),std(fld1),variance(fld1) from t2 group by companynr limit 3;
|
||||
@ -1955,7 +1955,7 @@ id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible WHERE
|
||||
explain select fld3 from t2 where fld1=fld1;
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t2 ALL NULL NULL NULL NULL 1200
|
||||
1 SIMPLE t2 ALL NULL NULL NULL NULL 1199
|
||||
select companynr,fld1 from t2 HAVING fld1=250501 or fld1=250502;
|
||||
companynr fld1
|
||||
34 250501
|
||||
@ -2007,7 +2007,7 @@ count(*)
|
||||
4181
|
||||
explain select min(fld1),max(fld1),count(*) from t2;
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t2 index NULL fld1 4 NULL 1200 Using index
|
||||
1 SIMPLE t2 index NULL fld1 4 NULL 1199 Using index
|
||||
select min(fld1),max(fld1),count(*) from t2;
|
||||
min(fld1) max(fld1) count(*)
|
||||
0 1232609 1199
|
||||
@ -2093,9 +2093,9 @@ show full columns from t2 from test like 's%';
|
||||
Field Type Collation Null Key Default Extra Privileges Comment
|
||||
show keys from t2;
|
||||
Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment
|
||||
t2 0 PRIMARY 1 auto A 1200 NULL NULL BTREE
|
||||
t2 0 fld1 1 fld1 A 1200 NULL NULL BTREE
|
||||
t2 1 fld3 1 fld3 A 1200 NULL NULL BTREE
|
||||
t2 0 PRIMARY 1 auto A 1199 NULL NULL BTREE
|
||||
t2 0 fld1 1 fld1 A 1199 NULL NULL BTREE
|
||||
t2 1 fld3 1 fld3 A 1199 NULL NULL BTREE
|
||||
drop table t4, t3, t2, t1;
|
||||
DO 1;
|
||||
DO benchmark(100,1+1),1,1;
|
||||
|
@ -29,3 +29,56 @@ let $merge_table_support= 0;
|
||||
--source include/index_merge2.inc
|
||||
--source include/index_merge_2sweeps.inc
|
||||
--source include/index_merge_ror_cpk.inc
|
||||
|
||||
--echo #
|
||||
--echo # BUG#56862/640419: Wrong result with sort_union index merge when one
|
||||
--echo # of the merged index scans is the primary key scan
|
||||
--echo #
|
||||
|
||||
CREATE TABLE t1 (
|
||||
pk int NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
a int,
|
||||
b int,
|
||||
INDEX idx(a))
|
||||
ENGINE=INNODB;
|
||||
|
||||
INSERT INTO t1(a,b) VALUES
|
||||
(11, 1100), (2, 200), (1, 100), (14, 1400), (5, 500),
|
||||
(3, 300), (17, 1700), (4, 400), (12, 1200), (8, 800),
|
||||
(6, 600), (18, 1800), (9, 900), (10, 1000), (7, 700),
|
||||
(13, 1300), (15, 1500), (19, 1900), (16, 1600), (20, 2000);
|
||||
INSERT INTO t1(a,b) SELECT a+20, b+2000 FROM t1;
|
||||
INSERT INTO t1(a,b) SELECT a+40, b+4000 FROM t1;
|
||||
INSERT INTO t1(a,b) SELECT a+80, b+8000 FROM t1;
|
||||
INSERT INTO t1(a,b) SELECT a,b FROM t1;
|
||||
INSERT INTO t1(a,b) SELECT a,b FROM t1;
|
||||
INSERT INTO t1(a,b) SELECT a,b FROM t1;
|
||||
INSERT INTO t1(a,b) SELECT a,b FROM t1;
|
||||
INSERT INTO t1(a,b) SELECT a,b FROM t1;
|
||||
INSERT INTO t1(a,b) SELECT a,b FROM t1;
|
||||
INSERT INTO t1(a,b) SELECT a,b FROM t1;
|
||||
INSERT INTO t1(a,b) SELECT a,b FROM t1;
|
||||
INSERT INTO t1(a,b) SELECT a,b FROM t1;
|
||||
INSERT INTO t1(a,b) SELECT a,b FROM t1;
|
||||
INSERT INTO t1 VALUES (1000000, 0, 0);
|
||||
|
||||
SET SESSION sort_buffer_size = 1024*36;
|
||||
|
||||
EXPLAIN
|
||||
SELECT COUNT(*) FROM
|
||||
(SELECT * FROM t1
|
||||
WHERE a BETWEEN 2 AND 7 OR pk=1000000) AS t;
|
||||
SELECT COUNT(*) FROM
|
||||
(SELECT * FROM t1
|
||||
WHERE a BETWEEN 2 AND 7 OR pk=1000000) AS t;
|
||||
|
||||
--replace_column 9 #
|
||||
EXPLAIN
|
||||
SELECT COUNT(*) FROM
|
||||
(SELECT * FROM t1 IGNORE INDEX(idx)
|
||||
WHERE a BETWEEN 2 AND 7 OR pk=1000000) AS t;
|
||||
SELECT COUNT(*) FROM
|
||||
(SELECT * FROM t1 IGNORE INDEX(idx)
|
||||
WHERE a BETWEEN 2 AND 7 OR pk=1000000) AS t;
|
||||
|
||||
DROP TABLE t1;
|
||||
|
@ -996,4 +996,81 @@ select * from t1 left join t2 on t1.b=t2.b where 1=1;
|
||||
|
||||
drop table t1,t2;
|
||||
|
||||
--echo #
|
||||
--echo # Bug#53161: outer join in the derived table is erroneously converted
|
||||
--echo # into an inner join for a query with a group by clause
|
||||
--echo #
|
||||
|
||||
create table t1 (pk int not null primary key, a int not null);
|
||||
create table t2 like t1;
|
||||
create table t3 like t1;
|
||||
create table t4 (pk int not null primary key);
|
||||
insert into t1 values (1000, 1), (1001, 1);
|
||||
insert into t2 values (2000, 2), (2001, 2);
|
||||
insert into t3 values (3000, 3), (3001, 2);
|
||||
insert into t4 values (4000), (4001);
|
||||
|
||||
explain extended
|
||||
select t2.pk,
|
||||
(select t3.pk+if(isnull(t4.pk),0,t4.pk)
|
||||
from t3 left join t4 on t4.pk=t3.pk
|
||||
where t3.pk=t2.pk+1000 limit 1 ) as t
|
||||
from t1,t2
|
||||
where t2.pk=t1.pk+1000 and t1.pk>1000
|
||||
group by t2.pk;
|
||||
|
||||
select t2.pk,
|
||||
(select t3.pk+if(isnull(t4.pk),0,t4.pk)
|
||||
from t3 left join t4 on t4.pk=t3.pk
|
||||
where t3.pk=t2.pk+1000 limit 1 ) as t
|
||||
from t1,t2
|
||||
where t2.pk=t1.pk+1000 and t1.pk>1000
|
||||
group by t2.pk;
|
||||
|
||||
drop table t1,t2,t3,t4;
|
||||
|
||||
--echo #
|
||||
--echo # Bug#57024: Poor performance when conjunctive condition over the outer
|
||||
--echo # table is used in the on condition of an outer join
|
||||
--echo #
|
||||
|
||||
create table t1 (a int);
|
||||
insert into t1 values (NULL), (NULL), (NULL), (NULL);
|
||||
insert into t1 select * from t1;
|
||||
insert into t1 select * from t1;
|
||||
insert into t1 select * from t1;
|
||||
insert into t1 select * from t1;
|
||||
insert into t1 select * from t1;
|
||||
insert into t1 select * from t1;
|
||||
insert into t1 select * from t1;
|
||||
insert into t1 select * from t1;
|
||||
insert into t1 select * from t1;
|
||||
insert into t1 select * from t1;
|
||||
insert into t1 select * from t1;
|
||||
insert into t1 select * from t1;
|
||||
insert into t1 select * from t1;
|
||||
insert into t1 select * from t1;
|
||||
insert into t1 select * from t1;
|
||||
insert into t1 select * from t1;
|
||||
insert into t1 select * from t1;
|
||||
insert into t1 select * from t1;
|
||||
insert into t1 values (4), (2), (1), (3);
|
||||
|
||||
create table t2 like t1;
|
||||
insert into t2 select if(t1.a is null, 10, t1.a) from t1;
|
||||
|
||||
create table t3 (a int, b int, index idx(a));
|
||||
insert into t3 values (1, 100), (3, 301), (4, 402), (1, 102), (1, 101);
|
||||
|
||||
analyze table t1,t2,t3;
|
||||
|
||||
flush status;
|
||||
select sum(t3.b) from t1 left join t3 on t3.a=t1.a and t1.a is not null;
|
||||
show status like "handler_read%";
|
||||
flush status;
|
||||
select sum(t3.b) from t2 left join t3 on t3.a=t2.a and t2.a <> 10;
|
||||
show status like "handler_read%";
|
||||
|
||||
drop table t1,t2,t3;
|
||||
|
||||
--echo End of 5.1 tests
|
||||
|
@ -40,16 +40,31 @@
|
||||
#include <m_string.h>
|
||||
#include <my_bit.h>
|
||||
|
||||
|
||||
/* Create a mask of the significant bits for the last byte (1,3,7,..255) */
|
||||
|
||||
static inline uchar last_byte_mask(uint bits)
|
||||
{
|
||||
/* Get the number of used bits-1 (0..7) in the last byte */
|
||||
unsigned int const used= (bits - 1U) & 7U;
|
||||
/* Return bitmask for the significant bits */
|
||||
return ((2U << used) - 1);
|
||||
}
|
||||
|
||||
/*
|
||||
Create a mask with the upper 'unused' bits set and the lower 'used'
|
||||
bits clear. The bits within each byte is stored in big-endian order.
|
||||
*/
|
||||
|
||||
static inline uchar invers_last_byte_mask(uint bits)
|
||||
{
|
||||
return last_byte_mask(bits) ^ 255;
|
||||
}
|
||||
|
||||
|
||||
void create_last_word_mask(MY_BITMAP *map)
|
||||
{
|
||||
/* Get the number of used bits (1..8) in the last byte */
|
||||
unsigned int const used= 1U + ((map->n_bits-1U) & 0x7U);
|
||||
|
||||
/*
|
||||
Create a mask with the upper 'unused' bits set and the lower 'used'
|
||||
bits clear. The bits within each byte is stored in big-endian order.
|
||||
*/
|
||||
unsigned char const mask= (~((1 << used) - 1)) & 255;
|
||||
unsigned char const mask= invers_last_byte_mask(map->n_bits);
|
||||
|
||||
/*
|
||||
The first bytes are to be set to zero since they represent real bits
|
||||
@ -267,40 +282,41 @@ void bitmap_set_prefix(MY_BITMAP *map, uint prefix_size)
|
||||
|
||||
my_bool bitmap_is_prefix(const MY_BITMAP *map, uint prefix_size)
|
||||
{
|
||||
uint prefix_bits= prefix_size & 0x7, res;
|
||||
uchar *m= (uchar*)map->bitmap;
|
||||
uchar *end_prefix= m+prefix_size/8;
|
||||
uint prefix_mask= last_byte_mask(prefix_size);
|
||||
uchar *m= (uchar*) map->bitmap;
|
||||
uchar *end_prefix= m+(prefix_size-1)/8;
|
||||
uchar *end;
|
||||
DBUG_ASSERT(m && prefix_size <= map->n_bits);
|
||||
end= m+no_bytes_in_map(map);
|
||||
|
||||
/* Empty prefix is always true */
|
||||
if (!prefix_size)
|
||||
return 1;
|
||||
|
||||
while (m < end_prefix)
|
||||
if (*m++ != 0xff)
|
||||
return 0;
|
||||
|
||||
*map->last_word_ptr&= ~map->last_word_mask; /*Clear bits*/
|
||||
res= 0;
|
||||
if (prefix_bits && *m++ != (1 << prefix_bits)-1)
|
||||
goto ret;
|
||||
end= ((uchar*) map->bitmap) + no_bytes_in_map(map) - 1;
|
||||
if (m == end)
|
||||
return ((*m & last_byte_mask(map->n_bits)) == prefix_mask);
|
||||
|
||||
while (m < end)
|
||||
if (*m++ != 0)
|
||||
goto ret;
|
||||
res= 1;
|
||||
ret:
|
||||
return res;
|
||||
if (*m != prefix_mask)
|
||||
return 0;
|
||||
|
||||
while (++m < end)
|
||||
if (*m != 0)
|
||||
return 0;
|
||||
return ((*m & last_byte_mask(map->n_bits)) == 0);
|
||||
}
|
||||
|
||||
|
||||
my_bool bitmap_is_set_all(const MY_BITMAP *map)
|
||||
{
|
||||
my_bitmap_map *data_ptr= map->bitmap;
|
||||
my_bitmap_map *end= map->last_word_ptr;
|
||||
*map->last_word_ptr |= map->last_word_mask;
|
||||
for (; data_ptr <= end; data_ptr++)
|
||||
for (; data_ptr < end; data_ptr++)
|
||||
if (*data_ptr != 0xFFFFFFFF)
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
return (*data_ptr | map->last_word_mask) == 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
|
||||
@ -308,13 +324,11 @@ my_bool bitmap_is_clear_all(const MY_BITMAP *map)
|
||||
{
|
||||
my_bitmap_map *data_ptr= map->bitmap;
|
||||
my_bitmap_map *end;
|
||||
if (*map->last_word_ptr & ~map->last_word_mask)
|
||||
return FALSE;
|
||||
end= map->last_word_ptr;
|
||||
for (; data_ptr < end; data_ptr++)
|
||||
if (*data_ptr)
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
return (*data_ptr & ~map->last_word_mask) == 0;
|
||||
}
|
||||
|
||||
/* Return TRUE if map1 is a subset of map2 */
|
||||
@ -327,14 +341,13 @@ my_bool bitmap_is_subset(const MY_BITMAP *map1, const MY_BITMAP *map2)
|
||||
map1->n_bits==map2->n_bits);
|
||||
|
||||
end= map1->last_word_ptr;
|
||||
*map1->last_word_ptr &= ~map1->last_word_mask;
|
||||
*map2->last_word_ptr &= ~map2->last_word_mask;
|
||||
while (m1 <= end)
|
||||
while (m1 < end)
|
||||
{
|
||||
if ((*m1++) & ~(*m2++))
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
/* here both maps have the same number of bits - see assert above */
|
||||
return ((*m1 & ~*m2 & ~map1->last_word_mask) ? 0 : 1);
|
||||
}
|
||||
|
||||
/* True if bitmaps has any common bits */
|
||||
@ -347,14 +360,13 @@ my_bool bitmap_is_overlapping(const MY_BITMAP *map1, const MY_BITMAP *map2)
|
||||
map1->n_bits==map2->n_bits);
|
||||
|
||||
end= map1->last_word_ptr;
|
||||
*map1->last_word_ptr &= ~map1->last_word_mask;
|
||||
*map2->last_word_ptr &= ~map2->last_word_mask;
|
||||
while (m1 <= end)
|
||||
while (m1 < end)
|
||||
{
|
||||
if ((*m1++) & (*m2++))
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
/* here both maps have the same number of bits - see assert above */
|
||||
return ((*m1 & *m2 & ~map1->last_word_mask) ? 1 : 0);
|
||||
}
|
||||
|
||||
|
||||
@ -366,15 +378,15 @@ void bitmap_intersect(MY_BITMAP *map, const MY_BITMAP *map2)
|
||||
DBUG_ASSERT(map->bitmap && map2->bitmap);
|
||||
|
||||
end= to+min(len,len2);
|
||||
*map2->last_word_ptr&= ~map2->last_word_mask; /*Clear last bits in map2*/
|
||||
while (to < end)
|
||||
*to++ &= *from++;
|
||||
|
||||
if (len2 < len)
|
||||
if (len2 <= len)
|
||||
{
|
||||
end+=len-len2;
|
||||
to[-1]&= ~map2->last_word_mask; /* Clear last not relevant bits */
|
||||
end+= len-len2;
|
||||
while (to < end)
|
||||
*to++=0;
|
||||
*to++= 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -386,13 +398,12 @@ my_bool bitmap_union_is_set_all(const MY_BITMAP *map1, const MY_BITMAP *map2)
|
||||
|
||||
DBUG_ASSERT(map1->bitmap && map2->bitmap &&
|
||||
map1->n_bits==map2->n_bits);
|
||||
*map1->last_word_ptr|= map1->last_word_mask;
|
||||
|
||||
end= map1->last_word_ptr;
|
||||
while ( m1 <= end)
|
||||
while ( m1 < end)
|
||||
if ((*m1++ | *m2++) != 0xFFFFFFFF)
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
/* here both maps have the same number of bits - see assert above */
|
||||
return ((*m1 | *m2 | map1->last_word_mask) != 0xFFFFFFFF);
|
||||
}
|
||||
|
||||
|
||||
@ -479,14 +490,13 @@ void bitmap_invert(MY_BITMAP *map)
|
||||
uint bitmap_bits_set(const MY_BITMAP *map)
|
||||
{
|
||||
uchar *m= (uchar*)map->bitmap;
|
||||
uchar *end= m + no_bytes_in_map(map);
|
||||
uchar *end= m + no_bytes_in_map(map) - 1;
|
||||
uint res= 0;
|
||||
|
||||
DBUG_ASSERT(map->bitmap);
|
||||
*map->last_word_ptr&= ~map->last_word_mask; /*Reset last bits to zero*/
|
||||
while (m < end)
|
||||
res+= my_count_bits_ushort(*m++);
|
||||
return res;
|
||||
return res + my_count_bits_ushort(*m & last_byte_mask(map->n_bits));
|
||||
}
|
||||
|
||||
|
||||
@ -510,27 +520,30 @@ uint bitmap_get_first_set(const MY_BITMAP *map)
|
||||
|
||||
DBUG_ASSERT(map->bitmap);
|
||||
data_ptr= map->bitmap;
|
||||
*map->last_word_ptr &= ~map->last_word_mask;
|
||||
|
||||
for (i=0; data_ptr <= end; data_ptr++, i++)
|
||||
{
|
||||
for (i=0; data_ptr < end; data_ptr++, i++)
|
||||
if (*data_ptr)
|
||||
goto found;
|
||||
if (!(*data_ptr & ~map->last_word_mask))
|
||||
return MY_BIT_NONE;
|
||||
|
||||
found:
|
||||
{
|
||||
byte_ptr= (uchar*)data_ptr;
|
||||
for (j=0; ; j++, byte_ptr++)
|
||||
{
|
||||
byte_ptr= (uchar*)data_ptr;
|
||||
for (j=0; ; j++, byte_ptr++)
|
||||
if (*byte_ptr)
|
||||
{
|
||||
if (*byte_ptr)
|
||||
for (k=0; ; k++)
|
||||
{
|
||||
for (k=0; ; k++)
|
||||
{
|
||||
if (*byte_ptr & (1 << k))
|
||||
return (i*32) + (j*8) + k;
|
||||
}
|
||||
if (*byte_ptr & (1 << k))
|
||||
return (i*32) + (j*8) + k;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return MY_BIT_NONE;
|
||||
DBUG_ASSERT(0);
|
||||
return MY_BIT_NONE; /* Impossible */
|
||||
}
|
||||
|
||||
|
||||
@ -544,25 +557,29 @@ uint bitmap_get_first(const MY_BITMAP *map)
|
||||
data_ptr= map->bitmap;
|
||||
*map->last_word_ptr|= map->last_word_mask;
|
||||
|
||||
for (i=0; data_ptr <= end; data_ptr++, i++)
|
||||
{
|
||||
for (i=0; data_ptr < end; data_ptr++, i++)
|
||||
if (*data_ptr != 0xFFFFFFFF)
|
||||
goto found;
|
||||
if ((*data_ptr | map->last_word_mask) == 0xFFFFFFFF)
|
||||
return MY_BIT_NONE;
|
||||
|
||||
found:
|
||||
{
|
||||
byte_ptr= (uchar*)data_ptr;
|
||||
for (j=0; ; j++, byte_ptr++)
|
||||
{
|
||||
byte_ptr= (uchar*)data_ptr;
|
||||
for (j=0; ; j++, byte_ptr++)
|
||||
if (*byte_ptr != 0xFF)
|
||||
{
|
||||
if (*byte_ptr != 0xFF)
|
||||
for (k=0; ; k++)
|
||||
{
|
||||
for (k=0; ; k++)
|
||||
{
|
||||
if (!(*byte_ptr & (1 << k)))
|
||||
return (i*32) + (j*8) + k;
|
||||
}
|
||||
if (!(*byte_ptr & (1 << k)))
|
||||
return (i*32) + (j*8) + k;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return MY_BIT_NONE;
|
||||
DBUG_ASSERT(0);
|
||||
return MY_BIT_NONE; /* Impossible */
|
||||
}
|
||||
|
||||
|
||||
@ -777,7 +794,7 @@ uint get_rand_bit(uint bitsize)
|
||||
return (rand() % bitsize);
|
||||
}
|
||||
|
||||
bool test_set_get_clear_bit(MY_BITMAP *map, uint bitsize)
|
||||
my_bool test_set_get_clear_bit(MY_BITMAP *map, uint bitsize)
|
||||
{
|
||||
uint i, test_bit;
|
||||
uint no_loops= bitsize > 128 ? 128 : bitsize;
|
||||
@ -800,7 +817,7 @@ error2:
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
bool test_flip_bit(MY_BITMAP *map, uint bitsize)
|
||||
my_bool test_flip_bit(MY_BITMAP *map, uint bitsize)
|
||||
{
|
||||
uint i, test_bit;
|
||||
uint no_loops= bitsize > 128 ? 128 : bitsize;
|
||||
@ -823,13 +840,13 @@ error2:
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
bool test_operators(MY_BITMAP *map __attribute__((unused)),
|
||||
my_bool test_operators(MY_BITMAP *map __attribute__((unused)),
|
||||
uint bitsize __attribute__((unused)))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
bool test_get_all_bits(MY_BITMAP *map, uint bitsize)
|
||||
my_bool test_get_all_bits(MY_BITMAP *map, uint bitsize)
|
||||
{
|
||||
uint i;
|
||||
bitmap_set_all(map);
|
||||
@ -871,7 +888,7 @@ error6:
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
bool test_compare_operators(MY_BITMAP *map, uint bitsize)
|
||||
my_bool test_compare_operators(MY_BITMAP *map, uint bitsize)
|
||||
{
|
||||
uint i, j, test_bit1, test_bit2, test_bit3,test_bit4;
|
||||
uint no_loops= bitsize > 128 ? 128 : bitsize;
|
||||
@ -977,7 +994,7 @@ error5:
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
bool test_count_bits_set(MY_BITMAP *map, uint bitsize)
|
||||
my_bool test_count_bits_set(MY_BITMAP *map, uint bitsize)
|
||||
{
|
||||
uint i, bit_count=0, test_bit;
|
||||
uint no_loops= bitsize > 128 ? 128 : bitsize;
|
||||
@ -1003,7 +1020,7 @@ error2:
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
bool test_get_first_bit(MY_BITMAP *map, uint bitsize)
|
||||
my_bool test_get_first_bit(MY_BITMAP *map, uint bitsize)
|
||||
{
|
||||
uint i, test_bit;
|
||||
uint no_loops= bitsize > 128 ? 128 : bitsize;
|
||||
@ -1028,7 +1045,7 @@ error2:
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
bool test_get_next_bit(MY_BITMAP *map, uint bitsize)
|
||||
my_bool test_get_next_bit(MY_BITMAP *map, uint bitsize)
|
||||
{
|
||||
uint i, j, test_bit;
|
||||
uint no_loops= bitsize > 128 ? 128 : bitsize;
|
||||
@ -1047,7 +1064,7 @@ error1:
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
bool test_prefix(MY_BITMAP *map, uint bitsize)
|
||||
my_bool test_prefix(MY_BITMAP *map, uint bitsize)
|
||||
{
|
||||
uint i, j, test_bit;
|
||||
uint no_loops= bitsize > 128 ? 128 : bitsize;
|
||||
@ -1082,7 +1099,7 @@ error3:
|
||||
}
|
||||
|
||||
|
||||
bool do_test(uint bitsize)
|
||||
my_bool do_test(uint bitsize)
|
||||
{
|
||||
MY_BITMAP map;
|
||||
my_bitmap_map buf[1024];
|
||||
|
@ -2591,6 +2591,7 @@ public:
|
||||
{
|
||||
return (*ref)->const_item() ? 0 : OUTER_REF_TABLE_BIT;
|
||||
}
|
||||
table_map not_null_tables() const { return 0; }
|
||||
virtual Ref_Type ref_type() { return OUTER_REF; }
|
||||
bool check_inner_refs_processor(uchar * arg);
|
||||
};
|
||||
|
@ -197,7 +197,8 @@ bool init_read_record(READ_RECORD *info,THD *thd, TABLE *table,
|
||||
tempfile= &select->file;
|
||||
else
|
||||
tempfile= table->sort.io_cache;
|
||||
if (tempfile && my_b_inited(tempfile)) // Test if ref-records was used
|
||||
if (tempfile && my_b_inited(tempfile) &&
|
||||
!(select && select->quick))
|
||||
{
|
||||
DBUG_PRINT("info",("using rr_from_tempfile"));
|
||||
info->read_record= (table->sort.addon_field ?
|
||||
|
@ -6630,6 +6630,9 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
|
||||
if (tmp_cond)
|
||||
{
|
||||
JOIN_TAB *cond_tab= tab < first_inner_tab ? first_inner_tab : tab;
|
||||
Item **sel_cond_ref= tab < first_inner_tab ?
|
||||
&first_inner_tab->on_precond :
|
||||
&tab->select_cond;
|
||||
/*
|
||||
First add the guards for match variables of
|
||||
all embedding outer join operations.
|
||||
@ -6652,14 +6655,14 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
|
||||
tmp_cond->quick_fix_field();
|
||||
/* Add the predicate to other pushed down predicates */
|
||||
DBUG_PRINT("info", ("Item_cond_and"));
|
||||
cond_tab->select_cond= !cond_tab->select_cond ? tmp_cond :
|
||||
new Item_cond_and(cond_tab->select_cond,
|
||||
tmp_cond);
|
||||
*sel_cond_ref= !(*sel_cond_ref) ?
|
||||
tmp_cond :
|
||||
new Item_cond_and(*sel_cond_ref, tmp_cond);
|
||||
DBUG_PRINT("info", ("Item_cond_and 0x%lx",
|
||||
(ulong)cond_tab->select_cond));
|
||||
if (!cond_tab->select_cond)
|
||||
DBUG_RETURN(1);
|
||||
cond_tab->select_cond->quick_fix_field();
|
||||
(ulong)(*sel_cond_ref)));
|
||||
if (!(*sel_cond_ref))
|
||||
DBUG_RETURN(1);
|
||||
(*sel_cond_ref)->quick_fix_field();
|
||||
}
|
||||
}
|
||||
first_inner_tab= first_inner_tab->first_upper;
|
||||
@ -11650,7 +11653,7 @@ sub_select(JOIN *join,JOIN_TAB *join_tab,bool end_of_records)
|
||||
return (*join_tab->next_select)(join,join_tab+1,end_of_records);
|
||||
|
||||
int error;
|
||||
enum_nested_loop_state rc;
|
||||
enum_nested_loop_state rc= NESTED_LOOP_OK;
|
||||
READ_RECORD *info= &join_tab->read_record;
|
||||
|
||||
if (join->resume_nested_loop)
|
||||
@ -11678,11 +11681,16 @@ sub_select(JOIN *join,JOIN_TAB *join_tab,bool end_of_records)
|
||||
|
||||
/* Set first_unmatched for the last inner table of this group */
|
||||
join_tab->last_inner->first_unmatched= join_tab;
|
||||
if (join_tab->on_precond && !join_tab->on_precond->val_int())
|
||||
rc= NESTED_LOOP_NO_MORE_ROWS;
|
||||
}
|
||||
join->thd->row_count= 0;
|
||||
|
||||
error= (*join_tab->read_first_record)(join_tab);
|
||||
rc= evaluate_join_record(join, join_tab, error);
|
||||
if (rc != NESTED_LOOP_NO_MORE_ROWS)
|
||||
{
|
||||
error= (*join_tab->read_first_record)(join_tab);
|
||||
rc= evaluate_join_record(join, join_tab, error);
|
||||
}
|
||||
}
|
||||
|
||||
while (rc == NESTED_LOOP_OK)
|
||||
|
@ -154,7 +154,9 @@ typedef struct st_join_table {
|
||||
TABLE *table;
|
||||
KEYUSE *keyuse; /**< pointer to first used key */
|
||||
SQL_SELECT *select;
|
||||
COND *select_cond;
|
||||
COND *select_cond;
|
||||
COND *on_precond; /**< part of on condition to check before
|
||||
accessing the first inner table */
|
||||
QUICK_SELECT_I *quick;
|
||||
Item **on_expr_ref; /**< pointer to the associated on expression */
|
||||
COND_EQUAL *cond_equal; /**< multiple equalities for the on expression */
|
||||
|
@ -1,7 +1,49 @@
|
||||
PBXT Release Notes
|
||||
==================
|
||||
|
||||
+------- 1.0.11 Pre-GA - 2010-05-11
|
||||
------- 1.0.11-7 Pre-GA - 2010-09-09
|
||||
|
||||
RN336: Compiled and tested with MySQL 5.1.50.
|
||||
|
||||
RN335: Fixed bug #523994: Deleting all records does not update table statistics.
|
||||
|
||||
RN334: Made a change to reduce the time that only temporary tables exist during the ALTER TABLE and REPAIR TABLE statements. This increases the chance of recovery if a crash occurs during these operations.
|
||||
|
||||
RN333: Log name of table when PBXT recovers an index on startup. If an error occurs during index recovery, the index is set to "repair pending".
|
||||
|
||||
RN332: Fixed an inifinite loop when a record in a row is corrupt. Added logging and set the table to "repair pending" in this case.
|
||||
|
||||
RN331: Fixed bug #626890: Crash on truncate table operation.
|
||||
|
||||
RN330: Added additional checks for corruption of the index free list.
|
||||
|
||||
------- 1.0.11-6 Pre-GA - 2010-07-08
|
||||
|
||||
RN329: Fixed bug #601245: make fails. PBXT did not compile if the partition engine was disabled in the MySQL build.
|
||||
|
||||
------- 1.0.11-5 Pre-GA - 2010-06-18
|
||||
|
||||
RN328: Fixed bug #595478: Compile fails (1.0.11-4).
|
||||
|
||||
------- 1.0.11-4 Pre-GA - 2010-06-15
|
||||
|
||||
RN327: Fixed a bug that caused a crash during delete on the index. The crash occurred due to memory overwrite when a long key is promoted after a shorter key is deleted, and the difference causes a node size overflow.
|
||||
|
||||
------- 1.0.11-3 Pre-GA - 2010-06-11
|
||||
|
||||
RN326: Fixed bug #587740: pbxt-1.0.11-pre2-ga first time create partition table error. This was not a new bug. The problem was the PBXT system table's .frm files are corrupted when the first PBXT table created is a partition table.
|
||||
|
||||
RN325: Fixed the "to-sweep" column output in xtstat.
|
||||
|
||||
------- 1.0.11-2 Pre-GA - 2010-05-26
|
||||
|
||||
RN324: Fixed bug #584070:pbxt-1.0.11-pre-ga does not work with mysql 5.1.47. This bug fix removes a hack which was done to avoid running into the LOCK_plugin lock.
|
||||
|
||||
------- 1.0.11-1 Pre-GA - 2010-05-19
|
||||
|
||||
RN323: Detect corruption of a key length in an index page. This bug fix avoids a possible crash due to index page corruption.
|
||||
|
||||
------- 1.0.11 Pre-GA - 2010-05-11
|
||||
|
||||
RN322: Creating a table the references a non-existing table can now only be done if you set: foreign_key_checks = 0. Also fixed a failure when creating tables with recursive foreign key declarations.
|
||||
|
||||
|
@ -40,8 +40,8 @@ libpbxt_la_LDFLAGS = -module
|
||||
# These are the warning Drizzle uses:
|
||||
# DRIZZLE_WARNINGS = -W -Wall -Wextra -pedantic -Wundef -Wredundant-decls -Wno-strict-aliasing -Wno-long-long -Wno-unused-parameter
|
||||
|
||||
libpbxt_la_CXXFLAGS = -shared $(AM_CXXFLAGS) -DMYSQL_DYNAMIC_PLUGIN
|
||||
libpbxt_la_CFLAGS = -shared $(AM_CFLAGS) -DMYSQL_DYNAMIC_PLUGIN -std=c99
|
||||
libpbxt_la_CXXFLAGS = $(AM_CXXFLAGS) -DMYSQL_DYNAMIC_PLUGIN
|
||||
libpbxt_la_CFLAGS = $(AM_CFLAGS) -DMYSQL_DYNAMIC_PLUGIN -std=c99
|
||||
|
||||
EXTRA_LIBRARIES = libpbxt.a
|
||||
noinst_LIBRARIES = libpbxt.a
|
||||
|
@ -667,6 +667,9 @@ xtPublic void xt_ind_init(XTThreadPtr self, size_t cache_size)
|
||||
#ifdef XT_USE_DIRECT_IO_ON_INDEX
|
||||
block->cb_data = buffer;
|
||||
buffer += XT_INDEX_PAGE_SIZE;
|
||||
#endif
|
||||
#ifdef CHECK_BLOCK_TRAILERS
|
||||
XT_SET_DISK_4(block->cp_check, 0xDEADBEEF);
|
||||
#endif
|
||||
ind_cac_globals.cg_free_list = block;
|
||||
block++;
|
||||
@ -684,6 +687,19 @@ xtPublic void xt_ind_init(XTThreadPtr self, size_t cache_size)
|
||||
cont_(a);
|
||||
}
|
||||
|
||||
#ifdef CHECK_BLOCK_TRAILERS
|
||||
xtPublic void check_block_trailers()
|
||||
{
|
||||
XTIndBlockPtr block;
|
||||
|
||||
block = ind_cac_globals.cg_blocks;
|
||||
for (u_int i=0; i<ind_cac_globals.cg_block_count; i++) {
|
||||
ASSERT_NS(XT_GET_DISK_4(block->cp_check) == 0xDEADBEEF);
|
||||
block++;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
xtPublic void xt_ind_exit(XTThreadPtr self)
|
||||
{
|
||||
#ifdef XT_USE_MYSYS
|
||||
@ -1283,7 +1299,7 @@ static XTIndBlockPtr ind_cac_fetch(XTOpenTablePtr ot, XTIndexPtr ind, xtIndexNod
|
||||
* Conditionally count the number of deleted entries in the index:
|
||||
* We do this before other threads can read the block.
|
||||
*/
|
||||
if (ind->mi_lazy_delete && read_data)
|
||||
if (ind && ind->mi_lazy_delete && read_data)
|
||||
xt_ind_count_deleted_items(ot->ot_table, ind, block);
|
||||
|
||||
/* Add to the hash table: */
|
||||
@ -1357,6 +1373,9 @@ xtPublic xtBool xt_ind_write(XTOpenTablePtr ot, XTIndexPtr ind, xtIndexNodeID ad
|
||||
IDX_CAC_UNLOCK(seg, ot->ot_thread);
|
||||
#ifdef XT_TRACK_INDEX_UPDATES
|
||||
ot->ot_ind_changed++;
|
||||
#endif
|
||||
#ifdef CHECK_BLOCK_TRAILERS
|
||||
check_block_trailers();
|
||||
#endif
|
||||
return OK;
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ struct XTIdxReadBuffer;
|
||||
|
||||
#ifdef DEBUG
|
||||
//#define XT_USE_CACHE_DEBUG_SIZES
|
||||
//#define CHECK_BLOCK_TRAILERS
|
||||
#endif
|
||||
|
||||
#ifdef XT_USE_CACHE_DEBUG_SIZES
|
||||
@ -116,6 +117,9 @@ typedef struct XTIndBlock {
|
||||
#else
|
||||
xtWord1 cb_data[XT_INDEX_PAGE_SIZE];
|
||||
#endif
|
||||
#ifdef CHECK_BLOCK_TRAILERS
|
||||
xtWord1 cp_check[4];
|
||||
#endif
|
||||
} XTIndBlockRec, *XTIndBlockPtr;
|
||||
|
||||
typedef struct XTIndReference {
|
||||
@ -177,6 +181,10 @@ xtBool xt_ind_copy_on_write(XTIndReferencePtr iref);
|
||||
XTIndHandlePtr xt_ind_get_handle(struct XTOpenTable *ot, XTIndexPtr ind, XTIndReferencePtr iref);
|
||||
void xt_ind_release_handle(XTIndHandlePtr handle, xtBool have_lock, XTThreadPtr thread);
|
||||
|
||||
#ifdef CHECK_BLOCK_TRAILERS
|
||||
extern void check_block_trailers();
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
//#define DEBUG_CHECK_IND_CACHE
|
||||
#endif
|
||||
|
@ -117,6 +117,7 @@ typedef struct XTDatabase : public XTHeap {
|
||||
XTSortedListPtr db_table_by_id;
|
||||
XTSortedListPtr db_table_paths; /* A list of table paths used by this database. */
|
||||
xtBool db_multi_path;
|
||||
XTSortedListPtr db_error_list; /* A list of errors already reported. */
|
||||
|
||||
/* The open table pool: */
|
||||
XTAllTablePoolsRec db_ot_pool;
|
||||
|
@ -396,7 +396,7 @@ void XTToken::expectNumber(XTThreadPtr self)
|
||||
struct charset_info_st;
|
||||
|
||||
class XTTokenizer {
|
||||
const struct charset_info_st *tkn_charset;
|
||||
MX_CONST_CHARSET_INFO *tkn_charset;
|
||||
char *tkn_cstring;
|
||||
char *tkn_curr_pos;
|
||||
XTToken *tkn_current;
|
||||
@ -1324,7 +1324,7 @@ void XTParseTable::parseDropIndex(XTThreadPtr self)
|
||||
class XTCreateTable : public XTParseTable {
|
||||
public:
|
||||
bool ct_convert;
|
||||
const struct charset_info_st *ct_charset;
|
||||
MX_CONST_CHARSET_INFO *ct_charset;
|
||||
XTPathStrPtr ct_tab_path;
|
||||
u_int ct_contraint_no;
|
||||
XTDDTable *ct_curr_table;
|
||||
@ -2039,11 +2039,6 @@ void XTDDTableRef::deleteAllRows(XTThreadPtr self)
|
||||
if (!(ot = xt_db_open_table_using_tab(tr_fkey->co_table->dt_table, self)))
|
||||
xt_throw(self);
|
||||
|
||||
/* {FREE-ROWS-BAD} */
|
||||
/*
|
||||
row_count = ((xtInt8) ot->ot_table->tab_row_eof_id) - 1;
|
||||
row_count -= (xtInt8) ot->ot_table->tab_row_fnum;
|
||||
*/
|
||||
/* Check if there are any rows in the referencing table: */
|
||||
if (!xt_tab_seq_init(ot))
|
||||
goto failed;
|
||||
|
@ -1249,7 +1249,7 @@ xtBool XTDataLogBuffer::dlb_write_thru_log(xtLogID XT_NDEBUG_UNUSED(log_id), xtL
|
||||
*/
|
||||
dlb_data_log->dlf_log_eof += size;
|
||||
#ifdef DEBUG
|
||||
if ((ulonglong) (log_offset + size) > (ulonglong) dlb_max_write_offset)
|
||||
if (log_offset + (xtLogOffset) size > (xtLogOffset) dlb_max_write_offset)
|
||||
dlb_max_write_offset = log_offset + size;
|
||||
#endif
|
||||
dlb_flush_required = TRUE;
|
||||
@ -1291,7 +1291,7 @@ xtBool XTDataLogBuffer::dlb_append_log(xtLogID XT_NDEBUG_UNUSED(log_id), xtLogOf
|
||||
if (!xt_pwrite_file(dlb_data_log->dlf_log_file, log_offset, size, data, &thread->st_statistics.st_data, thread))
|
||||
return FAILED;
|
||||
#ifdef DEBUG
|
||||
if ((ulonglong) (log_offset + size) > (ulonglong) dlb_max_write_offset)
|
||||
if (log_offset + (xtLogOffset) size > (xtLogOffset) dlb_max_write_offset)
|
||||
dlb_max_write_offset = log_offset + size;
|
||||
#endif
|
||||
dlb_flush_required = TRUE;
|
||||
@ -1734,8 +1734,8 @@ static xtBool dl_collect_garbage(XTThreadPtr self, XTDatabaseHPtr db, XTDataLogF
|
||||
xtLogOffset src_log_offset;
|
||||
xtLogID curr_log_id;
|
||||
xtLogOffset curr_log_offset;
|
||||
xtLogID dest_log_id= 0;
|
||||
xtLogOffset dest_log_offset= 0;
|
||||
xtLogID dest_log_id = 0;
|
||||
xtLogOffset dest_log_offset = 0;
|
||||
off_t garbage_count = 0;
|
||||
|
||||
memset(&cs, 0, sizeof(XTCompactorStateRec));
|
||||
|
@ -1622,8 +1622,11 @@ int xt_create_table_frm(handlerton *hton, THD* thd, const char *db, const char *
|
||||
COLUMN_FORMAT_TYPE_FIXED,
|
||||
#endif
|
||||
NULL /*default_value*/, NULL /*on_update_value*/, &comment, NULL /*change*/,
|
||||
NULL /*interval_list*/, info->field_charset, 0 /*uint_geom_type*/,
|
||||
NULL /*vcol_info*/, NULL /* create options */))
|
||||
NULL /*interval_list*/, info->field_charset, 0 /*uint_geom_type*/
|
||||
#if defined(MARIADB_BASE_VERSION) && MYSQL_VERSION_ID > 50200
|
||||
, NULL /*vcol_info*/, NULL /* create options */
|
||||
#endif
|
||||
))
|
||||
#endif
|
||||
goto error;
|
||||
|
||||
@ -1655,8 +1658,17 @@ int xt_create_table_frm(handlerton *hton, THD* thd, const char *db, const char *
|
||||
if (mysql_create_table_no_lock(thd, db, name, &create_info, &table_proto, &stmt->alter_info, 1, 0))
|
||||
goto error;
|
||||
#else
|
||||
#ifdef WITH_PARTITION_STORAGE_ENGINE
|
||||
partition_info *part_info;
|
||||
|
||||
part_info = thd->work_part_info;
|
||||
thd->work_part_info = NULL;
|
||||
#endif
|
||||
if (mysql_create_table_no_lock(thd, db, name, &mylex.create_info, &mylex.alter_info, 1, 0))
|
||||
goto error;
|
||||
#ifdef WITH_PARTITION_STORAGE_ENGINE
|
||||
thd->work_part_info = part_info;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
noerror:
|
||||
|
@ -1232,6 +1232,11 @@ static int pbxt_init(void *p)
|
||||
THD *thd = NULL;
|
||||
|
||||
#ifndef DRIZZLED
|
||||
#if MYSQL_VERSION_ID < 50147
|
||||
/* A hack which is no longer required after 5.1.46 */
|
||||
extern myxt_mutex_t LOCK_plugin;
|
||||
#endif
|
||||
|
||||
/* {MYSQL QUIRK}
|
||||
* I have to release this lock for PBXT recovery to
|
||||
* work, because it needs to open .frm files.
|
||||
@ -1248,6 +1253,9 @@ static int pbxt_init(void *p)
|
||||
* Only real problem, 2 threads try to load the same
|
||||
* plugin at the same time.
|
||||
*/
|
||||
#if MYSQL_VERSION_ID < 50147
|
||||
myxt_mutex_unlock(&LOCK_plugin);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Can't do this here yet, because I need a THD! */
|
||||
@ -1281,6 +1289,11 @@ static int pbxt_init(void *p)
|
||||
|
||||
if (thd)
|
||||
myxt_destroy_thread(thd, FALSE);
|
||||
#ifndef DRIZZLED
|
||||
#if MYSQL_VERSION_ID < 50147
|
||||
myxt_mutex_lock(&LOCK_plugin);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@ -1941,8 +1954,13 @@ xtPublic int ha_pbxt::reopen()
|
||||
* selectity of the indices, as soon as the number of rows
|
||||
* exceeds 200 (see [**])
|
||||
*/
|
||||
#ifdef XT_ROW_COUNT_CORRECTED
|
||||
/* {CORRECTED-ROW-COUNT} */
|
||||
pb_share->sh_recalc_selectivity = (pb_share->sh_table->tab_row_eof_id - 1 - pb_share->sh_table->tab_row_fnum) < 150;
|
||||
#else
|
||||
/* {FREE-ROWS-BAD} */
|
||||
pb_share->sh_recalc_selectivity = (pb_share->sh_table->tab_row_eof_id - 1 /* - pb_share->sh_table->tab_row_fnum */) < 150;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* I am not doing this anymore because it was only required
|
||||
@ -2289,6 +2307,36 @@ void ha_pbxt::internal_close(THD *thd, struct XTThread *self)
|
||||
*/
|
||||
if (!thd || thd_sql_command(thd) == SQLCOM_FLUSH) // FLUSH TABLES
|
||||
xt_sync_flush_table(self, ot);
|
||||
else {
|
||||
/* This change is a result of a problem mentioned by Arjen.
|
||||
* REPAIR and ALTER lead to the following sequence:
|
||||
* 1. tab -- copy --> tmp1
|
||||
* 2. tab -- rename --> tmp2
|
||||
* 3. tmp1 -- rename --> tab
|
||||
* 4. delete tmp2
|
||||
*
|
||||
* PBXT flushes a table before rename.
|
||||
* In the sequence above results in a table flush in step 3 which can
|
||||
* take a very long time.
|
||||
*
|
||||
* The problem is, during this time frame we have only temp tables.
|
||||
* A crash in this state leaves the database in a bad state.
|
||||
*
|
||||
* To reduce the time in this state, the flush needs to be done
|
||||
* elsewhere. The code below causes the flish to occur after
|
||||
* step 1:
|
||||
*/
|
||||
switch (thd_sql_command(thd)) {
|
||||
case SQLCOM_REPAIR:
|
||||
case SQLCOM_RENAME_TABLE:
|
||||
case SQLCOM_OPTIMIZE:
|
||||
case SQLCOM_ANALYZE:
|
||||
case SQLCOM_ALTER_TABLE:
|
||||
case SQLCOM_CREATE_INDEX:
|
||||
xt_sync_flush_table(self, ot);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
freer_(); // xt_db_return_table_to_pool(ot);
|
||||
}
|
||||
@ -2349,9 +2397,15 @@ int ha_pbxt::open(const char *table_path, int XT_UNUSED(mode), uint XT_UNUSED(te
|
||||
#else
|
||||
xt_tab_load_row_pointers(self, pb_open_tab);
|
||||
#endif
|
||||
|
||||
xt_ind_set_index_selectivity(pb_open_tab, self);
|
||||
#ifdef XT_ROW_COUNT_CORRECTED
|
||||
/* {CORRECTED-ROW-COUNT} */
|
||||
pb_share->sh_recalc_selectivity = (pb_share->sh_table->tab_row_eof_id - 1 - pb_share->sh_table->tab_row_fnum) < 150;
|
||||
#else
|
||||
/* {FREE-ROWS-BAD} */
|
||||
pb_share->sh_recalc_selectivity = (pb_share->sh_table->tab_row_eof_id - 1 /* - pb_share->sh_table->tab_row_fnum */) < 150;
|
||||
#endif
|
||||
}
|
||||
|
||||
init_auto_increment(0);
|
||||
@ -3922,6 +3976,8 @@ int ha_pbxt::info(uint flag)
|
||||
|
||||
if ((ot = pb_open_tab)) {
|
||||
if (flag & HA_STATUS_VARIABLE) {
|
||||
register XTTableHPtr tab = ot->ot_table;
|
||||
|
||||
/* {FREE-ROWS-BAD}
|
||||
* Free row count is not reliable, so ignore it.
|
||||
* The problem is if tab_row_fnum > tab_row_eof_id - 1 then
|
||||
@ -3948,11 +4004,26 @@ int ha_pbxt::info(uint flag)
|
||||
* the actual number of vectors. But it must assume that it has at
|
||||
* least EXTRA_RECORDS vectors.
|
||||
*/
|
||||
stats.deleted = /* ot->ot_table->tab_row_fnum */ 0;
|
||||
stats.records = (ha_rows) (ot->ot_table->tab_row_eof_id - 1 /* - stats.deleted */);
|
||||
stats.data_file_length = xt_rec_id_to_rec_offset(ot->ot_table, ot->ot_table->tab_rec_eof_id);
|
||||
stats.index_file_length = xt_ind_node_to_offset(ot->ot_table, ot->ot_table->tab_ind_eof);
|
||||
stats.delete_length = ot->ot_table->tab_rec_fnum * ot->ot_rec_size;
|
||||
#ifdef XT_ROW_COUNT_CORRECTED
|
||||
if (tab->tab_row_eof_id <= tab->tab_row_fnum ||
|
||||
(!tab->tab_row_free_id && tab->tab_row_fnum))
|
||||
xt_tab_check_free_lists(NULL, ot, false, true);
|
||||
stats.records = (ha_rows) tab->tab_row_eof_id - 1;
|
||||
if (stats.records >= tab->tab_row_fnum) {
|
||||
stats.deleted = tab->tab_row_fnum;
|
||||
stats.records -= stats.deleted;
|
||||
}
|
||||
else {
|
||||
stats.deleted = 0;
|
||||
stats.records = 2;
|
||||
}
|
||||
#else
|
||||
stats.deleted = /* tab->tab_row_fnum */ 0;
|
||||
stats.records = (ha_rows) (tab->tab_row_eof_id - 1 /* - stats.deleted */);
|
||||
#endif
|
||||
stats.data_file_length = xt_rec_id_to_rec_offset(tab, tab->tab_rec_eof_id);
|
||||
stats.index_file_length = xt_ind_node_to_offset(tab, tab->tab_ind_eof);
|
||||
stats.delete_length = tab->tab_rec_fnum * ot->ot_rec_size;
|
||||
//check_time = info.check_time;
|
||||
stats.mean_rec_length = (ulong) ot->ot_rec_size;
|
||||
}
|
||||
@ -4577,13 +4648,24 @@ xtPublic int ha_pbxt::external_lock(THD *thd, int lock_type)
|
||||
}
|
||||
|
||||
if (pb_share->sh_recalc_selectivity) {
|
||||
#ifdef XT_ROW_COUNT_CORRECTED
|
||||
/* {CORRECTED-ROW-COUNT} */
|
||||
if ((pb_share->sh_table->tab_row_eof_id - 1 - pb_share->sh_table->tab_row_fnum) >= 200)
|
||||
#else
|
||||
/* {FREE-ROWS-BAD} */
|
||||
if ((pb_share->sh_table->tab_row_eof_id - 1 /* - pb_share->sh_table->tab_row_fnum */) >= 200) {
|
||||
if ((pb_share->sh_table->tab_row_eof_id - 1 /* - pb_share->sh_table->tab_row_fnum */) >= 200)
|
||||
#endif
|
||||
{
|
||||
/* [**] */
|
||||
pb_share->sh_recalc_selectivity = FALSE;
|
||||
xt_ind_set_index_selectivity(pb_open_tab, self);
|
||||
#ifdef XT_ROW_COUNT_CORRECTED
|
||||
/* {CORRECTED-ROW-COUNT} */
|
||||
pb_share->sh_recalc_selectivity = (pb_share->sh_table->tab_row_eof_id - 1 - pb_share->sh_table->tab_row_fnum) < 150;
|
||||
#else
|
||||
/* {FREE-ROWS-BAD} */
|
||||
pb_share->sh_recalc_selectivity = (pb_share->sh_table->tab_row_eof_id - 1 /* - pb_share->sh_table->tab_row_fnum */) < 150;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -4631,6 +4713,17 @@ xtPublic int ha_pbxt::external_lock(THD *thd, int lock_type)
|
||||
goto complete;
|
||||
}
|
||||
cont_(a);
|
||||
|
||||
/* Occurs if you do:
|
||||
* truncate table t1;
|
||||
* truncate table t1;
|
||||
*/
|
||||
if (!pb_open_tab) {
|
||||
if ((err = reopen())) {
|
||||
pb_ex_in_use = 0;
|
||||
goto complete;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
pb_ex_in_use = 1;
|
||||
@ -6069,7 +6162,7 @@ mysql_declare_plugin(pbxt)
|
||||
drizzle_declare_plugin_end;
|
||||
#else
|
||||
mysql_declare_plugin_end;
|
||||
#ifdef MARIADB_BASE_VERSION
|
||||
#if defined(MARIADB_BASE_VERSION) && MYSQL_VERSION_ID > 50200
|
||||
maria_declare_plugin(pbxt)
|
||||
{ /* PBXT */
|
||||
MYSQL_STORAGE_ENGINE_PLUGIN,
|
||||
@ -6083,7 +6176,7 @@ maria_declare_plugin(pbxt)
|
||||
0x0001 /* 0.1 */,
|
||||
NULL, /* status variables */
|
||||
pbxt_system_variables, /* system variables */
|
||||
"1.0.09g RC3", /* string version */
|
||||
"1.0.11-7 Pre-GA", /* string version */
|
||||
MariaDB_PLUGIN_MATURITY_GAMMA /* maturity */
|
||||
},
|
||||
{ /* PBXT_STATISTICS */
|
||||
@ -6098,7 +6191,7 @@ maria_declare_plugin(pbxt)
|
||||
0x0005,
|
||||
NULL, /* status variables */
|
||||
NULL, /* system variables */
|
||||
"1.0.09g RC3", /* string version */
|
||||
"1.0.11-7 Pre-GA", /* string version */
|
||||
MariaDB_PLUGIN_MATURITY_GAMMA /* maturity */
|
||||
}
|
||||
maria_declare_plugin_end;
|
||||
|
@ -272,10 +272,17 @@ static xtBool idx_new_branch(XTOpenTablePtr ot, XTIndexPtr ind, xtIndexNodeID *a
|
||||
}
|
||||
|
||||
if ((XT_NODE_ID(wrote_pos) = XT_NODE_ID(tab->tab_ind_free))) {
|
||||
xtIndexNodeID next_node;
|
||||
|
||||
/* Use the block on the free list: */
|
||||
if (!xt_ind_read_bytes(ot, ind, wrote_pos, sizeof(XTIndFreeBlockRec), (xtWord1 *) &free_block))
|
||||
if (!xt_ind_read_bytes(ot, NULL, wrote_pos, sizeof(XTIndFreeBlockRec), (xtWord1 *) &free_block))
|
||||
goto failed;
|
||||
XT_NODE_ID(tab->tab_ind_free) = (xtIndexNodeID) XT_GET_DISK_8(free_block.if_next_block_8);
|
||||
XT_NODE_ID(next_node) = (xtIndexNodeID) XT_GET_DISK_8(free_block.if_next_block_8);
|
||||
if (XT_NODE_ID(next_node) >= XT_NODE_ID(tab->tab_ind_eof)) {
|
||||
xt_register_taberr(XT_REG_CONTEXT, XT_ERR_INDEX_CORRUPTED, tab->tab_name);
|
||||
goto failed;
|
||||
}
|
||||
XT_NODE_ID(tab->tab_ind_free) = XT_NODE_ID(next_node);
|
||||
xt_unlock_mutex_ns(&tab->tab_ind_lock);
|
||||
*address = wrote_pos;
|
||||
TRACK_BLOCK_ALLOC(wrote_pos);
|
||||
@ -1415,30 +1422,45 @@ static xtBool idx_replace_node_key(XTOpenTablePtr ot, XTIndexPtr ind, IdxStackIt
|
||||
if (idx_is_item_deleted(iref.ir_branch, &item->i_pos))
|
||||
iref.ir_block->cp_del_count--;
|
||||
}
|
||||
memmove(&iref.ir_branch->tb_data[item->i_pos.i_item_offset + item_size],
|
||||
&iref.ir_branch->tb_data[item->i_pos.i_item_offset + item->i_pos.i_item_size],
|
||||
item->i_pos.i_total_size - item->i_pos.i_item_offset - item->i_pos.i_item_size);
|
||||
memcpy(&iref.ir_branch->tb_data[item->i_pos.i_item_offset],
|
||||
item_buf, item_size);
|
||||
if (ind->mi_lazy_delete) {
|
||||
if (idx_is_item_deleted(iref.ir_branch, &item->i_pos))
|
||||
iref.ir_block->cp_del_count++;
|
||||
}
|
||||
item->i_pos.i_total_size = item->i_pos.i_total_size + item_size - item->i_pos.i_item_size;
|
||||
XT_SET_DISK_2(iref.ir_branch->tb_size_2, XT_MAKE_NODE_SIZE(item->i_pos.i_total_size));
|
||||
IDX_TRACE("%d-> %x\n", (int) XT_NODE_ID(current), (int) XT_GET_DISK_2(iref.ir_branch->tb_size_2));
|
||||
iref.ir_updated = TRUE;
|
||||
|
||||
if (item->i_pos.i_total_size + item_size - item->i_pos.i_item_size <= XT_INDEX_PAGE_DATA_SIZE) {
|
||||
/* The new item is larger than the old, this can result
|
||||
* in overflow of the node!
|
||||
*/
|
||||
memmove(&iref.ir_branch->tb_data[item->i_pos.i_item_offset + item_size],
|
||||
&iref.ir_branch->tb_data[item->i_pos.i_item_offset + item->i_pos.i_item_size],
|
||||
item->i_pos.i_total_size - item->i_pos.i_item_offset - item->i_pos.i_item_size);
|
||||
memcpy(&iref.ir_branch->tb_data[item->i_pos.i_item_offset],
|
||||
item_buf, item_size);
|
||||
if (ind->mi_lazy_delete) {
|
||||
if (idx_is_item_deleted(iref.ir_branch, &item->i_pos))
|
||||
iref.ir_block->cp_del_count++;
|
||||
}
|
||||
item->i_pos.i_total_size = item->i_pos.i_total_size + item_size - item->i_pos.i_item_size;
|
||||
XT_SET_DISK_2(iref.ir_branch->tb_size_2, XT_MAKE_NODE_SIZE(item->i_pos.i_total_size));
|
||||
IDX_TRACE("%d-> %x\n", (int) XT_NODE_ID(current), (int) XT_GET_DISK_2(iref.ir_branch->tb_size_2));
|
||||
iref.ir_updated = TRUE;
|
||||
|
||||
#ifdef DEBUG
|
||||
if (ind->mi_lazy_delete)
|
||||
ASSERT_NS(item->i_pos.i_total_size <= XT_INDEX_PAGE_DATA_SIZE);
|
||||
#endif
|
||||
if (item->i_pos.i_total_size <= XT_INDEX_PAGE_DATA_SIZE)
|
||||
return xt_ind_release(ot, ind, XT_UNLOCK_W_UPDATE, &iref);
|
||||
}
|
||||
|
||||
/* The node has overflowed!! */
|
||||
result.sr_item = item->i_pos;
|
||||
|
||||
memcpy(ot->ot_ind_wbuf.tb_data, iref.ir_branch->tb_data, item->i_pos.i_item_offset); // First part of the buffer
|
||||
memcpy(&ot->ot_ind_wbuf.tb_data[item->i_pos.i_item_offset], item_buf, item_size); // The new item
|
||||
memcpy(&ot->ot_ind_wbuf.tb_data[item->i_pos.i_item_offset + item_size],
|
||||
&iref.ir_branch->tb_data[item->i_pos.i_item_offset + item->i_pos.i_item_size],
|
||||
item->i_pos.i_total_size - item->i_pos.i_item_offset - item->i_pos.i_item_size);
|
||||
item->i_pos.i_total_size += item_size - item->i_pos.i_item_size;
|
||||
item->i_pos.i_item_size = item_size;
|
||||
XT_SET_DISK_2(ot->ot_ind_wbuf.tb_size_2, XT_MAKE_LEAF_SIZE(item->i_pos.i_total_size));
|
||||
IDX_TRACE("%d-> %x\n", (int) XT_NODE_ID(current), (int) XT_GET_DISK_2(ot->ot_ind_wbuf.tb_size_2));
|
||||
ASSERT_NS(item->i_pos.i_total_size > XT_INDEX_PAGE_DATA_SIZE && item->i_pos.i_total_size <= XT_INDEX_PAGE_DATA_SIZE*2);
|
||||
|
||||
/* Adjust the stack (we want the parents of the delete node): */
|
||||
for (;;) {
|
||||
if (idx_pop(stack) == item)
|
||||
@ -1448,7 +1470,7 @@ static xtBool idx_replace_node_key(XTOpenTablePtr ot, XTIndexPtr ind, IdxStackIt
|
||||
/* We assume that value can be overwritten (which is the case) */
|
||||
key_value.sv_flags = XT_SEARCH_WHOLE_KEY;
|
||||
key_value.sv_key = key_buf;
|
||||
if (!idx_get_middle_branch_item(ot, ind, iref.ir_branch, &key_value, &result))
|
||||
if (!idx_get_middle_branch_item(ot, ind, &ot->ot_ind_wbuf, &key_value, &result))
|
||||
goto failed_1;
|
||||
|
||||
if (!idx_new_branch(ot, ind, &new_branch))
|
||||
@ -1456,7 +1478,6 @@ static xtBool idx_replace_node_key(XTOpenTablePtr ot, XTIndexPtr ind, IdxStackIt
|
||||
|
||||
/* Split the node: */
|
||||
new_size = result.sr_item.i_total_size - result.sr_item.i_item_offset - result.sr_item.i_item_size;
|
||||
// TODO: Are 2 buffers now required?
|
||||
new_branch_ptr = (XTIdxBranchDPtr) &ot->ot_ind_wbuf.tb_data[XT_INDEX_PAGE_DATA_SIZE];
|
||||
memmove(new_branch_ptr->tb_data, &iref.ir_branch->tb_data[result.sr_item.i_item_offset + result.sr_item.i_item_size], new_size);
|
||||
|
||||
@ -1466,10 +1487,10 @@ static xtBool idx_replace_node_key(XTOpenTablePtr ot, XTIndexPtr ind, IdxStackIt
|
||||
goto failed_2;
|
||||
|
||||
/* Change the size of the old branch: */
|
||||
XT_SET_DISK_2(iref.ir_branch->tb_size_2, XT_MAKE_NODE_SIZE(result.sr_item.i_item_offset));
|
||||
IDX_TRACE("%d-> %x\n", (int) XT_NODE_ID(current), (int) XT_GET_DISK_2(iref.ir_branch->tb_size_2));
|
||||
XT_SET_DISK_2(ot->ot_ind_wbuf.tb_size_2, XT_MAKE_NODE_SIZE(result.sr_item.i_item_offset));
|
||||
IDX_TRACE("%d-> %x\n", (int) XT_NODE_ID(current), (int) XT_GET_DISK_2(ot->ot_ind_wbuf.tb_size_2));
|
||||
memcpy(iref.ir_branch, &ot->ot_ind_wbuf, offsetof(XTIdxBranchDRec, tb_data) + result.sr_item.i_item_offset);
|
||||
iref.ir_updated = TRUE;
|
||||
|
||||
xt_ind_release(ot, ind, XT_UNLOCK_W_UPDATE, &iref);
|
||||
|
||||
/* Insert the new branch into the parent node, using the new middle key value: */
|
||||
@ -2071,6 +2092,11 @@ xtPublic xtBool xt_idx_insert(XTOpenTablePtr ot, XTIndexPtr ind, xtRowID row_id,
|
||||
if (!idx_new_branch(ot, ind, &new_branch))
|
||||
goto failed_1;
|
||||
|
||||
if (XT_NODE_ID(current) == XT_NODE_ID(new_branch)) {
|
||||
xt_register_taberr(XT_REG_CONTEXT, XT_ERR_INDEX_CORRUPTED, ot->ot_table->tab_name);
|
||||
goto failed_1;
|
||||
}
|
||||
|
||||
/* Copy and write the rest of the data to the new node: */
|
||||
new_size = result.sr_item.i_total_size - result.sr_item.i_item_offset - result.sr_item.i_item_size;
|
||||
new_branch_ptr = (XTIdxBranchDPtr) &ot->ot_ind_wbuf.tb_data[XT_INDEX_PAGE_DATA_SIZE];
|
||||
@ -2723,6 +2749,10 @@ xtPublic xtBool xt_idx_search(XTOpenTablePtr ot, XTIndexPtr ind, register XTIdxS
|
||||
#endif
|
||||
ASSERT_NS(iref.ir_xlock == 2);
|
||||
ASSERT_NS(iref.ir_updated == 2);
|
||||
if (ind->mi_key_corrupted) {
|
||||
xt_register_taberr(XT_REG_CONTEXT, XT_ERR_INDEX_CORRUPTED, ot->ot_table->tab_name);
|
||||
return FAILED;
|
||||
}
|
||||
return OK;
|
||||
|
||||
failed:
|
||||
@ -2874,6 +2904,10 @@ xtPublic xtBool xt_idx_search_prev(XTOpenTablePtr ot, XTIndexPtr ind, register X
|
||||
//idx_check_index(ot, ind, TRUE);
|
||||
//idx_check_on_key(ot);
|
||||
#endif
|
||||
if (ind->mi_key_corrupted) {
|
||||
xt_register_taberr(XT_REG_CONTEXT, XT_ERR_INDEX_CORRUPTED, ot->ot_table->tab_name);
|
||||
return FAILED;
|
||||
}
|
||||
return OK;
|
||||
|
||||
failed:
|
||||
@ -2964,6 +2998,10 @@ xtPublic xtBool xt_idx_next(register XTOpenTablePtr ot, register XTIndexPtr ind,
|
||||
|
||||
if (!(XT_NODE_ID(current) = XT_NODE_ID(ind->mi_root))) {
|
||||
XT_INDEX_UNLOCK(ind, ot);
|
||||
if (ind->mi_key_corrupted) {
|
||||
xt_register_taberr(XT_REG_CONTEXT, XT_ERR_INDEX_CORRUPTED, ot->ot_table->tab_name);
|
||||
return FAILED;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
@ -3071,6 +3109,10 @@ xtPublic xtBool xt_idx_next(register XTOpenTablePtr ot, register XTIndexPtr ind,
|
||||
ot->ot_curr_rec_id = 0;
|
||||
ot->ot_curr_row_id = 0;
|
||||
XT_INDEX_UNLOCK(ind, ot);
|
||||
if (ind->mi_key_corrupted) {
|
||||
xt_register_taberr(XT_REG_CONTEXT, XT_ERR_INDEX_CORRUPTED, ot->ot_table->tab_name);
|
||||
return FAILED;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
@ -3112,6 +3154,10 @@ xtPublic xtBool xt_idx_next(register XTOpenTablePtr ot, register XTIndexPtr ind,
|
||||
ot->ot_curr_row_id = result.sr_row_id;
|
||||
ot->ot_ind_state = result.sr_item;
|
||||
|
||||
if (ind->mi_key_corrupted) {
|
||||
xt_register_taberr(XT_REG_CONTEXT, XT_ERR_INDEX_CORRUPTED, ot->ot_table->tab_name);
|
||||
return FAILED;
|
||||
}
|
||||
return OK;
|
||||
|
||||
failed:
|
||||
@ -3178,6 +3224,10 @@ xtPublic xtBool xt_idx_prev(register XTOpenTablePtr ot, register XTIndexPtr ind,
|
||||
|
||||
if (!(XT_NODE_ID(current) = XT_NODE_ID(ind->mi_root))) {
|
||||
XT_INDEX_UNLOCK(ind, ot);
|
||||
if (ind->mi_key_corrupted) {
|
||||
xt_register_taberr(XT_REG_CONTEXT, XT_ERR_INDEX_CORRUPTED, ot->ot_table->tab_name);
|
||||
return FAILED;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
@ -3274,6 +3324,10 @@ xtPublic xtBool xt_idx_prev(register XTOpenTablePtr ot, register XTIndexPtr ind,
|
||||
ot->ot_curr_row_id = 0;
|
||||
|
||||
XT_INDEX_UNLOCK(ind, ot);
|
||||
if (ind->mi_key_corrupted) {
|
||||
xt_register_taberr(XT_REG_CONTEXT, XT_ERR_INDEX_CORRUPTED, ot->ot_table->tab_name);
|
||||
return FAILED;
|
||||
}
|
||||
return OK;
|
||||
|
||||
unlock_check_on_key:
|
||||
@ -3302,6 +3356,10 @@ xtPublic xtBool xt_idx_prev(register XTOpenTablePtr ot, register XTIndexPtr ind,
|
||||
ot->ot_curr_rec_id = result.sr_rec_id;
|
||||
ot->ot_curr_row_id = result.sr_row_id;
|
||||
ot->ot_ind_state = result.sr_item;
|
||||
if (ind->mi_key_corrupted) {
|
||||
xt_register_taberr(XT_REG_CONTEXT, XT_ERR_INDEX_CORRUPTED, ot->ot_table->tab_name);
|
||||
return FAILED;
|
||||
}
|
||||
return OK;
|
||||
|
||||
failed:
|
||||
@ -3648,7 +3706,7 @@ xtPublic void xt_check_indices(XTOpenTablePtr ot)
|
||||
track_block_exists(current);
|
||||
#endif
|
||||
printf("%d ", (int) XT_NODE_ID(current));
|
||||
if (!xt_ind_read_bytes(ot, *ind, current, sizeof(XTIndFreeBlockRec), (xtWord1 *) &free_block)) {
|
||||
if (!xt_ind_read_bytes(ot, NULL, current, sizeof(XTIndFreeBlockRec), (xtWord1 *) &free_block)) {
|
||||
xt_log_and_clear_exception_ns();
|
||||
break;
|
||||
}
|
||||
@ -4141,11 +4199,18 @@ void XTIndexLogPool::ilp_init(struct XTThread *self, struct XTDatabase *db, size
|
||||
if (!ilp_open_log(&il, log_id, FALSE, self))
|
||||
goto failed;
|
||||
if (il->il_tab_id && il->il_log_eof) {
|
||||
char table_name[XT_IDENTIFIER_NAME_SIZE*3+3];
|
||||
|
||||
if (!il->il_open_table(&ot))
|
||||
goto failed;
|
||||
if (ot) {
|
||||
if (!il->il_apply_log(ot))
|
||||
goto failed;
|
||||
xt_tab_make_table_name(ot->ot_table, table_name, sizeof(table_name));
|
||||
xt_logf(XT_NT_INFO, "PBXT: Recovering index, table: %s, bytes to read: %llu\n", table_name, (u_llong) il->il_log_eof);
|
||||
if (!il->il_apply_log(ot)) {
|
||||
/* If recovery of an index fails, then it is corrupt! */
|
||||
xt_tab_disable_index(ot->ot_table, XT_INDEX_CORRUPTED);
|
||||
xt_log_and_clear_exception_ns();
|
||||
}
|
||||
ot->ot_thread = self;
|
||||
il->il_close_table(ot);
|
||||
}
|
||||
@ -4468,8 +4533,7 @@ xtBool XTIndexLog::il_apply_log(struct XTOpenTable *ot)
|
||||
/* Corrupt log?! */
|
||||
if (il_buffer_len < req_size) {
|
||||
xt_register_ixterr(XT_REG_CONTEXT, XT_ERR_INDEX_LOG_CORRUPT, xt_file_path(il_of));
|
||||
xt_log_and_clear_exception_ns();
|
||||
return OK;
|
||||
return FAILED;
|
||||
}
|
||||
if (!xt_pread_file(il_of, offset, il_buffer_len, il_buffer_len, il_buffer, NULL, &ot->ot_thread->st_statistics.st_ilog, ot->ot_thread))
|
||||
return FAILED;
|
||||
@ -4548,8 +4612,7 @@ xtBool XTIndexLog::il_apply_log(struct XTOpenTable *ot)
|
||||
/* Corrupt log?! */
|
||||
if (il_buffer_len < req_size) {
|
||||
xt_register_ixterr(XT_REG_CONTEXT, XT_ERR_INDEX_LOG_CORRUPT, xt_file_path(il_of));
|
||||
xt_log_and_clear_exception_ns();
|
||||
return OK;
|
||||
return FAILED;
|
||||
}
|
||||
if (!xt_pread_file(il_of, offset, il_buffer_len, il_buffer_len, il_buffer, NULL, &ot->ot_thread->st_statistics.st_ilog, ot->ot_thread))
|
||||
return FAILED;
|
||||
@ -4597,8 +4660,7 @@ xtBool XTIndexLog::il_apply_log(struct XTOpenTable *ot)
|
||||
break;
|
||||
default:
|
||||
xt_register_ixterr(XT_REG_CONTEXT, XT_ERR_INDEX_LOG_CORRUPT, xt_file_path(il_of));
|
||||
xt_log_and_clear_exception_ns();
|
||||
return OK;
|
||||
return FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -312,7 +312,7 @@ typedef struct XTIndex {
|
||||
u_int mi_flags;
|
||||
u_int mi_key_size;
|
||||
u_int mi_max_items; /* The maximum number of items that can fit in a leaf node. */
|
||||
xtBool mi_low_byte_first;
|
||||
xtBool mi_key_corrupted; /* Set to TRUE if a currupted index key is detected. */
|
||||
xtBool mi_fix_key;
|
||||
xtBool mi_lazy_delete; /* TRUE if index entries are "lazy deleted". */
|
||||
u_int mi_single_type; /* Used when the index contains a single field. */
|
||||
|
@ -1088,7 +1088,10 @@ xtPublic u_int myxt_get_key_length(XTIndexPtr ind, xtWord1 *key_buf)
|
||||
}
|
||||
|
||||
end:
|
||||
return (xtWord1 *) key_data - key_buf;
|
||||
u_int ilen = (xtWord1 *) key_data - key_buf;
|
||||
if (ilen > XT_INDEX_MAX_KEY_SIZE)
|
||||
ind->mi_key_corrupted = TRUE;
|
||||
return ilen;
|
||||
}
|
||||
|
||||
/* Derived from ha_key_cmp */
|
||||
@ -2183,7 +2186,8 @@ static XTIndexPtr my_create_index(XTThreadPtr self, TABLE *table_arg, u_int idx,
|
||||
xt_spinlock_init_with_autoname(self, &ind->mi_dirty_lock);
|
||||
ind->mi_index_no = idx;
|
||||
ind->mi_flags = (index->flags & (HA_NOSAME | HA_NULL_ARE_EQUAL | HA_UNIQUE_CHECK));
|
||||
ind->mi_low_byte_first = TS(table_arg)->db_low_byte_first;
|
||||
//ind->mi_low_byte_first = TS(table_arg)->db_low_byte_first;
|
||||
ind->mi_key_corrupted = FALSE;
|
||||
ind->mi_fix_key = TRUE;
|
||||
ind->mi_select_total = 0;
|
||||
ind->mi_subset_of = 0;
|
||||
|
@ -69,17 +69,17 @@ void myxt_free_dictionary(XTThreadPtr self, XTDictionary *dic);
|
||||
void myxt_move_dictionary(XTDictionaryPtr dic, XTDictionaryPtr source_dic);
|
||||
XTDDTable *myxt_create_table_from_table(XTThreadPtr self, STRUCT_TABLE *my_tab);
|
||||
|
||||
void myxt_static_convert_identifier(XTThreadPtr self, const struct charset_info_st *cs, char *from, char *to, size_t to_len);
|
||||
char *myxt_convert_identifier(XTThreadPtr self, const struct charset_info_st *cs, char *from);
|
||||
void myxt_static_convert_identifier(XTThreadPtr self, MX_CONST_CHARSET_INFO *cs, char *from, char *to, size_t to_len);
|
||||
char *myxt_convert_identifier(XTThreadPtr self, MX_CONST_CHARSET_INFO *cs, char *from);
|
||||
void myxt_static_convert_table_name(XTThreadPtr self, char *from, char *to, size_t to_len);
|
||||
void myxt_static_convert_file_name(char *from, char *to, size_t to_len);
|
||||
char *myxt_convert_table_name(XTThreadPtr self, char *from);
|
||||
int myxt_strcasecmp(char * a, char *b);
|
||||
int myxt_isspace(const struct charset_info_st *cs, char a);
|
||||
int myxt_ispunct(const struct charset_info_st *cs, char a);
|
||||
int myxt_isdigit(const struct charset_info_st *cs, char a);
|
||||
int myxt_isspace(MX_CONST_CHARSET_INFO *cs, char a);
|
||||
int myxt_ispunct(MX_CONST_CHARSET_INFO *cs, char a);
|
||||
int myxt_isdigit(MX_CONST_CHARSET_INFO *cs, char a);
|
||||
|
||||
const struct charset_info_st *myxt_getcharset(bool convert);
|
||||
MX_CONST_CHARSET_INFO *myxt_getcharset(bool convert);
|
||||
|
||||
void *myxt_create_thread();
|
||||
void myxt_destroy_thread(void *thread, xtBool end_threads);
|
||||
|
@ -1359,6 +1359,57 @@ static xtBool xres_sync_operations(XTThreadPtr self, XTDatabaseHPtr db, XTWriter
|
||||
return op_synced;
|
||||
}
|
||||
|
||||
#ifdef XT_CORRECT_TABLE_FREE_COUNT
|
||||
#define CORRECT_COUNT TRUE
|
||||
#else
|
||||
#define CORRECT_COUNT FALSE
|
||||
#endif
|
||||
#ifdef XT_CHECK_RECORD_FREE_COUNT
|
||||
#define CHECK_RECS TRUE
|
||||
#else
|
||||
#define CHECK_RECS FALSE
|
||||
#endif
|
||||
#if defined(XT_CHECK_RECORD_FREE_COUNT) || defined(XT_CHECK_ROW_FREE_COUNT)
|
||||
#define RECOVER_FREE_COUNTS
|
||||
#endif
|
||||
|
||||
#ifdef RECOVER_FREE_COUNTS
|
||||
/* {CORRECTED-ROW-COUNT}
|
||||
* This error can be repeated by crashing the server during
|
||||
* high activitity, after flush table writes the table header
|
||||
*
|
||||
* On recovery, the free count "from the future" is used as
|
||||
* the starting point for subsequent allocation and frees.
|
||||
* The count is wrong after that point.
|
||||
*
|
||||
* The recovery of the count only works correctly if a
|
||||
* checkpoint is complete successfully after that table
|
||||
* header is flushed. Basically the writing of the table
|
||||
* header should be synchronsized with the writing of the
|
||||
* end of the checkpoint.
|
||||
*
|
||||
* Another solution would be to log the count, along with
|
||||
* the allocate and free commannds.
|
||||
*
|
||||
* The 3rd solution is the one used here. The count is corrected
|
||||
* after recovery.
|
||||
*/
|
||||
static void xres_recover_table_free_counts(XTThreadPtr self, XTDatabaseHPtr db, XTWriterStatePtr ws)
|
||||
{
|
||||
u_int edx;
|
||||
XTTableEntryPtr te_ptr;
|
||||
XTTableHPtr tab;
|
||||
|
||||
xt_enum_tables_init(&edx);
|
||||
while ((te_ptr = xt_enum_tables_next(self, db, &edx))) {
|
||||
if ((tab = te_ptr->te_table)) {
|
||||
if (xres_open_table(self, ws, te_ptr->te_tab_id))
|
||||
xt_tab_check_free_lists(self, ws->ws_ot, CHECK_RECS, CORRECT_COUNT);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Operations from the log are applied in sequence order.
|
||||
* If the operations are out of sequence, they are buffered
|
||||
@ -2175,6 +2226,13 @@ xtBool XTXactRestart::xres_restart(XTThreadPtr self, xtLogID *log_id, xtLogOffse
|
||||
/* This is true because if no transaction was placed in RAM then
|
||||
* the next transaction in RAM will have the next ID: */
|
||||
db->db_xn_min_ram_id = db->db_xn_curr_id + 1;
|
||||
|
||||
#ifdef RECOVER_FREE_COUNTS
|
||||
if (xres_cp_log_id != *log_id || xres_cp_log_offset != *log_offset) {
|
||||
/* Recovery took place, correct the row count! */
|
||||
xres_recover_table_free_counts(self, db, &ws);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
failed:
|
||||
|
@ -380,7 +380,7 @@ xtPublic void xt_int8_to_byte_size(xtInt8 value, char *string)
|
||||
/* Version number must also be set in configure.in! */
|
||||
xtPublic c_char *xt_get_version(void)
|
||||
{
|
||||
return "1.0.11 Pre-GA";
|
||||
return "1.0.11-7 Pre-GA";
|
||||
}
|
||||
|
||||
/* Copy and URL decode! */
|
||||
|
@ -78,6 +78,65 @@
|
||||
|
||||
#define XT_MAX_TABLE_FILE_NAME_SIZE (XT_TABLE_NAME_SIZE+6+40)
|
||||
|
||||
/*
|
||||
* -----------------------------------------------------------------------
|
||||
* Handle Error Detected in a Table
|
||||
*/
|
||||
|
||||
struct XTTableError {
|
||||
xtTableID ter_tab_id;
|
||||
xtRecordID ter_rec_id;
|
||||
};
|
||||
|
||||
static int tab_comp_tab_error(XTThreadPtr XT_UNUSED(self), register const void *XT_UNUSED(thunk), register const void *a, register const void *b)
|
||||
{
|
||||
XTTableError *ter_a = ((XTTableError *) a);
|
||||
XTTableError *ter_b = (XTTableError *) b;
|
||||
|
||||
if (ter_a->ter_tab_id < ter_b->ter_tab_id)
|
||||
return -1;
|
||||
if (ter_a->ter_tab_id == ter_b->ter_tab_id) {
|
||||
if (ter_a->ter_rec_id < ter_b->ter_rec_id)
|
||||
return -1;
|
||||
if (ter_a->ter_rec_id == ter_b->ter_rec_id)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static xtBool tab_record_corrupt(XTOpenTablePtr ot, xtRowID row_id, xtRecordID rec_id, bool not_valid, int where)
|
||||
{
|
||||
XTTableHPtr tab = ot->ot_table;
|
||||
XTDatabaseHPtr db = tab->tab_db;
|
||||
XTTableError ter;
|
||||
XTTableError *ter_ptr;
|
||||
|
||||
ter.ter_tab_id = tab->tab_id;
|
||||
ter.ter_rec_id = rec_id;
|
||||
|
||||
xt_sl_lock_ns(db->db_error_list, ot->ot_thread);
|
||||
if (!(ter_ptr = (XTTableError *) xt_sl_find(NULL, db->db_error_list, &ter))) {
|
||||
xtBool ok;
|
||||
char table_name[XT_IDENTIFIER_NAME_SIZE*3+3];
|
||||
|
||||
ok = xt_sl_insert(NULL, db->db_error_list, &ter, &ter);
|
||||
xt_sl_unlock_ns(db->db_error_list);
|
||||
if (!ok)
|
||||
return FAILED;
|
||||
xt_tab_set_table_repair_pending(tab);
|
||||
xt_tab_make_table_name(tab, table_name, sizeof(table_name));
|
||||
xt_logf(XT_NT_ERROR, "#%d Table %s: row %llu, record %llu, is %s, REPAIR TABLE required.\n", where,
|
||||
table_name,
|
||||
(u_llong) row_id,
|
||||
(u_llong) rec_id,
|
||||
not_valid ? "not valid" : "free");
|
||||
}
|
||||
else
|
||||
xt_sl_unlock_ns(db->db_error_list);
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* -----------------------------------------------------------------------
|
||||
* Compare paths:
|
||||
@ -425,6 +484,7 @@ xtPublic void xt_tab_init_db(XTThreadPtr self, XTDatabaseHPtr db)
|
||||
db->db_tables = xt_new_hashtable(self, tab_list_comp, tab_list_hash, tab_list_free, TRUE, TRUE);
|
||||
db->db_table_by_id = xt_new_sortedlist(self, sizeof(XTTableEntryRec), 20, 20, tab_comp_by_id, db, tab_free_by_id, FALSE, FALSE);
|
||||
db->db_table_paths = xt_new_sortedlist(self, sizeof(XTTablePathPtr), 20, 20, tab_comp_path, db, tab_free_path, FALSE, FALSE);
|
||||
db->db_error_list = xt_new_sortedlist(self, sizeof(XTTableError), 20, 20, tab_comp_tab_error, db, NULL, TRUE, FALSE);
|
||||
|
||||
if (db->db_multi_path) {
|
||||
XTOpenFilePtr of;
|
||||
@ -649,6 +709,10 @@ xtPublic void xt_tab_exit_db(XTThreadPtr self, XTDatabaseHPtr db)
|
||||
xt_free_sortedlist(self, db->db_table_paths);
|
||||
db->db_table_paths = NULL;
|
||||
}
|
||||
if (db->db_error_list) {
|
||||
xt_free_sortedlist(self, db->db_error_list);
|
||||
db->db_error_list = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void tab_check_table(XTThreadPtr self, XTTableHPtr XT_UNUSED(tab))
|
||||
@ -1713,6 +1777,116 @@ xtPublic void xt_drop_table(XTThreadPtr self, XTPathStrPtr tab_name, xtBool drop
|
||||
exit_();
|
||||
}
|
||||
|
||||
xtPublic void xt_tab_check_free_lists(XTThreadPtr self, XTOpenTablePtr ot, bool check_recs, bool correct_count)
|
||||
{
|
||||
char table_name[XT_IDENTIFIER_NAME_SIZE*3+3];
|
||||
register XTTableHPtr tab = ot->ot_table;
|
||||
xtRowID prev_row_id;
|
||||
xtRowID row_id;
|
||||
xtRefID next_row_id;
|
||||
u_llong free_count;
|
||||
|
||||
xt_tab_make_table_name(tab, table_name, sizeof(table_name));
|
||||
if (check_recs) {
|
||||
xtRecordID prev_rec_id;
|
||||
xtRecordID rec_id;
|
||||
XTTabRecExtDRec rec_buf;
|
||||
|
||||
xt_lock_mutex_ns(&tab->tab_rec_lock);
|
||||
/* Checking the free list: */
|
||||
prev_rec_id = 0;
|
||||
free_count = 0;
|
||||
rec_id = tab->tab_rec_free_id;
|
||||
while (rec_id) {
|
||||
if (rec_id >= tab->tab_rec_eof_id) {
|
||||
xt_logf(XT_NT_ERROR, "Table %s: invalid reference on free list: %llu, ", table_name, (u_llong) rec_id);
|
||||
if (prev_rec_id)
|
||||
xt_logf(XT_NT_ERROR, "reference by: %llu\n", (u_llong) prev_rec_id);
|
||||
else
|
||||
xt_logf(XT_NT_ERROR, "reference by list head pointer\n");
|
||||
xt_tab_set_table_repair_pending(tab);
|
||||
break;
|
||||
}
|
||||
if (!xt_tab_get_rec_data(ot, rec_id, XT_REC_FIX_HEADER_SIZE, (xtWord1 *) &rec_buf)) {
|
||||
if (self)
|
||||
xt_throw(self);
|
||||
else
|
||||
xt_log_and_clear_warning(ot->ot_thread);
|
||||
break;
|
||||
}
|
||||
if ((rec_buf.tr_rec_type_1 & XT_TAB_STATUS_MASK) != XT_TAB_STATUS_FREED)
|
||||
xt_logf(XT_NT_INFO, "Table %s: record, %llu, on free list is not free\n", table_name, (u_llong) rec_id);
|
||||
free_count++;
|
||||
prev_rec_id = rec_id;
|
||||
rec_id = XT_GET_DISK_4(rec_buf.tr_prev_rec_id_4);
|
||||
}
|
||||
if (free_count != tab->tab_rec_fnum) {
|
||||
if (correct_count) {
|
||||
tab->tab_rec_fnum = free_count;
|
||||
tab->tab_head_rec_fnum = free_count;
|
||||
tab->tab_flush_pending = TRUE;
|
||||
xt_logf(XT_NT_INFO, "Table %s: free record count (%llu) has been set to the number of records on the list: %llu\n", table_name, (u_llong) tab->tab_rec_fnum, (u_llong) free_count);
|
||||
}
|
||||
else
|
||||
xt_logf(XT_NT_INFO, "Table %s: free record count (%llu) differs from the number of records on the list: %llu\n", table_name, (u_llong) tab->tab_rec_fnum, (u_llong) free_count);
|
||||
}
|
||||
xt_unlock_mutex_ns(&tab->tab_rec_lock);
|
||||
}
|
||||
|
||||
/* Check the row free list: */
|
||||
xt_lock_mutex_ns(&tab->tab_row_lock);
|
||||
|
||||
prev_row_id = 0;
|
||||
free_count = 0;
|
||||
row_id = tab->tab_row_free_id;
|
||||
while (row_id) {
|
||||
if (row_id >= tab->tab_row_eof_id) {
|
||||
xt_logf(XT_NT_ERROR, "Table %s: invalid reference on free row: %llu, ", table_name, (u_llong) row_id);
|
||||
if (prev_row_id)
|
||||
xt_logf(XT_NT_ERROR, "reference by: %llu\n", (u_llong) prev_row_id);
|
||||
else
|
||||
xt_logf(XT_NT_ERROR, "reference by list head pointer\n");
|
||||
xt_tab_set_table_repair_pending(tab);
|
||||
break;
|
||||
}
|
||||
if (!tab->tab_rows.xt_tc_read_4(ot->ot_row_file, row_id, &next_row_id, ot->ot_thread)) {
|
||||
if (self)
|
||||
xt_throw(self);
|
||||
else
|
||||
xt_log_and_clear_warning(ot->ot_thread);
|
||||
break;
|
||||
}
|
||||
free_count++;
|
||||
prev_row_id = row_id;
|
||||
row_id = next_row_id;
|
||||
}
|
||||
if (free_count != tab->tab_row_fnum) {
|
||||
if (correct_count) {
|
||||
/* tab_row_fnum is the current value, and tab_head_row_fnum is the value on
|
||||
* disk. tab_head_row_fnum is set by the writer as the changes are applied
|
||||
* to the database.
|
||||
*
|
||||
* This is the value then stored in the header of the file. This value
|
||||
* is in sync with other changes to the file.
|
||||
*
|
||||
* So the fact that I am setting both value means this will not work at
|
||||
* runtime, unless all changes have been applied by the writer.
|
||||
*
|
||||
* The correct way to do this at run time would be to add the change to the
|
||||
* transaction log, so that it is applied by the writer.
|
||||
*/
|
||||
tab->tab_row_fnum = free_count;
|
||||
tab->tab_head_row_fnum = free_count;
|
||||
tab->tab_flush_pending = TRUE;
|
||||
xt_logf(XT_NT_INFO, "Table %s: free row count (%llu) has been set to the number of rows on the list: %llu\n", table_name, (u_llong) tab->tab_row_fnum, (u_llong) free_count);
|
||||
}
|
||||
else
|
||||
xt_logf(XT_NT_INFO, "Table %s: free row count (%llu) differs from the number of rows on the list: %llu\n", table_name, (u_llong) tab->tab_row_fnum, (u_llong) free_count);
|
||||
}
|
||||
|
||||
xt_unlock_mutex_ns(&tab->tab_row_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Record buffer size:
|
||||
* -------------------
|
||||
@ -2010,7 +2184,7 @@ xtPublic void xt_check_table(XTThreadPtr self, XTOpenTablePtr ot)
|
||||
prec_id = rec_id;
|
||||
rec_id = XT_GET_DISK_4(rec_buf->tr_prev_rec_id_4);
|
||||
}
|
||||
if (free_count2 < free_rec_count)
|
||||
if (free_count2 != free_rec_count)
|
||||
xt_logf(XT_INFO, "Table %s: not all free blocks (%llu) on free list: %llu\n", tab->tab_name, (u_llong) free_rec_count, (u_llong) free_count2);
|
||||
|
||||
freer_(); // xt_unlock_mutex_ns(&tab->tab_rec_lock);
|
||||
@ -2042,6 +2216,29 @@ xtPublic void xt_check_table(XTThreadPtr self, XTOpenTablePtr ot)
|
||||
rec_id++;
|
||||
}
|
||||
|
||||
prec_id = 0;
|
||||
free_count2 = 0;
|
||||
row_id = tab->tab_row_free_id;
|
||||
while (row_id) {
|
||||
if (row_id >= tab->tab_row_eof_id) {
|
||||
xt_logf(XT_INFO, "Table %s: invalid reference on free row: %llu, ", tab->tab_name, (u_llong) row_id);
|
||||
if (prec_id)
|
||||
xt_logf(XT_INFO, "reference by: %llu\n", (u_llong) prec_id);
|
||||
else
|
||||
xt_logf(XT_INFO, "reference by list head pointer\n");
|
||||
break;
|
||||
}
|
||||
if (!tab->tab_rows.xt_tc_read_4(ot->ot_row_file, row_id, &ref_id, self)) {
|
||||
xt_log_and_clear_exception(self);
|
||||
break;
|
||||
}
|
||||
free_count2++;
|
||||
prec_id = row_id;
|
||||
row_id = ref_id;
|
||||
}
|
||||
if (free_count2 != tab->tab_row_fnum)
|
||||
xt_logf(XT_INFO, "Table %s: free row count (%llu) differs from the number of row on the list: %llu\n", tab->tab_name, (u_llong) tab->tab_row_fnum, (u_llong) free_count2);
|
||||
|
||||
freer_(); // xt_unlock_mutex(&tab->tab_row_lock);
|
||||
|
||||
#ifdef CHECK_INDEX_ON_CHECK_TABLE
|
||||
@ -3117,10 +3314,18 @@ static int tab_visible(register XTOpenTablePtr ot, XTTabRecHeadDPtr rec_head, xt
|
||||
#endif
|
||||
break;
|
||||
case XT_XN_REREAD:
|
||||
/* {RETRY-READ}
|
||||
* TODO: This is not as "correct" as it could be.
|
||||
* Such records should be considered to be aborted,
|
||||
* and removed from the list.
|
||||
*/
|
||||
if (invalid_rec != var_rec_id) {
|
||||
invalid_rec = var_rec_id;
|
||||
goto retry_3;
|
||||
}
|
||||
if (!tab_record_corrupt(ot, row_id, var_rec_id, true, 1))
|
||||
goto failed;
|
||||
|
||||
/* Assume end of list. */
|
||||
#ifdef XT_CRASH_DEBUG
|
||||
/* Should not happen! */
|
||||
@ -3308,6 +3513,8 @@ xtPublic int xt_tab_visible(XTOpenTablePtr ot)
|
||||
/* Avoid infinite loop: */
|
||||
if (read_again) {
|
||||
/* Should not happen! */
|
||||
if (!tab_record_corrupt(ot, row_id, ot->ot_curr_rec_id, true, 2))
|
||||
return XT_ERR;
|
||||
#ifdef XT_CRASH_DEBUG
|
||||
/* Generate a core dump! */
|
||||
xt_crash_me();
|
||||
@ -3364,6 +3571,8 @@ xtPublic int xt_tab_read_record(register XTOpenTablePtr ot, xtWord1 *buffer)
|
||||
/* Avoid infinite loop: */
|
||||
if (read_again) {
|
||||
/* Should not happen! */
|
||||
if (!tab_record_corrupt(ot, XT_GET_DISK_4(((XTTabRecHeadDPtr) ot->ot_row_rbuffer)->tr_row_id_4), ot->ot_curr_rec_id, true, 3))
|
||||
return XT_ERR;
|
||||
#ifdef XT_CRASH_DEBUG
|
||||
/* Generate a core dump! */
|
||||
xt_crash_me();
|
||||
@ -3580,6 +3789,7 @@ xtPublic xtBool xt_tab_free_row(XTOpenTablePtr ot, XTTableHPtr tab, xtRowID row_
|
||||
}
|
||||
tab->tab_row_free_id = row_id;
|
||||
tab->tab_row_fnum++;
|
||||
ASSERT_NS(tab->tab_row_fnum < tab->tab_row_eof_id);
|
||||
xt_unlock_mutex_ns(&tab->tab_row_lock);
|
||||
|
||||
if (!xt_xlog_modify_table(tab->tab_id, XT_LOG_ENT_ROW_FREED, op_seq, 0, row_id, sizeof(XTTabRowRefDRec), (xtWord1 *) &free_row, ot->ot_thread))
|
||||
@ -3776,7 +3986,7 @@ xtPublic int xt_tab_remove_record(XTOpenTablePtr ot, xtRecordID rec_id, xtWord1
|
||||
xt_lock_mutex_ns(&tab->tab_db->db_co_ext_lock);
|
||||
if (!xt_tab_get_rec_data(ot, rec_id, XT_REC_EXT_HEADER_SIZE, ot->ot_row_rbuffer)) {
|
||||
xt_unlock_mutex_ns(&tab->tab_db->db_co_ext_lock);
|
||||
return FAILED;
|
||||
return XT_ERR;
|
||||
}
|
||||
xt_unlock_mutex_ns(&tab->tab_db->db_co_ext_lock);
|
||||
|
||||
@ -3824,7 +4034,7 @@ xtPublic int xt_tab_remove_record(XTOpenTablePtr ot, xtRecordID rec_id, xtWord1
|
||||
XT_SET_DISK_4(free_rec->rf_next_rec_id_4, prev_rec_id);
|
||||
if (!xt_tab_put_rec_data(ot, rec_id, sizeof(XTTabRecFreeDRec), ot->ot_row_rbuffer, &op_seq)) {
|
||||
xt_unlock_mutex_ns(&tab->tab_rec_lock);
|
||||
return FAILED;
|
||||
return XT_ERR;
|
||||
}
|
||||
tab->tab_rec_free_id = rec_id;
|
||||
ASSERT_NS(tab->tab_rec_free_id < tab->tab_rec_eof_id);
|
||||
@ -3832,7 +4042,9 @@ xtPublic int xt_tab_remove_record(XTOpenTablePtr ot, xtRecordID rec_id, xtWord1
|
||||
xt_unlock_mutex_ns(&tab->tab_rec_lock);
|
||||
|
||||
free_rec->rf_rec_type_1 = old_rec_type;
|
||||
return xt_xlog_modify_table(tab->tab_id, XT_LOG_ENT_REC_REMOVED_BI, op_seq, (xtRecordID) new_rec_type, rec_id, rec_size, ot->ot_row_rbuffer, ot->ot_thread);
|
||||
if (!xt_xlog_modify_table(tab->tab_id, XT_LOG_ENT_REC_REMOVED_BI, op_seq, (xtRecordID) new_rec_type, rec_id, rec_size, ot->ot_row_rbuffer, ot->ot_thread))
|
||||
return XT_ERR;
|
||||
return OK;
|
||||
}
|
||||
|
||||
static xtRowID tab_new_row(XTOpenTablePtr ot, XTTableHPtr tab)
|
||||
@ -3851,6 +4063,7 @@ static xtRowID tab_new_row(XTOpenTablePtr ot, XTTableHPtr tab)
|
||||
return 0;
|
||||
}
|
||||
tab->tab_row_free_id = next_row_id;
|
||||
ASSERT_NS(tab->tab_row_fnum > 0);
|
||||
tab->tab_row_fnum--;
|
||||
}
|
||||
else {
|
||||
@ -4170,9 +4383,12 @@ static xtBool tab_wait_for_rollback(XTOpenTablePtr ot, xtRowID row_id, xtRecordI
|
||||
return FAILED;
|
||||
if (XT_REC_IS_CLEAN(var_head.tr_rec_type_1))
|
||||
goto locked;
|
||||
if (XT_REC_IS_FREE(var_head.tr_rec_type_1))
|
||||
if (XT_REC_IS_FREE(var_head.tr_rec_type_1)) {
|
||||
/* Should not happen: */
|
||||
if (!tab_record_corrupt(ot, row_id, var_rec_id, false, 4))
|
||||
return FAILED;
|
||||
goto record_invalid;
|
||||
}
|
||||
xn_id = XT_GET_DISK_4(var_head.tr_xact_id_4);
|
||||
switch (xt_xn_status(ot, xn_id, var_rec_id)) {
|
||||
case XT_XN_VISIBLE:
|
||||
@ -4195,6 +4411,8 @@ static xtBool tab_wait_for_rollback(XTOpenTablePtr ot, xtRowID row_id, xtRecordI
|
||||
XT_TAB_ROW_WRITE_LOCK(&tab->tab_row_rwlock[row_id % XT_ROW_RWLOCKS], ot->ot_thread);
|
||||
goto retry;
|
||||
case XT_XN_REREAD:
|
||||
if (!tab_record_corrupt(ot, row_id, var_rec_id, true, 5))
|
||||
return FAILED;
|
||||
goto record_invalid;
|
||||
}
|
||||
var_rec_id = XT_GET_DISK_4(var_head.tr_prev_rec_id_4);
|
||||
@ -4206,9 +4424,10 @@ static xtBool tab_wait_for_rollback(XTOpenTablePtr ot, xtRowID row_id, xtRecordI
|
||||
return FAILED;
|
||||
|
||||
record_invalid:
|
||||
/* {RETRY-READ} */
|
||||
/* Prevent an infinite loop due to a bad record: */
|
||||
if (invalid_rec != var_rec_id) {
|
||||
var_rec_id = invalid_rec;
|
||||
invalid_rec = var_rec_id;
|
||||
goto retry;
|
||||
}
|
||||
/* The record is invalid, it will be "overwritten"... */
|
||||
@ -4280,9 +4499,12 @@ xtPublic int xt_tab_maybe_committed(XTOpenTablePtr ot, xtRecordID rec_id, xtXact
|
||||
#ifdef TRACE_VARIATIONS_IN_DUP_CHECK
|
||||
t_type="Re-read";
|
||||
#endif
|
||||
/* {RETRY-READ} */
|
||||
/* Avoid infinite loop: */
|
||||
if (invalid_rec == rec_id) {
|
||||
/* Should not happen! */
|
||||
if (!tab_record_corrupt(ot, XT_GET_DISK_4(rec_head.tr_row_id_4), rec_id, true, 6))
|
||||
goto failed;
|
||||
#ifdef XT_CRASH_DEBUG
|
||||
/* Generate a core dump! */
|
||||
xt_crash_me();
|
||||
@ -4327,7 +4549,7 @@ xtPublic int xt_tab_maybe_committed(XTOpenTablePtr ot, xtRecordID rec_id, xtXact
|
||||
if (XT_REC_IS_FREE(rec_head.tr_rec_type_1)) {
|
||||
/* Should not happen: */
|
||||
if (invalid_rec != var_rec_id) {
|
||||
var_rec_id = invalid_rec;
|
||||
invalid_rec = var_rec_id;
|
||||
goto retry;
|
||||
}
|
||||
/* Assume end of list. */
|
||||
@ -4364,11 +4586,14 @@ xtPublic int xt_tab_maybe_committed(XTOpenTablePtr ot, xtRecordID rec_id, xtXact
|
||||
}
|
||||
break;
|
||||
case XT_XN_REREAD:
|
||||
/* {RETRY-READ} */
|
||||
if (invalid_rec != var_rec_id) {
|
||||
var_rec_id = invalid_rec;
|
||||
invalid_rec = var_rec_id;
|
||||
goto retry;
|
||||
}
|
||||
/* Assume end of list. */
|
||||
if (!tab_record_corrupt(ot, row_id, invalid_rec, true, 7))
|
||||
goto failed;
|
||||
#ifdef XT_CRASH_DEBUG
|
||||
/* Should not happen! */
|
||||
xt_crash_me();
|
||||
@ -5068,6 +5293,8 @@ xtPublic xtBool xt_tab_seq_next(XTOpenTablePtr ot, xtWord1 *buffer, xtBool *eof)
|
||||
ot->ot_on_page = FALSE;
|
||||
goto next_page;
|
||||
}
|
||||
if (!tab_record_corrupt(ot, XT_GET_DISK_4(((XTTabRecHeadDPtr) buff_ptr)->tr_row_id_4), invalid_rec, true, 8))
|
||||
return XT_ERR;
|
||||
#ifdef XT_CRASH_DEBUG
|
||||
/* Should not happen! */
|
||||
xt_crash_me();
|
||||
@ -5240,7 +5467,7 @@ static xtBool tab_exec_repair_pending(XTDatabaseHPtr db, int what, char *table_n
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void tab_make_table_name(XTTableHPtr tab, char *table_name, size_t size)
|
||||
xtPublic void xt_tab_make_table_name(XTTableHPtr tab, char *table_name, size_t size)
|
||||
{
|
||||
char *nptr;
|
||||
|
||||
@ -5316,7 +5543,7 @@ xtPublic xtBool xt_tab_is_table_repair_pending(XTTableHPtr tab)
|
||||
{
|
||||
char table_name[XT_IDENTIFIER_NAME_SIZE*3+3];
|
||||
|
||||
tab_make_table_name(tab, table_name, sizeof(table_name));
|
||||
xt_tab_make_table_name(tab, table_name, sizeof(table_name));
|
||||
return tab_exec_repair_pending(tab->tab_db, REP_FIND, table_name);
|
||||
}
|
||||
|
||||
@ -5326,7 +5553,7 @@ xtPublic void xt_tab_table_repaired(XTTableHPtr tab)
|
||||
char table_name[XT_IDENTIFIER_NAME_SIZE*3+3];
|
||||
|
||||
tab->tab_repair_pending = FALSE;
|
||||
tab_make_table_name(tab, table_name, sizeof(table_name));
|
||||
xt_tab_make_table_name(tab, table_name, sizeof(table_name));
|
||||
tab_exec_repair_pending(tab->tab_db, REP_DEL, table_name);
|
||||
}
|
||||
}
|
||||
@ -5337,7 +5564,7 @@ xtPublic void xt_tab_set_table_repair_pending(XTTableHPtr tab)
|
||||
char table_name[XT_IDENTIFIER_NAME_SIZE*3+3];
|
||||
|
||||
tab->tab_repair_pending = TRUE;
|
||||
tab_make_table_name(tab, table_name, sizeof(table_name));
|
||||
xt_tab_make_table_name(tab, table_name, sizeof(table_name));
|
||||
tab_exec_repair_pending(tab->tab_db, REP_ADD, table_name);
|
||||
}
|
||||
}
|
||||
|
@ -507,6 +507,7 @@ int xt_tab_compare_names(const char *n1, const char *n2);
|
||||
int xt_tab_compare_paths(char *n1, char *n2);
|
||||
void xt_tab_init_db(struct XTThread *self, struct XTDatabase *db);
|
||||
void xt_tab_exit_db(struct XTThread *self, struct XTDatabase *db);
|
||||
void xt_tab_check_free_lists(struct XTThread *self, XTOpenTablePtr ot, bool check_recs, bool correct_count);
|
||||
void xt_check_tables(struct XTThread *self);
|
||||
|
||||
char *xt_tab_file_to_name(size_t size, char *tab_name, char *file_name);
|
||||
@ -572,6 +573,7 @@ xtBool xt_tab_get_rec_data(register XTOpenTablePtr ot, xtRecordID rec_id, siz
|
||||
void xt_tab_disable_index(XTTableHPtr tab, u_int ind_error);
|
||||
void xt_tab_set_index_error(XTTableHPtr tab);
|
||||
|
||||
void xt_tab_make_table_name(XTTableHPtr tab, char *table_name, size_t size);
|
||||
xtBool xt_tab_is_table_repair_pending(XTTableHPtr tab);
|
||||
void xt_tab_table_repaired(XTTableHPtr tab);
|
||||
void xt_tab_set_table_repair_pending(XTTableHPtr tab);
|
||||
|
@ -224,11 +224,16 @@ static void thr_log_va(XTThreadPtr self, c_char *func, c_char *file, u_int line,
|
||||
#else
|
||||
/* Use the buffer, unless it is too small */
|
||||
va_list ap2;
|
||||
int bufsize;
|
||||
|
||||
va_copy(ap2, ap);
|
||||
if (vsnprintf(buffer, DEFAULT_LOG_BUFFER_SIZE, fmt, ap) >= DEFAULT_LOG_BUFFER_SIZE) {
|
||||
if (vasprintf(&log_string, fmt, ap2) == -1)
|
||||
bufsize = vsnprintf(buffer, DEFAULT_LOG_BUFFER_SIZE, fmt, ap);
|
||||
if (bufsize >= DEFAULT_LOG_BUFFER_SIZE) {
|
||||
log_string = (char *) malloc(bufsize + 1);
|
||||
if (vsnprintf(log_string, bufsize + 1, fmt, ap2) > bufsize) {
|
||||
free(log_string);
|
||||
log_string = NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
log_string = buffer;
|
||||
|
@ -1558,6 +1558,8 @@ xtPublic int xt_xn_status(XTOpenTablePtr ot, xtXactID xn_id, xtRecordID XT_UNUSE
|
||||
* Because we are only here because the record was valid but not
|
||||
* clean (you can confirm this by looking at the code that
|
||||
* calls this function).
|
||||
*
|
||||
* See {RETRY-READ}
|
||||
*/
|
||||
return XT_XN_REREAD;
|
||||
}
|
||||
@ -1743,7 +1745,7 @@ xtPublic xtWord8 xt_xn_bytes_to_sweep(XTDatabaseHPtr db, XTThreadPtr thread)
|
||||
}
|
||||
else {
|
||||
xn_log_id = x_log_id;
|
||||
x_log_offset = x_log_offset;
|
||||
xn_log_offset = x_log_offset;
|
||||
}
|
||||
}
|
||||
xn_id++;
|
||||
|
@ -397,6 +397,24 @@ typedef struct XTPathStr {
|
||||
*/
|
||||
#define XT_XLOG_FLUSH_FREQ 1000
|
||||
|
||||
/*
|
||||
* Define here if you want to check (and correct) the table free list
|
||||
* counts. The free list counts are not durable, because they are not
|
||||
* written to the log.
|
||||
*
|
||||
* The row free count is most critical because it can be used to
|
||||
* estimate the the of rows in the record.
|
||||
*/
|
||||
#define XT_CHECK_ROW_FREE_COUNT
|
||||
#ifdef DEBUG
|
||||
#define XT_CHECK_RECORD_FREE_COUNT
|
||||
#endif
|
||||
#define XT_CORRECT_TABLE_FREE_COUNT
|
||||
|
||||
#if defined(XT_CHECK_ROW_FREE_COUNT) && defined(XT_CORRECT_TABLE_FREE_COUNT)
|
||||
#define XT_ROW_COUNT_CORRECTED
|
||||
#endif
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
* GLOBAL CONSTANTS
|
||||
*/
|
||||
@ -782,7 +800,7 @@ extern xtBool pbxt_crash_debug;
|
||||
#define MX_ULONG_T uint32_t
|
||||
#define MX_ULONGLONG_T uint64_t
|
||||
#define MX_LONGLONG_T uint64_t
|
||||
#define MX_CHARSET_INFO const struct charset_info_st
|
||||
#define MX_CHARSET_INFO struct charset_info_st
|
||||
#define MX_CONST_CHARSET_INFO const struct charset_info_st
|
||||
#define MX_CONST const
|
||||
#define MX_BITMAP MyBitmap
|
||||
@ -873,7 +891,11 @@ extern "C" void session_mark_transaction_to_rollback(Session *session, bool all)
|
||||
#define MX_ULONGLONG_T ulonglong
|
||||
#define MX_LONGLONG_T longlong
|
||||
#define MX_CHARSET_INFO CHARSET_INFO
|
||||
#define MX_CONST_CHARSET_INFO const struct charset_info_st
|
||||
#if defined(MARIADB_BASE_VERSION) && MYSQL_VERSION_ID > 50200
|
||||
#define MX_CONST_CHARSET_INFO const struct charset_info_st
|
||||
#else
|
||||
#define MX_CONST_CHARSET_INFO struct charset_info_st
|
||||
#endif
|
||||
#define MX_CONST
|
||||
#define MX_BITMAP MY_BITMAP
|
||||
#define MX_BIT_SIZE() n_bits
|
||||
|
@ -25,7 +25,7 @@ socket = @MYSQL_UNIX_ADDR@
|
||||
[mysqld]
|
||||
port = @MYSQL_TCP_PORT@
|
||||
socket = @MYSQL_UNIX_ADDR@
|
||||
skip-locking
|
||||
skip-external-locking
|
||||
key_buffer_size = 384M
|
||||
max_allowed_packet = 1M
|
||||
table_open_cache = 512
|
||||
|
@ -25,7 +25,7 @@ socket = @MYSQL_UNIX_ADDR@
|
||||
[mysqld]
|
||||
port = @MYSQL_TCP_PORT@
|
||||
socket = @MYSQL_UNIX_ADDR@
|
||||
skip-locking
|
||||
skip-external-locking
|
||||
key_buffer_size = 256M
|
||||
max_allowed_packet = 1M
|
||||
table_open_cache = 256
|
||||
|
@ -26,7 +26,7 @@ socket = @MYSQL_UNIX_ADDR@
|
||||
[mysqld]
|
||||
port = @MYSQL_TCP_PORT@
|
||||
socket = @MYSQL_UNIX_ADDR@
|
||||
skip-locking
|
||||
skip-external-locking
|
||||
key_buffer_size = 16M
|
||||
max_allowed_packet = 1M
|
||||
table_open_cache = 64
|
||||
|
@ -26,7 +26,7 @@ socket = @MYSQL_UNIX_ADDR@
|
||||
[mysqld]
|
||||
port = @MYSQL_TCP_PORT@
|
||||
socket = @MYSQL_UNIX_ADDR@
|
||||
skip-locking
|
||||
skip-external-locking
|
||||
key_buffer_size = 16K
|
||||
max_allowed_packet = 1M
|
||||
table_open_cache = 4
|
||||
|
Loading…
x
Reference in New Issue
Block a user