Skip to content

Commit

Permalink
Don't cache queries for schema statements
Browse files Browse the repository at this point in the history
`test_middleware_caches` is sometimes failed since rails#29454.
The failure is due to schema statements are affected by query caching.
Bypassing query caching for schema statements to avoid the issue.
  • Loading branch information
kamipo committed Jul 20, 2017
1 parent b6e24db commit 188c4bb
Show file tree
Hide file tree
Showing 9 changed files with 71 additions and 58 deletions.
Expand Up @@ -51,9 +51,7 @@ def select_one(arel, name = nil, binds = [])

# Returns a single value from a record
def select_value(arel, name = nil, binds = [])
if result = select_rows(arel, name, binds).first
result.first
end
single_value_from_rows(select_rows(arel, name, binds))
end

# Returns an array of the values of the first column in a select:
Expand All @@ -68,6 +66,18 @@ def select_rows(arel, name = nil, binds = [])
select_all(arel, name, binds).rows
end

def query_value(sql, name = nil) # :nodoc:
single_value_from_rows(query(sql, name))
end

def query_values(sql, name = nil) # :nodoc:
query(sql, name).map(&:first)
end

def query(sql, name = nil) # :nodoc:
exec_query(sql, name).rows
end

# Executes the SQL statement in the context of this connection and returns
# the raw result from the connection adapter.
# Note: depending on your database connector, the result returned by this
Expand Down Expand Up @@ -370,7 +380,11 @@ def sql_for_insert(sql, pk, id_value, sequence_name, binds)
end

def last_inserted_id(result)
row = result.rows.first
single_value_from_rows(result.rows)
end

def single_value_from_rows(rows)
row = rows.first
row && row.first
end

Expand Down
Expand Up @@ -31,7 +31,7 @@ def table_alias_for(table_name)
# Returns the relation names useable to back Active Record models.
# For most adapters this means all #tables and #views.
def data_sources
select_values(data_source_sql, "SCHEMA")
query_values(data_source_sql, "SCHEMA")
rescue NotImplementedError
tables | views
end
Expand All @@ -41,37 +41,37 @@ def data_sources
# data_source_exists?(:ebooks)
#
def data_source_exists?(name)
select_values(data_source_sql(name), "SCHEMA").any? if name.present?
query_values(data_source_sql(name), "SCHEMA").any? if name.present?
rescue NotImplementedError
data_sources.include?(name.to_s)
end

# Returns an array of table names defined in the database.
def tables
select_values(data_source_sql(type: "BASE TABLE"), "SCHEMA")
query_values(data_source_sql(type: "BASE TABLE"), "SCHEMA")
end

# Checks to see if the table +table_name+ exists on the database.
#
# table_exists?(:developers)
#
def table_exists?(table_name)
select_values(data_source_sql(table_name, type: "BASE TABLE"), "SCHEMA").any? if table_name.present?
query_values(data_source_sql(table_name, type: "BASE TABLE"), "SCHEMA").any? if table_name.present?
rescue NotImplementedError
tables.include?(table_name.to_s)
end

# Returns an array of view names defined in the database.
def views
select_values(data_source_sql(type: "VIEW"), "SCHEMA")
query_values(data_source_sql(type: "VIEW"), "SCHEMA")
end

# Checks to see if the view +view_name+ exists on the database.
#
# view_exists?(:ebooks)
#
def view_exists?(view_name)
select_values(data_source_sql(view_name, type: "VIEW"), "SCHEMA").any? if view_name.present?
query_values(data_source_sql(view_name, type: "VIEW"), "SCHEMA").any? if view_name.present?
rescue NotImplementedError
views.include?(view_name.to_s)
end
Expand Down
Expand Up @@ -140,11 +140,11 @@ def supports_advisory_locks?
end

def get_advisory_lock(lock_name, timeout = 0) # :nodoc:
select_value("SELECT GET_LOCK(#{quote(lock_name)}, #{timeout})") == 1
query_value("SELECT GET_LOCK(#{quote(lock_name)}, #{timeout})") == 1
end

def release_advisory_lock(lock_name) # :nodoc:
select_value("SELECT RELEASE_LOCK(#{quote(lock_name)})") == 1
query_value("SELECT RELEASE_LOCK(#{quote(lock_name)})") == 1
end

def native_database_types
Expand Down Expand Up @@ -176,7 +176,7 @@ def error_number(exception) # :nodoc:
# REFERENTIAL INTEGRITY ====================================

def disable_referential_integrity #:nodoc:
old = select_value("SELECT @@FOREIGN_KEY_CHECKS")
old = query_value("SELECT @@FOREIGN_KEY_CHECKS")

begin
update("SET FOREIGN_KEY_CHECKS = 0")
Expand Down Expand Up @@ -291,7 +291,7 @@ def drop_database(name) #:nodoc:
end

def current_database
select_value "SELECT DATABASE() as db"
query_value("SELECT database()", "SCHEMA")
end

# Returns the database character set.
Expand Down Expand Up @@ -351,7 +351,7 @@ def new_column_from_field(table_name, field) # :nodoc:
def table_comment(table_name) # :nodoc:
scope = quoted_scope(table_name)

select_value(<<-SQL.strip_heredoc, "SCHEMA")
query_value(<<-SQL.strip_heredoc, "SCHEMA")
SELECT table_comment
FROM information_schema.tables
WHERE table_schema = #{scope[:schema]}
Expand Down Expand Up @@ -457,7 +457,7 @@ def foreign_keys(table_name)

scope = quoted_scope(table_name)

fk_info = select_all(<<-SQL.strip_heredoc, "SCHEMA")
fk_info = exec_query(<<-SQL.strip_heredoc, "SCHEMA")
SELECT fk.referenced_table_name AS 'to_table',
fk.referenced_column_name AS 'primary_key',
fk.column_name AS 'column',
Expand Down Expand Up @@ -534,7 +534,7 @@ def type_to_sql(type, limit: nil, precision: nil, scale: nil, unsigned: nil, **)

# SHOW VARIABLES LIKE 'name'
def show_variable(name)
select_value("SELECT @@#{name}", "SCHEMA")
query_value("SELECT @@#{name}", "SCHEMA")
rescue ActiveRecord::StatementInvalid
nil
end
Expand All @@ -544,7 +544,7 @@ def primary_keys(table_name) # :nodoc:

scope = quoted_scope(table_name)

select_values(<<-SQL.strip_heredoc, "SCHEMA")
query_values(<<-SQL.strip_heredoc, "SCHEMA")
SELECT column_name
FROM information_schema.key_column_usage
WHERE constraint_name = 'PRIMARY'
Expand Down Expand Up @@ -745,7 +745,7 @@ def rename_column_sql(table_name, column_name, new_column_name)
auto_increment: column.auto_increment?
}

current_type = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'", "SCHEMA")["Type"]
current_type = exec_query("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE #{quote(column_name)}", "SCHEMA").first["Type"]
td = create_table_definition(table_name)
cd = td.new_column_definition(new_column_name, current_type, options)
schema_creation.accept(ChangeColumnDefinition.new(cd, column.name))
Expand Down Expand Up @@ -864,7 +864,7 @@ def extract_foreign_key_action(specifier) # :nodoc:
end

def create_table_info(table_name) # :nodoc:
select_one("SHOW CREATE TABLE #{quote_table_name(table_name)}")["Create Table"]
exec_query("SHOW CREATE TABLE #{quote_table_name(table_name)}", "SCHEMA").first["Create Table"]
end

def create_table_definition(*args) # :nodoc:
Expand Down
Expand Up @@ -13,6 +13,10 @@ def select_all(arel, name = nil, binds = [], preparable: nil) # :nodoc:
result
end

def query(sql, name = nil) # :nodoc:
execute(sql, name).to_a
end

# Executes the SQL statement in the context of this connection.
def execute(sql, name = nil)
# make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
Expand Down
Expand Up @@ -47,7 +47,7 @@ def schema_precision(column)
def schema_collation(column)
if column.collation && table_name = column.table_name
@table_collation_cache ||= {}
@table_collation_cache[table_name] ||= select_one("SHOW TABLE STATUS LIKE '#{table_name}'")["Collation"]
@table_collation_cache[table_name] ||= exec_query("SHOW TABLE STATUS LIKE #{quote(table_name)}", "SCHEMA").first["Collation"]
column.collation.inspect if column.collation != @table_collation_cache[table_name]
end
end
Expand All @@ -64,7 +64,7 @@ def extract_expression_for_virtual_column(column)
" WHERE table_schema = #{scope[:schema]}" \
" AND table_name = #{scope[:name]}" \
" AND column_name = #{quote(column.name)}"
select_value(sql, "SCHEMA").inspect
query_value(sql, "SCHEMA").inspect
end
end
end
Expand Down
Expand Up @@ -77,6 +77,9 @@ def lookup_cast_type_from_column(column) # :nodoc:
end

private
def lookup_cast_type(sql_type)
super(query_value("SELECT #{quote(sql_type)}::regtype::oid", "SCHEMA").to_i)
end

def _quote(value)
case value
Expand Down
Expand Up @@ -60,7 +60,7 @@ def drop_table(table_name, options = {}) # :nodoc:

# Returns true if schema exists.
def schema_exists?(name)
select_value("SELECT COUNT(*) FROM pg_namespace WHERE nspname = #{quote(name)}", "SCHEMA").to_i > 0
query_value("SELECT COUNT(*) FROM pg_namespace WHERE nspname = #{quote(name)}", "SCHEMA").to_i > 0
end

# Verifies existence of an index with a given name.
Expand All @@ -73,7 +73,7 @@ def index_name_exists?(table_name, index_name, default = nil)
table = quoted_scope(table_name)
index = quoted_scope(index_name)

select_value(<<-SQL, "SCHEMA").to_i > 0
query_value(<<-SQL, "SCHEMA").to_i > 0
SELECT COUNT(*)
FROM pg_class t
INNER JOIN pg_index d ON t.oid = d.indrelid
Expand Down Expand Up @@ -173,7 +173,7 @@ def table_options(table_name) # :nodoc:
def table_comment(table_name) # :nodoc:
scope = quoted_scope(table_name, type: "BASE TABLE")
if scope[:name]
select_value(<<-SQL.strip_heredoc, "SCHEMA")
query_value(<<-SQL.strip_heredoc, "SCHEMA")
SELECT pg_catalog.obj_description(c.oid, 'pg_class')
FROM pg_catalog.pg_class c
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
Expand All @@ -186,32 +186,32 @@ def table_comment(table_name) # :nodoc:

# Returns the current database name.
def current_database
select_value("SELECT current_database()", "SCHEMA")
query_value("SELECT current_database()", "SCHEMA")
end

# Returns the current schema name.
def current_schema
select_value("SELECT current_schema", "SCHEMA")
query_value("SELECT current_schema", "SCHEMA")
end

# Returns the current database encoding format.
def encoding
select_value("SELECT pg_encoding_to_char(encoding) FROM pg_database WHERE datname LIKE '#{current_database}'", "SCHEMA")
query_value("SELECT pg_encoding_to_char(encoding) FROM pg_database WHERE datname = current_database()", "SCHEMA")
end

# Returns the current database collation.
def collation
select_value("SELECT datcollate FROM pg_database WHERE datname LIKE '#{current_database}'", "SCHEMA")
query_value("SELECT datcollate FROM pg_database WHERE datname = current_database()", "SCHEMA")
end

# Returns the current database ctype.
def ctype
select_value("SELECT datctype FROM pg_database WHERE datname LIKE '#{current_database}'", "SCHEMA")
query_value("SELECT datctype FROM pg_database WHERE datname = current_database()", "SCHEMA")
end

# Returns an array of schema names.
def schema_names
select_values(<<-SQL, "SCHEMA")
query_values(<<-SQL, "SCHEMA")
SELECT nspname
FROM pg_namespace
WHERE nspname !~ '^pg_.*'
Expand Down Expand Up @@ -244,12 +244,12 @@ def schema_search_path=(schema_csv)

# Returns the active schema search path.
def schema_search_path
@schema_search_path ||= select_value("SHOW search_path", "SCHEMA")
@schema_search_path ||= query_value("SHOW search_path", "SCHEMA")
end

# Returns the current client message level.
def client_min_messages
select_value("SHOW client_min_messages", "SCHEMA")
query_value("SHOW client_min_messages", "SCHEMA")
end

# Set the client message level.
Expand All @@ -267,7 +267,7 @@ def default_sequence_name(table_name, pk = "id") #:nodoc:
end

def serial_sequence(table, column)
select_value("SELECT pg_get_serial_sequence('#{table}', '#{column}')", "SCHEMA")
query_value("SELECT pg_get_serial_sequence(#{quote(table)}, #{quote(column)})", "SCHEMA")
end

# Sets the sequence of a table's primary key to the specified value.
Expand All @@ -278,7 +278,7 @@ def set_pk_sequence!(table, value) #:nodoc:
if sequence
quoted_sequence = quote_table_name(sequence)

select_value("SELECT setval('#{quoted_sequence}', #{value})", "SCHEMA")
query_value("SELECT setval(#{quote(quoted_sequence)}, #{value})", "SCHEMA")
else
@logger.warn "#{table} has primary key #{pk} with no default sequence." if @logger
end
Expand All @@ -300,18 +300,16 @@ def reset_pk_sequence!(table, pk = nil, sequence = nil) #:nodoc:

if pk && sequence
quoted_sequence = quote_table_name(sequence)
max_pk = select_value("select MAX(#{quote_column_name pk}) from #{quote_table_name(table)}")
max_pk = query_value("SELECT MAX(#{quote_column_name pk}) FROM #{quote_table_name(table)}", "SCHEMA")
if max_pk.nil?
if postgresql_version >= 100000
minvalue = select_value("SELECT seqmin from pg_sequence where seqrelid = '#{quoted_sequence}'::regclass")
minvalue = query_value("SELECT seqmin FROM pg_sequence WHERE seqrelid = #{quote(quoted_sequence)}::regclass", "SCHEMA")
else
minvalue = select_value("SELECT min_value FROM #{quoted_sequence}")
minvalue = query_value("SELECT min_value FROM #{quoted_sequence}", "SCHEMA")
end
end

select_value(<<-end_sql, "SCHEMA")
SELECT setval('#{quoted_sequence}', #{max_pk ? max_pk : minvalue}, #{max_pk ? true : false})
end_sql
query_value("SELECT setval(#{quote(quoted_sequence)}, #{max_pk ? max_pk : minvalue}, #{max_pk ? true : false})", "SCHEMA")
end
end

Expand Down Expand Up @@ -370,7 +368,7 @@ def pk_and_sequence_for(table) #:nodoc:
end

def primary_keys(table_name) # :nodoc:
select_values(<<-SQL.strip_heredoc, "SCHEMA")
query_values(<<-SQL.strip_heredoc, "SCHEMA")
SELECT a.attname
FROM (
SELECT indrelid, indkey, generate_subscripts(indkey, 1) idx
Expand Down Expand Up @@ -520,7 +518,7 @@ def rename_index(table_name, old_name, new_name)

def foreign_keys(table_name)
scope = quoted_scope(table_name)
fk_info = select_all(<<-SQL.strip_heredoc, "SCHEMA")
fk_info = exec_query(<<-SQL.strip_heredoc, "SCHEMA")
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
FROM pg_constraint c
JOIN pg_class t1 ON c.conrelid = t1.oid
Expand Down

0 comments on commit 188c4bb

Please sign in to comment.