activerecord 8.0.3 → 8.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +538 -512
- data/README.rdoc +1 -1
- data/lib/active_record/association_relation.rb +1 -1
- data/lib/active_record/associations/association.rb +1 -1
- data/lib/active_record/associations/belongs_to_association.rb +2 -0
- data/lib/active_record/associations/builder/association.rb +16 -5
- data/lib/active_record/associations/builder/belongs_to.rb +17 -4
- data/lib/active_record/associations/builder/collection_association.rb +7 -3
- data/lib/active_record/associations/builder/has_one.rb +1 -1
- data/lib/active_record/associations/builder/singular_association.rb +33 -5
- data/lib/active_record/associations/collection_proxy.rb +22 -4
- data/lib/active_record/associations/deprecation.rb +88 -0
- data/lib/active_record/associations/errors.rb +3 -0
- data/lib/active_record/associations/join_dependency.rb +2 -0
- data/lib/active_record/associations/preloader/branch.rb +1 -0
- data/lib/active_record/associations.rb +159 -21
- data/lib/active_record/attribute_methods/serialization.rb +16 -3
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +10 -2
- data/lib/active_record/attributes.rb +3 -0
- data/lib/active_record/autosave_association.rb +1 -1
- data/lib/active_record/base.rb +0 -2
- data/lib/active_record/coders/json.rb +14 -5
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +1 -3
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +16 -3
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +51 -12
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +405 -72
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +55 -40
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +19 -3
- data/lib/active_record/connection_adapters/abstract/quoting.rb +15 -24
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +7 -2
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +26 -34
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +85 -22
- data/lib/active_record/connection_adapters/abstract/transaction.rb +25 -3
- data/lib/active_record/connection_adapters/abstract_adapter.rb +86 -20
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +43 -13
- data/lib/active_record/connection_adapters/column.rb +17 -4
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +4 -4
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +2 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +42 -5
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +26 -4
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +27 -22
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +1 -2
- data/lib/active_record/connection_adapters/postgresql/column.rb +4 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +17 -15
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +21 -10
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +8 -6
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +8 -21
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +67 -31
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +81 -48
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +23 -7
- data/lib/active_record/connection_adapters/schema_cache.rb +2 -2
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +37 -25
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +0 -8
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +4 -13
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +56 -32
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +4 -3
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +1 -1
- data/lib/active_record/connection_adapters.rb +1 -0
- data/lib/active_record/connection_handling.rb +14 -9
- data/lib/active_record/core.rb +5 -4
- data/lib/active_record/counter_cache.rb +33 -8
- data/lib/active_record/database_configurations/database_config.rb +5 -1
- data/lib/active_record/database_configurations/hash_config.rb +53 -9
- data/lib/active_record/database_configurations/url_config.rb +13 -3
- data/lib/active_record/database_configurations.rb +7 -3
- data/lib/active_record/delegated_type.rb +1 -1
- data/lib/active_record/dynamic_matchers.rb +54 -69
- data/lib/active_record/encryption/encryptable_record.rb +4 -4
- data/lib/active_record/encryption/encrypted_attribute_type.rb +1 -1
- data/lib/active_record/encryption/encryptor.rb +12 -0
- data/lib/active_record/encryption/scheme.rb +1 -1
- data/lib/active_record/enum.rb +24 -8
- data/lib/active_record/errors.rb +20 -4
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/explain_registry.rb +51 -2
- data/lib/active_record/filter_attribute_handler.rb +73 -0
- data/lib/active_record/fixtures.rb +2 -2
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/inheritance.rb +1 -1
- data/lib/active_record/insert_all.rb +12 -7
- data/lib/active_record/locking/optimistic.rb +7 -0
- data/lib/active_record/locking/pessimistic.rb +5 -0
- data/lib/active_record/log_subscriber.rb +2 -6
- data/lib/active_record/middleware/shard_selector.rb +34 -17
- data/lib/active_record/migration/command_recorder.rb +14 -1
- data/lib/active_record/migration/compatibility.rb +34 -24
- data/lib/active_record/migration/default_schema_versions_formatter.rb +30 -0
- data/lib/active_record/migration.rb +26 -16
- data/lib/active_record/model_schema.rb +36 -10
- data/lib/active_record/nested_attributes.rb +2 -0
- data/lib/active_record/persistence.rb +34 -3
- data/lib/active_record/query_cache.rb +22 -15
- data/lib/active_record/query_logs.rb +3 -7
- data/lib/active_record/railtie.rb +32 -3
- data/lib/active_record/railties/controller_runtime.rb +11 -6
- data/lib/active_record/railties/databases.rake +15 -3
- data/lib/active_record/railties/job_checkpoints.rb +15 -0
- data/lib/active_record/railties/job_runtime.rb +10 -11
- data/lib/active_record/reflection.rb +35 -0
- data/lib/active_record/relation/batches.rb +25 -11
- data/lib/active_record/relation/calculations.rb +20 -9
- data/lib/active_record/relation/delegation.rb +0 -1
- data/lib/active_record/relation/finder_methods.rb +27 -11
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +9 -9
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +7 -7
- data/lib/active_record/relation/predicate_builder.rb +9 -7
- data/lib/active_record/relation/query_attribute.rb +3 -1
- data/lib/active_record/relation/query_methods.rb +40 -29
- data/lib/active_record/relation/where_clause.rb +1 -8
- data/lib/active_record/relation.rb +24 -12
- data/lib/active_record/result.rb +44 -21
- data/lib/active_record/runtime_registry.rb +41 -58
- data/lib/active_record/sanitization.rb +2 -0
- data/lib/active_record/schema_dumper.rb +12 -10
- data/lib/active_record/scoping.rb +0 -1
- data/lib/active_record/signed_id.rb +43 -15
- data/lib/active_record/statement_cache.rb +13 -9
- data/lib/active_record/store.rb +44 -19
- data/lib/active_record/structured_event_subscriber.rb +85 -0
- data/lib/active_record/table_metadata.rb +5 -20
- data/lib/active_record/tasks/abstract_tasks.rb +76 -0
- data/lib/active_record/tasks/database_tasks.rb +25 -34
- data/lib/active_record/tasks/mysql_database_tasks.rb +3 -40
- data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -39
- data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -26
- data/lib/active_record/test_databases.rb +14 -4
- data/lib/active_record/test_fixtures.rb +27 -2
- data/lib/active_record/testing/query_assertions.rb +8 -2
- data/lib/active_record/timestamp.rb +4 -2
- data/lib/active_record/transaction.rb +2 -5
- data/lib/active_record/transactions.rb +32 -10
- data/lib/active_record/type/hash_lookup_type_map.rb +2 -1
- data/lib/active_record/type/internal/timezone.rb +7 -0
- data/lib/active_record/type/json.rb +15 -2
- data/lib/active_record/type/serialized.rb +11 -4
- data/lib/active_record/type/type_map.rb +1 -1
- data/lib/active_record/type_caster/connection.rb +2 -1
- data/lib/active_record/validations/associated.rb +1 -1
- data/lib/active_record.rb +65 -3
- data/lib/arel/alias_predication.rb +2 -0
- data/lib/arel/crud.rb +6 -11
- data/lib/arel/nodes/count.rb +2 -2
- data/lib/arel/nodes/function.rb +4 -10
- data/lib/arel/nodes/named_function.rb +2 -2
- data/lib/arel/nodes/node.rb +1 -1
- data/lib/arel/nodes.rb +0 -2
- data/lib/arel/select_manager.rb +7 -2
- data/lib/arel/visitors/dot.rb +0 -3
- data/lib/arel/visitors/postgresql.rb +55 -0
- data/lib/arel/visitors/sqlite.rb +55 -8
- data/lib/arel/visitors/to_sql.rb +3 -21
- data/lib/arel.rb +3 -1
- data/lib/rails/generators/active_record/application_record/USAGE +1 -1
- metadata +14 -10
- data/lib/active_record/explain_subscriber.rb +0 -34
- data/lib/active_record/normalization.rb +0 -163
|
@@ -55,12 +55,15 @@ module ActiveRecord
|
|
|
55
55
|
# can be used to query the database repeatedly.
|
|
56
56
|
def cacheable_query(klass, arel) # :nodoc:
|
|
57
57
|
if prepared_statements
|
|
58
|
+
collector = collector()
|
|
59
|
+
collector.retryable = true
|
|
58
60
|
sql, binds = visitor.compile(arel.ast, collector)
|
|
59
|
-
query = klass.query(sql)
|
|
61
|
+
query = klass.query(sql, retryable: collector.retryable)
|
|
60
62
|
else
|
|
61
63
|
collector = klass.partial_query_collector
|
|
64
|
+
collector.retryable = true
|
|
62
65
|
parts, binds = visitor.compile(arel.ast, collector)
|
|
63
|
-
query = klass.partial_query(parts)
|
|
66
|
+
query = klass.partial_query(parts, retryable: collector.retryable)
|
|
64
67
|
end
|
|
65
68
|
[query, binds]
|
|
66
69
|
end
|
|
@@ -110,8 +113,8 @@ module ActiveRecord
|
|
|
110
113
|
query(...).map(&:first)
|
|
111
114
|
end
|
|
112
115
|
|
|
113
|
-
def query(
|
|
114
|
-
internal_exec_query(
|
|
116
|
+
def query(sql, name = nil, allow_retry: true, materialize_transactions: true) # :nodoc:
|
|
117
|
+
internal_exec_query(sql, name, allow_retry:, materialize_transactions:).rows
|
|
115
118
|
end
|
|
116
119
|
|
|
117
120
|
# Determines whether the SQL statement is a write query.
|
|
@@ -350,8 +353,24 @@ module ActiveRecord
|
|
|
350
353
|
# isolation level.
|
|
351
354
|
# :args: (requires_new: nil, isolation: nil, &block)
|
|
352
355
|
def transaction(requires_new: nil, isolation: nil, joinable: true, &block)
|
|
356
|
+
# If we're running inside the single, non-joinable transaction that
|
|
357
|
+
# ActiveRecord::TestFixtures starts around each example (depth == 1),
|
|
358
|
+
# an `isolation:` hint must be validated then ignored so that the
|
|
359
|
+
# adapter isn't asked to change the isolation level mid-transaction.
|
|
360
|
+
if isolation && !requires_new && open_transactions == 1 && !current_transaction.joinable?
|
|
361
|
+
iso = isolation.to_sym
|
|
362
|
+
|
|
363
|
+
unless transaction_isolation_levels.include?(iso)
|
|
364
|
+
raise ActiveRecord::TransactionIsolationError,
|
|
365
|
+
"invalid transaction isolation level: #{iso.inspect}"
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
current_transaction.isolation = iso
|
|
369
|
+
isolation = nil
|
|
370
|
+
end
|
|
371
|
+
|
|
353
372
|
if !requires_new && current_transaction.joinable?
|
|
354
|
-
if isolation
|
|
373
|
+
if isolation && current_transaction.isolation != isolation
|
|
355
374
|
raise ActiveRecord::TransactionIsolationError, "cannot set isolation when joining a transaction"
|
|
356
375
|
end
|
|
357
376
|
yield current_transaction.user_transaction
|
|
@@ -369,10 +388,10 @@ module ActiveRecord
|
|
|
369
388
|
:disable_lazy_transactions!, :enable_lazy_transactions!, :dirty_current_transaction,
|
|
370
389
|
to: :transaction_manager
|
|
371
390
|
|
|
372
|
-
def
|
|
391
|
+
def mark_transaction_written # :nodoc:
|
|
373
392
|
transaction = current_transaction
|
|
374
393
|
if transaction.open?
|
|
375
|
-
transaction.written ||=
|
|
394
|
+
transaction.written ||= true
|
|
376
395
|
end
|
|
377
396
|
end
|
|
378
397
|
|
|
@@ -417,13 +436,16 @@ module ActiveRecord
|
|
|
417
436
|
end
|
|
418
437
|
end
|
|
419
438
|
|
|
439
|
+
TRANSACTION_ISOLATION_LEVELS = {
|
|
440
|
+
read_uncommitted: "READ UNCOMMITTED",
|
|
441
|
+
read_committed: "READ COMMITTED",
|
|
442
|
+
repeatable_read: "REPEATABLE READ",
|
|
443
|
+
serializable: "SERIALIZABLE"
|
|
444
|
+
}.freeze
|
|
445
|
+
private_constant :TRANSACTION_ISOLATION_LEVELS
|
|
446
|
+
|
|
420
447
|
def transaction_isolation_levels
|
|
421
|
-
|
|
422
|
-
read_uncommitted: "READ UNCOMMITTED",
|
|
423
|
-
read_committed: "READ COMMITTED",
|
|
424
|
-
repeatable_read: "REPEATABLE READ",
|
|
425
|
-
serializable: "SERIALIZABLE"
|
|
426
|
-
}
|
|
448
|
+
TRANSACTION_ISOLATION_LEVELS
|
|
427
449
|
end
|
|
428
450
|
|
|
429
451
|
# Begins the transaction with the isolation level set. Raises an error by
|
|
@@ -499,20 +521,6 @@ module ActiveRecord
|
|
|
499
521
|
"DEFAULT VALUES"
|
|
500
522
|
end
|
|
501
523
|
|
|
502
|
-
# Sanitizes the given LIMIT parameter in order to prevent SQL injection.
|
|
503
|
-
#
|
|
504
|
-
# The +limit+ may be anything that can evaluate to a string via #to_s. It
|
|
505
|
-
# should look like an integer, or an Arel SQL literal.
|
|
506
|
-
#
|
|
507
|
-
# Returns Integer and Arel::Nodes::SqlLiteral limits as is.
|
|
508
|
-
def sanitize_limit(limit)
|
|
509
|
-
if limit.is_a?(Integer) || limit.is_a?(Arel::Nodes::SqlLiteral)
|
|
510
|
-
limit
|
|
511
|
-
else
|
|
512
|
-
Integer(limit)
|
|
513
|
-
end
|
|
514
|
-
end
|
|
515
|
-
|
|
516
524
|
# Fixture value is quoted by Arel, however scalar values
|
|
517
525
|
# are not quotable. In this case we want to convert
|
|
518
526
|
# the column value to YAML.
|
|
@@ -547,13 +555,22 @@ module ActiveRecord
|
|
|
547
555
|
cast_result(internal_execute(...))
|
|
548
556
|
end
|
|
549
557
|
|
|
558
|
+
def default_insert_value(column) # :nodoc:
|
|
559
|
+
DEFAULT_INSERT_VALUE
|
|
560
|
+
end
|
|
561
|
+
|
|
550
562
|
private
|
|
563
|
+
DEFAULT_INSERT_VALUE = Arel.sql("DEFAULT").freeze
|
|
564
|
+
private_constant :DEFAULT_INSERT_VALUE
|
|
565
|
+
|
|
551
566
|
# Lowest level way to execute a query. Doesn't check for illegal writes, doesn't annotate queries, yields a native result object.
|
|
552
567
|
def raw_execute(sql, name = nil, binds = [], prepare: false, async: false, allow_retry: false, materialize_transactions: true, batch: false)
|
|
553
568
|
type_casted_binds = type_casted_binds(binds)
|
|
554
|
-
log(sql, name, binds, type_casted_binds, async: async) do |notification_payload|
|
|
569
|
+
log(sql, name, binds, type_casted_binds, async: async, allow_retry: allow_retry) do |notification_payload|
|
|
555
570
|
with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
|
|
556
|
-
perform_query(conn, sql, binds, type_casted_binds, prepare: prepare, notification_payload: notification_payload, batch: batch)
|
|
571
|
+
result = perform_query(conn, sql, binds, type_casted_binds, prepare: prepare, notification_payload: notification_payload, batch: batch)
|
|
572
|
+
handle_warnings(result, sql)
|
|
573
|
+
result
|
|
557
574
|
end
|
|
558
575
|
end
|
|
559
576
|
end
|
|
@@ -562,6 +579,9 @@ module ActiveRecord
|
|
|
562
579
|
raise NotImplementedError
|
|
563
580
|
end
|
|
564
581
|
|
|
582
|
+
def handle_warnings(raw_result, sql)
|
|
583
|
+
end
|
|
584
|
+
|
|
565
585
|
# Receive a native adapter result object and returns an ActiveRecord::Result object.
|
|
566
586
|
def cast_result(raw_result)
|
|
567
587
|
raise NotImplementedError
|
|
@@ -572,8 +592,10 @@ module ActiveRecord
|
|
|
572
592
|
end
|
|
573
593
|
|
|
574
594
|
def preprocess_query(sql)
|
|
575
|
-
|
|
576
|
-
|
|
595
|
+
if write_query?(sql)
|
|
596
|
+
ensure_writes_are_allowed(sql)
|
|
597
|
+
mark_transaction_written
|
|
598
|
+
end
|
|
577
599
|
|
|
578
600
|
# We call tranformers after the write checks so we don't add extra parsing work.
|
|
579
601
|
# This means we assume no transformer whille change a read for a write
|
|
@@ -597,13 +619,6 @@ module ActiveRecord
|
|
|
597
619
|
end
|
|
598
620
|
end
|
|
599
621
|
|
|
600
|
-
DEFAULT_INSERT_VALUE = Arel.sql("DEFAULT").freeze
|
|
601
|
-
private_constant :DEFAULT_INSERT_VALUE
|
|
602
|
-
|
|
603
|
-
def default_insert_value(column)
|
|
604
|
-
DEFAULT_INSERT_VALUE
|
|
605
|
-
end
|
|
606
|
-
|
|
607
622
|
def build_fixture_sql(fixtures, table_name)
|
|
608
623
|
columns = schema_cache.columns_hash(table_name).reject { |_, column| supports_virtual_columns? && column.virtual? }
|
|
609
624
|
|
|
@@ -617,8 +632,8 @@ module ActiveRecord
|
|
|
617
632
|
|
|
618
633
|
columns.map do |name, column|
|
|
619
634
|
if fixture.key?(name)
|
|
620
|
-
|
|
621
|
-
with_yaml_fallback(
|
|
635
|
+
# TODO: Remove fetch_cast_type and the need for connection after we release 8.1.
|
|
636
|
+
with_yaml_fallback(column.fetch_cast_type(self).serialize(fixture[name]))
|
|
622
637
|
else
|
|
623
638
|
default_insert_value(column)
|
|
624
639
|
end
|
|
@@ -13,8 +13,6 @@ module ActiveRecord
|
|
|
13
13
|
dirties_query_cache base, :exec_query, :execute, :create, :insert, :update, :delete, :truncate,
|
|
14
14
|
:truncate_tables, :rollback_to_savepoint, :rollback_db_transaction, :restart_db_transaction,
|
|
15
15
|
:exec_insert_all
|
|
16
|
-
|
|
17
|
-
base.set_callback :checkin, :after, :unset_query_cache!
|
|
18
16
|
end
|
|
19
17
|
|
|
20
18
|
def dirties_query_cache(base, *method_names)
|
|
@@ -31,6 +29,18 @@ module ActiveRecord
|
|
|
31
29
|
end
|
|
32
30
|
end
|
|
33
31
|
|
|
32
|
+
# This is the actual query cache store.
|
|
33
|
+
#
|
|
34
|
+
# It has an internal hash whose keys are either SQL strings, or arrays of
|
|
35
|
+
# two elements [SQL string, binds], if there are binds. The hash values
|
|
36
|
+
# are their corresponding ActiveRecord::Result objects.
|
|
37
|
+
#
|
|
38
|
+
# Keeping the hash size under max size is achieved with LRU eviction.
|
|
39
|
+
#
|
|
40
|
+
# The store gets passed a version object, which is shared among the query
|
|
41
|
+
# cache stores of a given connection pool (see ConnectionPoolConfiguration
|
|
42
|
+
# down below). The version value may be externally changed as a way to
|
|
43
|
+
# signal cache invalidation, that is why all methods have a guard for it.
|
|
34
44
|
class Store # :nodoc:
|
|
35
45
|
attr_accessor :enabled, :dirties
|
|
36
46
|
alias_method :enabled?, :enabled
|
|
@@ -94,6 +104,12 @@ module ActiveRecord
|
|
|
94
104
|
end
|
|
95
105
|
end
|
|
96
106
|
|
|
107
|
+
# Each connection pool has one of these registries. They map execution
|
|
108
|
+
# contexts to query cache stores.
|
|
109
|
+
#
|
|
110
|
+
# The keys of the internal map are threads or fibers (whatever
|
|
111
|
+
# ActiveSupport::IsolatedExecutionState.context returns), and their
|
|
112
|
+
# associated values are their respective query cache stores.
|
|
97
113
|
class QueryCacheRegistry # :nodoc:
|
|
98
114
|
def initialize
|
|
99
115
|
@mutex = Mutex.new
|
|
@@ -250,7 +266,7 @@ module ActiveRecord
|
|
|
250
266
|
# If arel is locked this is a SELECT ... FOR UPDATE or somesuch.
|
|
251
267
|
# Such queries should not be cached.
|
|
252
268
|
if query_cache_enabled && !(arel.respond_to?(:locked) && arel.locked)
|
|
253
|
-
sql, binds, preparable, allow_retry = to_sql_and_binds(arel, binds, preparable)
|
|
269
|
+
sql, binds, preparable, allow_retry = to_sql_and_binds(arel, binds, preparable, allow_retry)
|
|
254
270
|
|
|
255
271
|
if async
|
|
256
272
|
result = lookup_sql_cache(sql, name, binds) || super(sql, name, binds, preparable: preparable, async: async, allow_retry: allow_retry)
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "active_support/core_ext/big_decimal/conversions"
|
|
4
|
-
require "active_support/multibyte/chars"
|
|
5
4
|
|
|
6
5
|
module ActiveRecord
|
|
7
6
|
module ConnectionAdapters # :nodoc:
|
|
@@ -84,7 +83,8 @@ module ActiveRecord
|
|
|
84
83
|
when Type::Time::Value then "'#{quoted_time(value)}'"
|
|
85
84
|
when Date, Time then "'#{quoted_date(value)}'"
|
|
86
85
|
when Class then "'#{value}'"
|
|
87
|
-
else
|
|
86
|
+
else
|
|
87
|
+
raise TypeError, "can't quote #{value.class.name}"
|
|
88
88
|
end
|
|
89
89
|
end
|
|
90
90
|
|
|
@@ -93,7 +93,7 @@ module ActiveRecord
|
|
|
93
93
|
# to a String.
|
|
94
94
|
def type_cast(value)
|
|
95
95
|
case value
|
|
96
|
-
when Symbol,
|
|
96
|
+
when Symbol, Type::Binary::Data, ActiveSupport::Multibyte::Chars
|
|
97
97
|
value.to_s
|
|
98
98
|
when true then unquoted_true
|
|
99
99
|
when false then unquoted_false
|
|
@@ -102,7 +102,8 @@ module ActiveRecord
|
|
|
102
102
|
when nil, Numeric, String then value
|
|
103
103
|
when Type::Time::Value then quoted_time(value)
|
|
104
104
|
when Date, Time then quoted_date(value)
|
|
105
|
-
else
|
|
105
|
+
else
|
|
106
|
+
raise TypeError, "can't cast #{value.class.name}"
|
|
106
107
|
end
|
|
107
108
|
end
|
|
108
109
|
|
|
@@ -113,19 +114,6 @@ module ActiveRecord
|
|
|
113
114
|
value
|
|
114
115
|
end
|
|
115
116
|
|
|
116
|
-
# If you are having to call this function, you are likely doing something
|
|
117
|
-
# wrong. The column does not have sufficient type information if the user
|
|
118
|
-
# provided a custom type on the class level either explicitly (via
|
|
119
|
-
# Attributes::ClassMethods#attribute) or implicitly (via
|
|
120
|
-
# AttributeMethods::Serialization::ClassMethods#serialize, +time_zone_aware_attributes+).
|
|
121
|
-
# In almost all cases, the sql type should only be used to change quoting behavior, when the primitive to
|
|
122
|
-
# represent the type doesn't sufficiently reflect the differences
|
|
123
|
-
# (varchar vs binary) for example. The type used to get this primitive
|
|
124
|
-
# should have been provided before reaching the connection adapter.
|
|
125
|
-
def lookup_cast_type_from_column(column) # :nodoc:
|
|
126
|
-
lookup_cast_type(column.sql_type)
|
|
127
|
-
end
|
|
128
|
-
|
|
129
117
|
# Quotes a string, escaping any ' (single quote) and \ (backslash)
|
|
130
118
|
# characters.
|
|
131
119
|
def quote_string(s)
|
|
@@ -158,7 +146,9 @@ module ActiveRecord
|
|
|
158
146
|
if value.is_a?(Proc)
|
|
159
147
|
value.call
|
|
160
148
|
else
|
|
161
|
-
|
|
149
|
+
# TODO: Remove fetch_cast_type and the need for connection after we release 8.1.
|
|
150
|
+
cast_type = column.fetch_cast_type(self)
|
|
151
|
+
value = cast_type.serialize(value)
|
|
162
152
|
quote(value)
|
|
163
153
|
end
|
|
164
154
|
end
|
|
@@ -208,10 +198,10 @@ module ActiveRecord
|
|
|
208
198
|
end
|
|
209
199
|
|
|
210
200
|
def sanitize_as_sql_comment(value) # :nodoc:
|
|
211
|
-
# Sanitize a string to appear within
|
|
201
|
+
# Sanitize a string to appear within an SQL comment
|
|
212
202
|
# For compatibility, this also surrounding "/*+", "/*", and "*/"
|
|
213
203
|
# charcacters, possibly with single surrounding space.
|
|
214
|
-
# Then follows that by replacing any internal "*/" or "
|
|
204
|
+
# Then follows that by replacing any internal "*/" or "/*" with
|
|
215
205
|
# "* /" or "/ *"
|
|
216
206
|
comment = value.to_s.dup
|
|
217
207
|
comment.gsub!(%r{\A\s*/\*\+?\s?|\s?\*/\s*\Z}, "")
|
|
@@ -220,6 +210,11 @@ module ActiveRecord
|
|
|
220
210
|
comment
|
|
221
211
|
end
|
|
222
212
|
|
|
213
|
+
def lookup_cast_type(sql_type) # :nodoc:
|
|
214
|
+
# TODO: Make this method private after we release 8.1.
|
|
215
|
+
type_map.lookup(sql_type)
|
|
216
|
+
end
|
|
217
|
+
|
|
223
218
|
private
|
|
224
219
|
def type_casted_binds(binds)
|
|
225
220
|
binds&.map do |value|
|
|
@@ -230,10 +225,6 @@ module ActiveRecord
|
|
|
230
225
|
end
|
|
231
226
|
end
|
|
232
227
|
end
|
|
233
|
-
|
|
234
|
-
def lookup_cast_type(sql_type)
|
|
235
|
-
type_map.lookup(sql_type)
|
|
236
|
-
end
|
|
237
228
|
end
|
|
238
229
|
end
|
|
239
230
|
end
|
|
@@ -17,7 +17,7 @@ module ActiveRecord
|
|
|
17
17
|
:options_include_default?, :supports_indexes_in_create?, :use_foreign_keys?,
|
|
18
18
|
:quoted_columns_for_index, :supports_partial_index?, :supports_check_constraints?,
|
|
19
19
|
:supports_index_include?, :supports_exclusion_constraints?, :supports_unique_constraints?,
|
|
20
|
-
:supports_nulls_not_distinct?,
|
|
20
|
+
:supports_nulls_not_distinct?, :lookup_cast_type,
|
|
21
21
|
to: :@conn, private: true
|
|
22
22
|
|
|
23
23
|
private
|
|
@@ -148,7 +148,7 @@ module ActiveRecord
|
|
|
148
148
|
end
|
|
149
149
|
|
|
150
150
|
def add_column_options!(sql, options)
|
|
151
|
-
sql << " DEFAULT #{
|
|
151
|
+
sql << " DEFAULT #{quote_default_expression_for_column_definition(options[:default], options[:column])}" if options_include_default?(options)
|
|
152
152
|
# must explicitly check for :null to allow change_column to work on migrations
|
|
153
153
|
if options[:null] == false
|
|
154
154
|
sql << " NOT NULL"
|
|
@@ -162,6 +162,11 @@ module ActiveRecord
|
|
|
162
162
|
sql
|
|
163
163
|
end
|
|
164
164
|
|
|
165
|
+
def quote_default_expression_for_column_definition(default, column_definition)
|
|
166
|
+
column_definition.cast_type = lookup_cast_type(column_definition.sql_type)
|
|
167
|
+
quote_default_expression(default, column_definition)
|
|
168
|
+
end
|
|
169
|
+
|
|
165
170
|
def to_sql(sql)
|
|
166
171
|
sql = sql.to_sql if sql.respond_to?(:to_sql)
|
|
167
172
|
sql
|
|
@@ -75,7 +75,7 @@ module ActiveRecord
|
|
|
75
75
|
# are typically created by methods in TableDefinition, and added to the
|
|
76
76
|
# +columns+ attribute of said TableDefinition object, in order to be used
|
|
77
77
|
# for generating a number of table creation or table changing SQL statements.
|
|
78
|
-
ColumnDefinition = Struct.new(:name, :type, :options, :sql_type) do # :nodoc:
|
|
78
|
+
ColumnDefinition = Struct.new(:name, :type, :options, :sql_type, :cast_type) do # :nodoc:
|
|
79
79
|
self::OPTION_NAMES = [
|
|
80
80
|
:limit,
|
|
81
81
|
:precision,
|
|
@@ -108,6 +108,10 @@ module ActiveRecord
|
|
|
108
108
|
def aliased_types(name, fallback)
|
|
109
109
|
"timestamp" == name ? :datetime : fallback
|
|
110
110
|
end
|
|
111
|
+
|
|
112
|
+
def fetch_cast_type(connection)
|
|
113
|
+
cast_type
|
|
114
|
+
end
|
|
111
115
|
end
|
|
112
116
|
|
|
113
117
|
AddColumnDefinition = Struct.new(:column) # :nodoc:
|
|
@@ -303,44 +307,32 @@ module ActiveRecord
|
|
|
303
307
|
module ColumnMethods
|
|
304
308
|
extend ActiveSupport::Concern
|
|
305
309
|
|
|
306
|
-
# Appends a primary key definition to the table definition.
|
|
307
|
-
# Can be called multiple times, but this is probably not a good idea.
|
|
308
|
-
def primary_key(name, type = :primary_key, **options)
|
|
309
|
-
column(name, type, **options.merge(primary_key: true))
|
|
310
|
-
end
|
|
311
|
-
|
|
312
|
-
##
|
|
313
|
-
# :method: column
|
|
314
|
-
# :call-seq: column(name, type, **options)
|
|
315
|
-
#
|
|
316
|
-
# Appends a column or columns of a specified type.
|
|
317
|
-
#
|
|
318
|
-
# t.string(:goat)
|
|
319
|
-
# t.string(:goat, :sheep)
|
|
320
|
-
#
|
|
321
|
-
# See TableDefinition#column
|
|
322
|
-
|
|
323
|
-
included do
|
|
324
|
-
define_column_methods :bigint, :binary, :boolean, :date, :datetime, :decimal,
|
|
325
|
-
:float, :integer, :json, :string, :text, :time, :timestamp, :virtual
|
|
326
|
-
|
|
327
|
-
alias :blob :binary
|
|
328
|
-
alias :numeric :decimal
|
|
329
|
-
end
|
|
330
|
-
|
|
331
310
|
class_methods do
|
|
332
|
-
|
|
333
|
-
column_types
|
|
334
|
-
|
|
311
|
+
private
|
|
312
|
+
def define_column_methods(*column_types) # :nodoc:
|
|
313
|
+
column_types.each do |column_type|
|
|
314
|
+
module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
|
335
315
|
def #{column_type}(*names, **options)
|
|
336
316
|
raise ArgumentError, "Missing column name(s) for #{column_type}" if names.empty?
|
|
337
317
|
names.each { |name| column(name, :#{column_type}, **options) }
|
|
338
318
|
end
|
|
339
|
-
|
|
319
|
+
RUBY
|
|
320
|
+
end
|
|
340
321
|
end
|
|
341
|
-
end
|
|
342
|
-
private :define_column_methods
|
|
343
322
|
end
|
|
323
|
+
extend ClassMethods
|
|
324
|
+
|
|
325
|
+
# Appends a primary key definition to the table definition.
|
|
326
|
+
# Can be called multiple times, but this is probably not a good idea.
|
|
327
|
+
def primary_key(name, type = :primary_key, **options)
|
|
328
|
+
column(name, type, **options, primary_key: true)
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
define_column_methods :bigint, :binary, :boolean, :date, :datetime, :decimal,
|
|
332
|
+
:float, :integer, :json, :string, :text, :time, :timestamp, :virtual
|
|
333
|
+
|
|
334
|
+
alias :blob :binary
|
|
335
|
+
alias :numeric :decimal
|
|
344
336
|
end
|
|
345
337
|
|
|
346
338
|
# = Active Record Connection Adapters \Table \Definition
|
|
@@ -351,7 +343,7 @@ module ActiveRecord
|
|
|
351
343
|
# Inside migration files, the +t+ object in {create_table}[rdoc-ref:SchemaStatements#create_table]
|
|
352
344
|
# is actually of this type:
|
|
353
345
|
#
|
|
354
|
-
# class SomeMigration < ActiveRecord::Migration[8.
|
|
346
|
+
# class SomeMigration < ActiveRecord::Migration[8.1]
|
|
355
347
|
# def up
|
|
356
348
|
# create_table :foo do |t|
|
|
357
349
|
# puts t.class # => "ActiveRecord::ConnectionAdapters::TableDefinition"
|
|
@@ -765,7 +757,7 @@ module ActiveRecord
|
|
|
765
757
|
# end
|
|
766
758
|
#
|
|
767
759
|
# See {connection.index_exists?}[rdoc-ref:SchemaStatements#index_exists?]
|
|
768
|
-
def index_exists?(column_name, **options)
|
|
760
|
+
def index_exists?(column_name = nil, **options)
|
|
769
761
|
@base.index_exists?(name, column_name, **options)
|
|
770
762
|
end
|
|
771
763
|
|
|
@@ -85,7 +85,8 @@ module ActiveRecord
|
|
|
85
85
|
|
|
86
86
|
def schema_default(column)
|
|
87
87
|
return unless column.has_default?
|
|
88
|
-
|
|
88
|
+
# TODO: Remove fetch_cast_type and the need for connection after we release 8.1.
|
|
89
|
+
type = column.fetch_cast_type(@connection)
|
|
89
90
|
default = type.deserialize(column.default)
|
|
90
91
|
if default.nil?
|
|
91
92
|
schema_expression(column)
|