Skip to content

fix: ExecuteDeleteAsync with navigation property generates invalid SQL (MySQL Error 1093) - #351

Open
Novoposarantus wants to merge 4 commits into
microting:masterfrom
Novoposarantus:fix/executedelete-leftjoin-mysql-1093
Open

Novoposarantus wants to merge 4 commits into
microting:masterfrom
Novoposarantus:fix/executedelete-leftjoin-mysql-1093

Conversation

@Novoposarantus

@Novoposarantus Novoposarantus commented May 15, 2026

Copy link
Copy Markdown

Problem

ExecuteDeleteAsync with a Where clause that traverses a navigation property (produces a LEFT JOIN) generates a DELETE ... WHERE id IN (SELECT id FROM same_table ...) subquery. MySQL 8.0 rejects this with Error 1093You can't specify target table for update in FROM clause.

Broken SQL (10.0.x)

DELETE FROM `Posts` AS `p`
WHERE `p`.`Id` IN (
    SELECT `p0`.`Id`
    FROM `Posts` AS `p0`
    LEFT JOIN `Blogs` AS `b` ON `p0`.`BlogId` = `b`.`Id`
    WHERE `b`.`Title` LIKE 'Arthur%'
)

Correct SQL (after fix)

DELETE `p`
FROM `Posts` AS `p`
LEFT JOIN `Blogs` AS `b` ON `p`.`BlogId` = `b`.`Id`
WHERE `b`.`Title` LIKE 'Arthur%'

Reproduction

// Any navigation-property predicate on ExecuteDeleteAsync triggers the bug:
await context.Posts
    .Where(p => p.Blog.Title.StartsWith("Arthur"))
    .ExecuteDeleteAsync();

Root Cause

MySqlQueryableMethodTranslatingExpressionVisitor.IsValidSelectExpressionForExecuteDelete only accepted InnerJoinExpression as extra tables. Navigation-property predicates produce LeftJoinExpression, so the method returned false, causing EF Core's base class to fall back to the subquery form.

The VisitDelete override in MySqlQuerySqlGenerator already handles LEFT JOINs correctly — it just never got reached.

Fix

One-line change in MySqlQueryableMethodTranslatingExpressionVisitor.cs:

- && selectExpression.Tables.Skip(1).All(t => t is InnerJoinExpression);
+ && selectExpression.Tables.Skip(1).All(t => t is InnerJoinExpression or LeftJoinExpression);

Test Updates

Tests previously expecting MySqlException (Error 1093) now assert the correct LEFT JOIN SQL:

  • NonSharedModelBulkUpdatesMySqlTest.Delete_predicate_based_on_optional_navigation
  • NorthwindBulkUpdatesMySqlTest.Delete_Where_using_navigation_2
  • NorthwindBulkUpdatesMySqlTest.Delete_Where_optional_navigation_predicate
  • NorthwindBulkUpdatesMySqlTest.Delete_with_LeftJoin
  • NorthwindBulkUpdatesMySqlTest.Delete_with_LeftJoin_via_flattened_GroupJoin

Note: SQL assertions for Delete_with_LeftJoin and Delete_with_LeftJoin_via_flattened_GroupJoin are best-guess inferences from sibling tests. Please verify against an actual MySQL 8.0 run and adjust aliases if needed.

Navigation property predicates in ExecuteDeleteAsync produce LeftJoinExpressions.
The previous guard only allowed InnerJoinExpression, causing EF Core to fall
back to a WHERE IN (SELECT ...) subquery form which MySQL 8.0 rejects with
Error 1093.

Fixes: ExecuteDeleteAsync with JOIN/navigation property generates invalid SQL
…LEFT JOIN SQL

After the IsValidSelectExpressionForExecuteDelete fix, MySQL now generates
DELETE ... LEFT JOIN ... instead of the invalid subquery form.
Delete_Where_using_navigation_2, Delete_Where_optional_navigation_predicate,
Delete_with_LeftJoin, and Delete_with_LeftJoin_via_flattened_GroupJoin now
generate valid DELETE ... LEFT JOIN ... form instead of throwing MySqlException
(Error 1093) or wrapping in a self-referencing EXISTS subquery.
Capture actual SQL from test run: generates the same LEFT JOIN subquery
pattern as Delete_with_LeftJoin.

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.

Copilot review overview

🔵 Needs a closer look

Two SQL baselines are unverified and must be captured from a MySQL 8.0 test run.

Review effort: Lite
Findings: None

What changed in this PR

Fixes MySQL Error 1093 for ExecuteDeleteAsync queries using navigation-property predicates.

Changes:

  • Allows LEFT JOIN expressions in delete translation.
  • Updates bulk-delete SQL expectations.
  • Requires verification of two inferred SQL baselines against MySQL 8.0.
File Summary
test/​EFCore.MySql.FunctionalTests/​BulkUpdates/​NorthwindBulkUpdatesMySqlTest.cs Updates navigation and left-join delete assertions.
test/​EFCore.MySql.FunctionalTests/​BulkUpdates/​NonSharedModelBulkUpdatesMySqlTest.cs Updates optional-navigation delete expectations.
src/​EFCore.MySql/​Query/​Internal/​MySqlQueryableMethodTranslatingExpressionVisitor.cs Enables delete translation with left joins.

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants