Skip to content

Commit

Permalink
Address review (partial)
Browse files Browse the repository at this point in the history
TODO:
- Investigate `Array.Length` as member vs node.
- Decide on whether to defer to client eval for pre-9.4 versions.
- Recheck/revert logic in `NpgsqlSqlTranslatingExpressionVisitor`
  to make sure the query pipeline is modified as minimally as
  possible.
  • Loading branch information
austindrenski committed Dec 12, 2018
1 parent 6f21661 commit b978f59
Show file tree
Hide file tree
Showing 10 changed files with 366 additions and 681 deletions.
102 changes: 0 additions & 102 deletions src/EFCore.PG/Extensions/NpgsqlArrayExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -274,26 +274,6 @@ public static T[] ArrayFill<T>(
[CanBeNull] [ItemNotNull] T[] dimensions)
=> throw ClientEvaluationNotSupportedException();

/// <summary>
/// Initializes an array with the given
/// </summary>
/// <param name="_">The DbFunctions instance.</param>
/// <param name="value">The value with which to initialize each element.</param>
/// <param name="dimensions">The dimensions of the array.</param>
/// <typeparam name="T">The type of the elements in the array.</typeparam>
/// <returns>
/// An array initialized with the given <paramref name="value"/> and <paramref name="dimensions"/>.
/// </returns>
/// <exception cref="ClientEvaluationNotSupportedException">
/// This method is only intended for use via SQL translation as part of an EF Core LINQ query.
/// </exception>
[NotNull]
public static T[,] MatrixFill<T>(
[CanBeNull] this DbFunctions _,
[CanBeNull] T value,
[CanBeNull] [ItemNotNull] T[] dimensions)
=> throw ClientEvaluationNotSupportedException();

/// <summary>
/// Initializes an array with the given
/// </summary>
Expand Down Expand Up @@ -336,24 +316,6 @@ public static string ArrayDimensions<T>(
[CanBeNull] T[] array)
=> throw ClientEvaluationNotSupportedException();

/// <summary>
/// Returns a text representation of the array dimensions.
/// </summary>
/// <param name="_">The DbFunctions instance.</param>
/// <param name="array">The array.</param>
/// <typeparam name="T">The type of the elements in the array.</typeparam>
/// <returns>
/// A text representation of the array dimensions.
/// </returns>
/// <exception cref="ClientEvaluationNotSupportedException">
/// This method is only intended for use via SQL translation as part of an EF Core LINQ query.
/// </exception>
[NotNull]
public static string ArrayDimensions<T>(
[CanBeNull] this DbFunctions _,
[CanBeNull] T[,] array)
=> throw ClientEvaluationNotSupportedException();

/// <summary>
/// Returns a text representation of the list dimensions.
/// </summary>
Expand Down Expand Up @@ -486,28 +448,6 @@ public static T[] ArrayReplace<T>(
[CanBeNull] T replacement)
=> throw ClientEvaluationNotSupportedException();

/// <summary>
/// Replaces each element equal to the given value with a new value.
/// </summary>
/// <param name="_">The DbFunctions instance.</param>
/// <param name="array">The array to search.</param>
/// <param name="current">The value to replace.</param>
/// <param name="replacement">The new value.</param>
/// <typeparam name="T">The type of the elements in the array.</typeparam>
/// <returns>
/// An array where each element equal to the given value is replaced with a new value.
/// </returns>
/// <exception cref="ClientEvaluationNotSupportedException">
/// This method is only intended for use via SQL translation as part of an EF Core LINQ query.
/// </exception>
[CanBeNull]
public static T[,] ArrayReplace<T>(
[CanBeNull] this DbFunctions _,
[CanBeNull] [ItemCanBeNull] T[,] array,
[CanBeNull] T current,
[CanBeNull] T replacement)
=> throw ClientEvaluationNotSupportedException();

/// <summary>
/// Replaces each element equal to the given value with a new value.
/// </summary>
Expand Down Expand Up @@ -554,26 +494,6 @@ public static string ArrayToString<T>(
[CanBeNull] string delimiter)
=> throw ClientEvaluationNotSupportedException();

/// <summary>
/// Concatenates elements using the supplied delimiter.
/// </summary>
/// <param name="_">The DbFunctions instance.</param>
/// <param name="array">The list to convert to a string in which to locate the value.</param>
/// <param name="delimiter">The value used to delimit the elements.</param>
/// <typeparam name="T">The type of the elements of <paramref name="array"/>.</typeparam>
/// <returns>
/// The string concatenation of the elements with the supplied delimiter.
/// </returns>
/// <exception cref="ClientEvaluationNotSupportedException">
/// This method is only intended for use via SQL translation as part of an EF Core LINQ query.
/// </exception>
[CanBeNull]
public static string ArrayToString<T>(
[CanBeNull] this DbFunctions _,
[CanBeNull] [ItemCanBeNull] T[,] array,
[CanBeNull] string delimiter)
=> throw ClientEvaluationNotSupportedException();

/// <summary>
/// Concatenates elements using the supplied delimiter.
/// </summary>
Expand Down Expand Up @@ -616,28 +536,6 @@ public static string ArrayToString<T>(
[CanBeNull] string nullString)
=> throw ClientEvaluationNotSupportedException();

/// <summary>
/// Concatenates elements using the supplied delimiter and the string representation for null elements.
/// </summary>
/// <param name="_">The DbFunctions instance.</param>
/// <param name="array">The list to convert to a string in which to locate the value.</param>
/// <param name="delimiter">The value used to delimit the elements.</param>
/// <param name="nullString">The value used to represent a null value.</param>
/// <typeparam name="T">The type of the elements of <paramref name="array"/>.</typeparam>
/// <returns>
/// The string concatenation of the elements with the supplied delimiter and null string.
/// </returns>
/// <exception cref="ClientEvaluationNotSupportedException">
/// This method is only intended for use via SQL translation as part of an EF Core LINQ query.
/// </exception>
[CanBeNull]
public static string ArrayToString<T>(
[CanBeNull] this DbFunctions _,
[CanBeNull] [ItemCanBeNull] T[,] array,
[CanBeNull] string delimiter,
[CanBeNull] string nullString)
=> throw ClientEvaluationNotSupportedException();

/// <summary>
/// Concatenates elements using the supplied delimiter and the string representation for null elements.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Query.Expressions;
Expand Down Expand Up @@ -29,92 +28,60 @@ public class NpgsqlArrayMemberTranslator : IMemberTranslator
public NpgsqlArrayMemberTranslator([CanBeNull] Version postgresVersion) => _postgresVersion = postgresVersion;

/// <inheritdoc />
public Expression Translate(MemberExpression expression)
=> ArrayInstanceHandler(expression) ?? ListInstanceHandler(expression);
public Expression Translate(MemberExpression e)
=> ArrayInstanceHandler(e) ??
ListInstanceHandler(e);

#region Handlers

/// <summary>
/// Translates instance members defined on <see cref="Array"/>.
/// </summary>
/// <param name="expression">The expression to translate.</param>
/// <returns>
/// A translated expression or null if no translation is supported.
/// </returns>
[CanBeNull]
Expression ArrayInstanceHandler([NotNull] MemberExpression expression)
Expression ArrayInstanceHandler([NotNull] MemberExpression e)
{
var instance = expression.Expression;
var instance = e.Expression;

if (instance is null || !instance.Type.IsArray)
if (instance == null || !instance.Type.IsArray || instance.Type.GetArrayRank() != 1)
return null;

switch (expression.Member.Name)
switch (e.Member.Name)
{
case nameof(Array.Length) when VersionAtLeast(9, 4):
return new SqlFunctionExpression("cardinality", typeof(int), new[] { instance });

case nameof(Array.Length) when VersionAtLeast(8, 4) && instance.Type.GetArrayRank() == 1:
return
Expression.Coalesce(
new SqlFunctionExpression(
"array_length",
typeof(int?),
new[] { instance, Expression.Constant(1) }),
Expression.Constant(0));

case nameof(Array.Length) when VersionAtLeast(8, 4):
return
Enumerable.Range(1, instance.Type.GetArrayRank())
.Select(x =>
Expression.Coalesce(
new SqlFunctionExpression(
"array_length",
typeof(int?),
new[] { instance, Expression.Constant(x) }),
Expression.Constant(0)))
.Cast<Expression>()
.Aggregate(Expression.Multiply);
return Expression.Coalesce(
new SqlFunctionExpression(
"array_length",
typeof(int?),
new[] { instance, Expression.Constant(1) }),
Expression.Constant(0));

case nameof(Array.Rank) when VersionAtLeast(8, 4):
return
Expression.Coalesce(
new SqlFunctionExpression(
"array_ndims",
typeof(int?),
new[] { instance }),
Expression.Constant(1));
return Expression.Coalesce(
new SqlFunctionExpression(
"array_ndims",
typeof(int?),
new[] { instance }),
Expression.Constant(1));

default:
return null;
}
}

/// <summary>
/// Translates instance members defined on <see cref="List{T}"/>.
/// </summary>
/// <param name="expression">The expression to translate.</param>
/// <returns>
/// A translated expression or null if no translation is supported.
/// </returns>
[CanBeNull]
Expression ListInstanceHandler([NotNull] MemberExpression expression)
Expression ListInstanceHandler([NotNull] MemberExpression e)
{
var instance = expression.Expression;
var instance = e.Expression;

if (instance is null || !instance.Type.IsGenericType || instance.Type.GetGenericTypeDefinition() != typeof(List<>))
return null;

switch (expression.Member.Name)
switch (e.Member.Name)
{
case nameof(IList.Count) when VersionAtLeast(8, 4):
return
Expression.Coalesce(
new SqlFunctionExpression(
"array_length",
typeof(int?),
new Expression[] { instance, Expression.Constant(1) }),
Expression.Constant(0));
return Expression.Coalesce(
new SqlFunctionExpression(
"array_length",
typeof(int?),
new Expression[] { instance, Expression.Constant(1) }),
Expression.Constant(0));

default:
return null;
Expand Down
Loading

0 comments on commit b978f59

Please sign in to comment.