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.rb496
-rw-r--r--activerecord/lib/active_record/base.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/quoting.rb45
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb9
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql_adapter.rb6
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb4
-rw-r--r--activerecord/lib/active_record/railties/databases.rake2
-rw-r--r--activerecord/lib/active_record/relation/calculations.rb14
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb11
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb24
-rw-r--r--activerecord/lib/active_record/relation/spawn_methods.rb11
-rw-r--r--activerecord/lib/active_record/test_case.rb15
12 files changed, 364 insertions, 275 deletions
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index 565ebf8197..affa2fbcaf 100644
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -1833,10 +1833,10 @@ module ActiveRecord
end
class JoinDependency # :nodoc:
- attr_reader :joins, :reflections, :table_aliases
+ attr_reader :join_parts, :reflections, :table_aliases
def initialize(base, associations, joins)
- @joins = [JoinBase.new(base, joins)]
+ @join_parts = [JoinBase.new(base, joins)]
@associations = associations
@reflections = []
@base_records_hash = {}
@@ -1849,17 +1849,17 @@ module ActiveRecord
def graft(*associations)
associations.each do |association|
join_associations.detect {|a| association == a} ||
- build(association.reflection.name, association.find_parent_in(self) || join_base, association.join_class)
+ build(association.reflection.name, association.find_parent_in(self) || join_base, association.join_type)
end
self
end
def join_associations
- @joins.last(@joins.length - 1)
+ join_parts.last(join_parts.length - 1)
end
def join_base
- @joins[0]
+ join_parts.first
end
def count_aliases_from_table_joins(name)
@@ -1917,22 +1917,24 @@ module ActiveRecord
protected
- def build(associations, parent = nil, join_class = Arel::InnerJoin)
- parent ||= @joins.last
+ def build(associations, parent = nil, join_type = Arel::InnerJoin)
+ parent ||= join_parts.last
case associations
when Symbol, String
reflection = parent.reflections[associations.to_s.intern] or
raise ConfigurationError, "Association named '#{ associations }' was not found; perhaps you misspelled it?"
@reflections << reflection
- @joins << build_join_association(reflection, parent).with_join_class(join_class)
+ join_association = build_join_association(reflection, parent)
+ join_association.join_type = join_type
+ @join_parts << join_association
when Array
associations.each do |association|
- build(association, parent, join_class)
+ build(association, parent, join_type)
end
when Hash
associations.keys.sort{|a,b|a.to_s<=>b.to_s}.each do |name|
- build(name, parent, join_class)
- build(associations[name], nil, join_class)
+ build(name, parent, join_type)
+ build(associations[name], nil, join_type)
end
else
raise ConfigurationError, associations.inspect
@@ -1949,91 +1951,111 @@ module ActiveRecord
JoinAssociation.new(reflection, self, parent)
end
- def construct(parent, associations, joins, row)
+ def construct(parent, associations, join_parts, row)
case associations
when Symbol, String
- join = joins.detect{|j| j.reflection.name.to_s == associations.to_s && j.parent_table_name == parent.class.table_name }
- raise(ConfigurationError, "No such association") if join.nil?
+ join_part = join_parts.detect { |j|
+ j.reflection.name.to_s == associations.to_s &&
+ j.parent_table_name == parent.class.table_name }
+ raise(ConfigurationError, "No such association") if join_part.nil?
- joins.delete(join)
- construct_association(parent, join, row)
+ join_parts.delete(join_part)
+ construct_association(parent, join_part, row)
when Array
associations.each do |association|
- construct(parent, association, joins, row)
+ construct(parent, association, join_parts, row)
end
when Hash
associations.sort_by { |k,_| k.to_s }.each do |name, assoc|
- join = joins.detect{|j| j.reflection.name.to_s == name.to_s && j.parent_table_name == parent.class.table_name }
- raise(ConfigurationError, "No such association") if join.nil?
-
- association = construct_association(parent, join, row)
- joins.delete(join)
- construct(association, assoc, joins, row) if association
+ join_part = join_parts.detect{ |j|
+ j.reflection.name.to_s == name.to_s &&
+ j.parent_table_name == parent.class.table_name }
+ raise(ConfigurationError, "No such association") if join_part.nil?
+
+ association = construct_association(parent, join_part, row)
+ join_parts.delete(join_part)
+ construct(association, assoc, join_parts, row) if association
end
else
raise ConfigurationError, associations.inspect
end
end
- def construct_association(record, join, row)
- return if record.id.to_s != join.parent.record_id(row).to_s
+ def construct_association(record, join_part, row)
+ return if record.id.to_s != join_part.parent.record_id(row).to_s
- macro = join.reflection.macro
+ macro = join_part.reflection.macro
if macro == :has_one
- return if record.instance_variable_defined?("@#{join.reflection.name}")
- association = join.instantiate(row) unless row[join.aliased_primary_key].nil?
- set_target_and_inverse(join, association, record)
+ return if record.instance_variable_defined?("@#{join_part.reflection.name}")
+ association = join_part.instantiate(row) unless row[join_part.aliased_primary_key].nil?
+ set_target_and_inverse(join_part, association, record)
else
- return if row[join.aliased_primary_key].nil?
- association = join.instantiate(row)
+ return if row[join_part.aliased_primary_key].nil?
+ association = join_part.instantiate(row)
case macro
when :has_many, :has_and_belongs_to_many
- collection = record.send(join.reflection.name)
+ collection = record.send(join_part.reflection.name)
collection.loaded
collection.target.push(association)
collection.__send__(:set_inverse_instance, association, record)
when :belongs_to
- set_target_and_inverse(join, association, record)
+ set_target_and_inverse(join_part, association, record)
else
- raise ConfigurationError, "unknown macro: #{join.reflection.macro}"
+ raise ConfigurationError, "unknown macro: #{join_part.reflection.macro}"
end
end
association
end
- def set_target_and_inverse(join, association, record)
- association_proxy = record.send("set_#{join.reflection.name}_target", association)
+ def set_target_and_inverse(join_part, association, record)
+ association_proxy = record.send("set_#{join_part.reflection.name}_target", association)
association_proxy.__send__(:set_inverse_instance, association, record)
end
-
- class JoinBase # :nodoc:
- attr_reader :active_record, :table_joins
- delegate :table_name, :column_names, :primary_key, :reflections, :sanitize_sql, :arel_engine, :to => :active_record
-
- def initialize(active_record, joins = nil)
+
+ # A JoinPart represents a part of a JoinDependency. It is an abstract class, inherited
+ # by JoinBase and JoinAssociation. A JoinBase represents the Active Record which
+ # everything else is being joined onto. A JoinAssociation represents an association which
+ # is joining to the base. A JoinAssociation may result in more than one actual join
+ # operations (for example a has_and_belongs_to_many JoinAssociation would result in
+ # two; one for the join table and one for the target table).
+ class JoinPart # :nodoc:
+ # The Active Record class which this join part is associated 'about'; for a JoinBase
+ # this is the actual base model, for a JoinAssociation this is the target model of the
+ # association.
+ attr_reader :active_record
+
+ delegate :table_name, :column_names, :primary_key, :reflections, :sanitize_sql, :arel_engine, :to => :active_record
+
+ def initialize(active_record)
@active_record = active_record
@cached_record = {}
- @table_joins = joins
end
-
+
def ==(other)
- other.class == self.class &&
- other.active_record == active_record &&
- other.table_joins == table_joins
+ raise NotImplementedError
end
-
+
+ # An Arel::Table for the active_record
+ def table
+ raise NotImplementedError
+ end
+
+ # The prefix to be used when aliasing columns in the active_record's table
def aliased_prefix
- "t0"
+ raise NotImplementedError
end
-
+
+ # The alias for the active_record's table
+ def aliased_table_name
+ raise NotImplementedError
+ end
+
+ # The alias for the primary key of the active_record's table
def aliased_primary_key
"#{aliased_prefix}_r0"
end
-
- def aliased_table_name
- active_record.table_name
- end
-
+
+ # An array of [column_name, alias] pairs for the table
def column_names_with_alias
unless defined?(@column_names_with_alias)
@column_names_with_alias = []
@@ -2059,33 +2081,74 @@ module ActiveRecord
end
end
- class JoinAssociation < JoinBase # :nodoc:
- attr_reader :reflection, :parent, :aliased_table_name, :aliased_prefix, :aliased_join_table_name, :parent_table_name, :join_class
- delegate :options, :klass, :through_reflection, :source_reflection, :to => :reflection
+ class JoinBase < JoinPart # :nodoc:
+ # Extra joins provided when the JoinDependency was created
+ attr_reader :table_joins
+
+ def initialize(active_record, joins = nil)
+ super(active_record)
+ @table_joins = joins
+ end
+
+ def ==(other)
+ other.class == self.class &&
+ other.active_record == active_record &&
+ other.table_joins == table_joins
+ end
+
+ def aliased_prefix
+ "t0"
+ end
+
+ def table
+ Arel::Table.new(table_name, :engine => arel_engine, :columns => active_record.columns)
+ end
+
+ def aliased_table_name
+ active_record.table_name
+ end
+ end
+
+ class JoinAssociation < JoinPart # :nodoc:
+ # The reflection of the association represented
+ attr_reader :reflection
+
+ # The JoinDependency object which this JoinAssociation exists within. This is mainly
+ # relevant for generating aliases which do not conflict with other joins which are
+ # part of the query.
+ attr_reader :join_dependency
+
+ # A JoinBase instance representing the active record we are joining onto.
+ # (So in Author.has_many :posts, the Author would be that base record.)
+ attr_reader :parent
+
+ # What type of join will be generated, either Arel::InnerJoin (default) or Arel::OuterJoin
+ attr_accessor :join_type
+
+ # These implement abstract methods from the superclass
+ attr_reader :aliased_prefix, :aliased_table_name
+
+ delegate :options, :through_reflection, :source_reflection, :to => :reflection
+ delegate :table, :table_name, :to => :parent, :prefix => true
def initialize(reflection, join_dependency, parent = nil)
reflection.check_validity!
+
if reflection.options[:polymorphic]
raise EagerLoadPolymorphicError.new(reflection)
end
super(reflection.klass)
- @join_dependency = join_dependency
- @parent = parent
- @reflection = reflection
- @aliased_prefix = "t#{ join_dependency.joins.size }"
- @parent_table_name = parent.active_record.table_name
- @aliased_table_name = aliased_table_name_for(table_name)
- @join = nil
- @join_class = Arel::InnerJoin
-
- if reflection.macro == :has_and_belongs_to_many
- @aliased_join_table_name = aliased_table_name_for(reflection.options[:join_table], "_join")
- end
-
- if [:has_many, :has_one].include?(reflection.macro) && reflection.options[:through]
- @aliased_join_table_name = aliased_table_name_for(reflection.through_reflection.klass.table_name, "_join")
- end
+
+ @reflection = reflection
+ @join_dependency = join_dependency
+ @parent = parent
+ @join_type = Arel::InnerJoin
+
+ # This must be done eagerly upon initialisation because the alias which is produced
+ # depends on the state of the join dependency, but we want it to work the same way
+ # every time.
+ allocate_aliases
end
def ==(other)
@@ -2095,63 +2158,29 @@ module ActiveRecord
end
def find_parent_in(other_join_dependency)
- other_join_dependency.joins.detect do |join|
- self.parent == join
+ other_join_dependency.join_parts.detect do |join_part|
+ self.parent == join_part
end
end
-
- def with_join_class(join_class)
- @join_class = join_class
- self
- end
-
- def association_join
- return @join if @join
-
- aliased_table = Arel::Table.new(table_name, :as => @aliased_table_name,
- :engine => arel_engine,
- :columns => klass.columns)
-
- parent_table = Arel::Table.new(parent.table_name, :as => parent.aliased_table_name,
- :engine => arel_engine,
- :columns => parent.active_record.columns)
-
- @join = send("build_#{reflection.macro}", aliased_table, parent_table)
-
- unless klass.descends_from_active_record?
- sti_column = aliased_table[klass.inheritance_column]
- sti_condition = sti_column.eq(klass.sti_name)
- klass.descendants.each {|subclass| sti_condition = sti_condition.or(sti_column.eq(subclass.sti_name)) }
-
- @join << sti_condition
- end
-
- [through_reflection, reflection].each do |ref|
- if ref && ref.options[:conditions]
- @join << interpolate_sql(sanitize_sql(ref.options[:conditions], aliased_table_name))
- end
- end
-
- @join
+
+ def join_to(relation)
+ send("join_#{reflection.macro}_to", relation)
end
- def relation
- aliased = Arel::Table.new(table_name, :as => @aliased_table_name,
- :engine => arel_engine,
- :columns => klass.columns)
-
- if reflection.macro == :has_and_belongs_to_many
- [Arel::Table.new(options[:join_table], :as => aliased_join_table_name, :engine => arel_engine), aliased]
- elsif reflection.options[:through]
- [Arel::Table.new(through_reflection.klass.table_name, :as => aliased_join_table_name, :engine => arel_engine), aliased]
- else
- aliased
- end
+ def join_relation(joining_relation)
+ self.join_type = Arel::OuterJoin
+ joining_relation.joins(self)
end
-
- def join_relation(joining_relation, join = nil)
- joining_relation.joins(self.with_join_class(Arel::OuterJoin))
+
+ def table
+ @table ||= Arel::Table.new(
+ table_name, :as => aliased_table_name,
+ :engine => arel_engine, :columns => active_record.columns
+ )
end
+
+ # More semantic name given we are talking about associations
+ alias_method :target_table, :table
protected
@@ -2180,87 +2209,174 @@ module ActiveRecord
ActiveRecord::Base.pluralize_table_names ? table_name.to_s.pluralize : table_name
end
- def table_alias_for(table_name, table_alias)
- "#{table_name} #{table_alias if table_name != table_alias}".strip
- end
-
- def table_name_and_alias
- table_alias_for table_name, @aliased_table_name
- end
-
def interpolate_sql(sql)
instance_eval("%@#{sql.gsub('@', '\@')}@", __FILE__, __LINE__)
end
private
+
+ def allocate_aliases
+ @aliased_prefix = "t#{ join_dependency.join_parts.size }"
+ @aliased_table_name = aliased_table_name_for(table_name)
+
+ if reflection.macro == :has_and_belongs_to_many
+ @aliased_join_table_name = aliased_table_name_for(reflection.options[:join_table], "_join")
+ elsif [:has_many, :has_one].include?(reflection.macro) && reflection.options[:through]
+ @aliased_join_table_name = aliased_table_name_for(reflection.through_reflection.klass.table_name, "_join")
+ end
+ end
+
+ def process_conditions(conditions, table_name)
+ Arel.sql(interpolate_sql(sanitize_sql(conditions, table_name)))
+ end
+
+ def join_target_table(relation, *conditions)
+ relation = relation.join(target_table, join_type)
+
+ # If the target table is an STI model then we must be sure to only include records of
+ # its type and its sub-types.
+ unless active_record.descends_from_active_record?
+ sti_column = target_table[active_record.inheritance_column]
+
+ sti_condition = sti_column.eq(active_record.sti_name)
+ active_record.descendants.each do |subclass|
+ sti_condition = sti_condition.or(sti_column.eq(subclass.sti_name))
+ end
+
+ conditions << sti_condition
+ end
+
+ # If the reflection has conditions, add them
+ if options[:conditions]
+ conditions << process_conditions(options[:conditions], aliased_table_name)
+ end
+
+ relation = relation.on(*conditions)
+ end
- def build_has_and_belongs_to_many(aliased_table, parent_table)
- join_table = Arel::Table.new(options[:join_table], :as => aliased_join_table_name, :engine => arel_engine)
- fk = options[:foreign_key] || reflection.active_record.to_s.foreign_key
- klass_fk = options[:association_foreign_key] || klass.to_s.foreign_key
-
- [
- join_table[fk].eq(parent_table[reflection.active_record.primary_key]),
- aliased_table[klass.primary_key].eq(join_table[klass_fk])
- ]
+ def join_has_and_belongs_to_many_to(relation)
+ join_table = Arel::Table.new(
+ options[:join_table], :engine => arel_engine,
+ :as => @aliased_join_table_name
+ )
+
+ fk = options[:foreign_key] || reflection.active_record.to_s.foreign_key
+ klass_fk = options[:association_foreign_key] || reflection.klass.to_s.foreign_key
+
+ relation = relation.join(join_table, join_type)
+ relation = relation.on(
+ join_table[fk].
+ eq(parent_table[reflection.active_record.primary_key])
+ )
+
+ join_target_table(
+ relation,
+ target_table[reflection.klass.primary_key].
+ eq(join_table[klass_fk])
+ )
end
- def build_has_many(aliased_table, parent_table)
+ def join_has_many_to(relation)
if reflection.options[:through]
- join_table = Arel::Table.new(through_reflection.klass.table_name,
- :as => aliased_join_table_name,
- :engine => arel_engine)
- jt_foreign_key = jt_as_extra = jt_source_extra = jt_sti_extra = nil
- first_key = second_key = nil
-
- if through_reflection.options[:as] # has_many :through against a polymorphic join
- as_key = through_reflection.options[:as].to_s
- jt_foreign_key = as_key + '_id'
- jt_as_extra = join_table[as_key + '_type'].eq(parent.active_record.base_class.name)
- else
- jt_foreign_key = through_reflection.primary_key_name
- end
-
- case source_reflection.macro
- when :has_many
- second_key = options[:foreign_key] || primary_key
+ join_has_many_through_to(relation)
+ elsif reflection.options[:as]
+ join_has_many_polymorphic_to(relation)
+ else
+ foreign_key = options[:foreign_key] || reflection.active_record.name.foreign_key
+ primary_key = options[:primary_key] || parent.primary_key
+
+ join_target_table(
+ relation,
+ target_table[foreign_key].
+ eq(parent_table[primary_key])
+ )
+ end
+ end
+ alias :join_has_one_to :join_has_many_to
+
+ def join_has_many_through_to(relation)
+ join_table = Arel::Table.new(
+ through_reflection.klass.table_name, :engine => arel_engine,
+ :as => @aliased_join_table_name
+ )
+
+ jt_conditions = []
+ jt_foreign_key = first_key = second_key = nil
+
+ if through_reflection.options[:as] # has_many :through against a polymorphic join
+ as_key = through_reflection.options[:as].to_s
+ jt_foreign_key = as_key + '_id'
+
+ jt_conditions <<
+ join_table[as_key + '_type'].
+ eq(parent.active_record.base_class.name)
+ else
+ jt_foreign_key = through_reflection.primary_key_name
+ end
- if source_reflection.options[:as]
- first_key = "#{source_reflection.options[:as]}_id"
- else
- first_key = through_reflection.klass.base_class.to_s.foreign_key
- end
+ case source_reflection.macro
+ when :has_many
+ second_key = options[:foreign_key] || primary_key
- unless through_reflection.klass.descends_from_active_record?
- jt_sti_extra = join_table[through_reflection.active_record.inheritance_column].eq(through_reflection.klass.sti_name)
- end
- when :belongs_to
- first_key = primary_key
- if reflection.options[:source_type]
- second_key = source_reflection.association_foreign_key
- jt_source_extra = join_table[reflection.source_reflection.options[:foreign_type]].eq(reflection.options[:source_type])
- else
- second_key = source_reflection.primary_key_name
- end
+ if source_reflection.options[:as]
+ first_key = "#{source_reflection.options[:as]}_id"
+ else
+ first_key = through_reflection.klass.base_class.to_s.foreign_key
end
- [
- [parent_table[parent.primary_key].eq(join_table[jt_foreign_key]), jt_as_extra, jt_source_extra, jt_sti_extra].compact,
- aliased_table[first_key].eq(join_table[second_key])
- ]
- elsif reflection.options[:as]
- id_rel = aliased_table["#{reflection.options[:as]}_id"].eq(parent_table[parent.primary_key])
- type_rel = aliased_table["#{reflection.options[:as]}_type"].eq(parent.active_record.base_class.name)
- [id_rel, type_rel]
- else
- foreign_key = options[:foreign_key] || reflection.active_record.name.foreign_key
- [aliased_table[foreign_key].eq(parent_table[reflection.options[:primary_key] || parent.primary_key])]
+ unless through_reflection.klass.descends_from_active_record?
+ jt_conditions <<
+ join_table[through_reflection.active_record.inheritance_column].
+ eq(through_reflection.klass.sti_name)
+ end
+ when :belongs_to
+ first_key = primary_key
+
+ if reflection.options[:source_type]
+ second_key = source_reflection.association_foreign_key
+
+ jt_conditions <<
+ join_table[reflection.source_reflection.options[:foreign_type]].
+ eq(reflection.options[:source_type])
+ else
+ second_key = source_reflection.primary_key_name
+ end
+ end
+
+ jt_conditions <<
+ parent_table[parent.primary_key].
+ eq(join_table[jt_foreign_key])
+
+ if through_reflection.options[:conditions]
+ jt_conditions << process_conditions(through_reflection.options[:conditions], aliased_table_name)
end
+
+ relation = relation.join(join_table, join_type).on(*jt_conditions)
+
+ join_target_table(
+ relation,
+ target_table[first_key].eq(join_table[second_key])
+ )
+ end
+
+ def join_has_many_polymorphic_to(relation)
+ join_target_table(
+ relation,
+ target_table["#{reflection.options[:as]}_id"].
+ eq(parent_table[parent.primary_key]),
+ target_table["#{reflection.options[:as]}_type"].
+ eq(parent.active_record.base_class.name)
+ )
end
- alias :build_has_one :build_has_many
- def build_belongs_to(aliased_table, parent_table)
- [aliased_table[options[:primary_key] || reflection.klass.primary_key].eq(parent_table[options[:foreign_key] || reflection.primary_key_name])]
+ def join_belongs_to_to(relation)
+ foreign_key = options[:foreign_key] || reflection.primary_key_name
+ primary_key = options[:primary_key] || reflection.klass.primary_key
+
+ join_target_table(
+ relation,
+ target_table[primary_key].eq(parent_table[foreign_key])
+ )
end
end
end
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index ff6be4ff19..78b3507dd9 100644
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -423,7 +423,7 @@ module ActiveRecord #:nodoc:
class << self # Class methods
delegate :find, :first, :last, :all, :destroy, :destroy_all, :exists?, :delete, :delete_all, :update, :update_all, :to => :scoped
delegate :find_each, :find_in_batches, :to => :scoped
- delegate :select, :group, :order, :reorder, :limit, :offset, :joins, :where, :preload, :eager_load, :includes, :from, :lock, :readonly, :having, :create_with, :to => :scoped
+ delegate :select, :group, :order, :except, :limit, :offset, :joins, :where, :preload, :eager_load, :includes, :from, :lock, :readonly, :having, :create_with, :to => :scoped
delegate :count, :average, :minimum, :maximum, :sum, :calculate, :to => :scoped
# Executes a custom SQL query against your database and returns all the results. The results will
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
index e2b3773a99..a7a12faac2 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
@@ -10,28 +10,31 @@ module ActiveRecord
return value.quoted_id if value.respond_to?(:quoted_id)
case value
- when String, ActiveSupport::Multibyte::Chars
- value = value.to_s
- if column && column.type == :binary && column.class.respond_to?(:string_to_binary)
- "'#{quote_string(column.class.string_to_binary(value))}'" # ' (for ruby-mode)
- elsif column && [:integer, :float].include?(column.type)
- value = column.type == :integer ? value.to_i : value.to_f
- value.to_s
- else
- "'#{quote_string(value)}'" # ' (for ruby-mode)
- end
- when NilClass then "NULL"
- when TrueClass then (column && column.type == :integer ? '1' : quoted_true)
- when FalseClass then (column && column.type == :integer ? '0' : quoted_false)
- when Float, Fixnum, Bignum then value.to_s
- # BigDecimals need to be output in a non-normalized form and quoted.
- when BigDecimal then value.to_s('F')
+ when String, ActiveSupport::Multibyte::Chars
+ value = value.to_s
+ return "'#{quote_string(value)}'" unless column
+
+ case column.type
+ when :binary then "'#{quote_string(column.string_to_binary(value))}'"
+ when :integer then value.to_i.to_s
+ when :float then value.to_f.to_s
+ else
+ "'#{quote_string(value)}'"
+ end
+
+ when true, false
+ if column && column.type == :integer
+ value ? '1' : '0'
else
- if value.acts_like?(:date) || value.acts_like?(:time)
- "'#{quoted_date(value)}'"
- else
- "'#{quote_string(value.to_s)}'"
- end
+ value ? quoted_true : quoted_false
+ end
+ # BigDecimals need to be put in a non-normalized form and quoted.
+ when nil then "NULL"
+ when BigDecimal then value.to_s('F')
+ when Numeric then value.to_s
+ when Date, Time then "'#{quoted_date(value)}'"
+ else
+ "'#{quote_string(value.to_s)}'"
end
end
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 6480aeb171..60ccf9edf3 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
@@ -114,6 +114,11 @@ module ActiveRecord
type_cast(default)
end
+ # Used to convert from Strings to BLOBs
+ def string_to_binary(value)
+ self.class.string_to_binary(value)
+ end
+
class << self
# Used to convert from Strings to BLOBs
def string_to_binary(value)
@@ -268,6 +273,10 @@ module ActiveRecord
# for generating a number of table creation or table changing SQL statements.
class ColumnDefinition < Struct.new(:base, :name, :type, :limit, :precision, :scale, :default, :null) #:nodoc:
+ def string_to_binary(value)
+ value
+ end
+
def sql_type
base.type_to_sql(type.to_sym, limit, precision, scale) rescue type
end
diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
index e7f7b37b27..a4b336dfaf 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
@@ -132,7 +132,7 @@ module ActiveRecord
cattr_accessor :emulate_booleans
self.emulate_booleans = true
- ADAPTER_NAME = 'MySQL'.freeze
+ ADAPTER_NAME = 'MySQL'
LOST_CONNECTION_ERROR_MESSAGES = [
"Server shutdown in progress",
@@ -140,10 +140,10 @@ module ActiveRecord
"Lost connection to MySQL server during query",
"MySQL server has gone away" ]
- QUOTED_TRUE, QUOTED_FALSE = '1'.freeze, '0'.freeze
+ QUOTED_TRUE, QUOTED_FALSE = '1', '0'
NATIVE_DATABASE_TYPES = {
- :primary_key => "int(11) DEFAULT NULL auto_increment PRIMARY KEY".freeze,
+ :primary_key => "int(11) DEFAULT NULL auto_increment PRIMARY KEY",
:string => { :name => "varchar", :limit => 255 },
:text => { :name => "text" },
:integer => { :name => "int", :limit => 4 },
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index 5f14284615..dce9e99d27 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -180,8 +180,6 @@ module ActiveRecord
# <encoding></tt> call on the connection.
# * <tt>:min_messages</tt> - An optional client min messages that is used in a
# <tt>SET client_min_messages TO <min_messages></tt> call on the connection.
- # * <tt>:allow_concurrency</tt> - If true, use async query methods so Ruby threads don't deadlock;
- # otherwise, use blocking query methods.
class PostgreSQLAdapter < AbstractAdapter
class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
def xml(*args)
@@ -930,7 +928,7 @@ module ActiveRecord
PGconn.translate_results = false if PGconn.respond_to?(:translate_results=)
# Ignore async_exec and async_query when using postgres-pr.
- @async = @config[:allow_concurrency] && @connection.respond_to?(:async_exec)
+ @async = @connection.respond_to?(:async_exec)
# Money type has a fixed precision of 10 in PostgreSQL 8.2 and below, and as of
# PostgreSQL 8.3 it has a fixed precision of 19. PostgreSQLColumn.extract_precision
diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake
index 5ad440e58d..1fbc8a1d32 100644
--- a/activerecord/lib/active_record/railties/databases.rake
+++ b/activerecord/lib/active_record/railties/databases.rake
@@ -494,7 +494,7 @@ namespace :railties do
end
on_skip = Proc.new do |name, migration|
- $stderr.puts "WARNING: Migration #{migration.basename} from #{name} has been skipped. Migration with the same name already exists."
+ puts "NOTE: Migration #{migration.basename} from #{name} has been skipped. Migration with the same name already exists."
end
on_copy = Proc.new do |name, migration, old_path|
diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb
index d79ef78b4d..6bf698fe97 100644
--- a/activerecord/lib/active_record/relation/calculations.rb
+++ b/activerecord/lib/active_record/relation/calculations.rb
@@ -177,7 +177,7 @@ module ActiveRecord
distinct = options[:distinct] || distinct
if @group_values.any?
- execute_grouped_calculation(operation, column_name)
+ execute_grouped_calculation(operation, column_name, distinct)
else
execute_simple_calculation(operation, column_name, distinct)
end
@@ -191,19 +191,23 @@ module ActiveRecord
end
end
+ def operation_over_aggregate_column(column, operation, distinct)
+ operation == 'count' ? column.count(distinct) : column.send(operation)
+ end
+
def execute_simple_calculation(operation, column_name, distinct) #:nodoc:
column = aggregate_column(column_name)
# Postgresql doesn't like ORDER BY when there are no GROUP BY
relation = except(:order)
- select_value = operation == 'count' ? column.count(distinct) : column.send(operation)
+ select_value = operation_over_aggregate_column(column, operation, distinct)
relation.select_values = [select_value]
type_cast_calculated_value(@klass.connection.select_value(relation.to_sql), column_for(column_name), operation)
end
- def execute_grouped_calculation(operation, column_name) #:nodoc:
+ def execute_grouped_calculation(operation, column_name, distinct) #:nodoc:
group_attr = @group_values.first
association = @klass.reflect_on_association(group_attr.to_sym)
associated = association && association.macro == :belongs_to # only count belongs_to associations
@@ -221,7 +225,7 @@ module ActiveRecord
relation = except(:group).group(group)
relation.select_values = [
- aggregate_column(column_name).send(operation).as(aggregate_alias),
+ operation_over_aggregate_column(aggregate_column(column_name), operation, distinct).as(aggregate_alias),
"#{group_field} AS #{group_alias}"
]
@@ -273,7 +277,7 @@ module ActiveRecord
else type_cast_using_column(value, column)
end
else
- value
+ type_cast_using_column(value, column)
end
end
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index ede1c8821e..b763e22ec6 100644
--- a/activerecord/lib/active_record/relation/finder_methods.rb
+++ b/activerecord/lib/active_record/relation/finder_methods.rb
@@ -291,8 +291,8 @@ module ActiveRecord
record = where(primary_key.eq(id)).first
unless record
- conditions = arel.wheres.map { |x| x.value }.join(', ')
- conditions = " [WHERE #{conditions}]" if conditions.present?
+ conditions = arel.where_sql
+ conditions = " [#{conditions}]" if conditions
raise RecordNotFound, "Couldn't find #{@klass.name} with ID=#{id}#{conditions}"
end
@@ -343,8 +343,11 @@ module ActiveRecord
end
def column_aliases(join_dependency)
- join_dependency.joins.collect{|join| join.column_names_with_alias.collect{|column_name, aliased_name|
- "#{connection.quote_table_name join.aliased_table_name}.#{connection.quote_column_name column_name} AS #{aliased_name}"}}.flatten.join(", ")
+ join_dependency.join_parts.collect { |join_part|
+ join_part.column_names_with_alias.collect{ |column_name, aliased_name|
+ "#{connection.quote_table_name join_part.aliased_table_name}.#{connection.quote_column_name column_name} AS #{aliased_name}"
+ }
+ }.flatten.join(", ")
end
def using_limitable_reflections?(reflections)
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index 001207514d..59ce76ea42 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -6,7 +6,7 @@ module ActiveRecord
extend ActiveSupport::Concern
attr_accessor :includes_values, :eager_load_values, :preload_values,
- :select_values, :group_values, :order_values, :reorder_flag, :joins_values, :where_values, :having_values,
+ :select_values, :group_values, :order_values, :joins_values, :where_values, :having_values,
:limit_value, :offset_value, :lock_value, :readonly_value, :create_with_value, :from_value
def includes(*args)
@@ -53,15 +53,6 @@ module ActiveRecord
relation
end
- def reorder(*args)
- relation = clone
- unless args.blank?
- relation.order_values = args
- relation.reorder_flag = true
- end
- relation
- end
-
def joins(*args)
relation = clone
@@ -230,19 +221,8 @@ module ActiveRecord
@implicit_readonly = true unless association_joins.empty? && stashed_association_joins.empty?
- to_join = []
-
join_dependency.join_associations.each do |association|
- if (association_relation = association.relation).is_a?(Array)
- to_join << [association_relation.first, association.join_class, association.association_join.first]
- to_join << [association_relation.last, association.join_class, association.association_join.last]
- else
- to_join << [association_relation, association.join_class, association.association_join]
- end
- end
-
- to_join.uniq.each do |left, join_class, right|
- relation = relation.join(left, join_class).on(*right)
+ relation = association.join_to(relation)
end
relation.join(custom_joins)
diff --git a/activerecord/lib/active_record/relation/spawn_methods.rb b/activerecord/lib/active_record/relation/spawn_methods.rb
index 9ecdb99bee..dcddc3dac4 100644
--- a/activerecord/lib/active_record/relation/spawn_methods.rb
+++ b/activerecord/lib/active_record/relation/spawn_methods.rb
@@ -19,20 +19,11 @@ module ActiveRecord
end
end
- (Relation::MULTI_VALUE_METHODS - [:joins, :where, :order]).each do |method|
+ (Relation::MULTI_VALUE_METHODS - [:joins, :where]).each do |method|
value = r.send(:"#{method}_values")
merged_relation.send(:"#{method}_values=", merged_relation.send(:"#{method}_values") + value) if value.present?
end
- order_value = r.order_values
- if order_value.present?
- if r.reorder_flag
- merged_relation.order_values = order_value
- else
- merged_relation.order_values = merged_relation.order_values + order_value
- end
- end
-
merged_relation = merged_relation.joins(r.joins_values)
merged_wheres = @where_values
diff --git a/activerecord/lib/active_record/test_case.rb b/activerecord/lib/active_record/test_case.rb
index 31b5a8651b..014a900c71 100644
--- a/activerecord/lib/active_record/test_case.rb
+++ b/activerecord/lib/active_record/test_case.rb
@@ -36,21 +36,6 @@ module ActiveRecord
assert_queries(0, &block)
end
- def self.use_concurrent_connections
- setup :connection_allow_concurrency_setup
- teardown :connection_allow_concurrency_teardown
- end
-
- def connection_allow_concurrency_setup
- @connection = ActiveRecord::Base.remove_connection
- ActiveRecord::Base.establish_connection(@connection.merge({:allow_concurrency => true}))
- end
-
- def connection_allow_concurrency_teardown
- ActiveRecord::Base.clear_all_connections!
- ActiveRecord::Base.establish_connection(@connection)
- end
-
def with_kcode(kcode)
if RUBY_VERSION < '1.9'
orig_kcode, $KCODE = $KCODE, kcode