MySQL 9.7.0
Source Code Documentation
table_with_cursor.h
Go to the documentation of this file.
1/* Copyright (c) 2024, 2026, Oracle and/or its affiliates.
2
3This program is free software; you can redistribute it and/or modify
4it under the terms of the GNU General Public License, version 2.0,
5as published by the Free Software Foundation.
6
7This program is designed to work with certain software (including
8but not limited to OpenSSL) that is licensed under separate terms,
9as designated in a particular file or component or in included license
10documentation. The authors of MySQL hereby grant you an additional
11permission to link the program and your derivative works with the
12separately licensed software that they have either included with
13the program or referenced in the documentation.
14
15This program is distributed in the hope that it will be useful,
16but WITHOUT ANY WARRANTY; without even the implied warranty of
17MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18GNU General Public License, version 2.0, for more details.
19
20You should have received a copy of the GNU General Public License
21along with this program; if not, write to the Free Software
22Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
23
24#ifndef TABLE_WITH_CURSOR_H
25#define TABLE_WITH_CURSOR_H
26
27#include <concepts> // std::convertible_to
28#include <cstring> // std::strlen
29#include <type_traits> // std::is_trivially_copyable_v
30#include "mysql/components/services/pfs_plugin_table_service.h" // READONLY, PSI_table_handle, PSI_field, PSI_pos, PFS_HA_ERR_END_OF_FILE, PFS_engine_table_share_proxy
31
32/// Concept requiring that objects are copyable using memcpy, and
33/// equality-testable using memcmp.
34///
35/// "Copyable using memcpy" is implied by std::is_trivially_copyable_v<Type>.
36///
37/// "Equality-testable using memcmp" cannot be checked in C++. Therefore, that
38/// is a semantic requirement (see https://en.cppreference.com/w/cpp/concepts ).
39/// A type that fails to be comparable using memcpy will not necessarily make
40/// the concept false; only make the program behavior undefined. This semantic
41/// requirement implies that the type has no internal padding bytes, which is
42/// for example guaranteed by:
43///
44/// - arithmetic types (https://en.cppreference.com/w/cpp/types/is_arithmetic);
45///
46/// - arrays of padding-free objects whose sizes are powers of two;
47///
48/// - structs/classes which satisfy std::standard_layout_v, for which all data
49/// members (1) are padding-free, and (2) have sizes that are powers of two, and
50/// (3) are ordered by size, largest first.
51///
52/// Although the compiler cannot deduce if a class is equality-testable using
53/// memcmp, you can check it manually on a given platform, by verifying
54/// (recursively, for nested objets) that the sum of the sizes of all parts is
55/// equal the size of the object as a whole.
56///
57/// tparam Type The class to test.
58template <class Type>
59concept Is_trivially_comparable = std::is_trivially_copyable_v<Type>
60 /* && has no internal padding bytes */;
61
62/// Concept that identifies that a class is a "table with cursor".
63///
64/// A table with cursor can be used to define a performance_schema table, using
65/// @c get_table_share_from_table_with_cursor.
66///
67/// An object represent a view over a snapshot of a table, together with a row
68/// cursor. The cursor should at any time be positioned either on a row, or at
69/// one-past-the-last-element. The class should implement the following members:
70///
71/// - Default constructor: Must ensure that the object holds a snapshot of the
72/// table, and must position the cursor at the first row. (Or if the table is
73/// empty, position the cursor at one-past-the-last-row.)
74///
75/// - advance() const: Must advance the cursor to the next row, if it is not
76/// already at the end.
77///
78/// - get_cursor() const: Must return a cursor object that represents the
79/// current position. The return type must model @c Is_trivially_comparable.
80///
81/// - set_cursor(Position): Given an object returned from `get_cursor()`, or a
82/// bytewise copy of one, this function must move the cursor to that position.
83/// The parameter type must be the same as the return type for `get_cursor()`.
84///
85/// - is_at_end() const -> bool: Must return true if the cursor position is at
86/// one-past-the-last-element, and false otherwise.
87///
88/// - copy_field(int from_index, PSI_field *to_field) const: Must copy the value
89/// of the current row, in the column with the given index, to `to_field`.
90///
91/// - static get_approximate_row_count() -> int: This is optional. If given, it
92/// should return an approximation of the row count. The value is only used as a
93/// hint, so it may be good for performance if it is accurate, but there is no
94/// strict requirement that it is consistent with the actual table size.
95///
96/// - static get_table_name() -> const char *: Must return the table name as a
97/// string.
98///
99/// - static get_table_definition() -> const char *: Must return the table
100/// definition as a string.
101///
102/// @note Tables can also be defined using the "handler interface" directly. The
103/// handler interface is C-oriented and has several legacy limitations, whereas
104/// "Table with cursor" is intended as a more C++-friendly alternative. It
105/// overcomes the limitations that implementations of the handler interface
106/// must...
107///
108/// - Define global functions. Table with cursor just requires user to define
109/// member functions, which may be more natural as they operate on an object.
110///
111/// - Expose a non-const pointer to the cursor, which the user may alter
112/// outside the control of the implementation. This violates OOP
113/// principles. Table with cursor does not have to do that.
114///
115/// - Position cursors at one-before-the-beginning. This is different from
116/// most iterator idioms. Table with cursor only has to support cursors
117/// positioned on a row or at one-past-the-last-row.
118///
119/// - Implement unusual requirements on the cursor type. This could not be
120/// worked around in table with cursor, but the requirement is modeled using a
121/// C++20 concept, providing partial type safety.
122///
123/// - Initialize the table using lengthy boilerplate code. Table with cursor
124/// implements that once and for all in an internal function that does not have
125/// to be re-implemented per table.
126///
127/// - By convention, use slightly cryptic names for some functions. Table with
128/// cursor attempts to use more straightforward names.
129///
130/// tparam Type the class to test
131template <class Type>
133 std::is_default_constructible_v<Type> &&
134 requires(Type object, int index, PSI_field *field) {
135 { object.advance() };
136 { object.get_cursor() } -> Is_trivially_comparable;
137 { object.set_cursor(object.get_cursor()) };
138 { object.is_at_end() } -> std::convertible_to<bool>;
139 { object.copy_field(index, field) };
140 { Type::get_approximate_row_count() } -> std::convertible_to<int>;
141 { Type::get_table_name() } -> std::same_as<const char *>;
142 { Type::get_table_definition() } -> std::same_as<const char *>;
143 };
144
145namespace detail {
146
147/// Adaptor class that takes a _table with cursor_ and provides an
148/// implementation of MySQL's handler interface.
149template <Is_table_with_cursor Table_with_cursor_tp>
151 public:
152 using Table_with_cursor_t = Table_with_cursor_tp;
154 using Cursor_t = decltype(std::declval<Table_with_cursor_t>().get_cursor());
156 "The return type for Table_with_cursor_t::get_cursor() must be "
157 "*trivially comparable*, i.e., both satisfy "
158 "std::is_trivially_copyable and "
159 "be equality-comparable using memcmp (i.e., must have no "
160 "internal padding "
161 "bytes).");
162
164 // clang-tidy suggests to initialize the cursors in initializer. But
165 // we can only do it after m_table_with_cursor is initialized and this is
166 // a practical way to ensure that.
167 // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
169 }
170
172 static auto &table_share = init_table_share();
173 return table_share;
174 }
175
176 private:
177 /// @return PFS_engine_table_share_proxy initialized based on this class.
179 // Pack the static members of this class in the format expected by the
180 // framework.
181 static PFS_engine_table_share_proxy table_share;
182 table_share.m_table_name = Table_with_cursor_t::get_table_name();
183 table_share.m_table_name_length = std::strlen(table_share.m_table_name);
184 table_share.m_table_definition =
185 Table_with_cursor_t::get_table_definition();
186 table_share.m_ref_length =
187 sizeof(decltype(std::declval<Table_with_cursor_t>().get_cursor()));
188 table_share.m_acl = READONLY;
189 table_share.get_row_count = get_row_count;
190 table_share.delete_all_rows = nullptr; // Read-only table
192 // Table without index
193 nullptr, // index_init
194 nullptr, // index_read
195 nullptr, // index_next
197 // Table without index
198 nullptr, // reset_position
199 // Read-only table
200 nullptr, // write_column_value
201 nullptr, // write_row_values
202 nullptr, // update_column_value
203 nullptr, // update_row_values
204 nullptr, // delete_row_values
206 return table_share;
207 }
208
209 /// @brief Implementation of @c PFS_engine_table_proxy::rnd_init, matching
210 /// @c rnd_init_t.
211 /// @param opaque_handle Pointer to Table_handle_adaptor object, cast to
212 /// PSI_table_handle *.
213 /// @return PFS_HA_ERR_END_OF_FILE if table is empty, 0 otherwise.
214 static int rnd_init(PSI_table_handle *opaque_handle, bool /*scan*/) {
215 return get_handle_adaptor(opaque_handle).do_rnd_init();
216 }
217
218 /// @brief Implementation of @c PFS_engine_table_proxy::rnd_next, matching
219 /// @c rnd_next_t.
220 /// @param opaque_handle Pointer to Table_handle_adaptor object, cast to
221 /// PSI_table_handle *.
222 /// @return PFS_HA_ERR_END_OF_FILE if it reached end of the table, 0
223 /// otherwise.
224 static int rnd_next(PSI_table_handle *opaque_handle) {
225 return get_handle_adaptor(opaque_handle).do_rnd_next();
226 }
227
228 /// @brief Implementation of @c PFS_engine_table_proxy::rnd_pos, matching
229 /// @c rnd_pos_t.
230 /// @param opaque_handle Pointer to Table_handle_adaptor object, cast to
231 /// PSI_table_handle *.
232 /// @return PFS_HA_ERR_END_OF_FILE if the position is at the end of the table,
233 /// 0 otherwise.
234 static int rnd_pos(PSI_table_handle *opaque_handle) {
235 return get_handle_adaptor(opaque_handle).do_rnd_pos();
236 }
237
238 /// @brief Implementation of @c PFS_engine_table_proxy::read_column_value,
239 /// matching @c read_column_value_t.
240 /// @param opaque_handle Pointer to Table_handle_adaptor object, cast to
241 /// PSI_table_handle *.
242 /// @param field PSI_field to which the value should be copied.
243 /// @param index Column number in the table.
244 /// @return 0.
245 static int read_column_value(PSI_table_handle *opaque_handle,
246 PSI_field *field, unsigned int index) {
247 assert(!get_handle_adaptor(opaque_handle).m_table_with_cursor.is_at_end());
248 get_handle_adaptor(opaque_handle).do_read_column_value(field, index);
249 return 0;
250 }
251
252 /// Implementation of @c PFS_engine_table_share_proxy::get_row_count, matching
253 /// @c get_row_count_t.
254 ///
255 /// @return The row count.
256 static unsigned long long get_row_count() {
257 if constexpr (requires {
258 Table_with_cursor_t::get_approximate_row_count();
259 }) {
260 return Table_with_cursor_t::get_approximate_row_count();
261 } else {
262 return 0;
263 }
264 }
265
266 /// Implementation of @c PFS_engine_table_proxy::open_table, matching
267 /// @c open_table_t.
268 ///
269 /// @param opaque_pos Pointer to pointer to opaque "position". *opaque_pos
270 /// will be set to point to the m_position member of the returned object.
271 /// @return Pointer to a new Table_handler_adaptor object, cast to
272 /// PSI_table_handle *.
273 static PSI_table_handle *open_table(PSI_pos **opaque_pos) {
274 // Implementing a C interface, we are required to use a raw pointer.
275 // NOLINTNEXTLINE(cppcoreguidelines-owning-memory)
276 auto *handle_ptr = new Self_t;
277 *opaque_pos = reinterpret_cast<PSI_pos *>(&handle_ptr->m_current_cursor);
278 return reinterpret_cast<PSI_table_handle *>(handle_ptr);
279 }
280
281 /// Implementation of @c PFS_engine_table_proxy::close_table, matching
282 /// @c close_table_t.
283 ///
284 /// @param opaque_handle Pointer to Table_handle_adaptor object, cast to
285 /// PSI_table_handle *.
286 static void close_table(PSI_table_handle *opaque_handle) {
287 // Implementing a C interface, we are required to use raw pointers.
288 // NOLINTNEXTLINE(cppcoreguidelines-owning-memory)
289 delete &get_handle_adaptor(opaque_handle);
290 }
291
292 /// Member function implementing @c rnd_init.
293 /// @return PFS_HA_ERR_END_OF_FILE if the table is empty, 0 otherwise.
295 m_before_first_row = true;
298 return status();
299 }
300
301 /// Member function implementing @c rnd_next.
302 /// @return PFS_HA_ERR_END_OF_FILE if the new position is at the end of the
303 /// table, 0 otherwise.
305 if (m_before_first_row) {
306 m_before_first_row = false;
307 } else {
308 m_table_with_cursor.advance();
310 }
311 return status();
312 }
313
314 /// Member function implementing @c rnd_pos.
315 /// @return PFS_HA_ERR_END_OF_FILE if the new position is at the end of the
316 /// table, 0 otherwise.
319 return status();
320 }
321
322 /// Member function implementing @c read_column_value.
323 /// @param field Opaque pointer to the output field.
324 /// @param index The column index.
325 void do_read_column_value(PSI_field *field, unsigned int index) {
326 m_table_with_cursor.copy_field(index, field);
327 }
328
329 /// @return PFS_HA_ERR_END_OF_FILE if the position is at the end of the
330 /// table, 0 otherwise.
331 int status() const {
332 return m_table_with_cursor.is_at_end() ? PFS_HA_ERR_END_OF_FILE : 0;
333 }
334
335 /// Cast point to PSI_table_handle to reference to Table_handle_adaptor.
336 /// @param opaque_handle PSI_table_handle that actually is a pointer to a
337 /// Table_handle_adaptor.
338 /// @return Reference to the Table_handle_adaptor pointed to by the parameter.
340 return *(Self_t *)opaque_handle;
341 }
342
343 /// True if the current position is "one-before-the-first-row".
344 ///
345 /// The caller expects that rnd_init followed by rnd_next positions the cursor
346 /// on the first row, thus it has to be initially positioned at
347 /// "one-before-the-first-row". Since Table_and_row does not have that
348 /// concept, we identify the position using this flag.
350
351 /// Table_with_cursor object representing the table and the current cursor
352 /// position.
354
355 /// Cursor to the first row. We capture this after initializing the object,
356 /// and use it to implement rnd_init.
358
359 /// Current cursor. We set this in rnd_next and rnd_init, share the pointer to
360 /// it with the Optimizer code in the output parameter for open_table, and
361 /// allow Optimizer to alter it as it needs, as long as the alteration is
362 /// followed by a call to rnd_pos.
364};
365
366} // namespace detail
367
368/// Takes a _table with cursor_ and returns a new
369/// PFS_engine_table_share_proxy for the class.
370template <Is_table_with_cursor Table_with_cursor>
373}
374
375#endif // ifndef TABLE_WITH_CURSOR_H
Adaptor class that takes a table with cursor and provides an implementation of MySQL's handler interf...
Definition: table_with_cursor.h:150
static void close_table(PSI_table_handle *opaque_handle)
Implementation of PFS_engine_table_proxy::close_table, matching close_table_t.
Definition: table_with_cursor.h:286
int do_rnd_pos()
Member function implementing rnd_pos.
Definition: table_with_cursor.h:317
static PFS_engine_table_share_proxy & init_table_share()
Definition: table_with_cursor.h:178
int status() const
Definition: table_with_cursor.h:331
static PSI_table_handle * open_table(PSI_pos **opaque_pos)
Implementation of PFS_engine_table_proxy::open_table, matching open_table_t.
Definition: table_with_cursor.h:273
static int rnd_next(PSI_table_handle *opaque_handle)
Implementation of PFS_engine_table_proxy::rnd_next, matching rnd_next_t.
Definition: table_with_cursor.h:224
static int rnd_init(PSI_table_handle *opaque_handle, bool)
Implementation of PFS_engine_table_proxy::rnd_init, matching rnd_init_t.
Definition: table_with_cursor.h:214
static PFS_engine_table_share_proxy & get_table_share()
Definition: table_with_cursor.h:171
static Self_t & get_handle_adaptor(PSI_table_handle *opaque_handle)
Cast point to PSI_table_handle to reference to Table_handle_adaptor.
Definition: table_with_cursor.h:339
int do_rnd_next()
Member function implementing rnd_next.
Definition: table_with_cursor.h:304
static int read_column_value(PSI_table_handle *opaque_handle, PSI_field *field, unsigned int index)
Implementation of PFS_engine_table_proxy::read_column_value, matching read_column_value_t.
Definition: table_with_cursor.h:245
bool m_before_first_row
True if the current position is "one-before-the-first-row".
Definition: table_with_cursor.h:349
decltype(std::declval< Table_with_cursor_t >().get_cursor()) Cursor_t
Definition: table_with_cursor.h:154
Table_handle_adaptor()
Definition: table_with_cursor.h:163
int do_rnd_init()
Member function implementing rnd_init.
Definition: table_with_cursor.h:294
Table_with_cursor_tp Table_with_cursor_t
Definition: table_with_cursor.h:152
Cursor_t m_current_cursor
Current cursor.
Definition: table_with_cursor.h:363
static int rnd_pos(PSI_table_handle *opaque_handle)
Implementation of PFS_engine_table_proxy::rnd_pos, matching rnd_pos_t.
Definition: table_with_cursor.h:234
Table_handle_adaptor< Table_with_cursor_t > Self_t
Definition: table_with_cursor.h:153
Cursor_t m_begin_cursor
Cursor to the first row.
Definition: table_with_cursor.h:357
void do_read_column_value(PSI_field *field, unsigned int index)
Member function implementing read_column_value.
Definition: table_with_cursor.h:325
static unsigned long long get_row_count()
Implementation of PFS_engine_table_share_proxy::get_row_count, matching get_row_count_t.
Definition: table_with_cursor.h:256
Table_with_cursor_t m_table_with_cursor
Table_with_cursor object representing the table and the current cursor position.
Definition: table_with_cursor.h:353
Concept that identifies that a class is a "table with cursor".
Definition: table_with_cursor.h:132
Concept requiring that objects are copyable using memcpy, and equality-testable using memcmp.
Definition: table_with_cursor.h:59
Definition: packet_based_table_with_cursor.h:36
bool index(const std::string &value, const String &search_for, uint32_t *idx)
Definition: contains.h:76
MediaType
Definition: media_type.h:33
@ READONLY
Definition: pfs_plugin_table_service.h:437
#define PFS_HA_ERR_END_OF_FILE
Definition: pfs_plugin_table_service.h:78
struct PSI_table_handle PSI_table_handle
This is an opaque structure to denote table handle in plugin/component code.
Definition: pfs_plugin_table_service.h:97
struct PSI_pos PSI_pos
This is an opaque structure to denote cursor position in plugin/component code.
Definition: pfs_plugin_table_service.h:102
struct PSI_field PSI_field
This is an opaque structure to denote field in plugin/component code.
Definition: pfs_plugin_table_service.h:93
A share to be initialized by plugin/component code and to be provided to add_table() service method o...
Definition: pfs_plugin_table_service.h:462
delete_all_rows_t delete_all_rows
Definition: pfs_plugin_table_service.h:479
const char * m_table_name
Definition: pfs_plugin_table_service.h:468
const char * m_table_definition
Definition: pfs_plugin_table_service.h:473
PFS_engine_table_proxy m_proxy_engine_table
Definition: pfs_plugin_table_service.h:465
unsigned int m_table_name_length
Definition: pfs_plugin_table_service.h:470
enum Access_control m_acl
Definition: pfs_plugin_table_service.h:477
get_row_count_t get_row_count
Definition: pfs_plugin_table_service.h:480
unsigned int m_ref_length
Definition: pfs_plugin_table_service.h:474
PFS_engine_table_share_proxy * get_table_share_from_table_with_cursor()
Takes a table with cursor and returns a new PFS_engine_table_share_proxy for the class.
Definition: table_with_cursor.h:371