Skip to content

Commit

Permalink
Maps additional array (IList) operators
Browse files Browse the repository at this point in the history
| C# expression                    | SQL generated by Npgsql |
|----------------------------------|-------------------------|
| `Array.Length`                   | `array_length(1, 1)`
| `IList.Count`                    | `array_length(a, 1)`
| `Enumerable.Count(a)`            | `array_length(a, 1)`
| `Array[0]`                       | `a[1]`
| `IList[0]`                       | `a[1]`
| `Enumerable.ElementAt(a, 0)`     | `a[1]`
| `Enumerable.Append(a, b)`        | `a || b`
| `Enumerable.Concat(a, b)`        | `a || b`
| `Enumerable.Prepend(a, b)`       | `b || a`
| `Object.Equals(a, b)`            | `a = b`
| `Enumerable.SequenceEqual(a, b)` | `a = b`
| `a.ToString()`                   | `array_to_string(a, ',')`
| `Enumerable.IndexOf(a, 0)`       | `COALESCE(array_position(a, 1), -1)`
| `Enumerable.Contains(a, b)`      | `b = ANY (a)`

| C# expression              | SQL generated by Npgsql |
|----------------------------|-------------------------|
| `a.All(x => b.Contains(x)` | `b <@ a`
| `a.Any(x => b.Contains(x)` | `a && b`

- Relational operators could also be supported via extensions on `EF.Functions`.

- https://www.postgresql.org/docs/current/static/functions-array.html#ARRAY-OPERATORS-TABLE
  • Loading branch information
austindrenski committed May 29, 2018
1 parent d9af112 commit 2efbbbd
Show file tree
Hide file tree
Showing 5 changed files with 272 additions and 104 deletions.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ public class NpgsqlCompositeMethodCallTranslator : RelationalCompositeMethodCall
/// </summary>
static readonly IMethodCallTranslator[] MethodCallTranslators =
{
new NpgsqlArraySequenceEqualTranslator(),
new NpgsqlConvertTranslator(),
new NpgsqlDateAddTranslator(),
new NpgsqlStringSubstringTranslator(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,13 @@

using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Query.Expressions;
using Microsoft.EntityFrameworkCore.Query.ExpressionTranslators;
using Npgsql.EntityFrameworkCore.PostgreSQL.Query.Expressions.Internal;

namespace Npgsql.EntityFrameworkCore.PostgreSQL.Query.ExpressionTranslators.Internal
{
Expand All @@ -44,45 +47,55 @@ public class NpgsqlListTranslator : IMethodCallTranslator
[CanBeNull]
public Expression Translate(MethodCallExpression expression)
{
if (!typeof(IList).IsAssignableFrom(expression.Method.DeclaringType))
if (expression.Object != null && !typeof(IList).IsAssignableFrom(expression.Object.Type))
return null;
if (expression.Object == null && expression.Arguments.Count > 0 && !typeof(IList).IsAssignableFrom(expression.Arguments[0].Type))
return null;

// TODO: use #430 to map @> to source.All(x => other.Contains(x));
// TODO: use #430 to map && to soucre.Any(x => other.Contains(x));

switch (expression.Method.Name)
{
// TODO: Currently handled in NpgsqlSqlTranslatingExpressionVisitor.
case nameof(Enumerable.Append):
case nameof(Enumerable.Concat):
return new CustomBinaryExpression(expression.Arguments[0], expression.Arguments[1], "||", expression.Arguments[0].Type);

// TODO: Currently handled in NpgsqlSqlTranslatingExpressionVisitor.
case nameof(Enumerable.Count):
return new SqlFunctionExpression("array_length", typeof(int), new[] { expression.Arguments[0], Expression.Constant(1) });

case "get_Item" when expression.Object is Expression instance:
return Expression.MakeIndex(instance, instance.Type.GetRuntimeProperty("Item"), expression.Arguments);
// case nameof(NpgsqlListExtensions.Contains):
// return new CustomBinaryExpression(expression.Arguments[0], expression.Arguments[1], "@>", typeof(bool));
//
// case nameof(NpgsqlListExtensions.ContainedBy):
// return new CustomBinaryExpression(expression.Arguments[0], expression.Arguments[1], "<@", typeof(bool));
//
// case nameof(NpgsqlListExtensions.Overlaps):
// return new CustomBinaryExpression(expression.Arguments[0], expression.Arguments[1], "&&", typeof(bool));
//
// case nameof(NpgsqlListExtensions.IsStrictlyLeftOf):
// return new CustomBinaryExpression(expression.Arguments[0], expression.Arguments[1], "<<", typeof(bool));
//
// case nameof(NpgsqlListExtensions.IsStrictlyRightOf):
// return new CustomBinaryExpression(expression.Arguments[0], expression.Arguments[1], ">>", typeof(bool));
//
// case nameof(NpgsqlListExtensions.DoesNotExtendRightOf):
// return new CustomBinaryExpression(expression.Arguments[0], expression.Arguments[1], "&<", typeof(bool));
//
// case nameof(NpgsqlListExtensions.DoesNotExtendLeftOf):
// return new CustomBinaryExpression(expression.Arguments[0], expression.Arguments[1], "&>", typeof(bool));
//
// case nameof(NpgsqlListExtensions.IsAdjacentTo):
// return new CustomBinaryExpression(expression.Arguments[0], expression.Arguments[1], "-|-", typeof(bool));
//
// case nameof(NpgsqlListExtensions.Union):
// return new CustomBinaryExpression(expression.Arguments[0], expression.Arguments[1], "+", expression.Arguments[0].Type);
//
// case nameof(NpgsqlListExtensions.Intersect):
// return new CustomBinaryExpression(expression.Arguments[0], expression.Arguments[1], "*", expression.Arguments[0].Type);
//
// case nameof(NpgsqlListExtensions.Except):
// return new CustomBinaryExpression(expression.Arguments[0], expression.Arguments[1], "-", expression.Arguments[0].Type);

case nameof(Enumerable.ElementAt):
return Expression.MakeIndex(expression.Arguments[0], expression.Arguments[0].Type.GetRuntimeProperty("Item"), new[] { expression.Arguments[1] });

case nameof(Enumerable.Prepend):
return new CustomBinaryExpression(expression.Arguments[1], expression.Arguments[0], "||", expression.Arguments[0].Type);

case nameof(Enumerable.SequenceEqual):
return Expression.MakeBinary(ExpressionType.Equal, expression.Arguments[0], expression.Arguments[1]);

case nameof(ToString):
return new SqlFunctionExpression("array_to_string", typeof(string), new[] { expression.Object, Expression.Constant(",") });

case nameof(IList.IndexOf):
return
new SqlFunctionExpression(
"COALESCE",
typeof(int),
new Expression[]
{
new SqlFunctionExpression(
"array_position",
typeof(int),
expression.Object is null
? (IEnumerable<Expression>)expression.Arguments
: new[] { expression.Object, expression.Arguments[0] }),
Expression.Constant(-1)
});

default:
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,11 @@ protected override Expression VisitSubQuery(SubQueryExpression expression)
var lastPropertyType = properties[properties.Count - 1].ClrType;
if (typeof(IList).IsAssignableFrom(lastPropertyType) && subQueryModel.ResultOperators.Count > 0)
{
if (subQueryModel.ResultOperators.First() is ConcatResultOperator concatResultOperator &&
Visit(fromExpression) is Expression first &&
Visit(concatResultOperator.Source2) is Expression second)
return new CustomBinaryExpression(first, second, "||", first.Type);

// Translate someArray.Length
if (subQueryModel.ResultOperators.First() is CountResultOperator)
return new SqlFunctionExpression("array_length", typeof(int), new[] { Visit(fromExpression), Expression.Constant(1) });
Expand Down
Loading

0 comments on commit 2efbbbd

Please sign in to comment.