alf-sequel 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 (45) hide show
  1. data/Gemfile +10 -7
  2. data/Gemfile.lock +26 -14
  3. data/README.md +45 -3
  4. data/lib/alf/sequel.rb +1 -0
  5. data/lib/alf/sequel/cog.rb +23 -75
  6. data/lib/alf/sequel/compiler.rb +8 -198
  7. data/lib/alf/sequel/connection.rb +5 -1
  8. data/lib/alf/sequel/connection/connection_methods.rb +4 -2
  9. data/lib/alf/sequel/connection/schema_methods.rb +2 -6
  10. data/lib/alf/sequel/connection/update_methods.rb +1 -1
  11. data/lib/alf/sequel/loader.rb +1 -0
  12. data/lib/alf/sequel/translator.rb +198 -0
  13. data/lib/alf/sequel/unit_of_work/insert.rb +10 -4
  14. data/lib/alf/sequel/unit_of_work/update.rb +3 -3
  15. data/lib/alf/sequel/version.rb +1 -1
  16. data/spec/connection/test_heading.rb +1 -1
  17. data/spec/{fixtures/sap.db → sap.db} +0 -0
  18. data/spec/spec_helper.rb +48 -6
  19. data/spec/unit_of_work/delete/test_delete.rb +2 -2
  20. data/spec/unit_of_work/insert/test_run.rb +12 -14
  21. data/spec/unit_of_work/update/test_run.rb +1 -1
  22. data/tasks/bench.rake +40 -0
  23. data/tasks/test.rake +3 -3
  24. metadata +29 -29
  25. data/lib/alf/sequel/compiler/predicate.rb +0 -76
  26. data/spec/alf.db +0 -0
  27. data/spec/compiler/test_clip.rb +0 -40
  28. data/spec/compiler/test_compact.rb +0 -18
  29. data/spec/compiler/test_extend.rb +0 -16
  30. data/spec/compiler/test_frame.rb +0 -89
  31. data/spec/compiler/test_intersect.rb +0 -18
  32. data/spec/compiler/test_join.rb +0 -26
  33. data/spec/compiler/test_leaf_operand.rb +0 -24
  34. data/spec/compiler/test_matching.rb +0 -34
  35. data/spec/compiler/test_not_matching.rb +0 -34
  36. data/spec/compiler/test_page.rb +0 -86
  37. data/spec/compiler/test_predicate.rb +0 -141
  38. data/spec/compiler/test_project.rb +0 -64
  39. data/spec/compiler/test_rename.rb +0 -26
  40. data/spec/compiler/test_restrict.rb +0 -48
  41. data/spec/compiler/test_sort.rb +0 -18
  42. data/spec/compiler/test_union.rb +0 -18
  43. data/spec/compiler_helper.rb +0 -34
  44. data/spec/fixtures/sap.rb +0 -43
  45. data/tasks/fixtures.rake +0 -12
data/Gemfile CHANGED
@@ -1,8 +1,15 @@
1
1
  source 'http://rubygems.org'
2
2
 
3
+ group :development do
4
+ gem "rake", "~> 10.1"
5
+ gem "rspec", "~> 2.14"
6
+ end
7
+
3
8
  group :runtime do
4
- gem "alf-core", "~> 0.14.0"
5
- gem "sequel", "~> 3.48"
9
+ gem "sequel", "~> 4.2"
10
+
11
+ gem "alf-core", path: "../alf-core"
12
+ gem "alf-sql", path: "../alf-sql"
6
13
  end
7
14
 
8
15
  group :test do
@@ -10,9 +17,5 @@ group :test do
10
17
  gem "rspec", "~> 2.14"
11
18
  gem "sqlite3", "~> 1.3", :platforms => ['mri', 'rbx']
12
19
  gem "jdbc-sqlite3", "~> 3.7", :platforms => ['jruby']
13
- end
14
-
15
- group :development do
16
- gem "rake", "~> 10.1"
17
- gem "rspec", "~> 2.14"
20
+ gem "pg"
18
21
  end
data/Gemfile.lock CHANGED
@@ -1,38 +1,50 @@
1
- GEM
2
- remote: http://rubygems.org/
1
+ PATH
2
+ remote: ../alf-core
3
3
  specs:
4
- alf-core (0.14.0)
4
+ alf-core (0.15.0)
5
5
  domain (~> 1.0)
6
6
  myrrha (~> 3.0)
7
7
  path (~> 1.3)
8
- sexpr (~> 0.5.1)
8
+ sexpr (~> 0.6.0)
9
+
10
+ PATH
11
+ remote: ../alf-sql
12
+ specs:
13
+ alf-sql (0.15.0)
14
+ alf-core (~> 0.15.0)
15
+ sexpr (~> 0.6.0)
16
+
17
+ GEM
18
+ remote: http://rubygems.org/
19
+ specs:
9
20
  diff-lcs (1.2.4)
10
21
  domain (1.0.0)
11
- jdbc-sqlite3 (3.7.2.1)
12
22
  myrrha (3.0.0)
13
23
  domain (~> 1.0)
14
24
  path (1.3.3)
25
+ pg (0.17.0)
15
26
  rake (10.1.0)
16
27
  rspec (2.14.1)
17
28
  rspec-core (~> 2.14.0)
18
29
  rspec-expectations (~> 2.14.0)
19
30
  rspec-mocks (~> 2.14.0)
20
- rspec-core (2.14.4)
21
- rspec-expectations (2.14.0)
31
+ rspec-core (2.14.7)
32
+ rspec-expectations (2.14.3)
22
33
  diff-lcs (>= 1.1.3, < 2.0)
23
- rspec-mocks (2.14.1)
24
- sequel (3.48.0)
25
- sexpr (0.5.1)
26
- sqlite3 (1.3.7)
34
+ rspec-mocks (2.14.4)
35
+ sequel (4.3.0)
36
+ sexpr (0.6.0)
37
+ sqlite3 (1.3.8)
27
38
 
28
39
  PLATFORMS
29
- java
30
40
  ruby
31
41
 
32
42
  DEPENDENCIES
33
- alf-core (~> 0.14.0)
43
+ alf-core!
44
+ alf-sql!
34
45
  jdbc-sqlite3 (~> 3.7)
46
+ pg
35
47
  rake (~> 10.1)
36
48
  rspec (~> 2.14)
37
- sequel (~> 3.48)
49
+ sequel (~> 4.2)
38
50
  sqlite3 (~> 1.3)
data/README.md CHANGED
@@ -3,9 +3,51 @@
3
3
  [![Build Status](https://secure.travis-ci.org/alf-tool/alf-sequel.png)](http://travis-ci.org/alf-tool/alf-sequel)
4
4
  [![Dependency Status](https://gemnasium.com/alf-tool/alf-sequel.png)](https://gemnasium.com/alf-tool/alf-sequel)
5
5
 
6
- A sequel adapter for alf
6
+ A DBMS adapter for Alf built atop [Sequel](http://sequel.rubyforge.org/).
7
7
 
8
8
  ## Links
9
9
 
10
- http://github.com/alf-tool/alf
11
- http://github.com/alf-tool/alf-sequel
10
+ * http://github.com/alf-tool/alf
11
+ * http://github.com/alf-tool/alf-sql
12
+ * http://github.com/alf-tool/alf-sequel
13
+
14
+ ## Synopsis
15
+
16
+ Extends [alf-sql](https://github.com/alf-tool/alf-sql#synopsis) in such a way
17
+ that most existing SQL databases can be used with Alf thanks to the awesome
18
+ [Sequel](http://sequel.rubyforge.org/) library.
19
+
20
+ ## Example
21
+
22
+ ```ruby
23
+ require 'alf-sequel'
24
+
25
+ Alf.connect("sap.db") do |conn|
26
+
27
+ # Send a query and puts the result
28
+ # (See Alf main docs for smarter ways of using query results)
29
+ relation = conn.query{
30
+ restrict(suppliers, city: 'London')
31
+ }
32
+
33
+ puts relation
34
+ # => +------+-------+---------+--------+
35
+ # | :sid | :name | :status | :city |
36
+ # +------+-------+---------+--------+
37
+ # | S1 | Smith | 20 | London |
38
+ # | S4 | Clark | 20 | London |
39
+ # +------+-------+---------+--------+
40
+
41
+ # alf-sequel overrides `Operand#to_sql` in such a way that the resulting
42
+ # SQL code is handled by Sequel itself and thus valid for the DBMS
43
+ # considered
44
+ sql = conn.parse{
45
+ restrict(suppliers, city: 'London')
46
+ }.to_sql
47
+
48
+ puts sql
49
+ # => SELECT `t1`.`sid`, `t1`.`name`, `t1`.`status`, `t1`.`city`
50
+ # FROM `suppliers` AS 't1'
51
+ # WHERE (`t1`.`city` = 'London')
52
+ end
53
+ ```
data/lib/alf/sequel.rb CHANGED
@@ -2,6 +2,7 @@ require_relative 'sequel/version'
2
2
  require_relative 'sequel/loader'
3
3
  require_relative 'sequel/adapter'
4
4
  require_relative 'sequel/connection'
5
+ require_relative 'sequel/translator'
5
6
  require_relative 'sequel/compiler'
6
7
  require_relative 'sequel/cog'
7
8
  require_relative 'sequel/unit_of_work'
@@ -1,97 +1,45 @@
1
1
  module Alf
2
2
  module Sequel
3
3
  class Cog
4
- include Engine::Cog
4
+ include Alf::Compiler::Cog
5
+ include Enumerable
5
6
 
6
- def initialize(expr, connection, opts)
7
- super(expr)
8
- @connection = connection
9
- @opts = opts
7
+ def initialize(expr, compiler, sexpr)
8
+ super(expr, compiler)
9
+ @sexpr = sexpr
10
10
  end
11
- attr_reader :connection, :opts
11
+ attr_reader :sexpr
12
12
 
13
- def as; opts[:as]; end
14
- def dataset; opts[:dataset]; end
15
-
16
- ### Cog
17
-
18
- def to_relation
19
- Relation.coerce(to_a)
20
- end
21
-
22
- def each
23
- dataset.each(&Proc.new)
24
- end
25
-
26
- ### Delegation to Dataset, that is, facade over ::Sequel itself
27
-
28
- def select(expr, attrs)
29
- branch expr, dataset: dataset.select(*qualify(attrs))
30
- end
31
-
32
- def rename(expr, attrs, opts)
33
- branch expr, dataset: dataset.select(*qualify(attrs)).from_self(opts),
34
- as: opts[:alias]
35
- end
36
-
37
- def distinct(expr, *args, &bl)
38
- branch expr, dataset: dataset.distinct(*args, &bl)
13
+ def should_be_reused?
14
+ sexpr.should_be_reused?
39
15
  end
40
16
 
41
- def order(expr, *args, &bl)
42
- branch expr, dataset: dataset.order(*args, &bl)
17
+ def cog_orders
18
+ [ sexpr.ordering ].compact
43
19
  end
44
20
 
45
- def filter(expr, *args, &bl)
46
- branch expr, dataset: dataset.filter(*args, &bl)
21
+ def dataset
22
+ @dataset ||= Translator.new(compiler.connection).call(sexpr)
47
23
  end
48
24
 
49
- def intersect(expr, other, opts={})
50
- branch expr, dataset: dataset.intersect(other.dataset, opts),
51
- as: opts[:alias]
25
+ def to_sql(buffer = "")
26
+ buffer << dataset.sql
27
+ buffer
52
28
  end
53
29
 
54
- def join(expr, other, cols, opts={})
55
- join = dataset.from_self.inner_join(other.dataset, cols, :table_alias => opts[:alias])
56
- branch expr, dataset: join.from_self(opts),
57
- as: opts[:alias]
58
- end
59
-
60
- def union(expr, other, opts={})
61
- branch expr, dataset: dataset.union(other.dataset, opts),
62
- as: opts[:alias]
63
- end
64
-
65
- def limit(expr, *args, &bl)
66
- branch expr, dataset: dataset.limit(*args, &bl)
67
- end
68
-
69
- ### compilation tools
70
-
71
- def sql
72
- dataset.sql
73
- end
74
-
75
- def qualify(attributes)
76
- return attributes unless as
77
- case attributes
78
- when Symbol
79
- ::Sequel.qualify(as, attributes)
80
- when Hash
81
- attributes.map{|k,v| ::Sequel.as(::Sequel.qualify(as, k), v) }
82
- else
83
- attributes.map{|a| ::Sequel.qualify(as, a)}
84
- end
30
+ def to_s
31
+ to_sql
85
32
  end
86
33
 
87
- def branch(expr, opts = {})
88
- Cog.new expr, connection, self.opts.merge(opts)
34
+ def inspect
35
+ "Alf::Sequel::Cog(#{to_sql})"
89
36
  end
90
37
 
91
- def to_s
92
- "Alf::Sequel::Cog|#{sql}"
38
+ def each(&bl)
39
+ return to_enum unless block_given?
40
+ dataset.each(&bl)
93
41
  end
94
42
 
95
- end # class Operand
43
+ end # class Cog
96
44
  end # module Sequel
97
45
  end # module Alf
@@ -1,209 +1,19 @@
1
1
  module Alf
2
2
  module Sequel
3
- class Compiler < Algebra::Compiler
3
+ class Compiler < Sql::Compiler
4
4
 
5
- def pass(expr)
6
- rewrite(expr)
5
+ def initialize(connection)
6
+ super()
7
+ @connection = connection
7
8
  end
8
- alias :on_missing :pass
9
+ attr_reader :connection
9
10
 
10
- def next_alias
11
- @as ||= 0
12
- :"t#{@as += 1}"
13
- end
14
-
15
- def on_leaf_operand(expr)
16
- if Algebra::Operand::Named===expr
17
- expr.connection.cog(expr.name, expr, :alias => next_alias)
18
- else
19
- expr.to_cog
20
- end
21
- end
22
-
23
- ### non relational
24
-
25
- alias :on_autonum :pass
26
- alias :on_coerce :pass
27
- alias :on_defaults :pass
28
- alias :on_generator :pass
29
-
30
- def on_clip(expr)
31
- rewrite(expr){|rw|
32
- rw.operand.select(expr, expr.stay_attributes)
33
- }
34
- end
35
-
36
- def on_compact(expr)
37
- rewrite(expr){|rw|
38
- rw.operand.distinct(expr)
39
- }
40
- end
41
-
42
- def on_sort(expr)
43
- rewrite(expr){|rw|
44
- operand = rw.operand
45
- ordering = to_sequel_ordering(operand, expr.ordering)
46
- operand.order(expr, *ordering)
47
- }
48
- end
49
-
50
- ### relational
51
-
52
- alias :on_extend :pass
53
-
54
- def on_frame(expr)
55
- rewrite(expr){|rw|
56
- offset, limit, ordering = expr.offset, expr.limit, expr.total_ordering
57
- operand = rw.operand
58
- ordering = to_sequel_ordering(operand, ordering)
59
- operand.order(expr, *ordering).limit(expr, limit, offset)
60
- }
61
- end
62
-
63
- alias :on_group :pass
64
- alias :on_infer_heading :pass
65
-
66
- def on_intersect(expr)
67
- rewrite(expr){|rw|
68
- rw.left.intersect(expr, rw.right, {:alias => next_alias})
69
- }
70
- end
71
-
72
- def on_join(expr)
73
- rewrite(expr){|rw|
74
- rw.left.join(expr, rw.right, expr.common_attributes.to_a, {:alias => next_alias})
75
- }
76
- end
77
-
78
- def on_matching(expr)
79
- rewrite(expr) do |rw|
80
- rw.left.filter(expr, matching2filter(expr, rw))
81
- end
82
- end
83
-
84
- def on_not_matching(expr)
85
- rewrite(expr) do |rw|
86
- rw.left.filter(expr, ~matching2filter(expr, rw))
87
- end
88
- end
89
-
90
- def matching2filter(expr, rw)
91
- commons = expr.common_attributes.to_a
92
- if commons.size==1
93
- # (NOT) IN (SELECT ...)
94
- pred = ::Alf::Predicate.in(commons.first, rw.right.select(expr, commons).dataset)
95
- Predicate.new(:qualifier => rw.left.as).call(pred)
96
- elsif commons.size==0
97
- # (NOT) EXISTS (SELECT ... no join condition ...)
98
- rw.right.dataset.exists
99
- else
100
- # (NOT) EXISTS (SELECT ...)
101
- filter = Hash[rw.left.qualify(commons).zip(rw.right.qualify(commons))]
102
- filter = ::Sequel.expr filter
103
- filter = rw.right.filter(expr, filter)
104
- filter.dataset.exists
105
- end
106
- end
107
-
108
- alias :on_minus :pass
109
-
110
- def on_page(expr)
111
- rewrite(expr){|rw|
112
- index, size, ordering = expr.page_index, expr.page_size, expr.total_ordering
113
- operand, offset, limit = rw.operand, (index.abs - 1) * size, size
114
- ordering = to_sequel_ordering(operand, index >= 0 ? ordering : ordering.reverse)
115
- operand.order(expr, *ordering).limit(expr, offset , limit)
116
- }
117
- end
118
-
119
- def on_project(expr)
120
- rewrite(expr){|rw|
121
- compiled = rw.operand.select(expr, expr.stay_attributes)
122
- compiled = compiled.distinct(expr) unless key_preserving?(expr){ false }
123
- compiled
124
- }
125
- end
126
-
127
- alias :on_quota :pass
128
- alias :on_rank :pass
129
-
130
- def on_rename(expr)
131
- rewrite(expr){|rw|
132
- rw.operand.rename(expr, expr.complete_renaming.to_hash, :alias => next_alias)
133
- }
134
- end
135
-
136
- def on_restrict(expr)
137
- rewrite(expr){|rw|
138
- filter = Predicate.new(:qualifier => rw.operand.as).call(rw.predicate)
139
- rw.operand.filter(expr, filter)
140
- }
141
- end
142
-
143
- alias :on_summarize :pass
144
- alias :on_ungroup :pass
145
-
146
- def on_union(expr)
147
- rewrite(expr){|rw|
148
- rw.left.union(expr, rw.right, :alias => next_alias)
149
- }
150
- end
151
-
152
- alias :on_unwrap :pass
153
- alias :on_wrap :pass
154
-
155
- private
156
-
157
- def to_sequel_ordering(operand, ordering)
158
- ordering.to_a.map{|(sel,dir)|
159
- raise NotSupportedError if sel.composite?
160
- ::Sequel.send(dir, operand.qualify(sel.outcoerce))
161
- }
162
- end
163
-
164
- def key_preserving?(expr)
165
- expr.key_preserving?
166
- rescue NotSupportedError
167
- block_given? ? yield : raise
168
- end
169
-
170
- private
171
-
172
- def rewrite(expr)
173
- # copy and apply on expr. the result is the same expression, but
174
- # with cogs instead of operands
175
- rewritten = copy_and_apply(expr)
176
-
177
- # Continue compilation process provided all operands are recognized.
178
- if block_given? and rewritten.operands.all?{|op| recognized?(op) }
179
- # Compilation of predicates may throw a pass
180
- catch(:pass){
181
- rewritten = yield(rewritten)
182
- }
183
- end
184
-
185
- # If the result is itself recognized, proceed
186
- # Otherwise, give a change to the upper-stage compiler with a proxy
187
- if recognized?(rewritten)
188
- rewritten
189
- else
190
- operands = rewritten.operands.map{|op|
191
- Algebra::Operand::Proxy.new(op)
192
- }
193
- rewritten = rewritten.with_operands(*operands)
194
- engine.call(rewritten)
195
- end
196
- end
197
-
198
- def recognized?(op)
199
- op.is_a?(Cog)
200
- end
11
+ protected
201
12
 
202
- def engine
203
- @engine ||= Engine::Compiler.new
13
+ def cog_class
14
+ Cog
204
15
  end
205
16
 
206
17
  end # class Compiler
207
18
  end # module Sequel
208
19
  end # module Alf
209
- require_relative 'compiler/predicate'