@@ -52,6 +52,7 @@ protected MySqlQueryableMethodTranslatingExpressionVisitor(
5252 {
5353 _sqlExpressionFactory = parentVisitor . _sqlExpressionFactory ;
5454 _typeMappingSource = parentVisitor . _typeMappingSource ;
55+ _sqlAliasManager = parentVisitor . _sqlAliasManager ;
5556 _options = parentVisitor . _options ;
5657 }
5758
@@ -226,38 +227,11 @@ protected override ShapedQueryExpression TranslateElementAtOrDefault(
226227
227228 protected override ShapedQueryExpression TransformJsonQueryToTable ( JsonQueryExpression jsonQueryExpression )
228229 {
229- // TODO: Implement JSON_TABLE support for structural types (entities/complex types) in JSON collections.
230- //
231- // Current Status:
232- // - TransformJsonQueryToTable implementation is complete and matches Npgsql pattern
233- // - JSON_TABLE syntax and COLUMNS clause generation is correct
234- // - Issue is in EF Core base SelectExpression.AddCrossJoin or MySQL SQL generator
235- // - When TranslateSelectMany calls AddCrossJoin, the CROSS JOIN keyword is not generated
236- // - This results in invalid SQL: "FROM table1 JSON_TABLE(...)" instead of "FROM table1 CROSS JOIN JSON_TABLE(...)"
237- //
238- // Investigation completed:
239- // - Npgsql uses identical CreateSelect pattern and it works for PostgreSQL
240- // - MySQL supports both comma and CROSS JOIN syntax with JSON_TABLE (manually verified)
241- // - The bug is in query assembly, not in provider-specific logic
242- // - Requires either: override TranslateSelectMany, patch EF Core AddCrossJoin, or fix MySQL SQL generator
243- //
244- // Partial implementation preserved below for reference (currently commented out).
245- // See commits: 11dc6b2, e17a1e9, 4b80703 for full implementation details.
246-
247- // For now, throw a clear exception to inform users this is not yet supported
248- throw new InvalidOperationException (
249- "Composing LINQ operators (such as SelectMany) over collections of structural types inside JSON documents " +
250- "is not currently supported by the MySQL provider. This feature requires fixes in EF Core's query assembly " +
251- "logic or MySQL-specific SQL generation. As a workaround, consider materializing the JSON data to the client " +
252- "using .AsEnumerable() or .ToList() before performing collection operations." ) ;
253-
254- /* PARTIAL IMPLEMENTATION - PRESERVED FOR FUTURE WORK
255-
256230 // Calculate the table alias for the JSON_TABLE function based on the last named path segment
257231 // (or the JSON column name if there are none)
258- var lastNamedPathSegment = jsonQueryExpression.Path.LastOrDefault(ps => ps.PropertyName is not null);
232+ var lastNamedPathSegmentPropertyName = jsonQueryExpression . Path . LastOrDefault ( ps => ps . PropertyName is not null ) . PropertyName ;
259233 var tableAlias = _sqlAliasManager . GenerateTableAlias (
260- lastNamedPathSegment.PropertyName ?? jsonQueryExpression.JsonColumn.Name);
234+ lastNamedPathSegmentPropertyName ?? jsonQueryExpression . JsonColumn . Name ) ;
261235
262236 var jsonTypeMapping = jsonQueryExpression . JsonColumn . TypeMapping ! ;
263237
@@ -273,8 +247,8 @@ protected override ShapedQueryExpression TransformJsonQueryToTable(JsonQueryExpr
273247 new MySqlJsonTableExpression . ColumnInfo (
274248 Name : jsonPropertyName ,
275249 TypeMapping : property . GetRelationalTypeMapping ( ) ,
276- // Path for JSON_TABLE: $[0] to access array element properties
277- Path: [new PathSegment(_sqlExpressionFactory.Constant(0, _typeMappingSource.FindMapping(typeof(int))) )],
250+ // Path for JSON_TABLE COLUMNS: each row is already an array element, so access properties directly
251+ Path : [ new PathSegment ( jsonPropertyName ) ] ,
278252 AsJson : false ) ) ;
279253 }
280254 }
@@ -295,7 +269,7 @@ protected override ShapedQueryExpression TransformJsonQueryToTable(JsonQueryExpr
295269 new MySqlJsonTableExpression . ColumnInfo (
296270 Name : jsonNavigationName ,
297271 TypeMapping : jsonTypeMapping ,
298- Path: [new PathSegment(_sqlExpressionFactory.Constant(0, _typeMappingSource.FindMapping(typeof(int))) )],
272+ Path : [ new PathSegment ( jsonNavigationName ) ] ,
299273 AsJson : true ) ) ;
300274 }
301275 break ;
@@ -310,7 +284,7 @@ protected override ShapedQueryExpression TransformJsonQueryToTable(JsonQueryExpr
310284 new MySqlJsonTableExpression . ColumnInfo (
311285 Name : jsonPropertyName ,
312286 TypeMapping : jsonTypeMapping ,
313- Path: [new PathSegment(_sqlExpressionFactory.Constant(0, _typeMappingSource.FindMapping(typeof(int))) )],
287+ Path : [ new PathSegment ( jsonPropertyName ) ] ,
314288 AsJson : true ) ) ;
315289 }
316290 break ;
@@ -319,10 +293,10 @@ protected override ShapedQueryExpression TransformJsonQueryToTable(JsonQueryExpr
319293 throw new UnreachableException ( ) ;
320294 }
321295
322- // MySQL JSON_TABLE requires the nested JSON document as raw JSON (not extracted as a scalar value) .
323- // We need to use JSON_EXTRACT (not JSON_VALUE) to get the JSON fragment with proper structure.
324- // Unlike Npgsql which can use JsonScalarExpression (translates to json extraction in PostgreSQL),
325- // MySQL's JsonScalarExpression translates to JSON_VALUE which strips quotes and can't feed JSON_TABLE .
296+ // json_to_recordset in Npgsql uses JsonScalarExpression which PostgreSQL handles properly .
297+ // However, MySQL's JsonScalarExpression translates to JSON_VALUE which extracts scalar values
298+ // and strips JSON structure. For JSON_TABLE, we need the raw JSON fragment.
299+ // Use JSON_EXTRACT explicitly to get the nested JSON array/object .
326300
327301 SqlExpression jsonSource ;
328302 if ( jsonQueryExpression . Path . Count > 0 )
@@ -341,7 +315,7 @@ protected override ShapedQueryExpression TransformJsonQueryToTable(JsonQueryExpr
341315 }
342316 }
343317
344- // Use JSON_EXTRACT to get the nested JSON document (not JSON_VALUE which extracts scalars)
318+ // Use JSON_EXTRACT to get the nested JSON document with structure intact
345319 jsonSource = _sqlExpressionFactory . Function (
346320 "JSON_EXTRACT" ,
347321 [ jsonQueryExpression . JsonColumn , _sqlExpressionFactory . Constant ( pathBuilder . ToString ( ) ) ] ,
@@ -386,7 +360,6 @@ [new PathSegment(_sqlExpressionFactory.Constant("*", RelationalTypeMapping.NullM
386360 new ProjectionMember ( ) ,
387361 typeof ( ValueBuffer ) ) ,
388362 false ) ) ;
389- */
390363 }
391364
392365 protected override ShapedQueryExpression TranslatePrimitiveCollection ( SqlExpression sqlExpression , IProperty property , string tableAlias )
0 commit comments