Conversation
| // data twice. | ||
| // Note that if the type mapping differs, we do send the same data twice (e.g. the same string may be sent once as Unicode, once as | ||
| // non-Unicode). | ||
| // TODO: Note that we perform Equals comparison on the value converter. We should be able to do reference comparison, but for |
| /// </summary> | ||
| /// <param name="selectExpression">The <see cref="SelectExpression" /> to check for ordering.</param> | ||
| /// <returns>Whether <paramref name="selectExpression"/> is ordered.</returns> | ||
| protected virtual bool IsOrdered(SelectExpression selectExpression) |
There was a problem hiding this comment.
Added this for PostgreSQL, where the results of unnest are guaranteed to be ordered without any explicit ORDER BY; this allows us to avoid the warning for Skip/Take over unordered collection.
| } | ||
| } | ||
|
|
||
| private sealed class SqlTypeMappingVerifyingExpressionVisitor : ExpressionVisitor |
There was a problem hiding this comment.
Note that this verification - that all SqlExpressions have a mapping - has been moved from the end of each individual SQL translations (of which there are potentially several in the query) to the end of the entire query. This is necessary since some mappings are inferred later.
| /// not used in application code. | ||
| /// </para> | ||
| /// </summary> | ||
| public class RowValueExpression : SqlExpression |
There was a problem hiding this comment.
This will also provide infrastructure for other uses of row values, e.g. #26822
| public virtual IStoreFunction? StoreFunction { get; } | ||
|
|
||
| /// <inheritdoc /> | ||
| ITableBase? ITableBasedExpression.Table |
There was a problem hiding this comment.
Before this PR, TableValuedFunctionExpression could only be used with a store function from the model; this changes it to be usable for any TVF, without any necessary model data on it (e.g. OpenJson).
| // 3. The element CLR type has a supported type mapping which isn't itself a collection (nested collections not yet supported). | ||
|
|
||
| // Note that e.g. Newtonsoft.Json's JToken is enumerable over itself, exclude that scenario to avoid stack overflow. | ||
| if (mappingInfo.ClrType?.TryGetElementType(typeof(IEnumerable<>)) is not { } elementClrType |
There was a problem hiding this comment.
@ajcvickers @AndriySvyryd this is another place where we need some type mapping infra (#30730) - at least some parts of this function should probably be in core/relational; we should also consider having providers just support collections via JSON arrays by default, without them needing to actually do anything (but allow Npgsql to override).
Note also the annoying construction of the collection type mapping below, with the cloning etc.
| /// any release. You should only use it directly in your code with extreme caution and knowing that | ||
| /// doing so can result in application failures when updating to a new Entity Framework Core release. | ||
| /// </summary> | ||
| protected override ShapedQueryExpression? TranslateCount(ShapedQueryExpression source, LambdaExpression? predicate) |
There was a problem hiding this comment.
Note this specialized translation of LINQ Count() when applied to a primitive collection (JSON string); instead of using json_each to convert to a table and use a SQL scalar subquery to do COUNT(*), we just use a dedicated function for that.
Npgsql has a ton of such specialized pattern matching translations - it's really nice. See roji/efcore.pg@83842cf#diff-710c2323c08f93bcf612ff7124133abf291f46900fcf8f0adc89eaf5709d22e9R119 and below.
| jsonEachExpression, columnName: "value", columnType: elementClrType, columnTypeMapping: elementTypeMapping, | ||
| isColumnNullable: null); | ||
|
|
||
| // TODO: SQLite does have REAL and BLOB types, which JSON does not. Need to possibly cast to that. |
|
|
||
| // The "standard" JSON timestamp representation is ISO8601, with a T between date and time; but SQLite's representation has | ||
| // no T. Apply a conversion on the value coming out of json_each. | ||
| SqliteDateTimeTypeMapping => _sqlExpressionFactory.Function( |
| /// <summary> | ||
| /// A value converter that converts a .NET primitive collection into a JSON string. | ||
| /// </summary> | ||
| // TODO: This currently just calls JsonSerialize.Serialize/Deserialize. It should go through the element type mapping's APIs for |
527ca05 to
642e5d7
Compare
src/EFCore.SqlServer/Infrastructure/SqlServerDbContextOptionsBuilder.cs
Outdated
Show resolved
Hide resolved
src/EFCore.SqlServer/Infrastructure/SqlServerDbContextOptionsBuilder.cs
Outdated
Show resolved
Hide resolved
| // So we clone the expression to avoid any side-effects, but that also prevents us from directly comparing columns (since | ||
| // the ValuesExpression has been cloned too). | ||
| // We should simply be able to pattern-match directly on the projection mappings (currently private) without cloning/applying | ||
| var clonedSelect = selectExpression.Clone(); |
There was a problem hiding this comment.
@maumar note this change - I think this aligns with how things are supposed to happen in the query pipeline.
Instead of cloning and applying the projection on the SelectExpression in order to check its projections, the right thing seems to be to look at the ShaperExpression instead, and use that the resolve the projection from the selectExpression - without needing to apply projections.
There was a problem hiding this comment.
apply projection has some really complicated logic, for stuff like group by or general subquery projection, so that might not be a generally safe approach. But for simple pattern match like here, it should be perfectly fine
There was a problem hiding this comment.
OK. Though I did remove cloning and applying the projection even for this simpler case - I now access the projection via the shaper instead (via selectExpression.GetProjection(projectionBindingExpression)). It feels more correct/safer (and even more efficient as we don't clone).
I do think we can probably improve things for this sort of pattern-matching - but we can think about it later.
src/EFCore.SqlServer/Infrastructure/Internal/SqlServerOptionsExtension.cs
Show resolved
Hide resolved
AndriySvyryd
left a comment
There was a problem hiding this comment.
Non-query parts look good enough.
src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs
Outdated
Show resolved
Hide resolved
...Core.SqlServer.FunctionalTests/Query/ComplexNavigationsCollectionsSplitQuerySqlServerTest.cs
Show resolved
Hide resolved
...Relational.Specification.Tests/Query/NonSharedPrimitiveCollectionsQueryRelationalTestBase.cs
Show resolved
Hide resolved
04e0025 to
6f77314
Compare
src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs
Outdated
Show resolved
Hide resolved
|
query stuff looking good as well! 🚀 |
Closes dotnet#29427 Closes dotnet#30426 Closes dotnet#13617
6f77314 to
95120af
Compare
Implements the bulk of #30731
Closes #29427
Closes #30426
Closes #13617
Closes #30163