activerecord 7.0.0.alpha1 → 7.0.0.rc3
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 +516 -10
- data/lib/active_record/associations/association.rb +2 -8
- data/lib/active_record/associations/builder/collection_association.rb +9 -2
- data/lib/active_record/associations/collection_association.rb +10 -2
- data/lib/active_record/associations/preloader/association.rb +68 -48
- data/lib/active_record/associations/preloader/batch.rb +3 -6
- data/lib/active_record/associations/preloader/through_association.rb +19 -9
- data/lib/active_record/associations/preloader.rb +14 -24
- data/lib/active_record/associations/through_association.rb +2 -2
- data/lib/active_record/associations.rb +16 -3
- data/lib/active_record/asynchronous_queries_tracker.rb +3 -0
- data/lib/active_record/attribute_methods/dirty.rb +9 -1
- data/lib/active_record/attribute_methods.rb +7 -5
- data/lib/active_record/autosave_association.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +6 -26
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -2
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +18 -5
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +0 -17
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/quoting.rb +33 -70
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +4 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +9 -2
- data/lib/active_record/connection_adapters/abstract/transaction.rb +12 -19
- data/lib/active_record/connection_adapters/abstract_adapter.rb +37 -8
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/column.rb +4 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +2 -2
- data/lib/active_record/connection_adapters/mysql/quoting.rb +23 -24
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +1 -1
- data/lib/active_record/connection_adapters/pool_config.rb +7 -5
- data/lib/active_record/connection_adapters/postgresql/column.rb +17 -1
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +44 -44
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +21 -1
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +18 -1
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +15 -4
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +48 -5
- data/lib/active_record/connection_adapters/schema_cache.rb +3 -1
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +2 -2
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +15 -16
- data/lib/active_record/connection_handling.rb +31 -19
- data/lib/active_record/core.rb +13 -24
- data/lib/active_record/database_configurations/connection_url_resolver.rb +2 -1
- data/lib/active_record/database_configurations/database_config.rb +0 -9
- data/lib/active_record/database_configurations/hash_config.rb +40 -8
- data/lib/active_record/database_configurations.rb +2 -27
- data/lib/active_record/delegated_type.rb +19 -0
- data/lib/active_record/encryption/encrypted_attribute_type.rb +1 -1
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +1 -2
- data/lib/active_record/encryption/message_serializer.rb +11 -1
- data/lib/active_record/encryption/scheme.rb +1 -1
- data/lib/active_record/enum.rb +8 -1
- data/lib/active_record/errors.rb +1 -1
- data/lib/active_record/explain_registry.rb +11 -6
- data/lib/active_record/fixture_set/table_row.rb +1 -1
- data/lib/active_record/fixtures.rb +1 -9
- data/lib/active_record/future_result.rb +2 -2
- data/lib/active_record/gem_version.rb +1 -1
- data/lib/active_record/insert_all.rb +52 -15
- data/lib/active_record/integration.rb +3 -2
- data/lib/active_record/legacy_yaml_adapter.rb +2 -39
- data/lib/active_record/locking/pessimistic.rb +9 -3
- data/lib/active_record/log_subscriber.rb +8 -1
- data/lib/active_record/middleware/shard_selector.rb +60 -0
- data/lib/active_record/model_schema.rb +1 -28
- data/lib/active_record/nested_attributes.rb +11 -10
- data/lib/active_record/no_touching.rb +1 -1
- data/lib/active_record/persistence.rb +99 -21
- data/lib/active_record/query_logs.rb +18 -83
- data/lib/active_record/railtie.rb +11 -1
- data/lib/active_record/railties/databases.rake +4 -91
- data/lib/active_record/reflection.rb +22 -6
- data/lib/active_record/relation/calculations.rb +1 -10
- data/lib/active_record/relation/finder_methods.rb +0 -13
- data/lib/active_record/relation/query_methods.rb +5 -14
- data/lib/active_record/relation/record_fetch_warning.rb +5 -7
- data/lib/active_record/relation/where_clause.rb +2 -15
- data/lib/active_record/relation.rb +11 -13
- data/lib/active_record/result.rb +0 -5
- data/lib/active_record/runtime_registry.rb +10 -12
- data/lib/active_record/schema_dumper.rb +7 -0
- data/lib/active_record/scoping.rb +34 -22
- data/lib/active_record/suppressor.rb +11 -15
- data/lib/active_record/tasks/database_tasks.rb +18 -44
- data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -1
- data/lib/active_record/validations/uniqueness.rb +1 -1
- data/lib/active_record.rb +41 -33
- data/lib/arel/crud.rb +12 -2
- data/lib/arel/delete_manager.rb +16 -0
- data/lib/arel/filter_predications.rb +9 -0
- data/lib/arel/nodes/delete_statement.rb +5 -1
- data/lib/arel/nodes/filter.rb +10 -0
- data/lib/arel/nodes/function.rb +1 -0
- data/lib/arel/nodes/update_statement.rb +5 -1
- data/lib/arel/nodes.rb +1 -0
- data/lib/arel/predications.rb +10 -2
- data/lib/arel/update_manager.rb +16 -0
- data/lib/arel/visitors/mysql.rb +2 -1
- data/lib/arel/visitors/to_sql.rb +15 -0
- data/lib/arel.rb +1 -0
- metadata +17 -13
@@ -10,7 +10,14 @@ module ActiveRecord
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def visit_AddForeignKey(o)
|
13
|
-
super.dup.tap
|
13
|
+
super.dup.tap do |sql|
|
14
|
+
if o.deferrable
|
15
|
+
sql << " DEFERRABLE"
|
16
|
+
sql << " INITIALLY #{o.deferrable.to_s.upcase}" unless o.deferrable == true
|
17
|
+
end
|
18
|
+
|
19
|
+
sql << " NOT VALID" unless o.validate?
|
20
|
+
end
|
14
21
|
end
|
15
22
|
|
16
23
|
def visit_CheckConstraintDefinition(o)
|
@@ -61,6 +68,19 @@ module ActiveRecord
|
|
61
68
|
if options[:collation]
|
62
69
|
sql << " COLLATE \"#{options[:collation]}\""
|
63
70
|
end
|
71
|
+
|
72
|
+
if as = options[:as]
|
73
|
+
sql << " GENERATED ALWAYS AS (#{as})"
|
74
|
+
|
75
|
+
if options[:stored]
|
76
|
+
sql << " STORED"
|
77
|
+
else
|
78
|
+
raise ArgumentError, <<~MSG
|
79
|
+
PostgreSQL currently does not support VIRTUAL (not persisted) generated columns.
|
80
|
+
Specify 'stored: true' option for '#{options[:column].name}'
|
81
|
+
MSG
|
82
|
+
end
|
83
|
+
end
|
64
84
|
super
|
65
85
|
end
|
66
86
|
|
@@ -173,11 +173,19 @@ module ActiveRecord
|
|
173
173
|
# :method: xml
|
174
174
|
# :call-seq: xml(*names, **options)
|
175
175
|
|
176
|
+
##
|
177
|
+
# :method: timestamptz
|
178
|
+
# :call-seq: timestamptz(*names, **options)
|
179
|
+
|
180
|
+
##
|
181
|
+
# :method: enum
|
182
|
+
# :call-seq: enum(*names, **options)
|
183
|
+
|
176
184
|
included do
|
177
185
|
define_column_methods :bigserial, :bit, :bit_varying, :cidr, :citext, :daterange,
|
178
186
|
:hstore, :inet, :interval, :int4range, :int8range, :jsonb, :ltree, :macaddr,
|
179
187
|
:money, :numrange, :oid, :point, :line, :lseg, :box, :path, :polygon, :circle,
|
180
|
-
:serial, :tsrange, :tstzrange, :tsvector, :uuid, :xml, :timestamptz
|
188
|
+
:serial, :tsrange, :tstzrange, :tsvector, :uuid, :xml, :timestamptz, :enum
|
181
189
|
end
|
182
190
|
end
|
183
191
|
|
@@ -191,6 +199,15 @@ module ActiveRecord
|
|
191
199
|
@unlogged = ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.create_unlogged_tables
|
192
200
|
end
|
193
201
|
|
202
|
+
def new_column_definition(name, type, **options) # :nodoc:
|
203
|
+
case type
|
204
|
+
when :virtual
|
205
|
+
type = options[:type]
|
206
|
+
end
|
207
|
+
|
208
|
+
super
|
209
|
+
end
|
210
|
+
|
194
211
|
private
|
195
212
|
def aliased_types(name, fallback)
|
196
213
|
fallback
|
@@ -16,9 +16,30 @@ module ActiveRecord
|
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
|
+
def types(stream)
|
20
|
+
types = @connection.enum_types
|
21
|
+
if types.any?
|
22
|
+
stream.puts " # Custom types defined in this database."
|
23
|
+
stream.puts " # Note that some types may not work with other database engines. Be careful if changing database."
|
24
|
+
types.sort.each do |name, values|
|
25
|
+
stream.puts " create_enum #{name.inspect}, #{values.split(",").inspect}"
|
26
|
+
end
|
27
|
+
stream.puts
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
19
31
|
def prepare_column_options(column)
|
20
32
|
spec = super
|
21
33
|
spec[:array] = "true" if column.array?
|
34
|
+
|
35
|
+
if @connection.supports_virtual_columns? && column.virtual?
|
36
|
+
spec[:as] = extract_expression_for_virtual_column(column)
|
37
|
+
spec[:stored] = true
|
38
|
+
spec = { type: schema_type(column).inspect }.merge!(spec)
|
39
|
+
end
|
40
|
+
|
41
|
+
spec[:enum_type] = "\"#{column.sql_type}\"" if column.enum?
|
42
|
+
|
22
43
|
spec
|
23
44
|
end
|
24
45
|
|
@@ -43,6 +64,10 @@ module ActiveRecord
|
|
43
64
|
def schema_expression(column)
|
44
65
|
super unless column.serial?
|
45
66
|
end
|
67
|
+
|
68
|
+
def extract_expression_for_virtual_column(column)
|
69
|
+
column.default_function.inspect
|
70
|
+
end
|
46
71
|
end
|
47
72
|
end
|
48
73
|
end
|
@@ -483,7 +483,7 @@ module ActiveRecord
|
|
483
483
|
def foreign_keys(table_name)
|
484
484
|
scope = quoted_scope(table_name)
|
485
485
|
fk_info = exec_query(<<~SQL, "SCHEMA")
|
486
|
-
SELECT t2.oid::regclass::text AS to_table, a1.attname AS column, a2.attname AS primary_key, c.conname AS name, c.confupdtype AS on_update, c.confdeltype AS on_delete, c.convalidated AS valid
|
486
|
+
SELECT t2.oid::regclass::text AS to_table, a1.attname AS column, a2.attname AS primary_key, c.conname AS name, c.confupdtype AS on_update, c.confdeltype AS on_delete, c.convalidated AS valid, c.condeferrable AS deferrable, c.condeferred AS deferred
|
487
487
|
FROM pg_constraint c
|
488
488
|
JOIN pg_class t1 ON c.conrelid = t1.oid
|
489
489
|
JOIN pg_class t2 ON c.confrelid = t2.oid
|
@@ -505,6 +505,8 @@ module ActiveRecord
|
|
505
505
|
|
506
506
|
options[:on_delete] = extract_foreign_key_action(row["on_delete"])
|
507
507
|
options[:on_update] = extract_foreign_key_action(row["on_update"])
|
508
|
+
options[:deferrable] = extract_foreign_key_deferrable(row["deferrable"], row["deferred"])
|
509
|
+
|
508
510
|
options[:validate] = row["valid"]
|
509
511
|
|
510
512
|
ForeignKeyDefinition.new(table_name, row["to_table"], options)
|
@@ -542,7 +544,7 @@ module ActiveRecord
|
|
542
544
|
end
|
543
545
|
|
544
546
|
# Maps logical Rails types to PostgreSQL-specific data types.
|
545
|
-
def type_to_sql(type, limit: nil, precision: nil, scale: nil, array: nil, **) # :nodoc:
|
547
|
+
def type_to_sql(type, limit: nil, precision: nil, scale: nil, array: nil, enum_type: nil, **) # :nodoc:
|
546
548
|
sql = \
|
547
549
|
case type.to_s
|
548
550
|
when "binary"
|
@@ -566,6 +568,10 @@ module ActiveRecord
|
|
566
568
|
when 5..8; "bigint"
|
567
569
|
else raise ArgumentError, "No integer type has byte size #{limit}. Use a numeric with scale 0 instead."
|
568
570
|
end
|
571
|
+
when "enum"
|
572
|
+
raise ArgumentError "enum_type is required for enums" if enum_type.nil?
|
573
|
+
|
574
|
+
enum_type
|
569
575
|
else
|
570
576
|
super
|
571
577
|
end
|
@@ -654,7 +660,7 @@ module ActiveRecord
|
|
654
660
|
end
|
655
661
|
|
656
662
|
def new_column_from_field(table_name, field)
|
657
|
-
column_name, type, default, notnull, oid, fmod, collation, comment = field
|
663
|
+
column_name, type, default, notnull, oid, fmod, collation, comment, attgenerated = field
|
658
664
|
type_metadata = fetch_type_metadata(column_name, type, oid.to_i, fmod.to_i)
|
659
665
|
default_value = extract_value_from_default(default)
|
660
666
|
default_function = extract_default_function(default_value, default)
|
@@ -671,7 +677,8 @@ module ActiveRecord
|
|
671
677
|
default_function,
|
672
678
|
collation: collation,
|
673
679
|
comment: comment.presence,
|
674
|
-
serial: serial
|
680
|
+
serial: serial,
|
681
|
+
generated: attgenerated
|
675
682
|
)
|
676
683
|
end
|
677
684
|
|
@@ -711,6 +718,10 @@ module ActiveRecord
|
|
711
718
|
end
|
712
719
|
end
|
713
720
|
|
721
|
+
def extract_foreign_key_deferrable(deferrable, deferred)
|
722
|
+
deferrable && (deferred ? :deferred : true)
|
723
|
+
end
|
724
|
+
|
714
725
|
def add_column_for_alter(table_name, column_name, type, **options)
|
715
726
|
return super unless options.key?(:comment)
|
716
727
|
[super, Proc.new { change_column_comment(table_name, column_name, options[:comment]) }]
|
@@ -125,6 +125,7 @@ module ActiveRecord
|
|
125
125
|
string: { name: "character varying" },
|
126
126
|
text: { name: "text" },
|
127
127
|
integer: { name: "integer", limit: 4 },
|
128
|
+
bigint: { name: "bigint" },
|
128
129
|
float: { name: "float" },
|
129
130
|
decimal: { name: "decimal" },
|
130
131
|
datetime: {}, # set dynamically based on datetime_type
|
@@ -163,6 +164,7 @@ module ActiveRecord
|
|
163
164
|
money: { name: "money" },
|
164
165
|
interval: { name: "interval" },
|
165
166
|
oid: { name: "oid" },
|
167
|
+
enum: {} # special type https://www.postgresql.org/docs/current/datatype-enum.html
|
166
168
|
}
|
167
169
|
|
168
170
|
OID = PostgreSQL::OID # :nodoc:
|
@@ -181,7 +183,7 @@ module ActiveRecord
|
|
181
183
|
end
|
182
184
|
|
183
185
|
def supports_partitioned_indexes?
|
184
|
-
database_version >= 110_000
|
186
|
+
database_version >= 110_000 # >= 11.0
|
185
187
|
end
|
186
188
|
|
187
189
|
def supports_partial_index?
|
@@ -208,6 +210,10 @@ module ActiveRecord
|
|
208
210
|
true
|
209
211
|
end
|
210
212
|
|
213
|
+
def supports_deferrable_constraints?
|
214
|
+
true
|
215
|
+
end
|
216
|
+
|
211
217
|
def supports_views?
|
212
218
|
true
|
213
219
|
end
|
@@ -233,12 +239,16 @@ module ActiveRecord
|
|
233
239
|
end
|
234
240
|
|
235
241
|
def supports_insert_on_conflict?
|
236
|
-
database_version >= 90500
|
242
|
+
database_version >= 90500 # >= 9.5
|
237
243
|
end
|
238
244
|
alias supports_insert_on_duplicate_skip? supports_insert_on_conflict?
|
239
245
|
alias supports_insert_on_duplicate_update? supports_insert_on_conflict?
|
240
246
|
alias supports_insert_conflict_target? supports_insert_on_conflict?
|
241
247
|
|
248
|
+
def supports_virtual_columns?
|
249
|
+
database_version >= 120_000 # >= 12.0
|
250
|
+
end
|
251
|
+
|
242
252
|
def index_algorithms
|
243
253
|
{ concurrently: "CONCURRENTLY" }
|
244
254
|
end
|
@@ -388,7 +398,7 @@ module ActiveRecord
|
|
388
398
|
end
|
389
399
|
|
390
400
|
def supports_pgcrypto_uuid?
|
391
|
-
database_version >= 90400
|
401
|
+
database_version >= 90400 # >= 9.4
|
392
402
|
end
|
393
403
|
|
394
404
|
def supports_optimizer_hints?
|
@@ -444,6 +454,38 @@ module ActiveRecord
|
|
444
454
|
exec_query("SELECT extname FROM pg_extension", "SCHEMA").cast_values
|
445
455
|
end
|
446
456
|
|
457
|
+
# Returns a list of defined enum types, and their values.
|
458
|
+
def enum_types
|
459
|
+
query = <<~SQL
|
460
|
+
SELECT
|
461
|
+
type.typname AS name,
|
462
|
+
string_agg(enum.enumlabel, ',' ORDER BY enum.enumsortorder) AS value
|
463
|
+
FROM pg_enum AS enum
|
464
|
+
JOIN pg_type AS type
|
465
|
+
ON (type.oid = enum.enumtypid)
|
466
|
+
GROUP BY type.typname;
|
467
|
+
SQL
|
468
|
+
exec_query(query, "SCHEMA").cast_values
|
469
|
+
end
|
470
|
+
|
471
|
+
# Given a name and an array of values, creates an enum type.
|
472
|
+
def create_enum(name, values)
|
473
|
+
sql_values = values.map { |s| "'#{s}'" }.join(", ")
|
474
|
+
query = <<~SQL
|
475
|
+
DO $$
|
476
|
+
BEGIN
|
477
|
+
IF NOT EXISTS (
|
478
|
+
SELECT 1 FROM pg_type t
|
479
|
+
WHERE t.typname = '#{name}'
|
480
|
+
) THEN
|
481
|
+
CREATE TYPE \"#{name}\" AS ENUM (#{sql_values});
|
482
|
+
END IF;
|
483
|
+
END
|
484
|
+
$$;
|
485
|
+
SQL
|
486
|
+
exec_query(query)
|
487
|
+
end
|
488
|
+
|
447
489
|
# Returns the configured supported identifier length supported by PostgreSQL
|
448
490
|
def max_identifier_length
|
449
491
|
@max_identifier_length ||= query_value("SHOW max_identifier_length", "SCHEMA").to_i
|
@@ -489,7 +531,7 @@ module ActiveRecord
|
|
489
531
|
end
|
490
532
|
|
491
533
|
def check_version # :nodoc:
|
492
|
-
if database_version < 90300
|
534
|
+
if database_version < 90300 # < 9.3
|
493
535
|
raise "Your version of PostgreSQL (#{database_version}) is too old. Active Record supports PostgreSQL >= 9.3."
|
494
536
|
end
|
495
537
|
end
|
@@ -874,7 +916,8 @@ module ActiveRecord
|
|
874
916
|
query(<<~SQL, "SCHEMA")
|
875
917
|
SELECT a.attname, format_type(a.atttypid, a.atttypmod),
|
876
918
|
pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod,
|
877
|
-
c.collname, col_description(a.attrelid, a.attnum) AS comment
|
919
|
+
c.collname, col_description(a.attrelid, a.attnum) AS comment,
|
920
|
+
#{supports_virtual_columns? ? 'attgenerated' : quote('')} as attgenerated
|
878
921
|
FROM pg_attribute a
|
879
922
|
LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum
|
880
923
|
LEFT JOIN pg_type t ON a.atttypid = t.oid
|
@@ -208,7 +208,7 @@ module ActiveRecord
|
|
208
208
|
end
|
209
209
|
|
210
210
|
def reset_version!
|
211
|
-
@version = connection.
|
211
|
+
@version = connection.schema_version
|
212
212
|
end
|
213
213
|
|
214
214
|
def derive_columns_hash_and_deduplicate_values
|
@@ -239,6 +239,8 @@ module ActiveRecord
|
|
239
239
|
end
|
240
240
|
|
241
241
|
def open(filename)
|
242
|
+
FileUtils.mkdir_p(File.dirname(filename))
|
243
|
+
|
242
244
|
File.atomic_write(filename) do |file|
|
243
245
|
if File.extname(filename) == ".gz"
|
244
246
|
zipper = Zlib::GzipWriter.new file
|
@@ -78,7 +78,7 @@ module ActiveRecord
|
|
78
78
|
raise TransactionIsolationError, "SQLite3 only supports the `read_uncommitted` transaction isolation level" if isolation != :read_uncommitted
|
79
79
|
raise StandardError, "You need to enable the shared-cache mode in SQLite mode before attempting to change the transaction isolation level" unless shared_cache?
|
80
80
|
|
81
|
-
|
81
|
+
ActiveSupport::IsolatedExecutionState[:active_record_read_uncommitted] = @connection.get_first_value("PRAGMA read_uncommitted")
|
82
82
|
@connection.read_uncommitted = true
|
83
83
|
begin_db_transaction
|
84
84
|
end
|
@@ -108,7 +108,7 @@ module ActiveRecord
|
|
108
108
|
|
109
109
|
private
|
110
110
|
def reset_read_uncommitted
|
111
|
-
read_uncommitted =
|
111
|
+
read_uncommitted = ActiveSupport::IsolatedExecutionState[:active_record_read_uncommitted]
|
112
112
|
return unless read_uncommitted
|
113
113
|
|
114
114
|
@connection.read_uncommitted = read_uncommitted
|
@@ -45,6 +45,21 @@ module ActiveRecord
|
|
45
45
|
0
|
46
46
|
end
|
47
47
|
|
48
|
+
def type_cast(value) # :nodoc:
|
49
|
+
case value
|
50
|
+
when BigDecimal
|
51
|
+
value.to_f
|
52
|
+
when String
|
53
|
+
if value.encoding == Encoding::ASCII_8BIT
|
54
|
+
super(value.encode(Encoding::UTF_8))
|
55
|
+
else
|
56
|
+
super
|
57
|
+
end
|
58
|
+
else
|
59
|
+
super
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
48
63
|
def column_name_matcher
|
49
64
|
COLUMN_NAME
|
50
65
|
end
|
@@ -80,22 +95,6 @@ module ActiveRecord
|
|
80
95
|
/ix
|
81
96
|
|
82
97
|
private_constant :COLUMN_NAME, :COLUMN_NAME_WITH_ORDER
|
83
|
-
|
84
|
-
private
|
85
|
-
def _type_cast(value)
|
86
|
-
case value
|
87
|
-
when BigDecimal
|
88
|
-
value.to_f
|
89
|
-
when String
|
90
|
-
if value.encoding == Encoding::ASCII_8BIT
|
91
|
-
super(value.encode(Encoding::UTF_8))
|
92
|
-
else
|
93
|
-
super
|
94
|
-
end
|
95
|
-
else
|
96
|
-
super
|
97
|
-
end
|
98
|
-
end
|
99
98
|
end
|
100
99
|
end
|
101
100
|
end
|
@@ -153,10 +153,6 @@ module ActiveRecord
|
|
153
153
|
raise ArgumentError, "must provide a `shard` and/or `role`."
|
154
154
|
end
|
155
155
|
|
156
|
-
unless role
|
157
|
-
raise ArgumentError, "`connected_to` cannot accept a `shard` argument without a `role`."
|
158
|
-
end
|
159
|
-
|
160
156
|
with_role_and_shard(role, shard, prevent_writes, &blk)
|
161
157
|
end
|
162
158
|
|
@@ -186,7 +182,7 @@ module ActiveRecord
|
|
186
182
|
|
187
183
|
prevent_writes = true if role == ActiveRecord.reading_role
|
188
184
|
|
189
|
-
|
185
|
+
append_to_connected_to_stack(role: role, shard: shard, prevent_writes: prevent_writes, klasses: classes)
|
190
186
|
yield
|
191
187
|
ensure
|
192
188
|
connected_to_stack.pop
|
@@ -206,7 +202,26 @@ module ActiveRecord
|
|
206
202
|
|
207
203
|
prevent_writes = true if role == ActiveRecord.reading_role
|
208
204
|
|
209
|
-
|
205
|
+
append_to_connected_to_stack(role: role, shard: shard, prevent_writes: prevent_writes, klasses: [self])
|
206
|
+
end
|
207
|
+
|
208
|
+
# Prohibit swapping shards while inside of the passed block.
|
209
|
+
#
|
210
|
+
# In some cases you may want to be able to swap shards but not allow a
|
211
|
+
# nested call to connected_to or connected_to_many to swap again. This
|
212
|
+
# is useful in cases you're using sharding to provide per-request
|
213
|
+
# database isolation.
|
214
|
+
def prohibit_shard_swapping(enabled = true)
|
215
|
+
prev_value = ActiveSupport::IsolatedExecutionState[:active_record_prohibit_shard_swapping]
|
216
|
+
ActiveSupport::IsolatedExecutionState[:active_record_prohibit_shard_swapping] = enabled
|
217
|
+
yield
|
218
|
+
ensure
|
219
|
+
ActiveSupport::IsolatedExecutionState[:active_record_prohibit_shard_swapping] = prev_value
|
220
|
+
end
|
221
|
+
|
222
|
+
# Determine whether or not shard swapping is currently prohibited
|
223
|
+
def shard_swapping_prohibited?
|
224
|
+
ActiveSupport::IsolatedExecutionState[:active_record_prohibit_shard_swapping]
|
210
225
|
end
|
211
226
|
|
212
227
|
# Prevent writing to the database regardless of role.
|
@@ -279,17 +294,6 @@ module ActiveRecord
|
|
279
294
|
self == Base || application_record_class?
|
280
295
|
end
|
281
296
|
|
282
|
-
# Returns the configuration of the associated connection as a hash:
|
283
|
-
#
|
284
|
-
# ActiveRecord::Base.connection_config
|
285
|
-
# # => {pool: 5, timeout: 5000, database: "db/development.sqlite3", adapter: "sqlite3"}
|
286
|
-
#
|
287
|
-
# Please use only for reading.
|
288
|
-
def connection_config
|
289
|
-
connection_pool.db_config.configuration_hash
|
290
|
-
end
|
291
|
-
deprecate connection_config: "Use connection_db_config instead"
|
292
|
-
|
293
297
|
# Returns the db_config object from the associated connection:
|
294
298
|
#
|
295
299
|
# ActiveRecord::Base.connection_db_config
|
@@ -361,12 +365,12 @@ module ActiveRecord
|
|
361
365
|
if ActiveRecord.legacy_connection_handling
|
362
366
|
with_handler(role.to_sym) do
|
363
367
|
connection_handler.while_preventing_writes(prevent_writes) do
|
364
|
-
|
368
|
+
append_to_connected_to_stack(shard: shard, klasses: [self])
|
365
369
|
yield
|
366
370
|
end
|
367
371
|
end
|
368
372
|
else
|
369
|
-
|
373
|
+
append_to_connected_to_stack(role: role, shard: shard, prevent_writes: prevent_writes, klasses: [self])
|
370
374
|
return_value = yield
|
371
375
|
return_value.load if return_value.is_a? ActiveRecord::Relation
|
372
376
|
return_value
|
@@ -375,6 +379,14 @@ module ActiveRecord
|
|
375
379
|
self.connected_to_stack.pop
|
376
380
|
end
|
377
381
|
|
382
|
+
def append_to_connected_to_stack(entry)
|
383
|
+
if shard_swapping_prohibited? && entry[:shard].present?
|
384
|
+
raise ArgumentError, "cannot swap `shard` while shard swapping is prohibited."
|
385
|
+
end
|
386
|
+
|
387
|
+
connected_to_stack << entry
|
388
|
+
end
|
389
|
+
|
378
390
|
def swap_connection_handler(handler, &blk) # :nodoc:
|
379
391
|
old_handler, ActiveRecord::Base.connection_handler = ActiveRecord::Base.connection_handler, handler
|
380
392
|
return_value = yield
|
data/lib/active_record/core.rb
CHANGED
@@ -77,6 +77,8 @@ module ActiveRecord
|
|
77
77
|
|
78
78
|
class_attribute :default_shard, instance_writer: false
|
79
79
|
|
80
|
+
class_attribute :shard_selector, instance_accessor: false, default: nil
|
81
|
+
|
80
82
|
def self.application_record_class? # :nodoc:
|
81
83
|
if ActiveRecord.application_record_class
|
82
84
|
self == ActiveRecord.application_record_class
|
@@ -90,11 +92,11 @@ module ActiveRecord
|
|
90
92
|
self.filter_attributes = []
|
91
93
|
|
92
94
|
def self.connection_handler
|
93
|
-
|
95
|
+
ActiveSupport::IsolatedExecutionState[:active_record_connection_handler] || default_connection_handler
|
94
96
|
end
|
95
97
|
|
96
98
|
def self.connection_handler=(handler)
|
97
|
-
|
99
|
+
ActiveSupport::IsolatedExecutionState[:active_record_connection_handler] = handler
|
98
100
|
end
|
99
101
|
|
100
102
|
def self.connection_handlers
|
@@ -129,8 +131,8 @@ module ActiveRecord
|
|
129
131
|
end
|
130
132
|
|
131
133
|
def self.asynchronous_queries_tracker # :nodoc:
|
132
|
-
|
133
|
-
|
134
|
+
ActiveSupport::IsolatedExecutionState[:active_record_asynchronous_queries_tracker] ||= \
|
135
|
+
AsynchronousQueriesTracker.new
|
134
136
|
end
|
135
137
|
|
136
138
|
# Returns the symbol representing the current connected role.
|
@@ -148,7 +150,7 @@ module ActiveRecord
|
|
148
150
|
else
|
149
151
|
connected_to_stack.reverse_each do |hash|
|
150
152
|
return hash[:role] if hash[:role] && hash[:klasses].include?(Base)
|
151
|
-
return hash[:role] if hash[:role] && hash[:klasses].include?(
|
153
|
+
return hash[:role] if hash[:role] && hash[:klasses].include?(connection_class_for_self)
|
152
154
|
end
|
153
155
|
|
154
156
|
default_role
|
@@ -167,7 +169,7 @@ module ActiveRecord
|
|
167
169
|
def self.current_shard
|
168
170
|
connected_to_stack.reverse_each do |hash|
|
169
171
|
return hash[:shard] if hash[:shard] && hash[:klasses].include?(Base)
|
170
|
-
return hash[:shard] if hash[:shard] && hash[:klasses].include?(
|
172
|
+
return hash[:shard] if hash[:shard] && hash[:klasses].include?(connection_class_for_self)
|
171
173
|
end
|
172
174
|
|
173
175
|
default_shard
|
@@ -189,7 +191,7 @@ module ActiveRecord
|
|
189
191
|
else
|
190
192
|
connected_to_stack.reverse_each do |hash|
|
191
193
|
return hash[:prevent_writes] if !hash[:prevent_writes].nil? && hash[:klasses].include?(Base)
|
192
|
-
return hash[:prevent_writes] if !hash[:prevent_writes].nil? && hash[:klasses].include?(
|
194
|
+
return hash[:prevent_writes] if !hash[:prevent_writes].nil? && hash[:klasses].include?(connection_class_for_self)
|
193
195
|
end
|
194
196
|
|
195
197
|
false
|
@@ -197,11 +199,11 @@ module ActiveRecord
|
|
197
199
|
end
|
198
200
|
|
199
201
|
def self.connected_to_stack # :nodoc:
|
200
|
-
if connected_to_stack =
|
202
|
+
if connected_to_stack = ActiveSupport::IsolatedExecutionState[:active_record_connected_to_stack]
|
201
203
|
connected_to_stack
|
202
204
|
else
|
203
205
|
connected_to_stack = Concurrent::Array.new
|
204
|
-
|
206
|
+
ActiveSupport::IsolatedExecutionState[:active_record_connected_to_stack] = connected_to_stack
|
205
207
|
connected_to_stack
|
206
208
|
end
|
207
209
|
end
|
@@ -218,7 +220,7 @@ module ActiveRecord
|
|
218
220
|
self.connection_class
|
219
221
|
end
|
220
222
|
|
221
|
-
def self.
|
223
|
+
def self.connection_class_for_self # :nodoc:
|
222
224
|
klass = self
|
223
225
|
|
224
226
|
until klass == Base
|
@@ -229,14 +231,6 @@ module ActiveRecord
|
|
229
231
|
klass
|
230
232
|
end
|
231
233
|
|
232
|
-
def self.allow_unsafe_raw_sql # :nodoc:
|
233
|
-
ActiveSupport::Deprecation.warn("ActiveRecord::Base.allow_unsafe_raw_sql is deprecated and will be removed in Rails 7.0")
|
234
|
-
end
|
235
|
-
|
236
|
-
def self.allow_unsafe_raw_sql=(value) # :nodoc:
|
237
|
-
ActiveSupport::Deprecation.warn("ActiveRecord::Base.allow_unsafe_raw_sql= is deprecated and will be removed in Rails 7.0")
|
238
|
-
end
|
239
|
-
|
240
234
|
self.default_connection_handler = ConnectionAdapters::ConnectionHandler.new
|
241
235
|
self.default_role = ActiveRecord.writing_role
|
242
236
|
self.default_shard = :default
|
@@ -427,11 +421,6 @@ module ActiveRecord
|
|
427
421
|
@arel_table ||= Arel::Table.new(table_name, klass: self)
|
428
422
|
end
|
429
423
|
|
430
|
-
def arel_attribute(name, table = arel_table) # :nodoc:
|
431
|
-
table[name]
|
432
|
-
end
|
433
|
-
deprecate :arel_attribute
|
434
|
-
|
435
424
|
def predicate_builder # :nodoc:
|
436
425
|
@predicate_builder ||= PredicateBuilder.new(table_metadata)
|
437
426
|
end
|
@@ -497,7 +486,7 @@ module ActiveRecord
|
|
497
486
|
# post.init_with(coder)
|
498
487
|
# post.title # => 'hello world'
|
499
488
|
def init_with(coder, &block)
|
500
|
-
coder = LegacyYamlAdapter.convert(
|
489
|
+
coder = LegacyYamlAdapter.convert(coder)
|
501
490
|
attributes = self.class.yaml_encoder.decode(coder)
|
502
491
|
init_with_attributes(attributes, coder["new_record"], &block)
|
503
492
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "uri"
|
3
4
|
require "active_support/core_ext/enumerable"
|
4
5
|
|
5
6
|
module ActiveRecord
|
@@ -67,7 +68,7 @@ module ActiveRecord
|
|
67
68
|
database: uri.opaque
|
68
69
|
)
|
69
70
|
else
|
70
|
-
query_hash.
|
71
|
+
query_hash.reverse_merge(
|
71
72
|
adapter: @adapter,
|
72
73
|
username: uri.user,
|
73
74
|
password: uri.password,
|