Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save AquaGeek/969916 to your computer and use it in GitHub Desktop.
Save AquaGeek/969916 to your computer and use it in GitHub Desktop.
Rails Lighthouse ticket #1533
From 328011d9841b0d4999878b582e50a6e9489a5ae5 Mon Sep 17 00:00:00 2001
From: Nathan Zook <blogger@pierian-spring.net>
Date: Thu, 4 Mar 2010 17:14:19 -0600
Subject: [PATCH] Broke out joining the quoted values in ActiveRecord::Base (to support the Oracle adapter)
---
activerecord/lib/active_record/base.rb | 6 +++++-
1 files changed, 5 insertions(+), 1 deletions(-)
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index cd67490..68dec91 100755
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -1639,13 +1639,17 @@ module ActiveRecord #:nodoc:
if value.respond_to?(:empty?) && value.empty?
connection.quote(nil)
else
- value.map { |v| connection.quote(v) }.join(',')
+ join_quoted_values_for_condition(value.map{|v| connection.quote(v)})
end
else
connection.quote(value)
end
end
+ def join_quoted_values_for_condition(values) #:nodoc:
+ values * ','
+ end
+
def raise_if_bind_arity_mismatch(statement, expected, provided) #:nodoc:
unless expected == provided
raise PreparedStatementInvalid, "wrong number of bind variables (#{provided} for #{expected}) in: #{statement}"
--
1.6.0.4
commit 3e79b5f92602051b47f9d20cedf51dd96ff93b82
Author: Giorgio Gonnella <ggonnell@yahoo.it>
Date: Mon Mar 2 17:44:41 2009 +0100
Solves preloading more than 1000 associated records in Oracle
Rails often uses "in" sql clauses when preloading associations. Oracle has
a 1000 term restriction on such clauses, so that you cannot say something
like "WHERE parent_id IN (1,2,3,4 ... 1001)".
[#1533 state:resolved]
diff --git a/vendor/rails/activerecord/lib/active_record/association_preload.rb b/vendor/rails/activerecord/lib/active_record/association_preload.rb
index 69300e5..4ecad6f 100644
--- a/vendor/rails/activerecord/lib/active_record/association_preload.rb
+++ b/vendor/rails/activerecord/lib/active_record/association_preload.rb
@@ -322,8 +322,9 @@ module ActiveRecord
end
end
conditions = "#{table_name}.#{connection.quote_column_name(primary_key)} #{in_or_equals_for_ids(ids)}"
+ conditions = ([conditions] * (ids.size.to_f/1000).ceil).join(" OR ")
conditions << append_conditions(reflection, preload_options)
- associated_records = klass.find(:all, :conditions => [conditions, ids],
+ associated_records = klass.find(:all, :conditions => [conditions, *ids.in_groups_of(1000, false)],
:include => options[:include],
:select => options[:select],
:joins => options[:joins],
@@ -335,20 +336,21 @@ module ActiveRecord
def find_associated_records(ids, reflection, preload_options)
options = reflection.options
table_name = reflection.klass.quoted_table_name
-
+
if interface = reflection.options[:as]
conditions = "#{reflection.klass.quoted_table_name}.#{connection.quote_column_name "#{interface}_id"} #{in_or_equals_for_ids(ids)} and #{reflection.klass.quoted_table_name}.#{connection.quote_column_name "#{interface}_type"} = '#{self.base_class.sti_name}'"
else
foreign_key = reflection.primary_key_name
conditions = "#{reflection.klass.quoted_table_name}.#{foreign_key} #{in_or_equals_for_ids(ids)}"
end
-
+
+ conditions = ([conditions] * (ids.size.to_f/1000).ceil).join(" OR ")
conditions << append_conditions(reflection, preload_options)
-
+
reflection.klass.find(:all,
:select => (preload_options[:select] || options[:select] || "#{table_name}.*"),
:include => preload_options[:include] || options[:include],
- :conditions => [conditions, ids],
+ :conditions => [conditions, *ids.in_groups_of(1000, false)],
:joins => options[:joins],
:group => preload_options[:group] || options[:group],
:order => preload_options[:order] || options[:order])
# Ugly monkey patch so Rails can load associations with more than 1000 items in
# Oracle. See: https://rails.lighthouseapp.com/projects/8994/tickets/1533
module ActiveRecord
module AssociationPreloadOracleWorkaround
# Oracle's IN() clause limit.
ORACLE_LIMIT = 1000
# Oracle's limit on number of arguments to a function.
ORACLE_CHUNK = 255
def self.included(base)
base.extend(ClassMethods)
base.class_eval do
class << self
alias_method_chain(:in_or_equals_for_ids, :oracle_workaround)
end
end
end
module ClassMethods
# Override in_or_equals_for_ids to split IN clauses that have more than
# 1000 items in them.
def in_or_equals_for_ids_with_oracle_workaround(ids)
sql = ""
if(ids.size > ORACLE_LIMIT)
# If there are more than 1000, actually separate them into groups of
# 255, since that seems to be the limit of Oracle's odcinumberlist().
in_clauses = []
0.step(ids.size, ORACLE_CHUNK) do |i|
ids_chunk = ids[i, ORACLE_CHUNK]
in_clauses << sanitize_sql_array(["(SELECT * FROM TABLE(sys.odcinumberlist(?)))", ids_chunk])
end
sql = "IN(#{in_clauses.join(" UNION ")})"
else
sql = in_or_equals_for_ids_without_oracle_workaround(ids)
end
sql
end
end
end
end
ActiveRecord::Base.send :include, ActiveRecord::AssociationPreloadOracleWorkaround
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment