diff options
author | Rafael Mendonça França <rafaelmfranca@gmail.com> | 2013-04-10 16:02:26 -0300 |
---|---|---|
committer | Rafael Mendonça França <rafaelmfranca@gmail.com> | 2013-04-10 16:02:26 -0300 |
commit | 0235cdf5ce0e92b88d49b8e135e696a89b76d097 (patch) | |
tree | 2a5fe1d457ca2ea10e95448eaf84ca6605983741 | |
parent | 0b38c84332a6e19250e4c2e34ebe46cd618360b6 (diff) | |
parent | af1a4bdc564864e80b234755651303dac68ae82e (diff) | |
download | rails-0235cdf5ce0e92b88d49b8e135e696a89b76d097.tar.gz rails-0235cdf5ce0e92b88d49b8e135e696a89b76d097.tar.bz2 rails-0235cdf5ce0e92b88d49b8e135e696a89b76d097.zip |
Merge pull request #10152 from Noemj/statement_cache
Statement cache
Conflicts:
activerecord/CHANGELOG.md
-rw-r--r-- | activerecord/CHANGELOG.md | 17 | ||||
-rw-r--r-- | activerecord/lib/active_record.rb | 1 | ||||
-rw-r--r-- | activerecord/lib/active_record/statement_cache.rb | 26 | ||||
-rw-r--r-- | activerecord/test/cases/statement_cache_test.rb | 64 |
4 files changed, 108 insertions, 0 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 57a054074b..cf697d8b16 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,5 +1,22 @@ ## Rails 4.0.0 (unreleased) ## +* Added Statement Cache to allow the caching of a single statement. The cache works by + duping the relation returned from yielding a statement which allows skipping the AST + building phase. + + Example: + + cache = ActiveRecord::StatementCache.new do + Book.where(:name => "my book").limit(100) + end + + books = cache.execute + + The solution attempts to get closer to the speed of `find_by_sql` but still maintaining + the expressiveness of the Active Record queries. + + *Olli Rissanen* + * Preserve context while merging relations with join information. class Comment < ActiveRecord::Base diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb index 249eabfd7d..0330c0f37f 100644 --- a/activerecord/lib/active_record.rb +++ b/activerecord/lib/active_record.rb @@ -57,6 +57,7 @@ module ActiveRecord autoload :SchemaMigration autoload :Scoping autoload :Serialization + autoload :StatementCache autoload :Store autoload :Timestamp autoload :Transactions diff --git a/activerecord/lib/active_record/statement_cache.rb b/activerecord/lib/active_record/statement_cache.rb new file mode 100644 index 0000000000..4a8c54414d --- /dev/null +++ b/activerecord/lib/active_record/statement_cache.rb @@ -0,0 +1,26 @@ +module ActiveRecord + + # Statement cache is used to cache a single statement in order to avoid creating the AST again. + # Initializing the cache is done by passing the statement in the initialization block: + # + # cache = ActiveRecord::StatementCache.new do + # Book.where(:name => "my book").limit(100) + # end + # + # The cached statement is executed by using the +execute+ method: + # + # cache.execute + # + # The relation returned by yield is cached, and for each +execute+ call the cached relation gets duped. + # Database is queried when +to_a+ is called on the relation. + class StatementCache + def initialize + @relation = yield + raise ArgumentError.new("Statement cannot be nil") if @relation.nil? + end + + def execute + @relation.dup.to_a + end + end +end diff --git a/activerecord/test/cases/statement_cache_test.rb b/activerecord/test/cases/statement_cache_test.rb new file mode 100644 index 0000000000..a8c97cccd4 --- /dev/null +++ b/activerecord/test/cases/statement_cache_test.rb @@ -0,0 +1,64 @@ +require 'cases/helper' +require 'models/book' +require 'models/liquid' +require 'models/molecule' +require 'models/electron' + +module ActiveRecord + class StatementCacheTest < ActiveRecord::TestCase + def setup + @connection = ActiveRecord::Base.connection + end + + def test_statement_cache_with_simple_statement + cache = ActiveRecord::StatementCache.new do + Book.where(name: "my book").where("author_id > 3") + end + + Book.create(name: "my book", author_id: 4) + + books = cache.execute + assert_equal "my book", books[0].name + end + + def test_statement_cache_with_nil_statement_raises_error + assert_raise(ArgumentError) do + cache = ActiveRecord::StatementCache.new do + nil + end + end + end + + def test_statement_cache_with_complex_statement + cache = ActiveRecord::StatementCache.new do + Liquid.joins(:molecules => :electrons).where('molecules.name' => 'dioxane', 'electrons.name' => 'lepton') + end + + salty = Liquid.create(name: 'salty') + molecule = salty.molecules.create(name: 'dioxane') + electron = molecule.electrons.create(name: 'lepton') + + liquids = cache.execute + assert_equal "salty", liquids[0].name + end + + def test_statement_cache_values_differ + cache = ActiveRecord::StatementCache.new do + Book.where(name: "my book") + end + + for i in 0..2 do + Book.create(name: "my book") + end + + first_books = cache.execute + + for i in 0..2 do + Book.create(name: "my book") + end + + additional_books = cache.execute + assert first_books != additional_books + end + end +end |