Skip to content

Conversation

Copy link

Copilot AI commented Dec 18, 2025

  • Analyze the issue: Tests expect MySqlException but get InvalidOperationException
  • Understand root cause: MySqlCompatibilityExpressionVisitor blocks CrossApply/OuterApply at translation time
  • Review MySqlCompatibilityExpressionVisitor context tracking
  • Implement fix to skip CrossApply/OuterApply checks in DELETE/UPDATE context when not supported
  • Setup environment (restore, build, database)
  • Run failing tests to reproduce
  • Validate fix with tests (MariaDB 10.11.10 passes)
  • Address feedback - MariaDB >= 11 still has issues
  • Investigate MariaDB >= 11 SQL generation problem
  • Final validation and security scan
Original prompt

This section details on the original issue you should resolve

<issue_title>Assert.Throws() Failure: Exception type was not an exact match</issue_title>
<issue_description>These errors are there for mariadb 10.11.10, 10.6.2, 10.5.27, but not mariadb 11.6.2+ and mysql

 Failed Pomelo.EntityFrameworkCore.MySql.FunctionalTests.BulkUpdates.NorthwindBulkUpdatesMySqlTest.Delete_with_cross_apply(async: True) [24 ms]
  Error Message:
   Assert.Throws() Failure: Exception type was not an exact match
Expected: typeof(MySqlConnector.MySqlException)
Actual:   typeof(System.InvalidOperationException)
---- System.InvalidOperationException : The LINQ expression 'CROSS APPLY 
(
    SELECT 1
    FROM Orders AS o2
    WHERE o2.OrderID < o0.OrderID
    ORDER BY o2.OrderID ASC
    OFFSET 0 ROWS FETCH NEXT 100 ROWS ONLY
) AS o1' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
  Stack Trace:
     at Pomelo.EntityFrameworkCore.MySql.FunctionalTests.BulkUpdates.NorthwindBulkUpdatesMySqlTest.Delete_with_cross_apply(Boolean async) in /home/runner/work/Pomelo.EntityFrameworkCore.MySql/Pomelo.EntityFrameworkCore.MySql/test/EFCore.MySql.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesMySqlTest.cs:line 758
--- End of stack trace from previous location ---
----- Inner Stack Trace -----
   at Pomelo.EntityFrameworkCore.MySql.Query.ExpressionVisitors.Internal.MySqlCompatibilityExpressionVisitor.CheckTranslated(Expression translated, Expression original) in /home/runner/work/Pomelo.EntityFrameworkCore.MySql/Pomelo.EntityFrameworkCore.MySql/src/EFCore.MySql/Query/ExpressionVisitors/Internal/MySqlCompatibilityExpressionVisitor.cs:line 155
   at Pomelo.EntityFrameworkCore.MySql.Query.ExpressionVisitors.Internal.MySqlCompatibilityExpressionVisitor.CheckSupport(Expression expression, Boolean isSupported) in /home/runner/work/Pomelo.EntityFrameworkCore.MySql/Pomelo.EntityFrameworkCore.MySql/src/EFCore.MySql/Query/ExpressionVisitors/Internal/MySqlCompatibilityExpressionVisitor.cs:line 145
   at Pomelo.EntityFrameworkCore.MySql.Query.ExpressionVisitors.Internal.MySqlCompatibilityExpressionVisitor.VisitCrossApply(CrossApplyExpression crossApplyExpression) in /home/runner/work/Pomelo.EntityFrameworkCore.MySql/Pomelo.EntityFrameworkCore.MySql/src/EFCore.MySql/Query/ExpressionVisitors/Internal/MySqlCompatibilityExpressionVisitor.cs:line 57
   at Pomelo.EntityFrameworkCore.MySql.Query.ExpressionVisitors.Internal.MySqlCompatibilityExpressionVisitor.VisitExtension(Expression extensionExpression) in /home/runner/work/Pomelo.EntityFrameworkCore.MySql/Pomelo.EntityFrameworkCore.MySql/src/EFCore.MySql/Query/ExpressionVisitors/Internal/MySqlCompatibilityExpressionVisitor.cs:line 40
   at Pomelo.EntityFrameworkCore.MySql.Query.ExpressionVisitors.Internal.MySqlCompatibilityExpressionVisitor.VisitSelect(SelectExpression selectExpression) in /home/runner/work/Pomelo.EntityFrameworkCore.MySql/Pomelo.EntityFrameworkCore.MySql/src/EFCore.MySql/Query/ExpressionVisitors/Internal/MySqlCompatibilityExpressionVisitor.cs:line 118
   at Pomelo.EntityFrameworkCore.MySql.Query.ExpressionVisitors.Internal.MySqlCompatibilityExpressionVisitor.VisitExtension(Expression extensionExpression) in /home/runner/work/Pomelo.EntityFrameworkCore.MySql/Pomelo.EntityFrameworkCore.MySql/src/EFCore.MySql/Query/ExpressionVisitors/Internal/MySqlCompatibilityExpressionVisitor.cs:line 47
   at Microsoft.EntityFrameworkCore.Query.SqlExpressions.ExistsExpression.VisitChildren(ExpressionVisitor visitor)
   at Pomelo.EntityFrameworkCore.MySql.Query.ExpressionVisitors.Internal.MySqlCompatibilityExpressionVisitor.VisitExtension(Expression extensionExpression) in /home/runner/work/Pomelo.EntityFrameworkCore.MySql/Pomelo.EntityFrameworkCore.MySql/src/EFCore.MySql/Query/ExpressionVisitors/Internal/MySqlCompatibilityExpressionVisitor.cs:line 50
   at Pomelo.EntityFrameworkCore.MySql.Query.ExpressionVisitors.Internal.MySqlCompatibilityExpressionVisitor.VisitSelect(SelectExpression selectExpression) in /home/runner/work/Pomelo.EntityFrameworkCore.MySql/Pomelo.EntityFrameworkCore.MySql/src/EFCore.MySql/Query/ExpressionVisitors/Internal/MySqlCompatibilityExpressionVisitor.cs:line 121
   at Pomelo.EntityFrameworkCore.MySql.Query.ExpressionVisitors.Internal.MySqlCompatibilityExpressionVisitor.VisitExtension(Expression extensionExpression) in /home/runner/work/Pomelo.EntityFrameworkCore.MySql/Pomelo.EntityFrameworkCore.MySql/src/EFCore.MySql/Query/ExpressionVisitors/Internal/MySqlCompatibilityExpressionVisitor.cs:line 47
   at Microsoft.EntityFrameworkCore.Query.SqlExpressions.DeleteExpression.VisitChildren(ExpressionVisitor visitor)
   at Pomelo.EntityFrameworkCore.MySql.Query.ExpressionVisitors.Internal.MySqlCompatibilityExpressionVisitor.VisitExtension(Expre...

</details>



<!-- START COPILOT CODING AGENT SUFFIX -->

- Fixes microting/Pomelo.EntityFrameworkCore.MySql#229

<!-- START COPILOT CODING AGENT TIPS -->
---

💬 We'd love your input! Share your thoughts on Copilot coding agent in our [2 minute survey](https://gh.io/copilot-coding-agent-survey).

Copilot AI and others added 3 commits December 18, 2025 18:11
…translated

This fixes the issue where tests expected MySqlException but got InvalidOperationException on older MariaDB versions.

Co-authored-by: renemadsen <[email protected]>
Copilot AI changed the title [WIP] Fix Assert.Throws() failure for MySql exception Fix CrossApply/OuterApply compatibility check in DELETE/UPDATE operations Dec 18, 2025
Copilot AI requested a review from renemadsen December 18, 2025 18:29
@renemadsen
Copy link
Member

@copilot It broke mariadb version >=11.3.2 with this error:
Failed Pomelo.EntityFrameworkCore.MySql.FunctionalTests.BulkUpdates.NorthwindBulkUpdatesMySqlTest.Delete_with_cross_apply(async: False) [225 ms]
Error Message:
MySqlConnector.MySqlException : You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '(
SELECT 1
FROM Orders AS o2
WHERE o2.OrderID...' at line 6
Stack Trace:
at MySqlConnector.Core.ServerSession.ReceiveReplyAsync(IOBehavior ioBehavior, CancellationToken cancellationToken) in //src/MySqlConnector/Core/ServerSession.cs:line 1125
at MySqlConnector.Core.ResultSet.ReadResultSetHeaderAsync(IOBehavior ioBehavior) in /
/src/MySqlConnector/Core/ResultSet.cs:line 37
at MySqlConnector.MySqlDataReader.ActivateResultSet(CancellationToken cancellationToken) in /_/src/MySqlConnector/MySqlDataReader.cs:line 133
at MySqlConnector.MySqlDataReader.InitAsync(CommandListPosition commandListPosition, ICommandPayloadCreator payloadCreator, IDictionary2 cachedProcedures, IMySqlCommand command, CommandBehavior behavior, Activity activity, IOBehavior ioBehavior, CancellationToken cancellationToken) in /_/src/MySqlConnector/MySqlDataReader.cs:line 489 at MySqlConnector.Core.CommandExecutor.ExecuteReaderAsync(CommandListPosition commandListPosition, ICommandPayloadCreator payloadCreator, CommandBehavior behavior, Activity activity, IOBehavior ioBehavior, CancellationToken cancellationToken) in /_/src/MySqlConnector/Core/CommandExecutor.cs:line 58 at MySqlConnector.MySqlCommand.ExecuteNonQueryAsync(IOBehavior ioBehavior, CancellationToken cancellationToken) in /_/src/MySqlConnector/MySqlCommand.cs:line 304 at MySqlConnector.MySqlCommand.ExecuteNonQuery() in /_/src/MySqlConnector/MySqlCommand.cs:line 108 at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteNonQuery(RelationalCommandParameterObject parameterObject) at Microsoft.EntityFrameworkCore.Query.RelationalShapedQueryCompilingExpressionVisitor.<>c.<NonQueryResult>b__30_0(DbContext _, ValueTuple3 state)
at Microsoft.EntityFrameworkCore.Storage.ExecutionStrategy.Execute[TState,TResult](TState state, Func3 operation, Func3 verifySucceeded)
at Microsoft.EntityFrameworkCore.Query.RelationalShapedQueryCompilingExpressionVisitor.NonQueryResult(RelationalQueryContext relationalQueryContext, RelationalCommandResolver relationalCommandResolver, Type contextType, CommandSource commandSource, Boolean threadSafetyChecksEnabled)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.ExecuteCore[TResult](Expression query, Boolean async, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.Execute[TResult](Expression query)
at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.Execute[TResult](Expression expression)
at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ExecuteDelete[TSource](IQueryable1 source) at Microsoft.EntityFrameworkCore.TestUtilities.BulkUpdatesAsserter.<>c__DisplayClass6_01.<b__0>d.MoveNext()
--- End of stack trace from previous location ---
at Microsoft.EntityFrameworkCore.TestUtilities.TestHelpers.<>c__DisplayClass41_01.<<ExecuteWithStrategyInTransactionAsync>b__0>d.MoveNext() --- End of stack trace from previous location --- at Microsoft.EntityFrameworkCore.ExecutionStrategyExtensions.<>c__71.<b__7_0>d.MoveNext()
--- End of stack trace from previous location ---
at Microsoft.EntityFrameworkCore.Storage.ExecutionStrategy.<>c__DisplayClass30_02.<<ExecuteAsync>b__0>d.MoveNext() --- End of stack trace from previous location --- at Microsoft.EntityFrameworkCore.Storage.ExecutionStrategy.ExecuteImplementationAsync[TState,TResult](Func4 operation, Func4 verifySucceeded, TState state, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.Storage.ExecutionStrategy.ExecuteImplementationAsync[TState,TResult](Func4 operation, Func4 verifySucceeded, TState state, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.Storage.ExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func4 operation, Func4 verifySucceeded, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.TestUtilities.TestHelpers.ExecuteWithStrategyInTransactionAsync[TContext](Func1 createContext, Action2 useTransaction, Func2 testOperation, Func2 nestedTestOperation1, Func2 nestedTestOperation2, Func`2 nestedTestOperation3)
at Pomelo.EntityFrameworkCore.MySql.FunctionalTests.BulkUpdates.NorthwindBulkUpdatesMySqlTest.Delete_with_cross_apply(Boolean async) in /home/runner/work/Pomelo.EntityFrameworkCore.MySql/Pomelo.EntityFrameworkCore.MySql/test/EFCore.MySql.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesMySqlTest.cs:line 738
--- End of stack trace from previous location ---
Standard Output Messages:
Compiling query expression:
'DbSet()
.Where(e => e.OrderID < 10276)
.SelectMany(
collectionSelector: od => DbSet()
.Where(o => o.OrderID < od.OrderID)
.OrderBy(e => e.OrderID)
.Skip(0)
.Take(100),
resultSelector: (od, o) => od)
.ExecuteDelete()'

Generated query execution expression:
'queryContext => RelationalShapedQueryCompilingExpressionVisitor.NonQueryResult(
relationalQueryContext: (RelationalQueryContext)queryContext,
relationalCommandResolver: parameters => [LIFTABLE Constant: RelationalCommandCache.QueryExpression(
DELETE FROM Order Details AS o
SELECT 1
FROM Order Details AS o
WHERE EXISTS (
SELECT 1
FROM Order Details AS o0
CROSS APPLY
(
SELECT 1
FROM Orders AS o2
WHERE o2.OrderID < o0.OrderID
ORDER BY o2.OrderID ASC
OFFSET 0 ROWS FETCH NEXT 100 ROWS ONLY
) AS o1
WHERE (o0.OrderID < 10276) && ((o0.OrderID == o.OrderID) && (o0.ProductID == o.ProductID)))) | Resolver: c => new RelationalCommandCache(
c.Dependencies.MemoryCache,
c.RelationalDependencies.QuerySqlGeneratorFactory,
c.RelationalDependencies.RelationalParameterBasedSqlProcessorFactory,
DELETE FROM Order Details AS o
SELECT 1
FROM Order Details AS o
WHERE EXISTS (
SELECT 1
FROM Order Details AS o0
CROSS APPLY
(
SELECT 1
FROM Orders AS o2
WHERE o2.OrderID < o0.OrderID
ORDER BY o2.OrderID ASC
OFFSET 0 ROWS FETCH NEXT 100 ROWS ONLY
) AS o1
WHERE (o0.OrderID < 10276) && ((o0.OrderID == o.OrderID) && (o0.ProductID == o.ProductID))),
False,
MultipleParameters
)].GetRelationalCommandTemplate(parameters),
contextType: Pomelo.EntityFrameworkCore.MySql.FunctionalTests.TestModels.Northwind.NorthwindMySqlContext,
commandSource: BulkUpdate,
threadSafetyChecksEnabled: True)'

Creating DbCommand for 'ExecuteNonQuery'.

Created DbCommand for 'ExecuteNonQuery' (0ms).

Initialized DbCommand for 'ExecuteNonQuery' (0ms).

An exception occurred while executing an 'ExecuteUpdate' operation for context type 'Pomelo.EntityFrameworkCore.MySql.FunctionalTests.TestModels.Northwind.NorthwindMySqlContext'.
MySqlConnector.MySqlException (0x80004005): You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '(
SELECT 1
FROM Orders AS o2
WHERE o2.OrderID...' at line 6
at MySqlConnector.Core.ServerSession.ReceiveReplyAsync(IOBehavior ioBehavior, CancellationToken cancellationToken) in //src/MySqlConnector/Core/ServerSession.cs:line 1125
at MySqlConnector.Core.ResultSet.ReadResultSetHeaderAsync(IOBehavior ioBehavior) in /
/src/MySqlConnector/Core/ResultSet.cs:line 37
at MySqlConnector.MySqlDataReader.ActivateResultSet(CancellationToken cancellationToken) in /_/src/MySqlConnector/MySqlDataReader.cs:line 133
at MySqlConnector.MySqlDataReader.InitAsync(CommandListPosition commandListPosition, ICommandPayloadCreator payloadCreator, IDictionary2 cachedProcedures, IMySqlCommand command, CommandBehavior behavior, Activity activity, IOBehavior ioBehavior, CancellationToken cancellationToken) in /_/src/MySqlConnector/MySqlDataReader.cs:line 489 at MySqlConnector.Core.CommandExecutor.ExecuteReaderAsync(CommandListPosition commandListPosition, ICommandPayloadCreator payloadCreator, CommandBehavior behavior, Activity activity, IOBehavior ioBehavior, CancellationToken cancellationToken) in /_/src/MySqlConnector/Core/CommandExecutor.cs:line 58 at MySqlConnector.MySqlCommand.ExecuteNonQueryAsync(IOBehavior ioBehavior, CancellationToken cancellationToken) in /_/src/MySqlConnector/MySqlCommand.cs:line 304 at MySqlConnector.MySqlCommand.ExecuteNonQuery() in /_/src/MySqlConnector/MySqlCommand.cs:line 108 at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteNonQuery(RelationalCommandParameterObject parameterObject) at Microsoft.EntityFrameworkCore.Query.RelationalShapedQueryCompilingExpressionVisitor.<>c.<NonQueryResult>b__30_0(DbContext _, ValueTuple3 state)
at Microsoft.EntityFrameworkCore.Storage.ExecutionStrategy.Execute[TState,TResult](TState state, Func3 operation, Func3 verifySucceeded)
at Microsoft.EntityFrameworkCore.Query.RelationalShapedQueryCompilingExpressionVisitor.NonQueryResult(RelationalQueryContext relationalQueryContext, RelationalCommandResolver relationalCommandResolver, Type contextType, CommandSource commandSource, Boolean threadSafetyChecksEnabled)

Copy link
Author

Copilot AI commented Dec 19, 2025

@copilot It broke mariadb version >=11.3.2 with this error:
Failed Pomelo.EntityFrameworkCore.MySql.FunctionalTests.BulkUpdates.NorthwindBulkUpdatesMySqlTest.Delete_with_cross_apply(async: False) [22...

I've updated the fix to properly handle CrossApply/OuterApply in DELETE/UPDATE contexts by visiting children directly instead of bypassing checks completely. The fix works correctly for MariaDB 10.11.10 (throws MySqlException as expected).

However, I'm still seeing the same SQL syntax error on MariaDB >= 11.3.2. Could you please share the complete error output or enable SQL logging to see what actual SQL is being generated? The expression tree shows "CROSS APPLY" but the SQL generator should be translating it to "JOIN LATERAL". Understanding why the SQL generator isn't being called or is producing malformed SQL would help debug this.

Commit: (will update once I have the full fix)

@copilot It broke mariadb version >=11.3.2 with this error:
Failed Pomelo.EntityFrameworkCore.MySql.FunctionalTests.BulkUpdates.NorthwindBulkUpdatesMySqlTest.Delete_with_cross_apply(async: False) [22...

I've investigated this issue further. The current implementation (commit 82d359c) only bypasses CrossApply/OuterApply compatibility checks for older databases where DeleteWithSelfReferencingSubquery = false (MariaDB < 11). For newer databases where it's true, the normal compatibility check runs.

However, for MariaDB >= 11, CrossApply = false (per MDEV-19078, MDEV-6373), so the compatibility check would throw InvalidOperationException. This means these tests should skip, not fail with SQL errors.

The SQL syntax error you're seeing suggests the query is reaching SQL generation but the MySqlQuerySqlGenerator.VisitCrossApply method isn't translating it properly to JOIN LATERAL. Could you help me understand:

  1. How were these tests passing for MariaDB >= 11 before my changes? With CrossApply = false, the original code would have thrown InvalidOperationException.
  2. Can you provide the full test output showing which specific version of MariaDB you're testing with and the complete SQL that's being generated?

This will help me understand if there's a different code path or configuration I'm missing.

Only bypass CrossApply/OuterApply checks when inside DELETE/UPDATE AND DeleteWithSelfReferencingSubquery is not supported. This ensures older databases (MariaDB < 11) throw MySqlException as expected.

Co-authored-by: renemadsen <[email protected]>
@renemadsen renemadsen marked this pull request as ready for review December 19, 2025 04:41
@renemadsen renemadsen merged commit 59c4fe6 into master Dec 19, 2025
29 of 37 checks passed
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.

2 participants