1
|
|
#region Copyright
|
2
|
|
|
3
|
|
|
4
|
|
|
5
|
|
|
6
|
|
|
7
|
|
|
8
|
|
|
9
|
|
|
10
|
|
|
11
|
|
|
12
|
|
|
13
|
|
|
14
|
|
|
15
|
|
|
16
|
|
|
17
|
|
#endregion
|
18
|
|
|
19
|
|
using System;
|
20
|
|
|
21
|
|
namespace Seasar.Dao.Parser
|
22
|
|
{
|
23
|
|
public class SqlTokenizerImpl : ISqlTokenizer
|
24
|
|
{
|
25
|
|
private string sql;
|
26
|
|
private int position = 0;
|
27
|
|
private string token;
|
28
|
|
private TokenType tokenType = TokenType.SQL;
|
29
|
|
private TokenType nextTokenType = TokenType.SQL;
|
30
|
|
private int bindVariableNum = 0;
|
31
|
|
|
32
|
156
|
public SqlTokenizerImpl(string sql)
|
33
|
|
{
|
34
|
156
|
this.sql = sql;
|
35
|
|
}
|
36
|
|
|
37
|
|
#region ISqlTokenizer メンバ
|
38
|
|
|
39
|
|
public string Token
|
40
|
|
{
|
41
|
603
|
get
|
42
|
|
{
|
43
|
603
|
return token;
|
44
|
|
}
|
45
|
|
}
|
46
|
|
|
47
|
|
public string Before
|
48
|
|
{
|
49
|
3
|
get
|
50
|
|
{
|
51
|
3
|
return sql.Substring(0, position);
|
52
|
|
}
|
53
|
|
}
|
54
|
|
|
55
|
|
public string After
|
56
|
|
{
|
57
|
3
|
get
|
58
|
|
{
|
59
|
3
|
return sql.Substring(position);
|
60
|
|
}
|
61
|
|
}
|
62
|
|
|
63
|
0
|
public int Position
|
64
|
|
{
|
65
|
|
get
|
66
|
|
{
|
67
|
|
return position;
|
68
|
|
}
|
69
|
|
}
|
70
|
|
|
71
|
|
public TokenType TokenType
|
72
|
|
{
|
73
|
503
|
get
|
74
|
|
{
|
75
|
503
|
return tokenType;
|
76
|
|
}
|
77
|
|
}
|
78
|
|
|
79
|
0
|
public TokenType NextTokenType
|
80
|
|
{
|
81
|
|
get { return NextTokenType; }
|
82
|
|
}
|
83
|
|
|
84
|
600
|
public TokenType Next()
|
85
|
|
{
|
86
|
600
|
if(position >= sql.Length)
|
87
|
|
{
|
88
|
118
|
token = null;
|
89
|
118
|
tokenType = TokenType.EOF;
|
90
|
118
|
nextTokenType = TokenType.EOF;
|
91
|
118
|
return tokenType;
|
92
|
|
}
|
93
|
482
|
switch(nextTokenType)
|
94
|
|
{
|
95
|
|
case TokenType.SQL:
|
96
|
262
|
ParseSql();
|
97
|
262
|
break;
|
98
|
|
case TokenType.COMMENT:
|
99
|
192
|
ParseComment();
|
100
|
190
|
break;
|
101
|
|
case TokenType.ELSE:
|
102
|
8
|
ParseElse();
|
103
|
8
|
break;
|
104
|
|
case TokenType.BIND_VARIABLE:
|
105
|
20
|
ParseBindVariable();
|
106
|
20
|
break;
|
107
|
|
default:
|
108
|
0
|
ParseEof();
|
109
|
0
|
break;
|
110
|
|
}
|
111
|
480
|
return tokenType;
|
112
|
|
}
|
113
|
|
|
114
|
144
|
public string SkipToken()
|
115
|
|
{
|
116
|
144
|
int index = sql.Length;
|
117
|
144
|
char quote = position < sql.Length ? sql.ToCharArray()[position] : '\0';
|
118
|
144
|
bool quoting = quote == '\'' || quote =='(';
|
119
|
144
|
if(quote == '(') quote = ')';
|
120
|
|
|
121
|
740
|
for(int i = quoting ? position + 1 : position; i < sql.Length; ++i)
|
122
|
|
{
|
123
|
702
|
char c = sql.ToCharArray()[i];
|
124
|
702
|
if((Char.IsWhiteSpace(c) || c == ',' || c == ')' || c == '(')
|
125
|
|
&& !quoting)
|
126
|
|
{
|
127
|
43
|
index = i;
|
128
|
43
|
break;
|
129
|
|
}
|
130
|
659
|
else if(c == '/' && i + 1 < sql.Length
|
131
|
|
&& sql.ToCharArray()[i + 1] == '*')
|
132
|
|
{
|
133
|
8
|
index = i;
|
134
|
8
|
break;
|
135
|
|
}
|
136
|
651
|
else if(c == '-' && i + 1 < sql.Length
|
137
|
|
&& sql.ToCharArray()[i + 1] == '-')
|
138
|
|
{
|
139
|
0
|
index = i;
|
140
|
0
|
break;
|
141
|
|
}
|
142
|
651
|
else if(quoting && quote == '\'' && c == '\''
|
143
|
|
&& (i + 1 >= sql.Length || sql.ToCharArray()[i + 1] != '\''))
|
144
|
|
{
|
145
|
37
|
index = i + 1;
|
146
|
37
|
break;
|
147
|
|
}
|
148
|
614
|
else if(quoting && c == quote)
|
149
|
|
{
|
150
|
18
|
index = i + 1;
|
151
|
18
|
break;
|
152
|
|
}
|
153
|
|
}
|
154
|
144
|
token = sql.Substring(position, (index - position));
|
155
|
144
|
tokenType = TokenType.SQL;
|
156
|
144
|
nextTokenType = TokenType.SQL;
|
157
|
144
|
position = index;
|
158
|
144
|
return token;
|
159
|
|
}
|
160
|
|
|
161
|
76
|
public string SkipWhitespace()
|
162
|
|
{
|
163
|
76
|
int index = SkipWhitespace(position);
|
164
|
76
|
token = sql.Substring(position, (index - position));
|
165
|
76
|
position = index;
|
166
|
76
|
return token;
|
167
|
|
}
|
168
|
|
|
169
|
|
#endregion
|
170
|
|
|
171
|
262
|
protected void ParseSql()
|
172
|
|
{
|
173
|
262
|
int commentStartPos = sql.IndexOf("/*", position);
|
174
|
262
|
int lineCommentStartPos = sql.IndexOf("--", position);
|
175
|
262
|
int bindVariableStartPos = sql.IndexOf("?", position);
|
176
|
262
|
int elseCommentStartPos = -1;
|
177
|
262
|
int elseCommentLength = -1;
|
178
|
|
|
179
|
262
|
if(bindVariableStartPos < 0)
|
180
|
244
|
bindVariableStartPos = sql.IndexOf("@", position);
|
181
|
262
|
if(lineCommentStartPos >= 0)
|
182
|
|
{
|
183
|
19
|
int skipPos = SkipWhitespace(lineCommentStartPos + 2);
|
184
|
19
|
if(skipPos + 4 < sql.Length
|
185
|
|
&& "ELSE" == sql.Substring(skipPos, ((skipPos + 4) - skipPos)))
|
186
|
|
{
|
187
|
19
|
elseCommentStartPos = lineCommentStartPos;
|
188
|
19
|
elseCommentLength = skipPos + 4 - lineCommentStartPos;
|
189
|
|
}
|
190
|
|
}
|
191
|
262
|
int nextStartPos = GetNextStartPos(commentStartPos,
|
192
|
|
elseCommentStartPos, bindVariableStartPos);
|
193
|
262
|
if(nextStartPos < 0)
|
194
|
|
{
|
195
|
42
|
token = sql.Substring(position);
|
196
|
42
|
nextTokenType = TokenType.EOF;
|
197
|
42
|
position = sql.Length;
|
198
|
42
|
tokenType = TokenType.SQL;
|
199
|
|
}
|
200
|
|
else
|
201
|
|
{
|
202
|
220
|
token = sql.Substring(position, nextStartPos - position);
|
203
|
220
|
tokenType = TokenType.SQL;
|
204
|
220
|
bool needNext = nextStartPos == position;
|
205
|
220
|
if(nextStartPos == commentStartPos)
|
206
|
|
{
|
207
|
192
|
nextTokenType = TokenType.COMMENT;
|
208
|
192
|
position = commentStartPos + 2;
|
209
|
|
}
|
210
|
28
|
else if(nextStartPos == elseCommentStartPos)
|
211
|
|
{
|
212
|
8
|
nextTokenType = TokenType.ELSE;
|
213
|
8
|
position = elseCommentStartPos + elseCommentLength;
|
214
|
|
}
|
215
|
20
|
else if(nextStartPos == bindVariableStartPos)
|
216
|
|
{
|
217
|
20
|
nextTokenType = TokenType.BIND_VARIABLE;
|
218
|
20
|
position = bindVariableStartPos;
|
219
|
|
}
|
220
|
220
|
if(needNext) Next();
|
221
|
|
}
|
222
|
|
}
|
223
|
|
|
224
|
262
|
protected int GetNextStartPos(int commentStartPos, int elseCommentStartPos,
|
225
|
|
int bindVariableStartPos)
|
226
|
|
{
|
227
|
262
|
int nextStartPos = -1;
|
228
|
262
|
if(commentStartPos >= 0)
|
229
|
200
|
nextStartPos = commentStartPos;
|
230
|
|
|
231
|
262
|
if(elseCommentStartPos >= 0
|
232
|
|
&& (nextStartPos < 0 || elseCommentStartPos < nextStartPos))
|
233
|
8
|
nextStartPos = elseCommentStartPos;
|
234
|
|
|
235
|
262
|
if(bindVariableStartPos >= 0
|
236
|
|
&& (nextStartPos < 0 || bindVariableStartPos < nextStartPos))
|
237
|
20
|
nextStartPos = bindVariableStartPos;
|
238
|
|
|
239
|
262
|
return nextStartPos;
|
240
|
|
}
|
241
|
|
|
242
|
|
protected string NextBindVariableName
|
243
|
|
{
|
244
|
20
|
get { return "$" + ++bindVariableNum; }
|
245
|
|
}
|
246
|
|
|
247
|
192
|
protected void ParseComment()
|
248
|
|
{
|
249
|
192
|
int commentEndPos = sql.IndexOf("*/", position);
|
250
|
192
|
if(commentEndPos < 0)
|
251
|
2
|
throw new TokenNotClosedRuntimeException("*/",
|
252
|
|
sql.Substring(position));
|
253
|
|
|
254
|
190
|
token = sql.Substring(position, (commentEndPos - position));
|
255
|
190
|
nextTokenType = TokenType.SQL;
|
256
|
190
|
position = commentEndPos + 2;
|
257
|
190
|
tokenType = TokenType.COMMENT;
|
258
|
|
}
|
259
|
|
|
260
|
20
|
protected void ParseBindVariable()
|
261
|
|
{
|
262
|
20
|
token = NextBindVariableName;
|
263
|
20
|
nextTokenType = TokenType.SQL;
|
264
|
20
|
position++;
|
265
|
20
|
tokenType = TokenType.BIND_VARIABLE;
|
266
|
|
}
|
267
|
|
|
268
|
8
|
protected void ParseElse()
|
269
|
|
{
|
270
|
8
|
token = null;
|
271
|
8
|
nextTokenType = TokenType.SQL;
|
272
|
8
|
tokenType = TokenType.ELSE;
|
273
|
|
}
|
274
|
|
|
275
|
0
|
protected void ParseEof()
|
276
|
|
{
|
277
|
|
token = null;
|
278
|
|
tokenType = TokenType.EOF;
|
279
|
|
nextTokenType = TokenType.EOF;
|
280
|
|
}
|
281
|
|
|
282
|
95
|
private int SkipWhitespace(int position)
|
283
|
|
{
|
284
|
95
|
int index = sql.Length;
|
285
|
160
|
for(int i = position; i < sql.Length; ++i)
|
286
|
|
{
|
287
|
152
|
char c = sql.ToCharArray()[i];
|
288
|
152
|
if(!Char.IsWhiteSpace(c))
|
289
|
|
{
|
290
|
87
|
index = i;
|
291
|
87
|
break;
|
292
|
|
}
|
293
|
|
}
|
294
|
95
|
return index;
|
295
|
|
}
|
296
|
|
}
|
297
|
|
}
|
298
|
|
|