Skip to content

Commit 6f19ea8

Browse files
committed
Add insert many overload
Use dictionary in insert clause Fixed sql server compiler test runner Improved string build performance
1 parent 8ae0e9d commit 6f19ea8

File tree

9 files changed

+149
-58
lines changed

9 files changed

+149
-58
lines changed

QueryBuilder.Tests/Infrastructure/TestCompilersContainer.cs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,14 @@ private static class Messages
1313
public const string ERR_INVALID_ENGINECODES = "Invalid engine codes supplied '{0}'";
1414
}
1515

16-
protected readonly IDictionary<string, Compiler> Compilers = new Dictionary<string, Compiler>
16+
protected readonly IDictionary<string, Func<Compiler>> Compilers = new Dictionary<string, Func<Compiler>>
1717
{
18-
[EngineCodes.Firebird] = new FirebirdCompiler(),
19-
[EngineCodes.MySql] = new MySqlCompiler(),
20-
[EngineCodes.Oracle] = new OracleCompiler(),
21-
[EngineCodes.PostgreSql] = new PostgresCompiler(),
22-
[EngineCodes.Sqlite] = new SqliteCompiler(),
23-
[EngineCodes.SqlServer] = new SqlServerCompiler()
18+
[EngineCodes.Firebird] = () => new FirebirdCompiler(),
19+
[EngineCodes.MySql] = () => new MySqlCompiler(),
20+
[EngineCodes.Oracle] = () => new OracleCompiler(),
21+
[EngineCodes.PostgreSql] = () => new PostgresCompiler(),
22+
[EngineCodes.Sqlite] = () => new SqliteCompiler(),
23+
[EngineCodes.SqlServer] = () => new SqlServerCompiler { UseLegacyPagination = true }
2424
};
2525

2626
public IEnumerable<string> KnownEngineCodes
@@ -40,7 +40,7 @@ public Compiler Get(string engineCode)
4040
throw new InvalidOperationException(string.Format(Messages.ERR_INVALID_ENGINECODE, engineCode));
4141
}
4242

43-
return Compilers[engineCode];
43+
return Compilers[engineCode]();
4444
}
4545

4646
/// <summary>
@@ -79,7 +79,7 @@ public TestSqlResultContainer Compile(IEnumerable<string> engineCodes, Query que
7979

8080
var results = Compilers
8181
.Where(w => codes.Contains(w.Key))
82-
.ToDictionary(k => k.Key, v => v.Value.Compile(query.Clone()));
82+
.ToDictionary(k => k.Key, v => v.Value().Compile(query.Clone()));
8383

8484
if (results.Count != codes.Count)
8585
{
@@ -99,7 +99,7 @@ public TestSqlResultContainer Compile(IEnumerable<string> engineCodes, Query que
9999
public TestSqlResultContainer Compile(Query query)
100100
{
101101
var resultKeyValues = Compilers
102-
.ToDictionary(k => k.Key, v => v.Value.Compile(query.Clone()));
102+
.ToDictionary(k => k.Key, v => v.Value().Compile(query.Clone()));
103103
return new TestSqlResultContainer(resultKeyValues);
104104
}
105105
}

QueryBuilder.Tests/InsertTests.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,30 @@ public void InsertMultiRecords()
9999
c[EngineCodes.Firebird]);
100100
}
101101

102+
[Fact]
103+
public void InsertMultiRecordsByDictionary()
104+
{
105+
var data = new List<Dictionary<string, object>>
106+
{
107+
new() { { "name", "Chiron" }, { "brand", "Bugatti" }, { "year", null } },
108+
new() { { "name", "Huayra" }, { "brand", "Pagani" }, { "year", 2012 } },
109+
new() { { "name", "Reventon roadster" }, { "brand", "Lamborghini" }, { "year", 2009 } }
110+
};
111+
112+
var query = new Query("expensive_cars")
113+
.AsInsert(data);
114+
115+
var c = Compile(query);
116+
117+
Assert.Equal(
118+
"INSERT INTO [expensive_cars] ([name], [brand], [year]) VALUES ('Chiron', 'Bugatti', NULL), ('Huayra', 'Pagani', 2012), ('Reventon roadster', 'Lamborghini', 2009)",
119+
c[EngineCodes.SqlServer]);
120+
121+
Assert.Equal(
122+
"INSERT INTO \"EXPENSIVE_CARS\" (\"NAME\", \"BRAND\", \"YEAR\") SELECT 'Chiron', 'Bugatti', NULL FROM RDB$DATABASE UNION ALL SELECT 'Huayra', 'Pagani', 2012 FROM RDB$DATABASE UNION ALL SELECT 'Reventon roadster', 'Lamborghini', 2009 FROM RDB$DATABASE",
123+
c[EngineCodes.Firebird]);
124+
}
125+
102126
[Fact]
103127
public void InsertWithNullValues()
104128
{

QueryBuilder/Clauses/InsertClause.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@ public abstract class AbstractInsertClause : AbstractClause
99

1010
public class InsertClause : AbstractInsertClause
1111
{
12-
public List<string> Columns { get; set; }
13-
public List<object> Values { get; set; }
12+
public Dictionary<string, object> Data { get; set; }
1413
public bool ReturnId { get; set; } = false;
1514

1615
public override AbstractClause Clone()
@@ -19,8 +18,7 @@ public override AbstractClause Clone()
1918
{
2019
Engine = Engine,
2120
Component = Component,
22-
Columns = Columns,
23-
Values = Values,
21+
Data = Data,
2422
ReturnId = ReturnId,
2523
};
2624
}

QueryBuilder/Compilers/Compiler.Conditions.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Collections.Generic;
33
using System.Linq;
44
using System.Reflection;
5+
using System.Text;
56

67
namespace SqlKata.Compilers
78
{
@@ -43,7 +44,7 @@ protected virtual string CompileCondition(SqlResult ctx, AbstractCondition claus
4344

4445
protected virtual string CompileConditions(SqlResult ctx, List<AbstractCondition> conditions)
4546
{
46-
var result = new List<string>();
47+
var result = new StringBuilder();
4748

4849
for (var i = 0; i < conditions.Count; i++)
4950
{
@@ -56,10 +57,10 @@ protected virtual string CompileConditions(SqlResult ctx, List<AbstractCondition
5657

5758
var boolOperator = i == 0 ? "" : (conditions[i].IsOr ? "OR " : "AND ");
5859

59-
result.Add(boolOperator + compiled);
60+
result.Append(boolOperator + compiled + " ");
6061
}
6162

62-
return string.Join(" ", result);
63+
return result.ToString().Trim();
6364
}
6465

6566
protected virtual string CompileRawCondition(SqlResult ctx, RawCondition x)

QueryBuilder/Compilers/Compiler.cs

Lines changed: 28 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -332,23 +332,25 @@ protected virtual SqlResult CompileUpdateQuery(Query query)
332332

333333

334334
var toUpdate = ctx.Query.GetOneComponent<InsertClause>("update", EngineCode);
335-
var parts = new List<string>();
335+
var parts = new StringBuilder(toUpdate.Data.Count);
336336

337-
for (var i = 0; i < toUpdate.Columns.Count; i++)
337+
var separator = ", ";
338+
339+
foreach (var item in toUpdate.Data)
338340
{
339-
parts.Add(Wrap(toUpdate.Columns[i]) + " = " + Parameter(ctx, toUpdate.Values[i]));
341+
parts.Append(Wrap(item.Key) + " = " + Parameter(ctx, item.Value) + separator);
340342
}
341343

342-
var sets = string.Join(", ", parts);
343-
344+
parts.Length -= separator.Length;
345+
344346
wheres = CompileWheres(ctx);
345347

346348
if (!string.IsNullOrEmpty(wheres))
347349
{
348350
wheres = " " + wheres;
349351
}
350352

351-
ctx.RawSql = $"UPDATE {table} SET {sets}{wheres}";
353+
ctx.RawSql = $"UPDATE {table} SET {parts}{wheres}".Trim();
352354

353355
return ctx;
354356
}
@@ -392,16 +394,18 @@ protected virtual SqlResult CompileInsertQuery(Query query)
392394

393395
var inserts = ctx.Query.GetComponents<AbstractInsertClause>("insert", EngineCode);
394396

397+
var sql = new StringBuilder(inserts.Count + 1);
398+
395399
if (inserts[0] is InsertClause insertClause)
396400
{
397-
var columns = string.Join(", ", WrapArray(insertClause.Columns));
398-
var values = string.Join(", ", Parameterize(ctx, insertClause.Values));
401+
var columns = string.Join(", ", WrapArray(insertClause.Data.Keys));
402+
var values = string.Join(", ", Parameterize(ctx, insertClause.Data.Values));
399403

400-
ctx.RawSql = $"INSERT INTO {table} ({columns}) VALUES ({values})";
404+
sql.Append($"INSERT INTO {table} ({columns}) VALUES ({values})");
401405

402406
if (insertClause.ReturnId && !string.IsNullOrEmpty(LastId))
403407
{
404-
ctx.RawSql += ";" + LastId;
408+
sql.Append(";" + LastId);
405409
}
406410
}
407411
else
@@ -418,7 +422,7 @@ protected virtual SqlResult CompileInsertQuery(Query query)
418422
var subCtx = CompileSelectQuery(clause.Query);
419423
ctx.Bindings.AddRange(subCtx.Bindings);
420424

421-
ctx.RawSql = $"INSERT INTO {table}{columns}{subCtx.RawSql}";
425+
sql.Append($"INSERT INTO {table}{columns}{subCtx.RawSql}");
422426
}
423427

424428
if (inserts.Count > 1)
@@ -427,11 +431,11 @@ protected virtual SqlResult CompileInsertQuery(Query query)
427431
{
428432
var clause = insert as InsertClause;
429433

430-
ctx.RawSql += ", (" + string.Join(", ", Parameterize(ctx, clause.Values)) + ")";
431-
434+
sql.Append(", (" + string.Join(", ", Parameterize(ctx, clause.Data.Values)) + ")");
432435
}
433436
}
434437

438+
ctx.RawSql = sql.ToString().Trim();
435439

436440
return ctx;
437441
}
@@ -442,7 +446,7 @@ protected virtual SqlResult CompileCteQuery(SqlResult ctx, Query query)
442446
var cteFinder = new CteFinder(query, EngineCode);
443447
var cteSearchResult = cteFinder.Find();
444448

445-
var rawSql = new StringBuilder("WITH ");
449+
var rawSql = new StringBuilder("WITH ", cteSearchResult.Count * 2 + 3);
446450
var cteBindings = new List<object>();
447451

448452
foreach (var cte in cteSearchResult)
@@ -459,7 +463,7 @@ protected virtual SqlResult CompileCteQuery(SqlResult ctx, Query query)
459463
rawSql.Append(ctx.RawSql);
460464

461465
ctx.Bindings.InsertRange(0, cteBindings);
462-
ctx.RawSql = rawSql.ToString();
466+
ctx.RawSql = rawSql.ToString().Trim();
463467

464468
return ctx;
465469
}
@@ -585,7 +589,7 @@ public virtual string CompileUnion(SqlResult ctx)
585589
return null;
586590
}
587591

588-
var combinedQueries = new List<string>();
592+
var combinedQueries = new StringBuilder();
589593

590594
var clauses = ctx.Query.GetComponents<AbstractCombine>("combine", EngineCode);
591595

@@ -599,21 +603,19 @@ public virtual string CompileUnion(SqlResult ctx)
599603

600604
ctx.Bindings.AddRange(subCtx.Bindings);
601605

602-
combinedQueries.Add($"{combineOperator}{subCtx.RawSql}");
606+
combinedQueries.Append($"{combineOperator}{subCtx.RawSql} ");
603607
}
604608
else
605609
{
606610
var combineRawClause = clause as RawCombine;
607611

608612
ctx.Bindings.AddRange(combineRawClause.Bindings);
609613

610-
combinedQueries.Add(WrapIdentifiers(combineRawClause.Expression));
611-
614+
combinedQueries.Append(WrapIdentifiers(combineRawClause.Expression) + " ");
612615
}
613616
}
614617

615-
return string.Join(" ", combinedQueries);
616-
618+
return combinedQueries.ToString().Trim();
617619
}
618620

619621
public virtual string CompileTableExpression(SqlResult ctx, AbstractFrom from)
@@ -745,7 +747,7 @@ public virtual string CompileHaving(SqlResult ctx)
745747
return null;
746748
}
747749

748-
var sql = new List<string>();
750+
var sql = new StringBuilder();
749751
string boolOperator;
750752

751753
var having = ctx.Query.GetComponents("having", EngineCode)
@@ -760,11 +762,11 @@ public virtual string CompileHaving(SqlResult ctx)
760762
{
761763
boolOperator = i > 0 ? having[i].IsOr ? "OR " : "AND " : "";
762764

763-
sql.Add(boolOperator + compiled);
765+
sql.Append(boolOperator + compiled + " ");
764766
}
765767
}
766768

767-
return $"HAVING {string.Join(" ", sql)}";
769+
return $"HAVING {sql}".Trim();
768770
}
769771

770772
public virtual string CompileLimit(SqlResult ctx)
@@ -958,9 +960,9 @@ public virtual string Parameterize<T>(SqlResult ctx, IEnumerable<T> values)
958960
/// </summary>
959961
/// <param name="values"></param>
960962
/// <returns></returns>
961-
public virtual List<string> WrapArray(List<string> values)
963+
public virtual IEnumerable<string> WrapArray(IEnumerable<string> values)
962964
{
963-
return values.Select(x => Wrap(x)).ToList();
965+
return values.Select(Wrap);
964966
}
965967

966968
public virtual string WrapIdentifiers(string input)
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
4+
namespace SqlKata.Extensions
5+
{
6+
public static class CollectionExtensions
7+
{
8+
public static Dictionary<string, object> MergeKeysAndValues(this List<string> keys, List<object> values)
9+
{
10+
var data = new Dictionary<string, object>();
11+
12+
for (var i = 0; i < keys.Count; i++)
13+
{
14+
data.Add(keys[i], values[i]);
15+
}
16+
17+
return data;
18+
}
19+
20+
public static Dictionary<string, object> CreateDictionary(this IEnumerable<KeyValuePair<string, object>> values)
21+
{
22+
if (values is Dictionary<string, object> dictionary)
23+
{
24+
return dictionary;
25+
}
26+
27+
return values.ToDictionary(x => x.Key, x => x.Value);
28+
}
29+
}
30+
}

QueryBuilder/Helper.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -96,14 +96,16 @@ public static string ReplaceAll(string subject, string match, Func<int, string>
9696

9797
public static string JoinArray(string glue, IEnumerable array)
9898
{
99-
var result = new List<string>();
99+
var result = new StringBuilder();
100100

101101
foreach (var item in array)
102102
{
103-
result.Add(item.ToString());
103+
result.Append(item + glue);
104104
}
105105

106-
return string.Join(glue, result);
106+
result.Length -= glue.Length;
107+
108+
return result.ToString().Trim();
107109
}
108110

109111
public static string ExpandParameters(string sql, string placeholder, object[] bindings)

0 commit comments

Comments
 (0)