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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e6536b48ec764ca63860c01d8f75b689449c0d29
4
- data.tar.gz: 4a5f92f4e5dc65f723e419ccbfe89dd424aa27ef
3
+ metadata.gz: 5cdd3816b79a6f29c387456d9ffdc12d9ae7872e
4
+ data.tar.gz: 4c30157082a6474c76a156efc7fcdb510f2f2d44
5
5
  SHA512:
6
- metadata.gz: 3d9fff6fdabf0f3be88b94cff5a7896d0625d82c47772683c3f40156c7cefa46387c95131c261aeb953978aec013e16acae40d203a9554233a61604fea59e5bb
7
- data.tar.gz: 6b8b239b5da4002e2ee96a0946acf7b9636f465f15eeac0f5ee227dc4fcccab68fa00b4b4a32a4827b0e9336e5f46a73036397dabe9855e6ea14351a56279fe8
6
+ metadata.gz: 95290af1dba5768f4a4bfee0357d53ddd70063a6fb6a34ee8c6b329805f44ec1ac49ba0fd5baea0b6946d4e81bf3250b30a954f2b1288c53e0eaa4019687e6d8
7
+ data.tar.gz: 95fb7f28fbaf62b2f12286857bd04ccc0849d397f085bd366f303572e21a01d6baeb023d4226f56b8223182f075f0c455a3ef06aa7433c677fa5390404c4b9d9
@@ -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
 
@@ -14,5 +14,9 @@ module Bmg
14
14
  @operand.each(&bl)
15
15
  end
16
16
 
17
+ def to_ast
18
+ [:leaf, operand]
19
+ end
20
+
17
21
  end
18
22
  end
@@ -41,6 +41,10 @@ module Bmg
41
41
  end
42
42
  end
43
43
 
44
+ def to_ast
45
+ [:allbut, operand.to_ast, butlist.dup]
46
+ end
47
+
44
48
  protected ### optimization
45
49
 
46
50
  def _restrict(type, predicate)
@@ -38,6 +38,10 @@ module Bmg
38
38
  h.values.each(&bl)
39
39
  end
40
40
 
41
+ def to_ast
42
+ [:autosummarize, operand.to_ast, by.dup, sums.dup]
43
+ end
44
+
41
45
  private
42
46
 
43
47
  # Returns the tuple determinant.
@@ -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 == predicate
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) unless bottom_p.tautology?
64
+ result = result.restrict(bottom_p)
41
65
  result = result.constants(constants)
42
- result = result.restrict(top_p) unless top_p.tautology?
66
+ result = result.restrict(top_p)
43
67
  result
44
68
  end
45
69
  rescue Predicate::NotSupportedError
@@ -32,6 +32,10 @@ module Bmg
32
32
  end
33
33
  end
34
34
 
35
+ def to_ast
36
+ [ :extend, operand.to_ast, extension.dup ]
37
+ end
38
+
35
39
  private
36
40
 
37
41
  def extend_it(tuple)
@@ -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)
@@ -40,6 +40,10 @@ module Bmg
40
40
  end
41
41
  end
42
42
 
43
+ def to_ast
44
+ [ :project, operand.to_ast, attrlist ]
45
+ end
46
+
43
47
  protected ### optimization
44
48
 
45
49
  def _restrict(type, predicate)
@@ -35,6 +35,10 @@ module Bmg
35
35
  end
36
36
  end
37
37
 
38
+ def to_ast
39
+ [ :rename, operand.to_ast, renaming.dup ]
40
+ end
41
+
38
42
  private
39
43
 
40
44
  def rename(tuple)
@@ -28,6 +28,10 @@ module Bmg
28
28
  end
29
29
  end
30
30
 
31
+ def to_ast
32
+ [ :restrict, operand.to_ast, predicate.sexpr ]
33
+ end
34
+
31
35
  protected
32
36
 
33
37
  def _restrict(type, predicate)
@@ -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, left, right, options = {})
23
+ def initialize(type, operands, options = {})
21
24
  @type = type
22
- @left = left
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 :left, :right, :options
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
- @left.each(&bl)
41
- @right.each(&bl)
42
+ operands.each do |op|
43
+ op.each(&bl)
44
+ end
42
45
  else
43
46
  seen = {}
44
- @left.each do |tuple|
45
- yield(tuple)
46
- seen[tuple] = true
47
- end
48
- @right.each do |tuple|
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
- left.restrict(predicate).union(right.restrict(predicate))
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
@@ -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
@@ -19,6 +19,10 @@ module Bmg
19
19
  @dataset.each(&bl)
20
20
  end
21
21
 
22
+ def to_ast
23
+ [:sequel, @dataset.sql]
24
+ end
25
+
22
26
  protected ### optimization
23
27
 
24
28
  def _restrict(type, predicate)
@@ -1,7 +1,7 @@
1
1
  module Bmg
2
2
  module Version
3
3
  MAJOR = 0
4
- MINOR = 3
4
+ MINOR = 4
5
5
  TINY = 0
6
6
  end
7
7
  VERSION = "#{Version::MAJOR}.#{Version::MINOR}.#{Version::TINY}"
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.3.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