Skip to content

Commit be370ae

Browse files
committed
WL#14672: Enable the hypergraph optimizer for UPDATE [5/8, iterator]
A new iterator class, UpdateRowsIterator, is added, and CreateIteratorFromAccessPath() creates an iterator of this type for access paths of type UPDATE_ROWS. Logic previously found in Query_result_update::send_data() is moved into UpdateRowsIterator::Read(). This follows the pattern used by DELETE. Moving logic from Query_result_delete to an iterator makes EXPLAIN ANALYZE show timing information also for the top-level UPDATE operation. Previously, it would only show the timing for the join operation. Change-Id: I7b64dc10267d5e9c05d03e8bf467d09f37864f00
1 parent 520aecb commit be370ae

File tree

7 files changed

+370
-186
lines changed

7 files changed

+370
-186
lines changed

mysql-test/r/explain_tree.result

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -979,6 +979,28 @@ x
979979
3
980980
DROP TABLE t;
981981
#
982+
# WL#14672: Enable the hypergraph optimizer for UPDATE
983+
#
984+
CREATE TABLE t(x INTEGER, y INTEGER);
985+
INSERT INTO t VALUES (1, 2), (2, 3), (3, 4);
986+
ANALYZE TABLE t;
987+
Table Op Msg_type Msg_text
988+
test.t analyze status OK
989+
EXPLAIN ANALYZE UPDATE t t1, t t2 SET t1.x = t2.y WHERE t1.x = t2.x;
990+
EXPLAIN
991+
-> Update t1 (buffered) (actual time=N.NNN..N.NNN rows=0 loops=1)
992+
-> Nested loop inner join (cost=2.20 rows=3) (actual time=N.NNN..N.NNN rows=3 loops=1)
993+
-> Table scan on t1 (cost=0.55 rows=3) (actual time=N.NNN..N.NNN rows=3 loops=1)
994+
-> Filter: (t2.x = t1.x) (cost=0.28 rows=1) (actual time=N.NNN..N.NNN rows=1 loops=3)
995+
-> Table scan on t2 (cost=0.28 rows=3) (actual time=N.NNN..N.NNN rows=3 loops=3)
996+
997+
SELECT * FROM t;
998+
x y
999+
1 2
1000+
2 3
1001+
3 4
1002+
DROP TABLE t;
1003+
#
9821004
# Bug#33905399 Missing profiling data for CTE
9831005
#
9841006
CREATE TABLE t1 (a INT PRIMARY KEY, b INT);

mysql-test/t/explain_tree.test

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -587,9 +587,28 @@ EXPLAIN FORMAT=TREE DELETE t FROM t WHERE 0=1;
587587
# Demonstrates EXPLAIN ANALYZE on a DELETE statement.
588588
--replace_regex /time=\d+\.\d+\.\.\d+\.\d+/time=N.NNN..N.NNN/
589589
EXPLAIN ANALYZE DELETE t1 FROM t t1, t t2 WHERE t1.x = t2.x + 1;
590-
# Should EXPLAIN ANALYZE DELETE actually delete rows? Currently, it
591-
# does not. (And EXPLAIN ANALYZE UPDATE/INSERT don't modify the table
592-
# either.)
590+
# EXPLAIN ANALYZE DELETE should delete rows. Currently, it does not.
591+
# (And EXPLAIN ANALYZE UPDATE/INSERT don't modify the table either.)
592+
# Tracked in bug#33962407.
593+
--sorted_result
594+
SELECT * FROM t;
595+
596+
DROP TABLE t;
597+
598+
--echo #
599+
--echo # WL#14672: Enable the hypergraph optimizer for UPDATE
600+
--echo #
601+
602+
CREATE TABLE t(x INTEGER, y INTEGER);
603+
INSERT INTO t VALUES (1, 2), (2, 3), (3, 4);
604+
ANALYZE TABLE t;
605+
606+
# Demonstrates EXPLAIN ANALYZE on an UPDATE statement.
607+
--replace_regex /time=\d+\.\d+\.\.\d+\.\d+/time=N.NNN..N.NNN/
608+
EXPLAIN ANALYZE UPDATE t t1, t t2 SET t1.x = t2.y WHERE t1.x = t2.x;
609+
# EXPLAIN ANALYZE UPDATE should update rows. Currently, it does not.
610+
# (And EXPLAIN ANALYZE INSERT/DELETE don't modify the table either.)
611+
# Tracked in bug#33962407.
593612
--sorted_result
594613
SELECT * FROM t;
595614

sql/iterators/update_rows_iterator.h

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
#ifndef SQL_ITERATORS_UPDATE_ROWS_ITERATOR_H_
2+
#define SQL_ITERATORS_UPDATE_ROWS_ITERATOR_H_
3+
4+
/* Copyright (c) 2022, Oracle and/or its affiliates.
5+
6+
This program is free software; you can redistribute it and/or modify
7+
it under the terms of the GNU General Public License, version 2.0,
8+
as published by the Free Software Foundation.
9+
10+
This program is also distributed with certain software (including
11+
but not limited to OpenSSL) that is licensed under separate terms,
12+
as designated in a particular file or component or in included license
13+
documentation. The authors of MySQL hereby grant you an additional
14+
permission to link the program and your derivative works with the
15+
separately licensed software that they have included with MySQL.
16+
17+
This program is distributed in the hope that it will be useful,
18+
but WITHOUT ANY WARRANTY; without even the implied warranty of
19+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20+
GNU General Public License, version 2.0, for more details.
21+
22+
You should have received a copy of the GNU General Public License
23+
along with this program; if not, write to the Free Software
24+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
25+
26+
#include <cassert>
27+
#include <memory>
28+
29+
#include "my_alloc.h"
30+
#include "my_base.h"
31+
#include "sql/iterators/row_iterator.h"
32+
#include "sql/sql_list.h"
33+
34+
class COPY_INFO;
35+
class Copy_field;
36+
class Item;
37+
class THD;
38+
struct TABLE;
39+
struct TABLE_LIST;
40+
template <class T>
41+
class mem_root_deque;
42+
43+
/// An iterator that performs updates to rows returned by its child iterator.
44+
class UpdateRowsIterator final : public RowIterator {
45+
public:
46+
UpdateRowsIterator(THD *thd, unique_ptr_destroy_only<RowIterator> source,
47+
TABLE *outermost_table, TABLE *immediate_table,
48+
TABLE_LIST *update_tables, TABLE **tmp_tables,
49+
Copy_field *copy_fields,
50+
List<TABLE> unupdated_check_opt_tables,
51+
COPY_INFO **update_operations,
52+
mem_root_deque<Item *> **fields_for_table,
53+
mem_root_deque<Item *> **values_for_table);
54+
~UpdateRowsIterator() override = default;
55+
bool Init() override;
56+
int Read() override;
57+
void StartPSIBatchMode() override { m_source->StartPSIBatchMode(); }
58+
void EndPSIBatchModeIfStarted() override {
59+
m_source->EndPSIBatchModeIfStarted();
60+
}
61+
void SetNullRowFlag([[maybe_unused]] bool is_null_row) override {
62+
assert(false);
63+
}
64+
void UnlockRow() override { assert(false); }
65+
ha_rows found_rows() const { return m_found_rows; }
66+
ha_rows updated_rows() const { return m_updated_rows; }
67+
68+
private:
69+
/// The iterator producing the rows to update.
70+
unique_ptr_destroy_only<RowIterator> m_source;
71+
/// The outermost table of the join. It may or may not be one of the tables
72+
/// being updated.
73+
TABLE *m_outermost_table;
74+
/// The table to perform immediate update on, or nullptr if immediate update
75+
/// is not possible.
76+
TABLE *m_immediate_table;
77+
/// Pointer to list of updated tables, linked via 'next_local'.
78+
TABLE_LIST *m_update_tables;
79+
/// Temporary tables used to store cached updates.
80+
TABLE **m_tmp_tables;
81+
/// Objects that copy the updated values from a temporary table to the update
82+
/// target table, and perform conversions if the types differ.
83+
Copy_field *m_copy_fields;
84+
/// Tables referenced in the CHECK OPTION condition of the updated view
85+
/// excluding the updated table.
86+
List<TABLE> m_unupdated_check_opt_tables;
87+
/// The update operations of each table in m_update_tables (indexed in the
88+
/// same order as m_update_tables).
89+
COPY_INFO **m_update_operations;
90+
/// The fields list decomposed into separate lists per table.
91+
mem_root_deque<Item *> **m_fields_for_table;
92+
/// The values list decomposed into separate lists per table.
93+
mem_root_deque<Item *> **m_values_for_table;
94+
/// The number of rows matching the WHERE and join conditions.
95+
ha_rows m_found_rows{0};
96+
/// The number of rows actually updated.
97+
ha_rows m_updated_rows{0};
98+
99+
/// Perform all the immediate updates for the current row returned by the
100+
/// join, and buffer row IDs for the non-immediate tables.
101+
///
102+
/// @param[out] trans_safe Gets set to false if a non-transactional table
103+
/// is updated.
104+
/// @param[out] transactional_tables Gets set to true if a transactional
105+
/// table is updated.
106+
/// @return True on error.
107+
bool DoImmediateUpdatesAndBufferRowIds(bool *trans_safe,
108+
bool *transactional_tables);
109+
110+
/// Perform all the delayed updates.
111+
///
112+
/// @param[in,out] trans_safe Gets set to false if a non-transactional table
113+
/// is updated.
114+
/// @param[out] transactional_tables Gets set to true if a transactional
115+
/// table is updated.
116+
/// @return True on error.
117+
bool DoDelayedUpdates(bool *trans_safe, bool *transactional_tables);
118+
};
119+
120+
#endif // SQL_ITERATORS_UPDATE_ROWS_ITERATOR_H_

sql/join_optimizer/access_path.cc

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1131,10 +1131,8 @@ unique_ptr_destroy_only<RowIterator> CreateIteratorFromAccessPath(
11311131
eligible_for_batch_mode, &job, &todo);
11321132
continue;
11331133
}
1134-
// TODO(khatlen): We should have an iterator for the update operation.
1135-
// For now, return the child. Query_result_update will act as a
1136-
// substitute for the update rows iterator.
1137-
iterator = std::move(job.children[0]);
1134+
iterator = CreateUpdateRowsIterator(thd, mem_root, join,
1135+
std::move(job.children[0]));
11381136
break;
11391137
}
11401138
}

sql/sql_delete.cc

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1044,9 +1044,9 @@ bool DeleteRowsIterator::Init() {
10441044
bool DeleteRowsIterator::DoImmediateDeletesAndBufferRowIds() {
10451045
DBUG_TRACE;
10461046

1047-
// For now, don't actually delete anything in EXPLAIN ANALYZE. (Maybe we
1048-
// should have done the deletions. If so, INSERT and UPDATE should also be
1049-
// changed to have side effects when running under EXPLAIN ANALYZE.)
1047+
// For now, don't actually delete anything in EXPLAIN ANALYZE. (If we enable
1048+
// it, INSERT and UPDATE should also be changed to have side effects when
1049+
// running under EXPLAIN ANALYZE.)
10501050
if (thd()->lex->is_explain_analyze) {
10511051
return false;
10521052
}

0 commit comments

Comments
 (0)