forked from npgsql/efcore.pg
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Support queryable array parameters and columns
Closes npgsql#2677
- Loading branch information
Showing
29 changed files
with
2,201 additions
and
321 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
98 changes: 98 additions & 0 deletions
98
src/EFCore.PG/Query/Expressions/Internal/PostgresArraySliceExpression.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
namespace Npgsql.EntityFrameworkCore.PostgreSQL.Query.Expressions.Internal; | ||
|
||
/// <summary> | ||
/// A SQL expression that represents a slicing into a PostgreSQL array (e.g. array[2:3]). | ||
/// </summary> | ||
/// <remarks> | ||
/// <see href="https://www.postgresql.org/docs/current/arrays.html#ARRAYS-ACCESSING" />. | ||
/// </remarks> | ||
public class PostgresArraySliceExpression : SqlExpression, IEquatable<PostgresArraySliceExpression> | ||
{ | ||
/// <summary> | ||
/// The array being sliced. | ||
/// </summary> | ||
public virtual SqlExpression Array { get; } | ||
|
||
/// <summary> | ||
/// The lower bound of the slice. | ||
/// </summary> | ||
public virtual SqlExpression? LowerBound { get; } | ||
|
||
/// <summary> | ||
/// The upper bound of the slice. | ||
/// </summary> | ||
public virtual SqlExpression? UpperBound { get; } | ||
|
||
/// <summary> | ||
/// Creates a new instance of the <see cref="PostgresArraySliceExpression" /> class. | ||
/// </summary> | ||
/// <param name="array">The array tp slice into.</param> | ||
/// <param name="lowerBound">The lower bound of the slice.</param> | ||
/// <param name="upperBound">The upper bound of the slice.</param> | ||
public PostgresArraySliceExpression( | ||
SqlExpression array, | ||
SqlExpression? lowerBound, | ||
SqlExpression? upperBound) | ||
: base(array.Type, array.TypeMapping) | ||
{ | ||
Check.NotNull(array, nameof(array)); | ||
|
||
if (lowerBound is null && upperBound is null) | ||
{ | ||
throw new ArgumentException("At least one of lowerBound or upperBound must be provided"); | ||
} | ||
|
||
Array = array; | ||
LowerBound = lowerBound; | ||
UpperBound = upperBound; | ||
} | ||
|
||
/// <summary> | ||
/// Creates a new expression that is like this one, but using the supplied children. If all of the children are the same, it will | ||
/// return this expression. | ||
/// </summary> | ||
/// <param name="array">The <see cref="Array" /> property of the result.</param> | ||
/// <param name="lowerBound">The lower bound of the slice.</param> | ||
/// <param name="upperBound">The upper bound of the slice.</param> | ||
/// <returns>This expression if no children changed, or an expression with the updated children.</returns> | ||
public virtual PostgresArraySliceExpression Update(SqlExpression array, SqlExpression? lowerBound, SqlExpression? upperBound) | ||
=> array == Array && lowerBound == LowerBound && upperBound == UpperBound | ||
? this | ||
: new PostgresArraySliceExpression(array, lowerBound, upperBound); | ||
|
||
/// <inheritdoc /> | ||
protected override Expression VisitChildren(ExpressionVisitor visitor) | ||
=> Update( | ||
(SqlExpression)visitor.Visit(Array), | ||
(SqlExpression?)visitor.Visit(LowerBound), | ||
(SqlExpression?)visitor.Visit(UpperBound)); | ||
|
||
/// <inheritdoc /> | ||
public virtual bool Equals(PostgresArraySliceExpression? other) | ||
=> ReferenceEquals(this, other) | ||
|| other is not null | ||
&& base.Equals(other) | ||
&& Array.Equals(other.Array) | ||
&& (LowerBound is null ? other.LowerBound is null : LowerBound.Equals(other.LowerBound)) | ||
&& (UpperBound is null ? other.UpperBound is null : UpperBound.Equals(other.UpperBound)); | ||
|
||
/// <inheritdoc /> | ||
public override bool Equals(object? obj) => obj is PostgresArraySliceExpression e && Equals(e); | ||
|
||
/// <inheritdoc /> | ||
public override int GetHashCode() => HashCode.Combine(base.GetHashCode(), Array, LowerBound, UpperBound); | ||
|
||
/// <inheritdoc /> | ||
protected override void Print(ExpressionPrinter expressionPrinter) | ||
{ | ||
expressionPrinter.Visit(Array); | ||
expressionPrinter.Append("["); | ||
expressionPrinter.Visit(LowerBound); | ||
expressionPrinter.Append(":"); | ||
expressionPrinter.Visit(UpperBound); | ||
expressionPrinter.Append("]"); | ||
} | ||
|
||
/// <inheritdoc /> | ||
public override string ToString() => $"{Array}[{LowerBound}:{UpperBound}]"; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
124 changes: 124 additions & 0 deletions
124
src/EFCore.PG/Query/Expressions/Internal/PostgresUnnestExpression.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
namespace Npgsql.EntityFrameworkCore.PostgreSQL.Query.Expressions.Internal; | ||
|
||
/// <summary> | ||
/// An expression that represents a PostgreSQL <c>unnest</c> function call in a SQL tree. | ||
/// </summary> | ||
/// <remarks> | ||
/// <para> | ||
/// This expression is just a <see cref="TableValuedFunctionExpression" />, adding the ability to provide an explicit column name | ||
/// for its output (<c>SELECT * FROM unnest(array) AS f(foo)</c>). This is necessary since when the column name isn't explicitly | ||
/// specified, it is automatically identical to the table alias (<c>f</c> above); since the table alias may get uniquified by | ||
/// EF, this would break queries. | ||
/// </para> | ||
/// <para> | ||
/// See <see href="https://www.postgresql.org/docs/current/functions-array.html#ARRAY-FUNCTIONS-TABLE">unnest</see> for more | ||
/// information and examples. | ||
/// </para> | ||
/// <para> | ||
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to | ||
/// the same compatibility standards as public APIs. It may be changed or removed without notice in | ||
/// 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. | ||
/// </para> | ||
/// </remarks> | ||
public class PostgresUnnestExpression : TableValuedFunctionExpression | ||
{ | ||
/// <summary> | ||
/// The array to be un-nested into a table. | ||
/// </summary> | ||
/// <remarks> | ||
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to | ||
/// the same compatibility standards as public APIs. It may be changed or removed without notice in | ||
/// 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. | ||
/// </remarks> | ||
public virtual SqlExpression Array | ||
=> Arguments[0]; | ||
|
||
/// <summary> | ||
/// The name of the column to be projected out from the <c>unnest</c> call. | ||
/// </summary> | ||
/// <remarks> | ||
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to | ||
/// the same compatibility standards as public APIs. It may be changed or removed without notice in | ||
/// 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. | ||
/// </remarks> | ||
public virtual string ColumnName { get; } | ||
|
||
/// <summary> | ||
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to | ||
/// the same compatibility standards as public APIs. It may be changed or removed without notice in | ||
/// 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> | ||
public PostgresUnnestExpression(SqlExpression array, string columnName) | ||
: this("u", array, columnName) | ||
{ | ||
} | ||
|
||
/// <summary> | ||
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to | ||
/// the same compatibility standards as public APIs. It may be changed or removed without notice in | ||
/// 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> | ||
public PostgresUnnestExpression(string alias, SqlExpression array, string columnName) | ||
: base(alias, "unnest", schema: null, builtIn: true, new[] { array }) | ||
{ | ||
ColumnName = columnName; | ||
} | ||
|
||
/// <summary> | ||
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to | ||
/// the same compatibility standards as public APIs. It may be changed or removed without notice in | ||
/// 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> | ||
public override TableValuedFunctionExpression Update(IReadOnlyList<SqlExpression> arguments) | ||
=> arguments is [var singleArgument] | ||
? Update(singleArgument) | ||
: throw new ArgumentException(); | ||
|
||
/// <summary> | ||
/// Creates a new expression that is like this one, but using the supplied children. If all of the children are the same, it will | ||
/// return this expression. | ||
/// </summary> | ||
/// <param name="array">The <see cref="Array" /> property of the result.</param> | ||
/// <returns>This expression if no children changed, or an expression with the updated children.</returns> | ||
public virtual PostgresUnnestExpression Update(SqlExpression array) | ||
=> array != Array | ||
? new PostgresUnnestExpression(Alias, array, ColumnName) | ||
: this; | ||
|
||
/// <inheritdoc /> | ||
protected override void Print(ExpressionPrinter expressionPrinter) | ||
{ | ||
expressionPrinter.Append(Name); | ||
expressionPrinter.Append("("); | ||
expressionPrinter.VisitCollection(Arguments); | ||
expressionPrinter.Append(")"); | ||
|
||
PrintAnnotations(expressionPrinter); | ||
expressionPrinter | ||
.Append(" AS ") | ||
.Append(Alias) | ||
.Append("(") | ||
.Append(ColumnName) | ||
.Append(")"); | ||
} | ||
|
||
/// <inheritdoc /> | ||
public override bool Equals(object? obj) | ||
=> obj != null | ||
&& (ReferenceEquals(this, obj) | ||
|| obj is PostgresUnnestExpression unnestExpression | ||
&& Equals(unnestExpression)); | ||
|
||
private bool Equals(PostgresUnnestExpression unnestExpression) | ||
=> base.Equals(unnestExpression) && ColumnName == unnestExpression.ColumnName; | ||
|
||
/// <inheritdoc /> | ||
public override int GetHashCode() | ||
=> base.GetHashCode(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
namespace Npgsql.EntityFrameworkCore.PostgreSQL.Query.Internal; | ||
|
||
/// <summary> | ||
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to | ||
/// the same compatibility standards as public APIs. It may be changed or removed without notice in | ||
/// 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> | ||
public class NpgsqlQueryRootProcessor : RelationalQueryRootProcessor | ||
{ | ||
/// <summary> | ||
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to | ||
/// the same compatibility standards as public APIs. It may be changed or removed without notice in | ||
/// 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> | ||
public NpgsqlQueryRootProcessor( | ||
QueryTranslationPreprocessorDependencies dependencies, | ||
RelationalQueryTranslationPreprocessorDependencies relationalDependencies, | ||
QueryCompilationContext queryCompilationContext) | ||
: base(dependencies, relationalDependencies, queryCompilationContext) | ||
{ | ||
} | ||
|
||
/// <summary> | ||
/// Converts a <see cref="ParameterExpression" /> to a <see cref="ParameterQueryRootExpression" />, to be later translated to | ||
/// PostgreSQL <c>unnest</c> over an array parameter. | ||
/// </summary> | ||
/// <remarks> | ||
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to | ||
/// the same compatibility standards as public APIs. It may be changed or removed without notice in | ||
/// 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. | ||
/// </remarks> | ||
protected override bool ShouldConvertToQueryRoot(ParameterExpression parameterExpression) | ||
=> true; | ||
} |
Oops, something went wrong.