diff options
author | Aaron Patterson <aaron.patterson@gmail.com> | 2014-02-14 18:01:12 -0800 |
---|---|---|
committer | Aaron Patterson <aaron.patterson@gmail.com> | 2014-02-14 18:01:12 -0800 |
commit | bfc776f7bb114e90cf91f16f5892be636ed2f0c8 (patch) | |
tree | a2e09840eaa58fb7ac01bf6db88443400bb915d5 /activerecord | |
parent | 4e823b61190388219868744a34dcfe926bad511c (diff) | |
download | rails-bfc776f7bb114e90cf91f16f5892be636ed2f0c8.tar.gz rails-bfc776f7bb114e90cf91f16f5892be636ed2f0c8.tar.bz2 rails-bfc776f7bb114e90cf91f16f5892be636ed2f0c8.zip |
add factory methods for empty alias trackers
If we know the alias tracker is empty, we can create one that doesn't
use a hash with default block for counting.
ActiveRecord::Base.establish_connection(adapter: 'sqlite3',
database: ':memory:')
ActiveRecord::Schema.define do
create_table :posts, force: true do |t|
t.integer :comments_count
end
create_table :comments, force: true do |t|
t.integer :post_id
end
end
class Post < ActiveRecord::Base; has_many :comments; end
class Comment < ActiveRecord::Base; belongs_to :post, counter_cache: true; end
10.times { Comment.create!(post: Post.create!) }
record = Post.first
association_name = :comments
Benchmark.ips do |x|
reflection = record.class.reflect_on_association(association_name)
association = reflection.association_class.new(record, reflection)
x.report('assoc') do
reflection.association_class.new(record, reflection)
end
x.report('reader') do
association.reader;nil
end
x.report('combined') do
reflection.association_class.new(record, reflection).reader;nil
end
end
[aaron@higgins rails (tracker)]$ TEST=ips bundle exec ruby ../1bb5456b5e035343df9d/gistfile1.rb
-- create_table(:posts, {:force=>true})
-> 0.0062s
-- create_table(:comments, {:force=>true})
-> 0.0003s
Calculating -------------------------------------
assoc 833 i/100ms
reader 28703 i/100ms
combined 839 i/100ms
-------------------------------------------------
assoc 9010.3 (±3.8%) i/s - 44982 in 5.000022s
reader 3214523.4 (±5.5%) i/s - 16016274 in 5.001136s
combined 8841.0 (±5.8%) i/s - 44467 in 5.049269s
[aaron@higgins rails (tracker)]$ TEST=ips bundle exec ruby ../1bb5456b5e035343df9d/gistfile1.rb
-- create_table(:posts, {:force=>true})
-> 0.0060s
-- create_table(:comments, {:force=>true})
-> 0.0003s
Calculating -------------------------------------
assoc 888 i/100ms
reader 29217 i/100ms
combined 900 i/100ms
-------------------------------------------------
assoc 9674.3 (±3.3%) i/s - 48840 in 5.054022s
reader 2988474.8 (±6.9%) i/s - 14842236 in 4.998230s
combined 9674.0 (±3.1%) i/s - 48600 in 5.028694s
Diffstat (limited to 'activerecord')
3 files changed, 38 insertions, 23 deletions
diff --git a/activerecord/lib/active_record/associations/alias_tracker.rb b/activerecord/lib/active_record/associations/alias_tracker.rb index ea8e1f5054..85109aee6c 100644 --- a/activerecord/lib/active_record/associations/alias_tracker.rb +++ b/activerecord/lib/active_record/associations/alias_tracker.rb @@ -7,10 +7,43 @@ module ActiveRecord class AliasTracker # :nodoc: attr_reader :aliases, :connection + def self.empty(connection) + new connection, Hash.new(0) + end + + def self.create(connection, table_joins) + if table_joins.empty? + empty connection + else + aliases = Hash.new { |h,k| + h[k] = initial_count_for(connection, k, table_joins) + } + new connection, aliases + end + end + + def self.initial_count_for(connection, name, table_joins) + # quoted_name should be downcased as some database adapters (Oracle) return quoted name in uppercase + quoted_name = connection.quote_table_name(name).downcase + + counts = table_joins.map do |join| + if join.is_a?(Arel::Nodes::StringJoin) + # Table names + table aliases + join.left.downcase.scan( + /join(?:\s+\w+)?\s+(\S+\s+)?#{quoted_name}\son/ + ).size + else + join.left.table_name == name ? 1 : 0 + end + end + + counts.sum + end + # table_joins is an array of arel joins which might conflict with the aliases we assign here - def initialize(connection, table_joins) - @aliases = Hash.new { |h,k| h[k] = initial_count_for(k, table_joins) } - @connection = connection + def initialize(connection, aliases) + @aliases = aliases + @connection = connection end def aliased_table_for(table_name, aliased_name) @@ -45,24 +78,6 @@ module ActiveRecord private - def initial_count_for(name, table_joins) - # quoted_name should be downcased as some database adapters (Oracle) return quoted name in uppercase - quoted_name = connection.quote_table_name(name).downcase - - counts = table_joins.map do |join| - if join.is_a?(Arel::Nodes::StringJoin) - # Table names + table aliases - join.left.downcase.scan( - /join(?:\s+\w+)?\s+(\S+\s+)?#{quoted_name}\son/ - ).size - else - join.left.table_name == name ? 1 : 0 - end - end - - counts.sum - end - def truncate(name) name.slice(0, connection.table_alias_length - 2) end diff --git a/activerecord/lib/active_record/associations/association_scope.rb b/activerecord/lib/active_record/associations/association_scope.rb index 63e81a17aa..27fd9e35db 100644 --- a/activerecord/lib/active_record/associations/association_scope.rb +++ b/activerecord/lib/active_record/associations/association_scope.rb @@ -12,7 +12,7 @@ module ActiveRecord reflection = association.reflection scope = klass.unscoped owner = association.owner - alias_tracker = AliasTracker.new(connection, []) + alias_tracker = AliasTracker.empty connection scope.extending! Array(reflection.options[:extend]) add_constraints(scope, owner, klass, reflection, alias_tracker) diff --git a/activerecord/lib/active_record/associations/join_dependency.rb b/activerecord/lib/active_record/associations/join_dependency.rb index 1c8a44d0c5..94f69d4c2d 100644 --- a/activerecord/lib/active_record/associations/join_dependency.rb +++ b/activerecord/lib/active_record/associations/join_dependency.rb @@ -93,7 +93,7 @@ module ActiveRecord # joins # => [] # def initialize(base, associations, joins) - @alias_tracker = AliasTracker.new(base.connection, joins) + @alias_tracker = AliasTracker.create(base.connection, joins) @alias_tracker.aliased_name_for(base.table_name, base.table_name) # Updates the count for base.table_name to 1 tree = self.class.make_tree associations @join_root = JoinBase.new base, build(tree, base) |