/* Copyright (c) 2018, 2021, Alibaba and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, as published by the Free Software Foundation. This program is also distributed with certain software (including but not limited to OpenSSL) that is licensed under separate terms, as designated in a particular file or component or in included license documentation. The authors of MySQL/PolarDB-X Engine hereby grant you an additional permission to link the program and your derivative works with the separately licensed software that they have included with MySQL/PolarDB-X Engine. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0, for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /** @file include/lizard0dict.h Special Lizard tablespace definition. Created 2020-03-19 by Jianwei.zhao *******************************************************/ #include "lizard0dict.h" #include "dict0dd.h" #include "dict0mem.h" #include "row0mysql.h" #include "lizard0data0types.h" #include "lizard0undo.h" namespace lizard { /** The space name of lizard */ const char *dict_lizard::s_lizard_space_name = "innodb_lizard"; /** The file name of lizard space */ const char *dict_lizard::s_lizard_space_file_name = "lizard.ibd"; static_assert(DATA_SCN_ID == 3, "DATA_SCN_ID != 3"); static_assert(DATA_SCN_ID_LEN == 8, "DATA_SCN_ID_LEN != 8"); static_assert(DATA_UNDO_PTR == 4, "DATA_UNDO_PTR != 4"); static_assert(DATA_UNDO_PTR_LEN == 8, "DATA_UNDO_PTR_LEN != 8"); static_assert(DATA_GCN_ID == 5, "DATA_GCN_ID != 5"); static_assert(DATA_GCN_ID_LEN == 8, "DATA_GCN_ID_LEN != 8"); /** Lizard: First several undo tablespaces will be treated as txn tablespace */ const char *dict_lizard::s_default_txn_space_names[] = { "innodb_undo_001", "innodb_undo_002", "innodb_undo_003", "innodb_undo_004"}; /** Judge the undo tablespace is txn tablespace by name */ bool is_txn_space_by_name(const char *name) { if (name && (strcmp(name, dict_lizard::s_default_txn_space_names[0]) == 0 || strcmp(name, dict_lizard::s_default_txn_space_names[1]) == 0 || strcmp(name, dict_lizard::s_default_txn_space_names[2]) == 0 || strcmp(name, dict_lizard::s_default_txn_space_names[3]) == 0)) return true; return false; } /** Add the SCN and UBA column into dict_table_t, for example: dict_table_t::col_names "...DB_SCN_ID\0DATA_UNDO_PTR\0..." @param[in] table dict_table_t @param[in] heap memory slice */ void dict_table_add_lizard_columns(dict_table_t *table, mem_heap_t *heap) { ut_ad(table && heap); if (table->is_intrinsic()) return; dict_mem_table_add_col(table, heap, "DB_SCN_ID", DATA_SYS, DATA_SCN_ID | DATA_NOT_NULL, DATA_SCN_ID_LEN); dict_mem_table_add_col(table, heap, "DB_UNDO_PTR", DATA_SYS, DATA_UNDO_PTR | DATA_NOT_NULL, DATA_UNDO_PTR_LEN); dict_mem_table_add_col(table, heap, "DB_GCN_ID", DATA_SYS, DATA_GCN_ID | DATA_NOT_NULL, DATA_GCN_ID_LEN); } /** Init txn_desc with the creator trx when created. @param[in] index the index being created @param[in] trx creator transaction @return DB_SUCCESS Success */ dberr_t dd_index_init_txn_desc(dict_index_t *index, trx_t *trx) { dberr_t err = DB_SUCCESS; ut_ad(index->table); ut_ad(!mutex_own(&trx->undo_mutex)); /** If a table is temporary, or even intrinsic, its dict info will not be written to dict tables. */ if (!index->table->is_temporary()) { mutex_enter(&trx->undo_mutex); err = trx_always_assign_txn_undo(trx); mutex_exit(&trx->undo_mutex); if (err == DB_SUCCESS) { assert_trx_scn_initial(trx); assert_undo_ptr_allocated(trx->txn_desc.undo_ptr); ut_ad(lizard_undo_ptr_is_active(trx->txn_desc.undo_ptr)); index->txn.uba = trx->txn_desc.undo_ptr; index->txn.scn = trx->txn_desc.cmmt.scn; index->txn.gcn = trx->txn_desc.cmmt.gcn; } } return err; } /** Check if a index should be seen by a transaction. @param[in] index the index being opened. @param[in] trx transaction. @return true if visible */ bool dd_index_modificatsion_visible(dict_index_t *index, const trx_t *trx, bool is_as_of, scn_t as_of_scn, gcn_t as_of_gcn) { txn_rec_t rec_txn; scn_t scn = index->txn.scn.load(); gcn_t gcn = index->txn.gcn.load(); ut_ad(trx); rec_txn = { index->trx_id, scn, index->txn.uba, gcn, }; if (scn == SCN_NULL || gcn == GCN_NULL) { lizard::txn_rec_real_state_by_misc(&rec_txn); /** It might be stored many times but they should be the same value */ index->txn.scn.store(rec_txn.scn); index->txn.gcn.store(rec_txn.gcn); } if (is_as_of) { ut_ad(as_of_scn != SCN_NULL || as_of_gcn != GCN_NULL); if (as_of_scn != SCN_NULL) { ut_ad(as_of_gcn == GCN_NULL); return (rec_txn.scn != SCN_NULL && as_of_scn != SCN_NULL && rec_txn.scn <= as_of_scn); } else { ut_ad(as_of_scn == SCN_NULL); if (srv_equal_gcn_visible) return (rec_txn.gcn != GCN_NULL && as_of_gcn != GCN_NULL && rec_txn.gcn <= as_of_gcn); else return (rec_txn.gcn != GCN_NULL && as_of_gcn != GCN_NULL && rec_txn.gcn < as_of_gcn); } } else { ut_ad(trx->vision.is_active()); /** When is_usable() is executed concurrently, SCN and UBA will be not consistent, the vision judgement only depend on real SCN, UBA state will be used to code defense, so here omit the check. */ return trx->vision.modifications_visible(&rec_txn, index->table->name, false); } } /** Fill index txn information from se_private_data. @param[in,out] index the index being opened. @param[in] p se_private_data from the mysql.indexes record. @return true if failed */ bool dd_index_fill_txn_desc(dict_index_t *index, const dd::Properties &p) { undo_ptr_t uba = 0; scn_t scn = SCN_NULL; gcn_t gcn = GCN_NULL; if (p.get(dd_index_key_strings[DD_INDEX_UBA], &uba) || p.get(dd_index_key_strings[DD_INDEX_SCN], &scn)) return true; /** GCN didn't add into properties first time, so judge it firstly. */ if (p.exists(dd_index_key_strings[DD_INDEX_GCN])) { p.get(dd_index_key_strings[DD_INDEX_GCN], &gcn); } index->txn.uba = uba; index->txn.scn = scn; index->txn.gcn = gcn; return false; } /** Add the lizard columns into data dictionary in server @param[in,out] dd_table data dictionary cache object @param[in,out] primary data dictionary primary key */ void dd_add_lizard_columns(dd::Table *dd_table, dd::Index *primary) { ut_ad(dd_table && primary); dd::Column *db_scn_id = dd_add_hidden_column( dd_table, "DB_SCN_ID", DATA_SCN_ID_LEN, dd::enum_column_types::LONGLONG); dd_add_hidden_element(primary, db_scn_id); dd::Column *db_undo_ptr = dd_add_hidden_column(dd_table, "DB_UNDO_PTR", DATA_UNDO_PTR_LEN, dd::enum_column_types::LONGLONG); dd_add_hidden_element(primary, db_undo_ptr); dd::Column *db_gcn_id = dd_add_hidden_column( dd_table, "DB_GCN_ID", DATA_GCN_ID_LEN, dd::enum_column_types::LONGLONG); dd_add_hidden_element(primary, db_gcn_id); } #if defined UNIV_DEBUG || defined LIZARD_DEBUG /** Check the dict_table_t object @param[in] table dict_table_t @return true Success */ bool lizard_dict_table_check(const dict_table_t *table) { size_t n_cols; size_t n_sys_cols; dict_col_t *col; const char *s; bool is_intrinsic; ulint i; ut_a(table); is_intrinsic = table->is_intrinsic(); s = table->col_names; /** Check columns */ n_cols = table->n_cols; if (is_intrinsic) { n_sys_cols = DATA_ITT_N_SYS_COLS + DATA_ITT_N_LIZARD_COLS; ut_a(n_cols > n_sys_cols); for (i = 0; i < n_cols - n_sys_cols; i++) { s += strlen(s) + 1; } /* Row id */ col = table->get_col(n_cols - n_sys_cols + DATA_ROW_ID); ut_a(col->mtype == DATA_SYS); ut_a(col->prtype == (DATA_ROW_ID | DATA_NOT_NULL)); ut_a(col->len == DATA_ROW_ID_LEN); ut_a(strcmp(s, "DB_ROW_ID") == 0); s += strlen(s) + 1; /* trx id */ col = table->get_col(n_cols - n_sys_cols + DATA_TRX_ID); ut_a(col->mtype == DATA_SYS); ut_a(col->prtype == (DATA_TRX_ID | DATA_NOT_NULL)); ut_a(col->len == DATA_TRX_ID_LEN); ut_a(strcmp(s, "DB_TRX_ID") == 0); } else { n_sys_cols = DATA_N_SYS_COLS + DATA_N_LIZARD_COLS; /** If there are only virtual columns in the table, the following equality is reached */ ut_a(n_cols >= n_sys_cols); for (i = 0; i < n_cols - n_sys_cols; i++) { s += strlen(s) + 1; } /* Row id */ col = table->get_col(n_cols - n_sys_cols + DATA_ROW_ID); ut_a(col->mtype == DATA_SYS); ut_a(col->prtype == (DATA_ROW_ID | DATA_NOT_NULL)); ut_a(col->len == DATA_ROW_ID_LEN); ut_a(strcmp(s, "DB_ROW_ID") == 0); s += strlen(s) + 1; /* trx id */ col = table->get_col(n_cols - n_sys_cols + DATA_TRX_ID); ut_a(col->mtype == DATA_SYS); ut_a(col->prtype == (DATA_TRX_ID | DATA_NOT_NULL)); ut_a(col->len == DATA_TRX_ID_LEN); ut_a(strcmp(s, "DB_TRX_ID") == 0); s += strlen(s) + 1; /* roll ptr */ col = table->get_col(n_cols - n_sys_cols + DATA_ROLL_PTR); ut_a(col->mtype == DATA_SYS); ut_a(col->prtype == (DATA_ROLL_PTR | DATA_NOT_NULL)); ut_a(col->len == DATA_ROLL_PTR_LEN); ut_a(strcmp(s, "DB_ROLL_PTR") == 0); s += strlen(s) + 1; /* scn id */ col = table->get_col(n_cols - n_sys_cols + DATA_SCN_ID); ut_a(col->mtype == DATA_SYS); ut_a(col->prtype == (DATA_SCN_ID | DATA_NOT_NULL)); ut_a(col->len == DATA_SCN_ID_LEN); ut_a(strcmp(s, "DB_SCN_ID") == 0); s += strlen(s) + 1; /* undo ptr */ col = table->get_col(n_cols - n_sys_cols + DATA_UNDO_PTR); ut_a(col->mtype == DATA_SYS); ut_a(col->prtype == (DATA_UNDO_PTR | DATA_NOT_NULL)); ut_a(col->len == DATA_UNDO_PTR_LEN); ut_a(strcmp(s, "DB_UNDO_PTR") == 0); s += strlen(s) + 1; /* gcn id */ col = table->get_col(n_cols - n_sys_cols + DATA_GCN_ID); ut_a(col->mtype == DATA_SYS); ut_a(col->prtype == (DATA_GCN_ID | DATA_NOT_NULL)); ut_a(col->len == DATA_GCN_ID_LEN); ut_a(strcmp(s, "DB_GCN_ID") == 0); } return true; } /** Check the dict_incex object @param[in] index dict_index_t @return true Success */ bool lizard_dict_index_check(const dict_index_t *index) { bool is_clust; size_t n_uniq; dict_field_t *field; dict_col_t *col; const char *col_name; ut_a(index); assert_lizard_dict_table_check(index->table); is_clust = index->is_clustered(); if (is_clust) { if (!index->table->is_intrinsic()) { n_uniq = index->n_uniq; /* trx_id */ field = index->get_field(n_uniq); col = field->col; col_name = index->table->get_col_name(col->ind); ut_a(strcmp(col_name, "DB_TRX_ID") == 0); /* roll ptr */ field = index->get_field(n_uniq + 1); col = field->col; col_name = index->table->get_col_name(col->ind); ut_a(strcmp(col_name, "DB_ROLL_PTR") == 0); /* scn id */ field = index->get_field(n_uniq + 2); col = field->col; col_name = index->table->get_col_name(col->ind); ut_a(strcmp(col_name, "DB_SCN_ID") == 0); /* undo ptr */ field = index->get_field(n_uniq + 3); col = field->col; col_name = index->table->get_col_name(col->ind); ut_a(strcmp(col_name, "DB_UNDO_PTR") == 0); /* gcn id */ field = index->get_field(n_uniq + 4); col = field->col; col_name = index->table->get_col_name(col->ind); ut_a(strcmp(col_name, "DB_GCN_ID") == 0); } else { n_uniq = index->n_uniq; /* trx_id */ field = index->get_field(n_uniq); col = field->col; col_name = index->table->get_col_name(col->ind); ut_a(strcmp(col_name, "DB_TRX_ID") == 0); } } else { for (size_t i = 0; i < index->n_def; i++) { field = index->get_field(i); col = field->col; if (col->is_virtual()) { col_name = dict_table_get_v_col_name( index->table, ((dict_v_col_t*)(col))->v_pos); } else { col_name = index->table->get_col_name(col->ind); } ut_a(strcmp(col_name, "DB_TRX_ID") != 0 && strcmp(col_name, "DB_ROLL_PTR") != 0 && strcmp(col_name, "DB_SCN_ID") != 0 && strcmp(col_name, "DB_UNDO_PTR") != 0 && strcmp(col_name, "DB_GCN_ID") != 0); } } return true; } #endif /* UNIV_DEBUG || LIZARD_DEBUG define */ } // namespace lizard