bmg 0.3.0 → 0.4.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.
- 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
|