alf 0.14.0 → 0.15.0
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.
- 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')
|