Skip to content

Commit 2837ba5

Browse files
author
Dag Wanvik
committed
Bug#36307622 [back-port] Wrong result from query with WHERE integer IN (SELECT 2 EXCEPT SELECT 4)
A work-around is to set the optimizer flag to not use hash map de-duplication for INTERSECT, EXCEPT, like so: SET optimizer_switch="hash_set_operations=off"; With hash_set_operations enabled, however, we get too may result rows. For the IN predicate, the set operation is computed repeatedly, with filters pushed down to set set operation operands: -> Filter: <in_optimizer>(c.pk,<exists>(select #2)) (cost=2.25 rows=20) -> Covering index scan on c using idx_c_col_datetime_key (cost=2.25 rows=20) -> Select #2 (subquery in condition; dependent) -> Limit: 1 row(s) (cost=2.61..2.61 rows=1) -> Table scan on <except temporary> (cost=2.61..2.61 rows=1) -> Except materialize with deduplication (cost=0.1..0.1 rows=1) -> Filter: (<cache>(c.pk) = <ref_null_helper>(2)) (cost=0..0 rows=1) -> Rows fetched before execution (cost=0..0 rows=1) -> Filter: (<cache>(c.pk) = <ref_null_helper>(4)) (cost=0..0 rows=1) -> Rows fetched before execution (cost=0..0 rows=1) Only the row with pk==2 should pass the filters under the except node, and that's what happens. However, on repeated execution, the hash map used to implement the Except materialize is not re-initialized to being empty. The patch adds reinitialization of the hash map for such cases. Change-Id: Idf2e36f9085e36748900017a0aad420e4e476f78
1 parent 5969f18 commit 2837ba5

File tree

3 files changed

+93
-13
lines changed

3 files changed

+93
-13
lines changed

mysql-test/r/query_expression-bugs.result

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,3 +334,41 @@ j
334334
DROP PREPARE p0;
335335
SET SESSION optimizer_trace = 'enabled=default';
336336
SET SESSION optimizer_switch = 'hash_set_operations=default';
337+
#
338+
# Bug#36307622 Wrong result from query with WHERE integer IN (SELECT 2 EXCEPT SELECT 4)
339+
#
340+
CREATE TABLE c (
341+
pk int NOT NULL AUTO_INCREMENT,
342+
col_datetime_key datetime DEFAULT NULL,
343+
col_varchar_key varchar(1) DEFAULT NULL,
344+
PRIMARY KEY (pk),
345+
KEY idx_c_col_datetime_key (col_datetime_key),
346+
KEY idx_c_col_varchar_key (col_varchar_key)
347+
);
348+
INSERT INTO c VALUES (1,'2022-10-30 08:18:58','o');
349+
INSERT INTO c VALUES (2,'1998-01-19 17:27:57','䋠');
350+
INSERT INTO c VALUES (3,'2015-09-01 16:34:18','X');
351+
INSERT INTO c VALUES (4,'2020-08-29 15:09:33','m');
352+
INSERT INTO c VALUES (5,'2018-07-01 22:36:45','d');
353+
INSERT INTO c VALUES (6,'2028-02-07 02:02:10','q');
354+
INSERT INTO c VALUES (7,'2016-02-04 17:29:46','8');
355+
INSERT INTO c VALUES (8,'2037-07-02 21:02:05','0');
356+
INSERT INTO c VALUES (9,'1970-08-03 04:25:41','旘');
357+
INSERT INTO c VALUES (10,'1973-07-18 13:38:35','v');
358+
INSERT INTO c VALUES (11,'1990-08-03 14:18:01','υ');
359+
INSERT INTO c VALUES (12,'2036-07-05 20:41:55','dž');
360+
INSERT INTO c VALUES (13,'2035-02-11 10:59:22','䩈');
361+
INSERT INTO c VALUES (14,'1992-10-24 00:44:20','L');
362+
INSERT INTO c VALUES (15,'1995-05-04 07:35:45','W');
363+
INSERT INTO c VALUES (16,'2027-01-15 17:09:03','η');
364+
INSERT INTO c VALUES (17,'1998-02-15 07:48:55','V');
365+
INSERT INTO c VALUES (18,'1998-02-16 17:42:54','᥋');
366+
INSERT INTO c VALUES (19,'1991-03-04 15:36:40','D');
367+
INSERT INTO c VALUES (20,'1973-06-24 15:12:44','O');
368+
SELECT pk FROM c WHERE pk IN (SELECT 2 EXCEPT SELECT 4);
369+
pk
370+
2
371+
SELECT pk FROM c WHERE pk IN (SELECT 2 EXCEPT SELECT 4) ORDER BY pk;
372+
pk
373+
2
374+
DROP TABLE c;

mysql-test/t/query_expression-bugs.test

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,3 +116,41 @@ DROP PREPARE p0;
116116

117117
SET SESSION optimizer_trace = 'enabled=default';
118118
SET SESSION optimizer_switch = 'hash_set_operations=default';
119+
120+
--echo #
121+
--echo # Bug#36307622 Wrong result from query with WHERE integer IN (SELECT 2 EXCEPT SELECT 4)
122+
--echo #
123+
CREATE TABLE c (
124+
pk int NOT NULL AUTO_INCREMENT,
125+
col_datetime_key datetime DEFAULT NULL,
126+
col_varchar_key varchar(1) DEFAULT NULL,
127+
PRIMARY KEY (pk),
128+
KEY idx_c_col_datetime_key (col_datetime_key),
129+
KEY idx_c_col_varchar_key (col_varchar_key)
130+
);
131+
132+
INSERT INTO c VALUES (1,'2022-10-30 08:18:58','o');
133+
INSERT INTO c VALUES (2,'1998-01-19 17:27:57','䋠');
134+
INSERT INTO c VALUES (3,'2015-09-01 16:34:18','X');
135+
INSERT INTO c VALUES (4,'2020-08-29 15:09:33','m');
136+
INSERT INTO c VALUES (5,'2018-07-01 22:36:45','d');
137+
INSERT INTO c VALUES (6,'2028-02-07 02:02:10','q');
138+
INSERT INTO c VALUES (7,'2016-02-04 17:29:46','8');
139+
INSERT INTO c VALUES (8,'2037-07-02 21:02:05','0');
140+
INSERT INTO c VALUES (9,'1970-08-03 04:25:41','旘');
141+
INSERT INTO c VALUES (10,'1973-07-18 13:38:35','v');
142+
INSERT INTO c VALUES (11,'1990-08-03 14:18:01','υ');
143+
INSERT INTO c VALUES (12,'2036-07-05 20:41:55','dž');
144+
INSERT INTO c VALUES (13,'2035-02-11 10:59:22','䩈');
145+
INSERT INTO c VALUES (14,'1992-10-24 00:44:20','L');
146+
INSERT INTO c VALUES (15,'1995-05-04 07:35:45','W');
147+
INSERT INTO c VALUES (16,'2027-01-15 17:09:03','η');
148+
INSERT INTO c VALUES (17,'1998-02-15 07:48:55','V');
149+
INSERT INTO c VALUES (18,'1998-02-16 17:42:54','᥋');
150+
INSERT INTO c VALUES (19,'1991-03-04 15:36:40','D');
151+
INSERT INTO c VALUES (20,'1973-06-24 15:12:44','O');
152+
153+
SELECT pk FROM c WHERE pk IN (SELECT 2 EXCEPT SELECT 4);
154+
SELECT pk FROM c WHERE pk IN (SELECT 2 EXCEPT SELECT 4) ORDER BY pk;
155+
156+
DROP TABLE c;

sql/iterators/composite_iterators.cc

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -618,6 +618,13 @@ class ImmutableStringHasher {
618618

619619
using materialize_iterator::Operand;
620620
using Operands = Mem_root_array<Operand>;
621+
using hash_map_type = ankerl::unordered_dense::segmented_map<
622+
ImmutableStringWithLength, LinkedImmutableString, ImmutableStringHasher>;
623+
624+
void reset_hash_map(hash_map_type *hash_map) {
625+
std::destroy_at(hash_map);
626+
std::construct_at(hash_map);
627+
}
621628

622629
/**
623630
Contains spill state for set operations' use of in-memory hash map.
@@ -876,17 +883,6 @@ class SpillState {
876883

877884
void set_secondary_overflow() { m_secondary_overflow = true; }
878885

879-
using hash_map_type = ankerl::unordered_dense::segmented_map<
880-
ImmutableStringWithLength, LinkedImmutableString, ImmutableStringHasher>;
881-
882-
static void reset_hash_map(hash_map_type *hash_map) {
883-
hash_map->~hash_map_type();
884-
auto *map = new (hash_map) hash_map_type();
885-
if (map == nullptr) {
886-
my_error(ER_OUTOFMEMORY, MYF(ME_FATALERROR), sizeof(hash_map_type));
887-
}
888-
}
889-
890886
/// Getter, cf. comment for \c m_secondary_overflow
891887
bool secondary_overflow() const { return m_secondary_overflow; }
892888
void secondary_overflow_handling_done() {
@@ -1385,8 +1381,6 @@ class MaterializeIterator final : public TableRowIterator {
13851381
MEM_ROOT *m_overflow_mem_root{nullptr};
13861382
size_t m_row_size_upper_bound;
13871383

1388-
using hash_map_type = SpillState::hash_map_type;
1389-
13901384
// The hash map where the rows are stored.
13911385
std::unique_ptr<hash_map_type> m_hash_map;
13921386

@@ -1445,6 +1439,7 @@ class MaterializeIterator final : public TableRowIterator {
14451439
bool process_row_hash(const Operand &operand, TABLE *t, ha_rows *stored_rows);
14461440
bool materialize_hash_map(TABLE *t, ha_rows *stored_rows);
14471441
bool load_HF_row_into_hash_map();
1442+
void init_hash_map_for_new_exec();
14481443
friend class SpillState;
14491444
};
14501445

@@ -1576,6 +1571,7 @@ bool MaterializeIterator<Profiler>::Init() {
15761571
} else {
15771572
table()->file->ha_index_or_rnd_end(); // @todo likely unneeded => remove
15781573
table()->file->ha_delete_all_rows();
1574+
if (m_use_hash_map) init_hash_map_for_new_exec();
15791575
}
15801576

15811577
if (m_query_expression != nullptr)
@@ -1976,6 +1972,14 @@ bool MaterializeIterator<Profiler>::load_HF_row_into_hash_map() {
19761972
return false;
19771973
}
19781974

1975+
template <typename Profiler>
1976+
void MaterializeIterator<Profiler>::init_hash_map_for_new_exec() {
1977+
if (m_hash_map == nullptr) return; // not used yet
1978+
reset_hash_map(m_hash_map.get());
1979+
m_mem_root->ClearForReuse();
1980+
m_rows_in_hash_map = 0;
1981+
}
1982+
19791983
/**
19801984
Save (or restore) blob pointers in \c Field::m_blob_backup. We need to have
19811985
two full copies of a record for comparison, so we save record[0] to record[1]

0 commit comments

Comments
 (0)