alf 0.14.0 → 0.15.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. data/Gemfile +6 -3
  2. data/Gemfile.lock +65 -19
  3. data/README.md +77 -183
  4. data/Rakefile +25 -0
  5. data/alf.gemspec +5 -3
  6. data/alf.noespec +8 -6
  7. data/lib/alf/loader.rb +1 -0
  8. data/lib/alf/version.rb +1 -1
  9. data/spec/facade/test_query.rb +12 -0
  10. data/spec/key-inference/queries.yml +10 -0
  11. data/spec/key-inference/test_all.rb +21 -0
  12. data/{test → spec}/migrations/test_folder_migration.rb +0 -0
  13. data/{test → spec}/migrations/test_sequel_migration.rb +1 -1
  14. data/spec/operators/ungroup/grouped.json +3 -0
  15. data/spec/operators/ungroup/test_on_json_content.rb +11 -0
  16. data/spec/operators/unwrap/test_on_json_content.rb +11 -0
  17. data/spec/operators/unwrap/wrapped.json +3 -0
  18. data/spec/optimizer/project/extend.yml +20 -0
  19. data/spec/optimizer/project/intersect.yml +10 -0
  20. data/spec/optimizer/project/join.yml +20 -0
  21. data/spec/optimizer/project/matching.yml +21 -0
  22. data/spec/optimizer/project/minus.yml +9 -0
  23. data/spec/optimizer/project/not_matching.yml +21 -0
  24. data/spec/optimizer/project/project.yml +88 -0
  25. data/spec/optimizer/project/rename.yml +30 -0
  26. data/spec/optimizer/project/sort.yml +45 -0
  27. data/spec/optimizer/project/union.yml +8 -0
  28. data/spec/optimizer/restrict/clip.yml +4 -0
  29. data/spec/optimizer/restrict/compact.yml +4 -0
  30. data/spec/optimizer/restrict/generator.yml +4 -0
  31. data/spec/optimizer/restrict/intersect.yml +4 -0
  32. data/spec/optimizer/restrict/leaf_operand.yml +4 -0
  33. data/spec/optimizer/restrict/minus.yml +4 -0
  34. data/spec/optimizer/restrict/page.yml +12 -0
  35. data/spec/optimizer/restrict/project.yml +4 -0
  36. data/spec/optimizer/restrict/sort.yml +4 -0
  37. data/spec/optimizer/restrict/union.yml +4 -0
  38. data/spec/optimizer/test_all.rb +34 -0
  39. data/spec/sql/helpers.rb +25 -0
  40. data/spec/sql/queries/01-leaf-operand.yml +5 -0
  41. data/spec/sql/queries/02-clip.yml +12 -0
  42. data/spec/sql/queries/03-sort.yml +58 -0
  43. data/spec/sql/queries/04-frame.yml +57 -0
  44. data/spec/sql/queries/05-intersect.yml +23 -0
  45. data/spec/sql/queries/06-join.yml +207 -0
  46. data/spec/sql/queries/07-matching.yml +76 -0
  47. data/spec/sql/queries/08-minus.yml +23 -0
  48. data/spec/sql/queries/09-not-matching.yml +57 -0
  49. data/spec/sql/queries/10-page.yml +31 -0
  50. data/spec/sql/queries/11-project.yml +48 -0
  51. data/spec/sql/queries/12-rename.yml +24 -0
  52. data/spec/sql/queries/13-restrict.yml +114 -0
  53. data/spec/sql/queries/15-union.yml +90 -0
  54. data/spec/sql/queries/16-wrap.yml +3 -0
  55. data/spec/sql/queries/91-reuse.yml +28 -0
  56. data/spec/sql/test_sequel_compiler.rb +41 -0
  57. data/spec/sql/test_sql_compiler.rb +52 -0
  58. data/{test → spec}/test_alf.rb +0 -0
  59. data/spec/test_helpers.rb +54 -0
  60. data/tasks/doc.rake +10 -0
  61. data/tasks/fixtures.rake +52 -0
  62. data/tasks/mod.rake +50 -0
  63. data/tasks/release.rake +34 -0
  64. data/tasks/test.rake +2 -2
  65. metadata +150 -19
  66. data/test/seeding/test_seeding.rb +0 -49
  67. data/test/test_helpers.rb +0 -24
@@ -0,0 +1,4 @@
1
+ - alf: |-
2
+ restrict(project(suppliers, [:sid, :name]), city: 'Paris')
3
+ optimized: |-
4
+ project(restrict(suppliers, city: 'Paris'), [:sid, :name])
@@ -0,0 +1,4 @@
1
+ - alf: |-
2
+ restrict(sort(suppliers, [:sid, :asc]), city: 'Paris')
3
+ optimized: |-
4
+ sort(restrict(suppliers, city: 'Paris'), [:sid, :asc])
@@ -0,0 +1,4 @@
1
+ - alf: |-
2
+ restrict(union(suppliers, suppliers), city: 'Paris')
3
+ optimized: |-
4
+ union(restrict(suppliers, city: 'Paris'), restrict(suppliers, city: 'Paris'))
@@ -0,0 +1,34 @@
1
+ require 'test_helpers'
2
+
3
+ Path.dir.glob('*').select{|f| f.directory? }.each do |folder|
4
+ optimizer = Alf::Optimizer.const_get(folder.basename.to_s.capitalize)
5
+
6
+ describe optimizer do
7
+
8
+ folder.glob('*.yml').each do |file|
9
+ context "On #{file.basename}" do
10
+
11
+ file.load.each do |query|
12
+ alf, optimized = query['alf'], query['optimized']
13
+
14
+ context "on #{alf}" do
15
+ let(:expr) { conn.parse(alf) }
16
+ let(:expected){ conn.parse(optimized) }
17
+
18
+ subject{ optimizer.new.call(expr) }
19
+
20
+ it 'should be optimized as expected' do
21
+ subject.should eq(expected)
22
+ end
23
+
24
+ it 'should have same heading as the initial expression' do
25
+ subject.heading.should eq(expr.heading)
26
+ end
27
+ end
28
+ end
29
+
30
+ end
31
+ end
32
+ end
33
+
34
+ end
@@ -0,0 +1,25 @@
1
+ require 'test_helpers'
2
+
3
+ module SqlTestHelpers
4
+
5
+ def each_query_file(&bl)
6
+ (Path.dir/'queries').glob("*.yml", &bl)
7
+ end
8
+
9
+ def each_query(&bl)
10
+ each_query_file do |file|
11
+ queries = file.load
12
+ queries.each(&bl)
13
+ end
14
+ end
15
+
16
+ def strip(x)
17
+ x.strip.gsub(/\s+/, " ").gsub(/\(\s+/, "(").gsub(/\s+\)/, ")")
18
+ end
19
+
20
+ end
21
+
22
+ RSpec.configure do |c|
23
+ c.include(SqlTestHelpers)
24
+ c.extend(SqlTestHelpers)
25
+ end
@@ -0,0 +1,5 @@
1
+ - alf: |-
2
+ suppliers
3
+ sql: |-
4
+ SELECT t1.sid, t1.name, t1.status, t1.city
5
+ FROM suppliers AS t1
@@ -0,0 +1,12 @@
1
+ # normal clip
2
+ - alf: |-
3
+ clip(suppliers, [:sid, :status])
4
+ sql: |-
5
+ SELECT t1.sid, t1.status
6
+ FROM suppliers AS t1
7
+ # clip with no attribute at all
8
+ - alf: |-
9
+ clip(suppliers, [])
10
+ sql: |-
11
+ SELECT TRUE AS is_table_dee
12
+ WHERE EXISTS(SELECT * FROM suppliers AS t1)
@@ -0,0 +1,58 @@
1
+ - alf: |-
2
+ sort(suppliers, [[:sid, :asc]])
3
+ sql: |-
4
+ SELECT t1.sid, t1.name, t1.status, t1.city
5
+ FROM suppliers AS t1
6
+ ORDER BY t1.sid ASC
7
+ #
8
+ - alf: |-
9
+ sort(suppliers, [[:sid, :desc]])
10
+ sql: |-
11
+ SELECT t1.sid, t1.name, t1.status, t1.city
12
+ FROM suppliers AS t1
13
+ ORDER BY t1.sid DESC
14
+ #
15
+ - alf: |-
16
+ sort(suppliers, [[:city, :desc], [:sid, :asc]])
17
+ sql: |-
18
+ SELECT t1.sid, t1.name, t1.status, t1.city
19
+ FROM suppliers AS t1
20
+ ORDER BY t1.city DESC, t1.sid ASC
21
+ #
22
+ - alf: |-
23
+ sort(sort(suppliers, [[:sid, :asc], [:city, :desc]]), [:sid, :asc])
24
+ sql: |-
25
+ SELECT t1.sid, t1.name, t1.status, t1.city
26
+ FROM suppliers AS t1
27
+ ORDER BY t1.sid ASC, t1.city DESC
28
+ #
29
+ - alf: |-
30
+ sort(sort(suppliers, [:sid, :asc]), [:sid, :desc])
31
+ sql: |-
32
+ WITH t2 AS (
33
+ SELECT t1.sid,
34
+ t1.name,
35
+ t1.status,
36
+ t1.city
37
+ FROM suppliers AS t1
38
+ ORDER BY t1.sid ASC
39
+ )
40
+ SELECT t2.sid, t2.name, t2.status, t2.city
41
+ FROM t2 AS t2
42
+ ORDER BY t2.sid DESC
43
+ #
44
+ - alf: |-
45
+ sort(union(suppliers_in_paris, suppliers_in_london), [[:sid, :asc]])
46
+ sql: |-
47
+ WITH t3 AS (
48
+ (SELECT t1.sid, t1.name, t1.status, t1.city
49
+ FROM suppliers AS t1
50
+ WHERE t1.city = 'Paris')
51
+ UNION
52
+ (SELECT t2.sid, t2.name, t2.status, t2.city
53
+ FROM suppliers AS t2
54
+ WHERE t2.city = 'London')
55
+ )
56
+ SELECT t3.sid, t3.name, t3.status, t3.city
57
+ FROM t3 AS t3
58
+ ORDER BY t3.sid ASC
@@ -0,0 +1,57 @@
1
+ # BASE case
2
+ - alf: |-
3
+ frame(suppliers, [[:sid, :asc]], 2, 4)
4
+ sql: |-
5
+ SELECT t1.sid, t1.name, t1.status, t1.city
6
+ FROM suppliers AS t1
7
+ ORDER BY t1.sid ASC
8
+ LIMIT 4 OFFSET 2
9
+ # KEY INFERENCE for total ordering
10
+ - alf: |-
11
+ frame(suppliers, [[:city, :desc]], 2, 4)
12
+ sql: |-
13
+ SELECT t1.sid, t1.name, t1.status, t1.city
14
+ FROM suppliers AS t1
15
+ ORDER BY t1.city DESC, t1.sid ASC
16
+ LIMIT 4 OFFSET 2
17
+ # ORDER BY already ok
18
+ - alf: |-
19
+ frame(sort(suppliers, [[:city, :desc], [:sid, :asc]]), [[:city, :desc]], 2, 4)
20
+ sql: |-
21
+ SELECT t1.sid, t1.name, t1.status, t1.city
22
+ FROM suppliers AS t1
23
+ ORDER BY t1.city DESC, t1.sid ASC
24
+ LIMIT 4 OFFSET 2
25
+ # ORDER BY existing but incompatible
26
+ - alf: |-
27
+ frame(sort(suppliers, [[:sid, :asc], [:city, :desc]]), [[:city, :desc]], 2, 4)
28
+ sql: |-
29
+ WITH t2 AS (
30
+ SELECT t1.sid, t1.name, t1.status, t1.city
31
+ FROM suppliers AS t1
32
+ ORDER BY t1.sid ASC, t1.city DESC
33
+ )
34
+ SELECT t2.sid, t2.name, t2.status, t2.city
35
+ FROM t2 AS t2
36
+ ORDER BY t2.city DESC, t2.sid ASC
37
+ LIMIT 4 OFFSET 2
38
+ # UNION and other nadics
39
+ - alf: |-
40
+ frame(union(suppliers_in_paris, suppliers_in_london), [[:sid, :asc]], 2, 4)
41
+ sql: |-
42
+ WITH t3 AS (
43
+ (SELECT t1.sid, t1.name, t1.status, t1.city
44
+ FROM suppliers AS t1
45
+ WHERE t1.city = 'Paris')
46
+ UNION
47
+ (SELECT t2.sid, t2.name, t2.status, t2.city
48
+ FROM suppliers AS t2
49
+ WHERE t2.city = 'London')
50
+ )
51
+ SELECT t3.sid, t3.name, t3.status, t3.city
52
+ FROM t3 AS t3
53
+ ORDER BY t3.sid ASC, t3.name ASC, t3.status ASC, t3.city ASC
54
+ LIMIT 4 OFFSET 2
55
+ comment: |-
56
+ The big ordering comes from the fact that key inference is not smart
57
+ enough to detect that sid is still a key in the resulting union.
@@ -0,0 +1,23 @@
1
+ - alf: |-
2
+ intersect(suppliers_in_paris, suppliers_in_london)
3
+ sql: |-
4
+ (SELECT t1.sid, t1.name, t1.status, t1.city
5
+ FROM suppliers AS t1
6
+ WHERE t1.city = 'Paris')
7
+ INTERSECT
8
+ (SELECT t2.sid, t2.name, t2.status, t2.city
9
+ FROM suppliers AS t2
10
+ WHERE t2.city = 'London')
11
+ - alf: |-
12
+ intersect(intersect(suppliers_in_paris, suppliers_in_london), suppliers)
13
+ sql: |-
14
+ ((SELECT t1.sid, t1.name, t1.status, t1.city
15
+ FROM suppliers AS t1
16
+ WHERE t1.city = 'Paris')
17
+ INTERSECT
18
+ (SELECT t2.sid, t2.name, t2.status, t2.city
19
+ FROM suppliers AS t2
20
+ WHERE t2.city = 'London'))
21
+ INTERSECT
22
+ (SELECT t3.sid, t3.name, t3.status, t3.city
23
+ FROM suppliers AS t3)
@@ -0,0 +1,207 @@
1
+ # cross join
2
+ - alf: |-
3
+ join(project(suppliers, [:sid]), project(parts, [:pid]))
4
+ sql:
5
+ SELECT t1.sid, t2.pid
6
+ FROM suppliers AS t1, parts AS t2
7
+ # inner join
8
+ - alf: |-
9
+ join(suppliers, supplies)
10
+ sql:
11
+ SELECT t1.sid, t1.name, t1.status, t1.city, t2.pid, t2.qty
12
+ FROM suppliers AS t1
13
+ JOIN supplies AS t2 ON t1.sid = t2.sid
14
+ # inner join with same operand
15
+ - alf: |-
16
+ join(suppliers, suppliers)
17
+ sql: |-
18
+ SELECT t1.sid, t1.name, t1.status, t1.city
19
+ FROM suppliers AS t1
20
+ JOIN suppliers AS t2 ON t1.sid = t2.sid
21
+ AND t1.name = t2.name
22
+ AND t1.status = t2.status
23
+ AND t1.city = t2.city
24
+ # inner join requiring keeping a common distinct
25
+ - alf: |-
26
+ join(project(suppliers, [:city]), project(parts, [:city]))
27
+ sql:
28
+ SELECT DISTINCT t1.city
29
+ FROM suppliers AS t1
30
+ JOIN parts AS t2 ON t1.city = t2.city
31
+ # inner join requiring keeping the left distinct
32
+ - alf: |-
33
+ join(project(suppliers, [:city]), parts)
34
+ sql:
35
+ SELECT DISTINCT t1.city, t2.pid, t2.name, t2.color, t2.weight
36
+ FROM suppliers AS t1
37
+ JOIN parts AS t2 ON t1.city = t2.city
38
+ # inner join requiring keeping the right distinct
39
+ - alf: |-
40
+ join(suppliers, project(parts, [:city]))
41
+ sql:
42
+ SELECT DISTINCT t1.sid, t1.name, t1.status, t1.city
43
+ FROM suppliers AS t1
44
+ JOIN parts AS t2 ON t1.city = t2.city
45
+ # inner join requiring renamings
46
+ - alf: |-
47
+ join(rename(suppliers, sid: :id), rename(supplies, sid: :id))
48
+ sql:
49
+ SELECT t1.sid AS id, t1.name, t1.status, t1.city, t2.pid, t2.qty
50
+ FROM suppliers AS t1
51
+ JOIN supplies AS t2 ON t1.sid = t2.sid
52
+ # triple join
53
+ - alf: |-
54
+ join(join(suppliers, supplies), project(parts, [:pid, :color]))
55
+ sql:
56
+ WITH t4 AS (
57
+ SELECT t1.sid, t1.name, t1.status, t1.city, t2.pid, t2.qty
58
+ FROM suppliers AS t1
59
+ JOIN supplies AS t2 ON t1.sid = t2.sid
60
+ )
61
+ SELECT t4.sid, t4.name, t4.status, t4.city, t4.pid, t4.qty, t3.color
62
+ FROM t4 AS t4
63
+ JOIN parts AS t3 ON t4.pid = t3.pid
64
+ # join of two joins
65
+ - alf: |-
66
+ join(join(suppliers, cities), join(project(parts, [:pid, :color]), supplies))
67
+ sql:
68
+ WITH
69
+ t5 AS (
70
+ SELECT t1.sid, t1.name, t1.status, t1.city, t2.country
71
+ FROM suppliers AS t1
72
+ JOIN cities AS t2 ON t1.city = t2.city
73
+ ),
74
+ t6 AS (
75
+ SELECT t3.pid, t3.color, t4.sid, t4.qty
76
+ FROM parts AS t3
77
+ JOIN supplies AS t4 ON t3.pid = t4.pid
78
+ )
79
+ SELECT t5.sid, t5.name, t5.status, t5.city, t5.country, t6.pid, t6.color, t6.qty
80
+ FROM t5 AS t5
81
+ JOIN t6 AS t6 ON t5.sid = t6.sid
82
+ # join with a where clause at left
83
+ - alf: |-
84
+ join(restrict(suppliers, sid: 'S2'), supplies)
85
+ sql:
86
+ SELECT t1.sid, t1.name, t1.status, t1.city, t2.pid, t2.qty
87
+ FROM suppliers AS t1
88
+ JOIN supplies AS t2 ON t1.sid = t2.sid
89
+ WHERE t1.sid = 'S2'
90
+ # join with a where clause at right
91
+ - alf: |-
92
+ join(suppliers, restrict(supplies, sid: 'S2'))
93
+ sql:
94
+ SELECT t1.sid, t1.name, t1.status, t1.city, t2.pid, t2.qty
95
+ FROM suppliers AS t1
96
+ JOIN supplies AS t2 ON t1.sid = t2.sid
97
+ WHERE t2.sid = 'S2'
98
+ # join with where clauses both sides
99
+ - alf: |-
100
+ join(restrict(suppliers, sid: 'S2'), restrict(supplies, qty: 300))
101
+ sql:
102
+ SELECT t1.sid, t1.name, t1.status, t1.city, t2.pid, t2.qty
103
+ FROM suppliers AS t1
104
+ JOIN supplies AS t2 ON t1.sid = t2.sid
105
+ WHERE t1.sid = 'S2'
106
+ AND t2.qty = 300
107
+ # join with an order-by clause at left
108
+ - alf: |-
109
+ join(sort(suppliers, [:sid, :asc]), supplies)
110
+ sql:
111
+ SELECT t1.sid, t1.name, t1.status, t1.city, t2.pid, t2.qty
112
+ FROM suppliers AS t1
113
+ JOIN supplies AS t2 ON t1.sid = t2.sid
114
+ ORDER BY t1.sid ASC
115
+ # join with an order-by clause at right
116
+ - alf: |-
117
+ join(suppliers, sort(supplies, [:qty, :asc]))
118
+ sql:
119
+ SELECT t1.sid, t1.name, t1.status, t1.city, t2.pid, t2.qty
120
+ FROM suppliers AS t1
121
+ JOIN supplies AS t2 ON t1.sid = t2.sid
122
+ ORDER BY t2.qty ASC
123
+ # join with an order-by clause on both sides
124
+ - alf: |-
125
+ join(sort(suppliers, [:sid, :asc]), sort(supplies, [:qty, :asc]))
126
+ sql:
127
+ SELECT t1.sid, t1.name, t1.status, t1.city, t2.pid, t2.qty
128
+ FROM suppliers AS t1
129
+ JOIN supplies AS t2 ON t1.sid = t2.sid
130
+ ORDER BY t1.sid ASC, t2.qty ASC
131
+ # join with a limit/offset clause at left
132
+ - alf: |-
133
+ join(frame(suppliers, [:sid, :asc], 5, 10), supplies)
134
+ sql:
135
+ WITH t3 AS (
136
+ SELECT t1.sid, t1.name, t1.status, t1.city
137
+ FROM suppliers AS t1
138
+ ORDER BY t1.sid ASC
139
+ LIMIT 10 OFFSET 5
140
+ )
141
+ SELECT t3.sid, t3.name, t3.status, t3.city, t2.pid, t2.qty
142
+ FROM t3 AS t3
143
+ JOIN supplies AS t2 ON t3.sid = t2.sid
144
+ # join with a limit/offset clause at right
145
+ - alf: |-
146
+ join(suppliers, frame(supplies, [[:sid, :asc], [:pid, :asc]], 5, 10))
147
+ sql:
148
+ WITH t3 AS (
149
+ SELECT t2.sid, t2.pid, t2.qty
150
+ FROM supplies AS t2
151
+ ORDER BY t2.sid ASC, t2.pid ASC
152
+ LIMIT 10 OFFSET 5
153
+ )
154
+ SELECT t1.sid, t1.name, t1.status, t1.city, t3.pid, t3.qty
155
+ FROM suppliers AS t1
156
+ JOIN t3 AS t3 ON t1.sid = t3.sid
157
+ # join with a limit/offset clause on both sides
158
+ - alf: |-
159
+ join(frame(suppliers, [:sid, :asc], 5, 10),
160
+ frame(supplies, [[:sid, :asc], [:pid, :asc]], 5, 10))
161
+ sql:
162
+ WITH t3 AS (SELECT t1.sid, t1.name, t1.status, t1.city
163
+ FROM suppliers AS t1
164
+ ORDER BY t1.sid ASC
165
+ LIMIT 10 OFFSET 5),
166
+ t4 AS (SELECT t2.sid, t2.pid, t2.qty
167
+ FROM supplies AS t2
168
+ ORDER BY t2.sid ASC, t2.pid ASC
169
+ LIMIT 10 OFFSET 5)
170
+ SELECT t3.sid, t3.name, t3.status, t3.city, t4.pid, t4.qty
171
+ FROM t3 AS t3
172
+ JOIN t4 AS t4 ON t3.sid = t4.sid
173
+ # join on a union at left
174
+ - alf: |-
175
+ join(union(suppliers_in_london, suppliers_in_paris), supplies)
176
+ sql:
177
+ WITH t4 AS (
178
+ (SELECT t1.sid, t1.name, t1.status, t1.city
179
+ FROM suppliers AS t1
180
+ WHERE t1.city = 'London')
181
+ UNION
182
+ (SELECT t2.sid, t2.name, t2.status, t2.city
183
+ FROM suppliers AS t2
184
+ WHERE t2.city = 'Paris')
185
+ )
186
+ SELECT t4.sid, t4.name, t4.status, t4.city, t3.pid, t3.qty
187
+ FROM t4 AS t4
188
+ JOIN supplies AS t3 ON t4.sid = t3.sid
189
+ # join on a union at right
190
+ - alf: |-
191
+ join(supplies, union(suppliers_in_london, suppliers_in_paris))
192
+ sql:
193
+ WITH t4 AS (
194
+ (SELECT t2.sid, t2.name, t2.status, t2.city
195
+ FROM suppliers AS t2
196
+ WHERE t2.city = 'London')
197
+ UNION
198
+ (SELECT t3.sid, t3.name, t3.status, t3.city
199
+ FROM suppliers AS t3
200
+ WHERE t3.city = 'Paris')
201
+ )
202
+ SELECT t1.sid, t1.pid, t1.qty, t4.name, t4.status, t4.city
203
+ FROM supplies AS t1
204
+ JOIN t4 AS t4 ON t1.sid = t4.sid
205
+ # joining from Relation::DEE
206
+ - alf: |-
207
+ join(Relation::DEE, suppliers)