sql_insert.cc | sql_insert.cc | |||
---|---|---|---|---|
skipping to change at line 108 | skipping to change at line 108 | |||
static void end_delayed_insert(THD *thd); | static void end_delayed_insert(THD *thd); | |||
pthread_handler_t handle_delayed_insert(void *arg); | pthread_handler_t handle_delayed_insert(void *arg); | |||
static void unlink_blobs(register TABLE *table); | static void unlink_blobs(register TABLE *table); | |||
#endif | #endif | |||
static bool check_view_insertability(THD *thd, TABLE_LIST *view); | static bool check_view_insertability(THD *thd, TABLE_LIST *view); | |||
/* | /* | |||
Check that insert/update fields are from the same single table of a view. | Check that insert/update fields are from the same single table of a view. | |||
SYNOPSIS | @param fields The insert/update fields to be checked. | |||
check_view_single_update() | @param values The insert/update values to be checked, NULL if | |||
fields The insert/update fields to be checked. | checking is not wanted. | |||
view The view for insert. | @param view The view for insert. | |||
map [in/out] The insert table map. | @param map [in/out] The insert table map. | |||
DESCRIPTION | This function is called in 2 cases: | |||
This function is called in 2 cases: | ||||
1. to check insert fields. In this case *map will be set to 0. | 1. to check insert fields. In this case *map will be set to 0. | |||
Insert fields are checked to be all from the same single underlying | Insert fields are checked to be all from the same single underlying | |||
table of the given view. Otherwise the error is thrown. Found table | table of the given view. Otherwise the error is thrown. Found table | |||
map is returned in the map parameter. | map is returned in the map parameter. | |||
2. to check update fields of the ON DUPLICATE KEY UPDATE clause. | 2. to check update fields of the ON DUPLICATE KEY UPDATE clause. | |||
In this case *map contains table_map found on the previous call of | In this case *map contains table_map found on the previous call of | |||
the function to check insert fields. Update fields are checked to be | the function to check insert fields. Update fields are checked to be | |||
from the same table as the insert fields. | from the same table as the insert fields. | |||
RETURN | @returns false if success. | |||
0 OK | ||||
1 Error | ||||
*/ | */ | |||
bool check_view_single_update(List<Item> &fields, List<Item> *values, | bool check_view_single_update(List<Item> &fields, List<Item> *values, | |||
TABLE_LIST *view, table_map *map) | TABLE_LIST *view, table_map *map) | |||
{ | { | |||
/* it is join view => we need to find the table for update */ | /* it is join view => we need to find the table for update */ | |||
List_iterator_fast<Item> it(fields); | List_iterator_fast<Item> it(fields); | |||
Item *item; | Item *item; | |||
TABLE_LIST *tbl= 0; // reset for call to check_single_table() | TABLE_LIST *tbl= 0; // reset for call to check_single_table() | |||
table_map tables= 0; | table_map tables= 0; | |||
skipping to change at line 177 | skipping to change at line 174 | |||
error: | error: | |||
my_error(ER_VIEW_MULTIUPDATE, MYF(0), | my_error(ER_VIEW_MULTIUPDATE, MYF(0), | |||
view->view_db.str, view->view_name.str); | view->view_db.str, view->view_name.str); | |||
return TRUE; | return TRUE; | |||
} | } | |||
/* | /* | |||
Check if insert fields are correct. | Check if insert fields are correct. | |||
SYNOPSIS | @param thd The current thread. | |||
check_insert_fields() | @param table_list The table we are inserting into (may be view) | |||
thd The current thread. | @param fields The insert fields. | |||
table The table for insert. | @param values The insert values. | |||
fields The insert fields. | @param check_unique If duplicate values should be rejected. | |||
values The insert values. | @param fields_and_values_from_different_maps If 'values' are allowed to | |||
check_unique If duplicate values should be rejected. | refer to other tables than those of 'fields' | |||
@param map See check_view_single_update | ||||
RETURN | @returns 0 if success, -1 if error | |||
0 OK | ||||
-1 Error | ||||
*/ | */ | |||
static int check_insert_fields(THD *thd, TABLE_LIST *table_list, | static int check_insert_fields(THD *thd, TABLE_LIST *table_list, | |||
List<Item> &fields, List<Item> &values, | List<Item> &fields, List<Item> &values, | |||
bool check_unique, | bool check_unique, | |||
bool fields_and_values_from_different_maps, | bool fields_and_values_from_different_maps, | |||
table_map *map) | table_map *map) | |||
{ | { | |||
TABLE *table= table_list->table; | TABLE *table= table_list->table; | |||
skipping to change at line 295 | skipping to change at line 291 | |||
(table_list->view && | (table_list->view && | |||
check_view_insertability(thd, table_list))) | check_view_insertability(thd, table_list))) | |||
{ | { | |||
my_error(ER_NON_INSERTABLE_TABLE, MYF(0), table_list->alias, "INSERT"); | my_error(ER_NON_INSERTABLE_TABLE, MYF(0), table_list->alias, "INSERT"); | |||
return -1; | return -1; | |||
} | } | |||
return 0; | return 0; | |||
} | } | |||
/* | /** | |||
Check update fields for the timestamp field. | Check if update fields are correct. | |||
SYNOPSIS | @param thd The current thread. | |||
check_update_fields() | @param insert_table_list The table we are inserting into (may be view) | |||
thd The current thread. | @param update_fields The update fields. | |||
insert_table_list The insert table list. | @param update_values The update values. | |||
table The table for update. | @param fields_and_values_from_different_maps If 'update_values' are allow | |||
update_fields The update fields. | ed to | |||
refer to other tables than those of 'update_fields' | ||||
@param map See check_view_single_update | ||||
RETURN | @returns 0 if success, -1 if error | |||
0 OK | ||||
-1 Error | ||||
*/ | */ | |||
static int check_update_fields(THD *thd, TABLE_LIST *insert_table_list, | static int check_update_fields(THD *thd, TABLE_LIST *insert_table_list, | |||
List<Item> &update_fields, | List<Item> &update_fields, | |||
List<Item> &update_values, table_map *map) | List<Item> &update_values, | |||
bool fields_and_values_from_different_maps, | ||||
table_map *map) | ||||
{ | { | |||
/* Check the fields we are going to modify */ | /* Check the fields we are going to modify */ | |||
if (setup_fields(thd, Ref_ptr_array(), | if (setup_fields(thd, Ref_ptr_array(), | |||
update_fields, MARK_COLUMNS_WRITE, 0, 0)) | update_fields, MARK_COLUMNS_WRITE, 0, 0)) | |||
return -1; | return -1; | |||
if (insert_table_list->effective_algorithm == VIEW_ALGORITHM_MERGE && | if (insert_table_list->effective_algorithm == VIEW_ALGORITHM_MERGE && | |||
check_view_single_update(update_fields, &update_values, | check_view_single_update(update_fields, | |||
fields_and_values_from_different_maps ? | ||||
(List<Item>*) 0 : &update_values, | ||||
insert_table_list, map)) | insert_table_list, map)) | |||
return -1; | return -1; | |||
return 0; | return 0; | |||
} | } | |||
/* | /* | |||
Prepare triggers for INSERT-like statement. | Prepare triggers for INSERT-like statement. | |||
SYNOPSIS | SYNOPSIS | |||
prepare_triggers_for_insert_stmt() | prepare_triggers_for_insert_stmt() | |||
skipping to change at line 1492 | skipping to change at line 1491 | |||
} | } | |||
if (!res) | if (!res) | |||
res= setup_fields(thd, Ref_ptr_array(), | res= setup_fields(thd, Ref_ptr_array(), | |||
update_values, MARK_COLUMNS_READ, 0, 0); | update_values, MARK_COLUMNS_READ, 0, 0); | |||
if (!res && duplic == DUP_UPDATE) | if (!res && duplic == DUP_UPDATE) | |||
{ | { | |||
select_lex->no_wrap_view_item= TRUE; | select_lex->no_wrap_view_item= TRUE; | |||
res= check_update_fields(thd, context->table_list, update_fields, | res= check_update_fields(thd, context->table_list, update_fields, | |||
update_values, &map); | update_values, false, &map); | |||
select_lex->no_wrap_view_item= FALSE; | select_lex->no_wrap_view_item= FALSE; | |||
} | } | |||
/* Restore the current context. */ | /* Restore the current context. */ | |||
ctx_state.restore_state(context, table_list); | ctx_state.restore_state(context, table_list); | |||
} | } | |||
if (res) | if (res) | |||
DBUG_RETURN(res); | DBUG_RETURN(res); | |||
skipping to change at line 1701 | skipping to change at line 1700 | |||
DBUG_ASSERT(update->get_changed_columns()->elements == | DBUG_ASSERT(update->get_changed_columns()->elements == | |||
update->update_values->elements); | update->update_values->elements); | |||
if (fill_record_n_invoke_before_triggers(thd, | if (fill_record_n_invoke_before_triggers(thd, | |||
*update->get_changed_colum ns(), | *update->get_changed_colum ns(), | |||
*update->update_values, | *update->update_values, | |||
ignore_errors, | ignore_errors, | |||
table->triggers, | table->triggers, | |||
TRG_EVENT_UPDATE)) | TRG_EVENT_UPDATE)) | |||
goto before_trg_err; | goto before_trg_err; | |||
bool insert_id_consumed= false; | ||||
if (// UPDATE clause specifies a value for the auto increment field | ||||
table->auto_increment_field_not_null && | ||||
// An auto increment value has been generated for this row | ||||
(insert_id_for_cur_row > 0)) | ||||
{ | ||||
// After-update value: | ||||
const ulonglong auto_incr_val= table->next_number_field->val_int( | ||||
); | ||||
if (auto_incr_val == insert_id_for_cur_row) | ||||
{ | ||||
// UPDATE wants to use the generated value | ||||
insert_id_consumed= true; | ||||
} | ||||
else if (table->file->auto_inc_interval_for_cur_row. | ||||
in_range(auto_incr_val)) | ||||
{ | ||||
/* | ||||
UPDATE wants to use one auto generated value which we have al | ||||
ready | ||||
reserved for another (previous or following) row. That may ca | ||||
use | ||||
a duplicate key error if we later try to insert the reserved | ||||
value. Such conflicts on auto generated values would be stran | ||||
ge | ||||
behavior, so we return a clear error now. | ||||
*/ | ||||
my_error(ER_AUTO_INCREMENT_CONFLICT, MYF(0)); | ||||
goto before_trg_err; | ||||
} | ||||
} | ||||
if (!insert_id_consumed) | ||||
table->file->restore_auto_increment(prev_insert_id); | ||||
/* CHECK OPTION for VIEW ... ON DUPLICATE KEY UPDATE ... */ | /* CHECK OPTION for VIEW ... ON DUPLICATE KEY UPDATE ... */ | |||
{ | { | |||
const TABLE_LIST *inserted_view= | const TABLE_LIST *inserted_view= | |||
table->pos_in_table_list->belong_to_view; | table->pos_in_table_list->belong_to_view; | |||
if (inserted_view != NULL) | if (inserted_view != NULL) | |||
{ | { | |||
res= inserted_view->view_check_option(thd, ignore_errors); | res= inserted_view->view_check_option(thd, ignore_errors); | |||
if (res == VIEW_CHECK_SKIP) | if (res == VIEW_CHECK_SKIP) | |||
goto ok_or_after_trg_err; | goto ok_or_after_trg_err; | |||
if (res == VIEW_CHECK_ERROR) | if (res == VIEW_CHECK_ERROR) | |||
goto before_trg_err; | goto before_trg_err; | |||
} | } | |||
} | } | |||
table->file->restore_auto_increment(prev_insert_id); | ||||
info->stats.touched++; | info->stats.touched++; | |||
if (!records_are_comparable(table) || compare_records(table)) | if (!records_are_comparable(table) || compare_records(table)) | |||
{ | { | |||
// Handle the INSERT ON DUPLICATE KEY UPDATE operation | // Handle the INSERT ON DUPLICATE KEY UPDATE operation | |||
update->set_function_defaults(table); | update->set_function_defaults(table); | |||
if ((error=table->file->ha_update_row(table->record[1], | if ((error=table->file->ha_update_row(table->record[1], | |||
table->record[0])) && | table->record[0])) && | |||
error != HA_ERR_RECORD_IS_THE_SAME) | error != HA_ERR_RECORD_IS_THE_SAME) | |||
{ | { | |||
skipping to change at line 1752 | skipping to change at line 1781 | |||
Except if LAST_INSERT_ID(#) was in the INSERT query, which is | Except if LAST_INSERT_ID(#) was in the INSERT query, which is | |||
handled separately by THD::arg_of_last_insert_id_function. | handled separately by THD::arg_of_last_insert_id_function. | |||
*/ | */ | |||
insert_id_for_cur_row= table->file->insert_id_for_cur_row= 0; | insert_id_for_cur_row= table->file->insert_id_for_cur_row= 0; | |||
trg_error= (table->triggers && | trg_error= (table->triggers && | |||
table->triggers->process_triggers(thd, TRG_EVENT_UPDA TE, | table->triggers->process_triggers(thd, TRG_EVENT_UPDA TE, | |||
TRG_ACTION_AFTER, T RUE)); | TRG_ACTION_AFTER, T RUE)); | |||
info->stats.copied++; | info->stats.copied++; | |||
} | } | |||
if (table->next_number_field) | ||||
table->file->adjust_next_insert_id_after_explicit_value( | ||||
table->next_number_field->val_int()); | ||||
goto ok_or_after_trg_err; | goto ok_or_after_trg_err; | |||
} | } | |||
else /* DUP_REPLACE */ | else /* DUP_REPLACE */ | |||
{ | { | |||
/* | /* | |||
The manual defines the REPLACE semantics that it is either | The manual defines the REPLACE semantics that it is either | |||
an INSERT or DELETE(s) + INSERT; FOREIGN KEY checks in | an INSERT or DELETE(s) + INSERT; FOREIGN KEY checks in | |||
InnoDB do not function in the defined way if we allow MySQL | InnoDB do not function in the defined way if we allow MySQL | |||
to convert the latter operation internally to an UPDATE. | to convert the latter operation internally to an UPDATE. | |||
We also should not perform this conversion if we have | We also should not perform this conversion if we have | |||
skipping to change at line 3397 | skipping to change at line 3423 | |||
Name_resolution_context_state ctx_state; | Name_resolution_context_state ctx_state; | |||
/* Save the state of the current name resolution context. */ | /* Save the state of the current name resolution context. */ | |||
ctx_state.save_state(context, table_list); | ctx_state.save_state(context, table_list); | |||
/* Perform name resolution only in the first table - 'table_list'. */ | /* Perform name resolution only in the first table - 'table_list'. */ | |||
table_list->next_local= 0; | table_list->next_local= 0; | |||
context->resolve_in_table_list_only(table_list); | context->resolve_in_table_list_only(table_list); | |||
lex->select_lex.no_wrap_view_item= TRUE; | lex->select_lex.no_wrap_view_item= TRUE; | |||
res= res || check_update_fields(thd, context->table_list, | res= res || | |||
*update.get_changed_columns(), | check_update_fields(thd, context->table_list, | |||
*update.update_values, | *update.get_changed_columns(), | |||
&map); | *update.update_values, | |||
/* | ||||
In INSERT SELECT ON DUPLICATE KEY UPDATE col=x | ||||
'x' can legally refer to a non-inserted table. | ||||
'x' is not even resolved yet. | ||||
*/ | ||||
true, | ||||
&map); | ||||
lex->select_lex.no_wrap_view_item= FALSE; | lex->select_lex.no_wrap_view_item= FALSE; | |||
/* | /* | |||
When we are not using GROUP BY and there are no ungrouped aggregate | When we are not using GROUP BY and there are no ungrouped aggregate | |||
functions | functions | |||
we can refer to other tables in the ON DUPLICATE KEY part. | we can refer to other tables in the ON DUPLICATE KEY part. | |||
We use next_name_resolution_table destructively, so check it first | We use next_name_resolution_table destructively, so check it first | |||
(views?). | (views?). | |||
*/ | */ | |||
DBUG_ASSERT (!table_list->next_name_resolution_table); | DBUG_ASSERT (!table_list->next_name_resolution_table); | |||
if (lex->select_lex.group_list.elements == 0 && | if (lex->select_lex.group_list.elements == 0 && | |||
skipping to change at line 3525 | skipping to change at line 3558 | |||
RETURN | RETURN | |||
0 OK | 0 OK | |||
*/ | */ | |||
int select_insert::prepare2(void) | int select_insert::prepare2(void) | |||
{ | { | |||
DBUG_ENTER("select_insert::prepare2"); | DBUG_ENTER("select_insert::prepare2"); | |||
if (thd->locked_tables_mode <= LTM_LOCK_TABLES && | if (thd->locked_tables_mode <= LTM_LOCK_TABLES && | |||
!thd->lex->describe) | !thd->lex->describe) | |||
{ | { | |||
DBUG_ASSERT(!bulk_insert_started); | ||||
// TODO: Is there no better estimation than 0 == Unknown number of rows ? | // TODO: Is there no better estimation than 0 == Unknown number of rows ? | |||
table->file->ha_start_bulk_insert((ha_rows) 0); | table->file->ha_start_bulk_insert((ha_rows) 0); | |||
bulk_insert_started= true; | ||||
} | } | |||
DBUG_RETURN(0); | DBUG_RETURN(0); | |||
} | } | |||
void select_insert::cleanup() | void select_insert::cleanup() | |||
{ | { | |||
/* select_insert/select_create are never re-used in prepared statement */ | /* select_insert/select_create are never re-used in prepared statement */ | |||
DBUG_ASSERT(0); | DBUG_ASSERT(0); | |||
} | } | |||
skipping to change at line 3650 | skipping to change at line 3685 | |||
{ | { | |||
int error; | int error; | |||
bool const trans_table= table->file->has_transactions(); | bool const trans_table= table->file->has_transactions(); | |||
ulonglong id, row_count; | ulonglong id, row_count; | |||
bool changed; | bool changed; | |||
THD::killed_state killed_status= thd->killed; | THD::killed_state killed_status= thd->killed; | |||
DBUG_ENTER("select_insert::send_eof"); | DBUG_ENTER("select_insert::send_eof"); | |||
DBUG_PRINT("enter", ("trans_table=%d, table_type='%s'", | DBUG_PRINT("enter", ("trans_table=%d, table_type='%s'", | |||
trans_table, table->file->table_type())); | trans_table, table->file->table_type())); | |||
error= (thd->locked_tables_mode <= LTM_LOCK_TABLES ? | error= (bulk_insert_started ? | |||
table->file->ha_end_bulk_insert() : 0); | table->file->ha_end_bulk_insert() : 0); | |||
if (!error && thd->is_error()) | if (!error && thd->is_error()) | |||
error= thd->get_stmt_da()->sql_errno(); | error= thd->get_stmt_da()->sql_errno(); | |||
table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY); | table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY); | |||
table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE); | table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE); | |||
changed= (info.stats.copied || info.stats.deleted || info.stats.updated); | changed= (info.stats.copied || info.stats.deleted || info.stats.updated); | |||
if (changed) | if (changed) | |||
{ | { | |||
skipping to change at line 3742 | skipping to change at line 3777 | |||
if (table) | if (table) | |||
{ | { | |||
bool changed, transactional_table; | bool changed, transactional_table; | |||
/* | /* | |||
Try to end the bulk insert which might have been started before. | Try to end the bulk insert which might have been started before. | |||
We don't need to do this if we are in prelocked mode (since we | We don't need to do this if we are in prelocked mode (since we | |||
don't use bulk insert in this case). Also we should not do this | don't use bulk insert in this case). Also we should not do this | |||
if tables are not locked yet (bulk insert is not started yet | if tables are not locked yet (bulk insert is not started yet | |||
in this case). | in this case). | |||
*/ | */ | |||
if (thd->locked_tables_mode <= LTM_LOCK_TABLES && | if (bulk_insert_started) | |||
thd->lex->is_query_tables_locked()) | ||||
table->file->ha_end_bulk_insert(); | table->file->ha_end_bulk_insert(); | |||
/* | /* | |||
If at least one row has been inserted/modified and will stay in | If at least one row has been inserted/modified and will stay in | |||
the table (the table doesn't have transactions) we must write to | the table (the table doesn't have transactions) we must write to | |||
the binlog (and the error code will make the slave stop). | the binlog (and the error code will make the slave stop). | |||
For many errors (example: we got a duplicate key error while | For many errors (example: we got a duplicate key error while | |||
inserting into a MyISAM table), no row will be added to the table, | inserting into a MyISAM table), no row will be added to the table, | |||
so passing the error to the slave will not help since there will | so passing the error to the slave will not help since there will | |||
skipping to change at line 4119 | skipping to change at line 4153 | |||
const bool ignore_errors= info.get_ignore_errors(); | const bool ignore_errors= info.get_ignore_errors(); | |||
if (ignore_errors || duplicate_handling != DUP_ERROR) | if (ignore_errors || duplicate_handling != DUP_ERROR) | |||
table->file->extra(HA_EXTRA_IGNORE_DUP_KEY); | table->file->extra(HA_EXTRA_IGNORE_DUP_KEY); | |||
if (duplicate_handling == DUP_REPLACE && | if (duplicate_handling == DUP_REPLACE && | |||
(!table->triggers || !table->triggers->has_delete_triggers())) | (!table->triggers || !table->triggers->has_delete_triggers())) | |||
table->file->extra(HA_EXTRA_WRITE_CAN_REPLACE); | table->file->extra(HA_EXTRA_WRITE_CAN_REPLACE); | |||
if (duplicate_handling == DUP_UPDATE) | if (duplicate_handling == DUP_UPDATE) | |||
table->file->extra(HA_EXTRA_INSERT_WITH_UPDATE); | table->file->extra(HA_EXTRA_INSERT_WITH_UPDATE); | |||
if (thd->locked_tables_mode <= LTM_LOCK_TABLES) | if (thd->locked_tables_mode <= LTM_LOCK_TABLES) | |||
{ | ||||
table->file->ha_start_bulk_insert((ha_rows) 0); | table->file->ha_start_bulk_insert((ha_rows) 0); | |||
bulk_insert_started= true; | ||||
} | ||||
thd->abort_on_warning= (!ignore_errors && thd->is_strict_mode()); | thd->abort_on_warning= (!ignore_errors && thd->is_strict_mode()); | |||
if (check_that_all_fields_are_given_values(thd, table, table_list)) | if (check_that_all_fields_are_given_values(thd, table, table_list)) | |||
DBUG_RETURN(1); | DBUG_RETURN(1); | |||
table->mark_columns_needed_for_insert(); | table->mark_columns_needed_for_insert(); | |||
table->file->extra(HA_EXTRA_WRITE_CACHE); | table->file->extra(HA_EXTRA_WRITE_CACHE); | |||
DBUG_RETURN(0); | DBUG_RETURN(0); | |||
} | } | |||
int | int | |||
select_create::binlog_show_create_table(TABLE **tables, uint count) | select_create::binlog_show_create_table(TABLE **tables, uint count) | |||
End of changes. 21 change blocks. | ||||
45 lines changed or deleted | 87 lines changed or added | |||
This html diff was produced by rfcdiff 1.41. The latest version is available from http://tools.ietf.org/tools/rfcdiff/ |