diff --git a/include/mysql_com.h b/include/mysql_com.h index 7240b69968f..32942d77671 100644 --- a/include/mysql_com.h +++ b/include/mysql_com.h @@ -198,7 +198,7 @@ enum enum_indicator_type #define VERS_SYS_END_FLAG (1 << 28) /* autogenerated column declared with `generated always as row end` (see II.a SQL Standard).*/ -#define VERS_OPTIMIZED_UPDATE_FLAG (1 << 29) /* column that doesn't support +#define VERS_UPDATE_UNVERSIONED_FLAG (1 << 29) /* column that doesn't support system versioning when table itself supports it*/ #define HIDDEN_FLAG (1 << 31) /* hide from SELECT * */ diff --git a/sql/field.h b/sql/field.h index b9b9e052ec6..fe9c162ef8d 100644 --- a/sql/field.h +++ b/sql/field.h @@ -1455,6 +1455,11 @@ public: return flags & (VERS_SYS_START_FLAG | VERS_SYS_END_FLAG); } + bool vers_update_unversioned() const + { + return flags & VERS_UPDATE_UNVERSIONED_FLAG; + } + virtual bool vers_trx_id() const { return false; diff --git a/sql/handler.cc b/sql/handler.cc index 97ade27d933..d2d7b09baae 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -6859,7 +6859,7 @@ bool Vers_parse_info::check_and_fix_implicit( !with_system_versioning) || f->versioning == Column_definition::WITHOUT_VERSIONING) { - f->flags|= VERS_OPTIMIZED_UPDATE_FLAG; + f->flags|= VERS_UPDATE_UNVERSIONED_FLAG; } } @@ -6999,7 +6999,7 @@ bool Vers_parse_info::check_and_fix_alter(THD *thd, Alter_info *alter_info, while (Create_field *f= it++) { if (f->versioning == Column_definition::WITHOUT_VERSIONING) - f->flags|= VERS_OPTIMIZED_UPDATE_FLAG; + f->flags|= VERS_UPDATE_UNVERSIONED_FLAG; if (f->change.str && (start == f->change || end == f->change)) { diff --git a/sql/item.cc b/sql/item.cc index 0a5886d2fcf..25d57105654 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -10384,7 +10384,7 @@ Item *Item_field::vers_optimized_fields_transformer(THD *thd, uchar *) if (!field) return this; - if (field->flags & VERS_OPTIMIZED_UPDATE_FLAG && context && + if (field->vers_update_unversioned() && context && field->table->pos_in_table_list && field->table->pos_in_table_list->vers_conditions) { diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 654e482a860..76875836462 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -710,12 +710,6 @@ extern "C" void thd_kill_timeout(THD* thd) mysql_mutex_unlock(&thd->LOCK_thd_data); } -Time_zone * thd_get_timezone(THD * thd) -{ - DBUG_ASSERT(thd && thd->variables.time_zone); - return thd->variables.time_zone; -} - void thd_vers_update_trt(THD * thd, bool value) { thd->vers_update_trt= value; diff --git a/sql/sql_class.h b/sql/sql_class.h index 1217bcc20d7..6fda5c624d6 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -155,6 +155,7 @@ extern bool volatile shutdown_in_progress; extern "C" LEX_STRING * thd_query_string (MYSQL_THD thd); extern "C" size_t thd_query_safe(MYSQL_THD thd, char *buf, size_t buflen); +void thd_vers_update_trt(THD *thd, bool value); /** @class CSET_STRING @@ -4564,6 +4565,8 @@ public: /* Handling of timeouts for commands */ thr_timer_t query_timer; + // Storage engine may set this to true is we want to write a row to + // transaction_registry table on transaction commit. bool vers_update_trt; public: diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 838649a6398..62bf4dad684 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -16743,7 +16743,7 @@ Field *create_tmp_field_from_field(THD *thd, Field *org_field, HIDDEN_FLAG | VERS_SYS_START_FLAG | VERS_SYS_END_FLAG | - VERS_OPTIMIZED_UPDATE_FLAG)); + VERS_UPDATE_UNVERSIONED_FLAG)); if (org_field->maybe_null() || (item && item->maybe_null)) new_field->flags&= ~NOT_NULL_FLAG; // Because of outer join if (org_field->type() == MYSQL_TYPE_VAR_STRING || diff --git a/sql/sql_show.cc b/sql/sql_show.cc index e6123da93fb..3985bf1d7e4 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -2188,7 +2188,7 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, packet->append(def_value.ptr(), def_value.length(), system_charset_info); } - if (field->flags & VERS_OPTIMIZED_UPDATE_FLAG) + if (field->vers_update_unversioned()) { packet->append(STRING_WITH_LEN(" WITHOUT SYSTEM VERSIONING")); } diff --git a/sql/sql_update.cc b/sql/sql_update.cc index cec410a0e62..6dbce3bd883 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -161,7 +161,7 @@ static bool check_has_vers_fields(List &items) while (Item *item= it++) { if (Item_field *item_field= item->field_for_view_update()) - if (!(item_field->field->flags & VERS_OPTIMIZED_UPDATE_FLAG)) + if (!item_field->field->vers_update_unversioned()) return true; } return false; diff --git a/sql/table.cc b/sql/table.cc index f5a031a1cda..fe169357d0c 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -2051,7 +2051,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, { uchar flags= *extra2_field_flags++; if (flags & VERS_OPTIMIZED_UPDATE) - reg_field->flags|= VERS_OPTIMIZED_UPDATE_FLAG; + reg_field->flags|= VERS_UPDATE_UNVERSIONED_FLAG; if (flags & HIDDEN) reg_field->flags|= HIDDEN_FLAG; } @@ -8527,7 +8527,8 @@ void TR_table::store(uint field_id, timeval ts) void TR_table::store_data(ulonglong trx_id, ulonglong commit_id, timeval commit_ts) { - timeval start_time= {thd->start_time, thd->start_time_sec_part}; + timeval start_time= {static_cast(thd->start_time), + static_cast(thd->start_time_sec_part)}; store(FLD_TRX_ID, trx_id); store(FLD_COMMIT_ID, commit_id); store(FLD_BEGIN_TS, start_time); diff --git a/sql/table.h b/sql/table.h index 33278b51a64..37232e876ea 100644 --- a/sql/table.h +++ b/sql/table.h @@ -2930,14 +2930,14 @@ inline void mark_as_null_row(TABLE *table) bool is_simple_order(ORDER *order); +class Open_tables_backup; + /** Transaction Registry Table (TRT) This table holds transaction IDs, their corresponding times and other transaction-related data which is used for transaction order resolution. When versioned table marks its records lifetime with transaction IDs, TRT is used to get their actual timestamps. */ - -class Open_tables_backup; class TR_table: public TABLE_LIST { THD *thd; @@ -2952,33 +2952,111 @@ public: FLD_ISO_LEVEL, FIELD_COUNT }; + /** + @param[in,out] Thread handle + @param[in] Current transaction is read-write. + */ TR_table(THD *_thd, bool rw= false); + /** + Opens a transaction_registry table. + + @retval true on error, false otherwise. + */ bool open(); ~TR_table(); + /** + @retval current thd + */ THD *get_thd() const { return thd; } + /** + Stores value to internal transaction_registry TABLE object. + + @param[in] field number in a TABLE + @param[in] value to store + */ void store(uint field_id, ulonglong val); + /** + Stores value to internal transaction_registry TABLE object. + + @param[in] field number in a TABLE + @param[in] value to store + */ void store(uint field_id, timeval ts); + /** + Stores value to internal transaction_registry TABLE object. + + @param[in] current (InnoDB) transaction id + @param[in] InnoDB transaction counter at the time of transaction commit + @param[in] transaction commit timestamp + */ void store_data(ulonglong trx_id, ulonglong commit_id, timeval commit_ts); + /** + Writes a row from internal TABLE object to transaction_registry table. + + @retval true on error, false otherwise. + */ bool update(); - // return true if found; false if not found or error + /** + Checks whether a row with specified transaction_id exists in a + transaction_registry table. + + @param[in] transacton_id value + @retval true if exists, false it not exists or an error occured + */ bool query(ulonglong trx_id); + /** + Gets a row from transaction_registry with the closest commit_timestamp to + first argument. We can search for a value which a lesser or greater than + first argument. Also loads a row into an internal TABLE object. + + @param[in] timestamp + @param[in] true if we search for a lesser timestamp, false if greater + @retval true if exists, false it not exists or an error occured + */ bool query(MYSQL_TIME &commit_time, bool backwards); - // return true if error + /** + Checks whether transaction1 sees transaction0. + + @param[out] true if transaction1 sees transaction0, undefined on error and + when transaction1=transaction0 and false otherwise + @param[in] transaction_id of transaction1 + @param[in] transaction_id of transaction0 + @param[in] commit time of transaction1 or 0 if we want it to be queried + @param[in] isolation level (from handler.h) of transaction1 + @param[in] commit time of transaction0 or 0 if we want it to be queried + @retval true on error, false otherwise + */ bool query_sees(bool &result, ulonglong trx_id1, ulonglong trx_id0, - ulonglong commit_id1= 0, enum_tx_isolation iso_level1= ISO_READ_UNCOMMITTED, + ulonglong commit_id1= 0, + enum_tx_isolation iso_level1= ISO_READ_UNCOMMITTED, ulonglong commit_id0= 0); + /** + @retval transaction isolation level of a row from internal TABLE object. + */ enum_tx_isolation iso_level() const; + /** + Stores transactioin isolation level to internal TABLE object. + */ void store_iso_level(enum_tx_isolation iso_level) { DBUG_ASSERT(iso_level <= ISO_SERIALIZABLE); store(FLD_ISO_LEVEL, iso_level + 1); } + /** + Writes a message to MariaDB log about incorrect transaction_registry schema. + + @param[in] a message explained what's incorrect in schema + */ void warn_schema_incorrect(const char *reason); + /** + Checks whether transaction_registry table has a correct schema. + + @retval true if schema is incorrect and false otherwise + */ bool check(); -public: TABLE * operator-> () const { return table; @@ -2992,13 +3070,13 @@ public: { return table; } - bool operator== (TABLE_LIST &subj) const + bool operator== (const TABLE_LIST &subj) const { if (0 != strcmp(db, subj.db)) return false; return (0 == strcmp(table_name, subj.table_name)); } - bool operator!= (TABLE_LIST &subj) const + bool operator!= (const TABLE_LIST &subj) const { return !(*this == subj); } diff --git a/sql/tztime.h b/sql/tztime.h index d3f19fa2fd3..7ffc36011e1 100644 --- a/sql/tztime.h +++ b/sql/tztime.h @@ -89,7 +89,5 @@ extern my_time_t sec_since_epoch_TIME(MYSQL_TIME *t); static const int MY_TZ_TABLES_COUNT= 4; -extern Time_zone* thd_get_timezone(THD* thd); - #endif /* !defined(TESTTIME) && !defined(TZINFO2SQL) */ #endif /* TZTIME_INCLUDED */ diff --git a/sql/unireg.cc b/sql/unireg.cc index e8d94741800..5da14e31243 100644 --- a/sql/unireg.cc +++ b/sql/unireg.cc @@ -121,7 +121,7 @@ bool has_extra2_field_flags(List &create_fields) List_iterator it(create_fields); while (Create_field *f= it++) { - if (f->flags & (VERS_OPTIMIZED_UPDATE_FLAG | HIDDEN_FLAG)) + if (f->flags & (VERS_UPDATE_UNVERSIONED_FLAG | HIDDEN_FLAG)) return true; } return false; @@ -356,7 +356,7 @@ LEX_CUSTRING build_frm_image(THD *thd, const char *table, while (Create_field *field= it++) { uchar flags= 0; - if (field->flags & VERS_OPTIMIZED_UPDATE_FLAG) + if (field->flags & VERS_UPDATE_UNVERSIONED_FLAG) flags|= VERS_OPTIMIZED_UPDATE; if (field->flags & HIDDEN_FLAG) flags|= HIDDEN; diff --git a/storage/innobase/data/data0data.cc b/storage/innobase/data/data0data.cc index 6601edfec9d..fbdb409dbc5 100644 --- a/storage/innobase/data/data0data.cc +++ b/storage/innobase/data/data0data.cc @@ -861,3 +861,17 @@ dfield_t::clone(mem_heap_t* heap) const return(obj); } + +/** Assuming field is sys_trx_end checks whether its value is not SYS_TRX_MAX. +@param dfield field to check +@return true for historical rows and false otherwise*/ +bool +dfield_is_historical_sys_trx_end(const dfield_t* dfield) +{ + static const trx_id_t MAX = TRX_ID_MAX; + ut_ad(dfield); + ut_ad(dfield->type.prtype & DATA_VERS_END); + const byte* data = static_cast(dfield_get_data(dfield)); + ut_ad(dfield_get_len(dfield) == 8); + return(memcmp(data, &MAX, 8)); +} diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 0da1fd603b4..b2be5e2f1b6 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -3623,10 +3623,9 @@ static const char* ha_innobase_exts[] = { NullS }; -void innodb_get_trt_data(TR_table &trt) -{ - THD *thd = trt.get_thd(); - trx_t *trx = thd_to_trx(thd); +void innodb_get_trt_data(TR_table& trt) { + THD* thd = trt.get_thd(); + trx_t* trx = thd_to_trx(thd); ut_a(trx); ut_a(trx->vers_update_trt); mutex_enter(&trx_sys->mutex); @@ -3637,8 +3636,7 @@ void innodb_get_trt_data(TR_table &trt) mutex_exit(&trx_sys->mutex); // silent downgrade cast warning on win64 - timeval commit_ts = {static_cast(sec), - static_cast(usec)}; + timeval commit_ts = {static_cast(sec), static_cast(usec)}; trt.store_data(trx->id, commit_id, commit_ts); trx->vers_update_trt = false; } @@ -6521,6 +6519,10 @@ no_such_table: } } + if (table && m_prebuilt->table) { + ut_ad(table->versioned() == m_prebuilt->table->versioned()); + } + info(HA_STATUS_NO_LOCK | HA_STATUS_VARIABLE | HA_STATUS_CONST); DBUG_RETURN(0); } @@ -8365,17 +8367,18 @@ no_commit: innobase_srv_conc_enter_innodb(m_prebuilt); - vers_set_fields = (table->versioned_write() && - (sql_command != SQLCOM_CREATE_TABLE || table->s->vtmd)) - ? - ROW_INS_VERSIONED : - ROW_INS_NORMAL; + vers_set_fields = + (table->versioned_write() + && (sql_command != SQLCOM_CREATE_TABLE || table->s->vtmd)) + ? ROW_INS_VERSIONED + : ROW_INS_NORMAL; /* Step-5: Execute insert graph that will result in actual insert. */ error = row_insert_for_mysql((byte*) record, m_prebuilt, vers_set_fields); - if (m_prebuilt->trx->vers_update_trt) + if (m_prebuilt->trx->vers_update_trt) { thd_vers_update_trt(m_user_thd, true); + } DEBUG_SYNC(m_user_thd, "ib_after_row_insert"); @@ -8626,6 +8629,7 @@ calc_row_difference( doc_id_t doc_id = FTS_NULL_DOC_ID; ulint num_v = 0; uint n_fields = mysql_fields(table); + bool table_versioned = prebuilt->table->versioned(); ut_ad(!srv_read_only_mode); @@ -8876,9 +8880,8 @@ calc_row_difference( } n_changed++; - if (!prebuilt->upd_node->versioned && - prebuilt->table->versioned() && - !(field->flags & VERS_OPTIMIZED_UPDATE_FLAG)) { + if (table_versioned + && !field->vers_update_unversioned()) { prebuilt->upd_node->versioned = true; } @@ -8987,9 +8990,7 @@ calc_row_difference( ++n_changed; - if (!prebuilt->upd_node->versioned && - prebuilt->table->versioned() && - !(field->flags & VERS_OPTIMIZED_UPDATE_FLAG)) { + if (table_versioned && !field->vers_update_unversioned()) { prebuilt->upd_node->versioned = true; } } else { @@ -9175,13 +9176,14 @@ ha_innobase::update_row( innobase_srv_conc_enter_innodb(m_prebuilt); - if (!table->versioned_write()) + if (!table->versioned_write()) { m_prebuilt->upd_node->versioned = false; + } if (m_prebuilt->upd_node->versioned) { vers_set_fields = true; - if (thd_sql_command(m_user_thd) == SQLCOM_ALTER_TABLE && !table->s->vtmd) - { + if (thd_sql_command(m_user_thd) == SQLCOM_ALTER_TABLE + && !table->s->vtmd) { m_prebuilt->upd_node->vers_delete = true; } else { m_prebuilt->upd_node->vers_delete = false; @@ -9192,12 +9194,14 @@ ha_innobase::update_row( error = row_update_for_mysql(m_prebuilt, vers_set_fields); if (error == DB_SUCCESS && vers_ins_row) { - if (trx->id != static_cast(table->vers_start_field()->val_int())) + if (trx->id != static_cast(table->vers_start_field()->val_int())) { error = row_insert_for_mysql((byte*) old_row, m_prebuilt, ROW_INS_HISTORICAL); + } } - if (m_prebuilt->trx->vers_update_trt) + if (m_prebuilt->trx->vers_update_trt) { thd_vers_update_trt(m_user_thd, true); + } if (error == DB_SUCCESS && autoinc) { /* A value for an AUTO_INCREMENT column @@ -9313,13 +9317,13 @@ ha_innobase::delete_row( innobase_srv_conc_enter_innodb(m_prebuilt); bool vers_set_fields = - table->versioned_write() && - table->vers_end_field()->is_max(); + table->versioned_write() && table->vers_end_field()->is_max(); error = row_update_for_mysql(m_prebuilt, vers_set_fields); - if (m_prebuilt->trx->vers_update_trt) + if (m_prebuilt->trx->vers_update_trt) { thd_vers_update_trt(m_user_thd, true); + } innobase_srv_conc_exit_innodb(m_prebuilt); @@ -11407,6 +11411,7 @@ create_table_info_t::create_table_def() for (i = 0; i < n_cols; i++) { ulint is_virtual; bool is_stored = false; + Field* field = m_form->field[i]; ulint vers_row_start = 0; ulint vers_row_end = 0; diff --git a/storage/innobase/handler/ha_innodb.h b/storage/innobase/handler/ha_innodb.h index 548cec11931..d7f5d36a680 100644 --- a/storage/innobase/handler/ha_innodb.h +++ b/storage/innobase/handler/ha_innodb.h @@ -586,8 +586,6 @@ bool thd_is_strict_mode(const MYSQL_THD thd); */ extern void mysql_bin_log_commit_pos(THD *thd, ulonglong *out_pos, const char **out_file); -extern void thd_vers_update_trt(THD * thd, bool value); - /** Get the partition_info working copy. @param thd Thread object. @return NULL or pointer to partition_info working copy. */ diff --git a/storage/innobase/handler/handler0alter.cc b/storage/innobase/handler/handler0alter.cc index 5ef48b5735a..64ef4a33504 100644 --- a/storage/innobase/handler/handler0alter.cc +++ b/storage/innobase/handler/handler0alter.cc @@ -631,9 +631,10 @@ instant_alter_column_possible( const Alter_inplace_info* ha_alter_info, const TABLE* table) { - if (ha_alter_info->create_info->vers_info.with_system_versioning) + // Making table system-versioned instantly is not implemented yet. + if (ha_alter_info->create_info->vers_info.with_system_versioning) { return false; - + } if (~ha_alter_info->handler_flags & Alter_inplace_info::ADD_STORED_BASE_COLUMN) { @@ -7086,8 +7087,9 @@ ok_exit: ctx->m_stage, add_v, eval_table, ha_alter_info->handler_flags & Alter_inplace_info::ALTER_DROP_HISTORICAL); - if (m_prebuilt->trx->vers_update_trt) + if (m_prebuilt->trx->vers_update_trt) { thd_vers_update_trt(m_user_thd, true); + } #ifndef DBUG_OFF oom: @@ -9753,7 +9755,6 @@ foreign_fail: DBUG_RETURN(false); } - /** @param thd the session @param start_value the lower bound diff --git a/storage/innobase/include/data0data.h b/storage/innobase/include/data0data.h index a0b3059ad40..30ad9a23260 100644 --- a/storage/innobase/include/data0data.h +++ b/storage/innobase/include/data0data.h @@ -508,6 +508,12 @@ dtuple_print( const dtuple_t* tuple) /*!< in: tuple */ MY_ATTRIBUTE((nonnull)); +/** Assuming field is sys_trx_end checks whether its value is not SYS_TRX_MAX. +@param dfield field to check +@return true for historical rows and false otherwise*/ +bool +dfield_is_historical_sys_trx_end(const dfield_t* dfield); + /** Print the contents of a tuple. @param[out] o output stream @param[in] field array of data fields diff --git a/storage/innobase/include/row0ins.h b/storage/innobase/include/row0ins.h index ea3883a5d25..21e5cd7e9ea 100644 --- a/storage/innobase/include/row0ins.h +++ b/storage/innobase/include/row0ins.h @@ -229,39 +229,4 @@ struct ins_node_t{ #define INS_NODE_ALLOC_ROW_ID 2 /* row id should be allocated */ #define INS_NODE_INSERT_ENTRIES 3 /* index entries should be built and inserted */ - -UNIV_INLINE -void row_ins_set_tuple_col_8( - dtuple_t* tuple, - int col, - ib_uint64_t data, - byte* buf) -{ - static const ulint fsize = sizeof(data); - dfield_t* dfield = dtuple_get_nth_field(tuple, col); - ut_ad(dfield->type.len == fsize); - if (dfield->len == UNIV_SQL_NULL) { - dfield_set_data(dfield, buf, fsize); - } - ut_ad(dfield->len == dfield->type.len && dfield->data); - mach_write_to_8(dfield->data, data); -} - -UNIV_INLINE -void row_ins_set_tuple_col_8( - dtuple_t* tuple, - int col, - timeval& data, - byte* buf) -{ - dfield_t* dfield = dtuple_get_nth_field(tuple, col); - ut_ad(dfield->type.len == 8); - if (dfield->len == UNIV_SQL_NULL) { - dfield_set_data(dfield, buf, 8); - } - ut_ad(dfield->len == dfield->type.len && dfield->data); - mach_write_to_4(reinterpret_cast(dfield->data), (ulint) data.tv_sec); - mach_write_to_4(reinterpret_cast(dfield->data) + 4, (ulint) data.tv_usec); -} - #endif diff --git a/storage/innobase/include/row0mysql.h b/storage/innobase/include/row0mysql.h index 5f3489e413a..54d7413ab8c 100644 --- a/storage/innobase/include/row0mysql.h +++ b/storage/innobase/include/row0mysql.h @@ -237,9 +237,12 @@ row_lock_table_for_mysql( /** System Versioning: row_insert_for_mysql() modes */ enum ins_mode_t { - ROW_INS_NORMAL = 0, ///< plain row (without versioning) - ROW_INS_VERSIONED, ///< sys_trx_start = TRX_ID, sys_trx_end = MAX - ROW_INS_HISTORICAL ///< sys_trx_end = TRX_ID + /* plain row (without versioning) */ + ROW_INS_NORMAL = 0, + /* sys_trx_start = TRX_ID, sys_trx_end = MAX */ + ROW_INS_VERSIONED, + /* sys_trx_end = TRX_ID */ + ROW_INS_HISTORICAL }; /** Does an insert for MySQL. diff --git a/storage/innobase/row/row0ins.cc b/storage/innobase/row/row0ins.cc index aa9e9709643..dfb67fde123 100644 --- a/storage/innobase/row/row0ins.cc +++ b/storage/innobase/row/row0ins.cc @@ -429,8 +429,8 @@ row_ins_cascade_ancestor_updates_table( upd_node = static_cast(parent); - if (upd_node->table == table && upd_node->is_delete == FALSE - && !upd_node->vers_delete) { + if (upd_node->table == table && !upd_node->is_delete + && !upd_node->vers_delete) { return(TRUE); } @@ -1573,18 +1573,19 @@ private: ulint& counter; }; -/*********************************************************************//** -Reads sys_trx_end field from clustered index row. +/** Reads sys_trx_end field from clustered index row. +@param[in] rec clustered row +@param[in] offsets offsets +@param[in] index clustered index @return trx_id_t */ static trx_id_t row_ins_get_sys_trx_end( -/*===================================*/ - const rec_t *rec, /*!< in: clustered row */ - ulint *offsets, /*!< in: offsets */ - dict_index_t *index) /*!< in: clustered index */ + const rec_t* rec, + const ulint* offsets, + const dict_index_t* index) { - ut_a(dict_index_is_clust(index)); + ut_a(index->is_clust()); ulint len; ulint nfield = dict_col_get_clust_pos( @@ -1594,51 +1595,46 @@ row_ins_get_sys_trx_end( return(mach_read_from_8(field)); } -/** -Performs search at clustered index and returns sys_trx_end if row was found. +/** Performs search at clustered index and returns sys_trx_end if row was found. @param[in] index secondary index of record @param[in] rec record in a secondary index -@param[out] end_trx_id value from clustered index -@return DB_SUCCESS, DB_NO_REFERENCED_ROW */ +@return sys_trx_end on success or 0 at failure */ static -dberr_t +trx_id_t row_ins_search_sys_trx_end( - dict_index_t *index, - const rec_t *rec, - trx_id_t *end_trx_id) + dict_index_t* index, + const rec_t* rec) { ut_ad(!index->is_clust()); - bool found = false; - mem_heap_t *heap = mem_heap_create(256); - dict_index_t *clust_index = NULL; + trx_id_t result = 0; + mem_heap_t* heap = NULL; + dict_index_t* clust_index = NULL; ulint offsets_[REC_OFFS_NORMAL_SIZE]; - ulint *offsets = offsets_; + ulint* offsets = offsets_; rec_offs_init(offsets_); mtr_t mtr; - mtr_start(&mtr); + mtr.start(); - rec_t *clust_rec = + rec_t* clust_rec = row_get_clust_rec(BTR_SEARCH_LEAF, rec, index, &clust_index, &mtr); - if (!clust_rec) - goto not_found; + if (clust_rec) { + offsets = rec_get_offsets(clust_rec, clust_index, offsets, true, + ULINT_UNDEFINED, &heap); - offsets = rec_get_offsets(clust_rec, clust_index, offsets, true, - ULINT_UNDEFINED, &heap); - - *end_trx_id = row_ins_get_sys_trx_end(clust_rec, offsets, clust_index); - found = true; -not_found: - mtr_commit(&mtr); - mem_heap_free(heap); - if (!found) { + result = + row_ins_get_sys_trx_end(clust_rec, offsets, clust_index); + } else { ib::error() << "foreign constraints: secondary index is out of " "sync"; - ut_ad(false && "secondary index is out of sync"); - return(DB_NO_REFERENCED_ROW); + ut_ad(!"secondary index is out of sync"); } - return(DB_SUCCESS); + mtr.commit(); + if (heap) { + mem_heap_free(heap); + } + return(result); } /***************************************************************//** @@ -1705,13 +1701,11 @@ row_ins_check_foreign_constraint( } /* System Versioning: if sys_trx_end != Inf, we suppress the foreign key check */ - if (table->versioned() && - dfield_get_type(field)->prtype & DATA_VERS_END) { - byte* data = static_cast(dfield_get_data(field)); - ut_ad(data); - trx_id_t end_trx_id = mach_read_from_8(data); - if (end_trx_id != TRX_ID_MAX) + if (dfield_get_type(field)->prtype & DATA_VERS_END) { + ut_ad(table->versioned()); + if (dfield_is_historical_sys_trx_end(field)) { goto exit_func; + } } } @@ -1844,18 +1838,19 @@ row_ins_check_foreign_constraint( if (check_table->versioned()) { trx_id_t end_trx_id = 0; - if (dict_index_is_clust(check_index)) { + if (check_index->is_clust()) { end_trx_id = row_ins_get_sys_trx_end( rec, offsets, check_index); - } else if (row_ins_search_sys_trx_end( - check_index, rec, &end_trx_id) != - DB_SUCCESS) { + } else if (!(end_trx_id = + row_ins_search_sys_trx_end( + check_index, rec))) { break; } - if (end_trx_id != TRX_ID_MAX) + if (end_trx_id != TRX_ID_MAX) { continue; + } } if (rec_get_deleted_flag(rec, diff --git a/storage/innobase/row/row0merge.cc b/storage/innobase/row/row0merge.cc index 40dcc96416c..ba9914e4978 100644 --- a/storage/innobase/row/row0merge.cc +++ b/storage/innobase/row/row0merge.cc @@ -1743,6 +1743,8 @@ row_merge_read_clustered_index( ib_uint64_t read_rows = 0; ib_uint64_t table_total_rows = 0; ulonglong historic_auto_decrement = 0xffffffffffffffff; + char new_sys_trx_start[8]; + char new_sys_trx_end[8]; DBUG_ENTER("row_merge_read_clustered_index"); @@ -1917,6 +1919,9 @@ row_merge_read_clustered_index( prev_fields = NULL; } + mach_write_to_8(new_sys_trx_start, trx->id); + mach_write_to_8(new_sys_trx_end, TRX_ID_MAX); + /* Scan the clustered index. */ for (;;) { const rec_t* rec; @@ -2242,13 +2247,10 @@ end_of_index: bool historical_row = false; if (new_table->versioned()) { - const dfield_t *dfield = dtuple_get_nth_field( - row, new_table->vers_end); - const byte *data = static_cast( - dfield_get_data(dfield)); - ut_ad(dfield_get_len(dfield) == 8); + const dfield_t* dfield = dtuple_get_nth_field( + row, new_table->vers_end); historical_row = - mach_read_from_8(data) != TRX_ID_MAX; + dfield_is_historical_sys_trx_end(dfield); } const dfield_t* dfield; @@ -2272,10 +2274,11 @@ end_of_index: } ulonglong value; - if (likely(!historical_row)) + if (likely(!historical_row)) { value = sequence++; - else + } else { value = historic_auto_decrement--; + } switch (dtype_get_mtype(dtype)) { case DATA_INT: { @@ -2305,30 +2308,27 @@ end_of_index: if (old_table->versioned()) { if (!new_table->versioned() || drop_historical) { - const dict_col_t *col = - &old_table->cols - [old_table->vers_end]; - const ulint nfield = dict_col_get_clust_pos( - col, clust_index); + const dict_col_t* col = + &old_table->cols[old_table->vers_end]; + const ulint nfield = + dict_col_get_clust_pos(col, clust_index); ulint len = 0; - const rec_t *sys_trx_end = rec_get_nth_field( - rec, offsets, nfield, &len); + const rec_t* sys_trx_end = rec_get_nth_field( + rec, offsets, nfield, &len); ut_ad(len == 8); - if (mach_read_from_8(sys_trx_end) != TRX_ID_MAX) + if (mach_read_from_8(sys_trx_end) + != TRX_ID_MAX) { continue; + } } } else if (new_table->versioned()) { - void *sys_trx_start = mem_heap_alloc(row_heap, 8); - void *sys_trx_end = mem_heap_alloc(row_heap, 8); - mach_write_to_8(sys_trx_start, trx->id); - mach_write_to_8(sys_trx_end, TRX_ID_MAX); - dfield_t *start = dtuple_get_nth_field( - row, new_table->vers_start); - dfield_t *end = dtuple_get_nth_field( - row, new_table->vers_end); - dfield_set_data(start, sys_trx_start, 8); - dfield_set_data(end, sys_trx_end, 8); - trx->vers_update_trt= true; + dfield_t* start = + dtuple_get_nth_field(row, new_table->vers_start); + dfield_t* end = + dtuple_get_nth_field(row, new_table->vers_end); + dfield_set_data(start, new_sys_trx_start, 8); + dfield_set_data(end, new_sys_trx_end, 8); + trx->vers_update_trt = true; } write_buffers: diff --git a/storage/innobase/row/row0mysql.cc b/storage/innobase/row/row0mysql.cc index e6315e7ac24..ee96940e9ff 100644 --- a/storage/innobase/row/row0mysql.cc +++ b/storage/innobase/row/row0mysql.cc @@ -1420,6 +1420,23 @@ row_mysql_get_table_status( return(err); } +/** Writes 8 bytes to nth tuple field +@param[in] tuple where to write +@param[in] nth index in tuple +@param[in] data what to write +@param[in] buf field data buffer */ +static +void +set_tuple_col_8(dtuple_t* tuple, int col, uint64_t data, byte* buf) { + dfield_t* dfield = dtuple_get_nth_field(tuple, col); + ut_ad(dfield->type.len == 8); + if (dfield->len == UNIV_SQL_NULL) { + dfield_set_data(dfield, buf, 8); + } + ut_ad(dfield->len == dfield->type.len && dfield->data); + mach_write_to_8(dfield->data, data); +} + /** Does an insert for MySQL. @param[in] mysql_rec row in the MySQL format @param[in,out] prebuilt prebuilt struct in MySQL handle @@ -1504,14 +1521,14 @@ row_insert_for_mysql( ut_ad(t->mysql_col_len == 8); if (ins_mode == ROW_INS_HISTORICAL) { - row_ins_set_tuple_col_8(node->row, table->vers_end, trx->id, node->vers_end_buf); + set_tuple_col_8(node->row, table->vers_end, trx->id, node->vers_end_buf); } else /* ROW_INS_VERSIONED */ { - row_ins_set_tuple_col_8(node->row, table->vers_end, IB_UINT64_MAX, node->vers_end_buf); - int8store(&mysql_rec[t->mysql_col_offset], IB_UINT64_MAX); + set_tuple_col_8(node->row, table->vers_end, TRX_ID_MAX, node->vers_end_buf); + int8store(&mysql_rec[t->mysql_col_offset], TRX_ID_MAX); t = &prebuilt->mysql_template[table->vers_start]; ut_ad(t->mysql_col_len == 8); - row_ins_set_tuple_col_8(node->row, table->vers_start, trx->id, node->vers_start_buf); + set_tuple_col_8(node->row, table->vers_start, trx->id, node->vers_start_buf); int8store(&mysql_rec[t->mysql_col_offset], trx->id); } } @@ -2129,8 +2146,8 @@ run_again: node->cascade_upd_nodes = cascade_upd_nodes; cascade_upd_nodes->pop_front(); thr->fk_cascade_depth++; - vers_set_fields = node->table->versioned() && - (node->is_delete || node->versioned); + vers_set_fields = node->table->versioned() + && (node->is_delete || node->versioned); goto run_again; } @@ -2210,11 +2227,12 @@ run_again: prebuilt->table->stat_modified_counter++; } - if (node->table->versioned() && - (node->versioned || node->vers_delete || - // TODO: improve this check (check if we touch only - // unversioned fields in foreigh table) - node->foreign)) { + if (node->table->versioned() + && (node->versioned + || node->vers_delete + // TODO: improve this check (check if we touch only + // unversioned fields in foreigh table) + || node->foreign)) { trx->vers_update_trt = true; }