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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +471 -9
- data/README.rdoc +3 -1
- data/lib/active_record.rb +0 -1
- data/lib/active_record/association_relation.rb +15 -6
- data/lib/active_record/associations.rb +4 -3
- data/lib/active_record/associations/association.rb +10 -2
- data/lib/active_record/associations/builder/association.rb +14 -18
- data/lib/active_record/associations/builder/belongs_to.rb +5 -2
- data/lib/active_record/associations/builder/collection_association.rb +5 -15
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -1
- data/lib/active_record/associations/builder/has_many.rb +2 -0
- data/lib/active_record/associations/builder/has_one.rb +35 -1
- data/lib/active_record/associations/builder/singular_association.rb +2 -0
- data/lib/active_record/associations/collection_association.rb +6 -2
- data/lib/active_record/associations/collection_proxy.rb +2 -2
- data/lib/active_record/associations/has_many_through_association.rb +4 -11
- data/lib/active_record/associations/join_dependency.rb +14 -9
- data/lib/active_record/associations/join_dependency/join_association.rb +12 -3
- data/lib/active_record/associations/preloader.rb +13 -8
- data/lib/active_record/associations/preloader/association.rb +34 -30
- data/lib/active_record/associations/preloader/through_association.rb +48 -28
- data/lib/active_record/attribute_methods.rb +3 -53
- data/lib/active_record/attribute_methods/before_type_cast.rb +4 -1
- data/lib/active_record/attribute_methods/dirty.rb +47 -14
- data/lib/active_record/attribute_methods/primary_key.rb +7 -15
- data/lib/active_record/attribute_methods/query.rb +2 -3
- data/lib/active_record/attribute_methods/read.rb +3 -9
- data/lib/active_record/attribute_methods/write.rb +6 -12
- data/lib/active_record/attributes.rb +13 -0
- data/lib/active_record/autosave_association.rb +21 -7
- data/lib/active_record/base.rb +0 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +109 -11
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +8 -4
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +88 -61
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +6 -4
- data/lib/active_record/connection_adapters/abstract/quoting.rb +63 -6
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +4 -7
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +79 -22
- data/lib/active_record/connection_adapters/abstract/transaction.rb +12 -4
- data/lib/active_record/connection_adapters/abstract_adapter.rb +114 -34
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +78 -73
- data/lib/active_record/connection_adapters/column.rb +17 -13
- data/lib/active_record/connection_adapters/connection_specification.rb +2 -2
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +2 -2
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +48 -8
- data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +7 -5
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +10 -7
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +18 -5
- data/lib/active_record/connection_adapters/postgresql/column.rb +17 -30
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +8 -2
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +39 -2
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +34 -38
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +23 -27
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +68 -27
- data/lib/active_record/connection_adapters/schema_cache.rb +32 -14
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +120 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +38 -2
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +2 -2
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +66 -114
- data/lib/active_record/connection_handling.rb +31 -13
- data/lib/active_record/core.rb +23 -24
- data/lib/active_record/database_configurations.rb +73 -44
- data/lib/active_record/database_configurations/hash_config.rb +11 -11
- data/lib/active_record/database_configurations/url_config.rb +12 -12
- data/lib/active_record/dynamic_matchers.rb +1 -1
- data/lib/active_record/enum.rb +15 -0
- data/lib/active_record/errors.rb +1 -1
- data/lib/active_record/fixtures.rb +11 -6
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/insert_all.rb +179 -0
- data/lib/active_record/integration.rb +13 -1
- data/lib/active_record/internal_metadata.rb +5 -1
- data/lib/active_record/locking/optimistic.rb +3 -4
- data/lib/active_record/log_subscriber.rb +1 -1
- data/lib/active_record/middleware/database_selector.rb +3 -3
- data/lib/active_record/middleware/database_selector/resolver.rb +4 -6
- data/lib/active_record/migration.rb +62 -44
- data/lib/active_record/migration/command_recorder.rb +28 -14
- data/lib/active_record/migration/compatibility.rb +10 -0
- data/lib/active_record/model_schema.rb +3 -0
- data/lib/active_record/persistence.rb +206 -13
- data/lib/active_record/querying.rb +17 -12
- data/lib/active_record/railtie.rb +0 -1
- data/lib/active_record/railties/databases.rake +127 -25
- data/lib/active_record/reflection.rb +3 -3
- data/lib/active_record/relation.rb +99 -20
- data/lib/active_record/relation/calculations.rb +38 -40
- data/lib/active_record/relation/delegation.rb +22 -30
- data/lib/active_record/relation/finder_methods.rb +17 -12
- data/lib/active_record/relation/merger.rb +11 -16
- data/lib/active_record/relation/query_methods.rb +228 -76
- data/lib/active_record/relation/where_clause.rb +9 -5
- data/lib/active_record/sanitization.rb +33 -4
- data/lib/active_record/schema.rb +1 -1
- data/lib/active_record/schema_dumper.rb +10 -1
- data/lib/active_record/schema_migration.rb +1 -1
- data/lib/active_record/scoping/default.rb +6 -7
- data/lib/active_record/scoping/named.rb +3 -2
- data/lib/active_record/statement_cache.rb +2 -2
- data/lib/active_record/store.rb +48 -0
- data/lib/active_record/table_metadata.rb +9 -13
- data/lib/active_record/tasks/database_tasks.rb +109 -6
- data/lib/active_record/tasks/mysql_database_tasks.rb +3 -1
- data/lib/active_record/test_databases.rb +1 -16
- data/lib/active_record/test_fixtures.rb +1 -0
- data/lib/active_record/timestamp.rb +26 -16
- data/lib/active_record/touch_later.rb +4 -2
- data/lib/active_record/transactions.rb +56 -46
- data/lib/active_record/type_caster/connection.rb +16 -10
- data/lib/active_record/validations.rb +1 -0
- data/lib/active_record/validations/uniqueness.rb +3 -5
- data/lib/arel.rb +12 -5
- data/lib/arel/insert_manager.rb +3 -3
- data/lib/arel/nodes.rb +2 -1
- data/lib/arel/nodes/comment.rb +29 -0
- data/lib/arel/nodes/select_core.rb +16 -12
- data/lib/arel/nodes/unary.rb +1 -0
- data/lib/arel/nodes/values_list.rb +2 -17
- data/lib/arel/select_manager.rb +10 -10
- data/lib/arel/visitors/depth_first.rb +7 -2
- data/lib/arel/visitors/dot.rb +7 -2
- data/lib/arel/visitors/ibm_db.rb +13 -0
- data/lib/arel/visitors/informix.rb +6 -0
- data/lib/arel/visitors/mssql.rb +15 -1
- data/lib/arel/visitors/oracle12.rb +4 -5
- data/lib/arel/visitors/postgresql.rb +4 -10
- data/lib/arel/visitors/to_sql.rb +107 -131
- data/lib/arel/visitors/visitor.rb +9 -5
- data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -1
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
- data/lib/rails/generators/active_record/model/model_generator.rb +1 -1
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
- metadata +16 -12
- data/lib/active_record/collection_cache_key.rb +0 -53
- 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.
|
50
|
-
|
51
|
-
|
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,
|
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
|
491
|
-
#
|
492
|
-
#
|
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
|
530
|
+
class Current < Migration #:nodoc:
|
524
531
|
end
|
525
532
|
|
526
|
-
def self.inherited(subclass)
|
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/
|
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
|
572
|
-
attr_accessor :disable_ddl_transaction
|
578
|
+
attr_accessor :delegate #:nodoc:
|
579
|
+
attr_accessor :disable_ddl_transaction #:nodoc:
|
573
580
|
|
574
|
-
def nearest_delegate
|
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
|
-
|
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!
|
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)
|
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
|
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
|
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
|
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,
|
1079
|
+
Migrator.new(:up, migrations, schema_migration)
|
1063
1080
|
end
|
1064
1081
|
|
1065
1082
|
def get_all_versions
|
1066
|
-
if
|
1067
|
-
|
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 =
|
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 =
|
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
|
-
|
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(
|
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
|
-
|
1344
|
+
@schema_migration.delete_by(version: version.to_s)
|
1327
1345
|
else
|
1328
1346
|
migrated << version
|
1329
|
-
|
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
|
-
|
237
|
+
options = args.extract_options!
|
238
|
+
from_table, to_table = args
|
235
239
|
|
236
|
-
to_table
|
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 <<
|
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)
|
@@ -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
|
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
|
-
|
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(
|
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
|
-
|
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
|
-
|
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
|
937
|
+
self.id ||= new_id if @primary_key
|
745
938
|
|
746
939
|
@new_record = false
|
747
940
|
|