Skip to content

Bb 13.1 bar mdev 39563 - #5566

Open
abarkov wants to merge 2 commits into
mainfrom
bb-13.1-bar-MDEV-39563
Open

Bb 13.1 bar mdev 39563#5566
abarkov wants to merge 2 commits into
mainfrom
bb-13.1-bar-MDEV-39563

Conversation

@abarkov

@abarkov abarkov commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

This PR imlements MDEV-39563 Implement UPDATE ... RETURNING ... INTO

@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Implements MDEV-39563 by extending the parser and execution pipeline to support UPDATE ... RETURNING ... INTO (Oracle mode), including support for assigning returned values into SP variables (including ROW/ROWTYPE fields) and assoc array scenarios, with new/updated MTR coverage.

Changes:

  • Extend grammar to allow an optional INTO <varlist> after UPDATE ... RETURNING ....
  • Plumb RETURNING ... INTO into the UPDATE execution path by reusing a parse-time select_result (e.g., select_dumpvar) when applicable.
  • Add/extend tests covering user variables, SP variables, ROW variables/fields, ROW TYPE OF (table/cursor), and assoc array behaviors; plus negative tests for unsupported DML contexts.

Reviewed changes

Copilot reviewed 21 out of 21 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
sql/sql_yacc.yy Adds opt_into... support for RETURNING ... INTO and refactors INTO-varlist init rule reuse.
sql/sql_update.h Adds a setter to inject a prebuilt select_result for UPDATE RETURNING.
sql/sql_update.cc Adjusts returning result creation/reuse across executions (esp. SP/PS reuse).
sql/sql_type_row.cc Adds runtime-by-name assignment path for ROWTYPE-based row-field OUT vars.
sql/sql_lex.h Declares LEX::handle_returning_into_varlist().
sql/sql_lex.cc Implements LEX::handle_returning_into_varlist() to bind RETURNING ... INTO to UPDATE only.
sql/sp_rcontext.h Declares set_variable_row_field_by_name() for runtime field lookup.
sql/sp_rcontext.cc Implements runtime row-field assignment by field name.
plugin/type_assoc_array/mysql-test/type_assoc_array/sp-assoc-array-returning.test New assoc-array focused test coverage for UPDATE RETURNING INTO.
plugin/type_assoc_array/mysql-test/type_assoc_array/sp-assoc-array-returning.result Expected results for the new assoc-array tests.
mysql-test/main/update_returning.test New core coverage for UPDATE RETURNING INTO, including ROW/ROWTYPE cases and repeated execution.
mysql-test/main/update_returning.result Expected results for UPDATE RETURNING INTO tests.
mysql-test/main/update_returning_into_row_var.inc Shared include for ROW/ROWTYPE INTO test permutations.
mysql-test/main/select_into_row.test New coverage for SELECT INTO ROWTYPE field stability (MDEV-40790).
mysql-test/main/select_into_row.result Expected results for SELECT INTO row tests.
mysql-test/main/replace_returning.test Adds negative coverage for unsupported REPLACE ... RETURNING ... INTO.
mysql-test/main/replace_returning.result Expected results for the new REPLACE negative test.
mysql-test/main/insert_returning.test Adds negative coverage for unsupported INSERT ... RETURNING ... INTO.
mysql-test/main/insert_returning.result Expected results for the new INSERT negative test.
mysql-test/main/delete_returning.test Adds negative coverage for unsupported DELETE ... RETURNING ... INTO.
mysql-test/main/delete_returning.result Expected results for the new DELETE negative test.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread sql/sql_lex.cc Outdated
Comment on lines +14283 to +14298
bool LEX::handle_returning_into_varlist()
{
if (!result)
return false; // No INTO
if (m_sql_cmd)
{
Sql_cmd_update *update;
if ((update= dynamic_cast<Sql_cmd_update*>(m_sql_cmd)))
{
update->set_returning_result(result);
return false;
}
}
my_error(ER_NOT_ALLOWED_IN_THIS_CONTEXT, MYF(0), "RETURNING..INTO");
return true;
}
Comment thread sql/sp_rcontext.cc
Comment on lines +691 to +702
int sp_rcontext::set_variable_row_field_by_name(THD *thd, uint var_idx,
const Lex_ident_sys_st &field_name,
Item **value)
{
DBUG_ENTER("sp_rcontext::set_variable_row_field");
DBUG_ASSERT(value);
uint field_idx= 0;
if (find_row_field_by_name_or_error(&field_idx, var_idx, field_name))
DBUG_RETURN(true);
Virtual_tmp_table *vtable= virtual_tmp_table_for_row(var_idx);
DBUG_RETURN(thd->sp_eval_expr(vtable->field[field_idx], value));
}
Comment thread sql/sql_type_row.cc
Comment on lines +151 to +156
bool check_assignability(THD *thd, const List<Item> &select_list,
bool *assign_as_row) const override
{
*assign_as_row= false;
return select_list.elements == 1;
}
Comment thread sql/sql_update.cc Outdated
Comment on lines +3365 to +3377
else
{
/*
Otherwise it is:
1. A non-RETURNING statement at all
2. Or a statement returning into an SP variable list:
UPDATE t1 SET a=a+1 WHERE a=10 RETURNING a INTO va;
In this case returning_result was created during parse time
and we preserve it for the second and further executions.
*/
DBUG_ASSERT(!returning_result ||
dynamic_cast<select_dumpvar*>(returning_result));
}
@abarkov
abarkov force-pushed the bb-13.1-bar-MDEV-39563 branch 2 times, most recently from a657340 to 668c6c4 Compare August 19, 2026 12:13
…es the server

The server crashed on DBUG_ASSERT on a SELECT into:
- a `ROW TYPE OF table1` field variable
- a `ROW TYPE OF cursor1` field variable

Fix:

- Adding a class my_var_sp_row_field_by_name
- Adding a method sp_rcontext::set_variable_row_field_by_name()
- Fixing the DBUG_ASSERT
@abarkov
abarkov force-pushed the bb-13.1-bar-MDEV-39563 branch 5 times, most recently from 6c23b8f to 72ba557 Compare August 21, 2026 07:16
Adding support for UPDATE .. RETURNING .. INTO queries.

For example:

  UPDATE t1 SET a=10,b=20 RETURNING a,b INTO va,vb;
  UPDATE t1 SET a=10,b=20 RETURNING a,b INTO @A,@b;

Limitations:
1. These types of queries:
   - REPLACE .. RETURNING .. INTO
   - DELETE .. RETURNING .. INTO
   - INSERT .. RETURNING .. INTO
   do not work - they return an error.
   They will be implemented separately, when needed.

2. UPDATE..RETURNING..INTO with --binlog_format=statement is not allowed
   and an error is raised.

3. Using OLD_VALUE(col) inside UPDATE..RETURNING..INTO is not allowed
   and an error is raised.

4. Multi-table updates, as well as single table updates with a subquery
   to the same table in WHERE (which get converted to multi-table) do
   not work and an error is raised.

Notes:

1. ANALYZE and EXPLAIN
   Both
     ANALYZE UPDATE .. RETURNING .. INTO ..
     EXPLAIN UPDATE .. RETURNING .. INTO ..
   return this error:
     'RETURNING..INTO' is not allowed in this context

2. Behavior on no data

  a. In case of degenerated plans (WHERE 1=0, LIMIT 0),
     no errors are raised.

  b. If the updated table contains no rows, the behavior depends on the engine,
     for example:
     - MyISAM returns no errors
     - InnoDB raises
         No data - zero rows fetched, selected, or processed
     This behavior is engine dependent because some engines (e.g. MyISAM)
     quickly know that the table has no records and execute the statement
     using a degenerated plan.

  c. If there are some rows, but non of them match the WHERE condition,
     then this error is raised:
       No data - zero rows fetched, selected, or processed

  d. If some rows where found but none of them actually
     got changed by the SET, still this error is raised:
       No data - zero rows fetched, selected, or processed
     The error message might be misleading. However, if we read
     it as "zero rows [that required updates] fetched", it looks OK.
     Let's not introduce a new error message for now.

Helper changes:

1. The grammar in analyze_stmt_command was changed to have
   LEX::analyze_stmt set to true earlier, so
   LEX::set_returning_into_result() already knows if this
   is an ANALYZE statement.

2. The Sql_cmd_update constructor is now called earlier in the grammar,
   to be able to call Sql_cmd_update::set_with_old_value_items()
   in the SET and RETURNING clauses.

3. Sql_cmd_dml::lex is now set during the constructor time.
   It makes things easier:
   - Sql_cmd_update::returns_result_set() needs the lex.
   - Sql_cmd_delete::orig_multitable and Sql_cmd_update::orig_multitable
     are not needed any more.
     They were used only in Sql_cmd_delete::sql_command_code() and
     Sql_cmd_update::sql_command_code().
     Sql_cmd_dml::sql_command_code() now returns lex->sql_command.
     The overrides Sql_cmd_delete::sql_command_code() and
     Sql_cmd_update::sql_command_code() were removed.
@abarkov
abarkov force-pushed the bb-13.1-bar-MDEV-39563 branch from 72ba557 to 99929e2 Compare August 21, 2026 09:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Development

Successfully merging this pull request may close these issues.

4 participants