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
@@ -28,12 +28,14 @@ module Alf
|
|
28
28
|
@sequel_db.disconnect if @sequel_db && @sequel_db != conn_spec
|
29
29
|
end
|
30
30
|
|
31
|
+
def close!
|
32
|
+
@sequel_db.disconnect if @sequel_db
|
33
|
+
end
|
34
|
+
|
31
35
|
def with_sequel_db
|
32
36
|
yield(sequel_db)
|
33
37
|
end
|
34
38
|
|
35
|
-
private
|
36
|
-
|
37
39
|
# Yields a Sequel::Database object
|
38
40
|
def sequel_db
|
39
41
|
@sequel_db ||= Adapter.sequel_db(conn_spec)
|
@@ -11,12 +11,8 @@ module Alf
|
|
11
11
|
sequel_db[name]
|
12
12
|
end
|
13
13
|
|
14
|
-
def cog(
|
15
|
-
|
16
|
-
Cog.new(expr, self, dataset: dataset(:"#{name}___#{as}"), as: as)
|
17
|
-
else
|
18
|
-
Cog.new(expr, self, dataset: dataset(name))
|
19
|
-
end
|
14
|
+
def cog(plan, expr)
|
15
|
+
plan.join(compiler).on_leaf_operand(plan, expr)
|
20
16
|
end
|
21
17
|
|
22
18
|
def heading(name)
|
@@ -38,7 +38,7 @@ module Alf
|
|
38
38
|
def with_dataset(name, predicate = nil)
|
39
39
|
ds = name.is_a?(Symbol) ? sequel_db[name] : name
|
40
40
|
if predicate && !predicate.tautology?
|
41
|
-
ds = ds.
|
41
|
+
ds = ds.where(Translator.new(sequel_db).call(predicate.sexpr))
|
42
42
|
end
|
43
43
|
yield(ds) if block_given?
|
44
44
|
end
|
data/lib/alf/sequel/loader.rb
CHANGED
@@ -0,0 +1,198 @@
|
|
1
|
+
module Alf
|
2
|
+
module Sequel
|
3
|
+
class Translator < Sexpr::Processor
|
4
|
+
|
5
|
+
def initialize(connection)
|
6
|
+
@connection = connection
|
7
|
+
end
|
8
|
+
attr_reader :connection
|
9
|
+
|
10
|
+
def on_with_exp(sexpr)
|
11
|
+
if sequel_db.select(1).supports_cte?
|
12
|
+
dataset = apply(sexpr.select_exp)
|
13
|
+
apply(sexpr.with_spec).each_pair do |name,subquery|
|
14
|
+
dataset = dataset.with(name, subquery)
|
15
|
+
end
|
16
|
+
dataset
|
17
|
+
else
|
18
|
+
apply(Sql::Processor::Flatten.new(Sql::Builder.new).call(sexpr))
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def on_with_spec(sexpr)
|
23
|
+
sexpr.each_with_object({}){|child,hash|
|
24
|
+
next if child == :with_spec
|
25
|
+
hash[apply(child.table_name)] = apply(child.subquery)
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
def on_set_operator(sexpr)
|
30
|
+
left, right = apply(sexpr.left), apply(sexpr.right)
|
31
|
+
left.send(sexpr.first, right, all: sexpr.all?, from_self: false)
|
32
|
+
end
|
33
|
+
alias :on_union :on_set_operator
|
34
|
+
alias :on_intersect :on_set_operator
|
35
|
+
alias :on_except :on_set_operator
|
36
|
+
|
37
|
+
def on_select_exp(sexpr)
|
38
|
+
dataset = sequel_db.select(1)
|
39
|
+
dataset = dataset(apply(sexpr.from_clause)) if sexpr.from_clause
|
40
|
+
#
|
41
|
+
selection = apply(sexpr.select_list)
|
42
|
+
predicate = apply(sexpr.predicate) if sexpr.predicate
|
43
|
+
order = apply(sexpr.order_by_clause) if sexpr.order_by_clause
|
44
|
+
limit = apply(sexpr.limit_clause) if sexpr.limit_clause
|
45
|
+
offset = apply(sexpr.offset_clause) if sexpr.offset_clause
|
46
|
+
#
|
47
|
+
dataset = dataset.select(*selection)
|
48
|
+
dataset = dataset.distinct if sexpr.distinct?
|
49
|
+
dataset = dataset.where(predicate) if predicate
|
50
|
+
dataset = dataset.order_by(*order) if order
|
51
|
+
dataset = dataset.limit(limit, offset) if limit or offset
|
52
|
+
dataset
|
53
|
+
end
|
54
|
+
|
55
|
+
def on_select_list(sexpr)
|
56
|
+
sexpr.sexpr_body.map{|c| apply(c) }
|
57
|
+
end
|
58
|
+
|
59
|
+
def on_select_star(sexpr)
|
60
|
+
::Sequel.lit('*')
|
61
|
+
end
|
62
|
+
|
63
|
+
def on_select_item(sexpr)
|
64
|
+
::Sequel.as(apply(sexpr.left), apply(sexpr.right))
|
65
|
+
end
|
66
|
+
|
67
|
+
def on_qualified_name(sexpr)
|
68
|
+
apply(sexpr.last).qualify(sexpr.qualifier)
|
69
|
+
end
|
70
|
+
|
71
|
+
def on_column_name(sexpr)
|
72
|
+
::Sequel.expr(sexpr.last.to_sym)
|
73
|
+
end
|
74
|
+
|
75
|
+
def on_from_clause(sexpr)
|
76
|
+
apply(sexpr.table_spec)
|
77
|
+
end
|
78
|
+
|
79
|
+
def on_table_name(sexpr)
|
80
|
+
::Sequel.identifier(sexpr.last)
|
81
|
+
end
|
82
|
+
|
83
|
+
def on_cross_join(sexpr)
|
84
|
+
left, right = apply(sexpr.left), apply(sexpr.right)
|
85
|
+
dataset(left).cross_join(right)
|
86
|
+
end
|
87
|
+
|
88
|
+
def on_inner_join(sexpr)
|
89
|
+
left, right = apply(sexpr.left), apply(sexpr.right)
|
90
|
+
options = {qualify: false, table_alias: false}
|
91
|
+
dataset(left).join_table(:inner, right, nil, options){|*args|
|
92
|
+
apply(sexpr.predicate)
|
93
|
+
}
|
94
|
+
end
|
95
|
+
|
96
|
+
def on_table_as(sexpr)
|
97
|
+
::Sequel.as(sexpr.table_name.to_sym, sexpr.as_name)
|
98
|
+
end
|
99
|
+
|
100
|
+
def on_subquery_as(sexpr)
|
101
|
+
::Sequel.as(apply(sexpr.subquery), sexpr.as_name)
|
102
|
+
end
|
103
|
+
|
104
|
+
def on_order_by_clause(sexpr)
|
105
|
+
sexpr.sexpr_body.map{|c| apply(c)}
|
106
|
+
end
|
107
|
+
|
108
|
+
def on_order_by_term(sexpr)
|
109
|
+
::Sequel.send(sexpr.direction, apply(sexpr.qualified_name))
|
110
|
+
end
|
111
|
+
|
112
|
+
def on_limit_clause(sexpr)
|
113
|
+
sexpr.last
|
114
|
+
end
|
115
|
+
|
116
|
+
def on_offset_clause(sexpr)
|
117
|
+
sexpr.last
|
118
|
+
end
|
119
|
+
|
120
|
+
### Predicate
|
121
|
+
|
122
|
+
def on_identifier(sexpr)
|
123
|
+
::Sequel.identifier(sexpr.last)
|
124
|
+
end
|
125
|
+
|
126
|
+
def on_qualified_identifier(sexpr)
|
127
|
+
::Sequel.as(sexpr.qualifier, sexpr.name)
|
128
|
+
end
|
129
|
+
|
130
|
+
def on_tautology(sexpr)
|
131
|
+
::Sequel::SQL::BooleanConstant.new(true)
|
132
|
+
end
|
133
|
+
|
134
|
+
def on_contradiction(sexpr)
|
135
|
+
::Sequel::SQL::BooleanConstant.new(false)
|
136
|
+
end
|
137
|
+
|
138
|
+
def on_literal(sexpr)
|
139
|
+
sexpr.last.nil? ? nil : ::Sequel.expr(sexpr.last)
|
140
|
+
end
|
141
|
+
|
142
|
+
def on_eq(sexpr)
|
143
|
+
left, right = apply(sexpr.left), apply(sexpr.right)
|
144
|
+
::Sequel.expr(left => right)
|
145
|
+
end
|
146
|
+
|
147
|
+
def on_neq(sexpr)
|
148
|
+
left, right = apply(sexpr.left), apply(sexpr.right)
|
149
|
+
~::Sequel.expr(left => right)
|
150
|
+
end
|
151
|
+
|
152
|
+
def on_dyadic_comp(sexpr)
|
153
|
+
left, right = apply(sexpr.left), apply(sexpr.right)
|
154
|
+
left.send(sexpr.operator_symbol, right)
|
155
|
+
end
|
156
|
+
alias :on_lt :on_dyadic_comp
|
157
|
+
alias :on_lte :on_dyadic_comp
|
158
|
+
alias :on_gt :on_dyadic_comp
|
159
|
+
alias :on_gte :on_dyadic_comp
|
160
|
+
|
161
|
+
def on_in(sexpr)
|
162
|
+
left, right = apply(sexpr.identifier), sexpr.last
|
163
|
+
right = apply(right) if sexpr.subquery?
|
164
|
+
::Sequel.expr(left => right)
|
165
|
+
end
|
166
|
+
|
167
|
+
def on_exists(sexpr)
|
168
|
+
apply(sexpr.last).exists
|
169
|
+
end
|
170
|
+
|
171
|
+
def on_not(sexpr)
|
172
|
+
~apply(sexpr.last)
|
173
|
+
end
|
174
|
+
|
175
|
+
def on_and(sexpr)
|
176
|
+
body = sexpr.sexpr_body
|
177
|
+
body[1..-1].inject(apply(body.first)){|f,t| f & apply(t) }
|
178
|
+
end
|
179
|
+
|
180
|
+
def on_or(sexpr)
|
181
|
+
body = sexpr.sexpr_body
|
182
|
+
body[1..-1].inject(apply(body.first)){|f,t| f | apply(t) }
|
183
|
+
end
|
184
|
+
|
185
|
+
private
|
186
|
+
|
187
|
+
def sequel_db
|
188
|
+
connection.sequel_db
|
189
|
+
end
|
190
|
+
|
191
|
+
def dataset(expr)
|
192
|
+
return expr if ::Sequel::Dataset===expr
|
193
|
+
sequel_db[expr]
|
194
|
+
end
|
195
|
+
|
196
|
+
end # class Translator
|
197
|
+
end # module Sequel
|
198
|
+
end # module Alf
|
@@ -39,13 +39,19 @@ module Alf
|
|
39
39
|
def _run
|
40
40
|
connection.with_dataset(@relvar_name) do |d|
|
41
41
|
bk = best_candidate_key
|
42
|
-
|
42
|
+
supported = d.supports_returning?(:insert)
|
43
|
+
|
44
|
+
if supported and bk and bk.size == 1
|
43
45
|
pk_field_name = bk.to_a.first
|
44
|
-
supported = d.supports_returning?(:insert)
|
45
46
|
d = d.returning(pk_field_name) if supported
|
46
47
|
@insert_result = @inserted.map{|t|
|
47
|
-
|
48
|
-
|
48
|
+
d.insert(t.to_hash).first
|
49
|
+
}
|
50
|
+
elsif bk
|
51
|
+
pk_field_name = bk.to_a.first
|
52
|
+
@insert_result = @inserted.map{|t|
|
53
|
+
result = d.insert(t.to_hash)
|
54
|
+
bk.project_tuple(t) || { pk_field_name => result }
|
49
55
|
}
|
50
56
|
else
|
51
57
|
@inserted.each{|t| d.insert(t.to_hash) }
|
@@ -39,9 +39,9 @@ module Alf
|
|
39
39
|
mr
|
40
40
|
else
|
41
41
|
filter = mr.tuple_extract.to_hash
|
42
|
-
tuples = connection.
|
43
|
-
.filter(
|
44
|
-
.select(
|
42
|
+
tuples = connection.dataset(@relvar_name)
|
43
|
+
.filter(filter)
|
44
|
+
.select(pkey.to_a)
|
45
45
|
Relation(tuples)
|
46
46
|
end
|
47
47
|
end
|
data/lib/alf/sequel/version.rb
CHANGED
@@ -6,7 +6,7 @@ module Alf
|
|
6
6
|
subject{ sap.heading(:suppliers) }
|
7
7
|
|
8
8
|
let(:expected){
|
9
|
-
Heading[:sid =>
|
9
|
+
Heading[:sid => String, :name => String, :status => Integer, :city => String]
|
10
10
|
}
|
11
11
|
|
12
12
|
it{ should eq(expected) }
|
Binary file
|
data/spec/spec_helper.rb
CHANGED
@@ -2,12 +2,12 @@ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
|
2
2
|
require 'alf-sequel'
|
3
3
|
require "rspec"
|
4
4
|
require 'path'
|
5
|
-
|
5
|
+
require 'logger'
|
6
6
|
|
7
7
|
module Helpers
|
8
8
|
|
9
9
|
def sequel_database_path
|
10
|
-
Path.dir/'
|
10
|
+
Path.dir/'sap.db'
|
11
11
|
end
|
12
12
|
|
13
13
|
def sequel_database_uri
|
@@ -15,15 +15,58 @@ module Helpers
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def sequel_database_memory
|
18
|
-
"#{Alf::Sequel::Adapter.sqlite_protocol}
|
18
|
+
"#{Alf::Sequel::Adapter.sqlite_protocol}:memory"
|
19
19
|
end
|
20
20
|
|
21
21
|
def sap
|
22
|
-
|
22
|
+
sequel_db = ::Sequel.connect(sequel_database_uri)
|
23
|
+
Alf.connect(sequel_db, schema_cache: false)
|
23
24
|
end
|
24
25
|
|
25
26
|
def sap_memory
|
26
|
-
|
27
|
+
sequel_db = ::Sequel.connect(sequel_database_memory)
|
28
|
+
sequel_db.create_table(:suppliers) do
|
29
|
+
String :sid
|
30
|
+
String :name
|
31
|
+
Integer :status
|
32
|
+
String :city
|
33
|
+
primary_key [:sid]
|
34
|
+
index :name, :unique => true
|
35
|
+
end
|
36
|
+
sequel_db.create_table(:parts) do
|
37
|
+
String :pid
|
38
|
+
String :name
|
39
|
+
String :color
|
40
|
+
Float :weight
|
41
|
+
String :city
|
42
|
+
primary_key [:pid]
|
43
|
+
end
|
44
|
+
sequel_db.create_table(:supplies) do
|
45
|
+
foreign_key :sid, :suppliers, :null=>false, :key=>[:sid], :deferrable=>true
|
46
|
+
foreign_key :pid, :parts, :null=>false, :key=>[:pid], :deferrable=>true
|
47
|
+
Integer :qty
|
48
|
+
primary_key [:sid, :pid]
|
49
|
+
end
|
50
|
+
[
|
51
|
+
{:sid => 'S1', :name => 'Smith', :status => 20, :city => 'London'},
|
52
|
+
{:sid => 'S2', :name => 'Jones', :status => 10, :city => 'Paris'},
|
53
|
+
{:sid => 'S3', :name => 'Blake', :status => 30, :city => 'Paris'},
|
54
|
+
{:sid => 'S4', :name => 'Clark', :status => 20, :city => 'London'},
|
55
|
+
{:sid => 'S5', :name => 'Adams', :status => 30, :city => 'Athens'}
|
56
|
+
].each do |tuple|
|
57
|
+
sequel_db[:suppliers].insert(tuple)
|
58
|
+
end
|
59
|
+
[
|
60
|
+
{:pid => 'P1', :name => 'Nut', :color => 'Red', :weight => 12.0, :city => 'London'},
|
61
|
+
{:pid => 'P2', :name => 'Bolt', :color => 'Green', :weight => 17.0, :city => 'Paris'},
|
62
|
+
{:pid => 'P3', :name => 'Screw', :color => 'Blue', :weight => 17.0, :city => 'Oslo'},
|
63
|
+
{:pid => 'P4', :name => 'Screw', :color => 'Red', :weight => 14.0, :city => 'London'},
|
64
|
+
{:pid => 'P5', :name => 'Cam', :color => 'Blue', :weight => 12.0, :city => 'Paris'},
|
65
|
+
{:pid => 'P6', :name => 'Cog', :color => 'Red', :weight => 19.0, :city => 'London'}
|
66
|
+
].each do |tuple|
|
67
|
+
sequel_db[:parts].insert(tuple)
|
68
|
+
end
|
69
|
+
Alf.connect(sequel_db, schema_cache: false)
|
27
70
|
end
|
28
71
|
|
29
72
|
end
|
@@ -31,5 +74,4 @@ end
|
|
31
74
|
RSpec.configure do |c|
|
32
75
|
c.include Helpers
|
33
76
|
c.extend Helpers
|
34
|
-
c.filter_run_excluding :ruby19 => (RUBY_VERSION < "1.9")
|
35
77
|
end
|
@@ -25,11 +25,11 @@ module Alf
|
|
25
25
|
|
26
26
|
context 'when predicate is not a tautology' do
|
27
27
|
let(:relvar_name){ :suppliers }
|
28
|
-
let(:predicate){ Predicate.eq(sid:
|
28
|
+
let(:predicate){ Predicate.eq(sid: "S1") }
|
29
29
|
|
30
30
|
it 'removes only targetted tuples' do
|
31
31
|
conn.dataset(relvar_name).should_not be_empty
|
32
|
-
conn.dataset(relvar_name).where(sid:
|
32
|
+
conn.dataset(relvar_name).where(sid: "S1").should be_empty
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
@@ -18,30 +18,30 @@ module Alf
|
|
18
18
|
let(:relvar_name){ :suppliers }
|
19
19
|
|
20
20
|
context 'with only one tuple' do
|
21
|
-
let(:tuples){ [{sid:
|
21
|
+
let(:tuples){ [{sid: 'S10', name: "Marcus", city: "Ouagadougou", status: 55}] }
|
22
22
|
|
23
23
|
it "inserts the tuple" do
|
24
|
-
conn.dataset(:suppliers).where(sid:
|
24
|
+
conn.dataset(:suppliers).where(sid: "S10").should_not be_empty
|
25
25
|
end
|
26
26
|
|
27
27
|
it 'keeps information about inserted tuples' do
|
28
|
-
uow.matching_relation.should eq(Relation sid:
|
28
|
+
uow.matching_relation.should eq(Relation sid: 'S10')
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
32
|
context 'with multiple tuples' do
|
33
33
|
let(:tuples){ [
|
34
|
-
{sid:
|
35
|
-
{sid:
|
34
|
+
{sid: "S10", name: "Marcus", city: "Ouagadougou", status: 55},
|
35
|
+
{sid: "S11", name: "Demete", city: "Albertville", status: 56}
|
36
36
|
]}
|
37
37
|
|
38
38
|
it "inserts the tuples" do
|
39
|
-
conn.dataset(:suppliers).where(sid:
|
40
|
-
conn.dataset(:suppliers).where(sid:
|
39
|
+
conn.dataset(:suppliers).where(sid: "S10").should_not be_empty
|
40
|
+
conn.dataset(:suppliers).where(sid: "S11").should_not be_empty
|
41
41
|
end
|
42
42
|
|
43
43
|
it 'keeps information about inserted tuples' do
|
44
|
-
uow.matching_relation.should eq(Relation sid: [
|
44
|
+
uow.matching_relation.should eq(Relation sid: ["S10", "S11"])
|
45
45
|
end
|
46
46
|
end
|
47
47
|
end
|
@@ -50,18 +50,16 @@ module Alf
|
|
50
50
|
let(:relvar_name){ :supplies }
|
51
51
|
|
52
52
|
let(:tuples){ [
|
53
|
-
{sid:
|
54
|
-
{sid:
|
53
|
+
{sid: "S5", pid: "P1"},
|
54
|
+
{sid: "S5", pid: "P2"}
|
55
55
|
]}
|
56
56
|
|
57
57
|
it "inserts the tuples" do
|
58
|
-
conn.dataset(:supplies).where(sid:
|
58
|
+
conn.dataset(:supplies).where(sid: "S5").to_a.size.should eq(2)
|
59
59
|
end
|
60
60
|
|
61
61
|
it 'keeps information about inserted tuples' do
|
62
|
-
|
63
|
-
uow.matching_relation.should eq(Relation(tuples))
|
64
|
-
}
|
62
|
+
uow.matching_relation.should eq(Relation(tuples))
|
65
63
|
end
|
66
64
|
end
|
67
65
|
|