Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

// The PostgreSQL License
//
// Copyright (C) 2016 The Npgsql Development Team
// Copyright (C) 2018 The Npgsql Development Team
//
// Permission to use, copy, modify, and distribute this software and its
// documentation for any purpose, without fee, and without a written
Expand Down Expand Up @@ -49,6 +49,7 @@ public NpgsqlCompositeExpressionFragmentTranslator(
AddTranslators(ExpressionFragmentTranslators);
}

// ReSharper disable once MemberCanBeProtected.Global
/// <summary>
/// Adds additional dispatches to the translators list.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

// The PostgreSQL License
//
// Copyright (C) 2016 The Npgsql Development Team
// Copyright (C) 2018 The Npgsql Development Team
//
// Permission to use, copy, modify, and distribute this software and its
// documentation for any purpose, without fee, and without a written
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

// The PostgreSQL License
//
// Copyright (C) 2016 The Npgsql Development Team
// Copyright (C) 2018 The Npgsql Development Team
//
// Permission to use, copy, modify, and distribute this software and its
// documentation for any purpose, without fee, and without a written
Expand Down Expand Up @@ -68,7 +68,7 @@ public NpgsqlCompositeMethodCallTranslator(
[NotNull] INpgsqlOptions npgsqlOptions)
: base(dependencies)
{
var instanceTranslators = new IMethodCallTranslator[]
var versionDependentTranslators = new IMethodCallTranslator[]
{
new NpgsqlDateAddTranslator(npgsqlOptions.PostgresVersion),
new NpgsqlMathTranslator(npgsqlOptions.PostgresVersion)
Expand All @@ -78,7 +78,7 @@ public NpgsqlCompositeMethodCallTranslator(
AddTranslators(MethodCallTranslators);

// ReSharper disable once DoNotCallOverridableMethodsInConstructor
AddTranslators(instanceTranslators);
AddTranslators(versionDependentTranslators);

foreach (var plugin in npgsqlOptions.Plugins)
plugin.AddMethodCallTranslators(this);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

// The PostgreSQL License
//
// Copyright (C) 2016 The Npgsql Development Team
// Copyright (C) 2018 The Npgsql Development Team
//
// Permission to use, copy, modify, and distribute this software and its
// documentation for any purpose, without fee, and without a written
Expand Down
34 changes: 19 additions & 15 deletions src/EFCore.PG/Query/Sql/Internal/NpgsqlQuerySqlGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

// The PostgreSQL License
//
// Copyright (C) 2016 The Npgsql Development Team
// Copyright (C) 2018 The Npgsql Development Team
//
// Permission to use, copy, modify, and distribute this software and its
// documentation for any purpose, without fee, and without a written
Expand Down Expand Up @@ -195,8 +195,7 @@ protected override Expression VisitBinary(BinaryExpression expression)
}

case ExpressionType.ArrayIndex:
VisitArrayIndex(expression);
return expression;
return VisitArrayIndex(expression);

default:
return base.VisitBinary(expression);
Expand Down Expand Up @@ -228,38 +227,43 @@ protected override Expression VisitUnary(UnaryExpression expression)
return base.VisitUnary(expression);
}

protected virtual void VisitArrayIndex([NotNull] BinaryExpression expression)
[NotNull]
protected virtual Expression VisitArrayIndex([NotNull] BinaryExpression expression)
{
Debug.Assert(expression.NodeType == ExpressionType.ArrayIndex);

if (expression.Left.Type == typeof(byte[]))
{
// bytea cannot be subscripted, but there's get_byte
VisitSqlFunction(new SqlFunctionExpression("get_byte", typeof(byte),
new[] { expression.Left, expression.Right }));
return;
// bytea cannot be subscripted, but there's get_byte.
return VisitSqlFunction(
new SqlFunctionExpression(
"get_byte",
typeof(byte),
new[] { expression.Left, expression.Right }));
}

if (expression.Left.Type == typeof(string))
{
// text cannot be subscripted, use substr
// PostgreSQL substr() is 1-based.

VisitSqlFunction(new SqlFunctionExpression("substr", typeof(char),
new[] { expression.Left, expression.Right, Expression.Constant(1) }));
return;
// text cannot be subscripted, use substr. PostgreSQL substr() is 1-based.
return VisitSqlFunction(
new SqlFunctionExpression(
"substr",
typeof(char),
new[] { expression.Left, expression.Right, Expression.Constant(1) }));
}

// Regular array from here
Visit(expression.Left);
Sql.Append('[');
Visit(GenerateOneBasedIndexExpression(expression.Right));
Sql.Append(']');
return expression;
}

/// <summary>
/// Produces expressions like: 1 = ANY ('{0,1,2}') or 'cat' LIKE ANY ('{a%,b%,c%}').
/// </summary>
[NotNull]
public virtual Expression VisitArrayAnyAll([NotNull] ArrayAnyAllExpression expression)
{
Visit(expression.Operand);
Expand Down Expand Up @@ -291,7 +295,7 @@ public virtual Expression VisitRegexMatch([NotNull] RegexMatchExpression express
Visit(expression.Match);
Sql.Append(" ~ ");

// PG regexps are singleline by default
// PG regexps are single-line by default
if (options == RegexOptions.Singleline)
{
Visit(expression.Pattern);
Expand Down
94 changes: 47 additions & 47 deletions test/EFCore.PG.FunctionalTests/Query/ArrayQueryTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public class ArrayQueryTest : IClassFixture<ArrayQueryTest.ArrayFixture>
[Fact]
public void Roundtrip()
{
using (var ctx = CreateContext())
using (var ctx = Fixture.CreateContext())
{
var x = ctx.SomeEntities.Single(e => e.Id == 1);
Assert.Equal(new[] { 3, 4 }, x.SomeArray);
Expand All @@ -34,7 +34,7 @@ public void Roundtrip()
[Fact]
public void Index_with_constant()
{
using (var ctx = CreateContext())
using (var ctx = Fixture.CreateContext())
{
var actual = ctx.SomeEntities.Where(e => e.SomeArray[0] == 3).ToList();
Assert.Equal(1, actual.Count);
Expand All @@ -45,7 +45,7 @@ public void Index_with_constant()
[Fact]
public void Index_with_non_constant()
{
using (var ctx = CreateContext())
using (var ctx = Fixture.CreateContext())
{
// ReSharper disable once ConvertToConstant.Local
var x = 0;
Expand All @@ -58,7 +58,7 @@ public void Index_with_non_constant()
[Fact]
public void Index_bytea_with_constant()
{
using (var ctx = CreateContext())
using (var ctx = Fixture.CreateContext())
{
var actual = ctx.SomeEntities.Where(e => e.SomeBytea[0] == 3).ToList();
Assert.Equal(1, actual.Count);
Expand All @@ -69,7 +69,7 @@ public void Index_bytea_with_constant()
[Fact]
public void Index_multidimensional()
{
using (var ctx = CreateContext())
using (var ctx = Fixture.CreateContext())
{
// Operations on multidimensional arrays aren't mapped to SQL yet
var actual = ctx.SomeEntities.Where(e => e.SomeMatrix[0, 0] == 5).ToList();
Expand All @@ -84,7 +84,7 @@ public void Index_multidimensional()
[Fact]
public void SequenceEqual_with_parameter()
{
using (var ctx = CreateContext())
using (var ctx = Fixture.CreateContext())
{
var arr = new[] { 3, 4 };
var x = ctx.SomeEntities.Single(e => e.SomeArray.SequenceEqual(arr));
Expand All @@ -96,7 +96,7 @@ public void SequenceEqual_with_parameter()
[Fact]
public void SequenceEqual_with_array_literal()
{
using (var ctx = CreateContext())
using (var ctx = Fixture.CreateContext())
{
var x = ctx.SomeEntities.Single(e => e.SomeArray.SequenceEqual(new[] { 3, 4 }));
Assert.Equal(new[] { 3, 4 }, x.SomeArray);
Expand All @@ -111,7 +111,7 @@ public void SequenceEqual_with_array_literal()
[Fact]
public void Contains_with_literal()
{
using (var ctx = CreateContext())
using (var ctx = Fixture.CreateContext())
{
var x = ctx.SomeEntities.Single(e => e.SomeArray.Contains(3));
Assert.Equal(new[] { 3, 4 }, x.SomeArray);
Expand All @@ -122,7 +122,7 @@ public void Contains_with_literal()
[Fact]
public void Contains_with_parameter()
{
using (var ctx = CreateContext())
using (var ctx = Fixture.CreateContext())
{
// ReSharper disable once ConvertToConstant.Local
var p = 3;
Expand All @@ -135,7 +135,7 @@ public void Contains_with_parameter()
[Fact]
public void Contains_with_column()
{
using (var ctx = CreateContext())
using (var ctx = Fixture.CreateContext())
{
var x = ctx.SomeEntities.Single(e => e.SomeArray.Contains(e.Id + 2));
Assert.Equal(new[] { 3, 4 }, x.SomeArray);
Expand All @@ -150,7 +150,7 @@ public void Contains_with_column()
[Fact]
public void Length()
{
using (var ctx = CreateContext())
using (var ctx = Fixture.CreateContext())
{
var x = ctx.SomeEntities.Single(e => e.SomeArray.Length == 2);
Assert.Equal(new[] { 3, 4 }, x.SomeArray);
Expand All @@ -161,7 +161,7 @@ public void Length()
[Fact(Skip = "https://github.com/aspnet/EntityFramework/issues/9242")]
public void Length_on_EF_Property()
{
using (var ctx = CreateContext())
using (var ctx = Fixture.CreateContext())
{
// TODO: This fails
var x = ctx.SomeEntities.Single(e => EF.Property<int[]>(e, nameof(SomeArrayEntity.SomeArray)).Length == 2);
Expand All @@ -173,7 +173,7 @@ public void Length_on_EF_Property()
[Fact]
public void Length_on_literal_not_translated()
{
using (var ctx = CreateContext())
using (var ctx = Fixture.CreateContext())
{
var _ = ctx.SomeEntities.Where(e => new[] { 1, 2, 3 }.Length == e.Id).ToList();
AssertDoesNotContainInSql("array_length");
Expand All @@ -187,7 +187,7 @@ public void Length_on_literal_not_translated()
[Fact]
public void Array_like_any_when_match_expression_is_column()
{
using (var ctx = CreateContext())
using (var ctx = Fixture.CreateContext())
{
var patterns = new[] { "a", "b", "c" };

Expand All @@ -210,7 +210,7 @@ public void Array_like_any_when_match_expression_is_column()
[Fact]
public void Array_like_any_not_translated_when_match_expression_is_qsre()
{
using (var ctx = CreateContext())
using (var ctx = Fixture.CreateContext())
{
var matches = new[] { "a", "b", "c" };

Expand Down Expand Up @@ -246,8 +246,6 @@ public ArrayQueryTest(ArrayFixture fixture)
Fixture.TestSqlLoggerFactory.Clear();
}

ArrayContext CreateContext() => Fixture.CreateContext();

void AssertContainsInSql(string expected)
=> Assert.Contains(expected, Fixture.TestSqlLoggerFactory.Sql);

Expand All @@ -257,8 +255,8 @@ void AssertDoesNotContainInSql(string expected)
public class ArrayContext : DbContext
{
public DbSet<SomeArrayEntity> SomeEntities { get; set; }

public ArrayContext(DbContextOptions options) : base(options) {}
protected override void OnModelCreating(ModelBuilder builder) {}
}

public class SomeArrayEntity
Expand All @@ -268,54 +266,56 @@ public class SomeArrayEntity
public int[,] SomeMatrix { get; set; }
public List<int> SomeList { get; set; }
public byte[] SomeBytea { get; set; }

// ReSharper disable once UnusedMember.Global
public string SomeText { get; set; }
}

public class ArrayFixture : IDisposable
{
readonly DbContextOptions _options;
readonly NpgsqlTestStore _testStore;
public TestSqlLoggerFactory TestSqlLoggerFactory { get; } = new TestSqlLoggerFactory();

public ArrayFixture()
{
_testStore = NpgsqlTestStore.CreateScratch();
_options = new DbContextOptionsBuilder()
.UseNpgsql(_testStore.ConnectionString, b => b.ApplyConfiguration())
.UseInternalServiceProvider(
new ServiceCollection()
.AddEntityFrameworkNpgsql()
.AddSingleton<ILoggerFactory>(TestSqlLoggerFactory)
.BuildServiceProvider())
.Options;

using (var ctx = CreateContext())
{
ctx.Database.EnsureCreated();
ctx.SomeEntities.Add(new SomeArrayEntity
{
Id = 1,
SomeArray = new[] { 3, 4 },
SomeBytea = new byte[] { 3, 4 },
SomeMatrix = new[,] { { 5, 6 }, { 7, 8 } },
SomeList = new List<int> { 3, 4 }
});
ctx.SomeEntities.Add(new SomeArrayEntity
{
Id = 2,
SomeArray = new[] { 5, 6, 7 },
SomeBytea = new byte[] { 5, 6, 7 },
SomeMatrix = new[,] { { 10, 11 }, { 12, 13 } },
SomeList = new List<int> { 3, 4 }
});
ctx.SomeEntities.AddRange(
new SomeArrayEntity
{
Id = 1,
SomeArray = new[] { 3, 4 },
SomeBytea = new byte[] { 3, 4 },
SomeMatrix = new[,] { { 5, 6 }, { 7, 8 } },
SomeList = new List<int> { 3, 4 }
},
new SomeArrayEntity
{
Id = 2,
SomeArray = new[] { 5, 6, 7 },
SomeBytea = new byte[] { 5, 6, 7 },
SomeMatrix = new[,] { { 10, 11 }, { 12, 13 } },
SomeList = new List<int> { 3, 4 }
});
ctx.SaveChanges();
}
}

readonly NpgsqlTestStore _testStore;
public ArrayContext CreateContext() => new ArrayContext(_options);
public ArrayContext CreateContext(Version postgresVersion = default)
=> new ArrayContext(CreateOptions(postgresVersion));

public void Dispose() => _testStore.Dispose();

DbContextOptions CreateOptions(Version postgresVersion = null)
=> new DbContextOptionsBuilder()
.UseNpgsql(_testStore.ConnectionString, b => b.ApplyConfiguration().SetPostgresVersion(postgresVersion))
.UseInternalServiceProvider(
new ServiceCollection()
.AddEntityFrameworkNpgsql()
.AddSingleton<ILoggerFactory>(TestSqlLoggerFactory)
.BuildServiceProvider())
.Options;
}

#endregion
Expand Down