activerecord 1.10.0 → 1.10.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

data/CHANGELOG CHANGED
@@ -1,3 +1,12 @@
1
+ *1.10.1* (20th April, 2005)
2
+
3
+ * Fixed frivilous database queries being triggered with eager loading on empty associations and other things
4
+
5
+ * Fixed order of loading in eager associations
6
+
7
+ * Fixed stray comma when using eager loading and ordering together from has_many associations #1143
8
+
9
+
1
10
  *1.10.0* (19th April, 2005)
2
11
 
3
12
  * Added eager loading of associations as a way to solve the N+1 problem more gracefully without piggy-back queries. Example:
@@ -121,7 +121,7 @@ module ActiveRecord
121
121
  #
122
122
  # Consider the following loop using the class above:
123
123
  #
124
- # for post in Post.find(:all, :limit => 100)
124
+ # for post in Post.find(:all)
125
125
  # puts "Post: " + post.title
126
126
  # puts "Written by: " + post.author.name
127
127
  # puts "Last comment on: " + post.comments.first.created_on
@@ -129,20 +129,29 @@ module ActiveRecord
129
129
  #
130
130
  # To iterate over these one hundred posts, we'll generate 201 database queries. Let's first just optimize it for retrieving the author:
131
131
  #
132
- # for post in Post.find(:all, :limit => 100, :include => :author)
132
+ # for post in Post.find(:all, :include => :author)
133
133
  #
134
134
  # This references the name of the belongs_to association that also used the :author symbol, so the find will now weave in a join something
135
135
  # like this: LEFT OUTER JOIN authors ON authors.id = posts.author_id. Doing so will cut down the number of queries from 201 to 101.
136
136
  #
137
137
  # We can improve upon the situation further by referencing both associations in the finder with:
138
138
  #
139
- # for post in Post.find(:all, :limit => 100, :include => [ :author, :comments ])
139
+ # for post in Post.find(:all, :include => [ :author, :comments ])
140
140
  #
141
141
  # That'll add another join along the lines of: LEFT OUTER JOIN comments ON comments.post_id = posts.id. And we'll be down to 1 query.
142
142
  # But that shouldn't fool you to think that you can pull out huge amounts of data with no performance penalty just because you've reduced
143
143
  # the number of queries. The database still needs to send all the data to Active Record and it still needs to be processed. So its no
144
144
  # catch-all for performance problems, but its a great way to cut down on the number of queries in a situation as the one described above.
145
145
  #
146
+ # Please note that because eager loading is fetching both models and associations in the same grab, it doesn't make sense to use the
147
+ # :limit property and it will be ignored if attempted.
148
+ #
149
+ # Also have in mind that since the eager loading is pulling from multiple tables, you'll have to disambiguate any column references
150
+ # in both conditions and orders. So :order => "posts.id DESC" will work while :order => "id DESC" will not. This may require that
151
+ # you alter the :order and :conditions on the association definitions themselves.
152
+ #
153
+ # It's currently not possible to use eager loading on multiple associations from the same table.
154
+ #
146
155
  # == Modules
147
156
  #
148
157
  # By default, associations will look for objects within the current module scope. Consider:
@@ -684,24 +693,28 @@ module ActiveRecord
684
693
  primary_key_table = generate_primary_key_table(reflections, schema_abbreviations)
685
694
 
686
695
  rows = select_all_rows(options, schema_abbreviations, reflections)
687
- records = { }
696
+ records, records_in_order = { }, []
688
697
  primary_key = primary_key_table[table_name]
689
698
 
690
699
  for row in rows
691
700
  id = row[primary_key]
692
- records[id] ||= instantiate(extract_record(schema_abbreviations, table_name, row))
693
-
701
+ records_in_order << (records[id] = instantiate(extract_record(schema_abbreviations, table_name, row))) unless records[id]
702
+ record = records[id]
703
+
694
704
  reflections.each do |reflection|
695
- next unless row[primary_key_table[reflection.table_name]]
696
-
697
705
  case reflection.macro
698
706
  when :has_many, :has_and_belongs_to_many
699
- records[id].send(reflection.name)
700
- records[id].instance_variable_get("@#{reflection.name}").target.push(
701
- reflection.klass.send(:instantiate, extract_record(schema_abbreviations, reflection.table_name, row))
702
- )
707
+ collection = record.send(reflection.name)
708
+ collection.loaded
709
+
710
+ next unless row[primary_key_table[reflection.table_name]]
711
+
712
+ association = reflection.klass.send(:instantiate, extract_record(schema_abbreviations, reflection.table_name, row))
713
+ collection.target.push(association) unless collection.target.include?(association)
703
714
  when :has_one, :belongs_to
704
- records[id].send(
715
+ next unless row[primary_key_table[reflection.table_name]]
716
+
717
+ record.send(
705
718
  "#{reflection.name}=",
706
719
  reflection.klass.send(:instantiate, extract_record(schema_abbreviations, reflection.table_name, row))
707
720
  )
@@ -709,9 +722,10 @@ module ActiveRecord
709
722
  end
710
723
  end
711
724
 
712
- return records.values
725
+ return records_in_order
713
726
  end
714
727
 
728
+
715
729
  def reflect_on_included_associations(associations)
716
730
  [ associations ].flatten.collect { |association| reflect_on_association(association) }
717
731
  end
@@ -32,6 +32,10 @@ module ActiveRecord
32
32
  @loaded
33
33
  end
34
34
 
35
+ def loaded
36
+ @loaded = true
37
+ end
38
+
35
39
  def target
36
40
  @target
37
41
  end
@@ -67,7 +67,13 @@ module ActiveRecord
67
67
  end
68
68
  else
69
69
  options[:conditions] = @finder_sql + (options[:conditions] ? " AND #{options[:conditions]}" : "")
70
- options[:order] = options[:order] ? "#{options[:order]}, #{@options[:order]}" : @options[:order]
70
+
71
+ if options[:order] && @options[:order]
72
+ options[:order] = "#{options[:order]}, #{@options[:order]}"
73
+ elsif @options[:order]
74
+ options[:order] = @options[:order]
75
+ end
76
+
71
77
  @association_class.find(args.size == 1 ? args.first : args, options)
72
78
  end
73
79
  end
data/rakefile CHANGED
@@ -8,7 +8,7 @@ require 'rake/contrib/rubyforgepublisher'
8
8
 
9
9
  PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
10
10
  PKG_NAME = 'activerecord'
11
- PKG_VERSION = '1.10.0' + PKG_BUILD
11
+ PKG_VERSION = '1.10.1' + PKG_BUILD
12
12
  PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
13
13
 
14
14
  RELEASE_NAME = "REL #{PKG_VERSION}"
@@ -17,6 +17,13 @@ class EagerAssociationTest < Test::Unit::TestCase
17
17
  assert post.comments.include?(@greetings)
18
18
  end
19
19
 
20
+ def test_with_ordering
21
+ posts = Post.find(:all, :include => :comments, :order => "posts.id DESC")
22
+ assert_equal @authorless, posts[0]
23
+ assert_equal @thinking, posts[1]
24
+ assert_equal @welcome, posts[2]
25
+ end
26
+
20
27
  def test_loading_with_multiple_associations
21
28
  posts = Post.find(:all, :include => [ :comments, :author, :categories ], :order => "posts.id")
22
29
  assert_equal 2, posts.first.comments.size
@@ -25,7 +32,7 @@ class EagerAssociationTest < Test::Unit::TestCase
25
32
  end
26
33
 
27
34
  def test_loading_from_an_association
28
- posts = @david.posts.find(:all, :include => :comments)
35
+ posts = @david.posts.find(:all, :include => :comments, :order => "posts.id")
29
36
  assert_equal 2, posts.first.comments.size
30
37
  end
31
38
 
@@ -199,8 +199,8 @@ create table categories (
199
199
  );
200
200
 
201
201
  create table categories_posts (
202
- category_id integer not null references developers initially deferred disable,
203
- post_id int integer not null references developers initially deferred disable
202
+ category_id integer not null references categories initially deferred disable,
203
+ post_id integer not null references posts initially deferred disable
204
204
  );
205
205
 
206
206
  create table fk_test_has_pk (
@@ -209,5 +209,5 @@ create table fk_test_has_pk (
209
209
 
210
210
  create table fk_test_has_fk (
211
211
  id integer not null primary key,
212
- fk_id integer not null references fk_test_has_fk initially deferred disable,
212
+ fk_id integer not null references fk_test_has_fk initially deferred disable
213
213
  );
@@ -1,6 +1,6 @@
1
1
  class Post < ActiveRecord::Base
2
2
  belongs_to :author
3
- has_many :comments
3
+ has_many :comments, :order => "body"
4
4
  has_and_belongs_to_many :categories
5
5
  end
6
6
 
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.8.8
3
3
  specification_version: 1
4
4
  name: activerecord
5
5
  version: !ruby/object:Gem::Version
6
- version: 1.10.0
7
- date: 2005-04-19
6
+ version: 1.10.1
7
+ date: 2005-04-20
8
8
  summary: Implements the ActiveRecord pattern for ORM.
9
9
  require_paths:
10
10
  - lib