aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/arel/nodes
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/lib/arel/nodes')
-rw-r--r--activerecord/lib/arel/nodes/and.rb31
-rw-r--r--activerecord/lib/arel/nodes/ascending.rb24
-rw-r--r--activerecord/lib/arel/nodes/binary.rb51
-rw-r--r--activerecord/lib/arel/nodes/bind_param.rb27
-rw-r--r--activerecord/lib/arel/nodes/case.rb54
-rw-r--r--activerecord/lib/arel/nodes/casted.rb45
-rw-r--r--activerecord/lib/arel/nodes/count.rb13
-rw-r--r--activerecord/lib/arel/nodes/delete_statement.rb37
-rw-r--r--activerecord/lib/arel/nodes/descending.rb24
-rw-r--r--activerecord/lib/arel/nodes/equality.rb10
-rw-r--r--activerecord/lib/arel/nodes/extract.rb23
-rw-r--r--activerecord/lib/arel/nodes/false.rb15
-rw-r--r--activerecord/lib/arel/nodes/full_outer_join.rb7
-rw-r--r--activerecord/lib/arel/nodes/function.rb44
-rw-r--r--activerecord/lib/arel/nodes/grouping.rb7
-rw-r--r--activerecord/lib/arel/nodes/in.rb7
-rw-r--r--activerecord/lib/arel/nodes/infix_operation.rb80
-rw-r--r--activerecord/lib/arel/nodes/inner_join.rb7
-rw-r--r--activerecord/lib/arel/nodes/insert_statement.rb36
-rw-r--r--activerecord/lib/arel/nodes/join_source.rb19
-rw-r--r--activerecord/lib/arel/nodes/matches.rb17
-rw-r--r--activerecord/lib/arel/nodes/named_function.rb22
-rw-r--r--activerecord/lib/arel/nodes/node.rb59
-rw-r--r--activerecord/lib/arel/nodes/node_expression.rb11
-rw-r--r--activerecord/lib/arel/nodes/outer_join.rb7
-rw-r--r--activerecord/lib/arel/nodes/over.rb16
-rw-r--r--activerecord/lib/arel/nodes/regexp.rb15
-rw-r--r--activerecord/lib/arel/nodes/right_outer_join.rb7
-rw-r--r--activerecord/lib/arel/nodes/select_core.rb64
-rw-r--r--activerecord/lib/arel/nodes/select_statement.rb40
-rw-r--r--activerecord/lib/arel/nodes/sql_literal.rb15
-rw-r--r--activerecord/lib/arel/nodes/string_join.rb10
-rw-r--r--activerecord/lib/arel/nodes/table_alias.rb26
-rw-r--r--activerecord/lib/arel/nodes/terminal.rb15
-rw-r--r--activerecord/lib/arel/nodes/true.rb15
-rw-r--r--activerecord/lib/arel/nodes/unary.rb44
-rw-r--r--activerecord/lib/arel/nodes/unary_operation.rb20
-rw-r--r--activerecord/lib/arel/nodes/unqualified_column.rb21
-rw-r--r--activerecord/lib/arel/nodes/update_statement.rb39
-rw-r--r--activerecord/lib/arel/nodes/values.rb15
-rw-r--r--activerecord/lib/arel/nodes/values_list.rb23
-rw-r--r--activerecord/lib/arel/nodes/window.rb125
-rw-r--r--activerecord/lib/arel/nodes/with.rb11
43 files changed, 1198 insertions, 0 deletions
diff --git a/activerecord/lib/arel/nodes/and.rb b/activerecord/lib/arel/nodes/and.rb
new file mode 100644
index 0000000000..1e2f61cf43
--- /dev/null
+++ b/activerecord/lib/arel/nodes/and.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+module Arel
+ module Nodes
+ class And < Arel::Nodes::Node
+ attr_reader :children
+
+ def initialize children
+ super()
+ @children = children
+ end
+
+ def left
+ children.first
+ end
+
+ def right
+ children[1]
+ end
+
+ def hash
+ children.hash
+ end
+
+ def eql? other
+ self.class == other.class &&
+ self.children == other.children
+ end
+ alias :== :eql?
+ end
+ end
+end
diff --git a/activerecord/lib/arel/nodes/ascending.rb b/activerecord/lib/arel/nodes/ascending.rb
new file mode 100644
index 0000000000..adadab55e4
--- /dev/null
+++ b/activerecord/lib/arel/nodes/ascending.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+module Arel
+ module Nodes
+ class Ascending < Ordering
+
+ def reverse
+ Descending.new(expr)
+ end
+
+ def direction
+ :asc
+ end
+
+ def ascending?
+ true
+ end
+
+ def descending?
+ false
+ end
+
+ end
+ end
+end
diff --git a/activerecord/lib/arel/nodes/binary.rb b/activerecord/lib/arel/nodes/binary.rb
new file mode 100644
index 0000000000..a86d4e4696
--- /dev/null
+++ b/activerecord/lib/arel/nodes/binary.rb
@@ -0,0 +1,51 @@
+# frozen_string_literal: true
+module Arel
+ module Nodes
+ class Binary < Arel::Nodes::NodeExpression
+ attr_accessor :left, :right
+
+ def initialize left, right
+ super()
+ @left = left
+ @right = right
+ end
+
+ def initialize_copy other
+ super
+ @left = @left.clone if @left
+ @right = @right.clone if @right
+ end
+
+ def hash
+ [self.class, @left, @right].hash
+ end
+
+ def eql? other
+ self.class == other.class &&
+ self.left == other.left &&
+ self.right == other.right
+ end
+ alias :== :eql?
+ end
+
+ %w{
+ As
+ Assignment
+ Between
+ GreaterThan
+ GreaterThanOrEqual
+ Join
+ LessThan
+ LessThanOrEqual
+ NotEqual
+ NotIn
+ Or
+ Union
+ UnionAll
+ Intersect
+ Except
+ }.each do |name|
+ const_set name, Class.new(Binary)
+ end
+ end
+end
diff --git a/activerecord/lib/arel/nodes/bind_param.rb b/activerecord/lib/arel/nodes/bind_param.rb
new file mode 100644
index 0000000000..efa4f452d4
--- /dev/null
+++ b/activerecord/lib/arel/nodes/bind_param.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+module Arel
+ module Nodes
+ class BindParam < Node
+ attr_accessor :value
+
+ def initialize(value)
+ @value = value
+ super()
+ end
+
+ def hash
+ [self.class, self.value].hash
+ end
+
+ def eql?(other)
+ other.is_a?(BindParam) &&
+ value == other.value
+ end
+ alias :== :eql?
+
+ def nil?
+ value.nil?
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/arel/nodes/case.rb b/activerecord/lib/arel/nodes/case.rb
new file mode 100644
index 0000000000..50ea1e0be2
--- /dev/null
+++ b/activerecord/lib/arel/nodes/case.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: true
+module Arel
+ module Nodes
+ class Case < Arel::Nodes::Node
+ attr_accessor :case, :conditions, :default
+
+ def initialize expression = nil, default = nil
+ @case = expression
+ @conditions = []
+ @default = default
+ end
+
+ def when condition, expression = nil
+ @conditions << When.new(Nodes.build_quoted(condition), expression)
+ self
+ end
+
+ def then expression
+ @conditions.last.right = Nodes.build_quoted(expression)
+ self
+ end
+
+ def else expression
+ @default = Else.new Nodes.build_quoted(expression)
+ self
+ end
+
+ def initialize_copy other
+ super
+ @case = @case.clone if @case
+ @conditions = @conditions.map { |x| x.clone }
+ @default = @default.clone if @default
+ end
+
+ def hash
+ [@case, @conditions, @default].hash
+ end
+
+ def eql? other
+ self.class == other.class &&
+ self.case == other.case &&
+ self.conditions == other.conditions &&
+ self.default == other.default
+ end
+ alias :== :eql?
+ end
+
+ class When < Binary # :nodoc:
+ end
+
+ class Else < Unary # :nodoc:
+ end
+ end
+end
diff --git a/activerecord/lib/arel/nodes/casted.rb b/activerecord/lib/arel/nodes/casted.rb
new file mode 100644
index 0000000000..f945063dd2
--- /dev/null
+++ b/activerecord/lib/arel/nodes/casted.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+module Arel
+ module Nodes
+ class Casted < Arel::Nodes::NodeExpression # :nodoc:
+ attr_reader :val, :attribute
+ def initialize val, attribute
+ @val = val
+ @attribute = attribute
+ super()
+ end
+
+ def nil?; @val.nil?; end
+
+ def hash
+ [self.class, val, attribute].hash
+ end
+
+ def eql? other
+ self.class == other.class &&
+ self.val == other.val &&
+ self.attribute == other.attribute
+ end
+ alias :== :eql?
+ end
+
+ class Quoted < Arel::Nodes::Unary # :nodoc:
+ alias :val :value
+ def nil?; val.nil?; end
+ end
+
+ def self.build_quoted other, attribute = nil
+ case other
+ when Arel::Nodes::Node, Arel::Attributes::Attribute, Arel::Table, Arel::Nodes::BindParam, Arel::SelectManager, Arel::Nodes::Quoted, Arel::Nodes::SqlLiteral
+ other
+ else
+ case attribute
+ when Arel::Attributes::Attribute
+ Casted.new other, attribute
+ else
+ Quoted.new other
+ end
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/arel/nodes/count.rb b/activerecord/lib/arel/nodes/count.rb
new file mode 100644
index 0000000000..4dd9be453f
--- /dev/null
+++ b/activerecord/lib/arel/nodes/count.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+module Arel
+ module Nodes
+ class Count < Arel::Nodes::Function
+ include Math
+
+ def initialize expr, distinct = false, aliaz = nil
+ super(expr, aliaz)
+ @distinct = distinct
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/arel/nodes/delete_statement.rb b/activerecord/lib/arel/nodes/delete_statement.rb
new file mode 100644
index 0000000000..063a5341e5
--- /dev/null
+++ b/activerecord/lib/arel/nodes/delete_statement.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+module Arel
+ module Nodes
+ class DeleteStatement < Arel::Nodes::Node
+ attr_accessor :left, :right
+ attr_accessor :limit
+
+ alias :relation :left
+ alias :relation= :left=
+ alias :wheres :right
+ alias :wheres= :right=
+
+ def initialize relation = nil, wheres = []
+ super()
+ @left = relation
+ @right = wheres
+ end
+
+ def initialize_copy other
+ super
+ @left = @left.clone if @left
+ @right = @right.clone if @right
+ end
+
+ def hash
+ [self.class, @left, @right].hash
+ end
+
+ def eql? other
+ self.class == other.class &&
+ self.left == other.left &&
+ self.right == other.right
+ end
+ alias :== :eql?
+ end
+ end
+end
diff --git a/activerecord/lib/arel/nodes/descending.rb b/activerecord/lib/arel/nodes/descending.rb
new file mode 100644
index 0000000000..d7261ab583
--- /dev/null
+++ b/activerecord/lib/arel/nodes/descending.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+module Arel
+ module Nodes
+ class Descending < Ordering
+
+ def reverse
+ Ascending.new(expr)
+ end
+
+ def direction
+ :desc
+ end
+
+ def ascending?
+ false
+ end
+
+ def descending?
+ true
+ end
+
+ end
+ end
+end
diff --git a/activerecord/lib/arel/nodes/equality.rb b/activerecord/lib/arel/nodes/equality.rb
new file mode 100644
index 0000000000..ef44725e24
--- /dev/null
+++ b/activerecord/lib/arel/nodes/equality.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+module Arel
+ module Nodes
+ class Equality < Arel::Nodes::Binary
+ def operator; :== end
+ alias :operand1 :left
+ alias :operand2 :right
+ end
+ end
+end
diff --git a/activerecord/lib/arel/nodes/extract.rb b/activerecord/lib/arel/nodes/extract.rb
new file mode 100644
index 0000000000..fdf3004c6a
--- /dev/null
+++ b/activerecord/lib/arel/nodes/extract.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+module Arel
+ module Nodes
+ class Extract < Arel::Nodes::Unary
+ attr_accessor :field
+
+ def initialize expr, field
+ super(expr)
+ @field = field
+ end
+
+ def hash
+ super ^ @field.hash
+ end
+
+ def eql? other
+ super &&
+ self.field == other.field
+ end
+ alias :== :eql?
+ end
+ end
+end
diff --git a/activerecord/lib/arel/nodes/false.rb b/activerecord/lib/arel/nodes/false.rb
new file mode 100644
index 0000000000..58132a2f90
--- /dev/null
+++ b/activerecord/lib/arel/nodes/false.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+module Arel
+ module Nodes
+ class False < Arel::Nodes::NodeExpression
+ def hash
+ self.class.hash
+ end
+
+ def eql? other
+ self.class == other.class
+ end
+ alias :== :eql?
+ end
+ end
+end
diff --git a/activerecord/lib/arel/nodes/full_outer_join.rb b/activerecord/lib/arel/nodes/full_outer_join.rb
new file mode 100644
index 0000000000..12a02d8cd9
--- /dev/null
+++ b/activerecord/lib/arel/nodes/full_outer_join.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+module Arel
+ module Nodes
+ class FullOuterJoin < Arel::Nodes::Join
+ end
+ end
+end
diff --git a/activerecord/lib/arel/nodes/function.rb b/activerecord/lib/arel/nodes/function.rb
new file mode 100644
index 0000000000..b3bf8f3e51
--- /dev/null
+++ b/activerecord/lib/arel/nodes/function.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+module Arel
+ module Nodes
+ class Function < Arel::Nodes::NodeExpression
+ include Arel::WindowPredications
+ attr_accessor :expressions, :alias, :distinct
+
+ def initialize expr, aliaz = nil
+ super()
+ @expressions = expr
+ @alias = aliaz && SqlLiteral.new(aliaz)
+ @distinct = false
+ end
+
+ def as aliaz
+ self.alias = SqlLiteral.new(aliaz)
+ self
+ end
+
+ def hash
+ [@expressions, @alias, @distinct].hash
+ end
+
+ def eql? other
+ self.class == other.class &&
+ self.expressions == other.expressions &&
+ self.alias == other.alias &&
+ self.distinct == other.distinct
+ end
+ alias :== :eql?
+
+ end
+
+ %w{
+ Sum
+ Exists
+ Max
+ Min
+ Avg
+ }.each do |name|
+ const_set(name, Class.new(Function))
+ end
+ end
+end
diff --git a/activerecord/lib/arel/nodes/grouping.rb b/activerecord/lib/arel/nodes/grouping.rb
new file mode 100644
index 0000000000..ffe66654ce
--- /dev/null
+++ b/activerecord/lib/arel/nodes/grouping.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+module Arel
+ module Nodes
+ class Grouping < Unary
+ end
+ end
+end
diff --git a/activerecord/lib/arel/nodes/in.rb b/activerecord/lib/arel/nodes/in.rb
new file mode 100644
index 0000000000..30cd771c40
--- /dev/null
+++ b/activerecord/lib/arel/nodes/in.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+module Arel
+ module Nodes
+ class In < Equality
+ end
+ end
+end
diff --git a/activerecord/lib/arel/nodes/infix_operation.rb b/activerecord/lib/arel/nodes/infix_operation.rb
new file mode 100644
index 0000000000..4eb7c5356f
--- /dev/null
+++ b/activerecord/lib/arel/nodes/infix_operation.rb
@@ -0,0 +1,80 @@
+# frozen_string_literal: true
+module Arel
+ module Nodes
+
+ class InfixOperation < Binary
+ include Arel::Expressions
+ include Arel::Predications
+ include Arel::OrderPredications
+ include Arel::AliasPredication
+ include Arel::Math
+
+ attr_reader :operator
+
+ def initialize operator, left, right
+ super(left, right)
+ @operator = operator
+ end
+ end
+
+ class Multiplication < InfixOperation
+ def initialize left, right
+ super(:*, left, right)
+ end
+ end
+
+ class Division < InfixOperation
+ def initialize left, right
+ super(:/, left, right)
+ end
+ end
+
+ class Addition < InfixOperation
+ def initialize left, right
+ super(:+, left, right)
+ end
+ end
+
+ class Subtraction < InfixOperation
+ def initialize left, right
+ super(:-, left, right)
+ end
+ end
+
+ class Concat < InfixOperation
+ def initialize left, right
+ super('||', left, right)
+ end
+ end
+
+ class BitwiseAnd < InfixOperation
+ def initialize left, right
+ super(:&, left, right)
+ end
+ end
+
+ class BitwiseOr < InfixOperation
+ def initialize left, right
+ super(:|, left, right)
+ end
+ end
+
+ class BitwiseXor < InfixOperation
+ def initialize left, right
+ super(:^, left, right)
+ end
+ end
+
+ class BitwiseShiftLeft < InfixOperation
+ def initialize left, right
+ super(:<<, left, right)
+ end
+ end
+
+ class BitwiseShiftRight < InfixOperation
+ def initialize left, right
+ super(:>>, left, right)
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/arel/nodes/inner_join.rb b/activerecord/lib/arel/nodes/inner_join.rb
new file mode 100644
index 0000000000..4e398267c3
--- /dev/null
+++ b/activerecord/lib/arel/nodes/inner_join.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+module Arel
+ module Nodes
+ class InnerJoin < Arel::Nodes::Join
+ end
+ end
+end
diff --git a/activerecord/lib/arel/nodes/insert_statement.rb b/activerecord/lib/arel/nodes/insert_statement.rb
new file mode 100644
index 0000000000..72793bc1ad
--- /dev/null
+++ b/activerecord/lib/arel/nodes/insert_statement.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+module Arel
+ module Nodes
+ class InsertStatement < Arel::Nodes::Node
+ attr_accessor :relation, :columns, :values, :select
+
+ def initialize
+ super()
+ @relation = nil
+ @columns = []
+ @values = nil
+ @select = nil
+ end
+
+ def initialize_copy other
+ super
+ @columns = @columns.clone
+ @values = @values.clone if @values
+ @select = @select.clone if @select
+ end
+
+ def hash
+ [@relation, @columns, @values, @select].hash
+ end
+
+ def eql? other
+ self.class == other.class &&
+ self.relation == other.relation &&
+ self.columns == other.columns &&
+ self.select == other.select &&
+ self.values == other.values
+ end
+ alias :== :eql?
+ end
+ end
+end
diff --git a/activerecord/lib/arel/nodes/join_source.rb b/activerecord/lib/arel/nodes/join_source.rb
new file mode 100644
index 0000000000..428ce8183e
--- /dev/null
+++ b/activerecord/lib/arel/nodes/join_source.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+module Arel
+ module Nodes
+ ###
+ # Class that represents a join source
+ #
+ # http://www.sqlite.org/syntaxdiagrams.html#join-source
+
+ class JoinSource < Arel::Nodes::Binary
+ def initialize single_source, joinop = []
+ super
+ end
+
+ def empty?
+ !left && right.empty?
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/arel/nodes/matches.rb b/activerecord/lib/arel/nodes/matches.rb
new file mode 100644
index 0000000000..3ad3850a8e
--- /dev/null
+++ b/activerecord/lib/arel/nodes/matches.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+module Arel
+ module Nodes
+ class Matches < Binary
+ attr_reader :escape
+ attr_accessor :case_sensitive
+
+ def initialize(left, right, escape = nil, case_sensitive = false)
+ super(left, right)
+ @escape = escape && Nodes.build_quoted(escape)
+ @case_sensitive = case_sensitive
+ end
+ end
+
+ class DoesNotMatch < Matches; end
+ end
+end
diff --git a/activerecord/lib/arel/nodes/named_function.rb b/activerecord/lib/arel/nodes/named_function.rb
new file mode 100644
index 0000000000..173838a7fd
--- /dev/null
+++ b/activerecord/lib/arel/nodes/named_function.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+module Arel
+ module Nodes
+ class NamedFunction < Arel::Nodes::Function
+ attr_accessor :name
+
+ def initialize name, expr, aliaz = nil
+ super(expr, aliaz)
+ @name = name
+ end
+
+ def hash
+ super ^ @name.hash
+ end
+
+ def eql? other
+ super && self.name == other.name
+ end
+ alias :== :eql?
+ end
+ end
+end
diff --git a/activerecord/lib/arel/nodes/node.rb b/activerecord/lib/arel/nodes/node.rb
new file mode 100644
index 0000000000..d2e6313dda
--- /dev/null
+++ b/activerecord/lib/arel/nodes/node.rb
@@ -0,0 +1,59 @@
+# frozen_string_literal: true
+module Arel
+ module Nodes
+ ###
+ # Abstract base class for all AST nodes
+ class Node
+ include Arel::FactoryMethods
+ include Enumerable
+
+ if $DEBUG
+ def _caller
+ @caller
+ end
+
+ def initialize
+ @caller = caller.dup
+ end
+ end
+
+ ###
+ # Factory method to create a Nodes::Not node that has the recipient of
+ # the caller as a child.
+ def not
+ Nodes::Not.new self
+ end
+
+ ###
+ # Factory method to create a Nodes::Grouping node that has an Nodes::Or
+ # node as a child.
+ def or right
+ Nodes::Grouping.new Nodes::Or.new(self, right)
+ end
+
+ ###
+ # Factory method to create an Nodes::And node.
+ def and right
+ Nodes::And.new [self, right]
+ end
+
+ # FIXME: this method should go away. I don't like people calling
+ # to_sql on non-head nodes. This forces us to walk the AST until we
+ # can find a node that has a "relation" member.
+ #
+ # Maybe we should just use `Table.engine`? :'(
+ def to_sql engine = Table.engine
+ collector = Arel::Collectors::SQLString.new
+ collector = engine.connection.visitor.accept self, collector
+ collector.value
+ end
+
+ # Iterate through AST, nodes will be yielded depth-first
+ def each &block
+ return enum_for(:each) unless block_given?
+
+ ::Arel::Visitors::DepthFirst.new(block).accept self
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/arel/nodes/node_expression.rb b/activerecord/lib/arel/nodes/node_expression.rb
new file mode 100644
index 0000000000..c4d4c8f428
--- /dev/null
+++ b/activerecord/lib/arel/nodes/node_expression.rb
@@ -0,0 +1,11 @@
+module Arel
+ module Nodes
+ class NodeExpression < Arel::Nodes::Node
+ include Arel::Expressions
+ include Arel::Predications
+ include Arel::AliasPredication
+ include Arel::OrderPredications
+ include Arel::Math
+ end
+ end
+end
diff --git a/activerecord/lib/arel/nodes/outer_join.rb b/activerecord/lib/arel/nodes/outer_join.rb
new file mode 100644
index 0000000000..c568655fe6
--- /dev/null
+++ b/activerecord/lib/arel/nodes/outer_join.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+module Arel
+ module Nodes
+ class OuterJoin < Arel::Nodes::Join
+ end
+ end
+end
diff --git a/activerecord/lib/arel/nodes/over.rb b/activerecord/lib/arel/nodes/over.rb
new file mode 100644
index 0000000000..47a34e69ea
--- /dev/null
+++ b/activerecord/lib/arel/nodes/over.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+module Arel
+ module Nodes
+
+ class Over < Binary
+ include Arel::AliasPredication
+
+ def initialize(left, right = nil)
+ super(left, right)
+ end
+
+ def operator; 'OVER' end
+ end
+
+ end
+end \ No newline at end of file
diff --git a/activerecord/lib/arel/nodes/regexp.rb b/activerecord/lib/arel/nodes/regexp.rb
new file mode 100644
index 0000000000..8a76185ef0
--- /dev/null
+++ b/activerecord/lib/arel/nodes/regexp.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+module Arel
+ module Nodes
+ class Regexp < Binary
+ attr_accessor :case_sensitive
+
+ def initialize(left, right, case_sensitive = true)
+ super(left, right)
+ @case_sensitive = case_sensitive
+ end
+ end
+
+ class NotRegexp < Regexp; end
+ end
+end
diff --git a/activerecord/lib/arel/nodes/right_outer_join.rb b/activerecord/lib/arel/nodes/right_outer_join.rb
new file mode 100644
index 0000000000..04ab31ebf0
--- /dev/null
+++ b/activerecord/lib/arel/nodes/right_outer_join.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+module Arel
+ module Nodes
+ class RightOuterJoin < Arel::Nodes::Join
+ end
+ end
+end
diff --git a/activerecord/lib/arel/nodes/select_core.rb b/activerecord/lib/arel/nodes/select_core.rb
new file mode 100644
index 0000000000..fa1c026107
--- /dev/null
+++ b/activerecord/lib/arel/nodes/select_core.rb
@@ -0,0 +1,64 @@
+# frozen_string_literal: true
+module Arel
+ module Nodes
+ class SelectCore < Arel::Nodes::Node
+ attr_accessor :top, :projections, :wheres, :groups, :windows
+ attr_accessor :havings, :source, :set_quantifier
+
+ def initialize
+ super()
+ @source = JoinSource.new nil
+ @top = nil
+
+ # https://ronsavage.github.io/SQL/sql-92.bnf.html#set%20quantifier
+ @set_quantifier = nil
+ @projections = []
+ @wheres = []
+ @groups = []
+ @havings = []
+ @windows = []
+ end
+
+ def from
+ @source.left
+ end
+
+ def from= value
+ @source.left = value
+ end
+
+ alias :froms= :from=
+ alias :froms :from
+
+ def initialize_copy other
+ super
+ @source = @source.clone if @source
+ @projections = @projections.clone
+ @wheres = @wheres.clone
+ @groups = @groups.clone
+ @havings = @havings.clone
+ @windows = @windows.clone
+ end
+
+ def hash
+ [
+ @source, @top, @set_quantifier, @projections,
+ @wheres, @groups, @havings, @windows
+ ].hash
+ end
+
+ def eql? other
+ self.class == other.class &&
+ self.source == other.source &&
+ self.top == other.top &&
+ self.set_quantifier == other.set_quantifier &&
+ self.projections == other.projections &&
+ self.wheres == other.wheres &&
+ self.groups == other.groups &&
+ self.havings == other.havings &&
+ self.windows == other.windows
+ end
+ alias :== :eql?
+ end
+ end
+end
diff --git a/activerecord/lib/arel/nodes/select_statement.rb b/activerecord/lib/arel/nodes/select_statement.rb
new file mode 100644
index 0000000000..79176d4be5
--- /dev/null
+++ b/activerecord/lib/arel/nodes/select_statement.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+module Arel
+ module Nodes
+ class SelectStatement < Arel::Nodes::NodeExpression
+ attr_reader :cores
+ attr_accessor :limit, :orders, :lock, :offset, :with
+
+ def initialize cores = [SelectCore.new]
+ super()
+ @cores = cores
+ @orders = []
+ @limit = nil
+ @lock = nil
+ @offset = nil
+ @with = nil
+ end
+
+ def initialize_copy other
+ super
+ @cores = @cores.map { |x| x.clone }
+ @orders = @orders.map { |x| x.clone }
+ end
+
+ def hash
+ [@cores, @orders, @limit, @lock, @offset, @with].hash
+ end
+
+ def eql? other
+ self.class == other.class &&
+ self.cores == other.cores &&
+ self.orders == other.orders &&
+ self.limit == other.limit &&
+ self.lock == other.lock &&
+ self.offset == other.offset &&
+ self.with == other.with
+ end
+ alias :== :eql?
+ end
+ end
+end
diff --git a/activerecord/lib/arel/nodes/sql_literal.rb b/activerecord/lib/arel/nodes/sql_literal.rb
new file mode 100644
index 0000000000..73575a7d49
--- /dev/null
+++ b/activerecord/lib/arel/nodes/sql_literal.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+module Arel
+ module Nodes
+ class SqlLiteral < String
+ include Arel::Expressions
+ include Arel::Predications
+ include Arel::AliasPredication
+ include Arel::OrderPredications
+
+ def encode_with(coder)
+ coder.scalar = self.to_s
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/arel/nodes/string_join.rb b/activerecord/lib/arel/nodes/string_join.rb
new file mode 100644
index 0000000000..21d6845c45
--- /dev/null
+++ b/activerecord/lib/arel/nodes/string_join.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+module Arel
+ module Nodes
+ class StringJoin < Arel::Nodes::Join
+ def initialize left, right = nil
+ super
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/arel/nodes/table_alias.rb b/activerecord/lib/arel/nodes/table_alias.rb
new file mode 100644
index 0000000000..78deb175b6
--- /dev/null
+++ b/activerecord/lib/arel/nodes/table_alias.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+module Arel
+ module Nodes
+ class TableAlias < Arel::Nodes::Binary
+ alias :name :right
+ alias :relation :left
+ alias :table_alias :name
+
+ def [] name
+ Attribute.new(self, name)
+ end
+
+ def table_name
+ relation.respond_to?(:name) ? relation.name : name
+ end
+
+ def type_cast_for_database(*args)
+ relation.type_cast_for_database(*args)
+ end
+
+ def able_to_type_cast?
+ relation.respond_to?(:able_to_type_cast?) && relation.able_to_type_cast?
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/arel/nodes/terminal.rb b/activerecord/lib/arel/nodes/terminal.rb
new file mode 100644
index 0000000000..3a1cd7f0e1
--- /dev/null
+++ b/activerecord/lib/arel/nodes/terminal.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+module Arel
+ module Nodes
+ class Distinct < Arel::Nodes::NodeExpression
+ def hash
+ self.class.hash
+ end
+
+ def eql? other
+ self.class == other.class
+ end
+ alias :== :eql?
+ end
+ end
+end
diff --git a/activerecord/lib/arel/nodes/true.rb b/activerecord/lib/arel/nodes/true.rb
new file mode 100644
index 0000000000..fdb8ed2095
--- /dev/null
+++ b/activerecord/lib/arel/nodes/true.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+module Arel
+ module Nodes
+ class True < Arel::Nodes::NodeExpression
+ def hash
+ self.class.hash
+ end
+
+ def eql? other
+ self.class == other.class
+ end
+ alias :== :eql?
+ end
+ end
+end
diff --git a/activerecord/lib/arel/nodes/unary.rb b/activerecord/lib/arel/nodes/unary.rb
new file mode 100644
index 0000000000..e458d87ab3
--- /dev/null
+++ b/activerecord/lib/arel/nodes/unary.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+module Arel
+ module Nodes
+ class Unary < Arel::Nodes::NodeExpression
+ attr_accessor :expr
+ alias :value :expr
+
+ def initialize expr
+ super()
+ @expr = expr
+ end
+
+ def hash
+ @expr.hash
+ end
+
+ def eql? other
+ self.class == other.class &&
+ self.expr == other.expr
+ end
+ alias :== :eql?
+ end
+
+ %w{
+ Bin
+ Cube
+ DistinctOn
+ Group
+ GroupingElement
+ GroupingSet
+ Lateral
+ Limit
+ Lock
+ Not
+ Offset
+ On
+ Ordering
+ RollUp
+ Top
+ }.each do |name|
+ const_set(name, Class.new(Unary))
+ end
+ end
+end
diff --git a/activerecord/lib/arel/nodes/unary_operation.rb b/activerecord/lib/arel/nodes/unary_operation.rb
new file mode 100644
index 0000000000..be4e270e76
--- /dev/null
+++ b/activerecord/lib/arel/nodes/unary_operation.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+module Arel
+ module Nodes
+
+ class UnaryOperation < Unary
+ attr_reader :operator
+
+ def initialize operator, operand
+ super(operand)
+ @operator = operator
+ end
+ end
+
+ class BitwiseNot < UnaryOperation
+ def initialize operand
+ super(:~, operand)
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/arel/nodes/unqualified_column.rb b/activerecord/lib/arel/nodes/unqualified_column.rb
new file mode 100644
index 0000000000..f9017238c8
--- /dev/null
+++ b/activerecord/lib/arel/nodes/unqualified_column.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+module Arel
+ module Nodes
+ class UnqualifiedColumn < Arel::Nodes::Unary
+ alias :attribute :expr
+ alias :attribute= :expr=
+
+ def relation
+ @expr.relation
+ end
+
+ def column
+ @expr.column
+ end
+
+ def name
+ @expr.name
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/arel/nodes/update_statement.rb b/activerecord/lib/arel/nodes/update_statement.rb
new file mode 100644
index 0000000000..286f0bd3ce
--- /dev/null
+++ b/activerecord/lib/arel/nodes/update_statement.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+module Arel
+ module Nodes
+ class UpdateStatement < Arel::Nodes::Node
+ attr_accessor :relation, :wheres, :values, :orders, :limit
+ attr_accessor :key
+
+ def initialize
+ @relation = nil
+ @wheres = []
+ @values = []
+ @orders = []
+ @limit = nil
+ @key = nil
+ end
+
+ def initialize_copy other
+ super
+ @wheres = @wheres.clone
+ @values = @values.clone
+ end
+
+ def hash
+ [@relation, @wheres, @values, @orders, @limit, @key].hash
+ end
+
+ def eql? other
+ self.class == other.class &&
+ self.relation == other.relation &&
+ self.wheres == other.wheres &&
+ self.values == other.values &&
+ self.orders == other.orders &&
+ self.limit == other.limit &&
+ self.key == other.key
+ end
+ alias :== :eql?
+ end
+ end
+end
diff --git a/activerecord/lib/arel/nodes/values.rb b/activerecord/lib/arel/nodes/values.rb
new file mode 100644
index 0000000000..b32d5063a2
--- /dev/null
+++ b/activerecord/lib/arel/nodes/values.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+module Arel
+ module Nodes
+ class Values < Arel::Nodes::Binary
+ alias :expressions :left
+ alias :expressions= :left=
+ alias :columns :right
+ alias :columns= :right=
+
+ def initialize exprs, columns = []
+ super
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/arel/nodes/values_list.rb b/activerecord/lib/arel/nodes/values_list.rb
new file mode 100644
index 0000000000..89cea1790d
--- /dev/null
+++ b/activerecord/lib/arel/nodes/values_list.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+module Arel
+ module Nodes
+ class ValuesList < Node
+ attr_reader :rows
+
+ def initialize(rows)
+ @rows = rows
+ super()
+ end
+
+ def hash
+ @rows.hash
+ end
+
+ def eql? other
+ self.class == other.class &&
+ self.rows == other.rows
+ end
+ alias :== :eql?
+ end
+ end
+end
diff --git a/activerecord/lib/arel/nodes/window.rb b/activerecord/lib/arel/nodes/window.rb
new file mode 100644
index 0000000000..23a005daba
--- /dev/null
+++ b/activerecord/lib/arel/nodes/window.rb
@@ -0,0 +1,125 @@
+# frozen_string_literal: true
+module Arel
+ module Nodes
+ class Window < Arel::Nodes::Node
+ attr_accessor :orders, :framing, :partitions
+
+ def initialize
+ @orders = []
+ @partitions = []
+ @framing = nil
+ end
+
+ def order *expr
+ # FIXME: We SHOULD NOT be converting these to SqlLiteral automatically
+ @orders.concat expr.map { |x|
+ String === x || Symbol === x ? Nodes::SqlLiteral.new(x.to_s) : x
+ }
+ self
+ end
+
+ def partition *expr
+ # FIXME: We SHOULD NOT be converting these to SqlLiteral automatically
+ @partitions.concat expr.map { |x|
+ String === x || Symbol === x ? Nodes::SqlLiteral.new(x.to_s) : x
+ }
+ self
+ end
+
+ def frame(expr)
+ @framing = expr
+ end
+
+ def rows(expr = nil)
+ if @framing
+ Rows.new(expr)
+ else
+ frame(Rows.new(expr))
+ end
+ end
+
+ def range(expr = nil)
+ if @framing
+ Range.new(expr)
+ else
+ frame(Range.new(expr))
+ end
+ end
+
+ def initialize_copy other
+ super
+ @orders = @orders.map { |x| x.clone }
+ end
+
+ def hash
+ [@orders, @framing].hash
+ end
+
+ def eql? other
+ self.class == other.class &&
+ self.orders == other.orders &&
+ self.framing == other.framing &&
+ self.partitions == other.partitions
+ end
+ alias :== :eql?
+ end
+
+ class NamedWindow < Window
+ attr_accessor :name
+
+ def initialize name
+ super()
+ @name = name
+ end
+
+ def initialize_copy other
+ super
+ @name = other.name.clone
+ end
+
+ def hash
+ super ^ @name.hash
+ end
+
+ def eql? other
+ super && self.name == other.name
+ end
+ alias :== :eql?
+ end
+
+ class Rows < Unary
+ def initialize(expr = nil)
+ super(expr)
+ end
+ end
+
+ class Range < Unary
+ def initialize(expr = nil)
+ super(expr)
+ end
+ end
+
+ class CurrentRow < Node
+ def hash
+ self.class.hash
+ end
+
+ def eql? other
+ self.class == other.class
+ end
+ alias :== :eql?
+ end
+
+ class Preceding < Unary
+ def initialize(expr = nil)
+ super(expr)
+ end
+ end
+
+ class Following < Unary
+ def initialize(expr = nil)
+ super(expr)
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/arel/nodes/with.rb b/activerecord/lib/arel/nodes/with.rb
new file mode 100644
index 0000000000..def7840ea3
--- /dev/null
+++ b/activerecord/lib/arel/nodes/with.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+module Arel
+ module Nodes
+ class With < Arel::Nodes::Unary
+ alias children expr
+ end
+
+ class WithRecursive < With; end
+ end
+end
+