diff options
Diffstat (limited to 'sql/sql_insert.cc')
-rw-r--r-- | sql/sql_insert.cc | 121 |
1 files changed, 90 insertions, 31 deletions
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index efcbe9ab58f..3b479841c1d 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -79,6 +79,65 @@ static bool check_view_insertability(THD *thd, TABLE_LIST *view); #define my_safe_afree(ptr, size, min_length) if (size > min_length) my_free(ptr,MYF(0)) #endif +/* + Check that insert/update fields are from the same single table of a view. + + SYNOPSIS + check_view_single_update() + fields The insert/update fields to be checked. + view The view for insert. + map [in/out] The insert table map. + + DESCRIPTION + This function is called in 2 cases: + 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 + table of the given view. Otherwise the error is thrown. Found table + map is returned in the map parameter. + 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 + the function to check insert fields. Update fields are checked to be + from the same table as the insert fields. + + RETURN + 0 OK + 1 Error +*/ + +bool check_view_single_update(List<Item> &fields, TABLE_LIST *view, + table_map *map) +{ + /* it is join view => we need to find the table for update */ + List_iterator_fast<Item> it(fields); + Item *item; + TABLE_LIST *tbl= 0; // reset for call to check_single_table() + table_map tables= 0; + + while ((item= it++)) + tables|= item->used_tables(); + + /* Check found map against provided map */ + if (*map) + { + if (tables != *map) + goto error; + return FALSE; + } + + if (view->check_single_table(&tbl, tables, view) || tbl == 0) + goto error; + + view->table= tbl->table; + *map= tables; + + return FALSE; + +error: + my_error(ER_VIEW_MULTIUPDATE, MYF(0), + view->view_db.str, view->view_name.str); + return TRUE; +} + /* Check if insert fields are correct. @@ -103,7 +162,7 @@ static bool check_view_insertability(THD *thd, TABLE_LIST *view); static int check_insert_fields(THD *thd, TABLE_LIST *table_list, List<Item> &fields, List<Item> &values, - bool check_unique) + bool check_unique, table_map *map) { TABLE *table= table_list->table; @@ -176,21 +235,9 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list, if (table_list->effective_algorithm == VIEW_ALGORITHM_MERGE) { - /* it is join view => we need to find table for update */ - List_iterator_fast<Item> it(fields); - Item *item; - TABLE_LIST *tbl= 0; // reset for call to check_single_table() - table_map map= 0; - - while ((item= it++)) - map|= item->used_tables(); - if (table_list->check_single_table(&tbl, map, table_list) || tbl == 0) - { - my_error(ER_VIEW_MULTIUPDATE, MYF(0), - table_list->view_db.str, table_list->view_name.str); + if (check_view_single_update(fields, table_list, map)) return -1; - } - table_list->table= table= tbl->table; + table= table_list->table; } if (check_unique && thd->dupp_field) @@ -240,7 +287,7 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list, */ static int check_update_fields(THD *thd, TABLE_LIST *insert_table_list, - List<Item> &update_fields) + List<Item> &update_fields, table_map *map) { TABLE *table= insert_table_list->table; query_id_t timestamp_query_id; @@ -263,6 +310,10 @@ static int check_update_fields(THD *thd, TABLE_LIST *insert_table_list, if (setup_fields(thd, 0, update_fields, 1, 0, 0)) return -1; + if (insert_table_list->effective_algorithm == VIEW_ALGORITHM_MERGE && + check_view_single_update(update_fields, insert_table_list, map)) + return -1; + if (table->timestamp_field) { /* Don't set timestamp column if this is modified. */ @@ -908,6 +959,7 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, Name_resolution_context_state ctx_state; bool insert_into_view= (table_list->view != 0); bool res= 0; + table_map map= 0; DBUG_ENTER("mysql_prepare_insert"); DBUG_PRINT("enter", ("table_list 0x%lx, table 0x%lx, view %d", (ulong)table_list, (ulong)table, @@ -957,12 +1009,12 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, /* Prepare the fields in the statement. */ if (values && !(res= check_insert_fields(thd, context->table_list, fields, *values, - !insert_into_view) || + !insert_into_view, &map) || setup_fields(thd, 0, *values, 0, 0, 0)) && duplic == DUP_UPDATE) { 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, &map); select_lex->no_wrap_view_item= FALSE; /* When we are not using GROUP BY we can refer to other tables in the @@ -1132,23 +1184,29 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) 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) + { + if ((error == HA_ERR_FOUND_DUPP_KEY) && info->ignore) { table->file->restore_auto_increment(); goto ok_or_after_trg_err; } goto err; - } - info->updated++; + } + if ((table->file->table_flags() & HA_PARTIAL_COLUMN_READ) || + compare_record(table, query_id)) + { + info->updated++; - if (table->next_number_field) - table->file->adjust_next_insert_id_after_explicit_value(table->next_number_field->val_int()); + if (table->next_number_field) + table->file->adjust_next_insert_id_after_explicit_value( + table->next_number_field->val_int()); - trg_error= (table->triggers && - table->triggers->process_triggers(thd, TRG_EVENT_UPDATE, - TRG_ACTION_AFTER, TRUE)); - info->copied++; + 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 */ @@ -2284,6 +2342,7 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u) { LEX *lex= thd->lex; int res; + table_map map= 0; SELECT_LEX *lex_current_select_save= lex->current_select; DBUG_ENTER("select_insert::prepare"); @@ -2295,7 +2354,7 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u) */ lex->current_select= &lex->select_lex; res= check_insert_fields(thd, table_list, *fields, values, - !insert_into_view) || + !insert_into_view, &map) || setup_fields(thd, 0, values, 0, 0, 0); if (info.handle_duplicates == DUP_UPDATE) @@ -2313,7 +2372,7 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u) lex->select_lex.no_wrap_view_item= TRUE; res= res || check_update_fields(thd, context->table_list, - *info.update_fields); + *info.update_fields, &map); lex->select_lex.no_wrap_view_item= FALSE; /* When we are not using GROUP BY we can refer to other tables in the @@ -2569,7 +2628,7 @@ bool select_insert::send_eof() } if (last_insert_id) - thd->insert_id(last_insert_id); // For binary log + thd->insert_id(info.copied ? last_insert_id : 0); // For binary log /* Write to binlog before commiting transaction */ if (mysql_bin_log.is_open()) { |