diff options
author | Michael Widenius <monty@askmonty.org> | 2010-09-28 16:05:45 +0300 |
---|---|---|
committer | Michael Widenius <monty@askmonty.org> | 2010-09-28 16:05:45 +0300 |
commit | 8b833274cad540f756039c8b730a5e692219d11c (patch) | |
tree | 01f11d0c3df0d30663d56314be7e715a4f900cf4 /storage | |
parent | 6b1c78e7f9945413d4482b9836cecb36ab7b18ac (diff) | |
download | mariadb-git-8b833274cad540f756039c8b730a5e692219d11c.tar.gz |
Merge with 1.0.11-7 Pre-GA - 2010-09-09
Updated results for failing test cases (In all cases the estimated number of rows was different)
Diffstat (limited to 'storage')
-rw-r--r-- | storage/pbxt/ChangeLog | 44 | ||||
-rw-r--r-- | storage/pbxt/src/Makefile.am | 4 | ||||
-rw-r--r-- | storage/pbxt/src/cache_xt.cc | 21 | ||||
-rw-r--r-- | storage/pbxt/src/cache_xt.h | 8 | ||||
-rw-r--r-- | storage/pbxt/src/database_xt.h | 1 | ||||
-rw-r--r-- | storage/pbxt/src/datadic_xt.cc | 9 | ||||
-rw-r--r-- | storage/pbxt/src/datalog_xt.cc | 8 | ||||
-rw-r--r-- | storage/pbxt/src/discover_xt.cc | 15 | ||||
-rw-r--r-- | storage/pbxt/src/ha_pbxt.cc | 140 | ||||
-rw-r--r-- | storage/pbxt/src/index_xt.cc | 124 | ||||
-rw-r--r-- | storage/pbxt/src/index_xt.h | 2 | ||||
-rw-r--r-- | storage/pbxt/src/myxt_xt.cc | 8 | ||||
-rw-r--r-- | storage/pbxt/src/myxt_xt.h | 12 | ||||
-rw-r--r-- | storage/pbxt/src/restart_xt.cc | 58 | ||||
-rw-r--r-- | storage/pbxt/src/strutil_xt.cc | 2 | ||||
-rw-r--r-- | storage/pbxt/src/table_xt.cc | 251 | ||||
-rw-r--r-- | storage/pbxt/src/table_xt.h | 2 | ||||
-rw-r--r-- | storage/pbxt/src/thread_xt.cc | 9 | ||||
-rw-r--r-- | storage/pbxt/src/xaction_xt.cc | 4 | ||||
-rw-r--r-- | storage/pbxt/src/xt_defs.h | 24 |
20 files changed, 663 insertions, 83 deletions
diff --git a/storage/pbxt/ChangeLog b/storage/pbxt/ChangeLog index b6023d26139..f0f9864d0d5 100644 --- a/storage/pbxt/ChangeLog +++ b/storage/pbxt/ChangeLog @@ -1,7 +1,49 @@ PBXT Release Notes ================== -+------- 1.0.11 Pre-GA - 2010-05-11 +------- 1.0.11-7 Pre-GA - 2010-09-09 + +RN336: Compiled and tested with MySQL 5.1.50. + +RN335: Fixed bug #523994: Deleting all records does not update table statistics. + +RN334: Made a change to reduce the time that only temporary tables exist during the ALTER TABLE and REPAIR TABLE statements. This increases the chance of recovery if a crash occurs during these operations. + +RN333: Log name of table when PBXT recovers an index on startup. If an error occurs during index recovery, the index is set to "repair pending". + +RN332: Fixed an inifinite loop when a record in a row is corrupt. Added logging and set the table to "repair pending" in this case. + +RN331: Fixed bug #626890: Crash on truncate table operation. + +RN330: Added additional checks for corruption of the index free list. + +------- 1.0.11-6 Pre-GA - 2010-07-08 + +RN329: Fixed bug #601245: make fails. PBXT did not compile if the partition engine was disabled in the MySQL build. + +------- 1.0.11-5 Pre-GA - 2010-06-18 + +RN328: Fixed bug #595478: Compile fails (1.0.11-4). + +------- 1.0.11-4 Pre-GA - 2010-06-15 + +RN327: Fixed a bug that caused a crash during delete on the index. The crash occurred due to memory overwrite when a long key is promoted after a shorter key is deleted, and the difference causes a node size overflow. + +------- 1.0.11-3 Pre-GA - 2010-06-11 + +RN326: Fixed bug #587740: pbxt-1.0.11-pre2-ga first time create partition table error. This was not a new bug. The problem was the PBXT system table's .frm files are corrupted when the first PBXT table created is a partition table. + +RN325: Fixed the "to-sweep" column output in xtstat. + +------- 1.0.11-2 Pre-GA - 2010-05-26 + +RN324: Fixed bug #584070:pbxt-1.0.11-pre-ga does not work with mysql 5.1.47. This bug fix removes a hack which was done to avoid running into the LOCK_plugin lock. + +------- 1.0.11-1 Pre-GA - 2010-05-19 + +RN323: Detect corruption of a key length in an index page. This bug fix avoids a possible crash due to index page corruption. + +------- 1.0.11 Pre-GA - 2010-05-11 RN322: Creating a table the references a non-existing table can now only be done if you set: foreign_key_checks = 0. Also fixed a failure when creating tables with recursive foreign key declarations. diff --git a/storage/pbxt/src/Makefile.am b/storage/pbxt/src/Makefile.am index 20399aee484..fc4c4ef8f1e 100644 --- a/storage/pbxt/src/Makefile.am +++ b/storage/pbxt/src/Makefile.am @@ -40,8 +40,8 @@ libpbxt_la_LDFLAGS = -module # These are the warning Drizzle uses: # DRIZZLE_WARNINGS = -W -Wall -Wextra -pedantic -Wundef -Wredundant-decls -Wno-strict-aliasing -Wno-long-long -Wno-unused-parameter -libpbxt_la_CXXFLAGS = -shared $(AM_CXXFLAGS) -DMYSQL_DYNAMIC_PLUGIN -libpbxt_la_CFLAGS = -shared $(AM_CFLAGS) -DMYSQL_DYNAMIC_PLUGIN -std=c99 +libpbxt_la_CXXFLAGS = $(AM_CXXFLAGS) -DMYSQL_DYNAMIC_PLUGIN +libpbxt_la_CFLAGS = $(AM_CFLAGS) -DMYSQL_DYNAMIC_PLUGIN -std=c99 EXTRA_LIBRARIES = libpbxt.a noinst_LIBRARIES = libpbxt.a diff --git a/storage/pbxt/src/cache_xt.cc b/storage/pbxt/src/cache_xt.cc index 85eea41dd79..24e42d9e984 100644 --- a/storage/pbxt/src/cache_xt.cc +++ b/storage/pbxt/src/cache_xt.cc @@ -668,6 +668,9 @@ xtPublic void xt_ind_init(XTThreadPtr self, size_t cache_size) block->cb_data = buffer; buffer += XT_INDEX_PAGE_SIZE; #endif +#ifdef CHECK_BLOCK_TRAILERS + XT_SET_DISK_4(block->cp_check, 0xDEADBEEF); +#endif ind_cac_globals.cg_free_list = block; block++; } @@ -684,6 +687,19 @@ xtPublic void xt_ind_init(XTThreadPtr self, size_t cache_size) cont_(a); } +#ifdef CHECK_BLOCK_TRAILERS +xtPublic void check_block_trailers() +{ + XTIndBlockPtr block; + + block = ind_cac_globals.cg_blocks; + for (u_int i=0; i<ind_cac_globals.cg_block_count; i++) { + ASSERT_NS(XT_GET_DISK_4(block->cp_check) == 0xDEADBEEF); + block++; + } +} +#endif + xtPublic void xt_ind_exit(XTThreadPtr self) { #ifdef XT_USE_MYSYS @@ -1283,7 +1299,7 @@ static XTIndBlockPtr ind_cac_fetch(XTOpenTablePtr ot, XTIndexPtr ind, xtIndexNod * Conditionally count the number of deleted entries in the index: * We do this before other threads can read the block. */ - if (ind->mi_lazy_delete && read_data) + if (ind && ind->mi_lazy_delete && read_data) xt_ind_count_deleted_items(ot->ot_table, ind, block); /* Add to the hash table: */ @@ -1358,6 +1374,9 @@ xtPublic xtBool xt_ind_write(XTOpenTablePtr ot, XTIndexPtr ind, xtIndexNodeID ad #ifdef XT_TRACK_INDEX_UPDATES ot->ot_ind_changed++; #endif +#ifdef CHECK_BLOCK_TRAILERS + check_block_trailers(); +#endif return OK; } diff --git a/storage/pbxt/src/cache_xt.h b/storage/pbxt/src/cache_xt.h index ca796ab1a74..63a5164e466 100644 --- a/storage/pbxt/src/cache_xt.h +++ b/storage/pbxt/src/cache_xt.h @@ -33,6 +33,7 @@ struct XTIdxReadBuffer; #ifdef DEBUG //#define XT_USE_CACHE_DEBUG_SIZES +//#define CHECK_BLOCK_TRAILERS #endif #ifdef XT_USE_CACHE_DEBUG_SIZES @@ -116,6 +117,9 @@ typedef struct XTIndBlock { #else xtWord1 cb_data[XT_INDEX_PAGE_SIZE]; #endif +#ifdef CHECK_BLOCK_TRAILERS + xtWord1 cp_check[4]; +#endif } XTIndBlockRec, *XTIndBlockPtr; typedef struct XTIndReference { @@ -177,6 +181,10 @@ xtBool xt_ind_copy_on_write(XTIndReferencePtr iref); XTIndHandlePtr xt_ind_get_handle(struct XTOpenTable *ot, XTIndexPtr ind, XTIndReferencePtr iref); void xt_ind_release_handle(XTIndHandlePtr handle, xtBool have_lock, XTThreadPtr thread); +#ifdef CHECK_BLOCK_TRAILERS +extern void check_block_trailers(); +#endif + #ifdef DEBUG //#define DEBUG_CHECK_IND_CACHE #endif diff --git a/storage/pbxt/src/database_xt.h b/storage/pbxt/src/database_xt.h index 1b1863d2045..7744aeeac31 100644 --- a/storage/pbxt/src/database_xt.h +++ b/storage/pbxt/src/database_xt.h @@ -117,6 +117,7 @@ typedef struct XTDatabase : public XTHeap { XTSortedListPtr db_table_by_id; XTSortedListPtr db_table_paths; /* A list of table paths used by this database. */ xtBool db_multi_path; + XTSortedListPtr db_error_list; /* A list of errors already reported. */ /* The open table pool: */ XTAllTablePoolsRec db_ot_pool; diff --git a/storage/pbxt/src/datadic_xt.cc b/storage/pbxt/src/datadic_xt.cc index 1a1ffeeb096..6a58d23d980 100644 --- a/storage/pbxt/src/datadic_xt.cc +++ b/storage/pbxt/src/datadic_xt.cc @@ -396,7 +396,7 @@ void XTToken::expectNumber(XTThreadPtr self) struct charset_info_st; class XTTokenizer { - struct charset_info_st *tkn_charset; + MX_CONST_CHARSET_INFO *tkn_charset; char *tkn_cstring; char *tkn_curr_pos; XTToken *tkn_current; @@ -1324,7 +1324,7 @@ void XTParseTable::parseDropIndex(XTThreadPtr self) class XTCreateTable : public XTParseTable { public: bool ct_convert; - struct charset_info_st *ct_charset; + MX_CONST_CHARSET_INFO *ct_charset; XTPathStrPtr ct_tab_path; u_int ct_contraint_no; XTDDTable *ct_curr_table; @@ -2039,11 +2039,6 @@ void XTDDTableRef::deleteAllRows(XTThreadPtr self) if (!(ot = xt_db_open_table_using_tab(tr_fkey->co_table->dt_table, self))) xt_throw(self); - /* {FREE-ROWS-BAD} */ - /* - row_count = ((xtInt8) ot->ot_table->tab_row_eof_id) - 1; - row_count -= (xtInt8) ot->ot_table->tab_row_fnum; - */ /* Check if there are any rows in the referencing table: */ if (!xt_tab_seq_init(ot)) goto failed; diff --git a/storage/pbxt/src/datalog_xt.cc b/storage/pbxt/src/datalog_xt.cc index ff58a122e10..3238f0cbd17 100644 --- a/storage/pbxt/src/datalog_xt.cc +++ b/storage/pbxt/src/datalog_xt.cc @@ -1249,7 +1249,7 @@ xtBool XTDataLogBuffer::dlb_write_thru_log(xtLogID XT_NDEBUG_UNUSED(log_id), xtL */ dlb_data_log->dlf_log_eof += size; #ifdef DEBUG - if ((ulonglong) (log_offset + size) > (ulonglong) dlb_max_write_offset) + if (log_offset + (xtLogOffset) size > (xtLogOffset) dlb_max_write_offset) dlb_max_write_offset = log_offset + size; #endif dlb_flush_required = TRUE; @@ -1291,7 +1291,7 @@ xtBool XTDataLogBuffer::dlb_append_log(xtLogID XT_NDEBUG_UNUSED(log_id), xtLogOf if (!xt_pwrite_file(dlb_data_log->dlf_log_file, log_offset, size, data, &thread->st_statistics.st_data, thread)) return FAILED; #ifdef DEBUG - if ((ulonglong) (log_offset + size) > (ulonglong) dlb_max_write_offset) + if (log_offset + (xtLogOffset) size > (xtLogOffset) dlb_max_write_offset) dlb_max_write_offset = log_offset + size; #endif dlb_flush_required = TRUE; @@ -1734,8 +1734,8 @@ static xtBool dl_collect_garbage(XTThreadPtr self, XTDatabaseHPtr db, XTDataLogF xtLogOffset src_log_offset; xtLogID curr_log_id; xtLogOffset curr_log_offset; - xtLogID dest_log_id= 0; - xtLogOffset dest_log_offset= 0; + xtLogID dest_log_id = 0; + xtLogOffset dest_log_offset = 0; off_t garbage_count = 0; memset(&cs, 0, sizeof(XTCompactorStateRec)); diff --git a/storage/pbxt/src/discover_xt.cc b/storage/pbxt/src/discover_xt.cc index 2a42c77ac69..7f7281d8c30 100644 --- a/storage/pbxt/src/discover_xt.cc +++ b/storage/pbxt/src/discover_xt.cc @@ -1622,7 +1622,11 @@ int xt_create_table_frm(handlerton *hton, THD* thd, const char *db, const char * COLUMN_FORMAT_TYPE_FIXED, #endif NULL /*default_value*/, NULL /*on_update_value*/, &comment, NULL /*change*/, - NULL /*interval_list*/, info->field_charset, 0 /*uint_geom_type*/)) + NULL /*interval_list*/, info->field_charset, 0 /*uint_geom_type*/ +#if defined(MARIADB_BASE_VERSION) && MYSQL_VERSION_ID > 50200 + , NULL /*vcol_info*/, NULL /* create options */ +#endif + )) #endif goto error; @@ -1654,8 +1658,17 @@ int xt_create_table_frm(handlerton *hton, THD* thd, const char *db, const char * if (mysql_create_table_no_lock(thd, db, name, &create_info, &table_proto, &stmt->alter_info, 1, 0)) goto error; #else +#ifdef WITH_PARTITION_STORAGE_ENGINE + partition_info *part_info; + + part_info = thd->work_part_info; + thd->work_part_info = NULL; +#endif if (mysql_create_table_no_lock(thd, db, name, &mylex.create_info, &mylex.alter_info, 1, 0)) goto error; +#ifdef WITH_PARTITION_STORAGE_ENGINE + thd->work_part_info = part_info; +#endif #endif noerror: diff --git a/storage/pbxt/src/ha_pbxt.cc b/storage/pbxt/src/ha_pbxt.cc index 5bf2069f656..ef0ae582c07 100644 --- a/storage/pbxt/src/ha_pbxt.cc +++ b/storage/pbxt/src/ha_pbxt.cc @@ -1232,6 +1232,11 @@ static int pbxt_init(void *p) THD *thd = NULL; #ifndef DRIZZLED +#if MYSQL_VERSION_ID < 50147 + /* A hack which is no longer required after 5.1.46 */ + extern myxt_mutex_t LOCK_plugin; +#endif + /* {MYSQL QUIRK} * I have to release this lock for PBXT recovery to * work, because it needs to open .frm files. @@ -1248,8 +1253,7 @@ static int pbxt_init(void *p) * Only real problem, 2 threads try to load the same * plugin at the same time. */ -#if MYSQL_VERSION_ID <= 50146 - extern myxt_mutex_t LOCK_plugin; +#if MYSQL_VERSION_ID < 50147 myxt_mutex_unlock(&LOCK_plugin); #endif #endif @@ -1285,9 +1289,11 @@ static int pbxt_init(void *p) if (thd) myxt_destroy_thread(thd, FALSE); -#if MYSQL_VERSION_ID <= 50146 && !defined(DRIZZLED) +#ifndef DRIZZLED +#if MYSQL_VERSION_ID < 50147 myxt_mutex_lock(&LOCK_plugin); #endif +#endif } #endif } @@ -1948,8 +1954,13 @@ xtPublic int ha_pbxt::reopen() * selectity of the indices, as soon as the number of rows * exceeds 200 (see [**]) */ +#ifdef XT_ROW_COUNT_CORRECTED + /* {CORRECTED-ROW-COUNT} */ + pb_share->sh_recalc_selectivity = (pb_share->sh_table->tab_row_eof_id - 1 - pb_share->sh_table->tab_row_fnum) < 150; +#else /* {FREE-ROWS-BAD} */ pb_share->sh_recalc_selectivity = (pb_share->sh_table->tab_row_eof_id - 1 /* - pb_share->sh_table->tab_row_fnum */) < 150; +#endif } /* I am not doing this anymore because it was only required @@ -2006,7 +2017,7 @@ static int pbxt_statistics_fill_table(THD *thd, TABLE_LIST *tables, COND *cond) xt_ha_open_database_of_table(self, (XTPathStrPtr) NULL); } - err = myxt_statistics_fill_table(self, thd, tables, cond, system_charset_info); + err = myxt_statistics_fill_table(self, thd, tables, cond, (void*) system_charset_info); } catch_(a) { err = xt_ha_pbxt_thread_error_for_mysql(thd, self, FALSE); @@ -2296,6 +2307,36 @@ void ha_pbxt::internal_close(THD *thd, struct XTThread *self) */ if (!thd || thd_sql_command(thd) == SQLCOM_FLUSH) // FLUSH TABLES xt_sync_flush_table(self, ot); + else { + /* This change is a result of a problem mentioned by Arjen. + * REPAIR and ALTER lead to the following sequence: + * 1. tab -- copy --> tmp1 + * 2. tab -- rename --> tmp2 + * 3. tmp1 -- rename --> tab + * 4. delete tmp2 + * + * PBXT flushes a table before rename. + * In the sequence above results in a table flush in step 3 which can + * take a very long time. + * + * The problem is, during this time frame we have only temp tables. + * A crash in this state leaves the database in a bad state. + * + * To reduce the time in this state, the flush needs to be done + * elsewhere. The code below causes the flish to occur after + * step 1: + */ + switch (thd_sql_command(thd)) { + case SQLCOM_REPAIR: + case SQLCOM_RENAME_TABLE: + case SQLCOM_OPTIMIZE: + case SQLCOM_ANALYZE: + case SQLCOM_ALTER_TABLE: + case SQLCOM_CREATE_INDEX: + xt_sync_flush_table(self, ot); + break; + } + } } freer_(); // xt_db_return_table_to_pool(ot); } @@ -2356,9 +2397,15 @@ int ha_pbxt::open(const char *table_path, int XT_UNUSED(mode), uint XT_UNUSED(te #else xt_tab_load_row_pointers(self, pb_open_tab); #endif + xt_ind_set_index_selectivity(pb_open_tab, self); +#ifdef XT_ROW_COUNT_CORRECTED + /* {CORRECTED-ROW-COUNT} */ + pb_share->sh_recalc_selectivity = (pb_share->sh_table->tab_row_eof_id - 1 - pb_share->sh_table->tab_row_fnum) < 150; +#else /* {FREE-ROWS-BAD} */ pb_share->sh_recalc_selectivity = (pb_share->sh_table->tab_row_eof_id - 1 /* - pb_share->sh_table->tab_row_fnum */) < 150; +#endif } init_auto_increment(0); @@ -3929,6 +3976,8 @@ int ha_pbxt::info(uint flag) if ((ot = pb_open_tab)) { if (flag & HA_STATUS_VARIABLE) { + register XTTableHPtr tab = ot->ot_table; + /* {FREE-ROWS-BAD} * Free row count is not reliable, so ignore it. * The problem is if tab_row_fnum > tab_row_eof_id - 1 then @@ -3955,11 +4004,26 @@ int ha_pbxt::info(uint flag) * the actual number of vectors. But it must assume that it has at * least EXTRA_RECORDS vectors. */ - stats.deleted = /* ot->ot_table->tab_row_fnum */ 0; - stats.records = (ha_rows) (ot->ot_table->tab_row_eof_id - 1 /* - stats.deleted */); - stats.data_file_length = xt_rec_id_to_rec_offset(ot->ot_table, ot->ot_table->tab_rec_eof_id); - stats.index_file_length = xt_ind_node_to_offset(ot->ot_table, ot->ot_table->tab_ind_eof); - stats.delete_length = ot->ot_table->tab_rec_fnum * ot->ot_rec_size; +#ifdef XT_ROW_COUNT_CORRECTED + if (tab->tab_row_eof_id <= tab->tab_row_fnum || + (!tab->tab_row_free_id && tab->tab_row_fnum)) + xt_tab_check_free_lists(NULL, ot, false, true); + stats.records = (ha_rows) tab->tab_row_eof_id - 1; + if (stats.records >= tab->tab_row_fnum) { + stats.deleted = tab->tab_row_fnum; + stats.records -= stats.deleted; + } + else { + stats.deleted = 0; + stats.records = 2; + } +#else + stats.deleted = /* tab->tab_row_fnum */ 0; + stats.records = (ha_rows) (tab->tab_row_eof_id - 1 /* - stats.deleted */); +#endif + stats.data_file_length = xt_rec_id_to_rec_offset(tab, tab->tab_rec_eof_id); + stats.index_file_length = xt_ind_node_to_offset(tab, tab->tab_ind_eof); + stats.delete_length = tab->tab_rec_fnum * ot->ot_rec_size; //check_time = info.check_time; stats.mean_rec_length = (ulong) ot->ot_rec_size; } @@ -4584,13 +4648,24 @@ xtPublic int ha_pbxt::external_lock(THD *thd, int lock_type) } if (pb_share->sh_recalc_selectivity) { +#ifdef XT_ROW_COUNT_CORRECTED + /* {CORRECTED-ROW-COUNT} */ + if ((pb_share->sh_table->tab_row_eof_id - 1 - pb_share->sh_table->tab_row_fnum) >= 200) +#else /* {FREE-ROWS-BAD} */ - if ((pb_share->sh_table->tab_row_eof_id - 1 /* - pb_share->sh_table->tab_row_fnum */) >= 200) { + if ((pb_share->sh_table->tab_row_eof_id - 1 /* - pb_share->sh_table->tab_row_fnum */) >= 200) +#endif + { /* [**] */ pb_share->sh_recalc_selectivity = FALSE; xt_ind_set_index_selectivity(pb_open_tab, self); +#ifdef XT_ROW_COUNT_CORRECTED + /* {CORRECTED-ROW-COUNT} */ + pb_share->sh_recalc_selectivity = (pb_share->sh_table->tab_row_eof_id - 1 - pb_share->sh_table->tab_row_fnum) < 150; +#else /* {FREE-ROWS-BAD} */ pb_share->sh_recalc_selectivity = (pb_share->sh_table->tab_row_eof_id - 1 /* - pb_share->sh_table->tab_row_fnum */) < 150; +#endif } } } @@ -4638,6 +4713,17 @@ xtPublic int ha_pbxt::external_lock(THD *thd, int lock_type) goto complete; } cont_(a); + + /* Occurs if you do: + * truncate table t1; + * truncate table t1; + */ + if (!pb_open_tab) { + if ((err = reopen())) { + pb_ex_in_use = 0; + goto complete; + } + } } else { pb_ex_in_use = 1; @@ -6076,6 +6162,40 @@ mysql_declare_plugin(pbxt) drizzle_declare_plugin_end; #else mysql_declare_plugin_end; +#if defined(MARIADB_BASE_VERSION) && MYSQL_VERSION_ID > 50200 +maria_declare_plugin(pbxt) +{ /* PBXT */ + MYSQL_STORAGE_ENGINE_PLUGIN, + &pbxt_storage_engine, + "PBXT", + "Paul McCullagh, PrimeBase Technologies GmbH", + "High performance, multi-versioning transactional engine", + PLUGIN_LICENSE_GPL, + pbxt_init, /* Plugin Init */ + pbxt_end, /* Plugin Deinit */ + 0x0001 /* 0.1 */, + NULL, /* status variables */ + pbxt_system_variables, /* system variables */ + "1.0.11-7 Pre-GA", /* string version */ + MariaDB_PLUGIN_MATURITY_GAMMA /* maturity */ +}, +{ /* PBXT_STATISTICS */ + MYSQL_INFORMATION_SCHEMA_PLUGIN, + &pbxt_statitics, + "PBXT_STATISTICS", + "Paul McCullagh, PrimeBase Technologies GmbH", + "PBXT internal system statitics", + PLUGIN_LICENSE_GPL, + pbxt_init_statistics, /* plugin init */ + pbxt_exit_statistics, /* plugin deinit */ + 0x0005, + NULL, /* status variables */ + NULL, /* system variables */ + "1.0.11-7 Pre-GA", /* string version */ + MariaDB_PLUGIN_MATURITY_GAMMA /* maturity */ +} +maria_declare_plugin_end; +#endif #endif #if defined(XT_WIN) && defined(XT_COREDUMP) diff --git a/storage/pbxt/src/index_xt.cc b/storage/pbxt/src/index_xt.cc index c8995fe253c..f6c4b4d8aa3 100644 --- a/storage/pbxt/src/index_xt.cc +++ b/storage/pbxt/src/index_xt.cc @@ -272,10 +272,17 @@ static xtBool idx_new_branch(XTOpenTablePtr ot, XTIndexPtr ind, xtIndexNodeID *a } if ((XT_NODE_ID(wrote_pos) = XT_NODE_ID(tab->tab_ind_free))) { + xtIndexNodeID next_node; + /* Use the block on the free list: */ - if (!xt_ind_read_bytes(ot, ind, wrote_pos, sizeof(XTIndFreeBlockRec), (xtWord1 *) &free_block)) + if (!xt_ind_read_bytes(ot, NULL, wrote_pos, sizeof(XTIndFreeBlockRec), (xtWord1 *) &free_block)) + goto failed; + XT_NODE_ID(next_node) = (xtIndexNodeID) XT_GET_DISK_8(free_block.if_next_block_8); + if (XT_NODE_ID(next_node) >= XT_NODE_ID(tab->tab_ind_eof)) { + xt_register_taberr(XT_REG_CONTEXT, XT_ERR_INDEX_CORRUPTED, tab->tab_name); goto failed; - XT_NODE_ID(tab->tab_ind_free) = (xtIndexNodeID) XT_GET_DISK_8(free_block.if_next_block_8); + } + XT_NODE_ID(tab->tab_ind_free) = XT_NODE_ID(next_node); xt_unlock_mutex_ns(&tab->tab_ind_lock); *address = wrote_pos; TRACK_BLOCK_ALLOC(wrote_pos); @@ -1415,30 +1422,45 @@ static xtBool idx_replace_node_key(XTOpenTablePtr ot, XTIndexPtr ind, IdxStackIt if (idx_is_item_deleted(iref.ir_branch, &item->i_pos)) iref.ir_block->cp_del_count--; } - memmove(&iref.ir_branch->tb_data[item->i_pos.i_item_offset + item_size], - &iref.ir_branch->tb_data[item->i_pos.i_item_offset + item->i_pos.i_item_size], - item->i_pos.i_total_size - item->i_pos.i_item_offset - item->i_pos.i_item_size); - memcpy(&iref.ir_branch->tb_data[item->i_pos.i_item_offset], - item_buf, item_size); - if (ind->mi_lazy_delete) { - if (idx_is_item_deleted(iref.ir_branch, &item->i_pos)) - iref.ir_block->cp_del_count++; - } - item->i_pos.i_total_size = item->i_pos.i_total_size + item_size - item->i_pos.i_item_size; - XT_SET_DISK_2(iref.ir_branch->tb_size_2, XT_MAKE_NODE_SIZE(item->i_pos.i_total_size)); - IDX_TRACE("%d-> %x\n", (int) XT_NODE_ID(current), (int) XT_GET_DISK_2(iref.ir_branch->tb_size_2)); - iref.ir_updated = TRUE; + + if (item->i_pos.i_total_size + item_size - item->i_pos.i_item_size <= XT_INDEX_PAGE_DATA_SIZE) { + /* The new item is larger than the old, this can result + * in overflow of the node! + */ + memmove(&iref.ir_branch->tb_data[item->i_pos.i_item_offset + item_size], + &iref.ir_branch->tb_data[item->i_pos.i_item_offset + item->i_pos.i_item_size], + item->i_pos.i_total_size - item->i_pos.i_item_offset - item->i_pos.i_item_size); + memcpy(&iref.ir_branch->tb_data[item->i_pos.i_item_offset], + item_buf, item_size); + if (ind->mi_lazy_delete) { + if (idx_is_item_deleted(iref.ir_branch, &item->i_pos)) + iref.ir_block->cp_del_count++; + } + item->i_pos.i_total_size = item->i_pos.i_total_size + item_size - item->i_pos.i_item_size; + XT_SET_DISK_2(iref.ir_branch->tb_size_2, XT_MAKE_NODE_SIZE(item->i_pos.i_total_size)); + IDX_TRACE("%d-> %x\n", (int) XT_NODE_ID(current), (int) XT_GET_DISK_2(iref.ir_branch->tb_size_2)); + iref.ir_updated = TRUE; #ifdef DEBUG - if (ind->mi_lazy_delete) ASSERT_NS(item->i_pos.i_total_size <= XT_INDEX_PAGE_DATA_SIZE); #endif - if (item->i_pos.i_total_size <= XT_INDEX_PAGE_DATA_SIZE) return xt_ind_release(ot, ind, XT_UNLOCK_W_UPDATE, &iref); + } /* The node has overflowed!! */ result.sr_item = item->i_pos; + memcpy(ot->ot_ind_wbuf.tb_data, iref.ir_branch->tb_data, item->i_pos.i_item_offset); // First part of the buffer + memcpy(&ot->ot_ind_wbuf.tb_data[item->i_pos.i_item_offset], item_buf, item_size); // The new item + memcpy(&ot->ot_ind_wbuf.tb_data[item->i_pos.i_item_offset + item_size], + &iref.ir_branch->tb_data[item->i_pos.i_item_offset + item->i_pos.i_item_size], + item->i_pos.i_total_size - item->i_pos.i_item_offset - item->i_pos.i_item_size); + item->i_pos.i_total_size += item_size - item->i_pos.i_item_size; + item->i_pos.i_item_size = item_size; + XT_SET_DISK_2(ot->ot_ind_wbuf.tb_size_2, XT_MAKE_LEAF_SIZE(item->i_pos.i_total_size)); + IDX_TRACE("%d-> %x\n", (int) XT_NODE_ID(current), (int) XT_GET_DISK_2(ot->ot_ind_wbuf.tb_size_2)); + ASSERT_NS(item->i_pos.i_total_size > XT_INDEX_PAGE_DATA_SIZE && item->i_pos.i_total_size <= XT_INDEX_PAGE_DATA_SIZE*2); + /* Adjust the stack (we want the parents of the delete node): */ for (;;) { if (idx_pop(stack) == item) @@ -1448,7 +1470,7 @@ static xtBool idx_replace_node_key(XTOpenTablePtr ot, XTIndexPtr ind, IdxStackIt /* We assume that value can be overwritten (which is the case) */ key_value.sv_flags = XT_SEARCH_WHOLE_KEY; key_value.sv_key = key_buf; - if (!idx_get_middle_branch_item(ot, ind, iref.ir_branch, &key_value, &result)) + if (!idx_get_middle_branch_item(ot, ind, &ot->ot_ind_wbuf, &key_value, &result)) goto failed_1; if (!idx_new_branch(ot, ind, &new_branch)) @@ -1456,7 +1478,6 @@ static xtBool idx_replace_node_key(XTOpenTablePtr ot, XTIndexPtr ind, IdxStackIt /* Split the node: */ new_size = result.sr_item.i_total_size - result.sr_item.i_item_offset - result.sr_item.i_item_size; - // TODO: Are 2 buffers now required? new_branch_ptr = (XTIdxBranchDPtr) &ot->ot_ind_wbuf.tb_data[XT_INDEX_PAGE_DATA_SIZE]; memmove(new_branch_ptr->tb_data, &iref.ir_branch->tb_data[result.sr_item.i_item_offset + result.sr_item.i_item_size], new_size); @@ -1466,10 +1487,10 @@ static xtBool idx_replace_node_key(XTOpenTablePtr ot, XTIndexPtr ind, IdxStackIt goto failed_2; /* Change the size of the old branch: */ - XT_SET_DISK_2(iref.ir_branch->tb_size_2, XT_MAKE_NODE_SIZE(result.sr_item.i_item_offset)); - IDX_TRACE("%d-> %x\n", (int) XT_NODE_ID(current), (int) XT_GET_DISK_2(iref.ir_branch->tb_size_2)); + XT_SET_DISK_2(ot->ot_ind_wbuf.tb_size_2, XT_MAKE_NODE_SIZE(result.sr_item.i_item_offset)); + IDX_TRACE("%d-> %x\n", (int) XT_NODE_ID(current), (int) XT_GET_DISK_2(ot->ot_ind_wbuf.tb_size_2)); + memcpy(iref.ir_branch, &ot->ot_ind_wbuf, offsetof(XTIdxBranchDRec, tb_data) + result.sr_item.i_item_offset); iref.ir_updated = TRUE; - xt_ind_release(ot, ind, XT_UNLOCK_W_UPDATE, &iref); /* Insert the new branch into the parent node, using the new middle key value: */ @@ -2071,6 +2092,11 @@ xtPublic xtBool xt_idx_insert(XTOpenTablePtr ot, XTIndexPtr ind, xtRowID row_id, if (!idx_new_branch(ot, ind, &new_branch)) goto failed_1; + if (XT_NODE_ID(current) == XT_NODE_ID(new_branch)) { + xt_register_taberr(XT_REG_CONTEXT, XT_ERR_INDEX_CORRUPTED, ot->ot_table->tab_name); + goto failed_1; + } + /* Copy and write the rest of the data to the new node: */ new_size = result.sr_item.i_total_size - result.sr_item.i_item_offset - result.sr_item.i_item_size; new_branch_ptr = (XTIdxBranchDPtr) &ot->ot_ind_wbuf.tb_data[XT_INDEX_PAGE_DATA_SIZE]; @@ -2723,6 +2749,10 @@ xtPublic xtBool xt_idx_search(XTOpenTablePtr ot, XTIndexPtr ind, register XTIdxS #endif ASSERT_NS(iref.ir_xlock == 2); ASSERT_NS(iref.ir_updated == 2); + if (ind->mi_key_corrupted) { + xt_register_taberr(XT_REG_CONTEXT, XT_ERR_INDEX_CORRUPTED, ot->ot_table->tab_name); + return FAILED; + } return OK; failed: @@ -2874,6 +2904,10 @@ xtPublic xtBool xt_idx_search_prev(XTOpenTablePtr ot, XTIndexPtr ind, register X //idx_check_index(ot, ind, TRUE); //idx_check_on_key(ot); #endif + if (ind->mi_key_corrupted) { + xt_register_taberr(XT_REG_CONTEXT, XT_ERR_INDEX_CORRUPTED, ot->ot_table->tab_name); + return FAILED; + } return OK; failed: @@ -2964,6 +2998,10 @@ xtPublic xtBool xt_idx_next(register XTOpenTablePtr ot, register XTIndexPtr ind, if (!(XT_NODE_ID(current) = XT_NODE_ID(ind->mi_root))) { XT_INDEX_UNLOCK(ind, ot); + if (ind->mi_key_corrupted) { + xt_register_taberr(XT_REG_CONTEXT, XT_ERR_INDEX_CORRUPTED, ot->ot_table->tab_name); + return FAILED; + } return OK; } @@ -3071,6 +3109,10 @@ xtPublic xtBool xt_idx_next(register XTOpenTablePtr ot, register XTIndexPtr ind, ot->ot_curr_rec_id = 0; ot->ot_curr_row_id = 0; XT_INDEX_UNLOCK(ind, ot); + if (ind->mi_key_corrupted) { + xt_register_taberr(XT_REG_CONTEXT, XT_ERR_INDEX_CORRUPTED, ot->ot_table->tab_name); + return FAILED; + } return OK; } @@ -3112,6 +3154,10 @@ xtPublic xtBool xt_idx_next(register XTOpenTablePtr ot, register XTIndexPtr ind, ot->ot_curr_row_id = result.sr_row_id; ot->ot_ind_state = result.sr_item; + if (ind->mi_key_corrupted) { + xt_register_taberr(XT_REG_CONTEXT, XT_ERR_INDEX_CORRUPTED, ot->ot_table->tab_name); + return FAILED; + } return OK; failed: @@ -3178,6 +3224,10 @@ xtPublic xtBool xt_idx_prev(register XTOpenTablePtr ot, register XTIndexPtr ind, if (!(XT_NODE_ID(current) = XT_NODE_ID(ind->mi_root))) { XT_INDEX_UNLOCK(ind, ot); + if (ind->mi_key_corrupted) { + xt_register_taberr(XT_REG_CONTEXT, XT_ERR_INDEX_CORRUPTED, ot->ot_table->tab_name); + return FAILED; + } return OK; } @@ -3274,6 +3324,10 @@ xtPublic xtBool xt_idx_prev(register XTOpenTablePtr ot, register XTIndexPtr ind, ot->ot_curr_row_id = 0; XT_INDEX_UNLOCK(ind, ot); + if (ind->mi_key_corrupted) { + xt_register_taberr(XT_REG_CONTEXT, XT_ERR_INDEX_CORRUPTED, ot->ot_table->tab_name); + return FAILED; + } return OK; unlock_check_on_key: @@ -3302,6 +3356,10 @@ xtPublic xtBool xt_idx_prev(register XTOpenTablePtr ot, register XTIndexPtr ind, ot->ot_curr_rec_id = result.sr_rec_id; ot->ot_curr_row_id = result.sr_row_id; ot->ot_ind_state = result.sr_item; + if (ind->mi_key_corrupted) { + xt_register_taberr(XT_REG_CONTEXT, XT_ERR_INDEX_CORRUPTED, ot->ot_table->tab_name); + return FAILED; + } return OK; failed: @@ -3648,7 +3706,7 @@ xtPublic void xt_check_indices(XTOpenTablePtr ot) track_block_exists(current); #endif printf("%d ", (int) XT_NODE_ID(current)); - if (!xt_ind_read_bytes(ot, *ind, current, sizeof(XTIndFreeBlockRec), (xtWord1 *) &free_block)) { + if (!xt_ind_read_bytes(ot, NULL, current, sizeof(XTIndFreeBlockRec), (xtWord1 *) &free_block)) { xt_log_and_clear_exception_ns(); break; } @@ -4141,11 +4199,18 @@ void XTIndexLogPool::ilp_init(struct XTThread *self, struct XTDatabase *db, size if (!ilp_open_log(&il, log_id, FALSE, self)) goto failed; if (il->il_tab_id && il->il_log_eof) { + char table_name[XT_IDENTIFIER_NAME_SIZE*3+3]; + if (!il->il_open_table(&ot)) goto failed; if (ot) { - if (!il->il_apply_log(ot)) - goto failed; + xt_tab_make_table_name(ot->ot_table, table_name, sizeof(table_name)); + xt_logf(XT_NT_INFO, "PBXT: Recovering index, table: %s, bytes to read: %llu\n", table_name, (u_llong) il->il_log_eof); + if (!il->il_apply_log(ot)) { + /* If recovery of an index fails, then it is corrupt! */ + xt_tab_disable_index(ot->ot_table, XT_INDEX_CORRUPTED); + xt_log_and_clear_exception_ns(); + } ot->ot_thread = self; il->il_close_table(ot); } @@ -4468,8 +4533,7 @@ xtBool XTIndexLog::il_apply_log(struct XTOpenTable *ot) /* Corrupt log?! */ if (il_buffer_len < req_size) { xt_register_ixterr(XT_REG_CONTEXT, XT_ERR_INDEX_LOG_CORRUPT, xt_file_path(il_of)); - xt_log_and_clear_exception_ns(); - return OK; + return FAILED; } if (!xt_pread_file(il_of, offset, il_buffer_len, il_buffer_len, il_buffer, NULL, &ot->ot_thread->st_statistics.st_ilog, ot->ot_thread)) return FAILED; @@ -4548,8 +4612,7 @@ xtBool XTIndexLog::il_apply_log(struct XTOpenTable *ot) /* Corrupt log?! */ if (il_buffer_len < req_size) { xt_register_ixterr(XT_REG_CONTEXT, XT_ERR_INDEX_LOG_CORRUPT, xt_file_path(il_of)); - xt_log_and_clear_exception_ns(); - return OK; + return FAILED; } if (!xt_pread_file(il_of, offset, il_buffer_len, il_buffer_len, il_buffer, NULL, &ot->ot_thread->st_statistics.st_ilog, ot->ot_thread)) return FAILED; @@ -4597,8 +4660,7 @@ xtBool XTIndexLog::il_apply_log(struct XTOpenTable *ot) break; default: xt_register_ixterr(XT_REG_CONTEXT, XT_ERR_INDEX_LOG_CORRUPT, xt_file_path(il_of)); - xt_log_and_clear_exception_ns(); - return OK; + return FAILED; } } diff --git a/storage/pbxt/src/index_xt.h b/storage/pbxt/src/index_xt.h index 52f8f32dd33..a56e7b3cdb7 100644 --- a/storage/pbxt/src/index_xt.h +++ b/storage/pbxt/src/index_xt.h @@ -312,7 +312,7 @@ typedef struct XTIndex { u_int mi_flags; u_int mi_key_size; u_int mi_max_items; /* The maximum number of items that can fit in a leaf node. */ - xtBool mi_low_byte_first; + xtBool mi_key_corrupted; /* Set to TRUE if a currupted index key is detected. */ xtBool mi_fix_key; xtBool mi_lazy_delete; /* TRUE if index entries are "lazy deleted". */ u_int mi_single_type; /* Used when the index contains a single field. */ diff --git a/storage/pbxt/src/myxt_xt.cc b/storage/pbxt/src/myxt_xt.cc index 51490fc00f5..410bf2d2f3c 100644 --- a/storage/pbxt/src/myxt_xt.cc +++ b/storage/pbxt/src/myxt_xt.cc @@ -1088,7 +1088,10 @@ xtPublic u_int myxt_get_key_length(XTIndexPtr ind, xtWord1 *key_buf) } end: - return (xtWord1 *) key_data - key_buf; + u_int ilen = (xtWord1 *) key_data - key_buf; + if (ilen > XT_INDEX_MAX_KEY_SIZE) + ind->mi_key_corrupted = TRUE; + return ilen; } /* Derived from ha_key_cmp */ @@ -2183,7 +2186,8 @@ static XTIndexPtr my_create_index(XTThreadPtr self, TABLE *table_arg, u_int idx, xt_spinlock_init_with_autoname(self, &ind->mi_dirty_lock); ind->mi_index_no = idx; ind->mi_flags = (index->flags & (HA_NOSAME | HA_NULL_ARE_EQUAL | HA_UNIQUE_CHECK)); - ind->mi_low_byte_first = TS(table_arg)->db_low_byte_first; + //ind->mi_low_byte_first = TS(table_arg)->db_low_byte_first; + ind->mi_key_corrupted = FALSE; ind->mi_fix_key = TRUE; ind->mi_select_total = 0; ind->mi_subset_of = 0; diff --git a/storage/pbxt/src/myxt_xt.h b/storage/pbxt/src/myxt_xt.h index 546b57c6e84..3898c8e30c6 100644 --- a/storage/pbxt/src/myxt_xt.h +++ b/storage/pbxt/src/myxt_xt.h @@ -69,17 +69,17 @@ void myxt_free_dictionary(XTThreadPtr self, XTDictionary *dic); void myxt_move_dictionary(XTDictionaryPtr dic, XTDictionaryPtr source_dic); XTDDTable *myxt_create_table_from_table(XTThreadPtr self, STRUCT_TABLE *my_tab); -void myxt_static_convert_identifier(XTThreadPtr self, struct charset_info_st *cs, char *from, char *to, size_t to_len); -char *myxt_convert_identifier(XTThreadPtr self, struct charset_info_st *cs, char *from); +void myxt_static_convert_identifier(XTThreadPtr self, MX_CONST_CHARSET_INFO *cs, char *from, char *to, size_t to_len); +char *myxt_convert_identifier(XTThreadPtr self, MX_CONST_CHARSET_INFO *cs, char *from); void myxt_static_convert_table_name(XTThreadPtr self, char *from, char *to, size_t to_len); void myxt_static_convert_file_name(char *from, char *to, size_t to_len); char *myxt_convert_table_name(XTThreadPtr self, char *from); int myxt_strcasecmp(char * a, char *b); -int myxt_isspace(struct charset_info_st *cs, char a); -int myxt_ispunct(struct charset_info_st *cs, char a); -int myxt_isdigit(struct charset_info_st *cs, char a); +int myxt_isspace(MX_CONST_CHARSET_INFO *cs, char a); +int myxt_ispunct(MX_CONST_CHARSET_INFO *cs, char a); +int myxt_isdigit(MX_CONST_CHARSET_INFO *cs, char a); -struct charset_info_st *myxt_getcharset(bool convert); +MX_CONST_CHARSET_INFO *myxt_getcharset(bool convert); void *myxt_create_thread(); void myxt_destroy_thread(void *thread, xtBool end_threads); diff --git a/storage/pbxt/src/restart_xt.cc b/storage/pbxt/src/restart_xt.cc index b0c8f2854ae..93720f2b113 100644 --- a/storage/pbxt/src/restart_xt.cc +++ b/storage/pbxt/src/restart_xt.cc @@ -1359,6 +1359,57 @@ static xtBool xres_sync_operations(XTThreadPtr self, XTDatabaseHPtr db, XTWriter return op_synced; } +#ifdef XT_CORRECT_TABLE_FREE_COUNT +#define CORRECT_COUNT TRUE +#else +#define CORRECT_COUNT FALSE +#endif +#ifdef XT_CHECK_RECORD_FREE_COUNT +#define CHECK_RECS TRUE +#else +#define CHECK_RECS FALSE +#endif +#if defined(XT_CHECK_RECORD_FREE_COUNT) || defined(XT_CHECK_ROW_FREE_COUNT) +#define RECOVER_FREE_COUNTS +#endif + +#ifdef RECOVER_FREE_COUNTS +/* {CORRECTED-ROW-COUNT} + * This error can be repeated by crashing the server during + * high activitity, after flush table writes the table header + * + * On recovery, the free count "from the future" is used as + * the starting point for subsequent allocation and frees. + * The count is wrong after that point. + * + * The recovery of the count only works correctly if a + * checkpoint is complete successfully after that table + * header is flushed. Basically the writing of the table + * header should be synchronsized with the writing of the + * end of the checkpoint. + * + * Another solution would be to log the count, along with + * the allocate and free commannds. + * + * The 3rd solution is the one used here. The count is corrected + * after recovery. + */ +static void xres_recover_table_free_counts(XTThreadPtr self, XTDatabaseHPtr db, XTWriterStatePtr ws) +{ + u_int edx; + XTTableEntryPtr te_ptr; + XTTableHPtr tab; + + xt_enum_tables_init(&edx); + while ((te_ptr = xt_enum_tables_next(self, db, &edx))) { + if ((tab = te_ptr->te_table)) { + if (xres_open_table(self, ws, te_ptr->te_tab_id)) + xt_tab_check_free_lists(self, ws->ws_ot, CHECK_RECS, CORRECT_COUNT); + } + } +} +#endif + /* * Operations from the log are applied in sequence order. * If the operations are out of sequence, they are buffered @@ -2175,6 +2226,13 @@ xtBool XTXactRestart::xres_restart(XTThreadPtr self, xtLogID *log_id, xtLogOffse /* This is true because if no transaction was placed in RAM then * the next transaction in RAM will have the next ID: */ db->db_xn_min_ram_id = db->db_xn_curr_id + 1; + +#ifdef RECOVER_FREE_COUNTS + if (xres_cp_log_id != *log_id || xres_cp_log_offset != *log_offset) { + /* Recovery took place, correct the row count! */ + xres_recover_table_free_counts(self, db, &ws); + } +#endif } failed: diff --git a/storage/pbxt/src/strutil_xt.cc b/storage/pbxt/src/strutil_xt.cc index 02132fbb06b..8183034a204 100644 --- a/storage/pbxt/src/strutil_xt.cc +++ b/storage/pbxt/src/strutil_xt.cc @@ -380,7 +380,7 @@ xtPublic void xt_int8_to_byte_size(xtInt8 value, char *string) /* Version number must also be set in configure.in! */ xtPublic c_char *xt_get_version(void) { - return "1.0.11 Pre-GA"; + return "1.0.11-7 Pre-GA"; } /* Copy and URL decode! */ diff --git a/storage/pbxt/src/table_xt.cc b/storage/pbxt/src/table_xt.cc index b01f4404ce3..2d93f161ac9 100644 --- a/storage/pbxt/src/table_xt.cc +++ b/storage/pbxt/src/table_xt.cc @@ -80,6 +80,65 @@ /* * ----------------------------------------------------------------------- + * Handle Error Detected in a Table + */ + +struct XTTableError { + xtTableID ter_tab_id; + xtRecordID ter_rec_id; +}; + +static int tab_comp_tab_error(XTThreadPtr XT_UNUSED(self), register const void *XT_UNUSED(thunk), register const void *a, register const void *b) +{ + XTTableError *ter_a = ((XTTableError *) a); + XTTableError *ter_b = (XTTableError *) b; + + if (ter_a->ter_tab_id < ter_b->ter_tab_id) + return -1; + if (ter_a->ter_tab_id == ter_b->ter_tab_id) { + if (ter_a->ter_rec_id < ter_b->ter_rec_id) + return -1; + if (ter_a->ter_rec_id == ter_b->ter_rec_id) + return 0; + return 1; + } + return 1; +} + +static xtBool tab_record_corrupt(XTOpenTablePtr ot, xtRowID row_id, xtRecordID rec_id, bool not_valid, int where) +{ + XTTableHPtr tab = ot->ot_table; + XTDatabaseHPtr db = tab->tab_db; + XTTableError ter; + XTTableError *ter_ptr; + + ter.ter_tab_id = tab->tab_id; + ter.ter_rec_id = rec_id; + + xt_sl_lock_ns(db->db_error_list, ot->ot_thread); + if (!(ter_ptr = (XTTableError *) xt_sl_find(NULL, db->db_error_list, &ter))) { + xtBool ok; + char table_name[XT_IDENTIFIER_NAME_SIZE*3+3]; + + ok = xt_sl_insert(NULL, db->db_error_list, &ter, &ter); + xt_sl_unlock_ns(db->db_error_list); + if (!ok) + return FAILED; + xt_tab_set_table_repair_pending(tab); + xt_tab_make_table_name(tab, table_name, sizeof(table_name)); + xt_logf(XT_NT_ERROR, "#%d Table %s: row %llu, record %llu, is %s, REPAIR TABLE required.\n", where, + table_name, + (u_llong) row_id, + (u_llong) rec_id, + not_valid ? "not valid" : "free"); + } + else + xt_sl_unlock_ns(db->db_error_list); + return OK; +} + +/* + * ----------------------------------------------------------------------- * Compare paths: */ @@ -425,6 +484,7 @@ xtPublic void xt_tab_init_db(XTThreadPtr self, XTDatabaseHPtr db) db->db_tables = xt_new_hashtable(self, tab_list_comp, tab_list_hash, tab_list_free, TRUE, TRUE); db->db_table_by_id = xt_new_sortedlist(self, sizeof(XTTableEntryRec), 20, 20, tab_comp_by_id, db, tab_free_by_id, FALSE, FALSE); db->db_table_paths = xt_new_sortedlist(self, sizeof(XTTablePathPtr), 20, 20, tab_comp_path, db, tab_free_path, FALSE, FALSE); + db->db_error_list = xt_new_sortedlist(self, sizeof(XTTableError), 20, 20, tab_comp_tab_error, db, NULL, TRUE, FALSE); if (db->db_multi_path) { XTOpenFilePtr of; @@ -649,6 +709,10 @@ xtPublic void xt_tab_exit_db(XTThreadPtr self, XTDatabaseHPtr db) xt_free_sortedlist(self, db->db_table_paths); db->db_table_paths = NULL; } + if (db->db_error_list) { + xt_free_sortedlist(self, db->db_error_list); + db->db_error_list = NULL; + } } static void tab_check_table(XTThreadPtr self, XTTableHPtr XT_UNUSED(tab)) @@ -1713,6 +1777,116 @@ xtPublic void xt_drop_table(XTThreadPtr self, XTPathStrPtr tab_name, xtBool drop exit_(); } +xtPublic void xt_tab_check_free_lists(XTThreadPtr self, XTOpenTablePtr ot, bool check_recs, bool correct_count) +{ + char table_name[XT_IDENTIFIER_NAME_SIZE*3+3]; + register XTTableHPtr tab = ot->ot_table; + xtRowID prev_row_id; + xtRowID row_id; + xtRefID next_row_id; + u_llong free_count; + + xt_tab_make_table_name(tab, table_name, sizeof(table_name)); + if (check_recs) { + xtRecordID prev_rec_id; + xtRecordID rec_id; + XTTabRecExtDRec rec_buf; + + xt_lock_mutex_ns(&tab->tab_rec_lock); + /* Checking the free list: */ + prev_rec_id = 0; + free_count = 0; + rec_id = tab->tab_rec_free_id; + while (rec_id) { + if (rec_id >= tab->tab_rec_eof_id) { + xt_logf(XT_NT_ERROR, "Table %s: invalid reference on free list: %llu, ", table_name, (u_llong) rec_id); + if (prev_rec_id) + xt_logf(XT_NT_ERROR, "reference by: %llu\n", (u_llong) prev_rec_id); + else + xt_logf(XT_NT_ERROR, "reference by list head pointer\n"); + xt_tab_set_table_repair_pending(tab); + break; + } + if (!xt_tab_get_rec_data(ot, rec_id, XT_REC_FIX_HEADER_SIZE, (xtWord1 *) &rec_buf)) { + if (self) + xt_throw(self); + else + xt_log_and_clear_warning(ot->ot_thread); + break; + } + if ((rec_buf.tr_rec_type_1 & XT_TAB_STATUS_MASK) != XT_TAB_STATUS_FREED) + xt_logf(XT_NT_INFO, "Table %s: record, %llu, on free list is not free\n", table_name, (u_llong) rec_id); + free_count++; + prev_rec_id = rec_id; + rec_id = XT_GET_DISK_4(rec_buf.tr_prev_rec_id_4); + } + if (free_count != tab->tab_rec_fnum) { + if (correct_count) { + tab->tab_rec_fnum = free_count; + tab->tab_head_rec_fnum = free_count; + tab->tab_flush_pending = TRUE; + xt_logf(XT_NT_INFO, "Table %s: free record count (%llu) has been set to the number of records on the list: %llu\n", table_name, (u_llong) tab->tab_rec_fnum, (u_llong) free_count); + } + else + xt_logf(XT_NT_INFO, "Table %s: free record count (%llu) differs from the number of records on the list: %llu\n", table_name, (u_llong) tab->tab_rec_fnum, (u_llong) free_count); + } + xt_unlock_mutex_ns(&tab->tab_rec_lock); + } + + /* Check the row free list: */ + xt_lock_mutex_ns(&tab->tab_row_lock); + + prev_row_id = 0; + free_count = 0; + row_id = tab->tab_row_free_id; + while (row_id) { + if (row_id >= tab->tab_row_eof_id) { + xt_logf(XT_NT_ERROR, "Table %s: invalid reference on free row: %llu, ", table_name, (u_llong) row_id); + if (prev_row_id) + xt_logf(XT_NT_ERROR, "reference by: %llu\n", (u_llong) prev_row_id); + else + xt_logf(XT_NT_ERROR, "reference by list head pointer\n"); + xt_tab_set_table_repair_pending(tab); + break; + } + if (!tab->tab_rows.xt_tc_read_4(ot->ot_row_file, row_id, &next_row_id, ot->ot_thread)) { + if (self) + xt_throw(self); + else + xt_log_and_clear_warning(ot->ot_thread); + break; + } + free_count++; + prev_row_id = row_id; + row_id = next_row_id; + } + if (free_count != tab->tab_row_fnum) { + if (correct_count) { + /* tab_row_fnum is the current value, and tab_head_row_fnum is the value on + * disk. tab_head_row_fnum is set by the writer as the changes are applied + * to the database. + * + * This is the value then stored in the header of the file. This value + * is in sync with other changes to the file. + * + * So the fact that I am setting both value means this will not work at + * runtime, unless all changes have been applied by the writer. + * + * The correct way to do this at run time would be to add the change to the + * transaction log, so that it is applied by the writer. + */ + tab->tab_row_fnum = free_count; + tab->tab_head_row_fnum = free_count; + tab->tab_flush_pending = TRUE; + xt_logf(XT_NT_INFO, "Table %s: free row count (%llu) has been set to the number of rows on the list: %llu\n", table_name, (u_llong) tab->tab_row_fnum, (u_llong) free_count); + } + else + xt_logf(XT_NT_INFO, "Table %s: free row count (%llu) differs from the number of rows on the list: %llu\n", table_name, (u_llong) tab->tab_row_fnum, (u_llong) free_count); + } + + xt_unlock_mutex_ns(&tab->tab_row_lock); +} + /* * Record buffer size: * ------------------- @@ -2010,7 +2184,7 @@ xtPublic void xt_check_table(XTThreadPtr self, XTOpenTablePtr ot) prec_id = rec_id; rec_id = XT_GET_DISK_4(rec_buf->tr_prev_rec_id_4); } - if (free_count2 < free_rec_count) + if (free_count2 != free_rec_count) xt_logf(XT_INFO, "Table %s: not all free blocks (%llu) on free list: %llu\n", tab->tab_name, (u_llong) free_rec_count, (u_llong) free_count2); freer_(); // xt_unlock_mutex_ns(&tab->tab_rec_lock); @@ -2042,6 +2216,29 @@ xtPublic void xt_check_table(XTThreadPtr self, XTOpenTablePtr ot) rec_id++; } + prec_id = 0; + free_count2 = 0; + row_id = tab->tab_row_free_id; + while (row_id) { + if (row_id >= tab->tab_row_eof_id) { + xt_logf(XT_INFO, "Table %s: invalid reference on free row: %llu, ", tab->tab_name, (u_llong) row_id); + if (prec_id) + xt_logf(XT_INFO, "reference by: %llu\n", (u_llong) prec_id); + else + xt_logf(XT_INFO, "reference by list head pointer\n"); + break; + } + if (!tab->tab_rows.xt_tc_read_4(ot->ot_row_file, row_id, &ref_id, self)) { + xt_log_and_clear_exception(self); + break; + } + free_count2++; + prec_id = row_id; + row_id = ref_id; + } + if (free_count2 != tab->tab_row_fnum) + xt_logf(XT_INFO, "Table %s: free row count (%llu) differs from the number of row on the list: %llu\n", tab->tab_name, (u_llong) tab->tab_row_fnum, (u_llong) free_count2); + freer_(); // xt_unlock_mutex(&tab->tab_row_lock); #ifdef CHECK_INDEX_ON_CHECK_TABLE @@ -3117,10 +3314,18 @@ static int tab_visible(register XTOpenTablePtr ot, XTTabRecHeadDPtr rec_head, xt #endif break; case XT_XN_REREAD: + /* {RETRY-READ} + * TODO: This is not as "correct" as it could be. + * Such records should be considered to be aborted, + * and removed from the list. + */ if (invalid_rec != var_rec_id) { invalid_rec = var_rec_id; goto retry_3; } + if (!tab_record_corrupt(ot, row_id, var_rec_id, true, 1)) + goto failed; + /* Assume end of list. */ #ifdef XT_CRASH_DEBUG /* Should not happen! */ @@ -3308,6 +3513,8 @@ xtPublic int xt_tab_visible(XTOpenTablePtr ot) /* Avoid infinite loop: */ if (read_again) { /* Should not happen! */ + if (!tab_record_corrupt(ot, row_id, ot->ot_curr_rec_id, true, 2)) + return XT_ERR; #ifdef XT_CRASH_DEBUG /* Generate a core dump! */ xt_crash_me(); @@ -3364,6 +3571,8 @@ xtPublic int xt_tab_read_record(register XTOpenTablePtr ot, xtWord1 *buffer) /* Avoid infinite loop: */ if (read_again) { /* Should not happen! */ + if (!tab_record_corrupt(ot, XT_GET_DISK_4(((XTTabRecHeadDPtr) ot->ot_row_rbuffer)->tr_row_id_4), ot->ot_curr_rec_id, true, 3)) + return XT_ERR; #ifdef XT_CRASH_DEBUG /* Generate a core dump! */ xt_crash_me(); @@ -3580,6 +3789,7 @@ xtPublic xtBool xt_tab_free_row(XTOpenTablePtr ot, XTTableHPtr tab, xtRowID row_ } tab->tab_row_free_id = row_id; tab->tab_row_fnum++; + ASSERT_NS(tab->tab_row_fnum < tab->tab_row_eof_id); xt_unlock_mutex_ns(&tab->tab_row_lock); if (!xt_xlog_modify_table(tab->tab_id, XT_LOG_ENT_ROW_FREED, op_seq, 0, row_id, sizeof(XTTabRowRefDRec), (xtWord1 *) &free_row, ot->ot_thread)) @@ -3776,7 +3986,7 @@ xtPublic int xt_tab_remove_record(XTOpenTablePtr ot, xtRecordID rec_id, xtWord1 xt_lock_mutex_ns(&tab->tab_db->db_co_ext_lock); if (!xt_tab_get_rec_data(ot, rec_id, XT_REC_EXT_HEADER_SIZE, ot->ot_row_rbuffer)) { xt_unlock_mutex_ns(&tab->tab_db->db_co_ext_lock); - return FAILED; + return XT_ERR; } xt_unlock_mutex_ns(&tab->tab_db->db_co_ext_lock); @@ -3824,7 +4034,7 @@ xtPublic int xt_tab_remove_record(XTOpenTablePtr ot, xtRecordID rec_id, xtWord1 XT_SET_DISK_4(free_rec->rf_next_rec_id_4, prev_rec_id); if (!xt_tab_put_rec_data(ot, rec_id, sizeof(XTTabRecFreeDRec), ot->ot_row_rbuffer, &op_seq)) { xt_unlock_mutex_ns(&tab->tab_rec_lock); - return FAILED; + return XT_ERR; } tab->tab_rec_free_id = rec_id; ASSERT_NS(tab->tab_rec_free_id < tab->tab_rec_eof_id); @@ -3832,7 +4042,9 @@ xtPublic int xt_tab_remove_record(XTOpenTablePtr ot, xtRecordID rec_id, xtWord1 xt_unlock_mutex_ns(&tab->tab_rec_lock); free_rec->rf_rec_type_1 = old_rec_type; - return xt_xlog_modify_table(tab->tab_id, XT_LOG_ENT_REC_REMOVED_BI, op_seq, (xtRecordID) new_rec_type, rec_id, rec_size, ot->ot_row_rbuffer, ot->ot_thread); + if (!xt_xlog_modify_table(tab->tab_id, XT_LOG_ENT_REC_REMOVED_BI, op_seq, (xtRecordID) new_rec_type, rec_id, rec_size, ot->ot_row_rbuffer, ot->ot_thread)) + return XT_ERR; + return OK; } static xtRowID tab_new_row(XTOpenTablePtr ot, XTTableHPtr tab) @@ -3851,6 +4063,7 @@ static xtRowID tab_new_row(XTOpenTablePtr ot, XTTableHPtr tab) return 0; } tab->tab_row_free_id = next_row_id; + ASSERT_NS(tab->tab_row_fnum > 0); tab->tab_row_fnum--; } else { @@ -4170,9 +4383,12 @@ static xtBool tab_wait_for_rollback(XTOpenTablePtr ot, xtRowID row_id, xtRecordI return FAILED; if (XT_REC_IS_CLEAN(var_head.tr_rec_type_1)) goto locked; - if (XT_REC_IS_FREE(var_head.tr_rec_type_1)) + if (XT_REC_IS_FREE(var_head.tr_rec_type_1)) { /* Should not happen: */ + if (!tab_record_corrupt(ot, row_id, var_rec_id, false, 4)) + return FAILED; goto record_invalid; + } xn_id = XT_GET_DISK_4(var_head.tr_xact_id_4); switch (xt_xn_status(ot, xn_id, var_rec_id)) { case XT_XN_VISIBLE: @@ -4195,6 +4411,8 @@ static xtBool tab_wait_for_rollback(XTOpenTablePtr ot, xtRowID row_id, xtRecordI XT_TAB_ROW_WRITE_LOCK(&tab->tab_row_rwlock[row_id % XT_ROW_RWLOCKS], ot->ot_thread); goto retry; case XT_XN_REREAD: + if (!tab_record_corrupt(ot, row_id, var_rec_id, true, 5)) + return FAILED; goto record_invalid; } var_rec_id = XT_GET_DISK_4(var_head.tr_prev_rec_id_4); @@ -4206,9 +4424,10 @@ static xtBool tab_wait_for_rollback(XTOpenTablePtr ot, xtRowID row_id, xtRecordI return FAILED; record_invalid: + /* {RETRY-READ} */ /* Prevent an infinite loop due to a bad record: */ if (invalid_rec != var_rec_id) { - var_rec_id = invalid_rec; + invalid_rec = var_rec_id; goto retry; } /* The record is invalid, it will be "overwritten"... */ @@ -4280,9 +4499,12 @@ xtPublic int xt_tab_maybe_committed(XTOpenTablePtr ot, xtRecordID rec_id, xtXact #ifdef TRACE_VARIATIONS_IN_DUP_CHECK t_type="Re-read"; #endif + /* {RETRY-READ} */ /* Avoid infinite loop: */ if (invalid_rec == rec_id) { /* Should not happen! */ + if (!tab_record_corrupt(ot, XT_GET_DISK_4(rec_head.tr_row_id_4), rec_id, true, 6)) + goto failed; #ifdef XT_CRASH_DEBUG /* Generate a core dump! */ xt_crash_me(); @@ -4327,7 +4549,7 @@ xtPublic int xt_tab_maybe_committed(XTOpenTablePtr ot, xtRecordID rec_id, xtXact if (XT_REC_IS_FREE(rec_head.tr_rec_type_1)) { /* Should not happen: */ if (invalid_rec != var_rec_id) { - var_rec_id = invalid_rec; + invalid_rec = var_rec_id; goto retry; } /* Assume end of list. */ @@ -4364,11 +4586,14 @@ xtPublic int xt_tab_maybe_committed(XTOpenTablePtr ot, xtRecordID rec_id, xtXact } break; case XT_XN_REREAD: + /* {RETRY-READ} */ if (invalid_rec != var_rec_id) { - var_rec_id = invalid_rec; + invalid_rec = var_rec_id; goto retry; } /* Assume end of list. */ + if (!tab_record_corrupt(ot, row_id, invalid_rec, true, 7)) + goto failed; #ifdef XT_CRASH_DEBUG /* Should not happen! */ xt_crash_me(); @@ -5068,6 +5293,8 @@ xtPublic xtBool xt_tab_seq_next(XTOpenTablePtr ot, xtWord1 *buffer, xtBool *eof) ot->ot_on_page = FALSE; goto next_page; } + if (!tab_record_corrupt(ot, XT_GET_DISK_4(((XTTabRecHeadDPtr) buff_ptr)->tr_row_id_4), invalid_rec, true, 8)) + return XT_ERR; #ifdef XT_CRASH_DEBUG /* Should not happen! */ xt_crash_me(); @@ -5240,7 +5467,7 @@ static xtBool tab_exec_repair_pending(XTDatabaseHPtr db, int what, char *table_n return FALSE; } -static void tab_make_table_name(XTTableHPtr tab, char *table_name, size_t size) +xtPublic void xt_tab_make_table_name(XTTableHPtr tab, char *table_name, size_t size) { char *nptr; @@ -5316,7 +5543,7 @@ xtPublic xtBool xt_tab_is_table_repair_pending(XTTableHPtr tab) { char table_name[XT_IDENTIFIER_NAME_SIZE*3+3]; - tab_make_table_name(tab, table_name, sizeof(table_name)); + xt_tab_make_table_name(tab, table_name, sizeof(table_name)); return tab_exec_repair_pending(tab->tab_db, REP_FIND, table_name); } @@ -5326,7 +5553,7 @@ xtPublic void xt_tab_table_repaired(XTTableHPtr tab) char table_name[XT_IDENTIFIER_NAME_SIZE*3+3]; tab->tab_repair_pending = FALSE; - tab_make_table_name(tab, table_name, sizeof(table_name)); + xt_tab_make_table_name(tab, table_name, sizeof(table_name)); tab_exec_repair_pending(tab->tab_db, REP_DEL, table_name); } } @@ -5337,7 +5564,7 @@ xtPublic void xt_tab_set_table_repair_pending(XTTableHPtr tab) char table_name[XT_IDENTIFIER_NAME_SIZE*3+3]; tab->tab_repair_pending = TRUE; - tab_make_table_name(tab, table_name, sizeof(table_name)); + xt_tab_make_table_name(tab, table_name, sizeof(table_name)); tab_exec_repair_pending(tab->tab_db, REP_ADD, table_name); } } diff --git a/storage/pbxt/src/table_xt.h b/storage/pbxt/src/table_xt.h index 83f2168dd6e..f6c32587419 100644 --- a/storage/pbxt/src/table_xt.h +++ b/storage/pbxt/src/table_xt.h @@ -507,6 +507,7 @@ int xt_tab_compare_names(const char *n1, const char *n2); int xt_tab_compare_paths(char *n1, char *n2); void xt_tab_init_db(struct XTThread *self, struct XTDatabase *db); void xt_tab_exit_db(struct XTThread *self, struct XTDatabase *db); +void xt_tab_check_free_lists(struct XTThread *self, XTOpenTablePtr ot, bool check_recs, bool correct_count); void xt_check_tables(struct XTThread *self); char *xt_tab_file_to_name(size_t size, char *tab_name, char *file_name); @@ -572,6 +573,7 @@ xtBool xt_tab_get_rec_data(register XTOpenTablePtr ot, xtRecordID rec_id, siz void xt_tab_disable_index(XTTableHPtr tab, u_int ind_error); void xt_tab_set_index_error(XTTableHPtr tab); +void xt_tab_make_table_name(XTTableHPtr tab, char *table_name, size_t size); xtBool xt_tab_is_table_repair_pending(XTTableHPtr tab); void xt_tab_table_repaired(XTTableHPtr tab); void xt_tab_set_table_repair_pending(XTTableHPtr tab); diff --git a/storage/pbxt/src/thread_xt.cc b/storage/pbxt/src/thread_xt.cc index ac42896d22f..52c2c6c29c5 100644 --- a/storage/pbxt/src/thread_xt.cc +++ b/storage/pbxt/src/thread_xt.cc @@ -224,11 +224,16 @@ static void thr_log_va(XTThreadPtr self, c_char *func, c_char *file, u_int line, #else /* Use the buffer, unless it is too small */ va_list ap2; + int bufsize; va_copy(ap2, ap); - if (vsnprintf(buffer, DEFAULT_LOG_BUFFER_SIZE, fmt, ap) >= DEFAULT_LOG_BUFFER_SIZE) { - if (vasprintf(&log_string, fmt, ap2) == -1) + bufsize = vsnprintf(buffer, DEFAULT_LOG_BUFFER_SIZE, fmt, ap); + if (bufsize >= DEFAULT_LOG_BUFFER_SIZE) { + log_string = (char *) malloc(bufsize + 1); + if (vsnprintf(log_string, bufsize + 1, fmt, ap2) > bufsize) { + free(log_string); log_string = NULL; + } } else log_string = buffer; diff --git a/storage/pbxt/src/xaction_xt.cc b/storage/pbxt/src/xaction_xt.cc index 7281eafd8db..48abc5d2b66 100644 --- a/storage/pbxt/src/xaction_xt.cc +++ b/storage/pbxt/src/xaction_xt.cc @@ -1558,6 +1558,8 @@ xtPublic int xt_xn_status(XTOpenTablePtr ot, xtXactID xn_id, xtRecordID XT_UNUSE * Because we are only here because the record was valid but not * clean (you can confirm this by looking at the code that * calls this function). + * + * See {RETRY-READ} */ return XT_XN_REREAD; } @@ -1743,7 +1745,7 @@ xtPublic xtWord8 xt_xn_bytes_to_sweep(XTDatabaseHPtr db, XTThreadPtr thread) } else { xn_log_id = x_log_id; - x_log_offset = x_log_offset; + xn_log_offset = x_log_offset; } } xn_id++; diff --git a/storage/pbxt/src/xt_defs.h b/storage/pbxt/src/xt_defs.h index 4a03f941cf8..3c77415265c 100644 --- a/storage/pbxt/src/xt_defs.h +++ b/storage/pbxt/src/xt_defs.h @@ -397,6 +397,24 @@ typedef struct XTPathStr { */ #define XT_XLOG_FLUSH_FREQ 1000 +/* + * Define here if you want to check (and correct) the table free list + * counts. The free list counts are not durable, because they are not + * written to the log. + * + * The row free count is most critical because it can be used to + * estimate the the of rows in the record. + */ +#define XT_CHECK_ROW_FREE_COUNT +#ifdef DEBUG +#define XT_CHECK_RECORD_FREE_COUNT +#endif +#define XT_CORRECT_TABLE_FREE_COUNT + +#if defined(XT_CHECK_ROW_FREE_COUNT) && defined(XT_CORRECT_TABLE_FREE_COUNT) +#define XT_ROW_COUNT_CORRECTED +#endif + /* ---------------------------------------------------------------------- * GLOBAL CONSTANTS */ @@ -873,7 +891,11 @@ extern "C" void session_mark_transaction_to_rollback(Session *session, bool all) #define MX_ULONGLONG_T ulonglong #define MX_LONGLONG_T longlong #define MX_CHARSET_INFO CHARSET_INFO -#define MX_CONST_CHARSET_INFO struct charset_info_st +#if defined(MARIADB_BASE_VERSION) && MYSQL_VERSION_ID > 50200 +#define MX_CONST_CHARSET_INFO const struct charset_info_st +#else +#define MX_CONST_CHARSET_INFO struct charset_info_st +#endif #define MX_CONST #define MX_BITMAP MY_BITMAP #define MX_BIT_SIZE() n_bits |