activerecord-refined 0.10.2 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/docs/ctes.md +2 -2
- data/docs/expressions.md +27 -13
- data/docs/functions.md +14 -10
- data/docs/index.md +1 -16
- data/docs/windows.md +2 -2
- data/examples/expressions.rb +13 -7
- data/lib/active_record/refined/ast.rb +330 -55
- data/lib/active_record/refined/dialect/mysql_compat.rb +16 -0
- data/lib/active_record/refined/dialect/mysqlish_json_functions.rb +45 -0
- data/lib/active_record/refined/dialect/oracle.rb +1 -7
- data/lib/active_record/refined/dialect/postgresql.rb +8 -0
- data/lib/active_record/refined/dialect/sql_server.rb +29 -5
- data/lib/active_record/refined/dialect/sqlite.rb +23 -0
- data/lib/active_record/refined/dialect.rb +132 -66
- data/lib/active_record/refined.rb +59 -22
- data/lib/activerecord-refined/version.rb +6 -1
- metadata +2 -1
|
@@ -5,6 +5,10 @@ require "json"
|
|
|
5
5
|
|
|
6
6
|
module ActiveRecord
|
|
7
7
|
module Refined
|
|
8
|
+
# The nodes a block's expressions build, compiled to Arel when the
|
|
9
|
+
# relation writes its SQL. What a user writes is the methods of
|
|
10
|
+
# {Predications}, {Arithmetics}, {BlockSyntax} and {BlockContext};
|
|
11
|
+
# what those give back is a node here.
|
|
8
12
|
module AST
|
|
9
13
|
# @private
|
|
10
14
|
NAME = /[[:alpha:]_][[:alnum:]_$]*/
|
|
@@ -28,18 +32,57 @@ module ActiveRecord
|
|
|
28
32
|
# @private
|
|
29
33
|
OPERATOR = %r{\A[+\-*/<>=~!@\#%^&|`?]+\z}
|
|
30
34
|
|
|
35
|
+
# @private
|
|
31
36
|
def self.check_name(name, pattern, what)
|
|
32
37
|
return name if pattern.match?(name.to_s)
|
|
33
38
|
raise ArgumentError, "#{name.inspect} is not a plain #{what}"
|
|
34
39
|
end
|
|
35
40
|
|
|
36
|
-
#
|
|
37
|
-
#
|
|
38
|
-
#
|
|
39
|
-
#
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
# Whether a node is one of the three things a condition can be: a
|
|
42
|
+
# predicate, or either of the escape hatches, which are conditions
|
|
43
|
+
# whenever what was written inside them is one.
|
|
44
|
+
# @private
|
|
45
|
+
def self.condition?(node)
|
|
46
|
+
node.is_a?(Predicate) || node.is_a?(Sql) || node.is_a?(Operation)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# @private
|
|
50
|
+
def self.check_condition(node, operator)
|
|
51
|
+
return node if condition?(node)
|
|
52
|
+
raise ArgumentError,
|
|
53
|
+
"#{operator} joins conditions; " \
|
|
54
|
+
"#{node.is_a?(Node) ? 'an expression' : node.inspect} is not one"
|
|
55
|
+
end
|
|
56
|
+
|
|
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
|
+
# What `&` and `|` say when the left side is not a condition. They
|
|
72
|
+
# mean AND and OR, so what was meant is either the bitwise operation,
|
|
73
|
+
# which has a name of its own, or a comparison whose parentheses Ruby's
|
|
74
|
+
# precedence ate; which one it is the other operand tells.
|
|
75
|
+
# @private
|
|
76
|
+
def self.refuse_logical(operator, logical, named, operand)
|
|
77
|
+
raise ArgumentError,
|
|
78
|
+
if condition?(operand)
|
|
79
|
+
"#{operator} joins conditions, and the left side is not one; " \
|
|
80
|
+
"a comparison there needs parentheses of its own: " \
|
|
81
|
+
"(:age >= 18) #{operator} ..."
|
|
82
|
+
else
|
|
83
|
+
"#{operator} between conditions is #{logical}; " \
|
|
84
|
+
"#{named} is SQL's bitwise operator"
|
|
85
|
+
end
|
|
43
86
|
end
|
|
44
87
|
|
|
45
88
|
# The conditions a column or an expression can be put in. Every one
|
|
@@ -410,7 +453,7 @@ module ActiveRecord
|
|
|
410
453
|
JsonHasKey.new(self, key)
|
|
411
454
|
end
|
|
412
455
|
|
|
413
|
-
# The keys of the object as a JSON array, as Hash#keys gives them. Oracle
|
|
456
|
+
# The keys of the object as a JSON array, as Hash#keys gives them. Oracle and SQL Server have none.
|
|
414
457
|
# @return [AST::JsonKeys]
|
|
415
458
|
#
|
|
416
459
|
# The keys of the document, as Hash#keys gives them: a JSON array.
|
|
@@ -419,14 +462,20 @@ module ActiveRecord
|
|
|
419
462
|
end
|
|
420
463
|
end
|
|
421
464
|
|
|
422
|
-
# The arithmetic and the bitwise
|
|
423
|
-
# expression. Ruby puts
|
|
465
|
+
# The arithmetic and the bitwise operations on a column or an
|
|
466
|
+
# expression. Ruby puts the operators above the comparisons, so
|
|
424
467
|
# `:price * :quantity > 100` groups the way it reads, and a number on
|
|
425
468
|
# the left -- `20 - :quantity` -- builds the same expression.
|
|
426
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
|
+
#
|
|
427
476
|
# @example
|
|
428
477
|
# LineItem.where { :price * :quantity > 1000 }
|
|
429
|
-
# LineItem.select {
|
|
478
|
+
# LineItem.select { :flags.bitwise_and(4).as(:featured) }
|
|
430
479
|
#
|
|
431
480
|
# Arithmetic builders shared by symbols, qualified columns and
|
|
432
481
|
# expressions. Imported into the Symbol refinement like Predications,
|
|
@@ -456,20 +505,33 @@ module ActiveRecord
|
|
|
456
505
|
Arithmetic.new(self, :/, other)
|
|
457
506
|
end
|
|
458
507
|
|
|
459
|
-
#
|
|
460
|
-
#
|
|
461
|
-
#
|
|
462
|
-
#
|
|
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.
|
|
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]
|
|
466
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)
|
|
467
529
|
Bitwise.new(self, :&, other)
|
|
468
530
|
end
|
|
469
531
|
|
|
470
532
|
# Bitwise OR.
|
|
471
533
|
# @return [AST::Bitwise]
|
|
472
|
-
def
|
|
534
|
+
def bitwise_or(other)
|
|
473
535
|
Bitwise.new(self, :|, other)
|
|
474
536
|
end
|
|
475
537
|
|
|
@@ -479,6 +541,12 @@ module ActiveRecord
|
|
|
479
541
|
Bitwise.new(self, :^, other)
|
|
480
542
|
end
|
|
481
543
|
|
|
544
|
+
# {#^} under a name.
|
|
545
|
+
# @return [AST::Bitwise]
|
|
546
|
+
def bitwise_xor(other)
|
|
547
|
+
Bitwise.new(self, :^, other)
|
|
548
|
+
end
|
|
549
|
+
|
|
482
550
|
# A shift left.
|
|
483
551
|
# @return [AST::Bitwise]
|
|
484
552
|
def <<(other)
|
|
@@ -496,13 +564,21 @@ module ActiveRecord
|
|
|
496
564
|
def ~
|
|
497
565
|
BitwiseNot.new(self)
|
|
498
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
|
|
499
574
|
end
|
|
500
575
|
|
|
501
576
|
# Arithmetic with the number on the left, imported into the numeric
|
|
502
577
|
# refinements: 20 - :quantity builds what :quantity + 20 builds. Only
|
|
503
578
|
# a column or an expression on the right means a query; anything else
|
|
504
579
|
# goes back to the number through super, so 1 + 2 is 3 inside a block
|
|
505
|
-
# too.
|
|
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.
|
|
506
582
|
# @private
|
|
507
583
|
module NumericArithmetics
|
|
508
584
|
def +(other)
|
|
@@ -527,12 +603,12 @@ module ActiveRecord
|
|
|
527
603
|
|
|
528
604
|
def &(other)
|
|
529
605
|
return super unless other.is_a?(::Symbol) || other.is_a?(Node)
|
|
530
|
-
|
|
606
|
+
AST.refuse_logical(:&, "AND", "bitwise_and", other)
|
|
531
607
|
end
|
|
532
608
|
|
|
533
609
|
def |(other)
|
|
534
610
|
return super unless other.is_a?(::Symbol) || other.is_a?(Node)
|
|
535
|
-
|
|
611
|
+
AST.refuse_logical(:|, "OR", "bitwise_or", other)
|
|
536
612
|
end
|
|
537
613
|
|
|
538
614
|
def ^(other)
|
|
@@ -549,14 +625,30 @@ module ActiveRecord
|
|
|
549
625
|
return super unless other.is_a?(::Symbol) || other.is_a?(Node)
|
|
550
626
|
Bitwise.new(self, :>>, other)
|
|
551
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
|
|
552
640
|
end
|
|
553
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}.
|
|
554
645
|
class Node
|
|
555
646
|
# The model travels with the table because some SQL cannot be written
|
|
556
647
|
# without knowing the adapter, and a node is built before anything
|
|
557
648
|
# knows which one it will be rendered for -- a symbol becomes a node
|
|
558
649
|
# inside a refinement, where there is no model to ask. Most nodes
|
|
559
650
|
# never look at it and only pass it on.
|
|
651
|
+
# @private
|
|
560
652
|
def to_arel(table, model)
|
|
561
653
|
raise ScriptError, "subclass must override this method"
|
|
562
654
|
end
|
|
@@ -643,20 +735,51 @@ module ActiveRecord
|
|
|
643
735
|
end
|
|
644
736
|
end
|
|
645
737
|
|
|
646
|
-
|
|
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) }
|
|
647
752
|
def &(other)
|
|
648
753
|
And.new(self, other)
|
|
649
754
|
end
|
|
650
755
|
|
|
756
|
+
# `OR`.
|
|
757
|
+
# @return [AST::Predicate]
|
|
651
758
|
def |(other)
|
|
652
759
|
Or.new(self, other)
|
|
653
760
|
end
|
|
654
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.
|
|
655
772
|
def !
|
|
656
773
|
Not.new(self)
|
|
657
774
|
end
|
|
658
775
|
end
|
|
659
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
|
+
|
|
660
783
|
# A literal standing where an expression would: `select { value(0).as(:depth) }`.
|
|
661
784
|
#
|
|
662
785
|
# Values reach the SQL quoted wherever they appear as an operand, but a
|
|
@@ -668,12 +791,14 @@ module ActiveRecord
|
|
|
668
791
|
include Predications
|
|
669
792
|
include Arithmetics
|
|
670
793
|
|
|
794
|
+
# @private
|
|
671
795
|
attr_reader :value
|
|
672
796
|
|
|
673
797
|
def initialize(value)
|
|
674
798
|
@value = value
|
|
675
799
|
end
|
|
676
800
|
|
|
801
|
+
# @private
|
|
677
802
|
def to_arel(_table, _model)
|
|
678
803
|
Arel::Nodes.build_quoted(value)
|
|
679
804
|
end
|
|
@@ -693,7 +818,10 @@ module ActiveRecord
|
|
|
693
818
|
class Sql < Node
|
|
694
819
|
include Predications
|
|
695
820
|
include Arithmetics
|
|
821
|
+
# After Arithmetics, whose & and | refuse: here they are AND and OR.
|
|
822
|
+
include Connectives
|
|
696
823
|
|
|
824
|
+
# @private
|
|
697
825
|
attr_reader :statement, :binds
|
|
698
826
|
|
|
699
827
|
def initialize(statement, binds)
|
|
@@ -705,10 +833,12 @@ module ActiveRecord
|
|
|
705
833
|
@binds = binds
|
|
706
834
|
end
|
|
707
835
|
|
|
836
|
+
# @private
|
|
708
837
|
def to_arel(_table, model)
|
|
709
838
|
Arel::Nodes::Grouping.new(field_arel(model))
|
|
710
839
|
end
|
|
711
840
|
|
|
841
|
+
# @private
|
|
712
842
|
def field_arel(model)
|
|
713
843
|
return Arel.sql(statement) if binds.empty?
|
|
714
844
|
|
|
@@ -731,6 +861,7 @@ module ActiveRecord
|
|
|
731
861
|
NOTHING = Object.new.freeze
|
|
732
862
|
private_constant :NOTHING
|
|
733
863
|
|
|
864
|
+
# @private
|
|
734
865
|
attr_reader :operand, :whens, :default
|
|
735
866
|
|
|
736
867
|
def initialize(operand = nil, whens = [], default = NOTHING)
|
|
@@ -742,7 +873,7 @@ module ActiveRecord
|
|
|
742
873
|
# The next `WHEN`: a value to compare the operand against, or a condition as a value or a block.
|
|
743
874
|
# @return [AST::Case::When]
|
|
744
875
|
def when(value = nil, &block)
|
|
745
|
-
|
|
876
|
+
When.new(self, Case.argument(:when, value, block))
|
|
746
877
|
end
|
|
747
878
|
|
|
748
879
|
# `THEN`, which belongs after a `when`; here it says so.
|
|
@@ -760,6 +891,7 @@ module ActiveRecord
|
|
|
760
891
|
Case.new(operand, whens, Case.argument(:else, value, block))
|
|
761
892
|
end
|
|
762
893
|
|
|
894
|
+
# @private
|
|
763
895
|
def to_arel(table, model)
|
|
764
896
|
raise ArgumentError, "case needs a when before it means anything" if whens.empty?
|
|
765
897
|
|
|
@@ -776,6 +908,7 @@ module ActiveRecord
|
|
|
776
908
|
# A value or a block, and exactly one of them: the block is what makes
|
|
777
909
|
# `when { :age >= 60 }` read like the blocks around it, and the value is
|
|
778
910
|
# what makes `when(10)` possible at all.
|
|
911
|
+
# @private
|
|
779
912
|
def self.argument(name, value, block)
|
|
780
913
|
if block
|
|
781
914
|
raise ArgumentError, "#{name} takes a value or a block, not both" unless value.nil?
|
|
@@ -788,7 +921,7 @@ module ActiveRecord
|
|
|
788
921
|
# What a `when` is until its `then` arrives. A Node so that using it
|
|
789
922
|
# as one says what is missing rather than reaching Active Record as
|
|
790
923
|
# something it cannot read.
|
|
791
|
-
class
|
|
924
|
+
class When < Node
|
|
792
925
|
def initialize(kase, condition)
|
|
793
926
|
@kase = kase
|
|
794
927
|
@condition = condition
|
|
@@ -802,6 +935,7 @@ module ActiveRecord
|
|
|
802
935
|
@kase.default)
|
|
803
936
|
end
|
|
804
937
|
|
|
938
|
+
# @private
|
|
805
939
|
def to_arel(_table, _model)
|
|
806
940
|
raise ArgumentError, "when needs a matching then"
|
|
807
941
|
end
|
|
@@ -810,6 +944,7 @@ module ActiveRecord
|
|
|
810
944
|
|
|
811
945
|
# A path into a JSON document, spelled the two ways the adapters want it.
|
|
812
946
|
# Shared, because reading a value and setting one walk the same path.
|
|
947
|
+
# @private
|
|
813
948
|
module JsonSteps
|
|
814
949
|
def check_steps(path, called)
|
|
815
950
|
raise ArgumentError, "#{called} needs a key or an index" if path.empty?
|
|
@@ -845,14 +980,6 @@ module ActiveRecord
|
|
|
845
980
|
end
|
|
846
981
|
end
|
|
847
982
|
|
|
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
983
|
# What a dug value may be compared with. dig_text gives text on every
|
|
857
984
|
# adapter, and what a text value compared with a number means is a
|
|
858
985
|
# question the three answer three ways: `dig_text(:n) == 5` is true on
|
|
@@ -886,7 +1013,7 @@ module ActiveRecord
|
|
|
886
1013
|
def between?(min, max) = super(comparison_value(min), comparison_value(max))
|
|
887
1014
|
def not_between?(min, max) = super(comparison_value(min), comparison_value(max))
|
|
888
1015
|
|
|
889
|
-
%i[+ - * / & | ^ << >>].each do |operator|
|
|
1016
|
+
%i[+ - * / & | ^ << >> bitwise_and bitwise_or bitwise_xor].each do |operator|
|
|
890
1017
|
define_method(operator) do |_other|
|
|
891
1018
|
raise ArgumentError, arithmetic_refusal(operator)
|
|
892
1019
|
end
|
|
@@ -896,6 +1023,10 @@ module ActiveRecord
|
|
|
896
1023
|
raise ArgumentError, arithmetic_refusal(:~)
|
|
897
1024
|
end
|
|
898
1025
|
|
|
1026
|
+
def bitwise_not
|
|
1027
|
+
raise ArgumentError, arithmetic_refusal(:bitwise_not)
|
|
1028
|
+
end
|
|
1029
|
+
|
|
899
1030
|
private
|
|
900
1031
|
# nil is left to the comparison itself, which says to use null?, and
|
|
901
1032
|
# so is anything the block built rather than wrote as a literal.
|
|
@@ -954,12 +1085,14 @@ module ActiveRecord
|
|
|
954
1085
|
# since a bare string beside JSON would be a JSON string, and every
|
|
955
1086
|
# string outranks every number in its ordering.
|
|
956
1087
|
class JsonLiteral < Node
|
|
1088
|
+
# @private
|
|
957
1089
|
attr_reader :value
|
|
958
1090
|
|
|
959
1091
|
def initialize(value)
|
|
960
1092
|
@value = value
|
|
961
1093
|
end
|
|
962
1094
|
|
|
1095
|
+
# @private
|
|
963
1096
|
def to_arel(_table, model)
|
|
964
1097
|
Dialect.for(model).json_literal(Arel::Nodes.build_quoted(JSON.generate(value)), model)
|
|
965
1098
|
end
|
|
@@ -988,6 +1121,7 @@ module ActiveRecord
|
|
|
988
1121
|
# advice to name. Included after JsonComparable, whose own
|
|
989
1122
|
# arithmetic_refusal it overrides.
|
|
990
1123
|
module ComputedJson
|
|
1124
|
+
# @private
|
|
991
1125
|
def json_value?
|
|
992
1126
|
true
|
|
993
1127
|
end
|
|
@@ -999,6 +1133,16 @@ module ActiveRecord
|
|
|
999
1133
|
end
|
|
1000
1134
|
end
|
|
1001
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.
|
|
1002
1146
|
class JsonPath < Node
|
|
1003
1147
|
include Predications
|
|
1004
1148
|
include Arithmetics
|
|
@@ -1006,6 +1150,7 @@ module ActiveRecord
|
|
|
1006
1150
|
include JsonComparable
|
|
1007
1151
|
include JsonDocument
|
|
1008
1152
|
|
|
1153
|
+
# @private
|
|
1009
1154
|
attr_reader :operand, :path
|
|
1010
1155
|
|
|
1011
1156
|
def initialize(operand, path, json_value: true)
|
|
@@ -1014,10 +1159,12 @@ module ActiveRecord
|
|
|
1014
1159
|
@json_value = json_value
|
|
1015
1160
|
end
|
|
1016
1161
|
|
|
1162
|
+
# @private
|
|
1017
1163
|
def json_value?
|
|
1018
1164
|
@json_value
|
|
1019
1165
|
end
|
|
1020
1166
|
|
|
1167
|
+
# @private
|
|
1021
1168
|
def to_arel(table, model)
|
|
1022
1169
|
Dialect.for(model).json_path(
|
|
1023
1170
|
to_arel_operand(operand, table, model),
|
|
@@ -1038,6 +1185,7 @@ module ActiveRecord
|
|
|
1038
1185
|
include JsonSteps
|
|
1039
1186
|
include JsonComparable
|
|
1040
1187
|
|
|
1188
|
+
# @private
|
|
1041
1189
|
attr_reader :operand, :path, :value
|
|
1042
1190
|
|
|
1043
1191
|
def initialize(operand, path, value)
|
|
@@ -1047,10 +1195,12 @@ module ActiveRecord
|
|
|
1047
1195
|
end
|
|
1048
1196
|
|
|
1049
1197
|
# Always JSON, which is what the comparison guard asks.
|
|
1198
|
+
# @private
|
|
1050
1199
|
def json_value?
|
|
1051
1200
|
true
|
|
1052
1201
|
end
|
|
1053
1202
|
|
|
1203
|
+
# @private
|
|
1054
1204
|
def to_arel(table, model)
|
|
1055
1205
|
Dialect.for(model).json_set(
|
|
1056
1206
|
to_arel_operand(operand, table, model),
|
|
@@ -1075,6 +1225,7 @@ module ActiveRecord
|
|
|
1075
1225
|
include JsonSteps
|
|
1076
1226
|
include JsonComparable
|
|
1077
1227
|
|
|
1228
|
+
# @private
|
|
1078
1229
|
attr_reader :operand, :keys
|
|
1079
1230
|
|
|
1080
1231
|
def initialize(operand, keys)
|
|
@@ -1082,10 +1233,12 @@ module ActiveRecord
|
|
|
1082
1233
|
@keys = check_keys(keys)
|
|
1083
1234
|
end
|
|
1084
1235
|
|
|
1236
|
+
# @private
|
|
1085
1237
|
def json_value?
|
|
1086
1238
|
true
|
|
1087
1239
|
end
|
|
1088
1240
|
|
|
1241
|
+
# @private
|
|
1089
1242
|
def to_arel(table, model)
|
|
1090
1243
|
Dialect.for(model).json_remove(
|
|
1091
1244
|
to_arel_operand(operand, table, model),
|
|
@@ -1113,6 +1266,7 @@ module ActiveRecord
|
|
|
1113
1266
|
|
|
1114
1267
|
# JSON containment: whether the document holds what is given.
|
|
1115
1268
|
class JsonContains < Predicate
|
|
1269
|
+
# @private
|
|
1116
1270
|
attr_reader :operand, :value
|
|
1117
1271
|
|
|
1118
1272
|
def initialize(operand, value)
|
|
@@ -1120,6 +1274,7 @@ module ActiveRecord
|
|
|
1120
1274
|
@value = value
|
|
1121
1275
|
end
|
|
1122
1276
|
|
|
1277
|
+
# @private
|
|
1123
1278
|
def to_arel(table, model)
|
|
1124
1279
|
Dialect.for(model).json_contains(
|
|
1125
1280
|
to_arel_operand(operand, table, model),
|
|
@@ -1133,6 +1288,7 @@ module ActiveRecord
|
|
|
1133
1288
|
# ? is a bind placeholder only to sanitize_sql, which none of the SQL
|
|
1134
1289
|
# written here passes through.
|
|
1135
1290
|
class JsonHasKey < Predicate
|
|
1291
|
+
# @private
|
|
1136
1292
|
attr_reader :operand, :key
|
|
1137
1293
|
|
|
1138
1294
|
def initialize(operand, key)
|
|
@@ -1140,6 +1296,7 @@ module ActiveRecord
|
|
|
1140
1296
|
@key = key
|
|
1141
1297
|
end
|
|
1142
1298
|
|
|
1299
|
+
# @private
|
|
1143
1300
|
def to_arel(table, model)
|
|
1144
1301
|
Dialect.for(model).json_has_key(
|
|
1145
1302
|
to_arel_operand(operand, table, model),
|
|
@@ -1160,12 +1317,14 @@ module ActiveRecord
|
|
|
1160
1317
|
include JsonComparable
|
|
1161
1318
|
include ComputedJson
|
|
1162
1319
|
|
|
1320
|
+
# @private
|
|
1163
1321
|
attr_reader :operand
|
|
1164
1322
|
|
|
1165
1323
|
def initialize(operand)
|
|
1166
1324
|
@operand = operand
|
|
1167
1325
|
end
|
|
1168
1326
|
|
|
1327
|
+
# @private
|
|
1169
1328
|
def to_arel(table, model)
|
|
1170
1329
|
Dialect.for(model).json_keys(to_arel_operand(operand, table, model), model)
|
|
1171
1330
|
end
|
|
@@ -1185,6 +1344,7 @@ module ActiveRecord
|
|
|
1185
1344
|
include JsonComparable
|
|
1186
1345
|
include ComputedJson
|
|
1187
1346
|
|
|
1347
|
+
# @private
|
|
1188
1348
|
attr_reader :kind, :values
|
|
1189
1349
|
|
|
1190
1350
|
def initialize(kind, values)
|
|
@@ -1192,6 +1352,7 @@ module ActiveRecord
|
|
|
1192
1352
|
@values = kind == :object ? check_pairs(values) : values
|
|
1193
1353
|
end
|
|
1194
1354
|
|
|
1355
|
+
# @private
|
|
1195
1356
|
def to_arel(table, model)
|
|
1196
1357
|
dialect = Dialect.for(model)
|
|
1197
1358
|
if kind == :array
|
|
@@ -1252,6 +1413,7 @@ module ActiveRecord
|
|
|
1252
1413
|
cube: Arel::Nodes::Cube,
|
|
1253
1414
|
}.freeze
|
|
1254
1415
|
|
|
1416
|
+
# @private
|
|
1255
1417
|
attr_reader :kind, :sets
|
|
1256
1418
|
|
|
1257
1419
|
def initialize(kind, sets)
|
|
@@ -1260,6 +1422,7 @@ module ActiveRecord
|
|
|
1260
1422
|
@sets = sets
|
|
1261
1423
|
end
|
|
1262
1424
|
|
|
1425
|
+
# @private
|
|
1263
1426
|
def to_arel(table, model)
|
|
1264
1427
|
return with_rollup(table, model) if Dialect.for(model).grouping_by_with_rollup?
|
|
1265
1428
|
|
|
@@ -1289,10 +1452,14 @@ module ActiveRecord
|
|
|
1289
1452
|
end
|
|
1290
1453
|
end
|
|
1291
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.
|
|
1292
1458
|
class Column < Node
|
|
1293
1459
|
include Predications
|
|
1294
1460
|
include Arithmetics
|
|
1295
1461
|
|
|
1462
|
+
# @private
|
|
1296
1463
|
attr_reader :table_name, :column_name
|
|
1297
1464
|
|
|
1298
1465
|
def initialize(table_name, column_name)
|
|
@@ -1300,6 +1467,7 @@ module ActiveRecord
|
|
|
1300
1467
|
@column_name = column_name
|
|
1301
1468
|
end
|
|
1302
1469
|
|
|
1470
|
+
# @private
|
|
1303
1471
|
def to_arel(_table, _model)
|
|
1304
1472
|
Arel::Table.new(table_name)[column_name]
|
|
1305
1473
|
end
|
|
@@ -1327,6 +1495,7 @@ module ActiveRecord
|
|
|
1327
1495
|
# @private
|
|
1328
1496
|
DATE_UNITS = %i[year month day].freeze
|
|
1329
1497
|
|
|
1498
|
+
# @private
|
|
1330
1499
|
attr_reader :left, :operator, :right
|
|
1331
1500
|
|
|
1332
1501
|
def initialize(left, operator, right)
|
|
@@ -1335,6 +1504,7 @@ module ActiveRecord
|
|
|
1335
1504
|
@right = right
|
|
1336
1505
|
end
|
|
1337
1506
|
|
|
1507
|
+
# @private
|
|
1338
1508
|
def to_arel(table, model)
|
|
1339
1509
|
arel_left = to_arel_operand(left, table, model)
|
|
1340
1510
|
return move_date(arel_left, model) if right.is_a?(::ActiveSupport::Duration)
|
|
@@ -1351,6 +1521,7 @@ module ActiveRecord
|
|
|
1351
1521
|
# what says so: a column the model declares a date, CURRENT_DATE, or
|
|
1352
1522
|
# one of those already moved by a date's units. The other families
|
|
1353
1523
|
# keep the type themselves and never ask.
|
|
1524
|
+
# @private
|
|
1354
1525
|
def self.date_operand?(operand, model)
|
|
1355
1526
|
case operand
|
|
1356
1527
|
when ::Symbol then model.type_for_attribute(operand).type == :date
|
|
@@ -1386,34 +1557,49 @@ module ActiveRecord
|
|
|
1386
1557
|
end
|
|
1387
1558
|
end
|
|
1388
1559
|
|
|
1389
|
-
# What the bitwise
|
|
1560
|
+
# What the bitwise operations refuse. Both refusals are there because
|
|
1390
1561
|
# the same Ruby would otherwise mean different things per adapter: MySQL
|
|
1391
1562
|
# and SQLite take a boolean for the one bit it is stored as, so
|
|
1392
|
-
# `published
|
|
1393
|
-
# PostgreSQL has no such operator and would say 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
|
|
1394
1566
|
module BitwiseOperands
|
|
1395
1567
|
private
|
|
1396
|
-
|
|
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)
|
|
1397
1582
|
return operand unless operand.is_a?(Predicate)
|
|
1398
1583
|
raise ArgumentError,
|
|
1399
|
-
"a condition cannot be an operand of
|
|
1584
|
+
"a condition cannot be an operand of a bitwise operation; " \
|
|
1400
1585
|
"& and | between conditions are AND and OR"
|
|
1401
1586
|
end
|
|
1402
1587
|
|
|
1403
1588
|
# Only the unqualified column can be checked, since that is the one
|
|
1404
1589
|
# the model is known to have.
|
|
1405
|
-
def check_not_boolean(operand,
|
|
1590
|
+
def check_not_boolean(operand, model)
|
|
1406
1591
|
return unless operand.is_a?(::Symbol)
|
|
1407
1592
|
return unless model.type_for_attribute(operand).type == :boolean
|
|
1408
1593
|
raise ArgumentError,
|
|
1409
|
-
"#{operand.inspect} is a boolean column, which
|
|
1410
|
-
"not take; #{operand.inspect}.true? is the condition"
|
|
1594
|
+
"#{operand.inspect} is a boolean column, which the bitwise " \
|
|
1595
|
+
"operations do not take; #{operand.inspect}.true? is the condition"
|
|
1411
1596
|
end
|
|
1412
1597
|
end
|
|
1413
1598
|
|
|
1414
|
-
# SQL's bitwise
|
|
1415
|
-
# keeps Ruby
|
|
1416
|
-
# and reads a | b & c from the left, where
|
|
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.
|
|
1417
1603
|
class Bitwise < Node
|
|
1418
1604
|
include Predications
|
|
1419
1605
|
include Arithmetics
|
|
@@ -1427,17 +1613,20 @@ module ActiveRecord
|
|
|
1427
1613
|
:>> => Arel::Nodes::BitwiseShiftRight,
|
|
1428
1614
|
}.freeze
|
|
1429
1615
|
|
|
1616
|
+
# @private
|
|
1430
1617
|
attr_reader :left, :operator, :right
|
|
1431
1618
|
|
|
1432
1619
|
def initialize(left, operator, right)
|
|
1433
1620
|
@left = left
|
|
1434
1621
|
@operator = operator
|
|
1435
|
-
@right = check_operand(right
|
|
1622
|
+
@right = check_operand(right)
|
|
1436
1623
|
end
|
|
1437
1624
|
|
|
1625
|
+
# @private
|
|
1438
1626
|
def to_arel(table, model)
|
|
1439
|
-
|
|
1440
|
-
check_not_boolean(
|
|
1627
|
+
check_operators_exist(model)
|
|
1628
|
+
check_not_boolean(left, model)
|
|
1629
|
+
check_not_boolean(right, model)
|
|
1441
1630
|
arel_left = to_arel_operand(left, table, model)
|
|
1442
1631
|
arel_right = to_arel_argument(right, table, model)
|
|
1443
1632
|
Arel::Nodes::Grouping.new(
|
|
@@ -1467,14 +1656,17 @@ module ActiveRecord
|
|
|
1467
1656
|
include Arithmetics
|
|
1468
1657
|
include BitwiseOperands
|
|
1469
1658
|
|
|
1659
|
+
# @private
|
|
1470
1660
|
attr_reader :operand
|
|
1471
1661
|
|
|
1472
1662
|
def initialize(operand)
|
|
1473
|
-
@operand = check_operand(operand
|
|
1663
|
+
@operand = check_operand(operand)
|
|
1474
1664
|
end
|
|
1475
1665
|
|
|
1666
|
+
# @private
|
|
1476
1667
|
def to_arel(table, model)
|
|
1477
|
-
|
|
1668
|
+
check_operators_exist(model)
|
|
1669
|
+
check_not_boolean(operand, model)
|
|
1478
1670
|
Arel::Nodes::Grouping.new(
|
|
1479
1671
|
Arel::Nodes::BitwiseNot.new(to_arel_operand(operand, table, model)))
|
|
1480
1672
|
end
|
|
@@ -1501,6 +1693,7 @@ module ActiveRecord
|
|
|
1501
1693
|
include Predications
|
|
1502
1694
|
include Arithmetics
|
|
1503
1695
|
|
|
1696
|
+
# @private
|
|
1504
1697
|
attr_reader :function, :partitions, :orders, :frame
|
|
1505
1698
|
|
|
1506
1699
|
def initialize(function, partitions = [], orders = [], frame = nil)
|
|
@@ -1538,6 +1731,7 @@ module ActiveRecord
|
|
|
1538
1731
|
Over.new(function, partitions, orders, framing(:range, bounds))
|
|
1539
1732
|
end
|
|
1540
1733
|
|
|
1734
|
+
# @private
|
|
1541
1735
|
def to_arel(table, model)
|
|
1542
1736
|
window = Arel::Nodes::Window.new
|
|
1543
1737
|
partitions.each { |expr| window.partition(to_arel_operand(expr, table, model)) }
|
|
@@ -1595,6 +1789,8 @@ module ActiveRecord
|
|
|
1595
1789
|
end
|
|
1596
1790
|
end
|
|
1597
1791
|
|
|
1792
|
+
# An aggregate -- `COUNT`, `SUM`, `AVG`, `MIN`, `MAX` -- over a group,
|
|
1793
|
+
# or, given {Windowing#over}, a window.
|
|
1598
1794
|
class Aggregate < Node
|
|
1599
1795
|
include Predications
|
|
1600
1796
|
include Arithmetics
|
|
@@ -1606,6 +1802,7 @@ module ActiveRecord
|
|
|
1606
1802
|
# @private
|
|
1607
1803
|
DISTINCT_FUNCTIONS = %i[count sum average].freeze
|
|
1608
1804
|
|
|
1805
|
+
# @private
|
|
1609
1806
|
attr_reader :operand, :function, :distinct, :condition
|
|
1610
1807
|
|
|
1611
1808
|
def initialize(operand, function, distinct: false, condition: nil)
|
|
@@ -1632,6 +1829,7 @@ module ActiveRecord
|
|
|
1632
1829
|
condition: Case.argument(:filter, condition, block))
|
|
1633
1830
|
end
|
|
1634
1831
|
|
|
1832
|
+
# @private
|
|
1635
1833
|
def to_arel(table, model)
|
|
1636
1834
|
return aggregate(operand, table, model) unless condition
|
|
1637
1835
|
|
|
@@ -1668,6 +1866,7 @@ module ActiveRecord
|
|
|
1668
1866
|
include ComputedJson
|
|
1669
1867
|
include Windowing
|
|
1670
1868
|
|
|
1869
|
+
# @private
|
|
1671
1870
|
attr_reader :kind, :operands, :condition
|
|
1672
1871
|
|
|
1673
1872
|
def initialize(kind, operands, condition: nil)
|
|
@@ -1687,10 +1886,12 @@ module ActiveRecord
|
|
|
1687
1886
|
|
|
1688
1887
|
# Over asks here before writing a window, since a family may take
|
|
1689
1888
|
# every other aggregate as one but not these two.
|
|
1889
|
+
# @private
|
|
1690
1890
|
def check_window(model)
|
|
1691
1891
|
Dialect.for(model).check_json_aggregate_window(json_source, model)
|
|
1692
1892
|
end
|
|
1693
1893
|
|
|
1894
|
+
# @private
|
|
1694
1895
|
def to_arel(table, model)
|
|
1695
1896
|
dialect = Dialect.for(model)
|
|
1696
1897
|
call = Arel::Nodes::NamedFunction.new(
|
|
@@ -1727,6 +1928,7 @@ module ActiveRecord
|
|
|
1727
1928
|
include Predications
|
|
1728
1929
|
include Windowing
|
|
1729
1930
|
|
|
1931
|
+
# @private
|
|
1730
1932
|
attr_reader :operand, :separator, :orders, :condition
|
|
1731
1933
|
|
|
1732
1934
|
def initialize(operand, separator, orders: [], condition: nil)
|
|
@@ -1754,10 +1956,12 @@ module ActiveRecord
|
|
|
1754
1956
|
condition: Case.argument(:filter, condition, block))
|
|
1755
1957
|
end
|
|
1756
1958
|
|
|
1959
|
+
# @private
|
|
1757
1960
|
def check_window(model)
|
|
1758
1961
|
Dialect.for(model).check_string_aggregate_window(model)
|
|
1759
1962
|
end
|
|
1760
1963
|
|
|
1964
|
+
# @private
|
|
1761
1965
|
def to_arel(table, model)
|
|
1762
1966
|
dialect = Dialect.for(model)
|
|
1763
1967
|
kept = condition && !dialect.filter_supported? ?
|
|
@@ -1789,6 +1993,7 @@ module ActiveRecord
|
|
|
1789
1993
|
# `quote: false` asks for the name as written, for a schema that wants
|
|
1790
1994
|
# the folding.
|
|
1791
1995
|
class As < Node
|
|
1996
|
+
# @private
|
|
1792
1997
|
attr_reader :operand, :alias_name, :quote
|
|
1793
1998
|
|
|
1794
1999
|
def initialize(operand, alias_name, quote: true)
|
|
@@ -1801,6 +2006,7 @@ module ActiveRecord
|
|
|
1801
2006
|
@quote = quote
|
|
1802
2007
|
end
|
|
1803
2008
|
|
|
2009
|
+
# @private
|
|
1804
2010
|
def to_arel(table, model)
|
|
1805
2011
|
to_arel_operand(operand, table, model).as(alias_sql(model))
|
|
1806
2012
|
end
|
|
@@ -1824,6 +2030,7 @@ module ActiveRecord
|
|
|
1824
2030
|
class Collate < Node
|
|
1825
2031
|
include Predications
|
|
1826
2032
|
|
|
2033
|
+
# @private
|
|
1827
2034
|
attr_reader :operand, :name
|
|
1828
2035
|
|
|
1829
2036
|
def initialize(operand, name)
|
|
@@ -1831,12 +2038,16 @@ module ActiveRecord
|
|
|
1831
2038
|
@operand = operand
|
|
1832
2039
|
end
|
|
1833
2040
|
|
|
2041
|
+
# @private
|
|
1834
2042
|
def to_arel(table, model)
|
|
1835
2043
|
Dialect.for(model).collate(to_arel_operand(operand, table, model), name, model)
|
|
1836
2044
|
end
|
|
1837
2045
|
end
|
|
1838
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.
|
|
1839
2049
|
class Ordering < Node
|
|
2050
|
+
# @private
|
|
1840
2051
|
attr_reader :operand, :direction, :nulls
|
|
1841
2052
|
|
|
1842
2053
|
def initialize(operand, direction, nulls = nil)
|
|
@@ -1861,17 +2072,22 @@ module ActiveRecord
|
|
|
1861
2072
|
Ordering.new(operand, direction, :nulls_last)
|
|
1862
2073
|
end
|
|
1863
2074
|
|
|
2075
|
+
# @private
|
|
1864
2076
|
def to_arel(table, model)
|
|
1865
2077
|
ordering = to_arel_operand(operand, table, model).public_send(direction)
|
|
1866
2078
|
nulls ? ordering.public_send(nulls) : ordering
|
|
1867
2079
|
end
|
|
1868
2080
|
end
|
|
1869
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.
|
|
1870
2085
|
class Function < Node
|
|
1871
2086
|
include Predications
|
|
1872
2087
|
include Arithmetics
|
|
1873
2088
|
include Windowing
|
|
1874
2089
|
|
|
2090
|
+
# @private
|
|
1875
2091
|
attr_reader :name, :args
|
|
1876
2092
|
|
|
1877
2093
|
def initialize(name, args)
|
|
@@ -1879,6 +2095,7 @@ module ActiveRecord
|
|
|
1879
2095
|
@args = args
|
|
1880
2096
|
end
|
|
1881
2097
|
|
|
2098
|
+
# @private
|
|
1882
2099
|
def to_arel(table, model)
|
|
1883
2100
|
arel_args = args.map { |arg| to_arel_argument(arg, table, model) }
|
|
1884
2101
|
Arel::Nodes::NamedFunction.new(name, arel_args)
|
|
@@ -1893,7 +2110,10 @@ module ActiveRecord
|
|
|
1893
2110
|
class Operation < Node
|
|
1894
2111
|
include Predications
|
|
1895
2112
|
include Arithmetics
|
|
2113
|
+
# After Arithmetics, whose & and | refuse: here they are AND and OR.
|
|
2114
|
+
include Connectives
|
|
1896
2115
|
|
|
2116
|
+
# @private
|
|
1897
2117
|
attr_reader :operator, :left, :right
|
|
1898
2118
|
|
|
1899
2119
|
def initialize(operator, left, right)
|
|
@@ -1902,6 +2122,7 @@ module ActiveRecord
|
|
|
1902
2122
|
@right = check_side(right)
|
|
1903
2123
|
end
|
|
1904
2124
|
|
|
2125
|
+
# @private
|
|
1905
2126
|
def to_arel(table, model)
|
|
1906
2127
|
Arel::Nodes::Grouping.new(
|
|
1907
2128
|
Arel::Nodes::InfixOperation.new(
|
|
@@ -1932,8 +2153,10 @@ module ActiveRecord
|
|
|
1932
2153
|
# On its own this refuses rather than reaching the database as an error
|
|
1933
2154
|
# there; over asks it for call_arel instead.
|
|
1934
2155
|
class WindowFunction < Function
|
|
2156
|
+
# @private
|
|
1935
2157
|
alias_method :call_arel, :to_arel
|
|
1936
2158
|
|
|
2159
|
+
# @private
|
|
1937
2160
|
def to_arel(_table, _model)
|
|
1938
2161
|
raise ArgumentError, "#{name.downcase} is a window function; it needs over"
|
|
1939
2162
|
end
|
|
@@ -1946,6 +2169,7 @@ module ActiveRecord
|
|
|
1946
2169
|
include Predications
|
|
1947
2170
|
include Arithmetics
|
|
1948
2171
|
|
|
2172
|
+
# @private
|
|
1949
2173
|
attr_reader :field, :operand
|
|
1950
2174
|
|
|
1951
2175
|
def initialize(field, operand)
|
|
@@ -1953,6 +2177,7 @@ module ActiveRecord
|
|
|
1953
2177
|
@operand = operand
|
|
1954
2178
|
end
|
|
1955
2179
|
|
|
2180
|
+
# @private
|
|
1956
2181
|
def to_arel(table, model)
|
|
1957
2182
|
Arel::Nodes::Extract.new(to_arel_argument(operand, table, model), field.to_s)
|
|
1958
2183
|
end
|
|
@@ -1966,6 +2191,7 @@ module ActiveRecord
|
|
|
1966
2191
|
include Predications
|
|
1967
2192
|
include Arithmetics
|
|
1968
2193
|
|
|
2194
|
+
# @private
|
|
1969
2195
|
attr_reader :operand, :sql_type
|
|
1970
2196
|
|
|
1971
2197
|
def initialize(operand, sql_type)
|
|
@@ -1973,6 +2199,7 @@ module ActiveRecord
|
|
|
1973
2199
|
@sql_type = AST.check_name(sql_type, TYPE_NAME, "SQL type")
|
|
1974
2200
|
end
|
|
1975
2201
|
|
|
2202
|
+
# @private
|
|
1976
2203
|
def to_arel(table, model)
|
|
1977
2204
|
Arel::Nodes::NamedFunction.new(
|
|
1978
2205
|
"CAST",
|
|
@@ -1991,6 +2218,7 @@ module ActiveRecord
|
|
|
1991
2218
|
include Predications
|
|
1992
2219
|
include Arithmetics
|
|
1993
2220
|
|
|
2221
|
+
# @private
|
|
1994
2222
|
attr_reader :name, :precision
|
|
1995
2223
|
|
|
1996
2224
|
def initialize(name, precision = nil)
|
|
@@ -2002,6 +2230,7 @@ module ActiveRecord
|
|
|
2002
2230
|
@precision = precision
|
|
2003
2231
|
end
|
|
2004
2232
|
|
|
2233
|
+
# @private
|
|
2005
2234
|
def to_arel(_table, _model)
|
|
2006
2235
|
Arel::Nodes::SqlLiteral.new(
|
|
2007
2236
|
precision ? "#{name}(#{precision})" : name)
|
|
@@ -2018,6 +2247,7 @@ module ActiveRecord
|
|
|
2018
2247
|
:> => :gt, :>= => :gteq, :< => :lt, :<= => :lteq
|
|
2019
2248
|
}.freeze
|
|
2020
2249
|
|
|
2250
|
+
# @private
|
|
2021
2251
|
attr_reader :column, :operator, :value
|
|
2022
2252
|
|
|
2023
2253
|
def initialize(column, operator, value)
|
|
@@ -2026,6 +2256,7 @@ module ActiveRecord
|
|
|
2026
2256
|
@value = value
|
|
2027
2257
|
end
|
|
2028
2258
|
|
|
2259
|
+
# @private
|
|
2029
2260
|
def to_arel(table, model)
|
|
2030
2261
|
arel_column = to_arel_operand(column, table, model)
|
|
2031
2262
|
arel_value =
|
|
@@ -2054,6 +2285,7 @@ module ActiveRecord
|
|
|
2054
2285
|
# IS TRUE, IS FALSE and their negations, which every adapter spells the
|
|
2055
2286
|
# same way and answers alike, NULL included.
|
|
2056
2287
|
class TruthValue < Predicate
|
|
2288
|
+
# @private
|
|
2057
2289
|
attr_reader :operand, :value, :negated
|
|
2058
2290
|
|
|
2059
2291
|
def initialize(operand, value, negated: false)
|
|
@@ -2062,6 +2294,7 @@ module ActiveRecord
|
|
|
2062
2294
|
@negated = negated
|
|
2063
2295
|
end
|
|
2064
2296
|
|
|
2297
|
+
# @private
|
|
2065
2298
|
def to_arel(table, model)
|
|
2066
2299
|
Dialect.for(model).truth_value(
|
|
2067
2300
|
to_arel_operand(operand, table, model), value, negated, model)
|
|
@@ -2072,6 +2305,7 @@ module ActiveRecord
|
|
|
2072
2305
|
# quantifiers each take. The treatment is Active Record's own
|
|
2073
2306
|
# RelationHandler's: without an explicit select list the subquery
|
|
2074
2307
|
# selects the model's primary key.
|
|
2308
|
+
# @private
|
|
2075
2309
|
module SetSubquery
|
|
2076
2310
|
private
|
|
2077
2311
|
def set_subquery(relation, spelling)
|
|
@@ -2095,10 +2329,12 @@ module ActiveRecord
|
|
|
2095
2329
|
|
|
2096
2330
|
# Range holds its endpoints to Comparable, which a quoted node is
|
|
2097
2331
|
# not, so this quacks the three methods Arel's between reads.
|
|
2332
|
+
# @private
|
|
2098
2333
|
QuotedRange = Struct.new(:begin, :end, :exclude_end) do
|
|
2099
2334
|
def exclude_end? = exclude_end
|
|
2100
2335
|
end
|
|
2101
2336
|
|
|
2337
|
+
# @private
|
|
2102
2338
|
attr_reader :operand, :values, :negated
|
|
2103
2339
|
|
|
2104
2340
|
def initialize(operand, values, negated: false)
|
|
@@ -2107,6 +2343,7 @@ module ActiveRecord
|
|
|
2107
2343
|
@negated = negated
|
|
2108
2344
|
end
|
|
2109
2345
|
|
|
2346
|
+
# @private
|
|
2110
2347
|
def to_arel(table, model)
|
|
2111
2348
|
arel_operand = to_arel_operand(operand, table, model)
|
|
2112
2349
|
case values
|
|
@@ -2178,6 +2415,7 @@ module ActiveRecord
|
|
|
2178
2415
|
class Quantified < Node
|
|
2179
2416
|
include SetSubquery
|
|
2180
2417
|
|
|
2418
|
+
# @private
|
|
2181
2419
|
attr_reader :kind, :relation
|
|
2182
2420
|
|
|
2183
2421
|
def initialize(kind, relation)
|
|
@@ -2192,6 +2430,7 @@ module ActiveRecord
|
|
|
2192
2430
|
# The subquery goes in as its own AST rather than as the manager,
|
|
2193
2431
|
# which would parenthesise it a second time -- and to PostgreSQL
|
|
2194
2432
|
# `ANY ((SELECT ...))` is ANY of one scalar, which it refuses.
|
|
2433
|
+
# @private
|
|
2195
2434
|
def to_arel(_table, _model)
|
|
2196
2435
|
Arel::Nodes::NamedFunction.new(kind, [set_subquery(relation, kind).ast])
|
|
2197
2436
|
end
|
|
@@ -2201,12 +2440,14 @@ module ActiveRecord
|
|
|
2201
2440
|
# outer table through qualified columns. EXISTS only asks whether a row
|
|
2202
2441
|
# comes back, so unlike In there is no select list to fix up.
|
|
2203
2442
|
class Exists < Predicate
|
|
2443
|
+
# @private
|
|
2204
2444
|
attr_reader :relation
|
|
2205
2445
|
|
|
2206
2446
|
def initialize(relation)
|
|
2207
2447
|
@relation = relation
|
|
2208
2448
|
end
|
|
2209
2449
|
|
|
2450
|
+
# @private
|
|
2210
2451
|
def to_arel(_table, _model)
|
|
2211
2452
|
subquery = relation
|
|
2212
2453
|
if subquery.eager_loading?
|
|
@@ -2216,6 +2457,11 @@ module ActiveRecord
|
|
|
2216
2457
|
end
|
|
2217
2458
|
end
|
|
2218
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.
|
|
2219
2465
|
class Like < Predicate
|
|
2220
2466
|
# @private
|
|
2221
2467
|
ESCAPE = "\\"
|
|
@@ -2223,17 +2469,20 @@ module ActiveRecord
|
|
|
2223
2469
|
# Escapes % and _ so that they match literally. The pattern built from
|
|
2224
2470
|
# the result must be used with ESCAPE, since SQLite has no default
|
|
2225
2471
|
# escape character.
|
|
2472
|
+
# @private
|
|
2226
2473
|
def self.escape(string)
|
|
2227
2474
|
ActiveRecord::Base.sanitize_sql_like(string, ESCAPE)
|
|
2228
2475
|
end
|
|
2229
2476
|
|
|
2230
2477
|
# ORs one LIKE per pattern, for the shortcuts that accept several
|
|
2231
2478
|
# literals the way String#start_with? does.
|
|
2479
|
+
# @private
|
|
2232
2480
|
def self.any(operand, patterns)
|
|
2233
2481
|
patterns.map { |pattern| new(operand, pattern, ESCAPE) }.
|
|
2234
2482
|
inject { |left, right| Or.new(left, right) }
|
|
2235
2483
|
end
|
|
2236
2484
|
|
|
2485
|
+
# @private
|
|
2237
2486
|
attr_reader :operand, :pattern, :escape, :case_sensitive, :negated
|
|
2238
2487
|
|
|
2239
2488
|
def initialize(operand, pattern, escape = nil, case_sensitive: true,
|
|
@@ -2245,6 +2494,7 @@ module ActiveRecord
|
|
|
2245
2494
|
@negated = negated
|
|
2246
2495
|
end
|
|
2247
2496
|
|
|
2497
|
+
# @private
|
|
2248
2498
|
def to_arel(table, model)
|
|
2249
2499
|
# Arel matches case-insensitively unless told otherwise, which is
|
|
2250
2500
|
# what picks ILIKE over LIKE on PostgreSQL.
|
|
@@ -2258,6 +2508,7 @@ module ActiveRecord
|
|
|
2258
2508
|
# MySQL. NULL compares as a value here, which is what separates these
|
|
2259
2509
|
# from = and <>.
|
|
2260
2510
|
class DistinctFrom < Predicate
|
|
2511
|
+
# @private
|
|
2261
2512
|
attr_reader :operand, :value, :negated
|
|
2262
2513
|
|
|
2263
2514
|
def initialize(operand, value, negated: false)
|
|
@@ -2266,6 +2517,7 @@ module ActiveRecord
|
|
|
2266
2517
|
@negated = negated
|
|
2267
2518
|
end
|
|
2268
2519
|
|
|
2520
|
+
# @private
|
|
2269
2521
|
def to_arel(table, model)
|
|
2270
2522
|
arel_operand = to_arel_operand(operand, table, model)
|
|
2271
2523
|
arel_value = value.is_a?(Node) ? value.to_arel(table, model) : value
|
|
@@ -2287,6 +2539,7 @@ module ActiveRecord
|
|
|
2287
2539
|
class ArrayPredicate < Predicate
|
|
2288
2540
|
# The whole-array comparisons take the collection kinds their
|
|
2289
2541
|
# namesakes compare against: an Array, or a Set for the Set methods.
|
|
2542
|
+
# @private
|
|
2290
2543
|
def self.elements(arg, method_name)
|
|
2291
2544
|
case arg
|
|
2292
2545
|
when ::Array then arg
|
|
@@ -2296,6 +2549,7 @@ module ActiveRecord
|
|
|
2296
2549
|
end
|
|
2297
2550
|
end
|
|
2298
2551
|
|
|
2552
|
+
# @private
|
|
2299
2553
|
attr_reader :operand, :operator, :elements
|
|
2300
2554
|
|
|
2301
2555
|
def initialize(operand, operator, elements)
|
|
@@ -2304,7 +2558,17 @@ module ActiveRecord
|
|
|
2304
2558
|
@elements = elements
|
|
2305
2559
|
end
|
|
2306
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.
|
|
2307
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
|
|
2308
2572
|
arel_operand = to_arel_operand(operand, table, model)
|
|
2309
2573
|
quoted = Arel::Nodes.build_quoted(array_literal)
|
|
2310
2574
|
case operator
|
|
@@ -2334,6 +2598,7 @@ module ActiveRecord
|
|
|
2334
2598
|
# Regular expression match: REGEXP on MySQL, ~ on PostgreSQL. SQLite has
|
|
2335
2599
|
# no regexp operator built in, so Arel raises NotImplementedError there.
|
|
2336
2600
|
class Match < Predicate
|
|
2601
|
+
# @private
|
|
2337
2602
|
attr_reader :operand, :pattern, :negated
|
|
2338
2603
|
|
|
2339
2604
|
def initialize(operand, pattern, negated: false)
|
|
@@ -2342,6 +2607,7 @@ module ActiveRecord
|
|
|
2342
2607
|
@negated = negated
|
|
2343
2608
|
end
|
|
2344
2609
|
|
|
2610
|
+
# @private
|
|
2345
2611
|
def to_arel(table, model)
|
|
2346
2612
|
arel_operand = to_arel_operand(operand, table, model)
|
|
2347
2613
|
if negated
|
|
@@ -2366,39 +2632,48 @@ module ActiveRecord
|
|
|
2366
2632
|
end
|
|
2367
2633
|
end
|
|
2368
2634
|
|
|
2635
|
+
# `AND`, which `&` between two conditions builds.
|
|
2369
2636
|
class And < Predicate
|
|
2637
|
+
# @private
|
|
2370
2638
|
attr_reader :left, :right
|
|
2371
2639
|
|
|
2372
2640
|
def initialize(left, right)
|
|
2373
|
-
@left = left
|
|
2374
|
-
@right = right
|
|
2641
|
+
@left = AST.check_condition(left, :&)
|
|
2642
|
+
@right = AST.check_condition(right, :&)
|
|
2375
2643
|
end
|
|
2376
2644
|
|
|
2645
|
+
# @private
|
|
2377
2646
|
def to_arel(table, model)
|
|
2378
2647
|
left.to_arel(table, model).and(right.to_arel(table, model))
|
|
2379
2648
|
end
|
|
2380
2649
|
end
|
|
2381
2650
|
|
|
2651
|
+
# `OR`, which `|` between two conditions builds.
|
|
2382
2652
|
class Or < Predicate
|
|
2653
|
+
# @private
|
|
2383
2654
|
attr_reader :left, :right
|
|
2384
2655
|
|
|
2385
2656
|
def initialize(left, right)
|
|
2386
|
-
@left = left
|
|
2387
|
-
@right = right
|
|
2657
|
+
@left = AST.check_condition(left, :|)
|
|
2658
|
+
@right = AST.check_condition(right, :|)
|
|
2388
2659
|
end
|
|
2389
2660
|
|
|
2661
|
+
# @private
|
|
2390
2662
|
def to_arel(table, model)
|
|
2391
2663
|
left.to_arel(table, model).or(right.to_arel(table, model))
|
|
2392
2664
|
end
|
|
2393
2665
|
end
|
|
2394
2666
|
|
|
2667
|
+
# `NOT`, which `!` on a condition builds.
|
|
2395
2668
|
class Not < Predicate
|
|
2669
|
+
# @private
|
|
2396
2670
|
attr_reader :operand
|
|
2397
2671
|
|
|
2398
2672
|
def initialize(operand)
|
|
2399
|
-
@operand = operand
|
|
2673
|
+
@operand = AST.check_condition(operand, :!)
|
|
2400
2674
|
end
|
|
2401
2675
|
|
|
2676
|
+
# @private
|
|
2402
2677
|
def to_arel(table, model)
|
|
2403
2678
|
Arel::Nodes::Not.new(operand.to_arel(table, model))
|
|
2404
2679
|
end
|