MySQL 9.1.0
Source Code Documentation
hash_join_buffer.h
Go to the documentation of this file.
1#ifndef SQL_ITERATORS_HASH_JOIN_BUFFER_H_
2#define SQL_ITERATORS_HASH_JOIN_BUFFER_H_
3
4/* Copyright (c) 2019, 2024, 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 designed to work 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 either included with
16 the program or referenced in the documentation.
17
18 This program is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU General Public License, version 2.0, for more details.
22
23 You should have received a copy of the GNU General Public License
24 along with this program; if not, write to the Free Software
25 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
26
27/// @file
28///
29/// This file contains the HashJoinRowBuffer class and related
30/// functions/classes.
31///
32/// A HashJoinBuffer is a row buffer that can hold a certain amount of rows.
33/// The rows are stored in a hash table, which allows for constant-time lookup.
34/// The HashJoinBuffer maintains its own internal MEM_ROOT, where all of the
35/// data is allocated.
36///
37/// The HashJoinBuffer contains an operand with rows from one or more tables,
38/// keyed on the value we join on. Consider the following trivial example:
39///
40/// SELECT t1.data FROM t1 JOIN t2 ON (t1.key = t2.key);
41///
42/// Let us say that the table "t2" is stored in a HashJoinBuffer. In this case,
43/// the hash table key will be the value found in "t2.key", since that is the
44/// join condition that belongs to t2. If we have multiple equalities, they
45/// will be concatenated together in order to form the hash table key. The hash
46/// table key is a std::string_view.
47///
48/// In order to store a row, we use the function StoreFromTableBuffers. See the
49/// comments attached to the function for more details.
50///
51/// The amount of memory a HashJoinBuffer instance can use is limited by the
52/// system variable "join_buffer_size". However, note that we check whether we
53/// have exceeded the memory limit _after_ we have inserted data into the row
54/// buffer. As such, we will probably use a little bit more memory than
55/// specified by join_buffer_size.
56///
57/// The primary use case for these classes is, as the name implies,
58/// for implementing hash join.
59
60#include <stddef.h>
61#include <cassert>
62#include <memory>
63#include <optional>
64#include <string_view>
65#include <vector>
66
67#include "my_alloc.h"
69#include "sql/pack_rows.h"
70#include "sql_string.h"
71
73class THD;
74
76 bool m_dont_error{false}; // input
77 bool m_full{false}; // output
78 size_t m_bytes_needed{0}; // output
79};
80
81namespace hash_join_buffer {
82
83/// The key type for the hash structure in HashJoinRowBuffer.
84///
85/// A key consists of the value from one or more columns, taken from the join
86/// condition(s) in the query. E.g., if the join condition is
87/// (t1.col1 = t2.col1 AND t1.col2 = t2.col2), the key is (col1, col2), with the
88/// two key parts concatenated together.
89///
90/// What the data actually contains depends on the comparison context for the
91/// join condition. For instance, if the join condition is between a string
92/// column and an integer column, the comparison will be done in a string
93/// context, and thus the integers will be converted to strings before storing.
94/// So the data we store in the key are in some cases converted, so that we can
95/// hash and compare them byte-by-byte (i.e. decimals), while other types are
96/// already comparable byte-by-byte (i.e. integers), and thus stored as-is.
97///
98/// Note that the key data can come from items as well as fields if the join
99/// condition is an expression. E.g. if the join condition is
100/// UPPER(t1.col1) = UPPER(t2.col1), the join key data will come from an Item
101/// instead of a Field.
102///
103/// The Key class never takes ownership of the data. As such, the user must
104/// ensure that the data has the proper lifetime. When storing rows in the row
105/// buffer, the data must have the same lifetime as the row buffer itself.
106/// When using the Key class for lookups in the row buffer, the same lifetime is
107/// not needed; the key object is only needed when the lookup is done.
108using Key = std::string_view;
109
110// A row in the hash join buffer is the same as the Key class.
112
113// A convenience form of LoadIntoTableBuffers() that also verifies the end
114// pointer for us.
116 BufferRow row);
117
118// A convenience form of the above that also decodes the LinkedImmutableString
119// for us.
122
124
126 public:
127 // Construct the buffer. Note that Init() must be called before the buffer can
128 // be used.
130 std::vector<HashJoinCondition> join_conditions,
131 size_t max_mem_available_bytes);
132
134
135 // Initialize the HashJoinRowBuffer so it is ready to store rows. This
136 // function can be called multiple times; subsequent calls will only clear the
137 // buffer for existing rows.
138 bool Init();
139
140 /// Store the row that is currently lying in the tables record buffers.
141 /// The hash map key is extracted from the join conditions that the row buffer
142 /// holds.
143 ///
144 /// @param thd the thread handler
145 /// @param reject_duplicate_keys If true, reject rows with duplicate keys.
146 /// If a row is rejected, the function will still return ROW_STORED.
147 ///
148 /// @retval ROW_STORED the row was stored.
149 /// @retval BUFFER_FULL the row was stored, and the buffer is full.
150 /// @retval FATAL_ERROR an unrecoverable error occurred (most likely,
151 /// malloc failed). It is the caller's responsibility to call
152 /// my_error().
153 StoreRowResult StoreRow(THD *thd, bool reject_duplicate_keys);
154
155 size_t size() const;
156
157 bool empty() const { return size() == 0; }
158
159 std::optional<LinkedImmutableString> find(Key key) const;
160
161 std::optional<LinkedImmutableString> first_row() const;
162
164 assert(Initialized());
165 return m_last_row_stored;
166 }
167
168 bool Initialized() const { return m_hash_map != nullptr; }
169
170 bool contains(const Key &key) const { return find(key).has_value(); }
171
172 private:
173 // The type of hash map in which the rows are stored.
174 class HashMap;
175
176 const std::vector<HashJoinCondition> m_join_conditions;
177
178 // A row can consist of parts from different tables. This structure tells us
179 // which tables that are involved.
181
182 // The MEM_ROOT on which all of the hash table keys and values are allocated.
183 // The actual hash map is on the regular heap.
185
186 // A MEM_ROOT used only for storing the final row (possibly both key and
187 // value). The code assumes fairly deeply that inserting a row never fails, so
188 // when m_mem_root goes full (we set a capacity on it to ensure that the last
189 // allocated block does not get too big), we allocate the very last row on
190 // this MEM_ROOT and the signal fullness so that we can start spilling to
191 // disk.
193
194 // The hash table where the rows are stored.
195 std::unique_ptr<HashMap> m_hash_map;
196
197 // A buffer we can use when we are constructing a join key from a join
198 // condition. In order to avoid reallocating memory, the buffer never shrinks.
201
202 // The maximum size of the buffer, given in bytes.
204
205 // The last row that was stored in the hash table, or nullptr if the hash
206 // table is empty. We may have to put this row back into the tables' record
207 // buffers if we have a child iterator that expects the record buffers to
208 // contain the last row returned by the storage engine (the probe phase of
209 // hash join may put any row in the hash table in the tables' record buffer).
210 // See HashJoinIterator::BuildHashTable() for an example of this.
212
213 // Fetch the relevant fields from each table, and pack them into m_mem_root
214 // as a LinkedImmutableString where the “next” pointer points to “next_ptr”.
215 // If that does not work (capacity reached), pack into m_overflow_mem_root
216 // instead and set “full” to true. If _that_ does not work (fatally out
217 // of memory), returns nullptr. Otherwise, returns a pointer to the newly
218 // packed string.
220 LinkedImmutableString next_ptr, StoreLinkedInfo *info);
221};
222
223} // namespace hash_join_buffer
224
225/// External interface to the corresponding member in HashJoinRowBuffer
227 MEM_ROOT *mem_root, MEM_ROOT *overflow_mem_root,
229 size_t row_size_upper_bound, StoreLinkedInfo *info);
230
231#endif // SQL_ITERATORS_HASH_JOIN_BUFFER_H_
A class that represents a join condition in a hash join.
Definition: item_cmpfunc.h:87
LinkedImmutableString is designed for storing rows (values) in hash join.
Definition: immutable_string.h:173
Using this class is fraught with peril, and you need to be very careful when doing so.
Definition: sql_string.h:167
For each client connection we create a separate thread with THD serving as a thread/connection descri...
Definition: sql_lexer_thd.h:36
Definition: hash_join_buffer.h:125
String m_buffer
Definition: hash_join_buffer.h:199
bool contains(const Key &key) const
Definition: hash_join_buffer.h:170
size_t m_row_size_upper_bound
Definition: hash_join_buffer.h:200
std::unique_ptr< HashMap > m_hash_map
Definition: hash_join_buffer.h:195
std::optional< LinkedImmutableString > first_row() const
Definition: hash_join_buffer.cc:349
size_t size() const
Definition: hash_join_buffer.cc:341
bool Init()
Definition: hash_join_buffer.cc:204
bool Initialized() const
Definition: hash_join_buffer.h:168
bool empty() const
Definition: hash_join_buffer.h:157
HashJoinRowBuffer(pack_rows::TableCollection tables, std::vector< HashJoinCondition > join_conditions, size_t max_mem_available_bytes)
Definition: hash_join_buffer.cc:186
std::optional< LinkedImmutableString > find(Key key) const
Definition: hash_join_buffer.cc:343
LinkedImmutableString m_last_row_stored
Definition: hash_join_buffer.h:211
const std::vector< HashJoinCondition > m_join_conditions
Definition: hash_join_buffer.h:174
const size_t m_max_mem_available
Definition: hash_join_buffer.h:203
StoreRowResult StoreRow(THD *thd, bool reject_duplicate_keys)
Store the row that is currently lying in the tables record buffers.
Definition: hash_join_buffer.cc:234
const pack_rows::TableCollection m_tables
Definition: hash_join_buffer.h:180
LinkedImmutableString StoreLinkedImmutableStringFromTableBuffers(LinkedImmutableString next_ptr, StoreLinkedInfo *info)
Definition: hash_join_buffer.cc:165
LinkedImmutableString LastRowStored() const
Definition: hash_join_buffer.h:163
MEM_ROOT m_overflow_mem_root
Definition: hash_join_buffer.h:192
MEM_ROOT m_mem_root
Definition: hash_join_buffer.h:184
A structure that contains a list of input tables for a hash join operation, BKA join operation or a s...
Definition: pack_rows.h:93
static MEM_ROOT mem_root
Definition: client_plugin.cc:114
LinkedImmutableString StoreLinkedImmutableStringFromTableBuffers(MEM_ROOT *mem_root, MEM_ROOT *overflow_mem_root, const pack_rows::TableCollection &tables, LinkedImmutableString next_ptr, size_t row_size_upper_bound, StoreLinkedInfo *info)
External interface to the corresponding member in HashJoinRowBuffer.
Definition: hash_join_buffer.cc:48
ImmutableString defines a storage format for strings that is designed to be as compact as possible,...
This file follows Google coding style, except for the name MEM_ROOT (which is kept for historical rea...
Definition: hash_join_buffer.cc:103
std::string_view Key
The key type for the hash structure in HashJoinRowBuffer.
Definition: hash_join_buffer.h:108
Key BufferRow
Definition: hash_join_buffer.h:111
void LoadImmutableStringIntoTableBuffers(const TableCollection &tables, LinkedImmutableString row)
Definition: hash_join_buffer.cc:181
void LoadBufferRowIntoTableBuffers(const TableCollection &tables, BufferRow row)
Definition: hash_join_buffer.cc:174
StoreRowResult
Definition: hash_join_buffer.h:123
Generic routines for packing rows (possibly from multiple tables at the same time) into strings,...
required string key
Definition: replication_asynchronous_connection_failover.proto:60
Our own string classes, used pervasively throughout the executor.
The MEM_ROOT is a simple arena, where allocations are carved out of larger blocks.
Definition: my_alloc.h:83
Definition: hash_join_buffer.h:75
size_t m_bytes_needed
Definition: hash_join_buffer.h:78
bool m_full
Definition: hash_join_buffer.h:77
bool m_dont_error
Definition: hash_join_buffer.h:76