diff options
Diffstat (limited to 'activerecord/lib/active_record')
21 files changed, 91 insertions, 87 deletions
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 5538c837b3..2cbfa42718 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -76,12 +76,6 @@ module ActiveRecord end end - class HasAndBelongsToManyAssociationWithPrimaryKeyError < ActiveRecordError #:nodoc: - def initialize(reflection) - super("Primary key is not allowed in a has_and_belongs_to_many join table (#{reflection.options[:join_table]}).") - end - end - class HasAndBelongsToManyAssociationForeignKeyNeeded < ActiveRecordError #:nodoc: def initialize(reflection) super("Cannot create self referential has_and_belongs_to_many association on '#{reflection.class_name rescue nil}##{reflection.name rescue nil}'. :association_foreign_key cannot be the same as the :foreign_key.") @@ -866,7 +860,7 @@ module ActiveRecord # the inverse of each other and the inverse of the +dungeon+ association on +EvilWizard+ # is the +evil_wizard+ association on +Dungeon+ (and vice-versa). By default, # Active Record doesn't know anything about these inverse relationships and so no object - # loading optimisation is possible. For example: + # loading optimization is possible. For example: # # d = Dungeon.first # t = d.traps.first diff --git a/activerecord/lib/active_record/associations/association_scope.rb b/activerecord/lib/active_record/associations/association_scope.rb index 94847bc2ae..9e6d9e73c5 100644 --- a/activerecord/lib/active_record/associations/association_scope.rb +++ b/activerecord/lib/active_record/associations/association_scope.rb @@ -60,7 +60,7 @@ module ActiveRecord scope = scope.joins(join( join_table, - table[reflection.active_record_primary_key]. + table[reflection.association_primary_key]. eq(join_table[reflection.association_foreign_key]) )) diff --git a/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb b/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb index 4b48757da7..d7632b2ea9 100644 --- a/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb +++ b/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb @@ -39,10 +39,6 @@ module ActiveRecord::Associations::Builder model.send(:undecorated_table_name, model.to_s), model.send(:undecorated_table_name, reflection.class_name) ) - - if model.connection.supports_primary_key? && (model.connection.primary_key(reflection.options[:join_table]) rescue false) - raise ActiveRecord::HasAndBelongsToManyAssociationWithPrimaryKeyError.new(reflection) - end end # Generates a join table name from two provided table names. diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb index 8a028c8996..7e1a41e84d 100644 --- a/activerecord/lib/active_record/associations/collection_association.rb +++ b/activerecord/lib/active_record/associations/collection_association.rb @@ -104,26 +104,11 @@ module ActiveRecord end def create(attributes = {}, options = {}, &block) - unless owner.persisted? - raise ActiveRecord::RecordNotSaved, "You cannot call create unless the parent is saved" - end - - if attributes.is_a?(Array) - attributes.collect { |attr| create(attr, options, &block) } - else - transaction do - add_to_target(build_record(attributes, options)) do |record| - yield(record) if block_given? - insert_record(record) - end - end - end + create_record(attributes, options, &block) end - def create!(attrs = {}, options = {}, &block) - record = create(attrs, options, &block) - Array.wrap(record).each(&:save!) - record + def create!(attributes = {}, options = {}, &block) + create_record(attributes, options, true, &block) end # Add +records+ to this association. Returns +self+ so method calls may be chained. @@ -402,9 +387,13 @@ module ActiveRecord return memory if persisted.empty? persisted.map! do |record| - mem_record = memory.delete(record) + # Unfortunately we cannot simply do memory.delete(record) since on 1.8 this returns + # record rather than memory.at(memory.index(record)). The behaviour is fixed in 1.9. + mem_index = memory.index(record) + + if mem_index + mem_record = memory.delete_at(mem_index) - if mem_record (record.attribute_names - mem_record.changes.keys).each do |name| mem_record[name] = record[name] end @@ -418,8 +407,25 @@ module ActiveRecord persisted + memory end + def create_record(attributes, options, raise = false, &block) + unless owner.persisted? + raise ActiveRecord::RecordNotSaved, "You cannot call create unless the parent is saved" + end + + if attributes.is_a?(Array) + attributes.collect { |attr| create_record(attr, options, raise, &block) } + else + transaction do + add_to_target(build_record(attributes, options)) do |record| + yield(record) if block_given? + insert_record(record, true, raise) + end + end + end + end + # Do the relevant stuff to insert the given record into the association collection. - def insert_record(record, validate = true) + def insert_record(record, validate = true, raise = false) raise NotImplementedError end @@ -430,7 +436,6 @@ module ActiveRecord def build_record(attributes, options) record = reflection.build_association(attributes, options) record.assign_attributes(create_scope.except(*record.changed), :without_protection => true) - record.assign_attributes(attributes, options) record end diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb index adfc71d435..81b4a26b04 100644 --- a/activerecord/lib/active_record/associations/collection_proxy.rb +++ b/activerecord/lib/active_record/associations/collection_proxy.rb @@ -56,6 +56,8 @@ module ActiveRecord Array.wrap(association.options[:extend]).each { |ext| proxy_extend(ext) } end + alias_method :new, :build + def respond_to?(*args) super || (load_target && target.respond_to?(*args)) || @@ -115,14 +117,6 @@ module ActiveRecord @association.reload self end - - def new(*args, &block) - if @association.is_a?(HasManyThroughAssociation) - @association.build(*args, &block) - else - method_missing(:new, *args, &block) - end - end end end end diff --git a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb index 217213808b..f7ce70db1a 100644 --- a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb +++ b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb @@ -9,8 +9,14 @@ module ActiveRecord super end - def insert_record(record, validate = true) - return if record.new_record? && !record.save(:validate => validate) + def insert_record(record, validate = true, raise = false) + if record.new_record? + if raise + record.save!(:validate => validate) + else + return unless record.save(:validate => validate) + end + end if options[:insert_sql] owner.connection.insert(interpolate(options[:insert_sql], record)) diff --git a/activerecord/lib/active_record/associations/has_many_association.rb b/activerecord/lib/active_record/associations/has_many_association.rb index 7172e89a05..50ee60284c 100644 --- a/activerecord/lib/active_record/associations/has_many_association.rb +++ b/activerecord/lib/active_record/associations/has_many_association.rb @@ -7,9 +7,14 @@ module ActiveRecord # is provided by its child HasManyThroughAssociation. class HasManyAssociation < CollectionAssociation #:nodoc: - def insert_record(record, validate = true) + def insert_record(record, validate = true, raise = false) set_owner_attributes(record) - record.save(:validate => validate) + + if raise + record.save!(:validate => validate) + else + record.save(:validate => validate) + end end private 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 7708228d23..2e818dca5d 100644 --- a/activerecord/lib/active_record/associations/has_many_through_association.rb +++ b/activerecord/lib/active_record/associations/has_many_through_association.rb @@ -6,8 +6,6 @@ module ActiveRecord class HasManyThroughAssociation < HasManyAssociation #:nodoc: include ThroughAssociation - alias_method :new, :build - # Returns the size of the collection by executing a SELECT COUNT(*) query if the collection hasn't been # loaded and calling collection.size if it has. If it's more likely than not that the collection does # have a size larger than zero, and you need to fetch that collection afterwards, it'll take one fewer @@ -33,9 +31,16 @@ module ActiveRecord super end - def insert_record(record, validate = true) + def insert_record(record, validate = true, raise = false) ensure_not_nested - return if record.new_record? && !record.save(:validate => validate) + + if record.new_record? + if raise + record.save!(:validate => validate) + else + return unless record.save(:validate => validate) + end + end through_record(record).save! update_counter(1) diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index f74810720d..08cc282d09 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1308,7 +1308,6 @@ MSG rescue NameError => e # We don't want to swallow NoMethodError < NameError errors raise e unless e.instance_of?(NameError) - rescue ArgumentError end 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 a754f46af0..74c07c624d 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -161,7 +161,7 @@ module ActiveRecord yield td if block_given? if options[:force] && table_exists?(table_name) - drop_table(table_name, options) + drop_table(table_name) end create_sql = "CREATE#{' TEMPORARY' if options[:temporary]} TABLE " @@ -253,7 +253,7 @@ module ActiveRecord end # Drops a table from the database. - def drop_table(table_name, options = {}) + def drop_table(table_name) execute "DROP TABLE #{quote_table_name(table_name)}" end diff --git a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb index 6e2c3f4527..24d8c8cad2 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb @@ -427,10 +427,6 @@ module ActiveRecord tables(nil, schema).include? table end - def drop_table(table_name, options = {}) - super(table_name, options) - end - # Returns an array of indexes for the given table. def indexes(table_name, name = nil) indexes = [] diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb index ee83f8120d..8d7dd8bacf 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb @@ -568,10 +568,6 @@ module ActiveRecord tables(nil, schema).include? table end - def drop_table(table_name, options = {}) - super(table_name, options) - end - # Returns an array of indexes for the given table. def indexes(table_name, name = nil)#:nodoc: indexes = [] diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 8b48c055ac..3e390ba994 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -805,8 +805,8 @@ module ActiveRecord end if pk && sequence - quoted_sequence = quote_column_name(sequence) - + quoted_sequence = quote_table_name(sequence) + select_value <<-end_sql, 'Reset sequence' SELECT setval('#{quoted_sequence}', (SELECT COALESCE(MAX(#{quote_column_name pk})+(SELECT increment_by FROM #{quoted_sequence}), (SELECT min_value FROM #{quoted_sequence})) FROM #{quote_table_name(table)}), false) end_sql @@ -818,18 +818,25 @@ module ActiveRecord # First try looking for a sequence with a dependency on the # given table's primary key. result = exec_query(<<-end_sql, 'SCHEMA').rows.first - SELECT attr.attname, seq.relname + SELECT attr.attname, ns.nspname, seq.relname FROM pg_class seq INNER JOIN pg_depend dep ON seq.oid = dep.objid INNER JOIN pg_attribute attr ON attr.attrelid = dep.refobjid AND attr.attnum = dep.refobjsubid INNER JOIN pg_constraint cons ON attr.attrelid = cons.conrelid AND attr.attnum = cons.conkey[1] + INNER JOIN pg_namespace ns ON seq.relnamespace = ns.oid WHERE seq.relkind = 'S' AND cons.contype = 'p' AND dep.refobjid = '#{quote_table_name(table)}'::regclass end_sql # [primary_key, sequence] - [result.first, result.last] + if result.second == 'public' then + sequence = result.last + else + sequence = result.second+'.'+result.last + end + + [result.first, sequence] rescue nil end diff --git a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb index 3c6f52e0fa..724b2e6d9c 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb @@ -102,6 +102,10 @@ module ActiveRecord # Clears the prepared statements cache. def clear_cache! + @statements.values.map { |hash| hash[:stmt] }.each { |stmt| + stmt.close unless stmt.closed? + } + @statements.clear end diff --git a/activerecord/lib/active_record/migration/command_recorder.rb b/activerecord/lib/active_record/migration/command_recorder.rb index cf4a97d054..f01e94169f 100644 --- a/activerecord/lib/active_record/migration/command_recorder.rb +++ b/activerecord/lib/active_record/migration/command_recorder.rb @@ -79,8 +79,10 @@ module ActiveRecord end def invert_add_index(args) - table, columns, _ = *args - [:remove_index, [table, {:column => columns}]] + table, columns, options = *args + index_name = options.try(:[], :name) + options_hash = index_name ? {:name => index_name} : {:column => columns} + [:remove_index, [table, options_hash]] end def invert_remove_timestamps(args) diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb index 588f52be44..14db7a6cd6 100644 --- a/activerecord/lib/active_record/named_scope.rb +++ b/activerecord/lib/active_record/named_scope.rb @@ -17,7 +17,7 @@ module ActiveRecord # posts.each {|p| puts p.name } # Fires "select * from posts" and loads post objects # # fruits = Fruit.scoped - # fruits = fruits.where(:colour => 'red') if options[:red_only] + # fruits = fruits.where(:color => 'red') if options[:red_only] # fruits = fruits.limit(10) if limited? # # Anonymous \scopes tend to be useful when procedurally generating complex diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index d88e2693b6..317af8a15d 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -6,13 +6,13 @@ module ActiveRecord JoinOperation = Struct.new(:relation, :join_class, :on) ASSOCIATION_METHODS = [:includes, :eager_load, :preload] MULTI_VALUE_METHODS = [:select, :group, :order, :joins, :where, :having, :bind] - SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :create_with, :from, :reorder] + SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :create_with, :from, :reorder, :reverse_order] include FinderMethods, Calculations, SpawnMethods, QueryMethods, Batches # These are explicitly delegated to improve performance (avoids method_missing) delegate :to_xml, :to_yaml, :length, :collect, :map, :each, :all?, :include?, :to => :to_a - delegate :table_name, :quoted_table_name, :primary_key, :quoted_primary_key, :to => :klass + delegate :table_name, :quoted_table_name, :primary_key, :quoted_primary_key, :connection, :column_hash,:to => :klass attr_reader :table, :klass, :loaded attr_accessor :extensions, :default_scoped diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index a785f38e89..aabe5c269b 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -196,7 +196,7 @@ module ActiveRecord def execute_simple_calculation(operation, column_name, distinct) #:nodoc: # Postgresql doesn't like ORDER BY when there are no GROUP BY - relation = reorder(nil) + relation = with_default_scope.reorder(nil) if operation == "count" && (relation.limit_value || relation.offset_value) # Shortcut when limit is zero. @@ -245,7 +245,7 @@ module ActiveRecord "#{field} AS #{aliaz}" } - relation = except(:group).group(group.join(',')) + relation = with_default_scope.except(:group).group(group.join(',')) relation.select_values = select_values calculated_data = @klass.connection.select_all(relation.to_sql) diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 94aa999715..739363415c 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -9,7 +9,7 @@ module ActiveRecord :select_values, :group_values, :order_values, :joins_values, :where_values, :having_values, :bind_values, :limit_value, :offset_value, :lock_value, :readonly_value, :create_with_value, - :from_value, :reorder_value + :from_value, :reorder_value, :reverse_order_value def includes(*args) args.reject! {|a| a.blank? } @@ -158,13 +158,9 @@ module ActiveRecord end def reverse_order - order_clause = arel.order_clauses - - order = order_clause.empty? ? - "#{table_name}.#{primary_key} DESC" : - reverse_sql_order(order_clause).join(', ') - - except(:order).order(Arel.sql(order)) + relation = clone + relation.reverse_order_value = !relation.reverse_order_value + relation end def arel @@ -186,6 +182,7 @@ module ActiveRecord arel.group(*@group_values.uniq.reject{|g| g.blank?}) unless @group_values.empty? order = @reorder_value ? @reorder_value : @order_values + order = reverse_sql_order(order) if @reverse_order_value arel.order(*order.uniq.reject{|o| o.blank?}) unless order.empty? build_select(arel, @select_values.uniq) @@ -306,6 +303,8 @@ module ActiveRecord end def reverse_sql_order(order_query) + order_query = ["#{quoted_table_name}.#{quoted_primary_key} ASC"] if order_query.empty? + order_query.join(', ').split(',').collect do |s| s.gsub!(/\sasc\Z/i, ' DESC') || s.gsub!(/\sdesc\Z/i, ' ASC') || s.concat(' DESC') end diff --git a/activerecord/lib/active_record/schema_dumper.rb b/activerecord/lib/active_record/schema_dumper.rb index 62e6999736..19585f6214 100644 --- a/activerecord/lib/active_record/schema_dumper.rb +++ b/activerecord/lib/active_record/schema_dumper.rb @@ -106,7 +106,7 @@ HEADER spec = {} spec[:name] = column.name.inspect - # AR has an optimisation which handles zero-scale decimals as integers. This + # AR has an optimization which handles zero-scale decimals as integers. This # code ensures that the dumper still dumps the column as a decimal. spec[:type] = if column.type == :integer && [/^numeric/, /^decimal/].any? { |e| e.match(column.sql_type) } 'decimal' diff --git a/activerecord/lib/active_record/serialization.rb b/activerecord/lib/active_record/serialization.rb index be4354ce6a..e3185a9c5a 100644 --- a/activerecord/lib/active_record/serialization.rb +++ b/activerecord/lib/active_record/serialization.rb @@ -31,9 +31,6 @@ module ActiveRecord #:nodoc: def serializable_add_includes(options = {}) return unless include_associations = options.delete(:include) - base_only_or_except = { :except => options[:except], - :only => options[:only] } - include_has_options = include_associations.is_a?(Hash) associations = include_has_options ? include_associations.keys : Array.wrap(include_associations) @@ -46,9 +43,8 @@ module ActiveRecord #:nodoc: end if records - association_options = include_has_options ? include_associations[association] : base_only_or_except - opts = options.merge(association_options) - yield(association, records, opts) + association_options = include_has_options ? include_associations[association] : {} + yield(association, records, association_options) end end |