activerecord 6.0.0.beta2 → 6.0.2.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 (142) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +471 -9
  3. data/README.rdoc +3 -1
  4. data/lib/active_record.rb +0 -1
  5. data/lib/active_record/association_relation.rb +15 -6
  6. data/lib/active_record/associations.rb +4 -3
  7. data/lib/active_record/associations/association.rb +10 -2
  8. data/lib/active_record/associations/builder/association.rb +14 -18
  9. data/lib/active_record/associations/builder/belongs_to.rb +5 -2
  10. data/lib/active_record/associations/builder/collection_association.rb +5 -15
  11. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -1
  12. data/lib/active_record/associations/builder/has_many.rb +2 -0
  13. data/lib/active_record/associations/builder/has_one.rb +35 -1
  14. data/lib/active_record/associations/builder/singular_association.rb +2 -0
  15. data/lib/active_record/associations/collection_association.rb +6 -2
  16. data/lib/active_record/associations/collection_proxy.rb +2 -2
  17. data/lib/active_record/associations/has_many_through_association.rb +4 -11
  18. data/lib/active_record/associations/join_dependency.rb +14 -9
  19. data/lib/active_record/associations/join_dependency/join_association.rb +12 -3
  20. data/lib/active_record/associations/preloader.rb +13 -8
  21. data/lib/active_record/associations/preloader/association.rb +34 -30
  22. data/lib/active_record/associations/preloader/through_association.rb +48 -28
  23. data/lib/active_record/attribute_methods.rb +3 -53
  24. data/lib/active_record/attribute_methods/before_type_cast.rb +4 -1
  25. data/lib/active_record/attribute_methods/dirty.rb +47 -14
  26. data/lib/active_record/attribute_methods/primary_key.rb +7 -15
  27. data/lib/active_record/attribute_methods/query.rb +2 -3
  28. data/lib/active_record/attribute_methods/read.rb +3 -9
  29. data/lib/active_record/attribute_methods/write.rb +6 -12
  30. data/lib/active_record/attributes.rb +13 -0
  31. data/lib/active_record/autosave_association.rb +21 -7
  32. data/lib/active_record/base.rb +0 -1
  33. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +109 -11
  34. data/lib/active_record/connection_adapters/abstract/database_limits.rb +8 -4
  35. data/lib/active_record/connection_adapters/abstract/database_statements.rb +88 -61
  36. data/lib/active_record/connection_adapters/abstract/query_cache.rb +6 -4
  37. data/lib/active_record/connection_adapters/abstract/quoting.rb +63 -6
  38. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +4 -7
  39. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -1
  40. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +79 -22
  41. data/lib/active_record/connection_adapters/abstract/transaction.rb +12 -4
  42. data/lib/active_record/connection_adapters/abstract_adapter.rb +114 -34
  43. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +78 -73
  44. data/lib/active_record/connection_adapters/column.rb +17 -13
  45. data/lib/active_record/connection_adapters/connection_specification.rb +2 -2
  46. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +2 -2
  47. data/lib/active_record/connection_adapters/mysql/database_statements.rb +48 -8
  48. data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
  49. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +7 -5
  50. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +10 -7
  51. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
  52. data/lib/active_record/connection_adapters/mysql2_adapter.rb +18 -5
  53. data/lib/active_record/connection_adapters/postgresql/column.rb +17 -30
  54. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +8 -2
  55. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  56. data/lib/active_record/connection_adapters/postgresql/quoting.rb +39 -2
  57. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +34 -38
  58. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +23 -27
  59. data/lib/active_record/connection_adapters/postgresql_adapter.rb +68 -27
  60. data/lib/active_record/connection_adapters/schema_cache.rb +32 -14
  61. data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
  62. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +120 -0
  63. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +38 -2
  64. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +2 -2
  65. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +66 -114
  66. data/lib/active_record/connection_handling.rb +31 -13
  67. data/lib/active_record/core.rb +23 -24
  68. data/lib/active_record/database_configurations.rb +73 -44
  69. data/lib/active_record/database_configurations/hash_config.rb +11 -11
  70. data/lib/active_record/database_configurations/url_config.rb +12 -12
  71. data/lib/active_record/dynamic_matchers.rb +1 -1
  72. data/lib/active_record/enum.rb +15 -0
  73. data/lib/active_record/errors.rb +1 -1
  74. data/lib/active_record/fixtures.rb +11 -6
  75. data/lib/active_record/gem_version.rb +2 -2
  76. data/lib/active_record/insert_all.rb +179 -0
  77. data/lib/active_record/integration.rb +13 -1
  78. data/lib/active_record/internal_metadata.rb +5 -1
  79. data/lib/active_record/locking/optimistic.rb +3 -4
  80. data/lib/active_record/log_subscriber.rb +1 -1
  81. data/lib/active_record/middleware/database_selector.rb +3 -3
  82. data/lib/active_record/middleware/database_selector/resolver.rb +4 -6
  83. data/lib/active_record/migration.rb +62 -44
  84. data/lib/active_record/migration/command_recorder.rb +28 -14
  85. data/lib/active_record/migration/compatibility.rb +10 -0
  86. data/lib/active_record/model_schema.rb +3 -0
  87. data/lib/active_record/persistence.rb +206 -13
  88. data/lib/active_record/querying.rb +17 -12
  89. data/lib/active_record/railtie.rb +0 -1
  90. data/lib/active_record/railties/databases.rake +127 -25
  91. data/lib/active_record/reflection.rb +3 -3
  92. data/lib/active_record/relation.rb +99 -20
  93. data/lib/active_record/relation/calculations.rb +38 -40
  94. data/lib/active_record/relation/delegation.rb +22 -30
  95. data/lib/active_record/relation/finder_methods.rb +17 -12
  96. data/lib/active_record/relation/merger.rb +11 -16
  97. data/lib/active_record/relation/query_methods.rb +228 -76
  98. data/lib/active_record/relation/where_clause.rb +9 -5
  99. data/lib/active_record/sanitization.rb +33 -4
  100. data/lib/active_record/schema.rb +1 -1
  101. data/lib/active_record/schema_dumper.rb +10 -1
  102. data/lib/active_record/schema_migration.rb +1 -1
  103. data/lib/active_record/scoping/default.rb +6 -7
  104. data/lib/active_record/scoping/named.rb +3 -2
  105. data/lib/active_record/statement_cache.rb +2 -2
  106. data/lib/active_record/store.rb +48 -0
  107. data/lib/active_record/table_metadata.rb +9 -13
  108. data/lib/active_record/tasks/database_tasks.rb +109 -6
  109. data/lib/active_record/tasks/mysql_database_tasks.rb +3 -1
  110. data/lib/active_record/test_databases.rb +1 -16
  111. data/lib/active_record/test_fixtures.rb +1 -0
  112. data/lib/active_record/timestamp.rb +26 -16
  113. data/lib/active_record/touch_later.rb +4 -2
  114. data/lib/active_record/transactions.rb +56 -46
  115. data/lib/active_record/type_caster/connection.rb +16 -10
  116. data/lib/active_record/validations.rb +1 -0
  117. data/lib/active_record/validations/uniqueness.rb +3 -5
  118. data/lib/arel.rb +12 -5
  119. data/lib/arel/insert_manager.rb +3 -3
  120. data/lib/arel/nodes.rb +2 -1
  121. data/lib/arel/nodes/comment.rb +29 -0
  122. data/lib/arel/nodes/select_core.rb +16 -12
  123. data/lib/arel/nodes/unary.rb +1 -0
  124. data/lib/arel/nodes/values_list.rb +2 -17
  125. data/lib/arel/select_manager.rb +10 -10
  126. data/lib/arel/visitors/depth_first.rb +7 -2
  127. data/lib/arel/visitors/dot.rb +7 -2
  128. data/lib/arel/visitors/ibm_db.rb +13 -0
  129. data/lib/arel/visitors/informix.rb +6 -0
  130. data/lib/arel/visitors/mssql.rb +15 -1
  131. data/lib/arel/visitors/oracle12.rb +4 -5
  132. data/lib/arel/visitors/postgresql.rb +4 -10
  133. data/lib/arel/visitors/to_sql.rb +107 -131
  134. data/lib/arel/visitors/visitor.rb +9 -5
  135. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -1
  136. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
  137. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
  138. data/lib/rails/generators/active_record/model/model_generator.rb +1 -1
  139. data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
  140. metadata +16 -12
  141. data/lib/active_record/collection_cache_key.rb +0 -53
  142. data/lib/arel/nodes/values.rb +0 -16
@@ -3,6 +3,8 @@
3
3
  module ActiveRecord
4
4
  module Tasks # :nodoc:
5
5
  class MySQLDatabaseTasks # :nodoc:
6
+ ER_DB_CREATE_EXISTS = 1007
7
+
6
8
  delegate :connection, :establish_connection, to: ActiveRecord::Base
7
9
 
8
10
  def initialize(configuration)
@@ -14,7 +16,7 @@ module ActiveRecord
14
16
  connection.create_database configuration["database"], creation_options
15
17
  establish_connection configuration
16
18
  rescue ActiveRecord::StatementInvalid => error
17
- if error.message.include?("database exists")
19
+ if connection.error_number(error.cause) == ER_DB_CREATE_EXISTS
18
20
  raise DatabaseAlreadyExists
19
21
  else
20
22
  raise
@@ -8,31 +8,16 @@ module ActiveRecord
8
8
  create_and_load_schema(i, env_name: Rails.env)
9
9
  end
10
10
 
11
- ActiveSupport::Testing::Parallelization.run_cleanup_hook do
12
- drop(env_name: Rails.env)
13
- end
14
-
15
11
  def self.create_and_load_schema(i, env_name:)
16
12
  old, ENV["VERBOSE"] = ENV["VERBOSE"], "false"
17
13
 
18
14
  ActiveRecord::Base.configurations.configs_for(env_name: env_name).each do |db_config|
19
15
  db_config.config["database"] += "-#{i}"
20
- ActiveRecord::Tasks::DatabaseTasks.create(db_config.config)
21
- ActiveRecord::Tasks::DatabaseTasks.load_schema(db_config.config, ActiveRecord::Base.schema_format, nil, env_name, db_config.spec_name)
16
+ ActiveRecord::Tasks::DatabaseTasks.reconstruct_from_schema(db_config.config, ActiveRecord::Base.schema_format, nil, env_name, db_config.spec_name)
22
17
  end
23
18
  ensure
24
19
  ActiveRecord::Base.establish_connection(Rails.env.to_sym)
25
20
  ENV["VERBOSE"] = old
26
21
  end
27
-
28
- def self.drop(env_name:)
29
- old, ENV["VERBOSE"] = ENV["VERBOSE"], "false"
30
-
31
- ActiveRecord::Base.configurations.configs_for(env_name: env_name).each do |db_config|
32
- ActiveRecord::Tasks::DatabaseTasks.drop(db_config.config)
33
- end
34
- ensure
35
- ENV["VERBOSE"] = old
36
- end
37
22
  end
38
23
  end
@@ -129,6 +129,7 @@ module ActiveRecord
129
129
  # When connections are established in the future, begin a transaction too
130
130
  @connection_subscriber = ActiveSupport::Notifications.subscribe("!connection.active_record") do |_, _, _, _, payload|
131
131
  spec_name = payload[:spec_name] if payload.key?(:spec_name)
132
+ setup_shared_connection_pool
132
133
 
133
134
  if spec_name
134
135
  begin
@@ -59,19 +59,26 @@ module ActiveRecord
59
59
  attribute_names.index_with(time || current_time_from_proper_timezone)
60
60
  end
61
61
 
62
- private
63
- def timestamp_attributes_for_create_in_model
64
- timestamp_attributes_for_create.select { |c| column_names.include?(c) }
65
- end
62
+ def timestamp_attributes_for_create_in_model
63
+ @timestamp_attributes_for_create_in_model ||=
64
+ (timestamp_attributes_for_create & column_names).freeze
65
+ end
66
66
 
67
- def timestamp_attributes_for_update_in_model
68
- timestamp_attributes_for_update.select { |c| column_names.include?(c) }
69
- end
67
+ def timestamp_attributes_for_update_in_model
68
+ @timestamp_attributes_for_update_in_model ||=
69
+ (timestamp_attributes_for_update & column_names).freeze
70
+ end
70
71
 
71
- def all_timestamp_attributes_in_model
72
- timestamp_attributes_for_create_in_model + timestamp_attributes_for_update_in_model
73
- end
72
+ def all_timestamp_attributes_in_model
73
+ @all_timestamp_attributes_in_model ||=
74
+ (timestamp_attributes_for_create_in_model + timestamp_attributes_for_update_in_model).freeze
75
+ end
74
76
 
77
+ def current_time_from_proper_timezone
78
+ default_timezone == :utc ? Time.now.utc : Time.now
79
+ end
80
+
81
+ private
75
82
  def timestamp_attributes_for_create
76
83
  ["created_at", "created_on"]
77
84
  end
@@ -80,8 +87,11 @@ module ActiveRecord
80
87
  ["updated_at", "updated_on"]
81
88
  end
82
89
 
83
- def current_time_from_proper_timezone
84
- default_timezone == :utc ? Time.now.utc : Time.now
90
+ def reload_schema_from_cache
91
+ @timestamp_attributes_for_create_in_model = nil
92
+ @timestamp_attributes_for_update_in_model = nil
93
+ @all_timestamp_attributes_in_model = nil
94
+ super
85
95
  end
86
96
  end
87
97
 
@@ -124,19 +134,19 @@ module ActiveRecord
124
134
  end
125
135
 
126
136
  def timestamp_attributes_for_create_in_model
127
- self.class.send(:timestamp_attributes_for_create_in_model)
137
+ self.class.timestamp_attributes_for_create_in_model
128
138
  end
129
139
 
130
140
  def timestamp_attributes_for_update_in_model
131
- self.class.send(:timestamp_attributes_for_update_in_model)
141
+ self.class.timestamp_attributes_for_update_in_model
132
142
  end
133
143
 
134
144
  def all_timestamp_attributes_in_model
135
- self.class.send(:all_timestamp_attributes_in_model)
145
+ self.class.all_timestamp_attributes_in_model
136
146
  end
137
147
 
138
148
  def current_time_from_proper_timezone
139
- self.class.send(:current_time_from_proper_timezone)
149
+ self.class.current_time_from_proper_timezone
140
150
  end
141
151
 
142
152
  def max_updated_column_timestamp
@@ -2,7 +2,7 @@
2
2
 
3
3
  module ActiveRecord
4
4
  # = Active Record Touch Later
5
- module TouchLater
5
+ module TouchLater # :nodoc:
6
6
  extend ActiveSupport::Concern
7
7
 
8
8
  included do
@@ -22,7 +22,8 @@ module ActiveRecord
22
22
  @_touch_time = current_time_from_proper_timezone
23
23
 
24
24
  surreptitiously_touch @_defer_touch_attrs
25
- self.class.connection.add_transaction_record self
25
+ add_to_transaction
26
+ @_new_record_before_last_commit ||= false
26
27
 
27
28
  # touch the parents as we are not calling the after_save callbacks
28
29
  self.class.reflect_on_all_associations(:belongs_to).each do |r|
@@ -48,6 +49,7 @@ module ActiveRecord
48
49
 
49
50
  def touch_deferred_attributes
50
51
  if has_defer_touch_attrs? && persisted?
52
+ @_skip_dirty_tracking = true
51
53
  touch(*@_defer_touch_attrs, time: @_touch_time)
52
54
  @_defer_touch_attrs, @_touch_time = nil, nil
53
55
  end
@@ -234,6 +234,12 @@ module ActiveRecord
234
234
  set_callback(:commit, :after, *args, &block)
235
235
  end
236
236
 
237
+ # Shortcut for <tt>after_commit :hook, on: [ :create, :update ]</tt>.
238
+ def after_save_commit(*args, &block)
239
+ set_options_for_callbacks!(args, on: [ :create, :update ])
240
+ set_callback(:commit, :after, *args, &block)
241
+ end
242
+
237
243
  # Shortcut for <tt>after_commit :hook, on: :create</tt>.
238
244
  def after_create_commit(*args, &block)
239
245
  set_options_for_callbacks!(args, on: :create)
@@ -327,14 +333,14 @@ module ActiveRecord
327
333
  # Ensure that it is not called if the object was never persisted (failed create),
328
334
  # but call it after the commit of a destroyed object.
329
335
  def committed!(should_run_callbacks: true) #:nodoc:
330
- if should_run_callbacks && (destroyed? || persisted?)
336
+ force_clear_transaction_record_state
337
+ if should_run_callbacks
331
338
  @_committed_already_called = true
332
339
  _run_commit_without_transaction_enrollment_callbacks
333
340
  _run_commit_callbacks
334
341
  end
335
342
  ensure
336
343
  @_committed_already_called = false
337
- force_clear_transaction_record_state
338
344
  end
339
345
 
340
346
  # Call the #after_rollback callbacks. The +force_restore_state+ argument indicates if the record
@@ -349,18 +355,6 @@ module ActiveRecord
349
355
  clear_transaction_record_state
350
356
  end
351
357
 
352
- # Add the record to the current transaction so that the #after_rollback and #after_commit callbacks
353
- # can be called.
354
- def add_to_transaction
355
- if has_transactional_callbacks?
356
- self.class.connection.add_transaction_record(self)
357
- else
358
- sync_with_transaction_state
359
- set_transaction_state(self.class.connection.transaction_state)
360
- end
361
- remember_transaction_record_state
362
- end
363
-
364
358
  # Executes +method+ within a transaction and captures its return value as a
365
359
  # status flag. If the status is true the transaction is committed, otherwise
366
360
  # a ROLLBACK is issued. In any case the status flag is returned.
@@ -370,29 +364,40 @@ module ActiveRecord
370
364
  def with_transaction_returning_status
371
365
  status = nil
372
366
  self.class.transaction do
373
- add_to_transaction
367
+ if has_transactional_callbacks?
368
+ add_to_transaction
369
+ else
370
+ sync_with_transaction_state if @transaction_state&.finalized?
371
+ @transaction_state = self.class.connection.transaction_state
372
+ end
373
+ remember_transaction_record_state
374
+
374
375
  status = yield
375
376
  raise ActiveRecord::Rollback unless status
376
377
  end
377
378
  status
378
379
  end
379
380
 
381
+ def trigger_transactional_callbacks? # :nodoc:
382
+ (@_new_record_before_last_commit || _trigger_update_callback) && persisted? ||
383
+ _trigger_destroy_callback && destroyed?
384
+ end
385
+
380
386
  private
381
387
  attr_reader :_committed_already_called, :_trigger_update_callback, :_trigger_destroy_callback
382
388
 
383
389
  # Save the new record state and id of a record so it can be restored later if a transaction fails.
384
390
  def remember_transaction_record_state
385
- @_start_transaction_state.reverse_merge!(
391
+ @_start_transaction_state ||= {
386
392
  id: id,
387
393
  new_record: @new_record,
388
394
  destroyed: @destroyed,
395
+ attributes: @attributes,
389
396
  frozen?: frozen?,
390
- )
391
- @_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) + 1
392
- remember_new_record_before_last_commit
393
- end
397
+ level: 0
398
+ }
399
+ @_start_transaction_state[:level] += 1
394
400
 
395
- def remember_new_record_before_last_commit
396
401
  if _committed_already_called
397
402
  @_new_record_before_last_commit = false
398
403
  else
@@ -402,27 +407,32 @@ module ActiveRecord
402
407
 
403
408
  # Clear the new record state and id of a record.
404
409
  def clear_transaction_record_state
405
- @_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) - 1
410
+ return unless @_start_transaction_state
411
+ @_start_transaction_state[:level] -= 1
406
412
  force_clear_transaction_record_state if @_start_transaction_state[:level] < 1
407
413
  end
408
414
 
409
415
  # Force to clear the transaction record state.
410
416
  def force_clear_transaction_record_state
411
- @_start_transaction_state.clear
417
+ @_start_transaction_state = nil
418
+ @transaction_state = nil
412
419
  end
413
420
 
414
421
  # Restore the new record state and id of a record that was previously saved by a call to save_record_state.
415
- def restore_transaction_record_state(force = false)
416
- unless @_start_transaction_state.empty?
417
- transaction_level = (@_start_transaction_state[:level] || 0) - 1
418
- if transaction_level < 1 || force
419
- restore_state = @_start_transaction_state
420
- thaw
422
+ def restore_transaction_record_state(force_restore_state = false)
423
+ if restore_state = @_start_transaction_state
424
+ if force_restore_state || restore_state[:level] <= 1
421
425
  @new_record = restore_state[:new_record]
422
426
  @destroyed = restore_state[:destroyed]
423
- pk = self.class.primary_key
424
- if pk && _read_attribute(pk) != restore_state[:id]
425
- _write_attribute(pk, restore_state[:id])
427
+ @attributes = restore_state[:attributes].map do |attr|
428
+ value = @attributes.fetch_value(attr.name)
429
+ attr = attr.with_value_from_user(value) if attr.value != value
430
+ attr
431
+ end
432
+ @mutations_from_database = nil
433
+ @mutations_before_last_save = nil
434
+ if @attributes.fetch_value(@primary_key) != restore_state[:id]
435
+ @attributes.write_from_user(@primary_key, restore_state[:id])
426
436
  end
427
437
  freeze if restore_state[:frozen?]
428
438
  end
@@ -443,8 +453,10 @@ module ActiveRecord
443
453
  end
444
454
  end
445
455
 
446
- def set_transaction_state(state)
447
- @transaction_state = state
456
+ # Add the record to the current transaction so that the #after_rollback and #after_commit
457
+ # callbacks can be called.
458
+ def add_to_transaction
459
+ self.class.connection.add_transaction_record(self)
448
460
  end
449
461
 
450
462
  def has_transactional_callbacks?
@@ -464,19 +476,17 @@ module ActiveRecord
464
476
  # This method checks to see if the ActiveRecord object's state reflects
465
477
  # the TransactionState, and rolls back or commits the Active Record object
466
478
  # as appropriate.
467
- #
468
- # Since Active Record objects can be inside multiple transactions, this
469
- # method recursively goes through the parent of the TransactionState and
470
- # checks if the Active Record object reflects the state of the object.
471
479
  def sync_with_transaction_state
472
- update_attributes_from_transaction_state(@transaction_state)
473
- end
474
-
475
- def update_attributes_from_transaction_state(transaction_state)
476
- if transaction_state && transaction_state.finalized?
477
- restore_transaction_record_state(transaction_state.fully_rolledback?) if transaction_state.rolledback?
478
- force_clear_transaction_record_state if transaction_state.fully_committed?
479
- clear_transaction_record_state if transaction_state.fully_completed?
480
+ if transaction_state = @transaction_state
481
+ if transaction_state.fully_committed?
482
+ force_clear_transaction_record_state
483
+ elsif transaction_state.committed?
484
+ clear_transaction_record_state
485
+ elsif transaction_state.rolledback?
486
+ force_restore_state = transaction_state.fully_rolledback?
487
+ restore_transaction_record_state(force_restore_state)
488
+ clear_transaction_record_state
489
+ end
480
490
  end
481
491
  end
482
492
  end
@@ -8,21 +8,27 @@ module ActiveRecord
8
8
  @table_name = table_name
9
9
  end
10
10
 
11
- def type_cast_for_database(attribute_name, value)
11
+ def type_cast_for_database(attr_name, value)
12
12
  return value if value.is_a?(Arel::Nodes::BindParam)
13
- column = column_for(attribute_name)
14
- connection.type_cast_from_column(column, value)
13
+ type = type_for_attribute(attr_name)
14
+ type.serialize(value)
15
15
  end
16
16
 
17
- private
18
- attr_reader :table_name
19
- delegate :connection, to: :@klass
17
+ def type_for_attribute(attr_name)
18
+ schema_cache = connection.schema_cache
20
19
 
21
- def column_for(attribute_name)
22
- if connection.schema_cache.data_source_exists?(table_name)
23
- connection.schema_cache.columns_hash(table_name)[attribute_name.to_s]
24
- end
20
+ if schema_cache.data_source_exists?(table_name)
21
+ column = schema_cache.columns_hash(table_name)[attr_name.to_s]
22
+ type = connection.lookup_cast_type_from_column(column) if column
25
23
  end
24
+
25
+ type || Type.default_value
26
+ end
27
+
28
+ delegate :connection, to: :@klass, private: true
29
+
30
+ private
31
+ attr_reader :table_name
26
32
  end
27
33
  end
28
34
  end
@@ -40,6 +40,7 @@ module ActiveRecord
40
40
  include ActiveModel::Validations
41
41
 
42
42
  # The validation process on save can be skipped by passing <tt>validate: false</tt>.
43
+ # The validation context can be changed by passing <tt>context: context</tt>.
43
44
  # The regular {ActiveRecord::Base#save}[rdoc-ref:Persistence#save] method is replaced
44
45
  # with this when the validations module is mixed in, which it is by default.
45
46
  def save(options = {})
@@ -25,7 +25,7 @@ module ActiveRecord
25
25
  if finder_class.primary_key
26
26
  relation = relation.where.not(finder_class.primary_key => record.id_in_database)
27
27
  else
28
- raise UnknownPrimaryKey.new(finder_class, "Can not validate uniqueness for persisted record without primary key.")
28
+ raise UnknownPrimaryKey.new(finder_class, "Cannot validate uniqueness for persisted record without primary key.")
29
29
  end
30
30
  end
31
31
  relation = scope_relation(record, relation)
@@ -60,10 +60,8 @@ module ActiveRecord
60
60
  comparison = relation.bind_attribute(attribute, value) do |attr, bind|
61
61
  return relation.none! if bind.unboundable?
62
62
 
63
- if bind.nil?
64
- attr.eq(bind)
65
- elsif !options.key?(:case_sensitive)
66
- klass.connection.default_uniqueness_comparison(attr, bind)
63
+ if !options.key?(:case_sensitive) || bind.nil?
64
+ klass.connection.default_uniqueness_comparison(attr, bind, klass)
67
65
  elsif options[:case_sensitive]
68
66
  klass.connection.case_sensitive_comparison(attr, bind)
69
67
  else
@@ -24,22 +24,29 @@ require "arel/update_manager"
24
24
  require "arel/delete_manager"
25
25
  require "arel/nodes"
26
26
 
27
- module Arel # :nodoc: all
27
+ module Arel
28
28
  VERSION = "10.0.0"
29
29
 
30
+ # Wrap a known-safe SQL string for passing to query methods, e.g.
31
+ #
32
+ # Post.order(Arel.sql("length(title)")).last
33
+ #
34
+ # Great caution should be taken to avoid SQL injection vulnerabilities.
35
+ # This method should not be used with unsafe values such as request
36
+ # parameters or model attributes.
30
37
  def self.sql(raw_sql)
31
38
  Arel::Nodes::SqlLiteral.new raw_sql
32
39
  end
33
40
 
34
- def self.star
41
+ def self.star # :nodoc:
35
42
  sql "*"
36
43
  end
37
44
 
38
- def self.arel_node?(value)
45
+ def self.arel_node?(value) # :nodoc:
39
46
  value.is_a?(Arel::Node) || value.is_a?(Arel::Attribute) || value.is_a?(Arel::Nodes::SqlLiteral)
40
47
  end
41
48
 
42
- def self.fetch_attribute(value)
49
+ def self.fetch_attribute(value) # :nodoc:
43
50
  case value
44
51
  when Arel::Nodes::Between, Arel::Nodes::In, Arel::Nodes::NotIn, Arel::Nodes::Equality, Arel::Nodes::NotEqual, Arel::Nodes::LessThan, Arel::Nodes::LessThanOrEqual, Arel::Nodes::GreaterThan, Arel::Nodes::GreaterThanOrEqual
45
52
  yield value.left.is_a?(Arel::Attributes::Attribute) ? value.left : value.right
@@ -47,5 +54,5 @@ module Arel # :nodoc: all
47
54
  end
48
55
 
49
56
  ## Convenience Alias
50
- Node = Arel::Nodes::Node
57
+ Node = Arel::Nodes::Node # :nodoc:
51
58
  end