From d8f99c36bae411dfde28ed18537db7671a57ceb9 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Thu, 23 Apr 2009 13:55:42 -0300 Subject: Added Arel integration to migration's version update table --- activerecord/lib/active_record/migration.rb | 50 ++++++++++++++--------------- 1 file changed, 25 insertions(+), 25 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index 657acd6dc0..8cb5febbb6 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -103,7 +103,7 @@ module ActiveRecord # # The Rails package has several tools to help create and apply migrations. # - # To generate a new migration, you can use + # To generate a new migration, you can use # script/generate migration MyNewMigration # # where MyNewMigration is the name of your migration. The generator will @@ -121,16 +121,16 @@ module ActiveRecord # def self.up # add_column :tablenames, :fieldname, :string # end - # + # # def self.down # remove_column :tablenames, :fieldname # end # end - # + # # To run migrations against the currently configured database, use # rake db:migrate. This will update the database by running all of the # pending migrations, creating the schema_migrations table - # (see "About the schema_migrations table" section below) if missing. It will also + # (see "About the schema_migrations table" section below) if missing. It will also # invoke the db:schema:dump task, which will update your db/schema.rb file # to match the structure of your database. # @@ -240,7 +240,7 @@ module ActiveRecord # lower than the current schema version: when migrating up, those # never-applied "interleaved" migrations will be automatically applied, and # when migrating down, never-applied "interleaved" migrations will be skipped. - # + # # == Timestamped Migrations # # By default, Rails generates migrations that look like: @@ -253,7 +253,7 @@ module ActiveRecord # off by setting: # # config.active_record.timestamped_migrations = false - # + # # In environment.rb. # class Migration @@ -389,9 +389,9 @@ module ActiveRecord def rollback(migrations_path, steps=1) migrator = self.new(:down, migrations_path) start_index = migrator.migrations.index(migrator.current_migration) - + return unless start_index - + finish = migrator.migrations[start_index + steps] down(migrations_path, finish ? finish.version : 0) end @@ -403,7 +403,7 @@ module ActiveRecord def down(migrations_path, target_version = nil) self.new(:down, migrations_path, target_version).migrate end - + def run(direction, migrations_path, target_version) self.new(direction, migrations_path, target_version).run end @@ -434,17 +434,17 @@ module ActiveRecord def initialize(direction, migrations_path, target_version = nil) raise StandardError.new("This database does not yet support migrations") unless Base.connection.supports_migrations? Base.connection.initialize_schema_migrations_table - @direction, @migrations_path, @target_version = direction, migrations_path, target_version + @direction, @migrations_path, @target_version = direction, migrations_path, target_version end def current_version migrated.last || 0 end - + def current_migration migrations.detect { |m| m.version == current_version } end - + def run target = migrations.detect { |m| m.version == @target_version } raise UnknownMigrationVersionError.new(@target_version) if target.nil? @@ -461,14 +461,14 @@ module ActiveRecord if target.nil? && !@target_version.nil? && @target_version > 0 raise UnknownMigrationVersionError.new(@target_version) end - + start = up? ? 0 : (migrations.index(current) || 0) finish = migrations.index(target) || migrations.size - 1 runnable = migrations[start..finish] - + # skip the last migration if we're headed down, but not ALL the way down runnable.pop if down? && !target.nil? - + runnable.each do |migration| Base.logger.info "Migrating to #{migration.name} (#{migration.version})" @@ -496,28 +496,28 @@ module ActiveRecord def migrations @migrations ||= begin files = Dir["#{@migrations_path}/[0-9]*_*.rb"] - + migrations = files.inject([]) do |klasses, file| version, name = file.scan(/([0-9]+)_([_a-z0-9]*).rb/).first - + raise IllegalMigrationNameError.new(file) unless version version = version.to_i - + if klasses.detect { |m| m.version == version } - raise DuplicateMigrationVersionError.new(version) + raise DuplicateMigrationVersionError.new(version) end if klasses.detect { |m| m.name == name.camelize } - raise DuplicateMigrationNameError.new(name.camelize) + raise DuplicateMigrationNameError.new(name.camelize) end - + klasses << returning(MigrationProxy.new) do |migration| migration.name = name.camelize migration.version = version migration.filename = file end end - + migrations = migrations.sort_by(&:version) down? ? migrations.reverse : migrations end @@ -534,15 +534,15 @@ module ActiveRecord private def record_version_state_after_migrating(version) - sm_table = self.class.schema_migrations_table_name + table = Arel(self.class.schema_migrations_table_name) @migrated_versions ||= [] if down? @migrated_versions.delete(version.to_i) - Base.connection.update("DELETE FROM #{sm_table} WHERE version = '#{version}'") + table.where(table["version"].eq(version)).delete else @migrated_versions.push(version.to_i).sort! - Base.connection.insert("INSERT INTO #{sm_table} (version) VALUES ('#{version}')") + table.insert table["version"] => version end end -- cgit v1.2.3 From 7958308ef66aece2d24ab1b884f4facb4751da70 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Thu, 23 Apr 2009 18:54:04 -0300 Subject: More progress on migrations. Arel updated. --- activerecord/lib/active_record/migration.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index 8cb5febbb6..c8db31700b 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -413,7 +413,8 @@ module ActiveRecord end def get_all_versions - Base.connection.select_values("SELECT version FROM #{schema_migrations_table_name}").map(&:to_i).sort + table = Arel(schema_migrations_table_name) + table.project(table['version']).select_values.map(&:to_i).sort end def current_version -- cgit v1.2.3 From e428c75d2b67c6a7bd5f5e7e1719cdece84d497f Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Thu, 23 Apr 2009 18:59:35 -0300 Subject: Remove connection method definition, since it's called just once. --- activerecord/lib/active_record/migration.rb | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index c8db31700b..aa9e9e8fdd 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -338,10 +338,6 @@ module ActiveRecord self.verbose = save end - def connection - ActiveRecord::Base.connection - end - def method_missing(method, *arguments, &block) arg_list = arguments.map(&:inspect) * ', ' @@ -349,7 +345,7 @@ module ActiveRecord unless arguments.empty? || method == :execute arguments[0] = Migrator.proper_table_name(arguments.first) end - connection.send(method, *arguments, &block) + Base.connection.send(method, *arguments, &block) end end end -- cgit v1.2.3 From 345e686d83c54fec6cb53cf77dea62d640a31954 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Fri, 24 Apr 2009 17:27:05 -0300 Subject: Changed locking to use Arel. Arel updated --- .../lib/active_record/locking/optimistic.rb | 33 +++++++++++++--------- 1 file changed, 19 insertions(+), 14 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb index 7fa7e267d8..ab0f5fc58d 100644 --- a/activerecord/lib/active_record/locking/optimistic.rb +++ b/activerecord/lib/active_record/locking/optimistic.rb @@ -89,14 +89,19 @@ module ActiveRecord attribute_names.uniq! begin - affected_rows = connection.update(<<-end_sql, "#{self.class.name} Update with optimistic locking") - UPDATE #{self.class.quoted_table_name} - SET #{quoted_comma_pair_list(connection, attributes_with_quotes(false, false, attribute_names))} - WHERE #{self.class.primary_key} = #{quote_value(id)} - AND #{self.class.quoted_locking_column} = #{quote_value(previous_value)} - end_sql - - unless affected_rows == 1 + table = Arel(self.class.table_name) + affected_rows = table.where( + table[self.class.primary_key].eq(quoted_id).and( + table[self.class.locking_column].eq(quote_value(previous_value)) + ) + ) + + attributes = {} + attributes_with_quotes(false, false, attribute_names).map { |k,v| + attributes.merge!(table[k] => v) + } + + unless affected_rows.update(attributes) == 1 raise ActiveRecord::StaleObjectError, "Attempted to update a stale object" end @@ -116,12 +121,12 @@ module ActiveRecord lock_col = self.class.locking_column previous_value = send(lock_col).to_i - affected_rows = connection.delete( - "DELETE FROM #{self.class.quoted_table_name} " + - "WHERE #{connection.quote_column_name(self.class.primary_key)} = #{quoted_id} " + - "AND #{self.class.quoted_locking_column} = #{quote_value(previous_value)}", - "#{self.class.name} Destroy" - ) + table = Arel(self.class.table_name, connection) + affected_rows = table.where( + table[self.class.primary_key].eq(quoted_id).and( + table[self.class.locking_column].eq(quote_value(previous_value)) + ) + ).delete unless affected_rows == 1 raise ActiveRecord::StaleObjectError, "Attempted to delete a stale object" -- cgit v1.2.3 From 0e113a040d934958ce3805ce6cda73c655def444 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Fri, 24 Apr 2009 17:32:16 -0300 Subject: Refactored locking update --- activerecord/lib/active_record/locking/optimistic.rb | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb index ab0f5fc58d..1251e9f013 100644 --- a/activerecord/lib/active_record/locking/optimistic.rb +++ b/activerecord/lib/active_record/locking/optimistic.rb @@ -90,18 +90,20 @@ module ActiveRecord begin table = Arel(self.class.table_name) - affected_rows = table.where( - table[self.class.primary_key].eq(quoted_id).and( - table[self.class.locking_column].eq(quote_value(previous_value)) - ) - ) attributes = {} attributes_with_quotes(false, false, attribute_names).map { |k,v| attributes.merge!(table[k] => v) } - unless affected_rows.update(attributes) == 1 + affected_rows = table.where( + table[self.class.primary_key].eq(quoted_id).and( + table[self.class.locking_column].eq(quote_value(previous_value)) + ) + ).update(attributes) + + + unless affected_rows == 1 raise ActiveRecord::StaleObjectError, "Attempted to update a stale object" end @@ -121,7 +123,7 @@ module ActiveRecord lock_col = self.class.locking_column previous_value = send(lock_col).to_i - table = Arel(self.class.table_name, connection) + table = Arel(self.class.table_name) affected_rows = table.where( table[self.class.primary_key].eq(quoted_id).and( table[self.class.locking_column].eq(quote_value(previous_value)) -- cgit v1.2.3 From 090539604b7685d838302c1773520622d87bd3d7 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Fri, 24 Apr 2009 21:48:45 -0300 Subject: construct_finder_sql now use Arel --- activerecord/lib/active_record/base.rb | 107 +++++++++++++++++---- .../connection_adapters/mysql_adapter.rb | 2 +- activerecord/lib/active_record/test_case.rb | 2 +- 3 files changed, 88 insertions(+), 23 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 97c36a675d..8bf92de6d4 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -390,7 +390,7 @@ module ActiveRecord #:nodoc: # So it's possible to assign a logger to the class through Base.logger= which will then be used by all # instances in the current object space. class Base - ## + ## # :singleton-method: # Accepts a logger conforming to the interface of Log4r or the default Ruby 1.8+ Logger class, which is then passed # on to any new database connections made and which can be retrieved on both a class and instance level by calling +logger+. @@ -424,11 +424,11 @@ module ActiveRecord #:nodoc: # as a Hash. # # For example, the following database.yml... - # + # # development: # adapter: sqlite3 # database: db/development.sqlite3 - # + # # production: # adapter: sqlite3 # database: db/production.sqlite3 @@ -1351,7 +1351,7 @@ module ActiveRecord #:nodoc: def self_and_descendants_from_active_record#nodoc: klass = self classes = [klass] - while klass != klass.base_class + while klass != klass.base_class classes << klass = klass.superclass end classes @@ -1385,7 +1385,7 @@ module ActiveRecord #:nodoc: def human_name(options = {}) defaults = self_and_descendants_from_active_record.map do |klass| :"#{klass.name.underscore}" - end + end defaults << self.name.humanize I18n.translate(defaults.shift, {:scope => [:activerecord, :models], :count => 1, :default => defaults}.merge(options)) end @@ -1689,20 +1689,85 @@ module ActiveRecord #:nodoc: def construct_finder_sql(options) scope = scope(:find) - sql = "SELECT #{options[:select] || (scope && scope[:select]) || default_select(options[:joins] || (scope && scope[:joins]))} " - sql << "FROM #{options[:from] || (scope && scope[:from]) || quoted_table_name} " - add_joins!(sql, options[:joins], scope) - add_conditions!(sql, options[:conditions], scope) + # TODO add lock to Arel + Arel(table_name). + join(construct_join(options[:joins], scope)). + where(construct_conditions(options[:conditions], scope)). + project(options[:select] || (scope && scope[:select]) || default_select(options[:joins] || (scope && scope[:joins]))). + group(construct_group(options[:group], options[:having], scope)). + order(construct_order(options[:order], scope)). + take(construct_limit(options, scope)). + skip(construct_offset(options, scope) + ).to_sql + end - add_group!(sql, options[:group], options[:having], scope) - add_order!(sql, options[:order], scope) - add_limit!(sql, options, scope) - add_lock!(sql, options, scope) + def construct_join(joins, scope = :auto) + scope = scope(:find) if :auto == scope + merged_joins = scope && scope[:joins] && joins ? merge_joins(scope[:joins], joins) : (joins || scope && scope[:joins]) + case merged_joins + when Symbol, Hash, Array + if array_of_strings?(merged_joins) + merged_joins.join(' ') + " " + else + join_dependency = ActiveRecord::Associations::ClassMethods::InnerJoinDependency.new(self, merged_joins, nil) + " #{join_dependency.join_associations.collect { |assoc| assoc.association_join }.join} " + end + when String + " #{merged_joins} " + end + end + + def construct_group(group, having, scope = :auto) + sql = '' + if group + sql << group.to_s + sql << " HAVING #{sanitize_sql_for_conditions(having)}" if having + else + scope = scope(:find) if :auto == scope + if scope && (scoped_group = scope[:group]) + sql << scoped_group.to_s + sql << " HAVING #{sanitize_sql_for_conditions(scope[:having])}" if scope[:having] + end + end + sql + end + def construct_order(order, scope = :auto) + sql = '' + scope = scope(:find) if :auto == scope + scoped_order = scope[:order] if scope + if order + sql << order.to_s + if scoped_order && scoped_order != order + sql << ", #{scoped_order}" + end + else + sql << scoped_order.to_s if scoped_order + end sql end + def construct_limit(options, scope = :auto) + scope = scope(:find) if :auto == scope + options[:limit] ||= scope[:limit] if scope + options[:limit] + end + + def construct_offset(options, scope = :auto) + scope = scope(:find) if :auto == scope + options[:offset] ||= scope[:offset] if scope + options[:offset] + end + + def construct_conditions(conditions, scope = :auto) + scope = scope(:find) if :auto == scope + conditions = [conditions] + conditions << scope[:conditions] if scope + conditions << type_condition if finder_needs_type_condition? + merge_conditions(*conditions) + end + # Merges includes so that the result is a valid +include+ def merge_includes(first, second) (safe_to_array(first) + safe_to_array(second)).uniq @@ -1958,7 +2023,7 @@ module ActiveRecord #:nodoc: attributes = construct_attributes_from_arguments( # attributes = construct_attributes_from_arguments( [:#{attribute_names.join(',:')}], args # [:user_name, :password], args ) # ) - # + # scoped(:conditions => attributes) # scoped(:conditions => attributes) end # end }, __FILE__, __LINE__ @@ -2478,7 +2543,7 @@ module ActiveRecord #:nodoc: # name # end # end - # + # # user = User.find_by_name('Phusion') # user_path(user) # => "/users/Phusion" def to_param @@ -2533,12 +2598,12 @@ module ActiveRecord #:nodoc: # If +perform_validation+ is true validations run. If any of them fail # the action is cancelled and +save+ returns +false+. If the flag is # false validations are bypassed altogether. See - # ActiveRecord::Validations for more information. + # ActiveRecord::Validations for more information. # # There's a series of callbacks associated with +save+. If any of the # before_* callbacks return +false+ the action is cancelled and # +save+ returns +false+. See ActiveRecord::Callbacks for further - # details. + # details. def save create_or_update end @@ -2550,12 +2615,12 @@ module ActiveRecord #:nodoc: # # With save! validations always run. If any of them fail # ActiveRecord::RecordInvalid gets raised. See ActiveRecord::Validations - # for more information. + # for more information. # # There's a series of callbacks associated with save!. If any of # the before_* callbacks return +false+ the action is cancelled # and save! raises ActiveRecord::RecordNotSaved. See - # ActiveRecord::Callbacks for further details. + # ActiveRecord::Callbacks for further details. def save! create_or_update || raise(RecordNotSaved) end @@ -2726,12 +2791,12 @@ module ActiveRecord #:nodoc: # class User < ActiveRecord::Base # attr_protected :is_admin # end - # + # # user = User.new # user.attributes = { :username => 'Phusion', :is_admin => true } # user.username # => "Phusion" # user.is_admin? # => false - # + # # user.send(:attributes=, { :username => 'Phusion', :is_admin => true }, false) # user.is_admin? # => true def attributes=(new_attributes, guard_protected_attributes = true) diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb index 9300df28ee..8d8df7dece 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb @@ -211,7 +211,7 @@ module ActiveRecord def supports_migrations? #:nodoc: true end - + def supports_savepoints? #:nodoc: true end diff --git a/activerecord/lib/active_record/test_case.rb b/activerecord/lib/active_record/test_case.rb index 8c6abaaccb..b790eb4343 100644 --- a/activerecord/lib/active_record/test_case.rb +++ b/activerecord/lib/active_record/test_case.rb @@ -20,7 +20,7 @@ module ActiveRecord patterns_to_match.each do |pattern| failed_patterns << pattern unless $queries_executed.any?{ |sql| pattern === sql } end - assert failed_patterns.empty?, "Query pattern(s) #{failed_patterns.map(&:inspect).join(', ')} not found." + assert failed_patterns.empty?, "Query pattern(s) #{failed_patterns.map(&:inspect).join(', ')} not found in #{$queries_executed}" end def assert_queries(num = 1) -- cgit v1.2.3 From 19d2ff83db5232a816dee201800baf3924705b31 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Wed, 29 Apr 2009 19:39:53 -0300 Subject: Calculations now use Arel to construct the query. Implemented other methods in AR::Base with Arel support. --- activerecord/lib/active_record/associations.rb | 28 ++-- activerecord/lib/active_record/base.rb | 31 ++-- activerecord/lib/active_record/calculations.rb | 197 +++++++++++-------------- activerecord/lib/active_record/test_case.rb | 2 +- 4 files changed, 123 insertions(+), 135 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 2115878e32..2dd1197192 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -510,14 +510,14 @@ module ActiveRecord # # Since only one table is loaded at a time, conditions or orders cannot reference tables other than the main one. If this is the case # Active Record falls back to the previously used LEFT OUTER JOIN based strategy. For example - # + # # Post.find(:all, :include => [ :author, :comments ], :conditions => ['comments.approved = ?', true]) # # will result in a single SQL query with joins along the lines of: LEFT OUTER JOIN comments ON comments.post_id = posts.id and # LEFT OUTER JOIN authors ON authors.id = posts.author_id. Note that using conditions like this can have unintended consequences. # 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 - # :order => "author.name DESC" will work but :order => "name DESC" will not. + # :order => "author.name DESC" will work but :order => "name DESC" will not. # # If you do want eagerload only some members of an association it is usually more natural to :include an association # which has conditions defined on it: @@ -551,10 +551,10 @@ module ActiveRecord # # Address.find(:all, :include => :addressable) # - # will execute one query to load the addresses and load the addressables with one query per addressable type. + # will execute one query to load the addresses and load the addressables with one query per addressable type. # For example if all the addressables are either of class Person or Company then a total of 3 queries will be executed. The list of # addressable types to load is determined on the back of the addresses loaded. This is not supported if Active Record has to fallback - # to the previous implementation of eager loading and will raise ActiveRecord::EagerLoadPolymorphicError. The reason is that the parent + # to the previous implementation of eager loading and will raise ActiveRecord::EagerLoadPolymorphicError. The reason is that the parent # model's type is a column value so its corresponding table name cannot be put in the +FROM+/+JOIN+ clauses of that query. # # == Table Aliasing @@ -869,7 +869,7 @@ module ActiveRecord # but not include the joined columns. Do not forget to include the primary and foreign keys, otherwise it will raise an error. # [:through] # Specifies a Join Model through which to perform the query. Options for :class_name and :foreign_key - # are ignored, as the association uses the source reflection. You can only use a :through query through a + # are ignored, as the association uses the source reflection. You can only use a :through query through a # has_one or belongs_to association on the join model. # [:source] # Specifies the source association name used by has_one :through queries. Only use it if the name cannot be @@ -1123,8 +1123,8 @@ module ActiveRecord # the association will use "project_id" as the default :association_foreign_key. # [:conditions] # Specify the conditions that the associated object must meet in order to be included as a +WHERE+ - # SQL fragment, such as authorized = 1. Record creations from the association are scoped if a hash is used. - # has_many :posts, :conditions => {:published => true} will create published posts with @blog.posts.create + # SQL fragment, such as authorized = 1. Record creations from the association are scoped if a hash is used. + # has_many :posts, :conditions => {:published => true} will create published posts with @blog.posts.create # or @blog.posts.build. # [:order] # Specify the order in which the associated objects are returned as an ORDER BY SQL fragment, @@ -1335,12 +1335,12 @@ module ActiveRecord "#{reflection.class_name}.send(:attr_readonly,\"#{cache_column}\".intern) if defined?(#{reflection.class_name}) && #{reflection.class_name}.respond_to?(:attr_readonly)" ) end - + def add_touch_callbacks(reflection, touch_attribute) method_name = "belongs_to_touch_after_save_or_destroy_for_#{reflection.name}".to_sym define_method(method_name) do association = send(reflection.name) - + if touch_attribute == true association.touch unless association.nil? else @@ -1552,7 +1552,7 @@ module ActiveRecord options[:extend] = create_extension_modules(association_id, extension, options[:extend]) reflection = create_reflection(:has_and_belongs_to_many, association_id, options, self) - + if reflection.association_foreign_key == reflection.primary_key_name raise HasAndBelongsToManyAssociationForeignKeyNeeded.new(reflection) end @@ -1607,6 +1607,14 @@ module ActiveRecord end end + def construct_limited_ids_condition(where, options, join_dependency) + unless (id_list = select_limited_ids_list(options, join_dependency)).empty? + "#{where.blank? ? 'WHERE ' : ' AND '} #{connection.quote_table_name table_name}.#{primary_key} IN (#{id_list}) " + else + throw :invalid_query + end + end + def select_limited_ids_list(options, join_dependency) pk = columns_hash[primary_key] diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 8bf92de6d4..8882a00dd5 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -904,9 +904,7 @@ module ActiveRecord #:nodoc: # Both calls delete the affected posts all at once with a single DELETE statement. If you need to destroy dependent # associations or call your before_* or +after_destroy+ callbacks, use the +destroy_all+ method instead. def delete_all(conditions = nil) - sql = "DELETE FROM #{quoted_table_name} " - add_conditions!(sql, conditions, scope(:find)) - connection.delete(sql, "#{name} Delete all") + Arel(table_name).where(construct_conditions(conditions, scope(:find))).delete end # Returns the result of an SQL statement that should only include a COUNT(*) in the SELECT part. @@ -1687,19 +1685,27 @@ module ActiveRecord #:nodoc: end end - def construct_finder_sql(options) + def arel_table(table) + Arel(table) + end + + def construct_finder_arel(options) scope = scope(:find) # TODO add lock to Arel - Arel(table_name). - join(construct_join(options[:joins], scope)). - where(construct_conditions(options[:conditions], scope)). + arel_table(options[:from] || table_name). + join(options[:merged_joins] || construct_join(options[:joins], scope)). + where(options[:merged_conditions] || construct_conditions(options[:conditions], scope)). project(options[:select] || (scope && scope[:select]) || default_select(options[:joins] || (scope && scope[:joins]))). group(construct_group(options[:group], options[:having], scope)). order(construct_order(options[:order], scope)). take(construct_limit(options, scope)). skip(construct_offset(options, scope) - ).to_sql + ) + end + + def construct_finder_sql(options) + construct_finder_arel(options).to_sql end def construct_join(joins, scope = :auto) @@ -1715,6 +1721,8 @@ module ActiveRecord #:nodoc: end when String " #{merged_joins} " + else + "" end end @@ -2644,11 +2652,8 @@ module ActiveRecord #:nodoc: # be made (since they can't be persisted). def destroy unless new_record? - connection.delete( - "DELETE FROM #{self.class.quoted_table_name} " + - "WHERE #{connection.quote_column_name(self.class.primary_key)} = #{quoted_id}", - "#{self.class.name} Destroy" - ) + table = Arel(self.class.table_name) + table.where(table[self.class.primary_key].eq(quoted_id)).delete end freeze diff --git a/activerecord/lib/active_record/calculations.rb b/activerecord/lib/active_record/calculations.rb index f077818d3b..9dbfcdf175 100644 --- a/activerecord/lib/active_record/calculations.rb +++ b/activerecord/lib/active_record/calculations.rb @@ -54,7 +54,7 @@ module ActiveRecord # # Person.average('age') # => 35.8 def average(column_name, options = {}) - calculate(:avg, column_name, options) + calculate(:average, column_name, options) end # Calculates the minimum value on a given column. The value is returned @@ -63,7 +63,7 @@ module ActiveRecord # # Person.minimum('age') # => 7 def minimum(column_name, options = {}) - calculate(:min, column_name, options) + calculate(:minimum, column_name, options) end # Calculates the maximum value on a given column. The value is returned @@ -72,7 +72,7 @@ module ActiveRecord # # Person.maximum('age') # => 93 def maximum(column_name, options = {}) - calculate(:max, column_name, options) + calculate(:maximum, column_name, options) end # Calculates the sum of values on a given column. The value is returned @@ -124,19 +124,96 @@ module ActiveRecord # Person.sum("2 * age") def calculate(operation, column_name, options = {}) validate_calculation_options(operation, options) - column_name = options[:select] if options[:select] - column_name = '*' if column_name == :all - column = column_for column_name + + scope = scope(:find) + + merged_includes = merge_includes(scope ? scope[:include] : [], options[:include]) + joins = construct_join(options[:joins], scope) + + if merged_includes.any? + join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(self, merged_includes, joins) + joins << join_dependency.join_associations.collect{|join| join.association_join }.join + end + + if operation == :count + if merged_includes.any? + distinct = true + column_name = options[:select] || primary_key + end + + distinct = nil if column_name.to_s =~ /\s*DISTINCT\s+/i + distinct ||= options[:distinct] + else + distinct = nil + end + catch :invalid_query do + conditions = construct_conditions(options[:conditions], scope) + conditions << construct_limited_ids_condition(conditions, options, join_dependency) if join_dependency && !using_limitable_reflections?(join_dependency.reflections) && ((scope && scope[:limit]) || options[:limit]) + if options[:group] - return execute_grouped_calculation(operation, column_name, column, options) + return execute_grouped_calculation(operation, column_name, options.merge(:merged_conditions => conditions, :merged_joins => joins, :distinct => distinct)) else - return execute_simple_calculation(operation, column_name, column, options) + return execute_simple_calculation(operation, column_name, options.merge(:merged_conditions => conditions, :merged_joins => joins, :distinct => distinct)) end end 0 end + def execute_simple_calculation(operation, column_name, options) #:nodoc: + table = options[:from] || table_name + value = if operation == :count + if column_name == :all && options[:select].blank? + column_name = "*" + elsif !options[:select].blank? + column_name = options[:select] + end + construct_finder_arel(options.merge(:select => Arel::Attribute.new(Arel(table), column_name).count(options[:distinct]))).select_value + else + construct_finder_arel(options.merge(:select => Arel::Attribute.new(Arel(table), column_name).send(operation))).select_value + end + + type_cast_calculated_value(value, column_for(column_name), operation) + end + + def execute_grouped_calculation(operation, column_name, options) #:nodoc: + group_attr = options[:group].to_s + association = reflect_on_association(group_attr.to_sym) + associated = association && association.macro == :belongs_to # only count belongs_to associations + group_field = associated ? association.primary_key_name : group_attr + group_alias = column_alias_for(group_field) + group_column = column_for group_field + + options[:group] = connection.adapter_name == 'FrontBase' ? group_alias : group_field + + aggregate_alias = column_alias_for(operation, column_name) + if operation == :count && column_name == :all + options[:select] = "COUNT(*) AS count_all, #{group_field} AS #{group_alias}" + else + arel_column = Arel::Attribute.new(Arel(table_name), column_name).send(operation) + options[:select] = "#{arel_column.as(aggregate_alias).to_sql}, #{group_field} AS #{group_alias}" + end + + + sql = construct_finder_arel(options) + + calculated_data = connection.select_all(sql.to_sql) + + if association + key_ids = calculated_data.collect { |row| row[group_alias] } + key_records = association.klass.base_class.find(key_ids) + key_records = key_records.inject({}) { |hsh, r| hsh.merge(r.id => r) } + end + + calculated_data.inject(ActiveSupport::OrderedHash.new) do |all, row| + key = type_cast_calculated_value(row[group_alias], group_column) + key = key_records[key] if associated + value = row[aggregate_alias] + all[key] = type_cast_calculated_value(value, column_for(column_name), operation) + all + end + end + protected def construct_count_options_from_args(*args) options = {} @@ -167,108 +244,6 @@ module ActiveRecord [column_name || :all, options] end - def construct_calculation_sql(operation, column_name, options) #:nodoc: - operation = operation.to_s.downcase - options = options.symbolize_keys - - scope = scope(:find) - merged_includes = merge_includes(scope ? scope[:include] : [], options[:include]) - aggregate_alias = column_alias_for(operation, column_name) - column_name = "#{connection.quote_table_name(table_name)}.#{column_name}" if column_names.include?(column_name.to_s) - - if operation == 'count' - if merged_includes.any? - options[:distinct] = true - column_name = options[:select] || [connection.quote_table_name(table_name), primary_key] * '.' - end - - if options[:distinct] - use_workaround = !connection.supports_count_distinct? - end - end - - if options[:distinct] && column_name.to_s !~ /\s*DISTINCT\s+/i - distinct = 'DISTINCT ' - end - sql = "SELECT #{operation}(#{distinct}#{column_name}) AS #{aggregate_alias}" - - # A (slower) workaround if we're using a backend, like sqlite, that doesn't support COUNT DISTINCT. - sql = "SELECT COUNT(*) AS #{aggregate_alias}" if use_workaround - - sql << ", #{options[:group_field]} AS #{options[:group_alias]}" if options[:group] - if options[:from] - sql << " FROM #{options[:from]} " - else - sql << " FROM (SELECT #{distinct}#{column_name}" if use_workaround - sql << " FROM #{connection.quote_table_name(table_name)} " - end - - joins = "" - add_joins!(joins, options[:joins], scope) - - if merged_includes.any? - join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(self, merged_includes, joins) - sql << join_dependency.join_associations.collect{|join| join.association_join }.join - end - - sql << joins unless joins.blank? - - add_conditions!(sql, options[:conditions], scope) - add_limited_ids_condition!(sql, options, join_dependency) if join_dependency && !using_limitable_reflections?(join_dependency.reflections) && ((scope && scope[:limit]) || options[:limit]) - - if options[:group] - group_key = connection.adapter_name == 'FrontBase' ? :group_alias : :group_field - sql << " GROUP BY #{options[group_key]} " - end - - if options[:group] && options[:having] - having = sanitize_sql_for_conditions(options[:having]) - - # FrontBase requires identifiers in the HAVING clause and chokes on function calls - if connection.adapter_name == 'FrontBase' - having.downcase! - having.gsub!(/#{operation}\s*\(\s*#{column_name}\s*\)/, aggregate_alias) - end - - sql << " HAVING #{having} " - end - - sql << " ORDER BY #{options[:order]} " if options[:order] - add_limit!(sql, options, scope) - sql << ") #{aggregate_alias}_subquery" if use_workaround - sql - end - - def execute_simple_calculation(operation, column_name, column, options) #:nodoc: - value = connection.select_value(construct_calculation_sql(operation, column_name, options)) - type_cast_calculated_value(value, column, operation) - end - - def execute_grouped_calculation(operation, column_name, column, options) #:nodoc: - group_attr = options[:group].to_s - association = reflect_on_association(group_attr.to_sym) - associated = association && association.macro == :belongs_to # only count belongs_to associations - group_field = associated ? association.primary_key_name : group_attr - group_alias = column_alias_for(group_field) - group_column = column_for group_field - sql = construct_calculation_sql(operation, column_name, options.merge(:group_field => group_field, :group_alias => group_alias)) - calculated_data = connection.select_all(sql) - aggregate_alias = column_alias_for(operation, column_name) - - if association - key_ids = calculated_data.collect { |row| row[group_alias] } - key_records = association.klass.base_class.find(key_ids) - key_records = key_records.inject({}) { |hsh, r| hsh.merge(r.id => r) } - end - - calculated_data.inject(ActiveSupport::OrderedHash.new) do |all, row| - key = type_cast_calculated_value(row[group_alias], group_column) - key = key_records[key] if associated - value = row[aggregate_alias] - all[key] = type_cast_calculated_value(value, column, operation) - all - end - end private def validate_calculation_options(operation, options = {}) @@ -304,7 +279,7 @@ module ActiveRecord case operation when 'count' then value.to_i when 'sum' then type_cast_using_column(value || '0', column) - when 'avg' then value && (value.is_a?(Fixnum) ? value.to_f : value).to_d + when 'average' then value && (value.is_a?(Fixnum) ? value.to_f : value).to_d else type_cast_using_column(value, column) end end diff --git a/activerecord/lib/active_record/test_case.rb b/activerecord/lib/active_record/test_case.rb index b790eb4343..2dfe2c09ea 100644 --- a/activerecord/lib/active_record/test_case.rb +++ b/activerecord/lib/active_record/test_case.rb @@ -20,7 +20,7 @@ module ActiveRecord patterns_to_match.each do |pattern| failed_patterns << pattern unless $queries_executed.any?{ |sql| pattern === sql } end - assert failed_patterns.empty?, "Query pattern(s) #{failed_patterns.map(&:inspect).join(', ')} not found in #{$queries_executed}" + assert failed_patterns.empty?, "Query pattern(s) #{failed_patterns.map(&:inspect).join(', ')} not found.#{$queries_executed.size == 0 ? '' : "\nQueries:\n#{$queries_executed.join("\n")}"}" end def assert_queries(num = 1) -- cgit v1.2.3 From 5b61168aafb28b9d4dcbe7651d6a5a63cdb68b32 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Thu, 30 Apr 2009 13:21:13 -0300 Subject: Added arel_attributes_values methods, refactored locking and AR#update to use this method --- activerecord/lib/active_record/base.rb | 32 ++++++++++++++++------ .../lib/active_record/locking/optimistic.rb | 9 +----- 2 files changed, 24 insertions(+), 17 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 8882a00dd5..5807d62f2a 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1077,7 +1077,7 @@ module ActiveRecord #:nodoc: # Returns an array of all the attributes that have been specified as readonly. def readonly_attributes - read_inheritable_attribute(:attr_readonly) + read_inheritable_attribute(:attr_readonly) || [] end # If you have an attribute that needs to be saved to the database as an object, and retrieved as the same object, @@ -2946,14 +2946,9 @@ module ActiveRecord #:nodoc: # Updates the associated record with values matching those of the instance attributes. # Returns the number of affected rows. def update(attribute_names = @attributes.keys) - quoted_attributes = attributes_with_quotes(false, false, attribute_names) - return 0 if quoted_attributes.empty? - connection.update( - "UPDATE #{self.class.quoted_table_name} " + - "SET #{quoted_comma_pair_list(connection, quoted_attributes)} " + - "WHERE #{connection.quote_column_name(self.class.primary_key)} = #{quote_value(id)}", - "#{self.class.name} Update" - ) + attributes_with_values = arel_attributes_values(false, false, attribute_names) + return 0 if attributes_with_values.empty? + table.where(table[self.class.primary_key].eq(id)).update(attributes_with_values) end # Creates a record with values matching those of the instance attributes @@ -3063,6 +3058,25 @@ module ActiveRecord #:nodoc: include_readonly_attributes ? quoted : remove_readonly_attributes(quoted) end + def table + @arel_table ||= Arel(self.class.table_name) + end + + def arel_attributes_values(include_primary_key = true, include_readonly_attributes = true, attribute_names = @attributes.keys) + attrs = {} + connection = self.class.connection + attribute_names.each do |name| + if (column = column_for_attribute(name)) && (include_primary_key || !column.primary) + value = read_attribute(name) + + if include_readonly_attributes || (!include_readonly_attributes && !self.class.readonly_attributes.include?(name)) + attrs[table[name]] = value + end + end + end + attrs + end + # Quote strings appropriately for SQL statements. def quote_value(value, column = nil) self.class.connection.quote(value, column) diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb index 1251e9f013..bb28444b07 100644 --- a/activerecord/lib/active_record/locking/optimistic.rb +++ b/activerecord/lib/active_record/locking/optimistic.rb @@ -89,18 +89,11 @@ module ActiveRecord attribute_names.uniq! begin - table = Arel(self.class.table_name) - - attributes = {} - attributes_with_quotes(false, false, attribute_names).map { |k,v| - attributes.merge!(table[k] => v) - } - affected_rows = table.where( table[self.class.primary_key].eq(quoted_id).and( table[self.class.locking_column].eq(quote_value(previous_value)) ) - ).update(attributes) + ).update(arel_attributes_values(false, false, attribute_names)) unless affected_rows == 1 -- cgit v1.2.3 From ca0530a53f6b019b0f5eb9a8c5c8b0081cd20d12 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Thu, 30 Apr 2009 13:47:51 -0300 Subject: exists? method now uses ARel --- activerecord/lib/active_record/base.rb | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 5807d62f2a..26c11e2dda 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -688,14 +688,9 @@ module ActiveRecord #:nodoc: # Person.exists?(['name LIKE ?', "%#{query}%"]) # Person.exists? def exists?(id_or_conditions = {}) - connection.select_all( - construct_finder_sql( - :select => "#{quoted_table_name}.#{primary_key}", - :conditions => expand_id_conditions(id_or_conditions), - :limit => 1 - ), - "#{name} Exists" - ).size > 0 + construct_finder_arel({ + :conditions =>expand_id_conditions(id_or_conditions) + }).project(arel_table[primary_key]).take(1).count > 0 end # Creates an object (or multiple objects) and saves it to the database, if validations pass. @@ -1685,8 +1680,8 @@ module ActiveRecord #:nodoc: end end - def arel_table(table) - Arel(table) + def arel_table(table = table_name) + @arel_table = Arel(table) end def construct_finder_arel(options) -- cgit v1.2.3 From 4bb6f77059a34ea7c8575523397032b4cc67e00a Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Thu, 30 Apr 2009 20:32:19 -0700 Subject: Work around count returning string (bug) --- activerecord/lib/active_record/base.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 26c11e2dda..4c23d2f8e2 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -690,7 +690,7 @@ module ActiveRecord #:nodoc: def exists?(id_or_conditions = {}) construct_finder_arel({ :conditions =>expand_id_conditions(id_or_conditions) - }).project(arel_table[primary_key]).take(1).count > 0 + }).project(arel_table[primary_key]).take(1).count.to_i > 0 end # Creates an object (or multiple objects) and saves it to the database, if validations pass. -- cgit v1.2.3 From 509c389bf8093d7995a52470c27c221adc972796 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Thu, 30 Apr 2009 20:33:50 -0700 Subject: Explicitly require builder for to_xml --- activerecord/lib/active_record/validations.rb | 2 ++ 1 file changed, 2 insertions(+) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb index d2d12b80c9..e6b61e0b35 100644 --- a/activerecord/lib/active_record/validations.rb +++ b/activerecord/lib/active_record/validations.rb @@ -1,3 +1,5 @@ +require 'builder' + module ActiveRecord # Raised by save! and create! when the record is invalid. Use the # +record+ method to retrieve the record which did not validate. -- cgit v1.2.3 From c9896884a46eb96508fc3e0d2a10be47eb195826 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Sat, 2 May 2009 03:40:31 -0300 Subject: Removed ARel table assignment --- activerecord/lib/active_record/locking/optimistic.rb | 1 - 1 file changed, 1 deletion(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb index bb28444b07..f66458cc8c 100644 --- a/activerecord/lib/active_record/locking/optimistic.rb +++ b/activerecord/lib/active_record/locking/optimistic.rb @@ -116,7 +116,6 @@ module ActiveRecord lock_col = self.class.locking_column previous_value = send(lock_col).to_i - table = Arel(self.class.table_name) affected_rows = table.where( table[self.class.primary_key].eq(quoted_id).and( table[self.class.locking_column].eq(quote_value(previous_value)) -- cgit v1.2.3 From 3fd467e9ed7f3a8d408ab2cb0782a5b13bb3f629 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Sat, 2 May 2009 04:02:09 -0300 Subject: Refactor to use arel_table method, and also use the same method name for instance and class methods. --- activerecord/lib/active_record/base.rb | 11 +++++------ activerecord/lib/active_record/calculations.rb | 16 +++++++--------- activerecord/lib/active_record/locking/optimistic.rb | 12 ++++++------ 3 files changed, 18 insertions(+), 21 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 4c23d2f8e2..b1ae2fbe78 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -899,7 +899,7 @@ module ActiveRecord #:nodoc: # Both calls delete the affected posts all at once with a single DELETE statement. If you need to destroy dependent # associations or call your before_* or +after_destroy+ callbacks, use the +destroy_all+ method instead. def delete_all(conditions = nil) - Arel(table_name).where(construct_conditions(conditions, scope(:find))).delete + arel_table.where(construct_conditions(conditions, scope(:find))).delete end # Returns the result of an SQL statement that should only include a COUNT(*) in the SELECT part. @@ -2647,8 +2647,7 @@ module ActiveRecord #:nodoc: # be made (since they can't be persisted). def destroy unless new_record? - table = Arel(self.class.table_name) - table.where(table[self.class.primary_key].eq(quoted_id)).delete + arel_table.where(arel_table[self.class.primary_key].eq(id)).delete end freeze @@ -2943,7 +2942,7 @@ module ActiveRecord #:nodoc: def update(attribute_names = @attributes.keys) attributes_with_values = arel_attributes_values(false, false, attribute_names) return 0 if attributes_with_values.empty? - table.where(table[self.class.primary_key].eq(id)).update(attributes_with_values) + arel_table.where(arel_table[self.class.primary_key].eq(id)).update(attributes_with_values) end # Creates a record with values matching those of the instance attributes @@ -3053,7 +3052,7 @@ module ActiveRecord #:nodoc: include_readonly_attributes ? quoted : remove_readonly_attributes(quoted) end - def table + def arel_table @arel_table ||= Arel(self.class.table_name) end @@ -3065,7 +3064,7 @@ module ActiveRecord #:nodoc: value = read_attribute(name) if include_readonly_attributes || (!include_readonly_attributes && !self.class.readonly_attributes.include?(name)) - attrs[table[name]] = value + attrs[arel_table[name]] = value end end end diff --git a/activerecord/lib/active_record/calculations.rb b/activerecord/lib/active_record/calculations.rb index 9dbfcdf175..6bd2163dc2 100644 --- a/activerecord/lib/active_record/calculations.rb +++ b/activerecord/lib/active_record/calculations.rb @@ -124,6 +124,7 @@ module ActiveRecord # Person.sum("2 * age") def calculate(operation, column_name, options = {}) validate_calculation_options(operation, options) + operation = operation.to_s.downcase scope = scope(:find) @@ -135,7 +136,7 @@ module ActiveRecord joins << join_dependency.join_associations.collect{|join| join.association_join }.join end - if operation == :count + if operation == "count" if merged_includes.any? distinct = true column_name = options[:select] || primary_key @@ -162,7 +163,7 @@ module ActiveRecord def execute_simple_calculation(operation, column_name, options) #:nodoc: table = options[:from] || table_name - value = if operation == :count + value = if operation == 'count' if column_name == :all && options[:select].blank? column_name = "*" elsif !options[:select].blank? @@ -187,17 +188,15 @@ module ActiveRecord options[:group] = connection.adapter_name == 'FrontBase' ? group_alias : group_field aggregate_alias = column_alias_for(operation, column_name) - if operation == :count && column_name == :all + + if operation == 'count' && column_name == :all options[:select] = "COUNT(*) AS count_all, #{group_field} AS #{group_alias}" else - arel_column = Arel::Attribute.new(Arel(table_name), column_name).send(operation) + arel_column = Arel::Attribute.new(arel_table, column_name).send(operation) options[:select] = "#{arel_column.as(aggregate_alias).to_sql}, #{group_field} AS #{group_alias}" end - - sql = construct_finder_arel(options) - - calculated_data = connection.select_all(sql.to_sql) + calculated_data = connection.select_all(construct_finder_sql(options)) if association key_ids = calculated_data.collect { |row| row[group_alias] } @@ -275,7 +274,6 @@ module ActiveRecord end def type_cast_calculated_value(value, column, operation = nil) - operation = operation.to_s.downcase case operation when 'count' then value.to_i when 'sum' then type_cast_using_column(value || '0', column) diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb index f66458cc8c..a4f5dd8516 100644 --- a/activerecord/lib/active_record/locking/optimistic.rb +++ b/activerecord/lib/active_record/locking/optimistic.rb @@ -89,9 +89,9 @@ module ActiveRecord attribute_names.uniq! begin - affected_rows = table.where( - table[self.class.primary_key].eq(quoted_id).and( - table[self.class.locking_column].eq(quote_value(previous_value)) + affected_rows = arel_table.where( + arel_table[self.class.primary_key].eq(quoted_id).and( + arel_table[self.class.locking_column].eq(quote_value(previous_value)) ) ).update(arel_attributes_values(false, false, attribute_names)) @@ -116,9 +116,9 @@ module ActiveRecord lock_col = self.class.locking_column previous_value = send(lock_col).to_i - affected_rows = table.where( - table[self.class.primary_key].eq(quoted_id).and( - table[self.class.locking_column].eq(quote_value(previous_value)) + affected_rows = arel_table.where( + arel_table[self.class.primary_key].eq(quoted_id).and( + arel_table[self.class.locking_column].eq(quote_value(previous_value)) ) ).delete -- cgit v1.2.3 From d522b7ccf61a71b4c66ddf39368513d1fd9cd577 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Sat, 2 May 2009 12:59:49 -0300 Subject: Revert "Work around count returning string (bug)" This reverts commit 4bb6f77059a34ea7c8575523397032b4cc67e00a. --- activerecord/lib/active_record/base.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index b1ae2fbe78..3b693b5f17 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -690,7 +690,7 @@ module ActiveRecord #:nodoc: def exists?(id_or_conditions = {}) construct_finder_arel({ :conditions =>expand_id_conditions(id_or_conditions) - }).project(arel_table[primary_key]).take(1).count.to_i > 0 + }).project(arel_table[primary_key]).take(1).count > 0 end # Creates an object (or multiple objects) and saves it to the database, if validations pass. -- cgit v1.2.3 From d19d4d2f20d87289eaeaa8df807d635ce72f3799 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Mon, 4 May 2009 21:53:29 -0300 Subject: Fixes to have all test passing on PostgreSQL. Calculations now use construct_calculation_arel, making construct_finder_arel less hackish. Updated ARel to support PostgreSQL. --- activerecord/lib/active_record/base.rb | 17 ++++++---------- activerecord/lib/active_record/calculations.rb | 28 ++++++++++++++++++++------ 2 files changed, 28 insertions(+), 17 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 3b693b5f17..bdfb0f45aa 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -688,9 +688,9 @@ module ActiveRecord #:nodoc: # Person.exists?(['name LIKE ?', "%#{query}%"]) # Person.exists? def exists?(id_or_conditions = {}) - construct_finder_arel({ - :conditions =>expand_id_conditions(id_or_conditions) - }).project(arel_table[primary_key]).take(1).count > 0 + connection.select_all(construct_finder_arel({ + :conditions => expand_id_conditions(id_or_conditions) + }).project(arel_table[primary_key]).take(1).to_sql).size > 0 end # Creates an object (or multiple objects) and saves it to the database, if validations pass. @@ -1688,9 +1688,9 @@ module ActiveRecord #:nodoc: scope = scope(:find) # TODO add lock to Arel - arel_table(options[:from] || table_name). - join(options[:merged_joins] || construct_join(options[:joins], scope)). - where(options[:merged_conditions] || construct_conditions(options[:conditions], scope)). + arel_table(table_name). + join(construct_join(options[:joins], scope)). + where(construct_conditions(options[:conditions], scope)). project(options[:select] || (scope && scope[:select]) || default_select(options[:joins] || (scope && scope[:joins]))). group(construct_group(options[:group], options[:having], scope)). order(construct_order(options[:order], scope)). @@ -1704,7 +1704,6 @@ module ActiveRecord #:nodoc: end def construct_join(joins, scope = :auto) - scope = scope(:find) if :auto == scope merged_joins = scope && scope[:joins] && joins ? merge_joins(scope[:joins], joins) : (joins || scope && scope[:joins]) case merged_joins when Symbol, Hash, Array @@ -1738,7 +1737,6 @@ module ActiveRecord #:nodoc: def construct_order(order, scope = :auto) sql = '' - scope = scope(:find) if :auto == scope scoped_order = scope[:order] if scope if order sql << order.to_s @@ -1752,19 +1750,16 @@ module ActiveRecord #:nodoc: end def construct_limit(options, scope = :auto) - scope = scope(:find) if :auto == scope options[:limit] ||= scope[:limit] if scope options[:limit] end def construct_offset(options, scope = :auto) - scope = scope(:find) if :auto == scope options[:offset] ||= scope[:offset] if scope options[:offset] end def construct_conditions(conditions, scope = :auto) - scope = scope(:find) if :auto == scope conditions = [conditions] conditions << scope[:conditions] if scope conditions << type_condition if finder_needs_type_condition? diff --git a/activerecord/lib/active_record/calculations.rb b/activerecord/lib/active_record/calculations.rb index 6bd2163dc2..c941be2837 100644 --- a/activerecord/lib/active_record/calculations.rb +++ b/activerecord/lib/active_record/calculations.rb @@ -153,9 +153,9 @@ module ActiveRecord conditions << construct_limited_ids_condition(conditions, options, join_dependency) if join_dependency && !using_limitable_reflections?(join_dependency.reflections) && ((scope && scope[:limit]) || options[:limit]) if options[:group] - return execute_grouped_calculation(operation, column_name, options.merge(:merged_conditions => conditions, :merged_joins => joins, :distinct => distinct)) + return execute_grouped_calculation(operation, column_name, options.merge(:conditions => conditions, :joins => joins, :distinct => distinct)) else - return execute_simple_calculation(operation, column_name, options.merge(:merged_conditions => conditions, :merged_joins => joins, :distinct => distinct)) + return execute_simple_calculation(operation, column_name, options.merge(:conditions => conditions, :joins => joins, :distinct => distinct)) end end 0 @@ -163,15 +163,16 @@ module ActiveRecord def execute_simple_calculation(operation, column_name, options) #:nodoc: table = options[:from] || table_name + value = if operation == 'count' if column_name == :all && options[:select].blank? column_name = "*" elsif !options[:select].blank? column_name = options[:select] end - construct_finder_arel(options.merge(:select => Arel::Attribute.new(Arel(table), column_name).count(options[:distinct]))).select_value + construct_calculation_arel(options.merge(:select => Arel::Attribute.new(Arel(table), column_name).count(options[:distinct]))).select_value else - construct_finder_arel(options.merge(:select => Arel::Attribute.new(Arel(table), column_name).send(operation))).select_value + construct_calculation_arel(options.merge(:select => Arel::Attribute.new(Arel(table), column_name).send(operation))).select_value end type_cast_calculated_value(value, column_for(column_name), operation) @@ -196,7 +197,7 @@ module ActiveRecord options[:select] = "#{arel_column.as(aggregate_alias).to_sql}, #{group_field} AS #{group_alias}" end - calculated_data = connection.select_all(construct_finder_sql(options)) + calculated_data = connection.select_all(construct_calculation_arel(options).to_sql) if association key_ids = calculated_data.collect { |row| row[group_alias] } @@ -213,7 +214,22 @@ module ActiveRecord end end - protected + protected + + def construct_calculation_arel(options) + scope = scope(:find) + + arel_table(options[:from] || table_name). + join(options[:joins]). + where(options[:conditions]). + project(options[:select]). + group(construct_group(options[:group], options[:having], scope)). + order(options[:order].to_s). + take(construct_limit(options, scope)). + skip(construct_offset(options, scope) + ) + end + def construct_count_options_from_args(*args) options = {} column_name = :all -- cgit v1.2.3 From 8885b2d6c1855742600d0afdb9dfc002acb62e5e Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Wed, 6 May 2009 14:16:03 -0300 Subject: Refactor to calculations. Migration's versions are string not integer. ARel submodule updated. --- activerecord/lib/active_record/calculations.rb | 17 ++++++++--------- .../connection_adapters/postgresql_adapter.rb | 20 ++++++++++---------- activerecord/lib/active_record/migration.rb | 10 +++++----- 3 files changed, 23 insertions(+), 24 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/calculations.rb b/activerecord/lib/active_record/calculations.rb index c941be2837..9d04686a5f 100644 --- a/activerecord/lib/active_record/calculations.rb +++ b/activerecord/lib/active_record/calculations.rb @@ -170,12 +170,12 @@ module ActiveRecord elsif !options[:select].blank? column_name = options[:select] end - construct_calculation_arel(options.merge(:select => Arel::Attribute.new(Arel(table), column_name).count(options[:distinct]))).select_value + construct_calculation_arel(options.merge(:select => Arel::Attribute.new(Arel(table), column_name).count(options[:distinct]))) else - construct_calculation_arel(options.merge(:select => Arel::Attribute.new(Arel(table), column_name).send(operation))).select_value + construct_calculation_arel(options.merge(:select => Arel::Attribute.new(Arel(table), column_name).send(operation))) end - type_cast_calculated_value(value, column_for(column_name), operation) + type_cast_calculated_value(connection.select_value(value.to_sql), column_for(column_name), operation) end def execute_grouped_calculation(operation, column_name, options) #:nodoc: @@ -190,12 +190,11 @@ module ActiveRecord aggregate_alias = column_alias_for(operation, column_name) - if operation == 'count' && column_name == :all - options[:select] = "COUNT(*) AS count_all, #{group_field} AS #{group_alias}" - else - arel_column = Arel::Attribute.new(arel_table, column_name).send(operation) - options[:select] = "#{arel_column.as(aggregate_alias).to_sql}, #{group_field} AS #{group_alias}" - end + options[:select] = (operation == 'count' && column_name == :all) ? + "COUNT(*) AS count_all" : + Arel::Attribute.new(arel_table, column_name).send(operation).as(aggregate_alias).to_sql + + options[:select] << ", #{group_field} AS #{group_alias}" calculated_data = connection.select_all(construct_calculation_arel(options).to_sql) diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 4961793866..12b2c4a4d1 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -67,7 +67,7 @@ module ActiveRecord # depending on the server specifics super end - + # Maps PostgreSQL-specific data types to logical Rails types. def simplified_type(field_type) case field_type @@ -102,7 +102,7 @@ module ActiveRecord :string # Arrays when /^\D+\[\]$/ - :string + :string # Object identifier types when /^oid$/ :integer @@ -111,7 +111,7 @@ module ActiveRecord super end end - + # Extracts the value from a PostgreSQL column default definition. def self.extract_value_from_default(default) case default @@ -272,7 +272,7 @@ module ActiveRecord def supports_ddl_transactions? true end - + def supports_savepoints? true end @@ -551,7 +551,7 @@ module ActiveRecord def rollback_db_transaction execute "ROLLBACK" end - + if defined?(PGconn::PQTRANS_IDLE) # The ruby-pg driver supports inspecting the transaction status, # while the ruby-postgres driver does not. @@ -896,18 +896,18 @@ module ActiveRecord sql = "DISTINCT ON (#{columns}) #{columns}, " sql << order_columns * ', ' end - + # Returns an ORDER BY clause for the passed order option. - # + # # PostgreSQL does not allow arbitrary ordering when using DISTINCT ON, so we work around this # by wrapping the +sql+ string as a sub-select and ordering in that query. def add_order_by_for_association_limiting!(sql, options) #:nodoc: return sql if options[:order].blank? - + order = options[:order].split(',').collect { |s| s.strip }.reject(&:blank?) order.map! { |s| 'DESC' if s =~ /\bdesc$/i } order = order.zip((0...order.size).to_a).map { |s,i| "id_list.alias_#{i} #{s}" }.join(', ') - + sql.replace "SELECT * FROM (#{sql}) AS id_list ORDER BY #{order}" end @@ -1020,7 +1020,7 @@ module ActiveRecord if res.ftype(cell_index) == MONEY_COLUMN_TYPE_OID # Because money output is formatted according to the locale, there are two # cases to consider (note the decimal separators): - # (1) $12,345,678.12 + # (1) $12,345,678.12 # (2) $12.345.678,12 case column = row[cell_index] when /^-?\D+[\d,]+\.\d{2}$/ # (1) diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index aa9e9e8fdd..ce02fe019f 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -410,7 +410,7 @@ module ActiveRecord def get_all_versions table = Arel(schema_migrations_table_name) - table.project(table['version']).select_values.map(&:to_i).sort + Base.connection.select_values(table.project(table['version']).to_sql).map(&:to_i).sort end def current_version @@ -535,11 +535,11 @@ module ActiveRecord @migrated_versions ||= [] if down? - @migrated_versions.delete(version.to_i) - table.where(table["version"].eq(version)).delete + @migrated_versions.delete(version) + table.where(table["version"].eq(version.to_s)).delete else - @migrated_versions.push(version.to_i).sort! - table.insert table["version"] => version + @migrated_versions.push(version).sort! + table.insert table["version"] => version.to_s end end -- cgit v1.2.3 From 522711952bf315bc52353e941183237a41f61b23 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Tue, 2 Jun 2009 11:40:01 -0300 Subject: Refactors to work with latest Arel implementation. --- activerecord/lib/active_record/base.rb | 12 ++++++++---- activerecord/lib/active_record/calculations.rb | 19 +++++++++---------- .../abstract/database_statements.rb | 4 ++-- .../connection_adapters/mysql_adapter.rb | 1 + .../connection_adapters/postgresql_adapter.rb | 1 + .../connection_adapters/sqlite_adapter.rb | 5 +++-- activerecord/lib/active_record/migration.rb | 4 ++-- 7 files changed, 26 insertions(+), 20 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 56f3bd9faa..81969d11c5 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -909,7 +909,11 @@ module ActiveRecord #:nodoc: # Both calls delete the affected posts all at once with a single DELETE statement. If you need to destroy dependent # associations or call your before_* or +after_destroy+ callbacks, use the +destroy_all+ method instead. def delete_all(conditions = nil) - arel_table.where(construct_conditions(conditions, scope(:find))).delete + if conditions + arel_table.where(Arel::SqlLiteral.new(construct_conditions(conditions, scope(:find)))).delete + else + arel_table.delete + end end # Returns the result of an SQL statement that should only include a COUNT(*) in the SELECT part. @@ -1691,7 +1695,7 @@ module ActiveRecord #:nodoc: end def arel_table(table = table_name) - @arel_table = Arel(table) + @arel_table = Arel::Table.new(table, ActiveRecord::Base.connection) end def construct_finder_arel(options) @@ -3058,7 +3062,7 @@ module ActiveRecord #:nodoc: end def arel_table - @arel_table ||= Arel(self.class.table_name) + @arel_table ||= Arel::Table.new(self.class.table_name, ActiveRecord::Base.connection) end def arel_attributes_values(include_primary_key = true, include_readonly_attributes = true, attribute_names = @attributes.keys) @@ -3069,7 +3073,7 @@ module ActiveRecord #:nodoc: value = read_attribute(name) if include_readonly_attributes || (!include_readonly_attributes && !self.class.readonly_attributes.include?(name)) - attrs[arel_table[name]] = value + attrs[arel_table[name]] = value.is_a?(Hash) ? value.to_yaml : value end end end diff --git a/activerecord/lib/active_record/calculations.rb b/activerecord/lib/active_record/calculations.rb index 98f9450662..8b8fb37d2d 100644 --- a/activerecord/lib/active_record/calculations.rb +++ b/activerecord/lib/active_record/calculations.rb @@ -161,19 +161,18 @@ module ActiveRecord end def execute_simple_calculation(operation, column_name, options) #:nodoc: - table = options[:from] || table_name - - value = if operation == 'count' - if column_name == :all && options[:select].blank? - column_name = "*" - elsif !options[:select].blank? - column_name = options[:select] - end - construct_calculation_arel(options.merge(:select => Arel::Attribute.new(Arel(table), column_name).count(options[:distinct]))) + column = if column_names.include?(column_name.to_s) + Arel::Attribute.new(arel_table(options[:from] || table_name), + options[:select] || column_name) else - construct_calculation_arel(options.merge(:select => Arel::Attribute.new(Arel(table), column_name).send(operation))) + Arel::SqlLiteral.new(options[:select] || + (column_name == :all ? "*" : column_name.to_s)) end + value = construct_calculation_arel(options.merge( + :select => operation == 'count' ? column.count(options[:distinct]) : column.send(operation) + )) + type_cast_calculated_value(connection.select_value(value.to_sql), column_for(column_name), operation) end 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 08601da00a..ce04a8109d 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb @@ -53,7 +53,7 @@ module ActiveRecord def delete(sql, name = nil) delete_sql(sql, name) end - + # Checks whether there is currently no transaction active. This is done # by querying the database driver, and does not use the transaction # house-keeping information recorded by #increment_open_transactions and @@ -170,7 +170,7 @@ module ActiveRecord end end end - + # Begins the transaction (and turns off auto-committing). def begin_db_transaction() end diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb index 0aae97a6a9..6d28e01544 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb @@ -331,6 +331,7 @@ module ActiveRecord super sql, name id_value || @connection.insert_id end + alias :create :insert_sql def update_sql(sql, name = nil) #:nodoc: super diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 4b2ddac634..763db3900d 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -486,6 +486,7 @@ module ActiveRecord end end end + alias :create :insert # create a 2D array representing the result set def result_as_array(res) #:nodoc: diff --git a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb index c9d0c9574f..cabb63bfaf 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb @@ -109,7 +109,7 @@ module ActiveRecord def supports_add_column? sqlite_version >= '3.1.6' end - + def disconnect! super @connection.close rescue nil @@ -181,6 +181,7 @@ module ActiveRecord def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc: super || @connection.last_insert_row_id end + alias :create :insert_sql def select_rows(sql, name = nil) execute(sql, name).map do |row| @@ -355,7 +356,7 @@ module ActiveRecord (options[:rename][column.name] || options[:rename][column.name.to_sym] || column.name) : column.name - + @definition.column(column_name, column.type, :limit => column.limit, :default => column.default, :null => column.null) diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index 557ab07abf..b069736778 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -409,7 +409,7 @@ module ActiveRecord end def get_all_versions - table = Arel(schema_migrations_table_name) + table = Arel::Table.new(schema_migrations_table_name, Base.connection) Base.connection.select_values(table.project(table['version']).to_sql).map(&:to_i).sort end @@ -531,7 +531,7 @@ module ActiveRecord private def record_version_state_after_migrating(version) - table = Arel(self.class.schema_migrations_table_name) + table = Arel::Table.new(self.class.schema_migrations_table_name, Base.connection) @migrated_versions ||= [] if down? -- cgit v1.2.3 From bbe51a1b9de7d3d825533dfee28968fed22acc0d Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Tue, 2 Jun 2009 12:27:26 -0300 Subject: No need to specify the engine now that it is in active_record.rb --- activerecord/lib/active_record/migration.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index b069736778..55512ab447 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -409,7 +409,7 @@ module ActiveRecord end def get_all_versions - table = Arel::Table.new(schema_migrations_table_name, Base.connection) + table = Arel::Table.new(schema_migrations_table_name) Base.connection.select_values(table.project(table['version']).to_sql).map(&:to_i).sort end @@ -531,7 +531,7 @@ module ActiveRecord private def record_version_state_after_migrating(version) - table = Arel::Table.new(self.class.schema_migrations_table_name, Base.connection) + table = Arel::Table.new(self.class.schema_migrations_table_name) @migrated_versions ||= [] if down? -- cgit v1.2.3 From 2474fbb4d60f7431af34886f0dccecfe037c565f Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Tue, 2 Jun 2009 12:29:09 -0300 Subject: Set ActiveRecord as Arel engine on load. --- activerecord/lib/active_record/base.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 81969d11c5..adc7f5335d 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1695,7 +1695,7 @@ module ActiveRecord #:nodoc: end def arel_table(table = table_name) - @arel_table = Arel::Table.new(table, ActiveRecord::Base.connection) + @arel_table = Arel::Table.new(table) end def construct_finder_arel(options) @@ -3062,7 +3062,7 @@ module ActiveRecord #:nodoc: end def arel_table - @arel_table ||= Arel::Table.new(self.class.table_name, ActiveRecord::Base.connection) + @arel_table = Arel::Table.new(self.class.table_name) end def arel_attributes_values(include_primary_key = true, include_readonly_attributes = true, attribute_names = @attributes.keys) -- cgit v1.2.3 From 8c3b8323f57d366fc308e773b286a1847552b0a3 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Wed, 10 Jun 2009 16:36:05 -0300 Subject: Use ARel in SQL generation through associations --- activerecord/lib/active_record/associations.rb | 26 +++++++++++++--------- .../associations/has_many_through_association.rb | 8 +++---- 2 files changed, 19 insertions(+), 15 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index a74e9b76dc..4ed0cb70e9 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1590,19 +1590,23 @@ module ActiveRecord def construct_finder_sql_with_included_associations(options, join_dependency) scope = scope(:find) - sql = "SELECT #{column_aliases(join_dependency)} FROM #{(scope && scope[:from]) || options[:from] || quoted_table_name} " - sql << join_dependency.join_associations.collect{|join| join.association_join }.join - add_joins!(sql, options[:joins], scope) - add_conditions!(sql, options[:conditions], scope) - add_limited_ids_condition!(sql, options, join_dependency) if !using_limitable_reflections?(join_dependency.reflections) && ((scope && scope[:limit]) || options[:limit]) + joins = join_dependency.join_associations.collect{|join| join.association_join }.join + joins << construct_join(options[:joins], scope) - add_group!(sql, options[:group], options[:having], scope) - add_order!(sql, options[:order], scope) - add_limit!(sql, options, scope) if using_limitable_reflections?(join_dependency.reflections) - add_lock!(sql, options, scope) + conditions = construct_conditions(options[:conditions], scope) || '' + conditions << construct_limited_ids_condition(conditions, options, join_dependency) if !using_limitable_reflections?(join_dependency.reflections) && ((scope && scope[:limit]) || options[:limit]) - return sanitize_sql(sql) + arel = arel_table((scope && scope[:from]) || options[:from] || table_name). + join(joins). + where(conditions). + project(column_aliases(join_dependency)). + group(construct_group(options[:group], options[:having], scope)). + order(construct_order(options[:order], scope)) + + arel = arel.take(construct_limit(options, scope)) if using_limitable_reflections?(join_dependency.reflections) + + return sanitize_sql(arel.to_sql) end def add_limited_ids_condition!(sql, options, join_dependency) @@ -1615,7 +1619,7 @@ module ActiveRecord def construct_limited_ids_condition(where, options, join_dependency) unless (id_list = select_limited_ids_list(options, join_dependency)).empty? - "#{where.blank? ? 'WHERE ' : ' AND '} #{connection.quote_table_name table_name}.#{primary_key} IN (#{id_list}) " + "#{where.blank? ? '' : ' AND '} #{connection.quote_table_name table_name}.#{primary_key} IN (#{id_list}) " else throw :invalid_query end 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 e8dbae9011..51fb75e1f5 100644 --- a/activerecord/lib/active_record/associations/has_many_through_association.rb +++ b/activerecord/lib/active_record/associations/has_many_through_association.rb @@ -32,7 +32,7 @@ module ActiveRecord return @target.size if loaded? return count end - + protected def target_reflection_has_associated_record? if @reflection.through_reflection.macro == :belongs_to && @owner[@reflection.through_reflection.primary_key_name].blank? @@ -48,7 +48,7 @@ module ActiveRecord options[:joins] = construct_joins(options[:joins]) options[:include] = @reflection.source_reflection.options[:include] if options[:include].nil? end - + def insert_record(record, force = true, validate = true) if record.new_record? if force @@ -131,7 +131,7 @@ module ActiveRecord end def construct_from - @reflection.quoted_table_name + @reflection.table_name end def construct_select(custom_select = nil) @@ -239,7 +239,7 @@ module ActiveRecord interpolate_sql(sanitize_sql(conditions)) end end - + def build_sti_condition @reflection.through_reflection.klass.send(:type_condition) end -- cgit v1.2.3 From 7be3e3ba0547587313d87bccb788a8466a62628a Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Wed, 10 Jun 2009 17:31:26 -0300 Subject: Use ARel in SQL generation through associations --- activerecord/lib/active_record/base.rb | 38 +++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 10 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index adc7f5335d..f0f70e0636 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -839,26 +839,44 @@ module ActiveRecord #:nodoc: # # Update all books that match our conditions, but limit it to 5 ordered by date # Book.update_all "author = 'David'", "title LIKE '%Rails%'", :order => 'created_at', :limit => 5 def update_all(updates, conditions = nil, options = {}) - sql = "UPDATE #{quoted_table_name} SET #{sanitize_sql_for_assignment(updates)} " + # sql = "UPDATE #{quoted_table_name} SET #{sanitize_sql_for_assignment(updates)} " - scope = scope(:find) + # scope = scope(:find) + + # select_sql = "" + # add_conditions!(select_sql, conditions, scope) + + # if options.has_key?(:limit) || (scope && scope[:limit]) + # # Only take order from scope if limit is also provided by scope, this + # # is useful for updating a has_many association with a limit. + # add_order!(select_sql, options[:order], scope) - select_sql = "" - add_conditions!(select_sql, conditions, scope) + # add_limit!(select_sql, options, scope) + # sql.concat(connection.limited_update_conditions(select_sql, quoted_table_name, connection.quote_column_name(primary_key))) + # else + # add_order!(select_sql, options[:order], nil) + # sql.concat(select_sql) + # end + + # connection.update(sql, "#{name} Update") + scope = scope(:find) + arel = arel_table + arel = arel.where(Arel::SqlLiteral.new(construct_conditions(conditions, scope))) if conditions || scope if options.has_key?(:limit) || (scope && scope[:limit]) # Only take order from scope if limit is also provided by scope, this # is useful for updating a has_many association with a limit. - add_order!(select_sql, options[:order], scope) + arel = arel.order(construct_order(options[:order], scope)) - add_limit!(select_sql, options, scope) - sql.concat(connection.limited_update_conditions(select_sql, quoted_table_name, connection.quote_column_name(primary_key))) + arel = arel.take(construct_limit(options, scope)) + #arel = arel.where(connection.limited_update_conditions(select_sql, quoted_table_name, connection.quote_column_name(primary_key))) + #sql.concat(connection.limited_update_conditions(select_sql, quoted_table_name, connection.quote_column_name(primary_key))) else - add_order!(select_sql, options[:order], nil) - sql.concat(select_sql) + arel = arel.order(construct_order(options[:order], nil)) + #sql.concat(select_sql) end - connection.update(sql, "#{name} Update") + arel.update(sanitize_sql_for_assignment(updates)) end # Destroys the records matching +conditions+ by instantiating each -- cgit v1.2.3 From 0587462050ff6f58b277246501d504e6780b481e Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Wed, 10 Jun 2009 17:32:20 -0300 Subject: Revert "Use ARel in SQL generation through associations" This reverts commit 7be3e3ba0547587313d87bccb788a8466a62628a. --- activerecord/lib/active_record/base.rb | 38 +++++++++------------------------- 1 file changed, 10 insertions(+), 28 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index f0f70e0636..adc7f5335d 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -839,44 +839,26 @@ module ActiveRecord #:nodoc: # # Update all books that match our conditions, but limit it to 5 ordered by date # Book.update_all "author = 'David'", "title LIKE '%Rails%'", :order => 'created_at', :limit => 5 def update_all(updates, conditions = nil, options = {}) - # sql = "UPDATE #{quoted_table_name} SET #{sanitize_sql_for_assignment(updates)} " + sql = "UPDATE #{quoted_table_name} SET #{sanitize_sql_for_assignment(updates)} " - # scope = scope(:find) - - # select_sql = "" - # add_conditions!(select_sql, conditions, scope) - - # if options.has_key?(:limit) || (scope && scope[:limit]) - # # Only take order from scope if limit is also provided by scope, this - # # is useful for updating a has_many association with a limit. - # add_order!(select_sql, options[:order], scope) - - # add_limit!(select_sql, options, scope) - # sql.concat(connection.limited_update_conditions(select_sql, quoted_table_name, connection.quote_column_name(primary_key))) - # else - # add_order!(select_sql, options[:order], nil) - # sql.concat(select_sql) - # end - - # connection.update(sql, "#{name} Update") scope = scope(:find) - arel = arel_table - arel = arel.where(Arel::SqlLiteral.new(construct_conditions(conditions, scope))) if conditions || scope + select_sql = "" + add_conditions!(select_sql, conditions, scope) + if options.has_key?(:limit) || (scope && scope[:limit]) # Only take order from scope if limit is also provided by scope, this # is useful for updating a has_many association with a limit. - arel = arel.order(construct_order(options[:order], scope)) + add_order!(select_sql, options[:order], scope) - arel = arel.take(construct_limit(options, scope)) - #arel = arel.where(connection.limited_update_conditions(select_sql, quoted_table_name, connection.quote_column_name(primary_key))) - #sql.concat(connection.limited_update_conditions(select_sql, quoted_table_name, connection.quote_column_name(primary_key))) + add_limit!(select_sql, options, scope) + sql.concat(connection.limited_update_conditions(select_sql, quoted_table_name, connection.quote_column_name(primary_key))) else - arel = arel.order(construct_order(options[:order], nil)) - #sql.concat(select_sql) + add_order!(select_sql, options[:order], nil) + sql.concat(select_sql) end - arel.update(sanitize_sql_for_assignment(updates)) + connection.update(sql, "#{name} Update") end # Destroys the records matching +conditions+ by instantiating each -- cgit v1.2.3 From 71528c29215d74bd030ce5381aac37e8624bd506 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Wed, 10 Jun 2009 19:36:45 -0300 Subject: Initial update_all migration --- activerecord/lib/active_record/base.rb | 38 +++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 10 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index adc7f5335d..4a07fbc1e1 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -839,26 +839,44 @@ module ActiveRecord #:nodoc: # # Update all books that match our conditions, but limit it to 5 ordered by date # Book.update_all "author = 'David'", "title LIKE '%Rails%'", :order => 'created_at', :limit => 5 def update_all(updates, conditions = nil, options = {}) - sql = "UPDATE #{quoted_table_name} SET #{sanitize_sql_for_assignment(updates)} " + # sql = "UPDATE #{quoted_table_name} SET #{sanitize_sql_for_assignment(updates)} " + # scope = scope(:find) + + # select_sql = "" + # add_conditions!(select_sql, conditions, scope) + + # if options.has_key?(:limit) || (scope && scope[:limit]) + # # Only take order from scope if limit is also provided by scope, this + # # is useful for updating a has_many association with a limit. + # add_order!(select_sql, options[:order], scope) + + # add_limit!(select_sql, options, scope) + # sql.concat(connection.limited_update_conditions(select_sql, quoted_table_name, connection.quote_column_name(primary_key))) + # else + # add_order!(select_sql, options[:order], nil) + # sql.concat(select_sql) + # end + # p sql + + # connection.update(sql, "#{name} Update") scope = scope(:find) - select_sql = "" - add_conditions!(select_sql, conditions, scope) + arel = arel_table + + if conditions = construct_conditions(conditions, scope) + arel = arel.where(Arel::SqlLiteral.new(conditions)) + end if options.has_key?(:limit) || (scope && scope[:limit]) # Only take order from scope if limit is also provided by scope, this # is useful for updating a has_many association with a limit. - add_order!(select_sql, options[:order], scope) - - add_limit!(select_sql, options, scope) - sql.concat(connection.limited_update_conditions(select_sql, quoted_table_name, connection.quote_column_name(primary_key))) + arel = arel.order(construct_order(options[:order], scope)).take(construct_limit(options, scope)) else - add_order!(select_sql, options[:order], nil) - sql.concat(select_sql) + arel = arel.order(construct_order(options[:order], nil)) end - connection.update(sql, "#{name} Update") + arel.update(sanitize_sql_for_assignment(updates)) end # Destroys the records matching +conditions+ by instantiating each -- cgit v1.2.3 From af9f9dd02c5539df0f05cb92e0799dc9e0bc8d46 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Wed, 10 Jun 2009 19:46:35 -0300 Subject: Use array of orders instead of string concatenation --- activerecord/lib/active_record/base.rb | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index adc7f5335d..df0cce4bf3 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1750,17 +1750,15 @@ module ActiveRecord #:nodoc: end def construct_order(order, scope = :auto) - sql = '' + orders = [] scoped_order = scope[:order] if scope if order - sql << order.to_s - if scoped_order && scoped_order != order - sql << ", #{scoped_order}" - end + orders << order + orders << scoped_order if scoped_order && scoped_order != order else - sql << scoped_order.to_s if scoped_order + orders << scoped_order if scoped_order end - sql + orders end def construct_limit(options, scope = :auto) -- cgit v1.2.3 From 3e4452c73f6f7ce2ff197b75d375f66f4a250a03 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Wed, 17 Jun 2009 15:10:57 -0300 Subject: Forget about auto scope, it's always explicit. --- activerecord/lib/active_record/base.rb | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 3f2b56d0c4..e04ec54913 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1736,7 +1736,7 @@ module ActiveRecord #:nodoc: construct_finder_arel(options).to_sql end - def construct_join(joins, scope = :auto) + def construct_join(joins, scope) merged_joins = scope && scope[:joins] && joins ? merge_joins(scope[:joins], joins) : (joins || scope && scope[:joins]) case merged_joins when Symbol, Hash, Array @@ -1753,13 +1753,12 @@ module ActiveRecord #:nodoc: end end - def construct_group(group, having, scope = :auto) + def construct_group(group, having, scope) sql = '' if group sql << group.to_s sql << " HAVING #{sanitize_sql_for_conditions(having)}" if having else - scope = scope(:find) if :auto == scope if scope && (scoped_group = scope[:group]) sql << scoped_group.to_s sql << " HAVING #{sanitize_sql_for_conditions(scope[:having])}" if scope[:having] @@ -1768,7 +1767,7 @@ module ActiveRecord #:nodoc: sql end - def construct_order(order, scope = :auto) + def construct_order(order, scope) orders = [] scoped_order = scope[:order] if scope if order @@ -1780,17 +1779,17 @@ module ActiveRecord #:nodoc: orders end - def construct_limit(options, scope = :auto) + def construct_limit(options, scope) options[:limit] ||= scope[:limit] if scope options[:limit] end - def construct_offset(options, scope = :auto) + def construct_offset(options, scope) options[:offset] ||= scope[:offset] if scope options[:offset] end - def construct_conditions(conditions, scope = :auto) + def construct_conditions(conditions, scope) conditions = [conditions] conditions << scope[:conditions] if scope conditions << type_condition if finder_needs_type_condition? -- cgit v1.2.3 From d587cf37947fe9a58c03eed3372717789b5349f4 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Tue, 23 Jun 2009 17:19:51 -0300 Subject: Removed old commented code --- activerecord/lib/active_record/base.rb | 21 --------------------- 1 file changed, 21 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index e04ec54913..32c339b338 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -840,27 +840,6 @@ module ActiveRecord #:nodoc: # # Update all books that match our conditions, but limit it to 5 ordered by date # Book.update_all "author = 'David'", "title LIKE '%Rails%'", :order => 'created_at', :limit => 5 def update_all(updates, conditions = nil, options = {}) - # sql = "UPDATE #{quoted_table_name} SET #{sanitize_sql_for_assignment(updates)} " - - # scope = scope(:find) - - # select_sql = "" - # add_conditions!(select_sql, conditions, scope) - - # if options.has_key?(:limit) || (scope && scope[:limit]) - # # Only take order from scope if limit is also provided by scope, this - # # is useful for updating a has_many association with a limit. - # add_order!(select_sql, options[:order], scope) - - # add_limit!(select_sql, options, scope) - # sql.concat(connection.limited_update_conditions(select_sql, quoted_table_name, connection.quote_column_name(primary_key))) - # else - # add_order!(select_sql, options[:order], nil) - # sql.concat(select_sql) - # end - # p sql - - # connection.update(sql, "#{name} Update") scope = scope(:find) arel = arel_table -- cgit v1.2.3 From 02a723f7b5f30b00fc77bc85162598f707c7b682 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Tue, 23 Jun 2009 17:38:31 -0300 Subject: Arel now buils SQL queries for associations. Removed old code and updated Arel version to support this. --- activerecord/lib/active_record/associations.rb | 53 +++------------------- .../abstract/schema_statements.rb | 6 --- .../connection_adapters/postgresql_adapter.rb | 14 ------ 3 files changed, 6 insertions(+), 67 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 29f58a04e6..97fdd9b1ba 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1604,14 +1604,6 @@ module ActiveRecord return sanitize_sql(arel.to_sql) end - def add_limited_ids_condition!(sql, options, join_dependency) - unless (id_list = select_limited_ids_list(options, join_dependency)).empty? - sql << "#{condition_word(sql)} #{connection.quote_table_name table_name}.#{primary_key} IN (#{id_list}) " - else - throw :invalid_query - end - end - def construct_limited_ids_condition(where, options, join_dependency) unless (id_list = select_limited_ids_list(options, join_dependency)).empty? "#{where.blank? ? '' : ' AND '} #{connection.quote_table_name table_name}.#{primary_key} IN (#{id_list}) " @@ -1630,47 +1622,18 @@ module ActiveRecord end def construct_finder_sql_for_association_limiting(options, join_dependency) - scope = scope(:find) - # Only join tables referenced in order or conditions since this is particularly slow on the pre-query. tables_from_conditions = conditions_tables(options) tables_from_order = order_tables(options) all_tables = tables_from_conditions + tables_from_order - distinct_join_associations = all_tables.uniq.map{|table| + options[:joins] = all_tables.uniq.map {|table| join_dependency.joins_for_table_name(table) - }.flatten.compact.uniq - - order = options[:order] - if scoped_order = (scope && scope[:order]) - order = order ? "#{order}, #{scoped_order}" : scoped_order - end - - is_distinct = !options[:joins].blank? || include_eager_conditions?(options, tables_from_conditions) || include_eager_order?(options, tables_from_order) - sql = "SELECT " - if is_distinct - sql << connection.distinct("#{connection.quote_table_name table_name}.#{primary_key}", order) - else - sql << primary_key - end - sql << " FROM #{connection.quote_table_name table_name} " + }.flatten.compact.uniq.collect { |assoc| assoc.association_join }.join - if is_distinct - sql << distinct_join_associations.collect { |assoc| assoc.association_join }.join - add_joins!(sql, options[:joins], scope) - end - - add_conditions!(sql, options[:conditions], scope) - add_group!(sql, options[:group], options[:having], scope) - - if order && is_distinct - connection.add_order_by_for_association_limiting!(sql, :order => order) - else - add_order!(sql, options[:order], scope) - end - - add_limit!(sql, options, scope) - - return sanitize_sql(sql) + construct_finder_sql(options.merge( + :select => connection.distinct("#{connection.quote_table_name table_name}.#{primary_key}", construct_order(options[:order], scope(:find)).join(",")) + ) + ) end def tables_in_string(string) @@ -1774,10 +1737,6 @@ module ActiveRecord end end - def condition_word(sql) - sql =~ /where/i ? " AND " : "WHERE " - end - def create_extension_modules(association_id, block_extension, extensions) if block_extension extension_module_name = "#{self.to_s.demodulize}#{association_id.to_s.camelize}AssociationExtension" 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 ff63ea3a2e..2f01a303e5 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -403,12 +403,6 @@ module ActiveRecord "DISTINCT #{columns}" end - # ORDER BY clause for the passed order option. - # PostgreSQL overrides this due to its stricter standards compliance. - def add_order_by_for_association_limiting!(sql, options) - sql << " ORDER BY #{options[:order]}" - end - # Adds timestamps (created_at and updated_at) columns to the named table. # ===== Examples # add_timestamps(:suppliers) diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 90a90a0a9b..6b230787c3 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -911,20 +911,6 @@ module ActiveRecord sql << order_columns * ', ' end - # Returns an ORDER BY clause for the passed order option. - # - # PostgreSQL does not allow arbitrary ordering when using DISTINCT ON, so we work around this - # by wrapping the +sql+ string as a sub-select and ordering in that query. - def add_order_by_for_association_limiting!(sql, options) #:nodoc: - return sql if options[:order].blank? - - order = options[:order].split(',').collect { |s| s.strip }.reject(&:blank?) - order.map! { |s| 'DESC' if s =~ /\bdesc$/i } - order = order.zip((0...order.size).to_a).map { |s,i| "id_list.alias_#{i} #{s}" }.join(', ') - - sql.replace "SELECT * FROM (#{sql}) AS id_list ORDER BY #{order}" - end - protected # Returns the version of the connected PostgreSQL version. def postgresql_version -- cgit v1.2.3 From 4864f92ee34e840307d968fa8b04972b6d786fe8 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Tue, 23 Jun 2009 17:57:31 -0300 Subject: Removed unused methods. --- activerecord/lib/active_record/base.rb | 68 +--------------------- .../abstract/database_statements.rb | 27 --------- .../connection_adapters/mysql_adapter.rb | 12 ---- 3 files changed, 1 insertion(+), 106 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 32c339b338..cc601b0d11 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1812,44 +1812,6 @@ module ActiveRecord #:nodoc: o.is_a?(Array) && o.all?{|obj| obj.is_a?(String)} end - def add_order!(sql, order, scope = :auto) - scope = scope(:find) if :auto == scope - scoped_order = scope[:order] if scope - if order - sql << " ORDER BY #{order}" - if scoped_order && scoped_order != order - sql << ", #{scoped_order}" - end - else - sql << " ORDER BY #{scoped_order}" if scoped_order - end - end - - def add_group!(sql, group, having, scope = :auto) - if group - sql << " GROUP BY #{group}" - sql << " HAVING #{sanitize_sql_for_conditions(having)}" if having - else - scope = scope(:find) if :auto == scope - if scope && (scoped_group = scope[:group]) - sql << " GROUP BY #{scoped_group}" - sql << " HAVING #{sanitize_sql_for_conditions(scope[:having])}" if scope[:having] - end - end - end - - # The optional scope argument is for the current :find scope. - def add_limit!(sql, options, scope = :auto) - scope = scope(:find) if :auto == scope - - if scope - options[:limit] ||= scope[:limit] - options[:offset] ||= scope[:offset] - end - - connection.add_limit_offset!(sql, options) - end - # The optional scope argument is for the current :find scope. # The :lock option has precedence over a scoped :lock. def add_lock!(sql, options, scope = :auto) @@ -1858,38 +1820,10 @@ module ActiveRecord #:nodoc: connection.add_lock!(sql, options) end - # The optional scope argument is for the current :find scope. - def add_joins!(sql, joins, scope = :auto) - scope = scope(:find) if :auto == scope - merged_joins = scope && scope[:joins] && joins ? merge_joins(scope[:joins], joins) : (joins || scope && scope[:joins]) - case merged_joins - when Symbol, Hash, Array - if array_of_strings?(merged_joins) - sql << merged_joins.join(' ') + " " - else - join_dependency = ActiveRecord::Associations::ClassMethods::InnerJoinDependency.new(self, merged_joins, nil) - sql << " #{join_dependency.join_associations.collect { |assoc| assoc.association_join }.join} " - end - when String - sql << " #{merged_joins} " - end - end - - # Adds a sanitized version of +conditions+ to the +sql+ string. Note that the passed-in +sql+ string is changed. - # The optional scope argument is for the current :find scope. - def add_conditions!(sql, conditions, scope = :auto) - scope = scope(:find) if :auto == scope - conditions = [conditions] - conditions << scope[:conditions] if scope - conditions << type_condition if finder_needs_type_condition? - merged_conditions = merge_conditions(*conditions) - sql << "WHERE #{merged_conditions} " unless merged_conditions.blank? - end - def type_condition(table_alias=nil) quoted_table_alias = self.connection.quote_table_name(table_alias || table_name) quoted_inheritance_column = connection.quote_column_name(inheritance_column) - type_condition = subclasses.inject("#{quoted_table_alias}.#{quoted_inheritance_column} = '#{sti_name}' ") do |condition, subclass| + type_condition = subclasses.inject("#{quoted_table_alias}.#{quoted_inheritance_column} = '#{sti_name}' " ) do |condition, subclass| condition << "OR #{quoted_table_alias}.#{quoted_inheritance_column} = '#{subclass.sti_name}' " end 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 ce04a8109d..26bf04f449 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb @@ -181,33 +181,6 @@ module ActiveRecord # done if the transaction block raises an exception or returns false. def rollback_db_transaction() end - # Alias for add_limit_offset!. - def add_limit!(sql, options) - add_limit_offset!(sql, options) if options - end - - # Appends +LIMIT+ and +OFFSET+ options to an SQL statement, or some SQL - # fragment that has the same semantics as LIMIT and OFFSET. - # - # +options+ must be a Hash which contains a +:limit+ option (required) - # and an +:offset+ option (optional). - # - # This method *modifies* the +sql+ parameter. - # - # ===== Examples - # add_limit_offset!('SELECT * FROM suppliers', {:limit => 10, :offset => 50}) - # generates - # SELECT * FROM suppliers LIMIT 10 OFFSET 50 - def add_limit_offset!(sql, options) - if limit = options[:limit] - sql << " LIMIT #{sanitize_limit(limit)}" - if offset = options[:offset] - sql << " OFFSET #{offset.to_i}" - end - end - sql - end - # Appends a locking clause to an SQL statement. # This method *modifies* the +sql+ parameter. # # SELECT * FROM suppliers FOR UPDATE diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb index 6d28e01544..cee93dcac2 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb @@ -368,18 +368,6 @@ module ActiveRecord execute("RELEASE SAVEPOINT #{current_savepoint_name}") end - def add_limit_offset!(sql, options) #:nodoc: - if limit = options[:limit] - limit = sanitize_limit(limit) - unless offset = options[:offset] - sql << " LIMIT #{limit}" - else - sql << " LIMIT #{offset.to_i}, #{limit}" - end - end - end - - # SCHEMA STATEMENTS ======================================== def structure_dump #:nodoc: -- cgit v1.2.3 From 3b27a4856f9a1a4d8f4e6d80343fcd5a80c40b35 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Tue, 23 Jun 2009 19:40:57 -0300 Subject: Refactoring: Calculations now use construct_finder_sql instead they own method. --- activerecord/lib/active_record/base.rb | 10 ++++------ activerecord/lib/active_record/calculations.rb | 23 ++++------------------- 2 files changed, 8 insertions(+), 25 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 2453d38aca..c7fc1fa124 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1696,11 +1696,9 @@ module ActiveRecord #:nodoc: @arel_table = Arel::Table.new(table) end - def construct_finder_arel(options) - scope = scope(:find) - + def construct_finder_arel(options, scope = scope(:find)) # TODO add lock to Arel - arel_table(table_name). + arel_table(options[:from] || table_name). join(construct_join(options[:joins], scope)). where(construct_conditions(options[:conditions], scope)). project(options[:select] || (scope && scope[:select]) || default_select(options[:joins] || (scope && scope[:joins]))). @@ -1711,8 +1709,8 @@ module ActiveRecord #:nodoc: ) end - def construct_finder_sql(options) - construct_finder_arel(options).to_sql + def construct_finder_sql(options, scope = scope(:find)) + construct_finder_arel(options, scope).to_sql end def construct_join(joins, scope) diff --git a/activerecord/lib/active_record/calculations.rb b/activerecord/lib/active_record/calculations.rb index 15ca5e14a2..5c7247b9c0 100644 --- a/activerecord/lib/active_record/calculations.rb +++ b/activerecord/lib/active_record/calculations.rb @@ -169,11 +169,11 @@ module ActiveRecord (column_name == :all ? "*" : column_name.to_s)) end - value = construct_calculation_arel(options.merge( + value = construct_finder_sql(options.merge( :select => operation == 'count' ? column.count(options[:distinct]) : column.send(operation) - )) + ), nil) - type_cast_calculated_value(connection.select_value(value.to_sql), column_for(column_name), operation) + type_cast_calculated_value(connection.select_value(value), column_for(column_name), operation) end def execute_grouped_calculation(operation, column_name, options) #:nodoc: @@ -194,7 +194,7 @@ module ActiveRecord options[:select] << ", #{group_field} AS #{group_alias}" - calculated_data = connection.select_all(construct_calculation_arel(options).to_sql) + calculated_data = connection.select_all(construct_finder_sql(options, nil)) if association key_ids = calculated_data.collect { |row| row[group_alias] } @@ -212,21 +212,6 @@ module ActiveRecord end protected - - def construct_calculation_arel(options) - scope = scope(:find) - - arel_table(options[:from] || table_name). - join(options[:joins]). - where(options[:conditions]). - project(options[:select]). - group(construct_group(options[:group], options[:having], scope)). - order(options[:order].to_s). - take(construct_limit(options, scope)). - skip(construct_offset(options, scope) - ) - end - def construct_count_options_from_args(*args) options = {} column_name = :all -- cgit v1.2.3 From 0f5785c99799b70919e18df3ef1cb43761979f5d Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Wed, 1 Jul 2009 16:16:11 -0300 Subject: Small refactor to update_all. --- activerecord/lib/active_record/base.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 56ed851d6a..e8793c3d2d 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -867,12 +867,12 @@ module ActiveRecord #:nodoc: arel = arel.where(Arel::SqlLiteral.new(conditions)) end - if options.has_key?(:limit) || (scope && scope[:limit]) + arel = if options.has_key?(:limit) || (scope && scope[:limit]) # Only take order from scope if limit is also provided by scope, this # is useful for updating a has_many association with a limit. - arel = arel.order(construct_order(options[:order], scope)).take(construct_limit(options, scope)) + arel.order(construct_order(options[:order], scope)).take(construct_limit(options, scope)) else - arel = arel.order(construct_order(options[:order], nil)) + arel.order(construct_order(options[:order], nil)) end arel.update(sanitize_sql_for_assignment(updates)) -- cgit v1.2.3 From 260c847817bbc3ad990609b7e41f787e1d2940ea Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Wed, 1 Jul 2009 20:34:26 -0300 Subject: Create is now powered by Arel. Removed methods that are no longer used. --- activerecord/lib/active_record/base.rb | 29 +++++++++++----------- .../abstract/database_statements.rb | 4 +-- .../connection_adapters/sqlite_adapter.rb | 4 +-- 3 files changed, 19 insertions(+), 18 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index e8793c3d2d..1f96e6dcc5 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -2907,18 +2907,13 @@ module ActiveRecord #:nodoc: self.id = connection.next_sequence_value(self.class.sequence_name) end - quoted_attributes = attributes_with_quotes - - statement = if quoted_attributes.empty? - connection.empty_insert_statement(self.class.table_name) + new_id = if arel_attributes_values.empty? + arel_table.insert connection.empty_insert_statement_value else - "INSERT INTO #{self.class.quoted_table_name} " + - "(#{quoted_column_names.join(', ')}) " + - "VALUES(#{quoted_attributes.values.join(', ')})" + arel_table.insert arel_attributes_values end - self.id = connection.insert(statement, "#{self.class.name} Create", - self.class.primary_key, self.id, self.class.sequence_name) + self.id ||= new_id @new_record = false id @@ -2987,6 +2982,10 @@ module ActiveRecord #:nodoc: default end + def arel_table + @arel_table = Arel::Table.new(self.class.table_name) + end + # Returns a copy of the attributes hash where all the values have been safely quoted for use in # an SQL statement. def attributes_with_quotes(include_primary_key = true, include_readonly_attributes = true, attribute_names = @attributes.keys) @@ -3007,10 +3006,8 @@ module ActiveRecord #:nodoc: include_readonly_attributes ? quoted : remove_readonly_attributes(quoted) end - def arel_table - @arel_table = Arel::Table.new(self.class.table_name) - end - + # Returns a copy of the attributes hash where all the values have been safely quoted for use in + # an Arel insert/update method. def arel_attributes_values(include_primary_key = true, include_readonly_attributes = true, attribute_names = @attributes.keys) attrs = {} connection = self.class.connection @@ -3019,7 +3016,11 @@ module ActiveRecord #:nodoc: value = read_attribute(name) if include_readonly_attributes || (!include_readonly_attributes && !self.class.readonly_attributes.include?(name)) - attrs[arel_table[name]] = value.is_a?(Hash) ? value.to_yaml : value + # We need explicit to_yaml because quote() does not properly convert Time/Date fields to YAML. + if value && self.class.serialized_attributes.has_key?(name) && (value.acts_like?(:date) || value.acts_like?(:time)) + value = value.to_yaml + end + attrs[arel_table[name]] = (value.is_a?(Hash) || value.is_a?(Array)) ? value.to_yaml : value end end end 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 26bf04f449..be89873632 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb @@ -208,8 +208,8 @@ module ActiveRecord execute "INSERT INTO #{quote_table_name(table_name)} (#{fixture.key_list}) VALUES (#{fixture.value_list})", 'Fixture Insert' end - def empty_insert_statement(table_name) - "INSERT INTO #{quote_table_name(table_name)} VALUES(DEFAULT)" + def empty_insert_statement_value + "VALUES(DEFAULT)" end def case_sensitive_equality_operator diff --git a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb index 8cbe08cd77..16a976a8c7 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb @@ -308,8 +308,8 @@ module ActiveRecord alter_table(table_name, :rename => {column_name.to_s => new_column_name.to_s}) end - def empty_insert_statement(table_name) - "INSERT INTO #{table_name} VALUES(NULL)" + def empty_insert_statement_value + "VALUES(NULL)" end protected -- cgit v1.2.3 From f4a23567e2612fc72ff3655a9169ed032610fc84 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Thu, 16 Jul 2009 13:35:07 -0300 Subject: Added ActiveRecord::Base#(where|join|project|group|order|take|skip) methods. --- activerecord/lib/active_record/associations.rb | 16 ++--- activerecord/lib/active_record/base.rb | 83 ++++++++++++++++---------- 2 files changed, 59 insertions(+), 40 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 31d3a89b9d..a4228e2a2a 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1599,16 +1599,16 @@ module ActiveRecord conditions = construct_conditions(options[:conditions], scope) || '' conditions << construct_limited_ids_condition(conditions, options, join_dependency) if !using_limitable_reflections?(join_dependency.reflections) && ((scope && scope[:limit]) || options[:limit]) - arel = arel_table((scope && scope[:from]) || options[:from] || table_name). - join(joins). - where(conditions). - project(column_aliases(join_dependency)). - group(construct_group(options[:group], options[:having], scope)). - order(construct_order(options[:order], scope)) + arel_table((scope && scope[:from]) || options[:from]) + join(joins) + where(conditions) + project(column_aliases(join_dependency)) + group(construct_group(options[:group], options[:having], scope)) + order(construct_order(options[:order], scope)) - arel = arel.take(construct_limit(options, scope)) if using_limitable_reflections?(join_dependency.reflections) + take(construct_limit(options[:limit], scope)) if using_limitable_reflections?(join_dependency.reflections) - return sanitize_sql(arel.to_sql) + return sanitize_sql(arel_relation.to_sql) end def construct_limited_ids_condition(where, options, join_dependency) diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index bb6869c6ce..400884264f 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -861,21 +861,22 @@ module ActiveRecord #:nodoc: def update_all(updates, conditions = nil, options = {}) scope = scope(:find) - arel = arel_table + arel_table if conditions = construct_conditions(conditions, scope) - arel = arel.where(Arel::SqlLiteral.new(conditions)) + where(Arel::SqlLiteral.new(conditions)) end - arel = if options.has_key?(:limit) || (scope && scope[:limit]) + if options.has_key?(:limit) || (scope && scope[:limit]) # Only take order from scope if limit is also provided by scope, this # is useful for updating a has_many association with a limit. - arel.order(construct_order(options[:order], scope)).take(construct_limit(options, scope)) + order(construct_order(options[:order], scope)) + take(construct_limit(options[:limit], scope)) else - arel.order(construct_order(options[:order], nil)) + order(construct_order(options[:order], nil)) end - arel.update(sanitize_sql_for_assignment(updates)) + arel_relation.update(sanitize_sql_for_assignment(updates)) end # Destroys the records matching +conditions+ by instantiating each @@ -1532,6 +1533,30 @@ module ActiveRecord #:nodoc: "(#{segments.join(') AND (')})" unless segments.empty? end + + def arel_table(table = nil) + table = table_name if table.blank? + self.arel_relation = Arel::Table.new(table) + end + + def arel_relation + Thread.current[:"#{self}_arel_relation"] ||= Arel::Table.new(table_name) + end + + def arel_relation=(relation) + Thread.current[:"#{self}_arel_relation"] = relation + end + + CLAUSES_METHODS = ["where", "join", "project", "group", "order", "take", "skip"].freeze + + for clause in CLAUSES_METHODS + class_eval %{ + def #{clause}(_#{clause}) + self.arel_relation = self.arel_relation.#{clause}(_#{clause}) if _#{clause} + end + } + end + private def find_initial(options) options.update(:limit => 1) @@ -1711,21 +1736,17 @@ module ActiveRecord #:nodoc: end end - def arel_table(table = table_name) - @arel_table = Arel::Table.new(table) - end - def construct_finder_arel(options, scope = scope(:find)) # TODO add lock to Arel - arel_table(options[:from] || table_name). - join(construct_join(options[:joins], scope)). - where(construct_conditions(options[:conditions], scope)). - project(options[:select] || (scope && scope[:select]) || default_select(options[:joins] || (scope && scope[:joins]))). - group(construct_group(options[:group], options[:having], scope)). - order(construct_order(options[:order], scope)). - take(construct_limit(options, scope)). - skip(construct_offset(options, scope) - ) + arel_table(options[:from]) + join(construct_join(options[:joins], scope)) + where(construct_conditions(options[:conditions], scope)) + project(options[:select] || (scope && scope[:select]) || default_select(options[:joins] || (scope && scope[:joins]))) + group(construct_group(options[:group], options[:having], scope)) + order(construct_order(options[:order], scope)) + take(construct_limit(options[:limit], scope)) + skip(construct_offset(options[:offset], scope)) + arel_relation end def construct_finder_sql(options, scope = scope(:find)) @@ -1754,11 +1775,9 @@ module ActiveRecord #:nodoc: if group sql << group.to_s sql << " HAVING #{sanitize_sql_for_conditions(having)}" if having - else - if scope && (scoped_group = scope[:group]) - sql << scoped_group.to_s - sql << " HAVING #{sanitize_sql_for_conditions(scope[:having])}" if scope[:having] - end + elsif scope && (scoped_group = scope[:group]) + sql << scoped_group.to_s + sql << " HAVING #{sanitize_sql_for_conditions(scope[:having])}" if scope[:having] end sql end @@ -1769,20 +1788,20 @@ module ActiveRecord #:nodoc: if order orders << order orders << scoped_order if scoped_order && scoped_order != order - else - orders << scoped_order if scoped_order + elsif scoped_order + orders << scoped_order end orders end - def construct_limit(options, scope) - options[:limit] ||= scope[:limit] if scope - options[:limit] + def construct_limit(limit, scope) + limit ||= scope[:limit] if scope + limit end - def construct_offset(options, scope) - options[:offset] ||= scope[:offset] if scope - options[:offset] + def construct_offset(offset, scope) + offset ||= scope[:offset] if scope + offset end def construct_conditions(conditions, scope) -- cgit v1.2.3 From b326faef0936e5a845d1f6eb9ed2200babfd05f8 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Mon, 20 Jul 2009 16:56:15 -0300 Subject: Performance boost for AR#create --- activerecord/lib/active_record/base.rb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 400884264f..60bd38e74c 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -2933,10 +2933,12 @@ module ActiveRecord #:nodoc: self.id = connection.next_sequence_value(self.class.sequence_name) end - new_id = if arel_attributes_values.empty? + attributes_values = arel_attributes_values + + new_id = if attributes_values.empty? arel_table.insert connection.empty_insert_statement_value else - arel_table.insert arel_attributes_values + arel_table.insert attributes_values end self.id ||= new_id @@ -3036,7 +3038,6 @@ module ActiveRecord #:nodoc: # an Arel insert/update method. def arel_attributes_values(include_primary_key = true, include_readonly_attributes = true, attribute_names = @attributes.keys) attrs = {} - connection = self.class.connection attribute_names.each do |name| if (column = column_for_attribute(name)) && (include_primary_key || !column.primary) value = read_attribute(name) -- cgit v1.2.3 From 0e0866e0565bd530ee265b9298accff4185f7022 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Tue, 21 Jul 2009 20:21:03 -0300 Subject: Introduced ActiveRecord::Relation, a layer between an ARel relation and an AR relation --- activerecord/lib/active_record/associations.rb | 17 ++++---- activerecord/lib/active_record/base.rb | 56 +++++++++----------------- activerecord/lib/active_record/relation.rb | 39 ++++++++++++++++++ 3 files changed, 67 insertions(+), 45 deletions(-) create mode 100644 activerecord/lib/active_record/relation.rb (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 47f97718eb..a0aeff68b6 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1602,16 +1602,17 @@ module ActiveRecord conditions = construct_conditions(options[:conditions], scope) || '' conditions << construct_limited_ids_condition(conditions, options, join_dependency) if !using_limitable_reflections?(join_dependency.reflections) && ((scope && scope[:limit]) || options[:limit]) - arel_table((scope && scope[:from]) || options[:from]) - join(joins) - where(conditions) - project(column_aliases(join_dependency)) - group(construct_group(options[:group], options[:having], scope)) - order(construct_order(options[:order], scope)) + relation = arel_table((scope && scope[:from]) || options[:from]). + join(joins). + where(conditions). + project(column_aliases(join_dependency)). + group(construct_group(options[:group], options[:having], scope)). + order(construct_order(options[:order], scope) + ) - take(construct_limit(options[:limit], scope)) if using_limitable_reflections?(join_dependency.reflections) + relation = relation.take(construct_limit(options[:limit], scope)) if using_limitable_reflections?(join_dependency.reflections) - return sanitize_sql(arel_relation.to_sql) + return sanitize_sql(relation.to_sql) end def construct_limited_ids_condition(where, options, join_dependency) diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 85f7cbfad2..718f7ea37b 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -663,7 +663,8 @@ module ActiveRecord #:nodoc: # This is an alias for find(:all). You can pass in all the same arguments to this method as you can # to find(:all) def all(*args) - find(:all, *args) + relation = arel_table + construct_finder_arel(*args) end # Executes a custom SQL query against your database and returns all the results. The results will @@ -861,22 +862,21 @@ module ActiveRecord #:nodoc: def update_all(updates, conditions = nil, options = {}) scope = scope(:find) - arel_table + relation = arel_table.relation if conditions = construct_conditions(conditions, scope) - where(Arel::SqlLiteral.new(conditions)) + relation = relation.where(Arel::SqlLiteral.new(conditions)) end if options.has_key?(:limit) || (scope && scope[:limit]) # Only take order from scope if limit is also provided by scope, this # is useful for updating a has_many association with a limit. - order(construct_order(options[:order], scope)) - take(construct_limit(options[:limit], scope)) + relation = relation.order(construct_order(options[:order], scope)).take(construct_limit(options[:limit], scope)) else - order(construct_order(options[:order], nil)) + relation = relation.order(construct_order(options[:order], nil)) end - arel_relation.update(sanitize_sql_for_assignment(updates)) + relation.update(sanitize_sql_for_assignment(updates)) end # Destroys the records matching +conditions+ by instantiating each @@ -1536,25 +1536,7 @@ module ActiveRecord #:nodoc: def arel_table(table = nil) table = table_name if table.blank? - self.arel_relation = Arel::Table.new(table) - end - - def arel_relation - Thread.current[:"#{self}_arel_relation"] ||= Arel::Table.new(table_name) - end - - def arel_relation=(relation) - Thread.current[:"#{self}_arel_relation"] = relation - end - - CLAUSES_METHODS = ["where", "join", "project", "group", "order", "take", "skip"].freeze - - for clause in CLAUSES_METHODS - class_eval %{ - def #{clause}(_#{clause}) - self.arel_relation = self.arel_relation.#{clause}(_#{clause}) if _#{clause} - end - } + Relation.new(self, table) end private @@ -1736,21 +1718,21 @@ module ActiveRecord #:nodoc: end end - def construct_finder_arel(options, scope = scope(:find)) + def construct_finder_arel(options = {}, scope = scope(:find)) # TODO add lock to Arel - arel_table(options[:from]) - join(construct_join(options[:joins], scope)) - where(construct_conditions(options[:conditions], scope)) - project(options[:select] || (scope && scope[:select]) || default_select(options[:joins] || (scope && scope[:joins]))) - group(construct_group(options[:group], options[:having], scope)) - order(construct_order(options[:order], scope)) - take(construct_limit(options[:limit], scope)) - skip(construct_offset(options[:offset], scope)) - arel_relation + arel_table(options[:from]). + join(construct_join(options[:joins], scope)). + where(construct_conditions(options[:conditions], scope)). + project(options[:select] || (scope && scope[:select]) || default_select(options[:joins] || (scope && scope[:joins]))). + group(construct_group(options[:group], options[:having], scope)). + order(construct_order(options[:order], scope)). + take(construct_limit(options[:limit], scope)). + skip(construct_offset(options[:offset], scope) + ) end def construct_finder_sql(options, scope = scope(:find)) - construct_finder_arel(options, scope).to_sql + construct_finder_arel(options, scope).relation.to_sql end def construct_join(joins, scope) diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb new file mode 100644 index 0000000000..36e8d98298 --- /dev/null +++ b/activerecord/lib/active_record/relation.rb @@ -0,0 +1,39 @@ +module ActiveRecord + class Relation + delegate :delete, :to_sql, :to => :relation + CLAUSES_METHODS = ["where", "join", "project", "group", "order", "take", "skip"].freeze + attr_reader :relation, :klass + + def initialize(klass, table = nil) + @klass = klass + @relation = Arel::Table.new(table || @klass.table_name) + end + + def to_a + @klass.find_by_sql(@relation.to_sql) + end + + def first + @relation = @relation.take(1) + to_a.first + end + + for clause in CLAUSES_METHODS + class_eval %{ + def #{clause}(_#{clause}) + @relation = @relation.#{clause}(_#{clause}) if _#{clause} + self + end + } + end + + private + def method_missing(method, *args, &block) + if @relation.respond_to?(method) + @relation.send(method, *args, &block) + elsif Array.instance_methods.include?(method.to_s) + to_a.send(method, *args, &block) + end + end + end +end -- cgit v1.2.3 From 5123a2359b964ba31c93296a36c9899564b537a8 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Tue, 21 Jul 2009 20:28:35 -0300 Subject: Removed unused local variable. --- activerecord/lib/active_record/base.rb | 1 - 1 file changed, 1 deletion(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 718f7ea37b..2375211577 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -663,7 +663,6 @@ module ActiveRecord #:nodoc: # This is an alias for find(:all). You can pass in all the same arguments to this method as you can # to find(:all) def all(*args) - relation = arel_table construct_finder_arel(*args) end -- cgit v1.2.3 From ca1e62f14219da3990bb354d4a363dbe6fa13435 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Tue, 21 Jul 2009 23:01:26 -0300 Subject: Performance: cache/reload arel relation when possible to speed up things. --- activerecord/lib/active_record/base.rb | 9 +++++---- activerecord/lib/active_record/locking/optimistic.rb | 4 ++-- 2 files changed, 7 insertions(+), 6 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 2375211577..7a34e345a2 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -2602,7 +2602,7 @@ module ActiveRecord #:nodoc: # be made (since they can't be persisted). def destroy unless new_record? - arel_table.where(arel_table[self.class.primary_key].eq(id)).delete + arel_table(true).where(arel_table[self.class.primary_key].eq(id)).delete end freeze @@ -2904,7 +2904,7 @@ module ActiveRecord #:nodoc: def update(attribute_names = @attributes.keys) attributes_with_values = arel_attributes_values(false, false, attribute_names) return 0 if attributes_with_values.empty? - arel_table.where(arel_table[self.class.primary_key].eq(id)).update(attributes_with_values) + arel_table(true).where(arel_table[self.class.primary_key].eq(id)).update(attributes_with_values) end # Creates a record with values matching those of the instance attributes @@ -2991,8 +2991,9 @@ module ActiveRecord #:nodoc: default end - def arel_table - @arel_table = Arel::Table.new(self.class.table_name) + def arel_table(reload = nil) + @arel_table = Relation.new(self, self.class.table_name) if reload || @arel_table.nil? + @arel_table end # Returns a copy of the attributes hash where all the values have been safely quoted for use in diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb index bc22c2a511..4e833ec871 100644 --- a/activerecord/lib/active_record/locking/optimistic.rb +++ b/activerecord/lib/active_record/locking/optimistic.rb @@ -89,7 +89,7 @@ module ActiveRecord attribute_names.uniq! begin - affected_rows = arel_table.where( + affected_rows = arel_table(true).where( arel_table[self.class.primary_key].eq(quoted_id).and( arel_table[self.class.locking_column].eq(quote_value(previous_value)) ) @@ -116,7 +116,7 @@ module ActiveRecord lock_col = self.class.locking_column previous_value = send(lock_col).to_i - affected_rows = arel_table.where( + affected_rows = arel_table(true).where( arel_table[self.class.primary_key].eq(quoted_id).and( arel_table[self.class.locking_column].eq(quote_value(previous_value)) ) -- cgit v1.2.3 From c1cbf02e3170f1004daf4a146cbc41176c2458d3 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Fri, 31 Jul 2009 16:08:22 -0300 Subject: Added ActiveRecord::Relation tests. Allow Relation to accept conditional hashes and arrays like #find does. --- activerecord/lib/active_record/base.rb | 8 ++++++-- activerecord/lib/active_record/relation.rb | 15 ++++++++++++++- 2 files changed, 20 insertions(+), 3 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 7a34e345a2..9a3a02870a 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -663,7 +663,11 @@ module ActiveRecord #:nodoc: # This is an alias for find(:all). You can pass in all the same arguments to this method as you can # to find(:all) def all(*args) - construct_finder_arel(*args) + if args.empty? && !scoped?(:find) + arel_table + else + construct_finder_arel(*args) + end end # Executes a custom SQL query against your database and returns all the results. The results will @@ -2992,7 +2996,7 @@ module ActiveRecord #:nodoc: end def arel_table(reload = nil) - @arel_table = Relation.new(self, self.class.table_name) if reload || @arel_table.nil? + @arel_table = Relation.new(self.class, self.class.table_name) if reload || @arel_table.nil? @arel_table end diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 36e8d98298..1c3a1dc53b 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -1,7 +1,7 @@ module ActiveRecord class Relation delegate :delete, :to_sql, :to => :relation - CLAUSES_METHODS = ["where", "join", "project", "group", "order", "take", "skip"].freeze + CLAUSES_METHODS = ["project", "group", "order", "take", "skip"].freeze attr_reader :relation, :klass def initialize(klass, table = nil) @@ -27,6 +27,19 @@ module ActiveRecord } end + def join(joins) + @relation = @relation.join(@klass.send(:construct_join, joins, nil)) if !joins.blank? + self + end + + def where(conditions) + if !conditions.blank? + conditions = @klass.send(:merge_conditions, conditions) if [String, Hash, Array].include?(conditions.class) + @relation = @relation.where(conditions) + end + self + end + private def method_missing(method, *args, &block) if @relation.respond_to?(method) -- cgit v1.2.3 From 31c83534d61cad2203ae57087442caedb4dbf22a Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Mon, 3 Aug 2009 13:59:34 -0300 Subject: Don't use local vars before testing its conditional. --- activerecord/lib/active_record/base.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 2b50333682..2f1f6175af 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1068,7 +1068,7 @@ module ActiveRecord #:nodoc: # If the access logic of your application is richer you can use Hash#except # or Hash#slice to sanitize the hash of parameters before they are # passed to Active Record. - # + # # For example, it could be the case that the list of protected attributes # for a given model depends on the role of the user: # @@ -1116,7 +1116,7 @@ module ActiveRecord #:nodoc: # If the access logic of your application is richer you can use Hash#except # or Hash#slice to sanitize the hash of parameters before they are # passed to Active Record. - # + # # For example, it could be the case that the list of accessible attributes # for a given model depends on the role of the user: # @@ -2968,14 +2968,14 @@ module ActiveRecord #:nodoc: attrs = {} attribute_names.each do |name| if (column = column_for_attribute(name)) && (include_primary_key || !column.primary) - value = read_attribute(name) if include_readonly_attributes || (!include_readonly_attributes && !self.class.readonly_attributes.include?(name)) - # We need explicit to_yaml because quote() does not properly convert Time/Date fields to YAML. - if value && self.class.serialized_attributes.has_key?(name) && (value.acts_like?(:date) || value.acts_like?(:time)) + value = read_attribute(name) + + if value && ((self.class.serialized_attributes.has_key?(name) && (value.acts_like?(:date) || value.acts_like?(:time))) || value.is_a?(Hash) || value.is_a?(Array)) value = value.to_yaml end - attrs[arel_table[name]] = (value.is_a?(Hash) || value.is_a?(Array)) ? value.to_yaml : value + attrs[arel_table[name]] = value end end end -- cgit v1.2.3 From f8eb4434d61fc1585c9b88dbb2d9159ea1f5b2fa Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Mon, 3 Aug 2009 14:15:47 -0300 Subject: Added collection iteration to AR::Relation. --- activerecord/lib/active_record/relation.rb | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 1c3a1dc53b..f89baa1a74 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -13,6 +13,10 @@ module ActiveRecord @klass.find_by_sql(@relation.to_sql) end + def each(&block) + to_a.each(&block) + end + def first @relation = @relation.take(1) to_a.first -- cgit v1.2.3 From 945ef58533e93d0abbf7ccdb3768e8654cbe6370 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Fri, 7 Aug 2009 13:16:34 -0300 Subject: More work on removing plain SQL from associations and use ARel instead. --- activerecord/lib/active_record/associations.rb | 39 +++++++++++++------------- activerecord/lib/active_record/calculations.rb | 31 ++++++++++++-------- 2 files changed, 39 insertions(+), 31 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 60da632b3b..7c445f5618 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1662,40 +1662,39 @@ module ActiveRecord def construct_finder_sql_with_included_associations(options, join_dependency) scope = scope(:find) + relation = arel_table((scope && scope[:from]) || options[:from]) + joins = join_dependency.join_associations.collect{|join| join.association_join }.join joins << construct_join(options[:joins], scope) + relation.join(joins) - conditions = construct_conditions(options[:conditions], scope) || '' - conditions << construct_limited_ids_condition(conditions, options, join_dependency) if !using_limitable_reflections?(join_dependency.reflections) && ((scope && scope[:limit]) || options[:limit]) - - relation = arel_table((scope && scope[:from]) || options[:from]). - join(joins). - where(conditions). - project(column_aliases(join_dependency)). - group(construct_group(options[:group], options[:having], scope)). - order(construct_order(options[:order], scope) - ) + relation.where(construct_conditions(options[:conditions], scope)) + relation.where(construct_arel_limited_ids_condition(options, join_dependency)) if join_dependency && !using_limitable_reflections?(join_dependency.reflections) && ((scope && scope[:limit]) || options[:limit]) - relation = relation.take(construct_limit(options[:limit], scope)) if using_limitable_reflections?(join_dependency.reflections) + relation.project(column_aliases(join_dependency)) + relation.group(construct_group(options[:group], options[:having], scope)) + relation.order(construct_order(options[:order], scope)) + relation.take(construct_limit(options[:limit], scope)) if using_limitable_reflections?(join_dependency.reflections) - return sanitize_sql(relation.to_sql) + sanitize_sql(relation.to_sql) end - def construct_limited_ids_condition(where, options, join_dependency) - unless (id_list = select_limited_ids_list(options, join_dependency)).empty? - "#{where.blank? ? '' : ' AND '} #{connection.quote_table_name table_name}.#{primary_key} IN (#{id_list}) " - else + def construct_arel_limited_ids_condition(options, join_dependency) + if (ids_array = select_limited_ids_array(options, join_dependency)).empty? throw :invalid_query + else + Arel::In.new( + Arel::SqlLiteral.new("#{connection.quote_table_name table_name}.#{primary_key}"), + ids_array + ) end end - def select_limited_ids_list(options, join_dependency) - pk = columns_hash[primary_key] - + def select_limited_ids_array(options, join_dependency) connection.select_all( construct_finder_sql_for_association_limiting(options, join_dependency), "#{name} Load IDs For Limited Eager Loading" - ).collect { |row| connection.quote(row[primary_key], pk) }.join(", ") + ).collect { |row| row[primary_key] } end def construct_finder_sql_for_association_limiting(options, join_dependency) diff --git a/activerecord/lib/active_record/calculations.rb b/activerecord/lib/active_record/calculations.rb index 5c7247b9c0..3c75200f82 100644 --- a/activerecord/lib/active_record/calculations.rb +++ b/activerecord/lib/active_record/calculations.rb @@ -148,19 +148,27 @@ module ActiveRecord end catch :invalid_query do - conditions = construct_conditions(options[:conditions], scope) - conditions << construct_limited_ids_condition(conditions, options, join_dependency) if join_dependency && !using_limitable_reflections?(join_dependency.reflections) && ((scope && scope[:limit]) || options[:limit]) + relation = arel_table((scope && scope[:from]) || options[:from]) + + relation.join(joins) + + relation.where(construct_conditions(options[:conditions], scope)) + relation.where(construct_arel_limited_ids_condition(options, join_dependency)) if join_dependency && !using_limitable_reflections?(join_dependency.reflections) && ((scope && scope[:limit]) || options[:limit]) + + relation.order(construct_order(options[:order], scope)) + relation.take(options[:limit]) + relation.skip(options[:offset]) if options[:group] - return execute_grouped_calculation(operation, column_name, options.merge(:conditions => conditions, :joins => joins, :distinct => distinct)) + return execute_grouped_calculation(operation, column_name, options, relation) else - return execute_simple_calculation(operation, column_name, options.merge(:conditions => conditions, :joins => joins, :distinct => distinct)) + return execute_simple_calculation(operation, column_name, options.merge(:distinct => distinct), relation) end end 0 end - def execute_simple_calculation(operation, column_name, options) #:nodoc: + def execute_simple_calculation(operation, column_name, options, relation) #:nodoc: column = if column_names.include?(column_name.to_s) Arel::Attribute.new(arel_table(options[:from] || table_name), options[:select] || column_name) @@ -169,14 +177,12 @@ module ActiveRecord (column_name == :all ? "*" : column_name.to_s)) end - value = construct_finder_sql(options.merge( - :select => operation == 'count' ? column.count(options[:distinct]) : column.send(operation) - ), nil) + relation.project(operation == 'count' ? column.count(options[:distinct]) : column.send(operation)) - type_cast_calculated_value(connection.select_value(value), column_for(column_name), operation) + type_cast_calculated_value(connection.select_value(relation.to_sql), column_for(column_name), operation) end - def execute_grouped_calculation(operation, column_name, options) #:nodoc: + def execute_grouped_calculation(operation, column_name, options, relation) #:nodoc: group_attr = options[:group].to_s association = reflect_on_association(group_attr.to_sym) associated = association && association.macro == :belongs_to # only count belongs_to associations @@ -194,7 +200,10 @@ module ActiveRecord options[:select] << ", #{group_field} AS #{group_alias}" - calculated_data = connection.select_all(construct_finder_sql(options, nil)) + relation.project(options[:select]) + relation.group(construct_group(options[:group], options[:having], nil)) + + calculated_data = connection.select_all(relation.to_sql) if association key_ids = calculated_data.collect { |row| row[group_alias] } -- cgit v1.2.3 From 6b5fab9300c3e5f2852409ad589d2166373787e7 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Fri, 7 Aug 2009 14:33:35 -0300 Subject: Don't scope order in calculations.' --- activerecord/lib/active_record/calculations.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/calculations.rb b/activerecord/lib/active_record/calculations.rb index 3c75200f82..43e4529575 100644 --- a/activerecord/lib/active_record/calculations.rb +++ b/activerecord/lib/active_record/calculations.rb @@ -155,7 +155,7 @@ module ActiveRecord relation.where(construct_conditions(options[:conditions], scope)) relation.where(construct_arel_limited_ids_condition(options, join_dependency)) if join_dependency && !using_limitable_reflections?(join_dependency.reflections) && ((scope && scope[:limit]) || options[:limit]) - relation.order(construct_order(options[:order], scope)) + relation.order(options[:order]) relation.take(options[:limit]) relation.skip(options[:offset]) -- cgit v1.2.3 From d469ad8a49779b3f3d6711989bb334534e94f099 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Fri, 7 Aug 2009 16:41:58 -0300 Subject: Removed useless OR. --- activerecord/lib/active_record/associations.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 7c445f5618..46378edb93 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -2184,7 +2184,7 @@ module ActiveRecord ] else "" - end || '' + end join << %(AND %s) % [ klass.send(:type_condition, aliased_table_name)] unless klass.descends_from_active_record? -- cgit v1.2.3 From ae9e1e9f6d08bbd5f5c1512b72d495168e9fa5e5 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Fri, 7 Aug 2009 17:55:36 -0300 Subject: Removed unused methods. --- activerecord/lib/active_record/associations.rb | 126 +++++++++++-------------- 1 file changed, 56 insertions(+), 70 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 46378edb93..aeca74ef4a 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1639,19 +1639,6 @@ module ActiveRecord reflection end - def reflect_on_included_associations(associations) - [ associations ].flatten.collect { |association| reflect_on_association(association.to_s.intern) } - end - - def guard_against_unlimitable_reflections(reflections, options) - if (options[:offset] || options[:limit]) && !using_limitable_reflections?(reflections) - raise( - ConfigurationError, - "You can not use offset and limit together with has_many or has_and_belongs_to_many associations" - ) - end - end - def select_all_rows(options, join_dependency) connection.select_all( construct_finder_sql_with_included_associations(options, join_dependency), @@ -2083,25 +2070,24 @@ module ActiveRecord options[:association_foreign_key] || klass.to_s.foreign_key ] when :has_many, :has_one - case - when reflection.options[:through] - through_conditions = through_reflection.options[:conditions] ? "AND #{interpolate_sql(sanitize_sql(through_reflection.options[:conditions]))}" : '' - - jt_foreign_key = jt_as_extra = jt_source_extra = jt_sti_extra = nil - first_key = second_key = as_extra = nil - - if through_reflection.options[:as] # has_many :through against a polymorphic join - jt_foreign_key = through_reflection.options[:as].to_s + '_id' - jt_as_extra = " AND %s.%s = %s" % [ - connection.quote_table_name(aliased_join_table_name), - connection.quote_column_name(through_reflection.options[:as].to_s + '_type'), - klass.quote_value(parent.active_record.base_class.name) - ] - else - jt_foreign_key = through_reflection.primary_key_name - end - - case source_reflection.macro + if reflection.options[:through] + through_conditions = through_reflection.options[:conditions] ? "AND #{interpolate_sql(sanitize_sql(through_reflection.options[:conditions]))}" : '' + + jt_foreign_key = jt_as_extra = jt_source_extra = jt_sti_extra = nil + first_key = second_key = as_extra = nil + + if through_reflection.options[:as] # has_many :through against a polymorphic join + jt_foreign_key = through_reflection.options[:as].to_s + '_id' + jt_as_extra = " AND %s.%s = %s" % [ + connection.quote_table_name(aliased_join_table_name), + connection.quote_column_name(through_reflection.options[:as].to_s + '_type'), + klass.quote_value(parent.active_record.base_class.name) + ] + else + jt_foreign_key = through_reflection.primary_key_name + end + + case source_reflection.macro when :has_many if source_reflection.options[:as] first_key = "#{source_reflection.options[:as]}_id" @@ -2134,45 +2120,45 @@ module ActiveRecord else second_key = source_reflection.primary_key_name end - end - - " #{join_type} %s ON (%s.%s = %s.%s%s%s%s) " % [ - table_alias_for(through_reflection.klass.table_name, aliased_join_table_name), - connection.quote_table_name(parent.aliased_table_name), - connection.quote_column_name(parent.primary_key), - connection.quote_table_name(aliased_join_table_name), - connection.quote_column_name(jt_foreign_key), - jt_as_extra, jt_source_extra, jt_sti_extra - ] + - " #{join_type} %s ON (%s.%s = %s.%s%s) " % [ - table_name_and_alias, - connection.quote_table_name(aliased_table_name), - connection.quote_column_name(first_key), - connection.quote_table_name(aliased_join_table_name), - connection.quote_column_name(second_key), - as_extra - ] + end + + " #{join_type} %s ON (%s.%s = %s.%s%s%s%s) " % [ + table_alias_for(through_reflection.klass.table_name, aliased_join_table_name), + connection.quote_table_name(parent.aliased_table_name), + connection.quote_column_name(parent.primary_key), + connection.quote_table_name(aliased_join_table_name), + connection.quote_column_name(jt_foreign_key), + jt_as_extra, jt_source_extra, jt_sti_extra + ] + + " #{join_type} %s ON (%s.%s = %s.%s%s) " % [ + table_name_and_alias, + connection.quote_table_name(aliased_table_name), + connection.quote_column_name(first_key), + connection.quote_table_name(aliased_join_table_name), + connection.quote_column_name(second_key), + as_extra + ] - when reflection.options[:as] && [:has_many, :has_one].include?(reflection.macro) - " #{join_type} %s ON %s.%s = %s.%s AND %s.%s = %s" % [ - table_name_and_alias, - connection.quote_table_name(aliased_table_name), - "#{reflection.options[:as]}_id", - connection.quote_table_name(parent.aliased_table_name), - parent.primary_key, - connection.quote_table_name(aliased_table_name), - "#{reflection.options[:as]}_type", - klass.quote_value(parent.active_record.base_class.name) - ] - else - foreign_key = options[:foreign_key] || reflection.active_record.name.foreign_key - " #{join_type} %s ON %s.%s = %s.%s " % [ - table_name_and_alias, - aliased_table_name, - foreign_key, - parent.aliased_table_name, - reflection.options[:primary_key] || parent.primary_key - ] + elsif reflection.options[:as] && [:has_many, :has_one].include?(reflection.macro) + " #{join_type} %s ON %s.%s = %s.%s AND %s.%s = %s" % [ + table_name_and_alias, + connection.quote_table_name(aliased_table_name), + "#{reflection.options[:as]}_id", + connection.quote_table_name(parent.aliased_table_name), + parent.primary_key, + connection.quote_table_name(aliased_table_name), + "#{reflection.options[:as]}_type", + klass.quote_value(parent.active_record.base_class.name) + ] + else + foreign_key = options[:foreign_key] || reflection.active_record.name.foreign_key + " #{join_type} %s ON %s.%s = %s.%s " % [ + table_name_and_alias, + aliased_table_name, + foreign_key, + parent.aliased_table_name, + reflection.options[:primary_key] || parent.primary_key + ] end when :belongs_to " #{join_type} %s ON %s.%s = %s.%s " % [ -- cgit v1.2.3 From 9ac01fad193b27e517ea772e0d1e13e06f4ddf34 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Fri, 14 Aug 2009 12:33:05 -0300 Subject: Use ARel's joins when building a query for finding records with included associations. --- activerecord/lib/active_record/associations.rb | 106 ++++++++++++++----------- activerecord/lib/active_record/base.rb | 21 +++-- activerecord/lib/active_record/calculations.rb | 23 ++---- activerecord/lib/active_record/relation.rb | 12 ++- 4 files changed, 88 insertions(+), 74 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index baad8fc5fd..e4cd515f15 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1673,14 +1673,20 @@ module ActiveRecord ) end - def construct_finder_sql_with_included_associations(options, join_dependency) + def construct_finder_arel_with_included_associations(options, join_dependency) scope = scope(:find) relation = arel_table((scope && scope[:from]) || options[:from]) - joins = join_dependency.join_associations.collect{|join| join.association_join }.join - joins << construct_join(options[:joins], scope) - relation.join(joins) + for association in join_dependency.join_associations + if association.relation.is_a?(Array) + relation.join(association.relation.first, association.join_type).on(association.association_join.first) + relation.join(association.relation.last, association.join_type).on(association.association_join.last) + else + relation.join(association.relation, association.join_type).on(association.association_join) + end + end + relation.join(construct_join(options[:joins], scope)) relation.where(construct_conditions(options[:conditions], scope)) relation.where(construct_arel_limited_ids_condition(options, join_dependency)) if !using_limitable_reflections?(join_dependency.reflections) && ((scope && scope[:limit]) || options[:limit]) @@ -1690,7 +1696,11 @@ module ActiveRecord relation.order(construct_order(options[:order], scope)) relation.take(construct_limit(options[:limit], scope)) if using_limitable_reflections?(join_dependency.reflections) - sanitize_sql(relation.to_sql) + relation + end + + def construct_finder_sql_with_included_associations(options, join_dependency) + sanitize_sql(construct_finder_arel_with_included_associations(options, join_dependency).to_sql) end def construct_arel_limited_ids_condition(options, join_dependency) @@ -1716,9 +1726,15 @@ module ActiveRecord relation = arel_table(options[:from]) - joins = join_dependency.join_associations.collect{|join| join.association_join }.join - joins << construct_join(options[:joins], scope) - relation.join(joins) + for association in join_dependency.join_associations + if association.relation.is_a?(Array) + relation.join(association.relation.first, association.join_type).on(association.association_join.first) + relation.join(association.relation.last, association.join_type).on(association.association_join.last) + else + relation.join(association.relation, association.join_type).on(association.association_join) + end + end + relation.join(construct_join(options[:joins], scope)) relation.where(construct_conditions(options[:conditions], scope)) relation.project(connection.distinct("#{connection.quote_table_name table_name}.#{primary_key}", construct_order(options[:order], scope(:find)).join(","))) @@ -1907,12 +1923,6 @@ module ActiveRecord end end - def join_for_table_name(table_name) - join = (@joins.select{|j|j.aliased_table_name == table_name.gsub(/^\"(.*)\"$/){$1} }.first) rescue nil - return join unless join.nil? - @joins.select{|j|j.is_a?(JoinAssociation) && j.aliased_join_table_name == table_name.gsub(/^\"(.*)\"$/){$1} }.first rescue nil - end - def joins_for_table_name(table_name) join = join_for_table_name(table_name) result = nil @@ -2088,19 +2098,18 @@ module ActiveRecord connection = reflection.active_record.connection join = case reflection.macro when :has_and_belongs_to_many - " #{join_type} %s ON %s.%s = %s.%s " % [ - table_alias_for(options[:join_table], aliased_join_table_name), + ["%s.%s = %s.%s " % [ connection.quote_table_name(aliased_join_table_name), options[:foreign_key] || reflection.active_record.to_s.foreign_key, connection.quote_table_name(parent.aliased_table_name), - reflection.active_record.primary_key] + - " #{join_type} %s ON %s.%s = %s.%s " % [ - table_name_and_alias, + reflection.active_record.primary_key], + "%s.%s = %s.%s " % [ connection.quote_table_name(aliased_table_name), klass.primary_key, connection.quote_table_name(aliased_join_table_name), options[:association_foreign_key] || klass.to_s.foreign_key ] + ] when :has_many, :has_one if reflection.options[:through] through_conditions = through_reflection.options[:conditions] ? "AND #{interpolate_sql(sanitize_sql(through_reflection.options[:conditions]))}" : '' @@ -2154,26 +2163,22 @@ module ActiveRecord end end - " #{join_type} %s ON (%s.%s = %s.%s%s%s%s) " % [ - table_alias_for(through_reflection.klass.table_name, aliased_join_table_name), + ["(%s.%s = %s.%s%s%s%s) " % [ connection.quote_table_name(parent.aliased_table_name), connection.quote_column_name(parent.primary_key), connection.quote_table_name(aliased_join_table_name), connection.quote_column_name(jt_foreign_key), - jt_as_extra, jt_source_extra, jt_sti_extra - ] + - " #{join_type} %s ON (%s.%s = %s.%s%s) " % [ - table_name_and_alias, + jt_as_extra, jt_source_extra, jt_sti_extra], + "(%s.%s = %s.%s%s) " % [ connection.quote_table_name(aliased_table_name), connection.quote_column_name(first_key), connection.quote_table_name(aliased_join_table_name), connection.quote_column_name(second_key), - as_extra + as_extra] ] elsif reflection.options[:as] && [:has_many, :has_one].include?(reflection.macro) - " #{join_type} %s ON %s.%s = %s.%s AND %s.%s = %s" % [ - table_name_and_alias, + "%s.%s = %s.%s AND %s.%s = %s" % [ connection.quote_table_name(aliased_table_name), "#{reflection.options[:as]}_id", connection.quote_table_name(parent.aliased_table_name), @@ -2184,8 +2189,7 @@ module ActiveRecord ] else foreign_key = options[:foreign_key] || reflection.active_record.name.foreign_key - " #{join_type} %s ON %s.%s = %s.%s " % [ - table_name_and_alias, + "%s.%s = %s.%s " % [ aliased_table_name, foreign_key, parent.aliased_table_name, @@ -2193,13 +2197,12 @@ module ActiveRecord ] end when :belongs_to - " #{join_type} %s ON %s.%s = %s.%s " % [ - table_name_and_alias, - connection.quote_table_name(aliased_table_name), - reflection.klass.primary_key, - connection.quote_table_name(parent.aliased_table_name), - options[:foreign_key] || reflection.primary_key_name - ] + "%s.%s = %s.%s " % [ + connection.quote_table_name(aliased_table_name), + reflection.klass.primary_key, + connection.quote_table_name(parent.aliased_table_name), + options[:foreign_key] || reflection.primary_key_name + ] else "" end @@ -2213,6 +2216,20 @@ module ActiveRecord join end + def relation + if reflection.macro == :has_and_belongs_to_many + [Arel::Table.new(table_alias_for(options[:join_table], aliased_join_table_name)), Arel::Table.new(table_name_and_alias)] + elsif reflection.options[:through] + [Arel::Table.new(table_alias_for(through_reflection.klass.table_name, aliased_join_table_name)), Arel::Table.new(table_name_and_alias)] + else + Arel::Table.new(table_name_and_alias) + end + end + + def join_type + Arel::OuterJoin + end + protected def aliased_table_name_for(name, suffix = nil) @@ -2238,7 +2255,7 @@ module ActiveRecord end def table_alias_for(table_name, table_alias) - "#{reflection.active_record.connection.quote_table_name(table_name)} #{table_alias if table_name != table_alias}".strip + "#{table_name} #{table_alias if table_name != table_alias}".strip end def table_name_and_alias @@ -2248,11 +2265,6 @@ module ActiveRecord def interpolate_sql(sql) instance_eval("%@#{sql.gsub('@', '\@')}@") end - - private - def join_type - "LEFT OUTER JOIN" - end end end @@ -2263,13 +2275,11 @@ module ActiveRecord end class InnerJoinAssociation < JoinAssociation - private - def join_type - "INNER JOIN" - end + def join_type + Arel::InnerJoin + end end end - end end end diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index e1f4461965..a6832b47ef 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1741,8 +1741,7 @@ module ActiveRecord #:nodoc: if array_of_strings?(merged_joins) merged_joins.join(' ') + " " else - join_dependency = ActiveRecord::Associations::ClassMethods::InnerJoinDependency.new(self, merged_joins, nil) - " #{join_dependency.join_associations.collect { |assoc| assoc.association_join }.join} " + build_association_joins(merged_joins) end when String " #{merged_joins} " @@ -1801,10 +1800,7 @@ module ActiveRecord #:nodoc: if joins.any?{|j| j.is_a?(String) || array_of_strings?(j) } joins = joins.collect do |join| join = [join] if join.is_a?(String) - unless array_of_strings?(join) - join_dependency = ActiveRecord::Associations::ClassMethods::InnerJoinDependency.new(self, join, nil) - join = join_dependency.join_associations.collect { |assoc| assoc.association_join } - end + join = build_association_joins(join) unless array_of_strings?(join) join end joins.flatten.map{|j| j.strip}.uniq @@ -1813,6 +1809,19 @@ module ActiveRecord #:nodoc: end end + def build_association_joins(joins) + join_dependency = ActiveRecord::Associations::ClassMethods::InnerJoinDependency.new(self, joins, nil) + relation = arel_table + join_dependency.join_associations.map { |association| + if association.relation.is_a?(Array) + [Arel::InnerJoin.new(relation, association.relation.first, association.association_join.first).joins(relation), + Arel::InnerJoin.new(relation, association.relation.last, association.association_join.last).joins(relation)].join() + else + Arel::InnerJoin.new(relation, association.relation, association.association_join).joins(relation) + end + }.join(" ") + end + # Object#to_a is deprecated, though it does have the desired behavior def safe_to_array(o) case o diff --git a/activerecord/lib/active_record/calculations.rb b/activerecord/lib/active_record/calculations.rb index 6a5f2222a2..ab501dac33 100644 --- a/activerecord/lib/active_record/calculations.rb +++ b/activerecord/lib/active_record/calculations.rb @@ -128,12 +128,6 @@ module ActiveRecord scope = scope(:find) merged_includes = merge_includes(scope ? scope[:include] : [], options[:include]) - joins = construct_join(options[:joins], scope) - - if merged_includes.any? - join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(self, merged_includes, joins) - joins << join_dependency.join_associations.collect{|join| join.association_join }.join - end if operation == "count" if merged_includes.any? @@ -148,17 +142,12 @@ module ActiveRecord end catch :invalid_query do - relation = arel_table((scope && scope[:from]) || options[:from]) - - relation.join(joins) - - relation.where(construct_conditions(options[:conditions], scope)) - relation.where(construct_arel_limited_ids_condition(options, join_dependency)) if join_dependency && !using_limitable_reflections?(join_dependency.reflections) && ((scope && scope[:limit]) || options[:limit]) - - relation.order(options[:order]) - relation.take(options[:limit]) - relation.skip(options[:offset]) - + relation = if merged_includes.any? + join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(self, merged_includes, construct_join(options[:joins], scope)) + construct_finder_arel_with_included_associations(options, join_dependency) + else + construct_finder_arel(options) + end if options[:group] return execute_grouped_calculation(operation, column_name, options, relation) else diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index f89baa1a74..456de73250 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -1,7 +1,7 @@ module ActiveRecord class Relation delegate :delete, :to_sql, :to => :relation - CLAUSES_METHODS = ["project", "group", "order", "take", "skip"].freeze + CLAUSES_METHODS = ["project", "group", "order", "take", "skip", "on"].freeze attr_reader :relation, :klass def initialize(klass, table = nil) @@ -31,8 +31,14 @@ module ActiveRecord } end - def join(joins) - @relation = @relation.join(@klass.send(:construct_join, joins, nil)) if !joins.blank? + def join(joins, join_type = nil) + if !joins.blank? + if [String, Hash, Array, Symbol].include?(joins.class) + @relation = @relation.join(@klass.send(:construct_join, joins, nil)) + else + @relation = @relation.join(joins, join_type) + end + end self end -- cgit v1.2.3 From 796ec652ade1bfbf074fc1cfd2cd2e7794f494bc Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Fri, 14 Aug 2009 15:59:55 -0300 Subject: Don't use regular rinder on calculations since scoping order blows PostreSQL. --- activerecord/lib/active_record/calculations.rb | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/calculations.rb b/activerecord/lib/active_record/calculations.rb index ab501dac33..658101d8ee 100644 --- a/activerecord/lib/active_record/calculations.rb +++ b/activerecord/lib/active_record/calculations.rb @@ -146,7 +146,13 @@ module ActiveRecord join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(self, merged_includes, construct_join(options[:joins], scope)) construct_finder_arel_with_included_associations(options, join_dependency) else - construct_finder_arel(options) + arel_table(options[:from]). + join(construct_join(options[:joins], scope)). + where(construct_conditions(options[:conditions], scope)). + group(construct_group(options[:group], options[:having], scope)). + order(options[:order]). + take(options[:limit]). + skip(options[:offset]) end if options[:group] return execute_grouped_calculation(operation, column_name, options, relation) -- cgit v1.2.3 From 770fca10fba68fa14308f33e923493e6f63bfa35 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Sun, 16 Aug 2009 17:27:45 -0300 Subject: Remove group when building the relation, it will be added later if options[:group] is given. --- activerecord/lib/active_record/calculations.rb | 1 - 1 file changed, 1 deletion(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/calculations.rb b/activerecord/lib/active_record/calculations.rb index 658101d8ee..82a171c6ad 100644 --- a/activerecord/lib/active_record/calculations.rb +++ b/activerecord/lib/active_record/calculations.rb @@ -149,7 +149,6 @@ module ActiveRecord arel_table(options[:from]). join(construct_join(options[:joins], scope)). where(construct_conditions(options[:conditions], scope)). - group(construct_group(options[:group], options[:having], scope)). order(options[:order]). take(options[:limit]). skip(options[:offset]) -- cgit v1.2.3 From c923409630a92d1a935699c1427702c822601165 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Mon, 17 Aug 2009 20:17:40 -0300 Subject: Clean up relation joins when finding records with included associations. --- activerecord/lib/active_record/associations.rb | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index e4cd515f15..406f08e247 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1679,12 +1679,7 @@ module ActiveRecord relation = arel_table((scope && scope[:from]) || options[:from]) for association in join_dependency.join_associations - if association.relation.is_a?(Array) - relation.join(association.relation.first, association.join_type).on(association.association_join.first) - relation.join(association.relation.last, association.join_type).on(association.association_join.last) - else - relation.join(association.relation, association.join_type).on(association.association_join) - end + relation = association.join_relation(relation) end relation.join(construct_join(options[:joins], scope)) @@ -1727,12 +1722,7 @@ module ActiveRecord relation = arel_table(options[:from]) for association in join_dependency.join_associations - if association.relation.is_a?(Array) - relation.join(association.relation.first, association.join_type).on(association.association_join.first) - relation.join(association.relation.last, association.join_type).on(association.association_join.last) - else - relation.join(association.relation, association.join_type).on(association.association_join) - end + relation = association.join_relation(relation) end relation.join(construct_join(options[:joins], scope)) @@ -2230,6 +2220,16 @@ module ActiveRecord Arel::OuterJoin end + def join_relation(joining_relation, join = nil) + if relation.is_a?(Array) + joining_relation.join(relation.first, join_type).on(association_join.first) + joining_relation.join(relation.last, join_type).on(association_join.last) + else + joining_relation.join(relation, join_type).on(association_join) + end + joining_relation + end + protected def aliased_table_name_for(name, suffix = nil) -- cgit v1.2.3 From 79e951ca9bc875da9e4a19adeff3634f0b5b7b76 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Tue, 18 Aug 2009 07:50:11 -0300 Subject: Use finder options as relation method names to provide more familiar naming. Use bang methods convention in methods that alter the relation. --- activerecord/lib/active_record/associations.rb | 34 +++++++++++++------------- activerecord/lib/active_record/base.rb | 31 ++++++++++++----------- activerecord/lib/active_record/calculations.rb | 18 +++++++------- activerecord/lib/active_record/relation.rb | 32 ++++++++++++++++++------ 4 files changed, 65 insertions(+), 50 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 406f08e247..d0322280d9 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1681,15 +1681,15 @@ module ActiveRecord for association in join_dependency.join_associations relation = association.join_relation(relation) end - relation.join(construct_join(options[:joins], scope)) - relation.where(construct_conditions(options[:conditions], scope)) - relation.where(construct_arel_limited_ids_condition(options, join_dependency)) if !using_limitable_reflections?(join_dependency.reflections) && ((scope && scope[:limit]) || options[:limit]) + relation.joins!(construct_join(options[:joins], scope)). + select!(column_aliases(join_dependency)). + group!(construct_group(options[:group], options[:having], scope)). + order!(construct_order(options[:order], scope)). + conditions!(construct_conditions(options[:conditions], scope)) - relation.project(column_aliases(join_dependency)) - relation.group(construct_group(options[:group], options[:having], scope)) - relation.order(construct_order(options[:order], scope)) - relation.take(construct_limit(options[:limit], scope)) if using_limitable_reflections?(join_dependency.reflections) + relation.conditions!(construct_arel_limited_ids_condition(options, join_dependency)) if !using_limitable_reflections?(join_dependency.reflections) && ((scope && scope[:limit]) || options[:limit]) + relation.limit!(construct_limit(options[:limit], scope)) if using_limitable_reflections?(join_dependency.reflections) relation end @@ -1724,15 +1724,15 @@ module ActiveRecord for association in join_dependency.join_associations relation = association.join_relation(relation) end - relation.join(construct_join(options[:joins], scope)) - relation.where(construct_conditions(options[:conditions], scope)) - relation.project(connection.distinct("#{connection.quote_table_name table_name}.#{primary_key}", construct_order(options[:order], scope(:find)).join(","))) + relation.joins!(construct_join(options[:joins], scope)). + conditions!(construct_conditions(options[:conditions], scope)). + group!(construct_group(options[:group], options[:having], scope)). + order!(construct_order(options[:order], scope)). + limit!(construct_limit(options[:limit], scope)). + offset!(construct_limit(options[:offset], scope)) - relation.group(construct_group(options[:group], options[:having], scope)) - relation.order(construct_order(options[:order], scope)) - relation.take(construct_limit(options[:limit], scope)) - relation.skip(construct_limit(options[:offset], scope)) + relation.select!(connection.distinct("#{connection.quote_table_name table_name}.#{primary_key}", construct_order(options[:order], scope(:find)).join(","))) sanitize_sql(relation.to_sql) end @@ -2222,10 +2222,10 @@ module ActiveRecord def join_relation(joining_relation, join = nil) if relation.is_a?(Array) - joining_relation.join(relation.first, join_type).on(association_join.first) - joining_relation.join(relation.last, join_type).on(association_join.last) + joining_relation.joins!(relation.first, join_type).on!(association_join.first) + joining_relation.joins!(relation.last, join_type).on!(association_join.last) else - joining_relation.join(relation, join_type).on(association_join) + joining_relation.joins!(relation, join_type).on!(association_join) end joining_relation end diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index a6832b47ef..2b79788b89 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -866,18 +866,18 @@ module ActiveRecord #:nodoc: def update_all(updates, conditions = nil, options = {}) scope = scope(:find) - relation = arel_table.relation + relation = arel_table if conditions = construct_conditions(conditions, scope) - relation = relation.where(Arel::SqlLiteral.new(conditions)) + relation.conditions!(Arel::SqlLiteral.new(conditions)) end if options.has_key?(:limit) || (scope && scope[:limit]) # Only take order from scope if limit is also provided by scope, this # is useful for updating a has_many association with a limit. - relation = relation.order(construct_order(options[:order], scope)).take(construct_limit(options[:limit], scope)) + relation.order!(construct_order(options[:order], scope)).limit!(construct_limit(options[:limit], scope)) else - relation = relation.order(construct_order(options[:order], nil)) + relation.order!(options[:order]) end relation.update(sanitize_sql_for_assignment(updates)) @@ -932,7 +932,7 @@ module ActiveRecord #:nodoc: # associations or call your before_* or +after_destroy+ callbacks, use the +destroy_all+ method instead. def delete_all(conditions = nil) if conditions - arel_table.where(Arel::SqlLiteral.new(construct_conditions(conditions, scope(:find)))).delete + arel_table.conditions!(Arel::SqlLiteral.new(construct_conditions(conditions, scope(:find)))).delete else arel_table.delete end @@ -1719,15 +1719,14 @@ module ActiveRecord #:nodoc: def construct_finder_arel(options = {}, scope = scope(:find)) # TODO add lock to Arel - arel_table(options[:from]). - join(construct_join(options[:joins], scope)). - where(construct_conditions(options[:conditions], scope)). - project(options[:select] || (scope && scope[:select]) || default_select(options[:joins] || (scope && scope[:joins]))). - group(construct_group(options[:group], options[:having], scope)). - order(construct_order(options[:order], scope)). - take(construct_limit(options[:limit], scope)). - skip(construct_offset(options[:offset], scope) - ) + relation = arel_table(options[:from]). + joins!(construct_join(options[:joins], scope)). + conditions!(construct_conditions(options[:conditions], scope)). + select!(options[:select] || (scope && scope[:select]) || default_select(options[:joins] || (scope && scope[:joins]))). + group!(construct_group(options[:group], options[:having], scope)). + order!(construct_order(options[:order], scope)). + limit!(construct_limit(options[:limit], scope)). + offset!(construct_offset(options[:offset], scope)) end def construct_finder_sql(options, scope = scope(:find)) @@ -2570,7 +2569,7 @@ module ActiveRecord #:nodoc: # be made (since they can't be persisted). def destroy unless new_record? - arel_table(true).where(arel_table[self.class.primary_key].eq(id)).delete + arel_table(true).conditions!(arel_table[self.class.primary_key].eq(id)).delete end @destroyed = true @@ -2865,7 +2864,7 @@ module ActiveRecord #:nodoc: def update(attribute_names = @attributes.keys) attributes_with_values = arel_attributes_values(false, false, attribute_names) return 0 if attributes_with_values.empty? - arel_table(true).where(arel_table[self.class.primary_key].eq(id)).update(attributes_with_values) + arel_table(true).conditions!(arel_table[self.class.primary_key].eq(id)).update(attributes_with_values) end # Creates a record with values matching those of the instance attributes diff --git a/activerecord/lib/active_record/calculations.rb b/activerecord/lib/active_record/calculations.rb index 82a171c6ad..cd2fbe9b79 100644 --- a/activerecord/lib/active_record/calculations.rb +++ b/activerecord/lib/active_record/calculations.rb @@ -146,12 +146,12 @@ module ActiveRecord join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(self, merged_includes, construct_join(options[:joins], scope)) construct_finder_arel_with_included_associations(options, join_dependency) else - arel_table(options[:from]). - join(construct_join(options[:joins], scope)). - where(construct_conditions(options[:conditions], scope)). - order(options[:order]). - take(options[:limit]). - skip(options[:offset]) + relation = arel_table(options[:from]). + joins!(construct_join(options[:joins], scope)). + conditions!(construct_conditions(options[:conditions], scope)). + order!(options[:order]). + limit!(options[:limit]). + offset!(options[:offset]) end if options[:group] return execute_grouped_calculation(operation, column_name, options, relation) @@ -171,7 +171,7 @@ module ActiveRecord (column_name == :all ? "*" : column_name.to_s)) end - relation.project(operation == 'count' ? column.count(options[:distinct]) : column.send(operation)) + relation.select!(operation == 'count' ? column.count(options[:distinct]) : column.send(operation)) type_cast_calculated_value(connection.select_value(relation.to_sql), column_for(column_name), operation) end @@ -194,8 +194,8 @@ module ActiveRecord options[:select] << ", #{group_field} AS #{group_alias}" - relation.project(options[:select]) - relation.group(construct_group(options[:group], options[:having], nil)) + relation.select!(options[:select]) + relation.group!(construct_group(options[:group], options[:having], nil)) calculated_data = connection.select_all(relation.to_sql) diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 456de73250..bbbb1da210 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -1,7 +1,7 @@ module ActiveRecord class Relation delegate :delete, :to_sql, :to => :relation - CLAUSES_METHODS = ["project", "group", "order", "take", "skip", "on"].freeze + CLAUSES_METHODS = ["project", "where", "group", "order", "take", "skip", "on"].freeze attr_reader :relation, :klass def initialize(klass, table = nil) @@ -24,25 +24,41 @@ module ActiveRecord for clause in CLAUSES_METHODS class_eval %{ - def #{clause}(_#{clause}) + def #{clause}!(_#{clause}) @relation = @relation.#{clause}(_#{clause}) if _#{clause} self end } end - def join(joins, join_type = nil) - if !joins.blank? - if [String, Hash, Array, Symbol].include?(joins.class) - @relation = @relation.join(@klass.send(:construct_join, joins, nil)) + + def select!(selection) + @relation = @relation.project(selection) if selection + self + end + + def limit!(limit) + @relation = @relation.take(limit) if limit + self + end + + def offset!(offset) + @relation = @relation.skip(offset) if offset + self + end + + def joins!(join, join_type = nil) + if !join.blank? + if [String, Hash, Array, Symbol].include?(join.class) + @relation = @relation.join(@klass.send(:construct_join, join, nil)) else - @relation = @relation.join(joins, join_type) + @relation = @relation.join(join, join_type) end end self end - def where(conditions) + def conditions!(conditions) if !conditions.blank? conditions = @klass.send(:merge_conditions, conditions) if [String, Hash, Array].include?(conditions.class) @relation = @relation.where(conditions) -- cgit v1.2.3 From fa8f5c2667fa82feab66aa102993ab4f123d42bc Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Tue, 18 Aug 2009 08:18:24 -0300 Subject: Relation#joins! should know what to join instead of delegating to construct_join. --- activerecord/lib/active_record/relation.rb | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index bbbb1da210..0d43c53d10 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -1,7 +1,7 @@ module ActiveRecord class Relation delegate :delete, :to_sql, :to => :relation - CLAUSES_METHODS = ["project", "where", "group", "order", "take", "skip", "on"].freeze + CLAUSES_METHODS = ["group", "order", "on"].freeze attr_reader :relation, :klass def initialize(klass, table = nil) @@ -31,7 +31,6 @@ module ActiveRecord } end - def select!(selection) @relation = @relation.project(selection) if selection self @@ -47,12 +46,19 @@ module ActiveRecord self end - def joins!(join, join_type = nil) - if !join.blank? - if [String, Hash, Array, Symbol].include?(join.class) - @relation = @relation.join(@klass.send(:construct_join, join, nil)) + def joins!(joins, join_type = nil) + if !joins.blank? + @relation = case joins + when String + @relation.join(joins) + when Hash, Array, Symbol + if @klass.send(:array_of_strings?, joins) + @relation.join(joins.join(' ')) + else + @relation.join(@klass.send(:build_association_joins, joins)) + end else - @relation = @relation.join(join, join_type) + @relation.join(joins, join_type) end end self -- cgit v1.2.3 From ac03bc91dbee36233e061eb624f7781a49ab6fcf Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Tue, 18 Aug 2009 08:27:37 -0300 Subject: Use explicit method definition instead of metaprogramming. --- activerecord/lib/active_record/relation.rb | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 0d43c53d10..9c7ba881fc 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -1,7 +1,6 @@ module ActiveRecord class Relation - delegate :delete, :to_sql, :to => :relation - CLAUSES_METHODS = ["group", "order", "on"].freeze + delegate :to_sql, :to => :relation attr_reader :relation, :klass def initialize(klass, table = nil) @@ -22,20 +21,26 @@ module ActiveRecord to_a.first end - for clause in CLAUSES_METHODS - class_eval %{ - def #{clause}!(_#{clause}) - @relation = @relation.#{clause}(_#{clause}) if _#{clause} - self - end - } - end - def select!(selection) @relation = @relation.project(selection) if selection self end + def on!(on) + @relation = @relation.on(on) if on + self + end + + def order!(order) + @relation = @relation.order(order) if order + self + end + + def group!(group) + @relation = @relation.group(group) if group + self + end + def limit!(limit) @relation = @relation.take(limit) if limit self -- cgit v1.2.3 From 66fbcc1de646129a3fc7415aa2f8416ef78d8cbb Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Tue, 18 Aug 2009 14:10:03 -0300 Subject: Use immutable relation objects to generate queries. --- activerecord/lib/active_record/associations.rb | 37 +++++++------- activerecord/lib/active_record/base.rb | 34 ++++++------ activerecord/lib/active_record/calculations.rb | 15 +++--- activerecord/lib/active_record/relation.rb | 71 ++++++++++++-------------- 4 files changed, 76 insertions(+), 81 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index d0322280d9..c0741bb572 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1682,14 +1682,14 @@ module ActiveRecord relation = association.join_relation(relation) end - relation.joins!(construct_join(options[:joins], scope)). - select!(column_aliases(join_dependency)). - group!(construct_group(options[:group], options[:having], scope)). - order!(construct_order(options[:order], scope)). - conditions!(construct_conditions(options[:conditions], scope)) + relation = relation.joins(construct_join(options[:joins], scope)). + select(column_aliases(join_dependency)). + group(construct_group(options[:group], options[:having], scope)). + order(construct_order(options[:order], scope)). + conditions(construct_conditions(options[:conditions], scope)) - relation.conditions!(construct_arel_limited_ids_condition(options, join_dependency)) if !using_limitable_reflections?(join_dependency.reflections) && ((scope && scope[:limit]) || options[:limit]) - relation.limit!(construct_limit(options[:limit], scope)) if using_limitable_reflections?(join_dependency.reflections) + relation = relation.conditions(construct_arel_limited_ids_condition(options, join_dependency)) if !using_limitable_reflections?(join_dependency.reflections) && ((scope && scope[:limit]) || options[:limit]) + relation = relation.limit(construct_limit(options[:limit], scope)) if using_limitable_reflections?(join_dependency.reflections) relation end @@ -1725,14 +1725,13 @@ module ActiveRecord relation = association.join_relation(relation) end - relation.joins!(construct_join(options[:joins], scope)). - conditions!(construct_conditions(options[:conditions], scope)). - group!(construct_group(options[:group], options[:having], scope)). - order!(construct_order(options[:order], scope)). - limit!(construct_limit(options[:limit], scope)). - offset!(construct_limit(options[:offset], scope)) - - relation.select!(connection.distinct("#{connection.quote_table_name table_name}.#{primary_key}", construct_order(options[:order], scope(:find)).join(","))) + relation = relation.joins(construct_join(options[:joins], scope)). + conditions(construct_conditions(options[:conditions], scope)). + group(construct_group(options[:group], options[:having], scope)). + order(construct_order(options[:order], scope)). + limit(construct_limit(options[:limit], scope)). + offset(construct_limit(options[:offset], scope)). + select(connection.distinct("#{connection.quote_table_name table_name}.#{primary_key}", construct_order(options[:order], scope(:find)).join(","))) sanitize_sql(relation.to_sql) end @@ -2222,12 +2221,12 @@ module ActiveRecord def join_relation(joining_relation, join = nil) if relation.is_a?(Array) - joining_relation.joins!(relation.first, join_type).on!(association_join.first) - joining_relation.joins!(relation.last, join_type).on!(association_join.last) + joining_relation. + joins(relation.first, join_type).on(association_join.first). + joins(relation.last, join_type).on(association_join.last) else - joining_relation.joins!(relation, join_type).on!(association_join) + joining_relation.joins(relation, join_type).on(association_join) end - joining_relation end protected diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 2b79788b89..b23ce53d43 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -869,15 +869,15 @@ module ActiveRecord #:nodoc: relation = arel_table if conditions = construct_conditions(conditions, scope) - relation.conditions!(Arel::SqlLiteral.new(conditions)) + relation = relation.conditions(Arel::SqlLiteral.new(conditions)) end - if options.has_key?(:limit) || (scope && scope[:limit]) + relation = if options.has_key?(:limit) || (scope && scope[:limit]) # Only take order from scope if limit is also provided by scope, this # is useful for updating a has_many association with a limit. - relation.order!(construct_order(options[:order], scope)).limit!(construct_limit(options[:limit], scope)) + relation.order(construct_order(options[:order], scope)).limit(construct_limit(options[:limit], scope)) else - relation.order!(options[:order]) + relation.order(options[:order]) end relation.update(sanitize_sql_for_assignment(updates)) @@ -932,7 +932,7 @@ module ActiveRecord #:nodoc: # associations or call your before_* or +after_destroy+ callbacks, use the +destroy_all+ method instead. def delete_all(conditions = nil) if conditions - arel_table.conditions!(Arel::SqlLiteral.new(construct_conditions(conditions, scope(:find)))).delete + arel_table.conditions(Arel::SqlLiteral.new(construct_conditions(conditions, scope(:find)))).delete else arel_table.delete end @@ -1535,7 +1535,7 @@ module ActiveRecord #:nodoc: def arel_table(table = nil) table = table_name if table.blank? - Relation.new(self, table) + Relation.new(self, Arel::Table.new(table)) end private @@ -1720,13 +1720,13 @@ module ActiveRecord #:nodoc: def construct_finder_arel(options = {}, scope = scope(:find)) # TODO add lock to Arel relation = arel_table(options[:from]). - joins!(construct_join(options[:joins], scope)). - conditions!(construct_conditions(options[:conditions], scope)). - select!(options[:select] || (scope && scope[:select]) || default_select(options[:joins] || (scope && scope[:joins]))). - group!(construct_group(options[:group], options[:having], scope)). - order!(construct_order(options[:order], scope)). - limit!(construct_limit(options[:limit], scope)). - offset!(construct_offset(options[:offset], scope)) + joins(construct_join(options[:joins], scope)). + conditions(construct_conditions(options[:conditions], scope)). + select(options[:select] || (scope && scope[:select]) || default_select(options[:joins] || (scope && scope[:joins]))). + group(construct_group(options[:group], options[:having], scope)). + order(construct_order(options[:order], scope)). + limit(construct_limit(options[:limit], scope)). + offset(construct_offset(options[:offset], scope)) end def construct_finder_sql(options, scope = scope(:find)) @@ -1810,7 +1810,7 @@ module ActiveRecord #:nodoc: def build_association_joins(joins) join_dependency = ActiveRecord::Associations::ClassMethods::InnerJoinDependency.new(self, joins, nil) - relation = arel_table + relation = arel_table.relation join_dependency.join_associations.map { |association| if association.relation.is_a?(Array) [Arel::InnerJoin.new(relation, association.relation.first, association.association_join.first).joins(relation), @@ -2569,7 +2569,7 @@ module ActiveRecord #:nodoc: # be made (since they can't be persisted). def destroy unless new_record? - arel_table(true).conditions!(arel_table[self.class.primary_key].eq(id)).delete + arel_table(true).conditions(arel_table[self.class.primary_key].eq(id)).delete end @destroyed = true @@ -2864,7 +2864,7 @@ module ActiveRecord #:nodoc: def update(attribute_names = @attributes.keys) attributes_with_values = arel_attributes_values(false, false, attribute_names) return 0 if attributes_with_values.empty? - arel_table(true).conditions!(arel_table[self.class.primary_key].eq(id)).update(attributes_with_values) + arel_table(true).conditions(arel_table[self.class.primary_key].eq(id)).update(attributes_with_values) end # Creates a record with values matching those of the instance attributes @@ -2952,7 +2952,7 @@ module ActiveRecord #:nodoc: end def arel_table(reload = nil) - @arel_table = Relation.new(self.class, self.class.table_name) if reload || @arel_table.nil? + @arel_table = Relation.new(self.class, Arel::Table.new(self.class.table_name)) if reload || @arel_table.nil? @arel_table end diff --git a/activerecord/lib/active_record/calculations.rb b/activerecord/lib/active_record/calculations.rb index cd2fbe9b79..40242333e5 100644 --- a/activerecord/lib/active_record/calculations.rb +++ b/activerecord/lib/active_record/calculations.rb @@ -147,11 +147,11 @@ module ActiveRecord construct_finder_arel_with_included_associations(options, join_dependency) else relation = arel_table(options[:from]). - joins!(construct_join(options[:joins], scope)). - conditions!(construct_conditions(options[:conditions], scope)). - order!(options[:order]). - limit!(options[:limit]). - offset!(options[:offset]) + joins(construct_join(options[:joins], scope)). + conditions(construct_conditions(options[:conditions], scope)). + order(options[:order]). + limit(options[:limit]). + offset(options[:offset]) end if options[:group] return execute_grouped_calculation(operation, column_name, options, relation) @@ -171,7 +171,7 @@ module ActiveRecord (column_name == :all ? "*" : column_name.to_s)) end - relation.select!(operation == 'count' ? column.count(options[:distinct]) : column.send(operation)) + relation = relation.select(operation == 'count' ? column.count(options[:distinct]) : column.send(operation)) type_cast_calculated_value(connection.select_value(relation.to_sql), column_for(column_name), operation) end @@ -194,8 +194,7 @@ module ActiveRecord options[:select] << ", #{group_field} AS #{group_alias}" - relation.select!(options[:select]) - relation.group!(construct_group(options[:group], options[:having], nil)) + relation = relation.select(options[:select]).group(construct_group(options[:group], options[:having], nil)) calculated_data = connection.select_all(relation.to_sql) diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 9c7ba881fc..bba0d0844d 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -3,9 +3,9 @@ module ActiveRecord delegate :to_sql, :to => :relation attr_reader :relation, :klass - def initialize(klass, table = nil) + def initialize(klass, relation) @klass = klass - @relation = Arel::Table.new(table || @klass.table_name) + @relation = relation end def to_a @@ -21,60 +21,57 @@ module ActiveRecord to_a.first end - def select!(selection) - @relation = @relation.project(selection) if selection - self + def select(selects) + Relation.new(@klass, @relation.project(selects)) end - def on!(on) - @relation = @relation.on(on) if on - self + def group(groups) + Relation.new(@klass, @relation.group(groups)) end - def order!(order) - @relation = @relation.order(order) if order - self + def order(orders) + Relation.new(@klass, @relation.order(orders)) end - def group!(group) - @relation = @relation.group(group) if group - self + def limit(limits) + Relation.new(@klass, @relation.take(limits)) end - def limit!(limit) - @relation = @relation.take(limit) if limit - self + def offset(offsets) + Relation.new(@klass, @relation.skip(offsets)) end - def offset!(offset) - @relation = @relation.skip(offset) if offset - self + def on(join) + Relation.new(@klass, @relation.on(join)) end - def joins!(joins, join_type = nil) - if !joins.blank? - @relation = case joins - when String - @relation.join(joins) - when Hash, Array, Symbol - if @klass.send(:array_of_strings?, joins) - @relation.join(joins.join(' ')) + def joins(join, join_type = nil) + if join.blank? + self + else + join = case join + when String + @relation.join(join) + when Hash, Array, Symbol + if @klass.send(:array_of_strings?, join) + @relation.join(join.join(' ')) + else + @relation.join(@klass.send(:build_association_joins, join)) + end else - @relation.join(@klass.send(:build_association_joins, joins)) - end - else - @relation.join(joins, join_type) + @relation.join(join, join_type) end + Relation.new(@klass, join) end - self end - def conditions!(conditions) - if !conditions.blank? + def conditions(conditions) + if conditions.blank? + self + else conditions = @klass.send(:merge_conditions, conditions) if [String, Hash, Array].include?(conditions.class) - @relation = @relation.where(conditions) + Relation.new(@klass, @relation.where(conditions)) end - self end private -- cgit v1.2.3 From 60926db9e06a7890409926211dffe773aff4a57d Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Tue, 18 Aug 2009 14:22:38 -0300 Subject: Inline initializer setup. --- activerecord/lib/active_record/relation.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index bba0d0844d..84cd739781 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -4,8 +4,7 @@ module ActiveRecord attr_reader :relation, :klass def initialize(klass, relation) - @klass = klass - @relation = relation + @klass, @relation = klass, relation end def to_a -- cgit v1.2.3 From 0d6997b6e3f25d87b08b4aacaa2140609d5cc19c Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Tue, 18 Aug 2009 14:49:11 -0300 Subject: Cache #arel_able when possible. --- activerecord/lib/active_record/base.rb | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index b23ce53d43..3bbe23865f 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1533,9 +1533,12 @@ module ActiveRecord #:nodoc: end - def arel_table(table = nil) + def arel_table(table = nil, reload = nil) table = table_name if table.blank? - Relation.new(self, Arel::Table.new(table)) + if reload || @arel_table.nil? || @arel_table.name != table + @arel_table = Relation.new(self, Arel::Table.new(table)) + end + @arel_table end private -- cgit v1.2.3 From fefb4c78ac8f37ea0b14cbb0c008f305a1bbd36f Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Tue, 18 Aug 2009 16:28:04 -0300 Subject: Cache arel_table when possible, use class method arel_table instead instance method. --- activerecord/lib/active_record/base.rb | 17 +++++++---------- activerecord/lib/active_record/locking/optimistic.rb | 8 ++++++-- 2 files changed, 13 insertions(+), 12 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 3bbe23865f..c74d8c6190 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -2572,7 +2572,7 @@ module ActiveRecord #:nodoc: # be made (since they can't be persisted). def destroy unless new_record? - arel_table(true).conditions(arel_table[self.class.primary_key].eq(id)).delete + self.class.arel_table(self.class.table_name, true).conditions(self.class.arel_table[self.class.primary_key].eq(id)).delete end @destroyed = true @@ -2867,7 +2867,7 @@ module ActiveRecord #:nodoc: def update(attribute_names = @attributes.keys) attributes_with_values = arel_attributes_values(false, false, attribute_names) return 0 if attributes_with_values.empty? - arel_table(true).conditions(arel_table[self.class.primary_key].eq(id)).update(attributes_with_values) + self.class.arel_table(self.class.table_name, true).conditions(self.class.arel_table[self.class.primary_key].eq(id)).update(attributes_with_values) end # Creates a record with values matching those of the instance attributes @@ -2877,12 +2877,14 @@ module ActiveRecord #:nodoc: self.id = connection.next_sequence_value(self.class.sequence_name) end + # Reload ARel relation cached table + self.class.arel_table(self.class.table_name, true) attributes_values = arel_attributes_values new_id = if attributes_values.empty? - arel_table.insert connection.empty_insert_statement_value + self.class.arel_table.insert connection.empty_insert_statement_value else - arel_table.insert attributes_values + self.class.arel_table.insert attributes_values end self.id ||= new_id @@ -2954,11 +2956,6 @@ module ActiveRecord #:nodoc: default end - def arel_table(reload = nil) - @arel_table = Relation.new(self.class, Arel::Table.new(self.class.table_name)) if reload || @arel_table.nil? - @arel_table - end - # Returns a copy of the attributes hash where all the values have been safely quoted for use in # an SQL statement. def attributes_with_quotes(include_primary_key = true, include_readonly_attributes = true, attribute_names = @attributes.keys) @@ -2992,7 +2989,7 @@ module ActiveRecord #:nodoc: if value && ((self.class.serialized_attributes.has_key?(name) && (value.acts_like?(:date) || value.acts_like?(:time))) || value.is_a?(Hash) || value.is_a?(Array)) value = value.to_yaml end - attrs[arel_table[name]] = value + attrs[self.class.arel_table[name]] = value end end end diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb index 4e833ec871..c8cd79a2b0 100644 --- a/activerecord/lib/active_record/locking/optimistic.rb +++ b/activerecord/lib/active_record/locking/optimistic.rb @@ -89,7 +89,9 @@ module ActiveRecord attribute_names.uniq! begin - affected_rows = arel_table(true).where( + arel_table = self.class.arel_table(self.class.table_name) + + affected_rows = arel_table.where( arel_table[self.class.primary_key].eq(quoted_id).and( arel_table[self.class.locking_column].eq(quote_value(previous_value)) ) @@ -116,7 +118,9 @@ module ActiveRecord lock_col = self.class.locking_column previous_value = send(lock_col).to_i - affected_rows = arel_table(true).where( + arel_table = self.class.arel_table(self.class.table_name) + + affected_rows = arel_table.where( arel_table[self.class.primary_key].eq(quoted_id).and( arel_table[self.class.locking_column].eq(quote_value(previous_value)) ) -- cgit v1.2.3 From 74ed123e082a9b2b160ddecc2eb141f1678c600a Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Tue, 18 Aug 2009 16:35:33 -0300 Subject: Override respond_to? in ActiveRecord::Relation to go with method_missing. --- activerecord/lib/active_record/relation.rb | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 84cd739781..b69a12bc7b 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -73,6 +73,14 @@ module ActiveRecord end end + def respond_to?(method) + if @relation.respond_to?(method) || Array.instance_methods.include?(method.to_s) + true + else + super + end + end + private def method_missing(method, *args, &block) if @relation.respond_to?(method) -- cgit v1.2.3 From 0abba2813be52cc8f8ff2544f8a3b18b5083be53 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Tue, 18 Aug 2009 19:38:03 -0300 Subject: Call to_sql method on ActiveRecord::Relation instance not it's relation attribute.' --- activerecord/lib/active_record/base.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index c74d8c6190..4636a8b338 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1733,7 +1733,7 @@ module ActiveRecord #:nodoc: end def construct_finder_sql(options, scope = scope(:find)) - construct_finder_arel(options, scope).relation.to_sql + construct_finder_arel(options, scope).to_sql end def construct_join(joins, scope) -- cgit v1.2.3 From c1f833dff47eb5c0a74eb80f011597c7130bc8d7 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Tue, 18 Aug 2009 19:38:34 -0300 Subject: habtm delete method integrated with ARel. --- .../associations/has_and_belongs_to_many_association.rb | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'activerecord/lib/active_record') 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 d91c555dad..a1aac3ead5 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 @@ -34,7 +34,7 @@ module ActiveRecord options[:readonly] = finding_with_ambiguous_select?(options[:select] || @reflection.options[:select]) options[:select] ||= (@reflection.options[:select] || '*') end - + def count_records load_target.size end @@ -85,9 +85,10 @@ module ActiveRecord if sql = @reflection.options[:delete_sql] records.each { |record| @owner.connection.delete(interpolate_sql(sql, record)) } else - ids = quoted_record_ids(records) - sql = "DELETE FROM #{@owner.connection.quote_table_name @reflection.options[:join_table]} WHERE #{@reflection.primary_key_name} = #{owner_quoted_id} AND #{@reflection.association_foreign_key} IN (#{ids})" - @owner.connection.delete(sql) + relation = arel_table(@reflection.options[:join_table]) + relation.conditions(relation[@reflection.primary_key_name].eq(@owner.id). + and(Arel::In.new(relation[@reflection.association_foreign_key], records.map(&:id))) + ).delete end end -- cgit v1.2.3 From d5476b466f086921c71cb1caf6b9773cbdb5b4e3 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Tue, 18 Aug 2009 19:51:41 -0300 Subject: habtm insertion with ARel integration. --- .../associations/has_and_belongs_to_many_association.rb | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) (limited to 'activerecord/lib/active_record') 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 a1aac3ead5..da1fbfe0b7 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 @@ -56,26 +56,23 @@ module ActiveRecord if @reflection.options[:insert_sql] @owner.connection.insert(interpolate_sql(@reflection.options[:insert_sql], record)) else + relation = arel_table(@reflection.options[:join_table]) attributes = columns.inject({}) do |attrs, column| case column.name.to_s when @reflection.primary_key_name.to_s - attrs[column.name] = owner_quoted_id + attrs[relation[column.name]] = owner_quoted_id when @reflection.association_foreign_key.to_s - attrs[column.name] = record.quoted_id + attrs[relation[column.name]] = record.quoted_id else if record.has_attribute?(column.name) value = @owner.send(:quote_value, record[column.name], column) - attrs[column.name] = value unless value.nil? + attrs[relation[column.name]] = value unless value.nil? end end attrs end - sql = - "INSERT INTO #{@owner.connection.quote_table_name @reflection.options[:join_table]} (#{@owner.send(:quoted_column_names, attributes).join(', ')}) " + - "VALUES (#{attributes.values.join(', ')})" - - @owner.connection.insert(sql) + relation.insert(attributes) end return true -- cgit v1.2.3 From a7178773d2f57454b55b20577091c00327565b67 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Tue, 18 Aug 2009 19:53:21 -0300 Subject: Remove now unused quote_table_name, ARel does that quoting now. --- activerecord/lib/active_record/base.rb | 7 ------- 1 file changed, 7 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 4636a8b338..1698f33da0 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -3103,13 +3103,6 @@ module ActiveRecord #:nodoc: hash.inject([]) { |list, pair| list << "#{pair.first} = #{pair.last}" }.join(", ") end - def quoted_column_names(attributes = attributes_with_quotes) - connection = self.class.connection - attributes.keys.collect do |column_name| - connection.quote_column_name(column_name) - end - end - def self.quoted_table_name self.connection.quote_table_name(self.table_name) end -- cgit v1.2.3 From a09215ee968250963256a39936a4136aeb1b65f6 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Tue, 18 Aug 2009 20:02:14 -0300 Subject: has_many nullify associations keys using ARel. --- .../active_record/associations/has_many_association.rb | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/associations/has_many_association.rb b/activerecord/lib/active_record/associations/has_many_association.rb index 73d3c23cd3..29ba84ee37 100644 --- a/activerecord/lib/active_record/associations/has_many_association.rb +++ b/activerecord/lib/active_record/associations/has_many_association.rb @@ -40,11 +40,11 @@ module ActiveRecord # we are certain the current target is an empty array. This is a # documented side-effect of the method that may avoid an extra SELECT. @target ||= [] and loaded if count == 0 - + if @reflection.options[:limit] count = [ @reflection.options[:limit], count ].min end - + return count end @@ -69,11 +69,11 @@ module ActiveRecord when :delete_all @reflection.klass.delete(records.map { |record| record.id }) else - ids = quoted_record_ids(records) - @reflection.klass.update_all( - "#{@reflection.primary_key_name} = NULL", - "#{@reflection.primary_key_name} = #{owner_quoted_id} AND #{@reflection.klass.primary_key} IN (#{ids})" - ) + relation = arel_table(@reflection.table_name) + relation.conditions(relation[@reflection.primary_key_name].eq(@owner.id). + and(Arel::In.new(relation[@reflection.klass.primary_key], records.map(&:id))) + ).update(relation[@reflection.primary_key_name] => nil) + @owner.class.update_counters(@owner.id, cached_counter_attribute_name => -records.size) if has_cached_counter? end end @@ -88,11 +88,11 @@ module ActiveRecord @finder_sql = interpolate_sql(@reflection.options[:finder_sql]) when @reflection.options[:as] - @finder_sql = + @finder_sql = "#{@reflection.quoted_table_name}.#{@reflection.options[:as]}_id = #{owner_quoted_id} AND " + "#{@reflection.quoted_table_name}.#{@reflection.options[:as]}_type = #{@owner.class.quote_value(@owner.class.base_class.name.to_s)}" @finder_sql << " AND (#{conditions})" if conditions - + else @finder_sql = "#{@reflection.quoted_table_name}.#{@reflection.primary_key_name} = #{owner_quoted_id}" @finder_sql << " AND (#{conditions})" if conditions -- cgit v1.2.3 From c36cfa7568cd51718d149107696e779d3282da45 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Tue, 18 Aug 2009 20:03:32 -0300 Subject: Remove unused quoted_record_ids, now ARel does this using ARel::In. --- activerecord/lib/active_record/associations/association_proxy.rb | 9 --------- 1 file changed, 9 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/associations/association_proxy.rb b/activerecord/lib/active_record/associations/association_proxy.rb index e36b04ea95..75218c01d2 100644 --- a/activerecord/lib/active_record/associations/association_proxy.rb +++ b/activerecord/lib/active_record/associations/association_proxy.rb @@ -156,15 +156,6 @@ module ActiveRecord @reflection.options[:dependent] end - # Returns a string with the IDs of +records+ joined with a comma, quoted - # if needed. The result is ready to be inserted into a SQL IN clause. - # - # quoted_record_ids(records) # => "23,56,58,67" - # - def quoted_record_ids(records) - records.map { |record| record.quoted_id }.join(',') - end - def interpolate_sql(sql, record = nil) @owner.send(:interpolate_sql, sql, record) end -- cgit v1.2.3 From 2048556a14c28f9814f9c6ad44a08084afec1afe Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Wed, 19 Aug 2009 11:09:12 -0300 Subject: Remove whitespace. --- activerecord/lib/active_record/associations.rb | 1 + activerecord/lib/active_record/associations/belongs_to_association.rb | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index c0741bb572..547df9c684 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -2085,6 +2085,7 @@ module ActiveRecord def association_join connection = reflection.active_record.connection + join = case reflection.macro when :has_and_belongs_to_many ["%s.%s = %s.%s " % [ diff --git a/activerecord/lib/active_record/associations/belongs_to_association.rb b/activerecord/lib/active_record/associations/belongs_to_association.rb index 628033c87a..d2f2267e5c 100644 --- a/activerecord/lib/active_record/associations/belongs_to_association.rb +++ b/activerecord/lib/active_record/associations/belongs_to_association.rb @@ -36,11 +36,11 @@ module ActiveRecord loaded record end - + def updated? @updated end - + private def find_target find_method = if @reflection.options[:primary_key] -- cgit v1.2.3 From 3e1ef198e086deb4f212523ce2338a027f0e4155 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Wed, 19 Aug 2009 16:08:54 -0300 Subject: Remove useless InnerJoinDependency, inner joins are performed through Arel::InnerJoin. --- activerecord/lib/active_record/associations.rb | 25 ++++--------------------- activerecord/lib/active_record/base.rb | 2 +- 2 files changed, 5 insertions(+), 22 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 547df9c684..4e31f943bc 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1787,7 +1787,7 @@ module ActiveRecord if array_of_strings?(merged_joins) tables_in_string(merged_joins.join(' ')) else - join_dependency = ActiveRecord::Associations::ClassMethods::InnerJoinDependency.new(self, merged_joins, nil) + join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(self, merged_joins, nil) join_dependency.join_associations.collect {|join_association| [join_association.aliased_join_table_name, join_association.aliased_table_name]}.flatten.compact end else @@ -2216,17 +2216,13 @@ module ActiveRecord end end - def join_type - Arel::OuterJoin - end - def join_relation(joining_relation, join = nil) if relation.is_a?(Array) joining_relation. - joins(relation.first, join_type).on(association_join.first). - joins(relation.last, join_type).on(association_join.last) + joins(relation.first, Arel::OuterJoin).on(association_join.first). + joins(relation.last, Arel::OuterJoin).on(association_join.last) else - joining_relation.joins(relation, join_type).on(association_join) + joining_relation.joins(relation, Arel::OuterJoin).on(association_join) end end @@ -2267,19 +2263,6 @@ module ActiveRecord end end end - - class InnerJoinDependency < JoinDependency # :nodoc: - protected - def build_join_association(reflection, parent) - InnerJoinAssociation.new(reflection, self, parent) - end - - class InnerJoinAssociation < JoinAssociation - def join_type - Arel::InnerJoin - end - end - end end end end diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 1698f33da0..dc439ff92e 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1812,7 +1812,7 @@ module ActiveRecord #:nodoc: end def build_association_joins(joins) - join_dependency = ActiveRecord::Associations::ClassMethods::InnerJoinDependency.new(self, joins, nil) + join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(self, joins, nil) relation = arel_table.relation join_dependency.join_associations.map { |association| if association.relation.is_a?(Array) -- cgit v1.2.3 From ccf5f2c4a20a9a275dd86f251fa026eee7f344c5 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Wed, 19 Aug 2009 16:17:16 -0300 Subject: Remove old method and comment. --- activerecord/lib/active_record/associations.rb | 14 -------------- 1 file changed, 14 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 4e31f943bc..4c07e6be07 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1912,19 +1912,6 @@ module ActiveRecord end end - def joins_for_table_name(table_name) - join = join_for_table_name(table_name) - result = nil - if join && join.is_a?(JoinAssociation) - result = [join] - if join.parent && join.parent.is_a?(JoinAssociation) - result = joins_for_table_name(join.parent.aliased_table_name) + - result - end - end - result - end - protected def build(associations, parent = nil) parent ||= @joins.last @@ -1948,7 +1935,6 @@ module ActiveRecord end end - # overridden in InnerJoinDependency subclass def build_join_association(reflection, parent) JoinAssociation.new(reflection, self, parent) end -- cgit v1.2.3 From b32474020c4ec2f2b9c78bd580466c494eac6eb3 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Wed, 19 Aug 2009 16:29:26 -0300 Subject: Don't sanitize_sql where it doesn't make sense. --- activerecord/lib/active_record/associations.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 4c07e6be07..1f1ae93bb6 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1695,7 +1695,7 @@ module ActiveRecord end def construct_finder_sql_with_included_associations(options, join_dependency) - sanitize_sql(construct_finder_arel_with_included_associations(options, join_dependency).to_sql) + construct_finder_arel_with_included_associations(options, join_dependency).to_sql end def construct_arel_limited_ids_condition(options, join_dependency) @@ -1733,7 +1733,7 @@ module ActiveRecord offset(construct_limit(options[:offset], scope)). select(connection.distinct("#{connection.quote_table_name table_name}.#{primary_key}", construct_order(options[:order], scope(:find)).join(","))) - sanitize_sql(relation.to_sql) + relation.to_sql end def tables_in_string(string) -- cgit v1.2.3 From 54fcbb881d1d707d65d38cd30f50049023448832 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Wed, 19 Aug 2009 17:45:13 -0300 Subject: Make sure join association methods are called once. --- activerecord/lib/active_record/associations.rb | 19 ++++++++++--------- activerecord/lib/active_record/base.rb | 8 ++++---- 2 files changed, 14 insertions(+), 13 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 1f1ae93bb6..068943c454 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -2059,6 +2059,7 @@ module ActiveRecord @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 if reflection.macro == :has_and_belongs_to_many @aliased_join_table_name = aliased_table_name_for(reflection.options[:join_table], "_join") @@ -2070,9 +2071,9 @@ module ActiveRecord end def association_join + return @join if @join connection = reflection.active_record.connection - - join = case reflection.macro + @join = case reflection.macro when :has_and_belongs_to_many ["%s.%s = %s.%s " % [ connection.quote_table_name(aliased_join_table_name), @@ -2182,14 +2183,14 @@ module ActiveRecord else "" end - join << %(AND %s) % [ + @join << %(AND %s) % [ klass.send(:type_condition, aliased_table_name)] unless klass.descends_from_active_record? [through_reflection, reflection].each do |ref| - join << "AND #{interpolate_sql(sanitize_sql(ref.options[:conditions], aliased_table_name))} " if ref && ref.options[:conditions] + @join << "AND #{interpolate_sql(sanitize_sql(ref.options[:conditions], aliased_table_name))} " if ref && ref.options[:conditions] end - join + @join end def relation @@ -2203,12 +2204,12 @@ module ActiveRecord end def join_relation(joining_relation, join = nil) - if relation.is_a?(Array) + if (relations = relation).is_a?(Array) joining_relation. - joins(relation.first, Arel::OuterJoin).on(association_join.first). - joins(relation.last, Arel::OuterJoin).on(association_join.last) + joins(relations.first, Arel::OuterJoin).on(association_join.first). + joins(relations.last, Arel::OuterJoin).on(association_join.last) else - joining_relation.joins(relation, Arel::OuterJoin).on(association_join) + joining_relation.joins(relations, Arel::OuterJoin).on(association_join) end end diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index dc439ff92e..b0286f7409 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1815,11 +1815,11 @@ module ActiveRecord #:nodoc: join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(self, joins, nil) relation = arel_table.relation join_dependency.join_associations.map { |association| - if association.relation.is_a?(Array) - [Arel::InnerJoin.new(relation, association.relation.first, association.association_join.first).joins(relation), - Arel::InnerJoin.new(relation, association.relation.last, association.association_join.last).joins(relation)].join() + if (association_relation = association.relation).is_a?(Array) + [Arel::InnerJoin.new(relation, association_relation.first, association.association_join.first).joins(relation), + Arel::InnerJoin.new(relation, association_relation.last, association.association_join.last).joins(relation)].join() else - Arel::InnerJoin.new(relation, association.relation, association.association_join).joins(relation) + Arel::InnerJoin.new(relation, association_relation, association.association_join).joins(relation) end }.join(" ") end -- cgit v1.2.3 From a60334fdc5d4f612bd2dd70e38d1e57481cd5910 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Wed, 19 Aug 2009 19:36:06 -0300 Subject: Remove useless conditionals/local var. --- activerecord/lib/active_record/associations.rb | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 068943c454..b363cceccb 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -2089,8 +2089,6 @@ module ActiveRecord ] when :has_many, :has_one if reflection.options[:through] - through_conditions = through_reflection.options[:conditions] ? "AND #{interpolate_sql(sanitize_sql(through_reflection.options[:conditions]))}" : '' - jt_foreign_key = jt_as_extra = jt_source_extra = jt_sti_extra = nil first_key = second_key = as_extra = nil @@ -2154,7 +2152,7 @@ module ActiveRecord as_extra] ] - elsif reflection.options[:as] && [:has_many, :has_one].include?(reflection.macro) + elsif reflection.options[:as] "%s.%s = %s.%s AND %s.%s = %s" % [ connection.quote_table_name(aliased_table_name), "#{reflection.options[:as]}_id", @@ -2180,8 +2178,6 @@ module ActiveRecord connection.quote_table_name(parent.aliased_table_name), options[:foreign_key] || reflection.primary_key_name ] - else - "" end @join << %(AND %s) % [ klass.send(:type_condition, aliased_table_name)] unless klass.descends_from_active_record? -- cgit v1.2.3 From 33746c44cb3ace58507d5edc9833088524821d22 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Tue, 25 Aug 2009 19:47:29 -0300 Subject: No need to create a new relation if the argument is blank. --- activerecord/lib/active_record/relation.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index b69a12bc7b..570ba3f80d 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -21,27 +21,27 @@ module ActiveRecord end def select(selects) - Relation.new(@klass, @relation.project(selects)) + selects.blank? ? self : Relation.new(@klass, @relation.project(selects)) end def group(groups) - Relation.new(@klass, @relation.group(groups)) + groups.blank? ? self : Relation.new(@klass, @relation.group(groups)) end def order(orders) - Relation.new(@klass, @relation.order(orders)) + orders.blank? ? self : Relation.new(@klass, @relation.order(orders)) end def limit(limits) - Relation.new(@klass, @relation.take(limits)) + limits.blank? ? self : Relation.new(@klass, @relation.take(limits)) end def offset(offsets) - Relation.new(@klass, @relation.skip(offsets)) + offsets.blank? ? self : Relation.new(@klass, @relation.skip(offsets)) end def on(join) - Relation.new(@klass, @relation.on(join)) + join.blank? ? self : Relation.new(@klass, @relation.on(join)) end def joins(join, join_type = nil) -- cgit v1.2.3 From 286f47f3b034db4550110b9a0f9ff48dda29e807 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Wed, 26 Aug 2009 16:19:19 -0300 Subject: Remove useless var local definition. --- activerecord/lib/active_record/base.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index b0286f7409..8217f5e8e9 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1722,7 +1722,7 @@ module ActiveRecord #:nodoc: def construct_finder_arel(options = {}, scope = scope(:find)) # TODO add lock to Arel - relation = arel_table(options[:from]). + arel_table(options[:from]). joins(construct_join(options[:joins], scope)). conditions(construct_conditions(options[:conditions], scope)). select(options[:select] || (scope && scope[:select]) || default_select(options[:joins] || (scope && scope[:joins]))). -- cgit v1.2.3 From 1cc0ea826e92169575b486e662dff1e1d44db992 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Thu, 27 Aug 2009 16:30:48 -0300 Subject: Performance: Don't reload the arel_table when destroying. --- activerecord/lib/active_record/base.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 8217f5e8e9..0307aa29d3 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -2572,7 +2572,7 @@ module ActiveRecord #:nodoc: # be made (since they can't be persisted). def destroy unless new_record? - self.class.arel_table(self.class.table_name, true).conditions(self.class.arel_table[self.class.primary_key].eq(id)).delete + self.class.arel_table(self.class.table_name).conditions(self.class.arel_table[self.class.primary_key].eq(id)).delete end @destroyed = true -- cgit v1.2.3 From e204f80dbb2134867b006d02c4d5d15c265351b8 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Thu, 27 Aug 2009 16:42:57 -0300 Subject: Performance: Don't reload the ARel relation on create, do it on reset_column_information. --- activerecord/lib/active_record/base.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 0307aa29d3..4e2261d044 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1365,6 +1365,8 @@ module ActiveRecord #:nodoc: # end def reset_column_information undefine_attribute_methods + # Reload ARel relation cached table + arel_table(table_name, true) @column_names = @columns = @columns_hash = @content_columns = @dynamic_methods_hash = @inheritance_column = nil end @@ -2877,8 +2879,6 @@ module ActiveRecord #:nodoc: self.id = connection.next_sequence_value(self.class.sequence_name) end - # Reload ARel relation cached table - self.class.arel_table(self.class.table_name, true) attributes_values = arel_attributes_values new_id = if attributes_values.empty? -- cgit v1.2.3 From 53f6c6d24e6fb25a62144c6ef5020cc04a893f56 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Thu, 27 Aug 2009 16:46:24 -0300 Subject: Performance: Don't reload ARel relation on update. --- activerecord/lib/active_record/base.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 4e2261d044..2fc7b1e9c6 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -2869,7 +2869,7 @@ module ActiveRecord #:nodoc: def update(attribute_names = @attributes.keys) attributes_with_values = arel_attributes_values(false, false, attribute_names) return 0 if attributes_with_values.empty? - self.class.arel_table(self.class.table_name, true).conditions(self.class.arel_table[self.class.primary_key].eq(id)).update(attributes_with_values) + self.class.arel_table(self.class.table_name).conditions(self.class.arel_table[self.class.primary_key].eq(id)).update(attributes_with_values) end # Creates a record with values matching those of the instance attributes -- cgit v1.2.3 From d56e987757923fa28ae90cf269707b78b744d66d Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Thu, 27 Aug 2009 16:49:16 -0300 Subject: No need to reload the relation table with a method param, just nil it. --- activerecord/lib/active_record/base.rb | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 2fc7b1e9c6..c11c049415 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1365,9 +1365,7 @@ module ActiveRecord #:nodoc: # end def reset_column_information undefine_attribute_methods - # Reload ARel relation cached table - arel_table(table_name, true) - @column_names = @columns = @columns_hash = @content_columns = @dynamic_methods_hash = @inheritance_column = nil + @arel_table = @column_names = @columns = @columns_hash = @content_columns = @dynamic_methods_hash = @inheritance_column = nil end def reset_column_information_and_inheritable_attributes_for_all_subclasses#:nodoc: @@ -1535,9 +1533,9 @@ module ActiveRecord #:nodoc: end - def arel_table(table = nil, reload = nil) + def arel_table(table = nil) table = table_name if table.blank? - if reload || @arel_table.nil? || @arel_table.name != table + if @arel_table.nil? || @arel_table.name != table @arel_table = Relation.new(self, Arel::Table.new(table)) end @arel_table -- cgit v1.2.3 From 7cce95b25ace33e04526d4490e487a080c1f9b96 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Thu, 27 Aug 2009 18:52:14 -0300 Subject: Add readonly support for relations. --- activerecord/lib/active_record/base.rb | 7 ++++++- activerecord/lib/active_record/relation.rb | 12 +++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index c11c049415..402d68c36e 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1722,7 +1722,7 @@ module ActiveRecord #:nodoc: def construct_finder_arel(options = {}, scope = scope(:find)) # TODO add lock to Arel - arel_table(options[:from]). + relation = arel_table(options[:from]). joins(construct_join(options[:joins], scope)). conditions(construct_conditions(options[:conditions], scope)). select(options[:select] || (scope && scope[:select]) || default_select(options[:joins] || (scope && scope[:joins]))). @@ -1730,6 +1730,11 @@ module ActiveRecord #:nodoc: order(construct_order(options[:order], scope)). limit(construct_limit(options[:limit], scope)). offset(construct_offset(options[:offset], scope)) + + relation = relation.readonly if options[:readonly] + + relation + end def construct_finder_sql(options, scope = scope(:find)) diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 570ba3f80d..4b53857d36 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -5,10 +5,20 @@ module ActiveRecord def initialize(klass, relation) @klass, @relation = klass, relation + @readonly = false + end + + def readonly + @readonly = true + self end def to_a - @klass.find_by_sql(@relation.to_sql) + records = @klass.find_by_sql(@relation.to_sql) + + records.each { |record| record.readonly! } if @readonly + + records end def each(&block) -- cgit v1.2.3 From f2c0725d79e29b02e30e7a4827851acc4a766730 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Thu, 27 Aug 2009 20:00:14 -0300 Subject: Revert "Add readonly support for relations." This reverts commit 7cce95b25ace33e04526d4490e487a080c1f9b96. --- activerecord/lib/active_record/base.rb | 7 +------ activerecord/lib/active_record/relation.rb | 12 +----------- 2 files changed, 2 insertions(+), 17 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 402d68c36e..c11c049415 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1722,7 +1722,7 @@ module ActiveRecord #:nodoc: def construct_finder_arel(options = {}, scope = scope(:find)) # TODO add lock to Arel - relation = arel_table(options[:from]). + arel_table(options[:from]). joins(construct_join(options[:joins], scope)). conditions(construct_conditions(options[:conditions], scope)). select(options[:select] || (scope && scope[:select]) || default_select(options[:joins] || (scope && scope[:joins]))). @@ -1730,11 +1730,6 @@ module ActiveRecord #:nodoc: order(construct_order(options[:order], scope)). limit(construct_limit(options[:limit], scope)). offset(construct_offset(options[:offset], scope)) - - relation = relation.readonly if options[:readonly] - - relation - end def construct_finder_sql(options, scope = scope(:find)) diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 4b53857d36..570ba3f80d 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -5,20 +5,10 @@ module ActiveRecord def initialize(klass, relation) @klass, @relation = klass, relation - @readonly = false - end - - def readonly - @readonly = true - self end def to_a - records = @klass.find_by_sql(@relation.to_sql) - - records.each { |record| record.readonly! } if @readonly - - records + @klass.find_by_sql(@relation.to_sql) end def each(&block) -- cgit v1.2.3 From 6b67df70ab1bc42d9a05571144cdf5614a7d4a6a Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Thu, 27 Aug 2009 20:03:46 -0300 Subject: Revert "Revert "Add readonly support for relations."" This reverts commit f2c0725d79e29b02e30e7a4827851acc4a766730. --- activerecord/lib/active_record/base.rb | 7 ++++++- activerecord/lib/active_record/relation.rb | 12 +++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index c11c049415..402d68c36e 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1722,7 +1722,7 @@ module ActiveRecord #:nodoc: def construct_finder_arel(options = {}, scope = scope(:find)) # TODO add lock to Arel - arel_table(options[:from]). + relation = arel_table(options[:from]). joins(construct_join(options[:joins], scope)). conditions(construct_conditions(options[:conditions], scope)). select(options[:select] || (scope && scope[:select]) || default_select(options[:joins] || (scope && scope[:joins]))). @@ -1730,6 +1730,11 @@ module ActiveRecord #:nodoc: order(construct_order(options[:order], scope)). limit(construct_limit(options[:limit], scope)). offset(construct_offset(options[:offset], scope)) + + relation = relation.readonly if options[:readonly] + + relation + end def construct_finder_sql(options, scope = scope(:find)) diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 570ba3f80d..4b53857d36 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -5,10 +5,20 @@ module ActiveRecord def initialize(klass, relation) @klass, @relation = klass, relation + @readonly = false + end + + def readonly + @readonly = true + self end def to_a - @klass.find_by_sql(@relation.to_sql) + records = @klass.find_by_sql(@relation.to_sql) + + records.each { |record| record.readonly! } if @readonly + + records end def each(&block) -- cgit v1.2.3 From c01c21b31d590f7e8d12e3ae083fcdf0f0c6fd54 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Tue, 1 Sep 2009 15:36:09 -0300 Subject: Added association preload to relation. --- activerecord/lib/active_record/base.rb | 23 ++++++++++++++++++++--- activerecord/lib/active_record/relation.rb | 8 ++++++++ 2 files changed, 28 insertions(+), 3 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 402d68c36e..1c12189e15 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -664,11 +664,28 @@ module ActiveRecord #:nodoc: # This is an alias for find(:all). You can pass in all the same arguments to this method as you can # to find(:all) def all(*args) - if args.empty? && !scoped?(:find) - arel_table + options = args.extract_options! + + + if options.empty? #&& !scoped?(:find) + relation = arel_table else - construct_finder_arel(*args) + include_associations = merge_includes(scope(:find, :include), options[:include]) + + # if include_associations.any? && references_eager_loaded_tables?(options) + # join_dependency = JoinDependency.new(self, include_associations, options[:joins]) + + # relation = construct_finder_arel_with_included_associations(options, join_dependency) + + # relation.preload(include_associations) + # else + relation = construct_finder_arel(options) + if include_associations.any? + relation.preload(include_associations) + # end + end end + relation end # Executes a custom SQL query against your database and returns all the results. The results will diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 4b53857d36..6abb2df8ff 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -6,6 +6,12 @@ module ActiveRecord def initialize(klass, relation) @klass, @relation = klass, relation @readonly = false + @associations_to_preload = [] + end + + def preload(association) + @associations_to_preload << association + @associations_to_preload.flatten! end def readonly @@ -16,6 +22,8 @@ module ActiveRecord def to_a records = @klass.find_by_sql(@relation.to_sql) + @klass.send :preload_associations, records, @associations_to_preload unless @associations_to_preload.empty? + records.each { |record| record.readonly! } if @readonly records -- cgit v1.2.3 From 3747f896a1b727d67e6022001007e5f58b24a267 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Mon, 5 Oct 2009 14:39:20 -0300 Subject: Moved relation's test to relation_test. --- activerecord/lib/active_record/base.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 1c12189e15..2fd3384877 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -667,7 +667,7 @@ module ActiveRecord #:nodoc: options = args.extract_options! - if options.empty? #&& !scoped?(:find) + if options.empty? && !scoped?(:find) relation = arel_table else include_associations = merge_includes(scope(:find, :include), options[:include]) @@ -1751,7 +1751,6 @@ module ActiveRecord #:nodoc: relation = relation.readonly if options[:readonly] relation - end def construct_finder_sql(options, scope = scope(:find)) -- cgit v1.2.3 From 65f055a3ed790d41aeca8d4ca7f3771b05cf544f Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Mon, 5 Oct 2009 15:24:08 -0300 Subject: Added eager loading support to Relation and ActiveRecord#all. --- activerecord/lib/active_record/associations.rb | 4 ++-- activerecord/lib/active_record/base.rb | 17 +++++--------- activerecord/lib/active_record/relation.rb | 32 ++++++++++++++++++++------ 3 files changed, 33 insertions(+), 20 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index b363cceccb..016745add2 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1455,9 +1455,9 @@ module ActiveRecord after_destroy(method_name) end - def find_with_associations(options = {}) + def find_with_associations(options = {}, join_dependency = nil) catch :invalid_query do - join_dependency = JoinDependency.new(self, merge_includes(scope(:find, :include), options[:include]), options[:joins]) + join_dependency ||= JoinDependency.new(self, merge_includes(scope(:find, :include), options[:include]), options[:joins]) rows = select_all_rows(options, join_dependency) return join_dependency.instantiate(rows) end diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 2fd3384877..60e69f020c 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -666,23 +666,18 @@ module ActiveRecord #:nodoc: def all(*args) options = args.extract_options! - if options.empty? && !scoped?(:find) relation = arel_table else + relation = construct_finder_arel(options) include_associations = merge_includes(scope(:find, :include), options[:include]) - # if include_associations.any? && references_eager_loaded_tables?(options) - # join_dependency = JoinDependency.new(self, include_associations, options[:joins]) - - # relation = construct_finder_arel_with_included_associations(options, join_dependency) - - # relation.preload(include_associations) - # else - relation = construct_finder_arel(options) - if include_associations.any? + if include_associations.any? + if references_eager_loaded_tables?(options) + relation.eager_load(include_associations) + else relation.preload(include_associations) - # end + end end end relation diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 6abb2df8ff..24fd29c7f3 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -7,11 +7,17 @@ module ActiveRecord @klass, @relation = klass, relation @readonly = false @associations_to_preload = [] + @eager_load_associations = [] end def preload(association) - @associations_to_preload << association - @associations_to_preload.flatten! + @associations_to_preload += association + self + end + + def eager_load(association) + @eager_load_associations += association + self end def readonly @@ -20,11 +26,23 @@ module ActiveRecord end def to_a - records = @klass.find_by_sql(@relation.to_sql) - - @klass.send :preload_associations, records, @associations_to_preload unless @associations_to_preload.empty? - - records.each { |record| record.readonly! } if @readonly + if @eager_load_associations.any? + records = catch :invalid_query do + @klass.send(:find_with_associations, { + :select => @relation.send(:select_clauses).join(', '), + :joins => @relation.joins(relation), + :group => @relation.send(:group_clauses).join(', '), + :order => @relation.send(:order_clauses).join(', '), + :conditions => @relation.send(:where_clauses).join("\n\tAND "), + :limit => @relation.taken + }, + ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, @eager_load_associations, nil)) + end + else + records = @klass.find_by_sql(@relation.to_sql) + @klass.send(:preload_associations, records, @associations_to_preload) unless @associations_to_preload.empty? + records.each { |record| record.readonly! } if @readonly + end records end -- cgit v1.2.3 From 23c168a4fdd5a8f2191283d1d5c626c348fb6ae0 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Mon, 5 Oct 2009 15:27:40 -0300 Subject: Initial documentation to Base#all. --- activerecord/lib/active_record/base.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 668506c01a..8ea6c69fc5 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -661,8 +661,8 @@ module ActiveRecord #:nodoc: find(:last, *args) end - # This is an alias for find(:all). You can pass in all the same arguments to this method as you can - # to find(:all) + # Returns an ActiveRecord::Relation object. You can pass in all the same arguments to this method as you can + # to find(:all). def all(*args) options = args.extract_options! -- cgit v1.2.3 From 1d5854826b27e5e8bfb041c57a49d1e46178b49e Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Wed, 7 Oct 2009 11:57:59 -0300 Subject: Allow preload and eager_load to work on relations at the same time. --- activerecord/lib/active_record/relation.rb | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 24fd29c7f3..db1c9c24de 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -26,9 +26,9 @@ module ActiveRecord end def to_a - if @eager_load_associations.any? - records = catch :invalid_query do - @klass.send(:find_with_associations, { + records = if @eager_load_associations.any? + catch :invalid_query do + return @klass.send(:find_with_associations, { :select => @relation.send(:select_clauses).join(', '), :joins => @relation.joins(relation), :group => @relation.send(:group_clauses).join(', '), @@ -38,12 +38,14 @@ module ActiveRecord }, ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, @eager_load_associations, nil)) end + [] else - records = @klass.find_by_sql(@relation.to_sql) - @klass.send(:preload_associations, records, @associations_to_preload) unless @associations_to_preload.empty? - records.each { |record| record.readonly! } if @readonly + @klass.find_by_sql(@relation.to_sql) end + @klass.send(:preload_associations, records, @associations_to_preload) unless @associations_to_preload.empty? + records.each { |record| record.readonly! } if @readonly + records end -- cgit v1.2.3 From 942d4b2e4fb6128c3163fb24893e3de9ae73e7c3 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Wed, 7 Oct 2009 12:17:51 -0300 Subject: Don't send table_name when there's no need to. --- activerecord/lib/active_record/base.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 8ea6c69fc5..4472db28ce 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -2573,7 +2573,7 @@ module ActiveRecord #:nodoc: # be made (since they can't be persisted). def destroy unless new_record? - self.class.arel_table(self.class.table_name).conditions(self.class.arel_table[self.class.primary_key].eq(id)).delete + self.class.arel_table.conditions(self.class.arel_table[self.class.primary_key].eq(id)).delete end @destroyed = true -- cgit v1.2.3 From f13a7bf4e17f265b340ca7d703caeed12716e386 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Wed, 7 Oct 2009 12:21:52 -0300 Subject: Don't send table_name when updating attributes, use the class arel_table. --- activerecord/lib/active_record/base.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 4472db28ce..44eaff3aa7 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -2868,7 +2868,7 @@ module ActiveRecord #:nodoc: def update(attribute_names = @attributes.keys) attributes_with_values = arel_attributes_values(false, false, attribute_names) return 0 if attributes_with_values.empty? - self.class.arel_table(self.class.table_name).conditions(self.class.arel_table[self.class.primary_key].eq(id)).update(attributes_with_values) + self.class.arel_table.conditions(self.class.arel_table[self.class.primary_key].eq(id)).update(attributes_with_values) end # Creates a record with values matching those of the instance attributes -- cgit v1.2.3 From 9a958a3d7dc1640c90abbe1164d7ccb7ea9ae685 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Wed, 7 Oct 2009 12:43:04 -0300 Subject: Relations: Added offset when finding with associations. Delegate array instance methods to to_a. --- activerecord/lib/active_record/relation.rb | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index db1c9c24de..6bc56ecf15 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -1,6 +1,7 @@ module ActiveRecord class Relation delegate :to_sql, :to => :relation + delegate :length, :collect, :find, :map, :each, :to => :to_a attr_reader :relation, :klass def initialize(klass, relation) @@ -34,7 +35,8 @@ module ActiveRecord :group => @relation.send(:group_clauses).join(', '), :order => @relation.send(:order_clauses).join(', '), :conditions => @relation.send(:where_clauses).join("\n\tAND "), - :limit => @relation.taken + :limit => @relation.taken, + :offset => @relation.skipped }, ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, @eager_load_associations, nil)) end @@ -49,10 +51,6 @@ module ActiveRecord records end - def each(&block) - to_a.each(&block) - end - def first @relation = @relation.take(1) to_a.first -- cgit v1.2.3 From 06ad817f92e70449fb796af1b0d7cb42a1bac8f0 Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Tue, 13 Oct 2009 15:15:00 -0300 Subject: Use ActiveSupport::Callbacks instead of ActiveSupport::DeprecatedCallbacks. --- activerecord/lib/active_record/connection_adapters/abstract_adapter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index cdf0aebfee..694e1e561c 100755 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -32,7 +32,7 @@ module ActiveRecord class AbstractAdapter include Quoting, DatabaseStatements, SchemaStatements include QueryCache - include ActiveSupport::DeprecatedCallbacks + include ActiveSupport::Callbacks define_callbacks :checkout, :checkin @@row_even = true -- cgit v1.2.3