activerecord 1.13.0 → 1.13.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.

Files changed (68) hide show
  1. data/CHANGELOG +91 -0
  2. data/lib/active_record.rb +2 -2
  3. data/lib/active_record/acts/list.rb +16 -12
  4. data/lib/active_record/acts/tree.rb +2 -2
  5. data/lib/active_record/aggregations.rb +6 -0
  6. data/lib/active_record/associations.rb +38 -16
  7. data/lib/active_record/associations/has_many_association.rb +2 -1
  8. data/lib/active_record/associations/has_one_association.rb +1 -1
  9. data/lib/active_record/base.rb +46 -33
  10. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +33 -9
  11. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +11 -2
  12. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +3 -0
  13. data/lib/active_record/connection_adapters/abstract_adapter.rb +41 -21
  14. data/lib/active_record/connection_adapters/firebird_adapter.rb +414 -0
  15. data/lib/active_record/connection_adapters/mysql_adapter.rb +68 -29
  16. data/lib/active_record/connection_adapters/oci_adapter.rb +141 -21
  17. data/lib/active_record/connection_adapters/postgresql_adapter.rb +82 -21
  18. data/lib/active_record/connection_adapters/sqlite_adapter.rb +3 -3
  19. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +39 -6
  20. data/lib/active_record/fixtures.rb +1 -0
  21. data/lib/active_record/migration.rb +30 -13
  22. data/lib/active_record/validations.rb +18 -7
  23. data/lib/active_record/vendor/mysql.rb +89 -12
  24. data/lib/active_record/version.rb +2 -2
  25. data/rakefile +38 -3
  26. data/test/abstract_unit.rb +5 -0
  27. data/test/aggregations_test.rb +19 -0
  28. data/test/associations_go_eager_test.rb +26 -2
  29. data/test/associations_test.rb +29 -10
  30. data/test/base_test.rb +57 -6
  31. data/test/binary_test.rb +3 -3
  32. data/test/connections/native_db2/connection.rb +1 -1
  33. data/test/connections/native_firebird/connection.rb +24 -0
  34. data/test/connections/native_mysql/connection.rb +1 -1
  35. data/test/connections/native_oci/connection.rb +1 -1
  36. data/test/connections/native_postgresql/connection.rb +6 -6
  37. data/test/connections/native_sqlite/connection.rb +1 -1
  38. data/test/connections/native_sqlite3/connection.rb +1 -1
  39. data/test/connections/native_sqlite3/in_memory_connection.rb +1 -1
  40. data/test/connections/native_sqlserver/connection.rb +1 -1
  41. data/test/connections/native_sqlserver_odbc/connection.rb +1 -1
  42. data/test/default_test_firebird.rb +16 -0
  43. data/test/deprecated_associations_test.rb +1 -1
  44. data/test/finder_test.rb +11 -1
  45. data/test/fixtures/author.rb +30 -30
  46. data/test/fixtures/comment.rb +1 -1
  47. data/test/fixtures/company.rb +3 -1
  48. data/test/fixtures/customer.rb +4 -0
  49. data/test/fixtures/db_definitions/firebird.drop.sql +54 -0
  50. data/test/fixtures/db_definitions/firebird.sql +259 -0
  51. data/test/fixtures/db_definitions/firebird2.drop.sql +2 -0
  52. data/test/fixtures/db_definitions/firebird2.sql +6 -0
  53. data/test/fixtures/db_definitions/oci.sql +8 -0
  54. data/test/fixtures/db_definitions/postgresql.sql +3 -2
  55. data/test/fixtures/developer.rb +10 -0
  56. data/test/fixtures/fixture_database.sqlite +0 -0
  57. data/test/fixtures/fixture_database_2.sqlite +0 -0
  58. data/test/fixtures/mixin.rb +11 -1
  59. data/test/fixtures/mixins.yml +20 -1
  60. data/test/fixtures_test.rb +65 -45
  61. data/test/inheritance_test.rb +1 -1
  62. data/test/migration_test.rb +7 -1
  63. data/test/mixin_test.rb +267 -98
  64. data/test/multiple_db_test.rb +13 -1
  65. data/test/pk_test.rb +1 -0
  66. metadata +11 -5
  67. data/lib/active_record/vendor/mysql411.rb +0 -311
  68. data/test/debug.log +0 -2857
data/CHANGELOG CHANGED
@@ -1,3 +1,94 @@
1
+ *1.13.1* (December 7th, 2005)
2
+
3
+ * MySQL: more robust test for nullified result hashes. #3124 [Stefan Kaes]
4
+
5
+ * SQLite: find database file when RAILS_ROOT is a symlink. #3116 [anna@wota.jp]
6
+
7
+ * Reloading an instance refreshes its aggregations as well as its associations. #3024 [François Beausolei]
8
+
9
+ * Fixed that using :include together with :conditions array in Base.find would cause NoMethodError #2887 [Paul Hammmond]
10
+
11
+ * PostgreSQL: more robust sequence name discovery. #3087 [Rick Olson]
12
+
13
+ * Oracle: use syntax compatible with Oracle 8. #3131 [Michael Schoen]
14
+
15
+ * MySQL: work around ruby-mysql/mysql-ruby inconsistency with mysql.stat. Eliminate usage of mysql.ping because it doesn't guarantee reconnect. Explicitly close and reopen the connection instead. [Jeremy Kemper]
16
+
17
+ * When AbstractAdapter#log rescues an exception, attempt to detect and reconnect to an inactive database connection. Connection adapter must respond to the active? and reconnect! instance methods. Initial support for PostgreSQL, MySQL, and SQLite. Make certain that all statements which may need reconnection are performed within a logged block: for example, this means no avoiding log(sql, name) { } if @logger.nil? [Jeremy Kemper]
18
+
19
+ * Firebird: active? and reconnect! methods for handling stale connections. #428 [Ken Kunz <kennethkunz@gmail.com>]
20
+
21
+ * Firebird: updated for FireRuby 0.4.0. #3009 [Ken Kunz <kennethkunz@gmail.com>]
22
+
23
+ * Introducing the Firebird adapter. Quote columns and use attribute_condition more consistently. Setup guide: http://wiki.rubyonrails.com/rails/pages/Firebird+Adapter #1874 [Ken Kunz <kennethkunz@gmail.com>]
24
+
25
+ * MySQL and PostgreSQL: active? compatibility with the pure-Ruby driver. #428 [Jeremy Kemper]
26
+
27
+ * Oracle: active? check pings the database rather than testing the last command status. #428 [Michael Schoen]
28
+
29
+ * SQLServer: resolve column aliasing/quoting collision when using limit or offset in an eager find. #2974 [kajism@yahoo.com]
30
+
31
+ * Reloading a model doesn't lose track of its connection. #2996 [junk@miriamtech.com, Jeremy Kemper]
32
+
33
+ * Fixed bug where using update_attribute after pushing a record to a habtm association of the object caused duplicate rows in the join table. #2888 [colman@rominato.com, Florian Weber, Michael Schoen]
34
+
35
+ * MySQL: introduce :encoding option to specify the character set for client, connection, and results. Only available for MySQL 4.1 and later with the mysql-ruby driver. Do SHOW CHARACTER SET in mysql client to see available encodings. #2975 [Shugo Maeda]
36
+
37
+ * Add tasks to create, drop and rebuild the MySQL and PostgreSQL test databases. [Marcel Molina Jr.]
38
+
39
+ * Correct boolean handling in generated reader methods. #2945 [don.park@gmail.com, Stefan Kaes]
40
+
41
+ * Don't generate read methods for columns whose names are not valid ruby method names. #2946 [Stefan Kaes]
42
+
43
+ * Document :force option to create_table. #2921 [Blair Zajac <blair@orcaware.com>]
44
+
45
+ * Don't add the same conditions twice in has_one finder sql. #2916 [Jeremy Evans]
46
+
47
+ * Rename Version constant to VERSION. #2802 [Marcel Molina Jr.]
48
+
49
+ * SQLServer: insert uses given primary key value if not nil rather than SELECT @@IDENTITY. #2866 [kajism@yahoo.com, Tom Ward <tom@popdog.net>]
50
+
51
+ * Correct documentation for Base.delete_all. #1568 [Newhydra]
52
+
53
+ * Oracle: test case for column default parsing. #2788 [Michael Schoen <schoenm@earthlink.net>]
54
+
55
+ * Update documentation for Migrations. #2861 [Tom Werner <tom@cube6media.com>]
56
+
57
+ * Oracle: Much faster column reflection. #2848 [Michael Schoen <schoenm@earthlink.net>]
58
+
59
+ * Base.reset_sequence_name analogous to reset_table_name (mostly useful for testing). Base.define_attr_method allows nil values. [Jeremy Kemper]
60
+
61
+ * PostgreSQL: smarter sequence name defaults, stricter last_insert_id, warn on pk without sequence. [Jeremy Kemper]
62
+
63
+ * PostgreSQL: correctly discover custom primary key sequences. #2594 [Blair Zajac <blair@orcaware.com>, meadow.nnick@gmail.com, Jeremy Kemper]
64
+
65
+ * SQLServer: don't report limits for unsupported field types. #2835 [Ryan Tomayko]
66
+
67
+ * Include the Enumerable module in ActiveRecord::Errors. [Rick Bradley <rick@rickbradley.com>]
68
+
69
+ * Add :group option, correspond to GROUP BY, to the find method and to the has_many association. #2818 [rubyonrails@atyp.de]
70
+
71
+ * Don't cast nil or empty strings to a dummy date. #2789 [Rick Bradley <rick@rickbradley.com>]
72
+
73
+ * acts_as_list plays nicely with inheritance by remembering the class which declared it. #2811 [rephorm@rephorm.com]
74
+
75
+ * Fix sqlite adaptor's detection of missing dbfile or database declaration. [Nicholas Seckar]
76
+
77
+ * Fixed acts_as_list for definitions without an explicit :order #2803 [jonathan@bluewire.net.nz]
78
+
79
+ * Upgrade bundled ruby-mysql 0.2.4 with mysql411 shim (see #440) to ruby-mysql 0.2.6 with a patchset for 4.1 protocol support. Local change [301] is now a part of the main driver; reapplied local change [2182]. Removed GC.start from Result.free. [tommy@tmtm.org, akuroda@gmail.com, Doug Fales <doug.fales@gmail.com>, Jeremy Kemper]
80
+
81
+ * Correct handling of complex order clauses with SQL Server limit emulation. #2770 [Tom Ward <tom@popdog.net>, Matt B.]
82
+
83
+ * Correct whitespace problem in Oracle default column value parsing. #2788 [rick@rickbradley.com]
84
+
85
+ * Destroy associated has_and_belongs_to_many records after all before_destroy callbacks but before destroy. This allows you to act on the habtm association as you please while preserving referential integrity. #2065 [larrywilliams1@gmail.com, sam.kirchmeier@gmail.com, elliot@townx.org, Jeremy Kemper]
86
+
87
+ * Deprecate the old, confusing :exclusively_dependent option in favor of :dependent => :delete_all. [Jeremy Kemper]
88
+
89
+ * More compatible Oracle column reflection. #2771 [Ryan Davis <ryand-ruby@zenspider.com>, Michael Schoen <schoenm@earthlink.net>]
90
+
91
+
1
92
  *1.13.0* (November 7th, 2005)
2
93
 
3
94
  * Fixed faulty regex in get_table_name method (SQLServerAdapter) #2639 [Ryan Tomayko]
@@ -66,11 +66,11 @@ ActiveRecord::Base.class_eval do
66
66
  end
67
67
 
68
68
  unless defined?(RAILS_CONNECTION_ADAPTERS)
69
- RAILS_CONNECTION_ADAPTERS = %w(mysql postgresql sqlite sqlserver db2 oci)
69
+ RAILS_CONNECTION_ADAPTERS = %w(mysql postgresql sqlite firebird sqlserver db2 oci)
70
70
  end
71
71
 
72
72
  RAILS_CONNECTION_ADAPTERS.each do |adapter|
73
- require "active_record/connection_adapters/#{adapter}_adapter"
73
+ require "active_record/connection_adapters/" + adapter + "_adapter"
74
74
  end
75
75
 
76
76
  require 'active_record/query_cache'
@@ -54,6 +54,10 @@ module ActiveRecord
54
54
  class_eval <<-EOV
55
55
  include ActiveRecord::Acts::List::InstanceMethods
56
56
 
57
+ def acts_as_list_class
58
+ ::#{self.name}
59
+ end
60
+
57
61
  def position_column
58
62
  '#{configuration[:column]}'
59
63
  end
@@ -78,7 +82,7 @@ module ActiveRecord
78
82
  def move_lower
79
83
  return unless lower_item
80
84
 
81
- self.class.transaction do
85
+ acts_as_list_class.transaction do
82
86
  lower_item.decrement_position
83
87
  increment_position
84
88
  end
@@ -87,7 +91,7 @@ module ActiveRecord
87
91
  def move_higher
88
92
  return unless higher_item
89
93
 
90
- self.class.transaction do
94
+ acts_as_list_class.transaction do
91
95
  higher_item.increment_position
92
96
  decrement_position
93
97
  end
@@ -95,7 +99,7 @@ module ActiveRecord
95
99
 
96
100
  def move_to_bottom
97
101
  return unless in_list?
98
- self.class.transaction do
102
+ acts_as_list_class.transaction do
99
103
  decrement_positions_on_lower_items
100
104
  assume_bottom_position
101
105
  end
@@ -103,7 +107,7 @@ module ActiveRecord
103
107
 
104
108
  def move_to_top
105
109
  return unless in_list?
106
- self.class.transaction do
110
+ acts_as_list_class.transaction do
107
111
  increment_positions_on_higher_items
108
112
  assume_top_position
109
113
  end
@@ -135,14 +139,14 @@ module ActiveRecord
135
139
 
136
140
  def higher_item
137
141
  return nil unless in_list?
138
- self.class.find(:first, :conditions =>
142
+ acts_as_list_class.find(:first, :conditions =>
139
143
  "#{scope_condition} AND #{position_column} = #{(send(position_column).to_i - 1).to_s}"
140
144
  )
141
145
  end
142
146
 
143
147
  def lower_item
144
148
  return nil unless in_list?
145
- self.class.find(:first, :conditions =>
149
+ acts_as_list_class.find(:first, :conditions =>
146
150
  "#{scope_condition} AND #{position_column} = #{(send(position_column).to_i + 1).to_s}"
147
151
  )
148
152
  end
@@ -171,7 +175,7 @@ module ActiveRecord
171
175
  def bottom_item(except = nil)
172
176
  conditions = scope_condition
173
177
  conditions = "#{conditions} AND id != #{except.id}" if except
174
- self.class.find(:first, :conditions => conditions, :order => "#{position_column} DESC")
178
+ acts_as_list_class.find(:first, :conditions => conditions, :order => "#{position_column} DESC")
175
179
  end
176
180
 
177
181
  def assume_bottom_position
@@ -184,7 +188,7 @@ module ActiveRecord
184
188
 
185
189
  # This has the effect of moving all the higher items up one.
186
190
  def decrement_positions_on_higher_items(position)
187
- self.class.update_all(
191
+ acts_as_list_class.update_all(
188
192
  "#{position_column} = (#{position_column} - 1)", "#{scope_condition} AND #{position_column} <= #{position}"
189
193
  )
190
194
  end
@@ -192,7 +196,7 @@ module ActiveRecord
192
196
  # This has the effect of moving all the lower items up one.
193
197
  def decrement_positions_on_lower_items
194
198
  return unless in_list?
195
- self.class.update_all(
199
+ acts_as_list_class.update_all(
196
200
  "#{position_column} = (#{position_column} - 1)", "#{scope_condition} AND #{position_column} > #{send(position_column).to_i}"
197
201
  )
198
202
  end
@@ -200,20 +204,20 @@ module ActiveRecord
200
204
  # This has the effect of moving all the higher items down one.
201
205
  def increment_positions_on_higher_items
202
206
  return unless in_list?
203
- self.class.update_all(
207
+ acts_as_list_class.update_all(
204
208
  "#{position_column} = (#{position_column} + 1)", "#{scope_condition} AND #{position_column} < #{send(position_column).to_i}"
205
209
  )
206
210
  end
207
211
 
208
212
  # This has the effect of moving all the lower items down one.
209
213
  def increment_positions_on_lower_items(position)
210
- self.class.update_all(
214
+ acts_as_list_class.update_all(
211
215
  "#{position_column} = (#{position_column} + 1)", "#{scope_condition} AND #{position_column} >= #{position}"
212
216
  )
213
217
  end
214
218
 
215
219
  def increment_positions_on_all_items
216
- self.class.update_all(
220
+ acts_as_list_class.update_all(
217
221
  "#{position_column} = (#{position_column} + 1)", "#{scope_condition}"
218
222
  )
219
223
  end
@@ -49,10 +49,10 @@ module ActiveRecord
49
49
 
50
50
  module_eval <<-END
51
51
  def self.roots
52
- self.find(:all, :conditions => "#{configuration[:foreign_key]} IS NULL", :order => "#{configuration[:order]}")
52
+ self.find(:all, :conditions => "#{configuration[:foreign_key]} IS NULL", :order => #{configuration[:order].nil? ? "nil" : %Q{"#{configuration[:order]}"}})
53
53
  end
54
54
  def self.root
55
- self.find(:first, :conditions => "#{configuration[:foreign_key]} IS NULL", :order => "#{configuration[:order]}")
55
+ self.find(:first, :conditions => "#{configuration[:foreign_key]} IS NULL", :order => #{configuration[:order].nil? ? "nil" : %Q{"#{configuration[:order]}"}})
56
56
  end
57
57
  END
58
58
 
@@ -5,6 +5,12 @@ module ActiveRecord
5
5
  base.extend(ClassMethods)
6
6
  end
7
7
 
8
+ def clear_aggregation_cache #:nodoc:
9
+ self.class.reflect_on_all_aggregations.to_a.each do |assoc|
10
+ instance_variable_set "@#{assoc.name}", nil
11
+ end unless self.new_record?
12
+ end
13
+
8
14
  # Active Record implements aggregation through a macro-like class method called +composed_of+ for representing attributes
9
15
  # as value objects. It expresses relationships like "Account [is] composed of Money [among other things]" or "Person [is]
10
16
  # composed of [an] address". Each call to the macro adds a description of how the value objects are created from the
@@ -307,14 +307,18 @@ module ActiveRecord
307
307
  # sql fragment, such as "price > 5 AND name LIKE 'B%'".
308
308
  # * <tt>:order</tt> - specify the order in which the associated objects are returned as a "ORDER BY" sql fragment,
309
309
  # such as "last_name, first_name DESC"
310
+ # * <tt>:group</tt> - specify the attribute by which the associated objects are returned as a "GROUP BY" sql fragment,
311
+ # such as "category"
310
312
  # * <tt>:foreign_key</tt> - specify the foreign key used for the association. By default this is guessed to be the name
311
313
  # of this class in lower-case and "_id" suffixed. So a +Person+ class that makes a has_many association will use "person_id"
312
314
  # as the default foreign_key.
313
315
  # * <tt>:dependent</tt> - if set to :destroy (or true) all the associated objects are destroyed
314
- # alongside this object. Also accepts :nullify which will set the associated object's foreign key
315
- # field to NULL.
316
+ # alongside this object by calling their destroy method. If set to :delete_all all associated
317
+ # objects are deleted *without* calling their destroy method. If set to :nullify all associated
318
+ # objects' foreign keys are set to NULL *without* calling their save callbacks.
316
319
  # May not be set if :exclusively_dependent is also set.
317
- # * <tt>:exclusively_dependent</tt> - if set to true all the associated object are deleted in one SQL statement without having their
320
+ # * <tt>:exclusively_dependent</tt> - Deprecated; equivalent to :dependent => :delete_all. If set to true all
321
+ # the associated object are deleted in one SQL statement without having their
318
322
  # before_destroy callback run. This should only be used on associations that depend solely on this class and don't need to do any
319
323
  # clean-up in before_destroy. The upside is that it's much faster, especially if there's a counter_cache involved.
320
324
  # May not be set if :dependent is also set.
@@ -339,7 +343,8 @@ module ActiveRecord
339
343
  options.assert_valid_keys(
340
344
  :foreign_key, :class_name, :exclusively_dependent, :dependent,
341
345
  :conditions, :order, :include, :finder_sql, :counter_sql,
342
- :before_add, :after_add, :before_remove, :after_remove, :extend
346
+ :before_add, :after_add, :before_remove, :after_remove, :extend,
347
+ :group
343
348
  )
344
349
 
345
350
  options[:extend] = create_extension_module(association_id, extension) if block_given?
@@ -350,24 +355,28 @@ module ActiveRecord
350
355
  require_association_class(association_class_name)
351
356
 
352
357
  raise ArgumentError, ':dependent and :exclusively_dependent are mutually exclusive options. You may specify one or the other.' if options[:dependent] and options[:exclusively_dependent]
353
-
358
+
359
+ if options[:exclusively_dependent]
360
+ options[:dependent] = :delete_all
361
+ #warn "The :exclusively_dependent option is deprecated. Please use :dependent => :delete_all instead.")
362
+ end
363
+
354
364
  # See HasManyAssociation#delete_records. Dependent associations
355
365
  # delete children, otherwise foreign key is set to NULL.
356
366
  case options[:dependent]
357
367
  when :destroy, true
358
368
  module_eval "before_destroy '#{association_name}.each { |o| o.destroy }'"
369
+ when :delete_all
370
+ module_eval "before_destroy { |record| #{association_class_name}.delete_all(%(#{association_class_primary_key_name} = \#{record.quoted_id})) }"
359
371
  when :nullify
360
372
  module_eval "before_destroy { |record| #{association_class_name}.update_all(%(#{association_class_primary_key_name} = NULL), %(#{association_class_primary_key_name} = \#{record.quoted_id})) }"
361
373
  when nil, false
362
374
  # pass
363
375
  else
364
- raise ArgumentError, 'The :dependent option expects either true, :destroy or :nullify'
365
- end
366
-
367
- if options[:exclusively_dependent]
368
- module_eval "before_destroy { |record| #{association_class_name}.delete_all(%(#{association_class_primary_key_name} = \#{record.quoted_id})) }"
376
+ raise ArgumentError, 'The :dependent option expects either true, :destroy, :delete_all, or :nullify'
369
377
  end
370
378
 
379
+
371
380
  add_multiple_associated_save_callbacks(association_name)
372
381
  add_association_callbacks(association_name, options)
373
382
 
@@ -646,8 +655,17 @@ module ActiveRecord
646
655
 
647
656
  collection_accessor_methods(association_name, association_class_name, association_class_primary_key_name, options, HasAndBelongsToManyAssociation)
648
657
 
649
- before_destroy_sql = "DELETE FROM #{options[:join_table]} WHERE #{association_class_primary_key_name} = \\\#{self.quoted_id}"
650
- module_eval(%{before_destroy "self.connection.delete(%{#{before_destroy_sql}})"}) # "
658
+ # Don't use a before_destroy callback since users' before_destroy
659
+ # callbacks will be executed after the association is wiped out.
660
+ old_method = "destroy_without_habtm_shim_for_#{association_name}"
661
+ class_eval <<-end_eval
662
+ alias_method :#{old_method}, :destroy_without_callbacks
663
+ def destroy_without_callbacks
664
+ #{association_name}.clear
665
+ #{old_method}
666
+ end
667
+ end_eval
668
+
651
669
  add_association_callbacks(association_name, options)
652
670
 
653
671
  # deprecated api
@@ -791,6 +809,9 @@ module ActiveRecord
791
809
  records_to_save.each { |record| association.send(:insert_record, record) }
792
810
  association.send(:construct_sql) # reconstruct the SQL queries now that we know the owner's id
793
811
  end
812
+
813
+ @new_record_before_save = false
814
+ true
794
815
  end_eval
795
816
 
796
817
  # Doesn't use after_save as that would save associations added in after_create/after_update twice
@@ -959,9 +980,10 @@ module ActiveRecord
959
980
  end
960
981
 
961
982
  def include_eager_conditions?(options)
962
- return false unless options[:conditions]
963
-
964
- options[:conditions].scan(/ ([^.]+)\.[^.]+ /).flatten.any? do |condition_table_name|
983
+ conditions = options[:conditions]
984
+ return false unless conditions
985
+ conditions = conditions.first if conditions.is_a?(Array)
986
+ conditions.scan(/(\w+)\.\w+/).flatten.any? do |condition_table_name|
965
987
  condition_table_name != table_name
966
988
  end
967
989
  end
@@ -981,7 +1003,7 @@ module ActiveRecord
981
1003
  end
982
1004
 
983
1005
  def column_aliases(schema_abbreviations)
984
- schema_abbreviations.collect { |cn, tc| "#{tc.join(".")} AS #{cn}" }.join(", ")
1006
+ schema_abbreviations.collect { |cn, tc| "#{tc[0]}.#{connection.quote_column_name tc[1]} AS #{cn}" }.join(", ")
985
1007
  end
986
1008
 
987
1009
  def association_join(reflection)
@@ -113,7 +113,8 @@ module ActiveRecord
113
113
  :order => @options[:order],
114
114
  :limit => @options[:limit],
115
115
  :joins => @options[:joins],
116
- :include => @options[:include]
116
+ :include => @options[:include],
117
+ :group => @options[:group]
117
118
  )
118
119
  end
119
120
  end
@@ -65,7 +65,7 @@ module ActiveRecord
65
65
  end
66
66
 
67
67
  def construct_sql
68
- @finder_sql = "#{@association_class.table_name}.#{@association_class_primary_key_name} = #{@owner.quoted_id}#{@options[:conditions] ? " AND " + @options[:conditions] : ""}"
68
+ @finder_sql = "#{@association_class.table_name}.#{@association_class_primary_key_name} = #{@owner.quoted_id}"
69
69
  @finder_sql << " AND (#{sanitize_sql(@options[:conditions])})" if @options[:conditions]
70
70
  @finder_sql
71
71
  end
@@ -243,24 +243,6 @@ module ActiveRecord #:nodoc:
243
243
  # on to any new database connections made and which can be retrieved on both a class and instance level by calling +logger+.
244
244
  cattr_accessor :logger
245
245
 
246
- # Returns the connection currently associated with the class. This can
247
- # also be used to "borrow" the connection to do database work unrelated
248
- # to any of the specific Active Records.
249
- def self.connection
250
- if allow_concurrency
251
- retrieve_connection
252
- else
253
- @connection ||= retrieve_connection
254
- end
255
- end
256
-
257
- # Returns the connection currently associated with the class. This can
258
- # also be used to "borrow" the connection to do database work that isn't
259
- # easily done without going straight to SQL.
260
- def connection
261
- self.class.connection
262
- end
263
-
264
246
  def self.inherited(child) #:nodoc:
265
247
  @@subclasses[self] ||= []
266
248
  @@subclasses[self] << child
@@ -363,6 +345,7 @@ module ActiveRecord #:nodoc:
363
345
  #
364
346
  # * <tt>:conditions</tt>: An SQL fragment like "administrator = 1" or [ "user_name = ?", username ]. See conditions in the intro.
365
347
  # * <tt>:order</tt>: An SQL fragment like "created_at DESC, name".
348
+ # * <tt>:group</tt>: An attribute name by which the result should be grouped. Uses the GROUP BY SQL-clause.
366
349
  # * <tt>:limit</tt>: An integer determining the limit on the number of rows that should be returned.
367
350
  # * <tt>:offset</tt>: An integer determining the offset from where the rows should be fetched. So at 5, it would skip the first 4 rows.
368
351
  # * <tt>:joins</tt>: An SQL fragment for additional joins like "LEFT JOIN comments ON comments.post_id = id". (Rarely needed).
@@ -391,6 +374,7 @@ module ActiveRecord #:nodoc:
391
374
  # Person.find(:all, :conditions => [ "category IN (?)", categories], :limit => 50)
392
375
  # Person.find(:all, :offset => 10, :limit => 10)
393
376
  # Person.find(:all, :include => [ :account, :friends ])
377
+ # Person.find(:all, :group => "category")
394
378
  def find(*args)
395
379
  options = extract_options_from_args!(args)
396
380
 
@@ -511,7 +495,7 @@ module ActiveRecord #:nodoc:
511
495
 
512
496
  # Deletes all the records that match the +condition+ without instantiating the objects first (and hence not
513
497
  # calling the destroy method). Example:
514
- # Post.destroy_all "person_id = 5 AND (category = 'Something' OR category = 'Else')"
498
+ # Post.delete_all "person_id = 5 AND (category = 'Something' OR category = 'Else')"
515
499
  def delete_all(conditions = nil)
516
500
  sql = "DELETE FROM #{table_name} "
517
501
  add_conditions!(sql, conditions)
@@ -644,9 +628,16 @@ module ActiveRecord #:nodoc:
644
628
  "type"
645
629
  end
646
630
 
647
- # Default sequence_name. Use set_sequence_name to override.
631
+ # Lazy-set the sequence name to the connection's default. This method
632
+ # is only ever called once since set_sequence_name overrides it.
648
633
  def sequence_name
649
- connection.default_sequence_name(table_name, primary_key)
634
+ reset_sequence_name
635
+ end
636
+
637
+ def reset_sequence_name
638
+ default = connection.default_sequence_name(table_name, primary_key)
639
+ set_sequence_name(default)
640
+ default
650
641
  end
651
642
 
652
643
  # Sets the table name to use to the given value, or (if the value
@@ -697,9 +688,11 @@ module ActiveRecord #:nodoc:
697
688
  # given block. This is required for Oracle and is useful for any
698
689
  # database which relies on sequences for primary key generation.
699
690
  #
700
- # Setting the sequence name when using other dbs will have no effect.
701
- # If a sequence name is not explicitly set when using Oracle, it will
702
- # default to the commonly used pattern of: #{table_name}_seq
691
+ # If a sequence name is not explicitly set when using Oracle or Firebird,
692
+ # it will default to the commonly used pattern of: #{table_name}_seq
693
+ #
694
+ # If a sequence name is not explicitly set when using PostgreSQL, it
695
+ # will discover the sequence corresponding to your primary key for you.
703
696
  #
704
697
  # Example:
705
698
  #
@@ -926,6 +919,7 @@ module ActiveRecord #:nodoc:
926
919
  sql = "SELECT #{options[:select] || '*'} FROM #{table_name} "
927
920
  add_joins!(sql, options)
928
921
  add_conditions!(sql, options[:conditions])
922
+ sql << " GROUP BY #{options[:group]} " if options[:group]
929
923
  sql << " ORDER BY #{options[:order]} " if options[:order]
930
924
  add_limit!(sql, options)
931
925
  sql
@@ -952,8 +946,9 @@ module ActiveRecord #:nodoc:
952
946
  end
953
947
 
954
948
  def type_condition
955
- type_condition = subclasses.inject("#{table_name}.#{inheritance_column} = '#{name.demodulize}' ") do |condition, subclass|
956
- condition << "OR #{table_name}.#{inheritance_column} = '#{subclass.name.demodulize}' "
949
+ quoted_inheritance_column = connection.quote_column_name(inheritance_column)
950
+ type_condition = subclasses.inject("#{table_name}.#{quoted_inheritance_column} = '#{name.demodulize}' ") do |condition, subclass|
951
+ condition << "OR #{table_name}.#{quoted_inheritance_column} = '#{subclass.name.demodulize}' "
957
952
  end
958
953
 
959
954
  " (#{type_condition}) "
@@ -1007,7 +1002,7 @@ module ActiveRecord #:nodoc:
1007
1002
 
1008
1003
  def construct_conditions_from_arguments(attribute_names, arguments)
1009
1004
  conditions = []
1010
- attribute_names.each_with_index { |name, idx| conditions << "#{table_name}.#{name} #{attribute_condition(arguments[idx])} " }
1005
+ attribute_names.each_with_index { |name, idx| conditions << "#{table_name}.#{connection.quote_column_name(name)} #{attribute_condition(arguments[idx])} " }
1011
1006
  [ conditions.join(" AND "), *arguments[0...attribute_names.length] ]
1012
1007
  end
1013
1008
 
@@ -1050,12 +1045,12 @@ module ActiveRecord #:nodoc:
1050
1045
  def define_attr_method(name, value=nil, &block)
1051
1046
  sing = class << self; self; end
1052
1047
  sing.send :alias_method, "original_#{name}", name
1053
- if value
1048
+ if block_given?
1049
+ sing.send :define_method, name, &block
1050
+ else
1054
1051
  # use eval instead of a block to work around a memory leak in dev
1055
1052
  # mode in fcgi
1056
1053
  sing.class_eval "def #{name}; #{value.to_s.inspect}; end"
1057
- else
1058
- sing.send :define_method, name, &block
1059
1054
  end
1060
1055
  end
1061
1056
 
@@ -1171,7 +1166,7 @@ module ActiveRecord #:nodoc:
1171
1166
  end
1172
1167
 
1173
1168
  def validate_find_options(options)
1174
- options.assert_valid_keys [:conditions, :include, :joins, :limit, :offset, :order, :select, :readonly]
1169
+ options.assert_valid_keys [:conditions, :include, :joins, :limit, :offset, :order, :select, :readonly, :group]
1175
1170
  end
1176
1171
 
1177
1172
  def encode_quoted_value(value)
@@ -1244,7 +1239,11 @@ module ActiveRecord #:nodoc:
1244
1239
  freeze
1245
1240
  end
1246
1241
 
1247
- # Returns a clone of the record that hasn't been assigned an id yet and is treated as a new record.
1242
+ # Returns a clone of the record that hasn't been assigned an id yet and
1243
+ # is treated as a new record. Note that this is a "shallow" clone:
1244
+ # it copies the object's attributes only, not its associations.
1245
+ # The extent of a "deep" clone is application-specific and is therefore
1246
+ # left to the application to implement according to its need.
1248
1247
  def clone
1249
1248
  attrs = self.attributes_before_type_cast
1250
1249
  attrs.delete(self.class.primary_key)
@@ -1305,6 +1304,7 @@ module ActiveRecord #:nodoc:
1305
1304
 
1306
1305
  # Reloads the attributes of this object from the database.
1307
1306
  def reload
1307
+ clear_aggregation_cache
1308
1308
  clear_association_cache
1309
1309
  @attributes.update(self.class.find(self.id).instance_variable_get('@attributes'))
1310
1310
  self
@@ -1443,6 +1443,10 @@ module ActiveRecord #:nodoc:
1443
1443
 
1444
1444
  # Creates a new record with values matching those of the instance attributes.
1445
1445
  def create
1446
+ if self.id.nil? and connection.prefetch_primary_key?(self.class.table_name)
1447
+ self.id = connection.next_sequence_value(self.class.sequence_name)
1448
+ end
1449
+
1446
1450
  self.id = connection.insert(
1447
1451
  "INSERT INTO #{self.class.table_name} " +
1448
1452
  "(#{quoted_column_names.join(', ')}) " +
@@ -1542,7 +1546,16 @@ module ActiveRecord #:nodoc:
1542
1546
  self.class.read_methods << attr_name
1543
1547
  end
1544
1548
 
1545
- self.class.class_eval("def #{symbol}; #{access_code}; end")
1549
+ begin
1550
+ self.class.class_eval("def #{symbol}; #{access_code}; end")
1551
+ rescue SyntaxError => err
1552
+ self.class.read_methods.delete(attr_name)
1553
+ if logger
1554
+ logger.warn "Exception occured during reader method compilation."
1555
+ logger.warn "Maybe #{attr_name} is not a valid Ruby identifier?"
1556
+ logger.warn "#{err.message}"
1557
+ end
1558
+ end
1546
1559
  end
1547
1560
 
1548
1561
  # Returns true if the attribute is of a text column and marked for serialization.