bmg 0.16.7 → 0.17.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3ca0b8ab7a34fca3dbf7cb37774b099289a8e15cf42fd381b9ee88f3ac01ff81
4
- data.tar.gz: e96b6905b920b0cf70e52227ffe19dcd40ce3a9263deadf032a628348f4c340c
3
+ metadata.gz: 0d94c440e4e3c70b16735c6d31e3ba50a4be12430d347d9ac8cff6622bc64fb3
4
+ data.tar.gz: 1443c66b0c1c621f97b2f2f8bea2c522c07151ec4bbef71b45585078635c5b7b
5
5
  SHA512:
6
- metadata.gz: 6727a3c5b475e5a69765b0f2d4093df4f2aa2ee67364589ee8c0a8e3d368acf50cbe092e7651791e3b13f70ee4d51718921166cac62595998f0b0de2775b1b54
7
- data.tar.gz: ef2c1b8eb520f5a4a3a7a24a95f7afcbb96be47e77872dbd55100a0369bd65d7c4d173234d4966591a803106b4937505c5d36ab8ee59d8fde2d6c261988e3ab8
6
+ metadata.gz: 7eba5332a684097f8cd95a1c28463bbb1e5b004788bf3b3133a55a0c1cf1b5b5335a41924b9ba98cae10fe81f2c4b0ad70bb27d4f97618a8d18a7dbc0f48265f
7
+ data.tar.gz: 0241a9d8b895ca70df1a7f705ceecce062369810a8b4f60e1f314db0ab2888dc0060ad2f37697b7c83ab11cd2eaefd13e8445d1486d9827f294766bd9f3471aa
data/Gemfile CHANGED
@@ -1,2 +1,5 @@
1
1
  source "https://rubygems.org"
2
2
  gemspec
3
+
4
+ gem "predicate", github: "enspirit/predicate", branch: "placeholders"
5
+ # gem "predicate", path: "../predicate"
@@ -12,7 +12,21 @@ module Bmg
12
12
 
13
13
  # Whether we need to convert each image as an Array,
14
14
  # instead of keeping a Relation instance
15
- array: false
15
+ array: false,
16
+
17
+ # The strategy to use for actual image algorithm. Default is
18
+ # :refilter_right. Possible values are:
19
+ #
20
+ # - :index_right : builds a memory index with tuples from right, then
21
+ # passes left tuples and joins them with the index values.
22
+ #
23
+ # - :refilter_right : the left operand is materialized and all
24
+ # distinct values collected. The right operand is lately restricted
25
+ # to only those matching values. :index_right is then applied on
26
+ # resulting operabds. This option only applies when (optimized) `on`
27
+ # contains one attribute only. ; it fallbacks on :index_right
28
+ # otherwise.
29
+ strategy: :refilter_right
16
30
 
17
31
  }
18
32
 
@@ -31,7 +45,55 @@ module Bmg
31
45
 
32
46
  public
33
47
 
34
- def each
48
+ def each(*args, &bl)
49
+ (options[:jit_optimized] ? self : jit_optimize)._each(*args, &bl)
50
+ end
51
+
52
+ def to_ast
53
+ [ :image, left.to_ast, right.to_ast, as, on, options.dup ]
54
+ end
55
+
56
+ protected
57
+
58
+ def _each(*args, &bl)
59
+ case s = options[:strategy]
60
+ when :index_right then _each_index_right(*args, &bl)
61
+ when :refilter_right then _each_refilter_right(*args, &bl)
62
+ else
63
+ raise ArgumentError, "Unknown strategy `#{s}`"
64
+ end
65
+ end
66
+
67
+ def _each_index_right(*args, &bl)
68
+ left_rel, right_rel = self.left, self.right
69
+ _each_implem(left_rel, right_rel, *args, &bl)
70
+ end
71
+
72
+ def _each_refilter_right(*args, &bl)
73
+ left_rel, right_rel = self.left, self.right
74
+
75
+ # find matching keys on left and rebind the right
76
+ # placeholder to them
77
+ values = left_rel.map{|t| t[on.first] }
78
+ placeholder = options[:refilter_right][:placeholder]
79
+ right_rel = right_rel.bind(placeholder => values)
80
+
81
+ _each_implem(left_rel, right_rel, *args, &bl)
82
+ end
83
+
84
+ def _each_implem(left_rel, right_rel, *args)
85
+ # build right index
86
+ index = build_right_index(right_rel)
87
+
88
+ # each left with image from right index
89
+ left_rel.each do |tuple|
90
+ key = tuple_project(tuple, on)
91
+ image = index[key] || (options[:array] ? [] : empty_image)
92
+ yield tuple.merge(as => image)
93
+ end
94
+ end
95
+
96
+ def build_right_index(right)
35
97
  index = Hash.new{|h,k| h[k] = empty_image }
36
98
  right.each_with_object(index) do |t, index|
37
99
  key = tuple_project(t, on)
@@ -42,15 +104,54 @@ module Bmg
42
104
  ix[k] = v.to_a
43
105
  end
44
106
  end
45
- left.each do |tuple|
46
- key = tuple_project(tuple, on)
47
- image = index[key] || (options[:array] ? [] : empty_image)
48
- yield tuple.merge(as => image)
107
+ index
108
+ end
109
+
110
+ protected ### jit_optimization
111
+
112
+ def jit_optimize
113
+ case s = options[:strategy]
114
+ when :index_right then jit_index_right
115
+ when :refilter_right then jit_refilter_right
116
+ else
117
+ raise ArgumentError, "Unknown strategy `#{s}`"
49
118
  end
50
119
  end
51
120
 
52
- def to_ast
53
- [ :image, left.to_ast, right.to_ast, as, on, options.dup ]
121
+ def jit_index_right
122
+ Image.new(
123
+ type,
124
+ left,
125
+ right,
126
+ as,
127
+ on,
128
+ options.merge(jit_optimized: true))
129
+ end
130
+
131
+ def jit_refilter_right
132
+ ltc = left.type.predicate.constants
133
+ rtc = right.type.predicate.constants
134
+ jit_allbut, jit_on = on.partition{|attr|
135
+ ltc.has_key?(attr) && rtc.has_key?(attr) && ltc[attr] == rtc[attr]
136
+ }
137
+ if jit_on.size == 1
138
+ p = Predicate.placeholder
139
+ Image.new(
140
+ type,
141
+ left.materialize,
142
+ right.restrict(Predicate.in(jit_on.first, p)).allbut(jit_allbut),
143
+ as,
144
+ jit_on,
145
+ options.merge(jit_optimized: true, refilter_right: { placeholder: p }))
146
+ else
147
+ Image.new(
148
+ type,
149
+ left,
150
+ right.allbut(jit_allbut),
151
+ as,
152
+ jit_on,
153
+ options.merge(jit_optimized: true, strategy: :index_right))
154
+ end
54
155
  end
55
156
 
56
157
  protected ### optimization
@@ -68,7 +169,7 @@ module Bmg
68
169
  def _restrict(type, predicate)
69
170
  on_as, rest = predicate.and_split([as])
70
171
  if rest.tautology?
71
- # push none situation: on_as is still the full predicate
172
+ # push index_right situation: on_as is still the full predicate
72
173
  super
73
174
  else
74
175
  # rest makes no reference to `as` and can be pushed
@@ -126,6 +227,16 @@ module Bmg
126
227
  Relation::InMemory.new(image_type, Set.new)
127
228
  end
128
229
 
230
+ public
231
+
232
+ def to_s
233
+ options[:jit_optimized] ? super : jit_optimize.to_s
234
+ end
235
+
236
+ def inspect
237
+ options[:jit_optimized] ? super : jit_optimize.inspect
238
+ end
239
+
129
240
  end # class Project
130
241
  end # module Operator
131
242
  end # module Bmg
@@ -21,6 +21,10 @@ module Bmg
21
21
 
22
22
  public
23
23
 
24
+ def bind(binding)
25
+ Restrict.new(type, operand.bind(binding), predicate.bind(binding))
26
+ end
27
+
24
28
  def each
25
29
  @operand.each do |tuple|
26
30
  yield(tuple) if @predicate.evaluate(tuple)
@@ -3,9 +3,13 @@ module Bmg
3
3
  module Binary
4
4
  include Operator
5
5
 
6
+ def bind(binding)
7
+ _with_operands(left.bind(binding), right.bind(binding))
8
+ end
9
+
6
10
  protected
7
11
 
8
- attr_reader :left, :right
12
+ attr_accessor :left, :right
9
13
 
10
14
  def _visit(parent, visitor)
11
15
  visitor.call(self, parent)
@@ -13,6 +17,13 @@ module Bmg
13
17
  right.send(:_visit, self, visitor)
14
18
  end
15
19
 
20
+ def _with_operands(left, right)
21
+ dup.tap{|d|
22
+ d.left = left
23
+ d.right = right
24
+ }
25
+ end
26
+
16
27
  def operands
17
28
  [left, right]
18
29
  end
@@ -3,9 +3,17 @@ module Bmg
3
3
  module Nary
4
4
  include Operator
5
5
 
6
+ def bind(binding)
7
+ _with_operands(operands.map{|op| op.bind(binding) })
8
+ end
9
+
6
10
  protected
7
11
 
8
- attr_reader :operands
12
+ attr_accessor :operands
13
+
14
+ def _with_operands(operands)
15
+ dup.tap{|d| d.operands = operands }
16
+ end
9
17
 
10
18
  def _visit(parent, visitor)
11
19
  visitor.call(self, parent)
@@ -3,15 +3,23 @@ module Bmg
3
3
  module Unary
4
4
  include Operator
5
5
 
6
+ def bind(binding)
7
+ _with_operand(operand.bind(binding))
8
+ end
9
+
6
10
  protected
7
11
 
8
- attr_reader :operand
12
+ attr_accessor :operand
9
13
 
10
14
  def _visit(parent, visitor)
11
15
  visitor.call(self, parent)
12
16
  operand._visit(self, visitor)
13
17
  end
14
18
 
19
+ def _with_operand(operand)
20
+ dup.tap{|d| d.operand = operand }
21
+ end
22
+
15
23
  def operands
16
24
  [operand]
17
25
  end
@@ -13,6 +13,10 @@ module Bmg
13
13
  Relation::Empty.new(type)
14
14
  end
15
15
 
16
+ def bind(binding)
17
+ self
18
+ end
19
+
16
20
  def with_typecheck
17
21
  dup.tap{|r|
18
22
  r.type = r.type.with_typecheck
@@ -50,7 +50,7 @@ module Bmg
50
50
  dataset = dataset.select(*selection)
51
51
  dataset = dataset.distinct if sexpr.distinct?
52
52
  dataset = dataset.where(predicate) if predicate
53
- dataset = dataset.group(*grouping) if grouping
53
+ dataset = dataset.group(grouping) if grouping
54
54
  dataset = dataset.order_by(*order) if order
55
55
  dataset = dataset.limit(limit, offset == 0 ? nil : offset) if limit or offset
56
56
  dataset
@@ -85,3 +85,4 @@ require_relative 'processor/semi_join'
85
85
  require_relative 'processor/flatten'
86
86
  require_relative 'processor/requalify'
87
87
  require_relative 'processor/summarize'
88
+ require_relative 'processor/bind'
@@ -0,0 +1,23 @@
1
+ module Bmg
2
+ module Sql
3
+ class Processor
4
+ class Bind < Processor
5
+
6
+ def initialize(binding, builder)
7
+ super(builder)
8
+ @binding = binding
9
+ end
10
+
11
+ def on_select_exp(sexpr)
12
+ if w = sexpr.where_clause
13
+ pred = Predicate::Grammar.sexpr(w.predicate.bind(@binding))
14
+ sexpr.with_update(:where_clause, [ :where_clause, pred ])
15
+ else
16
+ sexpr
17
+ end
18
+ end
19
+
20
+ end # class Bind
21
+ end # class Processor
22
+ end # module Sql
23
+ end # module Bmg
@@ -9,7 +9,7 @@ module Bmg
9
9
  h[k.to_s] = builder.next_qualifier!
10
10
  }
11
11
  end
12
- attr_reader :requalify
12
+ attr_reader :requalify
13
13
 
14
14
  alias :on_select_exp :copy_and_apply
15
15
  alias :on_missing :copy_and_apply
@@ -18,6 +18,12 @@ module Bmg
18
18
 
19
19
  public
20
20
 
21
+ def bind(binding)
22
+ expr = before_use(self.expr)
23
+ expr = Processor::Bind.new(binding, builder).call(expr)
24
+ _instance(type, builder, expr)
25
+ end
26
+
21
27
  def each(&bl)
22
28
  raise NotImplementedError
23
29
  end
@@ -1,8 +1,8 @@
1
1
  module Bmg
2
2
  module Version
3
3
  MAJOR = 0
4
- MINOR = 16
5
- TINY = 7
4
+ MINOR = 17
5
+ TINY = 0
6
6
  end
7
7
  VERSION = "#{Version::MAJOR}.#{Version::MINOR}.#{Version::TINY}"
8
8
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bmg
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.16.7
4
+ version: 0.17.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bernard Lambeau
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-05-15 00:00:00.000000000 Z
11
+ date: 2020-04-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: predicate
@@ -16,20 +16,20 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '2.2'
19
+ version: '2.3'
20
20
  - - ">="
21
21
  - !ruby/object:Gem::Version
22
- version: 2.2.1
22
+ version: 2.3.0
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
26
26
  requirements:
27
27
  - - "~>"
28
28
  - !ruby/object:Gem::Version
29
- version: '2.2'
29
+ version: '2.3'
30
30
  - - ">="
31
31
  - !ruby/object:Gem::Version
32
- version: 2.2.1
32
+ version: 2.3.0
33
33
  - !ruby/object:Gem::Dependency
34
34
  name: rake
35
35
  requirement: !ruby/object:Gem::Requirement
@@ -222,6 +222,7 @@ files:
222
222
  - lib/bmg/sql/nodes/with_spec.rb
223
223
  - lib/bmg/sql/processor.rb
224
224
  - lib/bmg/sql/processor/all.rb
225
+ - lib/bmg/sql/processor/bind.rb
225
226
  - lib/bmg/sql/processor/clip.rb
226
227
  - lib/bmg/sql/processor/constants.rb
227
228
  - lib/bmg/sql/processor/distinct.rb