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/