-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathSnowflakeCommand.Where.cs
166 lines (141 loc) · 6.37 KB
/
SnowflakeCommand.Where.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
//-----------------------------------------------------------------------
// <copyright file="SnowflakeCommand.Where.cs" company="Jonas Schubert">
// Copyright (c) Jonas Schubert. All rights reserved.
// </copyright>
// <author>Jonas Schubert</author>
//-----------------------------------------------------------------------
namespace Snowflake.Data.Xt
{
/// <summary>
/// The snowflake command.
/// </summary>
/// <typeparam name="T">The generic type. This is used to parse properties for the query.</typeparam>
public partial class SnowflakeCommand<T>
where T : class
{
/// <summary>
/// Adds a where clause.
/// https://docs.snowflake.com/en/sql-reference/constructs/where .
/// </summary>
/// <param name="predicate">The where predicate.</param>
/// <returns>The snowflake command.</returns>
/// <exception cref="InvalidOperationException">Command already has a where clause.</exception>
/// <exception cref="NotSupportedException">If type is not supported.</exception>
public SnowflakeCommand<T> Where(Expression<Func<T, bool>> predicate)
{
if (this.Sql.Contains("WHERE", StringComparison.Ordinal))
{
throw new InvalidOperationException("Command already has a where clause!");
}
var replacements = new Dictionary<string, string>(StringComparer.Ordinal);
this.WalkExpression(ref replacements, predicate);
var whereBody = predicate.Body.ToString();
foreach (var parameter in predicate.Parameters)
{
whereBody = whereBody.Replace(parameter.Name + ".", string.Empty, StringComparison.Ordinal);
}
foreach (var replacement in replacements)
{
whereBody = whereBody.Replace(replacement.Key, "?", StringComparison.Ordinal);
}
// TODO Add additional mappings, e.g. like https://github.com/phnx47/dapper-repositories/tree/main/src/SqlGenerator
foreach (var item in new (string, string)[]
{
("AndAlso", "AND"),
("OrElse", "OR"),
("==", "="),
("String.Empty", "''"),
})
{
whereBody = whereBody.Replace(item.Item1, item.Item2, StringComparison.Ordinal);
}
foreach (var property in this.Properties)
{
var propertyName = property.Value.Name;
var propertyTableAlias = string.IsNullOrWhiteSpace(property.Value.Table)
? this.Table.Alias
: this.Joins.Single(join => string.Equals(join.Table, property.Value.Table, StringComparison.Ordinal)).Alias;
whereBody = whereBody.Replace($"{property.Key.Name}", $"{propertyTableAlias}.{propertyName}", StringComparison.Ordinal);
}
this.SqlBuilder.Append($" WHERE {whereBody.Trim()}");
return this;
}
/// <summary>
/// Adds a where clause.
/// https://docs.snowflake.com/en/sql-reference/constructs/where .
/// </summary>
/// <param name="where">The where filter.</param>
/// <returns>The snowflake command.</returns>
/// <exception cref="InvalidOperationException">Command already has a where clause.</exception>
/// <exception cref="ArgumentNullException">Value for where clause may not be empty.</exception>
public SnowflakeCommand<T> Where(string where)
{
if (this.Sql.Contains("WHERE", StringComparison.Ordinal))
{
throw new InvalidOperationException("Command already has a where clause!");
}
if (string.IsNullOrWhiteSpace(where))
{
throw new ArgumentNullException(nameof(where), "Value for where clause may not be empty!");
}
this.SqlBuilder.Append($" {(where.Trim().StartsWith("WHERE", ignoreCase: true, CultureInfo.InvariantCulture) ? where.Trim() : $"WHERE {where.Trim()}")}");
return this;
}
/// <summary>
/// Walking the expression.
/// </summary>
/// <param name="replacements">The replacements.</param>
/// <param name="expression">The expression.</param>
/// <exception cref="NotSupportedException">If type is not supported.</exception>
private void WalkExpression(ref Dictionary<string, string> replacements, Expression expression)
{
#pragma warning disable IDE0010 // Add missing cases // TODO
switch (expression.NodeType)
{
case ExpressionType.MemberAccess:
var replacementExpression = expression.ToString();
if (replacementExpression.Contains("value(", StringComparison.Ordinal))
{
if (!replacements.ContainsKey(replacementExpression))
{
var invocation = Expression.Lambda(expression).Compile().DynamicInvoke();
var whereParameterType = invocation!.GetType();
var whereParameterValue = invocation!.ToString() !;
var dbType = this._typeMap[whereParameterType];
var parameterIndex = string.Format(CultureInfo.InvariantCulture, "{0}", this.ParameterList.Count + 1);
this.ParameterList.Add((parameterIndex, dbType, whereParameterValue));
replacements.Add(replacementExpression, whereParameterValue);
}
}
break;
case ExpressionType.GreaterThan:
case ExpressionType.GreaterThanOrEqual:
case ExpressionType.LessThan:
case ExpressionType.LessThanOrEqual:
case ExpressionType.OrElse:
case ExpressionType.AndAlso:
case ExpressionType.Equal:
var binaryExpression = expression as BinaryExpression;
this.WalkExpression(ref replacements, binaryExpression!.Left);
this.WalkExpression(ref replacements, binaryExpression!.Right);
break;
case ExpressionType.Call:
var methodCallExpression = expression as MethodCallExpression;
foreach (var argument in methodCallExpression!.Arguments)
{
this.WalkExpression(ref replacements, argument);
}
break;
case ExpressionType.Lambda:
var lambdaExpression = expression as LambdaExpression;
this.WalkExpression(ref replacements, lambdaExpression!.Body);
break;
case ExpressionType.Constant:
break;
default:
throw new NotSupportedException($"Unknown type \"{expression.NodeType}\".");
}
#pragma warning restore IDE0010 // Add missing cases
}
}
}