Index: sql/sql_lex.cc =================================================================== --- sql/sql_lex.cc (revision 5500) +++ sql/sql_lex.cc (revision 5508) @@ -449,6 +449,10 @@ lex->max_statement_time= 0; + lex->return_update= false; + lex->return_update_list.empty(); + lex->real_query_start= 0; + DBUG_VOID_RETURN; } Index: sql/sql_yacc.yy =================================================================== --- sql/sql_yacc.yy (revision 5500) +++ sql/sql_yacc.yy (revision 5508) @@ -7262,7 +7262,9 @@ top_level_select_init { LEX *lex= Lex; - lex->sql_command= SQLCOM_SELECT; + /* SELECT...UPDATE is regarded as a DML.*/ + if (lex->sql_command != SQLCOM_UPDATE) + lex->sql_command= SQLCOM_SELECT; } ; @@ -7306,6 +7308,13 @@ { LEX *lex= Lex; SELECT_LEX * sel= lex->current_select; + + /* SELECT...UPDATE only applies to top-level SELECT statement. */ + if (lex->sql_command == SQLCOM_UPDATE && sel != &lex->select_lex) + { + my_error(ER_CANT_USE_OPTION_HERE, MYF(0), "SELECT...UPDATE"); + MYSQL_YYABORT; + } if (lex->current_select->set_braces(0)) { my_parse_error(ER(ER_SYNTAX_ERROR)); @@ -7333,15 +7342,60 @@ { Select->parsing_place= NO_MATTER; } - select_into select_lock_type + select_part3 ; +select_part3: + select_into select_lock_type + | select_update + select_into: opt_order_clause opt_limit_clause {} | into | select_from | into select_from | select_from into + ; + +/* + Impliment Oracle/PostgreSQL's "UPDATE...RETURNING..." as "SELECT...UPDATE..." +*/ +select_update: + FROM UPDATE_SYM + { + /* SELECT...UPDATE only applies to top-level SELECT statement. */ + if (Lex->current_select != &Lex->select_lex) + { + my_error(ER_CANT_USE_OPTION_HERE, MYF(0), "SELECT...UPDATE"); + MYSQL_YYABORT; + } + /* there should be no select options for SELECT...UPDATE. */ + if (Select->options) + { + my_error(ER_CANT_USE_OPTION_HERE, MYF(0), "SELECT...UPDATE"); + MYSQL_YYABORT; + } + + Lex->sql_command= SQLCOM_UPDATE; + Lex->duplicates= DUP_ERROR; + Lex->return_update= true; + Lex->real_query_start= YYLIP->get_tok_start() - YYTHD->query(); + Lex->return_update_list.swap(Select->item_list); + Select->item_list.empty(); + } + opt_low_priority + opt_ci_on_success opt_rb_on_fail opt_queue_on_pk opt_target_affect_row + opt_ignore table_ident SET update_list + { + if (!Select->add_table_to_list(YYTHD, $10, NULL, + TL_OPTION_UPDATING, + TL_READ_DEFAULT, + MDL_SHARED_READ)) + MYSQL_YYABORT; + + Select->set_lock_for_tables($4); + } + where_clause opt_order_clause delete_limit_clause {} ; select_from: Index: sql/sql_update.cc =================================================================== --- sql/sql_update.cc (revision 5500) +++ sql/sql_update.cc (revision 5508) @@ -282,12 +282,24 @@ DBUG_ENTER("mysql_update"); uint specified_affect_row; + select_send result; + bool return_update= thd->lex->return_update; + List &return_fileds= thd->lex->return_update_list; + if (open_tables(thd, &table_list, &table_count, 0)) DBUG_RETURN(1); if (table_list->multitable_view) { DBUG_ASSERT(table_list->view != 0); + + /* select result from multitable-view update is not supported */ + if (return_update) + { + my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->table_name, + "SELECT...UPDATE"); + DBUG_RETURN(1); + } DBUG_PRINT("info", ("Switch to multi-update")); /* pass counter value */ thd->lex->table_count= table_count; @@ -315,6 +327,22 @@ table->covering_keys= table->s->keys_in_use; table->quick_keys.clear_all(); + if (return_update) + { + if (setup_wild(thd, table_list, return_fileds, 0, select_lex->with_wild)) + DBUG_RETURN(1); + if (setup_fields(thd, 0, return_fileds, MARK_COLUMNS_READ, 0, 0)) + DBUG_RETURN(1); + + result.set_thd(thd); + result.prepare(return_fileds, &(thd->lex->unit)); + + if (result.send_result_set_metadata(return_fileds, + Protocol::SEND_NUM_ROWS | + Protocol::SEND_EOF)) + DBUG_RETURN(1); + } + #ifndef NO_EMBEDDED_ACCESS_CHECKS /* Force privilege re-checking for views after they have been opened. */ want_privilege= (table_list->view ? UPDATE_ACL : @@ -395,7 +423,10 @@ if (prune_partitions(thd, table, conds)) { free_underlaid_joins(thd, select_lex); - my_ok(thd); // No matching records + if (return_update) + result.send_eof(); + else + my_ok(thd); // No matching records DBUG_RETURN(0); } #endif @@ -419,7 +450,10 @@ { DBUG_RETURN(1); // Error in where } - my_ok(thd); // No matching records + if (return_update) + result.send_eof(); + else + my_ok(thd); // No matching records DBUG_RETURN(0); } @@ -716,7 +750,11 @@ if (!error || error == HA_ERR_RECORD_IS_THE_SAME) { if (error != HA_ERR_RECORD_IS_THE_SAME) + { updated++; + if (return_update && result.send_data(return_fileds)) + goto err; + } else error= 0; } @@ -894,8 +932,11 @@ else errcode= query_error_code(thd, killed_status == THD::NOT_KILLED); + uint32 query_start= return_update? thd->lex->real_query_start: 0; + if (thd->binlog_query(THD::ROW_QUERY_TYPE, - thd->query(), thd->query_length(), + thd->query() + query_start, + thd->query_length() - query_start, transactional_table, FALSE, FALSE, errcode)) { error=1; // Rollback update @@ -917,7 +958,10 @@ (ulong) thd->warning_info->statement_warn_count()); ha_rows row_count= (thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated; - my_ok(thd, row_count, id, buff); + if (return_update) + result.send_eof(); + else + my_ok(thd, row_count, id, buff); thd->updated_row_count += row_count; DBUG_PRINT("info",("%ld records updated", (long) updated)); } Index: sql/sql_lex.h =================================================================== --- sql/sql_lex.h (revision 5500) +++ sql/sql_lex.h (revision 5508) @@ -2470,6 +2470,10 @@ /** Maximum execution time for a statement. */ ulong max_statement_time; + bool return_update; + List return_update_list; + uint32 real_query_start; + LEX(); virtual ~LEX()