Skip to content

Commit

Permalink
Refactor query SQL generation for standardization
Browse files Browse the repository at this point in the history
* GenerateTop() implementation moved to SqlServer
* GenerateLimitOffset() generates ISO SQL:2008 by default, overridden
  by SqlServer (and Sqlite)
  • Loading branch information
roji committed Jun 18, 2019
1 parent ea73a9e commit df9e419
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 17 deletions.
Empty file modified eng/common/dotnet-install.sh
100644 → 100755
Empty file.
20 changes: 11 additions & 9 deletions src/EFCore.Relational/Query/Pipeline/QuerySqlGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -531,19 +531,12 @@ protected virtual string GenerateOperator(SqlBinaryExpression binaryExpression)

protected virtual void GenerateTop(SelectExpression selectExpression)
{
if (selectExpression.Limit != null
&& selectExpression.Offset == null)
{
_relationalCommandBuilder.Append("TOP(");

Visit(selectExpression.Limit);

_relationalCommandBuilder.Append(") ");
}
}

protected virtual void GenerateLimitOffset(SelectExpression selectExpression)
{
// The below implements ISO SQL:2008

if (selectExpression.Offset != null)
{
_relationalCommandBuilder.AppendLine()
Expand All @@ -562,6 +555,15 @@ protected virtual void GenerateLimitOffset(SelectExpression selectExpression)
_relationalCommandBuilder.Append(" ROWS ONLY");
}
}
else if (selectExpression.Limit != null)
{
_relationalCommandBuilder.AppendLine()
.Append("FETCH FIRST ");

Visit(selectExpression.Limit);

_relationalCommandBuilder.Append(" ROWS ONLY");
}
}

private void GenerateList<T>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ public static IServiceCollection AddEntityFrameworkSqlServer([NotNull] this ISer
// New Query Pipeline
.TryAdd<IMethodCallTranslatorProvider, SqlServerMethodCallTranslatorProvider>()
.TryAdd<IMemberTranslatorProvider, SqlServerMemberTranslatorProvider>()
.TryAdd<IQuerySqlGeneratorFactory, SqlServerQuerySqlGeneratorFactory>()
.TryAdd<IShapedQueryOptimizerFactory, SqlServerShapedQueryOptimizerFactory>()
.TryAdd<IRelationalSqlTranslatingExpressionVisitorFactory, SqlServerSqlTranslatingExpressionVisitorFactory>()

Expand Down
55 changes: 55 additions & 0 deletions src/EFCore.SqlServer/Query/Pipeline/SqlServerQuerySqlGenerator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// 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 Microsoft.EntityFrameworkCore.Relational.Query.Pipeline;
using Microsoft.EntityFrameworkCore.Relational.Query.Pipeline.SqlExpressions;
using Microsoft.EntityFrameworkCore.Storage;

namespace Microsoft.EntityFrameworkCore.SqlServer.Query.Pipeline
{
public class SqlServerQuerySqlGenerator : QuerySqlGenerator
{
public SqlServerQuerySqlGenerator(
IRelationalCommandBuilderFactory relationalCommandBuilderFactory,
ISqlGenerationHelper sqlGenerationHelper)
: base(relationalCommandBuilderFactory, sqlGenerationHelper)
{
}

protected override void GenerateTop(SelectExpression selectExpression)
{
if (selectExpression.Limit != null
&& selectExpression.Offset == null)
{
Sql.Append("TOP(");

Visit(selectExpression.Limit);

Sql.Append(") ");
}
}

protected override void GenerateLimitOffset(SelectExpression selectExpression)
{
// Note: For Limit without Offset, SqlServer generates TOP()
if (selectExpression.Offset != null)
{
Sql.AppendLine()
.Append("OFFSET ");

Visit(selectExpression.Offset);

Sql.Append(" ROWS");

if (selectExpression.Limit != null)
{
Sql.Append(" FETCH NEXT ");

Visit(selectExpression.Limit);

Sql.Append(" ROWS ONLY");
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// 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 Microsoft.EntityFrameworkCore.Relational.Query.Pipeline;
using Microsoft.EntityFrameworkCore.Storage;

namespace Microsoft.EntityFrameworkCore.SqlServer.Query.Pipeline
{
public class SqlServerQuerySqlGeneratorFactory : QuerySqlGeneratorFactory
{
private readonly IRelationalCommandBuilderFactory _commandBuilderFactory;
private readonly ISqlGenerationHelper _sqlGenerationHelper;

public SqlServerQuerySqlGeneratorFactory(
IRelationalCommandBuilderFactory commandBuilderFactory,
ISqlGenerationHelper sqlGenerationHelper)
: base(commandBuilderFactory, sqlGenerationHelper)
{
_commandBuilderFactory = commandBuilderFactory;
_sqlGenerationHelper = sqlGenerationHelper;
}

public override QuerySqlGenerator Create()
=> new SqlServerQuerySqlGenerator(_commandBuilderFactory, _sqlGenerationHelper);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,6 @@ protected override string GenerateOperator(SqlBinaryExpression binaryExpression)
? " || "
: base.GenerateOperator(binaryExpression);

protected override void GenerateTop(SelectExpression selectExpression)
{
// Handled by GenerateLimitOffset
}

protected override void GenerateLimitOffset(SelectExpression selectExpression)
{
Check.NotNull(selectExpression, nameof(selectExpression));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ public SqliteQuerySqlGeneratorFactory(
}

public override QuerySqlGenerator Create()
{
return new SqliteQuerySqlGenerator(_commandBuilderFactory, _sqlGenerationHelper);
}
=> new SqliteQuerySqlGenerator(_commandBuilderFactory, _sqlGenerationHelper);
}
}

0 comments on commit df9e419

Please sign in to comment.