Skip to content

Commit 663df3a

Browse files
authored
Merge pull request rails#48069 from Shopify/ar-exec-query-flush-cache
Make Adapter#exec_query clear the query cache
2 parents 9b4fff2 + 3a32921 commit 663df3a

15 files changed

+80
-44
lines changed

activerecord/CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
* `AbstractAdapter#execute` and `#exec_query` now clear the query cache
2+
3+
If you need to perform a read only SQL query without clearing the query
4+
cache, use `AbstractAdapter#select_all`.
5+
6+
*Jean Boussier*
7+
18
* Make `.joins` / `.left_outer_joins` work with CTEs.
29

310
For example:

activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ def query_values(sql, name = nil) # :nodoc:
105105
end
106106

107107
def query(sql, name = nil) # :nodoc:
108-
exec_query(sql, name).rows
108+
internal_exec_query(sql, name).rows
109109
end
110110

111111
# Determines whether the SQL statement is a write query.
@@ -120,8 +120,12 @@ def write_query?(sql)
120120
# executing the SQL statement in case of a connection-related exception.
121121
# This option should only be enabled for known idempotent queries.
122122
#
123+
# Note: the query is assumed to have side effects and the query cache
124+
# will be cleared. If the query is read-only, consider using #select_all
125+
# instead.
126+
#
123127
# Note: depending on your database connector, the result returned by this
124-
# method may be manually memory managed. Consider using the exec_query
128+
# method may be manually memory managed. Consider using #exec_query
125129
# wrapper instead.
126130
def execute(sql, name = nil, allow_retry: false)
127131
internal_execute(sql, name, allow_retry: allow_retry)
@@ -130,34 +134,38 @@ def execute(sql, name = nil, allow_retry: false)
130134
# Executes +sql+ statement in the context of this connection using
131135
# +binds+ as the bind substitutes. +name+ is logged along with
132136
# the executed +sql+ statement.
137+
#
138+
# Note: the query is assumed to have side effects and the query cache
139+
# will be cleared. If the query is read-only, consider using #select_all
140+
# instead.
133141
def exec_query(sql, name = "SQL", binds = [], prepare: false)
134-
raise NotImplementedError
142+
internal_exec_query(sql, name, binds, prepare: prepare)
135143
end
136144

137145
# Executes insert +sql+ statement in the context of this connection using
138146
# +binds+ as the bind substitutes. +name+ is logged along with
139147
# the executed +sql+ statement.
140148
def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil)
141149
sql, binds = sql_for_insert(sql, pk, binds)
142-
exec_query(sql, name, binds)
150+
internal_exec_query(sql, name, binds)
143151
end
144152

145153
# Executes delete +sql+ statement in the context of this connection using
146154
# +binds+ as the bind substitutes. +name+ is logged along with
147155
# the executed +sql+ statement.
148156
def exec_delete(sql, name = nil, binds = [])
149-
exec_query(sql, name, binds)
157+
internal_exec_query(sql, name, binds)
150158
end
151159

152160
# Executes update +sql+ statement in the context of this connection using
153161
# +binds+ as the bind substitutes. +name+ is logged along with
154162
# the executed +sql+ statement.
155163
def exec_update(sql, name = nil, binds = [])
156-
exec_query(sql, name, binds)
164+
internal_exec_query(sql, name, binds)
157165
end
158166

159167
def exec_insert_all(sql, name) # :nodoc:
160-
exec_query(sql, name)
168+
internal_exec_query(sql, name)
161169
end
162170

163171
def explain(arel, binds = [], options = []) # :nodoc:
@@ -490,6 +498,10 @@ def high_precision_current_timestamp
490498
HIGH_PRECISION_CURRENT_TIMESTAMP
491499
end
492500

501+
def internal_exec_query(sql, name = "SQL", binds = [], prepare: false, async: false) # :nodoc:
502+
raise NotImplementedError
503+
end
504+
493505
private
494506
def internal_execute(sql, name = "SCHEMA", allow_retry: false, materialize_transactions: true)
495507
sql = transform_query(sql)
@@ -606,7 +618,7 @@ def select(sql, name = nil, binds = [], prepare: false, async: false)
606618
return future_result
607619
end
608620

609-
result = exec_query(sql, name, binds, prepare: prepare)
621+
result = internal_exec_query(sql, name, binds, prepare: prepare)
610622
if async
611623
FutureResult::Complete.new(result)
612624
else

activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@ module ConnectionAdapters # :nodoc:
77
module QueryCache
88
class << self
99
def included(base) # :nodoc:
10-
dirties_query_cache base, :execute, :create, :insert, :update, :delete, :truncate, :truncate_tables,
11-
:rollback_to_savepoint, :rollback_db_transaction, :restart_db_transaction, :exec_insert_all
10+
dirties_query_cache base, :exec_query, :execute, :create, :insert, :update, :delete, :truncate,
11+
:truncate_tables, :rollback_to_savepoint, :rollback_db_transaction, :restart_db_transaction,
12+
:exec_insert_all
1213

1314
base.set_callback :checkout, :after, :configure_query_cache!
1415
base.set_callback :checkin, :after, :disable_query_cache!
@@ -93,7 +94,7 @@ def clear_query_cache
9394
end
9495
end
9596

96-
def select_all(arel, name = nil, binds = [], preparable: nil, async: false)
97+
def select_all(arel, name = nil, binds = [], preparable: nil, async: false) # :nodoc:
9798
arel = arel_from_relation(arel)
9899

99100
# If arel is locked this is a SELECT ... FOR UPDATE or somesuch.

activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -464,7 +464,7 @@ def foreign_keys(table_name)
464464

465465
scope = quoted_scope(table_name)
466466

467-
fk_info = exec_query(<<~SQL, "SCHEMA")
467+
fk_info = internal_exec_query(<<~SQL, "SCHEMA")
468468
SELECT fk.referenced_table_name AS 'to_table',
469469
fk.referenced_column_name AS 'primary_key',
470470
fk.column_name AS 'column',
@@ -511,7 +511,7 @@ def check_constraints(table_name)
511511
SQL
512512
sql += " AND cc.table_name = #{scope[:name]}" if mariadb?
513513

514-
chk_info = exec_query(sql, "SCHEMA")
514+
chk_info = internal_exec_query(sql, "SCHEMA")
515515

516516
chk_info.map do |row|
517517
options = {
@@ -830,7 +830,7 @@ def rename_column_for_alter(table_name, column_name, new_column_name)
830830
comment: column.comment
831831
}
832832

833-
current_type = exec_query("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE #{quote(column_name)}", "SCHEMA").first["Type"]
833+
current_type = internal_exec_query("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE #{quote(column_name)}", "SCHEMA").first["Type"]
834834
td = create_table_definition(table_name)
835835
cd = td.new_column_definition(new_column_name, current_type, **options)
836836
schema_creation.accept(ChangeColumnDefinition.new(cd, column.name))
@@ -920,7 +920,7 @@ def column_definitions(table_name) # :nodoc:
920920
end
921921

922922
def create_table_info(table_name) # :nodoc:
923-
exec_query("SHOW CREATE TABLE #{quote_table_name(table_name)}", "SCHEMA").first["Create Table"]
923+
internal_exec_query("SHOW CREATE TABLE #{quote_table_name(table_name)}", "SCHEMA").first["Create Table"]
924924
end
925925

926926
def arel_visitor

activerecord/lib/active_record/connection_adapters/mysql/database_statements.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,13 @@ def write_query?(sql) # :nodoc:
3232
def explain(arel, binds = [], options = [])
3333
sql = build_explain_clause(options) + " " + to_sql(arel, binds)
3434
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
35-
result = exec_query(sql, "EXPLAIN", binds)
35+
result = internal_exec_query(sql, "EXPLAIN", binds)
3636
elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start
3737

3838
MySQL::ExplainPrettyPrinter.new.pp(result, elapsed)
3939
end
4040

41-
def exec_query(sql, name = "SQL", binds = [], prepare: false, async: false) # :nodoc:
41+
def internal_exec_query(sql, name = "SQL", binds = [], prepare: false, async: false) # :nodoc:
4242
if without_prepared_statement?(binds)
4343
execute_and_free(sql, name, async: async) do |result|
4444
if result

activerecord/lib/active_record/connection_adapters/mysql/schema_dumper.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ def schema_collation(column)
6666
if column.collation
6767
@table_collation_cache ||= {}
6868
@table_collation_cache[table_name] ||=
69-
@connection.exec_query("SHOW TABLE STATUS LIKE #{@connection.quote(table_name)}", "SCHEMA").first["Collation"]
69+
@connection.internal_exec_query("SHOW TABLE STATUS LIKE #{@connection.quote(table_name)}", "SCHEMA").first["Collation"]
7070
column.collation.inspect if column.collation != @table_collation_cache[table_name]
7171
end
7272
end

activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ module PostgreSQL
66
module DatabaseStatements
77
def explain(arel, binds = [], options = [])
88
sql = build_explain_clause(options) + " " + to_sql(arel, binds)
9-
result = exec_query(sql, "EXPLAIN", binds)
9+
result = internal_exec_query(sql, "EXPLAIN", binds)
1010
PostgreSQL::ExplainPrettyPrinter.new.pp(result)
1111
end
1212

@@ -57,7 +57,7 @@ def raw_execute(sql, name, async: false, allow_retry: false, materialize_transac
5757
end
5858
end
5959

60-
def exec_query(sql, name = "SQL", binds = [], prepare: false, async: false, allow_retry: false, materialize_transactions: true) # :nodoc:
60+
def internal_exec_query(sql, name = "SQL", binds = [], prepare: false, async: false, allow_retry: false, materialize_transactions: true) # :nodoc:
6161
execute_and_clear(sql, name, binds, prepare: prepare, async: async, allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |result|
6262
types = {}
6363
fields = result.fields
@@ -94,7 +94,7 @@ def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil) # :n
9494
if use_insert_returning? || pk == false
9595
super
9696
else
97-
result = exec_query(sql, name, binds)
97+
result = internal_exec_query(sql, name, binds)
9898
unless sequence_name
9999
table_ref = extract_table_ref_from_insert_sql(sql)
100100
if table_ref
@@ -169,7 +169,7 @@ def build_truncate_statements(table_names)
169169

170170
# Returns the current ID of a table's sequence.
171171
def last_insert_id_result(sequence_name)
172-
exec_query("SELECT currval(#{quote(sequence_name)})", "SQL")
172+
internal_exec_query("SELECT currval(#{quote(sequence_name)})", "SQL")
173173
end
174174

175175
def suppress_composite_primary_key(pk)

activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -520,7 +520,7 @@ def index_name(table_name, options) # :nodoc:
520520

521521
def foreign_keys(table_name)
522522
scope = quoted_scope(table_name)
523-
fk_info = exec_query(<<~SQL, "SCHEMA", allow_retry: true, materialize_transactions: false)
523+
fk_info = internal_exec_query(<<~SQL, "SCHEMA", allow_retry: true, materialize_transactions: false)
524524
SELECT t2.oid::regclass::text AS to_table, a1.attname AS column, a2.attname AS primary_key, c.conname AS name, c.confupdtype AS on_update, c.confdeltype AS on_delete, c.convalidated AS valid, c.condeferrable AS deferrable, c.condeferred AS deferred
525525
FROM pg_constraint c
526526
JOIN pg_class t1 ON c.conrelid = t1.oid
@@ -563,7 +563,7 @@ def foreign_table_exists?(table_name)
563563
def check_constraints(table_name) # :nodoc:
564564
scope = quoted_scope(table_name)
565565

566-
check_info = exec_query(<<-SQL, "SCHEMA", allow_retry: true, materialize_transactions: false)
566+
check_info = internal_exec_query(<<-SQL, "SCHEMA", allow_retry: true, materialize_transactions: false)
567567
SELECT conname, pg_get_constraintdef(c.oid, true) AS constraintdef, c.convalidated AS valid
568568
FROM pg_constraint c
569569
JOIN pg_class t ON c.conrelid = t.oid
@@ -589,7 +589,7 @@ def check_constraints(table_name) # :nodoc:
589589
def exclusion_constraints(table_name)
590590
scope = quoted_scope(table_name)
591591

592-
exclusion_info = exec_query(<<-SQL, "SCHEMA")
592+
exclusion_info = internal_exec_query(<<-SQL, "SCHEMA")
593593
SELECT conname, pg_get_constraintdef(c.oid) AS constraintdef, c.condeferrable, c.condeferred
594594
FROM pg_constraint c
595595
JOIN pg_class t ON c.conrelid = t.oid
@@ -623,7 +623,7 @@ def exclusion_constraints(table_name)
623623
def unique_keys(table_name)
624624
scope = quoted_scope(table_name)
625625

626-
unique_info = exec_query(<<~SQL, "SCHEMA", allow_retry: true, materialize_transactions: false)
626+
unique_info = internal_exec_query(<<~SQL, "SCHEMA", allow_retry: true, materialize_transactions: false)
627627
SELECT c.conname, c.conindid, c.condeferrable, c.condeferred
628628
FROM pg_constraint c
629629
JOIN pg_class t ON c.conrelid = t.oid

activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -459,7 +459,7 @@ def enable_extension(name, **)
459459
sql = +"CREATE EXTENSION IF NOT EXISTS \"#{name}\""
460460
sql << " SCHEMA #{schema}" if schema
461461

462-
exec_query(sql).tap { reload_type_map }
462+
internal_exec_query(sql).tap { reload_type_map }
463463
end
464464

465465
# Removes an extension from the database.
@@ -468,7 +468,7 @@ def enable_extension(name, **)
468468
# Set to +:cascade+ to drop dependent objects as well.
469469
# Defaults to false.
470470
def disable_extension(name, force: false)
471-
exec_query("DROP EXTENSION IF EXISTS \"#{name}\"#{' CASCADE' if force == :cascade}").tap {
471+
internal_exec_query("DROP EXTENSION IF EXISTS \"#{name}\"#{' CASCADE' if force == :cascade}").tap {
472472
reload_type_map
473473
}
474474
end
@@ -482,7 +482,7 @@ def extension_enabled?(name)
482482
end
483483

484484
def extensions
485-
exec_query("SELECT extname FROM pg_extension", "SCHEMA", allow_retry: true, materialize_transactions: false).cast_values
485+
internal_exec_query("SELECT extname FROM pg_extension", "SCHEMA", allow_retry: true, materialize_transactions: false).cast_values
486486
end
487487

488488
# Returns a list of defined enum types, and their values.
@@ -500,7 +500,7 @@ def enum_types
500500
GROUP BY type.OID, n.nspname, type.typname;
501501
SQL
502502

503-
exec_query(query, "SCHEMA", allow_retry: true, materialize_transactions: false).cast_values.each_with_object({}) do |row, memo|
503+
internal_exec_query(query, "SCHEMA", allow_retry: true, materialize_transactions: false).cast_values.each_with_object({}) do |row, memo|
504504
name, schema = row[0], row[2]
505505
schema = nil if schema == current_schema
506506
full_name = [schema, name].compact.join(".")
@@ -527,7 +527,7 @@ def create_enum(name, values, **options)
527527
END
528528
$$;
529529
SQL
530-
exec_query(query)
530+
internal_exec_query(query)
531531
end
532532

533533
# Drops an enum type.
@@ -543,7 +543,7 @@ def drop_enum(name, values = nil, **options)
543543
query = <<~SQL
544544
DROP TYPE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(name)};
545545
SQL
546-
exec_query(query)
546+
internal_exec_query(query)
547547
end
548548

549549
# Returns the configured supported identifier length supported by PostgreSQL

activerecord/lib/active_record/connection_adapters/sqlite3/database_statements.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@ def write_query?(sql) # :nodoc:
1717

1818
def explain(arel, binds = [], _options = [])
1919
sql = "EXPLAIN QUERY PLAN " + to_sql(arel, binds)
20-
result = exec_query(sql, "EXPLAIN", [])
20+
result = internal_exec_query(sql, "EXPLAIN", [])
2121
SQLite3::ExplainPrettyPrinter.new.pp(result)
2222
end
2323

24-
def exec_query(sql, name = nil, binds = [], prepare: false, async: false) # :nodoc:
24+
def internal_exec_query(sql, name = nil, binds = [], prepare: false, async: false) # :nodoc:
2525
sql = transform_query(sql)
2626
check_if_write_query(sql)
2727

@@ -57,7 +57,7 @@ def exec_query(sql, name = nil, binds = [], prepare: false, async: false) # :nod
5757
end
5858

5959
def exec_delete(sql, name = "SQL", binds = []) # :nodoc:
60-
exec_query(sql, name, binds)
60+
internal_exec_query(sql, name, binds)
6161
@raw_connection.changes
6262
end
6363
alias :exec_update :exec_delete

0 commit comments

Comments
 (0)