alf-sequel 0.13.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +4 -0
- data/Gemfile +19 -0
- data/Gemfile.lock +38 -0
- data/LICENCE.md +22 -0
- data/Manifest.txt +12 -0
- data/README.md +11 -0
- data/Rakefile +11 -0
- data/lib/alf/sequel/adapter.rb +57 -0
- data/lib/alf/sequel/cog.rb +92 -0
- data/lib/alf/sequel/compiler/predicate.rb +76 -0
- data/lib/alf/sequel/compiler.rb +168 -0
- data/lib/alf/sequel/connection/connection_methods.rb +46 -0
- data/lib/alf/sequel/connection/schema_methods.rb +76 -0
- data/lib/alf/sequel/connection/update_methods.rb +63 -0
- data/lib/alf/sequel/connection.rb +23 -0
- data/lib/alf/sequel/loader.rb +2 -0
- data/lib/alf/sequel/unit_of_work/atomic.rb +35 -0
- data/lib/alf/sequel/unit_of_work/delete.rb +24 -0
- data/lib/alf/sequel/unit_of_work/insert.rb +60 -0
- data/lib/alf/sequel/unit_of_work/update.rb +60 -0
- data/lib/alf/sequel/unit_of_work.rb +11 -0
- data/lib/alf/sequel/version.rb +16 -0
- data/lib/alf/sequel.rb +7 -0
- data/lib/alf-sequel.rb +1 -0
- data/spec/adapter/test_recognize.rb +50 -0
- data/spec/alf.db +0 -0
- data/spec/compiler/test_clip.rb +40 -0
- data/spec/compiler/test_compact.rb +18 -0
- data/spec/compiler/test_extend.rb +16 -0
- data/spec/compiler/test_intersect.rb +18 -0
- data/spec/compiler/test_join.rb +18 -0
- data/spec/compiler/test_leaf_operand.rb +24 -0
- data/spec/compiler/test_matching.rb +34 -0
- data/spec/compiler/test_not_matching.rb +34 -0
- data/spec/compiler/test_predicate.rb +141 -0
- data/spec/compiler/test_project.rb +64 -0
- data/spec/compiler/test_rename.rb +26 -0
- data/spec/compiler/test_restrict.rb +48 -0
- data/spec/compiler/test_sort.rb +18 -0
- data/spec/compiler/test_union.rb +18 -0
- data/spec/compiler_helper.rb +34 -0
- data/spec/connection/test_connection_uri.rb +54 -0
- data/spec/connection/test_delete.rb +20 -0
- data/spec/connection/test_heading.rb +16 -0
- data/spec/connection/test_insert.rb +24 -0
- data/spec/connection/test_keys.rb +24 -0
- data/spec/connection/test_lock.rb +15 -0
- data/spec/connection/test_ping.rb +26 -0
- data/spec/connection/test_relvar.rb +16 -0
- data/spec/connection/test_update.rb +20 -0
- data/spec/fixtures/sap.db +0 -0
- data/spec/fixtures/sap.rb +43 -0
- data/spec/spec_helper.rb +35 -0
- data/spec/test_assumptions.rb +14 -0
- data/spec/test_sequel.rb +10 -0
- data/spec/unit_of_work/atomic/test_run.rb +72 -0
- data/spec/unit_of_work/delete/test_delete.rb +39 -0
- data/spec/unit_of_work/insert/test_run.rb +71 -0
- data/spec/unit_of_work/update/test_run.rb +39 -0
- data/tasks/fixtures.rake +12 -0
- data/tasks/gem.rake +8 -0
- data/tasks/test.rake +6 -0
- metadata +174 -0
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
source 'http://rubygems.org'
|
2
|
+
|
3
|
+
group :runtime do
|
4
|
+
#gem "alf-core", :git => "git://github.com/alf-tool/alf-core.git"
|
5
|
+
gem "alf-core", "~> 0.13.0"
|
6
|
+
gem "sequel", "~> 3.48"
|
7
|
+
end
|
8
|
+
|
9
|
+
group :test do
|
10
|
+
gem "rake", "~> 10.1"
|
11
|
+
gem "rspec", "~> 2.14"
|
12
|
+
gem "sqlite3", "~> 1.3", :platforms => ['mri', 'rbx']
|
13
|
+
gem "jdbc-sqlite3", "~> 3.7", :platforms => ['jruby']
|
14
|
+
end
|
15
|
+
|
16
|
+
group :development do
|
17
|
+
gem "rake", "~> 10.1"
|
18
|
+
gem "rspec", "~> 2.14"
|
19
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
alf-core (0.13.0)
|
5
|
+
domain (~> 1.0)
|
6
|
+
myrrha (~> 3.0)
|
7
|
+
path (~> 1.3)
|
8
|
+
sexpr (~> 0.5.1)
|
9
|
+
diff-lcs (1.2.4)
|
10
|
+
domain (1.0.0)
|
11
|
+
jdbc-sqlite3 (3.7.2.1)
|
12
|
+
myrrha (3.0.0)
|
13
|
+
domain (~> 1.0)
|
14
|
+
path (1.3.1)
|
15
|
+
rake (10.1.0)
|
16
|
+
rspec (2.14.1)
|
17
|
+
rspec-core (~> 2.14.0)
|
18
|
+
rspec-expectations (~> 2.14.0)
|
19
|
+
rspec-mocks (~> 2.14.0)
|
20
|
+
rspec-core (2.14.4)
|
21
|
+
rspec-expectations (2.14.0)
|
22
|
+
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)
|
27
|
+
|
28
|
+
PLATFORMS
|
29
|
+
java
|
30
|
+
ruby
|
31
|
+
|
32
|
+
DEPENDENCIES
|
33
|
+
alf-core (~> 0.13.0)
|
34
|
+
jdbc-sqlite3 (~> 3.7)
|
35
|
+
rake (~> 10.1)
|
36
|
+
rspec (~> 2.14)
|
37
|
+
sequel (~> 3.48)
|
38
|
+
sqlite3 (~> 1.3)
|
data/LICENCE.md
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# The MIT Licence
|
2
|
+
|
3
|
+
Copyright (c) 2012 - Bernard Lambeau
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Manifest.txt
ADDED
data/README.md
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
# Alf::Sequel
|
2
|
+
|
3
|
+
[![Build Status](https://secure.travis-ci.org/alf-tool/alf-sequel.png)](http://travis-ci.org/alf-tool/alf-sequel)
|
4
|
+
[![Dependency Status](https://gemnasium.com/alf-tool/alf-sequel.png)](https://gemnasium.com/alf-tool/alf-sequel)
|
5
|
+
|
6
|
+
A sequel adapter for alf
|
7
|
+
|
8
|
+
## Links
|
9
|
+
|
10
|
+
http://github.com/alf-tool/alf
|
11
|
+
http://github.com/alf-tool/alf-sequel
|
data/Rakefile
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
module Alf
|
2
|
+
module Sequel
|
3
|
+
class Adapter < Alf::Adapter
|
4
|
+
|
5
|
+
class << self
|
6
|
+
|
7
|
+
def recognizes?(conn_spec)
|
8
|
+
case conn_spec
|
9
|
+
when ::Sequel::Database
|
10
|
+
true
|
11
|
+
when String
|
12
|
+
require 'uri'
|
13
|
+
uri = URI::parse(conn_spec) rescue nil
|
14
|
+
(uri && uri.scheme) or looks_a_sqlite_file?(conn_spec)
|
15
|
+
when Hash
|
16
|
+
conn_spec = Tools.symbolize_keys(conn_spec)
|
17
|
+
conn_spec[:adapter] && conn_spec[:database]
|
18
|
+
else
|
19
|
+
looks_a_sqlite_file?(conn_spec)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Returns true if `f` looks like a sqlite file
|
24
|
+
def looks_a_sqlite_file?(f)
|
25
|
+
return false unless Path.like?(f)
|
26
|
+
path = Path(f)
|
27
|
+
path.parent.directory? and ['db', 'sqlite', 'sqlite3'].include?(path.ext)
|
28
|
+
end
|
29
|
+
|
30
|
+
def sqlite_protocol
|
31
|
+
defined?(JRUBY_VERSION) ? "jdbc:sqlite" : "sqlite"
|
32
|
+
end
|
33
|
+
|
34
|
+
def sequel_db(conn_spec)
|
35
|
+
require 'sequel' unless defined?(::Sequel)
|
36
|
+
case conn_spec
|
37
|
+
when ::Sequel::Database
|
38
|
+
conn_spec
|
39
|
+
when String, Path
|
40
|
+
conn_spec = "#{sqlite_protocol}://#{conn_spec}" if looks_a_sqlite_file?(conn_spec)
|
41
|
+
::Sequel.connect(conn_spec)
|
42
|
+
else
|
43
|
+
raise ArgumentError, "Unrecognized connection specification `#{conn_spec.inspect}`"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
end # class << self
|
48
|
+
|
49
|
+
# Returns a low-level connection on this adapter
|
50
|
+
def connection
|
51
|
+
Connection.new(conn_spec)
|
52
|
+
end
|
53
|
+
|
54
|
+
::Alf::Adapter.register(:sequel, self)
|
55
|
+
end # class Adapter
|
56
|
+
end # module Sequel
|
57
|
+
end # module Alf
|
@@ -0,0 +1,92 @@
|
|
1
|
+
module Alf
|
2
|
+
module Sequel
|
3
|
+
class Cog
|
4
|
+
include Engine::Cog
|
5
|
+
|
6
|
+
def initialize(connection, opts)
|
7
|
+
@connection = connection
|
8
|
+
@opts = opts
|
9
|
+
end
|
10
|
+
attr_reader :connection, :opts
|
11
|
+
|
12
|
+
def as; opts[:as]; end
|
13
|
+
def dataset; opts[:dataset]; end
|
14
|
+
|
15
|
+
### Cog
|
16
|
+
|
17
|
+
def to_relation
|
18
|
+
Relation.coerce(to_a)
|
19
|
+
end
|
20
|
+
|
21
|
+
def each
|
22
|
+
dataset.each(&Proc.new)
|
23
|
+
end
|
24
|
+
|
25
|
+
### Delegation to Dataset, that is, facade over ::Sequel itself
|
26
|
+
|
27
|
+
def select(attrs)
|
28
|
+
branch dataset: dataset.select(*qualify(attrs))
|
29
|
+
end
|
30
|
+
|
31
|
+
def rename(attrs, opts)
|
32
|
+
branch dataset: dataset.select(*qualify(attrs)).from_self(opts),
|
33
|
+
as: opts[:alias]
|
34
|
+
end
|
35
|
+
|
36
|
+
def distinct(*args, &bl)
|
37
|
+
branch dataset: dataset.distinct(*args, &bl)
|
38
|
+
end
|
39
|
+
|
40
|
+
def order(*args, &bl)
|
41
|
+
branch dataset: dataset.order(*args, &bl)
|
42
|
+
end
|
43
|
+
|
44
|
+
def filter(*args, &bl)
|
45
|
+
branch dataset: dataset.filter(*args, &bl)
|
46
|
+
end
|
47
|
+
|
48
|
+
def intersect(other, opts={})
|
49
|
+
branch dataset: dataset.intersect(other.dataset, opts),
|
50
|
+
as: opts[:alias]
|
51
|
+
end
|
52
|
+
|
53
|
+
def join(other, cols, opts={})
|
54
|
+
join = dataset.inner_join(other.dataset, cols, :table_alias => opts[:alias])
|
55
|
+
branch dataset: join.from_self(opts),
|
56
|
+
as: opts[:alias]
|
57
|
+
end
|
58
|
+
|
59
|
+
def union(other, opts={})
|
60
|
+
branch dataset: dataset.union(other.dataset, opts),
|
61
|
+
as: opts[:alias]
|
62
|
+
end
|
63
|
+
|
64
|
+
### compilation tools
|
65
|
+
|
66
|
+
def sql
|
67
|
+
dataset.sql
|
68
|
+
end
|
69
|
+
|
70
|
+
def qualify(attributes)
|
71
|
+
return attributes unless as
|
72
|
+
case attributes
|
73
|
+
when Symbol
|
74
|
+
::Sequel.qualify(as, attributes)
|
75
|
+
when Hash
|
76
|
+
attributes.map{|k,v| ::Sequel.as(::Sequel.qualify(as, k), v) }
|
77
|
+
else
|
78
|
+
attributes.map{|a| ::Sequel.qualify(as, a)}
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def branch(opts = {})
|
83
|
+
Cog.new connection, self.opts.merge(opts)
|
84
|
+
end
|
85
|
+
|
86
|
+
def to_s
|
87
|
+
"Alf::Sequel::Cog|#{sql}"
|
88
|
+
end
|
89
|
+
|
90
|
+
end # class Operand
|
91
|
+
end # module Sequel
|
92
|
+
end # module Alf
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module Alf
|
2
|
+
module Sequel
|
3
|
+
class Compiler
|
4
|
+
class Predicate < Sexpr::Processor
|
5
|
+
|
6
|
+
def call(predicate)
|
7
|
+
super(predicate.expr)
|
8
|
+
end
|
9
|
+
|
10
|
+
def on_missing(sexpr)
|
11
|
+
throw :pass
|
12
|
+
end
|
13
|
+
alias :on_native :on_missing
|
14
|
+
|
15
|
+
def on_tautology(sexpr)
|
16
|
+
::Sequel::SQL::BooleanConstant.new(true)
|
17
|
+
end
|
18
|
+
|
19
|
+
def on_contradiction(sexpr)
|
20
|
+
::Sequel::SQL::BooleanConstant.new(false)
|
21
|
+
end
|
22
|
+
|
23
|
+
def on_var_ref(sexpr)
|
24
|
+
if qualifier = options[:qualifier]
|
25
|
+
::Sequel.qualify(qualifier, sexpr.last)
|
26
|
+
else
|
27
|
+
::Sequel.expr(sexpr.last)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def on_literal(sexpr)
|
32
|
+
sexpr.last.nil? ? nil : ::Sequel.expr(sexpr.last)
|
33
|
+
end
|
34
|
+
|
35
|
+
def on_eq(sexpr)
|
36
|
+
left, right = apply(sexpr.left), apply(sexpr.right)
|
37
|
+
::Sequel.expr(left => right)
|
38
|
+
end
|
39
|
+
|
40
|
+
def on_neq(sexpr)
|
41
|
+
left, right = apply(sexpr.left), apply(sexpr.right)
|
42
|
+
~::Sequel.expr(left => right)
|
43
|
+
end
|
44
|
+
|
45
|
+
def on_dyadic_comp(sexpr)
|
46
|
+
left, right = apply(sexpr.left), apply(sexpr.right)
|
47
|
+
left.send(sexpr.operator_symbol, right)
|
48
|
+
end
|
49
|
+
alias :on_lt :on_dyadic_comp
|
50
|
+
alias :on_lte :on_dyadic_comp
|
51
|
+
alias :on_gt :on_dyadic_comp
|
52
|
+
alias :on_gte :on_dyadic_comp
|
53
|
+
|
54
|
+
def on_in(sexpr)
|
55
|
+
left = apply(sexpr.var_ref)
|
56
|
+
::Sequel.expr(left => sexpr.values)
|
57
|
+
end
|
58
|
+
|
59
|
+
def on_not(sexpr)
|
60
|
+
~apply(sexpr.last)
|
61
|
+
end
|
62
|
+
|
63
|
+
def on_and(sexpr)
|
64
|
+
body = sexpr.sexpr_body
|
65
|
+
body[1..-1].inject(apply(body.first)){|f,t| f & apply(t) }
|
66
|
+
end
|
67
|
+
|
68
|
+
def on_or(sexpr)
|
69
|
+
body = sexpr.sexpr_body
|
70
|
+
body[1..-1].inject(apply(body.first)){|f,t| f | apply(t) }
|
71
|
+
end
|
72
|
+
|
73
|
+
end # class Predicate
|
74
|
+
end # class Compiler
|
75
|
+
end # module Sequel
|
76
|
+
end # module Alf
|
@@ -0,0 +1,168 @@
|
|
1
|
+
module Alf
|
2
|
+
module Sequel
|
3
|
+
class Compiler < Algebra::Compiler
|
4
|
+
|
5
|
+
def pass(expr)
|
6
|
+
rewrite(expr)
|
7
|
+
end
|
8
|
+
alias :on_missing :pass
|
9
|
+
|
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, :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.stay_attributes)
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
def on_compact(expr)
|
37
|
+
rewrite(expr){|rw|
|
38
|
+
rw.operand.distinct
|
39
|
+
}
|
40
|
+
end
|
41
|
+
|
42
|
+
def on_sort(expr)
|
43
|
+
rewrite(expr){|rw|
|
44
|
+
operand = rw.operand
|
45
|
+
ordering = expr.ordering.to_a.map{|(col,dir)|
|
46
|
+
::Sequel.send(dir, operand.qualify(col))
|
47
|
+
}
|
48
|
+
operand.order(*ordering)
|
49
|
+
}
|
50
|
+
end
|
51
|
+
|
52
|
+
### relational
|
53
|
+
|
54
|
+
alias :on_extend :pass
|
55
|
+
alias :on_group :pass
|
56
|
+
alias :on_infer_heading :pass
|
57
|
+
|
58
|
+
def on_intersect(expr)
|
59
|
+
rewrite(expr){|rw|
|
60
|
+
rw.left.intersect(rw.right, :alias => next_alias)
|
61
|
+
}
|
62
|
+
end
|
63
|
+
|
64
|
+
def on_join(expr)
|
65
|
+
rewrite(expr){|rw|
|
66
|
+
rw.left.join(rw.right, expr.common_attributes.to_a, :alias => next_alias)
|
67
|
+
}
|
68
|
+
end
|
69
|
+
|
70
|
+
def on_matching(expr)
|
71
|
+
rewrite(expr) do |rw|
|
72
|
+
rw.left.filter(matching2filter(expr, rw))
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def on_not_matching(expr)
|
77
|
+
rewrite(expr) do |rw|
|
78
|
+
rw.left.filter(~matching2filter(expr, rw))
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def matching2filter(expr, rw)
|
83
|
+
commons = expr.common_attributes.to_a
|
84
|
+
if commons.size==1
|
85
|
+
# (NOT) IN (SELECT ...)
|
86
|
+
pred = ::Alf::Predicate.in(commons.first, rw.right.select(commons).dataset)
|
87
|
+
Predicate.new(:qualifier => rw.left.as).call(pred)
|
88
|
+
elsif commons.size==0
|
89
|
+
# (NOT) EXISTS (SELECT ... no join condition ...)
|
90
|
+
rw.right.dataset.exists
|
91
|
+
else
|
92
|
+
# (NOT) EXISTS (SELECT ...)
|
93
|
+
filter = Hash[rw.left.qualify(commons).zip(rw.right.qualify(commons))]
|
94
|
+
filter = ::Sequel.expr filter
|
95
|
+
filter = rw.right.filter(filter)
|
96
|
+
filter.dataset.exists
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
alias :on_minus :pass
|
101
|
+
|
102
|
+
def on_project(expr)
|
103
|
+
rewrite(expr){|rw|
|
104
|
+
compiled = rw.operand.select(expr.stay_attributes)
|
105
|
+
compiled = compiled.distinct unless key_preserving?(expr){ false }
|
106
|
+
compiled
|
107
|
+
}
|
108
|
+
end
|
109
|
+
|
110
|
+
alias :on_quota :pass
|
111
|
+
alias :on_rank :pass
|
112
|
+
|
113
|
+
def on_rename(expr)
|
114
|
+
rewrite(expr){|rw|
|
115
|
+
rw.operand.rename(expr.complete_renaming.to_hash, :alias => next_alias)
|
116
|
+
}
|
117
|
+
end
|
118
|
+
|
119
|
+
def on_restrict(expr)
|
120
|
+
rewrite(expr){|rw|
|
121
|
+
filter = Predicate.new(:qualifier => rw.operand.as).call(rw.predicate)
|
122
|
+
rw.operand.filter(filter)
|
123
|
+
}
|
124
|
+
end
|
125
|
+
|
126
|
+
alias :on_summarize :pass
|
127
|
+
alias :on_ungroup :pass
|
128
|
+
|
129
|
+
def on_union(expr)
|
130
|
+
rewrite(expr){|rw|
|
131
|
+
rw.left.union(rw.right, :alias => next_alias)
|
132
|
+
}
|
133
|
+
end
|
134
|
+
|
135
|
+
alias :on_unwrap :pass
|
136
|
+
alias :on_wrap :pass
|
137
|
+
|
138
|
+
private
|
139
|
+
|
140
|
+
def key_preserving?(expr)
|
141
|
+
expr.key_preserving?
|
142
|
+
rescue NotSupportedError
|
143
|
+
block_given? ? yield : raise
|
144
|
+
end
|
145
|
+
|
146
|
+
private
|
147
|
+
|
148
|
+
def rewrite(expr)
|
149
|
+
rewrited = copy_and_apply(expr)
|
150
|
+
if block_given? and rewrited.operands.all?{|op| recognized?(op) }
|
151
|
+
catch(:pass){ rewrited = yield(rewrited) }
|
152
|
+
end
|
153
|
+
rewrited = engine.call(rewrited) unless recognized?(rewrited)
|
154
|
+
rewrited
|
155
|
+
end
|
156
|
+
|
157
|
+
def recognized?(op)
|
158
|
+
op.is_a?(Cog)
|
159
|
+
end
|
160
|
+
|
161
|
+
def engine
|
162
|
+
@engine ||= Engine::Compiler.new
|
163
|
+
end
|
164
|
+
|
165
|
+
end # class Compiler
|
166
|
+
end # module Sequel
|
167
|
+
end # module Alf
|
168
|
+
require_relative 'compiler/predicate'
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Alf
|
2
|
+
module Sequel
|
3
|
+
class Connection
|
4
|
+
module ConnectionMethods
|
5
|
+
|
6
|
+
def connection_uri(with_password = false)
|
7
|
+
if conn_spec.is_a?(Hash)
|
8
|
+
cs = Tuple(self.conn_spec)
|
9
|
+
adapter, host, port, database, user, password = cs.values_at(:adapter, :host, :port, :database, :user, :password)
|
10
|
+
user = "#{user}:#{password}" if user and password and with_password
|
11
|
+
user = "#{user}@" if user
|
12
|
+
host = "localhost" if host.nil? and not(adapter =~ /sqlite/)
|
13
|
+
host = "#{host}:#{port}" if host and port
|
14
|
+
host = "/#{user}#{host}" if host or user
|
15
|
+
"#{adapter}:/#{host}/#{database}"
|
16
|
+
elsif conn_spec.is_a?(String)
|
17
|
+
conn_spec
|
18
|
+
else
|
19
|
+
"Alf::Sequel::Connection(#{conn_spec})"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def ping
|
24
|
+
sequel_db.test_connection
|
25
|
+
end
|
26
|
+
|
27
|
+
def close
|
28
|
+
@sequel_db.disconnect if @sequel_db && @sequel_db != conn_spec
|
29
|
+
end
|
30
|
+
|
31
|
+
def with_sequel_db
|
32
|
+
yield(sequel_db)
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
# Yields a Sequel::Database object
|
38
|
+
def sequel_db
|
39
|
+
@sequel_db ||= Adapter.sequel_db(conn_spec)
|
40
|
+
block_given? ? yield(@sequel_db) : @sequel_db
|
41
|
+
end
|
42
|
+
|
43
|
+
end # module ConnectionMethods
|
44
|
+
end # module Connection
|
45
|
+
end # module Sequel
|
46
|
+
end # module Alf
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module Alf
|
2
|
+
module Sequel
|
3
|
+
class Connection
|
4
|
+
module SchemaMethods
|
5
|
+
|
6
|
+
def knows?(name)
|
7
|
+
sequel_db.table_exists?(name)
|
8
|
+
end
|
9
|
+
|
10
|
+
def dataset(name)
|
11
|
+
sequel_db[name]
|
12
|
+
end
|
13
|
+
|
14
|
+
def cog(name, opts={})
|
15
|
+
if as = opts[:alias]
|
16
|
+
Cog.new(self, dataset: dataset(:"#{name}___#{as}"), as: as)
|
17
|
+
else
|
18
|
+
Cog.new(self, dataset: dataset(name))
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def heading(name)
|
23
|
+
h = {}
|
24
|
+
sequel_db.schema(name).each do |pair|
|
25
|
+
column_name, info = pair
|
26
|
+
h[column_name] = dbtype_to_ruby_type(info)
|
27
|
+
end
|
28
|
+
Heading.new(h)
|
29
|
+
end
|
30
|
+
|
31
|
+
def keys(name)
|
32
|
+
# take the indexes
|
33
|
+
indexes = sequel_db.indexes(name).
|
34
|
+
values.
|
35
|
+
select{|i| i[:unique] == true }.
|
36
|
+
map{|i| AttrList.coerce(i[:columns]) }.
|
37
|
+
sort{|a1, a2| a1.size <=> a2.size}
|
38
|
+
|
39
|
+
# take single keys as well
|
40
|
+
key = sequel_db.schema(name).
|
41
|
+
select{|(colname, colinfo)| colinfo[:primary_key] }.
|
42
|
+
map(&:first)
|
43
|
+
indexes.unshift(AttrList.coerce(key)) unless key.empty?
|
44
|
+
|
45
|
+
Keys.new(indexes)
|
46
|
+
end
|
47
|
+
|
48
|
+
def migrate!(opts)
|
49
|
+
unless f = opts.migrations_folder
|
50
|
+
raise Alf::ConfigError, "Migrations folder not set"
|
51
|
+
end
|
52
|
+
with_sequel_db do |db|
|
53
|
+
::Sequel.extension(:migration)
|
54
|
+
::Sequel::Migrator.apply(db, f)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def dbtype_to_ruby_type(info)
|
61
|
+
return Object unless info[:type]
|
62
|
+
begin
|
63
|
+
Kernel.eval(info[:type].to_s.capitalize)
|
64
|
+
rescue NameError
|
65
|
+
case info[:type]
|
66
|
+
when :datetime then DateTime
|
67
|
+
when :boolean then Alf::Boolean
|
68
|
+
else Object
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
end # module SchemaMethods
|
74
|
+
end # module Connection
|
75
|
+
end # module Sequel
|
76
|
+
end # module Alf
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Alf
|
2
|
+
module Sequel
|
3
|
+
class Connection
|
4
|
+
module UpdateMethods
|
5
|
+
|
6
|
+
# Yields the block in a transaction
|
7
|
+
def in_transaction(&bl)
|
8
|
+
sequel_db.transaction(&bl)
|
9
|
+
end
|
10
|
+
|
11
|
+
def lock(name, mode, &bl)
|
12
|
+
with_dataset(name){|ds|
|
13
|
+
if ds.respond_to?(:lock)
|
14
|
+
ds.lock(mode.to_s.upcase, &bl)
|
15
|
+
else
|
16
|
+
yield
|
17
|
+
end
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
# Inserts `tuples` in the relvar called `name`
|
22
|
+
def insert(name, tuples)
|
23
|
+
insert_uow(name, tuples).run
|
24
|
+
end
|
25
|
+
|
26
|
+
# Delete from the relvar called `name`
|
27
|
+
def delete(name, predicate)
|
28
|
+
delete_uow(name, predicate).run
|
29
|
+
end
|
30
|
+
|
31
|
+
# Updates the relvar called `name`
|
32
|
+
def update(name, updating, predicate)
|
33
|
+
update_uow(name, updating, predicate).run
|
34
|
+
end
|
35
|
+
|
36
|
+
public # should be private
|
37
|
+
|
38
|
+
def with_dataset(name, predicate = nil)
|
39
|
+
ds = name.is_a?(Symbol) ? sequel_db[name] : name
|
40
|
+
if predicate && !predicate.tautology?
|
41
|
+
ds = ds.filter(Compiler::Predicate.call(predicate))
|
42
|
+
end
|
43
|
+
yield(ds) if block_given?
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def insert_uow(name, tuples)
|
49
|
+
UnitOfWork::Insert.new(self, name, tuples)
|
50
|
+
end
|
51
|
+
|
52
|
+
def delete_uow(name, predicate)
|
53
|
+
UnitOfWork::Delete.new(self, name, predicate)
|
54
|
+
end
|
55
|
+
|
56
|
+
def update_uow(name, updating, predicate)
|
57
|
+
UnitOfWork::Update.new(self, name, updating, predicate)
|
58
|
+
end
|
59
|
+
|
60
|
+
end # module UpdateMethods
|
61
|
+
end # module Connection
|
62
|
+
end # module Sequel
|
63
|
+
end # module Alf
|