diff options
Diffstat (limited to 'activerecord')
-rw-r--r-- | activerecord/lib/active_record.rb | 1 | ||||
-rwxr-xr-x | activerecord/lib/active_record/associations.rb | 2 | ||||
-rw-r--r-- | activerecord/lib/active_record/associations/association_proxy.rb | 2 | ||||
-rw-r--r-- | activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb | 2 | ||||
-rwxr-xr-x | activerecord/lib/active_record/base.rb | 38 | ||||
-rw-r--r-- | activerecord/lib/active_record/railtie.rb (renamed from activerecord/lib/active_record/rails.rb) | 6 | ||||
-rw-r--r-- | activerecord/lib/active_record/relation.rb | 15 | ||||
-rw-r--r-- | activerecord/lib/active_record/relation/finder_methods.rb | 8 | ||||
-rw-r--r-- | activerecord/lib/active_record/relation/predicate_builder.rb | 45 | ||||
-rw-r--r-- | activerecord/lib/active_record/relation/query_methods.rb | 15 |
10 files changed, 93 insertions, 41 deletions
diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb index a524dc50a1..cf439b0dc0 100644 --- a/activerecord/lib/active_record.rb +++ b/activerecord/lib/active_record.rb @@ -54,6 +54,7 @@ module ActiveRecord autoload :QueryMethods autoload :FinderMethods autoload :CalculationMethods + autoload :PredicateBuilder end autoload :Base diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index f0bad6c3ba..052197f7ad 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1488,7 +1488,7 @@ module ActiveRecord dependent_conditions = [] dependent_conditions << "#{reflection.primary_key_name} = \#{record.#{reflection.name}.send(:owner_quoted_id)}" dependent_conditions << "#{reflection.options[:as]}_type = '#{base_class.name}'" if reflection.options[:as] - dependent_conditions << sanitize_sql(reflection.options[:conditions], reflection.quoted_table_name) if reflection.options[:conditions] + dependent_conditions << sanitize_sql(reflection.options[:conditions], reflection.table_name) if reflection.options[:conditions] dependent_conditions << extra_conditions if extra_conditions dependent_conditions = dependent_conditions.collect {|where| "(#{where})" }.join(" AND ") dependent_conditions = dependent_conditions.gsub('@', '\@') diff --git a/activerecord/lib/active_record/associations/association_proxy.rb b/activerecord/lib/active_record/associations/association_proxy.rb index 7d8f4670fa..022dd2ae9b 100644 --- a/activerecord/lib/active_record/associations/association_proxy.rb +++ b/activerecord/lib/active_record/associations/association_proxy.rb @@ -161,7 +161,7 @@ module ActiveRecord end # Forwards the call to the reflection class. - def sanitize_sql(sql, table_name = @reflection.klass.quoted_table_name) + def sanitize_sql(sql, table_name = @reflection.klass.table_name) @reflection.klass.send(:sanitize_sql, sql, table_name) 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 9569b0c6f9..5913189c98 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 @@ -73,7 +73,7 @@ module ActiveRecord relation = arel_table(@reflection.options[:join_table]) relation.where(relation[@reflection.primary_key_name].eq(@owner.id). and(Arel::Predicates::In.new(relation[@reflection.association_foreign_key], records.map(&:id))) - ).delete + ).delete_all end end diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index c4bdbdad08..fb160dea9a 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -815,8 +815,8 @@ module ActiveRecord #:nodoc: # # # Delete multiple rows # Todo.delete([2,3,4]) - def delete(id) - delete_all([ "#{connection.quote_column_name(primary_key)} IN (?)", id ]) + def delete(id_or_array) + arel_table.where(construct_conditions(nil, scope(:find))).delete(id_or_array) end # Destroy an object (or multiple objects) that has the given id, the object is instantiated first, @@ -1508,6 +1508,10 @@ module ActiveRecord #:nodoc: Relation.new(self, Arel::Table.new(table || table_name)) end + def engine + @engine ||= Arel::Sql::Engine.new(self) + end + private # Finder methods must instantiate through this method to work with the # single-table inheritance model that makes it possible to create @@ -1964,7 +1968,7 @@ module ActiveRecord #:nodoc: # ["name='%s' and group_id='%s'", "foo'bar", 4] returns "name='foo''bar' and group_id='4'" # { :name => "foo'bar", :group_id => 4 } returns "name='foo''bar' and group_id='4'" # "name='foo''bar' and group_id='4'" returns "name='foo''bar' and group_id='4'" - def sanitize_sql_for_conditions(condition, table_name = quoted_table_name) + def sanitize_sql_for_conditions(condition, table_name = self.table_name) return nil if condition.blank? case condition @@ -2035,30 +2039,12 @@ module ActiveRecord #:nodoc: # And for value objects on a composed_of relationship: # { :address => Address.new("123 abc st.", "chicago") } # # => "address_street='123 abc st.' and address_city='chicago'" - def sanitize_sql_hash_for_conditions(attrs, default_table_name = quoted_table_name) + def sanitize_sql_hash_for_conditions(attrs, default_table_name = self.table_name) attrs = expand_hash_conditions_for_aggregates(attrs) - conditions = attrs.map do |attr, value| - table_name = default_table_name - - unless value.is_a?(Hash) - attr = attr.to_s - - # Extract table name from qualified attribute names. - if attr.include?('.') - attr_table_name, attr = attr.split('.', 2) - attr_table_name = connection.quote_table_name(attr_table_name) - else - attr_table_name = table_name - end - - attribute_condition("#{attr_table_name}.#{connection.quote_column_name(attr)}", value) - else - sanitize_sql_hash_for_conditions(value, connection.quote_table_name(attr.to_s)) - end - end.join(' AND ') - - replace_bind_variables(conditions, expand_range_bind_variables(attrs.values)) + table = Arel::Table.new(default_table_name, engine) + builder = PredicateBuilder.new(engine) + builder.build_from_hash(attrs, table).map(&:to_sql).join(' AND ') end alias_method :sanitize_sql_hash, :sanitize_sql_hash_for_conditions @@ -2323,7 +2309,7 @@ module ActiveRecord #:nodoc: # be made (since they can't be persisted). def destroy unless new_record? - self.class.arel_table.where(self.class.arel_table[self.class.primary_key].eq(id)).delete + self.class.arel_table.where(self.class.arel_table[self.class.primary_key].eq(id)).delete_all end @destroyed = true diff --git a/activerecord/lib/active_record/rails.rb b/activerecord/lib/active_record/railtie.rb index a13bd2a5da..657ee738c0 100644 --- a/activerecord/lib/active_record/rails.rb +++ b/activerecord/lib/active_record/railtie.rb @@ -2,10 +2,12 @@ # rails, so let's make sure that it gets required before # here. This is needed for correctly setting up the middleware. # In the future, this might become an optional require. -require "action_controller/rails" +require "active_record" +require "action_controller/railtie" +require "rails" module ActiveRecord - class Plugin < Rails::Plugin + class Railtie < Rails::Railtie plugin_name :active_record rake_tasks do diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index ae03e1d7e9..6a1237cdd1 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -6,7 +6,7 @@ module ActiveRecord delegate :length, :collect, :map, :each, :all?, :to => :to_a attr_reader :relation, :klass, :preload_associations, :eager_load_associations - attr_writer :readonly, :preload_associations, :eager_load_associations + attr_writer :readonly, :preload_associations, :eager_load_associations, :table def initialize(klass, relation) @klass, @relation = klass, relation @@ -109,6 +109,10 @@ module ActiveRecord @relation.delete.tap { reset } end + def delete(id_or_array) + where(@klass.primary_key => id_or_array).delete_all + end + def loaded? @loaded end @@ -129,9 +133,18 @@ module ActiveRecord relation.readonly = @readonly relation.preload_associations = @preload_associations relation.eager_load_associations = @eager_load_associations + relation.table = table relation end + def table + @table ||= Arel::Table.new(@klass.table_name, Arel::Sql::Engine.new(@klass)) + end + + def primary_key + @primary_key ||= table[@klass.primary_key] + end + protected def method_missing(method, *args, &block) diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index 7a1d6fc538..c3e5f27838 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -21,8 +21,8 @@ module ActiveRecord end def exists?(id = nil) - relation = select("#{@klass.quoted_table_name}.#{@klass.primary_key}").limit(1) - relation = relation.where(@klass.primary_key => id) if id + relation = select(primary_key).limit(1) + relation = relation.where(primary_key.eq(id)) if id relation.first ? true : false end @@ -78,7 +78,7 @@ module ActiveRecord end def find_one(id) - record = where(@klass.primary_key => id).first + record = where(primary_key.eq(id)).first unless record conditions = where_clause(', ') @@ -90,7 +90,7 @@ module ActiveRecord end def find_some(ids) - result = where(@klass.primary_key => ids).all + result = where(primary_key.in(ids)).all expected_size = if @relation.taken && ids.size > @relation.taken diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb new file mode 100644 index 0000000000..d5e0c90184 --- /dev/null +++ b/activerecord/lib/active_record/relation/predicate_builder.rb @@ -0,0 +1,45 @@ +module ActiveRecord + class PredicateBuilder + + def initialize(engine) + @engine = engine + end + + def build_from_hash(attributes, default_table) + predicates = attributes.map do |column, value| + arel_table = default_table + + if value.is_a?(Hash) + arel_table = Arel::Table.new(column, @engine) + build_from_hash(value, arel_table) + else + column = column.to_s + + if column.include?('.') + table_name, column = column.split('.', 2) + arel_table = Arel::Table.new(table_name, @engine) + end + + attribute = arel_table[column] || Arel::Attribute.new(arel_table, column.to_sym) + + case value + when Array, ActiveRecord::Associations::AssociationCollection, ActiveRecord::NamedScope::Scope + attribute.in(value) + when Range + # TODO : Arel should handle ranges with excluded end. + if value.exclude_end? + [attribute.gteq(value.begin), attribute.lt(value.end)] + else + attribute.in(value) + end + else + attribute.eq(value) + end + end + end + + predicates.flatten + end + + end +end diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 631c80da25..432b33e174 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -104,14 +104,19 @@ module ActiveRecord def where(*args) return spawn if args.blank? - if [String, Hash, Array].include?(args.first.class) - conditions = @klass.send(:merge_conditions, args.size > 1 ? Array.wrap(args) : args.first) - conditions = Arel::SqlLiteral.new(conditions) if conditions + builder = PredicateBuilder.new(Arel::Sql::Engine.new(@klass)) + + conditions = if [String, Array].include?(args.first.class) + merged = @klass.send(:merge_conditions, args.size > 1 ? Array.wrap(args) : args.first) + Arel::SqlLiteral.new(merged) if merged + elsif args.first.is_a?(Hash) + attributes = @klass.send(:expand_hash_conditions_for_aggregates, args.first) + builder.build_from_hash(attributes, table) else - conditions = args.first + args.first end - spawn(@relation.where(conditions)) + spawn(@relation.where(*conditions)) end private |