diff options
author | Sean Griffin <sean@seantheprogrammer.com> | 2015-10-20 14:08:15 -0600 |
---|---|---|
committer | Sean Griffin <sean@seantheprogrammer.com> | 2015-10-20 14:16:22 -0600 |
commit | cbcdecd2c55fca9613722779231de2d8dd67ad02 (patch) | |
tree | 2a7c03913b2206816a09c00994812271db8f14cc /activerecord/test/cases/adapters | |
parent | 72fba7db413561cf293178d5df0708c9380356fb (diff) | |
download | rails-cbcdecd2c55fca9613722779231de2d8dd67ad02.tar.gz rails-cbcdecd2c55fca9613722779231de2d8dd67ad02.tar.bz2 rails-cbcdecd2c55fca9613722779231de2d8dd67ad02.zip |
Do not cache prepared statements that are unlikely to have cache hits
Prior to this commit, Rails makes no differentiation between whether a
query uses bind parameters, and whether or not we cache that query as a
prepared statement. This leads to the cache populating extremely fast in
some cases, with the statements never being reused.
In particular, the two problematic cases are `where(foo: [1, 2, 3])` and
`where("foo = ?", 1)`. In both cases we'll end up quoting the values
rather than using a bind param, causing a cache entry for every value
ever used in that query.
It was noted that we can probably eventually change `where("foo = ?",
1)` to use a bind param, which would resolve that case. Additionally, on
PG we can change our generated query to be `WHERE foo = ANY($1)`, and
pass an array for the bind param. I hope to accomplish both in the
future.
For SQLite and MySQL, we still end up preparing the statements anyway,
we just don't cache it. The statement will be cleaned up after it is
executed. On postgres, we skip the prepare step entirely, as an API is
provided to execute with bind params without preparing the statement.
I'm not 100% happy on the way this ended up being structured. I was
hoping to use a decorator on the visitor, rather than mixing a module
into the object, but the way Arel has it's visitor pattern set up makes
it very difficult to extend without inheritance. I'd like to remove the
duplication from the various places that are extending it, but that'll
require a larger restructuring of that initialization logic. I'm going
to take another look at the structure of it soon.
This changes the signature of one of the adapter's internals, and will
require downstream changes from third party adapters. I'm not too
worried about this, as worst case they can simply add the parameter and
always ignore it, and just keep their previous behavior.
Fixes #21992.
Diffstat (limited to 'activerecord/test/cases/adapters')
-rw-r--r-- | activerecord/test/cases/adapters/postgresql/connection_test.rb | 2 | ||||
-rw-r--r-- | activerecord/test/cases/adapters/postgresql/schema_test.rb | 2 |
2 files changed, 2 insertions, 2 deletions
diff --git a/activerecord/test/cases/adapters/postgresql/connection_test.rb b/activerecord/test/cases/adapters/postgresql/connection_test.rb index 820d41e13b..722e2377c1 100644 --- a/activerecord/test/cases/adapters/postgresql/connection_test.rb +++ b/activerecord/test/cases/adapters/postgresql/connection_test.rb @@ -127,7 +127,7 @@ module ActiveRecord def test_statement_key_is_logged bind = Relation::QueryAttribute.new(nil, 1, Type::Value.new) - @connection.exec_query('SELECT $1::integer', 'SQL', [bind]) + @connection.exec_query('SELECT $1::integer', 'SQL', [bind], prepare: true) name = @subscriber.payloads.last[:statement_name] assert name res = @connection.exec_query("EXPLAIN (FORMAT JSON) EXECUTE #{name}(1)") diff --git a/activerecord/test/cases/adapters/postgresql/schema_test.rb b/activerecord/test/cases/adapters/postgresql/schema_test.rb index f89a394f96..93e98ec872 100644 --- a/activerecord/test/cases/adapters/postgresql/schema_test.rb +++ b/activerecord/test/cases/adapters/postgresql/schema_test.rb @@ -168,7 +168,7 @@ class SchemaTest < ActiveRecord::PostgreSQLTestCase def test_raise_wraped_exception_on_bad_prepare assert_raises(ActiveRecord::StatementInvalid) do - @connection.exec_query "select * from developers where id = ?", 'sql', [[nil, 1]] + @connection.exec_query "select * from developers where id = ?", 'sql', [bind_param(1)] end end |