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