diff options
Diffstat (limited to 'activerecord/lib')
16 files changed, 71 insertions, 76 deletions
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 265bad7bc2..ac1479ad8f 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -774,16 +774,15 @@ module ActiveRecord # In the above example posts with no approved comments are not returned at all, because # the conditions apply to the SQL statement as a whole and not just to the association. # + # You must disambiguate column references for this fallback to happen, for example + # <tt>order: "author.name DESC"</tt> will work but <tt>order: "name DESC"</tt> will not. + # # If you want to load all posts (including posts with no approved comments) then write # your own LEFT OUTER JOIN query using ON # - # Post.joins('LEFT OUTER JOIN comments ON comments.post_id = posts.id AND comments.approved = true') - # - # You must disambiguate column references for this fallback to happen, for example - # <tt>order: "author.name DESC"</tt> will work but <tt>order: "name DESC"</tt> will not. + # Post.joins("LEFT OUTER JOIN comments ON comments.post_id = posts.id AND comments.approved = '1'") # - # If you do want eager load only some members of an association it is usually more natural - # to include an association which has conditions defined on it: + # In this case it is usually more natural to include an association which has conditions defined on it: # # class Post < ActiveRecord::Base # has_many :approved_comments, -> { where approved: true }, class_name: 'Comment' diff --git a/activerecord/lib/active_record/associations/association_scope.rb b/activerecord/lib/active_record/associations/association_scope.rb index f1a3b23d5a..572f556999 100644 --- a/activerecord/lib/active_record/associations/association_scope.rb +++ b/activerecord/lib/active_record/associations/association_scope.rb @@ -47,15 +47,8 @@ module ActiveRecord def self.get_bind_values(owner, chain) bvs = [] chain.each_with_index do |reflection, i| - if reflection.source_macro == :belongs_to - foreign_key = reflection.foreign_key - else - foreign_key = reflection.active_record_primary_key - end - if reflection == chain.last - bvs << owner[foreign_key] - + bvs << reflection.join_id_for(owner) if reflection.type bvs << owner.class.base_class.name end diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb index 1c84973920..48628230c7 100644 --- a/activerecord/lib/active_record/associations/collection_association.rb +++ b/activerecord/lib/active_record/associations/collection_association.rb @@ -194,7 +194,7 @@ module ActiveRecord options[:dependent] end - delete_all_with_dependency(dependent).tap do + delete_records(:all, dependent).tap do reset loaded! end @@ -251,14 +251,6 @@ module ActiveRecord delete_or_destroy(records, dependent) end - def delete_all_with_dependency(dependent) - if dependent == :destroy - delete_or_destroy(load_target, dependent) - else - delete_records(:all, dependent) - end - end - # Deletes the +records+ and removes them from this association calling # +before_remove+ , +after_remove+ , +before_destroy+ and +after_destroy+ callbacks. # 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 f3af8605cd..0b122d2070 100644 --- a/activerecord/lib/active_record/associations/has_many_through_association.rb +++ b/activerecord/lib/active_record/associations/has_many_through_association.rb @@ -12,10 +12,11 @@ module ActiveRecord @through_association = nil end - # 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 - # SELECT query if you use #length. + # Returns the size of the collection by executing a SELECT COUNT(*) query + # if the collection hasn't been loaded, and by calling collection.size if + # it has. If the collection will likely have a size greater than zero, + # and if fetching the collection will be needed afterwards, one less + # SELECT query will be generated by using #length instead. def size if has_cached_counter? owner.read_attribute cached_counter_attribute_name(reflection) @@ -72,13 +73,11 @@ module ActiveRecord @through_association ||= owner.association(through_reflection.name) end - # We temporarily cache through record that has been build, because if we build a - # through record in build_record and then subsequently call insert_record, then we - # want to use the exact same object. + # The through record (built with build_record) is temporarily cached + # so that it may be reused if insert_record is subsequently called. # - # However, after insert_record has been called, we clear the cache entry because - # we want it to be possible to have multiple instances of the same record in an - # association + # However, after insert_record has been called, the cache is cleared in + # order to allow multiple instances of the same record in an association. def build_through_record(record) @through_records[record.object_id] ||= begin ensure_mutable diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb index f149d8f127..80cf7572df 100644 --- a/activerecord/lib/active_record/autosave_association.rb +++ b/activerecord/lib/active_record/autosave_association.rb @@ -304,7 +304,8 @@ module ActiveRecord def association_valid?(reflection, record) return true if record.destroyed? || record.marked_for_destruction? - unless valid = record.valid? + validation_context = self.validation_context unless [:create, :update].include?(self.validation_context) + unless valid = record.valid?(validation_context) if reflection.options[:autosave] record.errors.each do |attribute, message| attribute = "#{reflection.name}.#{attribute}" 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 aa99822389..ffa6af6d99 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -71,7 +71,8 @@ module ActiveRecord # column_exists?(:suppliers, :tax, :decimal, precision: 8, scale: 2) # def column_exists?(table_name, column_name, type = nil, options = {}) - columns(table_name).any?{ |c| c.name == column_name.to_s && + column_name = column_name.to_s + columns(table_name).any?{ |c| c.name == column_name && (!type || c.type == type) && (!options.key?(:limit) || c.limit == options[:limit]) && (!options.key?(:precision) || c.precision == options[:precision]) && diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index 78343cf4f5..3b3b03ff6e 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -120,7 +120,7 @@ module ActiveRecord end def collector - if @prepared_statements + if prepared_statements SQLString.new else BindCollector.new @@ -388,7 +388,13 @@ module ActiveRecord end def without_prepared_statement?(binds) - !@prepared_statements || binds.empty? + !prepared_statements || binds.empty? + end + + def column_for(table_name, column_name) # :nodoc: + column_name = column_name.to_s + columns(table_name).detect { |c| c.name == column_name } || + raise(ActiveRecordError, "No such column: #{table_name}.#{column_name}") end end 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 75c58ac7d9..4184fad81c 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb @@ -711,15 +711,13 @@ module ActiveRecord end def rename_column_sql(table_name, column_name, new_column_name) - options = { name: new_column_name } - - if column = columns(table_name).find { |c| c.name == column_name.to_s } - options[:default] = column.default - options[:null] = column.null - options[:auto_increment] = (column.extra == "auto_increment") - else - raise ActiveRecordError, "No such column: #{table_name}.#{column_name}" - end + column = column_for(table_name, column_name) + options = { + name: new_column_name, + default: column.default, + null: column.null, + auto_increment: column.extra == "auto_increment" + } current_type = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'", 'SCHEMA')["Type"] schema_creation.accept ChangeColumnDefinition.new column, current_type, options @@ -757,30 +755,23 @@ module ActiveRecord version[0] >= 5 end - def column_for(table_name, column_name) - unless column = columns(table_name).find { |c| c.name == column_name.to_s } - raise "No such column: #{table_name}.#{column_name}" - end - column - end - def configure_connection - variables = @config[:variables] || {} + variables = @config.fetch(:variables, {}).stringify_keys # By default, MySQL 'where id is null' selects the last inserted id. # Turn this off. http://dev.rubyonrails.org/ticket/6778 - variables[:sql_auto_is_null] = 0 + variables['sql_auto_is_null'] = 0 # Increase timeout so the server doesn't disconnect us. wait_timeout = @config[:wait_timeout] wait_timeout = 2147483 unless wait_timeout.is_a?(Fixnum) - variables[:wait_timeout] = self.class.type_cast_config_to_integer(wait_timeout) + variables['wait_timeout'] = self.class.type_cast_config_to_integer(wait_timeout) # Make MySQL reject illegal values rather than truncating or blanking them, see # http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html#sqlmode_strict_all_tables # If the user has provided another value for sql_mode, don't replace it. - if strict_mode? && !variables.has_key?(:sql_mode) - variables[:sql_mode] = 'STRICT_ALL_TABLES' + if strict_mode? && !variables.has_key?('sql_mode') + variables['sql_mode'] = 'STRICT_ALL_TABLES' end # NAMES does not have an equals sign, see 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 1dc7a6f0fd..e7169bd357 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb @@ -187,10 +187,6 @@ module ActiveRecord end end - def column_for(table_name, column_name) #:nodoc: - columns(table_name).detect { |c| c.name == column_name.to_s } - end - # Returns the current database name. def current_database query('select current_database()', 'SCHEMA')[0][0] diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb index dd4261cec7..737f2daa63 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb @@ -296,9 +296,12 @@ module ActiveRecord # Don't cache statements if they are not prepared if without_prepared_statement?(binds) stmt = @connection.prepare(sql) - cols = stmt.columns - records = stmt.to_a - stmt.close + begin + cols = stmt.columns + records = stmt.to_a + ensure + stmt.close + end stmt = records else cache = @statements[sql] ||= { @@ -492,11 +495,9 @@ module ActiveRecord end def rename_column(table_name, column_name, new_column_name) #:nodoc: - unless columns(table_name).detect{|c| c.name == column_name.to_s } - raise ActiveRecord::ActiveRecordError, "Missing column #{table_name}.#{column_name}" - end - alter_table(table_name, :rename => {column_name.to_s => new_column_name.to_s}) - rename_column_indexes(table_name, column_name, new_column_name) + column = column_for(table_name, column_name) + alter_table(table_name, rename: {column.name => new_column_name.to_s}) + rename_column_indexes(table_name, column.name, new_column_name) end protected diff --git a/activerecord/lib/active_record/log_subscriber.rb b/activerecord/lib/active_record/log_subscriber.rb index 654ef21b07..eb64d197f0 100644 --- a/activerecord/lib/active_record/log_subscriber.rb +++ b/activerecord/lib/active_record/log_subscriber.rb @@ -25,7 +25,7 @@ module ActiveRecord if column.binary? # This specifically deals with the PG adapter that casts bytea columns into a Hash. value = value[:value] if value.is_a?(Hash) - value = "<#{value.bytesize} bytes of binary data>" + value = value ? "<#{value.bytesize} bytes of binary data>" : "<NULL binary data>" end [column.name, value] diff --git a/activerecord/lib/active_record/model_schema.rb b/activerecord/lib/active_record/model_schema.rb index dc5ff02882..002bd16976 100644 --- a/activerecord/lib/active_record/model_schema.rb +++ b/activerecord/lib/active_record/model_schema.rb @@ -29,6 +29,10 @@ module ActiveRecord # :singleton-method: # Works like +table_name_prefix+, but appends instead of prepends (set to "_basecamp" gives "projects_basecamp", # "people_basecamp"). By default, the suffix is the empty string. + # + # If you are organising your models within modules, you can add a suffix to the models within + # a namespace by defining a singleton method in the parent module called table_name_suffix which + # returns your chosen suffix. class_attribute :table_name_suffix, instance_writer: false self.table_name_suffix = "" @@ -153,6 +157,10 @@ module ActiveRecord (parents.detect{ |p| p.respond_to?(:table_name_prefix) } || self).table_name_prefix end + def full_table_name_suffix #:nodoc: + (parents.detect {|p| p.respond_to?(:table_name_suffix) } || self).table_name_suffix + end + # Defines the name of the table column which will store the class name on single-table # inheritance situations. # @@ -337,7 +345,8 @@ module ActiveRecord contained = contained.singularize if parent.pluralize_table_names contained += '_' end - "#{full_table_name_prefix}#{contained}#{undecorated_table_name(name)}#{table_name_suffix}" + + "#{full_table_name_prefix}#{contained}#{undecorated_table_name(name)}#{full_table_name_suffix}" else # STI subclasses always use their superclass' table. base.table_name diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index 6b0459ea37..9538ead5f1 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -278,7 +278,7 @@ db_namespace = namespace :db do db_namespace['structure:dump'].reenable end - # desc "Recreate the databases from the structure.sql file" + desc "Recreate the databases from the structure.sql file" task :load => [:environment, :load_config] do ActiveRecord::Tasks::DatabaseTasks.load_schema(:sql, ENV['DB_STRUCTURE']) end diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index 95485ddada..7f4d77849a 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -278,6 +278,11 @@ module ActiveRecord end end + def join_id_for(owner) #:nodoc: + key = (source_macro == :belongs_to) ? foreign_key : active_record_primary_key + owner[key] + end + def through_reflection nil end diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index 0b56430b34..42c9881b48 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -242,7 +242,7 @@ module ActiveRecord return 0 if relation.limit_value == 0 query_builder = build_count_subquery(relation, column_name, distinct) - bind_values = relation.bind_values + bind_values = query_builder.bind_values + relation.bind_values else column = aggregate_column(column_name) @@ -389,9 +389,11 @@ module ActiveRecord aliased_column = aggregate_column(column_name == :all ? 1 : column_name).as(column_alias) relation.select_values = [aliased_column] - subquery = relation.arel.as(subquery_alias) + arel = relation.arel + subquery = arel.as(subquery_alias) sm = Arel::SelectManager.new relation.engine + sm.bind_values = arel.bind_values select_value = operation_over_aggregate_column(column_alias, 'count', distinct) sm.project(select_value).from(subquery) end diff --git a/activerecord/lib/active_record/relation/delegation.rb b/activerecord/lib/active_record/relation/delegation.rb index 9c666dcd3b..50f4d5c7ab 100644 --- a/activerecord/lib/active_record/relation/delegation.rb +++ b/activerecord/lib/active_record/relation/delegation.rb @@ -43,7 +43,7 @@ module ActiveRecord :keep_if, :pop, :shift, :delete_at, :compact, :select! ].to_set # :nodoc: - delegate :to_xml, :to_yaml, :length, :collect, :map, :each, :all?, :include?, :to_ary, to: :to_a + delegate :to_xml, :to_yaml, :length, :collect, :map, :each, :all?, :include?, :to_ary, :join, to: :to_a delegate :table_name, :quoted_table_name, :primary_key, :quoted_primary_key, :connection, :columns_hash, :to => :klass |