forked from nhibernate/nhibernate-core
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathSQLFunctionTemplate.cs
162 lines (142 loc) · 4.29 KB
/
SQLFunctionTemplate.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
using System.Collections;
using System.Globalization;
using System.Text;
using System.Text.RegularExpressions;
using NHibernate.Engine;
using NHibernate.SqlCommand;
using NHibernate.Type;
using System;
using System.Collections.Generic;
using System.Linq;
namespace NHibernate.Dialect.Function
{
/// <summary>
/// Represents HQL functions that can have different representations in different SQL dialects.
/// E.g. in HQL we can define function <code>concat(?1, ?2)</code> to concatenate two strings
/// p1 and p2. Target SQL function will be dialect-specific, e.g. <code>(?1 || ?2)</code> for
/// Oracle, <code>concat(?1, ?2)</code> for MySql, <code>(?1 + ?2)</code> for MS SQL.
/// Each dialect will define a template as a string (exactly like above) marking function
/// parameters with '?' followed by parameter's index (first index is 1).
/// </summary>
[Serializable]
public class SQLFunctionTemplate : ISQLFunction, ISQLFunctionExtended
{
private const int InvalidArgumentIndex = -1;
private static readonly Regex SplitRegex = new Regex("(\\?[0-9]+)");
private struct TemplateChunk
{
public string Text; // including prefix if parameter
public int ArgumentIndex;
public TemplateChunk(string chunk, int argIndex)
{
Text = chunk;
ArgumentIndex = argIndex;
}
}
private readonly IType returnType = null;
private readonly bool hasArguments;
private readonly bool hasParenthesesIfNoArgs;
private readonly string template;
private TemplateChunk[] chunks;
public SQLFunctionTemplate(IType type, string template) : this(type, template, true)
{
}
public SQLFunctionTemplate(IType type, string template, bool hasParenthesesIfNoArgs)
{
returnType = type;
this.template = template;
this.hasParenthesesIfNoArgs = hasParenthesesIfNoArgs;
InitFromTemplate();
hasArguments = chunks.Length > 1;
}
private void InitFromTemplate()
{
string[] stringChunks = SplitRegex.Split(template);
chunks = new TemplateChunk[stringChunks.Length];
for (int i = 0; i < stringChunks.Length; i++)
{
string chunk = stringChunks[i];
if (i % 2 == 0)
{
// Text part.
chunks[i] = new TemplateChunk(chunk, InvalidArgumentIndex);
}
else
{
// Separator, i.e. argument
int argIndex = int.Parse(chunk.Substring(1), CultureInfo.InvariantCulture);
chunks[i] = new TemplateChunk(stringChunks[i], argIndex);
}
}
}
#region ISQLFunction Members
// Since v5.3
[Obsolete("Use GetReturnType method instead.")]
public IType ReturnType(IType columnType, IMapping mapping)
{
return (returnType == null) ? columnType : returnType;
}
/// <inheritdoc />
public virtual IType GetReturnType(IEnumerable<IType> argumentTypes, IMapping mapping, bool throwOnError)
{
#pragma warning disable 618
return ReturnType(argumentTypes.FirstOrDefault(), mapping);
#pragma warning restore 618
}
/// <inheritdoc />
public virtual IType GetEffectiveReturnType(IEnumerable<IType> argumentTypes, IMapping mapping, bool throwOnError)
{
return GetReturnType(argumentTypes, mapping, throwOnError);
}
/// <inheritdoc />
public virtual string Name => null;
public bool HasArguments
{
get { return hasArguments; }
}
public bool HasParenthesesIfNoArguments
{
get { return hasParenthesesIfNoArgs; }
}
/// <summary>
/// Applies the template to passed in arguments.
/// </summary>
/// <param name="args">args function arguments</param>
/// <param name="factory">generated SQL function call</param>
/// <returns></returns>
public virtual SqlString Render(IList args, ISessionFactoryImplementor factory)
{
SqlStringBuilder buf = new SqlStringBuilder();
foreach (TemplateChunk tc in chunks)
{
if (tc.ArgumentIndex != InvalidArgumentIndex)
{
int adjustedIndex = tc.ArgumentIndex - 1; // Arg indices are one-based
object arg = adjustedIndex < args.Count ? args[adjustedIndex] : null;
// TODO: if (arg == null) QueryException is better ?
if (arg != null)
{
if (arg is Parameter || arg is SqlString)
{
buf.AddObject(arg);
}
else
{
buf.Add(arg.ToString());
}
}
}
else
{
buf.Add(tc.Text);
}
}
return buf.ToSqlString();
}
#endregion
public override string ToString()
{
return template;
}
}
}