activerecord 2.1.0 → 2.1.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 (86) hide show
  1. data/CHANGELOG +34 -0
  2. data/README +0 -0
  3. data/Rakefile +6 -5
  4. data/lib/active_record.rb +8 -10
  5. data/lib/active_record/association_preload.rb +17 -12
  6. data/lib/active_record/associations.rb +45 -27
  7. data/lib/active_record/associations/association_collection.rb +8 -5
  8. data/lib/active_record/associations/association_proxy.rb +2 -6
  9. data/lib/active_record/associations/belongs_to_association.rb +0 -0
  10. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -0
  11. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +2 -3
  12. data/lib/active_record/associations/has_many_association.rb +16 -4
  13. data/lib/active_record/associations/has_many_through_association.rb +1 -1
  14. data/lib/active_record/associations/has_one_association.rb +2 -2
  15. data/lib/active_record/associations/has_one_through_association.rb +4 -0
  16. data/lib/active_record/base.rb +33 -15
  17. data/lib/active_record/calculations.rb +20 -7
  18. data/lib/active_record/callbacks.rb +0 -0
  19. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +9 -6
  20. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +17 -10
  21. data/lib/active_record/connection_adapters/abstract_adapter.rb +0 -0
  22. data/lib/active_record/connection_adapters/mysql_adapter.rb +53 -24
  23. data/lib/active_record/connection_adapters/postgresql_adapter.rb +66 -20
  24. data/lib/active_record/connection_adapters/sqlite_adapter.rb +12 -0
  25. data/lib/active_record/dirty.rb +10 -3
  26. data/lib/active_record/fixtures.rb +0 -0
  27. data/lib/active_record/locking/optimistic.rb +1 -0
  28. data/lib/active_record/migration.rb +35 -8
  29. data/lib/active_record/named_scope.rb +6 -1
  30. data/lib/active_record/observer.rb +7 -5
  31. data/lib/active_record/test_case.rb +13 -2
  32. data/lib/active_record/validations.rb +19 -9
  33. data/lib/active_record/version.rb +1 -1
  34. data/test/cases/active_schema_test_postgresql.rb +2 -2
  35. data/test/cases/adapter_test.rb +1 -1
  36. data/test/cases/associations/belongs_to_associations_test.rb +19 -0
  37. data/test/cases/associations/cascaded_eager_loading_test.rb +13 -1
  38. data/test/cases/associations/eager_load_includes_full_sti_class_test.rb +36 -0
  39. data/test/cases/associations/eager_test.rb +25 -1
  40. data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +27 -5
  41. data/test/cases/associations/has_many_associations_test.rb +106 -4
  42. data/test/cases/associations/has_many_through_associations_test.rb +10 -0
  43. data/test/cases/associations/has_one_associations_test.rb +22 -0
  44. data/test/cases/associations/has_one_through_associations_test.rb +44 -5
  45. data/test/cases/associations/join_model_test.rb +7 -0
  46. data/test/cases/associations_test.rb +2 -2
  47. data/test/cases/attribute_methods_test.rb +10 -10
  48. data/test/cases/base_test.rb +39 -16
  49. data/test/cases/calculations_test.rb +53 -1
  50. data/test/cases/column_definition_test.rb +36 -0
  51. data/test/cases/database_statements_test.rb +12 -0
  52. data/test/cases/defaults_test.rb +1 -1
  53. data/test/cases/deprecated_finder_test.rb +0 -0
  54. data/test/cases/dirty_test.rb +94 -0
  55. data/test/cases/finder_test.rb +7 -0
  56. data/test/cases/fixtures_test.rb +0 -0
  57. data/test/cases/helper.rb +5 -5
  58. data/test/cases/inheritance_test.rb +9 -2
  59. data/test/cases/lifecycle_test.rb +54 -1
  60. data/test/cases/locking_test.rb +20 -0
  61. data/test/cases/method_scoping_test.rb +11 -1
  62. data/test/cases/migration_test.rb +147 -22
  63. data/test/cases/multiple_db_test.rb +1 -1
  64. data/test/cases/named_scope_test.rb +50 -1
  65. data/test/cases/query_cache_test.rb +4 -3
  66. data/test/cases/readonly_test.rb +0 -0
  67. data/test/cases/reflection_test.rb +3 -3
  68. data/test/cases/schema_dumper_test.rb +46 -0
  69. data/test/cases/unconnected_test.rb +0 -0
  70. data/test/cases/validations_test.rb +30 -5
  71. data/test/debug.log +358 -0
  72. data/test/fixtures/fixture_database.sqlite3 +0 -0
  73. data/test/fixtures/fixture_database_2.sqlite3 +0 -0
  74. data/test/models/author.rb +4 -0
  75. data/test/models/category.rb +1 -0
  76. data/test/models/company.rb +10 -1
  77. data/test/models/developer.rb +4 -1
  78. data/test/models/person.rb +1 -1
  79. data/test/models/post.rb +6 -1
  80. data/test/models/project.rb +1 -1
  81. data/test/models/reply.rb +0 -0
  82. data/test/models/topic.rb +1 -0
  83. data/test/schema/mysql_specific_schema.rb +2 -2
  84. data/test/schema/schema.rb +8 -0
  85. metadata +11 -5
  86. data/lib/active_record/vendor/db2.rb +0 -362
@@ -69,8 +69,8 @@ module ActiveRecord
69
69
  @target
70
70
  end
71
71
 
72
- def respond_to?(symbol, include_priv = false)
73
- proxy_respond_to?(symbol, include_priv) || (load_target && @target.respond_to?(symbol, include_priv))
72
+ def respond_to?(*args)
73
+ proxy_respond_to?(*args) || (load_target && @target.respond_to?(*args))
74
74
  end
75
75
 
76
76
  # Explicitly proxy === because the instance method removal above
@@ -131,10 +131,6 @@ module ActiveRecord
131
131
  records.map { |record| record.quoted_id }.join(',')
132
132
  end
133
133
 
134
- def interpolate_sql_options!(options, *keys)
135
- keys.each { |key| options[key] &&= interpolate_sql(options[key]) }
136
- end
137
-
138
134
  def interpolate_sql(sql, record = nil)
139
135
  @owner.send(:interpolate_sql, sql, record)
140
136
  end
@@ -70,10 +70,8 @@ module ActiveRecord
70
70
  end
71
71
 
72
72
  def construct_sql
73
- interpolate_sql_options!(@reflection.options, :finder_sql)
74
-
75
73
  if @reflection.options[:finder_sql]
76
- @finder_sql = @reflection.options[:finder_sql]
74
+ @finder_sql = interpolate_sql(@reflection.options[:finder_sql])
77
75
  else
78
76
  @finder_sql = "#{@owner.connection.quote_table_name @reflection.options[:join_table]}.#{@reflection.primary_key_name} = #{@owner.quoted_id} "
79
77
  @finder_sql << " AND (#{conditions})" if conditions
@@ -87,6 +85,7 @@ module ActiveRecord
87
85
  :joins => @join_sql,
88
86
  :readonly => false,
89
87
  :order => @reflection.options[:order],
88
+ :include => @reflection.options[:include],
90
89
  :limit => @reflection.options[:limit] } }
91
90
  end
92
91
 
@@ -14,7 +14,16 @@ module ActiveRecord
14
14
  @finder_sql + " AND (#{sanitize_sql(options[:conditions])})"
15
15
  options[:include] ||= @reflection.options[:include]
16
16
 
17
- @reflection.klass.count(column_name, options)
17
+ value = @reflection.klass.count(column_name, options)
18
+
19
+ limit = @reflection.options[:limit]
20
+ offset = @reflection.options[:offset]
21
+
22
+ if limit || offset
23
+ [ [value - offset.to_i, 0].max, limit.to_i ].min
24
+ else
25
+ value
26
+ end
18
27
  end
19
28
  end
20
29
 
@@ -27,8 +36,11 @@ module ActiveRecord
27
36
  else
28
37
  @reflection.klass.count(:conditions => @counter_sql, :include => @reflection.options[:include])
29
38
  end
30
-
31
- @target = [] and loaded if count == 0
39
+
40
+ # If there's nothing in the database and @target has no new records
41
+ # we are certain the current target is an empty array. This is a
42
+ # documented side-effect of the method that may avoid an extra SELECT.
43
+ @target ||= [] and loaded if count == 0
32
44
 
33
45
  if @reflection.options[:limit]
34
46
  count = [ @reflection.options[:limit], count ].min
@@ -100,7 +112,7 @@ module ActiveRecord
100
112
  create_scoping = {}
101
113
  set_belongs_to_association_for(create_scoping)
102
114
  {
103
- :find => { :conditions => @finder_sql, :readonly => false, :order => @reflection.options[:order], :limit => @reflection.options[:limit] },
115
+ :find => { :conditions => @finder_sql, :readonly => false, :order => @reflection.options[:order], :limit => @reflection.options[:limit], :include => @reflection.options[:include]},
104
116
  :create => create_scoping
105
117
  }
106
118
  end
@@ -237,7 +237,7 @@ module ActiveRecord
237
237
  end
238
238
 
239
239
  def build_sti_condition
240
- "#{@reflection.through_reflection.quoted_table_name}.#{@reflection.through_reflection.klass.inheritance_column} = #{@reflection.klass.quote_value(@reflection.through_reflection.klass.sti_name)}"
240
+ @reflection.through_reflection.klass.send(:type_condition)
241
241
  end
242
242
 
243
243
  alias_method :sql_conditions, :conditions
@@ -21,8 +21,8 @@ module ActiveRecord
21
21
  def replace(obj, dont_save = false)
22
22
  load_target
23
23
 
24
- unless @target.nil?
25
- if dependent? && !dont_save && @target != obj
24
+ unless @target.nil? || @target == obj
25
+ if dependent? && !dont_save
26
26
  @target.destroy unless @target.new_record?
27
27
  @owner.clear_association_cache
28
28
  else
@@ -22,6 +22,10 @@ module ActiveRecord
22
22
 
23
23
  def find_target
24
24
  super.first
25
+ end
26
+
27
+ def reset_target!
28
+ @target = nil
25
29
  end
26
30
  end
27
31
  end
@@ -372,7 +372,7 @@ module ActiveRecord #:nodoc:
372
372
  def self.reset_subclasses #:nodoc:
373
373
  nonreloadables = []
374
374
  subclasses.each do |klass|
375
- unless Dependencies.autoloaded? klass
375
+ unless ActiveSupport::Dependencies.autoloaded? klass
376
376
  nonreloadables << klass
377
377
  next
378
378
  end
@@ -439,6 +439,10 @@ module ActiveRecord #:nodoc:
439
439
  cattr_accessor :schema_format , :instance_writer => false
440
440
  @@schema_format = :ruby
441
441
 
442
+ # Specify whether or not to use timestamps for migration numbers
443
+ cattr_accessor :timestamped_migrations , :instance_writer => false
444
+ @@timestamped_migrations = true
445
+
442
446
  # Determine whether to store the full constant name including namespace when using STI
443
447
  superclass_delegating_accessor :store_full_sti_class
444
448
  self.store_full_sti_class = false
@@ -828,7 +832,7 @@ module ActiveRecord #:nodoc:
828
832
  def update_counters(id, counters)
829
833
  updates = counters.inject([]) { |list, (counter_name, increment)|
830
834
  sign = increment < 0 ? "-" : "+"
831
- list << "#{connection.quote_column_name(counter_name)} = #{connection.quote_column_name(counter_name)} #{sign} #{increment.abs}"
835
+ list << "#{connection.quote_column_name(counter_name)} = COALESCE(#{connection.quote_column_name(counter_name)}, 0) #{sign} #{increment.abs}"
832
836
  }.join(", ")
833
837
  update_all(updates, "#{connection.quote_column_name(primary_key)} = #{quote_value(id)}")
834
838
  end
@@ -1465,7 +1469,7 @@ module ActiveRecord #:nodoc:
1465
1469
 
1466
1470
  def construct_finder_sql(options)
1467
1471
  scope = scope(:find)
1468
- sql = "SELECT #{options[:select] || (scope && scope[:select]) || (options[:joins] && quoted_table_name + '.*') || '*'} "
1472
+ sql = "SELECT #{options[:select] || (scope && scope[:select]) || ((options[:joins] || (scope && scope[:joins])) && quoted_table_name + '.*') || '*'} "
1469
1473
  sql << "FROM #{(scope && scope[:from]) || options[:from] || quoted_table_name} "
1470
1474
 
1471
1475
  add_joins!(sql, options, scope)
@@ -1577,10 +1581,11 @@ module ActiveRecord #:nodoc:
1577
1581
  sql << "WHERE #{merged_conditions} " unless merged_conditions.blank?
1578
1582
  end
1579
1583
 
1580
- def type_condition
1584
+ def type_condition(table_alias=nil)
1585
+ quoted_table_alias = self.connection.quote_table_name(table_alias || table_name)
1581
1586
  quoted_inheritance_column = connection.quote_column_name(inheritance_column)
1582
- type_condition = subclasses.inject("#{quoted_table_name}.#{quoted_inheritance_column} = '#{sti_name}' ") do |condition, subclass|
1583
- condition << "OR #{quoted_table_name}.#{quoted_inheritance_column} = '#{subclass.sti_name}' "
1587
+ type_condition = subclasses.inject("#{quoted_table_alias}.#{quoted_inheritance_column} = '#{sti_name}' ") do |condition, subclass|
1588
+ condition << "OR #{quoted_table_alias}.#{quoted_inheritance_column} = '#{subclass.sti_name}' "
1584
1589
  end
1585
1590
 
1586
1591
  " (#{type_condition}) "
@@ -1717,7 +1722,7 @@ module ActiveRecord #:nodoc:
1717
1722
  def attribute_condition(argument)
1718
1723
  case argument
1719
1724
  when nil then "IS ?"
1720
- when Array, ActiveRecord::Associations::AssociationCollection then "IN (?)"
1725
+ when Array, ActiveRecord::Associations::AssociationCollection, ActiveRecord::NamedScope::Scope then "IN (?)"
1721
1726
  when Range then "BETWEEN ? AND ?"
1722
1727
  else "= ?"
1723
1728
  end
@@ -2053,9 +2058,10 @@ module ActiveRecord #:nodoc:
2053
2058
  end
2054
2059
 
2055
2060
  def replace_named_bind_variables(statement, bind_vars) #:nodoc:
2056
- statement.gsub(/:([a-zA-Z]\w*)/) do
2057
- match = $1.to_sym
2058
- if bind_vars.include?(match)
2061
+ statement.gsub(/(:?):([a-zA-Z]\w*)/) do
2062
+ if $1 == ':' # skip postgresql casts
2063
+ $& # return the whole match
2064
+ elsif bind_vars.include?(match = $2.to_sym)
2059
2065
  quote_bound_value(bind_vars[match])
2060
2066
  else
2061
2067
  raise PreparedStatementInvalid, "missing value for :#{match} in #{statement}"
@@ -2064,13 +2070,18 @@ module ActiveRecord #:nodoc:
2064
2070
  end
2065
2071
 
2066
2072
  def expand_range_bind_variables(bind_vars) #:nodoc:
2067
- bind_vars.sum do |var|
2073
+ expanded = []
2074
+
2075
+ bind_vars.each do |var|
2068
2076
  if var.is_a?(Range)
2069
- [var.first, var.last]
2077
+ expanded << var.first
2078
+ expanded << var.last
2070
2079
  else
2071
- [var]
2080
+ expanded << var
2072
2081
  end
2073
2082
  end
2083
+
2084
+ expanded
2074
2085
  end
2075
2086
 
2076
2087
  def quote_bound_value(value) #:nodoc:
@@ -2572,8 +2583,15 @@ module ActiveRecord #:nodoc:
2572
2583
  quoted = {}
2573
2584
  connection = self.class.connection
2574
2585
  attribute_names.each do |name|
2575
- if column = column_for_attribute(name)
2576
- quoted[name] = connection.quote(read_attribute(name), column) unless !include_primary_key && column.primary
2586
+ if (column = column_for_attribute(name)) && (include_primary_key || !column.primary)
2587
+ value = read_attribute(name)
2588
+
2589
+ # We need explicit to_yaml because quote() does not properly convert Time/Date fields to YAML.
2590
+ if value && self.class.serialized_attributes.has_key?(name) && (value.acts_like?(:date) || value.acts_like?(:time))
2591
+ value = value.to_yaml
2592
+ end
2593
+
2594
+ quoted[name] = connection.quote(value, column)
2577
2595
  end
2578
2596
  end
2579
2597
  include_readonly_attributes ? quoted : remove_readonly_attributes(quoted)
@@ -1,6 +1,6 @@
1
1
  module ActiveRecord
2
2
  module Calculations #:nodoc:
3
- CALCULATIONS_OPTIONS = [:conditions, :joins, :order, :select, :group, :having, :distinct, :limit, :offset, :include]
3
+ CALCULATIONS_OPTIONS = [:conditions, :joins, :order, :select, :group, :having, :distinct, :limit, :offset, :include, :from]
4
4
  def self.included(base)
5
5
  base.extend(ClassMethods)
6
6
  end
@@ -27,6 +27,8 @@ module ActiveRecord
27
27
  # * <tt>:select</tt>: By default, this is * as in SELECT * FROM, but can be changed if you, for example, want to do a join but not
28
28
  # include the joined columns.
29
29
  # * <tt>:distinct</tt>: Set this to true to make this a distinct calculation, such as SELECT COUNT(DISTINCT posts.id) ...
30
+ # * <tt>:from</tt> - By default, this is the table name of the class, but can be changed to an alternate table name (or even the name
31
+ # of a database view).
30
32
  #
31
33
  # Examples for counting all:
32
34
  # Person.count # returns the total count of all people
@@ -71,7 +73,7 @@ module ActiveRecord
71
73
  #
72
74
  # Person.sum('age')
73
75
  def sum(column_name, options = {})
74
- calculate(:sum, column_name, options) || 0
76
+ calculate(:sum, column_name, options)
75
77
  end
76
78
 
77
79
  # This calculates aggregate values in the given column. Methods for count, sum, average, minimum, and maximum have been added as shortcuts.
@@ -178,13 +180,23 @@ module ActiveRecord
178
180
  sql = "SELECT COUNT(*) AS #{aggregate_alias}" if use_workaround
179
181
 
180
182
  sql << ", #{options[:group_field]} AS #{options[:group_alias]}" if options[:group]
181
- sql << " FROM (SELECT #{distinct}#{column_name}" if use_workaround
182
- sql << " FROM #{connection.quote_table_name(table_name)} "
183
+ if options[:from]
184
+ sql << " FROM #{options[:from]} "
185
+ else
186
+ sql << " FROM (SELECT #{distinct}#{column_name}" if use_workaround
187
+ sql << " FROM #{connection.quote_table_name(table_name)} "
188
+ end
189
+
190
+ joins = ""
191
+ add_joins!(joins, options, scope)
192
+
183
193
  if merged_includes.any?
184
- join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(self, merged_includes, options[:joins])
194
+ join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(self, merged_includes, joins)
185
195
  sql << join_dependency.join_associations.collect{|join| join.association_join }.join
186
196
  end
187
- add_joins!(sql, options, scope)
197
+
198
+ sql << joins unless joins.blank?
199
+
188
200
  add_conditions!(sql, options[:conditions], scope)
189
201
  add_limited_ids_condition!(sql, options, join_dependency) if join_dependency && !using_limitable_reflections?(join_dependency.reflections) && ((scope && scope[:limit]) || options[:limit])
190
202
 
@@ -205,7 +217,7 @@ module ActiveRecord
205
217
 
206
218
  sql << " ORDER BY #{options[:order]} " if options[:order]
207
219
  add_limit!(sql, options, scope)
208
- sql << ')' if use_workaround
220
+ sql << ') AS #{aggregate_alias}_subquery' if use_workaround
209
221
  sql
210
222
  end
211
223
 
@@ -266,6 +278,7 @@ module ActiveRecord
266
278
  operation = operation.to_s.downcase
267
279
  case operation
268
280
  when 'count' then value.to_i
281
+ when 'sum' then value =~ /\./ ? value.to_f : value.to_i
269
282
  when 'avg' then value && value.to_f
270
283
  else column ? column.type_cast(value) : value
271
284
  end
File without changes
@@ -257,7 +257,10 @@ module ActiveRecord
257
257
 
258
258
  def to_sql
259
259
  column_sql = "#{base.quote_column_name(name)} #{sql_type}"
260
- add_column_options!(column_sql, :null => null, :default => default) unless type.to_sym == :primary_key
260
+ column_options = {}
261
+ column_options[:null] = null unless null.nil?
262
+ column_options[:default] = default unless default.nil?
263
+ add_column_options!(column_sql, column_options) unless type.to_sym == :primary_key
261
264
  column_sql
262
265
  end
263
266
  alias to_s :to_sql
@@ -304,8 +307,7 @@ module ActiveRecord
304
307
  #
305
308
  # Available options are (none of these exists by default):
306
309
  # * <tt>:limit</tt> -
307
- # Requests a maximum column length (<tt>:string</tt>, <tt>:text</tt>,
308
- # <tt>:binary</tt> or <tt>:integer</tt> columns only)
310
+ # Requests a maximum column length. This is number of characters for <tt>:string</tt> and <tt>:text</tt> columns and number of bytes for :binary and :integer columns.
309
311
  # * <tt>:default</tt> -
310
312
  # The column's default value. Use nil for NULL.
311
313
  # * <tt>:null</tt> -
@@ -442,9 +444,10 @@ module ActiveRecord
442
444
 
443
445
  # Appends <tt>:datetime</tt> columns <tt>:created_at</tt> and
444
446
  # <tt>:updated_at</tt> to the table.
445
- def timestamps
446
- column(:created_at, :datetime)
447
- column(:updated_at, :datetime)
447
+ def timestamps(*args)
448
+ options = args.extract_options!
449
+ column(:created_at, :datetime, options)
450
+ column(:updated_at, :datetime, options)
448
451
  end
449
452
 
450
453
  def references(*args)
@@ -331,15 +331,26 @@ module ActiveRecord
331
331
  end
332
332
 
333
333
  def assume_migrated_upto_version(version)
334
+ version = version.to_i
334
335
  sm_table = quote_table_name(ActiveRecord::Migrator.schema_migrations_table_name)
336
+
335
337
  migrated = select_values("SELECT version FROM #{sm_table}").map(&:to_i)
336
338
  versions = Dir['db/migrate/[0-9]*_*.rb'].map do |filename|
337
339
  filename.split('/').last.split('_').first.to_i
338
340
  end
339
341
 
340
- execute "INSERT INTO #{sm_table} (version) VALUES ('#{version}')" unless migrated.include?(version.to_i)
341
- (versions - migrated).select { |v| v < version.to_i }.each do |v|
342
- execute "INSERT INTO #{sm_table} (version) VALUES ('#{v}')"
342
+ unless migrated.include?(version)
343
+ execute "INSERT INTO #{sm_table} (version) VALUES ('#{version}')"
344
+ end
345
+
346
+ inserted = Set.new
347
+ (versions - migrated).each do |v|
348
+ if inserted.include?(v)
349
+ raise "Duplicate migration #{v}. Please renumber your migrations to resolve the conflict."
350
+ elsif v < version
351
+ execute "INSERT INTO #{sm_table} (version) VALUES ('#{v}')"
352
+ inserted << v
353
+ end
343
354
  end
344
355
  end
345
356
 
@@ -372,13 +383,9 @@ module ActiveRecord
372
383
 
373
384
  def add_column_options!(sql, options) #:nodoc:
374
385
  sql << " DEFAULT #{quote(options[:default], options[:column])}" if options_include_default?(options)
375
- # must explcitly check for :null to allow change_column to work on migrations
376
- if options.has_key? :null
377
- if options[:null] == false
378
- sql << " NOT NULL"
379
- else
380
- sql << " NULL"
381
- end
386
+ # must explicitly check for :null to allow change_column to work on migrations
387
+ if options[:null] == false
388
+ sql << " NOT NULL"
382
389
  end
383
390
  end
384
391
 
@@ -50,10 +50,7 @@ module ActiveRecord
50
50
  rescue LoadError => cannot_require_mysql
51
51
  # Use the bundled Ruby/MySQL driver if no driver is already in place
52
52
  begin
53
- ActiveRecord::Base.logger.info(
54
- "WARNING: You're using the Ruby-based MySQL library that ships with Rails. This library is not suited for production. " +
55
- "Please install the C-based MySQL library instead (gem install mysql)."
56
- ) if ActiveRecord::Base.logger
53
+ ActiveSupport::Deprecation.warn "You're using the Ruby-based MySQL library that ships with Rails. This library will be REMOVED FROM RAILS 2.2. Please switch to the offical mysql gem: `gem install mysql`", caller
57
54
 
58
55
  require 'active_record/vendor/mysql'
59
56
  rescue LoadError
@@ -113,7 +110,8 @@ module ActiveRecord
113
110
  end
114
111
 
115
112
  def extract_limit(sql_type)
116
- if sql_type =~ /blob|text/i
113
+ case sql_type
114
+ when /blob|text/i
117
115
  case sql_type
118
116
  when /tiny/i
119
117
  255
@@ -124,6 +122,11 @@ module ActiveRecord
124
122
  else
125
123
  super # we could return 65535 here, but we leave it undecorated by default
126
124
  end
125
+ when /^bigint/i; 8
126
+ when /^int/i; 4
127
+ when /^mediumint/i; 3
128
+ when /^smallint/i; 2
129
+ when /^tinyint/i; 1
127
130
  else
128
131
  super
129
132
  end
@@ -193,10 +196,10 @@ module ActiveRecord
193
196
 
194
197
  def native_database_types #:nodoc:
195
198
  {
196
- :primary_key => "int(11) DEFAULT NULL auto_increment PRIMARY KEY",
199
+ :primary_key => "int(11) DEFAULT NULL auto_increment PRIMARY KEY".freeze,
197
200
  :string => { :name => "varchar", :limit => 255 },
198
201
  :text => { :name => "text" },
199
- :integer => { :name => "int"},
202
+ :integer => { :name => "int", :limit => 4 },
200
203
  :float => { :name => "float" },
201
204
  :decimal => { :name => "decimal" },
202
205
  :datetime => { :name => "datetime" },
@@ -336,10 +339,11 @@ module ActiveRecord
336
339
 
337
340
  def add_limit_offset!(sql, options) #:nodoc:
338
341
  if limit = options[:limit]
342
+ limit = sanitize_limit(limit)
339
343
  unless offset = options[:offset]
340
344
  sql << " LIMIT #{limit}"
341
345
  else
342
- sql << " LIMIT #{offset}, #{limit}"
346
+ sql << " LIMIT #{offset.to_i}, #{limit}"
343
347
  end
344
348
  end
345
349
  end
@@ -439,18 +443,29 @@ module ActiveRecord
439
443
  end
440
444
 
441
445
  def change_column_default(table_name, column_name, default) #:nodoc:
442
- current_type = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'")["Type"]
446
+ column = column_for(table_name, column_name)
447
+ change_column table_name, column_name, column.sql_type, :default => default
448
+ end
449
+
450
+ def change_column_null(table_name, column_name, null, default = nil)
451
+ column = column_for(table_name, column_name)
452
+
453
+ unless null || default.nil?
454
+ execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
455
+ end
443
456
 
444
- execute("ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_column_name(column_name)} #{quote_column_name(column_name)} #{current_type} DEFAULT #{quote(default)}")
457
+ change_column table_name, column_name, column.sql_type, :null => null
445
458
  end
446
459
 
447
460
  def change_column(table_name, column_name, type, options = {}) #:nodoc:
461
+ column = column_for(table_name, column_name)
462
+
448
463
  unless options_include_default?(options)
449
- if column = columns(table_name).find { |c| c.name == column_name.to_s }
450
- options[:default] = column.default
451
- else
452
- raise "No such column: #{table_name}.#{column_name}"
453
- end
464
+ options[:default] = column.default
465
+ end
466
+
467
+ unless options.has_key?(:null)
468
+ options[:null] = column.null
454
469
  end
455
470
 
456
471
  change_column_sql = "ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_column_name(column_name)} #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
@@ -459,8 +474,17 @@ module ActiveRecord
459
474
  end
460
475
 
461
476
  def rename_column(table_name, column_name, new_column_name) #:nodoc:
477
+ options = {}
478
+ if column = columns(table_name).find { |c| c.name == column_name.to_s }
479
+ options[:default] = column.default
480
+ options[:null] = column.null
481
+ else
482
+ raise ActiveRecordError, "No such column: #{table_name}.#{column_name}"
483
+ end
462
484
  current_type = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'")["Type"]
463
- execute "ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_column_name(column_name)} #{quote_column_name(new_column_name)} #{current_type}"
485
+ rename_column_sql = "ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_column_name(column_name)} #{quote_column_name(new_column_name)} #{current_type}"
486
+ add_column_options!(rename_column_sql, options)
487
+ execute(rename_column_sql)
464
488
  end
465
489
 
466
490
  # Maps logical Rails types to MySQL-specific data types.
@@ -468,14 +492,12 @@ module ActiveRecord
468
492
  return super unless type.to_s == 'integer'
469
493
 
470
494
  case limit
471
- when 0..3
472
- "smallint(#{limit})"
473
- when 4..8
474
- "int(#{limit})"
475
- when 9..20
476
- "bigint(#{limit})"
477
- else
478
- 'int(11)'
495
+ when 1; 'tinyint'
496
+ when 2; 'smallint'
497
+ when 3; 'mediumint'
498
+ when nil, 4, 11; 'int(11)' # compatibility with MySQL default
499
+ when 5..8; 'bigint'
500
+ else raise(ActiveRecordError, "No integer type has byte size #{limit}")
479
501
  end
480
502
  end
481
503
 
@@ -525,6 +547,13 @@ module ActiveRecord
525
547
  def version
526
548
  @version ||= @connection.server_info.scan(/^(\d+)\.(\d+)\.(\d+)/).flatten.map { |v| v.to_i }
527
549
  end
550
+
551
+ def column_for(table_name, column_name)
552
+ unless column = columns(table_name).find { |c| c.name == column_name.to_s }
553
+ raise "No such column: #{table_name}.#{column_name}"
554
+ end
555
+ column
556
+ end
528
557
  end
529
558
  end
530
559
  end