From d739dd1627d1b20bde417045558848fe92a82d6c Mon Sep 17 00:00:00 2001 From: Sean Griffin Date: Tue, 1 Jul 2014 08:11:59 -0600 Subject: Use a type object for type casting behavior on SQLite3 --- .../connection_adapters/sqlite3_adapter.rb | 36 +++++++++++++--------- 1 file changed, 22 insertions(+), 14 deletions(-) (limited to 'activerecord/lib/active_record/connection_adapters') diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb index e6163771e8..2905cf4a01 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb @@ -50,6 +50,16 @@ module ActiveRecord end end + class SQLite3String < Type::String # :nodoc: + def type_cast_for_database(value) + if value.is_a?(::String) && value.encoding == Encoding::ASCII_8BIT + value.encode(Encoding::UTF_8) + else + super + end + end + end + # The SQLite3 adapter works SQLite 3.6.16 or newer # with the sqlite3-ruby drivers (available as gem from https://rubygems.org/gems/sqlite3). # @@ -220,13 +230,23 @@ module ActiveRecord # QUOTING ================================================== def _quote(value) # :nodoc: - if value.is_a?(Type::Binary::Data) + case value + when Type::Binary::Data "x'#{value.hex}'" else super end end + def _type_cast(value) # :nodoc: + case value + when BigDecimal + value.to_f + else + super + end + end + def quote_string(s) #:nodoc: @connection.class.quote(s) end @@ -249,19 +269,6 @@ module ActiveRecord end end - def type_cast(value, column) # :nodoc: - return value.to_f if BigDecimal === value - return super unless String === value - return super unless column && value - - value = super - if column.type == :string && value.encoding == Encoding::ASCII_8BIT - logger.error "Binary data inserted for `string` type on column `#{column.name}`" if logger - value = value.encode Encoding::UTF_8 - end - value - end - # DATABASE STATEMENTS ====================================== def explain(arel, binds = []) @@ -503,6 +510,7 @@ module ActiveRecord def initialize_type_map(m) super m.register_type(/binary/i, SQLite3Binary.new) + register_class_with_limit m, %r(char)i, SQLite3String end def select(sql, name = nil, binds = []) #:nodoc: -- cgit v1.2.3 From d5411fb37ca9a35f49a55fd52d2aefb7956b3e41 Mon Sep 17 00:00:00 2001 From: Sean Griffin Date: Fri, 11 Jul 2014 07:36:03 -0600 Subject: Don't rely on the column SQL type for bit string quoting --- .../connection_adapters/postgresql/oid/bit.rb | 26 ++++++++++++++++++++++ .../connection_adapters/postgresql/quoting.rb | 20 ++++++----------- 2 files changed, 33 insertions(+), 13 deletions(-) (limited to 'activerecord/lib/active_record/connection_adapters') diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/bit.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/bit.rb index 243ecd13cf..1dbb40ca1d 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/bit.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/bit.rb @@ -19,6 +19,32 @@ module ActiveRecord value end end + + def type_cast_for_database(value) + Data.new(super) if value + end + + class Data + def initialize(value) + @value = value + end + + def to_s + value + end + + def binary? + /\A[01]*\Z/ === value + end + + def hex? + /\A[0-9A-F]*\Z/i === value + end + + protected + + attr_reader :value + end end end end diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb index 5359c5b666..cf5c8d288e 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb @@ -18,8 +18,6 @@ module ActiveRecord def quote(value, column = nil) #:nodoc: return super unless column - sql_type = type_to_sql(column.type, column.limit, column.precision, column.scale) - case value when Float if value.infinite? || value.nan? @@ -27,16 +25,6 @@ module ActiveRecord else super end - when String - case sql_type - when /^bit/ - case value - when /\A[01]*\Z/ then "B'#{value}'" # Bit-string notation - when /\A[0-9A-F]*\Z/i then "X'#{value}'" # Hexadecimal notation - end - else - super - end else super end @@ -100,6 +88,12 @@ module ActiveRecord "'#{escape_bytea(value.to_s)}'" when OID::Xml::Data "xml '#{quote_string(value.to_s)}'" + when OID::Bit::Data + if value.binary? + "B'#{value}'" + elsif value.hex? + "X'#{value}'" + end else super end @@ -112,7 +106,7 @@ module ActiveRecord # See http://deveiate.org/code/pg/PGconn.html#method-i-exec_prepared-doc # for more information { value: value.to_s, format: 1 } - when OID::Xml::Data + when OID::Xml::Data, OID::Bit::Data value.to_s else super -- cgit v1.2.3 From f378f23653259dee98061b279b628eb774e6faf1 Mon Sep 17 00:00:00 2001 From: Abdelkader Boudih Date: Thu, 29 May 2014 07:45:56 +0000 Subject: Treat invalid uuid as nil --- .../active_record/connection_adapters/postgresql/oid/uuid.rb | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'activerecord/lib/active_record/connection_adapters') diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/uuid.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/uuid.rb index 89728b0fe2..dd97393eac 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/uuid.rb @@ -3,12 +3,21 @@ module ActiveRecord module PostgreSQL module OID # :nodoc: class Uuid < Type::Value # :nodoc: + RFC_4122 = %r{\A\{?[a-fA-F0-9]{4}-? + [a-fA-F0-9]{4}-? + [a-fA-F0-9]{4}-? + [1-5][a-fA-F0-9]{3}-? + [8-Bab][a-fA-F0-9]{3}-? + [a-fA-F0-9]{4}-? + [a-fA-F0-9]{4}-? + [a-fA-F0-9]{4}-?\}?\z}x + def type :uuid end def type_cast(value) - value.presence + value.to_s[RFC_4122, 0] end end end -- cgit v1.2.3 From 11338183265f759b07c8dffd526b92fb550ba79d Mon Sep 17 00:00:00 2001 From: Jeff Browning Date: Fri, 27 Jun 2014 10:04:18 -0400 Subject: Fix version detection for RENAME INDEX support. Fixes #15931. --- .../connection_adapters/abstract_mysql_adapter.rb | 14 +++++++++++++- .../active_record/connection_adapters/mysql2_adapter.rb | 4 ++-- .../lib/active_record/connection_adapters/mysql_adapter.rb | 6 +++--- 3 files changed, 18 insertions(+), 6 deletions(-) (limited to 'activerecord/lib/active_record/connection_adapters') 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 ccb957d2c8..db8c16914e 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb @@ -473,7 +473,7 @@ module ActiveRecord end def rename_index(table_name, old_name, new_name) - if (version[0] == 5 && version[1] >= 7) || version[0] >= 6 + if supports_rename_index? execute "ALTER TABLE #{quote_table_name(table_name)} RENAME INDEX #{quote_table_name(old_name)} TO #{quote_table_name(new_name)}" else super @@ -774,10 +774,22 @@ module ActiveRecord private + def version + @version ||= full_version.scan(/^(\d+)\.(\d+)\.(\d+)/).flatten.map { |v| v.to_i } + end + + def mariadb? + !!(full_version =~ /mariadb/i) + end + def supports_views? version[0] >= 5 end + def supports_rename_index? + mariadb? ? false : (version[0] == 5 && version[1] >= 7) || version[0] >= 6 + end + def configure_connection variables = @config.fetch(:variables, {}).stringify_keys diff --git a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb index 0a14cdfe89..39d52e6349 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb @@ -269,8 +269,8 @@ module ActiveRecord super end - def version - @version ||= @connection.info[:version].scan(/^(\d+)\.(\d+)\.(\d+)/).flatten.map { |v| v.to_i } + def full_version + @full_version ||= @connection.info[:version] end def set_field_encoding field_name diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb index ad07a46e51..a03bc28744 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb @@ -470,9 +470,9 @@ module ActiveRecord rows end - # Returns the version of the connected MySQL server. - def version - @version ||= @connection.server_info.scan(/^(\d+)\.(\d+)\.(\d+)/).flatten.map { |v| v.to_i } + # Returns the full version of the connected MySQL server. + def full_version + @full_version ||= @connection.server_info end def set_field_encoding field_name -- cgit v1.2.3 From 65122025fbfe6b75efc7220c1687bf2d8a7b8879 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Tue, 15 Jul 2014 12:43:19 -0300 Subject: Predicate methods don't need to return `true` / `false` --- .../lib/active_record/connection_adapters/abstract_mysql_adapter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record/connection_adapters') 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 db8c16914e..e5417a9556 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb @@ -779,7 +779,7 @@ module ActiveRecord end def mariadb? - !!(full_version =~ /mariadb/i) + full_version =~ /mariadb/i end def supports_views? -- cgit v1.2.3 From 6d327dbc0cd7969375e14a56c0a9bc6a7c847056 Mon Sep 17 00:00:00 2001 From: Andrey Novikov Date: Sat, 19 Jul 2014 23:38:38 +0400 Subject: Allow to specify a type for foreign key column in migrations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [Andrey Novikov & Ɓukasz Sarnacki] --- .../connection_adapters/abstract/schema_definitions.rb | 9 ++++++++- .../connection_adapters/abstract/schema_statements.rb | 3 ++- 2 files changed, 10 insertions(+), 2 deletions(-) (limited to 'activerecord/lib/active_record/connection_adapters') diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb index 98e6795f10..d51bf022a4 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb @@ -280,12 +280,19 @@ module ActiveRecord column(:updated_at, :datetime, options) end + # Adds an appropriately-named _id column as :integer (or whatever :type option specifies), + # plus a corresponding _type column if the :polymorphic option is supplied. If :polymorphic + # is a hash of options, these will be used when creating the _type column. The :index option + # will also create an index, similar to calling add_index. + # + # references :tagger, polymorphic: true, index: true, type: :uuid def references(*args) options = args.extract_options! polymorphic = options.delete(:polymorphic) index_options = options.delete(:index) + type = options.delete(:type) || :integer args.each do |col| - column("#{col}_id", :integer, options) + column("#{col}_id", type, options) column("#{col}_type", :string, polymorphic.is_a?(Hash) ? polymorphic : options) if polymorphic index(polymorphic ? %w(id type).map { |t| "#{col}_#{t}" } : "#{col}_id", index_options.is_a?(Hash) ? index_options : {}) if index_options 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 5814c2b711..8ecb7c0506 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -619,7 +619,8 @@ module ActiveRecord def add_reference(table_name, ref_name, options = {}) polymorphic = options.delete(:polymorphic) index_options = options.delete(:index) - add_column(table_name, "#{ref_name}_id", :integer, options) + type = options.delete(:type) || :integer + add_column(table_name, "#{ref_name}_id", type, options) add_column(table_name, "#{ref_name}_type", :string, polymorphic.is_a?(Hash) ? polymorphic : options) if polymorphic add_index(table_name, polymorphic ? %w[id type].map{ |t| "#{ref_name}_#{t}" } : "#{ref_name}_id", index_options.is_a?(Hash) ? index_options : {}) if index_options end -- cgit v1.2.3 From 2a67e12fdb832a6b6e94bed1a0b05290019f5514 Mon Sep 17 00:00:00 2001 From: Yves Senn Date: Thu, 24 Jul 2014 14:27:41 +0200 Subject: pg, `change_column_default, :table, :column, nil` issues `DROP DEFAULT`. Closes #16261. [Matthew Draper, Yves Senn] Using `DEFAULT NULL` results in the same behavior as `DROP DEFAULT`. However, PostgreSQL will cast the default to the columns type, which leaves us with a default like "default NULL::character varying". /cc @matthewd --- .../connection_adapters/postgresql/schema_statements.rb | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'activerecord/lib/active_record/connection_adapters') 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 e09672d239..7042817672 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb @@ -423,8 +423,16 @@ module ActiveRecord def change_column_default(table_name, column_name, default) clear_cache! column = column_for(table_name, column_name) + return unless column - execute "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} SET DEFAULT #{quote_default_value(default, column)}" if column + alter_column_query = "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} %s" + if default.nil? + # DEFAULT NULL results in the same behavior as DROP DEFAULT. However, PostgreSQL will + # cast the default to the columns type, which leaves us with a default like "default NULL::character varying". + execute alter_column_query % "DROP DEFAULT" + else + execute alter_column_query % "SET DEFAULT #{quote_default_value(default, column)}" + end end def change_column_null(table_name, column_name, null, default = nil) -- cgit v1.2.3 From 98d2421ef937a819ef8dadc94ba519842e33e7b6 Mon Sep 17 00:00:00 2001 From: a3gis Date: Thu, 24 Jul 2014 14:21:55 +0100 Subject: Fixes #16265 --- .../lib/active_record/connection_adapters/connection_specification.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record/connection_adapters') diff --git a/activerecord/lib/active_record/connection_adapters/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/connection_specification.rb index 2fcb085ab2..7082989f68 100644 --- a/activerecord/lib/active_record/connection_adapters/connection_specification.rb +++ b/activerecord/lib/active_record/connection_adapters/connection_specification.rb @@ -250,7 +250,7 @@ module ActiveRecord # Connection details inside of the "url" key win any merge conflicts def resolve_hash_connection(spec) if spec["url"] && spec["url"] !~ /^jdbc:/ - connection_hash = resolve_string_connection(spec.delete("url")) + connection_hash = resolve_url_connection(spec.delete("url")) spec.merge!(connection_hash) end spec -- cgit v1.2.3 From c0932162d27f4a41455893d38ef55cb1cc606575 Mon Sep 17 00:00:00 2001 From: a3gis Date: Thu, 24 Jul 2014 14:23:36 +0100 Subject: Fix documentation typo in ConnectionSpecification::Resolve.spec --- .../lib/active_record/connection_adapters/connection_specification.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record/connection_adapters') diff --git a/activerecord/lib/active_record/connection_adapters/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/connection_specification.rb index 7082989f68..5693031053 100644 --- a/activerecord/lib/active_record/connection_adapters/connection_specification.rb +++ b/activerecord/lib/active_record/connection_adapters/connection_specification.rb @@ -160,7 +160,7 @@ module ActiveRecord # config = { "production" => { "host" => "localhost", "database" => "foo", "adapter" => "sqlite3" } } # spec = Resolver.new(config).spec(:production) # spec.adapter_method - # # => "sqlite3" + # # => "sqlite3_connection" # spec.config # # => { "host" => "localhost", "database" => "foo", "adapter" => "sqlite3" } # -- cgit v1.2.3 From 99b82fdf03fcf6d6ca8e2d810ba35011723a5267 Mon Sep 17 00:00:00 2001 From: Philippe Creux Date: Thu, 24 Jul 2014 10:39:39 -0700 Subject: Add support for Postgresql JSONB [Philippe Creux, Chris Teague] --- .../connection_adapters/postgresql/oid.rb | 1 + .../connection_adapters/postgresql/oid/jsonb.rb | 23 ++++++++++++++++++++++ .../postgresql/schema_definitions.rb | 4 ++++ .../connection_adapters/postgresql_adapter.rb | 1 + 4 files changed, 29 insertions(+) create mode 100644 activerecord/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb (limited to 'activerecord/lib/active_record/connection_adapters') diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb index d05ce61330..d28a2b4fa0 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb @@ -14,6 +14,7 @@ require 'active_record/connection_adapters/postgresql/oid/hstore' require 'active_record/connection_adapters/postgresql/oid/inet' require 'active_record/connection_adapters/postgresql/oid/integer' require 'active_record/connection_adapters/postgresql/oid/json' +require 'active_record/connection_adapters/postgresql/oid/jsonb' require 'active_record/connection_adapters/postgresql/oid/money' require 'active_record/connection_adapters/postgresql/oid/point' require 'active_record/connection_adapters/postgresql/oid/range' diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb new file mode 100644 index 0000000000..34ed32ad35 --- /dev/null +++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb @@ -0,0 +1,23 @@ +module ActiveRecord + module ConnectionAdapters + module PostgreSQL + module OID # :nodoc: + class Jsonb < Json # :nodoc: + def type + :jsonb + end + + def changed_in_place?(raw_old_value, new_value) + # Postgres does not preserve insignificant whitespaces when + # roundtripping jsonb columns. This causes some false positives for + # the comparison here. Therefore, we need to parse and re-dump the + # raw value here to ensure the insignificant whitespaces are + # consitent with our encoder's output. + raw_old_value = type_cast_for_database(type_cast_from_database(raw_old_value)) + super(raw_old_value, new_value) + end + end + end + end + end +end diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_definitions.rb index 0867e5ef54..83554bbf74 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_definitions.rb @@ -64,6 +64,10 @@ module ActiveRecord column(name, :json, options) end + def jsonb(name, options = {}) + column(name, :jsonb, options) + end + def citext(name, options = {}) column(name, :citext, options) end diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index f660fc41cf..eede374678 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -451,6 +451,7 @@ module ActiveRecord m.register_type 'point', OID::Point.new m.register_type 'hstore', OID::Hstore.new m.register_type 'json', OID::Json.new + m.register_type 'jsonb', OID::Jsonb.new m.register_type 'cidr', OID::Cidr.new m.register_type 'inet', OID::Inet.new m.register_type 'uuid', OID::Uuid.new -- cgit v1.2.3 From 368525a5a5b43a2955d063c2f81af5d6ed1c2188 Mon Sep 17 00:00:00 2001 From: Arthur Neves Date: Thu, 24 Jul 2014 16:25:34 -0400 Subject: Remove finishing? method from transaction. The finishing variable on the transaction object was a work-around for the savepoint name, so after a rollback/commit the savepoint could be released with the previous name. related: 9296e6939bcc786149a07dac334267c4035b623a 60c88e64e26682a954f7c8cd6669d409ffffcc8b --- .../connection_adapters/abstract/transaction.rb | 33 +++++++--------------- .../connection_adapters/abstract_adapter.rb | 4 ++- 2 files changed, 13 insertions(+), 24 deletions(-) (limited to 'activerecord/lib/active_record/connection_adapters') diff --git a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb index bc4884b538..1721437615 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb @@ -78,45 +78,28 @@ module ActiveRecord @parent = parent @records = [] - @finishing = false @joinable = options.fetch(:joinable, true) end - # This state is necessary so that we correctly handle stuff that might - # happen in a commit/rollback. But it's kinda distasteful. Maybe we can - # find a better way to structure it in the future. - def finishing? - @finishing - end def joinable? - @joinable && !finishing? + @joinable end def number - if finishing? - parent.number - else - parent.number + 1 - end + parent.number + 1 end def begin(options = {}) - if finishing? - parent.begin - else - SavepointTransaction.new(connection, self, options) - end + SavepointTransaction.new(connection, self, options) end def rollback - @finishing = true perform_rollback parent end def commit - @finishing = true perform_commit parent end @@ -183,24 +166,28 @@ module ActiveRecord end class SavepointTransaction < OpenTransaction #:nodoc: + attr_reader :savepoint_name + def initialize(connection, parent, options = {}) if options[:isolation] raise ActiveRecord::TransactionIsolationError, "cannot set transaction isolation in a nested transaction" end super - connection.create_savepoint + + @savepoint_name = "active_record_#{number}" + connection.create_savepoint(@savepoint_name) end def perform_rollback - connection.rollback_to_savepoint + connection.rollback_to_savepoint(@savepoint_name) rollback_records end def perform_commit @state.set_state(:committed) @state.parent = parent.state - connection.release_savepoint + connection.release_savepoint(@savepoint_name) end end end diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index f8c054eb69..1397358f79 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -357,7 +357,9 @@ module ActiveRecord end def current_savepoint_name - "active_record_#{open_transactions}" + if current_transaction.is_a? SavepointTransaction + current_transaction.savepoint_name + end end # Check the connection back in to the connection pool -- cgit v1.2.3 From 8f32ddba776426384b46feaededaf57bb01f5aef Mon Sep 17 00:00:00 2001 From: a3gis Date: Fri, 25 Jul 2014 18:03:17 +0200 Subject: Fix documentation of SQlite3Adapter.columns As of https://github.com/rails/rails/commit/e781aa31fc52a7c696115302ef4d4e02bfd1533b SQLite3Column has been dropped. --- activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record/connection_adapters') diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb index bf96acad4a..faf1cdc686 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb @@ -385,7 +385,7 @@ module ActiveRecord table_name && tables(nil, table_name).any? end - # Returns an array of +SQLite3Column+ objects for the table specified by +table_name+. + # Returns an array of +Column+ objects for the table specified by +table_name+. def columns(table_name) #:nodoc: table_structure(table_name).map do |field| case field["dflt_value"] -- cgit v1.2.3 From 97bb76dc288d998a684b17a09d79708e2e4b584a Mon Sep 17 00:00:00 2001 From: Arthur Neves Date: Thu, 24 Jul 2014 13:37:49 -0400 Subject: Transactions refactoring Add a transaction manager per connection, so it can controls the connection responsibilities. Delegate transaction methods to transaction_manager --- .../abstract/database_statements.rb | 44 +++------------- .../connection_adapters/abstract/transaction.rb | 58 ++++++++++++++++++++++ .../connection_adapters/abstract_adapter.rb | 1 + 3 files changed, 65 insertions(+), 38 deletions(-) (limited to 'activerecord/lib/active_record/connection_adapters') diff --git a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb index e8ce00d92b..98e96099cb 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb @@ -203,62 +203,30 @@ module ActiveRecord if options[:isolation] raise ActiveRecord::TransactionIsolationError, "cannot set isolation when joining a transaction" end - yield else - within_new_transaction(options) { yield } + transaction_manager.within_new_transaction(options) { yield } end rescue ActiveRecord::Rollback # rollbacks are silently swallowed end - def within_new_transaction(options = {}) #:nodoc: - transaction = begin_transaction(options) - yield - rescue Exception => error - rollback_transaction if transaction - raise - ensure - begin - commit_transaction unless error - rescue Exception - rollback_transaction - raise - end - end - - def open_transactions - @transaction.number - end + attr_reader :transaction_manager #:nodoc: - def current_transaction #:nodoc: - @transaction - end + delegate :within_new_transaction, :open_transactions, :current_transaction, :begin_transaction, :commit_transaction, :rollback_transaction, to: :transaction_manager def transaction_open? - @transaction.open? - end - - def begin_transaction(options = {}) #:nodoc: - @transaction = @transaction.begin(options) - end - - def commit_transaction #:nodoc: - @transaction = @transaction.commit - end - - def rollback_transaction #:nodoc: - @transaction = @transaction.rollback + current_transaction.open? end def reset_transaction #:nodoc: - @transaction = ClosedTransaction.new(self) + @transaction_manager = TransactionManager.new(self) end # Register a record with the current transaction so that its after_commit and after_rollback callbacks # can be called. def add_transaction_record(record) - @transaction.add_record(record) + current_transaction.add_record(record) end # Begins the transaction (and turns off auto-committing). diff --git a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb index 1721437615..7618d6902d 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb @@ -1,5 +1,63 @@ module ActiveRecord module ConnectionAdapters + class TransactionManager #:nodoc: + def initialize(connection) + @stack = [] + @connection = connection + end + + def begin_transaction(options = {}) + transaction = + if @stack.empty? + RealTransaction.new(@connection, current_transaction, options) + else + SavepointTransaction.new(@connection, current_transaction, options) + end + + @stack.push(transaction) + transaction + end + + def commit_transaction + @stack.pop.commit + end + + def rollback_transaction + @stack.pop.rollback + end + + def within_new_transaction(options = {}) + transaction = begin_transaction options + yield + rescue Exception => error + transaction.rollback if transaction + raise + ensure + begin + transaction.commit unless error + rescue Exception + transaction.rollback + raise + ensure + @stack.pop if transaction + end + end + + def open_transactions + @stack.size + end + + def current_transaction + @stack.last || closed_transaction + end + + private + + def closed_transaction + @closed_transaction ||= ClosedTransaction.new(@connection) + end + end + class Transaction #:nodoc: attr_reader :connection diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index 1397358f79..c31726437f 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -45,6 +45,7 @@ module ActiveRecord end autoload_at 'active_record/connection_adapters/abstract/transaction' do + autoload :TransactionManager autoload :ClosedTransaction autoload :RealTransaction autoload :SavepointTransaction -- cgit v1.2.3 From d37bcc1d5a781687384fbe632a1850ab218ccbfd Mon Sep 17 00:00:00 2001 From: Arthur Neves Date: Mon, 28 Jul 2014 13:25:19 -0400 Subject: savepoint_name should return nil for non-savepoint transactions Also add test to assets the savepoint name --- .../lib/active_record/connection_adapters/abstract/transaction.rb | 7 ++++++- .../lib/active_record/connection_adapters/abstract_adapter.rb | 4 +--- 2 files changed, 7 insertions(+), 4 deletions(-) (limited to 'activerecord/lib/active_record/connection_adapters') diff --git a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb index 7618d6902d..54f873a2a2 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb @@ -69,6 +69,10 @@ module ActiveRecord def state @state end + + def savepoint_name + nil + end end class TransactionState @@ -233,7 +237,8 @@ module ActiveRecord super - @savepoint_name = "active_record_#{number}" + # Savepoint name only counts the Savepoint transactions, so we need to subtract 1 + @savepoint_name = "active_record_#{number - 1}" connection.create_savepoint(@savepoint_name) end diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index c31726437f..99c728814a 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -358,9 +358,7 @@ module ActiveRecord end def current_savepoint_name - if current_transaction.is_a? SavepointTransaction - current_transaction.savepoint_name - end + current_transaction.savepoint_name end # Check the connection back in to the connection pool -- cgit v1.2.3 From f5cec76ea8de1b9d076d0b1138ab8c2cabc0390d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Mon, 28 Jul 2014 15:04:31 -0300 Subject: Extract the transaction class to a local variable --- .../lib/active_record/connection_adapters/abstract/transaction.rb | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'activerecord/lib/active_record/connection_adapters') diff --git a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb index 54f873a2a2..3a266512a9 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb @@ -7,12 +7,8 @@ module ActiveRecord end def begin_transaction(options = {}) - transaction = - if @stack.empty? - RealTransaction.new(@connection, current_transaction, options) - else - SavepointTransaction.new(@connection, current_transaction, options) - end + transaction_class = @stack.empty? ? RealTransaction : SavepointTransaction + transaction = transaction_class.new(@connection, current_transaction, options) @stack.push(transaction) transaction -- cgit v1.2.3 From 2a7cf24cb7aab28f483a6772b608e2868a9030ba Mon Sep 17 00:00:00 2001 From: Ponomarev Nikolay Date: Tue, 29 Jul 2014 02:51:08 +0400 Subject: remove blank lines in the start of the ActiveRecord files --- activerecord/lib/active_record/connection_adapters/schema_cache.rb | 1 - 1 file changed, 1 deletion(-) (limited to 'activerecord/lib/active_record/connection_adapters') diff --git a/activerecord/lib/active_record/connection_adapters/schema_cache.rb b/activerecord/lib/active_record/connection_adapters/schema_cache.rb index 4d8afcf16a..3116bed596 100644 --- a/activerecord/lib/active_record/connection_adapters/schema_cache.rb +++ b/activerecord/lib/active_record/connection_adapters/schema_cache.rb @@ -1,4 +1,3 @@ - module ActiveRecord module ConnectionAdapters class SchemaCache -- cgit v1.2.3 From dd9829a9ea460ddcfc8d954f2b95161b52fff6e7 Mon Sep 17 00:00:00 2001 From: Arthur Neves Date: Tue, 29 Jul 2014 10:09:06 -0400 Subject: Remove @state.parent assignment on commit This piece of code was introduced on 67d8bb963d5d51fc644d6b1ca20164efb4cee6d7 , which was calling `committed?` in the `transaction_state` before calling the `committed!` method. However on 7386ffc781fca07a0c656db49fdb54678caef809, the `committed?` check was removed and replaced by a `finalized?`, which only checks if the state is not nil. Thus we can remove that line. --- .../lib/active_record/connection_adapters/abstract/transaction.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'activerecord/lib/active_record/connection_adapters') diff --git a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb index 3a266512a9..33cc22425d 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb @@ -72,7 +72,7 @@ module ActiveRecord end class TransactionState - attr_accessor :parent + attr_reader :parent VALID_STATES = Set.new([:committed, :rolledback, nil]) @@ -245,7 +245,6 @@ module ActiveRecord def perform_commit @state.set_state(:committed) - @state.parent = parent.state connection.release_savepoint(@savepoint_name) end end -- cgit v1.2.3 From 38be6335fe69966a65158639d2e00ac41618a67f Mon Sep 17 00:00:00 2001 From: Jack Danger Canty Date: Wed, 30 Jul 2014 21:57:30 -0700 Subject: Clarify how the ConnectionHandler works This makes the implicit description of how connection pooling works a little more explicit. It converts the examples of a model hierarchy into actual Ruby code and demonstrates how the key structure of the database.yml relates to the `establish_connection` method. --- .../abstract/connection_pool.rb | 51 +++++++++++++++------- 1 file changed, 36 insertions(+), 15 deletions(-) (limited to 'activerecord/lib/active_record/connection_adapters') diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb index cb75070e3a..a5fa9d6adc 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb @@ -462,23 +462,44 @@ module ActiveRecord # # For example, suppose that you have 5 models, with the following hierarchy: # - # | - # +-- Book - # | | - # | +-- ScaryBook - # | +-- GoodBook - # +-- Author - # +-- BankAccount + # class Author < ActiveRecord::Base + # end # - # Suppose that Book is to connect to a separate database (i.e. one other - # than the default database). Then Book, ScaryBook and GoodBook will all use - # the same connection pool. Likewise, Author and BankAccount will use the - # same connection pool. However, the connection pool used by Author/BankAccount - # is not the same as the one used by Book/ScaryBook/GoodBook. + # class BankAccount < ActiveRecord::Base + # end # - # Normally there is only a single ConnectionHandler instance, accessible via - # ActiveRecord::Base.connection_handler. Active Record models use this to - # determine the connection pool that they should use. + # class Book < ActiveRecord::Base + # establish_connection "library_db" + # end + # + # class ScaryBook < Book + # end + # + # class GoodBook < Book + # end + # + # And a database.yml that looked like this: + # + # development: + # database: my_application + # host: localhost + # + # library_db: + # database: library + # host: some.library.org + # + # Your primary database in the development environment is "my_application" + # but the Book model connects to a separate database called "library_db" + # (this can even be a database on a different machine). + # + # Book, ScaryBook and GoodBook will all use the same connection pool to + # "library_db" while Author, BankAccount, and any other models you create + # will use the default connection pool to "my_application". + # + # The various connection pools are managed by a single instance of + # ConnectionHandler accessible via ActiveRecord::Base.connection_handler. + # All Active Record models use this handler to determine the connection pool that they + # should use. class ConnectionHandler def initialize # These caches are keyed by klass.name, NOT klass. Keying them by klass -- cgit v1.2.3 From 91e4b65093f0c485507dadcb018c1f0d2661ba89 Mon Sep 17 00:00:00 2001 From: Arthur Neves Date: Mon, 28 Jul 2014 14:26:27 -0400 Subject: Remove being/number methods from transaction class --- .../connection_adapters/abstract/transaction.rb | 35 +++++----------------- 1 file changed, 8 insertions(+), 27 deletions(-) (limited to 'activerecord/lib/active_record/connection_adapters') diff --git a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb index 33cc22425d..1691daf861 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb @@ -8,7 +8,7 @@ module ActiveRecord def begin_transaction(options = {}) transaction_class = @stack.empty? ? RealTransaction : SavepointTransaction - transaction = transaction_class.new(@connection, current_transaction, options) + transaction = transaction_class.new(@connection, current_transaction, "active_record_#{@stack.size}", options) @stack.push(transaction) transaction @@ -102,14 +102,6 @@ module ActiveRecord end class ClosedTransaction < Transaction #:nodoc: - def number - 0 - end - - def begin(options = {}) - RealTransaction.new(connection, self, options) - end - def closed? true end @@ -144,14 +136,6 @@ module ActiveRecord @joinable end - def number - parent.number + 1 - end - - def begin(options = {}) - SavepointTransaction.new(connection, self, options) - end - def rollback perform_rollback parent @@ -202,8 +186,8 @@ module ActiveRecord end class RealTransaction < OpenTransaction #:nodoc: - def initialize(connection, parent, options = {}) - super + def initialize(connection, parent, _, options = {}) + super(connection, parent, options) if options[:isolation] connection.begin_isolated_db_transaction(options[:isolation]) @@ -226,26 +210,23 @@ module ActiveRecord class SavepointTransaction < OpenTransaction #:nodoc: attr_reader :savepoint_name - def initialize(connection, parent, options = {}) + def initialize(connection, parent, savepoint_name, options = {}) if options[:isolation] raise ActiveRecord::TransactionIsolationError, "cannot set transaction isolation in a nested transaction" end - super - - # Savepoint name only counts the Savepoint transactions, so we need to subtract 1 - @savepoint_name = "active_record_#{number - 1}" - connection.create_savepoint(@savepoint_name) + super(connection, parent, options) + connection.create_savepoint(@savepoint_name = savepoint_name) end def perform_rollback - connection.rollback_to_savepoint(@savepoint_name) + connection.rollback_to_savepoint(savepoint_name) rollback_records end def perform_commit @state.set_state(:committed) - connection.release_savepoint(@savepoint_name) + connection.release_savepoint(savepoint_name) end end end -- cgit v1.2.3 From dac9c92e3096ae196d7ea4b58e7141f4a36007ea Mon Sep 17 00:00:00 2001 From: Arthur Neves Date: Mon, 28 Jul 2014 13:07:47 -0400 Subject: Remove parent on Transaction object --- .../connection_adapters/abstract/transaction.rb | 25 ++++++++-------------- 1 file changed, 9 insertions(+), 16 deletions(-) (limited to 'activerecord/lib/active_record/connection_adapters') diff --git a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb index 1691daf861..17d57a7519 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb @@ -8,7 +8,7 @@ module ActiveRecord def begin_transaction(options = {}) transaction_class = @stack.empty? ? RealTransaction : SavepointTransaction - transaction = transaction_class.new(@connection, current_transaction, "active_record_#{@stack.size}", options) + transaction = transaction_class.new(@connection, "active_record_#{@stack.size}", options) @stack.push(transaction) transaction @@ -55,17 +55,13 @@ module ActiveRecord end class Transaction #:nodoc: - attr_reader :connection + attr_reader :connection, :state def initialize(connection) @connection = connection @state = TransactionState.new end - def state - @state - end - def savepoint_name nil end @@ -120,13 +116,12 @@ module ActiveRecord end class OpenTransaction < Transaction #:nodoc: - attr_reader :parent, :records + attr_reader :records attr_writer :joinable - def initialize(connection, parent, options = {}) + def initialize(connection, options = {}) super connection - @parent = parent @records = [] @joinable = options.fetch(:joinable, true) end @@ -138,12 +133,10 @@ module ActiveRecord def rollback perform_rollback - parent end def commit perform_commit - parent end def add_record(record) @@ -158,7 +151,7 @@ module ActiveRecord @state.set_state(:rolledback) records.uniq.each do |record| begin - record.rolledback!(parent.closed?) + record.rolledback!(self.is_a?(RealTransaction)) rescue => e record.logger.error(e) if record.respond_to?(:logger) && record.logger end @@ -186,8 +179,8 @@ module ActiveRecord end class RealTransaction < OpenTransaction #:nodoc: - def initialize(connection, parent, _, options = {}) - super(connection, parent, options) + def initialize(connection, _, options = {}) + super(connection, options) if options[:isolation] connection.begin_isolated_db_transaction(options[:isolation]) @@ -210,12 +203,12 @@ module ActiveRecord class SavepointTransaction < OpenTransaction #:nodoc: attr_reader :savepoint_name - def initialize(connection, parent, savepoint_name, options = {}) + def initialize(connection, savepoint_name, options = {}) if options[:isolation] raise ActiveRecord::TransactionIsolationError, "cannot set transaction isolation in a nested transaction" end - super(connection, parent, options) + super(connection, options) connection.create_savepoint(@savepoint_name = savepoint_name) end -- cgit v1.2.3 From 4140797967a8f50eccaa70614c01531dab6a90f4 Mon Sep 17 00:00:00 2001 From: Arthur Neves Date: Wed, 30 Jul 2014 14:32:56 -0400 Subject: Make ClosedTransaction a null object --- .../connection_adapters/abstract/transaction.rb | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) (limited to 'activerecord/lib/active_record/connection_adapters') diff --git a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb index 17d57a7519..ff7f922d7f 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb @@ -50,7 +50,7 @@ module ActiveRecord private def closed_transaction - @closed_transaction ||= ClosedTransaction.new(@connection) + @closed_transaction ||= ClosedTransaction.new end end @@ -98,21 +98,12 @@ module ActiveRecord end class ClosedTransaction < Transaction #:nodoc: - def closed? - true - end - - def open? - false - end - - def joinable? - false - end - + def initialize; super(nil); end + def closed?; true; end + def open?; false; end + def joinable?; false; end # This is a noop when there are no open transactions - def add_record(record) - end + def add_record(record); end end class OpenTransaction < Transaction #:nodoc: @@ -126,7 +117,6 @@ module ActiveRecord @joinable = options.fetch(:joinable, true) end - def joinable? @joinable end -- cgit v1.2.3 From 62c75f4eacf6466a3bd1b22f97cda7ab7b597064 Mon Sep 17 00:00:00 2001 From: Arthur Neves Date: Thu, 31 Jul 2014 16:25:51 -0400 Subject: Move TransactionManager to bottom of class --- .../connection_adapters/abstract/transaction.rb | 134 ++++++++++----------- 1 file changed, 67 insertions(+), 67 deletions(-) (limited to 'activerecord/lib/active_record/connection_adapters') diff --git a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb index ff7f922d7f..e6a53b2122 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb @@ -1,72 +1,5 @@ module ActiveRecord module ConnectionAdapters - class TransactionManager #:nodoc: - def initialize(connection) - @stack = [] - @connection = connection - end - - def begin_transaction(options = {}) - transaction_class = @stack.empty? ? RealTransaction : SavepointTransaction - transaction = transaction_class.new(@connection, "active_record_#{@stack.size}", options) - - @stack.push(transaction) - transaction - end - - def commit_transaction - @stack.pop.commit - end - - def rollback_transaction - @stack.pop.rollback - end - - def within_new_transaction(options = {}) - transaction = begin_transaction options - yield - rescue Exception => error - transaction.rollback if transaction - raise - ensure - begin - transaction.commit unless error - rescue Exception - transaction.rollback - raise - ensure - @stack.pop if transaction - end - end - - def open_transactions - @stack.size - end - - def current_transaction - @stack.last || closed_transaction - end - - private - - def closed_transaction - @closed_transaction ||= ClosedTransaction.new - end - end - - class Transaction #:nodoc: - attr_reader :connection, :state - - def initialize(connection) - @connection = connection - @state = TransactionState.new - end - - def savepoint_name - nil - end - end - class TransactionState attr_reader :parent @@ -97,6 +30,19 @@ module ActiveRecord end end + class Transaction #:nodoc: + attr_reader :connection, :state + + def initialize(connection) + @connection = connection + @state = TransactionState.new + end + + def savepoint_name + nil + end + end + class ClosedTransaction < Transaction #:nodoc: def initialize; super(nil); end def closed?; true; end @@ -212,5 +158,59 @@ module ActiveRecord connection.release_savepoint(savepoint_name) end end + + class TransactionManager #:nodoc: + def initialize(connection) + @stack = [] + @connection = connection + end + + def begin_transaction(options = {}) + transaction_class = @stack.empty? ? RealTransaction : SavepointTransaction + transaction = transaction_class.new(@connection, "active_record_#{@stack.size}", options) + + @stack.push(transaction) + transaction + end + + def commit_transaction + @stack.pop.commit + end + + def rollback_transaction + @stack.pop.rollback + end + + def within_new_transaction(options = {}) + transaction = begin_transaction options + yield + rescue Exception => error + transaction.rollback if transaction + raise + ensure + begin + transaction.commit unless error + rescue Exception + transaction.rollback + raise + ensure + @stack.pop if transaction + end + end + + def open_transactions + @stack.size + end + + def current_transaction + @stack.last || closed_transaction + end + + private + + def closed_transaction + @closed_transaction ||= ClosedTransaction.new + end + end end end -- cgit v1.2.3 From 057c23715434adcab9b12f987d615979d1f57549 Mon Sep 17 00:00:00 2001 From: Arthur Neves Date: Thu, 31 Jul 2014 16:31:33 -0400 Subject: Replace ClosedTransaction with NullTransaction --- .../active_record/connection_adapters/abstract/transaction.rb | 11 ++++------- .../lib/active_record/connection_adapters/abstract_adapter.rb | 2 +- 2 files changed, 5 insertions(+), 8 deletions(-) (limited to 'activerecord/lib/active_record/connection_adapters') diff --git a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb index e6a53b2122..46aaaae2ec 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb @@ -43,8 +43,8 @@ module ActiveRecord end end - class ClosedTransaction < Transaction #:nodoc: - def initialize; super(nil); end + class NullTransaction < Transaction #:nodoc: + def initialize; end def closed?; true; end def open?; false; end def joinable?; false; end @@ -203,14 +203,11 @@ module ActiveRecord end def current_transaction - @stack.last || closed_transaction + @stack.last || NULL_TRANSACTION end private - - def closed_transaction - @closed_transaction ||= ClosedTransaction.new - end + NULL_TRANSACTION = NullTransaction.new end end end diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index 99c728814a..a1b6671664 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -46,7 +46,7 @@ module ActiveRecord autoload_at 'active_record/connection_adapters/abstract/transaction' do autoload :TransactionManager - autoload :ClosedTransaction + autoload :NullTransaction autoload :RealTransaction autoload :SavepointTransaction autoload :TransactionState -- cgit v1.2.3 From d44702ee45219153c5e56da0a06ffe6ab5d14518 Mon Sep 17 00:00:00 2001 From: Carlos Antonio da Silva Date: Tue, 5 Aug 2014 08:22:11 -0300 Subject: Remove unused text? predicate method and delegation The method has been removed in 09206716f8695f6b8467f15c1befa5a4c3c10978 (PR #16074), but the delegation was apparently missed, and one instance of the method was added back with the addition of OID::Xml in 336be2bdf7dfa1b31879d0ab27e5f3101b351923 (PR #16072), so we can safely rm both. --- activerecord/lib/active_record/connection_adapters/column.rb | 2 +- .../lib/active_record/connection_adapters/postgresql/oid/xml.rb | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) (limited to 'activerecord/lib/active_record/connection_adapters') diff --git a/activerecord/lib/active_record/connection_adapters/column.rb b/activerecord/lib/active_record/connection_adapters/column.rb index 1f1e2c46f4..5f9cc6edd0 100644 --- a/activerecord/lib/active_record/connection_adapters/column.rb +++ b/activerecord/lib/active_record/connection_adapters/column.rb @@ -16,7 +16,7 @@ module ActiveRecord attr_reader :name, :cast_type, :null, :sql_type, :default, :default_function delegate :type, :precision, :scale, :limit, :klass, :accessor, - :text?, :number?, :binary?, :changed?, + :number?, :binary?, :changed?, :type_cast_from_user, :type_cast_from_database, :type_cast_for_database, :type_cast_for_schema, to: :cast_type diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/xml.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/xml.rb index 7323f12763..334af7c598 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/xml.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/xml.rb @@ -7,10 +7,6 @@ module ActiveRecord :xml end - def text? - false - end - def type_cast_for_database(value) return unless value Data.new(super) -- cgit v1.2.3 From 8298d3adb72eb17186dc0b155afec308071a3900 Mon Sep 17 00:00:00 2001 From: Arthur Neves Date: Thu, 31 Jul 2014 16:44:55 -0400 Subject: Cleanup Transaction inheritance. Transaction class doesnt need to encapsulate the transaction state using inheritance. This removes all Transaction subclasses, and let the Transaction object controls different actions based on its own state. Basically the only actions would behave differently are `being`,`commit`,`rollback` as they could act in a savepoint or in a real transaction. --- .../connection_adapters/abstract/transaction.rb | 124 +++++++++------------ 1 file changed, 54 insertions(+), 70 deletions(-) (limited to 'activerecord/lib/active_record/connection_adapters') diff --git a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb index 46aaaae2ec..4a7f2aaca8 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb @@ -30,49 +30,24 @@ module ActiveRecord end end - class Transaction #:nodoc: - attr_reader :connection, :state - - def initialize(connection) - @connection = connection - @state = TransactionState.new - end - - def savepoint_name - nil - end - end - - class NullTransaction < Transaction #:nodoc: + class NullTransaction #:nodoc: def initialize; end def closed?; true; end def open?; false; end def joinable?; false; end - # This is a noop when there are no open transactions def add_record(record); end end - class OpenTransaction < Transaction #:nodoc: - attr_reader :records - attr_writer :joinable - - def initialize(connection, options = {}) - super connection - - @records = [] - @joinable = options.fetch(:joinable, true) - end - - def joinable? - @joinable - end + class Transaction #:nodoc: - def rollback - perform_rollback - end + attr_reader :connection, :state, :records, :savepoint_name + attr_writer :joinable - def commit - perform_commit + def initialize(connection, options) + @connection = connection + @state = TransactionState.new + @records = [] + @joinable = options.fetch(:joinable, true) end def add_record(record) @@ -83,19 +58,25 @@ module ActiveRecord end end - def rollback_records + def rollback @state.set_state(:rolledback) + end + + def rollback_records records.uniq.each do |record| begin - record.rolledback!(self.is_a?(RealTransaction)) + record.rolledback! full_rollback? rescue => e record.logger.error(e) if record.respond_to?(:logger) && record.logger end end end - def commit_records + def commit @state.set_state(:committed) + end + + def commit_records records.uniq.each do |record| begin record.committed! @@ -105,57 +86,57 @@ module ActiveRecord end end - def closed? - false - end - - def open? - true - end + def full_rollback?; true; end + def joinable?; @joinable; end + def closed?; false; end + def open?; !closed?; end end - class RealTransaction < OpenTransaction #:nodoc: - def initialize(connection, _, options = {}) - super(connection, options) + class SavepointTransaction < Transaction + def initialize(connection, savepoint_name, options) + super(connection, options) if options[:isolation] - connection.begin_isolated_db_transaction(options[:isolation]) - else - connection.begin_db_transaction + raise ActiveRecord::TransactionIsolationError, "cannot set transaction isolation in a nested transaction" end + connection.create_savepoint(@savepoint_name = savepoint_name) end - def perform_rollback - connection.rollback_db_transaction + def rollback + super + connection.rollback_to_savepoint(savepoint_name) rollback_records end - def perform_commit - connection.commit_db_transaction - commit_records + def commit + super + connection.release_savepoint(savepoint_name) end + + def full_rollback?; false; end end - class SavepointTransaction < OpenTransaction #:nodoc: - attr_reader :savepoint_name + class RealTransaction < Transaction - def initialize(connection, savepoint_name, options = {}) + def initialize(connection, options) + super if options[:isolation] - raise ActiveRecord::TransactionIsolationError, "cannot set transaction isolation in a nested transaction" + connection.begin_isolated_db_transaction(options[:isolation]) + else + connection.begin_db_transaction end - - super(connection, options) - connection.create_savepoint(@savepoint_name = savepoint_name) end - def perform_rollback - connection.rollback_to_savepoint(savepoint_name) + def rollback + super + connection.rollback_db_transaction rollback_records end - def perform_commit - @state.set_state(:committed) - connection.release_savepoint(savepoint_name) + def commit + super + connection.commit_db_transaction + commit_records end end @@ -166,9 +147,12 @@ module ActiveRecord end def begin_transaction(options = {}) - transaction_class = @stack.empty? ? RealTransaction : SavepointTransaction - transaction = transaction_class.new(@connection, "active_record_#{@stack.size}", options) - + transaction = + if @stack.empty? + RealTransaction.new(@connection, options) + else + SavepointTransaction.new(@connection, "active_record_#{@stack.size}", options) + end @stack.push(transaction) transaction end -- cgit v1.2.3 From 8aead812baaacb962c15d0ab27bdaf4c388b7d59 Mon Sep 17 00:00:00 2001 From: tsukasaoishi Date: Thu, 7 Aug 2014 22:41:23 +0900 Subject: Tables existence check query is executed in large quantities When Rails starts, tables existence check query is executed number of models. In case of mysql, SHOW TABLES LIKE 'table1'; SHOW TABLES LIKE 'table2'; SHOW TABLES LIKE 'table3'; ... SHOW TABLES LIKE 'table999'; Add process to get the names of all tables by one query. --- activerecord/lib/active_record/connection_adapters/schema_cache.rb | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'activerecord/lib/active_record/connection_adapters') diff --git a/activerecord/lib/active_record/connection_adapters/schema_cache.rb b/activerecord/lib/active_record/connection_adapters/schema_cache.rb index 3116bed596..726810535d 100644 --- a/activerecord/lib/active_record/connection_adapters/schema_cache.rb +++ b/activerecord/lib/active_record/connection_adapters/schema_cache.rb @@ -19,6 +19,7 @@ module ActiveRecord # A cached lookup for table existence. def table_exists?(name) + prepare_tables if @tables.blank? return @tables[name] if @tables.key? name @tables[name] = connection.table_exists?(name) @@ -82,6 +83,12 @@ module ActiveRecord def marshal_load(array) @version, @columns, @columns_hash, @primary_keys, @tables = array end + + private + + def prepare_tables + connection.tables.each { |table| @tables[table] = true } + end end end end -- cgit v1.2.3 From 4e83815ce991efce2b84aa570f6673227ff0bb0d Mon Sep 17 00:00:00 2001 From: tsukasaoishi Date: Fri, 8 Aug 2014 01:03:09 +0900 Subject: change to empty? from blank? --- activerecord/lib/active_record/connection_adapters/schema_cache.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record/connection_adapters') diff --git a/activerecord/lib/active_record/connection_adapters/schema_cache.rb b/activerecord/lib/active_record/connection_adapters/schema_cache.rb index 726810535d..a10ce330c7 100644 --- a/activerecord/lib/active_record/connection_adapters/schema_cache.rb +++ b/activerecord/lib/active_record/connection_adapters/schema_cache.rb @@ -19,7 +19,7 @@ module ActiveRecord # A cached lookup for table existence. def table_exists?(name) - prepare_tables if @tables.blank? + prepare_tables if @tables.empty? return @tables[name] if @tables.key? name @tables[name] = connection.table_exists?(name) -- cgit v1.2.3 From 399f5f6346829210b4d58ed5f7c6d4d73ef67737 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 7 Aug 2014 12:10:31 -0700 Subject: use the uri parser so that newer version of Ruby work --- .../lib/active_record/connection_adapters/connection_specification.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record/connection_adapters') diff --git a/activerecord/lib/active_record/connection_adapters/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/connection_specification.rb index 5693031053..d28a54b8f9 100644 --- a/activerecord/lib/active_record/connection_adapters/connection_specification.rb +++ b/activerecord/lib/active_record/connection_adapters/connection_specification.rb @@ -32,7 +32,7 @@ module ActiveRecord # } def initialize(url) raise "Database URL cannot be empty" if url.blank? - @uri = URI.parse(url) + @uri = uri_parser.parse(url) @adapter = @uri.scheme.gsub('-', '_') @adapter = "postgresql" if @adapter == "postgres" -- cgit v1.2.3