activerecord-refined 0.11.0 → 0.12.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.
@@ -1,7 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # JSON.generate, for the document a containment test is given.
4
- require "json"
3
+ require "active_record/refined/ast/predications"
4
+ require "active_record/refined/ast/arithmetics"
5
+ require "active_record/refined/ast/node"
6
+ require "active_record/refined/ast/expressions"
7
+ require "active_record/refined/ast/windows"
8
+ require "active_record/refined/ast/functions"
9
+ require "active_record/refined/ast/json"
10
+ require "active_record/refined/ast/ordering"
11
+ require "active_record/refined/ast/grouping"
12
+ require "active_record/refined/ast/conditions"
5
13
 
6
14
  module ActiveRecord
7
15
  module Refined
@@ -54,20 +62,6 @@ module ActiveRecord
54
62
  "#{node.is_a?(Node) ? 'an expression' : node.inspect} is not one"
55
63
  end
56
64
 
57
- # What true, false and nil say when `&`, `|` or `^` finds a query node
58
- # on the right. Their own operators answer a bare boolean, so in
59
- # `:active == true & cond` Ruby gives `:active == (true & cond)` and
60
- # the condition would vanish from the query without a word; a number
61
- # in the same place lands in NumericArithmetics' refusals, but these
62
- # three have no bitwise reading to fall back on.
63
- # @private
64
- def self.refuse_ruby_operator(literal, operator)
65
- raise ArgumentError,
66
- "#{operator} on #{literal.inspect} is Ruby's own, and the query " \
67
- "would lose what stands to its right; the comparison takes " \
68
- "parentheses of its own: (:active == true) #{operator} ..."
69
- end
70
-
71
65
  # What `&` and `|` say when the left side is not a condition. They
72
66
  # mean AND and OR, so what was meant is either the bitwise operation,
73
67
  # which has a name of its own, or a comparison whose parentheses Ruby's
@@ -84,2600 +78,6 @@ module ActiveRecord
84
78
  "#{named} is SQL's bitwise operator"
85
79
  end
86
80
  end
87
-
88
- # The conditions a column or an expression can be put in. Every one
89
- # gives back a condition that combines with `&`, `|` and `!`, and the
90
- # comparisons quote a Ruby value on the right the way Active Record
91
- # does, or take a column, an expression or a subquery there.
92
- #
93
- # @example
94
- # Author.where { :age.between?(20, 40) & :name.like?("A%") }
95
- # Author.where { :id.in?(Post.select(:author_id)) }
96
- #
97
- # Predicate builders shared by symbols, qualified columns and
98
- # expressions. Imported into the Symbol refinement with
99
- # Refinement#import_methods, so every method must be defined with def.
100
- module Predications
101
- # `=`. A value, a column, an expression or a scalar subquery on the right; `nil` is refused, since `= NULL` is never true -- {#null?} is the spelling.
102
- # @return [AST::Predicate]
103
- # @example
104
- # Author.where { :name == "alice" }
105
- # Author.where { :age == Author.select { max(:age) } }
106
- #
107
- # == and != mean SQL = and <>, and = NULL is never true there, so nil
108
- # is rejected rather than silently rewritten to IS NULL. null? builds
109
- # its node directly and stays clear of this check.
110
- def ==(other)
111
- if other.nil?
112
- raise ArgumentError, "== does not take nil; use null? instead"
113
- end
114
- Comparison.new(self, :==, other)
115
- end
116
-
117
- # `!=`; `nil` is refused, as with `==`.
118
- # @return [AST::Predicate]
119
- def !=(other)
120
- if other.nil?
121
- raise ArgumentError, "!= does not take nil; use !null? instead"
122
- end
123
- Comparison.new(self, :!=, other)
124
- end
125
-
126
- # `>`.
127
- # @return [AST::Predicate]
128
- def >(other)
129
- Comparison.new(self, :>, other)
130
- end
131
-
132
- # `>=`.
133
- # @return [AST::Predicate]
134
- def >=(other)
135
- Comparison.new(self, :>=, other)
136
- end
137
-
138
- # `<`.
139
- # @return [AST::Predicate]
140
- def <(other)
141
- Comparison.new(self, :<, other)
142
- end
143
-
144
- # `<=`.
145
- # @return [AST::Predicate]
146
- def <=(other)
147
- Comparison.new(self, :<=, other)
148
- end
149
-
150
- # A regular expression match: `~` on PostgreSQL, `REGEXP` on MySQL, and what the adapter has elsewhere. A Ruby Regexp's source is the pattern.
151
- # @param pattern [Regexp, String]
152
- # @return [AST::Predicate]
153
- # @example
154
- # Author.where { :name =~ /^A/ }
155
- def =~(pattern)
156
- Match.new(self, pattern)
157
- end
158
-
159
- # The negated regular expression match.
160
- # @return [AST::Predicate]
161
- def !~(pattern)
162
- Match.new(self, pattern, negated: true)
163
- end
164
-
165
- # `IS NULL`.
166
- # @return [AST::Predicate]
167
- # @example
168
- # Author.where { :country.null? }
169
- #
170
- # `!` negates any predicate, so these are here for the four that SQL
171
- # spells for itself: IS NOT NULL rather than NOT (... IS NULL), and
172
- # likewise NOT IN and NOT LIKE. They mean the same thing either way,
173
- # including when the column is NULL; what they save is the reading.
174
- def null?
175
- Comparison.new(self, :==, nil)
176
- end
177
-
178
- # `IS NOT NULL`.
179
- # @return [AST::Predicate]
180
- def not_null?
181
- Comparison.new(self, :!=, nil)
182
- end
183
-
184
- # `IS TRUE`: true of the rows where the boolean is true, false where it is false or NULL -- where `== true` would be NULL.
185
- # @return [AST::Predicate]
186
- # @example
187
- # Post.where { :published.true? }
188
- #
189
- # IS TRUE and IS FALSE differ from a comparison against the literal in
190
- # what they make of NULL: `flag = TRUE` is itself NULL there, and a
191
- # NULL predicate selects nothing, while these two answer false. So the
192
- # difference shows in the negations: `not_true?` keeps the NULL rows
193
- # that `!(:flag == true)` drops.
194
- def true?
195
- TruthValue.new(self, true)
196
- end
197
-
198
- # `IS NOT TRUE`: keeps the NULL rows that `!(:flag == true)` drops.
199
- # @return [AST::Predicate]
200
- def not_true?
201
- TruthValue.new(self, true, negated: true)
202
- end
203
-
204
- # `IS FALSE`.
205
- # @return [AST::Predicate]
206
- def false?
207
- TruthValue.new(self, false)
208
- end
209
-
210
- # `IS NOT FALSE`.
211
- # @return [AST::Predicate]
212
- def not_false?
213
- TruthValue.new(self, false, negated: true)
214
- end
215
-
216
- # `IN (...)`: an array of values, a range, or a relation as a subquery.
217
- # @param values [Array, Range, ActiveRecord::Relation]
218
- # @return [AST::Predicate]
219
- # @example
220
- # Author.where { :country.in?(%w[JP US]) }
221
- # Author.where { :id.in?(Post.select(:author_id)) }
222
- def in?(values)
223
- In.new(self, values)
224
- end
225
-
226
- # `NOT IN (...)`.
227
- # @return [AST::Predicate]
228
- def not_in?(values)
229
- In.new(self, values, negated: true)
230
- end
231
-
232
- # `BETWEEN min AND max`, with either end a value, a column or an expression.
233
- # @return [AST::Predicate]
234
- # @example
235
- # Author.where { :age.between?(20, 40) }
236
- #
237
- # Not min..max: an endpoint may be an expression, which Range would
238
- # refuse to hold, since expressions do not compare among themselves.
239
- def between?(min, max)
240
- In.new(self, In::QuotedRange.new(min, max, false))
241
- end
242
-
243
- # `NOT BETWEEN min AND max`.
244
- # @return [AST::Predicate]
245
- def not_between?(min, max)
246
- In.new(self, In::QuotedRange.new(min, max, false), negated: true)
247
- end
248
-
249
- # `CASE column WHEN value THEN ...`: a CASE with this as the operand, each `when` a value it is compared against, followed by `then` and finally `else`.
250
- # @return [AST::Case::When]
251
- # @example
252
- # Author.select { :country.when("JP").then("Japan").else("elsewhere").as(:where) }
253
- #
254
- # CASE with this as the operand, compared against each `when`:
255
- # `:age.when(10).then(1).else(0)`. The other shape, where each `when`
256
- # carries its own condition, starts at `case_when`.
257
- def when(value = nil, &block)
258
- Case.new(self).when(value, &block)
259
- end
260
-
261
- # `LIKE pattern`, the pattern as written: `%` and `_` are its wildcards.
262
- # @param pattern [String]
263
- # @return [AST::Predicate]
264
- # @example
265
- # Author.where { :name.like?("A%") }
266
- def like?(pattern)
267
- Like.new(self, pattern)
268
- end
269
-
270
- # `NOT LIKE pattern`.
271
- # @return [AST::Predicate]
272
- def not_like?(pattern)
273
- Like.new(self, pattern, negated: true)
274
- end
275
-
276
- # A case-insensitive `LIKE`: `ILIKE` on PostgreSQL, and `LIKE` over both sides lower-cased elsewhere.
277
- # @return [AST::Predicate]
278
- def ilike?(pattern)
279
- Like.new(self, pattern, nil, case_sensitive: false)
280
- end
281
-
282
- # The negated case-insensitive `LIKE`.
283
- # @return [AST::Predicate]
284
- def not_ilike?(pattern)
285
- Like.new(self, pattern, nil, case_sensitive: false, negated: true)
286
- end
287
-
288
- # Case-insensitive equality: `LOWER(column) = LOWER(value)`.
289
- # @return [AST::Predicate]
290
- # @example
291
- # Author.where { :name.casecmp?("Alice") }
292
- #
293
- # Case-insensitive equality, folded on both sides rather than left to
294
- # the collation, so it means the same thing on every adapter.
295
- def casecmp?(value)
296
- if value.nil?
297
- raise ArgumentError, "casecmp? does not take nil; use null? instead"
298
- end
299
- Comparison.new(Function.new("LOWER", [self]), :==,
300
- Function.new("LOWER", [value]))
301
- end
302
-
303
- # `IS DISTINCT FROM`: `!=` that treats NULL as a value. `IS NOT` on SQLite, `NOT <=>` on MySQL.
304
- # @return [AST::Predicate]
305
- #
306
- # Null-safe comparison: unlike = and <>, these treat NULL as a value,
307
- # so not_distinct_from? is the one equality that may take nil.
308
- def distinct_from?(value)
309
- DistinctFrom.new(self, value, negated: true)
310
- end
311
-
312
- # `IS NOT DISTINCT FROM`: `=` that treats NULL as a value, so this is the one equality that takes `nil`.
313
- # @return [AST::Predicate]
314
- # @example
315
- # Author.where { :country.not_distinct_from?(nil) }
316
- def not_distinct_from?(value)
317
- DistinctFrom.new(self, value)
318
- end
319
-
320
- # `LIKE 'prefix%'`, the prefix escaped so that a `%` or `_` in it is itself; several prefixes are `OR`ed.
321
- # @return [AST::Predicate]
322
- # @example
323
- # Author.where { :name.start_with?("A", "B") }
324
- def start_with?(*prefixes)
325
- if prefixes.empty?
326
- raise ArgumentError, "start_with? needs at least one prefix"
327
- end
328
- Like.any(self, prefixes.map { |prefix| "#{Like.escape(prefix)}%" })
329
- end
330
-
331
- # `LIKE '%suffix'`, escaped as {#start_with?} escapes.
332
- # @return [AST::Predicate]
333
- def end_with?(*suffixes)
334
- if suffixes.empty?
335
- raise ArgumentError, "end_with? needs at least one suffix"
336
- end
337
- Like.any(self, suffixes.map { |suffix| "%#{Like.escape(suffix)}" })
338
- end
339
-
340
- # `LIKE '%substring%'`, escaped as {#start_with?} escapes.
341
- # @return [AST::Predicate]
342
- # @example
343
- # Post.where { :title.include?("ruby") }
344
- def include?(substring)
345
- Like.new(self, "%#{Like.escape(substring)}%", Like::ESCAPE)
346
- end
347
-
348
- # Whether a PostgreSQL array column holds the element: `@> ARRAY[element]`.
349
- # @return [AST::Predicate]
350
- # @example
351
- # Post.where { :tags.member?("ruby") }
352
- #
353
- # The array comparisons carry the meaning of their Ruby namesakes.
354
- # member? is Enumerable's element test, so an Array argument is
355
- # rejected rather than quietly meaning something Array#member? does
356
- # not; whole-array comparisons go by the Set and Array names.
357
- def member?(element)
358
- if element.is_a?(::Array) || element.is_a?(::Set)
359
- raise ArgumentError,
360
- "member? takes a single element; use superset? to require every element"
361
- end
362
- ArrayPredicate.new(self, :"@>", [element])
363
- end
364
-
365
- # Whether an array column holds every element given: `@>`.
366
- # @return [AST::Predicate]
367
- def superset?(elements)
368
- ArrayPredicate.new(self, :"@>", ArrayPredicate.elements(elements, "superset?"))
369
- end
370
-
371
- # Whether every element of an array column is among those given: `<@`.
372
- # @return [AST::Predicate]
373
- def subset?(elements)
374
- ArrayPredicate.new(self, :"<@", ArrayPredicate.elements(elements, "subset?"))
375
- end
376
-
377
- # Whether an array column and the elements given share any: `&&`.
378
- # @return [AST::Predicate]
379
- # @example
380
- # Post.where { :tags.intersect?(%w[ruby sql]) }
381
- def intersect?(elements)
382
- ArrayPredicate.new(self, :"&&", ArrayPredicate.elements(elements, "intersect?"))
383
- end
384
-
385
- # The JSON at a path into a JSON column, still JSON -- to be dug further, compared with a Ruby value, or asked {#key?} and the rest. A string or a symbol steps into an object, an integer into an array. `#>` on PostgreSQL, `JSON_EXTRACT` on MySQL, `->` on SQLite.
386
- # @return [AST::JsonPath]
387
- # @example
388
- # Doc.select { :meta.dig(:author).as(:author) }
389
- # Doc.where { :meta.dig(:author).key?(:name) }
390
- #
391
- # Reading inside a JSON document, by the name of what Hash does. A
392
- # string or symbol steps into an object, an integer into an array, and
393
- # what comes back is still JSON, the way Hash#dig hands back the
394
- # structure itself -- for a document to be dug into further or asked
395
- # the JSON questions. dig_text gives the value as text instead,
396
- # which is what a comparison wants.
397
- def dig(*path)
398
- JsonPath.new(self, path)
399
- end
400
-
401
- # The value at a path as text, which is what a comparison against a string wants where the JSON type would not do: `#>>` on PostgreSQL, `JSON_UNQUOTE(JSON_EXTRACT(...))` on MySQL, `->>` on SQLite.
402
- # @return [AST::JsonPath]
403
- # @example
404
- # Doc.where { :meta.dig_text(:author, :name) == "alice" }
405
- def dig_text(*path)
406
- JsonPath.new(self, path, json_value: false)
407
- end
408
-
409
- # The document without the keys given, as Hash#except gives it; an expression, for `update_all` to write back.
410
- # @return [AST::JsonExcept]
411
- # @example
412
- # Doc.update_all { { meta: :meta.except(:draft) } }
413
- #
414
- # Keys taken out of a JSON document, by the name of what Hash does,
415
- # and taking keys as Hash#except takes them. Like bury it gives back
416
- # the document changed rather than writing it anywhere.
417
- def except(*keys)
418
- JsonExcept.new(self, keys)
419
- end
420
-
421
- # The document with a value set at a path, as {#dig} reads one; an expression, for `update_all` to write back.
422
- # @return [AST::JsonSet]
423
- # @example
424
- # Doc.update_all { { meta: :meta.bury(:author, :name, "alice") } }
425
- #
426
- # What dig reads, bury sets: the last argument is the value and the
427
- # rest are the path to it. The document comes back changed rather
428
- # than being written anywhere, which update_all is for.
429
- def bury(*path, value)
430
- JsonSet.new(self, path, value)
431
- end
432
-
433
- # Whether the document contains the Ruby document given, which SQL calls containment: `@>` on PostgreSQL, `JSON_CONTAINS` on MySQL. SQLite and MariaDB have none.
434
- # @return [AST::Predicate]
435
- # @example
436
- # Doc.where { :meta.contains?(author: { name: "alice" }) }
437
- #
438
- # Whether the document holds what is given, which SQL calls
439
- # containment. SQLite has no equivalent.
440
- def contains?(value)
441
- JsonContains.new(self, value)
442
- end
443
-
444
- # Whether the object has the key, as Hash#key? asks.
445
- # @return [AST::Predicate]
446
- # @example
447
- # Doc.where { :meta.key?(:author) }
448
- #
449
- # Whether the key is there at all, as Hash#key? asks. Hash has
450
- # has_key? too; one name is enough, and this is the one Ruby's own
451
- # style prefers.
452
- def key?(key)
453
- JsonHasKey.new(self, key)
454
- end
455
-
456
- # The keys of the object as a JSON array, as Hash#keys gives them. Oracle and SQL Server have none.
457
- # @return [AST::JsonKeys]
458
- #
459
- # The keys of the document, as Hash#keys gives them: a JSON array.
460
- def keys
461
- JsonKeys.new(self)
462
- end
463
- end
464
-
465
- # The arithmetic and the bitwise operations on a column or an
466
- # expression. Ruby puts the operators above the comparisons, so
467
- # `:price * :quantity > 100` groups the way it reads, and a number on
468
- # the left -- `20 - :quantity` -- builds the same expression.
469
- #
470
- # Bitwise AND and OR are named, not spelled `&` and `|`: those two are
471
- # AND and OR between conditions and mean nothing else anywhere. The
472
- # rest keep their operators, with {#bitwise_xor} and {#bitwise_not}
473
- # beside `^` and `~` for a reader who would rather have the name.
474
- # Oracle, which has no bitwise operators at all, refuses every one.
475
- #
476
- # @example
477
- # LineItem.where { :price * :quantity > 1000 }
478
- # LineItem.select { :flags.bitwise_and(4).as(:featured) }
479
- #
480
- # Arithmetic builders shared by symbols, qualified columns and
481
- # expressions. Imported into the Symbol refinement like Predications,
482
- # so every method must be defined with def.
483
- module Arithmetics
484
- # `+`; with an Active Support duration on the right, a date moved: `:due_on + 3.days`.
485
- # @return [AST::Arithmetic]
486
- def +(other)
487
- Arithmetic.new(self, :+, other)
488
- end
489
-
490
- # `-`; with a duration on the right, a date moved back.
491
- # @return [AST::Arithmetic]
492
- def -(other)
493
- Arithmetic.new(self, :-, other)
494
- end
495
-
496
- # `*`.
497
- # @return [AST::Arithmetic]
498
- def *(other)
499
- Arithmetic.new(self, :*, other)
500
- end
501
-
502
- # `/`.
503
- # @return [AST::Arithmetic]
504
- def /(other)
505
- Arithmetic.new(self, :/, other)
506
- end
507
-
508
- # `&` is AND, and a column is not a condition, so this refuses:
509
- # {#bitwise_and} is the SQL operator, and `.true?` makes a boolean
510
- # column a condition.
511
- # @raise [ArgumentError]
512
- def &(other)
513
- AST.refuse_logical(:&, "AND", "bitwise_and", other)
514
- end
515
-
516
- # `|` is OR, and refuses here as `&` does.
517
- # @raise [ArgumentError]
518
- def |(other)
519
- AST.refuse_logical(:|, "OR", "bitwise_or", other)
520
- end
521
-
522
- # Bitwise AND: `"flags" & 4`. It is spelled as a name because `&`
523
- # is AND, and a method binds tighter than any comparison, so nothing
524
- # has to be parenthesised to be compared.
525
- # @return [AST::Bitwise]
526
- # @example
527
- # Post.where { :flags.bitwise_and(4) > 0 }
528
- def bitwise_and(other)
529
- Bitwise.new(self, :&, other)
530
- end
531
-
532
- # Bitwise OR.
533
- # @return [AST::Bitwise]
534
- def bitwise_or(other)
535
- Bitwise.new(self, :|, other)
536
- end
537
-
538
- # Bitwise XOR: `#` on PostgreSQL, `^` on MySQL, and the two operations it is made of on SQLite.
539
- # @return [AST::Bitwise]
540
- def ^(other)
541
- Bitwise.new(self, :^, other)
542
- end
543
-
544
- # {#^} under a name.
545
- # @return [AST::Bitwise]
546
- def bitwise_xor(other)
547
- Bitwise.new(self, :^, other)
548
- end
549
-
550
- # A shift left.
551
- # @return [AST::Bitwise]
552
- def <<(other)
553
- Bitwise.new(self, :<<, other)
554
- end
555
-
556
- # A shift right.
557
- # @return [AST::Bitwise]
558
- def >>(other)
559
- Bitwise.new(self, :>>, other)
560
- end
561
-
562
- # Bitwise NOT.
563
- # @return [AST::BitwiseNot]
564
- def ~
565
- BitwiseNot.new(self)
566
- end
567
-
568
- # {#~} under a name. `!` is not the one to reach for: it is Ruby's
569
- # own on a column and answers `false`, which no query ever wanted.
570
- # @return [AST::BitwiseNot]
571
- def bitwise_not
572
- BitwiseNot.new(self)
573
- end
574
- end
575
-
576
- # Arithmetic with the number on the left, imported into the numeric
577
- # refinements: 20 - :quantity builds what :quantity + 20 builds. Only
578
- # a column or an expression on the right means a query; anything else
579
- # goes back to the number through super, so 1 + 2 is 3 inside a block
580
- # too, and 4 & 5 is 4. & and | refuse a query the way they do on a
581
- # column, bitwise_and and its kin carrying those two operations.
582
- # @private
583
- module NumericArithmetics
584
- def +(other)
585
- return super unless other.is_a?(::Symbol) || other.is_a?(Node)
586
- Arithmetic.new(self, :+, other)
587
- end
588
-
589
- def -(other)
590
- return super unless other.is_a?(::Symbol) || other.is_a?(Node)
591
- Arithmetic.new(self, :-, other)
592
- end
593
-
594
- def *(other)
595
- return super unless other.is_a?(::Symbol) || other.is_a?(Node)
596
- Arithmetic.new(self, :*, other)
597
- end
598
-
599
- def /(other)
600
- return super unless other.is_a?(::Symbol) || other.is_a?(Node)
601
- Arithmetic.new(self, :/, other)
602
- end
603
-
604
- def &(other)
605
- return super unless other.is_a?(::Symbol) || other.is_a?(Node)
606
- AST.refuse_logical(:&, "AND", "bitwise_and", other)
607
- end
608
-
609
- def |(other)
610
- return super unless other.is_a?(::Symbol) || other.is_a?(Node)
611
- AST.refuse_logical(:|, "OR", "bitwise_or", other)
612
- end
613
-
614
- def ^(other)
615
- return super unless other.is_a?(::Symbol) || other.is_a?(Node)
616
- Bitwise.new(self, :^, other)
617
- end
618
-
619
- def <<(other)
620
- return super unless other.is_a?(::Symbol) || other.is_a?(Node)
621
- Bitwise.new(self, :<<, other)
622
- end
623
-
624
- def >>(other)
625
- return super unless other.is_a?(::Symbol) || other.is_a?(Node)
626
- Bitwise.new(self, :>>, other)
627
- end
628
-
629
- def bitwise_and(other)
630
- Bitwise.new(self, :&, other)
631
- end
632
-
633
- def bitwise_or(other)
634
- Bitwise.new(self, :|, other)
635
- end
636
-
637
- def bitwise_xor(other)
638
- Bitwise.new(self, :^, other)
639
- end
640
- end
641
-
642
- # An expression a block has built, whatever it was built from. The
643
- # methods here are what every one takes; most subclasses add the
644
- # conditions of {Predications} and the operators of {Arithmetics}.
645
- class Node
646
- # The model travels with the table because some SQL cannot be written
647
- # without knowing the adapter, and a node is built before anything
648
- # knows which one it will be rendered for -- a symbol becomes a node
649
- # inside a refinement, where there is no model to ask. Most nodes
650
- # never look at it and only pass it on.
651
- # @private
652
- def to_arel(table, model)
653
- raise ScriptError, "subclass must override this method"
654
- end
655
-
656
- # The expression under an alias, as {BlockSyntax#as} gives a column
657
- # one.
658
- # @return [AST::As]
659
- def as(alias_name, quote: true)
660
- As.new(self, alias_name, quote: quote)
661
- end
662
-
663
- # An ascending ordering by the expression.
664
- # @return [AST::Ordering]
665
- def asc
666
- Ordering.new(self, :asc)
667
- end
668
-
669
- # A descending ordering by the expression.
670
- # @return [AST::Ordering]
671
- def desc
672
- Ordering.new(self, :desc)
673
- end
674
-
675
- # The expression under a collation, as {BlockSyntax#collate}.
676
- # @return [AST::Collate]
677
- def collate(name)
678
- Collate.new(self, name)
679
- end
680
-
681
- private
682
- # Resolves an operand denoting a column or an expression. A number
683
- # rides along for Arel to write out, which it can do for Integer and
684
- # Float alone: a BigDecimal is quoted, which the adapter spells as
685
- # the exact decimal, and a Rational, which no decimal spells exactly,
686
- # is refused.
687
- def to_arel_operand(operand, table, model)
688
- case operand
689
- when Node then operand.to_arel(table, model)
690
- when :* then Arel.star
691
- when Symbol then table[operand]
692
- when ::BigDecimal, ::Rational then quote_number(operand)
693
- else operand
694
- end
695
- end
696
-
697
- # A bare symbol is a column in every position, the value side of a
698
- # comparison included. The name is checked against the model, since
699
- # a name it has no column for is almost always an enum value spelled
700
- # as a symbol -- which, taken as a column, would quietly compare
701
- # against nothing anyone meant.
702
- def column_operand(name, table, model)
703
- unless model.column_names.include?(name.to_s)
704
- raise ArgumentError,
705
- "#{name.inspect} is no column of #{model.table_name}; an enum " \
706
- "value is written as its string, a column of another table " \
707
- "qualified"
708
- end
709
- table[name]
710
- end
711
-
712
- # A number compares as itself, the way a bound ? does: the typed path
713
- # would cast 99.5 against an integer column to 99 and quietly move
714
- # the boundary. Everything else keeps the column's own
715
- # serialization -- an enum's name, a time's zone, a custom type's
716
- # scaling.
717
- def quote_number(value)
718
- case value
719
- when ::Rational
720
- raise ArgumentError,
721
- "a Rational has no exact SQL spelling; to_d says the decimal meant"
722
- when ::Integer, ::Float, ::BigDecimal
723
- Arel::Nodes.build_quoted(value)
724
- else value
725
- end
726
- end
727
-
728
- # Resolves a function argument: a column or an expression as above,
729
- # anything else a value to be quoted.
730
- def to_arel_argument(arg, table, model)
731
- case arg
732
- when Node, Symbol, ::Rational then to_arel_operand(arg, table, model)
733
- else Arel::Nodes.build_quoted(arg)
734
- end
735
- end
736
- end
737
-
738
- # `&`, `|` and `!` -- AND, OR and NOT, what joins conditions into one
739
- # and negates them. A predicate carries them, and so do {Sql} and
740
- # {Operation}, whose SQL is read as a condition the moment it is
741
- # combined like one.
742
- #
743
- # Included in those three and never imported into a refinement, as
744
- # {Predications} is: a bare symbol is a column, and a column is not a
745
- # condition.
746
- module Connectives
747
- # `AND`.
748
- # @return [AST::Predicate]
749
- # @example
750
- # Author.where { :age.between?(20, 40) & (:country == "JP") }
751
- # Post.where { sql("score > 0") & (:published == true) }
752
- def &(other)
753
- And.new(self, other)
754
- end
755
-
756
- # `OR`.
757
- # @return [AST::Predicate]
758
- def |(other)
759
- Or.new(self, other)
760
- end
761
-
762
- # `NOT (condition)`, negating anything; the negations SQL spells for
763
- # itself, `IS NOT NULL` and its kin, have names of their own under
764
- # {Predications}.
765
- # @return [AST::Predicate]
766
- # @example
767
- # Author.where { !:name.like?("A%") }
768
- #
769
- # Being here rather than only on Predicate is what keeps `!` on Sql
770
- # and Operation from falling through to Ruby's own, which would
771
- # quietly answer false.
772
- def !
773
- Not.new(self)
774
- end
775
- end
776
-
777
- # A condition: what a comparison or one of the tests gives back, and
778
- # what `where`, `having` and a join's block hand the relation.
779
- class Predicate < Node
780
- include Connectives
781
- end
782
-
783
- # A literal standing where an expression would: `select { value(0).as(:depth) }`.
784
- #
785
- # Values reach the SQL quoted wherever they appear as an operand, but a
786
- # bare Ruby literal at the top of a select list is refused as saying
787
- # nothing. `value` is the spelling that quotes it there, and it carries
788
- # the predications with it, so a literal can be compared and combined
789
- # like anything else.
790
- class Value < Node
791
- include Predications
792
- include Arithmetics
793
-
794
- # @private
795
- attr_reader :value
796
-
797
- def initialize(value)
798
- @value = value
799
- end
800
-
801
- # @private
802
- def to_arel(_table, _model)
803
- Arel::Nodes.build_quoted(value)
804
- end
805
- end
806
-
807
- # SQL as written: `sql("length(name) > ?", 10)`. The ? and :name
808
- # placeholders take quoted values through sanitize_sql_array, which
809
- # needs the connection, so the binds wait here until the model is
810
- # known. Without binds the statement passes untouched -- which is
811
- # what leaves PostgreSQL's ? operators writable, since only the
812
- # positional-bind rewrite reads ? as a placeholder.
813
- #
814
- # As an operand the statement is parenthesized: its precedence is
815
- # whatever was written inside. The top of a select list gets it bare,
816
- # through field_arel, where parentheses would refuse an alias written
817
- # into the string.
818
- class Sql < Node
819
- include Predications
820
- include Arithmetics
821
- # After Arithmetics, whose & and | refuse: here they are AND and OR.
822
- include Connectives
823
-
824
- # @private
825
- attr_reader :statement, :binds
826
-
827
- def initialize(statement, binds)
828
- unless statement.is_a?(::String)
829
- raise ArgumentError,
830
- "sql takes the statement as a string, not #{statement.inspect}"
831
- end
832
- @statement = statement
833
- @binds = binds
834
- end
835
-
836
- # @private
837
- def to_arel(_table, model)
838
- Arel::Nodes::Grouping.new(field_arel(model))
839
- end
840
-
841
- # @private
842
- def field_arel(model)
843
- return Arel.sql(statement) if binds.empty?
844
-
845
- Arel.sql(model.sanitize_sql_array([statement, *binds]))
846
- end
847
- end
848
-
849
- # CASE, in both of the shapes SQL has for it. With an operand, each
850
- # `when` is something to compare it against; without one, each `when` is
851
- # a condition of its own.
852
- #
853
- # Every method returns a new node rather than adding to this one, so a
854
- # case kept in a variable can be branched from more than once.
855
- class Case < Node
856
- include Predications
857
- include Arithmetics
858
-
859
- # Having no ELSE is not the same as an ELSE of nil, and nil is what an
860
- # omitted argument looks like, so the absence needs a value of its own.
861
- NOTHING = Object.new.freeze
862
- private_constant :NOTHING
863
-
864
- # @private
865
- attr_reader :operand, :whens, :default
866
-
867
- def initialize(operand = nil, whens = [], default = NOTHING)
868
- @operand = operand
869
- @whens = whens
870
- @default = default
871
- end
872
-
873
- # The next `WHEN`: a value to compare the operand against, or a condition as a value or a block.
874
- # @return [AST::Case::When]
875
- def when(value = nil, &block)
876
- When.new(self, Case.argument(:when, value, block))
877
- end
878
-
879
- # `THEN`, which belongs after a `when`; here it says so.
880
- # @raise [ArgumentError]
881
- #
882
- # Kernel#then is on every object, so `then` in the wrong place would be
883
- # answered by it -- with no block, silently, with an Enumerator.
884
- def then(*)
885
- raise ArgumentError, "then follows a when, and there is none to follow here"
886
- end
887
-
888
- # `ELSE value`, as a value or a block, closing the CASE. Without one the CASE gives NULL where no `when` matched.
889
- # @return [AST::Case]
890
- def else(value = nil, &block)
891
- Case.new(operand, whens, Case.argument(:else, value, block))
892
- end
893
-
894
- # @private
895
- def to_arel(table, model)
896
- raise ArgumentError, "case needs a when before it means anything" if whens.empty?
897
-
898
- node = operand ? Arel::Nodes::Case.new(to_arel_operand(operand, table, model))
899
- : Arel::Nodes::Case.new
900
- whens.each do |condition, result|
901
- node.when(to_arel_argument(condition, table, model)).
902
- then(to_arel_argument(result, table, model))
903
- end
904
- node.else(to_arel_argument(default, table, model)) unless default.equal?(NOTHING)
905
- node
906
- end
907
-
908
- # A value or a block, and exactly one of them: the block is what makes
909
- # `when { :age >= 60 }` read like the blocks around it, and the value is
910
- # what makes `when(10)` possible at all.
911
- # @private
912
- def self.argument(name, value, block)
913
- if block
914
- raise ArgumentError, "#{name} takes a value or a block, not both" unless value.nil?
915
- return block.call
916
- end
917
- raise ArgumentError, "#{name} needs a value or a block" if value.nil?
918
- value
919
- end
920
-
921
- # What a `when` is until its `then` arrives. A Node so that using it
922
- # as one says what is missing rather than reaching Active Record as
923
- # something it cannot read.
924
- class When < Node
925
- def initialize(kase, condition)
926
- @kase = kase
927
- @condition = condition
928
- end
929
-
930
- # `THEN value`, as a value or a block, for the `when` before it.
931
- # @return [AST::Case]
932
- def then(value = nil, &block)
933
- Case.new(@kase.operand,
934
- @kase.whens + [[@condition, Case.argument(:then, value, block)]],
935
- @kase.default)
936
- end
937
-
938
- # @private
939
- def to_arel(_table, _model)
940
- raise ArgumentError, "when needs a matching then"
941
- end
942
- end
943
- end
944
-
945
- # A path into a JSON document, spelled the two ways the adapters want it.
946
- # Shared, because reading a value and setting one walk the same path.
947
- # @private
948
- module JsonSteps
949
- def check_steps(path, called)
950
- raise ArgumentError, "#{called} needs a key or an index" if path.empty?
951
- path.each do |step|
952
- next if step.is_a?(::Integer) || step.is_a?(::String) || step.is_a?(::Symbol)
953
- raise ArgumentError, "a step is a key or an array index, not #{step.inspect}"
954
- end
955
- path
956
- end
957
-
958
- # PostgreSQL takes the steps as a text array, where every element is
959
- # quoted so that a comma or a brace in a key is part of it. except
960
- # writes its keys the same way, which are steps of no one path.
961
- def steps_array(steps = path)
962
- "{#{steps.map { |step| %("#{escape_step(step)}") }.join(',')}}"
963
- end
964
-
965
- # MySQL and SQLite take a path expression instead, where an integer is
966
- # a subscript and a name that is not plain has to be quoted.
967
- def dollar_path
968
- path.inject(+"$") { |so_far, step| so_far << dollar_step(step) }
969
- end
970
-
971
- def dollar_step(step)
972
- return "[#{step}]" if step.is_a?(::Integer)
973
- name = step.to_s
974
- "." + (name.match?(/\A[[:alpha:]_][[:alnum:]_]*\z/) ?
975
- name : %("#{escape_step(step)}"))
976
- end
977
-
978
- def escape_step(step)
979
- step.to_s.gsub("\\", "\\\\").gsub('"', '\\"')
980
- end
981
- end
982
-
983
- # What a dug value may be compared with. dig_text gives text on every
984
- # adapter, and what a text value compared with a number means is a
985
- # question the three answer three ways: `dig_text(:n) == 5` is true on
986
- # SQLite, an error on PostgreSQL and true on MySQL, while
987
- # `dig_text(:flag) == true` is true, an error, and false. cast is what
988
- # says which type was meant, and then all three agree.
989
- #
990
- # dig, bury and except give JSON, and a JSON comparison belongs to the
991
- # JSON types -- jsonb and MySQL's -- where numbers compare as numbers
992
- # and documents structurally, key order and spelling aside. The Ruby
993
- # value goes in as a JSON literal, and the adapters without such a
994
- # type refuse it from JsonLiteral when the SQL is written, which is
995
- # when the adapter is known.
996
- #
997
- # A string against dig_text, and anything the block itself built -- a
998
- # column, a function, another dug value -- go through untouched.
999
- #
1000
- # Arithmetic and the bit operators are refused outright on both sides:
1001
- # `dig_text(:n) + 1` is 6 on SQLite, an error on PostgreSQL and 6.0 on
1002
- # MariaDB, and an expression on the right does not change what the
1003
- # dug side is.
1004
- module JsonComparable
1005
- %i[== != < <= > >=].each do |operator|
1006
- define_method(operator) do |other|
1007
- super(comparison_value(other))
1008
- end
1009
- end
1010
-
1011
- def in?(values) = super(comparison_set(values))
1012
- def not_in?(values) = super(comparison_set(values))
1013
- def between?(min, max) = super(comparison_value(min), comparison_value(max))
1014
- def not_between?(min, max) = super(comparison_value(min), comparison_value(max))
1015
-
1016
- %i[+ - * / & | ^ << >> bitwise_and bitwise_or bitwise_xor].each do |operator|
1017
- define_method(operator) do |_other|
1018
- raise ArgumentError, arithmetic_refusal(operator)
1019
- end
1020
- end
1021
-
1022
- def ~
1023
- raise ArgumentError, arithmetic_refusal(:~)
1024
- end
1025
-
1026
- def bitwise_not
1027
- raise ArgumentError, arithmetic_refusal(:bitwise_not)
1028
- end
1029
-
1030
- private
1031
- # nil is left to the comparison itself, which says to use null?, and
1032
- # so is anything the block built rather than wrote as a literal.
1033
- def comparison_value(other)
1034
- return other if other.nil? || other.is_a?(Node) || other.is_a?(::Symbol) ||
1035
- other.is_a?(Arel::Nodes::Node) ||
1036
- other.is_a?(Arel::Attributes::Attribute) ||
1037
- other.is_a?(ActiveRecord::Relation)
1038
- return json_literal(other) if json_value?
1039
- return other if other.is_a?(::String)
1040
-
1041
- raise ArgumentError,
1042
- "dig_text gives text, and comparing it with #{other.inspect} means " \
1043
- "something different on every adapter; cast it to the type meant"
1044
- end
1045
-
1046
- def json_literal(other)
1047
- case other
1048
- when ::String, ::Integer, ::Float, ::BigDecimal, true, false, ::Hash, ::Array
1049
- JsonLiteral.new(other)
1050
- when ::Rational
1051
- raise ArgumentError,
1052
- "a Rational has no exact SQL spelling; to_d says the decimal meant"
1053
- else
1054
- raise ArgumentError,
1055
- "#{json_source} gives JSON, and #{other.inspect} has no JSON " \
1056
- "spelling; dig_text gives the value"
1057
- end
1058
- end
1059
-
1060
- def comparison_set(values)
1061
- case values
1062
- when ActiveRecord::Relation then values
1063
- when ::Range
1064
- In::QuotedRange.new(comparison_value(values.begin),
1065
- comparison_value(values.end), values.exclude_end?)
1066
- else values.map { |value| comparison_value(value) }
1067
- end
1068
- end
1069
-
1070
- def arithmetic_refusal(operator)
1071
- json_value? ?
1072
- "#{json_source} gives JSON, and #{operator} on it means something " \
1073
- "different on every adapter; cast dig_text to the type meant" :
1074
- "dig_text gives text, and #{operator} on it means something " \
1075
- "different on every adapter; cast it to the type meant"
1076
- end
1077
- end
1078
-
1079
- # A Ruby value on the JSON side of a comparison, which jsonb and
1080
- # MySQL's JSON type answer alike: numbers compare as numbers and
1081
- # documents structurally. SQLite and MariaDB have only the text of
1082
- # each -- spelling and key order deciding what equality means -- and
1083
- # refuse here. PostgreSQL needs no cast, an untyped literal beside a
1084
- # jsonb operand coercing to jsonb; MySQL is told CAST(... AS JSON),
1085
- # since a bare string beside JSON would be a JSON string, and every
1086
- # string outranks every number in its ordering.
1087
- class JsonLiteral < Node
1088
- # @private
1089
- attr_reader :value
1090
-
1091
- def initialize(value)
1092
- @value = value
1093
- end
1094
-
1095
- # @private
1096
- def to_arel(_table, model)
1097
- Dialect.for(model).json_literal(Arel::Nodes.build_quoted(JSON.generate(value)), model)
1098
- end
1099
- end
1100
-
1101
- # The JSON operations read a document, and what dig gives is one:
1102
- # `dig(:author).key?(:email)` and `dig(:tags).contains?(...)` are the
1103
- # same question asked of a part of it, and the adapters answer them
1104
- # alike. What dig_text gives is text, and reading that as a document
1105
- # again is where they part company: SQLite parses it back and MySQL
1106
- # takes it as written, where PostgreSQL has no such function for text.
1107
- module JsonDocument
1108
- %i[dig dig_text key? keys contains? bury except].each do |name|
1109
- define_method(name) do |*args|
1110
- unless json_value?
1111
- raise ArgumentError,
1112
- "dig_text gives text, and #{name} reads JSON; dig keeps it"
1113
- end
1114
- super(*args)
1115
- end
1116
- end
1117
- end
1118
-
1119
- # JSON the query computes rather than reads out of a document: always
1120
- # a JSON value, with no dig_text counterpart for JsonComparable's
1121
- # advice to name. Included after JsonComparable, whose own
1122
- # arithmetic_refusal it overrides.
1123
- module ComputedJson
1124
- # @private
1125
- def json_value?
1126
- true
1127
- end
1128
-
1129
- private
1130
- def arithmetic_refusal(operator)
1131
- "#{json_source} gives JSON, and #{operator} on it means " \
1132
- "something different on every adapter"
1133
- end
1134
- end
1135
-
1136
- # Reading inside a JSON document, which is what {Predications#dig} and
1137
- # {Predications#dig_text} build. Every adapter can do it and no two
1138
- # spell it alike: PostgreSQL walks an array of steps, SQLite has the
1139
- # operators with a $ path, and MySQL has the functions -- which is what
1140
- # this uses for that family, since MariaDB answers to the same adapter
1141
- # and has no -> at all.
1142
- #
1143
- # The path is turned into a string either way, so a key with a space or
1144
- # a quote in it travels as itself rather than having to be refused.
1145
- # What a dug value compares with is {JsonComparable}'s to say.
1146
- class JsonPath < Node
1147
- include Predications
1148
- include Arithmetics
1149
- include JsonSteps
1150
- include JsonComparable
1151
- include JsonDocument
1152
-
1153
- # @private
1154
- attr_reader :operand, :path
1155
-
1156
- def initialize(operand, path, json_value: true)
1157
- @operand = operand
1158
- @path = check_steps(path, "dig")
1159
- @json_value = json_value
1160
- end
1161
-
1162
- # @private
1163
- def json_value?
1164
- @json_value
1165
- end
1166
-
1167
- # @private
1168
- def to_arel(table, model)
1169
- Dialect.for(model).json_path(
1170
- to_arel_operand(operand, table, model),
1171
- dollar_path, steps_array, json_value?, model)
1172
- end
1173
-
1174
- private
1175
- def json_source
1176
- "dig"
1177
- end
1178
- end
1179
-
1180
- # Setting a value inside a JSON document, which is what bury does to what
1181
- # dig reads. The document comes back changed rather than being written
1182
- # anywhere; update_all is what writes it.
1183
- class JsonSet < Node
1184
- include Predications
1185
- include JsonSteps
1186
- include JsonComparable
1187
-
1188
- # @private
1189
- attr_reader :operand, :path, :value
1190
-
1191
- def initialize(operand, path, value)
1192
- @operand = operand
1193
- @path = check_steps(path, "bury")
1194
- @value = value
1195
- end
1196
-
1197
- # Always JSON, which is what the comparison guard asks.
1198
- # @private
1199
- def json_value?
1200
- true
1201
- end
1202
-
1203
- # @private
1204
- def to_arel(table, model)
1205
- Dialect.for(model).json_set(
1206
- to_arel_operand(operand, table, model),
1207
- steps_array, dollar_path, value,
1208
- (to_arel_operand(value, table, model) if expression?), model)
1209
- end
1210
-
1211
- private
1212
- def expression?
1213
- value.is_a?(Node) || value.is_a?(::Symbol)
1214
- end
1215
-
1216
- def json_source
1217
- "bury"
1218
- end
1219
- end
1220
-
1221
- # Keys taken out of a JSON document. PostgreSQL subtracts them, the
1222
- # other two remove a path apiece.
1223
- class JsonExcept < Node
1224
- include Predications
1225
- include JsonSteps
1226
- include JsonComparable
1227
-
1228
- # @private
1229
- attr_reader :operand, :keys
1230
-
1231
- def initialize(operand, keys)
1232
- @operand = operand
1233
- @keys = check_keys(keys)
1234
- end
1235
-
1236
- # @private
1237
- def json_value?
1238
- true
1239
- end
1240
-
1241
- # @private
1242
- def to_arel(table, model)
1243
- Dialect.for(model).json_remove(
1244
- to_arel_operand(operand, table, model),
1245
- keys.map { |key| "$#{dollar_step(key)}" },
1246
- steps_array(keys), model)
1247
- end
1248
-
1249
- private
1250
- # Keys, as Hash#except takes them: an index into an array is not what
1251
- # the name says anywhere, and is bury's business through a path.
1252
- def check_keys(keys)
1253
- raise ArgumentError, "except needs a key" if keys.empty?
1254
- keys.each do |key|
1255
- next if key.is_a?(::String) || key.is_a?(::Symbol)
1256
- raise ArgumentError,
1257
- "except takes keys of the document, not #{key.inspect}"
1258
- end
1259
- keys
1260
- end
1261
-
1262
- def json_source
1263
- "except"
1264
- end
1265
- end
1266
-
1267
- # JSON containment: whether the document holds what is given.
1268
- class JsonContains < Predicate
1269
- # @private
1270
- attr_reader :operand, :value
1271
-
1272
- def initialize(operand, value)
1273
- @operand = operand
1274
- @value = value
1275
- end
1276
-
1277
- # @private
1278
- def to_arel(table, model)
1279
- Dialect.for(model).json_contains(
1280
- to_arel_operand(operand, table, model),
1281
- Arel::Nodes.build_quoted(JSON.generate(value)), model)
1282
- end
1283
- end
1284
-
1285
- # Whether a key is in the document. PostgreSQL's spelling is the ?
1286
- # operator rather than jsonb_exists, the function it is shorthand for,
1287
- # because a GIN index matches the operator and never the function. A
1288
- # ? is a bind placeholder only to sanitize_sql, which none of the SQL
1289
- # written here passes through.
1290
- class JsonHasKey < Predicate
1291
- # @private
1292
- attr_reader :operand, :key
1293
-
1294
- def initialize(operand, key)
1295
- @operand = operand
1296
- @key = key
1297
- end
1298
-
1299
- # @private
1300
- def to_arel(table, model)
1301
- Dialect.for(model).json_has_key(
1302
- to_arel_operand(operand, table, model),
1303
- Arel::Nodes.build_quoted(key.to_s),
1304
- Arel::Nodes.build_quoted("$.#{key}"), model)
1305
- end
1306
- end
1307
-
1308
- # The keys of a JSON document, as Hash#keys gives them: a JSON array.
1309
- # Only the MySQL family has a function for it; the other two reach the
1310
- # same array through a subquery over their key-listing functions. The
1311
- # type guard is what makes all four answer alike: the keys of what is
1312
- # not an object are NULL rather than SQLite's array indices or
1313
- # PostgreSQL's error, and the keys of {} are [] rather than
1314
- # PostgreSQL's NULL, jsonb_agg over no rows.
1315
- class JsonKeys < Node
1316
- include Predications
1317
- include JsonComparable
1318
- include ComputedJson
1319
-
1320
- # @private
1321
- attr_reader :operand
1322
-
1323
- def initialize(operand)
1324
- @operand = operand
1325
- end
1326
-
1327
- # @private
1328
- def to_arel(table, model)
1329
- Dialect.for(model).json_keys(to_arel_operand(operand, table, model), model)
1330
- end
1331
-
1332
- private
1333
- def json_source
1334
- "keys"
1335
- end
1336
- end
1337
-
1338
- # A JSON document built in the row: json_array from the values given,
1339
- # json_object from a Ruby hash. SQLite and the MySQL family both say
1340
- # the standard names; PostgreSQL is asked to build jsonb, whose
1341
- # documents the other JSON operations here read.
1342
- class JsonBuild < Node
1343
- include Predications
1344
- include JsonComparable
1345
- include ComputedJson
1346
-
1347
- # @private
1348
- attr_reader :kind, :values
1349
-
1350
- def initialize(kind, values)
1351
- @kind = kind
1352
- @values = kind == :object ? check_pairs(values) : values
1353
- end
1354
-
1355
- # @private
1356
- def to_arel(table, model)
1357
- dialect = Dialect.for(model)
1358
- if kind == :array
1359
- dialect.json_build(:array, nil,
1360
- values.map { |value| build_argument(value, dialect, table, model) }, model)
1361
- else
1362
- dialect.json_build(:object, values.keys.map(&:to_s),
1363
- values.values.map { |value| build_argument(value, dialect, table, model) }, model)
1364
- end
1365
- end
1366
-
1367
- private
1368
- # An expression is itself and a bare scalar is quoted; a document or
1369
- # a boolean the dialect embeds as JSON, as bury takes it.
1370
- def build_argument(value, dialect, table, model)
1371
- case value
1372
- when Node, ::Symbol then to_arel_operand(value, table, model)
1373
- when ::Hash, ::Array, true, false then dialect.json_build_argument(value, model)
1374
- when ::Rational then quote_number(value)
1375
- else Arel::Nodes.build_quoted(value)
1376
- end
1377
- end
1378
-
1379
- # The keys come from Ruby as Hash keys rather than alternating with
1380
- # the values as SQL has them, which is what keeps a bare symbol
1381
- # free to mean a column on the value side. Anything but a name is
1382
- # refused here, before the adapters answer a NULL key three ways.
1383
- def check_pairs(pairs)
1384
- unless pairs.is_a?(::Hash)
1385
- raise ArgumentError,
1386
- "json_object takes a hash of keys to values, not #{pairs.inspect}"
1387
- end
1388
- pairs.each_key do |key|
1389
- next if key.is_a?(::String) || key.is_a?(::Symbol)
1390
- raise ArgumentError,
1391
- "a key of json_object is a string or a symbol, not #{key.inspect}"
1392
- end
1393
- pairs
1394
- end
1395
-
1396
- def json_source
1397
- "json_#{kind}"
1398
- end
1399
- end
1400
-
1401
- # GROUP BY GROUPING SETS / ROLLUP / CUBE: several groupings asked for at
1402
- # once, the totals of each coming back beside the rows. PostgreSQL has
1403
- # all three and the MySQL family rollup alone; the block raises for the
1404
- # rest before it gets this far.
1405
- #
1406
- # Each set is a list of its own, so grouping_sets takes lists and rollup
1407
- # and cube take the columns themselves.
1408
- class GroupingSets < Node
1409
- # @private
1410
- KINDS = {
1411
- grouping_sets: Arel::Nodes::GroupingSet,
1412
- rollup: Arel::Nodes::RollUp,
1413
- cube: Arel::Nodes::Cube,
1414
- }.freeze
1415
-
1416
- # @private
1417
- attr_reader :kind, :sets
1418
-
1419
- def initialize(kind, sets)
1420
- raise ArgumentError, "#{kind} needs something to group by" if sets.empty?
1421
- @kind = kind
1422
- @sets = sets
1423
- end
1424
-
1425
- # @private
1426
- def to_arel(table, model)
1427
- return with_rollup(table, model) if Dialect.for(model).grouping_by_with_rollup?
1428
-
1429
- KINDS.fetch(kind).new(
1430
- if kind == :grouping_sets
1431
- sets.map do |set|
1432
- Arel::Nodes::GroupingElement.new(
1433
- Array(set).map { |column| to_arel_operand(column, table, model) })
1434
- end
1435
- else
1436
- sets.map { |column| to_arel_operand(column, table, model) }
1437
- end)
1438
- end
1439
-
1440
- private
1441
- # The MySQL family spells rollup WITH ROLLUP, trailing the whole
1442
- # group list rather than wrapping a list of its own -- which is also
1443
- # why a rollup cannot stand beside other group entries there. The
1444
- # columns are compiled by the connection's own visitor, so their
1445
- # quoting is the adapter's.
1446
- def with_rollup(table, model)
1447
- columns = sets.map { |column| to_arel_operand(column, table, model) }
1448
- sql = model.with_connection do |connection|
1449
- columns.map { |column| connection.visitor.compile(column) }.join(", ")
1450
- end
1451
- Arel.sql("#{sql} WITH ROLLUP")
1452
- end
1453
- end
1454
-
1455
- # A column of a named table, which is what `:posts[:author_id]` builds
1456
- # (see {BlockSyntax#[]}): the spelling for another table's column in a
1457
- # join's ON or a query over one.
1458
- class Column < Node
1459
- include Predications
1460
- include Arithmetics
1461
-
1462
- # @private
1463
- attr_reader :table_name, :column_name
1464
-
1465
- def initialize(table_name, column_name)
1466
- @table_name = table_name
1467
- @column_name = column_name
1468
- end
1469
-
1470
- # @private
1471
- def to_arel(_table, _model)
1472
- Arel::Table.new(table_name)[column_name]
1473
- end
1474
- end
1475
-
1476
- # Arithmetic on columns and expressions. Ruby's precedence puts these
1477
- # above the comparison operators, so :price * :quantity > 100 groups the
1478
- # way it reads.
1479
- #
1480
- # A Duration on the right moves a date: `:due_on + 3.days`. No two
1481
- # families spell the move alike, so the dialect writes it, a part of the
1482
- # duration at a time.
1483
- class Arithmetic < Node
1484
- include Predications
1485
- include Arithmetics
1486
-
1487
- # Active Support's parts, as the units the SQL takes. A week is seven
1488
- # days: SQLite and Oracle have no week.
1489
- # @private
1490
- UNITS = {
1491
- years: :year, months: :month, weeks: :day, days: :day,
1492
- hours: :hour, minutes: :minute, seconds: :second,
1493
- }.freeze
1494
-
1495
- # @private
1496
- DATE_UNITS = %i[year month day].freeze
1497
-
1498
- # @private
1499
- attr_reader :left, :operator, :right
1500
-
1501
- def initialize(left, operator, right)
1502
- @left = left
1503
- @operator = operator
1504
- @right = right
1505
- end
1506
-
1507
- # @private
1508
- def to_arel(table, model)
1509
- arel_left = to_arel_operand(left, table, model)
1510
- return move_date(arel_left, model) if right.is_a?(::ActiveSupport::Duration)
1511
- # The operator dispatches Arel's Math, which a bare number carries
1512
- # none of; quoted, it is a node with the same methods.
1513
- arel_left = Arel::Nodes.build_quoted(arel_left) if arel_left.is_a?(::Numeric)
1514
- arel_left.public_send(operator, to_arel_operand(right, table, model))
1515
- end
1516
-
1517
- # SQLite has no date type, and its datetime() gives whatever it is
1518
- # handed a time of day, so a date column moved by a day would come
1519
- # back a midnight and sort past the same day written bare. Its
1520
- # dialect has date() for what is a date to begin with, and this is
1521
- # what says so: a column the model declares a date, CURRENT_DATE, or
1522
- # one of those already moved by a date's units. The other families
1523
- # keep the type themselves and never ask.
1524
- # @private
1525
- def self.date_operand?(operand, model)
1526
- case operand
1527
- when ::Symbol then model.type_for_attribute(operand).type == :date
1528
- when DatetimeValueFunction then operand.name == "CURRENT_DATE"
1529
- when Arithmetic
1530
- operand.right.is_a?(::ActiveSupport::Duration) &&
1531
- date_operand?(operand.left, model) &&
1532
- operand.right.parts.keys.all? { |part| DATE_UNITS.include?(UNITS[part]) }
1533
- else false
1534
- end
1535
- end
1536
-
1537
- private
1538
- # Each amount is written into the SQL as a number, and a fraction
1539
- # of a unit is not one every family takes, so it has to be a whole
1540
- # one.
1541
- def move_date(date, model)
1542
- unless operator == :+ || operator == :-
1543
- raise ArgumentError,
1544
- "a duration is added to a date or subtracted from it, not #{operator}"
1545
- end
1546
- dialect = Dialect.for(model)
1547
- date_only = Arithmetic.date_operand?(left, model)
1548
- right.parts.reduce(date) do |arel, (part, amount)|
1549
- unless amount.is_a?(::Integer)
1550
- raise ArgumentError, "#{amount.inspect} #{part} is not a whole number of them"
1551
- end
1552
- unit = UNITS.fetch(part)
1553
- amount *= 7 if part == :weeks
1554
- dialect.add_interval(arel, amount, unit, operator == :-,
1555
- date_only && DATE_UNITS.include?(unit))
1556
- end
1557
- end
1558
- end
1559
-
1560
- # What the bitwise operations refuse. Both refusals are there because
1561
- # the same Ruby would otherwise mean different things per adapter: MySQL
1562
- # and SQLite take a boolean for the one bit it is stored as, so
1563
- # `published.bitwise_and(active)` would quietly be the AND it looks
1564
- # like, while PostgreSQL has no such operator and would say so.
1565
- # @private
1566
- module BitwiseOperands
1567
- private
1568
- # Oracle is the family with no bitwise operators at all -- BITAND
1569
- # is a function, with no OR, XOR, shift or NOT beside it -- so the
1570
- # node refuses there before its server has to.
1571
- def check_operators_exist(model)
1572
- return if Dialect.for(model).bitwise_operators_supported?
1573
- raise NotImplementedError,
1574
- "the bitwise operations have no equivalent on " \
1575
- "#{model.connection_db_config.adapter}"
1576
- end
1577
-
1578
- # A predicate alone is refused. The escape hatches are not: what
1579
- # sql() or op() holds is as often an expression as a condition, and
1580
- # here it is being read as the former.
1581
- def check_operand(operand)
1582
- return operand unless operand.is_a?(Predicate)
1583
- raise ArgumentError,
1584
- "a condition cannot be an operand of a bitwise operation; " \
1585
- "& and | between conditions are AND and OR"
1586
- end
1587
-
1588
- # Only the unqualified column can be checked, since that is the one
1589
- # the model is known to have.
1590
- def check_not_boolean(operand, model)
1591
- return unless operand.is_a?(::Symbol)
1592
- return unless model.type_for_attribute(operand).type == :boolean
1593
- raise ArgumentError,
1594
- "#{operand.inspect} is a boolean column, which the bitwise " \
1595
- "operations do not take; #{operand.inspect}.true? is the condition"
1596
- end
1597
- end
1598
-
1599
- # SQL's bitwise operations. Each parenthesises itself, which is what
1600
- # keeps the grouping the Ruby asked for: PostgreSQL gives & and | the
1601
- # same precedence and reads a | b & c from the left, where
1602
- # a.bitwise_or(b.bitwise_and(c)) means the other thing.
1603
- class Bitwise < Node
1604
- include Predications
1605
- include Arithmetics
1606
- include BitwiseOperands
1607
-
1608
- # @private
1609
- NODES = {
1610
- :& => Arel::Nodes::BitwiseAnd,
1611
- :| => Arel::Nodes::BitwiseOr,
1612
- :<< => Arel::Nodes::BitwiseShiftLeft,
1613
- :>> => Arel::Nodes::BitwiseShiftRight,
1614
- }.freeze
1615
-
1616
- # @private
1617
- attr_reader :left, :operator, :right
1618
-
1619
- def initialize(left, operator, right)
1620
- @left = left
1621
- @operator = operator
1622
- @right = check_operand(right)
1623
- end
1624
-
1625
- # @private
1626
- def to_arel(table, model)
1627
- check_operators_exist(model)
1628
- check_not_boolean(left, model)
1629
- check_not_boolean(right, model)
1630
- arel_left = to_arel_operand(left, table, model)
1631
- arel_right = to_arel_argument(right, table, model)
1632
- Arel::Nodes::Grouping.new(
1633
- if operator == :^
1634
- xor(arel_left, arel_right, model)
1635
- else
1636
- NODES.fetch(operator).new(arel_left, arel_right)
1637
- end)
1638
- end
1639
-
1640
- private
1641
- # Arel has a node for XOR, but it writes ^ on every adapter, and ^ is
1642
- # exponentiation to PostgreSQL -- a wrong answer rather than an error.
1643
- # PostgreSQL's own spelling, #, is where a comment starts on MySQL, so
1644
- # it cannot be the portable one either. SQLite has no XOR at all;
1645
- # (a | b) - (a & b) is it, at the cost of naming each operand twice.
1646
- def xor(left, right, model)
1647
- Dialect.for(model).bitwise_xor(left, right)
1648
- end
1649
- end
1650
-
1651
- # ~, which every adapter has. MySQL answers with the unsigned 64-bit
1652
- # number where the others answer with a negative one; the bits are the
1653
- # same, and only reading the value back tells them apart.
1654
- class BitwiseNot < Node
1655
- include Predications
1656
- include Arithmetics
1657
- include BitwiseOperands
1658
-
1659
- # @private
1660
- attr_reader :operand
1661
-
1662
- def initialize(operand)
1663
- @operand = check_operand(operand)
1664
- end
1665
-
1666
- # @private
1667
- def to_arel(table, model)
1668
- check_operators_exist(model)
1669
- check_not_boolean(operand, model)
1670
- Arel::Nodes::Grouping.new(
1671
- Arel::Nodes::BitwiseNot.new(to_arel_operand(operand, table, model)))
1672
- end
1673
- end
1674
-
1675
- # OVER, on the two things that can carry a window: an aggregate, and a
1676
- # function.
1677
- module Windowing
1678
- # `OVER ()`, an empty window to be filled by {Over#partition},
1679
- # {Over#order}, {Over#rows} and {Over#range}.
1680
- # @return [AST::Over]
1681
- # @example
1682
- # Author.select { avg(:age).over.partition(:country).as(:country_average) }
1683
- # Post.select { sum(:likes).over.order(:created_at).rows(..0).as(:running) }
1684
- def over
1685
- Over.new(self)
1686
- end
1687
- end
1688
-
1689
- # A function with a window. The window is built by chaining, the way
1690
- # Arel's own is, and each method returns a new node rather than adding to
1691
- # this one, so a window can be finished more than one way.
1692
- class Over < Node
1693
- include Predications
1694
- include Arithmetics
1695
-
1696
- # @private
1697
- attr_reader :function, :partitions, :orders, :frame
1698
-
1699
- def initialize(function, partitions = [], orders = [], frame = nil)
1700
- @function = function
1701
- @partitions = partitions
1702
- @orders = orders
1703
- @frame = frame
1704
- end
1705
-
1706
- # `PARTITION BY`, the columns or expressions given.
1707
- # @return [AST::Over]
1708
- def partition(*exprs)
1709
- raise ArgumentError, "partition needs an expression" if exprs.empty?
1710
- Over.new(function, partitions + exprs, orders, frame)
1711
- end
1712
-
1713
- # `ORDER BY` within the window: columns, or orderings such as `:age.desc`.
1714
- # @return [AST::Over]
1715
- def order(*exprs)
1716
- raise ArgumentError, "order needs an expression" if exprs.empty?
1717
- Over.new(function, partitions, orders + exprs, frame)
1718
- end
1719
-
1720
- # `ROWS BETWEEN`, as a range of rows counted from the current one: negative before it, positive after, `0` the row itself, an open end unbounded. `rows(..0)` is a running total, `rows(-1..1)` the row and its neighbours.
1721
- # @param bounds [Range]
1722
- # @return [AST::Over]
1723
- def rows(bounds)
1724
- Over.new(function, partitions, orders, framing(:rows, bounds))
1725
- end
1726
-
1727
- # `RANGE BETWEEN`, with the bounds as {#rows} takes them.
1728
- # @param bounds [Range]
1729
- # @return [AST::Over]
1730
- def range(bounds)
1731
- Over.new(function, partitions, orders, framing(:range, bounds))
1732
- end
1733
-
1734
- # @private
1735
- def to_arel(table, model)
1736
- window = Arel::Nodes::Window.new
1737
- partitions.each { |expr| window.partition(to_arel_operand(expr, table, model)) }
1738
- orders.each { |expr| window.order(to_arel_operand(expr, table, model)) }
1739
- frame_arel(window) if frame
1740
-
1741
- # Not every aggregate can ride a window everywhere; the node itself
1742
- # says where, once the adapter is known.
1743
- function.check_window(model) if function.respond_to?(:check_window)
1744
-
1745
- # A window-only function refuses to build on its own; here is where
1746
- # it is asked for the call itself.
1747
- arel_function =
1748
- function.is_a?(WindowFunction) ? function.call_arel(table, model)
1749
- : function.to_arel(table, model)
1750
- Arel::Nodes::Over.new(arel_function, window)
1751
- end
1752
-
1753
- private
1754
- # The frame is a range of rows counted from the current one: negative
1755
- # before it, positive after, 0 the row itself, and an open end for
1756
- # unbounded. `rows(..0)` is what a running total wants.
1757
- def framing(kind, bounds)
1758
- raise ArgumentError, "a window has one frame" if frame
1759
- unless bounds.is_a?(::Range)
1760
- raise ArgumentError, "#{kind} takes a range of rows, as in rows(..0)"
1761
- end
1762
- if bounds.exclude_end?
1763
- raise ArgumentError, "a frame ends on a row rather than before one; use .."
1764
- end
1765
- [bounds.begin, bounds.end].each do |bound|
1766
- next if bound.nil? || bound.is_a?(::Integer)
1767
- raise ArgumentError,
1768
- "a frame bound is a number of rows, or nothing for unbounded"
1769
- end
1770
- [kind, bounds.begin, bounds.end]
1771
- end
1772
-
1773
- # Arel wants the keyword itself on the left of the BETWEEN, which is
1774
- # what window.rows with no argument hands back.
1775
- def frame_arel(window)
1776
- kind, from, to = frame
1777
- window.frame(
1778
- Arel::Nodes::Between.new(
1779
- window.public_send(kind),
1780
- Arel::Nodes::And.new([bound(from, Arel::Nodes::Preceding.new),
1781
- bound(to, Arel::Nodes::Following.new)])))
1782
- end
1783
-
1784
- def bound(rows, unbounded)
1785
- return unbounded if rows.nil?
1786
- return Arel::Nodes::CurrentRow.new if rows.zero?
1787
- rows.negative? ? Arel::Nodes::Preceding.new(-rows)
1788
- : Arel::Nodes::Following.new(rows)
1789
- end
1790
- end
1791
-
1792
- # An aggregate -- `COUNT`, `SUM`, `AVG`, `MIN`, `MAX` -- over a group,
1793
- # or, given {Windowing#over}, a window.
1794
- class Aggregate < Node
1795
- include Predications
1796
- include Arithmetics
1797
- include Windowing
1798
-
1799
- # The aggregates DISTINCT changes: over each value once, count counts
1800
- # fewer and sum and avg reckon less. min and max give the same
1801
- # either way, so a DISTINCT there is refused as saying nothing.
1802
- # @private
1803
- DISTINCT_FUNCTIONS = %i[count sum average].freeze
1804
-
1805
- # @private
1806
- attr_reader :operand, :function, :distinct, :condition
1807
-
1808
- def initialize(operand, function, distinct: false, condition: nil)
1809
- if distinct && !DISTINCT_FUNCTIONS.include?(function)
1810
- raise ArgumentError, "#{function} does not take distinct; it would give the same"
1811
- end
1812
- if distinct && operand == :*
1813
- raise ArgumentError, "count(:*) does not take distinct; name a column"
1814
- end
1815
- @operand = operand
1816
- @function = function
1817
- @distinct = distinct
1818
- @condition = condition
1819
- end
1820
-
1821
- # `FILTER (WHERE condition)`: the aggregate taken over the rows the
1822
- # condition holds for, as a value or a block. Where there is no
1823
- # FILTER clause -- MySQL, SQL Server -- the CASE that means the same.
1824
- # @return [AST::Aggregate]
1825
- # @example
1826
- # Author.select { [count(:*).as(:all), count(:*).filter { :age < 50 }.as(:young)] }
1827
- def filter(condition = nil, &block)
1828
- Aggregate.new(operand, function, distinct: distinct,
1829
- condition: Case.argument(:filter, condition, block))
1830
- end
1831
-
1832
- # @private
1833
- def to_arel(table, model)
1834
- return aggregate(operand, table, model) unless condition
1835
-
1836
- # A family without a FILTER clause gets the CASE that means the same.
1837
- # An aggregate passes over a NULL, so the case that yields nothing for
1838
- # the rows the condition misses is the same aggregate over the same
1839
- # rows -- count(*) has no operand to keep, and counts a 1 instead.
1840
- unless Dialect.for(model).filter_supported?
1841
- kept = Case.new.when(condition).then(operand == :* ? 1 : operand)
1842
- return aggregate(kept, table, model)
1843
- end
1844
-
1845
- aggregate(operand, table, model).filter(condition.to_arel(table, model))
1846
- end
1847
-
1848
- private
1849
- def aggregate(over, table, model)
1850
- arel_operand = to_arel_operand(over, table, model)
1851
- return arel_operand.count(distinct) if function == :count
1852
- call = arel_operand.public_send(function)
1853
- call.distinct = distinct
1854
- call
1855
- end
1856
- end
1857
-
1858
- # Rows gathered into one JSON document: json_arrayagg collects a value
1859
- # from each row into an array, json_objectagg a key and a value into an
1860
- # object. Every adapter has the pair under a name of its own; what
1861
- # PostgreSQL gets is the jsonb one, whose documents the other JSON
1862
- # operations here read.
1863
- class JsonAggregate < Node
1864
- include Predications
1865
- include JsonComparable
1866
- include ComputedJson
1867
- include Windowing
1868
-
1869
- # @private
1870
- attr_reader :kind, :operands, :condition
1871
-
1872
- def initialize(kind, operands, condition: nil)
1873
- @kind = kind
1874
- @operands = operands
1875
- @condition = condition
1876
- end
1877
-
1878
- # `FILTER (WHERE condition)`, as {Aggregate#filter}; refused on the
1879
- # MySQL family, where the CASE that stands in would leave a JSON null
1880
- # for every row it drops.
1881
- # @return [AST::JsonAggregate]
1882
- def filter(condition = nil, &block)
1883
- JsonAggregate.new(kind, operands,
1884
- condition: Case.argument(:filter, condition, block))
1885
- end
1886
-
1887
- # Over asks here before writing a window, since a family may take
1888
- # every other aggregate as one but not these two.
1889
- # @private
1890
- def check_window(model)
1891
- Dialect.for(model).check_json_aggregate_window(json_source, model)
1892
- end
1893
-
1894
- # @private
1895
- def to_arel(table, model)
1896
- dialect = Dialect.for(model)
1897
- call = Arel::Nodes::NamedFunction.new(
1898
- dialect.json_aggregate_name(kind),
1899
- operands.map { |operand| to_arel_argument(operand, table, model) })
1900
- return call unless condition
1901
-
1902
- # The CASE that stands in for FILTER elsewhere hands the aggregate a
1903
- # NULL for every row the condition misses, and these two keep a NULL
1904
- # -- as JSON null -- rather than passing over it, so it is refused.
1905
- unless dialect.json_aggregate_filter_supported?
1906
- raise NotImplementedError,
1907
- "#{json_source}.filter has no equivalent on " \
1908
- "#{model.connection_db_config.adapter}; a CASE would leave a " \
1909
- "null in the document for every row it drops"
1910
- end
1911
- call.filter(condition.to_arel(table, model))
1912
- end
1913
-
1914
- private
1915
- def json_source
1916
- "json_#{kind}"
1917
- end
1918
- end
1919
-
1920
- # The strings of a group joined into one, a separator between:
1921
- # `string_agg(:title, ", ")`, with `.order` for the order they are
1922
- # joined in. Every family has it under a name of its own with the
1923
- # ORDER BY in a place of its own, and Arel has no node for an ORDER BY
1924
- # inside a call, so the dialect writes the call. A NULL is passed
1925
- # over as by any aggregate, so the CASE that stands in for FILTER
1926
- # means the same here and is not refused as the JSON aggregates' is.
1927
- class StringAggregate < Node
1928
- include Predications
1929
- include Windowing
1930
-
1931
- # @private
1932
- attr_reader :operand, :separator, :orders, :condition
1933
-
1934
- def initialize(operand, separator, orders: [], condition: nil)
1935
- unless separator.is_a?(::String)
1936
- raise ArgumentError, "#{separator.inspect} is not a String separator"
1937
- end
1938
- @operand = operand
1939
- @separator = separator
1940
- @orders = orders
1941
- @condition = condition
1942
- end
1943
-
1944
- # The order the strings are joined in: columns, or orderings such as
1945
- # `:title.desc`.
1946
- # @return [AST::StringAggregate]
1947
- def order(*exprs)
1948
- raise ArgumentError, "order needs an expression" if exprs.empty?
1949
- StringAggregate.new(operand, separator, orders: orders + exprs, condition: condition)
1950
- end
1951
-
1952
- # `FILTER (WHERE condition)`, as {Aggregate#filter}.
1953
- # @return [AST::StringAggregate]
1954
- def filter(condition = nil, &block)
1955
- StringAggregate.new(operand, separator, orders: orders,
1956
- condition: Case.argument(:filter, condition, block))
1957
- end
1958
-
1959
- # @private
1960
- def check_window(model)
1961
- Dialect.for(model).check_string_aggregate_window(model)
1962
- end
1963
-
1964
- # @private
1965
- def to_arel(table, model)
1966
- dialect = Dialect.for(model)
1967
- kept = condition && !dialect.filter_supported? ?
1968
- Case.new.when(condition).then(operand) : operand
1969
- call = dialect.string_agg(
1970
- to_arel_argument(kept, table, model), separator,
1971
- orders.map { |expr| to_arel_operand(expr, table, model) },
1972
- string_operand?(model), model)
1973
- return call unless condition && dialect.filter_supported?
1974
- Arel::Nodes::Filter.new(call, condition.to_arel(table, model))
1975
- end
1976
-
1977
- private
1978
- # Whether the operand is a column the model declares a string.
1979
- # PostgreSQL asks, its STRING_AGG taking text and nothing else; the
1980
- # others convert for themselves.
1981
- def string_operand?(model)
1982
- operand.is_a?(::Symbol) &&
1983
- %i[string text].include?(model.type_for_attribute(operand).type)
1984
- end
1985
- end
1986
-
1987
- # A column alias, quoted by the adapter, so that the name asked for is
1988
- # the name that comes back: unquoted, PostgreSQL folds a capital away
1989
- # and the other two keep it, which is one block meaning two things.
1990
- # Quoting also leaves nothing to refuse -- a name that would have been
1991
- # SQL is an identifier with a strange name instead.
1992
- #
1993
- # `quote: false` asks for the name as written, for a schema that wants
1994
- # the folding.
1995
- class As < Node
1996
- # @private
1997
- attr_reader :operand, :alias_name, :quote
1998
-
1999
- def initialize(operand, alias_name, quote: true)
2000
- # Checked here rather than where the SQL is built, so that a name
2001
- # the adapter is not being asked to quote is refused where it was
2002
- # written.
2003
- AST.check_name(alias_name, ALIAS_NAME, "column alias") unless quote
2004
- @operand = operand
2005
- @alias_name = alias_name
2006
- @quote = quote
2007
- end
2008
-
2009
- # @private
2010
- def to_arel(table, model)
2011
- to_arel_operand(operand, table, model).as(alias_sql(model))
2012
- end
2013
-
2014
- private
2015
- def alias_sql(model)
2016
- name = alias_name.to_s
2017
- return name unless quote
2018
- model.with_connection { |connection| connection.quote_column_name(name) }
2019
- end
2020
- end
2021
-
2022
- # A collation named for a comparison or an ordering: `:name.collate(:ci)`.
2023
- # It stands as an expression -- compared, ordered by, selected -- and
2024
- # gives back one of its own, so the collation carries through.
2025
- #
2026
- # The name follows COLLATE as a bare identifier with no Arel node of its
2027
- # own. What names are safe turns on whether the family quotes it -- only
2028
- # PostgreSQL does -- so the dialect checks the name as it builds the
2029
- # clause, rather than this node holding one rule for all of them.
2030
- class Collate < Node
2031
- include Predications
2032
-
2033
- # @private
2034
- attr_reader :operand, :name
2035
-
2036
- def initialize(operand, name)
2037
- @name = name.to_s
2038
- @operand = operand
2039
- end
2040
-
2041
- # @private
2042
- def to_arel(table, model)
2043
- Dialect.for(model).collate(to_arel_operand(operand, table, model), name, model)
2044
- end
2045
- end
2046
-
2047
- # An ordering, `"age" DESC`: a direction on a column or an expression,
2048
- # and through {#nulls_first} and {#nulls_last} a place for the NULLs.
2049
- class Ordering < Node
2050
- # @private
2051
- attr_reader :operand, :direction, :nulls
2052
-
2053
- def initialize(operand, direction, nulls = nil)
2054
- @operand = operand
2055
- @direction = direction
2056
- @nulls = nulls
2057
- end
2058
-
2059
- # `NULLS FIRST`; portable, since Arel emulates it where MySQL has
2060
- # none.
2061
- # @return [AST::Ordering]
2062
- #
2063
- # MySQL has no NULLS FIRST/LAST, but Arel emulates it there with a
2064
- # leading IS NULL ordering, so these are portable.
2065
- def nulls_first
2066
- Ordering.new(operand, direction, :nulls_first)
2067
- end
2068
-
2069
- # `NULLS LAST`.
2070
- # @return [AST::Ordering]
2071
- def nulls_last
2072
- Ordering.new(operand, direction, :nulls_last)
2073
- end
2074
-
2075
- # @private
2076
- def to_arel(table, model)
2077
- ordering = to_arel_operand(operand, table, model).public_send(direction)
2078
- nulls ? ordering.public_send(nulls) : ordering
2079
- end
2080
- end
2081
-
2082
- # A function call, `UPPER(name)`: what the scalar functions of
2083
- # {BlockContext} build, and {BlockContext#fn} for a function the list
2084
- # does not name.
2085
- class Function < Node
2086
- include Predications
2087
- include Arithmetics
2088
- include Windowing
2089
-
2090
- # @private
2091
- attr_reader :name, :args
2092
-
2093
- def initialize(name, args)
2094
- @name = name
2095
- @args = args
2096
- end
2097
-
2098
- # @private
2099
- def to_arel(table, model)
2100
- arel_args = args.map { |arg| to_arel_argument(arg, table, model) }
2101
- Arel::Nodes::NamedFunction.new(name, arel_args)
2102
- end
2103
- end
2104
-
2105
- # Escape hatch for operators without a spelling of their own, the way
2106
- # fn is for functions. The operator is emitted as written -- whether
2107
- # the adapter has it is the caller's assertion, as fn's names are --
2108
- # and the values ride as quoted literals, so on PostgreSQL an untyped
2109
- # one takes the type of the operand beside it.
2110
- class Operation < Node
2111
- include Predications
2112
- include Arithmetics
2113
- # After Arithmetics, whose & and | refuse: here they are AND and OR.
2114
- include Connectives
2115
-
2116
- # @private
2117
- attr_reader :operator, :left, :right
2118
-
2119
- def initialize(operator, left, right)
2120
- @operator = AST.check_name(operator, OPERATOR, "operator").to_s
2121
- @left = check_side(left)
2122
- @right = check_side(right)
2123
- end
2124
-
2125
- # @private
2126
- def to_arel(table, model)
2127
- Arel::Nodes::Grouping.new(
2128
- Arel::Nodes::InfixOperation.new(
2129
- operator, side(left, table, model), side(right, table, model)))
2130
- end
2131
-
2132
- private
2133
- # An expression operand is parenthesized: an unknown operator's
2134
- # precedence is unknown too, and PostgreSQL reads its named
2135
- # operators from the left, so a bare infix on the right would take
2136
- # the new operator's left side into its own.
2137
- def side(operand, table, model)
2138
- arel = to_arel_argument(operand, table, model)
2139
- operand.is_a?(Node) ? Arel::Nodes::Grouping.new(arel) : arel
2140
- end
2141
-
2142
- def check_side(operand)
2143
- if operand.is_a?(::Hash) || operand.is_a?(::Array) || operand.is_a?(::Set)
2144
- raise ArgumentError,
2145
- "#{operand.inspect} has no one SQL spelling; a string says it " \
2146
- "in the adapter's own, to_json for a document"
2147
- end
2148
- operand
2149
- end
2150
- end
2151
-
2152
- # ROW_NUMBER and its kind: functions that say nothing without a window.
2153
- # On its own this refuses rather than reaching the database as an error
2154
- # there; over asks it for call_arel instead.
2155
- class WindowFunction < Function
2156
- # @private
2157
- alias_method :call_arel, :to_arel
2158
-
2159
- # @private
2160
- def to_arel(_table, _model)
2161
- raise ArgumentError, "#{name.downcase} is a window function; it needs over"
2162
- end
2163
- end
2164
-
2165
- # EXTRACT(field FROM expr). The field is grammar rather than a value --
2166
- # a keyword the adapter reads bare -- so it has to be a plain name,
2167
- # which Arel upcases on the way out.
2168
- class Extract < Node
2169
- include Predications
2170
- include Arithmetics
2171
-
2172
- # @private
2173
- attr_reader :field, :operand
2174
-
2175
- def initialize(field, operand)
2176
- @field = AST.check_name(field, ALIAS_NAME, "extract field")
2177
- @operand = operand
2178
- end
2179
-
2180
- # @private
2181
- def to_arel(table, model)
2182
- Arel::Nodes::Extract.new(to_arel_argument(operand, table, model), field.to_s)
2183
- end
2184
- end
2185
-
2186
- # CAST(expr AS type). The type is grammar too, written into the SQL as
2187
- # given -- it is the adapter's own name for the type, and whether it
2188
- # exists is the database's to say -- so it has to look like one:
2189
- # a plain name, at most parenthesized with lengths.
2190
- class Cast < Node
2191
- include Predications
2192
- include Arithmetics
2193
-
2194
- # @private
2195
- attr_reader :operand, :sql_type
2196
-
2197
- def initialize(operand, sql_type)
2198
- @operand = operand
2199
- @sql_type = AST.check_name(sql_type, TYPE_NAME, "SQL type")
2200
- end
2201
-
2202
- # @private
2203
- def to_arel(table, model)
2204
- Arel::Nodes::NamedFunction.new(
2205
- "CAST",
2206
- [Arel::Nodes::As.new(to_arel_argument(operand, table, model),
2207
- Arel::Nodes::SqlLiteral.new(sql_type.to_s))])
2208
- end
2209
- end
2210
-
2211
- # CURRENT_TIMESTAMP and its relatives, what the SQL grammar calls a
2212
- # datetime value function. The grammar has them bare, and PostgreSQL
2213
- # and SQLite reject them written as calls, so unlike Function the name
2214
- # is emitted without parentheses. A precision is the one thing that
2215
- # does go into parentheses, and it is written into the SQL as given, so
2216
- # only an Integer is accepted.
2217
- class DatetimeValueFunction < Node
2218
- include Predications
2219
- include Arithmetics
2220
-
2221
- # @private
2222
- attr_reader :name, :precision
2223
-
2224
- def initialize(name, precision = nil)
2225
- unless precision.nil? || precision.is_a?(Integer)
2226
- raise ArgumentError,
2227
- "#{precision.inspect} is not an Integer precision"
2228
- end
2229
- @name = name
2230
- @precision = precision
2231
- end
2232
-
2233
- # @private
2234
- def to_arel(_table, _model)
2235
- Arel::Nodes::SqlLiteral.new(
2236
- precision ? "#{name}(#{precision})" : name)
2237
- end
2238
- end
2239
-
2240
- # A plain SQL comparison. The value is passed through as it is, so a Range
2241
- # or an Array compares against a PostgreSQL range or array column, the way
2242
- # Active Record's own force_equality? types do.
2243
- class Comparison < Predicate
2244
- # @private
2245
- OPERATOR_MAP = {
2246
- :== => :eq, :!= => :not_eq,
2247
- :> => :gt, :>= => :gteq, :< => :lt, :<= => :lteq
2248
- }.freeze
2249
-
2250
- # @private
2251
- attr_reader :column, :operator, :value
2252
-
2253
- def initialize(column, operator, value)
2254
- @column = column
2255
- @operator = operator
2256
- @value = value
2257
- end
2258
-
2259
- # @private
2260
- def to_arel(table, model)
2261
- arel_column = to_arel_operand(column, table, model)
2262
- arel_value =
2263
- case value
2264
- when Node then value.to_arel(table, model)
2265
- when ::Symbol then column_operand(value, table, model)
2266
- when ActiveRecord::Relation then scalar_subquery(value)
2267
- else quote_number(value)
2268
- end
2269
- arel_column.public_send(OPERATOR_MAP.fetch(operator), arel_value)
2270
- end
2271
-
2272
- private
2273
- # A relation compared against a column has to yield a single value, so
2274
- # unlike In there is no sensible default select list to fall back on.
2275
- def scalar_subquery(relation)
2276
- if relation.select_values.empty?
2277
- raise ArgumentError,
2278
- "#{operator} needs a subquery selecting one value; add a select"
2279
- end
2280
- relation = relation.send(:apply_join_dependency) if relation.eager_loading?
2281
- relation.arel
2282
- end
2283
- end
2284
-
2285
- # IS TRUE, IS FALSE and their negations, which every adapter spells the
2286
- # same way and answers alike, NULL included.
2287
- class TruthValue < Predicate
2288
- # @private
2289
- attr_reader :operand, :value, :negated
2290
-
2291
- def initialize(operand, value, negated: false)
2292
- @operand = operand
2293
- @value = value
2294
- @negated = negated
2295
- end
2296
-
2297
- # @private
2298
- def to_arel(table, model)
2299
- Dialect.for(model).truth_value(
2300
- to_arel_operand(operand, table, model), value, negated, model)
2301
- end
2302
- end
2303
-
2304
- # A relation standing for a set of values, which is what IN and the
2305
- # quantifiers each take. The treatment is Active Record's own
2306
- # RelationHandler's: without an explicit select list the subquery
2307
- # selects the model's primary key.
2308
- # @private
2309
- module SetSubquery
2310
- private
2311
- def set_subquery(relation, spelling)
2312
- relation = relation.send(:apply_join_dependency) if relation.eager_loading?
2313
- if relation.select_values.empty?
2314
- model = relation.model
2315
- if model.composite_primary_key?
2316
- raise ArgumentError,
2317
- "Cannot map composite primary key #{model.primary_key} to #{spelling}"
2318
- end
2319
- relation = relation.select(relation.table[model.primary_key])
2320
- end
2321
- relation.arel
2322
- end
2323
- end
2324
-
2325
- # IN for a list of values, BETWEEN for a range, IN (SELECT ...) for a
2326
- # relation.
2327
- class In < Predicate
2328
- include SetSubquery
2329
-
2330
- # Range holds its endpoints to Comparable, which a quoted node is
2331
- # not, so this quacks the three methods Arel's between reads.
2332
- # @private
2333
- QuotedRange = Struct.new(:begin, :end, :exclude_end) do
2334
- def exclude_end? = exclude_end
2335
- end
2336
-
2337
- # @private
2338
- attr_reader :operand, :values, :negated
2339
-
2340
- def initialize(operand, values, negated: false)
2341
- @operand = operand
2342
- @values = values
2343
- @negated = negated
2344
- end
2345
-
2346
- # @private
2347
- def to_arel(table, model)
2348
- arel_operand = to_arel_operand(operand, table, model)
2349
- case values
2350
- when Range, QuotedRange
2351
- lower = quote_value(values.begin, table, model)
2352
- upper = quote_value(values.end, table, model)
2353
- if json_between?(model) && lower && upper && !negated
2354
- return arel_operand.gteq(lower).and(
2355
- values.exclude_end? ? arel_operand.lt(upper) : arel_operand.lteq(upper))
2356
- end
2357
- range = QuotedRange.new(lower, upper, values.exclude_end?)
2358
- arel_operand.public_send(negated ? :not_between : :between, range)
2359
- when ActiveRecord::Relation
2360
- arel_operand.public_send(negated ? :not_in : :in, set_subquery(values, "IN"))
2361
- else
2362
- arg = values
2363
- if arg.is_a?(::Array)
2364
- arg = arg.map { |value| quote_value(value, table, model) }
2365
- return json_list(arel_operand, arg) if json_list?(model)
2366
- end
2367
- arel_operand.public_send(negated ? :not_in : :in, arg)
2368
- end
2369
- end
2370
-
2371
- private
2372
- # An element that is already an expression resolves, a symbol is a
2373
- # column here as everywhere, a number is quoted as itself, and the
2374
- # rest ride for Arel to cast by the column.
2375
- def quote_value(value, table, model)
2376
- case value
2377
- when Node then value.to_arel(table, model)
2378
- when ::Symbol then column_operand(value, table, model)
2379
- else quote_number(value)
2380
- end
2381
- end
2382
-
2383
- # MySQL leaves IN and BETWEEN out of its JSON comparisons -- they
2384
- # fall back to another comparison entirely -- so on it a JSON set is
2385
- # spelled as the comparisons it means: the closed range as its two
2386
- # bounds, the list as one equality per element. That names the dug
2387
- # value once per element, the price SQLite's XOR pays per operand;
2388
- # a negated range needs nothing, Arel writing it as two comparisons
2389
- # everywhere. MariaDB never gets this far: the endpoints refuse as
2390
- # they resolve.
2391
- def json_between?(model)
2392
- (values.begin.is_a?(JsonLiteral) || values.end.is_a?(JsonLiteral)) &&
2393
- Dialect.for(model).json_list_by_element?
2394
- end
2395
-
2396
- def json_list?(model)
2397
- values.any? { |value| value.is_a?(JsonLiteral) } &&
2398
- Dialect.for(model).json_list_by_element?
2399
- end
2400
-
2401
- def json_list(arel_operand, elements)
2402
- comparisons = elements.map do |element|
2403
- negated ? arel_operand.not_eq(element) : arel_operand.eq(element)
2404
- end
2405
- joined = comparisons.inject do |so_far, piece|
2406
- negated ? so_far.and(piece) : so_far.or(piece)
2407
- end
2408
- negated ? Arel::Nodes::Grouping.new(joined) : joined
2409
- end
2410
- end
2411
-
2412
- # ANY and ALL, which stand on the right of a comparison and say how many
2413
- # of the subquery's rows have to satisfy it. Where a scalar subquery
2414
- # has to return one row, these take as many as come.
2415
- class Quantified < Node
2416
- include SetSubquery
2417
-
2418
- # @private
2419
- attr_reader :kind, :relation
2420
-
2421
- def initialize(kind, relation)
2422
- unless relation.is_a?(ActiveRecord::Relation)
2423
- raise ArgumentError,
2424
- "#{kind} takes a relation as its subquery; a list is what in? takes"
2425
- end
2426
- @kind = kind
2427
- @relation = relation
2428
- end
2429
-
2430
- # The subquery goes in as its own AST rather than as the manager,
2431
- # which would parenthesise it a second time -- and to PostgreSQL
2432
- # `ANY ((SELECT ...))` is ANY of one scalar, which it refuses.
2433
- # @private
2434
- def to_arel(_table, _model)
2435
- Arel::Nodes::NamedFunction.new(kind, [set_subquery(relation, kind).ast])
2436
- end
2437
- end
2438
-
2439
- # EXISTS (SELECT ...) for a relation. Correlate the subquery with the
2440
- # outer table through qualified columns. EXISTS only asks whether a row
2441
- # comes back, so unlike In there is no select list to fix up.
2442
- class Exists < Predicate
2443
- # @private
2444
- attr_reader :relation
2445
-
2446
- def initialize(relation)
2447
- @relation = relation
2448
- end
2449
-
2450
- # @private
2451
- def to_arel(_table, _model)
2452
- subquery = relation
2453
- if subquery.eager_loading?
2454
- subquery = subquery.send(:apply_join_dependency)
2455
- end
2456
- subquery.arel.exists
2457
- end
2458
- end
2459
-
2460
- # `LIKE`, negated or case-insensitive as asked. The pattern of
2461
- # {Predications#like?} goes as written, `%` and `_` its wildcards; the
2462
- # shortcuts, {Predications#start_with?} and its kin, escape theirs and
2463
- # say so with `ESCAPE`, since SQLite reads no escape character unless
2464
- # told one.
2465
- class Like < Predicate
2466
- # @private
2467
- ESCAPE = "\\"
2468
-
2469
- # Escapes % and _ so that they match literally. The pattern built from
2470
- # the result must be used with ESCAPE, since SQLite has no default
2471
- # escape character.
2472
- # @private
2473
- def self.escape(string)
2474
- ActiveRecord::Base.sanitize_sql_like(string, ESCAPE)
2475
- end
2476
-
2477
- # ORs one LIKE per pattern, for the shortcuts that accept several
2478
- # literals the way String#start_with? does.
2479
- # @private
2480
- def self.any(operand, patterns)
2481
- patterns.map { |pattern| new(operand, pattern, ESCAPE) }.
2482
- inject { |left, right| Or.new(left, right) }
2483
- end
2484
-
2485
- # @private
2486
- attr_reader :operand, :pattern, :escape, :case_sensitive, :negated
2487
-
2488
- def initialize(operand, pattern, escape = nil, case_sensitive: true,
2489
- negated: false)
2490
- @operand = operand
2491
- @pattern = pattern
2492
- @escape = escape
2493
- @case_sensitive = case_sensitive
2494
- @negated = negated
2495
- end
2496
-
2497
- # @private
2498
- def to_arel(table, model)
2499
- # Arel matches case-insensitively unless told otherwise, which is
2500
- # what picks ILIKE over LIKE on PostgreSQL.
2501
- to_arel_operand(operand, table, model).
2502
- public_send(negated ? :does_not_match : :matches,
2503
- pattern, escape, case_sensitive)
2504
- end
2505
- end
2506
-
2507
- # IS [NOT] DISTINCT FROM, spelled IS / IS NOT on SQLite and <=> on
2508
- # MySQL. NULL compares as a value here, which is what separates these
2509
- # from = and <>.
2510
- class DistinctFrom < Predicate
2511
- # @private
2512
- attr_reader :operand, :value, :negated
2513
-
2514
- def initialize(operand, value, negated: false)
2515
- @operand = operand
2516
- @value = value
2517
- @negated = negated
2518
- end
2519
-
2520
- # @private
2521
- def to_arel(table, model)
2522
- arel_operand = to_arel_operand(operand, table, model)
2523
- arel_value = value.is_a?(Node) ? value.to_arel(table, model) : value
2524
- if negated
2525
- arel_operand.is_distinct_from(arel_value)
2526
- else
2527
- arel_operand.is_not_distinct_from(arel_value)
2528
- end
2529
- end
2530
- end
2531
-
2532
- # Comparisons against a PostgreSQL array column, named after the Ruby
2533
- # methods that mean the same thing: member? is Enumerable's element
2534
- # test, superset? and subset? are Set's whole-array containment, and
2535
- # intersect? is Array's "any element in common". Each name maps to one
2536
- # operator; the elements are rendered as an array literal, which
2537
- # PostgreSQL coerces to the column's element type, so any expression
2538
- # works as the operand and no schema lookup is needed.
2539
- class ArrayPredicate < Predicate
2540
- # The whole-array comparisons take the collection kinds their
2541
- # namesakes compare against: an Array, or a Set for the Set methods.
2542
- # @private
2543
- def self.elements(arg, method_name)
2544
- case arg
2545
- when ::Array then arg
2546
- when ::Set then arg.to_a
2547
- else
2548
- raise ArgumentError, "#{method_name} takes an Array or Set of elements"
2549
- end
2550
- end
2551
-
2552
- # @private
2553
- attr_reader :operand, :operator, :elements
2554
-
2555
- def initialize(operand, operator, elements)
2556
- @operand = operand
2557
- @operator = operator
2558
- @elements = elements
2559
- end
2560
-
2561
- # @private
2562
- #
2563
- # The refusal is the gem's rather than Arel's: the visitor stopped
2564
- # @> and && off PostgreSQL, but <@ rode an InfixOperation and
2565
- # rendered anywhere, so subset? alone reached the other servers.
2566
- def to_arel(table, model)
2567
- unless Dialect.for(model).array_comparisons_supported?
2568
- raise NotImplementedError,
2569
- "the array comparisons have no equivalent on " \
2570
- "#{model.connection_db_config.adapter}"
2571
- end
2572
- arel_operand = to_arel_operand(operand, table, model)
2573
- quoted = Arel::Nodes.build_quoted(array_literal)
2574
- case operator
2575
- when :"@>" then Arel::Nodes::Contains.new(arel_operand, quoted)
2576
- when :"&&" then Arel::Nodes::Overlaps.new(arel_operand, quoted)
2577
- else Arel::Nodes::InfixOperation.new(operator, arel_operand, quoted)
2578
- end
2579
- end
2580
-
2581
- private
2582
- # PostgreSQL array input syntax: elements joined by commas inside
2583
- # braces, and an element is double-quoted whenever it is empty, spells
2584
- # NULL, or contains a character the parser treats specially.
2585
- def array_literal
2586
- encoded = elements.map do |value|
2587
- s = value.to_s
2588
- if s.empty? || s.casecmp?("null") || s.match?(/[\s{},"\\]/)
2589
- "\"#{s.gsub(/["\\]/) { |c| "\\#{c}" }}\""
2590
- else
2591
- s
2592
- end
2593
- end
2594
- "{#{encoded.join(',')}}"
2595
- end
2596
- end
2597
-
2598
- # Regular expression match: REGEXP on MySQL, ~ on PostgreSQL. SQLite has
2599
- # no regexp operator built in, so Arel raises NotImplementedError there.
2600
- class Match < Predicate
2601
- # @private
2602
- attr_reader :operand, :pattern, :negated
2603
-
2604
- def initialize(operand, pattern, negated: false)
2605
- @operand = operand
2606
- @pattern = pattern.is_a?(Regexp) ? regexp_source(pattern) : pattern
2607
- @negated = negated
2608
- end
2609
-
2610
- # @private
2611
- def to_arel(table, model)
2612
- arel_operand = to_arel_operand(operand, table, model)
2613
- if negated
2614
- arel_operand.does_not_match_regexp(pattern)
2615
- else
2616
- arel_operand.matches_regexp(pattern)
2617
- end
2618
- end
2619
-
2620
- private
2621
- # A Regexp literal reads naturally with =~, but only its source crosses
2622
- # over; the database has its own dialect and no notion of Ruby's flags.
2623
- # Dropping a flag would silently change what the query matches, so
2624
- # anything beyond a plain literal is refused rather than ignored.
2625
- def regexp_source(regexp)
2626
- unless regexp.options.zero?
2627
- raise ArgumentError,
2628
- "#{regexp.inspect} has options that SQL cannot express; " \
2629
- "pass the pattern as a string instead"
2630
- end
2631
- regexp.source
2632
- end
2633
- end
2634
-
2635
- # `AND`, which `&` between two conditions builds.
2636
- class And < Predicate
2637
- # @private
2638
- attr_reader :left, :right
2639
-
2640
- def initialize(left, right)
2641
- @left = AST.check_condition(left, :&)
2642
- @right = AST.check_condition(right, :&)
2643
- end
2644
-
2645
- # @private
2646
- def to_arel(table, model)
2647
- left.to_arel(table, model).and(right.to_arel(table, model))
2648
- end
2649
- end
2650
-
2651
- # `OR`, which `|` between two conditions builds.
2652
- class Or < Predicate
2653
- # @private
2654
- attr_reader :left, :right
2655
-
2656
- def initialize(left, right)
2657
- @left = AST.check_condition(left, :|)
2658
- @right = AST.check_condition(right, :|)
2659
- end
2660
-
2661
- # @private
2662
- def to_arel(table, model)
2663
- left.to_arel(table, model).or(right.to_arel(table, model))
2664
- end
2665
- end
2666
-
2667
- # `NOT`, which `!` on a condition builds.
2668
- class Not < Predicate
2669
- # @private
2670
- attr_reader :operand
2671
-
2672
- def initialize(operand)
2673
- @operand = AST.check_condition(operand, :!)
2674
- end
2675
-
2676
- # @private
2677
- def to_arel(table, model)
2678
- Arel::Nodes::Not.new(operand.to_arel(table, model))
2679
- end
2680
- end
2681
81
  end
2682
82
  end
2683
83
  end