diff options
Diffstat (limited to 'activerecord/lib/active_record')
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. |