Skip to content

Commit

Permalink
SQLite: Use infix GLOB and REGEXP operators
Browse files Browse the repository at this point in the history
Mostly just to be pretentious [insert word here]
  • Loading branch information
bricelam committed Nov 5, 2020
1 parent 347adb4 commit c10ec5f
Show file tree
Hide file tree
Showing 13 changed files with 573 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ public static IServiceCollection AddEntityFrameworkSqlite([NotNull] this IServic
.TryAdd<IQueryableMethodTranslatingExpressionVisitorFactory, SqliteQueryableMethodTranslatingExpressionVisitorFactory>()
.TryAdd<IRelationalSqlTranslatingExpressionVisitorFactory, SqliteSqlTranslatingExpressionVisitorFactory>()
.TryAdd<IQueryTranslationPostprocessorFactory, SqliteQueryTranslationPostprocessorFactory>()
.TryAdd<IRelationalParameterBasedSqlProcessorFactory, SqliteParameterBasedSqlProcessorFactory>()
.TryAddProviderSpecificServices(
b => b.TryAddScoped<ISqliteRelationalConnection, SqliteRelationalConnection>());

Expand Down
25 changes: 25 additions & 0 deletions src/EFCore.Sqlite.Core/Query/Internal/SqliteExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
using Microsoft.EntityFrameworkCore.Sqlite.Query.SqlExpressions.Internal;
using Microsoft.EntityFrameworkCore.Storage;

#nullable enable
Expand Down Expand Up @@ -65,5 +66,29 @@ public static SqlFunctionExpression Strftime(
returnType,
typeMapping);
}

/// <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 static SqliteGlobExpression Glob(
[NotNull] SqlExpression match,
[NotNull] SqlExpression pattern,
[CanBeNull] RelationalTypeMapping? typeMapping = null)
=> new SqliteGlobExpression(match, pattern, typeMapping);

/// <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 static SqliteRegexpExpression Regexp(
[NotNull] SqlExpression match,
[NotNull] SqlExpression pattern,
[CanBeNull] RelationalTypeMapping? typeMapping = null)
=> new SqliteRegexpExpression(match, pattern, typeMapping);
}
}
31 changes: 18 additions & 13 deletions src/EFCore.Sqlite.Core/Query/Internal/SqliteGlobMethodTranslator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.Utilities;

namespace Microsoft.EntityFrameworkCore.Sqlite.Query.Internal
Expand All @@ -23,15 +24,24 @@ public class SqliteGlobMethodTranslator : IMethodCallTranslator
.GetMethod(nameof(SqliteDbFunctionsExtensions.Glob), new[] { typeof(DbFunctions), typeof(string), typeof(string) });

private readonly ISqlExpressionFactory _sqlExpressionFactory;
private readonly IRelationalTypeMappingSource _typeMappingSource;

/// <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 SqliteGlobMethodTranslator([NotNull] ISqlExpressionFactory sqlExpressionFactory)
=> _sqlExpressionFactory = Check.NotNull(sqlExpressionFactory, nameof(sqlExpressionFactory));
public SqliteGlobMethodTranslator(
[NotNull] ISqlExpressionFactory sqlExpressionFactory,
[NotNull] IRelationalTypeMappingSource typeMappingSource)
{
Check.NotNull(sqlExpressionFactory, nameof(sqlExpressionFactory));
Check.NotNull(typeMappingSource, nameof(typeMappingSource));

_sqlExpressionFactory = sqlExpressionFactory;
_typeMappingSource = typeMappingSource;
}

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
Expand All @@ -53,18 +63,13 @@ public virtual SqlExpression Translate(
{
var matchExpression = arguments[1];
var pattern = arguments[2];
var stringTypeMapping = ExpressionExtensions.InferTypeMapping(matchExpression, pattern);
var stringTypeMapping = ExpressionExtensions.InferTypeMapping(matchExpression, pattern)
?? _typeMappingSource.FindMapping(typeof(string));

return _sqlExpressionFactory.Function(
"glob",
new[]
{
_sqlExpressionFactory.ApplyTypeMapping(pattern, stringTypeMapping),
_sqlExpressionFactory.ApplyTypeMapping(matchExpression, stringTypeMapping)
},
nullable: true,
argumentsPropagateNullability: new[] { true, true },
typeof(bool));
return SqliteExpression.Glob(
_sqlExpressionFactory.ApplyTypeMapping(matchExpression, stringTypeMapping),
_sqlExpressionFactory.ApplyTypeMapping(pattern, stringTypeMapping),
_typeMappingSource.GetMapping(typeof(bool)));
}

return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,18 @@ public SqliteMethodCallTranslatorProvider([NotNull] RelationalMethodCallTranslat
: base(dependencies)
{
var sqlExpressionFactory = dependencies.SqlExpressionFactory;
var typeMappingSource = dependencies.RelationalTypeMappingSource;

AddTranslators(
new IMethodCallTranslator[]
{
new SqliteByteArrayMethodTranslator(sqlExpressionFactory),
new SqliteDateTimeAddTranslator(sqlExpressionFactory),
new SqliteGlobMethodTranslator(sqlExpressionFactory),
new SqliteGlobMethodTranslator(sqlExpressionFactory, typeMappingSource),
new SqliteHexMethodTranslator(sqlExpressionFactory),
new SqliteMathTranslator(sqlExpressionFactory),
new SqliteObjectToStringTranslator(sqlExpressionFactory),
new SqliteRegexMethodTranslator(sqlExpressionFactory),
new SqliteRegexMethodTranslator(sqlExpressionFactory, typeMappingSource),
new SqliteStringMethodTranslator(sqlExpressionFactory),
new SqliteSubstrMethodTranslator(sqlExpressionFactory)
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Collections.Generic;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
using Microsoft.EntityFrameworkCore.Utilities;

namespace Microsoft.EntityFrameworkCore.Sqlite.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 SqliteParameterBasedSqlProcessor : RelationalParameterBasedSqlProcessor
{
/// <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 SqliteParameterBasedSqlProcessor(
[NotNull] RelationalParameterBasedSqlProcessorDependencies dependencies,
bool useRelationalNulls)
: base(dependencies, useRelationalNulls)
{
}

/// <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>
protected override SelectExpression ProcessSqlNullability(
SelectExpression selectExpression,
IReadOnlyDictionary<string, object> parametersValues,
out bool canCache)
{
Check.NotNull(selectExpression, nameof(selectExpression));
Check.NotNull(parametersValues, nameof(parametersValues));

return new SqliteSqlNullabilityProcessor(Dependencies, UseRelationalNulls).Process(selectExpression, parametersValues, out canCache);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Utilities;

namespace Microsoft.EntityFrameworkCore.Sqlite.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 SqliteParameterBasedSqlProcessorFactory : IRelationalParameterBasedSqlProcessorFactory
{
private readonly RelationalParameterBasedSqlProcessorDependencies _dependencies;

/// <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 SqliteParameterBasedSqlProcessorFactory(
[NotNull] RelationalParameterBasedSqlProcessorDependencies dependencies)
{
Check.NotNull(dependencies, nameof(dependencies));

_dependencies = dependencies;
}

/// <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 virtual RelationalParameterBasedSqlProcessor Create(bool useRelationalNulls)
=> new SqliteParameterBasedSqlProcessor(_dependencies, useRelationalNulls);
}
}
57 changes: 57 additions & 0 deletions src/EFCore.Sqlite.Core/Query/Internal/SqliteQuerySqlGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
using Microsoft.EntityFrameworkCore.Sqlite.Query.SqlExpressions.Internal;
using Microsoft.EntityFrameworkCore.Utilities;

#nullable enable
Expand All @@ -30,6 +31,62 @@ public SqliteQuerySqlGenerator([NotNull] QuerySqlGeneratorDependencies dependenc
{
}

/// <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>
protected override Expression VisitExtension(Expression extensionExpression)
{
Check.NotNull(extensionExpression, nameof(extensionExpression));

switch (extensionExpression)
{
case SqliteGlobExpression globExpression:
return VisitGlob(globExpression);

case SqliteRegexpExpression regexpExpression:
return VisitRegexp(regexpExpression);
}

return base.VisitExtension(extensionExpression);
}

/// <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>
protected virtual Expression VisitGlob([NotNull] SqliteGlobExpression globExpression)
{
Check.NotNull(globExpression, nameof(globExpression));

Visit(globExpression.Match);
Sql.Append(" GLOB ");
Visit(globExpression.Pattern);

return globExpression;
}

/// <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>
protected virtual Expression VisitRegexp([NotNull] SqliteRegexpExpression regexpExpression)
{
Check.NotNull(regexpExpression, nameof(regexpExpression));

Visit(regexpExpression.Match);
Sql.Append(" REGEXP ");
Visit(regexpExpression.Pattern);

return regexpExpression;
}

/// <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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.Utilities;

#nullable enable
Expand All @@ -27,18 +28,23 @@ private readonly static MethodInfo _regexIsMatchMethodInfo
= typeof(Regex).GetRuntimeMethod(nameof(Regex.IsMatch), new Type[] { typeof(string), typeof(string) });

private readonly ISqlExpressionFactory _sqlExpressionFactory;
private readonly IRelationalTypeMappingSource _typeMappingSource;

/// <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 SqliteRegexMethodTranslator([NotNull] ISqlExpressionFactory sqlExpressionFactory)
public SqliteRegexMethodTranslator(
[NotNull] ISqlExpressionFactory sqlExpressionFactory,
[NotNull] IRelationalTypeMappingSource typeMappingSource)
{
Check.NotNull(sqlExpressionFactory, nameof(sqlExpressionFactory));
Check.NotNull(typeMappingSource, nameof(typeMappingSource));

_sqlExpressionFactory = sqlExpressionFactory;
_typeMappingSource = typeMappingSource;
}

/// <summary>
Expand All @@ -61,18 +67,13 @@ public SqliteRegexMethodTranslator([NotNull] ISqlExpressionFactory sqlExpression
{
var input = arguments[0];
var pattern = arguments[1];
var stringTypeMapping = ExpressionExtensions.InferTypeMapping(input, pattern);
var stringTypeMapping = ExpressionExtensions.InferTypeMapping(input, pattern)
?? _typeMappingSource.FindMapping(typeof(string));

return _sqlExpressionFactory.Function(
"regexp",
new[]
{
_sqlExpressionFactory.ApplyTypeMapping(pattern, stringTypeMapping),
_sqlExpressionFactory.ApplyTypeMapping(input, stringTypeMapping)
},
nullable: true,
argumentsPropagateNullability: new[] { true, true },
typeof(bool));
return SqliteExpression.Regexp(
_sqlExpressionFactory.ApplyTypeMapping(input, stringTypeMapping),
_sqlExpressionFactory.ApplyTypeMapping(pattern, stringTypeMapping),
_typeMappingSource.GetMapping(typeof(bool)));
}

return null;
Expand Down
Loading

0 comments on commit c10ec5f

Please sign in to comment.