lilit-sql 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/lilit_sql.rb +364 -224
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 387d30110c85b2e4c85aef31d108675aaba18405d13f8b84984f3ce237a11caa
|
4
|
+
data.tar.gz: 342b105a73a26c3dc3507fddb2c3adccacc69ef23afea327a1f3fc9ba15c0671
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f83a531e2aa9b9a1a03be86374555e21a1fff1115a06b36db492a3c56b3f2d398e91e8ea38b0bca0767b48b0861fe43cc5979f285bc52637cbccc218b09bd278
|
7
|
+
data.tar.gz: b2c571325ddc399f639c68b291f16df4db25cdadfa2d4320b2456f771aa2a9c4e8a4a95fb9b7d5a39858c27c715cb1e79e4f3721f32967acd426ded134464452
|
data/lib/lilit_sql.rb
CHANGED
@@ -4,10 +4,146 @@ require 'ruby2ruby'
|
|
4
4
|
require 'ruby_parser'
|
5
5
|
require 'sourcify'
|
6
6
|
|
7
|
+
class Expr
|
8
|
+
def and(other)
|
9
|
+
BinaryOperation.new(self, :and, other)
|
10
|
+
end
|
11
|
+
|
12
|
+
def eq(other)
|
13
|
+
BinaryOperation.new(self, :'=', other)
|
14
|
+
end
|
15
|
+
|
16
|
+
def not
|
17
|
+
UnaryOperation.new(:not, self)
|
18
|
+
end
|
19
|
+
|
20
|
+
def minus
|
21
|
+
UnaryOperation.new(:-, self)
|
22
|
+
end
|
23
|
+
|
24
|
+
def plus
|
25
|
+
UnaryOperation.new(:+, self)
|
26
|
+
end
|
27
|
+
|
28
|
+
def ne(other)
|
29
|
+
BinaryOperation.new(self, :'!=', other)
|
30
|
+
end
|
31
|
+
|
32
|
+
def in(list)
|
33
|
+
BinaryOperation.new(self, :in, list)
|
34
|
+
end
|
35
|
+
|
36
|
+
def *(other)
|
37
|
+
BinaryOperation.new(self, :*, other)
|
38
|
+
end
|
39
|
+
|
40
|
+
def +(other)
|
41
|
+
BinaryOperation.new(self, :+, other)
|
42
|
+
end
|
43
|
+
|
44
|
+
def >=(other)
|
45
|
+
BinaryOperation.new(self, :'>=', other)
|
46
|
+
end
|
47
|
+
|
48
|
+
def <=(other)
|
49
|
+
BinaryOperation.new(self, :<=, other)
|
50
|
+
end
|
51
|
+
|
52
|
+
def >(other)
|
53
|
+
BinaryOperation.new(self, :'>', other)
|
54
|
+
end
|
55
|
+
|
56
|
+
def <(other)
|
57
|
+
BinaryOperation.new(self, :<, other)
|
58
|
+
end
|
59
|
+
|
60
|
+
def asc
|
61
|
+
OrderedByExpr.new(self, :asc)
|
62
|
+
end
|
63
|
+
|
64
|
+
def desc
|
65
|
+
OrderedByExpr.new(self, :desc)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
class OrderedByExpr
|
70
|
+
attr_accessor :expr, :direction
|
71
|
+
|
72
|
+
def initialize(expr, direction = nil)
|
73
|
+
@expr = expr
|
74
|
+
@direction = direction
|
75
|
+
end
|
76
|
+
|
77
|
+
def sql
|
78
|
+
s = @expr.ref_sql
|
79
|
+
|
80
|
+
if @direction
|
81
|
+
s += " #{direction}"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
class UnaryOperation < Expr
|
87
|
+
attr_accessor :op
|
88
|
+
|
89
|
+
def initialize(op, value)
|
90
|
+
@op = op
|
91
|
+
@value = value
|
92
|
+
end
|
93
|
+
|
94
|
+
def ref_sql
|
95
|
+
"#{@op} (#{@value.ref_sql})"
|
96
|
+
end
|
97
|
+
|
98
|
+
def ==(other)
|
99
|
+
other.class == self.class && other.state == state
|
100
|
+
end
|
101
|
+
|
102
|
+
def state
|
103
|
+
instance_variables.map { |variable| instance_variable_get variable }
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
class BinaryOperation < Expr
|
108
|
+
attr_accessor :left, :op, :right
|
109
|
+
|
110
|
+
def initialize(left, op, right)
|
111
|
+
@left = left
|
112
|
+
@op = op
|
113
|
+
@right = lit(right)
|
114
|
+
end
|
115
|
+
|
116
|
+
def ref_sql
|
117
|
+
if @right.is_a?(Literal) && @right.value.nil?
|
118
|
+
return (
|
119
|
+
if @op == :'='
|
120
|
+
"#{@left.ref_sql} is #{@right.ref_sql}"
|
121
|
+
elsif @op == :!=
|
122
|
+
"#{@left.ref_sql} is not #{@right.ref_sql}"
|
123
|
+
else
|
124
|
+
raise ArgumentError.new("Nil doesn't support the operator: #{@op}")
|
125
|
+
end
|
126
|
+
)
|
127
|
+
end
|
128
|
+
|
129
|
+
if @op == :in
|
130
|
+
return "#{@left.ref_sql} in (#{@right.map(&:ref_sql).join(', ')})"
|
131
|
+
end
|
132
|
+
|
133
|
+
"#{@left.ref_sql} #{@op} #{@right.ref_sql}"
|
134
|
+
end
|
135
|
+
|
136
|
+
def ==(other)
|
137
|
+
other.class == self.class && other.state == state
|
138
|
+
end
|
139
|
+
|
140
|
+
def state
|
141
|
+
instance_variables.map { |variable| instance_variable_get variable }
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
7
145
|
class From
|
8
|
-
attr_accessor :source
|
9
|
-
attr_accessor :join_type
|
10
|
-
attr_accessor :condition
|
146
|
+
attr_accessor :source, :join_type, :condition
|
11
147
|
|
12
148
|
def initialize(source, join_type = nil, condition = nil, alias_name = nil)
|
13
149
|
@source = source
|
@@ -16,9 +152,7 @@ class From
|
|
16
152
|
@alias_name = alias_name
|
17
153
|
end
|
18
154
|
|
19
|
-
|
20
|
-
@alias_name = value
|
21
|
-
end
|
155
|
+
attr_writer :alias_name
|
22
156
|
|
23
157
|
def raw_alias_name
|
24
158
|
@alias_name
|
@@ -33,9 +167,26 @@ class From
|
|
33
167
|
end
|
34
168
|
end
|
35
169
|
|
170
|
+
class CrossJoinUnnest
|
171
|
+
attr_accessor :from, :row, :ordinality
|
172
|
+
|
173
|
+
def initialize(from, row, ordinality)
|
174
|
+
@from = from
|
175
|
+
@row = row
|
176
|
+
@ordinality = ordinality
|
177
|
+
end
|
178
|
+
|
179
|
+
def rows
|
180
|
+
[@row].map {|r| r.with_from(@from)}
|
181
|
+
end
|
182
|
+
|
183
|
+
def alias_name
|
184
|
+
@from.source.table_name
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
36
188
|
class GroupBy
|
37
|
-
attr_accessor :query
|
38
|
-
attr_accessor :keys
|
189
|
+
attr_accessor :query, :keys
|
39
190
|
|
40
191
|
def initialize(query, keys)
|
41
192
|
@query = query
|
@@ -43,12 +194,13 @@ class GroupBy
|
|
43
194
|
end
|
44
195
|
|
45
196
|
def aggregate(&blk)
|
46
|
-
result = blk.call(@keys, *@query.rows)
|
197
|
+
result = expr(&blk).call(@keys, *@query.rows)
|
47
198
|
|
48
199
|
Query.new(
|
49
|
-
@query.froms
|
50
|
-
@query.conditions
|
200
|
+
@query.froms,
|
201
|
+
@query.conditions,
|
51
202
|
@keys,
|
203
|
+
@query.order_bys,
|
52
204
|
Row.new(result.class.members, result)
|
53
205
|
)
|
54
206
|
end
|
@@ -70,37 +222,44 @@ class Row
|
|
70
222
|
end
|
71
223
|
|
72
224
|
def col(name)
|
73
|
-
found = @columns.select {|c| c.name == name}.first
|
225
|
+
found = @columns.select { |c| c.name == name }.first
|
74
226
|
|
75
|
-
raise ArgumentError
|
227
|
+
raise ArgumentError, "#{name} is not found in the columns: #{@columns.map(&:name).inspect}" if found.nil?
|
76
228
|
|
77
229
|
found
|
78
230
|
end
|
79
231
|
|
80
232
|
def with_from(from)
|
81
|
-
Row.new(@columns.map {|c| c.with_from(from)})
|
82
|
-
end
|
83
|
-
|
84
|
-
private def method_missing(symbol, *args)
|
85
|
-
begin
|
86
|
-
col(symbol)
|
87
|
-
rescue ArgumentError
|
88
|
-
super
|
89
|
-
end
|
233
|
+
Row.new(@columns.map { |c| c.with_from(from) })
|
90
234
|
end
|
91
235
|
|
92
236
|
def has?(name)
|
93
|
-
@columns.any? {|c| c.name == name}
|
237
|
+
@columns.any? { |c| c.name == name }
|
94
238
|
end
|
95
239
|
|
96
240
|
def decl_sql
|
97
|
-
@columns.map
|
241
|
+
@columns.map(&:decl_sql).join(', ')
|
242
|
+
end
|
243
|
+
|
244
|
+
private
|
245
|
+
|
246
|
+
def method_missing(symbol, *args)
|
247
|
+
col(symbol)
|
248
|
+
rescue ArgumentError
|
249
|
+
super
|
250
|
+
end
|
251
|
+
|
252
|
+
def respond_to_missing?(method_name, include_private = false)
|
253
|
+
!col(method_name).nil?
|
254
|
+
rescue ArgumentError
|
255
|
+
super
|
98
256
|
end
|
99
257
|
end
|
100
258
|
|
101
|
-
|
102
|
-
|
103
|
-
|
259
|
+
|
260
|
+
|
261
|
+
class Column < Expr
|
262
|
+
attr_accessor :name, :origin
|
104
263
|
|
105
264
|
def initialize(name, origin = nil, from = nil)
|
106
265
|
@name = name
|
@@ -112,22 +271,6 @@ class Column
|
|
112
271
|
Column.new(@name, @origin, from)
|
113
272
|
end
|
114
273
|
|
115
|
-
def eq(other)
|
116
|
-
Expr.new(self, :eq, other)
|
117
|
-
end
|
118
|
-
|
119
|
-
def in(list)
|
120
|
-
Expr.new(self, :in, list)
|
121
|
-
end
|
122
|
-
|
123
|
-
def *(other)
|
124
|
-
Expr.new(self, :*, other)
|
125
|
-
end
|
126
|
-
|
127
|
-
def <=(other)
|
128
|
-
Expr.new(self, :<=, other)
|
129
|
-
end
|
130
|
-
|
131
274
|
def ref_sql
|
132
275
|
"#{@from.alias_name}.#{@name}"
|
133
276
|
end
|
@@ -140,25 +283,22 @@ class Column
|
|
140
283
|
else
|
141
284
|
origin.ref_sql
|
142
285
|
end
|
143
|
-
if origin_sql != @name.to_s
|
144
|
-
s += "#{origin_sql} as "
|
145
|
-
end
|
286
|
+
s += "#{origin_sql} as " if origin_sql != @name.to_s
|
146
287
|
end
|
147
288
|
s += @name.to_s
|
148
289
|
s
|
149
290
|
end
|
150
291
|
|
151
292
|
def ==(other)
|
152
|
-
other.class == self.class && other.state ==
|
293
|
+
other.class == self.class && other.state == state
|
153
294
|
end
|
154
295
|
|
155
296
|
def state
|
156
|
-
|
297
|
+
instance_variables.map { |variable| instance_variable_get variable }
|
157
298
|
end
|
158
299
|
end
|
159
300
|
|
160
301
|
class Count < Column
|
161
|
-
|
162
302
|
def initialize
|
163
303
|
super(nil)
|
164
304
|
end
|
@@ -168,12 +308,11 @@ class Count < Column
|
|
168
308
|
end
|
169
309
|
|
170
310
|
def decl_sql
|
171
|
-
|
311
|
+
'count(*)'
|
172
312
|
end
|
173
313
|
end
|
174
314
|
|
175
315
|
class Sum < Column
|
176
|
-
|
177
316
|
def initialize(col)
|
178
317
|
super(nil, col)
|
179
318
|
end
|
@@ -197,7 +336,7 @@ module Aggregate
|
|
197
336
|
end
|
198
337
|
end
|
199
338
|
|
200
|
-
class Literal
|
339
|
+
class Literal < Expr
|
201
340
|
attr_accessor :value
|
202
341
|
|
203
342
|
def initialize(value)
|
@@ -210,78 +349,39 @@ class Literal
|
|
210
349
|
|
211
350
|
def decl_sql
|
212
351
|
if @value.nil?
|
213
|
-
|
352
|
+
'null'
|
214
353
|
elsif @value.is_a?(Integer) || @value.is_a?(Float)
|
215
|
-
|
354
|
+
@value.to_s
|
216
355
|
elsif @value.is_a?(String)
|
217
356
|
"'#{@value}'"
|
218
357
|
else
|
219
|
-
raise NotImplementedError
|
358
|
+
raise NotImplementedError, "Literal doesn't support render #{@value.class} (#{@value})"
|
220
359
|
end
|
221
360
|
end
|
222
361
|
|
223
362
|
def ==(other)
|
224
|
-
other.class == self.class && other.state ==
|
363
|
+
other.class == self.class && other.state == state
|
225
364
|
end
|
226
365
|
|
227
366
|
def state
|
228
|
-
|
367
|
+
instance_variables.map { |variable| instance_variable_get variable }
|
229
368
|
end
|
230
369
|
end
|
231
370
|
|
232
|
-
|
233
|
-
|
234
|
-
attr_accessor :op
|
235
|
-
attr_accessor :right
|
236
|
-
|
237
|
-
def initialize(left, op, right)
|
238
|
-
@left = left
|
239
|
-
@op = op
|
240
|
-
@right = right
|
241
|
-
end
|
371
|
+
def lit(value)
|
372
|
+
return value if value.is_a?(Literal)
|
242
373
|
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
"#{left.ref_sql} and #{right.ref_sql}"
|
250
|
-
elsif op == :eq
|
251
|
-
if right.is_a?(Literal) && right.value.nil?
|
252
|
-
"#{left.ref_sql} is #{right.ref_sql}"
|
253
|
-
else
|
254
|
-
"#{left.ref_sql} = #{right.ref_sql}"
|
255
|
-
end
|
256
|
-
elsif op == :ne
|
257
|
-
if right.is_a?(Literal) && right.value.nil?
|
258
|
-
"#{left.ref_sql} is not #{right.ref_sql}"
|
259
|
-
else
|
260
|
-
"#{left.ref_sql} != #{right.ref_sql}"
|
261
|
-
end
|
262
|
-
elsif op == :*
|
263
|
-
"#{left.ref_sql} * #{right.ref_sql}"
|
264
|
-
elsif op == :<=
|
265
|
-
"#{left.ref_sql} <= #{right.ref_sql}"
|
266
|
-
elsif op == :in
|
267
|
-
"#{left.ref_sql} in (#{right.map {|r|r.ref_sql}.join(', ')})"
|
268
|
-
else
|
269
|
-
raise ArgumentError.new("#{op} is not supported by Expr")
|
270
|
-
end
|
271
|
-
end
|
272
|
-
|
273
|
-
def ==(other)
|
274
|
-
other.class == self.class && other.state == self.state
|
275
|
-
end
|
276
|
-
|
277
|
-
def state
|
278
|
-
self.instance_variables.map { |variable| self.instance_variable_get variable }
|
374
|
+
if value.is_a?(String) || value.is_a?(Integer) || value.is_a?(Float) || value.nil?
|
375
|
+
Literal.new(value)
|
376
|
+
elsif value.is_a?(Array)
|
377
|
+
value.map { |v| lit(v) }
|
378
|
+
else
|
379
|
+
value
|
279
380
|
end
|
280
381
|
end
|
281
382
|
|
282
383
|
class Table
|
283
|
-
attr_accessor :table_name
|
284
|
-
attr_accessor :rows
|
384
|
+
attr_accessor :table_name, :rows
|
285
385
|
|
286
386
|
def initialize(struct, table_name)
|
287
387
|
@table_name = table_name
|
@@ -294,16 +394,16 @@ class Table
|
|
294
394
|
end
|
295
395
|
|
296
396
|
class Query
|
297
|
-
attr_accessor :froms
|
298
|
-
attr_accessor :conditions
|
299
|
-
attr_accessor :grouped_keys
|
300
|
-
attr_accessor :row
|
397
|
+
attr_accessor :froms, :conditions, :grouped_keys, :order_bys, :row, :_limit, :_offset
|
301
398
|
|
302
|
-
def initialize(froms, conditions = [], grouped_keys = [], row = nil)
|
399
|
+
def initialize(froms, conditions = [], grouped_keys = [], order_bys = [], row = nil, limit = nil, offset = nil)
|
303
400
|
@froms = froms + []
|
304
401
|
@conditions = conditions + []
|
305
402
|
@grouped_keys = grouped_keys + []
|
403
|
+
@order_bys = order_bys + []
|
306
404
|
@row = row
|
405
|
+
@_limit = limit
|
406
|
+
@_offset = offset
|
307
407
|
@subquery_name = nil
|
308
408
|
end
|
309
409
|
|
@@ -316,36 +416,65 @@ class Query
|
|
316
416
|
end
|
317
417
|
|
318
418
|
def map(&blk)
|
319
|
-
if @row
|
320
|
-
return Query.from(self).map(&blk)
|
321
|
-
end
|
419
|
+
return Query.from(self).map(&blk) if @row
|
322
420
|
|
323
421
|
result = expr(&blk).call(*get_from_rows)
|
324
422
|
Query.new(
|
325
423
|
@froms,
|
326
424
|
@conditions,
|
327
425
|
@grouped_keys,
|
328
|
-
|
426
|
+
@order_bys,
|
427
|
+
Row.new(result.class.members, result),
|
428
|
+
@_limit,
|
429
|
+
@_offset
|
329
430
|
)
|
330
431
|
end
|
331
432
|
|
332
433
|
def has?(column_name)
|
333
|
-
rows.any? {|r| r.has?(column_name)}
|
434
|
+
rows.any? { |r| r.has?(column_name) }
|
334
435
|
end
|
335
436
|
|
336
437
|
def rows
|
337
|
-
if @row
|
338
|
-
return [@row]
|
339
|
-
end
|
438
|
+
return [@row] if @row
|
340
439
|
|
341
440
|
get_from_rows
|
342
441
|
end
|
343
442
|
|
344
|
-
def
|
345
|
-
if @row
|
346
|
-
|
443
|
+
def order_by(&blk)
|
444
|
+
return Query.from(self).order_by(&blk) if @row
|
445
|
+
|
446
|
+
result = expr(&blk).call(*get_from_rows)
|
447
|
+
|
448
|
+
if result.is_a?(OrderedByExpr)
|
449
|
+
result = [result]
|
450
|
+
elsif result.is_a?(Expr)
|
451
|
+
result = OrderedByExpr.new(result)
|
347
452
|
end
|
348
453
|
|
454
|
+
Query.new(
|
455
|
+
@froms,
|
456
|
+
@conditions,
|
457
|
+
@grouped_keys,
|
458
|
+
@order_bys + result,
|
459
|
+
@row,
|
460
|
+
@_limit,
|
461
|
+
@_offset
|
462
|
+
)
|
463
|
+
end
|
464
|
+
|
465
|
+
def limit(number)
|
466
|
+
@_limit = number
|
467
|
+
self
|
468
|
+
end
|
469
|
+
|
470
|
+
def offset(number)
|
471
|
+
@_offset = number
|
472
|
+
self
|
473
|
+
end
|
474
|
+
|
475
|
+
def group_by(&blk)
|
476
|
+
return Query.from(self).group_by(&blk) if @row
|
477
|
+
|
349
478
|
result = expr(&blk).call(*get_from_rows)
|
350
479
|
|
351
480
|
if result.is_a?(Column)
|
@@ -365,94 +494,125 @@ class Query
|
|
365
494
|
perform_join(:left_join, other, &blk)
|
366
495
|
end
|
367
496
|
|
497
|
+
def right_join(other, &blk)
|
498
|
+
perform_join(:right_join, other, &blk)
|
499
|
+
end
|
500
|
+
|
501
|
+
def cross_join(other)
|
502
|
+
perform_join(:cross_join, other)
|
503
|
+
end
|
504
|
+
|
505
|
+
def cross_join_unnest(ordinality = false, &blk)
|
506
|
+
return Query.from(self).cross_join_unnest(ordinality, &blk) if @row || @conditions.size.positive? || @order_bys.size.positive? || @_limit || @_offset
|
507
|
+
|
508
|
+
result = expr(&blk).call(*get_from_rows)
|
509
|
+
row = Row.new(result.class.members, result)
|
510
|
+
from = CrossJoinUnnest.new(From.new(Table.new(result.class, 't')), row, ordinality)
|
511
|
+
Query.new(
|
512
|
+
@froms + [from],
|
513
|
+
@conditions,
|
514
|
+
@grouped_keys,
|
515
|
+
)
|
516
|
+
end
|
517
|
+
|
368
518
|
def where(&blk)
|
369
|
-
if @row
|
370
|
-
return Query.from(self).where(&blk)
|
371
|
-
end
|
519
|
+
return Query.from(self).where(&blk) if @row
|
372
520
|
|
373
521
|
condition = expr(&blk).call(*get_from_rows)
|
374
522
|
Query.new(
|
375
523
|
@froms,
|
376
524
|
@conditions + [condition],
|
377
525
|
@grouped_keys,
|
378
|
-
@
|
526
|
+
@order_bys,
|
527
|
+
@row,
|
528
|
+
@_limit,
|
529
|
+
@_offset
|
379
530
|
)
|
380
531
|
end
|
381
532
|
|
382
533
|
def subquery_name
|
383
|
-
if is_vanilla
|
384
|
-
return @froms.first.source.subquery_name
|
385
|
-
end
|
534
|
+
return @froms.first.source.subquery_name if is_vanilla
|
386
535
|
|
387
|
-
if @subquery_name.nil?
|
388
|
-
raise ArgumentError.new("The query #{self.inspect} doesn't have a subquery name")
|
389
|
-
end
|
536
|
+
raise ArgumentError, "The query #{inspect} doesn't have a subquery name" if @subquery_name.nil?
|
390
537
|
|
391
538
|
@subquery_name
|
392
539
|
end
|
393
540
|
|
394
|
-
|
395
|
-
@subquery_name = value
|
396
|
-
end
|
541
|
+
attr_writer :subquery_name
|
397
542
|
|
398
543
|
def sql
|
399
|
-
s =
|
400
|
-
s += rows.map
|
401
|
-
s +=
|
544
|
+
s = 'select '
|
545
|
+
s += rows.map(&:decl_sql).join(', ')
|
546
|
+
s += ' from'
|
402
547
|
|
403
548
|
@froms.each_with_index do |from, index|
|
404
|
-
if
|
405
|
-
if
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
549
|
+
if from.is_a?(From)
|
550
|
+
if index >= 1
|
551
|
+
if from.join_type == :join
|
552
|
+
s += ' join'
|
553
|
+
elsif from.join_type == :left_join
|
554
|
+
s += ' left join'
|
555
|
+
elsif from.join_type == :right_join
|
556
|
+
s += ' right join'
|
557
|
+
elsif from.join_type == :cross_join
|
558
|
+
s += ' cross join'
|
559
|
+
else
|
560
|
+
raise ArgumentError, "The join type #{from.join_type} is not supoprted."
|
561
|
+
end
|
411
562
|
end
|
412
|
-
end
|
413
563
|
|
414
|
-
|
564
|
+
s += " #{from.source.subquery_name}"
|
415
565
|
|
416
|
-
|
417
|
-
s += " #{from.alias_name}"
|
418
|
-
end
|
566
|
+
s += " #{from.alias_name}" if from.source.subquery_name != from.alias_name
|
419
567
|
|
420
|
-
|
421
|
-
|
568
|
+
s += " on #{from.condition.ref_sql}" if from.condition
|
569
|
+
elsif from.is_a?(CrossJoinUnnest)
|
570
|
+
origins, cols = from.rows.first.columns.map {|c| [c.origin.ref_sql, c.name]}.transpose
|
571
|
+
s += " cross join unnest (#{origins.join(', ')})"
|
572
|
+
if from.ordinality
|
573
|
+
s += ' with ordinality'
|
574
|
+
cols.push('ordinal')
|
575
|
+
end
|
576
|
+
s += " as #{from.from.source.table_name} (#{cols.join(', ')})"
|
577
|
+
else
|
578
|
+
raise ArgumentError.new("From doesn't support #{from.inspect}")
|
422
579
|
end
|
423
580
|
end
|
424
581
|
|
425
|
-
if @conditions.size
|
426
|
-
|
582
|
+
s += " where #{@conditions.map(&:ref_sql).join(' and ')}" if @conditions.size.positive?
|
583
|
+
|
584
|
+
s += " group by #{@grouped_keys.map(&:ref_sql).join(', ')}" if @grouped_keys.size.positive?
|
585
|
+
|
586
|
+
s += " order by #{@order_bys.map(&:sql).join(', ')}" if @order_bys.size.positive?
|
587
|
+
|
588
|
+
if @_offset
|
589
|
+
s += " offset #{@_offset}"
|
427
590
|
end
|
428
591
|
|
429
|
-
if @
|
430
|
-
s += "
|
592
|
+
if @_limit
|
593
|
+
s += " limit #{@_limit}"
|
431
594
|
end
|
432
595
|
|
433
596
|
s
|
434
597
|
end
|
435
598
|
|
436
599
|
private
|
600
|
+
|
437
601
|
def get_from_rows
|
438
|
-
@froms.map {|f| f.rows.map {|r|r.with_from(f)}}.flatten
|
602
|
+
@froms.map { |f| f.rows.map { |r| r.with_from(f) } }.flatten
|
439
603
|
end
|
440
604
|
|
441
605
|
def get_next_alias
|
442
|
-
alias_names = @froms.map
|
606
|
+
alias_names = @froms.map(&:raw_alias_name).compact
|
443
607
|
index = 0
|
444
608
|
alias_names.sort.each do |name|
|
445
|
-
if name == "alias#{index}"
|
446
|
-
index += 1
|
447
|
-
end
|
609
|
+
index += 1 if name == "alias#{index}"
|
448
610
|
end
|
449
611
|
"alias#{index}"
|
450
612
|
end
|
451
613
|
|
452
614
|
def perform_join(join_type, other, &blk)
|
453
|
-
if @row || @conditions.size
|
454
|
-
return Query.from(self).send(:perform_join, join_type, other, &blk)
|
455
|
-
end
|
615
|
+
return Query.from(self).send(:perform_join, join_type, other, &blk) if @row || @conditions.size.positive? || @order_bys.size.positive? || @_limit || @_offset
|
456
616
|
|
457
617
|
alias_name = nil
|
458
618
|
@froms.each do |from|
|
@@ -463,13 +623,16 @@ class Query
|
|
463
623
|
end
|
464
624
|
|
465
625
|
other_from = From.new(other, join_type, nil, alias_name)
|
466
|
-
|
467
|
-
|
626
|
+
if blk
|
627
|
+
condition = expr(&blk).call(*(get_from_rows + other_from.rows.map { |r| r.with_from(other_from) }))
|
628
|
+
other_from.condition = condition
|
629
|
+
end
|
468
630
|
|
469
631
|
Query.new(
|
470
632
|
@froms + [other_from],
|
471
633
|
@conditions,
|
472
634
|
@grouped_keys,
|
635
|
+
@order_bys,
|
473
636
|
@row
|
474
637
|
)
|
475
638
|
end
|
@@ -477,9 +640,9 @@ end
|
|
477
640
|
|
478
641
|
class IfElse
|
479
642
|
def initialize(cond, true_result, false_result)
|
480
|
-
@condition = cond
|
481
|
-
@true_result = true_result
|
482
|
-
@false_result = false_result
|
643
|
+
@condition = lit(cond)
|
644
|
+
@true_result = lit(true_result)
|
645
|
+
@false_result = lit(false_result)
|
483
646
|
end
|
484
647
|
|
485
648
|
def ref_sql
|
@@ -491,11 +654,11 @@ class IfElse
|
|
491
654
|
end
|
492
655
|
|
493
656
|
def ==(other)
|
494
|
-
other.class == self.class && other.state ==
|
657
|
+
other.class == self.class && other.state == state
|
495
658
|
end
|
496
659
|
|
497
660
|
def state
|
498
|
-
|
661
|
+
instance_variables.map { |variable| instance_variable_get variable }
|
499
662
|
end
|
500
663
|
end
|
501
664
|
|
@@ -509,16 +672,12 @@ def generate_sql(query)
|
|
509
672
|
|
510
673
|
sql = ''
|
511
674
|
|
512
|
-
if queries.size
|
513
|
-
sql += 'with '
|
514
|
-
end
|
675
|
+
sql += 'with ' if queries.size.positive?
|
515
676
|
|
516
|
-
queries.map.with_index do |
|
517
|
-
if index
|
518
|
-
|
519
|
-
|
520
|
-
query.subquery_name = "subquery#{index}"
|
521
|
-
sql += "#{query.subquery_name} as (\n#{query.sql}\n)\n"
|
677
|
+
queries.map.with_index do |q, index|
|
678
|
+
sql += ', ' if index.positive?
|
679
|
+
q.subquery_name = "subquery#{index}"
|
680
|
+
sql += "#{q.subquery_name} as (\n#{q.sql}\n)\n"
|
522
681
|
end
|
523
682
|
|
524
683
|
sql += last_query.sql
|
@@ -531,11 +690,12 @@ def fill(query)
|
|
531
690
|
|
532
691
|
queries = []
|
533
692
|
query.froms.each do |from|
|
534
|
-
if from.
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
693
|
+
next if from.is_a?(CrossJoinUnnest)
|
694
|
+
next unless from.source.is_a?(Query)
|
695
|
+
|
696
|
+
subqueries = fill(from.source)
|
697
|
+
subqueries.each do |subquery|
|
698
|
+
queries.push(subquery)
|
539
699
|
end
|
540
700
|
end
|
541
701
|
queries.push(query)
|
@@ -544,22 +704,6 @@ end
|
|
544
704
|
|
545
705
|
$ruby2ruby = Ruby2Ruby.new
|
546
706
|
|
547
|
-
def search_for_expr_block(parsed)
|
548
|
-
# s(:iter, s(:call, nil, :expr)
|
549
|
-
|
550
|
-
if parsed[0] == :iter && parsed[1][0] == :call && parsed[1][1].nil? && parsed[1][2] == :expr
|
551
|
-
return parsed[3]
|
552
|
-
end
|
553
|
-
|
554
|
-
parsed.each do |component|
|
555
|
-
if component.is_a?(Sexp)
|
556
|
-
return search_for_expr_block(component)
|
557
|
-
end
|
558
|
-
end
|
559
|
-
|
560
|
-
nil
|
561
|
-
end
|
562
|
-
|
563
707
|
def rewrite(parsed)
|
564
708
|
parsed = parsed.map do |component|
|
565
709
|
if component.is_a?(Sexp)
|
@@ -569,8 +713,25 @@ def rewrite(parsed)
|
|
569
713
|
end
|
570
714
|
end
|
571
715
|
|
572
|
-
if parsed[0] == :call
|
573
|
-
parsed[2]
|
716
|
+
if parsed[0] == :call
|
717
|
+
if parsed[2] == :==
|
718
|
+
parsed[2] = :eq
|
719
|
+
elsif parsed[2] == :!=
|
720
|
+
parsed[2] = :ne
|
721
|
+
elsif parsed[2] == :!
|
722
|
+
parsed[2] = :not
|
723
|
+
elsif parsed[2] == :-@
|
724
|
+
parsed[2] = :minus
|
725
|
+
elsif parsed[2] == :+@
|
726
|
+
parsed[2] = :plus
|
727
|
+
elsif parsed[2] == :nil?
|
728
|
+
parsed = Sexp.new(
|
729
|
+
:call,
|
730
|
+
parsed[1],
|
731
|
+
:eq,
|
732
|
+
Sexp.new(:nil)
|
733
|
+
)
|
734
|
+
end
|
574
735
|
elsif parsed[0] == :and
|
575
736
|
parsed = Sexp.new(
|
576
737
|
:call,
|
@@ -578,20 +739,6 @@ def rewrite(parsed)
|
|
578
739
|
:and,
|
579
740
|
parsed[2]
|
580
741
|
)
|
581
|
-
elsif parsed[0] == :str
|
582
|
-
parsed = Sexp.new(
|
583
|
-
:call,
|
584
|
-
Sexp.new(:const, :Literal),
|
585
|
-
:new,
|
586
|
-
Sexp.new(:str, parsed[1])
|
587
|
-
)
|
588
|
-
elsif parsed[0] == :lit && (parsed[1].is_a?(Integer) || parsed[1].is_a?(Float))
|
589
|
-
parsed = Sexp.new(
|
590
|
-
:call,
|
591
|
-
Sexp.new(:const, :Literal),
|
592
|
-
:new,
|
593
|
-
Sexp.new(:lit, parsed[1])
|
594
|
-
)
|
595
742
|
elsif parsed[0] == :case && parsed[2] && parsed[2][0] == :in
|
596
743
|
parsed = Sexp.new(
|
597
744
|
:call,
|
@@ -606,14 +753,7 @@ def rewrite(parsed)
|
|
606
753
|
:ifElse,
|
607
754
|
parsed[1],
|
608
755
|
parsed[2],
|
609
|
-
parsed[3]
|
610
|
-
)
|
611
|
-
elsif parsed[0] == :nil
|
612
|
-
parsed = Sexp.new(
|
613
|
-
:call,
|
614
|
-
Sexp.new(:const, :Literal),
|
615
|
-
:new,
|
616
|
-
Sexp.new(:nil)
|
756
|
+
parsed[3]
|
617
757
|
)
|
618
758
|
end
|
619
759
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lilit-sql
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tanin Na Nakorn
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-06-
|
11
|
+
date: 2023-06-29 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: |-
|
14
14
|
lilit-sql is a Ruby DSL for composing production-grade analytical SQLs
|