activerecord 4.2.0 → 4.2.1.rc1

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 (69) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +206 -1
  3. data/lib/active_record/associations.rb +4 -3
  4. data/lib/active_record/associations/belongs_to_association.rb +9 -5
  5. data/lib/active_record/associations/builder/collection_association.rb +5 -1
  6. data/lib/active_record/associations/collection_association.rb +17 -2
  7. data/lib/active_record/associations/collection_proxy.rb +5 -0
  8. data/lib/active_record/associations/foreign_association.rb +11 -0
  9. data/lib/active_record/associations/has_many_association.rb +22 -14
  10. data/lib/active_record/associations/has_many_through_association.rb +2 -2
  11. data/lib/active_record/associations/has_one_association.rb +1 -0
  12. data/lib/active_record/associations/through_association.rb +11 -0
  13. data/lib/active_record/attribute.rb +15 -1
  14. data/lib/active_record/attribute_methods/before_type_cast.rb +5 -0
  15. data/lib/active_record/attribute_methods/dirty.rb +7 -3
  16. data/lib/active_record/attribute_methods/time_zone_conversion.rb +5 -1
  17. data/lib/active_record/attribute_set/builder.rb +11 -1
  18. data/lib/active_record/attributes.rb +7 -0
  19. data/lib/active_record/autosave_association.rb +23 -8
  20. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +7 -5
  21. data/lib/active_record/connection_adapters/abstract/database_statements.rb +12 -1
  22. data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -1
  23. data/lib/active_record/connection_adapters/abstract/savepoints.rb +1 -1
  24. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +25 -7
  25. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +38 -6
  26. data/lib/active_record/connection_adapters/abstract/transaction.rb +1 -1
  27. data/lib/active_record/connection_adapters/abstract_adapter.rb +13 -6
  28. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +23 -1
  29. data/lib/active_record/connection_adapters/column.rb +1 -1
  30. data/lib/active_record/connection_adapters/mysql2_adapter.rb +1 -1
  31. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +1 -1
  32. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +1 -0
  33. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +9 -0
  34. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +1 -1
  35. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -0
  36. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +3 -3
  37. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +6 -7
  38. data/lib/active_record/connection_adapters/postgresql_adapter.rb +3 -3
  39. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +10 -16
  40. data/lib/active_record/connection_handling.rb +1 -1
  41. data/lib/active_record/core.rb +13 -7
  42. data/lib/active_record/counter_cache.rb +1 -1
  43. data/lib/active_record/fixtures.rb +1 -1
  44. data/lib/active_record/gem_version.rb +2 -2
  45. data/lib/active_record/locking/optimistic.rb +16 -14
  46. data/lib/active_record/migration.rb +1 -1
  47. data/lib/active_record/nested_attributes.rb +1 -1
  48. data/lib/active_record/no_touching.rb +1 -1
  49. data/lib/active_record/persistence.rb +2 -1
  50. data/lib/active_record/railties/databases.rake +2 -2
  51. data/lib/active_record/reflection.rb +1 -1
  52. data/lib/active_record/relation.rb +1 -1
  53. data/lib/active_record/relation/finder_methods.rb +1 -1
  54. data/lib/active_record/relation/predicate_builder.rb +15 -0
  55. data/lib/active_record/relation/predicate_builder/array_handler.rb +1 -1
  56. data/lib/active_record/relation/query_methods.rb +19 -18
  57. data/lib/active_record/schema_dumper.rb +1 -1
  58. data/lib/active_record/transactions.rb +6 -8
  59. data/lib/active_record/type/date_time.rb +14 -3
  60. data/lib/active_record/type/decimal.rb +9 -1
  61. data/lib/active_record/type/integer.rb +9 -5
  62. data/lib/active_record/type/numeric.rb +1 -1
  63. data/lib/active_record/type/serialized.rb +1 -1
  64. data/lib/active_record/type/string.rb +4 -0
  65. data/lib/active_record/type/value.rb +4 -0
  66. data/lib/active_record/validations/uniqueness.rb +1 -1
  67. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +0 -3
  68. data/lib/rails/generators/active_record/migration/templates/migration.rb +0 -6
  69. metadata +10 -9
@@ -192,7 +192,7 @@ module ActiveRecord
192
192
  ensure
193
193
  unless error
194
194
  if Thread.current.status == 'aborting'
195
- rollback_transaction
195
+ rollback_transaction if transaction
196
196
  else
197
197
  begin
198
198
  commit_transaction
@@ -341,9 +341,6 @@ module ActiveRecord
341
341
  def create_savepoint(name = nil)
342
342
  end
343
343
 
344
- def rollback_to_savepoint(name = nil)
345
- end
346
-
347
344
  def release_savepoint(name = nil)
348
345
  end
349
346
 
@@ -385,7 +382,7 @@ module ActiveRecord
385
382
  end
386
383
 
387
384
  def column_name_for_operation(operation, node) # :nodoc:
388
- node.to_sql
385
+ visitor.accept(node, collector).value
389
386
  end
390
387
 
391
388
  protected
@@ -445,11 +442,21 @@ module ActiveRecord
445
442
  end
446
443
 
447
444
  def extract_limit(sql_type) # :nodoc:
448
- $1.to_i if sql_type =~ /\((.*)\)/
445
+ case sql_type
446
+ when /^bigint/i
447
+ 8
448
+ when /\((.*)\)/
449
+ $1.to_i
450
+ end
449
451
  end
450
452
 
451
453
  def translate_exception_class(e, sql)
452
- message = "#{e.class.name}: #{e.message}: #{sql}"
454
+ begin
455
+ message = "#{e.class.name}: #{e.message}: #{sql}"
456
+ rescue Encoding::CompatibilityError
457
+ message = "#{e.class.name}: #{e.message.force_encoding sql.encoding}: #{sql}"
458
+ end
459
+
453
460
  @logger.error message if @logger
454
461
  exception = translate_exception(e, message)
455
462
  exception.set_backtrace e.backtrace
@@ -324,7 +324,7 @@ module ActiveRecord
324
324
  execute "COMMIT"
325
325
  end
326
326
 
327
- def rollback_db_transaction #:nodoc:
327
+ def exec_rollback_db_transaction #:nodoc:
328
328
  execute "ROLLBACK"
329
329
  end
330
330
 
@@ -492,6 +492,8 @@ module ActiveRecord
492
492
 
493
493
  def rename_index(table_name, old_name, new_name)
494
494
  if supports_rename_index?
495
+ validate_index_length!(table_name, new_name)
496
+
495
497
  execute "ALTER TABLE #{quote_table_name(table_name)} RENAME INDEX #{quote_table_name(old_name)} TO #{quote_table_name(new_name)}"
496
498
  else
497
499
  super
@@ -582,6 +584,13 @@ module ActiveRecord
582
584
  when 0x1000000..0xffffffff; 'longtext'
583
585
  else raise(ActiveRecordError, "No text type has character length #{limit}")
584
586
  end
587
+ when 'datetime'
588
+ return super unless precision
589
+
590
+ case precision
591
+ when 0..6; "datetime(#{precision})"
592
+ else raise(ActiveRecordError, "No datetime type has precision of #{precision}. The allowed range of precision is from 0 to 6.")
593
+ end
585
594
  else
586
595
  super
587
596
  end
@@ -670,6 +679,11 @@ module ActiveRecord
670
679
  m.alias_type %r(year)i, 'integer'
671
680
  m.alias_type %r(bit)i, 'binary'
672
681
 
682
+ m.register_type(%r(datetime)i) do |sql_type|
683
+ precision = extract_precision(sql_type)
684
+ MysqlDateTime.new(precision: precision)
685
+ end
686
+
673
687
  m.register_type(%r(enum)i) do |sql_type|
674
688
  limit = sql_type[/^enum\((.+)\)/i, 1]
675
689
  .split(',').map{|enum| enum.strip.length - 2}.max
@@ -859,6 +873,14 @@ module ActiveRecord
859
873
  end
860
874
  end
861
875
 
876
+ class MysqlDateTime < Type::DateTime # :nodoc:
877
+ private
878
+
879
+ def has_precision?
880
+ precision || 0
881
+ end
882
+ end
883
+
862
884
  class MysqlString < Type::String # :nodoc:
863
885
  def type_cast_for_database(value)
864
886
  case value
@@ -16,7 +16,7 @@ module ActiveRecord
16
16
  attr_reader :name, :cast_type, :null, :sql_type, :default, :default_function
17
17
 
18
18
  delegate :type, :precision, :scale, :limit, :klass, :accessor,
19
- :number?, :binary?, :changed?,
19
+ :text?, :number?, :binary?, :changed?,
20
20
  :type_cast_from_user, :type_cast_from_database, :type_cast_for_database,
21
21
  :type_cast_for_schema,
22
22
  to: :cast_type
@@ -39,7 +39,7 @@ module ActiveRecord
39
39
 
40
40
  MAX_INDEX_LENGTH_FOR_UTF8MB4 = 191
41
41
  def initialize_schema_migrations_table
42
- if @config[:encoding] == 'utf8mb4'
42
+ if charset == 'utf8mb4'
43
43
  ActiveRecord::SchemaMigration.create_table(MAX_INDEX_LENGTH_FOR_UTF8MB4)
44
44
  else
45
45
  ActiveRecord::SchemaMigration.create_table
@@ -223,7 +223,7 @@ module ActiveRecord
223
223
  end
224
224
 
225
225
  # Aborts a transaction.
226
- def rollback_db_transaction
226
+ def exec_rollback_db_transaction
227
227
  execute "ROLLBACK"
228
228
  end
229
229
  end
@@ -5,6 +5,7 @@ module ActiveRecord
5
5
  class Bytea < Type::Binary # :nodoc:
6
6
  def type_cast_from_database(value)
7
7
  return if value.nil?
8
+ return value.to_s if value.is_a?(Type::Binary::Data)
8
9
  PGconn.unescape_bytea(super)
9
10
  end
10
11
  end
@@ -5,6 +5,15 @@ module ActiveRecord
5
5
  class DateTime < Type::DateTime # :nodoc:
6
6
  include Infinity
7
7
 
8
+ def type_cast_for_database(value)
9
+ if has_precision? && value.acts_like?(:time) && value.year <= 0
10
+ bce_year = format("%04d", -value.year + 1)
11
+ super.sub(/^-?\d+/, bce_year) + " BC"
12
+ else
13
+ super
14
+ end
15
+ end
16
+
8
17
  def cast_value(value)
9
18
  if value.is_a?(::String)
10
19
  case value
@@ -11,7 +11,7 @@ module ActiveRecord
11
11
 
12
12
  def type_cast_from_database(value)
13
13
  if value.is_a?(::String)
14
- ::ActiveSupport::JSON.decode(value)
14
+ ::ActiveSupport::JSON.decode(value) rescue nil
15
15
  else
16
16
  super
17
17
  end
@@ -8,6 +8,10 @@ module ActiveRecord
8
8
  def initialize(type)
9
9
  @type = type
10
10
  end
11
+
12
+ def text?
13
+ false
14
+ end
11
15
  end
12
16
  end
13
17
  end
@@ -76,15 +76,15 @@ module ActiveRecord
76
76
  column(name, :point, options)
77
77
  end
78
78
 
79
- def bit(name, options)
79
+ def bit(name, options = {})
80
80
  column(name, :bit, options)
81
81
  end
82
82
 
83
- def bit_varying(name, options)
83
+ def bit_varying(name, options = {})
84
84
  column(name, :bit_varying, options)
85
85
  end
86
86
 
87
- def money(name, options)
87
+ def money(name, options = {})
88
88
  column(name, :money, options)
89
89
  end
90
90
  end
@@ -386,15 +386,15 @@ module ActiveRecord
386
386
 
387
387
  # Returns just a table's primary key
388
388
  def primary_key(table)
389
- row = exec_query(<<-end_sql, 'SCHEMA').rows.first
389
+ pks = exec_query(<<-end_sql, 'SCHEMA').rows
390
390
  SELECT attr.attname
391
391
  FROM pg_attribute attr
392
- INNER JOIN pg_constraint cons ON attr.attrelid = cons.conrelid AND attr.attnum = cons.conkey[1]
392
+ INNER JOIN pg_constraint cons ON attr.attrelid = cons.conrelid AND attr.attnum = any(cons.conkey)
393
393
  WHERE cons.contype = 'p'
394
394
  AND cons.conrelid = '#{quote_table_name(table)}'::regclass
395
395
  end_sql
396
-
397
- row && row.first
396
+ return nil unless pks.count == 1
397
+ pks[0][0]
398
398
  end
399
399
 
400
400
  # Renames a table.
@@ -484,9 +484,8 @@ module ActiveRecord
484
484
  end
485
485
 
486
486
  def rename_index(table_name, old_name, new_name)
487
- if new_name.length > allowed_index_name_length
488
- raise ArgumentError, "Index name '#{new_name}' on table '#{table_name}' is too long; the limit is #{allowed_index_name_length} characters"
489
- end
487
+ validate_index_length!(table_name, new_name)
488
+
490
489
  execute "ALTER INDEX #{quote_column_name(old_name)} RENAME TO #{quote_table_name(new_name)}"
491
490
  end
492
491
 
@@ -64,7 +64,7 @@ module ActiveRecord
64
64
  # <tt>SET client_min_messages TO <min_messages></tt> call on the connection.
65
65
  # * <tt>:variables</tt> - An optional hash of additional parameters that
66
66
  # will be used in <tt>SET SESSION key = val</tt> calls on the connection.
67
- # * <tt>:insert_returning</tt> - An optional boolean to control the use or <tt>RETURNING</tt> for <tt>INSERT</tt> statements
67
+ # * <tt>:insert_returning</tt> - An optional boolean to control the use of <tt>RETURNING</tt> for <tt>INSERT</tt> statements
68
68
  # defaults to true.
69
69
  #
70
70
  # Any further options are used as connection parameters to libpq. See
@@ -445,8 +445,8 @@ module ActiveRecord
445
445
 
446
446
  def initialize_type_map(m) # :nodoc:
447
447
  register_class_with_limit m, 'int2', OID::Integer
448
- m.alias_type 'int4', 'int2'
449
- m.alias_type 'int8', 'int2'
448
+ register_class_with_limit m, 'int4', OID::Integer
449
+ register_class_with_limit m, 'int8', OID::Integer
450
450
  m.alias_type 'oid', 'int2'
451
451
  m.register_type 'float4', OID::Float.new
452
452
  m.alias_type 'float8', 'float4'
@@ -50,16 +50,6 @@ module ActiveRecord
50
50
  end
51
51
  end
52
52
 
53
- class SQLite3String < Type::String # :nodoc:
54
- def type_cast_for_database(value)
55
- if value.is_a?(::String) && value.encoding == Encoding::ASCII_8BIT
56
- value.encode(Encoding::UTF_8)
57
- else
58
- super
59
- end
60
- end
61
- end
62
-
63
53
  # The SQLite3 adapter works SQLite 3.6.16 or newer
64
54
  # with the sqlite3-ruby drivers (available as gem from https://rubygems.org/gems/sqlite3).
65
55
  #
@@ -239,6 +229,12 @@ module ActiveRecord
239
229
  case value
240
230
  when BigDecimal
241
231
  value.to_f
232
+ when String
233
+ if value.encoding == Encoding::ASCII_8BIT
234
+ super(value.encode(Encoding::UTF_8))
235
+ else
236
+ super
237
+ end
242
238
  else
243
239
  super
244
240
  end
@@ -361,7 +357,7 @@ module ActiveRecord
361
357
  log('commit transaction',nil) { @connection.commit }
362
358
  end
363
359
 
364
- def rollback_db_transaction #:nodoc:
360
+ def exec_rollback_db_transaction #:nodoc:
365
361
  log('rollback transaction',nil) { @connection.rollback }
366
362
  end
367
363
 
@@ -428,10 +424,9 @@ module ActiveRecord
428
424
  end
429
425
 
430
426
  def primary_key(table_name) #:nodoc:
431
- column = table_structure(table_name).find { |field|
432
- field['pk'] == 1
433
- }
434
- column && column['name']
427
+ pks = table_structure(table_name).select { |f| f['pk'] > 0 }
428
+ return nil unless pks.count == 1
429
+ pks[0]['name']
435
430
  end
436
431
 
437
432
  def remove_index!(table_name, index_name) #:nodoc:
@@ -509,7 +504,6 @@ module ActiveRecord
509
504
  def initialize_type_map(m)
510
505
  super
511
506
  m.register_type(/binary/i, SQLite3Binary.new)
512
- register_class_with_limit m, %r(char)i, SQLite3String
513
507
  end
514
508
 
515
509
  def table_structure(table_name)
@@ -1,6 +1,6 @@
1
1
  module ActiveRecord
2
2
  module ConnectionHandling
3
- RAILS_ENV = -> { (Rails.env if defined?(Rails)) || ENV["RAILS_ENV"] || ENV["RACK_ENV"] }
3
+ RAILS_ENV = -> { (Rails.env if defined?(Rails.env)) || ENV["RAILS_ENV"] || ENV["RACK_ENV"] }
4
4
  DEFAULT_ENV = -> { RAILS_ENV.call || "default_env" }
5
5
 
6
6
  # Establishes the connection to the database. Accepts a hash as input where
@@ -114,16 +114,16 @@ module ActiveRecord
114
114
  super
115
115
  end
116
116
 
117
- def initialize_find_by_cache
117
+ def initialize_find_by_cache # :nodoc:
118
118
  self.find_by_statement_cache = {}.extend(Mutex_m)
119
119
  end
120
120
 
121
- def inherited(child_class)
121
+ def inherited(child_class) # :nodoc:
122
122
  child_class.initialize_find_by_cache
123
123
  super
124
124
  end
125
125
 
126
- def find(*ids)
126
+ def find(*ids) # :nodoc:
127
127
  # We don't have cache keys for this stuff yet
128
128
  return super unless ids.length == 1
129
129
  # Allow symbols to super to maintain compatibility for deprecated finders until Rails 5
@@ -131,6 +131,7 @@ module ActiveRecord
131
131
  return super if block_given? ||
132
132
  primary_key.nil? ||
133
133
  default_scopes.any? ||
134
+ current_scope ||
134
135
  columns_hash.include?(inheritance_column) ||
135
136
  ids.first.kind_of?(Array)
136
137
 
@@ -158,7 +159,7 @@ module ActiveRecord
158
159
  raise RecordNotFound, "Couldn't find #{name} with an out of range value for '#{primary_key}'"
159
160
  end
160
161
 
161
- def find_by(*args)
162
+ def find_by(*args) # :nodoc:
162
163
  return super if current_scope || !(Hash === args.first) || reflect_on_all_aggregations.any?
163
164
  return super if default_scopes.any?
164
165
 
@@ -191,11 +192,11 @@ module ActiveRecord
191
192
  end
192
193
  end
193
194
 
194
- def find_by!(*args)
195
+ def find_by!(*args) # :nodoc:
195
196
  find_by(*args) or raise RecordNotFound.new("Couldn't find #{name}")
196
197
  end
197
198
 
198
- def initialize_generated_modules
199
+ def initialize_generated_modules # :nodoc:
199
200
  generated_association_methods
200
201
  end
201
202
 
@@ -270,11 +271,11 @@ module ActiveRecord
270
271
  # User.new(first_name: 'Jamie')
271
272
  def initialize(attributes = nil, options = {})
272
273
  @attributes = self.class._default_attributes.dup
274
+ self.class.define_attribute_methods
273
275
 
274
276
  init_internals
275
277
  initialize_internals_callback
276
278
 
277
- self.class.define_attribute_methods
278
279
  # +options+ argument is only needed to make protected_attributes gem easier to hook.
279
280
  # Remove it when we drop support to this gem.
280
281
  init_attributes(attributes, options) if attributes
@@ -452,6 +453,7 @@ module ActiveRecord
452
453
  # Takes a PP and prettily prints this record to it, allowing you to get a nice result from `pp record`
453
454
  # when pp is required.
454
455
  def pretty_print(pp)
456
+ return super if custom_inspect_method_defined?
455
457
  pp.object_address_group(self) do
456
458
  if defined?(@attributes) && @attributes
457
459
  column_names = self.class.column_names.select { |name| has_attribute?(name) || new_record? }
@@ -562,5 +564,9 @@ module ActiveRecord
562
564
  @attributes = @attributes.dup
563
565
  end
564
566
  end
567
+
568
+ def custom_inspect_method_defined?
569
+ self.class.instance_method(:inspect).owner != ActiveRecord::Base.instance_method(:inspect).owner
570
+ end
565
571
  end
566
572
  end
@@ -167,7 +167,7 @@ module ActiveRecord
167
167
 
168
168
  def each_counter_cached_associations
169
169
  _reflections.each do |name, reflection|
170
- yield association(name) if reflection.belongs_to? && reflection.counter_cache_column
170
+ yield association(name.to_sym) if reflection.belongs_to? && reflection.counter_cache_column
171
171
  end
172
172
  end
173
173
 
@@ -633,7 +633,7 @@ module ActiveRecord
633
633
 
634
634
  # interpolate the fixture label
635
635
  row.each do |key, value|
636
- row[key] = value.gsub("$LABEL", label) if value.is_a?(String)
636
+ row[key] = value.gsub("$LABEL", label.to_s) if value.is_a?(String)
637
637
  end
638
638
 
639
639
  # generate a primary key if necessary
@@ -7,8 +7,8 @@ module ActiveRecord
7
7
  module VERSION
8
8
  MAJOR = 4
9
9
  MINOR = 2
10
- TINY = 0
11
- PRE = nil
10
+ TINY = 1
11
+ PRE = "rc1"
12
12
 
13
13
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
14
14
  end
@@ -66,6 +66,15 @@ module ActiveRecord
66
66
  send(lock_col + '=', previous_lock_value + 1)
67
67
  end
68
68
 
69
+ def _create_record(attribute_names = self.attribute_names, *) # :nodoc:
70
+ if locking_enabled?
71
+ # We always want to persist the locking version, even if we don't detect
72
+ # a change from the default, since the database might have no default
73
+ attribute_names |= [self.class.locking_column]
74
+ end
75
+ super
76
+ end
77
+
69
78
  def _update_record(attribute_names = self.attribute_names) #:nodoc:
70
79
  return super unless locking_enabled?
71
80
  return 0 if attribute_names.empty?
@@ -80,17 +89,15 @@ module ActiveRecord
80
89
  begin
81
90
  relation = self.class.unscoped
82
91
 
83
- stmt = relation.where(
84
- relation.table[self.class.primary_key].eq(id).and(
85
- relation.table[lock_col].eq(self.class.quote_value(previous_lock_value, column_for_attribute(lock_col)))
86
- )
87
- ).arel.compile_update(
88
- arel_attributes_with_values_for_update(attribute_names),
89
- self.class.primary_key
92
+ affected_rows = relation.where(
93
+ self.class.primary_key => id,
94
+ lock_col => previous_lock_value,
95
+ ).update_all(
96
+ Hash[attribute_names.map do |name|
97
+ [name, _read_attribute(name)]
98
+ end]
90
99
  )
91
100
 
92
- affected_rows = self.class.connection.update stmt
93
-
94
101
  unless affected_rows == 1
95
102
  raise ActiveRecord::StaleObjectError.new(self, "update")
96
103
  end
@@ -187,11 +194,6 @@ module ActiveRecord
187
194
  super.to_i
188
195
  end
189
196
 
190
- def changed?(old_value, *)
191
- # Ensure we save if the default was `nil`
192
- super || old_value == 0
193
- end
194
-
195
197
  def init_with(coder)
196
198
  __setobj__(coder['subtype'])
197
199
  end