alf-sequel 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 +10 -7
- data/Gemfile.lock +26 -14
- data/README.md +45 -3
- data/lib/alf/sequel.rb +1 -0
- data/lib/alf/sequel/cog.rb +23 -75
- data/lib/alf/sequel/compiler.rb +8 -198
- data/lib/alf/sequel/connection.rb +5 -1
- data/lib/alf/sequel/connection/connection_methods.rb +4 -2
- data/lib/alf/sequel/connection/schema_methods.rb +2 -6
- data/lib/alf/sequel/connection/update_methods.rb +1 -1
- data/lib/alf/sequel/loader.rb +1 -0
- data/lib/alf/sequel/translator.rb +198 -0
- data/lib/alf/sequel/unit_of_work/insert.rb +10 -4
- data/lib/alf/sequel/unit_of_work/update.rb +3 -3
- data/lib/alf/sequel/version.rb +1 -1
- data/spec/connection/test_heading.rb +1 -1
- data/spec/{fixtures/sap.db → sap.db} +0 -0
- data/spec/spec_helper.rb +48 -6
- data/spec/unit_of_work/delete/test_delete.rb +2 -2
- data/spec/unit_of_work/insert/test_run.rb +12 -14
- data/spec/unit_of_work/update/test_run.rb +1 -1
- data/tasks/bench.rake +40 -0
- data/tasks/test.rake +3 -3
- metadata +29 -29
- data/lib/alf/sequel/compiler/predicate.rb +0 -76
- data/spec/alf.db +0 -0
- data/spec/compiler/test_clip.rb +0 -40
- data/spec/compiler/test_compact.rb +0 -18
- data/spec/compiler/test_extend.rb +0 -16
- data/spec/compiler/test_frame.rb +0 -89
- data/spec/compiler/test_intersect.rb +0 -18
- data/spec/compiler/test_join.rb +0 -26
- data/spec/compiler/test_leaf_operand.rb +0 -24
- data/spec/compiler/test_matching.rb +0 -34
- data/spec/compiler/test_not_matching.rb +0 -34
- data/spec/compiler/test_page.rb +0 -86
- data/spec/compiler/test_predicate.rb +0 -141
- data/spec/compiler/test_project.rb +0 -64
- data/spec/compiler/test_rename.rb +0 -26
- data/spec/compiler/test_restrict.rb +0 -48
- data/spec/compiler/test_sort.rb +0 -18
- data/spec/compiler/test_union.rb +0 -18
- data/spec/compiler_helper.rb +0 -34
- data/spec/fixtures/sap.rb +0 -43
- 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 "
|
5
|
-
|
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
|
-
|
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
|
-
|
2
|
-
remote:
|
1
|
+
PATH
|
2
|
+
remote: ../alf-core
|
3
3
|
specs:
|
4
|
-
alf-core (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.
|
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.
|
21
|
-
rspec-expectations (2.14.
|
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.
|
24
|
-
sequel (3.
|
25
|
-
sexpr (0.
|
26
|
-
sqlite3 (1.3.
|
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
|
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 (~>
|
49
|
+
sequel (~> 4.2)
|
38
50
|
sqlite3 (~> 1.3)
|
data/README.md
CHANGED
@@ -3,9 +3,51 @@
|
|
3
3
|
[](http://travis-ci.org/alf-tool/alf-sequel)
|
4
4
|
[](https://gemnasium.com/alf-tool/alf-sequel)
|
5
5
|
|
6
|
-
A
|
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-
|
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'
|
data/lib/alf/sequel/cog.rb
CHANGED
@@ -1,97 +1,45 @@
|
|
1
1
|
module Alf
|
2
2
|
module Sequel
|
3
3
|
class Cog
|
4
|
-
include
|
4
|
+
include Alf::Compiler::Cog
|
5
|
+
include Enumerable
|
5
6
|
|
6
|
-
def initialize(expr,
|
7
|
-
super(expr)
|
8
|
-
@
|
9
|
-
@opts = opts
|
7
|
+
def initialize(expr, compiler, sexpr)
|
8
|
+
super(expr, compiler)
|
9
|
+
@sexpr = sexpr
|
10
10
|
end
|
11
|
-
attr_reader :
|
11
|
+
attr_reader :sexpr
|
12
12
|
|
13
|
-
def
|
14
|
-
|
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
|
42
|
-
|
17
|
+
def cog_orders
|
18
|
+
[ sexpr.ordering ].compact
|
43
19
|
end
|
44
20
|
|
45
|
-
def
|
46
|
-
|
21
|
+
def dataset
|
22
|
+
@dataset ||= Translator.new(compiler.connection).call(sexpr)
|
47
23
|
end
|
48
24
|
|
49
|
-
def
|
50
|
-
|
51
|
-
|
25
|
+
def to_sql(buffer = "")
|
26
|
+
buffer << dataset.sql
|
27
|
+
buffer
|
52
28
|
end
|
53
29
|
|
54
|
-
def
|
55
|
-
|
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
|
88
|
-
Cog
|
34
|
+
def inspect
|
35
|
+
"Alf::Sequel::Cog(#{to_sql})"
|
89
36
|
end
|
90
37
|
|
91
|
-
def
|
92
|
-
|
38
|
+
def each(&bl)
|
39
|
+
return to_enum unless block_given?
|
40
|
+
dataset.each(&bl)
|
93
41
|
end
|
94
42
|
|
95
|
-
end # class
|
43
|
+
end # class Cog
|
96
44
|
end # module Sequel
|
97
45
|
end # module Alf
|
data/lib/alf/sequel/compiler.rb
CHANGED
@@ -1,209 +1,19 @@
|
|
1
1
|
module Alf
|
2
2
|
module Sequel
|
3
|
-
class Compiler <
|
3
|
+
class Compiler < Sql::Compiler
|
4
4
|
|
5
|
-
def
|
6
|
-
|
5
|
+
def initialize(connection)
|
6
|
+
super()
|
7
|
+
@connection = connection
|
7
8
|
end
|
8
|
-
|
9
|
+
attr_reader :connection
|
9
10
|
|
10
|
-
|
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
|
203
|
-
|
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'
|