diff options
Diffstat (limited to 'activerecord/lib/active_record/connection_adapters')
8 files changed, 162 insertions, 160 deletions
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 4840307094..7041d92586 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -1185,12 +1185,22 @@ module ActiveRecord end # Changes the comment for a table or removes it if +nil+. - def change_table_comment(table_name, comment) + # + # Passing a hash containing +:from+ and +:to+ will make this change + # reversible in migration: + # + # change_table_comment(:posts, from: "old_comment", to: "new_comment") + def change_table_comment(table_name, comment_or_changes) raise NotImplementedError, "#{self.class} does not support changing table comments" end # Changes the comment for a column or removes it if +nil+. - def change_column_comment(table_name, column_name, comment) + # + # Passing a hash containing +:from+ and +:to+ will make this change + # reversible in migration: + # + # change_column_comment(:posts, :state, from: "old_comment", to: "new_comment") + def change_column_comment(table_name, column_name, comment_or_changes) raise NotImplementedError, "#{self.class} does not support changing column comments" end @@ -1374,11 +1384,37 @@ module ActiveRecord default_or_changes end end + alias :extract_new_comment_value :extract_new_default_value def can_remove_index_by_name?(options) options.is_a?(Hash) && options.key?(:name) && options.except(:name, :algorithm).empty? end + def bulk_change_table(table_name, operations) + sql_fragments = [] + non_combinable_operations = [] + + operations.each do |command, args| + table, arguments = args.shift, args + method = :"#{command}_for_alter" + + if respond_to?(method, true) + sqls, procs = Array(send(method, table, *arguments)).partition { |v| v.is_a?(String) } + sql_fragments << sqls + non_combinable_operations.concat(procs) + else + execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty? + non_combinable_operations.each(&:call) + sql_fragments = [] + non_combinable_operations = [] + send(command, table, *arguments) + end + end + + execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty? + non_combinable_operations.each(&:call) + end + def add_column_for_alter(table_name, column_name, type, options = {}) td = create_table_definition(table_name) cd = td.new_column_definition(column_name, type, options) 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 ca8bbc14da..8b907759c6 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb @@ -63,7 +63,7 @@ module ActiveRecord /mariadb/i.match?(full_version) end - def supports_bulk_alter? #:nodoc: + def supports_bulk_alter? true end @@ -285,22 +285,8 @@ module ActiveRecord SQL end - def bulk_change_table(table_name, operations) #:nodoc: - sqls = operations.flat_map do |command, args| - table, arguments = args.shift, args - method = :"#{command}_for_alter" - - if respond_to?(method, true) - send(method, table, *arguments) - else - raise "Unknown method called : #{method}(#{arguments.inspect})" - end - end.join(", ") - - execute("ALTER TABLE #{quote_table_name(table_name)} #{sqls}") - end - - def change_table_comment(table_name, comment) #:nodoc: + def change_table_comment(table_name, comment_or_changes) # :nodoc: + comment = extract_new_comment_value(comment_or_changes) comment = "" if comment.nil? execute("ALTER TABLE #{quote_table_name(table_name)} COMMENT #{quote(comment)}") end @@ -356,7 +342,8 @@ module ActiveRecord change_column table_name, column_name, nil, null: null end - def change_column_comment(table_name, column_name, comment) #:nodoc: + def change_column_comment(table_name, column_name, comment_or_changes) # :nodoc: + comment = extract_new_comment_value(comment_or_changes) change_column table_name, column_name, nil, comment: comment end diff --git a/activerecord/lib/active_record/connection_adapters/mysql/type_metadata.rb b/activerecord/lib/active_record/connection_adapters/mysql/type_metadata.rb index 56479f27bf..9167593064 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql/type_metadata.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql/type_metadata.rb @@ -10,12 +10,11 @@ module ActiveRecord def initialize(type_metadata, extra: "") super(type_metadata) - @type_metadata = type_metadata @extra = extra end def ==(other) - other.is_a?(MySQL::TypeMetadata) && + other.is_a?(TypeMetadata) && __getobj__ == other.__getobj__ && extra == other.extra end diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/column.rb b/activerecord/lib/active_record/connection_adapters/postgresql/column.rb index ef98d2b37a..ec25bb1e19 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/column.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/column.rb @@ -4,8 +4,7 @@ module ActiveRecord module ConnectionAdapters module PostgreSQL class Column < ConnectionAdapters::Column # :nodoc: - delegate :array, :oid, :fmod, to: :sql_type_metadata - alias :array? :array + delegate :oid, :fmod, to: :sql_type_metadata def initialize(*, serial: nil, **) super @@ -15,6 +14,15 @@ module ActiveRecord def serial? @serial end + + def array + sql_type_metadata.sql_type.end_with?("[]") + end + alias :array? :array + + def sql_type + super.sub(/\[\]\z/, "") + end end end PostgreSQLColumn = PostgreSQL::Column # :nodoc: 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 c412d1f34c..40c5e51d92 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb @@ -368,31 +368,6 @@ module ActiveRecord SQL end - def bulk_change_table(table_name, operations) - sql_fragments = [] - non_combinable_operations = [] - - operations.each do |command, args| - table, arguments = args.shift, args - method = :"#{command}_for_alter" - - if respond_to?(method, true) - sqls, procs = Array(send(method, table, *arguments)).partition { |v| v.is_a?(String) } - sql_fragments << sqls - non_combinable_operations.concat(procs) - else - execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty? - non_combinable_operations.each(&:call) - sql_fragments = [] - non_combinable_operations = [] - send(command, table, *arguments) - end - end - - execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty? - non_combinable_operations.each(&:call) - end - # Renames a table. # Also renames a table's primary key sequence if the sequence name exists and # matches the Active Record default. @@ -443,14 +418,16 @@ module ActiveRecord end # Adds comment for given table column or drops it if +comment+ is a +nil+ - def change_column_comment(table_name, column_name, comment) # :nodoc: + def change_column_comment(table_name, column_name, comment_or_changes) # :nodoc: clear_cache! + comment = extract_new_comment_value(comment_or_changes) execute "COMMENT ON COLUMN #{quote_table_name(table_name)}.#{quote_column_name(column_name)} IS #{quote(comment)}" end # Adds comment for given table or drops it if +comment+ is a +nil+ - def change_table_comment(table_name, comment) # :nodoc: + def change_table_comment(table_name, comment_or_changes) # :nodoc: clear_cache! + comment = extract_new_comment_value(comment_or_changes) execute "COMMENT ON TABLE #{quote_table_name(table_name)} IS #{quote(comment)}" end @@ -675,7 +652,7 @@ module ActiveRecord precision: cast_type.precision, scale: cast_type.scale, ) - PostgreSQLTypeMetadata.new(simple_type, oid: oid, fmod: fmod) + PostgreSQL::TypeMetadata.new(simple_type, oid: oid, fmod: fmod) end def sequence_name_from_parts(table_name, column_name, suffix) diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/type_metadata.rb b/activerecord/lib/active_record/connection_adapters/postgresql/type_metadata.rb index 403b3ead98..8bdec623af 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/type_metadata.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/type_metadata.rb @@ -3,39 +3,34 @@ module ActiveRecord # :stopdoc: module ConnectionAdapters - class PostgreSQLTypeMetadata < DelegateClass(SqlTypeMetadata) - undef to_yaml if method_defined?(:to_yaml) + module PostgreSQL + class TypeMetadata < DelegateClass(SqlTypeMetadata) + undef to_yaml if method_defined?(:to_yaml) - attr_reader :oid, :fmod, :array + attr_reader :oid, :fmod - def initialize(type_metadata, oid: nil, fmod: nil) - super(type_metadata) - @type_metadata = type_metadata - @oid = oid - @fmod = fmod - @array = /\[\]$/.match?(type_metadata.sql_type) - end - - def sql_type - super.gsub(/\[\]$/, "") - end + def initialize(type_metadata, oid: nil, fmod: nil) + super(type_metadata) + @oid = oid + @fmod = fmod + end - def ==(other) - other.is_a?(PostgreSQLTypeMetadata) && - __getobj__ == other.__getobj__ && - oid == other.oid && - fmod == other.fmod && - array == other.array - end - alias eql? == + def ==(other) + other.is_a?(TypeMetadata) && + __getobj__ == other.__getobj__ && + oid == other.oid && + fmod == other.fmod + end + alias eql? == - def hash - PostgreSQLTypeMetadata.hash ^ - __getobj__.hash ^ - oid.hash ^ - fmod.hash ^ - array.hash + def hash + TypeMetadata.hash ^ + __getobj__.hash ^ + oid.hash ^ + fmod.hash + end end end + PostgreSQLTypeMetadata = PostgreSQL::TypeMetadata end end diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3/database_statements.rb b/activerecord/lib/active_record/connection_adapters/sqlite3/database_statements.rb index ffa75172b5..46ce1a15b5 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite3/database_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite3/database_statements.rb @@ -4,6 +4,82 @@ module ActiveRecord module ConnectionAdapters module SQLite3 module DatabaseStatements + READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(:begin, :commit, :explain, :select, :pragma, :release, :savepoint, :rollback) # :nodoc: + private_constant :READ_QUERY + + def write_query?(sql) # :nodoc: + !READ_QUERY.match?(sql) + end + + def execute(sql, name = nil) #:nodoc: + if preventing_writes? && write_query?(sql) + raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}" + end + + materialize_transactions + + log(sql, name) do + ActiveSupport::Dependencies.interlock.permit_concurrent_loads do + @connection.execute(sql) + end + end + end + + def exec_query(sql, name = nil, binds = [], prepare: false) + if preventing_writes? && write_query?(sql) + raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}" + end + + materialize_transactions + + type_casted_binds = type_casted_binds(binds) + + log(sql, name, binds, type_casted_binds) do + ActiveSupport::Dependencies.interlock.permit_concurrent_loads do + # Don't cache statements if they are not prepared + unless prepare + stmt = @connection.prepare(sql) + begin + cols = stmt.columns + unless without_prepared_statement?(binds) + stmt.bind_params(type_casted_binds) + end + records = stmt.to_a + ensure + stmt.close + end + else + stmt = @statements[sql] ||= @connection.prepare(sql) + cols = stmt.columns + stmt.reset! + stmt.bind_params(type_casted_binds) + records = stmt.to_a + end + + ActiveRecord::Result.new(cols, records) + end + end + end + + def exec_delete(sql, name = "SQL", binds = []) + exec_query(sql, name, binds) + @connection.changes + end + alias :exec_update :exec_delete + + def begin_db_transaction #:nodoc: + log("begin transaction", nil) { @connection.transaction } + end + + def commit_db_transaction #:nodoc: + log("commit transaction", nil) { @connection.commit } + end + + def exec_rollback_db_transaction #:nodoc: + log("rollback transaction", nil) { @connection.rollback } + end + + private def execute_batch(sql, name = nil) if preventing_writes? && write_query?(sql) @@ -19,6 +95,10 @@ module ActiveRecord end end + def last_inserted_id(result) + @connection.last_insert_row_id + end + def build_fixture_statements(fixture_set) fixture_set.flat_map do |table_name, fixtures| next if fixtures.empty? diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb index 1801924c09..f5f5827d04 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb @@ -204,91 +204,11 @@ module ActiveRecord #-- # DATABASE STATEMENTS ====================================== #++ - - READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(:begin, :commit, :explain, :select, :pragma, :release, :savepoint, :rollback) # :nodoc: - private_constant :READ_QUERY - - def write_query?(sql) # :nodoc: - !READ_QUERY.match?(sql) - end - def explain(arel, binds = []) sql = "EXPLAIN QUERY PLAN #{to_sql(arel, binds)}" SQLite3::ExplainPrettyPrinter.new.pp(exec_query(sql, "EXPLAIN", [])) end - def exec_query(sql, name = nil, binds = [], prepare: false) - if preventing_writes? && write_query?(sql) - raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}" - end - - materialize_transactions - - type_casted_binds = type_casted_binds(binds) - - log(sql, name, binds, type_casted_binds) do - ActiveSupport::Dependencies.interlock.permit_concurrent_loads do - # Don't cache statements if they are not prepared - unless prepare - stmt = @connection.prepare(sql) - begin - cols = stmt.columns - unless without_prepared_statement?(binds) - stmt.bind_params(type_casted_binds) - end - records = stmt.to_a - ensure - stmt.close - end - else - stmt = @statements[sql] ||= @connection.prepare(sql) - cols = stmt.columns - stmt.reset! - stmt.bind_params(type_casted_binds) - records = stmt.to_a - end - - ActiveRecord::Result.new(cols, records) - end - end - end - - def exec_delete(sql, name = "SQL", binds = []) - exec_query(sql, name, binds) - @connection.changes - end - alias :exec_update :exec_delete - - def last_inserted_id(result) - @connection.last_insert_row_id - end - - def execute(sql, name = nil) #:nodoc: - if preventing_writes? && write_query?(sql) - raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}" - end - - materialize_transactions - - log(sql, name) do - ActiveSupport::Dependencies.interlock.permit_concurrent_loads do - @connection.execute(sql) - end - end - end - - def begin_db_transaction #:nodoc: - log("begin transaction", nil) { @connection.transaction } - end - - def commit_db_transaction #:nodoc: - log("commit transaction", nil) { @connection.commit } - end - - def exec_rollback_db_transaction #:nodoc: - log("rollback transaction", nil) { @connection.rollback } - end - # SCHEMA STATEMENTS ======================================== def primary_keys(table_name) # :nodoc: |