bmg 0.9.1 → 0.10.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.
Files changed (92) hide show
  1. checksums.yaml +4 -4
  2. data/lib/bmg/algebra.rb +2 -26
  3. data/lib/bmg/algebra/shortcuts.rb +14 -0
  4. data/lib/bmg/operator/autowrap.rb +32 -2
  5. data/lib/bmg/operator/constants.rb +12 -0
  6. data/lib/bmg/operator/image.rb +10 -0
  7. data/lib/bmg/operator/rename.rb +8 -0
  8. data/lib/bmg/relation/spied.rb +4 -9
  9. data/lib/bmg/sequel.rb +53 -2
  10. data/lib/bmg/sequel/relation.rb +24 -19
  11. data/lib/bmg/sequel/translator.rb +153 -0
  12. data/lib/bmg/sequel/type_inference.rb +42 -0
  13. data/lib/bmg/sql.rb +20 -0
  14. data/lib/bmg/sql/builder.rb +177 -0
  15. data/lib/bmg/sql/dialect.rb +15 -0
  16. data/lib/bmg/sql/ext/predicate.rb +23 -0
  17. data/lib/bmg/sql/ext/predicate/and.rb +9 -0
  18. data/lib/bmg/sql/ext/predicate/contradiction.rb +10 -0
  19. data/lib/bmg/sql/ext/predicate/dyadic_comp.rb +12 -0
  20. data/lib/bmg/sql/ext/predicate/eq.rb +9 -0
  21. data/lib/bmg/sql/ext/predicate/exists.rb +12 -0
  22. data/lib/bmg/sql/ext/predicate/expr.rb +18 -0
  23. data/lib/bmg/sql/ext/predicate/gt.rb +9 -0
  24. data/lib/bmg/sql/ext/predicate/gte.rb +9 -0
  25. data/lib/bmg/sql/ext/predicate/identifier.rb +10 -0
  26. data/lib/bmg/sql/ext/predicate/in.rb +29 -0
  27. data/lib/bmg/sql/ext/predicate/literal.rb +10 -0
  28. data/lib/bmg/sql/ext/predicate/lt.rb +9 -0
  29. data/lib/bmg/sql/ext/predicate/lte.rb +9 -0
  30. data/lib/bmg/sql/ext/predicate/nadic_bool.rb +16 -0
  31. data/lib/bmg/sql/ext/predicate/native.rb +9 -0
  32. data/lib/bmg/sql/ext/predicate/neq.rb +9 -0
  33. data/lib/bmg/sql/ext/predicate/not.rb +12 -0
  34. data/lib/bmg/sql/ext/predicate/or.rb +9 -0
  35. data/lib/bmg/sql/ext/predicate/qualified_identifier.rb +12 -0
  36. data/lib/bmg/sql/ext/predicate/tautology.rb +10 -0
  37. data/lib/bmg/sql/grammar.rb +45 -0
  38. data/lib/bmg/sql/grammar.sexp.yml +96 -0
  39. data/lib/bmg/sql/nodes/column_name.rb +25 -0
  40. data/lib/bmg/sql/nodes/cross_join.rb +28 -0
  41. data/lib/bmg/sql/nodes/except.rb +14 -0
  42. data/lib/bmg/sql/nodes/expr.rb +94 -0
  43. data/lib/bmg/sql/nodes/from_clause.rb +24 -0
  44. data/lib/bmg/sql/nodes/inner_join.rb +37 -0
  45. data/lib/bmg/sql/nodes/intersect.rb +14 -0
  46. data/lib/bmg/sql/nodes/limit_clause.rb +19 -0
  47. data/lib/bmg/sql/nodes/literal.rb +18 -0
  48. data/lib/bmg/sql/nodes/name_intro.rb +23 -0
  49. data/lib/bmg/sql/nodes/native_table_as.rb +31 -0
  50. data/lib/bmg/sql/nodes/offset_clause.rb +19 -0
  51. data/lib/bmg/sql/nodes/order_by_clause.rb +25 -0
  52. data/lib/bmg/sql/nodes/order_by_term.rb +30 -0
  53. data/lib/bmg/sql/nodes/qualified_name.rb +32 -0
  54. data/lib/bmg/sql/nodes/range_var_name.rb +17 -0
  55. data/lib/bmg/sql/nodes/select_exp.rb +109 -0
  56. data/lib/bmg/sql/nodes/select_item.rb +37 -0
  57. data/lib/bmg/sql/nodes/select_list.rb +35 -0
  58. data/lib/bmg/sql/nodes/select_star.rb +22 -0
  59. data/lib/bmg/sql/nodes/set_operator.rb +68 -0
  60. data/lib/bmg/sql/nodes/set_quantifier.rb +20 -0
  61. data/lib/bmg/sql/nodes/subquery_as.rb +28 -0
  62. data/lib/bmg/sql/nodes/table_as.rb +31 -0
  63. data/lib/bmg/sql/nodes/table_name.rb +17 -0
  64. data/lib/bmg/sql/nodes/union.rb +14 -0
  65. data/lib/bmg/sql/nodes/where_clause.rb +20 -0
  66. data/lib/bmg/sql/nodes/with_exp.rb +51 -0
  67. data/lib/bmg/sql/nodes/with_spec.rb +24 -0
  68. data/lib/bmg/sql/processor.rb +85 -0
  69. data/lib/bmg/sql/processor/all.rb +17 -0
  70. data/lib/bmg/sql/processor/clip.rb +57 -0
  71. data/lib/bmg/sql/processor/distinct.rb +17 -0
  72. data/lib/bmg/sql/processor/flatten.rb +24 -0
  73. data/lib/bmg/sql/processor/from_self.rb +29 -0
  74. data/lib/bmg/sql/processor/join.rb +80 -0
  75. data/lib/bmg/sql/processor/join_support.rb +28 -0
  76. data/lib/bmg/sql/processor/limit_offset.rb +30 -0
  77. data/lib/bmg/sql/processor/merge.rb +49 -0
  78. data/lib/bmg/sql/processor/order_by.rb +32 -0
  79. data/lib/bmg/sql/processor/rename.rb +25 -0
  80. data/lib/bmg/sql/processor/reorder.rb +21 -0
  81. data/lib/bmg/sql/processor/requalify.rb +24 -0
  82. data/lib/bmg/sql/processor/semi_join.rb +68 -0
  83. data/lib/bmg/sql/processor/star.rb +17 -0
  84. data/lib/bmg/sql/processor/where.rb +25 -0
  85. data/lib/bmg/sql/relation.rb +141 -0
  86. data/lib/bmg/sql/version.rb +16 -0
  87. data/lib/bmg/support.rb +1 -0
  88. data/lib/bmg/support/keys.rb +59 -0
  89. data/lib/bmg/type.rb +95 -14
  90. data/lib/bmg/version.rb +2 -2
  91. data/tasks/test.rake +9 -2
  92. metadata +97 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8dd18a64aa1408889e2decc14ddb3a51bd0ec73d
4
- data.tar.gz: 561af2cc9d8793ac79b1011c23c4e6c721cd26d3
3
+ metadata.gz: 4b5e5a6fe330469b706c911dbc8d54e7feebd903
4
+ data.tar.gz: f14ca254437c5dd3c896c24cf0b31b75a338f280
5
5
  SHA512:
6
- metadata.gz: 1870b9fcb76b2667ed83967ff80c7776f40d9622f55195aee8428a06fdeca4d298e014a82f1db904bbd3acc985fc0be94e9e1dc3ae1f47a0e06a6d5b753f2fb9
7
- data.tar.gz: 49107701d07c644f7f6eed57b6c28c8a5dc61a38beda056d4a9b18bc0bfdd489627239f0ec944ac3933e00ebe8552a0c36c568cee6672bc938c1acdae0a9df6f
6
+ metadata.gz: b6510a06199ceeca0a06d95ba6d58685483d9dd47c1780f156b2a5854c916c9e4458e2b0ba1862569d657a2dbe1b0e5957c1c5cb29fda6ee5e9de5c1fc4bca67
7
+ data.tar.gz: 980af325f9e60808fd8c84ad5b9a75f5b63acb416b665e1b66a5c0a95a017e7f35c01a6796ebe89477373ca7c055b34e22a1ab4e4fdaf938547b4afac73075df
@@ -1,23 +1,6 @@
1
1
  module Bmg
2
2
  module Algebra
3
3
 
4
- METHODS = [
5
- :allbut,
6
- :autowrap,
7
- :autosummarize,
8
- :constants,
9
- :extend,
10
- :group,
11
- :image,
12
- :matching,
13
- :page,
14
- :project,
15
- :rename,
16
- :restrict,
17
- :rxmatch,
18
- :union
19
- ]
20
-
21
4
  def allbut(butlist = [])
22
5
  _allbut self.type.allbut(butlist), butlist
23
6
  end
@@ -140,15 +123,6 @@ module Bmg
140
123
  end
141
124
  protected :_restrict
142
125
 
143
- def rxmatch(attrs, matcher, options = {})
144
- _rxmatch type.rxmatch(attrs, matcher, options), attrs, matcher, options
145
- end
146
-
147
- def _rxmatch(type, attrs, matcher, options)
148
- Operator::Rxmatch.new(type, self, attrs, matcher, options)
149
- end
150
- protected :_rxmatch
151
-
152
126
  def union(other, options = {})
153
127
  _union self.type.union(other.type), other, options
154
128
  end
@@ -167,5 +141,7 @@ module Bmg
167
141
  self
168
142
  end
169
143
 
144
+ require_relative 'algebra/shortcuts'
145
+ include Shortcuts
170
146
  end # module Algebra
171
147
  end # module Bmg
@@ -0,0 +1,14 @@
1
+ module Bmg
2
+ module Algebra
3
+ module Shortcuts
4
+
5
+ def rxmatch(attrs, matcher, options = {})
6
+ predicate = attrs.inject(Predicate.contradiction){|p,a|
7
+ p | Predicate.match(a, matcher, options)
8
+ }
9
+ self.restrict(predicate)
10
+ end
11
+
12
+ end # module Shortcuts
13
+ end # module Algebra
14
+ end # module Bmg
@@ -41,7 +41,7 @@ module Bmg
41
41
 
42
42
  def each
43
43
  @operand.each do |tuple|
44
- yield autowrap(tuple)
44
+ yield autowrap_tuple(tuple)
45
45
  end
46
46
  end
47
47
 
@@ -49,6 +49,19 @@ module Bmg
49
49
  [ :autowrap, operand.to_ast, @original_options.dup ]
50
50
  end
51
51
 
52
+ protected ### optimization
53
+
54
+ def _restrict(type, predicate)
55
+ return super unless operand.type.knows_attrlist?
56
+ roots = Support.wrapped_roots(operand.type.to_attrlist, options[:split])
57
+ vars = predicate.free_variables
58
+ if (roots & vars).empty?
59
+ operand.restrict(predicate).autowrap(options)
60
+ else
61
+ super
62
+ end
63
+ end
64
+
52
65
  protected ### inspect
53
66
 
54
67
  def args
@@ -57,7 +70,7 @@ module Bmg
57
70
 
58
71
  private
59
72
 
60
- def autowrap(tuple)
73
+ def autowrap_tuple(tuple)
61
74
  separator = @options[:split]
62
75
  autowrapped = tuple.each_with_object({}){|(k,v),h|
63
76
  parts = k.to_s.split(separator).map(&:to_sym)
@@ -97,6 +110,11 @@ module Bmg
97
110
  none: ->(t,k){ t }
98
111
  }
99
112
 
113
+ def self.new(remover)
114
+ return remover if remover.is_a?(NoLeftJoinNoise)
115
+ super
116
+ end
117
+
100
118
  def initialize(remover)
101
119
  @remover_to_s = remover
102
120
  @remover = case remover
@@ -128,6 +146,18 @@ module Bmg
128
146
 
129
147
  end # NoLeftJoinNoise
130
148
 
149
+ module Support
150
+
151
+ def wrapped_roots(attrlist, split_symbol)
152
+ attrlist.map{|a|
153
+ split = a.to_s.split(split_symbol)
154
+ split.size == 1 ? nil : split[0]
155
+ }.compact.uniq.map(&:to_sym)
156
+ end
157
+ module_function :wrapped_roots
158
+
159
+ end # module Support
160
+
131
161
  end # class Autowrap
132
162
  end # module Operator
133
163
  end # module Bmg
@@ -56,6 +56,18 @@ module Bmg
56
56
 
57
57
  protected ### optimization
58
58
 
59
+ def _page(type, ordering, page_index, options)
60
+ attrs = ordering.map{|(k,v)| k}
61
+ cs_attrs = constants.keys
62
+ if (attrs & cs_attrs).empty?
63
+ operand
64
+ .page(ordering, page_index, options)
65
+ .constants(constants)
66
+ else
67
+ super
68
+ end
69
+ end
70
+
59
71
  def _restrict(type, predicate)
60
72
  # bottom_p makes no reference to constants, top_p possibly
61
73
  # does...
@@ -55,6 +55,16 @@ module Bmg
55
55
 
56
56
  protected ### optimization
57
57
 
58
+ def _page(type, ordering, page_index, opts)
59
+ if ordering.map{|(k,v)| k}.include?(as)
60
+ super
61
+ else
62
+ left
63
+ .page(ordering, page_index, opts)
64
+ .image(right, as, on, options)
65
+ end
66
+ end
67
+
58
68
  def _restrict(type, predicate)
59
69
  on_as, rest = predicate.and_split([as])
60
70
  if rest.tautology?
@@ -62,6 +62,14 @@ module Bmg
62
62
 
63
63
  protected ### optimization
64
64
 
65
+ def _page(type, ordering, page_index, options)
66
+ rr = reverse_renaming
67
+ ordering = ordering.map{|(k,v)|
68
+ v.nil? ? rr[k] || k : [rr[k] || k, v]
69
+ }
70
+ operand.page(ordering, page_index, options).rename(renaming)
71
+ end
72
+
65
73
  def _restrict(type, predicate)
66
74
  operand.restrict(predicate.rename(reverse_renaming)).rename(renaming)
67
75
  end
@@ -29,20 +29,15 @@ module Bmg
29
29
 
30
30
  public ### algebra
31
31
 
32
- Algebra::METHODS.each do |m|
32
+ Algebra.public_instance_methods(false).each do |m|
33
+ next if [:spied, :unspied].include?(m)
34
+
33
35
  define_method(m) do |*args, &bl|
36
+ args = args.map{|a| a.respond_to?(:unspied) ? a.unspied : a }
34
37
  operand.send(m, *args, &bl).spied(spy)
35
38
  end
36
39
  end
37
40
 
38
- def image(right, *args)
39
- operand.image(right.unspied, *args).spied(spy)
40
- end
41
-
42
- def union(right, *args)
43
- operand.union(right.unspied, *args).spied(spy)
44
- end
45
-
46
41
  def unspied
47
42
  operand
48
43
  end
@@ -1,11 +1,62 @@
1
+ require 'bmg/sql'
1
2
  require 'sequel'
2
3
  require 'predicate/sequel'
3
4
  module Bmg
4
5
 
5
- def sequel(dataset, type = Type::ANY)
6
- Sequel::Relation.new(type, dataset).spied(main_spy)
6
+ module Sequel
7
+
8
+ def sequel(*args, &bl)
9
+ source, sequel_db, type = sequel_params(*args, &bl)
10
+ builder = Sql::Builder.new
11
+ sexpr = builder.select_all(type.to_attrlist, source)
12
+ Sequel::Relation.new(type, builder, sexpr, sequel_db).spied(Bmg.main_spy)
13
+ end
14
+ module_function :sequel
15
+
16
+ def sequel_params(source, sequel_db = nil, type = nil)
17
+ sequel_db, type = nil, sequel_db if sequel_db.nil? or sequel_db.is_a?(Type)
18
+ sequel_db = source.db if sequel_db.nil? and source.is_a?(::Sequel::Dataset)
19
+ raise ArgumentError, "A Sequel::Database object is required" if sequel_db.nil?
20
+ raise ArgumentError, "Type's attrlist must be known (#{type})" if type && !type.knows_attrlist?
21
+ type = infer_type!(sequel_db, source) if type.nil?
22
+ [source, sequel_db, type]
23
+ end
24
+ module_function :sequel_params
25
+
26
+ def infer_type!(sequel_db, source)
27
+ raise "Sequel::Relation requires a type for `#{source}`" unless source.is_a?(Symbol)
28
+ TypeInference.new(sequel_db).call(source)
29
+ end
30
+ module_function :infer_type!
31
+
32
+ end
33
+
34
+ # Builds a Relation that uses Sequel for managing real data
35
+ # accesses.
36
+ #
37
+ # Supported signatures:
38
+ #
39
+ # # Table name, providing the Sequel's Database object
40
+ # Bmg.sequel(:suppliers, DB)
41
+ #
42
+ # # Sequel dataset object, embedding the Database object
43
+ # # `from_self` will be used at compilation time, you don't
44
+ # # need to call it yourself.
45
+ # Bmg.sequel(DB[:suppliers])
46
+ #
47
+ # # Similar, but with with a pure SQL query
48
+ # Bmg.sequel(DB[%Q{SELECT ... FROM ...}])
49
+ #
50
+ # # All signatures above with an explicit type object, e.g.:
51
+ # Bmg.sequel(:suppliers, DB, Type::ANY)
52
+ # Bmg.sequel(DB[:suppliers], Type::ANY)
53
+ #
54
+ def sequel(source, sequel_db = nil, type = nil)
55
+ Sequel.sequel(source, sequel_db, type)
7
56
  end
8
57
  module_function :sequel
9
58
 
10
59
  end
60
+ require_relative 'sequel/translator'
61
+ require_relative 'sequel/type_inference'
11
62
  require_relative 'sequel/relation'
@@ -1,22 +1,15 @@
1
1
  module Bmg
2
2
  module Sequel
3
- class Relation
4
- include Bmg::Relation
3
+ class Relation < Sql::Relation
5
4
 
6
- def initialize(type, dataset)
7
- @type = type
8
- @dataset = dataset
5
+ def initialize(type, builder, source, sequel_db)
6
+ super(type, builder, source)
7
+ @sequel_db = sequel_db
9
8
  end
10
- attr_reader :type
11
-
12
- protected
13
-
14
- attr_reader :dataset
15
-
16
- public
9
+ attr_reader :sequel_db
17
10
 
18
11
  def each(&bl)
19
- @dataset.each(&bl)
12
+ dataset.each(&bl)
20
13
  end
21
14
 
22
15
  def delete
@@ -44,19 +37,31 @@ module Bmg
44
37
  [:sequel, dataset.sql]
45
38
  end
46
39
 
40
+ def to_sql
41
+ dataset.sql
42
+ end
43
+
47
44
  def to_s
48
45
  "(sequel #{dataset.sql})"
49
46
  end
50
47
  alias :inspect :to_s
51
48
 
52
- protected ### optimization
49
+ protected
50
+
51
+ def dataset
52
+ @dataset ||= Translator.new(sequel_db).call(self.expr)
53
+ end
54
+
55
+ def _instance(type, builder, expr)
56
+ Relation.new(type, builder, expr, sequel_db)
57
+ end
53
58
 
54
- def _restrict(type, predicate)
55
- Relation.new type, dataset.where(predicate.to_sequel)
56
- rescue NotImplementedError, Predicate::NotSupportedError
57
- super
59
+ def extract_compatible_sexpr(operand)
60
+ return nil unless operand.is_a?(Bmg::Sequel::Relation)
61
+ return nil unless self.sequel_db == operand.sequel_db
62
+ operand.expr
58
63
  end
59
64
 
60
65
  end # class Relation
61
- end # module Operator
66
+ end # module Sequel
62
67
  end # module Bmg
@@ -0,0 +1,153 @@
1
+ module Bmg
2
+ module Sequel
3
+ class Translator < Sexpr::Processor
4
+ include ::Predicate::ToSequel::Methods
5
+
6
+ def initialize(sequel_db)
7
+ @sequel_db = sequel_db
8
+ end
9
+ attr_reader :sequel_db
10
+
11
+ def on_with_exp(sexpr)
12
+ if sequel_db.select(1).supports_cte?
13
+ dataset = apply(sexpr.select_exp)
14
+ apply(sexpr.with_spec).each_pair do |name,subquery|
15
+ dataset = dataset.with(name, subquery)
16
+ end
17
+ dataset
18
+ else
19
+ apply(Sql::Processor::Flatten.new(Sql::Builder.new).call(sexpr))
20
+ end
21
+ end
22
+
23
+ def on_with_spec(sexpr)
24
+ sexpr.each_with_object({}){|child,hash|
25
+ next if child == :with_spec
26
+ hash[apply(child.table_name)] = apply(child.subquery)
27
+ }
28
+ end
29
+
30
+ def on_set_operator(sexpr)
31
+ left, right = apply(sexpr.left), apply(sexpr.right)
32
+ left = left.from_self if sexpr.left.set_operator?
33
+ left.send(sexpr.first, right, all: sexpr.all?, from_self: false)
34
+ end
35
+ alias :on_union :on_set_operator
36
+ alias :on_intersect :on_set_operator
37
+ alias :on_except :on_set_operator
38
+
39
+ def on_select_exp(sexpr)
40
+ dataset = sequel_db.select(1)
41
+ dataset = dataset(apply(sexpr.from_clause)) if sexpr.from_clause
42
+ #
43
+ selection = apply(sexpr.select_list)
44
+ predicate = apply(sexpr.predicate) if sexpr.predicate
45
+ order = apply(sexpr.order_by_clause) if sexpr.order_by_clause
46
+ limit = apply(sexpr.limit_clause) if sexpr.limit_clause
47
+ offset = apply(sexpr.offset_clause) if sexpr.offset_clause
48
+ #
49
+ dataset = dataset.select(*selection)
50
+ dataset = dataset.distinct if sexpr.distinct?
51
+ dataset = dataset.where(predicate) if predicate
52
+ dataset = dataset.order_by(*order) if order
53
+ dataset = dataset.limit(limit, offset == 0 ? nil : offset) if limit or offset
54
+ dataset
55
+ end
56
+
57
+ def on_select_list(sexpr)
58
+ sexpr.sexpr_body.map{|c| apply(c) }
59
+ end
60
+
61
+ def on_select_star(sexpr)
62
+ ::Sequel.lit('*')
63
+ end
64
+
65
+ def on_select_item(sexpr)
66
+ left = apply(sexpr.left)
67
+ right = apply(sexpr.right)
68
+ if left.column == right.value
69
+ left
70
+ else
71
+ ::Sequel.as(left, right)
72
+ end
73
+ end
74
+
75
+ def on_qualified_name(sexpr)
76
+ apply(sexpr.last).qualify(sexpr.qualifier)
77
+ end
78
+
79
+ def on_column_name(sexpr)
80
+ ::Sequel.expr(sexpr.last.to_sym)
81
+ end
82
+
83
+ def on_from_clause(sexpr)
84
+ apply(sexpr.table_spec)
85
+ end
86
+
87
+ def on_table_name(sexpr)
88
+ ::Sequel.identifier(sexpr.last)
89
+ end
90
+
91
+ def on_cross_join(sexpr)
92
+ left, right = apply(sexpr.left), apply(sexpr.right)
93
+ dataset(left).cross_join(right)
94
+ end
95
+
96
+ def on_inner_join(sexpr)
97
+ left, right = apply(sexpr.left), apply(sexpr.right)
98
+ options = {qualify: false, table_alias: false}
99
+ dataset(left).join_table(:inner, right, nil, options){|*args|
100
+ apply(sexpr.predicate)
101
+ }
102
+ end
103
+
104
+ def on_table_as(sexpr)
105
+ ::Sequel.as(::Sequel.identifier(sexpr.table_name), ::Sequel.identifier(sexpr.as_name))
106
+ end
107
+
108
+ def on_subquery_as(sexpr)
109
+ ::Sequel.as(apply(sexpr.subquery), ::Sequel.identifier(sexpr.as_name))
110
+ end
111
+
112
+ def on_native_table_as(sexpr)
113
+ sexpr[1].from_self(:alias => sexpr.as_name)
114
+ end
115
+
116
+ def on_order_by_clause(sexpr)
117
+ sexpr.sexpr_body.map{|c| apply(c)}
118
+ end
119
+
120
+ def on_order_by_term(sexpr)
121
+ ::Sequel.send(sexpr.direction, apply(sexpr.qualified_name))
122
+ end
123
+
124
+ def on_limit_clause(sexpr)
125
+ sexpr.last
126
+ end
127
+
128
+ def on_offset_clause(sexpr)
129
+ sexpr.last
130
+ end
131
+
132
+ public ### Predicate hack
133
+
134
+ def on_in(sexpr)
135
+ left, right = apply(sexpr.identifier), sexpr.last
136
+ right = apply(right) if sexpr.subquery?
137
+ ::Sequel.expr(left => right)
138
+ end
139
+
140
+ def on_exists(sexpr)
141
+ apply(sexpr.last).exists
142
+ end
143
+
144
+ private
145
+
146
+ def dataset(expr)
147
+ return expr if ::Sequel::Dataset===expr
148
+ sequel_db[expr]
149
+ end
150
+
151
+ end # class Translator
152
+ end # module Sequel
153
+ end # module Bmg