Skip to content

Commit

Permalink
Use SQLite CLI parameter syntax for SQLite query string
Browse files Browse the repository at this point in the history
  • Loading branch information
ajcvickers committed Dec 24, 2019
1 parent bde2b14 commit 5bfb114
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 2 deletions.
Expand Up @@ -25,7 +25,12 @@ public class SqlServerQueryStringFactory : IRelationalQueryStringFactory
{
private readonly IRelationalTypeMappingSource _typeMapper;

/// <summary>Initializes a new instance of the <see cref="T:System.Object" /> class.</summary>
/// <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 SqlServerQueryStringFactory([NotNull] IRelationalTypeMappingSource typeMapper)
{
_typeMapper = typeMapper;
Expand Down
Expand Up @@ -63,6 +63,7 @@ public static IServiceCollection AddEntityFrameworkSqlite([NotNull] this IServic
.TryAdd<IMigrationsSqlGenerator, SqliteMigrationsSqlGenerator>()
.TryAdd<IRelationalDatabaseCreator, SqliteDatabaseCreator>()
.TryAdd<IHistoryRepository, SqliteHistoryRepository>()
.TryAdd<IRelationalQueryStringFactory, SqliteQueryStringFactory>()

// New Query Pipeline
.TryAdd<IMethodCallTranslatorProvider, SqliteMethodCallTranslatorProvider>()
Expand Down
67 changes: 67 additions & 0 deletions src/EFCore.Sqlite.Core/Query/Internal/SqliteQueryStringFactory.cs
@@ -0,0 +1,67 @@
// 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;
using System.Data.Common;
using System.Text;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Storage;

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 SqliteQueryStringFactory : IRelationalQueryStringFactory
{
private readonly IRelationalTypeMappingSource _typeMapper;

/// <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 SqliteQueryStringFactory([NotNull] IRelationalTypeMappingSource typeMapper)
{
_typeMapper = typeMapper;
}

/// <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 string Create(DbCommand command)
{
if (command.Parameters.Count == 0)
{
return command.CommandText;
}

var builder = new StringBuilder();
foreach (DbParameter parameter in command.Parameters)
{
var value = parameter.Value;
builder
.Append(".param set ")
.Append(parameter.ParameterName)
.Append(' ')
.AppendLine(
value == null || value == DBNull.Value
? "NULL"
: _typeMapper.FindMapping(value.GetType())?.GenerateSqlLiteral(value)
?? value.ToString());
}

return builder
.AppendLine()
.Append(command.CommandText).ToString();
}
}
}
32 changes: 32 additions & 0 deletions test/EFCore.Sqlite.FunctionalTests/Query/FromSqlQuerySqliteTest.cs
Expand Up @@ -4,6 +4,7 @@
using System.Data.Common;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore.TestUtilities;
using Xunit;
using Xunit.Abstractions;

namespace Microsoft.EntityFrameworkCore.Query
Expand All @@ -16,6 +17,37 @@ public FromSqlQuerySqliteTest(NorthwindQuerySqliteFixture<NoopModelCustomizer> f
fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper);
}

public override string FromSqlRaw_queryable_composed()
{
var queryString = base.FromSqlRaw_queryable_composed();

var expected = @"SELECT ""c"".""CustomerID"", ""c"".""Address"", ""c"".""City"", ""c"".""CompanyName"", ""c"".""ContactName"", ""c"".""ContactTitle"", ""c"".""Country"", ""c"".""Fax"", ""c"".""Phone"", ""c"".""PostalCode"", ""c"".""Region""
FROM (
SELECT * FROM ""Customers""
) AS ""c""
WHERE ('z' = '') OR (instr(""c"".""ContactName"", 'z') > 0)";

Assert.Equal(expected, queryString);

return null;
}

public override string FromSqlRaw_queryable_with_parameters_and_closure()
{
var queryString = base.FromSqlRaw_queryable_with_parameters_and_closure();

Assert.Equal(@".param set p0 'London'
.param set @__contactTitle_1 'Sales Representative'
SELECT ""c"".""CustomerID"", ""c"".""Address"", ""c"".""City"", ""c"".""CompanyName"", ""c"".""ContactName"", ""c"".""ContactTitle"", ""c"".""Country"", ""c"".""Fax"", ""c"".""Phone"", ""c"".""PostalCode"", ""c"".""Region""
FROM (
SELECT * FROM ""Customers"" WHERE ""City"" = @p0
) AS ""c""
WHERE ""c"".""ContactTitle"" = @__contactTitle_1", queryString);

return null;
}

public override void Bad_data_error_handling_invalid_cast_key()
{
// Not supported on SQLite
Expand Down
Expand Up @@ -36,7 +36,8 @@ public override async Task<string> Where_simple_closure(bool async)
WHERE ""c"".""City"" = @__city_0");

Assert.Equal(
@"-- @__city_0='London' (Size = 6)
@".param set @__city_0 'London'
SELECT ""c"".""CustomerID"", ""c"".""Address"", ""c"".""City"", ""c"".""CompanyName"", ""c"".""ContactName"", ""c"".""ContactTitle"", ""c"".""Country"", ""c"".""Fax"", ""c"".""Phone"", ""c"".""PostalCode"", ""c"".""Region""
FROM ""Customers"" AS ""c""
WHERE ""c"".""City"" = @__city_0", queryString, ignoreLineEndingDifferences: true, ignoreWhiteSpaceDifferences: true);
Expand Down

0 comments on commit 5bfb114

Please sign in to comment.