Skip to content

Commit 3535479

Browse files
author
Wood, Roger C
committed
Merge from upstream master.
2 parents b403f9c + bfa39f5 commit 3535479

File tree

10 files changed

+93
-86
lines changed

10 files changed

+93
-86
lines changed

QueryBuilder/ColumnAttribute.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ public ColumnAttribute(string name)
1414
{
1515
throw new ArgumentNullException("Name parameter is required");
1616
}
17+
1718
Name = name;
1819
}
1920

QueryBuilder/Compilers/Compiler.Conditions.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,12 @@ protected virtual string CompileCondition(SqlResult ctx, AbstractCondition claus
2626

2727
try
2828
{
29-
var result = methodInfo.Invoke(this, new object[] { ctx, clause });
29+
30+
var result = methodInfo.Invoke(this, new object[] {
31+
ctx,
32+
clause
33+
});
34+
3035
return result as string;
3136
}
3237
catch (Exception ex)

QueryBuilder/Compilers/Compiler.cs

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ public partial class Compiler
99
{
1010
private readonly ConditionsCompilerProvider _compileConditionMethodsProvider;
1111
protected virtual string parameterPlaceholder { get; set; } = "?";
12-
protected virtual string parameterPlaceholderPrefix { get; set; } = "@p";
12+
protected virtual string parameterPrefix { get; set; } = "@p";
1313
protected virtual string OpeningIdentifier { get; set; } = "\"";
1414
protected virtual string ClosingIdentifier { get; set; } = "\"";
1515
protected virtual string ColumnAsKeyword { get; set; } = "AS ";
@@ -48,13 +48,13 @@ protected Compiler()
4848
protected Dictionary<string, object> generateNamedBindings(object[] bindings)
4949
{
5050
return Helper.Flatten(bindings).Select((v, i) => new { i, v })
51-
.ToDictionary(x => parameterPlaceholderPrefix + x.i, x => x.v);
51+
.ToDictionary(x => parameterPrefix + x.i, x => x.v);
5252
}
5353

5454
protected SqlResult PrepareResult(SqlResult ctx)
5555
{
5656
ctx.NamedBindings = generateNamedBindings(ctx.Bindings.ToArray());
57-
ctx.Sql = Helper.ReplaceAll(ctx.RawSql, parameterPlaceholder, i => parameterPlaceholderPrefix + i);
57+
ctx.Sql = Helper.ReplaceAll(ctx.RawSql, parameterPlaceholder, i => parameterPrefix + i);
5858
return ctx;
5959
}
6060

@@ -79,7 +79,7 @@ private Query TransformAggregateQuery(Query query)
7979

8080
var outerClause = new AggregateClause()
8181
{
82-
Columns = new List<string> {"*"},
82+
Columns = new List<string> { "*" },
8383
Type = clause.Type
8484
};
8585

@@ -129,6 +129,13 @@ protected virtual SqlResult CompileRaw(Query query)
129129
return ctx;
130130
}
131131

132+
/// <summary>
133+
/// Add the passed operator(s) to the white list so they can be used with
134+
/// the Where/Having methods, this prevent passing arbitrary operators
135+
/// that opens the door for SQL injections.
136+
/// </summary>
137+
/// <param name="operators"></param>
138+
/// <returns></returns>
132139
public Compiler Whitelist(params string[] operators)
133140
{
134141
foreach (var op in operators)
@@ -300,9 +307,10 @@ protected virtual SqlResult CompileInsertQuery(Query query)
300307

301308
if (inserts[0] is InsertClause insertClause)
302309
{
303-
ctx.RawSql = $"INSERT INTO {table}"
304-
+ " (" + string.Join(", ", WrapArray(insertClause.Columns)) + ") "
305-
+ "VALUES (" + string.Join(", ", Parameterize(ctx, insertClause.Values)) + ")";
310+
var columns = string.Join(", ", WrapArray(insertClause.Columns));
311+
var values = string.Join(", ", Parameterize(ctx, insertClause.Values));
312+
313+
ctx.RawSql = $"INSERT INTO {table} ({columns}) VALUES ({values})";
306314

307315
if (insertClause.ReturnId && !string.IsNullOrEmpty(LastId))
308316
{
@@ -817,11 +825,11 @@ public virtual string WrapIdentifiers(string input)
817825
return input
818826

819827
// deprecated
820-
.ReplaceIdentifierUnlessEscaped(this.EscapeCharacter,"{", this.OpeningIdentifier)
821-
.ReplaceIdentifierUnlessEscaped(this.EscapeCharacter,"}", this.ClosingIdentifier)
828+
.ReplaceIdentifierUnlessEscaped(this.EscapeCharacter, "{", this.OpeningIdentifier)
829+
.ReplaceIdentifierUnlessEscaped(this.EscapeCharacter, "}", this.ClosingIdentifier)
822830

823-
.ReplaceIdentifierUnlessEscaped(this.EscapeCharacter,"[", this.OpeningIdentifier)
824-
.ReplaceIdentifierUnlessEscaped(this.EscapeCharacter,"]", this.ClosingIdentifier);
831+
.ReplaceIdentifierUnlessEscaped(this.EscapeCharacter, "[", this.OpeningIdentifier)
832+
.ReplaceIdentifierUnlessEscaped(this.EscapeCharacter, "]", this.ClosingIdentifier);
825833
}
826834
}
827835
}

QueryBuilder/Compilers/CteFinder.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ public class CteFinder
88
private readonly string engineCode;
99
private HashSet<string> namesOfPreviousCtes;
1010
private List<AbstractFrom> orderedCteList;
11-
11+
1212
public CteFinder(Query query, string engineCode)
1313
{
1414
this.query = query;
@@ -21,21 +21,21 @@ public List<AbstractFrom> Find()
2121
return orderedCteList;
2222

2323
namesOfPreviousCtes = new HashSet<string>();
24-
25-
orderedCteList = FindInternal(query);
26-
24+
25+
orderedCteList = findInternal(query);
26+
2727
namesOfPreviousCtes.Clear();
2828
namesOfPreviousCtes = null;
2929

3030
return orderedCteList;
3131
}
3232

33-
private List<AbstractFrom> FindInternal(Query queryToSearch)
33+
private List<AbstractFrom> findInternal(Query queryToSearch)
3434
{
3535
var cteList = queryToSearch.GetComponents<AbstractFrom>("cte", engineCode);
3636

3737
var resultList = new List<AbstractFrom>();
38-
38+
3939
foreach (var cte in cteList)
4040
{
4141
if (namesOfPreviousCtes.Contains(cte.Alias))
@@ -46,7 +46,7 @@ private List<AbstractFrom> FindInternal(Query queryToSearch)
4646

4747
if (cte is QueryFromClause queryFromClause)
4848
{
49-
resultList.InsertRange(0, FindInternal(queryFromClause.Query));
49+
resultList.InsertRange(0, findInternal(queryFromClause.Query));
5050
}
5151
}
5252

QueryBuilder/Compilers/OracleCompiler.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ public OracleCompiler()
1111
{
1212
ColumnAsKeyword = "";
1313
TableAsKeyword = "";
14-
parameterPlaceholderPrefix = ":p";
14+
parameterPrefix = ":p";
1515
}
1616

1717
public override string EngineCode { get; } = EngineCodes.Oracle;

QueryBuilder/Compilers/SqliteCompiler.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ public class SqliteCompiler : Compiler
88
{
99
public override string EngineCode { get; } = EngineCodes.Sqlite;
1010
protected override string parameterPlaceholder { get; set; } = "?";
11-
protected override string parameterPlaceholderPrefix { get; set; } = "@p";
11+
protected override string parameterPrefix { get; set; } = "@p";
1212
protected override string OpeningIdentifier { get; set; } = "\"";
1313
protected override string ClosingIdentifier { get; set; } = "\"";
1414
protected override string LastId { get; set; } = "select last_insert_rowid()";

QueryBuilder/IgnoreAttribute.cs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,22 @@
33
namespace SqlKata
44
{
55
/// <summary>
6-
/// This class is used as metadata to ignore a property on an object in order to exclude it from query
6+
/// This class is used as metadata to ignore a property on insert and update queries
77
/// </summary>
88
/// <example>
99
/// <code>
10-
/// public class Person
11-
/// {
10+
/// public class Person
11+
/// {
1212
/// public string Name {get ;set;}
13+
///
1314
/// [Ignore]
1415
/// public string PhoneNumber {get ;set;}
1516
///
1617
/// }
1718
///
18-
/// output: SELECT [Name] FROM [Person]
19+
/// new Query("Table").Insert(new Person { Name = "User", PhoneNumber = "70123456" })
20+
///
21+
/// output: INSERT INTO [Table] ([Name]) VALUES('User')
1922
/// </code>
2023
/// </example>
2124
public class IgnoreAttribute : Attribute

QueryBuilder/Query.Insert.cs

Lines changed: 1 addition & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -9,36 +9,11 @@ public partial class Query
99
{
1010
public Query AsInsert(object data, bool returnId = false)
1111
{
12-
var dictionary = BuildDictionaryOnInsert(data);
12+
var dictionary = BuildDictionaryFromObject(data);
1313

1414
return AsInsert(dictionary, returnId);
1515
}
1616

17-
18-
private Dictionary<string, object> BuildDictionaryOnInsert(object data)
19-
{
20-
21-
var dictionary = new Dictionary<string, object>();
22-
var props = data.GetType().GetRuntimeProperties();
23-
24-
foreach (PropertyInfo property in props)
25-
{
26-
if (property.GetCustomAttribute(typeof(IgnoreAttribute)) != null)
27-
{
28-
continue;
29-
}
30-
31-
var value = property.GetValue(data);
32-
33-
var colAttr = property.GetCustomAttribute(typeof(ColumnAttribute)) as ColumnAttribute;
34-
var name = colAttr?.Name ?? property.Name;
35-
36-
dictionary.Add(name, value);
37-
}
38-
39-
return dictionary;
40-
}
41-
4217
public Query AsInsert(IEnumerable<string> columns, IEnumerable<object> values)
4318
{
4419
var columnsList = columns?.ToList();

QueryBuilder/Query.Update.cs

Lines changed: 3 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using System;
2-
using System.Collections.Concurrent;
32
using System.Collections.Generic;
43
using System.Linq;
54
using System.Reflection;
@@ -12,40 +11,9 @@ public partial class Query
1211

1312
public Query AsUpdate(object data)
1413
{
15-
var dictionary = BuildDictionaryOnUpdate(data);
16-
return AsUpdate(dictionary);
17-
}
18-
19-
private static readonly ConcurrentDictionary<Type, PropertyInfo[]> CacheDictionaryProperties = new ConcurrentDictionary<Type, PropertyInfo[]>();
20-
21-
private Dictionary<string, object> BuildDictionaryOnUpdate(object data)
22-
{
23-
var dictionary = new Dictionary<string, object>();
24-
var props = CacheDictionaryProperties.GetOrAdd(data.GetType(), type => type.GetRuntimeProperties().ToArray());
14+
var dictionary = BuildDictionaryFromObject(data, considerKeys: true);
2515

26-
foreach (PropertyInfo property in props)
27-
{
28-
if (property.GetCustomAttribute(typeof(IgnoreAttribute)) != null)
29-
{
30-
continue;
31-
}
32-
33-
var value = property.GetValue(data);
34-
35-
var colAttr = property.GetCustomAttribute(typeof(ColumnAttribute)) as ColumnAttribute;
36-
var name = colAttr?.Name ?? property.Name;
37-
if (colAttr != null)
38-
{
39-
if ((colAttr as KeyAttribute) != null)
40-
{
41-
this.Where(name, value);
42-
}
43-
}
44-
45-
dictionary.Add(name, value);
46-
}
47-
48-
return dictionary;
16+
return AsUpdate(dictionary);
4917
}
5018

5119
public Query AsUpdate(IEnumerable<string> columns, IEnumerable<object> values)
@@ -92,4 +60,4 @@ public Query AsUpdate(IReadOnlyDictionary<string, object> data)
9260
}
9361

9462
}
95-
}
63+
}

QueryBuilder/Query.cs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
using System;
2+
using System.Collections.Concurrent;
23
using System.Collections.Generic;
34
using System.Linq;
5+
using System.Reflection;
46

57
namespace SqlKata
68
{
@@ -309,6 +311,51 @@ public Query IncludeMany(string relationName, Query query, string foreignKey = n
309311
{
310312
return Include(relationName, query, foreignKey, localKey, isMany: true);
311313
}
314+
315+
private static readonly ConcurrentDictionary<Type, PropertyInfo[]> CacheDictionaryProperties = new ConcurrentDictionary<Type, PropertyInfo[]>();
316+
317+
/// <summary>
318+
/// Build a dictionary from plain object, intended to be used with Insert and Update queries
319+
/// </summary>
320+
/// <param name="data">the plain C# object</param>
321+
/// <param name="considerKeys">
322+
/// When true it will search for properties with the [Key] attribute
323+
/// and add it automatically to the Where clause
324+
/// </param>
325+
/// <returns></returns>
326+
private Dictionary<string, object> BuildDictionaryFromObject(object data, bool considerKeys = false)
327+
{
328+
329+
var dictionary = new Dictionary<string, object>();
330+
var props = CacheDictionaryProperties.GetOrAdd(data.GetType(), type => type.GetRuntimeProperties().ToArray());
331+
332+
foreach (var property in props)
333+
{
334+
if (property.GetCustomAttribute(typeof(IgnoreAttribute)) != null)
335+
{
336+
continue;
337+
}
338+
339+
var value = property.GetValue(data);
340+
341+
var colAttr = property.GetCustomAttribute(typeof(ColumnAttribute)) as ColumnAttribute;
342+
343+
var name = colAttr?.Name ?? property.Name;
344+
345+
dictionary.Add(name, value);
346+
347+
if (considerKeys && colAttr != null)
348+
{
349+
if ((colAttr as KeyAttribute) != null)
350+
{
351+
this.Where(name, value);
352+
}
353+
}
354+
355+
}
356+
357+
return dictionary;
358+
}
312359

313360
}
314361
}

0 commit comments

Comments
 (0)