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
@@ -46,11 +46,9 @@ module ActiveRecord
46
46
  private
47
47
 
48
48
  def read_from_primary(&blk)
49
- ActiveRecord::Base.connection.while_preventing_writes do
50
- ActiveRecord::Base.connected_to(role: ActiveRecord::Base.writing_role) do
51
- instrumenter.instrument("database_selector.active_record.read_from_primary") do
52
- yield
53
- end
49
+ ActiveRecord::Base.connected_to(role: ActiveRecord::Base.writing_role, prevent_writes: true) do
50
+ instrumenter.instrument("database_selector.active_record.read_from_primary") do
51
+ yield
54
52
  end
55
53
  end
56
54
  end
@@ -64,7 +62,7 @@ module ActiveRecord
64
62
  end
65
63
 
66
64
  def write_to_primary(&blk)
67
- ActiveRecord::Base.connected_to(role: ActiveRecord::Base.writing_role) do
65
+ ActiveRecord::Base.connected_to(role: ActiveRecord::Base.writing_role, prevent_writes: false) do
68
66
  instrumenter.instrument("database_selector.active_record.wrote_to_primary") do
69
67
  yield
70
68
  ensure
@@ -4,9 +4,10 @@ require "benchmark"
4
4
  require "set"
5
5
  require "zlib"
6
6
  require "active_support/core_ext/module/attribute_accessors"
7
+ require "active_support/actionable_error"
7
8
 
8
9
  module ActiveRecord
9
- class MigrationError < ActiveRecordError#:nodoc:
10
+ class MigrationError < ActiveRecordError #:nodoc:
10
11
  def initialize(message = nil)
11
12
  message = "\n\n#{message}\n\n" if message
12
13
  super
@@ -87,7 +88,7 @@ module ActiveRecord
87
88
  class IrreversibleMigration < MigrationError
88
89
  end
89
90
 
90
- class DuplicateMigrationVersionError < MigrationError#:nodoc:
91
+ class DuplicateMigrationVersionError < MigrationError #:nodoc:
91
92
  def initialize(version = nil)
92
93
  if version
93
94
  super("Multiple migrations have the version number #{version}.")
@@ -97,7 +98,7 @@ module ActiveRecord
97
98
  end
98
99
  end
99
100
 
100
- class DuplicateMigrationNameError < MigrationError#:nodoc:
101
+ class DuplicateMigrationNameError < MigrationError #:nodoc:
101
102
  def initialize(name = nil)
102
103
  if name
103
104
  super("Multiple migrations have the name #{name}.")
@@ -117,7 +118,7 @@ module ActiveRecord
117
118
  end
118
119
  end
119
120
 
120
- class IllegalMigrationNameError < MigrationError#:nodoc:
121
+ class IllegalMigrationNameError < MigrationError #:nodoc:
121
122
  def initialize(name = nil)
122
123
  if name
123
124
  super("Illegal name for migration file: #{name}\n\t(only lower case letters, numbers, and '_' allowed).")
@@ -127,7 +128,13 @@ module ActiveRecord
127
128
  end
128
129
  end
129
130
 
130
- class PendingMigrationError < MigrationError#:nodoc:
131
+ class PendingMigrationError < MigrationError #:nodoc:
132
+ include ActiveSupport::ActionableError
133
+
134
+ action "Run pending migrations" do
135
+ ActiveRecord::Tasks::DatabaseTasks.migrate
136
+ end
137
+
131
138
  def initialize(message = nil)
132
139
  if !message && defined?(Rails.env)
133
140
  super("Migrations are pending. To resolve this issue, run:\n\n rails db:migrate RAILS_ENV=#{::Rails.env}")
@@ -308,7 +315,7 @@ module ActiveRecord
308
315
  # named +column_name+ from the table called +table_name+.
309
316
  # * <tt>remove_columns(table_name, *column_names)</tt>: Removes the given
310
317
  # columns from the table definition.
311
- # * <tt>remove_foreign_key(from_table, options_or_to_table)</tt>: Removes the
318
+ # * <tt>remove_foreign_key(from_table, to_table = nil, **options)</tt>: Removes the
312
319
  # given foreign key from the table called +table_name+.
313
320
  # * <tt>remove_index(table_name, column: column_names)</tt>: Removes the index
314
321
  # specified by +column_names+.
@@ -487,9 +494,9 @@ module ActiveRecord
487
494
  # This migration will create the horses table for you on the way up, and
488
495
  # automatically figure out how to drop the table on the way down.
489
496
  #
490
- # Some commands like +remove_column+ cannot be reversed. If you care to
491
- # define how to move up and down in these cases, you should define the +up+
492
- # and +down+ methods as before.
497
+ # Some commands cannot be reversed. If you care to define how to move up
498
+ # and down in these cases, you should define the +up+ and +down+ methods
499
+ # as before.
493
500
  #
494
501
  # If a command cannot be reversed, an
495
502
  # <tt>ActiveRecord::IrreversibleMigration</tt> exception will be raised when
@@ -520,10 +527,10 @@ module ActiveRecord
520
527
  autoload :Compatibility, "active_record/migration/compatibility"
521
528
 
522
529
  # This must be defined before the inherited hook, below
523
- class Current < Migration # :nodoc:
530
+ class Current < Migration #:nodoc:
524
531
  end
525
532
 
526
- def self.inherited(subclass) # :nodoc:
533
+ def self.inherited(subclass) #:nodoc:
527
534
  super
528
535
  if subclass.superclass == Migration
529
536
  raise StandardError, "Directly inheriting from ActiveRecord::Migration is not supported. " \
@@ -541,7 +548,7 @@ module ActiveRecord
541
548
  ActiveRecord::VERSION::STRING.to_f
542
549
  end
543
550
 
544
- MigrationFilenameRegexp = /\A([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?\.rb\z/ # :nodoc:
551
+ MigrationFilenameRegexp = /\A([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?\.rb\z/ #:nodoc:
545
552
 
546
553
  # This class is used to verify that all migrations have been run before
547
554
  # loading a web page if <tt>config.active_record.migration_error</tt> is set to :page_load
@@ -568,10 +575,10 @@ module ActiveRecord
568
575
  end
569
576
 
570
577
  class << self
571
- attr_accessor :delegate # :nodoc:
572
- attr_accessor :disable_ddl_transaction # :nodoc:
578
+ attr_accessor :delegate #:nodoc:
579
+ attr_accessor :disable_ddl_transaction #:nodoc:
573
580
 
574
- def nearest_delegate # :nodoc:
581
+ def nearest_delegate #:nodoc:
575
582
  delegate || superclass.nearest_delegate
576
583
  end
577
584
 
@@ -581,27 +588,35 @@ module ActiveRecord
581
588
  end
582
589
 
583
590
  def load_schema_if_pending!
584
- if Base.connection.migration_context.needs_migration? || !Base.connection.migration_context.any_migrations?
591
+ current_config = Base.connection_config
592
+ all_configs = ActiveRecord::Base.configurations.configs_for(env_name: Rails.env)
593
+
594
+ needs_update = !all_configs.all? do |db_config|
595
+ Tasks::DatabaseTasks.schema_up_to_date?(db_config.config, ActiveRecord::Base.schema_format, nil, Rails.env, db_config.spec_name)
596
+ end
597
+
598
+ if needs_update
585
599
  # Roundtrip to Rake to allow plugins to hook into database initialization.
586
600
  root = defined?(ENGINE_ROOT) ? ENGINE_ROOT : Rails.root
587
601
  FileUtils.cd(root) do
588
- current_config = Base.connection_config
589
602
  Base.clear_all_connections!
590
603
  system("bin/rails db:test:prepare")
591
- # Establish a new connection, the old database may be gone (db:test:prepare uses purge)
592
- Base.establish_connection(current_config)
593
604
  end
594
- check_pending!
595
605
  end
606
+
607
+ # Establish a new connection, the old database may be gone (db:test:prepare uses purge)
608
+ Base.establish_connection(current_config)
609
+
610
+ check_pending!
596
611
  end
597
612
 
598
- def maintain_test_schema! # :nodoc:
613
+ def maintain_test_schema! #:nodoc:
599
614
  if ActiveRecord::Base.maintain_test_schema
600
615
  suppress_messages { load_schema_if_pending! }
601
616
  end
602
617
  end
603
618
 
604
- def method_missing(name, *args, &block) # :nodoc:
619
+ def method_missing(name, *args, &block) #:nodoc:
605
620
  nearest_delegate.send(name, *args, &block)
606
621
  end
607
622
 
@@ -618,7 +633,7 @@ module ActiveRecord
618
633
  end
619
634
  end
620
635
 
621
- def disable_ddl_transaction # :nodoc:
636
+ def disable_ddl_transaction #:nodoc:
622
637
  self.class.disable_ddl_transaction
623
638
  end
624
639
 
@@ -693,7 +708,7 @@ module ActiveRecord
693
708
  connection.respond_to?(:reverting) && connection.reverting
694
709
  end
695
710
 
696
- ReversibleBlockHelper = Struct.new(:reverting) do # :nodoc:
711
+ ReversibleBlockHelper = Struct.new(:reverting) do #:nodoc:
697
712
  def up
698
713
  yield unless reverting
699
714
  end
@@ -878,13 +893,14 @@ module ActiveRecord
878
893
 
879
894
  def copy(destination, sources, options = {})
880
895
  copied = []
896
+ schema_migration = options[:schema_migration] || ActiveRecord::SchemaMigration
881
897
 
882
898
  FileUtils.mkdir_p(destination) unless File.exist?(destination)
883
899
 
884
- destination_migrations = ActiveRecord::MigrationContext.new(destination).migrations
900
+ destination_migrations = ActiveRecord::MigrationContext.new(destination, schema_migration).migrations
885
901
  last = destination_migrations.last
886
902
  sources.each do |scope, path|
887
- source_migrations = ActiveRecord::MigrationContext.new(path).migrations
903
+ source_migrations = ActiveRecord::MigrationContext.new(path, schema_migration).migrations
888
904
 
889
905
  source_migrations.each do |migration|
890
906
  source = File.binread(migration.filename)
@@ -1006,11 +1022,12 @@ module ActiveRecord
1006
1022
  end
1007
1023
  end
1008
1024
 
1009
- class MigrationContext # :nodoc:
1010
- attr_reader :migrations_paths
1025
+ class MigrationContext #:nodoc:
1026
+ attr_reader :migrations_paths, :schema_migration
1011
1027
 
1012
- def initialize(migrations_paths)
1028
+ def initialize(migrations_paths, schema_migration)
1013
1029
  @migrations_paths = migrations_paths
1030
+ @schema_migration = schema_migration
1014
1031
  end
1015
1032
 
1016
1033
  def migrate(target_version = nil, &block)
@@ -1041,7 +1058,7 @@ module ActiveRecord
1041
1058
  migrations
1042
1059
  end
1043
1060
 
1044
- Migrator.new(:up, selected_migrations, target_version).migrate
1061
+ Migrator.new(:up, selected_migrations, schema_migration, target_version).migrate
1045
1062
  end
1046
1063
 
1047
1064
  def down(target_version = nil)
@@ -1051,20 +1068,20 @@ module ActiveRecord
1051
1068
  migrations
1052
1069
  end
1053
1070
 
1054
- Migrator.new(:down, selected_migrations, target_version).migrate
1071
+ Migrator.new(:down, selected_migrations, schema_migration, target_version).migrate
1055
1072
  end
1056
1073
 
1057
1074
  def run(direction, target_version)
1058
- Migrator.new(direction, migrations, target_version).run
1075
+ Migrator.new(direction, migrations, schema_migration, target_version).run
1059
1076
  end
1060
1077
 
1061
1078
  def open
1062
- Migrator.new(:up, migrations, nil)
1079
+ Migrator.new(:up, migrations, schema_migration)
1063
1080
  end
1064
1081
 
1065
1082
  def get_all_versions
1066
- if SchemaMigration.table_exists?
1067
- SchemaMigration.all_versions.map(&:to_i)
1083
+ if schema_migration.table_exists?
1084
+ schema_migration.all_versions.map(&:to_i)
1068
1085
  else
1069
1086
  []
1070
1087
  end
@@ -1101,12 +1118,12 @@ module ActiveRecord
1101
1118
  end
1102
1119
 
1103
1120
  def migrations_status
1104
- db_list = ActiveRecord::SchemaMigration.normalized_versions
1121
+ db_list = schema_migration.normalized_versions
1105
1122
 
1106
1123
  file_list = migration_files.map do |file|
1107
1124
  version, name, scope = parse_migration_filename(file)
1108
1125
  raise IllegalMigrationNameError.new(file) unless version
1109
- version = ActiveRecord::SchemaMigration.normalize_migration_number(version)
1126
+ version = schema_migration.normalize_migration_number(version)
1110
1127
  status = db_list.delete(version) ? "up" : "down"
1111
1128
  [status, version, (name + scope).humanize]
1112
1129
  end.compact
@@ -1146,7 +1163,7 @@ module ActiveRecord
1146
1163
  end
1147
1164
 
1148
1165
  def move(direction, steps)
1149
- migrator = Migrator.new(direction, migrations)
1166
+ migrator = Migrator.new(direction, migrations, schema_migration)
1150
1167
 
1151
1168
  if current_version != 0 && !migrator.current_migration
1152
1169
  raise UnknownMigrationVersionError.new(current_version)
@@ -1171,21 +1188,22 @@ module ActiveRecord
1171
1188
 
1172
1189
  # For cases where a table doesn't exist like loading from schema cache
1173
1190
  def current_version
1174
- MigrationContext.new(migrations_paths).current_version
1191
+ MigrationContext.new(migrations_paths, SchemaMigration).current_version
1175
1192
  end
1176
1193
  end
1177
1194
 
1178
1195
  self.migrations_paths = ["db/migrate"]
1179
1196
 
1180
- def initialize(direction, migrations, target_version = nil)
1197
+ def initialize(direction, migrations, schema_migration, target_version = nil)
1181
1198
  @direction = direction
1182
1199
  @target_version = target_version
1183
1200
  @migrated_versions = nil
1184
1201
  @migrations = migrations
1202
+ @schema_migration = schema_migration
1185
1203
 
1186
1204
  validate(@migrations)
1187
1205
 
1188
- ActiveRecord::SchemaMigration.create_table
1206
+ @schema_migration.create_table
1189
1207
  ActiveRecord::InternalMetadata.create_table
1190
1208
  end
1191
1209
 
@@ -1239,7 +1257,7 @@ module ActiveRecord
1239
1257
  end
1240
1258
 
1241
1259
  def load_migrated
1242
- @migrated_versions = Set.new(Base.connection.migration_context.get_all_versions)
1260
+ @migrated_versions = Set.new(@schema_migration.all_versions.map(&:to_i))
1243
1261
  end
1244
1262
 
1245
1263
  private
@@ -1323,10 +1341,10 @@ module ActiveRecord
1323
1341
  def record_version_state_after_migrating(version)
1324
1342
  if down?
1325
1343
  migrated.delete(version)
1326
- ActiveRecord::SchemaMigration.delete_by(version: version.to_s)
1344
+ @schema_migration.delete_by(version: version.to_s)
1327
1345
  else
1328
1346
  migrated << version
1329
- ActiveRecord::SchemaMigration.create!(version: version.to_s)
1347
+ @schema_migration.create!(version: version.to_s)
1330
1348
  end
1331
1349
  end
1332
1350
 
@@ -14,6 +14,8 @@ module ActiveRecord
14
14
  # * change_column
15
15
  # * change_column_default (must supply a :from and :to option)
16
16
  # * change_column_null
17
+ # * change_column_comment (must supply a :from and :to option)
18
+ # * change_table_comment (must supply a :from and :to option)
17
19
  # * create_join_table
18
20
  # * create_table
19
21
  # * disable_extension
@@ -35,7 +37,8 @@ module ActiveRecord
35
37
  :change_column_default, :add_reference, :remove_reference, :transaction,
36
38
  :drop_join_table, :drop_table, :execute_block, :enable_extension, :disable_extension,
37
39
  :change_column, :execute, :remove_columns, :change_column_null,
38
- :add_foreign_key, :remove_foreign_key
40
+ :add_foreign_key, :remove_foreign_key,
41
+ :change_column_comment, :change_table_comment
39
42
  ]
40
43
  include JoinTable
41
44
 
@@ -231,28 +234,39 @@ module ActiveRecord
231
234
  end
232
235
 
233
236
  def invert_remove_foreign_key(args)
234
- from_table, options_or_to_table, options_or_nil = args
237
+ options = args.extract_options!
238
+ from_table, to_table = args
235
239
 
236
- to_table = if options_or_to_table.is_a?(Hash)
237
- options_or_to_table[:to_table]
238
- else
239
- options_or_to_table
240
- end
241
-
242
- remove_options = if options_or_to_table.is_a?(Hash)
243
- options_or_to_table.except(:to_table)
244
- else
245
- options_or_nil
246
- end
240
+ to_table ||= options.delete(:to_table)
247
241
 
248
242
  raise ActiveRecord::IrreversibleMigration, "remove_foreign_key is only reversible if given a second table" if to_table.nil?
249
243
 
250
244
  reversed_args = [from_table, to_table]
251
- reversed_args << remove_options if remove_options.present?
245
+ reversed_args << options unless options.empty?
252
246
 
253
247
  [:add_foreign_key, reversed_args]
254
248
  end
255
249
 
250
+ def invert_change_column_comment(args)
251
+ table, column, options = *args
252
+
253
+ unless options && options.is_a?(Hash) && options.has_key?(:from) && options.has_key?(:to)
254
+ raise ActiveRecord::IrreversibleMigration, "change_column_comment is only reversible if given a :from and :to option."
255
+ end
256
+
257
+ [:change_column_comment, [table, column, from: options[:to], to: options[:from]]]
258
+ end
259
+
260
+ def invert_change_table_comment(args)
261
+ table, options = *args
262
+
263
+ unless options && options.is_a?(Hash) && options.has_key?(:from) && options.has_key?(:to)
264
+ raise ActiveRecord::IrreversibleMigration, "change_table_comment is only reversible if given a :from and :to option."
265
+ end
266
+
267
+ [:change_table_comment, [table, from: options[:to], to: options[:from]]]
268
+ end
269
+
256
270
  def respond_to_missing?(method, _)
257
271
  super || delegate.respond_to?(method)
258
272
  end
@@ -27,6 +27,16 @@ module ActiveRecord
27
27
  def invert_transaction(args, &block)
28
28
  [:transaction, args, block]
29
29
  end
30
+
31
+ def invert_change_column_comment(args)
32
+ table_name, column_name, comment = args
33
+ [:change_column_comment, [table_name, column_name, from: comment, to: comment]]
34
+ end
35
+
36
+ def invert_change_table_comment(args)
37
+ table_name, comment = args
38
+ [:change_table_comment, [table_name, from: comment, to: comment]]
39
+ end
30
40
  end
31
41
 
32
42
  def create_table(table_name, **options)
@@ -480,6 +480,9 @@ module ActiveRecord
480
480
  load_schema!
481
481
 
482
482
  @schema_loaded = true
483
+ rescue
484
+ reload_schema_from_cache # If the schema loading failed half way through, we must reset the state.
485
+ raise
483
486
  end
484
487
  end
485
488
 
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_record/insert_all"
4
+
3
5
  module ActiveRecord
4
6
  # = Active Record \Persistence
5
7
  module Persistence
@@ -55,6 +57,192 @@ module ActiveRecord
55
57
  end
56
58
  end
57
59
 
60
+ # Inserts a single record into the database in a single SQL INSERT
61
+ # statement. It does not instantiate any models nor does it trigger
62
+ # Active Record callbacks or validations. Though passed values
63
+ # go through Active Record's type casting and serialization.
64
+ #
65
+ # See <tt>ActiveRecord::Persistence#insert_all</tt> for documentation.
66
+ def insert(attributes, returning: nil, unique_by: nil)
67
+ insert_all([ attributes ], returning: returning, unique_by: unique_by)
68
+ end
69
+
70
+ # Inserts multiple records into the database in a single SQL INSERT
71
+ # statement. It does not instantiate any models nor does it trigger
72
+ # Active Record callbacks or validations. Though passed values
73
+ # go through Active Record's type casting and serialization.
74
+ #
75
+ # The +attributes+ parameter is an Array of Hashes. Every Hash determines
76
+ # the attributes for a single row and must have the same keys.
77
+ #
78
+ # Rows are considered to be unique by every unique index on the table. Any
79
+ # duplicate rows are skipped.
80
+ # Override with <tt>:unique_by</tt> (see below).
81
+ #
82
+ # Returns an <tt>ActiveRecord::Result</tt> with its contents based on
83
+ # <tt>:returning</tt> (see below).
84
+ #
85
+ # ==== Options
86
+ #
87
+ # [:returning]
88
+ # (PostgreSQL only) An array of attributes to return for all successfully
89
+ # inserted records, which by default is the primary key.
90
+ # Pass <tt>returning: %w[ id name ]</tt> for both id and name
91
+ # or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
92
+ # clause entirely.
93
+ #
94
+ # [:unique_by]
95
+ # (PostgreSQL and SQLite only) By default rows are considered to be unique
96
+ # by every unique index on the table. Any duplicate rows are skipped.
97
+ #
98
+ # To skip rows according to just one unique index pass <tt>:unique_by</tt>.
99
+ #
100
+ # Consider a Book model where no duplicate ISBNs make sense, but if any
101
+ # row has an existing id, or is not unique by another unique index,
102
+ # <tt>ActiveRecord::RecordNotUnique</tt> is raised.
103
+ #
104
+ # Unique indexes can be identified by columns or name:
105
+ #
106
+ # unique_by: :isbn
107
+ # unique_by: %i[ author_id name ]
108
+ # unique_by: :index_books_on_isbn
109
+ #
110
+ # Because it relies on the index information from the database
111
+ # <tt>:unique_by</tt> is recommended to be paired with
112
+ # Active Record's schema_cache.
113
+ #
114
+ # ==== Example
115
+ #
116
+ # # Insert records and skip inserting any duplicates.
117
+ # # Here "Eloquent Ruby" is skipped because its id is not unique.
118
+ #
119
+ # Book.insert_all([
120
+ # { id: 1, title: "Rework", author: "David" },
121
+ # { id: 1, title: "Eloquent Ruby", author: "Russ" }
122
+ # ])
123
+ def insert_all(attributes, returning: nil, unique_by: nil)
124
+ InsertAll.new(self, attributes, on_duplicate: :skip, returning: returning, unique_by: unique_by).execute
125
+ end
126
+
127
+ # Inserts a single record into the database in a single SQL INSERT
128
+ # statement. It does not instantiate any models nor does it trigger
129
+ # Active Record callbacks or validations. Though passed values
130
+ # go through Active Record's type casting and serialization.
131
+ #
132
+ # See <tt>ActiveRecord::Persistence#insert_all!</tt> for more.
133
+ def insert!(attributes, returning: nil)
134
+ insert_all!([ attributes ], returning: returning)
135
+ end
136
+
137
+ # Inserts multiple records into the database in a single SQL INSERT
138
+ # statement. It does not instantiate any models nor does it trigger
139
+ # Active Record callbacks or validations. Though passed values
140
+ # go through Active Record's type casting and serialization.
141
+ #
142
+ # The +attributes+ parameter is an Array of Hashes. Every Hash determines
143
+ # the attributes for a single row and must have the same keys.
144
+ #
145
+ # Raises <tt>ActiveRecord::RecordNotUnique</tt> if any rows violate a
146
+ # unique index on the table. In that case, no rows are inserted.
147
+ #
148
+ # To skip duplicate rows, see <tt>ActiveRecord::Persistence#insert_all</tt>.
149
+ # To replace them, see <tt>ActiveRecord::Persistence#upsert_all</tt>.
150
+ #
151
+ # Returns an <tt>ActiveRecord::Result</tt> with its contents based on
152
+ # <tt>:returning</tt> (see below).
153
+ #
154
+ # ==== Options
155
+ #
156
+ # [:returning]
157
+ # (PostgreSQL only) An array of attributes to return for all successfully
158
+ # inserted records, which by default is the primary key.
159
+ # Pass <tt>returning: %w[ id name ]</tt> for both id and name
160
+ # or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
161
+ # clause entirely.
162
+ #
163
+ # ==== Examples
164
+ #
165
+ # # Insert multiple records
166
+ # Book.insert_all!([
167
+ # { title: "Rework", author: "David" },
168
+ # { title: "Eloquent Ruby", author: "Russ" }
169
+ # ])
170
+ #
171
+ # # Raises ActiveRecord::RecordNotUnique because "Eloquent Ruby"
172
+ # # does not have a unique id.
173
+ # Book.insert_all!([
174
+ # { id: 1, title: "Rework", author: "David" },
175
+ # { id: 1, title: "Eloquent Ruby", author: "Russ" }
176
+ # ])
177
+ def insert_all!(attributes, returning: nil)
178
+ InsertAll.new(self, attributes, on_duplicate: :raise, returning: returning).execute
179
+ end
180
+
181
+ # Updates or inserts (upserts) a single record into the database in a
182
+ # single SQL INSERT statement. It does not instantiate any models nor does
183
+ # it trigger Active Record callbacks or validations. Though passed values
184
+ # go through Active Record's type casting and serialization.
185
+ #
186
+ # See <tt>ActiveRecord::Persistence#upsert_all</tt> for documentation.
187
+ def upsert(attributes, returning: nil, unique_by: nil)
188
+ upsert_all([ attributes ], returning: returning, unique_by: unique_by)
189
+ end
190
+
191
+ # Updates or inserts (upserts) multiple records into the database in a
192
+ # single SQL INSERT statement. It does not instantiate any models nor does
193
+ # it trigger Active Record callbacks or validations. Though passed values
194
+ # go through Active Record's type casting and serialization.
195
+ #
196
+ # The +attributes+ parameter is an Array of Hashes. Every Hash determines
197
+ # the attributes for a single row and must have the same keys.
198
+ #
199
+ # Returns an <tt>ActiveRecord::Result</tt> with its contents based on
200
+ # <tt>:returning</tt> (see below).
201
+ #
202
+ # ==== Options
203
+ #
204
+ # [:returning]
205
+ # (PostgreSQL only) An array of attributes to return for all successfully
206
+ # inserted records, which by default is the primary key.
207
+ # Pass <tt>returning: %w[ id name ]</tt> for both id and name
208
+ # or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
209
+ # clause entirely.
210
+ #
211
+ # [:unique_by]
212
+ # (PostgreSQL and SQLite only) By default rows are considered to be unique
213
+ # by every unique index on the table. Any duplicate rows are skipped.
214
+ #
215
+ # To skip rows according to just one unique index pass <tt>:unique_by</tt>.
216
+ #
217
+ # Consider a Book model where no duplicate ISBNs make sense, but if any
218
+ # row has an existing id, or is not unique by another unique index,
219
+ # <tt>ActiveRecord::RecordNotUnique</tt> is raised.
220
+ #
221
+ # Unique indexes can be identified by columns or name:
222
+ #
223
+ # unique_by: :isbn
224
+ # unique_by: %i[ author_id name ]
225
+ # unique_by: :index_books_on_isbn
226
+ #
227
+ # Because it relies on the index information from the database
228
+ # <tt>:unique_by</tt> is recommended to be paired with
229
+ # Active Record's schema_cache.
230
+ #
231
+ # ==== Examples
232
+ #
233
+ # # Inserts multiple records, performing an upsert when records have duplicate ISBNs.
234
+ # # Here "Eloquent Ruby" overwrites "Rework" because its ISBN is duplicate.
235
+ #
236
+ # Book.upsert_all([
237
+ # { title: "Rework", author: "David", isbn: "1" },
238
+ # { title: "Eloquent Ruby", author: "Russ", isbn: "1" }
239
+ # ], unique_by: :isbn)
240
+ #
241
+ # Book.find_by(isbn: "1").title # => "Eloquent Ruby"
242
+ def upsert_all(attributes, returning: nil, unique_by: nil)
243
+ InsertAll.new(self, attributes, on_duplicate: :update, returning: returning, unique_by: unique_by).execute
244
+ end
245
+
58
246
  # Given an attributes hash, +instantiate+ returns a new instance of
59
247
  # the appropriate class. Accepts only keys as strings.
60
248
  #
@@ -165,6 +353,7 @@ module ActiveRecord
165
353
  end
166
354
 
167
355
  def _insert_record(values) # :nodoc:
356
+ primary_key = self.primary_key
168
357
  primary_key_value = nil
169
358
 
170
359
  if primary_key && Hash === values
@@ -235,20 +424,20 @@ module ActiveRecord
235
424
  # Returns true if this object hasn't been saved yet -- that is, a record
236
425
  # for the object doesn't exist in the database yet; otherwise, returns false.
237
426
  def new_record?
238
- sync_with_transaction_state
427
+ sync_with_transaction_state if @transaction_state&.finalized?
239
428
  @new_record
240
429
  end
241
430
 
242
431
  # Returns true if this object has been destroyed, otherwise returns false.
243
432
  def destroyed?
244
- sync_with_transaction_state
433
+ sync_with_transaction_state if @transaction_state&.finalized?
245
434
  @destroyed
246
435
  end
247
436
 
248
437
  # Returns true if the record is persisted, i.e. it's not a new record and it was
249
438
  # not destroyed, otherwise returns false.
250
439
  def persisted?
251
- sync_with_transaction_state
440
+ sync_with_transaction_state if @transaction_state&.finalized?
252
441
  !(@new_record || @destroyed)
253
442
  end
254
443
 
@@ -342,7 +531,6 @@ module ActiveRecord
342
531
  def destroy
343
532
  _raise_readonly_record_error if readonly?
344
533
  destroy_associations
345
- self.class.connection.add_transaction_record(self)
346
534
  @_trigger_destroy_callback = if persisted?
347
535
  destroy_row > 0
348
536
  else
@@ -380,7 +568,6 @@ module ActiveRecord
380
568
  became.send(:initialize)
381
569
  became.instance_variable_set("@attributes", @attributes)
382
570
  became.instance_variable_set("@mutations_from_database", @mutations_from_database ||= nil)
383
- became.instance_variable_set("@changed_attributes", attributes_changed_by_setter)
384
571
  became.instance_variable_set("@new_record", new_record?)
385
572
  became.instance_variable_set("@destroyed", destroyed?)
386
573
  became.errors.copy!(errors)
@@ -477,8 +664,13 @@ module ActiveRecord
477
664
  raise ActiveRecordError, "cannot update a new record" if new_record?
478
665
  raise ActiveRecordError, "cannot update a destroyed record" if destroyed?
479
666
 
667
+ attributes = attributes.transform_keys do |key|
668
+ name = key.to_s
669
+ self.class.attribute_aliases[name] || name
670
+ end
671
+
480
672
  attributes.each_key do |key|
481
- verify_readonly_attribute(key.to_s)
673
+ verify_readonly_attribute(key)
482
674
  end
483
675
 
484
676
  id_in_database = self.id_in_database
@@ -488,7 +680,7 @@ module ActiveRecord
488
680
 
489
681
  affected_rows = self.class._update_record(
490
682
  attributes,
491
- self.class.primary_key => id_in_database
683
+ @primary_key => id_in_database
492
684
  )
493
685
 
494
686
  affected_rows == 1
@@ -665,7 +857,9 @@ module ActiveRecord
665
857
  end
666
858
 
667
859
  attribute_names = timestamp_attributes_for_update_in_model
668
- attribute_names |= names.map(&:to_s)
860
+ attribute_names |= names.map!(&:to_s).map! { |name|
861
+ self.class.attribute_aliases[name] || name
862
+ }
669
863
 
670
864
  unless attribute_names.empty?
671
865
  affected_rows = _touch_row(attribute_names, time)
@@ -686,15 +880,14 @@ module ActiveRecord
686
880
  end
687
881
 
688
882
  def _delete_row
689
- self.class._delete_record(self.class.primary_key => id_in_database)
883
+ self.class._delete_record(@primary_key => id_in_database)
690
884
  end
691
885
 
692
886
  def _touch_row(attribute_names, time)
693
887
  time ||= current_time_from_proper_timezone
694
888
 
695
889
  attribute_names.each do |attr_name|
696
- write_attribute(attr_name, time)
697
- clear_attribute_change(attr_name)
890
+ _write_attribute(attr_name, time)
698
891
  end
699
892
 
700
893
  _update_row(attribute_names, "touch")
@@ -703,7 +896,7 @@ module ActiveRecord
703
896
  def _update_row(attribute_names, attempted_action = "update")
704
897
  self.class._update_record(
705
898
  attributes_with_values(attribute_names),
706
- self.class.primary_key => id_in_database
899
+ @primary_key => id_in_database
707
900
  )
708
901
  end
709
902
 
@@ -741,7 +934,7 @@ module ActiveRecord
741
934
  attributes_with_values(attribute_names)
742
935
  )
743
936
 
744
- self.id ||= new_id if self.class.primary_key
937
+ self.id ||= new_id if @primary_key
745
938
 
746
939
  @new_record = false
747
940