mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Merge pull request #35795 from alimi/cache-database-version
Cache database version in schema cache
This commit is contained in:
commit
b1fc1319df
18 changed files with 106 additions and 67 deletions
|
@ -810,6 +810,7 @@ module ActiveRecord
|
|||
def new_connection
|
||||
Base.send(spec.adapter_method, spec.config).tap do |conn|
|
||||
conn.schema_cache = schema_cache.dup if schema_cache
|
||||
conn.check_version
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -133,8 +133,6 @@ module ActiveRecord
|
|||
@advisory_locks_enabled = self.class.type_cast_config_to_boolean(
|
||||
config.fetch(:advisory_locks, true)
|
||||
)
|
||||
|
||||
check_version
|
||||
end
|
||||
|
||||
def replica?
|
||||
|
@ -575,10 +573,18 @@ module ActiveRecord
|
|||
"INSERT #{insert.into} #{insert.values_list}"
|
||||
end
|
||||
|
||||
private
|
||||
def check_version
|
||||
def get_database_version # :nodoc:
|
||||
end
|
||||
|
||||
def database_version # :nodoc:
|
||||
schema_cache.database_version
|
||||
end
|
||||
|
||||
def check_version # :nodoc:
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def type_map
|
||||
@type_map ||= Type::TypeMap.new.tap do |mapping|
|
||||
initialize_type_map(mapping)
|
||||
|
|
|
@ -55,8 +55,8 @@ module ActiveRecord
|
|||
super(connection, logger, config)
|
||||
end
|
||||
|
||||
def version #:nodoc:
|
||||
@version ||= Version.new(version_string)
|
||||
def get_database_version #:nodoc:
|
||||
Version.new(version_string)
|
||||
end
|
||||
|
||||
def mariadb? # :nodoc:
|
||||
|
@ -68,11 +68,11 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
def supports_index_sort_order?
|
||||
!mariadb? && version >= "8.0.1"
|
||||
!mariadb? && database_version >= "8.0.1"
|
||||
end
|
||||
|
||||
def supports_expression_index?
|
||||
!mariadb? && version >= "8.0.13"
|
||||
!mariadb? && database_version >= "8.0.13"
|
||||
end
|
||||
|
||||
def supports_transaction_isolation?
|
||||
|
@ -96,16 +96,16 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
def supports_datetime_with_precision?
|
||||
mariadb? || version >= "5.6.4"
|
||||
mariadb? || database_version >= "5.6.4"
|
||||
end
|
||||
|
||||
def supports_virtual_columns?
|
||||
mariadb? || version >= "5.7.5"
|
||||
mariadb? || database_version >= "5.7.5"
|
||||
end
|
||||
|
||||
# See https://dev.mysql.com/doc/refman/8.0/en/optimizer-hints.html for more details.
|
||||
def supports_optimizer_hints?
|
||||
!mariadb? && version >= "5.7.7"
|
||||
!mariadb? && database_version >= "5.7.7"
|
||||
end
|
||||
|
||||
def supports_advisory_locks?
|
||||
|
@ -526,12 +526,13 @@ module ActiveRecord
|
|||
sql
|
||||
end
|
||||
|
||||
def check_version # :nodoc:
|
||||
if database_version < "5.5.8"
|
||||
raise "Your version of MySQL (#{database_version}) is too old. Active Record supports MySQL >= 5.5.8."
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def check_version
|
||||
if version < "5.5.8"
|
||||
raise "Your version of MySQL (#{version_string}) is too old. Active Record supports MySQL >= 5.5.8."
|
||||
end
|
||||
end
|
||||
|
||||
def initialize_type_map(m = type_map)
|
||||
super
|
||||
|
@ -702,7 +703,7 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
def supports_rename_index?
|
||||
mariadb? ? false : version >= "5.7.6"
|
||||
mariadb? ? false : database_version >= "5.7.6"
|
||||
end
|
||||
|
||||
def configure_connection
|
||||
|
|
|
@ -64,7 +64,7 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
def extract_expression_for_virtual_column(column)
|
||||
if @connection.mariadb? && @connection.version < "10.2.5"
|
||||
if @connection.mariadb? && @connection.database_version < "10.2.5"
|
||||
create_table_info = @connection.send(:create_table_info, column.table_name)
|
||||
column_name = @connection.quote_column_name(column.name)
|
||||
if %r/#{column_name} #{Regexp.quote(column.sql_type)}(?: COLLATE \w+)? AS \((?<expression>.+?)\) #{column.extra}/ =~ create_table_info
|
||||
|
|
|
@ -126,9 +126,9 @@ module ActiveRecord
|
|||
|
||||
def row_format_dynamic_by_default?
|
||||
if mariadb?
|
||||
version >= "10.2.2"
|
||||
database_version >= "10.2.2"
|
||||
else
|
||||
version >= "5.7.9"
|
||||
database_version >= "5.7.9"
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -43,7 +43,7 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
def supports_json?
|
||||
!mariadb? && version >= "5.7.8"
|
||||
!mariadb? && database_version >= "5.7.8"
|
||||
end
|
||||
|
||||
def supports_comments?
|
||||
|
|
|
@ -287,7 +287,7 @@ module ActiveRecord
|
|||
quoted_sequence = quote_table_name(sequence)
|
||||
max_pk = query_value("SELECT MAX(#{quote_column_name pk}) FROM #{quote_table_name(table)}", "SCHEMA")
|
||||
if max_pk.nil?
|
||||
if postgresql_version >= 100000
|
||||
if database_version >= 100000
|
||||
minvalue = query_value("SELECT seqmin FROM pg_sequence WHERE seqrelid = #{quote(quoted_sequence)}::regclass", "SCHEMA")
|
||||
else
|
||||
minvalue = query_value("SELECT min_value FROM #{quoted_sequence}", "SCHEMA")
|
||||
|
|
|
@ -201,7 +201,7 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
def supports_insert_on_conflict?
|
||||
postgresql_version >= 90500
|
||||
database_version >= 90500
|
||||
end
|
||||
alias supports_insert_on_duplicate_skip? supports_insert_on_conflict?
|
||||
alias supports_insert_on_duplicate_update? supports_insert_on_conflict?
|
||||
|
@ -344,7 +344,7 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
def supports_pgcrypto_uuid?
|
||||
postgresql_version >= 90400
|
||||
database_version >= 90400
|
||||
end
|
||||
|
||||
def supports_optimizer_hints?
|
||||
|
@ -424,7 +424,7 @@ module ActiveRecord
|
|||
}
|
||||
|
||||
# Returns the version of the connected PostgreSQL server.
|
||||
def postgresql_version
|
||||
def get_database_version
|
||||
@connection.server_version
|
||||
end
|
||||
|
||||
|
@ -446,12 +446,13 @@ module ActiveRecord
|
|||
sql
|
||||
end
|
||||
|
||||
def check_version # :nodoc:
|
||||
if database_version < 90300
|
||||
raise "Your version of PostgreSQL (#{database_version}) is too old. Active Record supports PostgreSQL >= 9.3."
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def check_version
|
||||
if postgresql_version < 90300
|
||||
raise "Your version of PostgreSQL (#{postgresql_version}) is too old. Active Record supports PostgreSQL >= 9.3."
|
||||
end
|
||||
end
|
||||
|
||||
# See https://www.postgresql.org/docs/current/static/errcodes-appendix.html
|
||||
VALUE_LIMIT_VIOLATION = "22001"
|
||||
|
|
|
@ -32,6 +32,7 @@ module ActiveRecord
|
|||
coder["data_sources"] = @data_sources
|
||||
coder["indexes"] = @indexes
|
||||
coder["version"] = connection.migration_context.current_version
|
||||
coder["database_version"] = database_version
|
||||
end
|
||||
|
||||
def init_with(coder)
|
||||
|
@ -41,6 +42,7 @@ module ActiveRecord
|
|||
@data_sources = coder["data_sources"]
|
||||
@indexes = coder["indexes"] || {}
|
||||
@version = coder["version"]
|
||||
@database_version = coder["database_version"]
|
||||
end
|
||||
|
||||
def primary_keys(table_name)
|
||||
|
@ -91,6 +93,10 @@ module ActiveRecord
|
|||
@indexes[table_name] ||= connection.indexes(table_name)
|
||||
end
|
||||
|
||||
def database_version # :nodoc:
|
||||
@database_version ||= connection.get_database_version
|
||||
end
|
||||
|
||||
# Clears out internal caches
|
||||
def clear!
|
||||
@columns.clear
|
||||
|
@ -99,6 +105,7 @@ module ActiveRecord
|
|||
@data_sources.clear
|
||||
@indexes.clear
|
||||
@version = nil
|
||||
@database_version = nil
|
||||
end
|
||||
|
||||
def size
|
||||
|
@ -117,11 +124,11 @@ module ActiveRecord
|
|||
def marshal_dump
|
||||
# if we get current version during initialization, it happens stack over flow.
|
||||
@version = connection.migration_context.current_version
|
||||
[@version, @columns, @columns_hash, @primary_keys, @data_sources, @indexes]
|
||||
[@version, @columns, @columns_hash, @primary_keys, @data_sources, @indexes, database_version]
|
||||
end
|
||||
|
||||
def marshal_load(array)
|
||||
@version, @columns, @columns_hash, @primary_keys, @data_sources, @indexes = array
|
||||
@version, @columns, @columns_hash, @primary_keys, @data_sources, @indexes, @database_version = array
|
||||
@indexes = @indexes || {}
|
||||
end
|
||||
|
||||
|
|
|
@ -111,7 +111,7 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
def supports_expression_index?
|
||||
sqlite_version >= "3.9.0"
|
||||
database_version >= "3.9.0"
|
||||
end
|
||||
|
||||
def requires_reloading?
|
||||
|
@ -135,7 +135,7 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
def supports_insert_on_conflict?
|
||||
sqlite_version >= "3.24.0"
|
||||
database_version >= "3.24.0"
|
||||
end
|
||||
alias supports_insert_on_duplicate_skip? supports_insert_on_conflict?
|
||||
alias supports_insert_on_duplicate_update? supports_insert_on_conflict?
|
||||
|
@ -397,6 +397,16 @@ module ActiveRecord
|
|||
sql
|
||||
end
|
||||
|
||||
def get_database_version # :nodoc:
|
||||
SQLite3Adapter::Version.new(query_value("SELECT sqlite_version(*)"))
|
||||
end
|
||||
|
||||
def check_version # :nodoc:
|
||||
if database_version < "3.8.0"
|
||||
raise "Your version of SQLite (#{database_version}) is too old. Active Record supports SQLite >= 3.8."
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
# See https://www.sqlite.org/limits.html,
|
||||
# the default value is 999 when not configured.
|
||||
|
@ -404,12 +414,6 @@ module ActiveRecord
|
|||
999
|
||||
end
|
||||
|
||||
def check_version
|
||||
if sqlite_version < "3.8.0"
|
||||
raise "Your version of SQLite (#{sqlite_version}) is too old. Active Record supports SQLite >= 3.8."
|
||||
end
|
||||
end
|
||||
|
||||
def initialize_type_map(m = type_map)
|
||||
super
|
||||
register_class_with_limit m, %r(int)i, SQLite3Integer
|
||||
|
@ -527,10 +531,6 @@ module ActiveRecord
|
|||
SELECT #{quoted_from_columns} FROM #{quote_table_name(from)}")
|
||||
end
|
||||
|
||||
def sqlite_version
|
||||
@sqlite_version ||= SQLite3Adapter::Version.new(query_value("SELECT sqlite_version(*)"))
|
||||
end
|
||||
|
||||
def translate_exception(exception, message:, sql:, binds:)
|
||||
case exception.message
|
||||
# SQLite 3.8.2 returns a newly formatted error message:
|
||||
|
|
|
@ -46,10 +46,7 @@ class Mysql2DatetimePrecisionQuotingTest < ActiveRecord::Mysql2TestCase
|
|||
|
||||
def stub_version(full_version_string)
|
||||
@connection.stub(:full_version, full_version_string) do
|
||||
@connection.remove_instance_variable(:@version) if @connection.instance_variable_defined?(:@version)
|
||||
yield
|
||||
end
|
||||
ensure
|
||||
@connection.remove_instance_variable(:@version) if @connection.instance_variable_defined?(:@version)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -9,7 +9,7 @@ class Mysql2StoredProcedureTest < ActiveRecord::Mysql2TestCase
|
|||
|
||||
def setup
|
||||
@connection = ActiveRecord::Base.connection
|
||||
unless ActiveRecord::Base.connection.version >= "5.6.0"
|
||||
unless ActiveRecord::Base.connection.database_version >= "5.6.0"
|
||||
skip("no stored procedure support")
|
||||
end
|
||||
end
|
||||
|
|
|
@ -247,7 +247,7 @@ class PostgreSQLGeometricLineTest < ActiveRecord::PostgreSQLTestCase
|
|||
class PostgresqlLine < ActiveRecord::Base; end
|
||||
|
||||
setup do
|
||||
unless ActiveRecord::Base.connection.send(:postgresql_version) >= 90400
|
||||
unless ActiveRecord::Base.connection.database_version >= 90400
|
||||
skip("line type is not fully implemented")
|
||||
end
|
||||
@connection = ActiveRecord::Base.connection
|
||||
|
|
|
@ -12,7 +12,7 @@ class PostgreSQLPartitionsTest < ActiveRecord::PostgreSQLTestCase
|
|||
end
|
||||
|
||||
def test_partitions_table_exists
|
||||
skip unless ActiveRecord::Base.connection.postgresql_version >= 100000
|
||||
skip unless ActiveRecord::Base.connection.database_version >= 100000
|
||||
@connection.create_table :partitioned_events, force: true, id: false,
|
||||
options: "partition by range (issued_at)" do |t|
|
||||
t.timestamp :issued_at
|
||||
|
|
|
@ -8,6 +8,7 @@ module ActiveRecord
|
|||
def setup
|
||||
@connection = ActiveRecord::Base.connection
|
||||
@cache = SchemaCache.new @connection
|
||||
@database_version = @connection.get_database_version
|
||||
end
|
||||
|
||||
def test_primary_key
|
||||
|
@ -28,6 +29,7 @@ module ActiveRecord
|
|||
assert new_cache.data_sources("posts")
|
||||
assert_equal "id", new_cache.primary_keys("posts")
|
||||
assert_equal 1, new_cache.indexes("posts").size
|
||||
assert_equal @database_version.to_s, new_cache.database_version.to_s
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -55,6 +57,20 @@ module ActiveRecord
|
|||
@connection.schema_cache = old_cache
|
||||
end
|
||||
|
||||
def test_yaml_loads_5_1_dump_without_database_version_still_queries_for_database_version
|
||||
@cache = YAML.load(File.read(schema_dump_path))
|
||||
|
||||
# Simulate assignment in railtie after loading the cache.
|
||||
old_cache, @connection.schema_cache = @connection.schema_cache, @cache
|
||||
|
||||
# We can't verify queries get executed because the database version gets
|
||||
# cached in both MySQL and PostgreSQL outside of the schema cache.
|
||||
assert_nil @cache.instance_variable_get(:@database_version)
|
||||
assert_equal @database_version.to_s, @cache.database_version.to_s
|
||||
ensure
|
||||
@connection.schema_cache = old_cache
|
||||
end
|
||||
|
||||
def test_primary_key_for_non_existent_table
|
||||
assert_nil @cache.primary_keys("omgponies")
|
||||
end
|
||||
|
@ -74,6 +90,14 @@ module ActiveRecord
|
|||
assert_equal indexes, @cache.indexes("posts")
|
||||
end
|
||||
|
||||
def test_caches_database_version
|
||||
@cache.database_version # cache database_version
|
||||
|
||||
assert_no_queries do
|
||||
assert_equal @database_version.to_s, @cache.database_version.to_s
|
||||
end
|
||||
end
|
||||
|
||||
def test_clearing
|
||||
@cache.columns("posts")
|
||||
@cache.columns_hash("posts")
|
||||
|
@ -84,6 +108,7 @@ module ActiveRecord
|
|||
@cache.clear!
|
||||
|
||||
assert_equal 0, @cache.size
|
||||
assert_nil @cache.instance_variable_get(:@database_version)
|
||||
end
|
||||
|
||||
def test_dump_and_load
|
||||
|
@ -101,6 +126,7 @@ module ActiveRecord
|
|||
assert @cache.data_sources("posts")
|
||||
assert_equal "id", @cache.primary_keys("posts")
|
||||
assert_equal 1, @cache.indexes("posts").size
|
||||
assert_equal @database_version.to_s, @cache.database_version.to_s
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -89,7 +89,7 @@ if current_adapter?(:PostgreSQLAdapter)
|
|||
|
||||
test "schema dump includes default expression" do
|
||||
output = dump_table_schema("defaults")
|
||||
if ActiveRecord::Base.connection.postgresql_version >= 100000
|
||||
if ActiveRecord::Base.connection.database_version >= 100000
|
||||
assert_match %r/t\.date\s+"modified_date",\s+default: -> { "CURRENT_DATE" }/, output
|
||||
assert_match %r/t\.datetime\s+"modified_time",\s+default: -> { "CURRENT_TIMESTAMP" }/, output
|
||||
else
|
||||
|
|
|
@ -53,7 +53,7 @@ def supports_default_expression?
|
|||
true
|
||||
elsif current_adapter?(:Mysql2Adapter)
|
||||
conn = ActiveRecord::Base.connection
|
||||
!conn.mariadb? && conn.version >= "8.0.13"
|
||||
!conn.mariadb? && conn.database_version >= "8.0.13"
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -136,7 +136,7 @@ module ActiveRecord
|
|||
def test_remove_column_with_multi_column_index
|
||||
# MariaDB starting with 10.2.8
|
||||
# Dropping a column that is part of a multi-column UNIQUE constraint is not permitted.
|
||||
skip if current_adapter?(:Mysql2Adapter) && connection.mariadb? && connection.version >= "10.2.8"
|
||||
skip if current_adapter?(:Mysql2Adapter) && connection.mariadb? && connection.database_version >= "10.2.8"
|
||||
|
||||
add_column "test_models", :hat_size, :integer
|
||||
add_column "test_models", :hat_style, :string, limit: 100
|
||||
|
|
Loading…
Reference in a new issue