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.
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
data/lib/alf/loader.rb CHANGED
@@ -1,3 +1,4 @@
1
1
  require "alf-core"
2
+ require "alf-sql"
2
3
  require "alf-sequel"
3
4
  require "alf-shell"
data/lib/alf/version.rb CHANGED
@@ -2,7 +2,7 @@ module Alf
2
2
  module Version
3
3
 
4
4
  MAJOR = 0
5
- MINOR = 14
5
+ MINOR = 15
6
6
  TINY = 0
7
7
 
8
8
  def self.to_s
@@ -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,10 @@
1
+ - alf: |-
2
+ suppliers
3
+ keys:
4
+ - [ sid ]
5
+ - [ name ]
6
+ - alf: |-
7
+ matching(suppliers, Relation(city: ['London', 'Paris']))
8
+ keys:
9
+ - [ sid ]
10
+ - [ name ]
@@ -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
@@ -2,7 +2,7 @@ require 'test_helpers'
2
2
  describe "Migrations with a Sequel adapter" do
3
3
 
4
4
  let(:db){
5
- Alf.database(victim.conn_spec, opts)
5
+ Alf.database("#{Alf::Sequel::Adapter.sqlite_protocol}:memory", opts)
6
6
  }
7
7
 
8
8
  context 'when no migration folder is set' do
@@ -0,0 +1,3 @@
1
+ [{
2
+ "suppliers": [ {"sid": "S1"} ]
3
+ }]
@@ -0,0 +1,11 @@
1
+ require 'test_helpers'
2
+ describe Alf::Algebra::Ungroup, "on json content" do
3
+
4
+ it 'works as expected' do
5
+ Alf.connect(Path.dir) do |conn|
6
+ rel = conn.relvar{ ungroup(grouped, :suppliers) }.to_a
7
+ rel.should eq([{sid: "S1"}])
8
+ end
9
+ end
10
+
11
+ end
@@ -0,0 +1,11 @@
1
+ require 'test_helpers'
2
+ describe Alf::Algebra::Unwrap, "on json content" do
3
+
4
+ it 'works as expected' do
5
+ Alf.connect(Path.dir) do |conn|
6
+ rel = conn.relvar{ unwrap(wrapped, :wrapped) }.to_a
7
+ rel.should eq([{sid: "S1"}])
8
+ end
9
+ end
10
+
11
+ end
@@ -0,0 +1,3 @@
1
+ [
2
+ { "wrapped": {"sid": "S1"} }
3
+ ]
@@ -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,4 @@
1
+ - alf: |-
2
+ restrict(clip(suppliers, [:sid, :name]), city: 'Paris')
3
+ optimized: |-
4
+ clip(restrict(suppliers, city: 'Paris'), [:sid, :name])
@@ -0,0 +1,4 @@
1
+ - alf: |-
2
+ restrict(compact(suppliers), city: 'Paris')
3
+ optimized: |-
4
+ compact(restrict(suppliers, city: 'Paris'))
@@ -0,0 +1,4 @@
1
+ - alf: |-
2
+ restrict(generator(10, :auto), auto: 2)
3
+ optimized: |-
4
+ restrict(generator(10, :auto), auto: 2)
@@ -0,0 +1,4 @@
1
+ - alf: |-
2
+ restrict(intersect(suppliers, suppliers), city: 'Paris')
3
+ optimized: |-
4
+ intersect(restrict(suppliers, city: 'Paris'), restrict(suppliers, city: 'Paris'))
@@ -0,0 +1,4 @@
1
+ - alf: |-
2
+ restrict(suppliers, city: 'Paris')
3
+ optimized: |-
4
+ restrict(suppliers, city: 'Paris')
@@ -0,0 +1,4 @@
1
+ - alf: |-
2
+ restrict(minus(suppliers, suppliers), city: 'Paris')
3
+ optimized: |-
4
+ minus(restrict(suppliers, city: 'Paris'), restrict(suppliers, city: 'Paris'))
@@ -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')