• unknown's avatar
    Fix for bugs: · efc2479d
    unknown authored
     #5860 "Multi-table UPDATE does not activate update triggers"
     #6812 "Triggers are not activated for INSERT ... SELECT"
     #8755 "Trigger is not activated by LOAD DATA".
    This patch also implements proper handling of triggers for special forms
    of insert like REPLACE or INSERT ... ON DUPLICATE KEY UPDATE. 
    Also now we don't call after trigger in case when we have failed to
    inserted/update or delete row. Trigger failure should stop statement
    execution.
    
    I have not properly tested handling of errors which happen inside of
    triggers in this patch, since it is simplier to do this once we will be
    able to access tables from triggers.
    
    
    mysql-test/r/trigger.result:
      Added tests for triggers behavior for various non-standard forms of
      INSERT such as REPLACE and INSERT ... ON DUPLICATE KEY UPDATE.
      Also added tests for bugs #5860 "Multi-table UPDATE does not activate
      update triggers", #6812 "Triggers are not activated for INSERT ... SELECT"
      and #8755 "Trigger is not activated by LOAD DATA".
    mysql-test/t/trigger.test:
      Added tests for triggers behavior for various non-standard forms of
      INSERT such as REPLACE and INSERT ... ON DUPLICATE KEY UPDATE.
      Also added tests for bugs #5860 "Multi-table UPDATE does not activate
      update triggers", #6812 "Triggers are not activated for INSERT ... SELECT"
      and #8755 "Trigger is not activated by LOAD DATA".
    sql/item.cc:
      Since it turned out that at trigger loading time we can't say in which
      buffer TABLE::record[0] or record[1] old version of row will be stored
      we have to change our approach to binding of Item_trigger_field to
      Field instances.
      Now after trigger parsing (in Item_trigger_field::setup_table()) we only
      find index of proper Field in the TABLE::field array. Then before trigger
      is invoked we set Table_triggers_list::old_field/new_field so they point
      to arrays holding Field instances bound to buffers with proper row
      versions. And as last step in Item_trigger_field::fix_fields() we get
      pointer to Field from those arrays using saved field index.
      
      Item_trigger_field::setup_field()/fix_fields() were changed to implement
      this approach.
    sql/item.h:
      Since it turned out that at trigger loading time we can't say in which
      buffer TABLE::record[0] or record[1] old version of row will be stored
      we have to change our approach to binding of Item_trigger_field to
      Field instances.
      Now after trigger parsing (in Item_trigger_field::setup_table()) we only
      find index of proper Field in the TABLE::field array. Then before trigger
      is invoked we set Table_triggers_list::old_field/new_field so they point
      to arrays holding Field instances bound to buffers with proper row
      versions. And as last step in Item_trigger_field::fix_fields() we get
      pointer to Field from those arrays using saved field index.
      
      Item_trigger_field:
      - Added field_idx member to store index of Field object corresponding to
        this Item in TABLE::field array.
      - Added triggers member to be able to access to parent Table_trigger_list
        object from fix_fields() method.
      - setup_field() no longer needs to know for which type of event this
        trigger is, since it does not make decision Field for which buffer
        (record[0] or record[1] is appropriate for this Item_trigger_field)
    sql/mysql_priv.h:
      Added fill_record_n_invoke_before_triggers() methods. They are simple
      wrappers around fill_record() which invoke proper before trigger right
      after filling record with values.
    sql/sql_base.cc:
      Added fill_record_n_invoke_before_triggers() methods. They are simple
      wrappers around fill_record() which invoke proper before trigger right
      after filling record with values.
    sql/sql_delete.cc:
      mysql_delete():
        Now we stop statement execution if one of triggers failed, we also
        don't execute after delete trigger if we failed to delete row from
        the table (We also pass information about which buffer contains old
        version of row to process_triggers()).
      multi_delete::send_data()/do_deletes():
        Now we also invoke triggers in case of multi-delete.
    sql/sql_insert.cc:
      mysql_insert():
        Moved invocation of before triggers to fill_record_n_invoke_before_triggers()
        method. After triggers are now executed as part of write_record().
        (as nice side effect now we also stop statement execution if one of
        triggers fail).
      write_record():
        Invoke after insert trigger after performing insert. Also invoke proper
        triggers if insert is converted to update or conflicting row is deleted.
        Cleaned up error handling a bit - no sense to report error via
        handler::print_error if it was not generated by handler method and
        was reported before.
        Also now we will execute after trigger only if we really have written
        row to the table. 
      select_insert::send_data()/store_values():
        We should also execute INSERT triggers for INSERT ... SELECT statement.
    sql/sql_load.cc:
      read_fixed_length()/read_sep_field():
        We should execute INSERT triggers when processing LOAD DATA statement.
        Small cleanup in auto-increment related code. Also moved check for
        thd->killed which is used to abort LOAD DATA in case of problems
        in 'traditional' mode to better place..
    sql/sql_trigger.cc:
      Since it turned out that at trigger loading time we can't say in which
      buffer TABLE::record[0] or record[1] old version of row will be stored
      we have to change our approach to binding of Item_trigger_field to
      Field instances.
      Now after trigger parsing (in Item_trigger_field::setup_table()) we only
      find index of proper Field in the TABLE::field array. Then before trigger
      is invoked we set Table_triggers_list::old_field/new_field so they point
      to arrays holding Field instances bound to buffers with proper row
      versions. And as last step in Item_trigger_field::fix_fields() we get
      pointer to Field from those arrays using saved field index.
      
      Table_triggers_list methods were changed to implement this approach
      (see also comments for sql_trigger.h).
    sql/sql_trigger.h:
      Since it turned out that at trigger loading time we can't say in which
      buffer TABLE::record[0] or record[1] old version of row will be stored
      we have to change our approach to binding of Item_trigger_field to
      Field instances.
      Now after trigger parsing (in Item_trigger_field::setup_table()) we only
      find index of proper Field in the TABLE::field array. Then before trigger
      is invoked we set Table_triggers_list::old_field/new_field so they point
      to arrays holding Field instances bound to buffers with proper row
      versions. And as last step in Item_trigger_field::fix_fields() we get
      pointer to Field from those arrays using saved field index.
      
      Changed Table_triggers_list to implement this new approach:
      - Added record1_field member to store array of Field objects bound
        to TABLE::record[1] buffer (instead of existing old_field member)
      - Added new_field member and changed meaning of old_field member.
        During trigger execution they should point to arrays of Field objects
        bound to buffers holding new and old versions of row respectively.
      - Added 'table' member to be able to get access to TABLE instance
        (for which this trigger list object was created) from process_triggers()
        method.
      - Now process_triggers() method sets old_field and new_field members
        properly before executing triggers body (basing on new 
        old_row_is_record1 parameter value).
      - Renamed prepare_old_row_accessors_method() to prepare_record1_accessors()
      
      Also added has_before_update_triggers() method which allows to check
      whenever any before update triggers exist for table.
    sql/sql_update.cc:
      mysql_update():
        Now we invoke before triggers in fill_record_n_invoke_before_triggers()
        method. Also now we abort statement execution when one of triggers fail.
      safe_update_on_fly():
        When we are trying to understand if we can update first table in multi
        update on the fly we should take into account that BEFORE UPDATE
        trigger can change field values.
      multi_update::send_data()/do_updates()
        We should execute proper triggers when doing multi-update
        (in both cases when we do it on the fly and using temporary tables).
    efc2479d
trigger.test 11.3 KB