You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

224 lines
8.9 KiB

9 years ago
MDEV-29694 Remove the InnoDB change buffer The purpose of the change buffer was to reduce random disk access, which could be useful on rotational storage, but maybe less so on solid-state storage. When we wished to (1) insert a record into a non-unique secondary index, (2) delete-mark a secondary index record, (3) delete a secondary index record as part of purge (but not ROLLBACK), and the B-tree leaf page where the record belongs to is not in the buffer pool, we inserted a record into the change buffer B-tree, indexed by the page identifier. When the page was eventually read into the buffer pool, we looked up the change buffer B-tree for any modifications to the page, applied these upon the completion of the read operation. This was called the insert buffer merge. We remove the change buffer, because it has been the source of various hard-to-reproduce corruption bugs, including those fixed in commit 5b9ee8d8193a8c7a8ebdd35eedcadc3ae78e7fc1 and commit 165564d3c33ae3d677d70644a83afcb744bdbf65 but not limited to them. A downgrade will fail with a clear message starting with commit db14eb16f9977453467ec4765f481bb2f71814ba (MDEV-30106). buf_page_t::state: Merge IBUF_EXIST to UNFIXED and WRITE_FIX_IBUF to WRITE_FIX. buf_pool_t::watch[]: Remove. trx_t: Move isolation_level, check_foreigns, check_unique_secondary, bulk_insert into the same bit-field. The only purpose of trx_t::check_unique_secondary is to enable bulk insert into an empty table. It no longer enables insert buffering for UNIQUE INDEX. btr_cur_t::thr: Remove. This field was originally needed for change buffering. Later, its use was extended to cover SPATIAL INDEX. Much of the time, rtr_info::thr holds this field. When it does not, we will add parameters to SPATIAL INDEX specific functions. ibuf_upgrade_needed(): Check if the change buffer needs to be updated. ibuf_upgrade(): Merge and upgrade the change buffer after all redo log has been applied. Free any pages consumed by the change buffer, and zero out the change buffer root page to mark the upgrade completed, and to prevent a downgrade to an earlier version. dict_load_tablespaces(): Renamed from dict_check_tablespaces_and_store_max_id(). This needs to be invoked before ibuf_upgrade(). btr_cur_open_at_rnd_pos(): Specialize for use in persistent statistics. The change buffer merge does not need this function anymore. btr_page_alloc(): Renamed from btr_page_alloc_low(). We no longer allocate any change buffer pages. btr_cur_open_at_rnd_pos(): Specialize for use in persistent statistics. The change buffer merge does not need this function anymore. row_search_index_entry(), btr_lift_page_up(): Add a parameter thr for the SPATIAL INDEX case. rtr_page_split_and_insert(): Specialized from btr_page_split_and_insert(). rtr_root_raise_and_insert(): Specialized from btr_root_raise_and_insert(). Note: The support for upgrading from the MySQL 3.23 or MySQL 4.0 change buffer format that predates the MySQL 4.1 introduction of the option innodb_file_per_table was removed in MySQL 5.6.5 as part of mysql/mysql-server@69b6241a79876ae98bb0c9dce7c8d8799d6ad273 and MariaDB 10.0.11 as part of 1d0f70c2f894b27e98773a282871d32802f67964. In the tests innodb.log_upgrade and innodb.log_corruption, we create valid (upgraded) change buffer pages. Tested by: Matthias Leich
3 years ago
MDEV-32027 Opening all .ibd files on InnoDB startup can be slow dict_find_max_space_id(): Return SELECT MAX(SPACE) FROM SYS_TABLES. dict_check_tablespaces_and_store_max_id(): In the normal case (no encryption plugin has been loaded and the change buffer is empty), invoke dict_find_max_space_id() and do not open any .ibd files. If a std::set<uint32_t> has been specified, open the files whose tablespace ID is mentioned. Else, open all data files that are identified by SYS_TABLES records. fil_ibd_open(): Remove a call to os_file_get_last_error() that can report a misleading error, such as EINVAL inside my_realpath() that is not an actual error. This could be invoked when a data file is found but the FSP_SPACE_FLAGS are incorrect, such as is the case for table test.td in ./mtr --mysqld=--innodb-buffer-pool-dump-at-shutdown=0 innodb.table_flags buf_load(): If any tablespaces could not be found, invoke dict_check_tablespaces_and_store_max_id() on the missing tablespaces. dict_load_tablespace(): Try to load the tablespace unless it was found to be futile. This fixes failures related to FTS_*.ibd files for FULLTEXT INDEX. btr_cur_t::search_leaf(): Prevent a crash when the tablespace does not exist. This was caught by the test innodb_fts.fts_concurrent_insert when the change to dict_load_tablespaces() was not present. We modify a few tests to ensure that tables will not be loaded at startup. For some fault injection tests this means that the corrupted tables will not be loaded, because dict_load_tablespace() would perform stricter checks than dict_check_tablespaces_and_store_max_id(). Tested by: Matthias Leich Reviewed by: Thirunarayanan Balathandayuthapani
2 years ago
MDEV-32027 Opening all .ibd files on InnoDB startup can be slow dict_find_max_space_id(): Return SELECT MAX(SPACE) FROM SYS_TABLES. dict_check_tablespaces_and_store_max_id(): In the normal case (no encryption plugin has been loaded and the change buffer is empty), invoke dict_find_max_space_id() and do not open any .ibd files. If a std::set<uint32_t> has been specified, open the files whose tablespace ID is mentioned. Else, open all data files that are identified by SYS_TABLES records. fil_ibd_open(): Remove a call to os_file_get_last_error() that can report a misleading error, such as EINVAL inside my_realpath() that is not an actual error. This could be invoked when a data file is found but the FSP_SPACE_FLAGS are incorrect, such as is the case for table test.td in ./mtr --mysqld=--innodb-buffer-pool-dump-at-shutdown=0 innodb.table_flags buf_load(): If any tablespaces could not be found, invoke dict_check_tablespaces_and_store_max_id() on the missing tablespaces. dict_load_tablespace(): Try to load the tablespace unless it was found to be futile. This fixes failures related to FTS_*.ibd files for FULLTEXT INDEX. btr_cur_t::search_leaf(): Prevent a crash when the tablespace does not exist. This was caught by the test innodb_fts.fts_concurrent_insert when the change to dict_load_tablespaces() was not present. We modify a few tests to ensure that tables will not be loaded at startup. For some fault injection tests this means that the corrupted tables will not be loaded, because dict_load_tablespace() would perform stricter checks than dict_check_tablespaces_and_store_max_id(). Tested by: Matthias Leich Reviewed by: Thirunarayanan Balathandayuthapani
2 years ago
MDEV-27234: Data dictionary recovery was not READ COMMITTED This also fixes MDEV-20198: Instant ALTER TABLE is not crash safe InnoDB dictionary recovery wrongly used the READ UNCOMMITTED isolation level, causing some mismatch. For example, if a table was renamed or replaced in a transaction, according to READ UNCOMMITTED the table might not exist at all. We implement READ COMMITTED isolation level for accessing the dictionary tables SYS_TABLES, SYS_COLUMNS, SYS_INDEXES, SYS_FIELDS, SYS_VIRTUAL, SYS_FOREIGN, SYS_FOREIGN_COLS. For most of these tables, no secondary index exists. For the secondary indexes (on SYS_TABLES.ID, SYS_FOREIGN.FOR_NAME, SYS_FOREIGN.REF_NAME), we will always look up the primary key in the clustered index and check if the record actually is a committed version. dict_check_sys_tables(): Recover tablespaces also from delete-marked committed records, so that if a matching .ibd file exists, it will be removed by fil_delete_tablespace() when the committed delete-marked SYS_INDEXES record of the clustered index is purged in row_purge_remove_clust_if_poss_low(). fil_ibd_open(): Change the Boolean parameter "validate" to a ternary one, to suppress error messages when the file might not exist. It is possible that a .ibd file was deleted and the server shut down before the SYS_INDEXES and SYS_TABLES records were purged. Hence, if dict_check_sys_tables() finds a committed delete-marked record, we must not complain if the tablespace file is not found. On Windows, we msut treat ERROR_PATH_NOT_FOUND (directory not found) in the same way as ERROR_FILE_NOT_FOUND. This fixes a few failures where a previous test successfully executed DROP DATABASE (and deleted all files and the directory), but a committed delete-marked SYS_TABLES record had not been purged before server restart. dict_getnext_system_low(): Do not filter out delete-marked records. dict_startscan_system(), dict_getnext_system(): Do filter out delete-marked records, for accessing the INFORMATION_SCHEMA tables. dict_sys_tables_rec_read(): Return the DB_TRX_ID of the committed version of the record. This is needed in dict_load_table_low(). dict_load_foreign_cols(), dict_load_foreign(): Add a parameter for the current transaction identifier. In some DDL operations, the FOREIGN KEY constraints are being loaded from the data dictionary before the DDL transaction has been committed. For SYS_FOREIGN and SYS_FOREIGN_COLS, we must implement the special case of READ COMMITTED that the changes of the uncommitted current transaction are visible. dict_load_foreign(): Validate the table name. We could find a SYS_FOREIGN.ID via a committed delete-marked secondary index record that does not match the REF_NAME or FOR_NAME of the secondary index record. dict_load_index_low(): Optionally take the table as a parameter, so that table->def_trx_id can be updated in case of a committed delete-marked SYS_INDEXES record corresponding to DROP INDEX, but not corresponding to an index stub of ADD INDEX. dict_load_indexes(): Do not update table->def_trx_id in case of delete-marked records. rec_is_metadata(), rec_offs_make_valid(), rec_get_offsets_func(), row_build_low(): Relax some assertions. We may now have !index->is_instant() even if a metadata record is present in the index. Previously, the recovery of instant ADD/DROP COLUMN assumed that READ UNCOMMITTED of the data dictionary will be performed. Now, we will have a READ COMMITTED copy of the data dictionary cache, and a READ UNCOMMITTED copy of the metadata record. btr_page_reorganize_low(): Correctly update the FIL_PAGE_TYPE when rolling back an instant ADD/DROP COLUMN operation. row_rec_to_index_entry_impl(): Relax some assertions, and disallow accessing "extra" fields. This fixes the recovery of a crash during an instant ADD COLUMN after a successful instant DROP COLUMN, in the test innodb.instant_alter_crash. Tested by: Matthias Leich
4 years ago
MDEV-29504/MDEV-29849 TRUNCATE breaks FOREIGN KEY locking ha_innobase::referenced_by_foreign_key(): Protect the check with dict_sys.freeze(), to prevent races with TRUNCATE TABLE. The test innodb.instant_alter_crash has been adjusted for this additional locking. dict_table_is_referenced_by_foreign_key(): Removed (merged to the only caller). create_table_info_t::create_table(): Ignore missing indexes for FOREIGN KEY constraints if foreign_key_checks=0. create_table_info_t::create_table_update_dict(): Rewritten as a static function. Do not return any error. ha_innobase::create(): When trx!=nullptr and we are operating on a persistent table, do not rollback, commit, or release the data dictionary latch. ha_innobase::truncate(): Protect the entire critical section with an exclusive dict_sys.latch, so that ha_innobase::referenced_by_foreign_key() on referenced tables will return a consistent result. In case of a failure, invoke dict_load_foreigns() to restore also any FOREIGN KEY constraints. ha_innobase::free_foreign_key_create_info(): Define inline. lock_release(): Disregard innodb_evict_tables_on_commit_debug=ON when dict_sys.locked() holds. It would hold when fts_load_stopword() is invoked by create_table_info_t::create_table_update_dict(). dict_sys_t::locked(): Return whether the current thread is holding the exclusive dict_sys.latch. dict_sys_t::frozen_not_locked(): Return whether any thread is holding a shared dict_sys.latch. In the test main.mysql_upgrade, the InnoDB persistent statistics will no longer be recalculated in ha_innobase::open() as part of CHECK TABLE ... FOR UPGRADE. They were deleted earlier in the test. Tested by: Matthias Leich
3 years ago
MDEV-27234: Data dictionary recovery was not READ COMMITTED This also fixes MDEV-20198: Instant ALTER TABLE is not crash safe InnoDB dictionary recovery wrongly used the READ UNCOMMITTED isolation level, causing some mismatch. For example, if a table was renamed or replaced in a transaction, according to READ UNCOMMITTED the table might not exist at all. We implement READ COMMITTED isolation level for accessing the dictionary tables SYS_TABLES, SYS_COLUMNS, SYS_INDEXES, SYS_FIELDS, SYS_VIRTUAL, SYS_FOREIGN, SYS_FOREIGN_COLS. For most of these tables, no secondary index exists. For the secondary indexes (on SYS_TABLES.ID, SYS_FOREIGN.FOR_NAME, SYS_FOREIGN.REF_NAME), we will always look up the primary key in the clustered index and check if the record actually is a committed version. dict_check_sys_tables(): Recover tablespaces also from delete-marked committed records, so that if a matching .ibd file exists, it will be removed by fil_delete_tablespace() when the committed delete-marked SYS_INDEXES record of the clustered index is purged in row_purge_remove_clust_if_poss_low(). fil_ibd_open(): Change the Boolean parameter "validate" to a ternary one, to suppress error messages when the file might not exist. It is possible that a .ibd file was deleted and the server shut down before the SYS_INDEXES and SYS_TABLES records were purged. Hence, if dict_check_sys_tables() finds a committed delete-marked record, we must not complain if the tablespace file is not found. On Windows, we msut treat ERROR_PATH_NOT_FOUND (directory not found) in the same way as ERROR_FILE_NOT_FOUND. This fixes a few failures where a previous test successfully executed DROP DATABASE (and deleted all files and the directory), but a committed delete-marked SYS_TABLES record had not been purged before server restart. dict_getnext_system_low(): Do not filter out delete-marked records. dict_startscan_system(), dict_getnext_system(): Do filter out delete-marked records, for accessing the INFORMATION_SCHEMA tables. dict_sys_tables_rec_read(): Return the DB_TRX_ID of the committed version of the record. This is needed in dict_load_table_low(). dict_load_foreign_cols(), dict_load_foreign(): Add a parameter for the current transaction identifier. In some DDL operations, the FOREIGN KEY constraints are being loaded from the data dictionary before the DDL transaction has been committed. For SYS_FOREIGN and SYS_FOREIGN_COLS, we must implement the special case of READ COMMITTED that the changes of the uncommitted current transaction are visible. dict_load_foreign(): Validate the table name. We could find a SYS_FOREIGN.ID via a committed delete-marked secondary index record that does not match the REF_NAME or FOR_NAME of the secondary index record. dict_load_index_low(): Optionally take the table as a parameter, so that table->def_trx_id can be updated in case of a committed delete-marked SYS_INDEXES record corresponding to DROP INDEX, but not corresponding to an index stub of ADD INDEX. dict_load_indexes(): Do not update table->def_trx_id in case of delete-marked records. rec_is_metadata(), rec_offs_make_valid(), rec_get_offsets_func(), row_build_low(): Relax some assertions. We may now have !index->is_instant() even if a metadata record is present in the index. Previously, the recovery of instant ADD/DROP COLUMN assumed that READ UNCOMMITTED of the data dictionary will be performed. Now, we will have a READ COMMITTED copy of the data dictionary cache, and a READ UNCOMMITTED copy of the metadata record. btr_page_reorganize_low(): Correctly update the FIL_PAGE_TYPE when rolling back an instant ADD/DROP COLUMN operation. row_rec_to_index_entry_impl(): Relax some assertions, and disallow accessing "extra" fields. This fixes the recovery of a crash during an instant ADD COLUMN after a successful instant DROP COLUMN, in the test innodb.instant_alter_crash. Tested by: Matthias Leich
4 years ago
  1. /*****************************************************************************
  2. Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved.
  3. Copyright (c) 2017, 2023, MariaDB Corporation.
  4. This program is free software; you can redistribute it and/or modify it under
  5. the terms of the GNU General Public License as published by the Free Software
  6. Foundation; version 2 of the License.
  7. This program is distributed in the hope that it will be useful, but WITHOUT
  8. ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  9. FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  10. You should have received a copy of the GNU General Public License along with
  11. this program; if not, write to the Free Software Foundation, Inc.,
  12. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
  13. *****************************************************************************/
  14. /**************************************************//**
  15. @file include/dict0load.h
  16. Loads to the memory cache database object definitions
  17. from dictionary tables
  18. Created 4/24/1996 Heikki Tuuri
  19. *******************************************************/
  20. #ifndef dict0load_h
  21. #define dict0load_h
  22. #include "dict0types.h"
  23. #include "trx0types.h"
  24. #include "ut0byte.h"
  25. #include "mem0mem.h"
  26. #include "btr0types.h"
  27. #include <deque>
  28. #include <set>
  29. /** A stack of table names related through foreign key constraints */
  30. typedef std::deque<const char*, ut_allocator<const char*> > dict_names_t;
  31. /** Check MAX(SPACE) FROM SYS_TABLES and store it in fil_system.
  32. Open each data file if an encryption plugin has been loaded.
  33. @param spaces set of tablespace files to open
  34. @param upgrade whether we need to invoke ibuf_upgrade() */
  35. void dict_load_tablespaces(const std::set<uint32_t> *spaces= nullptr,
  36. bool upgrade= false);
  37. /** Make sure the data_file_name is saved in dict_table_t if needed.
  38. @param[in,out] table Table object */
  39. void dict_get_and_save_data_dir_path(dict_table_t* table);
  40. /***********************************************************************//**
  41. Loads a table object based on the table id.
  42. @return table; NULL if table does not exist */
  43. dict_table_t*
  44. dict_load_table_on_id(
  45. /*==================*/
  46. table_id_t table_id, /*!< in: table id */
  47. dict_err_ignore_t ignore_err); /*!< in: errors to ignore
  48. when loading the table */
  49. /********************************************************************//**
  50. This function is called when the database is booted.
  51. Loads system table index definitions except for the clustered index which
  52. is added to the dictionary cache at booting before calling this function. */
  53. void
  54. dict_load_sys_table(
  55. /*================*/
  56. dict_table_t* table); /*!< in: system table */
  57. /***********************************************************************//**
  58. Loads foreign key constraints where the table is either the foreign key
  59. holder or where the table is referenced by a foreign key. Adds these
  60. constraints to the data dictionary.
  61. The foreign key constraint is loaded only if the referenced table is also
  62. in the dictionary cache. If the referenced table is not in dictionary
  63. cache, then it is added to the output parameter (fk_tables).
  64. @return DB_SUCCESS or error code */
  65. dberr_t
  66. dict_load_foreigns(
  67. /*===============*/
  68. const char* table_name, /*!< in: table name */
  69. const char** col_names, /*!< in: column names, or NULL
  70. to use table->col_names */
  71. trx_id_t trx_id, /*!< in: DDL transaction id,
  72. or 0 to check
  73. recursive load of tables
  74. chained by FK */
  75. bool check_charsets, /*!< in: whether to check
  76. charset compatibility */
  77. dict_err_ignore_t ignore_err, /*!< in: error to be ignored */
  78. dict_names_t& fk_tables) /*!< out: stack of table names
  79. which must be loaded
  80. subsequently to load all the
  81. foreign key constraints. */
  82. MY_ATTRIBUTE((nonnull(1)));
  83. /********************************************************************//**
  84. This function opens a system table, and return the first record.
  85. @return first record of the system table */
  86. const rec_t*
  87. dict_startscan_system(
  88. /*==================*/
  89. btr_pcur_t* pcur, /*!< out: persistent cursor to
  90. the record */
  91. mtr_t* mtr, /*!< in: the mini-transaction */
  92. dict_table_t* table); /*!< in: system table */
  93. /********************************************************************//**
  94. This function get the next system table record as we scan the table.
  95. @return the record if found, NULL if end of scan. */
  96. const rec_t*
  97. dict_getnext_system(
  98. /*================*/
  99. btr_pcur_t* pcur, /*!< in/out: persistent cursor
  100. to the record */
  101. mtr_t* mtr); /*!< in: the mini-transaction */
  102. /** Load a table definition from a SYS_TABLES record to dict_table_t.
  103. Do not load any columns or indexes.
  104. @param[in,out] mtr mini-transaction
  105. @param[in] uncommitted whether to use READ UNCOMMITTED isolation level
  106. @param[in] rec SYS_TABLES record
  107. @param[out,own] table table, or nullptr
  108. @return error message
  109. @retval nullptr on success */
  110. const char *dict_load_table_low(mtr_t *mtr, bool uncommitted,
  111. const rec_t *rec, dict_table_t **table)
  112. MY_ATTRIBUTE((nonnull, warn_unused_result));
  113. /********************************************************************//**
  114. This function parses a SYS_INDEXES record and populate a dict_index_t
  115. structure with the information from the record. For detail information
  116. about SYS_INDEXES fields, please refer to dict_boot() function.
  117. @return error message, or NULL on success */
  118. const char*
  119. dict_process_sys_indexes_rec(
  120. /*=========================*/
  121. mem_heap_t* heap, /*!< in/out: heap memory */
  122. const rec_t* rec, /*!< in: current SYS_INDEXES rec */
  123. dict_index_t* index, /*!< out: dict_index_t to be
  124. filled */
  125. table_id_t* table_id); /*!< out: table id */
  126. /********************************************************************//**
  127. This function parses a SYS_COLUMNS record and populate a dict_column_t
  128. structure with the information from the record.
  129. @return error message, or NULL on success */
  130. const char*
  131. dict_process_sys_columns_rec(
  132. /*=========================*/
  133. mem_heap_t* heap, /*!< in/out: heap memory */
  134. const rec_t* rec, /*!< in: current SYS_COLUMNS rec */
  135. dict_col_t* column, /*!< out: dict_col_t to be filled */
  136. table_id_t* table_id, /*!< out: table id */
  137. const char** col_name, /*!< out: column name */
  138. ulint* nth_v_col); /*!< out: if virtual col, this is
  139. records its sequence number */
  140. /** This function parses a SYS_VIRTUAL record and extract virtual column
  141. information
  142. @param[in,out] heap heap memory
  143. @param[in] rec current SYS_COLUMNS rec
  144. @param[in,out] table_id table id
  145. @param[in,out] pos virtual column position
  146. @param[in,out] base_pos base column position
  147. @return error message, or NULL on success */
  148. const char*
  149. dict_process_sys_virtual_rec(
  150. const rec_t* rec,
  151. table_id_t* table_id,
  152. ulint* pos,
  153. ulint* base_pos);
  154. /********************************************************************//**
  155. This function parses a SYS_FIELDS record and populate a dict_field_t
  156. structure with the information from the record.
  157. @return error message, or NULL on success */
  158. const char*
  159. dict_process_sys_fields_rec(
  160. /*========================*/
  161. mem_heap_t* heap, /*!< in/out: heap memory */
  162. const rec_t* rec, /*!< in: current SYS_FIELDS rec */
  163. dict_field_t* sys_field, /*!< out: dict_field_t to be
  164. filled */
  165. ulint* pos, /*!< out: Field position */
  166. index_id_t* index_id, /*!< out: current index id */
  167. index_id_t last_id); /*!< in: previous index id */
  168. /********************************************************************//**
  169. This function parses a SYS_FOREIGN record and populate a dict_foreign_t
  170. structure with the information from the record. For detail information
  171. about SYS_FOREIGN fields, please refer to dict_load_foreign() function
  172. @return error message, or NULL on success */
  173. const char*
  174. dict_process_sys_foreign_rec(
  175. /*=========================*/
  176. mem_heap_t* heap, /*!< in/out: heap memory */
  177. const rec_t* rec, /*!< in: current SYS_FOREIGN rec */
  178. dict_foreign_t* foreign); /*!< out: dict_foreign_t to be
  179. filled */
  180. /********************************************************************//**
  181. This function parses a SYS_FOREIGN_COLS record and extract necessary
  182. information from the record and return to caller.
  183. @return error message, or NULL on success */
  184. const char*
  185. dict_process_sys_foreign_col_rec(
  186. /*=============================*/
  187. mem_heap_t* heap, /*!< in/out: heap memory */
  188. const rec_t* rec, /*!< in: current SYS_FOREIGN_COLS rec */
  189. const char** name, /*!< out: foreign key constraint name */
  190. const char** for_col_name, /*!< out: referencing column name */
  191. const char** ref_col_name, /*!< out: referenced column name
  192. in referenced table */
  193. ulint* pos); /*!< out: column position */
  194. /** This function gets the next system table record as it scans
  195. the table.
  196. @param pcur persistent cursor
  197. @param mtr mini-transaction
  198. @return the next record if found
  199. @retval nullptr at the end of the table */
  200. const rec_t*
  201. dict_getnext_system_low(btr_pcur_t *pcur, mtr_t *mtr);
  202. #endif