bmg 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/bmg/algebra.rb +4 -4
- data/lib/bmg/leaf.rb +4 -0
- data/lib/bmg/operator/allbut.rb +4 -0
- data/lib/bmg/operator/autosummarize.rb +4 -0
- data/lib/bmg/operator/autowrap.rb +5 -0
- data/lib/bmg/operator/constants.rb +27 -3
- data/lib/bmg/operator/extend.rb +4 -0
- data/lib/bmg/operator/image.rb +22 -0
- data/lib/bmg/operator/project.rb +4 -0
- data/lib/bmg/operator/rename.rb +4 -0
- data/lib/bmg/operator/restrict.rb +4 -0
- data/lib/bmg/operator/union.rb +39 -13
- data/lib/bmg/relation.rb +30 -0
- data/lib/bmg/relation/empty.rb +65 -0
- data/lib/bmg/sequel/relation.rb +4 -0
- data/lib/bmg/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5cdd3816b79a6f29c387456d9ffdc12d9ae7872e
|
4
|
+
data.tar.gz: 4c30157082a6474c76a156efc7fcdb510f2f2d44
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 95290af1dba5768f4a4bfee0357d53ddd70063a6fb6a34ee8c6b329805f44ec1ac49ba0fd5baea0b6946d4e81bf3250b30a954f2b1288c53e0eaa4019687e6d8
|
7
|
+
data.tar.gz: 95fb7f28fbaf62b2f12286857bd04ccc0849d397f085bd366f303572e21a01d6baeb023d4226f56b8223182f075f0c455a3ef06aa7433c677fa5390404c4b9d9
|
data/lib/bmg/algebra.rb
CHANGED
@@ -82,12 +82,12 @@ module Bmg
|
|
82
82
|
end
|
83
83
|
protected :_restrict
|
84
84
|
|
85
|
-
def union(other)
|
86
|
-
_union self.type.union(other.type), other
|
85
|
+
def union(other, options = {})
|
86
|
+
_union self.type.union(other.type), other, options
|
87
87
|
end
|
88
88
|
|
89
|
-
def _union(type, other)
|
90
|
-
Operator::Union.new(type, self, other)
|
89
|
+
def _union(type, other, options)
|
90
|
+
Operator::Union.new(type, [self, other], options)
|
91
91
|
end
|
92
92
|
protected :_union
|
93
93
|
|
data/lib/bmg/leaf.rb
CHANGED
data/lib/bmg/operator/allbut.rb
CHANGED
@@ -28,6 +28,7 @@ module Bmg
|
|
28
28
|
def initialize(type, operand, options = {})
|
29
29
|
@type = type
|
30
30
|
@operand = operand
|
31
|
+
@original_options = options
|
31
32
|
@options = DEFAULT_OPTIONS.merge(options)
|
32
33
|
@options[:postprocessor] = NoLeftJoinNoise.new(@options[:postprocessor])
|
33
34
|
end
|
@@ -45,6 +46,10 @@ module Bmg
|
|
45
46
|
end
|
46
47
|
end
|
47
48
|
|
49
|
+
def to_ast
|
50
|
+
[ :autowrap, operand.to_ast, @original_options.dup ]
|
51
|
+
end
|
52
|
+
|
48
53
|
private
|
49
54
|
|
50
55
|
def autowrap(tuple)
|
@@ -29,17 +29,41 @@ module Bmg
|
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
|
+
def to_ast
|
33
|
+
[ :constants, operand.to_ast, constants.dup ]
|
34
|
+
end
|
35
|
+
|
32
36
|
protected ### optimization
|
33
37
|
|
34
38
|
def _restrict(type, predicate)
|
35
39
|
top_p, bottom_p = predicate.and_split(constants.keys)
|
36
|
-
if top_p
|
40
|
+
if top_p.tautology?
|
41
|
+
# push all situation
|
42
|
+
result = operand
|
43
|
+
result = result.restrict(bottom_p) unless bottom_p.tautology?
|
44
|
+
result = result.constants(constants)
|
45
|
+
result
|
46
|
+
elsif (top_p.free_variables - constants.keys).empty?
|
47
|
+
# top_p applies to constants only
|
48
|
+
if eval = top_p.evaluate(constants)
|
49
|
+
result = operand
|
50
|
+
result = result.restrict(bottom_p) unless bottom_p.tautology?
|
51
|
+
result = result.constants(constants)
|
52
|
+
result
|
53
|
+
else
|
54
|
+
Relation.empty(type)
|
55
|
+
end
|
56
|
+
elsif bottom_p.tautology?
|
57
|
+
# push none situation, no optimization possible since top_p
|
58
|
+
# is not a tautology
|
37
59
|
super
|
38
60
|
else
|
61
|
+
# top_p and bottom_p are complex predicates. Let apply each
|
62
|
+
# of them
|
39
63
|
result = operand
|
40
|
-
result = result.restrict(bottom_p)
|
64
|
+
result = result.restrict(bottom_p)
|
41
65
|
result = result.constants(constants)
|
42
|
-
result = result.restrict(top_p)
|
66
|
+
result = result.restrict(top_p)
|
43
67
|
result
|
44
68
|
end
|
45
69
|
rescue Predicate::NotSupportedError
|
data/lib/bmg/operator/extend.rb
CHANGED
data/lib/bmg/operator/image.rb
CHANGED
@@ -50,6 +50,28 @@ module Bmg
|
|
50
50
|
end
|
51
51
|
end
|
52
52
|
|
53
|
+
def to_ast
|
54
|
+
[ :image, left.to_ast, right.to_ast, as, on, options.dup ]
|
55
|
+
end
|
56
|
+
|
57
|
+
protected ### optimization
|
58
|
+
|
59
|
+
def _restrict(type, predicate)
|
60
|
+
on_as, rest = predicate.and_split([as])
|
61
|
+
if on_as == predicate
|
62
|
+
super
|
63
|
+
else
|
64
|
+
shared, left_only = rest.and_split(on)
|
65
|
+
new_left = left.restrict(rest)
|
66
|
+
new_right = shared.tautology? ? right : right.restrict(shared)
|
67
|
+
opt = new_left.image(new_right, as, on, options)
|
68
|
+
opt = opt.restrict(on_as) unless on_as.tautology?
|
69
|
+
opt
|
70
|
+
end
|
71
|
+
rescue Predicate::NotSupportedError
|
72
|
+
super
|
73
|
+
end
|
74
|
+
|
53
75
|
private
|
54
76
|
|
55
77
|
def tuple_project(tuple, on)
|
data/lib/bmg/operator/project.rb
CHANGED
data/lib/bmg/operator/rename.rb
CHANGED
data/lib/bmg/operator/union.rb
CHANGED
@@ -6,6 +6,9 @@ module Bmg
|
|
6
6
|
# Returns all tuples of the left operand followed by all
|
7
7
|
# tuples from the right operand.
|
8
8
|
#
|
9
|
+
# This implementation is actually a NAry-Union, since it handles
|
10
|
+
# an arbitrary number of operands.
|
11
|
+
#
|
9
12
|
# By default, this operator strips duplicates, as of relational
|
10
13
|
# theory. Please set the `:all` option to true to avoid this
|
11
14
|
# behavior and save execution time.
|
@@ -17,17 +20,16 @@ module Bmg
|
|
17
20
|
all: false
|
18
21
|
}
|
19
22
|
|
20
|
-
def initialize(type,
|
23
|
+
def initialize(type, operands, options = {})
|
21
24
|
@type = type
|
22
|
-
@
|
23
|
-
@right = right
|
25
|
+
@operands = operands
|
24
26
|
@options = DEFAULT_OPTIONS.merge(options)
|
25
27
|
end
|
26
28
|
attr_reader :type
|
27
29
|
|
28
30
|
protected
|
29
31
|
|
30
|
-
attr_reader :
|
32
|
+
attr_reader :operands, :options
|
31
33
|
|
32
34
|
public
|
33
35
|
|
@@ -37,24 +39,48 @@ module Bmg
|
|
37
39
|
|
38
40
|
def each(&bl)
|
39
41
|
if all?
|
40
|
-
|
41
|
-
|
42
|
+
operands.each do |op|
|
43
|
+
op.each(&bl)
|
44
|
+
end
|
42
45
|
else
|
43
46
|
seen = {}
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
yield(tuple) unless seen.has_key?(tuple)
|
47
|
+
operands.each do |op|
|
48
|
+
op.each do |tuple|
|
49
|
+
yield(tuple) unless seen.has_key?(tuple)
|
50
|
+
seen[tuple] = true
|
51
|
+
end
|
50
52
|
end
|
51
53
|
end
|
52
54
|
end
|
53
55
|
|
56
|
+
def to_ast
|
57
|
+
[ :union ] + operands.map(&:to_ast) + [ options.dup ]
|
58
|
+
end
|
59
|
+
|
54
60
|
protected ### optimization
|
55
61
|
|
56
62
|
def _restrict(type, predicate)
|
57
|
-
|
63
|
+
ops = operands
|
64
|
+
.map {|op| op.restrict(predicate) }
|
65
|
+
.reject{|op| op.is_a?(Relation::Empty) }
|
66
|
+
case ops.size
|
67
|
+
when 0 then Relation.empty(type)
|
68
|
+
when 1 then ops.first
|
69
|
+
else Union.new(type, ops, options)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def _union(type, other, options)
|
74
|
+
return self if other.is_a?(Relation::Empty)
|
75
|
+
norm_options = DEFAULT_OPTIONS.merge(options)
|
76
|
+
return super unless norm_options == self.options
|
77
|
+
case other
|
78
|
+
when Union
|
79
|
+
return super unless norm_options == other.send(:options)
|
80
|
+
Union.new(type, operands + other.operands, options)
|
81
|
+
else
|
82
|
+
Union.new(type, operands + [other], options)
|
83
|
+
end
|
58
84
|
end
|
59
85
|
|
60
86
|
end # class Union
|
data/lib/bmg/relation.rb
CHANGED
@@ -7,6 +7,10 @@ module Bmg
|
|
7
7
|
operand.is_a?(Relation) ? operand : Leaf.new(type, operand)
|
8
8
|
end
|
9
9
|
|
10
|
+
def self.empty(type = Type::ANY)
|
11
|
+
Relation::Empty.new(type)
|
12
|
+
end
|
13
|
+
|
10
14
|
# Private helper to implement `one` and `one_or_nil`
|
11
15
|
def one_or_yield(&bl)
|
12
16
|
first = nil
|
@@ -31,5 +35,31 @@ module Bmg
|
|
31
35
|
one_or_yield{ nil }
|
32
36
|
end
|
33
37
|
|
38
|
+
# Converts to an sexpr expression.
|
39
|
+
def to_ast
|
40
|
+
raise "Bmg is missing a feature!"
|
41
|
+
end
|
42
|
+
|
43
|
+
# Returns a String representing the query plan
|
44
|
+
def debug(max_level = nil, on = STDERR)
|
45
|
+
on.puts _debug(to_ast, 1, max_level)
|
46
|
+
self
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def _debug(ast, level = 1, max_level = nil)
|
52
|
+
return ast.inspect if ast.is_a?(Symbol)
|
53
|
+
return ast.to_s unless ast.is_a?(Array)
|
54
|
+
return ast.to_s unless ast.first.is_a?(Symbol)
|
55
|
+
if max_level && level>max_level
|
56
|
+
"(#{ast.first} ...)"
|
57
|
+
else
|
58
|
+
sep = " " * level
|
59
|
+
"(#{ast.first}\n" + ast[1..-1].map{|a| _debug(a, 1+level, max_level) }.join("\n").gsub(/^/, sep) + ")"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
34
63
|
end # module Relation
|
35
64
|
end # module Bmg
|
65
|
+
require_relative 'relation/empty'
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Bmg
|
2
|
+
module Relation
|
3
|
+
#
|
4
|
+
# The empty relation, of a given type.
|
5
|
+
#
|
6
|
+
# This relation implementation exists mostly for optimization
|
7
|
+
# purposes, since knowing that a relation is empty allows
|
8
|
+
# simplifying many expressions.
|
9
|
+
#
|
10
|
+
class Empty
|
11
|
+
include Relation
|
12
|
+
|
13
|
+
def initialize(type)
|
14
|
+
@type = type
|
15
|
+
end
|
16
|
+
attr_reader :type
|
17
|
+
|
18
|
+
def each(&bl)
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_ast
|
22
|
+
[ :empty ]
|
23
|
+
end
|
24
|
+
|
25
|
+
protected ### optimization
|
26
|
+
|
27
|
+
def _allbut(type, *args)
|
28
|
+
Empty.new(type)
|
29
|
+
end
|
30
|
+
|
31
|
+
def _autosummarize(type, *args)
|
32
|
+
Empty.new(type)
|
33
|
+
end
|
34
|
+
|
35
|
+
def _autowrap(type, *args)
|
36
|
+
Empty.new(type)
|
37
|
+
end
|
38
|
+
|
39
|
+
def _extend(type, *args)
|
40
|
+
Empty.new(type)
|
41
|
+
end
|
42
|
+
|
43
|
+
def _image(type, *args)
|
44
|
+
Empty.new(type)
|
45
|
+
end
|
46
|
+
|
47
|
+
def _project(type, *args)
|
48
|
+
Empty.new(type)
|
49
|
+
end
|
50
|
+
|
51
|
+
def _rename(type, *args)
|
52
|
+
Empty.new(type)
|
53
|
+
end
|
54
|
+
|
55
|
+
def _restrict(type, predicate)
|
56
|
+
self
|
57
|
+
end
|
58
|
+
|
59
|
+
def _union(type, other, options)
|
60
|
+
other
|
61
|
+
end
|
62
|
+
|
63
|
+
end # class Empty
|
64
|
+
end # module Relation
|
65
|
+
end # module Bmg
|
data/lib/bmg/sequel/relation.rb
CHANGED
data/lib/bmg/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bmg
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bernard Lambeau
|
@@ -144,6 +144,7 @@ files:
|
|
144
144
|
- lib/bmg/reader/csv.rb
|
145
145
|
- lib/bmg/reader/excel.rb
|
146
146
|
- lib/bmg/relation.rb
|
147
|
+
- lib/bmg/relation/empty.rb
|
147
148
|
- lib/bmg/sequel.rb
|
148
149
|
- lib/bmg/sequel/relation.rb
|
149
150
|
- lib/bmg/type.rb
|