sql-parser-tl 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/lib/sql-parser/parser.racc +369 -0
- data/lib/sql-parser/parser.racc.rb +1777 -0
- data/lib/sql-parser/parser.rex +113 -0
- data/lib/sql-parser/parser.rex.rb +324 -0
- data/lib/sql-parser/sql_visitor.rb +383 -0
- data/lib/sql-parser/statement.rb +556 -0
- data/lib/sql-parser/version.rb +5 -0
- data/lib/sql-parser.rb +13 -0
- metadata +119 -0
@@ -0,0 +1,383 @@
|
|
1
|
+
module SQLParser
|
2
|
+
|
3
|
+
class SQLVisitor
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@negated = false
|
7
|
+
end
|
8
|
+
|
9
|
+
def visit(node)
|
10
|
+
node.accept(self)
|
11
|
+
end
|
12
|
+
|
13
|
+
def visit_Insert(o)
|
14
|
+
name = visit(o.table_reference)
|
15
|
+
columns = ' ' + visit(o.column_list) if o.column_list
|
16
|
+
values = ' VALUES ' + visit(o.in_value_list)
|
17
|
+
"INSERT INTO #{name}#{columns}#{values}"
|
18
|
+
end
|
19
|
+
|
20
|
+
def visit_DirectSelect(o)
|
21
|
+
[
|
22
|
+
o.query_expression,
|
23
|
+
o.order_by,
|
24
|
+
o.limit_clause
|
25
|
+
].compact.collect { |e| visit(e) }.join(' ')
|
26
|
+
end
|
27
|
+
|
28
|
+
def visit_OrderBy(o)
|
29
|
+
"ORDER BY #{arrayize(o.sort_specification)}"
|
30
|
+
end
|
31
|
+
|
32
|
+
def visit_LimitClause(o)
|
33
|
+
"LIMIT #{o.limit} OFFSET #{o.offset}"
|
34
|
+
end
|
35
|
+
|
36
|
+
def visit_Subquery(o)
|
37
|
+
"(#{visit(o.query_specification)})"
|
38
|
+
end
|
39
|
+
|
40
|
+
def visit_Select(o)
|
41
|
+
# FIXME: This feels like a hack
|
42
|
+
initialize
|
43
|
+
|
44
|
+
"SELECT #{visit_all([o.list, o.table_expression].compact).join(' ')}"
|
45
|
+
end
|
46
|
+
|
47
|
+
def visit_SelectList(o)
|
48
|
+
arrayize(o.columns)
|
49
|
+
end
|
50
|
+
|
51
|
+
def visit_Distinct(o)
|
52
|
+
"DISTINCT(#{visit(o.column)})"
|
53
|
+
end
|
54
|
+
|
55
|
+
def visit_All(o)
|
56
|
+
'*'
|
57
|
+
end
|
58
|
+
|
59
|
+
def visit_TableExpression(o)
|
60
|
+
[
|
61
|
+
o.from_clause,
|
62
|
+
o.where_clause,
|
63
|
+
o.group_by_clause,
|
64
|
+
o.having_clause
|
65
|
+
].compact.collect { |e| visit(e) }.join(' ')
|
66
|
+
end
|
67
|
+
|
68
|
+
def visit_FromClause(o)
|
69
|
+
"FROM #{arrayize(o.tables)}"
|
70
|
+
end
|
71
|
+
|
72
|
+
def visit_OrderClause(o)
|
73
|
+
"ORDER BY #{arrayize(o.columns)}"
|
74
|
+
end
|
75
|
+
|
76
|
+
def visit_Ascending(o)
|
77
|
+
"#{visit(o.column)} ASC"
|
78
|
+
end
|
79
|
+
|
80
|
+
def visit_Descending(o)
|
81
|
+
"#{visit(o.column)} DESC"
|
82
|
+
end
|
83
|
+
|
84
|
+
def visit_HavingClause(o)
|
85
|
+
"HAVING #{visit(o.search_condition)}"
|
86
|
+
end
|
87
|
+
|
88
|
+
def visit_GroupByClause(o)
|
89
|
+
"GROUP BY #{arrayize(o.columns)}"
|
90
|
+
end
|
91
|
+
|
92
|
+
def visit_WhereClause(o)
|
93
|
+
"WHERE #{visit(o.search_condition)}"
|
94
|
+
end
|
95
|
+
|
96
|
+
def visit_On(o)
|
97
|
+
"ON #{visit(o.search_condition)}"
|
98
|
+
end
|
99
|
+
|
100
|
+
def visit_Using(o)
|
101
|
+
"USING (#{arrayize(o.columns)})"
|
102
|
+
end
|
103
|
+
|
104
|
+
def visit_Or(o)
|
105
|
+
search_condition('OR', o)
|
106
|
+
end
|
107
|
+
|
108
|
+
def visit_And(o)
|
109
|
+
search_condition('AND', o)
|
110
|
+
end
|
111
|
+
|
112
|
+
def visit_Exists(o)
|
113
|
+
if @negated
|
114
|
+
"NOT EXISTS #{visit(o.table_subquery)}"
|
115
|
+
else
|
116
|
+
"EXISTS #{visit(o.table_subquery)}"
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def visit_Is(o)
|
121
|
+
if @negated
|
122
|
+
comparison('IS NOT', o)
|
123
|
+
else
|
124
|
+
comparison('IS', o)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def visit_Like(o)
|
129
|
+
if @negated
|
130
|
+
comparison('NOT LIKE', o)
|
131
|
+
else
|
132
|
+
comparison('LIKE', o)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def visit_In(o)
|
137
|
+
if @negated
|
138
|
+
comparison('NOT IN', o)
|
139
|
+
else
|
140
|
+
comparison('IN', o)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def visit_InColumnList(o)
|
145
|
+
"(#{arrayize(o.columns)})"
|
146
|
+
end
|
147
|
+
|
148
|
+
def visit_InValueList(o)
|
149
|
+
"(#{arrayize(o.values)})"
|
150
|
+
end
|
151
|
+
|
152
|
+
def visit_Between(o)
|
153
|
+
if @negated
|
154
|
+
comparison('NOT BETWEEN', o)
|
155
|
+
else
|
156
|
+
comparison('BETWEEN', o)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def visit_BetweenRange(o)
|
161
|
+
"#{visit(o.min)} AND #{visit(o.max)}"
|
162
|
+
end
|
163
|
+
|
164
|
+
def visit_GreaterOrEquals(o)
|
165
|
+
comparison('>=', o)
|
166
|
+
end
|
167
|
+
|
168
|
+
def visit_LessOrEquals(o)
|
169
|
+
comparison('<=', o)
|
170
|
+
end
|
171
|
+
|
172
|
+
def visit_Greater(o)
|
173
|
+
comparison('>', o)
|
174
|
+
end
|
175
|
+
|
176
|
+
def visit_Less(o)
|
177
|
+
comparison('<', o)
|
178
|
+
end
|
179
|
+
|
180
|
+
def visit_Equals(o)
|
181
|
+
if @negated
|
182
|
+
comparison('<>', o)
|
183
|
+
else
|
184
|
+
comparison('=', o)
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
def visit_Sum(o)
|
189
|
+
aggregate('SUM', o)
|
190
|
+
end
|
191
|
+
|
192
|
+
def visit_Minimum(o)
|
193
|
+
aggregate('MIN', o)
|
194
|
+
end
|
195
|
+
|
196
|
+
def visit_Maximum(o)
|
197
|
+
aggregate('MAX', o)
|
198
|
+
end
|
199
|
+
|
200
|
+
def visit_Average(o)
|
201
|
+
aggregate('AVG', o)
|
202
|
+
end
|
203
|
+
|
204
|
+
def visit_Count(o)
|
205
|
+
aggregate('COUNT', o)
|
206
|
+
end
|
207
|
+
|
208
|
+
def visit_CrossJoin(o)
|
209
|
+
"#{visit(o.left)} CROSS JOIN #{visit(o.right)}"
|
210
|
+
end
|
211
|
+
|
212
|
+
def visit_InnerJoin(o)
|
213
|
+
qualified_join('INNER', o)
|
214
|
+
end
|
215
|
+
|
216
|
+
def visit_LeftJoin(o)
|
217
|
+
qualified_join('LEFT', o)
|
218
|
+
end
|
219
|
+
|
220
|
+
def visit_LeftOuterJoin(o)
|
221
|
+
qualified_join('LEFT OUTER', o)
|
222
|
+
end
|
223
|
+
|
224
|
+
def visit_RightJoin(o)
|
225
|
+
qualified_join('RIGHT', o)
|
226
|
+
end
|
227
|
+
|
228
|
+
def visit_RightOuterJoin(o)
|
229
|
+
qualified_join('RIGHT OUTER', o)
|
230
|
+
end
|
231
|
+
|
232
|
+
def visit_FullJoin(o)
|
233
|
+
qualified_join('FULL', o)
|
234
|
+
end
|
235
|
+
|
236
|
+
def visit_FullOuterJoin(o)
|
237
|
+
qualified_join('FULL OUTER', o)
|
238
|
+
end
|
239
|
+
|
240
|
+
def visit_Table(o)
|
241
|
+
quote(o.name)
|
242
|
+
end
|
243
|
+
|
244
|
+
def visit_QualifiedColumn(o)
|
245
|
+
"#{visit(o.table)}.#{visit(o.column)}"
|
246
|
+
end
|
247
|
+
|
248
|
+
def visit_Column(o)
|
249
|
+
quote(o.name)
|
250
|
+
end
|
251
|
+
|
252
|
+
def visit_As(o)
|
253
|
+
"#{visit(o.original)} AS #{visit(o.alias)}"
|
254
|
+
end
|
255
|
+
|
256
|
+
def visit_ForceIndex(o)
|
257
|
+
"#{visit(o.table)} FORCE INDEX (`#{o.index}`)"
|
258
|
+
end
|
259
|
+
|
260
|
+
def visit_UseIndex(o)
|
261
|
+
"#{visit(o.table)} USE INDEX (`#{o.index}`)"
|
262
|
+
end
|
263
|
+
|
264
|
+
def visit_IgnoreIndex(o)
|
265
|
+
"#{visit(o.table)} IGNORE INDEX (`#{o.index}`)"
|
266
|
+
end
|
267
|
+
|
268
|
+
def visit_Multiply(o)
|
269
|
+
arithmetic('*', o)
|
270
|
+
end
|
271
|
+
|
272
|
+
def visit_Divide(o)
|
273
|
+
arithmetic('/', o)
|
274
|
+
end
|
275
|
+
|
276
|
+
def visit_Add(o)
|
277
|
+
arithmetic('+', o)
|
278
|
+
end
|
279
|
+
|
280
|
+
def visit_Subtract(o)
|
281
|
+
arithmetic('-', o)
|
282
|
+
end
|
283
|
+
|
284
|
+
def visit_Not(o)
|
285
|
+
negate { visit(o.value) }
|
286
|
+
end
|
287
|
+
|
288
|
+
def visit_UnaryPlus(o)
|
289
|
+
"+#{visit(o.value)}"
|
290
|
+
end
|
291
|
+
|
292
|
+
def visit_UnaryMinus(o)
|
293
|
+
"-#{visit(o.value)}"
|
294
|
+
end
|
295
|
+
|
296
|
+
def visit_True(o)
|
297
|
+
'TRUE'
|
298
|
+
end
|
299
|
+
|
300
|
+
def visit_False(o)
|
301
|
+
'FALSE'
|
302
|
+
end
|
303
|
+
|
304
|
+
def visit_Null(o)
|
305
|
+
'NULL'
|
306
|
+
end
|
307
|
+
|
308
|
+
def visit_CurrentUser(o)
|
309
|
+
'CURRENT_USER'
|
310
|
+
end
|
311
|
+
|
312
|
+
def visit_DateTime(o)
|
313
|
+
"'%s'" % escape(o.value.strftime('%Y-%m-%d %H:%M:%S'))
|
314
|
+
end
|
315
|
+
|
316
|
+
def visit_Date(o)
|
317
|
+
"DATE '%s'" % escape(o.value.strftime('%Y-%m-%d'))
|
318
|
+
end
|
319
|
+
|
320
|
+
def visit_String(o)
|
321
|
+
"'%s'" % escape(o.value)
|
322
|
+
end
|
323
|
+
|
324
|
+
def visit_ApproximateFloat(o)
|
325
|
+
"#{visit(o.mantissa)}E#{visit(o.exponent)}"
|
326
|
+
end
|
327
|
+
|
328
|
+
def visit_Float(o)
|
329
|
+
o.value.to_s
|
330
|
+
end
|
331
|
+
|
332
|
+
def visit_Integer(o)
|
333
|
+
o.value.to_s
|
334
|
+
end
|
335
|
+
|
336
|
+
private
|
337
|
+
|
338
|
+
def negate
|
339
|
+
@negated = true
|
340
|
+
yield
|
341
|
+
ensure
|
342
|
+
@negated = false
|
343
|
+
end
|
344
|
+
|
345
|
+
def quote(str)
|
346
|
+
"`#{str}`"
|
347
|
+
end
|
348
|
+
|
349
|
+
def escape(str)
|
350
|
+
str.gsub(/'/, "''")
|
351
|
+
end
|
352
|
+
|
353
|
+
def arithmetic(operator, o)
|
354
|
+
search_condition(operator, o)
|
355
|
+
end
|
356
|
+
|
357
|
+
def comparison(operator, o)
|
358
|
+
[visit(o.left), operator, visit(o.right)].join(' ')
|
359
|
+
end
|
360
|
+
|
361
|
+
def search_condition(operator, o)
|
362
|
+
"(#{visit(o.left)} #{operator} #{visit(o.right)})"
|
363
|
+
end
|
364
|
+
|
365
|
+
def visit_all(nodes)
|
366
|
+
nodes.collect { |e| visit(e) }
|
367
|
+
end
|
368
|
+
|
369
|
+
def arrayize(arr)
|
370
|
+
visit_all(arr).join(', ')
|
371
|
+
end
|
372
|
+
|
373
|
+
def aggregate(function_name, o)
|
374
|
+
"#{function_name}(#{visit(o.column)})"
|
375
|
+
end
|
376
|
+
|
377
|
+
def qualified_join(join_type, o)
|
378
|
+
"#{visit(o.left)} #{join_type} JOIN #{visit(o.right)} #{visit(o.search_condition)}"
|
379
|
+
end
|
380
|
+
|
381
|
+
end
|
382
|
+
|
383
|
+
end
|