alf 0.14.0 → 0.15.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +6 -3
- data/Gemfile.lock +65 -19
- data/README.md +77 -183
- data/Rakefile +25 -0
- data/alf.gemspec +5 -3
- data/alf.noespec +8 -6
- data/lib/alf/loader.rb +1 -0
- data/lib/alf/version.rb +1 -1
- data/spec/facade/test_query.rb +12 -0
- data/spec/key-inference/queries.yml +10 -0
- data/spec/key-inference/test_all.rb +21 -0
- data/{test → spec}/migrations/test_folder_migration.rb +0 -0
- data/{test → spec}/migrations/test_sequel_migration.rb +1 -1
- data/spec/operators/ungroup/grouped.json +3 -0
- data/spec/operators/ungroup/test_on_json_content.rb +11 -0
- data/spec/operators/unwrap/test_on_json_content.rb +11 -0
- data/spec/operators/unwrap/wrapped.json +3 -0
- data/spec/optimizer/project/extend.yml +20 -0
- data/spec/optimizer/project/intersect.yml +10 -0
- data/spec/optimizer/project/join.yml +20 -0
- data/spec/optimizer/project/matching.yml +21 -0
- data/spec/optimizer/project/minus.yml +9 -0
- data/spec/optimizer/project/not_matching.yml +21 -0
- data/spec/optimizer/project/project.yml +88 -0
- data/spec/optimizer/project/rename.yml +30 -0
- data/spec/optimizer/project/sort.yml +45 -0
- data/spec/optimizer/project/union.yml +8 -0
- data/spec/optimizer/restrict/clip.yml +4 -0
- data/spec/optimizer/restrict/compact.yml +4 -0
- data/spec/optimizer/restrict/generator.yml +4 -0
- data/spec/optimizer/restrict/intersect.yml +4 -0
- data/spec/optimizer/restrict/leaf_operand.yml +4 -0
- data/spec/optimizer/restrict/minus.yml +4 -0
- data/spec/optimizer/restrict/page.yml +12 -0
- data/spec/optimizer/restrict/project.yml +4 -0
- data/spec/optimizer/restrict/sort.yml +4 -0
- data/spec/optimizer/restrict/union.yml +4 -0
- data/spec/optimizer/test_all.rb +34 -0
- data/spec/sql/helpers.rb +25 -0
- data/spec/sql/queries/01-leaf-operand.yml +5 -0
- data/spec/sql/queries/02-clip.yml +12 -0
- data/spec/sql/queries/03-sort.yml +58 -0
- data/spec/sql/queries/04-frame.yml +57 -0
- data/spec/sql/queries/05-intersect.yml +23 -0
- data/spec/sql/queries/06-join.yml +207 -0
- data/spec/sql/queries/07-matching.yml +76 -0
- data/spec/sql/queries/08-minus.yml +23 -0
- data/spec/sql/queries/09-not-matching.yml +57 -0
- data/spec/sql/queries/10-page.yml +31 -0
- data/spec/sql/queries/11-project.yml +48 -0
- data/spec/sql/queries/12-rename.yml +24 -0
- data/spec/sql/queries/13-restrict.yml +114 -0
- data/spec/sql/queries/15-union.yml +90 -0
- data/spec/sql/queries/16-wrap.yml +3 -0
- data/spec/sql/queries/91-reuse.yml +28 -0
- data/spec/sql/test_sequel_compiler.rb +41 -0
- data/spec/sql/test_sql_compiler.rb +52 -0
- data/{test → spec}/test_alf.rb +0 -0
- data/spec/test_helpers.rb +54 -0
- data/tasks/doc.rake +10 -0
- data/tasks/fixtures.rake +52 -0
- data/tasks/mod.rake +50 -0
- data/tasks/release.rake +34 -0
- data/tasks/test.rake +2 -2
- metadata +150 -19
- data/test/seeding/test_seeding.rb +0 -49
- data/test/test_helpers.rb +0 -24
data/lib/alf/loader.rb
CHANGED
data/lib/alf/version.rb
CHANGED
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'test_helpers'
|
2
|
+
describe Alf, "query" do
|
3
|
+
|
4
|
+
subject{ Alf.query(adapter){ suppliers } }
|
5
|
+
|
6
|
+
it{ should be_a(Relation) }
|
7
|
+
|
8
|
+
it 'should have expected heading' do
|
9
|
+
subject.heading.should eq(Heading(sid: String, name: String, status: Integer, city: String))
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'test_helpers'
|
2
|
+
module Alf
|
3
|
+
describe "Key inference" do
|
4
|
+
|
5
|
+
subject{ expr.keys }
|
6
|
+
|
7
|
+
(Path.dir/"queries.yml").load.each do |query|
|
8
|
+
alf = query['alf']
|
9
|
+
keys = query['keys']
|
10
|
+
|
11
|
+
describe "Key inference on '#{query}'" do
|
12
|
+
let(:expr){ conn.parse(alf) }
|
13
|
+
|
14
|
+
it 'should be as expected' do
|
15
|
+
subject.should eq(Alf::Keys.coerce(keys))
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
File without changes
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# when extension only is kept
|
2
|
+
- alf: |-
|
3
|
+
project(extend(suppliers, a: "A", b: "B"), [:a, :b])
|
4
|
+
optimized: |-
|
5
|
+
project(extend(suppliers, a: "A", b: "B"), [:a, :b])
|
6
|
+
# when extension is not at all touched
|
7
|
+
- alf: |-
|
8
|
+
project(extend(suppliers, a: "A", b: "B"), [:sid, :a, :b])
|
9
|
+
optimized: |-
|
10
|
+
project(extend(suppliers, a: "A", b: "B"), [:sid, :a, :b])
|
11
|
+
# when extension is entirely removed
|
12
|
+
- alf: |-
|
13
|
+
project(extend(suppliers, a: "A", b: "B"), [:sid])
|
14
|
+
optimized: |-
|
15
|
+
project(suppliers, [:sid])
|
16
|
+
# when extension is partially removed
|
17
|
+
- alf: |-
|
18
|
+
project(extend(suppliers, a: "A", b: "B"), [:sid, :a])
|
19
|
+
optimized: |-
|
20
|
+
project(extend(suppliers, a: "A"), [:sid, :a])
|
@@ -0,0 +1,10 @@
|
|
1
|
+
- alf: |-
|
2
|
+
project(intersect(suppliers_in_london, suppliers_in_paris), [:name])
|
3
|
+
optimized: |-
|
4
|
+
project(intersect(suppliers_in_london, suppliers_in_paris), [:name])
|
5
|
+
comment: |-
|
6
|
+
This is NOT intersect(project, project). Indeed, no supplier is both in
|
7
|
+
Paris and in London, so the intersection is empty and hence the projection.
|
8
|
+
In contrast, two suppliers might have the same name and be one in London
|
9
|
+
and the other in Paris. Therefore, projecting first would return those
|
10
|
+
two names and the intersection would not be empty.
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# join preserving all join attributes
|
2
|
+
- alf: |-
|
3
|
+
project(join(suppliers, supplies), [:sid, :name, :pid, :qty])
|
4
|
+
optimized: |-
|
5
|
+
join(project(suppliers, [:sid, :name]), supplies)
|
6
|
+
# join preserving all join attributes (allbut)
|
7
|
+
- alf: |-
|
8
|
+
allbut(join(suppliers, supplies), [:status, :qty])
|
9
|
+
optimized: |-
|
10
|
+
join(allbut(suppliers, [:status]), allbut(supplies, [:qty]))
|
11
|
+
# when not preserving all join attributes
|
12
|
+
- alf: |-
|
13
|
+
project(join(suppliers, supplies), [:name, :qty])
|
14
|
+
optimized: |-
|
15
|
+
project(join(project(suppliers, [:sid, :name]), project(supplies, [:sid, :qty])), [:name, :qty])
|
16
|
+
# when not preserving all join attributes (allbut)
|
17
|
+
- alf: |-
|
18
|
+
allbut(join(suppliers, supplies), [:sid, :pid])
|
19
|
+
optimized: |-
|
20
|
+
allbut(join(suppliers, allbut(supplies, [:pid])), [:sid])
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# when matching attributes are preserved
|
2
|
+
- alf: |-
|
3
|
+
project(matching(suppliers, supplies), [:sid, :name])
|
4
|
+
optimized: |-
|
5
|
+
matching(project(suppliers, [:sid, :name]), supplies)
|
6
|
+
# when matching attributes are preserved (allbut)
|
7
|
+
- alf: |-
|
8
|
+
allbut(matching(suppliers, supplies), [:city])
|
9
|
+
optimized: |-
|
10
|
+
matching(allbut(suppliers, [:city]), supplies)
|
11
|
+
|
12
|
+
# when matching attributes are not preserved
|
13
|
+
- alf: |-
|
14
|
+
project(matching(suppliers, supplies), [:name])
|
15
|
+
optimized: |-
|
16
|
+
project(matching(project(suppliers, [:sid, :name]), supplies), [:name])
|
17
|
+
# when matching attributes are not preserved (allbut)
|
18
|
+
- alf: |-
|
19
|
+
allbut(matching(suppliers, supplies), [:sid, :name])
|
20
|
+
optimized: |-
|
21
|
+
allbut(matching(allbut(suppliers, [:name]), supplies), [:sid])
|
@@ -0,0 +1,9 @@
|
|
1
|
+
- alf: |-
|
2
|
+
project(minus(suppliers_in_london, suppliers_in_paris), [:name])
|
3
|
+
optimized: |-
|
4
|
+
project(minus(suppliers_in_london, suppliers_in_paris), [:name])
|
5
|
+
comment: |-
|
6
|
+
This is NOT minus(project, project). Indeed, as a supplier cannot be both
|
7
|
+
in Paris and in London, the minus simply returns the left operand. In
|
8
|
+
contrast, if we project first then the city information is lost and we
|
9
|
+
could restrict the left operand through common names, which is wrong.
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# when matching attributes are preserved
|
2
|
+
- alf: |-
|
3
|
+
project(not_matching(suppliers, supplies), [:sid, :name])
|
4
|
+
optimized: |-
|
5
|
+
not_matching(project(suppliers, [:sid, :name]), supplies)
|
6
|
+
# when matching attributes are preserved (allbut)
|
7
|
+
- alf: |-
|
8
|
+
allbut(not_matching(suppliers, supplies), [:city])
|
9
|
+
optimized: |-
|
10
|
+
not_matching(allbut(suppliers, [:city]), supplies)
|
11
|
+
|
12
|
+
# when matching attributes are not preserved
|
13
|
+
- alf: |-
|
14
|
+
project(not_matching(suppliers, supplies), [:name])
|
15
|
+
optimized: |-
|
16
|
+
project(not_matching(project(suppliers, [:sid, :name]), supplies), [:name])
|
17
|
+
# when matching attributes are not preserved (allbut)
|
18
|
+
- alf: |-
|
19
|
+
allbut(not_matching(suppliers, supplies), [:sid, :name])
|
20
|
+
optimized: |-
|
21
|
+
allbut(not_matching(allbut(suppliers, [:name]), supplies), [:sid])
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# projection on leaf operand
|
2
|
+
- alf: |-
|
3
|
+
project(suppliers, [:sid])
|
4
|
+
optimized: |-
|
5
|
+
project(suppliers, [:sid])
|
6
|
+
|
7
|
+
# allbut on leaf operand
|
8
|
+
- alf: |-
|
9
|
+
allbut(suppliers, [:sid])
|
10
|
+
optimized: |-
|
11
|
+
allbut(suppliers, [:sid])
|
12
|
+
|
13
|
+
# project(project) on different sets
|
14
|
+
- alf: |-
|
15
|
+
project(project(suppliers, [:sid]), [:name])
|
16
|
+
optimized: |-
|
17
|
+
project(suppliers, [])
|
18
|
+
# project(project) on a subset
|
19
|
+
- alf: |-
|
20
|
+
project(project(suppliers, [:sid, :name]), [:sid])
|
21
|
+
optimized: |-
|
22
|
+
project(suppliers, [:sid])
|
23
|
+
# project(project) on a superset
|
24
|
+
- alf: |-
|
25
|
+
project(project(suppliers, [:sid]), [:sid, :name])
|
26
|
+
optimized: |-
|
27
|
+
project(suppliers, [:sid])
|
28
|
+
|
29
|
+
# allbut(allbut) on different sets
|
30
|
+
- alf: |-
|
31
|
+
allbut(allbut(suppliers, [:sid]), [:name])
|
32
|
+
optimized: |-
|
33
|
+
allbut(suppliers, [:sid, :name])
|
34
|
+
# allbut(allbut) on a subset
|
35
|
+
- alf: |-
|
36
|
+
allbut(allbut(suppliers, [:sid, :name]), [:sid])
|
37
|
+
optimized: |-
|
38
|
+
allbut(suppliers, [:sid, :name])
|
39
|
+
# allbut(allbut) on a superset
|
40
|
+
- alf: |-
|
41
|
+
allbut(allbut(suppliers, [:sid]), [:sid, :name])
|
42
|
+
optimized: |-
|
43
|
+
allbut(suppliers, [:sid, :name])
|
44
|
+
|
45
|
+
# project(allbut(...)) on different sets
|
46
|
+
- alf: |-
|
47
|
+
project(allbut(suppliers, [:name]), [:sid])
|
48
|
+
optimized: |-
|
49
|
+
project(suppliers, [:sid])
|
50
|
+
# project(allbut(...)) on a subset
|
51
|
+
- alf: |-
|
52
|
+
project(allbut(suppliers, [:sid, :name]), [:sid])
|
53
|
+
optimized: |-
|
54
|
+
project(suppliers, [])
|
55
|
+
# project(allbut(...)) on a superset
|
56
|
+
- alf: |-
|
57
|
+
project(allbut(suppliers, [:sid]), [:sid, :name])
|
58
|
+
optimized: |-
|
59
|
+
project(suppliers, [:name])
|
60
|
+
|
61
|
+
# allbut(project(...)) on different sets
|
62
|
+
- alf: |-
|
63
|
+
allbut(project(suppliers, [:name]), [:sid])
|
64
|
+
optimized: |-
|
65
|
+
project(suppliers, [:name])
|
66
|
+
# allbut(project(...)) on a subset
|
67
|
+
- alf: |-
|
68
|
+
allbut(project(suppliers, [:sid, :name]), [:sid])
|
69
|
+
optimized: |-
|
70
|
+
project(suppliers, [:name])
|
71
|
+
# allbut(project(...)) on a superset
|
72
|
+
- alf: |-
|
73
|
+
allbut(project(suppliers, [:sid]), [:sid, :name])
|
74
|
+
optimized: |-
|
75
|
+
project(suppliers, [])
|
76
|
+
|
77
|
+
# project when used inside another otherator
|
78
|
+
- alf: |-
|
79
|
+
rename(project(project(suppliers, [:name, :sid]), [:sid]), sid: :id)
|
80
|
+
optimized: |-
|
81
|
+
rename(project(suppliers, [:sid]), sid: :id)
|
82
|
+
# project when should be applied recursively
|
83
|
+
- alf: |-
|
84
|
+
inside = project(project(suppliers, [:sid, :name, :status]), [:name, :status])
|
85
|
+
project(intersect(inside, suppliers), [:sname])
|
86
|
+
optimized: |-
|
87
|
+
inside = project(suppliers, [:name, :status])
|
88
|
+
project(intersect(inside, suppliers), [:sname])
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# when renaming is preserved
|
2
|
+
- alf: |-
|
3
|
+
project(rename(suppliers, :sid => :id), [:id, :name])
|
4
|
+
optimized: |-
|
5
|
+
rename(project(suppliers, [:sid, :name]), :sid => :id)
|
6
|
+
# when renaming is preserved (allbut)
|
7
|
+
- alf: |-
|
8
|
+
allbut(rename(suppliers, :sid => :id), [:city])
|
9
|
+
optimized: |-
|
10
|
+
rename(allbut(suppliers, [:city]), :sid => :id)
|
11
|
+
# when renaming is projected away
|
12
|
+
- alf: |-
|
13
|
+
project(rename(suppliers, :sid => :id), [:name])
|
14
|
+
optimized: |-
|
15
|
+
project(suppliers, [:name])
|
16
|
+
# when renaming is partially projected away
|
17
|
+
- alf: |-
|
18
|
+
project(rename(suppliers, :sid => :id, :name => :sname), [:sname])
|
19
|
+
optimized: |-
|
20
|
+
rename(project(suppliers, [:name]), :name => :sname)
|
21
|
+
# when renaming is projected away (allbut)
|
22
|
+
- alf: |-
|
23
|
+
allbut(rename(suppliers, :sid => :id), [:id, :name])
|
24
|
+
optimized: |-
|
25
|
+
allbut(suppliers, [:sid, :name])
|
26
|
+
# when renaming is partially projected away (allbut)
|
27
|
+
- alf: |-
|
28
|
+
allbut(rename(suppliers, :sid => :id, :name => :sname), [:id])
|
29
|
+
optimized: |-
|
30
|
+
rename(allbut(suppliers, [:sid]), :name => :sname)
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# when all sort attributes are preserved
|
2
|
+
- alf: |-
|
3
|
+
project(sort(suppliers, [:name, :asc]), [:sid, :name])
|
4
|
+
optimized: |-
|
5
|
+
sort(project(suppliers, [:sid, :name]), [:name, :asc])
|
6
|
+
# when all sort attributes are preserved (allbut)
|
7
|
+
- alf: |-
|
8
|
+
allbut(sort(suppliers, [:name, :asc]), [:city])
|
9
|
+
optimized: |-
|
10
|
+
sort(allbut(suppliers, [:city]), [:name, :asc])
|
11
|
+
# when no sort attribute is preserved
|
12
|
+
- alf: |-
|
13
|
+
project(sort(suppliers, [:name, :asc]), [:sid])
|
14
|
+
optimized: |-
|
15
|
+
project(sort(project(suppliers, [:sid, :name]), [:name, :asc]), [:sid])
|
16
|
+
# when no sort attribute is preserved (allbut)
|
17
|
+
- alf: |-
|
18
|
+
allbut(sort(suppliers, [:name, :asc]), [:name])
|
19
|
+
optimized: |-
|
20
|
+
allbut(sort(suppliers, [:name, :asc]), [:name])
|
21
|
+
# when no sort attribute is preserved (allbut II)
|
22
|
+
- alf: |-
|
23
|
+
allbut(sort(suppliers, [:name, :asc]), [:name, :city])
|
24
|
+
optimized: |-
|
25
|
+
allbut(sort(allbut(suppliers, [:city]), [:name, :asc]), [:name])
|
26
|
+
# when some sort attributes are preserved
|
27
|
+
- alf: |-
|
28
|
+
project(sort(suppliers, [[:name, :asc], [:sid, :asc]]), [:sid])
|
29
|
+
optimized: |-
|
30
|
+
project(sort(project(suppliers, [:sid, :name]), [[:name, :asc], [:sid, :asc]]), [:sid])
|
31
|
+
# when some sort attributes are preserved (II)
|
32
|
+
- alf: |-
|
33
|
+
project(sort(suppliers, [[:name, :asc], [:sid, :asc]]), [:sid, :city])
|
34
|
+
optimized: |-
|
35
|
+
project(sort(project(suppliers, [:sid, :name, :city]), [[:name, :asc], [:sid, :asc]]), [:sid, :city])
|
36
|
+
# when some sort attributes are preserved (allbut)
|
37
|
+
- alf: |-
|
38
|
+
allbut(sort(suppliers, [[:name, :asc], [:sid, :asc]]), [:sid])
|
39
|
+
optimized: |-
|
40
|
+
allbut(sort(suppliers, [[:name, :asc], [:sid, :asc]]), [:sid])
|
41
|
+
# when some sort attributes are preserved (allbut II)
|
42
|
+
- alf: |-
|
43
|
+
allbut(sort(suppliers, [[:name, :asc], [:sid, :asc]]), [:sid, :city])
|
44
|
+
optimized: |-
|
45
|
+
allbut(sort(allbut(suppliers, [:city]), [[:name, :asc], [:sid, :asc]]), [:sid])
|
@@ -0,0 +1,8 @@
|
|
1
|
+
- alf: |-
|
2
|
+
project(union(suppliers_in_london, suppliers_in_paris), [:name])
|
3
|
+
optimized: |-
|
4
|
+
union(project(suppliers_in_london, [:name]), project(suppliers_in_paris, [:name]))
|
5
|
+
- alf: |-
|
6
|
+
allbut(union(suppliers_in_london, suppliers_in_paris), [:name])
|
7
|
+
optimized: |-
|
8
|
+
union(allbut(suppliers_in_london, [:name]), allbut(suppliers_in_paris, [:name]))
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# restrict of a page is not optimizable
|
2
|
+
- alf: |-
|
3
|
+
restrict(page(suppliers, [:name], 1, page_size: 5), city: 'Paris')
|
4
|
+
optimized: |-
|
5
|
+
restrict(page(suppliers, [:name], 1, page_size: 5), city: 'Paris')
|
6
|
+
# ... but the inner operand is
|
7
|
+
- alf: |-
|
8
|
+
inside = restrict(compact(suppliers), city: 'Paris')
|
9
|
+
restrict(page(inside, [:name], 1, page_size: 5), city: 'Paris')
|
10
|
+
optimized: |-
|
11
|
+
inside = compact(restrict(suppliers, city: 'Paris'))
|
12
|
+
restrict(page(inside, [:name], 1, page_size: 5), city: 'Paris')
|