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.rb79
-rw-r--r--activerecord/lib/active_record/associations/collection_proxy.rb103
-rw-r--r--activerecord/lib/active_record/associations/join_dependency.rb6
-rw-r--r--activerecord/lib/active_record/associations/preloader/association.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb3
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql/column.rb20
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql/type_metadata.rb7
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb5
-rw-r--r--activerecord/lib/active_record/querying.rb3
-rw-r--r--activerecord/lib/active_record/reflection.rb4
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb12
-rw-r--r--activerecord/lib/active_record/sanitization.rb5
15 files changed, 115 insertions, 140 deletions
diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb
index 7c688d663c..e17ca81867 100644
--- a/activerecord/lib/active_record/associations/collection_association.rb
+++ b/activerecord/lib/active_record/associations/collection_association.rb
@@ -82,14 +82,6 @@ module ActiveRecord
@target = []
end
- def select(*fields)
- if block_given?
- load_target.select.each { |e| yield e }
- else
- scope.select(*fields)
- end
- end
-
def find(*args)
if block_given?
load_target.find(*args) { |*block_args| yield(*block_args) }
@@ -111,50 +103,6 @@ module ActiveRecord
end
end
- def first(limit = nil)
- find_nth_or_last(:first, limit)
- end
-
- def second
- find_nth_or_last(:second)
- end
-
- def third
- find_nth_or_last(:third)
- end
-
- def fourth
- find_nth_or_last(:fourth)
- end
-
- def fifth
- find_nth_or_last(:fifth)
- end
-
- def forty_two
- find_nth_or_last(:forty_two)
- end
-
- def third_to_last
- find_nth_or_last(:third_to_last)
- end
-
- def second_to_last
- find_nth_or_last(:second_to_last)
- end
-
- def last(limit = nil)
- find_nth_or_last(:last, limit)
- end
-
- def take(limit = nil)
- if find_from_target?
- limit ? load_target.take(limit) : load_target.first
- else
- scope.take(limit)
- end
- end
-
def build(attributes = {}, &block)
if attributes.is_a?(Array)
attributes.collect { |attr| build(attr, &block) }
@@ -453,6 +401,12 @@ module ActiveRecord
owner.new_record? && !foreign_key_present?
end
+ def find_from_target?
+ loaded? ||
+ owner.new_record? ||
+ target.any? { |record| record.new_record? || record.changed? }
+ end
+
private
def find_target
@@ -599,21 +553,6 @@ module ActiveRecord
owner.class.send(full_callback_name)
end
- # Should we deal with assoc.first or assoc.last by issuing an independent query to
- # the database, or by getting the target, and then taking the first/last item from that?
- #
- # If the args is just a non-empty options hash, go to the database.
- #
- # Otherwise, go to the database only if none of the following are true:
- # * target already loaded
- # * owner is new record
- # * target contains new or changed record(s)
- def find_from_target?
- loaded? ||
- owner.new_record? ||
- target.any? { |record| record.new_record? || record.changed? }
- end
-
def include_in_memory?(record)
if reflection.is_a?(ActiveRecord::Reflection::ThroughReflection)
assoc = owner.association(reflection.through_reflection.name)
@@ -640,12 +579,6 @@ module ActiveRecord
load_target.select { |r| ids.include?(r.id.to_s) }
end
end
-
- # Fetches the first/last using SQL if possible, otherwise from the target array.
- def find_nth_or_last(ordinal, limit = nil)
- collection = find_from_target? ? load_target : scope
- limit ? collection.send(ordinal, limit) : collection.send(ordinal)
- end
end
end
end
diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb
index 36f6c1b9c3..926defbb47 100644
--- a/activerecord/lib/active_record/associations/collection_proxy.rb
+++ b/activerecord/lib/active_record/associations/collection_proxy.rb
@@ -53,6 +53,12 @@ module ActiveRecord
@association.loaded?
end
+ ##
+ # :method: select
+ #
+ # :call-seq:
+ # select(*fields, &block)
+ #
# Works in two ways.
#
# *First:* Specify a subset of fields to be selected from the result set.
@@ -106,9 +112,6 @@ module ActiveRecord
# # #<Pet id: 2, name: "Spook">,
# # #<Pet id: 3, name: "Choo-Choo">
# # ]
- def select(*fields, &block)
- @association.select(*fields, &block)
- end
# Finds an object in the collection responding to the +id+. Uses the same
# rules as ActiveRecord::Base.find. Returns ActiveRecord::RecordNotFound
@@ -140,6 +143,12 @@ module ActiveRecord
@association.find(*args, &block)
end
+ ##
+ # :method: first
+ #
+ # :call-seq:
+ # first(limit = nil)
+ #
# Returns the first record, or the first +n+ records, from the collection.
# If the collection is empty, the first form returns +nil+, and the second
# form returns an empty array.
@@ -166,45 +175,63 @@ module ActiveRecord
# another_person_without.pets # => []
# another_person_without.pets.first # => nil
# another_person_without.pets.first(3) # => []
- def first(limit = nil)
- @association.first(limit)
- end
+ ##
+ # :method: second
+ #
+ # :call-seq:
+ # second()
+ #
# Same as #first except returns only the second record.
- def second
- @association.second
- end
+ ##
+ # :method: third
+ #
+ # :call-seq:
+ # third()
+ #
# Same as #first except returns only the third record.
- def third
- @association.third
- end
+ ##
+ # :method: fourth
+ #
+ # :call-seq:
+ # fourth()
+ #
# Same as #first except returns only the fourth record.
- def fourth
- @association.fourth
- end
+ ##
+ # :method: fifth
+ #
+ # :call-seq:
+ # fifth()
+ #
# Same as #first except returns only the fifth record.
- def fifth
- @association.fifth
- end
+ ##
+ # :method: forty_two
+ #
+ # :call-seq:
+ # forty_two()
+ #
# Same as #first except returns only the forty second record.
# Also known as accessing "the reddit".
- def forty_two
- @association.forty_two
- end
+ ##
+ # :method: third_to_last
+ #
+ # :call-seq:
+ # third_to_last()
+ #
# Same as #first except returns only the third-to-last record.
- def third_to_last
- @association.third_to_last
- end
+ ##
+ # :method: second_to_last
+ #
+ # :call-seq:
+ # second_to_last()
+ #
# Same as #first except returns only the second-to-last record.
- def second_to_last
- @association.second_to_last
- end
# Returns the last record, or the last +n+ records, from the collection.
# If the collection is empty, the first form returns +nil+, and the second
@@ -233,7 +260,8 @@ module ActiveRecord
# another_person_without.pets.last # => nil
# another_person_without.pets.last(3) # => []
def last(limit = nil)
- @association.last(limit)
+ load_target if find_from_target?
+ super
end
# Gives a record (or N records if a parameter is supplied) from the collection
@@ -262,7 +290,8 @@ module ActiveRecord
# another_person_without.pets.take # => nil
# another_person_without.pets.take(2) # => []
def take(limit = nil)
- @association.take(limit)
+ load_target if find_from_target?
+ super
end
# Returns a new object of the collection type that has been instantiated
@@ -1078,12 +1107,28 @@ module ActiveRecord
self
end
+ protected
+
+ def find_nth_with_limit(index, limit)
+ load_target if find_from_target?
+ super
+ end
+
+ def find_nth_from_last(index)
+ load_target if find_from_target?
+ super
+ end
+
private
def null_scope?
@association.null_scope?
end
+ def find_from_target?
+ @association.find_from_target?
+ end
+
def exec_queries
load_target
end
diff --git a/activerecord/lib/active_record/associations/join_dependency.rb b/activerecord/lib/active_record/associations/join_dependency.rb
index 3946d5baa4..62acad0eda 100644
--- a/activerecord/lib/active_record/associations/join_dependency.rb
+++ b/activerecord/lib/active_record/associations/join_dependency.rb
@@ -274,7 +274,11 @@ module ActiveRecord
construct(model, node, row, rs, seen, model_cache, aliases)
else
model = construct_model(ar_parent, node, row, model_cache, id, aliases)
- model.readonly!
+
+ if node.reflection.scope_for(node.base_klass).readonly_value
+ model.readonly!
+ end
+
seen[ar_parent.object_id][node.base_klass][id] = model
construct(model, node, row, rs, seen, model_cache, aliases)
end
diff --git a/activerecord/lib/active_record/associations/preloader/association.rb b/activerecord/lib/active_record/associations/preloader/association.rb
index a8afa48865..4bb627f399 100644
--- a/activerecord/lib/active_record/associations/preloader/association.rb
+++ b/activerecord/lib/active_record/associations/preloader/association.rb
@@ -113,7 +113,7 @@ module ActiveRecord
end
def reflection_scope
- @reflection_scope ||= reflection.scope ? klass.unscoped.instance_exec(nil, &reflection.scope) : klass.unscoped
+ @reflection_scope ||= reflection.scope_for(klass)
end
def build_scope
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 126047584e..def1dadb6a 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
@@ -379,7 +379,7 @@ module ActiveRecord
end
def sql_for_insert(sql, pk, id_value, sequence_name, binds)
- [sql, binds, pk, sequence_name]
+ [sql, binds]
end
def last_inserted_id(result)
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 d9a799676f..ffde4f2c93 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
@@ -303,7 +303,7 @@ module ActiveRecord
# end
def column(name, type, options = {})
name = name.to_s
- type = type.to_sym
+ type = type.to_sym if type
options = options.dup
if @columns_hash[name] && @columns_hash[name].primary_key?
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 afa0860707..45d782e45e 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -1047,7 +1047,8 @@ module ActiveRecord
end
def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc:
- if native = native_database_types[type.to_sym]
+ type = type.to_sym if type
+ if native = native_database_types[type]
column_type_sql = (native.is_a?(Hash) ? native[:name] : native).dup
if type == :decimal # ignore limit, use precision and scale
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 f09c9c9e7f..3a28879c15 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -709,7 +709,7 @@ module ActiveRecord
end
def fetch_type_metadata(sql_type, extra = "")
- MySQL::TypeMetadata.new(super(sql_type), extra: extra, strict: strict_mode?)
+ MySQL::TypeMetadata.new(super(sql_type), extra: extra)
end
def add_index_length(quoted_columns, **options)
diff --git a/activerecord/lib/active_record/connection_adapters/mysql/column.rb b/activerecord/lib/active_record/connection_adapters/mysql/column.rb
index 5452e44c84..72b57f4b8f 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql/column.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql/column.rb
@@ -2,17 +2,7 @@ module ActiveRecord
module ConnectionAdapters
module MySQL
class Column < ConnectionAdapters::Column # :nodoc:
- delegate :strict, :extra, to: :sql_type_metadata, allow_nil: true
-
- def initialize(*)
- super
- extract_default
- end
-
- def has_default?
- return false if blob_or_text_column? # MySQL forbids defaults on blob and text columns
- super
- end
+ delegate :extra, to: :sql_type_metadata, allow_nil: true
def blob_or_text_column?
/\A(?:tiny|medium|long)?blob\b/ === sql_type || type == :text
@@ -29,14 +19,6 @@ module ActiveRecord
def auto_increment?
extra == "auto_increment"
end
-
- private
-
- def extract_default
- if blob_or_text_column?
- @default = null || strict ? nil : ""
- end
- end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/mysql/type_metadata.rb b/activerecord/lib/active_record/connection_adapters/mysql/type_metadata.rb
index 1be5cb4740..24dcf852e1 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql/type_metadata.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql/type_metadata.rb
@@ -2,13 +2,12 @@ module ActiveRecord
module ConnectionAdapters
module MySQL
class TypeMetadata < DelegateClass(SqlTypeMetadata) # :nodoc:
- attr_reader :extra, :strict
+ attr_reader :extra
- def initialize(type_metadata, extra: "", strict: false)
+ def initialize(type_metadata, extra: "")
super(type_metadata)
@type_metadata = type_metadata
@extra = extra
- @strict = strict
end
def ==(other)
@@ -24,7 +23,7 @@ module ActiveRecord
protected
def attributes_for_hash
- [self.class, @type_metadata, extra, strict]
+ [self.class, @type_metadata, extra]
end
end
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 46aca2ab54..7414eba6c5 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb
@@ -124,14 +124,13 @@ module ActiveRecord
pk = primary_key(table_ref) if table_ref
end
- pk = suppress_composite_primary_key(pk)
-
- if pk && use_insert_returning?
+ if pk = suppress_composite_primary_key(pk)
sql = "#{sql} RETURNING #{quote_column_name(pk)}"
end
super
end
+ protected :sql_for_insert
def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil)
if use_insert_returning? || pk == false
diff --git a/activerecord/lib/active_record/querying.rb b/activerecord/lib/active_record/querying.rb
index 7b4b8c60f8..dd7d650207 100644
--- a/activerecord/lib/active_record/querying.rb
+++ b/activerecord/lib/active_record/querying.rb
@@ -62,8 +62,7 @@ module ActiveRecord
#
# * +sql+ - An SQL statement which should return a count query from the database, see the example above.
def count_by_sql(sql)
- sql = sanitize_conditions(sql)
- connection.select_value(sql, "#{name} Count").to_i
+ connection.select_value(sanitize_sql(sql), "#{name} Count").to_i
end
end
end
diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb
index 3553ff4da3..8c5e4d042e 100644
--- a/activerecord/lib/active_record/reflection.rb
+++ b/activerecord/lib/active_record/reflection.rb
@@ -311,6 +311,10 @@ module ActiveRecord
active_record == other_aggregation.active_record
end
+ def scope_for(klass)
+ scope ? klass.unscoped.instance_exec(nil, &scope) : klass.unscoped
+ end
+
private
def derive_class_name
name.to_s.camelize
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index ff43def901..376867675b 100644
--- a/activerecord/lib/active_record/relation/finder_methods.rb
+++ b/activerecord/lib/active_record/relation/finder_methods.rb
@@ -97,7 +97,7 @@ module ActiveRecord
# Person.take(5) # returns 5 objects fetched by SELECT * FROM people LIMIT 5
# Person.where(["name LIKE '%?'", name]).take
def take(limit = nil)
- limit ? limit(limit).to_a : find_take
+ limit ? find_take_with_limit(limit) : find_take
end
# Same as #take but raises ActiveRecord::RecordNotFound if no record
@@ -526,13 +526,21 @@ module ActiveRecord
end
end
+ def find_take_with_limit(limit)
+ if loaded?
+ records.take(limit)
+ else
+ limit(limit).to_a
+ end
+ end
+
def find_nth(index)
@offsets[offset_index + index] ||= find_nth_with_limit(index, 1).first
end
def find_nth_with_limit(index, limit)
if loaded?
- records[index, limit]
+ records[index, limit] || []
else
relation = if order_values.empty? && primary_key
order(arel_attribute(primary_key).asc)
diff --git a/activerecord/lib/active_record/sanitization.rb b/activerecord/lib/active_record/sanitization.rb
index c40e98715e..7f596120eb 100644
--- a/activerecord/lib/active_record/sanitization.rb
+++ b/activerecord/lib/active_record/sanitization.rb
@@ -29,8 +29,9 @@ module ActiveRecord
else condition
end
end
- alias_method :sanitize_sql, :sanitize_sql_for_conditions
- alias_method :sanitize_conditions, :sanitize_sql
+ alias :sanitize_sql :sanitize_sql_for_conditions
+ alias :sanitize_conditions :sanitize_sql
+ deprecate sanitize_conditions: :sanitize_sql
# Accepts an array, hash, or string of SQL conditions and sanitizes
# them into a valid SQL fragment for a SET clause.