diff options
author | Ryuta Kamizono <kamipo@gmail.com> | 2019-04-24 17:33:36 +0900 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-04-24 17:33:36 +0900 |
commit | b7c7f9bb80bd45a2ab782af8e88f00b7e21752e1 (patch) | |
tree | 6902aaec7f9e8e6053949b94b96597bb16331005 /activerecord/lib/arel/visitors | |
parent | 2c0729d8cb13100ea576337ebb7703320203c548 (diff) | |
parent | 5d858b5d3e5539ede0ea541a8e14126a3ee30800 (diff) | |
download | rails-b7c7f9bb80bd45a2ab782af8e88f00b7e21752e1.tar.gz rails-b7c7f9bb80bd45a2ab782af8e88f00b7e21752e1.tar.bz2 rails-b7c7f9bb80bd45a2ab782af8e88f00b7e21752e1.zip |
Merge pull request #36074 from kamipo/in_clause_length
Fix sliced IN clauses to be grouped
Diffstat (limited to 'activerecord/lib/arel/visitors')
-rw-r--r-- | activerecord/lib/arel/visitors/oracle.rb | 48 | ||||
-rw-r--r-- | activerecord/lib/arel/visitors/oracle12.rb | 57 | ||||
-rw-r--r-- | activerecord/lib/arel/visitors/to_sql.rb | 58 |
3 files changed, 49 insertions, 114 deletions
diff --git a/activerecord/lib/arel/visitors/oracle.rb b/activerecord/lib/arel/visitors/oracle.rb index 500974dff5..f96bf65ee5 100644 --- a/activerecord/lib/arel/visitors/oracle.rb +++ b/activerecord/lib/arel/visitors/oracle.rb @@ -87,50 +87,6 @@ module Arel # :nodoc: all collector << " )" end - def visit_Arel_Nodes_In(o, collector) - if Array === o.right && !o.right.empty? - o.right.delete_if { |value| unboundable?(value) } - end - - if Array === o.right && o.right.empty? - collector << "1=0" - else - first = true - o.right.each_slice(in_clause_length) do |sliced_o_right| - collector << " OR " unless first - first = false - - collector = visit o.left, collector - collector << " IN (" - visit(sliced_o_right, collector) - collector << ")" - end - end - collector - end - - def visit_Arel_Nodes_NotIn(o, collector) - if Array === o.right && !o.right.empty? - o.right.delete_if { |value| unboundable?(value) } - end - - if Array === o.right && o.right.empty? - collector << "1=1" - else - first = true - o.right.each_slice(in_clause_length) do |sliced_o_right| - collector << " AND " unless first - first = false - - collector = visit o.left, collector - collector << " NOT IN (" - visit(sliced_o_right, collector) - collector << ")" - end - end - collector - end - def visit_Arel_Nodes_UpdateStatement(o, collector) # Oracle does not allow ORDER BY/LIMIT in UPDATEs. if o.orders.any? && o.limit.nil? @@ -198,10 +154,6 @@ module Arel # :nodoc: all collector = visit [o.left, o.right, 0, 1], collector collector << ")" end - - def in_clause_length - 1000 - end end end end diff --git a/activerecord/lib/arel/visitors/oracle12.rb b/activerecord/lib/arel/visitors/oracle12.rb index 8e0f07fca9..6269bc3907 100644 --- a/activerecord/lib/arel/visitors/oracle12.rb +++ b/activerecord/lib/arel/visitors/oracle12.rb @@ -8,11 +8,10 @@ module Arel # :nodoc: all def visit_Arel_Nodes_SelectStatement(o, collector) # Oracle does not allow LIMIT clause with select for update if o.limit && o.lock - raise ArgumentError, <<-MSG - 'Combination of limit and lock is not supported. - because generated SQL statements - `SELECT FOR UPDATE and FETCH FIRST n ROWS` generates ORA-02014.` - MSG + raise ArgumentError, <<~MSG + Combination of limit and lock is not supported. Because generated SQL statements + `SELECT FOR UPDATE and FETCH FIRST n ROWS` generates ORA-02014. + MSG end super end @@ -41,50 +40,6 @@ module Arel # :nodoc: all collector << " )" end - def visit_Arel_Nodes_In(o, collector) - if Array === o.right && !o.right.empty? - o.right.delete_if { |value| unboundable?(value) } - end - - if Array === o.right && o.right.empty? - collector << "1=0" - else - first = true - o.right.each_slice(in_clause_length) do |sliced_o_right| - collector << " OR " unless first - first = false - - collector = visit o.left, collector - collector << " IN (" - visit(sliced_o_right, collector) - collector << ")" - end - end - collector - end - - def visit_Arel_Nodes_NotIn(o, collector) - if Array === o.right && !o.right.empty? - o.right.delete_if { |value| unboundable?(value) } - end - - if Array === o.right && o.right.empty? - collector << "1=1" - else - first = true - o.right.each_slice(in_clause_length) do |sliced_o_right| - collector << " AND " unless first - first = false - - collector = visit o.left, collector - collector << " NOT IN (" - visit(sliced_o_right, collector) - collector << ")" - end - end - collector - end - def visit_Arel_Nodes_UpdateStatement(o, collector) # Oracle does not allow ORDER BY/LIMIT in UPDATEs. if o.orders.any? && o.limit.nil? @@ -106,10 +61,6 @@ module Arel # :nodoc: all collector = visit [o.left, o.right, 0, 1], collector collector << ")" end - - def in_clause_length - 1000 - end end end end diff --git a/activerecord/lib/arel/visitors/to_sql.rb b/activerecord/lib/arel/visitors/to_sql.rb index 277d553e6c..4740e6d94f 100644 --- a/activerecord/lib/arel/visitors/to_sql.rb +++ b/activerecord/lib/arel/visitors/to_sql.rb @@ -513,34 +513,66 @@ module Arel # :nodoc: all end def visit_Arel_Nodes_In(o, collector) - if Array === o.right && !o.right.empty? + unless Array === o.right + return collect_in_clause(o.left, o.right, collector) + end + + unless o.right.empty? o.right.delete_if { |value| unboundable?(value) } end - if Array === o.right && o.right.empty? - collector << "1=0" + return collector << "1=0" if o.right.empty? + + in_clause_length = @connection.in_clause_length + + if !in_clause_length || o.right.length <= in_clause_length + collect_in_clause(o.left, o.right, collector) else - collector = visit o.left, collector - collector << " IN (" - visit(o.right, collector) << ")" + collector << "(" + o.right.each_slice(in_clause_length).each_with_index do |right, i| + collector << " OR " unless i == 0 + collect_in_clause(o.left, right, collector) + end + collector << ")" end end + def collect_in_clause(left, right, collector) + collector = visit left, collector + collector << " IN (" + visit(right, collector) << ")" + end + def visit_Arel_Nodes_NotIn(o, collector) - if Array === o.right && !o.right.empty? + unless Array === o.right + return collect_not_in_clause(o.left, o.right, collector) + end + + unless o.right.empty? o.right.delete_if { |value| unboundable?(value) } end - if Array === o.right && o.right.empty? - collector << "1=1" + return collector << "1=1" if o.right.empty? + + in_clause_length = @connection.in_clause_length + + if !in_clause_length || o.right.length <= in_clause_length + collect_not_in_clause(o.left, o.right, collector) else - collector = visit o.left, collector - collector << " NOT IN (" - collector = visit o.right, collector - collector << ")" + o.right.each_slice(in_clause_length).each_with_index do |right, i| + collector << " AND " unless i == 0 + collect_not_in_clause(o.left, right, collector) + end + collector end end + def collect_not_in_clause(left, right, collector) + collector = visit left, collector + collector << " NOT IN (" + visit(right, collector) << ")" + end + def visit_Arel_Nodes_And(o, collector) inject_join o.children, collector, " AND " end |