提交 815dd11b 编写于 作者: R Ryuta Kamizono

Refactor `SchemaDumper` to make it possible to adapter specific customization

Currently `SchemaDumper` is only customizable for column options. But
3rd party connection adapters (oracle-enhanced etc) need to customizable
for table or index dumping also. To make it possible, I introduced
adapter specific `SchemaDumper` classes for that.
上级 f2099361
......@@ -4,25 +4,24 @@
module ActiveRecord
module ConnectionAdapters # :nodoc:
# The goal of this module is to move Adapter specific column
# definitions to the Adapter instead of having it in the schema
# dumper itself. This code represents the normal case.
# We can then redefine how certain data types may be handled in the schema dumper on the
# Adapter level by over-writing this code inside the database specific adapters
module ColumnDumper
def column_spec(column)
[schema_type_with_virtual(column), prepare_column_options(column)]
end
def column_spec_for_primary_key(column)
return {} if default_primary_key?(column)
spec = { id: schema_type(column).inspect }
spec.merge!(prepare_column_options(column).except!(:null))
spec[:default] ||= "nil" if explicit_primary_key_default?(column)
spec
class SchemaDumper < SchemaDumper # :nodoc:
def self.create(connection, options)
new(connection, options)
end
private
def column_spec(column)
[schema_type_with_virtual(column), prepare_column_options(column)]
end
def column_spec_for_primary_key(column)
return {} if default_primary_key?(column)
spec = { id: schema_type(column).inspect }
spec.merge!(prepare_column_options(column).except!(:null))
spec[:default] ||= "nil" if explicit_primary_key_default?(column)
spec
end
def prepare_column_options(column)
spec = {}
spec[:limit] = schema_limit(column)
......@@ -45,7 +44,7 @@ def explicit_primary_key_default?(column)
end
def schema_type_with_virtual(column)
if supports_virtual_columns? && column.virtual?
if @connection.supports_virtual_columns? && column.virtual?
:virtual
else
schema_type(column)
......@@ -62,7 +61,7 @@ def schema_type(column)
def schema_limit(column)
limit = column.limit unless column.bigint?
limit.inspect if limit && limit != native_database_types[column.type][:limit]
limit.inspect if limit && limit != @connection.native_database_types[column.type][:limit]
end
def schema_precision(column)
......@@ -75,7 +74,7 @@ def schema_scale(column)
def schema_default(column)
return unless column.has_default?
type = lookup_cast_type_from_column(column)
type = @connection.lookup_cast_type_from_column(column)
default = type.deserialize(column.default)
if default.nil?
schema_expression(column)
......
......@@ -1174,6 +1174,10 @@ def change_column_comment(table_name, column_name, comment) #:nodoc:
raise NotImplementedError, "#{self.class} does not support changing column comments"
end
def create_schema_dumper(options) # :nodoc:
SchemaDumper.create(self, options)
end
private
def column_options_keys
[:limit, :precision, :scale, :default, :null, :collation, :comment]
......
......@@ -72,7 +72,6 @@ class AbstractAdapter
include Quoting, DatabaseStatements, SchemaStatements
include DatabaseLimits
include QueryCache
include ColumnDumper
include Savepoints
SIMPLE_INT = /\A\d+\z/
......
......@@ -17,7 +17,6 @@ module ActiveRecord
module ConnectionAdapters
class AbstractMysqlAdapter < AbstractAdapter
include MySQL::Quoting
include MySQL::ColumnDumper
include MySQL::SchemaStatements
##
......
......@@ -3,13 +3,13 @@
module ActiveRecord
module ConnectionAdapters
module MySQL
module ColumnDumper # :nodoc:
class SchemaDumper < ConnectionAdapters::SchemaDumper # :nodoc:
private
def prepare_column_options(column)
spec = super
spec[:unsigned] = "true" if column.unsigned?
if supports_virtual_columns? && column.virtual?
if @connection.supports_virtual_columns? && column.virtual?
spec[:as] = extract_expression_for_virtual_column(column)
spec[:stored] = "true" if /\b(?:STORED|PERSISTENT)\b/.match?(column.extra)
spec = { type: schema_type(column).inspect }.merge!(spec)
......@@ -44,24 +44,27 @@ def schema_precision(column)
def schema_collation(column)
if column.collation && table_name = column.table_name
@table_collation_cache ||= {}
@table_collation_cache[table_name] ||= exec_query("SHOW TABLE STATUS LIKE #{quote(table_name)}", "SCHEMA").first["Collation"]
@table_collation_cache[table_name] ||=
@connection.exec_query("SHOW TABLE STATUS LIKE #{@connection.quote(table_name)}", "SCHEMA").first["Collation"]
column.collation.inspect if column.collation != @table_collation_cache[table_name]
end
end
def extract_expression_for_virtual_column(column)
if mariadb? && version < "10.2.5"
create_table_info = create_table_info(column.table_name)
if %r/#{quote_column_name(column.name)} #{Regexp.quote(column.sql_type)}(?: COLLATE \w+)? AS \((?<expression>.+?)\) #{column.extra}/ =~ create_table_info
if @connection.mariadb? && @connection.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
$~[:expression].inspect
end
else
scope = quoted_scope(column.table_name)
scope = @connection.send(:quoted_scope, column.table_name)
column_name = @connection.quote(column.name)
sql = "SELECT generation_expression FROM information_schema.columns" \
" WHERE table_schema = #{scope[:schema]}" \
" AND table_name = #{scope[:name]}" \
" AND column_name = #{quote(column.name)}"
query_value(sql, "SCHEMA").inspect
" AND column_name = #{column_name}"
@connection.query_value(sql, "SCHEMA").inspect
end
end
end
......
......@@ -66,6 +66,10 @@ def update_table_definition(table_name, base)
MySQL::Table.new(table_name, base)
end
def create_schema_dumper(options)
MySQL::SchemaDumper.create(self, options)
end
private
CHARSETS_OF_4BYTES_MAXLEN = ["utf8mb4", "utf16", "utf16le", "utf32"]
......
......@@ -3,7 +3,7 @@
module ActiveRecord
module ConnectionAdapters
module PostgreSQL
module ColumnDumper # :nodoc:
class SchemaDumper < ConnectionAdapters::SchemaDumper # :nodoc:
private
def prepare_column_options(column)
spec = super
......
......@@ -590,6 +590,10 @@ def update_table_definition(table_name, base) # :nodoc:
PostgreSQL::Table.new(table_name, base)
end
def create_schema_dumper(options) # :nodoc:
PostgreSQL::SchemaDumper.create(self, options)
end
private
def schema_creation
PostgreSQL::SchemaCreation.new(self)
......
......@@ -121,7 +121,6 @@ class PostgreSQLAdapter < AbstractAdapter
include PostgreSQL::ReferentialIntegrity
include PostgreSQL::SchemaStatements
include PostgreSQL::DatabaseStatements
include PostgreSQL::ColumnDumper
def supports_index_sort_order?
true
......
......@@ -3,9 +3,8 @@
module ActiveRecord
module ConnectionAdapters
module SQLite3
module ColumnDumper # :nodoc:
class SchemaDumper < ConnectionAdapters::SchemaDumper # :nodoc:
private
def default_primary_key?(column)
schema_type(column) == :integer
end
......
......@@ -43,6 +43,10 @@ def update_table_definition(table_name, base)
SQLite3::Table.new(table_name, base)
end
def create_schema_dumper(options)
SQLite3::SchemaDumper.create(self, options)
end
private
def schema_creation
SQLite3::SchemaCreation.new(self)
......
......@@ -57,7 +57,6 @@ class SQLite3Adapter < AbstractAdapter
ADAPTER_NAME = "SQLite".freeze
include SQLite3::Quoting
include SQLite3::ColumnDumper
include SQLite3::SchemaStatements
NATIVE_DATABASE_TYPES = {
......
......@@ -19,7 +19,7 @@ class SchemaDumper #:nodoc:
class << self
def dump(connection = ActiveRecord::Base.connection, stream = STDOUT, config = ActiveRecord::Base)
new(connection, generate_options(config)).dump(stream)
connection.create_schema_dumper(generate_options(config)).dump(stream)
stream
end
......@@ -123,7 +123,7 @@ def table(table, stream)
when String
tbl.print ", primary_key: #{pk.inspect}" unless pk == "id"
pkcol = columns.detect { |c| c.name == pk }
pkcolspec = @connection.column_spec_for_primary_key(pkcol)
pkcolspec = column_spec_for_primary_key(pkcol)
if pkcolspec.present?
tbl.print ", #{format_colspec(pkcolspec)}"
end
......@@ -145,7 +145,7 @@ def table(table, stream)
columns.each do |column|
raise StandardError, "Unknown type '#{column.sql_type}' for column '#{column.name}'" unless @connection.valid_type?(column.type)
next if column.name == pk
type, colspec = @connection.column_spec(column)
type, colspec = column_spec(column)
tbl.print " t.#{type} #{column.name.inspect}"
tbl.print ", #{format_colspec(colspec)}" if colspec.present?
tbl.puts
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册