diff options
Diffstat (limited to 'activerecord/lib/arel/nodes')
43 files changed, 1247 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..bf516db35f --- /dev/null +++ b/activerecord/lib/arel/nodes/and.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +module Arel # :nodoc: all + module Nodes + class And < Arel::Nodes::NodeExpression + 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..8b617f4df5 --- /dev/null +++ b/activerecord/lib/arel/nodes/ascending.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module Arel # :nodoc: all + 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..e184e99c73 --- /dev/null +++ b/activerecord/lib/arel/nodes/binary.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +module Arel # :nodoc: all + 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..344e46479f --- /dev/null +++ b/activerecord/lib/arel/nodes/bind_param.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +module Arel # :nodoc: all + module Nodes + class BindParam < Node + attr_reader :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 + + def infinite? + value.respond_to?(:infinite?) && value.infinite? + end + + def unboundable? + value.respond_to?(:unboundable?) && value.unboundable? + 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..1c4b727bf6 --- /dev/null +++ b/activerecord/lib/arel/nodes/case.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +module Arel # :nodoc: all + module Nodes + class Case < Arel::Nodes::NodeExpression + 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..6e911b717d --- /dev/null +++ b/activerecord/lib/arel/nodes/casted.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +module Arel # :nodoc: all + 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 + + def infinite? + value.respond_to?(:infinite?) && value.infinite? + 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/comment.rb b/activerecord/lib/arel/nodes/comment.rb new file mode 100644 index 0000000000..237ff27e7e --- /dev/null +++ b/activerecord/lib/arel/nodes/comment.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +module Arel # :nodoc: all + module Nodes + class Comment < Arel::Nodes::Node + attr_reader :values + + def initialize(values) + super() + @values = values + end + + def initialize_copy(other) + super + @values = @values.clone + end + + def hash + [@values].hash + end + + def eql?(other) + self.class == other.class && + self.values == other.values + end + alias :== :eql? + 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..880464639d --- /dev/null +++ b/activerecord/lib/arel/nodes/count.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +module Arel # :nodoc: all + module Nodes + class Count < Arel::Nodes::Function + 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..a419975335 --- /dev/null +++ b/activerecord/lib/arel/nodes/delete_statement.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +module Arel # :nodoc: all + module Nodes + class DeleteStatement < Arel::Nodes::Node + attr_accessor :left, :right, :orders, :limit, :offset, :key + + alias :relation :left + alias :relation= :left= + alias :wheres :right + alias :wheres= :right= + + def initialize(relation = nil, wheres = []) + super() + @left = relation + @right = wheres + @orders = [] + @limit = nil + @offset = nil + @key = nil + end + + def initialize_copy(other) + super + @left = @left.clone if @left + @right = @right.clone if @right + end + + def hash + [self.class, @left, @right, @orders, @limit, @offset, @key].hash + end + + def eql?(other) + self.class == other.class && + self.left == other.left && + self.right == other.right && + self.orders == other.orders && + self.limit == other.limit && + self.offset == other.offset && + self.key == other.key + 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..f3f6992ca8 --- /dev/null +++ b/activerecord/lib/arel/nodes/descending.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module Arel # :nodoc: all + 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..551d56c2ff --- /dev/null +++ b/activerecord/lib/arel/nodes/equality.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +module Arel # :nodoc: all + module Nodes + class Equality < Arel::Nodes::Binary + def operator; :== end + alias :operand1 :left + alias :operand2 :right + end + + %w{ + IsDistinctFrom + IsNotDistinctFrom + }.each do |name| + const_set name, Class.new(Equality) + 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..5799ee9b8f --- /dev/null +++ b/activerecord/lib/arel/nodes/extract.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +module Arel # :nodoc: all + 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..1e5bf04be5 --- /dev/null +++ b/activerecord/lib/arel/nodes/false.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Arel # :nodoc: all + 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..91bb81f2e3 --- /dev/null +++ b/activerecord/lib/arel/nodes/full_outer_join.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +module Arel # :nodoc: all + 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..0a439b39f5 --- /dev/null +++ b/activerecord/lib/arel/nodes/function.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +module Arel # :nodoc: all + 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..4d0bd69d4d --- /dev/null +++ b/activerecord/lib/arel/nodes/grouping.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +module Arel # :nodoc: all + 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..2be45d6f99 --- /dev/null +++ b/activerecord/lib/arel/nodes/in.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +module Arel # :nodoc: all + 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..bc7e20dcc6 --- /dev/null +++ b/activerecord/lib/arel/nodes/infix_operation.rb @@ -0,0 +1,80 @@ +# frozen_string_literal: true + +module Arel # :nodoc: all + 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..519fafad09 --- /dev/null +++ b/activerecord/lib/arel/nodes/inner_join.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +module Arel # :nodoc: all + 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..d28fd1f6c8 --- /dev/null +++ b/activerecord/lib/arel/nodes/insert_statement.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +module Arel # :nodoc: all + 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..abf0944623 --- /dev/null +++ b/activerecord/lib/arel/nodes/join_source.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module Arel # :nodoc: all + 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..fd5734f4bd --- /dev/null +++ b/activerecord/lib/arel/nodes/matches.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +module Arel # :nodoc: all + 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..126462d6d6 --- /dev/null +++ b/activerecord/lib/arel/nodes/named_function.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module Arel # :nodoc: all + 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..8086102bde --- /dev/null +++ b/activerecord/lib/arel/nodes/node.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +module Arel # :nodoc: all + module Nodes + ### + # Abstract base class for all AST nodes + class Node + include Arel::FactoryMethods + include Enumerable + + ### + # 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..cbcfaba37c --- /dev/null +++ b/activerecord/lib/arel/nodes/node_expression.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Arel # :nodoc: all + 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..0a3042be61 --- /dev/null +++ b/activerecord/lib/arel/nodes/outer_join.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +module Arel # :nodoc: all + 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..91176764a9 --- /dev/null +++ b/activerecord/lib/arel/nodes/over.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Arel # :nodoc: all + module Nodes + class Over < Binary + include Arel::AliasPredication + + def initialize(left, right = nil) + super(left, right) + end + + def operator; "OVER" end + end + end +end diff --git a/activerecord/lib/arel/nodes/regexp.rb b/activerecord/lib/arel/nodes/regexp.rb new file mode 100644 index 0000000000..7c25095569 --- /dev/null +++ b/activerecord/lib/arel/nodes/regexp.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Arel # :nodoc: all + 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..04ed4aaa78 --- /dev/null +++ b/activerecord/lib/arel/nodes/right_outer_join.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +module Arel # :nodoc: all + 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..11b4f39ece --- /dev/null +++ b/activerecord/lib/arel/nodes/select_core.rb @@ -0,0 +1,67 @@ +# frozen_string_literal: true + +module Arel # :nodoc: all + module Nodes + class SelectCore < Arel::Nodes::Node + attr_accessor :projections, :wheres, :groups, :windows, :comment + attr_accessor :havings, :source, :set_quantifier, :optimizer_hints + + def initialize + super() + @source = JoinSource.new nil + + # https://ronsavage.github.io/SQL/sql-92.bnf.html#set%20quantifier + @set_quantifier = nil + @optimizer_hints = nil + @projections = [] + @wheres = [] + @groups = [] + @havings = [] + @windows = [] + @comment = nil + 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, @set_quantifier, @projections, @optimizer_hints, + @wheres, @groups, @havings, @windows, @comment + ].hash + end + + def eql?(other) + self.class == other.class && + self.source == other.source && + self.set_quantifier == other.set_quantifier && + self.optimizer_hints == other.optimizer_hints && + self.projections == other.projections && + self.wheres == other.wheres && + self.groups == other.groups && + self.havings == other.havings && + self.windows == other.windows && + self.comment == other.comment + 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..eff5dad939 --- /dev/null +++ b/activerecord/lib/arel/nodes/select_statement.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +module Arel # :nodoc: all + 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..d25a8521b7 --- /dev/null +++ b/activerecord/lib/arel/nodes/sql_literal.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Arel # :nodoc: all + 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..86027fcab7 --- /dev/null +++ b/activerecord/lib/arel/nodes/string_join.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +module Arel # :nodoc: all + 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..f95ca16a3d --- /dev/null +++ b/activerecord/lib/arel/nodes/table_alias.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +module Arel # :nodoc: all + 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..d84c453f1a --- /dev/null +++ b/activerecord/lib/arel/nodes/terminal.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Arel # :nodoc: all + 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..c891012969 --- /dev/null +++ b/activerecord/lib/arel/nodes/true.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Arel # :nodoc: all + 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..6d1ac36b0e --- /dev/null +++ b/activerecord/lib/arel/nodes/unary.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +module Arel # :nodoc: all + 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 + OptimizerHints + Ordering + RollUp + }.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..524282ac84 --- /dev/null +++ b/activerecord/lib/arel/nodes/unary_operation.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module Arel # :nodoc: all + 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..7c3e0720d7 --- /dev/null +++ b/activerecord/lib/arel/nodes/unqualified_column.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +module Arel # :nodoc: all + 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..cfaa19e392 --- /dev/null +++ b/activerecord/lib/arel/nodes/update_statement.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +module Arel # :nodoc: all + module Nodes + class UpdateStatement < Arel::Nodes::Node + attr_accessor :relation, :wheres, :values, :orders, :limit, :offset, :key + + def initialize + @relation = nil + @wheres = [] + @values = [] + @orders = [] + @limit = nil + @offset = nil + @key = nil + end + + def initialize_copy(other) + super + @wheres = @wheres.clone + @values = @values.clone + end + + def hash + [@relation, @wheres, @values, @orders, @limit, @offset, @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.offset == other.offset && + self.key == other.key + end + alias :== :eql? + 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..1a9d9ebf01 --- /dev/null +++ b/activerecord/lib/arel/nodes/values_list.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +module Arel # :nodoc: all + module Nodes + class ValuesList < Unary + alias :rows :expr + 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..4916fc7fbe --- /dev/null +++ b/activerecord/lib/arel/nodes/window.rb @@ -0,0 +1,126 @@ +# frozen_string_literal: true + +module Arel # :nodoc: all + 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..157bdcaa08 --- /dev/null +++ b/activerecord/lib/arel/nodes/with.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +module Arel # :nodoc: all + module Nodes + class With < Arel::Nodes::Unary + alias children expr + end + + class WithRecursive < With; end + end +end |