diff --git a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb index cce8883076042190f3a2e6d8098c37b932b1dd13..f0c0fbab6c9c5f9d9962727f79a96adba135f7ed 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb @@ -61,17 +61,6 @@ def lookup_cast_type_from_column(column) # :nodoc: lookup_cast_type(column.sql_type) end - def fetch_type_metadata(sql_type) - cast_type = lookup_cast_type(sql_type) - SqlTypeMetadata.new( - sql_type: sql_type, - type: cast_type.type, - limit: cast_type.limit, - precision: cast_type.precision, - scale: cast_type.scale, - ) - end - # Quotes a string, escaping any ' (single quote) and \ (backslash) # characters. def quote_string(s) @@ -161,6 +150,10 @@ def type_casted_binds(binds) # :nodoc: end private + def lookup_cast_type(sql_type) + type_map.lookup(sql_type) + end + def id_value_for_database(value) if primary_key = value.class.primary_key value.instance_variable_get(:@attributes)[primary_key].value_for_database diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb index c48a4acff815c6b1d46c43a5474665f3f2f7db8e..a4fecc4a8e348bac39b41b6b0ff6881d2386e3e7 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb @@ -133,5 +133,6 @@ def action_sql(action, dependency) end end end + SchemaCreation = AbstractAdapter::SchemaCreation # :nodoc: end end diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb index 1cc10e3b9382e57f07518854dc2b2c48dec95be9..13629dee7f63a3a53f4cd4cf60071e44953fba11 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -976,16 +976,6 @@ def foreign_key_exists?(from_table, options_or_to_table = {}) foreign_key_for(from_table, options_or_to_table).present? end - def foreign_key_for(from_table, options_or_to_table = {}) # :nodoc: - return unless supports_foreign_keys? - foreign_keys(from_table).detect { |fk| fk.defined_for? options_or_to_table } - end - - def foreign_key_for!(from_table, options_or_to_table = {}) # :nodoc: - foreign_key_for(from_table, options_or_to_table) || \ - raise(ArgumentError, "Table '#{from_table}' has no foreign key for #{options_or_to_table}") - end - def foreign_key_column_for(table_name) # :nodoc: prefix = Base.table_name_prefix suffix = Base.table_name_suffix @@ -1005,19 +995,6 @@ def dump_schema_information #:nodoc: insert_versions_sql(versions) end - def insert_versions_sql(versions) # :nodoc: - sm_table = quote_table_name(ActiveRecord::SchemaMigration.table_name) - - if versions.is_a?(Array) - sql = "INSERT INTO #{sm_table} (version) VALUES\n" - sql << versions.map { |v| "(#{quote(v)})" }.join(",\n") - sql << ";\n\n" - sql - else - "INSERT INTO #{sm_table} (version) VALUES (#{quote(versions)});" - end - end - def initialize_schema_migrations_table # :nodoc: ActiveRecord::SchemaMigration.create_table end @@ -1263,6 +1240,10 @@ def rename_column_indexes(table_name, column_name, new_column_name) end end + def schema_creation + SchemaCreation.new(self) + end + def create_table_definition(*args) TableDefinition.new(*args) end @@ -1271,6 +1252,17 @@ def create_alter_table(name) AlterTable.new create_table_definition(name) end + def fetch_type_metadata(sql_type) + cast_type = lookup_cast_type(sql_type) + SqlTypeMetadata.new( + sql_type: sql_type, + type: cast_type.type, + limit: cast_type.limit, + precision: cast_type.precision, + scale: cast_type.scale, + ) + end + def index_column_names(column_names) if column_names.is_a?(String) && /\W/.match?(column_names) column_names @@ -1295,6 +1287,24 @@ def foreign_key_name(table_name, options) end end + def foreign_key_for(from_table, options_or_to_table = {}) + return unless supports_foreign_keys? + foreign_keys(from_table).detect { |fk| fk.defined_for? options_or_to_table } + end + + def foreign_key_for!(from_table, options_or_to_table = {}) + foreign_key_for(from_table, options_or_to_table) || \ + raise(ArgumentError, "Table '#{from_table}' has no foreign key for #{options_or_to_table}") + end + + def extract_foreign_key_action(specifier) + case specifier + when "CASCADE"; :cascade + when "SET NULL"; :nullify + when "RESTRICT"; :restrict + end + end + def validate_index_length!(table_name, new_name, internal = false) max_index_length = internal ? index_name_length : allowed_index_name_length @@ -1315,6 +1325,19 @@ def can_remove_index_by_name?(options) options.is_a?(Hash) && options.key?(:name) && options.except(:name, :algorithm).empty? end + def insert_versions_sql(versions) + sm_table = quote_table_name(ActiveRecord::SchemaMigration.table_name) + + if versions.is_a?(Array) + sql = "INSERT INTO #{sm_table} (version) VALUES\n" + sql << versions.map { |v| "(#{quote(v)})" }.join(",\n") + sql << ";\n\n" + sql + else + "INSERT INTO #{sm_table} (version) VALUES (#{quote(versions)});" + end + end + def data_source_sql(name = nil, type: nil) raise NotImplementedError end diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index 550b4cc86bfe7554b6925ea0c5e218713b49a1f2..96083e65192b0d96afc28ae8c39ecbdc38aa3fdf 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -74,7 +74,7 @@ class AbstractAdapter SIMPLE_INT = /\A\d+\z/ attr_accessor :visitor, :pool - attr_reader :schema_cache, :owner, :logger + attr_reader :schema_cache, :owner, :logger, :prepared_statements alias :in_use? :owner def self.type_cast_config_to_integer(config) @@ -93,8 +93,6 @@ def self.type_cast_config_to_boolean(config) end end - attr_reader :prepared_statements - def initialize(connection, logger = nil, config = {}) # :nodoc: super() @@ -142,26 +140,10 @@ def compile(bvs, conn) end end - def collector - if prepared_statements - SQLString.new - else - BindCollector.new - end - end - - def arel_visitor # :nodoc: - Arel::Visitors::ToSql.new(self) - end - def valid_type?(type) # :nodoc: !native_database_types[type].nil? end - def schema_creation - SchemaCreation.new self - end - # this method must only be called while holding connection pool's mutex def lease if in_use? @@ -475,14 +457,6 @@ def type_map # :nodoc: end end - def new_column(name, default, sql_type_metadata, null, table_name, default_function = nil, collation = nil) # :nodoc: - Column.new(name, default, sql_type_metadata, null, table_name, default_function, collation) - end - - def lookup_cast_type(sql_type) # :nodoc: - type_map.lookup(sql_type) - end - def column_name_for_operation(operation, node) # :nodoc: visitor.accept(node, collector).value end @@ -629,6 +603,18 @@ def column_for(table_name, column_name) columns(table_name).detect { |c| c.name == column_name } || raise(ActiveRecordError, "No such column: #{table_name}.#{column_name}") end + + def collector + if prepared_statements + SQLString.new + else + BindCollector.new + end + end + + def arel_visitor + Arel::Visitors::ToSql.new(self) + end end end end diff --git a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb index 55ec112c1749a1a72365f10234fd8012806f0096..f118e086bbf443c5afb9afb62ff580bd317d36ca 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb @@ -22,14 +22,6 @@ def update_table_definition(table_name, base) # :nodoc: MySQL::Table.new(table_name, base) end - def schema_creation # :nodoc: - MySQL::SchemaCreation.new(self) - end - - def arel_visitor # :nodoc: - Arel::Visitors::MySQL.new(self) - end - ## # :singleton-method: # By default, the Mysql2Adapter will consider all columns of type tinyint(1) @@ -171,10 +163,6 @@ def each_hash(result) # :nodoc: raise NotImplementedError end - def new_column(*args) #:nodoc: - MySQL::Column.new(*args) - end - # Must return the MySQL error number from the exception, if the exception has an # error number. def error_number(exception) # :nodoc: @@ -346,16 +334,6 @@ def indexes(table_name, name = nil) #:nodoc: indexes end - def new_column_from_field(table_name, field) # :nodoc: - type_metadata = fetch_type_metadata(field[:Type], field[:Extra]) - if type_metadata.type == :datetime && field[:Default] == "CURRENT_TIMESTAMP" - default, default_function = nil, field[:Default] - else - default, default_function = field[:Default], nil - end - new_column(field[:Field], default, type_metadata, field[:Null] == "YES", table_name, default_function, field[:Collation], comment: field[:Comment].presence) - end - def table_comment(table_name) # :nodoc: scope = quoted_scope(table_name) @@ -658,10 +636,6 @@ def extract_precision(sql_type) end end - def fetch_type_metadata(sql_type, extra = "") - MySQL::TypeMetadata.new(super(sql_type), extra: extra) - end - def add_index_length(quoted_columns, **options) if length = options[:length] case length @@ -864,19 +838,12 @@ def column_definitions(table_name) # :nodoc: end end - def extract_foreign_key_action(specifier) # :nodoc: - case specifier - when "CASCADE"; :cascade - when "SET NULL"; :nullify - end - end - def create_table_info(table_name) # :nodoc: select_one("SHOW CREATE TABLE #{quote_table_name(table_name)}")["Create Table"] end - def create_table_definition(*args) # :nodoc: - MySQL::TableDefinition.new(*args) + def arel_visitor + Arel::Visitors::MySQL.new(self) end def mismatched_foreign_key(message) diff --git a/activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb index 10c8bd179a4704a233b1bd1010b57b2f03ac9420..9e2d0fb5e7e4360aeb6d4d2dfba42a624a8c6272 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb @@ -3,6 +3,42 @@ module ConnectionAdapters module MySQL module SchemaStatements # :nodoc: private + def schema_creation + MySQL::SchemaCreation.new(self) + end + + def create_table_definition(*args) + MySQL::TableDefinition.new(*args) + end + + def new_column_from_field(table_name, field) + type_metadata = fetch_type_metadata(field[:Type], field[:Extra]) + if type_metadata.type == :datetime && field[:Default] == "CURRENT_TIMESTAMP" + default, default_function = nil, field[:Default] + else + default, default_function = field[:Default], nil + end + + MySQL::Column.new( + field[:Field], + default, + type_metadata, + field[:Null] == "YES", + table_name, + default_function, + field[:Collation], + comment: field[:Comment].presence + ) + end + + def fetch_type_metadata(sql_type, extra = "") + MySQL::TypeMetadata.new(super(sql_type), extra: extra) + end + + def extract_foreign_key_action(specifier) + super unless specifier == "RESTRICT" + end + def data_source_sql(name = nil, type: nil) scope = quoted_scope(name, type: type) diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb index e399b6e499f00fc788f9950284a3715f4c7c134f..da8d0c6992d55b4d23cb14384d95468ae27ffd40 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb @@ -77,6 +77,9 @@ def lookup_cast_type_from_column(column) # :nodoc: end private + def lookup_cast_type(sql_type) + super(select_value("SELECT #{quote(sql_type)}::regtype::oid", "SCHEMA").to_i) + end def _quote(value) case value diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb index a332375b786a2aee823288a256924b84cae967b1..ea62302988f105b2851fd3fd0c9fe4fc856eaaa9 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb @@ -144,25 +144,6 @@ def indexes(table_name, name = nil) # :nodoc: end.compact end - def new_column_from_field(table_name, field) # :nondoc: - column_name, type, default, notnull, oid, fmod, collation, comment = field - oid = oid.to_i - fmod = fmod.to_i - type_metadata = fetch_type_metadata(column_name, type, oid, fmod) - default_value = extract_value_from_default(default) - default_function = extract_default_function(default_value, default) - PostgreSQLColumn.new( - column_name, - default_value, - type_metadata, - !notnull, - table_name, - default_function, - collation, - comment: comment.presence - ) - end - def table_options(table_name) # :nodoc: if comment = table_comment(table_name) { comment: comment } @@ -538,14 +519,6 @@ def foreign_keys(table_name) end end - def extract_foreign_key_action(specifier) # :nodoc: - case specifier - when "c"; :cascade - when "n"; :nullify - when "r"; :restrict - end - end - # Maps logical Rails types to PostgreSQL-specific data types. def type_to_sql(type, limit: nil, precision: nil, scale: nil, array: nil, **) # :nodoc: sql = \ @@ -593,19 +566,53 @@ def columns_for_distinct(columns, orders) #:nodoc: [super, *order_columns].join(", ") end - def fetch_type_metadata(column_name, sql_type, oid, fmod) - cast_type = get_oid_type(oid, fmod, column_name, sql_type) - simple_type = SqlTypeMetadata.new( - sql_type: sql_type, - type: cast_type.type, - limit: cast_type.limit, - precision: cast_type.precision, - scale: cast_type.scale, - ) - PostgreSQLTypeMetadata.new(simple_type, oid: oid, fmod: fmod) - end - private + def schema_creation + PostgreSQL::SchemaCreation.new(self) + end + + def create_table_definition(*args) + PostgreSQL::TableDefinition.new(*args) + end + + def new_column_from_field(table_name, field) + column_name, type, default, notnull, oid, fmod, collation, comment = field + type_metadata = fetch_type_metadata(column_name, type, oid.to_i, fmod.to_i) + default_value = extract_value_from_default(default) + default_function = extract_default_function(default_value, default) + + PostgreSQLColumn.new( + column_name, + default_value, + type_metadata, + !notnull, + table_name, + default_function, + collation, + comment: comment.presence + ) + end + + def fetch_type_metadata(column_name, sql_type, oid, fmod) + cast_type = get_oid_type(oid, fmod, column_name, sql_type) + simple_type = SqlTypeMetadata.new( + sql_type: sql_type, + type: cast_type.type, + limit: cast_type.limit, + precision: cast_type.precision, + scale: cast_type.scale, + ) + PostgreSQLTypeMetadata.new(simple_type, oid: oid, fmod: fmod) + end + + def extract_foreign_key_action(specifier) + case specifier + when "c"; :cascade + when "n"; :nullify + when "r"; :restrict + end + end + def data_source_sql(name = nil, type: nil) scope = quoted_scope(name, type: type) scope[:type] ||= "'r','v','m'" # (r)elation/table, (v)iew, (m)aterialized view diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 73cf3ac8622289e8ca7259a2693e293d91c632f7..93c6fad0394f00d7b6c87077aeacb6d67db8d22e 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -121,14 +121,6 @@ class PostgreSQLAdapter < AbstractAdapter include PostgreSQL::DatabaseStatements include PostgreSQL::ColumnDumper - def schema_creation # :nodoc: - PostgreSQL::SchemaCreation.new self - end - - def arel_visitor # :nodoc: - Arel::Visitors::PostgreSQL.new(self) - end - # Returns true, since this connection adapter supports prepared statement # caching. def supports_statement_cache? @@ -376,11 +368,6 @@ def update_table_definition(table_name, base) #:nodoc: PostgreSQL::Table.new(table_name, base) end - def lookup_cast_type(sql_type) # :nodoc: - oid = execute("SELECT #{quote(sql_type)}::regtype::oid", "SCHEMA").first["oid"].to_i - super(oid) - end - def column_name_for_operation(operation, node) # :nodoc: OPERATION_ALIASES.fetch(operation) { operation.downcase } end @@ -777,8 +764,8 @@ def extract_table_ref_from_insert_sql(sql) $1.strip if $1 end - def create_table_definition(*args) - PostgreSQL::TableDefinition.new(*args) + def arel_visitor + Arel::Visitors::PostgreSQL.new(self) end def can_perform_case_insensitive_comparison_for?(column) diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/sqlite3/schema_statements.rb index 4ba245a0d8715c1b331d5a4628db7d5f0daf4811..8066a05c5e5c92dd082a1b111b32611b675c5c97 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite3/schema_statements.rb @@ -3,6 +3,31 @@ module ConnectionAdapters module SQLite3 module SchemaStatements # :nodoc: private + def schema_creation + SQLite3::SchemaCreation.new(self) + end + + def create_table_definition(*args) + SQLite3::TableDefinition.new(*args) + end + + def new_column_from_field(table_name, field) + default = \ + case field["dflt_value"] + when /^null$/i + nil + when /^'(.*)'$/m + $1.gsub("''", "'") + when /^"(.*)"$/m + $1.gsub('""', '"') + else + field["dflt_value"] + end + + type_metadata = fetch_type_metadata(field["type"]) + Column.new(field["name"], default, type_metadata, field["notnull"].to_i == 0, table_name, nil, field["collation"]) + end + def data_source_sql(name = nil, type: nil) scope = quoted_scope(name, type: type) scope[:type] ||= "'table','view'" diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb index d24bfc0c93672a7fe8090caf82e89e5f1ea0c03d..c54b88f7d11af073b8749fbbc6e945fb1328a85d 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb @@ -84,14 +84,6 @@ def update_table_definition(table_name, base) # :nodoc: SQLite3::Table.new(table_name, base) end - def schema_creation # :nodoc: - SQLite3::SchemaCreation.new self - end - - def arel_visitor # :nodoc: - Arel::Visitors::SQLite.new(self) - end - def initialize(connection, logger, connection_options, config) super(connection, logger, config) @@ -267,22 +259,6 @@ def exec_rollback_db_transaction #:nodoc: # SCHEMA STATEMENTS ======================================== - def new_column_from_field(table_name, field) # :nondoc: - case field["dflt_value"] - when /^null$/i - field["dflt_value"] = nil - when /^'(.*)'$/m - field["dflt_value"] = $1.gsub("''", "'") - when /^"(.*)"$/m - field["dflt_value"] = $1.gsub('""', '"') - end - - collation = field["collation"] - sql_type = field["type"] - type_metadata = fetch_type_metadata(sql_type) - new_column(field["name"], field["dflt_value"], type_metadata, field["notnull"].to_i == 0, table_name, nil, collation) - end - # Returns an array of indexes for the given table. def indexes(table_name, name = nil) #:nodoc: if name @@ -559,16 +535,8 @@ def table_structure_with_collation(table_name, basic_structure) end end - def create_table_definition(*args) - SQLite3::TableDefinition.new(*args) - end - - def extract_foreign_key_action(specifier) - case specifier - when "CASCADE"; :cascade - when "SET NULL"; :nullify - when "RESTRICT"; :restrict - end + def arel_visitor + Arel::Visitors::SQLite.new(self) end def configure_connection diff --git a/activerecord/test/cases/column_definition_test.rb b/activerecord/test/cases/column_definition_test.rb index d2307001192ceb52b61c5ca50318aae676563965..90c8d21c43f64de97e0f8df9b17f6702a0602115 100644 --- a/activerecord/test/cases/column_definition_test.rb +++ b/activerecord/test/cases/column_definition_test.rb @@ -8,7 +8,7 @@ def setup def @adapter.native_database_types { string: "varchar" } end - @viz = @adapter.schema_creation + @viz = @adapter.send(:schema_creation) end # Avoid column definitions in create table statements like: