From 029d05fb4381316ccd5a698b2d285cb563e3b2cd Mon Sep 17 00:00:00 2001 From: Yasuo Honda Date: Wed, 13 Mar 2019 09:38:18 +0000 Subject: Address `ORA-01795: maximum number of expressions in a list is 1000` * To address this error, this commit splits expressions by slices of 1000 elements. * "Oracle Database Error Messages 18c" https://docs.oracle.com/en/database/oracle/oracle-database/18/errmg/ ``` ORA-01795: maximum number of expressions in a list is 1000 Cause: Number of expressions in the query exceeded than 1000. Note that unused column/expressions are also counted Maximum number of expressions that are allowed are 1000. ``` * This commit addresses this ORA-01795 error Note: Actually addressing this error raises another "ORA-00913: too many values" Number of values Oracle database allows is 65535 regardless bind values or literal values. ```ruby $ ARCONN=oracle bin/test test/cases/bind_parameter_test.rb -n test_too_many_binds ... snip ... Error: ActiveRecord::BindParameterTest#test_too_many_binds: ActiveRecord::StatementInvalid: OCIError: ORA-01795: maximum number of expressions in a list is 1000 stmt.c:267:in oci8lib_260.so /home/yahonda/.rbenv/versions/2.6.2/lib/ruby/gems/2.6.0/gems/ruby-oci8-2.2.7/lib/oci8/cursor.rb:131:in `exec' /home/yahonda/git/oracle-enhanced/lib/active_record/connection_adapters/oracle_enhanced/oci_connection.rb:142:in `exec' /home/yahonda/git/oracle-enhanced/lib/active_record/connection_adapters/oracle_enhanced/database_statements.rb:41:in `block in exec_query' /home/yahonda/git/rails/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:676:in `block (2 levels) in log' /home/yahonda/.rbenv/versions/2.6.2/lib/ruby/2.6.0/monitor.rb:230:in `mon_synchronize' /home/yahonda/git/rails/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:675:in `block in log' /home/yahonda/git/rails/activesupport/lib/active_support/notifications/instrumenter.rb:24:in `instrument' /home/yahonda/git/rails/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:666:in `log' /home/yahonda/git/oracle-enhanced/lib/active_record/connection_adapters/oracle_enhanced/dbms_output.rb:36:in `log' /home/yahonda/git/oracle-enhanced/lib/active_record/connection_adapters/oracle_enhanced/database_statements.rb:24:in `exec_query' /home/yahonda/git/rails/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:484:in `select' /home/yahonda/git/rails/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:70:in `select_all' /home/yahonda/git/rails/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb:106:in `select_all' /home/yahonda/git/rails/activerecord/lib/active_record/relation/calculations.rb:299:in `block in execute_simple_calculation' /home/yahonda/git/rails/activerecord/lib/active_record/relation.rb:755:in `skip_query_cache_if_necessary' /home/yahonda/git/rails/activerecord/lib/active_record/relation/calculations.rb:299:in `execute_simple_calculation' /home/yahonda/git/rails/activerecord/lib/active_record/relation/calculations.rb:251:in `perform_calculation' /home/yahonda/git/rails/activerecord/lib/active_record/relation/calculations.rb:141:in `calculate' /home/yahonda/git/rails/activerecord/lib/active_record/relation/calculations.rb:49:in `count' /home/yahonda/git/rails/activerecord/test/cases/bind_parameter_test.rb:113:in `test_too_many_binds' bin/test test/cases/bind_parameter_test.rb:109 .............................F ``` --- activerecord/lib/arel/visitors/oracle.rb | 48 ++++++++++++++++++++++++++++++ activerecord/lib/arel/visitors/oracle12.rb | 48 ++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+) (limited to 'activerecord/lib') diff --git a/activerecord/lib/arel/visitors/oracle.rb b/activerecord/lib/arel/visitors/oracle.rb index f96bf65ee5..500974dff5 100644 --- a/activerecord/lib/arel/visitors/oracle.rb +++ b/activerecord/lib/arel/visitors/oracle.rb @@ -87,6 +87,50 @@ 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? @@ -154,6 +198,10 @@ 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 9a7fe4d626..8e0f07fca9 100644 --- a/activerecord/lib/arel/visitors/oracle12.rb +++ b/activerecord/lib/arel/visitors/oracle12.rb @@ -41,6 +41,50 @@ 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? @@ -62,6 +106,10 @@ module Arel # :nodoc: all collector = visit [o.left, o.right, 0, 1], collector collector << ")" end + + def in_clause_length + 1000 + end end end end -- cgit v1.2.3