-
Notifications
You must be signed in to change notification settings - Fork 934
/
Copy pathParameterParser.cs
160 lines (151 loc) · 4.78 KB
/
ParameterParser.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
using System;
using NHibernate.Hql;
using NHibernate.Util;
namespace NHibernate.Engine.Query
{
/// <summary>
/// The single available method <see cref="ParameterParser.Parse" />
/// is responsible for parsing a query string and recognizing tokens in
/// relation to parameters (either named, ejb3-style, or ordinal) and
/// providing callbacks about such recognitions.
/// </summary>
public class ParameterParser
{
public interface IRecognizer
{
void OutParameter(int position);
void OrdinalParameter(int position);
void NamedParameter(string name, int position);
void JpaPositionalParameter(string name, int position);
void Other(char character);
void Other(string sqlPart);
}
// Disallow instantiation
private ParameterParser()
{
}
/// <summary>
/// Performs the actual parsing and tokenizing of the query string making appropriate
/// callbacks to the given recognizer upon recognition of the various tokens.
/// </summary>
/// <remarks>
/// Note that currently, this only knows how to deal with a single output
/// parameter (for callable statements). If we later add support for
/// multiple output params, this, obviously, needs to change.
/// </remarks>
/// <param name="sqlString">The string to be parsed/tokenized.</param>
/// <param name="recognizer">The thing which handles recognition events.</param>
/// <exception cref="QueryException" />
public static void Parse(string sqlString, IRecognizer recognizer)
{
// TODO: WTF? "CALL"... it may work for ORACLE but what about others RDBMS ? (by FM)
var indexOfCall = sqlString.IndexOf("call", StringComparison.Ordinal);
bool hasMainOutputParameter = CallableParser.HasReturnParameter(sqlString, indexOfCall);
bool foundMainOutputParam = false;
int stringLength = sqlString.Length;
bool inQuote = false;
bool afterNewLine = false;
for (int indx = 0; indx < stringLength; indx++)
{
int currentNewLineLength;
// check comments
if (indx + 1 < stringLength && sqlString.Substring(indx,2) == "/*")
{
var closeCommentIdx = sqlString.IndexOf("*/", indx + 2, StringComparison.Ordinal);
recognizer.Other(sqlString.Substring(indx, (closeCommentIdx- indx)+2));
indx = closeCommentIdx + 1;
continue;
}
if (afterNewLine && (indx + 1 < stringLength) && sqlString.Substring(indx, 2) == "--")
{
var closeCommentIdx = sqlString.IndexOfAnyNewLine(indx + 2, out currentNewLineLength);
string comment;
if (closeCommentIdx == -1)
{
closeCommentIdx = sqlString.Length;
comment = sqlString.Substring(indx);
}
else
{
comment = sqlString.Substring(indx, closeCommentIdx - indx + currentNewLineLength);
}
recognizer.Other(comment);
indx = closeCommentIdx + currentNewLineLength - 1;
continue;
}
if (sqlString.IsAnyNewLine(indx, out currentNewLineLength))
{
afterNewLine = true;
indx += currentNewLineLength - 1;
recognizer.Other(Environment.NewLine);
continue;
}
afterNewLine = false;
char c = sqlString[indx];
if (inQuote)
{
if ('\'' == c)
{
inQuote = false;
}
recognizer.Other(c);
}
else if ('\'' == c)
{
inQuote = true;
recognizer.Other(c);
}
else
{
if (c == ':')
{
// named parameter
int right = StringHelper.FirstIndexOfChar(sqlString, ParserHelper.HqlSeparatorsAsCharArray, indx + 1);
int chopLocation = right < 0 ? sqlString.Length : right;
string param = sqlString.Substring(indx + 1, chopLocation - (indx + 1));
recognizer.NamedParameter(param, indx);
indx = chopLocation - 1;
}
else if (c == '?')
{
// could be either an ordinal or ejb3-positional parameter
if (indx < stringLength - 1 && char.IsDigit(sqlString[indx + 1]))
{
// a peek ahead showed this as an ejb3-positional parameter
int right = StringHelper.FirstIndexOfChar(sqlString, ParserHelper.HqlSeparatorsAsCharArray, indx + 1);
int chopLocation = right < 0 ? sqlString.Length : right;
string param = sqlString.Substring(indx + 1, chopLocation - (indx + 1));
// make sure this "name" is an integral
try
{
int.Parse(param);
}
catch (FormatException e)
{
throw new QueryException("ejb3-style positional param was not an integral ordinal", e);
}
recognizer.JpaPositionalParameter(param, indx);
indx = chopLocation - 1;
}
else
{
if (hasMainOutputParameter && !foundMainOutputParam)
{
foundMainOutputParam = true;
recognizer.OutParameter(indx);
}
else
{
recognizer.OrdinalParameter(indx);
}
}
}
else
{
recognizer.Other(c);
}
}
}
}
}
}