[Commits] fcbb6badf82: Update test results after swithcing to InnoDB-like trx isolation
by Sergei Petrunia 21 Jan '19
by Sergei Petrunia 21 Jan '19
21 Jan '19
revision-id: fcbb6badf821e040d40ed23f15d30328324125e2 (fb-prod201801-193-gfcbb6badf82)
parent(s): 4da9cb93fb8c291a0023451963c15fd2971b0210
author: Sergei Petrunia
committer: Sergei Petrunia
timestamp: 2019-01-22 00:53:40 +0300
message:
Update test results after swithcing to InnoDB-like trx isolation
---
...issue243_transactionStatus-range_locking.result | 151 +++++++++++++++++++++
.../r/level_repeatable_read-range_locking.result | 106 +++++++++++++++
mysql-test/suite/rocksdb/t/issue111.test | 4 +
.../issue243_transactionStatus-range_locking.test | 10 ++
.../rocksdb/t/issue243_transactionStatus.test | 4 +
.../t/level_repeatable_read-range_locking.test | 9 ++
.../suite/rocksdb/t/level_repeatable_read.test | 3 +
7 files changed, 287 insertions(+)
diff --git a/mysql-test/suite/rocksdb/r/issue243_transactionStatus-range_locking.result b/mysql-test/suite/rocksdb/r/issue243_transactionStatus-range_locking.result
new file mode 100644
index 00000000000..28bbcb48c96
--- /dev/null
+++ b/mysql-test/suite/rocksdb/r/issue243_transactionStatus-range_locking.result
@@ -0,0 +1,151 @@
+DROP TABLE IF EXISTS t1;
+CREATE TABLE t1 (
+id INT,
+val1 INT,
+val2 INT,
+PRIMARY KEY (id)
+) ENGINE=rocksdb;
+INSERT INTO t1 VALUES(1,1,1),(2,1,2);
+SELECT * FROM t1;
+id val1 val2
+1 1 1
+2 1 2
+UPDATE t1 SET val1=2 WHERE id=2;
+SELECT * FROM t1;
+id val1 val2
+1 1 1
+2 2 2
+SHOW ENGINE rocksdb TRANSACTION STATUS;
+Type Name Status
+rocksdb
+============================================================
+TIMESTAMP ROCKSDB TRANSACTION MONITOR OUTPUT
+============================================================
+---------
+SNAPSHOTS
+---------
+LIST OF SNAPSHOTS FOR EACH SESSION:
+----------LATEST DETECTED DEADLOCKS----------
+-----------------------------------------
+END OF ROCKSDB TRANSACTION MONITOR OUTPUT
+=========================================
+
+SET AUTOCOMMIT=0;
+START TRANSACTION;
+INSERT INTO t1 VALUES(20,1,1),(30,30,30);
+SELECT * FROM t1;
+id val1 val2
+1 1 1
+2 2 2
+20 1 1
+30 30 30
+UPDATE t1 SET val1=20, val2=20 WHERE id=20;
+SELECT * FROM t1;
+id val1 val2
+1 1 1
+2 2 2
+20 20 20
+30 30 30
+DELETE FROM t1 WHERE id=30;
+SHOW ENGINE rocksdb TRANSACTION STATUS;
+Type Name Status
+rocksdb
+============================================================
+TIMESTAMP ROCKSDB TRANSACTION MONITOR OUTPUT
+============================================================
+---------
+SNAPSHOTS
+---------
+LIST OF SNAPSHOTS FOR EACH SESSION:
+---SNAPSHOT, ACTIVE NUM sec
+MySQL thread id TID, OS thread handle PTR, query id QID localhost root ACTION
+SHOW ENGINE rocksdb TRANSACTION STATUS
+lock count 8, write count 4
+insert count 2, update count 1, delete count 1
+----------LATEST DETECTED DEADLOCKS----------
+-----------------------------------------
+END OF ROCKSDB TRANSACTION MONITOR OUTPUT
+=========================================
+
+ROLLBACK;
+SHOW ENGINE rocksdb TRANSACTION STATUS;
+Type Name Status
+rocksdb
+============================================================
+TIMESTAMP ROCKSDB TRANSACTION MONITOR OUTPUT
+============================================================
+---------
+SNAPSHOTS
+---------
+LIST OF SNAPSHOTS FOR EACH SESSION:
+----------LATEST DETECTED DEADLOCKS----------
+-----------------------------------------
+END OF ROCKSDB TRANSACTION MONITOR OUTPUT
+=========================================
+
+START TRANSACTION;
+INSERT INTO t1 VALUES(40,40,40);
+SHOW ENGINE rocksdb TRANSACTION STATUS;
+Type Name Status
+rocksdb
+============================================================
+TIMESTAMP ROCKSDB TRANSACTION MONITOR OUTPUT
+============================================================
+---------
+SNAPSHOTS
+---------
+LIST OF SNAPSHOTS FOR EACH SESSION:
+----------LATEST DETECTED DEADLOCKS----------
+-----------------------------------------
+END OF ROCKSDB TRANSACTION MONITOR OUTPUT
+=========================================
+
+COMMIT;
+SHOW ENGINE rocksdb TRANSACTION STATUS;
+Type Name Status
+rocksdb
+============================================================
+TIMESTAMP ROCKSDB TRANSACTION MONITOR OUTPUT
+============================================================
+---------
+SNAPSHOTS
+---------
+LIST OF SNAPSHOTS FOR EACH SESSION:
+----------LATEST DETECTED DEADLOCKS----------
+-----------------------------------------
+END OF ROCKSDB TRANSACTION MONITOR OUTPUT
+=========================================
+
+SET AUTOCOMMIT=1;
+DROP TABLE t1;
+DROP TABLE IF EXISTS t2;
+CREATE TABLE t2 (
+id1 INT,
+id2 INT,
+value INT,
+PRIMARY KEY (id1),
+KEY (id2)
+) ENGINE=rocksdb;
+SET AUTOCOMMIT=0;
+START TRANSACTION;
+INSERT INTO t2 VALUES(1,2,0),(10,20,30);
+UPDATE t2 SET value=3 WHERE id2=2;
+DELETE FROM t2 WHERE id1=10;
+SHOW ENGINE rocksdb TRANSACTION STATUS;
+Type Name Status
+rocksdb
+============================================================
+TIMESTAMP ROCKSDB TRANSACTION MONITOR OUTPUT
+============================================================
+---------
+SNAPSHOTS
+---------
+LIST OF SNAPSHOTS FOR EACH SESSION:
+----------LATEST DETECTED DEADLOCKS----------
+-----------------------------------------
+END OF ROCKSDB TRANSACTION MONITOR OUTPUT
+=========================================
+
+ROLLBACK;
+SET AUTOCOMMIT=1;
+DROP TABLE t2;
diff --git a/mysql-test/suite/rocksdb/r/level_repeatable_read-range_locking.result b/mysql-test/suite/rocksdb/r/level_repeatable_read-range_locking.result
new file mode 100644
index 00000000000..3c5f1c2bf7c
--- /dev/null
+++ b/mysql-test/suite/rocksdb/r/level_repeatable_read-range_locking.result
@@ -0,0 +1,106 @@
+DROP TABLE IF EXISTS t1;
+connect con1,localhost,root,,;
+SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
+connect con2,localhost,root,,;
+SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
+connection con1;
+CREATE TABLE t1 (a INT, pk INT AUTO_INCREMENT PRIMARY KEY) ENGINE=rocksdb;
+START TRANSACTION;
+SELECT a FROM t1;
+a
+connection con2;
+BEGIN;
+INSERT INTO t1 (a) VALUES(1);
+connection con1;
+SELECT a FROM t1;
+a
+connection con2;
+INSERT INTO t1 (a) VALUES (2);
+connection con1;
+SELECT a FROM t1;
+a
+INSERT INTO t1 (a) SELECT a+100 FROM t1;
+SELECT a FROM t1;
+a
+connection con2;
+SELECT a FROM t1;
+a
+1
+2
+COMMIT;
+SELECT a FROM t1;
+a
+1
+2
+connection con1;
+SELECT a FROM t1;
+a
+INSERT INTO t1 (a) SELECT a+200 FROM t1;
+SELECT a FROM t1;
+a
+201
+202
+COMMIT;
+SELECT a FROM t1;
+a
+1
+2
+201
+202
+connection con2;
+SELECT a FROM t1;
+a
+1
+2
+201
+202
+connection default;
+CREATE TABLE t2 (a INT PRIMARY KEY) ENGINE=rocksdb;
+INSERT INTO t2 (a) VALUES (1);
+COMMIT;
+connection con1;
+BEGIN;
+SELECT a from t2;
+a
+1
+INSERT INTO t2 (a) VALUES (1), (3);
+ERROR 23000: Duplicate entry '1' for key 'PRIMARY'
+connection con2;
+INSERT INTO t2 (a) VALUES (2);
+COMMIT;
+connection con1;
+SELECT a from t2;
+a
+1
+COMMIT;
+connection default;
+disconnect con1;
+disconnect con2;
+DROP TABLE t1;
+DROP TABLE t2;
+CREATE TABLE t3 (
+pk int unsigned PRIMARY KEY,
+count int unsigned DEFAULT '0'
+) ENGINE=ROCKSDB;
+connect con1,localhost,root,,;
+SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
+connect con2,localhost,root,,;
+SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
+connection con1;
+BEGIN;
+SELECT * FROM t3;
+pk count
+connection con2;
+BEGIN;
+INSERT INTO t3 (pk) VALUES(1) ON DUPLICATE KEY UPDATE count=count+1;
+COMMIT;
+connection con1;
+INSERT INTO t3 (pk) VALUES(1) ON DUPLICATE KEY UPDATE count=count+1;
+COMMIT;
+SELECT count FROM t3;
+count
+0
+connection default;
+disconnect con1;
+disconnect con2;
+DROP TABLE t3;
diff --git a/mysql-test/suite/rocksdb/t/issue111.test b/mysql-test/suite/rocksdb/t/issue111.test
index 671ea4708d6..3657e977a70 100644
--- a/mysql-test/suite/rocksdb/t/issue111.test
+++ b/mysql-test/suite/rocksdb/t/issue111.test
@@ -1,5 +1,9 @@
--source include/have_rocksdb.inc
+# The testcase here assumes key tracking is present
+# (and range locking uses InnoDB-like approach, "DMLs use Read Commited")
+--source suite/rocksdb/include/not_range_locking.inc
+
connect (con2,localhost,root,,);
connection default;
diff --git a/mysql-test/suite/rocksdb/t/issue243_transactionStatus-range_locking.test b/mysql-test/suite/rocksdb/t/issue243_transactionStatus-range_locking.test
new file mode 100644
index 00000000000..465fb9099da
--- /dev/null
+++ b/mysql-test/suite/rocksdb/t/issue243_transactionStatus-range_locking.test
@@ -0,0 +1,10 @@
+#
+# A range-locking variant of issue243_transactionStatus.test
+
+--source include/have_rocksdb.inc
+--source suite/rocksdb/include/have_range_locking.inc
+
+let $forced_range_locking=1;
+--source issue243_transactionStatus.test
+
+
diff --git a/mysql-test/suite/rocksdb/t/issue243_transactionStatus.test b/mysql-test/suite/rocksdb/t/issue243_transactionStatus.test
index 0997bde3f49..c07a3c423df 100644
--- a/mysql-test/suite/rocksdb/t/issue243_transactionStatus.test
+++ b/mysql-test/suite/rocksdb/t/issue243_transactionStatus.test
@@ -1,5 +1,9 @@
--source include/have_rocksdb.inc
+if (!$forced_range_locking) {
+--source suite/rocksdb/include/not_range_locking.inc
+}
+
--disable_warnings
DROP TABLE IF EXISTS t1;
--enable_warnings
diff --git a/mysql-test/suite/rocksdb/t/level_repeatable_read-range_locking.test b/mysql-test/suite/rocksdb/t/level_repeatable_read-range_locking.test
new file mode 100644
index 00000000000..6c42c7be12c
--- /dev/null
+++ b/mysql-test/suite/rocksdb/t/level_repeatable_read-range_locking.test
@@ -0,0 +1,9 @@
+--source include/have_rocksdb.inc
+
+# Range locking uses InnoDB-like transaction isolation, which
+# means the results differ from "true" Repeatable Read.
+--source suite/rocksdb/include/have_range_locking.inc
+
+let $trx_isolation = REPEATABLE READ;
+--source transaction_isolation.inc
+
diff --git a/mysql-test/suite/rocksdb/t/level_repeatable_read.test b/mysql-test/suite/rocksdb/t/level_repeatable_read.test
index cf29073f69e..b81dcf31ab1 100644
--- a/mysql-test/suite/rocksdb/t/level_repeatable_read.test
+++ b/mysql-test/suite/rocksdb/t/level_repeatable_read.test
@@ -1,5 +1,8 @@
--source include/have_rocksdb.inc
+# See level_repeatable_read-range_locking variant
+--source suite/rocksdb/include/not_range_locking.inc
+
let $trx_isolation = REPEATABLE READ;
--source transaction_isolation.inc
1
0
[Commits] 4da9cb93fb8: Use InnoDB-like transaction isolation with Range Locking mode.
by Sergei Petrunia 21 Jan '19
by Sergei Petrunia 21 Jan '19
21 Jan '19
revision-id: 4da9cb93fb8c291a0023451963c15fd2971b0210 (fb-prod201801-192-g4da9cb93fb8)
parent(s): 70d97cc103fd98c7a4952e7b3a54f272fa7b36f4
author: Sergei Petrunia
committer: Sergei Petrunia
timestamp: 2019-01-21 21:36:09 +0300
message:
Use InnoDB-like transaction isolation with Range Locking mode.
DML statements (UPDATE/DELETE/..) will always read the latest committed
data (as opposed to transaction's snapshot).
---
mysql-test/suite/rocksdb/r/range_locking.result | 109 ++++++++++++++++++++++++
mysql-test/suite/rocksdb/t/range_locking.test | 95 +++++++++++++++++++++
storage/rocksdb/ha_rocksdb.cc | 81 +++++++++++++++---
3 files changed, 271 insertions(+), 14 deletions(-)
diff --git a/mysql-test/suite/rocksdb/r/range_locking.result b/mysql-test/suite/rocksdb/r/range_locking.result
index a43f7d668d4..b0217d5269a 100644
--- a/mysql-test/suite/rocksdb/r/range_locking.result
+++ b/mysql-test/suite/rocksdb/r/range_locking.result
@@ -192,3 +192,112 @@ rollback;
disconnect con1;
connection default;
drop table t0,t1;
+#
+# Transaction isolation test
+#
+create table t1 (pk int primary key, a int) engine=rocksdb;
+insert into t1 values (1,1),(2,2),(3,3);
+connect con1,localhost,root,,;
+# TRX1: Start, Allocate a snapshot
+connection con1;
+begin;
+select * from t1;
+pk a
+1 1
+2 2
+3 3
+# TRX2: Make a change that TRX1 will not see
+connection default;
+update t1 set a=2222 where pk=2;
+# TRX1: Now, make a change that would overwrite TRX2'x change and commit
+connection con1;
+update t1 set a=a+1 where pk=2;
+commit;
+# Examine the result:
+# pk=2, a=2223 means UPDATE in TRX1 used "read committed" (InnoDB-like isolation)
+# pk=2, a=3 means UPDATE in TRX1 silently overwrote TRX2
+# (and with key tracking, one would get an error on the second UPDATE)
+connection default;
+select * from t1;
+pk a
+1 1
+2 2223
+3 3
+disconnect con1;
+connection default;
+drop table t1;
+#
+# Same test as above, but check the range scan
+#
+create table t1 (pk int primary key, a int) engine=rocksdb;
+insert into t1 values (1,1),(2,2),(3,3),(4,4),(5,5),(6,6);
+connect con1,localhost,root,,;
+# TRX1: Start, Allocate a snapshot
+connection con1;
+begin;
+select * from t1;
+pk a
+1 1
+2 2
+3 3
+4 4
+5 5
+6 6
+# TRX2: Make a change that TRX1 will not see
+connection default;
+update t1 set a=2222 where pk between 3 and 5;
+# TRX1: Now, make a change that would overwrite TRX2'x change and commit
+connection con1;
+update t1 set a=a+1 where pk between 3 and 5;
+commit;
+# Examine the result:
+# pk={3,4,5} a=2223 means UPDATE in TRX1 used "read committed" (InnoDB-like isolation)
+connection default;
+select * from t1;
+pk a
+1 1
+2 2
+3 2223
+4 2223
+5 2223
+6 6
+disconnect con1;
+connection default;
+drop table t1;
+#
+# Same as above, but test SELECT FOR UPDATE.
+#
+create table t1 (pk int primary key, a int) engine=rocksdb;
+insert into t1 values (1,1),(2,2),(3,3),(4,4),(5,5),(6,6);
+connect con1,localhost,root,,;
+# TRX1: Start, Allocate a snapshot
+connection con1;
+begin;
+select * from t1;
+pk a
+1 1
+2 2
+3 3
+4 4
+5 5
+6 6
+# TRX2: Make a change that TRX1 will not see
+connection default;
+update t1 set a=222 where pk=2;
+update t1 set a=333 where pk=3;
+# TRX1: Check what select [FOR UPDATE] sees
+connection con1;
+select * from t1 where pk in (2,3);
+pk a
+2 2
+3 3
+select * from t1 where pk=2 for update;
+pk a
+2 222
+select * from t1 where pk=2;
+pk a
+2 2
+commit;
+disconnect con1;
+connection default;
+drop table t1;
diff --git a/mysql-test/suite/rocksdb/t/range_locking.test b/mysql-test/suite/rocksdb/t/range_locking.test
index 5c21f374bac..e1e8ac92bdc 100644
--- a/mysql-test/suite/rocksdb/t/range_locking.test
+++ b/mysql-test/suite/rocksdb/t/range_locking.test
@@ -211,3 +211,98 @@ disconnect con1;
connection default;
drop table t0,t1;
+--echo #
+--echo # Transaction isolation test
+--echo #
+
+create table t1 (pk int primary key, a int) engine=rocksdb;
+insert into t1 values (1,1),(2,2),(3,3);
+
+connect (con1,localhost,root,,);
+
+--echo # TRX1: Start, Allocate a snapshot
+connection con1;
+begin;
+select * from t1;
+
+--echo # TRX2: Make a change that TRX1 will not see
+connection default;
+update t1 set a=2222 where pk=2;
+
+--echo # TRX1: Now, make a change that would overwrite TRX2'x change and commit
+connection con1;
+update t1 set a=a+1 where pk=2;
+commit;
+
+--echo # Examine the result:
+--echo # pk=2, a=2223 means UPDATE in TRX1 used "read committed" (InnoDB-like isolation)
+--echo # pk=2, a=3 means UPDATE in TRX1 silently overwrote TRX2
+--echo # (and with key tracking, one would get an error on the second UPDATE)
+connection default;
+select * from t1;
+
+disconnect con1;
+connection default;
+drop table t1;
+
+--echo #
+--echo # Same test as above, but check the range scan
+--echo #
+
+create table t1 (pk int primary key, a int) engine=rocksdb;
+insert into t1 values (1,1),(2,2),(3,3),(4,4),(5,5),(6,6);
+
+connect (con1,localhost,root,,);
+
+--echo # TRX1: Start, Allocate a snapshot
+connection con1;
+begin;
+select * from t1;
+
+--echo # TRX2: Make a change that TRX1 will not see
+connection default;
+update t1 set a=2222 where pk between 3 and 5;
+
+--echo # TRX1: Now, make a change that would overwrite TRX2'x change and commit
+connection con1;
+update t1 set a=a+1 where pk between 3 and 5;
+commit;
+
+--echo # Examine the result:
+--echo # pk={3,4,5} a=2223 means UPDATE in TRX1 used "read committed" (InnoDB-like isolation)
+connection default;
+select * from t1;
+
+disconnect con1;
+connection default;
+drop table t1;
+
+--echo #
+--echo # Same as above, but test SELECT FOR UPDATE.
+--echo #
+create table t1 (pk int primary key, a int) engine=rocksdb;
+insert into t1 values (1,1),(2,2),(3,3),(4,4),(5,5),(6,6);
+
+connect (con1,localhost,root,,);
+
+--echo # TRX1: Start, Allocate a snapshot
+connection con1;
+begin;
+select * from t1;
+
+--echo # TRX2: Make a change that TRX1 will not see
+connection default;
+update t1 set a=222 where pk=2;
+update t1 set a=333 where pk=3;
+
+--echo # TRX1: Check what select [FOR UPDATE] sees
+connection con1;
+select * from t1 where pk in (2,3);
+select * from t1 where pk=2 for update;
+select * from t1 where pk=2;
+
+commit;
+
+disconnect con1;
+connection default;
+drop table t1;
diff --git a/storage/rocksdb/ha_rocksdb.cc b/storage/rocksdb/ha_rocksdb.cc
index c4f611a663f..f6f11ad4133 100644
--- a/storage/rocksdb/ha_rocksdb.cc
+++ b/storage/rocksdb/ha_rocksdb.cc
@@ -2101,8 +2101,39 @@ protected:
virtual void do_set_savepoint() = 0;
virtual void do_rollback_to_savepoint() = 0;
+ private:
+ /*
+ If true, the current statement should not use a snapshot for reading.
+ Note that in a multi-statement transaction, the snapshot may have been
+ allocated by another statement.
+ */
+ bool m_stmt_ignores_snapshot = false;
+
+ /* Snapshot-ignore mode will put away m_reads_opts.snapshot here: */
+ const rocksdb::Snapshot *m_saved_snapshot;
+
public:
+
+ void start_ignore_snapshot() {
+ // note: this may be called several times for the same statement
+ if (!m_stmt_ignores_snapshot) {
+ m_saved_snapshot = m_read_opts.snapshot;
+ m_read_opts.snapshot = nullptr;
+ m_stmt_ignores_snapshot= true;
+ }
+ }
+
+ void end_ignore_snapshot_if_needed() {
+ if (m_stmt_ignores_snapshot) {
+ m_stmt_ignores_snapshot = false;
+ m_read_opts.snapshot = m_saved_snapshot;
+ m_saved_snapshot = nullptr;
+ }
+ }
+ bool in_snapshot_ignore_mode() const { return m_stmt_ignores_snapshot; }
+
rocksdb::ReadOptions m_read_opts;
+
const char *m_mysql_log_file_name;
my_off_t m_mysql_log_offset;
const char *m_mysql_gtid;
@@ -2596,7 +2627,7 @@ public:
virtual bool is_tx_started() const = 0;
virtual void start_tx() = 0;
- virtual void start_stmt() = 0;
+ virtual void start_stmt(bool is_dml_statement) = 0;
void set_initial_savepoint() {
/*
@@ -2849,7 +2880,7 @@ public:
}
void acquire_snapshot(bool acquire_now) override {
- if (m_read_opts.snapshot == nullptr) {
+ if (m_read_opts.snapshot == nullptr && !in_snapshot_ignore_mode()) {
const auto thd_ss = std::static_pointer_cast<Rdb_explicit_snapshot>(
m_thd->get_explicit_snapshot());
if (thd_ss) {
@@ -2964,7 +2995,7 @@ public:
if (value != nullptr) {
value->Reset();
- }
+ } // psergey-todo: m_read_opts.snapshot below!
return m_rocksdb_tx->GetForUpdate(m_read_opts, column_family, key, value,
exclusive);
}
@@ -3028,13 +3059,25 @@ public:
/*
Start a statement inside a multi-statement transaction.
- @todo: are we sure this is called once (and not several times) per
- statement start?
+ @note: If a statement uses N tables, this function will be called N times,
+ for each TABLE object that is used.
For hooking to start of statement that is its own transaction, see
ha_rocksdb::external_lock().
*/
- void start_stmt() override {
+ void start_stmt(bool is_dml_statement) override {
+
+ if (rocksdb_use_range_locking && is_dml_statement) {
+ /*
+ In Range Locking mode, RocksDB does not do "key tracking".
+ Use InnoDB-like concurrency mode: make the DML statements always read
+ the latest data (instead of using transaction's snapshot).
+ This "downgrades" the transaction isolation to READ-COMMITTED on the
+ master, but in return the actions can be replayed on the slave.
+ */
+ start_ignore_snapshot();
+ }
+
// Set the snapshot to delayed acquisition (SetSnapshotOnNextOperation)
acquire_snapshot(false);
}
@@ -3270,7 +3313,7 @@ public:
set_initial_savepoint();
}
- void start_stmt() override {}
+ void start_stmt(bool is_dml_statement) override {}
void rollback_stmt() override {
if (m_batch)
@@ -3484,8 +3527,10 @@ static int rocksdb_prepare(handlerton *const hton, THD *const thd,
DEBUG_SYNC(thd, "rocksdb.prepared");
}
- else
+ else {
tx->make_stmt_savepoint_permanent();
+ tx->end_ignore_snapshot_if_needed();
+ }
return HA_EXIT_SUCCESS;
}
@@ -3660,6 +3705,7 @@ static int rocksdb_commit(handlerton *const hton, THD *const thd,
*/
tx->set_tx_failed(false);
tx->make_stmt_savepoint_permanent();
+ tx->end_ignore_snapshot_if_needed();
}
if (my_core::thd_tx_isolation(thd) <= ISO_READ_COMMITTED) {
@@ -3698,6 +3744,7 @@ static int rocksdb_rollback(handlerton *const hton, THD *const thd,
*/
tx->rollback_stmt();
+ tx->end_ignore_snapshot_if_needed();
tx->set_tx_failed(true);
}
@@ -4242,13 +4289,19 @@ static bool rocksdb_show_status(handlerton *const hton, THD *const thd,
return res;
}
+
+/*
+ @param is_dml_statement If true, we are is a DML statement
+*/
+
static inline void rocksdb_register_tx(handlerton *const hton, THD *const thd,
- Rdb_transaction *const tx) {
+ Rdb_transaction *const tx,
+ bool is_dml_stmt) {
DBUG_ASSERT(tx != nullptr);
trans_register_ha(thd, FALSE, rocksdb_hton);
if (my_core::thd_test_options(thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) {
- tx->start_stmt();
+ tx->start_stmt(is_dml_stmt);
trans_register_ha(thd, TRUE, rocksdb_hton);
}
}
@@ -4344,7 +4397,7 @@ static int rocksdb_start_tx_and_assign_read_view(
DBUG_ASSERT(!tx->has_snapshot());
tx->set_tx_read_only(true);
- rocksdb_register_tx(hton, thd, tx);
+ rocksdb_register_tx(hton, thd, tx, false);
tx->acquire_snapshot(true);
if (ss_info) {
@@ -4492,7 +4545,7 @@ static int rocksdb_start_tx_with_shared_read_view(
DBUG_ASSERT(!tx->has_snapshot());
tx->set_tx_read_only(true);
- rocksdb_register_tx(hton, thd, tx);
+ rocksdb_register_tx(hton, thd, tx, false);
tx->acquire_snapshot(true);
// case: an explicit snapshot was not assigned to this transaction
@@ -11010,7 +11063,7 @@ int ha_rocksdb::external_lock(THD *const thd, int lock_type) {
}
}
tx->m_n_mysql_tables_in_use++;
- rocksdb_register_tx(rocksdb_hton, thd, tx);
+ rocksdb_register_tx(rocksdb_hton, thd, tx, (lock_type == F_WRLCK));
tx->io_perf_start(&m_io_perf);
}
@@ -11037,7 +11090,7 @@ int ha_rocksdb::start_stmt(THD *const thd, thr_lock_type lock_type) {
Rdb_transaction *const tx = get_or_create_tx(thd);
read_thd_vars(thd);
- rocksdb_register_tx(ht, thd, tx);
+ rocksdb_register_tx(ht, thd, tx, (lock_type == F_WRLCK));
tx->io_perf_start(&m_io_perf);
DBUG_RETURN(HA_EXIT_SUCCESS);
1
0
[Commits] 7cb8b3b5b1b: Use InnoDB-like transaction isolation with Range Locking mode.
by Sergei Petrunia 21 Jan '19
by Sergei Petrunia 21 Jan '19
21 Jan '19
revision-id: 7cb8b3b5b1be9f7e2434ece77a46e6a641f2e2e1 (fb-prod201801-192-g7cb8b3b5b1b)
parent(s): 70d97cc103fd98c7a4952e7b3a54f272fa7b36f4
author: Sergei Petrunia
committer: Sergei Petrunia
timestamp: 2019-01-21 20:28:52 +0300
message:
Use InnoDB-like transaction isolation with Range Locking mode.
DML statements (UPDATE/DELETE/..) will always read the latest committed
data (as opposed to transaction's snapshot).
---
mysql-test/suite/rocksdb/r/range_locking.result | 70 +++++++++++++++++++++++
mysql-test/suite/rocksdb/t/range_locking.test | 64 +++++++++++++++++++++
storage/rocksdb/ha_rocksdb.cc | 75 ++++++++++++++++++++-----
3 files changed, 196 insertions(+), 13 deletions(-)
diff --git a/mysql-test/suite/rocksdb/r/range_locking.result b/mysql-test/suite/rocksdb/r/range_locking.result
index a43f7d668d4..2fe9cedac79 100644
--- a/mysql-test/suite/rocksdb/r/range_locking.result
+++ b/mysql-test/suite/rocksdb/r/range_locking.result
@@ -192,3 +192,73 @@ rollback;
disconnect con1;
connection default;
drop table t0,t1;
+#
+# Transaction isolation test
+#
+create table t1 (pk int primary key, a int) engine=rocksdb;
+insert into t1 values (1,1),(2,2),(3,3);
+connect con1,localhost,root,,;
+# TRX1: Start, Allocate a snapshot
+connection con1;
+begin;
+select * from t1;
+pk a
+1 1
+2 2
+3 3
+# TRX2: Make a change that TRX1 will not see
+connection default;
+update t1 set a=2222 where pk=2;
+# TRX1: Now, make a change that would overwrite TRX2'x change and commit
+connection con1;
+update t1 set a=a+1 where pk=2;
+commit;
+# Examine the result:
+# pk=2, a=2223 means UPDATE in TRX1 used "read committed" (InnoDB-like isolation)
+# pk=2, a=3 means UPDATE in TRX1 silently overwrote TRX2
+# (and with key tracking, one would get an error on the second UPDATE)
+connection default;
+select * from t1;
+pk a
+1 1
+2 2223
+3 3
+disconnect con1;
+connection default;
+drop table t1;
+# The same test as above, but with range scans.
+create table t1 (pk int primary key, a int) engine=rocksdb;
+insert into t1 values (1,1),(2,2),(3,3),(4,4),(5,5),(6,6);
+connect con1,localhost,root,,;
+# TRX1: Start, Allocate a snapshot
+connection con1;
+begin;
+select * from t1;
+pk a
+1 1
+2 2
+3 3
+4 4
+5 5
+6 6
+# TRX2: Make a change that TRX1 will not see
+connection default;
+update t1 set a=2222 where pk between 3 and 5;
+# TRX1: Now, make a change that would overwrite TRX2'x change and commit
+connection con1;
+update t1 set a=a+1 where pk between 3 and 5;
+commit;
+# Examine the result:
+# pk={3,4,5} a=2223 means UPDATE in TRX1 used "read committed" (InnoDB-like isolation)
+connection default;
+select * from t1;
+pk a
+1 1
+2 2
+3 2223
+4 2223
+5 2223
+6 6
+disconnect con1;
+connection default;
+drop table t1;
diff --git a/mysql-test/suite/rocksdb/t/range_locking.test b/mysql-test/suite/rocksdb/t/range_locking.test
index 5c21f374bac..d2e80707754 100644
--- a/mysql-test/suite/rocksdb/t/range_locking.test
+++ b/mysql-test/suite/rocksdb/t/range_locking.test
@@ -211,3 +211,67 @@ disconnect con1;
connection default;
drop table t0,t1;
+--echo #
+--echo # Transaction isolation test
+--echo #
+
+create table t1 (pk int primary key, a int) engine=rocksdb;
+insert into t1 values (1,1),(2,2),(3,3);
+
+connect (con1,localhost,root,,);
+
+--echo # TRX1: Start, Allocate a snapshot
+connection con1;
+begin;
+select * from t1;
+
+--echo # TRX2: Make a change that TRX1 will not see
+connection default;
+update t1 set a=2222 where pk=2;
+
+--echo # TRX1: Now, make a change that would overwrite TRX2'x change and commit
+connection con1;
+update t1 set a=a+1 where pk=2;
+commit;
+
+--echo # Examine the result:
+--echo # pk=2, a=2223 means UPDATE in TRX1 used "read committed" (InnoDB-like isolation)
+--echo # pk=2, a=3 means UPDATE in TRX1 silently overwrote TRX2
+--echo # (and with key tracking, one would get an error on the second UPDATE)
+connection default;
+select * from t1;
+
+disconnect con1;
+connection default;
+drop table t1;
+
+--echo # The same test as above, but with range scans.
+
+create table t1 (pk int primary key, a int) engine=rocksdb;
+insert into t1 values (1,1),(2,2),(3,3),(4,4),(5,5),(6,6);
+
+connect (con1,localhost,root,,);
+
+--echo # TRX1: Start, Allocate a snapshot
+connection con1;
+begin;
+select * from t1;
+
+--echo # TRX2: Make a change that TRX1 will not see
+connection default;
+update t1 set a=2222 where pk between 3 and 5;
+
+--echo # TRX1: Now, make a change that would overwrite TRX2'x change and commit
+connection con1;
+update t1 set a=a+1 where pk between 3 and 5;
+commit;
+
+--echo # Examine the result:
+--echo # pk={3,4,5} a=2223 means UPDATE in TRX1 used "read committed" (InnoDB-like isolation)
+connection default;
+select * from t1;
+
+disconnect con1;
+connection default;
+drop table t1;
+
diff --git a/storage/rocksdb/ha_rocksdb.cc b/storage/rocksdb/ha_rocksdb.cc
index c4f611a663f..00117ed9e4f 100644
--- a/storage/rocksdb/ha_rocksdb.cc
+++ b/storage/rocksdb/ha_rocksdb.cc
@@ -2101,8 +2101,39 @@ protected:
virtual void do_set_savepoint() = 0;
virtual void do_rollback_to_savepoint() = 0;
+ private:
+ /*
+ If true, the current statement should not use a snapshot for reading.
+ Note that in a multi-statement transaction, the snapshot may have been
+ allocated by another statement.
+ */
+ bool m_stmt_ignores_snapshot = false;
+
+ /* Snapshot-ignore mode will put away m_reads_opts.snapshot here: */
+ const rocksdb::Snapshot *m_saved_snapshot;
+
public:
+
+ void start_ignore_snapshot() {
+ // note: this may be called several times for the same statement
+ if (!m_stmt_ignores_snapshot) {
+ m_saved_snapshot = m_read_opts.snapshot;
+ m_read_opts.snapshot = nullptr;
+ m_stmt_ignores_snapshot= true;
+ }
+ }
+
+ void end_ignore_snapshot() {
+ if (m_stmt_ignores_snapshot) {
+ m_stmt_ignores_snapshot = false;
+ m_read_opts.snapshot = m_saved_snapshot;
+ m_saved_snapshot = nullptr;
+ }
+ }
+ bool in_snapshot_ignore_mode() const { return m_stmt_ignores_snapshot; }
+
rocksdb::ReadOptions m_read_opts;
+
const char *m_mysql_log_file_name;
my_off_t m_mysql_log_offset;
const char *m_mysql_gtid;
@@ -2596,7 +2627,7 @@ public:
virtual bool is_tx_started() const = 0;
virtual void start_tx() = 0;
- virtual void start_stmt() = 0;
+ virtual void start_stmt(bool is_dml_statement) = 0;
void set_initial_savepoint() {
/*
@@ -2849,7 +2880,7 @@ public:
}
void acquire_snapshot(bool acquire_now) override {
- if (m_read_opts.snapshot == nullptr) {
+ if (m_read_opts.snapshot == nullptr && !in_snapshot_ignore_mode()) {
const auto thd_ss = std::static_pointer_cast<Rdb_explicit_snapshot>(
m_thd->get_explicit_snapshot());
if (thd_ss) {
@@ -2964,7 +2995,7 @@ public:
if (value != nullptr) {
value->Reset();
- }
+ } // psergey-todo: m_read_opts.snapshot below!
return m_rocksdb_tx->GetForUpdate(m_read_opts, column_family, key, value,
exclusive);
}
@@ -3028,13 +3059,25 @@ public:
/*
Start a statement inside a multi-statement transaction.
- @todo: are we sure this is called once (and not several times) per
- statement start?
+ @note: If a statement uses N tables, this function will be called N times,
+ for each TABLE object that is used.
For hooking to start of statement that is its own transaction, see
ha_rocksdb::external_lock().
*/
- void start_stmt() override {
+ void start_stmt(bool is_dml_statement) override {
+
+ if (rocksdb_use_range_locking && is_dml_statement) {
+ /*
+ In Range Locking mode, RocksDB does not do "key tracking".
+ Use InnoDB-like concurrency mode: make the DML statements always read
+ the latest data (instead of using transaction's snapshot).
+ This "downgrades" the transaction isolation to READ-COMMITTED on the
+ master, but in return the actions can be replayed on the slave.
+ */
+ start_ignore_snapshot();
+ }
+
// Set the snapshot to delayed acquisition (SetSnapshotOnNextOperation)
acquire_snapshot(false);
}
@@ -3270,7 +3313,7 @@ public:
set_initial_savepoint();
}
- void start_stmt() override {}
+ void start_stmt(bool is_dml_statement) override {}
void rollback_stmt() override {
if (m_batch)
@@ -4242,13 +4285,19 @@ static bool rocksdb_show_status(handlerton *const hton, THD *const thd,
return res;
}
+
+/*
+ @param is_dml_statement If true, we are is a DML statement
+*/
+
static inline void rocksdb_register_tx(handlerton *const hton, THD *const thd,
- Rdb_transaction *const tx) {
+ Rdb_transaction *const tx,
+ bool is_dml_stmt) {
DBUG_ASSERT(tx != nullptr);
trans_register_ha(thd, FALSE, rocksdb_hton);
if (my_core::thd_test_options(thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) {
- tx->start_stmt();
+ tx->start_stmt(is_dml_stmt);
trans_register_ha(thd, TRUE, rocksdb_hton);
}
}
@@ -4344,7 +4393,7 @@ static int rocksdb_start_tx_and_assign_read_view(
DBUG_ASSERT(!tx->has_snapshot());
tx->set_tx_read_only(true);
- rocksdb_register_tx(hton, thd, tx);
+ rocksdb_register_tx(hton, thd, tx, false);
tx->acquire_snapshot(true);
if (ss_info) {
@@ -4492,7 +4541,7 @@ static int rocksdb_start_tx_with_shared_read_view(
DBUG_ASSERT(!tx->has_snapshot());
tx->set_tx_read_only(true);
- rocksdb_register_tx(hton, thd, tx);
+ rocksdb_register_tx(hton, thd, tx, false);
tx->acquire_snapshot(true);
// case: an explicit snapshot was not assigned to this transaction
@@ -11010,7 +11059,7 @@ int ha_rocksdb::external_lock(THD *const thd, int lock_type) {
}
}
tx->m_n_mysql_tables_in_use++;
- rocksdb_register_tx(rocksdb_hton, thd, tx);
+ rocksdb_register_tx(rocksdb_hton, thd, tx, (lock_type == F_WRLCK));
tx->io_perf_start(&m_io_perf);
}
@@ -11037,7 +11086,7 @@ int ha_rocksdb::start_stmt(THD *const thd, thr_lock_type lock_type) {
Rdb_transaction *const tx = get_or_create_tx(thd);
read_thd_vars(thd);
- rocksdb_register_tx(ht, thd, tx);
+ rocksdb_register_tx(ht, thd, tx, (lock_type == F_WRLCK));
tx->io_perf_start(&m_io_perf);
DBUG_RETURN(HA_EXIT_SUCCESS);
1
0
revision-id: 70d97cc103fd98c7a4952e7b3a54f272fa7b36f4 (fb-prod201801-191-g70d97cc103f)
parent(s): 1dd0e500095ac01f5b103714f4712276551385a1
author: Sergei Petrunia
committer: Sergei Petrunia
timestamp: 2019-01-21 20:02:18 +0300
message:
Update test result
---
mysql-test/suite/rocksdb/r/range_locking_escalation.result | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/mysql-test/suite/rocksdb/r/range_locking_escalation.result b/mysql-test/suite/rocksdb/r/range_locking_escalation.result
index bef02dea860..722c99fec84 100644
--- a/mysql-test/suite/rocksdb/r/range_locking_escalation.result
+++ b/mysql-test/suite/rocksdb/r/range_locking_escalation.result
@@ -23,5 +23,5 @@ count(*)
10000
show status like 'rocksdb_locktree_escalation_count';
Variable_name Value
-rocksdb_locktree_escalation_count 3313
+rocksdb_locktree_escalation_count 3324
drop table t0,t1;
1
0
[Commits] 7d1cb6f1ea9: MDEV-14605 Changes to "ON UPDATE CURRENT_TIMESTAMP" fields are not
by andrei.elkinīŧ pp.inet.fi 21 Jan '19
by andrei.elkinīŧ pp.inet.fi 21 Jan '19
21 Jan '19
revision-id: 7d1cb6f1ea99bc58bf45c3aa22edb8e25230257b (mariadb-10.1.37-63-g7d1cb6f1ea9)
parent(s): c1aae370879490dc0a3cd63c5b1010fa21b1f62c
author: Andrei Elkin
committer: Andrei Elkin
timestamp: 2019-01-21 18:19:00 +0200
message:
MDEV-14605 Changes to "ON UPDATE CURRENT_TIMESTAMP" fields are not
always logged properly with binlog_row_image=MINIMAL
There are two issues fixed in this commit.
The first is an observation of a multi-table UPDATE binlogged
in row-format in binlog_row_image=MINIMAL mode. While the UPDATE aims
at a table with an ON-UPDATE attribute its binlog after-image misses
to record also installed default value.
The reason for that turns out missed marking of default-capable fields
in TABLE::write_set.
This is fixed to mark such fields similarly to 10.2's MDEV-10134 patch (db7edfed17efe6)
that introduced it. The marking affects 93d1e5ce0b841bed's TABLE:rpl_write_set
though and thus does not intervene (in 10.1) with MDEV-10134 agenda.
The 2nd issue is extra columns in in binlog_row_image=MINIMAL before-image
while merely a packed primary key is enough. The test main.mysqlbinlog_row_minimal
always had a wrong result recorded.
This is fixed to invoke a function that intended for read_set
possible filtering and which is called (supposed to) in all type of MDL, UPDATE
including; the test results have gotten corrected.
---
mysql-test/r/mysqlbinlog_row_minimal.result | 45 ++++++++++++++---------------
sql/sql_class.cc | 23 +++++++++++++--
sql/table.cc | 6 ++--
sql/table.h | 6 +++-
4 files changed, 51 insertions(+), 29 deletions(-)
diff --git a/mysql-test/r/mysqlbinlog_row_minimal.result b/mysql-test/r/mysqlbinlog_row_minimal.result
index 6ffaeeafc53..a0f2e0cefbb 100644
--- a/mysql-test/r/mysqlbinlog_row_minimal.result
+++ b/mysql-test/r/mysqlbinlog_row_minimal.result
@@ -214,39 +214,36 @@ BEGIN
# at 1997
#<date> server id 1 end_log_pos 2049 Table_map: `test`.`t2` mapped to number 31
# at 2049
-#<date> server id 1 end_log_pos 2119 Update_rows: table id 31 flags: STMT_END_F
+#<date> server id 1 end_log_pos 2111 Update_rows: table id 31 flags: STMT_END_F
### UPDATE `test`.`t2`
### WHERE
### @1=10 /* INT meta=0 nullable=0 is_null=0 */
-### @5=4 /* INT meta=0 nullable=1 is_null=0 */
### SET
### @5=5 /* INT meta=0 nullable=1 is_null=0 */
### UPDATE `test`.`t2`
### WHERE
### @1=11 /* INT meta=0 nullable=0 is_null=0 */
-### @5=4 /* INT meta=0 nullable=1 is_null=0 */
### SET
### @5=5 /* INT meta=0 nullable=1 is_null=0 */
### UPDATE `test`.`t2`
### WHERE
### @1=12 /* INT meta=0 nullable=0 is_null=0 */
-### @5=NULL /* INT meta=0 nullable=1 is_null=1 */
### SET
### @5=5 /* INT meta=0 nullable=1 is_null=0 */
-# at 2119
-#<date> server id 1 end_log_pos 2188 Query thread_id=4 exec_time=x error_code=0
+# at 2111
+#<date> server id 1 end_log_pos 2180 Query thread_id=4 exec_time=x error_code=0
SET TIMESTAMP=X/*!*/;
COMMIT
/*!*/;
-# at 2188
-#<date> server id 1 end_log_pos 2226 GTID 0-1-9
+# at 2180
+#<date> server id 1 end_log_pos 2218 GTID 0-1-9
/*!100001 SET @@session.gtid_seq_no=9*//*!*/;
BEGIN
/*!*/;
-# at 2226
-#<date> server id 1 end_log_pos 2278 Table_map: `test`.`t1` mapped to number 30
-# at 2278
-#<date> server id 1 end_log_pos 2328 Delete_rows: table id 30 flags: STMT_END_F
+# at 2218
+#<date> server id 1 end_log_pos 2270 Table_map: `test`.`t1` mapped to number 30
+# at 2270
+#<date> server id 1 end_log_pos 2320 Delete_rows: table id 30 flags: STMT_END_F
### DELETE FROM `test`.`t1`
### WHERE
### @1=10 /* INT meta=0 nullable=0 is_null=0 */
@@ -259,20 +256,20 @@ BEGIN
### DELETE FROM `test`.`t1`
### WHERE
### @1=13 /* INT meta=0 nullable=0 is_null=0 */
-# at 2328
-#<date> server id 1 end_log_pos 2397 Query thread_id=4 exec_time=x error_code=0
+# at 2320
+#<date> server id 1 end_log_pos 2389 Query thread_id=4 exec_time=x error_code=0
SET TIMESTAMP=X/*!*/;
COMMIT
/*!*/;
-# at 2397
-#<date> server id 1 end_log_pos 2435 GTID 0-1-10
+# at 2389
+#<date> server id 1 end_log_pos 2427 GTID 0-1-10
/*!100001 SET @@session.gtid_seq_no=10*//*!*/;
BEGIN
/*!*/;
-# at 2435
-#<date> server id 1 end_log_pos 2487 Table_map: `test`.`t2` mapped to number 31
-# at 2487
-#<date> server id 1 end_log_pos 2537 Delete_rows: table id 31 flags: STMT_END_F
+# at 2427
+#<date> server id 1 end_log_pos 2479 Table_map: `test`.`t2` mapped to number 31
+# at 2479
+#<date> server id 1 end_log_pos 2529 Delete_rows: table id 31 flags: STMT_END_F
### DELETE FROM `test`.`t2`
### WHERE
### @1=10 /* INT meta=0 nullable=0 is_null=0 */
@@ -285,13 +282,13 @@ BEGIN
### DELETE FROM `test`.`t2`
### WHERE
### @1=13 /* INT meta=0 nullable=0 is_null=0 */
-# at 2537
-#<date> server id 1 end_log_pos 2606 Query thread_id=4 exec_time=x error_code=0
+# at 2529
+#<date> server id 1 end_log_pos 2598 Query thread_id=4 exec_time=x error_code=0
SET TIMESTAMP=X/*!*/;
COMMIT
/*!*/;
-# at 2606
-#<date> server id 1 end_log_pos 2650 Rotate to master-bin.000002 pos: 4
+# at 2598
+#<date> server id 1 end_log_pos 2642 Rotate to master-bin.000002 pos: 4
DELIMITER ;
# End of log file
ROLLBACK /* added by mysqlbinlog */;
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 8424b4477c3..0971d4fdaaa 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -6390,6 +6390,22 @@ int THD::binlog_update_row(TABLE* table, bool is_trans,
DBUG_ASSERT(is_current_stmt_binlog_format_row() &&
((WSREP(this) && wsrep_emulate_bin_log) || mysql_bin_log.is_open()));
+ /**
+ Save a reference to the original read bitmaps
+ We will need this to restore the bitmaps at the end as
+ binlog_prepare_row_images() may change table->read_set.
+ table->read_set is used by pack_row and deep in
+ binlog_prepare_pending_events().
+ */
+ MY_BITMAP *old_read_set= table->read_set;
+
+ /**
+ This will remove spurious fields required during execution but
+ not needed for binlogging. This is done according to the:
+ binlog-row-image option.
+ */
+ binlog_prepare_row_images(table);
+
size_t const before_maxlen = max_row_length(table, before_record);
size_t const after_maxlen = max_row_length(table, after_record);
@@ -6401,9 +6417,9 @@ int THD::binlog_update_row(TABLE* table, bool is_trans,
uchar *after_row= row_data.slot(1);
size_t const before_size= pack_row(table, table->read_set, before_row,
- before_record);
+ before_record);
size_t const after_size= pack_row(table, table->rpl_write_set, after_row,
- after_record);
+ after_record);
/* Ensure that all events in a GTID group are in the same cache */
if (variables.option_bits & OPTION_GTID_BEGIN)
@@ -6431,6 +6447,9 @@ int THD::binlog_update_row(TABLE* table, bool is_trans,
int error= ev->add_row_data(before_row, before_size) ||
ev->add_row_data(after_row, after_size);
+ /* restore read set for the rest of execution */
+ table->column_bitmaps_set_no_signal(old_read_set,
+ table->write_set);
return error;
}
diff --git a/sql/table.cc b/sql/table.cc
index ca06eee077c..31f0d255847 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -6137,6 +6137,8 @@ void TABLE::mark_columns_per_binlog_row_image()
mark_columns_used_by_index_no_reset(s->primary_key, read_set);
/* Only write columns that have changed */
rpl_write_set= write_set;
+ if (default_field)
+ mark_default_fields_for_write(rpl_write_set);
break;
default:
@@ -6283,7 +6285,7 @@ bool TABLE::has_default_function(bool is_update)
Add all fields that have a default function to the table write set.
*/
-void TABLE::mark_default_fields_for_write()
+void TABLE::mark_default_fields_for_write(MY_BITMAP* bset)
{
Field **dfield_ptr, *dfield;
enum_sql_command cmd= in_use->lex->sql_command;
@@ -6294,7 +6296,7 @@ void TABLE::mark_default_fields_for_write()
dfield->has_insert_default_function()) ||
((sql_command_flags[cmd] & CF_UPDATES_DATA) &&
dfield->has_update_default_function()))
- bitmap_set_bit(write_set, dfield->field_index);
+ bitmap_set_bit(bset, dfield->field_index);
}
}
diff --git a/sql/table.h b/sql/table.h
index ca32234579f..d57eeb559a1 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -1353,7 +1353,11 @@ struct TABLE
void mark_columns_per_binlog_row_image(void);
bool mark_virtual_col(Field *field);
void mark_virtual_columns_for_write(bool insert_fl);
- void mark_default_fields_for_write();
+ void mark_default_fields_for_write(MY_BITMAP* bset);
+ inline void mark_default_fields_for_write()
+ {
+ mark_default_fields_for_write(write_set);
+ }
bool has_default_function(bool is_update);
inline void column_bitmaps_set(MY_BITMAP *read_set_arg,
MY_BITMAP *write_set_arg)
1
0
revision-id: 9efd264d8773a54dbceb929869e3dc88b8ee3c13 (mariadb-10.3.6-339-g9efd264d877)
parent(s): c1defbca61c230f05f0ca5e1488ce79bb902654c
author: Jan LindstrÃļm
committer: Jan LindstrÃļm
timestamp: 2019-01-21 10:58:01 +0200
message:
Fix test failure on galera_many_rows
---
mysql-test/suite/galera/t/galera_many_rows.cnf | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/mysql-test/suite/galera/t/galera_many_rows.cnf b/mysql-test/suite/galera/t/galera_many_rows.cnf
new file mode 100644
index 00000000000..24c4cc1c60d
--- /dev/null
+++ b/mysql-test/suite/galera/t/galera_many_rows.cnf
@@ -0,0 +1,10 @@
+!include ../galera_2nodes.cnf
+
+[mysqld.1]
+innodb-status-output=ON
+innodb-status-output-locks=ON
+
+[mysqld.2]
+innodb-status-output=ON
+innodb-status-output-locks=ON
+
1
0
[Commits] 322f916: MDEV-16188: Rearchitected the classes for rowid filters.
by IgorBabaev 20 Jan '19
by IgorBabaev 20 Jan '19
20 Jan '19
revision-id: 322f91664bac5692e86e986778bcd7adfb743ede (mariadb-10.3.6-103-g322f916)
parent(s): 8a9532f2cc1a8eeb53ff04ca2c28b4756afc845b
author: Igor Babaev
committer: Igor Babaev
timestamp: 2019-01-19 23:53:47 -0800
message:
MDEV-16188: Rearchitected the classes for rowid filters.
Also consistently renamed variables and functions where rowid filters were used.
---
mysql-test/main/show_explain.cc | 0
mysql-test/main/show_explain.result | 8 -
mysql-test/main/show_explain.test | 10 -
mysql-test/main/subselect_sj2.result | 5 +
mysql-test/main/subselect_sj2.test | 2 +
mysql-test/main/subselect_sj2_jcl6.result | 13 ++
mysql-test/main/subselect_sj2_jcl6.test | 11 +
mysql-test/main/subselect_sj2_mat.result | 5 +
sql/opt_subselect.h | 2 +-
sql/rowid_filter.cc | 158 ++++++++------
sql/rowid_filter.h | 330 ++++++++++++++----------------
sql/sql_select.cc | 65 +++---
sql/sql_select.h | 14 +-
sql/table.cc | 6 +-
sql/table.h | 17 +-
15 files changed, 332 insertions(+), 314 deletions(-)
diff --git a/mysql-test/main/show_explain.cc b/mysql-test/main/show_explain.cc
new file mode 100644
index 0000000..e69de29
diff --git a/mysql-test/main/show_explain.result b/mysql-test/main/show_explain.result
index 4cf6b66..32364d0 100644
--- a/mysql-test/main/show_explain.result
+++ b/mysql-test/main/show_explain.result
@@ -1,8 +1,3 @@
-set @innodb_stats_persistent_save= @@innodb_stats_persistent;
-set @innodb_stats_persistent_sample_pages_save=
-@@innodb_stats_persistent_sample_pages;
-set global innodb_stats_persistent= 1;
-set global innodb_stats_persistent_sample_pages=100;
drop table if exists t0, t1, t2, t3, t4;
drop view if exists v1;
SET @old_debug= @@session.debug;
@@ -1320,6 +1315,3 @@ drop table t0,t1,t2;
connection default;
disconnect con1;
set debug_sync='RESET';
-set global innodb_stats_persistent= @innodb_stats_persistent_save;
-set global innodb_stats_persistent_sample_pages=
-@innodb_stats_persistent_sample_pages_save;
diff --git a/mysql-test/main/show_explain.test b/mysql-test/main/show_explain.test
index 4145e79..6647ca0 100644
--- a/mysql-test/main/show_explain.test
+++ b/mysql-test/main/show_explain.test
@@ -4,12 +4,6 @@
--source include/have_debug.inc
--source include/have_innodb.inc
-set @innodb_stats_persistent_save= @@innodb_stats_persistent;
-set @innodb_stats_persistent_sample_pages_save=
- @@innodb_stats_persistent_sample_pages;
-
-set global innodb_stats_persistent= 1;
-set global innodb_stats_persistent_sample_pages=100;
--disable_warnings
drop table if exists t0, t1, t2, t3, t4;
@@ -1211,7 +1205,3 @@ connection default;
disconnect con1;
set debug_sync='RESET';
-set global innodb_stats_persistent= @innodb_stats_persistent_save;
-set global innodb_stats_persistent_sample_pages=
- @innodb_stats_persistent_sample_pages_save;
-
diff --git a/mysql-test/main/subselect_sj2.result b/mysql-test/main/subselect_sj2.result
index 9025dce..e5ed30c 100644
--- a/mysql-test/main/subselect_sj2.result
+++ b/mysql-test/main/subselect_sj2.result
@@ -1251,6 +1251,11 @@ INSERT IGNORE INTO t2 (t2id, t1idref) SELECT t1id, t1id FROM t1;
INSERT IGNORE INTO t1 VALUES (200001, 'a');
INSERT IGNORE INTO t2 (t2id, t1idref) VALUES (200011, 200001),(200012, 200001),(200013, 200001);
INSERT IGNORE INTO t3 VALUES (1, 200011, 1), (1, 200012, 2), (1, 200013, 3);
+ANALYZE TABLE t1,t2,t3;
+Table Op Msg_type Msg_text
+test.t1 analyze status OK
+test.t2 analyze status OK
+test.t3 analyze status OK
set @tmp7474= @@optimizer_search_depth;
SET SESSION optimizer_search_depth = 1;
SELECT SQL_NO_CACHE
diff --git a/mysql-test/main/subselect_sj2.test b/mysql-test/main/subselect_sj2.test
index e04c15f..9ed886d 100644
--- a/mysql-test/main/subselect_sj2.test
+++ b/mysql-test/main/subselect_sj2.test
@@ -1395,6 +1395,8 @@ INSERT IGNORE INTO t1 VALUES (200001, 'a');
INSERT IGNORE INTO t2 (t2id, t1idref) VALUES (200011, 200001),(200012, 200001),(200013, 200001);
INSERT IGNORE INTO t3 VALUES (1, 200011, 1), (1, 200012, 2), (1, 200013, 3);
+ANALYZE TABLE t1,t2,t3;
+
set @tmp7474= @@optimizer_search_depth;
SET SESSION optimizer_search_depth = 1;
diff --git a/mysql-test/main/subselect_sj2_jcl6.result b/mysql-test/main/subselect_sj2_jcl6.result
index 5dcaa08..b930bdc 100644
--- a/mysql-test/main/subselect_sj2_jcl6.result
+++ b/mysql-test/main/subselect_sj2_jcl6.result
@@ -1267,6 +1267,11 @@ INSERT IGNORE INTO t2 (t2id, t1idref) SELECT t1id, t1id FROM t1;
INSERT IGNORE INTO t1 VALUES (200001, 'a');
INSERT IGNORE INTO t2 (t2id, t1idref) VALUES (200011, 200001),(200012, 200001),(200013, 200001);
INSERT IGNORE INTO t3 VALUES (1, 200011, 1), (1, 200012, 2), (1, 200013, 3);
+ANALYZE TABLE t1,t2,t3;
+Table Op Msg_type Msg_text
+test.t1 analyze status OK
+test.t2 analyze status OK
+test.t3 analyze status OK
set @tmp7474= @@optimizer_search_depth;
SET SESSION optimizer_search_depth = 1;
SELECT SQL_NO_CACHE
@@ -1371,6 +1376,11 @@ set global innodb_stats_persistent= @innodb_stats_persistent_save;
set global innodb_stats_persistent_sample_pages=
@innodb_stats_persistent_sample_pages_save;
set optimizer_switch=@subselect_sj2_tmp;
+set @innodb_stats_persistent_save= @@innodb_stats_persistent;
+set @innodb_stats_persistent_sample_pages_save=
+@@innodb_stats_persistent_sample_pages;
+set global innodb_stats_persistent= 1;
+set global innodb_stats_persistent_sample_pages=100;
#
# Bug #898073: potential incremental join cache for semijoin
#
@@ -1463,6 +1473,9 @@ set join_cache_level=default;
show variables like 'join_cache_level';
Variable_name Value
join_cache_level 2
+set global innodb_stats_persistent= @innodb_stats_persistent_save;
+set global innodb_stats_persistent_sample_pages=
+@innodb_stats_persistent_sample_pages_save;
set @@optimizer_switch=@save_optimizer_switch_jcl6;
set @optimizer_switch_for_subselect_sj2_test=NULL;
set @join_cache_level_subselect_sj2_test=NULL;
diff --git a/mysql-test/main/subselect_sj2_jcl6.test b/mysql-test/main/subselect_sj2_jcl6.test
index 7ff0871..9be6102 100644
--- a/mysql-test/main/subselect_sj2_jcl6.test
+++ b/mysql-test/main/subselect_sj2_jcl6.test
@@ -16,6 +16,13 @@ set @join_cache_level_for_subselect_sj2_test=@@join_cache_level;
--source subselect_sj2.test
+set @innodb_stats_persistent_save= @@innodb_stats_persistent;
+set @innodb_stats_persistent_sample_pages_save=
+ @@innodb_stats_persistent_sample_pages;
+
+set global innodb_stats_persistent= 1;
+set global innodb_stats_persistent_sample_pages=100;
+
--echo #
--echo # Bug #898073: potential incremental join cache for semijoin
--echo #
@@ -107,6 +114,10 @@ DROP TABLE t1,t2;
set join_cache_level=default;
show variables like 'join_cache_level';
+set global innodb_stats_persistent= @innodb_stats_persistent_save;
+set global innodb_stats_persistent_sample_pages=
+ @innodb_stats_persistent_sample_pages_save;
+
set @@optimizer_switch=@save_optimizer_switch_jcl6;
set @optimizer_switch_for_subselect_sj2_test=NULL;
set @join_cache_level_subselect_sj2_test=NULL;
diff --git a/mysql-test/main/subselect_sj2_mat.result b/mysql-test/main/subselect_sj2_mat.result
index 29a3737..65dfddc 100644
--- a/mysql-test/main/subselect_sj2_mat.result
+++ b/mysql-test/main/subselect_sj2_mat.result
@@ -1253,6 +1253,11 @@ INSERT IGNORE INTO t2 (t2id, t1idref) SELECT t1id, t1id FROM t1;
INSERT IGNORE INTO t1 VALUES (200001, 'a');
INSERT IGNORE INTO t2 (t2id, t1idref) VALUES (200011, 200001),(200012, 200001),(200013, 200001);
INSERT IGNORE INTO t3 VALUES (1, 200011, 1), (1, 200012, 2), (1, 200013, 3);
+ANALYZE TABLE t1,t2,t3;
+Table Op Msg_type Msg_text
+test.t1 analyze status OK
+test.t2 analyze status OK
+test.t3 analyze status OK
set @tmp7474= @@optimizer_search_depth;
SET SESSION optimizer_search_depth = 1;
SELECT SQL_NO_CACHE
diff --git a/sql/opt_subselect.h b/sql/opt_subselect.h
index 846add7..e81b100 100644
--- a/sql/opt_subselect.h
+++ b/sql/opt_subselect.h
@@ -303,7 +303,7 @@ class Loose_scan_opt
pos->loosescan_picker.loosescan_parts= best_max_loose_keypart + 1;
pos->use_join_buffer= FALSE;
pos->table= tab;
- pos->filter= tab->filter;
+ pos->range_rowid_filter_info= tab->range_rowid_filter_info;
// todo need ref_depend_map ?
DBUG_PRINT("info", ("Produced a LooseScan plan, key %s, %s",
tab->table->key_info[best_loose_scan_key].name.str,
diff --git a/sql/rowid_filter.cc b/sql/rowid_filter.cc
index 7af9c4e..6b357e1 100644
--- a/sql/rowid_filter.cc
+++ b/sql/rowid_filter.cc
@@ -6,11 +6,11 @@
#include "sql_select.h"
inline
-double Range_filter_cost_info::lookup_cost(
- Rowid_filter_container_type cont_type)
+double Range_rowid_filter_cost_info::lookup_cost(
+ Rowid_filter_container_type cont_type)
{
switch (cont_type) {
- case ORDERED_ARRAY_CONTAINER:
+ case SORTED_ARRAY_CONTAINER:
return log(est_elements)*0.01;
default:
DBUG_ASSERT(0);
@@ -20,8 +20,8 @@ double Range_filter_cost_info::lookup_cost(
inline
-double Range_filter_cost_info::avg_access_and_eval_gain_per_row(
- Rowid_filter_container_type cont_type)
+double Range_rowid_filter_cost_info::avg_access_and_eval_gain_per_row(
+ Rowid_filter_container_type cont_type)
{
return (1+1.0/TIME_FOR_COMPARE) * (1 - selectivity) -
lookup_cost(cont_type);
@@ -33,8 +33,8 @@ double Range_filter_cost_info::avg_access_and_eval_gain_per_row(
and gets slope and interscept values.
*/
-void Range_filter_cost_info::init(Rowid_filter_container_type cont_type,
- TABLE *tab, uint idx)
+void Range_rowid_filter_cost_info::init(Rowid_filter_container_type cont_type,
+ TABLE *tab, uint idx)
{
container_type= cont_type;
table= tab;
@@ -49,15 +49,15 @@ void Range_filter_cost_info::init(Rowid_filter_container_type cont_type,
}
double
-Range_filter_cost_info::build_cost(Rowid_filter_container_type container_type)
+Range_rowid_filter_cost_info::build_cost(Rowid_filter_container_type cont_type)
{
double cost= 0;
cost+= table->quick_index_only_costs[key_no];
- switch (container_type) {
+ switch (cont_type) {
- case ORDERED_ARRAY_CONTAINER:
+ case SORTED_ARRAY_CONTAINER:
cost+= ARRAY_WRITE_COST * est_elements; /* cost filling the container */
cost+= ARRAY_SORT_C * est_elements * log(est_elements); /* sorting cost */
break;
@@ -68,10 +68,27 @@ Range_filter_cost_info::build_cost(Rowid_filter_container_type container_type)
return cost;
}
+
+Rowid_filter_container *Range_rowid_filter_cost_info::create_container()
+{
+ THD *thd= table->in_use;
+ uint elem_sz= table->file->ref_length;
+ Rowid_filter_container *res= 0;
+
+ switch (container_type) {
+ case SORTED_ARRAY_CONTAINER:
+ res= new (thd->mem_root) Rowid_filter_sorted_array(est_elements, elem_sz);
+ break;
+ default:
+ DBUG_ASSERT(0);
+ }
+ return res;
+}
static
-int compare_range_filter_cost_info_by_a(Range_filter_cost_info **filter_ptr_1,
- Range_filter_cost_info **filter_ptr_2)
+int compare_range_rowid_filter_cost_info_by_a(
+ Range_rowid_filter_cost_info **filter_ptr_1,
+ Range_rowid_filter_cost_info **filter_ptr_2)
{
double diff= (*filter_ptr_2)->a - (*filter_ptr_1)->a;
return (diff < 0 ? -1 : (diff > 0 ? 1 : 0));
@@ -83,16 +100,16 @@ int compare_range_filter_cost_info_by_a(Range_filter_cost_info **filter_ptr_1,
@details
*/
-void TABLE::prune_range_filters()
+void TABLE::prune_range_rowid_filters()
{
uint i, j;
- Range_filter_cost_info **filter_ptr_1= range_filter_cost_info_ptr;
- for (i= 0; i < range_filter_cost_info_elems; i++, filter_ptr_1++)
+ Range_rowid_filter_cost_info **filter_ptr_1= range_rowid_filter_cost_info_ptr;
+ for (i= 0; i < range_rowid_filter_cost_info_elems; i++, filter_ptr_1++)
{
uint key_no= (*filter_ptr_1)->key_no;
- Range_filter_cost_info **filter_ptr_2= filter_ptr_1 + 1;
- for (j= i+1; j < range_filter_cost_info_elems; j++, filter_ptr_2++)
+ Range_rowid_filter_cost_info **filter_ptr_2= filter_ptr_1 + 1;
+ for (j= i+1; j < range_rowid_filter_cost_info_elems; j++, filter_ptr_2++)
{
key_map map= key_info[key_no].overlapped;
map.intersect(key_info[(*filter_ptr_2)->key_no].overlapped);
@@ -105,16 +122,18 @@ void TABLE::prune_range_filters()
}
/* Sort the array range_filter_cost_info by 'a' */
- my_qsort(range_filter_cost_info_ptr,
- range_filter_cost_info_elems,
- sizeof(Range_filter_cost_info *),
- (qsort_cmp) compare_range_filter_cost_info_by_a);
-
- Range_filter_cost_info **cand_filter_ptr= range_filter_cost_info_ptr;
- for (i= 0; i < range_filter_cost_info_elems; i++, cand_filter_ptr++)
+ my_qsort(range_rowid_filter_cost_info_ptr,
+ range_rowid_filter_cost_info_elems,
+ sizeof(Range_rowid_filter_cost_info *),
+ (qsort_cmp) compare_range_rowid_filter_cost_info_by_a);
+
+ Range_rowid_filter_cost_info **cand_filter_ptr=
+ range_rowid_filter_cost_info_ptr;
+ for (i= 0; i < range_rowid_filter_cost_info_elems; i++, cand_filter_ptr++)
{
bool is_pruned= false;
- Range_filter_cost_info **usable_filter_ptr= range_filter_cost_info_ptr;
+ Range_rowid_filter_cost_info **usable_filter_ptr=
+ range_rowid_filter_cost_info_ptr;
key_map abs_indep;
abs_indep.clear_all();
for (uint j= 0; j < i; j++, usable_filter_ptr++)
@@ -130,29 +149,30 @@ void TABLE::prune_range_filters()
}
else
{
- Range_filter_cost_info *moved= *cand_filter_ptr;
+ Range_rowid_filter_cost_info *moved= *cand_filter_ptr;
memmove(usable_filter_ptr+1, usable_filter_ptr,
- sizeof(Range_filter_cost_info *) * (i-j-1));
+ sizeof(Range_rowid_filter_cost_info *) * (i-j-1));
*usable_filter_ptr= moved;
}
}
if (is_pruned)
{
memmove(cand_filter_ptr, cand_filter_ptr+1,
- sizeof(Range_filter_cost_info *) *
- (range_filter_cost_info_elems - 1 - i));
- range_filter_cost_info_elems--;
+ sizeof(Range_rowid_filter_cost_info *) *
+ (range_rowid_filter_cost_info_elems - 1 - i));
+ range_rowid_filter_cost_info_elems--;
}
}
}
static uint
-get_max_range_filter_elements_for_table(THD *thd, TABLE *tab,
- Rowid_filter_container_type cont_type)
+get_max_range_rowid_filter_elems_for_table(
+ THD *thd, TABLE *tab,
+ Rowid_filter_container_type cont_type)
{
switch (cont_type) {
- case ORDERED_ARRAY_CONTAINER :
+ case SORTED_ARRAY_CONTAINER :
return thd->variables.max_rowid_filter_size/tab->file->ref_length;
default :
DBUG_ASSERT(0);
@@ -160,7 +180,7 @@ get_max_range_filter_elements_for_table(THD *thd, TABLE *tab,
}
}
-void TABLE::init_cost_info_for_usable_range_filters(THD *thd)
+void TABLE::init_cost_info_for_usable_range_rowid_filters(THD *thd)
{
uint key_no;
key_map usable_range_filter_keys;
@@ -173,60 +193,64 @@ void TABLE::init_cost_info_for_usable_range_filters(THD *thd)
if (key_no == s->primary_key && file->primary_key_is_clustered())
continue;
if (quick_rows[key_no] >
- get_max_range_filter_elements_for_table(thd, this,
- ORDERED_ARRAY_CONTAINER))
+ get_max_range_rowid_filter_elems_for_table(thd, this,
+ SORTED_ARRAY_CONTAINER))
continue;
usable_range_filter_keys.set_bit(key_no);
}
- range_filter_cost_info_elems= usable_range_filter_keys.bits_set();
- if (!range_filter_cost_info_elems)
+ range_rowid_filter_cost_info_elems= usable_range_filter_keys.bits_set();
+ if (!range_rowid_filter_cost_info_elems)
return;
- range_filter_cost_info_ptr=
- (Range_filter_cost_info **) thd->calloc(sizeof(Range_filter_cost_info *) *
- range_filter_cost_info_elems);
- range_filter_cost_info=
- new (thd->mem_root) Range_filter_cost_info[range_filter_cost_info_elems];
- if (!range_filter_cost_info_ptr || !range_filter_cost_info)
+ range_rowid_filter_cost_info_ptr=
+ (Range_rowid_filter_cost_info **)
+ thd->calloc(sizeof(Range_rowid_filter_cost_info *) *
+ range_rowid_filter_cost_info_elems);
+ range_rowid_filter_cost_info=
+ new (thd->mem_root)
+ Range_rowid_filter_cost_info[range_rowid_filter_cost_info_elems];
+ if (!range_rowid_filter_cost_info_ptr || !range_rowid_filter_cost_info)
{
- range_filter_cost_info_elems= 0;
+ range_rowid_filter_cost_info_elems= 0;
return;
}
- Range_filter_cost_info **curr_ptr= range_filter_cost_info_ptr;
- Range_filter_cost_info *curr_filter_cost_info= range_filter_cost_info;
+ Range_rowid_filter_cost_info **curr_ptr= range_rowid_filter_cost_info_ptr;
+ Range_rowid_filter_cost_info *curr_filter_cost_info=
+ range_rowid_filter_cost_info;
key_map::Iterator li(usable_range_filter_keys);
while ((key_no= li++) != key_map::Iterator::BITMAP_END)
{
*curr_ptr= curr_filter_cost_info;
- curr_filter_cost_info->init(ORDERED_ARRAY_CONTAINER, this, key_no);
+ curr_filter_cost_info->init(SORTED_ARRAY_CONTAINER, this, key_no);
curr_ptr++;
curr_filter_cost_info++;
}
- prune_range_filters();
+ prune_range_rowid_filters();
}
-Range_filter_cost_info *TABLE::best_filter_for_partial_join(uint access_key_no,
- double records)
+Range_rowid_filter_cost_info *
+TABLE::best_range_rowid_filter_for_partial_join(uint access_key_no,
+ double records)
{
- if (!this || range_filter_cost_info_elems == 0 ||
+ if (!this || range_rowid_filter_cost_info_elems == 0 ||
covering_keys.is_set(access_key_no))
return 0;
if (access_key_no == s->primary_key && file->primary_key_is_clustered())
return 0;
- Range_filter_cost_info *best_filter= 0;
+ Range_rowid_filter_cost_info *best_filter= 0;
double best_filter_gain= 0;
key_map *overlapped= &key_info[access_key_no].overlapped;
- for (uint i= 0; i < range_filter_cost_info_elems ; i++)
+ for (uint i= 0; i < range_rowid_filter_cost_info_elems ; i++)
{
double curr_gain = 0;
- Range_filter_cost_info *filter= range_filter_cost_info_ptr[i];
+ Range_rowid_filter_cost_info *filter= range_rowid_filter_cost_info_ptr[i];
if ((filter->key_no == access_key_no) ||
overlapped->is_set(filter->key_no))
continue;
@@ -243,7 +267,7 @@ Range_filter_cost_info *TABLE::best_filter_for_partial_join(uint access_key_no,
}
-bool Range_filter_ordered_array::fill()
+bool Range_rowid_filter::fill()
{
int rc= 0;
handler *file= table->file;
@@ -275,7 +299,7 @@ bool Range_filter_ordered_array::fill()
if (!rc)
{
file->position(quick->record);
- if (refpos_container.add((char*) file->ref))
+ if (container->add(NULL, (char*) file->ref))
rc= 1;
}
}
@@ -288,21 +312,19 @@ bool Range_filter_ordered_array::fill()
file->in_range_check_pushed_down= in_range_check_pushed_down_save;
if (rc != HA_ERR_END_OF_FILE)
return 1;
- container_is_filled= true;
table->file->rowid_filter_is_active= true;
return 0;
}
-bool Range_filter_ordered_array::sort()
-{
- refpos_container.sort(refpos_order_cmp, (void *) (table->file));
- return false;
-}
-
-
-bool Range_filter_ordered_array::check(char *elem)
+bool Rowid_filter_sorted_array::check(void *ctxt, char *elem)
{
+ TABLE *table= (TABLE *) ctxt;
+ if (!is_checked)
+ {
+ refpos_container.sort(refpos_order_cmp, (void *) (table->file));
+ is_checked= true;
+ }
int l= 0;
int r= refpos_container.elements()-1;
while (l <= r)
@@ -321,8 +343,10 @@ bool Range_filter_ordered_array::check(char *elem)
}
-Range_filter_ordered_array::~Range_filter_ordered_array()
+Range_rowid_filter::~Range_rowid_filter()
{
+ delete container;
+ container= 0;
if (select)
{
if (select->quick)
diff --git a/sql/rowid_filter.h b/sql/rowid_filter.h
index 7cec865..3578866 100644
--- a/sql/rowid_filter.h
+++ b/sql/rowid_filter.h
@@ -1,117 +1,66 @@
#ifndef ROWID_FILTER_INCLUDED
#define ROWID_FILTER_INCLUDED
-/**
- It makes sense to apply filters for a certain join order when the following
- inequality holds:
-
- #T + c4*#T > #T*sel(Fi) + c4*#T*sel(Fi) +
- I/O(Fi) + c1*#(Fi) + c2*#(Fi)*log(#(Fi)) +
- c3*#T (1),
-
- where #T - the fanout of the partial join
- Fi - a filter for the index with the number i in
- the key_map of available indexes for this table
- sel(Fi) - the selectivity of the index with the number
- i
- c4*#T,
- c4*#T*sel(Fi) - a cost to apply available predicates
- c4 - a constant to apply available predicates
- I/O(Fi) - a cost of the I/O accesses to Fi
- #(Fi) - a number of estimated records that range
- access would use
- c1*#(Fi) - a cost to write in Fi
- c1 - a constant to write one element in Fi
- c2*#(Fi)*log(#(Fi)) - a cost to sort in Fi
- c2 - a sorting constant
- c3*(#T) - a cost to look-up into a current partial join
- c3 - a constant to look-up into Fi
-
- Let's set a new variable FBCi (filter building cost for the filter with
- index i):
-
- FBCi = I/O(Fi) + c1*#(Fi) + c2*#(Fi)*log(#(Fi))
-
- It can be seen that FBCi doesn't depend on #T.
-
- So using this variable (1) can be rewritten:
-
- #T + c4*#T > #T*sel(Fi) + c4*#T*sel(Fi) +
- FBCi +
- c3*#T
-
- To get a possible cost improvement when a filter is used right part
- of the (1) inequality should be deducted from the left part.
- Denote it as G(#T):
-
- G(#T)= #T + c4*#T - (#T*sel(Fi) + c4*#T*sel(Fi) + FBCi + c3*#T) (2)
-
- On the prepare stage when filters are created #T value isn't known.
-
- To find out what filter is the best among available one for the table
- (what filter gives the biggest gain) a knowledge about linear functions
- can be used. Consider filter gain as a linear function:
-
- Gi(#T)= ai*#T + bi (3)
-
- where ai= 1+c4-c3-sel(Fi)*(1+c4),
- bi= -FBCi
-
- Filter gain can be interpreted as an ordinate, #T as abscissa.
-
- So the aim is to find the linear function that has the biggest ordinate value
- for each positive abscissa (because #T can't be negative) comparing with
- the other available functions.
-
- Consider two filters Fi, Fj or linear functions with a positive slope.
- To find out which linear function is better let's find their intersection
- point coordinates.
-
- Gi(#T0)= Gj(#T0) (using (2))=>
- #T0= (bj - bi)/(ai - aj) (using (3))
- =>
- #T0= (BCFj-BCFi)/((sel(Fj)-sel(Fi))*(1+c4))
-
- If put #T0 value into the (3) formula G(#T0) can be easily found.
-
- It can be seen that if two linear functions intersect in II, III or IV
- quadrants the linear function with a bigger slope value will always
- be better.
-
- If two functions intersect in the I quadrant for #T1 < #T0 a function
- with a smaller slope value will give a better gain and when #T1 > #T0
- function with a bigger slope will give better gain.
-
- for each #T1 > #T0 if (ai > aj) => (Gi(#T1) >= Gj(#T1))
- #T1 <= #T0 if (ai > aj) => (Gi(#T1) <= Gj(#T1))
-
- So both linear functions should be saved.
-
- Interesting cases:
-
- 1. For Fi,Fj filters ai=aj.
-
- In this case intercepts bi and bj should be compared.
- The filter with the biggest intercept will give a better result.
-
- 2. Only one filter remains after the calculations and for some join order
- it is equal to the index that is used to access table. Therefore, this
- filter can't be used.
-
- In this case the gain is computed for every filter that can be constructed
- for this table.
-
- After information about filters is computed for each partial join order
- it is checked if the filter can be applied to the current table.
- If it gives a cost improvement it is saved as the best plan for this
- partial join.
-*/
#include "mariadb.h"
#include "sql_array.h"
+/**
+ @class Rowid_filter
+
+ What rowid / primary filters are
+ --------------------------------
+
+ Consider a join query Q of the form
+ SELECT * FROM T1, ... , Tk WHERE P.
+
+ For any of the table reference Ti(Q) from the from clause of Q different
+ rowid / primary key filters (pk-filters for short) can be built.
+ A pk-filter F built for Ti(Q) is a set of rowids / primary keys of Ti
+ F= {pk1,...,pkN} such that for any row r=r1||...||rk from the result set of Q
+ ri's rowid / primary key pk(ri) is contained in F.
+
+ When pk-filters are useful
+ --------------------------
+
+ If building a pk-filter F for Ti(Q )is not too costly and its cardinality #F
+ is much less than the cardinality of T - #T then using the pk-filter when
+ executing Q might be quite beneficial.
+
+ Let r be a random row from Ti. Let s(F) be the probability that pk(r)
+ belongs to F. Let BC(F) be the cost of building F.
+
+ Suppose that the optimizer has chosen for Q a plan with this join order
+ T1 => ... Tk and that the table Ti is accessed by a ref access using index I.
+ Let K = {k1,...,kM} be the set of all rowid/primary keys values used to access
+ rows of Ti when looking for matches in this table.to join Ti by index I.
+
+ Let's assume that two set sets K and F are uncorrelated. With this assumption
+ if before accessing data from Ti by the rowid / primary key k we first
+ check whether k is in F then we can expect saving on M*(1-s(S)) accesses of
+ data rows from Ti. If we can guarantee that test whether k is in F is
+ relatively cheap then we can gain a lot assuming that BC(F) is much less
+ then the cost of fetching M*(1-s(S)) records from Ti and following
+ evaluation of conditions pushed into Ti.
+
+ Making pk-filter test cheap
+ ---------------------------
+
+ If the search structure to test whether an element is in F can be fully
+ placed in RAM then this test is expected to be be much cheaper than a random
+ access of a record from Ti. We'll consider two search structures for
+ pk-filters: ordered array and bloom filter. Ordered array is easy to
+ implement, but it's space consuming. If a filter contains primary keys
+ then at least space for each primary key from the filter must be allocated
+ in the search structure. On a the opposite a bloom filter requires a
+ fixed number of bits and this number does not depend on the cardinality
+ of the pk-filter (10 bits per element will serve pk-filter of any size).
+*/
+
class TABLE;
class SQL_SELECT;
+class Rowid_filter_container;
+class Range_rowid_filter_cost_info;
/* Cost to write rowid into array */
#define ARRAY_WRITE_COST 0.005
@@ -122,73 +71,76 @@ class SQL_SELECT;
typedef enum
{
- ORDERED_ARRAY_CONTAINER,
+ SORTED_ARRAY_CONTAINER,
BLOOM_FILTER_CONTAINER
} Rowid_filter_container_type;
-class Range_filter_cost_info : public Sql_alloc
+class Rowid_filter_container : public Sql_alloc
{
public:
- Rowid_filter_container_type container_type;
- TABLE *table;
- uint key_no;
- double est_elements;
- double b; // intercept of the linear function
- double a; // slope of the linear function
- double selectivity;
- double cross_x;
- key_map abs_independent;
+ virtual Rowid_filter_container_type get_type() = 0;
+ virtual bool alloc() = 0;
+ virtual bool add(void *ctxt, char *elem) = 0;
+ virtual bool check(void *ctxt, char *elem) = 0;
+ virtual ~Rowid_filter_container() {}
+};
- /**
- Filter cost functions
- */
- Range_filter_cost_info() : table(0), key_no(0) {}
+class Rowid_filter : public Sql_alloc
+{
+protected:
+ Rowid_filter_container *container;
+public:
+ Rowid_filter(Rowid_filter_container *container_arg)
+ : container(container_arg) {}
+
+ virtual bool build() = 0;
+ virtual bool check(char *elem) = 0;
- void init(Rowid_filter_container_type cont_type,
- TABLE *tab, uint key_numb);
+ virtual ~Rowid_filter() {}
- double build_cost(Rowid_filter_container_type container_type);
+ Rowid_filter_container *get_container() { return container; }
+};
- inline double lookup_cost(Rowid_filter_container_type cont_type);
- inline double
- avg_access_and_eval_gain_per_row(Rowid_filter_container_type cont_type);
+class Range_rowid_filter: public Rowid_filter
+{
+ TABLE *table;
+ SQL_SELECT *select;
+ Range_rowid_filter_cost_info *cost_info;
- /**
- Get the gain that usage of filter promises for 'rows' key entries
- */
- inline double get_gain(double rows)
- {
- return rows * a - b;
- }
+public:
+ Range_rowid_filter(TABLE *tab,
+ Range_rowid_filter_cost_info *cost_arg,
+ Rowid_filter_container *container_arg,
+ SQL_SELECT *sel)
+ : Rowid_filter(container_arg), table(tab), select(sel), cost_info(cost_arg)
+ {}
- inline double get_adjusted_gain(double rows, double worst_seeks)
- {
- return get_gain(rows) -
- (1 - selectivity) * (rows - MY_MIN(rows, worst_seeks));
- }
+ ~Range_rowid_filter();
- inline double get_cmp_gain(double rows)
- {
- return rows * (1 - selectivity) / TIME_FOR_COMPARE;
- }
+ bool build() { return fill(); }
+
+ bool check(char *elem) { return container->check(table, elem); }
+
+ bool fill();
+ SQL_SELECT *get_select() { return select; }
};
-class Refpos_container_ordered_array : public Sql_alloc
+class Refpos_container_sorted_array : public Sql_alloc
{
- uint elem_size;
uint max_elements;
+ uint elem_size;
Dynamic_array<char> *array;
public:
- Refpos_container_ordered_array(uint elem_sz, uint max_elems)
- : elem_size(elem_sz), max_elements(max_elems), array(0) {}
+ Refpos_container_sorted_array(uint max_elems, uint elem_sz)
+ : max_elements(max_elems), elem_size(elem_sz), array(0) {}
- ~Refpos_container_ordered_array()
+ ~Refpos_container_sorted_array()
{
delete array;
array= 0;
@@ -226,57 +178,77 @@ class Refpos_container_ordered_array : public Sql_alloc
}
};
-class Range_filter_ordered_array : public Sql_alloc
+class Rowid_filter_sorted_array: public Rowid_filter_container
{
- TABLE *table;
- SQL_SELECT *select;
- bool container_is_filled;
- Refpos_container_ordered_array refpos_container;
-
+ Refpos_container_sorted_array refpos_container;
+ bool is_checked;
+
public:
- Range_filter_ordered_array(TABLE *tab, SQL_SELECT *sel, uint elems)
- : table(tab), select(sel), container_is_filled(false),
- refpos_container(table->file->ref_length, elems)
- {}
-
- ~Range_filter_ordered_array();
+ Rowid_filter_sorted_array(uint elems, uint elem_size)
+ : refpos_container(elems, elem_size), is_checked(false) {}
- SQL_SELECT *get_select() { return select; }
+ Rowid_filter_container_type get_type()
+ { return SORTED_ARRAY_CONTAINER; }
bool alloc() { return refpos_container.alloc(); }
- bool is_filled() { return container_is_filled; }
-
- bool fill();
+ bool add(void *ctxt, char *elem) { return refpos_container.add(elem); }
- bool sort();
-
- bool check(char *elem);
+ bool check(void *ctxt, char *elem);
};
-class Rowid_filter : public Sql_alloc
-{
- Range_filter_cost_info *cost_info;
- Range_filter_ordered_array *container;
+class Range_rowid_filter_cost_info : public Sql_alloc
+{
public:
- Rowid_filter(Range_filter_cost_info *cost_arg,
- Range_filter_ordered_array *container_arg)
- : cost_info(cost_arg), container(container_arg) {}
+ Rowid_filter_container_type container_type;
+ TABLE *table;
+ uint key_no;
+ double est_elements;
+ double b; // intercept of the linear function
+ double a; // slope of the linear function
+ double selectivity;
+ double cross_x;
+ key_map abs_independent;
+
+ /**
+ Filter cost functions
+ */
+
+ Range_rowid_filter_cost_info() : table(0), key_no(0) {}
+
+ void init(Rowid_filter_container_type cont_type,
+ TABLE *tab, uint key_numb);
+
+ double build_cost(Rowid_filter_container_type container_type);
+
+ inline double lookup_cost(Rowid_filter_container_type cont_type);
+
+ inline double
+ avg_access_and_eval_gain_per_row(Rowid_filter_container_type cont_type);
- Range_filter_ordered_array *get_container() { return container; }
+ /**
+ Get the gain that usage of filter promises for 'rows' key entries
+ */
+ inline double get_gain(double rows)
+ {
+ return rows * a - b;
+ }
- ~Rowid_filter()
+ inline double get_adjusted_gain(double rows, double worst_seeks)
{
- delete container;
+ return get_gain(rows) -
+ (1 - selectivity) * (rows - MY_MIN(rows, worst_seeks));
}
- bool is_active()
+ inline double get_cmp_gain(double rows)
{
- return get_container()->is_filled();
+ return rows * (1 - selectivity) / TIME_FOR_COMPARE;
}
- bool check(char *buf) { return get_container()->check(buf); }
+ Rowid_filter_container *create_container();
+
};
+
#endif /* ROWID_FILTER_INCLUDED */
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 724e156..531fe5f 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -1465,9 +1465,9 @@ int JOIN::optimize()
}
-bool JOIN::make_range_filters()
+bool JOIN::make_range_rowid_filters()
{
- DBUG_ENTER("make_range_filters");
+ DBUG_ENTER("make_range_rowid_filters");
JOIN_TAB *tab;
@@ -1475,12 +1475,11 @@ bool JOIN::make_range_filters()
tab;
tab= next_linear_tab(this, tab, WITH_BUSH_ROOTS))
{
- if (tab->filter)
+ if (tab->range_rowid_filter_info)
{
int err;
SQL_SELECT *sel;
- uint elems;
- Range_filter_ordered_array *filter_container= NULL;
+ Rowid_filter_container *filter_container= NULL;
Item **sargable_cond= get_sargable_cond(this, tab->table);
sel= make_select(tab->table, const_table_map, const_table_map,
*sargable_cond, (SORT_INFO*) 0, 1, &err);
@@ -1489,7 +1488,7 @@ bool JOIN::make_range_filters()
key_map filter_map;
filter_map.clear_all();
- filter_map.set_bit(tab->filter->key_no);
+ filter_map.set_bit(tab->range_rowid_filter_info->key_no);
filter_map.merge(tab->table->with_impossible_ranges);
bool force_index_save= tab->table->force_index;
tab->table->force_index= true;
@@ -1500,13 +1499,15 @@ bool JOIN::make_range_filters()
if (thd->is_error())
DBUG_RETURN(1);
DBUG_ASSERT(sel->quick);
- elems= (uint) tab->filter->est_elements;
filter_container=
- new (thd->mem_root) Range_filter_ordered_array(tab->table, sel, elems);
+ tab->range_rowid_filter_info->create_container();
if (filter_container)
{
tab->rowid_filter=
- new (thd->mem_root) Rowid_filter(tab->filter, filter_container);
+ new (thd->mem_root) Range_rowid_filter(
+ tab->table,
+ tab->range_rowid_filter_info,
+ filter_container, sel);
}
}
}
@@ -1515,9 +1516,9 @@ bool JOIN::make_range_filters()
bool
-JOIN::init_range_filters()
+JOIN::init_range_rowid_filters()
{
- DBUG_ENTER("init_range_filters");
+ DBUG_ENTER("init_range_rowid_filters");
JOIN_TAB *tab;
@@ -1534,7 +1535,7 @@ JOIN::init_range_filters()
continue;
}
tab->table->file->rowid_filter_push(tab->rowid_filter);
- tab->is_rowid_filter_filled= false;
+ tab->is_rowid_filter_built= false;
}
DBUG_RETURN(0);
}
@@ -2058,7 +2059,7 @@ int JOIN::optimize_stage2()
if (get_best_combination())
DBUG_RETURN(1);
- if (make_range_filters())
+ if (make_range_rowid_filters())
DBUG_RETURN(1);
if (select_lex->handle_derived(thd->lex, DT_OPTIMIZE))
@@ -2725,7 +2726,7 @@ int JOIN::optimize_stage2()
if (init_join_caches())
DBUG_RETURN(1);
- if (init_range_filters())
+ if (init_range_rowid_filters())
DBUG_RETURN(1);
error= 0;
@@ -5120,7 +5121,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
impossible_range= records == 0 && s->table->reginfo.impossible_range;
if (join->thd->lex->sql_command == SQLCOM_SELECT &&
optimizer_flag(join->thd, OPTIMIZER_SWITCH_USE_ROWID_FILTER))
- s->table->init_cost_info_for_usable_range_filters(join->thd);
+ s->table->init_cost_info_for_usable_range_rowid_filters(join->thd);
}
if (!impossible_range)
{
@@ -6926,14 +6927,14 @@ best_access_path(JOIN *join,
double best_time= DBL_MAX;
double records= DBL_MAX;
table_map best_ref_depends_map= 0;
- Range_filter_cost_info *best_filter= 0;
+ Range_rowid_filter_cost_info *best_filter= 0;
double tmp;
ha_rows rec;
bool best_uses_jbuf= FALSE;
MY_BITMAP *eq_join_set= &s->table->eq_join_set;
KEYUSE *hj_start_key= 0;
SplM_plan_info *spl_plan= 0;
- Range_filter_cost_info *filter= 0;
+ Range_rowid_filter_cost_info *filter= 0;
disable_jbuf= disable_jbuf || idx == join->const_tables;
@@ -7329,7 +7330,8 @@ best_access_path(JOIN *join,
if (records < DBL_MAX)
{
double rows= record_count * records;
- filter= table->best_filter_for_partial_join(start_key->key, rows);
+ filter=
+ table->best_range_rowid_filter_for_partial_join(start_key->key, rows);
if (filter)
{
tmp-= filter->get_adjusted_gain(rows, s->worst_seeks) -
@@ -7461,7 +7463,8 @@ best_access_path(JOIN *join,
{
double rows= record_count * s->found_records;
uint key_no= s->quick->index;
- filter= s->table->best_filter_for_partial_join(key_no, rows);
+ filter= s->table->best_range_rowid_filter_for_partial_join(key_no,
+ rows);
if (filter)
{
tmp-= filter->get_gain(rows);
@@ -7559,7 +7562,7 @@ best_access_path(JOIN *join,
pos->loosescan_picker.loosescan_key= MAX_KEY;
pos->use_join_buffer= best_uses_jbuf;
pos->spl_plan= spl_plan;
- pos->filter= best_filter;
+ pos->range_rowid_filter_info= best_filter;
loose_scan_opt.save_to_position(s, loose_scan_pos);
@@ -9825,7 +9828,7 @@ bool JOIN::get_best_combination()
is_hash_join_key_no(j->ref.key))
hash_join= TRUE;
- j->filter= best_positions[tablenr].filter;
+ j->range_rowid_filter_info= best_positions[tablenr].range_rowid_filter_info;
loop_end:
/*
@@ -12566,14 +12569,13 @@ bool error_if_full_join(JOIN *join)
}
-void JOIN_TAB::fill_range_filter_if_needed()
+void JOIN_TAB::build_range_rowid_filter_if_needed()
{
- if (rowid_filter && !is_rowid_filter_filled)
+ if (rowid_filter && !is_rowid_filter_built)
{
- if (!rowid_filter->get_container()->fill())
+ if (!rowid_filter->build())
{
- rowid_filter->get_container()->sort();
- is_rowid_filter_filled= true;
+ is_rowid_filter_built= true;
}
else
{
@@ -19493,7 +19495,7 @@ sub_select(JOIN *join,JOIN_TAB *join_tab,bool end_of_records)
if (!join_tab->preread_init_done && join_tab->preread_init())
DBUG_RETURN(NESTED_LOOP_ERROR);
- join_tab->fill_range_filter_if_needed();
+ join_tab->build_range_rowid_filter_if_needed();
join->return_tab= join_tab;
@@ -20439,7 +20441,7 @@ int join_init_read_record(JOIN_TAB *tab)
if (tab->filesort && tab->sort_table()) // Sort table.
return 1;
- tab->fill_range_filter_if_needed();
+ tab->build_range_rowid_filter_if_needed();
DBUG_EXECUTE_IF("kill_join_init_read_record",
tab->join->thd->set_killed(KILL_QUERY););
@@ -22490,7 +22492,7 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
tab->use_quick=1;
tab->ref.key= -1;
tab->ref.key_parts=0; // Don't use ref key.
- tab->filter= 0;
+ tab->range_rowid_filter_info= 0;
if (tab->rowid_filter)
{
delete tab->rowid_filter;
@@ -25331,11 +25333,12 @@ bool JOIN_TAB::save_explain_data(Explain_table_access *eta,
if (rowid_filter)
{
- QUICK_SELECT_I *quick= rowid_filter->get_container()->get_select()->quick;
+ Range_rowid_filter *range_filter= (Range_rowid_filter *) rowid_filter;
+ QUICK_SELECT_I *quick= range_filter->get_select()->quick;
Explain_rowid_filter *erf= new (thd->mem_root) Explain_rowid_filter;
erf->quick= quick->get_explain(thd->mem_root);
- erf->selectivity= filter->selectivity;
+ erf->selectivity= range_rowid_filter_info->selectivity;
erf->rows= quick->records;
eta->rowid_filter= erf;
//psergey-todo: also do setup for ANALYZE here.
diff --git a/sql/sql_select.h b/sql/sql_select.h
index 2fea84d..bfa4274 100644
--- a/sql/sql_select.h
+++ b/sql/sql_select.h
@@ -510,11 +510,11 @@ typedef struct st_join_table {
uint n_sj_tables;
bool preread_init_done;
- Range_filter_cost_info *filter;
+ Range_rowid_filter_cost_info *range_rowid_filter_info;
Rowid_filter *rowid_filter;
- bool is_rowid_filter_filled;
+ bool is_rowid_filter_built;
- void fill_range_filter_if_needed();
+ void build_range_rowid_filter_if_needed();
void cleanup();
inline bool is_using_loose_index_scan()
{
@@ -889,7 +889,7 @@ class Sj_materialization_picker : public Semi_join_strategy_picker
};
-class Range_filter_cost_info;
+class Range_rowid_filter_cost_info;
class Rowid_filter;
@@ -976,7 +976,7 @@ typedef struct st_position
/* Info on splitting plan used at this position */
SplM_plan_info *spl_plan;
/* The index for which filter can be built */
- Range_filter_cost_info *filter;
+ Range_rowid_filter_cost_info *range_rowid_filter_info;
} POSITION;
typedef Bounds_checked_array<Item_null_result*> Item_null_array;
@@ -1626,8 +1626,8 @@ class JOIN :public Sql_alloc
bool optimize_unflattened_subqueries();
bool optimize_constant_subqueries();
int init_join_caches();
- bool make_range_filters();
- bool init_range_filters();
+ bool make_range_rowid_filters();
+ bool init_range_rowid_filters();
bool make_sum_func_list(List<Item> &all_fields, List<Item> &send_fields,
bool before_group_by, bool recompute= FALSE);
diff --git a/sql/table.cc b/sql/table.cc
index 67c369f..bb18940 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -4695,9 +4695,9 @@ void TABLE::init(THD *thd, TABLE_LIST *tl)
created= TRUE;
cond_selectivity= 1.0;
cond_selectivity_sampling_explain= NULL;
- range_filter_cost_info_elems= 0;
- range_filter_cost_info_ptr= NULL;
- range_filter_cost_info= NULL;
+ range_rowid_filter_cost_info_elems= 0;
+ range_rowid_filter_cost_info_ptr= NULL;
+ range_rowid_filter_cost_info= NULL;
#ifdef HAVE_REPLICATION
/* used in RBR Triggers */
master_had_triggers= 0;
diff --git a/sql/table.h b/sql/table.h
index feeb9ee..9918758 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -55,7 +55,7 @@ class Virtual_column_info;
class Table_triggers_list;
class TMP_TABLE_PARAM;
class SEQUENCE;
-class Range_filter_cost_info;
+class Range_rowid_filter_cost_info;
/*
Used to identify NESTED_JOIN structures within a join (applicable only to
@@ -1504,13 +1504,14 @@ struct TABLE
void add_splitting_info_for_key_field(struct KEY_FIELD *key_field);
key_map with_impossible_ranges;
- uint range_filter_cost_info_elems;
- Range_filter_cost_info **range_filter_cost_info_ptr;
- Range_filter_cost_info *range_filter_cost_info;
- void init_cost_info_for_usable_range_filters(THD *thd);
- void prune_range_filters();
- Range_filter_cost_info *best_filter_for_partial_join(uint access_key_no,
- double records);
+ uint range_rowid_filter_cost_info_elems;
+ Range_rowid_filter_cost_info **range_rowid_filter_cost_info_ptr;
+ Range_rowid_filter_cost_info *range_rowid_filter_cost_info;
+ void init_cost_info_for_usable_range_rowid_filters(THD *thd);
+ void prune_range_rowid_filters();
+ Range_rowid_filter_cost_info *
+ best_range_rowid_filter_for_partial_join(uint access_key_no,
+ double records);
/**
System Versioning support
*/
1
0
19 Jan '19
revision-id: 81768c1a878ee11e39d2b4d1ed0d52a35a00c6dc (mariadb-10.4.1-102-g81768c1)
parent(s): 4edb29380c98058a28e49c826bacee9c83473579
committer: Alexey Botchkov
timestamp: 2019-01-20 02:45:58 +0400
message:
MDEV-5313 Improving audit plugin API.
service_sql added.
---
include/mysql/service_sql.h | 101 ++++++++++++++++++++++++++++++++++++++++++++
include/mysql/services.h | 1 +
include/service_versions.h | 1 +
libservices/CMakeLists.txt | 1 +
libservices/sql_service.c | 19 +++++++++
sql/sql_plugin_services.ic | 22 +++++++++-
6 files changed, 143 insertions(+), 2 deletions(-)
diff --git a/include/mysql/service_sql.h b/include/mysql/service_sql.h
new file mode 100644
index 0000000..2af5177
--- /dev/null
+++ b/include/mysql/service_sql.h
@@ -0,0 +1,101 @@
+/* Copyright (C) 2019 MariaDB Corporation
+
+ 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 */
+
+#ifndef MYSQL_SERVICE_SQL
+#define MYSQL_SERVICE_SQL
+
+/**
+ @file
+ sql service
+
+ Provides interface for plugins to execute SQL queries.
+
+*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct st_mysql MYSQL;
+typedef struct st_mysql_res MYSQL_RES;
+typedef char **MYSQL_ROW;
+
+
+extern struct sql_service_st {
+ MYSQL *(*sqls_init)(MYSQL *mysql);
+ void (*sqls_close)(MYSQL *mysql);
+ int (*sqls_real_query)(MYSQL *mysql, const char *q,
+ unsigned long length);
+ unsigned long (*sqls_affected_rows)(MYSQL *mysql);
+ uint (*sqls_errno)(MYSQL *mysql);
+ const char *(*sqls_error)(MYSQL *mysql);
+ MYSQL_RES *(*sqls_store_result)(MYSQL *mysql);
+ void (*sqls_free_result)(MYSQL_RES *result);
+ unsigned long (*sqls_num_rows)(MYSQL_RES *res);
+ unsigned int (*sqls_num_fields)(MYSQL_RES *res);
+ MYSQL_ROW (*sqls_fetch_row)(MYSQL_RES *result);
+ unsigned long * (*sqls_fetch_lengths)(MYSQL_RES *result);
+} *sql_service;
+
+#ifdef MYSQL_DYNAMIC_PLUGIN
+
+#define sqls_init sql_service->sqls_init
+#define sqls_close sql_service->sqls_close
+#define sqls_real_query sql_service->sqls_real_query
+#define sqls_affected_rows sql_service->sqls_affected_rows
+#define sqls_warning_count sql_service->sqls_warning_count
+#define sqls_errno sql_service->sqls_errno
+#define sqls_sqlstate sql_service->sqls_sqlstate
+#define sqls_error sql_service->sqls_error
+#define sqls_store_result sql_service->sqls_store_result
+#define sqls_free_result sql_service->sqls_free_result
+#define sqls_num_rows sql_service->sqls_num_rows
+#define sqls_num_fields sql_service->sqls_num_fields
+#define sqls_fetch_field sql_service->sqls_fetch_field
+#define sqls_field_seek sql_service->sqls_field_seek
+#define sqls_fetch_row sql_service->sqls_fetch_row
+#define sqls_fetch_lengths sql_service->sqls_fetch_lengths
+
+#else
+
+MYSQL *sqls_init(MYSQL *mysql);
+void sqls_close(MYSQL *mysql);
+int sqls_real_query(MYSQL *mysql, const char *q,
+ unsigned long length);
+
+unsigned long sqls_affected_rows(MYSQL *mysql);
+#define sqls_errno mysql_errno
+#define sqls_error mysql_error
+#define sqls_store_result mysql_store_result
+#define sqls_free_result mysql_free_result
+unsigned long sqls_num_rows(MYSQL_RES *res);
+#define sqls_num_fields mysql_num_fields
+#define sqls_fetch_field mysql_fetch_field
+#define sqls_field_seek mysql_field_seek
+#define sqls_fetch_row mysql_fetch_row
+#define sqls_fetch_lengths mysql_fetch_lengths
+
+#endif /*MYSQL_DYNAMIC_PLUGIN*/
+
+
+#ifdef __cplusplus
+}
+#endif
+
+//#undef MYSQL_SERVER
+
+#endif /*MYSQL_SERVICE_SQL */
+
+
diff --git a/include/mysql/services.h b/include/mysql/services.h
index 6dc970d..1ad5ae1 100644
--- a/include/mysql/services.h
+++ b/include/mysql/services.h
@@ -40,6 +40,7 @@ extern "C" {
#include <mysql/service_thd_timezone.h>
#include <mysql/service_thd_wait.h>
#include <mysql/service_json.h>
+#include <mysql/service_sql.h>
/*#include <mysql/service_wsrep.h>*/
#ifdef __cplusplus
diff --git a/include/service_versions.h b/include/service_versions.h
index 050012d..4cb4725 100644
--- a/include/service_versions.h
+++ b/include/service_versions.h
@@ -43,3 +43,4 @@
#define VERSION_thd_wait 0x0100
#define VERSION_wsrep 0x0202
#define VERSION_json 0x0100
+#define VERSION_sql 0x0100
diff --git a/libservices/CMakeLists.txt b/libservices/CMakeLists.txt
index b99be71..0120648 100644
--- a/libservices/CMakeLists.txt
+++ b/libservices/CMakeLists.txt
@@ -38,6 +38,7 @@ SET(MYSQLSERVICES_SOURCES
thd_wait_service.c
wsrep_service.c
json_service.c
+ sql_service.c
)
ADD_CONVENIENCE_LIBRARY(mysqlservices ${MYSQLSERVICES_SOURCES})
diff --git a/libservices/sql_service.c b/libservices/sql_service.c
new file mode 100644
index 0000000..39a9d0a
--- /dev/null
+++ b/libservices/sql_service.c
@@ -0,0 +1,19 @@
+
+/* Copyright (c) 2019, Monty Program Ab
+
+ 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
+*/
+
+#include <service_versions.h>
+SERVICE_VERSION sql_service= (void*)VERSION_sql;
diff --git a/sql/sql_plugin_services.ic b/sql/sql_plugin_services.ic
index c730490..fd0dba4 100644
--- a/sql/sql_plugin_services.ic
+++ b/sql/sql_plugin_services.ic
@@ -17,6 +17,7 @@
/* support for Services */
#include <service_versions.h>
#include <mysql/service_wsrep.h>
+#include <mysql.h>
struct st_service_ref {
const char *name;
@@ -217,7 +218,7 @@ static struct my_print_error_service_st my_print_error_handler=
my_printv_error
};
-struct json_service_st json_handler=
+static struct json_service_st json_handler=
{
json_type,
json_get_array_item,
@@ -227,6 +228,22 @@ struct json_service_st json_handler=
json_unescape_json
};
+static struct sql_service_st sql_handler=
+{
+ sqls_init,
+ sqls_close,
+ sqls_real_query,
+ sqls_affected_rows,
+ mysql_errno,
+ mysql_error,
+ mysql_store_result,
+ mysql_free_result,
+ sqls_num_rows,
+ mysql_num_fields,
+ mysql_fetch_row,
+ mysql_fetch_lengths
+};
+
static struct st_service_ref list_of_services[]=
{
{ "base64_service", VERSION_base64, &base64_handler },
@@ -250,6 +267,7 @@ static struct st_service_ref list_of_services[]=
{ "thd_timezone_service", VERSION_thd_timezone, &thd_timezone_handler },
{ "thd_wait_service", VERSION_thd_wait, &thd_wait_handler },
{ "wsrep_service", VERSION_wsrep, &wsrep_handler },
- { "json_service", VERSION_json, &json_handler }
+ { "json_service", VERSION_json, &json_handler },
+ { "sql_service", VERSION_sql, &sql_handler }
};
1
0
19 Jan '19
revision-id: 83b46d0e18da954033cfe7ba807ef6a6a8bbf257 (mariadb-10.0.37-41-g83b46d0e18d)
parent(s): d0d0f88f2cd4da23c2c2da702da51fb533e7fb8a
author: Varun Gupta
committer: Varun Gupta
timestamp: 2019-01-19 20:12:00 +0530
message:
MDEV-18255: Server crashes in Bitmap<64u>::intersect
The server crashes here because we try to update condition comprising
of a non-merged semi-join which was cleaned up due to the IMPOSSIBLE WHERE
in the parent.
So the approach to fix this is for a certain select $X:
at the end of its JOIN::optimize() call
we call JOIN::optimize_unflattened_subqueries (this causes children of select $X be optimized)
then for the current select we update_used_tables() inside JOIN::optimize_unflattened_subqueries
Updated few test results
---
mysql-test/r/subselect.result | 6 +++---
mysql-test/r/subselect_mat.result | 18 +++++++++++++++++-
mysql-test/r/subselect_no_exists_to_in.result | 6 +++---
mysql-test/r/subselect_no_mat.result | 6 +++---
mysql-test/r/subselect_no_opts.result | 6 +++---
mysql-test/r/subselect_no_scache.result | 6 +++---
mysql-test/r/subselect_no_semijoin.result | 6 +++---
mysql-test/r/subselect_sj.result | 2 +-
mysql-test/r/subselect_sj_jcl6.result | 2 +-
mysql-test/r/subselect_sj_mat.result | 2 +-
mysql-test/t/subselect_mat.test | 13 +++++++++++++
sql/opt_subselect.cc | 6 +++++-
sql/sql_lex.cc | 1 -
13 files changed, 56 insertions(+), 24 deletions(-)
diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result
index b074fb371a5..dbfc33639be 100644
--- a/mysql-test/r/subselect.result
+++ b/mysql-test/r/subselect.result
@@ -4584,7 +4584,7 @@ SELECT MAX(b), (SELECT COUNT(*) FROM st1,st2 WHERE st2.b <= t1.b)
FROM t1
WHERE a = 230;
MAX(b) (SELECT COUNT(*) FROM st1,st2 WHERE st2.b <= t1.b)
-NULL NULL
+NULL 0
DROP TABLE t1, st1, st2;
#
# Bug #48709: Assertion failed in sql_select.cc:11782:
@@ -6656,7 +6656,7 @@ id select_type table type possible_keys key key_len ref rows Extra
2 SUBQUERY t1 ALL NULL NULL NULL NULL 2
SELECT COUNT(f1), (SELECT f1 FROM t1 WHERE f2 > 0 limit 1) AS f4 FROM t2, t1 WHERE 'v'= f3;
COUNT(f1) f4
-0 NULL
+0 7
EXPLAIN
SELECT COUNT(f1), exists(SELECT f1 FROM t1 WHERE f2 > 0 limit 1) AS f4 FROM t2, t1 WHERE 'v'= f3;
id select_type table type possible_keys key key_len ref rows Extra
@@ -6664,7 +6664,7 @@ id select_type table type possible_keys key key_len ref rows Extra
2 SUBQUERY t1 ALL NULL NULL NULL NULL 2
SELECT COUNT(f1), exists(SELECT f1 FROM t1 WHERE f2 > 0 limit 1) AS f4 FROM t2, t1 WHERE 'v'= f3;
COUNT(f1) f4
-0 0
+0 1
EXPLAIN
SELECT COUNT(f1), f2 > ALL (SELECT f1 FROM t1 WHERE f2 > 0) AS f4 FROM t2, t1 WHERE 'v'= f3;
id select_type table type possible_keys key key_len ref rows Extra
diff --git a/mysql-test/r/subselect_mat.result b/mysql-test/r/subselect_mat.result
index aa0ac73abd2..cceda9f8599 100644
--- a/mysql-test/r/subselect_mat.result
+++ b/mysql-test/r/subselect_mat.result
@@ -1896,7 +1896,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using where; Using join buffer (flat, BNL join)
2 MATERIALIZED t2 ALL NULL NULL NULL NULL 3 100.00
Warnings:
-Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from <materialize> (select max(`test`.`t2`.`c`) from `test`.`t2`) join `test`.`t1` where ((`test`.`t1`.`b` = 7) and (`test`.`t1`.`a` = `<subquery2>`.`MAX(c)`) and (<cache>(isnull(/*always not null*/ 1)) or (`<subquery2>`.`MAX(c)` = 7)))
+Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from <materialize> (select max(`test`.`t2`.`c`) from `test`.`t2`) join `test`.`t1` where ((`test`.`t1`.`b` = 7) and (`test`.`t1`.`a` = `<subquery2>`.`MAX(c)`) and (0 or (`<subquery2>`.`MAX(c)` = 7)))
SELECT * FROM t1
WHERE a IN (SELECT MAX(c) FROM t2) AND b=7 AND (a IS NULL OR a=b);
a b
@@ -2822,3 +2822,19 @@ id select_type table type possible_keys key key_len ref rows Extra
SELECT * FROM t2 WHERE f IN ( SELECT LEFT('foo',0) FROM t1 ORDER BY 1 );
f
DROP TABLE t1, t2;
+#
+# MDEV-18255: Server crashes in Bitmap<64u>::intersect
+#
+create table t1 (v1 varchar(1)) engine=myisam ;
+create table t2 (v1 varchar(1)) engine=myisam ;
+explain
+select 1 from t1 where exists
+(select 1 from t1 where t1.v1 in (select t2.v1 from t2 having t2.v1 < 'j')) ;
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY NULL NULL NULL NULL NULL NULL NULL Impossible WHERE
+2 SUBQUERY NULL NULL NULL NULL NULL NULL NULL no matching row in const table
+3 MATERIALIZED NULL NULL NULL NULL NULL NULL NULL no matching row in const table
+select 1 from t1 where exists
+(select 1 from t1 where t1.v1 in (select t2.v1 from t2 having t2.v1 < 'j')) ;
+1
+drop table t1,t2;
diff --git a/mysql-test/r/subselect_no_exists_to_in.result b/mysql-test/r/subselect_no_exists_to_in.result
index d5aa16a2ce9..664ffe015d1 100644
--- a/mysql-test/r/subselect_no_exists_to_in.result
+++ b/mysql-test/r/subselect_no_exists_to_in.result
@@ -4586,7 +4586,7 @@ SELECT MAX(b), (SELECT COUNT(*) FROM st1,st2 WHERE st2.b <= t1.b)
FROM t1
WHERE a = 230;
MAX(b) (SELECT COUNT(*) FROM st1,st2 WHERE st2.b <= t1.b)
-NULL NULL
+NULL 0
DROP TABLE t1, st1, st2;
#
# Bug #48709: Assertion failed in sql_select.cc:11782:
@@ -6656,7 +6656,7 @@ id select_type table type possible_keys key key_len ref rows Extra
2 SUBQUERY t1 ALL NULL NULL NULL NULL 2
SELECT COUNT(f1), (SELECT f1 FROM t1 WHERE f2 > 0 limit 1) AS f4 FROM t2, t1 WHERE 'v'= f3;
COUNT(f1) f4
-0 NULL
+0 7
EXPLAIN
SELECT COUNT(f1), exists(SELECT f1 FROM t1 WHERE f2 > 0 limit 1) AS f4 FROM t2, t1 WHERE 'v'= f3;
id select_type table type possible_keys key key_len ref rows Extra
@@ -6664,7 +6664,7 @@ id select_type table type possible_keys key key_len ref rows Extra
2 SUBQUERY t1 ALL NULL NULL NULL NULL 2
SELECT COUNT(f1), exists(SELECT f1 FROM t1 WHERE f2 > 0 limit 1) AS f4 FROM t2, t1 WHERE 'v'= f3;
COUNT(f1) f4
-0 0
+0 1
EXPLAIN
SELECT COUNT(f1), f2 > ALL (SELECT f1 FROM t1 WHERE f2 > 0) AS f4 FROM t2, t1 WHERE 'v'= f3;
id select_type table type possible_keys key key_len ref rows Extra
diff --git a/mysql-test/r/subselect_no_mat.result b/mysql-test/r/subselect_no_mat.result
index aff68bd6729..1ad808a7e8a 100644
--- a/mysql-test/r/subselect_no_mat.result
+++ b/mysql-test/r/subselect_no_mat.result
@@ -4584,7 +4584,7 @@ SELECT MAX(b), (SELECT COUNT(*) FROM st1,st2 WHERE st2.b <= t1.b)
FROM t1
WHERE a = 230;
MAX(b) (SELECT COUNT(*) FROM st1,st2 WHERE st2.b <= t1.b)
-NULL NULL
+NULL 0
DROP TABLE t1, st1, st2;
#
# Bug #48709: Assertion failed in sql_select.cc:11782:
@@ -6651,7 +6651,7 @@ id select_type table type possible_keys key key_len ref rows Extra
2 SUBQUERY t1 ALL NULL NULL NULL NULL 2
SELECT COUNT(f1), (SELECT f1 FROM t1 WHERE f2 > 0 limit 1) AS f4 FROM t2, t1 WHERE 'v'= f3;
COUNT(f1) f4
-0 NULL
+0 7
EXPLAIN
SELECT COUNT(f1), exists(SELECT f1 FROM t1 WHERE f2 > 0 limit 1) AS f4 FROM t2, t1 WHERE 'v'= f3;
id select_type table type possible_keys key key_len ref rows Extra
@@ -6659,7 +6659,7 @@ id select_type table type possible_keys key key_len ref rows Extra
2 SUBQUERY t1 ALL NULL NULL NULL NULL 2
SELECT COUNT(f1), exists(SELECT f1 FROM t1 WHERE f2 > 0 limit 1) AS f4 FROM t2, t1 WHERE 'v'= f3;
COUNT(f1) f4
-0 0
+0 1
EXPLAIN
SELECT COUNT(f1), f2 > ALL (SELECT f1 FROM t1 WHERE f2 > 0) AS f4 FROM t2, t1 WHERE 'v'= f3;
id select_type table type possible_keys key key_len ref rows Extra
diff --git a/mysql-test/r/subselect_no_opts.result b/mysql-test/r/subselect_no_opts.result
index f1181785a5c..d33d561bc3d 100644
--- a/mysql-test/r/subselect_no_opts.result
+++ b/mysql-test/r/subselect_no_opts.result
@@ -4580,7 +4580,7 @@ SELECT MAX(b), (SELECT COUNT(*) FROM st1,st2 WHERE st2.b <= t1.b)
FROM t1
WHERE a = 230;
MAX(b) (SELECT COUNT(*) FROM st1,st2 WHERE st2.b <= t1.b)
-NULL NULL
+NULL 0
DROP TABLE t1, st1, st2;
#
# Bug #48709: Assertion failed in sql_select.cc:11782:
@@ -6647,7 +6647,7 @@ id select_type table type possible_keys key key_len ref rows Extra
2 SUBQUERY t1 ALL NULL NULL NULL NULL 2
SELECT COUNT(f1), (SELECT f1 FROM t1 WHERE f2 > 0 limit 1) AS f4 FROM t2, t1 WHERE 'v'= f3;
COUNT(f1) f4
-0 NULL
+0 7
EXPLAIN
SELECT COUNT(f1), exists(SELECT f1 FROM t1 WHERE f2 > 0 limit 1) AS f4 FROM t2, t1 WHERE 'v'= f3;
id select_type table type possible_keys key key_len ref rows Extra
@@ -6655,7 +6655,7 @@ id select_type table type possible_keys key key_len ref rows Extra
2 SUBQUERY t1 ALL NULL NULL NULL NULL 2
SELECT COUNT(f1), exists(SELECT f1 FROM t1 WHERE f2 > 0 limit 1) AS f4 FROM t2, t1 WHERE 'v'= f3;
COUNT(f1) f4
-0 0
+0 1
EXPLAIN
SELECT COUNT(f1), f2 > ALL (SELECT f1 FROM t1 WHERE f2 > 0) AS f4 FROM t2, t1 WHERE 'v'= f3;
id select_type table type possible_keys key key_len ref rows Extra
diff --git a/mysql-test/r/subselect_no_scache.result b/mysql-test/r/subselect_no_scache.result
index 6cefce21c20..e2762cde548 100644
--- a/mysql-test/r/subselect_no_scache.result
+++ b/mysql-test/r/subselect_no_scache.result
@@ -4590,7 +4590,7 @@ SELECT MAX(b), (SELECT COUNT(*) FROM st1,st2 WHERE st2.b <= t1.b)
FROM t1
WHERE a = 230;
MAX(b) (SELECT COUNT(*) FROM st1,st2 WHERE st2.b <= t1.b)
-NULL NULL
+NULL 0
DROP TABLE t1, st1, st2;
#
# Bug #48709: Assertion failed in sql_select.cc:11782:
@@ -6662,7 +6662,7 @@ id select_type table type possible_keys key key_len ref rows Extra
2 SUBQUERY t1 ALL NULL NULL NULL NULL 2
SELECT COUNT(f1), (SELECT f1 FROM t1 WHERE f2 > 0 limit 1) AS f4 FROM t2, t1 WHERE 'v'= f3;
COUNT(f1) f4
-0 NULL
+0 7
EXPLAIN
SELECT COUNT(f1), exists(SELECT f1 FROM t1 WHERE f2 > 0 limit 1) AS f4 FROM t2, t1 WHERE 'v'= f3;
id select_type table type possible_keys key key_len ref rows Extra
@@ -6670,7 +6670,7 @@ id select_type table type possible_keys key key_len ref rows Extra
2 SUBQUERY t1 ALL NULL NULL NULL NULL 2
SELECT COUNT(f1), exists(SELECT f1 FROM t1 WHERE f2 > 0 limit 1) AS f4 FROM t2, t1 WHERE 'v'= f3;
COUNT(f1) f4
-0 0
+0 1
EXPLAIN
SELECT COUNT(f1), f2 > ALL (SELECT f1 FROM t1 WHERE f2 > 0) AS f4 FROM t2, t1 WHERE 'v'= f3;
id select_type table type possible_keys key key_len ref rows Extra
diff --git a/mysql-test/r/subselect_no_semijoin.result b/mysql-test/r/subselect_no_semijoin.result
index 884374a74b1..d812b84cbd9 100644
--- a/mysql-test/r/subselect_no_semijoin.result
+++ b/mysql-test/r/subselect_no_semijoin.result
@@ -4580,7 +4580,7 @@ SELECT MAX(b), (SELECT COUNT(*) FROM st1,st2 WHERE st2.b <= t1.b)
FROM t1
WHERE a = 230;
MAX(b) (SELECT COUNT(*) FROM st1,st2 WHERE st2.b <= t1.b)
-NULL NULL
+NULL 0
DROP TABLE t1, st1, st2;
#
# Bug #48709: Assertion failed in sql_select.cc:11782:
@@ -6647,7 +6647,7 @@ id select_type table type possible_keys key key_len ref rows Extra
2 SUBQUERY t1 ALL NULL NULL NULL NULL 2
SELECT COUNT(f1), (SELECT f1 FROM t1 WHERE f2 > 0 limit 1) AS f4 FROM t2, t1 WHERE 'v'= f3;
COUNT(f1) f4
-0 NULL
+0 7
EXPLAIN
SELECT COUNT(f1), exists(SELECT f1 FROM t1 WHERE f2 > 0 limit 1) AS f4 FROM t2, t1 WHERE 'v'= f3;
id select_type table type possible_keys key key_len ref rows Extra
@@ -6655,7 +6655,7 @@ id select_type table type possible_keys key key_len ref rows Extra
2 SUBQUERY t1 ALL NULL NULL NULL NULL 2
SELECT COUNT(f1), exists(SELECT f1 FROM t1 WHERE f2 > 0 limit 1) AS f4 FROM t2, t1 WHERE 'v'= f3;
COUNT(f1) f4
-0 0
+0 1
EXPLAIN
SELECT COUNT(f1), f2 > ALL (SELECT f1 FROM t1 WHERE f2 > 0) AS f4 FROM t2, t1 WHERE 'v'= f3;
id select_type table type possible_keys key key_len ref rows Extra
diff --git a/mysql-test/r/subselect_sj.result b/mysql-test/r/subselect_sj.result
index fd9a66d8ef1..6f94b6236aa 100644
--- a/mysql-test/r/subselect_sj.result
+++ b/mysql-test/r/subselect_sj.result
@@ -3132,7 +3132,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY t3 ALL NULL NULL NULL NULL 2 100.00 Using where
2 SUBQUERY t4 ALL NULL NULL NULL NULL 2 100.00 Using where
Warnings:
-Note 1003 select `test`.`t1`.`c1` AS `c1` from `test`.`t1` left join (`test`.`t2` join `test`.`t3`) on(((`test`.`t3`.`c3` = `test`.`t1`.`c1`) and <cache>(<in_optimizer>(1,<exists>(select `test`.`t4`.`c4` from `test`.`t4` where (1 = `test`.`t4`.`c4`)))))) where 1
+Note 1003 select `test`.`t1`.`c1` AS `c1` from `test`.`t1` left join (`test`.`t2` join `test`.`t3`) on(((`test`.`t3`.`c3` = `test`.`t1`.`c1`) and 0)) where 1
# mdev-12820
SELECT *
FROM t1
diff --git a/mysql-test/r/subselect_sj_jcl6.result b/mysql-test/r/subselect_sj_jcl6.result
index 71493df594f..e8a7b789bb8 100644
--- a/mysql-test/r/subselect_sj_jcl6.result
+++ b/mysql-test/r/subselect_sj_jcl6.result
@@ -3146,7 +3146,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY t3 ALL NULL NULL NULL NULL 2 100.00 Using where; Using join buffer (incremental, BNL join)
2 SUBQUERY t4 ALL NULL NULL NULL NULL 2 100.00 Using where
Warnings:
-Note 1003 select `test`.`t1`.`c1` AS `c1` from `test`.`t1` left join (`test`.`t2` join `test`.`t3`) on(((`test`.`t3`.`c3` = `test`.`t1`.`c1`) and <cache>(<in_optimizer>(1,<exists>(select `test`.`t4`.`c4` from `test`.`t4` where (1 = `test`.`t4`.`c4`)))))) where 1
+Note 1003 select `test`.`t1`.`c1` AS `c1` from `test`.`t1` left join (`test`.`t2` join `test`.`t3`) on(((`test`.`t3`.`c3` = `test`.`t1`.`c1`) and 0)) where 1
# mdev-12820
SELECT *
FROM t1
diff --git a/mysql-test/r/subselect_sj_mat.result b/mysql-test/r/subselect_sj_mat.result
index b48be32441a..2cb99ae450b 100644
--- a/mysql-test/r/subselect_sj_mat.result
+++ b/mysql-test/r/subselect_sj_mat.result
@@ -1934,7 +1934,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY t1 ALL NULL NULL NULL NULL 3 100.00 Using where; Using join buffer (flat, BNL join)
2 MATERIALIZED t2 ALL NULL NULL NULL NULL 3 100.00
Warnings:
-Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from <materialize> (select max(`test`.`t2`.`c`) from `test`.`t2`) join `test`.`t1` where ((`test`.`t1`.`b` = 7) and (`test`.`t1`.`a` = `<subquery2>`.`MAX(c)`) and (<cache>(isnull(/*always not null*/ 1)) or (`<subquery2>`.`MAX(c)` = 7)))
+Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from <materialize> (select max(`test`.`t2`.`c`) from `test`.`t2`) join `test`.`t1` where ((`test`.`t1`.`b` = 7) and (`test`.`t1`.`a` = `<subquery2>`.`MAX(c)`) and (0 or (`<subquery2>`.`MAX(c)` = 7)))
SELECT * FROM t1
WHERE a IN (SELECT MAX(c) FROM t2) AND b=7 AND (a IS NULL OR a=b);
a b
diff --git a/mysql-test/t/subselect_mat.test b/mysql-test/t/subselect_mat.test
index 5211f35b48b..66a6cc97acb 100644
--- a/mysql-test/t/subselect_mat.test
+++ b/mysql-test/t/subselect_mat.test
@@ -267,3 +267,16 @@ explain
SELECT * FROM t2 WHERE f IN ( SELECT LEFT('foo',0) FROM t1 ORDER BY 1 );
SELECT * FROM t2 WHERE f IN ( SELECT LEFT('foo',0) FROM t1 ORDER BY 1 );
DROP TABLE t1, t2;
+
+--echo #
+--echo # MDEV-18255: Server crashes in Bitmap<64u>::intersect
+--echo #
+create table t1 (v1 varchar(1)) engine=myisam ;
+create table t2 (v1 varchar(1)) engine=myisam ;
+
+explain
+select 1 from t1 where exists
+ (select 1 from t1 where t1.v1 in (select t2.v1 from t2 having t2.v1 < 'j')) ;
+select 1 from t1 where exists
+ (select 1 from t1 where t1.v1 in (select t2.v1 from t2 having t2.v1 < 'j')) ;
+drop table t1,t2;
diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc
index f757823be7c..8a8e390ade3 100644
--- a/sql/opt_subselect.cc
+++ b/sql/opt_subselect.cc
@@ -5298,7 +5298,11 @@ static void remove_subq_pushed_predicates(JOIN *join, Item **where)
bool JOIN::optimize_unflattened_subqueries()
{
- return select_lex->optimize_unflattened_subqueries(false);
+ bool val= select_lex->optimize_unflattened_subqueries(false);
+ if (val)
+ return val;
+ select_lex->update_used_tables();
+ return false;
}
/**
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 08c169c5999..891cf9987c6 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -3551,7 +3551,6 @@ bool st_select_lex::optimize_unflattened_subqueries(bool const_only)
inner_join->select_options|= SELECT_DESCRIBE;
}
res= inner_join->optimize();
- sl->update_used_tables();
sl->update_correlated_cache();
is_correlated_unit|= sl->is_correlated;
inner_join->select_options= save_options;
1
0
revision-id: b14a70f4c8a6a3b0f26f622e0728eabaab95b957 (mariadb-10.3.6-330-gb14a70f4c8a)
parent(s): 140fcab9571dbd15652ed7c7b798818556e3805d
author: Jan LindstrÃļm
committer: Jan LindstrÃļm
timestamp: 2019-01-18 17:05:30 +0200
message:
Fixed compiler error on embedded server linking
---
sql/wsrep_dummy.cc | 39 +++++++++++++++++++++++++++++++++++++++
1 file changed, 39 insertions(+)
diff --git a/sql/wsrep_dummy.cc b/sql/wsrep_dummy.cc
index 593cfeca8ef..916788483ab 100644
--- a/sql/wsrep_dummy.cc
+++ b/sql/wsrep_dummy.cc
@@ -85,3 +85,42 @@ my_bool wsrep_thd_is_aborting(const THD *)
void wsrep_set_data_home_dir(const char *)
{ }
+
+my_bool wsrep_thd_is_local(const THD *)
+{ return 0; }
+
+void wsrep_thd_self_abort(THD *)
+{ }
+
+int wsrep_thd_append_key(THD *, const struct wsrep_key*, int, enum Wsrep_service_key_type)
+{ return 0; }
+
+const char* wsrep_thd_client_state_str(const THD*)
+{ return 0; }
+
+const char* wsrep_thd_client_mode_str(const THD*)
+{ return 0; }
+
+const char* wsrep_thd_transaction_state_str(const THD*)
+{ return 0; }
+
+query_id_t wsrep_thd_transaction_id(const THD *)
+{ return 0; }
+
+my_bool wsrep_thd_bf_abort(const THD *, THD *, my_bool)
+{ return 0; }
+
+my_bool wsrep_thd_order_before(const THD*, const THD *)
+{ return 0; }
+
+void wsrep_handle_SR_rollback(THD*, THD*)
+{ }
+
+my_bool wsrep_thd_skip_locking(const THD*)
+{ return 0;}
+
+const char* wsrep_get_sr_table_name()
+{ return 0; }
+
+my_bool wsrep_get_debug()
+{ return 0;}
1
0