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
224 changes: 224 additions & 0 deletions QueryBuilder.Tests/GeneralTests.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using SqlKata.Compilers;
using SqlKata.Extensions;
using SqlKata.Tests.Infrastructure;
using System;
using System.Linq;
using Xunit;

namespace SqlKata.Tests
Expand Down Expand Up @@ -160,5 +162,227 @@ public void WrapWithMultipleSpaces()

Assert.Equal("[My Table One] AS [Table One]", compiler.Wrap("My Table One as Table One"));
}

[Fact]
public void CompilerSpecificFrom()
{
var query = new Query()
.ForSqlServer(q => q.From("mssql"))
.ForPostgreSql(q => q.From("pgsql"))
.ForMySql(q => q.From("mysql"));
var engines = new[] { EngineCodes.SqlServer, EngineCodes.MySql, EngineCodes.PostgreSql };
var c = Compilers.Compile(engines, query);

Assert.Equal("SELECT * FROM [mssql]", c[EngineCodes.SqlServer].RawSql);
Assert.Equal("SELECT * FROM \"pgsql\"", c[EngineCodes.PostgreSql].RawSql);
Assert.Equal("SELECT * FROM `mysql`", c[EngineCodes.MySql].RawSql);
}

[Fact]
public void CompilerSpecificFromRaw()
{
var query = new Query()
.ForSqlServer(q => q.FromRaw("[mssql]"))
.ForPostgreSql(q => q.FromRaw("[pgsql]"))
.ForMySql(q => q.FromRaw("[mysql]"));
var engines = new[] { EngineCodes.SqlServer, EngineCodes.MySql, EngineCodes.PostgreSql };
var c = Compilers.Compile(engines, query);

Assert.Equal("SELECT * FROM [mssql]", c[EngineCodes.SqlServer].RawSql);
Assert.Equal("SELECT * FROM \"pgsql\"", c[EngineCodes.PostgreSql].RawSql);
Assert.Equal("SELECT * FROM `mysql`", c[EngineCodes.MySql].RawSql);
}

[Fact]
public void CompilerSpecificFromMixed()
{
var query = new Query()
.ForSqlServer(q => q.From("mssql"))
.ForPostgreSql(q => q.FromRaw("[pgsql]"))
.ForMySql(q => q.From("mysql"));
var engines = new[] { EngineCodes.SqlServer, EngineCodes.MySql, EngineCodes.PostgreSql };
var c = Compilers.Compile(engines, query);

Assert.Equal("SELECT * FROM [mssql]", c[EngineCodes.SqlServer].RawSql);
Assert.Equal("SELECT * FROM \"pgsql\"", c[EngineCodes.PostgreSql].RawSql);
Assert.Equal("SELECT * FROM `mysql`", c[EngineCodes.MySql].RawSql);
}

[Fact]
public void OneFromPerEngine()
{
var query = new Query("generic")
.ForSqlServer(q => q.From("dnu"))
.ForSqlServer(q => q.From("mssql"));
var engines = new[] { EngineCodes.SqlServer, EngineCodes.MySql, EngineCodes.PostgreSql };
var c = Compilers.Compile(engines, query);

Assert.Equal(2, query.Clauses.OfType<AbstractFrom>().Count());
Assert.Equal("SELECT * FROM [mssql]", c[EngineCodes.SqlServer].RawSql);
Assert.Equal("SELECT * FROM \"generic\"", c[EngineCodes.PostgreSql].RawSql);
Assert.Equal("SELECT * FROM `generic`", c[EngineCodes.MySql].RawSql);
}

[Theory]
[InlineData(null, null)]
[InlineData(null, "mssql")]
[InlineData("original", null)]
[InlineData("original", "mssql")]
public void AddOrReplace_Works(string table, string engine)
{
var query = new Query();
if (table != null)
query.From(table);
query.AddOrReplaceComponent("from", new FromClause() { Table = "updated", Engine = engine });
var froms = query.Clauses.OfType<FromClause>();

Assert.Single(froms);
Assert.Equal("updated", froms.Single().Table);
}

[Theory]
[InlineData(null, "generic")]
[InlineData(EngineCodes.SqlServer, "mssql")]
[InlineData(EngineCodes.MySql, "generic")]
public void GetOneComponent_Prefers_Engine(string engine, string column)
{
var query = new Query()
.Where("generic", "foo")
.ForSqlServer(q => q.Where("mssql", "foo"));

var where = query.GetOneComponent("where", engine) as BasicCondition;

Assert.NotNull(where);
Assert.Equal(column, where.Column);
}

[Fact]
public void AddOrReplace_Throws_MoreThanOne()
{
var query = new Query()
.Where("a", "b")
.Where("c", "d");

Action act = () => query.AddOrReplaceComponent("where", new BasicCondition());
Assert.Throws<InvalidOperationException>(act);
}

[Fact]
public void OneLimitPerEngine()
{
var query = new Query("mytable")
.ForSqlServer(q => q.Limit(5))
.ForSqlServer(q => q.Limit(10));

var limits = query.GetComponents<LimitClause>("limit", EngineCodes.SqlServer);
Assert.Single(limits);
Assert.Equal(10, limits.Single().Limit);
}

[Fact]
public void CompilerSpecificLimit()
{
var query = new Query("mytable")
.ForSqlServer(q => q.Limit(5))
.ForPostgreSql(q => q.Limit(10));

var engines = new[] { EngineCodes.SqlServer, EngineCodes.MySql, EngineCodes.PostgreSql };
var c = Compilers.Compile(engines, query);

Assert.Equal(2, query.GetComponents("limit").Count());
Assert.Equal("SELECT TOP (5) * FROM [mytable]", c[EngineCodes.SqlServer].ToString());
Assert.Equal("SELECT * FROM \"mytable\" LIMIT 10", c[EngineCodes.PostgreSql].ToString());
Assert.Equal("SELECT * FROM `mytable`", c[EngineCodes.MySql].ToString());
}

[Fact]
public void OneOffsetPerEngine()
{
var query = new Query("mytable")
.ForSqlServer(q => q.Offset(5))
.ForSqlServer(q => q.Offset(10));

var limits = query.GetComponents<OffsetClause>("offset", EngineCodes.SqlServer);
Assert.Single(limits);
Assert.Equal(10, limits.Single().Offset);
}

[Fact]
public void CompilerSpecificOffset()
{
var query = new Query("mytable")
.ForMySql(q => q.Offset(5))
.ForPostgreSql(q => q.Offset(10));

var engines = new[] { EngineCodes.SqlServer, EngineCodes.MySql, EngineCodes.PostgreSql };
var c = Compilers.Compile(engines, query);

Assert.Equal(2, query.GetComponents("offset").Count());
Assert.Equal("SELECT * FROM `mytable` LIMIT 18446744073709551615 OFFSET 5", c[EngineCodes.MySql].ToString());
Assert.Equal("SELECT * FROM \"mytable\" OFFSET 10", c[EngineCodes.PostgreSql].ToString());
Assert.Equal("SELECT * FROM [mytable]", c[EngineCodes.SqlServer].ToString());
}

[Fact]
public void Limit_Takes_Generic_If_Needed()
{
var query = new Query("mytable")
.Limit(5)
.Offset(10)
.ForPostgreSql(q => q.Offset(20));

var engines = new[] { EngineCodes.MySql, EngineCodes.PostgreSql };
var c = Compilers.Compile(engines, query);

Assert.Equal("SELECT * FROM `mytable` LIMIT 5 OFFSET 10", c[EngineCodes.MySql].ToString());
Assert.Equal("SELECT * FROM \"mytable\" LIMIT 5 OFFSET 20", c[EngineCodes.PostgreSql].ToString());
}

[Fact]
public void Offset_Takes_Generic_If_Needed()
{
var query = new Query("mytable")
.Limit(5)
.Offset(10)
.ForPostgreSql(q => q.Limit(20));

var engines = new[] { EngineCodes.MySql, EngineCodes.PostgreSql };
var c = Compilers.Compile(engines, query);

Assert.Equal("SELECT * FROM `mytable` LIMIT 5 OFFSET 10", c[EngineCodes.MySql].ToString());
Assert.Equal("SELECT * FROM \"mytable\" LIMIT 20 OFFSET 10", c[EngineCodes.PostgreSql].ToString());
}

[Fact]
public void Can_Change_Generic_Limit_After_SpecificOffset()
{
var query = new Query("mytable")
.Limit(5)
.Offset(10)
.ForPostgreSql(q => q.Offset(20))
.Limit(7);

var engines = new[] { EngineCodes.MySql, EngineCodes.PostgreSql };
var c = Compilers.Compile(engines, query);

Assert.Equal("SELECT * FROM `mytable` LIMIT 7 OFFSET 10", c[EngineCodes.MySql].ToString());
Assert.Equal("SELECT * FROM \"mytable\" LIMIT 7 OFFSET 20", c[EngineCodes.PostgreSql].ToString());
}

[Fact]
public void Can_Change_Generic_Offset_After_SpecificLimit()
{
var query = new Query("mytable")
.Limit(5)
.Offset(10)
.ForPostgreSql(q => q.Limit(20))
.Offset(7);

var engines = new[] { EngineCodes.MySql, EngineCodes.PostgreSql };
var c = Compilers.Compile(engines, query);

Assert.Equal("SELECT * FROM `mytable` LIMIT 5 OFFSET 7", c[EngineCodes.MySql].ToString());
Assert.Equal("SELECT * FROM \"mytable\" LIMIT 20 OFFSET 7", c[EngineCodes.PostgreSql].ToString());
}
}
}
41 changes: 30 additions & 11 deletions QueryBuilder/BaseQuery.cs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,28 @@ public Q AddComponent(string component, AbstractClause clause, string engineCode
return (Q)this;
}

/// <summary>
/// If the query already contains a clause for the given component
/// and engine, replace it with the specified clause. Otherwise, just
/// add the clause.
/// </summary>
/// <param name="component"></param>
/// <param name="clause"></param>
/// <param name="engineCode"></param>
/// <returns></returns>
public Q AddOrReplaceComponent(string component, AbstractClause clause, string engineCode = null)
{
engineCode = engineCode ?? EngineScope;

var current = GetComponents(component).SingleOrDefault(c => c.Engine == engineCode);
if (current != null)
Clauses.Remove(current);

return AddComponent(component, clause, engineCode);
}



/// <summary>
/// Get the list of clauses for a component.
/// </summary>
Expand Down Expand Up @@ -123,13 +145,10 @@ public List<AbstractClause> GetComponents(string component, string engineCode =
/// <returns></returns>
public C GetOneComponent<C>(string component, string engineCode = null) where C : AbstractClause
{
if (engineCode == null)
{
engineCode = EngineScope;
}
engineCode = engineCode ?? EngineScope;

return GetComponents<C>(component, engineCode)
.FirstOrDefault();
var all = GetComponents<C>(component, engineCode);
return all.FirstOrDefault(c => c.Engine == engineCode) ?? all.FirstOrDefault(c => c.Engine == null);
}

/// <summary>
Expand All @@ -149,7 +168,7 @@ public AbstractClause GetOneComponent(string component, string engineCode = null
}

/// <summary>
/// Return wether the query has clauses for a component.
/// Return whether the query has clauses for a component.
/// </summary>
/// <param name="component"></param>
/// <param name="engineCode"></param>
Expand Down Expand Up @@ -247,9 +266,9 @@ protected bool GetNot()
/// <returns></returns>
public Q From(string table)
{
return ClearComponent("from").AddComponent("from", new FromClause
return AddOrReplaceComponent("from", new FromClause
{
Table = table
Table = table,
});
}

Expand All @@ -263,15 +282,15 @@ public Q From(Query query, string alias = null)
query.As(alias);
};

return ClearComponent("from").AddComponent("from", new QueryFromClause
return AddOrReplaceComponent("from", new QueryFromClause
{
Query = query
});
}

public Q FromRaw(string sql, params object[] bindings)
{
return ClearComponent("from").AddComponent("from", new RawFromClause
return AddOrReplaceComponent("from", new RawFromClause
{
Expression = sql,
Bindings = bindings,
Expand Down
1 change: 1 addition & 0 deletions QueryBuilder/Clauses/FromClause.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public override AbstractClause Clone()
{
return new FromClause
{
Engine = Engine,
Alias = Alias,
Table = Table,
Component = Component,
Expand Down
Loading