Skip to content

Add DELETE ... RETURNING clause support - #725

Open
evgenyp-azm wants to merge 1 commit into
mysql:8.4from
evgenyp-azm:feat-8.4-delete-returning
Open

Add DELETE ... RETURNING clause support#725
evgenyp-azm wants to merge 1 commit into
mysql:8.4from
evgenyp-azm:feat-8.4-delete-returning

Conversation

@evgenyp-azm

@evgenyp-azm evgenyp-azm commented Aug 19, 2026

Copy link
Copy Markdown

This contribution is under the OCA signed by Amazon and covering submissions to the MySQL project.

What does this change do?

Adds support for a RETURNING clause on single-table DELETE statements, so a DELETE can return a result set built from the rows it deleted instead of just an affected-row count. The clause goes after ORDER BY/LIMIT and accepts the same expression list as a SELECT output list, including *, qualified wildcards, aliases, and subqueries:

DELETE FROM products

WHERE obsolete = 1

ORDER BY created_at

LIMIT 100

RETURNING id, name, created_at;

The rows are sent to the client as an ordinary result set, protocol-identical to a SELECT.

Why is it needed?

Returning data from modified rows is a well-established SQL pattern: PostgreSQL has supported it since 8.2, MariaDB since 10.0 for DELETE, and it appears in the SQL:2016 standard draft. It removes the need to run a SELECT before the DELETE to capture the doomed rows, which matters in three ways:

*) Audit logging and archival can capture computed or generated column values of deleted rows in one statement rather than a SELECT + DELETE round trip.

*) Queue-table and work-claiming patterns become a single atomic statement, which is the common way to hand rows to a downstream pipeline.

*) It removes a class of application-level race conditions. The SELECT-then-DELETE pattern needs explicit locking or serializable isolation to be correct under concurrency; DELETE ... RETURNING doesn't, because the read and the delete are the same operation.

How was it tested?

  • Added/updated MTR tests under mysql-test/t/delete_returning.test

  • scripts/ci/mtr.sh passes locally

  • Ran the relevant full suite (name it): main,innodb

Contributor checklist

  • I have signed the OCA with the email on these commits

  • Code is formatted (scripts/ci/format.sh)

  • Commits are focused with descriptive messages

AI assistance

  • I did not use AI assistance for this contribution

  • I used AI assistance for this contribution

If AI assistance was used, describe the tool(s) and extent of use:

Anthropic Opus was used as coding assistant, in test generation and review.

Whole submitted code and tests were manually reviewed and manually tested.

Areas touched

Parser, DELETE query executor.

Side-effects

The change removes RETURNING_SYM from ident_keywords_unambiguous, so unquoted RETURNING can no longer be used as an identifier and must be backtick-quoted. The reasoning is in the sql_yacc.yy comment.

Implement the RETURNING clause for single-table DELETE statements,
allowing the statement to return a result set of the deleted rows.

Syntax: DELETE FROM t WHERE ... RETURNING select_expr [, ...]

Supported features:
- Any SQL expression computable from row fields (columns, functions,
  arithmetic, subqueries)
- Aliases via AS keyword
- Wildcard expansion (*, table.*)
- Table-qualified column references
- Correlated scalar subqueries in RETURNING
- IN/EXISTS subqueries in RETURNING
- User-defined functions in RETURNING
- Works with WHERE, ORDER BY, LIMIT, PARTITION clauses
- Works with updatable views
- Works with prepared statements
- Works with stored procedures (CALL with multi-result protocol)
- Works with BEFORE/AFTER DELETE triggers
- Respects EXPLAIN (no side effects)
- Compatible with ONLY_FULL_GROUP_BY sql_mode

Restrictions:
- Not allowed in multi-table DELETE (syntax error at parser level)
- Aggregate functions not allowed (ER_INVALID_GROUP_FUNC_USE)
- RETURNING can no longer be used unquoted as an identifier; it must be
  backtick-quoted. See the sql_yacc.yy comment for the rationale.

Implementation:
- Parser (sql_yacc.yy): Add opt_delete_returning rule to single-table
  delete_stmt. Remove RETURNING_SYM from ident_keywords_unambiguous to
  avoid a reduce/reduce conflict with the select_alias grammar.
- Parse tree (parse_tree_nodes.h/.cc): Add opt_returning_list member to
  PT_delete. Set parsing_place=CTX_SELECT_LIST during contextualization
  so subqueries get proper outer_context for outer reference resolution.
- Command (sql_delete.h): Add m_returning flag to Sql_cmd_delete.
- Preparation (sql_delete.cc): In prepare_inner(), expand wildcards via
  setup_wild(), resolve RETURNING items via setup_fields(SELECT_ACL),
  validate no aggregates, set up Query_result_send. Skip multi-table
  conversion (hypergraph and subquery paths) when RETURNING is present.
  Pass empty field list to setup_order() to avoid crash from unresolved
  RETURNING items in base_ref_items.
- Execution (sql_delete.cc): In delete_from_single_table(), skip
  delete_all_rows() optimization, send result set metadata before loop,
  evaluate and send RETURNING row data before each delete, send EOF
  instead of my_ok(). Handle empty result sets on all early-exit paths
  including execute_inner() is_empty_query() path.
- Stored procedures (sp.cc): Flag DELETE ... RETURNING with
  sp_head::MULTI_RESULTS so CALL sets SERVER_MORE_RESULTS_EXISTS,
  enabling the client multi-result protocol.
- Access (sql_lex.h): Make setup_wild() public for use by Sql_cmd_delete.

Tests: mysql-test/t/delete_returning.test (26 test cases covering
basic expressions, errors, subqueries, ORDER BY+LIMIT, views,
prepared statements, stored procedures, triggers, EXPLAIN, partitions,
sql_mode, and privilege checks)

Also update mysql-test/suite/json/t/json_value.test to backtick-quote
the RETURNING identifier, which is required by the keyword change above.

This contribution is under the OCA signed by Amazon and covering
submissions to the MySQL project.
@oracle-contributor-agreement oracle-contributor-agreement Bot added the OCA Verified All contributors have signed the Oracle Contributor Agreement. label Aug 19, 2026
@evgenyp-azm
evgenyp-azm marked this pull request as ready for review August 19, 2026 21:06
@evgenyp-azm
evgenyp-azm requested a review from a team August 19, 2026 21:06
@ogrovlen ogrovlen added the Optimizer Changes touching optimizer code label Aug 20, 2026
@ogrovlen
ogrovlen requested a review from roylyseng August 21, 2026 05:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

OCA Verified All contributors have signed the Oracle Contributor Agreement. Optimizer Changes touching optimizer code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants