activerecord 1.14.4 → 1.15.0

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 (159) hide show
  1. data/CHANGELOG +400 -1
  2. data/README +2 -2
  3. data/RUNNING_UNIT_TESTS +21 -3
  4. data/Rakefile +55 -10
  5. data/lib/active_record.rb +10 -4
  6. data/lib/active_record/acts/list.rb +15 -4
  7. data/lib/active_record/acts/nested_set.rb +11 -12
  8. data/lib/active_record/acts/tree.rb +13 -14
  9. data/lib/active_record/aggregations.rb +46 -22
  10. data/lib/active_record/associations.rb +213 -162
  11. data/lib/active_record/associations/association_collection.rb +45 -15
  12. data/lib/active_record/associations/association_proxy.rb +32 -13
  13. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +18 -18
  14. data/lib/active_record/associations/has_many_association.rb +37 -17
  15. data/lib/active_record/associations/has_many_through_association.rb +120 -30
  16. data/lib/active_record/associations/has_one_association.rb +1 -1
  17. data/lib/active_record/attribute_methods.rb +75 -0
  18. data/lib/active_record/base.rb +282 -203
  19. data/lib/active_record/calculations.rb +95 -54
  20. data/lib/active_record/callbacks.rb +13 -24
  21. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +12 -1
  22. data/lib/active_record/connection_adapters/abstract/connection_specification.rb.rej +21 -0
  23. data/lib/active_record/connection_adapters/abstract/database_statements.rb +30 -4
  24. data/lib/active_record/connection_adapters/abstract/quoting.rb +16 -9
  25. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +121 -37
  26. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +55 -23
  27. data/lib/active_record/connection_adapters/abstract_adapter.rb +8 -0
  28. data/lib/active_record/connection_adapters/db2_adapter.rb +1 -11
  29. data/lib/active_record/connection_adapters/firebird_adapter.rb +364 -50
  30. data/lib/active_record/connection_adapters/frontbase_adapter.rb +861 -0
  31. data/lib/active_record/connection_adapters/mysql_adapter.rb +86 -33
  32. data/lib/active_record/connection_adapters/openbase_adapter.rb +4 -3
  33. data/lib/active_record/connection_adapters/oracle_adapter.rb +151 -127
  34. data/lib/active_record/connection_adapters/postgresql_adapter.rb +125 -48
  35. data/lib/active_record/connection_adapters/sqlite_adapter.rb +38 -10
  36. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +183 -155
  37. data/lib/active_record/connection_adapters/sybase_adapter.rb +190 -212
  38. data/lib/active_record/deprecated_associations.rb +24 -10
  39. data/lib/active_record/deprecated_finders.rb +4 -1
  40. data/lib/active_record/fixtures.rb +37 -23
  41. data/lib/active_record/locking/optimistic.rb +106 -0
  42. data/lib/active_record/locking/pessimistic.rb +77 -0
  43. data/lib/active_record/migration.rb +8 -5
  44. data/lib/active_record/observer.rb +73 -34
  45. data/lib/active_record/reflection.rb +21 -7
  46. data/lib/active_record/schema_dumper.rb +33 -5
  47. data/lib/active_record/timestamp.rb +23 -34
  48. data/lib/active_record/transactions.rb +37 -30
  49. data/lib/active_record/validations.rb +46 -30
  50. data/lib/active_record/vendor/mysql.rb +20 -5
  51. data/lib/active_record/version.rb +2 -2
  52. data/lib/active_record/wrappings.rb +1 -2
  53. data/lib/active_record/xml_serialization.rb +308 -0
  54. data/test/aaa_create_tables_test.rb +5 -1
  55. data/test/abstract_unit.rb +18 -8
  56. data/test/{active_schema_mysql.rb → active_schema_test_mysql.rb} +2 -2
  57. data/test/adapter_test.rb +9 -7
  58. data/test/adapter_test_sqlserver.rb +81 -0
  59. data/test/aggregations_test.rb +29 -0
  60. data/test/{association_callbacks_test.rb → associations/callbacks_test.rb} +10 -8
  61. data/test/{associations_cascaded_eager_loading_test.rb → associations/cascaded_eager_loading_test.rb} +35 -3
  62. data/test/{associations_go_eager_test.rb → associations/eager_test.rb} +36 -2
  63. data/test/{associations_extensions_test.rb → associations/extension_test.rb} +5 -0
  64. data/test/{associations_join_model_test.rb → associations/join_model_test.rb} +118 -8
  65. data/test/associations_test.rb +339 -45
  66. data/test/attribute_methods_test.rb +49 -0
  67. data/test/base_test.rb +321 -67
  68. data/test/calculations_test.rb +48 -10
  69. data/test/callbacks_test.rb +13 -0
  70. data/test/connection_test_firebird.rb +8 -0
  71. data/test/connections/native_db2/connection.rb +18 -17
  72. data/test/connections/native_firebird/connection.rb +19 -17
  73. data/test/connections/native_frontbase/connection.rb +27 -0
  74. data/test/connections/native_mysql/connection.rb +18 -15
  75. data/test/connections/native_openbase/connection.rb +14 -15
  76. data/test/connections/native_oracle/connection.rb +16 -12
  77. data/test/connections/native_postgresql/connection.rb +16 -17
  78. data/test/connections/native_sqlite/connection.rb +3 -6
  79. data/test/connections/native_sqlite3/connection.rb +3 -6
  80. data/test/connections/native_sqlserver/connection.rb +16 -17
  81. data/test/connections/native_sqlserver_odbc/connection.rb +18 -19
  82. data/test/connections/native_sybase/connection.rb +16 -17
  83. data/test/datatype_test_postgresql.rb +52 -0
  84. data/test/defaults_test.rb +52 -10
  85. data/test/deprecated_associations_test.rb +151 -107
  86. data/test/deprecated_finder_test.rb +83 -66
  87. data/test/empty_date_time_test.rb +25 -0
  88. data/test/finder_test.rb +118 -11
  89. data/test/fixtures/accounts.yml +6 -1
  90. data/test/fixtures/author.rb +27 -4
  91. data/test/fixtures/categorizations.yml +8 -2
  92. data/test/fixtures/category.rb +1 -2
  93. data/test/fixtures/comments.yml +0 -6
  94. data/test/fixtures/companies.yml +6 -1
  95. data/test/fixtures/company.rb +23 -1
  96. data/test/fixtures/company_in_module.rb +8 -10
  97. data/test/fixtures/customer.rb +2 -2
  98. data/test/fixtures/customers.yml +9 -0
  99. data/test/fixtures/db_definitions/db2.drop.sql +1 -0
  100. data/test/fixtures/db_definitions/db2.sql +9 -0
  101. data/test/fixtures/db_definitions/firebird.drop.sql +3 -0
  102. data/test/fixtures/db_definitions/firebird.sql +13 -1
  103. data/test/fixtures/db_definitions/frontbase.drop.sql +31 -0
  104. data/test/fixtures/db_definitions/frontbase.sql +262 -0
  105. data/test/fixtures/db_definitions/frontbase2.drop.sql +1 -0
  106. data/test/fixtures/db_definitions/frontbase2.sql +4 -0
  107. data/test/fixtures/db_definitions/mysql.drop.sql +1 -0
  108. data/test/fixtures/db_definitions/mysql.sql +23 -14
  109. data/test/fixtures/db_definitions/openbase.sql +13 -1
  110. data/test/fixtures/db_definitions/oracle.drop.sql +2 -0
  111. data/test/fixtures/db_definitions/oracle.sql +29 -2
  112. data/test/fixtures/db_definitions/postgresql.drop.sql +3 -1
  113. data/test/fixtures/db_definitions/postgresql.sql +13 -3
  114. data/test/fixtures/db_definitions/schema.rb +29 -1
  115. data/test/fixtures/db_definitions/sqlite.drop.sql +1 -0
  116. data/test/fixtures/db_definitions/sqlite.sql +12 -3
  117. data/test/fixtures/db_definitions/sqlserver.drop.sql +3 -0
  118. data/test/fixtures/db_definitions/sqlserver.sql +35 -0
  119. data/test/fixtures/db_definitions/sybase.drop.sql +2 -0
  120. data/test/fixtures/db_definitions/sybase.sql +13 -4
  121. data/test/fixtures/developer.rb +12 -0
  122. data/test/fixtures/edge.rb +5 -0
  123. data/test/fixtures/edges.yml +6 -0
  124. data/test/fixtures/funny_jokes.yml +3 -7
  125. data/test/fixtures/migrations_with_decimal/1_give_me_big_numbers.rb +15 -0
  126. data/test/fixtures/migrations_with_missing_versions/1000_people_have_middle_names.rb +9 -0
  127. data/test/fixtures/migrations_with_missing_versions/1_people_have_last_names.rb +9 -0
  128. data/test/fixtures/migrations_with_missing_versions/3_we_need_reminders.rb +12 -0
  129. data/test/fixtures/migrations_with_missing_versions/4_innocent_jointable.rb +12 -0
  130. data/test/fixtures/mixin.rb +15 -0
  131. data/test/fixtures/mixins.yml +38 -0
  132. data/test/fixtures/post.rb +3 -2
  133. data/test/fixtures/project.rb +3 -1
  134. data/test/fixtures/topic.rb +6 -1
  135. data/test/fixtures/topics.yml +4 -4
  136. data/test/fixtures/vertex.rb +9 -0
  137. data/test/fixtures/vertices.yml +4 -0
  138. data/test/fixtures_test.rb +45 -0
  139. data/test/inheritance_test.rb +67 -6
  140. data/test/lifecycle_test.rb +40 -19
  141. data/test/locking_test.rb +170 -26
  142. data/test/method_scoping_test.rb +2 -2
  143. data/test/migration_test.rb +387 -110
  144. data/test/migration_test_firebird.rb +124 -0
  145. data/test/mixin_nested_set_test.rb +14 -2
  146. data/test/mixin_test.rb +56 -18
  147. data/test/modules_test.rb +8 -2
  148. data/test/multiple_db_test.rb +2 -2
  149. data/test/pk_test.rb +1 -0
  150. data/test/reflection_test.rb +8 -2
  151. data/test/schema_authorization_test_postgresql.rb +75 -0
  152. data/test/schema_dumper_test.rb +40 -4
  153. data/test/table_name_test_sqlserver.rb +23 -0
  154. data/test/threaded_connections_test.rb +19 -16
  155. data/test/transactions_test.rb +86 -72
  156. data/test/validations_test.rb +126 -56
  157. data/test/xml_serialization_test.rb +125 -0
  158. metadata +45 -11
  159. data/lib/active_record/locking.rb +0 -79
@@ -1,10 +1,12 @@
1
- require 'parsedate'
1
+ require 'date'
2
+ require 'bigdecimal'
3
+ require 'bigdecimal/util'
2
4
 
3
5
  module ActiveRecord
4
6
  module ConnectionAdapters #:nodoc:
5
7
  # An abstract definition of a column in a table.
6
8
  class Column
7
- attr_reader :name, :default, :type, :limit, :null, :sql_type
9
+ attr_reader :name, :default, :type, :limit, :null, :sql_type, :precision, :scale
8
10
  attr_accessor :primary
9
11
 
10
12
  # Instantiates a new column in the table.
@@ -14,22 +16,20 @@ module ActiveRecord
14
16
  # +sql_type+ is only used to extract the column's length, if necessary. For example, <tt>company_name varchar(<b>60</b>)</tt>.
15
17
  # +null+ determines if this column allows +NULL+ values.
16
18
  def initialize(name, default, sql_type = nil, null = true)
17
- @name, @type, @null = name, simplified_type(sql_type), null
18
- @sql_type = sql_type
19
- # have to do this one separately because type_cast depends on #type
19
+ @name, @sql_type, @null = name, sql_type, null
20
+ @limit, @precision, @scale = extract_limit(sql_type), extract_precision(sql_type), extract_scale(sql_type)
21
+ @type = simplified_type(sql_type)
20
22
  @default = type_cast(default)
21
- @limit = extract_limit(sql_type) unless sql_type.nil?
23
+
22
24
  @primary = nil
23
- @text = [:string, :text].include? @type
24
- @number = [:float, :integer].include? @type
25
25
  end
26
26
 
27
27
  def text?
28
- @text
28
+ [:string, :text].include? type
29
29
  end
30
30
 
31
31
  def number?
32
- @number
32
+ [:float, :integer, :decimal].include? type
33
33
  end
34
34
 
35
35
  # Returns the Ruby class that corresponds to the abstract data type.
@@ -37,6 +37,7 @@ module ActiveRecord
37
37
  case type
38
38
  when :integer then Fixnum
39
39
  when :float then Float
40
+ when :decimal then BigDecimal
40
41
  when :datetime then Time
41
42
  when :date then Date
42
43
  when :timestamp then Time
@@ -55,6 +56,7 @@ module ActiveRecord
55
56
  when :text then value
56
57
  when :integer then value.to_i rescue value ? 1 : 0
57
58
  when :float then value.to_f
59
+ when :decimal then self.class.value_to_decimal(value)
58
60
  when :datetime then self.class.string_to_time(value)
59
61
  when :timestamp then self.class.string_to_time(value)
60
62
  when :time then self.class.string_to_dummy_time(value)
@@ -71,6 +73,7 @@ module ActiveRecord
71
73
  when :text then nil
72
74
  when :integer then "(#{var_name}.to_i rescue #{var_name} ? 1 : 0)"
73
75
  when :float then "#{var_name}.to_f"
76
+ when :decimal then "#{self.class.name}.value_to_decimal(#{var_name})"
74
77
  when :datetime then "#{self.class.name}.string_to_time(#{var_name})"
75
78
  when :timestamp then "#{self.class.name}.string_to_time(#{var_name})"
76
79
  when :time then "#{self.class.name}.string_to_dummy_time(#{var_name})"
@@ -108,39 +111,74 @@ module ActiveRecord
108
111
 
109
112
  def self.string_to_time(string)
110
113
  return string unless string.is_a?(String)
111
- time_array = ParseDate.parsedate(string)[0..5]
114
+ time_hash = Date._parse(string)
115
+ time_hash[:sec_fraction] = microseconds(time_hash)
116
+ time_array = time_hash.values_at(:year, :mon, :mday, :hour, :min, :sec, :sec_fraction)
112
117
  # treat 0000-00-00 00:00:00 as nil
113
- Time.send(Base.default_timezone, *time_array) rescue nil
118
+ Time.send(Base.default_timezone, *time_array) rescue DateTime.new(*time_array[0..5]) rescue nil
114
119
  end
115
120
 
116
121
  def self.string_to_dummy_time(string)
117
122
  return string unless string.is_a?(String)
118
- time_array = ParseDate.parsedate(string)
123
+ return nil if string.empty?
124
+ time_hash = Date._parse(string)
125
+ time_hash[:sec_fraction] = microseconds(time_hash)
119
126
  # pad the resulting array with dummy date information
120
- time_array[0] = 2000; time_array[1] = 1; time_array[2] = 1;
127
+ time_array = [2000, 1, 1]
128
+ time_array += time_hash.values_at(:hour, :min, :sec, :sec_fraction)
121
129
  Time.send(Base.default_timezone, *time_array) rescue nil
122
130
  end
123
131
 
124
132
  # convert something to a boolean
125
133
  def self.value_to_boolean(value)
126
- return value if value==true || value==false
127
- case value.to_s.downcase
128
- when "true", "t", "1" then true
129
- else false
134
+ if value == true || value == false
135
+ value
136
+ else
137
+ %w(true t 1).include?(value.to_s.downcase)
138
+ end
139
+ end
140
+
141
+ # convert something to a BigDecimal
142
+ def self.value_to_decimal(value)
143
+ if value.is_a?(BigDecimal)
144
+ value
145
+ elsif value.respond_to?(:to_d)
146
+ value.to_d
147
+ else
148
+ value.to_s.to_d
130
149
  end
131
150
  end
132
151
 
133
- private
152
+ private
153
+ # '0.123456' -> 123456
154
+ # '1.123456' -> 123456
155
+ def self.microseconds(time)
156
+ ((time[:sec_fraction].to_f % 1) * 1_000_000).to_i
157
+ end
158
+
134
159
  def extract_limit(sql_type)
135
160
  $1.to_i if sql_type =~ /\((.*)\)/
136
161
  end
137
162
 
163
+ def extract_precision(sql_type)
164
+ $2.to_i if sql_type =~ /^(numeric|decimal|number)\((\d+)(,\d+)?\)/i
165
+ end
166
+
167
+ def extract_scale(sql_type)
168
+ case sql_type
169
+ when /^(numeric|decimal|number)\((\d+)\)/i then 0
170
+ when /^(numeric|decimal|number)\((\d+)(,(\d+))\)/i then $4.to_i
171
+ end
172
+ end
173
+
138
174
  def simplified_type(field_type)
139
175
  case field_type
140
176
  when /int/i
141
177
  :integer
142
- when /float|double|decimal|numeric/i
178
+ when /float|double/i
143
179
  :float
180
+ when /decimal|numeric|number/i
181
+ extract_scale(field_type) == 0 ? :integer : :decimal
144
182
  when /datetime/i
145
183
  :datetime
146
184
  when /timestamp/i
@@ -164,18 +202,20 @@ module ActiveRecord
164
202
  class IndexDefinition < Struct.new(:table, :name, :unique, :columns) #:nodoc:
165
203
  end
166
204
 
167
- class ColumnDefinition < Struct.new(:base, :name, :type, :limit, :default, :null) #:nodoc:
205
+ class ColumnDefinition < Struct.new(:base, :name, :type, :limit, :precision, :scale, :default, :null) #:nodoc:
206
+
207
+ def sql_type
208
+ base.type_to_sql(type.to_sym, limit, precision, scale) rescue type
209
+ end
210
+
168
211
  def to_sql
169
- column_sql = "#{base.quote_column_name(name)} #{type_to_sql(type.to_sym, limit)}"
170
- add_column_options!(column_sql, :null => null, :default => default)
212
+ column_sql = "#{base.quote_column_name(name)} #{sql_type}"
213
+ add_column_options!(column_sql, :null => null, :default => default) unless type.to_sym == :primary_key
171
214
  column_sql
172
215
  end
173
216
  alias to_s :to_sql
174
217
 
175
218
  private
176
- def type_to_sql(name, limit)
177
- base.type_to_sql(name, limit) rescue name
178
- end
179
219
 
180
220
  def add_column_options!(sql, options)
181
221
  base.add_column_options!(sql, options.merge(:column => self))
@@ -195,7 +235,7 @@ module ActiveRecord
195
235
  # Appends a primary key definition to the table definition.
196
236
  # Can be called multiple times, but this is probably not a good idea.
197
237
  def primary_key(name)
198
- column(name, native[:primary_key])
238
+ column(name, :primary_key)
199
239
  end
200
240
 
201
241
  # Returns a ColumnDefinition for the column with name +name+.
@@ -206,37 +246,81 @@ module ActiveRecord
206
246
  # Instantiates a new column for the table.
207
247
  # The +type+ parameter must be one of the following values:
208
248
  # <tt>:primary_key</tt>, <tt>:string</tt>, <tt>:text</tt>,
209
- # <tt>:integer</tt>, <tt>:float</tt>, <tt>:datetime</tt>,
210
- # <tt>:timestamp</tt>, <tt>:time</tt>, <tt>:date</tt>,
211
- # <tt>:binary</tt>, <tt>:boolean</tt>.
249
+ # <tt>:integer</tt>, <tt>:float</tt>, <tt>:decimal</tt>,
250
+ # <tt>:datetime</tt>, <tt>:timestamp</tt>, <tt>:time</tt>,
251
+ # <tt>:date</tt>, <tt>:binary</tt>, <tt>:boolean</tt>.
212
252
  #
213
253
  # Available options are (none of these exists by default):
214
254
  # * <tt>:limit</tt>:
215
255
  # Requests a maximum column length (<tt>:string</tt>, <tt>:text</tt>,
216
256
  # <tt>:binary</tt> or <tt>:integer</tt> columns only)
217
257
  # * <tt>:default</tt>:
218
- # The column's default value. You cannot explicitely set the default
219
- # value to +NULL+. Simply leave off this option if you want a +NULL+
220
- # default value.
258
+ # The column's default value. Use nil for NULL.
221
259
  # * <tt>:null</tt>:
222
260
  # Allows or disallows +NULL+ values in the column. This option could
223
261
  # have been named <tt>:null_allowed</tt>.
262
+ # * <tt>:precision</tt>:
263
+ # Specifies the precision for a <tt>:decimal</tt> column.
264
+ # * <tt>:scale</tt>:
265
+ # Specifies the scale for a <tt>:decimal</tt> column.
266
+ #
267
+ # Please be aware of different RDBMS implementations behavior with
268
+ # <tt>:decimal</tt> columns:
269
+ # * The SQL standard says the default scale should be 0, <tt>:scale</tt> <=
270
+ # <tt>:precision</tt>, and makes no comments about the requirements of
271
+ # <tt>:precision</tt>.
272
+ # * MySQL: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..30].
273
+ # Default is (10,0).
274
+ # * PostgreSQL: <tt>:precision</tt> [1..infinity],
275
+ # <tt>:scale</tt> [0..infinity]. No default.
276
+ # * SQLite2: Any <tt>:precision</tt> and <tt>:scale</tt> may be used.
277
+ # Internal storage as strings. No default.
278
+ # * SQLite3: No restrictions on <tt>:precision</tt> and <tt>:scale</tt>,
279
+ # but the maximum supported <tt>:precision</tt> is 16. No default.
280
+ # * Oracle: <tt>:precision</tt> [1..38], <tt>:scale</tt> [-84..127].
281
+ # Default is (38,0).
282
+ # * DB2: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..62].
283
+ # Default unknown.
284
+ # * Firebird: <tt>:precision</tt> [1..18], <tt>:scale</tt> [0..18].
285
+ # Default (9,0). Internal types NUMERIC and DECIMAL have different
286
+ # storage rules, decimal being better.
287
+ # * FrontBase?: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38].
288
+ # Default (38,0). WARNING Max <tt>:precision</tt>/<tt>:scale</tt> for
289
+ # NUMERIC is 19, and DECIMAL is 38.
290
+ # * SqlServer?: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38].
291
+ # Default (38,0).
292
+ # * Sybase: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38].
293
+ # Default (38,0).
294
+ # * OpenBase?: Documentation unclear. Claims storage in <tt>double</tt>.
224
295
  #
225
296
  # This method returns <tt>self</tt>.
226
297
  #
227
298
  # ===== Examples
228
- # # Assuming def is an instance of TableDefinition
229
- # def.column(:granted, :boolean)
299
+ # # Assuming td is an instance of TableDefinition
300
+ # td.column(:granted, :boolean)
230
301
  # #=> granted BOOLEAN
231
302
  #
232
- # def.column(:picture, :binary, :limit => 2.megabytes)
303
+ # td.column(:picture, :binary, :limit => 2.megabytes)
233
304
  # #=> picture BLOB(2097152)
234
305
  #
235
- # def.column(:sales_stage, :string, :limit => 20, :default => 'new', :null => false)
306
+ # td.column(:sales_stage, :string, :limit => 20, :default => 'new', :null => false)
236
307
  # #=> sales_stage VARCHAR(20) DEFAULT 'new' NOT NULL
308
+ #
309
+ # def.column(:bill_gates_money, :decimal, :precision => 15, :scale => 2)
310
+ # #=> bill_gates_money DECIMAL(15,2)
311
+ #
312
+ # def.column(:sensor_reading, :decimal, :precision => 30, :scale => 20)
313
+ # #=> sensor_reading DECIMAL(30,20)
314
+ #
315
+ # # While <tt>:scale</tt> defaults to zero on most databases, it
316
+ # # probably wouldn't hurt to include it.
317
+ # def.column(:huge_integer, :decimal, :precision => 30)
318
+ # #=> huge_integer DECIMAL(30)
237
319
  def column(name, type, options = {})
238
320
  column = self[name] || ColumnDefinition.new(@base, name, type)
239
321
  column.limit = options[:limit] || native[type.to_sym][:limit] if options[:limit] or native[type.to_sym]
322
+ column.precision = options[:precision]
323
+ column.scale = options[:scale]
240
324
  column.default = options[:default]
241
325
  column.null = options[:null]
242
326
  @columns << column unless @columns.include? column
@@ -44,8 +44,8 @@ module ActiveRecord
44
44
  #
45
45
  # The +options+ hash can include the following keys:
46
46
  # [<tt>:id</tt>]
47
- # Set to true or false to add/not add a primary key column
48
- # automatically. Defaults to true.
47
+ # Whether to automatically add a primary key column. Defaults to true.
48
+ # Join tables for has_and_belongs_to_many should set :id => false.
49
49
  # [<tt>:primary_key</tt>]
50
50
  # The name of the primary key, if one is to be added automatically.
51
51
  # Defaults to +id+.
@@ -94,7 +94,7 @@ module ActiveRecord
94
94
  yield table_definition
95
95
 
96
96
  if options[:force]
97
- drop_table(name) rescue nil
97
+ drop_table(name, options) rescue nil
98
98
  end
99
99
 
100
100
  create_sql = "CREATE#{' TEMPORARY' if options[:temporary]} TABLE "
@@ -112,14 +112,14 @@ module ActiveRecord
112
112
  end
113
113
 
114
114
  # Drops a table from the database.
115
- def drop_table(name)
115
+ def drop_table(name, options = {})
116
116
  execute "DROP TABLE #{name}"
117
117
  end
118
118
 
119
119
  # Adds a new column to the named table.
120
120
  # See TableDefinition#column for details of the options you can use.
121
121
  def add_column(table_name, column_name, type, options = {})
122
- add_column_sql = "ALTER TABLE #{table_name} ADD #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit])}"
122
+ add_column_sql = "ALTER TABLE #{table_name} ADD #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
123
123
  add_column_options!(add_column_sql, options)
124
124
  execute(add_column_sql)
125
125
  end
@@ -178,14 +178,14 @@ module ActiveRecord
178
178
  # ====== Creating a unique index
179
179
  # add_index(:accounts, [:branch_id, :party_id], :unique => true)
180
180
  # generates
181
- # CREATE UNIQUE INDEX accounts_branch_id_index ON accounts(branch_id, party_id)
181
+ # CREATE UNIQUE INDEX accounts_branch_id_party_id_index ON accounts(branch_id, party_id)
182
182
  # ====== Creating a named index
183
183
  # add_index(:accounts, [:branch_id, :party_id], :unique => true, :name => 'by_branch_party')
184
184
  # generates
185
185
  # CREATE UNIQUE INDEX by_branch_party ON accounts(branch_id, party_id)
186
186
  def add_index(table_name, column_name, options = {})
187
187
  column_names = Array(column_name)
188
- index_name = index_name(table_name, :column => column_names.first)
188
+ index_name = index_name(table_name, :column => column_names)
189
189
 
190
190
  if Hash === options # legacy support, since this param was a string
191
191
  index_type = options[:unique] ? "UNIQUE" : ""
@@ -199,16 +199,14 @@ module ActiveRecord
199
199
 
200
200
  # Remove the given index from the table.
201
201
  #
202
- # Remove the suppliers_name_index in the suppliers table (legacy support, use the second or third forms).
202
+ # Remove the suppliers_name_index in the suppliers table.
203
203
  # remove_index :suppliers, :name
204
- # Remove the index named accounts_branch_id in the accounts table.
204
+ # Remove the index named accounts_branch_id_index in the accounts table.
205
205
  # remove_index :accounts, :column => :branch_id
206
+ # Remove the index named accounts_branch_id_party_id_index in the accounts table.
207
+ # remove_index :accounts, :column => [:branch_id, :party_id]
206
208
  # Remove the index named by_branch_party in the accounts table.
207
209
  # remove_index :accounts, :name => :by_branch_party
208
- #
209
- # You can remove an index on multiple columns by specifying the first column.
210
- # add_index :accounts, [:username, :password]
211
- # remove_index :accounts, :username
212
210
  def remove_index(table_name, options = {})
213
211
  execute "DROP INDEX #{quote_column_name(index_name(table_name, options))} ON #{table_name}"
214
212
  end
@@ -216,14 +214,14 @@ module ActiveRecord
216
214
  def index_name(table_name, options) #:nodoc:
217
215
  if Hash === options # legacy support
218
216
  if options[:column]
219
- "#{table_name}_#{options[:column]}_index"
217
+ "index_#{table_name}_on_#{Array(options[:column]) * '_and_'}"
220
218
  elsif options[:name]
221
219
  options[:name]
222
220
  else
223
221
  raise ArgumentError, "You must specify the index name"
224
222
  end
225
223
  else
226
- "#{table_name}_#{options}_index"
224
+ index_name(table_name, :column => options)
227
225
  end
228
226
  end
229
227
 
@@ -254,18 +252,52 @@ module ActiveRecord
254
252
  end
255
253
 
256
254
 
257
- def type_to_sql(type, limit = nil) #:nodoc:
255
+ def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc:
258
256
  native = native_database_types[type]
259
- limit ||= native[:limit]
260
- column_type_sql = native[:name]
261
- column_type_sql << "(#{limit})" if limit
262
- column_type_sql
263
- end
264
-
257
+ column_type_sql = native.is_a?(Hash) ? native[:name] : native
258
+ if type == :decimal # ignore limit, use precison and scale
259
+ precision ||= native[:precision]
260
+ scale ||= native[:scale]
261
+ if precision
262
+ if scale
263
+ column_type_sql << "(#{precision},#{scale})"
264
+ else
265
+ column_type_sql << "(#{precision})"
266
+ end
267
+ else
268
+ raise ArgumentError, "Error adding decimal column: precision cannot be empty if scale if specified" if scale
269
+ end
270
+ column_type_sql
271
+ else
272
+ limit ||= native[:limit]
273
+ column_type_sql << "(#{limit})" if limit
274
+ column_type_sql
275
+ end
276
+ end
277
+
265
278
  def add_column_options!(sql, options) #:nodoc:
266
- sql << " DEFAULT #{quote(options[:default], options[:column])}" unless options[:default].nil?
279
+ sql << " DEFAULT #{quote(options[:default], options[:column])}" if options_include_default?(options)
267
280
  sql << " NOT NULL" if options[:null] == false
268
281
  end
282
+
283
+ # SELECT DISTINCT clause for a given set of columns and a given ORDER BY clause.
284
+ # Both PostgreSQL and Oracle overrides this for custom DISTINCT syntax.
285
+ #
286
+ # distinct("posts.id", "posts.created_at desc")
287
+ def distinct(columns, order_by)
288
+ "DISTINCT #{columns}"
289
+ end
290
+
291
+ # ORDER BY clause for the passed order option.
292
+ # PostgreSQL overrides this due to its stricter standards compliance.
293
+ def add_order_by_for_association_limiting!(sql, options)
294
+ sql << "ORDER BY #{options[:order]}"
295
+ end
296
+
297
+ protected
298
+ def options_include_default?(options)
299
+ options.include?(:default) && !(options[:null] == false && options[:default].nil?)
300
+ end
269
301
  end
270
302
  end
271
303
  end
@@ -1,5 +1,7 @@
1
1
  require 'benchmark'
2
2
  require 'date'
3
+ require 'bigdecimal'
4
+ require 'bigdecimal/util'
3
5
 
4
6
  require 'active_record/connection_adapters/abstract/schema_definitions'
5
7
  require 'active_record/connection_adapters/abstract/schema_statements'
@@ -77,6 +79,12 @@ module ActiveRecord
77
79
  @active = false
78
80
  end
79
81
 
82
+ # Returns true if its safe to reload the connection between requests for development mode.
83
+ # This is not the case for Ruby/MySQL and it's not necessary for any adapters except SQLite.
84
+ def requires_reloading?
85
+ false
86
+ end
87
+
80
88
  # Lazily verify this connection, calling +active?+ only if it hasn't
81
89
  # been called for +timeout+ seconds.
82
90
  def verify!(timeout)
@@ -47,14 +47,6 @@ begin
47
47
  end
48
48
  end
49
49
  end
50
-
51
- def select_all(sql, name = nil)
52
- select(sql, name)
53
- end
54
-
55
- def select_one(sql, name = nil)
56
- select(sql, name).first
57
- end
58
50
 
59
51
  def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
60
52
  execute(sql, name = nil)
@@ -72,9 +64,6 @@ begin
72
64
  rows_affected
73
65
  end
74
66
 
75
- alias_method :update, :execute
76
- alias_method :delete, :execute
77
-
78
67
  def begin_db_transaction
79
68
  @connection.set_auto_commit_off
80
69
  end
@@ -162,6 +151,7 @@ begin
162
151
  :text => { :name => 'clob', :limit => 32768 },
163
152
  :integer => { :name => 'int' },
164
153
  :float => { :name => 'float' },
154
+ :decimal => { :name => 'decimal' },
165
155
  :datetime => { :name => 'timestamp' },
166
156
  :timestamp => { :name => 'timestamp' },
167
157
  :time => { :name => 'time' },