提交 ddc63fc2 编写于 作者: S Shay Rojansky

Correct LTree type mapping logic

Fixes #2487
上级 07400f75
......@@ -57,32 +57,32 @@ public class NpgsqlLTreeTranslator : IMethodCallTranslator, IMemberTranslator
nameof(LTree.IsAncestorOf)
=> new PostgresBinaryExpression(
PostgresExpressionType.Contains,
_sqlExpressionFactory.ApplyTypeMapping(instance!, _ltreeTypeMapping),
_sqlExpressionFactory.ApplyTypeMapping(arguments[0], _ltreeTypeMapping),
ApplyTypeMappingOrConvert(instance!, _ltreeTypeMapping),
ApplyTypeMappingOrConvert(arguments[0], _ltreeTypeMapping),
typeof(bool),
_boolTypeMapping),
nameof(LTree.IsDescendantOf)
=> new PostgresBinaryExpression(
PostgresExpressionType.ContainedBy,
_sqlExpressionFactory.ApplyTypeMapping(instance!, _ltreeTypeMapping),
_sqlExpressionFactory.ApplyTypeMapping(arguments[0], _ltreeTypeMapping),
ApplyTypeMappingOrConvert(instance!, _ltreeTypeMapping),
ApplyTypeMappingOrConvert(arguments[0], _ltreeTypeMapping),
typeof(bool),
_boolTypeMapping),
nameof(LTree.MatchesLQuery)
=> new PostgresBinaryExpression(
PostgresExpressionType.LTreeMatches,
_sqlExpressionFactory.ApplyTypeMapping(instance!, _ltreeTypeMapping),
_sqlExpressionFactory.ApplyTypeMapping(arguments[0], _lqueryTypeMapping),
ApplyTypeMappingOrConvert(instance!, _ltreeTypeMapping),
ApplyTypeMappingOrConvert(arguments[0], _lqueryTypeMapping),
typeof(bool),
_boolTypeMapping),
nameof(LTree.MatchesLTxtQuery)
=> new PostgresBinaryExpression(
PostgresExpressionType.LTreeMatches,
_sqlExpressionFactory.ApplyTypeMapping(instance!, _ltreeTypeMapping),
_sqlExpressionFactory.ApplyTypeMapping(arguments[0], _ltxtqueryTypeMapping),
ApplyTypeMappingOrConvert(instance!, _ltreeTypeMapping),
ApplyTypeMappingOrConvert(arguments[0], _ltxtqueryTypeMapping),
typeof(bool),
_boolTypeMapping),
......@@ -171,7 +171,7 @@ public class NpgsqlLTreeTranslator : IMethodCallTranslator, IMemberTranslator
{
return new PostgresBinaryExpression(
PostgresExpressionType.LTreeMatchesAny,
_sqlExpressionFactory.ApplyTypeMapping(Visit(predicateInstance), _ltreeTypeMapping),
ApplyTypeMappingOrConvert(Visit(predicateInstance), _ltreeTypeMapping),
_sqlExpressionFactory.ApplyTypeMapping(Visit(array), _lqueryArrayTypeMapping),
typeof(bool),
_boolTypeMapping);
......@@ -184,7 +184,7 @@ public class NpgsqlLTreeTranslator : IMethodCallTranslator, IMemberTranslator
return new PostgresBinaryExpression(
PostgresExpressionType.Contains,
_sqlExpressionFactory.ApplyTypeMapping(Visit(array), _ltreeArrayTypeMapping),
_sqlExpressionFactory.ApplyTypeMapping(Visit(predicateArguments[0]), _ltreeTypeMapping),
ApplyTypeMappingOrConvert(Visit(predicateArguments[0]), _ltreeTypeMapping),
typeof(bool),
_boolTypeMapping);
}
......@@ -196,7 +196,7 @@ public class NpgsqlLTreeTranslator : IMethodCallTranslator, IMemberTranslator
return new PostgresBinaryExpression(
PostgresExpressionType.ContainedBy,
_sqlExpressionFactory.ApplyTypeMapping(Visit(array), _ltreeArrayTypeMapping),
_sqlExpressionFactory.ApplyTypeMapping(Visit(predicateArguments[0]), _ltreeTypeMapping),
ApplyTypeMappingOrConvert(Visit(predicateArguments[0]), _ltreeTypeMapping),
typeof(bool),
_boolTypeMapping);
}
......@@ -208,7 +208,7 @@ public class NpgsqlLTreeTranslator : IMethodCallTranslator, IMemberTranslator
return new PostgresBinaryExpression(
PostgresExpressionType.LTreeMatches,
_sqlExpressionFactory.ApplyTypeMapping(Visit(array), _ltreeArrayTypeMapping),
_sqlExpressionFactory.ApplyTypeMapping(Visit(predicateArguments[0]), _lqueryTypeMapping),
ApplyTypeMappingOrConvert(Visit(predicateArguments[0]), _lqueryTypeMapping),
typeof(bool),
_boolTypeMapping);
}
......@@ -220,7 +220,7 @@ public class NpgsqlLTreeTranslator : IMethodCallTranslator, IMemberTranslator
return new PostgresBinaryExpression(
PostgresExpressionType.LTreeMatches,
_sqlExpressionFactory.ApplyTypeMapping(Visit(array), _ltreeArrayTypeMapping),
_sqlExpressionFactory.ApplyTypeMapping(Visit(predicateArguments[0]), _ltxtqueryTypeMapping),
ApplyTypeMappingOrConvert(Visit(predicateArguments[0]), _ltxtqueryTypeMapping),
typeof(bool),
_boolTypeMapping);
}
......@@ -267,7 +267,7 @@ public class NpgsqlLTreeTranslator : IMethodCallTranslator, IMemberTranslator
return new PostgresBinaryExpression(
PostgresExpressionType.LTreeFirstAncestor,
_sqlExpressionFactory.ApplyTypeMapping(Visit(array), _ltreeArrayTypeMapping),
_sqlExpressionFactory.ApplyTypeMapping(Visit(predicateArguments[0]), _ltreeTypeMapping),
ApplyTypeMappingOrConvert(Visit(predicateArguments[0]), _ltreeTypeMapping),
typeof(LTree),
_ltreeTypeMapping);
}
......@@ -279,7 +279,7 @@ public class NpgsqlLTreeTranslator : IMethodCallTranslator, IMemberTranslator
return new PostgresBinaryExpression(
PostgresExpressionType.LTreeFirstDescendent,
_sqlExpressionFactory.ApplyTypeMapping(Visit(array), _ltreeArrayTypeMapping),
_sqlExpressionFactory.ApplyTypeMapping(Visit(predicateArguments[0]), _ltreeTypeMapping),
ApplyTypeMappingOrConvert(Visit(predicateArguments[0]), _ltreeTypeMapping),
typeof(LTree),
_ltreeTypeMapping);
}
......@@ -291,7 +291,7 @@ public class NpgsqlLTreeTranslator : IMethodCallTranslator, IMemberTranslator
return new PostgresBinaryExpression(
PostgresExpressionType.LTreeFirstMatches,
_sqlExpressionFactory.ApplyTypeMapping(Visit(array), _ltreeArrayTypeMapping),
_sqlExpressionFactory.ApplyTypeMapping(Visit(predicateArguments[0]), _lqueryTypeMapping),
ApplyTypeMappingOrConvert(Visit(predicateArguments[0]), _lqueryTypeMapping),
typeof(LTree),
_ltreeTypeMapping);
}
......@@ -303,7 +303,7 @@ public class NpgsqlLTreeTranslator : IMethodCallTranslator, IMemberTranslator
return new PostgresBinaryExpression(
PostgresExpressionType.LTreeFirstMatches,
_sqlExpressionFactory.ApplyTypeMapping(Visit(array), _ltreeArrayTypeMapping),
_sqlExpressionFactory.ApplyTypeMapping(Visit(predicateArguments[0]), _ltxtqueryTypeMapping),
ApplyTypeMappingOrConvert(Visit(predicateArguments[0]), _ltxtqueryTypeMapping),
typeof(string),
_ltreeTypeMapping);
}
......@@ -315,4 +315,18 @@ public class NpgsqlLTreeTranslator : IMethodCallTranslator, IMemberTranslator
SqlExpression Visit(Expression expression)
=> (SqlExpression)sqlTranslatingExpressionVisitor.Visit(expression);
}
// Applying e.g. the LQuery type mapping on a function operator is a bit tricky.
// If it's a constant, we can just apply the mapping: the constant will get rendered as an untyped string literal, and PG will
// coerce it as the function parameter.
// If it's a parameter, we can also just apply the mapping (which causes NpgsqlDbType to be set to LQuery).
// For anything else, we may need an explicit cast to LQuery, e.g. a plain text column or a concatenation between strings;
// apply the default type mapping and then apply an additional Convert node if the resulting mapping isn't what we need.
private SqlExpression ApplyTypeMappingOrConvert(SqlExpression sqlExpression, RelationalTypeMapping typeMapping)
=> sqlExpression is SqlConstantExpression or SqlParameterExpression
? _sqlExpressionFactory.ApplyTypeMapping(sqlExpression, typeMapping)
: _sqlExpressionFactory.ApplyDefaultTypeMapping(sqlExpression) is var expressionWithDefaultTypeMapping
&& expressionWithDefaultTypeMapping.TypeMapping!.StoreType == typeMapping.StoreType
? expressionWithDefaultTypeMapping
: _sqlExpressionFactory.Convert(expressionWithDefaultTypeMapping, typeMapping.ClrType, typeMapping);
}
\ No newline at end of file
......@@ -128,13 +128,44 @@ public void LTree_matches_LQuery()
Assert.Equal(4, entity.Id);
AssertSql(
"""
SELECT l."Id", l."Path", l."PathAsString"
SELECT l."Id", l."Path", l."PathAsString", l."SomeString"
FROM "LTreeEntities" AS l
WHERE l."Path" ~ '*.Astrophysics'
LIMIT 2
""");
}
[ConditionalFact] // #2487
public void LTree_matches_LQuery_with_string_column()
{
using var ctx = CreateContext();
var entity = ctx.LTreeEntities.Single(l => l.Path.MatchesLQuery(l.SomeString));
Assert.Equal(4, entity.Id);
AssertSql(
"""
SELECT l."Id", l."Path", l."PathAsString", l."SomeString"
FROM "LTreeEntities" AS l
WHERE l."Path" ~ l."SomeString"::lquery
LIMIT 2
""");
}
[ConditionalFact] // #2487
public void LTree_matches_LQuery_with_concat()
{
using var ctx = CreateContext();
var count = ctx.LTreeEntities.Count(l => l.Path.MatchesLQuery("*.Astrophysics." + l.Id));
Assert.Equal(0, count);
AssertSql(
"""
SELECT count(*)::int
FROM "LTreeEntities" AS l
WHERE l."Path" ~ CAST(('*.Astrophysics.' || l."Id"::text) AS lquery)
""");
}
[ConditionalFact]
public void LTree_matches_any_LQuery()
{
......@@ -147,7 +178,7 @@ public void LTree_matches_any_LQuery()
"""
@__lqueries_0={ '*.Astrophysics', '*.Geology' } (DbType = Object)
SELECT l."Id", l."Path", l."PathAsString"
SELECT l."Id", l."Path", l."PathAsString", l."SomeString"
FROM "LTreeEntities" AS l
WHERE l."Path" ? @__lqueries_0
LIMIT 2
......@@ -178,7 +209,7 @@ public void LTree_concat()
Assert.Equal(2, entity.Id);
AssertSql(
"""
SELECT l."Id", l."Path", l."PathAsString"
SELECT l."Id", l."Path", l."PathAsString", l."SomeString"
FROM "LTreeEntities" AS l
WHERE (l."Path"::text || '.Astronomy') = 'Top.Science.Astronomy'
LIMIT 2
......@@ -395,7 +426,7 @@ public void Subpath2()
Assert.Equal(4, result.Id);
AssertSql(
"""
SELECT l."Id", l."Path", l."PathAsString"
SELECT l."Id", l."Path", l."PathAsString", l."SomeString"
FROM "LTreeEntities" AS l
WHERE nlevel(l."Path") > 2 AND subpath(l."Path", 2) = 'Astronomy.Astrophysics'
LIMIT 2
......@@ -495,6 +526,7 @@ public static void Seed(LTreeQueryContext context)
foreach (var ltreeEntity in ltreeEntities)
{
ltreeEntity.PathAsString = ltreeEntity.Path;
ltreeEntity.SomeString = "*.Astrophysics";
}
context.LTreeEntities.AddRange(ltreeEntities);
......@@ -512,6 +544,9 @@ public class LTreeEntity
[Required]
[Column(TypeName = "ltree")]
public string PathAsString { get; set; }
[Required]
public string SomeString { get; set; }
}
public class LTreeQueryFixture : SharedStoreFixtureBase<LTreeQueryContext>
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册