lilit-sql 0.0.1 → 0.0.2
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 +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
|