bmg 0.9.1 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
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