aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/arel/visitors
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/lib/arel/visitors')
-rw-r--r--activerecord/lib/arel/visitors/depth_first.rb2
-rw-r--r--activerecord/lib/arel/visitors/dot.rb2
-rw-r--r--activerecord/lib/arel/visitors/ibm_db.rb6
-rw-r--r--activerecord/lib/arel/visitors/mssql.rb25
-rw-r--r--activerecord/lib/arel/visitors/mysql.rb56
-rw-r--r--activerecord/lib/arel/visitors/oracle.rb6
-rw-r--r--activerecord/lib/arel/visitors/oracle12.rb6
-rw-r--r--activerecord/lib/arel/visitors/postgresql.rb12
-rw-r--r--activerecord/lib/arel/visitors/sqlite.rb12
-rw-r--r--activerecord/lib/arel/visitors/to_sql.rb119
10 files changed, 165 insertions, 81 deletions
diff --git a/activerecord/lib/arel/visitors/depth_first.rb b/activerecord/lib/arel/visitors/depth_first.rb
index 8f65d303ac..92d309453c 100644
--- a/activerecord/lib/arel/visitors/depth_first.rb
+++ b/activerecord/lib/arel/visitors/depth_first.rb
@@ -95,6 +95,8 @@ module Arel # :nodoc: all
alias :visit_Arel_Nodes_NotEqual :binary
alias :visit_Arel_Nodes_NotIn :binary
alias :visit_Arel_Nodes_NotRegexp :binary
+ alias :visit_Arel_Nodes_IsNotDistinctFrom :binary
+ alias :visit_Arel_Nodes_IsDistinctFrom :binary
alias :visit_Arel_Nodes_Or :binary
alias :visit_Arel_Nodes_OuterJoin :binary
alias :visit_Arel_Nodes_Regexp :binary
diff --git a/activerecord/lib/arel/visitors/dot.rb b/activerecord/lib/arel/visitors/dot.rb
index 9054f0159b..6389c875cb 100644
--- a/activerecord/lib/arel/visitors/dot.rb
+++ b/activerecord/lib/arel/visitors/dot.rb
@@ -195,6 +195,8 @@ module Arel # :nodoc: all
alias :visit_Arel_Nodes_JoinSource :binary
alias :visit_Arel_Nodes_LessThan :binary
alias :visit_Arel_Nodes_LessThanOrEqual :binary
+ alias :visit_Arel_Nodes_IsNotDistinctFrom :binary
+ alias :visit_Arel_Nodes_IsDistinctFrom :binary
alias :visit_Arel_Nodes_Matches :binary
alias :visit_Arel_Nodes_NotEqual :binary
alias :visit_Arel_Nodes_NotIn :binary
diff --git a/activerecord/lib/arel/visitors/ibm_db.rb b/activerecord/lib/arel/visitors/ibm_db.rb
index 0a06aef60b..73166054da 100644
--- a/activerecord/lib/arel/visitors/ibm_db.rb
+++ b/activerecord/lib/arel/visitors/ibm_db.rb
@@ -10,6 +10,12 @@ module Arel # :nodoc: all
collector = visit o.expr, collector
collector << " ROWS ONLY"
end
+
+ def is_distinct_from(o, collector)
+ collector << "DECODE("
+ collector = visit [o.left, o.right, 0, 1], collector
+ collector << ")"
+ end
end
end
end
diff --git a/activerecord/lib/arel/visitors/mssql.rb b/activerecord/lib/arel/visitors/mssql.rb
index d564e19089..fdd864b40d 100644
--- a/activerecord/lib/arel/visitors/mssql.rb
+++ b/activerecord/lib/arel/visitors/mssql.rb
@@ -12,6 +12,31 @@ module Arel # :nodoc: all
private
+ def visit_Arel_Nodes_IsNotDistinctFrom(o, collector)
+ right = o.right
+
+ if right.nil?
+ collector = visit o.left, collector
+ collector << " IS NULL"
+ else
+ collector << "EXISTS (VALUES ("
+ collector = visit o.left, collector
+ collector << ") INTERSECT VALUES ("
+ collector = visit right, collector
+ collector << "))"
+ end
+ end
+
+ def visit_Arel_Nodes_IsDistinctFrom(o, collector)
+ if o.right.nil?
+ collector = visit o.left, collector
+ collector << " IS NOT NULL"
+ else
+ collector << "NOT "
+ visit_Arel_Nodes_IsNotDistinctFrom o, collector
+ end
+ end
+
def visit_Arel_Visitors_MSSQL_RowNumber(o, collector)
collector << "ROW_NUMBER() OVER (ORDER BY "
inject_join(o.children, collector, ", ") << ") as _row_num"
diff --git a/activerecord/lib/arel/visitors/mysql.rb b/activerecord/lib/arel/visitors/mysql.rb
index 081452caeb..dd77cfdf66 100644
--- a/activerecord/lib/arel/visitors/mysql.rb
+++ b/activerecord/lib/arel/visitors/mysql.rb
@@ -4,34 +4,6 @@ module Arel # :nodoc: all
module Visitors
class MySQL < Arel::Visitors::ToSql
private
- def visit_Arel_Nodes_Union(o, collector, suppress_parens = false)
- unless suppress_parens
- collector << "( "
- end
-
- collector = case o.left
- when Arel::Nodes::Union
- visit_Arel_Nodes_Union o.left, collector, true
- else
- visit o.left, collector
- end
-
- collector << " UNION "
-
- collector = case o.right
- when Arel::Nodes::Union
- visit_Arel_Nodes_Union o.right, collector, true
- else
- visit o.right, collector
- end
-
- if suppress_parens
- collector
- else
- collector << " )"
- end
- end
-
def visit_Arel_Nodes_Bin(o, collector)
collector << "BINARY "
visit o.expr, collector
@@ -65,30 +37,28 @@ module Arel # :nodoc: all
collector
end
+ def visit_Arel_Nodes_IsNotDistinctFrom(o, collector)
+ collector = visit o.left, collector
+ collector << " <=> "
+ visit o.right, collector
+ end
+
+ def visit_Arel_Nodes_IsDistinctFrom(o, collector)
+ collector << "NOT "
+ visit_Arel_Nodes_IsNotDistinctFrom o, collector
+ end
+
# In the simple case, MySQL allows us to place JOINs directly into the UPDATE
# query. However, this does not allow for LIMIT, OFFSET and ORDER. To support
# these, we must use a subquery.
def prepare_update_statement(o)
- if has_join_sources?(o)
- if has_limit_or_offset_or_orders?(o)
- super
- else
- o
- end
- elsif o.offset
- super
- else
- o
- end
- end
-
- def prepare_delete_statement(o)
- if has_join_sources?(o) || o.offset
+ if o.offset || has_join_sources?(o) && has_limit_or_offset_or_orders?(o)
super
else
o
end
end
+ alias :prepare_delete_statement :prepare_update_statement
# MySQL is too stupid to create a temporary table for use subquery, so we have
# to give it some prompting in the form of a subsubquery.
diff --git a/activerecord/lib/arel/visitors/oracle.rb b/activerecord/lib/arel/visitors/oracle.rb
index 30a1529d46..f96bf65ee5 100644
--- a/activerecord/lib/arel/visitors/oracle.rb
+++ b/activerecord/lib/arel/visitors/oracle.rb
@@ -148,6 +148,12 @@ module Arel # :nodoc: all
def visit_Arel_Nodes_BindParam(o, collector)
collector.add_bind(o.value) { |i| ":a#{i}" }
end
+
+ def is_distinct_from(o, collector)
+ collector << "DECODE("
+ collector = visit [o.left, o.right, 0, 1], collector
+ collector << ")"
+ end
end
end
end
diff --git a/activerecord/lib/arel/visitors/oracle12.rb b/activerecord/lib/arel/visitors/oracle12.rb
index 7061f06087..b092aa95e0 100644
--- a/activerecord/lib/arel/visitors/oracle12.rb
+++ b/activerecord/lib/arel/visitors/oracle12.rb
@@ -56,6 +56,12 @@ module Arel # :nodoc: all
def visit_Arel_Nodes_BindParam(o, collector)
collector.add_bind(o.value) { |i| ":a#{i}" }
end
+
+ def is_distinct_from(o, collector)
+ collector << "DECODE("
+ collector = visit [o.left, o.right, 0, 1], collector
+ collector << ")"
+ end
end
end
end
diff --git a/activerecord/lib/arel/visitors/postgresql.rb b/activerecord/lib/arel/visitors/postgresql.rb
index c5110fa89c..920776b4dc 100644
--- a/activerecord/lib/arel/visitors/postgresql.rb
+++ b/activerecord/lib/arel/visitors/postgresql.rb
@@ -77,6 +77,18 @@ module Arel # :nodoc: all
grouping_parentheses o, collector
end
+ def visit_Arel_Nodes_IsNotDistinctFrom(o, collector)
+ collector = visit o.left, collector
+ collector << " IS NOT DISTINCT FROM "
+ visit o.right, collector
+ end
+
+ def visit_Arel_Nodes_IsDistinctFrom(o, collector)
+ collector = visit o.left, collector
+ collector << " IS DISTINCT FROM "
+ visit o.right, collector
+ end
+
# Used by Lateral visitor to enclose select queries in parentheses
def grouping_parentheses(o, collector)
if o.expr.is_a? Nodes::SelectStatement
diff --git a/activerecord/lib/arel/visitors/sqlite.rb b/activerecord/lib/arel/visitors/sqlite.rb
index cb1d2424ad..af6f7e856a 100644
--- a/activerecord/lib/arel/visitors/sqlite.rb
+++ b/activerecord/lib/arel/visitors/sqlite.rb
@@ -22,6 +22,18 @@ module Arel # :nodoc: all
def visit_Arel_Nodes_False(o, collector)
collector << "0"
end
+
+ def visit_Arel_Nodes_IsNotDistinctFrom(o, collector)
+ collector = visit o.left, collector
+ collector << " IS "
+ visit o.right, collector
+ end
+
+ def visit_Arel_Nodes_IsDistinctFrom(o, collector)
+ collector = visit o.left, collector
+ collector << " IS NOT "
+ visit o.right, collector
+ end
end
end
end
diff --git a/activerecord/lib/arel/visitors/to_sql.rb b/activerecord/lib/arel/visitors/to_sql.rb
index 7c0f6c2e97..f9fe4404eb 100644
--- a/activerecord/lib/arel/visitors/to_sql.rb
+++ b/activerecord/lib/arel/visitors/to_sql.rb
@@ -76,10 +76,18 @@ module Arel # :nodoc: all
def visit_Arel_Nodes_DeleteStatement(o, collector)
o = prepare_delete_statement(o)
- collector << "DELETE FROM "
+ if has_join_sources?(o)
+ collector << "DELETE "
+ visit o.relation.left, collector
+ collector << " FROM "
+ else
+ collector << "DELETE FROM "
+ end
collector = visit o.relation, collector
- collect_where_for(o, collector)
+ collect_nodes_for o.wheres, collector, " WHERE ", " AND "
+ collect_nodes_for o.orders, collector, " ORDER BY "
+ maybe_visit o.limit, collector
end
def visit_Arel_Nodes_UpdateStatement(o, collector)
@@ -87,12 +95,11 @@ module Arel # :nodoc: all
collector << "UPDATE "
collector = visit o.relation, collector
- unless o.values.empty?
- collector << " SET "
- collector = inject_join o.values, collector, ", "
- end
+ collect_nodes_for o.values, collector, " SET "
- collect_where_for(o, collector)
+ collect_nodes_for o.wheres, collector, " WHERE ", " AND "
+ collect_nodes_for o.orders, collector, " ORDER BY "
+ maybe_visit o.limit, collector
end
def visit_Arel_Nodes_InsertStatement(o, collector)
@@ -225,10 +232,7 @@ module Arel # :nodoc: all
collect_nodes_for o.wheres, collector, WHERE, AND
collect_nodes_for o.groups, collector, GROUP_BY
- unless o.havings.empty?
- collector << " HAVING "
- inject_join o.havings, collector, AND
- end
+ collect_nodes_for o.havings, collector, " HAVING ", AND
collect_nodes_for o.windows, collector, WINDOW
collector
@@ -237,11 +241,7 @@ module Arel # :nodoc: all
def collect_nodes_for(nodes, collector, spacer, connector = COMMA)
unless nodes.empty?
collector << spacer
- len = nodes.length - 1
- nodes.each_with_index do |x, i|
- collector = visit(x, collector)
- collector << connector unless len == i
- end
+ inject_join nodes, collector, connector
end
end
@@ -268,13 +268,11 @@ module Arel # :nodoc: all
end
def visit_Arel_Nodes_Union(o, collector)
- collector << "( "
- infix_value(o, collector, " UNION ") << " )"
+ infix_value_with_paren(o, collector, " UNION ")
end
def visit_Arel_Nodes_UnionAll(o, collector)
- collector << "( "
- infix_value(o, collector, " UNION ALL ") << " )"
+ infix_value_with_paren(o, collector, " UNION ALL ")
end
def visit_Arel_Nodes_Intersect(o, collector)
@@ -296,10 +294,7 @@ module Arel # :nodoc: all
def visit_Arel_Nodes_Window(o, collector)
collector << "("
- if o.partitions.any?
- collector << "PARTITION BY "
- collector = inject_join o.partitions, collector, ", "
- end
+ collect_nodes_for o.partitions, collector, "PARTITION BY "
if o.orders.any?
collector << SPACE if o.partitions.any?
@@ -582,6 +577,10 @@ module Arel # :nodoc: all
end
def visit_Arel_Nodes_In(o, collector)
+ if Array === o.right && !o.right.empty?
+ o.right.keep_if { |value| boundable?(value) }
+ end
+
if Array === o.right && o.right.empty?
collector << "1=0"
else
@@ -592,6 +591,10 @@ module Arel # :nodoc: all
end
def visit_Arel_Nodes_NotIn(o, collector)
+ if Array === o.right && !o.right.empty?
+ o.right.keep_if { |value| boundable?(value) }
+ end
+
if Array === o.right && o.right.empty?
collector << "1=1"
else
@@ -638,6 +641,26 @@ module Arel # :nodoc: all
end
end
+ def visit_Arel_Nodes_IsNotDistinctFrom(o, collector)
+ if o.right.nil?
+ collector = visit o.left, collector
+ collector << " IS NULL"
+ else
+ collector = is_distinct_from(o, collector)
+ collector << " = 0"
+ end
+ end
+
+ def visit_Arel_Nodes_IsDistinctFrom(o, collector)
+ if o.right.nil?
+ collector = visit o.left, collector
+ collector << " IS NOT NULL"
+ else
+ collector = is_distinct_from(o, collector)
+ collector << " = 1"
+ end
+ end
+
def visit_Arel_Nodes_NotEqual(o, collector)
right = o.right
@@ -791,6 +814,10 @@ module Arel # :nodoc: all
}
end
+ def boundable?(value)
+ !value.respond_to?(:boundable?) || value.boundable?
+ end
+
def has_join_sources?(o)
o.relation.is_a?(Nodes::JoinSource) && !o.relation.right.empty?
end
@@ -830,26 +857,29 @@ module Arel # :nodoc: all
stmt
end
- def collect_where_for(o, collector)
- unless o.wheres.empty?
- collector << " WHERE "
- collector = inject_join o.wheres, collector, " AND "
- end
-
- unless o.orders.empty?
- collector << " ORDER BY "
- collector = inject_join o.orders, collector, ", "
- end
-
- maybe_visit o.limit, collector
- end
-
def infix_value(o, collector, value)
collector = visit o.left, collector
collector << value
visit o.right, collector
end
+ def infix_value_with_paren(o, collector, value, suppress_parens = false)
+ collector << "( " unless suppress_parens
+ collector = if o.left.class == o.class
+ infix_value_with_paren(o.left, collector, value, true)
+ else
+ visit o.left, collector
+ end
+ collector << value
+ collector = if o.right.class == o.class
+ infix_value_with_paren(o.right, collector, value, true)
+ else
+ visit o.right, collector
+ end
+ collector << " )" unless suppress_parens
+ collector
+ end
+
def aggregate(name, o, collector)
collector << "#{name}("
if o.distinct
@@ -863,6 +893,19 @@ module Arel # :nodoc: all
collector
end
end
+
+ def is_distinct_from(o, collector)
+ collector << "CASE WHEN "
+ collector = visit o.left, collector
+ collector << " = "
+ collector = visit o.right, collector
+ collector << " OR ("
+ collector = visit o.left, collector
+ collector << " IS NULL AND "
+ collector = visit o.right, collector
+ collector << " IS NULL)"
+ collector << " THEN 0 ELSE 1 END"
+ end
end
end
end