aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/lib/active_record')
-rw-r--r--activerecord/lib/active_record/associations/collection_association.rb15
-rw-r--r--activerecord/lib/active_record/associations/has_many_through_association.rb4
-rw-r--r--activerecord/lib/active_record/attribute_methods/query.rb2
-rw-r--r--activerecord/lib/active_record/attribute_methods/serialization.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb14
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb7
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb8
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb6
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb8
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb20
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb10
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb10
-rw-r--r--activerecord/lib/active_record/core.rb2
-rw-r--r--activerecord/lib/active_record/explain_subscriber.rb7
-rw-r--r--activerecord/lib/active_record/integration.rb23
-rw-r--r--activerecord/lib/active_record/log_subscriber.rb4
-rw-r--r--activerecord/lib/active_record/migration.rb1
-rw-r--r--activerecord/lib/active_record/persistence.rb7
-rw-r--r--activerecord/lib/active_record/query_cache.rb10
-rw-r--r--activerecord/lib/active_record/reflection.rb4
-rw-r--r--activerecord/lib/active_record/relation/calculations.rb8
-rw-r--r--activerecord/lib/active_record/relation/record_fetch_warning.rb6
-rw-r--r--activerecord/lib/active_record/schema_dumper.rb39
-rw-r--r--activerecord/lib/active_record/type.rb4
-rw-r--r--activerecord/lib/active_record/type/helpers.rb5
-rw-r--r--activerecord/lib/active_record/type/internal/abstract_json.rb10
-rw-r--r--activerecord/lib/active_record/type/serialized.rb4
-rw-r--r--activerecord/lib/active_record/type/value.rb5
28 files changed, 138 insertions, 107 deletions
diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb
index e5b3af8252..278c95e27b 100644
--- a/activerecord/lib/active_record/associations/collection_association.rb
+++ b/activerecord/lib/active_record/associations/collection_association.rb
@@ -309,15 +309,12 @@ module ActiveRecord
def replace_on_target(record, index, skip_callbacks)
callback(:before_add, record) unless skip_callbacks
- was_loaded = loaded?
yield(record) if block_given?
- unless !was_loaded && loaded?
- if index
- @target[index] = record
- else
- @target << record
- end
+ if index
+ @target[index] = record
+ else
+ append_record(record)
end
callback(:after_add, record) unless skip_callbacks
@@ -514,6 +511,10 @@ module ActiveRecord
load_target.select { |r| ids.include?(r.id.to_s) }
end
end
+
+ def append_record(record)
+ @target << record unless @target.include?(record)
+ end
end
end
end
diff --git a/activerecord/lib/active_record/associations/has_many_through_association.rb b/activerecord/lib/active_record/associations/has_many_through_association.rb
index d258eac0ed..1f264d325a 100644
--- a/activerecord/lib/active_record/associations/has_many_through_association.rb
+++ b/activerecord/lib/active_record/associations/has_many_through_association.rb
@@ -203,6 +203,10 @@ module ActiveRecord
def invertible_for?(record)
false
end
+
+ def append_record(record)
+ @target << record
+ end
end
end
end
diff --git a/activerecord/lib/active_record/attribute_methods/query.rb b/activerecord/lib/active_record/attribute_methods/query.rb
index 10498f4322..05f0e974b6 100644
--- a/activerecord/lib/active_record/attribute_methods/query.rb
+++ b/activerecord/lib/active_record/attribute_methods/query.rb
@@ -19,7 +19,7 @@ module ActiveRecord
if Numeric === value || value !~ /[^0-9]/
!value.to_i.zero?
else
- return false if ActiveModel::Type::Boolean::FALSE_VALUES.include?(value)
+ return false if ActiveRecord::Type::Boolean::FALSE_VALUES.include?(value)
!value.blank?
end
elsif value.respond_to?(:zero?)
diff --git a/activerecord/lib/active_record/attribute_methods/serialization.rb b/activerecord/lib/active_record/attribute_methods/serialization.rb
index c70178cd2c..945192fe04 100644
--- a/activerecord/lib/active_record/attribute_methods/serialization.rb
+++ b/activerecord/lib/active_record/attribute_methods/serialization.rb
@@ -26,7 +26,7 @@ module ActiveRecord
# ==== Parameters
#
# * +attr_name+ - The field name that should be serialized.
- # * +class_name_or_coder+ - Optional, a coder object, which responds to `.load` / `.dump`
+ # * +class_name_or_coder+ - Optional, a coder object, which responds to +.load+ and +.dump+
# or a class name that the object type should be equal to.
#
# ==== Example
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
index 6ca53c72ce..2f8a89e88e 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
@@ -65,7 +65,7 @@ module ActiveRecord
if @query_cache_enabled && !locked?(arel)
arel, binds = binds_from_relation arel, binds
sql = to_sql(arel, binds)
- cache_sql(sql, binds) { super(sql, name, binds, preparable: preparable) }
+ cache_sql(sql, name, binds) { super(sql, name, binds, preparable: preparable) }
else
super
end
@@ -73,11 +73,17 @@ module ActiveRecord
private
- def cache_sql(sql, binds)
+ def cache_sql(sql, name, binds)
result =
if @query_cache[sql].key?(binds)
- ActiveSupport::Notifications.instrument("sql.active_record",
- sql: sql, binds: binds, name: "CACHE", connection_id: object_id)
+ ActiveSupport::Notifications.instrument(
+ "sql.active_record",
+ sql: sql,
+ binds: binds,
+ name: name,
+ connection_id: object_id,
+ cached: true,
+ )
@query_cache[sql][binds]
else
@query_cache[sql][binds] = yield
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb
index 8bb7362c2e..06c89ca072 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb
@@ -7,10 +7,7 @@ module ActiveRecord
# Adapter level by over-writing this code inside the database specific adapters
module ColumnDumper
def column_spec(column)
- spec = Hash[prepare_column_options(column).map { |k, v| [k, "#{k}: #{v}"] }]
- spec[:name] = column.name.inspect
- spec[:type] = schema_type(column).to_s
- spec
+ [schema_type(column), prepare_column_options(column)]
end
def column_spec_for_primary_key(column)
@@ -53,7 +50,7 @@ module ActiveRecord
# Lists the valid migration options
def migration_keys
- [:name, :limit, :precision, :scale, :default, :null, :collation, :comment]
+ [:limit, :precision, :scale, :default, :null, :collation, :comment]
end
private
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 29520ed9c8..1df20a0c56 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -120,7 +120,7 @@ module ActiveRecord
checks = []
checks << lambda { |c| c.name == column_name }
checks << lambda { |c| c.type == type } if type
- (migration_keys - [:name]).each do |attr|
+ migration_keys.each do |attr|
checks << lambda { |c| c.send(attr) == options[attr] } if options.key?(attr)
end
@@ -284,10 +284,10 @@ module ActiveRecord
end
if supports_comments? && !supports_comments_in_create?
- change_table_comment(table_name, comment) if comment
+ change_table_comment(table_name, comment) if comment.present?
td.columns.each do |column|
- change_column_comment(table_name, column.name, column.comment) if column.comment
+ change_column_comment(table_name, column.name, column.comment) if column.comment.present?
end
end
@@ -1192,7 +1192,7 @@ module ActiveRecord
def quoted_columns_for_index(column_names, **options)
return [column_names] if column_names.is_a?(String)
- quoted_columns = Hash[column_names.map { |name| [name, quote_column_name(name).dup] }]
+ quoted_columns = Hash[column_names.map { |name| [name.to_sym, quote_column_name(name).dup] }]
add_options_for_index_columns(quoted_columns, options).values
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 be8511f119..e7bd0e7c12 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -384,11 +384,11 @@ module ActiveRecord
mysql_index_type = row[:Index_type].downcase.to_sym
index_type = INDEX_TYPES.include?(mysql_index_type) ? mysql_index_type : nil
index_using = INDEX_USINGS.include?(mysql_index_type) ? mysql_index_type : nil
- indexes << IndexDefinition.new(row[:Table], row[:Key_name], row[:Non_unique].to_i == 0, [], [], nil, nil, index_type, index_using, row[:Index_comment].presence)
+ indexes << IndexDefinition.new(row[:Table], row[:Key_name], row[:Non_unique].to_i == 0, [], {}, nil, nil, index_type, index_using, row[:Index_comment].presence)
end
indexes.last.columns << row[:Column_name]
- indexes.last.lengths << row[:Sub_part]
+ indexes.last.lengths.merge!(row[:Column_name] => row[:Sub_part].to_i) if row[:Sub_part]
end
end
@@ -509,7 +509,7 @@ module ActiveRecord
end
def add_sql_comment!(sql, comment) # :nodoc:
- sql << " COMMENT #{quote(comment)}" if comment
+ sql << " COMMENT #{quote(comment)}" if comment.present?
sql
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb
index 7414eba6c5..092543259f 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb
@@ -89,10 +89,10 @@ module ActiveRecord
end
end
- # Executes an SQL statement, returning a PGresult object on success
- # or raising a PGError exception otherwise.
- # Note: the PGresult object is manually memory managed; if you don't
- # need it specifically, you many want consider the exec_query wrapper.
+ # Executes an SQL statement, returning a PG::Result object on success
+ # or raising a PG::Error exception otherwise.
+ # Note: the PG::Result object is manually memory managed; if you don't
+ # need it specifically, you may want consider the <tt>exec_query</tt> wrapper.
def execute(sql, name = nil)
log(sql, name) do
@connection.async_exec(sql)
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 29a77580f5..69f797da3a 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
@@ -86,7 +86,7 @@ module ActiveRecord
SELECT c.relname
FROM pg_class c
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
- WHERE c.relkind IN ('r', 'v','m') -- (r)elation/table, (v)iew, (m)aterialized view
+ WHERE c.relkind IN ('r','v','m') -- (r)elation/table, (v)iew, (m)aterialized view
AND n.nspname = ANY (current_schemas(false))
SQL
end
@@ -108,13 +108,13 @@ module ActiveRecord
name = Utils.extract_schema_qualified_name(name.to_s)
return false unless name.identifier
- select_value(<<-SQL, "SCHEMA").to_i > 0
- SELECT COUNT(*)
+ select_values(<<-SQL, "SCHEMA").any?
+ SELECT c.relname
FROM pg_class c
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relkind IN ('r','v','m') -- (r)elation/table, (v)iew, (m)aterialized view
- AND c.relname = '#{name.identifier}'
- AND n.nspname = #{name.schema ? "'#{name.schema}'" : 'ANY (current_schemas(false))'}
+ AND c.relname = #{quote(name.identifier)}
+ AND n.nspname = #{name.schema ? quote(name.schema) : "ANY (current_schemas(false))"}
SQL
end
@@ -137,8 +137,8 @@ module ActiveRecord
FROM pg_class c
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relkind IN ('v','m') -- (v)iew, (m)aterialized view
- AND c.relname = '#{name.identifier}'
- AND n.nspname = #{name.schema ? "'#{name.schema}'" : 'ANY (current_schemas(false))'}
+ AND c.relname = #{quote(name.identifier)}
+ AND n.nspname = #{name.schema ? quote(name.schema) : "ANY (current_schemas(false))"}
SQL
end
@@ -239,7 +239,9 @@ module ActiveRecord
end
def table_options(table_name) # :nodoc:
- { comment: table_comment(table_name) }
+ if comment = table_comment(table_name)
+ { comment: comment }
+ end
end
# Returns a comment stored in database for given table
@@ -439,7 +441,7 @@ module ActiveRecord
WITH pk_constraint AS (
SELECT conrelid, unnest(conkey) AS connum FROM pg_constraint
WHERE contype = 'p'
- AND conrelid = '#{quote_table_name(table_name)}'::regclass
+ AND conrelid = #{quote(quote_table_name(table_name))}::regclass
), cons AS (
SELECT conrelid, connum, row_number() OVER() AS rownum FROM pk_constraint
)
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index 8001c0dd53..acd1eeace7 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -750,7 +750,7 @@ module ActiveRecord
col_description(a.attrelid, a.attnum) AS comment
FROM pg_attribute a LEFT JOIN pg_attrdef d
ON a.attrelid = d.adrelid AND a.attnum = d.adnum
- WHERE a.attrelid = '#{quote_table_name(table_name)}'::regclass
+ WHERE a.attrelid = #{quote(quote_table_name(table_name))}::regclass
AND a.attnum > 0 AND NOT a.attisdropped
ORDER BY a.attnum
end_sql
@@ -771,10 +771,14 @@ module ActiveRecord
sql = <<-end_sql
SELECT exists(
SELECT * FROM pg_proc
+ WHERE proname = 'lower'
+ AND proargtypes = ARRAY[#{quote column.sql_type}::regtype]::oidvector
+ ) OR exists(
+ SELECT * FROM pg_proc
INNER JOIN pg_cast
- ON casttarget::text::oidvector = proargtypes
+ ON ARRAY[casttarget]::oidvector = proargtypes
WHERE proname = 'lower'
- AND castsource = '#{column.sql_type}'::regtype::oid
+ AND castsource = #{quote column.sql_type}::regtype
)
end_sql
execute_and_clear(sql, "SCHEMA", []) do |result|
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
index e2b534b511..ebeac425d0 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
@@ -530,10 +530,12 @@ module ActiveRecord
def table_structure_with_collation(table_name, basic_structure)
collation_hash = {}
- sql = "SELECT sql FROM
- (SELECT * FROM sqlite_master UNION ALL
- SELECT * FROM sqlite_temp_master)
- WHERE type='table' and name='#{ table_name }' \;"
+ sql = <<-SQL
+ SELECT sql FROM
+ (SELECT * FROM sqlite_master UNION ALL
+ SELECT * FROM sqlite_temp_master)
+ WHERE type = 'table' AND name = #{quote(table_name)}
+ SQL
# Result will have following sample string
# CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb
index 3465b68ac6..622df0cfc1 100644
--- a/activerecord/lib/active_record/core.rb
+++ b/activerecord/lib/active_record/core.rb
@@ -452,7 +452,7 @@ module ActiveRecord
# [ Person.find(1), Person.find(2), Person.find(3) ] & [ Person.find(1), Person.find(4) ] # => [ Person.find(1) ]
def hash
if id
- [self.class, id].hash
+ self.class.hash ^ self.id.hash
else
super
end
diff --git a/activerecord/lib/active_record/explain_subscriber.rb b/activerecord/lib/active_record/explain_subscriber.rb
index 706b57842f..abd8cfc8f2 100644
--- a/activerecord/lib/active_record/explain_subscriber.rb
+++ b/activerecord/lib/active_record/explain_subscriber.rb
@@ -18,10 +18,13 @@ module ActiveRecord
#
# On the other hand, we want to monitor the performance of our real database
# queries, not the performance of the access to the query cache.
- IGNORED_PAYLOADS = %w(SCHEMA EXPLAIN CACHE)
+ IGNORED_PAYLOADS = %w(SCHEMA EXPLAIN)
EXPLAINED_SQLS = /\A\s*(with|select|update|delete|insert)\b/i
def ignore_payload?(payload)
- payload[:exception] || IGNORED_PAYLOADS.include?(payload[:name]) || payload[:sql] !~ EXPLAINED_SQLS
+ payload[:exception] ||
+ payload[:cached] ||
+ IGNORED_PAYLOADS.include?(payload[:name]) ||
+ payload[:sql] !~ EXPLAINED_SQLS
end
ActiveSupport::Notifications.subscribe("sql.active_record", new)
diff --git a/activerecord/lib/active_record/integration.rb b/activerecord/lib/active_record/integration.rb
index e4c7a55541..3c54c6048d 100644
--- a/activerecord/lib/active_record/integration.rb
+++ b/activerecord/lib/active_record/integration.rb
@@ -53,18 +53,21 @@ module ActiveRecord
#
# Person.find(5).cache_key(:updated_at, :last_reviewed_at)
def cache_key(*timestamp_names)
- case
- when new_record?
+ if new_record?
"#{model_name.cache_key}/new"
- when timestamp_names.any?
- timestamp = max_updated_column_timestamp(timestamp_names)
- timestamp = timestamp.utc.to_s(cache_timestamp_format)
- "#{model_name.cache_key}/#{id}-#{timestamp}"
- when timestamp = max_updated_column_timestamp
- timestamp = timestamp.utc.to_s(cache_timestamp_format)
- "#{model_name.cache_key}/#{id}-#{timestamp}"
else
- "#{model_name.cache_key}/#{id}"
+ timestamp = if timestamp_names.any?
+ max_updated_column_timestamp(timestamp_names)
+ else
+ max_updated_column_timestamp
+ end
+
+ if timestamp
+ timestamp = timestamp.utc.to_s(cache_timestamp_format)
+ "#{model_name.cache_key}/#{id}-#{timestamp}"
+ else
+ "#{model_name.cache_key}/#{id}"
+ end
end
end
diff --git a/activerecord/lib/active_record/log_subscriber.rb b/activerecord/lib/active_record/log_subscriber.rb
index f31931316c..ad71c6cde8 100644
--- a/activerecord/lib/active_record/log_subscriber.rb
+++ b/activerecord/lib/active_record/log_subscriber.rb
@@ -26,15 +26,15 @@ module ActiveRecord
end
def sql(event)
- return unless logger.debug?
-
self.class.runtime += event.duration
+ return unless logger.debug?
payload = event.payload
return if IGNORE_PAYLOAD_NAMES.include?(payload[:name])
name = "#{payload[:name]} (#{event.duration.round(1)}ms)"
+ name = "CACHE #{name}" if payload[:cached]
sql = payload[:sql]
binds = nil
diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb
index 05568039d8..627e93b5b6 100644
--- a/activerecord/lib/active_record/migration.rb
+++ b/activerecord/lib/active_record/migration.rb
@@ -1,4 +1,5 @@
require "set"
+require "zlib"
require "active_support/core_ext/module/attribute_accessors"
require "active_support/core_ext/regexp"
diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb
index 978fb27cab..6933f3f9b8 100644
--- a/activerecord/lib/active_record/persistence.rb
+++ b/activerecord/lib/active_record/persistence.rb
@@ -107,7 +107,7 @@ module ActiveRecord
#
# By default, save always runs validations. If any of them fail the action
# is cancelled and #save returns +false+, and the record won't be saved. However, if you supply
- # validate: false, validations are bypassed altogether. See
+ # <tt>validate: false</tt>, validations are bypassed altogether. See
# ActiveRecord::Validations for more information.
#
# By default, #save also sets the +updated_at+/+updated_on+ attributes to
@@ -134,7 +134,7 @@ module ActiveRecord
#
# By default, #save! always runs validations. If any of them fail
# ActiveRecord::RecordInvalid gets raised, and the record won't be saved. However, if you supply
- # validate: false, validations are bypassed altogether. See
+ # <tt>validate: false</tt>, validations are bypassed altogether. See
# ActiveRecord::Validations for more information.
#
# By default, #save! also sets the +updated_at+/+updated_on+ attributes to
@@ -252,7 +252,8 @@ module ActiveRecord
name = name.to_s
verify_readonly_attribute(name)
public_send("#{name}=", value)
- save(validate: false) if changed?
+
+ changed? ? save(validate: false) : true
end
# Updates the attributes of the model from the passed-in hash and saves the
diff --git a/activerecord/lib/active_record/query_cache.rb b/activerecord/lib/active_record/query_cache.rb
index 387dd8e9bd..c45c8c1697 100644
--- a/activerecord/lib/active_record/query_cache.rb
+++ b/activerecord/lib/active_record/query_cache.rb
@@ -34,16 +34,14 @@ module ActiveRecord
def self.complete(enabled)
ActiveRecord::Base.connection.clear_query_cache
ActiveRecord::Base.connection.disable_query_cache! unless enabled
+
+ unless ActiveRecord::Base.connected? && ActiveRecord::Base.connection.transaction_open?
+ ActiveRecord::Base.clear_active_connections!
+ end
end
def self.install_executor_hooks(executor = ActiveSupport::Executor)
executor.register_hook(self)
-
- executor.to_complete do
- unless ActiveRecord::Base.connected? && ActiveRecord::Base.connection.transaction_open?
- ActiveRecord::Base.clear_active_connections!
- end
- end
end
end
end
diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb
index 9b692f55d2..57020e00c9 100644
--- a/activerecord/lib/active_record/reflection.rb
+++ b/activerecord/lib/active_record/reflection.rb
@@ -282,6 +282,10 @@ module ActiveRecord
end
def autosave=(autosave)
+ # autosave and inverse_of do not get along together nowadays. They may
+ # for example cause double saves. Thus, we disable this flag. If in the
+ # future those two flags are known to work well together, this could be
+ # removed.
@automatic_inverse_of = false
@options[:autosave] = autosave
parent_reflection = self.parent_reflection
diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb
index a887be8a20..e4676f79a5 100644
--- a/activerecord/lib/active_record/relation/calculations.rb
+++ b/activerecord/lib/active_record/relation/calculations.rb
@@ -112,10 +112,6 @@ module ActiveRecord
# ...
# end
def calculate(operation, column_name)
- if column_name.is_a?(Symbol) && attribute_alias?(column_name)
- column_name = attribute_alias(column_name)
- end
-
if has_include?(column_name)
relation = construct_relation_for_association_calculations
relation = relation.distinct if operation.to_s.downcase == "count"
@@ -215,8 +211,8 @@ module ActiveRecord
def aggregate_column(column_name)
return column_name if Arel::Expressions === column_name
- if @klass.column_names.include?(column_name.to_s)
- Arel::Attribute.new(@klass.unscoped.table, column_name)
+ if @klass.has_attribute?(column_name.to_s) || @klass.attribute_alias?(column_name.to_s)
+ @klass.arel_attribute(column_name)
else
Arel.sql(column_name == :all ? "*" : column_name.to_s)
end
diff --git a/activerecord/lib/active_record/relation/record_fetch_warning.rb b/activerecord/lib/active_record/relation/record_fetch_warning.rb
index dbd08811fa..31544c730e 100644
--- a/activerecord/lib/active_record/relation/record_fetch_warning.rb
+++ b/activerecord/lib/active_record/relation/record_fetch_warning.rb
@@ -2,15 +2,15 @@ module ActiveRecord
class Relation
module RecordFetchWarning
# When this module is prepended to ActiveRecord::Relation and
- # `config.active_record.warn_on_records_fetched_greater_than` is
+ # +config.active_record.warn_on_records_fetched_greater_than+ is
# set to an integer, if the number of records a query returns is
- # greater than the value of `warn_on_records_fetched_greater_than`,
+ # greater than the value of +warn_on_records_fetched_greater_than+,
# a warning is logged. This allows for the detection of queries that
# return a large number of records, which could cause memory bloat.
#
# In most cases, fetching large number of records can be performed
# efficiently using the ActiveRecord::Batches methods.
- # See active_record/lib/relation/batches.rb for more information.
+ # See ActiveRecord::Batches for more information.
def exec_queries
QueryRegistry.reset
diff --git a/activerecord/lib/active_record/schema_dumper.rb b/activerecord/lib/active_record/schema_dumper.rb
index ab2d64e903..c1c6519cfa 100644
--- a/activerecord/lib/active_record/schema_dumper.rb
+++ b/activerecord/lib/active_record/schema_dumper.rb
@@ -115,9 +115,7 @@ HEADER
pkcol = columns.detect { |c| c.name == pk }
pkcolspec = @connection.column_spec_for_primary_key(pkcol)
if pkcolspec.present?
- pkcolspec.each do |key, value|
- tbl.print ", #{key}: #{value}"
- end
+ tbl.print ", #{format_colspec(pkcolspec)}"
end
when Array
tbl.print ", primary_key: #{pk.inspect}"
@@ -128,26 +126,19 @@ HEADER
table_options = @connection.table_options(table)
if table_options.present?
- table_options.each do |key, value|
- tbl.print ", #{key}: #{value.inspect}" if value.present?
- end
+ tbl.print ", #{format_options(table_options)}"
end
tbl.puts " do |t|"
# then dump all non-primary key columns
- column_specs = columns.map do |column|
+ columns.each do |column|
raise StandardError, "Unknown type '#{column.sql_type}' for column '#{column.name}'" unless @connection.valid_type?(column.type)
next if column.name == pk
- @connection.column_spec(column)
- end.compact
-
- # find all migration keys used in this table
- keys = @connection.migration_keys
-
- column_specs.each do |colspec|
- values = keys.map { |key| colspec[key] }.compact
- tbl.puts " t.#{colspec[:type]} #{values.join(", ")}"
+ type, colspec = @connection.column_spec(column)
+ tbl.print " t.#{type} #{column.name.inspect}"
+ tbl.print ", #{format_colspec(colspec)}" if colspec.present?
+ tbl.puts
end
indexes_in_create(table, tbl)
@@ -194,12 +185,8 @@ HEADER
"name: #{index.name.inspect}",
]
index_parts << "unique: true" if index.unique
-
- index_lengths = (index.lengths || []).compact
- index_parts << "length: #{Hash[index.columns.zip(index.lengths)].inspect}" if index_lengths.any?
-
- index_orders = index.orders || {}
- index_parts << "order: #{index.orders.inspect}" if index_orders.any?
+ index_parts << "length: { #{format_options(index.lengths)} }" if index.lengths.present?
+ index_parts << "order: { #{format_options(index.orders)} }" if index.orders.present?
index_parts << "where: #{index.where.inspect}" if index.where
index_parts << "using: #{index.using.inspect}" if index.using
index_parts << "type: #{index.type.inspect}" if index.type
@@ -237,6 +224,14 @@ HEADER
end
end
+ def format_colspec(colspec)
+ colspec.map { |key, value| "#{key}: #{value}" }.join(", ")
+ end
+
+ def format_options(options)
+ options.map { |key, value| "#{key}: #{value.inspect}" }.join(", ")
+ end
+
def remove_prefix_and_suffix(table)
table.gsub(/^(#{@options[:table_name_prefix]})(.+)(#{@options[:table_name_suffix]})$/, "\\2")
end
diff --git a/activerecord/lib/active_record/type.rb b/activerecord/lib/active_record/type.rb
index 0b48d2186a..84373dddf2 100644
--- a/activerecord/lib/active_record/type.rb
+++ b/activerecord/lib/active_record/type.rb
@@ -1,4 +1,6 @@
require "active_model/type"
+require "active_record/type/helpers"
+require "active_record/type/value"
require "active_record/type/internal/abstract_json"
require "active_record/type/internal/timezone"
@@ -48,7 +50,6 @@ module ActiveRecord
end
end
- Helpers = ActiveModel::Type::Helpers
BigInteger = ActiveModel::Type::BigInteger
Binary = ActiveModel::Type::Binary
Boolean = ActiveModel::Type::Boolean
@@ -59,7 +60,6 @@ module ActiveRecord
String = ActiveModel::Type::String
Text = ActiveModel::Type::Text
UnsignedInteger = ActiveModel::Type::UnsignedInteger
- Value = ActiveModel::Type::Value
register(:big_integer, Type::BigInteger, override: false)
register(:binary, Type::Binary, override: false)
diff --git a/activerecord/lib/active_record/type/helpers.rb b/activerecord/lib/active_record/type/helpers.rb
new file mode 100644
index 0000000000..a32ccd4bc3
--- /dev/null
+++ b/activerecord/lib/active_record/type/helpers.rb
@@ -0,0 +1,5 @@
+module ActiveRecord
+ module Type
+ Helpers = ActiveModel::Type::Helpers
+ end
+end
diff --git a/activerecord/lib/active_record/type/internal/abstract_json.rb b/activerecord/lib/active_record/type/internal/abstract_json.rb
index 513c938088..67028546e4 100644
--- a/activerecord/lib/active_record/type/internal/abstract_json.rb
+++ b/activerecord/lib/active_record/type/internal/abstract_json.rb
@@ -1,8 +1,8 @@
module ActiveRecord
module Type
module Internal # :nodoc:
- class AbstractJson < ActiveModel::Type::Value # :nodoc:
- include ActiveModel::Type::Helpers::Mutable
+ class AbstractJson < Type::Value # :nodoc:
+ include Type::Helpers::Mutable
def type
:json
@@ -17,7 +17,11 @@ module ActiveRecord
end
def serialize(value)
- ::ActiveSupport::JSON.encode(value)
+ if value.nil?
+ nil
+ else
+ ::ActiveSupport::JSON.encode(value)
+ end
end
def accessor
diff --git a/activerecord/lib/active_record/type/serialized.rb b/activerecord/lib/active_record/type/serialized.rb
index ac9134bfcb..ca12c83b1a 100644
--- a/activerecord/lib/active_record/type/serialized.rb
+++ b/activerecord/lib/active_record/type/serialized.rb
@@ -1,7 +1,7 @@
module ActiveRecord
module Type
- class Serialized < DelegateClass(ActiveModel::Type::Value) # :nodoc:
- include ActiveModel::Type::Helpers::Mutable
+ class Serialized < DelegateClass(Type::Value) # :nodoc:
+ include Type::Helpers::Mutable
attr_reader :subtype, :coder
diff --git a/activerecord/lib/active_record/type/value.rb b/activerecord/lib/active_record/type/value.rb
new file mode 100644
index 0000000000..89ef29106b
--- /dev/null
+++ b/activerecord/lib/active_record/type/value.rb
@@ -0,0 +1,5 @@
+module ActiveRecord
+ module Type
+ class Value < ActiveModel::Type::Value; end
+ end
+end