14 Feb '19
revision-id: 6ffbfb92ed33b8b94a0de826d7971cf87a78f64e (mariadb-10.3.12-54-g6ffbfb92ed3)
parent(s): 56a8acd2e90c6b9e5f0554e9335590e0427ed2fd
author: Oleksandr Byelkin
committer: Oleksandr Byelkin
timestamp: 2019-02-14 12:53:49 +0100
message:
Try to fix windows compiler warnings
---
sql/log_event.cc | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/sql/log_event.cc b/sql/log_event.cc
index 7d099479549..28ac34c8a0f 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -11959,7 +11959,7 @@ bool copy_cache_to_file_wrapped(IO_CACHE *body,
contribution of non-compressed packet.
*/
my_fprintf(file, fmt_frag, 0);
- if (my_b_copy_to_file(body, file, cache_size/2 + 1))
+ if (my_b_copy_to_file(body, file, (size_t) cache_size/2 + 1))
goto err;
my_fprintf(file, fmt_n_delim, delimiter);
@@ -12018,7 +12018,8 @@ bool copy_cache_to_string_wrapped(IO_CACHE *cache,
if (reinit_io_cache(cache, READ_CACHE, 0L, FALSE, FALSE))
goto err;
- if (!(to->str= (char*) my_malloc(cache->end_of_file + fmt_size, MYF(0))))
+ if (!(to->str= (char*) my_malloc((size_t)cache->end_of_file + fmt_size,
+ MYF(0))))
{
perror("Out of memory: can't allocate memory in "
"copy_cache_to_string_wrapped().");
@@ -12027,7 +12028,8 @@ bool copy_cache_to_string_wrapped(IO_CACHE *cache,
if (!do_wrap)
{
- if (my_b_read(cache, (uchar*) to->str, (to->length= cache->end_of_file)))
+ if (my_b_read(cache, (uchar*) to->str,
+ (to->length= (size_t)cache->end_of_file)))
goto err;
}
else if (4 + sizeof(str_binlog) + cache_size + sizeof(fmt_delim) >
@@ -12073,10 +12075,10 @@ bool copy_cache_to_string_wrapped(IO_CACHE *cache,
char *str= to->str;
str += (to->length= sprintf(str, str_binlog));
- if (my_b_read(cache, (uchar*) str, cache->end_of_file))
+ if (my_b_read(cache, (uchar*) str, (size_t)cache->end_of_file))
goto err;
str += cache->end_of_file;
- to->length += cache->end_of_file;
+ to->length += (size_t)cache->end_of_file;
if (!is_verbose)
to->length += sprintf(str , fmt_delim, delimiter);
}
1
0
revision-id: 56a8acd2e90c6b9e5f0554e9335590e0427ed2fd (mariadb-10.3.12-53-g56a8acd2e90)
parent(s): 1b6b99be24b1757fc7ee06f5dc4c3af0218aedc8
author: Oleksandr Byelkin
committer: Oleksandr Byelkin
timestamp: 2019-02-14 11:06:15 +0100
message:
fix problem of last /
---
storage/innobase/handler/handler0alter.cc | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/storage/innobase/handler/handler0alter.cc b/storage/innobase/handler/handler0alter.cc
index 41607ec589e..16bd8111e98 100644
--- a/storage/innobase/handler/handler0alter.cc
+++ b/storage/innobase/handler/handler0alter.cc
@@ -5034,7 +5034,9 @@ prepare_inplace_alter_table_dict(
goto err_exit;
}
- size_t prefixlen= strlen(mysql_data_home) + 1;
+ size_t prefixlen= strlen(mysql_data_home);
+ if (mysql_data_home[prefixlen-1] != FN_LIBCHAR)
+ prefixlen++;
size_t tablen = altered_table->s->path.length - prefixlen;
const char* part = ctx->old_table->name.part();
size_t partlen = part ? strlen(part) : 0;
1
0
[Commits] d90f54d: MDEV-16188 Introduced the notion of adjusted filter gain.
by IgorBabaev 14 Feb '19
by IgorBabaev 14 Feb '19
14 Feb '19
revision-id: d90f54da28fbadf519900d4a696a1aee0b1177c7 (mariadb-10.3.6-114-gd90f54d)
parent(s): cd00d03fe2d5b92d4451545e3b15477d027c83d0
author: Igor Babaev
committer: Igor Babaev
timestamp: 2019-02-14 00:15:48 -0800
message:
MDEV-16188 Introduced the notion of adjusted filter gain.
Due to inconsistent usage of different cost models to calculate
the cost of ref accesses we have to make the calculation of the
gain promising by usage a range filter more complex.
---
mysql-test/main/key_cache.result | 6 +--
mysql-test/main/range.result | 2 +-
mysql-test/main/select.result | 8 ++--
mysql-test/main/select_jcl6.result | 8 ++--
mysql-test/main/select_pkeycache.result | 8 ++--
mysql-test/main/subselect2.result | 2 +-
mysql-test/main/subselect_sj2.result | 20 +++++-----
mysql-test/main/subselect_sj2.test | 2 +
mysql-test/main/subselect_sj2_jcl6.result | 20 +++++-----
mysql-test/main/subselect_sj2_mat.result | 20 +++++-----
sql/ha_partition.cc | 2 +-
sql/rowid_filter.cc | 66 ++++++++++++++++++++++++++++---
sql/rowid_filter.h | 37 ++++++++++-------
sql/sql_select.cc | 63 +++++++++++++++++++----------
sql/table.h | 3 +-
15 files changed, 178 insertions(+), 89 deletions(-)
diff --git a/mysql-test/main/key_cache.result b/mysql-test/main/key_cache.result
index e1f8892..36c75ad 100644
--- a/mysql-test/main/key_cache.result
+++ b/mysql-test/main/key_cache.result
@@ -739,13 +739,13 @@ p
1019
explain select i from t2 where a='yyyy' and i=3;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t2 ref|filter k1,k2 k1|k2 5|11 const 189 (27%) Using where; Using rowid filter
+1 SIMPLE t2 ref k1,k2 k1 5 const 189 Using where
select i from t2 where a='yyyy' and i=3;
i
3
explain select a from t2 where a='yyyy' and i=3;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t2 ref|filter k1,k2 k1|k2 5|11 const 189 (27%) Using where; Using rowid filter
+1 SIMPLE t2 ref k1,k2 k1 5 const 189 Using where
select a from t2 where a='yyyy' and i=3 ;
a
yyyy
@@ -753,7 +753,7 @@ select * from information_schema.key_caches where segment_number is null;
KEY_CACHE_NAME SEGMENTS SEGMENT_NUMBER FULL_SIZE BLOCK_SIZE USED_BLOCKS UNUSED_BLOCKS DIRTY_BLOCKS READ_REQUESTS READS WRITE_REQUESTS WRITES
default 2 NULL 32768 1024 # # 0 3178 24 1552 18
small NULL NULL 1048576 1024 # # 0 0 0 0 0
-keycache1 7 NULL 262143 2048 # # 0 3283 43 1594 30
+keycache1 7 NULL 262143 2048 # # 0 3231 43 1594 30
keycache2 NULL NULL 1048576 1024 # # 0 6 6 3 3
set global keycache1.key_cache_block_size=2*1024;
insert into t2 values (7000, 3, 'yyyy');
diff --git a/mysql-test/main/range.result b/mysql-test/main/range.result
index a0e04fc..bcb43d1 100644
--- a/mysql-test/main/range.result
+++ b/mysql-test/main/range.result
@@ -280,7 +280,7 @@ INSERT INTO t1 VALUES
(33,5),(33,5),(33,5),(33,5),(34,5),(35,5);
EXPLAIN SELECT * FROM t1 WHERE a IN(1,2) AND b=5;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 range a,b a 5 NULL 2 Using index condition; Using where
+1 SIMPLE t1 ref|filter a,b b|a 5|5 const 15 (5%) Using where; Using rowid filter
SELECT * FROM t1 WHERE a IN(1,2) AND b=5;
a b
DROP TABLE t1;
diff --git a/mysql-test/main/select.result b/mysql-test/main/select.result
index 7a71c34..f1a976b 100644
--- a/mysql-test/main/select.result
+++ b/mysql-test/main/select.result
@@ -3475,13 +3475,13 @@ INSERT INTO t2 VALUES
EXPLAIN
SELECT a, c, d, f FROM t1,t2 WHERE a=c AND b BETWEEN 4 AND 6;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 range PRIMARY,b b 5 NULL 3 Using index condition
-1 SIMPLE t2 ref c c 5 test.t1.a 2
+1 SIMPLE t2 ALL c NULL NULL NULL 18 Using where
+1 SIMPLE t1 eq_ref|filter PRIMARY,b PRIMARY|b 4|5 test.t2.c 1 (30%) Using where; Using rowid filter
EXPLAIN
SELECT a, c, d, f FROM t1,t2 WHERE a=c AND b BETWEEN 4 AND 6 AND a > 0;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 range PRIMARY,b b 5 NULL 3 Using index condition; Using where
-1 SIMPLE t2 ref c c 5 test.t1.a 2
+1 SIMPLE t2 ALL c NULL NULL NULL 18 Using where
+1 SIMPLE t1 eq_ref|filter PRIMARY,b PRIMARY|b 4|5 test.t2.c 1 (30%) Using where; Using rowid filter
DROP TABLE t1, t2;
create table t1 (
a int unsigned not null auto_increment primary key,
diff --git a/mysql-test/main/select_jcl6.result b/mysql-test/main/select_jcl6.result
index 8b49623..8f1539b 100644
--- a/mysql-test/main/select_jcl6.result
+++ b/mysql-test/main/select_jcl6.result
@@ -3486,13 +3486,13 @@ INSERT INTO t2 VALUES
EXPLAIN
SELECT a, c, d, f FROM t1,t2 WHERE a=c AND b BETWEEN 4 AND 6;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 range PRIMARY,b b 5 NULL 3 Using index condition; Rowid-ordered scan
-1 SIMPLE t2 ref c c 5 test.t1.a 2 Using join buffer (flat, BKA join); Key-ordered Rowid-ordered scan
+1 SIMPLE t2 ALL c NULL NULL NULL 18 Using where
+1 SIMPLE t1 eq_ref|filter PRIMARY,b PRIMARY|b 4|5 test.t2.c 1 (30%) Using where; Using join buffer (flat, BKA join); Key-ordered Rowid-ordered scan; Using rowid filter
EXPLAIN
SELECT a, c, d, f FROM t1,t2 WHERE a=c AND b BETWEEN 4 AND 6 AND a > 0;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 range PRIMARY,b b 5 NULL 3 Using index condition; Using where; Rowid-ordered scan
-1 SIMPLE t2 ref c c 5 test.t1.a 2 Using join buffer (flat, BKA join); Key-ordered Rowid-ordered scan
+1 SIMPLE t2 ALL c NULL NULL NULL 18 Using where
+1 SIMPLE t1 eq_ref|filter PRIMARY,b PRIMARY|b 4|5 test.t2.c 1 (30%) Using where; Using join buffer (flat, BKA join); Key-ordered Rowid-ordered scan; Using rowid filter
DROP TABLE t1, t2;
create table t1 (
a int unsigned not null auto_increment primary key,
diff --git a/mysql-test/main/select_pkeycache.result b/mysql-test/main/select_pkeycache.result
index 7a71c34..f1a976b 100644
--- a/mysql-test/main/select_pkeycache.result
+++ b/mysql-test/main/select_pkeycache.result
@@ -3475,13 +3475,13 @@ INSERT INTO t2 VALUES
EXPLAIN
SELECT a, c, d, f FROM t1,t2 WHERE a=c AND b BETWEEN 4 AND 6;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 range PRIMARY,b b 5 NULL 3 Using index condition
-1 SIMPLE t2 ref c c 5 test.t1.a 2
+1 SIMPLE t2 ALL c NULL NULL NULL 18 Using where
+1 SIMPLE t1 eq_ref|filter PRIMARY,b PRIMARY|b 4|5 test.t2.c 1 (30%) Using where; Using rowid filter
EXPLAIN
SELECT a, c, d, f FROM t1,t2 WHERE a=c AND b BETWEEN 4 AND 6 AND a > 0;
id select_type table type possible_keys key key_len ref rows Extra
-1 SIMPLE t1 range PRIMARY,b b 5 NULL 3 Using index condition; Using where
-1 SIMPLE t2 ref c c 5 test.t1.a 2
+1 SIMPLE t2 ALL c NULL NULL NULL 18 Using where
+1 SIMPLE t1 eq_ref|filter PRIMARY,b PRIMARY|b 4|5 test.t2.c 1 (30%) Using where; Using rowid filter
DROP TABLE t1, t2;
create table t1 (
a int unsigned not null auto_increment primary key,
diff --git a/mysql-test/main/subselect2.result b/mysql-test/main/subselect2.result
index 7620842..21bf9aa 100644
--- a/mysql-test/main/subselect2.result
+++ b/mysql-test/main/subselect2.result
@@ -132,7 +132,7 @@ id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t3 eq_ref PRIMARY,FFOLDERID_IDX,CMFLDRPARNT_IDX PRIMARY 34 test.t3.PARENTID 1 Using where
1 PRIMARY t3 eq_ref PRIMARY,FFOLDERID_IDX,CMFLDRPARNT_IDX PRIMARY 34 test.t3.PARENTID 1 Using where
1 PRIMARY t3 eq_ref PRIMARY,FFOLDERID_IDX,CMFLDRPARNT_IDX PRIMARY 34 test.t3.PARENTID 1 Using where
-1 PRIMARY t3 eq_ref PRIMARY,FFOLDERID_IDX,CMFLDRPARNT_IDX PRIMARY 34 test.t3.PARENTID 1 Using where
+1 PRIMARY t3 ref|filter PRIMARY,FFOLDERID_IDX,CMFLDRPARNT_IDX FFOLDERID_IDX|CMFLDRPARNT_IDX 34|35 test.t3.PARENTID 1 (29%) Using where; Using rowid filter
drop table t1, t2, t3, t4;
CREATE TABLE t1 (a int(10) , PRIMARY KEY (a)) Engine=InnoDB;
INSERT INTO t1 VALUES (1),(2);
diff --git a/mysql-test/main/subselect_sj2.result b/mysql-test/main/subselect_sj2.result
index 6b86f5a..649647d 100644
--- a/mysql-test/main/subselect_sj2.result
+++ b/mysql-test/main/subselect_sj2.result
@@ -1107,11 +1107,11 @@ WHERE alias5.b = alias4.b
AND ( alias5.b >= alias3.b OR alias5.c != alias3.c )
);
id select_type table type possible_keys key key_len ref rows Extra
-1 PRIMARY alias3 ALL PRIMARY NULL NULL NULL 19
-1 PRIMARY alias5 index PRIMARY c 4 NULL 19 Using where; Using index
-1 PRIMARY alias4 eq_ref PRIMARY,c PRIMARY 4 test.alias5.b 1 Using where; FirstMatch(alias3)
-1 PRIMARY alias1 ALL NULL NULL NULL NULL 14 Using join buffer (flat, BNL join)
-1 PRIMARY alias2 ALL NULL NULL NULL NULL 14 Using join buffer (flat, BNL join)
+1 PRIMARY alias3 ALL PRIMARY NULL NULL NULL #
+1 PRIMARY alias5 index PRIMARY c 4 NULL # Using where; Using index
+1 PRIMARY alias4 eq_ref PRIMARY,c PRIMARY 4 test.alias5.b # Using where; FirstMatch(alias3)
+1 PRIMARY alias1 ALL NULL NULL NULL NULL # Using join buffer (flat, BNL join)
+1 PRIMARY alias2 ALL NULL NULL NULL NULL # Using join buffer (flat, BNL join)
SELECT COUNT(*) FROM t1 AS alias1, t1 AS alias2, t2 AS alias3
WHERE alias3.d IN (
SELECT alias4.c FROM t2 AS alias4, t2 AS alias5
@@ -1128,11 +1128,11 @@ WHERE alias5.b = alias4.b
AND ( alias5.b >= alias3.b OR alias3.c != alias5.c )
);
id select_type table type possible_keys key key_len ref rows Extra
-1 PRIMARY alias3 ALL PRIMARY NULL NULL NULL 19
-1 PRIMARY alias5 index PRIMARY c 4 NULL 19 Using where; Using index
-1 PRIMARY alias4 eq_ref PRIMARY,c PRIMARY 4 test.alias5.b 1 Using where; FirstMatch(alias3)
-1 PRIMARY alias1 ALL NULL NULL NULL NULL 14 Using join buffer (flat, BNL join)
-1 PRIMARY alias2 ALL NULL NULL NULL NULL 14 Using join buffer (flat, BNL join)
+1 PRIMARY alias3 ALL PRIMARY NULL NULL NULL #
+1 PRIMARY alias5 index PRIMARY c 4 NULL # Using where; Using index
+1 PRIMARY alias4 eq_ref PRIMARY,c PRIMARY 4 test.alias5.b # Using where; FirstMatch(alias3)
+1 PRIMARY alias1 ALL NULL NULL NULL NULL # Using join buffer (flat, BNL join)
+1 PRIMARY alias2 ALL NULL NULL NULL NULL # Using join buffer (flat, BNL join)
SELECT COUNT(*) FROM t1 AS alias1, t1 AS alias2, t2 AS alias3
WHERE alias3.d IN (
SELECT alias4.c FROM t2 AS alias4, t2 AS alias5
diff --git a/mysql-test/main/subselect_sj2.test b/mysql-test/main/subselect_sj2.test
index 9ed886d..e7dd6e7 100644
--- a/mysql-test/main/subselect_sj2.test
+++ b/mysql-test/main/subselect_sj2.test
@@ -1223,6 +1223,7 @@ INSERT INTO t2 VALUES
(13,'b','b'),(14,'x','x'),(15,'g','g'),(16,'p','p'),
(17,'q','q'),(18,'w','w'),(19,'d','d');
+--replace_column 9 #
EXPLAIN
SELECT COUNT(*) FROM t1 AS alias1, t1 AS alias2, t2 AS alias3
WHERE alias3.d IN (
@@ -1242,6 +1243,7 @@ WHERE alias3.d IN (
# Do the same EXPLAIN SELECT and SELECT
# with "alias3.c != alias5.c" instead of "alias5.c != alias3.c"
+--replace_column 9 #
EXPLAIN
SELECT COUNT(*) FROM t1 AS alias1, t1 AS alias2, t2 AS alias3
WHERE alias3.d IN (
diff --git a/mysql-test/main/subselect_sj2_jcl6.result b/mysql-test/main/subselect_sj2_jcl6.result
index c523525..62866bd 100644
--- a/mysql-test/main/subselect_sj2_jcl6.result
+++ b/mysql-test/main/subselect_sj2_jcl6.result
@@ -1123,11 +1123,11 @@ WHERE alias5.b = alias4.b
AND ( alias5.b >= alias3.b OR alias5.c != alias3.c )
);
id select_type table type possible_keys key key_len ref rows Extra
-1 PRIMARY alias3 ALL PRIMARY NULL NULL NULL 19
-1 PRIMARY alias5 index PRIMARY c 4 NULL 19 Using where; Using index; Using join buffer (flat, BNL join)
-1 PRIMARY alias4 eq_ref PRIMARY,c PRIMARY 4 test.alias5.b 1 Using where; FirstMatch(alias3); Using join buffer (incremental, BKA join); Key-ordered scan
-1 PRIMARY alias1 ALL NULL NULL NULL NULL 14 Using join buffer (incremental, BNL join)
-1 PRIMARY alias2 ALL NULL NULL NULL NULL 14 Using join buffer (incremental, BNL join)
+1 PRIMARY alias3 ALL PRIMARY NULL NULL NULL #
+1 PRIMARY alias5 index PRIMARY c 4 NULL # Using where; Using index; Using join buffer (flat, BNL join)
+1 PRIMARY alias4 eq_ref PRIMARY,c PRIMARY 4 test.alias5.b # Using where; FirstMatch(alias3); Using join buffer (incremental, BKA join); Key-ordered scan
+1 PRIMARY alias1 ALL NULL NULL NULL NULL # Using join buffer (incremental, BNL join)
+1 PRIMARY alias2 ALL NULL NULL NULL NULL # Using join buffer (incremental, BNL join)
SELECT COUNT(*) FROM t1 AS alias1, t1 AS alias2, t2 AS alias3
WHERE alias3.d IN (
SELECT alias4.c FROM t2 AS alias4, t2 AS alias5
@@ -1144,11 +1144,11 @@ WHERE alias5.b = alias4.b
AND ( alias5.b >= alias3.b OR alias3.c != alias5.c )
);
id select_type table type possible_keys key key_len ref rows Extra
-1 PRIMARY alias3 ALL PRIMARY NULL NULL NULL 19
-1 PRIMARY alias5 index PRIMARY c 4 NULL 19 Using where; Using index; Using join buffer (flat, BNL join)
-1 PRIMARY alias4 eq_ref PRIMARY,c PRIMARY 4 test.alias5.b 1 Using where; FirstMatch(alias3); Using join buffer (incremental, BKA join); Key-ordered scan
-1 PRIMARY alias1 ALL NULL NULL NULL NULL 14 Using join buffer (incremental, BNL join)
-1 PRIMARY alias2 ALL NULL NULL NULL NULL 14 Using join buffer (incremental, BNL join)
+1 PRIMARY alias3 ALL PRIMARY NULL NULL NULL #
+1 PRIMARY alias5 index PRIMARY c 4 NULL # Using where; Using index; Using join buffer (flat, BNL join)
+1 PRIMARY alias4 eq_ref PRIMARY,c PRIMARY 4 test.alias5.b # Using where; FirstMatch(alias3); Using join buffer (incremental, BKA join); Key-ordered scan
+1 PRIMARY alias1 ALL NULL NULL NULL NULL # Using join buffer (incremental, BNL join)
+1 PRIMARY alias2 ALL NULL NULL NULL NULL # Using join buffer (incremental, BNL join)
SELECT COUNT(*) FROM t1 AS alias1, t1 AS alias2, t2 AS alias3
WHERE alias3.d IN (
SELECT alias4.c FROM t2 AS alias4, t2 AS alias5
diff --git a/mysql-test/main/subselect_sj2_mat.result b/mysql-test/main/subselect_sj2_mat.result
index c05db8d..91ab00d 100644
--- a/mysql-test/main/subselect_sj2_mat.result
+++ b/mysql-test/main/subselect_sj2_mat.result
@@ -1109,11 +1109,11 @@ WHERE alias5.b = alias4.b
AND ( alias5.b >= alias3.b OR alias5.c != alias3.c )
);
id select_type table type possible_keys key key_len ref rows Extra
-1 PRIMARY alias3 ALL PRIMARY NULL NULL NULL 19
-1 PRIMARY alias5 index PRIMARY c 4 NULL 19 Using where; Using index
-1 PRIMARY alias4 eq_ref PRIMARY,c PRIMARY 4 test.alias5.b 1 Using where; FirstMatch(alias3)
-1 PRIMARY alias1 ALL NULL NULL NULL NULL 14 Using join buffer (flat, BNL join)
-1 PRIMARY alias2 ALL NULL NULL NULL NULL 14 Using join buffer (flat, BNL join)
+1 PRIMARY alias3 ALL PRIMARY NULL NULL NULL #
+1 PRIMARY alias5 index PRIMARY c 4 NULL # Using where; Using index
+1 PRIMARY alias4 eq_ref PRIMARY,c PRIMARY 4 test.alias5.b # Using where; FirstMatch(alias3)
+1 PRIMARY alias1 ALL NULL NULL NULL NULL # Using join buffer (flat, BNL join)
+1 PRIMARY alias2 ALL NULL NULL NULL NULL # Using join buffer (flat, BNL join)
SELECT COUNT(*) FROM t1 AS alias1, t1 AS alias2, t2 AS alias3
WHERE alias3.d IN (
SELECT alias4.c FROM t2 AS alias4, t2 AS alias5
@@ -1130,11 +1130,11 @@ WHERE alias5.b = alias4.b
AND ( alias5.b >= alias3.b OR alias3.c != alias5.c )
);
id select_type table type possible_keys key key_len ref rows Extra
-1 PRIMARY alias3 ALL PRIMARY NULL NULL NULL 19
-1 PRIMARY alias5 index PRIMARY c 4 NULL 19 Using where; Using index
-1 PRIMARY alias4 eq_ref PRIMARY,c PRIMARY 4 test.alias5.b 1 Using where; FirstMatch(alias3)
-1 PRIMARY alias1 ALL NULL NULL NULL NULL 14 Using join buffer (flat, BNL join)
-1 PRIMARY alias2 ALL NULL NULL NULL NULL 14 Using join buffer (flat, BNL join)
+1 PRIMARY alias3 ALL PRIMARY NULL NULL NULL #
+1 PRIMARY alias5 index PRIMARY c 4 NULL # Using where; Using index
+1 PRIMARY alias4 eq_ref PRIMARY,c PRIMARY 4 test.alias5.b # Using where; FirstMatch(alias3)
+1 PRIMARY alias1 ALL NULL NULL NULL NULL # Using join buffer (flat, BNL join)
+1 PRIMARY alias2 ALL NULL NULL NULL NULL # Using join buffer (flat, BNL join)
SELECT COUNT(*) FROM t1 AS alias1, t1 AS alias2, t2 AS alias3
WHERE alias3.d IN (
SELECT alias4.c FROM t2 AS alias4, t2 AS alias5
diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc
index acf20b4..c13b26c 100644
--- a/sql/ha_partition.cc
+++ b/sql/ha_partition.cc
@@ -6345,7 +6345,7 @@ ha_rows ha_partition::multi_range_read_info(uint keyno, uint n_ranges,
{
uint i;
handler **file;
- ha_rows rows;
+ ha_rows rows= 0;
Cost_estimate part_cost;
DBUG_ENTER("ha_partition::multi_range_read_info");
DBUG_PRINT("enter", ("partition this: %p", this));
diff --git a/sql/rowid_filter.cc b/sql/rowid_filter.cc
index cea0009..4a4869b 100644
--- a/sql/rowid_filter.cc
+++ b/sql/rowid_filter.cc
@@ -49,6 +49,54 @@ double Range_rowid_filter_cost_info::avg_access_and_eval_gain_per_row(
lookup_cost(cont_type);
}
+
+/**
+ @brief
+ The average adjusted gain in cost per row of using the filter
+
+ @param access_cost_factor the adjusted cost of access a row
+
+ @details
+ The current code to estimate the cost of a ref access is quite inconsistent:
+ in some cases the effect of page buffers is taken into account, for others
+ just the engine dependent read_time() is employed. That's why the average
+ cost of one random seek might differ from 1.
+ The parameter access_cost_factor can be considered as the cost of a random
+ seek that is used for the given ref access. Changing the cost of a random
+ seek we have to change the first coefficient in the linear formula by which
+ we calculate the gain of usage the given filter for a_adj. This function
+ calculates the value of a_adj.
+
+ @note
+ Currently we require that access_cost_factor should be a number between
+ 0.0 and 1.0
+*/
+
+inline
+double Range_rowid_filter_cost_info::avg_adjusted_gain_per_row(
+ double access_cost_factor)
+{
+ return a - (1 - access_cost_factor) * (1 - selectivity);
+}
+
+
+/**
+ @brief
+ Set the parameters used to choose the filter with the best adjusted gain
+
+ @note
+ This function must be called before the call of get_adjusted_gain()
+ for the given filter.
+*/
+
+inline void
+Range_rowid_filter_cost_info::set_adjusted_gain_param(double access_cost_factor)
+{
+ a_adj= avg_adjusted_gain_per_row(access_cost_factor);
+ cross_x_adj= b / a_adj;
+}
+
+
/**
@brief
Initialize the cost info structure for a range filter
@@ -358,6 +406,7 @@ void TABLE::init_cost_info_for_usable_range_rowid_filters(THD *thd)
@param access_key_no The index by which the table is accessed
@param records The estimated total number of key tuples with this access
+ @param access_cost_factor the cost of a random seek to access the table
@details
The function looks through the array of cost info for range filters
@@ -365,8 +414,12 @@ void TABLE::init_cost_info_for_usable_range_rowid_filters(THD *thd)
gain with the the ref or range access of the table by access_key_no.
As the array is sorted by cross_x in ascending order the function stops
the look through as soon as it reaches the first element with
- cross_x > records because the range filter for this element and the
- range filters for all remaining elements do not promise positive gains
+ cross_x_adj > records because the range filter for this element and the
+ range filters for all remaining elements do not promise positive gains.
+
+ @note
+ It is easy to see that if cross_x[i] > cross_x[j] then
+ cross_x_adj[i] > cross_x_adj[j]
@retval Pointer to the cost info for the range filter that promises
the greatest gain, NULL if there is no such range filter
@@ -374,7 +427,8 @@ void TABLE::init_cost_info_for_usable_range_rowid_filters(THD *thd)
Range_rowid_filter_cost_info *
TABLE::best_range_rowid_filter_for_partial_join(uint access_key_no,
- double records)
+ double records,
+ double access_cost_factor)
{
if (!this || range_rowid_filter_cost_info_elems == 0 ||
covering_keys.is_set(access_key_no))
@@ -408,13 +462,15 @@ TABLE::best_range_rowid_filter_for_partial_join(uint access_key_no,
no_filter_usage.is_set(filter->key_no))
continue;
- if (records < filter->cross_x)
+ filter->set_adjusted_gain_param(access_cost_factor);
+
+ if (records < filter->cross_x_adj)
{
/* Does not make sense to look through the remaining filters */
break;
}
- curr_gain= filter->get_gain(records);
+ curr_gain= filter->get_adjusted_gain(records);
if (best_filter_gain < curr_gain)
{
best_filter_gain= curr_gain;
diff --git a/sql/rowid_filter.h b/sql/rowid_filter.h
index 3195e76..3f8faea 100644
--- a/sql/rowid_filter.h
+++ b/sql/rowid_filter.h
@@ -396,6 +396,13 @@ class Range_rowid_filter_cost_info : public Sql_alloc
/* Used for pruning of the potential range filters */
key_map abs_independent;
+ /*
+ These two parameters are used to choose the best range filter
+ in the function TABLE::best_range_rowid_filter_for_partial_join
+ */
+ double a_adj;
+ double cross_x_adj;
+
public:
/* The type of the container of the range filter */
Rowid_filter_container_type container_type;
@@ -416,30 +423,29 @@ class Range_rowid_filter_cost_info : public Sql_alloc
inline double
avg_access_and_eval_gain_per_row(Rowid_filter_container_type cont_type);
- /* Get the gain that usage of filter promises for 'rows' key tuples */
- inline double get_gain(double rows)
+ inline double avg_adjusted_gain_per_row(double access_cost_factor);
+
+ inline void set_adjusted_gain_param(double access_cost_factor);
+
+ /* Get the gain that usage of filter promises for r key tuples */
+ inline double get_gain(double r)
{
- return rows * a - b;
+ return r * a - b;
}
- /*
- The gain promised by usage of the filter at the assumption that
- when the table is accessed without the filter at most worst_seeks
- pages are fetched from disk to access the data rows of the table
- */
- inline double get_adjusted_gain(double rows, double worst_seeks)
+ /* Get the adjusted gain that usage of filter promises for r key tuples */
+ inline double get_adjusted_gain(double r)
{
- return get_gain(rows) -
- (1 - selectivity) * (rows - MY_MIN(rows, worst_seeks));
+ return r * a_adj - b;
}
/*
- The gain promised by usage of the filter
+ The gain promised by usage of the filter for r key tuples
due to less condition evaluations
*/
- inline double get_cmp_gain(double rows)
+ inline double get_cmp_gain(double r)
{
- return rows * (1 - selectivity) / TIME_FOR_COMPARE;
+ return r * (1 - selectivity) / TIME_FOR_COMPARE;
}
Rowid_filter_container *create_container();
@@ -455,7 +461,8 @@ class Range_rowid_filter_cost_info : public Sql_alloc
friend
Range_rowid_filter_cost_info *
TABLE::best_range_rowid_filter_for_partial_join(uint access_key_no,
- double records);
+ double records,
+ double access_cost_factor);
};
#endif /* ROWID_FILTER_INCLUDED */
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 3d23134..cec5ff7 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -6981,6 +6981,7 @@ best_access_path(JOIN *join,
KEYUSE *hj_start_key= 0;
SplM_plan_info *spl_plan= 0;
Range_rowid_filter_cost_info *filter= 0;
+ double filter_cmp_gain= 0;
disable_jbuf= disable_jbuf || idx == join->const_tables;
@@ -7376,12 +7377,14 @@ best_access_path(JOIN *join,
if (records < DBL_MAX)
{
double rows= record_count * records;
+ double access_cost_factor= MY_MIN(tmp / rows, 1.0);
filter=
- table->best_range_rowid_filter_for_partial_join(start_key->key, rows);
+ table->best_range_rowid_filter_for_partial_join(start_key->key, rows,
+ access_cost_factor);
if (filter)
{
- tmp-= filter->get_adjusted_gain(rows, s->worst_seeks) -
- filter->get_cmp_gain(rows);
+ filter->get_cmp_gain(rows);
+ tmp-= filter->get_adjusted_gain(rows) - filter->get_cmp_gain(rows);
DBUG_ASSERT(tmp >= 0);
}
}
@@ -7508,12 +7511,14 @@ best_access_path(JOIN *join,
if ( s->quick->get_type() == QUICK_SELECT_I::QS_TYPE_RANGE)
{
double rows= record_count * s->found_records;
+ double access_cost_factor= MY_MIN(tmp / rows, 1.0);
uint key_no= s->quick->index;
- filter= s->table->best_range_rowid_filter_for_partial_join(key_no,
- rows);
+ filter=
+ s->table->best_range_rowid_filter_for_partial_join(key_no, rows,
+ access_cost_factor);
if (filter)
{
- tmp-= filter->get_gain(rows);
+ tmp-= filter->get_adjusted_gain(rows);
DBUG_ASSERT(tmp >= 0);
}
}
@@ -7556,7 +7561,6 @@ best_access_path(JOIN *join,
}
}
- double best_records= rnd_records;
/* Splitting technique cannot be used with join cache */
if (s->table->is_splittable())
tmp+= s->table->get_materialization_cost();
@@ -7569,28 +7573,32 @@ best_access_path(JOIN *join,
tmp give us total cost of using TABLE SCAN
*/
- double filter_cmp_gain= 0;
- if (filter)
+ double best_filter_cmp_gain= 0;
+ if (best_filter)
{
- filter_cmp_gain= filter->get_cmp_gain(record_count * s->found_records);
+ best_filter_cmp_gain= best_filter->get_cmp_gain(record_count * records);
}
if (best == DBL_MAX ||
(tmp + record_count/(double) TIME_FOR_COMPARE*rnd_records <
(best_key->is_for_hash_join() ? best_time :
best + record_count/(double) TIME_FOR_COMPARE*records -
- filter_cmp_gain)))
+ best_filter_cmp_gain)))
{
/*
If the table has a range (s->quick is set) make_join_select()
will ensure that this will be used
*/
best= tmp;
- records= best_records;
+ records= rnd_records;
best_key= 0;
best_filter= 0;
- if (s->quick && s->quick->get_type() == QUICK_SELECT_I::QS_TYPE_RANGE)
+ if (s->quick && s->quick->get_type() == QUICK_SELECT_I::QS_TYPE_RANGE &&
+ filter)
+ {
best_filter= filter;
+ best_filter_cmp_gain= filter_cmp_gain;
+ }
/* range/index_merge/ALL/index access method are "independent", so: */
best_ref_depends_map= 0;
best_uses_jbuf= MY_TEST(!disable_jbuf && !((s->table->map &
@@ -8091,14 +8099,22 @@ optimize_straight_join(JOIN *join, table_map join_tables)
for (JOIN_TAB **pos= join->best_ref + idx ; (s= *pos) ; pos++)
{
- /* Find the best access method from 's' to the current partial plan */
+ POSITION *position= join->positions + idx;
+ /* Find the best access method from 's' to the current partial plan */
best_access_path(join, s, join_tables, idx, disable_jbuf, record_count,
- join->positions + idx, &loose_scan_pos);
+ position, &loose_scan_pos);
/* compute the cost of the new plan extended with 's' */
- record_count*= join->positions[idx].records_read;
- read_time+= join->positions[idx].read_time +
- record_count / (double) TIME_FOR_COMPARE;
+ record_count*= position->records_read;
+ double filter_cmp_gain= 0;
+ if (position->range_rowid_filter_info)
+ {
+ filter_cmp_gain=
+ position->range_rowid_filter_info->get_cmp_gain(record_count);
+ }
+ read_time+= position->read_time +
+ record_count / (double) TIME_FOR_COMPARE -
+ filter_cmp_gain;
advance_sj_state(join, join_tables, idx, &record_count, &read_time,
&loose_scan_pos);
@@ -8107,7 +8123,7 @@ optimize_straight_join(JOIN *join, table_map join_tables)
if (use_cond_selectivity > 1)
pushdown_cond_selectivity= table_cond_selectivity(join, idx, s,
join_tables);
- join->positions[idx].cond_selectivity= pushdown_cond_selectivity;
+ position->cond_selectivity= pushdown_cond_selectivity;
++idx;
}
@@ -9006,8 +9022,15 @@ best_extension_by_limited_search(JOIN *join,
current_record_count= record_count * position->records_read;
else
current_record_count= DBL_MAX;
+ double filter_cmp_gain= 0;
+ if (position->range_rowid_filter_info)
+ {
+ filter_cmp_gain=
+ position->range_rowid_filter_info->get_cmp_gain(current_record_count);
+ }
current_read_time=read_time + position->read_time +
- current_record_count / (double) TIME_FOR_COMPARE;
+ current_record_count / (double) TIME_FOR_COMPARE -
+ filter_cmp_gain;
advance_sj_state(join, remaining_tables, idx, ¤t_record_count,
¤t_read_time, &loose_scan_pos);
diff --git a/sql/table.h b/sql/table.h
index 377176f..af38082 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -1524,7 +1524,8 @@ struct TABLE
void prune_range_rowid_filters();
Range_rowid_filter_cost_info *
best_range_rowid_filter_for_partial_join(uint access_key_no,
- double records);
+ double records,
+ double access_cost_factor);
/**
System Versioning support
1
0
[Commits] b428e82: MDEV-7974 backport fix for mysql bug#12161 (XA and binlog).
by holyfoot@askmonty.org 13 Feb '19
by holyfoot@askmonty.org 13 Feb '19
13 Feb '19
revision-id: b428e822da09b2bc82a2447332bb980f93c80262 (mariadb-10.4.1-103-gb428e82)
parent(s): 8aae31cf494678b6253031c627566e50bc666920
committer: Alexey Botchkov
timestamp: 2019-02-14 02:46:57 +0400
message:
MDEV-7974 backport fix for mysql bug#12161 (XA and binlog).
XA transactions now are kept persistent after prepare.
XA_prepare_log_event implamented, and XA tranasctions are logged
as XA transactions.
---
sql/handler.cc | 9 ++
sql/handler.h | 10 ++
sql/log.cc | 115 +++++++++++++---
sql/log.h | 10 ++
sql/log_event.cc | 397 +++++++++++++++++++++++++++++++++++++++++++++++++++--
sql/log_event.h | 81 +++++++++++
sql/sql_class.cc | 18 +--
sql/sql_class.h | 20 ++-
sql/sql_connect.cc | 1 +
sql/transaction.cc | 69 +++++++++-
sql/transaction.h | 1 +
11 files changed, 691 insertions(+), 40 deletions(-)
diff --git a/sql/handler.cc b/sql/handler.cc
index 001055c..3b2a3e0 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -1214,6 +1214,9 @@ static int prepare_or_error(handlerton *ht, THD *thd, bool all)
}
+/*static inline */int
+binlog_commit_flush_xid_caches(THD *thd, binlog_cache_mngr *cache_mngr,
+ bool all, my_xid xid);
/**
@retval
0 ok
@@ -1225,6 +1228,7 @@ int ha_prepare(THD *thd)
int error=0, all=1;
THD_TRANS *trans=all ? &thd->transaction.all : &thd->transaction.stmt;
Ha_trx_info *ha_info= trans->ha_list;
+
DBUG_ENTER("ha_prepare");
if (ha_info)
@@ -1250,6 +1254,11 @@ int ha_prepare(THD *thd)
}
}
+ if (unlikely(tc_log->log_prepare(thd, all)))
+ {
+ ha_rollback_trans(thd, all);
+ error=1;
+ }
}
DBUG_RETURN(error);
diff --git a/sql/handler.h b/sql/handler.h
index fc6246c..613c1c3 100644
--- a/sql/handler.h
+++ b/sql/handler.h
@@ -810,6 +810,16 @@ struct xid_t {
long gtrid_length;
long bqual_length;
char data[XIDDATASIZE]; // not \0-terminated !
+ /*
+ The size of the string containing serialized Xid representation
+ is computed as a sum of
+ eight as the number of formatting symbols (X'',X'',)
+ plus 2 x XIDDATASIZE (2 due to hex format),
+ plus space for decimal digits of XID::formatID,
+ plus one for 0x0.
+ */
+ static const uint ser_buf_size=
+ 8 + 2 * XIDDATASIZE + 4 * sizeof(long) + 1;
xid_t() {} /* Remove gcc warning */
bool eq(struct xid_t *xid)
diff --git a/sql/log.cc b/sql/log.cc
index a56117a..316b871 100644
--- a/sql/log.cc
+++ b/sql/log.cc
@@ -87,6 +87,9 @@ static bool binlog_savepoint_rollback_can_release_mdl(handlerton *hton,
static int binlog_commit(handlerton *hton, THD *thd, bool all);
static int binlog_rollback(handlerton *hton, THD *thd, bool all);
static int binlog_prepare(handlerton *hton, THD *thd, bool all);
+static int binlog_xa_recover(handlerton *hton, XID *xid_list, uint len);
+static int binlog_commit_by_xid(handlerton *hton, XID *xid);
+static int binlog_rollback_by_xid(handlerton *hton, XID *xid);
static int binlog_start_consistent_snapshot(handlerton *hton, THD *thd);
static const LEX_CSTRING write_error_msg=
@@ -1688,6 +1691,9 @@ int binlog_init(void *p)
binlog_hton->commit= binlog_commit;
binlog_hton->rollback= binlog_rollback;
binlog_hton->prepare= binlog_prepare;
+ binlog_hton->recover= binlog_xa_recover;
+ binlog_hton->commit_by_xid = binlog_commit_by_xid;
+ binlog_hton->rollback_by_xid = binlog_rollback_by_xid;
binlog_hton->start_consistent_snapshot= binlog_start_consistent_snapshot;
binlog_hton->flags= HTON_NOT_USER_SELECTABLE | HTON_HIDDEN;
return 0;
@@ -1883,23 +1889,16 @@ static inline int
binlog_commit_flush_xid_caches(THD *thd, binlog_cache_mngr *cache_mngr,
bool all, my_xid xid)
{
- if (xid)
- {
- Xid_log_event end_evt(thd, xid, TRUE);
- return (binlog_flush_cache(thd, cache_mngr, &end_evt, all, TRUE, TRUE));
- }
- else
+ /* Mask XA COMMIT ... ONE PHASE as plain BEGIN ... COMMIT */
+ if (!xid)
{
- /*
- Empty xid occurs in XA COMMIT ... ONE PHASE.
- In this case, we do not have a MySQL xid for the transaction, and the
- external XA transaction coordinator will have to handle recovery if
- needed. So we end the transaction with a plain COMMIT query event.
- */
- Query_log_event end_evt(thd, STRING_WITH_LEN("COMMIT"),
- TRUE, TRUE, TRUE, 0);
- return (binlog_flush_cache(thd, cache_mngr, &end_evt, all, TRUE, TRUE));
+ DBUG_ASSERT(thd->transaction.xid_state.xa_state == XA_IDLE &&
+ thd->lex->xa_opt == XA_ONE_PHASE);
+ xid= thd->query_id;
}
+
+ Xid_log_event end_evt(thd, xid, TRUE);
+ return (binlog_flush_cache(thd, cache_mngr, &end_evt, all, TRUE, TRUE));
}
/**
@@ -1961,11 +1960,77 @@ static int binlog_prepare(handlerton *hton, THD *thd, bool all)
do nothing.
just pretend we can do 2pc, so that MySQL won't
switch to 1pc.
- real work will be done in MYSQL_BIN_LOG::log_and_order()
+ real work is done in MYSQL_BIN_LOG::log_and_order()
*/
return 0;
}
+
+static int serialize_xid(XID *xid, char *buf)
+{
+ size_t size;
+ buf[0]= '\'';
+ memcpy(buf+1, xid->data, xid->gtrid_length);
+ size= xid->gtrid_length + 2;
+ buf[size-1]= '\'';
+ if (xid->bqual_length == 0 && xid->formatID == 1)
+ return size;
+
+ memcpy(buf+size, ", '", 3);
+ memcpy(buf+size+3, xid->data+xid->gtrid_length, xid->bqual_length);
+ size+= 3 + xid->bqual_length;
+ buf[size]= '\'';
+ size++;
+ if (xid->formatID != 1)
+ size+= sprintf(buf+size, ", %ld", xid->formatID);
+ return size;
+}
+
+
+static int binlog_xa_recover(handlerton *hton __attribute__((unused)),
+ XID *xid_list __attribute__((unused)),
+ uint len __attribute__((unused)))
+{
+ /* Does nothing. */
+ return 0;
+}
+
+
+static int binlog_commit_by_xid(handlerton *hton, XID *xid)
+{
+ THD *thd= current_thd;
+ const size_t xc_len= sizeof("XA COMMIT ") - 1; // do not count trailing 0
+ char buf[xc_len + xid_t::ser_buf_size];
+ size_t buflen;
+ binlog_cache_mngr *const cache_mngr= thd->binlog_setup_trx_data();
+
+ DBUG_ASSERT(thd->lex->sql_command == SQLCOM_XA_COMMIT);
+
+ if (!cache_mngr)
+ return 1;
+
+ memcpy(buf, "XA COMMIT ", xc_len);
+ buflen= xc_len + serialize_xid(xid, buf+xc_len);
+ Query_log_event qe(thd, buf, buflen, TRUE, FALSE, FALSE, 0);
+ return binlog_flush_cache(thd, cache_mngr, &qe, TRUE, TRUE, TRUE);
+}
+
+
+static int binlog_rollback_by_xid(handlerton *hton, XID *xid)
+{
+ THD *thd= current_thd;
+ const size_t xr_len= sizeof("XA ROLLBACK ") - 1; // do not count trailing 0
+ char buf[xr_len + xid_t::ser_buf_size];
+ size_t buflen;
+
+ DBUG_ASSERT(thd->lex->sql_command == SQLCOM_XA_ROLLBACK);
+ memcpy(buf, "XA ROLLBACK ", xr_len);
+ buflen= xr_len + serialize_xid(xid, buf+xr_len);
+ Query_log_event qe(thd, buf, buflen, FALSE, TRUE, TRUE, 0);
+ return mysql_bin_log.write_event(&qe);
+}
+
+
/*
We flush the cache wrapped in a beging/rollback if:
. aborting a single or multi-statement transaction and;
@@ -9809,6 +9874,24 @@ int TC_LOG_BINLOG::unlog(ulong cookie, my_xid xid)
DBUG_RETURN(BINLOG_COOKIE_GET_ERROR_FLAG(cookie));
}
+
+int TC_LOG_BINLOG::log_prepare(THD *thd, bool all)
+{
+ XID *xid= &thd->transaction.xid_state.xid;
+ XA_prepare_log_event end_evt(thd, xid, FALSE);
+ binlog_cache_mngr *cache_mngr= thd->binlog_setup_trx_data();
+
+ if (!cache_mngr)
+ {
+ WSREP_DEBUG("Skipping empty log_xid: %s", thd->query());
+ return 0;
+ }
+
+ cache_mngr->using_xa= FALSE;
+ return (binlog_flush_cache(thd, cache_mngr, &end_evt, all, TRUE, TRUE));
+}
+
+
void
TC_LOG_BINLOG::commit_checkpoint_notify(void *cookie)
{
diff --git a/sql/log.h b/sql/log.h
index 7dfdb36..92fdf95 100644
--- a/sql/log.h
+++ b/sql/log.h
@@ -61,6 +61,7 @@ class TC_LOG
bool need_prepare_ordered,
bool need_commit_ordered) = 0;
virtual int unlog(ulong cookie, my_xid xid)=0;
+ virtual int log_prepare(THD *thd, bool all)= 0;
virtual void commit_checkpoint_notify(void *cookie)= 0;
protected:
@@ -115,6 +116,10 @@ class TC_LOG_DUMMY: public TC_LOG // use it to disable the logging
return 1;
}
int unlog(ulong cookie, my_xid xid) { return 0; }
+ int log_prepare(THD *thd, bool all)
+ {
+ return 0;
+ }
void commit_checkpoint_notify(void *cookie) { DBUG_ASSERT(0); };
};
@@ -198,6 +203,10 @@ class TC_LOG_MMAP: public TC_LOG
int log_and_order(THD *thd, my_xid xid, bool all,
bool need_prepare_ordered, bool need_commit_ordered);
int unlog(ulong cookie, my_xid xid);
+ int log_prepare(THD *thd, bool all)
+ {
+ return 0;
+ }
void commit_checkpoint_notify(void *cookie);
int recover();
@@ -698,6 +707,7 @@ class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG
int log_and_order(THD *thd, my_xid xid, bool all,
bool need_prepare_ordered, bool need_commit_ordered);
int unlog(ulong cookie, my_xid xid);
+ int log_prepare(THD *thd, bool all);
void commit_checkpoint_notify(void *cookie);
int recover(LOG_INFO *linfo, const char *last_log_name, IO_CACHE *first_log,
Format_description_log_event *fdle, bool do_xa);
diff --git a/sql/log_event.cc b/sql/log_event.cc
index 7a0d0be..354c5f3 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -2139,6 +2139,9 @@ Log_event* Log_event::read_log_event(const char* buf, uint event_len,
case XID_EVENT:
ev = new Xid_log_event(buf, fdle);
break;
+ case XA_PREPARE_LOG_EVENT:
+ ev = new XA_prepare_log_event(buf, fdle);
+ break;
case RAND_EVENT:
ev = new Rand_log_event(buf, fdle);
break;
@@ -2190,7 +2193,6 @@ Log_event* Log_event::read_log_event(const char* buf, uint event_len,
case PREVIOUS_GTIDS_LOG_EVENT:
case TRANSACTION_CONTEXT_EVENT:
case VIEW_CHANGE_EVENT:
- case XA_PREPARE_LOG_EVENT:
ev= new Ignorable_log_event(buf, fdle,
get_type_str((Log_event_type) event_type));
break;
@@ -6222,6 +6224,7 @@ Format_description_log_event(uint8 binlog_ver, const char* server_ver)
post_header_len[USER_VAR_EVENT-1]= USER_VAR_HEADER_LEN;
post_header_len[FORMAT_DESCRIPTION_EVENT-1]= FORMAT_DESCRIPTION_HEADER_LEN;
post_header_len[XID_EVENT-1]= XID_HEADER_LEN;
+ post_header_len[XA_PREPARE_LOG_EVENT-1]= XA_PREPARE_HEADER_LEN;
post_header_len[BEGIN_LOAD_QUERY_EVENT-1]= BEGIN_LOAD_QUERY_HEADER_LEN;
post_header_len[EXECUTE_LOAD_QUERY_EVENT-1]= EXECUTE_LOAD_QUERY_HEADER_LEN;
/*
@@ -7874,7 +7877,7 @@ Gtid_log_event::Gtid_log_event(const char *buf, uint event_len,
buf+= 8;
domain_id= uint4korr(buf);
buf+= 4;
- flags2= *buf;
+ flags2= *(buf++);
if (flags2 & FL_GROUP_COMMIT_ID)
{
if (event_len < (uint)header_size + GTID_HEADER_LEN + 2)
@@ -7882,8 +7885,22 @@ Gtid_log_event::Gtid_log_event(const char *buf, uint event_len,
seq_no= 0; // So is_valid() returns false
return;
}
- ++buf;
commit_id= uint8korr(buf);
+ buf+= 8;
+ }
+ if (flags2 & FL_XA_TRANSACTION)
+ {
+ xid.formatID= (long) buf[0];
+ xid.gtrid_length= (long) buf[1];
+ xid.bqual_length= (long) buf[2];
+
+ buf+= 3;
+ if (xid.formatID >= 0)
+ {
+ long data_length= xid.bqual_length + xid.gtrid_length;
+ memcpy(xid.data, buf, data_length);
+ buf+= data_length;
+ }
}
}
@@ -7914,6 +7931,12 @@ Gtid_log_event::Gtid_log_event(THD *thd_arg, uint64 seq_no_arg,
/* Preserve any DDL or WAITED flag in the slave's binlog. */
if (thd_arg->rgi_slave)
flags2|= (thd_arg->rgi_slave->gtid_ev_flags2 & (FL_DDL|FL_WAITED));
+ if (thd->transaction.xid_state.xa_state == XA_IDLE &&
+ thd->lex->xa_opt != XA_ONE_PHASE)
+ {
+ flags2|= FL_XA_TRANSACTION;
+ xid= thd->transaction.xid_state.xid;
+ }
}
@@ -7956,7 +7979,7 @@ Gtid_log_event::peek(const char *event_start, size_t event_len,
bool
Gtid_log_event::write()
{
- uchar buf[GTID_HEADER_LEN+2];
+ uchar buf[GTID_HEADER_LEN+2+sizeof(XID)];
size_t write_len;
int8store(buf, seq_no);
@@ -7968,8 +7991,25 @@ Gtid_log_event::write()
write_len= GTID_HEADER_LEN + 2;
}
else
+ write_len= 13;
+
+ if (flags2 & FL_XA_TRANSACTION)
+ {
+ buf[write_len]= (uchar) ((char) xid.formatID);
+ buf[write_len+1]= (uchar) xid.gtrid_length;
+ buf[write_len+2]= (uchar) xid.bqual_length;
+ write_len+= 3;
+ if (xid.formatID >= 0)
+ {
+ long data_length= xid.bqual_length + xid.gtrid_length;
+ memcpy(buf+write_len, xid.data, data_length);
+ write_len+= data_length;
+ }
+ }
+
+ if (write_len < GTID_HEADER_LEN)
{
- bzero(buf+13, GTID_HEADER_LEN-13);
+ bzero(buf+write_len, GTID_HEADER_LEN-write_len);
write_len= GTID_HEADER_LEN;
}
return write_header(write_len) ||
@@ -8012,7 +8052,7 @@ Gtid_log_event::make_compatible_event(String *packet, bool *need_dummy_event,
void
Gtid_log_event::pack_info(Protocol *protocol)
{
- char buf[6+5+10+1+10+1+20+1+4+20+1];
+ char buf[6+5+10+1+10+1+20+1+4+20+1+5+128];
char *p;
p = strmov(buf, (flags2 & FL_STANDALONE ? "GTID " : "BEGIN GTID "));
p= longlong10_to_str(domain_id, p, 10);
@@ -8026,6 +8066,12 @@ Gtid_log_event::pack_info(Protocol *protocol)
p= longlong10_to_str(commit_id, p, 10);
}
+ if (flags2 & FL_XA_TRANSACTION)
+ {
+ p= strmov(p, " XID :");
+ p= strnmov(p, xid.data, xid.bqual_length + xid.gtrid_length);
+ }
+
protocol->store(buf, p-buf, &my_charset_bin);
}
@@ -8079,11 +8125,25 @@ Gtid_log_event::do_apply_event(rpl_group_info *rgi)
thd->lex->sql_command= SQLCOM_BEGIN;
thd->is_slave_error= 0;
status_var_increment(thd->status_var.com_stat[thd->lex->sql_command]);
- if (trans_begin(thd, 0))
+ if (flags2 & FL_XA_TRANSACTION)
{
- DBUG_PRINT("error", ("trans_begin() failed"));
- thd->is_slave_error= 1;
+ thd->lex->xid= &xid;
+ thd->lex->xa_opt= XA_NONE;
+ if (trans_xa_start(thd))
+ {
+ DBUG_PRINT("error", ("trans_xa_start() failed"));
+ thd->is_slave_error= 1;
+ }
+ }
+ else
+ {
+ if (trans_begin(thd, 0))
+ {
+ DBUG_PRINT("error", ("trans_begin() failed"));
+ thd->is_slave_error= 1;
+ }
}
+
thd->update_stats();
if (likely(!thd->is_slave_error))
@@ -8202,9 +8262,29 @@ Gtid_log_event::print(FILE *file, PRINT_EVENT_INFO *print_event_info)
buf, print_event_info->delimiter))
goto err;
}
- if (!(flags2 & FL_STANDALONE))
- if (my_b_printf(&cache, is_flashback ? "COMMIT\n%s\n" : "BEGIN\n%s\n", print_event_info->delimiter))
+ if ((flags2 & FL_XA_TRANSACTION) && !is_flashback)
+ {
+ my_b_write_string(&cache, "XA START '");
+ my_b_write(&cache, (uchar *) xid.data, xid.gtrid_length);
+ my_b_write_string(&cache, "'");
+ if (xid.bqual_length > 0 || xid.formatID != 1)
+ {
+ my_b_write_string(&cache, ", '");
+ my_b_write(&cache, (uchar *) xid.data+xid.gtrid_length, xid.bqual_length);
+ my_b_write_string(&cache, "'");
+ if (xid.formatID != 1)
+ if (my_b_printf(&cache, ", %d", xid.formatID))
+ goto err;
+ }
+ if (my_b_printf(&cache, "%s\n", print_event_info->delimiter))
+ goto err;
+ }
+ else if (!(flags2 & FL_STANDALONE))
+ {
+ if (my_b_printf(&cache, is_flashback ? "COMMIT\n%s\n" : "BEGIN\n%s\n",
+ print_event_info->delimiter))
goto err;
+ }
return cache.flush_data();
err:
@@ -9003,6 +9083,300 @@ Xid_log_event::do_shall_skip(rpl_group_info *rgi)
#endif /* !MYSQL_CLIENT */
+/**
+ Function serializes XID which is characterized by by four last arguments
+ of the function.
+ Serialized XID is presented in valid hex format and is returned to
+ the caller in a buffer pointed by the first argument.
+ The buffer size provived by the caller must be not less than
+ 8 + 2 * XIDDATASIZE + 4 * sizeof(XID::formatID) + 1, see
+ XID::serialize_xid() that is a caller and plugin.h for XID declaration.
+
+ @param buf pointer to a buffer allocated for storing serialized data
+
+ @return the value of the buffer pointer
+*/
+
+char *XA_prepare_log_event::event_xid_t::serialize(char *buf) const
+{
+ int i;
+ char *c= buf;
+ /*
+ Build a string like following pattern:
+ X'hex11hex12...hex1m',X'hex21hex22...hex2n',11
+ and store it into buf.
+ Here hex1i and hex2k are hexadecimals representing XID's internal
+ raw bytes (1 <= i <= m, 1 <= k <= n), and `m' and `n' even numbers
+ half of which corresponding to the lengths of XID's components.
+ */
+ c[0]= 'X';
+ c[1]= '\'';
+ c+= 2;
+ for (i= 0; i < gtrid_length; i++)
+ {
+ c[0]=_dig_vec_lower[((uchar*) data)[i] >> 4];
+ c[1]=_dig_vec_lower[((uchar*) data)[i] & 0x0f];
+ c+= 2;
+ }
+ c[0]= '\'';
+ c[1]= ',';
+ c[2]= 'X';
+ c[3]= '\'';
+ c+= 4;
+
+ for (; i < gtrid_length + bqual_length; i++)
+ {
+ c[0]=_dig_vec_lower[((uchar*) data)[i] >> 4];
+ c[1]=_dig_vec_lower[((uchar*) data)[i] & 0x0f];
+ c+= 2;
+ }
+ c[0]= '\'';
+ sprintf(c+1, ",%lu", formatID);
+
+ return buf;
+}
+
+
+/**************************************************************************
+ XA_prepare_log_event methods
+**************************************************************************/
+/**
+ @note
+ It's ok not to use int8store here,
+ as long as xid_t::set(ulonglong) and
+ xid_t::get_n_xid doesn't do it either.
+ We don't care about actual values of xids as long as
+ identical numbers compare identically
+*/
+
+XA_prepare_log_event::
+XA_prepare_log_event(const char* buf,
+ const Format_description_log_event *description_event)
+ :Log_event(buf, description_event)
+{
+ uint32 temp= 0;
+ uint8 temp_byte;
+
+ buf+= description_event->common_header_len +
+ description_event->post_header_len[XA_PREPARE_LOG_EVENT-1];
+ memcpy(&temp_byte, buf, 1);
+ one_phase= (bool) temp_byte;
+ buf += sizeof(temp_byte);
+ memcpy(&temp, buf, sizeof(temp));
+ m_xid.formatID= le32toh(temp);
+ buf += sizeof(temp);
+ memcpy(&temp, buf, sizeof(temp));
+ m_xid.gtrid_length= le32toh(temp);
+ buf += sizeof(temp);
+ memcpy(&temp, buf, sizeof(temp));
+ m_xid.bqual_length= le32toh(temp);
+ buf += sizeof(temp);
+ memcpy(m_xid.data, buf, m_xid.gtrid_length + m_xid.bqual_length);
+
+ xid= NULL;
+}
+
+
+#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
+void XA_prepare_log_event::pack_info(Protocol *protocol)
+{
+ char buf[ser_buf_size];
+ char query[sizeof("XA COMMIT ONE PHASE") + 1 + sizeof(buf)];
+
+ /* RHS of the following assert is unknown to client sources */
+ compile_time_assert(ser_buf_size == XID::ser_buf_size);
+ m_xid.serialize(buf);
+ sprintf(query,
+ (one_phase ? "XA COMMIT %s ONE PHASE" : "XA PREPARE %s"),
+ buf);
+
+ protocol->store(query, strlen(query), &my_charset_bin);
+}
+#endif
+
+
+#ifndef MYSQL_CLIENT
+bool XA_prepare_log_event::write()
+{
+ uchar data[1 + 4 + 4 + 4];
+ uint8 one_phase_byte= one_phase;
+
+ data[0]= one_phase;
+ int4store(data+1, static_cast<XID*>(xid)->formatID);
+ int4store(data+(1+4), static_cast<XID*>(xid)->gtrid_length);
+ int4store(data+(1+4+4), static_cast<XID*>(xid)->bqual_length);
+
+ DBUG_ASSERT(xid_bufs_size == sizeof(data) - 1);
+
+ return write_header(sizeof(one_phase_byte) + xid_bufs_size +
+ static_cast<XID*>(xid)->gtrid_length +
+ static_cast<XID*>(xid)->bqual_length) ||
+ write_data(data, sizeof(data)) ||
+ write_data((uchar*) static_cast<XID*>(xid)->data,
+ static_cast<XID*>(xid)->gtrid_length +
+ static_cast<XID*>(xid)->bqual_length) ||
+ write_footer();
+}
+#endif
+
+
+#ifdef MYSQL_CLIENT
+bool XA_prepare_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
+{
+ Write_on_release_cache cache(&print_event_info->head_cache, file,
+ Write_on_release_cache::FLUSH_F, this);
+ char buf[ser_buf_size];
+
+ m_xid.serialize(buf);
+
+ if (!print_event_info->short_form)
+ {
+ print_header(&cache, print_event_info, FALSE);
+ if (my_b_printf(&cache, "\tXID = %s\n", buf))
+ goto error;
+ }
+
+ if (my_b_printf(&cache, "XA END %s\n%s\n",
+ buf, print_event_info->delimiter) ||
+ my_b_printf(&cache, "XA PREPARE %s\n%s\n",
+ buf, print_event_info->delimiter))
+ goto error;
+
+ return cache.flush_data();
+error:
+ return TRUE;
+}
+#endif /* MYSQL_CLIENT */
+
+
+#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
+int XA_prepare_log_event::do_apply_event(rpl_group_info *rgi)
+{
+ bool res;
+ int err;
+ rpl_gtid gtid;
+ uint64 sub_id= 0;
+ Relay_log_info const *rli= rgi->rli;
+ xid_t xid;
+ void *hton= NULL;
+
+ /*
+ XID_EVENT works like a COMMIT statement. And it also updates the
+ mysql.gtid_slave_pos table with the GTID of the current transaction.
+
+ Therefore, it acts much like a normal SQL statement, so we need to do
+ THD::reset_for_next_command() as if starting a new statement.
+ */
+ thd->reset_for_next_command();
+ /*
+ Record any GTID in the same transaction, so slave state is transactionally
+ consistent.
+ */
+#ifdef WITH_WSREP
+ thd->wsrep_affected_rows= 0;
+#endif
+
+ if (rgi->gtid_pending)
+ {
+ xa_states c_state= thd->transaction.xid_state.xa_state;
+ sub_id= rgi->gtid_sub_id;
+ rgi->gtid_pending= false;
+
+ gtid= rgi->current_gtid;
+
+ thd->transaction.xid_state.xa_state= XA_ACTIVE;
+ err= rpl_global_gtid_slave_state->record_gtid(thd, >id, sub_id, true,
+ false, &hton);
+ thd->transaction.xid_state.xa_state= c_state;
+ if (err)
+ {
+ int ec= thd->get_stmt_da()->sql_errno();
+ /*
+ Do not report an error if this is really a kill due to a deadlock.
+ In this case, the transaction will be re-tried instead.
+ */
+ if (!is_parallel_retry_error(rgi, ec))
+ rli->report(ERROR_LEVEL, ER_CANNOT_UPDATE_GTID_STATE, rgi->gtid_info(),
+ "Error during XID COMMIT: failed to update GTID state in "
+ "%s.%s: %d: %s",
+ "mysql", rpl_gtid_slave_state_table_name.str, ec,
+ thd->get_stmt_da()->message());
+ thd->is_slave_error= 1;
+ return err;
+ }
+
+ DBUG_EXECUTE_IF("gtid_fail_after_record_gtid",
+ { my_error(ER_ERROR_DURING_COMMIT, MYF(0), HA_ERR_WRONG_COMMAND);
+ thd->is_slave_error= 1;
+ return 1;
+ });
+ }
+ /* For a slave XA_prepare_log_event is COMMIT */
+ general_log_print(thd, COM_QUERY,
+ "COMMIT /* implicit, from Xid_log_event */");
+ thd->variables.option_bits&= ~OPTION_GTID_BEGIN;
+
+ xid.set(m_xid.formatID,
+ m_xid.data, m_xid.gtrid_length,
+ m_xid.data + m_xid.gtrid_length, m_xid.bqual_length);
+
+ thd->lex->xid= &xid;
+ if (trans_xa_end(thd))
+ return 1;
+
+ if (!one_phase)
+ {
+ res= trans_xa_prepare(thd);
+ }
+ else
+ {
+ res= trans_xa_commit(thd);
+ thd->mdl_context.release_transactional_locks();
+ }
+
+
+ if (!res && sub_id)
+ rpl_global_gtid_slave_state->update_state_hash(sub_id, >id, hton, rgi);
+
+ /*
+ Increment the global status commit count variable
+ */
+ status_var_increment(thd->status_var.com_stat[SQLCOM_COMMIT]);
+
+ return res;
+}
+
+
+Log_event::enum_skip_reason
+XA_prepare_log_event::do_shall_skip(rpl_group_info *rgi)
+{
+ DBUG_ENTER("Xid_log_event::do_shall_skip");
+ if (rgi->rli->slave_skip_counter > 0)
+ {
+ DBUG_ASSERT(!rgi->rli->get_flag(Relay_log_info::IN_TRANSACTION));
+ thd->variables.option_bits&= ~(OPTION_BEGIN | OPTION_GTID_BEGIN);
+ DBUG_RETURN(Log_event::EVENT_SKIP_COUNT);
+ }
+#ifdef WITH_WSREP
+ else if (wsrep_mysql_replication_bundle && WSREP_ON &&
+ opt_slave_domain_parallel_threads == 0)
+ {
+ if (++thd->wsrep_mysql_replicated < (int)wsrep_mysql_replication_bundle)
+ {
+ WSREP_DEBUG("skipping wsrep commit %d", thd->wsrep_mysql_replicated);
+ DBUG_RETURN(Log_event::EVENT_SKIP_IGNORE);
+ }
+ else
+ {
+ thd->wsrep_mysql_replicated = 0;
+ }
+ }
+#endif
+ DBUG_RETURN(Log_event::do_shall_skip(rgi));
+}
+#endif /* !MYSQL_CLIENT */
+
+
/**************************************************************************
User_var_log_event methods
**************************************************************************/
@@ -14789,7 +15163,6 @@ bool event_that_should_be_ignored(const char *buf)
event_type == PREVIOUS_GTIDS_LOG_EVENT ||
event_type == TRANSACTION_CONTEXT_EVENT ||
event_type == VIEW_CHANGE_EVENT ||
- event_type == XA_PREPARE_LOG_EVENT ||
(uint2korr(buf + FLAGS_OFFSET) & LOG_EVENT_IGNORABLE_F))
return 1;
return 0;
diff --git a/sql/log_event.h b/sql/log_event.h
index 38a40c9..b5c48c9 100644
--- a/sql/log_event.h
+++ b/sql/log_event.h
@@ -217,6 +217,7 @@ class String;
#define GTID_HEADER_LEN 19
#define GTID_LIST_HEADER_LEN 4
#define START_ENCRYPTION_HEADER_LEN 0
+#define XA_PREPARE_HEADER_LEN 0
/*
Max number of possible extra bytes in a replication event compared to a
@@ -3064,6 +3065,79 @@ class Xid_log_event: public Log_event
#endif
};
+
+/**
+ @class XA_prepare_log_event
+
+ Similar to Xid_log_event except that
+ - it is specific to XA transaction
+ - it carries out the prepare logics rather than the final committing
+ when @c one_phase member is off.
+ From the groupping perspective the event finalizes the current "prepare" group
+ started with XA START Query-log-event.
+ When @c one_phase is false Commit of Rollback for XA transaction are
+ logged separately to the prepare-group events so being a groups of
+ their own.
+*/
+
+class XA_prepare_log_event: public Log_event
+{
+protected:
+ /* The event_xid_t members were copied from handler.h */
+ struct event_xid_t
+ {
+ long formatID;
+ long gtrid_length;
+ long bqual_length;
+ char data[MYSQL_XIDDATASIZE]; // not \0-terminated !
+ char *serialize(char *buf) const;
+ };
+
+ /* size of serialization buffer is explained in $MYSQL/sql/xa.h. */
+ static const uint ser_buf_size=
+ 8 + 2 * MYSQL_XIDDATASIZE + 4 * sizeof(long) + 1;
+
+ /* Total size of buffers to hold serialized members of XID struct */
+ static const int xid_bufs_size= 12;
+ event_xid_t m_xid;
+ void *xid;
+ bool one_phase;
+
+public:
+#ifdef MYSQL_SERVER
+ XA_prepare_log_event(THD* thd_arg, XID *xid_arg, bool one_phase_arg):
+ Log_event(thd_arg, 0, TRUE), xid(xid_arg), one_phase(one_phase_arg)
+ {
+ cache_type= Log_event::EVENT_NO_CACHE;
+ }
+#ifdef HAVE_REPLICATION
+ void pack_info(Protocol* protocol);
+#endif /* HAVE_REPLICATION */
+#else
+ bool print(FILE* file, PRINT_EVENT_INFO* print_event_info);
+#endif
+ XA_prepare_log_event(const char* buf,
+ const Format_description_log_event *description_event);
+ ~XA_prepare_log_event() {}
+ Log_event_type get_type_code() { return XA_PREPARE_LOG_EVENT; }
+ int get_data_size()
+ {
+ return xid_bufs_size + m_xid.gtrid_length + m_xid.bqual_length;
+ }
+
+#ifdef MYSQL_SERVER
+ bool write();
+#endif
+ bool is_valid() const { return 1; }
+
+private:
+#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
+ virtual int do_apply_event(rpl_group_info *rgi);
+ enum_skip_reason do_shall_skip(rpl_group_info *rgi);
+#endif
+};
+
+
/**
@class User_var_log_event
@@ -3376,6 +3450,11 @@ class Gtid_log_event: public Log_event
uint64 seq_no;
uint64 commit_id;
uint32 domain_id;
+#ifdef MYSQL_SERVER
+ XID xid;
+#else
+ struct st_mysql_xid xid;
+#endif
uchar flags2;
/* Flags2. */
@@ -3404,6 +3483,8 @@ class Gtid_log_event: public Log_event
static const uchar FL_WAITED= 16;
/* FL_DDL is set for event group containing DDL. */
static const uchar FL_DDL= 32;
+ /* FL_XA_TRANSACTION is set for XA transaction. */
+ static const uchar FL_XA_TRANSACTION= 64;
#ifdef MYSQL_SERVER
Gtid_log_event(THD *thd_arg, uint64 seq_no, uint32 domain_id, bool standalone,
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index fa2f866..cc75da9 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -1461,12 +1461,19 @@ void THD::cleanup(void)
DBUG_ASSERT(cleanup_done == 0);
set_killed(KILL_CONNECTION);
-#ifdef ENABLE_WHEN_BINLOG_WILL_BE_ABLE_TO_PREPARE
if (transaction.xid_state.xa_state == XA_PREPARED)
{
-#error xid_state in the cache should be replaced by the allocated value
+ trans_detach(this);
+ transaction.xid_state.xa_state= XA_NOTR;
+ transaction.xid_state.rm_error= 0;
+ }
+ else
+ {
+ transaction.xid_state.xa_state= XA_NOTR;
+ transaction.xid_state.rm_error= 0;
+ trans_rollback(this);
+ xid_cache_delete(this, &transaction.xid_state);
}
-#endif
mysql_ha_cleanup(this);
locked_tables_list.unlock_locked_tables(this);
@@ -1474,11 +1481,6 @@ void THD::cleanup(void)
delete_dynamic(&user_var_events);
close_temporary_tables();
- transaction.xid_state.xa_state= XA_NOTR;
- transaction.xid_state.rm_error= 0;
- trans_rollback(this);
- xid_cache_delete(this, &transaction.xid_state);
-
DBUG_ASSERT(open_tables == NULL);
/*
If the thread was in the middle of an ongoing transaction (rolled
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 69fabee..76befcb 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -1255,6 +1255,18 @@ typedef struct st_xid_state {
/* Error reported by the Resource Manager (RM) to the Transaction Manager. */
uint rm_error;
XID_cache_element *xid_cache_element;
+ /*
+ Binary logging status.
+ It is set to TRUE at XA PREPARE if the transaction was written
+ to the binlog.
+ Naturally FALSE means the transaction was not written to
+ the binlog. Happens if the trnasaction did not modify anything
+ or binlogging was turned off. In that case we shouldn't binlog
+ the consequent XA COMMIT/ROLLBACK.
+ The recovered transaction after server restart sets it to TRUE always.
+ That can cause inconsistencies (shoud be fixed?).
+ */
+ bool is_binlogged;
/**
Check that XA transaction has an uncommitted work. Report an error
@@ -1278,6 +1290,12 @@ typedef struct st_xid_state {
}
return false;
}
+
+ void reset()
+ {
+ xid.null();
+ is_binlogged= FALSE;
+ }
} XID_STATE;
void xid_cache_init(void);
@@ -2603,7 +2621,7 @@ class THD :public Statement,
then.
*/
if (!xid_state.rm_error)
- xid_state.xid.null();
+ xid_state.reset();
free_root(&mem_root,MYF(MY_KEEP_PREALLOC));
DBUG_VOID_RETURN;
}
diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc
index b48070b..3e4a067 100644
--- a/sql/sql_connect.cc
+++ b/sql/sql_connect.cc
@@ -1414,6 +1414,7 @@ void do_handle_one_connection(CONNECT *connect)
#endif
end_thread:
close_connection(thd);
+ thd->get_stmt_da()->reset_diagnostics_area();
if (thd->userstat_running)
update_global_user_stats(thd, create_user, time(NULL));
diff --git a/sql/transaction.cc b/sql/transaction.cc
index 13614d3..64533d7 100644
--- a/sql/transaction.cc
+++ b/sql/transaction.cc
@@ -790,6 +790,44 @@ bool trans_release_savepoint(THD *thd, LEX_CSTRING name)
/**
+ Detach the current XA transaction;
+
+ @param thd Current thread
+
+ @retval FALSE Success
+ @retval TRUE Failure
+*/
+
+bool trans_detach(THD *thd)
+{
+ XID_STATE *xid_s= &thd->transaction.xid_state;
+ Ha_trx_info *ha_info, *ha_info_next;
+
+ DBUG_ENTER("trans_detach");
+
+// DBUG_ASSERT(xid_s->xa_state == XA_PREPARED &&
+// xid_cache_search(thd, &xid_s->xid));
+
+ xid_cache_delete(thd, xid_s);
+ if (xid_cache_insert(&xid_s->xid, XA_PREPARED))
+ DBUG_RETURN(TRUE);
+
+ for (ha_info= thd->transaction.all.ha_list;
+ ha_info;
+ ha_info= ha_info_next)
+ {
+ ha_info_next= ha_info->next();
+ ha_info->reset(); /* keep it conveniently zero-filled */
+ }
+
+ thd->transaction.all.ha_list= 0;
+ thd->transaction.all.no_2pc= 0;
+
+ DBUG_RETURN(FALSE);
+}
+
+
+/**
Starts an XA transaction with the given xid value.
@param thd Current thread
@@ -928,6 +966,12 @@ bool trans_xa_commit(THD *thd)
res= !xs;
if (res)
my_error(ER_XAER_NOTA, MYF(0));
+ else if (thd->in_multi_stmt_transaction_mode())
+ {
+ my_error(ER_XAER_RMFAIL, MYF(0),
+ xa_state_names[thd->transaction.xid_state.xa_state]);
+ res= TRUE;
+ }
else
{
res= xa_trans_rolled_back(xs);
@@ -978,8 +1022,16 @@ bool trans_xa_commit(THD *thd)
{
DEBUG_SYNC(thd, "trans_xa_commit_after_acquire_commit_lock");
- res= MY_TEST(ha_commit_one_phase(thd, 1));
- if (res)
+ if(WSREP_EMULATE_BINLOG(thd) || mysql_bin_log.is_open())
+ {
+ res= thd->binlog_query(THD::THD::STMT_QUERY_TYPE,
+ thd->query(), thd->query_length(),
+ FALSE, FALSE, FALSE, 0);
+ }
+ else
+ res= 0;
+
+ if (res || (res= MY_TEST(ha_commit_one_phase(thd, 1))))
my_error(ER_XAER_RMERR, MYF(0));
}
}
@@ -1044,7 +1096,18 @@ bool trans_xa_rollback(THD *thd)
DBUG_RETURN(TRUE);
}
- res= xa_trans_force_rollback(thd);
+ if(WSREP_EMULATE_BINLOG(thd) || mysql_bin_log.is_open())
+ {
+ res= thd->binlog_query(THD::THD::STMT_QUERY_TYPE,
+ thd->query(), thd->query_length(),
+ FALSE, FALSE, FALSE, 0);
+ }
+ else
+ res= 0;
+
+ res= res || xa_trans_force_rollback(thd);
+ if (res || (res= MY_TEST(xa_trans_force_rollback(thd))))
+ my_error(ER_XAER_RMERR, MYF(0));
thd->variables.option_bits&= ~(OPTION_BEGIN | OPTION_KEEP_LOG);
thd->transaction.all.reset();
diff --git a/sql/transaction.h b/sql/transaction.h
index 7e34693..f228cc6 100644
--- a/sql/transaction.h
+++ b/sql/transaction.h
@@ -29,6 +29,7 @@ bool trans_commit(THD *thd);
bool trans_commit_implicit(THD *thd);
bool trans_rollback(THD *thd);
bool trans_rollback_implicit(THD *thd);
+bool trans_detach(THD *thd);
bool trans_commit_stmt(THD *thd);
bool trans_rollback_stmt(THD *thd);
1
0
[Commits] b428e82: MDEV-7974 backport fix for mysql bug#12161 (XA and binlog).
by holyfoot@askmonty.org 13 Feb '19
by holyfoot@askmonty.org 13 Feb '19
13 Feb '19
revision-id: b428e822da09b2bc82a2447332bb980f93c80262 (mariadb-10.4.1-103-gb428e82)
parent(s): 8aae31cf494678b6253031c627566e50bc666920
committer: Alexey Botchkov
timestamp: 2019-02-14 02:46:57 +0400
message:
MDEV-7974 backport fix for mysql bug#12161 (XA and binlog).
XA transactions now are kept persistent after prepare.
XA_prepare_log_event implamented, and XA tranasctions are logged
as XA transactions.
---
sql/handler.cc | 9 ++
sql/handler.h | 10 ++
sql/log.cc | 115 +++++++++++++---
sql/log.h | 10 ++
sql/log_event.cc | 397 +++++++++++++++++++++++++++++++++++++++++++++++++++--
sql/log_event.h | 81 +++++++++++
sql/sql_class.cc | 18 +--
sql/sql_class.h | 20 ++-
sql/sql_connect.cc | 1 +
sql/transaction.cc | 69 +++++++++-
sql/transaction.h | 1 +
11 files changed, 691 insertions(+), 40 deletions(-)
diff --git a/sql/handler.cc b/sql/handler.cc
index 001055c..3b2a3e0 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -1214,6 +1214,9 @@ static int prepare_or_error(handlerton *ht, THD *thd, bool all)
}
+/*static inline */int
+binlog_commit_flush_xid_caches(THD *thd, binlog_cache_mngr *cache_mngr,
+ bool all, my_xid xid);
/**
@retval
0 ok
@@ -1225,6 +1228,7 @@ int ha_prepare(THD *thd)
int error=0, all=1;
THD_TRANS *trans=all ? &thd->transaction.all : &thd->transaction.stmt;
Ha_trx_info *ha_info= trans->ha_list;
+
DBUG_ENTER("ha_prepare");
if (ha_info)
@@ -1250,6 +1254,11 @@ int ha_prepare(THD *thd)
}
}
+ if (unlikely(tc_log->log_prepare(thd, all)))
+ {
+ ha_rollback_trans(thd, all);
+ error=1;
+ }
}
DBUG_RETURN(error);
diff --git a/sql/handler.h b/sql/handler.h
index fc6246c..613c1c3 100644
--- a/sql/handler.h
+++ b/sql/handler.h
@@ -810,6 +810,16 @@ struct xid_t {
long gtrid_length;
long bqual_length;
char data[XIDDATASIZE]; // not \0-terminated !
+ /*
+ The size of the string containing serialized Xid representation
+ is computed as a sum of
+ eight as the number of formatting symbols (X'',X'',)
+ plus 2 x XIDDATASIZE (2 due to hex format),
+ plus space for decimal digits of XID::formatID,
+ plus one for 0x0.
+ */
+ static const uint ser_buf_size=
+ 8 + 2 * XIDDATASIZE + 4 * sizeof(long) + 1;
xid_t() {} /* Remove gcc warning */
bool eq(struct xid_t *xid)
diff --git a/sql/log.cc b/sql/log.cc
index a56117a..316b871 100644
--- a/sql/log.cc
+++ b/sql/log.cc
@@ -87,6 +87,9 @@ static bool binlog_savepoint_rollback_can_release_mdl(handlerton *hton,
static int binlog_commit(handlerton *hton, THD *thd, bool all);
static int binlog_rollback(handlerton *hton, THD *thd, bool all);
static int binlog_prepare(handlerton *hton, THD *thd, bool all);
+static int binlog_xa_recover(handlerton *hton, XID *xid_list, uint len);
+static int binlog_commit_by_xid(handlerton *hton, XID *xid);
+static int binlog_rollback_by_xid(handlerton *hton, XID *xid);
static int binlog_start_consistent_snapshot(handlerton *hton, THD *thd);
static const LEX_CSTRING write_error_msg=
@@ -1688,6 +1691,9 @@ int binlog_init(void *p)
binlog_hton->commit= binlog_commit;
binlog_hton->rollback= binlog_rollback;
binlog_hton->prepare= binlog_prepare;
+ binlog_hton->recover= binlog_xa_recover;
+ binlog_hton->commit_by_xid = binlog_commit_by_xid;
+ binlog_hton->rollback_by_xid = binlog_rollback_by_xid;
binlog_hton->start_consistent_snapshot= binlog_start_consistent_snapshot;
binlog_hton->flags= HTON_NOT_USER_SELECTABLE | HTON_HIDDEN;
return 0;
@@ -1883,23 +1889,16 @@ static inline int
binlog_commit_flush_xid_caches(THD *thd, binlog_cache_mngr *cache_mngr,
bool all, my_xid xid)
{
- if (xid)
- {
- Xid_log_event end_evt(thd, xid, TRUE);
- return (binlog_flush_cache(thd, cache_mngr, &end_evt, all, TRUE, TRUE));
- }
- else
+ /* Mask XA COMMIT ... ONE PHASE as plain BEGIN ... COMMIT */
+ if (!xid)
{
- /*
- Empty xid occurs in XA COMMIT ... ONE PHASE.
- In this case, we do not have a MySQL xid for the transaction, and the
- external XA transaction coordinator will have to handle recovery if
- needed. So we end the transaction with a plain COMMIT query event.
- */
- Query_log_event end_evt(thd, STRING_WITH_LEN("COMMIT"),
- TRUE, TRUE, TRUE, 0);
- return (binlog_flush_cache(thd, cache_mngr, &end_evt, all, TRUE, TRUE));
+ DBUG_ASSERT(thd->transaction.xid_state.xa_state == XA_IDLE &&
+ thd->lex->xa_opt == XA_ONE_PHASE);
+ xid= thd->query_id;
}
+
+ Xid_log_event end_evt(thd, xid, TRUE);
+ return (binlog_flush_cache(thd, cache_mngr, &end_evt, all, TRUE, TRUE));
}
/**
@@ -1961,11 +1960,77 @@ static int binlog_prepare(handlerton *hton, THD *thd, bool all)
do nothing.
just pretend we can do 2pc, so that MySQL won't
switch to 1pc.
- real work will be done in MYSQL_BIN_LOG::log_and_order()
+ real work is done in MYSQL_BIN_LOG::log_and_order()
*/
return 0;
}
+
+static int serialize_xid(XID *xid, char *buf)
+{
+ size_t size;
+ buf[0]= '\'';
+ memcpy(buf+1, xid->data, xid->gtrid_length);
+ size= xid->gtrid_length + 2;
+ buf[size-1]= '\'';
+ if (xid->bqual_length == 0 && xid->formatID == 1)
+ return size;
+
+ memcpy(buf+size, ", '", 3);
+ memcpy(buf+size+3, xid->data+xid->gtrid_length, xid->bqual_length);
+ size+= 3 + xid->bqual_length;
+ buf[size]= '\'';
+ size++;
+ if (xid->formatID != 1)
+ size+= sprintf(buf+size, ", %ld", xid->formatID);
+ return size;
+}
+
+
+static int binlog_xa_recover(handlerton *hton __attribute__((unused)),
+ XID *xid_list __attribute__((unused)),
+ uint len __attribute__((unused)))
+{
+ /* Does nothing. */
+ return 0;
+}
+
+
+static int binlog_commit_by_xid(handlerton *hton, XID *xid)
+{
+ THD *thd= current_thd;
+ const size_t xc_len= sizeof("XA COMMIT ") - 1; // do not count trailing 0
+ char buf[xc_len + xid_t::ser_buf_size];
+ size_t buflen;
+ binlog_cache_mngr *const cache_mngr= thd->binlog_setup_trx_data();
+
+ DBUG_ASSERT(thd->lex->sql_command == SQLCOM_XA_COMMIT);
+
+ if (!cache_mngr)
+ return 1;
+
+ memcpy(buf, "XA COMMIT ", xc_len);
+ buflen= xc_len + serialize_xid(xid, buf+xc_len);
+ Query_log_event qe(thd, buf, buflen, TRUE, FALSE, FALSE, 0);
+ return binlog_flush_cache(thd, cache_mngr, &qe, TRUE, TRUE, TRUE);
+}
+
+
+static int binlog_rollback_by_xid(handlerton *hton, XID *xid)
+{
+ THD *thd= current_thd;
+ const size_t xr_len= sizeof("XA ROLLBACK ") - 1; // do not count trailing 0
+ char buf[xr_len + xid_t::ser_buf_size];
+ size_t buflen;
+
+ DBUG_ASSERT(thd->lex->sql_command == SQLCOM_XA_ROLLBACK);
+ memcpy(buf, "XA ROLLBACK ", xr_len);
+ buflen= xr_len + serialize_xid(xid, buf+xr_len);
+ Query_log_event qe(thd, buf, buflen, FALSE, TRUE, TRUE, 0);
+ return mysql_bin_log.write_event(&qe);
+}
+
+
/*
We flush the cache wrapped in a beging/rollback if:
. aborting a single or multi-statement transaction and;
@@ -9809,6 +9874,24 @@ int TC_LOG_BINLOG::unlog(ulong cookie, my_xid xid)
DBUG_RETURN(BINLOG_COOKIE_GET_ERROR_FLAG(cookie));
}
+
+int TC_LOG_BINLOG::log_prepare(THD *thd, bool all)
+{
+ XID *xid= &thd->transaction.xid_state.xid;
+ XA_prepare_log_event end_evt(thd, xid, FALSE);
+ binlog_cache_mngr *cache_mngr= thd->binlog_setup_trx_data();
+
+ if (!cache_mngr)
+ {
+ WSREP_DEBUG("Skipping empty log_xid: %s", thd->query());
+ return 0;
+ }
+
+ cache_mngr->using_xa= FALSE;
+ return (binlog_flush_cache(thd, cache_mngr, &end_evt, all, TRUE, TRUE));
+}
+
+
void
TC_LOG_BINLOG::commit_checkpoint_notify(void *cookie)
{
diff --git a/sql/log.h b/sql/log.h
index 7dfdb36..92fdf95 100644
--- a/sql/log.h
+++ b/sql/log.h
@@ -61,6 +61,7 @@ class TC_LOG
bool need_prepare_ordered,
bool need_commit_ordered) = 0;
virtual int unlog(ulong cookie, my_xid xid)=0;
+ virtual int log_prepare(THD *thd, bool all)= 0;
virtual void commit_checkpoint_notify(void *cookie)= 0;
protected:
@@ -115,6 +116,10 @@ class TC_LOG_DUMMY: public TC_LOG // use it to disable the logging
return 1;
}
int unlog(ulong cookie, my_xid xid) { return 0; }
+ int log_prepare(THD *thd, bool all)
+ {
+ return 0;
+ }
void commit_checkpoint_notify(void *cookie) { DBUG_ASSERT(0); };
};
@@ -198,6 +203,10 @@ class TC_LOG_MMAP: public TC_LOG
int log_and_order(THD *thd, my_xid xid, bool all,
bool need_prepare_ordered, bool need_commit_ordered);
int unlog(ulong cookie, my_xid xid);
+ int log_prepare(THD *thd, bool all)
+ {
+ return 0;
+ }
void commit_checkpoint_notify(void *cookie);
int recover();
@@ -698,6 +707,7 @@ class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG
int log_and_order(THD *thd, my_xid xid, bool all,
bool need_prepare_ordered, bool need_commit_ordered);
int unlog(ulong cookie, my_xid xid);
+ int log_prepare(THD *thd, bool all);
void commit_checkpoint_notify(void *cookie);
int recover(LOG_INFO *linfo, const char *last_log_name, IO_CACHE *first_log,
Format_description_log_event *fdle, bool do_xa);
diff --git a/sql/log_event.cc b/sql/log_event.cc
index 7a0d0be..354c5f3 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -2139,6 +2139,9 @@ Log_event* Log_event::read_log_event(const char* buf, uint event_len,
case XID_EVENT:
ev = new Xid_log_event(buf, fdle);
break;
+ case XA_PREPARE_LOG_EVENT:
+ ev = new XA_prepare_log_event(buf, fdle);
+ break;
case RAND_EVENT:
ev = new Rand_log_event(buf, fdle);
break;
@@ -2190,7 +2193,6 @@ Log_event* Log_event::read_log_event(const char* buf, uint event_len,
case PREVIOUS_GTIDS_LOG_EVENT:
case TRANSACTION_CONTEXT_EVENT:
case VIEW_CHANGE_EVENT:
- case XA_PREPARE_LOG_EVENT:
ev= new Ignorable_log_event(buf, fdle,
get_type_str((Log_event_type) event_type));
break;
@@ -6222,6 +6224,7 @@ Format_description_log_event(uint8 binlog_ver, const char* server_ver)
post_header_len[USER_VAR_EVENT-1]= USER_VAR_HEADER_LEN;
post_header_len[FORMAT_DESCRIPTION_EVENT-1]= FORMAT_DESCRIPTION_HEADER_LEN;
post_header_len[XID_EVENT-1]= XID_HEADER_LEN;
+ post_header_len[XA_PREPARE_LOG_EVENT-1]= XA_PREPARE_HEADER_LEN;
post_header_len[BEGIN_LOAD_QUERY_EVENT-1]= BEGIN_LOAD_QUERY_HEADER_LEN;
post_header_len[EXECUTE_LOAD_QUERY_EVENT-1]= EXECUTE_LOAD_QUERY_HEADER_LEN;
/*
@@ -7874,7 +7877,7 @@ Gtid_log_event::Gtid_log_event(const char *buf, uint event_len,
buf+= 8;
domain_id= uint4korr(buf);
buf+= 4;
- flags2= *buf;
+ flags2= *(buf++);
if (flags2 & FL_GROUP_COMMIT_ID)
{
if (event_len < (uint)header_size + GTID_HEADER_LEN + 2)
@@ -7882,8 +7885,22 @@ Gtid_log_event::Gtid_log_event(const char *buf, uint event_len,
seq_no= 0; // So is_valid() returns false
return;
}
- ++buf;
commit_id= uint8korr(buf);
+ buf+= 8;
+ }
+ if (flags2 & FL_XA_TRANSACTION)
+ {
+ xid.formatID= (long) buf[0];
+ xid.gtrid_length= (long) buf[1];
+ xid.bqual_length= (long) buf[2];
+
+ buf+= 3;
+ if (xid.formatID >= 0)
+ {
+ long data_length= xid.bqual_length + xid.gtrid_length;
+ memcpy(xid.data, buf, data_length);
+ buf+= data_length;
+ }
}
}
@@ -7914,6 +7931,12 @@ Gtid_log_event::Gtid_log_event(THD *thd_arg, uint64 seq_no_arg,
/* Preserve any DDL or WAITED flag in the slave's binlog. */
if (thd_arg->rgi_slave)
flags2|= (thd_arg->rgi_slave->gtid_ev_flags2 & (FL_DDL|FL_WAITED));
+ if (thd->transaction.xid_state.xa_state == XA_IDLE &&
+ thd->lex->xa_opt != XA_ONE_PHASE)
+ {
+ flags2|= FL_XA_TRANSACTION;
+ xid= thd->transaction.xid_state.xid;
+ }
}
@@ -7956,7 +7979,7 @@ Gtid_log_event::peek(const char *event_start, size_t event_len,
bool
Gtid_log_event::write()
{
- uchar buf[GTID_HEADER_LEN+2];
+ uchar buf[GTID_HEADER_LEN+2+sizeof(XID)];
size_t write_len;
int8store(buf, seq_no);
@@ -7968,8 +7991,25 @@ Gtid_log_event::write()
write_len= GTID_HEADER_LEN + 2;
}
else
+ write_len= 13;
+
+ if (flags2 & FL_XA_TRANSACTION)
+ {
+ buf[write_len]= (uchar) ((char) xid.formatID);
+ buf[write_len+1]= (uchar) xid.gtrid_length;
+ buf[write_len+2]= (uchar) xid.bqual_length;
+ write_len+= 3;
+ if (xid.formatID >= 0)
+ {
+ long data_length= xid.bqual_length + xid.gtrid_length;
+ memcpy(buf+write_len, xid.data, data_length);
+ write_len+= data_length;
+ }
+ }
+
+ if (write_len < GTID_HEADER_LEN)
{
- bzero(buf+13, GTID_HEADER_LEN-13);
+ bzero(buf+write_len, GTID_HEADER_LEN-write_len);
write_len= GTID_HEADER_LEN;
}
return write_header(write_len) ||
@@ -8012,7 +8052,7 @@ Gtid_log_event::make_compatible_event(String *packet, bool *need_dummy_event,
void
Gtid_log_event::pack_info(Protocol *protocol)
{
- char buf[6+5+10+1+10+1+20+1+4+20+1];
+ char buf[6+5+10+1+10+1+20+1+4+20+1+5+128];
char *p;
p = strmov(buf, (flags2 & FL_STANDALONE ? "GTID " : "BEGIN GTID "));
p= longlong10_to_str(domain_id, p, 10);
@@ -8026,6 +8066,12 @@ Gtid_log_event::pack_info(Protocol *protocol)
p= longlong10_to_str(commit_id, p, 10);
}
+ if (flags2 & FL_XA_TRANSACTION)
+ {
+ p= strmov(p, " XID :");
+ p= strnmov(p, xid.data, xid.bqual_length + xid.gtrid_length);
+ }
+
protocol->store(buf, p-buf, &my_charset_bin);
}
@@ -8079,11 +8125,25 @@ Gtid_log_event::do_apply_event(rpl_group_info *rgi)
thd->lex->sql_command= SQLCOM_BEGIN;
thd->is_slave_error= 0;
status_var_increment(thd->status_var.com_stat[thd->lex->sql_command]);
- if (trans_begin(thd, 0))
+ if (flags2 & FL_XA_TRANSACTION)
{
- DBUG_PRINT("error", ("trans_begin() failed"));
- thd->is_slave_error= 1;
+ thd->lex->xid= &xid;
+ thd->lex->xa_opt= XA_NONE;
+ if (trans_xa_start(thd))
+ {
+ DBUG_PRINT("error", ("trans_xa_start() failed"));
+ thd->is_slave_error= 1;
+ }
+ }
+ else
+ {
+ if (trans_begin(thd, 0))
+ {
+ DBUG_PRINT("error", ("trans_begin() failed"));
+ thd->is_slave_error= 1;
+ }
}
+
thd->update_stats();
if (likely(!thd->is_slave_error))
@@ -8202,9 +8262,29 @@ Gtid_log_event::print(FILE *file, PRINT_EVENT_INFO *print_event_info)
buf, print_event_info->delimiter))
goto err;
}
- if (!(flags2 & FL_STANDALONE))
- if (my_b_printf(&cache, is_flashback ? "COMMIT\n%s\n" : "BEGIN\n%s\n", print_event_info->delimiter))
+ if ((flags2 & FL_XA_TRANSACTION) && !is_flashback)
+ {
+ my_b_write_string(&cache, "XA START '");
+ my_b_write(&cache, (uchar *) xid.data, xid.gtrid_length);
+ my_b_write_string(&cache, "'");
+ if (xid.bqual_length > 0 || xid.formatID != 1)
+ {
+ my_b_write_string(&cache, ", '");
+ my_b_write(&cache, (uchar *) xid.data+xid.gtrid_length, xid.bqual_length);
+ my_b_write_string(&cache, "'");
+ if (xid.formatID != 1)
+ if (my_b_printf(&cache, ", %d", xid.formatID))
+ goto err;
+ }
+ if (my_b_printf(&cache, "%s\n", print_event_info->delimiter))
+ goto err;
+ }
+ else if (!(flags2 & FL_STANDALONE))
+ {
+ if (my_b_printf(&cache, is_flashback ? "COMMIT\n%s\n" : "BEGIN\n%s\n",
+ print_event_info->delimiter))
goto err;
+ }
return cache.flush_data();
err:
@@ -9003,6 +9083,300 @@ Xid_log_event::do_shall_skip(rpl_group_info *rgi)
#endif /* !MYSQL_CLIENT */
+/**
+ Function serializes XID which is characterized by by four last arguments
+ of the function.
+ Serialized XID is presented in valid hex format and is returned to
+ the caller in a buffer pointed by the first argument.
+ The buffer size provived by the caller must be not less than
+ 8 + 2 * XIDDATASIZE + 4 * sizeof(XID::formatID) + 1, see
+ XID::serialize_xid() that is a caller and plugin.h for XID declaration.
+
+ @param buf pointer to a buffer allocated for storing serialized data
+
+ @return the value of the buffer pointer
+*/
+
+char *XA_prepare_log_event::event_xid_t::serialize(char *buf) const
+{
+ int i;
+ char *c= buf;
+ /*
+ Build a string like following pattern:
+ X'hex11hex12...hex1m',X'hex21hex22...hex2n',11
+ and store it into buf.
+ Here hex1i and hex2k are hexadecimals representing XID's internal
+ raw bytes (1 <= i <= m, 1 <= k <= n), and `m' and `n' even numbers
+ half of which corresponding to the lengths of XID's components.
+ */
+ c[0]= 'X';
+ c[1]= '\'';
+ c+= 2;
+ for (i= 0; i < gtrid_length; i++)
+ {
+ c[0]=_dig_vec_lower[((uchar*) data)[i] >> 4];
+ c[1]=_dig_vec_lower[((uchar*) data)[i] & 0x0f];
+ c+= 2;
+ }
+ c[0]= '\'';
+ c[1]= ',';
+ c[2]= 'X';
+ c[3]= '\'';
+ c+= 4;
+
+ for (; i < gtrid_length + bqual_length; i++)
+ {
+ c[0]=_dig_vec_lower[((uchar*) data)[i] >> 4];
+ c[1]=_dig_vec_lower[((uchar*) data)[i] & 0x0f];
+ c+= 2;
+ }
+ c[0]= '\'';
+ sprintf(c+1, ",%lu", formatID);
+
+ return buf;
+}
+
+
+/**************************************************************************
+ XA_prepare_log_event methods
+**************************************************************************/
+/**
+ @note
+ It's ok not to use int8store here,
+ as long as xid_t::set(ulonglong) and
+ xid_t::get_n_xid doesn't do it either.
+ We don't care about actual values of xids as long as
+ identical numbers compare identically
+*/
+
+XA_prepare_log_event::
+XA_prepare_log_event(const char* buf,
+ const Format_description_log_event *description_event)
+ :Log_event(buf, description_event)
+{
+ uint32 temp= 0;
+ uint8 temp_byte;
+
+ buf+= description_event->common_header_len +
+ description_event->post_header_len[XA_PREPARE_LOG_EVENT-1];
+ memcpy(&temp_byte, buf, 1);
+ one_phase= (bool) temp_byte;
+ buf += sizeof(temp_byte);
+ memcpy(&temp, buf, sizeof(temp));
+ m_xid.formatID= le32toh(temp);
+ buf += sizeof(temp);
+ memcpy(&temp, buf, sizeof(temp));
+ m_xid.gtrid_length= le32toh(temp);
+ buf += sizeof(temp);
+ memcpy(&temp, buf, sizeof(temp));
+ m_xid.bqual_length= le32toh(temp);
+ buf += sizeof(temp);
+ memcpy(m_xid.data, buf, m_xid.gtrid_length + m_xid.bqual_length);
+
+ xid= NULL;
+}
+
+
+#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
+void XA_prepare_log_event::pack_info(Protocol *protocol)
+{
+ char buf[ser_buf_size];
+ char query[sizeof("XA COMMIT ONE PHASE") + 1 + sizeof(buf)];
+
+ /* RHS of the following assert is unknown to client sources */
+ compile_time_assert(ser_buf_size == XID::ser_buf_size);
+ m_xid.serialize(buf);
+ sprintf(query,
+ (one_phase ? "XA COMMIT %s ONE PHASE" : "XA PREPARE %s"),
+ buf);
+
+ protocol->store(query, strlen(query), &my_charset_bin);
+}
+#endif
+
+
+#ifndef MYSQL_CLIENT
+bool XA_prepare_log_event::write()
+{
+ uchar data[1 + 4 + 4 + 4];
+ uint8 one_phase_byte= one_phase;
+
+ data[0]= one_phase;
+ int4store(data+1, static_cast<XID*>(xid)->formatID);
+ int4store(data+(1+4), static_cast<XID*>(xid)->gtrid_length);
+ int4store(data+(1+4+4), static_cast<XID*>(xid)->bqual_length);
+
+ DBUG_ASSERT(xid_bufs_size == sizeof(data) - 1);
+
+ return write_header(sizeof(one_phase_byte) + xid_bufs_size +
+ static_cast<XID*>(xid)->gtrid_length +
+ static_cast<XID*>(xid)->bqual_length) ||
+ write_data(data, sizeof(data)) ||
+ write_data((uchar*) static_cast<XID*>(xid)->data,
+ static_cast<XID*>(xid)->gtrid_length +
+ static_cast<XID*>(xid)->bqual_length) ||
+ write_footer();
+}
+#endif
+
+
+#ifdef MYSQL_CLIENT
+bool XA_prepare_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
+{
+ Write_on_release_cache cache(&print_event_info->head_cache, file,
+ Write_on_release_cache::FLUSH_F, this);
+ char buf[ser_buf_size];
+
+ m_xid.serialize(buf);
+
+ if (!print_event_info->short_form)
+ {
+ print_header(&cache, print_event_info, FALSE);
+ if (my_b_printf(&cache, "\tXID = %s\n", buf))
+ goto error;
+ }
+
+ if (my_b_printf(&cache, "XA END %s\n%s\n",
+ buf, print_event_info->delimiter) ||
+ my_b_printf(&cache, "XA PREPARE %s\n%s\n",
+ buf, print_event_info->delimiter))
+ goto error;
+
+ return cache.flush_data();
+error:
+ return TRUE;
+}
+#endif /* MYSQL_CLIENT */
+
+
+#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
+int XA_prepare_log_event::do_apply_event(rpl_group_info *rgi)
+{
+ bool res;
+ int err;
+ rpl_gtid gtid;
+ uint64 sub_id= 0;
+ Relay_log_info const *rli= rgi->rli;
+ xid_t xid;
+ void *hton= NULL;
+
+ /*
+ XID_EVENT works like a COMMIT statement. And it also updates the
+ mysql.gtid_slave_pos table with the GTID of the current transaction.
+
+ Therefore, it acts much like a normal SQL statement, so we need to do
+ THD::reset_for_next_command() as if starting a new statement.
+ */
+ thd->reset_for_next_command();
+ /*
+ Record any GTID in the same transaction, so slave state is transactionally
+ consistent.
+ */
+#ifdef WITH_WSREP
+ thd->wsrep_affected_rows= 0;
+#endif
+
+ if (rgi->gtid_pending)
+ {
+ xa_states c_state= thd->transaction.xid_state.xa_state;
+ sub_id= rgi->gtid_sub_id;
+ rgi->gtid_pending= false;
+
+ gtid= rgi->current_gtid;
+
+ thd->transaction.xid_state.xa_state= XA_ACTIVE;
+ err= rpl_global_gtid_slave_state->record_gtid(thd, >id, sub_id, true,
+ false, &hton);
+ thd->transaction.xid_state.xa_state= c_state;
+ if (err)
+ {
+ int ec= thd->get_stmt_da()->sql_errno();
+ /*
+ Do not report an error if this is really a kill due to a deadlock.
+ In this case, the transaction will be re-tried instead.
+ */
+ if (!is_parallel_retry_error(rgi, ec))
+ rli->report(ERROR_LEVEL, ER_CANNOT_UPDATE_GTID_STATE, rgi->gtid_info(),
+ "Error during XID COMMIT: failed to update GTID state in "
+ "%s.%s: %d: %s",
+ "mysql", rpl_gtid_slave_state_table_name.str, ec,
+ thd->get_stmt_da()->message());
+ thd->is_slave_error= 1;
+ return err;
+ }
+
+ DBUG_EXECUTE_IF("gtid_fail_after_record_gtid",
+ { my_error(ER_ERROR_DURING_COMMIT, MYF(0), HA_ERR_WRONG_COMMAND);
+ thd->is_slave_error= 1;
+ return 1;
+ });
+ }
+ /* For a slave XA_prepare_log_event is COMMIT */
+ general_log_print(thd, COM_QUERY,
+ "COMMIT /* implicit, from Xid_log_event */");
+ thd->variables.option_bits&= ~OPTION_GTID_BEGIN;
+
+ xid.set(m_xid.formatID,
+ m_xid.data, m_xid.gtrid_length,
+ m_xid.data + m_xid.gtrid_length, m_xid.bqual_length);
+
+ thd->lex->xid= &xid;
+ if (trans_xa_end(thd))
+ return 1;
+
+ if (!one_phase)
+ {
+ res= trans_xa_prepare(thd);
+ }
+ else
+ {
+ res= trans_xa_commit(thd);
+ thd->mdl_context.release_transactional_locks();
+ }
+
+
+ if (!res && sub_id)
+ rpl_global_gtid_slave_state->update_state_hash(sub_id, >id, hton, rgi);
+
+ /*
+ Increment the global status commit count variable
+ */
+ status_var_increment(thd->status_var.com_stat[SQLCOM_COMMIT]);
+
+ return res;
+}
+
+
+Log_event::enum_skip_reason
+XA_prepare_log_event::do_shall_skip(rpl_group_info *rgi)
+{
+ DBUG_ENTER("Xid_log_event::do_shall_skip");
+ if (rgi->rli->slave_skip_counter > 0)
+ {
+ DBUG_ASSERT(!rgi->rli->get_flag(Relay_log_info::IN_TRANSACTION));
+ thd->variables.option_bits&= ~(OPTION_BEGIN | OPTION_GTID_BEGIN);
+ DBUG_RETURN(Log_event::EVENT_SKIP_COUNT);
+ }
+#ifdef WITH_WSREP
+ else if (wsrep_mysql_replication_bundle && WSREP_ON &&
+ opt_slave_domain_parallel_threads == 0)
+ {
+ if (++thd->wsrep_mysql_replicated < (int)wsrep_mysql_replication_bundle)
+ {
+ WSREP_DEBUG("skipping wsrep commit %d", thd->wsrep_mysql_replicated);
+ DBUG_RETURN(Log_event::EVENT_SKIP_IGNORE);
+ }
+ else
+ {
+ thd->wsrep_mysql_replicated = 0;
+ }
+ }
+#endif
+ DBUG_RETURN(Log_event::do_shall_skip(rgi));
+}
+#endif /* !MYSQL_CLIENT */
+
+
/**************************************************************************
User_var_log_event methods
**************************************************************************/
@@ -14789,7 +15163,6 @@ bool event_that_should_be_ignored(const char *buf)
event_type == PREVIOUS_GTIDS_LOG_EVENT ||
event_type == TRANSACTION_CONTEXT_EVENT ||
event_type == VIEW_CHANGE_EVENT ||
- event_type == XA_PREPARE_LOG_EVENT ||
(uint2korr(buf + FLAGS_OFFSET) & LOG_EVENT_IGNORABLE_F))
return 1;
return 0;
diff --git a/sql/log_event.h b/sql/log_event.h
index 38a40c9..b5c48c9 100644
--- a/sql/log_event.h
+++ b/sql/log_event.h
@@ -217,6 +217,7 @@ class String;
#define GTID_HEADER_LEN 19
#define GTID_LIST_HEADER_LEN 4
#define START_ENCRYPTION_HEADER_LEN 0
+#define XA_PREPARE_HEADER_LEN 0
/*
Max number of possible extra bytes in a replication event compared to a
@@ -3064,6 +3065,79 @@ class Xid_log_event: public Log_event
#endif
};
+
+/**
+ @class XA_prepare_log_event
+
+ Similar to Xid_log_event except that
+ - it is specific to XA transaction
+ - it carries out the prepare logics rather than the final committing
+ when @c one_phase member is off.
+ From the groupping perspective the event finalizes the current "prepare" group
+ started with XA START Query-log-event.
+ When @c one_phase is false Commit of Rollback for XA transaction are
+ logged separately to the prepare-group events so being a groups of
+ their own.
+*/
+
+class XA_prepare_log_event: public Log_event
+{
+protected:
+ /* The event_xid_t members were copied from handler.h */
+ struct event_xid_t
+ {
+ long formatID;
+ long gtrid_length;
+ long bqual_length;
+ char data[MYSQL_XIDDATASIZE]; // not \0-terminated !
+ char *serialize(char *buf) const;
+ };
+
+ /* size of serialization buffer is explained in $MYSQL/sql/xa.h. */
+ static const uint ser_buf_size=
+ 8 + 2 * MYSQL_XIDDATASIZE + 4 * sizeof(long) + 1;
+
+ /* Total size of buffers to hold serialized members of XID struct */
+ static const int xid_bufs_size= 12;
+ event_xid_t m_xid;
+ void *xid;
+ bool one_phase;
+
+public:
+#ifdef MYSQL_SERVER
+ XA_prepare_log_event(THD* thd_arg, XID *xid_arg, bool one_phase_arg):
+ Log_event(thd_arg, 0, TRUE), xid(xid_arg), one_phase(one_phase_arg)
+ {
+ cache_type= Log_event::EVENT_NO_CACHE;
+ }
+#ifdef HAVE_REPLICATION
+ void pack_info(Protocol* protocol);
+#endif /* HAVE_REPLICATION */
+#else
+ bool print(FILE* file, PRINT_EVENT_INFO* print_event_info);
+#endif
+ XA_prepare_log_event(const char* buf,
+ const Format_description_log_event *description_event);
+ ~XA_prepare_log_event() {}
+ Log_event_type get_type_code() { return XA_PREPARE_LOG_EVENT; }
+ int get_data_size()
+ {
+ return xid_bufs_size + m_xid.gtrid_length + m_xid.bqual_length;
+ }
+
+#ifdef MYSQL_SERVER
+ bool write();
+#endif
+ bool is_valid() const { return 1; }
+
+private:
+#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
+ virtual int do_apply_event(rpl_group_info *rgi);
+ enum_skip_reason do_shall_skip(rpl_group_info *rgi);
+#endif
+};
+
+
/**
@class User_var_log_event
@@ -3376,6 +3450,11 @@ class Gtid_log_event: public Log_event
uint64 seq_no;
uint64 commit_id;
uint32 domain_id;
+#ifdef MYSQL_SERVER
+ XID xid;
+#else
+ struct st_mysql_xid xid;
+#endif
uchar flags2;
/* Flags2. */
@@ -3404,6 +3483,8 @@ class Gtid_log_event: public Log_event
static const uchar FL_WAITED= 16;
/* FL_DDL is set for event group containing DDL. */
static const uchar FL_DDL= 32;
+ /* FL_XA_TRANSACTION is set for XA transaction. */
+ static const uchar FL_XA_TRANSACTION= 64;
#ifdef MYSQL_SERVER
Gtid_log_event(THD *thd_arg, uint64 seq_no, uint32 domain_id, bool standalone,
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index fa2f866..cc75da9 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -1461,12 +1461,19 @@ void THD::cleanup(void)
DBUG_ASSERT(cleanup_done == 0);
set_killed(KILL_CONNECTION);
-#ifdef ENABLE_WHEN_BINLOG_WILL_BE_ABLE_TO_PREPARE
if (transaction.xid_state.xa_state == XA_PREPARED)
{
-#error xid_state in the cache should be replaced by the allocated value
+ trans_detach(this);
+ transaction.xid_state.xa_state= XA_NOTR;
+ transaction.xid_state.rm_error= 0;
+ }
+ else
+ {
+ transaction.xid_state.xa_state= XA_NOTR;
+ transaction.xid_state.rm_error= 0;
+ trans_rollback(this);
+ xid_cache_delete(this, &transaction.xid_state);
}
-#endif
mysql_ha_cleanup(this);
locked_tables_list.unlock_locked_tables(this);
@@ -1474,11 +1481,6 @@ void THD::cleanup(void)
delete_dynamic(&user_var_events);
close_temporary_tables();
- transaction.xid_state.xa_state= XA_NOTR;
- transaction.xid_state.rm_error= 0;
- trans_rollback(this);
- xid_cache_delete(this, &transaction.xid_state);
-
DBUG_ASSERT(open_tables == NULL);
/*
If the thread was in the middle of an ongoing transaction (rolled
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 69fabee..76befcb 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -1255,6 +1255,18 @@ typedef struct st_xid_state {
/* Error reported by the Resource Manager (RM) to the Transaction Manager. */
uint rm_error;
XID_cache_element *xid_cache_element;
+ /*
+ Binary logging status.
+ It is set to TRUE at XA PREPARE if the transaction was written
+ to the binlog.
+ Naturally FALSE means the transaction was not written to
+ the binlog. Happens if the trnasaction did not modify anything
+ or binlogging was turned off. In that case we shouldn't binlog
+ the consequent XA COMMIT/ROLLBACK.
+ The recovered transaction after server restart sets it to TRUE always.
+ That can cause inconsistencies (shoud be fixed?).
+ */
+ bool is_binlogged;
/**
Check that XA transaction has an uncommitted work. Report an error
@@ -1278,6 +1290,12 @@ typedef struct st_xid_state {
}
return false;
}
+
+ void reset()
+ {
+ xid.null();
+ is_binlogged= FALSE;
+ }
} XID_STATE;
void xid_cache_init(void);
@@ -2603,7 +2621,7 @@ class THD :public Statement,
then.
*/
if (!xid_state.rm_error)
- xid_state.xid.null();
+ xid_state.reset();
free_root(&mem_root,MYF(MY_KEEP_PREALLOC));
DBUG_VOID_RETURN;
}
diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc
index b48070b..3e4a067 100644
--- a/sql/sql_connect.cc
+++ b/sql/sql_connect.cc
@@ -1414,6 +1414,7 @@ void do_handle_one_connection(CONNECT *connect)
#endif
end_thread:
close_connection(thd);
+ thd->get_stmt_da()->reset_diagnostics_area();
if (thd->userstat_running)
update_global_user_stats(thd, create_user, time(NULL));
diff --git a/sql/transaction.cc b/sql/transaction.cc
index 13614d3..64533d7 100644
--- a/sql/transaction.cc
+++ b/sql/transaction.cc
@@ -790,6 +790,44 @@ bool trans_release_savepoint(THD *thd, LEX_CSTRING name)
/**
+ Detach the current XA transaction;
+
+ @param thd Current thread
+
+ @retval FALSE Success
+ @retval TRUE Failure
+*/
+
+bool trans_detach(THD *thd)
+{
+ XID_STATE *xid_s= &thd->transaction.xid_state;
+ Ha_trx_info *ha_info, *ha_info_next;
+
+ DBUG_ENTER("trans_detach");
+
+// DBUG_ASSERT(xid_s->xa_state == XA_PREPARED &&
+// xid_cache_search(thd, &xid_s->xid));
+
+ xid_cache_delete(thd, xid_s);
+ if (xid_cache_insert(&xid_s->xid, XA_PREPARED))
+ DBUG_RETURN(TRUE);
+
+ for (ha_info= thd->transaction.all.ha_list;
+ ha_info;
+ ha_info= ha_info_next)
+ {
+ ha_info_next= ha_info->next();
+ ha_info->reset(); /* keep it conveniently zero-filled */
+ }
+
+ thd->transaction.all.ha_list= 0;
+ thd->transaction.all.no_2pc= 0;
+
+ DBUG_RETURN(FALSE);
+}
+
+
+/**
Starts an XA transaction with the given xid value.
@param thd Current thread
@@ -928,6 +966,12 @@ bool trans_xa_commit(THD *thd)
res= !xs;
if (res)
my_error(ER_XAER_NOTA, MYF(0));
+ else if (thd->in_multi_stmt_transaction_mode())
+ {
+ my_error(ER_XAER_RMFAIL, MYF(0),
+ xa_state_names[thd->transaction.xid_state.xa_state]);
+ res= TRUE;
+ }
else
{
res= xa_trans_rolled_back(xs);
@@ -978,8 +1022,16 @@ bool trans_xa_commit(THD *thd)
{
DEBUG_SYNC(thd, "trans_xa_commit_after_acquire_commit_lock");
- res= MY_TEST(ha_commit_one_phase(thd, 1));
- if (res)
+ if(WSREP_EMULATE_BINLOG(thd) || mysql_bin_log.is_open())
+ {
+ res= thd->binlog_query(THD::THD::STMT_QUERY_TYPE,
+ thd->query(), thd->query_length(),
+ FALSE, FALSE, FALSE, 0);
+ }
+ else
+ res= 0;
+
+ if (res || (res= MY_TEST(ha_commit_one_phase(thd, 1))))
my_error(ER_XAER_RMERR, MYF(0));
}
}
@@ -1044,7 +1096,18 @@ bool trans_xa_rollback(THD *thd)
DBUG_RETURN(TRUE);
}
- res= xa_trans_force_rollback(thd);
+ if(WSREP_EMULATE_BINLOG(thd) || mysql_bin_log.is_open())
+ {
+ res= thd->binlog_query(THD::THD::STMT_QUERY_TYPE,
+ thd->query(), thd->query_length(),
+ FALSE, FALSE, FALSE, 0);
+ }
+ else
+ res= 0;
+
+ res= res || xa_trans_force_rollback(thd);
+ if (res || (res= MY_TEST(xa_trans_force_rollback(thd))))
+ my_error(ER_XAER_RMERR, MYF(0));
thd->variables.option_bits&= ~(OPTION_BEGIN | OPTION_KEEP_LOG);
thd->transaction.all.reset();
diff --git a/sql/transaction.h b/sql/transaction.h
index 7e34693..f228cc6 100644
--- a/sql/transaction.h
+++ b/sql/transaction.h
@@ -29,6 +29,7 @@ bool trans_commit(THD *thd);
bool trans_commit_implicit(THD *thd);
bool trans_rollback(THD *thd);
bool trans_rollback_implicit(THD *thd);
+bool trans_detach(THD *thd);
bool trans_commit_stmt(THD *thd);
bool trans_rollback_stmt(THD *thd);
1
0
revision-id: e8b6c15010e6fffe17e27c165b8c60b51a8f66a7 (mariadb-10.3.12-55-ge8b6c15)
parent(s): ce0678f6cb367c2f3bf897d467846b47aa710529
committer: Alexey Botchkov
timestamp: 2019-02-13 23:26:23 +0400
message:
connect.xml.result fixed.
---
storage/connect/mysql-test/connect/r/xml.result | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/storage/connect/mysql-test/connect/r/xml.result b/storage/connect/mysql-test/connect/r/xml.result
index 99739b1..6a0c9db 100644
--- a/storage/connect/mysql-test/connect/r/xml.result
+++ b/storage/connect/mysql-test/connect/r/xml.result
@@ -323,7 +323,7 @@ HEX(c) 3F3F3F3F3F3F3F
Warnings:
Level Warning
Code 1366
-Message Incorrect string value: '\xC3\x81\xC3\x82\xC3\x83...' for column 'c' at row 1
+Message Incorrect string value: '\xC3\x81\xC3\x82\xC3\x83...' for column `test`.`t1`.`c` at row 1
Level Warning
Code 1105
Message Out of range value ÁÂÃÄÅÆÇ for column 'c' at row 1
1
0
[Commits] 62fad4e: MDEV-17096 Pushdown of simple derived tables to storage engines
by IgorBabaev 13 Feb '19
by IgorBabaev 13 Feb '19
13 Feb '19
revision-id: 62fad4e8e964786cd1c8d675f3c071a405a8d187 (mariadb-10.3.6-137-g62fad4e)
parent(s): 953ca199fb62bcd190d2af4d6177f986564ec1ad
author: Igor Babaev
committer: Igor Babaev
timestamp: 2019-02-13 08:55:38 -0800
message:
MDEV-17096 Pushdown of simple derived tables to storage engines
Fixing failures of federated test on 32-bit platforms
---
storage/federatedx/ha_federatedx.cc | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/storage/federatedx/ha_federatedx.cc b/storage/federatedx/ha_federatedx.cc
index f55b0bc..b0a08a0 100644
--- a/storage/federatedx/ha_federatedx.cc
+++ b/storage/federatedx/ha_federatedx.cc
@@ -3684,7 +3684,7 @@ struct st_mysql_storage_engine federatedx_storage_engine=
my_bool use_pushdown;
static MYSQL_SYSVAR_BOOL(pushdown, use_pushdown, 0,
"Use query fragments pushdown capabilities", NULL, NULL, FALSE);
-static struct st_mysql_sys_var* sysvars[]= { MYSQL_SYSVAR(pushdown) };
+static struct st_mysql_sys_var* sysvars[]= { MYSQL_SYSVAR(pushdown), NULL };
#include "federatedx_pushdown.cc"
1
0
[Commits] a78365c11ac: MDEV-18553: MDEV-16327 pre-requisits isolation of LIMIT/OFFSET handling
by Oleksandr Byelkin 13 Feb '19
by Oleksandr Byelkin 13 Feb '19
13 Feb '19
revision-id: a78365c11ac8b8ac2697a185d2516152a44f3dfe (mariadb-10.4.1-78-ga78365c11ac)
parent(s): d97a26a67dca82d166e9edefe5f950ea8a110dfd
author: Oleksandr Byelkin
committer: Oleksandr Byelkin
timestamp: 2019-02-13 09:54:51 +0100
message:
MDEV-18553: MDEV-16327 pre-requisits isolation of LIMIT/OFFSET handling
---
sql/group_by_handler.cc | 8 +++----
sql/group_by_handler.h | 1 +
sql/item_subselect.cc | 2 +-
sql/opt_subselect.cc | 9 +++-----
sql/sql_class.cc | 44 +++++++++++-------------------------
sql/sql_class.h | 6 ++---
sql/sql_derived.cc | 2 +-
sql/sql_error.cc | 10 ++++-----
sql/sql_insert.cc | 7 ++----
sql/sql_lex.cc | 10 ++-------
sql/sql_lex.h | 59 ++++++++++++++++++++++++++++++++++++++++++++++++-
sql/sql_parse.cc | 13 ++++++-----
sql/sql_profile.cc | 10 ++++-----
sql/sql_repl.cc | 10 ++++-----
sql/sql_select.cc | 46 ++++++++++++++++++++------------------
sql/sql_union.cc | 36 +++++++++++++-----------------
16 files changed, 148 insertions(+), 125 deletions(-)
diff --git a/sql/group_by_handler.cc b/sql/group_by_handler.cc
index f18758a2d94..f5ed24370b1 100644
--- a/sql/group_by_handler.cc
+++ b/sql/group_by_handler.cc
@@ -40,7 +40,7 @@ int Pushdown_query::execute(JOIN *join)
{
int err;
ha_rows max_limit;
- ha_rows *reset_limit= 0;
+ bool reset_limit= FALSE;
Item **reset_item= 0;
THD *thd= handler->thd;
TABLE *table= handler->table;
@@ -52,11 +52,11 @@ int Pushdown_query::execute(JOIN *join)
if (store_data_in_temp_table)
{
max_limit= join->tmp_table_param.end_write_records;
- reset_limit= &join->unit->select_limit_cnt;
+ reset_limit= TRUE;
}
else
{
- max_limit= join->unit->select_limit_cnt;
+ max_limit= join->unit->lim.get_select_limit();
if (join->unit->fake_select_lex)
reset_item= &join->unit->fake_select_lex->select_limit;
}
@@ -112,7 +112,7 @@ int Pushdown_query::execute(JOIN *join)
break; // LIMIT reached
join->do_send_rows= 0; // Calculate FOUND_ROWS()
if (reset_limit)
- *reset_limit= HA_POS_ERROR;
+ join->unit->lim.set_unlimited();
if (reset_item)
*reset_item= 0;
}
diff --git a/sql/group_by_handler.h b/sql/group_by_handler.h
index d3f48a15c24..be981f0da5f 100644
--- a/sql/group_by_handler.h
+++ b/sql/group_by_handler.h
@@ -56,6 +56,7 @@ struct Query
ORDER *order_by;
Item *having;
// LIMIT
+ //ha_rows select_limit_cnt, offset_limit_cnt;
};
class group_by_handler
diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc
index 0ace59fd2fc..e860c3fa98d 100644
--- a/sql/item_subselect.cc
+++ b/sql/item_subselect.cc
@@ -2721,7 +2721,7 @@ bool Item_in_subselect::inject_in_to_exists_cond(JOIN *join_arg)
join_arg->thd->change_item_tree(&unit->global_parameters()->select_limit,
new (thd->mem_root)
Item_int(thd, (int32) 1));
- unit->select_limit_cnt= 1;
+ unit->lim.set_single_row();
DBUG_RETURN(false);
}
diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc
index c4c30c9b50d..e9284926385 100644
--- a/sql/opt_subselect.cc
+++ b/sql/opt_subselect.cc
@@ -5492,11 +5492,8 @@ int select_value_catcher::send_data(List<Item> &items)
DBUG_ASSERT(!assigned);
DBUG_ASSERT(items.elements == n_elements);
- if (unit->offset_limit_cnt)
- { // Using limit offset,count
- unit->offset_limit_cnt--;
- DBUG_RETURN(0);
- }
+ if (unit->lim.check_and_move_offset())
+ DBUG_RETURN(0); // Using limit offset,count
Item *val_item;
List_iterator_fast<Item> li(items);
@@ -6289,7 +6286,7 @@ bool JOIN::choose_subquery_plan(table_map join_tables)
Set the limit of this JOIN object as well, because normally its being
set in the beginning of JOIN::optimize, which was already done.
*/
- select_limit= in_subs->unit->select_limit_cnt;
+ select_limit= in_subs->unit->lim.get_select_limit();
}
else if (in_subs->test_strategy(SUBS_IN_TO_EXISTS))
{
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 2952adbd3e6..ce6d71082fb 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -2936,11 +2936,8 @@ int select_send::send_data(List<Item> &items)
DBUG_ENTER("select_send::send_data");
/* unit is not set when using 'delete ... returning' */
- if (unit && unit->offset_limit_cnt)
- { // using limit offset,count
- unit->offset_limit_cnt--;
- DBUG_RETURN(FALSE);
- }
+ if (unit && unit->lim.check_and_move_offset())
+ DBUG_RETURN(FALSE); // using limit offset,count
if (thd->killed == ABORT_QUERY)
DBUG_RETURN(FALSE);
@@ -3205,11 +3202,8 @@ int select_export::send_data(List<Item> &items)
String tmp(buff,sizeof(buff),&my_charset_bin),*res;
tmp.length(0);
- if (unit->offset_limit_cnt)
- { // using limit offset,count
- unit->offset_limit_cnt--;
- DBUG_RETURN(0);
- }
+ if (unit->lim.check_and_move_offset())
+ DBUG_RETURN(0); // using limit offset,count
if (thd->killed == ABORT_QUERY)
DBUG_RETURN(0);
row_count++;
@@ -3465,11 +3459,8 @@ int select_dump::send_data(List<Item> &items)
Item *item;
DBUG_ENTER("select_dump::send_data");
- if (unit->offset_limit_cnt)
- { // using limit offset,count
- unit->offset_limit_cnt--;
- DBUG_RETURN(0);
- }
+ if (unit->lim.check_and_move_offset())
+ DBUG_RETURN(0); // using limit offset,count
if (thd->killed == ABORT_QUERY)
DBUG_RETURN(0);
@@ -3508,11 +3499,8 @@ int select_singlerow_subselect::send_data(List<Item> &items)
MYF(current_thd->lex->ignore ? ME_WARNING : 0));
DBUG_RETURN(1);
}
- if (unit->offset_limit_cnt)
- { // Using limit offset,count
- unit->offset_limit_cnt--;
- DBUG_RETURN(0);
- }
+ if (unit->lim.check_and_move_offset())
+ DBUG_RETURN(0); // Using limit offset,count
if (thd->killed == ABORT_QUERY)
DBUG_RETURN(0);
List_iterator_fast<Item> li(items);
@@ -3649,11 +3637,8 @@ int select_exists_subselect::send_data(List<Item> &items)
{
DBUG_ENTER("select_exists_subselect::send_data");
Item_exists_subselect *it= (Item_exists_subselect *)item;
- if (unit->offset_limit_cnt)
- { // Using limit offset,count
- unit->offset_limit_cnt--;
- DBUG_RETURN(0);
- }
+ if (unit->lim.check_and_move_offset())
+ DBUG_RETURN(0); // Using limit offset,count
if (thd->killed == ABORT_QUERY)
DBUG_RETURN(0);
it->value= 1;
@@ -4060,12 +4045,9 @@ int select_dumpvar::send_data(List<Item> &items)
{
DBUG_ENTER("select_dumpvar::send_data");
- if (unit->offset_limit_cnt)
- { // using limit offset,count
- unit->offset_limit_cnt--;
- DBUG_RETURN(0);
- }
- if (row_count++)
+ if (unit->lim.check_and_move_offset())
+ DBUG_RETURN(0); // using limit offset,count
+ if (row_count++)
{
my_message(ER_TOO_MANY_ROWS, ER_THD(thd, ER_TOO_MANY_ROWS), MYF(0));
DBUG_RETURN(1);
diff --git a/sql/sql_class.h b/sql/sql_class.h
index b1da6e19247..7427b2745bc 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -5117,9 +5117,9 @@ class select_result :public select_result_sink
/* this method is called just before the first row of the table can be read */
virtual void prepare_to_read_rows() {}
- void reset_offset_limit()
+ void remove_offset_limit()
{
- unit->offset_limit_cnt= 0;
+ unit->lim.remove_offset();
}
/*
@@ -5786,7 +5786,7 @@ class select_union_direct :public select_unit
*/
DBUG_ASSERT(false); /* purecov: inspected */
}
- void reset_offset_limit_cnt()
+ void remove_offset_limit()
{
// EXPLAIN should never output to a select_union_direct
DBUG_ASSERT(false); /* purecov: inspected */
diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc
index 878aa715b84..4fd44a18550 100644
--- a/sql/sql_derived.cc
+++ b/sql/sql_derived.cc
@@ -1148,7 +1148,7 @@ bool mysql_derived_fill(THD *thd, LEX *lex, TABLE_LIST *derived)
{
SELECT_LEX *first_select= unit->first_select();
unit->set_limit(unit->global_parameters());
- if (unit->select_limit_cnt == HA_POS_ERROR)
+ if (unit->lim.is_unlimited())
first_select->options&= ~OPTION_FOUND_ROWS;
lex->current_select= first_select;
diff --git a/sql/sql_error.cc b/sql/sql_error.cc
index 8d639f9271d..6bb36dbb089 100644
--- a/sql/sql_error.cc
+++ b/sql/sql_error.cc
@@ -783,7 +783,7 @@ bool mysqld_show_warnings(THD *thd, ulong levels_to_show)
const Sql_condition *err;
SELECT_LEX *sel= thd->lex->first_select_lex();
SELECT_LEX_UNIT *unit= &thd->lex->unit;
- ulonglong idx= 0;
+ ha_rows idx;
Protocol *protocol=thd->protocol;
DBUG_ENTER("mysqld_show_warnings");
@@ -808,14 +808,14 @@ bool mysqld_show_warnings(THD *thd, ulong levels_to_show)
Diagnostics_area::Sql_condition_iterator it=
thd->get_stmt_da()->sql_conditions();
- while ((err= it++))
+ for (idx= 1; (err= it++) ; idx++)
{
/* Skip levels that the user is not interested in */
if (!(levels_to_show & ((ulong) 1 << err->get_level())))
continue;
- if (++idx <= unit->offset_limit_cnt)
- continue;
- if (idx > unit->select_limit_cnt)
+ if (unit->lim.check_and_move_offset())
+ continue; // using limit offset,count
+ if (idx > unit->lim.get_select_limit())
break;
protocol->prepare_for_resend();
protocol->store(warning_level_names[err->get_level()].str,
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index f5e4185db92..c4f60f0551c 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -3840,11 +3840,8 @@ int select_insert::send_data(List<Item> &values)
DBUG_ENTER("select_insert::send_data");
bool error=0;
- if (unit->offset_limit_cnt)
- { // using limit offset,count
- unit->offset_limit_cnt--;
- DBUG_RETURN(0);
- }
+ if (unit->lim.check_and_move_offset())
+ DBUG_RETURN(0); // using limit offset,count
if (unlikely(thd->killed == ABORT_QUERY))
DBUG_RETURN(0);
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 1b8f448553b..2c298a17391 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -2321,8 +2321,7 @@ void st_select_lex_unit::init_query()
{
init_query_common();
set_linkage(GLOBAL_OPTIONS_TYPE);
- select_limit_cnt= HA_POS_ERROR;
- offset_limit_cnt= 0;
+ lim.set_unlimited();
union_distinct= 0;
prepared= optimized= optimized_2= executed= 0;
optimize_started= 0;
@@ -3462,12 +3461,7 @@ void st_select_lex_unit::set_limit(st_select_lex *sl)
{
DBUG_ASSERT(!thd->stmt_arena->is_stmt_prepare());
- offset_limit_cnt= sl->get_offset();
- select_limit_cnt= sl->get_limit();
- if (select_limit_cnt + offset_limit_cnt >= select_limit_cnt)
- select_limit_cnt+= offset_limit_cnt;
- else
- select_limit_cnt= HA_POS_ERROR;
+ lim.set_limit(sl->get_limit(), sl->get_offset());
}
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 40ba3b6e7b7..0270a4acfcc 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -816,6 +816,63 @@ void create_explain_query(LEX *lex, MEM_ROOT *mem_root);
void create_explain_query_if_not_exists(LEX *lex, MEM_ROOT *mem_root);
bool print_explain_for_slow_log(LEX *lex, THD *thd, String *str);
+class Select_limit_counters
+{
+ ha_rows offset_limit_cnt_start,
+ select_limit_cnt, offset_limit_cnt;
+
+ public:
+ Select_limit_counters():
+ offset_limit_cnt_start(0),
+ select_limit_cnt(0), offset_limit_cnt(0)
+ {};
+
+ void set_limit(ha_rows limit, ha_rows offset)
+ {
+ offset_limit_cnt_start= offset;
+ select_limit_cnt= limit;
+ if (select_limit_cnt + offset_limit_cnt_start >=
+ select_limit_cnt)
+ select_limit_cnt+= offset_limit_cnt_start;
+ else
+ select_limit_cnt= HA_POS_ERROR;
+ reset();
+ }
+
+ void set_single_row()
+ {
+ offset_limit_cnt= offset_limit_cnt_start= 0;
+ select_limit_cnt= 1;
+ }
+
+ void reset()
+ {
+ offset_limit_cnt= offset_limit_cnt_start;
+ }
+
+ bool is_unlimited()
+ { return select_limit_cnt == HA_POS_ERROR; }
+ void set_unlimited()
+ { select_limit_cnt= HA_POS_ERROR; offset_limit_cnt= 0; }
+
+ bool check_and_move_offset()
+ {
+ if (offset_limit_cnt)
+ {
+ offset_limit_cnt--;
+ return TRUE;
+ }
+ return FALSE;
+ }
+ void remove_offset() { offset_limit_cnt= 0; }
+
+ ha_rows get_select_limit()
+ { return select_limit_cnt; }
+ ha_rows get_offset_limit()
+ { return offset_limit_cnt; }
+};
+
+
class st_select_lex_unit: public st_select_lex_node {
protected:
TABLE_LIST result_table_list;
@@ -891,7 +948,7 @@ class st_select_lex_unit: public st_select_lex_node {
//node on which we should return current_select pointer after parsing subquery
st_select_lex *return_to;
/* LIMIT clause runtime counters */
- ha_rows select_limit_cnt, offset_limit_cnt;
+ Select_limit_counters lim;
/* not NULL if unit used in subselect, point to subselect item */
Item_subselect *item;
/*
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 7d6f71cda21..9e6600d65b6 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -4579,7 +4579,7 @@ mysql_execute_command(THD *thd)
select_lex->where,
select_lex->order_list.elements,
select_lex->order_list.first,
- unit->select_limit_cnt,
+ unit->lim.get_select_limit(),
lex->duplicates, lex->ignore,
&found, &updated);
MYSQL_UPDATE_DONE(res, found, updated);
@@ -4924,7 +4924,7 @@ mysql_execute_command(THD *thd)
res = mysql_delete(thd, all_tables,
select_lex->where, &select_lex->order_list,
- unit->select_limit_cnt, select_lex->options,
+ unit->lim.get_select_limit(), select_lex->options,
sel_result);
if (replaced_protocol)
@@ -5771,7 +5771,8 @@ mysql_execute_command(THD *thd)
res= mysql_ha_read(thd, first_table, lex->ha_read_mode, lex->ident.str,
lex->insert_list, lex->ha_rkey_mode, select_lex->where,
- unit->select_limit_cnt, unit->offset_limit_cnt);
+ unit->lim.get_select_limit(),
+ unit->lim.get_offset_limit());
break;
case SQLCOM_BEGIN:
@@ -6511,8 +6512,8 @@ static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables)
/*
Do like the original select_describe did: remove OFFSET from the
top-level LIMIT
- */
- result->reset_offset_limit();
+ */
+ result->remove_offset_limit();
if (lex->explain_json)
{
lex->explain->print_explain_json(result, lex->analyze_stmt);
@@ -7853,7 +7854,7 @@ void mysql_init_multi_delete(LEX *lex)
lex->sql_command= SQLCOM_DELETE_MULTI;
mysql_init_select(lex);
lex->first_select_lex()->select_limit= 0;
- lex->unit.select_limit_cnt= HA_POS_ERROR;
+ lex->unit.lim.set_unlimited();
lex->first_select_lex()->table_list.
save_and_clear(&lex->auxiliary_table_list);
lex->query_tables= 0;
diff --git a/sql/sql_profile.cc b/sql/sql_profile.cc
index 6ca21aebb37..e2b7b18faac 100644
--- a/sql/sql_profile.cc
+++ b/sql/sql_profile.cc
@@ -404,7 +404,7 @@ bool PROFILING::show_profiles()
MEM_ROOT *mem_root= thd->mem_root;
SELECT_LEX *sel= thd->lex->first_select_lex();
SELECT_LEX_UNIT *unit= &thd->lex->unit;
- ha_rows idx= 0;
+ ha_rows idx;
Protocol *protocol= thd->protocol;
void *iterator;
DBUG_ENTER("PROFILING::show_profiles");
@@ -428,9 +428,9 @@ bool PROFILING::show_profiles()
unit->set_limit(sel);
- for (iterator= history.new_iterator();
+ for (iterator= history.new_iterator(), idx= 1;
iterator != NULL;
- iterator= history.iterator_next(iterator))
+ iterator= history.iterator_next(iterator), idx++)
{
prof= history.iterator_value(iterator);
@@ -438,9 +438,9 @@ bool PROFILING::show_profiles()
double query_time_usecs= prof->m_end_time_usecs - prof->m_start_time_usecs;
- if (++idx <= unit->offset_limit_cnt)
+ if (unit->lim.check_and_move_offset())
continue;
- if (idx > unit->select_limit_cnt)
+ if (idx > unit->lim.get_select_limit())
break;
protocol->prepare_for_resend();
diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc
index 2ee175293de..6f712750e41 100644
--- a/sql/sql_repl.cc
+++ b/sql/sql_repl.cc
@@ -3911,7 +3911,7 @@ bool mysql_show_binlog_events(THD* thd)
if (binary_log->is_open())
{
SELECT_LEX_UNIT *unit= &thd->lex->unit;
- ha_rows event_count, limit_start, limit_end;
+ ha_rows event_count;
my_off_t pos = MY_MAX(BIN_LOG_HEADER_SIZE, lex_mi->pos); // user-friendly
char search_file_name[FN_REFLEN], *name;
const char *log_file_name = lex_mi->log_file_name;
@@ -3926,8 +3926,6 @@ bool mysql_show_binlog_events(THD* thd)
}
unit->set_limit(thd->lex->current_select);
- limit_start= unit->offset_limit_cnt;
- limit_end= unit->select_limit_cnt;
name= search_file_name;
if (log_file_name)
@@ -4006,7 +4004,7 @@ bool mysql_show_binlog_events(THD* thd)
description_event,
opt_master_verify_checksum)); )
{
- if (event_count >= limit_start &&
+ if (!unit->lim.check_and_move_offset() &&
ev->net_send(protocol, linfo.log_file_name, pos))
{
errmsg = "Net error";
@@ -4040,11 +4038,11 @@ bool mysql_show_binlog_events(THD* thd)
pos = my_b_tell(&log);
- if (++event_count >= limit_end)
+ if (++event_count >= unit->lim.get_select_limit())
break;
}
- if (unlikely(event_count < limit_end && log.error))
+ if (unlikely(event_count < unit->lim.get_select_limit() && log.error))
{
errmsg = "Wrong offset or I/O error";
mysql_mutex_unlock(log_lock);
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 978f0785887..aa4c40866a9 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -1508,7 +1508,7 @@ JOIN::optimize_inner()
{
DBUG_ENTER("JOIN::optimize");
subq_exit_fl= false;
- do_send_rows = (unit->select_limit_cnt) ? 1 : 0;
+ do_send_rows = (unit->lim.get_select_limit()) ? 1 : 0;
DEBUG_SYNC(thd, "before_join_optimize");
@@ -1579,9 +1579,9 @@ JOIN::optimize_inner()
DBUG_RETURN(-1);
row_limit= ((select_distinct || order || group_list) ? HA_POS_ERROR :
- unit->select_limit_cnt);
+ unit->lim.get_select_limit());
/* select_limit is used to decide if we are likely to scan the whole table */
- select_limit= unit->select_limit_cnt;
+ select_limit= unit->lim.get_select_limit();
if (having || (select_options & OPTION_FOUND_ROWS))
select_limit= HA_POS_ERROR;
#ifdef HAVE_REF_TO_FIELDS // Not done yet
@@ -1790,9 +1790,10 @@ JOIN::optimize_inner()
thd->change_item_tree(&sel->having, having);
}
if (cond_value == Item::COND_FALSE || having_value == Item::COND_FALSE ||
- (!unit->select_limit_cnt && !(select_options & OPTION_FOUND_ROWS)))
+ (!unit->lim.get_select_limit() &&
+ !(select_options & OPTION_FOUND_ROWS)))
{ /* Impossible cond */
- if (unit->select_limit_cnt)
+ if (unit->lim.get_select_limit())
{
DBUG_PRINT("info", (having_value == Item::COND_FALSE ?
"Impossible HAVING" : "Impossible WHERE"));
@@ -3314,7 +3315,7 @@ bool JOIN::make_aggr_tables_info()
*/
sort_tab->filesort->limit=
(has_group_by || (join_tab + table_count > curr_tab + 1)) ?
- select_limit : unit->select_limit_cnt;
+ select_limit : unit->lim.get_select_limit();
}
if (!only_const_tables() &&
!join_tab[const_tables].filesort &&
@@ -3698,8 +3699,7 @@ JOIN::reinit()
{
DBUG_ENTER("JOIN::reinit");
- unit->offset_limit_cnt= (ha_rows)(select_lex->offset_limit ?
- select_lex->offset_limit->val_uint() : 0);
+ unit->lim.reset();
first_record= false;
group_sent= false;
@@ -5157,7 +5157,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
for (i= 0; i < join->table_count ; i++)
records*= join->best_positions[i].records_read ?
(ha_rows)join->best_positions[i].records_read : 1;
- set_if_smaller(records, unit->select_limit_cnt);
+ set_if_smaller(records, unit->lim.get_select_limit());
join->select_lex->increase_derived_records(records);
}
}
@@ -7446,7 +7446,7 @@ best_access_path(JOIN *join,
if (!best_key &&
idx == join->const_tables &&
s->table == join->sort_by_table &&
- join->unit->select_limit_cnt >= records)
+ join->unit->lim.get_select_limit() >= records)
join->sort_by_table= (TABLE*) 1; // Must use temporary table
DBUG_VOID_RETURN;
@@ -10799,7 +10799,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
!tab->loosescan_match_tab && // (1)
((cond && (!tab->keys.is_subset(tab->const_keys) && i > 0)) ||
(!tab->const_keys.is_clear_all() && i == join->const_tables &&
- join->unit->select_limit_cnt <
+ join->unit->lim.get_select_limit() <
join->best_positions[i].records_read &&
!(join->select_options & OPTION_FOUND_ROWS))))
{
@@ -10823,7 +10823,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
(join->select_options &
OPTION_FOUND_ROWS ?
HA_POS_ERROR :
- join->unit->select_limit_cnt), 0,
+ join->unit->lim.get_select_limit()), 0,
FALSE, FALSE) < 0)
{
/*
@@ -10837,7 +10837,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
(join->select_options &
OPTION_FOUND_ROWS ?
HA_POS_ERROR :
- join->unit->select_limit_cnt),0,
+ join->unit->lim.get_select_limit()),0,
FALSE, FALSE) < 0)
DBUG_RETURN(1); // Impossible WHERE
}
@@ -20581,7 +20581,7 @@ end_send(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
}
++join->send_records;
- if (join->send_records >= join->unit->select_limit_cnt &&
+ if (join->send_records >= join->unit->lim.get_select_limit() &&
!join->do_send_rows)
{
/*
@@ -20599,7 +20599,7 @@ end_send(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
DBUG_RETURN(NESTED_LOOP_QUERY_LIMIT);
}
}
- if (join->send_records >= join->unit->select_limit_cnt &&
+ if (join->send_records >= join->unit->lim.get_select_limit() &&
join->do_send_rows)
{
if (join->select_options & OPTION_FOUND_ROWS)
@@ -20740,13 +20740,13 @@ end_send_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */
if (end_of_records)
DBUG_RETURN(NESTED_LOOP_OK);
- if (join->send_records >= join->unit->select_limit_cnt &&
+ if (join->send_records >= join->unit->lim.get_select_limit() &&
join->do_send_rows)
{
if (!(join->select_options & OPTION_FOUND_ROWS))
DBUG_RETURN(NESTED_LOOP_QUERY_LIMIT); // Abort nicely
join->do_send_rows=0;
- join->unit->select_limit_cnt = HA_POS_ERROR;
+ join->unit->lim.set_unlimited();
}
else if (join->send_records >= join->fetch_limit)
{
@@ -20831,7 +20831,7 @@ end_write(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
if (!(join->select_options & OPTION_FOUND_ROWS))
DBUG_RETURN(NESTED_LOOP_QUERY_LIMIT);
join->do_send_rows=0;
- join->unit->select_limit_cnt = HA_POS_ERROR;
+ join->unit->lim.set_unlimited();
}
}
}
@@ -22142,7 +22142,9 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
(tab->join->select_options &
OPTION_FOUND_ROWS) ?
HA_POS_ERROR :
- tab->join->unit->select_limit_cnt,TRUE,
+ tab->join->unit->
+ lim.get_select_limit(),
+ TRUE,
TRUE, FALSE) <= 0;
if (res)
{
@@ -22244,7 +22246,7 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
select->test_quick_select(join->thd, tmp_map, 0,
join->select_options & OPTION_FOUND_ROWS ?
HA_POS_ERROR :
- join->unit->select_limit_cnt,
+ join->unit->lim.get_select_limit(),
TRUE, FALSE, FALSE);
if (cond_saved)
@@ -22671,7 +22673,7 @@ JOIN_TAB::remove_duplicates()
if (!field_count && !(join->select_options & OPTION_FOUND_ROWS) && !having)
{ // only const items with no OPTION_FOUND_ROWS
- join->unit->select_limit_cnt= 1; // Only send first row
+ join->unit->lim.set_single_row(); // Only send first row
DBUG_RETURN(false);
}
@@ -24859,7 +24861,7 @@ int JOIN::rollup_send_data(uint idx)
copy_ref_ptr_array(ref_ptrs, rollup.ref_pointer_arrays[i]);
if ((!having || having->val_int()))
{
- if (send_records < unit->select_limit_cnt && do_send_rows &&
+ if (send_records < unit->lim.get_select_limit() && do_send_rows &&
(res= result->send_data(rollup.fields[i])) > 0)
return 1;
if (!res)
diff --git a/sql/sql_union.cc b/sql/sql_union.cc
index ca8a5e7a8b3..d563301a339 100644
--- a/sql/sql_union.cc
+++ b/sql/sql_union.cc
@@ -110,11 +110,8 @@ int select_unit::send_data(List<Item> &values)
{
int rc;
int not_reported_error= 0;
- if (unit->offset_limit_cnt)
- { // using limit offset,count
- unit->offset_limit_cnt--;
- return 0;
- }
+ if (unit->lim.check_and_move_offset())
+ return 0; // using limit offset,count
if (thd->killed == ABORT_QUERY)
return 0;
if (table->no_rows_with_nulls)
@@ -883,8 +880,7 @@ bool st_select_lex_unit::prepare(TABLE_LIST *derived_arg,
else
{
sl->join->result= result;
- select_limit_cnt= HA_POS_ERROR;
- offset_limit_cnt= 0;
+ lim.set_unlimited();
if (!sl->join->procedure &&
result->prepare(sl->join->fields_list, this))
{
@@ -1317,7 +1313,7 @@ bool st_select_lex_unit::optimize()
if (sl->tvc)
{
sl->tvc->select_options=
- (select_limit_cnt == HA_POS_ERROR || sl->braces) ?
+ (lim.is_unlimited() || sl->braces) ?
sl->options & ~OPTION_FOUND_ROWS : sl->options | found_rows_for_union;
if (sl->tvc->optimize(thd))
{
@@ -1337,13 +1333,13 @@ bool st_select_lex_unit::optimize()
set_limit(sl);
if (sl == global_parameters() || describe)
{
- offset_limit_cnt= 0;
+ lim.remove_offset();
/*
We can't use LIMIT at this stage if we are using ORDER BY for the
whole query
*/
if (sl->order_list.first || describe)
- select_limit_cnt= HA_POS_ERROR;
+ lim.set_unlimited();
}
/*
@@ -1352,7 +1348,7 @@ bool st_select_lex_unit::optimize()
Otherwise, SQL_CALC_FOUND_ROWS should be done on all sub parts.
*/
sl->join->select_options=
- (select_limit_cnt == HA_POS_ERROR || sl->braces) ?
+ (lim.is_unlimited() || sl->braces) ?
sl->options & ~OPTION_FOUND_ROWS : sl->options | found_rows_for_union;
saved_error= sl->join->optimize();
@@ -1432,13 +1428,13 @@ bool st_select_lex_unit::exec()
set_limit(sl);
if (sl == global_parameters() || describe)
{
- offset_limit_cnt= 0;
+ lim.remove_offset();
/*
We can't use LIMIT at this stage if we are using ORDER BY for the
whole query
*/
if (sl->order_list.first || describe)
- select_limit_cnt= HA_POS_ERROR;
+ lim.set_unlimited();
}
/*
@@ -1449,14 +1445,14 @@ bool st_select_lex_unit::exec()
if (sl->tvc)
{
sl->tvc->select_options=
- (select_limit_cnt == HA_POS_ERROR || sl->braces) ?
+ (lim.is_unlimited() || sl->braces) ?
sl->options & ~OPTION_FOUND_ROWS : sl->options | found_rows_for_union;
saved_error= sl->tvc->optimize(thd);
}
else
{
- sl->join->select_options=
- (select_limit_cnt == HA_POS_ERROR || sl->braces) ?
+ sl->join->select_options=
+ (lim.is_unlimited() || sl->braces) ?
sl->options & ~OPTION_FOUND_ROWS : sl->options | found_rows_for_union;
saved_error= sl->join->optimize();
}
@@ -1478,9 +1474,7 @@ bool st_select_lex_unit::exec()
}
if (!sl->tvc)
saved_error= sl->join->error;
- offset_limit_cnt= (ha_rows)(sl->offset_limit ?
- sl->offset_limit->val_uint() :
- 0);
+ lim.reset();
if (likely(!saved_error))
{
examined_rows+= thd->get_examined_row_count();
@@ -1507,8 +1501,8 @@ bool st_select_lex_unit::exec()
DBUG_RETURN(1);
}
}
- if (found_rows_for_union && !sl->braces &&
- select_limit_cnt != HA_POS_ERROR)
+ if (found_rows_for_union && !sl->braces &&
+ !lim.is_unlimited())
{
/*
This is a union without braces. Remember the number of rows that
1
0
revision-id: 953ca199fb62bcd190d2af4d6177f986564ec1ad (mariadb-10.3.6-136-g953ca19)
parent(s): 27c3abde3071ad2010cbcda5b07435ad15364a70 be8709eb7bdf2a68a1c04fd8ab368113f5f39b63
author: Igor Babaev
committer: Igor Babaev
timestamp: 2019-02-12 23:19:43 -0800
message:
Merge branch '10.4' into bb-10.4-mdev17096
cmake/build_configurations/mysql_release.cmake | 3 +
cmake/wsrep.cmake | 20 +-
extra/innochecksum.cc | 138 +-
extra/mariabackup/fil_cur.cc | 32 +-
extra/mariabackup/fil_cur.h | 7 +-
extra/mariabackup/read_filt.cc | 2 +-
extra/mariabackup/read_filt.h | 2 +-
extra/mariabackup/write_filt.cc | 12 +-
extra/mariabackup/xtrabackup.cc | 37 +-
extra/mariabackup/xtrabackup.h | 9 +-
libmysqld/CMakeLists.txt | 1 +
mysql-test/main/failed_auth_unixsocket.result | 2 +-
mysql-test/main/failed_auth_unixsocket.test | 2 +-
mysql-test/main/information_schema.result | 3 +
.../main/information_schema_all_engines.result | 8 +-
mysql-test/main/mysql_upgrade-6984.result | 2 +-
mysql-test/main/mysql_upgrade-6984.test | 2 +-
mysql-test/main/mysqld--help.result | 8 +
mysql-test/main/opt_trace.result | 3499 ++++++++++++++++++++
mysql-test/main/opt_trace.test | 335 ++
mysql-test/main/opt_trace_index_merge.result | 249 ++
mysql-test/main/opt_trace_index_merge.test | 21 +
.../main/opt_trace_index_merge_innodb.result | 242 ++
mysql-test/main/opt_trace_index_merge_innodb.test | 31 +
mysql-test/main/opt_trace_security.result | 396 +++
mysql-test/main/opt_trace_security.test | 197 ++
mysql-test/main/type_timestamp.result | 9 +
mysql-test/main/type_timestamp.test | 8 +
mysql-test/suite/funcs_1/r/is_columns_is.result | 8 +
.../suite/funcs_1/r/is_columns_is_embedded.result | 8 +
mysql-test/suite/funcs_1/r/is_tables_is.result | 50 +
.../suite/funcs_1/r/is_tables_is_embedded.result | 50 +
mysql-test/suite/galera/disabled.def | 1 -
.../galera/r/galera_gcache_recover_manytrx.result | 2 +
mysql-test/suite/galera/r/galera_sst_rsync2.result | 2 +
.../galera/r/galera_var_load_data_splitting.result | 4 +
mysql-test/suite/galera/t/galera_sst_mysqldump.cnf | 2 -
.../r/galera_evs_suspect_timeout.result | 1 -
.../t/galera_evs_suspect_timeout.test | 2 +-
.../suite/innodb/r/alter_varchar_change.result | 18 +-
.../suite/innodb/r/instant_alter_bugs.result | 47 +
.../suite/innodb/r/instant_alter_import.result | 72 +
.../suite/innodb/t/alter_varchar_change.test | 18 +-
mysql-test/suite/innodb/t/instant_alter_bugs.test | 49 +
.../suite/innodb/t/instant_alter_import.test | 84 +
.../roles/i_s_applicable_roles_is_default.result | 2 +-
.../roles/i_s_applicable_roles_is_default.test | 2 +-
.../sys_vars/r/sysvars_server_embedded,32bit.rdiff | 319 +-
.../sys_vars/r/sysvars_server_embedded.result | 28 +
.../r/sysvars_server_notembedded,32bit.rdiff | 339 +-
.../sys_vars/r/sysvars_server_notembedded.result | 28 +
mysys/my_fopen.c | 5 +-
.../mysql-test/user_variables/basic.result | 2 +-
plugin/user_variables/user_variables.cc | 2 +-
scripts/mysql_install_db.sh | 29 +-
scripts/mysql_secure_installation.sh | 59 +-
scripts/mysql_system_tables_data.sql | 9 +-
scripts/wsrep_sst_mariabackup.sh | 4 +-
scripts/wsrep_sst_rsync.sh | 23 +-
sql/CMakeLists.txt | 1 +
sql/ha_sequence.cc | 2 +-
sql/handler.h | 1 +
sql/my_json_writer.cc | 75 +-
sql/my_json_writer.h | 425 ++-
sql/mysqld.cc | 9 +-
sql/mysqld.h | 3 +-
sql/opt_range.cc | 845 ++++-
sql/opt_range.h | 2 +-
sql/opt_subselect.cc | 66 +-
sql/opt_table_elimination.cc | 30 +-
sql/opt_trace.cc | 722 ++++
sql/opt_trace.h | 201 ++
sql/opt_trace_context.h | 92 +
sql/set_var.h | 9 +
sql/sp_head.cc | 13 +
sql/sp_head.h | 1 +
sql/sql_class.cc | 14 +
sql/sql_class.h | 15 +
sql/sql_derived.cc | 49 +-
sql/sql_explain.cc | 2 +-
sql/sql_parse.cc | 13 +-
sql/sql_parse.h | 2 +-
sql/sql_prepare.cc | 12 +
sql/sql_select.cc | 632 +++-
sql/sql_show.cc | 7 +
sql/sql_test.cc | 27 +-
sql/sql_test.h | 2 +
sql/sql_type.h | 8 +-
sql/sql_view.cc | 10 +
sql/sys_vars.cc | 18 +
sql/table.cc | 2 +
sql/wsrep_mysqld.cc | 15 +-
sql/wsrep_server_state.cc | 1 +
sql/wsrep_sst.cc | 17 +-
storage/innobase/btr/btr0btr.cc | 115 +-
storage/innobase/btr/btr0bulk.cc | 13 +-
storage/innobase/btr/btr0cur.cc | 179 +-
storage/innobase/btr/btr0defragment.cc | 18 +-
storage/innobase/btr/btr0pcur.cc | 2 +-
storage/innobase/btr/btr0scrub.cc | 22 +-
storage/innobase/btr/btr0sea.cc | 2 +-
storage/innobase/buf/buf0buf.cc | 166 +-
storage/innobase/buf/buf0dblwr.cc | 82 +-
storage/innobase/buf/buf0dump.cc | 8 +-
storage/innobase/buf/buf0flu.cc | 11 +-
storage/innobase/buf/buf0lru.cc | 68 +-
storage/innobase/buf/buf0rea.cc | 99 +-
storage/innobase/data/data0data.cc | 8 +-
storage/innobase/dict/dict0boot.cc | 2 +-
storage/innobase/dict/dict0crea.cc | 24 +-
storage/innobase/dict/dict0dict.cc | 56 +-
storage/innobase/dict/dict0stats.cc | 4 +-
storage/innobase/fil/fil0crypt.cc | 103 +-
storage/innobase/fil/fil0fil.cc | 140 +-
storage/innobase/fil/fil0pagecompress.cc | 3 +-
storage/innobase/fsp/fsp0file.cc | 43 +-
storage/innobase/fsp/fsp0fsp.cc | 441 +--
storage/innobase/fts/fts0fts.cc | 4 +-
storage/innobase/fts/fts0que.cc | 8 +-
storage/innobase/fut/fut0lst.cc | 64 +-
storage/innobase/gis/gis0rtree.cc | 8 +-
storage/innobase/gis/gis0sea.cc | 11 +-
storage/innobase/handler/ha_innodb.cc | 81 +-
storage/innobase/handler/handler0alter.cc | 41 +-
storage/innobase/handler/i_s.cc | 13 +-
storage/innobase/ibuf/ibuf0ibuf.cc | 346 +-
storage/innobase/include/btr0btr.h | 57 +-
storage/innobase/include/btr0btr.ic | 14 +-
storage/innobase/include/btr0cur.h | 16 +-
storage/innobase/include/btr0types.h | 10 +-
storage/innobase/include/buf0buf.h | 56 +-
storage/innobase/include/buf0rea.h | 37 +-
storage/innobase/include/dict0dict.h | 34 +-
storage/innobase/include/dict0dict.ic | 22 -
storage/innobase/include/fil0crypt.h | 41 +-
storage/innobase/include/fil0fil.h | 59 +-
storage/innobase/include/fsp0fsp.h | 106 +-
storage/innobase/include/fsp0fsp.ic | 69 +-
storage/innobase/include/fut0fut.h | 28 +-
storage/innobase/include/fut0fut.ic | 68 -
storage/innobase/include/ibuf0ibuf.h | 75 +-
storage/innobase/include/ibuf0ibuf.ic | 49 +-
storage/innobase/include/mem0mem.ic | 4 +-
storage/innobase/include/mtr0types.h | 9 +-
storage/innobase/include/os0file.h | 2 +-
storage/innobase/include/page0size.h | 197 --
storage/innobase/include/page0zip.h | 15 +-
storage/innobase/include/page0zip.ic | 21 +-
storage/innobase/include/row0ext.h | 9 +-
storage/innobase/include/trx0rseg.ic | 5 +-
storage/innobase/include/trx0sys.h | 2 +-
storage/innobase/include/trx0undo.ic | 8 +-
storage/innobase/lock/lock0lock.cc | 2 +-
storage/innobase/log/log0log.cc | 8 +-
storage/innobase/log/log0recv.cc | 32 +-
storage/innobase/mtr/mtr0mtr.cc | 2 +-
storage/innobase/os/os0file.cc | 2 -
storage/innobase/page/page0zip.cc | 17 +-
storage/innobase/rem/rem0rec.cc | 8 +-
storage/innobase/row/row0ext.cc | 32 +-
storage/innobase/row/row0ftsort.cc | 4 +-
storage/innobase/row/row0import.cc | 119 +-
storage/innobase/row/row0log.cc | 18 +-
storage/innobase/row/row0merge.cc | 17 +-
storage/innobase/row/row0mysql.cc | 12 +-
storage/innobase/row/row0purge.cc | 2 +-
storage/innobase/row/row0row.cc | 4 +-
storage/innobase/row/row0sel.cc | 16 +-
storage/innobase/row/row0upd.cc | 39 +-
storage/innobase/srv/srv0srv.cc | 2 -
storage/innobase/srv/srv0start.cc | 13 +-
storage/innobase/trx/trx0rec.cc | 34 +-
storage/innobase/trx/trx0rseg.cc | 20 +-
storage/innobase/trx/trx0undo.cc | 8 +-
wsrep-lib | 2 +-
175 files changed, 10544 insertions(+), 2956 deletions(-)
diff --cc sql/sql_select.cc
index 38b0e7e,06205a4..1f12490
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@@ -64,7 -64,8 +64,9 @@@
#include "sys_vars_shared.h"
#include "sp_head.h"
#include "sp_rcontext.h"
+#include "select_handler.h"
+ #include "my_json_writer.h"
+ #include "opt_trace.h"
/*
A key part number that means we're using a fulltext scan.
1
0
[Commits] 27c3abd: MDEV-17096 Pushdown of simple derived tables to storage engines
by IgorBabaev 13 Feb '19
by IgorBabaev 13 Feb '19
13 Feb '19
revision-id: 27c3abde3071ad2010cbcda5b07435ad15364a70 (mariadb-10.3.6-135-g27c3abd)
parent(s): 17d00d9a94da2c2b57fc7cf75036d92ee6dc9298
author: Igor Babaev
committer: Igor Babaev
timestamp: 2019-02-12 22:56:24 -0800
message:
MDEV-17096 Pushdown of simple derived tables to storage engines
MDEV-17631 select_handler for a full query pushdown
Added comments and file headers for files introduced in these tasks.
---
sql/derived_handler.cc | 63 ++++++++++++++++++++++++++-----
sql/derived_handler.h | 30 +++++++++++++--
sql/select_handler.cc | 43 +++++++++++++++++++++
sql/select_handler.h | 28 +++++++++++++-
sql/sql_derived.cc | 21 +++++++++++
sql/sql_lex.h | 2 +
sql/sql_select.cc | 26 +++++++++++++
sql/table.h | 6 +++
storage/federatedx/federatedx_pushdown.cc | 19 ++++++++++
9 files changed, 223 insertions(+), 15 deletions(-)
diff --git a/sql/derived_handler.cc b/sql/derived_handler.cc
index 1fa5e94..76fd736 100644
--- a/sql/derived_handler.cc
+++ b/sql/derived_handler.cc
@@ -1,18 +1,41 @@
+/*
+ Copyright (c) 2018, 2019 MariaDB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */
+
#include "mariadb.h"
#include "sql_priv.h"
#include "sql_select.h"
#include "derived_handler.h"
-void derived_handler::set_derived(TABLE_LIST *tbl)
-{
- derived= tbl;
- table= tbl->table;
- unit= tbl->derived;
- select= unit->first_select();
- tmp_table_param= select->next_select() ?
- ((select_unit *)(unit->result))->get_tmp_table_param() :
- &select->join->tmp_table_param;
-}
+
+/**
+ The methods of the Pushdown_derived class.
+
+ The objects of this class are used for pushdown of the derived tables
+ into engines. The main method of the class is Pushdown_derived::execute()
+ that initiates execution of the query specifying a derived by a foreign
+ engine, receives the rows of the result set and put them in a temporary
+ table on the server side.
+
+ The method uses only the functions of the derived_handle interface to do
+ this. The constructor of the class gets this interface as a parameter.
+
+ Currently a derived tables pushed into an engine is always materialized.
+ It could be changed if the cases when the tables is used as driving table.
+*/
+
Pushdown_derived::Pushdown_derived(TABLE_LIST *tbl, derived_handler *h)
: derived(tbl), handler(h)
@@ -20,11 +43,13 @@ Pushdown_derived::Pushdown_derived(TABLE_LIST *tbl, derived_handler *h)
is_analyze= handler->thd->lex->analyze_stmt;
}
+
Pushdown_derived::~Pushdown_derived()
{
delete handler;
}
+
int Pushdown_derived::execute()
{
int err;
@@ -82,3 +107,21 @@ int Pushdown_derived::execute()
DBUG_RETURN(-1); // Error not sent to client
}
+
+void derived_handler::print_error(int error, myf errflag)
+{
+ my_error(ER_GET_ERRNO, MYF(0), error, hton_name(ht)->str);
+}
+
+
+void derived_handler::set_derived(TABLE_LIST *tbl)
+{
+ derived= tbl;
+ table= tbl->table;
+ unit= tbl->derived;
+ select= unit->first_select();
+ tmp_table_param= select->next_select() ?
+ ((select_unit *)(unit->result))->get_tmp_table_param() :
+ &select->join->tmp_table_param;
+}
+
diff --git a/sql/derived_handler.h b/sql/derived_handler.h
index c312a93..171165b 100644
--- a/sql/derived_handler.h
+++ b/sql/derived_handler.h
@@ -1,3 +1,19 @@
+/*
+ Copyright (c) 2016, 2017 MariaDB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
#ifndef DERIVED_HANDLER_INCLUDED
#define DERIVED_HANDLER_INCLUDED
@@ -8,6 +24,13 @@ class TMP_TABLE_PARAM;
typedef class st_select_lex_unit SELECT_LEX_UNIT;
+/**
+ @class derived_handler
+
+ This interface class is to be used for execution of queries that specify
+ derived table by foreign engines
+*/
+
class derived_handler
{
public:
@@ -23,11 +46,12 @@ class derived_handler
*/
TABLE *table;
+ /* The parameters if the temporary table used at its creation */
TMP_TABLE_PARAM *tmp_table_param;
- SELECT_LEX_UNIT *unit;
+ SELECT_LEX_UNIT *unit; // Specifies the derived table
- SELECT_LEX *select;
+ SELECT_LEX *select; // The first select of the specification
derived_handler(THD *thd_arg, handlerton *ht_arg)
: thd(thd_arg), ht(ht_arg), derived(0),table(0), tmp_table_param(0),
@@ -53,7 +77,7 @@ class derived_handler
virtual int end_scan()=0;
/* Report errors */
- virtual void print_error(int error, myf errflag)=0;
+ virtual void print_error(int error, myf errflag);
void set_derived(TABLE_LIST *tbl);
};
diff --git a/sql/select_handler.cc b/sql/select_handler.cc
index 9a8d391..f020d2f 100644
--- a/sql/select_handler.cc
+++ b/sql/select_handler.cc
@@ -1,21 +1,55 @@
+/*
+ Copyright (c) 2018, 2019 MariaDB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */
+
#include "mariadb.h"
#include "sql_priv.h"
#include "sql_select.h"
#include "select_handler.h"
+/**
+ The methods of the Pushdown_select class.
+
+ The objects of this class are used for pushdown of the select queries
+ into engines. The main method of the class is Pushdown_select::execute()
+ that initiates execution of a select query by a foreign engine, receives the
+ rows of the result set, put it in a buffer of a temporary table and send
+ them from the buffer directly into output.
+
+ The method uses the functions of the select_handle interface to do this.
+ It also employes plus some helper functions to create the needed temporary
+ table and to send rows from the temporary table into output.
+ The constructor of the class gets the select_handler interface as a parameter.
+*/
+
+
Pushdown_select::Pushdown_select(SELECT_LEX *sel, select_handler *h)
: select(sel), handler(h)
{
is_analyze= handler->thd->lex->analyze_stmt;
}
+
Pushdown_select::~Pushdown_select()
{
delete handler;
select->select_h= NULL;
}
+
bool Pushdown_select::init()
{
List<Item> types;
@@ -38,6 +72,7 @@ bool Pushdown_select::init()
DBUG_RETURN(false);
}
+
bool Pushdown_select::send_result_set_metadata()
{
THD *thd= handler->thd;
@@ -59,6 +94,7 @@ bool Pushdown_select::send_result_set_metadata()
DBUG_RETURN(false);
}
+
bool Pushdown_select::send_data()
{
THD *thd= handler->thd;
@@ -83,6 +119,7 @@ bool Pushdown_select::send_data()
DBUG_RETURN(false);
}
+
bool Pushdown_select::send_eof()
{
THD *thd= handler->thd;
@@ -98,6 +135,7 @@ bool Pushdown_select::send_eof()
DBUG_RETURN(false);
}
+
int Pushdown_select::execute()
{
int err;
@@ -143,3 +181,8 @@ int Pushdown_select::execute()
handler->print_error(err, MYF(0));
DBUG_RETURN(-1); // Error not sent to client
}
+
+void select_handler::print_error(int error, myf errflag)
+{
+ my_error(ER_GET_ERRNO, MYF(0), error, hton_name(ht)->str);
+}
diff --git a/sql/select_handler.h b/sql/select_handler.h
index 19a1883..e2ad13b 100644
--- a/sql/select_handler.h
+++ b/sql/select_handler.h
@@ -1,20 +1,44 @@
+/*
+ Copyright (c) 2018, 2019 MariaDB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
#ifndef SELECT_HANDLER_INCLUDED
#define SELECT_HANDLER_INCLUDED
#include "mariadb.h"
#include "sql_priv.h"
+/**
+ @class select_handler
+
+ This interface class is to be used for execution of select queries
+ by foreign engines
+*/
+
class select_handler
{
public:
THD *thd;
handlerton *ht;
- SELECT_LEX *select;
+ SELECT_LEX *select; // Select to be excuted
/*
Temporary table where all results should be stored in record[0]
The table has a field for every item from the select_lex::item_list.
+ The table is actually never filled. Only its record buffer is used.
*/
TABLE *table;
@@ -42,7 +66,7 @@ class select_handler
virtual int end_scan() = 0;
/* Report errors */
- virtual void print_error(int error, myf errflag) = 0;
+ virtual void print_error(int error, myf errflag);
};
#endif /* SELECT_HANDLER_INCLUDED */
diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc
index 564049e..fbd8365 100644
--- a/sql/sql_derived.cc
+++ b/sql/sql_derived.cc
@@ -932,6 +932,7 @@ bool mysql_derived_optimize(THD *thd, LEX *lex, TABLE_LIST *derived)
if (derived->is_materialized_derived() && derived->dt_handler)
{
+ /* Create an object for execution of the query specifying the table */
if (!(derived->pushdown_derived=
new (thd->mem_root) Pushdown_derived(derived, derived->dt_handler)))
{
@@ -1151,6 +1152,7 @@ bool mysql_derived_fill(THD *thd, LEX *lex, TABLE_LIST *derived)
int res;
if (unit->executed)
DBUG_RETURN(FALSE);
+ /* Execute the query that specifies the derived table by a foreign engine */
res= derived->pushdown_derived->execute();
unit->executed= true;
delete derived->pushdown_derived;
@@ -1457,6 +1459,25 @@ bool pushdown_cond_for_derived(THD *thd, Item *cond, TABLE_LIST *derived)
}
+/**
+ @brief
+ Look for provision of the derived_handler interface by a foreign engine
+
+ @param thd The thread handler
+
+ @details
+ The function looks through its tables of the query that specifies this
+ derived table searching for a table whose handlerton owns a
+ create_derived call-back function. If the call of this function returns
+ a derived_handler interface object then the server will push the query
+ specifying the derived table into this engine.
+ This is a responsibility of the create_derived call-back function to
+ check whether the engine can execute the query.
+
+ @retval the found derived_handler if the search is successful
+ 0 otherwise
+*/
+
derived_handler *TABLE_LIST::find_derived_handler(THD *thd)
{
if (!derived || is_recursive_with_table())
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index c33307c..25e8ad3 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -1257,7 +1257,9 @@ class st_select_lex: public st_select_lex_node
table_value_constr *tvc;
bool in_tvc;
+ /* The interface employed to execute the select query by a foreign engine */
select_handler *select_h;
+ /* The object used to organize execution of the query by a foreign engine */
Pushdown_select *pushdown_select;
/** System Versioning */
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index e9040e1..38b0e7e 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -1444,7 +1444,10 @@ int JOIN::optimize()
if (select_lex->pushdown_select)
{
if (!(select_options & SELECT_DESCRIBE))
+ {
+ /* Prepare to execute the query pushed into a foreign engine */
res= select_lex->pushdown_select->init();
+ }
with_two_phase_optimization= false;
}
else if (optimization_state == JOIN::OPTIMIZATION_PHASE_1_DONE)
@@ -4074,6 +4077,7 @@ void JOIN::exec_inner()
}
else if (select_lex->pushdown_select)
{
+ /* Execute the query pushed into a foreign engine */
error= select_lex->pushdown_select->execute();
DBUG_VOID_RETURN;
}
@@ -4288,9 +4292,11 @@ mysql_select(THD *thd,
}
}
+ /* Look for a table owned by an engine with the select_handler interface */
select_lex->select_h= select_lex->find_select_handler(thd);
if (select_lex->select_h)
{
+ /* Create a Pushdown_select object for later execution of the query */
if (!(select_lex->pushdown_select=
new (thd->mem_root) Pushdown_select(select_lex,
select_lex->select_h)))
@@ -27620,6 +27626,26 @@ Item *remove_pushed_top_conjuncts(THD *thd, Item *cond)
return cond;
}
+
+/**
+ @brief
+ Look for provision of the select_handler interface by a foreign engine
+
+ @param thd The thread handler
+
+ @details
+ The function checks that this is an upper level select and if so looks
+ through its tables searching for one whose handlerton owns a
+ create_select call-back function. If the call of this function returns
+ a select_handler interface object then the server will push the select
+ query into this engine.
+ This is a responsibility of the create_select call-back function to
+ check whether the engine can execute the query.
+
+ @retval the found select_handler if the search is successful
+ 0 otherwise
+*/
+
select_handler *SELECT_LEX::find_select_handler(THD *thd)
{
if (next_select())
diff --git a/sql/table.h b/sql/table.h
index f7bcdaa..40dd752 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -2136,8 +2136,14 @@ struct TABLE_LIST
TABLE_LIST * next_with_rec_ref;
bool is_derived_with_recursive_reference;
bool block_handle_derived;
+ /* The interface employed to materialize the table by a foreign engine */
derived_handler *dt_handler;
+ /* The text of the query specifying the derived table */
LEX_CSTRING derived_spec;
+ /*
+ The object used to organize execution of the query that specifies
+ the derived table by a foreign engine
+ */
Pushdown_derived *pushdown_derived;
ST_SCHEMA_TABLE *schema_table; /* Information_schema table */
st_select_lex *schema_select_lex;
diff --git a/storage/federatedx/federatedx_pushdown.cc b/storage/federatedx/federatedx_pushdown.cc
index 906d87c..2bcee94 100644
--- a/storage/federatedx/federatedx_pushdown.cc
+++ b/storage/federatedx/federatedx_pushdown.cc
@@ -16,6 +16,25 @@
/* !!! For inclusion into ha_federatedx.cc */
+
+/*
+ This is a quick a dirty implemention of the derived_handler and select_handler
+ interfaces to be used to push select queries and the queries specifying
+ derived tables into FEDERATEDX engine.
+ The functions
+ create_federatedx_derived_handler and
+ create_federatedx_select_handler
+ that return the corresponding interfaces for pushdown capabilities do
+ not check a lot of things. In particular they do not check that the tables
+ of the pushed queries belong to the same foreign server.
+
+ The implementation is provided purely for testing purposes.
+ The pushdown capabilities are enabled by turning on the plugin system
+ variable federated_pushdown:
+ set global federated_pushdown=1;
+*/
+
+
static derived_handler*
create_federatedx_derived_handler(THD* thd, TABLE_LIST *derived)
{
1
0