Skip to content

Commit e1d33a7

Browse files
Merge pull request #243 from asherber/from-preserve-engine
Include the Engine when cloning a FromClause
2 parents 3e3a054 + 9e16e9d commit e1d33a7

File tree

7 files changed

+312
-91
lines changed

7 files changed

+312
-91
lines changed

QueryBuilder.Tests/GeneralTests.cs

Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
using SqlKata.Compilers;
22
using SqlKata.Extensions;
33
using SqlKata.Tests.Infrastructure;
4+
using System;
5+
using System.Linq;
46
using Xunit;
57

68
namespace SqlKata.Tests
@@ -160,5 +162,227 @@ public void WrapWithMultipleSpaces()
160162

161163
Assert.Equal("[My Table One] AS [Table One]", compiler.Wrap("My Table One as Table One"));
162164
}
165+
166+
[Fact]
167+
public void CompilerSpecificFrom()
168+
{
169+
var query = new Query()
170+
.ForSqlServer(q => q.From("mssql"))
171+
.ForPostgreSql(q => q.From("pgsql"))
172+
.ForMySql(q => q.From("mysql"));
173+
var engines = new[] { EngineCodes.SqlServer, EngineCodes.MySql, EngineCodes.PostgreSql };
174+
var c = Compilers.Compile(engines, query);
175+
176+
Assert.Equal("SELECT * FROM [mssql]", c[EngineCodes.SqlServer].RawSql);
177+
Assert.Equal("SELECT * FROM \"pgsql\"", c[EngineCodes.PostgreSql].RawSql);
178+
Assert.Equal("SELECT * FROM `mysql`", c[EngineCodes.MySql].RawSql);
179+
}
180+
181+
[Fact]
182+
public void CompilerSpecificFromRaw()
183+
{
184+
var query = new Query()
185+
.ForSqlServer(q => q.FromRaw("[mssql]"))
186+
.ForPostgreSql(q => q.FromRaw("[pgsql]"))
187+
.ForMySql(q => q.FromRaw("[mysql]"));
188+
var engines = new[] { EngineCodes.SqlServer, EngineCodes.MySql, EngineCodes.PostgreSql };
189+
var c = Compilers.Compile(engines, query);
190+
191+
Assert.Equal("SELECT * FROM [mssql]", c[EngineCodes.SqlServer].RawSql);
192+
Assert.Equal("SELECT * FROM \"pgsql\"", c[EngineCodes.PostgreSql].RawSql);
193+
Assert.Equal("SELECT * FROM `mysql`", c[EngineCodes.MySql].RawSql);
194+
}
195+
196+
[Fact]
197+
public void CompilerSpecificFromMixed()
198+
{
199+
var query = new Query()
200+
.ForSqlServer(q => q.From("mssql"))
201+
.ForPostgreSql(q => q.FromRaw("[pgsql]"))
202+
.ForMySql(q => q.From("mysql"));
203+
var engines = new[] { EngineCodes.SqlServer, EngineCodes.MySql, EngineCodes.PostgreSql };
204+
var c = Compilers.Compile(engines, query);
205+
206+
Assert.Equal("SELECT * FROM [mssql]", c[EngineCodes.SqlServer].RawSql);
207+
Assert.Equal("SELECT * FROM \"pgsql\"", c[EngineCodes.PostgreSql].RawSql);
208+
Assert.Equal("SELECT * FROM `mysql`", c[EngineCodes.MySql].RawSql);
209+
}
210+
211+
[Fact]
212+
public void OneFromPerEngine()
213+
{
214+
var query = new Query("generic")
215+
.ForSqlServer(q => q.From("dnu"))
216+
.ForSqlServer(q => q.From("mssql"));
217+
var engines = new[] { EngineCodes.SqlServer, EngineCodes.MySql, EngineCodes.PostgreSql };
218+
var c = Compilers.Compile(engines, query);
219+
220+
Assert.Equal(2, query.Clauses.OfType<AbstractFrom>().Count());
221+
Assert.Equal("SELECT * FROM [mssql]", c[EngineCodes.SqlServer].RawSql);
222+
Assert.Equal("SELECT * FROM \"generic\"", c[EngineCodes.PostgreSql].RawSql);
223+
Assert.Equal("SELECT * FROM `generic`", c[EngineCodes.MySql].RawSql);
224+
}
225+
226+
[Theory]
227+
[InlineData(null, null)]
228+
[InlineData(null, "mssql")]
229+
[InlineData("original", null)]
230+
[InlineData("original", "mssql")]
231+
public void AddOrReplace_Works(string table, string engine)
232+
{
233+
var query = new Query();
234+
if (table != null)
235+
query.From(table);
236+
query.AddOrReplaceComponent("from", new FromClause() { Table = "updated", Engine = engine });
237+
var froms = query.Clauses.OfType<FromClause>();
238+
239+
Assert.Single(froms);
240+
Assert.Equal("updated", froms.Single().Table);
241+
}
242+
243+
[Theory]
244+
[InlineData(null, "generic")]
245+
[InlineData(EngineCodes.SqlServer, "mssql")]
246+
[InlineData(EngineCodes.MySql, "generic")]
247+
public void GetOneComponent_Prefers_Engine(string engine, string column)
248+
{
249+
var query = new Query()
250+
.Where("generic", "foo")
251+
.ForSqlServer(q => q.Where("mssql", "foo"));
252+
253+
var where = query.GetOneComponent("where", engine) as BasicCondition;
254+
255+
Assert.NotNull(where);
256+
Assert.Equal(column, where.Column);
257+
}
258+
259+
[Fact]
260+
public void AddOrReplace_Throws_MoreThanOne()
261+
{
262+
var query = new Query()
263+
.Where("a", "b")
264+
.Where("c", "d");
265+
266+
Action act = () => query.AddOrReplaceComponent("where", new BasicCondition());
267+
Assert.Throws<InvalidOperationException>(act);
268+
}
269+
270+
[Fact]
271+
public void OneLimitPerEngine()
272+
{
273+
var query = new Query("mytable")
274+
.ForSqlServer(q => q.Limit(5))
275+
.ForSqlServer(q => q.Limit(10));
276+
277+
var limits = query.GetComponents<LimitClause>("limit", EngineCodes.SqlServer);
278+
Assert.Single(limits);
279+
Assert.Equal(10, limits.Single().Limit);
280+
}
281+
282+
[Fact]
283+
public void CompilerSpecificLimit()
284+
{
285+
var query = new Query("mytable")
286+
.ForSqlServer(q => q.Limit(5))
287+
.ForPostgreSql(q => q.Limit(10));
288+
289+
var engines = new[] { EngineCodes.SqlServer, EngineCodes.MySql, EngineCodes.PostgreSql };
290+
var c = Compilers.Compile(engines, query);
291+
292+
Assert.Equal(2, query.GetComponents("limit").Count());
293+
Assert.Equal("SELECT TOP (5) * FROM [mytable]", c[EngineCodes.SqlServer].ToString());
294+
Assert.Equal("SELECT * FROM \"mytable\" LIMIT 10", c[EngineCodes.PostgreSql].ToString());
295+
Assert.Equal("SELECT * FROM `mytable`", c[EngineCodes.MySql].ToString());
296+
}
297+
298+
[Fact]
299+
public void OneOffsetPerEngine()
300+
{
301+
var query = new Query("mytable")
302+
.ForSqlServer(q => q.Offset(5))
303+
.ForSqlServer(q => q.Offset(10));
304+
305+
var limits = query.GetComponents<OffsetClause>("offset", EngineCodes.SqlServer);
306+
Assert.Single(limits);
307+
Assert.Equal(10, limits.Single().Offset);
308+
}
309+
310+
[Fact]
311+
public void CompilerSpecificOffset()
312+
{
313+
var query = new Query("mytable")
314+
.ForMySql(q => q.Offset(5))
315+
.ForPostgreSql(q => q.Offset(10));
316+
317+
var engines = new[] { EngineCodes.SqlServer, EngineCodes.MySql, EngineCodes.PostgreSql };
318+
var c = Compilers.Compile(engines, query);
319+
320+
Assert.Equal(2, query.GetComponents("offset").Count());
321+
Assert.Equal("SELECT * FROM `mytable` LIMIT 18446744073709551615 OFFSET 5", c[EngineCodes.MySql].ToString());
322+
Assert.Equal("SELECT * FROM \"mytable\" OFFSET 10", c[EngineCodes.PostgreSql].ToString());
323+
Assert.Equal("SELECT * FROM [mytable]", c[EngineCodes.SqlServer].ToString());
324+
}
325+
326+
[Fact]
327+
public void Limit_Takes_Generic_If_Needed()
328+
{
329+
var query = new Query("mytable")
330+
.Limit(5)
331+
.Offset(10)
332+
.ForPostgreSql(q => q.Offset(20));
333+
334+
var engines = new[] { EngineCodes.MySql, EngineCodes.PostgreSql };
335+
var c = Compilers.Compile(engines, query);
336+
337+
Assert.Equal("SELECT * FROM `mytable` LIMIT 5 OFFSET 10", c[EngineCodes.MySql].ToString());
338+
Assert.Equal("SELECT * FROM \"mytable\" LIMIT 5 OFFSET 20", c[EngineCodes.PostgreSql].ToString());
339+
}
340+
341+
[Fact]
342+
public void Offset_Takes_Generic_If_Needed()
343+
{
344+
var query = new Query("mytable")
345+
.Limit(5)
346+
.Offset(10)
347+
.ForPostgreSql(q => q.Limit(20));
348+
349+
var engines = new[] { EngineCodes.MySql, EngineCodes.PostgreSql };
350+
var c = Compilers.Compile(engines, query);
351+
352+
Assert.Equal("SELECT * FROM `mytable` LIMIT 5 OFFSET 10", c[EngineCodes.MySql].ToString());
353+
Assert.Equal("SELECT * FROM \"mytable\" LIMIT 20 OFFSET 10", c[EngineCodes.PostgreSql].ToString());
354+
}
355+
356+
[Fact]
357+
public void Can_Change_Generic_Limit_After_SpecificOffset()
358+
{
359+
var query = new Query("mytable")
360+
.Limit(5)
361+
.Offset(10)
362+
.ForPostgreSql(q => q.Offset(20))
363+
.Limit(7);
364+
365+
var engines = new[] { EngineCodes.MySql, EngineCodes.PostgreSql };
366+
var c = Compilers.Compile(engines, query);
367+
368+
Assert.Equal("SELECT * FROM `mytable` LIMIT 7 OFFSET 10", c[EngineCodes.MySql].ToString());
369+
Assert.Equal("SELECT * FROM \"mytable\" LIMIT 7 OFFSET 20", c[EngineCodes.PostgreSql].ToString());
370+
}
371+
372+
[Fact]
373+
public void Can_Change_Generic_Offset_After_SpecificLimit()
374+
{
375+
var query = new Query("mytable")
376+
.Limit(5)
377+
.Offset(10)
378+
.ForPostgreSql(q => q.Limit(20))
379+
.Offset(7);
380+
381+
var engines = new[] { EngineCodes.MySql, EngineCodes.PostgreSql };
382+
var c = Compilers.Compile(engines, query);
383+
384+
Assert.Equal("SELECT * FROM `mytable` LIMIT 5 OFFSET 7", c[EngineCodes.MySql].ToString());
385+
Assert.Equal("SELECT * FROM \"mytable\" LIMIT 20 OFFSET 7", c[EngineCodes.PostgreSql].ToString());
386+
}
163387
}
164388
}

QueryBuilder/BaseQuery.cs

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,28 @@ public Q AddComponent(string component, AbstractClause clause, string engineCode
8282
return (Q)this;
8383
}
8484

85+
/// <summary>
86+
/// If the query already contains a clause for the given component
87+
/// and engine, replace it with the specified clause. Otherwise, just
88+
/// add the clause.
89+
/// </summary>
90+
/// <param name="component"></param>
91+
/// <param name="clause"></param>
92+
/// <param name="engineCode"></param>
93+
/// <returns></returns>
94+
public Q AddOrReplaceComponent(string component, AbstractClause clause, string engineCode = null)
95+
{
96+
engineCode = engineCode ?? EngineScope;
97+
98+
var current = GetComponents(component).SingleOrDefault(c => c.Engine == engineCode);
99+
if (current != null)
100+
Clauses.Remove(current);
101+
102+
return AddComponent(component, clause, engineCode);
103+
}
104+
105+
106+
85107
/// <summary>
86108
/// Get the list of clauses for a component.
87109
/// </summary>
@@ -123,13 +145,10 @@ public List<AbstractClause> GetComponents(string component, string engineCode =
123145
/// <returns></returns>
124146
public C GetOneComponent<C>(string component, string engineCode = null) where C : AbstractClause
125147
{
126-
if (engineCode == null)
127-
{
128-
engineCode = EngineScope;
129-
}
148+
engineCode = engineCode ?? EngineScope;
130149

131-
return GetComponents<C>(component, engineCode)
132-
.FirstOrDefault();
150+
var all = GetComponents<C>(component, engineCode);
151+
return all.FirstOrDefault(c => c.Engine == engineCode) ?? all.FirstOrDefault(c => c.Engine == null);
133152
}
134153

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

151170
/// <summary>
152-
/// Return wether the query has clauses for a component.
171+
/// Return whether the query has clauses for a component.
153172
/// </summary>
154173
/// <param name="component"></param>
155174
/// <param name="engineCode"></param>
@@ -247,9 +266,9 @@ protected bool GetNot()
247266
/// <returns></returns>
248267
public Q From(string table)
249268
{
250-
return ClearComponent("from").AddComponent("from", new FromClause
269+
return AddOrReplaceComponent("from", new FromClause
251270
{
252-
Table = table
271+
Table = table,
253272
});
254273
}
255274

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

266-
return ClearComponent("from").AddComponent("from", new QueryFromClause
285+
return AddOrReplaceComponent("from", new QueryFromClause
267286
{
268287
Query = query
269288
});
270289
}
271290

272291
public Q FromRaw(string sql, params object[] bindings)
273292
{
274-
return ClearComponent("from").AddComponent("from", new RawFromClause
293+
return AddOrReplaceComponent("from", new RawFromClause
275294
{
276295
Expression = sql,
277296
Bindings = bindings,

QueryBuilder/Clauses/FromClause.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ public override AbstractClause Clone()
4040
{
4141
return new FromClause
4242
{
43+
Engine = Engine,
4344
Alias = Alias,
4445
Table = Table,
4546
Component = Component,

0 commit comments

Comments
 (0)