• Thirunarayanan Balathandayuthapani's avatar
    MDEV-15250 UPSERT during ALTER TABLE results in 'Duplicate entry' error for alter · 4b80c11f
    Thirunarayanan Balathandayuthapani authored
    - InnoDB DDL results in `Duplicate entry' if concurrent DML throws
    duplicate key error. The following scenario explains the problem
    
    connection con1:
      ALTER TABLE t1 FORCE;
    
    connection con2:
      INSERT INTO t1(pk, uk) VALUES (2, 2), (3, 2);
    
    In connection con2, InnoDB throws the 'DUPLICATE KEY' error because
    of unique index. Alter operation will throw the error when applying
    the concurrent DML log.
    
    - Inserting the duplicate key for unique index logs the insert
    operation for online ALTER TABLE. When insertion fails,
    transaction does rollback and it leads to logging of
    delete operation for online ALTER TABLE.
    While applying the insert log entries, alter operation
    encounters 'DUPLICATE KEY' error.
    
    - To avoid the above fake duplicate scenario, InnoDB should
    not write any log for online ALTER TABLE before DML transaction
    commit.
    
    - User thread which does DML can apply the online log if
    InnoDB ran out of online log and index is marked as completed.
    Set online log error if apply phase encountered any error.
    It can also clear all other indexes log, marks the newly
    added indexes as corrupted.
    
    - Removed the old online code which was a part of DML operations
    
    commit_inplace_alter_table() : Does apply the online log
    for the last batch of secondary index log and does frees
    the log for the completed index.
    
    trx_t::apply_online_log: Set to true while writing the undo
    log if the modified table has active DDL
    
    trx_t::apply_log(): Apply the DML changes to online DDL tables
    
    dict_table_t::is_active_ddl(): Returns true if the table
    has an active DDL
    
    dict_index_t::online_log_make_dummy(): Assign dummy value
    for clustered index online log to indicate the secondary
    indexes are being rebuild.
    
    dict_index_t::online_log_is_dummy(): Check whether the online
    log has dummy value
    
    ha_innobase_inplace_ctx::log_failure(): Handle the apply log
    failure for online DDL transaction
    
    row_log_mark_other_online_index_abort(): Clear out all other
    online index log after encountering the error during
    row_log_apply()
    
    row_log_get_error(): Get the error happened during row_log_apply()
    
    row_log_online_op(): Does apply the online log if index is
    completed and ran out of memory. Returns false if apply log fails
    
    UndorecApplier: Introduced a class to maintain the undo log
    record, latched undo buffer page, parse the undo log record,
    maintain the undo record type, info bits and update vector
    
    UndorecApplier::get_old_rec(): Get the correct version of the
    clustered index record that was modified by the current undo
    log record
    
    UndorecApplier::clear_undo_rec(): Clear the undo log related
    information after applying the undo log record
    
    UndorecApplier::log_update(): Handle the update, delete undo
    log and apply it on online indexes
    
    UndorecApplier::log_insert(): Handle the insert undo log
    and apply it on online indexes
    
    UndorecApplier::is_same(): Check whether the given roll pointer
    is generated by the current undo log record information
    
    trx_t::rollback_low(): Set apply_online_log for the transaction
    after partially rollbacked transaction has any active DDL
    
    prepare_inplace_alter_table_dict(): After allocating the online
    log, InnoDB does create fulltext common tables. Fulltext index
    doesn't allow the index to be online. So removed the dead
    code of online log removal
    
    Thanks to Marko Mäkelä for providing the initial prototype and
    Matthias Leich for testing the issue patiently.
    4b80c11f
trx0trx.cc 57.8 KB