mirror of
https://github.com/MariaDB/server.git
synced 2025-08-08 11:22:35 +03:00
Fix for bugs:
#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.
This commit is contained in:
@@ -398,7 +398,9 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
|
||||
if (fields.elements || !value_count)
|
||||
{
|
||||
restore_record(table,s->default_values); // Get empty record
|
||||
if (fill_record(thd, fields, *values, 0))
|
||||
if (fill_record_n_invoke_before_triggers(thd, fields, *values, 0,
|
||||
table->triggers,
|
||||
TRG_EVENT_INSERT))
|
||||
{
|
||||
if (values_list.elements != 1 && !thd->net.report_error)
|
||||
{
|
||||
@@ -419,8 +421,17 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
|
||||
if (thd->used_tables) // Column used in values()
|
||||
restore_record(table,s->default_values); // Get empty record
|
||||
else
|
||||
table->record[0][0]= table->s->default_values[0]; // Fix delete marker
|
||||
if (fill_record(thd, table->field, *values, 0))
|
||||
{
|
||||
/*
|
||||
Fix delete marker. No need to restore rest of record since it will
|
||||
be overwritten by fill_record() anyway (and fill_record() does not
|
||||
use default values in this case).
|
||||
*/
|
||||
table->record[0][0]= table->s->default_values[0];
|
||||
}
|
||||
if (fill_record_n_invoke_before_triggers(thd, table->field, *values, 0,
|
||||
table->triggers,
|
||||
TRG_EVENT_INSERT))
|
||||
{
|
||||
if (values_list.elements != 1 && ! thd->net.report_error)
|
||||
{
|
||||
@@ -432,14 +443,6 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
FIXME: Actually we should do this before
|
||||
check_that_all_fields_are_given_values Or even go into write_record ?
|
||||
*/
|
||||
if (table->triggers)
|
||||
table->triggers->process_triggers(thd, TRG_EVENT_INSERT,
|
||||
TRG_ACTION_BEFORE);
|
||||
|
||||
if ((res= table_list->view_check_option(thd,
|
||||
(values_list.elements == 1 ?
|
||||
0 :
|
||||
@@ -473,9 +476,6 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
|
||||
if (error)
|
||||
break;
|
||||
thd->row_count++;
|
||||
|
||||
if (table->triggers)
|
||||
table->triggers->process_triggers(thd, TRG_EVENT_INSERT, TRG_ACTION_AFTER);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -802,15 +802,35 @@ static int last_uniq_key(TABLE *table,uint keynr)
|
||||
|
||||
|
||||
/*
|
||||
Write a record to table with optional deleting of conflicting records
|
||||
Write a record to table with optional deleting of conflicting records,
|
||||
invoke proper triggers if needed.
|
||||
|
||||
Sets thd->no_trans_update if table which is updated didn't have transactions
|
||||
SYNOPSIS
|
||||
write_record()
|
||||
thd - thread context
|
||||
table - table to which record should be written
|
||||
info - COPY_INFO structure describing handling of duplicates
|
||||
and which is used for counting number of records inserted
|
||||
and deleted.
|
||||
|
||||
NOTE
|
||||
Once this record will be written to table after insert trigger will
|
||||
be invoked. If instead of inserting new record we will update old one
|
||||
then both on update triggers will work instead. Similarly both on
|
||||
delete triggers will be invoked if we will delete conflicting records.
|
||||
|
||||
Sets thd->no_trans_update if table which is updated didn't have
|
||||
transactions.
|
||||
|
||||
RETURN VALUE
|
||||
0 - success
|
||||
non-0 - error
|
||||
*/
|
||||
|
||||
|
||||
int write_record(THD *thd, TABLE *table,COPY_INFO *info)
|
||||
{
|
||||
int error;
|
||||
int error, trg_error= 0;
|
||||
char *key=0;
|
||||
DBUG_ENTER("write_record");
|
||||
|
||||
@@ -881,25 +901,33 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
|
||||
restore_record(table,record[1]);
|
||||
DBUG_ASSERT(info->update_fields->elements ==
|
||||
info->update_values->elements);
|
||||
if (fill_record(thd, *info->update_fields, *info->update_values, 0))
|
||||
goto err;
|
||||
if (fill_record_n_invoke_before_triggers(thd, *info->update_fields,
|
||||
*info->update_values, 0,
|
||||
table->triggers,
|
||||
TRG_EVENT_UPDATE))
|
||||
goto before_trg_err;
|
||||
|
||||
/* CHECK OPTION for VIEW ... ON DUPLICATE KEY UPDATE ... */
|
||||
if (info->view &&
|
||||
(res= info->view->view_check_option(current_thd, info->ignore)) ==
|
||||
VIEW_CHECK_SKIP)
|
||||
break;
|
||||
goto ok_or_after_trg_err;
|
||||
if (res == VIEW_CHECK_ERROR)
|
||||
goto err;
|
||||
goto before_trg_err;
|
||||
|
||||
if ((error=table->file->update_row(table->record[1],table->record[0])))
|
||||
{
|
||||
if ((error == HA_ERR_FOUND_DUPP_KEY) && info->ignore)
|
||||
break;
|
||||
goto ok_or_after_trg_err;
|
||||
goto err;
|
||||
}
|
||||
info->updated++;
|
||||
break;
|
||||
|
||||
trg_error= (table->triggers &&
|
||||
table->triggers->process_triggers(thd, TRG_EVENT_UPDATE,
|
||||
TRG_ACTION_AFTER, TRUE));
|
||||
info->copied++;
|
||||
goto ok_or_after_trg_err;
|
||||
}
|
||||
else /* DUP_REPLACE */
|
||||
{
|
||||
@@ -916,20 +944,48 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
|
||||
(table->timestamp_field_type == TIMESTAMP_NO_AUTO_SET ||
|
||||
table->timestamp_field_type == TIMESTAMP_AUTO_SET_ON_BOTH))
|
||||
{
|
||||
if (table->triggers &&
|
||||
table->triggers->process_triggers(thd, TRG_EVENT_UPDATE,
|
||||
TRG_ACTION_BEFORE, TRUE))
|
||||
goto before_trg_err;
|
||||
if ((error=table->file->update_row(table->record[1],
|
||||
table->record[0])))
|
||||
goto err;
|
||||
info->deleted++;
|
||||
break; /* Update logfile and count */
|
||||
trg_error= (table->triggers &&
|
||||
table->triggers->process_triggers(thd, TRG_EVENT_UPDATE,
|
||||
TRG_ACTION_AFTER,
|
||||
TRUE));
|
||||
/* Update logfile and count */
|
||||
info->copied++;
|
||||
goto ok_or_after_trg_err;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (table->triggers &&
|
||||
table->triggers->process_triggers(thd, TRG_EVENT_DELETE,
|
||||
TRG_ACTION_BEFORE, TRUE))
|
||||
goto before_trg_err;
|
||||
if ((error=table->file->delete_row(table->record[1])))
|
||||
goto err;
|
||||
info->deleted++;
|
||||
if (!table->file->has_transactions())
|
||||
thd->no_trans_update= 1;
|
||||
if (table->triggers &&
|
||||
table->triggers->process_triggers(thd, TRG_EVENT_DELETE,
|
||||
TRG_ACTION_AFTER, TRUE))
|
||||
{
|
||||
trg_error= 1;
|
||||
goto ok_or_after_trg_err;
|
||||
}
|
||||
/* Let us attempt do write_row() once more */
|
||||
}
|
||||
else if ((error=table->file->delete_row(table->record[1])))
|
||||
goto err;
|
||||
info->deleted++;
|
||||
if (!table->file->has_transactions())
|
||||
thd->no_trans_update= 1;
|
||||
}
|
||||
}
|
||||
info->copied++;
|
||||
trg_error= (table->triggers &&
|
||||
table->triggers->process_triggers(thd, TRG_EVENT_INSERT,
|
||||
TRG_ACTION_AFTER, TRUE));
|
||||
}
|
||||
else if ((error=table->file->write_row(table->record[0])))
|
||||
{
|
||||
@@ -939,18 +995,27 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
|
||||
table->file->restore_auto_increment();
|
||||
}
|
||||
else
|
||||
{
|
||||
info->copied++;
|
||||
trg_error= (table->triggers &&
|
||||
table->triggers->process_triggers(thd, TRG_EVENT_INSERT,
|
||||
TRG_ACTION_AFTER, TRUE));
|
||||
}
|
||||
|
||||
ok_or_after_trg_err:
|
||||
if (key)
|
||||
my_safe_afree(key,table->s->max_unique_length,MAX_KEY_LENGTH);
|
||||
if (!table->file->has_transactions())
|
||||
thd->no_trans_update= 1;
|
||||
DBUG_RETURN(0);
|
||||
DBUG_RETURN(trg_error);
|
||||
|
||||
err:
|
||||
if (key)
|
||||
my_afree(key);
|
||||
info->last_errno= error;
|
||||
table->file->print_error(error,MYF(0));
|
||||
|
||||
before_trg_err:
|
||||
if (key)
|
||||
my_safe_afree(key, table->s->max_unique_length, MAX_KEY_LENGTH);
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
|
||||
@@ -2013,12 +2078,27 @@ bool select_insert::send_data(List<Item> &values)
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
}
|
||||
if (!(error= write_record(thd, table,&info)) && table->next_number_field)
|
||||
if (!(error= write_record(thd, table, &info)))
|
||||
{
|
||||
/* Clear for next record */
|
||||
table->next_number_field->reset();
|
||||
if (! last_insert_id && thd->insert_id_used)
|
||||
last_insert_id=thd->insert_id();
|
||||
if (table->triggers)
|
||||
{
|
||||
/*
|
||||
If triggers exist then whey can modify some fields which were not
|
||||
originally touched by INSERT ... SELECT, so we have to restore
|
||||
their original values for the next row.
|
||||
*/
|
||||
restore_record(table, s->default_values);
|
||||
}
|
||||
if (table->next_number_field)
|
||||
{
|
||||
/*
|
||||
Clear auto-increment field for the next record, if triggers are used
|
||||
we will clear it twice, but this should be cheap.
|
||||
*/
|
||||
table->next_number_field->reset();
|
||||
if (!last_insert_id && thd->insert_id_used)
|
||||
last_insert_id= thd->insert_id();
|
||||
}
|
||||
}
|
||||
DBUG_RETURN(error);
|
||||
}
|
||||
@@ -2027,9 +2107,11 @@ bool select_insert::send_data(List<Item> &values)
|
||||
void select_insert::store_values(List<Item> &values)
|
||||
{
|
||||
if (fields->elements)
|
||||
fill_record(thd, *fields, values, 1);
|
||||
fill_record_n_invoke_before_triggers(thd, *fields, values, 1,
|
||||
table->triggers, TRG_EVENT_INSERT);
|
||||
else
|
||||
fill_record(thd, table->field, values, 1);
|
||||
fill_record_n_invoke_before_triggers(thd, table->field, values, 1,
|
||||
table->triggers, TRG_EVENT_INSERT);
|
||||
}
|
||||
|
||||
void select_insert::send_error(uint errcode,const char *err)
|
||||
@@ -2172,7 +2254,8 @@ select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
|
||||
|
||||
void select_create::store_values(List<Item> &values)
|
||||
{
|
||||
fill_record(thd, field, values, 1);
|
||||
fill_record_n_invoke_before_triggers(thd, field, values, 1,
|
||||
table->triggers, TRG_EVENT_INSERT);
|
||||
}
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user