提交 f458b376 编写于 作者: T Tobias Lütke

Introducing Model.cache { ... } for the occasional query caching needs. (...

Introducing Model.cache { ... } for the occasional query caching needs. ( fantastic to reduce the 200 SELECT * from accounts WHERE id=1 queries in your views ) 


git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@6138 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
上级 23b2abe3
*SVN*
* Reworked David's query cache to be available as Model.cache {...}. For the duration of the block no select query should be run more then once. Any inserts/deletes/executes will flush the whole cache however [Tobias Luetke]
Task.cache { Task.find(1); Task.find(1) } #=> 1 query
* When dealing with SQLite3, use the table_info pragma helper, so that the bindings can do some translation for when sqlite3 breaks incompatibly between point releases. [Jamis Buck]
* Oracle: fix lob and text default handling. #7344 [gfriedrich, Michael Schoen]
......
......@@ -6,7 +6,7 @@ def initialize(connection)
end
def clear_query_cache
@query_cache = {}
@query_cache.clear
end
def select_all(sql, name = nil)
......@@ -16,14 +16,27 @@ def select_all(sql, name = nil)
def select_one(sql, name = nil)
@query_cache[sql] ||= @connection.select_one(sql, name)
end
def select_values(sql, name = nil)
(@query_cache[sql] ||= @connection.select_values(sql, name)).dup
end
def select_value(sql, name = nil)
@query_cache[sql] ||= @connection.select_value(sql, name)
end
def execute(sql, name = nil)
clear_query_cache
@connection.execute(sql, name)
end
def columns(table_name, name = nil)
@query_cache["SHOW FIELDS FROM #{table_name}"] ||= @connection.columns(table_name, name)
end
def insert(sql, name = nil, pk = nil, id_value = nil)
def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
clear_query_cache
@connection.insert(sql, name, pk, id_value)
@connection.insert(sql, name, pk, id_value, sequence_name)
end
def update(sql, name = nil)
......@@ -41,24 +54,26 @@ def method_missing(method, *arguments, &proc)
@connection.send(method, *arguments, &proc)
end
end
class Base
# Set the connection for the class with caching on
class << self
alias_method :connection_without_query_cache=, :connection=
def connection=(spec)
if spec.is_a?(ConnectionSpecification) and spec.config[:query_cache]
spec = QueryCache.new(self.send(spec.adapter_method, spec.config))
end
self.connection_without_query_cache = spec
alias_method :connection_without_query_cache, :connection
def query_caches
(Thread.current[:query_cache] ||= {})
end
def cache
query_caches[self] = QueryCache.new(connection)
yield
ensure
query_caches[self] = nil
end
def connection
query_caches[self] || connection_without_query_cache
end
end
end
class AbstractAdapter #:nodoc:
# Stub method to be able to treat the connection the same whether the query cache has been turned on or not
def clear_query_cache
end
end
end
end
......@@ -36,16 +36,10 @@ def assert_date_from_db(expected, actual, message = nil)
end
def assert_queries(num = 1)
ActiveRecord::Base.connection.class.class_eval do
self.query_count = 0
alias_method :execute, :execute_with_query_counting
end
$query_count = 0
yield
ensure
ActiveRecord::Base.connection.class.class_eval do
alias_method :execute, :execute_without_query_counting
end
assert_equal num, ActiveRecord::Base.connection.query_count, "#{ActiveRecord::Base.connection.query_count} instead of #{num} queries were executed."
assert_equal num, $query_count, "#{$query_count} instead of #{num} queries were executed."
end
def assert_no_queries(&block)
......@@ -60,16 +54,26 @@ def current_adapter?(*types)
end
end
ActiveRecord::Base.connection.class.class_eval do
cattr_accessor :query_count
def uses_mocha(test_name)
require 'mocha'
require 'stubba'
yield
rescue LoadError
$stderr.puts "Skipping #{test_name} tests. `gem install mocha` and try again."
end
# Array of regexes of queries that are not counted against query_count
@@ignore_list = [/^SELECT currval/, /^SELECT CAST/, /^SELECT @@IDENTITY/]
ActiveRecord::Base.connection.class.class_eval do
if not (const_get('IGNORED_SQL') rescue nil)
IGNORED_SQL = [/^PRAGMA/, /^SELECT currval/, /^SELECT CAST/, /^SELECT @@IDENTITY/]
def execute_with_counting(sql, name = nil, &block)
$query_count ||= 0
$query_count += 1 unless IGNORED_SQL.any? { |r| sql =~ r }
execute_without_counting(sql, name, &block)
end
alias_method :execute_without_query_counting, :execute
def execute_with_query_counting(sql, name = nil, &block)
self.query_count += 1 unless @@ignore_list.any? { |r| sql =~ r }
execute_without_query_counting(sql, name, &block)
alias_method_chain :execute, :counting
end
end
......
require 'abstract_unit'
require 'fixtures/topic'
require 'fixtures/reply'
require 'fixtures/task'
class QueryCacheTest < Test::Unit::TestCase
fixtures :tasks
def test_find_queries
assert_queries(2) { Task.find(1); Task.find(1) }
end
def test_find_queries_with_cache
Task.cache do
assert_queries(1) { Task.find(1); Task.find(1) }
end
end
def test_find_queries_with_cache
Task.cache do
assert_queries(1) { Task.find(1); Task.find(1) }
end
end
def test_cache_is_scoped_on_actual_class_only
Task.cache do
assert_queries(2) { Topic.find(1); Topic.find(1) }
end
end
end
uses_mocha('QueryCacheExpiryTest') do
class QueryCacheExpiryTest < Test::Unit::TestCase
fixtures :tasks
def test_find
ActiveRecord::QueryCache.any_instance.expects(:clear_query_cache).times(0)
Task.cache do
Task.find(1)
end
end
def test_save
ActiveRecord::QueryCache.any_instance.expects(:clear_query_cache).times(1)
Task.cache do
Task.find(1).save
end
end
def test_destroy
ActiveRecord::QueryCache.any_instance.expects(:clear_query_cache).at_least_once
Task.cache do
Task.find(1).destroy
end
end
def test_create
ActiveRecord::QueryCache.any_instance.expects(:clear_query_cache).times(1)
Task.cache do
Task.create!
end
end
def test_new_save
ActiveRecord::QueryCache.any_instance.expects(:clear_query_cache).times(1)
Task.cache do
Task.new.save
end
end
end
end
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册